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 }