Skip to content

Commit 1c254c3

Browse files
committed
Escape username within DN correctly and remove escape_userdn
1 parent 1d9d0d5 commit 1c254c3

File tree

3 files changed

+20
-34
lines changed

3 files changed

+20
-34
lines changed

README.md

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -235,12 +235,6 @@ Attribute containing user's name needed for building DN string, if `lookup_dn` i
235235
See `user_search_base` for info on how this attribute is used.
236236
For most LDAP servers, this is username. For Active Directory, it is cn.
237237

238-
#### `LDAPAuthenticator.escape_userdn`
239-
240-
If set to True, escape special chars in userdn when authenticating in LDAP.
241-
On some LDAP servers, when userdn contains chars like '(', ')', '\' authentication may fail when those chars
242-
are not escaped.
243-
244238
#### `LDAPAuthenticator.auth_state_attributes`
245239

246240
An optional list of attributes to be fetched for a user after login.
@@ -274,7 +268,6 @@ c.LDAPAuthenticator.lookup_dn_search_password = 'secret'
274268
c.LDAPAuthenticator.user_search_base = 'ou=people,dc=wikimedia,dc=org'
275269
c.LDAPAuthenticator.user_attribute = 'sAMAccountName'
276270
c.LDAPAuthenticator.lookup_dn_user_dn_attribute = 'cn'
277-
c.LDAPAuthenticator.escape_userdn = False
278271
```
279272

280273
In setup above, first LDAP will be searched (with account ldap_search_user_technical_account) for users that have sAMAccountName=login

ldapauthenticator/ldapauthenticator.py

Lines changed: 20 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import ldap3
55
from jupyterhub.auth import Authenticator
66
from ldap3.utils.conv import escape_filter_chars
7+
from ldap3.utils.dn import escape_rdn
78
from traitlets import Bool, Int, List, Unicode, Union, UseEnum, observe, validate
89

910

@@ -162,20 +163,17 @@ def _validate_bind_dn_template(self, proposal):
162163
help="List of attributes to be searched",
163164
)
164165

165-
# FIXME: Use something other than this? THIS IS LAME, akin to websites restricting things you
166-
# can use in usernames / passwords to protect from SQL injection!
167166
valid_username_regex = Unicode(
168167
r"^[a-z][.a-z0-9_-]*$",
169168
config=True,
170169
help="""
171-
Regex for validating usernames - those that do not match this regex will be rejected.
172-
173-
This is primarily used as a measure against LDAP injection, which has fatal security
174-
considerations. The default works for most LDAP installations, but some users might need
175-
to modify it to fit their custom installs. If you are modifying it, be sure to understand
176-
the implications of allowing additional characters in usernames and what that means for
177-
LDAP injection issues. See https://www.owasp.org/index.php/LDAP_injection for an overview
178-
of LDAP injection.
170+
Regex for validating usernames - those that do not match this regex will
171+
be rejected.
172+
173+
This config was primarily introduced to prevent LDAP injection
174+
(https://www.owasp.org/index.php/LDAP_injection), but that is since 2.0
175+
being mitigated by escaping all sensitive characters when interacting
176+
with the LDAP server.
179177
""",
180178
)
181179

@@ -246,7 +244,8 @@ def _validate_bind_dn_template(self, proposal):
246244
default_value=None,
247245
allow_none=True,
248246
help="""
249-
Technical account for user lookup, if `lookup_dn` is set to True.
247+
DN for a technical user account allowed to search for information about
248+
provided username, if `lookup_dn` is set to True.
250249
251250
If both lookup_dn_search_user and lookup_dn_search_password are None, then anonymous LDAP query will be done.
252251
""",
@@ -278,13 +277,16 @@ def _validate_bind_dn_template(self, proposal):
278277
False,
279278
config=True,
280279
help="""
281-
If set to True, escape special chars in userdn when authenticating in LDAP.
282-
283-
On some LDAP servers, when userdn contains chars like '(', ')', '\' authentication may fail when those chars
284-
are not escaped.
280+
Removed in 2.0, configuring this no longer has any effect.
285281
""",
286282
)
287283

284+
@observe("escape_userdn")
285+
def _observe_escape_userdn(self, change):
286+
self.log.warning(
287+
"LDAPAuthenticator.escape_userdn was removed in 2.0 and no longer has any effect."
288+
)
289+
288290
search_filter = Unicode(
289291
config=True, help="LDAP3 Search Filter whose results are allowed access"
290292
)
@@ -310,16 +312,13 @@ def resolve_username(self, username_supplied_by_user):
310312
Resolves a username supplied by a user to the a user DN when lookup_dn
311313
is True.
312314
"""
313-
search_dn = self.lookup_dn_search_user
314-
if self.escape_userdn:
315-
search_dn = escape_filter_chars(search_dn)
316315
conn = self.get_connection(
317-
userdn=search_dn,
316+
userdn=self.lookup_dn_search_user,
318317
password=self.lookup_dn_search_password,
319318
)
320319
if not conn.bind():
321320
self.log.warning(
322-
f"Failed to connect to LDAP server with search user '{search_dn}'"
321+
f"Failed to connect to LDAP server with search user '{self.lookup_dn_search_user}'"
323322
)
324323
return (None, None)
325324

@@ -441,17 +440,12 @@ async def authenticate(self, handler, data):
441440
username, resolved_dn = self.resolve_username(username)
442441
if not username:
443442
return None
444-
if str(self.lookup_dn_user_dn_attribute).upper() == "CN":
445-
# Only escape commas if the lookup attribute is CN
446-
username = re.subn(r"([^\\]),", r"\1\,", username)[0]
447443
if not bind_dn_template:
448444
bind_dn_template = [resolved_dn]
449445

450446
is_bound = False
451447
for dn in bind_dn_template:
452-
userdn = dn.format(username=username)
453-
if self.escape_userdn:
454-
userdn = escape_filter_chars(userdn)
448+
userdn = dn.format(username=escape_rdn(username))
455449
self.log.debug(f"Attempting to bind {username} with {userdn}")
456450
msg = "Status of user bind {username} with {userdn} : {is_bound}"
457451
try:

ldapauthenticator/tests/conftest.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ def authenticator():
1414
authenticator.user_search_base = "ou=people,dc=planetexpress,dc=com"
1515
authenticator.user_attribute = "uid"
1616
authenticator.lookup_dn_user_dn_attribute = "cn"
17-
authenticator.escape_userdn = True
1817
authenticator.attributes = ["uid", "cn", "mail", "ou"]
1918
authenticator.use_lookup_dn_username = False
2019

0 commit comments

Comments
 (0)