fix: pam_set_data error handling, setsid+fd sweep in grandchild, is_replace logic, null pamh guard
This commit is contained in:
@@ -10,7 +10,6 @@ pub const PAM_IGNORE: c_int = 25;
|
|||||||
pub const PAM_AUTH_ERR: c_int = 7;
|
pub const PAM_AUTH_ERR: c_int = 7;
|
||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
pub const PAM_AUTH_ERR: c_int = 9;
|
pub const PAM_AUTH_ERR: c_int = 9;
|
||||||
// Fallback for CI on non-Linux non-macOS (should not occur in production)
|
|
||||||
#[cfg(not(any(target_os = "linux", target_os = "macos")))]
|
#[cfg(not(any(target_os = "linux", target_os = "macos")))]
|
||||||
pub const PAM_AUTH_ERR: c_int = 7;
|
pub const PAM_AUTH_ERR: c_int = 7;
|
||||||
|
|
||||||
@@ -55,7 +54,7 @@ unsafe extern "C" fn ahfail_cleanup(
|
|||||||
data: *mut c_void,
|
data: *mut c_void,
|
||||||
error_status: c_int,
|
error_status: c_int,
|
||||||
) {
|
) {
|
||||||
// Reclaim any stored path string to avoid a leak.
|
// Reclaim any stored path string to avoid a leak (PAM guarantees this fires exactly once).
|
||||||
let path_override: Option<String> = if data.is_null() {
|
let path_override: Option<String> = if data.is_null() {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
@@ -63,8 +62,11 @@ unsafe extern "C" fn ahfail_cleanup(
|
|||||||
};
|
};
|
||||||
|
|
||||||
if is_replace(error_status) {
|
if is_replace(error_status) {
|
||||||
// Previous attempt failed, same PAM handle; a new attempt is starting.
|
// Data replaced — a new pam_set_data call overwrote ours. Only spawn on failure;
|
||||||
|
// on success (PAM_SUCCESS | PAM_DATA_REPLACE) we do nothing.
|
||||||
|
if is_failure(error_status) {
|
||||||
spawn_display(path_override);
|
spawn_display(path_override);
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if is_failure(error_status) {
|
if is_failure(error_status) {
|
||||||
@@ -83,10 +85,12 @@ pub unsafe extern "C" fn pam_sm_authenticate(
|
|||||||
argc: c_int,
|
argc: c_int,
|
||||||
argv: *const *const c_char,
|
argv: *const *const c_char,
|
||||||
) -> c_int {
|
) -> c_int {
|
||||||
|
if pamh.is_null() { return PAM_IGNORE; }
|
||||||
|
|
||||||
let args = argc_argv(argc, argv);
|
let args = argc_argv(argc, argv);
|
||||||
let display_path = read_display_path_arg(args);
|
let display_path = read_display_path_arg(args);
|
||||||
|
|
||||||
let key = match CString::new("ahfail") {
|
let key = match CString::new("dk.weircon.ahfail") {
|
||||||
Ok(k) => k,
|
Ok(k) => k,
|
||||||
Err(_) => return PAM_IGNORE,
|
Err(_) => return PAM_IGNORE,
|
||||||
};
|
};
|
||||||
@@ -97,7 +101,11 @@ pub unsafe extern "C" fn pam_sm_authenticate(
|
|||||||
None => std::ptr::null_mut(),
|
None => std::ptr::null_mut(),
|
||||||
};
|
};
|
||||||
|
|
||||||
pam_set_data(pamh, key.as_ptr(), path_ptr, Some(ahfail_cleanup));
|
let ret = pam_set_data(pamh, key.as_ptr(), path_ptr, Some(ahfail_cleanup));
|
||||||
|
if ret != PAM_SUCCESS && !path_ptr.is_null() {
|
||||||
|
// pam_set_data failed — cleanup will never fire, so free the Box ourselves.
|
||||||
|
drop(Box::from_raw(path_ptr as *mut String));
|
||||||
|
}
|
||||||
PAM_IGNORE
|
PAM_IGNORE
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -163,8 +171,12 @@ fn spawn_display(path_override: Option<String>) {
|
|||||||
// Intermediate: fork again then exit immediately.
|
// Intermediate: fork again then exit immediately.
|
||||||
let pid2: pid_t = libc::fork();
|
let pid2: pid_t = libc::fork();
|
||||||
if pid2 != 0 { libc::_exit(0); }
|
if pid2 != 0 { libc::_exit(0); }
|
||||||
// Grandchild: close inherited fds, exec ahfail-display.
|
// Grandchild: detach from PAM daemon's session, close all inherited fds, exec.
|
||||||
libc::close(0); libc::close(1); libc::close(2);
|
libc::setsid();
|
||||||
|
let max_fd = libc::sysconf(libc::_SC_OPEN_MAX) as c_int;
|
||||||
|
for fd in 0..max_fd.min(4096) {
|
||||||
|
libc::close(fd);
|
||||||
|
}
|
||||||
let args: [*const c_char; 2] = [cpath.as_ptr(), std::ptr::null()];
|
let args: [*const c_char; 2] = [cpath.as_ptr(), std::ptr::null()];
|
||||||
libc::execv(cpath.as_ptr(), args.as_ptr());
|
libc::execv(cpath.as_ptr(), args.as_ptr());
|
||||||
libc::_exit(1);
|
libc::_exit(1);
|
||||||
|
|||||||
Reference in New Issue
Block a user