# HG changeset patch # User Paul Fisher # Date 1746341815 14400 # Node ID daa2cde64601ec34757e5008838e5da8dba600d5 # Parent 676675c3d434c6025062a010237acc26aa751770 Big big refactor. Probably should have been multiple changes. - Makes FFI safer by explicitly specifying c_int in calls. - Uses ToPrimitive/FromPrimitive to make this easier. - Pulls PamFlag variables into a bitflags! struct. - Pulls PamMessageStyle variables into an enum. - Renames ResultCode to ErrorCode. - Switches from PAM_SUCCESS to using a Result<(), ErrorCode>. - Uses thiserror to make ErrorCode into an Error. - Gets rid of pam_try! because now we have Results. - Expands some names (e.g. Conv to Conversation). - Adds more doc comments. - Returns passwords as a SecureString, to avoid unnecessarily keeping it around in memory. diff -r 676675c3d434 -r daa2cde64601 Cargo.toml --- a/Cargo.toml Sun May 04 00:58:04 2025 -0400 +++ b/Cargo.toml Sun May 04 02:56:55 2025 -0400 @@ -10,5 +10,9 @@ edition = "2021" [dependencies] +bitflags = "2.9.0" libc = "0.2.97" +num-derive = "0.4.2" +num-traits = "0.2.19" +secure-string = "0.3.0" thiserror = "2.0.12" diff -r 676675c3d434 -r daa2cde64601 src/constants.rs --- a/src/constants.rs Sun May 04 00:58:04 2025 -0400 +++ b/src/constants.rs Sun May 04 02:56:55 2025 -0400 @@ -1,99 +1,144 @@ +use bitflags::bitflags; use libc::{c_int, c_uint}; - +use num_derive::{FromPrimitive, ToPrimitive}; +use num_traits::{FromPrimitive, ToPrimitive}; // TODO: Import constants from C header file at compile time. -pub type PamFlag = c_uint; -pub type PamItemType = c_int; -pub type PamMessageStyle = c_int; - // The Linux-PAM flags // see /usr/include/security/_pam_types.h -pub const PAM_SILENT: PamFlag = 0x8000; -pub const PAM_DISALLOW_NULL_AUTHTOK: PamFlag = 0x0001; -pub const PAM_ESTABLISH_CRED: PamFlag = 0x0002; -pub const PAM_DELETE_CRED: PamFlag = 0x0004; -pub const PAM_REINITIALIZE_CRED: PamFlag = 0x0008; -pub const PAM_REFRESH_CRED: PamFlag = 0x0010; -pub const PAM_CHANGE_EXPIRED_AUTHTOK: PamFlag = 0x0020; +bitflags! { + #[derive(Debug, PartialEq)] + #[repr(transparent)] + pub struct Flags: c_uint { + const SILENT = 0x8000; + const DISALLOW_NULL_AUTHTOK = 0x0001; + const ESTABLISH_CRED = 0x0002; + const DELETE_CRED = 0x0004; + const REINITIALIZE_CRED = 0x0008; + const REFRESH_CRED = 0x0010; + const CHANGE_EXPIRED_AUTHTOK= 0x0020; + } +} -// Message styles -pub const PAM_PROMPT_ECHO_OFF: PamMessageStyle = 1; -pub const PAM_PROMPT_ECHO_ON: PamMessageStyle = 2; -pub const PAM_ERROR_MSG: PamMessageStyle = 3; -pub const PAM_TEXT_INFO: PamMessageStyle = 4; -/// yes/no/maybe conditionals -pub const PAM_RADIO_TYPE: PamMessageStyle = 5; -pub const PAM_BINARY_PROMPT: PamMessageStyle = 7; +/// Styles of message that are shown to the user. +#[derive(Debug, PartialEq, FromPrimitive, ToPrimitive)] +#[non_exhaustive] // non-exhaustive because C might give us back anything! +pub enum MessageStyle { + /// Requests information from the user; will be masked when typing. + PromptEchoOff = 1, + /// Requests information from the user; will not be masked. + PromptEchoOn = 2, + /// An error message. + ErrorMsg = 3, + /// An informational message. + TextInfo = 4, + /// Yes/No/Maybe conditionals. Linux-PAM specific. + RadioType = 5, + /// For server–client non-human interaction. + /// NOT part of the X/Open PAM specification. + BinaryPrompt = 7, +} -/// The Linux-PAM return values. +impl From for c_int { + fn from(val: MessageStyle) -> Self { + val.to_i32().unwrap_or(0) + } +} + +/// The Linux-PAM error return values. +/// Success is instead represented by the `Ok` entry of a `Result`. +/// Most abbreviations (except `AuthTok` and `Max`) are now full words. /// For more detailed information, see -/// /usr/include/security/_pam_types.h +/// `/usr/include/security/_pam_types.h`. #[allow(non_camel_case_types, dead_code)] -#[derive(Debug, PartialEq, thiserror::Error)] -#[repr(C)] -pub enum PamResultCode { - #[error("Not an error")] - PAM_SUCCESS = 0, +#[derive(Copy, Clone, Debug, PartialEq, thiserror::Error, FromPrimitive, ToPrimitive)] +#[non_exhaustive] // C might give us anything! +pub enum ErrorCode { #[error("dlopen() failure when dynamically loading a service module")] - PAM_OPEN_ERR = 1, + OpenError = 1, #[error("symbol not found")] - PAM_SYMBOL_ERR = 2, + SymbolError = 2, #[error("error in service module")] - PAM_SERVICE_ERR = 3, + ServiceError = 3, #[error("system error")] - PAM_SYSTEM_ERR = 4, + SystemError = 4, #[error("memory buffer error")] - PAM_BUF_ERR = 5, + BufferError = 5, #[error("permission denied")] - PAM_PERM_DENIED = 6, + PermissionDenied = 6, #[error("authentication failure")] - PAM_AUTH_ERR = 7, + AuthenticationError = 7, #[error("cannot access authentication data due to insufficient credentials")] - PAM_CRED_INSUFFICIENT = 8, + CredentialsInsufficient = 8, #[error("underlying authentication service cannot retrieve authentication information")] - PAM_AUTHINFO_UNAVAIL = 9, + AuthInfoUnavailable = 9, #[error("user not known to the underlying authentication module")] - PAM_USER_UNKNOWN = 10, + UserUnknown = 10, #[error("retry limit reached; do not attempt further")] - PAM_MAXTRIES = 11, + MaxTries = 11, #[error("new authentication token required")] - PAM_NEW_AUTHTOK_REQD = 12, + NewAuthTokRequired = 12, #[error("user account has expired")] - PAM_ACCT_EXPIRED = 13, + AccountExpired = 13, #[error("cannot make/remove an entry for the specified session")] - PAM_SESSION_ERR = 14, + SessionError = 14, #[error("underlying authentication service cannot retrieve user credentials")] - PAM_CRED_UNAVAIL = 15, + CredentialsUnavailable = 15, #[error("user credentials expired")] - PAM_CRED_EXPIRED = 16, + CredentialsExpired = 16, #[error("failure setting user credentials")] - PAM_CRED_ERR = 17, + CredentialsError = 17, #[error("no module-specific data is present")] - PAM_NO_MODULE_DATA = 18, + NoModuleData = 18, #[error("conversation error")] - PAM_CONV_ERR = 19, + ConversationError = 19, #[error("authentication token manipulation error")] - PAM_AUTHTOK_ERR = 20, + AuthTokError = 20, #[error("authentication information cannot be recovered")] - PAM_AUTHTOK_RECOVERY_ERR = 21, + AuthTokRecoveryError = 21, #[error("authentication token lock busy")] - PAM_AUTHTOK_LOCK_BUSY = 22, + AuthTokLockBusy = 22, #[error("authentication token aging disabled")] - PAM_AUTHTOK_DISABLE_AGING = 23, + AuthTokDisableAging = 23, #[error("preliminary check by password service")] - PAM_TRY_AGAIN = 24, + TryAgain = 24, #[error("ignore underlying account module, regardless of control flag")] - PAM_IGNORE = 25, + Ignore = 25, #[error("critical error; this module should fail now")] - PAM_ABORT = 26, + Abort = 26, #[error("authentication token has expired")] - PAM_AUTHTOK_EXPIRED = 27, + AuthTokExpired = 27, #[error("module is not known")] - PAM_MODULE_UNKNOWN = 28, + ModuleUnknown = 28, #[error("bad item passed to pam_[whatever]_item")] - PAM_BAD_ITEM = 29, + BadItem = 29, #[error("conversation function is event-driven and data is not available yet")] - PAM_CONV_AGAIN = 30, + ConversationAgain = 30, #[error("call this function again to complete authentication stack")] - PAM_INCOMPLETE = 31, + Incomplete = 31, } + +pub type PamResult = Result; +impl ErrorCode { + /// Converts a PamResult into the result code that C wants. + pub fn result_to_c(value: PamResult<()>) -> c_int { + match value { + Ok(_) => 0, // PAM_SUCCESS + Err(otherwise) => otherwise.into(), + } + } + + /// Converts a C result code into a PamResult, with success as Ok. + pub fn result_from(value: c_int) -> PamResult<()> { + match value { + 0 => Ok(()), + value => Err(Self::from_i64(value as i64).unwrap_or(Self::ConversationError)) + } + } +} + +impl From for c_int { + fn from(val: ErrorCode) -> Self { + val.to_i32().unwrap_or(0) + } +} diff -r 676675c3d434 -r daa2cde64601 src/conv.rs --- a/src/conv.rs Sun May 04 00:58:04 2025 -0400 +++ b/src/conv.rs Sun May 04 02:56:55 2025 -0400 @@ -2,19 +2,19 @@ use std::ffi::{CStr, CString}; use std::ptr; -use crate::constants::PamMessageStyle; -use crate::constants::PamResultCode; +use crate::constants::MessageStyle; +use crate::constants::PamResult; +use crate::constants::ErrorCode; use crate::items::Item; -use crate::module::PamResult; #[repr(C)] -struct PamMessage { - msg_style: PamMessageStyle, +struct Message { + msg_style: MessageStyle, msg: *const c_char, } #[repr(C)] -struct PamResponse { +struct Response { resp: *const c_char, resp_retcode: libc::c_int, // Unused - always zero } @@ -23,10 +23,10 @@ pub struct Inner { conv: extern "C" fn( num_msg: c_int, - pam_message: &&PamMessage, - pam_response: &mut *const PamResponse, + pam_message: &&Message, + pam_response: &mut *const Response, appdata_ptr: *const libc::c_void, - ) -> PamResultCode, + ) -> c_int, appdata_ptr: *const libc::c_void, } @@ -53,27 +53,23 @@ /// Note that the user experience will depend on how the client implements /// these message styles - and not all applications implement all message /// styles. - pub fn send(&self, style: PamMessageStyle, msg: &str) -> PamResult> { - let mut resp_ptr: *const PamResponse = ptr::null(); + pub fn send(&self, style: MessageStyle, msg: &str) -> PamResult> { + let mut resp_ptr: *const Response = ptr::null(); let msg_cstr = CString::new(msg).unwrap(); - let msg = PamMessage { + let msg = Message { msg_style: style, msg: msg_cstr.as_ptr(), }; - let ret = (self.0.conv)(1, &&msg, &mut resp_ptr, self.0.appdata_ptr); + ErrorCode::result_from(ret)?; - if PamResultCode::PAM_SUCCESS == ret { - // PamResponse.resp is null for styles that don't return user input like PAM_TEXT_INFO - let response = unsafe { (*resp_ptr).resp }; - if response.is_null() { - Ok(None) - } else { - Ok(Some(unsafe { CStr::from_ptr(response) })) + let result = unsafe { + match (*resp_ptr).resp { + p if p.is_null() => None, + p => Some(CStr::from_ptr(p)), } - } else { - Err(ret) - } + }; + Ok(result) } } @@ -81,7 +77,7 @@ type Raw = Inner; fn type_id() -> crate::items::ItemType { - crate::items::ItemType::Conv + crate::items::ItemType::Conversation } unsafe fn from_raw(raw: *const Self::Raw) -> Self { diff -r 676675c3d434 -r daa2cde64601 src/items.rs --- a/src/items.rs Sun May 04 00:58:04 2025 -0400 +++ b/src/items.rs Sun May 04 02:56:55 2025 -0400 @@ -1,7 +1,12 @@ -use std::ffi::CStr; +use num_derive::{FromPrimitive, ToPrimitive}; +use num_traits::{FromPrimitive, ToPrimitive}; +use std::ffi::{c_int, CStr}; -#[repr(u32)] +#[derive(FromPrimitive, ToPrimitive)] +#[repr(i32)] pub enum ItemType { + /// Unset. This should never be used. + Unset = 0, /// The service name Service = 1, /// The user name @@ -9,15 +14,15 @@ /// The tty name Tty = 3, /// The remote host name - RHost = 4, + RemoteHost = 4, /// The pam_conv structure - Conv = 5, + Conversation = 5, /// The authentication token (password) AuthTok = 6, /// The old authentication token OldAuthTok = 7, /// The remote user name - RUser = 8, + RemoteUser = 8, /// the prompt for getting a username UserPrompt = 9, /// app supplied function to override failure delays @@ -30,6 +35,18 @@ AuthTokType = 13, } +impl From for ItemType { + fn from(value: c_int) -> Self { + Self::from_i32(value).unwrap_or(Self::Unset) + } +} + +impl From for c_int { + fn from(val: ItemType) -> Self { + val.to_i32().unwrap_or(0) + } +} + // A type that can be requested by `pam::Handle::get_item`. pub trait Item { /// The `repr(C)` type that is returned (by pointer) by the underlying `pam_get_item` function. @@ -83,9 +100,9 @@ cstr_item!(Service); cstr_item!(User); cstr_item!(Tty); -cstr_item!(RHost); +cstr_item!(RemoteHost); // Conv cstr_item!(AuthTok); cstr_item!(OldAuthTok); -cstr_item!(RUser); +cstr_item!(RemoteUser); cstr_item!(UserPrompt); diff -r 676675c3d434 -r daa2cde64601 src/macros.rs --- a/src/macros.rs Sun May 04 00:58:04 2025 -0400 +++ b/src/macros.rs Sun May 04 02:56:55 2025 -0400 @@ -7,10 +7,10 @@ /// Here is full example of a PAM module that would authenticate and authorize everybody: /// /// ``` -/// #[macro_use] extern crate pam; +/// #[macro_use] extern crate nonstick; /// -/// use pam::module::{PamHooks, PamHandle}; -/// use pam::constants::{PamResultCode, PamFlag}; +/// use nonstick::module::{PamHooks, PamHandle}; +/// use nonstick::constants::{PamResult, Flags}; /// use std::ffi::CStr; /// /// # fn main() {} @@ -18,14 +18,14 @@ /// pam_hooks!(MyPamModule); /// /// impl PamHooks for MyPamModule { -/// fn sm_authenticate(pamh: &mut PamHandle, args: Vec<&CStr>, flags: PamFlag) -> PamResultCode { -/// println!("Everybody is authenticated!"); -/// PamResultCode::PAM_SUCCESS +/// fn acct_mgmt(pamh: &mut PamHandle, args: Vec<&CStr>, flags: Flags) -> PamResult<()> { +/// println!("Everybody is authorized!"); +/// Ok(()) /// } /// -/// fn acct_mgmt(pamh: &mut PamHandle, args: Vec<&CStr>, flags: PamFlag) -> PamResultCode { -/// println!("Everybody is authorized!"); -/// PamResultCode::PAM_SUCCESS +/// fn sm_authenticate(pamh: &mut PamHandle, args: Vec<&CStr>, flags: Flags) -> PamResult<()> { +/// println!("Everybody is authenticated!"); +/// Ok(()) /// } /// } /// ``` @@ -36,7 +36,7 @@ mod pam_hooks_scope { use std::ffi::CStr; use std::os::raw::{c_char, c_int}; - use $crate::constants::{PamFlag, PamResultCode}; + use $crate::constants::{Flags, ErrorCode}; use $crate::module::{PamHandle, PamHooks}; fn extract_argv<'a>(argc: c_int, argv: *const *const c_char) -> Vec<&'a CStr> { @@ -48,88 +48,72 @@ #[no_mangle] pub extern "C" fn pam_sm_acct_mgmt( pamh: &mut PamHandle, - flags: PamFlag, + flags: Flags, argc: c_int, argv: *const *const c_char, - ) -> PamResultCode { + ) -> c_int { let args = extract_argv(argc, argv); - super::$ident::acct_mgmt(pamh, args, flags) + ErrorCode::result_to_c(super::$ident::acct_mgmt(pamh, args, flags)) } #[no_mangle] pub extern "C" fn pam_sm_authenticate( pamh: &mut PamHandle, - flags: PamFlag, + flags: Flags, argc: c_int, argv: *const *const c_char, - ) -> PamResultCode { + ) -> c_int { let args = extract_argv(argc, argv); - super::$ident::sm_authenticate(pamh, args, flags) + ErrorCode::result_to_c(super::$ident::sm_authenticate(pamh, args, flags)) } #[no_mangle] pub extern "C" fn pam_sm_chauthtok( pamh: &mut PamHandle, - flags: PamFlag, + flags: Flags, argc: c_int, argv: *const *const c_char, - ) -> PamResultCode { + ) -> c_int { let args = extract_argv(argc, argv); - super::$ident::sm_chauthtok(pamh, args, flags) + ErrorCode::result_to_c(super::$ident::sm_chauthtok(pamh, args, flags)) } #[no_mangle] pub extern "C" fn pam_sm_close_session( pamh: &mut PamHandle, - flags: PamFlag, + flags: Flags, argc: c_int, argv: *const *const c_char, - ) -> PamResultCode { + ) -> c_int { let args = extract_argv(argc, argv); - super::$ident::sm_close_session(pamh, args, flags) + ErrorCode::result_to_c(super::$ident::sm_close_session(pamh, args, flags)) } #[no_mangle] pub extern "C" fn pam_sm_open_session( pamh: &mut PamHandle, - flags: PamFlag, + flags: Flags, argc: c_int, argv: *const *const c_char, - ) -> PamResultCode { + ) -> c_int { let args = extract_argv(argc, argv); - super::$ident::sm_open_session(pamh, args, flags) + ErrorCode::result_to_c(super::$ident::sm_open_session(pamh, args, flags)) } #[no_mangle] pub extern "C" fn pam_sm_setcred( pamh: &mut PamHandle, - flags: PamFlag, + flags: Flags, argc: c_int, argv: *const *const c_char, - ) -> PamResultCode { + ) -> c_int { let args = extract_argv(argc, argv); - super::$ident::sm_setcred(pamh, args, flags) + ErrorCode::result_to_c(super::$ident::sm_setcred(pamh, args, flags)) } } }; } -#[macro_export] -macro_rules! pam_try { - ($r:expr) => { - match $r { - Ok(t) => t, - Err(e) => return e, - } - }; - ($r:expr, $e:expr) => { - match $r { - Ok(t) => t, - Err(_) => return $e, - } - }; -} - #[cfg(test)] pub mod test { use crate::module::PamHooks; diff -r 676675c3d434 -r daa2cde64601 src/module.rs --- a/src/module.rs Sun May 04 00:58:04 2025 -0400 +++ b/src/module.rs Sun May 04 02:56:55 2025 -0400 @@ -1,9 +1,10 @@ //! Functions for use in pam modules. -use crate::constants::{PamFlag, PamResultCode}; +use crate::constants::{Flags, PamResult, ErrorCode}; use crate::items::{Item, ItemType}; use libc::c_char; -use std::ffi::{CStr, CString}; +use std::ffi::{c_int, CStr, CString}; +use secure_string::SecureString; /// Opaque type, used as a pointer when making pam API calls. /// @@ -21,7 +22,7 @@ pamh: *const PamHandle, module_data_name: *const c_char, data: &mut *const libc::c_void, - ) -> PamResultCode; + ) -> c_int; fn pam_set_data( pamh: *const PamHandle, @@ -30,45 +31,39 @@ cleanup: extern "C" fn( pamh: *const PamHandle, data: *mut libc::c_void, - error_status: PamResultCode, + error_status: c_int, ), - ) -> PamResultCode; + ) -> c_int; fn pam_get_item( pamh: *const PamHandle, - item_type: ItemType, + item_type: c_int, item: &mut *const libc::c_void, - ) -> PamResultCode; + ) -> c_int; - fn pam_set_item( - pamh: *mut PamHandle, - item_type: ItemType, - item: *const libc::c_void, - ) -> PamResultCode; + fn pam_set_item(pamh: *mut PamHandle, item_type: c_int, item: *const libc::c_void) -> c_int; - fn pam_get_user( - pamh: *const PamHandle, - user: &*mut c_char, - prompt: *const c_char, - ) -> PamResultCode; + fn pam_get_user(pamh: *const PamHandle, user: &*mut c_char, prompt: *const c_char) -> c_int; fn pam_get_authtok( pamh: *const PamHandle, - item_type: ItemType, + item_type: c_int, data: &*mut c_char, prompt: *const c_char, - ) -> PamResultCode; + ) -> c_int; } -pub extern "C" fn cleanup(_: *const PamHandle, c_data: *mut libc::c_void, _: PamResultCode) { +/// Function called at the end of a PAM session that is called to clean up +/// a value previously provided to PAM in a `pam_set_data` call. +/// +/// You should never call this yourself. +extern "C" fn cleanup(_: *const PamHandle, c_data: *mut libc::c_void, _: c_int) { unsafe { let _data: Box = Box::from_raw(c_data.cast::()); } } -pub type PamResult = Result; - impl PamHandle { /// Gets some value, identified by `key`, that has been set by the module /// previously. @@ -87,9 +82,9 @@ /// /// The data, if present, is owned by the current PAM conversation. pub unsafe fn get_data(&self, key: &str) -> PamResult> { - let c_key = CString::new(key).map_err(|_| PamResultCode::PAM_CONV_ERR)?; + let c_key = CString::new(key).map_err(|_| ErrorCode::ConversationError)?; let mut ptr: *const libc::c_void = std::ptr::null(); - to_result(pam_get_data(self, c_key.as_ptr(), &mut ptr))?; + ErrorCode::result_from(pam_get_data(self, c_key.as_ptr(), &mut ptr))?; match ptr.is_null() { true => Ok(None), false => { @@ -109,8 +104,8 @@ /// /// Returns an error if the underlying PAM function call fails. pub fn set_data(&mut self, key: &str, data: Box) -> PamResult<()> { - let c_key = CString::new(key).map_err(|_| PamResultCode::PAM_CONV_ERR)?; - let res = unsafe { + let c_key = CString::new(key).map_err(|_| ErrorCode::ConversationError)?; + let ret = unsafe { pam_set_data( self, c_key.as_ptr(), @@ -118,7 +113,7 @@ cleanup::, ) }; - to_result(res) + ErrorCode::result_from(ret) } /// Retrieves a value that has been set, possibly by the pam client. @@ -136,8 +131,8 @@ pub fn get_item(&self) -> PamResult> { let mut ptr: *const libc::c_void = std::ptr::null(); let out = unsafe { - let r = pam_get_item(self, T::type_id(), &mut ptr); - to_result(r)?; + let ret = pam_get_item(self, T::type_id().into(), &mut ptr); + ErrorCode::result_from(ret)?; let typed_ptr = ptr.cast::(); match typed_ptr.is_null() { true => None, @@ -156,9 +151,9 @@ /// /// Returns an error if the underlying PAM function call fails. pub fn set_item(&mut self, item: T) -> PamResult<()> { - let res = - unsafe { pam_set_item(self, T::type_id(), item.into_raw().cast::()) }; - to_result(res) + let ret = + unsafe { pam_set_item(self, T::type_id().into(), item.into_raw().cast::()) }; + ErrorCode::result_from(ret) } /// Retrieves the name of the user who is authenticating or logging in. @@ -174,11 +169,9 @@ pub fn get_user(&self, prompt: Option<&str>) -> PamResult { let prompt = option_cstr(prompt)?; let output: *mut c_char = std::ptr::null_mut(); - let res = unsafe { pam_get_user(self, &output, prompt_ptr(prompt.as_ref())) }; - match res { - PamResultCode::PAM_SUCCESS => copy_pam_string(output), - otherwise => Err(otherwise), - } + let ret = unsafe { pam_get_user(self, &output, prompt_ptr(prompt.as_ref())) }; + ErrorCode::result_from(ret)?; + copy_pam_string(output) } /// Retrieves the authentication token from the user. @@ -191,19 +184,19 @@ /// # Errors /// /// Returns an error if the underlying PAM function call fails. - pub fn get_authtok(&self, prompt: Option<&str>) -> PamResult { + pub fn get_authtok(&self, prompt: Option<&str>) -> PamResult { let prompt = option_cstr(prompt)?; let output: *mut c_char = std::ptr::null_mut(); let res = unsafe { pam_get_authtok( self, - ItemType::AuthTok, + ItemType::AuthTok.into(), &output, prompt_ptr(prompt.as_ref()), ) }; - to_result(res)?; - copy_pam_string(output) + ErrorCode::result_from(res)?; + copy_pam_string(output).map(SecureString::from) } } @@ -212,11 +205,11 @@ prompt .map(CString::new) .transpose() - .map_err(|_| PamResultCode::PAM_CONV_ERR) + .map_err(|_| ErrorCode::ConversationError) } /// The pointer to the prompt CString, or null if absent. -fn prompt_ptr(prompt: Option<&CString>) -> *const c_char { +pub(crate) fn prompt_ptr(prompt: Option<&CString>) -> *const c_char { match prompt { Some(c_str) => c_str.as_ptr(), None => std::ptr::null(), @@ -225,24 +218,16 @@ /// Creates an owned copy of a string that is returned from a /// pam_get_whatever function. -fn copy_pam_string(result_ptr: *const c_char) -> PamResult { +pub(crate) fn copy_pam_string(result_ptr: *const c_char) -> PamResult { // We really shouldn't get a null pointer back here, but if we do, return nothing. if result_ptr.is_null() { return Ok(String::new()); } let bytes = unsafe { CStr::from_ptr(result_ptr) }; - Ok(bytes + bytes .to_str() - .map_err(|_| PamResultCode::PAM_CONV_ERR)? - .into()) -} - -/// Convenience to transform a `PamResultCode` into a unit `PamResult`. -fn to_result(result: PamResultCode) -> PamResult<()> { - match result { - PamResultCode::PAM_SUCCESS => Ok(()), - otherwise => Err(otherwise), - } + .map(String::from) + .map_err(|_| ErrorCode::ConversationError) } /// Provides functions that are invoked by the entrypoints generated by the @@ -258,13 +243,13 @@ /// authentication module. This function checks for other things. Such things might be: the time of /// day or the date, the terminal line, remote hostname, etc. This function may also determine /// things like the expiration on passwords, and respond that the user change it before continuing. - fn acct_mgmt(pamh: &mut PamHandle, args: Vec<&CStr>, flags: PamFlag) -> PamResultCode { - PamResultCode::PAM_IGNORE + fn acct_mgmt(handle: &mut PamHandle, args: Vec<&CStr>, flags: Flags) -> PamResult<()> { + Err(ErrorCode::Ignore) } /// This function performs the task of authenticating the user. - fn sm_authenticate(pamh: &mut PamHandle, args: Vec<&CStr>, flags: PamFlag) -> PamResultCode { - PamResultCode::PAM_IGNORE + fn sm_authenticate(handle: &mut PamHandle, args: Vec<&CStr>, flags: Flags) -> PamResult<()> { + Err(ErrorCode::Ignore) } /// This function is used to (re-)set the authentication token of the user. @@ -273,18 +258,18 @@ /// `PAM_PRELIM_CHECK` and then, if the module does not return `PAM_TRY_AGAIN`, subsequently with /// `PAM_UPDATE_AUTHTOK`. It is only on the second call that the authorization token is /// (possibly) changed. - fn sm_chauthtok(pamh: &mut PamHandle, args: Vec<&CStr>, flags: PamFlag) -> PamResultCode { - PamResultCode::PAM_IGNORE + fn sm_chauthtok(handle: &mut PamHandle, args: Vec<&CStr>, flags: Flags) -> PamResult<()> { + Err(ErrorCode::Ignore) } /// This function is called to terminate a session. - fn sm_close_session(pamh: &mut PamHandle, args: Vec<&CStr>, flags: PamFlag) -> PamResultCode { - PamResultCode::PAM_IGNORE + fn sm_close_session(handle: &mut PamHandle, args: Vec<&CStr>, flags: Flags) -> PamResult<()> { + Err(ErrorCode::Ignore) } /// This function is called to commence a session. - fn sm_open_session(pamh: &mut PamHandle, args: Vec<&CStr>, flags: PamFlag) -> PamResultCode { - PamResultCode::PAM_IGNORE + fn sm_open_session(handle: &mut PamHandle, args: Vec<&CStr>, flags: Flags) -> PamResult<()> { + Err(ErrorCode::Ignore) } /// This function performs the task of altering the credentials of the user with respect to the @@ -292,7 +277,7 @@ /// information about a user than their authentication token. This function is used to make such /// information available to the application. It should only be called after the user has been /// authenticated but before a session has been established. - fn sm_setcred(pamh: &mut PamHandle, args: Vec<&CStr>, flags: PamFlag) -> PamResultCode { - PamResultCode::PAM_IGNORE + fn sm_setcred(handle: &mut PamHandle, args: Vec<&CStr>, flags: Flags) -> PamResult<()> { + Err(ErrorCode::Ignore) } }