From 70d66f6a74fd922f14b0bcef9ed5becfe348f2ce Mon Sep 17 00:00:00 2001 From: Shimun Date: Sat, 4 Apr 2020 15:26:49 +0200 Subject: [PATCH] added debug flag --- .drone.yml | 6 +-- Cargo.lock | 10 ++-- Cargo.toml | 6 ++- src/.lib.rs.rustfmt | 121 ++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 4 +- src/main.rs | 5 +- src/pamfido2.rs | 34 ++++++++++--- 7 files changed, 163 insertions(+), 23 deletions(-) create mode 100644 src/.lib.rs.rustfmt diff --git a/.drone.yml b/.drone.yml index e9bd9a5..7a2f490 100644 --- a/.drone.yml +++ b/.drone.yml @@ -6,14 +6,14 @@ steps: image: rust:1.41.0 commands: - apt update && apt install -y libpam-dev - - cargo test --release + - cargo test --debug - name: build_relase image: rust:1.41.0 commands: - apt update && apt install -y libpam-dev - - cargo build --release + - cargo build --debug - mkdir -p bin lib - - bash -c 'cp $${CARGO_TARGET_DIR:-target}/release/fido2-pam-credential bin/; cp $${CARGO_TARGET_DIR:-target}/release/libpam_fido2.so lib/' + - bash -c 'cp $${CARGO_TARGET_DIR:-target}/debug/pam_fido2-cli bin/; cp $${CARGO_TARGET_DIR:-target}/debug/libpam_fido2.so lib/' - strip bin/* - tar cvzf pam_fido2.tar.gz bin lib src Cargo.* when: diff --git a/Cargo.lock b/Cargo.lock index 36a7e11..bf97cc8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -409,7 +409,7 @@ dependencies = [ "ctrlc 3.1.4 (registry+https://github.com/rust-lang/crates.io-index)", "failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", "hex 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "pamsm 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "pamsm 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", "regex 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "structopt 0.3.11 (registry+https://github.com/rust-lang/crates.io-index)", @@ -417,12 +417,8 @@ dependencies = [ [[package]] name = "pamsm" -version = "0.2.0" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", - "time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", -] [[package]] name = "ppv-lite86" @@ -917,7 +913,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum nix 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)" = "50e4785f2c3b7589a0d0c1dd60285e1188adac4006e8abd6dd578e1567027363" "checksum num-derive 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "eafd0b45c5537c3ba526f79d3e75120036502bebacbb3f3220914067ce39dbf2" "checksum num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "6ba9a427cfca2be13aa6f6403b0b7e7368fe982bfa16fccc450ce74c46cd9b32" -"checksum pamsm 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "56d923d68b74200a15da0c04329bc38e9975939ad4f23d2ea2baaa5aa23fc80c" +"checksum pamsm 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "0477e904be95ca350676c9e9e73776d2316b7a33d3d9fb72bc56884a2366e307" "checksum ppv-lite86 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "74490b50b9fbe561ac330df47c08f3f33073d2d00c150f719147d7c54522fa1b" "checksum proc-macro-error 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)" = "e7959c6467d962050d639361f7703b2051c43036d03493c36f01d440fdd3138a" "checksum proc-macro-error-attr 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)" = "e4002d9f55991d5e019fb940a90e1a95eb80c24e77cb2462dd4dc869604d543a" diff --git a/Cargo.toml b/Cargo.toml index 5ab241d..28abdfc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pam_fido2" -version = "0.2.2" +version = "0.2.4" authors = ["shimun "] edition = "2018" @@ -15,10 +15,12 @@ crate-type = ["cdylib"] name = "pam_fido2-cli" path = "src/main.rs" +[profile.release] +opt-level = 0 [dependencies] ctap_hmac = { git = "https://git.shimun.net/shimun/ctap", rev = "cc48719cfa1375909877265905264fad875d1fa3", features = ["request_multiple"] } -pamsm = "0.2.0" +pamsm = { version = "0.3.3", features = ["libpam"] } regex = "1.3.1" rand = "0.7.2" hex = "0.4.0" diff --git a/src/.lib.rs.rustfmt b/src/.lib.rs.rustfmt new file mode 100644 index 0000000..17c2219 --- /dev/null +++ b/src/.lib.rs.rustfmt @@ -0,0 +1,121 @@ +#[macro_use] +extern crate pamsm; +extern crate ctap_hmac as ctap; +use pamsm::{Pam, PamError, PamFlag, PamServiceModule}; +use rand::Rng; +use regex::Regex; +use std::fs::File; +use std::io::{self, prelude::*, BufReader}; +use std::time::{Duration, SystemTime}; + +struct PamFido2; + +struct Settings { + pub device_timeout: Duration, + pub user_credentials: Vec<(String, String)>, +} + +impl Default for Settings { + fn default() -> Self { + Settings { + device_timeout: Duration::from_secs(15), + user_credentials: vec![("091566e43802c5a29971c1e08d7865d959af862cc28af22dacf413ac26b90f6dea7d1ac491d9d3712c63f7b8d6cfadf86d057d099d382246dbe9c87f133ed167881b65030000".into(),".*".into())], + } + } +} + +impl Settings { + + /*pub fn load(file: impl Read) -> io::Result { + + }*/ + + pub fn load() -> Settings { + let mut creds = Vec::new(); + for path in &["/etc/pam_fido2.conf"] { + let file = match File::open(&path) { + Ok(file) => file, + _ => continue, + }; + let reader = BufReader::new(file); + + for line in reader.lines() { + let line = line.unwrap(); + let mut parts = line.split(":"); + let user = parts.by_ref().next().unwrap(); + creds.push(( + (&parts.collect::>()[..].join(":")).to_string(), + user.to_string(), + )); + } + } + Settings { + user_credentials: creds, + ..Default::default() + } + } + + pub fn get_credential(&self, user: &str) -> Option { + for (cred, pattern) in self.user_credentials.iter() { + let re = Regex::new(&pattern).expect(&["Invalid regex pattern:", &pattern].join(" ")); + if re.is_match(user) { + let mut parts = cred.split(":"); + //TODO: use expect + let id = parts.by_ref().next().unwrap(); + let key = parts.by_ref().next().unwrap(); + return Some(ctap::FidoCredential { + id: hex::decode(id).unwrap(), + public_key: hex::decode(key).unwrap(), + rp_id: "pam".into(), + }); + } + } + None + } +} + +impl PamFido2 { + fn settings(&self) -> Settings { + Settings::load() + } +} + +impl PamServiceModule for PamFido2 { + fn authenticate(self: &Self, pamh: Pam, _: PamFlag, _args: Vec) -> PamError { + let settings = self.settings(); + let begin = SystemTime::now(); + let mut device = loop { + if let Ok(mut devices) = ctap::get_devices() { + if let Some(dev) = devices.next() { + break ctap::FidoDevice::new(&dev).unwrap(); + } + } + if begin.elapsed().unwrap() > settings.device_timeout { + return PamError::AUTH_ERR; + } + }; + let challenge = rand::thread_rng().gen::<[u8; 32]>(); + let credential = match settings + .get_credential( + &pamh + .get_cached_user() + .ok() + .map(|name| name.unwrap().to_str().unwrap().to_string()) + .expect("Faied to get username"), + ) { Some(cred) => cred, _ => return PamError::CRED_UNAVAIL }; + match device.get_assertion(&credential, &challenge) { + Ok(true) => PamError::SUCCESS, + _ => PamError::AUTH_ERR, + } + } +} + +pamsm_init!(Box::new(PamFido2)); + +#[cfg(test)] +mod tests { + #[test] + fn it_works() { + assert_eq!(2 + 2, 4); + } +} diff --git a/src/lib.rs b/src/lib.rs index a7ce442..9abaef4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,6 +2,6 @@ extern crate pamsm; extern crate ctap_hmac as ctap; mod pamfido2; - +use crate::pamsm::PamServiceModule; use pamfido2::PamFido2; -pamsm_init!(Box::new(PamFido2)); +pam_module!(PamFido2); diff --git a/src/main.rs b/src/main.rs index b939610..0177403 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,5 @@ -#[macro_use] -extern crate pamsm; extern crate ctap_hmac as ctap; +extern crate pamsm; mod pamfido2; use ctap::{make_credential_devices, FidoCredentialRequestBuilder, FidoDevice}; @@ -74,7 +73,7 @@ fn main() -> Result<(), Box> { auth_file: authfile, username, } => { - let res = PamFido2.authenticate( + let res = PamFido2.fido_authenticate( &username, vec![["authfile=", authfile.to_str().unwrap()].join("")], )?; diff --git a/src/pamfido2.rs b/src/pamfido2.rs index 470314b..71aa5d7 100644 --- a/src/pamfido2.rs +++ b/src/pamfido2.rs @@ -1,4 +1,5 @@ use ctap::{get_assertion_devices, FidoAssertionRequestBuilder, FidoDevice, FidoErrorKind}; +use pamsm::PamLibExt; use pamsm::{Pam, PamError, PamFlag, PamServiceModule}; use rand::Rng; use regex::Regex; @@ -16,11 +17,13 @@ pub struct PamFido2; struct Settings { pub device_timeout: Duration, pub user_credentials: Vec<(String, String)>, + pub debug: bool, } impl Default for Settings { fn default() -> Self { Settings { + debug: false, device_timeout: Duration::from_secs(15), user_credentials: vec![("091566e43802c5a29971c1e08d7865d959af862cc28af22dacf413ac26b90f6dea7d1ac491d9d3712c63f7b8d6cfadf86d057d099d382246dbe9c87f133ed167881b65030000".into(),".*".into())], } @@ -54,6 +57,9 @@ impl Settings { if let Some(timeout) = timeout { settings.device_timeout = timeout; }; + if let Some(debug) = args.get("debug").and_then(|d| d.parse::().ok()) { + settings.debug = debug; + } Ok(settings) } pub fn load(path: impl AsRef) -> io::Result { @@ -98,18 +104,25 @@ impl Settings { } impl PamFido2 { - pub fn authenticate( + pub fn fido_authenticate( &self, username: &str, args: Vec, ) -> Result> { let settings = Settings::from_args(args)?; + let debug = |msg: &dyn Fn() -> String| { + if settings.debug { + eprintln!("{}", msg()) + } + }; let begin = SystemTime::now(); let challenge = rand::thread_rng().gen::<[u8; 32]>(); let credentials = settings.get_credentials(username); if credentials.is_empty() { + debug(&|| format!("Found no credentials for {}", username)); return Ok(PamError::AUTH_ERR); } + debug(&|| format!("Found {} credentials for {}", credentials.len(), username)); let slice = credentials.iter().collect::>(); let request = FidoAssertionRequestBuilder::default() .rp_id("fido2pam") @@ -123,10 +136,19 @@ impl PamFido2 { .compat()? .filter_map(|handle| FidoDevice::new(&handle).ok()) .collect::>(); + if devices.is_empty() { + debug(&|| "Please connect your authenicator!".into()); + std::thread::sleep(Duration::from_secs(1)); + continue; + } match get_assertion_devices(&request, devices.iter_mut()) { - Ok(_) => return Ok(PamError::SUCCESS), + Ok((cred, _)) => { + debug(&|| format!("Got assertion for credential: {}", hex::encode(&cred.id))); + return Ok(PamError::SUCCESS); + } Err(_) if begin.elapsed().unwrap() > settings.device_timeout => { - return Ok(PamError::AUTH_ERR) + debug(&|| format!("Exceeded {:?} timeout", settings.device_timeout)); + return Ok(PamError::AUTH_ERR); } Err(e) if e.kind() == FidoErrorKind::DeviceUnsupported => continue, Err(e) => Err(e).compat()?, @@ -136,14 +158,14 @@ impl PamFido2 { } impl PamServiceModule for PamFido2 { - fn authenticate(self: &Self, pamh: Pam, _: PamFlag, args: Vec) -> PamError { + fn authenticate(pamh: Pam, _: PamFlag, args: Vec) -> PamError { let username = &pamh .get_cached_user() .ok() .map(|name| name.unwrap().to_str().unwrap().to_string()) - .expect("Faied to get username"); + .expect("Failed to get username"); - match self.authenticate(username, args) { + match PamFido2.fido_authenticate(username, args) { Ok(e) => e, Err(e) => { eprintln!("{}", e);