Mercurial > crates > nonstick
comparison src/libpam/handle.rs @ 75:c30811b4afae
rename pam_ffi submodule to libpam.
author | Paul Fisher <paul@pfish.zone> |
---|---|
date | Fri, 06 Jun 2025 22:35:08 -0400 |
parents | src/pam_ffi/handle.rs@c7c596e6388f |
children | 351bdc13005e |
comparison
equal
deleted
inserted
replaced
74:c7c596e6388f | 75:c30811b4afae |
---|---|
1 use super::conversation::LibPamConversation; | |
2 use crate::constants::{ErrorCode, InvalidEnum, Result}; | |
3 use crate::conv::Message; | |
4 use crate::handle::{PamApplicationOnly, PamModuleOnly, PamShared}; | |
5 use crate::libpam::memory; | |
6 use crate::libpam::memory::Immovable; | |
7 use crate::{Conversation, Response}; | |
8 use num_derive::FromPrimitive; | |
9 use num_traits::FromPrimitive; | |
10 use std::ffi::{c_char, c_int}; | |
11 use std::ops::{Deref, DerefMut}; | |
12 use std::result::Result as StdResult; | |
13 use std::{mem, ptr}; | |
14 | |
15 /// An owned PAM handle. | |
16 #[repr(transparent)] | |
17 pub struct OwnedLibPamHandle(*mut LibPamHandle); | |
18 | |
19 /// An opaque structure that a PAM handle points to. | |
20 #[repr(C)] | |
21 pub struct LibPamHandle { | |
22 _data: (), | |
23 _marker: Immovable, | |
24 } | |
25 | |
26 impl LibPamHandle { | |
27 /// Gets a C string item. | |
28 /// | |
29 /// # Safety | |
30 /// | |
31 /// You better be requesting an item which is a C string. | |
32 unsafe fn get_cstr_item(&mut self, item_type: ItemType) -> Result<Option<&str>> { | |
33 let mut output = ptr::null(); | |
34 let ret = unsafe { super::pam_get_item(self, item_type as c_int, &mut output) }; | |
35 ErrorCode::result_from(ret)?; | |
36 memory::wrap_string(output.cast()) | |
37 } | |
38 | |
39 /// Sets a C string item. | |
40 /// | |
41 /// # Safety | |
42 /// | |
43 /// You better be setting an item which is a C string. | |
44 unsafe fn set_cstr_item(&mut self, item_type: ItemType, data: Option<&str>) -> Result<()> { | |
45 let data_str = memory::option_cstr(data)?; | |
46 let ret = unsafe { | |
47 super::pam_set_item( | |
48 self, | |
49 item_type as c_int, | |
50 memory::prompt_ptr(data_str.as_ref()).cast(), | |
51 ) | |
52 }; | |
53 ErrorCode::result_from(ret) | |
54 } | |
55 | |
56 /// Gets the `PAM_CONV` item from the handle. | |
57 fn conversation_item(&mut self) -> Result<&mut LibPamConversation> { | |
58 let output: *mut LibPamConversation = ptr::null_mut(); | |
59 let result = unsafe { | |
60 super::pam_get_item( | |
61 self, | |
62 ItemType::Conversation.into(), | |
63 &mut output.cast_const().cast(), | |
64 ) | |
65 }; | |
66 ErrorCode::result_from(result)?; | |
67 // SAFETY: We got this result from PAM, and we're checking if it's null. | |
68 unsafe { output.as_mut() }.ok_or(ErrorCode::ConversationError) | |
69 } | |
70 } | |
71 | |
72 impl PamApplicationOnly for OwnedLibPamHandle { | |
73 fn close(self, status: Result<()>) -> Result<()> { | |
74 let ret = unsafe { super::pam_end(self.0, ErrorCode::result_to_c(status)) }; | |
75 // Forget rather than dropping, since dropping also calls pam_end. | |
76 mem::forget(self); | |
77 ErrorCode::result_from(ret) | |
78 } | |
79 } | |
80 | |
81 impl Deref for OwnedLibPamHandle { | |
82 type Target = LibPamHandle; | |
83 fn deref(&self) -> &Self::Target { | |
84 unsafe { &*self.0 } | |
85 } | |
86 } | |
87 | |
88 impl DerefMut for OwnedLibPamHandle { | |
89 fn deref_mut(&mut self) -> &mut Self::Target { | |
90 unsafe { &mut *self.0 } | |
91 } | |
92 } | |
93 | |
94 impl Drop for OwnedLibPamHandle { | |
95 /// Ends the PAM session with a zero error code. | |
96 /// You probably want to call [`close`](Self::close) instead of | |
97 /// letting this drop by itself. | |
98 fn drop(&mut self) { | |
99 unsafe { | |
100 super::pam_end(self.0, 0); | |
101 } | |
102 } | |
103 } | |
104 | |
105 macro_rules! cstr_item { | |
106 (get = $getter:ident, item = $item_type:path) => { | |
107 fn $getter(&mut self) -> Result<Option<&str>> { | |
108 unsafe { self.get_cstr_item($item_type) } | |
109 } | |
110 }; | |
111 (set = $setter:ident, item = $item_type:path) => { | |
112 fn $setter(&mut self, value: Option<&str>) -> Result<()> { | |
113 unsafe { self.set_cstr_item($item_type, value) } | |
114 } | |
115 }; | |
116 } | |
117 | |
118 impl PamShared for LibPamHandle { | |
119 fn get_user(&mut self, prompt: Option<&str>) -> Result<&str> { | |
120 let prompt = memory::option_cstr(prompt)?; | |
121 let mut output: *const c_char = ptr::null(); | |
122 let ret = | |
123 unsafe { super::pam_get_user(self, &mut output, memory::prompt_ptr(prompt.as_ref())) }; | |
124 ErrorCode::result_from(ret)?; | |
125 unsafe { memory::wrap_string(output) } | |
126 .transpose() | |
127 .unwrap_or(Err(ErrorCode::ConversationError)) | |
128 } | |
129 | |
130 cstr_item!(get = user_item, item = ItemType::User); | |
131 cstr_item!(set = set_user_item, item = ItemType::User); | |
132 cstr_item!(get = service, item = ItemType::Service); | |
133 cstr_item!(set = set_service, item = ItemType::Service); | |
134 cstr_item!(get = user_prompt, item = ItemType::UserPrompt); | |
135 cstr_item!(set = set_user_prompt, item = ItemType::UserPrompt); | |
136 cstr_item!(get = tty_name, item = ItemType::Tty); | |
137 cstr_item!(set = set_tty_name, item = ItemType::Tty); | |
138 cstr_item!(get = remote_user, item = ItemType::RemoteUser); | |
139 cstr_item!(set = set_remote_user, item = ItemType::RemoteUser); | |
140 cstr_item!(get = remote_host, item = ItemType::RemoteHost); | |
141 cstr_item!(set = set_remote_host, item = ItemType::RemoteHost); | |
142 cstr_item!(set = set_authtok_item, item = ItemType::AuthTok); | |
143 cstr_item!(set = set_old_authtok_item, item = ItemType::OldAuthTok); | |
144 } | |
145 | |
146 impl Conversation for LibPamHandle { | |
147 fn communicate(&mut self, messages: &[Message]) -> Result<Vec<Response>> { | |
148 self.conversation_item()?.communicate(messages) | |
149 } | |
150 } | |
151 | |
152 impl PamModuleOnly for LibPamHandle { | |
153 fn get_authtok(&mut self, prompt: Option<&str>) -> Result<&str> { | |
154 let prompt = memory::option_cstr(prompt)?; | |
155 let mut output: *const c_char = ptr::null_mut(); | |
156 // SAFETY: We're calling this with known-good values. | |
157 let res = unsafe { | |
158 super::pam_get_authtok( | |
159 self, | |
160 ItemType::AuthTok.into(), | |
161 &mut output, | |
162 memory::prompt_ptr(prompt.as_ref()), | |
163 ) | |
164 }; | |
165 ErrorCode::result_from(res)?; | |
166 // SAFETY: We got this string from PAM. | |
167 unsafe { memory::wrap_string(output) } | |
168 .transpose() | |
169 .unwrap_or(Err(ErrorCode::ConversationError)) | |
170 } | |
171 | |
172 cstr_item!(get = authtok_item, item = ItemType::AuthTok); | |
173 cstr_item!(get = old_authtok_item, item = ItemType::OldAuthTok); | |
174 } | |
175 | |
176 /// Function called at the end of a PAM session that is called to clean up | |
177 /// a value previously provided to PAM in a `pam_set_data` call. | |
178 /// | |
179 /// You should never call this yourself. | |
180 extern "C" fn set_data_cleanup<T>(_: *const libc::c_void, c_data: *mut libc::c_void, _: c_int) { | |
181 unsafe { | |
182 let _data: Box<T> = Box::from_raw(c_data.cast()); | |
183 } | |
184 } | |
185 | |
186 /// Identifies what is being gotten or set with `pam_get_item` | |
187 /// or `pam_set_item`. | |
188 #[derive(FromPrimitive)] | |
189 #[repr(i32)] | |
190 #[non_exhaustive] // because C could give us anything! | |
191 pub enum ItemType { | |
192 /// The PAM service name. | |
193 Service = 1, | |
194 /// The user's login name. | |
195 User = 2, | |
196 /// The TTY name. | |
197 Tty = 3, | |
198 /// The remote host (if applicable). | |
199 RemoteHost = 4, | |
200 /// The conversation struct (not a CStr-based item). | |
201 Conversation = 5, | |
202 /// The authentication token (password). | |
203 AuthTok = 6, | |
204 /// The old authentication token (when changing passwords). | |
205 OldAuthTok = 7, | |
206 /// The remote user's name. | |
207 RemoteUser = 8, | |
208 /// The prompt shown when requesting a username. | |
209 UserPrompt = 9, | |
210 /// App-supplied function to override failure delays. | |
211 FailDelay = 10, | |
212 /// X display name. | |
213 XDisplay = 11, | |
214 /// X server authentication data. | |
215 XAuthData = 12, | |
216 /// The type of `pam_get_authtok`. | |
217 AuthTokType = 13, | |
218 } | |
219 | |
220 impl TryFrom<c_int> for ItemType { | |
221 type Error = InvalidEnum<Self>; | |
222 fn try_from(value: c_int) -> StdResult<Self, Self::Error> { | |
223 Self::from_i32(value).ok_or(value.into()) | |
224 } | |
225 } | |
226 | |
227 impl From<ItemType> for c_int { | |
228 fn from(val: ItemType) -> Self { | |
229 val as Self | |
230 } | |
231 } |