From 6026e0a014965a91d0d7ca8d63e0c285cbee2c75 Mon Sep 17 00:00:00 2001 From: Arceliar Date: Sun, 6 May 2018 16:32:34 -0500 Subject: [PATCH] Optional peer authentication, if non-empty then incoming TCP and all UDP peers must match one of these box keys --- src/yggdrasil/config/config.go | 1 + src/yggdrasil/debug.go | 6 ++++++ src/yggdrasil/peer.go | 13 ++++++++++--- src/yggdrasil/tcp.go | 17 +++++++++++++---- src/yggdrasil/udp.go | 8 ++++++++ yggdrasil.go | 7 +++++++ 6 files changed, 45 insertions(+), 7 deletions(-) diff --git a/src/yggdrasil/config/config.go b/src/yggdrasil/config/config.go index 30c8d23..d1b58a9 100644 --- a/src/yggdrasil/config/config.go +++ b/src/yggdrasil/config/config.go @@ -5,6 +5,7 @@ type NodeConfig struct { Listen string AdminListen string Peers []string + PeerBoxPubs []string BoxPub string BoxPriv string SigPub string diff --git a/src/yggdrasil/debug.go b/src/yggdrasil/debug.go index f87bb75..5358379 100644 --- a/src/yggdrasil/debug.go +++ b/src/yggdrasil/debug.go @@ -397,6 +397,12 @@ func (c *Core) DEBUG_setIfceExpr(expr *regexp.Regexp) { c.ifceExpr = expr } +func (c *Core) DEBUG_addAuthBoxPub(boxBytes []byte) { + var box boxPubKey + copy(box[:], boxBytes) + c.peers.authBoxPubs[box] = struct{}{} +} + //////////////////////////////////////////////////////////////////////////////// func DEBUG_simLinkPeers(p, q *peer) { diff --git a/src/yggdrasil/peer.go b/src/yggdrasil/peer.go index ea05728..c32557d 100644 --- a/src/yggdrasil/peer.go +++ b/src/yggdrasil/peer.go @@ -30,9 +30,10 @@ import "math" //import "fmt" type peers struct { - core *Core - mutex sync.Mutex // Synchronize writes to atomic - ports atomic.Value //map[Port]*peer, use CoW semantics + core *Core + authBoxPubs map[boxPubKey]struct{} + mutex sync.Mutex // Synchronize writes to atomic + ports atomic.Value //map[Port]*peer, use CoW semantics //ports map[Port]*peer } @@ -41,6 +42,12 @@ func (ps *peers) init(c *Core) { defer ps.mutex.Unlock() ps.putPorts(make(map[switchPort]*peer)) ps.core = c + ps.authBoxPubs = make(map[boxPubKey]struct{}) +} + +func (ps *peers) isAuthBoxPub(box *boxPubKey) bool { + _, isIn := ps.authBoxPubs[*box] + return isIn || len(ps.authBoxPubs) == 0 } func (ps *peers) getPorts() map[switchPort]*peer { diff --git a/src/yggdrasil/tcp.go b/src/yggdrasil/tcp.go index 3a4e9fb..be2ba83 100644 --- a/src/yggdrasil/tcp.go +++ b/src/yggdrasil/tcp.go @@ -62,7 +62,7 @@ func (iface *tcpInterface) listener() { if err != nil { panic(err) } - go iface.handler(sock) + go iface.handler(sock, true) } } @@ -81,7 +81,7 @@ func (iface *tcpInterface) callWithConn(conn net.Conn) { delete(iface.calls, raddr) iface.mutex.Unlock() }() - iface.handler(conn) + iface.handler(conn, false) } }() } @@ -106,12 +106,12 @@ func (iface *tcpInterface) call(saddr string) { if err != nil { return } - iface.handler(conn) + iface.handler(conn, false) } }() } -func (iface *tcpInterface) handler(sock net.Conn) { +func (iface *tcpInterface) handler(sock net.Conn, incoming bool) { defer sock.Close() // Get our keys keys := []byte{} @@ -150,6 +150,15 @@ func (iface *tcpInterface) handler(sock net.Conn) { if equiv(info.sig[:], iface.core.sigPub[:]) { return } + // Check if we're authorized to connect to this key / IP + if incoming && !iface.core.peers.isAuthBoxPub(&info.box) { + // Allow unauthorized peers if they're link-local + raddrStr, _, _ := net.SplitHostPort(sock.RemoteAddr().String()) + raddr := net.ParseIP(raddrStr) + if !raddr.IsLinkLocalUnicast() { + return + } + } // Check if we already have a connection to this node, close and block if yes info.localAddr, _, _ = net.SplitHostPort(sock.LocalAddr().String()) info.remoteAddr, _, _ = net.SplitHostPort(sock.RemoteAddr().String()) diff --git a/src/yggdrasil/udp.go b/src/yggdrasil/udp.go index fffda93..53aaec1 100644 --- a/src/yggdrasil/udp.go +++ b/src/yggdrasil/udp.go @@ -204,6 +204,14 @@ func (iface *udpInterface) handleKeys(msg []byte, addr connAddr) { iface.mutex.RUnlock() if !isIn { udpAddr := addr.toUDPAddr() + // Check if we're authorized to connect to this key / IP + // TODO monitor and always allow outgoing connections + if !iface.core.peers.isAuthBoxPub(&ks.box) { + // Allow unauthorized peers if they're link-local + if !udpAddr.IP.IsLinkLocalUnicast() { + return + } + } themNodeID := getNodeID(&ks.box) themAddr := address_addrForNodeID(themNodeID) themAddrString := net.IP(themAddr[:]).String() diff --git a/yggdrasil.go b/yggdrasil.go index 30c6a79..e2b75ae 100644 --- a/yggdrasil.go +++ b/yggdrasil.go @@ -58,6 +58,13 @@ func (n *node) init(cfg *nodeConfig, logger *log.Logger) { panic(err) } n.core.DEBUG_setIfceExpr(ifceExpr) + for _, pBoxStr := range cfg.PeerBoxPubs { + pbox, err := hex.DecodeString(pBoxStr) + if err != nil { + panic(err) + } + n.core.DEBUG_addAuthBoxPub(pbox) + } logger.Println("Starting interface...") n.core.DEBUG_setupAndStartGlobalTCPInterface(cfg.Listen) // Listen for peers on TCP