Mercurial > crates > nonstick
comparison src/libpam/question.rs @ 139:33b9622ed6d2
Remove redundant memory management in nonstick::libpam; fix UB.
- Uses the libpam-sys-helpers BinaryPayload / OwnedBinaryPayload structs
to handle memory management and parsing for Linux-PAM binary messages.
- Gets rid of the (technically) undefined behavior in PtrPtrVec
due to pointer provenance.
- Don't check for malloc failing. It won't, even if it does.
- Formatting/cleanups/etc.
| author | Paul Fisher <paul@pfish.zone> |
|---|---|
| date | Thu, 03 Jul 2025 23:57:49 -0400 |
| parents | 80c07e5ab22f |
| children | a508a69c068a |
comparison
equal
deleted
inserted
replaced
| 138:999bf07efbcb | 139:33b9622ed6d2 |
|---|---|
| 1 //! Data and types dealing with PAM messages. | 1 //! Data and types dealing with PAM messages. |
| 2 | 2 |
| 3 #[cfg(feature = "linux-pam-ext")] | 3 #[cfg(feature = "linux-pam-ext")] |
| 4 use crate::conv::{BinaryQAndA, RadioQAndA}; | 4 use crate::conv::{BinaryQAndA, RadioQAndA}; |
| 5 use libpam_sys_helpers::memory::{BinaryPayload, TooBigError}; | |
| 5 use crate::conv::{ErrorMsg, Exchange, InfoMsg, MaskedQAndA, QAndA}; | 6 use crate::conv::{ErrorMsg, Exchange, InfoMsg, MaskedQAndA, QAndA}; |
| 6 use crate::libpam::conversation::OwnedExchange; | 7 use crate::libpam::conversation::OwnedExchange; |
| 7 use crate::libpam::memory::{CBinaryData, CHeapBox, CHeapString}; | 8 use crate::libpam::memory::{CHeapBox, CHeapPayload, CHeapString}; |
| 8 use crate::ErrorCode; | 9 use crate::ErrorCode; |
| 9 use crate::Result; | 10 use crate::Result; |
| 10 use num_enum::{IntoPrimitive, TryFromPrimitive}; | 11 use num_enum::{IntoPrimitive, TryFromPrimitive}; |
| 11 use std::ffi::{c_int, c_void, CStr}; | 12 use std::ffi::{c_int, c_void, CStr}; |
| 13 use std::ptr::NonNull; | |
| 12 | 14 |
| 13 mod style_const { | 15 mod style_const { |
| 14 pub use libpam_sys::*; | 16 pub use libpam_sys::*; |
| 15 #[cfg(not(feature = "link"))] | 17 #[cfg(not(feature = "link"))] |
| 16 #[cfg_pam_impl(not("LinuxPam"))] | 18 #[cfg_pam_impl(not("LinuxPam"))] |
| 44 } | 46 } |
| 45 | 47 |
| 46 /// A question sent by PAM or a module to an application. | 48 /// A question sent by PAM or a module to an application. |
| 47 /// | 49 /// |
| 48 /// PAM refers to this as a "message", but we call it a question | 50 /// PAM refers to this as a "message", but we call it a question |
| 49 /// to avoid confusion with [`Message`](crate::conv::Exchange). | 51 /// to avoid confusion. |
| 50 /// | 52 /// |
| 51 /// This question, and its internal data, is owned by its creator | 53 /// This question, and its internal data, is owned by its creator |
| 52 /// (either the module or PAM itself). | 54 /// (either the module or PAM itself). |
| 53 #[repr(C)] | 55 #[repr(C)] |
| 54 #[derive(Debug)] | 56 #[derive(Debug)] |
| 58 /// A description of the data requested. | 60 /// A description of the data requested. |
| 59 /// | 61 /// |
| 60 /// For most requests, this will be an owned [`CStr`], | 62 /// For most requests, this will be an owned [`CStr`], |
| 61 /// but for requests with style `PAM_BINARY_PROMPT`, | 63 /// but for requests with style `PAM_BINARY_PROMPT`, |
| 62 /// this will be `CBinaryData` (a Linux-PAM extension). | 64 /// this will be `CBinaryData` (a Linux-PAM extension). |
| 63 pub data: Option<CHeapBox<c_void>>, | 65 pub data: Option<NonNull<c_void>>, |
| 64 } | 66 } |
| 65 | 67 |
| 66 impl Question { | 68 impl Question { |
| 67 /// Gets this message's data pointer as a string. | 69 /// Gets this message's data pointer as a string. |
| 68 /// | 70 /// |
| 70 /// | 72 /// |
| 71 /// It's up to you to pass this only on types with a string value. | 73 /// It's up to you to pass this only on types with a string value. |
| 72 unsafe fn string_data(&self) -> Result<&str> { | 74 unsafe fn string_data(&self) -> Result<&str> { |
| 73 match self.data.as_ref() { | 75 match self.data.as_ref() { |
| 74 None => Ok(""), | 76 None => Ok(""), |
| 75 Some(data) => CStr::from_ptr(CHeapBox::as_ptr(data).cast().as_ptr()) | 77 Some(data) => CStr::from_ptr(data.as_ptr().cast()) |
| 76 .to_str() | 78 .to_str() |
| 77 .map_err(|_| ErrorCode::ConversationError), | 79 .map_err(|_| ErrorCode::ConversationError), |
| 78 } | 80 } |
| 79 } | 81 } |
| 80 | 82 |
| 81 /// Gets this message's data pointer as borrowed binary data. | 83 /// Gets this message's data pointer as borrowed binary data. |
| 82 unsafe fn binary_data(&self) -> (&[u8], u8) { | 84 unsafe fn binary_data(&self) -> (&[u8], u8) { |
| 83 self.data | 85 self.data |
| 84 .as_ref() | 86 .as_ref() |
| 85 .map(|data| CBinaryData::data(CHeapBox::as_ptr(data).cast())) | 87 .map(|data| BinaryPayload::contents(data.as_ptr().cast())) |
| 86 .unwrap_or_default() | 88 .unwrap_or_default() |
| 87 } | 89 } |
| 88 } | 90 } |
| 89 | 91 |
| 90 impl TryFrom<&Exchange<'_>> for Question { | 92 impl TryFrom<&Exchange<'_>> for Question { |
| 102 Exchange::Error(p) => alloc(Style::ErrorMsg, p.question()), | 104 Exchange::Error(p) => alloc(Style::ErrorMsg, p.question()), |
| 103 Exchange::Info(p) => alloc(Style::TextInfo, p.question()), | 105 Exchange::Info(p) => alloc(Style::TextInfo, p.question()), |
| 104 #[cfg(feature = "linux-pam-ext")] | 106 #[cfg(feature = "linux-pam-ext")] |
| 105 Exchange::RadioPrompt(p) => alloc(Style::RadioType, p.question()), | 107 Exchange::RadioPrompt(p) => alloc(Style::RadioType, p.question()), |
| 106 #[cfg(feature = "linux-pam-ext")] | 108 #[cfg(feature = "linux-pam-ext")] |
| 107 Exchange::BinaryPrompt(p) => Ok((Style::BinaryPrompt, unsafe { | 109 Exchange::BinaryPrompt(p) => { |
| 108 CHeapBox::cast(CBinaryData::alloc(p.question())?) | 110 let (data, typ) = p.question(); |
| 109 })), | 111 let payload = CHeapPayload::new(data, typ)?.into_inner(); |
| 112 Ok((Style::BinaryPrompt, unsafe { CHeapBox::cast(payload) })) | |
| 113 }, | |
| 110 #[cfg(not(feature = "linux-pam-ext"))] | 114 #[cfg(not(feature = "linux-pam-ext"))] |
| 111 Exchange::RadioPrompt(_) | Exchange::BinaryPrompt(_) => { | 115 Exchange::RadioPrompt(_) | Exchange::BinaryPrompt(_) => { |
| 112 Err(ErrorCode::ConversationError) | 116 Err(ErrorCode::ConversationError) |
| 113 } | 117 } |
| 114 }?; | 118 }?; |
| 115 Ok(Self { | 119 Ok(Self { |
| 116 style: style.into(), | 120 style: style.into(), |
| 117 data: Some(data), | 121 data: Some(CHeapBox::into_ptr(data)), |
| 118 }) | 122 }) |
| 119 } | 123 } |
| 120 } | 124 } |
| 121 | 125 |
| 122 impl Drop for Question { | 126 impl Drop for Question { |
| 129 if let Ok(style) = Style::try_from(self.style) { | 133 if let Ok(style) = Style::try_from(self.style) { |
| 130 let _ = match style { | 134 let _ = match style { |
| 131 #[cfg(feature = "linux-pam-ext")] | 135 #[cfg(feature = "linux-pam-ext")] |
| 132 Style::BinaryPrompt => self | 136 Style::BinaryPrompt => self |
| 133 .data | 137 .data |
| 134 .as_ref() | 138 .as_mut() |
| 135 .map(|p| CBinaryData::zero_contents(CHeapBox::as_ptr(p).cast())), | 139 .map(|p| BinaryPayload::zero(p.as_ptr().cast())), |
| 136 #[cfg(feature = "linux-pam-ext")] | 140 #[cfg(feature = "linux-pam-ext")] |
| 137 Style::RadioType => self | 141 Style::RadioType => self |
| 138 .data | 142 .data |
| 139 .as_ref() | 143 .as_mut() |
| 140 .map(|p| CHeapString::zero(CHeapBox::as_ptr(p).cast())), | 144 .map(|p| CHeapString::zero(p.cast())), |
| 141 Style::TextInfo | 145 Style::TextInfo |
| 142 | Style::ErrorMsg | 146 | Style::ErrorMsg |
| 143 | Style::PromptEchoOff | 147 | Style::PromptEchoOff |
| 144 | Style::PromptEchoOn => self | 148 | Style::PromptEchoOn => { |
| 145 .data | 149 self.data.as_mut().map(|p| CHeapString::zero(p.cast())) |
| 146 .as_ref() | 150 } |
| 147 .map(|p| CHeapString::zero(CHeapBox::as_ptr(p).cast())), | |
| 148 }; | 151 }; |
| 149 }; | 152 }; |
| 153 let _ = self.data.map(|p| CHeapBox::from_ptr(p)); | |
| 150 } | 154 } |
| 151 } | 155 } |
| 152 } | 156 } |
| 153 | 157 |
| 154 impl<'a> TryFrom<&'a Question> for OwnedExchange<'a> { | 158 impl<'a> TryFrom<&'a Question> for OwnedExchange<'a> { |
| 176 }; | 180 }; |
| 177 Ok(prompt) | 181 Ok(prompt) |
| 178 } | 182 } |
| 179 } | 183 } |
| 180 | 184 |
| 185 #[cfg(feature = "linux-pam-ext")] | |
| 186 impl From<TooBigError> for ErrorCode { | |
| 187 fn from(_: TooBigError) -> Self { | |
| 188 ErrorCode::BufferError | |
| 189 } | |
| 190 } | |
| 191 | |
| 181 #[cfg(test)] | 192 #[cfg(test)] |
| 182 mod tests { | 193 mod tests { |
| 183 use super::*; | 194 use super::*; |
| 184 | 195 |
| 185 macro_rules! assert_matches { | 196 macro_rules! assert_matches { |
