Mercurial > crates > nonstick
diff 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 |
line wrap: on
line diff
--- a/src/constants.rs Sun Jun 08 04:21:58 2025 -0400 +++ b/src/constants.rs Tue Jun 10 01:09:30 2025 -0400 @@ -1,13 +1,79 @@ //! Constants and enum values from the PAM library. +#[cfg(feature = "link")] +use crate::libpam::pam_ffi; use bitflags::bitflags; -use libc::{c_int, c_uint}; -use num_derive::FromPrimitive; -use num_traits::FromPrimitive; -use std::any; -use std::marker::PhantomData; +use libc::c_int; +use num_enum::{IntoPrimitive, TryFromPrimitive}; use std::result::Result as StdResult; +/// Arbitrary values for PAM constants when not linking against system PAM. +/// +/// **The values of these constants are deliberately selected _not_ to match +/// any PAM implementations. Applications should always use the symbolic value +/// and not a magic number.** +#[cfg(not(feature = "link"))] +mod ffi { + macro_rules! define { + ($(#[$attr:meta])* $($name:ident = $value:expr),+) => { + define!( + @meta { $(#[$attr])* } + $(pub const $name: i32 = $value;)+ + ); + }; + (@meta $m:tt $($i:item)+) => { define!(@expand $($m $i)+); }; + (@expand $({ $(#[$m:meta])* } $i:item)+) => {$($(#[$m])* $i)+}; + } + const fn bit(n: i8) -> i32 { + 1 << n + } + define!( + PAM_SILENT = bit(13), + PAM_DISALLOW_NULL_AUTHTOK = bit(14), + PAM_ESTABLISH_CRED = bit(15), + PAM_DELETE_CRED = bit(16), + PAM_REINITIALIZE_CRED = bit(17), + PAM_REFRESH_CRED = bit(18), + PAM_CHANGE_EXPIRED_AUTHTOK = bit(19), + PAM_PRELIM_CHECK = bit(20), + PAM_UPDATE_AUTHTOK = bit(21) + ); + + define!( + PAM_ABORT = 513, + PAM_ACCT_EXPIRED = 514, + PAM_AUTHINFO_UNAVAIL = 515, + PAM_AUTHTOK_DISABLE_AGING = 516, + PAM_AUTHTOK_ERR = 517, + PAM_AUTHTOK_EXPIRED = 518, + PAM_AUTHTOK_LOCK_BUSY = 519, + PAM_AUTHTOK_RECOVERY_ERR = 520, + PAM_AUTH_ERR = 521, + PAM_BAD_ITEM = 522, + PAM_BUF_ERR = 533, + PAM_CONV_AGAIN = 534, + PAM_CONV_ERR = 535, + PAM_CRED_ERR = 536, + PAM_CRED_EXPIRED = 537, + PAM_CRED_INSUFFICIENT = 538, + PAM_CRED_UNAVAIL = 539, + PAM_IGNORE = 540, + PAM_INCOMPLETE = 541, + PAM_MAXTRIES = 542, + PAM_MODULE_UNKNOWN = 543, + PAM_NEW_AUTHTOK_REQD = 544, + PAM_NO_MODULE_DATA = 545, + PAM_OPEN_ERR = 546, + PAM_PERM_DENIED = 547, + PAM_SERVICE_ERR = 548, + PAM_SESSION_ERR = 549, + PAM_SYMBOL_ERR = 550, + PAM_SYSTEM_ERR = 551, + PAM_TRY_AGAIN = 552, + PAM_USER_UNKNOWN = 553 + ); +} + bitflags! { /// The available PAM flags. /// @@ -15,26 +81,26 @@ /// See `/usr/include/security/pam_modules.h` for more details. #[derive(Debug, PartialEq)] #[repr(transparent)] - pub struct Flags: c_uint { + pub struct Flags: c_int { /// The module should not generate any messages. - const SILENT = 0x8000; + const SILENT = pam_ffi::PAM_SILENT; /// The module should return [ErrorCode::AuthError] /// if the user has an empty authentication token /// rather than immediately accepting them. - const DISALLOW_NULL_AUTHTOK = 0x0001; + const DISALLOW_NULL_AUTHTOK = pam_ffi::PAM_DISALLOW_NULL_AUTHTOK; // Flag used for `set_credentials`. /// Set user credentials for an authentication service. - const ESTABLISH_CREDENTIALS = 0x0002; + const ESTABLISH_CREDENTIALS = pam_ffi::PAM_ESTABLISH_CRED; /// Delete user credentials associated with /// an authentication service. - const DELETE_CREDENTIALS = 0x0004; + const DELETE_CREDENTIALS = pam_ffi::PAM_DELETE_CRED; /// Reinitialize user credentials. - const REINITIALIZE_CREDENTIALS = 0x0008; + const REINITIALIZE_CREDENTIALS = pam_ffi::PAM_REINITIALIZE_CRED; /// Extend the lifetime of user credentials. - const REFRESH_CREDENTIALS = 0x0010; + const REFRESH_CREDENTIALS = pam_ffi::PAM_REFRESH_CRED; // Flags used for password changing. @@ -43,7 +109,7 @@ /// the password service should update all passwords. /// /// This flag is only used by `change_authtok`. - const CHANGE_EXPIRED_AUTHTOK = 0x0020; + const CHANGE_EXPIRED_AUTHTOK = pam_ffi::PAM_CHANGE_EXPIRED_AUTHTOK; /// This is a preliminary check for password changing. /// The password should not be changed. @@ -52,7 +118,7 @@ /// Applications may not use this flag. /// /// This flag is only used by `change_authtok`. - const PRELIMINARY_CHECK = 0x4000; + const PRELIMINARY_CHECK = pam_ffi::PAM_PRELIM_CHECK; /// The password should actuallyPR be updated. /// This and [Self::PRELIMINARY_CHECK] are mutually exclusive. /// @@ -60,7 +126,7 @@ /// Applications may not use this flag. /// /// This flag is only used by `change_authtok`. - const UPDATE_AUTHTOK = 0x2000; + const UPDATE_AUTHTOK = pam_ffi::PAM_UPDATE_AUTHTOK; } } @@ -70,71 +136,72 @@ /// For more detailed information, see /// `/usr/include/security/_pam_types.h`. #[allow(non_camel_case_types, dead_code)] -#[derive(Copy, Clone, Debug, PartialEq, thiserror::Error, FromPrimitive)] +#[derive(Copy, Clone, Debug, PartialEq, thiserror::Error, TryFromPrimitive, IntoPrimitive)] #[non_exhaustive] // C might give us anything! +#[repr(i32)] pub enum ErrorCode { #[error("dlopen() failure when dynamically loading a service module")] - OpenError = 1, + OpenError = pam_ffi::PAM_OPEN_ERR, #[error("symbol not found")] - SymbolError = 2, + SymbolError = pam_ffi::PAM_SYMBOL_ERR, #[error("error in service module")] - ServiceError = 3, + ServiceError = pam_ffi::PAM_SERVICE_ERR, #[error("system error")] - SystemError = 4, + SystemError = pam_ffi::PAM_SYSTEM_ERR, #[error("memory buffer error")] - BufferError = 5, + BufferError = pam_ffi::PAM_BUF_ERR, #[error("permission denied")] - PermissionDenied = 6, + PermissionDenied = pam_ffi::PAM_PERM_DENIED, #[error("authentication failure")] - AuthenticationError = 7, + AuthenticationError = pam_ffi::PAM_AUTH_ERR, #[error("cannot access authentication data due to insufficient credentials")] - CredentialsInsufficient = 8, + CredentialsInsufficient = pam_ffi::PAM_CRED_INSUFFICIENT, #[error("underlying authentication service cannot retrieve authentication information")] - AuthInfoUnavailable = 9, + AuthInfoUnavailable = pam_ffi::PAM_AUTHINFO_UNAVAIL, #[error("user not known to the underlying authentication module")] - UserUnknown = 10, + UserUnknown = pam_ffi::PAM_USER_UNKNOWN, #[error("retry limit reached; do not attempt further")] - MaxTries = 11, + MaxTries = pam_ffi::PAM_MAXTRIES, #[error("new authentication token required")] - NewAuthTokRequired = 12, + NewAuthTokRequired = pam_ffi::PAM_NEW_AUTHTOK_REQD, #[error("user account has expired")] - AccountExpired = 13, + AccountExpired = pam_ffi::PAM_ACCT_EXPIRED, #[error("cannot make/remove an entry for the specified session")] - SessionError = 14, + SessionError = pam_ffi::PAM_SESSION_ERR, #[error("underlying authentication service cannot retrieve user credentials")] - CredentialsUnavailable = 15, + CredentialsUnavailable = pam_ffi::PAM_CRED_UNAVAIL, #[error("user credentials expired")] - CredentialsExpired = 16, + CredentialsExpired = pam_ffi::PAM_CRED_EXPIRED, #[error("failure setting user credentials")] - CredentialsError = 17, + CredentialsError = pam_ffi::PAM_CRED_ERR, #[error("no module-specific data is present")] - NoModuleData = 18, + NoModuleData = pam_ffi::PAM_NO_MODULE_DATA, #[error("conversation error")] - ConversationError = 19, + ConversationError = pam_ffi::PAM_CONV_ERR, #[error("authentication token manipulation error")] - AuthTokError = 20, + AuthTokError = pam_ffi::PAM_AUTHTOK_ERR, #[error("authentication information cannot be recovered")] - AuthTokRecoveryError = 21, + AuthTokRecoveryError = pam_ffi::PAM_AUTHTOK_RECOVERY_ERR, #[error("authentication token lock busy")] - AuthTokLockBusy = 22, + AuthTokLockBusy = pam_ffi::PAM_AUTHTOK_LOCK_BUSY, #[error("authentication token aging disabled")] - AuthTokDisableAging = 23, + AuthTokDisableAging = pam_ffi::PAM_AUTHTOK_DISABLE_AGING, #[error("preliminary password check failed")] - TryAgain = 24, + TryAgain = pam_ffi::PAM_TRY_AGAIN, #[error("ignore underlying account module, regardless of control flag")] - Ignore = 25, + Ignore = pam_ffi::PAM_IGNORE, #[error("critical error; this module should fail now")] - Abort = 26, + Abort = pam_ffi::PAM_ABORT, #[error("authentication token has expired")] - AuthTokExpired = 27, + AuthTokExpired = pam_ffi::PAM_AUTHTOK_EXPIRED, #[error("module is not known")] - ModuleUnknown = 28, + ModuleUnknown = pam_ffi::PAM_MODULE_UNKNOWN, #[error("bad item passed to pam_[whatever]_item")] - BadItem = 29, + BadItem = pam_ffi::PAM_BAD_ITEM, #[error("conversation function is event-driven and data is not available yet")] - ConversationAgain = 30, + ConversationAgain = pam_ffi::PAM_CONV_AGAIN, #[error("call this function again to complete authentication stack")] - Incomplete = 31, + Incomplete = pam_ffi::PAM_INCOMPLETE, } /// A PAM-specific Result type with an [ErrorCode] error. @@ -159,37 +226,6 @@ } } -impl TryFrom<c_int> for ErrorCode { - type Error = InvalidEnum<Self>; - - fn try_from(value: c_int) -> StdResult<Self, Self::Error> { - Self::from_i32(value).ok_or(value.into()) - } -} - -impl From<ErrorCode> for c_int { - fn from(val: ErrorCode) -> Self { - val as Self - } -} - -/// Error returned when attempting to coerce an invalid C integer into an enum. -#[derive(Debug, PartialEq, thiserror::Error)] -#[error("{0} is not a valid {type}", type = any::type_name::<T>())] -pub struct InvalidEnum<T>(c_int, PhantomData<T>); - -impl<T> From<InvalidEnum<T>> for c_int { - fn from(value: InvalidEnum<T>) -> Self { - value.0 - } -} - -impl<T> From<c_int> for InvalidEnum<T> { - fn from(value: c_int) -> Self { - Self(value, PhantomData) - } -} - /// Returned when text that should not have any `\0` bytes in it does. /// Analogous to [`std::ffi::NulError`], but the data it was created from /// is borrowed. @@ -199,13 +235,15 @@ #[test] fn test_enums() { - assert_eq!(Ok(ErrorCode::ServiceError), 3.try_into()); - assert_eq!(Err(InvalidEnum::from(999)), ErrorCode::try_from(999)); assert_eq!(Ok(()), ErrorCode::result_from(0)); - assert_eq!(Err(ErrorCode::Abort), ErrorCode::result_from(26)); + assert_eq!( + pam_ffi::PAM_BAD_ITEM, + ErrorCode::result_to_c::<()>(Err(ErrorCode::BadItem)) + ); + assert_eq!( + Err(ErrorCode::Abort), + ErrorCode::result_from(pam_ffi::PAM_ABORT) + ); assert_eq!(Err(ErrorCode::SystemError), ErrorCode::result_from(423)); - assert!(InvalidEnum::<ErrorCode>(33, PhantomData) - .to_string() - .starts_with("33 is not a valid ")); } }