Skip to content

Commit

Permalink
Merge #80: Automatically sync TLSA records with Firefox cert_override…
Browse files Browse the repository at this point in the history
….txt

fade5f4 Allow ncdns to automatically sync TLSA records with Firefox cert_override.txt. (JeremyRand)

Pull request description:

Tree-SHA512: 05360437bea506eacb71e37d62781627f5c60de61c4b30d60c2e0efa7da9f511a087715514cdc5be0ab0fbcf0229bfea31133dcb486c947a4068d7b562ad13c4
  • Loading branch information
JeremyRand committed Aug 16, 2018
2 parents 21c3c3d + fade5f4 commit bad0c81
Show file tree
Hide file tree
Showing 3 changed files with 191 additions and 0 deletions.
7 changes: 7 additions & 0 deletions server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
"github.com/miekg/dns"
"github.com/namecoin/ncdns/backend"
"github.com/namecoin/ncdns/namecoin"
"github.com/namecoin/ncdns/tlsoverridefirefox/tlsoverridefirefoxsync"
"gopkg.in/hlandau/madns.v1"
)

Expand Down Expand Up @@ -213,6 +214,12 @@ func (s *Server) Start() error {
s.tcpServer = s.runListener("tcp")
s.wgStart.Wait()
log.Info("Listeners started")

err := tlsoverridefirefoxsync.Start(s.namecoinConn, s.cfg.CanonicalSuffix)
if err != nil {
return fmt.Errorf("Couldn't start Firefox override sync: %s", err)
}

return nil
}

Expand Down
50 changes: 50 additions & 0 deletions tlsoverridefirefox/filter.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package tlsoverridefirefox

import (
"fmt"
"net"
"strings"
)

// FilterOverrides accepts as input the contents of a Firefox cert_override.txt
// and a host suffix to blacklist (usually "bit"). It returns the contents of
// a new Firefox cert_override.txt that contains all overrides as the input
// file except for those that match the host suffix.
func FilterOverrides(overrides, blacklistedHostSuffix string) (string, error) {
result := ""

overridesSlice := strings.Split(overrides, "\n")

for _, override := range overridesSlice {
trimmed := strings.TrimSpace(override)
if trimmed == "" {
// This is a blank line; don't try to parse it or
// include it in output.
continue
}
if strings.HasPrefix(trimmed, "#") {
// This is a comment; pass it through verbatim.
result = result + override + "\n"
continue
}

tabSplit := strings.Split(override, "\t")
hostAndPort := tabSplit[0]

host, _, err := net.SplitHostPort(hostAndPort)
if err != nil {
// Don't log err since it may contain private data.
return "", fmt.Errorf("Error parsing hostport")
}

if host == blacklistedHostSuffix ||
strings.HasSuffix(host, "."+blacklistedHostSuffix) {
// Host is blacklisted; don't include it in output
continue
}

result = result + override + "\n"
}

return result, nil
}
134 changes: 134 additions & 0 deletions tlsoverridefirefox/tlsoverridefirefoxsync/firefoxoverridesync.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
package tlsoverridefirefoxsync

import (
"bytes"
"io/ioutil"
"os"
"path/filepath"
"sync"
"time"

"github.com/hlandau/xlog"
"gopkg.in/hlandau/easyconfig.v1/cflag"

"github.com/namecoin/ncdns/namecoin"
"github.com/namecoin/ncdns/ncdumpzone"
"github.com/namecoin/ncdns/tlsoverridefirefox"
)

var (
flagGroup = cflag.NewGroup(nil, "tlsoverridefirefox")
syncEnableFlag = cflag.Bool(flagGroup, "sync", false,
"Synchronize TLSA records from the Namecoin zone to Firefox's "+
"cert_override.txt")
firefoxProfileDirFlag = cflag.String(flagGroup, "profiledir", "",
"Firefox profile directory")
)

var log, Log = xlog.New("ncdns.tlsoverridefirefoxsync")

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.

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")

zoneDataMux.Lock()
zoneData = result.String()
zoneDataReady = true
zoneDataMux.Unlock()

time.Sleep(10 * time.Minute)
}
}

func watchProfile(suffix string) {
if firefoxProfileDirFlag.Value() == "" {
log.Fatal("Missing required config option tlsoverridefirefox.profiledir")
}

for {
if profileInUse() {
time.Sleep(1 * time.Second)
continue
}

// At this point we know that Firefox is not running.

zoneDataMux.Lock()
zoneDataReadyLocal := zoneDataReady
zoneDataLocal := zoneData
zoneDataMux.Unlock()

if !zoneDataReadyLocal {
time.Sleep(1 * time.Second)
continue
}

log.Debug("Syncing zone to cert_override.txt...")

prevOverrides, err := ioutil.ReadFile(
firefoxProfileDirFlag.Value() + "/cert_override.txt")
if err != nil {
if os.IsNotExist(err) {
// cert_override.txt doesn't exist in a default
// Firefox install; it's only created once the
// first override is configured in the Firefox
// GUI. If it's not there, we can pretend we
// read an empty file.
prevOverrides = []byte(``)
} else {
log.Fatale(err,
"Couldn't read Firefox "+
"cert_override.txt")
}
}

filteredPrevOverrides, err := tlsoverridefirefox.
FilterOverrides(string(prevOverrides), suffix)
log.Fatale(err, "Couldn't filter Firefox overrides")

newOverrides := filteredPrevOverrides + zoneDataLocal + "\n"

// TODO: Does 0600 match the default behavior of Firefox?
// 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")

log.Debug("Finished syncing zone to cert_override.txt")

time.Sleep(10 * time.Minute)
}
}

func profileInUse() bool {
// 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")

return matches != nil
}

// Start starts 2 background threads that synchronize the blockchain's TLSA
// records to a Firefox profile's cert_override.txt. It accepts a connection
// to access Namecoin Core, as well as a host suffix (usually "bit").
func Start(conn namecoin.Conn, suffix string) error {
if syncEnableFlag.Value() {
go watchZone(conn)
go watchProfile(suffix)
}
return nil
}

0 comments on commit bad0c81

Please sign in to comment.