extern crate ldap3;
#[cfg(target_env = "musl")]
extern crate openssl_probe;
extern crate serde;
#[macro_use]
extern crate serde_derive;
#[macro_use]
extern crate structopt;
extern crate toml;
use std::error::Error;
use std::fs::File;
use std::io::prelude::*;
use std::os::unix::fs::MetadataExt;
use std::path::PathBuf;
use ldap3::{LdapConn, Scope, SearchEntry};
use structopt::StructOpt;
macro_rules! texterr {
($($tt:tt)*) => {
Err(From::from(format!($($tt)*)))
}
}
#[derive(Debug, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "lowercase")]
enum LdapScope {
Base,
One,
Subtree,
}
#[derive(Debug, Deserialize, PartialEq)]
struct Config {
uri: String,
base: String,
binddn: Option<String>,
bindpw: Option<String>,
scope: LdapScope,
filter: String,
attr: String,
}
#[derive(StructOpt, Debug)]
#[structopt()]
struct Opt {
#[structopt(
short = "c", long = "config", parse(from_os_str), default_value = "/etc/sakcl.conf"
)]
config: PathBuf,
#[structopt(name = "USERNAME")]
uid: String,
}
fn main() {
let opt = Opt::from_args();
if let Err(e) = run(opt) {
eprintln!("{}", e);
::std::process::exit(1);
}
}
#[cfg(target_env = "musl")]
fn openssl_env_init() {
openssl_probe::init_ssl_cert_env_vars();
}
#[cfg(not(target_env = "musl"))]
fn openssl_env_init() {}
fn run(opt: Opt) -> Result<(), Box<Error>> {
let cfg: Config = {
let mut file = File::open(&opt.config)
.map_err(|e| format!("Unable to open config at {}: {}", opt.config.display(), e))?;
let md = file.metadata()
.map_err(|e| format!("Unable to read permissions on config file: {}", e))?;
let mode = md.mode();
if (mode & 0o377) > 0 {
return texterr!(
"insecure permissions on config file. Got {:o} and expected {:o}",
mode,
(0o777400 & mode)
);
}
let mut s = String::new();
file.read_to_string(&mut s)?;
toml::from_str(&s).map_err(|e| format!("invalid config: {}", e))
}?;
openssl_env_init();
let ldap = LdapConn::new(&cfg.uri)?;
if let Some(binddn) = cfg.binddn {
ldap.simple_bind(&binddn, &cfg.bindpw.unwrap_or_else(|| "".to_string()))?
.success()?;
}
let scope = match cfg.scope {
LdapScope::Base => Scope::Base,
LdapScope::One => Scope::OneLevel,
LdapScope::Subtree => Scope::Subtree,
};
let (rs, _res) = ldap.search(
&cfg.base,
scope,
&cfg.filter.replace("*", &opt.uid),
vec![&cfg.attr],
)?
.success()?;
for entry in rs {
let entry = SearchEntry::construct(entry);
if let Some(keys) = entry.attrs.get(&cfg.attr) {
for key in keys {
println!("{}", key);
}
}
}
Ok(())
}