Password DB and other shenanigans

This commit is contained in:
Gregor Reitzenstein 2020-11-30 07:23:47 +01:00
parent 7956616891
commit 7e9002aa94
7 changed files with 69 additions and 20 deletions

View File

@ -39,7 +39,7 @@ impl connection_capnp::bootstrap::Server for Bootstrap {
// TODO: When should we allow multiple auth and how do me make sure that does not leak // TODO: When should we allow multiple auth and how do me make sure that does not leak
// priviledges (e.g. due to previously issues caps)? // priviledges (e.g. due to previously issues caps)?
res.get().set_auth(capnp_rpc::new_client(auth::Auth::new(self.session.clone()))); res.get().set_auth(capnp_rpc::new_client(auth::Auth::new(self.db.passdb.clone(), self.session.clone())));
Promise::ok(()) Promise::ok(())
} }

View File

@ -27,23 +27,49 @@ use crate::config::Settings;
use crate::api::Session; use crate::api::Session;
pub use crate::schema::auth_capnp; pub use crate::schema::auth_capnp;
use crate::db::pass::PassDB;
pub struct AppData; pub struct AppData {
passdb: Arc<PassDB>,
}
pub struct SessionData; pub struct SessionData;
struct CB; struct CB;
impl Callback<AppData, SessionData> for CB { impl Callback<AppData, SessionData> for CB {
fn callback(sasl: SaslCtx<AppData, SessionData>, session: SaslSession<SessionData>, prop: Property) -> libc::c_int { fn callback(mut sasl: SaslCtx<AppData, SessionData>, session: SaslSession<SessionData>, prop: Property) -> libc::c_int {
let ret = match prop { let ret = match prop {
Property::GSASL_VALIDATE_SIMPLE => { Property::GSASL_VALIDATE_SIMPLE => {
let authid = session.get_property(Property::GSASL_AUTHID).unwrap().to_string_lossy(); let authid = match session.get_property(Property::GSASL_AUTHID) {
let pass = session.get_property(Property::GSASL_PASSWORD).unwrap().to_string_lossy(); None => return ReturnCode::GSASL_NO_AUTHID as libc::c_int,
Some(a) => {
match a.to_str() {
Ok(s) => s,
Err(e) => return ReturnCode::GSASL_SASLPREP_ERROR as libc::c_int,
}
},
};
if authid == "test" && pass == "secret" { let pass = session.get_property(Property::GSASL_PASSWORD);
if pass.is_none() {
return ReturnCode::GSASL_NO_PASSWORD as libc::c_int;
}
let pass = pass.unwrap();
if let Some(sessiondata) = sasl.retrieve_mut() {
if let Ok(Some(b)) = sessiondata.passdb.check(authid, pass.to_bytes()) {
if b {
ReturnCode::GSASL_OK ReturnCode::GSASL_OK
} else { } else {
ReturnCode::GSASL_AUTHENTICATION_ERROR ReturnCode::GSASL_AUTHENTICATION_ERROR
} }
} else {
ReturnCode::GSASL_AUTHENTICATION_ERROR
}
} else {
ReturnCode::GSASL_AUTHENTICATION_ERROR
}
} }
p => { p => {
println!("Callback called with property {:?}", p); println!("Callback called with property {:?}", p);
@ -60,10 +86,10 @@ pub struct Auth {
} }
impl Auth { impl Auth {
pub fn new(session: Arc<Session>) -> Self { pub fn new(passdb: Arc<PassDB>, session: Arc<Session>) -> Self {
let mut ctx = SASL::new().unwrap(); let mut ctx = SASL::new().unwrap();
let mut appdata = Box::new(AppData); let mut appdata = Box::new(AppData { passdb });
ctx.store(appdata); ctx.store(appdata);

View File

@ -1,25 +1,24 @@
use std::sync::Arc; use std::sync::Arc;
/// (Hashed) password database
pub mod pass;
/// User storage
pub mod user;
/// Access control storage /// Access control storage
/// ///
/// Stores&Retrieves Permissions and Roles /// Stores&Retrieves Permissions and Roles
pub mod access; pub mod access;
/// User storage
///
/// Stores&Retrieves Users
pub mod user;
/// Machine storage /// Machine storage
/// ///
/// Stores&Retrieves Machines /// Stores&Retrieves Machines
pub mod machine; pub mod machine;
/// Authenticate users
pub mod pass;
#[derive(Clone)] #[derive(Clone)]
pub struct Databases { pub struct Databases {
pub access: Arc<access::AccessControl>, pub access: Arc<access::AccessControl>,
pub machine: Arc<machine::MachineDB>, pub machine: Arc<machine::MachineDB>,
pub passdb: Arc<pass::PassDB>,
} }

View File

@ -18,7 +18,18 @@ impl PassDB {
Self { log, env, db } Self { log, env, db }
} }
pub fn check<T: Transaction>(&self, txn: &T, authcid: &str, password: &[u8]) -> Result<Option<bool>> { pub fn init(log: Logger, env: Arc<Environment>) -> Result<Self> {
let mut flags = lmdb::DatabaseFlags::empty();
flags.set(lmdb::DatabaseFlags::INTEGER_KEY, true);
let db = env.create_db(Some("pass"), flags)?;
Ok(Self::new(log, env, db))
}
/// Check a password for a given authcid.
///
/// `Ok(None)` means the given authcid is not stored in the database
pub fn check_with_txn<T: Transaction>(&self, txn: &T, authcid: &str, password: &[u8]) -> Result<Option<bool>> {
match txn.get(self.db, &authcid.as_bytes()) { match txn.get(self.db, &authcid.as_bytes()) {
Ok(bytes) => { Ok(bytes) => {
let encoded = unsafe { std::str::from_utf8_unchecked(bytes) }; let encoded = unsafe { std::str::from_utf8_unchecked(bytes) };
@ -29,8 +40,13 @@ impl PassDB {
Err(e) => { Err(e.into()) }, Err(e) => { Err(e.into()) },
} }
} }
pub fn check(&self, authcid: &str, password: &[u8]) -> Result<Option<bool>> {
let txn = self.env.begin_ro_txn()?;
self.check_with_txn(&txn, authcid, password)
}
pub fn store(&self, txn: &mut RwTransaction, authcid: &str, password: &[u8]) -> Result<()> { /// Store a password for a given authcid, potentially overwriting an existing password
pub fn store_with_txn(&self, txn: &mut RwTransaction, authcid: &str, password: &[u8]) -> Result<()> {
let config = argon2::Config::default(); let config = argon2::Config::default();
let salt: [u8; 16] = rand::random(); let salt: [u8; 16] = rand::random();
let hash = argon2::hash_encoded(password, &salt, &config)?; let hash = argon2::hash_encoded(password, &salt, &config)?;

View File

@ -9,8 +9,11 @@ use std::collections::HashMap;
mod internal; mod internal;
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
/// An user
pub struct User { pub struct User {
/// The precise (and unique) identifier of this user
pub id: UserId, pub id: UserId,
/// Data BFFH stores on this user to base decisions on
pub data: UserData, pub data: UserData,
} }
@ -20,7 +23,7 @@ pub struct User {
/// This identity is internal to FabAccess and completely independent from the authentication /// This identity is internal to FabAccess and completely independent from the authentication
/// method or source /// method or source
pub struct UserId { pub struct UserId {
/// Main User ID. Generally an user name or similar /// Main User ID. Generally an user name or similar. Locally unique
uid: String, uid: String,
/// Sub user ID. /// Sub user ID.
/// ///
@ -55,7 +58,9 @@ impl fmt::Display for UserId {
} }
#[derive(PartialEq, Eq, Debug, Clone, Serialize, Deserialize)] #[derive(PartialEq, Eq, Debug, Clone, Serialize, Deserialize)]
/// A Person, from the Authorization perspective /// Data on an user to base decisions on
///
/// This of course includes authorization data, i.e. that users set roles
pub struct UserData { pub struct UserData {
/// A Person has N ≥ 0 roles. /// A Person has N ≥ 0 roles.
/// Persons are only ever given roles, not permissions directly /// Persons are only ever given roles, not permissions directly

View File

@ -217,9 +217,12 @@ fn main() -> Result<(), Error> {
let pdb = pdb?; let pdb = pdb?;
let mut ac = db::access::AccessControl::new(); let mut ac = db::access::AccessControl::new();
ac.add_source_unchecked("Internal".to_string(), Box::new(pdb)); ac.add_source_unchecked("Internal".to_string(), Box::new(pdb));
let passdb = db::pass::PassDB::init(log.new(o!("system" => "passwords")), env.clone()).unwrap();
let db = db::Databases { let db = db::Databases {
access: Arc::new(db::access::AccessControl::new()), access: Arc::new(db::access::AccessControl::new()),
machine: Arc::new(machdb), machine: Arc::new(machdb),
passdb: Arc::new(passdb),
}; };
// Since the below closures will happen at a much later time we need to make sure all pointers // Since the below closures will happen at a much later time we need to make sure all pointers

View File