comparison src/libpam/question.rs @ 108:e97534be35e3

Make some proc macros for doing cfg-like stuff for PAM impls.
author Paul Fisher <paul@pfish.zone>
date Sat, 28 Jun 2025 00:34:45 -0400
parents 49d9e2b5c189
children 178310336596
comparison
equal deleted inserted replaced
107:49c6633f6fd2 108:e97534be35e3
1 //! Data and types dealing with PAM messages. 1 //! Data and types dealing with PAM messages.
2 2
3 #[cfg(feature = "linux-pam-extensions")] 3 #[cfg(feature = "linux-pam-ext")]
4 use crate::conv::{BinaryQAndA, RadioQAndA}; 4 use crate::conv::{BinaryQAndA, RadioQAndA};
5 use crate::conv::{ErrorMsg, InfoMsg, MaskedQAndA, Message, QAndA}; 5 use crate::conv::{ErrorMsg, InfoMsg, MaskedQAndA, Message, QAndA};
6 use crate::libpam::conversation::OwnedMessage; 6 use crate::libpam::conversation::OwnedMessage;
7 use crate::libpam::memory::{CBinaryData, CHeapBox, CHeapString, Immovable}; 7 use crate::libpam::memory::{CBinaryData, CHeapBox, CHeapString, Immovable};
8 use crate::libpam::pam_ffi; 8 use crate::libpam::pam_ffi;
186 /// An error message. 186 /// An error message.
187 ErrorMsg = pam_ffi::PAM_ERROR_MSG, 187 ErrorMsg = pam_ffi::PAM_ERROR_MSG,
188 /// An informational message. 188 /// An informational message.
189 TextInfo = pam_ffi::PAM_TEXT_INFO, 189 TextInfo = pam_ffi::PAM_TEXT_INFO,
190 /// Yes/No/Maybe conditionals. A Linux-PAM extension. 190 /// Yes/No/Maybe conditionals. A Linux-PAM extension.
191 #[cfg(feature = "linux-pam-extensions")] 191 #[cfg(feature = "linux-pam-ext")]
192 RadioType = pam_ffi::PAM_RADIO_TYPE, 192 RadioType = pam_ffi::PAM_RADIO_TYPE,
193 /// For server–client non-human interaction. 193 /// For server–client non-human interaction.
194 /// 194 ///
195 /// NOT part of the X/Open PAM specification. 195 /// NOT part of the X/Open PAM specification.
196 /// A Linux-PAM extension. 196 /// A Linux-PAM extension.
197 #[cfg(feature = "linux-pam-extensions")] 197 #[cfg(feature = "linux-pam-ext")]
198 BinaryPrompt = pam_ffi::PAM_BINARY_PROMPT, 198 BinaryPrompt = pam_ffi::PAM_BINARY_PROMPT,
199 } 199 }
200 200
201 impl Question { 201 impl Question {
202 /// Gets this message's data pointer as a string. 202 /// Gets this message's data pointer as a string.
234 let (style, data): (_, CHeapBox<c_void>) = match *msg { 234 let (style, data): (_, CHeapBox<c_void>) = match *msg {
235 Message::MaskedPrompt(p) => alloc(Style::PromptEchoOff, p.question()), 235 Message::MaskedPrompt(p) => alloc(Style::PromptEchoOff, p.question()),
236 Message::Prompt(p) => alloc(Style::PromptEchoOn, p.question()), 236 Message::Prompt(p) => alloc(Style::PromptEchoOn, p.question()),
237 Message::Error(p) => alloc(Style::ErrorMsg, p.question()), 237 Message::Error(p) => alloc(Style::ErrorMsg, p.question()),
238 Message::Info(p) => alloc(Style::TextInfo, p.question()), 238 Message::Info(p) => alloc(Style::TextInfo, p.question()),
239 #[cfg(feature = "linux-pam-extensions")] 239 #[cfg(feature = "linux-pam-ext")]
240 Message::RadioPrompt(p) => alloc(Style::RadioType, p.question()), 240 Message::RadioPrompt(p) => alloc(Style::RadioType, p.question()),
241 #[cfg(feature = "linux-pam-extensions")] 241 #[cfg(feature = "linux-pam-ext")]
242 Message::BinaryPrompt(p) => Ok((Style::BinaryPrompt, unsafe { 242 Message::BinaryPrompt(p) => Ok((Style::BinaryPrompt, unsafe {
243 CHeapBox::cast(CBinaryData::alloc(p.question())?) 243 CHeapBox::cast(CBinaryData::alloc(p.question())?)
244 })), 244 })),
245 #[cfg(not(feature = "linux-pam-extensions"))] 245 #[cfg(not(feature = "linux-pam-ext"))]
246 Message::RadioPrompt(_) | Message::BinaryPrompt(_) => Err(ErrorCode::ConversationError), 246 Message::RadioPrompt(_) | Message::BinaryPrompt(_) => Err(ErrorCode::ConversationError),
247 }?; 247 }?;
248 Ok(Self { 248 Ok(Self {
249 style: style.into(), 249 style: style.into(),
250 data: Some(data), 250 data: Some(data),
259 unsafe { 259 unsafe {
260 // This is nice-to-have. We'll try to zero out the data 260 // This is nice-to-have. We'll try to zero out the data
261 // in the Question. If it's not a supported format, we skip it. 261 // in the Question. If it's not a supported format, we skip it.
262 if let Ok(style) = Style::try_from(self.style) { 262 if let Ok(style) = Style::try_from(self.style) {
263 let _ = match style { 263 let _ = match style {
264 #[cfg(feature = "linux-pam-extensions")] 264 #[cfg(feature = "linux-pam-ext")]
265 Style::BinaryPrompt => self 265 Style::BinaryPrompt => self
266 .data 266 .data
267 .as_ref() 267 .as_ref()
268 .map(|p| CBinaryData::zero_contents(CHeapBox::as_ptr(p).cast())), 268 .map(|p| CBinaryData::zero_contents(CHeapBox::as_ptr(p).cast())),
269 #[cfg(feature = "linux-pam-extensions")] 269 #[cfg(feature = "linux-pam-ext")]
270 Style::RadioType => self 270 Style::RadioType => self
271 .data 271 .data
272 .as_ref() 272 .as_ref()
273 .map(|p| CHeapString::zero(CHeapBox::as_ptr(p).cast())), 273 .map(|p| CHeapString::zero(CHeapBox::as_ptr(p).cast())),
274 Style::TextInfo 274 Style::TextInfo
299 Self::MaskedPrompt(MaskedQAndA::new(question.string_data()?)) 299 Self::MaskedPrompt(MaskedQAndA::new(question.string_data()?))
300 } 300 }
301 Style::PromptEchoOn => Self::Prompt(QAndA::new(question.string_data()?)), 301 Style::PromptEchoOn => Self::Prompt(QAndA::new(question.string_data()?)),
302 Style::ErrorMsg => Self::Error(ErrorMsg::new(question.string_data()?)), 302 Style::ErrorMsg => Self::Error(ErrorMsg::new(question.string_data()?)),
303 Style::TextInfo => Self::Info(InfoMsg::new(question.string_data()?)), 303 Style::TextInfo => Self::Info(InfoMsg::new(question.string_data()?)),
304 #[cfg(feature = "linux-pam-extensions")] 304 #[cfg(feature = "linux-pam-ext")]
305 Style::RadioType => Self::RadioPrompt(RadioQAndA::new(question.string_data()?)), 305 Style::RadioType => Self::RadioPrompt(RadioQAndA::new(question.string_data()?)),
306 #[cfg(feature = "linux-pam-extensions")] 306 #[cfg(feature = "linux-pam-ext")]
307 Style::BinaryPrompt => Self::BinaryPrompt(BinaryQAndA::new(question.binary_data())), 307 Style::BinaryPrompt => Self::BinaryPrompt(BinaryQAndA::new(question.binary_data())),
308 } 308 }
309 }; 309 };
310 Ok(prompt) 310 Ok(prompt)
311 } 311 }
351 assert_matches!(hey => OwnedMessage::Info, "hey"); 351 assert_matches!(hey => OwnedMessage::Info, "hey");
352 assert_matches!(gasp => OwnedMessage::Error, "gasp"); 352 assert_matches!(gasp => OwnedMessage::Error, "gasp");
353 } 353 }
354 354
355 #[test] 355 #[test]
356 #[cfg(not(feature = "linux-pam-extensions"))] 356 #[cfg(not(feature = "linux-pam-ext"))]
357 fn no_linux_extensions() { 357 fn no_linux_extensions() {
358 use crate::conv::{BinaryQAndA, RadioQAndA}; 358 use crate::conv::{BinaryQAndA, RadioQAndA};
359 <$typ>::new(&[ 359 <$typ>::new(&[
360 BinaryQAndA::new((&[5, 4, 3, 2, 1], 66)).message(), 360 BinaryQAndA::new((&[5, 4, 3, 2, 1], 66)).message(),
361 RadioQAndA::new("you must choose").message(), 361 RadioQAndA::new("you must choose").message(),
362 ]).unwrap_err(); 362 ]).unwrap_err();
363 } 363 }
364 364
365 #[test] 365 #[test]
366 #[cfg(feature = "linux-pam-extensions")] 366 #[cfg(feature = "linux-pam-ext")]
367 fn linux_extensions() { 367 fn linux_extensions() {
368 let interrogation = Box::pin(<$typ>::new(&[ 368 let interrogation = Box::pin(<$typ>::new(&[
369 BinaryQAndA::new((&[5, 4, 3, 2, 1], 66)).message(), 369 BinaryQAndA::new((&[5, 4, 3, 2, 1], 66)).message(),
370 RadioQAndA::new("you must choose").message(), 370 RadioQAndA::new("you must choose").message(),
371 ]).unwrap()); 371 ]).unwrap());