diff --git a/Cargo.lock b/Cargo.lock index 641166a12..cba6ec716 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -62,7 +62,7 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" dependencies = [ - "getrandom 0.2.4", + "getrandom 0.2.5", "once_cell", "version_check", ] @@ -87,9 +87,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.54" +version = "1.0.55" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a99269dff3bc004caa411f38845c20303f1e393ca2bd6581576fa3a7f59577d" +checksum = "159bb86af3a200e19a068f4224eae4c8bb2d0fa054c7e5d1cacd5cef95e684cd" [[package]] name = "arrayref" @@ -146,9 +146,9 @@ dependencies = [ [[package]] name = "async-global-executor" -version = "2.0.2" +version = "2.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9586ec52317f36de58453159d48351bc244bc24ced3effc1fce22f3d48664af6" +checksum = "c026b7e44f1316b567ee750fea85103f87fcb80792b860e979f221259796ca0a" dependencies = [ "async-channel", "async-executor", @@ -646,7 +646,7 @@ dependencies = [ [[package]] name = "concread" version = "0.3.0" -source = "git+https://github.com/kanidm/concread.git#7ab828262e05aada2330ae325d95e75f97b8dc4e" +source = "git+https://github.com/kanidm/concread.git#5681626deb73a0f2efd77effdd8cab2ebecbaca3" dependencies = [ "ahash", "crossbeam", @@ -1191,7 +1191,7 @@ checksum = "93804560e638370a8be6d59ce71ed803e55e230abdbf42598e666b41adda9b1f" dependencies = [ "base64 0.13.0", "byteorder", - "getrandom 0.2.4", + "getrandom 0.2.5", "openssl", "zeroize", ] @@ -1381,9 +1381,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "418d37c8b1d42553c93648be529cb70f920d3baf8ef469b74b9638df426e0b4c" +checksum = "d39cd93900197114fa1fcb7ae84ca742095eed9442088988ae74fa744e930e77" dependencies = [ "cfg-if 1.0.0", "js-sys", @@ -1750,9 +1750,9 @@ checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" [[package]] name = "idlset" -version = "0.2.1" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "579210ffb32d97e1dc12f65d35c1da76e4bad18fa5a4280443544cefd3413b9f" +checksum = "d1612d42a5e18df31a30916d2005ff41981c1d606b9180ef9bd1dbd919fa4eac" dependencies = [ "serde", "serde_derive", @@ -1903,10 +1903,9 @@ dependencies = [ "jemallocator", "kanidm_proto", "lazy_static", - "ldap3_server", + "ldap3_proto", "libc", "libsqlite3-sys", - "num_cpus", "num_enum", "openssl", "profiles", @@ -2075,6 +2074,17 @@ dependencies = [ "nom 2.2.1", ] +[[package]] +name = "ldap3_proto" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38792e250a5442c1af9a13c3e979796e96b494862d751a4172300b6f668d449c" +dependencies = [ + "bytes", + "lber", + "tokio-util", +] + [[package]] name = "ldap3_server" version = "0.1.11" @@ -2101,9 +2111,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.118" +version = "0.2.119" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06e509672465a0504304aa87f9f176f2b2b716ed8fb105ebe5c02dc6dce96a94" +checksum = "1bf2e165bb3457c8e098ea76f3e3bc9db55f87aa90d52d0e6be741470916aaa4" [[package]] name = "libnss" @@ -2173,9 +2183,9 @@ dependencies = [ [[package]] name = "lru" -version = "0.7.2" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "274353858935c992b13c0ca408752e2121da852d07dec7ce5f108c77dfa14d1f" +checksum = "fcb87f3080f6d1d69e8c564c0fcfde1d7aa8cc451ce40cae89479111f03bc0eb" dependencies = [ "hashbrown", ] @@ -2206,9 +2216,9 @@ checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f" [[package]] name = "mathru" -version = "0.11.1" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4eb52272ccab6f72f5b857472cc93cffe203e15e6e83eac270ccefeb9c1ec54e" +checksum = "708d5b0c8cb68b9ae6e6c9a112a97a6e4c1ed597271963473470604ff22ab595" dependencies = [ "rand 0.8.5", ] @@ -2399,7 +2409,7 @@ checksum = "80e47cfc4c0a1a519d9a025ebfbac3a2439d1b5cdf397d72dcb79b11d9920dab" dependencies = [ "base64 0.13.0", "chrono", - "getrandom 0.2.4", + "getrandom 0.2.5", "http", "rand 0.8.5", "serde", @@ -2412,9 +2422,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.9.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da32515d9f6e6e489d7bc9d84c71b060db7247dc035bbe44eac88cf87486d8d5" +checksum = "87f3e037eac156d1775da914196f0f37741a274155e34a0b7e427c35d2a2ecb9" [[package]] name = "oncemutex" @@ -2718,9 +2728,9 @@ checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872" [[package]] name = "proc-macro-crate" -version = "1.1.2" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9dada8c9981fcf32929c3c0f0cd796a9284aca335565227ed88c83babb1d43dc" +checksum = "e17d47ce914bf4de440332250b0edd23ce48c005f59fab39d3335866b114f11a" dependencies = [ "thiserror", "toml", @@ -2906,7 +2916,7 @@ version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" dependencies = [ - "getrandom 0.2.4", + "getrandom 0.2.5", ] [[package]] @@ -2945,9 +2955,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.2.10" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8383f39639269cde97d255a32bdb68c047337295414940c68bdd30c2e13203ff" +checksum = "8380fe0152551244f0747b1bf41737e0f8a74f97a14ccefd1148187271634f3c" dependencies = [ "bitflags", ] @@ -2958,7 +2968,7 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "528532f3d801c87aec9def2add9ca802fe569e44a544afe633765267840abe64" dependencies = [ - "getrandom 0.2.4", + "getrandom 0.2.5", "redox_syscall", ] @@ -3106,7 +3116,7 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" dependencies = [ - "semver 1.0.5", + "semver 1.0.6", ] [[package]] @@ -3175,7 +3185,7 @@ dependencies = [ "futures-util", "kanidm", "kanidm_proto", - "ldap3_server", + "ldap3_proto", "libc", "openssl", "profiles", @@ -3223,9 +3233,9 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.5" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0486718e92ec9a68fbed73bb5ef687d71103b142595b406835649bebd33f72c7" +checksum = "a4a3381e03edd24287172047536f20cabde766e2cd3e65e6b00fb3af51c4f38d" [[package]] name = "semver-parser" @@ -3421,9 +3431,9 @@ dependencies = [ [[package]] name = "smartstring" -version = "0.2.9" +version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31aa6a31c0c2b21327ce875f7e8952322acfcfd0c27569a6e18a647281352c9b" +checksum = "e714dff2b33f2321fdcd475b71cec79781a692d846f37f415fb395a1d2bcd48e" dependencies = [ "serde", "static_assertions", @@ -4010,7 +4020,7 @@ version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7" dependencies = [ - "getrandom 0.2.4", + "getrandom 0.2.5", "serde", ] @@ -4400,9 +4410,9 @@ dependencies = [ [[package]] name = "zeroize" -version = "1.5.2" +version = "1.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c88870063c39ee00ec285a2f8d6a966e5b6fb2becc4e8dac77ed0d370ed6006" +checksum = "50344758e2f40e3a1fcfc8f6f91aa57b5f8ebd8d27919fe6451f15aaaf9ee608" dependencies = [ "zeroize_derive", ] diff --git a/kanidmd/Cargo.toml b/kanidmd/Cargo.toml index a70bf4dc2..573f62fd6 100644 --- a/kanidmd/Cargo.toml +++ b/kanidmd/Cargo.toml @@ -62,19 +62,17 @@ time = { version = "0.2", features = ["serde", "std"] } hashbrown = { version = "0.11", features = ["serde", "inline-more", "ahash"] } concread = "^0.3" smolset = "1.3" -# concread = { version = "^0.2.9", features = ["simd_support"] } sshkeys = "^0.3.1" -# rpassword = "5.0" -num_cpus = "1.10" - zxcvbn = "2.0" base64 = "0.13" idlset = { version = "^0.2" } -ldap3_server = "0.1" +# Needs to be ldap3_proto! +ldap3_proto = "0.2.2" + webauthn-rs = "0.3" libc = "0.2" @@ -91,7 +89,6 @@ num_enum = "0.5" [features] -# simd_support = [ "concread/simd_support" ] # default = [ "libsqlite3-sys/bundled", "openssl/vendored" ] [dev-dependencies] diff --git a/kanidmd/Dockerfile b/kanidmd/Dockerfile index 88da0154d..f289c2f57 100644 --- a/kanidmd/Dockerfile +++ b/kanidmd/Dockerfile @@ -9,8 +9,7 @@ RUN zypper ar obs://devel:languages:rust devel:languages:rust && \ zypper install -y \ cargo \ rust \ - gcc \ - clang lld \ + gcc clang lld \ make automake autoconf \ libopenssl-devel pam-devel \ sqlite3-devel \ diff --git a/kanidmd/score/Cargo.toml b/kanidmd/score/Cargo.toml index 718508fd6..54d3e49a5 100644 --- a/kanidmd/score/Cargo.toml +++ b/kanidmd/score/Cargo.toml @@ -26,7 +26,7 @@ tokio = { version = "1", features = ["full"] } tokio-util = { version = "0.6", features = ["codec"] } tokio-openssl = "0.6" openssl = "0.10" -ldap3_server = "0.1" +ldap3_proto = "0.2.2" bundy = "^0.1.1" diff --git a/kanidmd/score/src/ldaps.rs b/kanidmd/score/src/ldaps.rs index 127deb929..4d281f0c3 100644 --- a/kanidmd/score/src/ldaps.rs +++ b/kanidmd/score/src/ldaps.rs @@ -7,7 +7,7 @@ use tokio_openssl::SslStream; use futures_util::sink::SinkExt; use futures_util::stream::StreamExt; -use ldap3_server::{proto::LdapMsg, LdapCodec}; +use ldap3_proto::{proto::LdapMsg, LdapCodec}; use std::marker::Unpin; use std::net; use std::str::FromStr; diff --git a/kanidmd/src/lib/actors/v1_read.rs b/kanidmd/src/lib/actors/v1_read.rs index f7ece7cf5..eb33d404d 100644 --- a/kanidmd/src/lib/actors/v1_read.rs +++ b/kanidmd/src/lib/actors/v1_read.rs @@ -36,7 +36,7 @@ use std::fs; use std::path::{Path, PathBuf}; use uuid::Uuid; -use ldap3_server::simple::*; +use ldap3_proto::simple::*; use std::convert::TryFrom; // =========================================================== diff --git a/kanidmd/src/lib/be/mod.rs b/kanidmd/src/lib/be/mod.rs index 780005225..8f4c3e6ad 100644 --- a/kanidmd/src/lib/be/mod.rs +++ b/kanidmd/src/lib/be/mod.rs @@ -44,7 +44,8 @@ use crate::be::idl_arc_sqlite::{ // Re-export this pub use crate::be::idl_sqlite::FsType; -const FILTER_SEARCH_TEST_THRESHOLD: usize = 2; +// Currently disabled due to improvements in idlset for intersection handling. +const FILTER_SEARCH_TEST_THRESHOLD: usize = 0; const FILTER_EXISTS_TEST_THRESHOLD: usize = 0; #[derive(Debug, Clone)] diff --git a/kanidmd/src/lib/config.rs b/kanidmd/src/lib/config.rs index e88b5bd84..f6f2995e4 100644 --- a/kanidmd/src/lib/config.rs +++ b/kanidmd/src/lib/config.rs @@ -135,7 +135,12 @@ impl Configuration { let mut c = Configuration { address: String::from("127.0.0.1:8080"), ldapaddress: None, - threads: num_cpus::get(), + threads: std::thread::available_parallelism() + .map(|t| t.get()) + .unwrap_or_else(|_e| { + eprintln!("WARNING: Unable to read number of available CPUs, defaulting to 1"); + 1 + }), db_path: String::from(""), db_fs_type: None, db_arc_size: None, diff --git a/kanidmd/src/lib/credential/mod.rs b/kanidmd/src/lib/credential/mod.rs index d07606407..225057984 100644 --- a/kanidmd/src/lib/credential/mod.rs +++ b/kanidmd/src/lib/credential/mod.rs @@ -755,22 +755,22 @@ impl Credential { } } - pub(crate) fn softlock_policy(&self) -> Option { + pub(crate) fn softlock_policy(&self) -> CredSoftLockPolicy { match &self.type_ { CredentialType::Password(_pw) | CredentialType::GeneratedPassword(_pw) => { - Some(CredSoftLockPolicy::Password) + CredSoftLockPolicy::Password } CredentialType::PasswordMfa(_pw, totp, wan, _) => { // For backup code, use totp/wan policy (whatever is available) if let Some(r_totp) = totp { - Some(CredSoftLockPolicy::Totp(r_totp.step)) + CredSoftLockPolicy::Totp(r_totp.step) } else if !wan.is_empty() { - Some(CredSoftLockPolicy::Webauthn) + CredSoftLockPolicy::Webauthn } else { - None + CredSoftLockPolicy::Password } } - CredentialType::Webauthn(_wan) => Some(CredSoftLockPolicy::Webauthn), + CredentialType::Webauthn(_wan) => CredSoftLockPolicy::Webauthn, } } diff --git a/kanidmd/src/lib/credential/softlock.rs b/kanidmd/src/lib/credential/softlock.rs index 4d612a04b..859a170aa 100644 --- a/kanidmd/src/lib/credential/softlock.rs +++ b/kanidmd/src/lib/credential/softlock.rs @@ -65,6 +65,7 @@ pub enum CredSoftLockPolicy { Password, Totp(u64), Webauthn, + Unrestricted, } impl CredSoftLockPolicy { @@ -111,6 +112,10 @@ impl CredSoftLockPolicy { ct + Duration::from_secs(1), ) } + CredSoftLockPolicy::Unrestricted => { + // No action needed + LockState::Init + } } } } diff --git a/kanidmd/src/lib/entry.rs b/kanidmd/src/lib/entry.rs index eecfd73a5..048996722 100644 --- a/kanidmd/src/lib/entry.rs +++ b/kanidmd/src/lib/entry.rs @@ -43,7 +43,7 @@ use crate::be::dbentry::{DbEntry, DbEntryV1, DbEntryVers}; use crate::be::{IdxKey, IdxSlope}; use hashbrown::HashMap; -use ldap3_server::simple::{LdapPartialAttribute, LdapSearchResultEntry}; +use ldap3_proto::simple::{LdapPartialAttribute, LdapSearchResultEntry}; use smartstring::alias::String as AttrString; use std::collections::BTreeMap as Map; pub use std::collections::BTreeSet as Set; diff --git a/kanidmd/src/lib/event.rs b/kanidmd/src/lib/event.rs index 0fc1a290b..f6d088ca6 100644 --- a/kanidmd/src/lib/event.rs +++ b/kanidmd/src/lib/event.rs @@ -31,7 +31,7 @@ use kanidm_proto::v1::{ SearchRequest, SearchResponse, UserAuthToken, WhoamiResponse, }; -use ldap3_server::simple::LdapFilter; +use ldap3_proto::simple::LdapFilter; use std::collections::BTreeSet; use std::time::Duration; use uuid::Uuid; diff --git a/kanidmd/src/lib/filter.rs b/kanidmd/src/lib/filter.rs index 2c205cb5f..0770a2143 100644 --- a/kanidmd/src/lib/filter.rs +++ b/kanidmd/src/lib/filter.rs @@ -17,7 +17,7 @@ use crate::value::{IndexType, PartialValue}; use concread::arcache::ARCacheReadTxn; use kanidm_proto::v1::Filter as ProtoFilter; use kanidm_proto::v1::{OperationError, SchemaError}; -use ldap3_server::proto::{LdapFilter, LdapSubstringFilter}; +use ldap3_proto::proto::{LdapFilter, LdapSubstringFilter}; // use smartstring::alias::String as AttrString; use serde::Deserialize; use std::cmp::{Ordering, PartialOrd}; @@ -1332,7 +1332,7 @@ mod tests { use std::collections::BTreeSet; use kanidm_proto::v1::Filter as ProtoFilter; - use ldap3_server::simple::LdapFilter; + use ldap3_proto::simple::LdapFilter; #[test] fn test_filter_simple() { diff --git a/kanidmd/src/lib/idm/account.rs b/kanidmd/src/lib/idm/account.rs index b099f2c04..0d3047121 100644 --- a/kanidmd/src/lib/idm/account.rs +++ b/kanidmd/src/lib/idm/account.rs @@ -238,17 +238,27 @@ impl Account { inputs } - pub fn primary_cred_uuid(&self) -> Uuid { - match &self.primary { - Some(cred) => cred.uuid, - None => UUID_ANONYMOUS, - } + pub fn primary_cred_uuid(&self) -> Option { + self.primary.as_ref().map(|cred| cred.uuid).or_else(|| { + if self.is_anonymous() { + Some(UUID_ANONYMOUS) + } else { + None + } + }) } - pub fn primary_cred_softlock_policy(&self) -> Option { + pub fn primary_cred_uuid_and_policy(&self) -> Option<(Uuid, CredSoftLockPolicy)> { self.primary .as_ref() - .and_then(|cred| cred.softlock_policy()) + .map(|cred| (cred.uuid, cred.softlock_policy())) + .or_else(|| { + if self.is_anonymous() { + Some((UUID_ANONYMOUS, CredSoftLockPolicy::Unrestricted)) + } else { + None + } + }) } pub fn is_anonymous(&self) -> bool { diff --git a/kanidmd/src/lib/idm/server.rs b/kanidmd/src/lib/idm/server.rs index 722bce8b4..cfb5a205b 100644 --- a/kanidmd/src/lib/idm/server.rs +++ b/kanidmd/src/lib/idm/server.rs @@ -66,6 +66,7 @@ use concread::{ use rand::prelude::*; use std::convert::TryFrom; use std::{sync::Arc, time::Duration}; +use tokio::sync::Mutex; use url::Url; use webauthn_rs::Webauthn; @@ -75,16 +76,17 @@ use super::event::{GenerateBackupCodeEvent, ReadBackupCodeEvent, RemoveBackupCod use tracing::trace; +type AuthSessionMutex = Arc>; +type CredSoftLockMutex = Arc>; + pub struct IdmServer { // There is a good reason to keep this single thread - it // means that limits to sessions can be easily applied and checked to // variaous accounts, and we have a good idea of how to structure the // in memory caches related to locking. session_ticket: Semaphore, - sessions: BptreeMap, - // Do we need a softlock ticket? - softlock_ticket: Semaphore, - softlocks: HashMap, + sessions: BptreeMap, + softlocks: HashMap, /// A set of in progress MFA registrations mfareg_sessions: BptreeMap, /// Reference to the query server. @@ -102,10 +104,9 @@ pub struct IdmServer { /// Contains methods that require writes, but in the context of writing to the idm in memory structures (maybe the query server too). This is things like authentication. pub struct IdmServerAuthTransaction<'a> { session_ticket: &'a Semaphore, - sessions: &'a BptreeMap, + sessions: &'a BptreeMap, + softlocks: &'a HashMap, - softlock_ticket: &'a Semaphore, - softlocks: &'a HashMap, pub qs_read: QueryServerReadTransaction<'a>, /// Thread/Server ID sid: Sid, @@ -220,7 +221,6 @@ impl IdmServer { IdmServer { session_ticket: Semaphore::new(1), sessions: BptreeMap::new(), - softlock_ticket: Semaphore::new(1), softlocks: HashMap::new(), mfareg_sessions: BptreeMap::new(), qs, @@ -245,15 +245,11 @@ impl IdmServer { let mut rng = StdRng::from_entropy(); rng.fill(&mut sid); - // let session_ticket = self.session_ticket.acquire().await; let qs_read = self.qs.read_async().await; IdmServerAuthTransaction { - // _session_ticket: session_ticket, - // sessions: self.sessions.write(), session_ticket: &self.session_ticket, sessions: &self.sessions, - softlock_ticket: &self.softlock_ticket, softlocks: &self.softlocks, qs_read, sid, @@ -526,7 +522,7 @@ impl<'a> IdmServerAuthTransaction<'a> { // // Check anything needed? Get the current auth-session-id from request // because it associates to the nonce's etc which were all cached. - let euuid = self.qs_read.name_to_uuid(init.name.as_str())?; // I CAN'T TRACE WHERE AUDITSCOPE GOES :((( + let euuid = self.qs_read.name_to_uuid(init.name.as_str())?; // Get the first / single entry we expect here .... let entry = self.qs_read.internal_search_uuid(&euuid)?; @@ -543,32 +539,54 @@ impl<'a> IdmServerAuthTransaction<'a> { // out of the session tree. let account = Account::try_from_entry_ro(entry.as_ref(), &mut self.qs_read)?; + // Intent to take both trees to write. + let _session_ticket = self.session_ticket.acquire().await; + // Check the credential that the auth_session will attempt to // use. - let is_valid = { - let cred_uuid = account.primary_cred_uuid(); - // Acquire the softlock map - let _softlock_ticket = self.softlock_ticket.acquire().await; - let mut softlock_write = self.softlocks.write(); - // Does it exist? - let r = softlock_write - .get_mut(&cred_uuid) - .map(|slock| { - // Apply the current time. - slock.apply_time_step(ct); - // Now check the results - slock.is_valid() - }) - .unwrap_or(true); - softlock_write.commit(); - r + // + // NOTE: Very careful use of await here to avoid an issue with write. + let maybe_slock_ref = + account + .primary_cred_uuid_and_policy() + .map(|(cred_uuid, policy)| { + // Acquire the softlock map + // + // We have no issue calling this with .write here, since we + // already hold the session_ticket above. + let mut softlock_write = self.softlocks.write(); + let slock_ref: CredSoftLockMutex = + if let Some(slock_ref) = softlock_write.get(&cred_uuid) { + slock_ref.clone() + } else { + // Create if not exist, and the cred type supports softlocking. + let slock = Arc::new(Mutex::new(CredSoftLock::new(policy))); + softlock_write.insert(cred_uuid, slock.clone()); + slock + }; + softlock_write.commit(); + slock_ref + }); + + let mut maybe_slock = if let Some(slock_ref) = maybe_slock_ref.as_ref() { + Some(slock_ref.lock().await) + } else { + None + }; + + // Need to as_mut here so that we hold the slock for the whole operation. + let is_valid = if let Some(slock) = maybe_slock.as_mut() { + slock.apply_time_step(ct); + slock.is_valid() + } else { + false }; let (auth_session, state) = if is_valid { AuthSession::new(account, self.webauthn, ct) } else { // it's softlocked, don't even bother. - security_info!("Account is softlocked."); + security_info!("Account is softlocked, or has no credentials associated."); ( None, AuthState::Denied("Account is temporarily locked".to_string()), @@ -577,14 +595,12 @@ impl<'a> IdmServerAuthTransaction<'a> { match auth_session { Some(auth_session) => { - // Now acquire the session tree for writing. - let _session_ticket = self.session_ticket.acquire().await; let mut session_write = self.sessions.write(); spanned!("idm::server::auth -> sessions", { if session_write.contains_key(&sessionid) { Err(OperationError::InvalidSessionState) } else { - session_write.insert(sessionid, auth_session); + session_write.insert(sessionid, Arc::new(Mutex::new(auth_session))); // Debugging: ensure we really inserted ... debug_assert!(session_write.get(&sessionid).is_some()); Ok(()) @@ -612,34 +628,38 @@ impl<'a> IdmServerAuthTransaction<'a> { } // AuthEventStep::Init AuthEventStep::Begin(mech) => { // lperf_segment!("idm::server::auth", || { - let _session_ticket = self.session_ticket.acquire().await; - let _softlock_ticket = self.softlock_ticket.acquire().await; + // let _session_ticket = self.session_ticket.acquire().await; - let mut session_write = self.sessions.write(); + let session_read = self.sessions.read(); // Do we have a session? - let auth_session = session_write + let auth_session_ref = session_read // Why is the session missing? - .get_mut(&mech.sessionid) + .get(&mech.sessionid) + .map(|auth_session_ref| auth_session_ref.clone()) .ok_or_else(|| { admin_error!("Invalid Session State (no present session uuid)"); OperationError::InvalidSessionState })?; - // From the auth_session, determine if the current account - // credential that we are using has become softlocked or not. - let mut softlock_write = self.softlocks.write(); + let mut auth_session = auth_session_ref.lock().await; - let cred_uuid = auth_session.get_account().primary_cred_uuid(); - - let is_valid = softlock_write - .get_mut(&cred_uuid) - .map(|slock| { - // Apply the current time. - slock.apply_time_step(ct); - // Now check the results - slock.is_valid() - }) - .unwrap_or(true); + let is_valid = + if let Some(cred_uuid) = auth_session.get_account().primary_cred_uuid() { + // From the auth_session, determine if the current account + // credential that we are using has become softlocked or not. + let softlock_read = self.softlocks.read(); + if let Some(slock_ref) = softlock_read.get(&cred_uuid) { + let mut slock = slock_ref.lock().await; + // Apply the current time. + slock.apply_time_step(ct); + // Now check the results + slock.is_valid() + } else { + false + } + } else { + false + }; let r = if is_valid { // Indicate to the session which auth mech we now want to proceed with. @@ -656,78 +676,88 @@ impl<'a> IdmServerAuthTransaction<'a> { delay, } }); - softlock_write.commit(); - session_write.commit(); + // softlock_write.commit(); + // session_write.commit(); r } // End AuthEventStep::Mech AuthEventStep::Cred(creds) => { // lperf_segment!("idm::server::auth", || { - let _session_ticket = self.session_ticket.acquire().await; - let _softlock_ticket = self.softlock_ticket.acquire().await; + // let _session_ticket = self.session_ticket.acquire().await; - let mut session_write = self.sessions.write(); + let session_read = self.sessions.read(); // Do we have a session? - let auth_session = session_write + let auth_session_ref = session_read // Why is the session missing? - .get_mut(&creds.sessionid) + .get(&creds.sessionid) + .map(|auth_session_ref| auth_session_ref.clone()) .ok_or_else(|| { admin_error!("Invalid Session State (no present session uuid)"); OperationError::InvalidSessionState })?; + let mut auth_session = auth_session_ref.lock().await; + + let maybe_slock_ref = + auth_session + .get_account() + .primary_cred_uuid() + .and_then(|cred_uuid| { + let softlock_read = self.softlocks.read(); + + softlock_read.get(&cred_uuid).map(|s| s.clone()) + }); + // From the auth_session, determine if the current account // credential that we are using has become softlocked or not. - let mut softlock_write = self.softlocks.write(); - let cred_uuid = auth_session.get_account().primary_cred_uuid(); + let maybe_slock = if let Some(s) = maybe_slock_ref.as_ref() { + Some(s.lock().await) + } else { + None + }; - let is_valid = softlock_write - .get_mut(&cred_uuid) - .map(|slock| { - // Apply the current time. - slock.apply_time_step(ct); - // Now check the results - slock.is_valid() - }) - .unwrap_or(true); + let maybe_valid = if let Some(mut slock) = maybe_slock { + // Apply the current time. + slock.apply_time_step(ct); + // Now check the results + if slock.is_valid() { + Some(slock) + } else { + None + } + } else { + None + }; - let r = if is_valid { - // Process the credentials here as required. - // Basically throw them at the auth_session and see what - // falls out. - let pw_badlist_cache = Some(&(*self.pw_badlist_cache)); - auth_session - .validate_creds( - &creds.cred, - &ct, - &self.async_tx, - self.webauthn, - pw_badlist_cache, - &*self.uat_bundy_hmac, - ) - .map(|aus| { - // Inspect the result: - // if it was a failure, we need to inc the softlock. - if let AuthState::Denied(_) = &aus { - if let Some(slock) = softlock_write.get_mut(&cred_uuid) { + let r = match maybe_valid { + Some(mut slock) => { + // Process the credentials here as required. + // Basically throw them at the auth_session and see what + // falls out. + let pw_badlist_cache = Some(&(*self.pw_badlist_cache)); + auth_session + .validate_creds( + &creds.cred, + &ct, + &self.async_tx, + self.webauthn, + pw_badlist_cache, + &*self.uat_bundy_hmac, + ) + .map(|aus| { + // Inspect the result: + // if it was a failure, we need to inc the softlock. + if let AuthState::Denied(_) = &aus { // Update it. slock.record_failure(ct); - } else { - // Create if not exist, and the cred type supports softlocking. - if let Some(policy) = - auth_session.get_account().primary_cred_softlock_policy() - { - let mut slock = CredSoftLock::new(policy); - slock.record_failure(ct); - softlock_write.insert(cred_uuid, slock); - } - } - }; - aus - }) - } else { - // Fail the session - auth_session.end_session("Account is temporarily locked") + }; + aus + }) + } + _ => { + // Fail the session + auth_session.end_session("Account is temporarily locked") + } } .map(|aus| { // TODO: Change this william! @@ -740,8 +770,8 @@ impl<'a> IdmServerAuthTransaction<'a> { delay, } }); - softlock_write.commit(); - session_write.commit(); + // softlock_write.commit(); + // session_write.commit(); r } // End AuthEventStep::Cred } @@ -769,45 +799,53 @@ impl<'a> IdmServerAuthTransaction<'a> { return Ok(None); } - let _softlock_ticket = self.softlock_ticket.acquire().await; - let mut softlock_write = self.softlocks.write(); + let maybe_slock_ref = match account.unix_cred_uuid_and_policy() { + Some((cred_uuid, policy)) => { + let softlock_read = self.softlocks.read(); + let slock_ref = match softlock_read.get(&cred_uuid) { + Some(slock_ref) => slock_ref.clone(), + None => { + let _session_ticket = self.session_ticket.acquire().await; + let mut softlock_write = self.softlocks.write(); + let slock = Arc::new(Mutex::new(CredSoftLock::new(policy))); + softlock_write.insert(cred_uuid, slock.clone()); + softlock_write.commit(); + slock + } + }; + Some(slock_ref) + } + None => None, + }; - let cred_uuid = account.unix_cred_uuid(); - let is_valid = if let Some(cu) = cred_uuid.as_ref() { - // Advanced and then check the softlock. - softlock_write - .get_mut(cu) - .map(|slock| { - // Apply the current time. - slock.apply_time_step(ct); - // Now check the results - slock.is_valid() - }) - // No sl, it's valid. - .unwrap_or(true) + let maybe_slock = if let Some(s) = maybe_slock_ref.as_ref() { + Some(s.lock().await) } else { - // No cred id? It'll fail in verify ... - true + None + }; + + let maybe_valid = if let Some(mut slock) = maybe_slock { + // Apply the current time. + slock.apply_time_step(ct); + // Now check the results + if slock.is_valid() { + Some(slock) + } else { + None + } + } else { + None }; // Validate the unix_pw - this checks the account/cred lock states. - let res = if is_valid { + let res = if let Some(mut slock) = maybe_valid { // Account is unlocked, can proceed. account .verify_unix_credential(uae.cleartext.as_str(), &self.async_tx, ct) .map(|res| { if res.is_none() { - if let Some(cu) = cred_uuid.as_ref() { - // Update the cred failure. - if let Some(slock) = softlock_write.get_mut(cu) { - // Update it. - slock.record_failure(ct); - } else if let Some(policy) = account.unix_cred_softlock_policy() { - let mut slock = CredSoftLock::new(policy); - slock.record_failure(ct); - softlock_write.insert(*cu, slock); - }; - } + // Update it. + slock.record_failure(ct); }; res }) @@ -816,8 +854,6 @@ impl<'a> IdmServerAuthTransaction<'a> { security_info!("Account is softlocked."); Ok(None) }; - - softlock_write.commit(); res } @@ -869,28 +905,45 @@ impl<'a> IdmServerAuthTransaction<'a> { return Ok(None); } - let _softlock_ticket = self.softlock_ticket.acquire().await; - let mut softlock_write = self.softlocks.write(); - - let cred_uuid = account.unix_cred_uuid(); - let is_valid = if let Some(cu) = cred_uuid.as_ref() { - // Advanced and then check the softlock. - softlock_write - .get_mut(cu) - .map(|slock| { - // Apply the current time. - slock.apply_time_step(ct); - // Now check the results - slock.is_valid() - }) - // No sl, it's valid. - .unwrap_or(true) - } else { - // No cred id? It'll fail in verify ... - true + let maybe_slock_ref = match account.unix_cred_uuid_and_policy() { + Some((cred_uuid, policy)) => { + let softlock_read = self.softlocks.read(); + let slock_ref = match softlock_read.get(&cred_uuid) { + Some(slock_ref) => slock_ref.clone(), + None => { + let _session_ticket = self.session_ticket.acquire().await; + let mut softlock_write = self.softlocks.write(); + let slock = Arc::new(Mutex::new(CredSoftLock::new(policy))); + softlock_write.insert(cred_uuid, slock.clone()); + softlock_write.commit(); + slock + } + }; + Some(slock_ref) + } + None => None, }; - let res = if is_valid { + let maybe_slock = if let Some(s) = maybe_slock_ref.as_ref() { + Some(s.lock().await) + } else { + None + }; + + let maybe_valid = if let Some(mut slock) = maybe_slock { + // Apply the current time. + slock.apply_time_step(ct); + // Now check the results + if slock.is_valid() { + Some(slock) + } else { + None + } + } else { + None + }; + + let res = if let Some(mut slock) = maybe_valid { if account .verify_unix_credential(lae.cleartext.as_str(), &self.async_tx, ct)? .is_some() @@ -927,17 +980,7 @@ impl<'a> IdmServerAuthTransaction<'a> { })) } else { // PW failure, update softlock. - if let Some(cu) = cred_uuid.as_ref() { - // Update the cred failure. - if let Some(slock) = softlock_write.get_mut(cu) { - // Update it. - slock.record_failure(ct); - } else if let Some(policy) = account.unix_cred_softlock_policy() { - let mut slock = CredSoftLock::new(policy); - slock.record_failure(ct); - softlock_write.insert(*cu, slock); - }; - }; + slock.record_failure(ct); Ok(None) } } else { @@ -945,8 +988,6 @@ impl<'a> IdmServerAuthTransaction<'a> { security_info!("Account is softlocked."); Ok(None) }; - - softlock_write.commit(); res } } diff --git a/kanidmd/src/lib/idm/unix.rs b/kanidmd/src/lib/idm/unix.rs index e1f889f72..7871ed792 100644 --- a/kanidmd/src/lib/idm/unix.rs +++ b/kanidmd/src/lib/idm/unix.rs @@ -166,12 +166,10 @@ impl UnixUserAccount { }) } - pub fn unix_cred_uuid(&self) -> Option { - self.cred.as_ref().map(|c| c.uuid) - } - - pub fn unix_cred_softlock_policy(&self) -> Option { - self.cred.as_ref().and_then(|cred| cred.softlock_policy()) + pub fn unix_cred_uuid_and_policy(&self) -> Option<(Uuid, CredSoftLockPolicy)> { + self.cred + .as_ref() + .map(|cred| (cred.uuid, cred.softlock_policy())) } pub fn is_anonymous(&self) -> bool { diff --git a/kanidmd/src/lib/ldap.rs b/kanidmd/src/lib/ldap.rs index f4c3cc0fe..c500fc511 100644 --- a/kanidmd/src/lib/ldap.rs +++ b/kanidmd/src/lib/ldap.rs @@ -7,7 +7,7 @@ use crate::idm::server::{IdmServer, IdmServerTransaction}; use crate::prelude::*; use async_std::task; use kanidm_proto::v1::{OperationError, UserAuthToken}; -use ldap3_server::simple::*; +use ldap3_proto::simple::*; use regex::Regex; use std::collections::BTreeSet; use std::iter; @@ -531,8 +531,8 @@ mod tests { use crate::ldap::LdapServer; use async_std::task; use hashbrown::HashSet; - use ldap3_server::proto::{LdapFilter, LdapOp, LdapSearchScope}; - use ldap3_server::simple::*; + use ldap3_proto::proto::{LdapFilter, LdapOp, LdapSearchScope}; + use ldap3_proto::simple::*; const TEST_PASSWORD: &'static str = "ntaoeuntnaoeuhraohuercahu😍"; diff --git a/profiles/RUST_MSRV b/profiles/RUST_MSRV index 79f82f6b8..bb120e876 100644 --- a/profiles/RUST_MSRV +++ b/profiles/RUST_MSRV @@ -1 +1 @@ -1.58.0 +1.59.0