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 { 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, ()> { 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, ()> { self.send(PAM_PROMPT_ECHO_OFF, msg) } pub fn send_info(&self, msg: &str) -> Result, ()> { self.send(PAM_TEXT_INFO, msg) } pub fn send_error(&self, msg: &str) -> Result, ()> { self.send(PAM_ERROR_MSG, msg) } }