Mercurial > crates > nonstick
comparison src/libpam/question.rs @ 143:ebb71a412b58
Turn everything into OsString and Just Walk Out! for strings with nul.
To reduce the hazard surface of the API, this replaces most uses of &str
with &OsStr (and likewise with String/OsString).
Also, I've decided that instead of dealing with callers putting `\0`
in their parameters, I'm going to follow the example of std::env and
Just Walk Out! (i.e., panic!()).
This makes things a lot less annoying for both me and (hopefully) users.
author | Paul Fisher <paul@pfish.zone> |
---|---|
date | Sat, 05 Jul 2025 22:12:46 -0400 |
parents | a508a69c068a |
children | 4b3a5095f68c |
comparison
equal
deleted
inserted
replaced
142:5c1e315c18ff | 143:ebb71a412b58 |
---|---|
3 use crate::conv::{ErrorMsg, Exchange, InfoMsg, MaskedQAndA, QAndA}; | 3 use crate::conv::{ErrorMsg, Exchange, InfoMsg, MaskedQAndA, QAndA}; |
4 use crate::libpam::conversation::OwnedExchange; | 4 use crate::libpam::conversation::OwnedExchange; |
5 use crate::libpam::memory; | 5 use crate::libpam::memory; |
6 use crate::ErrorCode; | 6 use crate::ErrorCode; |
7 use crate::Result; | 7 use crate::Result; |
8 use libpam_sys_helpers::memory as pammem; | 8 use libpam_sys_helpers::memory as pam_mem; |
9 use num_enum::{IntoPrimitive, TryFromPrimitive}; | 9 use num_enum::{IntoPrimitive, TryFromPrimitive}; |
10 use std::ffi::{c_int, c_void, CStr}; | 10 use std::ffi::{c_int, c_void, CStr, OsStr}; |
11 use std::os::unix::ffi::OsStrExt; | |
11 use std::ptr::NonNull; | 12 use std::ptr::NonNull; |
12 | 13 |
13 mod style_const { | 14 mod style_const { |
14 pub use libpam_sys::*; | 15 pub use libpam_sys::*; |
15 #[cfg(not(feature = "link"))] | 16 #[cfg(not(feature = "link"))] |
67 /// Gets this message's data pointer as a string. | 68 /// Gets this message's data pointer as a string. |
68 /// | 69 /// |
69 /// # Safety | 70 /// # Safety |
70 /// | 71 /// |
71 /// It's up to you to pass this only on types with a string value. | 72 /// It's up to you to pass this only on types with a string value. |
72 unsafe fn string_data(&self) -> Result<&str> { | 73 unsafe fn string_data(&self) -> &OsStr { |
73 match self.data.as_ref() { | 74 match self.data.as_ref() { |
74 None => Ok(""), | 75 None => "".as_ref(), |
75 Some(data) => CStr::from_ptr(data.as_ptr().cast()) | 76 Some(data) => OsStr::from_bytes(CStr::from_ptr(data.as_ptr().cast()).to_bytes()), |
76 .to_str() | |
77 .map_err(|_| ErrorCode::ConversationError), | |
78 } | 77 } |
79 } | 78 } |
80 | 79 |
81 /// Gets this message's data pointer as borrowed binary data. | 80 /// Gets this message's data pointer as borrowed binary data. |
82 unsafe fn binary_data(&self) -> (&[u8], u8) { | 81 unsafe fn binary_data(&self) -> (&[u8], u8) { |
83 self.data | 82 self.data |
84 .as_ref() | 83 .as_ref() |
85 .map(|data| pammem::BinaryPayload::contents(data.as_ptr().cast())) | 84 .map(|data| pam_mem::BinaryPayload::contents(data.as_ptr().cast())) |
86 .unwrap_or_default() | 85 .unwrap_or_default() |
87 } | 86 } |
88 } | 87 } |
89 | 88 |
90 impl TryFrom<&Exchange<'_>> for Question { | 89 impl TryFrom<&Exchange<'_>> for Question { |
91 type Error = ErrorCode; | 90 type Error = ErrorCode; |
92 fn try_from(msg: &Exchange) -> Result<Self> { | 91 fn try_from(msg: &Exchange) -> Result<Self> { |
93 let alloc = |style, text| -> Result<_> { | 92 let alloc = |style, text: &OsStr| -> Result<_> { |
94 Ok((style, unsafe { | 93 Ok((style, unsafe { |
95 memory::CHeapBox::cast(memory::CHeapString::new(text)?.into_box()) | 94 memory::CHeapBox::cast(memory::CHeapString::new(text.as_bytes()).into_box()) |
96 })) | 95 })) |
97 }; | 96 }; |
98 // We will only allocate heap data if we have a valid input. | 97 // We will only allocate heap data if we have a valid input. |
99 let (style, data): (_, memory::CHeapBox<c_void>) = match *msg { | 98 let (style, data): (_, memory::CHeapBox<c_void>) = match *msg { |
100 Exchange::MaskedPrompt(p) => alloc(Style::PromptEchoOff, p.question()), | 99 Exchange::MaskedPrompt(p) => alloc(Style::PromptEchoOff, p.question()), |
134 let _ = match style { | 133 let _ = match style { |
135 #[cfg(feature = "linux-pam-ext")] | 134 #[cfg(feature = "linux-pam-ext")] |
136 Style::BinaryPrompt => self | 135 Style::BinaryPrompt => self |
137 .data | 136 .data |
138 .as_mut() | 137 .as_mut() |
139 .map(|p| pammem::BinaryPayload::zero(p.as_ptr().cast())), | 138 .map(|p| pam_mem::BinaryPayload::zero(p.as_ptr().cast())), |
140 #[cfg(feature = "linux-pam-ext")] | 139 #[cfg(feature = "linux-pam-ext")] |
141 Style::RadioType => self | 140 Style::RadioType => self |
142 .data | 141 .data |
143 .as_mut() | 142 .as_mut() |
144 .map(|p| memory::CHeapString::zero(p.cast())), | 143 .map(|p| memory::CHeapString::zero(p.cast())), |
166 // SAFETY: In all cases below, we're creating questions based on | 165 // SAFETY: In all cases below, we're creating questions based on |
167 // known types that we get from PAM and the inner types it should have. | 166 // known types that we get from PAM and the inner types it should have. |
168 let prompt = unsafe { | 167 let prompt = unsafe { |
169 match style { | 168 match style { |
170 Style::PromptEchoOff => { | 169 Style::PromptEchoOff => { |
171 Self::MaskedPrompt(MaskedQAndA::new(question.string_data()?)) | 170 Self::MaskedPrompt(MaskedQAndA::new(question.string_data())) |
172 } | 171 } |
173 Style::PromptEchoOn => Self::Prompt(QAndA::new(question.string_data()?)), | 172 Style::PromptEchoOn => Self::Prompt(QAndA::new(question.string_data())), |
174 Style::ErrorMsg => Self::Error(ErrorMsg::new(question.string_data()?)), | 173 Style::ErrorMsg => Self::Error(ErrorMsg::new(question.string_data())), |
175 Style::TextInfo => Self::Info(InfoMsg::new(question.string_data()?)), | 174 Style::TextInfo => Self::Info(InfoMsg::new(question.string_data())), |
176 #[cfg(feature = "linux-pam-ext")] | 175 #[cfg(feature = "linux-pam-ext")] |
177 Style::RadioType => { | 176 Style::RadioType => { |
178 Self::RadioPrompt(crate::conv::RadioQAndA::new(question.string_data()?)) | 177 Self::RadioPrompt(crate::conv::RadioQAndA::new(question.string_data())) |
179 } | 178 } |
180 #[cfg(feature = "linux-pam-ext")] | 179 #[cfg(feature = "linux-pam-ext")] |
181 Style::BinaryPrompt => { | 180 Style::BinaryPrompt => { |
182 Self::BinaryPrompt(crate::conv::BinaryQAndA::new(question.binary_data())) | 181 Self::BinaryPrompt(crate::conv::BinaryQAndA::new(question.binary_data())) |
183 } | 182 } |
186 Ok(prompt) | 185 Ok(prompt) |
187 } | 186 } |
188 } | 187 } |
189 | 188 |
190 #[cfg(feature = "linux-pam-ext")] | 189 #[cfg(feature = "linux-pam-ext")] |
191 impl From<pammem::TooBigError> for ErrorCode { | 190 impl From<pam_mem::TooBigError> for ErrorCode { |
192 fn from(_: pammem::TooBigError) -> Self { | 191 fn from(_: pam_mem::TooBigError) -> Self { |
193 ErrorCode::BufferError | 192 ErrorCode::BufferError |
194 } | 193 } |
195 } | 194 } |
196 | 195 |
197 #[cfg(test)] | 196 #[cfg(test)] |
217 | 216 |
218 #[test] | 217 #[test] |
219 fn standard() { | 218 fn standard() { |
220 assert_matches!( | 219 assert_matches!( |
221 (Exchange::MaskedPrompt, "hocus pocus"), | 220 (Exchange::MaskedPrompt, "hocus pocus"), |
222 MaskedQAndA::new("hocus pocus") | 221 MaskedQAndA::new("hocus pocus".as_ref()) |
223 ); | 222 ); |
224 assert_matches!((Exchange::Prompt, "what"), QAndA::new("what")); | 223 assert_matches!((Exchange::Prompt, "what"), QAndA::new("what".as_ref())); |
225 assert_matches!((Exchange::Prompt, "who"), QAndA::new("who")); | 224 assert_matches!((Exchange::Prompt, "who"), QAndA::new("who".as_ref())); |
226 assert_matches!((Exchange::Info, "hey"), InfoMsg::new("hey")); | 225 assert_matches!((Exchange::Info, "hey"), InfoMsg::new("hey".as_ref())); |
227 assert_matches!((Exchange::Error, "gasp"), ErrorMsg::new("gasp")); | 226 assert_matches!((Exchange::Error, "gasp"), ErrorMsg::new("gasp".as_ref())); |
228 } | 227 } |
229 | 228 |
230 #[test] | 229 #[test] |
231 #[cfg(feature = "linux-pam-ext")] | 230 #[cfg(feature = "linux-pam-ext")] |
232 fn linux_extensions() { | 231 fn linux_extensions() { |
235 (Exchange::BinaryPrompt, (&[5, 4, 3, 2, 1][..], 66)), | 234 (Exchange::BinaryPrompt, (&[5, 4, 3, 2, 1][..], 66)), |
236 BinaryQAndA::new((&[5, 4, 3, 2, 1], 66)) | 235 BinaryQAndA::new((&[5, 4, 3, 2, 1], 66)) |
237 ); | 236 ); |
238 assert_matches!( | 237 assert_matches!( |
239 (Exchange::RadioPrompt, "you must choose"), | 238 (Exchange::RadioPrompt, "you must choose"), |
240 RadioQAndA::new("you must choose") | 239 RadioQAndA::new("you must choose".as_ref()) |
241 ); | 240 ); |
242 } | 241 } |
243 } | 242 } |