diff --git a/src/api.rs b/src/api.rs index ee25cf7..fd294eb 100644 --- a/src/api.rs +++ b/src/api.rs @@ -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 // 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(()) } diff --git a/src/api/auth.rs b/src/api/auth.rs index 7fd0ea0..6c3bbc2 100644 --- a/src/api/auth.rs +++ b/src/api/auth.rs @@ -27,20 +27,46 @@ use crate::config::Settings; use crate::api::Session; pub use crate::schema::auth_capnp; +use crate::db::pass::PassDB; -pub struct AppData; +pub struct AppData { + passdb: Arc, +} pub struct SessionData; + struct CB; impl Callback for CB { - fn callback(sasl: SaslCtx, session: SaslSession, prop: Property) -> libc::c_int { + fn callback(mut sasl: SaslCtx, session: SaslSession, prop: Property) -> libc::c_int { let ret = match prop { Property::GSASL_VALIDATE_SIMPLE => { - let authid = session.get_property(Property::GSASL_AUTHID).unwrap().to_string_lossy(); - let pass = session.get_property(Property::GSASL_PASSWORD).unwrap().to_string_lossy(); + let authid = match session.get_property(Property::GSASL_AUTHID) { + 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" { - ReturnCode::GSASL_OK + 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 + } else { + ReturnCode::GSASL_AUTHENTICATION_ERROR + } + } else { + ReturnCode::GSASL_AUTHENTICATION_ERROR + } } else { ReturnCode::GSASL_AUTHENTICATION_ERROR } @@ -60,10 +86,10 @@ pub struct Auth { } impl Auth { - pub fn new(session: Arc) -> Self { + pub fn new(passdb: Arc, session: Arc) -> Self { let mut ctx = SASL::new().unwrap(); - let mut appdata = Box::new(AppData); + let mut appdata = Box::new(AppData { passdb }); ctx.store(appdata); diff --git a/src/db.rs b/src/db.rs index d5981b1..97140d5 100644 --- a/src/db.rs +++ b/src/db.rs @@ -1,25 +1,24 @@ use std::sync::Arc; +/// (Hashed) password database +pub mod pass; + +/// User storage +pub mod user; /// Access control storage /// /// Stores&Retrieves Permissions and Roles pub mod access; -/// User storage -/// -/// Stores&Retrieves Users -pub mod user; /// Machine storage /// /// Stores&Retrieves Machines pub mod machine; -/// Authenticate users -pub mod pass; - #[derive(Clone)] pub struct Databases { pub access: Arc, pub machine: Arc, + pub passdb: Arc, } diff --git a/src/db/pass.rs b/src/db/pass.rs index 2dd431f..7a7da05 100644 --- a/src/db/pass.rs +++ b/src/db/pass.rs @@ -18,7 +18,18 @@ impl PassDB { Self { log, env, db } } - pub fn check(&self, txn: &T, authcid: &str, password: &[u8]) -> Result> { + pub fn init(log: Logger, env: Arc) -> Result { + 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(&self, txn: &T, authcid: &str, password: &[u8]) -> Result> { match txn.get(self.db, &authcid.as_bytes()) { Ok(bytes) => { let encoded = unsafe { std::str::from_utf8_unchecked(bytes) }; @@ -29,8 +40,13 @@ impl PassDB { Err(e) => { Err(e.into()) }, } } + pub fn check(&self, authcid: &str, password: &[u8]) -> Result> { + 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 salt: [u8; 16] = rand::random(); let hash = argon2::hash_encoded(password, &salt, &config)?; diff --git a/src/db/user.rs b/src/db/user.rs index 2acab80..5f213d5 100644 --- a/src/db/user.rs +++ b/src/db/user.rs @@ -9,8 +9,11 @@ use std::collections::HashMap; mod internal; #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +/// An user pub struct User { + /// The precise (and unique) identifier of this user pub id: UserId, + /// Data BFFH stores on this user to base decisions on pub data: UserData, } @@ -20,7 +23,7 @@ pub struct User { /// This identity is internal to FabAccess and completely independent from the authentication /// method or source 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, /// Sub user ID. /// @@ -55,7 +58,9 @@ impl fmt::Display for UserId { } #[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 { /// A Person has N ≥ 0 roles. /// Persons are only ever given roles, not permissions directly diff --git a/src/main.rs b/src/main.rs index a73f7cd..4823d98 100644 --- a/src/main.rs +++ b/src/main.rs @@ -217,9 +217,12 @@ fn main() -> Result<(), Error> { let pdb = pdb?; let mut ac = db::access::AccessControl::new(); 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 { access: Arc::new(db::access::AccessControl::new()), 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 diff --git a/src/user.rs b/src/user.rs deleted file mode 100644 index e69de29..0000000