use std::{ffi::NulError, fmt};
use crate::cutils::string_from_ptr;
use super::sys::*;
pub type PamResult<T, E = PamError> = Result<T, E>;
#[derive(PartialEq, Eq, Debug)]
pub enum PamErrorType {
Success,
OpenError,
SymbolError,
ServiceError,
SystemError,
BufferError,
ConversationError,
PermissionDenied,
MaxTries,
AuthError,
NewAuthTokenRequired,
CredentialsInsufficient,
AuthInfoUnavailable,
UserUnknown,
CredentialsUnavailable,
CredentialsExpired,
CredentialsError,
AccountExpired,
AuthTokenExpired,
SessionError,
AuthTokenError,
AuthTokenRecoveryError,
AuthTokenLockBusy,
AuthTokenDisableAging,
NoModuleData,
Ignore,
Abort,
TryAgain,
ModuleUnknown,
BadItem, UnknownErrorType(i32),
}
impl PamErrorType {
pub(super) fn from_int(errno: libc::c_int) -> PamErrorType {
use PamErrorType::*;
match errno as libc::c_uint {
PAM_SUCCESS => Success,
PAM_OPEN_ERR => OpenError,
PAM_SYMBOL_ERR => SymbolError,
PAM_SERVICE_ERR => ServiceError,
PAM_SYSTEM_ERR => SystemError,
PAM_BUF_ERR => BufferError,
PAM_CONV_ERR => ConversationError,
PAM_PERM_DENIED => PermissionDenied,
PAM_MAXTRIES => MaxTries,
PAM_AUTH_ERR => AuthError,
PAM_NEW_AUTHTOK_REQD => NewAuthTokenRequired,
PAM_CRED_INSUFFICIENT => CredentialsInsufficient,
PAM_AUTHINFO_UNAVAIL => AuthInfoUnavailable,
PAM_USER_UNKNOWN => UserUnknown,
PAM_CRED_UNAVAIL => CredentialsUnavailable,
PAM_CRED_EXPIRED => CredentialsExpired,
PAM_CRED_ERR => CredentialsError,
PAM_ACCT_EXPIRED => AccountExpired,
PAM_AUTHTOK_EXPIRED => AuthTokenExpired,
PAM_SESSION_ERR => SessionError,
PAM_AUTHTOK_ERR => AuthTokenError,
PAM_AUTHTOK_RECOVERY_ERR => AuthTokenRecoveryError,
PAM_AUTHTOK_LOCK_BUSY => AuthTokenLockBusy,
PAM_AUTHTOK_DISABLE_AGING => AuthTokenDisableAging,
PAM_NO_MODULE_DATA => NoModuleData,
PAM_IGNORE => Ignore,
PAM_ABORT => Abort,
PAM_TRY_AGAIN => TryAgain,
PAM_MODULE_UNKNOWN => ModuleUnknown,
PAM_BAD_ITEM => BadItem,
_ => UnknownErrorType(errno),
}
}
pub fn as_int(&self) -> libc::c_int {
use PamErrorType::*;
match self {
Success => PAM_SUCCESS as libc::c_int,
OpenError => PAM_OPEN_ERR as libc::c_int,
SymbolError => PAM_SYMBOL_ERR as libc::c_int,
ServiceError => PAM_SERVICE_ERR as libc::c_int,
SystemError => PAM_SYSTEM_ERR as libc::c_int,
BufferError => PAM_BUF_ERR as libc::c_int,
ConversationError => PAM_CONV_ERR as libc::c_int,
PermissionDenied => PAM_PERM_DENIED as libc::c_int,
MaxTries => PAM_MAXTRIES as libc::c_int,
AuthError => PAM_AUTH_ERR as libc::c_int,
NewAuthTokenRequired => PAM_NEW_AUTHTOK_REQD as libc::c_int,
CredentialsInsufficient => PAM_CRED_INSUFFICIENT as libc::c_int,
AuthInfoUnavailable => PAM_AUTHINFO_UNAVAIL as libc::c_int,
UserUnknown => PAM_USER_UNKNOWN as libc::c_int,
CredentialsUnavailable => PAM_CRED_UNAVAIL as libc::c_int,
CredentialsExpired => PAM_CRED_EXPIRED as libc::c_int,
CredentialsError => PAM_CRED_ERR as libc::c_int,
AccountExpired => PAM_ACCT_EXPIRED as libc::c_int,
AuthTokenExpired => PAM_AUTHTOK_EXPIRED as libc::c_int,
SessionError => PAM_SESSION_ERR as libc::c_int,
AuthTokenError => PAM_AUTHTOK_ERR as libc::c_int,
AuthTokenRecoveryError => PAM_AUTHTOK_RECOVERY_ERR as libc::c_int,
AuthTokenLockBusy => PAM_AUTHTOK_LOCK_BUSY as libc::c_int,
AuthTokenDisableAging => PAM_AUTHTOK_DISABLE_AGING as libc::c_int,
NoModuleData => PAM_NO_MODULE_DATA as libc::c_int,
Ignore => PAM_IGNORE as libc::c_int,
Abort => PAM_ABORT as libc::c_int,
TryAgain => PAM_TRY_AGAIN as libc::c_int,
ModuleUnknown => PAM_MODULE_UNKNOWN as libc::c_int,
BadItem => PAM_BAD_ITEM as libc::c_int,
UnknownErrorType(e) => *e,
}
}
fn get_err_msg(&self) -> String {
let data = unsafe { pam_strerror(std::ptr::null_mut(), self.as_int()) };
if data.is_null() {
String::from("Error unresolved by PAM")
} else {
unsafe { string_from_ptr(data) }
}
}
}
#[derive(Debug)]
pub enum PamError {
UnexpectedNulByte(NulError),
InvalidState,
Pam(PamErrorType, String),
IoError(std::io::Error),
SessionAlreadyOpen,
SessionNotOpen,
EnvListFailure,
InteractionRequired,
}
impl From<std::io::Error> for PamError {
fn from(err: std::io::Error) -> Self {
PamError::IoError(err)
}
}
impl From<NulError> for PamError {
fn from(err: NulError) -> Self {
PamError::UnexpectedNulByte(err)
}
}
impl fmt::Display for PamError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
PamError::UnexpectedNulByte(_) => write!(f, "Unexpected nul byte in input"),
PamError::InvalidState => {
write!(
f,
"Could not initiate pam because the state is not complete"
)
}
PamError::Pam(tp, msg) => write!(f, "PAM returned an error ({tp:?}): {msg}"),
PamError::IoError(e) => write!(f, "IO error: {e}"),
PamError::SessionAlreadyOpen => {
write!(f, "Cannot open session while one is already open")
}
PamError::SessionNotOpen => write!(f, "Cannot close session while none is open"),
PamError::EnvListFailure => {
write!(
f,
"It was not possible to get a list of environment variables"
)
}
PamError::InteractionRequired => write!(f, "Interaction is required"),
}
}
}
impl PamError {
pub(super) fn from_pam(errno: libc::c_int) -> PamError {
let tp = PamErrorType::from_int(errno);
let msg = tp.get_err_msg();
PamError::Pam(tp, msg)
}
}
pub(super) fn pam_err(err: libc::c_int) -> Result<(), PamError> {
if err == PAM_SUCCESS as libc::c_int {
Ok(())
} else {
Err(PamError::from_pam(err))
}
}
#[cfg(test)]
mod test {
use super::PamErrorType;
#[test]
fn isomorphy() {
for i in -100..100 {
let pam = PamErrorType::from_int(i);
assert_eq!(pam.as_int(), i);
assert_eq!(PamErrorType::from_int(pam.as_int()), pam);
}
}
}