mirror of
				https://github.com/yggdrasil-network/yggdrasil-go.git
				synced 2025-11-04 11:15:07 +03:00 
			
		
		
		
	Initial support for pinning public keys in peering strings
This commit is contained in:
		
							parent
							
								
									b4d72dc604
								
							
						
					
					
						commit
						e849b3e119
					
				
					 2 changed files with 75 additions and 24 deletions
				
			
		| 
						 | 
					@ -1,6 +1,7 @@
 | 
				
			||||||
package yggdrasil
 | 
					package yggdrasil
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
 | 
						"bytes"
 | 
				
			||||||
	"encoding/hex"
 | 
						"encoding/hex"
 | 
				
			||||||
	"errors"
 | 
						"errors"
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
| 
						 | 
					@ -50,6 +51,7 @@ type linkInterface struct {
 | 
				
			||||||
	name           string
 | 
						name           string
 | 
				
			||||||
	link           *link
 | 
						link           *link
 | 
				
			||||||
	peer           *peer
 | 
						peer           *peer
 | 
				
			||||||
 | 
						options        linkOptions
 | 
				
			||||||
	msgIO          linkInterfaceMsgIO
 | 
						msgIO          linkInterfaceMsgIO
 | 
				
			||||||
	info           linkInfo
 | 
						info           linkInfo
 | 
				
			||||||
	incoming       bool
 | 
						incoming       bool
 | 
				
			||||||
| 
						 | 
					@ -67,6 +69,10 @@ type linkInterface struct {
 | 
				
			||||||
	unstalled      bool        // False if an idle notification to the switch hasn't been sent because we stalled (or are first starting up)
 | 
						unstalled      bool        // False if an idle notification to the switch hasn't been sent because we stalled (or are first starting up)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type linkOptions struct {
 | 
				
			||||||
 | 
						pinningInfo *url.Userinfo
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (l *link) init(c *Core) error {
 | 
					func (l *link) init(c *Core) error {
 | 
				
			||||||
	l.core = c
 | 
						l.core = c
 | 
				
			||||||
	l.mutex.Lock()
 | 
						l.mutex.Lock()
 | 
				
			||||||
| 
						 | 
					@ -92,13 +98,19 @@ func (l *link) call(uri string, sintf string) error {
 | 
				
			||||||
		return fmt.Errorf("peer %s is not correctly formatted (%s)", uri, err)
 | 
							return fmt.Errorf("peer %s is not correctly formatted (%s)", uri, err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	pathtokens := strings.Split(strings.Trim(u.Path, "/"), "/")
 | 
						pathtokens := strings.Split(strings.Trim(u.Path, "/"), "/")
 | 
				
			||||||
 | 
						tcpOpts := tcpOptions{}
 | 
				
			||||||
 | 
						if u.User != nil {
 | 
				
			||||||
 | 
							tcpOpts.pinningInfo = u.User
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	switch u.Scheme {
 | 
						switch u.Scheme {
 | 
				
			||||||
	case "tcp":
 | 
						case "tcp":
 | 
				
			||||||
		l.tcp.call(u.Host, nil, sintf, nil)
 | 
							l.tcp.call(u.Host, tcpOpts, sintf)
 | 
				
			||||||
	case "socks":
 | 
						case "socks":
 | 
				
			||||||
		l.tcp.call(pathtokens[0], u.Host, sintf, nil)
 | 
							tcpOpts.socksProxyAddr = u.Host
 | 
				
			||||||
 | 
							l.tcp.call(pathtokens[0], tcpOpts, sintf)
 | 
				
			||||||
	case "tls":
 | 
						case "tls":
 | 
				
			||||||
		l.tcp.call(u.Host, nil, sintf, l.tcp.tls.forDialer)
 | 
							tcpOpts.upgrade = l.tcp.tls.forDialer
 | 
				
			||||||
 | 
							l.tcp.call(u.Host, tcpOpts, sintf)
 | 
				
			||||||
	default:
 | 
						default:
 | 
				
			||||||
		return errors.New("unknown call scheme: " + u.Scheme)
 | 
							return errors.New("unknown call scheme: " + u.Scheme)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -122,12 +134,13 @@ func (l *link) listen(uri string) error {
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (l *link) create(msgIO linkInterfaceMsgIO, name, linkType, local, remote string, incoming, force bool) (*linkInterface, error) {
 | 
					func (l *link) create(msgIO linkInterfaceMsgIO, name, linkType, local, remote string, incoming, force bool, options linkOptions) (*linkInterface, error) {
 | 
				
			||||||
	// Technically anything unique would work for names, but let's pick something human readable, just for debugging
 | 
						// Technically anything unique would work for names, but let's pick something human readable, just for debugging
 | 
				
			||||||
	intf := linkInterface{
 | 
						intf := linkInterface{
 | 
				
			||||||
		name:  name,
 | 
							name:    name,
 | 
				
			||||||
		link:  l,
 | 
							link:    l,
 | 
				
			||||||
		msgIO: msgIO,
 | 
							options: options,
 | 
				
			||||||
 | 
							msgIO:   msgIO,
 | 
				
			||||||
		info: linkInfo{
 | 
							info: linkInfo{
 | 
				
			||||||
			linkType: linkType,
 | 
								linkType: linkType,
 | 
				
			||||||
			local:    local,
 | 
								local:    local,
 | 
				
			||||||
| 
						 | 
					@ -181,6 +194,36 @@ func (intf *linkInterface) handler() error {
 | 
				
			||||||
		intf.link.core.log.Errorln("Failed to connect to node: " + intf.name + " version: " + fmt.Sprintf("%d.%d", meta.ver, meta.minorVer))
 | 
							intf.link.core.log.Errorln("Failed to connect to node: " + intf.name + " version: " + fmt.Sprintf("%d.%d", meta.ver, meta.minorVer))
 | 
				
			||||||
		return errors.New("failed to connect: wrong version")
 | 
							return errors.New("failed to connect: wrong version")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						// Check if the remote side matches the keys we expected. This is a bit of a weak
 | 
				
			||||||
 | 
						// check - in future versions we really should check a signature or something like that.
 | 
				
			||||||
 | 
						if pinning := intf.options.pinningInfo; pinning != nil {
 | 
				
			||||||
 | 
							allowed := true
 | 
				
			||||||
 | 
							keytype := pinning.Username()
 | 
				
			||||||
 | 
							if pubkey, ok := pinning.Password(); ok {
 | 
				
			||||||
 | 
								switch keytype {
 | 
				
			||||||
 | 
								case "curve25519":
 | 
				
			||||||
 | 
									boxPub, err := hex.DecodeString(pubkey)
 | 
				
			||||||
 | 
									if err != nil || len(boxPub) != crypto.BoxPubKeyLen {
 | 
				
			||||||
 | 
										allowed = false
 | 
				
			||||||
 | 
										break
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									allowed = bytes.Compare(boxPub, meta.box[:]) == 0
 | 
				
			||||||
 | 
								case "ed25519":
 | 
				
			||||||
 | 
									sigPub, err := hex.DecodeString(pubkey)
 | 
				
			||||||
 | 
									if err != nil || len(sigPub) != crypto.SigPubKeyLen {
 | 
				
			||||||
 | 
										allowed = false
 | 
				
			||||||
 | 
										break
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									allowed = bytes.Compare(sigPub, meta.sig[:]) == 0
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								allowed = false
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if !allowed {
 | 
				
			||||||
 | 
								intf.link.core.log.Errorf("Failed to connect to node: %q sent key that does not match pinned %q key", intf.name, keytype)
 | 
				
			||||||
 | 
								return fmt.Errorf("failed to connect: host does not match pinned %q key", pinning.Username())
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	// Check if we're authorized to connect to this key / IP
 | 
						// Check if we're authorized to connect to this key / IP
 | 
				
			||||||
	if intf.incoming && !intf.force && !intf.link.core.peers.isAllowedEncryptionPublicKey(&meta.box) {
 | 
						if intf.incoming && !intf.force && !intf.link.core.peers.isAllowedEncryptionPublicKey(&meta.box) {
 | 
				
			||||||
		intf.link.core.log.Warnf("%s connection from %s forbidden: AllowedEncryptionPublicKeys does not contain key %s",
 | 
							intf.link.core.log.Warnf("%s connection from %s forbidden: AllowedEncryptionPublicKeys does not contain key %s",
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -57,6 +57,12 @@ type TcpUpgrade struct {
 | 
				
			||||||
	name    string
 | 
						name    string
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type tcpOptions struct {
 | 
				
			||||||
 | 
						linkOptions
 | 
				
			||||||
 | 
						upgrade        *TcpUpgrade
 | 
				
			||||||
 | 
						socksProxyAddr string
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (l *TcpListener) Stop() {
 | 
					func (l *TcpListener) Stop() {
 | 
				
			||||||
	defer func() { recover() }()
 | 
						defer func() { recover() }()
 | 
				
			||||||
	close(l.stop)
 | 
						close(l.stop)
 | 
				
			||||||
| 
						 | 
					@ -221,7 +227,10 @@ func (t *tcp) listener(l *TcpListener, listenaddr string) {
 | 
				
			||||||
			return
 | 
								return
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		t.waitgroup.Add(1)
 | 
							t.waitgroup.Add(1)
 | 
				
			||||||
		go t.handler(sock, true, nil, l.upgrade)
 | 
							options := tcpOptions{
 | 
				
			||||||
 | 
								upgrade: l.upgrade,
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							go t.handler(sock, true, options)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -239,12 +248,12 @@ func (t *tcp) startCalling(saddr string) bool {
 | 
				
			||||||
// If the dial is successful, it launches the handler.
 | 
					// If the dial is successful, it launches the handler.
 | 
				
			||||||
// When finished, it removes the outgoing call, so reconnection attempts can be made later.
 | 
					// When finished, it removes the outgoing call, so reconnection attempts can be made later.
 | 
				
			||||||
// This all happens in a separate goroutine that it spawns.
 | 
					// This all happens in a separate goroutine that it spawns.
 | 
				
			||||||
func (t *tcp) call(saddr string, options interface{}, sintf string, upgrade *TcpUpgrade) {
 | 
					func (t *tcp) call(saddr string, options tcpOptions, sintf string) {
 | 
				
			||||||
	go func() {
 | 
						go func() {
 | 
				
			||||||
		callname := saddr
 | 
							callname := saddr
 | 
				
			||||||
		callproto := "TCP"
 | 
							callproto := "TCP"
 | 
				
			||||||
		if upgrade != nil {
 | 
							if options.upgrade != nil {
 | 
				
			||||||
			callproto = strings.ToUpper(upgrade.name)
 | 
								callproto = strings.ToUpper(options.upgrade.name)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		if sintf != "" {
 | 
							if sintf != "" {
 | 
				
			||||||
			callname = fmt.Sprintf("%s/%s/%s", callproto, saddr, sintf)
 | 
								callname = fmt.Sprintf("%s/%s/%s", callproto, saddr, sintf)
 | 
				
			||||||
| 
						 | 
					@ -263,12 +272,11 @@ func (t *tcp) call(saddr string, options interface{}, sintf string, upgrade *Tcp
 | 
				
			||||||
		}()
 | 
							}()
 | 
				
			||||||
		var conn net.Conn
 | 
							var conn net.Conn
 | 
				
			||||||
		var err error
 | 
							var err error
 | 
				
			||||||
		socksaddr, issocks := options.(string)
 | 
							if options.socksProxyAddr != "" {
 | 
				
			||||||
		if issocks {
 | 
					 | 
				
			||||||
			if sintf != "" {
 | 
								if sintf != "" {
 | 
				
			||||||
				return
 | 
									return
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			dialerdst, er := net.ResolveTCPAddr("tcp", socksaddr)
 | 
								dialerdst, er := net.ResolveTCPAddr("tcp", options.socksProxyAddr)
 | 
				
			||||||
			if er != nil {
 | 
								if er != nil {
 | 
				
			||||||
				return
 | 
									return
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
| 
						 | 
					@ -282,7 +290,7 @@ func (t *tcp) call(saddr string, options interface{}, sintf string, upgrade *Tcp
 | 
				
			||||||
				return
 | 
									return
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			t.waitgroup.Add(1)
 | 
								t.waitgroup.Add(1)
 | 
				
			||||||
			t.handler(conn, false, saddr, nil)
 | 
								t.handler(conn, false, options)
 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
			dst, err := net.ResolveTCPAddr("tcp", saddr)
 | 
								dst, err := net.ResolveTCPAddr("tcp", saddr)
 | 
				
			||||||
			if err != nil {
 | 
								if err != nil {
 | 
				
			||||||
| 
						 | 
					@ -348,19 +356,19 @@ func (t *tcp) call(saddr string, options interface{}, sintf string, upgrade *Tcp
 | 
				
			||||||
				return
 | 
									return
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			t.waitgroup.Add(1)
 | 
								t.waitgroup.Add(1)
 | 
				
			||||||
			t.handler(conn, false, nil, upgrade)
 | 
								t.handler(conn, false, options)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}()
 | 
						}()
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (t *tcp) handler(sock net.Conn, incoming bool, options interface{}, upgrade *TcpUpgrade) {
 | 
					func (t *tcp) handler(sock net.Conn, incoming bool, options tcpOptions) {
 | 
				
			||||||
	defer t.waitgroup.Done() // Happens after sock.close
 | 
						defer t.waitgroup.Done() // Happens after sock.close
 | 
				
			||||||
	defer sock.Close()
 | 
						defer sock.Close()
 | 
				
			||||||
	t.setExtraOptions(sock)
 | 
						t.setExtraOptions(sock)
 | 
				
			||||||
	var upgraded bool
 | 
						var upgraded bool
 | 
				
			||||||
	if upgrade != nil {
 | 
						if options.upgrade != nil {
 | 
				
			||||||
		var err error
 | 
							var err error
 | 
				
			||||||
		if sock, err = upgrade.upgrade(sock); err != nil {
 | 
							if sock, err = options.upgrade.upgrade(sock); err != nil {
 | 
				
			||||||
			t.link.core.log.Errorln("TCP handler upgrade failed:", err)
 | 
								t.link.core.log.Errorln("TCP handler upgrade failed:", err)
 | 
				
			||||||
			return
 | 
								return
 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
| 
						 | 
					@ -370,14 +378,14 @@ func (t *tcp) handler(sock net.Conn, incoming bool, options interface{}, upgrade
 | 
				
			||||||
	stream := stream{}
 | 
						stream := stream{}
 | 
				
			||||||
	stream.init(sock)
 | 
						stream.init(sock)
 | 
				
			||||||
	var name, proto, local, remote string
 | 
						var name, proto, local, remote string
 | 
				
			||||||
	if socksaddr, issocks := options.(string); issocks {
 | 
						if options.socksProxyAddr != "" {
 | 
				
			||||||
		name = "socks://" + sock.RemoteAddr().String() + "/" + socksaddr
 | 
							name = "socks://" + sock.RemoteAddr().String() + "/" + options.socksProxyAddr
 | 
				
			||||||
		proto = "socks"
 | 
							proto = "socks"
 | 
				
			||||||
		local, _, _ = net.SplitHostPort(sock.LocalAddr().String())
 | 
							local, _, _ = net.SplitHostPort(sock.LocalAddr().String())
 | 
				
			||||||
		remote, _, _ = net.SplitHostPort(socksaddr)
 | 
							remote, _, _ = net.SplitHostPort(options.socksProxyAddr)
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		if upgraded {
 | 
							if upgraded {
 | 
				
			||||||
			proto = upgrade.name
 | 
								proto = options.upgrade.name
 | 
				
			||||||
			name = proto + "://" + sock.RemoteAddr().String()
 | 
								name = proto + "://" + sock.RemoteAddr().String()
 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
			proto = "tcp"
 | 
								proto = "tcp"
 | 
				
			||||||
| 
						 | 
					@ -387,7 +395,7 @@ func (t *tcp) handler(sock net.Conn, incoming bool, options interface{}, upgrade
 | 
				
			||||||
		remote, _, _ = net.SplitHostPort(sock.RemoteAddr().String())
 | 
							remote, _, _ = net.SplitHostPort(sock.RemoteAddr().String())
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	force := net.ParseIP(strings.Split(remote, "%")[0]).IsLinkLocalUnicast()
 | 
						force := net.ParseIP(strings.Split(remote, "%")[0]).IsLinkLocalUnicast()
 | 
				
			||||||
	link, err := t.link.core.link.create(&stream, name, proto, local, remote, incoming, force)
 | 
						link, err := t.link.core.link.create(&stream, name, proto, local, remote, incoming, force, options.linkOptions)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		t.link.core.log.Println(err)
 | 
							t.link.core.log.Println(err)
 | 
				
			||||||
		panic(err)
 | 
							panic(err)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue