mirror of
https://github.com/kanidm/kanidm.git
synced 2025-02-23 20:47:01 +01:00
Restrict posix passwords on ldap bind with config
Signed-off-by: NavinShrinivas <karupal2002@gmail.com>
This commit is contained in:
parent
e02328ae8b
commit
12ea1c8702
|
@ -66,6 +66,7 @@ pub const ATTR_DN: &str = "dn";
|
||||||
pub const ATTR_DOMAIN_DISPLAY_NAME: &str = "domain_display_name";
|
pub const ATTR_DOMAIN_DISPLAY_NAME: &str = "domain_display_name";
|
||||||
pub const ATTR_DOMAIN_LDAP_BASEDN: &str = "domain_ldap_basedn";
|
pub const ATTR_DOMAIN_LDAP_BASEDN: &str = "domain_ldap_basedn";
|
||||||
pub const ATTR_DOMAIN_NAME: &str = "domain_name";
|
pub const ATTR_DOMAIN_NAME: &str = "domain_name";
|
||||||
|
pub const ATTR_DOMAIN_LDAP_ALLOW_UNIX_PW_BIND : &str = "domain_ldap_allow_unix_pw_bind";
|
||||||
pub const ATTR_DOMAIN_SSID: &str = "domain_ssid";
|
pub const ATTR_DOMAIN_SSID: &str = "domain_ssid";
|
||||||
pub const ATTR_DOMAIN_TOKEN_KEY: &str = "domain_token_key";
|
pub const ATTR_DOMAIN_TOKEN_KEY: &str = "domain_token_key";
|
||||||
pub const ATTR_DOMAIN_UUID: &str = "domain_uuid";
|
pub const ATTR_DOMAIN_UUID: &str = "domain_uuid";
|
||||||
|
|
|
@ -71,6 +71,7 @@ pub enum Attribute {
|
||||||
DomainDisplayName,
|
DomainDisplayName,
|
||||||
DomainLdapBasedn,
|
DomainLdapBasedn,
|
||||||
DomainName,
|
DomainName,
|
||||||
|
DomainLdapAllowUnixPwBind,
|
||||||
DomainSsid,
|
DomainSsid,
|
||||||
DomainTokenKey,
|
DomainTokenKey,
|
||||||
DomainUuid,
|
DomainUuid,
|
||||||
|
@ -247,6 +248,7 @@ impl TryFrom<String> for Attribute {
|
||||||
ATTR_DOMAIN_DISPLAY_NAME => Attribute::DomainDisplayName,
|
ATTR_DOMAIN_DISPLAY_NAME => Attribute::DomainDisplayName,
|
||||||
ATTR_DOMAIN_LDAP_BASEDN => Attribute::DomainLdapBasedn,
|
ATTR_DOMAIN_LDAP_BASEDN => Attribute::DomainLdapBasedn,
|
||||||
ATTR_DOMAIN_NAME => Attribute::DomainName,
|
ATTR_DOMAIN_NAME => Attribute::DomainName,
|
||||||
|
ATTR_DOMAIN_LDAP_ALLOW_UNIX_PW_BIND => Attribute::DomainLdapAllowUnixPwBind,
|
||||||
ATTR_DOMAIN_SSID => Attribute::DomainSsid,
|
ATTR_DOMAIN_SSID => Attribute::DomainSsid,
|
||||||
ATTR_DOMAIN_TOKEN_KEY => Attribute::DomainTokenKey,
|
ATTR_DOMAIN_TOKEN_KEY => Attribute::DomainTokenKey,
|
||||||
ATTR_DOMAIN_UUID => Attribute::DomainUuid,
|
ATTR_DOMAIN_UUID => Attribute::DomainUuid,
|
||||||
|
@ -397,6 +399,7 @@ impl From<Attribute> for &'static str {
|
||||||
Attribute::Dn => ATTR_DN,
|
Attribute::Dn => ATTR_DN,
|
||||||
Attribute::Domain => ATTR_DOMAIN,
|
Attribute::Domain => ATTR_DOMAIN,
|
||||||
Attribute::DomainDisplayName => ATTR_DOMAIN_DISPLAY_NAME,
|
Attribute::DomainDisplayName => ATTR_DOMAIN_DISPLAY_NAME,
|
||||||
|
Attribute::DomainLdapAllowUnixPwBind => ATTR_DOMAIN_LDAP_ALLOW_UNIX_PW_BIND,
|
||||||
Attribute::DomainLdapBasedn => ATTR_DOMAIN_LDAP_BASEDN,
|
Attribute::DomainLdapBasedn => ATTR_DOMAIN_LDAP_BASEDN,
|
||||||
Attribute::DomainName => ATTR_DOMAIN_NAME,
|
Attribute::DomainName => ATTR_DOMAIN_NAME,
|
||||||
Attribute::DomainSsid => ATTR_DOMAIN_SSID,
|
Attribute::DomainSsid => ATTR_DOMAIN_SSID,
|
||||||
|
|
|
@ -111,6 +111,15 @@ pub static ref SCHEMA_ATTR_DOMAIN_NAME: SchemaAttribute = SchemaAttribute {
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub static ref SCHEMA_ATTR_DOMAIN_LDAP_ALLOW_UNIX_PW_BIND: SchemaAttribute = SchemaAttribute {
|
||||||
|
uuid: UUID_SCHEMA_ATTR_DOMAIN_LDAP_ALLOW_UNIX_PW_BIND,
|
||||||
|
name: Attribute::DomainLdapAllowUnixPwBind.into(),
|
||||||
|
description: "Configuration to allow binds to LDAP objects using UNIX passwords.".to_string(),
|
||||||
|
unique: false,
|
||||||
|
syntax: SyntaxType::Boolean,
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
|
||||||
pub static ref SCHEMA_ATTR_DOMAIN_LDAP_BASEDN: SchemaAttribute = SchemaAttribute {
|
pub static ref SCHEMA_ATTR_DOMAIN_LDAP_BASEDN: SchemaAttribute = SchemaAttribute {
|
||||||
uuid: UUID_SCHEMA_ATTR_DOMAIN_LDAP_BASEDN,
|
uuid: UUID_SCHEMA_ATTR_DOMAIN_LDAP_BASEDN,
|
||||||
name: Attribute::DomainLdapBasedn.into(),
|
name: Attribute::DomainLdapBasedn.into(),
|
||||||
|
@ -708,7 +717,7 @@ pub static ref SCHEMA_CLASS_DOMAIN_INFO: SchemaClass = SchemaClass {
|
||||||
name: EntryClass::DomainInfo.into(),
|
name: EntryClass::DomainInfo.into(),
|
||||||
description: "Local domain information and partial configuration.to_string().".to_string(),
|
description: "Local domain information and partial configuration.to_string().".to_string(),
|
||||||
|
|
||||||
systemmay: vec![Attribute::DomainSsid.into(), Attribute::DomainLdapBasedn.into()],
|
systemmay: vec![Attribute::DomainSsid.into(), Attribute::DomainLdapBasedn.into(),Attribute::DomainLdapAllowUnixPwBind.into()],
|
||||||
systemmust: vec![
|
systemmust: vec![
|
||||||
Attribute::Name.into(),
|
Attribute::Name.into(),
|
||||||
Attribute::DomainUuid.into(),
|
Attribute::DomainUuid.into(),
|
||||||
|
|
|
@ -243,6 +243,9 @@ pub const UUID_SCHEMA_ATTR_IMAGE: Uuid = uuid!("00000000-0000-0000-0000-ffff0000
|
||||||
pub const UUID_SCHEMA_ATTR_DENIED_NAME: Uuid = uuid!("00000000-0000-0000-0000-ffff00000144");
|
pub const UUID_SCHEMA_ATTR_DENIED_NAME: Uuid = uuid!("00000000-0000-0000-0000-ffff00000144");
|
||||||
|
|
||||||
// Leave 145 for ldap unix pw bind
|
// Leave 145 for ldap unix pw bind
|
||||||
|
pub const UUID_SCHEMA_ATTR_DOMAIN_LDAP_ALLOW_UNIX_PW_BIND: Uuid =
|
||||||
|
uuid!("00000000-0000-0000-0000-ffff00000145");
|
||||||
|
|
||||||
pub const UUID_SCHEMA_CLASS_ACCOUNT_POLICY: Uuid = uuid!("00000000-0000-0000-0000-ffff00000146");
|
pub const UUID_SCHEMA_CLASS_ACCOUNT_POLICY: Uuid = uuid!("00000000-0000-0000-0000-ffff00000146");
|
||||||
|
|
||||||
// System and domain infos
|
// System and domain infos
|
||||||
|
|
|
@ -450,7 +450,7 @@ impl LdapServer {
|
||||||
idm_auth.auth_ldap(&lae, ct).await.and_then(|r| {
|
idm_auth.auth_ldap(&lae, ct).await.and_then(|r| {
|
||||||
idm_auth.commit().map(|_| {
|
idm_auth.commit().map(|_| {
|
||||||
if r.is_some() {
|
if r.is_some() {
|
||||||
security_info!(%dn, "✅ LDAP Bind success");
|
security_info!(%dn, "✅ LDAP Bind (password) success");
|
||||||
} else {
|
} else {
|
||||||
security_info!(%dn, "❌ LDAP Bind failure");
|
security_info!(%dn, "❌ LDAP Bind failure");
|
||||||
};
|
};
|
||||||
|
@ -643,13 +643,51 @@ mod tests {
|
||||||
let pce = UnixPasswordChangeEvent::new_internal(UUID_ADMIN, TEST_PASSWORD);
|
let pce = UnixPasswordChangeEvent::new_internal(UUID_ADMIN, TEST_PASSWORD);
|
||||||
|
|
||||||
assert!(idms_prox_write.set_unix_account_password(&pce).is_ok());
|
assert!(idms_prox_write.set_unix_account_password(&pce).is_ok());
|
||||||
assert!(idms_prox_write.commit().is_ok());
|
assert!(idms_prox_write.commit().is_ok()); // Committing all configs
|
||||||
|
|
||||||
|
// default UNIX_PW bind (default is set to true)
|
||||||
|
// Hence allows all unix binds
|
||||||
|
let admin_t = ldaps
|
||||||
|
.do_bind(idms, "admin", TEST_PASSWORD)
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
|
.unwrap();
|
||||||
|
assert!(admin_t.effective_session == LdapSession::UnixBind(UUID_ADMIN));
|
||||||
|
let admin_t = ldaps
|
||||||
|
.do_bind(idms, "admin@example.com", TEST_PASSWORD)
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
|
.unwrap();
|
||||||
|
assert!(admin_t.effective_session == LdapSession::UnixBind(UUID_ADMIN));
|
||||||
|
|
||||||
|
// Setting UNIX_PW_BIND flag to false:
|
||||||
|
// Hence all of the below authentication will fail (asserts are still satisfied)
|
||||||
|
let mut idms_prox_write = idms.proxy_write(duration_from_epoch_now()).await;
|
||||||
|
let disallow_unix_pw_flag = ModifyEvent::new_internal_invalid(
|
||||||
|
filter!(f_eq(Attribute::Uuid, PartialValue::Uuid(UUID_DOMAIN_INFO))),
|
||||||
|
ModifyList::new_purge_and_set(Attribute::DomainLdapAllowUnixPwBind, Value::Bool(false)),
|
||||||
|
);
|
||||||
|
assert!(idms_prox_write
|
||||||
|
.qs_write
|
||||||
|
.modify(&disallow_unix_pw_flag)
|
||||||
|
.is_ok());
|
||||||
|
assert!(idms_prox_write.commit().is_ok());
|
||||||
let anon_t = ldaps.do_bind(idms, "", "").await.unwrap().unwrap();
|
let anon_t = ldaps.do_bind(idms, "", "").await.unwrap().unwrap();
|
||||||
assert!(anon_t.effective_session == LdapSession::UnixBind(UUID_ANONYMOUS));
|
assert!(anon_t.effective_session == LdapSession::UnixBind(UUID_ANONYMOUS));
|
||||||
assert!(
|
assert!(
|
||||||
ldaps.do_bind(idms, "", "test").await.unwrap_err() == OperationError::NotAuthenticated
|
ldaps.do_bind(idms, "", "test").await.unwrap_err() == OperationError::NotAuthenticated
|
||||||
);
|
);
|
||||||
|
let admin_t = ldaps.do_bind(idms, "admin", TEST_PASSWORD).await.unwrap();
|
||||||
|
assert!(admin_t.is_none() == true);
|
||||||
|
|
||||||
|
// Setting UNIX_PW_BIND flag to true :
|
||||||
|
let mut idms_prox_write = idms.proxy_write(duration_from_epoch_now()).await;
|
||||||
|
let allow_unix_pw_flag = ModifyEvent::new_internal_invalid(
|
||||||
|
filter!(f_eq(Attribute::Uuid, PartialValue::Uuid(UUID_DOMAIN_INFO))),
|
||||||
|
ModifyList::new_purge_and_set(Attribute::DomainLdapAllowUnixPwBind, Value::Bool(true)),
|
||||||
|
);
|
||||||
|
assert!(idms_prox_write.qs_write.modify(&allow_unix_pw_flag).is_ok());
|
||||||
|
assert!(idms_prox_write.commit().is_ok());
|
||||||
|
|
||||||
// Now test the admin and various DN's
|
// Now test the admin and various DN's
|
||||||
let admin_t = ldaps
|
let admin_t = ldaps
|
||||||
|
|
|
@ -1325,6 +1325,10 @@ impl<'a> IdmServerAuthTransaction<'a> {
|
||||||
effective_session: LdapSession::UnixBind(UUID_ANONYMOUS),
|
effective_session: LdapSession::UnixBind(UUID_ANONYMOUS),
|
||||||
}))
|
}))
|
||||||
} else {
|
} else {
|
||||||
|
if !self.qs_read.d_info.d_ldap_allow_unix_pw_bind {
|
||||||
|
security_info!("Bind not allowed through Unix passwords.");
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
let account =
|
let account =
|
||||||
UnixUserAccount::try_from_entry_ro(account_entry.as_ref(), &mut self.qs_read)?;
|
UnixUserAccount::try_from_entry_ro(account_entry.as_ref(), &mut self.qs_read)?;
|
||||||
|
|
||||||
|
|
|
@ -565,6 +565,7 @@ impl<'a> QueryServerWriteTransaction<'a> {
|
||||||
SCHEMA_ATTR_DOMAIN_DISPLAY_NAME.clone().into(),
|
SCHEMA_ATTR_DOMAIN_DISPLAY_NAME.clone().into(),
|
||||||
SCHEMA_ATTR_DOMAIN_LDAP_BASEDN.clone().into(),
|
SCHEMA_ATTR_DOMAIN_LDAP_BASEDN.clone().into(),
|
||||||
SCHEMA_ATTR_DOMAIN_NAME.clone().into(),
|
SCHEMA_ATTR_DOMAIN_NAME.clone().into(),
|
||||||
|
SCHEMA_ATTR_DOMAIN_LDAP_ALLOW_UNIX_PW_BIND.clone().into(),
|
||||||
SCHEMA_ATTR_DOMAIN_SSID.clone().into(),
|
SCHEMA_ATTR_DOMAIN_SSID.clone().into(),
|
||||||
SCHEMA_ATTR_DOMAIN_TOKEN_KEY.clone().into(),
|
SCHEMA_ATTR_DOMAIN_TOKEN_KEY.clone().into(),
|
||||||
SCHEMA_ATTR_DOMAIN_UUID.clone().into(),
|
SCHEMA_ATTR_DOMAIN_UUID.clone().into(),
|
||||||
|
|
|
@ -65,6 +65,7 @@ pub struct DomainInfo {
|
||||||
pub(crate) d_name: String,
|
pub(crate) d_name: String,
|
||||||
pub(crate) d_display: String,
|
pub(crate) d_display: String,
|
||||||
pub(crate) d_vers: DomainVersion,
|
pub(crate) d_vers: DomainVersion,
|
||||||
|
pub(crate) d_ldap_allow_unix_pw_bind: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Default)]
|
#[derive(Debug, Clone, PartialEq, Eq, Default)]
|
||||||
|
@ -813,7 +814,13 @@ pub trait QueryServerTransaction<'a> {
|
||||||
e
|
e
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
fn get_domain_ldap_allow_unix_pw_bind(&mut self) -> Result<bool, OperationError> {
|
||||||
|
self.internal_search_uuid(UUID_DOMAIN_INFO).map(|entry| {
|
||||||
|
entry
|
||||||
|
.get_ava_single_bool(Attribute::DomainLdapAllowUnixPwBind)
|
||||||
|
.unwrap_or(true)
|
||||||
|
})
|
||||||
|
}
|
||||||
fn get_domain_cookie_key(&mut self) -> Result<[u8; 64], OperationError> {
|
fn get_domain_cookie_key(&mut self) -> Result<[u8; 64], OperationError> {
|
||||||
self.internal_search_uuid(UUID_DOMAIN_INFO)
|
self.internal_search_uuid(UUID_DOMAIN_INFO)
|
||||||
.and_then(|e| {
|
.and_then(|e| {
|
||||||
|
@ -1130,6 +1137,7 @@ impl QueryServer {
|
||||||
// we set the domain_display_name to the configuration file's domain_name
|
// we set the domain_display_name to the configuration file's domain_name
|
||||||
// here because the database is not started, so we cannot pull it from there.
|
// here because the database is not started, so we cannot pull it from there.
|
||||||
d_display: domain_name,
|
d_display: domain_name,
|
||||||
|
d_ldap_allow_unix_pw_bind: false,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// These default to empty, but they'll be populated shortly.
|
// These default to empty, but they'll be populated shortly.
|
||||||
|
@ -1563,8 +1571,16 @@ impl<'a> QueryServerWriteTransaction<'a> {
|
||||||
pub(crate) fn reload_domain_info(&mut self) -> Result<(), OperationError> {
|
pub(crate) fn reload_domain_info(&mut self) -> Result<(), OperationError> {
|
||||||
let domain_name = self.get_db_domain_name()?;
|
let domain_name = self.get_db_domain_name()?;
|
||||||
let display_name = self.get_db_domain_display_name()?;
|
let display_name = self.get_db_domain_display_name()?;
|
||||||
|
let domain_ldap_allow_unix_pw_bind = match self.get_domain_ldap_allow_unix_pw_bind() {
|
||||||
|
Ok(v) => v,
|
||||||
|
_ => {
|
||||||
|
admin_warn!("Defaulting ldap_allow_unix_pw_bind to true");
|
||||||
|
true
|
||||||
|
}
|
||||||
|
};
|
||||||
let domain_uuid = self.be_txn.get_db_d_uuid()?;
|
let domain_uuid = self.be_txn.get_db_d_uuid()?;
|
||||||
let mut_d_info = self.d_info.get_mut();
|
let mut_d_info = self.d_info.get_mut();
|
||||||
|
mut_d_info.d_ldap_allow_unix_pw_bind = domain_ldap_allow_unix_pw_bind;
|
||||||
if mut_d_info.d_uuid != domain_uuid {
|
if mut_d_info.d_uuid != domain_uuid {
|
||||||
admin_warn!(
|
admin_warn!(
|
||||||
"Using domain uuid from the database {} - was {} in memory",
|
"Using domain uuid from the database {} - was {} in memory",
|
||||||
|
|
Loading…
Reference in a new issue