Mercurial > crates > nonstick
diff src/libpam/handle.rs @ 159:634cd5f2ac8b
Separate logging into its own trait apart from the rest of PAM.
author | Paul Fisher <paul@pfish.zone> |
---|---|
date | Sat, 12 Jul 2025 18:16:18 -0400 |
parents | 0099f2f79f86 |
children | a75a66cb4181 |
line wrap: on
line diff
--- a/src/libpam/handle.rs Sat Jul 12 17:17:37 2025 -0400 +++ b/src/libpam/handle.rs Sat Jul 12 18:16:18 2025 -0400 @@ -8,7 +8,7 @@ use crate::libpam::environ::{LibPamEnviron, LibPamEnvironMut}; use crate::libpam::items::{LibPamItems, LibPamItemsMut}; use crate::libpam::{items, memory}; -use crate::logging::{Level, Location}; +use crate::logging::{Level, Location, Logger}; use crate::{Conversation, EnvironMap, Flags, ModuleClient, Transaction}; use libpam_sys_consts::constants; use num_enum::{IntoPrimitive, TryFromPrimitive}; @@ -43,43 +43,45 @@ } impl TransactionBuilder { + /// Creates a builder to start a PAM transaction for the given service. + /// + /// The service name is what controls the steps and checks PAM goes through + /// when authenticating a user. This corresponds to the configuration file + /// usually at <code>/etc/pam.d/<var>service_name</var></code>. + /// + /// # References + #[doc = linklist!(pam_start: adg, _std)] + /// + #[doc = stdlinks!(3 pam_start)] + #[doc = guide!(adg: "adg-interface-by-app-expected.html#adg-pam_start")] + pub fn new_with_service(service_name: impl AsRef<OsStr>) -> Self { + Self { + service_name: service_name.as_ref().into(), + username: None, + } + } + /// Updates the service name. - pub fn service_name(mut self, service_name: OsString) -> Self { - self.service_name = service_name; + pub fn service_name(mut self, service_name: impl AsRef<OsStr>) -> Self { + self.service_name = service_name.as_ref().into(); self } + /// Sets the username. Setting this will avoid the need for an extra /// round trip through the conversation and may otherwise improve /// the login experience. - pub fn username(mut self, username: OsString) -> Self { - self.username = Some(username); + pub fn username(mut self, username: impl AsRef<OsStr>) -> Self { + self.username = Some(username.as_ref().into()); self } - /// Builds a PAM handle and starts the transaction. + + /// Builds the PAM handle and starts the transaction. pub fn build(self, conv: impl Conversation) -> Result<LibPamTransaction<impl Conversation>> { LibPamTransaction::start(self.service_name, self.username, conv) } } impl<C: Conversation> LibPamTransaction<C> { - /// Creates a builder to start a PAM transaction for the given service. - /// - /// The service name is what controls the steps and checks PAM goes through - /// when authenticating a user. This corresponds to the configuration file - /// named <code>/etc/pam.d/<var>service_name</var></code>. - /// - /// # References - #[doc = linklist!(pam_start: adg, _std)] - /// - #[doc = stdlinks!(3 pam_start)] - #[doc = guide!(adg: "adg-interface-by-app-expected.html#adg-pam_start")] - pub fn build_with_service(service_name: OsString) -> TransactionBuilder { - TransactionBuilder { - service_name, - username: None, - } - } - fn start(service_name: OsString, username: Option<OsString>, conversation: C) -> Result<Self> { let conv = Box::new(OwnedConversation::new(conversation)); let service_cstr = CString::new(service_name.as_bytes()).expect("null is forbidden"); @@ -216,6 +218,10 @@ result.as_ref().map(drop).map_err(|&e| e) } +impl<C: Conversation> Logger for LibPamTransaction<C> { + delegate!(fn log(&self, level: Level, location: Location<'_>, entry: fmt::Arguments) -> ()); +} + impl<C: Conversation> Transaction for LibPamTransaction<C> { delegate!(fn authenticate(&mut self, flags: Flags) -> Result<()>); delegate!(fn account_management(&mut self, flags: Flags) -> Result<()>); @@ -223,7 +229,6 @@ } impl<C: Conversation> PamShared for LibPamTransaction<C> { - delegate!(fn log(&self, level: Level, location: Location<'_>, entry: fmt::Arguments) -> ()); delegate!(fn environ(&self) -> impl EnvironMap); delegate!(fn environ_mut(&mut self) -> impl EnvironMapMut); delegate!(fn username(&mut self, prompt: Option<&OsStr>) -> Result<OsString>); @@ -321,13 +326,13 @@ } } -impl PamShared for LibPamHandle { +impl Logger for LibPamHandle { fn log(&self, level: Level, loc: Location<'_>, entry: fmt::Arguments) { let entry = match CString::new(entry.to_string()).ok() { Some(e) => e, None => return, }; - #[cfg(pam_impl = "LinuxPam")] + #[cfg(any(pam_impl = "LinuxPam", pam_impl = "Sun"))] { let level = match level { Level::Error => libc::LOG_ERR, @@ -337,6 +342,7 @@ }; _ = loc; // SAFETY: We're calling this function with a known value. + #[cfg(pam_impl = "LinuxPam")] unsafe { libpam_sys::pam_syslog( self.raw_ref(), @@ -345,6 +351,10 @@ entry.as_ptr(), ) } + #[cfg(pam_impl = "Sun")] + unsafe { + libpam_sys::__pam_log(level, b"%s\0".as_ptr().cast(), entry.as_ptr()) + } } #[cfg(pam_impl = "OpenPam")] { @@ -366,7 +376,9 @@ } } } +} +impl PamShared for LibPamHandle { fn username(&mut self, prompt: Option<&OsStr>) -> Result<OsString> { let prompt = memory::option_cstr_os(prompt); let mut output: *const c_char = ptr::null(); @@ -488,7 +500,7 @@ #[cfg(any(pam_impl = "LinuxPam", pam_impl = "OpenPam"))] fn get_authtok(&mut self, prompt: Option<&OsStr>, item_type: ItemType) -> Result<OsString> { let prompt = memory::option_cstr_os(prompt); - let mut output: *const c_char = ptr::null_mut(); + let mut output: *const c_char = ptr::null(); // SAFETY: We're calling this with known-good values. let res = unsafe { libpam_sys::pam_get_authtok( @@ -503,9 +515,46 @@ unsafe { memory::copy_pam_string(output) }.ok_or(ErrorCode::ConversationError) } - #[cfg(not(any(pam_impl = "LinuxPam", pam_impl = "OpenPam")))] + #[cfg(pam_impl = "Sun")] fn get_authtok(&mut self, prompt: Option<&OsStr>, item_type: ItemType) -> Result<OsString> { - Err(ErrorCode::ConversationError) + use crate::libpam::memory::CHeapString; + use std::os::unix::ffi::OsStringExt; + // Sun's __pam_get_authtok function is a little weird and requires + // that you specify where you want the authtok to come from. + // First we see if there's an authtok already set. + let mut output: *mut c_char = ptr::null_mut(); + let result = unsafe { + libpam_sys::__pam_get_authtok( + self.raw_mut(), + libpam_sys::PAM_HANDLE, + item_type.into(), + ptr::null(), + &mut output, + ) + }; + let output = unsafe { CHeapString::from_ptr(output) }; + if result == libpam_sys::PAM_SUCCESS { + if let Some(output) = output { + return Ok(OsString::from_vec(output.to_bytes().into())); + } + } + drop(output); + let mut output: *mut c_char = ptr::null_mut(); + let prompt = memory::option_cstr_os(prompt); + let result = unsafe { + libpam_sys::__pam_get_authtok( + self.raw_mut(), + libpam_sys::PAM_PROMPT, + item_type.into(), + memory::prompt_ptr(prompt.as_deref()), + &mut output, + ) + }; + let output = unsafe { CHeapString::from_ptr(output) }; + ErrorCode::result_from(result)?; + output + .map(|s| OsString::from_vec(s.to_bytes().into())) + .ok_or(ErrorCode::ConversationError) } /// Gets the `PAM_CONV` item from the handle. @@ -526,7 +575,7 @@ /// Identifies what is being gotten or set with `pam_get_item` /// or `pam_set_item`. -#[derive(TryFromPrimitive, IntoPrimitive)] +#[derive(Clone, Copy, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)] #[repr(i32)] #[non_exhaustive] // because C could give us anything! pub enum ItemType {