Support crazy wildcards (not just in the left-most label)

This commit is contained in:
Andrew Ayer 2016-05-10 10:37:10 -07:00
parent e99ee481a4
commit 92fbdcb947
2 changed files with 53 additions and 34 deletions

View File

@ -40,19 +40,12 @@ func trimTrailingDots (value string) string {
}
var stateDir = flag.String("state_dir", DefaultStateDir(), "Directory for storing state")
var watchDomains []string
var watchDomainSuffixes []string
func addWatchDomain (asciiDomain string) {
watchDomains = append(watchDomains, asciiDomain)
watchDomainSuffixes = append(watchDomainSuffixes, "." + asciiDomain)
}
var watchDomains [][]string
func setWatchDomains (domains []string) error {
for _, domain := range domains {
if domain == "." { // "." as in root zone (matches everything)
watchDomains = []string{}
watchDomainSuffixes = []string{""}
watchDomains = [][]string{[]string{}}
break
} else {
asciiDomain, err := idna.ToASCII(strings.ToLower(trimTrailingDots(domain)))
@ -60,44 +53,52 @@ func setWatchDomains (domains []string) error {
return fmt.Errorf("Invalid domain `%s': %s", domain, err)
}
addWatchDomain(asciiDomain)
// Also monitor DNS names that _might_ match this domain (wildcards,
// label redactions, and unparseable labels).
// For example, if we're monitoring sub.example.com, also monitor:
// *.example.com
// ?.example.com
// <unparsable>.example.com
// TODO: support for wildcards that are not the entire label (e.g. ac-*.fr)
var parentDomain string
if dot := strings.IndexRune(asciiDomain, '.'); dot != -1 {
parentDomain = asciiDomain[dot:]
}
addWatchDomain("*" + parentDomain)
addWatchDomain("?" + parentDomain)
addWatchDomain(certspotter.UnparsableDNSLabelPlaceholder + parentDomain)
watchDomains = append(watchDomains, strings.Split(asciiDomain, "."))
}
}
return nil
}
func dnsNameMatches (dnsName string) bool {
for _, domain := range watchDomains {
if dnsName == domain {
return true
func dnsLabelMatches (certLabel string, watchLabel string) bool {
// For fail-safe behavior, if a label was unparsable, it matches everything.
// Similarly, redacted labels match everything, since the label _might_ be
// for a name we're interested in.
return certLabel == "*" ||
certLabel == "?" ||
certLabel == certspotter.UnparsableDNSLabelPlaceholder ||
certspotter.MatchWildcard(certLabel, watchLabel)
}
func dnsNameMatches (dnsName []string, watchDomain []string) bool {
for len(dnsName) > 0 && len(watchDomain) > 0 {
certLabel := dnsName[len(dnsName)-1]
watchLabel := watchDomain[len(watchDomain)-1]
if !dnsLabelMatches(certLabel, watchLabel) {
return false
}
for _, domainSuffix := range watchDomainSuffixes {
if strings.HasSuffix(dnsName, domainSuffix) {
dnsName = dnsName[:len(dnsName)-1]
watchDomain = watchDomain[:len(watchDomain)-1]
}
return len(watchDomain) == 0
}
func dnsNameIsWatched (dnsName string) bool {
labels := strings.Split(dnsName, ".")
for _, watchDomain := range watchDomains {
if dnsNameMatches(labels, watchDomain) {
return true
}
}
return false
}
func anyDnsNameMatches (dnsNames []string) bool {
func anyDnsNameIsWatched (dnsNames []string) bool {
for _, dnsName := range dnsNames {
if dnsNameMatches(dnsName) {
if dnsNameIsWatched(dnsName) {
return true
}
}
@ -122,7 +123,7 @@ func processEntry (scanner *certspotter.Scanner, entry *ct.LogEntry) {
// parse error), report the certificate because we can't say for sure it
// doesn't match a domain we care about. We try very hard to make sure
// parsing identifiers always succeeds, so false alarms should be rare.
if info.Identifiers == nil || anyDnsNameMatches(info.Identifiers.DNSNames) {
if info.Identifiers == nil || anyDnsNameIsWatched(info.Identifiers.DNSNames) {
cmd.LogEntry(&info)
}
}

View File

@ -399,3 +399,21 @@ func WriteCertRepository (repoPath string, isPrecert bool, certs [][]byte) (bool
return false, path, nil
}
func MatchWildcard (pattern string, dnsName string) bool {
for len(pattern) > 0 {
if pattern[0] == '*' {
if len(dnsName) > 0 && dnsName[0] != '.' && MatchWildcard(pattern, dnsName[1:]) {
return true
}
pattern = pattern[1:]
} else {
if len(dnsName) == 0 || pattern[0] != dnsName[0] {
return false
}
pattern = pattern[1:]
dnsName = dnsName[1:]
}
}
return len(dnsName) == 0
}