Mercurial > crates > nonstick
diff 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 |
line wrap: on
line diff
--- a/src/libpam/handle.rs Mon Jun 23 14:26:34 2025 -0400 +++ b/src/libpam/handle.rs Mon Jun 23 19:10:34 2025 -0400 @@ -5,7 +5,7 @@ pub use crate::libpam::pam_ffi::LibPamHandle; use crate::libpam::{memory, pam_ffi}; use crate::logging::Level; -use crate::{Conversation, PamHandleModule}; +use crate::{Conversation, Flags, PamHandleApplication, PamHandleModule}; use num_enum::{IntoPrimitive, TryFromPrimitive}; use std::cell::Cell; use std::ffi::{c_char, c_int, CString}; @@ -36,6 +36,76 @@ _conversation_lifetime: PhantomData<&'a mut ()>, } +#[derive(Debug, PartialEq)] +pub struct HandleBuilder { + service_name: String, + username: Option<String>, +} + +impl HandleBuilder { + /// Creates a new HandleBuilder for the given service. + fn new(service_name: String) -> Self { + Self{service_name, username: Default::default()} + } + /// Updates the service name. + pub fn service_name(mut self, service_name: String) -> Self { + self.service_name = service_name; self + } + /// Updates the username. + pub fn username(mut self, username: String) -> Self { + self.username = Some(username); self + } + + pub fn build(self, conv: &impl Conversation) -> Result<OwnedLibPamHandle> { + OwnedLibPamHandle::start(self.service_name, self.username, conv) + } +} + +impl OwnedLibPamHandle<'_> { + pub fn build_with_service(service_name: String) -> HandleBuilder { + HandleBuilder::new(service_name) + } + fn start(service_name: String, username: Option<String>, conversation: &impl Conversation) -> Result<Self> { + let conv = LibPamConversation::wrap(conversation); + let service_cstr = CString::new(service_name).map_err(|_| ErrorCode::ConversationError)?; + let username_cstr = memory::prompt_ptr(memory::option_cstr(username.as_deref())?.as_ref()); + + let mut handle: *mut LibPamHandle = ptr::null_mut(); + // SAFETY: We've set everything up properly to call `pam_start`. + // The returned value will be a valid pointer provided the result is OK. + let result = unsafe { pam_ffi::pam_start(service_cstr.as_ptr(), username_cstr, &conv, &mut handle) }; + ErrorCode::result_from(result)?; + Ok(Self{ + handle: HandleWrap(handle), + last_return: Cell::new(Ok(())), + _conversation_lifetime: Default::default(), + }) + } +} + +impl PamHandleApplication for OwnedLibPamHandle<'_> { + fn authenticate(&mut self, flags: Flags) -> Result<()> { + let ret = unsafe { pam_ffi::pam_authenticate(self.handle.0, flags.bits() as c_int)}; + let result = ErrorCode::result_from(ret); + self.last_return.set(result); + result + } + + fn account_management(&mut self, flags: Flags) -> Result<()> { + let ret = unsafe { pam_ffi::pam_acct_mgmt(self.handle.0, flags.bits() as c_int)}; + let result = ErrorCode::result_from(ret); + self.last_return.set(result); + result + } + + fn change_authtok(&mut self, flags: Flags) -> Result<()> { + let ret = unsafe { pam_ffi::pam_chauthtok(self.handle.0, flags.bits() as c_int)}; + let result = ErrorCode::result_from(ret); + self.last_return.set(result); + result + } +} + // TODO: pam_authenticate - app // pam_setcred - app // pam_acct_mgmt - app