Mercurial > crates > nonstick
diff src/module.rs @ 1:b195a14058bb
initial commit
author | Jesse Hallett <jesse@galois.com> |
---|---|
date | Thu, 05 Mar 2015 16:25:10 -0800 |
parents | |
children | 2ec97116d72c |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/module.rs Thu Mar 05 16:25:10 2015 -0800 @@ -0,0 +1,154 @@ +//! Functions for use in pam modules. + +use libc::{c_char}; +use std::{mem, ptr}; +use std::ffi::{CStr, CString}; + +use constants; +use constants::*; + +/// 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 { + fn pam_get_data(pamh: *const PamHandleT, + module_data_name: *const c_char, + data: & *mut PamDataT, + ) -> PamResultCode; + + fn pam_set_data(pamh: *const PamHandleT, + module_data_name: *const c_char, + data: Box<PamDataT>, + cleanup: extern fn (pamh: *const PamHandleT, + data: Box<PamDataT>, + error_status: PamResultCode + ), + ) -> PamResultCode; + + fn pam_get_item(pamh: *const PamHandleT, + item_type: PamItemType, + item: & *mut 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`. + /// + /// The argument will always be `None`. Its purpose is to provide a type + /// label - the value is not important. + fn item_type(_: Option<Self>) -> 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: *mut PamDataT = ptr::null_mut(); + let res = pam_get_data(pamh, c_key, &mut ptr); + if constants::PAM_SUCCESS == res && !ptr.is_null() { + let raw_data: &PamDataT = ptr.as_ref().unwrap(); + let data: &T = mem::transmute(raw_data); + 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 constants::PAM_SUCCESS == res { Ok(()) } else { Err(res) } +} + +#[no_mangle] +pub extern 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 ptr: *mut PamItemT = ptr::null_mut(); + let (res, item) = unsafe { + let r = pam_get_item(pamh, PamItem::item_type(None::<T>), &ptr); + let raw_item: &PamItemT = ptr.as_ref().unwrap(); + let t: &T = mem::transmute(raw_item); + (r, t) + }; + if constants::PAM_SUCCESS == res { Ok(item) } 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 constants::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(|_| PAM_CONV_ERR) + } + else { + Err(res) + } +}