Mercurial > crates > nonstick
comparison src/constants.rs @ 80:5aa1a010f1e8
Start using PAM headers; improve owned/borrowed distinction.
- Uses bindgen to generate bindings (only if needed).
- Gets the story together on owned vs. borrowed handles.
- Reduces number of mutable borrows in handle operation
(since `PamHandle` is neither `Send` nor `Sync`,
we never have to worry about thread safety.
- Improves a bunch of macros so we don't have our own
special syntax for docs.
- Implement question indirection for standard XSSO PAM implementations.
author | Paul Fisher <paul@pfish.zone> |
---|---|
date | Tue, 10 Jun 2025 01:09:30 -0400 |
parents | 351bdc13005e |
children | a638a45e5f1f |
comparison
equal
deleted
inserted
replaced
79:2128123b9406 | 80:5aa1a010f1e8 |
---|---|
1 //! Constants and enum values from the PAM library. | 1 //! Constants and enum values from the PAM library. |
2 | 2 |
3 #[cfg(feature = "link")] | |
4 use crate::libpam::pam_ffi; | |
3 use bitflags::bitflags; | 5 use bitflags::bitflags; |
4 use libc::{c_int, c_uint}; | 6 use libc::c_int; |
5 use num_derive::FromPrimitive; | 7 use num_enum::{IntoPrimitive, TryFromPrimitive}; |
6 use num_traits::FromPrimitive; | |
7 use std::any; | |
8 use std::marker::PhantomData; | |
9 use std::result::Result as StdResult; | 8 use std::result::Result as StdResult; |
9 | |
10 /// Arbitrary values for PAM constants when not linking against system PAM. | |
11 /// | |
12 /// **The values of these constants are deliberately selected _not_ to match | |
13 /// any PAM implementations. Applications should always use the symbolic value | |
14 /// and not a magic number.** | |
15 #[cfg(not(feature = "link"))] | |
16 mod ffi { | |
17 macro_rules! define { | |
18 ($(#[$attr:meta])* $($name:ident = $value:expr),+) => { | |
19 define!( | |
20 @meta { $(#[$attr])* } | |
21 $(pub const $name: i32 = $value;)+ | |
22 ); | |
23 }; | |
24 (@meta $m:tt $($i:item)+) => { define!(@expand $($m $i)+); }; | |
25 (@expand $({ $(#[$m:meta])* } $i:item)+) => {$($(#[$m])* $i)+}; | |
26 } | |
27 const fn bit(n: i8) -> i32 { | |
28 1 << n | |
29 } | |
30 define!( | |
31 PAM_SILENT = bit(13), | |
32 PAM_DISALLOW_NULL_AUTHTOK = bit(14), | |
33 PAM_ESTABLISH_CRED = bit(15), | |
34 PAM_DELETE_CRED = bit(16), | |
35 PAM_REINITIALIZE_CRED = bit(17), | |
36 PAM_REFRESH_CRED = bit(18), | |
37 PAM_CHANGE_EXPIRED_AUTHTOK = bit(19), | |
38 PAM_PRELIM_CHECK = bit(20), | |
39 PAM_UPDATE_AUTHTOK = bit(21) | |
40 ); | |
41 | |
42 define!( | |
43 PAM_ABORT = 513, | |
44 PAM_ACCT_EXPIRED = 514, | |
45 PAM_AUTHINFO_UNAVAIL = 515, | |
46 PAM_AUTHTOK_DISABLE_AGING = 516, | |
47 PAM_AUTHTOK_ERR = 517, | |
48 PAM_AUTHTOK_EXPIRED = 518, | |
49 PAM_AUTHTOK_LOCK_BUSY = 519, | |
50 PAM_AUTHTOK_RECOVERY_ERR = 520, | |
51 PAM_AUTH_ERR = 521, | |
52 PAM_BAD_ITEM = 522, | |
53 PAM_BUF_ERR = 533, | |
54 PAM_CONV_AGAIN = 534, | |
55 PAM_CONV_ERR = 535, | |
56 PAM_CRED_ERR = 536, | |
57 PAM_CRED_EXPIRED = 537, | |
58 PAM_CRED_INSUFFICIENT = 538, | |
59 PAM_CRED_UNAVAIL = 539, | |
60 PAM_IGNORE = 540, | |
61 PAM_INCOMPLETE = 541, | |
62 PAM_MAXTRIES = 542, | |
63 PAM_MODULE_UNKNOWN = 543, | |
64 PAM_NEW_AUTHTOK_REQD = 544, | |
65 PAM_NO_MODULE_DATA = 545, | |
66 PAM_OPEN_ERR = 546, | |
67 PAM_PERM_DENIED = 547, | |
68 PAM_SERVICE_ERR = 548, | |
69 PAM_SESSION_ERR = 549, | |
70 PAM_SYMBOL_ERR = 550, | |
71 PAM_SYSTEM_ERR = 551, | |
72 PAM_TRY_AGAIN = 552, | |
73 PAM_USER_UNKNOWN = 553 | |
74 ); | |
75 } | |
10 | 76 |
11 bitflags! { | 77 bitflags! { |
12 /// The available PAM flags. | 78 /// The available PAM flags. |
13 /// | 79 /// |
14 /// See `/usr/include/security/_pam_types.h` and | 80 /// See `/usr/include/security/_pam_types.h` and |
15 /// See `/usr/include/security/pam_modules.h` for more details. | 81 /// See `/usr/include/security/pam_modules.h` for more details. |
16 #[derive(Debug, PartialEq)] | 82 #[derive(Debug, PartialEq)] |
17 #[repr(transparent)] | 83 #[repr(transparent)] |
18 pub struct Flags: c_uint { | 84 pub struct Flags: c_int { |
19 /// The module should not generate any messages. | 85 /// The module should not generate any messages. |
20 const SILENT = 0x8000; | 86 const SILENT = pam_ffi::PAM_SILENT; |
21 | 87 |
22 /// The module should return [ErrorCode::AuthError] | 88 /// The module should return [ErrorCode::AuthError] |
23 /// if the user has an empty authentication token | 89 /// if the user has an empty authentication token |
24 /// rather than immediately accepting them. | 90 /// rather than immediately accepting them. |
25 const DISALLOW_NULL_AUTHTOK = 0x0001; | 91 const DISALLOW_NULL_AUTHTOK = pam_ffi::PAM_DISALLOW_NULL_AUTHTOK; |
26 | 92 |
27 // Flag used for `set_credentials`. | 93 // Flag used for `set_credentials`. |
28 | 94 |
29 /// Set user credentials for an authentication service. | 95 /// Set user credentials for an authentication service. |
30 const ESTABLISH_CREDENTIALS = 0x0002; | 96 const ESTABLISH_CREDENTIALS = pam_ffi::PAM_ESTABLISH_CRED; |
31 /// Delete user credentials associated with | 97 /// Delete user credentials associated with |
32 /// an authentication service. | 98 /// an authentication service. |
33 const DELETE_CREDENTIALS = 0x0004; | 99 const DELETE_CREDENTIALS = pam_ffi::PAM_DELETE_CRED; |
34 /// Reinitialize user credentials. | 100 /// Reinitialize user credentials. |
35 const REINITIALIZE_CREDENTIALS = 0x0008; | 101 const REINITIALIZE_CREDENTIALS = pam_ffi::PAM_REINITIALIZE_CRED; |
36 /// Extend the lifetime of user credentials. | 102 /// Extend the lifetime of user credentials. |
37 const REFRESH_CREDENTIALS = 0x0010; | 103 const REFRESH_CREDENTIALS = pam_ffi::PAM_REFRESH_CRED; |
38 | 104 |
39 // Flags used for password changing. | 105 // Flags used for password changing. |
40 | 106 |
41 /// The password service should only update those passwords | 107 /// The password service should only update those passwords |
42 /// that have aged. If this flag is _not_ passed, | 108 /// that have aged. If this flag is _not_ passed, |
43 /// the password service should update all passwords. | 109 /// the password service should update all passwords. |
44 /// | 110 /// |
45 /// This flag is only used by `change_authtok`. | 111 /// This flag is only used by `change_authtok`. |
46 const CHANGE_EXPIRED_AUTHTOK = 0x0020; | 112 const CHANGE_EXPIRED_AUTHTOK = pam_ffi::PAM_CHANGE_EXPIRED_AUTHTOK; |
47 | 113 |
48 /// This is a preliminary check for password changing. | 114 /// This is a preliminary check for password changing. |
49 /// The password should not be changed. | 115 /// The password should not be changed. |
50 /// | 116 /// |
51 /// This is only used between PAM and a module. | 117 /// This is only used between PAM and a module. |
52 /// Applications may not use this flag. | 118 /// Applications may not use this flag. |
53 /// | 119 /// |
54 /// This flag is only used by `change_authtok`. | 120 /// This flag is only used by `change_authtok`. |
55 const PRELIMINARY_CHECK = 0x4000; | 121 const PRELIMINARY_CHECK = pam_ffi::PAM_PRELIM_CHECK; |
56 /// The password should actuallyPR be updated. | 122 /// The password should actuallyPR be updated. |
57 /// This and [Self::PRELIMINARY_CHECK] are mutually exclusive. | 123 /// This and [Self::PRELIMINARY_CHECK] are mutually exclusive. |
58 /// | 124 /// |
59 /// This is only used between PAM and a module. | 125 /// This is only used between PAM and a module. |
60 /// Applications may not use this flag. | 126 /// Applications may not use this flag. |
61 /// | 127 /// |
62 /// This flag is only used by `change_authtok`. | 128 /// This flag is only used by `change_authtok`. |
63 const UPDATE_AUTHTOK = 0x2000; | 129 const UPDATE_AUTHTOK = pam_ffi::PAM_UPDATE_AUTHTOK; |
64 } | 130 } |
65 } | 131 } |
66 | 132 |
67 /// The Linux-PAM error return values. Success is an Ok [Result]. | 133 /// The Linux-PAM error return values. Success is an Ok [Result]. |
68 /// | 134 /// |
69 /// Most abbreviations (except `AuthTok` and `Max`) are now full words. | 135 /// Most abbreviations (except `AuthTok` and `Max`) are now full words. |
70 /// For more detailed information, see | 136 /// For more detailed information, see |
71 /// `/usr/include/security/_pam_types.h`. | 137 /// `/usr/include/security/_pam_types.h`. |
72 #[allow(non_camel_case_types, dead_code)] | 138 #[allow(non_camel_case_types, dead_code)] |
73 #[derive(Copy, Clone, Debug, PartialEq, thiserror::Error, FromPrimitive)] | 139 #[derive(Copy, Clone, Debug, PartialEq, thiserror::Error, TryFromPrimitive, IntoPrimitive)] |
74 #[non_exhaustive] // C might give us anything! | 140 #[non_exhaustive] // C might give us anything! |
141 #[repr(i32)] | |
75 pub enum ErrorCode { | 142 pub enum ErrorCode { |
76 #[error("dlopen() failure when dynamically loading a service module")] | 143 #[error("dlopen() failure when dynamically loading a service module")] |
77 OpenError = 1, | 144 OpenError = pam_ffi::PAM_OPEN_ERR, |
78 #[error("symbol not found")] | 145 #[error("symbol not found")] |
79 SymbolError = 2, | 146 SymbolError = pam_ffi::PAM_SYMBOL_ERR, |
80 #[error("error in service module")] | 147 #[error("error in service module")] |
81 ServiceError = 3, | 148 ServiceError = pam_ffi::PAM_SERVICE_ERR, |
82 #[error("system error")] | 149 #[error("system error")] |
83 SystemError = 4, | 150 SystemError = pam_ffi::PAM_SYSTEM_ERR, |
84 #[error("memory buffer error")] | 151 #[error("memory buffer error")] |
85 BufferError = 5, | 152 BufferError = pam_ffi::PAM_BUF_ERR, |
86 #[error("permission denied")] | 153 #[error("permission denied")] |
87 PermissionDenied = 6, | 154 PermissionDenied = pam_ffi::PAM_PERM_DENIED, |
88 #[error("authentication failure")] | 155 #[error("authentication failure")] |
89 AuthenticationError = 7, | 156 AuthenticationError = pam_ffi::PAM_AUTH_ERR, |
90 #[error("cannot access authentication data due to insufficient credentials")] | 157 #[error("cannot access authentication data due to insufficient credentials")] |
91 CredentialsInsufficient = 8, | 158 CredentialsInsufficient = pam_ffi::PAM_CRED_INSUFFICIENT, |
92 #[error("underlying authentication service cannot retrieve authentication information")] | 159 #[error("underlying authentication service cannot retrieve authentication information")] |
93 AuthInfoUnavailable = 9, | 160 AuthInfoUnavailable = pam_ffi::PAM_AUTHINFO_UNAVAIL, |
94 #[error("user not known to the underlying authentication module")] | 161 #[error("user not known to the underlying authentication module")] |
95 UserUnknown = 10, | 162 UserUnknown = pam_ffi::PAM_USER_UNKNOWN, |
96 #[error("retry limit reached; do not attempt further")] | 163 #[error("retry limit reached; do not attempt further")] |
97 MaxTries = 11, | 164 MaxTries = pam_ffi::PAM_MAXTRIES, |
98 #[error("new authentication token required")] | 165 #[error("new authentication token required")] |
99 NewAuthTokRequired = 12, | 166 NewAuthTokRequired = pam_ffi::PAM_NEW_AUTHTOK_REQD, |
100 #[error("user account has expired")] | 167 #[error("user account has expired")] |
101 AccountExpired = 13, | 168 AccountExpired = pam_ffi::PAM_ACCT_EXPIRED, |
102 #[error("cannot make/remove an entry for the specified session")] | 169 #[error("cannot make/remove an entry for the specified session")] |
103 SessionError = 14, | 170 SessionError = pam_ffi::PAM_SESSION_ERR, |
104 #[error("underlying authentication service cannot retrieve user credentials")] | 171 #[error("underlying authentication service cannot retrieve user credentials")] |
105 CredentialsUnavailable = 15, | 172 CredentialsUnavailable = pam_ffi::PAM_CRED_UNAVAIL, |
106 #[error("user credentials expired")] | 173 #[error("user credentials expired")] |
107 CredentialsExpired = 16, | 174 CredentialsExpired = pam_ffi::PAM_CRED_EXPIRED, |
108 #[error("failure setting user credentials")] | 175 #[error("failure setting user credentials")] |
109 CredentialsError = 17, | 176 CredentialsError = pam_ffi::PAM_CRED_ERR, |
110 #[error("no module-specific data is present")] | 177 #[error("no module-specific data is present")] |
111 NoModuleData = 18, | 178 NoModuleData = pam_ffi::PAM_NO_MODULE_DATA, |
112 #[error("conversation error")] | 179 #[error("conversation error")] |
113 ConversationError = 19, | 180 ConversationError = pam_ffi::PAM_CONV_ERR, |
114 #[error("authentication token manipulation error")] | 181 #[error("authentication token manipulation error")] |
115 AuthTokError = 20, | 182 AuthTokError = pam_ffi::PAM_AUTHTOK_ERR, |
116 #[error("authentication information cannot be recovered")] | 183 #[error("authentication information cannot be recovered")] |
117 AuthTokRecoveryError = 21, | 184 AuthTokRecoveryError = pam_ffi::PAM_AUTHTOK_RECOVERY_ERR, |
118 #[error("authentication token lock busy")] | 185 #[error("authentication token lock busy")] |
119 AuthTokLockBusy = 22, | 186 AuthTokLockBusy = pam_ffi::PAM_AUTHTOK_LOCK_BUSY, |
120 #[error("authentication token aging disabled")] | 187 #[error("authentication token aging disabled")] |
121 AuthTokDisableAging = 23, | 188 AuthTokDisableAging = pam_ffi::PAM_AUTHTOK_DISABLE_AGING, |
122 #[error("preliminary password check failed")] | 189 #[error("preliminary password check failed")] |
123 TryAgain = 24, | 190 TryAgain = pam_ffi::PAM_TRY_AGAIN, |
124 #[error("ignore underlying account module, regardless of control flag")] | 191 #[error("ignore underlying account module, regardless of control flag")] |
125 Ignore = 25, | 192 Ignore = pam_ffi::PAM_IGNORE, |
126 #[error("critical error; this module should fail now")] | 193 #[error("critical error; this module should fail now")] |
127 Abort = 26, | 194 Abort = pam_ffi::PAM_ABORT, |
128 #[error("authentication token has expired")] | 195 #[error("authentication token has expired")] |
129 AuthTokExpired = 27, | 196 AuthTokExpired = pam_ffi::PAM_AUTHTOK_EXPIRED, |
130 #[error("module is not known")] | 197 #[error("module is not known")] |
131 ModuleUnknown = 28, | 198 ModuleUnknown = pam_ffi::PAM_MODULE_UNKNOWN, |
132 #[error("bad item passed to pam_[whatever]_item")] | 199 #[error("bad item passed to pam_[whatever]_item")] |
133 BadItem = 29, | 200 BadItem = pam_ffi::PAM_BAD_ITEM, |
134 #[error("conversation function is event-driven and data is not available yet")] | 201 #[error("conversation function is event-driven and data is not available yet")] |
135 ConversationAgain = 30, | 202 ConversationAgain = pam_ffi::PAM_CONV_AGAIN, |
136 #[error("call this function again to complete authentication stack")] | 203 #[error("call this function again to complete authentication stack")] |
137 Incomplete = 31, | 204 Incomplete = pam_ffi::PAM_INCOMPLETE, |
138 } | 205 } |
139 | 206 |
140 /// A PAM-specific Result type with an [ErrorCode] error. | 207 /// A PAM-specific Result type with an [ErrorCode] error. |
141 pub type Result<T> = StdResult<T, ErrorCode>; | 208 pub type Result<T> = StdResult<T, ErrorCode>; |
142 | 209 |
157 value => Err(value.try_into().unwrap_or(Self::SystemError)), | 224 value => Err(value.try_into().unwrap_or(Self::SystemError)), |
158 } | 225 } |
159 } | 226 } |
160 } | 227 } |
161 | 228 |
162 impl TryFrom<c_int> for ErrorCode { | |
163 type Error = InvalidEnum<Self>; | |
164 | |
165 fn try_from(value: c_int) -> StdResult<Self, Self::Error> { | |
166 Self::from_i32(value).ok_or(value.into()) | |
167 } | |
168 } | |
169 | |
170 impl From<ErrorCode> for c_int { | |
171 fn from(val: ErrorCode) -> Self { | |
172 val as Self | |
173 } | |
174 } | |
175 | |
176 /// Error returned when attempting to coerce an invalid C integer into an enum. | |
177 #[derive(Debug, PartialEq, thiserror::Error)] | |
178 #[error("{0} is not a valid {type}", type = any::type_name::<T>())] | |
179 pub struct InvalidEnum<T>(c_int, PhantomData<T>); | |
180 | |
181 impl<T> From<InvalidEnum<T>> for c_int { | |
182 fn from(value: InvalidEnum<T>) -> Self { | |
183 value.0 | |
184 } | |
185 } | |
186 | |
187 impl<T> From<c_int> for InvalidEnum<T> { | |
188 fn from(value: c_int) -> Self { | |
189 Self(value, PhantomData) | |
190 } | |
191 } | |
192 | |
193 /// Returned when text that should not have any `\0` bytes in it does. | 229 /// Returned when text that should not have any `\0` bytes in it does. |
194 /// Analogous to [`std::ffi::NulError`], but the data it was created from | 230 /// Analogous to [`std::ffi::NulError`], but the data it was created from |
195 /// is borrowed. | 231 /// is borrowed. |
196 #[cfg(test)] | 232 #[cfg(test)] |
197 mod tests { | 233 mod tests { |
198 use super::*; | 234 use super::*; |
199 | 235 |
200 #[test] | 236 #[test] |
201 fn test_enums() { | 237 fn test_enums() { |
202 assert_eq!(Ok(ErrorCode::ServiceError), 3.try_into()); | |
203 assert_eq!(Err(InvalidEnum::from(999)), ErrorCode::try_from(999)); | |
204 assert_eq!(Ok(()), ErrorCode::result_from(0)); | 238 assert_eq!(Ok(()), ErrorCode::result_from(0)); |
205 assert_eq!(Err(ErrorCode::Abort), ErrorCode::result_from(26)); | 239 assert_eq!( |
240 pam_ffi::PAM_BAD_ITEM, | |
241 ErrorCode::result_to_c::<()>(Err(ErrorCode::BadItem)) | |
242 ); | |
243 assert_eq!( | |
244 Err(ErrorCode::Abort), | |
245 ErrorCode::result_from(pam_ffi::PAM_ABORT) | |
246 ); | |
206 assert_eq!(Err(ErrorCode::SystemError), ErrorCode::result_from(423)); | 247 assert_eq!(Err(ErrorCode::SystemError), ErrorCode::result_from(423)); |
207 assert!(InvalidEnum::<ErrorCode>(33, PhantomData) | 248 } |
208 .to_string() | 249 } |
209 .starts_with("33 is not a valid ")); | |
210 } | |
211 } |