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 |
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 } |