diff src/libpam/conversation.rs @ 130:80c07e5ab22f

Transfer over (almost) completely to using libpam-sys. This reimplements everything in nonstick on top of the new -sys crate. We don't yet use libpam-sys's helpers for binary message payloads. Soon.
author Paul Fisher <paul@pfish.zone>
date Tue, 01 Jul 2025 06:11:43 -0400
parents 94b51fa4f797
children efbc235f01d3
line wrap: on
line diff
--- a/src/libpam/conversation.rs	Mon Jun 30 23:49:54 2025 -0400
+++ b/src/libpam/conversation.rs	Tue Jul 01 06:11:43 2025 -0400
@@ -1,25 +1,35 @@
 use crate::conv::{BinaryQAndA, RadioQAndA};
-use crate::conv::{Conversation, ErrorMsg, InfoMsg, MaskedQAndA, Message, QAndA};
+use crate::conv::{Conversation, ErrorMsg, Exchange, InfoMsg, MaskedQAndA, QAndA};
 use crate::libpam::answer::BinaryAnswer;
 use crate::libpam::answer::{Answer, Answers, TextAnswer};
 use crate::libpam::memory::CBinaryData;
-use crate::libpam::pam_ffi::AppData;
-pub use crate::libpam::pam_ffi::LibPamConversation;
-use crate::libpam::question::QuestionsTrait;
-use crate::libpam::question::{Question, Questions};
+use crate::libpam::question::Question;
 use crate::ErrorCode;
 use crate::Result;
+use libpam_sys::helpers::PtrPtrVec;
+use libpam_sys::AppData;
 use std::ffi::c_int;
 use std::iter;
 use std::marker::PhantomData;
 use std::ptr::NonNull;
 use std::result::Result as StdResult;
 
+/// The type used by PAM to call back into a conversation.
+#[repr(C)]
+pub struct LibPamConversation<'a> {
+    pam_conv: libpam_sys::pam_conv,
+    /// Marker to associate the lifetime of this with the conversation
+    /// that was passed in.
+    pub life: PhantomData<&'a mut ()>,
+}
+
 impl LibPamConversation<'_> {
     pub fn wrap<C: Conversation>(conv: &C) -> Self {
         Self {
-            callback: Self::wrapper_callback::<C>,
-            appdata: (conv as *const C).cast(),
+            pam_conv: libpam_sys::pam_conv {
+                conv: Self::wrapper_callback::<C>,
+                appdata_ptr: (conv as *const C).cast_mut().cast(),
+            },
             life: PhantomData,
         }
     }
@@ -29,9 +39,9 @@
     /// PAM calls this, we compute answers, then send them back.
     unsafe extern "C" fn wrapper_callback<C: Conversation>(
         count: c_int,
-        questions: *const *const Question,
-        answers: *mut *mut Answer,
-        me: *const AppData,
+        questions: *const *const libpam_sys::pam_message,
+        answers: *mut *mut libpam_sys::pam_response,
+        me: *mut AppData,
     ) -> c_int {
         let internal = || {
             // Collect all our pointers
@@ -39,23 +49,23 @@
                 .cast::<C>()
                 .as_ref()
                 .ok_or(ErrorCode::ConversationError)?;
-            let indirect = Questions::borrow_ptr(questions, count as usize);
+            let q_iter = PtrPtrVec::<Question>::iter_over(questions, count as usize);
             let answers_ptr = answers.as_mut().ok_or(ErrorCode::ConversationError)?;
 
             // Build our owned list of Q&As from the questions we've been asked
-            let messages: Vec<OwnedMessage> = indirect
+            let messages: Vec<OwnedExchange> = q_iter
                 .map(TryInto::try_into)
                 .collect::<Result<_>>()
                 .map_err(|_| ErrorCode::ConversationError)?;
             // Borrow all those Q&As and ask them.
             // If we got an invalid message type, bail before sending.
-            let borrowed: Result<Vec<_>> = messages.iter().map(Message::try_from).collect();
+            let borrowed: Result<Vec<_>> = messages.iter().map(Exchange::try_from).collect();
             // TODO: Do we want to log something here?
             conv.communicate(&borrowed?);
 
             // Send our answers back.
             let owned = Answers::build(messages)?;
-            *answers_ptr = owned.into_ptr().as_ptr();
+            *answers_ptr = owned.into_ptr();
             Ok(())
         };
         ErrorCode::result_to_c(internal())
@@ -63,17 +73,18 @@
 }
 
 impl Conversation for LibPamConversation<'_> {
