Mercurial > crates > nonstick
diff src/module.rs @ 56:daa2cde64601
Big big refactor. Probably should have been multiple changes.
- Makes FFI safer by explicitly specifying c_int in calls.
- Uses ToPrimitive/FromPrimitive to make this easier.
- Pulls PamFlag variables into a bitflags! struct.
- Pulls PamMessageStyle variables into an enum.
- Renames ResultCode to ErrorCode.
- Switches from PAM_SUCCESS to using a Result<(), ErrorCode>.
- Uses thiserror to make ErrorCode into an Error.
- Gets rid of pam_try! because now we have Results.
- Expands some names (e.g. Conv to Conversation).
- Adds more doc comments.
- Returns passwords as a SecureString, to avoid unnecessarily
keeping it around in memory.
author | Paul Fisher <paul@pfish.zone> |
---|---|
date | Sun, 04 May 2025 02:56:55 -0400 |
parents | 9d1160b02d2c |
children | 3f4a77aa88be |
line wrap: on
line diff
--- a/src/module.rs Sun May 04 00:58:04 2025 -0400 +++ b/src/module.rs Sun May 04 02:56:55 2025 -0400 @@ -1,9 +1,10 @@ //! Functions for use in pam modules. -use crate::constants::{PamFlag, PamResultCode}; +use crate::constants::{Flags, PamResult, ErrorCode}; use crate::items::{Item, ItemType}; use libc::c_char; -use std::ffi::{CStr, CString}; +use std::ffi::{c_int, CStr, CString}; +use secure_string::SecureString; /// Opaque type, used as a pointer when making pam API calls. /// @@ -21,7 +22,7 @@ pamh: *const PamHandle, module_data_name: *const c_char, data: &mut *const libc::c_void, - ) -> PamResultCode; + ) -> c_int; fn pam_set_data( pamh: *const PamHandle, @@ -30,45 +31,39 @@ cleanup: extern "C" fn( pamh: *const PamHandle, data: *mut libc::c_void, - error_status: PamResultCode, + error_status: c_int, ), - ) -> PamResultCode; + ) -> c_int; fn pam_get_item( pamh: *const PamHandle, - item_type: ItemType, + item_type: c_int, item: &mut *const libc::c_void, - ) -> PamResultCode; + ) -> c_int; - fn pam_set_item( - pamh: *mut PamHandle, - item_type: ItemType, - item: *const libc::c_void, - ) -> PamResultCode; + fn pam_set_item(pamh: *mut PamHandle, item_type: c_int, item: *const libc::c_void) -> c_int; - fn pam_get_user( - pamh: *const PamHandle, - user: &*mut c_char, - prompt: *const c_char, - ) -> PamResultCode; + fn pam_get_user(pamh: *const PamHandle, user: &*mut c_char, prompt: *const c_char) -> c_int; fn pam_get_authtok( pamh: *const PamHandle, - item_type: ItemType, + item_type: c_int, data: &*mut c_char, prompt: *const c_char, - ) -> PamResultCode; + ) -> c_int; } -pub extern "C" fn cleanup<T>(_: *const PamHandle, c_data: *mut libc::c_void, _: PamResultCode) { +/// Function called at the end of a PAM session that is called to clean up +/// a value previously provided to PAM in a `pam_set_data` call. +/// +/// You should never call this yourself. +extern "C" fn cleanup<T>(_: *const PamHandle, c_data: *mut libc::c_void, _: c_int) { unsafe { let _data: Box<T> = Box::from_raw(c_data.cast::<T>()); } } -pub type PamResult<T> = Result<T, PamResultCode>; - impl PamHandle { /// Gets some value, identified by `key`, that has been set by the module /// previously. @@ -87,9 +82,9 @@ /// /// The data, if present, is owned by the current PAM conversation. pub unsafe fn get_data<T>(&self, key: &str) -> PamResult<Option<&T>> { - let c_key = CString::new(key).map_err(|_| PamResultCode::PAM_CONV_ERR)?; + let c_key = CString::new(key).map_err(|_| ErrorCode::ConversationError)?; let mut ptr: *const libc::c_void = std::ptr::null(); - to_result(pam_get_data(self, c_key.as_ptr(), &mut ptr))?; + ErrorCode::result_from(pam_get_data(self, c_key.as_ptr(), &mut ptr))?; match ptr.is_null() { true => Ok(None), false => { @@ -109,8 +104,8 @@ /// /// Returns an error if the underlying PAM function call fails. pub fn set_data<T>(&mut self, key: &str, data: Box<T>) -> PamResult<()> { - let c_key = CString::new(key).map_err(|_| PamResultCode::PAM_CONV_ERR)?; - let res = unsafe { + let c_key = CString::new(key).map_err(|_| ErrorCode::ConversationError)?; + let ret = unsafe { pam_set_data( self, c_key.as_ptr(), @@ -118,7 +113,7 @@ cleanup::<T>, ) }; - to_result(res) + ErrorCode::result_from(ret) } /// Retrieves a value that has been set, possibly by the pam client. @@ -136,8 +131,8 @@ pub fn get_item<T: crate::items::Item>(&self) -> PamResult<Option<T>> { let mut ptr: *const libc::c_void = std::ptr::null(); let out = unsafe { - let r = pam_get_item(self, T::type_id(), &mut ptr); - to_result(r)?; + let ret = pam_get_item(self, T::type_id().into(), &mut ptr); + ErrorCode::result_from(ret)?; let typed_ptr = ptr.cast::<T::Raw>(); match typed_ptr.is_null() { true => None, @@ -156,9 +151,9 @@ /// /// Returns an error if the underlying PAM function call fails. pub fn set_item<T: Item>(&mut self, item: T) -> PamResult<()> { - let res = - unsafe { pam_set_item(self, T::type_id(), item.into_raw().cast::<libc::c_void>()) }; - to_result(res) + let ret = + unsafe { pam_set_item(self, T::type_id().into(), item.into_raw().cast::<libc::c_void>()) }; + ErrorCode::result_from(ret) } /// Retrieves the name of the user who is authenticating or logging in. @@ -174,11 +169,9 @@ pub fn get_user(&self, prompt: Option<&str>) -> PamResult<String> { let prompt = option_cstr(prompt)?; let output: *mut c_char = std::ptr::null_mut(); - let res = unsafe { pam_get_user(self, &output, prompt_ptr(prompt.as_ref())) }; - match res { - PamResultCode::PAM_SUCCESS => copy_pam_string(output), - otherwise => Err(otherwise), - } + let ret = unsafe { pam_get_user(self, &output, prompt_ptr(prompt.as_ref())) }; + ErrorCode::result_from(ret)?; + copy_pam_string(output) } /// Retrieves the authentication token from the user. @@ -191,19 +184,19 @@ /// # Errors /// /// Returns an error if the underlying PAM function call fails. - pub fn get_authtok(&self, prompt: Option<&str>) -> PamResult<String> { + pub fn get_authtok(&self, prompt: Option<&str>) -> PamResult<SecureString> { let prompt = option_cstr(prompt)?; let output: *mut c_char = std::ptr::null_mut(); let res = unsafe { pam_get_authtok( self, - ItemType::AuthTok, + ItemType::AuthTok.into(), &output, prompt_ptr(prompt.as_ref()), ) }; - to_result(res)?; - copy_pam_string(output) + ErrorCode::result_from(res)?; + copy_pam_string(output).map(SecureString::from) } } @@ -212,11 +205,11 @@ prompt .map(CString::new) .transpose() - .map_err(|_| PamResultCode::PAM_CONV_ERR) + .map_err(|_| ErrorCode::ConversationError) } /// The pointer to the prompt CString, or null if absent. -fn prompt_ptr(prompt: Option<&CString>) -> *const c_char { +pub(crate) fn prompt_ptr(prompt: Option<&CString>) -> *const c_char { match prompt { Some(c_str) => c_str.as_ptr(), None => std::ptr::null(), @@ -225,24 +218,16 @@ /// Creates an owned copy of a string that is returned from a /// <code>pam_get_<var>whatever</var></code> function. -fn copy_pam_string(result_ptr: *const c_char) -> PamResult<String> { +pub(crate) fn copy_pam_string(result_ptr: *const c_char) -> PamResult<String> { // We really shouldn't get a null pointer back here, but if we do, return nothing. if result_ptr.is_null() { return Ok(String::new()); } let bytes = unsafe { CStr::from_ptr(result_ptr) }; - Ok(bytes + bytes .to_str() - .map_err(|_| PamResultCode::PAM_CONV_ERR)? - .into()) -} - -/// Convenience to transform a `PamResultCode` into a unit `PamResult`. -fn to_result(result: PamResultCode) -> PamResult<()> { - match result { - PamResultCode::PAM_SUCCESS => Ok(()), - otherwise => Err(otherwise), - } + .map(String::from) + .map_err(|_| ErrorCode::ConversationError) } /// Provides functions that are invoked by the entrypoints generated by the @@ -258,13 +243,13 @@ /// authentication module. This function checks for other things. Such things might be: the time of /// day or the date, the terminal line, remote hostname, etc. This function may also determine /// things like the expiration on passwords, and respond that the user change it before continuing. - fn acct_mgmt(pamh: &mut PamHandle, args: Vec<&CStr>, flags: PamFlag) -> PamResultCode { - PamResultCode::PAM_IGNORE + fn acct_mgmt(handle: &mut PamHandle, args: Vec<&CStr>, flags: Flags) -> PamResult<()> { + Err(ErrorCode::Ignore) } /// This function performs the task of authenticating the user. - fn sm_authenticate(pamh: &mut PamHandle, args: Vec<&CStr>, flags: PamFlag) -> PamResultCode { - PamResultCode::PAM_IGNORE + fn sm_authenticate(handle: &mut PamHandle, args: Vec<&CStr>, flags: Flags) -> PamResult<()> { + Err(ErrorCode::Ignore) } /// This function is used to (re-)set the authentication token of the user. @@ -273,18 +258,18 @@ /// `PAM_PRELIM_CHECK` and then, if the module does not return `PAM_TRY_AGAIN`, subsequently with /// `PAM_UPDATE_AUTHTOK`. It is only on the second call that the authorization token is /// (possibly) changed. - fn sm_chauthtok(pamh: &mut PamHandle, args: Vec<&CStr>, flags: PamFlag) -> PamResultCode { - PamResultCode::PAM_IGNORE + fn sm_chauthtok(handle: &mut PamHandle, args: Vec<&CStr>, flags: Flags) -> PamResult<()> { + Err(ErrorCode::Ignore) } /// This function is called to terminate a session. - fn sm_close_session(pamh: &mut PamHandle, args: Vec<&CStr>, flags: PamFlag) -> PamResultCode { - PamResultCode::PAM_IGNORE + fn sm_close_session(handle: &mut PamHandle, args: Vec<&CStr>, flags: Flags) -> PamResult<()> { + Err(ErrorCode::Ignore) } /// This function is called to commence a session. - fn sm_open_session(pamh: &mut PamHandle, args: Vec<&CStr>, flags: PamFlag) -> PamResultCode { - PamResultCode::PAM_IGNORE + fn sm_open_session(handle: &mut PamHandle, args: Vec<&CStr>, flags: Flags) -> PamResult<()> { + Err(ErrorCode::Ignore) } /// This function performs the task of altering the credentials of the user with respect to the @@ -292,7 +277,7 @@ /// information about a user than their authentication token. This function is used to make such /// information available to the application. It should only be called after the user has been /// authenticated but before a session has been established. - fn sm_setcred(pamh: &mut PamHandle, args: Vec<&CStr>, flags: PamFlag) -> PamResultCode { - PamResultCode::PAM_IGNORE + fn sm_setcred(handle: &mut PamHandle, args: Vec<&CStr>, flags: Flags) -> PamResult<()> { + Err(ErrorCode::Ignore) } }