Mercurial > crates > nonstick
diff src/handle.rs @ 64:bbe84835d6db v0.0.5
More organization; add lots of docs.
- moves `PamHandle` to its own module, since it will be used
by both modules and clients.
- adds a ton of documentation to the `PamModule` trait
and reorders methods to most-interesting-first.
- adds more flag values from pam_modules.h.
- other misc cleanup.
author | Paul Fisher <paul@pfish.zone> |
---|---|
date | Thu, 22 May 2025 01:52:32 -0400 |
parents | src/module.rs@05cc2c27334f |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/handle.rs Thu May 22 01:52:32 2025 -0400 @@ -0,0 +1,208 @@ +//! Where [PamHandle] lives. +use crate::items::{Item, ItemType}; +use crate::{memory, pam_ffi, ErrorCode}; +use libc::c_char; +use secure_string::SecureString; +use std::ffi::{c_int, CString}; + +/// Your interface to a PAM handle. +/// +/// This structure wraps an opaque PAM-provided pointer and gives you +/// a safe and familiar struct-based API to interact with PAM. +#[repr(transparent)] +pub struct PamHandle(*mut libc::c_void); + +impl PamHandle { + /// Retrieves the name of the user who is authenticating or logging in. + /// + /// This is effectively like `handle.get_item::<Item::User>()`. + /// See the [`pam_get_user` manual page][man] + /// or [`pam_get_user` in the Module Writer's Guide][mwg]. + /// + /// # Example + /// + /// ```no_run + /// # use nonstick::PamHandle; + /// # fn _doc(handle: &PamHandle) -> Result<(), Box<dyn std::error::Error>> { + /// // Get the username using the default prompt. + /// let user = handle.get_user(None)?; + /// // Get the username using a custom prompt. + /// let user = handle.get_user(Some("who ARE you even???"))?; + /// # Ok(()) + /// # } + /// ``` + /// + /// [man]: https://www.man7.org/linux/man-pages/man3/pam_get_user.3.html + /// [mwg]: https://www.chiark.greenend.org.uk/doc/libpam-doc/html/mwg-expected-by-module-item.html#mwg-pam_get_user + pub fn get_user(&self, prompt: Option<&str>) -> crate::Result<String> { + let prompt = memory::option_cstr(prompt)?; + let mut output: *const c_char = std::ptr::null_mut(); + let ret = unsafe { + pam_ffi::pam_get_user(self.0, &mut output, memory::prompt_ptr(prompt.as_ref())) + }; + ErrorCode::result_from(ret)?; + memory::copy_pam_string(output) + } + + /// Retrieves the authentication token from the user. + /// + /// This is essentially like `handle.get_item::<Item::AuthTok>()`. + /// + /// See the [`pam_get_authtok` manual page][man] + /// or [`pam_get_item` in the Module Writer's Guide][mwg]. + /// + /// # Example + /// + /// ```no_run + /// # use nonstick::PamHandle; + /// # fn _doc(handle: &PamHandle) -> Result<(), Box<dyn std::error::Error>> { + /// // Get the user's password using the default prompt. + /// let pass = handle.get_authtok(None)?; + /// // Get the user's password using a custom prompt. + /// let pass = handle.get_authtok(Some("Reveal your secrets!"))?; + /// Ok(()) + /// # } + /// ``` + /// + /// [man]: https://www.man7.org/linux/man-pages/man3/pam_get_authtok.3.html + /// [mwg]: https://www.chiark.greenend.org.uk/doc/libpam-doc/html/mwg-expected-by-module-item.html#mwg-pam_get_item + pub fn get_authtok(&self, prompt: Option<&str>) -> crate::Result<SecureString> { + let prompt = memory::option_cstr(prompt)?; + let mut output: *const c_char = std::ptr::null_mut(); + let res = unsafe { + pam_ffi::pam_get_authtok( + self.0, + ItemType::AuthTok.into(), + &mut output, + memory::prompt_ptr(prompt.as_ref()), + ) + }; + ErrorCode::result_from(res)?; + memory::copy_pam_string(output).map(SecureString::from) + } + + /// Retrieves an [Item] that has been set, possibly by the PAM client. + /// + /// These items are *references to PAM memory* + /// which are *owned by the PAM session* + /// and you should never modify them. + /// + /// See the [`pam_get_item` manual page][man] + /// or [`pam_get_item` in the Module Writer's Guide][mwg]. + /// + /// # Example + /// + /// ```no_run + /// # use nonstick::PamHandle; + /// use nonstick::items::Service; + /// + /// # fn _doc(pam_handle: &PamHandle) -> Result<(), Box<dyn std::error::Error>> { + /// let svc: Option<Service> = pam_handle.get_item()?; + /// match svc { + /// Some(name) => eprintln!("The calling service name is {:?}", name.to_string_lossy()), + /// None => eprintln!("Who knows what the calling service is?"), + /// } + /// # Ok(()) + /// # } + /// ``` + /// + /// [man]: https://www.man7.org/linux/man-pages/man3/pam_get_item.3.html + /// [mwg]: https://www.chiark.greenend.org.uk/doc/libpam-doc/html/mwg-expected-by-module-item.html#mwg-pam_get_item + pub fn get_item<T: Item>(&self) -> crate::Result<Option<T>> { + let mut ptr: *const libc::c_void = std::ptr::null(); + let out = unsafe { + let ret = pam_ffi::pam_get_item(self.0, T::type_id().into(), &mut ptr); + ErrorCode::result_from(ret)?; + let typed_ptr: *const T::Raw = ptr.cast(); + match typed_ptr.is_null() { + true => None, + false => Some(T::from_raw(typed_ptr)), + } + }; + Ok(out) + } + + /// Sets an item in the PAM context. It can be retrieved using [`get_item`](Self::get_item). + /// + /// See the [`pam_set_item` manual page][man] + /// or [`pam_set_item` in the Module Writer's Guide][mwg]. + /// + /// [man]: https://www.man7.org/linux/man-pages/man3/pam_set_item.3.html + /// [mwg]: https://www.chiark.greenend.org.uk/doc/libpam-doc/html/mwg-expected-by-module-item.html#mwg-pam_set_item + pub fn set_item<T: Item>(&mut self, item: T) -> crate::Result<()> { + let ret = + unsafe { pam_ffi::pam_set_item(self.0, T::type_id().into(), item.into_raw().cast()) }; + ErrorCode::result_from(ret) + } + + /// Gets some pointer, identified by `key`, that has been set previously + /// using [`set_data`](Self::set_data). + /// + /// The data, if present, is still owned by the current PAM session. + /// + /// See the [`pam_get_data` manual page][man] + /// or [`pam_get_data` in the Module Writer's Guide][mwg]. + /// + /// # Safety + /// + /// The data stored under the provided key must be of type `T`, + /// otherwise you'll get back a completely invalid `&T` + /// and further behavior is undefined. + /// + /// [man]: https://www.man7.org/linux/man-pages/man3/pam_get_data.3.html + /// [mwg]: https://www.chiark.greenend.org.uk/doc/libpam-doc/html/mwg-expected-by-module-item.html#mwg-pam_get_data + pub unsafe fn get_data<T>(&self, key: &str) -> crate::Result<Option<&T>> { + let c_key = CString::new(key).map_err(|_| ErrorCode::ConversationError)?; + let mut ptr: *const libc::c_void = std::ptr::null(); + ErrorCode::result_from(pam_ffi::pam_get_data(self.0, c_key.as_ptr(), &mut ptr))?; + match ptr.is_null() { + true => Ok(None), + false => { + let typed_ptr = ptr.cast(); + Ok(Some(&*typed_ptr)) + } + } + } + + /// Stores a pointer that can be retrieved later with [`get_data`](Self::get_data). + /// + /// This data is accessible to this module and other PAM modules + /// (using the provided `key`), but is *not* accessible to the application. + /// The PAM session takes ownership of the data, and it will be dropped + /// when the session ends. + /// + /// See the [`pam_set_data` manual page][man] + /// or [`pam_set_data` in the Module Writer's Guide][mwg]. + /// + /// [man]: https://www.man7.org/linux/man-pages/man3/pam_set_data.3.html + /// [mwg]: https://www.chiark.greenend.org.uk/doc/libpam-doc/html/mwg-expected-by-module-item.html#mwg-pam_set_data + pub fn set_data<T>(&mut self, key: &str, data: Box<T>) -> crate::Result<()> { + let c_key = CString::new(key).map_err(|_| ErrorCode::ConversationError)?; + let ret = unsafe { + pam_ffi::pam_set_data( + self.0, + c_key.as_ptr(), + Box::into_raw(data).cast(), + Self::set_data_cleanup::<T>, + ) + }; + ErrorCode::result_from(ret) + } + + /// Function called at the end of a PAM session that is called to clean up + /// a value previously provided to PAM in a `pam_set_data` call. + /// + /// You should never call this yourself. + extern "C" fn set_data_cleanup<T>(_: *const libc::c_void, c_data: *mut libc::c_void, _: c_int) { + unsafe { + let _data: Box<T> = Box::from_raw(c_data.cast()); + } + } +} + +impl From<*mut libc::c_void> for PamHandle { + /// Wraps an internal Handle pointer. + fn from(value: *mut libc::c_void) -> Self { + Self(value) + } +}