diff options
Diffstat (limited to 'src-repo/src/raw_conv.rs')
| -rw-r--r-- | src-repo/src/raw_conv.rs | 116 |
1 files changed, 116 insertions, 0 deletions
diff --git a/src-repo/src/raw_conv.rs b/src-repo/src/raw_conv.rs new file mode 100644 index 0000000..fe93b64 --- /dev/null +++ b/src-repo/src/raw_conv.rs @@ -0,0 +1,116 @@ +use std::ffi::{CStr, CString}; +use std::ptr; + +const PAM_CONV: libc::c_int = 5; +const PAM_PROMPT_ECHO_OFF: libc::c_int = 1; +const PAM_PROMPT_ECHO_ON: libc::c_int = 2; +const PAM_ERROR_MSG: libc::c_int = 3; +const PAM_TEXT_INFO: libc::c_int = 4; + +#[repr(C)] +struct PamMessage { + msg_style: libc::c_int, + msg: *const libc::c_char, +} + +#[repr(C)] +struct PamResponse { + resp: *mut libc::c_char, + resp_retcode: libc::c_int, +} + +type PamConvFn = unsafe extern "C" fn( + num_msg: libc::c_int, + msg: *mut *const PamMessage, + resp: *mut *mut PamResponse, + appdata_ptr: *mut libc::c_void, +) -> libc::c_int; + +#[repr(C)] +struct PamConv { + conv: PamConvFn, + appdata_ptr: *mut libc::c_void, +} + +extern "C" { + fn pam_get_item( + pamh: *const libc::c_void, + item_type: libc::c_int, + item: *mut *const libc::c_void, + ) -> libc::c_int; +} + +/// Thread-safe wrapper around a raw PAM conversation function pointer. +pub struct RawConv { + conv_fn: PamConvFn, + appdata_ptr: *mut libc::c_void, +} + +unsafe impl Send for RawConv {} + +impl RawConv { + /// Extract the raw PAM conversation from a PAM handle. + pub fn from_pam_handle(pamh: *mut libc::c_void) -> Option<Self> { + unsafe { + let mut conv_ptr: *const libc::c_void = ptr::null(); + let ret = pam_get_item(pamh, PAM_CONV, &mut conv_ptr); + if ret != 0 || conv_ptr.is_null() { + return None; + } + let pam_conv = &*(conv_ptr as *const PamConv); + Some(RawConv { + conv_fn: pam_conv.conv, + appdata_ptr: pam_conv.appdata_ptr, + }) + } + } + + /// Send a message through the PAM conversation. + /// Returns the response string for prompt types, or None for info/error. + pub fn send(&self, msg_style: libc::c_int, msg: &str) -> Result<Option<CString>, ()> { + let c_msg = CString::new(msg).map_err(|_| ())?; + let pam_msg = PamMessage { + msg_style, + msg: c_msg.as_ptr(), + }; + let mut msg_ptr: *const PamMessage = &pam_msg; + let mut resp_ptr: *mut PamResponse = ptr::null_mut(); + + let ret = unsafe { (self.conv_fn)(1, &mut msg_ptr, &mut resp_ptr, self.appdata_ptr) }; + + if ret != 0 { + return Err(()); + } + + if resp_ptr.is_null() { + return Ok(None); + } + + let response = unsafe { + let resp = &*resp_ptr; + let result = if resp.resp.is_null() { + None + } else { + let s = CStr::from_ptr(resp.resp).to_owned(); + libc::free(resp.resp as *mut libc::c_void); + Some(s) + }; + libc::free(resp_ptr as *mut libc::c_void); + result + }; + + Ok(response) + } + + pub fn send_prompt(&self, msg: &str) -> Result<Option<CString>, ()> { + self.send(PAM_PROMPT_ECHO_OFF, msg) + } + + pub fn send_info(&self, msg: &str) -> Result<Option<CString>, ()> { + self.send(PAM_TEXT_INFO, msg) + } + + pub fn send_error(&self, msg: &str) -> Result<Option<CString>, ()> { + self.send(PAM_ERROR_MSG, msg) + } +} |
