diff src/libpam/answer.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 e97534be35e3
children
line wrap: on
line diff
--- a/src/libpam/answer.rs	Mon Jun 30 23:49:54 2025 -0400
+++ b/src/libpam/answer.rs	Tue Jul 01 06:11:43 2025 -0400
@@ -1,11 +1,10 @@
 //! Types used to communicate data from the application to the module.
 
-use crate::libpam::conversation::OwnedMessage;
+use crate::libpam::conversation::OwnedExchange;
 use crate::libpam::memory;
-use crate::libpam::memory::{CBinaryData, CHeapBox, CHeapString};
-pub use crate::libpam::pam_ffi::Answer;
+use crate::libpam::memory::{CBinaryData, CHeapBox, CHeapString, Immovable};
 use crate::{ErrorCode, Result};
-use std::ffi::CStr;
+use std::ffi::{c_int, c_void, CStr};
 use std::mem::ManuallyDrop;
 use std::ops::{Deref, DerefMut};
 use std::ptr::NonNull;
@@ -22,7 +21,7 @@
 
 impl Answers {
     /// Builds an Answers out of the given answered Message Q&As.
-    pub fn build(value: Vec<OwnedMessage>) -> Result<Self> {
+    pub fn build(value: Vec<OwnedExchange>) -> Result<Self> {
         let mut outputs = Self {
             base: memory::calloc(value.len())?,
             count: value.len(),
@@ -31,14 +30,16 @@
         // all allocated answer memory.
         for (input, output) in iter::zip(value, outputs.iter_mut()) {
             match input {
-                OwnedMessage::MaskedPrompt(p) => TextAnswer::fill(output, p.answer()?.as_ref())?,
-                OwnedMessage::Prompt(p) => TextAnswer::fill(output, &(p.answer()?))?,
-                OwnedMessage::Error(p) => TextAnswer::fill(output, p.answer().map(|_| "")?)?,
-                OwnedMessage::Info(p) => TextAnswer::fill(output, p.answer().map(|_| "")?)?,
+                OwnedExchange::MaskedPrompt(p) => TextAnswer::fill(output, p.answer()?.as_ref())?,
+                OwnedExchange::Prompt(p) => TextAnswer::fill(output, &(p.answer()?))?,
+                OwnedExchange::Error(p) => TextAnswer::fill(output, p.answer().map(|_| "")?)?,
+                OwnedExchange::Info(p) => TextAnswer::fill(output, p.answer().map(|_| "")?)?,
                 // If we're here, that means that we *got* a Linux-PAM
                 // question from PAM, so we're OK to answer it.
-                OwnedMessage::RadioPrompt(p) => TextAnswer::fill(output, &(p.answer()?))?,
-                OwnedMessage::BinaryPrompt(p) => BinaryAnswer::fill(output, (&p.answer()?).into())?,
+                OwnedExchange::RadioPrompt(p) => TextAnswer::fill(output, &(p.answer()?))?,
+                OwnedExchange::BinaryPrompt(p) => {
+                    BinaryAnswer::fill(output, (&p.answer()?).into())?
+                }
             }
         }
         Ok(outputs)
@@ -48,8 +49,8 @@
     ///
     /// This object is consumed and the `Answer` pointer now owns its data.
     /// It can be recreated with [`Self::from_c_heap`].
-    pub fn into_ptr(self) -> NonNull<Answer> {
-        ManuallyDrop::new(self).base
+    pub fn into_ptr(self) -> *mut libpam_sys::pam_response {
+        ManuallyDrop::new(self).base.as_ptr().cast()
     }
 
     /// Takes ownership of a list of answers allocated on the C heap.
@@ -58,8 +59,11 @@
     ///
     /// It's up to you to make sure you pass a valid pointer,
     /// like one that you got from PAM, or maybe [`Self::into_ptr`].
-    pub unsafe fn from_c_heap(base: NonNull<Answer>, count: usize) -> Self {
-        Answers { base, count }
+    pub unsafe fn from_c_heap(base: NonNull<libpam_sys::pam_response>, count: usize) -> Self {
+        Answers {
+            base: NonNull::new_unchecked(base.as_ptr().cast()),
+            count,
+        }
     }
 }
 
@@ -91,6 +95,25 @@
     }
 }
 
+/// Generic version of answer data.
+///
+/// This has the same structure as [`BinaryAnswer`](crate::libpam::answer::BinaryAnswer)
+/// and [`TextAnswer`](crate::libpam::answer::TextAnswer).
+#[repr(C)]
+#[derive(Debug, Default)]
+pub struct Answer {
+    /// Owned pointer to the data returned in an answer.
+    /// For most answers, this will be a
+    /// [`CHeapString`](crate::libpam::memory::CHeapString),
+    /// but for [`BinaryQAndA`](crate::conv::BinaryQAndA)s
+    /// (a Linux-PAM extension), this will be a [`CHeapBox`] of
+    /// [`CBinaryData`](crate::libpam::memory::CBinaryData).
+    pub data: Option<CHeapBox<c_void>>,
+    /// Unused. Just here for the padding.
+    return_code: c_int,
+    _marker: Immovable,
+}
+
 #[repr(transparent)]
 #[derive(Debug)]
 pub struct TextAnswer(Answer);
@@ -223,19 +246,19 @@
         assert_eq!("", up.contents().unwrap());
     }
 
-    fn round_trip(msgs: Vec<OwnedMessage>) -> Answers {
+    fn round_trip(msgs: Vec<OwnedExchange>) -> Answers {
         let n = msgs.len();
         let sent = Answers::build(msgs).unwrap();
-        unsafe { Answers::from_c_heap(sent.into_ptr(), n) }
+        unsafe { Answers::from_c_heap(NonNull::new_unchecked(sent.into_ptr()), n) }
     }
 
     #[test]
     fn test_round_trip() {
         let mut answers = round_trip(vec![
-            answered!(QAndA, OwnedMessage::Prompt, "whats going on".to_owned()),
-            answered!(MaskedQAndA, OwnedMessage::MaskedPrompt, "well then".into()),
-            answered!(ErrorMsg, OwnedMessage::Error, ()),
-            answered!(InfoMsg, OwnedMessage::Info, ()),
+            answered!(QAndA, OwnedExchange::Prompt, "whats going on".to_owned()),
+            answered!(MaskedQAndA, OwnedExchange::MaskedPrompt, "well then".into()),
+            answered!(ErrorMsg, OwnedExchange::Error, ()),
+            answered!(InfoMsg, OwnedExchange::Info, ()),
         ]);
 
         if let [going, well, err, info] = &mut answers[..] {
@@ -254,13 +277,13 @@
         let binary_msg = {
             let qa = BinaryQAndA::new((&[][..], 0));
             qa.set_answer(Ok(BinaryData::new(vec![1, 2, 3], 99)));
-            OwnedMessage::BinaryPrompt(qa)
+            OwnedExchange::BinaryPrompt(qa)
         };
         let mut answers = round_trip(vec![
             binary_msg,
             answered!(
                 RadioQAndA,
-                OwnedMessage::RadioPrompt,
+                OwnedExchange::RadioPrompt,
                 "beep boop".to_owned()
             ),
         ]);