comparison src/module.rs @ 66:a674799a5cd3

Make `PamHandle` and `PamModuleHandle` traits. This creates traits for PAM functionality and pulls the definitions of that functionality out of the original `PamHandle` (renamed to `LibPamHandle`) and into those traits. This supports testing PAM module implementations using mock PAM library implementations. Also uses a better representation of opaque pointers.
author Paul Fisher <paul@pfish.zone>
date Tue, 27 May 2025 14:37:28 -0400
parents bbe84835d6db
children
comparison
equal deleted inserted replaced
65:8e507c7af9cf 66:a674799a5cd3
1 //! Functions and types useful for implementing a PAM module. 1 //! Functions and types useful for implementing a PAM module.
2 2
3 use crate::constants::{ErrorCode, Flags, Result}; 3 use crate::constants::{ErrorCode, Flags, Result};
4 use crate::handle::PamHandle; 4 use crate::handle::PamModuleHandle;
5 use std::ffi::CStr; 5 use std::ffi::CStr;
6 6
7 /// A trait for a PAM module to implement. 7 /// A trait for a PAM module to implement.
8 /// 8 ///
9 /// The default implementations of all these hooks tell PAM to ignore them 9 /// The default implementations of all these hooks tell PAM to ignore them
16 /// and the [PAM Module Writer’s Guide][mwg]. 16 /// and the [PAM Module Writer’s Guide][mwg].
17 /// 17 ///
18 /// [manpage]: https://www.man7.org/linux/man-pages/man3/pam.3.html 18 /// [manpage]: https://www.man7.org/linux/man-pages/man3/pam.3.html
19 /// [mwg]: https://www.chiark.greenend.org.uk/doc/libpam-doc/html/Linux-PAM_MWG.html 19 /// [mwg]: https://www.chiark.greenend.org.uk/doc/libpam-doc/html/Linux-PAM_MWG.html
20 #[allow(unused_variables)] 20 #[allow(unused_variables)]
21 pub trait PamModule { 21 pub trait PamModule<T: PamModuleHandle> {
22 // Functions for auth modules. 22 // Functions for auth modules.
23 23
24 /// Authenticate the user. 24 /// Authenticate the user.
25 /// 25 ///
26 /// This is probably the first thing you want to implement. 26 /// This is probably the first thing you want to implement.
27 /// In most cases, you will want to get the user and password, 27 /// In most cases, you will want to get the user and password,
28 /// using [`PamHandle::get_user`] and [`PamHandle::get_authtok`], 28 /// using [`LibPamHandle::get_user`] and [`LibPamHandle::get_authtok`],
29 /// and verify them against something. 29 /// and verify them against something.
30 /// 30 ///
31 /// See [the Module Writer's Guide entry for `pam_sm_authenticate`][mwg] 31 /// See [the Module Writer's Guide entry for `pam_sm_authenticate`][mwg]
32 /// for more information. 32 /// for more information.
33 /// 33 ///
53 /// - [`ErrorCode::UserUnknown`]: The supplied username is not known by this service. 53 /// - [`ErrorCode::UserUnknown`]: The supplied username is not known by this service.
54 /// - [`ErrorCode::MaxTries`]: The user has tried authenticating too many times. 54 /// - [`ErrorCode::MaxTries`]: The user has tried authenticating too many times.
55 /// They should not try again. 55 /// They should not try again.
56 /// 56 ///
57 /// [mwg]: https://www.chiark.greenend.org.uk/doc/libpam-doc/html/mwg-expected-of-module-auth.html#mwg-pam_sm_authenticate 57 /// [mwg]: https://www.chiark.greenend.org.uk/doc/libpam-doc/html/mwg-expected-of-module-auth.html#mwg-pam_sm_authenticate
58 fn authenticate(handle: &mut PamHandle, args: Vec<&CStr>, flags: Flags) -> Result<()> { 58 fn authenticate(handle: &mut T, args: Vec<&CStr>, flags: Flags) -> Result<()> {
59 Err(ErrorCode::Ignore) 59 Err(ErrorCode::Ignore)
60 } 60 }
61 61
62 /// Perform "account management". 62 /// Perform "account management".
63 /// 63 ///
96 /// this module in [`Self::change_authtok`]. 96 /// this module in [`Self::change_authtok`].
97 /// - [`ErrorCode::PermissionDenied`]: This one is pretty self-explanatory. 97 /// - [`ErrorCode::PermissionDenied`]: This one is pretty self-explanatory.
98 /// - [`ErrorCode::UserUnknown`]: The supplied username is not known by this service. 98 /// - [`ErrorCode::UserUnknown`]: The supplied username is not known by this service.
99 /// 99 ///
100 /// [mwg]: https://www.chiark.greenend.org.uk/doc/libpam-doc/html/mwg-expected-of-module-acct.html#mwg-pam_sm_acct_mgmt 100 /// [mwg]: https://www.chiark.greenend.org.uk/doc/libpam-doc/html/mwg-expected-of-module-acct.html#mwg-pam_sm_acct_mgmt
101 fn account_management(handle: &mut PamHandle, args: Vec<&CStr>, flags: Flags) -> Result<()> { 101 fn account_management(handle: &mut T, args: Vec<&CStr>, flags: Flags) -> Result<()> {
102 Err(ErrorCode::Ignore) 102 Err(ErrorCode::Ignore)
103 } 103 }
104 104
105 /// Set credentials on this session. 105 /// Set credentials on this session.
106 /// 106 ///
132 /// - [`ErrorCode::CredentialsExpired`]: The credentials have expired. 132 /// - [`ErrorCode::CredentialsExpired`]: The credentials have expired.
133 /// - [`ErrorCode::CredentialsError`]: Some other error occurred when setting credentials. 133 /// - [`ErrorCode::CredentialsError`]: Some other error occurred when setting credentials.
134 /// - [`ErrorCode::UserUnknown`]: The supplied username is not known by this service. 134 /// - [`ErrorCode::UserUnknown`]: The supplied username is not known by this service.
135 /// 135 ///
136 /// [mwg]: https://www.chiark.greenend.org.uk/doc/libpam-doc/html/mwg-expected-of-module-auth.html#mwg-pam_sm_setcred 136 /// [mwg]: https://www.chiark.greenend.org.uk/doc/libpam-doc/html/mwg-expected-of-module-auth.html#mwg-pam_sm_setcred
137 fn set_credentials(handle: &mut PamHandle, args: Vec<&CStr>, flags: Flags) -> Result<()> { 137 fn set_credentials(handle: &mut T, args: Vec<&CStr>, flags: Flags) -> Result<()> {
138 Err(ErrorCode::Ignore) 138 Err(ErrorCode::Ignore)
139 } 139 }
140 140
141 // Function for chauthtok modules. 141 // Function for chauthtok modules.
142 142
180 /// - [`ErrorCode::TryAgain`]: When the preliminary check is unsuccessful, 180 /// - [`ErrorCode::TryAgain`]: When the preliminary check is unsuccessful,
181 /// ask the user for a new authentication token. 181 /// ask the user for a new authentication token.
182 /// - [`ErrorCode::UserUnknown`]: The supplied username is not known by this service. 182 /// - [`ErrorCode::UserUnknown`]: The supplied username is not known by this service.
183 /// 183 ///
184 /// [mwg]: https://www.chiark.greenend.org.uk/doc/libpam-doc/html/mwg-expected-of-module-chauthtok.html#mwg-pam_sm_chauthtok 184 /// [mwg]: https://www.chiark.greenend.org.uk/doc/libpam-doc/html/mwg-expected-of-module-chauthtok.html#mwg-pam_sm_chauthtok
185 fn change_authtok(handle: &mut PamHandle, args: Vec<&CStr>, flags: Flags) -> Result<()> { 185 fn change_authtok(handle: &mut T, args: Vec<&CStr>, flags: Flags) -> Result<()> {
186 Err(ErrorCode::Ignore) 186 Err(ErrorCode::Ignore)
187 } 187 }
188 188
189 // Functions for session modules. 189 // Functions for session modules.
190 190
204 /// A sensible error code to return is: 204 /// A sensible error code to return is:
205 /// 205 ///
206 /// - [`ErrorCode::SessionError`]: Cannot make an entry for this session. 206 /// - [`ErrorCode::SessionError`]: Cannot make an entry for this session.
207 /// 207 ///
208 /// [mwg]: https://www.chiark.greenend.org.uk/doc/libpam-doc/html/mwg-expected-of-module-session.html#mwg-pam_sm_open_session 208 /// [mwg]: https://www.chiark.greenend.org.uk/doc/libpam-doc/html/mwg-expected-of-module-session.html#mwg-pam_sm_open_session
209 fn open_session(handle: &mut PamHandle, args: Vec<&CStr>, flags: Flags) -> Result<()> { 209 fn open_session(handle: &mut T, args: Vec<&CStr>, flags: Flags) -> Result<()> {
210 Err(ErrorCode::Ignore) 210 Err(ErrorCode::Ignore)
211 } 211 }
212 212
213 /// Called when a session is being terminated. 213 /// Called when a session is being terminated.
214 /// 214 ///
226 /// A sensible error code to return is: 226 /// A sensible error code to return is:
227 /// 227 ///
228 /// - [`ErrorCode::SessionError`]: Cannot remove an entry for this session. 228 /// - [`ErrorCode::SessionError`]: Cannot remove an entry for this session.
229 /// 229 ///
230 /// [mwg]: https://www.chiark.greenend.org.uk/doc/libpam-doc/html/mwg-expected-of-module-session.html#mwg-pam_sm_close_session 230 /// [mwg]: https://www.chiark.greenend.org.uk/doc/libpam-doc/html/mwg-expected-of-module-session.html#mwg-pam_sm_close_session
231 fn close_session(handle: &mut PamHandle, args: Vec<&CStr>, flags: Flags) -> Result<()> { 231 fn close_session(handle: &mut T, args: Vec<&CStr>, flags: Flags) -> Result<()> {
232 Err(ErrorCode::Ignore) 232 Err(ErrorCode::Ignore)
233 } 233 }
234 } 234 }
235 235
236 /// Generates the dynamic library entry points for a [PamModule] implementation. 236 /// Generates the dynamic library entry points for a [PamModule] implementation.
242 /// ## Examples: 242 /// ## Examples:
243 /// 243 ///
244 /// Here is full example of a PAM module that would authenticate and authorize everybody: 244 /// Here is full example of a PAM module that would authenticate and authorize everybody:
245 /// 245 ///
246 /// ```no_run 246 /// ```no_run
247 /// use nonstick::{Flags, PamHandle, PamModule, Result as PamResult, pam_hooks}; 247 /// use nonstick::{Flags, LibPamHandle, PamModule, PamModuleHandle, Result as PamResult, pam_hooks};
248 /// use std::ffi::CStr; 248 /// use std::ffi::CStr;
249 /// # fn main() {} 249 /// # fn main() {}
250 /// 250 ///
251 /// struct MyPamModule; 251 /// struct MyPamModule;
252 /// pam_hooks!(MyPamModule); 252 /// pam_hooks!(MyPamModule);
253 /// 253 ///
254 /// impl PamModule for MyPamModule { 254 /// impl<T: PamModuleHandle> PamModule<T> for MyPamModule {
255 /// fn authenticate(handle: &mut PamHandle, args: Vec<&CStr>, flags: Flags) -> PamResult<()> { 255 /// fn authenticate(handle: &mut T, args: Vec<&CStr>, flags: Flags) -> PamResult<()> {
256 /// let password = handle.get_authtok(Some("what's your password?"))?; 256 /// let password = handle.get_authtok(Some("what's your password?"))?;
257 /// eprintln!("If you say your password is {:?}, who am I to disagree!", password.unsecure()); 257 /// eprintln!("If you say your password is {:?}, who am I to disagree!", password.unsecure());
258 /// Ok(()) 258 /// Ok(())
259 /// } 259 /// }
260 /// 260 ///
261 /// fn account_management(handle: &mut PamHandle, args: Vec<&CStr>, flags: Flags) -> PamResult<()> { 261 /// fn account_management(handle: &mut T, args: Vec<&CStr>, flags: Flags) -> PamResult<()> {
262 /// let username = handle.get_user(None)?; 262 /// let username = handle.get_user(None)?;
263 /// // You should use a Conversation to communicate with the user 263 /// // You should use a Conversation to communicate with the user
264 /// // instead of writing to the console, but this is just an example. 264 /// // instead of writing to the console, but this is just an example.
265 /// eprintln!("Hello {username}! I trust you unconditionally!"); 265 /// eprintln!("Hello {username}! I trust you unconditionally!");
266 /// Ok(()) 266 /// Ok(())
270 #[macro_export] 270 #[macro_export]
271 macro_rules! pam_hooks { 271 macro_rules! pam_hooks {
272 ($ident:ident) => { 272 ($ident:ident) => {
273 mod _pam_hooks_scope { 273 mod _pam_hooks_scope {
274 use std::ffi::{c_char, c_int, CStr}; 274 use std::ffi::{c_char, c_int, CStr};
275 use $crate::{ErrorCode, Flags, PamModule}; 275 use $crate::{ErrorCode, Flags, LibPamHandle, PamModule};
276 276
277 #[no_mangle] 277 #[no_mangle]
278 extern "C" fn pam_sm_acct_mgmt( 278 extern "C" fn pam_sm_acct_mgmt(
279 pamh: *mut libc::c_void, 279 pamh: *mut libc::c_void,
280 flags: Flags, 280 flags: Flags,
281 argc: c_int, 281 argc: c_int,
282 argv: *const *const c_char, 282 argv: *const *const c_char,
283 ) -> c_int { 283 ) -> c_int {
284 let args = extract_argv(argc, argv); 284 let args = extract_argv(argc, argv);
285 ErrorCode::result_to_c(super::$ident::account_management( 285 ErrorCode::result_to_c(super::$ident::account_management(
286 &mut pamh.into(), 286 unsafe { LibPamHandle::from_ptr(pamh) },
287 args, 287 args,
288 flags, 288 flags,
289 )) 289 ))
290 } 290 }
291 291
295 flags: Flags, 295 flags: Flags,
296 argc: c_int, 296 argc: c_int,
297 argv: *const *const c_char, 297 argv: *const *const c_char,
298 ) -> c_int { 298 ) -> c_int {
299 let args = extract_argv(argc, argv); 299 let args = extract_argv(argc, argv);
300 ErrorCode::result_to_c(super::$ident::authenticate(&mut pamh.into(), args, flags)) 300 ErrorCode::result_to_c(super::$ident::authenticate(
301 unsafe { LibPamHandle::from_ptr(pamh) },
302 args,
303 flags,
304 ))
301 } 305 }
302 306
303 #[no_mangle] 307 #[no_mangle]
304 extern "C" fn pam_sm_chauthtok( 308 extern "C" fn pam_sm_chauthtok(
305 pamh: *mut libc::c_void, 309 pamh: *mut libc::c_void,
306 flags: Flags, 310 flags: Flags,
307 argc: c_int, 311 argc: c_int,
308 argv: *const *const c_char, 312 argv: *const *const c_char,
309 ) -> c_int { 313 ) -> c_int {
310 let args = extract_argv(argc, argv); 314 let args = extract_argv(argc, argv);
311 ErrorCode::result_to_c(super::$ident::change_authtok(&mut pamh.into(), args, flags)) 315 ErrorCode::result_to_c(super::$ident::change_authtok(
316 unsafe { LibPamHandle::from_ptr(pamh) },
317 args,
318 flags,
319 ))
312 } 320 }
313 321
314 #[no_mangle] 322 #[no_mangle]
315 extern "C" fn pam_sm_close_session( 323 extern "C" fn pam_sm_close_session(
316 pamh: *mut libc::c_void, 324 pamh: *mut libc::c_void,
317 flags: Flags, 325 flags: Flags,
318 argc: c_int, 326 argc: c_int,
319 argv: *const *const c_char, 327 argv: *const *const c_char,
320 ) -> c_int { 328 ) -> c_int {
321 let args = extract_argv(argc, argv); 329 let args = extract_argv(argc, argv);
322 ErrorCode::result_to_c(super::$ident::close_session(&mut pamh.into(), args, flags)) 330 ErrorCode::result_to_c(super::$ident::close_session(
331 unsafe { LibPamHandle::from_ptr(pamh) },
332 args,
333 flags,
334 ))
323 } 335 }
324 336
325 #[no_mangle] 337 #[no_mangle]
326 extern "C" fn pam_sm_open_session( 338 extern "C" fn pam_sm_open_session(
327 pamh: *mut libc::c_void, 339 pamh: *mut libc::c_void,
328 flags: Flags, 340 flags: Flags,
329 argc: c_int, 341 argc: c_int,
330 argv: *const *const c_char, 342 argv: *const *const c_char,
331 ) -> c_int { 343 ) -> c_int {
332 let args = extract_argv(argc, argv); 344 let args = extract_argv(argc, argv);
333 ErrorCode::result_to_c(super::$ident::open_session(&mut pamh.into(), args, flags)) 345 ErrorCode::result_to_c(super::$ident::open_session(
346 unsafe { LibPamHandle::from_ptr(pamh) },
347 args,
348 flags,
349 ))
334 } 350 }
335 351
336 #[no_mangle] 352 #[no_mangle]
337 extern "C" fn pam_sm_setcred( 353 extern "C" fn pam_sm_setcred(
338 pamh: *mut libc::c_void, 354 pamh: *mut libc::c_void,
340 argc: c_int, 356 argc: c_int,
341 argv: *const *const c_char, 357 argv: *const *const c_char,
342 ) -> c_int { 358 ) -> c_int {
343 let args = extract_argv(argc, argv); 359 let args = extract_argv(argc, argv);
344 ErrorCode::result_to_c(super::$ident::set_credentials( 360 ErrorCode::result_to_c(super::$ident::set_credentials(
345 &mut pamh.into(), 361 unsafe { LibPamHandle::from_ptr(pamh) },
346 args, 362 args,
347 flags, 363 flags,
348 )) 364 ))
349 } 365 }
350 366
362 }; 378 };
363 } 379 }
364 380
365 #[cfg(test)] 381 #[cfg(test)]
366 pub mod test { 382 pub mod test {
367 use crate::module::PamModule; 383 use crate::module::{PamModule, PamModuleHandle};
368 384
369 struct Foo; 385 struct Foo;
370 impl PamModule for Foo {} 386 impl<T: PamModuleHandle> PamModule<T> for Foo {}
371 387
372 pam_hooks!(Foo); 388 pam_hooks!(Foo);
373 } 389 }