comparison src/constants.rs @ 90:f6186e41399b

Miscellaneous fixes and cleanup: - Rename `get_user` to `username` and `get_authtok` to `authtok`. - Use pam_strerror for error messages. - Add library linkage to build.rs (it was missing???).
author Paul Fisher <paul@pfish.zone>
date Sat, 14 Jun 2025 09:30:16 -0400
parents 05291b601f0a
children 5ddbcada30f2
comparison
equal deleted inserted replaced
89:dd3e9c4bcde3 90:f6186e41399b
7 #[cfg(feature = "link")] 7 #[cfg(feature = "link")]
8 use crate::libpam::pam_ffi; 8 use crate::libpam::pam_ffi;
9 use bitflags::bitflags; 9 use bitflags::bitflags;
10 use libc::c_int; 10 use libc::c_int;
11 use num_enum::{IntoPrimitive, TryFromPrimitive}; 11 use num_enum::{IntoPrimitive, TryFromPrimitive};
12 use std::error::Error;
12 use std::ffi::c_uint; 13 use std::ffi::c_uint;
14 use std::fmt;
15 use std::fmt::{Display, Formatter};
13 use std::result::Result as StdResult; 16 use std::result::Result as StdResult;
14 17
15 /// Arbitrary values for PAM constants when not linking against system PAM. 18 /// Arbitrary values for PAM constants when not linking against system PAM.
16 /// 19 ///
17 /// **The values of these constants are deliberately selected _not_ to match 20 /// **The values of these constants are deliberately selected _not_ to match
75 PAM_SYMBOL_ERR = 550, 78 PAM_SYMBOL_ERR = 550,
76 PAM_SYSTEM_ERR = 551, 79 PAM_SYSTEM_ERR = 551,
77 PAM_TRY_AGAIN = 552, 80 PAM_TRY_AGAIN = 552,
78 PAM_USER_UNKNOWN = 553 81 PAM_USER_UNKNOWN = 553
79 ); 82 );
83
84 fn strerror(val: c_uint) -> Option<&'static str> {
85 None
86 }
80 } 87 }
81 88
82 bitflags! { 89 bitflags! {
83 /// The available PAM flags. 90 /// The available PAM flags.
84 /// 91 ///
138 /// 145 ///
139 /// Most abbreviations (except `AuthTok` and `Max`) are now full words. 146 /// Most abbreviations (except `AuthTok` and `Max`) are now full words.
140 /// For more detailed information, see 147 /// For more detailed information, see
141 /// `/usr/include/security/_pam_types.h`. 148 /// `/usr/include/security/_pam_types.h`.
142 #[allow(non_camel_case_types, dead_code)] 149 #[allow(non_camel_case_types, dead_code)]
143 #[derive(Copy, Clone, Debug, PartialEq, thiserror::Error, TryFromPrimitive, IntoPrimitive)] 150 #[derive(Copy, Clone, Debug, PartialEq, TryFromPrimitive, IntoPrimitive)]
144 #[non_exhaustive] // C might give us anything! 151 #[non_exhaustive] // C might give us anything!
145 #[repr(u32)] 152 #[repr(u32)]
146 pub enum ErrorCode { 153 pub enum ErrorCode {
147 #[error("dlopen() failure when dynamically loading a service module")]
148 OpenError = pam_ffi::PAM_OPEN_ERR, 154 OpenError = pam_ffi::PAM_OPEN_ERR,
149 #[error("symbol not found")]
150 SymbolError = pam_ffi::PAM_SYMBOL_ERR, 155 SymbolError = pam_ffi::PAM_SYMBOL_ERR,
151 #[error("error in service module")]
152 ServiceError = pam_ffi::PAM_SERVICE_ERR, 156 ServiceError = pam_ffi::PAM_SERVICE_ERR,
153 #[error("system error")]
154 SystemError = pam_ffi::PAM_SYSTEM_ERR, 157 SystemError = pam_ffi::PAM_SYSTEM_ERR,
155 #[error("memory buffer error")]
156 BufferError = pam_ffi::PAM_BUF_ERR, 158 BufferError = pam_ffi::PAM_BUF_ERR,
157 #[error("permission denied")]
158 PermissionDenied = pam_ffi::PAM_PERM_DENIED, 159 PermissionDenied = pam_ffi::PAM_PERM_DENIED,
159 #[error("authentication failure")]
160 AuthenticationError = pam_ffi::PAM_AUTH_ERR, 160 AuthenticationError = pam_ffi::PAM_AUTH_ERR,
161 #[error("cannot access authentication data due to insufficient credentials")]
162 CredentialsInsufficient = pam_ffi::PAM_CRED_INSUFFICIENT, 161 CredentialsInsufficient = pam_ffi::PAM_CRED_INSUFFICIENT,
163 #[error("underlying authentication service cannot retrieve authentication information")]
164 AuthInfoUnavailable = pam_ffi::PAM_AUTHINFO_UNAVAIL, 162 AuthInfoUnavailable = pam_ffi::PAM_AUTHINFO_UNAVAIL,
165 #[error("user not known to the underlying authentication module")]
166 UserUnknown = pam_ffi::PAM_USER_UNKNOWN, 163 UserUnknown = pam_ffi::PAM_USER_UNKNOWN,
167 #[error("retry limit reached; do not attempt further")]
168 MaxTries = pam_ffi::PAM_MAXTRIES, 164 MaxTries = pam_ffi::PAM_MAXTRIES,
169 #[error("new authentication token required")]
170 NewAuthTokRequired = pam_ffi::PAM_NEW_AUTHTOK_REQD, 165 NewAuthTokRequired = pam_ffi::PAM_NEW_AUTHTOK_REQD,
171 #[error("user account has expired")]
172 AccountExpired = pam_ffi::PAM_ACCT_EXPIRED, 166 AccountExpired = pam_ffi::PAM_ACCT_EXPIRED,
173 #[error("cannot make/remove an entry for the specified session")]
174 SessionError = pam_ffi::PAM_SESSION_ERR, 167 SessionError = pam_ffi::PAM_SESSION_ERR,
175 #[error("underlying authentication service cannot retrieve user credentials")]
176 CredentialsUnavailable = pam_ffi::PAM_CRED_UNAVAIL, 168 CredentialsUnavailable = pam_ffi::PAM_CRED_UNAVAIL,
177 #[error("user credentials expired")]
178 CredentialsExpired = pam_ffi::PAM_CRED_EXPIRED, 169 CredentialsExpired = pam_ffi::PAM_CRED_EXPIRED,
179 #[error("failure setting user credentials")]
180 CredentialsError = pam_ffi::PAM_CRED_ERR, 170 CredentialsError = pam_ffi::PAM_CRED_ERR,
181 #[error("no module-specific data is present")]
182 NoModuleData = pam_ffi::PAM_NO_MODULE_DATA, 171 NoModuleData = pam_ffi::PAM_NO_MODULE_DATA,
183 #[error("conversation error")]
184 ConversationError = pam_ffi::PAM_CONV_ERR, 172 ConversationError = pam_ffi::PAM_CONV_ERR,
185 #[error("authentication token manipulation error")]
186 AuthTokError = pam_ffi::PAM_AUTHTOK_ERR, 173 AuthTokError = pam_ffi::PAM_AUTHTOK_ERR,
187 #[error("authentication information cannot be recovered")]
188 AuthTokRecoveryError = pam_ffi::PAM_AUTHTOK_RECOVERY_ERR, 174 AuthTokRecoveryError = pam_ffi::PAM_AUTHTOK_RECOVERY_ERR,
189 #[error("authentication token lock busy")]
190 AuthTokLockBusy = pam_ffi::PAM_AUTHTOK_LOCK_BUSY, 175 AuthTokLockBusy = pam_ffi::PAM_AUTHTOK_LOCK_BUSY,
191 #[error("authentication token aging disabled")]
192 AuthTokDisableAging = pam_ffi::PAM_AUTHTOK_DISABLE_AGING, 176 AuthTokDisableAging = pam_ffi::PAM_AUTHTOK_DISABLE_AGING,
193 #[error("preliminary password check failed")]
194 TryAgain = pam_ffi::PAM_TRY_AGAIN, 177 TryAgain = pam_ffi::PAM_TRY_AGAIN,
195 #[error("ignore underlying account module, regardless of control flag")]
196 Ignore = pam_ffi::PAM_IGNORE, 178 Ignore = pam_ffi::PAM_IGNORE,
197 #[error("critical error; this module should fail now")]
198 Abort = pam_ffi::PAM_ABORT, 179 Abort = pam_ffi::PAM_ABORT,
199 #[error("authentication token has expired")]
200 AuthTokExpired = pam_ffi::PAM_AUTHTOK_EXPIRED, 180 AuthTokExpired = pam_ffi::PAM_AUTHTOK_EXPIRED,
201 #[error("module is not known")]
202 ModuleUnknown = pam_ffi::PAM_MODULE_UNKNOWN, 181 ModuleUnknown = pam_ffi::PAM_MODULE_UNKNOWN,
203 #[error("bad item passed to pam_[whatever]_item")]
204 BadItem = pam_ffi::PAM_BAD_ITEM, 182 BadItem = pam_ffi::PAM_BAD_ITEM,
205 #[cfg(feature = "linux-pam-extensions")] 183 #[cfg(feature = "linux-pam-extensions")]
206 #[error("conversation function is event-driven and data is not available yet")]
207 ConversationAgain = pam_ffi::PAM_CONV_AGAIN, 184 ConversationAgain = pam_ffi::PAM_CONV_AGAIN,
208 #[cfg(feature = "linux-pam-extensions")] 185 #[cfg(feature = "linux-pam-extensions")]
209 #[error("call this function again to complete authentication stack")]
210 Incomplete = pam_ffi::PAM_INCOMPLETE, 186 Incomplete = pam_ffi::PAM_INCOMPLETE,
211 } 187 }
212 188
213 /// A PAM-specific Result type with an [ErrorCode] error. 189 /// A PAM-specific Result type with an [ErrorCode] error.
214 pub type Result<T> = StdResult<T, ErrorCode>; 190 pub type Result<T> = StdResult<T, ErrorCode>;
191
192 impl Display for ErrorCode {
193 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
194 match pam_ffi::strerror((*self).into()) {
195 Some(err) => f.write_str(err),
196 None => self.fmt_internal(f),
197 }
198 }
199 }
200
201 impl Error for ErrorCode {}
215 202
216 impl ErrorCode { 203 impl ErrorCode {
217 /// Converts this [Result] into a C-compatible result code. 204 /// Converts this [Result] into a C-compatible result code.
218 pub fn result_to_c<T>(value: Result<T>) -> c_int { 205 pub fn result_to_c<T>(value: Result<T>) -> c_int {
219 match value { 206 match value {
227 pub fn result_from(value: c_int) -> Result<()> { 214 pub fn result_from(value: c_int) -> Result<()> {
228 match value { 215 match value {
229 0 => Ok(()), 216 0 => Ok(()),
230 value => Err((value as u32).try_into().unwrap_or(Self::SystemError)), 217 value => Err((value as u32).try_into().unwrap_or(Self::SystemError)),
231 } 218 }
219 }
220
221 /// A basic Display implementation for if we don't link against PAM.
222 fn fmt_internal(self, f: &mut Formatter<'_>) -> fmt::Result {
223 write!(f, "PAM error: {self:?} ({n})", n = self as c_uint)
232 } 224 }
233 } 225 }
234 226
235 /// Returned when text that should not have any `\0` bytes in it does. 227 /// Returned when text that should not have any `\0` bytes in it does.
236 /// Analogous to [`std::ffi::NulError`], but the data it was created from 228 /// Analogous to [`std::ffi::NulError`], but the data it was created from