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 }