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:
+52
-5
@@ -7,6 +7,11 @@ use zeroize::Zeroizing;
|
||||
|
||||
// PAM return codes
|
||||
const PAM_SUCCESS: i32 = 0;
|
||||
const PAM_BUF_ERR: i32 = 5;
|
||||
|
||||
// PAM message styles
|
||||
const PAM_PROMPT_ECHO_OFF: libc::c_int = 1;
|
||||
const PAM_PROMPT_ECHO_ON: libc::c_int = 2;
|
||||
|
||||
/// PAM message structure (pam_message).
|
||||
#[repr(C)]
|
||||
@@ -60,7 +65,7 @@ unsafe extern "C" {
|
||||
/// which PAM will free. The appdata_ptr must point to a valid CString (the password).
|
||||
unsafe extern "C" fn pam_conv_callback(
|
||||
num_msg: libc::c_int,
|
||||
_msg: *mut *const PamMessage,
|
||||
msg: *mut *const PamMessage,
|
||||
resp: *mut *mut PamResponse,
|
||||
appdata_ptr: *mut libc::c_void,
|
||||
) -> libc::c_int {
|
||||
@@ -83,10 +88,48 @@ unsafe extern "C" fn pam_conv_callback(
|
||||
}
|
||||
|
||||
for i in 0..num_msg as isize {
|
||||
// Safety: strdup allocates with malloc — PAM will free() the resp strings.
|
||||
// We dereference password which is valid for the lifetime of authenticate().
|
||||
let resp_ptr = resp_array.offset(i);
|
||||
(*resp_ptr).resp = libc::strdup((*password).as_ptr());
|
||||
// Safety: msg is an array of pointers provided by PAM
|
||||
let pam_msg = *msg.offset(i);
|
||||
let msg_style = (*pam_msg).msg_style;
|
||||
|
||||
match msg_style {
|
||||
PAM_PROMPT_ECHO_OFF => {
|
||||
// Password prompt — provide the password via strdup
|
||||
let dup = libc::strdup((*password).as_ptr());
|
||||
if dup.is_null() {
|
||||
// strdup failed (OOM) — free all previously allocated strings
|
||||
for j in 0..i {
|
||||
let prev = resp_array.offset(j);
|
||||
if !(*prev).resp.is_null() {
|
||||
libc::free((*prev).resp as *mut libc::c_void);
|
||||
}
|
||||
}
|
||||
libc::free(resp_array as *mut libc::c_void);
|
||||
return PAM_BUF_ERR;
|
||||
}
|
||||
(*resp_ptr).resp = dup;
|
||||
}
|
||||
PAM_PROMPT_ECHO_ON => {
|
||||
// Visible prompt — provide empty string, never the password
|
||||
let empty = libc::strdup(b"\0".as_ptr() as *const libc::c_char);
|
||||
if empty.is_null() {
|
||||
for j in 0..i {
|
||||
let prev = resp_array.offset(j);
|
||||
if !(*prev).resp.is_null() {
|
||||
libc::free((*prev).resp as *mut libc::c_void);
|
||||
}
|
||||
}
|
||||
libc::free(resp_array as *mut libc::c_void);
|
||||
return PAM_BUF_ERR;
|
||||
}
|
||||
(*resp_ptr).resp = empty;
|
||||
}
|
||||
_ => {
|
||||
// PAM_ERROR_MSG, PAM_TEXT_INFO, or unknown — no response expected
|
||||
(*resp_ptr).resp = ptr::null_mut();
|
||||
}
|
||||
}
|
||||
(*resp_ptr).resp_retcode = 0;
|
||||
}
|
||||
|
||||
@@ -140,7 +183,11 @@ pub fn authenticate(username: &str, password: &str) -> bool {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Safety: handle is valid after successful pam_start
|
||||
if handle.is_null() {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Safety: handle is valid and non-null after successful pam_start
|
||||
let auth_ret = unsafe { pam_authenticate(handle, 0) };
|
||||
let acct_ret = if auth_ret == PAM_SUCCESS {
|
||||
// Safety: handle is valid, check account restrictions
|
||||
|
||||
Reference in New Issue
Block a user