From 35f1a17cdfc461df2400e83e50791f1709ea53eb Mon Sep 17 00:00:00 2001 From: nevaforget Date: Fri, 24 Apr 2026 12:52:59 +0200 Subject: [PATCH] =?UTF-8?q?fix:=20audit=20fix=20=E2=80=94=20reduce=20passw?= =?UTF-8?q?ord=20copies=20in=20memory=20(v0.8.4)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - attempt_login takes Zeroizing by value, eliminating the redundant Zeroizing::new(password.to_string()) that doubled the Rust-owned copy. - Clear password_entry's internal buffer immediately after extracting the password, shortening the window during which the GTK GString persists in non-zeroizable libc memory. --- Cargo.lock | 2 +- Cargo.toml | 2 +- DECISIONS.md | 7 +++++++ src/greeter.rs | 9 ++++++--- 4 files changed, 15 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c5445b8..586ac30 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -575,7 +575,7 @@ dependencies = [ [[package]] name = "moongreet" -version = "0.8.3" +version = "0.8.4" dependencies = [ "gdk-pixbuf", "gdk4", diff --git a/Cargo.toml b/Cargo.toml index b5e5548..738c21b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "moongreet" -version = "0.8.3" +version = "0.8.4" edition = "2024" description = "A greetd greeter for Wayland with GTK4 and Layer Shell" license = "MIT" diff --git a/DECISIONS.md b/DECISIONS.md index 94e9f8e..9a3ea48 100644 --- a/DECISIONS.md +++ b/DECISIONS.md @@ -1,5 +1,12 @@ # Decisions +## 2026-04-24 – Audit fix: shrink password-in-memory window (v0.8.4) + +- **Who**: ClaudeCode, Dom +- **Why**: Security audit flagged the GTK password path as holding more copies of the plaintext password in memory than necessary. `attempt_login` wrapped the already-`Zeroizing` caller value into a second `Zeroizing` (`password.to_string()`), and the GTK `GString` backing `entry.text()` persisted in libc malloc'd memory until the allocator reused the page. +- **Tradeoffs**: The GTK `GString` and the libc `strdup` copy on the PAM FFI boundary remain non-zeroizable — this is an inherent GTK/libc limitation, already documented in CLAUDE.md. This change reduces the Rust-owned copies to one and clears the `PasswordEntry` text field immediately after extraction to shorten the GTK-side window. +- **How**: (1) `attempt_login` now takes `password: Zeroizing` by value instead of `&str`, moving ownership into the `spawn_blocking` closure. (2) The redundant `Zeroizing::new(password.to_string())` inside `attempt_login` is removed. (3) `password_entry.set_text("")` is called right after the password is extracted from the activate handler, shortening the lifetime of the GTK-internal buffer. + ## 2026-04-21 – Ship polkit rule in moongreet instead of moonarch (v0.8.3) - **Who**: ClaudeCode, Dom diff --git a/src/greeter.rs b/src/greeter.rs index fa73b37..e298beb 100644 --- a/src/greeter.rs +++ b/src/greeter.rs @@ -493,6 +493,10 @@ pub fn create_greeter_window( let Some(user) = user else { return }; let password = Zeroizing::new(entry.text().to_string()); + // Clear the GTK entry's internal buffer as early as possible. GTK allocates + // the backing `GString` via libc malloc, which `zeroize` cannot reach — the + // best we can do is shorten the window during which it resides in memory. + entry.set_text(""); let session = get_selected_session(&session_dropdown, &sessions_rc); let Some(session) = session else { @@ -502,7 +506,7 @@ pub fn create_greeter_window( attempt_login( &user, - &password, + password, &session, strings, &state, @@ -953,7 +957,7 @@ fn set_login_sensitive( #[allow(clippy::too_many_arguments)] fn attempt_login( user: &User, - password: &str, + password: Zeroizing, session: &Session, strings: &'static Strings, state: &Rc>, @@ -992,7 +996,6 @@ fn attempt_login( set_login_sensitive(password_entry, session_dropdown, false); let username = user.username.clone(); - let password = Zeroizing::new(password.to_string()); let exec_cmd = session.exec_cmd.clone(); let session_name = session.name.clone(); let greetd_sock = state.borrow().greetd_sock.clone();