ncdumpzone: Add Firefox mode.

This mode outputs a cert_override.txt file (based on TLSA records) that Firefox will accept.
pull/61/head
JeremyRand 6 years ago
parent 3792dd3e95
commit 2e50c75232
No known key found for this signature in database
GPG Key ID: B3F2D165786D6570

@ -3,6 +3,7 @@ package main
import "gopkg.in/alecthomas/kingpin.v2"
import "github.com/namecoin/ncdns/ncdomain"
import "github.com/namecoin/ncdns/namecoin"
import "github.com/namecoin/ncdns/tlsoverridefirefox"
import "github.com/namecoin/ncdns/util"
import "github.com/hlandau/xlog"
import "strings"
@ -14,6 +15,9 @@ var (
rpchostFlag = kingpin.Flag("rpchost", "Namecoin RPC host:port").Default("127.0.0.1:8336").String()
rpcuserFlag = kingpin.Flag("rpcuser", "Namecoin RPC username").String()
rpcpassFlag = kingpin.Flag("rpcpass", "Namecoin RPC password").String()
formatFlag = kingpin.Flag("format", "Output format. \"zonefile\" = "+
"DNS zone file. \"firefox-override\" = Firefox "+
"cert_override.txt format.").Default("zonefile").String()
)
var conn namecoin.Conn
@ -79,7 +83,15 @@ func main() {
log.Warne(err, "error generating RRs")
for _, rr := range rrs {
fmt.Print(rr.String(), "\n")
if *formatFlag == "zonefile" {
fmt.Print(rr.String(), "\n")
} else if *formatFlag == "firefox-override" {
result, err := tlsoverridefirefox.OverrideFromRR(rr)
if err != nil {
panic(err)
}
fmt.Print(result)
}
}
}

@ -0,0 +1,124 @@
package tlsoverridefirefox
import (
"crypto/sha256"
"encoding/base64"
"encoding/hex"
"fmt"
"strings"
"github.com/miekg/dns"
"github.com/namecoin/ncdns/util"
)
// OverrideFromRR returns a Firefox certificate override (in cert_override.txt
// format) derived from rr. If no such override can be derived, returns an
// empty string.
func OverrideFromRR(rr dns.RR) (string, error) {
tlsa, ok := rr.(*dns.TLSA)
if !ok {
return "", nil
}
portLabel, protocolLabelAndHost := util.SplitDomainTail(tlsa.Hdr.Name)
protocolLabel, hostFQDN := util.SplitDomainTail(protocolLabelAndHost)
if protocolLabel != "_tcp" {
return "", nil
}
if !strings.HasPrefix(portLabel, "_") {
return "", nil
}
port := strings.TrimPrefix(portLabel, "_")
if !strings.HasSuffix(hostFQDN, ".") {
return "", fmt.Errorf("TLSA not a FQDN")
}
host := strings.TrimSuffix(hostFQDN, ".")
// SHA256, as per https://dxr.mozilla.org/mozilla-central/source/security/manager/ssl/nsCertOverrideService.cpp
fingerprintAlgo := "OID.2.16.840.1.101.3.4.2.1"
// Possible Usage values:
// 0: CA constraint. No override is necessary.
// 1: Service certificate constraint. No override is necessary.
// 2: Trust anchor assertion. Firefox doesn't support these.
// 3: Domain-issued certificate. Do an override in this case.
if tlsa.Usage != 3 {
return "", nil
}
// Only a full certificate selector can yield a SHA256 certificate
// fingerprint.
if tlsa.Selector != 0 {
return "", nil
}
fingerprint, err := getFingerprint(tlsa)
if err != nil {
return "", nil
}
overrideMask := "U"
// Format documented in https://dxr.mozilla.org/mozilla-central/source/security/manager/ssl/nsNSSCertificate.cpp
// However, it looks empirically like we can just use 0-length serial
// number and 0-length DN, and Firefox doesn't care.
dbKey := base64.StdEncoding.EncodeToString([]byte{
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0})
return host + ":" + port + "\t" + fingerprintAlgo + "\t" +
fingerprint + "\t" + overrideMask + "\t" + dbKey + "\n", nil
}
func getFingerprint(tlsa *dns.TLSA) (string, error) {
var fingerprint string
// SHA512 fingerprint can't yield a SHA256 fingerprint
if tlsa.MatchingType == 2 {
return "", fmt.Errorf("SHA512 fingerprint can't yield a " +
"SHA256 fingerprint")
}
if tlsa.MatchingType == 1 {
// SHA256 fingerprint
fingerprintBytes, err := hex.DecodeString(tlsa.Certificate)
if err != nil {
return "", err
}
fingerprint = insertColons(fingerprintBytes)
} else if tlsa.MatchingType == 0 {
// Exact match
certificateBytes, err := hex.DecodeString(tlsa.Certificate)
if err != nil {
return "", err
}
fingerprintArray := sha256.Sum256(certificateBytes)
fingerprint = insertColons(fingerprintArray[:])
} else {
// Unknown MatchingType
return "", fmt.Errorf("Unknown MatchingType")
}
return strings.ToUpper(fingerprint), nil
}
// Based on FingerprintLegacyMD5 from
// https://github.com/golang/crypto/blob/master/ssh/keys.go
func insertColons(input []byte) string {
hexarray := make([]string, len(input))
for i, c := range input {
hexarray[i] = hex.EncodeToString([]byte{c})
}
return strings.Join(hexarray, ":")
}
Loading…
Cancel
Save