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