Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] Add an option to not use unsafe send and require answering pending prompt #8

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion pam-any
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
auth sufficient libpam_any.so { "mode": "One", "modules": { "login": "Password", "pam-random": "Random Chance" } }
auth sufficient libpam_any.so { "mode": "One", "modules": { "login": "Password", "pam-random": "Random Chance" }, "gross_hack": false }
account sufficient libpam_any.so
91 changes: 91 additions & 0 deletions src/gross_hack.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
use std::sync::{Arc, Mutex};
use std::sync::mpsc::channel;
use std::thread;

use crossbeam_channel::select;
use pam::{Client, PamResult};
use pam_bindings::constants::PamResultCode::{PAM_AUTH_ERR, PAM_SUCCESS};
use pam_bindings::constants::PamResultCode;
use pam_bindings::conv::Conv;

use crate::input::Input;
use crate::message::ConvSendMessage;
use crate::mode::Mode;
use crate::pam_any_conversation::{PamAnyConversation};
use crate::prompt::ConvPrompt;
use crate::un_hide_input::un_hide_input;
use crate::unsafe_send::UnsafeSend;

pub fn gross_hack(input: Input, conv: Conv<'static>, user: String) -> PamResultCode {
let conv = Arc::new(Mutex::new(UnsafeSend { conv }));

let (tx, rx) = channel::<PamResult<()>>();
let _handles = input
.modules
.iter()
.map(|(service, service_display_name)| {
let service = service.to_owned();
let tx = tx.clone();
let conv = conv.clone();
let user = user.clone();
let service_display_name = service_display_name.to_owned();
let (info_rx, prompt_tx, prompt_rx, conversation) =
PamAnyConversation::new(service_display_name, user);
thread::spawn(move || {
thread::spawn(move || loop {
let conv = conv.clone();
select! {
recv(info_rx) -> info => {
if let Ok(info) = info {
thread::spawn(move || {
conv.lock().unwrap().send_message(&info).unwrap();
});
}
},
recv(prompt_rx) -> prompt => {
if let Ok(prompt) = prompt {
let response = conv.lock().unwrap().prompt(&prompt);
prompt_tx.send(response).unwrap();
}
}
}
});
let mut client = Client::with_conversation(&service, conversation).unwrap();
let result = client.authenticate();
let _ = tx.send(result);
})
})
.collect::<Vec<_>>();
match input.mode {
Mode::One => {
let mut failed_modules = 0;
for result in rx {
if result.is_ok() {
un_hide_input().unwrap();
return PAM_SUCCESS;
} else {
failed_modules += 1;
if failed_modules == input.modules.len() {
return PAM_AUTH_ERR;
}
}
}
PAM_AUTH_ERR
}
Mode::All => {
let mut successful_modules = 0;
for result in rx {
if result.is_ok() {
successful_modules += 1;
if successful_modules == input.modules.len() {
un_hide_input().unwrap();
return PAM_SUCCESS;
}
} else {
return PAM_AUTH_ERR;
}
}
PAM_AUTH_ERR
}
}
}
10 changes: 10 additions & 0 deletions src/input.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
use crate::mode::Mode;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;

#[derive(Serialize, Deserialize, Debug)]
pub struct Input {
pub mode: Mode,
pub modules: HashMap<String, String>,
pub gross_hack: bool
}
87 changes: 19 additions & 68 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,39 +1,35 @@
use std::collections::HashMap;
use std::ffi::CStr;
use std::sync::{Arc, Mutex};
use std::sync::mpsc::channel;
use std::thread;

use pam::{Client, PamResult};
use pam_bindings::constants::PamResultCode::PAM_AUTH_ERR;
use pam_bindings::constants::{PamFlag, PamResultCode};
use pam_bindings::constants::PamResultCode::{PAM_AUTH_ERR, PAM_SUCCESS};
use pam_bindings::conv::Conv;
use pam_bindings::module::{PamHandle, PamHooks};
use pam_bindings::pam_try;
use crate::mode::Mode;
use serde::{Serialize, Deserialize};

use crate::pam_any_conversation::PamAnyConversation;
use crate::un_hide_input::un_hide_input;
use crate::unsafe_send::UnsafeSend;
use crate::gross_hack::gross_hack;
use crate::input::Input;
use crate::safe::safe;

mod pam_any_conversation;
mod unsafe_send;
mod gross_hack;
mod input;
mod mode;
mod pam_any_conversation;
mod un_hide_input;
mod unsafe_send;
mod safe;
mod message;
mod prompt;

struct PamAny;
pam_bindings::pam_hooks!(PamAny);

#[derive(Serialize, Deserialize, Debug)]
struct Input {
mode: Mode,
modules: HashMap<String, String>,
}

