From 6aab7a411fcefc51f16f38b5786bc336cd701f5e Mon Sep 17 00:00:00 2001 From: JeremyRand Date: Sat, 27 Oct 2018 09:35:12 +0000 Subject: [PATCH] (WIP) Don't throw fatal errors when cert_override.txt sync fails Instead just stop resolving domains until the error goes away. This fixes issues where ncdns would exit if, for example, Namecoin Core wasn't running or wasn't fully synced when ncdns was launched. --- backend/backend.go | 6 + .../firefoxoverridesync.go | 110 +++++++++++++++--- 2 files changed, 99 insertions(+), 17 deletions(-) diff --git a/backend/backend.go b/backend/backend.go index 3edf719..5060385 100644 --- a/backend/backend.go +++ b/backend/backend.go @@ -7,6 +7,7 @@ import "github.com/namecoin/ncdns/namecoin" import "github.com/namecoin/ncdns/util" import "github.com/namecoin/ncdns/ncdomain" import "github.com/namecoin/ncdns/tlshook" +import "github.com/namecoin/ncdns/tlsoverridefirefox/tlsoverridefirefoxsync" import "github.com/namecoin/tlsrestrictnss/tlsrestrictnsssync" import "github.com/hlandau/xlog" import "sync" @@ -108,6 +109,11 @@ func convertEmail(email string) (string, error) { // Do low-level queries against an abstract zone file. This is the per-query // entrypoint from madns. func (b *Backend) Lookup(qname string) (rrs []dns.RR, err error) { + if !tlsoverridefirefoxsync.IsReady() { + err = fmt.Errorf("tlsoverridefirefoxsync not ready") + return + } + if !tlsrestrictnsssync.IsReady() { err = fmt.Errorf("tlsrestrictnss not ready") return diff --git a/tlsoverridefirefox/tlsoverridefirefoxsync/firefoxoverridesync.go b/tlsoverridefirefox/tlsoverridefirefoxsync/firefoxoverridesync.go index 190a274..3c0a12a 100644 --- a/tlsoverridefirefox/tlsoverridefirefoxsync/firefoxoverridesync.go +++ b/tlsoverridefirefox/tlsoverridefirefoxsync/firefoxoverridesync.go @@ -2,6 +2,7 @@ package tlsoverridefirefoxsync import ( "bytes" + "fmt" "io/ioutil" "os" "path/filepath" @@ -31,18 +32,36 @@ var zoneData string var zoneDataReady = false var zoneDataMux sync.Mutex -// Note: the reason for the Fatal reaction to errors is that, if we stop -// syncing the override list, Firefox will continue trusting .bit certs that -// might be revoked in Namecoin. Therefore, it is important that, in such a -// situation, .bit domains must stop resolving until the issue is corrected. -// Forcing ncdns to exit is the least complex way to achieve this. +// This is true when an error occurred during sync. Such an error could leave +// the cert_override.txt with positive overrides that have since been revoked +// by the blockchain, which would be a security issue if .bit resolving isn't +// stopped. +var syncFailure = true +var syncFailureMux sync.Mutex + +func checkFlagsSane() error { + if firefoxProfileDirFlag.Value() == "" { + return fmt.Errorf("Missing required config option tlsoverridefirefox.profiledir") + } + + return nil +} func watchZone(conn namecoin.Conn) { for { var result bytes.Buffer err := ncdumpzone.Dump(conn, &result, "firefox-override") - log.Fatale(err, "Couldn't dump zone for Firefox override sync") + if err != nil { + log.Errore(err, "Couldn't dump zone for Firefox override sync") + + syncFailureMux.Lock() + syncFailure = true + syncFailureMux.Unlock() + + time.Sleep(1 * time.Second) + continue + } zoneDataMux.Lock() zoneData = result.String() @@ -54,12 +73,19 @@ func watchZone(conn namecoin.Conn) { } func watchProfile(suffix string) { - if firefoxProfileDirFlag.Value() == "" { - log.Fatal("Missing required config option tlsoverridefirefox.profiledir") - } - for { - if profileInUse() { + inUse, err := profileInUse() + if err != nil { + log.Errore(err, "Couldn't check if Firefox is running for override sync") + + syncFailureMux.Lock() + syncFailure = true + syncFailureMux.Unlock() + + time.Sleep(1 * time.Second) + continue + } + if inUse { time.Sleep(1 * time.Second) continue } @@ -89,15 +115,31 @@ func watchProfile(suffix string) { // read an empty file. prevOverrides = []byte(``) } else { - log.Fatale(err, + log.Errore(err, "Couldn't read Firefox "+ "cert_override.txt") + + syncFailureMux.Lock() + syncFailure = true + syncFailureMux.Unlock() + + time.Sleep(1 * time.Second) + continue } } filteredPrevOverrides, err := tlsoverridefirefox. FilterOverrides(string(prevOverrides), suffix) - log.Fatale(err, "Couldn't filter Firefox overrides") + if err != nil { + log.Errore(err, "Couldn't filter Firefox overrides") + + syncFailureMux.Lock() + syncFailure = true + syncFailureMux.Unlock() + + time.Sleep(1 * time.Second) + continue + } newOverrides := filteredPrevOverrides + zoneDataLocal + "\n" @@ -105,21 +147,46 @@ func watchProfile(suffix string) { // TODO: maybe instead write to a temp file and then move the file into place? err = ioutil.WriteFile(firefoxProfileDirFlag.Value()+ "/cert_override.txt", []byte(newOverrides), 0600) - log.Fatale(err, "Couldn't write Firefox cert_override.txt") + if err != nil { + log.Errore(err, "Couldn't write Firefox cert_override.txt") + syncFailureMux.Lock() + syncFailure = true + syncFailureMux.Unlock() + + time.Sleep(1 * time.Second) + continue + } + + syncFailureMux.Lock() + syncFailure = false + syncFailureMux.Unlock() log.Debug("Finished syncing zone to cert_override.txt") time.Sleep(10 * time.Minute) } } -func profileInUse() bool { +func profileInUse() (bool, error) { // This glob pattern matches the ".sqlite-wal" and ".sqlite-shm" files // that are only present when Firefox's databases are open. matches, err := filepath.Glob(firefoxProfileDirFlag.Value() + "/*.sqlite-*") - log.Fatale(err, "Couldn't check if Firefox is running for override sync") + if err != nil { + return true, err + } + + return matches != nil, nil +} + +// IsReady returns true if the overrides are successfully synced. If it +// returns false, it may be unsafe for TLS connections to rely on the synced +// overrides. +func IsReady() bool { + syncFailureMux.Lock() + result := !syncFailure + syncFailureMux.Unlock() - return matches != nil + return result } // Start starts 2 background threads that synchronize the blockchain's TLSA @@ -127,8 +194,17 @@ func profileInUse() bool { // to access Namecoin Core, as well as a host suffix (usually "bit"). func Start(conn namecoin.Conn, suffix string) error { if syncEnableFlag.Value() { + err := checkFlagsSane() + if err != nil { + return err + } + go watchZone(conn) go watchProfile(suffix) + } else { + syncFailureMux.Lock() + syncFailure = false + syncFailureMux.Unlock() } return nil }