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.