impl PamHooks for PamAny {
fn sm_authenticate(pamh: &mut PamHandle, args: Vec<&CStr>, _flags: PamFlag) -> PamResultCode {
let arg_string = args.iter().map(|s| s.to_str().unwrap()).collect::<Vec<_>>().join(" ");
let arg_string = args
.iter()
.map(|s| s.to_str().unwrap())
.collect::<Vec<_>>()
.join(" ");
// println!("Input: {}", arg_string);
let input = pam_try!(serde_json::from_str::<Input>(&arg_string).map_err(|_e| PAM_AUTH_ERR));
// println!("Input: {:#?}", input);
Expand All @@ -46,56 +42,11 @@ impl PamHooks for PamAny {
return err;
}
};
let conv = Arc::new(Mutex::new(UnsafeSend { conv }));
let user = pam_try!(pamh.get_user(None));

let (tx, rx) = channel::<PamResult<()>>();
let _handles = input.modules.iter().map(|(service, service_display_name)| {
let service = service.to_owned();
let tx = tx.clone();
let conv = conv.clone();
let user = user.clone();
let service_display_name = service_display_name.to_owned();
thread::spawn(move || {
let mut client = Client::with_conversation(
&service,
PamAnyConversation { service_display_name, user, conv },
).unwrap();
let result = client.authenticate();
let _ = tx.send(result);
})
}).collect::<Vec<_>>();
match input.mode {
Mode::One => {
let mut failed_modules = 0;
for result in rx {
if result.is_ok() {
un_hide_input().unwrap();
return PAM_SUCCESS;
} else {
failed_modules += 1;
if failed_modules == input.modules.len() {
return PAM_AUTH_ERR;
}
}
}
PAM_AUTH_ERR
}
Mode::All => {
let mut successful_modules = 0;
for result in rx {
if result.is_ok() {
successful_modules += 1;
if successful_modules == input.modules.len() {
un_hide_input().unwrap();
return PAM_SUCCESS;
}
} else {
return PAM_AUTH_ERR;
}
}
PAM_AUTH_ERR
}
match input.gross_hack {
true => gross_hack(input, conv, user),
false => safe(input, conv, user)
}
}
}
28 changes: 28 additions & 0 deletions src/message.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
use pam::ffi::PAM_TEXT_INFO;
use pam_bindings::constants::PAM_ERROR_MSG;
use pam_bindings::conv::Conv;
use pam_bindings::module::PamResult;

pub enum MessageType {
Info,
Error,
}

pub struct Message {
pub message_type: MessageType,
pub message: String,
}

pub trait ConvSendMessage {
fn send_message(&self, message: &Message) -> PamResult<()>;
}

impl<'a> ConvSendMessage for Conv<'a> {
fn send_message(&self, message: &Message) -> PamResult<()> {
self.send(match message.message_type {
MessageType::Info => PAM_TEXT_INFO,
MessageType::Error => PAM_ERROR_MSG
}, &message.message)?;
Ok(())
}
}
2 changes: 1 addition & 1 deletion src/mode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,5 @@ use serde::{Deserialize, Serialize};
#[derive(Debug, Copy, Clone, Serialize, Deserialize)]
pub enum Mode {
One,
All
All,
}
92 changes: 64 additions & 28 deletions src/pam_any_conversation.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,56 @@
use std::ffi::{CStr, CString};
use std::sync::{Arc, Mutex};
use std::thread;

use crossbeam_channel::{bounded, Receiver, Sender, unbounded};
use pam::Conversation;
use pam::ffi::PAM_ERROR_MSG;
use pam_bindings::constants::{PAM_PROMPT_ECHO_OFF, PAM_TEXT_INFO};
use crate::unsafe_send::UnsafeSend;

use crate::message::{Message, MessageType};
use crate::prompt::{PromptInput, PromptOutput, PromptType};

pub struct PamAnyConversation {
pub service_display_name: String,
pub user: String,
pub conv: Arc<Mutex<UnsafeSend>>,
service_display_name: String,
user: String,
info_tx: Sender<Message>,
prompt_input: Sender<PromptInput>,
prompt_output: Receiver<PromptOutput>,
}

impl PamAnyConversation {
pub fn new(
service_display_name: String,
user: String,
) -> (
Receiver<Message>,
Sender<PromptOutput>,
Receiver<PromptInput>,
Self,
) {
let (info_tx, info_rx) = unbounded();
let (prompt_input_tx, prompt_input_rx) = bounded(1);
let (prompt_output_tx, prompt_output_rx) = bounded(1);
(
info_rx,
prompt_output_tx,
prompt_input_rx,
Self {
service_display_name,
user,
info_tx,
prompt_input: prompt_input_tx,
prompt_output: prompt_output_rx,
},
)
}

fn message(&mut self, msg: &CStr, message_type: MessageType) {
let msg = msg.to_str().map_err(|_e| ()).unwrap();
let msg = format!("[{}] {}", self.service_display_name, msg);
self.info_tx
.send(Message {
message_type,
message: msg,
})
.unwrap();
}
}

impl Conversation for PamAnyConversation {
Expand All @@ -19,31 +60,26 @@ impl Conversation for PamAnyConversation {

fn prompt_blind(&mut self, msg: &CStr) -> Result<CString, ()> {
let msg = msg.to_str().map_err(|_e| ())?;
let conv = self.conv.lock().map_err(|_e| ())?;
let response = conv.send(PAM_PROMPT_ECHO_OFF, &format!("[{}] {}", self.service_display_name, msg)).map_err(|_e| ())?;
match response {
Some(c_str) => {
Ok(c_str.into())
}
None => Err(())
}
let msg = format!("[{}] {}", self.service_display_name, msg);
self.prompt_input
.send(PromptInput {
prompt_type: PromptType::Blind,
message: msg,
})
.map_err(|_e| ())?;
let response = self
.prompt_output
.recv()
.map_err(|_e| ())?
.map_err(|_e| ())?;
Ok(response)
}

fn info(&mut self, msg: &CStr) {
let msg = msg.to_str().map_err(|_e| ()).unwrap();
let msg = format!("[{}] {}", self.service_display_name, msg);
let conv = self.conv.clone();
thread::spawn(move || {
conv.lock().unwrap().send(PAM_TEXT_INFO, &msg).unwrap();
});
self.message(msg, MessageType::Info)
}

fn error(&mut self, msg: &CStr) {
let msg = msg.to_str().map_err(|_e| ()).unwrap();
let msg = format!("[{}] {}", self.service_display_name, msg);
let conv = self.conv.clone();
thread::spawn(move || {
conv.lock().unwrap().send(PAM_ERROR_MSG, &msg).unwrap();
});
self.message(msg, MessageType::Error)
}
}
Loading