From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from firstgate.proxmox.com (firstgate.proxmox.com [212.224.123.68]) by lore.proxmox.com (Postfix) with ESMTPS id 2B9081FF186 for ; Fri, 29 Aug 2025 20:26:38 +0200 (CEST) Received: from firstgate.proxmox.com (localhost [127.0.0.1]) by firstgate.proxmox.com (Proxmox) with ESMTP id CFC1634BD5; Fri, 29 Aug 2025 20:26:45 +0200 (CEST) To: pve-devel@lists.proxmox.com Date: Fri, 29 Aug 2025 14:26:03 -0400 MIME-Version: 1.0 Message-ID: List-Id: Proxmox VE development discussion List-Post: From: Peter via pve-devel Precedence: list Cc: Peter X-Mailman-Version: 2.1.29 X-BeenThere: pve-devel@lists.proxmox.com List-Subscribe: , List-Unsubscribe: , List-Archive: Reply-To: Proxmox VE development discussion List-Help: Subject: [pve-devel] [PATCH installer 1/1] assistant: validate: add verify-password option Content-Type: multipart/mixed; boundary="===============5621269156354975057==" Errors-To: pve-devel-bounces@lists.proxmox.com Sender: "pve-devel" --===============5621269156354975057== Content-Type: message/rfc822 Content-Disposition: inline Return-Path: X-Original-To: pve-devel@lists.proxmox.com Delivered-To: pve-devel@lists.proxmox.com Received: from firstgate.proxmox.com (firstgate.proxmox.com [212.224.123.68]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits)) (No client certificate requested) by lists.proxmox.com (Postfix) with ESMTPS id A8FAFD181C for ; Fri, 29 Aug 2025 20:26:43 +0200 (CEST) Received: from firstgate.proxmox.com (localhost [127.0.0.1]) by firstgate.proxmox.com (Proxmox) with ESMTP id 7FAF834B1F for ; Fri, 29 Aug 2025 20:26:43 +0200 (CEST) Received: from mail-qk1-x72d.google.com (mail-qk1-x72d.google.com [IPv6:2607:f8b0:4864:20::72d]) (using TLSv1.3 with cipher TLS_AES_128_GCM_SHA256 (128/128 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by firstgate.proxmox.com (Proxmox) with ESMTPS for ; Fri, 29 Aug 2025 20:26:42 +0200 (CEST) Received: by mail-qk1-x72d.google.com with SMTP id af79cd13be357-7f903bb47c7so219043985a.0 for ; Fri, 29 Aug 2025 11:26:42 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1756491995; x=1757096795; darn=lists.proxmox.com; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:sender:from:to:cc:subject:date:message-id:reply-to; bh=V8OQC+9VIkKbbhQ6MaLXc7ZacV6lxaNvZ4kdoZJEAyw=; b=bnBcHNCtbCeJhP95sMKHZWJrVH2snBCWPNi7aYqW25TIzTLWUbDVCFPnmrd6Gnlk4g H+hjB53cJo6ZSSPPbsunTDaWOiRbdu68FsPsul5NIIZEAa+tjVCssHg/GSwmIOaSNaE7 Xr637982ixlu//6kIJ4ugdBUms4RKNRh3eV8GfX9kTFZdmakutiVZTXbRxgE1HPvps6K 4g5MvLPm4G9QSK1/d2kIZxmA6JPbw0uszxXcG7Yw4D7h6MJ7olO9ICrliNufkgUp29Me LVzRO2Xvx30zeOH4MqQj2SoSjk1mmlcSUWYIoZuWHJbH6EyopyLYSg/BT+GJivfH5VfP qfzw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1756491995; x=1757096795; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:sender:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to; bh=V8OQC+9VIkKbbhQ6MaLXc7ZacV6lxaNvZ4kdoZJEAyw=; b=FfOkbsndjsfkpHsxOzXfJ9tBTyK6sQJqfm1GdglFPMQABe5itO3Qt2ZaoHEIBioB1V arU2gOE1vFxvHZt+GSZZ2uxkdhh1jbDammfblxe0LqKAHQIbKnjmAsrKK5/JzLI/bOeI j9FWsF5iBl9nV1eWyJ30I/5l4NbwW7Cq6DVt3/EeKiaS0/UWT58zry8FsDscMczHolMH ZQZ2abloKGs+HApHxY0TEP3M+/8TSy2IXhqW9gzD+jupqH2K4qB85FL9ECZ7H/aMkX49 64Q8EZILh0fMzA7Qb3pFOjeDYhRXzzIy6IuxHUgCbafMTEPGdTaOvnPpoXRt3Gjkp06v GMYg== X-Gm-Message-State: AOJu0YxmgQox6ue5mAQTaILh5bT/4POudX0/RTwBjfP5lFgmVDnmZRpl ueuaAQwIAzB6Bbi5K6gr75SLVBSbwlW6H+UQ2fivAQOGd4lQzQLU5D+aiZg6LA== X-Gm-Gg: ASbGncteOi7OISxRgtjoGnFlAslFV6krP1cKYK6hQ799QOiN6X2lp1pMlJx9En48PgT K01kqaV2iqgdKfAAM26St4aAHSYPTvGTxuEHCqA6fVwbwpBCf6T1YnZaTjrAd+vuaPVZHvdnBZy hlOC/MB9gc5nx+vAxbMMfWIHwsjCfnuzU7yFlIM8RXUx7YxMmazWaXkeYnMuCuh4Eq6ER2SOnuV e1V4VEJ+PLi1KelpdIripp80bo+lqUURoXANn41+tmLaJ5IgvUlNQbs5DONy10km+MVh3jaZLyn 4L10+65CagR0jMI0I5JcAqL/nTSWUF1u8De1zcsKYG32BQ9GkolFEYoP0n+2lcp8ANJuTZeG4K4 UOB1dGhYZx/M4J4EC1YWmnfzEOGJMnQbx+9Er3pyyKX7sOT3dNFMdevUktrmBFQjYbS9L2YPaK8 7csrzNhQM8hyf9VWnxCIjQCLIMZ+u4JWB8I/O38IP4bZ14LpPZJN487XgLxlxxLzmo6ysC6yyBs ijTgw== X-Google-Smtp-Source: AGHT+IEbgKGHLI8SW0/8JDwEq+sQHC/jLNc4sBlTOFzZq7VmXUDY6iaPmvMRYf+kYE788rxGScFqbQ== X-Received: by 2002:a05:620a:459e:b0:7f1:51e6:5108 with SMTP id af79cd13be357-7f151e65571mr2157237485a.53.1756491994767; Fri, 29 Aug 2025 11:26:34 -0700 (PDT) Received: from pvedev.in.creath.net (pool-173-76-101-7.bstnma.fios.verizon.net. [173.76.101.7]) by smtp.gmail.com with ESMTPSA id af79cd13be357-7fc1484e8e8sm225508385a.40.2025.08.29.11.26.34 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 29 Aug 2025 11:26:34 -0700 (PDT) Sender: Peter Creath From: Peter To: pve-devel@lists.proxmox.com Subject: [PATCH installer 1/1] assistant: validate: add verify-password option Date: Fri, 29 Aug 2025 14:26:03 -0400 Message-ID: <20250829182603.46493-1-pjcreath+proxmox@gmail.com> X-Mailer: git-send-email 2.47.2 MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-SPAM-LEVEL: Spam detection results: 0 BAYES_00 -1.9 Bayes spam probability is 0 to 1% DKIM_SIGNED 0.1 Message has a DKIM or DK signature, not necessarily valid DKIM_VALID -0.1 Message has at least one valid DKIM or DK signature DKIM_VALID_AU -0.1 Message has a valid DKIM or DK signature from author's domain DKIM_VALID_EF -0.1 Message has a valid DKIM or DK signature from envelope-from domain DMARC_PASS -0.1 DMARC pass policy FREEMAIL_FROM 0.001 Sender email is commonly abused enduser mail provider RCVD_IN_DNSWL_NONE -0.0001 Sender listed at https://www.dnswl.org/, no trust SPF_HELO_NONE 0.001 SPF: HELO does not publish an SPF Record SPF_PASS -0.001 SPF: sender matches SPF record URIBL_BLOCKED 0.001 ADMINISTRATOR NOTICE: The query to URIBL was blocked. See http://wiki.apache.org/spamassassin/DnsBlocklists#dnsbl-block for more information. [main.rs] Adds an option to interactively verify the hashed root password in the answer file, so that mistakes can be caught before installation. Signed-off-by: Peter --- In preparing an answer file for auto-installation, I somehow mangled the hashed root password, which I only discovered after performing the automated installation. This patch adds an option to the auto-install assistant that lets the user verify the hash in the answer file by interactively typing in the expected password and checking it against the hash. I don't love that I had to add an unsafe call to crypt(), but there isn't a Rust implementation of yescrypt. To minimize the impact I wrapped the unsafe call in its own function. This is my first submission to this mailing list. I've tried to follow all of the guidelines in the developer documentation, so please forgive any oversights and let me know if there's anything I should do differently. proxmox-auto-install-assistant/Cargo.toml | 2 + proxmox-auto-install-assistant/src/main.rs | 51 +++++++++++++++++++++- 2 files changed, 52 insertions(+), 1 deletion(-) diff --git a/proxmox-auto-install-assistant/Cargo.toml b/proxmox-auto-install-assistant/Cargo.toml index 9b4a9c4..8af7d9d 100644 --- a/proxmox-auto-install-assistant/Cargo.toml +++ b/proxmox-auto-install-assistant/Cargo.toml @@ -17,4 +17,6 @@ proxmox-installer-common = { workspace = true, features = [ "cli" ] } serde_json.workspace = true toml.workspace = true +crypt-sys = "0.1" glob = "0.3" +rpassword = "7.2" diff --git a/proxmox-auto-install-assistant/src/main.rs b/proxmox-auto-install-assistant/src/main.rs index 5d6c1d5..88d0032 100644 --- a/proxmox-auto-install-assistant/src/main.rs +++ b/proxmox-auto-install-assistant/src/main.rs @@ -2,10 +2,13 @@ //! Additional uses are to validate the format of an answer file or to test match filters and print //! information on the properties to match against for the current hardware. -#![forbid(unsafe_code)] +//#![deny(unsafe_code)] use anyhow::{Context, Result, bail, format_err}; +use crypt_sys::crypt; use glob::Pattern; +use rpassword::prompt_password; +use std::ffi::{CStr, CString}; use std::{ collections::BTreeMap, fmt, fs, @@ -153,12 +156,15 @@ struct CommandValidateAnswerArgs { path: PathBuf, /// Whether to also show the full answer as parsed. debug: bool, + /// Interactively verify the hashed root password. + verify_password: bool, } impl cli::Subcommand for CommandValidateAnswerArgs { fn parse(args: &mut cli::Arguments) -> Result { Ok(Self { debug: args.contains(["-d", "--debug"]), + verify_password: args.contains("--verify-password"), // Needs to be last path: args.free_from_str()?, }) @@ -176,6 +182,7 @@ ARGUMENTS: OPTIONS: -d, --debug Also show the full answer as parsed. + --verify-password Interactively verify the hashed root password. -h, --help Print this help -V, --version Print version "#, @@ -545,6 +552,42 @@ fn validate_answer_file_keys(path: impl AsRef + fmt::Debug) -> Result Result { + let password_c = CString::new(password.as_bytes()).unwrap(); + let hash_c = CString::new(hash.as_bytes()).unwrap(); + + let hashed_attempt = unsafe { + let result_ptr = crypt(password_c.as_ptr(), hash_c.as_ptr()); + if result_ptr.is_null() { + bail!("crypt() call failed"); + } + // result_ptr is a statically allocated block in the library, + // so it doesn't need to be freed. + CStr::from_ptr(result_ptr).to_string_lossy().into_owned() + }; + + Ok(hashed_attempt == hash) +} + +fn verify_hashed_password_interactive(answer: &Answer) -> Result<()> { + if let Some(hashed) = &answer.global.root_password_hashed { + println!("Verifying hashed root password."); + let password = prompt_password("Enter root password to verify: ") + .context("Failed to read password")?; + + if system_crypt(&password, hashed)? { + println!("Password matches hashed password."); + Ok(()) + } else { + bail!("Password does not match hashed password."); + } + } else { + bail!("'root-password-hashed' not set in answer file, cannot verify."); + } +} + fn validate_answer(args: &CommandValidateAnswerArgs) -> Result<()> { let mut valid = validate_answer_file_keys(&args.path)?; @@ -553,6 +596,12 @@ fn validate_answer(args: &CommandValidateAnswerArgs) -> Result<()> { if args.debug { println!("Parsed data from answer file:\n{:#?}", answer); } + if args.verify_password { + if let Err(err) = verify_hashed_password_interactive(&answer) { + eprintln!("{err:#}"); + valid = false; + } + } } Err(err) => { eprintln!("{err:#}"); -- 2.47.2 --===============5621269156354975057== Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Content-Disposition: inline _______________________________________________ pve-devel mailing list pve-devel@lists.proxmox.com https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel --===============5621269156354975057==--