diff --git a/certinject/certinject_linux.go b/certinject/certinject_linux.go new file mode 100644 index 0000000..71ddb30 --- /dev/null +++ b/certinject/certinject_linux.go @@ -0,0 +1,34 @@ +package certinject + +import "gopkg.in/hlandau/easyconfig.v1/cflag" +import "github.com/hlandau/xlog" + + +// This package is used to add and remove certificates to the system trust +// store. +// Currently only supports the system NSS store. + +var log, Log = xlog.New("ncdns.certinject") + +var ( + flagGroup = cflag.NewGroup(nil, "certstore") + nssSharedFlag = cflag.Bool(flagGroup, "nss-shared", false, "Synchronize TLS certs to the NSS shared trust store? This enables HTTPS to work with Chromium/Chrome. Only use if you've set up null HPKP in Chromium/Chrome as per documentation. If you haven't set up null HPKP, or if you access ncdns from browsers not based on Chromium, this is unsafe and should not be used.") + certExpirePeriod = cflag.Int(flagGroup, "expire", 60 * 30, "Duration (in seconds) after which TLS certs will be removed from the trust store. Making this smaller than the DNS TTL (default 600) may cause TLS errors.") +) + +// Injects the given cert into all configured trust stores. +func InjectCert(derBytes []byte) { + + if nssSharedFlag.Value() { + injectCertNssShared(derBytes) + } +} + +// Cleans expired certs from all configured trust stores. +func CleanCerts() { + + if nssSharedFlag.Value() { + cleanCertsNssShared() + } + +} diff --git a/certinject/certinject_misc.go b/certinject/certinject_misc.go index ff40bc5..c017530 100644 --- a/certinject/certinject_misc.go +++ b/certinject/certinject_misc.go @@ -1,4 +1,4 @@ -// +build !windows +// +build !windows,!linux package certinject diff --git a/certinject/nss_shared_linux.go b/certinject/nss_shared_linux.go new file mode 100644 index 0000000..1d60885 --- /dev/null +++ b/certinject/nss_shared_linux.go @@ -0,0 +1,89 @@ +package certinject + +import "crypto/sha256" +import "encoding/hex" +import "io/ioutil" +import "os" +import "os/exec" +import "strings" +import "math" +import "time" + +var homeDir = os.Getenv("HOME") + +var certDir = homeDir + "/.ncdns/certs" +var nssDir = "sql:" + homeDir + "/.pki/nssdb" + +func injectCertNssShared(derBytes []byte) { + + fingerprint := sha256.Sum256(derBytes) + + fingerprintHex := hex.EncodeToString(fingerprint[:]) + + path := certDir + "/" + fingerprintHex + ".pem" + + injectCertFile(derBytes, path) + + nickname := nicknameFromFingerprintHexNssShared(fingerprintHex) + + cmd := exec.Command("certutil", "-d", nssDir, "-A", "-t", "CP,,", "-n", nickname, "-a", "-i", path) + + err := cmd.Run() + if err != nil { + log.Fatal(err) + } + +} + +func cleanCertsNssShared() { + + certFiles, _ := ioutil.ReadDir(certDir + "/") + + // for all Namecoin certs in the folder + for _, f := range certFiles { + + // Check if the cert is expired + expired, err := checkCertExpiredNssShared(f) + if err != nil { + log.Fatal(err) + } + + // delete the cert if it's expired + if expired { + + filename := f.Name() + + fingerprintHex := strings.Replace(filename, ".pem", "", -1) + + nickname := nicknameFromFingerprintHexNssShared(fingerprintHex) + + // Delete the cert from NSS + cmd := exec.Command("certutil", "-d", nssDir, "-D", "-n", nickname) + + err := cmd.Run() + if err != nil { + log.Fatal(err) + } + + // Also delete the cert from the filesystem + err = os.Remove(certDir + "/" + filename) + } + } + +} + +func checkCertExpiredNssShared(certFile os.FileInfo) (bool, error) { + + // Get the last modified time + certFileModTime := certFile.ModTime() + + // If the cert's last modified timestamp differs too much from the current time in either direction, consider it expired + expired := math.Abs( time.Since(certFileModTime).Seconds() ) > float64(certExpirePeriod.Value()) + + return expired, nil + +} + +func nicknameFromFingerprintHexNssShared(fingerprintHex string) string { + return "Namecoin-" + fingerprintHex +}