Mercurial > crates > nonstick
view src/constants.rs @ 73:ac6881304c78
Do conversations, along with way too much stuff.
This implements conversations, along with all the memory management
brouhaha that goes along with it. The conversation now lives directly
on the handle rather than being a thing you have to get from it
and then call manually. It Turns Out this makes things a lot easier!
I guess we reorganized things again. For the last time. For real.
I promise.
This all passes ASAN, so it seems Pretty Good!
author | Paul Fisher <paul@pfish.zone> |
---|---|
date | Thu, 05 Jun 2025 03:41:38 -0400 |
parents | 58f9d2a4df38 |
children | 351bdc13005e |
line wrap: on
line source
//! Constants and enum values from the PAM library. 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 std::result::Result as StdResult; 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, PartialEq)] #[repr(transparent)] pub struct Flags: c_uint { /// The module should not generate any messages. const SILENT = 0x8000; /// The module should return [ErrorCode::AuthError] /// if the user has an empty authentication token /// rather than immediately accepting them. const DISALLOW_NULL_AUTHTOK = 0x0001; // Flag used for `set_credentials`. /// Set user credentials for an authentication service. const ESTABLISH_CREDENTIALS = 0x0002; /// Delete user credentials associated with /// an authentication service. const DELETE_CREDENTIALS = 0x0004; /// Reinitialize user credentials. const REINITIALIZE_CREDENTIALS = 0x0008; /// Extend the lifetime of user credentials. const REFRESH_CREDENTIALS = 0x0010; // 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 = 0x0020; /// 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 = 0x4000; /// 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 = 0x2000; } } /// 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, thiserror::Error, FromPrimitive)] #[non_exhaustive] // C might give us anything! pub enum ErrorCode { #[error("dlopen() failure when dynamically loading a service module")] OpenError = 1, #[error("symbol not found")] SymbolError = 2, #[error("error in service module")] ServiceError = 3, #[error("system error")] SystemError = 4, #[error("memory buffer error")] BufferError = 5, #[error("permission denied")] PermissionDenied = 6, #[error("authentication failure")] AuthenticationError = 7, #[error("cannot access authentication data due to insufficient credentials")] CredentialsInsufficient = 8, #[error("underlying authentication service cannot retrieve authentication information")] AuthInfoUnavailable = 9, #[error("user not known to the underlying authentication module")] UserUnknown = 10, #[error("retry limit reached; do not attempt further")] MaxTries = 11, #[error("new authentication token required")] NewAuthTokRequired = 12, #[error("user account has expired")] AccountExpired = 13, #[error("cannot make/remove an entry for the specified session")] SessionError = 14, #[error("underlying authentication service cannot retrieve user credentials")] CredentialsUnavailable = 15, #[error("user credentials expired")] CredentialsExpired = 16, #[error("failure setting user credentials")] CredentialsError = 17, #[error("no module-specific data is present")] NoModuleData = 18, #[error("conversation error")] ConversationError = 19, #[error("authentication token manipulation error")] AuthTokError = 20, #[error("authentication information cannot be recovered")] AuthTokRecoveryError = 21, #[error("authentication token lock busy")] AuthTokLockBusy = 22, #[error("authentication token aging disabled")] AuthTokDisableAging = 23, #[error("preliminary password check failed")] TryAgain = 24, #[error("ignore underlying account module, regardless of control flag")] Ignore = 25, #[error("critical error; this module should fail now")] Abort = 26, #[error("authentication token has expired")] AuthTokExpired = 27, #[error("module is not known")] ModuleUnknown = 28, #[error("bad item passed to pam_[whatever]_item")] BadItem = 29, #[error("conversation function is event-driven and data is not available yet")] ConversationAgain = 30, #[error("call this function again to complete authentication stack")] Incomplete = 31, } /// A PAM-specific Result type with an [ErrorCode] error. pub type Result<T> = StdResult<T, 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) => otherwise.into(), } } /// 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.try_into().unwrap_or(Self::SystemError)), } } } 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(thiserror::Error)] #[error("{0} is not a valid {type}", type = any::type_name::<T>())] #[derive(Debug, PartialEq)] 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. #[cfg(test)] mod tests { use super::*; #[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!(Err(ErrorCode::SystemError), ErrorCode::result_from(423)); assert!(InvalidEnum::<ErrorCode>(33, PhantomData) .to_string() .starts_with("33 is not a valid ")); } }