comparison 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
comparison
equal deleted inserted replaced
170:f052e2417195 171:e27c5c667a5a
1 use crate::constants::{ErrorCode, RawFlags, Result};
2 use crate::libpam::handle::LibPamHandle;
3 use crate::module::PamModule;
4 use crate::{AuthnFlags, AuthtokAction, BaseFlags, CredAction};
5 use std::ffi::{c_char, c_int, c_void, CStr};
6
1 /// Generates the dynamic library entry points for a PAM module 7 /// Generates the dynamic library entry points for a PAM module
2 /// 8 ///
3 /// Calling `pam_hooks!(SomeType)` on a type that implements 9 /// Calling `pam_hooks!(SomeType)` on a type that implements
4 /// [`PamModule`](crate::PamModule) will generate the exported 10 /// [`PamModule`] will generate the exported
5 /// `extern "C"` functions that PAM uses to call into your module. 11 /// `extern "C"` functions that PAM uses to call into your module.
6 /// 12 ///
7 /// ## Examples: 13 /// ## Examples:
8 /// 14 ///
9 /// Here is full example of a PAM module that would authenticate 15 /// Here is full example of a PAM module that would authenticate
39 /// ``` 45 /// ```
40 #[macro_export] 46 #[macro_export]
41 macro_rules! pam_hooks { 47 macro_rules! pam_hooks {
42 ($ident:ident) => { 48 ($ident:ident) => {
43 mod _pam_hooks_scope { 49 mod _pam_hooks_scope {
44 use std::ffi::{c_char, c_int, c_void, CStr}; 50 use std::ffi::{c_char, c_int, c_void};
45 use $crate::{ 51 use $crate::ModuleExporter;
46 AuthnFlags, AuthtokAction, BaseFlags, CredAction, ErrorCode, LibPamHandle, 52 use $crate::constants::{RawFlags, ReturnCode};
47 PamModule,
48 };
49 53
50 macro_rules! handle { 54 macro_rules! export {
51 ($pamh:ident) => { 55 ($func:ident) => {
52 match unsafe { $pamh.cast::<LibPamHandle>().as_mut() } { 56 #[no_mangle]
53 Some(handle) => handle, 57 unsafe extern "C" fn $func(
54 None => return ErrorCode::Ignore.into(), 58 pamh: *mut c_void,
59 flags: RawFlags,
60 argc: c_int,
61 argv: *const *const c_char,
62 ) -> c_int {
63 let ret: ReturnCode = ModuleExporter::$func::<super::$ident>(
64 pamh, flags, argc, argv
65 ).into();
66 ret.into()
55 } 67 }
56 }; 68 };
57 } 69 }
58 70
59 #[no_mangle] 71 export!(pam_sm_acct_mgmt);
60 extern "C" fn pam_sm_acct_mgmt( 72 export!(pam_sm_authenticate);
61 pamh: *mut c_void, 73 export!(pam_sm_chauthtok);
62 flags: AuthnFlags, 74 export!(pam_sm_close_session);
63 argc: c_int, 75 export!(pam_sm_open_session);
64 argv: *const *const c_char, 76 export!(pam_sm_setcred);
65 ) -> c_int {
66 let handle = handle!(pamh);
67 let args = extract_argv(argc, argv);
68 ErrorCode::result_to_c(super::$ident::account_management(handle, args, flags))
69 }
70
71 #[no_mangle]
72 extern "C" fn pam_sm_authenticate(
73 pamh: *mut c_void,
74 flags: AuthnFlags,
75 argc: c_int,
76 argv: *const *const c_char,
77 ) -> c_int {
78 let handle = handle!(pamh);
79 let args = extract_argv(argc, argv);
80 ErrorCode::result_to_c(super::$ident::authenticate(handle, args, flags))
81 }
82
83 #[no_mangle]
84 extern "C" fn pam_sm_chauthtok(
85 pamh: *mut c_void,
86 flags: c_int,
87 argc: c_int,
88 argv: *const *const c_char,
89 ) -> c_int {
90 let handle = handle!(pamh);
91 let (action, flags) = match AuthtokAction::extract(flags) {
92 Ok(val) => val,
93 Err(e) => return e.into(),
94 };
95 let args = extract_argv(argc, argv);
96 ErrorCode::result_to_c(super::$ident::change_authtok(handle, args, action, flags))
97 }
98
99 #[no_mangle]
100 extern "C" fn pam_sm_close_session(
101 pamh: *mut c_void,
102 flags: BaseFlags,
103 argc: c_int,
104 argv: *const *const c_char,
105 ) -> c_int {
106 let handle = handle!(pamh);
107 let args = extract_argv(argc, argv);
108 ErrorCode::result_to_c(super::$ident::close_session(handle, args, flags))
109 }
110
111 #[no_mangle]
112 extern "C" fn pam_sm_open_session(
113 pamh: *mut c_void,
114 flags: BaseFlags,
115 argc: c_int,
116 argv: *const *const c_char,
117 ) -> c_int {
118 let handle = handle!(pamh);
119 let args = extract_argv(argc, argv);
120 ErrorCode::result_to_c(super::$ident::open_session(handle, args, flags))
121 }
122
123 #[no_mangle]
124 extern "C" fn pam_sm_setcred(
125 pamh: *mut c_void,
126 flags: c_int,
127 argc: c_int,
128 argv: *const *const c_char,
129 ) -> c_int {
130 let handle = handle!(pamh);
131 let (action, flags) = match CredAction::extract(flags) {
132 Ok(val) => val,
133 Err(e) => return e.into(),
134 };
135 let args = extract_argv(argc, argv);
136 ErrorCode::result_to_c(super::$ident::set_credentials(handle, args, action, flags))
137 }
138
139 /// Turns `argc`/`argv` into a [Vec] of [CStr]s.
140 ///
141 /// # Safety
142 ///
143 /// We use this only with arguments we get from `libpam`, which we kind of have to trust.
144 fn extract_argv<'a>(argc: c_int, argv: *const *const c_char) -> Vec<&'a CStr> {
145 (0..argc)
146 .map(|o| unsafe { CStr::from_ptr(*argv.offset(o as isize) as *const c_char) })
147 .collect()
148 }
149 } 77 }
150 }; 78 };
79 }
80
81 #[doc(hidden)]
82 pub struct ModuleExporter;
83
84 // All of the below are only intended to be called directly from C.
85 #[allow(clippy::missing_safety_doc)]
86 impl ModuleExporter {
87 pub unsafe fn pam_sm_acct_mgmt<M: PamModule<LibPamHandle>>(
88 pamh: *mut c_void,
89 flags: RawFlags,
90 argc: c_int,
91 argv: *const *const c_char,
92 ) -> Result<()> {
93 let handle = wrap(pamh)?;
94 let args = extract_argv(argc, argv);
95 M::account_management(handle, args, AuthnFlags::from(flags))
96 }
97
98 pub unsafe fn pam_sm_authenticate<M: PamModule<LibPamHandle>>(
99 pamh: *mut c_void,
100 flags: RawFlags,
101 argc: c_int,
102 argv: *const *const c_char,
103 ) -> Result<()> {
104 let handle = wrap(pamh)?;
105 let args = extract_argv(argc, argv);
106 M::authenticate(handle, args, AuthnFlags::from(flags))
107 }
108
109 pub unsafe fn pam_sm_chauthtok<M: PamModule<LibPamHandle>>(
110 pamh: *mut c_void,
111 flags: RawFlags,
112 argc: c_int,
113 argv: *const *const c_char,
114 ) -> Result<()> {
115 let handle = wrap(pamh)?;
116 let (action, flags) = AuthtokAction::extract(flags)?;
117 let args = extract_argv(argc, argv);
118 M::change_authtok(handle, args, action, flags)
119 }
120
121 pub unsafe fn pam_sm_close_session<M: PamModule<LibPamHandle>>(
122 pamh: *mut c_void,
123 flags: RawFlags,
124 argc: c_int,
125 argv: *const *const c_char,
126 ) -> Result<()> {
127 let handle = wrap(pamh)?;
128 let args = extract_argv(argc, argv);
129 M::close_session(handle, args, BaseFlags::from(flags))
130 }
131
132 pub unsafe fn pam_sm_open_session<M: PamModule<LibPamHandle>>(
133 pamh: *mut c_void,
134 flags: RawFlags,
135 argc: c_int,
136 argv: *const *const c_char,
137 ) -> Result<()> {
138 let handle = wrap(pamh)?;
139 let args = extract_argv(argc, argv);
140 M::open_session(handle, args, BaseFlags::from(flags))
141 }
142
143 pub unsafe fn pam_sm_setcred<M: PamModule<LibPamHandle>>(
144 pamh: *mut c_void,
145 flags: RawFlags,
146 argc: c_int,
147 argv: *const *const c_char,
148 ) -> Result<()> {
149 let handle = wrap(pamh)?;
150 let (action, flags) = CredAction::extract(flags)?;
151 let args = extract_argv(argc, argv);
152 M::set_credentials(handle, args, action, flags)
153 }
154 }
155
156 /// Turns `argc`/`argv` into a [Vec] of [CStr]s.
157 ///
158 /// # Safety
159 ///
160 /// We use this only with arguments we get from `libpam`, which we kind of have to trust.
161 unsafe fn extract_argv<'a>(argc: c_int, argv: *const *const c_char) -> Vec<&'a CStr> {
162 (0..argc)
163 .map(|o| unsafe { CStr::from_ptr(*argv.offset(o as isize) as *const c_char) })
164 .collect()
165 }
166
167 /// Wraps the pointer in a PAM handle, or returns an error if it's null.
168 ///
169 /// # Safety
170 ///
171 /// It's up to you to pass a valid handle.
172 unsafe fn wrap<'a>(handle: *mut c_void) -> Result<&'a mut LibPamHandle> {
173 handle.cast::<LibPamHandle>().as_mut().ok_or(ErrorCode::SystemError)
151 } 174 }
152 175
153 #[cfg(test)] 176 #[cfg(test)]
154 mod tests { 177 mod tests {
155 // Compile-time test that the `pam_hooks` macro compiles. 178 // Compile-time test that the `pam_hooks` macro compiles.