Mercurial > crates > nonstick
comparison 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 |
comparison
equal
deleted
inserted
replaced
129:5b2de52dd8b2 | 130:80c07e5ab22f |
---|---|
1 //! Types used to communicate data from the application to the module. | 1 //! Types used to communicate data from the application to the module. |
2 | 2 |
3 use crate::libpam::conversation::OwnedMessage; | 3 use crate::libpam::conversation::OwnedExchange; |
4 use crate::libpam::memory; | 4 use crate::libpam::memory; |
5 use crate::libpam::memory::{CBinaryData, CHeapBox, CHeapString}; | 5 use crate::libpam::memory::{CBinaryData, CHeapBox, CHeapString, Immovable}; |
6 pub use crate::libpam::pam_ffi::Answer; | |
7 use crate::{ErrorCode, Result}; | 6 use crate::{ErrorCode, Result}; |
8 use std::ffi::CStr; | 7 use std::ffi::{c_int, c_void, CStr}; |
9 use std::mem::ManuallyDrop; | 8 use std::mem::ManuallyDrop; |
10 use std::ops::{Deref, DerefMut}; | 9 use std::ops::{Deref, DerefMut}; |
11 use std::ptr::NonNull; | 10 use std::ptr::NonNull; |
12 use std::{iter, ptr, slice}; | 11 use std::{iter, ptr, slice}; |
13 | 12 |
20 count: usize, | 19 count: usize, |
21 } | 20 } |
22 | 21 |
23 impl Answers { | 22 impl Answers { |
24 /// Builds an Answers out of the given answered Message Q&As. | 23 /// Builds an Answers out of the given answered Message Q&As. |
25 pub fn build(value: Vec<OwnedMessage>) -> Result<Self> { | 24 pub fn build(value: Vec<OwnedExchange>) -> Result<Self> { |
26 let mut outputs = Self { | 25 let mut outputs = Self { |
27 base: memory::calloc(value.len())?, | 26 base: memory::calloc(value.len())?, |
28 count: value.len(), | 27 count: value.len(), |
29 }; | 28 }; |
30 // Even if we fail during this process, we still end up freeing | 29 // Even if we fail during this process, we still end up freeing |
31 // all allocated answer memory. | 30 // all allocated answer memory. |
32 for (input, output) in iter::zip(value, outputs.iter_mut()) { | 31 for (input, output) in iter::zip(value, outputs.iter_mut()) { |
33 match input { | 32 match input { |
34 OwnedMessage::MaskedPrompt(p) => TextAnswer::fill(output, p.answer()?.as_ref())?, | 33 OwnedExchange::MaskedPrompt(p) => TextAnswer::fill(output, p.answer()?.as_ref())?, |
35 OwnedMessage::Prompt(p) => TextAnswer::fill(output, &(p.answer()?))?, | 34 OwnedExchange::Prompt(p) => TextAnswer::fill(output, &(p.answer()?))?, |
36 OwnedMessage::Error(p) => TextAnswer::fill(output, p.answer().map(|_| "")?)?, | 35 OwnedExchange::Error(p) => TextAnswer::fill(output, p.answer().map(|_| "")?)?, |
37 OwnedMessage::Info(p) => TextAnswer::fill(output, p.answer().map(|_| "")?)?, | 36 OwnedExchange::Info(p) => TextAnswer::fill(output, p.answer().map(|_| "")?)?, |
38 // If we're here, that means that we *got* a Linux-PAM | 37 // If we're here, that means that we *got* a Linux-PAM |
39 // question from PAM, so we're OK to answer it. | 38 // question from PAM, so we're OK to answer it. |
40 OwnedMessage::RadioPrompt(p) => TextAnswer::fill(output, &(p.answer()?))?, | 39 OwnedExchange::RadioPrompt(p) => TextAnswer::fill(output, &(p.answer()?))?, |
41 OwnedMessage::BinaryPrompt(p) => BinaryAnswer::fill(output, (&p.answer()?).into())?, | 40 OwnedExchange::BinaryPrompt(p) => { |
41 BinaryAnswer::fill(output, (&p.answer()?).into())? | |
42 } | |
42 } | 43 } |
43 } | 44 } |
44 Ok(outputs) | 45 Ok(outputs) |
45 } | 46 } |
46 | 47 |
47 /// Converts this into a `*Answer` for passing to PAM. | 48 /// Converts this into a `*Answer` for passing to PAM. |
48 /// | 49 /// |
49 /// This object is consumed and the `Answer` pointer now owns its data. | 50 /// This object is consumed and the `Answer` pointer now owns its data. |
50 /// It can be recreated with [`Self::from_c_heap`]. | 51 /// It can be recreated with [`Self::from_c_heap`]. |
51 pub fn into_ptr(self) -> NonNull<Answer> { | 52 pub fn into_ptr(self) -> *mut libpam_sys::pam_response { |
52 ManuallyDrop::new(self).base | 53 ManuallyDrop::new(self).base.as_ptr().cast() |
53 } | 54 } |
54 | 55 |
55 /// Takes ownership of a list of answers allocated on the C heap. | 56 /// Takes ownership of a list of answers allocated on the C heap. |
56 /// | 57 /// |
57 /// # Safety | 58 /// # Safety |
58 /// | 59 /// |
59 /// It's up to you to make sure you pass a valid pointer, | 60 /// It's up to you to make sure you pass a valid pointer, |
60 /// like one that you got from PAM, or maybe [`Self::into_ptr`]. | 61 /// like one that you got from PAM, or maybe [`Self::into_ptr`]. |
61 pub unsafe fn from_c_heap(base: NonNull<Answer>, count: usize) -> Self { | 62 pub unsafe fn from_c_heap(base: NonNull<libpam_sys::pam_response>, count: usize) -> Self { |
62 Answers { base, count } | 63 Answers { |
64 base: NonNull::new_unchecked(base.as_ptr().cast()), | |
65 count, | |
66 } | |
63 } | 67 } |
64 } | 68 } |
65 | 69 |
66 impl Deref for Answers { | 70 impl Deref for Answers { |
67 type Target = [Answer]; | 71 type Target = [Answer]; |
87 ptr::drop_in_place(answer) | 91 ptr::drop_in_place(answer) |
88 } | 92 } |
89 memory::free(self.base.as_ptr()) | 93 memory::free(self.base.as_ptr()) |
90 } | 94 } |
91 } | 95 } |
96 } | |
97 | |
98 /// Generic version of answer data. | |
99 /// | |
100 /// This has the same structure as [`BinaryAnswer`](crate::libpam::answer::BinaryAnswer) | |
101 /// and [`TextAnswer`](crate::libpam::answer::TextAnswer). | |
102 #[repr(C)] | |
103 #[derive(Debug, Default)] | |
104 pub struct Answer { | |
105 /// Owned pointer to the data returned in an answer. | |
106 /// For most answers, this will be a | |
107 /// [`CHeapString`](crate::libpam::memory::CHeapString), | |
108 /// but for [`BinaryQAndA`](crate::conv::BinaryQAndA)s | |
109 /// (a Linux-PAM extension), this will be a [`CHeapBox`] of | |
110 /// [`CBinaryData`](crate::libpam::memory::CBinaryData). | |
111 pub data: Option<CHeapBox<c_void>>, | |
112 /// Unused. Just here for the padding. | |
113 return_code: c_int, | |
114 _marker: Immovable, | |
92 } | 115 } |
93 | 116 |
94 #[repr(transparent)] | 117 #[repr(transparent)] |
95 #[derive(Debug)] | 118 #[derive(Debug)] |
96 pub struct TextAnswer(Answer); | 119 pub struct TextAnswer(Answer); |
221 assert_eq!(want, up.contents().unwrap()); | 244 assert_eq!(want, up.contents().unwrap()); |
222 up.zero_contents(); | 245 up.zero_contents(); |
223 assert_eq!("", up.contents().unwrap()); | 246 assert_eq!("", up.contents().unwrap()); |
224 } | 247 } |
225 | 248 |
226 fn round_trip(msgs: Vec<OwnedMessage>) -> Answers { | 249 fn round_trip(msgs: Vec<OwnedExchange>) -> Answers { |
227 let n = msgs.len(); | 250 let n = msgs.len(); |
228 let sent = Answers::build(msgs).unwrap(); | 251 let sent = Answers::build(msgs).unwrap(); |
229 unsafe { Answers::from_c_heap(sent.into_ptr(), n) } | 252 unsafe { Answers::from_c_heap(NonNull::new_unchecked(sent.into_ptr()), n) } |
230 } | 253 } |
231 | 254 |
232 #[test] | 255 #[test] |
233 fn test_round_trip() { | 256 fn test_round_trip() { |
234 let mut answers = round_trip(vec![ | 257 let mut answers = round_trip(vec![ |
235 answered!(QAndA, OwnedMessage::Prompt, "whats going on".to_owned()), | 258 answered!(QAndA, OwnedExchange::Prompt, "whats going on".to_owned()), |
236 answered!(MaskedQAndA, OwnedMessage::MaskedPrompt, "well then".into()), | 259 answered!(MaskedQAndA, OwnedExchange::MaskedPrompt, "well then".into()), |
237 answered!(ErrorMsg, OwnedMessage::Error, ()), | 260 answered!(ErrorMsg, OwnedExchange::Error, ()), |
238 answered!(InfoMsg, OwnedMessage::Info, ()), | 261 answered!(InfoMsg, OwnedExchange::Info, ()), |
239 ]); | 262 ]); |
240 | 263 |
241 if let [going, well, err, info] = &mut answers[..] { | 264 if let [going, well, err, info] = &mut answers[..] { |
242 assert_text_answer("whats going on", going); | 265 assert_text_answer("whats going on", going); |
243 assert_text_answer("well then", well); | 266 assert_text_answer("well then", well); |
252 fn test_round_trip_linux() { | 275 fn test_round_trip_linux() { |
253 use crate::conv::{BinaryData, BinaryQAndA, RadioQAndA}; | 276 use crate::conv::{BinaryData, BinaryQAndA, RadioQAndA}; |
254 let binary_msg = { | 277 let binary_msg = { |
255 let qa = BinaryQAndA::new((&[][..], 0)); | 278 let qa = BinaryQAndA::new((&[][..], 0)); |
256 qa.set_answer(Ok(BinaryData::new(vec![1, 2, 3], 99))); | 279 qa.set_answer(Ok(BinaryData::new(vec![1, 2, 3], 99))); |
257 OwnedMessage::BinaryPrompt(qa) | 280 OwnedExchange::BinaryPrompt(qa) |
258 }; | 281 }; |
259 let mut answers = round_trip(vec![ | 282 let mut answers = round_trip(vec![ |
260 binary_msg, | 283 binary_msg, |
261 answered!( | 284 answered!( |
262 RadioQAndA, | 285 RadioQAndA, |
263 OwnedMessage::RadioPrompt, | 286 OwnedExchange::RadioPrompt, |
264 "beep boop".to_owned() | 287 "beep boop".to_owned() |
265 ), | 288 ), |
266 ]); | 289 ]); |
267 | 290 |
268 if let [bin, radio] = &mut answers[..] { | 291 if let [bin, radio] = &mut answers[..] { |