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 |
