From 3f6a67ea301e932ad7c0e4fdf8633860baa35fe2 Mon Sep 17 00:00:00 2001 From: Julian Rother <julian@cccv.de> Date: Mon, 30 Aug 2021 19:55:07 +0200 Subject: [PATCH] Catch LDAPSASLPrepError on login Ldap3 raises LDAPSASLPrepError on bind if the password contains characters forbidden by SASLPrep (string preperation/normalization algorithm for user names and passwords). Examples are carriage return ("\r") or newline ("\n") characters. See #100. --- tests/test_session.py | 8 ++++++++ uffd/ldap.py | 4 ++-- uffd/session/views.py | 4 ++-- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/tests/test_session.py b/tests/test_session.py index f99ab1a1..312a66b4 100644 --- a/tests/test_session.py +++ b/tests/test_session.py @@ -87,6 +87,14 @@ class TestSession(UffdTestCase): self.assertEqual(r.status_code, 200) self.assertLoggedOut() + # Regression test for #100 (uncatched LDAPSASLPrepError) + def test_saslprep_invalid_password(self): + r = self.client.post(path=url_for('session.login'), + data={'loginname': self.test_data.get('user').get('loginname'), 'password': 'wrongpassword\n'}, follow_redirects=True) + dump('login_saslprep_invalid_password', r) + self.assertEqual(r.status_code, 200) + self.assertLoggedOut() + def test_wrong_user(self): r = self.client.post(path=url_for('session.login'), data={'loginname': 'nouser', 'password': self.test_data.get('user').get('password')}, diff --git a/uffd/ldap.py b/uffd/ldap.py index ed31bb01..832ab508 100644 --- a/uffd/ldap.py +++ b/uffd/ldap.py @@ -4,7 +4,7 @@ import hashlib from flask import current_app, request, abort, session import ldap3 -from ldap3.core.exceptions import LDAPBindError, LDAPPasswordIsMandatoryError, LDAPInvalidDnError +from ldap3.core.exceptions import LDAPBindError, LDAPPasswordIsMandatoryError, LDAPInvalidDnError, LDAPSASLPrepError # We import LDAPCommitError only because it is imported from us by other files. It is not needed here from uffd.ldapalchemy import LDAPMapper, LDAPCommitError # pylint: disable=unused-import @@ -74,7 +74,7 @@ def test_user_bind(bind_dn, bind_pw): conn = connect_and_bind_to_ldap(server, bind_dn, bind_pw) if not conn: return False - except (LDAPBindError, LDAPPasswordIsMandatoryError, LDAPInvalidDnError): + except (LDAPBindError, LDAPPasswordIsMandatoryError, LDAPInvalidDnError, LDAPSASLPrepError): return False conn.search(conn.user, encode_filter(current_app.config["LDAP_USER_SEARCH_FILTER"])) diff --git a/uffd/session/views.py b/uffd/session/views.py index cde2a3fd..a29557a7 100644 --- a/uffd/session/views.py +++ b/uffd/session/views.py @@ -9,7 +9,7 @@ from uffd.database import db from uffd.csrf import csrf_protect from uffd.secure_redirect import secure_local_redirect from uffd.user.models import User -from uffd.ldap import ldap, test_user_bind, LDAPInvalidDnError, LDAPBindError, LDAPPasswordIsMandatoryError +from uffd.ldap import ldap, test_user_bind, LDAPInvalidDnError, LDAPBindError, LDAPPasswordIsMandatoryError, LDAPSASLPrepError from uffd.ratelimit import Ratelimit, host_ratelimit, format_delay from uffd.session.models import DeviceLoginInitiation, DeviceLoginConfirmation @@ -46,7 +46,7 @@ def login_get_user(loginname, password): session['user_pw'] = password try: ldap.get_connection() - except (LDAPBindError, LDAPPasswordIsMandatoryError): + except (LDAPBindError, LDAPPasswordIsMandatoryError, LDAPSASLPrepError): session.clear() return None -- GitLab