From 63f29249d394ed458654e5116c9ad424df60fd9d Mon Sep 17 00:00:00 2001 From: shimun Date: Fri, 25 Sep 2020 00:23:36 +0200 Subject: [PATCH] pam module --- Cargo.lock | 7 ++++ Cargo.toml | 11 ++++++ src/lib.rs | 111 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 129 insertions(+) create mode 100644 src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index b91b404..679ecf0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -383,6 +383,7 @@ dependencies = [ "failure", "hex", "libcryptsetup-rs", + "pamsm", "ring", "rpassword", "serde", @@ -596,6 +597,12 @@ version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1ab52be62400ca80aa00285d25253d7f7c437b7375c4de678f5405d3afe82ca5" +[[package]] +name = "pamsm" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3580ed2ebe075c74db583233318abf4b07bc8d9a40c7691d0ae9c186e19e43dd" + [[package]] name = "peeking_take_while" version = "0.1.2" diff --git a/Cargo.toml b/Cargo.toml index ad486f0..c4d86c5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,6 +24,7 @@ libcryptsetup-rs = "0.4.1" serde_json = "1.0.51" serde_derive = "1.0.106" serde = "1.0.106" +pamsm = { version = "0.4.1", features = ["libpam"] } [build-dependencies] ctap_hmac = { version="0.4.2", features = ["request_multiple"] } @@ -41,12 +42,22 @@ panic = 'abort' incremental = false overflow-checks = false +[[bin]] +name = "fido2luks" +path = "src/main.rs" + +[lib] +name = "fido2luks_pam" +path = "src/lib.rs" +crate-type = ["cdylib"] + [package.metadata.deb] depends = "$auto, cryptsetup" build-depends = "libclang-dev, libcryptsetup-dev" extended-description = "Decrypt your LUKS partition using a FIDO2 compatible authenticator" assets = [ ["target/release/fido2luks", "usr/bin/", "755"], + ["target/release/libfido2luks_pam.so", "usr/lib/x86_64-linux-gnu/security/pam_fido2luks", "755"], ["fido2luks.bash", "usr/share/bash-completion/completions/fido2luks", "644"], ["initramfs-tools/keyscript.sh", "/lib/cryptsetup/scripts/fido2luks", "755" ], ["initramfs-tools/hook/fido2luks.sh", "etc/initramfs-tools/hooks/", "755" ], diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..139cd2a --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,111 @@ +#[macro_use] +extern crate failure; +extern crate ctap_hmac as ctap; +#[macro_use] +extern crate serde_derive; +#[macro_use] +extern crate pamsm; +use crate::cli_args::{CommaSeparated, HexEncoded}; +use crate::device::*; +use crate::error::*; +use crate::luks::*; +use ctap::FidoCredential; +use failure::_core::time::Duration; +use pamsm::PamLibExt; +use pamsm::*; +use std::collections::{HashMap, HashSet}; +use std::ffi::CStr; +use std::str::FromStr; + +pub mod cli_args; +pub mod device; +pub mod error; +pub mod luks; +pub mod util; + +struct PamFido2Luks; + +impl PamFido2Luks { + fn open(&self, password: String, args: Vec) -> Fido2LuksResult<()> { + let args: HashMap = args + .into_iter() + .filter_map(|arg| { + let mut parts = arg.split("="); + parts + .by_ref() + .next() + .map(|key| (key.to_string(), parts.collect::>().join("="))) + }) + .collect(); + + let credentials = args + .get("credentials") + .map(|creds| { + >::from_str(creds) + .expect("Invalid credentials") + .0 //TODO: proper error handling + }) + .unwrap_or_default(); + let pin = args.get("pin"); + let device = args.get("device"); + let name = args.get("name"); + + if let (Some(device), Some(name)) = (device, name) { + let mut device = LuksDevice::load(device)?; + let mut additional_credentials: HashSet = HashSet::new(); + if device.is_luks2()? { + for token in device.tokens()? { + let (_, token) = token?; + additional_credentials.extend(token.credential.into_iter()); + } + } + let credentials: Vec = credentials + .into_iter() + .chain(additional_credentials.into_iter()) + .map(|cred| HexEncoded::from_str(cred.as_str())) + .map(|cred| FidoCredential { + id: cred.unwrap().0, + public_key: None, + }) + .collect(); + let credentials: Vec<&FidoCredential> = credentials.iter().collect(); + if !credentials.is_empty() { + let secret = util::sha256(&[&perform_challenge( + &credentials[..], + &util::sha256(&[password.as_bytes()]), + Duration::from_secs(15), + pin.map(AsRef::as_ref), + )? + .0[..]]); + device.activate(name.as_str(), &secret[..], None)?; + } else { + unimplemented!("custom error") + } + unimplemented!() + } else { + unimplemented!("custom error") + } + } +} + +impl PamServiceModule for PamFido2Luks { + fn authenticate(pamh: Pam, flag: PamFlag, args: Vec) -> PamError { + let password = match pamh.get_authtok(None) { + Err(_) => return PamError::AUTH_ERR, + Ok(p) => p.map(|s| s.to_str().map(str::to_string).unwrap()), + }; + if let Some(password) = password { + match PamFido2Luks.open(password, args) { + Ok(_) => PamError::SUCCESS, + Err(e) => match e { + //TODO: output more detailed error + _ => PamError::AUTH_ERR, + }, + } + } else { + PamError::AUTH_ERR + } + } +} + +pam_module!(PamFido2Luks);