From 1240fe3eb60ddb32b2f72add30b7b676f94705df Mon Sep 17 00:00:00 2001 From: Mikkel Krautz Date: Sat, 12 Nov 2011 23:55:33 +0100 Subject: [PATCH] Enforce certhash-based bans and add strong certificate checking (non-working for now, crypto/tls doesn't verify client certificates) --- client.go | 10 +++++++++ message.go | 2 +- server.go | 63 ++++++++++++++++++++++++++++++++++++++---------------- 3 files changed, 55 insertions(+), 20 deletions(-) diff --git a/client.go b/client.go index 56b5516..0b56f3f 100644 --- a/client.go +++ b/client.go @@ -7,6 +7,7 @@ package main import ( "bufio" "bytes" + "crypto/tls" "encoding/binary" "goprotobuf.googlecode.com/hg/proto" "grumble/blobstore" @@ -125,6 +126,15 @@ func (client *Client) ShownName() string { return client.Username } +// Check whether the client's certificate is +// verified. +func (client *Client) IsVerified() bool { + tlsconn := client.conn.(*tls.Conn) + state := tlsconn.ConnectionState() + client.Printf("%v", state.VerifiedChains) + return len(state.VerifiedChains) > 0 +} + // Log a panic and disconnect the client. func (client *Client) Panic(v ...interface{}) { client.Print(v) diff --git a/message.go b/message.go index d7ec1ea..5840cd3 100644 --- a/message.go +++ b/message.go @@ -1327,7 +1327,7 @@ func (server *Server) handleUserStatsMessage(client *Client, msg *Message) { for i := len(state.PeerCertificates) - 1; i >= 0; i-- { stats.Certificates = append(stats.Certificates, state.PeerCertificates[i].Raw) } - // fixme(mkrautz): strong certificate checking + stats.StrongCertificate = proto.Bool(target.IsVerified()) } } diff --git a/server.go b/server.go index f1dae38..0e02860 100644 --- a/server.go +++ b/server.go @@ -227,7 +227,7 @@ func (server *Server) CheckSuperUserPassword(password string) bool { } // Called by the server to initiate a new client connection. -func (server *Server) NewClient(conn net.Conn) (err error) { +func (server *Server) handleIncomingClient(conn net.Conn) (err error) { client := new(Client) addr := conn.RemoteAddr() if addr == nil { @@ -253,6 +253,30 @@ func (server *Server) NewClient(conn net.Conn) (err error) { client.user = nil + // Extract user's cert hash + tlsconn := client.conn.(*tls.Conn) + err = tlsconn.Handshake() + if err != nil { + client.Printf("TLS handshake failed: %v", err) + client.Disconnect() + return + } + + state := tlsconn.ConnectionState() + if len(state.PeerCertificates) > 0 { + hash := sha1.New() + hash.Write(state.PeerCertificates[0].Raw) + sum := hash.Sum() + client.CertHash = hex.EncodeToString(sum) + } + + // Check whether the client's cert hash is banned + if server.IsCertHashBanned(client.CertHash) { + client.Printf("Certificate hash is banned") + client.Disconnect() + return + } + go client.receiver() go client.udpreceiver() @@ -434,26 +458,13 @@ func (server *Server) handleAuthenticate(client *Client, msg *Message) { client.Username = *auth.Username - // Extract certhash - tlsconn, ok := client.conn.(*tls.Conn) - if !ok { - client.Panic("Invalid connection") - return - } - state := tlsconn.ConnectionState() - if len(state.PeerCertificates) > 0 { - hash := sha1.New() - hash.Write(state.PeerCertificates[0].Raw) - sum := hash.Sum() - client.CertHash = hex.EncodeToString(sum) - } - if client.Username == "SuperUser" { if auth.Password == nil { client.RejectAuth(mumbleproto.Reject_WrongUserPW, "") return } else { if server.CheckSuperUserPassword(*auth.Password) { + ok := false client.user, ok = server.UserNameMap[client.Username] if !ok { client.RejectAuth(mumbleproto.Reject_InvalidUsername, "") @@ -1155,7 +1166,7 @@ func (server *Server) RemoveExpiredBans() { } // Is the incoming connection conn banned? -func (server *Server) IsBanned(conn net.Conn) bool { +func (server *Server) IsConnectionBanned(conn net.Conn) bool { server.banlock.RLock() defer server.banlock.RUnlock() @@ -1169,6 +1180,20 @@ func (server *Server) IsBanned(conn net.Conn) bool { return false } +// Is the certificate hash banned? +func (server *Server) IsCertHashBanned(hash string) bool { + server.banlock.RLock() + defer server.banlock.RUnlock() + + for _, ban := range server.Bans { + if ban.CertHash == hash && !ban.IsExpired() { + return true + } + } + + return false +} + // Filter incoming text according to the server's current rules. func (server *Server) FilterText(text string) (filtered string, err error) { options := &htmlfilter.Options{ @@ -1197,8 +1222,8 @@ func (server *Server) acceptLoop() { // Remove expired bans server.RemoveExpiredBans() - // Is the client banned? - if server.IsBanned(conn) { + // Is the client IP-banned? + if server.IsConnectionBanned(conn) { server.Printf("Rejected client %v: Banned", conn.RemoteAddr()) err := conn.Close() if err != nil { @@ -1209,7 +1234,7 @@ func (server *Server) acceptLoop() { // Create a new client connection from our *tls.Conn // which wraps net.TCPConn. - err = server.NewClient(conn) + err = server.handleIncomingClient(conn) if err != nil { server.Printf("Unable to handle new client: %v", err) continue