Skip to content

Commit d569dd5

Browse files
LDAP Authentication - Fail login attempts when wildcards are used (demisto#38256)
* Fail login when wildcards are used * Fail login when wildcards are used for AD users * Added unit tests * pre commit * Update Packs/OpenLDAP/ReleaseNotes/2_0_16.md Co-authored-by: yuvalbenshalom <[email protected]> --------- Co-authored-by: yuvalbenshalom <[email protected]>
1 parent d0d8b2e commit d569dd5

File tree

4 files changed

+114
-23
lines changed

4 files changed

+114
-23
lines changed

Packs/OpenLDAP/Integrations/OpenLDAP/OpenLDAP.py

+65-12
Original file line numberDiff line numberDiff line change
@@ -492,18 +492,34 @@ def _fetch_specific_groups(self, specific_groups: str) -> dict:
492492
}
493493

494494
@staticmethod
495-
def _get_ad_username(username: str) -> str:
495+
def _get_ad_username(logon_name: str) -> str:
496496
"""
497-
Gets a user logon name (the username that is used for log in to XSOAR)
497+
Gets a User logon name (the username that is used for log in to XSOAR)
498498
and returns the Active Directory username.
499499
"""
500-
x_username = username
501-
if '\\' in username:
502-
x_username = username.split('\\')[1]
503-
elif '@' in username:
504-
x_username = username.split('@')[0]
500+
ad_username = logon_name
501+
if '\\' in logon_name:
502+
ad_username = logon_name.split('\\')[1]
503+
elif '@' in logon_name:
504+
ad_username = logon_name.split('@')[0]
505505

506-
return x_username
506+
return ad_username
507+
508+
@staticmethod
509+
def _has_wildcards_in_ad_logon(logon_name: str):
510+
"""
511+
Gets a User logon name (the username that is used for log in to XSOAR) and checks if it includes wildcards.
512+
raises exception if wildcards are found in the logon name.
513+
(Wildcards are illegal characters for active directory logon names).
514+
"""
515+
err_msg = f"Wildcards were detected in the user logon name - Input Username: {logon_name}."\
516+
f" Wildcards are not permitted for user authentication purposes."
517+
518+
wildcards = ['*', '?']
519+
for wildcard in wildcards:
520+
if wildcard in logon_name:
521+
demisto.debug(f"LDAP Authentication - User login attempt failed - {err_msg}")
522+
raise Exception(f"LDAP Authentication - Authentication failed - {err_msg}")
507523

