Mercurial > crates > nonstick
view src/handle.rs @ 68:e4e7d68234d0 default tip
Added tag v0.0.6 for changeset 71e432a213ee
author | Paul Fisher <paul@pfish.zone> |
---|---|
date | Tue, 27 May 2025 16:40:49 -0400 |
parents | a674799a5cd3 |
children |
line wrap: on
line source
//! The wrapper types and traits for handles into the PAM library. use crate::constants::{ErrorCode, Result}; use crate::items::{Item, ItemType}; use crate::{memory, pam_ffi}; use libc::c_char; use secure_string::SecureString; use std::ffi::{c_int, CString}; use std::mem; /// Features of a PAM handle that are available to applications and modules. /// /// You probably want [`LibPamHandle`]. This trait is intended to allow creating /// mock PAM handle types used for testing PAM modules and applications. pub trait 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: &impl 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 fn get_user(&self, prompt: Option<&str>) -> Result<String>; /// 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: &impl 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 fn get_authtok(&self, prompt: Option<&str>) -> Result<SecureString>; /// 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: &impl 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 fn get_item<T: Item>(&self) -> Result<Option<T>>; /// 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 fn set_item<T: Item>(&mut self, item: T) -> Result<()>; /// Closes the PAM session on an owned PAM handle. /// /// This should be called with the result of the application's last call /// into PAM services. Since this is only applicable to *owned* PAM handles, /// a PAM module should never call this (and it will never be handed /// an owned `PamHandle` that it can `close`). /// /// See the [`pam_end` manual page][man] for more information. /// /// ```no_run /// # use nonstick::PamHandle; /// # use std::error::Error; /// # fn _doc(handle: impl PamHandle, auth_result: nonstick::Result<()>) -> Result<(), Box<dyn Error>> { /// // Earlier: authentication was performed and the result was stored /// // into auth_result. /// handle.close(auth_result)?; /// # Ok(()) /// # } /// ``` /// /// [man]: https://www.man7.org/linux/man-pages/man3/pam_end.3.html fn close(self, status: Result<()>) -> Result<()>; } /// Functionality of a PAM handle that can be expected by a PAM module. /// /// If you are not writing a PAM module (e.g., you are writing an application), /// you should not use any of the functionality exposed by this trait. /// /// Like [`PamHandle`], this is intended to allow creating mock implementations /// of PAM for testing PAM modules. pub trait PamModuleHandle: PamHandle { /// 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 unsafe fn get_data<T>(&self, key: &str) -> Result<Option<&T>>; /// 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 fn set_data<T>(&mut self, key: &str, data: Box<T>) -> Result<()>; } /// A [`PamHandle`] backed by `libpam`, i.e., a real PAM handle. /// /// This structure wraps an opaque PAM handle and gives you a nice Rusty /// interface to use PAM. #[repr(C)] pub struct LibPamHandle(pam_ffi::Handle); impl LibPamHandle { /// Converts a pointer passed from PAM into a borrowed handle. /// /// # Safety /// /// It is your responsibility to provide a valid pointer. pub unsafe fn from_ptr<'a>(ptr: *mut libc::c_void) -> &'a mut LibPamHandle { &mut *(ptr as *mut LibPamHandle) } } impl Drop for LibPamHandle { /// Ends the PAM session with a zero error code. fn drop(&mut self) { unsafe { pam_ffi::pam_end(&mut self.0, 0); } } } impl PamHandle for LibPamHandle { 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) } 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) } 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) } fn set_item<T: Item>(&mut self, item: T) -> crate::Result<()> { let ret = unsafe { pam_ffi::pam_set_item(&mut self.0, T::type_id().into(), item.into_raw().cast()) }; ErrorCode::result_from(ret) } fn close(mut self, status: Result<()>) -> Result<()> { let result = unsafe { pam_ffi::pam_end(&mut self.0, ErrorCode::result_to_c(status)) }; // Since we've already `pam_end`ed this session, we don't want it to be // double-freed on drop. mem::forget(self); ErrorCode::result_from(result) } } impl PamModuleHandle for LibPamHandle { 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)) } } } 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( &mut self.0, c_key.as_ptr(), Box::into_raw(data).cast(), 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()); } }