// Copyright (c) 2017 raphael.catolino@gmail.com
#![allow(non_camel_case_types)]
#![allow(clippy::upper_case_acronyms)]
use pam_types::PamHandle;
use std::fmt;
use std::os::raw::c_int;
/// Opaque PAM handle, with additional native methods available via `PamLibExt`.
#[repr(transparent)]
pub struct Pam(pub(crate) PamHandle);
bitflags! {
pub struct PamFlags : c_int {
const DATA_REPLACE = 0x2000_0000;
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;
}
}
impl fmt::Display for PamError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{:?}", self)
}
}
macro_rules! int_enum {
( $name:ident ($ukey:ident = $uvalue:expr) {
$( $key:ident = $value:expr ),*
}) => {
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum $name {
$( $key = $value, )*
$ukey = $uvalue,
}
impl $name {
#[cfg(feature = "libpam")]
pub(crate) fn new(r: c_int) -> $name {
match r {
$( $value => $name::$key, )*
_ => $name::$ukey,
}
}
}
}
}
int_enum! {
PamError (UNKNOWN_RESULT = -1) {
SUCCESS = 0, /* Successful function return */
OPEN_ERR = 1, /* dlopen() failure when dynamically */
SYMBOL_ERR = 2, /* Symbol not found */
SERVICE_ERR = 3, /* Error in service module */
SYSTEM_ERR = 4, /* System error */
BUF_ERR = 5, /* Memory buffer error */
PERM_DENIED = 6, /* Permission denied */
AUTH_ERR = 7, /* Authentication failure */
CRED_INSUFFICIENT = 8, /* Can not access authentication data */
AUTHINFO_UNAVAIL = 9, /* Underlying authentication service can not retrieve authentication information */
USER_UNKNOWN = 10, /* User not known to the underlying authenticaiton module */
MAXTRIES = 11, /* An authentication service has maintained a retry count which has been reached. No further retries should be attempted */
NEW_AUTHTOK_REQD = 12, /* New authentication token required. */
ACCT_EXPIRED = 13, /* User account has expired */
SESSION_ERR = 14, /* Can not make/remove an entry for the specified session */
CRED_UNAVAIL = 15, /* Underlying authentication service can not retrieve user credentials */
CRED_EXPIRED = 16, /* User credentials expired */
CRED_ERR = 17, /* Failure setting user credentials */
NO_MODULE_DATA = 18, /* No module specific data is present */
CONV_ERR = 19, /* Conversation error */
AUTHTOK_ERR = 20, /* Authentication token manipulation error */
AUTHTOK_RECOVERY_ERR = 21, /* Authentication information cannot be recovered */
AUTHTOK_LOCK_BUSY = 22, /* Authentication token lock busy */
AUTHTOK_DISABLE_AGING = 23, /* Authentication token aging disabled */
TRY_AGAIN = 24, /* Preliminary check by password service */
IGNORE = 25, /* Ignore underlying account module regardless of whether the control flag is required, optional, or sufficient */
ABORT = 26, /* Critical error (?module fail now request) */
AUTHTOK_EXPIRED = 27, /* user's authentication token has expired */
MODULE_UNKNOWN = 28, /* module is not known */
BAD_ITEM = 29, /* Bad item passed to *_item() */
CONV_AGAIN = 30, /* conversation function is event driven and data is not available yet */
INCOMPLETE = 31 /* please call this function again to complete authentication stack. Before calling again, verify that conversation is completed */
}
}
/// Default service module implementation.
/// All default functions return SERVICE_ERR.
/// You can override functions depending on what kind of module you implement.
/// See the respective pam_sm_* man pages for documentation.
pub trait PamServiceModule {
fn open_session(_: Pam, _: PamFlags, _: Vec<String>) -> PamError {
PamError::SERVICE_ERR
}
fn close_session(_: Pam, _: PamFlags, _: Vec<String>) -> PamError {
PamError::SERVICE_ERR
}
fn authenticate(_: Pam, _: PamFlags, _: Vec<String>) -> PamError {
PamError::SERVICE_ERR
}
fn setcred(_: Pam, _: PamFlags, _: Vec<String>) -> PamError {
PamError::SERVICE_ERR
}
fn acct_mgmt(_: Pam, _: PamFlags, _: Vec<String>) -> PamError {
PamError::SERVICE_ERR
}
fn chauthtok(_: Pam, _: PamFlags, _: Vec<String>) -> PamError {
PamError::SERVICE_ERR
}
}
/// Define entrypoints for the PAM module.
///
/// This macro must be called exactly once in a PAM module.
/// It then exports all the pam_sm_* symbols.
///
/// The argument to the macro is a type implementing the
/// `PamServiceModule` trait.
///
/// # Example
///
/// ```ignore
/// // lib.rs
/// #[macro_use] extern crate pamsm;
///
/// pam_module!(MyPamService);
/// ```
#[macro_export]
macro_rules! pam_module {
($pamsm_ty:ty) => {
// Check trait bound on input type.
fn _check_pamsm_trait<T: pamsm::PamServiceModule>() {}
fn _t() {
_check_pamsm_trait::<$pamsm_ty>()
}
// Callback entry definition.
macro_rules! pam_callback {
($pam_cb:ident, $rust_cb:ident) => {
#[no_mangle]
#[doc(hidden)]
pub unsafe extern "C" fn $pam_cb(
pamh: pamsm::Pam,
flags: std::os::raw::c_int,
argc: std::os::raw::c_int,
argv: *const *const std::os::raw::c_char,
) -> std::os::raw::c_int {
use std::os::raw::c_int;
if argc < 0 {
return pamsm::PamError::SERVICE_ERR as std::os::raw::c_int;
}
let mut args = Vec::<String>::with_capacity(argc as usize);
for count in 0..(argc as isize) {
match {
std::ffi::CStr::from_ptr(
*argv.offset(count) as *const std::os::raw::c_char
)
.to_str()
} {
Ok(s) => args.push(s.to_owned()),
Err(_) => return pamsm::PamError::SERVICE_ERR as c_int,
};
}
<$pamsm_ty>::$rust_cb(pamh, PamFlags::from_bits_unchecked(flags), args)
as c_int
}
};
}
pam_callback!(pam_sm_open_session, open_session);
pam_callback!(pam_sm_close_session, close_session);
pam_callback!(pam_sm_authenticate, authenticate);
pam_callback!(pam_sm_setcred, setcred);
pam_callback!(pam_sm_acct_mgmt, acct_mgmt);
pam_callback!(pam_sm_chauthtok, chauthtok);
};
}