aboutsummaryrefslogtreecommitdiff
path: root/pkg/monitor/matcher.go
diff options
context:
space:
mode:
Diffstat (limited to 'pkg/monitor/matcher.go')
-rw-r--r--pkg/monitor/matcher.go90
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
+}