2020-06-18 02:30:42 +02:00
|
|
|
#![deny(warnings)]
|
2020-08-01 12:31:05 +02:00
|
|
|
#![warn(unused_extern_crates)]
|
|
|
|
#![deny(clippy::unwrap_used)]
|
|
|
|
#![deny(clippy::expect_used)]
|
|
|
|
#![deny(clippy::panic)]
|
|
|
|
#![deny(clippy::unreachable)]
|
|
|
|
#![deny(clippy::await_holding_lock)]
|
|
|
|
#![deny(clippy::needless_pass_by_value)]
|
|
|
|
#![deny(clippy::trivially_copy_pass_by_ref)]
|
|
|
|
|
|
|
|
// extern crate libc;
|
2020-02-29 05:02:14 +01:00
|
|
|
|
|
|
|
mod pam;
|
|
|
|
use crate::pam::constants::*;
|
|
|
|
use crate::pam::conv::PamConv;
|
|
|
|
use crate::pam::module::{PamHandle, PamHooks};
|
|
|
|
|
|
|
|
use std::collections::BTreeSet;
|
|
|
|
use std::convert::TryFrom;
|
|
|
|
use std::ffi::CStr;
|
|
|
|
// use std::os::raw::c_char;
|
|
|
|
|
|
|
|
// use futures::executor::block_on;
|
|
|
|
use tokio::runtime::Runtime;
|
|
|
|
|
|
|
|
use kanidm_unix_common::client::call_daemon;
|
|
|
|
use kanidm_unix_common::unix_config::KanidmUnixdConfig;
|
|
|
|
use kanidm_unix_common::unix_proto::{ClientRequest, ClientResponse};
|
|
|
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
struct Options {
|
|
|
|
debug: bool,
|
|
|
|
use_first_pass: bool,
|
|
|
|
ignore_unknown_user: bool,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl TryFrom<&Vec<&CStr>> for Options {
|
|
|
|
type Error = ();
|
|
|
|
|
|
|
|
fn try_from(args: &Vec<&CStr>) -> Result<Self, Self::Error> {
|
|
|
|
let opts: Result<BTreeSet<&str>, _> = args.iter().map(|cs| cs.to_str()).collect();
|
|
|
|
let gopts = match opts {
|
|
|
|
Ok(o) => o,
|
|
|
|
Err(e) => {
|
|
|
|
println!("Error in module args -> {:?}", e);
|
|
|
|
return Err(());
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
Ok(Options {
|
|
|
|
debug: gopts.contains("debug"),
|
|
|
|
use_first_pass: gopts.contains("use_first_pass"),
|
|
|
|
ignore_unknown_user: gopts.contains("ignore_unknown_user"),
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn get_cfg() -> Result<KanidmUnixdConfig, PamResultCode> {
|
|
|
|
KanidmUnixdConfig::new()
|
|
|
|
.read_options_from_optional_config("/etc/kanidm/unixd")
|
|
|
|
.map_err(|_| PamResultCode::PAM_SERVICE_ERR)
|
|
|
|
}
|
|
|
|
|
|
|
|
struct PamKanidm;
|
|
|
|
pam_hooks!(PamKanidm);
|
|
|
|
|
|
|
|
impl PamHooks for PamKanidm {
|
|
|
|
fn acct_mgmt(pamh: &PamHandle, args: Vec<&CStr>, _flags: PamFlag) -> PamResultCode {
|
|
|
|
let opts = match Options::try_from(&args) {
|
|
|
|
Ok(o) => o,
|
|
|
|
Err(_) => return PamResultCode::PAM_SERVICE_ERR,
|
|
|
|
};
|
|
|
|
|
|
|
|
if opts.debug {
|
|
|
|
println!("acct_mgmt");
|
|
|
|
println!("args -> {:?}", args);
|
|
|
|
println!("opts -> {:?}", opts);
|
|
|
|
}
|
|
|
|
|
|
|
|
let account_id = match pamh.get_user(None) {
|
|
|
|
Ok(aid) => aid,
|
|
|
|
Err(e) => {
|
|
|
|
if opts.debug {
|
|
|
|
println!("Error get_user -> {:?}", e);
|
|
|
|
}
|
|
|
|
return e;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
let cfg = match get_cfg() {
|
|
|
|
Ok(cfg) => cfg,
|
|
|
|
Err(e) => return e,
|
|
|
|
};
|
|
|
|
let req = ClientRequest::PamAccountAllowed(account_id);
|
|
|
|
// PamResultCode::PAM_IGNORE
|
|
|
|
|
|
|
|
let mut rt = match Runtime::new() {
|
|
|
|
Ok(rt) => rt,
|
|
|
|
Err(_) => return PamResultCode::PAM_SERVICE_ERR,
|
|
|
|
};
|
|
|
|
|
|
|
|
match rt.block_on(call_daemon(cfg.sock_path.as_str(), req)) {
|
|
|
|
Ok(r) => match r {
|
|
|
|
ClientResponse::PamStatus(Some(true)) => {
|
|
|
|
// println!("PAM_SUCCESS");
|
|
|
|
PamResultCode::PAM_SUCCESS
|
|
|
|
}
|
|
|
|
ClientResponse::PamStatus(Some(false)) => {
|
|
|
|
// println!("PAM_IGNORE");
|
|
|
|
PamResultCode::PAM_AUTH_ERR
|
|
|
|
}
|
|
|
|
ClientResponse::PamStatus(None) => {
|
|
|
|
if opts.ignore_unknown_user {
|
|
|
|
PamResultCode::PAM_IGNORE
|
|
|
|
} else {
|
|
|
|
PamResultCode::PAM_USER_UNKNOWN
|
|
|
|
}
|
|
|
|
}
|
|
|
|
_ => {
|
|
|
|
// unexpected response.
|
|
|
|
if opts.debug {
|
|
|
|
println!("PAM_IGNORE -> {:?}", r);
|
|
|
|
}
|
|
|
|
PamResultCode::PAM_IGNORE
|
|
|
|
}
|
|
|
|
},
|
|
|
|
Err(e) => {
|
|
|
|
if opts.debug {
|
|
|
|
println!("PAM_IGNORE -> {:?}", e);
|
|
|
|
}
|
|
|
|
PamResultCode::PAM_IGNORE
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn sm_authenticate(pamh: &PamHandle, args: Vec<&CStr>, _flags: PamFlag) -> PamResultCode {
|
|
|
|
let opts = match Options::try_from(&args) {
|
|
|
|
Ok(o) => o,
|
|
|
|
Err(_) => return PamResultCode::PAM_SERVICE_ERR,
|
|
|
|
};
|
|
|
|
|
|
|
|
if opts.debug {
|
|
|
|
println!("sm_authenticate");
|
|
|
|
println!("args -> {:?}", args);
|
|
|
|
println!("opts -> {:?}", opts);
|
|
|
|
}
|
|
|
|
|
|
|
|
let account_id = match pamh.get_user(None) {
|
|
|
|
Ok(aid) => aid,
|
|
|
|
Err(e) => {
|
|
|
|
println!("Error get_user -> {:?}", e);
|
|
|
|
return e;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
let authtok = match pamh.get_authtok() {
|
|
|
|
Ok(atok) => atok,
|
|
|
|
Err(e) => {
|
|
|
|
if opts.debug {
|
|
|
|
println!("Error get_authtok -> {:?}", e);
|
|
|
|
}
|
|
|
|
return e;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
let authtok = match authtok {
|
|
|
|
Some(v) => v,
|
|
|
|
None => {
|
|
|
|
if opts.use_first_pass {
|
|
|
|
if opts.debug {
|
|
|
|
println!("Don't have an authtok, returning PAM_AUTH_ERR");
|
|
|
|
}
|
|
|
|
return PamResultCode::PAM_AUTH_ERR;
|
|
|
|
} else {
|
|
|
|
let conv = match pamh.get_item::<PamConv>() {
|
|
|
|
Ok(conv) => conv,
|
|
|
|
Err(err) => {
|
|
|
|
if opts.debug {
|
|
|
|
println!("Couldn't get pam_conv");
|
|
|
|
}
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
match conv.send(PAM_PROMPT_ECHO_OFF, "Password: ") {
|
|
|
|
Ok(password) => match password {
|
|
|
|
Some(pw) => pw,
|
|
|
|
None => {
|
|
|
|
if opts.debug {
|
|
|
|
println!("No password");
|
|
|
|
}
|
|
|
|
return PamResultCode::PAM_CRED_INSUFFICIENT;
|
|
|
|
}
|
|
|
|
},
|
|
|
|
Err(err) => {
|
|
|
|
if opts.debug {
|
|
|
|
println!("Couldn't get password");
|
|
|
|
}
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} // end opts.use_first_pass
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
let cfg = match get_cfg() {
|
|
|
|
Ok(cfg) => cfg,
|
|
|
|
Err(e) => return e,
|
|
|
|
};
|
|
|
|
let req = ClientRequest::PamAuthenticate(account_id, authtok);
|
|
|
|
|
|
|
|
let mut rt = match Runtime::new() {
|
|
|
|
Ok(rt) => rt,
|
|
|
|
Err(_) => return PamResultCode::PAM_SERVICE_ERR,
|
|
|
|
};
|
|
|
|
|
|
|
|
match rt.block_on(call_daemon(cfg.sock_path.as_str(), req)) {
|
|
|
|
Ok(r) => match r {
|
|
|
|
ClientResponse::PamStatus(Some(true)) => {
|
|
|
|
// println!("PAM_SUCCESS");
|
|
|
|
PamResultCode::PAM_SUCCESS
|
|
|
|
}
|
|
|
|
ClientResponse::PamStatus(Some(false)) => {
|
|
|
|
// println!("PAM_AUTH_ERR");
|
|
|
|
PamResultCode::PAM_AUTH_ERR
|
|
|
|
}
|
|
|
|
ClientResponse::PamStatus(None) => {
|
|
|
|
// println!("PAM_USER_UNKNOWN");
|
|
|
|
if opts.ignore_unknown_user {
|
|
|
|
PamResultCode::PAM_IGNORE
|
|
|
|
} else {
|
|
|
|
PamResultCode::PAM_USER_UNKNOWN
|
|
|
|
}
|
|
|
|
}
|
|
|
|
_ => {
|
|
|
|
// unexpected response.
|
|
|
|
if opts.debug {
|
|
|
|
println!("PAM_IGNORE -> {:?}", r);
|
|
|
|
}
|
|
|
|
PamResultCode::PAM_IGNORE
|
|
|
|
}
|
|
|
|
},
|
|
|
|
Err(e) => {
|
|
|
|
if opts.debug {
|
|
|
|
println!("PAM_IGNORE -> {:?}", e);
|
|
|
|
}
|
|
|
|
PamResultCode::PAM_IGNORE
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn sm_chauthtok(_pamh: &PamHandle, args: Vec<&CStr>, _flags: PamFlag) -> PamResultCode {
|
|
|
|
let opts = match Options::try_from(&args) {
|
|
|
|
Ok(o) => o,
|
|
|
|
Err(_) => return PamResultCode::PAM_SERVICE_ERR,
|
|
|
|
};
|
|
|
|
|
|
|
|
if opts.debug {
|
|
|
|
println!("sm_chauthtok");
|
|
|
|
println!("args -> {:?}", args);
|
|
|
|
println!("opts -> {:?}", opts);
|
|
|
|
}
|
|
|
|
PamResultCode::PAM_IGNORE
|
|
|
|
}
|
|
|
|
|
|
|
|
fn sm_close_session(_pamh: &PamHandle, args: Vec<&CStr>, _flags: PamFlag) -> PamResultCode {
|
|
|
|
let opts = match Options::try_from(&args) {
|
|
|
|
Ok(o) => o,
|
|
|
|
Err(_) => return PamResultCode::PAM_SERVICE_ERR,
|
|
|
|
};
|
|
|
|
|
|
|
|
if opts.debug {
|
|
|
|
println!("sm_close_session");
|
|
|
|
println!("args -> {:?}", args);
|
|
|
|
println!("opts -> {:?}", opts);
|
|
|
|
}
|
|
|
|
PamResultCode::PAM_SUCCESS
|
|
|
|
}
|
|
|
|
|
|
|
|
fn sm_open_session(_pamh: &PamHandle, args: Vec<&CStr>, _flags: PamFlag) -> PamResultCode {
|
|
|
|
let opts = match Options::try_from(&args) {
|
|
|
|
Ok(o) => o,
|
|
|
|
Err(_) => return PamResultCode::PAM_SERVICE_ERR,
|
|
|
|
};
|
|
|
|
|
|
|
|
if opts.debug {
|
|
|
|
println!("sm_open_session");
|
|
|
|
println!("args -> {:?}", args);
|
|
|
|
println!("opts -> {:?}", opts);
|
|
|
|
}
|
|
|
|
PamResultCode::PAM_SUCCESS
|
|
|
|
}
|
|
|
|
|
|
|
|
fn sm_setcred(_pamh: &PamHandle, args: Vec<&CStr>, _flags: PamFlag) -> PamResultCode {
|
|
|
|
let opts = match Options::try_from(&args) {
|
|
|
|
Ok(o) => o,
|
|
|
|
Err(_) => return PamResultCode::PAM_SERVICE_ERR,
|
|
|
|
};
|
|
|
|
|
|
|
|
if opts.debug {
|
|
|
|
println!("sm_setcred");
|
|
|
|
println!("args -> {:?}", args);
|
|
|
|
println!("opts -> {:?}", opts);
|
|
|
|
}
|
|
|
|
PamResultCode::PAM_SUCCESS
|
2020-02-15 01:27:25 +01:00
|
|
|
}
|
|
|
|
}
|