Initial ACL and group support.

This commit is contained in:
Mikkel Krautz 2010-11-23 00:37:24 +01:00
parent 1e67afca46
commit 4903558e4a
7 changed files with 1047 additions and 34 deletions

View file

@ -20,7 +20,9 @@ GOFILES = \
tlsserver.go \
server.go \
client.go \
channel.go
channel.go \
acl.go \
group.go
.PHONY: grumble
grumble: pkg

240
acl.go Normal file
View file

@ -0,0 +1,240 @@
// 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) bool {
// SuperUser can't speak or whisper, but everything else is OK
if client.UserId == 0 {
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
}

View file

@ -1,3 +1,7 @@
// 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
// A Mumble channel
@ -8,10 +12,17 @@ type Channel struct {
Temporary bool
Position int
clients map[uint32]*Client
clients map[uint32]*Client
parent *Channel
children map[int]*Channel
parent *Channel
children map[int]*Channel
// ACL
ACL []*ChannelACL
InheritACL bool
// Groups
Groups map[string]*Group
}
func NewChannel(id int, name string) (channel *Channel) {
@ -20,6 +31,8 @@ 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 = map[string]*Group{}
return
}

View file

@ -37,14 +37,17 @@ type Client struct {
udp bool
// Personal
UserId int
Session uint32
Username string
Hash string
Tokens []string
Channel *Channel
}
// Something invalid happened on the wire.
func (client *Client) Panic(reason string) {
log.Printf("Client panic: %s", reason)
client.Disconnect()
}
@ -54,6 +57,8 @@ func (client *Client) Disconnect() {
close(client.udprecv)
close(client.msgchan)
client.conn.Close()
client.server.RemoveClient(client)
}
}
@ -108,6 +113,49 @@ func (c *Client) sendProtoMessage(kind uint16, msg interface{}) (err os.Error) {
return
}
// Send permission denied by type
func (c *Client) sendPermissionDeniedType(kind string) {
val, ok := mumbleproto.PermissionDenied_DenyType_value[kind]
if ok {
d, err := proto.Marshal(&mumbleproto.PermissionDenied{
Type: mumbleproto.NewPermissionDenied_DenyType(val),
})
if err != nil {
c.Panic(err.String())
return
}
c.msgchan <- &Message{
buf: d,
kind: MessagePermissionDenied,
}
} else {
log.Printf("Unknown permission denied type.")
}
}
// Send permission denied by who, what, where
func (c *Client) sendPermissionDenied(who *Client, where *Channel, what Permission) {
d, err := proto.Marshal(&mumbleproto.PermissionDenied{
Permission: proto.Uint32(uint32(what)),
ChannelId: proto.Uint32(uint32(where.Id)),
Session: proto.Uint32(who.Session),
Type: mumbleproto.NewPermissionDenied_DenyType(mumbleproto.PermissionDenied_Permission),
})
if err != nil {
c.Panic(err.String())
}
c.msgchan <- &Message{
buf: d,
kind: MessagePermissionDenied,
}
}
// Send permission denied fallback
func (c *Client) sendPermissionDeniedFallback(kind string, version uint32, text string) {
// fixme(mkrautz): Do fallback kind of stuff...
c.sendPermissionDeniedType(kind)
}
// UDP receiver.
func (client *Client) udpreceiver() {
for buf := range client.udprecv {
@ -152,7 +200,7 @@ func (client *Client) udpreceiver() {
buf: outbuf[0 : 1+outgoing.Size()],
target: target,
}
// Server loopback
// Server loopback
} else {
client.sendUdp(&Message{
buf: outbuf[0 : 1+outgoing.Size()],

356
group.go Normal file
View file

@ -0,0 +1,356 @@
// 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
import (
"log"
"strings"
"strconv"
)
type Group struct {
// The channel that this group resides in
Channel *Channel
// The name of this group
Name string
// The inherit flag means that this group will inherit group
// members from its parent.
Inherit bool
// The inheritable flag means that subchannels can
// inherit the members of this group.
Inheritable bool
// Group adds permissions to these users
Add map[int]bool
// Group removes permissions from these users
Remove map[int]bool
// Temporary add (authenticators)
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
grp.Name = name
grp.Add = make(map[int]bool)
grp.Remove = make(map[int]bool)
grp.Temporary = make(map[int]bool)
return grp
}
// Check whether the Add set contains id.
func (group *Group) AddContains(id int) (ok bool) {
_, ok = group.Add[id]
return
}
// Check whether the Remove set contains id.
func (group *Group) RemoveContains(id int) (ok bool) {
_, ok = group.Remove[id]
return
}
// Check 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{}
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 {
// 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 {
break
}
// Add the group to the list of groups to be considered
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
}
for _, curgroup := range groups {
for uid, _ := range curgroup.Add {
members[uid] = true
}
for uid, _ := range curgroup.Remove {
members[uid] = false, false
}
}
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.
//
// The channel aclchan will always be either equal to current, or be an ancestor.
func GroupMemberCheck(current *Channel, aclchan *Channel, name string, client *Client) bool {
invert := false
token := false
hash := false
// Returns the 'correct' return value considering the value
// of the invert flag.
retvalify := func(in bool) bool {
if invert {
return !in
}
return in
}
member := false
channel := current
for {
// Empty group name are not valid.
if len(name) == 0 {
return false
}
// Invert
if name[0] == '!' {
invert = true
name = name[1:]
continue
}
// Evaluate in ACL context (not current channel)
if name[0] == '~' {
channel = aclchan
name = name[1:]
continue
}
// Token
if name[0] == '#' {
token = true
name = name[1:]
continue
}
// Hash
if name[0] == '$' {
hash = true
name = name[1:]
continue
}
break
}
// The user is part of this group if the remaining name is part of
// his access token list.
if token {
log.Printf("GroupMemberCheck: Implement token matching")
member = false // fixme(mkrautz)
// The user is part of this group if the remaining name matches his
// cert hash.
} else if hash {
log.Printf("GroupMemberCheck: Implement hash matching")
member = false // fixme(mkrautz)
// None
} else if name == "none" {
member = false
// Everyone
} else if name == "all" {
member = true
// The user is part of the auth group is he is authenticated. That is,
// his UserId is >= 0.
} else if name == "auth" {
member = client.UserId >= 0
// The user is part of the strong group if he is authenticated to the server
// via a strong certificate (i.e. non-self-signed).
} else if name == "strong" {
log.Printf("GroupMemberCheck: Implement strong certificate matching")
member = false // fixme(mkrautz)
// Is the user in the currently evaluated channel?
} else if name == "in" {
member = client.Channel == channel
// Is the user not in the currently evaluated channel?
} else if name == "out" {
member = client.Channel != channel
// fixme(mkrautz): The sub group implementation below hasn't been thoroughly
// tested yet. It might be a bit buggy!
} else if name == "sub" {
// Strip away the "sub," part of the name
name = name[4:]
mindesc := 1
maxdesc := 1000
minpath := 0
// Parse the groupname to extract the values we should use
// for minpath (first argument), mindesc (second argument),
// and maxdesc (third argument).
args := strings.Split(name, ",", 3)
nargs := len(args)
if nargs == 3 {
if len(args[2]) > 0 {
if result, err := strconv.Atoi(args[2]); err == nil {
maxdesc = result
}
}
}
if nargs >= 2 {
if len(args[1]) > 0 {
if result, err := strconv.Atoi(args[1]); err == nil {
mindesc = result
}
}
}
if nargs >= 1 {
if len(args[0]) > 0 {
if result, err := strconv.Atoi(args[0]); err == nil {
minpath = result
}
}
}
// 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
}
// 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)
if cofs == -1 {
log.Printf("Invalid chain")
return false
}
// Add the first parameter of our sub group to cofs to get our 'base' channel.
cofs += minpath
// Check that the minpath parameter that was given is a valid index for groupChain.
if cofs >= len(groupChain) {
return retvalify(false)
} else if cofs < 0 {
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 {
return retvalify(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.
mindepth := cofs + mindesc
maxdepth := cofs + maxdesc
pdepth := len(playerChain) - 1
member = pdepth >= mindepth && pdepth <= maxdepth
// Non-magic groups
} else {
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.
if iter != channel && !group.Inheritable {
break
}
// Prepend group
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
}
for _, group := range groups {
if group.AddContains(client.UserId) || group.TemporaryContains(client.UserId) || group.TemporaryContains(-int(client.Session)) {
member = true
}
if group.RemoveContains(client.UserId) {
member = false
}
}
}
return retvalify(member)
}
// 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 {
names := map[string]bool{}
// 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 {
// A non-inheritable group in parent. Discard it.
if channel != iter && !group.Inheritable {
names[group.Name] = false, false
// An inheritable group. Add it to the list.
} else {
names[group.Name] = true
}
}
}
return names
}

View file

@ -1,4 +1,3 @@
// Grumble - an implementation of Murmur in Go
// 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.
@ -161,24 +160,47 @@ func (server *Server) handleUserStateMessage(client *Client, msg *Message) {
// Has a channel ID
if userstate.ChannelId != nil {
// Destination channel
dstChan := server.channels[int(*userstate.ChannelId)]
log.Printf("dstChan = %v", dstChan)
dstChan, ok := server.channels[int(*userstate.ChannelId)]
if !ok {
return
}
// If the user and the actor aren't the same, check whether the actor has the 'move' permission
// on the user's channel to move.
if actor != user && !server.HasPermission(actor, user.Channel, MovePermission) {
client.sendPermissionDenied(actor, user.Channel, MovePermission)
return
}
// Check whether the actor has 'move' permissions on dstChan. Check whether user has 'enter'
// permissions on dstChan.
if !server.HasPermission(actor, dstChan, MovePermission) && !server.HasPermission(user, dstChan, EnterPermission) {
client.sendPermissionDenied(user, dstChan, EnterPermission)
return
}
// Check whether the channel is full.
// fixme(mkrautz): See above.
}
if userstate.Mute != nil || userstate.Deaf != nil || userstate.Suppress != nil || userstate.PrioritySpeaker != nil {
// Disallow for SuperUser
if user.UserId == 0 {
client.sendPermissionDeniedType("SuperUser")
return
}
// Check whether the actor has 'mutedeafen' permission on user's channel.
if !server.HasPermission(actor, user.Channel, MuteDeafenPermission) {
client.sendPermissionDenied(actor, user.Channel, MuteDeafenPermission)
return
}
// Check if this was a suppress operation. Only the server can suppress users.
if userstate.Suppress != nil {
client.sendPermissionDenied(actor, user.Channel, MuteDeafenPermission)
return
}
}
// Comment set/clear
@ -190,8 +212,15 @@ func (server *Server) handleUserStateMessage(client *Client, msg *Message) {
if user != actor {
// Check if actor has 'move' permissions on the root channel. It is needed
// to clear another user's comment.
if !server.HasPermission(actor, server.root, MovePermission) {
client.sendPermissionDenied(actor, server.root, MovePermission)
return
}
// Only allow empty text.
if len(comment) > 0 {
return
}
}
// Check if the text is allowed.
@ -209,9 +238,20 @@ func (server *Server) handleUserStateMessage(client *Client, msg *Message) {
if userstate.UserId != nil {
// If user == actor, check for 'selfregister' permission on root channel.
// If user != actor, check for 'register' permission on root channel.
permCheck := Permission(NonePermission)
uid := *userstate.UserId
if user == actor {
permCheck = SelfRegisterPermission
} else {
permCheck = RegisterPermission
}
if uid >= 0 || !server.HasPermission(actor, server.root, SelfRegisterPermission) {
client.sendPermissionDenied(actor, server.root, permCheck)
return
}
// Check if the UserId in the message is >= 0. A registration attempt
// must use a negative UserId.
// If user's hash is empty, deny...
// fixme(mkrautz)
}
// Prevent self-targetting state changes to be applied to other users
@ -226,7 +266,7 @@ func (server *Server) handleUserStateMessage(client *Client, msg *Message) {
if actor != user && (userstate.SelfDeaf != nil || userstate.SelfMute != nil ||
userstate.Texture != nil || userstate.PluginContext != nil || userstate.PluginIdentity != nil ||
userstate.Recording != nil) {
return
return
}
}
@ -234,6 +274,7 @@ func (server *Server) handleUserStateMessage(client *Client, msg *Message) {
func (server *Server) handleBanListMessage(client *Client, msg *Message) {
}
// Broadcast text messages
func (server *Server) handleTextMessage(client *Client, msg *Message) {
txtmsg := &mumbleproto.TextMessage{}
err := proto.Unmarshal(msg.buf, txtmsg)
@ -242,15 +283,50 @@ func (server *Server) handleTextMessage(client *Client, msg *Message) {
return
}
users := []*Client{}
for i := 0; i < len(txtmsg.Session); i++ {
user, ok := server.clients[txtmsg.Session[i]]
if !ok {
log.Panic("Could not look up client by session")
// fixme(mkrautz): Check text message length.
// fixme(mkrautz): Sanitize text as well.
users := make(map[uint32]*Client)
// 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)
}
for _, user := range channel.clients {
users[user.Session] = user
}
}
users = append(users, user)
}
// 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)
return
}
for _, user := range channel.clients {
users[user.Session] = user
}
}
}
// Direct-to-users
for _, session := range txtmsg.Session {
if user, ok := server.clients[session]; ok {
if !server.HasPermission(client, user.Channel, TextMessagePermission) {
client.sendPermissionDenied(client, user.Channel, TextMessagePermission)
return
}
users[session] = user
}
}
// Remove ourselves
users[client.Session] = nil, false
for _, user := range users {
user.sendProtoMessage(MessageTextMessage, &mumbleproto.TextMessage{
Actor: proto.Uint32(client.Session),
@ -259,7 +335,214 @@ 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)
if err != nil {
client.Panic(err.String())
}
// Look up the channel this ACL message operates on.
channel, ok := server.channels[int(*acl.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)
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 acl.Query != nil && *acl.Query != false {
reply.InheritAcls = proto.Bool(channel.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 {
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 {
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.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 {
group := channel.Groups[name]
pgroup, ok := parent.Groups[name]
if !ok {
pgroup = nil
}
mpgroup := &mumbleproto.ACL_ChanGroup{}
mpgroup.Name = proto.String(name)
mpgroup.Inherit = proto.Bool(true)
if group != nil {
mpgroup.Inherit = proto.Bool(group.Inherit)
}
mpgroup.Inheritable = proto.Bool(true)
if group != nil {
mpgroup.Inheritable = proto.Bool(group.Inheritable)
}
mpgroup.Inherited = proto.Bool(pgroup != nil && 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 {
toadd := map[int]bool{}
for uid, _ := range group.Add {
users[uid] = true
toadd[uid] = true
}
for uid, _ := range group.Remove {
users[uid] = true
toadd[uid] = false, false
}
for uid, _ := range toadd {
mpgroup.Add = append(mpgroup.Add, uint32(uid))
}
}
if pgroup != nil {
for uid, _ := range pgroup.Members() {
users[uid] = true
mpgroup.InheritedMembers = append(mpgroup.InheritedMembers, uint32(uid))
}
}
reply.Groups = append(reply.Groups, mpgroup)
}
if err := client.sendProtoMessage(MessageACL, reply); err != nil {
client.Panic(err.String())
}
// Map the user ids in the user map to usernames of users.
// fixme(mkrautz): This requires a persistent datastore, because it retrieves registered users.
queryusers := &mumbleproto.QueryUsers{}
for uid, _ := range users {
queryusers.Ids = append(queryusers.Ids, uint32(uid))
queryusers.Names = append(queryusers.Names, "Unknown")
}
if len(queryusers.Ids) > 0 {
client.sendProtoMessage(MessageQueryUsers, reply)
}
// Set new groups and ACLs
} else {
// Get old temporary members
oldtmp := map[string]map[int]bool{}
for name, grp := range channel.Groups {
oldtmp[name] = grp.Temporary
}
// Clear current ACLs and groups
channel.ACL = []*ChannelACL{}
channel.Groups = map[string]*Group{}
// Add the received groups to the channel.
channel.InheritACL = *acl.InheritAcls
for _, pbgrp := range acl.Groups {
changroup := NewGroup(channel, *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.Groups[changroup.Name] = changroup
}
// Add the received ACLs to the channel.
for _, pbacl := range acl.Acls {
chanacl := NewChannelACL(channel)
chanacl.ApplyHere = *pbacl.ApplyHere
chanacl.ApplySubs = *pbacl.ApplySubs
if pbacl.UserId != nil {
chanacl.UserId = int(*pbacl.UserId)
} else {
chanacl.Group = *pbacl.Group
}
chanacl.Deny = Permission(*pbacl.Deny & AllPermissions)
chanacl.Allow = Permission(*pbacl.Grant & AllPermissions)
channel.ACL = append(channel.ACL, chanacl)
}
// Clear the server's ACL cache
server.ClearACLCache()
// Regular user?
if (!server.HasPermission(client, channel, WritePermission) && client.UserId >= 0) || len(client.Hash) > 0 {
chanacl := NewChannelACL(channel)
chanacl.ApplyHere = true
chanacl.ApplySubs = false
if client.UserId >= 0 {
chanacl.UserId = client.UserId
} else {
chanacl.Group = "$" + client.Hash
}
chanacl.UserId = client.UserId
chanacl.Deny = Permission(NonePermission)
chanacl.Allow = Permission(WritePermission | TraversePermission)
channel.ACL = append(channel.ACL, chanacl)
server.ClearACLCache()
}
// fixme(mkrautz): Sync channel to datastore
}
}
// User query
@ -274,5 +557,22 @@ func (server *Server) handleUserStatsMessage(client *Client, msg *Message) {
if err != nil {
client.Panic(err.String())
}
log.Printf("UserStatsMessage")
log.Printf("UserStats")
}
// Permission query
func (server *Server) handlePermissionQuery(client *Client, msg *Message) {
query := &mumbleproto.PermissionQuery{}
err := proto.Unmarshal(msg.buf, query)
if err != nil {
client.Panic(err.String())
}
if query.ChannelId == nil {
return
}
channel := server.channels[int(*query.ChannelId)]
server.sendClientPermissions(client, channel)
}

View file

@ -63,9 +63,12 @@ type Server struct {
PreferAlphaCodec bool
// Channels
chanid int
root *Channel
channels map[int]*Channel
chanid int
root *Channel
channels map[int]*Channel
// ACL cache
aclcache ACLCache
}
// Allocate a new Murmur instance
@ -93,6 +96,8 @@ func NewServer(addr string, port int) (s *Server, err os.Error) {
subChan := s.NewChannel("SubChannel")
s.root.AddChild(subChan)
s.aclcache = NewACLCache()
go s.handler()
return
@ -117,6 +122,8 @@ func (server *Server) NewClient(conn net.Conn) (err os.Error) {
client.msgchan = make(chan *Message)
client.udprecv = make(chan []byte)
client.UserId = -1
go client.receiver()
go client.udpreceiver()
go client.sender()
@ -149,7 +156,7 @@ func (server *Server) RemoveClient(client *Client) {
channel.RemoveClient(client)
err := server.broadcastProtoMessage(MessageUserRemove, &mumbleproto.UserRemove{
Session: proto.Uint32(client.Session),
Session: proto.Uint32(client.Session),
})
if err != nil {
// server panic
@ -166,7 +173,7 @@ func (server *Server) NewChannel(name string) (channel *Channel) {
// Remove a channel from the server.
func (server *Server) RemoveChanel(channel *Channel) {
if (channel.Id == 0) {
if channel.Id == 0 {
log.Printf("Attempted to remove root channel.")
return
}
@ -284,22 +291,41 @@ func (server *Server) handleAuthenticate(client *Client, msg *Message) {
// Broadcast the the user entered a channel
server.root.AddClient(client)
err = server.broadcastProtoMessage(MessageUserState, &mumbleproto.UserState{
if client.Username == "SuperUser" {
client.UserId = 0
}
userstate := &mumbleproto.UserState{
Session: proto.Uint32(client.Session),
Name: proto.String(client.Username),
ChannelId: proto.Uint32(0),
})
if err != nil {
}
if client.UserId >= 0 {
userstate.UserId = proto.Uint32(uint32(client.UserId))
}
if err := client.sendProtoMessage(MessageUserState, userstate); err != nil {
client.Panic(err.String())
}
server.sendUserList(client)
err = client.sendProtoMessage(MessageServerSync, &mumbleproto.ServerSync{
Session: proto.Uint32(client.Session),
MaxBandwidth: proto.Uint32(server.MaxBandwidth),
})
if err != nil {
sync := &mumbleproto.ServerSync{}
sync.Session = proto.Uint32(client.Session)
sync.MaxBandwidth = proto.Uint32(server.MaxBandwidth)
if client.UserId == 0 {
sync.Permissions = proto.Uint64(uint64(AllPermissions))
} else {
server.HasPermission(client, server.root, EnterPermission)
perm := server.aclcache.GetPermission(client, server.root)
if !perm.IsCached() {
client.Panic("Corrupt ACL cache")
return
}
perm.ClearCacheBit()
sync.Permissions = proto.Uint64(uint64(perm))
}
if err = client.sendProtoMessage(MessageServerSync, sync); err != nil {
client.Panic(err.String())
return
}
@ -377,6 +403,9 @@ func (server *Server) sendUserList(client *Client) {
if user.state != StateClientAuthenticated {
continue
}
if user == client {
continue
}
err := client.sendProtoMessage(MessageUserState, &mumbleproto.UserState{
Session: proto.Uint32(user.Session),
@ -392,6 +421,26 @@ func (server *Server) sendUserList(client *Client) {
}
// Send a client its permissions for channel.
func (server *Server) sendClientPermissions(client *Client, channel *Channel) {
// No caching for SuperUser
if client.UserId == 0 {
return
}
// Update cache
server.HasPermission(client, channel, EnterPermission)
perm := server.aclcache.GetPermission(client, channel)
log.Printf("Permissions = 0x%x", perm)
// fixme(mkrautz): Cache which permissions we've already sent.
client.sendProtoMessage(MessagePermissionQuery, &mumbleproto.PermissionQuery{
ChannelId: proto.Uint32(uint32(channel.Id)),
Permissions: proto.Uint32(uint32(perm)),
})
}
func (server *Server) broadcastProtoMessage(kind uint16, msg interface{}) (err os.Error) {
for _, client := range server.clients {
if client.state != StateClientAuthenticated {
@ -438,7 +487,7 @@ func (server *Server) handleIncomingMessage(client *Client, msg *Message) {
case MessageVoiceTarget:
log.Printf("MessageVoiceTarget from client")
case MessagePermissionQuery:
log.Printf("MessagePermissionQuery from client")
server.handlePermissionQuery(msg.client, msg)
case MessageCodecVersion:
log.Printf("MessageCodecVersion from client")
case MessageUserStats:
@ -470,11 +519,11 @@ func (s *Server) SendUDP() {
crypted := make([]byte, len(msg.buf)+4)
msg.client.crypt.Encrypt(crypted, msg.buf)
s.udpconn.WriteTo(crypted, msg.client.udpaddr)
// Non-encrypted
// Non-encrypted
} else if msg.address != nil {
s.udpconn.WriteTo(msg.buf, msg.address)
} else {
// Skipping
// Skipping
}
}
}
@ -564,6 +613,11 @@ func (server *Server) ListenUDP() {
}
}
// Clear the ACL cache
func (s *Server) ClearACLCache() {
s.aclcache = NewACLCache()
}
// The accept loop of the server.
func (s *Server) ListenAndMurmur() {