fix: security and correctness audit fixes (v0.4.1)

PAM conv callback: check msg_style (password only for ECHO_OFF),
handle strdup OOM with proper cleanup, null-check PAM handle.

Fingerprint: self-wire D-Bus g-signal in start() via Rc<RefCell<>>
and connect_local — VerifyStatus signals are now actually dispatched.
VerifyStop before VerifyStart in restart_verify.

Lockscreen: password entry stays active after faillock threshold
(PAM decides lockout, not UI), use Zeroizing<String> from GTK entry.

Release builds exit(1) without ext-session-lock-v1 support.

Config: fingerprint_enabled as Option<bool> so empty user config
does not override system config.

Dead code: remove unused i18n strings and fingerprint accessors,
parameterize faillock_warning max_attempts.
This commit is contained in:
2026-03-28 00:06:27 +01:00
parent 64f032cd9a
commit 17f8930ff7
8 changed files with 201 additions and 70 deletions
+64 -26
View File
@@ -3,6 +3,8 @@
use gio::prelude::*;
use gtk4::gio;
use std::cell::RefCell;
use std::rc::Rc;
const FPRINTD_BUS_NAME: &str = "net.reactivated.Fprint";
const FPRINTD_MANAGER_PATH: &str = "/net/reactivated/Fprint/Manager";
@@ -126,24 +128,31 @@ impl FingerprintListener {
}
}
/// Whether the listener is currently running.
pub fn is_running(&self) -> bool {
self.running
}
/// Start listening for fingerprint verification.
pub fn start<F, G>(&mut self, username: &str, on_success: F, on_failure: G)
where
/// Connects the D-Bus g-signal handler internally. The `listener` parameter
/// must be the same `Rc<RefCell<FingerprintListener>>` that owns `self`.
pub fn start<F, G>(
listener: &Rc<RefCell<FingerprintListener>>,
username: &str,
on_success: F,
on_failure: G,
) where
F: Fn() + 'static,
G: Fn() + 'static,
{
let proxy = match &self.device_proxy {
Some(p) => p,
None => return,
let proxy = {
let inner = listener.borrow();
match inner.device_proxy.clone() {
Some(p) => p,
None => return,
}
};
self.on_success = Some(Box::new(on_success));
self.on_failure = Some(Box::new(on_failure));
{
let mut inner = listener.borrow_mut();
inner.on_success = Some(Box::new(on_success));
inner.on_failure = Some(Box::new(on_failure));
}
// Claim the device
let args = glib::Variant::from((&username,));
@@ -178,12 +187,42 @@ impl FingerprintListener {
return;
}
self.running = true;
// Connect the g-signal handler on the proxy to dispatch VerifyStatus
let listener_weak = Rc::downgrade(listener);
let signal_id = proxy.connect_local("g-signal", false, move |values| {
// g-signal arguments: (proxy, sender_name, signal_name, parameters)
let signal_name: String = match values[2].get() {
Ok(v) => v,
Err(_) => return None,
};
if signal_name.as_str() != "VerifyStatus" {
return None;
}
// Note: Signal handling is set up by the caller via connect_g_signal()
// because FingerprintListener is not an Rc and the g-signal callback
// needs to reference mutable state. The caller (lockscreen.rs) will
// connect the proxy's "g-signal" and call on_verify_status().
let params = match values[3].get::<glib::Variant>() {
Ok(v) => v,
Err(_) => return None,
};
let status = params
.child_value(0)
.get::<String>()
.unwrap_or_default();
let done = params
.child_value(1)
.get::<bool>()
.unwrap_or(false);
if let Some(listener_rc) = listener_weak.upgrade() {
listener_rc.borrow_mut().on_verify_status(&status, done);
}
None
});
let mut inner = listener.borrow_mut();
inner.signal_id = Some(signal_id);
inner.running = true;
}
/// Process a VerifyStatus signal from fprintd.
@@ -228,6 +267,14 @@ impl FingerprintListener {
/// Restart fingerprint verification after a completed attempt.
fn restart_verify(&self) {
if let Some(ref proxy) = self.device_proxy {
// VerifyStop before VerifyStart to avoid D-Bus errors
let _ = proxy.call_sync(
"VerifyStop",
None,
gio::DBusCallFlags::NONE,
-1,
gio::Cancellable::NONE,
);
let args = glib::Variant::from((&"any",));
if let Err(e) = proxy.call_sync(
"VerifyStart",
@@ -269,15 +316,6 @@ impl FingerprintListener {
}
}
/// Get a reference to the device proxy for signal connection.
pub fn device_proxy(&self) -> Option<&gio::DBusProxy> {
self.device_proxy.as_ref()
}
/// Store the signal handler ID for cleanup.
pub fn set_signal_id(&mut self, id: glib::SignalHandlerId) {
self.signal_id = Some(id);
}
}
#[cfg(test)]