pam_client2/conversation.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
//! Conversation trait definition module
/***********************************************************************
* (c) 2021 Christoph Grenz <christophg+gitorious @ grenz-bonn.de> *
* *
* This Source Code Form is subject to the terms of the Mozilla Public *
* License, v. 2.0. If a copy of the MPL was not distributed with this *
* file, You can obtain one at http://mozilla.org/MPL/2.0/. *
***********************************************************************/
#![forbid(unsafe_code)]
use crate::error::ErrorCode;
use std::ffi::{CStr, CString};
use std::result::Result;
/// Trait for PAM conversation functions
///
/// Implement this for custom behaviour when a PAM module asks for usernames,
/// passwords, etc. or wants to show a message to the user
#[rustversion::attr(since(1.48), doc(alias = "pam_conv"))]
pub trait ConversationHandler {
/// Called by [`Context`][`crate::Context`] directly after taking ownership
/// of the handler.
///
/// May be called multiple times if
/// [`Context::replace_conversation()`][`crate::Context::replace_conversation`]
/// is used. In this case it is called each time a context takes ownership
/// and passed the current target username of that context (if any) as the
/// argument.
///
/// The default implementation does nothing.
fn init(&mut self, _default_user: Option<&str>) {}
/// Obtains a string whilst echoing text (e.g. username)
///
/// # Errors
/// You should return one of the following error codes on failure.
/// - [`ErrorCode::CONV_ERR`]: Conversation failure.
/// - [`ErrorCode::BUF_ERR`]: Memory allocation error.
/// - [`ErrorCode::CONV_AGAIN`]: no result yet, the PAM library should
/// pass [`ErrorCode::INCOMPLETE`] to the application and let it
/// try again later.
fn prompt_echo_on(&mut self, prompt: &CStr) -> Result<CString, ErrorCode>;
/// Obtains a string without echoing any text (e.g. password)
///
/// # Errors
/// You should return one of the following error codes on failure.
/// - [`ErrorCode::CONV_ERR`]: Conversation failure.
/// - [`ErrorCode::BUF_ERR`]: Memory allocation error.
/// - [`ErrorCode::CONV_AGAIN`]: no result yet, the PAM library should
/// pass [`ErrorCode::INCOMPLETE`] to the application and let it
/// try again later.
fn prompt_echo_off(&mut self, prompt: &CStr) -> Result<CString, ErrorCode>;
/// Displays some text.
fn text_info(&mut self, msg: &CStr);
/// Displays an error message.
fn error_msg(&mut self, msg: &CStr);
/// Obtains a yes/no answer (Linux specific).
///
/// The default implementation calls `prompt_echo_on` and maps any answer
/// starting with 'y' or 'j' to "yes" and everything else to "no".
///
/// # Errors
/// You should return one of the following error codes on failure.
/// - [`ErrorCode::CONV_ERR`]: Conversation failure.
/// - [`ErrorCode::BUF_ERR`]: Memory allocation error.
/// - [`ErrorCode::CONV_AGAIN`]: no result yet, the PAM library should
/// pass [`ErrorCode::INCOMPLETE`] to the application and let it
/// try again later.
fn radio_prompt(&mut self, prompt: &CStr) -> Result<bool, ErrorCode> {
let prompt = [prompt.to_bytes(), b" [y/N]\0"].concat();
self.prompt_echo_on(CStr::from_bytes_with_nul(&prompt).unwrap())
.map(|s| matches!(s.as_bytes_with_nul()[0], b'Y' | b'y' | b'j' | b'J'))
}
/// Exchanges binary data (Linux specific, experimental).
///
/// The default implementation returns a conversation error.
///
/// # Errors
/// You should return one of the following error codes on failure.
/// - [`ErrorCode::CONV_ERR`]: Conversation failure.
/// - [`ErrorCode::BUF_ERR`]: Memory allocation error.
/// - [`ErrorCode::CONV_AGAIN`]: no result yet, the PAM library should
/// pass [`ErrorCode::INCOMPLETE`] to the application and let it
/// try again later.
fn binary_prompt(&mut self, _type: u8, _data: &[u8]) -> Result<(u8, Vec<u8>), ErrorCode> {
Err(ErrorCode::CONV_ERR)
}
}
macro_rules! impl_for_wrapper {
($type:ty) => {
impl_for_wrapper!($type, <>);
};
($type:ty, $($params:tt)*) => {
impl $($params)* ConversationHandler for $type {
#[inline]
fn init(&mut self, default_user: Option<&str>) {
(**self).init(default_user)
}
#[inline]
fn prompt_echo_on(&mut self, prompt: &CStr) -> Result<CString, ErrorCode> {
(**self).prompt_echo_on(prompt)
}
#[inline]
fn prompt_echo_off(&mut self, prompt: &CStr) -> Result<CString, ErrorCode> {
(**self).prompt_echo_off(prompt)
}
#[inline]
fn text_info(&mut self, msg: &CStr) {
(**self).text_info(msg)
}
#[inline]
fn error_msg(&mut self, msg: &CStr) {
(**self).error_msg(msg)
}
#[inline]
fn radio_prompt(&mut self, prompt: &CStr) -> Result<bool, ErrorCode> {
(**self).radio_prompt(prompt)
}
#[inline]
fn binary_prompt(
&mut self,
type_: u8,
data: &[u8],
) -> Result<(u8, Vec<u8>), ErrorCode> {
(**self).binary_prompt(type_, data)
}
}
};
}
impl_for_wrapper!(&'a mut T, <'a, T: ConversationHandler + ?Sized>);
impl_for_wrapper!(Box<T>, <T: ConversationHandler + ?Sized>);