Mercurial > crates > nonstick
diff src/libpam/message.rs @ 77:351bdc13005e
Update the libpam module to work with the new structure.
author | Paul Fisher <paul@pfish.zone> |
---|---|
date | Sun, 08 Jun 2025 01:03:46 -0400 |
parents | c30811b4afae |
children |
line wrap: on
line diff
--- a/src/libpam/message.rs Sat Jun 07 18:55:27 2025 -0400 +++ b/src/libpam/message.rs Sun Jun 08 01:03:46 2025 -0400 @@ -1,24 +1,17 @@ //! Data and types dealing with PAM messages. use crate::constants::InvalidEnum; -use crate::conv::Message; +use crate::libpam::conversation::OwnedMessage; use crate::libpam::memory; -use crate::libpam::memory::{CBinaryData, Immovable, NulError, TooBigError}; +use crate::libpam::memory::{CBinaryData, Immovable}; +use crate::ErrorCode; +use crate::Result; use num_derive::FromPrimitive; use num_traits::FromPrimitive; use std::ffi::{c_int, c_void, CStr}; use std::result::Result as StdResult; -use std::str::Utf8Error; use std::{ptr, slice}; - -#[derive(Debug, thiserror::Error)] -#[error("error creating PAM message: {0}")] -pub enum ConversionError { - InvalidEnum(#[from] InvalidEnum<Style>), - Utf8Error(#[from] Utf8Error), - NulError(#[from] NulError), - TooBigError(#[from] TooBigError), -} +use crate::conv::{BorrowedBinaryData, ErrorMsg, InfoMsg, MaskedQAndA, Message, QAndA, RadioQAndA}; /// The C enum values for messages shown to the user. #[derive(Debug, PartialEq, FromPrimitive)] @@ -53,11 +46,15 @@ } } -/// A message sent by PAM or a module to an application. -/// This message, and its internal data, is owned by the creator +/// A question sent by PAM or a module to an application. +/// +/// PAM refers to this as a "message", but we call it a question +/// to avoid confusion with [`Message`]. +/// +/// This question, and its internal data, is owned by its creator /// (either the module or PAM itself). #[repr(C)] -pub struct RawMessage { +pub struct Question { /// The style of message to request. style: c_int, /// A description of the data requested. @@ -69,8 +66,8 @@ _marker: Immovable, } -impl RawMessage { - pub fn set(&mut self, msg: Message) -> StdResult<(), ConversionError> { +impl Question { + pub fn fill(&mut self, msg: &Message) -> Result<()> { let (style, data) = copy_to_heap(msg)?; self.clear(); // SAFETY: We allocated this ourselves or were given it by PAM. @@ -86,14 +83,25 @@ /// # Safety /// /// It's up to you to pass this only on types with a string value. - unsafe fn string_data(&self) -> StdResult<&str, Utf8Error> { + unsafe fn string_data(&self) -> Result<&str> { if self.data.is_null() { Ok("") } else { - CStr::from_ptr(self.data.cast()).to_str() + CStr::from_ptr(self.data.cast()) + .to_str() + .map_err(|_| ErrorCode::ConversationError) } } + /// Gets this message's data pointer as borrowed binary data. + unsafe fn binary_data(&self) -> BorrowedBinaryData { + self.data + .cast::<CBinaryData>() + .as_ref() + .map(Into::into) + .unwrap_or_default() + } + /// Zeroes out the data stored here. fn clear(&mut self) { // SAFETY: We either created this data or we got it from PAM. @@ -119,23 +127,61 @@ } } -/// Copies the contents of this message to the C heap. -fn copy_to_heap(msg: Message) -> StdResult<(Style, *mut c_void), ConversionError> { - let alloc = |style, text| Ok((style, memory::malloc_str(text)?.cast())); - match msg { - Message::MaskedPrompt(text) => alloc(Style::PromptEchoOff, text), - Message::Prompt(text) => alloc(Style::PromptEchoOn, text), - Message::RadioPrompt(text) => alloc(Style::RadioType, text), - Message::ErrorMsg(text) => alloc(Style::ErrorMsg, text), - Message::InfoMsg(text) => alloc(Style::TextInfo, text), - Message::BinaryPrompt { data, data_type } => Ok(( - Style::BinaryPrompt, - (CBinaryData::alloc(data, data_type)?).cast(), - )), +impl<'a> TryFrom<&'a Question> for OwnedMessage<'a> { + type Error = ErrorCode; + fn try_from(question: &'a Question) -> Result<Self> { + let style: Style = question + .style + .try_into() + .map_err(|_| ErrorCode::ConversationError)?; + // SAFETY: In all cases below, we're matching the + let prompt = unsafe { + match style { + Style::PromptEchoOff => { + Self::MaskedPrompt(MaskedQAndA::new(question.string_data()?)) + } + Style::PromptEchoOn => Self::Prompt(QAndA::new(question.string_data()?)), + Style::ErrorMsg => Self::Error(ErrorMsg::new(question.string_data()?)), + Style::TextInfo => Self::Info(InfoMsg::new(question.string_data()?)), + Style::RadioType => Self::RadioPrompt(RadioQAndA::new(question.string_data()?)), + Style::BinaryPrompt => Self::BinaryPrompt(question.binary_data().into()), + } + }; + Ok(prompt) } } -/// Abstraction of a list-of-messages to be sent in a PAM conversation. +/// Copies the contents of this message to the C heap. +fn copy_to_heap(msg: &Message) -> Result<(Style, *mut c_void)> { + let alloc = |style, text| Ok((style, memory::malloc_str(text)?.cast())); + match *msg { + Message::MaskedPrompt(p) => alloc(Style::PromptEchoOff, p.question()), + Message::Prompt(p) => alloc(Style::PromptEchoOn, p.question()), + Message::RadioPrompt(p) => alloc(Style::RadioType, p.question()), + Message::Error(p) => alloc(Style::ErrorMsg, p.question()), + Message::Info(p) => alloc(Style::TextInfo, p.question()), + Message::BinaryPrompt(p) => { + let q = p.question(); + Ok(( + Style::BinaryPrompt, + CBinaryData::alloc(q.data(), q.data_type())?.cast(), + )) + } + } +} + +/// Abstraction of a collection of questions to be sent in a PAM conversation. +/// +/// The PAM C API conversation function looks like this: +/// +/// ```c +/// int pam_conv( +/// int count, +/// const struct pam_message **questions, +/// struct pam_response **answers, +/// void *appdata_ptr, +/// ) +/// ``` /// /// On Linux-PAM and other compatible implementations, `messages` /// is treated as a pointer-to-pointers, like `int argc, char **argv`. @@ -170,39 +216,39 @@ /// ``` /// /// ***THIS LIBRARY CURRENTLY SUPPORTS ONLY LINUX-PAM.*** -pub struct OwnedMessages { - /// An indirection to the messages themselves, stored on the C heap. - indirect: *mut MessageIndirector, - /// The number of messages in the list. +pub struct Questions { + /// An indirection to the questions themselves, stored on the C heap. + indirect: *mut Indirect, + /// The number of questions. count: usize, } -impl OwnedMessages { - /// Allocates data to store messages on the C heap. +impl Questions { + /// Allocates data to store questions on the C heap. pub fn alloc(count: usize) -> Self { Self { - indirect: MessageIndirector::alloc(count), + indirect: Indirect::alloc(count), count, } } /// The pointer to the thing with the actual list. - pub fn indirector(&self) -> *const MessageIndirector { + pub fn indirect(&self) -> *const Indirect { self.indirect } - pub fn iter(&self) -> impl Iterator<Item = &RawMessage> { + pub fn iter(&self) -> impl Iterator<Item = &Question> { // SAFETY: we're iterating over an amount we know. unsafe { (*self.indirect).iter(self.count) } } - pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut RawMessage> { + pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut Question> { // SAFETY: we're iterating over an amount we know. unsafe { (*self.indirect).iter_mut(self.count) } } } -impl Drop for OwnedMessages { +impl Drop for Questions { fn drop(&mut self) { // SAFETY: We are valid and have a valid pointer. // Once we're done, everything will be safe. @@ -221,23 +267,22 @@ /// This is kept separate to provide a place where we can separate /// the pointer-to-pointer-to-list from pointer-to-list-of-pointers. #[repr(transparent)] -pub struct MessageIndirector { - base: [*mut RawMessage; 0], +pub struct Indirect { + base: [*mut Question; 0], _marker: Immovable, } -impl MessageIndirector { +impl Indirect { /// Allocates memory for this indirector and all its members. fn alloc(count: usize) -> *mut Self { // SAFETY: We're only allocating, and when we're done, // everything will be in a known-good state. unsafe { - let me_ptr: *mut MessageIndirector = - libc::calloc(count, size_of::<*mut RawMessage>()).cast(); + let me_ptr: *mut Indirect = libc::calloc(count, size_of::<*mut Question>()).cast(); let me = &mut *me_ptr; let ptr_list = slice::from_raw_parts_mut(me.base.as_mut_ptr(), count); for entry in ptr_list { - *entry = libc::calloc(1, size_of::<RawMessage>()).cast(); + *entry = libc::calloc(1, size_of::<Question>()).cast(); } me } @@ -248,7 +293,7 @@ /// # Safety /// /// You have to provide the right count. - pub unsafe fn iter(&self, count: usize) -> impl Iterator<Item = &RawMessage> { + pub unsafe fn iter(&self, count: usize) -> impl Iterator<Item = &Question> { (0..count).map(|idx| &**self.base.as_ptr().add(idx)) } @@ -257,7 +302,7 @@ /// # Safety /// /// You have to provide the right count. - pub unsafe fn iter_mut(&mut self, count: usize) -> impl Iterator<Item = &mut RawMessage> { + pub unsafe fn iter_mut(&mut self, count: usize) -> impl Iterator<Item = &mut Question> { (0..count).map(|idx| &mut **self.base.as_mut_ptr().add(idx)) } @@ -278,57 +323,26 @@ } } -impl<'a> TryFrom<&'a RawMessage> for Message<'a> { - type Error = ConversionError; - - /// Retrieves the data stored in this message. - fn try_from(input: &RawMessage) -> StdResult<Message, ConversionError> { - let style: Style = input.style.try_into()?; - // SAFETY: We either allocated this message ourselves or were provided it by PAM. - let result = unsafe { - match style { - Style::PromptEchoOff => Message::MaskedPrompt(input.string_data()?), - Style::PromptEchoOn => Message::Prompt(input.string_data()?), - Style::TextInfo => Message::InfoMsg(input.string_data()?), - Style::ErrorMsg => Message::ErrorMsg(input.string_data()?), - Style::RadioType => Message::ErrorMsg(input.string_data()?), - Style::BinaryPrompt => input.data.cast::<CBinaryData>().as_ref().map_or_else( - || Message::BinaryPrompt { - data_type: 0, - data: &[], - }, - |data| Message::BinaryPrompt { - data_type: data.data_type(), - data: data.contents(), - }, - ), - } - }; - Ok(result) - } -} - #[cfg(test)] mod tests { - use crate::conv::Message; - use crate::libpam::message::OwnedMessages; + use super::{MaskedQAndA, Questions}; + use crate::conv::{BinaryQAndA, QAndA}; #[test] fn test_owned_messages() { - let mut tons_of_messages = OwnedMessages::alloc(10); + let mut tons_of_messages = Questions::alloc(10); let mut msgs: Vec<_> = tons_of_messages.iter_mut().collect(); assert!(msgs.get(10).is_none()); let last_msg = &mut msgs[9]; - last_msg.set(Message::MaskedPrompt("hocus pocus")).unwrap(); + last_msg + .fill(&MaskedQAndA::new("hocus pocus").message()) + .unwrap(); let another_msg = &mut msgs[0]; another_msg - .set(Message::BinaryPrompt { - data: &[5, 4, 3, 2, 1], - data_type: 99, - }) + .fill(&BinaryQAndA::new(&[5, 4, 3, 2, 1], 66).message()) .unwrap(); let overwrite = &mut msgs[3]; - overwrite.set(Message::Prompt("what")).unwrap(); - overwrite.set(Message::Prompt("who")).unwrap(); + overwrite.fill(&QAndA::new("what").message()).unwrap(); + overwrite.fill(&QAndA::new("who").message()).unwrap(); } }