// Helper method for users entering new channels func (server *Server) userEnterChannel(client *Client, channel *Channel, userstate *mumbleproto.UserState) { if client.Channel == channel { return } oldchan := client.Channel if oldchan != nil { oldchan.RemoveClient(client) if oldchan.IsTemporary() && oldchan.IsEmpty() { server.tempRemove <- oldchan } } channel.AddClient(client) server.ClearCaches() server.UpdateFrozenUserLastChannel(client) canspeak := acl.HasPermission(&channel.ACL, client, acl.SpeakPermission) if canspeak == client.Suppress { client.Suppress = !canspeak userstate.Suppress = proto.Bool(client.Suppress) } server.sendClientPermissions(client, channel) if channel.parent != nil { server.sendClientPermissions(client, channel.parent) } }
func (server *Server) handleChannelRemoveMessage(client *Client, msg *Message) { chanremove := &mumbleproto.ChannelRemove{} err := proto.Unmarshal(msg.buf, chanremove) if err != nil { client.Panic(err) return } if chanremove.ChannelId == nil { return } channel, exists := server.Channels[int(*chanremove.ChannelId)] if !exists { return } if !acl.HasPermission(&channel.ACL, client, acl.WritePermission) { client.sendPermissionDenied(client, channel, acl.WritePermission) return } // Update datastore if !channel.IsTemporary() { server.DeleteFrozenChannel(channel) } server.RemoveChannel(channel) }
// Remove a channel func (server *Server) RemoveChannel(channel *Channel) { // Can't remove root if channel == server.RootChannel() { return } // Remove all links for _, linkedChannel := range channel.Links { delete(linkedChannel.Links, channel.Id) } // Remove all subchannels for _, subChannel := range channel.children { server.RemoveChannel(subChannel) } // Remove all clients for _, client := range channel.clients { target := channel.parent for target.parent != nil && !acl.HasPermission(&target.ACL, client, acl.EnterPermission) { target = target.parent } userstate := &mumbleproto.UserState{} userstate.Session = proto.Uint32(client.Session()) userstate.ChannelId = proto.Uint32(uint32(target.Id)) server.userEnterChannel(client, target, userstate) if err := server.broadcastProtoMessage(userstate); err != nil { server.Panicf("%v", err) } } // Remove the channel itself parent := channel.parent delete(parent.children, channel.Id) delete(server.Channels, channel.Id) chanremove := &mumbleproto.ChannelRemove{ ChannelId: proto.Uint32(uint32(channel.Id)), } if err := server.broadcastProtoMessage(chanremove); err != nil { server.Panicf("%v", err) } }
// Send the contents of the VoiceBroadcast to all targets specified in the // VoiceTarget. func (vt *VoiceTarget) SendVoiceBroadcast(vb *VoiceBroadcast) { buf := vb.buf client := vb.client server := client.server direct := vt.directCache fromChannels := vt.fromChannelsCache if direct == nil || fromChannels == nil { direct = make(map[uint32]*Client) fromChannels = make(map[uint32]*Client) for _, vtc := range vt.channels { channel := server.Channels[int(vtc.id)] if channel == nil { continue } if !vtc.subChannels && !vtc.links && vtc.onlyGroup == "" { if acl.HasPermission(&channel.ACL, client, acl.WhisperPermission) { for _, target := range channel.clients { fromChannels[target.Session()] = target } } } else { server.Printf("%v", vtc) newchans := make(map[int]*Channel) if vtc.links { newchans = channel.AllLinks() } else { newchans[channel.Id] = channel } if vtc.subChannels { subchans := channel.AllSubChannels() for k, v := range subchans { newchans[k] = v } } for _, newchan := range newchans { if acl.HasPermission(&newchan.ACL, client, acl.WhisperPermission) { for _, target := range newchan.clients { if vtc.onlyGroup == "" || acl.GroupMemberCheck(&newchan.ACL, &newchan.ACL, vtc.onlyGroup, target) { fromChannels[target.Session()] = target } } } } } } for _, session := range vt.sessions { target := server.clients[session] if target != nil { if _, alreadyInFromChannels := fromChannels[target.Session()]; !alreadyInFromChannels { direct[target.Session()] = target } } } // Make sure we don't send to ourselves. delete(direct, client.Session()) delete(fromChannels, client.Session()) if vt.directCache == nil { vt.directCache = direct } if vt.fromChannelsCache == nil { vt.fromChannelsCache = fromChannels } } kind := buf[0] & 0xe0 if len(fromChannels) > 0 { for _, target := range fromChannels { buf[0] = kind | 2 err := target.SendUDP(buf) if err != nil { target.Panicf("Unable to send UDP packet: %v", err.Error()) } } } if len(direct) > 0 { for _, target := range direct { buf[0] = kind | 2 target.SendUDP(buf) err := target.SendUDP(buf) if err != nil { target.Panicf("Unable to send UDP packet: %v", err.Error()) } } } }
// Broadcast text messages func (server *Server) handleTextMessage(client *Client, msg *Message) { txtmsg := &mumbleproto.TextMessage{} err := proto.Unmarshal(msg.buf, txtmsg) if err != nil { client.Panic(err) return } filtered, err := server.FilterText(*txtmsg.Message) if err != nil { client.sendPermissionDeniedType(mumbleproto.PermissionDenied_TextTooLong) return } if len(filtered) == 0 { return } txtmsg.Message = proto.String(filtered) clients := make(map[uint32]*Client) // Tree for _, chanid := range txtmsg.TreeId { if channel, ok := server.Channels[int(chanid)]; ok { if !acl.HasPermission(&channel.ACL, client, acl.TextMessagePermission) { client.sendPermissionDenied(client, channel, acl.TextMessagePermission) return } for _, target := range channel.clients { clients[target.Session()] = target } } } // Direct-to-channel for _, chanid := range txtmsg.ChannelId { if channel, ok := server.Channels[int(chanid)]; ok { if !acl.HasPermission(&channel.ACL, client, acl.TextMessagePermission) { client.sendPermissionDenied(client, channel, acl.TextMessagePermission) return } for _, target := range channel.clients { clients[target.Session()] = target } } } // Direct-to-clients for _, session := range txtmsg.Session { if target, ok := server.clients[session]; ok { if !acl.HasPermission(&target.Channel.ACL, client, acl.TextMessagePermission) { client.sendPermissionDenied(client, target.Channel, acl.TextMessagePermission) return } clients[session] = target } } // Remove ourselves delete(clients, client.Session()) for _, target := range clients { target.sendMessage(&mumbleproto.TextMessage{ Actor: proto.Uint32(client.Session()), Message: txtmsg.Message, }) } }
func (server *Server) handleBanListMessage(client *Client, msg *Message) { banlist := &mumbleproto.BanList{} err := proto.Unmarshal(msg.buf, banlist) if err != nil { client.Panic(err) return } rootChan := server.RootChannel() if !acl.HasPermission(&rootChan.ACL, client, acl.BanPermission) { client.sendPermissionDenied(client, rootChan, acl.BanPermission) return } if banlist.Query != nil && *banlist.Query != false { banlist.Reset() server.banlock.RLock() defer server.banlock.RUnlock() for _, ban := range server.Bans { entry := &mumbleproto.BanList_BanEntry{} entry.Address = ban.IP entry.Mask = proto.Uint32(uint32(ban.Mask)) entry.Name = proto.String(ban.Username) entry.Hash = proto.String(ban.CertHash) entry.Reason = proto.String(ban.Reason) entry.Start = proto.String(ban.ISOStartDate()) entry.Duration = proto.Uint32(ban.Duration) banlist.Bans = append(banlist.Bans, entry) } if err := client.sendMessage(banlist); err != nil { client.Panic("Unable to send BanList") } } else { server.banlock.Lock() defer server.banlock.Unlock() server.Bans = server.Bans[0:0] for _, entry := range banlist.Bans { ban := ban.Ban{} ban.IP = entry.Address ban.Mask = int(*entry.Mask) if entry.Name != nil { ban.Username = *entry.Name } if entry.Hash != nil { ban.CertHash = *entry.Hash } if entry.Reason != nil { ban.Reason = *entry.Reason } if entry.Start != nil { ban.SetISOStartDate(*entry.Start) } if entry.Duration != nil { ban.Duration = *entry.Duration } server.Bans = append(server.Bans, ban) } server.UpdateFrozenBans(server.Bans) client.Printf("Banlist updated") } }
// Handle user state changes func (server *Server) handleUserStateMessage(client *Client, msg *Message) { userstate := &mumbleproto.UserState{} err := proto.Unmarshal(msg.buf, userstate) if err != nil { client.Panic(err) return } actor, ok := server.clients[client.Session()] if !ok { server.Panic("Client not found in server's client map.") return } target := actor if userstate.Session != nil { target, ok = server.clients[*userstate.Session] if !ok { client.Panic("Invalid session in UserState message") return } } userstate.Session = proto.Uint32(target.Session()) userstate.Actor = proto.Uint32(actor.Session()) // Does it have a channel ID? if userstate.ChannelId != nil { // Destination channel dstChan, ok := server.Channels[int(*userstate.ChannelId)] if !ok { return } // If the user and the actor aren't the same, check whether the actor has MovePermission on // the user's curent channel. if actor != target && !acl.HasPermission(&target.Channel.ACL, actor, acl.MovePermission) { client.sendPermissionDenied(actor, target.Channel, acl.MovePermission) return } // Check whether the actor has MovePermission on dstChan. Check whether user has EnterPermission // on dstChan. if !acl.HasPermission(&dstChan.ACL, actor, acl.MovePermission) && !acl.HasPermission(&dstChan.ACL, target, acl.EnterPermission) { client.sendPermissionDenied(target, dstChan, acl.EnterPermission) return } maxChannelUsers := server.cfg.IntValue("MaxChannelUsers") if maxChannelUsers != 0 && len(dstChan.clients) >= maxChannelUsers { client.sendPermissionDeniedFallback(mumbleproto.PermissionDenied_ChannelFull, 0x010201, "Channel is full") return } } if userstate.Mute != nil || userstate.Deaf != nil || userstate.Suppress != nil || userstate.PrioritySpeaker != nil { // Disallow for SuperUser if target.IsSuperUser() { client.sendPermissionDeniedType(mumbleproto.PermissionDenied_SuperUser) return } // Check whether the actor has 'mutedeafen' permission on user's channel. if !acl.HasPermission(&target.Channel.ACL, actor, acl.MuteDeafenPermission) { client.sendPermissionDenied(actor, target.Channel, acl.MuteDeafenPermission) return } // Check if this was a suppress operation. Only the server can suppress users. if userstate.Suppress != nil { client.sendPermissionDenied(actor, target.Channel, acl.MuteDeafenPermission) return } } // Comment set/clear if userstate.Comment != nil { comment := *userstate.Comment // Clearing another user's comment. if target != actor { // Check if actor has 'move' permissions on the root channel. It is needed // to clear another user's comment. rootChan := server.RootChannel() if !acl.HasPermission(&rootChan.ACL, actor, acl.MovePermission) { client.sendPermissionDenied(actor, rootChan, acl.MovePermission) return } // Only allow empty text. if len(comment) > 0 { client.sendPermissionDeniedType(mumbleproto.PermissionDenied_TextTooLong) return } } filtered, err := server.FilterText(comment) if err != nil { client.sendPermissionDeniedType(mumbleproto.PermissionDenied_TextTooLong) return } userstate.Comment = proto.String(filtered) } // Texture change if userstate.Texture != nil { maximg := server.cfg.IntValue("MaxImageMessageLength") if maximg > 0 && len(userstate.Texture) > maximg { client.sendPermissionDeniedType(mumbleproto.PermissionDenied_TextTooLong) return } } // Registration if userstate.UserId != nil { // If user == actor, check for SelfRegisterPermission on root channel. // If user != actor, check for RegisterPermission permission on root channel. perm := acl.Permission(acl.RegisterPermission) if actor == target { perm = acl.Permission(acl.SelfRegisterPermission) } rootChan := server.RootChannel() if target.IsRegistered() || !acl.HasPermission(&rootChan.ACL, actor, perm) { client.sendPermissionDenied(actor, rootChan, perm) return } if !target.HasCertificate() { client.sendPermissionDeniedTypeUser(mumbleproto.PermissionDenied_MissingCertificate, target) return } } // Prevent self-targetting state changes to be applied to other users // That is, if actor != user, then: // Discard message if it has any of the following things set: // - SelfDeaf // - SelfMute // - Texture // - PluginContext // - PluginIdentity // - Recording 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") return } broadcast := false if userstate.Texture != nil && target.user != nil { key, err := blobStore.Put(userstate.Texture) if err != nil { server.Panicf("Blobstore error: %v", err) return } if target.user.TextureBlob != key { target.user.TextureBlob = key } else { userstate.Texture = nil } broadcast = true } if userstate.SelfDeaf != nil { target.SelfDeaf = *userstate.SelfDeaf if target.SelfDeaf { userstate.SelfDeaf = proto.Bool(true) target.SelfMute = true } broadcast = true } if userstate.SelfMute != nil { target.SelfMute = *userstate.SelfMute if !target.SelfMute { userstate.SelfDeaf = proto.Bool(false) target.SelfDeaf = false } } if userstate.PluginContext != nil { target.PluginContext = userstate.PluginContext } if userstate.PluginIdentity != nil { target.PluginIdentity = *userstate.PluginIdentity } if userstate.Comment != nil && target.user != nil { key, err := blobStore.Put([]byte(*userstate.Comment)) if err != nil { server.Panicf("Blobstore error: %v", err) } if target.user.CommentBlob != key { target.user.CommentBlob = key } else { userstate.Comment = nil } broadcast = true } if userstate.Mute != nil || userstate.Deaf != nil || userstate.Suppress != nil || userstate.PrioritySpeaker != nil { if userstate.Deaf != nil { target.Deaf = *userstate.Deaf if target.Deaf { userstate.Mute = proto.Bool(true) } } if userstate.Mute != nil { target.Mute = *userstate.Mute if !target.Mute { userstate.Deaf = proto.Bool(false) target.Deaf = false } } if userstate.Suppress != nil { target.Suppress = *userstate.Suppress } if userstate.PrioritySpeaker != nil { target.PrioritySpeaker = *userstate.PrioritySpeaker } broadcast = true } if userstate.Recording != nil && *userstate.Recording != target.Recording { target.Recording = *userstate.Recording txtmsg := &mumbleproto.TextMessage{} txtmsg.TreeId = append(txtmsg.TreeId, uint32(0)) if target.Recording { txtmsg.Message = proto.String(fmt.Sprintf("User '%s' started recording", target.ShownName())) } else { txtmsg.Message = proto.String(fmt.Sprintf("User '%s' stopped recording", target.ShownName())) } server.broadcastProtoMessageWithPredicate(txtmsg, func(client *Client) bool { return client.Version < 0x10203 }) broadcast = true } userRegistrationChanged := false if userstate.UserId != nil { uid, err := server.RegisterClient(target) if err != nil { client.Printf("Unable to register: %v", err) userstate.UserId = nil } else { userstate.UserId = proto.Uint32(uid) client.user = server.Users[uid] userRegistrationChanged = true } broadcast = true } if userstate.ChannelId != nil { channel, ok := server.Channels[int(*userstate.ChannelId)] if ok { server.userEnterChannel(target, channel, userstate) broadcast = true } } if broadcast { // This variable denotes the length of a zlib-encoded "old-style" texture. // Mumble and Murmur used qCompress and qUncompress from Qt to compress // 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. texture := userstate.Texture texlen := uint32(0) if texture != nil && len(texture) > 4 { texlen = uint32(texture[0])<<24 | uint32(texture[1])<<16 | uint32(texture[2])<<8 | uint32(texture[3]) } if texture != nil && len(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 err := server.broadcastProtoMessageWithPredicate(userstate, func(client *Client) bool { return client.Version < 0x10202 }) if err != nil { server.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 = texture } else { // Old style texture. We can send the message as-is. err := server.broadcastProtoMessageWithPredicate(userstate, func(client *Client) bool { return client.Version < 0x10202 }) if err != nil { server.Panic("Unable to broadcast UserState") } } // 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.Texture != nil && target.user != nil && target.user.HasTexture() { userstate.Texture = nil userstate.TextureHash = target.user.TextureBlobHashBytes() } else if target.user == nil { userstate.Texture = nil userstate.TextureHash = nil } // Ditto for comments. if userstate.Comment != nil && target.user.HasComment() { userstate.Comment = nil userstate.CommentHash = target.user.CommentBlobHashBytes() } else if target.user == nil { userstate.Comment = nil userstate.CommentHash = nil } if userRegistrationChanged { server.ClearCaches() } err := server.broadcastProtoMessageWithPredicate(userstate, func(client *Client) bool { return client.Version >= 0x10203 }) if err != nil { server.Panic("Unable to broadcast UserState") } } if target.IsRegistered() { server.UpdateFrozenUser(target, userstate) } }
// Handle a user remove packet. This can either be a client disconnecting, or a // user kicking or kick-banning another player. func (server *Server) handleUserRemoveMessage(client *Client, msg *Message) { userremove := &mumbleproto.UserRemove{} err := proto.Unmarshal(msg.buf, userremove) if err != nil { client.Panic(err) return } // Get the client to be removed. removeClient, ok := server.clients[*userremove.Session] if !ok { client.Panic("Invalid session in UserRemove message") return } isBan := false if userremove.Ban != nil { isBan = *userremove.Ban } // Check client's permissions perm := acl.Permission(acl.KickPermission) if isBan { perm = acl.Permission(acl.BanPermission) } rootChan := server.RootChannel() if removeClient.IsSuperUser() || !acl.HasPermission(&rootChan.ACL, client, perm) { client.sendPermissionDenied(client, rootChan, perm) return } if isBan { ban := ban.Ban{} ban.IP = removeClient.conn.RemoteAddr().(*net.TCPAddr).IP ban.Mask = 128 if userremove.Reason != nil { ban.Reason = *userremove.Reason } ban.Username = removeClient.ShownName() ban.CertHash = removeClient.CertHash() ban.Start = time.Now().Unix() ban.Duration = 0 server.banlock.Lock() server.Bans = append(server.Bans, ban) server.UpdateFrozenBans(server.Bans) server.banlock.Unlock() } userremove.Actor = proto.Uint32(uint32(client.Session())) if err = server.broadcastProtoMessage(userremove); err != nil { server.Panicf("Unable to broadcast UserRemove message") return } if isBan { client.Printf("Kick-banned %v (%v)", removeClient.ShownName(), removeClient.Session()) } else { client.Printf("Kicked %v (%v)", removeClient.ShownName(), removeClient.Session()) } removeClient.ForceDisconnect() }
// User list query, user rename, user de-register func (server *Server) handleUserList(client *Client, msg *Message) { userlist := &mumbleproto.UserList{} err := proto.Unmarshal(msg.buf, userlist) if err != nil { client.Panic(err) return } // Only users who are allowed to register other users can access the user list. rootChan := server.RootChannel() if !acl.HasPermission(&rootChan.ACL, client, acl.RegisterPermission) { client.sendPermissionDenied(client, rootChan, acl.RegisterPermission) return } // Query user list if len(userlist.Users) == 0 { for uid, user := range server.Users { if uid == 0 { continue } userlist.Users = append(userlist.Users, &mumbleproto.UserList_User{ UserId: proto.Uint32(uid), Name: proto.String(user.Name), }) } if err := client.sendMessage(userlist); err != nil { client.Panic(err) return } // Rename, registration removal } else { if len(userlist.Users) > 0 { tx := server.freezelog.BeginTx() for _, listUser := range userlist.Users { uid := *listUser.UserId if uid == 0 { continue } user, ok := server.Users[uid] if ok { if listUser.Name == nil { // De-register server.RemoveRegistration(uid) err := tx.Put(&freezer.UserRemove{Id: listUser.UserId}) if err != nil { server.Fatal(err) } } else { // Rename user // todo(mkrautz): Validate name. user.Name = *listUser.Name err := tx.Put(&freezer.User{Id: listUser.UserId, Name: listUser.Name}) if err != nil { server.Fatal(err) } } } } err := tx.Commit() if err != nil { server.Fatal(err) } } } }
// Handle channel state change. func (server *Server) handleChannelStateMessage(client *Client, msg *Message) { chanstate := &mumbleproto.ChannelState{} err := proto.Unmarshal(msg.buf, chanstate) if err != nil { client.Panic(err) return } var channel *Channel var parent *Channel var ok bool // Lookup channel for channel ID if chanstate.ChannelId != nil { channel, ok = server.Channels[int(*chanstate.ChannelId)] if !ok { client.Panic("Invalid channel specified in ChannelState message") return } } // Lookup parent if chanstate.Parent != nil { parent, ok = server.Channels[int(*chanstate.Parent)] if !ok { client.Panic("Invalid parent channel specified in ChannelState message") return } } // The server can't receive links through the links field in the ChannelState message, // because clients are supposed to send modifications to a channel's link state through // the links_add and links_remove fields. // Make sure the links field is clear so we can transmit the channel's link state in our reply. chanstate.Links = nil var name string var description string // Extract the description and perform sanity checks. if chanstate.Description != nil { description, err = server.FilterText(*chanstate.Description) if err != nil { client.sendPermissionDeniedType(mumbleproto.PermissionDenied_TextTooLong) return } } // Extract the the name of channel and check whether it's valid. // A valid channel name is a name that: // a) Isn't already used by a channel at the same level as the channel itself (that is, channels // that have a common parent can't have the same name. // b) A name must be a valid name on the server (it must pass the channel name regexp) if chanstate.Name != nil { name = *chanstate.Name // We don't allow renames for the root channel. if channel != nil && channel.Id != 0 { // Pick a parent. If the name change is part of a re-parent (a channel move), // we must evaluate the parent variable. Since we're explicitly exlcuding the root // channel from renames, channels that are the target of renames are guaranteed to have // a parent. evalp := parent if evalp == nil { evalp = channel.parent } for _, iter := range evalp.children { if iter.Name == name { client.sendPermissionDeniedType(mumbleproto.PermissionDenied_ChannelName) return } } } } // If the channel does not exist already, the ChannelState message is a create operation. if channel == nil { if parent == nil || len(name) == 0 { return } // Check whether the client has permission to create the channel in parent. perm := acl.Permission(acl.NonePermission) if *chanstate.Temporary { perm = acl.Permission(acl.TempChannelPermission) } else { perm = acl.Permission(acl.MakeChannelPermission) } if !acl.HasPermission(&parent.ACL, client, perm) { client.sendPermissionDenied(client, parent, perm) return } // Only registered users can create channels. if !client.IsRegistered() && !client.HasCertificate() { client.sendPermissionDeniedTypeUser(mumbleproto.PermissionDenied_MissingCertificate, client) return } // We can't add channels to a temporary channel if parent.IsTemporary() { client.sendPermissionDeniedType(mumbleproto.PermissionDenied_TemporaryChannel) return } key := "" if len(description) > 0 { key, err = blobStore.Put([]byte(description)) if err != nil { server.Panicf("Blobstore error: %v", err) } } // Add the new channel channel = server.AddChannel(name) channel.DescriptionBlob = key channel.temporary = *chanstate.Temporary channel.Position = int(*chanstate.Position) parent.AddChild(channel) // Add the creator to the channel's admin group if client.IsRegistered() { grp := acl.EmptyGroupWithName("admin") grp.Add[client.UserId()] = true channel.ACL.Groups["admin"] = grp } // If the client wouldn't have WritePermission in the just-created channel, // add a +write ACL for the user's hash. if !acl.HasPermission(&channel.ACL, client, acl.WritePermission) { aclEntry := acl.ACL{} aclEntry.ApplyHere = true aclEntry.ApplySubs = true if client.IsRegistered() { aclEntry.UserId = client.UserId() } else { aclEntry.Group = "$" + client.CertHash() } aclEntry.Deny = acl.Permission(acl.NonePermission) aclEntry.Allow = acl.Permission(acl.WritePermission | acl.TraversePermission) channel.ACL.ACLs = append(channel.ACL.ACLs, aclEntry) server.ClearCaches() } chanstate.ChannelId = proto.Uint32(uint32(channel.Id)) // Broadcast channel add server.broadcastProtoMessageWithPredicate(chanstate, func(client *Client) bool { return client.Version < 0x10202 }) // Remove description if client knows how to handle blobs. if chanstate.Description != nil && channel.HasDescription() { chanstate.Description = nil chanstate.DescriptionHash = channel.DescriptionBlobHashBytes() } server.broadcastProtoMessageWithPredicate(chanstate, func(client *Client) bool { return client.Version >= 0x10202 }) // If it's a temporary channel, move the creator in there. if channel.IsTemporary() { userstate := &mumbleproto.UserState{} userstate.Session = proto.Uint32(client.Session()) userstate.ChannelId = proto.Uint32(uint32(channel.Id)) server.userEnterChannel(client, channel, userstate) server.broadcastProtoMessage(userstate) } } else { // Edit existing channel. // First, check whether the actor has the neccessary permissions. // Name change. if chanstate.Name != nil { // The client can only rename the channel if it has WritePermission in the channel. // Also, clients cannot change the name of the root channel. if !acl.HasPermission(&channel.ACL, client, acl.WritePermission) || channel.Id == 0 { client.sendPermissionDenied(client, channel, acl.WritePermission) return } } // Description change if chanstate.Description != nil { if !acl.HasPermission(&channel.ACL, client, acl.WritePermission) { client.sendPermissionDenied(client, channel, acl.WritePermission) return } } // Position change if chanstate.Position != nil { if !acl.HasPermission(&channel.ACL, client, acl.WritePermission) { client.sendPermissionDenied(client, channel, acl.WritePermission) return } } // Parent change (channel move) if parent != nil { // No-op? if parent == channel.parent { return } // Make sure that channel we're operating on is not a parent of the new parent. iter := parent for iter != nil { if iter == channel { client.Panic("Illegal channel reparent") return } iter = iter.parent } // A temporary channel must not have any subchannels, so deny it. if parent.IsTemporary() { client.sendPermissionDeniedType(mumbleproto.PermissionDenied_TemporaryChannel) return } // To move a channel, the user must have WritePermission in the channel if !acl.HasPermission(&channel.ACL, client, acl.WritePermission) { client.sendPermissionDenied(client, channel, acl.WritePermission) return } // And the user must also have MakeChannel permission in the new parent if !acl.HasPermission(&parent.ACL, client, acl.MakeChannelPermission) { client.sendPermissionDenied(client, parent, acl.MakeChannelPermission) return } // If a sibling of parent already has this name, don't allow it. for _, iter := range parent.children { if iter.Name == channel.Name { client.sendPermissionDeniedType(mumbleproto.PermissionDenied_ChannelName) return } } } // Links linkadd := []*Channel{} linkremove := []*Channel{} if len(chanstate.LinksAdd) > 0 || len(chanstate.LinksRemove) > 0 { // Client must have permission to link if !acl.HasPermission(&channel.ACL, client, acl.LinkChannelPermission) { client.sendPermissionDenied(client, channel, acl.LinkChannelPermission) return } // Add any valid channels to linkremove slice for _, cid := range chanstate.LinksRemove { if iter, ok := server.Channels[int(cid)]; ok { linkremove = append(linkremove, iter) } } // Add any valid channels to linkadd slice for _, cid := range chanstate.LinksAdd { if iter, ok := server.Channels[int(cid)]; ok { if !acl.HasPermission(&iter.ACL, client, acl.LinkChannelPermission) { client.sendPermissionDenied(client, iter, acl.LinkChannelPermission) return } linkadd = append(linkadd, iter) } } } // Permission checks done! // Channel move if parent != nil { channel.parent.RemoveChild(channel) parent.AddChild(channel) } // Rename if chanstate.Name != nil { channel.Name = *chanstate.Name } // Description change if chanstate.Description != nil { if len(description) == 0 { channel.DescriptionBlob = "" } else { key, err := blobStore.Put([]byte(description)) if err != nil { server.Panicf("Blobstore error: %v", err) } channel.DescriptionBlob = key } } // Position change if chanstate.Position != nil { channel.Position = int(*chanstate.Position) } // Add links for _, iter := range linkadd { server.LinkChannels(channel, iter) } // Remove links for _, iter := range linkremove { server.UnlinkChannels(channel, iter) } // Broadcast the update server.broadcastProtoMessageWithPredicate(chanstate, func(client *Client) bool { return client.Version < 0x10202 }) // Remove description blob when sending to 1.2.2 >= users. Only send the blob hash. if channel.HasDescription() { chanstate.Description = nil chanstate.DescriptionHash = channel.DescriptionBlobHashBytes() } chanstate.DescriptionHash = channel.DescriptionBlobHashBytes() server.broadcastProtoMessageWithPredicate(chanstate, func(client *Client) bool { return client.Version >= 0x10202 }) } // Update channel in datastore if !channel.IsTemporary() { server.UpdateFrozenChannel(channel, chanstate) } }
// User stats message. Shown in the Mumble client when a // user right clicks a user and selects 'User Information'. func (server *Server) handleUserStatsMessage(client *Client, msg *Message) { stats := &mumbleproto.UserStats{} err := proto.Unmarshal(msg.buf, stats) if err != nil { client.Panic(err) return } if stats.Session == nil { return } target, exists := server.clients[*stats.Session] if !exists { return } extended := false // If a client is requesting a UserStats from itself, serve it the whole deal. if client == target { extended = true } // Otherwise, only send extended UserStats for people with +register permissions // on the root channel. rootChan := server.RootChannel() if acl.HasPermission(&rootChan.ACL, client, acl.RegisterPermission) { extended = true } // If the client wasn't granted extended permissions, only allow it to query // users in channels it can enter. if !extended && !acl.HasPermission(&target.Channel.ACL, client, acl.EnterPermission) { client.sendPermissionDenied(client, target.Channel, acl.EnterPermission) return } details := extended local := extended || target.Channel == client.Channel if stats.StatsOnly != nil && *stats.StatsOnly == true { details = false } stats.Reset() stats.Session = proto.Uint32(target.Session()) if details { if tlsconn := target.conn.(*tls.Conn); tlsconn != nil { state := tlsconn.ConnectionState() for i := len(state.PeerCertificates) - 1; i >= 0; i-- { stats.Certificates = append(stats.Certificates, state.PeerCertificates[i].Raw) } stats.StrongCertificate = proto.Bool(target.IsVerified()) } } if local { fromClient := &mumbleproto.UserStats_Stats{} fromClient.Good = proto.Uint32(target.crypt.Good) fromClient.Late = proto.Uint32(target.crypt.Late) fromClient.Lost = proto.Uint32(target.crypt.Lost) fromClient.Resync = proto.Uint32(target.crypt.Resync) stats.FromClient = fromClient fromServer := &mumbleproto.UserStats_Stats{} fromServer.Good = proto.Uint32(target.crypt.RemoteGood) fromServer.Late = proto.Uint32(target.crypt.RemoteLate) fromServer.Lost = proto.Uint32(target.crypt.RemoteLost) fromServer.Resync = proto.Uint32(target.crypt.RemoteResync) stats.FromServer = fromServer } stats.UdpPackets = proto.Uint32(target.UdpPackets) stats.TcpPackets = proto.Uint32(target.TcpPackets) stats.UdpPingAvg = proto.Float32(target.UdpPingAvg) stats.UdpPingVar = proto.Float32(target.UdpPingVar) stats.TcpPingAvg = proto.Float32(target.TcpPingAvg) stats.TcpPingVar = proto.Float32(target.TcpPingVar) if details { version := &mumbleproto.Version{} version.Version = proto.Uint32(target.Version) if len(target.ClientName) > 0 { version.Release = proto.String(target.ClientName) } if len(target.OSName) > 0 { version.Os = proto.String(target.OSName) if len(target.OSVersion) > 0 { version.OsVersion = proto.String(target.OSVersion) } } stats.Version = version stats.CeltVersions = target.codecs stats.Opus = proto.Bool(target.opus) stats.Address = target.tcpaddr.IP } // fixme(mkrautz): we don't do bandwidth tracking yet if err := client.sendMessage(stats); err != nil { client.Panic(err) return } }
// ACL set/query func (server *Server) handleAclMessage(client *Client, msg *Message) { pacl := &mumbleproto.ACL{} err := proto.Unmarshal(msg.buf, pacl) if err != nil { client.Panic(err) return } // Look up the channel this ACL message operates on. channel, ok := server.Channels[int(*pacl.ChannelId)] if !ok { return } // Does the user have permission to update or look at ACLs? if !acl.HasPermission(&channel.ACL, client, acl.WritePermission) && !(channel.parent != nil && acl.HasPermission(&channel.parent.ACL, client, acl.WritePermission)) { client.sendPermissionDenied(client, channel, acl.WritePermission) return } reply := &mumbleproto.ACL{} reply.ChannelId = proto.Uint32(uint32(channel.Id)) channels := []*Channel{} users := map[int]bool{} // Query the current ACL state for the channel if pacl.Query != nil && *pacl.Query != false { reply.InheritAcls = proto.Bool(channel.ACL.InheritACL) // Walk the channel tree to get all relevant channels. // (Stop if we reach a channel that doesn't have the InheritACL flag set) iter := channel for iter != nil { channels = append([]*Channel{iter}, channels...) if iter == channel || iter.ACL.InheritACL { iter = iter.parent } else { iter = nil } } // Construct the protobuf ChanACL objects corresponding to the ACLs defined // in our channel list. reply.Acls = []*mumbleproto.ACL_ChanACL{} for _, iter := range channels { for _, chanacl := range iter.ACL.ACLs { if iter == channel || chanacl.ApplySubs { mpacl := &mumbleproto.ACL_ChanACL{} mpacl.Inherited = proto.Bool(iter != channel) mpacl.ApplyHere = proto.Bool(chanacl.ApplyHere) mpacl.ApplySubs = proto.Bool(chanacl.ApplySubs) if chanacl.UserId >= 0 { mpacl.UserId = proto.Uint32(uint32(chanacl.UserId)) users[chanacl.UserId] = true } else { mpacl.Group = proto.String(chanacl.Group) } mpacl.Grant = proto.Uint32(uint32(chanacl.Allow)) mpacl.Deny = proto.Uint32(uint32(chanacl.Deny)) reply.Acls = append(reply.Acls, mpacl) } } } parent := channel.parent allnames := channel.ACL.GroupNames() // Construct the protobuf ChanGroups that we send back to the client. // Also constructs a usermap that is a set user ids from the channel's groups. reply.Groups = []*mumbleproto.ACL_ChanGroup{} for _, name := range allnames { var ( group acl.Group hasgroup bool pgroup acl.Group haspgroup bool ) group, hasgroup = channel.ACL.Groups[name] if parent != nil { pgroup, haspgroup = parent.ACL.Groups[name] } mpgroup := &mumbleproto.ACL_ChanGroup{} mpgroup.Name = proto.String(name) mpgroup.Inherit = proto.Bool(true) if hasgroup { mpgroup.Inherit = proto.Bool(group.Inherit) } mpgroup.Inheritable = proto.Bool(true) if hasgroup { mpgroup.Inheritable = proto.Bool(group.Inheritable) } mpgroup.Inherited = proto.Bool(haspgroup && pgroup.Inheritable) // Add the set of user ids that this group affects to the user map. // This is used later on in this function to send the client a QueryUsers // message that maps user ids to usernames. if hasgroup { toadd := map[int]bool{} for uid, _ := range group.Add { users[uid] = true toadd[uid] = true } for uid, _ := range group.Remove { users[uid] = true delete(toadd, uid) } for uid, _ := range toadd { mpgroup.Add = append(mpgroup.Add, uint32(uid)) } } if haspgroup { for uid, _ := range pgroup.MembersInContext(&parent.ACL) { users[uid] = true mpgroup.InheritedMembers = append(mpgroup.InheritedMembers, uint32(uid)) } } reply.Groups = append(reply.Groups, mpgroup) } if err := client.sendMessage(reply); err != nil { client.Panic(err) return } // Map the user ids in the user map to usernames of users. queryusers := &mumbleproto.QueryUsers{} for uid, _ := range users { user, ok := server.Users[uint32(uid)] if !ok { client.Printf("Invalid user id in ACL") continue } queryusers.Ids = append(queryusers.Ids, uint32(uid)) queryusers.Names = append(queryusers.Names, user.Name) } if len(queryusers.Ids) > 0 { client.sendMessage(queryusers) } // Set new groups and ACLs } else { // Get old temporary members oldtmp := map[string]map[int]bool{} for name, grp := range channel.ACL.Groups { oldtmp[name] = grp.Temporary } // Clear current ACLs and groups channel.ACL.ACLs = []acl.ACL{} channel.ACL.Groups = map[string]acl.Group{} // Add the received groups to the channel. channel.ACL.InheritACL = *pacl.InheritAcls for _, pbgrp := range pacl.Groups { changroup := acl.EmptyGroupWithName(*pbgrp.Name) changroup.Inherit = *pbgrp.Inherit changroup.Inheritable = *pbgrp.Inheritable for _, uid := range pbgrp.Add { changroup.Add[int(uid)] = true } for _, uid := range pbgrp.Remove { changroup.Remove[int(uid)] = true } if temp, ok := oldtmp[*pbgrp.Name]; ok { changroup.Temporary = temp } channel.ACL.Groups[changroup.Name] = changroup } // Add the received ACLs to the channel. for _, pbacl := range pacl.Acls { chanacl := acl.ACL{} chanacl.ApplyHere = *pbacl.ApplyHere chanacl.ApplySubs = *pbacl.ApplySubs if pbacl.UserId != nil { chanacl.UserId = int(*pbacl.UserId) } else { chanacl.Group = *pbacl.Group } chanacl.Deny = acl.Permission(*pbacl.Deny & acl.AllPermissions) chanacl.Allow = acl.Permission(*pbacl.Grant & acl.AllPermissions) channel.ACL.ACLs = append(channel.ACL.ACLs, chanacl) } // Clear the Server's caches server.ClearCaches() // Regular user? if !acl.HasPermission(&channel.ACL, client, acl.WritePermission) && client.IsRegistered() || client.HasCertificate() { chanacl := acl.ACL{} chanacl.ApplyHere = true chanacl.ApplySubs = false if client.IsRegistered() { chanacl.UserId = client.UserId() } else if client.HasCertificate() { chanacl.Group = "$" + client.CertHash() } chanacl.Deny = acl.Permission(acl.NonePermission) chanacl.Allow = acl.Permission(acl.WritePermission | acl.TraversePermission) channel.ACL.ACLs = append(channel.ACL.ACLs, chanacl) server.ClearCaches() } // Update freezer server.UpdateFrozenChannelACLs(channel) } }