comparison src/libpam/handle.rs @ 97:efe2f5f8b5b2

Implement "stateless" application-side PAM calls. This introduces `authenticate`, `account_management`, and `change_authtok`. These are the three PAM operations that are stateless (i.e., they don't start a session or modify global credentials).
author Paul Fisher <paul@pfish.zone>
date Mon, 23 Jun 2025 19:10:34 -0400
parents f3e260f9ddcb
children b87100c5eed4
comparison
equal deleted inserted replaced
96:f3e260f9ddcb 97:efe2f5f8b5b2
3 use crate::conv::Message; 3 use crate::conv::Message;
4 use crate::handle::PamShared; 4 use crate::handle::PamShared;
5 pub use crate::libpam::pam_ffi::LibPamHandle; 5 pub use crate::libpam::pam_ffi::LibPamHandle;
6 use crate::libpam::{memory, pam_ffi}; 6 use crate::libpam::{memory, pam_ffi};
7 use crate::logging::Level; 7 use crate::logging::Level;
8 use crate::{Conversation, PamHandleModule}; 8 use crate::{Conversation, Flags, PamHandleApplication, PamHandleModule};
9 use num_enum::{IntoPrimitive, TryFromPrimitive}; 9 use num_enum::{IntoPrimitive, TryFromPrimitive};
10 use std::cell::Cell; 10 use std::cell::Cell;
11 use std::ffi::{c_char, c_int, CString}; 11 use std::ffi::{c_char, c_int, CString};
12 use std::marker::PhantomData; 12 use std::marker::PhantomData;
13 use std::ops::{Deref, DerefMut}; 13 use std::ops::{Deref, DerefMut};
32 /// An owned PAM handle. 32 /// An owned PAM handle.
33 pub struct OwnedLibPamHandle<'a> { 33 pub struct OwnedLibPamHandle<'a> {
34 handle: HandleWrap, 34 handle: HandleWrap,
35 last_return: Cell<Result<()>>, 35 last_return: Cell<Result<()>>,
36 _conversation_lifetime: PhantomData<&'a mut ()>, 36 _conversation_lifetime: PhantomData<&'a mut ()>,
37 }
38
39 #[derive(Debug, PartialEq)]
40 pub struct HandleBuilder {
41 service_name: String,
42 username: Option<String>,
43 }
44
45 impl HandleBuilder {
46 /// Creates a new HandleBuilder for the given service.
47 fn new(service_name: String) -> Self {
48 Self{service_name, username: Default::default()}
49 }
50 /// Updates the service name.
51 pub fn service_name(mut self, service_name: String) -> Self {
52 self.service_name = service_name; self
53 }
54 /// Updates the username.
55 pub fn username(mut self, username: String) -> Self {
56 self.username = Some(username); self
57 }
58
59 pub fn build(self, conv: &impl Conversation) -> Result<OwnedLibPamHandle> {
60 OwnedLibPamHandle::start(self.service_name, self.username, conv)
61 }
62 }
63
64 impl OwnedLibPamHandle<'_> {
65 pub fn build_with_service(service_name: String) -> HandleBuilder {
66 HandleBuilder::new(service_name)
67 }
68 fn start(service_name: String, username: Option<String>, conversation: &impl Conversation) -> Result<Self> {
69 let conv = LibPamConversation::wrap(conversation);
70 let service_cstr = CString::new(service_name).map_err(|_| ErrorCode::ConversationError)?;
71 let username_cstr = memory::prompt_ptr(memory::option_cstr(username.as_deref())?.as_ref());
72
73 let mut handle: *mut LibPamHandle = ptr::null_mut();
74 // SAFETY: We've set everything up properly to call `pam_start`.
75 // The returned value will be a valid pointer provided the result is OK.
76 let result = unsafe { pam_ffi::pam_start(service_cstr.as_ptr(), username_cstr, &conv, &mut handle) };
77 ErrorCode::result_from(result)?;
78 Ok(Self{
79 handle: HandleWrap(handle),
80 last_return: Cell::new(Ok(())),
81 _conversation_lifetime: Default::default(),
82 })
83 }
84 }
85
86 impl PamHandleApplication for OwnedLibPamHandle<'_> {
87 fn authenticate(&mut self, flags: Flags) -> Result<()> {
88 let ret = unsafe { pam_ffi::pam_authenticate(self.handle.0, flags.bits() as c_int)};
89 let result = ErrorCode::result_from(ret);
90 self.last_return.set(result);
91 result
92 }
93
94 fn account_management(&mut self, flags: Flags) -> Result<()> {
95 let ret = unsafe { pam_ffi::pam_acct_mgmt(self.handle.0, flags.bits() as c_int)};
96 let result = ErrorCode::result_from(ret);
97 self.last_return.set(result);
98 result
99 }
100
101 fn change_authtok(&mut self, flags: Flags) -> Result<()> {
102 let ret = unsafe { pam_ffi::pam_chauthtok(self.handle.0, flags.bits() as c_int)};
103 let result = ErrorCode::result_from(ret);
104 self.last_return.set(result);
105 result
106 }
37 } 107 }
38 108
39 // TODO: pam_authenticate - app 109 // TODO: pam_authenticate - app
40 // pam_setcred - app 110 // pam_setcred - app
41 // pam_acct_mgmt - app 111 // pam_acct_mgmt - app