Mercurial > crates > nonstick
view src/libpam/handle.rs @ 75:c30811b4afae
rename pam_ffi submodule to libpam.
author | Paul Fisher <paul@pfish.zone> |
---|---|
date | Fri, 06 Jun 2025 22:35:08 -0400 |
parents | src/pam_ffi/handle.rs@c7c596e6388f |
children | 351bdc13005e |
line wrap: on
line source
use super::conversation::LibPamConversation; use crate::constants::{ErrorCode, InvalidEnum, Result}; use crate::conv::Message; use crate::handle::{PamApplicationOnly, PamModuleOnly, PamShared}; use crate::libpam::memory; use crate::libpam::memory::Immovable; use crate::{Conversation, Response}; use num_derive::FromPrimitive; use num_traits::FromPrimitive; use std::ffi::{c_char, c_int}; use std::ops::{Deref, DerefMut}; use std::result::Result as StdResult; use std::{mem, ptr}; /// An owned PAM handle. #[repr(transparent)] pub struct OwnedLibPamHandle(*mut LibPamHandle); /// An opaque structure that a PAM handle points to. #[repr(C)] pub struct LibPamHandle { _data: (), _marker: Immovable, } impl LibPamHandle { /// Gets a C string item. /// /// # Safety /// /// You better be requesting an item which is a C string. unsafe fn get_cstr_item(&mut self, item_type: ItemType) -> Result<Option<&str>> { let mut output = ptr::null(); let ret = unsafe { super::pam_get_item(self, item_type as c_int, &mut output) }; ErrorCode::result_from(ret)?; memory::wrap_string(output.cast()) } /// Sets a C string item. /// /// # Safety /// /// You better be setting an item which is a C string. unsafe fn set_cstr_item(&mut self, item_type: ItemType, data: Option<&str>) -> Result<()> { let data_str = memory::option_cstr(data)?; let ret = unsafe { super::pam_set_item( self, item_type as c_int, memory::prompt_ptr(data_str.as_ref()).cast(), ) }; ErrorCode::result_from(ret) } /// Gets the `PAM_CONV` item from the handle. fn conversation_item(&mut self) -> Result<&mut LibPamConversation> { let output: *mut LibPamConversation = ptr::null_mut(); let result = unsafe { super::pam_get_item( self, ItemType::Conversation.into(), &mut output.cast_const().cast(), ) }; ErrorCode::result_from(result)?; // SAFETY: We got this result from PAM, and we're checking if it's null. unsafe { output.as_mut() }.ok_or(ErrorCode::ConversationError) } } impl PamApplicationOnly for OwnedLibPamHandle { fn close(self, status: Result<()>) -> Result<()> { let ret = unsafe { super::pam_end(self.0, ErrorCode::result_to_c(status)) }; // Forget rather than dropping, since dropping also calls pam_end. mem::forget(self); ErrorCode::result_from(ret) } } impl Deref for OwnedLibPamHandle { type Target = LibPamHandle; fn deref(&self) -> &Self::Target { unsafe { &*self.0 } } } impl DerefMut for OwnedLibPamHandle { fn deref_mut(&mut self) -> &mut Self::Target { unsafe { &mut *self.0 } } } impl Drop for OwnedLibPamHandle { /// Ends the PAM session with a zero error code. /// You probably want to call [`close`](Self::close) instead of /// letting this drop by itself. fn drop(&mut self) { unsafe { super::pam_end(self.0, 0); } } } macro_rules! cstr_item { (get = $getter:ident, item = $item_type:path) => { fn $getter(&mut self) -> Result<Option<&str>> { unsafe { self.get_cstr_item($item_type) } } }; (set = $setter:ident, item = $item_type:path) => { fn $setter(&mut self, value: Option<&str>) -> Result<()> { unsafe { self.set_cstr_item($item_type, value) } } }; } impl PamShared for LibPamHandle { fn get_user(&mut self, prompt: Option<&str>) -> Result<&str> { let prompt = memory::option_cstr(prompt)?; let mut output: *const c_char = ptr::null(); let ret = unsafe { super::pam_get_user(self, &mut output, memory::prompt_ptr(prompt.as_ref())) }; ErrorCode::result_from(ret)?; unsafe { memory::wrap_string(output) } .transpose() .unwrap_or(Err(ErrorCode::ConversationError)) } cstr_item!(get = user_item, item = ItemType::User); cstr_item!(set = set_user_item, item = ItemType::User); cstr_item!(get = service, item = ItemType::Service); cstr_item!(set = set_service, item = ItemType::Service); cstr_item!(get = user_prompt, item = ItemType::UserPrompt); cstr_item!(set = set_user_prompt, item = ItemType::UserPrompt); cstr_item!(get = tty_name, item = ItemType::Tty); cstr_item!(set = set_tty_name, item = ItemType::Tty); cstr_item!(get = remote_user, item = ItemType::RemoteUser); cstr_item!(set = set_remote_user, item = ItemType::RemoteUser); cstr_item!(get = remote_host, item = ItemType::RemoteHost); cstr_item!(set = set_remote_host, item = ItemType::RemoteHost); cstr_item!(set = set_authtok_item, item = ItemType::AuthTok); cstr_item!(set = set_old_authtok_item, item = ItemType::OldAuthTok); } impl Conversation for LibPamHandle { fn communicate(&mut self, messages: &[Message]) -> Result<Vec<Response>> { self.conversation_item()?.communicate(messages) } } impl PamModuleOnly for LibPamHandle { fn get_authtok(&mut self, prompt: Option<&str>) -> Result<&str> { let prompt = memory::option_cstr(prompt)?; let mut output: *const c_char = ptr::null_mut(); // SAFETY: We're calling this with known-good values. let res = unsafe { super::pam_get_authtok( self, ItemType::AuthTok.into(), &mut output, memory::prompt_ptr(prompt.as_ref()), ) }; ErrorCode::result_from(res)?; // SAFETY: We got this string from PAM. unsafe { memory::wrap_string(output) } .transpose() .unwrap_or(Err(ErrorCode::ConversationError)) } cstr_item!(get = authtok_item, item = ItemType::AuthTok); cstr_item!(get = old_authtok_item, item = ItemType::OldAuthTok); } /// 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()); } } /// Identifies what is being gotten or set with `pam_get_item` /// or `pam_set_item`. #[derive(FromPrimitive)] #[repr(i32)] #[non_exhaustive] // because C could give us anything! pub enum ItemType { /// The PAM service name. Service = 1, /// The user's login name. User = 2, /// The TTY name. Tty = 3, /// The remote host (if applicable). RemoteHost = 4, /// The conversation struct (not a CStr-based item). Conversation = 5, /// The authentication token (password). AuthTok = 6, /// The old authentication token (when changing passwords). OldAuthTok = 7, /// The remote user's name. RemoteUser = 8, /// The prompt shown when requesting a username. UserPrompt = 9, /// App-supplied function to override failure delays. FailDelay = 10, /// X display name. XDisplay = 11, /// X server authentication data. XAuthData = 12, /// The type of `pam_get_authtok`. AuthTokType = 13, } impl TryFrom<c_int> for ItemType { type Error = InvalidEnum<Self>; fn try_from(value: c_int) -> StdResult<Self, Self::Error> { Self::from_i32(value).ok_or(value.into()) } } impl From<ItemType> for c_int { fn from(val: ItemType) -> Self { val as Self } }