Repository navigation
Expand file tree
/
Copy pathmain.go
More file actions
168 lines (149 loc) · 5.12 KB
/
Copy pathmain.go
File metadata and controls
168 lines (149 loc) · 5.12 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
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
package main
import (
"flag"
"fmt"
"os"
"strings"
"sync"
"time"
)
var (
timeout *time.Duration
concurrency *int
jsonOut *bool
strict *bool
port *string
starttls *bool
file string
inCert string
noout *bool
fEndDate *bool
fIssuer *bool
fNote *bool
fSAN *bool
fSerial *bool
fStartDate *bool
fStatus *bool
fSubject *bool
revoked *bool
args []string
)
func init() {
timeout = flag.Duration("timeout", 8*time.Second, "per-connection / CRL-fetch timeout")
concurrency = flag.Int("c", 10, "number of concurrent workers")
jsonOut = flag.Bool("json", false, "output JSON instead of a table")
strict = flag.Bool("strict", false, "require a valid TLS chain before inspecting the cert (default: fetch regardless)")
port = flag.String("port", "443", "default port when a target doesn't specify one")
starttls = flag.Bool("starttls", false, "upgrade to TLS via STARTTLS instead of an implicit handshake (SMTP/IMAP/POP3/FTP/LDAP; dialect inferred from the port)")
flag.StringVar(&file, "file", "", "read targets from file(s) (comma-separated)")
flag.StringVar(&file, "f", "", "shorthand for -file")
flag.StringVar(&inCert, "incert", "", "validate certificate(s) from local file(s) (comma-separated, PEM or DER) instead of connecting over the network")
noout = flag.Bool("noout", false, "narrow output to just the fields selected below (same table/JSON shape, fewer columns/keys)")
fSubject = flag.Bool("subject", false, "certificate subject (extra column; also selectable under -noout)")
fIssuer = flag.Bool("issuer", false, "certificate issuer (shown by default; also selectable under -noout)")
fSerial = flag.Bool("serial", false, "certificate serial number (shown by default; also selectable under -noout)")
fSAN = flag.Bool("san", false, "certificate Subject Alternative Names (extra column; also selectable under -noout)")
fStartDate = flag.Bool("startdate", false, "certificate notBefore date (extra column; also selectable under -noout)")
fEndDate = flag.Bool("enddate", false, "certificate notAfter date (extra column; also selectable under -noout)")
fNote = flag.Bool("notes", false, "result note (shown by default; also selectable under -noout)")
fStatus = flag.Bool("status", false, "target status (shown by default; also selectable under -noout)")
revoked = flag.Bool("revoked", false, "display only targets that are revoked or that errored out")
flag.Usage = func() {
fmt.Fprintf(os.Stderr, "sslcheck: fetch a host's TLS certificate and check it against its CRL(s)\n\n")
fmt.Fprintf(os.Stderr, "usage:\n sslcheck [flags] host [host...]\n sslcheck [flags] -f hosts.txt\n cat hosts.txt | sslcheck [flags]\n sslcheck [flags] -incert cert.pem\n\n")
fmt.Fprintf(os.Stderr, "targets may be bare hostnames, host:port, or full URLs, e.g.:\n")
fmt.Fprintf(os.Stderr, " example.com\n example.com:8443\n https://example.com/whatever\n\n")
fmt.Fprintf(os.Stderr, "append :starttls or :tls to a target to override -starttls for just that target, e.g.:\n")
fmt.Fprintf(os.Stderr, " smtp.example.org:587:starttls\n ldap://ldap.example.org:389:starttls\n\n")
fmt.Fprintf(os.Stderr, "flags:\n")
flag.PrintDefaults()
}
}
func main() {
flag.CommandLine.Parse(reorderArgs(os.Args[1:], flag.CommandLine))
args = flag.Args()
var targets []string
var err error
if file != "" {
for f := range strings.SplitSeq(file, ",") {
if e := readfile(&targets, strings.TrimSpace(f)); e != nil {
fmt.Printf("error with file %s: %v", f, err)
continue
}
if err != nil {
fmt.Printf("error with file %s: %v", f, err)
continue
}
}
}
if len(args) == 0 {
readStdin(&targets)
} else {
targets = append(targets, args...)
}
var certFiles []string
if inCert != "" {
for f := range strings.SplitSeq(inCert, ",") {
if f = strings.TrimSpace(f); f != "" {
certFiles = append(certFiles, f)
}
}
}
if len(targets) == 0 && len(certFiles) == 0 {
flag.Usage()
os.Exit(1)
}
targetOpts := NewTargetOptions(*port, *timeout, *strict, *starttls)
// cache := newCRLCache()
results := make([]Result, len(targets)+len(certFiles))
sem := make(chan struct{}, *concurrency)
var wg sync.WaitGroup
for i, t := range targets {
wg.Add(1)
sem <- struct{}{}
go func(i int, t string) {
defer wg.Done()
defer func() { <-sem }()
results[i] = checkTarget(t, targetOpts)
}(i, t)
}
for i, f := range certFiles {
wg.Add(1)
sem <- struct{}{}
go func(i int, f string) {
defer wg.Done()
defer func() { <-sem }()
results[len(targets)+i] = checkCertFile(f, targetOpts)
}(i, f)
}
wg.Wait()
opts := PrintOptions{
subject: *fSubject,
issuer: *fIssuer,
serial: *fSerial,
status: *fStatus,
note: *fNote,
san: *fSAN,
startDate: *fStartDate,
endDate: *fEndDate,
revokedOnly: *revoked,
json: *jsonOut,
noout: *noout,
}
printResults(results, opts)
hasError, hasRevoked := false, false
for _, r := range results {
if r.Error != nil {
hasError = true
}
if r.Revoked {
hasRevoked = true
}
}
switch {
case hasRevoked:
os.Exit(2)
case hasError:
os.Exit(1)
}
}