Something went wrong. Try again.
This repository has no description
Something went wrong. Try again.
17 kB · 498 lines
Rust
at master
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499use crate::pam::{Pam, PamError, PamFlags};use crate::pam_types::{ LogLvl, PamConv, PamHandle, PamItemType, PamMessage, PamMsgStyle, PamResponse,};use std::ffi::{CStr, CString, NulError};use std::ops::Deref;use std::option::Option;use std::os::raw::{c_char, c_int, c_void};use std::ptr;
pub type PamResult<T> = Result<T, PamError>;/// Prototype of the callback used with [`PamLibExt::send_bytes`]pub type PamCleanupCb = fn(&Vec<u8>, Pam, PamFlags, PamError);
/// Owned copy of the PAM environment list returned by [`PamLibExt::getenvlist`].#[derive(Clone, Debug, Default, Eq, PartialEq)]pub struct PamEnvList { env: Vec<CString>,}
impl PamEnvList { /// Iterate over environment entries in `NAME=value` form. pub fn iter(&self) -> impl Iterator<Item = &CStr> { self.env.iter().map(CString::as_c_str) }
/// Return the copied environment entries in `NAME=value` form. pub fn into_vec(self) -> Vec<CString> { self.env }}
#[derive(Clone)]struct PamByteData { cb: Option<PamCleanupCb>, data: Vec<u8>,}
/// Trait to implement for data stored with pam using [`PamLibExt::send_data`]/// in order to provide a cleanup callback./// # Example/// ```/// extern crate pamsm;/// use pamsm::{Pam, PamData, PamError, PamFlags};/// use std::fs::write;////// struct Token([u8; 32]);////// impl PamData for Token {/// fn cleanup(&self, _pam: Pam, flags: PamFlags, status: PamError) {/// if !flags.contains(PamFlags::DATA_REPLACE) && status == PamError::SUCCESS {/// match write(".token.bin", self.0) {/// Ok(_) => (),/// Err(err) => {/// if !flags.contains(PamFlags::SILENT) {/// println!("Error persisting token : {:?}", err);/// }/// }/// };/// }/// }/// }/// ```pub trait PamData { /// The cleanup method will be called before the data is dropped by pam. /// See `pam_set_data (3)` fn cleanup(&self, _pam: Pam, _flags: PamFlags, _status: PamError) {}}
impl PamData for PamByteData { fn cleanup(&self, pam: Pam, flags: PamFlags, status: PamError) { if let Some(cb) = self.cb { (cb)(&self.data, pam, flags, status); } }}
/// Blanket implementation for types that implement `Deref<T>` when `T` implements `PamData`.impl<T: PamData, U> PamData for Uwhere U: Deref<Target = T>,{ fn cleanup(&self, pam: Pam, flags: PamFlags, status: PamError) { T::cleanup(self, pam, flags, status) }}
impl PamError { fn to_result<T>(self, ok: T) -> PamResult<T> { if self == PamError::SUCCESS { Ok(ok) } else { Err(self) } }}
/// This contains a private marker trait, used to seal private traits.mod private { pub trait Sealed {} impl Sealed for super::Pam {}}
impl Pam { // End users should call the item specific methods fn get_cstr_item(&self, item_type: PamItemType) -> PamResult<Option<&CStr>> { match item_type { PamItemType::CONV | PamItemType::FAIL_DELAY | PamItemType::XAUTHDATA => { panic!("Error, get_cstr_item can only be used with pam item returning c-strings") } _ => (), } let mut raw_item: *const c_void = ptr::null(); let r = unsafe { PamError::new(pam_get_item(self.0, item_type as c_int, &mut raw_item)) }; if raw_item.is_null() { r.to_result(None) } else { // pam should keep the underlying token allocated during the lifetime of the module r.to_result(Some(unsafe { CStr::from_ptr(raw_item as *const c_char) })) } }}
/// Extension trait over `Pam`, usually provided by the `libpam` shared library.pub trait PamLibExt: private::Sealed { /// Get the username. If the PAM_USER item is not set, this function /// prompts for a username (like get_authtok). /// Returns PamError::SERVICE_ERR if the prompt contains any null byte fn get_user(&self, prompt: Option<&str>) -> PamResult<Option<&CStr>>;
fn get_tty(&self) -> PamResult<Option<&CStr>>;
/// Get the username, i.e. the PAM_USER item. If it's not set return None. fn get_cached_user(&self) -> PamResult<Option<&CStr>>;
/// Get the cached authentication token. fn get_cached_authtok(&self) -> PamResult<Option<&CStr>>;
/// Get the cached old authentication token. fn get_cached_oldauthtok(&self) -> PamResult<Option<&CStr>>;
/// Get the cached authentication token or prompt the user for one if there isn't any. /// Returns PamError::SERVICE_ERR if the prompt contains any null byte fn get_authtok(&self, prompt: Option<&str>) -> PamResult<Option<&CStr>>;
fn set_authtok(&self, authtok: &CString) -> PamResult<()>;
/// Get the remote hostname. fn get_rhost(&self) -> PamResult<Option<&CStr>>;
/// Get the remote username. fn get_ruser(&self) -> PamResult<Option<&CStr>>;
/// Get the service name. fn get_service(&self) -> PamResult<Option<&CStr>>;
/// Prompt the user for custom input. /// Returns PamError::SERVICE_ERR if the prompt contains any null byte fn conv(&self, prompt: Option<&str>, style: PamMsgStyle) -> PamResult<Option<&CStr>>;
/// Get a variable from the pam environment list. fn getenv(&self, name: &str) -> PamResult<Option<&CStr>>;
/// Get a copy of the full pam environment list. fn getenvlist(&self) -> PamResult<PamEnvList>;
/// Put a variable in the pam environment list. /// `name_value` takes for form documented in pam_putent(3) : /// /// - `NAME=value` will set variable `NAME` to value `value` /// - `NAME=` will set variable `NAME` to an empty value /// - `NAME` will unset the variable `NAME` fn putenv(&self, name_value: &str) -> PamResult<()>;
/// Send data to be stored by the pam library under the name `module_name`. /// The data can then be retrieved from a different /// callback in this module, or even by a different module /// using [`retrieve_data<T>`][Self::retrieve_data]. /// /// When this method is called a second time with the same `module_name`, the method /// [`PamData::cleanup`] is called on the data previously stored. /// The same happens when the application calls `pam_end (3)` /// /// If your data can be converted into / from [`Vec<u8>`][std::vec::Vec] /// you should consider using the [`send_bytes`][Self::send_bytes] method instead. /// /// # Safety /// This method should not be used if the [`send_bytes`][Self::send_bytes] method is also used /// with the same `module_name`. unsafe fn send_data<T: PamData + Clone + Send>( &self, module_name: &str, data: T, ) -> PamResult<()>;
/// Retrieve data previously stored with [`send_data<T>`][Self::send_data]. /// /// Note that the result is a _copy_ of the data and not a shared reference, /// which differs from the behavior of the underlying `pam_get_data (3)` function. /// /// If you want to share the data instead you can wrap it in [`Arc`][std::sync::Arc]. /// # Safety /// The type parameter `T` must be the same as the one used in /// [`send_data<T>`][Self::send_data] with the name `module_name`. /// /// If the data was stored with [`send_bytes`][Self::send_bytes] you must use /// [`retrieve_bytes`][Self::retrieve_bytes] instead. unsafe fn retrieve_data<T: PamData + Clone + Send>(&self, module_name: &str) -> PamResult<T>;
/// Similar to [`send_data`][Self::send_data], but only works with [`Vec<u8>`][std::vec::Vec]. /// The PamData trait doesn't have to be implemented on the data, a callback can be passed /// as an argument instead. fn send_bytes( &self, module_name: &str, data: Vec<u8>, cb: Option<PamCleanupCb>, ) -> PamResult<()>;
/// Retrieve bytes previously stored with [`send_bytes`][Self::send_bytes]. /// The result is a clone of the data. fn retrieve_bytes(&self, module_name: &str) -> PamResult<Vec<u8>>;
/// Send a message to syslog. fn syslog(&self, lvl: LogLvl, msg: &str) -> PamResult<()>;}
impl From<NulError> for PamError { fn from(_: NulError) -> PamError { PamError::SERVICE_ERR }}
impl PamLibExt for Pam { fn get_user(&self, prompt: Option<&str>) -> PamResult<Option<&CStr>> { let cprompt = match prompt { None => None, Some(p) => Some(CString::new(p)?), }; let mut raw_user: *const c_char = ptr::null(); let r = unsafe { PamError::new(pam_get_user( self.0, &mut raw_user, cprompt.as_ref().map_or(ptr::null(), |p| p.as_ptr()), )) };
if raw_user.is_null() { r.to_result(None) } else { r.to_result(Some(unsafe { CStr::from_ptr(raw_user) })) } }
fn get_tty(&self) -> PamResult<Option<&CStr>> { self.get_cstr_item(PamItemType::TTY) }
fn get_cached_user(&self) -> PamResult<Option<&CStr>> { self.get_cstr_item(PamItemType::USER) }
fn get_cached_authtok(&self) -> PamResult<Option<&CStr>> { self.get_cstr_item(PamItemType::AUTHTOK) }
fn get_cached_oldauthtok(&self) -> PamResult<Option<&CStr>> { self.get_cstr_item(PamItemType::OLDAUTHTOK) }
fn get_authtok(&self, prompt: Option<&str>) -> PamResult<Option<&CStr>> { let cprompt = match prompt { None => None, Some(p) => Some(CString::new(p)?), }; let mut raw_at: *const c_char = ptr::null(); let r = unsafe { PamError::new(pam_get_authtok( self.0, PamItemType::AUTHTOK as i32, &mut raw_at, cprompt.as_ref().map_or(ptr::null(), |p| p.as_ptr()), )) };
if raw_at.is_null() { r.to_result(None) } else { r.to_result(unsafe { Some(CStr::from_ptr(raw_at)) }) } }
fn set_authtok(&self, authtok: &CString) -> PamResult<()> { unsafe { set_item( self.0, PamItemType::AUTHTOK, authtok.as_ptr() as *const c_void, ) } }
fn get_rhost(&self) -> PamResult<Option<&CStr>> { self.get_cstr_item(PamItemType::RHOST) }
fn get_ruser(&self) -> PamResult<Option<&CStr>> { self.get_cstr_item(PamItemType::RUSER) }
fn get_service(&self) -> PamResult<Option<&CStr>> { self.get_cstr_item(PamItemType::SERVICE) }
fn conv(&self, prompt: Option<&str>, style: PamMsgStyle) -> PamResult<Option<&CStr>> { let mut conv_pointer: *const c_void = ptr::null(); let r = unsafe { PamError::new(pam_get_item( self.0, PamItemType::CONV as c_int, &mut conv_pointer, )) };
if r != PamError::SUCCESS { return Err(r); }
if conv_pointer.is_null() { return Ok(None); }
let conv = unsafe { &*(conv_pointer as *const PamConv) }; let mut resp_ptr: *mut PamResponse = ptr::null_mut(); let msg_cstr = CString::new(prompt.unwrap_or(""))?; let msg = PamMessage { msg_style: style, msg: msg_cstr.as_ptr(), };
match conv.cb.map(|cb| { PamError::new(cb( 1, &mut (&msg as *const PamMessage), &mut resp_ptr, conv.appdata_ptr, )) }) { Some(PamError::SUCCESS) => { Ok(unsafe { (*resp_ptr).resp }.map(|r| unsafe { CStr::from_ptr(r.as_ptr()) })) } Some(ret) => Err(ret), None => Ok(None), } }
fn getenv(&self, name: &str) -> PamResult<Option<&CStr>> { let cname = CString::new(name)?; let cenv = unsafe { pam_getenv(self.0, cname.as_ptr()) };
if cenv.is_null() { Ok(None) } else { unsafe { Ok(Some(CStr::from_ptr(cenv))) } } }
fn getenvlist(&self) -> PamResult<PamEnvList> { let raw_env = unsafe { pam_getenvlist(self.0) }; if raw_env.is_null() { return Ok(PamEnvList::default()); }
let mut env = Vec::new(); let mut entry = raw_env; unsafe { while !(*entry).is_null() { env.push(CStr::from_ptr(*entry).to_owned()); free((*entry).cast::<c_void>()); entry = entry.add(1); } free(raw_env.cast::<c_void>()); }
Ok(PamEnvList { env }) }
fn putenv(&self, name_value: &str) -> PamResult<()> { let cenv = CString::new(name_value)?; unsafe { PamError::new(pam_putenv(self.0, cenv.as_ptr())).to_result(()) } }
unsafe fn send_data<T: PamData + Clone + Send>( &self, module_name: &str, data: T, ) -> PamResult<()> { // The data has to be allocated on the heap because it will outlive the call stack. let data_copy = Box::new(data); PamError::new(unsafe { pam_set_data( self.0, CString::new(module_name)?.as_ptr(), Box::into_raw(data_copy) as *mut c_void, Some(pam_data_cleanup::<T>), ) }) .to_result(()) }
unsafe fn retrieve_data<T: PamData + Clone + Send>(&self, module_name: &str) -> PamResult<T> { let mut data_ptr: *const c_void = ptr::null(); // pam_get_data should be safe as long as T is the type that what used in send_data. PamError::new(unsafe { pam_get_data(self.0, CString::new(module_name)?.as_ptr(), &mut data_ptr) }) .to_result(data_ptr as *const T) .map(|ptr| unsafe { (*ptr).clone() }) // pam guaranties the data is valid when SUCCESS is returned. }
fn send_bytes( &self, module_name: &str, data: Vec<u8>, cb: Option<PamCleanupCb>, ) -> PamResult<()> { let data_cb = PamByteData { cb, data }; unsafe { self.send_data(module_name, data_cb) } }
fn retrieve_bytes(&self, module_name: &str) -> PamResult<Vec<u8>> { unsafe { self.retrieve_data::<PamByteData>(module_name) }.map(|data_cb| data_cb.data) }
fn syslog(&self, lvl: LogLvl, msg: &str) -> PamResult<()> { let fmt = b"%s\0".as_ptr() as *const c_char; let cmsg = CString::new(msg)?; unsafe { pam_syslog(self.0, lvl as c_int, fmt, cmsg.as_ptr()); } Ok(()) }}
unsafe extern "C" fn pam_data_cleanup<T: PamData + Clone + Send>( handle: PamHandle, data: *mut c_void, error_status: c_int,) { unsafe { Box::from_raw(data as *mut T).cleanup( Pam(handle), PamFlags::from_bits_truncate(error_status), PamError::new(error_status & 0xff), ) };}
unsafe fn set_item(pamh: PamHandle, item_type: PamItemType, item: *const c_void) -> PamResult<()> { PamError::new(unsafe { pam_set_item(pamh, item_type as c_int, item) }).to_result(())}
// Raw functions#[link(name = "pam")]unsafe extern "C" { pub fn pam_set_item(pamh: PamHandle, item_type: c_int, item: *const c_void) -> c_int; pub fn pam_get_item(pamh: PamHandle, item_type: c_int, item: *mut *const c_void) -> c_int; pub fn pam_strerror(pamh: PamHandle, errnum: c_int) -> *const c_char; pub fn pam_putenv(pamh: PamHandle, name_value: *const c_char) -> c_int; pub fn pam_getenv(pamh: PamHandle, name: *const c_char) -> *const c_char; pub fn pam_getenvlist(pamh: PamHandle) -> *mut *mut c_char;
pub fn pam_set_data( pamh: PamHandle, module_data_name: *const c_char, data: *mut c_void, cleanup: Option<unsafe extern "C" fn(_: PamHandle, _: *mut c_void, _: c_int)>, ) -> c_int; pub fn pam_get_data( pamh: PamHandle, module_data_name: *const c_char, data: *mut *const c_void, ) -> c_int; pub fn pam_get_user(pamh: PamHandle, user: *mut *const c_char, prompt: *const c_char) -> c_int; pub fn pam_get_authtok( pamh: PamHandle, item: c_int, authok_ptr: *mut *const c_char, prompt: *const c_char, ) -> c_int;
pub fn pam_syslog(pamh: PamHandle, priority: c_int, fmt: *const c_char, ...) -> c_void;}
unsafe extern "C" { fn free(ptr: *mut c_void);}