// 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) } }
// Unfreeze unfreezes the contents of a freezer.Channel // into a channel. func (c *Channel) Unfreeze(fc *freezer.Channel) { if fc.Name != nil { c.Name = *fc.Name } if fc.Position != nil { c.Position = int(*fc.Position) } if fc.InheritAcl != nil { c.ACL.InheritACL = *fc.InheritAcl } if fc.DescriptionBlob != nil { c.DescriptionBlob = *fc.DescriptionBlob } // Update ACLs if fc.Acl != nil { c.ACL.ACLs = nil for _, facl := range fc.Acl { aclEntry := acl.ACL{} if facl.ApplyHere != nil { aclEntry.ApplyHere = *facl.ApplyHere } if facl.ApplySubs != nil { aclEntry.ApplySubs = *facl.ApplySubs } if facl.UserId != nil { aclEntry.UserId = int(*facl.UserId) } else { aclEntry.UserId = -1 } if facl.Group != nil { aclEntry.Group = *facl.Group } if facl.Deny != nil { aclEntry.Deny = acl.Permission(*facl.Deny) } if facl.Allow != nil { aclEntry.Allow = acl.Permission(*facl.Allow) } c.ACL.ACLs = append(c.ACL.ACLs, aclEntry) } } // Update groups if fc.Groups != nil { c.ACL.Groups = make(map[string]acl.Group) for _, fgrp := range fc.Groups { if fgrp.Name == nil { continue } g := acl.Group{} if fgrp.Inherit != nil { g.Inherit = *fgrp.Inherit } if fgrp.Inheritable != nil { g.Inheritable = *fgrp.Inheritable } for _, uid := range fgrp.Add { g.Add[int(uid)] = true } for _, uid := range fgrp.Remove { g.Remove[int(uid)] = true } c.ACL.Groups[g.Name] = g } } // Hook up links, but make them point to the channel itself. // We can't be sure that the channels the links point to exist // yet, so we delay hooking up the map 'correctly' to later. if fc.Links != nil { c.Links = make(map[int]*Channel) for _, link := range fc.Links { c.Links[int(link)] = c } } }
// 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) } }