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.
This commit is contained in:
ospab 2026-07-18 18:14:19 +03:00
parent d9686c9344
commit 9a891310f9
2 changed files with 6 additions and 18 deletions

View File

@ -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

View File

@ -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);