Mercurial > crates > nonstick
comparison src/libpam/question.rs @ 93:efc2b56c8928
Remove undefined behavior per MIRI.
This replaces a bunch of raw pointers with NonNull and removes all the
undefined behavior that we can find with MIRI.
We also remove the `SecureString` dependency (since it doesn't work with MIRI,
and because it's not really necessary).
author | Paul Fisher <paul@pfish.zone> |
---|---|
date | Mon, 23 Jun 2025 13:02:58 -0400 |
parents | dd3e9c4bcde3 |
children | b87100c5eed4 |
comparison
equal
deleted
inserted
replaced
92:5ddbcada30f2 | 93:efc2b56c8928 |
---|---|
9 use crate::libpam::{memory, pam_ffi}; | 9 use crate::libpam::{memory, pam_ffi}; |
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::ptr::NonNull; | |
14 use std::{ptr, slice}; | 15 use std::{ptr, 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: |
212 } | 213 } |
213 } | 214 } |
214 | 215 |
215 /// Gets this message's data pointer as borrowed binary data. | 216 /// Gets this message's data pointer as borrowed binary data. |
216 unsafe fn binary_data(&self) -> (&[u8], u8) { | 217 unsafe fn binary_data(&self) -> (&[u8], u8) { |
217 self.data | 218 NonNull::new(self.data) |
218 .cast::<CBinaryData>() | 219 .map(|nn| nn.cast()) |
219 .as_ref() | 220 .map(|ptr| CBinaryData::data(ptr)) |
220 .map(Into::into) | |
221 .unwrap_or_default() | 221 .unwrap_or_default() |
222 } | 222 } |
223 } | 223 } |
224 | 224 |
225 impl TryFrom<&Message<'_>> for Question { | 225 impl TryFrom<&Message<'_>> for Question { |
226 type Error = ErrorCode; | 226 type Error = ErrorCode; |
227 fn try_from(msg: &Message) -> Result<Self> { | 227 fn try_from(msg: &Message) -> Result<Self> { |
228 let alloc = |style, text| Ok((style, memory::malloc_str(text)?.cast())); | 228 let alloc = |style, text| Ok((style, memory::malloc_str(text)?.cast())); |
229 // We will only allocate heap data if we have a valid input. | 229 // We will only allocate heap data if we have a valid input. |
230 let (style, data): (_, *mut c_void) = match *msg { | 230 let (style, data): (_, NonNull<c_void>) = match *msg { |
231 Message::MaskedPrompt(p) => alloc(Style::PromptEchoOff, p.question()), | 231 Message::MaskedPrompt(p) => alloc(Style::PromptEchoOff, p.question()), |
232 Message::Prompt(p) => alloc(Style::PromptEchoOn, p.question()), | 232 Message::Prompt(p) => alloc(Style::PromptEchoOn, p.question()), |
233 Message::Error(p) => alloc(Style::ErrorMsg, p.question()), | 233 Message::Error(p) => alloc(Style::ErrorMsg, p.question()), |
234 Message::Info(p) => alloc(Style::TextInfo, p.question()), | 234 Message::Info(p) => alloc(Style::TextInfo, p.question()), |
235 #[cfg(feature = "linux-pam-extensions")] | 235 #[cfg(feature = "linux-pam-extensions")] |
242 #[cfg(not(feature = "linux-pam-extensions"))] | 242 #[cfg(not(feature = "linux-pam-extensions"))] |
243 Message::RadioPrompt(_) | Message::BinaryPrompt(_) => Err(ErrorCode::ConversationError), | 243 Message::RadioPrompt(_) | Message::BinaryPrompt(_) => Err(ErrorCode::ConversationError), |
244 }?; | 244 }?; |
245 Ok(Self { | 245 Ok(Self { |
246 style: style.into(), | 246 style: style.into(), |
247 data, | 247 data: data.as_ptr(), |
248 _marker: Default::default(), | 248 _marker: Default::default(), |
249 }) | 249 }) |
250 } | 250 } |
251 } | 251 } |
252 | 252 |
259 // in the Question. If it's not a supported format, we skip it. | 259 // in the Question. If it's not a supported format, we skip it. |
260 if let Ok(style) = Style::try_from(self.style) { | 260 if let Ok(style) = Style::try_from(self.style) { |
261 match style { | 261 match style { |
262 #[cfg(feature = "linux-pam-extensions")] | 262 #[cfg(feature = "linux-pam-extensions")] |
263 Style::BinaryPrompt => { | 263 Style::BinaryPrompt => { |
264 if let Some(d) = self.data.cast::<CBinaryData>().as_mut() { | 264 if let Some(d) = NonNull::new(self.data) { |
265 d.zero_contents() | 265 CBinaryData::zero_contents(d.cast()) |
266 } | 266 } |
267 } | 267 } |
268 #[cfg(feature = "linux-pam-extensions")] | 268 #[cfg(feature = "linux-pam-extensions")] |
269 Style::RadioType => memory::zero_c_string(self.data.cast()), | 269 Style::RadioType => memory::zero_c_string(self.data.cast()), |
270 Style::TextInfo | 270 Style::TextInfo |
365 RadioQAndA::new("you must choose").message(), | 365 RadioQAndA::new("you must choose").message(), |
366 ]).unwrap(); | 366 ]).unwrap(); |
367 let indirect = interrogation.ptr(); | 367 let indirect = interrogation.ptr(); |
368 | 368 |
369 let remade = unsafe { $typ::borrow_ptr(indirect, interrogation.len()) }; | 369 let remade = unsafe { $typ::borrow_ptr(indirect, interrogation.len()) }; |
370 let messages: Vec<OwnedMessage> = unsafe { remade } | 370 let messages: Vec<OwnedMessage> = remade |
371 .map(TryInto::try_into) | 371 .map(TryInto::try_into) |
372 .collect::<Result<_>>() | 372 .collect::<Result<_>>() |
373 .unwrap(); | 373 .unwrap(); |
374 let [bin, choose] = messages.try_into().unwrap(); | 374 let [bin, choose] = messages.try_into().unwrap(); |
375 assert_matches!(bin => OwnedMessage::BinaryPrompt, (&[5, 4, 3, 2, 1][..], 66)); | 375 assert_matches!(bin => OwnedMessage::BinaryPrompt, (&[5, 4, 3, 2, 1][..], 66)); |