-
-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathsebel.go
More file actions
145 lines (115 loc) · 3.44 KB
/
Copy pathsebel.go
File metadata and controls
145 lines (115 loc) · 3.44 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
package sebel
import (
"fmt"
"net"
"crypto/tls"
"crypto/x509"
"net/http"
"github.com/teler-sh/sebel/pkg/cert"
"github.com/teler-sh/sebel/pkg/sslbl"
)
// Sebel holds information and [Options].
type Sebel struct {
data data
options *Options
tls *tls.ConnectionState
}
// New creates a new instance of [Sebel] with the provided options.
func New(opt ...Options) *Sebel {
sebel := new(Sebel)
if len(opt) > 0 {
sebel.options = &opt[0]
} else {
sebel.options = new(Options)
}
sebel.data.sslbl = sslbl.MustGet()
if sebel.options.DataRefreshInterval > 0 {
sslbl.StartBackgroundRefresh(sebel.options.DataRefreshInterval)
}
return sebel
}
// Close stops the background refresh goroutine if running.
//
// It is only necessary to call this if [Options].DataRefreshInterval was set.
// Safe to call even if background refresh was not started.
func (s *Sebel) Close() {
sslbl.StopBackgroundRefresh()
}
// RoundTripper creates a new RoundTripper using the provided [http.RoundTripper]
// and [Sebel] instance.
func (s *Sebel) RoundTripper(rt http.RoundTripper) http.RoundTripper {
return &roundTripper{RoundTripper: rt, sebel: s}
}
// CheckTLS checks the TLS connection against the SSLBL (SSL Blacklist) and
// returns the SSLBL record.
//
// It returns [ErrSSLBlacklist] error if the certificate is blacklisted.
func (s *Sebel) CheckTLS(connState *tls.ConnectionState) (*sslbl.Record, error) {
s.tls = connState
return s.checkTLS()
}
// CheckHost connects to the specified host, retrieves its TLS certificate,
// and checks it against the SSLBL (SSL Blacklist).
//
// The config parameter allows customizing TLS behavior. Pass nil to use defaults.
//
// It returns [ErrSSLBlacklist] error if the certificate is blacklisted.
func (s *Sebel) CheckHost(host, port string, config *tls.Config) (*sslbl.Record, error) {
conn, err := tls.Dial("tcp", net.JoinHostPort(host, port), config)
if err != nil {
return nil, err
}
defer func() { _ = conn.Close() }()
connState := conn.ConnectionState()
s.tls = &connState
return s.checkTLS()
}
// getCert retrieves the peer certificate from the TLS connection state.
func (s *Sebel) getCert() *x509.Certificate {
if s.tls == nil {
return nil
}
if cert := s.tls.PeerCertificates; len(cert) > 0 {
return cert[0]
}
return nil
}
// checkTLS runs actual checks on the TLS connection and returns the SSLBL
// record and [ErrSSLBlacklist] error if blacklisted.
func (s *Sebel) checkTLS() (*sslbl.Record, error) {
record, ok := new(sslbl.Record), false
// return early if disabled
if s.options.DisableSSLBlacklist {
return record, nil
}
data := s.data.sslbl
if len(data) == 0 {
return record, ErrNoSSLBLData
}
certificate := s.getCert()
if certificate == nil {
return record, nil
}
fingerprint := cert.New(certificate)
sha1sum := fingerprint.SHA1().String()
record, ok = sslbl.Find(sha1sum, data)
if ok {
reason := record.Listing.Reason
s.write(record, sha1sum)
return record, fmt.Errorf("%w: %s detected", ErrSSLBlacklist, reason)
}
return record, nil
}
// write writes the blacklist detection to the configured output writer.
func (s *Sebel) write(record *sslbl.Record, fingerprint string) {
if s.options.Output == nil {
return
}
var msg string
if s.options.Formatter != nil {
msg = s.options.Formatter(record, fingerprint)
} else {
msg = fmt.Sprintf("%s: fingerprint=%q reason=%q\n", ErrSSLBlacklist, fingerprint, record.Listing.Reason)
}
_, _ = fmt.Fprint(s.options.Output, msg)
}