Mercurial > crates > nonstick
view src/constants.rs @ 87:05291b601f0a default tip
Well and truly separate the Linux extensions.
This separates the Linux extensions on the libpam side,
and disables the two enums on the interface side.
Users can still call the Linux extensions from non-Linux PAM impls,
but they'll get a conversation error back.
author | Paul Fisher <paul@pfish.zone> |
---|---|
date | Tue, 10 Jun 2025 04:40:01 -0400 |
parents | 23162cd399aa |
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)] #[cfg(feature = "link")] use crate::libpam::pam_ffi; use bitflags::bitflags; use libc::c_int; use num_enum::{IntoPrimitive, TryFromPrimitive}; use std::ffi::c_uint; use std::result::Result as StdResult; /// Arbitrary values for PAM constants when not linking against system PAM. /// /// **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.** #[cfg(not(feature = "link"))] mod pam_ffi { macro_rules! define { ($(#[$attr:meta])* $($name:ident = $value:expr),+) => { define!( @meta { $(#[$attr])* } $(pub const $name: u32 = $value;)+ ); }; (@meta $m:tt $($i:item)+) => { define!(@expand $($m $i)+); }; (@expand $({ $(#[$m:meta])* } $i:item)+) => {$($(#[$m])* $i)+}; } const fn bit(n: u8) -> u32 { 1 << n } define!( PAM_SILENT = bit(13), PAM_DISALLOW_NULL_AUTHTOK = bit(14), PAM_ESTABLISH_CRED = bit(15), PAM_DELETE_CRED = bit(16), PAM_REINITIALIZE_CRED = bit(17), PAM_REFRESH_CRED = bit(18), PAM_CHANGE_EXPIRED_AUTHTOK = bit(19), PAM_PRELIM_CHECK = bit(20), PAM_UPDATE_AUTHTOK = bit(21) ); define!( PAM_ABORT = 513, PAM_ACCT_EXPIRED = 514, PAM_AUTHINFO_UNAVAIL = 515, PAM_AUTHTOK_DISABLE_AGING = 516, PAM_AUTHTOK_ERR = 517, PAM_AUTHTOK_EXPIRED = 518, PAM_AUTHTOK_LOCK_BUSY = 519, PAM_AUTHTOK_RECOVERY_ERR = 520, PAM_AUTH_ERR = 521, PAM_BAD_ITEM = 522, PAM_BUF_ERR = 533, PAM_CONV_AGAIN = 534, PAM_CONV_ERR = 535, PAM_CRED_ERR = 536, PAM_CRED_EXPIRED = 537, PAM_CRED_INSUFFICIENT = 538, PAM_CRED_UNAVAIL = 539, PAM_IGNORE = 540, PAM_INCOMPLETE = 541, PAM_MAXTRIES = 542, PAM_MODULE_UNKNOWN = 543, PAM_NEW_AUTHTOK_REQD = 544, PAM_NO_MODULE_DATA = 545, PAM_OPEN_ERR = 546, PAM_PERM_DENIED = 547, PAM_SERVICE_ERR = 548, PAM_SESSION_ERR = 549, PAM_SYMBOL_ERR = 550, PAM_SYSTEM_ERR = 551, PAM_TRY_AGAIN = 552, PAM_USER_UNKNOWN = 553 ); } 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, PartialEq)] #[repr(transparent)] pub struct Flags: c_uint { /// The module should not generate any messages. const SILENT = pam_ffi::PAM_SILENT as u32; /// The module should return [ErrorCode::AuthError] /// if the user has an empty authentication token /// rather than immediately accepting them. const DISALLOW_NULL_AUTHTOK = pam_ffi::PAM_DISALLOW_NULL_AUTHTOK as u32; // Flag used for `set_credentials`. /// Set user credentials for an authentication service. const ESTABLISH_CREDENTIALS = pam_ffi::PAM_ESTABLISH_CRED as u32; /// Delete user credentials associated with /// an authentication service. const DELETE_CREDENTIALS = pam_ffi::PAM_DELETE_CRED as u32; /// Reinitialize user credentials. const REINITIALIZE_CREDENTIALS = pam_ffi::PAM_REINITIALIZE_CRED as u32; /// Extend the lifetime of user credentials. const REFRESH_CREDENTIALS = pam_ffi::PAM_REFRESH_CRED as u32; // 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 = pam_ffi::PAM_CHANGE_EXPIRED_AUTHTOK as u32; /// 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 = pam_ffi::PAM_PRELIM_CHECK as u32; /// 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 = pam_ffi::PAM_UPDATE_AUTHTOK as u32; } } /// The Linux-PAM error return values. Success is an Ok [Result]. /// /// Most abbreviations (except `AuthTok` and `Max`) are now full words. /// For more detailed information, see /// `/usr/include/security/_pam_types.h`. #[allow(non_camel_case_types, dead_code)] #[derive(Copy, Clone, Debug, PartialEq, thiserror::Error, TryFromPrimitive, IntoPrimitive)] #[non_exhaustive] // C might give us anything! #[repr(u32)] pub enum ErrorCode { #[error("dlopen() failure when dynamically loading a service module")] OpenError = pam_ffi::PAM_OPEN_ERR, #[error("symbol not found")] SymbolError = pam_ffi::PAM_SYMBOL_ERR, #[error("error in service module")] ServiceError = pam_ffi::PAM_SERVICE_ERR, #[error("system error")] SystemError = pam_ffi::PAM_SYSTEM_ERR, #[error("memory buffer error")] BufferError = pam_ffi::PAM_BUF_ERR, #[error("permission denied")] PermissionDenied = pam_ffi::PAM_PERM_DENIED, #[error("authentication failure")] AuthenticationError = pam_ffi::PAM_AUTH_ERR, #[error("cannot access authentication data due to insufficient credentials")] CredentialsInsufficient = pam_ffi::PAM_CRED_INSUFFICIENT, #[error("underlying authentication service cannot retrieve authentication information")] AuthInfoUnavailable = pam_ffi::PAM_AUTHINFO_UNAVAIL, #[error("user not known to the underlying authentication module")] UserUnknown = pam_ffi::PAM_USER_UNKNOWN, #[error("retry limit reached; do not attempt further")] MaxTries = pam_ffi::PAM_MAXTRIES, #[error("new authentication token required")] NewAuthTokRequired = pam_ffi::PAM_NEW_AUTHTOK_REQD, #[error("user account has expired")] AccountExpired = pam_ffi::PAM_ACCT_EXPIRED, #[error("cannot make/remove an entry for the specified session")] SessionError = pam_ffi::PAM_SESSION_ERR, #[error("underlying authentication service cannot retrieve user credentials")] CredentialsUnavailable = pam_ffi::PAM_CRED_UNAVAIL, #[error("user credentials expired")] CredentialsExpired = pam_ffi::PAM_CRED_EXPIRED, #[error("failure setting user credentials")] CredentialsError = pam_ffi::PAM_CRED_ERR, #[error("no module-specific data is present")] NoModuleData = pam_ffi::PAM_NO_MODULE_DATA, #[error("conversation error")] ConversationError = pam_ffi::PAM_CONV_ERR, #[error("authentication token manipulation error")] AuthTokError = pam_ffi::PAM_AUTHTOK_ERR, #[error("authentication information cannot be recovered")] AuthTokRecoveryError = pam_ffi::PAM_AUTHTOK_RECOVERY_ERR, #[error("authentication token lock busy")] AuthTokLockBusy = pam_ffi::PAM_AUTHTOK_LOCK_BUSY, #[error("authentication token aging disabled")] AuthTokDisableAging = pam_ffi::PAM_AUTHTOK_DISABLE_AGING, #[error("preliminary password check failed")] TryAgain = pam_ffi::PAM_TRY_AGAIN, #[error("ignore underlying account module, regardless of control flag")] Ignore = pam_ffi::PAM_IGNORE, #[error("critical error; this module should fail now")] Abort = pam_ffi::PAM_ABORT, #[error("authentication token has expired")] AuthTokExpired = pam_ffi::PAM_AUTHTOK_EXPIRED, #[error("module is not known")] ModuleUnknown = pam_ffi::PAM_MODULE_UNKNOWN, #[error("bad item passed to pam_[whatever]_item")] BadItem = pam_ffi::PAM_BAD_ITEM, #[cfg(feature = "linux-pam-extensions")] #[error("conversation function is event-driven and data is not available yet")] ConversationAgain = pam_ffi::PAM_CONV_AGAIN, #[cfg(feature = "linux-pam-extensions")] #[error("call this function again to complete authentication stack")] Incomplete = pam_ffi::PAM_INCOMPLETE, } /// A PAM-specific Result type with an [ErrorCode] error. pub type Result<T> = StdResult<T, 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) => u32::from(otherwise) as i32, } } /// 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 as u32).try_into().unwrap_or(Self::SystemError)), } } } /// Returned when text that should not have any `\0` bytes in it does. /// Analogous to [`std::ffi::NulError`], but the data it was created from /// is borrowed. #[cfg(test)] mod tests { use super::*; #[test] fn test_enums() { assert_eq!(Ok(()), ErrorCode::result_from(0)); assert_eq!( pam_ffi::PAM_BAD_ITEM as i32, ErrorCode::result_to_c::<()>(Err(ErrorCode::BadItem)) ); assert_eq!( Err(ErrorCode::Abort), ErrorCode::result_from(pam_ffi::PAM_ABORT as i32) ); assert_eq!(Err(ErrorCode::SystemError), ErrorCode::result_from(423)); } }