106 auth concurrency (#643)

This commit is contained in:
Firstyear 2022-03-07 09:22:35 +10:00 committed by GitHub
parent f252d91e13
commit fa610c6d88
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
18 changed files with 315 additions and 249 deletions

84
Cargo.lock generated
View file

@ -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",
]

View file

@ -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]

View file

@ -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 \

View file

@ -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"

View file

@ -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;

View file

@ -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;
// ===========================================================

View file

@ -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)]

View file

@ -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,

View file

@ -755,22 +755,22 @@ impl Credential {
}
}
pub(crate) fn softlock_policy(&self) -> Option<CredSoftLockPolicy> {
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,
}
}

View file

@ -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
}
}
}
}

View file

@ -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;

View file

@ -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;

View file

@ -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() {

View file

@ -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<Uuid> {
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<CredSoftLockPolicy> {
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 {

View file

@ -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<Mutex<AuthSession>>;
type CredSoftLockMutex = Arc<Mutex<CredSoftLock>>;
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<Uuid, AuthSession>,
// Do we need a softlock ticket?
softlock_ticket: Semaphore,
softlocks: HashMap<Uuid, CredSoftLock>,
sessions: BptreeMap<Uuid, AuthSessionMutex>,
softlocks: HashMap<Uuid, CredSoftLockMutex>,
/// A set of in progress MFA registrations
mfareg_sessions: BptreeMap<Uuid, MfaRegSession>,
/// 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<Uuid, AuthSession>,
sessions: &'a BptreeMap<Uuid, AuthSessionMutex>,
softlocks: &'a HashMap<Uuid, CredSoftLockMutex>,
softlock_ticket: &'a Semaphore,
softlocks: &'a HashMap<Uuid, CredSoftLock>,
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();
//
// 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
let _softlock_ticket = self.softlock_ticket.acquire().await;
//
// We have no issue calling this with .write here, since we
// already hold the session_ticket above.
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);
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();
r
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<Init> -> 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<Begin>", || {
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
})?;
let mut auth_session = auth_session_ref.lock().await;
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 mut softlock_write = self.softlocks.write();
let cred_uuid = auth_session.get_account().primary_cred_uuid();
let is_valid = softlock_write
.get_mut(&cred_uuid)
.map(|slock| {
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()
})
.unwrap_or(true);
} else {
false
}
} else {
false
};
let r = if is_valid {
// Indicate to the session which auth mech we now want to proceed with.
@ -656,42 +676,61 @@ 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<Creds>", || {
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| {
let maybe_valid = if let Some(mut slock) = maybe_slock {
// Apply the current time.
slock.apply_time_step(ct);
// Now check the results
slock.is_valid()
})
.unwrap_or(true);
if slock.is_valid() {
Some(slock)
} else {
None
}
} else {
None
};
let r = if is_valid {
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.
@ -709,26 +748,17 @@ impl<'a> IdmServerAuthTransaction<'a> {
// 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) {
// 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")
}
}
.map(|aus| {
// TODO: Change this william!
// For now ...
@ -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 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| {
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
slock.is_valid()
})
// No sl, it's valid.
.unwrap_or(true)
if slock.is_valid() {
Some(slock)
} else {
// No cred id? It'll fail in verify ...
true
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);
};
}
};
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 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| {
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
slock.is_valid()
})
// No sl, it's valid.
.unwrap_or(true)
if slock.is_valid() {
Some(slock)
} else {
// No cred id? It'll fail in verify ...
true
None
}
} else {
None
};
let res = if is_valid {
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);
};
};
Ok(None)
}
} else {
@ -945,8 +988,6 @@ impl<'a> IdmServerAuthTransaction<'a> {
security_info!("Account is softlocked.");
Ok(None)
};
softlock_write.commit();
res
}
}

View file

@ -166,12 +166,10 @@ impl UnixUserAccount {
})
}
pub fn unix_cred_uuid(&self) -> Option<Uuid> {
self.cred.as_ref().map(|c| c.uuid)
}
pub fn unix_cred_softlock_policy(&self) -> Option<CredSoftLockPolicy> {
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 {

View file

@ -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😍";

View file

@ -1 +1 @@
1.58.0
1.59.0