Mercurial > crates > nonstick
comparison 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 |
comparison
equal
deleted
inserted
replaced
| 165:c4b1e280463c | 166:2f5913131295 |
|---|---|
| 1 //! Constants and enum values from the PAM library. | 1 //! Constants and enum values from the PAM library. |
| 2 | 2 |
| 3 use crate::_doc::{linklist, man7, manbsd, xsso}; | 3 use crate::_doc::{linklist, man7, manbsd, xsso}; |
| 4 use bitflags::bitflags; | 4 use bitflags::bitflags; |
| 5 use libpam_sys_consts::constants; | |
| 5 use num_enum::{IntoPrimitive, TryFromPrimitive}; | 6 use num_enum::{IntoPrimitive, TryFromPrimitive}; |
| 6 use std::error::Error; | 7 use std::error::Error; |
| 7 use std::ffi::c_int; | 8 use std::ffi::c_int; |
| 8 use std::fmt; | 9 use std::fmt; |
| 9 use std::fmt::{Display, Formatter}; | 10 use std::fmt::{Display, Formatter}; |
| 10 use std::result::Result as StdResult; | 11 use std::result::Result as StdResult; |
| 11 | 12 |
| 12 /// Values for constants not provided by certain PAM implementations. | 13 /// Creates a bitflags! macro, with an extra SILENT element. |
| 13 /// | 14 macro_rules! pam_flags { |
| 14 /// **The values of these constants are deliberately selected _not_ to match | 15 ( |
| 15 /// any PAM implementations. Applications should always use the symbolic value | 16 $(#[$m:meta])* |
| 16 /// and not a magic number.** | 17 $name:ident { |
| 17 mod pam_constants { | 18 $($inner:tt)* |
| 18 pub use libpam_sys_consts::constants::*; | 19 } |
| 19 | 20 ) => { |
| 20 macro_rules! define { | 21 bitflags! { |
| 21 ($(#[$attr:meta])* $($name:ident = $value:expr;)+) => { | 22 $(#[$m])* |
| 22 define!( | 23 #[derive(Clone, Copy, Debug, Default, PartialEq)] |
| 23 @meta { $(#[$attr])* } | 24 #[repr(transparent)] |
| 24 $(pub const $name: i32 = $value;)+ | 25 pub struct $name: c_int { |
| 25 ); | 26 /// The module should not generate any messages. |
| 26 }; | 27 const SILENT = constants::PAM_SILENT; |
| 27 (@meta $m:tt $($i:item)+) => { define!(@expand $($m $i)+); }; | 28 $($inner)* |
| 28 (@expand $({ $(#[$m:meta])* } $i:item)+) => {$($(#[$m])* $i)+}; | 29 } |
| 29 } | 30 } |
| 30 | 31 } |
| 31 define!( | 32 } |
| 32 /// A fictitious constant for testing purposes. | 33 |
| 33 #[cfg(not(pam_impl = "OpenPam"))] | 34 pam_flags! { |
| 34 PAM_BAD_CONSTANT = 513; | 35 /// Flags for authentication and account management. |
| 35 PAM_BAD_FEATURE = 514; | 36 AuthnFlags { |
| 36 ); | 37 /// The module should return [AuthError](ErrorCode::AuthError) |
| 37 | 38 /// if the user has an empty authentication token, rather than |
| 38 define!( | 39 /// allowing them to log in. |
| 39 /// A fictitious constant for testing purposes. | 40 const DISALLOW_NULL_AUTHTOK = constants::PAM_DISALLOW_NULL_AUTHTOK; |
| 40 #[cfg(not(any(pam_impl = "LinuxPam", pam_impl = "OpenPam")))] | 41 } |
| 41 PAM_BAD_ITEM = 515; | 42 } |
| 42 PAM_MODULE_UNKNOWN = 516; | 43 |
| 43 ); | 44 pam_flags! { |
| 44 | 45 /// Flags for changing the authentication token. |
| 45 define!( | 46 AuthtokFlags { |
| 46 /// A fictitious constant for testing purposes. | 47 /// Indicates that the user's authentication token should |
| 47 #[cfg(not(pam_impl = "LinuxPam"))] | 48 /// only be changed if it is expired. If not passed, |
| 48 PAM_CONV_AGAIN = 517; | 49 /// the authentication token should be changed unconditionally. |
| 49 PAM_INCOMPLETE = 518; | 50 const CHANGE_EXPIRED_AUTHTOK = constants::PAM_CHANGE_EXPIRED_AUTHTOK; |
| 50 ); | 51 |
| 51 } | 52 /// Don't check if the password is any good (Sun only). |
| 52 | 53 #[cfg(pam_impl = "Sun")] |
| 53 bitflags! { | 54 const NO_AUTHTOK_CHECK = constants::PAM_NO_AUTHTOK_CHECK; |
| 54 /// The available PAM flags. | 55 } |
| 55 /// | 56 } |
| 56 /// See `/usr/include/security/_pam_types.h` and | 57 |
| 57 /// See `/usr/include/security/pam_modules.h` for more details. | 58 pam_flags! { |
| 58 #[derive(Debug, Default, PartialEq)] | 59 /// Common flag(s) shared by all PAM actions. |
| 59 #[repr(transparent)] | 60 BaseFlags {} |
| 60 pub struct Flags: c_int { | 61 } |
| 61 /// The module should not generate any messages. | 62 |
| 62 const SILENT = pam_constants::PAM_SILENT; | 63 #[cfg(feature = "openpam-ext")] |
| 63 | 64 const BAD_CONST: ErrorCode = ErrorCode::BadConstant; |
| 64 /// The module should return [ErrorCode::AuthError] | 65 #[cfg(not(feature = "openpam-ext"))] |
| 65 /// if the user has an empty authentication token | 66 const BAD_CONST: ErrorCode = ErrorCode::SystemError; |
| 66 /// rather than immediately accepting them. | 67 |
| 67 const DISALLOW_NULL_AUTHTOK = pam_constants::PAM_DISALLOW_NULL_AUTHTOK; | 68 macro_rules! flag_enum { |
| 68 | 69 ( |
| 69 // Flag used for `set_credentials`. | 70 $(#[$m:meta])* |
| 70 | 71 $name:ident { |
| 71 /// Set user credentials for an authentication service. | 72 $( |
| 72 const ESTABLISH_CREDENTIALS = pam_constants::PAM_ESTABLISH_CRED; | 73 $(#[$item_m:meta])* |
| 73 /// Delete user credentials associated with | 74 $item_name:ident = $item_value:expr, |
| 74 /// an authentication service. | 75 )* |
| 75 const DELETE_CREDENTIALS = pam_constants::PAM_DELETE_CRED; | 76 } |
| 76 /// Reinitialize user credentials. | 77 ) => { |
| 77 const REINITIALIZE_CREDENTIALS = pam_constants::PAM_REINITIALIZE_CRED; | 78 $(#[$m])* |
| 78 /// Extend the lifetime of user credentials. | 79 #[derive(Clone, Copy, Debug, PartialEq, TryFromPrimitive, IntoPrimitive)] |
| 79 const REFRESH_CREDENTIALS = pam_constants::PAM_REFRESH_CRED; | 80 #[repr(i32)] |
| 80 | 81 pub enum $name { |
| 81 // Flags used for password changing. | 82 $( |
| 82 | 83 $(#[$item_m])* |
| 83 /// The password service should only update those passwords | 84 $item_name = $item_value, |
| 84 /// that have aged. If this flag is _not_ passed, | 85 )* |
| 85 /// the password service should update all passwords. | 86 } |
| 86 /// | 87 |
| 87 /// This flag is only used by `change_authtok`. | 88 impl $name { |
| 88 const CHANGE_EXPIRED_AUTHTOK = pam_constants::PAM_CHANGE_EXPIRED_AUTHTOK; | 89 const ALL_VALUES: i32 = 0 $( | $item_value)*; |
| 89 /// This is a preliminary check for password changing. | 90 |
| 90 /// The password should not be changed. | 91 fn split(value: i32) -> Result<(Option<Self>, i32)> { |
| 91 /// | 92 let me = value & Self::ALL_VALUES; |
| 92 /// This is only used between PAM and a module. | 93 let them = value & !Self::ALL_VALUES; |
| 93 /// Applications may not use this flag. | 94 let me = match me { |
| 94 /// | 95 0 => None, |
| 95 /// This flag is only used by `change_authtok`. | 96 n => Some(Self::try_from(n).map_err(|_| BAD_CONST)?), |
| 96 const PRELIMINARY_CHECK = pam_constants::PAM_PRELIM_CHECK; | 97 }; |
| 97 /// The password should actuallyPR be updated. | 98 Ok((me, them)) |
| 98 /// This and [Self::PRELIMINARY_CHECK] are mutually exclusive. | 99 } |
| 99 /// | 100 } |
| 100 /// This is only used between PAM and a module. | 101 } |
| 101 /// Applications may not use this flag. | 102 } |
| 102 /// | 103 |
| 103 /// This flag is only used by `change_authtok`. | 104 flag_enum! { |
| 104 const UPDATE_AUTHTOK = pam_constants::PAM_UPDATE_AUTHTOK; | 105 /// The credential management action that should take place. |
| 106 CredAction { | |
| 107 /// Set the user's credentials from this module. Default if unspecified. | |
| 108 Establish = constants::PAM_ESTABLISH_CRED, | |
| 109 /// Revoke the user's credentials established by this module. | |
| 110 Delete = constants::PAM_DELETE_CRED, | |
| 111 /// Fully reinitialize the user's credentials from this module. | |
| 112 Reinitialize = constants::PAM_REINITIALIZE_CRED, | |
| 113 /// Extend the lifetime of the user's credentials from this module. | |
| 114 Refresh = constants::PAM_REFRESH_CRED, | |
| 115 } | |
| 116 } | |
| 117 | |
| 118 impl CredAction { | |
| 119 /// Separates this enum from the remaining [`BaseFlags`]. | |
| 120 pub fn extract(value: i32) -> Result<(Self, BaseFlags)> { | |
| 121 Self::split(value) | |
| 122 .map(|(act, rest)| (act.unwrap_or_default(), BaseFlags::from_bits_retain(rest))) | |
| 123 } | |
| 124 } | |
| 125 | |
| 126 impl Default for CredAction { | |
| 127 fn default() -> Self { | |
| 128 Self::Establish | |
| 129 } | |
| 130 } | |
| 131 | |
| 132 flag_enum! { | |
| 133 AuthtokAction { | |
| 134 /// This is a preliminary call to check if we're ready to change passwords | |
| 135 /// and that the new password is acceptable. | |
| 136 PreliminaryCheck = constants::PAM_PRELIM_CHECK, | |
| 137 /// You should actually update the password. | |
| 138 Update = constants::PAM_UPDATE_AUTHTOK, | |
| 139 } | |
| 140 } | |
| 141 | |
| 142 impl AuthtokAction { | |
| 143 /// Separates this enum from the remaining [`AuthtokFlags`]. | |
| 144 pub fn extract(value: i32) -> Result<(Self, AuthtokFlags)> { | |
| 145 match Self::split(value)? { | |
| 146 (Some(act), rest) => Ok((act, AuthtokFlags::from_bits_retain(rest))), | |
| 147 (None, _) => Err(BAD_CONST), | |
| 148 } | |
| 105 } | 149 } |
| 106 } | 150 } |
| 107 | 151 |
| 108 /// The PAM error return codes. | 152 /// The PAM error return codes. |
| 109 /// | 153 /// |
| 122 #[allow(non_camel_case_types, dead_code)] | 166 #[allow(non_camel_case_types, dead_code)] |
| 123 #[derive(Copy, Clone, Debug, PartialEq, TryFromPrimitive, IntoPrimitive)] | 167 #[derive(Copy, Clone, Debug, PartialEq, TryFromPrimitive, IntoPrimitive)] |
| 124 #[non_exhaustive] // C might give us anything! | 168 #[non_exhaustive] // C might give us anything! |
| 125 #[repr(i32)] | 169 #[repr(i32)] |
| 126 pub enum ErrorCode { | 170 pub enum ErrorCode { |
| 127 OpenError = pam_constants::PAM_OPEN_ERR, | 171 OpenError = constants::PAM_OPEN_ERR, |
| 128 SymbolError = pam_constants::PAM_SYMBOL_ERR, | 172 SymbolError = constants::PAM_SYMBOL_ERR, |
| 129 ServiceError = pam_constants::PAM_SERVICE_ERR, | 173 ServiceError = constants::PAM_SERVICE_ERR, |
| 130 SystemError = pam_constants::PAM_SYSTEM_ERR, | 174 SystemError = constants::PAM_SYSTEM_ERR, |
| 131 BufferError = pam_constants::PAM_BUF_ERR, | 175 BufferError = constants::PAM_BUF_ERR, |
| 132 PermissionDenied = pam_constants::PAM_PERM_DENIED, | 176 PermissionDenied = constants::PAM_PERM_DENIED, |
| 133 AuthenticationError = pam_constants::PAM_AUTH_ERR, | 177 AuthenticationError = constants::PAM_AUTH_ERR, |
| 134 CredentialsInsufficient = pam_constants::PAM_CRED_INSUFFICIENT, | 178 CredentialsInsufficient = constants::PAM_CRED_INSUFFICIENT, |
| 135 AuthInfoUnavailable = pam_constants::PAM_AUTHINFO_UNAVAIL, | 179 AuthInfoUnavailable = constants::PAM_AUTHINFO_UNAVAIL, |
| 136 UserUnknown = pam_constants::PAM_USER_UNKNOWN, | 180 UserUnknown = constants::PAM_USER_UNKNOWN, |
| 137 MaxTries = pam_constants::PAM_MAXTRIES, | 181 MaxTries = constants::PAM_MAXTRIES, |
| 138 NewAuthTokRequired = pam_constants::PAM_NEW_AUTHTOK_REQD, | 182 NewAuthTokRequired = constants::PAM_NEW_AUTHTOK_REQD, |
| 139 AccountExpired = pam_constants::PAM_ACCT_EXPIRED, | 183 AccountExpired = constants::PAM_ACCT_EXPIRED, |
| 140 SessionError = pam_constants::PAM_SESSION_ERR, | 184 SessionError = constants::PAM_SESSION_ERR, |
| 141 CredentialsUnavailable = pam_constants::PAM_CRED_UNAVAIL, | 185 CredentialsUnavailable = constants::PAM_CRED_UNAVAIL, |
| 142 CredentialsExpired = pam_constants::PAM_CRED_EXPIRED, | 186 CredentialsExpired = constants::PAM_CRED_EXPIRED, |
| 143 CredentialsError = pam_constants::PAM_CRED_ERR, | 187 CredentialsError = constants::PAM_CRED_ERR, |
| 144 NoModuleData = pam_constants::PAM_NO_MODULE_DATA, | 188 NoModuleData = constants::PAM_NO_MODULE_DATA, |
| 145 ConversationError = pam_constants::PAM_CONV_ERR, | 189 ConversationError = constants::PAM_CONV_ERR, |
| 146 AuthTokError = pam_constants::PAM_AUTHTOK_ERR, | 190 AuthTokError = constants::PAM_AUTHTOK_ERR, |
| 147 AuthTokRecoveryError = pam_constants::PAM_AUTHTOK_RECOVERY_ERR, | 191 AuthTokRecoveryError = constants::PAM_AUTHTOK_RECOVERY_ERR, |
| 148 AuthTokLockBusy = pam_constants::PAM_AUTHTOK_LOCK_BUSY, | 192 AuthTokLockBusy = constants::PAM_AUTHTOK_LOCK_BUSY, |
| 149 AuthTokDisableAging = pam_constants::PAM_AUTHTOK_DISABLE_AGING, | 193 AuthTokDisableAging = constants::PAM_AUTHTOK_DISABLE_AGING, |
| 150 TryAgain = pam_constants::PAM_TRY_AGAIN, | 194 TryAgain = constants::PAM_TRY_AGAIN, |
| 151 Ignore = pam_constants::PAM_IGNORE, | 195 Ignore = constants::PAM_IGNORE, |
| 152 Abort = pam_constants::PAM_ABORT, | 196 Abort = constants::PAM_ABORT, |
| 153 AuthTokExpired = pam_constants::PAM_AUTHTOK_EXPIRED, | 197 AuthTokExpired = constants::PAM_AUTHTOK_EXPIRED, |
| 154 #[cfg(feature = "basic-ext")] | 198 #[cfg(feature = "basic-ext")] |
| 155 ModuleUnknown = pam_constants::PAM_MODULE_UNKNOWN, | 199 ModuleUnknown = constants::PAM_MODULE_UNKNOWN, |
| 156 #[cfg(feature = "basic-ext")] | 200 #[cfg(feature = "basic-ext")] |
| 157 BadItem = pam_constants::PAM_BAD_ITEM, | 201 BadItem = constants::PAM_BAD_ITEM, |
| 158 #[cfg(feature = "linux-pam-ext")] | 202 #[cfg(feature = "linux-pam-ext")] |
| 159 ConversationAgain = pam_constants::PAM_CONV_AGAIN, | 203 ConversationAgain = constants::PAM_CONV_AGAIN, |
| 160 #[cfg(feature = "linux-pam-ext")] | 204 #[cfg(feature = "linux-pam-ext")] |
| 161 Incomplete = pam_constants::PAM_INCOMPLETE, | 205 Incomplete = constants::PAM_INCOMPLETE, |
| 206 #[cfg(feature = "openpam-ext")] | |
| 207 DomainUnknown = constants::PAM_DOMAIN_UNKNOWN, | |
| 208 #[cfg(feature = "openpam-ext")] | |
| 209 BadHandle = constants::PAM_BAD_HANDLE, | |
| 210 #[cfg(feature = "openpam-ext")] | |
| 211 BadFeature = constants::PAM_BAD_FEATURE, | |
| 212 #[cfg(feature = "openpam-ext")] | |
| 213 BadConstant = constants::PAM_BAD_CONSTANT, | |
| 162 } | 214 } |
| 163 | 215 |
| 164 /// A PAM-specific Result type with an [ErrorCode] error. | 216 /// A PAM-specific Result type with an [ErrorCode] error. |
| 165 pub type Result<T> = StdResult<T, ErrorCode>; | 217 pub type Result<T> = StdResult<T, ErrorCode>; |
| 166 | 218 |
| 226 | 278 |
| 227 #[test] | 279 #[test] |
| 228 fn test_enums() { | 280 fn test_enums() { |
| 229 assert_eq!(Ok(()), ErrorCode::result_from(0)); | 281 assert_eq!(Ok(()), ErrorCode::result_from(0)); |
| 230 assert_eq!( | 282 assert_eq!( |
| 231 pam_constants::PAM_SESSION_ERR, | 283 constants::PAM_SESSION_ERR, |
| 232 ErrorCode::result_to_c::<()>(Err(ErrorCode::SessionError)) | 284 ErrorCode::result_to_c::<()>(Err(ErrorCode::SessionError)) |
| 233 ); | 285 ); |
| 234 assert_eq!( | 286 assert_eq!( |
| 235 Err(ErrorCode::Abort), | 287 Err(ErrorCode::Abort), |
| 236 ErrorCode::result_from(pam_constants::PAM_ABORT) | 288 ErrorCode::result_from(constants::PAM_ABORT) |
| 237 ); | 289 ); |
| 238 assert_eq!(Err(ErrorCode::SystemError), ErrorCode::result_from(423)); | 290 assert_eq!(Err(ErrorCode::SystemError), ErrorCode::result_from(423)); |
| 239 } | 291 } |
| 240 } | 292 |
| 293 #[test] | |
| 294 fn test_flag_enums() { | |
| 295 AuthtokAction::extract(-1).expect_err("too many set"); | |
| 296 AuthtokAction::extract(0).expect_err("too few set"); | |
| 297 assert_eq!( | |
| 298 Ok(( | |
| 299 AuthtokAction::Update, | |
| 300 AuthtokFlags::from_bits_retain(0x7fff0000) | |
| 301 )), | |
| 302 AuthtokAction::extract(0x7fff0000 | constants::PAM_UPDATE_AUTHTOK) | |
| 303 ); | |
| 304 CredAction::extract(0xffff).expect_err("too many set"); | |
| 305 assert_eq!( | |
| 306 Ok((CredAction::Establish, BaseFlags::empty())), | |
| 307 CredAction::extract(0) | |
| 308 ); | |
| 309 assert_eq!( | |
| 310 Ok((CredAction::Delete, BaseFlags::from_bits_retain(0x55000000))), | |
| 311 CredAction::extract(0x55000000 | constants::PAM_DELETE_CRED) | |
| 312 ); | |
| 313 } | |
| 314 } |
