This commit is contained in:
111
src/lib.rs
Normal file
111
src/lib.rs
Normal file
@@ -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<String>) -> Fido2LuksResult<()> {
|
||||
let args: HashMap<String, String> = args
|
||||
.into_iter()
|
||||
.filter_map(|arg| {
|
||||
let mut parts = arg.split("=");
|
||||
parts
|
||||
.by_ref()
|
||||
.next()
|
||||
.map(|key| (key.to_string(), parts.collect::<Vec<_>>().join("=")))
|
||||
})
|
||||
.collect();
|
||||
|
||||
let credentials = args
|
||||
.get("credentials")
|
||||
.map(|creds| {
|
||||
<CommaSeparated<String>>::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<String> = HashSet::new();
|
||||
if device.is_luks2()? {
|
||||
for token in device.tokens()? {
|
||||
let (_, token) = token?;
|
||||
additional_credentials.extend(token.credential.into_iter());
|
||||
}
|
||||
}
|
||||
let credentials: Vec<FidoCredential> = 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<String>) -> 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);
|
Reference in New Issue
Block a user