Mercurial > crates > nonstick
comparison src/libpam/handle.rs @ 141:a508a69c068a
Remove a lot of Results from functions.
Many functions are documented to only return failing Results when given
improper inputs or when there is a memory allocation failure (which
can be verified by looking at the source). In cases where we know our
input is correct, we don't need to check for memory allocation errors
for the same reason that Rust doesn't do so when you, e.g., create a
new Vec.
author | Paul Fisher <paul@pfish.zone> |
---|---|
date | Sat, 05 Jul 2025 17:16:56 -0400 |
parents | 6c1e1bdb4164 |
children | ebb71a412b58 |
comparison
equal
deleted
inserted
replaced
140:add7228adb2f | 141:a508a69c068a |
---|---|
1 use super::conversation::LibPamConversation; | 1 use super::conversation::{OwnedConversation, PamConv}; |
2 use crate::constants::{ErrorCode, Result}; | 2 use crate::constants::{ErrorCode, Result}; |
3 use crate::conv::Exchange; | 3 use crate::conv::Exchange; |
4 use crate::environ::EnvironMapMut; | 4 use crate::environ::EnvironMapMut; |
5 use crate::handle::PamShared; | 5 use crate::handle::PamShared; |
6 use crate::libpam::environ::{LibPamEnviron, LibPamEnvironMut}; | 6 use crate::libpam::environ::{LibPamEnviron, LibPamEnvironMut}; |
8 use crate::logging::{Level, Location}; | 8 use crate::logging::{Level, Location}; |
9 use crate::{ | 9 use crate::{ |
10 guide, linklist, stdlinks, Conversation, EnvironMap, Flags, PamHandleApplication, | 10 guide, linklist, stdlinks, Conversation, EnvironMap, Flags, PamHandleApplication, |
11 PamHandleModule, | 11 PamHandleModule, |
12 }; | 12 }; |
13 use libpam_sys_helpers::constants; | |
13 use num_enum::{IntoPrimitive, TryFromPrimitive}; | 14 use num_enum::{IntoPrimitive, TryFromPrimitive}; |
14 use std::cell::Cell; | 15 use std::cell::Cell; |
15 use std::ffi::{c_char, c_int, CString}; | 16 use std::ffi::{c_char, c_int, CString}; |
16 | 17 use std::mem::ManuallyDrop; |
17 use std::ptr; | 18 use std::ptr; |
18 use std::ptr::NonNull; | 19 use std::ptr::NonNull; |
19 | 20 |
20 /// Owner for a PAM handle. | |
21 pub struct LibPamHandle(pub NonNull<libpam_sys::pam_handle>); | |
22 | |
23 impl AsRef<libpam_sys::pam_handle> for LibPamHandle { | |
24 fn as_ref(&self) -> &libpam_sys::pam_handle { | |
25 unsafe { self.0.as_ref() } | |
26 } | |
27 } | |
28 | |
29 impl AsMut<libpam_sys::pam_handle> for LibPamHandle { | |
30 fn as_mut(&mut self) -> &mut libpam_sys::pam_handle { | |
31 unsafe { self.0.as_mut() } | |
32 } | |
33 } | |
34 | |
35 /// An owned PAM handle. | 21 /// An owned PAM handle. |
36 pub struct OwnedLibPamHandle<'a> { | 22 pub struct OwnedLibPamHandle<C: Conversation> { |
37 /// The handle itself. | 23 /// The handle itself. |
38 handle: LibPamHandle, | 24 handle: ManuallyDrop<RawPamHandle>, |
39 /// The last return value from the handle. | 25 /// The last return value from the handle. |
40 last_return: Cell<Result<()>>, | 26 last_return: Cell<Result<()>>, |
41 /// If set, the Conversation that this PAM handle owns. | 27 /// If set, the Conversation that this PAM handle owns. |
42 /// | 28 /// |
43 /// We have to hold on to this because the PAM specification doesn't | 29 /// We have to hold on to this because the PAM specification doesn't |
44 /// actually say what the PAM library should do with a passed-in | 30 /// actually say what the PAM library should do with a passed-in |
45 /// conversation. Linux-PAM copies the contents of the `pam_conv` struct | 31 /// conversation. Linux-PAM copies the contents of the `pam_conv` struct |
46 /// that you pass in to `pam_start`, but OpenPAM uses the pointer itself, | 32 /// that you pass in to `pam_start`, but OpenPAM uses the pointer itself, |
47 /// so you have to keep it in one place. | 33 /// so you have to keep it in one place. |
48 conversation: Option<Box<LibPamConversation<'a>>>, | 34 conversation: Box<OwnedConversation<C>>, |
49 } | 35 } |
50 | 36 |
51 #[derive(Debug, PartialEq)] | 37 #[derive(Debug, PartialEq)] |
52 pub struct HandleBuilder { | 38 pub struct HandleBuilder { |
53 service_name: String, | 39 service_name: String, |
66 pub fn username(mut self, username: String) -> Self { | 52 pub fn username(mut self, username: String) -> Self { |
67 self.username = Some(username); | 53 self.username = Some(username); |
68 self | 54 self |
69 } | 55 } |
70 /// Builds a PAM handle and starts the transaction. | 56 /// Builds a PAM handle and starts the transaction. |
71 pub fn build(self, conv: &impl Conversation) -> Result<OwnedLibPamHandle> { | 57 pub fn build(self, conv: impl Conversation) -> Result<OwnedLibPamHandle<impl Conversation>> { |
72 OwnedLibPamHandle::start(self.service_name, self.username, conv) | 58 OwnedLibPamHandle::start(self.service_name, self.username, conv) |
73 } | 59 } |
74 } | 60 } |
75 | 61 |
76 impl OwnedLibPamHandle<'_> { | 62 impl<C: Conversation> OwnedLibPamHandle<C> { |
77 /// Creates a builder to start a PAM transaction for the given service. | 63 /// Creates a builder to start a PAM transaction for the given service. |
78 /// | 64 /// |
79 /// The service name is what controls the steps and checks PAM goes through | 65 /// The service name is what controls the steps and checks PAM goes through |
80 /// when authenticating a user. This corresponds to the configuration file | 66 /// when authenticating a user. This corresponds to the configuration file |
81 /// named <code>/etc/pam.d/<var>service_name</var></code>. | 67 /// named <code>/etc/pam.d/<var>service_name</var></code>. |
90 service_name, | 76 service_name, |
91 username: None, | 77 username: None, |
92 } | 78 } |
93 } | 79 } |
94 | 80 |
95 fn start( | 81 fn start(service_name: String, username: Option<String>, conversation: C) -> Result<Self> { |
96 service_name: String, | 82 let conv = Box::new(OwnedConversation::new(conversation)); |
97 username: Option<String>, | |
98 conversation: &impl Conversation, | |
99 ) -> Result<Self> { | |
100 let conv = Box::new(LibPamConversation::wrap(conversation)); | |
101 let service_cstr = CString::new(service_name).map_err(|_| ErrorCode::ConversationError)?; | 83 let service_cstr = CString::new(service_name).map_err(|_| ErrorCode::ConversationError)?; |
102 let username_cstr = memory::prompt_ptr(memory::option_cstr(username.as_deref())?.as_ref()); | 84 let username_cstr = memory::prompt_ptr(memory::option_cstr(username.as_deref())?.as_ref()); |
103 | 85 |
104 let mut handle: *mut libpam_sys::pam_handle = ptr::null_mut(); | 86 let mut handle: *mut libpam_sys::pam_handle = ptr::null_mut(); |
105 // SAFETY: We've set everything up properly to call `pam_start`. | 87 // SAFETY: We've set everything up properly to call `pam_start`. |
106 // The returned value will be a valid pointer provided the result is OK. | 88 // The returned value will be a valid pointer provided the result is OK. |
107 let result = unsafe { | 89 let result = unsafe { |
108 libpam_sys::pam_start( | 90 libpam_sys::pam_start( |
109 service_cstr.as_ptr(), | 91 service_cstr.as_ptr(), |
110 username_cstr, | 92 username_cstr, |
111 (conv.as_ref() as *const LibPamConversation) | 93 (conv.as_ref() as *const OwnedConversation<C>) |
112 .cast_mut() | 94 .cast_mut() |
113 .cast(), | 95 .cast(), |
114 &mut handle, | 96 &mut handle, |
115 ) | 97 ) |
116 }; | 98 }; |
117 ErrorCode::result_from(result)?; | 99 ErrorCode::result_from(result)?; |
118 let handle = NonNull::new(handle).ok_or(ErrorCode::BufferError)?; | 100 let handle = NonNull::new(handle).ok_or(ErrorCode::BufferError)?; |
119 Ok(Self { | 101 Ok(Self { |
120 handle: LibPamHandle(handle), | 102 handle: ManuallyDrop::new(RawPamHandle(handle)), |
121 last_return: Cell::new(Ok(())), | 103 last_return: Cell::new(Ok(())), |
122 conversation: Some(conv), | 104 conversation: conv, |
123 }) | 105 }) |
124 } | 106 } |
125 } | 107 |
126 | 108 /// "Quietly" closes the PAM session on an owned PAM handle. |
127 impl PamHandleApplication for OwnedLibPamHandle<'_> { | 109 /// |
128 fn authenticate(&mut self, flags: Flags) -> Result<()> { | 110 /// This internally calls `pam_end` with the appropriate error code. |
129 let ret = | 111 /// |
130 unsafe { libpam_sys::pam_authenticate(self.handle.as_mut(), flags.bits() as c_int) }; | 112 /// # References |
131 let result = ErrorCode::result_from(ret); | 113 #[doc = linklist!(pam_end: adg, _std)] |
132 self.last_return.set(result); | 114 /// |
133 result | 115 #[doc = guide!(adg: "adg-interface-by-app-expected.html#adg-pam_end")] |
134 } | 116 #[doc = stdlinks!(3 pam_end)] |
135 | 117 |
136 fn account_management(&mut self, flags: Flags) -> Result<()> { | 118 fn end_quiet(self) {} |
137 let ret = unsafe { libpam_sys::pam_acct_mgmt(self.handle.as_mut(), flags.bits() as c_int) }; | 119 } |
138 let result = ErrorCode::result_from(ret); | 120 |
139 self.last_return.set(result); | 121 macro_rules! wrap { |
140 result | 122 (fn $name:ident { $pam_func:ident }) => { |
141 } | 123 fn $name(&mut self, flags: Flags) -> Result<()> { |
142 | 124 ErrorCode::result_from(unsafe { libpam_sys::$pam_func(self.0.as_mut(), flags.bits()) }) |
143 fn change_authtok(&mut self, flags: Flags) -> Result<()> { | 125 } |
144 let ret = unsafe { libpam_sys::pam_chauthtok(self.handle.as_mut(), flags.bits() as c_int) }; | 126 }; |
145 let result = ErrorCode::result_from(ret); | 127 } |
146 self.last_return.set(result); | 128 |
147 result | 129 impl PamHandleApplication for RawPamHandle { |
148 } | 130 wrap!(fn authenticate { pam_authenticate }); |
131 wrap!(fn account_management { pam_acct_mgmt }); | |
132 wrap!(fn change_authtok { pam_chauthtok }); | |
149 } | 133 } |
150 | 134 |
151 // TODO: pam_authenticate - app | 135 // TODO: pam_authenticate - app |
152 // pam_setcred - app | 136 // pam_setcred - app |
153 // pam_acct_mgmt - app | 137 // pam_acct_mgmt - app |
156 // pam_close_session - app | 140 // pam_close_session - app |
157 // pam_putenv - shared | 141 // pam_putenv - shared |
158 // pam_getenv - shared | 142 // pam_getenv - shared |
159 // pam_getenvlist - shared | 143 // pam_getenvlist - shared |
160 | 144 |
161 impl Drop for OwnedLibPamHandle<'_> { | 145 impl<C: Conversation> Drop for OwnedLibPamHandle<C> { |
162 /// Closes the PAM session on an owned PAM handle. | 146 /// Closes the PAM session on an owned PAM handle. |
163 /// | 147 /// |
164 /// This internally calls `pam_end` with the appropriate error code. | 148 /// This internally calls `pam_end` with the appropriate error code. |
165 /// | 149 /// |
166 /// # References | 150 /// # References |
169 #[doc = guide!(adg: "adg-interface-by-app-expected.html#adg-pam_end")] | 153 #[doc = guide!(adg: "adg-interface-by-app-expected.html#adg-pam_end")] |
170 #[doc = stdlinks!(3 pam_end)] | 154 #[doc = stdlinks!(3 pam_end)] |
171 fn drop(&mut self) { | 155 fn drop(&mut self) { |
172 unsafe { | 156 unsafe { |
173 libpam_sys::pam_end( | 157 libpam_sys::pam_end( |
174 self.handle.as_mut(), | 158 self.handle.raw_mut(), |
175 ErrorCode::result_to_c(self.last_return.get()), | 159 ErrorCode::result_to_c(self.last_return.get()), |
176 ); | 160 ); |
177 } | 161 } |
178 } | 162 } |
163 } | |
164 | |
165 macro_rules! delegate { | |
166 // First have the kind that save the result after delegation. | |
167 (fn $meth:ident(&self $(, $param:ident: $typ:ty)*) -> Result<$ret:ty>) => { | |
168 fn $meth(&self $(, $param: $typ)*) -> Result<$ret> { | |
169 let result = self.handle.$meth($($param),*); | |
170 self.last_return.set(split(&result)); | |
171 result | |
172 } | |
173 }; | |
174 (fn $meth:ident(&mut self $(, $param:ident: $typ:ty)*) -> Result<$ret:ty>) => { | |
175 fn $meth(&mut self $(, $param: $typ)*) -> Result<$ret> { | |
176 let result = self.handle.$meth($($param),*); | |
177 self.last_return.set(split(&result)); | |
178 result | |
179 } | |
180 }; | |
181 // Then have the kind that are just raw delegates | |
182 (fn $meth:ident(&self $(, $param:ident: $typ:ty)*) -> $ret:ty) => { | |
183 fn $meth(&self $(, $param: $typ)*) -> $ret { | |
184 self.handle.$meth($($param),*) | |
185 } | |
186 }; | |
187 (fn $meth:ident(&mut self $(, $param:ident: $typ:ty)*) -> $ret:ty) => { | |
188 fn $meth(&mut self $(, $param: $typ)*) -> $ret { | |
189 self.handle.$meth($($param),*) | |
190 } | |
191 }; | |
192 // Then have item getters / setters | |
193 (get = $get:ident$(, set = $set:ident)?) => { | |
194 delegate!(fn $get(&self) -> Result<Option<String>>); | |
195 $(delegate!(set = $set);)? | |
196 }; | |
197 (set = $set:ident) => { | |
198 delegate!(fn $set(&mut self, value: Option<&str>) -> Result<()>); | |
199 }; | |
200 } | |
201 | |
202 fn split<T>(result: &Result<T>) -> Result<()> { | |
203 result.as_ref().map(drop).map_err(|&e| e) | |
204 } | |
205 | |
206 impl<C: Conversation> PamShared for OwnedLibPamHandle<C> { | |
207 delegate!(fn log(&self, level: Level, location: Location<'_>, entry: &str) -> ()); | |
208 delegate!(fn environ(&self) -> impl EnvironMap); | |
209 delegate!(fn environ_mut(&mut self) -> impl EnvironMapMut); | |
210 delegate!(fn username(&mut self, prompt: Option<&str>) -> Result<String>); | |
211 delegate!(get = user_item, set = set_user_item); | |
212 delegate!(get = service, set = set_service); | |
213 delegate!(get = user_prompt, set = set_user_prompt); | |
214 delegate!(get = tty_name, set = set_tty_name); | |
215 delegate!(get = remote_user, set = set_remote_user); | |
216 delegate!(get = remote_host, set = set_remote_host); | |
217 delegate!(set = set_authtok_item); | |
218 delegate!(set = set_old_authtok_item); | |
179 } | 219 } |
180 | 220 |
181 /// Macro to implement getting/setting a CStr-based item. | 221 /// Macro to implement getting/setting a CStr-based item. |
182 macro_rules! cstr_item { | 222 macro_rules! cstr_item { |
183 (get = $getter:ident, item = $item_type:path) => { | 223 (get = $getter:ident, item = $item_type:path) => { |
190 unsafe { self.set_cstr_item($item_type, value) } | 230 unsafe { self.set_cstr_item($item_type, value) } |
191 } | 231 } |
192 }; | 232 }; |
193 } | 233 } |
194 | 234 |
195 impl PamShared for LibPamHandle { | 235 /// An owned variation of a basic PAM handle. |
236 /// | |
237 /// This is the most basic version of a wrapped PAM handle. It's mostly used | |
238 /// as the inside of the [`OwnedLibPamHandle`], but can also be used to "adopt" | |
239 /// a PAM handle created by another library. | |
240 /// | |
241 /// If [`Self::end`] is not called, this will always call `pam_end` reporting | |
242 /// successful completion. | |
243 pub struct RawPamHandle(NonNull<libpam_sys::pam_handle>); | |
244 | |
245 impl RawPamHandle { | |
246 /// Takes ownership of the pointer to the given PAM handle. | |
247 /// | |
248 /// **Do not use this just to get a reference to a PAM handle.** | |
249 /// | |
250 /// # Safety | |
251 /// | |
252 /// - The pointer must point to a valid PAM handle. | |
253 /// - The conversation associated with the handle must remain valid | |
254 /// for as long as the handle is open. | |
255 pub unsafe fn from_ptr(handle: NonNull<libpam_sys::pam_handle>) -> Self { | |
256 Self(handle) | |
257 } | |
258 | |
259 /// Ends the transaction, reporting `error_code` to cleanup callbacks. | |
260 /// | |
261 /// # References | |
262 #[doc = linklist!(pam_end: adg, _std)] | |
263 /// | |
264 #[doc = guide!(adg: "adg-interface-by-app-expected.html#adg-pam_end")] | |
265 #[doc = stdlinks!(3 pam_end)] | |
266 pub fn end(self, result: Result<()>) { | |
267 let mut me = ManuallyDrop::new(self); | |
268 unsafe { libpam_sys::pam_end(me.raw_mut(), ErrorCode::result_to_c(result)) }; | |
269 } | |
270 | |
271 #[cfg_attr( | |
272 not(pam_impl = "LinuxPam"), | |
273 doc = "Exactly equivalent to [`Self::end`], except on Linux-PAM." | |
274 )] | |
275 #[cfg_attr( | |
276 pam_impl = "LinuxPam", | |
277 doc = "Ends the transaction \"quietly\", reporting `error_code` to cleanup callbacks." | |
278 )] | |
279 /// | |
280 /// On Linux-PAM only, this sets the | |
281 /// [`PAM_DATA_SILENT`](libpam_sys::PAM_DATA_SILENT) flag on the flags | |
282 /// passed to the cleanup callbacks. This conventionally means that this | |
283 /// `pam_end` call is occurring on a forked process, and that a session | |
284 /// may still be open on the parent process, and modules "should not treat | |
285 /// the call too seriously". | |
286 /// | |
287 /// # References | |
288 #[doc = linklist!(pam_end: adg, _std)] | |
289 /// | |
290 #[doc = guide!(adg: "adg-interface-by-app-expected.html#adg-pam_end")] | |
291 #[doc = stdlinks!(3 pam_end)] | |
292 pub fn end_quiet(self, result: Result<()>) { | |
293 let mut me = ManuallyDrop::new(self); | |
294 let result = ErrorCode::result_to_c(result); | |
295 #[cfg(pam_impl = "LinuxPam")] | |
296 let result = result | libpam_sys::PAM_DATA_SILENT; | |
297 unsafe { | |
298 libpam_sys::pam_end(me.raw_mut(), result); | |
299 } | |
300 } | |
301 | |
302 /// Consumes this and gives you back the raw PAM handle. | |
303 pub fn into_inner(self) -> NonNull<libpam_sys::pam_handle> { | |
304 let me = ManuallyDrop::new(self); | |
305 me.0 | |
306 } | |
307 | |
308 /// Gets a reference to the inner PAM handle. | |
309 pub fn raw_ref(&self) -> &libpam_sys::pam_handle { | |
310 unsafe { self.0.as_ref() } | |
311 } | |
312 /// Gets a mutable reference to the inner PAM handle. | |
313 pub fn raw_mut(&mut self) -> &mut libpam_sys::pam_handle { | |
314 unsafe { self.0.as_mut() } | |
315 } | |
316 } | |
317 | |
318 impl Drop for RawPamHandle { | |
319 fn drop(&mut self) { | |
320 unsafe { libpam_sys::pam_end(self.0.as_mut(), 0) }; | |
321 } | |
322 } | |
323 | |
324 impl PamShared for RawPamHandle { | |
196 #[cfg(any())] | 325 #[cfg(any())] |
197 fn log(&self, level: Level, loc: Location<'_>, entry: &str) { | 326 fn log(&self, level: Level, loc: Location<'_>, entry: &str) { |
198 let entry = match CString::new(entry).or_else(|_| CString::new(dbg!(entry))) { | 327 let entry = match CString::new(entry).or_else(|_| CString::new(dbg!(entry))) { |
199 Ok(cstr) => cstr, | 328 Ok(cstr) => cstr, |
200 _ => return, | 329 _ => return, |
227 fn username(&mut self, prompt: Option<&str>) -> Result<String> { | 356 fn username(&mut self, prompt: Option<&str>) -> Result<String> { |
228 let prompt = memory::option_cstr(prompt)?; | 357 let prompt = memory::option_cstr(prompt)?; |
229 let mut output: *const c_char = ptr::null(); | 358 let mut output: *const c_char = ptr::null(); |
230 let ret = unsafe { | 359 let ret = unsafe { |
231 libpam_sys::pam_get_user( | 360 libpam_sys::pam_get_user( |
232 self.as_mut(), | 361 self.raw_mut(), |
233 &mut output, | 362 &mut output, |
234 memory::prompt_ptr(prompt.as_ref()), | 363 memory::prompt_ptr(prompt.as_ref()), |
235 ) | 364 ) |
236 }; | 365 }; |
237 ErrorCode::result_from(ret)?; | 366 ErrorCode::result_from(ret)?; |
262 cstr_item!(set = set_remote_host, item = ItemType::RemoteHost); | 391 cstr_item!(set = set_remote_host, item = ItemType::RemoteHost); |
263 cstr_item!(set = set_authtok_item, item = ItemType::AuthTok); | 392 cstr_item!(set = set_authtok_item, item = ItemType::AuthTok); |
264 cstr_item!(set = set_old_authtok_item, item = ItemType::OldAuthTok); | 393 cstr_item!(set = set_old_authtok_item, item = ItemType::OldAuthTok); |
265 } | 394 } |
266 | 395 |
267 impl Conversation for LibPamHandle { | 396 impl Conversation for RawPamHandle { |
268 fn communicate(&self, messages: &[Exchange]) { | 397 fn communicate(&self, messages: &[Exchange]) { |
269 match self.conversation_item() { | 398 match self.conversation_item() { |
270 Ok(conv) => conv.communicate(messages), | 399 Ok(conv) => conv.communicate(messages), |
271 Err(e) => { | 400 Err(e) => { |
272 for msg in messages { | 401 for msg in messages { |
275 } | 404 } |
276 } | 405 } |
277 } | 406 } |
278 } | 407 } |
279 | 408 |
280 impl PamHandleModule for LibPamHandle { | 409 impl PamHandleModule for RawPamHandle { |
410 fn authtok(&mut self, prompt: Option<&str>) -> Result<String> { | |
411 self.get_authtok(prompt, ItemType::AuthTok) | |
412 } | |
413 | |
414 fn old_authtok(&mut self, prompt: Option<&str>) -> Result<String> { | |
415 self.get_authtok(prompt, ItemType::OldAuthTok) | |
416 } | |
417 | |
418 cstr_item!(get = authtok_item, item = ItemType::AuthTok); | |
419 cstr_item!(get = old_authtok_item, item = ItemType::OldAuthTok); | |
420 } | |
421 | |
422 /// Function called at the end of a PAM session that is called to clean up | |
423 /// a value previously provided to PAM in a `pam_set_data` call. | |
424 /// | |
425 /// You should never call this yourself. | |
426 extern "C" fn set_data_cleanup<T>(_: *const libc::c_void, c_data: *mut libc::c_void, _: c_int) { | |
427 unsafe { | |
428 let _data: Box<T> = Box::from_raw(c_data.cast()); | |
429 } | |
430 } | |
431 | |
432 // Implementations of internal functions. | |
433 impl RawPamHandle { | |
281 #[cfg(any(pam_impl = "LinuxPam", pam_impl = "OpenPam"))] | 434 #[cfg(any(pam_impl = "LinuxPam", pam_impl = "OpenPam"))] |
282 fn authtok(&mut self, prompt: Option<&str>) -> Result<String> { | 435 fn get_authtok(&mut self, prompt: Option<&str>, item_type: ItemType) -> Result<String> { |
283 let prompt = memory::option_cstr(prompt)?; | 436 let prompt = memory::option_cstr(prompt)?; |
284 let mut output: *const c_char = ptr::null_mut(); | 437 let mut output: *const c_char = ptr::null_mut(); |
285 // SAFETY: We're calling this with known-good values. | 438 // SAFETY: We're calling this with known-good values. |
286 let res = unsafe { | 439 let res = unsafe { |
287 libpam_sys::pam_get_authtok( | 440 libpam_sys::pam_get_authtok( |
288 self.as_mut(), | 441 self.raw_mut(), |
289 ItemType::AuthTok.into(), | 442 item_type.into(), |
290 &mut output, | 443 &mut output, |
291 memory::prompt_ptr(prompt.as_ref()), | 444 memory::prompt_ptr(prompt.as_ref()), |
292 ) | 445 ) |
293 }; | 446 }; |
294 ErrorCode::result_from(res)?; | 447 ErrorCode::result_from(res)?; |
297 .transpose() | 450 .transpose() |
298 .unwrap_or(Err(ErrorCode::ConversationError)) | 451 .unwrap_or(Err(ErrorCode::ConversationError)) |
299 } | 452 } |
300 | 453 |
301 #[cfg(not(any(pam_impl = "LinuxPam", pam_impl = "OpenPam")))] | 454 #[cfg(not(any(pam_impl = "LinuxPam", pam_impl = "OpenPam")))] |
302 fn authtok(&mut self, prompt: Option<&str>) -> Result<String> { | 455 fn get_authtok(&mut self, prompt: Option<&str>, item_type: ItemType) -> Result<String> { |
303 Err(ErrorCode::ConversationError) | 456 Err(ErrorCode::ConversationError) |
304 } | 457 } |
305 | 458 |
306 cstr_item!(get = authtok_item, item = ItemType::AuthTok); | |
307 cstr_item!(get = old_authtok_item, item = ItemType::OldAuthTok); | |
308 } | |
309 | |
310 /// Function called at the end of a PAM session that is called to clean up | |
311 /// a value previously provided to PAM in a `pam_set_data` call. | |
312 /// | |
313 /// You should never call this yourself. | |
314 extern "C" fn set_data_cleanup<T>(_: *const libc::c_void, c_data: *mut libc::c_void, _: c_int) { | |
315 unsafe { | |
316 let _data: Box<T> = Box::from_raw(c_data.cast()); | |
317 } | |
318 } | |
319 | |
320 impl LibPamHandle { | |
321 /// Gets a C string item. | 459 /// Gets a C string item. |
322 /// | 460 /// |
323 /// # Safety | 461 /// # Safety |
324 /// | 462 /// |
325 /// You better be requesting an item which is a C string. | 463 /// You better be requesting an item which is a C string. |
326 unsafe fn get_cstr_item(&self, item_type: ItemType) -> Result<Option<String>> { | 464 unsafe fn get_cstr_item(&self, item_type: ItemType) -> Result<Option<String>> { |
327 let mut output = ptr::null(); | 465 let mut output = ptr::null(); |
328 let ret = | 466 let ret = |
329 unsafe { libpam_sys::pam_get_item(self.as_ref(), item_type as c_int, &mut output) }; | 467 unsafe { libpam_sys::pam_get_item(self.raw_ref(), item_type as c_int, &mut output) }; |
330 ErrorCode::result_from(ret)?; | 468 ErrorCode::result_from(ret)?; |
331 memory::copy_pam_string(output.cast()) | 469 memory::copy_pam_string(output.cast()) |
332 } | 470 } |
333 | 471 |
334 /// Sets a C string item. | 472 /// Sets a C string item. |
338 /// You better be setting an item which is a C string. | 476 /// You better be setting an item which is a C string. |
339 unsafe fn set_cstr_item(&mut self, item_type: ItemType, data: Option<&str>) -> Result<()> { | 477 unsafe fn set_cstr_item(&mut self, item_type: ItemType, data: Option<&str>) -> Result<()> { |
340 let data_str = memory::option_cstr(data)?; | 478 let data_str = memory::option_cstr(data)?; |
341 let ret = unsafe { | 479 let ret = unsafe { |
342 libpam_sys::pam_set_item( | 480 libpam_sys::pam_set_item( |
343 self.as_mut(), | 481 self.raw_mut(), |
344 item_type as c_int, | 482 item_type as c_int, |
345 memory::prompt_ptr(data_str.as_ref()).cast(), | 483 memory::prompt_ptr(data_str.as_ref()).cast(), |
346 ) | 484 ) |
347 }; | 485 }; |
348 ErrorCode::result_from(ret) | 486 ErrorCode::result_from(ret) |
349 } | 487 } |
350 | 488 |
351 /// Gets the `PAM_CONV` item from the handle. | 489 /// Gets the `PAM_CONV` item from the handle. |
352 fn conversation_item(&self) -> Result<&mut LibPamConversation<'_>> { | 490 fn conversation_item(&self) -> Result<&PamConv> { |
353 let output: *mut LibPamConversation = ptr::null_mut(); | 491 let output: *const PamConv = ptr::null_mut(); |
354 let result = unsafe { | 492 let result = unsafe { |
355 libpam_sys::pam_get_item( | 493 libpam_sys::pam_get_item( |
356 self.as_ref(), | 494 self.raw_ref(), |
357 ItemType::Conversation.into(), | 495 ItemType::Conversation.into(), |
358 &mut output.cast_const().cast(), | 496 &mut output.cast(), |
359 ) | 497 ) |
360 }; | 498 }; |
361 ErrorCode::result_from(result)?; | 499 ErrorCode::result_from(result)?; |
362 // SAFETY: We got this result from PAM, and we're checking if it's null. | 500 // SAFETY: We got this result from PAM, and we're checking if it's null. |
363 unsafe { output.as_mut() }.ok_or(ErrorCode::ConversationError) | 501 unsafe { output.as_ref() }.ok_or(ErrorCode::ConversationError) |
364 } | 502 } |
365 } | |
366 | |
367 macro_rules! delegate { | |
368 // First have the kind that save the result after delegation. | |
369 (fn $meth:ident(&self $(, $param:ident: $typ:ty)*) -> Result<$ret:ty>) => { | |
370 fn $meth(&self $(, $param: $typ)*) -> Result<$ret> { | |
371 let result = self.handle.$meth($($param),*); | |
372 self.last_return.set(split(&result)); | |
373 result | |
374 } | |
375 }; | |
376 (fn $meth:ident(&mut self $(, $param:ident: $typ:ty)*) -> Result<$ret:ty>) => { | |
377 fn $meth(&mut self $(, $param: $typ)*) -> Result<$ret> { | |
378 let result = self.handle.$meth($($param),*); | |
379 self.last_return.set(split(&result)); | |
380 result | |
381 } | |
382 }; | |
383 // Then have the kind that are just raw delegates | |
384 (fn $meth:ident(&self $(, $param:ident: $typ:ty)*) -> $ret:ty) => { | |
385 fn $meth(&self $(, $param: $typ)*) -> $ret { | |
386 self.handle.$meth($($param),*) | |
387 } | |
388 }; | |
389 (fn $meth:ident(&mut self $(, $param:ident: $typ:ty)*) -> $ret:ty) => { | |
390 fn $meth(&mut self $(, $param: $typ)*) -> $ret { | |
391 self.handle.$meth($($param),*) | |
392 } | |
393 }; | |
394 // Then have item getters / setters | |
395 (get = $get:ident$(, set = $set:ident)?) => { | |
396 delegate!(fn $get(&self) -> Result<Option<String>>); | |
397 $(delegate!(set = $set);)? | |
398 }; | |
399 (set = $set:ident) => { | |
400 delegate!(fn $set(&mut self, value: Option<&str>) -> Result<()>); | |
401 }; | |
402 } | |
403 | |
404 fn split<T>(result: &Result<T>) -> Result<()> { | |
405 result.as_ref().map(drop).map_err(|&e| e) | |
406 } | |
407 | |
408 impl PamShared for OwnedLibPamHandle<'_> { | |
409 delegate!(fn log(&self, level: Level, location: Location<'_>, entry: &str) -> ()); | |
410 delegate!(fn environ(&self) -> impl EnvironMap); | |
411 delegate!(fn environ_mut(&mut self) -> impl EnvironMapMut); | |
412 delegate!(fn username(&mut self, prompt: Option<&str>) -> Result<String>); | |
413 delegate!(get = user_item, set = set_user_item); | |
414 delegate!(get = service, set = set_service); | |
415 delegate!(get = user_prompt, set = set_user_prompt); | |
416 delegate!(get = tty_name, set = set_tty_name); | |
417 delegate!(get = remote_user, set = set_remote_user); | |
418 delegate!(get = remote_host, set = set_remote_host); | |
419 delegate!(set = set_authtok_item); | |
420 delegate!(set = set_old_authtok_item); | |
421 } | 503 } |
422 | 504 |
423 /// Identifies what is being gotten or set with `pam_get_item` | 505 /// Identifies what is being gotten or set with `pam_get_item` |
424 /// or `pam_set_item`. | 506 /// or `pam_set_item`. |
425 #[derive(TryFromPrimitive, IntoPrimitive)] | 507 #[derive(TryFromPrimitive, IntoPrimitive)] |
426 #[repr(i32)] | 508 #[repr(i32)] |
427 #[non_exhaustive] // because C could give us anything! | 509 #[non_exhaustive] // because C could give us anything! |
428 pub enum ItemType { | 510 pub enum ItemType { |
429 /// The PAM service name. | 511 /// The PAM service name. |
430 Service = 1, | 512 Service = constants::PAM_SERVICE, |
431 /// The user's login name. | 513 /// The user's login name. |
432 User = 2, | 514 User = constants::PAM_USER, |
433 /// The TTY name. | 515 /// The TTY name. |
434 Tty = 3, | 516 Tty = constants::PAM_TTY, |
435 /// The remote host (if applicable). | 517 /// The remote host (if applicable). |
436 RemoteHost = 4, | 518 RemoteHost = constants::PAM_RHOST, |
437 /// The conversation struct (not a CStr-based item). | 519 /// The conversation struct (not a CStr-based item). |
438 Conversation = 5, | 520 Conversation = constants::PAM_CONV, |
439 /// The authentication token (password). | 521 /// The authentication token (password). |
440 AuthTok = 6, | 522 AuthTok = constants::PAM_AUTHTOK, |
441 /// The old authentication token (when changing passwords). | 523 /// The old authentication token (when changing passwords). |
442 OldAuthTok = 7, | 524 OldAuthTok = constants::PAM_OLDAUTHTOK, |
443 /// The remote user's name. | 525 /// The remote user's name. |
444 RemoteUser = 8, | 526 RemoteUser = constants::PAM_RUSER, |
445 /// The prompt shown when requesting a username. | 527 /// The prompt shown when requesting a username. |
446 UserPrompt = 9, | 528 UserPrompt = constants::PAM_USER_PROMPT, |
529 #[cfg(feature = "linux-pam-ext")] | |
447 /// App-supplied function to override failure delays. | 530 /// App-supplied function to override failure delays. |
448 FailDelay = 10, | 531 FailDelay = constants::PAM_FAIL_DELAY, |
532 #[cfg(feature = "linux-pam-ext")] | |
449 /// X display name. | 533 /// X display name. |
450 XDisplay = 11, | 534 XDisplay = constants::PAM_XDISPLAY, |
535 #[cfg(feature = "linux-pam-ext")] | |
451 /// X server authentication data. | 536 /// X server authentication data. |
452 XAuthData = 12, | 537 XAuthData = constants::PAM_XAUTHDATA, |
538 #[cfg(feature = "linux-pam-ext")] | |
453 /// The type of `pam_get_authtok`. | 539 /// The type of `pam_get_authtok`. |
454 AuthTokType = 13, | 540 AuthTokType = constants::PAM_AUTHTOK_TYPE, |
455 } | 541 } |