Mercurial > crates > nonstick
diff src/constants.rs @ 56:daa2cde64601
Big big refactor. Probably should have been multiple changes.
- Makes FFI safer by explicitly specifying c_int in calls.
- Uses ToPrimitive/FromPrimitive to make this easier.
- Pulls PamFlag variables into a bitflags! struct.
- Pulls PamMessageStyle variables into an enum.
- Renames ResultCode to ErrorCode.
- Switches from PAM_SUCCESS to using a Result<(), ErrorCode>.
- Uses thiserror to make ErrorCode into an Error.
- Gets rid of pam_try! because now we have Results.
- Expands some names (e.g. Conv to Conversation).
- Adds more doc comments.
- Returns passwords as a SecureString, to avoid unnecessarily
keeping it around in memory.
author | Paul Fisher <paul@pfish.zone> |
---|---|
date | Sun, 04 May 2025 02:56:55 -0400 |
parents | 676675c3d434 |
children | 3f4a77aa88be |
line wrap: on
line diff
--- a/src/constants.rs Sun May 04 00:58:04 2025 -0400 +++ b/src/constants.rs Sun May 04 02:56:55 2025 -0400 @@ -1,99 +1,144 @@ +use bitflags::bitflags; use libc::{c_int, c_uint}; - +use num_derive::{FromPrimitive, ToPrimitive}; +use num_traits::{FromPrimitive, ToPrimitive}; // TODO: Import constants from C header file at compile time. -pub type PamFlag = c_uint; -pub type PamItemType = c_int; -pub type PamMessageStyle = c_int; - // The Linux-PAM flags // see /usr/include/security/_pam_types.h -pub const PAM_SILENT: PamFlag = 0x8000; -pub const PAM_DISALLOW_NULL_AUTHTOK: PamFlag = 0x0001; -pub const PAM_ESTABLISH_CRED: PamFlag = 0x0002; -pub const PAM_DELETE_CRED: PamFlag = 0x0004; -pub const PAM_REINITIALIZE_CRED: PamFlag = 0x0008; -pub const PAM_REFRESH_CRED: PamFlag = 0x0010; -pub const PAM_CHANGE_EXPIRED_AUTHTOK: PamFlag = 0x0020; +bitflags! { + #[derive(Debug, PartialEq)] + #[repr(transparent)] + pub struct Flags: c_uint { + const SILENT = 0x8000; + const DISALLOW_NULL_AUTHTOK = 0x0001; + const ESTABLISH_CRED = 0x0002; + const DELETE_CRED = 0x0004; + const REINITIALIZE_CRED = 0x0008; + const REFRESH_CRED = 0x0010; + const CHANGE_EXPIRED_AUTHTOK= 0x0020; + } +} -// Message styles -pub const PAM_PROMPT_ECHO_OFF: PamMessageStyle = 1; -pub const PAM_PROMPT_ECHO_ON: PamMessageStyle = 2; -pub const PAM_ERROR_MSG: PamMessageStyle = 3; -pub const PAM_TEXT_INFO: PamMessageStyle = 4; -/// yes/no/maybe conditionals -pub const PAM_RADIO_TYPE: PamMessageStyle = 5; -pub const PAM_BINARY_PROMPT: PamMessageStyle = 7; +/// Styles of message that are shown to the user. +#[derive(Debug, PartialEq, FromPrimitive, ToPrimitive)] +#[non_exhaustive] // non-exhaustive because C might give us back anything! +pub enum MessageStyle { + /// Requests information from the user; will be masked when typing. + PromptEchoOff = 1, + /// Requests information from the user; will not be masked. + PromptEchoOn = 2, + /// An error message. + ErrorMsg = 3, + /// An informational message. + TextInfo = 4, + /// Yes/No/Maybe conditionals. Linux-PAM specific. + RadioType = 5, + /// For server–client non-human interaction. + /// NOT part of the X/Open PAM specification. + BinaryPrompt = 7, +} -/// The Linux-PAM return values. +impl From<MessageStyle> for c_int { + fn from(val: MessageStyle) -> Self { + val.to_i32().unwrap_or(0) + } +} + +/// The Linux-PAM error return values. +/// Success is instead represented by the `Ok` entry of a `Result`. +/// Most abbreviations (except `AuthTok` and `Max`) are now full words. /// For more detailed information, see -/// /usr/include/security/_pam_types.h +/// `/usr/include/security/_pam_types.h`. #[allow(non_camel_case_types, dead_code)] -#[derive(Debug, PartialEq, thiserror::Error)] -#[repr(C)] -pub enum PamResultCode { - #[error("Not an error")] - PAM_SUCCESS = 0, +#[derive(Copy, Clone, Debug, PartialEq, thiserror::Error, FromPrimitive, ToPrimitive)] +#[non_exhaustive] // C might give us anything! +pub enum ErrorCode { #[error("dlopen() failure when dynamically loading a service module")] - PAM_OPEN_ERR = 1, + OpenError = 1, #[error("symbol not found")] - PAM_SYMBOL_ERR = 2, + SymbolError = 2, #[error("error in service module")] - PAM_SERVICE_ERR = 3, + ServiceError = 3, #[error("system error")] - PAM_SYSTEM_ERR = 4, + SystemError = 4, #[error("memory buffer error")] - PAM_BUF_ERR = 5, + BufferError = 5, #[error("permission denied")] - PAM_PERM_DENIED = 6, + PermissionDenied = 6, #[error("authentication failure")] - PAM_AUTH_ERR = 7, + AuthenticationError = 7, #[error("cannot access authentication data due to insufficient credentials")] - PAM_CRED_INSUFFICIENT = 8, + CredentialsInsufficient = 8, #[error("underlying authentication service cannot retrieve authentication information")] - PAM_AUTHINFO_UNAVAIL = 9, + AuthInfoUnavailable = 9, #[error("user not known to the underlying authentication module")] - PAM_USER_UNKNOWN = 10, + UserUnknown = 10, #[error("retry limit reached; do not attempt further")] - PAM_MAXTRIES = 11, + MaxTries = 11, #[error("new authentication token required")] - PAM_NEW_AUTHTOK_REQD = 12, + NewAuthTokRequired = 12, #[error("user account has expired")] - PAM_ACCT_EXPIRED = 13, + AccountExpired = 13, #[error("cannot make/remove an entry for the specified session")] - PAM_SESSION_ERR = 14, + SessionError = 14, #[error("underlying authentication service cannot retrieve user credentials")] - PAM_CRED_UNAVAIL = 15, + CredentialsUnavailable = 15, #[error("user credentials expired")] - PAM_CRED_EXPIRED = 16, + CredentialsExpired = 16, #[error("failure setting user credentials")] - PAM_CRED_ERR = 17, + CredentialsError = 17, #[error("no module-specific data is present")] - PAM_NO_MODULE_DATA = 18, + NoModuleData = 18, #[error("conversation error")] - PAM_CONV_ERR = 19, + ConversationError = 19, #[error("authentication token manipulation error")] - PAM_AUTHTOK_ERR = 20, + AuthTokError = 20, #[error("authentication information cannot be recovered")] - PAM_AUTHTOK_RECOVERY_ERR = 21, + AuthTokRecoveryError = 21, #[error("authentication token lock busy")] - PAM_AUTHTOK_LOCK_BUSY = 22, + AuthTokLockBusy = 22, #[error("authentication token aging disabled")] - PAM_AUTHTOK_DISABLE_AGING = 23, + AuthTokDisableAging = 23, #[error("preliminary check by password service")] - PAM_TRY_AGAIN = 24, + TryAgain = 24, #[error("ignore underlying account module, regardless of control flag")] - PAM_IGNORE = 25, + Ignore = 25, #[error("critical error; this module should fail now")] - PAM_ABORT = 26, + Abort = 26, #[error("authentication token has expired")] - PAM_AUTHTOK_EXPIRED = 27, + AuthTokExpired = 27, #[error("module is not known")] - PAM_MODULE_UNKNOWN = 28, + ModuleUnknown = 28, #[error("bad item passed to pam_[whatever]_item")] - PAM_BAD_ITEM = 29, + BadItem = 29, #[error("conversation function is event-driven and data is not available yet")] - PAM_CONV_AGAIN = 30, + ConversationAgain = 30, #[error("call this function again to complete authentication stack")] - PAM_INCOMPLETE = 31, + Incomplete = 31, } + +pub type PamResult<T> = Result<T, ErrorCode>; +impl ErrorCode { + /// Converts a PamResult into the result code that C wants. + pub fn result_to_c(value: PamResult<()>) -> c_int { + match value { + Ok(_) => 0, // PAM_SUCCESS + Err(otherwise) => otherwise.into(), + } + } + + /// Converts a C result code into a PamResult, with success as Ok. + pub fn result_from(value: c_int) -> PamResult<()> { + match value { + 0 => Ok(()), + value => Err(Self::from_i64(value as i64).unwrap_or(Self::ConversationError)) + } + } +} + +impl From<ErrorCode> for c_int { + fn from(val: ErrorCode) -> Self { + val.to_i32().unwrap_or(0) + } +}