Mercurial > crates > nonstick
view src/constants.rs @ 98:b87100c5eed4
Start on environment variables, and make pointers nicer.
This starts work on the PAM environment handling, and in so doing,
introduces the CHeapBox and CHeapString structs. These are analogous
to Box and CString, but they're located on the C heap rather than
being Rust-managed memory.
This is because environment variables deal with even more pointers
and it turns out we can lose a lot of manual freeing using homemade
smart pointers.
author | Paul Fisher <paul@pfish.zone> |
---|---|
date | Tue, 24 Jun 2025 04:25:25 -0400 |
parents | efe2f5f8b5b2 |
children | dfcd96a74ac4 |
line wrap: on
line source
//! Constants and enum values from the PAM library. // We have a lot of dumb casts that we just gotta do because of differences // between Linux-PAM and OpenPAM header files. #![allow(clippy::unnecessary_cast)] #[cfg(feature = "link")] use crate::libpam::pam_ffi; use bitflags::bitflags; use libc::c_int; use num_enum::{IntoPrimitive, TryFromPrimitive}; use std::error::Error; use std::ffi::c_uint; use std::fmt; use std::fmt::{Display, Formatter}; 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 pam_ffi { use std::ffi::c_uint; macro_rules! define { ($(#[$attr:meta])* $($name:ident = $value:expr),+) => { define!( @meta { $(#[$attr])* } $(pub const $name: u32 = $value;)+ ); }; (@meta $m:tt $($i:item)+) => { define!(@expand $($m $i)+); }; (@expand $({ $(#[$m:meta])* } $i:item)+) => {$($(#[$m])* $i)+}; } const fn bit(n: u8) -> u32 { 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 ); /// Dummy implementation of strerror so that it always returns None. pub fn strerror(val: c_uint) -> Option<&'static str> { _ = val; None } } bitflags! { /// The available PAM flags. /// /// See `/usr/include/security/_pam_types.h` and /// See `/usr/include/security/pam_modules.h` for more details. #[derive(Debug, Default, PartialEq)] #[repr(transparent)] pub struct Flags: c_uint { /// The module should not generate any messages. const SILENT = pam_ffi::PAM_SILENT as u32; /// The module should return [ErrorCode::AuthError] /// if the user has an empty authentication token /// rather than immediately accepting them. const DISALLOW_NULL_AUTHTOK = pam_ffi::PAM_DISALLOW_NULL_AUTHTOK as u32; // Flag used for `set_credentials`. /// Set user credentials for an authentication service. const ESTABLISH_CREDENTIALS = pam_ffi::PAM_ESTABLISH_CRED as u32; /// Delete user credentials associated with /// an authentication service. const DELETE_CREDENTIALS = pam_ffi::PAM_DELETE_CRED as u32; /// Reinitialize user credentials. const REINITIALIZE_CREDENTIALS = pam_ffi::PAM_REINITIALIZE_CRED as u32; /// Extend the lifetime of user credentials. const REFRESH_CREDENTIALS = pam_ffi::PAM_REFRESH_CRED as u32; // Flags used for password changing. /// The password service should only update those passwords /// that have aged. If this flag is _not_ passed, /// the password service should update all passwords. /// /// This flag is only used by `change_authtok`. const CHANGE_EXPIRED_AUTHTOK = pam_ffi::PAM_CHANGE_EXPIRED_AUTHTOK as u32; /// This is a preliminary check for password changing. /// The password should not be changed. /// /// This is only used between PAM and a module. /// Applications may not use this flag. /// /// This flag is only used by `change_authtok`. const PRELIMINARY_CHECK = pam_ffi::PAM_PRELIM_CHECK as u32; /// The password should actuallyPR be updated. /// This and [Self::PRELIMINARY_CHECK] are mutually exclusive. /// /// This is only used between PAM and a module. /// Applications may not use this flag. /// /// This flag is only used by `change_authtok`. const UPDATE_AUTHTOK = pam_ffi::PAM_UPDATE_AUTHTOK as u32; } } /// The Linux-PAM error return values. Success is an Ok [Result]. /// /// Most abbreviations (except `AuthTok` and `Max`) are now full words. /// For more detailed information, see /// `/usr/include/security/_pam_types.h`. #[allow(non_camel_case_types, dead_code)] #[derive(Copy, Clone, Debug, PartialEq, TryFromPrimitive, IntoPrimitive)] #[non_exhaustive] // C might give us anything! #[repr(u32)] pub enum ErrorCode { OpenError = pam_ffi::PAM_OPEN_ERR, SymbolError = pam_ffi::PAM_SYMBOL_ERR, ServiceError = pam_ffi::PAM_SERVICE_ERR, SystemError = pam_ffi::PAM_SYSTEM_ERR, BufferError = pam_ffi::PAM_BUF_ERR, PermissionDenied = pam_ffi::PAM_PERM_DENIED, AuthenticationError = pam_ffi::PAM_AUTH_ERR, CredentialsInsufficient = pam_ffi::PAM_CRED_INSUFFICIENT, AuthInfoUnavailable = pam_ffi::PAM_AUTHINFO_UNAVAIL, UserUnknown = pam_ffi::PAM_USER_UNKNOWN, MaxTries = pam_ffi::PAM_MAXTRIES, NewAuthTokRequired = pam_ffi::PAM_NEW_AUTHTOK_REQD, AccountExpired = pam_ffi::PAM_ACCT_EXPIRED, SessionError = pam_ffi::PAM_SESSION_ERR, CredentialsUnavailable = pam_ffi::PAM_CRED_UNAVAIL, CredentialsExpired = pam_ffi::PAM_CRED_EXPIRED, CredentialsError = pam_ffi::PAM_CRED_ERR, NoModuleData = pam_ffi::PAM_NO_MODULE_DATA, ConversationError = pam_ffi::PAM_CONV_ERR, AuthTokError = pam_ffi::PAM_AUTHTOK_ERR, AuthTokRecoveryError = pam_ffi::PAM_AUTHTOK_RECOVERY_ERR, AuthTokLockBusy = pam_ffi::PAM_AUTHTOK_LOCK_BUSY, AuthTokDisableAging = pam_ffi::PAM_AUTHTOK_DISABLE_AGING, TryAgain = pam_ffi::PAM_TRY_AGAIN, Ignore = pam_ffi::PAM_IGNORE, Abort = pam_ffi::PAM_ABORT, AuthTokExpired = pam_ffi::PAM_AUTHTOK_EXPIRED, ModuleUnknown = pam_ffi::PAM_MODULE_UNKNOWN, BadItem = pam_ffi::PAM_BAD_ITEM, #[cfg(feature = "linux-pam-extensions")] ConversationAgain = pam_ffi::PAM_CONV_AGAIN, #[cfg(feature = "linux-pam-extensions")] Incomplete = pam_ffi::PAM_INCOMPLETE, } /// A PAM-specific Result type with an [ErrorCode] error. pub type Result<T> = StdResult<T, ErrorCode>; impl Display for ErrorCode { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { match pam_ffi::strerror((*self).into()) { Some(err) => f.write_str(err), None => self.fmt_internal(f), } } } impl Error for ErrorCode {} impl ErrorCode { /// Converts this [Result] into a C-compatible result code. pub fn result_to_c<T>(value: Result<T>) -> c_int { match value { Ok(_) => 0, // PAM_SUCCESS Err(otherwise) => u32::from(otherwise) as i32, } } /// Converts a C result code into a [Result], with success as Ok. /// Invalid values are returned as a [Self::SystemError]. pub fn result_from(value: c_int) -> Result<()> { match value { 0 => Ok(()), value => Err((value as u32).try_into().unwrap_or(Self::SystemError)), } } /// A basic Display implementation for if we don't link against PAM. fn fmt_internal(self, f: &mut Formatter<'_>) -> fmt::Result { write!(f, "PAM error: {self:?} ({n})", n = self as c_uint) } } /// 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. #[cfg(test)] mod tests { use super::*; #[test] fn test_enums() { assert_eq!(Ok(()), ErrorCode::result_from(0)); assert_eq!( pam_ffi::PAM_BAD_ITEM as i32, ErrorCode::result_to_c::<()>(Err(ErrorCode::BadItem)) ); assert_eq!( Err(ErrorCode::Abort), ErrorCode::result_from(pam_ffi::PAM_ABORT as i32) ); assert_eq!(Err(ErrorCode::SystemError), ErrorCode::result_from(423)); } }