fix: audit MEDIUM fixes — D-Bus race, TOCTOU, FP reset, entry clear (v0.6.11)

- fingerprint: split cleanup_dbus into a sync take_cleanup_proxy() + async
  perform_dbus_cleanup(). resume_async now awaits VerifyStop+Release before
  re-claiming, so fprintd cannot reject the Claim on a slow bus. stop()
  still spawns the cleanup fire-and-forget.
- fingerprint: remove failed_attempts = 0 from resume_async. An attacker
  with sensor control could otherwise cycle verify-match → account-fail →
  resume and never trip the 10-attempt cap.
- lockscreen: open the wallpaper with O_NOFOLLOW and build the texture
  from bytes, closing the TOCTOU between the symlink check and Texture::
  from_file.
- lockscreen: clear password_entry immediately after extracting the
  Zeroizing<String>, shortening the window the GLib GString copy stays in
  libc-malloc'd memory.
This commit is contained in:
2026-04-24 13:21:19 +02:00
parent 39d9cbb624
commit 9dfd1829e9
5 changed files with 81 additions and 24 deletions
+30 -3
View File
@@ -244,6 +244,10 @@ pub fn create_lockscreen_window(
if password.is_empty() {
return;
}
// 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("");
entry.set_sensitive(false);
let username = username.clone();
@@ -518,12 +522,35 @@ pub fn start_fingerprint(
/// Load the wallpaper as a texture once, for sharing across all windows.
/// Returns None if no wallpaper path is provided or the file cannot be loaded.
/// Blur is applied at render time via GPU (GskBlurNode), not here.
///
/// Opens the file with O_NOFOLLOW to close the TOCTOU window between the
/// symlink check in `resolve_background_path_with` and this read. If the path
/// was swapped for a symlink after the check, `open` fails with ELOOP.
pub fn load_background_texture(bg_path: &Path) -> Option<gdk::Texture> {
let file = gio::File::for_path(bg_path);
match gdk::Texture::from_file(&file) {
use std::io::Read;
use std::os::unix::fs::OpenOptionsExt;
let mut file = match std::fs::OpenOptions::new()
.read(true)
.custom_flags(libc::O_NOFOLLOW)
.open(bg_path)
{
Ok(f) => f,
Err(e) => {
log::warn!("Failed to open wallpaper {}: {e}", bg_path.display());
return None;
}
};
let mut bytes = Vec::new();
if let Err(e) = file.read_to_end(&mut bytes) {
log::warn!("Failed to read wallpaper {}: {e}", bg_path.display());
return None;
}
let glib_bytes = glib::Bytes::from_owned(bytes);
match gdk::Texture::from_bytes(&glib_bytes) {
Ok(texture) => Some(texture),
Err(e) => {
log::warn!("Failed to load wallpaper {}: {e}", bg_path.display());
log::warn!("Failed to decode wallpaper {}: {e}", bg_path.display());
None
}
}