508524
def _get_auto_bind_value(self) -> str:
509525
"""
@@ -637,6 +653,34 @@ def get_user_groups(self, user_identifier: str):
637653
paged_size=self._page_size)
638654
return LdapClient._parse_ldap_users_groups_entries(ldap_group_entries)
639655

656+
def validate_exact_username_match(self, username: str, user_dn: str):
657+
"""
658+
Validates that the given username is an exact match of the username appeared in the DN returned from the ldap search.
659+
Raises exception if the username does not exactly match the username in the DN.
660+
Used in authentication commands ('ad-authenticate', 'ad-authenticate-and-roles').
661+
662+
Background:
663+
LDAP servers support the use of wildcards primarily through the '*' and the '?' symbols for searching entries in the
664+
directory. In addition, '*' and '?' are valid characters for OpenLdap usernames.
665+
666+
When the username provided by the user differs from the one retrieved from the DN in the LDAP server,
667+
it typically indicates that wildcards were used in the input username.
668+
Example: for input username: 'test*', the Base DN returned from the ldap search could be user_dn:
669+
'uid=test,cn=Users,dc=openldaptest'.
670+
Since the authentication process relies on exact username matches for login, the login attempt will be denied if there's
671+
a discrepancy.
672+
Login will only be successful if the input username exactly matches the username in the DN from the LDAP search.
673+
674+
Args:
675+
username (str): the username given as an input by the user.
676+
user_dn (str): the DN returned from an ldap search of the username.
677+
"""
678+
err_msg = f"Mismatch likely due to wildcard use. Input Username: {username}, LDAP Search Base DN: {user_dn}."\
679+
f"Authentication requires exact match."
680+
if f'{self.USER_IDENTIFIER_ATTRIBUTE}={username}' not in user_dn:
681+
demisto.debug(f"LDAP Authentication - User login attempt failed - {err_msg}")
682+
raise Exception(f"LDAP Authentication - Authentication failed - {err_msg}")
683+
640684
def authenticate_and_roles_openldap(self, username: str, password: str, pull_name: bool = True,
641685
pull_mail: bool = True, pull_phone: bool = False, mail_attribute: str = 'mail',
642686
name_attribute: str = 'name', phone_attribute: str = 'mobile') -> dict:
@@ -647,7 +691,10 @@ def authenticate_and_roles_openldap(self, username: str, password: str, pull_nam
647691
user_data = self.get_user_data(username=username, search_user_by_dn=search_user_by_dn, pull_name=pull_name,
648692
pull_mail=pull_mail, pull_phone=pull_phone, mail_attribute=mail_attribute,
649693
name_attribute=name_attribute, phone_attribute=phone_attribute)
650-
self.authenticate_ldap_user(user_data['dn'], password)
694+
user_dn = user_data['dn']
695+
self.validate_exact_username_match(username, user_dn) # fail login attempt when a wildcard is used
696+
697+
self.authenticate_ldap_user(user_dn, password)
651698
user_groups = self.get_user_groups(user_identifier)
652699

653700
return {
@@ -665,7 +712,8 @@ def authenticate_and_roles_active_directory(self, username: str, password: str,
665712
"""
666713
Implements authenticate and roles command for Active Directory.
667714
"""
668-
xsoar_username = self._get_ad_username(username)
715+
self._has_wildcards_in_ad_logon(username) # fail login attempt when a wildcard is used
716+
ad_username = self._get_ad_username(username)
669717
auto_bind = self._get_auto_bind_value()
670718

671719
with Connection(self._ldap_server, self._username, self._password, auto_bind=auto_bind) as ldap_conn:
@@ -679,7 +727,7 @@ def authenticate_and_roles_active_directory(self, username: str, password: str,
679727
if pull_phone:
680728
attributes.append(phone_attribute)
681729

682-
search_filter = f'(|(sAMAccountName={xsoar_username})(userPrincipalName={username}))'
730+
search_filter = f'(|(sAMAccountName={ad_username})(userPrincipalName={username}))'
683731
ldap_conn_entries = ldap_conn.extend.standard.paged_search(search_base=self._base_dn,
684732
search_filter=search_filter,
685733
attributes=attributes,
@@ -764,7 +812,12 @@ def ad_authenticate(self, username: str, password: str) -> str:
764812
# If the given username is not a full DN, search for it in the ldap server and find it's full DN
765813
search_user_by_dn, _ = LdapClient._is_valid_dn(username, self.USER_IDENTIFIER_ATTRIBUTE)
766814
user_data_entry, _ = self.search_user_data(username, [self.GROUPS_IDENTIFIER_ATTRIBUTE], search_user_by_dn)
767-
username = user_data_entry.entry_dn
815+
user_dn = user_data_entry.entry_dn
816+
self.validate_exact_username_match(username, user_dn) # fail authentication when a wildcard is used
817+
username = user_dn
818+
819+
elif self._ldap_server_vendor == self.ACTIVE_DIRECTORY:
820+
self._has_wildcards_in_ad_logon(username) # fail authentication when a wildcard is used
768821

769822
return self.authenticate_ldap_user(username, password)
770823

Packs/OpenLDAP/Integrations/OpenLDAP/OpenLDAP_test.py

+42-10
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,27 @@ def test_get_ad_username(self, user_logon_name, expected_ad_username):
146146
ad_username = client._get_ad_username(user_logon_name)
147147
assert ad_username == expected_ad_username
148148

149+
@pytest.mark.parametrize('user_logon_name', [
150+
('test*'),
151+
('test?test'),
152+
])
153+
def test_has_wildcards_in_ad_logon(self, user_logon_name):
154+
"""
155+
Given:
156+
1. A user logon name contains the "*" symbol.
157+
2. A user logon name contains the "?" symbol.
158+
When:
159+
- Running the 'has_wildcards_in_ad_logon()' function.
160+
Then:
161+
- Verify that an exception is raised due to the use of wildcards in the logon name.
162+
"""
163+
client = LdapClient({'ldap_server_vendor': 'Active Directory', 'host': 'server_ip'})
164+
165+
with pytest.raises(Exception) as e:
166+
client._has_wildcards_in_ad_logon(user_logon_name)
167+
assert 'Wildcards were detected in the user logon name' in e.value.args[0]
168+
assert user_logon_name in e.value.args[0]
169+
149170
@pytest.mark.parametrize('connection_type, expected_auto_bind_value', [
150171
('Start TLS', 'TLS_BEFORE_BIND'),
151172
('SSL', 'NO_TLS'),
@@ -223,11 +244,27 @@ def test_is_valid_dn_user_id_not_in_dn(self, dn, user_identifier_attribute):
223244
client._is_valid_dn(dn, client.USER_IDENTIFIER_ATTRIBUTE)
224245
assert e.value.args[0] == f'OpenLDAP {user_identifier_attribute} attribute was not found in user DN : {dn}'
225246

226-
# def test_get_user_data(self):
227-
# client = LdapClient({'ldap_server_vendor': 'OpenLDAP', 'host': 'server_ip',
228-
# 'connection_type': 'SSL', 'user_identifier_attribute': 'uid'})
229-
#
230-
# mocker.patch('OpenLDAP.LdapClient.search_user_data', return_value=)
247+
@pytest.mark.parametrize('user_logon_name, user_dn', [
248+
('test*', 'uid=test,cn=Users,dc=openldaptest'),
249+
('tes?t', 'uid=test,cn=Users,dc=openldaptest')
250+
])
251+
def test_validate_exact_username_match(self, user_logon_name, user_dn):
252+
"""
253+
Given:
254+
1. A user logon name contains the "*" symbol, and the user dn.
255+
2. A user logon name contains the "?" symbol, and the user dn
256+
When:
257+
- Running the 'validate_exact_username_match()' function.
258+
Then:
259+
- Verify that an exception is raised due to the use of wildcards in the logon name.
260+
"""
261+
client = LdapClient({'ldap_server_vendor': 'OpenLDAP', 'host': 'server_ip'})
262+
263+
with pytest.raises(Exception) as e:
264+
client.validate_exact_username_match(user_logon_name, user_dn)
265+
assert 'Mismatch likely due to wildcard use' in e.value.args[0]
266+
assert user_logon_name in e.value.args[0]
267+
assert user_dn in e.value.args[0]
231268

232269

233270
class TestLDAPAuthentication:
@@ -306,11 +343,6 @@ def test_get_formatted_custom_attributes_invalid_attributes_input(self):
306343
f'{client.CUSTOM_ATTRIBUTE}')
307344

308345

309-
'''
310-
please add doc string to the tests in our format given: when: then
311-
'''
312-
313-
314346
class TestEntriesPagedSearch(unittest.TestCase):
315347

316348
def setUp(self):

Packs/OpenLDAP/ReleaseNotes/2_0_16.md

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
2+
#### Integrations
3+
4+
##### LDAP Authentication
5+
6+
- Fixed an issue where the ***ad-authenticate-and-roles*** command did not filter wildcard symbols in usernames.

Packs/OpenLDAP/pack_metadata.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"name": "LDAP Authentication",
33
"description": "Authenticate using Open LDAP or Active Directory",
44
"support": "xsoar",
5-
"currentVersion": "2.0.15",
5+
"currentVersion": "2.0.16",
66
"author": "Cortex XSOAR",
77
"url": "https://www.paloaltonetworks.com/cortex",
88
"email": "",

0 commit comments

Comments
 (0)