From 1d8582f9374db917cd79de61a38ee688367d9b5b Mon Sep 17 00:00:00 2001 From: Valentin Tolmer Date: Mon, 30 May 2022 17:59:02 +0200 Subject: [PATCH] ldap: lowercase all DN, fields, values --- server/src/infra/ldap_handler.rs | 76 ++++++++++++++++++-------------- 1 file changed, 44 insertions(+), 32 deletions(-) diff --git a/server/src/infra/ldap_handler.rs b/server/src/infra/ldap_handler.rs index 9daed83..99b675b 100644 --- a/server/src/infra/ldap_handler.rs +++ b/server/src/infra/ldap_handler.rs @@ -39,6 +39,7 @@ where } fn parse_distinguished_name(dn: &str) -> Result> { + assert!(dn == dn.to_ascii_lowercase()); dn.split(',') .map(|s| make_dn_pair(s.split('=').map(str::trim).map(String::from))) .collect() @@ -102,7 +103,7 @@ fn get_user_id_from_distinguished_name( } fn get_user_attribute(user: &User, attribute: &str, dn: &str) -> Result>> { - Ok(Some(match attribute.to_lowercase().as_str() { + Ok(Some(match attribute.to_ascii_lowercase().as_str() { "objectclass" => vec![ "inetOrgPerson".to_string(), "posixAccount".to_string(), @@ -191,7 +192,7 @@ fn get_group_attribute( attribute: &str, user_filter: &Option<&UserId>, ) -> Result>> { - Ok(Some(match attribute.to_lowercase().as_str() { + Ok(Some(match attribute.to_ascii_lowercase().as_str() { "objectclass" => vec!["groupOfUniqueNames".to_string()], "dn" | "distinguishedname" => vec![format!( "cn={},ou=groups,{}", @@ -250,6 +251,14 @@ fn make_ldap_search_group_result_entry( } fn is_subtree(subtree: &[(String, String)], base_tree: &[(String, String)]) -> bool { + for (k, v) in subtree { + assert!(k == &k.to_ascii_lowercase()); + assert!(v == &v.to_ascii_lowercase()); + } + for (k, v) in base_tree { + assert!(k == &k.to_ascii_lowercase()); + assert!(v == &v.to_ascii_lowercase()); + } if subtree.len() < base_tree.len() { return false; } @@ -263,22 +272,20 @@ fn is_subtree(subtree: &[(String, String)], base_tree: &[(String, String)]) -> b } fn map_field(field: &str) -> Result { + assert!(field == field.to_ascii_lowercase()); Ok(if field == "uid" { "user_id".to_string() } else if field == "mail" { "email".to_string() - } else if field == "cn" || field.to_lowercase() == "displayname" { + } else if field == "cn" || field == "displayname" { "display_name".to_string() - } else if field.to_lowercase() == "givenname" { + } else if field == "givenname" { "first_name".to_string() } else if field == "sn" { "last_name".to_string() } else if field == "avatar" { "avatar".to_string() - } else if field.to_lowercase() == "creationdate" - || field.to_lowercase() == "createtimestamp" - || field.to_lowercase() == "modifytimestamp" - { + } else if field == "creationdate" || field == "createtimestamp" || field == "modifytimestamp" { "creation_date".to_string() } else { bail!("Unknown field: {}", field); @@ -357,7 +364,8 @@ pub struct LdapHandler { } impl LdapHandler { - pub fn new(backend_handler: Backend, ldap_base_dn: String) -> Self { + pub fn new(backend_handler: Backend, mut ldap_base_dn: String) -> Self { + ldap_base_dn.make_ascii_lowercase(); Self { user_info: None, backend_handler, @@ -374,7 +382,7 @@ impl LdapHandler (LdapResultCode, String) { debug!(r#"Received bind request for "{}""#, &request.dn); let user_id = match get_user_id_from_distinguished_name( - &request.dn, + &request.dn.to_ascii_lowercase(), &self.base_dn, &self.base_dn_str, ) { @@ -514,7 +522,7 @@ impl LdapHandler dn, Err(_) => { return vec![make_search_error( @@ -685,15 +693,17 @@ impl LdapHandler Result { match filter { LdapFilter::Equality(field, value) => { - if field == "member" || field.to_lowercase() == "uniquemember" { + let field = &field.to_ascii_lowercase(); + let value = &value.to_ascii_lowercase(); + if field == "member" || field == "uniquemember" { let user_name = get_user_id_from_distinguished_name( value, &self.base_dn, &self.base_dn_str, )?; Ok(GroupRequestFilter::Member(user_name)) - } else if field.to_lowercase() == "objectclass" { - if value == "groupOfUniqueNames" || value == "groupOfNames" { + } else if field == "objectclass" { + if value == "groupofuniquenames" || value == "groupofnames" { Ok(GroupRequestFilter::And(vec![])) } else { Ok(GroupRequestFilter::Not(Box::new(GroupRequestFilter::And( @@ -725,7 +735,7 @@ impl LdapHandler { - if ALL_GROUP_ATTRIBUTE_KEYS.contains(&field.to_lowercase().as_str()) { + if ALL_GROUP_ATTRIBUTE_KEYS.contains(&field.to_ascii_lowercase().as_str()) { Ok(GroupRequestFilter::And(vec![])) } else { Ok(GroupRequestFilter::Not(Box::new(GroupRequestFilter::And( @@ -755,14 +765,15 @@ impl LdapHandler { - if field.to_lowercase() == "memberof" { + let field = &field.to_ascii_lowercase(); + if field == "memberof" { let group_name = get_group_id_from_distinguished_name( - value, + &value.to_ascii_lowercase(), &self.base_dn, &self.base_dn_str, )?; Ok(UserRequestFilter::MemberOf(group_name)) - } else if field.to_lowercase() == "objectclass" { + } else if field == "objectclass" { if value == "person" || value == "inetOrgPerson" || value == "posixAccount" @@ -784,8 +795,9 @@ impl LdapHandler { + let field = &field.to_ascii_lowercase(); // Check that it's a field we support. - if field.to_lowercase() == "objectclass" || map_field(field).is_ok() { + if field == "objectclass" || map_field(field).is_ok() { Ok(UserRequestFilter::And(vec![])) } else { Ok(UserRequestFilter::Not(Box::new(UserRequestFilter::And( @@ -872,7 +884,7 @@ mod tests { filter: LdapFilter, attrs: Vec, ) -> LdapSearchRequest { - make_search_request::("ou=people,dc=example,dc=com", filter, attrs) + make_search_request::("ou=people,Dc=example,dc=com", filter, attrs) } async fn setup_bound_handler( @@ -891,9 +903,9 @@ mod tests { set.insert(GroupIdAndName(GroupId(42), "lldap_admin".to_string())); Ok(set) }); - let mut ldap_handler = LdapHandler::new(mock, "dc=example,dc=com".to_string()); + let mut ldap_handler = LdapHandler::new(mock, "dc=Example,dc=com".to_string()); let request = LdapBindRequest { - dn: "uid=test,ou=people,dc=example,dc=com".to_string(), + dn: "uid=test,ou=people,dc=example,dc=coM".to_string(), cred: LdapBindCred::Simple("pass".to_string()), }; assert_eq!( @@ -916,7 +928,7 @@ mod tests { mock.expect_get_user_groups() .with(eq(UserId::new("bob"))) .return_once(|_| Ok(HashSet::new())); - let mut ldap_handler = LdapHandler::new(mock, "dc=example,dc=com".to_string()); + let mut ldap_handler = LdapHandler::new(mock, "dc=eXample,dc=com".to_string()); let request = LdapOp::BindRequest(LdapBindRequest { dn: "uid=bob,ou=people,dc=example,dc=com".to_string(), @@ -1240,14 +1252,14 @@ mod tests { }, Group { id: GroupId(3), - display_name: "bestgroup".to_string(), + display_name: "BestGroup".to_string(), users: vec![UserId::new("john")], }, ]) }); let mut ldap_handler = setup_bound_handler(mock).await; let request = make_search_request( - "ou=groups,dc=example,dc=com", + "ou=groups,dc=example,dc=cOm", LdapFilter::And(vec![]), vec!["objectClass", "dn", "cn", "uniqueMember"], ); @@ -1279,7 +1291,7 @@ mod tests { ], }), LdapOp::SearchResultEntry(LdapSearchResultEntry { - dn: "cn=bestgroup,ou=groups,dc=example,dc=com".to_string(), + dn: "cn=BestGroup,ou=groups,dc=example,dc=com".to_string(), attributes: vec![ LdapPartialAttribute { atype: "objectClass".to_string(), @@ -1287,11 +1299,11 @@ mod tests { }, LdapPartialAttribute { atype: "dn".to_string(), - vals: vec!["cn=bestgroup,ou=groups,dc=example,dc=com".to_string()] + vals: vec!["cn=BestGroup,ou=groups,dc=example,dc=com".to_string()] }, LdapPartialAttribute { atype: "cn".to_string(), - vals: vec!["bestgroup".to_string()] + vals: vec!["BestGroup".to_string()] }, LdapPartialAttribute { atype: "uniqueMember".to_string(), @@ -1331,17 +1343,17 @@ mod tests { let request = make_search_request( "ou=groups,dc=example,dc=com", LdapFilter::And(vec![ - LdapFilter::Equality("cn".to_string(), "group_1".to_string()), + LdapFilter::Equality("cN".to_string(), "Group_1".to_string()), LdapFilter::Equality( "uniqueMember".to_string(), - "uid=bob,ou=people,dc=example,dc=com".to_string(), + "uid=bob,ou=peopLe,Dc=eXample,dc=com".to_string(), ), - LdapFilter::Equality("objectclass".to_string(), "groupOfUniqueNames".to_string()), + LdapFilter::Equality("obJEctclass".to_string(), "groupOfUniqueNames".to_string()), LdapFilter::Equality("objectclass".to_string(), "groupOfNames".to_string()), LdapFilter::Present("objectclass".to_string()), LdapFilter::Present("dn".to_string()), LdapFilter::Not(Box::new(LdapFilter::Present( - "random_attribute".to_string(), + "random_attribUte".to_string(), ))), ]), vec!["1.1"],