Mercurial > crates > nonstick
view src/libpam/module.rs @ 166:2f5913131295
Separate flag/action flags into flags and action.
This also individualizes the type of flag for each PAM function,
so that you can only call a function with the right flags and values.
author | Paul Fisher <paul@pfish.zone> |
---|---|
date | Tue, 15 Jul 2025 00:32:24 -0400 |
parents | 1bc52025156b |
children | e27c5c667a5a |
line wrap: on
line source
/// Generates the dynamic library entry points for a PAM module /// /// Calling `pam_hooks!(SomeType)` on a type that implements /// [`PamModule`](crate::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: /// /// ```no_run /// use nonstick::{ /// pam_hooks, ConversationAdapter, AuthnFlags, LibPamTransaction, ModuleClient, PamModule, /// Result as PamResult, /// }; /// use std::ffi::CStr; /// # fn main() {} /// /// struct MyPamModule; /// pam_hooks!(MyPamModule); /// /// impl<T: ModuleClient> PamModule<T> for MyPamModule { /// fn authenticate(handle: &mut T, args: Vec<&CStr>, flags: AuthnFlags) -> PamResult<()> { /// let password = handle.authtok(Some("what's your password?".as_ref()))?; /// let response = /// format!("If you say your password is {password:?}, who am I to disagree?"); /// handle.info_msg(&response); /// Ok(()) /// } /// /// fn account_management(handle: &mut T, args: Vec<&CStr>, flags: AuthnFlags) -> PamResult<()> { /// let username = handle.username(None)?; /// let response = format!("Hello {username:?}! I trust you unconditionally."); /// handle.info_msg(&response); /// Ok(()) /// } /// } /// ``` #[macro_export] macro_rules! pam_hooks { ($ident:ident) => { mod _pam_hooks_scope { use std::ffi::{c_char, c_int, c_void, CStr}; use $crate::{ AuthnFlags, AuthtokAction, BaseFlags, CredAction, ErrorCode, LibPamHandle, PamModule, }; macro_rules! handle { ($pamh:ident) => { match unsafe { $pamh.cast::<LibPamHandle>().as_mut() } { Some(handle) => handle, None => return ErrorCode::Ignore.into(), } }; } #[no_mangle] extern "C" fn pam_sm_acct_mgmt( pamh: *mut c_void, flags: AuthnFlags, argc: c_int, argv: *const *const c_char, ) -> c_int { let handle = handle!(pamh); let args = extract_argv(argc, argv); ErrorCode::result_to_c(super::$ident::account_management(handle, args, flags)) } #[no_mangle] extern "C" fn pam_sm_authenticate( pamh: *mut c_void, flags: AuthnFlags, argc: c_int, argv: *const *const c_char, ) -> c_int { let handle = handle!(pamh); let args = extract_argv(argc, argv); ErrorCode::result_to_c(super::$ident::authenticate(handle, args, flags)) } #[no_mangle] extern "C" fn pam_sm_chauthtok( pamh: *mut c_void, flags: c_int, argc: c_int, argv: *const *const c_char, ) -> c_int { let handle = handle!(pamh); let (action, flags) = match AuthtokAction::extract(flags) { Ok(val) => val, Err(e) => return e.into(), }; let args = extract_argv(argc, argv); ErrorCode::result_to_c(super::$ident::change_authtok(handle, args, action, flags)) } #[no_mangle] extern "C" fn pam_sm_close_session( pamh: *mut c_void, flags: BaseFlags, argc: c_int, argv: *const *const c_char, ) -> c_int { let handle = handle!(pamh); let args = extract_argv(argc, argv); ErrorCode::result_to_c(super::$ident::close_session(handle, args, flags)) } #[no_mangle] extern "C" fn pam_sm_open_session( pamh: *mut c_void, flags: BaseFlags, argc: c_int, argv: *const *const c_char, ) -> c_int { let handle = handle!(pamh); let args = extract_argv(argc, argv); ErrorCode::result_to_c(super::$ident::open_session(handle, args, flags)) } #[no_mangle] extern "C" fn pam_sm_setcred( pamh: *mut c_void, flags: c_int, argc: c_int, argv: *const *const c_char, ) -> c_int { let handle = handle!(pamh); let (action, flags) = match CredAction::extract(flags) { Ok(val) => val, Err(e) => return e.into(), }; let args = extract_argv(argc, argv); ErrorCode::result_to_c(super::$ident::set_credentials(handle, args, action, flags)) } /// 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::{ModuleClient, PamModule}; struct Foo; impl<T: ModuleClient> PamModule<T> for Foo {} pam_hooks!(Foo); }