From b0649cb5d354f6ef5772c519b688b55aa3b85586 Mon Sep 17 00:00:00 2001 From: Daniel Oaks Date: Mon, 16 Oct 2017 08:47:49 +1000 Subject: [PATCH] WEBIRC: Allow protecting with fingerprint and parse `tls` flag --- irc/config.go | 2 +- irc/gateways.go | 70 ++++++++++++++++++++++++++++++++++++++++--------- irc/help.go | 9 +++++-- oragono.yaml | 3 +++ 4 files changed, 68 insertions(+), 16 deletions(-) diff --git a/irc/config.go b/irc/config.go index be365a21..77f0039e 100644 --- a/irc/config.go +++ b/irc/config.go @@ -402,7 +402,7 @@ func LoadConfig(filename string) (config *Config, err error) { continue } - err = webirc.ProcessPassword() + err = webirc.Populate() if err != nil { return nil, fmt.Errorf("Could not parse WebIRC config: %s", err.Error()) } diff --git a/irc/gateways.go b/irc/gateways.go index a0328f4b..7aa72628 100644 --- a/irc/gateways.go +++ b/irc/gateways.go @@ -6,8 +6,10 @@ package irc import ( + "errors" "fmt" "net" + "strings" "github.com/oragono/oragono/irc/passwd" @@ -18,23 +20,51 @@ import ( type webircConfig struct { PasswordString string `yaml:"password"` Password []byte `yaml:"password-bytes"` + Fingerprint string Hosts []string } -// ProcessPassword populates our password. -func (wc *webircConfig) ProcessPassword() error { - password, error := passwd.DecodePasswordHash(wc.PasswordString) - wc.Password = password - return error +// Populate fills out our password or fingerprint. +func (wc *webircConfig) Populate() (err error) { + if wc.Fingerprint == "" && wc.PasswordString == "" { + return errors.New("Fingerprint or password needs to be specified") + } + + if wc.PasswordString != "" { + var password []byte + password, err = passwd.DecodePasswordHash(wc.PasswordString) + wc.Password = password + } + return err } -// WEBIRC password gateway hostname ip +// WEBIRC [:flag1 flag2=x flag3] func webircHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool { // only allow unregistered clients to use this command if client.registered { return false } + // process flags + var secure bool + if 4 < len(msg.Params) { + for _, x := range strings.Split(msg.Params[4], " ") { + // split into key=value + var key string + if strings.Contains(x, "=") { + y := strings.SplitN(x, "=", 2) + key, _ = y[0], y[1] + } else { + key = x + } + + // only accept "tls" flag if the gateway's connection to us is secure as well + if strings.ToLower(key) == "tls" && client.flags[TLS] { + secure = true + } + } + } + clientAddress := utils.IPString(client.socket.conn.RemoteAddr()) clientHostname := client.hostname server.configurableStateMutex.RLock() @@ -42,13 +72,17 @@ func webircHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool { for _, info := range server.webirc { for _, address := range info.Hosts { if clientHostname == address || clientAddress == address { - // confirm password + // confirm password and/or fingerprint givenPassword := msg.Params[0] - if passwd.ComparePasswordString(info.Password, givenPassword) == nil { - proxiedIP := msg.Params[3] - - return client.ApplyProxiedIP(proxiedIP) + if 0 < len(info.Password) && passwd.ComparePasswordString(info.Password, givenPassword) != nil { + continue } + if 0 < len(info.Fingerprint) && client.certfp != info.Fingerprint { + continue + } + + proxiedIP := msg.Params[3] + return client.ApplyProxiedIP(proxiedIP, secure) } } } @@ -73,7 +107,8 @@ func proxyHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool { if clientHostname == address || clientAddress == address { proxiedIP := msg.Params[1] - return client.ApplyProxiedIP(proxiedIP) + // assume PROXY connections are always secure + return client.ApplyProxiedIP(proxiedIP, true) } } client.Quit("PROXY command is not usable from your address") @@ -81,7 +116,7 @@ func proxyHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool { } // ApplyProxiedIP applies the given IP to the client. -func (client *Client) ApplyProxiedIP(proxiedIP string) (exiting bool) { +func (client *Client) ApplyProxiedIP(proxiedIP string, tls bool) (exiting bool) { // ensure IP is sane parsedProxiedIP := net.ParseIP(proxiedIP) if parsedProxiedIP == nil { @@ -99,5 +134,14 @@ func (client *Client) ApplyProxiedIP(proxiedIP string) (exiting bool) { client.proxiedIP = proxiedIP client.rawHostname = utils.LookupHostname(proxiedIP) client.hostname = client.rawHostname + + // set tls info + client.certfp = "" + if tls { + client.flags[TLS] = true + } else { + delete(client.flags, TLS) + } + return false } diff --git a/irc/help.go b/irc/help.go index 148d07a9..d0f61b18 100644 --- a/irc/help.go +++ b/irc/help.go @@ -463,11 +463,16 @@ Views the version of software and the RPL_ISUPPORT tokens for the given server.` }, "webirc": { oper: true, // not really, but it's restricted anyways - text: `WEBIRC + text: `WEBIRC [:] Used by web<->IRC gateways and bouncers, the WEBIRC command allows gateways to pass-through the real IP addresses of clients: -ircv3.net/specs/extensions/webirc.html`, +ircv3.net/specs/extensions/webirc.html + + is a list of space-separated strings indicating various details about +the connection from the client to the gateway, such as: + +- tls: this flag indicates that the client->gateway connection is secure`, }, "who": { text: `WHO [o] diff --git a/oragono.yaml b/oragono.yaml index 1ab2c636..02dc6bb3 100644 --- a/oragono.yaml +++ b/oragono.yaml @@ -69,6 +69,9 @@ server: webirc: # one webirc block -- should correspond to one set of gateways - + # tls fingerprint the gateway must connect with to use this webirc block + fingerprint: 938dd33f4b76dcaf7ce5eb25c852369cb4b8fb47ba22fc235aa29c6623a5f182 + # password the gateway uses to connect, made with oragono genpasswd password: JDJhJDA0JG9rTTVERlNRa0hpOEZpNkhjZE95SU9Da1BseFdlcWtOTEQxNEFERVlqbEZNTkdhOVlYUkMu