-
Notifications
You must be signed in to change notification settings - Fork 0
/
main.go
153 lines (141 loc) · 3.66 KB
/
main.go
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
package main
import (
"encoding/csv"
"errors"
"flag"
"fmt"
"os"
"strings"
"time"
)
const TIMEFORMAT_SHORT = "060102150405Z"
const TIMEFORMAT_LONG = "20060102150405Z"
const (
CERT_STATUS = iota
CERT_EXPIRED
CERT_REVOKED
CERT_SERIAL
CERT_FILENAME
CERT_CN
)
const (
OUTPUT_EXPIRED = "CRITICAL: already expired"
OUTPUT_EXPIRES_30D = "WARNING: expires in < 30d"
OUTPUT_EXPIRES_90D = "INFO: expires in < 90d"
)
var TIME_90D = time.Now().Add(90 * 24 * time.Hour)
var TIME_30D = time.Now().Add(30 * 24 * time.Hour)
var TIME_NOW = time.Now()
type Certificate struct {
Status string
Expired *time.Time
Revoked *time.Time
Serial *string
Filename *string
CN *string
}
type Certificates struct {
Certificates []Certificate
}
// parse_time parses an OpenSSL index file timestamp. It worries about the
// two different formats known. It returns a time.Time struct and an
// error, which is nil in case of success.
func (c Certificates) parse_time(timestring string) (*time.Time, error) {
date, err := time.Parse(TIMEFORMAT_SHORT, timestring)
if err == nil {
return &date, nil
}
date, err = time.Parse(TIMEFORMAT_LONG, timestring)
if err != nil {
return nil, err
}
return &date, nil
}
// process_line parses a line and adds it as a Certificate struct to our
// internal list of Certificates. It simply panics in case of errors, let's
// find out in production if this is a great idea ;)
func (c *Certificates) process_line(line []string) {
cert := Certificate{}
expired, err := c.parse_time(line[CERT_EXPIRED])
if err != nil {
panic(err)
}
cert.Expired = expired
revoked_string := line[CERT_REVOKED]
if revoked_string != "" {
revoked, err := c.parse_time(line[CERT_REVOKED])
if err != nil {
panic(err)
}
cert.Revoked = revoked
}
cert.Status = line[CERT_STATUS]
filename := &line[CERT_FILENAME]
if *filename != "unknown" {
cert.Filename = &line[CERT_FILENAME]
}
cert.Serial = &line[CERT_SERIAL]
cert.CN = &line[CERT_CN]
switch cert.Status {
case "V":
c.Certificates = append(c.Certificates, cert)
}
}
// get_certificate_cn retrieves the certificates CommonName, together with an
// err in case something went wrong.
func (c *Certificate) get_certificate_cn() (*string, error) {
t := strings.Split(*c.CN, "/")
for _, item := range t {
if strings.HasPrefix(item, "CN=") {
cn := strings.TrimPrefix(item, "CN=")
return &cn, nil
}
}
return nil, errors.New("No CN found!")
}
// print_expired iterates over all Certificates stores internally, and prints
// out helpful information for ones either already expired, or the ones that
// expire within the next 90 or 30 days.
func (c *Certificates) print_expired() {
for _, cert := range c.Certificates {
var cn *string
var err error
var expired string
if cert.Expired.Before(TIME_NOW) {
cn, err = cert.get_certificate_cn()
expired = OUTPUT_EXPIRED
} else if cert.Expired.Before(TIME_30D) {
cn, err = cert.get_certificate_cn()
expired = OUTPUT_EXPIRES_30D
} else if cert.Expired.Before(TIME_90D) {
cn, err = cert.get_certificate_cn()
expired = OUTPUT_EXPIRES_90D
} else {
continue
}
if err != nil {
panic(err)
}
fmt.Println("Expiry Date:", cert.Expired.Format(time.DateOnly), "CN:", *cn, "SN:", *cert.Serial, expired)
}
}
func main() {
filename := flag.String("filename", "index.txt", "Path to our OpenSSL index file")
flag.Parse()
file, err := os.Open(*filename)
if err != nil {
panic(err)
}
defer file.Close()
csvreader := csv.NewReader(file)
csvreader.Comma = '\t'
lines, err := csvreader.ReadAll()
if err != nil {
panic(err)
}
cert_store := Certificates{}
for _, line := range lines {
cert_store.process_line(line)
}
cert_store.print_expired()
}