From 3ef203a83f42ee19ca433f16cbc0b8bfdda7a20f Mon Sep 17 00:00:00 2001 From: Mikkel Krautz Date: Sat, 9 Feb 2013 21:33:32 +0100 Subject: [PATCH] grumble, pkg/acl: move ACL handling to the acl package. --- acl.go | 241 ---------------------------------- channel.go | 16 +-- client.go | 33 +++-- freeze.go | 91 ++++++------- message.go | 244 ++++++++++++++++++----------------- murmurdb.go | 25 ++-- pkg/acl/acl.go | 169 ++++++++++++++++++++++++ pkg/acl/context.go | 48 +++++++ group.go => pkg/acl/group.go | 203 ++++++++++++++--------------- pkg/acl/interfaces.go | 23 ++++ server.go | 103 +++++++-------- voicetarget.go | 20 +-- 12 files changed, 608 insertions(+), 608 deletions(-) delete mode 100644 acl.go create mode 100644 pkg/acl/acl.go create mode 100644 pkg/acl/context.go rename group.go => pkg/acl/group.go (55%) create mode 100644 pkg/acl/interfaces.go diff --git a/acl.go b/acl.go deleted file mode 100644 index 6a15547..0000000 --- a/acl.go +++ /dev/null @@ -1,241 +0,0 @@ -// Copyright (c) 2010 The Grumble Authors -// The use of this source code is goverened by a BSD-style -// license that can be found in the LICENSE-file. - -package main - -const ( - // Per-channel permissions - NonePermission = 0x0 - WritePermission = 0x1 - TraversePermission = 0x2 - EnterPermission = 0x4 - SpeakPermission = 0x8 - MuteDeafenPermission = 0x10 - MovePermission = 0x20 - MakeChannelPermission = 0x40 - LinkChannelPermission = 0x80 - WhisperPermission = 0x100 - TextMessagePermission = 0x200 - TempChannelPermission = 0x400 - - // Root channel only - KickPermission = 0x10000 - BanPermission = 0x20000 - RegisterPermission = 0x40000 - SelfRegisterPermission = 0x80000 - - // Extra flags - CachedPermission = 0x8000000 - AllPermissions = 0xf07ff -) - -type Permission uint32 - -// Check whether the given flags are set on perm -func (perm Permission) IsSet(check Permission) bool { - return perm&check == check -} - -// Check whether the Permission is marked as cached -// (i.e. that it was read from an ACLCache) -func (perm Permission) IsCached() bool { - return perm.IsSet(CachedPermission) -} - -// Clear a flag in the Permission -func (perm Permission) ClearFlag(flag Permission) { - perm &= ^flag -} - -// Clear the cache bit in the Permission -func (perm Permission) ClearCacheBit() { - perm.ClearFlag(CachedPermission) -} - -// A channel-to-permission mapping used in the ACLCache -type ChannelCache map[int]Permission - -// The ACLCache maps a user id to a ChannelCache map. -// The ChannelCache map maps a channel to its permissions. -type ACLCache map[uint32]ChannelCache - -// Creates a new ACLCache -func NewACLCache() ACLCache { - return make(map[uint32]ChannelCache) -} - -// Store a client's permissions for a particular channel. When the permissions are stored, -// the permission will have the CachedPermission flag added to it. -func (cache ACLCache) StorePermission(client *Client, channel *Channel, perm Permission) { - chancache, ok := cache[client.Session] - if !ok { - chancache = make(map[int]Permission) - cache[client.Session] = chancache - } - chancache[channel.Id] = perm | CachedPermission -} - -// Get a client's permissions for a partcular channel. NonePermission will be returned -// on error. To determine whether the returned value was retrieved from the cache, the -// caller must call IsCached() on the returned permission. -func (cache ACLCache) GetPermission(client *Client, channel *Channel) (perm Permission) { - chancache, ok := cache[client.Session] - perm = Permission(NonePermission) - if !ok { - return - } - perm, ok = chancache[channel.Id] - if !ok { - perm = Permission(NonePermission) - return - } - return -} - -// An ACL as defined on a channel. -// An ACL can be defined for either a user or a group. -type ChannelACL struct { - // The channel that the ChannelACL is defined on. - Channel *Channel - - // The user id that this ACL applied to. If this - // field is -1, the ACL is a group ACL. - UserId int - // The group that this ACL applies to. - Group string - - // The ApplyHere flag determines whether the ACL - // should apply to the current channel. - ApplyHere bool - // The ApplySubs flag determines whethr the ACL - // should apply to subchannels. - ApplySubs bool - - // The allowed permission flags. - Allow Permission - // The allowed permission flags. The Deny flags override - // permissions set in Allow. - Deny Permission -} - -// Returns true if the ACL is defined on a user -// (as opposed to a group) -func (acl ChannelACL) IsUserACL() bool { - return acl.UserId != -1 -} - -// Returns true if the ACL is defined on a channel -// (as opposed to a user) -func (acl ChannelACL) IsChannelACL() bool { - return !acl.IsUserACL() -} - -// Create a new ACL for channel. Does not add it to the channel's -// ACL list. This must be done manually. -func NewChannelACL(channel *Channel) *ChannelACL { - return &ChannelACL{ - Channel: channel, - UserId: -1, - } -} - -// Check whether client has permission perm on channel. Perm *must* be a single permission, -// and not a combination of permissions. -func (server *Server) HasPermission(client *Client, channel *Channel, perm Permission) (ok bool) { - // SuperUser can't speak or whisper, but everything else is OK - if client.IsSuperUser() { - if perm == SpeakPermission || perm == WhisperPermission { - return false - } - return true - } - - // First, try to look in the server's ACLCache. - granted := Permission(NonePermission) - cached := server.aclcache.GetPermission(client, channel) - if cached.IsCached() { - granted = cached - // The +write permission implies all permissions except for +speak and +whisper. - // For more information regarding this check, please see the comment regarding a simmilar - // check at the bottom of this function. - if perm != SpeakPermission && perm != WhisperPermission { - return (granted & (perm | WritePermission)) != NonePermission - } else { - return (granted & perm) != NonePermission - } - } - - // Default permissions - def := Permission(TraversePermission | EnterPermission | SpeakPermission | WhisperPermission | TextMessagePermission) - granted = def - - channels := []*Channel{} - iter := channel - for iter != nil { - channels = append([]*Channel{iter}, channels...) - iter = iter.parent - } - - traverse := true - write := false - - for _, iter := range channels { - // If the channel does not inherit any ACLs, use the default permissions. - if !iter.InheritACL { - granted = def - } - // Iterate through ACLs that are defined on iter. Note: this does not include - // ACLs that iter has inherited from a parent (unless there is also a group on - // iter with the same name, that changes the permissions a bit!) - for _, acl := range iter.ACL { - // Determine whether the ACL applies to client. If it is - // a user ACL and the user id of the ACL matches client, we're good to go. - // - // 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() - matchGroup := GroupMemberCheck(channel, iter, acl.Group, client) - if matchUser || matchGroup { - if acl.Allow.IsSet(TraversePermission) { - traverse = true - } - if acl.Deny.IsSet(TraversePermission) { - traverse = false - } - if acl.Allow.IsSet(WritePermission) { - write = true - } - if acl.Deny.IsSet(WritePermission) { - write = false - } - if (channel == iter && acl.ApplyHere) || (channel != iter && acl.ApplySubs) { - granted |= acl.Allow - granted &= ^acl.Deny - } - } - } - // If traverse is not set and the user doesn't have write permissions - // on the channel, the user will not have any permissions. - // This is because -traverse removes all permissions, and +write grants - // all permissions. - if !traverse && !write { - granted = NonePermission - break - } - } - - // Cache the result - server.aclcache.StorePermission(client, channel, granted) - - // The +write permission implies all permissions except for +speak and +whisper. - // This means that if the user has WritePermission, we should return true for all - // permissions exccept SpeakPermission and WhisperPermission. - if perm != SpeakPermission && perm != WhisperPermission { - return (granted & (perm | WritePermission)) != NonePermission - } else { - return (granted & perm) != NonePermission - } - - return false -} diff --git a/channel.go b/channel.go index e15dd09..6f18507 100644 --- a/channel.go +++ b/channel.go @@ -6,6 +6,7 @@ package main import ( "encoding/hex" + "mumbleapp.com/grumble/pkg/acl" ) // A Mumble channel @@ -20,11 +21,7 @@ type Channel struct { children map[int]*Channel // ACL - ACL []*ChannelACL - InheritACL bool - - // Groups - Groups map[string]*Group + ACL acl.Context // Links Links map[int]*Channel @@ -39,8 +36,7 @@ func NewChannel(id int, name string) (channel *Channel) { channel.Name = name channel.clients = make(map[uint32]*Client) channel.children = make(map[int]*Channel) - channel.ACL = []*ChannelACL{} - channel.Groups = make(map[string]*Group) + channel.ACL.Groups = make(map[string]acl.Group) channel.Links = make(map[int]*Channel) return } @@ -48,24 +44,26 @@ func NewChannel(id int, name string) (channel *Channel) { // Add a child channel to a channel func (channel *Channel) AddChild(child *Channel) { child.parent = channel + child.ACL.Parent = &channel.ACL channel.children[child.Id] = child } // Remove a child channel from a parent func (channel *Channel) RemoveChild(child *Channel) { child.parent = nil + child.ACL.Parent = nil delete(channel.children, child.Id) } // Add client func (channel *Channel) AddClient(client *Client) { - channel.clients[client.Session] = client + channel.clients[client.Session()] = client client.Channel = channel } // Remove client func (channel *Channel) RemoveClient(client *Client) { - delete(channel.clients, client.Session) + delete(channel.clients, client.Session()) client.Channel = nil } diff --git a/client.go b/client.go index ef18d97..d112cfd 100644 --- a/client.go +++ b/client.go @@ -13,6 +13,7 @@ import ( "errors" "io" "log" + "mumbleapp.com/grumble/pkg/acl" "mumbleapp.com/grumble/pkg/cryptstate" "mumbleapp.com/grumble/pkg/mumbleproto" "mumbleapp.com/grumble/pkg/packetdata" @@ -74,10 +75,10 @@ type Client struct { // Personal Username string - Session uint32 - CertHash string + session uint32 + certHash string Email string - Tokens []string + tokens []string Channel *Channel SelfMute bool SelfDeaf bool @@ -97,7 +98,7 @@ func (client *Client) IsRegistered() bool { // Does the client have a certificate? func (client *Client) HasCertificate() bool { - return len(client.CertHash) > 0 + return len(client.certHash) > 0 } // Is the client the SuperUser? @@ -108,6 +109,22 @@ func (client *Client) IsSuperUser() bool { return client.user.Id == 0 } +func (client *Client) ACLContext() *acl.Context { + return &client.Channel.ACL +} + +func (client *Client) CertHash() string { + return client.certHash +} + +func (client *Client) Session() uint32 { + return client.session +} + +func (client *Client) Tokens() []string { + return client.tokens +} + // Get the User ID of this client. // Returns -1 if the client is not a registered user. func (client *Client) UserId() int { @@ -252,7 +269,7 @@ func (c *Client) sendPermissionDeniedTypeUser(denyType mumbleproto.PermissionDen Type: denyType.Enum(), } if user != nil { - pd.Session = proto.Uint32(uint32(user.Session)) + pd.Session = proto.Uint32(uint32(user.Session())) } err := c.sendMessage(pd) if err != nil { @@ -262,11 +279,11 @@ func (c *Client) sendPermissionDeniedTypeUser(denyType mumbleproto.PermissionDen } // Send permission denied by who, what, where -func (c *Client) sendPermissionDenied(who *Client, where *Channel, what Permission) { +func (c *Client) sendPermissionDenied(who *Client, where *Channel, what acl.Permission) { pd := &mumbleproto.PermissionDenied{ Permission: proto.Uint32(uint32(what)), ChannelId: proto.Uint32(uint32(where.Id)), - Session: proto.Uint32(who.Session), + Session: proto.Uint32(who.Session()), Type: mumbleproto.PermissionDenied_Permission.Enum(), } err := c.sendMessage(pd) @@ -334,7 +351,7 @@ func (client *Client) udpRecvLoop() { incoming.Skip(size & 0x1fff) } - outgoing.PutUint32(client.Session) + outgoing.PutUint32(client.Session()) outgoing.PutBytes(buf[1 : 1+(len(buf)-1)]) outbuf[0] = buf[0] & 0xe0 // strip target diff --git a/freeze.go b/freeze.go index c3523b2..7aef1c6 100644 --- a/freeze.go +++ b/freeze.go @@ -10,6 +10,7 @@ import ( "io" "io/ioutil" "log" + "mumbleapp.com/grumble/pkg/acl" "mumbleapp.com/grumble/pkg/ban" "mumbleapp.com/grumble/pkg/freezer" "mumbleapp.com/grumble/pkg/mumbleproto" @@ -170,12 +171,12 @@ func (channel *Channel) Freeze() (fc *freezer.Channel, err error) { fc.ParentId = proto.Uint32(uint32(channel.parent.Id)) } fc.Position = proto.Int64(int64(channel.Position)) - fc.InheritAcl = proto.Bool(channel.InheritACL) + fc.InheritAcl = proto.Bool(channel.ACL.InheritACL) // Freeze the channel's ACLs acls := []*freezer.ACL{} - for _, acl := range channel.ACL { - facl, err := acl.Freeze() + for _, acl := range channel.ACL.ACLs { + facl, err := FreezeACL(acl) if err != nil { return nil, err } @@ -185,8 +186,8 @@ func (channel *Channel) Freeze() (fc *freezer.Channel, err error) { // Freeze the channel's groups groups := []*freezer.Group{} - for _, grp := range channel.Groups { - fgrp, err := grp.Freeze() + for _, grp := range channel.ACL.Groups { + fgrp, err := FreezeGroup(grp) if err != nil { return nil, err } @@ -217,7 +218,7 @@ func (c *Channel) Unfreeze(fc *freezer.Channel) { c.Position = int(*fc.Position) } if fc.InheritAcl != nil { - c.InheritACL = *fc.InheritAcl + c.ACL.InheritACL = *fc.InheritAcl } if fc.DescriptionBlob != nil { c.DescriptionBlob = *fc.DescriptionBlob @@ -225,41 +226,41 @@ func (c *Channel) Unfreeze(fc *freezer.Channel) { // Update ACLs if fc.Acl != nil { - c.ACL = nil + c.ACL.ACLs = nil for _, facl := range fc.Acl { - acl := NewChannelACL(c) + aclEntry := acl.ACL{} if facl.ApplyHere != nil { - acl.ApplyHere = *facl.ApplyHere + aclEntry.ApplyHere = *facl.ApplyHere } if facl.ApplySubs != nil { - acl.ApplySubs = *facl.ApplySubs + aclEntry.ApplySubs = *facl.ApplySubs } if facl.UserId != nil { - acl.UserId = int(*facl.UserId) + aclEntry.UserId = int(*facl.UserId) } else { - acl.UserId = -1 + aclEntry.UserId = -1 } if facl.Group != nil { - acl.Group = *facl.Group + aclEntry.Group = *facl.Group } if facl.Deny != nil { - acl.Deny = Permission(*facl.Deny) + aclEntry.Deny = acl.Permission(*facl.Deny) } if facl.Allow != nil { - acl.Allow = Permission(*facl.Allow) + aclEntry.Allow = acl.Permission(*facl.Allow) } - c.ACL = append(c.ACL, acl) + c.ACL.ACLs = append(c.ACL.ACLs, aclEntry) } } // Update groups if fc.Groups != nil { - c.Groups = make(map[string]*Group) + c.ACL.Groups = make(map[string]acl.Group) for _, fgrp := range fc.Groups { if fgrp.Name == nil { continue } - g := NewGroup(c, *fgrp.Name) + g := acl.Group{} if fgrp.Inherit != nil { g.Inherit = *fgrp.Inherit } @@ -272,7 +273,7 @@ func (c *Channel) Unfreeze(fc *freezer.Channel) { for _, uid := range fgrp.Remove { g.Remove[int(uid)] = true } - c.Groups[g.Name] = g + c.ACL.Groups[g.Name] = g } } @@ -331,40 +332,34 @@ func (u *User) Unfreeze(fu *freezer.User) { // Freeze a ChannelACL into it a flattened protobuf-based structure // ready to be persisted to disk. -func (acl *ChannelACL) Freeze() (facl *freezer.ACL, err error) { - facl = new(freezer.ACL) - - if acl.UserId != -1 { - facl.UserId = proto.Uint32(uint32(acl.UserId)) +func FreezeACL(aclEntry acl.ACL) (*freezer.ACL, error) { + frozenAcl := &freezer.ACL{} + if aclEntry.UserId != -1 { + frozenAcl.UserId = proto.Uint32(uint32(aclEntry.UserId)) } else { - facl.Group = proto.String(acl.Group) + frozenAcl.Group = proto.String(aclEntry.Group) } - facl.ApplyHere = proto.Bool(acl.ApplyHere) - facl.ApplySubs = proto.Bool(acl.ApplySubs) - facl.Allow = proto.Uint32(uint32(acl.Allow)) - facl.Deny = proto.Uint32(uint32(acl.Deny)) - - return + frozenAcl.ApplyHere = proto.Bool(aclEntry.ApplyHere) + frozenAcl.ApplySubs = proto.Bool(aclEntry.ApplySubs) + frozenAcl.Allow = proto.Uint32(uint32(aclEntry.Allow)) + frozenAcl.Deny = proto.Uint32(uint32(aclEntry.Deny)) + return frozenAcl, nil } // Freeze a Group into a flattened protobuf-based structure // ready to be persisted to disk. -func (group *Group) Freeze() (fgrp *freezer.Group, err error) { - fgrp = new(freezer.Group) - - fgrp.Name = proto.String(group.Name) - fgrp.Inherit = proto.Bool(group.Inherit) - fgrp.Inheritable = proto.Bool(group.Inheritable) - +func FreezeGroup(group acl.Group) (*freezer.Group, error) { + frozenGroup := &freezer.Group{} + frozenGroup.Name = proto.String(group.Name) + frozenGroup.Inherit = proto.Bool(group.Inherit) + frozenGroup.Inheritable = proto.Bool(group.Inheritable) for _, id := range group.AddUsers() { - fgrp.Add = append(fgrp.Add, uint32(id)) + frozenGroup.Add = append(frozenGroup.Add, uint32(id)) } - for _, id := range group.RemoveUsers() { - fgrp.Remove = append(fgrp.Remove, uint32(id)) + frozenGroup.Remove = append(frozenGroup.Remove, uint32(id)) } - - return + return frozenGroup, nil } // Create a new server from its on-disk representation. @@ -788,11 +783,11 @@ func (server *Server) UpdateFrozenChannelACLs(channel *Channel) { fc := &freezer.Channel{} fc.Id = proto.Uint32(uint32(channel.Id)) - fc.InheritAcl = proto.Bool(channel.InheritACL) + fc.InheritAcl = proto.Bool(channel.ACL.InheritACL) acls := []*freezer.ACL{} - for _, acl := range channel.ACL { - facl, err := acl.Freeze() + for _, aclEntry := range channel.ACL.ACLs { + facl, err := FreezeACL(aclEntry) if err != nil { return } @@ -801,8 +796,8 @@ func (server *Server) UpdateFrozenChannelACLs(channel *Channel) { fc.Acl = acls groups := []*freezer.Group{} - for _, grp := range channel.Groups { - fgrp, err := grp.Freeze() + for _, grp := range channel.ACL.Groups { + fgrp, err := FreezeGroup(grp) if err != nil { return } diff --git a/message.go b/message.go index 654b9d9..088f58f 100644 --- a/message.go +++ b/message.go @@ -9,6 +9,7 @@ import ( "crypto/aes" "crypto/tls" "fmt" + "mumbleapp.com/grumble/pkg/acl" "mumbleapp.com/grumble/pkg/ban" "mumbleapp.com/grumble/pkg/freezer" "mumbleapp.com/grumble/pkg/mumbleproto" @@ -129,8 +130,8 @@ func (server *Server) handleChannelRemoveMessage(client *Client, msg *Message) { return } - if !server.HasPermission(client, channel, WritePermission) { - client.sendPermissionDenied(client, channel, WritePermission) + if !acl.HasPermission(&channel.ACL, client, acl.WritePermission) { + client.sendPermissionDenied(client, channel, acl.WritePermission) return } @@ -225,13 +226,13 @@ func (server *Server) handleChannelStateMessage(client *Client, msg *Message) { } // Check whether the client has permission to create the channel in parent. - perm := Permission(NonePermission) + perm := acl.Permission(acl.NonePermission) if *chanstate.Temporary { - perm = Permission(TempChannelPermission) + perm = acl.Permission(acl.TempChannelPermission) } else { - perm = Permission(MakeChannelPermission) + perm = acl.Permission(acl.MakeChannelPermission) } - if !server.HasPermission(client, parent, perm) { + if !acl.HasPermission(&parent.ACL, client, perm) { client.sendPermissionDenied(client, parent, perm) return } @@ -265,26 +266,26 @@ func (server *Server) handleChannelStateMessage(client *Client, msg *Message) { // Add the creator to the channel's admin group if client.IsRegistered() { - grp := NewGroup(channel, "admin") + grp := acl.EmptyGroupWithName("admin") grp.Add[client.UserId()] = true - channel.Groups["admin"] = grp + 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 !server.HasPermission(client, channel, WritePermission) { - acl := NewChannelACL(channel) - acl.ApplyHere = true - acl.ApplySubs = true + if !acl.HasPermission(&channel.ACL, client, acl.WritePermission) { + aclEntry := acl.ACL{} + aclEntry.ApplyHere = true + aclEntry.ApplySubs = true if client.IsRegistered() { - acl.UserId = client.UserId() + aclEntry.UserId = client.UserId() } else { - acl.Group = "$" + client.CertHash + aclEntry.Group = "$" + client.CertHash() } - acl.Deny = Permission(NonePermission) - acl.Allow = Permission(WritePermission | TraversePermission) + aclEntry.Deny = acl.Permission(acl.NonePermission) + aclEntry.Allow = acl.Permission(acl.WritePermission | acl.TraversePermission) - channel.ACL = append(channel.ACL, acl) + channel.ACL.ACLs = append(channel.ACL.ACLs, aclEntry) server.ClearCaches() } @@ -308,7 +309,7 @@ func (server *Server) handleChannelStateMessage(client *Client, msg *Message) { // If it's a temporary channel, move the creator in there. if channel.IsTemporary() { userstate := &mumbleproto.UserState{} - userstate.Session = proto.Uint32(client.Session) + userstate.Session = proto.Uint32(client.Session()) userstate.ChannelId = proto.Uint32(uint32(channel.Id)) server.userEnterChannel(client, channel, userstate) server.broadcastProtoMessage(userstate) @@ -321,24 +322,24 @@ func (server *Server) handleChannelStateMessage(client *Client, msg *Message) { 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 !server.HasPermission(client, channel, WritePermission) || channel.Id == 0 { - client.sendPermissionDenied(client, channel, WritePermission) + 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 !server.HasPermission(client, channel, WritePermission) { - client.sendPermissionDenied(client, channel, WritePermission) + if !acl.HasPermission(&channel.ACL, client, acl.WritePermission) { + client.sendPermissionDenied(client, channel, acl.WritePermission) return } } // Position change if chanstate.Position != nil { - if !server.HasPermission(client, channel, WritePermission) { - client.sendPermissionDenied(client, channel, WritePermission) + if !acl.HasPermission(&channel.ACL, client, acl.WritePermission) { + client.sendPermissionDenied(client, channel, acl.WritePermission) return } } @@ -367,14 +368,14 @@ func (server *Server) handleChannelStateMessage(client *Client, msg *Message) { } // To move a channel, the user must have WritePermission in the channel - if !server.HasPermission(client, channel, WritePermission) { - client.sendPermissionDenied(client, channel, WritePermission) + 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 !server.HasPermission(client, parent, MakeChannelPermission) { - client.sendPermissionDenied(client, parent, MakeChannelPermission) + if !acl.HasPermission(&parent.ACL, client, acl.MakeChannelPermission) { + client.sendPermissionDenied(client, parent, acl.MakeChannelPermission) return } @@ -392,8 +393,8 @@ func (server *Server) handleChannelStateMessage(client *Client, msg *Message) { linkremove := []*Channel{} if len(chanstate.LinksAdd) > 0 || len(chanstate.LinksRemove) > 0 { // Client must have permission to link - if !server.HasPermission(client, channel, LinkChannelPermission) { - client.sendPermissionDenied(client, channel, LinkChannelPermission) + if !acl.HasPermission(&channel.ACL, client, acl.LinkChannelPermission) { + client.sendPermissionDenied(client, channel, acl.LinkChannelPermission) return } // Add any valid channels to linkremove slice @@ -405,8 +406,8 @@ func (server *Server) handleChannelStateMessage(client *Client, msg *Message) { // Add any valid channels to linkadd slice for _, cid := range chanstate.LinksAdd { if iter, ok := server.Channels[int(cid)]; ok { - if !server.HasPermission(client, iter, LinkChannelPermission) { - client.sendPermissionDenied(client, iter, LinkChannelPermission) + if !acl.HasPermission(&iter.ACL, client, acl.LinkChannelPermission) { + client.sendPermissionDenied(client, iter, acl.LinkChannelPermission) return } linkadd = append(linkadd, iter) @@ -500,12 +501,13 @@ func (server *Server) handleUserRemoveMessage(client *Client, msg *Message) { } // Check client's permissions - perm := Permission(KickPermission) + perm := acl.Permission(acl.KickPermission) if isBan { - perm = Permission(BanPermission) + perm = acl.Permission(acl.BanPermission) } - if removeClient.IsSuperUser() || !server.HasPermission(client, server.RootChannel(), perm) { - client.sendPermissionDenied(client, server.RootChannel(), perm) + rootChan := server.RootChannel() + if removeClient.IsSuperUser() || !acl.HasPermission(&rootChan.ACL, client, perm) { + client.sendPermissionDenied(client, rootChan, perm) return } @@ -517,7 +519,7 @@ func (server *Server) handleUserRemoveMessage(client *Client, msg *Message) { ban.Reason = *userremove.Reason } ban.Username = removeClient.ShownName() - ban.CertHash = removeClient.CertHash + ban.CertHash = removeClient.CertHash() ban.Start = time.Now().Unix() ban.Duration = 0 @@ -527,16 +529,16 @@ func (server *Server) handleUserRemoveMessage(client *Client, msg *Message) { server.banlock.Unlock() } - userremove.Actor = proto.Uint32(uint32(client.Session)) + 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) + client.Printf("Kick-banned %v (%v)", removeClient.ShownName(), removeClient.Session()) } else { - client.Printf("Kicked %v (%v)", removeClient.ShownName(), removeClient.Session) + client.Printf("Kicked %v (%v)", removeClient.ShownName(), removeClient.Session()) } removeClient.ForceDisconnect() @@ -551,7 +553,7 @@ func (server *Server) handleUserStateMessage(client *Client, msg *Message) { return } - actor, ok := server.clients[client.Session] + actor, ok := server.clients[client.Session()] if !ok { server.Panic("Client not found in server's client map.") return @@ -565,8 +567,8 @@ func (server *Server) handleUserStateMessage(client *Client, msg *Message) { } } - userstate.Session = proto.Uint32(target.Session) - userstate.Actor = proto.Uint32(actor.Session) + userstate.Session = proto.Uint32(target.Session()) + userstate.Actor = proto.Uint32(actor.Session()) // Does it have a channel ID? if userstate.ChannelId != nil { @@ -578,15 +580,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 != target && !server.HasPermission(actor, target.Channel, MovePermission) { - client.sendPermissionDenied(actor, target.Channel, MovePermission) + 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 !server.HasPermission(actor, dstChan, MovePermission) && !server.HasPermission(target, dstChan, EnterPermission) { - client.sendPermissionDenied(target, dstChan, EnterPermission) + if !acl.HasPermission(&dstChan.ACL, actor, acl.MovePermission) && !acl.HasPermission(&dstChan.ACL, target, acl.EnterPermission) { + client.sendPermissionDenied(target, dstChan, acl.EnterPermission) return } @@ -606,14 +608,14 @@ func (server *Server) handleUserStateMessage(client *Client, msg *Message) { } // Check whether the actor has 'mutedeafen' permission on user's channel. - if !server.HasPermission(actor, target.Channel, MuteDeafenPermission) { - client.sendPermissionDenied(actor, target.Channel, MuteDeafenPermission) + 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, MuteDeafenPermission) + client.sendPermissionDenied(actor, target.Channel, acl.MuteDeafenPermission) return } } @@ -626,8 +628,9 @@ func (server *Server) handleUserStateMessage(client *Client, msg *Message) { 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.RootChannel(), MovePermission) { - client.sendPermissionDenied(actor, server.RootChannel(), MovePermission) + rootChan := server.RootChannel() + if !acl.HasPermission(&rootChan.ACL, actor, acl.MovePermission) { + client.sendPermissionDenied(actor, rootChan, acl.MovePermission) return } @@ -660,17 +663,18 @@ func (server *Server) handleUserStateMessage(client *Client, msg *Message) { if userstate.UserId != nil { // If user == actor, check for SelfRegisterPermission on root channel. // If user != actor, check for RegisterPermission permission on root channel. - perm := Permission(RegisterPermission) + perm := acl.Permission(acl.RegisterPermission) if actor == target { - perm = Permission(SelfRegisterPermission) + perm = acl.Permission(acl.SelfRegisterPermission) } - if target.IsRegistered() || !server.HasPermission(actor, server.RootChannel(), perm) { - client.sendPermissionDenied(actor, server.RootChannel(), perm) + rootChan := server.RootChannel() + if target.IsRegistered() || !acl.HasPermission(&rootChan.ACL, actor, perm) { + client.sendPermissionDenied(actor, rootChan, perm) return } - if len(target.CertHash) == 0 { + if !target.HasCertificate() { client.sendPermissionDeniedTypeUser(mumbleproto.PermissionDenied_MissingCertificate, target) return } @@ -890,8 +894,9 @@ func (server *Server) handleBanListMessage(client *Client, msg *Message) { return } - if !server.HasPermission(client, server.RootChannel(), BanPermission) { - client.sendPermissionDenied(client, server.RootChannel(), BanPermission) + rootChan := server.RootChannel() + if !acl.HasPermission(&rootChan.ACL, client, acl.BanPermission) { + client.sendPermissionDenied(client, rootChan, acl.BanPermission) return } @@ -974,12 +979,12 @@ func (server *Server) handleTextMessage(client *Client, msg *Message) { // Tree for _, chanid := range txtmsg.TreeId { if channel, ok := server.Channels[int(chanid)]; ok { - if !server.HasPermission(client, channel, TextMessagePermission) { - client.sendPermissionDenied(client, channel, TextMessagePermission) + if !acl.HasPermission(&channel.ACL, client, acl.TextMessagePermission) { + client.sendPermissionDenied(client, channel, acl.TextMessagePermission) return } for _, target := range channel.clients { - clients[target.Session] = target + clients[target.Session()] = target } } } @@ -987,12 +992,12 @@ func (server *Server) handleTextMessage(client *Client, msg *Message) { // Direct-to-channel for _, chanid := range txtmsg.ChannelId { if channel, ok := server.Channels[int(chanid)]; ok { - if !server.HasPermission(client, channel, TextMessagePermission) { - client.sendPermissionDenied(client, channel, TextMessagePermission) + if !acl.HasPermission(&channel.ACL, client, acl.TextMessagePermission) { + client.sendPermissionDenied(client, channel, acl.TextMessagePermission) return } for _, target := range channel.clients { - clients[target.Session] = target + clients[target.Session()] = target } } } @@ -1000,8 +1005,8 @@ func (server *Server) handleTextMessage(client *Client, msg *Message) { // Direct-to-clients for _, session := range txtmsg.Session { if target, ok := server.clients[session]; ok { - if !server.HasPermission(client, target.Channel, TextMessagePermission) { - client.sendPermissionDenied(client, target.Channel, TextMessagePermission) + if !acl.HasPermission(&target.Channel.ACL, client, acl.TextMessagePermission) { + client.sendPermissionDenied(client, target.Channel, acl.TextMessagePermission) return } clients[session] = target @@ -1009,11 +1014,11 @@ func (server *Server) handleTextMessage(client *Client, msg *Message) { } // Remove ourselves - delete(clients, client.Session) + delete(clients, client.Session()) for _, target := range clients { target.sendMessage(&mumbleproto.TextMessage{ - Actor: proto.Uint32(client.Session), + Actor: proto.Uint32(client.Session()), Message: txtmsg.Message, }) } @@ -1021,22 +1026,22 @@ func (server *Server) handleTextMessage(client *Client, msg *Message) { // ACL set/query func (server *Server) handleAclMessage(client *Client, msg *Message) { - acl := &mumbleproto.ACL{} - err := proto.Unmarshal(msg.buf, acl) + 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(*acl.ChannelId)] + channel, ok := server.Channels[int(*pacl.ChannelId)] if !ok { return } // Does the user have permission to update or look at ACLs? - if !server.HasPermission(client, channel, WritePermission) && !(channel.parent != nil && server.HasPermission(client, channel.parent, WritePermission)) { - client.sendPermissionDenied(client, channel, WritePermission) + 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 } @@ -1047,14 +1052,14 @@ func (server *Server) handleAclMessage(client *Client, msg *Message) { users := map[int]bool{} // Query the current ACL state for the channel - if acl.Query != nil && *acl.Query != false { - reply.InheritAcls = proto.Bool(channel.InheritACL) + 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.InheritACL { + if iter == channel || iter.ACL.InheritACL { iter = iter.parent } else { iter = nil @@ -1065,7 +1070,7 @@ func (server *Server) handleAclMessage(client *Client, msg *Message) { // in our channel list. reply.Acls = []*mumbleproto.ACL_ChanACL{} for _, iter := range channels { - for _, chanacl := range iter.ACL { + for _, chanacl := range iter.ACL.ACLs { if iter == channel || chanacl.ApplySubs { mpacl := &mumbleproto.ACL_ChanACL{} mpacl.Inherited = proto.Bool(iter != channel) @@ -1085,40 +1090,43 @@ func (server *Server) handleAclMessage(client *Client, msg *Message) { } parent := channel.parent - allnames := channel.GroupNames() + 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 { + for _, name := range allnames { var ( - group *Group - pgroup *Group + group acl.Group + hasgroup bool + pgroup acl.Group + haspgroup bool ) - group = channel.Groups[name] + + group, hasgroup = channel.ACL.Groups[name] if parent != nil { - pgroup = parent.Groups[name] + pgroup, haspgroup = parent.ACL.Groups[name] } mpgroup := &mumbleproto.ACL_ChanGroup{} mpgroup.Name = proto.String(name) mpgroup.Inherit = proto.Bool(true) - if group != nil { + if hasgroup { mpgroup.Inherit = proto.Bool(group.Inherit) } mpgroup.Inheritable = proto.Bool(true) - if group != nil { + if hasgroup { mpgroup.Inheritable = proto.Bool(group.Inheritable) } - mpgroup.Inherited = proto.Bool(pgroup != nil && pgroup.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 group != nil { + if hasgroup { toadd := map[int]bool{} for uid, _ := range group.Add { users[uid] = true @@ -1132,8 +1140,8 @@ func (server *Server) handleAclMessage(client *Client, msg *Message) { mpgroup.Add = append(mpgroup.Add, uint32(uid)) } } - if pgroup != nil { - for uid, _ := range pgroup.Members() { + if haspgroup { + for uid, _ := range pgroup.MembersInContext(&parent.ACL) { users[uid] = true mpgroup.InheritedMembers = append(mpgroup.InheritedMembers, uint32(uid)) } @@ -1167,18 +1175,18 @@ func (server *Server) handleAclMessage(client *Client, msg *Message) { // Get old temporary members oldtmp := map[string]map[int]bool{} - for name, grp := range channel.Groups { + for name, grp := range channel.ACL.Groups { oldtmp[name] = grp.Temporary } // Clear current ACLs and groups - channel.ACL = []*ChannelACL{} - channel.Groups = map[string]*Group{} + channel.ACL.ACLs = []acl.ACL{} + channel.ACL.Groups = map[string]acl.Group{} // Add the received groups to the channel. - channel.InheritACL = *acl.InheritAcls - for _, pbgrp := range acl.Groups { - changroup := NewGroup(channel, *pbgrp.Name) + channel.ACL.InheritACL = *pacl.InheritAcls + for _, pbgrp := range pacl.Groups { + changroup := acl.EmptyGroupWithName(*pbgrp.Name) changroup.Inherit = *pbgrp.Inherit changroup.Inheritable = *pbgrp.Inheritable @@ -1192,12 +1200,11 @@ func (server *Server) handleAclMessage(client *Client, msg *Message) { changroup.Temporary = temp } - channel.Groups[changroup.Name] = changroup + channel.ACL.Groups[changroup.Name] = changroup } // Add the received ACLs to the channel. - for _, pbacl := range acl.Acls { - chanacl := NewChannelACL(channel) - + for _, pbacl := range pacl.Acls { + chanacl := acl.ACL{} chanacl.ApplyHere = *pbacl.ApplyHere chanacl.ApplySubs = *pbacl.ApplySubs if pbacl.UserId != nil { @@ -1205,30 +1212,29 @@ func (server *Server) handleAclMessage(client *Client, msg *Message) { } else { chanacl.Group = *pbacl.Group } - chanacl.Deny = Permission(*pbacl.Deny & AllPermissions) - chanacl.Allow = Permission(*pbacl.Grant & AllPermissions) + chanacl.Deny = acl.Permission(*pbacl.Deny & acl.AllPermissions) + chanacl.Allow = acl.Permission(*pbacl.Grant & acl.AllPermissions) - channel.ACL = append(channel.ACL, chanacl) + channel.ACL.ACLs = append(channel.ACL.ACLs, chanacl) } // Clear the Server's caches server.ClearCaches() // Regular user? - if !server.HasPermission(client, channel, WritePermission) && client.IsRegistered() || client.HasCertificate() { - chanacl := NewChannelACL(channel) - + 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.Group = "$" + client.CertHash() } - chanacl.Deny = Permission(NonePermission) - chanacl.Allow = Permission(WritePermission | TraversePermission) + chanacl.Deny = acl.Permission(acl.NonePermission) + chanacl.Allow = acl.Permission(acl.WritePermission | acl.TraversePermission) - channel.ACL = append(channel.ACL, chanacl) + channel.ACL.ACLs = append(channel.ACL.ACLs, chanacl) server.ClearCaches() } @@ -1299,14 +1305,15 @@ func (server *Server) handleUserStatsMessage(client *Client, msg *Message) { } // Otherwise, only send extended UserStats for people with +register permissions // on the root channel. - if server.HasPermission(client, server.RootChannel(), RegisterPermission) { + 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 && !server.HasPermission(client, target.Channel, EnterPermission) { - client.sendPermissionDenied(client, target.Channel, EnterPermission) + if !extended && !acl.HasPermission(&target.Channel.ACL, client, acl.EnterPermission) { + client.sendPermissionDenied(client, target.Channel, acl.EnterPermission) return } @@ -1318,7 +1325,7 @@ func (server *Server) handleUserStatsMessage(client *Client, msg *Message) { } stats.Reset() - stats.Session = proto.Uint32(target.Session) + stats.Session = proto.Uint32(target.Session()) if details { if tlsconn := target.conn.(*tls.Conn); tlsconn != nil { @@ -1472,7 +1479,7 @@ func (server *Server) handleRequestBlob(client *Client, msg *Message) { return } userstate.Reset() - userstate.Session = proto.Uint32(uint32(target.Session)) + userstate.Session = proto.Uint32(uint32(target.Session())) userstate.Texture = buf if err := client.sendMessage(userstate); err != nil { client.Panic(err) @@ -1497,7 +1504,7 @@ func (server *Server) handleRequestBlob(client *Client, msg *Message) { return } userstate.Reset() - userstate.Session = proto.Uint32(uint32(target.Session)) + userstate.Session = proto.Uint32(uint32(target.Session())) userstate.Comment = proto.String(string(buf)) if err := client.sendMessage(userstate); err != nil { client.Panic(err) @@ -1543,8 +1550,9 @@ func (server *Server) handleUserList(client *Client, msg *Message) { } // Only users who are allowed to register other users can access the user list. - if !server.HasPermission(client, server.RootChannel(), RegisterPermission) { - client.sendPermissionDenied(client, server.RootChannel(), RegisterPermission) + rootChan := server.RootChannel() + if !acl.HasPermission(&rootChan.ACL, client, acl.RegisterPermission) { + client.sendPermissionDenied(client, rootChan, acl.RegisterPermission) return } diff --git a/murmurdb.go b/murmurdb.go index 440aa0a..3307df5 100644 --- a/murmurdb.go +++ b/murmurdb.go @@ -13,6 +13,7 @@ import ( "database/sql" "errors" "log" + "mumbleapp.com/grumble/pkg/acl" "mumbleapp.com/grumble/pkg/ban" "net" "os" @@ -197,23 +198,23 @@ func populateChannelACLFromDatabase(server *Server, c *Channel, db *sql.DB) erro return err } - acl := NewChannelACL(c) - acl.ApplyHere = ApplyHere - acl.ApplySubs = ApplySub + aclEntry := acl.ACL{} + aclEntry.ApplyHere = ApplyHere + aclEntry.ApplySubs = ApplySub if len(UserId) > 0 { - acl.UserId, err = strconv.Atoi(UserId) + aclEntry.UserId, err = strconv.Atoi(UserId) if err != nil { return err } } else if len(Group) > 0 { - acl.Group = Group + aclEntry.Group = Group } else { return errors.New("Invalid ACL: Neither Group or UserId specified") } - acl.Deny = Permission(Deny) - acl.Allow = Permission(Allow) - c.ACL = append(c.ACL, acl) + aclEntry.Deny = acl.Permission(Deny) + aclEntry.Allow = acl.Permission(Allow) + c.ACL.ACLs = append(c.ACL.ACLs, aclEntry) } return nil @@ -231,7 +232,7 @@ func populateChannelGroupsFromDatabase(server *Server, c *Channel, db *sql.DB) e return err } - groups := make(map[int64]*Group) + groups := make(map[int64]acl.Group) for rows.Next() { var ( @@ -245,10 +246,10 @@ func populateChannelGroupsFromDatabase(server *Server, c *Channel, db *sql.DB) e return err } - g := NewGroup(c, Name) + g := acl.EmptyGroupWithName(Name) g.Inherit = Inherit g.Inheritable = Inheritable - c.Groups[g.Name] = g + c.ACL.Groups[g.Name] = g groups[GroupId] = g } @@ -314,7 +315,7 @@ func populateChannelsFromDatabase(server *Server, db *sql.DB, parentId int) erro c := NewChannel(chanid, name) server.Channels[c.Id] = c - c.InheritACL = inherit + c.ACL.InheritACL = inherit parent.AddChild(c) } diff --git a/pkg/acl/acl.go b/pkg/acl/acl.go new file mode 100644 index 0000000..06771a3 --- /dev/null +++ b/pkg/acl/acl.go @@ -0,0 +1,169 @@ +// Copyright (c) 2010-2013 The Grumble Authors +// The use of this source code is goverened by a BSD-style +// license that can be found in the LICENSE-file. + +package acl + +const ( + // Per-channel permissions + NonePermission = 0x0 + WritePermission = 0x1 + TraversePermission = 0x2 + EnterPermission = 0x4 + SpeakPermission = 0x8 + MuteDeafenPermission = 0x10 + MovePermission = 0x20 + MakeChannelPermission = 0x40 + LinkChannelPermission = 0x80 + WhisperPermission = 0x100 + TextMessagePermission = 0x200 + TempChannelPermission = 0x400 + + // Root channel only + KickPermission = 0x10000 + BanPermission = 0x20000 + RegisterPermission = 0x40000 + SelfRegisterPermission = 0x80000 + + // Extra flags + CachedPermission = 0x8000000 + AllPermissions = 0xf07ff +) + +// Permission represents a permission in Mumble's ACL system. +type Permission uint32 + +// Check whether the given flags are set on perm +func (perm Permission) isSet(check Permission) bool { + return perm&check == check +} + +// IsCached checks whether the ACL has its cache bit set, +// signalling that it was returned from an ACLCache. +func (perm Permission) IsCached() bool { + return perm.isSet(CachedPermission) +} + +// Clean returns a Permission that has its cache bit cleared. +func (perm Permission) Clean() Permission { + return perm ^Permission(CachedPermission) +} + +// An ACL as defined in an ACL context. +// An ACL can be defined for either a user or a group. +type ACL struct { + // The user id that this ACL applied to. If this + // field is -1, the ACL is a group ACL. + UserId int + // The group that this ACL applies to. + Group string + + // The ApplyHere flag determines whether the ACL + // should apply to the current channel. + ApplyHere bool + // The ApplySubs flag determines whethr the ACL + // should apply to subchannels. + ApplySubs bool + + // The allowed permission flags. + Allow Permission + // The allowed permission flags. The Deny flags override + // permissions set in Allow. + Deny Permission +} + +// IsUserACL returns true if the ACL is defined for a user, +// as opposed to a group. +func (acl *ACL) IsUserACL() bool { + return acl.UserId != -1 +} + +// IsChannelACL returns true if the ACL is defined for a group, +// as opposed to a user. +func (acl *ACL) IsChannelACL() bool { + return !acl.IsUserACL() +} + +// HasPermission checks whether the given user has permission perm in the given context. +// The permission perm must be a single permission and not a combination of permissions. +func HasPermission(ctx *Context, user User, perm Permission) bool { + // We can't check permissions on a nil ctx. + if (ctx == nil) { + panic("acl: HasPermission got nil context") + } + + // SuperUser can't speak or whisper, but everything else is OK + if user.UserId() == 0 { + if perm == SpeakPermission || perm == WhisperPermission { + return false + } + return true + } + + // Default permissions + defaults := Permission(TraversePermission | EnterPermission | SpeakPermission | WhisperPermission | TextMessagePermission) + granted := defaults + contexts := buildChain(ctx) + origCtx := ctx + + traverse := true + write := false + + for _, ctx := range contexts { + // If the context does not inherit any ACLs, use the default permissions. + if !ctx.InheritACL { + granted = defaults + } + // Iterate through ACLs that are defined on ctx. Note: this does not include + // ACLs that iter has inherited from a parent (unless there is also a group on + // iter with the same name, that changes the permissions a bit!) + for _, acl := range ctx.ACLs { + // Determine whether the ACL applies to user. + // If it is a user ACL and the user id of the ACL + // matches user's id, we're good to go. + // + // 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 == user.UserId() + matchGroup := GroupMemberCheck(origCtx, ctx, acl.Group, user) + if matchUser || matchGroup { + if acl.Allow.isSet(TraversePermission) { + traverse = true + } + if acl.Deny.isSet(TraversePermission) { + traverse = false + } + if acl.Allow.isSet(WritePermission) { + write = true + } + if acl.Deny.isSet(WritePermission) { + write = false + } + if (origCtx == ctx && acl.ApplyHere) || (origCtx != ctx && acl.ApplySubs) { + granted |= acl.Allow + granted &= ^acl.Deny + } + } + } + // If traverse is not set and the user doesn't have write permissions + // on the channel, the user will not have any permissions. + // This is because -traverse removes all permissions, and +write grants + // all permissions. + if !traverse && !write { + granted = NonePermission + break + } + } + + // The +write permission implies all permissions except for +speak and +whisper. + // This means that if the user has WritePermission, we should return true for all + // permissions exccept SpeakPermission and WhisperPermission. + if perm != SpeakPermission && perm != WhisperPermission { + return (granted & (perm | WritePermission)) != NonePermission + } else { + return (granted & perm) != NonePermission + } + + return false +} diff --git a/pkg/acl/context.go b/pkg/acl/context.go new file mode 100644 index 0000000..336acea --- /dev/null +++ b/pkg/acl/context.go @@ -0,0 +1,48 @@ +// Copyright (c) 2010-2013 The Grumble Authors +// The use of this source code is goverened by a BSD-style +// license that can be found in the LICENSE-file. + +package acl + +// Context represents a context in which ACLs can +// be understood. Typically embedded into a type +// that represents a Mumble channel. +type Context struct { + // Parent points to the context's parent. + // May be nil if the Context does not have a parent. + Parent *Context + + // ACLs is the Context's list of ACL entries. + ACLs []ACL + + // Groups is the Context's representation of groups. + // It is indexed by the Group's name. + Groups map[string]Group + + // InheritACL determines whether this context should + // inherit ACLs from its parent. + InheritACL bool +} + +// indexOf finds the index of the context ctx in the context chain contexts. +// Returns -1 if the given context was not found in the context chain. +func indexOf(contexts []*Context, ctx *Context) int { + for i, iter := range contexts { + if iter == ctx { + return i + } + } + return -1 +} + +// buildChain walks from the context ctx back through all of its parents, +// collecting them all in a slice. The first element of the returned +// slice is the final ancestor (it has a nil Parent). +func buildChain(ctx *Context) []*Context { + chain := []*Context{} + for ctx != nil { + chain = append([]*Context{ctx}, chain...) + ctx = ctx.Parent + } + return chain +} diff --git a/group.go b/pkg/acl/group.go similarity index 55% rename from group.go rename to pkg/acl/group.go index fc1ebe3..61f06c0 100644 --- a/group.go +++ b/pkg/acl/group.go @@ -1,8 +1,7 @@ -// Copyright (c) 2010 The Grumble Authors +// Copyright (c) 2010-2013 The Grumble Authors // The use of this source code is goverened by a BSD-style // license that can be found in the LICENSE-file. - -package main +package acl import ( "log" @@ -10,10 +9,8 @@ import ( "strings" ) +// Group represents a Group in an Context. type Group struct { - // The channel that this group resides in - Channel *Channel - // The name of this group Name string @@ -33,11 +30,9 @@ type Group struct { Temporary map[int]bool } -// Create a new group for channel with name. Does not add it to the channels -// group list. -func NewGroup(channel *Channel, name string) *Group { - grp := &Group{} - grp.Channel = channel +// EmptyGroupWithName creates a new Group with the given name. +func EmptyGroupWithName(name string) Group { + grp := Group{} grp.Name = name grp.Add = make(map[int]bool) grp.Remove = make(map[int]bool) @@ -45,13 +40,13 @@ func NewGroup(channel *Channel, name string) *Group { return grp } -// Check whether the Add set contains id. +// AddContains checks whether the Add set contains id. func (group *Group) AddContains(id int) (ok bool) { _, ok = group.Add[id] return } -// Get the list of user ids in the Add set. +// AddUsers gets the list of user ids in the Add set. func (group *Group) AddUsers() []int { users := []int{} for uid, _ := range group.Add { @@ -60,13 +55,13 @@ func (group *Group) AddUsers() []int { return users } -// Check whether the Remove set contains id. +// RemoveContains checks whether the Remove set contains id. func (group *Group) RemoveContains(id int) (ok bool) { _, ok = group.Remove[id] return } -// Get the list of user ids in the Remove set. +// RemoveUsers gets the list of user ids in the Remove set. func (group *Group) RemoveUsers() []int { users := []int{} for uid, _ := range group.Remove { @@ -75,41 +70,38 @@ func (group *Group) RemoveUsers() []int { return users } -// Check whether the Temporary set contains id. +// TemporaryContains checks whether the Temporary set contains id. func (group *Group) TemporaryContains(id int) (ok bool) { _, ok = group.Temporary[id] return } -// Get the set of user id's from the group. This includes group -// members that have been inherited from an ancestor. -func (group *Group) Members() map[int]bool { - groups := []*Group{} +// MembersInContext gets the set of user id's from the group in the given context. +// This includes group members that have been inherited from an ancestor context. +func (group *Group) MembersInContext(ctx *Context) map[int]bool { + groups := []Group{} members := map[int]bool{} - // The channel that the group is defined on. - channel := group.Channel - - // Walk a group's channel tree, starting with the channel the group - // is defined on, followed by its parent channels. - iter := group.Channel - for iter != nil { - curgroup := iter.Groups[group.Name] - if curgroup != nil { + // Walk a group's context chain, starting with the context the group + // is defined on, followed by its parent contexts. + origCtx := ctx + for ctx != nil { + curgroup, ok := ctx.Groups[group.Name] + if ok { // If the group is not inheritable, and we're looking at an // ancestor group, we've looked in all the groups we should. - if iter != channel && !curgroup.Inheritable { + if ctx != origCtx && !curgroup.Inheritable { break } // Add the group to the list of groups to be considered - groups = append([]*Group{curgroup}, groups...) + groups = append([]Group{curgroup}, groups...) // If this group does not inherit from groups in its ancestors, stop looking // for more ancestor groups. if !curgroup.Inherit { break } } - iter = iter.parent + ctx = ctx.Parent } for _, curgroup := range groups { @@ -124,13 +116,18 @@ func (group *Group) Members() map[int]bool { return members } -// Checks whether a user is a member of the group as defined on channel. -// The channel current is the channel that group membership is currently being evaluated for. -// The channel aclchan is the channel that the group is defined on. This means that current inherits -// the group from an acl in aclchan. +// GroupMemberCheck checks whether a user is a member +// of the group as defined in the given context. // -// The channel aclchan will always be either equal to current, or be an ancestor. -func GroupMemberCheck(current *Channel, aclchan *Channel, name string, client *Client) (ok bool) { +// The 'current' context is the context that group +// membership is currently being evaluated for. +// +// The 'acl' context is the context of the ACL that +// that group membership is being evaluated for. +// +// The acl context will always be either equal to +// current, or be an ancestor. +func GroupMemberCheck(current *Context, acl *Context, name string, user User) (ok bool) { valid := true invert := false token := false @@ -160,7 +157,7 @@ func GroupMemberCheck(current *Channel, aclchan *Channel, name string, client *C } // Evaluate in ACL context (not current channel) if name[0] == '~' { - channel = aclchan + channel = acl name = name[1:] continue } @@ -182,8 +179,8 @@ func GroupMemberCheck(current *Channel, aclchan *Channel, name string, client *C if token { // The user is part of this group if the remaining name is part of // his access token list. The name check is case-insensitive. - for _, clientToken := range client.Tokens { - if strings.ToLower(name) == strings.ToLower(clientToken) { + for _, token := range user.Tokens() { + if strings.ToLower(name) == strings.ToLower(token) { return true } } @@ -191,7 +188,7 @@ func GroupMemberCheck(current *Channel, aclchan *Channel, name string, client *C } else if hash { // The client is part of this group if the remaining name matches the // client's cert hash. - if strings.ToLower(name) == strings.ToLower(client.CertHash) { + if strings.ToLower(name) == strings.ToLower(user.CertHash()) { return true } return false @@ -204,7 +201,7 @@ func GroupMemberCheck(current *Channel, aclchan *Channel, name string, client *C } else if name == "auth" { // The user is part of the auth group is he is authenticated. That is, // his UserId is >= 0. - return client.IsRegistered() + return user.UserId() >= 0 } else if name == "strong" { // The user is part of the strong group if he is authenticated to the server // via a strong certificate (i.e. non-self-signed, trusted by the server's @@ -213,10 +210,10 @@ func GroupMemberCheck(current *Channel, aclchan *Channel, name string, client *C return false } else if name == "in" { // Is the user in the currently evaluated channel? - return client.Channel == channel + return user.ACLContext() == channel } else if name == "out" { // Is the user not in the currently evaluated channel? - return client.Channel != channel + return user.ACLContext() != channel } else if name == "sub" { // fixme(mkrautz): The sub group implementation below hasn't been thoroughly // tested yet. It might be a bit buggy! @@ -255,45 +252,30 @@ func GroupMemberCheck(current *Channel, aclchan *Channel, name string, client *C } } - // Build a chain of channels, starting from the client's current channel. - playerChain := []*Channel{} - iter := client.Channel - for iter != nil { - playerChain = append([]*Channel{iter}, playerChain...) - iter = iter.parent - } - // Build a chain of channels, starting from the channel current. This is - // the channel that group membership is checked against, notwithstanding - // the ~ group operator. - groupChain := []*Channel{} - iter = current - for iter != nil { - groupChain = append([]*Channel{iter}, groupChain...) - iter = iter.parent - } + // Build a context chain starting from the + // user's current context. + userChain := buildChain(user.ACLContext()) + // Build a chain of contexts, starting from + // the 'current' context. This is the context + // that group membership is checked against, + // notwithstanding the ~ group operator. + groupChain := buildChain(current) - // Helper function that finds the given channel in the channels slice. - // Returns -1 if the given channel was not found in the slice. - indexOf := func(channels []*Channel, channel *Channel) int { - for i, iter := range channels { - if iter == channel { - return i - } - } - return -1 - } - - // Find the index of channel that the group is currently being evaluated on. - // This can be either aclchan or current depending on the ~ group operator. - cofs := indexOf(groupChain, channel) + // Find the index of the context that the group + // is currently being evaluated on. This can be + // either the 'acl' context or 'current' context + // depending on the ~ group operator. + cofs := indexOf(groupChain, current) if cofs == -1 { valid = false return false } - // Add the first parameter of our sub group to cofs to get our 'base' channel. + // Add the first parameter of our sub group to cofs + // to get our base context. cofs += minpath - // Check that the minpath parameter that was given is a valid index for groupChain. + // Check that the minpath parameter that was given + // is a valid index for groupChain. if cofs >= len(groupChain) { valid = false return false @@ -301,48 +283,50 @@ func GroupMemberCheck(current *Channel, aclchan *Channel, name string, client *C cofs = 0 } - // If our 'base' channel is not in the playerChain, the group does not apply to the client. - if indexOf(playerChain, groupChain[cofs]) == -1 { + // If our base context is not in the userChain, the + // group does not apply to the user. + if indexOf(userChain, groupChain[cofs]) == -1 { return false } - // Down here, we're certain that the playerChain includes the base channel - // *somewhere*. We must now determine if the path depth makes the user a - // member of the group. + // Down here, we're certain that the userChain + // includes the base context somewhere in its + // chain. We must now determine if the path depth + // makes the user a member of the group. mindepth := cofs + mindesc maxdepth := cofs + maxdesc - pdepth := len(playerChain) - 1 + pdepth := len(userChain) - 1 return pdepth >= mindepth && pdepth <= maxdepth } else { // Non-magic groups - groups := []*Group{} + groups := []Group{} iter := channel for iter != nil { if group, ok := iter.Groups[name]; ok { // Skip non-inheritable groups if we're in parents - // of our evaluated channel. + // of our evaluated context. if iter != channel && !group.Inheritable { break } // Prepend group - groups = append([]*Group{group}, groups...) + groups = append([]Group{group}, groups...) // If this group does not inherit from groups in its ancestors, stop looking // for more ancestor groups. if !group.Inherit { break } } - iter = iter.parent + iter = iter.Parent } isMember := false for _, group := range groups { - if group.AddContains(client.UserId()) || group.TemporaryContains(client.UserId()) || group.TemporaryContains(-int(client.Session)) { + if group.AddContains(user.UserId()) || group.TemporaryContains(user.UserId()) || group.TemporaryContains(-int(user.Session())) { isMember = true } - if group.RemoveContains(client.UserId()) { + if group.RemoveContains(user.UserId()) { isMember = false } } @@ -352,28 +336,21 @@ func GroupMemberCheck(current *Channel, aclchan *Channel, name string, client *C return false } -// Get the list of group names in a particular channel. -// This function walks the through the channel and all its -// parent channels to figure out all groups that affect -// the channel while considering group inheritance. -func (channel *Channel) GroupNames() map[string]bool { +// Get the list of group names for the given ACL context. +// +// This function walks the through the context chain to figure +// out all groups that affect the given context whilst considering +// group inheritance. +func (ctx *Context) GroupNames() []string { names := map[string]bool{} + origCtx := ctx + contexts := []*Context{} - // Construct a list of channels. Fartherst away ancestors - // are put in front of the list, allowing us to linearly - // iterate the list to determine inheritance. - channels := []*Channel{} - iter := channel - for iter != nil { - channels = append([]*Channel{iter}, channels...) - iter = iter.parent - } - - // Walk through all channels and groups in them. - for _, iter := range channels { - for _, group := range iter.Groups { + // Walk through the whole context chain and all groups in it. + for _, ctx := range contexts { + for _, group := range ctx.Groups { // A non-inheritable group in parent. Discard it. - if channel != iter && !group.Inheritable { + if ctx != origCtx && !group.Inheritable { delete(names, group.Name) // An inheritable group. Add it to the list. } else { @@ -381,5 +358,13 @@ func (channel *Channel) GroupNames() map[string]bool { } } } - return names + + // Convert to slice + stringNames := make([]string, 0, len(names)) + for name, ok := range names { + if ok { + stringNames = append(stringNames, name) + } + } + return stringNames } diff --git a/pkg/acl/interfaces.go b/pkg/acl/interfaces.go new file mode 100644 index 0000000..eb06af5 --- /dev/null +++ b/pkg/acl/interfaces.go @@ -0,0 +1,23 @@ +// Copyright (c) 2013 The Grumble Authors +// The use of this source code is goverened by a BSD-style +// license that can be found in the LICENSE-file. + +package acl + +// User represents a user on a Mumble server. +// The User interface represents the method set that +// must be implemented in order to check a user's +// permissions in an ACL context. +type User interface { + Session() uint32 + UserId() int + + CertHash() string + Tokens() []string + ACLContext() *Context +} + +// Channel represents a Channel on a Mumble server. +type Channel interface { + ChannelId() int +} \ No newline at end of file diff --git a/server.go b/server.go index e57de1a..d67d6e1 100644 --- a/server.go +++ b/server.go @@ -17,6 +17,7 @@ import ( "fmt" "hash" "log" + "mumbleapp.com/grumble/pkg/acl" "mumbleapp.com/grumble/pkg/ban" "mumbleapp.com/grumble/pkg/freezer" "mumbleapp.com/grumble/pkg/htmlfilter" @@ -107,9 +108,6 @@ type Server struct { numLogOps int freezelog *freezer.Log - // ACL cache - aclcache ACLCache - // Bans banlock sync.RWMutex Bans []ban.Ban @@ -125,7 +123,7 @@ type clientLogForwarder struct { func (lf clientLogForwarder) Write(incoming []byte) (int, error) { buf := new(bytes.Buffer) - buf.WriteString(fmt.Sprintf("<%v:%v(%v)> ", lf.client.Session, lf.client.ShownName(), lf.client.UserId())) + buf.WriteString(fmt.Sprintf("<%v:%v(%v)> ", lf.client.Session(), lf.client.ShownName(), lf.client.UserId())) buf.Write(incoming) lf.logger.Output(3, buf.String()) return len(incoming), nil @@ -147,7 +145,6 @@ func NewServer(id int64) (s *Server, err error) { s.nextUserId = 1 s.Channels = make(map[int]*Channel) - s.aclcache = NewACLCache() s.Channels[0] = NewChannel(0, "Root") s.nextChanId = 1 @@ -238,8 +235,8 @@ func (server *Server) handleIncomingClient(conn net.Conn) (err error) { client.lf = &clientLogForwarder{client, server.Logger} client.Logger = log.New(client.lf, "", 0) - client.Session = server.pool.Get() - client.Printf("New connection: %v (%v)", conn.RemoteAddr(), client.Session) + client.session = server.pool.Get() + client.Printf("New connection: %v (%v)", conn.RemoteAddr(), client.Session()) client.tcpaddr = addr.(*net.TCPAddr) client.server = server @@ -263,15 +260,17 @@ func (server *Server) handleIncomingClient(conn net.Conn) (err error) { } state := tlsconn.ConnectionState() + log.Printf("peerCerts = %#v", state.PeerCertificates) if len(state.PeerCertificates) > 0 { hash := sha1.New() hash.Write(state.PeerCertificates[0].Raw) sum := hash.Sum(nil) - client.CertHash = hex.EncodeToString(sum) + client.certHash = hex.EncodeToString(sum) + log.Printf("%v", client.CertHash()) } // Check whether the client's cert hash is banned - if server.IsCertHashBanned(client.CertHash) { + if server.IsCertHashBanned(client.CertHash()) { client.Printf("Certificate hash is banned") client.Disconnect() return @@ -302,8 +301,8 @@ func (server *Server) RemoveClient(client *Client, kicked bool) { } server.hmutex.Unlock() - delete(server.clients, client.Session) - server.pool.Reclaim(client.Session) + delete(server.clients, client.Session()) + server.pool.Reclaim(client.Session()) // Remove client from channel channel := client.Channel @@ -316,7 +315,7 @@ func (server *Server) RemoveClient(client *Client, kicked bool) { // at this point. if !kicked && client.state > StateClientAuthenticated { err := server.broadcastProtoMessage(&mumbleproto.UserRemove{ - Session: proto.Uint32(client.Session), + Session: proto.Uint32(client.Session()), }) if err != nil { server.Panic("Unable to broadcast UserRemove message for disconnected client.") @@ -449,7 +448,7 @@ func (server *Server) handleAuthenticate(client *Client, msg *Message) { // Set access tokens. Clients can set their access tokens any time // by sending an Authenticate message with he contents of their new // access token list. - client.Tokens = auth.Tokens + client.tokens = auth.Tokens server.ClearCaches() if client.state >= StateClientAuthenticated { @@ -485,7 +484,7 @@ func (server *Server) handleAuthenticate(client *Client, msg *Message) { // First look up registration by name. user, exists := server.UserNameMap[client.Username] if exists { - if len(client.CertHash) > 0 && user.CertHash == client.CertHash { + if client.HasCertificate() && user.CertHash == client.CertHash() { client.user = user } else { client.RejectAuth(mumbleproto.Reject_WrongUserPW, "Wrong certificate hash") @@ -494,8 +493,8 @@ func (server *Server) handleAuthenticate(client *Client, msg *Message) { } // Name matching didn't do. Try matching by certificate. - if client.user == nil && len(client.CertHash) > 0 { - user, exists := server.UserCertMap[client.CertHash] + if client.user == nil && client.HasCertificate() { + user, exists := server.UserCertMap[client.CertHash()] if exists { client.user = user } @@ -557,15 +556,15 @@ func (server *Server) finishAuthenticate(client *Client) { } // Add the client to the connected list - server.clients[client.Session] = client + server.clients[client.Session()] = client // Warn clients without CELT support that they might not be able to talk to everyone else. if len(client.codecs) == 0 { client.codecs = []int32{CeltCompatBitstream} - server.Printf("Client %v connected without CELT codecs. Faking compat bitstream.", client.Session) + server.Printf("Client %v connected without CELT codecs. Faking compat bitstream.", client.Session()) if server.Opus && !client.opus { client.sendMessage(&mumbleproto.TextMessage{ - Session: []uint32{client.Session}, + Session: []uint32{client.Session()}, Message: proto.String("WARNING: Your client doesn't support the CELT codec, you won't be able to talk to or hear most clients. Please make sure your client was built with CELT support."), }) } @@ -592,13 +591,13 @@ func (server *Server) finishAuthenticate(client *Client) { } userstate := &mumbleproto.UserState{ - Session: proto.Uint32(client.Session), + Session: proto.Uint32(client.Session()), Name: proto.String(client.ShownName()), ChannelId: proto.Uint32(uint32(channel.Id)), } - if len(client.CertHash) > 0 { - userstate.Hash = proto.String(client.CertHash) + if client.HasCertificate() { + userstate.Hash = proto.String(client.CertHash()) } if client.IsRegistered() { @@ -639,20 +638,18 @@ func (server *Server) finishAuthenticate(client *Client) { server.sendUserList(client) sync := &mumbleproto.ServerSync{} - sync.Session = proto.Uint32(client.Session) + sync.Session = proto.Uint32(client.Session()) sync.MaxBandwidth = proto.Uint32(server.cfg.Uint32Value("MaxBandwidth")) sync.WelcomeText = proto.String(server.cfg.StringValue("WelcomeText")) if client.IsSuperUser() { - sync.Permissions = proto.Uint64(uint64(AllPermissions)) + sync.Permissions = proto.Uint64(uint64(acl.AllPermissions)) } else { - server.HasPermission(client, server.RootChannel(), EnterPermission) - perm := server.aclcache.GetPermission(client, server.RootChannel()) - if !perm.IsCached() { - client.Panic("Corrupt ACL cache") - return - } - perm.ClearCacheBit() - sync.Permissions = proto.Uint64(uint64(perm)) + // fixme(mkrautz): previously we calculated the user's + // permissions and sent them to the client in here. This + // code relied on our ACL cache, but that has been temporarily + // thrown out because of our ACL handling code moving to its + // own package. + sync.Permissions = nil } if err := client.sendMessage(sync); err != nil { client.Panicf("%v", err) @@ -729,7 +726,7 @@ func (server *Server) updateCodecVersions(connecting *Client) { } } else if server.Opus == enableOpus { if server.Opus && connecting != nil && !connecting.opus { - txtMsg.Session = []uint32{connecting.Session} + txtMsg.Session = []uint32{connecting.Session()} connecting.sendMessage(txtMsg) } return @@ -751,7 +748,7 @@ func (server *Server) updateCodecVersions(connecting *Client) { if server.Opus { for _, client := range server.clients { if !client.opus && client.state == StateClientReady { - txtMsg.Session = []uint32{connecting.Session} + txtMsg.Session = []uint32{connecting.Session()} err := client.sendMessage(txtMsg) if err != nil { client.Panicf("%v", err) @@ -759,7 +756,7 @@ func (server *Server) updateCodecVersions(connecting *Client) { } } if connecting != nil && !connecting.opus { - txtMsg.Session = []uint32{connecting.Session} + txtMsg.Session = []uint32{connecting.Session()} connecting.sendMessage(txtMsg) } } @@ -778,13 +775,13 @@ func (server *Server) sendUserList(client *Client) { } userstate := &mumbleproto.UserState{ - Session: proto.Uint32(connectedClient.Session), + Session: proto.Uint32(connectedClient.Session()), Name: proto.String(connectedClient.ShownName()), ChannelId: proto.Uint32(uint32(connectedClient.Channel.Id)), } - if len(connectedClient.CertHash) > 0 { - userstate.Hash = proto.String(connectedClient.CertHash) + if connectedClient.HasCertificate() { + userstate.Hash = proto.String(connectedClient.CertHash()) } if connectedClient.IsRegistered() { @@ -857,11 +854,10 @@ func (server *Server) sendClientPermissions(client *Client, channel *Channel) { return } - // Update cache - server.HasPermission(client, channel, EnterPermission) - perm := server.aclcache.GetPermission(client, channel) + // fixme(mkrautz): re-add when we have ACL caching + return - // fixme(mkrautz): Cache which permissions we've already sent. + perm := acl.Permission(acl.NonePermission) client.sendMessage(&mumbleproto.PermissionQuery{ ChannelId: proto.Uint32(uint32(channel.Id)), Permissions: proto.Uint32(uint32(perm)), @@ -1038,7 +1034,6 @@ func (server *Server) handleUdpPacket(udpaddr *net.UDPAddr, buf []byte, nread in // Clear the Server's caches func (server *Server) ClearCaches() { - server.aclcache = NewACLCache() for _, client := range server.clients { client.ClearCaches() } @@ -1063,7 +1058,7 @@ func (server *Server) userEnterChannel(client *Client, channel *Channel, usersta server.UpdateFrozenUserLastChannel(client) - canspeak := server.HasPermission(client, channel, SpeakPermission) + canspeak := acl.HasPermission(&channel.ACL, client, acl.SpeakPermission) if canspeak == client.Suppress { client.Suppress = !canspeak userstate.Suppress = proto.Bool(client.Suppress) @@ -1090,16 +1085,16 @@ func (s *Server) RegisterClient(client *Client) (uid uint32, err error) { } // Grumble can only register users with certificates. - if len(client.CertHash) == 0 { + if client.HasCertificate() { return 0, errors.New("no cert hash") } user.Email = client.Email - user.CertHash = client.CertHash + user.CertHash = client.CertHash() uid = s.nextUserId s.Users[uid] = user - s.UserCertMap[client.CertHash] = user + s.UserCertMap[client.CertHash()] = user s.UserNameMap[client.Username] = user return uid, nil @@ -1126,16 +1121,16 @@ func (s *Server) RemoveRegistration(uid uint32) (err error) { // Remove references for user id uid from channel. Traverses subchannels. func (s *Server) removeRegisteredUserFromChannel(uid uint32, channel *Channel) { - newACL := []*ChannelACL{} - for _, chanacl := range channel.ACL { + newACL := []acl.ACL{} + for _, chanacl := range channel.ACL.ACLs { if chanacl.UserId == int(uid) { continue } newACL = append(newACL, chanacl) } - channel.ACL = newACL + channel.ACL.ACLs = newACL - for _, grp := range channel.Groups { + for _, grp := range channel.ACL.Groups { if _, ok := grp.Add[int(uid)]; ok { delete(grp.Add, int(uid)) } @@ -1155,7 +1150,7 @@ func (s *Server) removeRegisteredUserFromChannel(uid uint32, channel *Channel) { // Remove a channel func (server *Server) RemoveChannel(channel *Channel) { // Can't remove root - if channel.parent == nil { + if channel == server.RootChannel() { return } @@ -1172,12 +1167,12 @@ func (server *Server) RemoveChannel(channel *Channel) { // Remove all clients for _, client := range channel.clients { target := channel.parent - for target.parent != nil && !server.HasPermission(client, target, EnterPermission) { + for target.parent != nil && !acl.HasPermission(&target.ACL, client, acl.EnterPermission) { target = target.parent } userstate := &mumbleproto.UserState{} - userstate.Session = proto.Uint32(client.Session) + 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 { diff --git a/voicetarget.go b/voicetarget.go index 030d645..c009e82 100644 --- a/voicetarget.go +++ b/voicetarget.go @@ -4,6 +4,8 @@ package main +import "mumbleapp.com/grumble/pkg/acl" + // A VoiceTarget holds information about a single // VoiceTarget entry of a Client. type VoiceTarget struct { @@ -72,9 +74,9 @@ func (vt *VoiceTarget) SendVoiceBroadcast(vb *VoiceBroadcast) { } if !vtc.subChannels && !vtc.links && vtc.onlyGroup == "" { - if server.HasPermission(client, channel, WhisperPermission) { + if acl.HasPermission(&channel.ACL, client, acl.WhisperPermission) { for _, target := range channel.clients { - fromChannels[target.Session] = target + fromChannels[target.Session()] = target } } } else { @@ -92,10 +94,10 @@ func (vt *VoiceTarget) SendVoiceBroadcast(vb *VoiceBroadcast) { } } for _, newchan := range newchans { - if server.HasPermission(client, newchan, WhisperPermission) { + if acl.HasPermission(&newchan.ACL, client, acl.WhisperPermission) { for _, target := range newchan.clients { - if vtc.onlyGroup == "" || GroupMemberCheck(newchan, newchan, vtc.onlyGroup, target) { - fromChannels[target.Session] = target + if vtc.onlyGroup == "" || acl.GroupMemberCheck(&newchan.ACL, &newchan.ACL, vtc.onlyGroup, target) { + fromChannels[target.Session()] = target } } } @@ -106,15 +108,15 @@ func (vt *VoiceTarget) SendVoiceBroadcast(vb *VoiceBroadcast) { for _, session := range vt.sessions { target := server.clients[session] if target != nil { - if _, alreadyInFromChannels := fromChannels[target.Session]; !alreadyInFromChannels { - direct[target.Session] = target + 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) + delete(direct, client.Session()) + delete(fromChannels, client.Session()) if vt.directCache == nil { vt.directCache = direct