Mercurial > crates > nonstick
diff pam/src/module.rs @ 15:27730595f1ea
Adding pam-http module
author | Anthony Nowell <anthony@algorithmia.com> |
---|---|
date | Sun, 24 Sep 2017 00:22:29 -0600 |
parents | |
children | d654aa0655e5 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pam/src/module.rs Sun Sep 24 00:22:29 2017 -0600 @@ -0,0 +1,179 @@ +//! Functions for use in pam modules. + +use libc::c_char; +use std::{mem, ptr}; +use std::ffi::{CStr, CString}; + +use constants::{PamResultCode, PamItemType}; + +/// Opaque type, used as a pointer when making pam API calls. +/// +/// A module is invoked via an external function such as `pam_sm_authenticate`. +/// Such a call provides a pam handle pointer. The same pointer should be given +/// as an argument when making API calls. +#[allow(missing_copy_implementations)] +pub enum PamHandleT {} + +#[allow(missing_copy_implementations)] +enum PamItemT {} + +#[allow(missing_copy_implementations)] +pub enum PamDataT {} + +#[link(name = "pam")] +extern "C" { + fn pam_get_data(pamh: *const PamHandleT, + module_data_name: *const c_char, + data: &mut *const PamDataT) + -> PamResultCode; + + fn pam_set_data(pamh: *const PamHandleT, + module_data_name: *const c_char, + data: Box<PamDataT>, + cleanup: extern "C" fn(pamh: *const PamHandleT, + data: Box<PamDataT>, + error_status: PamResultCode)) + -> PamResultCode; + + fn pam_get_item(pamh: *const PamHandleT, + item_type: PamItemType, + item: &mut *const PamItemT) + -> PamResultCode; + + fn pam_set_item(pamh: *mut PamHandleT, + item_type: PamItemType, + item: &PamItemT) + -> PamResultCode; + + fn pam_get_user(pamh: *const PamHandleT, + user: &*mut c_char, + prompt: *const c_char) + -> PamResultCode; +} + +pub type PamResult<T> = Result<T, PamResultCode>; + +/// Type-level mapping for safely retrieving values with `get_item`. +/// +/// See `pam_get_item` in +/// http://www.linux-pam.org/Linux-PAM-html/mwg-expected-by-module-item.html +pub trait PamItem { + /// Maps a Rust type to a pam constant. + /// + /// For example, the type PamConv maps to the constant PAM_CONV. The pam + /// API contract specifies that when the API function `pam_get_item` is + /// called with the constant PAM_CONV, it will return a value of type + /// `PamConv`. + fn item_type() -> PamItemType; +} + +/// Gets some value, identified by `key`, that has been set by the module +/// previously. +/// +/// See `pam_get_data` in +/// http://www.linux-pam.org/Linux-PAM-html/mwg-expected-by-module-item.html +pub unsafe fn get_data<'a, T>(pamh: &'a PamHandleT, key: &str) -> PamResult<&'a T> { + let c_key = CString::new(key).unwrap().as_ptr(); + let mut ptr: *const PamDataT = ptr::null(); + let res = pam_get_data(pamh, c_key, &mut ptr); + if PamResultCode::PAM_SUCCESS == res && !ptr.is_null() { + let typed_ptr: *const T = mem::transmute(ptr); + let data: &T = &*typed_ptr; + Ok(data) + } else { + Err(res) + } +} + +/// Stores a value that can be retrieved later with `get_data`. The value lives +/// as long as the current pam cycle. +/// +/// See `pam_set_data` in +/// http://www.linux-pam.org/Linux-PAM-html/mwg-expected-by-module-item.html +pub fn set_data<T>(pamh: &PamHandleT, key: &str, data: Box<T>) -> PamResult<()> { + let c_key = CString::new(key).unwrap().as_ptr(); + let res = unsafe { + let c_data: Box<PamDataT> = mem::transmute(data); + pam_set_data(pamh, c_key, c_data, cleanup::<T>) + }; + if PamResultCode::PAM_SUCCESS == res { + Ok(()) + } else { + Err(res) + } +} + +#[no_mangle] +pub extern "C" fn cleanup<T>(_: *const PamHandleT, c_data: Box<PamDataT>, _: PamResultCode) { + unsafe { + let data: Box<T> = mem::transmute(c_data); + mem::drop(data); + } +} + +/// Retrieves a value that has been set, possibly by the pam client. This is +/// particularly useful for getting a `PamConv` reference. +/// +/// See `pam_get_item` in +/// http://www.linux-pam.org/Linux-PAM-html/mwg-expected-by-module-item.html +pub fn get_item<'a, T: PamItem>(pamh: &'a PamHandleT) -> PamResult<&'a T> { + let mut ptr: *const PamItemT = ptr::null(); + let (res, item) = unsafe { + let r = pam_get_item(pamh, T::item_type(), &mut ptr); + let typed_ptr: *const T = mem::transmute(ptr); + let t: &T = &*typed_ptr; + (r, t) + }; + if PamResultCode::PAM_SUCCESS == res { + Ok(item) + } else { + Err(res) + } +} + +/// Sets a value in the pam context. The value can be retrieved using +/// `get_item`. +/// +/// Note that all items are strings, except `PAM_CONV` and `PAM_FAIL_DELAY`. +/// +/// See `pam_set_item` in +/// http://www.linux-pam.org/Linux-PAM-html/mwg-expected-by-module-item.html +pub fn set_item_str<'a, T: PamItem>(pamh: &'a mut PamHandleT, item: &str) -> PamResult<()> { + let c_item = CString::new(item).unwrap().as_ptr(); + + let res = unsafe { + pam_set_item(pamh, + T::item_type(), + + // unwrapping is okay here, as c_item will not be a NULL + // pointer + (c_item as *const PamItemT).as_ref().unwrap()) + }; + if PamResultCode::PAM_SUCCESS == res { + Ok(()) + } else { + Err(res) + } +} + +/// Retrieves the name of the user who is authenticating or logging in. +/// +/// This is really a specialization of `get_item`. +/// +/// See `pam_get_user` in +/// http://www.linux-pam.org/Linux-PAM-html/mwg-expected-by-module-item.html +pub fn get_user<'a>(pamh: &'a PamHandleT, prompt: Option<&str>) -> PamResult<String> { + let ptr: *mut c_char = ptr::null_mut(); + let c_prompt = match prompt { + Some(p) => CString::new(p).unwrap().as_ptr(), + None => ptr::null(), + }; + let res = unsafe { pam_get_user(pamh, &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) + } +}