diff options
author | Rasmus Dahlberg <rasmus@rgdd.se> | 2023-12-09 17:08:45 +0100 |
---|---|---|
committer | Rasmus Dahlberg <rasmus@rgdd.se> | 2023-12-10 20:38:21 +0100 |
commit | 895d5fea41177e444c18f4fdc820fffa5f67d5bf (patch) | |
tree | 42fd1e9507384abcd9c6e82f18ccca813f8b6296 /pkg/monitor/matcher.go | |
parent | f124940f75e1f49100fe5381019d2f65d6915304 (diff) |
Add drafty skeleton
Diffstat (limited to 'pkg/monitor/matcher.go')
-rw-r--r-- | pkg/monitor/matcher.go | 90 |
1 files changed, 90 insertions, 0 deletions
diff --git a/pkg/monitor/matcher.go b/pkg/monitor/matcher.go new file mode 100644 index 0000000..fa3a894 --- /dev/null +++ b/pkg/monitor/matcher.go @@ -0,0 +1,90 @@ +package monitor + +import ( + "fmt" + "strings" + + ct "github.com/google/certificate-transparency-go" +) + +type Matcher interface { + Match(leafInput, extraData []byte) (bool, error) +} + +// MatchAll matches all certificates +type MatchAll struct{} + +func (m *MatchAll) Match(leafInput, extraData []byte) (bool, error) { + return true, nil +} + +// MatchWildcards matches a list of wildcards, see the MatchWildcard type +type MatchWildcards []MatchWildcard + +func (m *MatchWildcards) Match(leafInput, extraData []byte) (bool, error) { + sans, err := getSANs(ct.LeafEntry{LeafInput: leafInput, ExtraData: extraData}) + if err != nil { + return false, err + } + return m.match(sans), nil +} + +func (m *MatchWildcards) match(sans []string) bool { + for _, mw := range (*m)[:] { + if mw.match(sans) { + return true + } + } + return false +} + +// MatchWildcard exclude matches for `.*<Exclude>\.<Wildcard>`, but will +// otherwise match on any `.*\.<Wildcard>` as well as SANs equal to <Wildcard>. +// +// For example, let <Wildcard> be example.org and Exclude be [foo, bar]. Then +// example.org and www.example.org would match, whereas foo.example.org, +// sub.foo.example.org, and bar.example.org. would not match. +type MatchWildcard struct { + Wildcard string `json:"wildcard"` + Excludes []string `json:"excludes"` +} + +func (m *MatchWildcard) match(sans []string) bool { + for _, san := range sans { + if san == m.Wildcard { + return true + } + if strings.HasSuffix(san, "."+m.Wildcard) && !m.exclude(san) { + return true + } + } + return false +} + +func (m *MatchWildcard) exclude(san string) bool { + for _, exclude := range m.Excludes { + suffix := exclude + "." + m.Wildcard + if strings.HasSuffix(san, suffix) { + return true + } + } + return false +} + +func getSANs(entry ct.LeafEntry) ([]string, error) { + // Warning: here be dragons, parsing of DNS names in certificates... + e, err := ct.LogEntryFromLeaf(0, &entry) + if err != nil { + return nil, fmt.Errorf("parse leaf: %v", err) + } + if e.Precert == nil && e.X509Cert == nil { + return nil, fmt.Errorf("neither precertificate nor certificate in leaf") + } + if e.Precert != nil && e.X509Cert != nil { + return nil, fmt.Errorf("both certificate and precertificate in leaf") + } + if e.Precert != nil { + return e.Precert.TBSCertificate.DNSNames, nil + } + return e.X509Cert.DNSNames, nil +} |