Mercurial > crates > nonstick
comparison src/libpam/question.rs @ 101:94b51fa4f797
Fix memory soundness issues:
- Ensure Questions are pinned in memory when sending them through PAM.
- Hold on to the PAM conversation struct after we build it.
(Linux-PAM is leninent about this and copies the pam_conv structure.)
author | Paul Fisher <paul@pfish.zone> |
---|---|
date | Tue, 24 Jun 2025 17:54:33 -0400 |
parents | b87100c5eed4 |
children |
comparison
equal
deleted
inserted
replaced
100:3f11b8d30f63 | 101:94b51fa4f797 |
---|---|
9 pub use crate::libpam::pam_ffi::Question; | 9 pub use crate::libpam::pam_ffi::Question; |
10 use crate::ErrorCode; | 10 use crate::ErrorCode; |
11 use crate::Result; | 11 use crate::Result; |
12 use num_enum::{IntoPrimitive, TryFromPrimitive}; | 12 use num_enum::{IntoPrimitive, TryFromPrimitive}; |
13 use std::ffi::{c_void, CStr}; | 13 use std::ffi::{c_void, CStr}; |
14 use std::pin::Pin; | |
14 use std::slice; | 15 use std::slice; |
15 | 16 |
16 /// Abstraction of a collection of questions to be sent in a PAM conversation. | 17 /// Abstraction of a collection of questions to be sent in a PAM conversation. |
17 /// | 18 /// |
18 /// The PAM C API conversation function looks like this: | 19 /// The PAM C API conversation function looks like this: |
62 fn new(messages: &[Message]) -> Result<Self> | 63 fn new(messages: &[Message]) -> Result<Self> |
63 where | 64 where |
64 Self: Sized; | 65 Self: Sized; |
65 | 66 |
66 /// Gets the pointer that is passed . | 67 /// Gets the pointer that is passed . |
67 fn ptr(&self) -> *const *const Question; | 68 fn ptr(self: Pin<&Self>) -> *const *const Question; |
68 | 69 |
69 /// Converts a pointer into a borrowed list of Questions. | 70 /// Converts a pointer into a borrowed list of Questions. |
70 /// | 71 /// |
71 /// # Safety | 72 /// # Safety |
72 /// | 73 /// |
113 questions, | 114 questions, |
114 _marker: Default::default(), | 115 _marker: Default::default(), |
115 }) | 116 }) |
116 } | 117 } |
117 | 118 |
118 fn ptr(&self) -> *const *const Question { | 119 fn ptr(self: Pin<&Self>) -> *const *const Question { |
119 &self.pointer as *const *const Question | 120 &self.pointer as *const *const Question |
120 } | 121 } |
121 | 122 |
122 unsafe fn borrow_ptr<'a>( | 123 unsafe fn borrow_ptr<'a>( |
123 ptr: *const *const Question, | 124 ptr: *const *const Question, |
129 | 130 |
130 /// The Linux version of the pointer train to questions. | 131 /// The Linux version of the pointer train to questions. |
131 #[derive(Debug)] | 132 #[derive(Debug)] |
132 #[repr(C)] | 133 #[repr(C)] |
133 pub struct LinuxPamQuestions { | 134 pub struct LinuxPamQuestions { |
134 #[allow(clippy::vec_box)] // we need to do this. | 135 #[allow(clippy::vec_box)] // we need to box vec items. |
135 /// The place where the questions are. | 136 /// The place where the questions are. |
136 questions: Vec<Box<Question>>, | 137 questions: Vec<Box<Question>>, |
137 _marker: Immovable, | |
138 } | 138 } |
139 | 139 |
140 impl LinuxPamQuestions { | 140 impl LinuxPamQuestions { |
141 fn len(&self) -> usize { | 141 fn len(&self) -> usize { |
142 self.questions.len() | 142 self.questions.len() |
153 .iter() | 153 .iter() |
154 .map(|msg| Question::try_from(msg).map(Box::new)) | 154 .map(|msg| Question::try_from(msg).map(Box::new)) |
155 .collect(); | 155 .collect(); |
156 Ok(Self { | 156 Ok(Self { |
157 questions: questions?, | 157 questions: questions?, |
158 _marker: Default::default(), | |
159 }) | 158 }) |
160 } | 159 } |
161 | 160 |
162 fn ptr(&self) -> *const *const Question { | 161 fn ptr(self: Pin<&Self>) -> *const *const Question { |
163 self.questions.as_ptr().cast() | 162 self.questions.as_ptr().cast() |
164 } | 163 } |
165 | 164 |
166 unsafe fn borrow_ptr<'a>( | 165 unsafe fn borrow_ptr<'a>( |
167 ptr: *const *const Question, | 166 ptr: *const *const Question, |
244 Message::RadioPrompt(_) | Message::BinaryPrompt(_) => Err(ErrorCode::ConversationError), | 243 Message::RadioPrompt(_) | Message::BinaryPrompt(_) => Err(ErrorCode::ConversationError), |
245 }?; | 244 }?; |
246 Ok(Self { | 245 Ok(Self { |
247 style: style.into(), | 246 style: style.into(), |
248 data: Some(data), | 247 data: Some(data), |
249 _marker: Default::default(), | |
250 }) | 248 }) |
251 } | 249 } |
252 } | 250 } |
253 | 251 |
254 impl Drop for Question { | 252 impl Drop for Question { |
326 macro_rules! tests { ($fn_name:ident<$typ:ident>) => { | 324 macro_rules! tests { ($fn_name:ident<$typ:ident>) => { |
327 mod $fn_name { | 325 mod $fn_name { |
328 use super::super::*; | 326 use super::super::*; |
329 #[test] | 327 #[test] |
330 fn standard() { | 328 fn standard() { |
331 let interrogation = <$typ>::new(&[ | 329 let interrogation = Box::pin(<$typ>::new(&[ |
332 MaskedQAndA::new("hocus pocus").message(), | 330 MaskedQAndA::new("hocus pocus").message(), |
333 QAndA::new("what").message(), | 331 QAndA::new("what").message(), |
334 QAndA::new("who").message(), | 332 QAndA::new("who").message(), |
335 InfoMsg::new("hey").message(), | 333 InfoMsg::new("hey").message(), |
336 ErrorMsg::new("gasp").message(), | 334 ErrorMsg::new("gasp").message(), |
337 ]) | 335 ]) |
338 .unwrap(); | 336 .unwrap()); |
339 let indirect = interrogation.ptr(); | 337 let indirect = interrogation.as_ref().ptr(); |
340 | 338 |
341 let remade = unsafe { $typ::borrow_ptr(indirect, interrogation.len()) }; | 339 let remade = unsafe { $typ::borrow_ptr(indirect, interrogation.len()) }; |
342 let messages: Vec<OwnedMessage> = remade | 340 let messages: Vec<OwnedMessage> = remade |
343 .map(TryInto::try_into) | 341 .map(TryInto::try_into) |
344 .collect::<Result<_>>() | 342 .collect::<Result<_>>() |
362 } | 360 } |
363 | 361 |
364 #[test] | 362 #[test] |
365 #[cfg(feature = "linux-pam-extensions")] | 363 #[cfg(feature = "linux-pam-extensions")] |
366 fn linux_extensions() { | 364 fn linux_extensions() { |
367 let interrogation = <$typ>::new(&[ | 365 let interrogation = Box::pin(<$typ>::new(&[ |
368 BinaryQAndA::new((&[5, 4, 3, 2, 1], 66)).message(), | 366 BinaryQAndA::new((&[5, 4, 3, 2, 1], 66)).message(), |
369 RadioQAndA::new("you must choose").message(), | 367 RadioQAndA::new("you must choose").message(), |
370 ]).unwrap(); | 368 ]).unwrap()); |
371 let indirect = interrogation.ptr(); | 369 let indirect = interrogation.as_ref().ptr(); |
372 | 370 |
373 let remade = unsafe { $typ::borrow_ptr(indirect, interrogation.len()) }; | 371 let remade = unsafe { $typ::borrow_ptr(indirect, interrogation.len()) }; |
374 let messages: Vec<OwnedMessage> = remade | 372 let messages: Vec<OwnedMessage> = remade |
375 .map(TryInto::try_into) | 373 .map(TryInto::try_into) |
376 .collect::<Result<_>>() | 374 .collect::<Result<_>>() |