1use libc::{c_int, c_void, calloc, free, size_t, strdup};
2
3use std::ffi::{CStr, CString};
4use std::mem;
5
6use crate::{ffi::pam_conv, PamMessage, PamMessageStyle, PamResponse, PamReturnCode};
7
8pub trait Conversation {
19 fn prompt_echo(&mut self, msg: &CStr) -> Result<CString, ()>;
24 fn prompt_blind(&mut self, msg: &CStr) -> Result<CString, ()>;
29 fn info(&mut self, msg: &CStr);
31 fn error(&mut self, msg: &CStr);
33}
34
35pub struct PasswordConv {
40 login: String,
41 passwd: String,
42}
43
44impl PasswordConv {
45 pub(crate) fn new() -> PasswordConv {
47 PasswordConv {
48 login: String::new(),
49 passwd: String::new(),
50 }
51 }
52
53 pub fn set_credentials<U: Into<String>, V: Into<String>>(&mut self, login: U, password: V) {
55 self.login = login.into();
56 self.passwd = password.into();
57 }
58}
59
60impl Conversation for PasswordConv {
61 fn prompt_echo(&mut self, _msg: &CStr) -> Result<CString, ()> {
62 CString::new(self.login.clone()).map_err(|_| ())
63 }
64 fn prompt_blind(&mut self, _msg: &CStr) -> Result<CString, ()> {
65 CString::new(self.passwd.clone()).map_err(|_| ())
66 }
67 fn info(&mut self, _msg: &CStr) {}
68 fn error(&mut self, msg: &CStr) {
69 eprintln!("[PAM ERROR] {}", msg.to_string_lossy());
70 }
71}
72
73pub(crate) fn into_pam_conv<C: Conversation>(conv: &mut C) -> pam_conv {
74 pam_conv {
75 conv: Some(converse::<C>),
76 appdata_ptr: conv as *mut C as *mut c_void,
77 }
78}
79
80pub(crate) unsafe extern "C" fn converse<C: Conversation>(
82 num_msg: c_int,
83 msg: *mut *const PamMessage,
84 out_resp: *mut *mut PamResponse,
85 appdata_ptr: *mut c_void,
86) -> c_int {
87 let resp =
89 calloc(num_msg as usize, mem::size_of::<PamResponse>() as size_t) as *mut PamResponse;
90 if resp.is_null() {
91 return PamReturnCode::Buf_Err as c_int;
92 }
93
94 let handler = &mut *(appdata_ptr as *mut C);
95
96 let mut result: PamReturnCode = PamReturnCode::Success;
97 for i in 0..num_msg as isize {
98 let m: &mut PamMessage = &mut *(*(msg.offset(i)) as *mut PamMessage);
101 let r: &mut PamResponse = &mut *(resp.offset(i));
102
103 let msg = CStr::from_ptr(m.msg);
104 match PamMessageStyle::from(m.msg_style) {
106 PamMessageStyle::Prompt_Echo_On => {
107 if let Ok(handler_response) = handler.prompt_echo(msg) {
108 r.resp = strdup(handler_response.as_ptr());
109 } else {
110 result = PamReturnCode::Conv_Err;
111 }
112 }
113 PamMessageStyle::Prompt_Echo_Off => {
114 if let Ok(handler_response) = handler.prompt_blind(msg) {
115 r.resp = strdup(handler_response.as_ptr());
116 } else {
117 result = PamReturnCode::Conv_Err;
118 }
119 }
120 PamMessageStyle::Text_Info => {
121 handler.info(msg);
122 }
123 PamMessageStyle::Error_Msg => {
124 handler.error(msg);
125 result = PamReturnCode::Conv_Err;
126 }
127 }
128 if result != PamReturnCode::Success {
129 break;
130 }
131 }
132
133 if result != PamReturnCode::Success {
135 free(resp as *mut c_void);
136 } else {
137 *out_resp = resp;
138 }
139
140 result as c_int
141}