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