-    fn communicate(&self, messages: &[Message]) {
+    fn communicate(&self, messages: &[Exchange]) {
         let internal = || {
-            let questions = Box::pin(Questions::new(messages)?);
+            let questions: Result<_> = messages.iter().map(Question::try_from).collect();
+            let questions = PtrPtrVec::new(questions?);
             let mut response_pointer = std::ptr::null_mut();
             // SAFETY: We're calling into PAM with valid everything.
             let result = unsafe {
-                (self.callback)(
+                (self.pam_conv.conv)(
                     messages.len() as c_int,
-                    questions.as_ref().ptr(),
+                    questions.as_ptr(),
                     &mut response_pointer,
-                    self.appdata,
+                    self.pam_conv.appdata_ptr,
                 )
             };
             ErrorCode::result_from(result)?;
@@ -96,9 +107,9 @@
     }
 }
 
-/// Like [`Message`], but this time we own the contents.
+/// Like [`Exchange`], but this time we own the contents.
 #[derive(Debug)]
-pub enum OwnedMessage<'a> {
+pub enum OwnedExchange<'a> {
     MaskedPrompt(MaskedQAndA<'a>),
     Prompt(QAndA<'a>),
     Info(InfoMsg<'a>),
@@ -107,16 +118,16 @@
     BinaryPrompt(BinaryQAndA<'a>),
 }
 
-impl<'a> TryFrom<&'a OwnedMessage<'a>> for Message<'a> {
+impl<'a> TryFrom<&'a OwnedExchange<'a>> for Exchange<'a> {
     type Error = ErrorCode;
-    fn try_from(src: &'a OwnedMessage) -> StdResult<Self, ErrorCode> {
+    fn try_from(src: &'a OwnedExchange) -> StdResult<Self, ErrorCode> {
         match src {
-            OwnedMessage::MaskedPrompt(m) => Ok(Message::MaskedPrompt(m)),
-            OwnedMessage::Prompt(m) => Ok(Message::Prompt(m)),
-            OwnedMessage::Info(m) => Ok(Message::Info(m)),
-            OwnedMessage::Error(m) => Ok(Message::Error(m)),
-            OwnedMessage::RadioPrompt(m) => Ok(Message::RadioPrompt(m)),
-            OwnedMessage::BinaryPrompt(m) => Ok(Message::BinaryPrompt(m)),
+            OwnedExchange::MaskedPrompt(m) => Ok(Exchange::MaskedPrompt(m)),
+            OwnedExchange::Prompt(m) => Ok(Exchange::Prompt(m)),
+            OwnedExchange::Info(m) => Ok(Exchange::Info(m)),
+            OwnedExchange::Error(m) => Ok(Exchange::Error(m)),
+            OwnedExchange::RadioPrompt(m) => Ok(Exchange::RadioPrompt(m)),
+            OwnedExchange::BinaryPrompt(m) => Ok(Exchange::BinaryPrompt(m)),
         }
     }
 }
@@ -126,7 +137,7 @@
 /// # Safety
 ///
 /// You are responsible for ensuring that the src-dst pair matches.
-unsafe fn convert(msg: &Message, resp: &mut Answer) {
+unsafe fn convert(msg: &Exchange, resp: &mut Answer) {
     macro_rules! fill_text {
         ($dst:ident, $src:ident) => {{
             let text_resp = unsafe { TextAnswer::upcast($src) };
@@ -134,12 +145,12 @@
         }};
     }
     match *msg {
-        Message::MaskedPrompt(qa) => fill_text!(qa, resp),
-        Message::Prompt(qa) => fill_text!(qa, resp),
-        Message::Error(m) => m.set_answer(Ok(())),
-        Message::Info(m) => m.set_answer(Ok(())),
-        Message::RadioPrompt(qa) => fill_text!(qa, resp),
-        Message::BinaryPrompt(qa) => {
+        Exchange::MaskedPrompt(qa) => fill_text!(qa, resp),
+        Exchange::Prompt(qa) => fill_text!(qa, resp),
+        Exchange::Error(m) => m.set_answer(Ok(())),
+        Exchange::Info(m) => m.set_answer(Ok(())),
+        Exchange::RadioPrompt(qa) => fill_text!(qa, resp),
+        Exchange::BinaryPrompt(qa) => {
             let bin_resp = unsafe { BinaryAnswer::upcast(resp) };
             qa.set_answer(Ok(bin_resp
                 .data()