From 7cb6388349f968fe198d87791ddb2a9b66209bf2 Mon Sep 17 00:00:00 2001 From: Viktor Liu Date: Sat, 23 May 2026 19:15:01 +0200 Subject: [PATCH] Decline VNC approval early when no console user is logged in --- client/vnc/server/console_user_darwin.go | 19 +++++++++++++++++++ client/vnc/server/console_user_other.go | 7 +++++++ client/vnc/server/console_user_windows.go | 13 +++++++++++++ client/vnc/server/server.go | 5 +++++ 4 files changed, 44 insertions(+) create mode 100644 client/vnc/server/console_user_darwin.go create mode 100644 client/vnc/server/console_user_other.go create mode 100644 client/vnc/server/console_user_windows.go diff --git a/client/vnc/server/console_user_darwin.go b/client/vnc/server/console_user_darwin.go new file mode 100644 index 000000000..18091850a --- /dev/null +++ b/client/vnc/server/console_user_darwin.go @@ -0,0 +1,19 @@ +package server + +import "errors" + +// consoleHasInteractiveUser returns true when a user is logged into the +// console (i.e. an Aqua session is active). At the loginwindow there is +// nobody to display an approval prompt to, so callers can decline +// without waiting on the broker. +func consoleHasInteractiveUser() bool { + if _, err := consoleUserID(); err != nil { + if errors.Is(err, errNoConsoleUser) { + return false + } + // Unknown error: fail closed so a probe-time glitch does not + // silently let an unattended console accept VNC sessions. + return false + } + return true +} diff --git a/client/vnc/server/console_user_other.go b/client/vnc/server/console_user_other.go new file mode 100644 index 000000000..ddc67bfd4 --- /dev/null +++ b/client/vnc/server/console_user_other.go @@ -0,0 +1,7 @@ +//go:build !darwin && !windows + +package server + +// consoleHasInteractiveUser is unused outside service mode (darwin/windows) +// but the symbol must exist so gateApproval compiles on all platforms. +func consoleHasInteractiveUser() bool { return true } diff --git a/client/vnc/server/console_user_windows.go b/client/vnc/server/console_user_windows.go new file mode 100644 index 000000000..70197d8cc --- /dev/null +++ b/client/vnc/server/console_user_windows.go @@ -0,0 +1,13 @@ +package server + +// consoleHasInteractiveUser returns true when there is a logged-in user +// session on the box. At the lock/login screen WTSQueryUserName is empty, +// which means there is nobody to display an approval prompt to. Callers +// should decline without waiting on the broker in that case. +func consoleHasInteractiveUser() bool { + sid := getActiveSessionID() + if sid == 0 { + return false + } + return wtsSessionHasUser(sid) +} diff --git a/client/vnc/server/server.go b/client/vnc/server/server.go index f7c74ffdd..eb9751018 100644 --- a/client/vnc/server/server.go +++ b/client/vnc/server/server.go @@ -431,6 +431,11 @@ func (s *Server) gateApproval(conn net.Conn, header *connectionHeader, connLog * connLog.Warn("VNC connection rejected: approval required but no approver") return false, ApprovalDecision{} } + if s.serviceMode && !consoleHasInteractiveUser() { + rejectConnection(conn, codeMessage(RejectCodeNoConsoleUser, "no interactive user session")) + connLog.Info("VNC connection rejected: no interactive user session to approve") + return false, ApprovalDecision{} + } info := ApprovalInfo{ SourceIP: sourceIPString(conn.RemoteAddr()), Mode: modeString(header.mode),