Implement password handling for unattended access
build / build-linux-amd64 (push) Successful in 2m0s

This commit is contained in:
2026-05-08 09:32:13 +02:00
parent c1eaac1cb3
commit 0af4f5edce
7 changed files with 168 additions and 14 deletions
+6 -6
View File
File diff suppressed because one or more lines are too long
+3 -3
View File
File diff suppressed because one or more lines are too long
+26 -2
View File
@@ -166,11 +166,22 @@ export class Session {
// UTF-8 bytes the way the Rust side reads them.
// pwd_hash = SHA256(password_text || salt_utf8_bytes)
// password_response = SHA256(pwd_hash || challenge_utf8_bytes)
//
// Special case the empty password: the desktop client sends LITERAL
// empty bytes for `lr.password` when the user hasn't typed a
// password yet (client.rs:3472-3475 — "login without password, the
// remote side can click accept"). The host's connection.rs:2488
// branches on `lr.password.is_empty()` to start the cm_popup
// approval flow; if we send a SHA-256-derived 32-byte response
// instead, the host treats it as a wrong-password attempt and
// returns LOGIN_MSG_PASSWORD_WRONG, which collapses the
// attended-no-password approval path.
const enc = new TextEncoder();
const saltBytes = enc.encode(salt);
const challengeBytes = enc.encode(challenge);
const pwdHash = sha256(concat(opts.password, saltBytes));
const passwordResp = sha256(concat(pwdHash, challengeBytes));
const passwordResp = opts.password.length === 0
? new Uint8Array(0)
: sha256(concat(sha256(concat(opts.password, saltBytes)), challengeBytes));
// -------- 8. Send Message{login_request} --------
const loginReq = hbb.Message.create({
@@ -219,6 +230,19 @@ export class Session {
// replay them into its main receive dispatch.
const preloginExtras: hbb.Message[] = [];
let approvalNotified = false;
// Empty-password path: when the host has a permanent password set
// AND a user is logged in, connection.rs:2488 just calls
// try_start_cm and stays silent (no LOGIN_MSG_NO_PASSWORD_ACCESS).
// The desktop client renders "Please wait for the remote side…" on
// its own as soon as it sends LoginRequest in that case; we do the
// same here so the UI doesn't sit on the "secure handshake + login"
// step indefinitely. The notification is idempotent — if the host
// *does* later send LOGIN_MSG_NO_PASSWORD_ACCESS, the existing
// branch below no-ops on `approvalNotified`.
if (passwordResp.length === 0) {
approvalNotified = true;
try { opts.onAwaitingApproval?.(); } catch { /* swallow */ }
}
while (true) {
const respMsg = await session.recv();
if (respMsg.test_delay) {