mirror of
https://github.com/netbirdio/netbird.git
synced 2026-09-11 17:29:07 +02:00
Make session key authorization atomic, unblock the encoder on teardown, drop agent privileges unconditionally
This commit is contained in:
@@ -55,12 +55,14 @@ var vncAgentCmd = &cobra.Command{
|
||||
// the listening socket: keeps a post-auth bug in the encoder /
|
||||
// input / capture paths confined to the user's own privileges
|
||||
// rather than escalating to host root, and makes the daemon's
|
||||
// LOCAL_PEERCRED check see the right uid. No-op on Windows
|
||||
// (both processes run as SYSTEM) and when --target-uid is 0.
|
||||
if vncAgentTargetUID != 0 {
|
||||
if err := dropAgentPrivileges(vncAgentTargetUID); err != nil {
|
||||
return fmt.Errorf("drop privileges to uid %d: %w", vncAgentTargetUID, err)
|
||||
}
|
||||
// LOCAL_PEERCRED check see the right uid. No-op on Windows, where
|
||||
// both processes run as SYSTEM.
|
||||
//
|
||||
// Called unconditionally: a missing or zero --target-uid is exactly
|
||||
// the case the Darwin implementation refuses, and skipping the call
|
||||
// for it would leave the agent running as root instead.
|
||||
if err := dropAgentPrivileges(vncAgentTargetUID); err != nil {
|
||||
return fmt.Errorf("drop privileges to uid %d: %w", vncAgentTargetUID, err)
|
||||
}
|
||||
|
||||
if err := os.Remove(vncAgentSocket); err != nil && !os.IsNotExist(err) {
|
||||
|
||||
@@ -68,6 +68,15 @@ func (c *xfixesCursor) Cursor() (*image.RGBA, int, int, uint64, error) {
|
||||
}
|
||||
return nil, 0, 0, 0, fmt.Errorf("cursor has zero extent")
|
||||
}
|
||||
// Anything past maxCursorDim is discarded by the encoder, so decoding it
|
||||
// would allocate and convert a sprite that can only be thrown away. Keep
|
||||
// showing the last good cursor instead.
|
||||
if w > maxCursorDim || h > maxCursorDim {
|
||||
if c.lastImg != nil {
|
||||
return c.lastImg, c.lastHotX, c.lastHotY, c.lastSerial, nil
|
||||
}
|
||||
return nil, 0, 0, 0, fmt.Errorf("cursor %dx%d exceeds %d", w, h, maxCursorDim)
|
||||
}
|
||||
if len(reply.CursorImage) < w*h {
|
||||
if c.lastImg != nil {
|
||||
return c.lastImg, c.lastHotX, c.lastHotY, c.lastSerial, nil
|
||||
|
||||
@@ -63,17 +63,16 @@ func (s *Server) authenticateSession(header *connectionHeader) (string, error) {
|
||||
return "", fmt.Errorf("client static key missing")
|
||||
}
|
||||
|
||||
userIDHash, err := s.authorizer.LookupSessionKey(header.clientStatic)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("lookup session pubkey: %w", err)
|
||||
}
|
||||
|
||||
osUser := "*"
|
||||
if header.mode == ModeSession {
|
||||
osUser = header.username
|
||||
}
|
||||
if _, err := s.authorizer.AuthorizeOSUserBySessionKey(userIDHash, osUser); err != nil {
|
||||
return "", fmt.Errorf("authorize OS user %q: %w", osUser, err)
|
||||
|
||||
// One call, so a management update that revokes the key cannot land between
|
||||
// resolving it and authorizing the identity it named.
|
||||
userIDHash, _, err := s.authorizer.AuthorizeSessionKey(header.clientStatic, osUser)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("authorize session key for OS user %q: %w", osUser, err)
|
||||
}
|
||||
return userIDHash.String(), nil
|
||||
}
|
||||
|
||||
@@ -448,5 +448,9 @@ func TestNoise_SessionMode_OSUserCheckRunsAfterHandshake(t *testing.T) {
|
||||
|
||||
reason := readRFBFailure(t, conn)
|
||||
assert.Contains(t, reason, RejectCodeAuthForbidden)
|
||||
assert.Contains(t, reason, "authorize OS user")
|
||||
// The key itself resolved: what refused the session is the OS-user mapping,
|
||||
// which is the half of the check that runs after the handshake.
|
||||
assert.Contains(t, reason, "bob")
|
||||
assert.Contains(t, reason, "no machine user mapping")
|
||||
assert.NotContains(t, reason, sshauth.ErrSessionKeyNotKnown.Error())
|
||||
}
|
||||
|
||||
@@ -198,6 +198,13 @@ func (s *session) serve() {
|
||||
encoderDone := make(chan struct{})
|
||||
go s.encoderLoop(encoderDone)
|
||||
defer func() {
|
||||
// Close the connection before waiting for the encoder. It may be parked
|
||||
// in a write to a client that stopped reading, and the only thing that
|
||||
// would unblock it otherwise is whatever deadline messageLoop last set
|
||||
// on the shared conn: that leaves the session, and the connection slot
|
||||
// it holds, alive for as long as that takes. The caller closes the
|
||||
// connection too; a second Close is harmless.
|
||||
s.conn.Close()
|
||||
close(s.encodeCh)
|
||||
<-encoderDone
|
||||
}()
|
||||
|
||||
Reference in New Issue
Block a user