Mercurial > crates > nonstick
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())); |