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);
+}