pam_client2/conversation.rs
1//! Conversation trait definition module
2
3/***********************************************************************
4 * (c) 2021 Christoph Grenz <christophg+gitorious @ grenz-bonn.de> *
5 * *
6 * This Source Code Form is subject to the terms of the Mozilla Public *
7 * License, v. 2.0. If a copy of the MPL was not distributed with this *
8 * file, You can obtain one at http://mozilla.org/MPL/2.0/. *
9 ***********************************************************************/
10
11#![forbid(unsafe_code)]
12
13use crate::error::ErrorCode;
14use std::ffi::{CStr, CString};
15use std::result::Result;
16
17/// Trait for PAM conversation functions
18///
19/// Implement this for custom behaviour when a PAM module asks for usernames,
20/// passwords, etc. or wants to show a message to the user
21#[rustversion::attr(since(1.48), doc(alias = "pam_conv"))]
22pub trait ConversationHandler {
23 /// Called by [`Context`][`crate::Context`] directly after taking ownership
24 /// of the handler.
25 ///
26 /// May be called multiple times if
27 /// [`Context::replace_conversation()`][`crate::Context::replace_conversation`]
28 /// is used. In this case it is called each time a context takes ownership
29 /// and passed the current target username of that context (if any) as the
30 /// argument.
31 ///
32 /// The default implementation does nothing.
33 fn init(&mut self, _default_user: Option<&str>) {}
34
35 /// Obtains a string whilst echoing text (e.g. username)
36 ///
37 /// # Errors
38 /// You should return one of the following error codes on failure.
39 /// - [`ErrorCode::CONV_ERR`]: Conversation failure.
40 /// - [`ErrorCode::BUF_ERR`]: Memory allocation error.
41 /// - [`ErrorCode::CONV_AGAIN`]: no result yet, the PAM library should
42 /// pass [`ErrorCode::INCOMPLETE`] to the application and let it
43 /// try again later.
44 fn prompt_echo_on(&mut self, prompt: &CStr) -> Result<CString, ErrorCode>;
45
46 /// Obtains a string without echoing any text (e.g. password)
47 ///
48 /// # Errors
49 /// You should return one of the following error codes on failure.
50 /// - [`ErrorCode::CONV_ERR`]: Conversation failure.
51 /// - [`ErrorCode::BUF_ERR`]: Memory allocation error.
52 /// - [`ErrorCode::CONV_AGAIN`]: no result yet, the PAM library should
53 /// pass [`ErrorCode::INCOMPLETE`] to the application and let it
54 /// try again later.
55 fn prompt_echo_off(&mut self, prompt: &CStr) -> Result<CString, ErrorCode>;
56
57 /// Displays some text.
58 fn text_info(&mut self, msg: &CStr);
59
60 /// Displays an error message.
61 fn error_msg(&mut self, msg: &CStr);
62
63 /// Obtains a yes/no answer (Linux specific).
64 ///
65 /// The default implementation calls `prompt_echo_on` and maps any answer
66 /// starting with 'y' or 'j' to "yes" and everything else to "no".
67 ///
68 /// # Errors
69 /// You should return one of the following error codes on failure.
70 /// - [`ErrorCode::CONV_ERR`]: Conversation failure.
71 /// - [`ErrorCode::BUF_ERR`]: Memory allocation error.
72 /// - [`ErrorCode::CONV_AGAIN`]: no result yet, the PAM library should
73 /// pass [`ErrorCode::INCOMPLETE`] to the application and let it
74 /// try again later.
75 fn radio_prompt(&mut self, prompt: &CStr) -> Result<bool, ErrorCode> {
76 let prompt = [prompt.to_bytes(), b" [y/N]\0"].concat();
77
78 self.prompt_echo_on(CStr::from_bytes_with_nul(&prompt).unwrap())
79 .map(|s| matches!(s.as_bytes_with_nul()[0], b'Y' | b'y' | b'j' | b'J'))
80 }
81
82 /// Exchanges binary data (Linux specific, experimental).
83 ///
84 /// The default implementation returns a conversation error.
85 ///
86 /// # Errors
87 /// You should return one of the following error codes on failure.
88 /// - [`ErrorCode::CONV_ERR`]: Conversation failure.
89 /// - [`ErrorCode::BUF_ERR`]: Memory allocation error.
90 /// - [`ErrorCode::CONV_AGAIN`]: no result yet, the PAM library should
91 /// pass [`ErrorCode::INCOMPLETE`] to the application and let it
92 /// try again later.
93 fn binary_prompt(&mut self, _type: u8, _data: &[u8]) -> Result<(u8, Vec<u8>), ErrorCode> {
94 Err(ErrorCode::CONV_ERR)
95 }
96}
97
98macro_rules! impl_for_wrapper {
99 ($type:ty) => {
100 impl_for_wrapper!($type, <>);
101 };
102 ($type:ty, $($params:tt)*) => {
103 impl $($params)* ConversationHandler for $type {
104 #[inline]
105 fn init(&mut self, default_user: Option<&str>) {
106 (**self).init(default_user)
107 }
108
109 #[inline]
110 fn prompt_echo_on(&mut self, prompt: &CStr) -> Result<CString, ErrorCode> {
111 (**self).prompt_echo_on(prompt)
112 }
113
114 #[inline]
115 fn prompt_echo_off(&mut self, prompt: &CStr) -> Result<CString, ErrorCode> {
116 (**self).prompt_echo_off(prompt)
117 }
118
119 #[inline]
120 fn text_info(&mut self, msg: &CStr) {
121 (**self).text_info(msg)
122 }
123
124 #[inline]
125 fn error_msg(&mut self, msg: &CStr) {
126 (**self).error_msg(msg)
127 }
128
129 #[inline]
130 fn radio_prompt(&mut self, prompt: &CStr) -> Result<bool, ErrorCode> {
131 (**self).radio_prompt(prompt)
132 }
133
134 #[inline]
135 fn binary_prompt(
136 &mut self,
137 type_: u8,
138 data: &[u8],
139 ) -> Result<(u8, Vec<u8>), ErrorCode> {
140 (**self).binary_prompt(type_, data)
141 }
142 }
143 };
144}
145impl_for_wrapper!(&'a mut T, <'a, T: ConversationHandler + ?Sized>);
146impl_for_wrapper!(Box<T>, <T: ConversationHandler + ?Sized>);