Mercurial > crates > nonstick
diff src/conv.rs @ 71:58f9d2a4df38
Reorganize everything again???
- Splits ffi/memory stuff into a bunch of stuff in the pam_ffi module.
- Builds infrastructure for passing Messages and Responses.
- Adds tests for some things at least.
author | Paul Fisher <paul@pfish.zone> |
---|---|
date | Tue, 03 Jun 2025 21:54:58 -0400 |
parents | 9f8381a1c09c |
children | 47eb242a4f88 |
line wrap: on
line diff
--- a/src/conv.rs Tue Jun 03 01:21:59 2025 -0400 +++ b/src/conv.rs Tue Jun 03 21:54:58 2025 -0400 @@ -3,13 +3,9 @@ // Temporarily allowed until we get the actual conversation functions hooked up. #![allow(dead_code)] -use crate::constants::{NulError, Result, TooBigError}; -use crate::pam_ffi::{BinaryResponseInner, GenericResponse, TextResponseInner}; +use crate::constants::Result; +use crate::pam_ffi::Message; use secure_string::SecureString; -use std::mem; -use std::result::Result as StdResult; -use std::str::Utf8Error; - // TODO: In most cases, we should be passing around references to strings // or binary data. Right now we don't because that turns type inference and // trait definitions/implementations into a HUGE MESS. @@ -18,47 +14,6 @@ // associated types in the various Conversation traits to avoid copying // when unnecessary. -/// The types of message and request that can be sent to a user. -/// -/// The data within each enum value is the prompt (or other information) -/// that will be presented to the user. -#[derive(Debug)] -pub enum Message<'a> { - /// Requests information from the user; will be masked when typing. - /// - /// Response: [`Response::MaskedText`] - MaskedPrompt(&'a str), - /// Requests information from the user; will not be masked. - /// - /// Response: [`Response::Text`] - Prompt(&'a str), - /// "Yes/No/Maybe conditionals" (a Linux-PAM extension). - /// - /// Response: [`Response::Text`] - /// (Linux-PAM documentation doesn't define its contents.) - RadioPrompt(&'a str), - /// Raises an error message to the user. - /// - /// Response: [`Response::NoResponse`] - Error(&'a str), - /// Sends an informational message to the user. - /// - /// Response: [`Response::NoResponse`] - Info(&'a str), - /// Requests binary data from the client (a Linux-PAM extension). - /// - /// This is used for non-human or non-keyboard prompts (security key?). - /// NOT part of the X/Open PAM specification. - /// - /// Response: [`Response::Binary`] - BinaryPrompt { - /// Some binary data. - data: &'a [u8], - /// A "type" that you can use for signalling. Has no strict definition in PAM. - data_type: u8, - }, -} - /// The responses that PAM will return from a request. #[derive(Debug, PartialEq, derive_more::From)] pub enum Response { @@ -103,9 +58,6 @@ /// /// The returned Vec of messages always contains exactly as many entries /// as there were messages in the request; one corresponding to each. - /// - /// Messages with no response (e.g. [info](Message::Info) and - /// [error](Message::Error)) will have a `None` entry instead of a `Response`. fn send(&mut self, messages: &[Message]) -> Result<Vec<Response>>; } @@ -152,84 +104,6 @@ } } -/// An owned text response to a PAM conversation. -/// -/// It points to a value on the C heap. -#[repr(C)] -struct TextResponse(*mut TextResponseInner); - -impl TextResponse { - /// Allocates a new response with the given text. - /// - /// A copy of the provided text will be allocated on the C heap. - pub fn new(text: impl AsRef<str>) -> StdResult<Self, NulError> { - TextResponseInner::alloc(text).map(Self) - } - - /// Converts this into a GenericResponse. - fn generic(self) -> *mut GenericResponse { - let ret = self.0 as *mut GenericResponse; - mem::forget(self); - ret - } - - /// Gets the string data, if possible. - pub fn as_str(&self) -> StdResult<&str, Utf8Error> { - // SAFETY: We allocated this ourselves or got it back from PAM. - unsafe { &*self.0 }.contents().to_str() - } -} - -impl Drop for TextResponse { - /// Frees an owned response. - fn drop(&mut self) { - // SAFETY: We allocated this ourselves, or it was provided by PAM. - unsafe { TextResponseInner::free(self.0) } - } -} - -/// An owned binary response to a PAM conversation. -/// -/// It points to a value on the C heap. -#[repr(C)] -pub struct BinaryResponse(pub(super) *mut BinaryResponseInner); - -impl BinaryResponse { - /// Creates a binary response with the given data. - /// - /// A copy of the data will be made and allocated on the C heap. - pub fn new(data: &[u8], data_type: u8) -> StdResult<Self, TooBigError> { - BinaryResponseInner::alloc(data, data_type).map(Self) - } - - /// Converts this into a GenericResponse. - fn generic(self) -> *mut GenericResponse { - let ret = self.0 as *mut GenericResponse; - mem::forget(self); - ret - } - - /// The data type we point to. - pub fn data_type(&self) -> u8 { - // SAFETY: We allocated this ourselves or got it back from PAM. - unsafe { &*self.0 }.data_type() - } - - /// The data we point to. - pub fn data(&self) -> &[u8] { - // SAFETY: We allocated this ourselves or got it back from PAM. - unsafe { &*self.0 }.contents() - } -} - -impl Drop for BinaryResponse { - /// Frees an owned response. - fn drop(&mut self) { - // SAFETY: We allocated this ourselves, or it was provided by PAM. - unsafe { BinaryResponseInner::free(self.0) } - } -} - /// Owned binary data. #[derive(Debug, PartialEq)] pub struct BinaryData { @@ -249,16 +123,6 @@ } } -impl From<BinaryResponse> for BinaryData { - /// Copies the data onto the Rust heap. - fn from(value: BinaryResponse) -> Self { - Self { - data: value.data().to_vec(), - data_type: value.data_type(), - } - } -} - impl From<BinaryData> for Vec<u8> { /// Extracts the inner vector from the BinaryData. fn from(value: BinaryData) -> Self { @@ -267,13 +131,9 @@ } #[cfg(test)] -mod test { - use super::{ - BinaryResponse, Conversation, DemuxedConversation, Message, Response, SecureString, - TextResponse, - }; +mod tests { + use super::{Conversation, DemuxedConversation, Message, Response, SecureString}; use crate::constants::ErrorCode; - use crate::pam_ffi::GenericResponse; #[test] fn test_demux() { @@ -362,33 +222,4 @@ .unwrap() ); } - - // The below tests are used in conjunction with ASAN to verify - // that we correctly clean up all our memory. - - #[test] - fn test_text_response() { - let resp = TextResponse::new("it's a-me!").unwrap(); - assert_eq!("it's a-me!", resp.as_str().unwrap()); - } - - #[test] - fn test_binary_response() { - let data = [123, 210, 55]; - let resp = BinaryResponse::new(&data, 99).unwrap(); - assert_eq!(data, resp.data()); - assert_eq!(99, resp.data_type()); - } - - #[test] - fn test_to_generic() { - let text = TextResponse::new("oh no").unwrap(); - let text = text.generic(); - let binary = BinaryResponse::new(&[], 33).unwrap(); - let binary = binary.generic(); - unsafe { - GenericResponse::free(text); - GenericResponse::free(binary); - } - } }