Mercurial > crates > nonstick
comparison src/libpam/handle.rs @ 153:3036f2e6a022
Add module-specific data support.
This adds support for a safe form of `pam_get_data` and `pam_set_data`,
where data is (as best as humanly possible) type-safe and restricted
to only the module where it was created.
author | Paul Fisher <paul@pfish.zone> |
---|---|
date | Tue, 08 Jul 2025 00:31:54 -0400 |
parents | 4b3a5095f68c |
children | ab8020566cd9 |
comparison
equal
deleted
inserted
replaced
152:4d11b2e7da83 | 153:3036f2e6a022 |
---|---|
1 use super::conversation::{OwnedConversation, PamConv}; | 1 use super::conversation::{OwnedConversation, PamConv}; |
2 use crate::_doc::{guide, linklist, stdlinks}; | 2 use crate::_doc::{guide, linklist, man7, stdlinks}; |
3 use crate::constants::{ErrorCode, Result}; | 3 use crate::constants::{ErrorCode, Result}; |
4 use crate::conv::Exchange; | 4 use crate::conv::Exchange; |
5 use crate::environ::EnvironMapMut; | 5 use crate::environ::EnvironMapMut; |
6 use crate::handle::PamShared; | 6 use crate::handle::PamShared; |
7 use crate::items::{Items, ItemsMut}; | 7 use crate::items::{Items, ItemsMut}; |
10 use crate::libpam::{items, memory}; | 10 use crate::libpam::{items, memory}; |
11 use crate::logging::{Level, Location}; | 11 use crate::logging::{Level, Location}; |
12 use crate::{Conversation, EnvironMap, Flags, ModuleClient, Transaction}; | 12 use crate::{Conversation, EnvironMap, Flags, ModuleClient, Transaction}; |
13 use libpam_sys_consts::constants; | 13 use libpam_sys_consts::constants; |
14 use num_enum::{IntoPrimitive, TryFromPrimitive}; | 14 use num_enum::{IntoPrimitive, TryFromPrimitive}; |
15 use std::any::TypeId; | |
15 use std::cell::Cell; | 16 use std::cell::Cell; |
16 use std::ffi::{c_char, c_int, CString, OsStr, OsString}; | 17 use std::ffi::{c_char, c_int, c_void, CString, OsStr, OsString}; |
17 use std::mem::ManuallyDrop; | 18 use std::mem::ManuallyDrop; |
18 use std::os::unix::ffi::OsStrExt; | 19 use std::os::unix::ffi::OsStrExt; |
19 use std::ptr; | 20 use std::ptr; |
20 use std::ptr::NonNull; | 21 use std::ptr::NonNull; |
21 | 22 |
105 last_return: Cell::new(Ok(())), | 106 last_return: Cell::new(Ok(())), |
106 conversation: conv, | 107 conversation: conv, |
107 }) | 108 }) |
108 } | 109 } |
109 | 110 |
110 /// "Quietly" closes the PAM session on an owned PAM handle. | 111 #[cfg_attr( |
111 /// | 112 pam_impl = "LinuxPam", |
112 /// This internally calls `pam_end` with the appropriate error code. | 113 doc = "Ends the PAM transaction \"quietly\" (on Linux-PAM only)." |
113 /// | 114 )] |
114 /// # References | 115 #[cfg_attr( |
115 #[doc = linklist!(pam_end: adg, _std)] | 116 not(pam_impl = "LinuxPam"), |
116 /// | 117 doc = "Exactly equivalent to `drop(self)` (except on Linux-PAM)." |
117 #[doc = guide!(adg: "adg-interface-by-app-expected.html#adg-pam_end")] | 118 )] |
118 #[doc = stdlinks!(3 pam_end)] | 119 /// |
119 | 120 /// On Linux-PAM, this is equivalent to passing the `PAM_DATA_SILENT` flag |
120 fn end_quiet(self) {} | 121 /// to [`pam_end` on Linux-PAM][man7], which signals that data cleanup |
122 /// should "not treat the call too seriously" \[sic]. | |
123 /// | |
124 /// On other platforms, this is no different than letting the transaction | |
125 /// end on its own. | |
126 /// | |
127 #[doc = man7!(3 pam_end)] | |
128 pub fn end_silent(self) { | |
129 #[cfg(pam_impl = "LinuxPam")] | |
130 { | |
131 let mut me = ManuallyDrop::new(self); | |
132 me.end_internal(libpam_sys::PAM_DATA_SILENT); | |
133 } | |
134 // If it's not LinuxPam, we just drop normally. | |
135 } | |
136 | |
137 /// Internal "end" function, which binary-ORs the status with `or_with`. | |
138 fn end_internal(&mut self, or_with: i32) { | |
139 let result = ErrorCode::result_to_c(self.last_return.get()) | or_with; | |
140 unsafe { libpam_sys::pam_end(self.handle.raw_mut(), result) }; | |
141 } | |
121 } | 142 } |
122 | 143 |
123 macro_rules! wrap { | 144 macro_rules! wrap { |
124 (fn $name:ident { $pam_func:ident }) => { | 145 (fn $name:ident { $pam_func:ident }) => { |
125 fn $name(&mut self, flags: Flags) -> Result<()> { | 146 fn $name(&mut self, flags: Flags) -> Result<()> { |
148 #[doc = linklist!(pam_end: adg, _std)] | 169 #[doc = linklist!(pam_end: adg, _std)] |
149 /// | 170 /// |
150 #[doc = guide!(adg: "adg-interface-by-app-expected.html#adg-pam_end")] | 171 #[doc = guide!(adg: "adg-interface-by-app-expected.html#adg-pam_end")] |
151 #[doc = stdlinks!(3 pam_end)] | 172 #[doc = stdlinks!(3 pam_end)] |
152 fn drop(&mut self) { | 173 fn drop(&mut self) { |
153 unsafe { | 174 self.end_internal(0) |
154 libpam_sys::pam_end( | |
155 self.handle.raw_mut(), | |
156 ErrorCode::result_to_c(self.last_return.get()), | |
157 ); | |
158 } | |
159 } | 175 } |
160 } | 176 } |
161 | 177 |
162 macro_rules! delegate { | 178 macro_rules! delegate { |
163 // First have the kind that save the result after delegation. | 179 // First have the kind that save the result after delegation. |
271 /// # References | 287 /// # References |
272 #[doc = linklist!(pam_end: adg, _std)] | 288 #[doc = linklist!(pam_end: adg, _std)] |
273 /// | 289 /// |
274 #[doc = guide!(adg: "adg-interface-by-app-expected.html#adg-pam_end")] | 290 #[doc = guide!(adg: "adg-interface-by-app-expected.html#adg-pam_end")] |
275 #[doc = stdlinks!(3 pam_end)] | 291 #[doc = stdlinks!(3 pam_end)] |
276 pub fn end_quiet(self, result: Result<()>) { | 292 pub fn end_silent(self, result: Result<()>) { |
277 let mut me = ManuallyDrop::new(self); | 293 let mut me = ManuallyDrop::new(self); |
278 let result = ErrorCode::result_to_c(result); | 294 let result = ErrorCode::result_to_c(result); |
279 #[cfg(pam_impl = "LinuxPam")] | 295 #[cfg(pam_impl = "LinuxPam")] |
280 let result = result | libpam_sys::PAM_DATA_SILENT; | 296 let result = result | libpam_sys::PAM_DATA_SILENT; |
281 unsafe { | 297 unsafe { |
388 | 404 |
389 fn old_authtok(&mut self, prompt: Option<&OsStr>) -> Result<OsString> { | 405 fn old_authtok(&mut self, prompt: Option<&OsStr>) -> Result<OsString> { |
390 self.get_authtok(prompt, ItemType::OldAuthTok) | 406 self.get_authtok(prompt, ItemType::OldAuthTok) |
391 } | 407 } |
392 | 408 |
409 fn get_module_data<T: 'static>(&self, key: &str) -> Option<&T> { | |
410 // It's technically unsafe to do this, but we assume that other modules | |
411 // aren't going to go out of their way to find the key we've used | |
412 // and corrupt its value's data. | |
413 let full_key = module_data_key::<T>(key); | |
414 let mut ptr: *const c_void = ptr::null(); | |
415 unsafe { | |
416 ErrorCode::result_from(libpam_sys::pam_get_data( | |
417 self.raw_ref(), | |
418 full_key.as_ptr(), | |
419 &mut ptr, | |
420 )) | |
421 .ok()?; | |
422 | |
423 (ptr as *const T).as_ref() | |
424 } | |
425 } | |
426 | |
427 fn set_module_data<T: 'static>(&mut self, key: &str, data: T) -> Result<()> { | |
428 let full_key = module_data_key::<T>(key); | |
429 let data = Box::new(data); | |
430 ErrorCode::result_from(unsafe { | |
431 libpam_sys::pam_set_data( | |
432 self.raw_mut(), | |
433 full_key.as_ptr(), | |
434 Box::into_raw(data).cast(), | |
435 drop_module_data::<T>, | |
436 ) | |
437 }) | |
438 } | |
439 | |
393 fn authtok_item(&self) -> Result<Option<OsString>> { | 440 fn authtok_item(&self) -> Result<Option<OsString>> { |
394 unsafe { items::get_cstr_item(self, ItemType::AuthTok) } | 441 unsafe { items::get_cstr_item(self, ItemType::AuthTok) } |
395 } | 442 } |
396 fn old_authtok_item(&self) -> Result<Option<OsString>> { | 443 fn old_authtok_item(&self) -> Result<Option<OsString>> { |
397 unsafe { items::get_cstr_item(self, ItemType::OldAuthTok) } | 444 unsafe { items::get_cstr_item(self, ItemType::OldAuthTok) } |
398 } | 445 } |
446 } | |
447 | |
448 /// Constructs a type-specific, module-specific key for this data. | |
449 fn module_data_key<T: 'static>(key: &str) -> CString { | |
450 // The type ID is unique per-type. | |
451 let tid = TypeId::of::<T>(); | |
452 // The `set_data_cleanup` function lives statically inside each PAM module, | |
453 // so its address will be different between `pam_a.so` and `pam_b.so`, | |
454 // even if both modules .so files are byte-for-byte identical. | |
455 let cleanup_addr = drop_module_data::<T> as usize; | |
456 // Then, by adding the key, | |
457 let key = format!("{key:?}::{tid:?}::{cleanup_addr:016x}"); | |
458 CString::new(key).expect("null bytes somehow got into a debug string?") | |
399 } | 459 } |
400 | 460 |
401 /// Function called at the end of a PAM session that is called to clean up | 461 /// Function called at the end of a PAM session that is called to clean up |
402 /// a value previously provided to PAM in a `pam_set_data` call. | 462 /// a value previously provided to PAM in a `pam_set_data` call. |
403 /// | 463 /// |
404 /// You should never call this yourself. | 464 /// You should never call this yourself. |
405 extern "C" fn set_data_cleanup<T>(_: *const libc::c_void, c_data: *mut libc::c_void, _: c_int) { | 465 extern "C" fn drop_module_data<T>(_: *mut libpam_sys::pam_handle, c_data: *mut c_void, _: c_int) { |
406 unsafe { | 466 unsafe { |
407 let _data: Box<T> = Box::from_raw(c_data.cast()); | 467 // Adopt the pointer into a Box and immediately drop it. |
468 let _: Box<T> = Box::from_raw(c_data.cast()); | |
408 } | 469 } |
409 } | 470 } |
410 | 471 |
411 // Implementations of internal functions. | 472 // Implementations of internal functions. |
412 impl LibPamHandle { | 473 impl LibPamHandle { |