Mercurial > crates > nonstick
comparison src/libpam/handle.rs @ 144:56b559b7ecea
Big rename: separate concepts of Transaction from Handle.
- An application that uses PAM creates a Transaction.
- The Transaction has a Handle.
Currently, a module still get something called a "handle",
but that's probably going to change soon.
| author | Paul Fisher <paul@pfish.zone> |
|---|---|
| date | Sun, 06 Jul 2025 11:59:26 -0400 |
| parents | ebb71a412b58 |
| children | 1bc52025156b |
comparison
equal
deleted
inserted
replaced
| 143:ebb71a412b58 | 144:56b559b7ecea |
|---|---|
| 5 use crate::handle::PamShared; | 5 use crate::handle::PamShared; |
| 6 use crate::libpam::environ::{LibPamEnviron, LibPamEnvironMut}; | 6 use crate::libpam::environ::{LibPamEnviron, LibPamEnvironMut}; |
| 7 use crate::libpam::memory; | 7 use crate::libpam::memory; |
| 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, PamHandleModule, Transaction, |
| 11 PamHandleModule, | |
| 12 }; | 11 }; |
| 13 use libpam_sys_helpers::constants; | 12 use libpam_sys_helpers::constants; |
| 14 use num_enum::{IntoPrimitive, TryFromPrimitive}; | 13 use num_enum::{IntoPrimitive, TryFromPrimitive}; |
| 15 use std::cell::Cell; | 14 use std::cell::Cell; |
| 16 use std::ffi::{c_char, c_int, CString, OsStr, OsString}; | 15 use std::ffi::{c_char, c_int, CString, OsStr, OsString}; |
| 18 use std::os::unix::ffi::OsStrExt; | 17 use std::os::unix::ffi::OsStrExt; |
| 19 use std::ptr; | 18 use std::ptr; |
| 20 use std::ptr::NonNull; | 19 use std::ptr::NonNull; |
| 21 | 20 |
| 22 /// An owned PAM handle. | 21 /// An owned PAM handle. |
| 23 pub struct OwnedLibPamHandle<C: Conversation> { | 22 pub struct LibPamTransaction<C: Conversation> { |
| 24 /// The handle itself. | 23 /// The handle itself. |
| 25 handle: ManuallyDrop<RawPamHandle>, | 24 handle: ManuallyDrop<LibPamHandle>, |
| 26 /// The last return value from the handle. | 25 /// The last return value from the handle. |
| 27 last_return: Cell<Result<()>>, | 26 last_return: Cell<Result<()>>, |
| 28 /// If set, the Conversation that this PAM handle owns. | 27 /// If set, the Conversation that this PAM handle owns. |
| 29 /// | 28 /// |
| 30 /// 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 |
| 34 /// so you have to keep it in one place. | 33 /// so you have to keep it in one place. |
| 35 conversation: Box<OwnedConversation<C>>, | 34 conversation: Box<OwnedConversation<C>>, |
| 36 } | 35 } |
| 37 | 36 |
| 38 #[derive(Debug, PartialEq)] | 37 #[derive(Debug, PartialEq)] |
| 39 pub struct HandleBuilder { | 38 pub struct TransactionBuilder { |
| 40 service_name: OsString, | 39 service_name: OsString, |
| 41 username: Option<OsString>, | 40 username: Option<OsString>, |
| 42 } | 41 } |
| 43 | 42 |
| 44 impl HandleBuilder { | 43 impl TransactionBuilder { |
| 45 /// Updates the service name. | 44 /// Updates the service name. |
| 46 pub fn service_name(mut self, service_name: OsString) -> Self { | 45 pub fn service_name(mut self, service_name: OsString) -> Self { |
| 47 self.service_name = service_name; | 46 self.service_name = service_name; |
| 48 self | 47 self |
| 49 } | 48 } |
| 53 pub fn username(mut self, username: OsString) -> Self { | 52 pub fn username(mut self, username: OsString) -> Self { |
| 54 self.username = Some(username); | 53 self.username = Some(username); |
| 55 self | 54 self |
| 56 } | 55 } |
| 57 /// Builds a PAM handle and starts the transaction. | 56 /// Builds a PAM handle and starts the transaction. |
| 58 pub fn build(self, conv: impl Conversation) -> Result<OwnedLibPamHandle<impl Conversation>> { | 57 pub fn build(self, conv: impl Conversation) -> Result<LibPamTransaction<impl Conversation>> { |
| 59 OwnedLibPamHandle::start(self.service_name, self.username, conv) | 58 LibPamTransaction::start(self.service_name, self.username, conv) |
| 60 } | 59 } |
| 61 } | 60 } |
| 62 | 61 |
| 63 impl<C: Conversation> OwnedLibPamHandle<C> { | 62 impl<C: Conversation> LibPamTransaction<C> { |
| 64 /// 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. |
| 65 /// | 64 /// |
| 66 /// 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 |
| 67 /// when authenticating a user. This corresponds to the configuration file | 66 /// when authenticating a user. This corresponds to the configuration file |
| 68 /// named <code>/etc/pam.d/<var>service_name</var></code>. | 67 /// named <code>/etc/pam.d/<var>service_name</var></code>. |
| 70 /// # References | 69 /// # References |
| 71 #[doc = linklist!(pam_start: adg, _std)] | 70 #[doc = linklist!(pam_start: adg, _std)] |
| 72 /// | 71 /// |
| 73 #[doc = stdlinks!(3 pam_start)] | 72 #[doc = stdlinks!(3 pam_start)] |
| 74 #[doc = guide!(adg: "adg-interface-by-app-expected.html#adg-pam_start")] | 73 #[doc = guide!(adg: "adg-interface-by-app-expected.html#adg-pam_start")] |
| 75 pub fn build_with_service(service_name: OsString) -> HandleBuilder { | 74 pub fn build_with_service(service_name: OsString) -> TransactionBuilder { |
| 76 HandleBuilder { | 75 TransactionBuilder { |
| 77 service_name, | 76 service_name, |
| 78 username: None, | 77 username: None, |
| 79 } | 78 } |
| 80 } | 79 } |
| 81 | 80 |
| 99 ) | 98 ) |
| 100 }; | 99 }; |
| 101 ErrorCode::result_from(result)?; | 100 ErrorCode::result_from(result)?; |
| 102 let handle = NonNull::new(handle).ok_or(ErrorCode::BufferError)?; | 101 let handle = NonNull::new(handle).ok_or(ErrorCode::BufferError)?; |
| 103 Ok(Self { | 102 Ok(Self { |
| 104 handle: ManuallyDrop::new(RawPamHandle(handle)), | 103 handle: ManuallyDrop::new(LibPamHandle(handle)), |
| 105 last_return: Cell::new(Ok(())), | 104 last_return: Cell::new(Ok(())), |
| 106 conversation: conv, | 105 conversation: conv, |
| 107 }) | 106 }) |
| 108 } | 107 } |
| 109 | 108 |
| 126 ErrorCode::result_from(unsafe { libpam_sys::$pam_func(self.0.as_mut(), flags.bits()) }) | 125 ErrorCode::result_from(unsafe { libpam_sys::$pam_func(self.0.as_mut(), flags.bits()) }) |
| 127 } | 126 } |
| 128 }; | 127 }; |
| 129 } | 128 } |
| 130 | 129 |
| 131 impl PamHandleApplication for RawPamHandle { | 130 impl Transaction for LibPamHandle { |
| 132 wrap!(fn authenticate { pam_authenticate }); | 131 wrap!(fn authenticate { pam_authenticate }); |
| 133 wrap!(fn account_management { pam_acct_mgmt }); | 132 wrap!(fn account_management { pam_acct_mgmt }); |
| 134 wrap!(fn change_authtok { pam_chauthtok }); | 133 wrap!(fn change_authtok { pam_chauthtok }); |
| 135 } | 134 } |
| 136 | 135 |
| 142 // pam_close_session - app | 141 // pam_close_session - app |
| 143 // pam_putenv - shared | 142 // pam_putenv - shared |
| 144 // pam_getenv - shared | 143 // pam_getenv - shared |
| 145 // pam_getenvlist - shared | 144 // pam_getenvlist - shared |
| 146 | 145 |
| 147 impl<C: Conversation> Drop for OwnedLibPamHandle<C> { | 146 impl<C: Conversation> Drop for LibPamTransaction<C> { |
| 148 /// Closes the PAM session on an owned PAM handle. | 147 /// Closes the PAM session on an owned PAM handle. |
| 149 /// | 148 /// |
| 150 /// This internally calls `pam_end` with the appropriate error code. | 149 /// This internally calls `pam_end` with the appropriate error code. |
| 151 /// | 150 /// |
| 152 /// # References | 151 /// # References |
| 203 | 202 |
| 204 fn split<T>(result: &Result<T>) -> Result<()> { | 203 fn split<T>(result: &Result<T>) -> Result<()> { |
| 205 result.as_ref().map(drop).map_err(|&e| e) | 204 result.as_ref().map(drop).map_err(|&e| e) |
| 206 } | 205 } |
| 207 | 206 |
| 208 impl<C: Conversation> PamShared for OwnedLibPamHandle<C> { | 207 impl<C: Conversation> PamShared for LibPamTransaction<C> { |
| 209 delegate!(fn log(&self, level: Level, location: Location<'_>, entry: &str) -> ()); | 208 delegate!(fn log(&self, level: Level, location: Location<'_>, entry: &str) -> ()); |
| 210 delegate!(fn environ(&self) -> impl EnvironMap); | 209 delegate!(fn environ(&self) -> impl EnvironMap); |
| 211 delegate!(fn environ_mut(&mut self) -> impl EnvironMapMut); | 210 delegate!(fn environ_mut(&mut self) -> impl EnvironMapMut); |
| 212 delegate!(fn username(&mut self, prompt: Option<&OsStr>) -> Result<OsString>); | 211 delegate!(fn username(&mut self, prompt: Option<&OsStr>) -> Result<OsString>); |
| 213 delegate!(get = user_item, set = set_user_item); | 212 delegate!(get = user_item, set = set_user_item); |
| 235 } | 234 } |
| 236 | 235 |
| 237 /// An owned variation of a basic PAM handle. | 236 /// An owned variation of a basic PAM handle. |
| 238 /// | 237 /// |
| 239 /// This is the most basic version of a wrapped PAM handle. It's mostly used | 238 /// This is the most basic version of a wrapped PAM handle. It's mostly used |
| 240 /// as the inside of the [`OwnedLibPamHandle`], but can also be used to "adopt" | 239 /// as the inside of the [`LibPamTransaction`], but can also be used to "adopt" |
| 241 /// a PAM handle created by another library. | 240 /// a PAM handle created by another library. |
| 242 /// | 241 /// |
| 243 /// If [`Self::end`] is not called, this will always call `pam_end` reporting | 242 /// If [`Self::end`] is not called, this will always call `pam_end` reporting |
| 244 /// successful completion. | 243 /// successful completion. |
| 245 pub struct RawPamHandle(NonNull<libpam_sys::pam_handle>); | 244 #[repr(transparent)] |
| 246 | 245 pub struct LibPamHandle(NonNull<libpam_sys::pam_handle>); |
| 247 impl RawPamHandle { | 246 |
| 247 impl LibPamHandle { | |
| 248 /// Takes ownership of the pointer to the given PAM handle. | 248 /// Takes ownership of the pointer to the given PAM handle. |
| 249 /// | 249 /// |
| 250 /// **Do not use this just to get a reference to a PAM handle.** | 250 /// **Do not use this just to get a reference to a PAM handle.** |
| 251 /// | 251 /// |
| 252 /// # Safety | 252 /// # Safety |
| 315 pub fn raw_mut(&mut self) -> &mut libpam_sys::pam_handle { | 315 pub fn raw_mut(&mut self) -> &mut libpam_sys::pam_handle { |
| 316 unsafe { self.0.as_mut() } | 316 unsafe { self.0.as_mut() } |
| 317 } | 317 } |
| 318 } | 318 } |
| 319 | 319 |
| 320 impl Drop for RawPamHandle { | 320 impl Drop for LibPamHandle { |
| 321 fn drop(&mut self) { | 321 fn drop(&mut self) { |
| 322 unsafe { libpam_sys::pam_end(self.0.as_mut(), 0) }; | 322 unsafe { libpam_sys::pam_end(self.0.as_mut(), 0) }; |
| 323 } | 323 } |
| 324 } | 324 } |
| 325 | 325 |
| 326 impl PamShared for RawPamHandle { | 326 impl PamShared for LibPamHandle { |
| 327 #[cfg(any())] | 327 #[cfg(any())] |
| 328 fn log(&self, level: Level, loc: Location<'_>, entry: &str) { | 328 fn log(&self, level: Level, loc: Location<'_>, entry: &str) { |
| 329 let entry = match CString::new(entry).or_else(|_| CString::new(dbg!(entry))) { | 329 let entry = match CString::new(entry).or_else(|_| CString::new(dbg!(entry))) { |
| 330 Ok(cstr) => cstr, | 330 Ok(cstr) => cstr, |
| 331 _ => return, | 331 _ => return, |
| 391 cstr_item!(set = set_remote_host, item = ItemType::RemoteHost); | 391 cstr_item!(set = set_remote_host, item = ItemType::RemoteHost); |
| 392 cstr_item!(set = set_authtok_item, item = ItemType::AuthTok); | 392 cstr_item!(set = set_authtok_item, item = ItemType::AuthTok); |
| 393 cstr_item!(set = set_old_authtok_item, item = ItemType::OldAuthTok); | 393 cstr_item!(set = set_old_authtok_item, item = ItemType::OldAuthTok); |
| 394 } | 394 } |
| 395 | 395 |
| 396 impl Conversation for RawPamHandle { | 396 impl Conversation for LibPamHandle { |
| 397 fn communicate(&self, messages: &[Exchange]) { | 397 fn communicate(&self, messages: &[Exchange]) { |
| 398 match self.conversation_item() { | 398 match self.conversation_item() { |
| 399 Ok(conv) => conv.communicate(messages), | 399 Ok(conv) => conv.communicate(messages), |
| 400 Err(e) => { | 400 Err(e) => { |
| 401 for msg in messages { | 401 for msg in messages { |
| 404 } | 404 } |
| 405 } | 405 } |
| 406 } | 406 } |
| 407 } | 407 } |
| 408 | 408 |
| 409 impl PamHandleModule for RawPamHandle { | 409 impl PamHandleModule for LibPamHandle { |
| 410 fn authtok(&mut self, prompt: Option<&OsStr>) -> Result<OsString> { | 410 fn authtok(&mut self, prompt: Option<&OsStr>) -> Result<OsString> { |
| 411 self.get_authtok(prompt, ItemType::AuthTok) | 411 self.get_authtok(prompt, ItemType::AuthTok) |
| 412 } | 412 } |
| 413 | 413 |
| 414 fn old_authtok(&mut self, prompt: Option<&OsStr>) -> Result<OsString> { | 414 fn old_authtok(&mut self, prompt: Option<&OsStr>) -> Result<OsString> { |
| 428 let _data: Box<T> = Box::from_raw(c_data.cast()); | 428 let _data: Box<T> = Box::from_raw(c_data.cast()); |
| 429 } | 429 } |
| 430 } | 430 } |
| 431 | 431 |
| 432 // Implementations of internal functions. | 432 // Implementations of internal functions. |
| 433 impl RawPamHandle { | 433 impl LibPamHandle { |
| 434 #[cfg(any(pam_impl = "LinuxPam", pam_impl = "OpenPam"))] | 434 #[cfg(any(pam_impl = "LinuxPam", pam_impl = "OpenPam"))] |
| 435 fn get_authtok(&mut self, prompt: Option<&OsStr>, item_type: ItemType) -> Result<OsString> { | 435 fn get_authtok(&mut self, prompt: Option<&OsStr>, item_type: ItemType) -> Result<OsString> { |
| 436 let prompt = memory::option_cstr_os(prompt); | 436 let prompt = memory::option_cstr_os(prompt); |
| 437 let mut output: *const c_char = ptr::null_mut(); | 437 let mut output: *const c_char = ptr::null_mut(); |
| 438 // SAFETY: We're calling this with known-good values. | 438 // SAFETY: We're calling this with known-good values. |
