forked from fufesou/pam
-
Notifications
You must be signed in to change notification settings - Fork 0
/
conv.rs
150 lines (136 loc) · 5.12 KB
/
conv.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
use libc::{c_int, c_void, calloc, free, size_t, strdup};
use pam_sys::pam_conv;
use std::ffi::{CStr, CString};
use std::mem;
use crate::{PamMessage, PamMessageStyle, PamResponse, PamReturnCode};
/// A trait representing the PAM authentification conversation
///
/// PAM authentification is done as a conversation mechanism, in which PAM
/// asks several questions and the client (your code) answers them. This trait
/// is a representation of such a conversation, which one method for each message
/// PAM can send you.
///
/// This is the trait to implement if you want to customize the conversation with
/// PAM. If you just want a simple login/password authentication, you can use the
/// `PasswordConv` implementation provided by this crate.
pub trait Conversation {
/// PAM requests a value that should be echoed to the user as they type it
///
/// This would typically be the username. The exact question is provided as the
/// `msg` argument if you wish to display it to your user.
fn prompt_echo(&mut self, msg: &CStr) -> Result<CString, ()>;
/// PAM requests a value that should be typed blindly by the user
///
/// This would typically be the password. The exact question is provided as the
/// `msg` argument if you wish to display it to your user.
fn prompt_blind(&mut self, msg: &CStr) -> Result<CString, ()>;
/// This is an informational message from PAM
fn info(&mut self, msg: &CStr);
/// This is an error message from PAM
fn error(&mut self, msg: &CStr);
/// Get the username that is being authenticated
///
/// This method is not a PAM callback, but is rather used by the `Authenticator` to
/// setup the environment when opening a session.
fn username(&self) -> &str;
}
/// A minimalistic conversation handler, that uses given login and password
///
/// This conversation handler is not really interactive, but simply returns to
/// PAM the value that have been set using the `set_credentials` method.
pub struct PasswordConv {
login: String,
passwd: String,
}
impl PasswordConv {
/// Create a new `PasswordConv` handler
pub(crate) fn new() -> PasswordConv {
PasswordConv {
login: String::new(),
passwd: String::new(),
}
}
/// Set the credentials that this handler will provide to PAM
pub fn set_credentials<U: Into<String>, V: Into<String>>(&mut self, login: U, password: V) {
self.login = login.into();
self.passwd = password.into();
}
}
impl Conversation for PasswordConv {
fn prompt_echo(&mut self, _msg: &CStr) -> Result<CString, ()> {
CString::new(self.login.clone()).map_err(|_| ())
}
fn prompt_blind(&mut self, _msg: &CStr) -> Result<CString, ()> {
CString::new(self.passwd.clone()).map_err(|_| ())
}
fn info(&mut self, _msg: &CStr) {}
fn error(&mut self, msg: &CStr) {
eprintln!("[PAM ERROR] {}", msg.to_string_lossy());
}
fn username(&self) -> &str {
&self.login
}
}
pub(crate) fn into_pam_conv<C: Conversation>(conv: &mut C) -> pam_conv {
pam_conv {
conv: Some(converse::<C>),
appdata_ptr: conv as *mut C as *mut c_void,
}
}
// FIXME: verify this
pub(crate) unsafe extern "C" fn converse<C: Conversation>(
num_msg: c_int,
msg: *mut *const PamMessage,
out_resp: *mut *mut PamResponse,
appdata_ptr: *mut c_void,
) -> c_int {
// allocate space for responses
let resp =
calloc(num_msg as usize, mem::size_of::<PamResponse>() as size_t) as *mut PamResponse;
if resp.is_null() {
return PamReturnCode::Buf_Err as c_int;
}
let handler = &mut *(appdata_ptr as *mut C);
let mut result: PamReturnCode = PamReturnCode::Success;
for i in 0..num_msg as isize {
// get indexed values
// FIXME: check this
let m: &mut PamMessage = &mut *(*(msg.offset(i)) as *mut PamMessage);
let r: &mut PamResponse = &mut *(resp.offset(i));
let msg = CStr::from_ptr(m.msg);
// match on msg_style
match PamMessageStyle::from(m.msg_style) {
PamMessageStyle::Prompt_Echo_On => {
if let Ok(handler_response) = handler.prompt_echo(msg) {
r.resp = strdup(handler_response.as_ptr());
} else {
result = PamReturnCode::Conv_Err;
}
}
PamMessageStyle::Prompt_Echo_Off => {
if let Ok(handler_response) = handler.prompt_blind(msg) {
r.resp = strdup(handler_response.as_ptr());
} else {
result = PamReturnCode::Conv_Err;
}
}
PamMessageStyle::Text_Info => {
handler.info(msg);
}
PamMessageStyle::Error_Msg => {
handler.error(msg);
result = PamReturnCode::Conv_Err;
}
}
if result != PamReturnCode::Success {
break;
}
}
// free allocated memory if an error occured
if result != PamReturnCode::Success {
free(resp as *mut c_void);
} else {
*out_resp = resp;
}
result as c_int
}