Mercurial > crates > nonstick
view src/constants.rs @ 132:0b6a17f8c894 default tip
Get constant test working again with OpenPAM.
author | Paul Fisher <paul@pfish.zone> |
---|---|
date | Wed, 02 Jul 2025 02:34:29 -0400 |
parents | a632a8874131 |
children |
line wrap: on
line source
//! Constants and enum values from the PAM library. // We have a lot of dumb casts that we just gotta do because of differences // between Linux-PAM and OpenPAM header files. #![allow(clippy::unnecessary_cast)] use crate::{linklist, man7, manbsd, xsso}; use bitflags::bitflags; use num_enum::{IntoPrimitive, TryFromPrimitive}; use std::error::Error; use std::ffi::c_int; use std::fmt; use std::fmt::{Display, Formatter}; use std::result::Result as StdResult; /// Values for constants not provided by certain PAM implementations. /// /// **The values of these constants are deliberately selected _not_ to match /// any PAM implementations. Applications should always use the symbolic value /// and not a magic number.** mod pam_ffi { pub use libpam_sys::*; macro_rules! define { ($(#[$attr:meta])* $($name:ident = $value:expr;)+) => { define!( @meta { $(#[$attr])* } $(pub const $name: i32 = $value;)+ ); }; (@meta $m:tt $($i:item)+) => { define!(@expand $($m $i)+); }; (@expand $({ $(#[$m:meta])* } $i:item)+) => {$($(#[$m])* $i)+}; } define!( /// A fictitious constant for testing purposes. #[cfg(not(feature = "link"))] #[cfg_pam_impl(not("OpenPam"))] PAM_BAD_CONSTANT = 513; PAM_BAD_FEATURE = 514; ); define!( /// A fictitious constant for testing purposes. #[cfg(not(feature = "link"))] #[cfg_pam_impl(not(any("LinuxPam", "OpenPam")))] PAM_BAD_ITEM = 515; PAM_MODULE_UNKNOWN = 516; ); define!( /// A fictitious constant for testing purposes. #[cfg(not(feature = "link"))] #[cfg_pam_impl(not("LinuxPam"))] PAM_CONV_AGAIN = 517; PAM_INCOMPLETE = 518; ); } bitflags! { /// The available PAM flags. /// /// See `/usr/include/security/_pam_types.h` and /// See `/usr/include/security/pam_modules.h` for more details. #[derive(Debug, Default, PartialEq)] #[repr(transparent)] pub struct Flags: c_int { /// The module should not generate any messages. const SILENT = libpam_sys::PAM_SILENT; /// The module should return [ErrorCode::AuthError] /// if the user has an empty authentication token /// rather than immediately accepting them. const DISALLOW_NULL_AUTHTOK = libpam_sys::PAM_DISALLOW_NULL_AUTHTOK; // Flag used for `set_credentials`. /// Set user credentials for an authentication service. const ESTABLISH_CREDENTIALS = libpam_sys::PAM_ESTABLISH_CRED; /// Delete user credentials associated with /// an authentication service. const DELETE_CREDENTIALS = libpam_sys::PAM_DELETE_CRED; /// Reinitialize user credentials. const REINITIALIZE_CREDENTIALS = libpam_sys::PAM_REINITIALIZE_CRED; /// Extend the lifetime of user credentials. const REFRESH_CREDENTIALS = libpam_sys::PAM_REFRESH_CRED; // Flags used for password changing. /// The password service should only update those passwords /// that have aged. If this flag is _not_ passed, /// the password service should update all passwords. /// /// This flag is only used by `change_authtok`. const CHANGE_EXPIRED_AUTHTOK = libpam_sys::PAM_CHANGE_EXPIRED_AUTHTOK; /// This is a preliminary check for password changing. /// The password should not be changed. /// /// This is only used between PAM and a module. /// Applications may not use this flag. /// /// This flag is only used by `change_authtok`. const PRELIMINARY_CHECK = libpam_sys::PAM_PRELIM_CHECK; /// The password should actuallyPR be updated. /// This and [Self::PRELIMINARY_CHECK] are mutually exclusive. /// /// This is only used between PAM and a module. /// Applications may not use this flag. /// /// This flag is only used by `change_authtok`. const UPDATE_AUTHTOK = libpam_sys::PAM_UPDATE_AUTHTOK; } } /// The PAM error return codes. /// /// These are returned by most PAM functions if an error of some kind occurs. /// /// Instead of being an error code, success is represented by an Ok [`Result`]. /// /// # References /// #[doc = linklist!(pam: man7, manbsd)] /// - [X/SSO error code specification][xsso] /// #[doc = man7!(3 pam "RETURN_VALUES")] #[doc = manbsd!(3 pam "RETURN%20VALUES")] #[doc = xsso!("chap5.htm#tagcjh_06_02")] #[allow(non_camel_case_types, dead_code)] #[derive(Copy, Clone, Debug, PartialEq, TryFromPrimitive, IntoPrimitive)] #[non_exhaustive] // C might give us anything! #[repr(i32)] pub enum ErrorCode { OpenError = pam_ffi::PAM_OPEN_ERR, SymbolError = pam_ffi::PAM_SYMBOL_ERR, ServiceError = pam_ffi::PAM_SERVICE_ERR, SystemError = pam_ffi::PAM_SYSTEM_ERR, BufferError = pam_ffi::PAM_BUF_ERR, PermissionDenied = pam_ffi::PAM_PERM_DENIED, AuthenticationError = pam_ffi::PAM_AUTH_ERR, CredentialsInsufficient = pam_ffi::PAM_CRED_INSUFFICIENT, AuthInfoUnavailable = pam_ffi::PAM_AUTHINFO_UNAVAIL, UserUnknown = pam_ffi::PAM_USER_UNKNOWN, MaxTries = pam_ffi::PAM_MAXTRIES, NewAuthTokRequired = pam_ffi::PAM_NEW_AUTHTOK_REQD, AccountExpired = pam_ffi::PAM_ACCT_EXPIRED, SessionError = pam_ffi::PAM_SESSION_ERR, CredentialsUnavailable = pam_ffi::PAM_CRED_UNAVAIL, CredentialsExpired = pam_ffi::PAM_CRED_EXPIRED, CredentialsError = pam_ffi::PAM_CRED_ERR, NoModuleData = pam_ffi::PAM_NO_MODULE_DATA, ConversationError = pam_ffi::PAM_CONV_ERR, AuthTokError = pam_ffi::PAM_AUTHTOK_ERR, AuthTokRecoveryError = pam_ffi::PAM_AUTHTOK_RECOVERY_ERR, AuthTokLockBusy = pam_ffi::PAM_AUTHTOK_LOCK_BUSY, AuthTokDisableAging = pam_ffi::PAM_AUTHTOK_DISABLE_AGING, TryAgain = pam_ffi::PAM_TRY_AGAIN, Ignore = pam_ffi::PAM_IGNORE, Abort = pam_ffi::PAM_ABORT, AuthTokExpired = pam_ffi::PAM_AUTHTOK_EXPIRED, #[cfg(feature = "basic-ext")] ModuleUnknown = pam_ffi::PAM_MODULE_UNKNOWN, #[cfg(feature = "basic-ext")] BadItem = pam_ffi::PAM_BAD_ITEM, #[cfg(feature = "linux-pam-ext")] ConversationAgain = pam_ffi::PAM_CONV_AGAIN, #[cfg(feature = "linux-pam-ext")] Incomplete = pam_ffi::PAM_INCOMPLETE, } /// A PAM-specific Result type with an [ErrorCode] error. pub type Result<T> = StdResult<T, ErrorCode>; impl Display for ErrorCode { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { match strerror((*self).into()) { Some(err) => f.write_str(err), None => self.fmt_internal(f), } } } impl Error for ErrorCode {} impl ErrorCode { /// Converts this [Result] into a C-compatible result code. pub fn result_to_c<T>(value: Result<T>) -> c_int { match value { Ok(_) => 0, // PAM_SUCCESS Err(otherwise) => otherwise.into(), } } /// Converts a C result code into a [Result], with success as Ok. /// Invalid values are returned as a [Self::SystemError]. pub fn result_from(value: c_int) -> Result<()> { match value { 0 => Ok(()), value => Err(value.try_into().unwrap_or(Self::SystemError)), } } /// A basic Display implementation for if we don't link against PAM. fn fmt_internal(self, f: &mut Formatter<'_>) -> fmt::Result { let n: c_int = self.into(); write!(f, "PAM error: {self:?} ({n})") } } /// Gets a string version of an error message. #[cfg(feature = "link")] pub fn strerror(code: c_int) -> Option<&'static str> { use std::ffi::CStr; use std::ptr; // SAFETY: PAM impls don't care about the PAM handle and always return // static strings. let strerror = unsafe { libpam_sys::pam_strerror(ptr::null(), code as c_int) }; // SAFETY: We just got this back from PAM and we checked if it's null. (!strerror.is_null()) .then(|| unsafe { CStr::from_ptr(strerror) }.to_str().ok()) .flatten() } /// Dummy implementation of strerror so that it always returns None. #[cfg(not(feature = "link"))] pub fn strerror(_: c_int) -> Option<&'static str> { None } #[cfg(test)] mod tests { use super::*; #[test] fn test_enums() { assert_eq!(Ok(()), ErrorCode::result_from(0)); assert_eq!( pam_ffi::PAM_SESSION_ERR as i32, ErrorCode::result_to_c::<()>(Err(ErrorCode::SessionError)) ); assert_eq!( Err(ErrorCode::Abort), ErrorCode::result_from(pam_ffi::PAM_ABORT as i32) ); assert_eq!(Err(ErrorCode::SystemError), ErrorCode::result_from(423)); } }