Mercurial > crates > nonstick
comparison src/module.rs @ 74:c7c596e6388f
Make conversations type-safe (last big reorg) (REAL) (NOT CLICKBAIT)
In previous versions of Conversation, you could send messages and then
return messages of the wrong type or in the wrong order or whatever.
The receiver would then have to make sure that there were the right
number of messages and that each message was the right type.
That's annoying.
This change makes the `Message` enum a two-way channel, where the asker
puts their question into it, and then the answerer (the conversation)
puts the answer in and returns control to the asker. The asker then
only has to pull the Answer of the type they wanted out of the message.
author | Paul Fisher <paul@pfish.zone> |
---|---|
date | Fri, 06 Jun 2025 22:21:17 -0400 |
parents | ac6881304c78 |
children | 002adfb98c5c |
comparison
equal
deleted
inserted
replaced
73:ac6881304c78 | 74:c7c596e6388f |
---|---|
232 /// [mwg]: https://www.chiark.greenend.org.uk/doc/libpam-doc/html/mwg-expected-of-module-session.html#mwg-pam_sm_close_session | 232 /// [mwg]: https://www.chiark.greenend.org.uk/doc/libpam-doc/html/mwg-expected-of-module-session.html#mwg-pam_sm_close_session |
233 fn close_session(handle: &mut T, args: Vec<&CStr>, flags: Flags) -> Result<()> { | 233 fn close_session(handle: &mut T, args: Vec<&CStr>, flags: Flags) -> Result<()> { |
234 Err(ErrorCode::Ignore) | 234 Err(ErrorCode::Ignore) |
235 } | 235 } |
236 } | 236 } |
237 | |
238 /// Generates the dynamic library entry points for a [PamModule] implementation. | |
239 /// | |
240 /// Calling `pam_hooks!(SomeType)` on a type that implements [PamModule] will | |
241 /// generate the exported `extern "C"` functions that PAM uses to call into | |
242 /// your module. | |
243 /// | |
244 /// ## Examples: | |
245 /// | |
246 /// Here is full example of a PAM module that would authenticate and authorize everybody: | |
247 /// | |
248 /// ```no_run | |
249 /// use nonstick::{Flags, OwnedLibPamHandle, PamModule, PamHandleModule, Result as PamResult, pam_hooks}; | |
250 /// use std::ffi::CStr; | |
251 /// # fn main() {} | |
252 /// | |
253 /// struct MyPamModule; | |
254 /// pam_hooks!(MyPamModule); | |
255 /// | |
256 /// impl<T: PamHandleModule> PamModule<T> for MyPamModule { | |
257 /// fn authenticate(handle: &mut T, args: Vec<&CStr>, flags: Flags) -> PamResult<()> { | |
258 /// let password = handle.get_authtok(Some("what's your password?"))?; | |
259 /// // You should use a Conversation to communicate with the user | |
260 /// // instead of writing to the console, but this is just an example. | |
261 /// eprintln!("If you say your password is {password:?}, who am I to disagree?"); | |
262 /// Ok(()) | |
263 /// } | |
264 /// | |
265 /// fn account_management(handle: &mut T, args: Vec<&CStr>, flags: Flags) -> PamResult<()> { | |
266 /// let username = handle.get_user(None)?; | |
267 /// eprintln!("Hello {username:?}! I trust you unconditionally!"); | |
268 /// Ok(()) | |
269 /// } | |
270 /// } | |
271 /// ``` | |
272 #[macro_export] | |
273 macro_rules! pam_hooks { | |
274 ($ident:ident) => { | |
275 mod _pam_hooks_scope { | |
276 use std::ffi::{c_char, c_int, CStr}; | |
277 use $crate::{ErrorCode, Flags, LibPamHandle, PamModule}; | |
278 | |
279 #[no_mangle] | |
280 extern "C" fn pam_sm_acct_mgmt( | |
281 pamh: *mut libc::c_void, | |
282 flags: Flags, | |
283 argc: c_int, | |
284 argv: *const *const c_char, | |
285 ) -> c_int { | |
286 if let Some(handle) = unsafe { pamh.cast::<LibPamHandle>().as_mut() } { | |
287 let args = extract_argv(argc, argv); | |
288 ErrorCode::result_to_c(super::$ident::account_management(handle, args, flags)) | |
289 } else { | |
290 ErrorCode::Ignore as c_int | |
291 } | |
292 } | |
293 | |
294 #[no_mangle] | |
295 extern "C" fn pam_sm_authenticate( | |
296 pamh: *mut libc::c_void, | |
297 flags: Flags, | |
298 argc: c_int, | |
299 argv: *const *const c_char, | |
300 ) -> c_int { | |
301 if let Some(handle) = unsafe { pamh.cast::<LibPamHandle>().as_mut() } { | |
302 let args = extract_argv(argc, argv); | |
303 ErrorCode::result_to_c(super::$ident::authenticate(handle, args, flags)) | |
304 } else { | |
305 ErrorCode::Ignore as c_int | |
306 } | |
307 } | |
308 | |
309 #[no_mangle] | |
310 extern "C" fn pam_sm_chauthtok( | |
311 pamh: *mut libc::c_void, | |
312 flags: Flags, | |
313 argc: c_int, | |
314 argv: *const *const c_char, | |
315 ) -> c_int { | |
316 if let Some(handle) = unsafe { pamh.cast::<LibPamHandle>().as_mut() } { | |
317 let args = extract_argv(argc, argv); | |
318 ErrorCode::result_to_c(super::$ident::change_authtok(handle, args, flags)) | |
319 } else { | |
320 ErrorCode::Ignore as c_int | |
321 } | |
322 } | |
323 | |
324 #[no_mangle] | |
325 extern "C" fn pam_sm_close_session( | |
326 pamh: *mut libc::c_void, | |
327 flags: Flags, | |
328 argc: c_int, | |
329 argv: *const *const c_char, | |
330 ) -> c_int { | |
331 if let Some(handle) = unsafe { pamh.cast::<LibPamHandle>().as_mut() } { | |
332 let args = extract_argv(argc, argv); | |
333 ErrorCode::result_to_c(super::$ident::close_session(handle, args, flags)) | |
334 } else { | |
335 ErrorCode::Ignore as c_int | |
336 } | |
337 } | |
338 | |
339 #[no_mangle] | |
340 extern "C" fn pam_sm_open_session( | |
341 pamh: *mut libc::c_void, | |
342 flags: Flags, | |
343 argc: c_int, | |
344 argv: *const *const c_char, | |
345 ) -> c_int { | |
346 let args = extract_argv(argc, argv); | |
347 if let Some(handle) = unsafe { pamh.cast::<LibPamHandle>().as_mut() } { | |
348 ErrorCode::result_to_c(super::$ident::open_session(handle, args, flags)) | |
349 } else { | |
350 ErrorCode::Ignore as c_int | |
351 } | |
352 } | |
353 | |
354 #[no_mangle] | |
355 extern "C" fn pam_sm_setcred( | |
356 pamh: *mut libc::c_void, | |
357 flags: Flags, | |
358 argc: c_int, | |
359 argv: *const *const c_char, | |
360 ) -> c_int { | |
361 let args = extract_argv(argc, argv); | |
362 if let Some(handle) = unsafe { pamh.cast::<LibPamHandle>().as_mut() } { | |
363 ErrorCode::result_to_c(super::$ident::set_credentials(handle, args, flags)) | |
364 } else { | |
365 ErrorCode::Ignore as c_int | |
366 } | |
367 } | |
368 | |
369 /// Turns `argc`/`argv` into a [Vec] of [CStr]s. | |
370 /// | |
371 /// # Safety | |
372 /// | |
373 /// We use this only with arguments we get from `libpam`, which we kind of have to trust. | |
374 fn extract_argv<'a>(argc: c_int, argv: *const *const c_char) -> Vec<&'a CStr> { | |
375 (0..argc) | |
376 .map(|o| unsafe { CStr::from_ptr(*argv.offset(o as isize) as *const c_char) }) | |
377 .collect() | |
378 } | |
379 } | |
380 }; | |
381 } | |
382 | |
383 #[cfg(test)] | |
384 mod tests { | |
385 // Compile-time test that the `pam_hooks` macro compiles. | |
386 use super::super::{PamHandleModule, PamModule}; | |
387 struct Foo; | |
388 impl<T: PamHandleModule> PamModule<T> for Foo {} | |
389 | |
390 pam_hooks!(Foo); | |
391 } |