Mercurial > crates > nonstick
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 |