Mercurial > crates > nonstick
diff src/libpam/module.rs @ 75:c30811b4afae
rename pam_ffi submodule to libpam.
author | Paul Fisher <paul@pfish.zone> |
---|---|
date | Fri, 06 Jun 2025 22:35:08 -0400 |
parents | src/pam_ffi/module.rs@c7c596e6388f |
children | 351bdc13005e |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/libpam/module.rs Fri Jun 06 22:35:08 2025 -0400 @@ -0,0 +1,153 @@ +use std::ffi::CStr; + +/// Generates the dynamic library entry points for a [PamModule] implementation. +/// +/// Calling `pam_hooks!(SomeType)` on a type that implements [PamModule] will +/// generate the exported `extern "C"` functions that PAM uses to call into +/// your module. +/// +/// ## Examples: +/// +/// Here is full example of a PAM module that would authenticate and authorize everybody: +/// +/// ``` +/// use nonstick::{Flags, OwnedLibPamHandle, PamModule, PamHandleModule, Result as PamResult, pam_hooks}; +/// use std::ffi::CStr; +/// # fn main() {} +/// +/// struct MyPamModule; +/// pam_hooks!(MyPamModule); +/// +/// impl<T: PamHandleModule> PamModule<T> for MyPamModule { +/// fn authenticate(handle: &mut T, args: Vec<&CStr>, flags: Flags) -> PamResult<()> { +/// let password = handle.get_authtok(Some("what's your password?"))?; +/// handle.info_msg(fmt!("If you say your password is {password:?}, who am I to disagree?")); +/// } +/// +/// fn account_management(handle: &mut T, args: Vec<&CStr>, flags: Flags) -> PamResult<()> { +/// let username = handle.get_user(None)?; +/// handle.info_msg(fmt!("Hello {username}! I trust you unconditionally.")) +/// Ok(()) +/// } +/// } +/// ``` +#[macro_export] +macro_rules! pam_hooks { + ($ident:ident) => { + mod _pam_hooks_scope { + use std::ffi::{c_char, c_int, CStr}; + use $crate::{ErrorCode, Flags, LibPamHandle, PamModule}; + + #[no_mangle] + extern "C" fn pam_sm_acct_mgmt( + pamh: *mut libc::c_void, + flags: Flags, + argc: c_int, + argv: *const *const c_char, + ) -> c_int { + if let Some(handle) = unsafe { pamh.cast::<LibPamHandle>().as_mut() } { + let args = extract_argv(argc, argv); + ErrorCode::result_to_c(super::$ident::account_management(handle, args, flags)) + } else { + ErrorCode::Ignore as c_int + } + } + + #[no_mangle] + extern "C" fn pam_sm_authenticate( + pamh: *mut libc::c_void, + flags: Flags, + argc: c_int, + argv: *const *const c_char, + ) -> c_int { + if let Some(handle) = unsafe { pamh.cast::<LibPamHandle>().as_mut() } { + let args = extract_argv(argc, argv); + ErrorCode::result_to_c(super::$ident::authenticate(handle, args, flags)) + } else { + ErrorCode::Ignore as c_int + } + } + + #[no_mangle] + extern "C" fn pam_sm_chauthtok( + pamh: *mut libc::c_void, + flags: Flags, + argc: c_int, + argv: *const *const c_char, + ) -> c_int { + if let Some(handle) = unsafe { pamh.cast::<LibPamHandle>().as_mut() } { + let args = extract_argv(argc, argv); + ErrorCode::result_to_c(super::$ident::change_authtok(handle, args, flags)) + } else { + ErrorCode::Ignore as c_int + } + } + + #[no_mangle] + extern "C" fn pam_sm_close_session( + pamh: *mut libc::c_void, + flags: Flags, + argc: c_int, + argv: *const *const c_char, + ) -> c_int { + if let Some(handle) = unsafe { pamh.cast::<LibPamHandle>().as_mut() } { + let args = extract_argv(argc, argv); + ErrorCode::result_to_c(super::$ident::close_session(handle, args, flags)) + } else { + ErrorCode::Ignore as c_int + } + } + + #[no_mangle] + extern "C" fn pam_sm_open_session( + pamh: *mut libc::c_void, + flags: Flags, + argc: c_int, + argv: *const *const c_char, + ) -> c_int { + let args = extract_argv(argc, argv); + if let Some(handle) = unsafe { pamh.cast::<LibPamHandle>().as_mut() } { + ErrorCode::result_to_c(super::$ident::open_session(handle, args, flags)) + } else { + ErrorCode::Ignore as c_int + } + } + + #[no_mangle] + extern "C" fn pam_sm_setcred( + pamh: *mut libc::c_void, + flags: Flags, + argc: c_int, + argv: *const *const c_char, + ) -> c_int { + let args = extract_argv(argc, argv); + if let Some(handle) = unsafe { pamh.cast::<LibPamHandle>().as_mut() } { + ErrorCode::result_to_c(super::$ident::set_credentials(handle, args, flags)) + } else { + ErrorCode::Ignore as c_int + } + } + + /// Turns `argc`/`argv` into a [Vec] of [CStr]s. + /// + /// # Safety + /// + /// We use this only with arguments we get from `libpam`, which we kind of have to trust. + fn extract_argv<'a>(argc: c_int, argv: *const *const c_char) -> Vec<&'a CStr> { + (0..argc) + .map(|o| unsafe { CStr::from_ptr(*argv.offset(o as isize) as *const c_char) }) + .collect() + } + } + }; +} + +#[cfg(test)] +mod tests { + // Compile-time test that the `pam_hooks` macro compiles. + use crate::{PamHandleModule, PamModule}; + struct Foo; + impl<T: PamHandleModule> PamModule<T> for Foo {} + + pam_hooks!(Foo); +}