Mercurial > crates > nonstick
diff src/constants.rs @ 171:e27c5c667a5a
Create full new types for return code and flags, separate end to end.
This plumbs the ReturnCode and RawFlags types through the places where
we call into or are called from PAM.
Also adds Sun documentation to the project.
author | Paul Fisher <paul@pfish.zone> |
---|---|
date | Fri, 25 Jul 2025 20:52:14 -0400 |
parents | f052e2417195 |
children | 6727cbe56f4a |
line wrap: on
line diff
--- a/src/constants.rs Wed Jul 16 18:45:20 2025 -0400 +++ b/src/constants.rs Fri Jul 25 20:52:14 2025 -0400 @@ -1,157 +1,109 @@ //! Constants and enum values from the PAM library. -use crate::_doc::{linklist, man7, manbsd, xsso}; +use crate::_doc::{linklist, man7, manbsd, mansun, xsso}; use bitflags::bitflags; -use num_enum::{IntoPrimitive, TryFromPrimitive}; use std::error::Error; use std::ffi::c_int; use std::fmt; use std::fmt::{Display, Formatter}; use std::result::Result as StdResult; -#[cfg(features = "link")] -use libpam_sys_consts::constants as pam_constants; - - -/// The union of constants available in all versions of PAM. -/// The values here are fictitious and should not be used. -#[cfg(not(features = "link"))] -mod pam_constants { - pub const PAM_SUCCESS: i32 = 0; - - /// Generates a sequence of values. - macro_rules! c_enum { - ($first:ident = $value:expr, $($rest:ident,)*) => { - c_enum!(($value) $first, $($rest,)*); - }; - (($value:expr) $first:ident, $($rest:ident,)*) => { - pub const $first: i32 = $value; - c_enum!(($value+1) $($rest,)*); - }; - (($value:expr)) => {}; - } - - // Since all these values are fictitious, we can start them wherever. - // All the items. - c_enum!( - PAM_SERVICE = 64, - PAM_USER, - PAM_TTY, - PAM_RHOST, - PAM_CONV, - PAM_AUTHTOK, - PAM_OLDAUTHTOK, - PAM_RUSER, - PAM_USER_PROMPT, - // Linux-only items. - PAM_FAIL_DELAY, - PAM_XDISPLAY, - PAM_XAUTHDATA, - PAM_AUTHTOK_TYPE, - // OpenPAM-only items. - PAM_REPOSITORY, - PAM_AUTHTOK_PROMPT, - PAM_OLDAUTHTOK_PROMPT, - PAM_HOST, - // Sun-only items. - PAM_RESOURCE, - PAM_AUSER, - ); - - // Prompt types. - c_enum!( - PAM_PROMPT_ECHO_OFF = 96, - PAM_PROMPT_ECHO_ON, - PAM_ERROR_MSG, - PAM_TEXT_INFO, - PAM_RADIO_TYPE, - PAM_BINARY_PROMPT, - ); +macro_rules! wrapper { + ( + $(#[$m:meta])* + $viz:vis $name:ident($wraps:ty); + ) => { + $(#[$m])* + #[derive(Clone, Copy, Debug, PartialEq, Eq)] + #[repr(transparent)] + $viz struct $name(i32); - // Errors. - c_enum!( - PAM_OPEN_ERR = 128, - PAM_SYMBOL_ERR, - PAM_SERVICE_ERR, - PAM_SYSTEM_ERR, - PAM_BUF_ERR, - PAM_PERM_DENIED, - PAM_AUTH_ERR, - PAM_CRED_INSUFFICIENT, - PAM_AUTHINFO_UNAVAIL, - PAM_USER_UNKNOWN, - PAM_MAXTRIES, - PAM_NEW_AUTHTOK_REQD, - PAM_ACCT_EXPIRED, - PAM_SESSION_ERR, - PAM_CRED_UNAVAIL, - PAM_CRED_EXPIRED, - PAM_CRED_ERR, - PAM_NO_MODULE_DATA, - PAM_CONV_ERR, - PAM_AUTHTOK_ERR, - PAM_AUTHTOK_RECOVERY_ERR, - PAM_AUTHTOK_LOCK_BUSY, - PAM_AUTHTOK_DISABLE_AGING, - PAM_TRY_AGAIN, - PAM_IGNORE, - PAM_ABORT, - PAM_AUTHTOK_EXPIRED, - PAM_MODULE_UNKNOWN, - PAM_BAD_ITEM, - PAM_CONV_AGAIN, - PAM_INCOMPLETE, - // OpenPAM-only errors. - PAM_DOMAIN_UNKNOWN, - PAM_BAD_HANDLE, - PAM_BAD_FEATURE, - PAM_BAD_CONSTANT, - ); - - macro_rules! flag_enum { - ($first:ident = $value:expr, $($rest:ident,)*) => { - flag_enum!(($value) $first, $($rest,)*); - }; - (($value:expr) $first:ident, $($rest:ident,)*) => { - pub const $first: i32 = $value; - flag_enum!(($value*2) $($rest,)*); - }; - (($value:expr)) => {}; + impl From<i32> for $name { + fn from(value: i32) -> Self { + Self(value) + } + } + impl From<$name> for i32 { + fn from(value: $name) -> Self { + value.0 + } + } } - - flag_enum!( - PAM_SILENT = 256, - PAM_DISALLOW_NULL_AUTHTOK, - PAM_ESTABLISH_CRED, - PAM_DELETE_CRED, - PAM_REINITIALIZE_CRED, - PAM_REFRESH_CRED, - - PAM_CHANGE_EXPIRED_AUTHTOK, - - PAM_PRELIM_CHECK, - PAM_UPDATE_AUTHTOK, - PAM_DATA_REPLACE, - PAM_DATA_SILENT, - ); } -/// Creates a bitflags! macro, with an extra SILENT element. +wrapper! { + /// Type of the flags that PAM passes to us (or that we pass to PAM). + pub RawFlags(c_int); +} +wrapper! { + /// The error code that we return to PAM. + pub ReturnCode(c_int); +} + +impl ReturnCode { + /// A successful return. + pub const SUCCESS: Self = Self(0); +} + macro_rules! pam_flags { ( $(#[$m:meta])* $name:ident { - $($inner:tt)* + $( + $(#[$m_ident:ident $($m_arg:tt)*])* + const $item_name:ident = (link = $value_value:expr, else = $other_value:expr); + )* } ) => { bitflags! { + #[derive(Clone, Copy, Debug, Default, PartialEq)] $(#[$m])* - #[derive(Clone, Copy, Debug, Default, PartialEq)] - #[repr(transparent)] - pub struct $name: c_int { - /// The module should not generate any messages. - const SILENT = pam_constants::PAM_SILENT; - $($inner)* + pub struct $name: u16 { + $( + $(#[$m_ident $($m_arg)*])* + const $item_name = $other_value; + )* + } + } + + #[cfg(feature = "link")] + impl From<RawFlags> for $name { + #[allow(unused_doc_comments)] + fn from(value: RawFlags) -> Self { + eprintln!(concat!(stringify!($name), " FROM RAW FLAGS")); + let value: c_int = value.into(); + let result = Self::empty(); + $( + $(#[$m_ident $($m_arg)*])* + let result = result | if value & $value_value == 0 { + eprintln!(concat!("checked against ", stringify!($value_value))); + Self::empty() + } else { + eprintln!(concat!("checked against ", stringify!($value_value), " success")); + Self::$item_name + }; + )* + result + } + } + + #[cfg(feature = "link")] + impl From<$name> for RawFlags { + #[allow(unused_doc_comments)] + fn from(value: $name) -> Self { + eprintln!(concat!("RAW FLAGS FROM ", stringify!($name))); + let result = 0; + $( + $(#[$m_ident $($m_arg)*])* + let result = result | if value.contains($name::$item_name) { + eprintln!(concat!("checked against ", stringify!($item_name), " success")); + $value_value + } else { + eprintln!(concat!("checked against ", stringify!($item_name))); + 0 + }; + )* + Self(result) } } } @@ -160,30 +112,39 @@ pam_flags! { /// Flags for authentication and account management. AuthnFlags { + /// The PAM module should not generate any messages. + const SILENT = (link = libpam_sys::PAM_SILENT, else = 0x8000); + /// The module should return [AuthError](ErrorCode::AuthError) /// if the user has an empty authentication token, rather than /// allowing them to log in. - const DISALLOW_NULL_AUTHTOK = pam_constants::PAM_DISALLOW_NULL_AUTHTOK; + const DISALLOW_NULL_AUTHTOK = (link = libpam_sys::PAM_DISALLOW_NULL_AUTHTOK, else = 0b1); } } pam_flags! { /// Flags for changing the authentication token. AuthtokFlags { + /// The PAM module should not generate any messages. + const SILENT = (link = libpam_sys::PAM_SILENT, else = 0x8000); + /// Indicates that the user's authentication token should /// only be changed if it is expired. If not passed, /// the authentication token should be changed unconditionally. - const CHANGE_EXPIRED_AUTHTOK = pam_constants::PAM_CHANGE_EXPIRED_AUTHTOK; + const CHANGE_EXPIRED_AUTHTOK = (link = libpam_sys::PAM_CHANGE_EXPIRED_AUTHTOK, else = 0b10); /// Don't check if the password is any good (Sun only). #[cfg(pam_impl = "Sun")] - const NO_AUTHTOK_CHECK = pam_constants::PAM_NO_AUTHTOK_CHECK; + const NO_AUTHTOK_CHECK = (link = libpam_sys::PAM_NO_AUTHTOK_CHECK, else = 0b100); } } pam_flags! { /// Common flag(s) shared by all PAM actions. - BaseFlags {} + BaseFlags { + /// The PAM module should not generate any messages. + const SILENT = (link = libpam_sys::PAM_SILENT, else = 0x8000); + } } #[cfg(feature = "openpam-ext")] @@ -197,29 +158,53 @@ $name:ident { $( $(#[$item_m:meta])* - $item_name:ident = $item_value:expr, + $item_name:ident = $item_value:path, )* } ) => { $(#[$m])* - #[derive(Clone, Copy, Debug, PartialEq, TryFromPrimitive, IntoPrimitive)] - #[repr(i32)] + #[derive(Clone, Copy, Debug, PartialEq)] pub enum $name { $( $(#[$item_m])* - $item_name = $item_value, + $item_name, )* } + #[cfg(feature = "link")] + impl TryFrom<RawFlags> for $name { + type Error = ErrorCode; + fn try_from(value: RawFlags) -> Result<$name> { + match value.0 { + $( + $item_value => Ok(Self::$item_name), + )* + _ => Err(BAD_CONST), + } + } + } + + #[cfg(feature = "link")] + impl From<$name> for RawFlags { + fn from(value: $name) -> Self { + match value { + $( + $name::$item_name => $item_value.into(), + )* + } + } + } + + #[cfg(feature = "link")] impl $name { const ALL_VALUES: i32 = 0 $( | $item_value)*; - fn split(value: i32) -> Result<(Option<Self>, i32)> { - let me = value & Self::ALL_VALUES; - let them = value & !Self::ALL_VALUES; - let me = match me { - 0 => None, - n => Some(Self::try_from(n).map_err(|_| BAD_CONST)?), + fn split(value: RawFlags) -> Result<(Option<Self>, RawFlags)> { + let me = value.0 & Self::ALL_VALUES; + let them = (value.0 & !Self::ALL_VALUES).into(); + let me = match RawFlags(me) { + RawFlags(0) => None, + other => Some(Self::try_from(other).map_err(|_| BAD_CONST)?), }; Ok((me, them)) } @@ -231,21 +216,21 @@ /// The credential management action that should take place. CredAction { /// Set the user's credentials from this module. Default if unspecified. - Establish = pam_constants::PAM_ESTABLISH_CRED, + Establish = libpam_sys::PAM_ESTABLISH_CRED, /// Revoke the user's credentials established by this module. - Delete = pam_constants::PAM_DELETE_CRED, + Delete = libpam_sys::PAM_DELETE_CRED, /// Fully reinitialize the user's credentials from this module. - Reinitialize = pam_constants::PAM_REINITIALIZE_CRED, + Reinitialize = libpam_sys::PAM_REINITIALIZE_CRED, /// Extend the lifetime of the user's credentials from this module. - Refresh = pam_constants::PAM_REFRESH_CRED, + Refresh = libpam_sys::PAM_REFRESH_CRED, } } +#[cfg(feature = "link")] impl CredAction { /// Separates this enum from the remaining [`BaseFlags`]. - pub fn extract(value: i32) -> Result<(Self, BaseFlags)> { - Self::split(value) - .map(|(act, rest)| (act.unwrap_or_default(), BaseFlags::from_bits_retain(rest))) + pub(crate) fn extract(value: RawFlags) -> Result<(Self, BaseFlags)> { + Self::split(value).map(|(act, rest)| (act.unwrap_or_default(), BaseFlags::from(rest))) } } @@ -257,148 +242,200 @@ flag_enum! { AuthtokAction { - /// This is a preliminary call to check if we're ready to change passwords - /// and that the new password is acceptable. - PreliminaryCheck = pam_constants::PAM_PRELIM_CHECK, - /// You should actually update the password. - Update = pam_constants::PAM_UPDATE_AUTHTOK, + /// On this call, just validate that the password is acceptable + /// and that you have all the resources you need to change it. + /// + /// This corresponds to the constant `PAM_PRELIM_CHECK`. + Validate = libpam_sys::PAM_PRELIM_CHECK, + /// Actually perform the update. + /// + /// This corresponds to the constant `PAM_UPDATE_AUTHTOK`. + Update = libpam_sys::PAM_UPDATE_AUTHTOK, } } +#[cfg(feature = "link")] impl AuthtokAction { /// Separates this enum from the remaining [`AuthtokFlags`]. - pub fn extract(value: i32) -> Result<(Self, AuthtokFlags)> { + pub(crate) fn extract(value: RawFlags) -> Result<(Self, AuthtokFlags)> { match Self::split(value)? { - (Some(act), rest) => Ok((act, AuthtokFlags::from_bits_retain(rest))), + (Some(act), rest) => Ok((act, AuthtokFlags::from(rest))), (None, _) => Err(BAD_CONST), } } } -/// The PAM error return codes. -/// -/// These are returned by most PAM functions if an error of some kind occurs. -/// -/// Instead of being an error code, success is represented by an Ok [`Result`]. -/// -/// # References -/// -#[doc = linklist!(pam: man7, manbsd)] -/// - [X/SSO error code specification][xsso] -/// -#[doc = man7!(3 pam "RETURN_VALUES")] -#[doc = manbsd!(3 pam "RETURN%20VALUES")] -#[doc = xsso!("chap5.htm#tagcjh_06_02")] -#[allow(non_camel_case_types, dead_code)] -#[derive(Copy, Clone, Debug, PartialEq, TryFromPrimitive, IntoPrimitive)] -#[non_exhaustive] // C might give us anything! -#[repr(i32)] -pub enum ErrorCode { - OpenError = pam_constants::PAM_OPEN_ERR, - SymbolError = pam_constants::PAM_SYMBOL_ERR, - ServiceError = pam_constants::PAM_SERVICE_ERR, - SystemError = pam_constants::PAM_SYSTEM_ERR, - BufferError = pam_constants::PAM_BUF_ERR, - PermissionDenied = pam_constants::PAM_PERM_DENIED, - AuthenticationError = pam_constants::PAM_AUTH_ERR, - CredentialsInsufficient = pam_constants::PAM_CRED_INSUFFICIENT, - AuthInfoUnavailable = pam_constants::PAM_AUTHINFO_UNAVAIL, - UserUnknown = pam_constants::PAM_USER_UNKNOWN, - MaxTries = pam_constants::PAM_MAXTRIES, - NewAuthTokRequired = pam_constants::PAM_NEW_AUTHTOK_REQD, - AccountExpired = pam_constants::PAM_ACCT_EXPIRED, - SessionError = pam_constants::PAM_SESSION_ERR, - CredentialsUnavailable = pam_constants::PAM_CRED_UNAVAIL, - CredentialsExpired = pam_constants::PAM_CRED_EXPIRED, - CredentialsError = pam_constants::PAM_CRED_ERR, - NoModuleData = pam_constants::PAM_NO_MODULE_DATA, - ConversationError = pam_constants::PAM_CONV_ERR, - AuthTokError = pam_constants::PAM_AUTHTOK_ERR, - AuthTokRecoveryError = pam_constants::PAM_AUTHTOK_RECOVERY_ERR, - AuthTokLockBusy = pam_constants::PAM_AUTHTOK_LOCK_BUSY, - AuthTokDisableAging = pam_constants::PAM_AUTHTOK_DISABLE_AGING, - TryAgain = pam_constants::PAM_TRY_AGAIN, - Ignore = pam_constants::PAM_IGNORE, - Abort = pam_constants::PAM_ABORT, - AuthTokExpired = pam_constants::PAM_AUTHTOK_EXPIRED, - #[cfg(feature = "basic-ext")] - ModuleUnknown = pam_constants::PAM_MODULE_UNKNOWN, - #[cfg(feature = "basic-ext")] - BadItem = pam_constants::PAM_BAD_ITEM, - #[cfg(feature = "linux-pam-ext")] - ConversationAgain = pam_constants::PAM_CONV_AGAIN, - #[cfg(feature = "linux-pam-ext")] - Incomplete = pam_constants::PAM_INCOMPLETE, - #[cfg(feature = "openpam-ext")] - DomainUnknown = pam_constants::PAM_DOMAIN_UNKNOWN, - #[cfg(feature = "openpam-ext")] - BadHandle = pam_constants::PAM_BAD_HANDLE, - #[cfg(feature = "openpam-ext")] - BadFeature = pam_constants::PAM_BAD_FEATURE, - #[cfg(feature = "openpam-ext")] - BadConstant = pam_constants::PAM_BAD_CONSTANT, +/// Constructs an enum which has the values if it's linked +macro_rules! linky_enum { + ( + $(#[$om:meta])* + pub enum $name:ident($wrap:ty) { + $( + $(#[$im:meta])* + $key:ident = $value:path, + )* + } + ) => { + $(#[$om])* + #[derive(Copy, Clone, Debug, PartialEq, Eq)] + pub enum $name { + $( + $(#[$im])* + $key, + )* + } + + #[cfg(feature = "link")] + impl TryFrom<$wrap> for $name { + type Error = ErrorCode; + fn try_from(value: $wrap) -> Result<Self> { + match value.into() { + $( + $(#[$im])* + $value => Ok(Self::$key), + )* + _ => Err(BAD_CONST), + } + } + } + + #[cfg(feature = "link")] + impl From<$name> for $wrap { + fn from(value: $name) -> Self { + match value { + $( + $(#[$im])* + $name::$key => $value.into(), + )* + } + } + } + } +} + +linky_enum! { + /// The PAM error return codes. + /// + /// These are returned by most PAM functions if an error of some kind occurs. + /// + /// Instead of being an error code, success is represented by an Ok [`Result`]. + /// + /// **Do not depend upon the numerical value of these error codes, + /// or the enum's representation type. + /// The available codes and their values will vary depending upon + /// PAM implementation.** + /// + /// # References + /// + #[doc = linklist!(pam: man7, manbsd, mansun)] + /// - [X/SSO error code specification][xsso] + /// + #[doc = man7!(3 pam "RETURN_VALUES")] + #[doc = manbsd!(3 pam "RETURN%20VALUES")] + #[doc = mansun!([3 "pam"] pam "return-values")] + #[doc = xsso!("chap5.htm#tagcjh_06_02")] + #[allow(non_camel_case_types, dead_code)] + #[non_exhaustive] // Different PAMs have different error code sets. + pub enum ErrorCode(ReturnCode) { + OpenError = libpam_sys::PAM_OPEN_ERR, + SymbolError = libpam_sys::PAM_SYMBOL_ERR, + ServiceError = libpam_sys::PAM_SERVICE_ERR, + SystemError = libpam_sys::PAM_SYSTEM_ERR, + BufferError = libpam_sys::PAM_BUF_ERR, + PermissionDenied = libpam_sys::PAM_PERM_DENIED, + AuthenticationError = libpam_sys::PAM_AUTH_ERR, + CredentialsInsufficient = libpam_sys::PAM_CRED_INSUFFICIENT, + AuthInfoUnavailable = libpam_sys::PAM_AUTHINFO_UNAVAIL, + UserUnknown = libpam_sys::PAM_USER_UNKNOWN, + MaxTries = libpam_sys::PAM_MAXTRIES, + NewAuthTokRequired = libpam_sys::PAM_NEW_AUTHTOK_REQD, + AccountExpired = libpam_sys::PAM_ACCT_EXPIRED, + SessionError = libpam_sys::PAM_SESSION_ERR, + CredentialsUnavailable = libpam_sys::PAM_CRED_UNAVAIL, + CredentialsExpired = libpam_sys::PAM_CRED_EXPIRED, + CredentialsError = libpam_sys::PAM_CRED_ERR, + NoModuleData = libpam_sys::PAM_NO_MODULE_DATA, + ConversationError = libpam_sys::PAM_CONV_ERR, + AuthTokError = libpam_sys::PAM_AUTHTOK_ERR, + AuthTokRecoveryError = libpam_sys::PAM_AUTHTOK_RECOVERY_ERR, + AuthTokLockBusy = libpam_sys::PAM_AUTHTOK_LOCK_BUSY, + AuthTokDisableAging = libpam_sys::PAM_AUTHTOK_DISABLE_AGING, + TryAgain = libpam_sys::PAM_TRY_AGAIN, + Ignore = libpam_sys::PAM_IGNORE, + Abort = libpam_sys::PAM_ABORT, + AuthTokExpired = libpam_sys::PAM_AUTHTOK_EXPIRED, + #[cfg(feature = "basic-ext")] + ModuleUnknown = libpam_sys::PAM_MODULE_UNKNOWN, + #[cfg(feature = "basic-ext")] + BadItem = libpam_sys::PAM_BAD_ITEM, + #[cfg(feature = "linux-pam-ext")] + ConversationAgain = libpam_sys::PAM_CONV_AGAIN, + #[cfg(feature = "linux-pam-ext")] + Incomplete = libpam_sys::PAM_INCOMPLETE, + #[cfg(feature = "openpam-ext")] + DomainUnknown = libpam_sys::PAM_DOMAIN_UNKNOWN, + #[cfg(feature = "openpam-ext")] + BadHandle = libpam_sys::PAM_BAD_HANDLE, + #[cfg(feature = "openpam-ext")] + BadFeature = libpam_sys::PAM_BAD_FEATURE, + #[cfg(feature = "openpam-ext")] + BadConstant = libpam_sys::PAM_BAD_CONSTANT, + } } /// A PAM-specific Result type with an [ErrorCode] error. pub type Result<T> = StdResult<T, ErrorCode>; impl Display for ErrorCode { + #[cfg(all( + feature = "link", + any(pam_impl = "LinuxPam", pam_impl = "OpenPam", pam_impl = "Sun") + ))] fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - match strerror((*self).into()) { - Some(err) => f.write_str(err), - None => self.fmt_internal(f), + use std::ffi::CStr; + use std::ptr; + // SAFETY: PAM impls don't care about the PAM handle and always return + // static strings. + let got = unsafe { libpam_sys::pam_strerror(ptr::null(), *self as c_int) }; + if got.is_null() { + // This shouldn't happen. + write!(f, "PAM error: {self:?} ({:?})", *self as c_int) + } else { + // SAFETY: We just got this back from PAM and we checked if it's null. + f.write_str(&unsafe { CStr::from_ptr(got) }.to_string_lossy()) } } + #[cfg(not(all( + feature = "link", + any(pam_impl = "LinuxPam", pam_impl = "OpenPam", pam_impl = "Sun") + )))] + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + fmt::Debug::fmt(self, f) + } } impl Error for ErrorCode {} +#[cfg(feature = "link")] 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(), + pub(crate) fn result_from(ret: c_int) -> Result<()> { + match ret { + 0 => Ok(()), + value => Err(ReturnCode(value).try_into().unwrap_or(BAD_CONST)), } } - - /// 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)), - } - } - - /// A basic Display implementation for if we don't link against PAM. - fn fmt_internal(self, f: &mut Formatter<'_>) -> fmt::Result { - let n: c_int = self.into(); - write!(f, "PAM error: {self:?} ({n})") - } } -/// Gets a string version of an error message. -#[cfg(feature = "link")] -pub fn strerror(code: c_int) -> Option<&'static str> { - use std::ffi::CStr; - use std::ptr; - // SAFETY: PAM impls don't care about the PAM handle and always return - // static strings. - let strerror = unsafe { libpam_sys::pam_strerror(ptr::null(), code as c_int) }; - // SAFETY: We just got this back from PAM and we checked if it's null. - (!strerror.is_null()) - .then(|| unsafe { CStr::from_ptr(strerror) }.to_str().ok()) - .flatten() +impl<T> From<Result<T>> for ReturnCode { + fn from(value: Result<T>) -> Self { + match value { + Ok(_) => ReturnCode::SUCCESS, + Err(otherwise) => otherwise.into() + } + } } -/// Dummy implementation of strerror so that it always returns None. -#[cfg(not(feature = "link"))] -pub fn strerror(_: c_int) -> Option<&'static str> { - None -} - -#[cfg(test)] +#[cfg(all(test, feature = "link"))] mod tests { use super::*; @@ -406,35 +443,72 @@ fn test_enums() { assert_eq!(Ok(()), ErrorCode::result_from(0)); assert_eq!( - pam_constants::PAM_SESSION_ERR, - ErrorCode::result_to_c::<()>(Err(ErrorCode::SessionError)) + ReturnCode(libpam_sys::PAM_SESSION_ERR), + Result::<()>::Err(ErrorCode::SessionError).into() + ); + assert_eq!( + Result::<()>::Err(ErrorCode::Abort), + ErrorCode::result_from(libpam_sys::PAM_ABORT) + ); + assert_eq!(Err(BAD_CONST), ErrorCode::result_from(423)); + } + + #[test] + fn test_flags() { + assert_eq!( + AuthtokFlags::CHANGE_EXPIRED_AUTHTOK | AuthtokFlags::SILENT, + AuthtokFlags::from(RawFlags( + libpam_sys::PAM_SILENT | libpam_sys::PAM_CHANGE_EXPIRED_AUTHTOK + )) + ); + assert_eq!( + RawFlags(libpam_sys::PAM_DISALLOW_NULL_AUTHTOK), + AuthnFlags::DISALLOW_NULL_AUTHTOK.into() ); assert_eq!( - Err(ErrorCode::Abort), - ErrorCode::result_from(pam_constants::PAM_ABORT) + RawFlags(libpam_sys::PAM_SILENT | libpam_sys::PAM_CHANGE_EXPIRED_AUTHTOK), + (AuthtokFlags::SILENT | AuthtokFlags::CHANGE_EXPIRED_AUTHTOK).into() + ); + } + + #[test] + #[cfg(pam_impl = "Sun")] + fn test_flags_sun() { + assert_eq!( + AuthtokFlags::NO_AUTHTOK_CHECK, + AuthtokFlags::from(RawFlags(libpam_sys::PAM_NO_AUTHTOK_CHECK)) ); - assert_eq!(Err(ErrorCode::SystemError), ErrorCode::result_from(423)); + assert_eq!( + RawFlags( + libpam_sys::PAM_SILENT + | libpam_sys::PAM_CHANGE_EXPIRED_AUTHTOK + | libpam_sys::PAM_NO_AUTHTOK_CHECK + ), + (AuthtokFlags::SILENT + | AuthtokFlags::CHANGE_EXPIRED_AUTHTOK + | AuthtokFlags::NO_AUTHTOK_CHECK) + .into() + ); } #[test] fn test_flag_enums() { - AuthtokAction::extract(-1).expect_err("too many set"); - AuthtokAction::extract(0).expect_err("too few set"); + AuthtokAction::extract((-1).into()).expect_err("too many set"); + AuthtokAction::extract(0.into()).expect_err("too few set"); assert_eq!( - Ok(( - AuthtokAction::Update, - AuthtokFlags::from_bits_retain(0x7f000000) - )), - AuthtokAction::extract(0x7f000000 | pam_constants::PAM_UPDATE_AUTHTOK) + Ok((AuthtokAction::Update, AuthtokFlags::SILENT,)), + AuthtokAction::extract( + (libpam_sys::PAM_SILENT | libpam_sys::PAM_UPDATE_AUTHTOK).into() + ) ); - CredAction::extract(0xffff).expect_err("too many set"); + CredAction::extract(0xffff.into()).expect_err("too many set"); assert_eq!( Ok((CredAction::Establish, BaseFlags::empty())), - CredAction::extract(0) + CredAction::extract(0.into()) ); assert_eq!( - Ok((CredAction::Delete, BaseFlags::from_bits_retain(0x55000000))), - CredAction::extract(0x55000000 | pam_constants::PAM_DELETE_CRED) + Ok((CredAction::Delete, BaseFlags::SILENT)), + CredAction::extract((libpam_sys::PAM_SILENT | libpam_sys::PAM_DELETE_CRED).into()) ); } }