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