Andrew Ayer b6dec7822d Overhaul to be more robust and simpler
All certificates are now parsed with a special, extremely
lax parser that extracts only the DNS names.  Only if the
DNS names match the domains we're interested in will we attempt
to parse the cert with the real X509 parser.  This ensures that
we won't miss a very badly encoded certificate that has been
issued for a monitored domain.

As of the time of commit, the lax parser is able to process every
logged certificate in the known logs.
2016-02-09 10:28:52 -08:00

108 lines
2.5 KiB
Go

package main
import (
"flag"
"fmt"
"os"
"bufio"
"strings"
"github.com/google/certificate-transparency/go"
"src.agwa.name/ctwatch"
"src.agwa.name/ctwatch/cmd"
)
var stateDir = flag.String("state_dir", cmd.DefaultStateDir("ctwatch"), "Directory for storing state")
var watchDomains []string
var watchDomainSuffixes []string
func setWatchDomains (domains []string) {
for _, domain := range domains {
watchDomains = append(watchDomains, strings.ToLower(domain))
watchDomainSuffixes = append(watchDomainSuffixes, "." + strings.ToLower(domain))
}
}
func dnsNameMatches (dnsName string) bool {
dnsNameLower := strings.ToLower(dnsName)
for _, domain := range watchDomains {
if dnsNameLower == domain {
return true
}
}
for _, domainSuffix := range watchDomainSuffixes {
if strings.HasSuffix(dnsNameLower, domainSuffix) {
return true
}
}
return false
}
func anyDnsNameMatches (dnsNames []string) bool {
for _, dnsName := range dnsNames {
if dnsNameMatches(dnsName) {
return true
}
}
return false
}
func processEntry (scanner *ctwatch.Scanner, entry *ct.LogEntry) {
info := ctwatch.EntryInfo{
LogUri: scanner.LogUri,
Entry: entry,
}
// Extract DNS names
var dnsNames []string
dnsNames, info.ParseError = ctwatch.EntryDNSNames(entry)
if info.ParseError == nil {
// Match DNS names
if !anyDnsNameMatches(dnsNames) {
return
}
// Parse the certificate
info.ParsedCert, info.ParseError = ctwatch.ParseEntryCertificate(entry)
if info.ParsedCert != nil {
info.CertInfo = ctwatch.MakeCertInfo(info.ParsedCert)
} else {
info.CertInfo.DnsNames = dnsNames
}
}
cmd.LogEntry(&info)
}
func main() {
flag.Parse()
if flag.NArg() == 0 {
fmt.Fprintf(os.Stderr, "Usage: %s [flags] domain ...\n", os.Args[0])
fmt.Fprintf(os.Stderr, "\n")
fmt.Fprintf(os.Stderr, "To read domain list from stdin, use '-'. To monitor all domains, use '.'.\n")
fmt.Fprintf(os.Stderr, "See '%s -help' for a list of valid flags.\n", os.Args[0])
os.Exit(2)
}
if flag.NArg() == 1 && flag.Arg(0) == "-" {
var domains []string
scanner := bufio.NewScanner(os.Stdin)
for scanner.Scan() {
domains = append(domains, scanner.Text())
}
if err := scanner.Err(); err != nil {
fmt.Fprintf(os.Stderr, "%s: Error reading standard input: %s\n", os.Args[0], err)
os.Exit(3)
}
setWatchDomains(domains)
} else if flag.NArg() == 1 && flag.Arg(0) == "." { // "." as in root zone
watchDomainSuffixes = []string{""}
} else {
setWatchDomains(flag.Args())
}
cmd.Main(*stateDir, processEntry)
}