Carry the VNC authorization across a listener rebind

This commit is contained in:
Viktor Liu
2026-09-09 09:23:58 +02:00
parent ab1f297030
commit c622075088
5 changed files with 152 additions and 15 deletions
+14 -2
View File
@@ -217,8 +217,10 @@ func (a *Authorizer) GetUserIDClaim() string {
return a.userIDClaim
}
// Config returns the authorization currently in force. The user list and the
// machine-user map are copies; the originals stay in use here.
// Config returns the authorization currently in force, in a form that can be
// fed back to Update to reproduce it. The user list, the machine-user map and
// the session-key entries are copies; the originals stay in use here. The
// session-key entries come out in unspecified order.
func (a *Authorizer) Config() *Config {
a.mu.RLock()
defer a.mu.RUnlock()
@@ -228,10 +230,20 @@ func (a *Authorizer) Config() *Config {
machineUsers[osUser] = slices.Clone(indexes)
}
sessionPubKeys := make([]SessionPubKey, 0, len(a.sessionPubKeys))
for key, userIDHash := range a.sessionPubKeys {
sessionPubKeys = append(sessionPubKeys, SessionPubKey{
PubKey: slices.Clone(key[:]),
UserIDHash: userIDHash,
DisplayName: a.sessionDisplayNames[key],
})
}
return &Config{
UserIDClaim: a.userIDClaim,
AuthorizedUsers: slices.Clone(a.authorizedUsers),
MachineUsers: machineUsers,
SessionPubKeys: sessionPubKeys,
}
}
+58
View File
@@ -757,3 +757,61 @@ func TestAuthorizer_AuthorizeSessionKey_UnauthorizedUser(t *testing.T) {
_, _, err = a.AuthorizeSessionKey(pub, "alice")
require.ErrorIs(t, err, ErrUserNotAuthorized)
}
// Config must round-trip through Update: the VNC listener rebind reads the
// running server's authorization and hands it to the replacement, so a session
// key dropped here is an authorized peer refused until the next network map.
func TestAuthorizer_Config_RoundTripsSessionPubKeys(t *testing.T) {
pub := bytesRepeat(0x77, sessionPubKeyLen)
userHash, err := sshauth.HashUserID("alice")
require.NoError(t, err)
original := NewAuthorizer()
original.Update(&Config{
UserIDClaim: "email",
AuthorizedUsers: []sshauth.UserIDHash{userHash},
MachineUsers: map[string][]uint32{Wildcard: {0}},
SessionPubKeys: []SessionPubKey{
{PubKey: pub, UserIDHash: userHash, DisplayName: "Alice"},
},
})
carried := original.Config()
require.NotNil(t, carried)
assert.Equal(t, "email", carried.UserIDClaim)
assert.Equal(t, []sshauth.UserIDHash{userHash}, carried.AuthorizedUsers)
assert.Equal(t, map[string][]uint32{Wildcard: {0}}, carried.MachineUsers)
require.Len(t, carried.SessionPubKeys, 1)
assert.Equal(t, pub, carried.SessionPubKeys[0].PubKey)
assert.Equal(t, userHash, carried.SessionPubKeys[0].UserIDHash)
assert.Equal(t, "Alice", carried.SessionPubKeys[0].DisplayName)
rebuilt := NewAuthorizer()
rebuilt.Update(carried)
gotHash, _, err := rebuilt.AuthorizeSessionKey(pub, "alice")
require.NoError(t, err)
assert.Equal(t, userHash, gotHash)
assert.Equal(t, "Alice", rebuilt.LookupSessionDisplayName(pub))
}
// The copies Config hands out must not alias the authorizer's own state, or a
// caller mutating what it read changes the policy in force.
func TestAuthorizer_Config_CopiesAreIndependent(t *testing.T) {
pub := bytesRepeat(0x78, sessionPubKeyLen)
userHash, err := sshauth.HashUserID("alice")
require.NoError(t, err)
a := NewAuthorizer()
a.Update(&Config{
AuthorizedUsers: []sshauth.UserIDHash{userHash},
MachineUsers: map[string][]uint32{Wildcard: {0}},
SessionPubKeys: []SessionPubKey{{PubKey: pub, UserIDHash: userHash}},
})
carried := a.Config()
carried.MachineUsers[Wildcard][0] = 42
carried.SessionPubKeys[0].PubKey[0] ^= 0xFF
_, _, err = a.AuthorizeSessionKey(pub, "alice")
require.NoError(t, err, "mutating the returned config must not affect the authorizer")
}