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 } |