Mercurial > crates > nonstick
diff src/constants.rs @ 166:2f5913131295
Separate flag/action flags into flags and action.
This also individualizes the type of flag for each PAM function,
so that you can only call a function with the right flags and values.
author | Paul Fisher <paul@pfish.zone> |
---|---|
date | Tue, 15 Jul 2025 00:32:24 -0400 |
parents | 4b3a5095f68c |
children | f052e2417195 |
line wrap: on
line diff
--- a/src/constants.rs Mon Jul 14 18:56:55 2025 -0400 +++ b/src/constants.rs Tue Jul 15 00:32:24 2025 -0400 @@ -2,6 +2,7 @@ use crate::_doc::{linklist, man7, manbsd, xsso}; use bitflags::bitflags; +use libpam_sys_consts::constants; use num_enum::{IntoPrimitive, TryFromPrimitive}; use std::error::Error; use std::ffi::c_int; @@ -9,99 +10,142 @@ 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_constants { - pub use libpam_sys_consts::constants::*; - - 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)+}; +/// Creates a bitflags! macro, with an extra SILENT element. +macro_rules! pam_flags { + ( + $(#[$m:meta])* + $name:ident { + $($inner:tt)* + } + ) => { + bitflags! { + $(#[$m])* + #[derive(Clone, Copy, Debug, Default, PartialEq)] + #[repr(transparent)] + pub struct $name: c_int { + /// The module should not generate any messages. + const SILENT = constants::PAM_SILENT; + $($inner)* + } + } } +} - define!( - /// A fictitious constant for testing purposes. - #[cfg(not(pam_impl = "OpenPam"))] - PAM_BAD_CONSTANT = 513; - PAM_BAD_FEATURE = 514; - ); +pam_flags! { + /// Flags for authentication and account management. + AuthnFlags { + /// The module should return [AuthError](ErrorCode::AuthError) + /// if the user has an empty authentication token, rather than + /// allowing them to log in. + const DISALLOW_NULL_AUTHTOK = constants::PAM_DISALLOW_NULL_AUTHTOK; + } +} - define!( - /// A fictitious constant for testing purposes. - #[cfg(not(any(pam_impl = "LinuxPam", pam_impl = "OpenPam")))] - PAM_BAD_ITEM = 515; - PAM_MODULE_UNKNOWN = 516; - ); +pam_flags! { + /// Flags for changing the authentication token. + AuthtokFlags { + /// Indicates that the user's authentication token should + /// only be changed if it is expired. If not passed, + /// the authentication token should be changed unconditionally. + const CHANGE_EXPIRED_AUTHTOK = constants::PAM_CHANGE_EXPIRED_AUTHTOK; - define!( - /// A fictitious constant for testing purposes. - #[cfg(not(pam_impl = "LinuxPam"))] - PAM_CONV_AGAIN = 517; - PAM_INCOMPLETE = 518; - ); + /// Don't check if the password is any good (Sun only). + #[cfg(pam_impl = "Sun")] + const NO_AUTHTOK_CHECK = constants::PAM_NO_AUTHTOK_CHECK; + } +} + +pam_flags! { + /// Common flag(s) shared by all PAM actions. + BaseFlags {} } -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 = pam_constants::PAM_SILENT; +#[cfg(feature = "openpam-ext")] +const BAD_CONST: ErrorCode = ErrorCode::BadConstant; +#[cfg(not(feature = "openpam-ext"))] +const BAD_CONST: ErrorCode = ErrorCode::SystemError; - /// The module should return [ErrorCode::AuthError] - /// if the user has an empty authentication token - /// rather than immediately accepting them. - const DISALLOW_NULL_AUTHTOK = pam_constants::PAM_DISALLOW_NULL_AUTHTOK; +macro_rules! flag_enum { + ( + $(#[$m:meta])* + $name:ident { + $( + $(#[$item_m:meta])* + $item_name:ident = $item_value:expr, + )* + } + ) => { + $(#[$m])* + #[derive(Clone, Copy, Debug, PartialEq, TryFromPrimitive, IntoPrimitive)] + #[repr(i32)] + pub enum $name { + $( + $(#[$item_m])* + $item_name = $item_value, + )* + } - // Flag used for `set_credentials`. + impl $name { + const ALL_VALUES: i32 = 0 $( | $item_value)*; + + fn split(value: i32) -> Result<(Option<Self>, i32)> { + let me = value & Self::ALL_VALUES; + let them = value & !Self::ALL_VALUES; + let me = match me { + 0 => None, + n => Some(Self::try_from(n).map_err(|_| BAD_CONST)?), + }; + Ok((me, them)) + } + } + } +} - /// Set user credentials for an authentication service. - const ESTABLISH_CREDENTIALS = pam_constants::PAM_ESTABLISH_CRED; - /// Delete user credentials associated with - /// an authentication service. - const DELETE_CREDENTIALS = pam_constants::PAM_DELETE_CRED; - /// Reinitialize user credentials. - const REINITIALIZE_CREDENTIALS = pam_constants::PAM_REINITIALIZE_CRED; - /// Extend the lifetime of user credentials. - const REFRESH_CREDENTIALS = pam_constants::PAM_REFRESH_CRED; +flag_enum! { + /// The credential management action that should take place. + CredAction { + /// Set the user's credentials from this module. Default if unspecified. + Establish = constants::PAM_ESTABLISH_CRED, + /// Revoke the user's credentials established by this module. + Delete = constants::PAM_DELETE_CRED, + /// Fully reinitialize the user's credentials from this module. + Reinitialize = constants::PAM_REINITIALIZE_CRED, + /// Extend the lifetime of the user's credentials from this module. + Refresh = constants::PAM_REFRESH_CRED, + } +} - // Flags used for password changing. +impl CredAction { + /// Separates this enum from the remaining [`BaseFlags`]. + pub fn extract(value: i32) -> Result<(Self, BaseFlags)> { + Self::split(value) + .map(|(act, rest)| (act.unwrap_or_default(), BaseFlags::from_bits_retain(rest))) + } +} - /// 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_constants::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 = pam_constants::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 = pam_constants::PAM_UPDATE_AUTHTOK; +impl Default for CredAction { + fn default() -> Self { + Self::Establish + } +} + +flag_enum! { + AuthtokAction { + /// This is a preliminary call to check if we're ready to change passwords + /// and that the new password is acceptable. + PreliminaryCheck = constants::PAM_PRELIM_CHECK, + /// You should actually update the password. + Update = constants::PAM_UPDATE_AUTHTOK, + } +} + +impl AuthtokAction { + /// Separates this enum from the remaining [`AuthtokFlags`]. + pub fn extract(value: i32) -> Result<(Self, AuthtokFlags)> { + match Self::split(value)? { + (Some(act), rest) => Ok((act, AuthtokFlags::from_bits_retain(rest))), + (None, _) => Err(BAD_CONST), + } } } @@ -124,41 +168,49 @@ #[non_exhaustive] // C might give us anything! #[repr(i32)] pub enum ErrorCode { - OpenError = pam_constants::PAM_OPEN_ERR, - SymbolError = pam_constants::PAM_SYMBOL_ERR, - ServiceError = pam_constants::PAM_SERVICE_ERR, - SystemError = pam_constants::PAM_SYSTEM_ERR, - BufferError = pam_constants::PAM_BUF_ERR, - PermissionDenied = pam_constants::PAM_PERM_DENIED, - AuthenticationError = pam_constants::PAM_AUTH_ERR, - CredentialsInsufficient = pam_constants::PAM_CRED_INSUFFICIENT, - AuthInfoUnavailable = pam_constants::PAM_AUTHINFO_UNAVAIL, - UserUnknown = pam_constants::PAM_USER_UNKNOWN, - MaxTries = pam_constants::PAM_MAXTRIES, - NewAuthTokRequired = pam_constants::PAM_NEW_AUTHTOK_REQD, - AccountExpired = pam_constants::PAM_ACCT_EXPIRED, - SessionError = pam_constants::PAM_SESSION_ERR, - CredentialsUnavailable = pam_constants::PAM_CRED_UNAVAIL, - CredentialsExpired = pam_constants::PAM_CRED_EXPIRED, - CredentialsError = pam_constants::PAM_CRED_ERR, - NoModuleData = pam_constants::PAM_NO_MODULE_DATA, - ConversationError = pam_constants::PAM_CONV_ERR, - AuthTokError = pam_constants::PAM_AUTHTOK_ERR, - AuthTokRecoveryError = pam_constants::PAM_AUTHTOK_RECOVERY_ERR, - AuthTokLockBusy = pam_constants::PAM_AUTHTOK_LOCK_BUSY, - AuthTokDisableAging = pam_constants::PAM_AUTHTOK_DISABLE_AGING, - TryAgain = pam_constants::PAM_TRY_AGAIN, - Ignore = pam_constants::PAM_IGNORE, - Abort = pam_constants::PAM_ABORT, - AuthTokExpired = pam_constants::PAM_AUTHTOK_EXPIRED, + OpenError = constants::PAM_OPEN_ERR, + SymbolError = constants::PAM_SYMBOL_ERR, + ServiceError = constants::PAM_SERVICE_ERR, + SystemError = constants::PAM_SYSTEM_ERR, + BufferError = constants::PAM_BUF_ERR, + PermissionDenied = constants::PAM_PERM_DENIED, + AuthenticationError = constants::PAM_AUTH_ERR, + CredentialsInsufficient = constants::PAM_CRED_INSUFFICIENT, + AuthInfoUnavailable = constants::PAM_AUTHINFO_UNAVAIL, + UserUnknown = constants::PAM_USER_UNKNOWN, + MaxTries = constants::PAM_MAXTRIES, + NewAuthTokRequired = constants::PAM_NEW_AUTHTOK_REQD, + AccountExpired = constants::PAM_ACCT_EXPIRED, + SessionError = constants::PAM_SESSION_ERR, + CredentialsUnavailable = constants::PAM_CRED_UNAVAIL, + CredentialsExpired = constants::PAM_CRED_EXPIRED, + CredentialsError = constants::PAM_CRED_ERR, + NoModuleData = constants::PAM_NO_MODULE_DATA, + ConversationError = constants::PAM_CONV_ERR, + AuthTokError = constants::PAM_AUTHTOK_ERR, + AuthTokRecoveryError = constants::PAM_AUTHTOK_RECOVERY_ERR, + AuthTokLockBusy = constants::PAM_AUTHTOK_LOCK_BUSY, + AuthTokDisableAging = constants::PAM_AUTHTOK_DISABLE_AGING, + TryAgain = constants::PAM_TRY_AGAIN, + Ignore = constants::PAM_IGNORE, + Abort = constants::PAM_ABORT, + AuthTokExpired = constants::PAM_AUTHTOK_EXPIRED, #[cfg(feature = "basic-ext")] - ModuleUnknown = pam_constants::PAM_MODULE_UNKNOWN, + ModuleUnknown = constants::PAM_MODULE_UNKNOWN, #[cfg(feature = "basic-ext")] - BadItem = pam_constants::PAM_BAD_ITEM, + BadItem = constants::PAM_BAD_ITEM, + #[cfg(feature = "linux-pam-ext")] + ConversationAgain = constants::PAM_CONV_AGAIN, #[cfg(feature = "linux-pam-ext")] - ConversationAgain = pam_constants::PAM_CONV_AGAIN, - #[cfg(feature = "linux-pam-ext")] - Incomplete = pam_constants::PAM_INCOMPLETE, + Incomplete = constants::PAM_INCOMPLETE, + #[cfg(feature = "openpam-ext")] + DomainUnknown = constants::PAM_DOMAIN_UNKNOWN, + #[cfg(feature = "openpam-ext")] + BadHandle = constants::PAM_BAD_HANDLE, + #[cfg(feature = "openpam-ext")] + BadFeature = constants::PAM_BAD_FEATURE, + #[cfg(feature = "openpam-ext")] + BadConstant = constants::PAM_BAD_CONSTANT, } /// A PAM-specific Result type with an [ErrorCode] error. @@ -228,13 +280,35 @@ fn test_enums() { assert_eq!(Ok(()), ErrorCode::result_from(0)); assert_eq!( - pam_constants::PAM_SESSION_ERR, + constants::PAM_SESSION_ERR, ErrorCode::result_to_c::<()>(Err(ErrorCode::SessionError)) ); assert_eq!( Err(ErrorCode::Abort), - ErrorCode::result_from(pam_constants::PAM_ABORT) + ErrorCode::result_from(constants::PAM_ABORT) ); assert_eq!(Err(ErrorCode::SystemError), ErrorCode::result_from(423)); } + + #[test] + fn test_flag_enums() { + AuthtokAction::extract(-1).expect_err("too many set"); + AuthtokAction::extract(0).expect_err("too few set"); + assert_eq!( + Ok(( + AuthtokAction::Update, + AuthtokFlags::from_bits_retain(0x7fff0000) + )), + AuthtokAction::extract(0x7fff0000 | constants::PAM_UPDATE_AUTHTOK) + ); + CredAction::extract(0xffff).expect_err("too many set"); + assert_eq!( + Ok((CredAction::Establish, BaseFlags::empty())), + CredAction::extract(0) + ); + assert_eq!( + Ok((CredAction::Delete, BaseFlags::from_bits_retain(0x55000000))), + CredAction::extract(0x55000000 | constants::PAM_DELETE_CRED) + ); + } }