diff --git a/irc/client.go b/irc/client.go index 80b98465..66053df2 100644 --- a/irc/client.go +++ b/irc/client.go @@ -51,6 +51,7 @@ type Client struct { socket *Socket username Name isDestroyed bool + certfp string } func NewClient(server *Server, conn net.Conn, isTLS bool) *Client { @@ -201,6 +202,11 @@ func (client *Client) Register() { if client.registered { return } + if client.flags[TLS] { + // error is not useful to us here anyways, so we can ignore it + client.certfp, _ = client.socket.CertFP() + //TODO(dan): login based on certfp + } client.registered = true client.Touch() } diff --git a/irc/server.go b/irc/server.go index 66d85e6a..6331daec 100644 --- a/irc/server.go +++ b/irc/server.go @@ -261,6 +261,7 @@ func (s *Server) listen(addr string, tlsMap map[Name]*tls.Config) { tlsString := "plaintext" if listenTLS { + config.ClientAuth = tls.RequestClientCert listener = tls.NewListener(listener, config) tlsString = "TLS" } diff --git a/irc/socket.go b/irc/socket.go index 228c7726..c7ea3054 100644 --- a/irc/socket.go +++ b/irc/socket.go @@ -6,11 +6,20 @@ package irc import ( "bufio" + "crypto/sha256" + "crypto/tls" + "encoding/hex" + "errors" "io" "net" "strings" ) +var ( + errNotTls = errors.New("Not a TLS connection") + errNoPeerCerts = errors.New("Client did not provide a certificate") +) + // Socket represents an IRC socket. type Socket struct { Closed bool @@ -35,6 +44,24 @@ func (socket *Socket) Close() { socket.conn.Close() } +// CertFP returns the fingerprint of the certificate provided by the client. +func (socket *Socket) CertFP() (string, error) { + var tlsConn, isTLS = socket.conn.(*tls.Conn) + if !isTLS { + return "", errNotTls + } + + peerCerts := tlsConn.ConnectionState().PeerCertificates + if len(peerCerts) < 1 { + return "", errNoPeerCerts + } + + rawCert := sha256.Sum256(peerCerts[0].Raw) + fingerprint := hex.EncodeToString(rawCert[:]) + + return fingerprint, nil +} + // Read returns a single IRC line from a Socket. func (socket *Socket) Read() (string, error) { if socket.Closed {