diff options
Diffstat (limited to 'pkg/policy/wildcard.go')
-rw-r--r-- | pkg/policy/wildcard.go | 83 |
1 files changed, 83 insertions, 0 deletions
diff --git a/pkg/policy/wildcard.go b/pkg/policy/wildcard.go new file mode 100644 index 0000000..58b0d17 --- /dev/null +++ b/pkg/policy/wildcard.go @@ -0,0 +1,83 @@ +package policy + +import ( + "encoding/json" + "fmt" + "strings" + "time" + + "rgdd.se/silent-ct/pkg/crtutil" +) + +// Wildcards implement the monitor.Matcher interface for a list of wildcards. +// +// Warning: parsing of SANs in certificates is hard. This matcher depends on +// the parsing defined in github.com/google/certificate-transparency-go/x509. +type Wildcards []Wildcard + +func (w *Wildcards) Match(leafData, extraData []byte) (bool, error) { + crt, err := crtutil.CertificateFromLogEntry(leafData, extraData) + if err != nil { + return false, err + } + return w.match(crt.DNSNames, crt.NotAfter), nil +} + +func (w *Wildcards) match(sans []string, notAfter time.Time) bool { + for _, wildcard := range *w { + if wildcard.Match(sans, notAfter) { + return true + } + } + return false +} + +// Wildcard matches any string that ends with `Wildcard`, unless: +// +// 1. `Excludes[i] + "." + Wildcard` is a longer suffix match, or +// 2. the certificate expired before the BootstrapAt timestamp. +type Wildcard struct { + BootstrapAt time.Time `json:"bootstrap_at"` + Wildcard string `json:"wildcard"` + Excludes []string `json:"excludes",omitempty"` +} + +func (w *Wildcard) UnmarshalJSON(data []byte) error { + type internal Wildcard + if err := json.Unmarshal(data, (*internal)(w)); err != nil { + return err + } + return w.Validate() +} + +func (w *Wildcard) Validate() error { + if w.BootstrapAt.IsZero() { + return fmt.Errorf("bootstrap time is required") + } + if len(w.Wildcard) == 0 { + return fmt.Errorf("wildcard is required") + } + return nil +} + +func (w *Wildcard) Match(sans []string, expiresAt time.Time) bool { + for _, san := range sans { + if san == w.Wildcard { + return w.BootstrapAt.Before(expiresAt) + } + if strings.HasSuffix(san, "."+w.Wildcard) && !w.exclude(san) { + return w.BootstrapAt.Before(expiresAt) + } + } + return false +} + +func (w *Wildcard) exclude(san string) bool { + for _, exclude := range w.Excludes { + suffix := exclude + "." + w.Wildcard + if strings.HasSuffix(san, suffix) { + return true + } + } + return false +} |