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 { |