use libc::c_char;
use std::ffi::{CStr, CString};
use constants::{PamFlag, PamResultCode};
#[repr(C)]
pub struct PamHandle {
_data: [u8; 0],
}
#[link(name = "pam")]
extern "C" {
fn pam_get_data(
pamh: *const PamHandle,
module_data_name: *const c_char,
data: &mut *const libc::c_void,
) -> PamResultCode;
fn pam_set_data(
pamh: *const PamHandle,
module_data_name: *const c_char,
data: *mut libc::c_void,
cleanup: extern "C" fn(
pamh: *const PamHandle,
data: *mut libc::c_void,
error_status: PamResultCode,
),
) -> PamResultCode;
fn pam_get_item(
pamh: *const PamHandle,
item_type: crate::items::ItemType,
item: &mut *const libc::c_void,
) -> PamResultCode;
fn pam_set_item(
pamh: *mut PamHandle,
item_type: crate::items::ItemType,
item: *const libc::c_void,
) -> PamResultCode;
fn pam_get_user(
pamh: *const PamHandle,
user: &mut *const c_char,
prompt: *const c_char,
) -> PamResultCode;
}
pub extern "C" fn cleanup<T>(_: *const PamHandle, c_data: *mut libc::c_void, _: PamResultCode) {
unsafe {
let _data: Box<T> = Box::from_raw(c_data.cast::<T>());
}
}
pub type PamResult<T> = Result<T, PamResultCode>;
impl PamHandle {
pub unsafe fn get_data<'a, T>(&'a self, key: &str) -> PamResult<&'a T> {
let c_key = CString::new(key).unwrap();
let mut ptr: *const libc::c_void = std::ptr::null();
let res = pam_get_data(self, c_key.as_ptr(), &mut ptr);
if PamResultCode::PAM_SUCCESS == res && !ptr.is_null() {
let typed_ptr = ptr.cast::<T>();
let data: &T = &*typed_ptr;
Ok(data)
} else {
Err(res)
}
}
pub fn set_data<T>(&self, key: &str, data: Box<T>) -> PamResult<()> {
let c_key = CString::new(key).unwrap();
let res = unsafe {
pam_set_data(
self,
c_key.as_ptr(),
Box::into_raw(data).cast::<libc::c_void>(),
cleanup::<T>,
)
};
if PamResultCode::PAM_SUCCESS == res {
Ok(())
} else {
Err(res)
}
}
pub fn get_item<T: crate::items::Item>(&self) -> PamResult<Option<T>> {
let mut ptr: *const libc::c_void = std::ptr::null();
let (res, item) = unsafe {
let r = pam_get_item(self, T::type_id(), &mut ptr);
let typed_ptr = ptr.cast::<T::Raw>();
let t = if typed_ptr.is_null() {
None
} else {
Some(T::from_raw(typed_ptr))
};
(r, t)
};
if PamResultCode::PAM_SUCCESS == res {
Ok(item)
} else {
Err(res)
}
}
pub fn set_item_str<T: crate::items::Item>(&mut self, item: T) -> PamResult<()> {
let res =
unsafe { pam_set_item(self, T::type_id(), item.into_raw().cast::<libc::c_void>()) };
if PamResultCode::PAM_SUCCESS == res {
Ok(())
} else {
Err(res)
}
}
pub fn get_user(&self, prompt: Option<&str>) -> PamResult<String> {
let mut ptr: *const c_char = std::ptr::null_mut();
let prompt_string;
let c_prompt = match prompt {
Some(p) => {
prompt_string = CString::new(p).unwrap();
prompt_string.as_ptr()
}
None => std::ptr::null(),
};
let res = unsafe { pam_get_user(self, &mut ptr, c_prompt) };
if PamResultCode::PAM_SUCCESS == res && !ptr.is_null() {
let const_ptr = ptr as *const c_char;
let bytes = unsafe { CStr::from_ptr(const_ptr).to_bytes() };
String::from_utf8(bytes.to_vec()).map_err(|_| PamResultCode::PAM_CONV_ERR)
} else {
Err(res)
}
}
}
#[allow(unused_variables)]
pub trait PamHooks {
fn acct_mgmt(pamh: &mut PamHandle, args: Vec<&CStr>, flags: PamFlag) -> PamResultCode {
PamResultCode::PAM_IGNORE
}
fn sm_authenticate(pamh: &mut PamHandle, args: Vec<&CStr>, flags: PamFlag) -> PamResultCode {
PamResultCode::PAM_IGNORE
}
fn sm_chauthtok(pamh: &mut PamHandle, args: Vec<&CStr>, flags: PamFlag) -> PamResultCode {
PamResultCode::PAM_IGNORE
}
fn sm_close_session(pamh: &mut PamHandle, args: Vec<&CStr>, flags: PamFlag) -> PamResultCode {
PamResultCode::PAM_IGNORE
}
fn sm_open_session(pamh: &mut PamHandle, args: Vec<&CStr>, flags: PamFlag) -> PamResultCode {
PamResultCode::PAM_IGNORE
}
fn sm_setcred(pamh: &mut PamHandle, args: Vec<&CStr>, flags: PamFlag) -> PamResultCode {
PamResultCode::PAM_IGNORE
}
}