From 58fca6bcf765efb2ae4955d7d4d602f750363e8b Mon Sep 17 00:00:00 2001 From: Mikkel Krautz Date: Sat, 9 Apr 2011 21:34:02 +0200 Subject: [PATCH] Recognize registered users by their certificate hash. --- acl.go | 4 +- client.go | 52 +++++++++++++-- freeze.go | 2 +- group.go | 6 +- grumble.go | 3 +- message.go | 188 ++++++++++++++++++++++++++-------------------------- murmurdb.go | 42 ++++++------ server.go | 78 +++++++++++++--------- user.go | 4 +- 9 files changed, 215 insertions(+), 164 deletions(-) diff --git a/acl.go b/acl.go index 9ca83ca..3b9da62 100644 --- a/acl.go +++ b/acl.go @@ -143,7 +143,7 @@ func NewChannelACL(channel *Channel) *ChannelACL { // and not a combination of permissions. func (server *Server) HasPermission(client *Client, channel *Channel, perm Permission) bool { // SuperUser can't speak or whisper, but everything else is OK - if client.UserId == 0 { + if client.IsSuperUser() { if perm == SpeakPermission || perm == WhisperPermission { return false } @@ -193,7 +193,7 @@ func (server *Server) HasPermission(client *Client, channel *Channel, perm Permi // // If it's a group ACL, we have to parse and interpret the group string in the // current context to determine membership. For that we use GroupMemberCheck. - matchUser := acl.IsUserACL() && acl.UserId == client.UserId + matchUser := acl.IsUserACL() && acl.UserId == client.UserId() matchGroup := GroupMemberCheck(channel, iter, acl.Group, client) if matchUser || matchGroup { if acl.Allow.IsSet(TraversePermission) { diff --git a/client.go b/client.go index 85f7f05..b7b488b 100644 --- a/client.go +++ b/client.go @@ -37,6 +37,15 @@ type Client struct { codecs []int32 udp bool + // If the client is a registered user on the server, + // the user field will point to the registration record. + user *User + + // If the client has SuperUser privileges, superUser will be true. + // Note that Grumble doesn't store credentials of the SuperUser in + // the user data store, so we have to keep track of it separately. + superUser bool + // Version Version uint32 ClientName string @@ -50,10 +59,9 @@ type Client struct { TextureHash []byte // Personal - UserId int - Session uint32 Username string - Hash string + Session uint32 + CertHash string Tokens []string Channel *Channel SelfMute bool @@ -67,6 +75,40 @@ type Client struct { PluginIdentity string } +// Is the client a registered user? +func (client *Client) IsRegistered() bool { + return client.user != nil || client.IsSuperUser() +} + +// Does the client have a certificate? +func (client *Client) HasCertificate() bool { + return len(client.CertHash) > 0 +} + +// Is the client the SuperUser? +func (client *Client) IsSuperUser() bool { + return client.superUser +} + +// Get the User ID of this client. +// Returns -1 if the client is not a registered user. +func (client *Client) UserId() int { + if client.user == nil { + return -1 + } else if client.superUser { + return 0 + } + return int(client.user.Id) +} + +// Get the client's shown name. +func (client *Client) ShownName() string { + if client.IsRegistered() { + return client.user.Name + } + return client.Username +} + // Something invalid happened on the wire. func (client *Client) Panic(reason string) { log.Printf("Client panic: %s", reason) @@ -103,8 +145,8 @@ func (client *Client) RejectAuth(kind, reason string) { } client.sendProtoMessage(MessageReject, &mumbleproto.Reject{ - Type: mumbleproto.NewReject_RejectType(mumbleproto.Reject_RejectType_value[kind]), - Reason: reasonString, + Type: mumbleproto.NewReject_RejectType(mumbleproto.Reject_RejectType_value[kind]), + Reason: reasonString, }) client.ForceDisconnect() diff --git a/freeze.go b/freeze.go index 12f01a0..36cde99 100644 --- a/freeze.go +++ b/freeze.go @@ -182,7 +182,7 @@ func NewServerFromFrozen(filename string) (s *Server, err os.Error) { decoder := gob.NewDecoder(zr) decoder.Decode(&fs) - s, err = NewServer(int64(fs.Id), "", int(DefaultPort+fs.Id-1)) + s, err = NewServer(int64(fs.Id), "0.0.0.0", int(DefaultPort+fs.Id-1)) if err != nil { return nil, err } diff --git a/group.go b/group.go index c76e09b..6761ed0 100644 --- a/group.go +++ b/group.go @@ -198,7 +198,7 @@ func GroupMemberCheck(current *Channel, aclchan *Channel, name string, client *C // The user is part of the auth group is he is authenticated. That is, // his UserId is >= 0. } else if name == "auth" { - member = client.UserId >= 0 + member = client.IsRegistered() // The user is part of the strong group if he is authenticated to the server // via a strong certificate (i.e. non-self-signed). } else if name == "strong" { @@ -329,10 +329,10 @@ func GroupMemberCheck(current *Channel, aclchan *Channel, name string, client *C } for _, group := range groups { - if group.AddContains(client.UserId) || group.TemporaryContains(client.UserId) || group.TemporaryContains(-int(client.Session)) { + if group.AddContains(client.UserId()) || group.TemporaryContains(client.UserId()) || group.TemporaryContains(-int(client.Session)) { member = true } - if group.RemoveContains(client.UserId) { + if group.RemoveContains(client.UserId()) { member = false } } diff --git a/grumble.go b/grumble.go index ead227c..f29a06d 100644 --- a/grumble.go +++ b/grumble.go @@ -100,7 +100,6 @@ func main() { } log.Printf("Using blob directory: %s", *blobdir) - // Should we import data from a Murmur SQLite file? if len(*sqlitedb) > 0 { f, err := os.Open(*datadir) @@ -139,7 +138,7 @@ func main() { f, err := os.Open(*datadir) if err != nil { - log.Fatalf("Murmur import failed: %s", err.String()) + log.Fatalf("Murmur import failed: %s", err.String()) } defer f.Close() diff --git a/message.go b/message.go index b2128ea..ca0d710 100644 --- a/message.go +++ b/message.go @@ -223,7 +223,7 @@ func (server *Server) handleChannelStateMessage(client *Client, msg *Message) { } // Only registered users can create channels. - if client.UserId < 0 && len(client.Hash) == 0 { + if !client.IsRegistered() && !client.HasCertificate() { client.sendPermissionDeniedTypeUser("MissingCertificate", client) return } @@ -251,9 +251,9 @@ func (server *Server) handleChannelStateMessage(client *Client, msg *Message) { } // Add the creator to the channel's admin group - if client.UserId >= 0 { + if client.IsRegistered() { grp := NewGroup(channel, "admin") - grp.Add[client.UserId] = true + grp.Add[client.UserId()] = true channel.Groups["admin"] = grp } @@ -263,10 +263,10 @@ func (server *Server) handleChannelStateMessage(client *Client, msg *Message) { acl := NewChannelACL(channel) acl.ApplyHere = true acl.ApplySubs = true - if client.UserId >= 0 { - acl.UserId = client.UserId + if client.IsRegistered() { + acl.UserId = client.UserId() } else { - acl.Group = "$" + client.Hash + acl.Group = "$" + client.CertHash } acl.Deny = Permission(NonePermission) acl.Allow = Permission(WritePermission | TraversePermission) @@ -464,8 +464,8 @@ func (server *Server) handleUserRemoveMessage(client *Client, msg *Message) { client.Panic(err.String()) } - // Get the user to be removed. - user, ok := server.clients[*userremove.Session] + // Get the client to be removed. + removeClient, ok := server.clients[*userremove.Session] if !ok { client.Panic("Invalid session in UserRemove message") return @@ -476,12 +476,13 @@ func (server *Server) handleUserRemoveMessage(client *Client, msg *Message) { ban = *userremove.Ban } - // Check user's permissions + // Check client's permissions perm := Permission(KickPermission) if ban { perm = Permission(BanPermission) } - if user.UserId == 0 || !server.HasPermission(client, server.root, perm) { + + if removeClient.IsSuperUser() || !server.HasPermission(client, server.root, perm) { client.sendPermissionDenied(client, server.root, perm) return } @@ -497,7 +498,7 @@ func (server *Server) handleUserRemoveMessage(client *Client, msg *Message) { return } - user.ForceDisconnect() + removeClient.ForceDisconnect() } // Handle user state changes @@ -513,16 +514,16 @@ func (server *Server) handleUserStateMessage(client *Client, msg *Message) { log.Panic("Client not found in server's client map.") return } - user := actor + target := actor if userstate.Session != nil { - user, ok = server.clients[*userstate.Session] + target, ok = server.clients[*userstate.Session] if !ok { client.Panic("Invalid session in UserState message") return } } - userstate.Session = proto.Uint32(user.Session) + userstate.Session = proto.Uint32(target.Session) userstate.Actor = proto.Uint32(actor.Session) // Does it have a channel ID? @@ -535,15 +536,15 @@ func (server *Server) handleUserStateMessage(client *Client, msg *Message) { // If the user and the actor aren't the same, check whether the actor has MovePermission on // the user's curent channel. - if actor != user && !server.HasPermission(actor, user.Channel, MovePermission) { - client.sendPermissionDenied(actor, user.Channel, MovePermission) + if actor != target && !server.HasPermission(actor, target.Channel, MovePermission) { + client.sendPermissionDenied(actor, target.Channel, MovePermission) return } // Check whether the actor has MovePermission on dstChan. Check whether user has EnterPermission // on dstChan. - if !server.HasPermission(actor, dstChan, MovePermission) && !server.HasPermission(user, dstChan, EnterPermission) { - client.sendPermissionDenied(user, dstChan, EnterPermission) + if !server.HasPermission(actor, dstChan, MovePermission) && !server.HasPermission(target, dstChan, EnterPermission) { + client.sendPermissionDenied(target, dstChan, EnterPermission) return } @@ -552,20 +553,20 @@ func (server *Server) handleUserStateMessage(client *Client, msg *Message) { if userstate.Mute != nil || userstate.Deaf != nil || userstate.Suppress != nil || userstate.PrioritySpeaker != nil { // Disallow for SuperUser - if user.UserId == 0 { + if target.IsSuperUser() { client.sendPermissionDeniedType("SuperUser") return } // Check whether the actor has 'mutedeafen' permission on user's channel. - if !server.HasPermission(actor, user.Channel, MuteDeafenPermission) { - client.sendPermissionDenied(actor, user.Channel, MuteDeafenPermission) + if !server.HasPermission(actor, target.Channel, MuteDeafenPermission) { + client.sendPermissionDenied(actor, target.Channel, MuteDeafenPermission) return } // Check if this was a suppress operation. Only the server can suppress users. if userstate.Suppress != nil { - client.sendPermissionDenied(actor, user.Channel, MuteDeafenPermission) + client.sendPermissionDenied(actor, target.Channel, MuteDeafenPermission) return } } @@ -575,7 +576,7 @@ func (server *Server) handleUserStateMessage(client *Client, msg *Message) { comment := *userstate.Comment // Clearing another user's comment. - if user != actor { + if target != actor { // Check if actor has 'move' permissions on the root channel. It is needed // to clear another user's comment. if !server.HasPermission(actor, server.root, MovePermission) { @@ -594,7 +595,7 @@ func (server *Server) handleUserStateMessage(client *Client, msg *Message) { // Only set the comment if it is different from the current // user comment. - if comment == user.Comment { + if comment == target.Comment { userstate.Comment = nil } } @@ -610,7 +611,7 @@ func (server *Server) handleUserStateMessage(client *Client, msg *Message) { // If user != actor, check for RegisterPermission permission on root channel. permCheck := Permission(NonePermission) uid := *userstate.UserId - if user == actor { + if target == actor { permCheck = SelfRegisterPermission } else { permCheck = RegisterPermission @@ -621,8 +622,8 @@ func (server *Server) handleUserStateMessage(client *Client, msg *Message) { } // We can't register a user with an empty hash. - if len(user.Hash) == 0 { - client.sendPermissionDeniedTypeUser("MissingCertificate", user) + if len(target.CertHash) == 0 { + client.sendPermissionDeniedTypeUser("MissingCertificate", target) } } @@ -635,7 +636,7 @@ func (server *Server) handleUserStateMessage(client *Client, msg *Message) { // - PluginContext // - PluginIdentity // - Recording - if actor != user && (userstate.SelfDeaf != nil || userstate.SelfMute != nil || + if actor != target && (userstate.SelfDeaf != nil || userstate.SelfMute != nil || userstate.Texture != nil || userstate.PluginContext != nil || userstate.PluginIdentity != nil || userstate.Recording != nil) { client.Panic("Invalid UserState") @@ -645,86 +646,86 @@ func (server *Server) handleUserStateMessage(client *Client, msg *Message) { broadcast := false if userstate.Texture != nil { - user.Texture = userstate.Texture - if len(user.Texture) >= 128 { + target.Texture = userstate.Texture + if len(target.Texture) >= 128 { hash := sha1.New() - hash.Write(user.Texture) - user.TextureHash = hash.Sum() + hash.Write(target.Texture) + target.TextureHash = hash.Sum() } else { - user.TextureHash = []byte{} + target.TextureHash = []byte{} } broadcast = true } if userstate.SelfDeaf != nil { - user.SelfDeaf = *userstate.SelfDeaf - if user.SelfDeaf { + target.SelfDeaf = *userstate.SelfDeaf + if target.SelfDeaf { userstate.SelfDeaf = proto.Bool(true) - user.SelfMute = true + target.SelfMute = true } broadcast = true } if userstate.SelfMute != nil { - user.SelfMute = *userstate.SelfMute - if !user.SelfMute { + target.SelfMute = *userstate.SelfMute + if !target.SelfMute { userstate.SelfDeaf = proto.Bool(false) - user.SelfDeaf = false + target.SelfDeaf = false } } if userstate.PluginContext != nil { - user.PluginContext = userstate.PluginContext + target.PluginContext = userstate.PluginContext } if userstate.PluginIdentity != nil { - user.PluginIdentity = *userstate.PluginIdentity + target.PluginIdentity = *userstate.PluginIdentity } if userstate.Comment != nil { - user.Comment = *userstate.Comment - if len(user.Comment) >= 128 { + target.Comment = *userstate.Comment + if len(target.Comment) >= 128 { hash := sha1.New() - hash.Write([]byte(user.Comment)) - user.CommentHash = hash.Sum() + hash.Write([]byte(target.Comment)) + target.CommentHash = hash.Sum() } else { - user.CommentHash = []byte{} + target.CommentHash = []byte{} } broadcast = true } if userstate.Mute != nil || userstate.Deaf != nil || userstate.Suppress != nil || userstate.PrioritySpeaker != nil { if userstate.Deaf != nil { - user.Deaf = *userstate.Deaf - if user.Deaf { + target.Deaf = *userstate.Deaf + if target.Deaf { userstate.Mute = proto.Bool(true) } } if userstate.Mute != nil { - user.Mute = *userstate.Mute - if !user.Mute { + target.Mute = *userstate.Mute + if !target.Mute { userstate.Deaf = proto.Bool(false) - user.Deaf = false + target.Deaf = false } } if userstate.Suppress != nil { - user.Suppress = *userstate.Suppress + target.Suppress = *userstate.Suppress } if userstate.PrioritySpeaker != nil { - user.PrioritySpeaker = *userstate.PrioritySpeaker + target.PrioritySpeaker = *userstate.PrioritySpeaker } broadcast = true } - if userstate.Recording != nil && *userstate.Recording != user.Recording { - user.Recording = *userstate.Recording + if userstate.Recording != nil && *userstate.Recording != target.Recording { + target.Recording = *userstate.Recording txtmsg := &mumbleproto.TextMessage{} txtmsg.TreeId = append(txtmsg.TreeId, uint32(0)) - if user.Recording { - txtmsg.Message = proto.String(fmt.Sprintf("User '%s' started recording", user.Username)) + if target.Recording { + txtmsg.Message = proto.String(fmt.Sprintf("User '%s' started recording", target.Username)) } else { - txtmsg.Message = proto.String(fmt.Sprintf("User '%s' stopped recording", user.Username)) + txtmsg.Message = proto.String(fmt.Sprintf("User '%s' stopped recording", target.Username)) } server.broadcastProtoMessageWithPredicate(MessageTextMessage, txtmsg, func(client *Client) bool { @@ -743,7 +744,7 @@ func (server *Server) handleUserStateMessage(client *Client, msg *Message) { if userstate.ChannelId != nil { channel, ok := server.Channels[int(*userstate.ChannelId)] if ok { - server.userEnterChannel(user, channel, userstate) + server.userEnterChannel(target, channel, userstate) broadcast = true } } @@ -754,10 +755,10 @@ func (server *Server) handleUserStateMessage(client *Client, msg *Message) { // textures that were sent over the wire. We can use this to determine // whether a texture is a "new style" or an "old style" texture. texlen := uint32(0) - if user.Texture != nil && len(user.Texture) > 4 { - texlen = uint32(user.Texture[0])<<24 | uint32(user.Texture[1])<<16 | uint32(user.Texture[2])<<8 | uint32(user.Texture[3]) + if target.Texture != nil && len(target.Texture) > 4 { + texlen = uint32(target.Texture[0])<<24 | uint32(target.Texture[1])<<16 | uint32(target.Texture[2])<<8 | uint32(target.Texture[3]) } - if userstate.Texture != nil && len(user.Texture) > 4 && texlen != 600*60*4 { + if userstate.Texture != nil && len(target.Texture) > 4 && texlen != 600*60*4 { // The sent texture is a new-style texture. Strip it from the message // we send to pre-1.2.2 clients. userstate.Texture = nil @@ -768,7 +769,7 @@ func (server *Server) handleUserStateMessage(client *Client, msg *Message) { log.Panic("Unable to broadcast UserState") } // Re-add it to the message, so that 1.2.2+ clients *do* get the new-style texture. - userstate.Texture = user.Texture + userstate.Texture = target.Texture } else { // Old style texture. We can send the message as-is. err := server.broadcastProtoMessageWithPredicate(MessageUserState, userstate, func(client *Client) bool { @@ -782,14 +783,14 @@ func (server *Server) handleUserStateMessage(client *Client, msg *Message) { // If a texture hash is set on user, we transmit that instead of // the texture itself. This allows the client to intelligently fetch // the blobs that it does not already have in its local storage. - if userstate != nil && len(user.TextureHash) > 0 { + if userstate != nil && len(target.TextureHash) > 0 { userstate.Texture = nil - userstate.TextureHash = user.TextureHash + userstate.TextureHash = target.TextureHash } // Ditto for comments. - if userstate.Comment != nil && len(user.CommentHash) > 0 { + if userstate.Comment != nil && len(target.CommentHash) > 0 { userstate.Comment = nil - userstate.CommentHash = user.CommentHash + userstate.CommentHash = target.CommentHash } err := server.broadcastProtoMessageWithPredicate(MessageUserState, userstate, func(client *Client) bool { @@ -816,7 +817,7 @@ func (server *Server) handleTextMessage(client *Client, msg *Message) { // fixme(mkrautz): Check text message length. // fixme(mkrautz): Sanitize text as well. - users := make(map[uint32]*Client) + clients := make(map[uint32]*Client) // Tree for _, chanid := range txtmsg.TreeId { @@ -824,8 +825,8 @@ func (server *Server) handleTextMessage(client *Client, msg *Message) { if !server.HasPermission(client, channel, TextMessagePermission) { client.sendPermissionDenied(client, channel, TextMessagePermission) } - for _, user := range channel.clients { - users[user.Session] = user + for _, target := range channel.clients { + clients[target.Session] = target } } } @@ -837,28 +838,28 @@ func (server *Server) handleTextMessage(client *Client, msg *Message) { client.sendPermissionDenied(client, channel, TextMessagePermission) return } - for _, user := range channel.clients { - users[user.Session] = user + for _, target := range channel.clients { + clients[target.Session] = target } } } - // Direct-to-users + // Direct-to-clients for _, session := range txtmsg.Session { - if user, ok := server.clients[session]; ok { - if !server.HasPermission(client, user.Channel, TextMessagePermission) { - client.sendPermissionDenied(client, user.Channel, TextMessagePermission) + if target, ok := server.clients[session]; ok { + if !server.HasPermission(client, target.Channel, TextMessagePermission) { + client.sendPermissionDenied(client, target.Channel, TextMessagePermission) return } - users[session] = user + clients[session] = target } } // Remove ourselves - users[client.Session] = nil, false + clients[client.Session] = nil, false - for _, user := range users { - user.sendProtoMessage(MessageTextMessage, &mumbleproto.TextMessage{ + for _, target := range clients { + target.sendProtoMessage(MessageTextMessage, &mumbleproto.TextMessage{ Actor: proto.Uint32(client.Session), Message: txtmsg.Message, }) @@ -1052,17 +1053,16 @@ func (server *Server) handleAclMessage(client *Client, msg *Message) { server.ClearACLCache() // Regular user? - if !server.HasPermission(client, channel, WritePermission) && (client.UserId >= 0 || len(client.Hash) > 0) { + if !server.HasPermission(client, channel, WritePermission) && client.IsRegistered() || client.HasCertificate() { chanacl := NewChannelACL(channel) chanacl.ApplyHere = true chanacl.ApplySubs = false - if client.UserId >= 0 { - chanacl.UserId = client.UserId - } else { - chanacl.Group = "$" + client.Hash + if client.IsRegistered() { + chanacl.UserId = client.UserId() + } else if client.HasCertificate() { + chanacl.Group = "$" + client.CertHash } - chanacl.UserId = client.UserId chanacl.Deny = Permission(NonePermission) chanacl.Allow = Permission(WritePermission | TraversePermission) @@ -1121,11 +1121,11 @@ func (server *Server) handleRequestBlob(client *Client, msg *Message) { // Request for user textures if len(blobreq.SessionTexture) > 0 { for _, sid := range blobreq.SessionTexture { - if user, ok := server.clients[sid]; ok { - if len(user.Texture) > 0 { + if target, ok := server.clients[sid]; ok { + if len(target.Texture) > 0 { userstate.Reset() - userstate.Session = proto.Uint32(uint32(user.Session)) - userstate.Texture = user.Texture + userstate.Session = proto.Uint32(uint32(target.Session)) + userstate.Texture = target.Texture if err := client.sendProtoMessage(MessageUserState, userstate); err != nil { client.Panic(err.String()) return @@ -1138,11 +1138,11 @@ func (server *Server) handleRequestBlob(client *Client, msg *Message) { // Request for user comments if len(blobreq.SessionComment) > 0 { for _, sid := range blobreq.SessionComment { - if user, ok := server.clients[sid]; ok { - if len(user.Comment) > 0 { + if target, ok := server.clients[sid]; ok { + if len(target.Comment) > 0 { userstate.Reset() - userstate.Session = proto.Uint32(uint32(user.Session)) - userstate.Comment = proto.String(user.Comment) + userstate.Session = proto.Uint32(uint32(target.Session)) + userstate.Comment = proto.String(target.Comment) if err := client.sendProtoMessage(MessageUserState, userstate); err != nil { client.Panic(err.String()) return diff --git a/murmurdb.go b/murmurdb.go index e3b63d0..f9c224e 100644 --- a/murmurdb.go +++ b/murmurdb.go @@ -31,7 +31,7 @@ const ( // Create a new Server from a Murmur SQLite database func NewServerFromSQLite(id int64, db *sqlite.Conn) (s *Server, err os.Error) { - s, err = NewServer(id, "", int(DefaultPort + id - 1)) + s, err = NewServer(id, "", int(DefaultPort+id-1)) if err != nil { return nil, err } @@ -73,8 +73,8 @@ func populateChannelsFromDatabase(server *Server, db *sqlite.Conn, parentId int) for stmt.Next() { var ( - name string - chanid int + name string + chanid int inherit bool ) err = stmt.Scan(&chanid, &name, &inherit) @@ -139,12 +139,12 @@ func populateChannelsFromDatabase(server *Server, db *sqlite.Conn, parentId int) for stmt.Next() { var ( - UserId string - Group string + UserId string + Group string ApplyHere bool - ApplySub bool - Allow int64 - Deny int64 + ApplySub bool + Allow int64 + Deny int64 ) if err := stmt.Scan(&UserId, &Group, &ApplyHere, &ApplySub, &Allow, &Deny); err != nil { return err @@ -184,9 +184,9 @@ func populateChannelsFromDatabase(server *Server, db *sqlite.Conn, parentId int) for stmt.Next() { var ( - GroupId int64 - Name string - Inherit bool + GroupId int64 + Name string + Inherit bool Inheritable bool ) @@ -216,7 +216,7 @@ func populateChannelsFromDatabase(server *Server, db *sqlite.Conn, parentId int) for stmt.Next() { var ( UserId int64 - Add bool + Add bool ) if err := stmt.Scan(&UserId, &Add); err != nil { @@ -256,7 +256,7 @@ func populateChannelLinkInfo(server *Server, db *sqlite.Conn) (err os.Error) { for stmt.Next() { var ( ChannelId int - LinkId int + LinkId int ) if err := stmt.Scan(&ChannelId, &LinkId); err != nil { return err @@ -292,12 +292,12 @@ func populateUsers(server *Server, db *sqlite.Conn) (err os.Error) { for stmt.Next() { var ( - UserId int64 - UserName string + UserId int64 + UserName string SHA1Password string - LastChannel int - Texture []byte - LastActive int64 + LastChannel int + Texture []byte + LastActive int64 ) err = stmt.Scan(&UserId, &UserName, &SHA1Password, &LastChannel, &Texture, &LastActive) @@ -340,7 +340,7 @@ func populateUsers(server *Server, db *sqlite.Conn) (err os.Error) { for stmt.Next() { var ( - Key int + Key int Value string ) @@ -355,7 +355,7 @@ func populateUsers(server *Server, db *sqlite.Conn) (err os.Error) { case UserInfoComment: // unhandled case UserInfoHash: - user.CertHash = "sha1-" + Value + user.CertHash = Value case UserInfoLastActive: // not a kv-pair (trigger) case UserInfoPassword: @@ -366,7 +366,5 @@ func populateUsers(server *Server, db *sqlite.Conn) (err os.Error) { } } - return } - diff --git a/server.go b/server.go index b0276e5..a7b0d01 100644 --- a/server.go +++ b/server.go @@ -104,16 +104,7 @@ func NewServer(id int64, addr string, port int) (s *Server, err os.Error) { s.MaxUsers = 10 s.Channels = make(map[int]*Channel) - s.root = s.NewChannel(0, "Root") - - /* - err = s.addChannelsFromDB(0) - if err != nil { - return nil, err - } - */ - s.aclcache = NewACLCache() return @@ -122,7 +113,7 @@ func NewServer(id int64, addr string, port int) (s *Server, err os.Error) { // Check whether password matches the set SuperUser password. func (server *Server) CheckSuperUserPassword(password string) bool { parts := strings.Split(server.superUserPassword, "$", -1) - if len(parts) != 3 { + if len(parts) != 3 { return false } @@ -174,7 +165,7 @@ func (server *Server) NewClient(conn net.Conn) (err os.Error) { client.msgchan = make(chan *Message) client.udprecv = make(chan []byte) - client.UserId = -1 + client.user = nil go client.receiver() go client.udpreceiver() @@ -334,10 +325,47 @@ func (server *Server) handleAuthenticate(client *Client, msg *Message) { if len(state.PeerCertificates) > 0 { hash := sha1.New() hash.Write(state.PeerCertificates[0].Raw) - client.Hash = hex.EncodeToString(hash.Sum()) + sum := hash.Sum() + client.CertHash = hex.EncodeToString(sum) } - log.Printf("hash=%s", client.Hash) + if client.Username == "SuperUser" { + if auth.Password == nil { + client.RejectAuth("WrongUserPW", "") + return + } else { + if server.CheckSuperUserPassword(*auth.Password) { + client.superUser = true + } else { + client.RejectAuth("WrongUserPW", "") + return + } + } + } else { + // First look up registration by name. + user, exists := server.UserNameMap[client.Username] + if exists { + if user.CertHash == client.CertHash { + client.user = user + } else { + client.Panic("Invalid cert hash for user") + return + } + } + + // Name matching didn't do. Try matching by certificate. + if client.user == nil { + user, exists := server.UserCertMap[client.CertHash] + if exists { + client.user = user + } + } + + // Found a user for this guy + if client.user != nil { + log.Printf("Client authenticated as %v", client.user.Name) + } + } // Setup the cryptstate for the client. client.crypt, err = cryptstate.New() @@ -383,29 +411,13 @@ func (server *Server) handleAuthenticate(client *Client, msg *Message) { server.hclients[host] = append(server.hclients[host], client) server.hmutex.Unlock() - // SuperUser login check - if client.Username == "SuperUser" { - // No password specified - if auth.Password == nil { - client.RejectAuth("WrongUserPW", "") - return - } else { - if server.CheckSuperUserPassword(*auth.Password) { - client.UserId = 0 - } else { - client.RejectAuth("WrongUserPW", "") - return - } - } - } - userstate := &mumbleproto.UserState{ Session: proto.Uint32(client.Session), Name: proto.String(client.Username), ChannelId: proto.Uint32(0), } - if client.UserId >= 0 { - userstate.UserId = proto.Uint32(uint32(client.UserId)) + if client.IsRegistered() { + userstate.UserId = proto.Uint32(uint32(client.UserId())) } server.userEnterChannel(client, server.root, userstate) if err := server.broadcastProtoMessage(MessageUserState, userstate); err != nil { @@ -417,7 +429,7 @@ func (server *Server) handleAuthenticate(client *Client, msg *Message) { sync := &mumbleproto.ServerSync{} sync.Session = proto.Uint32(client.Session) sync.MaxBandwidth = proto.Uint32(server.MaxBandwidth) - if client.UserId == 0 { + if client.IsSuperUser() { sync.Permissions = proto.Uint64(uint64(AllPermissions)) } else { server.HasPermission(client, server.root, EnterPermission) @@ -532,7 +544,7 @@ func (server *Server) sendUserList(client *Client) { // Send a client its permissions for channel. func (server *Server) sendClientPermissions(client *Client, channel *Channel) { // No caching for SuperUser - if client.UserId == 0 { + if client.IsSuperUser() { return } diff --git a/user.go b/user.go index 26828a3..4bded11 100644 --- a/user.go +++ b/user.go @@ -33,7 +33,7 @@ func NewUser(id uint32, name string) (user *User, err os.Error) { } return &User{ - Id: id, + Id: id, Name: name, - }, nil + },nil }