diff src/libpam/module.rs @ 171:e27c5c667a5a

Create full new types for return code and flags, separate end to end. This plumbs the ReturnCode and RawFlags types through the places where we call into or are called from PAM. Also adds Sun documentation to the project.
author Paul Fisher <paul@pfish.zone>
date Fri, 25 Jul 2025 20:52:14 -0400
parents 2f5913131295
children 6727cbe56f4a
line wrap: on
line diff
--- a/src/libpam/module.rs	Wed Jul 16 18:45:20 2025 -0400
+++ b/src/libpam/module.rs	Fri Jul 25 20:52:14 2025 -0400
@@ -1,7 +1,13 @@
+use crate::constants::{ErrorCode, RawFlags, Result};
+use crate::libpam::handle::LibPamHandle;
+use crate::module::PamModule;
+use crate::{AuthnFlags, AuthtokAction, BaseFlags, CredAction};
+use std::ffi::{c_char, c_int, c_void, CStr};
+
 /// 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
+/// [`PamModule`] will generate the exported
 /// `extern "C"` functions that PAM uses to call into your module.
 ///
 /// ## Examples:
@@ -41,115 +47,132 @@
 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,
-            };
+            use std::ffi::{c_char, c_int, c_void};
+            use $crate::ModuleExporter;
+            use $crate::constants::{RawFlags, ReturnCode};
 
-            macro_rules! handle {
-                ($pamh:ident) => {
-                    match unsafe { $pamh.cast::<LibPamHandle>().as_mut() } {
-                        Some(handle) => handle,
-                        None => return ErrorCode::Ignore.into(),
+            macro_rules! export {
+                ($func:ident) => {
+                    #[no_mangle]
+                    unsafe extern "C" fn $func(
+                        pamh: *mut c_void,
+                        flags: RawFlags,
+                        argc: c_int,
+                        argv: *const *const c_char,
+                    ) -> c_int {
+                        let ret: ReturnCode = ModuleExporter::$func::<super::$ident>(
+                            pamh, flags, argc, argv
+                        ).into();
+                        ret.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()
-            }
+            export!(pam_sm_acct_mgmt);
+            export!(pam_sm_authenticate);
+            export!(pam_sm_chauthtok);
+            export!(pam_sm_close_session);
+            export!(pam_sm_open_session);
+            export!(pam_sm_setcred);
         }
     };
 }
 
+#[doc(hidden)]
+pub struct ModuleExporter;
+
+// All of the below are only intended to be called directly from C.
+#[allow(clippy::missing_safety_doc)]
+impl ModuleExporter {
+    pub unsafe fn pam_sm_acct_mgmt<M: PamModule<LibPamHandle>>(
+        pamh: *mut c_void,
+        flags: RawFlags,
+        argc: c_int,
+        argv: *const *const c_char,
+    ) -> Result<()> {
+        let handle = wrap(pamh)?;
+        let args = extract_argv(argc, argv);
+        M::account_management(handle, args, AuthnFlags::from(flags))
+    }
+
+    pub unsafe fn pam_sm_authenticate<M: PamModule<LibPamHandle>>(
+        pamh: *mut c_void,
+        flags: RawFlags,
+        argc: c_int,
+        argv: *const *const c_char,
+    ) -> Result<()> {
+        let handle = wrap(pamh)?;
+        let args = extract_argv(argc, argv);
+        M::authenticate(handle, args, AuthnFlags::from(flags))
+    }
+
+    pub unsafe fn pam_sm_chauthtok<M: PamModule<LibPamHandle>>(
+        pamh: *mut c_void,
+        flags: RawFlags,
+        argc: c_int,
+        argv: *const *const c_char,
+    ) -> Result<()> {
+        let handle = wrap(pamh)?;
+        let (action, flags) = AuthtokAction::extract(flags)?;
+        let args = extract_argv(argc, argv);
+        M::change_authtok(handle, args, action, flags)
+    }
+
+    pub unsafe fn pam_sm_close_session<M: PamModule<LibPamHandle>>(
+        pamh: *mut c_void,
+        flags: RawFlags,
+        argc: c_int,
+        argv: *const *const c_char,
+    ) -> Result<()> {
+        let handle = wrap(pamh)?;
+        let args = extract_argv(argc, argv);
+        M::close_session(handle, args, BaseFlags::from(flags))
+    }
+
+    pub unsafe fn pam_sm_open_session<M: PamModule<LibPamHandle>>(
+        pamh: *mut c_void,
+        flags: RawFlags,
+        argc: c_int,
+        argv: *const *const c_char,
+    ) -> Result<()> {
+        let handle = wrap(pamh)?;
+        let args = extract_argv(argc, argv);
+        M::open_session(handle, args, BaseFlags::from(flags))
+    }
+
+    pub unsafe fn pam_sm_setcred<M: PamModule<LibPamHandle>>(
+        pamh: *mut c_void,
+        flags: RawFlags,
+        argc: c_int,
+        argv: *const *const c_char,
+    ) -> Result<()> {
+        let handle = wrap(pamh)?;
+        let (action, flags) = CredAction::extract(flags)?;
+        let args = extract_argv(argc, argv);
+        M::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.
+unsafe 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()
+}
+
+/// Wraps the pointer in a PAM handle, or returns an error if it's null.
+///
+/// # Safety
+///
+/// It's up to you to pass a valid handle.
+unsafe fn wrap<'a>(handle: *mut c_void) -> Result<&'a mut LibPamHandle> {
+    handle.cast::<LibPamHandle>().as_mut().ok_or(ErrorCode::SystemError)
+}
+
 #[cfg(test)]
 mod tests {
     // Compile-time test that the `pam_hooks` macro compiles.