comparison src/libpam/conversation.rs @ 130:80c07e5ab22f

Transfer over (almost) completely to using libpam-sys. This reimplements everything in nonstick on top of the new -sys crate. We don't yet use libpam-sys's helpers for binary message payloads. Soon.
author Paul Fisher <paul@pfish.zone>
date Tue, 01 Jul 2025 06:11:43 -0400
parents 94b51fa4f797
children
comparison
equal deleted inserted replaced
129:5b2de52dd8b2 130:80c07e5ab22f
1 use crate::conv::{BinaryQAndA, RadioQAndA}; 1 use crate::conv::{BinaryQAndA, RadioQAndA};
2 use crate::conv::{Conversation, ErrorMsg, InfoMsg, MaskedQAndA, Message, QAndA}; 2 use crate::conv::{Conversation, ErrorMsg, Exchange, InfoMsg, MaskedQAndA, QAndA};
3 use crate::libpam::answer::BinaryAnswer; 3 use crate::libpam::answer::BinaryAnswer;
4 use crate::libpam::answer::{Answer, Answers, TextAnswer}; 4 use crate::libpam::answer::{Answer, Answers, TextAnswer};
5 use crate::libpam::memory::CBinaryData; 5 use crate::libpam::memory::CBinaryData;
6 use crate::libpam::pam_ffi::AppData; 6 use crate::libpam::question::Question;
7 pub use crate::libpam::pam_ffi::LibPamConversation;
8 use crate::libpam::question::QuestionsTrait;
9 use crate::libpam::question::{Question, Questions};
10 use crate::ErrorCode; 7 use crate::ErrorCode;
11 use crate::Result; 8 use crate::Result;
9 use libpam_sys::helpers::PtrPtrVec;
10 use libpam_sys::AppData;
12 use std::ffi::c_int; 11 use std::ffi::c_int;
13 use std::iter; 12 use std::iter;
14 use std::marker::PhantomData; 13 use std::marker::PhantomData;
15 use std::ptr::NonNull; 14 use std::ptr::NonNull;
16 use std::result::Result as StdResult; 15 use std::result::Result as StdResult;
17 16
17 /// The type used by PAM to call back into a conversation.
18 #[repr(C)]
19 pub struct LibPamConversation<'a> {
20 pam_conv: libpam_sys::pam_conv,
21 /// Marker to associate the lifetime of this with the conversation
22 /// that was passed in.
23 pub life: PhantomData<&'a mut ()>,
24 }
25
18 impl LibPamConversation<'_> { 26 impl LibPamConversation<'_> {
19 pub fn wrap<C: Conversation>(conv: &C) -> Self { 27 pub fn wrap<C: Conversation>(conv: &C) -> Self {
20 Self { 28 Self {
21 callback: Self::wrapper_callback::<C>, 29 pam_conv: libpam_sys::pam_conv {
22 appdata: (conv as *const C).cast(), 30 conv: Self::wrapper_callback::<C>,
31 appdata_ptr: (conv as *const C).cast_mut().cast(),
32 },
23 life: PhantomData, 33 life: PhantomData,
24 } 34 }
25 } 35 }
26 36
27 /// Passed as the conversation function into PAM for an owned handle. 37 /// Passed as the conversation function into PAM for an owned handle.
28 /// 38 ///
29 /// PAM calls this, we compute answers, then send them back. 39 /// PAM calls this, we compute answers, then send them back.
30 unsafe extern "C" fn wrapper_callback<C: Conversation>( 40 unsafe extern "C" fn wrapper_callback<C: Conversation>(
31 count: c_int, 41 count: c_int,
32 questions: *const *const Question, 42 questions: *const *const libpam_sys::pam_message,
33 answers: *mut *mut Answer, 43 answers: *mut *mut libpam_sys::pam_response,
34 me: *const AppData, 44 me: *mut AppData,
35 ) -> c_int { 45 ) -> c_int {
36 let internal = || { 46 let internal = || {
37 // Collect all our pointers 47 // Collect all our pointers
38 let conv = me 48 let conv = me
39 .cast::<C>() 49 .cast::<C>()
40 .as_ref() 50 .as_ref()
41 .ok_or(ErrorCode::ConversationError)?; 51 .ok_or(ErrorCode::ConversationError)?;
42 let indirect = Questions::borrow_ptr(questions, count as usize); 52 let q_iter = PtrPtrVec::<Question>::iter_over(questions, count as usize);
43 let answers_ptr = answers.as_mut().ok_or(ErrorCode::ConversationError)?; 53 let answers_ptr = answers.as_mut().ok_or(ErrorCode::ConversationError)?;
44 54
45 // Build our owned list of Q&As from the questions we've been asked 55 // Build our owned list of Q&As from the questions we've been asked
46 let messages: Vec<OwnedMessage> = indirect 56 let messages: Vec<OwnedExchange> = q_iter
47 .map(TryInto::try_into) 57 .map(TryInto::try_into)
48 .collect::<Result<_>>() 58 .collect::<Result<_>>()
49 .map_err(|_| ErrorCode::ConversationError)?; 59 .map_err(|_| ErrorCode::ConversationError)?;
50 // Borrow all those Q&As and ask them. 60 // Borrow all those Q&As and ask them.
51 // If we got an invalid message type, bail before sending. 61 // If we got an invalid message type, bail before sending.
52 let borrowed: Result<Vec<_>> = messages.iter().map(Message::try_from).collect(); 62 let borrowed: Result<Vec<_>> = messages.iter().map(Exchange::try_from).collect();
53 // TODO: Do we want to log something here? 63 // TODO: Do we want to log something here?
54 conv.communicate(&borrowed?); 64 conv.communicate(&borrowed?);
55 65
56 // Send our answers back. 66 // Send our answers back.
57 let owned = Answers::build(messages)?; 67 let owned = Answers::build(messages)?;
58 *answers_ptr = owned.into_ptr().as_ptr(); 68 *answers_ptr = owned.into_ptr();
59 Ok(()) 69 Ok(())
60 }; 70 };
61 ErrorCode::result_to_c(internal()) 71 ErrorCode::result_to_c(internal())
62 } 72 }
63 } 73 }
64 74
65 impl Conversation for LibPamConversation<'_> { 75 impl Conversation for LibPamConversation<'_> {
66 fn communicate(&self, messages: &[Message]) { 76 fn communicate(&self, messages: &[Exchange]) {
67 let internal = || { 77 let internal = || {
68 let questions = Box::pin(Questions::new(messages)?); 78 let questions: Result<_> = messages.iter().map(Question::try_from).collect();
79 let questions = PtrPtrVec::new(questions?);
69 let mut response_pointer = std::ptr::null_mut(); 80 let mut response_pointer = std::ptr::null_mut();
70 // SAFETY: We're calling into PAM with valid everything. 81 // SAFETY: We're calling into PAM with valid everything.
71 let result = unsafe { 82 let result = unsafe {
72 (self.callback)( 83 (self.pam_conv.conv)(
73 messages.len() as c_int, 84 messages.len() as c_int,
74 questions.as_ref().ptr(), 85 questions.as_ptr(),
75 &mut response_pointer, 86 &mut response_pointer,
76 self.appdata, 87 self.pam_conv.appdata_ptr,
77 ) 88 )
78 }; 89 };
79 ErrorCode::result_from(result)?; 90 ErrorCode::result_from(result)?;
80 // SAFETY: This is a pointer we just got back from PAM. 91 // SAFETY: This is a pointer we just got back from PAM.
81 // We have to trust that the responses from PAM match up 92 // We have to trust that the responses from PAM match up
94 messages.iter().for_each(|m| m.set_error(e)) 105 messages.iter().for_each(|m| m.set_error(e))
95 } 106 }
96 } 107 }
97 } 108 }
98 109
99 /// Like [`Message`], but this time we own the contents. 110 /// Like [`Exchange`], but this time we own the contents.
100 #[derive(Debug)] 111 #[derive(Debug)]
101 pub enum OwnedMessage<'a> { 112 pub enum OwnedExchange<'a> {
102 MaskedPrompt(MaskedQAndA<'a>), 113 MaskedPrompt(MaskedQAndA<'a>),
103 Prompt(QAndA<'a>), 114 Prompt(QAndA<'a>),
104 Info(InfoMsg<'a>), 115 Info(InfoMsg<'a>),
105 Error(ErrorMsg<'a>), 116 Error(ErrorMsg<'a>),
106 RadioPrompt(RadioQAndA<'a>), 117 RadioPrompt(RadioQAndA<'a>),
107 BinaryPrompt(BinaryQAndA<'a>), 118 BinaryPrompt(BinaryQAndA<'a>),
108 } 119 }
109 120
110 impl<'a> TryFrom<&'a OwnedMessage<'a>> for Message<'a> { 121 impl<'a> TryFrom<&'a OwnedExchange<'a>> for Exchange<'a> {
111 type Error = ErrorCode; 122 type Error = ErrorCode;
112 fn try_from(src: &'a OwnedMessage) -> StdResult<Self, ErrorCode> { 123 fn try_from(src: &'a OwnedExchange) -> StdResult<Self, ErrorCode> {
113 match src { 124 match src {
114 OwnedMessage::MaskedPrompt(m) => Ok(Message::MaskedPrompt(m)), 125 OwnedExchange::MaskedPrompt(m) => Ok(Exchange::MaskedPrompt(m)),
115 OwnedMessage::Prompt(m) => Ok(Message::Prompt(m)), 126 OwnedExchange::Prompt(m) => Ok(Exchange::Prompt(m)),
116 OwnedMessage::Info(m) => Ok(Message::Info(m)), 127 OwnedExchange::Info(m) => Ok(Exchange::Info(m)),
117 OwnedMessage::Error(m) => Ok(Message::Error(m)), 128 OwnedExchange::Error(m) => Ok(Exchange::Error(m)),
118 OwnedMessage::RadioPrompt(m) => Ok(Message::RadioPrompt(m)), 129 OwnedExchange::RadioPrompt(m) => Ok(Exchange::RadioPrompt(m)),
119 OwnedMessage::BinaryPrompt(m) => Ok(Message::BinaryPrompt(m)), 130 OwnedExchange::BinaryPrompt(m) => Ok(Exchange::BinaryPrompt(m)),
120 } 131 }
121 } 132 }
122 } 133 }
123 134
124 /// Fills in the answer of the Message with the given response. 135 /// Fills in the answer of the Message with the given response.
125 /// 136 ///
126 /// # Safety 137 /// # Safety
127 /// 138 ///
128 /// You are responsible for ensuring that the src-dst pair matches. 139 /// You are responsible for ensuring that the src-dst pair matches.
129 unsafe fn convert(msg: &Message, resp: &mut Answer) { 140 unsafe fn convert(msg: &Exchange, resp: &mut Answer) {
130 macro_rules! fill_text { 141 macro_rules! fill_text {
131 ($dst:ident, $src:ident) => {{ 142 ($dst:ident, $src:ident) => {{
132 let text_resp = unsafe { TextAnswer::upcast($src) }; 143 let text_resp = unsafe { TextAnswer::upcast($src) };
133 $dst.set_answer(text_resp.contents().map(Into::into)); 144 $dst.set_answer(text_resp.contents().map(Into::into));
134 }}; 145 }};
135 } 146 }
136 match *msg { 147 match *msg {
137 Message::MaskedPrompt(qa) => fill_text!(qa, resp), 148 Exchange::MaskedPrompt(qa) => fill_text!(qa, resp),
138 Message::Prompt(qa) => fill_text!(qa, resp), 149 Exchange::Prompt(qa) => fill_text!(qa, resp),
139 Message::Error(m) => m.set_answer(Ok(())), 150 Exchange::Error(m) => m.set_answer(Ok(())),
140 Message::Info(m) => m.set_answer(Ok(())), 151 Exchange::Info(m) => m.set_answer(Ok(())),
141 Message::RadioPrompt(qa) => fill_text!(qa, resp), 152 Exchange::RadioPrompt(qa) => fill_text!(qa, resp),
142 Message::BinaryPrompt(qa) => { 153 Exchange::BinaryPrompt(qa) => {
143 let bin_resp = unsafe { BinaryAnswer::upcast(resp) }; 154 let bin_resp = unsafe { BinaryAnswer::upcast(resp) };
144 qa.set_answer(Ok(bin_resp 155 qa.set_answer(Ok(bin_resp
145 .data() 156 .data()
146 .map(|d| unsafe { CBinaryData::as_binary_data(d) }) 157 .map(|d| unsafe { CBinaryData::as_binary_data(d) })
147 .unwrap_or_default())); 158 .unwrap_or_default()));