Mercurial > crates > nonstick
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 | 9f8381a1c09c |
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 } |
