From 63f29249d394ed458654e5116c9ad424df60fd9d Mon Sep 17 00:00:00 2001 From: shimun Date: Fri, 25 Sep 2020 00:23:36 +0200 Subject: [PATCH 01/15] 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); -- 2.49.0 From d6f6c7c218d235d224ef59c79bfc7e20743e550d Mon Sep 17 00:00:00 2001 From: shimun Date: Fri, 25 Sep 2020 00:24:46 +0200 Subject: [PATCH 02/15] ext --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index c4d86c5..27fed42 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -57,7 +57,7 @@ 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"], + ["target/release/libfido2luks_pam.so", "usr/lib/x86_64-linux-gnu/security/pam_fido2luks.so", "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" ], -- 2.49.0 From e5249966935fac47e8ba73bc04d0868df3d7c5da Mon Sep 17 00:00:00 2001 From: shimun Date: Fri, 25 Sep 2020 00:51:42 +0200 Subject: [PATCH 03/15] add --- src/lib.rs | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 139cd2a..6f393b5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -15,6 +15,7 @@ use pamsm::PamLibExt; use pamsm::*; use std::collections::{HashMap, HashSet}; use std::ffi::CStr; +use std::path::Path; use std::str::FromStr; pub mod cli_args; @@ -26,7 +27,7 @@ pub mod util; struct PamFido2Luks; impl PamFido2Luks { - fn open(&self, password: String, args: Vec) -> Fido2LuksResult<()> { + fn open(&self, user: String, password: String, args: Vec) -> Fido2LuksResult<()> { let args: HashMap = args .into_iter() .filter_map(|arg| { @@ -47,10 +48,18 @@ impl PamFido2Luks { }) .unwrap_or_default(); let pin = args.get("pin"); - let device = args.get("device"); - let name = args.get("name"); + let device = args + .get("device") + .map(|device| device.replace("%user%", user.as_str())); + let name = args + .get("name") + .map(|name| name.replace("%user%", user.as_str())); if let (Some(device), Some(name)) = (device, name) { + if !Path::new(&device).exists() || Path::new(&format!("/dev/mapper/{}", name)).exists() + { + return Ok(()); + } let mut device = LuksDevice::load(device)?; let mut additional_credentials: HashSet = HashSet::new(); if device.is_luks2()? { @@ -90,12 +99,16 @@ impl PamFido2Luks { impl PamServiceModule for PamFido2Luks { fn authenticate(pamh: Pam, flag: PamFlag, args: Vec) -> PamError { + let user = match pamh.get_cached_user() { + Err(_) => return PamError::AUTH_ERR, + Ok(p) => p.map(|s| s.to_str().map(str::to_string).unwrap()), + }; 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) { + if let (Some(user), Some(password)) = (user, password) { + match PamFido2Luks.open(user, password, args) { Ok(_) => PamError::SUCCESS, Err(e) => match e { //TODO: output more detailed error -- 2.49.0 From d4094b8a6ae59d8f862f7f9edbcc3944fcb52320 Mon Sep 17 00:00:00 2001 From: shimun Date: Fri, 25 Sep 2020 01:00:59 +0200 Subject: [PATCH 04/15] dbg --- src/lib.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 6f393b5..edd24c9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -100,11 +100,11 @@ impl PamFido2Luks { impl PamServiceModule for PamFido2Luks { fn authenticate(pamh: Pam, flag: PamFlag, args: Vec) -> PamError { let user = match pamh.get_cached_user() { - Err(_) => return PamError::AUTH_ERR, + Err(_) => return dbg!(PamError::AUTH_ERR), Ok(p) => p.map(|s| s.to_str().map(str::to_string).unwrap()), }; let password = match pamh.get_authtok(None) { - Err(_) => return PamError::AUTH_ERR, + Err(_) => return dbg!(PamError::AUTH_ERR), Ok(p) => p.map(|s| s.to_str().map(str::to_string).unwrap()), }; if let (Some(user), Some(password)) = (user, password) { @@ -112,11 +112,11 @@ impl PamServiceModule for PamFido2Luks { Ok(_) => PamError::SUCCESS, Err(e) => match e { //TODO: output more detailed error - _ => PamError::AUTH_ERR, + _ => dbg!(PamError::AUTH_ERR), }, } } else { - PamError::AUTH_ERR + dbg!(PamError::AUTH_ERR) } } } -- 2.49.0 From 985f6f664bed0c064a68ca45845375a503e125fa Mon Sep 17 00:00:00 2001 From: shimun Date: Fri, 25 Sep 2020 01:19:47 +0200 Subject: [PATCH 05/15] include password --- src/lib.rs | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index edd24c9..f4d7867 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -79,13 +79,16 @@ impl PamFido2Luks { .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[..]]); + let secret = util::sha256(&[ + password.as_bytes(), + &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") @@ -112,11 +115,11 @@ impl PamServiceModule for PamFido2Luks { Ok(_) => PamError::SUCCESS, Err(e) => match e { //TODO: output more detailed error - _ => dbg!(PamError::AUTH_ERR), + _ => PamError::AUTH_ERR, }, } } else { - dbg!(PamError::AUTH_ERR) + PamError::AUTH_ERR } } } -- 2.49.0 From f2a8e412ac1ab5c454dc02241d5247765f822aad Mon Sep 17 00:00:00 2001 From: shimun Date: Fri, 25 Sep 2020 01:21:34 +0200 Subject: [PATCH 06/15] salt --- src/lib.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index f4d7867..238be78 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -79,11 +79,12 @@ impl PamFido2Luks { .collect(); let credentials: Vec<&FidoCredential> = credentials.iter().collect(); if !credentials.is_empty() { + let salt = util::sha256(&[password.as_bytes()]); let secret = util::sha256(&[ - password.as_bytes(), + &salt, &perform_challenge( &credentials[..], - &util::sha256(&[password.as_bytes()]), + &salt, Duration::from_secs(15), pin.map(AsRef::as_ref), )? -- 2.49.0 From 79849df2840953236324e8692a694acab8244371 Mon Sep 17 00:00:00 2001 From: shimun Date: Fri, 25 Sep 2020 01:38:17 +0200 Subject: [PATCH 07/15] add pam dependency --- Cargo.lock | 2 +- Cargo.toml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 679ecf0..b129021 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -377,7 +377,7 @@ dependencies = [ [[package]] name = "fido2luks" -version = "0.2.14" +version = "0.2.15" dependencies = [ "ctap_hmac", "failure", diff --git a/Cargo.toml b/Cargo.toml index 27fed42..aa4c136 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "fido2luks" -version = "0.2.14" +version = "0.2.15" authors = ["shimunn "] edition = "2018" @@ -53,7 +53,7 @@ crate-type = ["cdylib"] [package.metadata.deb] depends = "$auto, cryptsetup" -build-depends = "libclang-dev, libcryptsetup-dev" +build-depends = "libclang-dev, libcryptsetup-dev, libpam-dev" extended-description = "Decrypt your LUKS partition using a FIDO2 compatible authenticator" assets = [ ["target/release/fido2luks", "usr/bin/", "755"], -- 2.49.0 From 31ee2dcbe7a4a844d1fb93b12fa7e7d8655854ff Mon Sep 17 00:00:00 2001 From: shimun Date: Mon, 28 Sep 2020 17:27:13 +0200 Subject: [PATCH 08/15] libpam-dev --- .drone.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.drone.yml b/.drone.yml index 7e3a505..00daafa 100644 --- a/.drone.yml +++ b/.drone.yml @@ -12,7 +12,7 @@ steps: environment: DEBIAN_FRONTEND: noninteractive commands: - - apt update && apt install -y cargo libkeyutils-dev libclang-dev clang pkg-config libcryptsetup-dev + - apt update && apt install -y cargo libkeyutils-dev libclang-dev clang pkg-config libcryptsetup-dev libpam-dev - cargo test --locked - name: publish image: ubuntu:focal @@ -22,7 +22,7 @@ steps: from_secret: cargo_tkn commands: - grep -E 'version ?= ?"${DRONE_TAG}"' -i Cargo.toml || (printf "incorrect crate/tag version" && exit 1) - - apt update && apt install -y cargo libkeyutils-dev libclang-dev clang pkg-config libcryptsetup-dev + - apt update && apt install -y cargo libkeyutils-dev libclang-dev clang pkg-config libcryptsetup-dev libpam-dev - cargo package --all-features - cargo publish --all-features when: -- 2.49.0 From fbbf6066310f8af03d30596ecdd9e2ce8433e51e Mon Sep 17 00:00:00 2001 From: shimun Date: Tue, 29 Sep 2020 18:28:22 +0200 Subject: [PATCH 09/15] sudo --- Cargo.lock | 11 ++++++++++ Cargo.toml | 1 + src/lib.rs | 64 ++++++++++++++++++++++++++++++++++++++++-------------- 3 files changed, 60 insertions(+), 16 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b129021..04e4dd2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -390,6 +390,7 @@ dependencies = [ "serde_derive", "serde_json", "structopt", + "sudo", ] [[package]] @@ -987,6 +988,16 @@ dependencies = [ "syn 1.0.40", ] +[[package]] +name = "sudo" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a88e74edf206f281aff2820aa2066c781331044c770626dcafe19491f214e05" +dependencies = [ + "libc", + "log", +] + [[package]] name = "syn" version = "0.15.44" diff --git a/Cargo.toml b/Cargo.toml index aa4c136..14d75cd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,6 +25,7 @@ serde_json = "1.0.51" serde_derive = "1.0.106" serde = "1.0.106" pamsm = { version = "0.4.1", features = ["libpam"] } +sudo = "0.5.0" [build-dependencies] ctap_hmac = { version="0.4.2", features = ["request_multiple"] } diff --git a/src/lib.rs b/src/lib.rs index 238be78..c2f3a86 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -17,6 +17,7 @@ use std::collections::{HashMap, HashSet}; use std::ffi::CStr; use std::path::Path; use std::str::FromStr; +use sudo::{self, RunningAs}; pub mod cli_args; pub mod device; @@ -27,7 +28,12 @@ pub mod util; struct PamFido2Luks; impl PamFido2Luks { - fn open(&self, user: String, password: String, args: Vec) -> Fido2LuksResult<()> { + fn open( + &self, + user: String, + mut password: impl FnMut() -> PamResult, + args: Vec, + ) -> Fido2LuksResult<()> { let args: HashMap = args .into_iter() .filter_map(|arg| { @@ -55,11 +61,26 @@ impl PamFido2Luks { .get("name") .map(|name| name.replace("%user%", user.as_str())); + let attempts = args + .get("attempts") + .and_then(|a| a.parse::().ok()) + .unwrap_or(3); + if let (Some(device), Some(name)) = (device, name) { if !Path::new(&device).exists() || Path::new(&format!("/dev/mapper/{}", name)).exists() { return Ok(()); } + // root required to mount luks + match sudo::check() { + RunningAs::User => { + //err + unimplemented!("no root") + } + _ => { + sudo::escalate_if_needed().unwrap(); + } + } let mut device = LuksDevice::load(device)?; let mut additional_credentials: HashSet = HashSet::new(); if device.is_luks2()? { @@ -79,22 +100,24 @@ impl PamFido2Luks { .collect(); let credentials: Vec<&FidoCredential> = credentials.iter().collect(); if !credentials.is_empty() { - let salt = util::sha256(&[password.as_bytes()]); - let secret = util::sha256(&[ - &salt, - &perform_challenge( - &credentials[..], + for _ in 0..attempts { + let salt = util::sha256(&[password().expect("Password").as_bytes()]); + let secret = util::sha256(&[ &salt, - Duration::from_secs(15), - pin.map(AsRef::as_ref), - )? - .0[..], - ]); - device.activate(name.as_str(), &secret[..], None)?; + &perform_challenge( + &credentials[..], + &salt, + Duration::from_secs(15), + pin.map(AsRef::as_ref), + )? + .0[..], + ]); + device.activate(name.as_str(), &secret[..], None)?; + } + Ok(()) } else { unimplemented!("custom error") } - unimplemented!() } else { unimplemented!("custom error") } @@ -107,12 +130,21 @@ impl PamServiceModule for PamFido2Luks { Err(_) => return dbg!(PamError::AUTH_ERR), Ok(p) => p.map(|s| s.to_str().map(str::to_string).unwrap()), }; - let password = match pamh.get_authtok(None) { + let mut password = match pamh.get_authtok(None) { Err(_) => return dbg!(PamError::AUTH_ERR), Ok(p) => p.map(|s| s.to_str().map(str::to_string).unwrap()), }; - if let (Some(user), Some(password)) = (user, password) { - match PamFido2Luks.open(user, password, args) { + if let Some(user) = user { + match PamFido2Luks.open( + user, + move || match password.take() { + Some(pass) => Ok(pass), + None => pamh + .conv(Some("Fido2 salt: "), PamMsgStyle::PROMPT_ECHO_OFF) + .map(|s| s.map(|s| s.to_str().unwrap()).unwrap_or("").to_string()), + }, + args, + ) { Ok(_) => PamError::SUCCESS, Err(e) => match e { //TODO: output more detailed error -- 2.49.0 From f6627d887b2b3f0f94ddcbf651bc8ab6c7fc3dc3 Mon Sep 17 00:00:00 2001 From: shimun Date: Tue, 29 Sep 2020 19:21:34 +0200 Subject: [PATCH 10/15] proper error messages --- Cargo.toml | 1 + src/error.rs | 33 ++++++++++++++++++++++++- src/lib.rs | 68 +++++++++++++++++++++++++++++++++------------------- 3 files changed, 77 insertions(+), 25 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 14d75cd..3674cf4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,6 +34,7 @@ ring = "0.13.5" failure = "0.1.5" rpassword = "4.0.1" libcryptsetup-rs = "0.4.1" +pamsm = { version = "0.4.1", features = ["libpam"] } structopt = "0.3.2" [profile.release] diff --git a/src/error.rs b/src/error.rs index 03d9741..aa7c4a1 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,10 +1,10 @@ use ctap::FidoError; use libcryptsetup_rs::LibcryptErr; +use pamsm::PamError; use std::io; use std::io::ErrorKind; use std::string::FromUtf8Error; use Fido2LuksError::*; - pub type Fido2LuksResult = Result; #[derive(Debug, Fail)] @@ -29,6 +29,10 @@ pub enum Fido2LuksError { WrongSecret, #[fail(display = "not an utf8 string")] StringEncodingError { cause: FromUtf8Error }, + #[fail(display = "elevated privileges required")] + MissingPrivileges, + #[fail(display = "{}", cause)] + Configuration { cause: ConfigurationError }, } impl Fido2LuksError { @@ -50,6 +54,20 @@ pub enum AskPassError { IO(io::Error), #[fail(display = "provided passwords don't match")] Mismatch, + #[fail(display = "unable to retrieve password: {}", _0)] + Pam(PamError), +} + +impl From for AskPassError { + fn from(e: PamError) -> Self { + AskPassError::Pam(e) + } +} + +impl From for AskPassError { + fn from(e: io::Error) -> Self { + AskPassError::IO(e) + } } #[derive(Debug, Fail)] @@ -112,3 +130,16 @@ impl From for Fido2LuksError { StringEncodingError { cause: e } } } +#[derive(Debug, Fail)] +pub enum ConfigurationError { + #[fail(display = "config is missing some values: {:?}", _0)] + Missing(Vec), + #[fail(display = "config attribute {} contains an invalid value: {}", _1, _0)] + InvalidValue(String, String), +} + +impl From for Fido2LuksError { + fn from(cause: ConfigurationError) -> Fido2LuksError { + Fido2LuksError::Configuration { cause } + } +} diff --git a/src/lib.rs b/src/lib.rs index c2f3a86..a52c3b1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,8 +3,6 @@ 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::*; @@ -14,7 +12,6 @@ use failure::_core::time::Duration; use pamsm::PamLibExt; use pamsm::*; use std::collections::{HashMap, HashSet}; -use std::ffi::CStr; use std::path::Path; use std::str::FromStr; use sudo::{self, RunningAs}; @@ -45,15 +42,19 @@ impl PamFido2Luks { }) .collect(); - let credentials = args - .get("credentials") - .map(|creds| { - >::from_str(creds) - .expect("Invalid credentials") - .0 //TODO: proper error handling - }) - .unwrap_or_default(); + let credentials = match args.get("credentials").map(|creds| { + >::from_str(creds) + .map(|cs| cs.0) + .map_err(|_| ConfigurationError::InvalidValue("credentials".into(), creds.into())) + }) { + Some(creds) => creds?, + _ => Vec::new(), + }; let pin = args.get("pin"); + let pin_prefix = args + .get("pin-prefix") + .map(|p| p.parse::().unwrap_or_default()) + .unwrap_or_default(); let device = args .get("device") .map(|device| device.replace("%user%", user.as_str())); @@ -61,7 +62,7 @@ impl PamFido2Luks { .get("name") .map(|name| name.replace("%user%", user.as_str())); - let attempts = args + let mut attempts = args .get("attempts") .and_then(|a| a.parse::().ok()) .unwrap_or(3); @@ -73,10 +74,7 @@ impl PamFido2Luks { } // root required to mount luks match sudo::check() { - RunningAs::User => { - //err - unimplemented!("no root") - } + RunningAs::User => return Err(Fido2LuksError::MissingPrivileges), _ => { sudo::escalate_if_needed().unwrap(); } @@ -100,32 +98,54 @@ impl PamFido2Luks { .collect(); let credentials: Vec<&FidoCredential> = credentials.iter().collect(); if !credentials.is_empty() { - for _ in 0..attempts { - let salt = util::sha256(&[password().expect("Password").as_bytes()]); + loop { + let (pin, pass) = if pin_prefix { + let password = password() + .map_err(|e| Fido2LuksError::AskPassError { cause: e.into() })?; + let mut parts = password.split(":"); + ( + parts.next().map(|p| p.to_string()).or(pin.cloned()), + parts.collect::>().join(":"), + ) + } else { + ( + pin.cloned(), + password() + .map_err(|e| Fido2LuksError::AskPassError { cause: e.into() })?, + ) + }; + + let salt = util::sha256(&[pass.as_bytes()]); let secret = util::sha256(&[ &salt, &perform_challenge( &credentials[..], &salt, Duration::from_secs(15), - pin.map(AsRef::as_ref), + pin.as_ref().map(String::as_str), )? .0[..], ]); - device.activate(name.as_str(), &secret[..], None)?; + match device.activate(name.as_str(), &secret[..], None) { + Ok(_) => return Ok(()), + _ if attempts > 0 => { + attempts -= 1; + continue; + } + Err(e) => break Err(e), + } } - Ok(()) } else { - unimplemented!("custom error") + Err(ConfigurationError::Missing(vec!["credentials".into()]).into()) } } else { - unimplemented!("custom error") + Ok(()) } } } impl PamServiceModule for PamFido2Luks { - fn authenticate(pamh: Pam, flag: PamFlag, args: Vec) -> PamError { + fn authenticate(pamh: Pam, _flag: PamFlag, args: Vec) -> PamError { let user = match pamh.get_cached_user() { Err(_) => return dbg!(PamError::AUTH_ERR), Ok(p) => p.map(|s| s.to_str().map(str::to_string).unwrap()), -- 2.49.0 From 11ac32d3f1e2defc017583ef9090f77af68c3e34 Mon Sep 17 00:00:00 2001 From: shimun Date: Tue, 29 Sep 2020 19:24:33 +0200 Subject: [PATCH 11/15] print error msg --- src/lib.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index a52c3b1..8fad51a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -166,10 +166,12 @@ impl PamServiceModule for PamFido2Luks { args, ) { Ok(_) => PamError::SUCCESS, - Err(e) => match e { - //TODO: output more detailed error - _ => PamError::AUTH_ERR, - }, + Err(e) => { + eprintln!("{}", e); + match e { + _ => PamError::AUTH_ERR, + } + } } } else { PamError::AUTH_ERR -- 2.49.0 From 197d9f511ccc34b65cbdb62245374c3c0a19cc25 Mon Sep 17 00:00:00 2001 From: shimun Date: Tue, 29 Sep 2020 19:31:48 +0200 Subject: [PATCH 12/15] wrap all error cases --- src/error.rs | 6 ++++++ src/lib.rs | 55 +++++++++++++++++++++++++++------------------------- 2 files changed, 35 insertions(+), 26 deletions(-) diff --git a/src/error.rs b/src/error.rs index aa7c4a1..bcaa5cd 100644 --- a/src/error.rs +++ b/src/error.rs @@ -70,6 +70,12 @@ impl From for AskPassError { } } +impl From for Fido2LuksError { + fn from(cause: AskPassError) -> Self { + Fido2LuksError::AskPassError { cause } + } +} + #[derive(Debug, Fail)] pub enum LuksError { #[fail(display = "This feature requires to the LUKS device to be formatted as LUKS 2")] diff --git a/src/lib.rs b/src/lib.rs index 8fad51a..fa1329f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -146,35 +146,38 @@ impl PamFido2Luks { impl PamServiceModule for PamFido2Luks { fn authenticate(pamh: Pam, _flag: PamFlag, args: Vec) -> PamError { - let user = match pamh.get_cached_user() { - Err(_) => return dbg!(PamError::AUTH_ERR), - Ok(p) => p.map(|s| s.to_str().map(str::to_string).unwrap()), + let perfrom_authenticate = move || -> Fido2LuksResult<()> { + let user = match pamh.get_cached_user() { + Err(e) => Err(AskPassError::Pam(e))?, + Ok(p) => p.map(|s| s.to_str().map(str::to_string).unwrap()), + }; + let mut password = match pamh.get_authtok(None) { + Err(e) => Err(AskPassError::Pam(e))?, + Ok(p) => p.map(|s| s.to_str().map(str::to_string).unwrap()), + }; + if let Some(user) = user { + PamFido2Luks.open( + user, + move || match password.take() { + Some(pass) => Ok(pass), + None => pamh + .conv(Some("Fido2 salt: "), PamMsgStyle::PROMPT_ECHO_OFF) + .map(|s| s.map(|s| s.to_str().unwrap()).unwrap_or("").to_string()), + }, + args + ) + } else { + Err(AskPassError::Pam(PamError::AUTH_ERR))? + } }; - let mut password = match pamh.get_authtok(None) { - Err(_) => return dbg!(PamError::AUTH_ERR), - Ok(p) => p.map(|s| s.to_str().map(str::to_string).unwrap()), - }; - if let Some(user) = user { - match PamFido2Luks.open( - user, - move || match password.take() { - Some(pass) => Ok(pass), - None => pamh - .conv(Some("Fido2 salt: "), PamMsgStyle::PROMPT_ECHO_OFF) - .map(|s| s.map(|s| s.to_str().unwrap()).unwrap_or("").to_string()), - }, - args, - ) { - Ok(_) => PamError::SUCCESS, - Err(e) => { - eprintln!("{}", e); - match e { - _ => PamError::AUTH_ERR, - } + match perfrom_authenticate() { + Ok(_) => PamError::SUCCESS, + Err(e) => { + eprintln!("{}", e); + match e { + _ => PamError::AUTH_ERR, } } - } else { - PamError::AUTH_ERR } } } -- 2.49.0 From 4e3d79917976def3f44baa77b6dd04a0878441d6 Mon Sep 17 00:00:00 2001 From: shimun Date: Tue, 29 Sep 2020 19:36:24 +0200 Subject: [PATCH 13/15] fix test --- src/cli_args/config.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cli_args/config.rs b/src/cli_args/config.rs index d2ef46c..cde093f 100644 --- a/src/cli_args/config.rs +++ b/src/cli_args/config.rs @@ -198,7 +198,7 @@ mod test { fn input_salt_obtain() { assert_eq!( SecretInput::String("abc".into()) - .obtain(&PasswordHelper::Stdin) + .obtain_sha256(&PasswordHelper::Stdin) .unwrap(), [ 186, 120, 22, 191, 143, 1, 207, 234, 65, 65, 64, 222, 93, 174, 34, 35, 176, 3, 97, -- 2.49.0 From 349807a6c49edaebce2a3412305aaee9cb313e73 Mon Sep 17 00:00:00 2001 From: shimun Date: Tue, 29 Sep 2020 19:41:20 +0200 Subject: [PATCH 14/15] hint pin input --- src/lib.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index fa1329f..5b443a0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -28,7 +28,7 @@ impl PamFido2Luks { fn open( &self, user: String, - mut password: impl FnMut() -> PamResult, + mut password: impl FnMut(&str) -> PamResult, args: Vec, ) -> Fido2LuksResult<()> { let args: HashMap = args @@ -100,7 +100,7 @@ impl PamFido2Luks { if !credentials.is_empty() { loop { let (pin, pass) = if pin_prefix { - let password = password() + let password = password("PIN + FIDO2 salt (pin:password):") .map_err(|e| Fido2LuksError::AskPassError { cause: e.into() })?; let mut parts = password.split(":"); ( @@ -110,7 +110,7 @@ impl PamFido2Luks { } else { ( pin.cloned(), - password() + password("FIDO2 salt: ") .map_err(|e| Fido2LuksError::AskPassError { cause: e.into() })?, ) }; @@ -158,13 +158,13 @@ impl PamServiceModule for PamFido2Luks { if let Some(user) = user { PamFido2Luks.open( user, - move || match password.take() { + move |q: &str| match password.take() { Some(pass) => Ok(pass), None => pamh - .conv(Some("Fido2 salt: "), PamMsgStyle::PROMPT_ECHO_OFF) + .conv(Some(q), PamMsgStyle::PROMPT_ECHO_OFF) .map(|s| s.map(|s| s.to_str().unwrap()).unwrap_or("").to_string()), }, - args + args, ) } else { Err(AskPassError::Pam(PamError::AUTH_ERR))? -- 2.49.0 From ddaf3f926446e7b3177d930b14251131f1026524 Mon Sep 17 00:00:00 2001 From: shimun Date: Tue, 29 Sep 2020 20:32:00 +0200 Subject: [PATCH 15/15] err --- src/lib.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 5b443a0..d476438 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -76,7 +76,7 @@ impl PamFido2Luks { match sudo::check() { RunningAs::User => return Err(Fido2LuksError::MissingPrivileges), _ => { - sudo::escalate_if_needed().unwrap(); + sudo::escalate_if_needed().map_err(|_| Fido2LuksError::MissingPrivileges)?; } } let mut device = LuksDevice::load(device)?; @@ -174,9 +174,7 @@ impl PamServiceModule for PamFido2Luks { Ok(_) => PamError::SUCCESS, Err(e) => { eprintln!("{}", e); - match e { - _ => PamError::AUTH_ERR, - } + PamError::AUTH_ERR } } } -- 2.49.0