From 9a891310f9ce17661870201a818003072dede91b Mon Sep 17 00:00:00 2001 From: ospab Date: Sat, 18 Jul 2026 18:14:19 +0300 Subject: [PATCH] fix(cli): setup wizard used a fake password hash, locking admins out of their own panel The Server+Panel setup wizard's panel-password hashing was a placeholder: std::collections::hash_map::DefaultHasher (SipHash, not cryptographic, and not even a 256-bit output - only the first 8 of 32 bytes were real, the rest zero-padded), left in by the comment "sha2 is not a direct dep of ostp/Cargo.toml, so we use std's hasher as a placeholder digest here." api.rs's handle_login computes the REAL SHA256 hex digest of the submitted password and compares it against config.json's stored password_hash. Since the wizard's placeholder never produces the same value as real SHA256 of the same password, anyone who set up a panel through this wizard could never actually log into it with the password it just showed them - a complete functional break of the wizard-driven admin flow, not a corner case. Added sha2 as a direct ostp dependency and replaced the placeholder with the exact same format!("{:x}", Sha256::digest(..)) api.rs's login check uses. --- ostp/Cargo.toml | 1 + ostp/src/main.rs | 23 +++++------------------ 2 files changed, 6 insertions(+), 18 deletions(-) diff --git a/ostp/Cargo.toml b/ostp/Cargo.toml index 0139621..f5e160b 100644 --- a/ostp/Cargo.toml +++ b/ostp/Cargo.toml @@ -21,3 +21,4 @@ tracing-subscriber = { version = "0.3", features = ["env-filter"] } ostp-core = { path = "../ostp-core" } colored = "2.1" rlimit = "0.11.0" +sha2.workspace = true diff --git a/ostp/src/main.rs b/ostp/src/main.rs index 852378e..8918773 100644 --- a/ostp/src/main.rs +++ b/ostp/src/main.rs @@ -720,24 +720,11 @@ fn run_setup_wizard(config_path: &std::path::Path) -> Result<()> { }) as char }).collect(); let password = wizard_prompt("Admin password (blank for random)", &rand_pass); - let pass_hash = { - use std::fmt::Write as _; - let mut hash = String::new(); - let digest: [u8; 32] = { - use std::collections::hash_map::DefaultHasher; - use std::hash::{Hash, Hasher}; - // Panel password hashing. sha2 is not a direct dep of ostp/Cargo.toml, - // so we use std's hasher as a placeholder digest here. - let mut h = DefaultHasher::new(); - password.hash(&mut h); - let v = h.finish(); - let mut out = [0u8; 32]; - out[..8].copy_from_slice(&v.to_be_bytes()); - out - }; - for b in digest { let _ = write!(hash, "{:02x}", b); } - hash - }; + // Must match api.rs's handle_login exactly (format!("{:x}", Sha256::digest(..))) - + // this used to be a DefaultHasher (SipHash) placeholder that produced a + // differently-shaped digest, so a password set up through this wizard could + // never actually log into the panel it just configured. + let pass_hash = format!("{:x}", sha2::Sha256::digest(password.as_bytes())); wizard_step(4, TOTAL, "Saving configuration"); let panel_bind = format!("0.0.0.0:{}", panel_port);