comparison src/libpam/answer.rs @ 87:05291b601f0a

Well and truly separate the Linux extensions. This separates the Linux extensions on the libpam side, and disables the two enums on the interface side. Users can still call the Linux extensions from non-Linux PAM impls, but they'll get a conversation error back.
author Paul Fisher <paul@pfish.zone>
date Tue, 10 Jun 2025 04:40:01 -0400
parents 5aa1a010f1e8
children
comparison
equal deleted inserted replaced
86:23162cd399aa 87:05291b601f0a
27 // all allocated answer memory. 27 // all allocated answer memory.
28 for (input, output) in iter::zip(value, outputs.iter_mut()) { 28 for (input, output) in iter::zip(value, outputs.iter_mut()) {
29 match input { 29 match input {
30 OwnedMessage::MaskedPrompt(p) => TextAnswer::fill(output, p.answer()?.unsecure())?, 30 OwnedMessage::MaskedPrompt(p) => TextAnswer::fill(output, p.answer()?.unsecure())?,
31 OwnedMessage::Prompt(p) => TextAnswer::fill(output, &(p.answer()?))?, 31 OwnedMessage::Prompt(p) => TextAnswer::fill(output, &(p.answer()?))?,
32 OwnedMessage::BinaryPrompt(p) => BinaryAnswer::fill(output, (&p.answer()?).into())?,
33 OwnedMessage::Error(p) => TextAnswer::fill(output, p.answer().map(|_| "")?)?, 32 OwnedMessage::Error(p) => TextAnswer::fill(output, p.answer().map(|_| "")?)?,
34 OwnedMessage::Info(p) => TextAnswer::fill(output, p.answer().map(|_| "")?)?, 33 OwnedMessage::Info(p) => TextAnswer::fill(output, p.answer().map(|_| "")?)?,
34 // If we're here, that means that we *got* a Linux-PAM
35 // question from PAM, so we're OK to answer it.
35 OwnedMessage::RadioPrompt(p) => TextAnswer::fill(output, &(p.answer()?))?, 36 OwnedMessage::RadioPrompt(p) => TextAnswer::fill(output, &(p.answer()?))?,
37 OwnedMessage::BinaryPrompt(p) => BinaryAnswer::fill(output, (&p.answer()?).into())?,
36 } 38 }
37 } 39 }
38 Ok(outputs) 40 Ok(outputs)
39 } 41 }
40 42
211 } 213 }
212 } 214 }
213 215
214 #[cfg(test)] 216 #[cfg(test)]
215 mod tests { 217 mod tests {
216 use super::{Answer, Answers, BinaryAnswer, TextAnswer}; 218 use super::*;
217 use crate::conv::{BinaryQAndA, ErrorMsg, InfoMsg, MaskedQAndA, QAndA, RadioQAndA}; 219 use crate::conv::{ErrorMsg, InfoMsg, MaskedQAndA, QAndA};
218 use crate::libpam::conversation::OwnedMessage; 220
219 use crate::libpam::memory; 221 macro_rules! answered {
220 use crate::BinaryData; 222 ($typ:ty, $msg:path, $data:expr) => {{
223 let qa = <$typ>::new("");
224 qa.set_answer(Ok($data));
225 $msg(qa)
226 }};
227 }
228
229 fn assert_text_answer(want: &str, answer: &mut Answer) {
230 let up = unsafe { TextAnswer::upcast(answer) };
231 assert_eq!(want, up.contents().unwrap());
232 up.free_contents();
233 assert_eq!("", up.contents().unwrap());
234 }
235
236 fn round_trip(msgs: Vec<OwnedMessage>) -> Answers {
237 let n = msgs.len();
238 let sent = Answers::build(msgs).unwrap();
239 unsafe { Answers::from_c_heap(sent.into_ptr(), n) }
240 }
221 241
222 #[test] 242 #[test]
223 fn test_round_trip() { 243 fn test_round_trip() {
244 let mut answers = round_trip(vec![
245 answered!(QAndA, OwnedMessage::Prompt, "whats going on".to_owned()),
246 answered!(MaskedQAndA, OwnedMessage::MaskedPrompt, "well then".into()),
247 answered!(ErrorMsg, OwnedMessage::Error, ()),
248 answered!(InfoMsg, OwnedMessage::Info, ()),
249 ]);
250
251 if let [going, well, err, info] = &mut answers[..] {
252 assert_text_answer("whats going on", going);
253 assert_text_answer("well then", well);
254 assert_text_answer("", err);
255 assert_text_answer("", info);
256 } else {
257 panic!("received wrong size {len}!", len = answers.len())
258 }
259 }
260
261 #[cfg(feature = "linux-pam-extensions")]
262 fn test_round_trip_linux() {
263 use crate::conv::{BinaryData, BinaryQAndA, RadioQAndA};
224 let binary_msg = { 264 let binary_msg = {
225 let qa = BinaryQAndA::new((&[][..], 0)); 265 let qa = BinaryQAndA::new((&[][..], 0));
226 qa.set_answer(Ok(BinaryData::new(vec![1, 2, 3], 99))); 266 qa.set_answer(Ok(BinaryData::new(vec![1, 2, 3], 99)));
227 OwnedMessage::BinaryPrompt(qa) 267 OwnedMessage::BinaryPrompt(qa)
228 }; 268 };
229 269 let mut answers = round_trip(vec![
230 macro_rules! answered {
231 ($typ:ty, $msg:path, $data:expr) => {{
232 let qa = <$typ>::new("");
233 qa.set_answer(Ok($data));
234 $msg(qa)
235 }};
236 }
237
238 let answers = vec![
239 binary_msg, 270 binary_msg,
240 answered!(QAndA, OwnedMessage::Prompt, "whats going on".to_owned()),
241 answered!(MaskedQAndA, OwnedMessage::MaskedPrompt, "well then".into()),
242 answered!(ErrorMsg, OwnedMessage::Error, ()),
243 answered!(InfoMsg, OwnedMessage::Info, ()),
244 answered!( 271 answered!(
245 RadioQAndA, 272 RadioQAndA,
246 OwnedMessage::RadioPrompt, 273 OwnedMessage::RadioPrompt,
247 "beep boop".to_owned() 274 "beep boop".to_owned()
248 ), 275 ),
249 ]; 276 ]);
250 let n = answers.len(); 277
251 let sent = Answers::build(answers).unwrap(); 278 if let [bin, radio] = &mut answers[..] {
252 let heap_answers = sent.into_ptr(); 279 let up = unsafe { BinaryAnswer::upcast(bin) };
253 let mut received = unsafe { Answers::from_c_heap(heap_answers, n) }; 280 assert_eq!(BinaryData::from((&[1, 2, 3][..], 99)), up.data().into());
254
255 let assert_text = |want, raw| {
256 let up = unsafe { TextAnswer::upcast(raw) };
257 assert_eq!(want, up.contents().unwrap());
258 up.free_contents();
259 assert_eq!("", up.contents().unwrap());
260 };
261 let assert_bin = |want, raw| {
262 let up = unsafe { BinaryAnswer::upcast(raw) };
263 assert_eq!(BinaryData::from(want), up.data().into());
264 up.zero_contents(); 281 up.zero_contents();
265 assert_eq!(BinaryData::default(), up.data().into()); 282 assert_eq!(BinaryData::default(), up.data().into());
266 }; 283
267 if let [zero, one, two, three, four, five] = &mut received[..] { 284 assert_text_answer("beep boop", radio);
268 assert_bin((&[1, 2, 3][..], 99), zero);
269 assert_text("whats going on", one);
270 assert_text("well then", two);
271 assert_text("", three);
272 assert_text("", four);
273 assert_text("beep boop", five);
274 } else { 285 } else {
275 panic!("received wrong size {len}!", len = received.len()) 286 panic!("received wrong size {len}!", len = answers.len())
276 } 287 }
277 } 288 }
278 289
279 #[test] 290 #[test]
280 fn test_text_answer() { 291 fn test_text_answer() {
290 unsafe { memory::free(answer_ptr) } 301 unsafe { memory::free(answer_ptr) }
291 } 302 }
292 303
293 #[test] 304 #[test]
294 fn test_binary_answer() { 305 fn test_binary_answer() {
306 use crate::conv::BinaryData;
295 let answer_ptr: *mut Answer = memory::calloc(1); 307 let answer_ptr: *mut Answer = memory::calloc(1);
296 let answer = unsafe { &mut *answer_ptr }; 308 let answer = unsafe { &mut *answer_ptr };
297 let real_data = BinaryData::new([1, 2, 3, 4, 5, 6, 7, 8], 9); 309 let real_data = BinaryData::new([1, 2, 3, 4, 5, 6, 7, 8], 9);
298 BinaryAnswer::fill(answer, (&real_data).into()).expect("alloc should succeed"); 310 BinaryAnswer::fill(answer, (&real_data).into()).expect("alloc should succeed");
299 let bin_answer = unsafe { BinaryAnswer::upcast(answer) }; 311 let bin_answer = unsafe { BinaryAnswer::upcast(answer) };
304 } 316 }
305 317
306 #[test] 318 #[test]
307 #[ignore] 319 #[ignore]
308 fn test_binary_answer_too_big() { 320 fn test_binary_answer_too_big() {
309 let big_data: Vec<u8> = vec![0xFFu8; 10_000_000_000]; 321 let big_data: Vec<u8> = vec![0xFFu8; 0x1_0000_0001];
310 let answer_ptr: *mut Answer = memory::calloc(1); 322 let answer_ptr: *mut Answer = memory::calloc(1);
311 let answer = unsafe { &mut *answer_ptr }; 323 let answer = unsafe { &mut *answer_ptr };
312 BinaryAnswer::fill(answer, (&big_data, 100)).expect_err("this is too big!"); 324 BinaryAnswer::fill(answer, (&big_data, 100)).expect_err("this is too big!");
313 answer.free_contents(); 325 answer.free_contents();
314 unsafe { memory::free(answer) } 326 unsafe { memory::free(answer) }