Sign Up
Log In
Log In
or
Sign Up
Places
All Projects
Status Monitor
Collapse sidebar
Cloud:OpenStack:Newton
python-Django
CVE-2019-19844.patch
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File CVE-2019-19844.patch of Package python-Django
commit 49acc65fd86dad138803ca3c8810010f020f5b24 Author: Simon Charette <charette.s@gmail.com> Date: Mon Dec 16 21:51:57 2019 -0500 [1.11.x] Fixed CVE-2019-19844 -- Used verified user email for password reset requests. Backport of 5b1fbcef7a8bec991ebe7b2a18b5d5a95d72cb70 from master. Co-Authored-By: Florian Apolloner <florian@apolloner.eu> (cherry picked from commit f4cff43bf921fcea6a29b726eb66767f67753fa2) diff --git a/django/contrib/auth/forms.py b/django/contrib/auth/forms.py index cc3be5ba0be2..c8c115d45baf 100644 --- a/django/contrib/auth/forms.py +++ b/django/contrib/auth/forms.py @@ -17,10 +17,25 @@ from django.utils.encoding import force_bytes from django.utils.html import format_html, format_html_join from django.utils.http import urlsafe_base64_encode from django.utils.safestring import mark_safe +from django.utils.six import PY3 from django.utils.text import capfirst from django.utils.translation import ugettext, ugettext_lazy as _ +def _unicode_ci_compare(s1, s2): + """ + Perform case-insensitive comparison of two identifiers, using the + recommended algorithm from Unicode Technical Report 36, section + 2.11.2(B)(2). + """ + normalized1 = unicodedata.normalize('NFKC', s1) + normalized2 = unicodedata.normalize('NFKC', s2) + if PY3: + return normalized1.casefold() == normalized2.casefold() + # lower() is the best alternative available on Python 2. + return normalized1.lower() == normalized2.lower() + + class ReadOnlyPasswordHashWidget(forms.Widget): def render(self, name, value, attrs): encoded = value @@ -220,9 +235,16 @@ class PasswordResetForm(forms.Form): resetting their password. """ - active_users = get_user_model()._default_manager.filter( - email__iexact=email, is_active=True) - return (u for u in active_users if u.has_usable_password()) + email_field_name = UserModel.get_email_field_name() + active_users = UserModel._default_manager.filter(**{ + '%s__iexact' % email_field_name: email, + 'is_active': True, + }) + return ( + u for u in active_users + if u.has_usable_password() and + _unicode_ci_compare(email, getattr(u, email_field_name)) + ) def save(self, domain_override=None, subject_template_name='registration/password_reset_subject.txt', @@ -234,6 +256,7 @@ class PasswordResetForm(forms.Form): user. """ email = self.cleaned_data["email"] + email_field_name = UserModel.get_email_field_name() for user in self.get_users(email): if not domain_override: current_site = get_current_site(request) @@ -241,8 +264,9 @@ class PasswordResetForm(forms.Form): domain = current_site.domain else: site_name = domain = domain_override + user_email = getattr(user, email_field_name) context = { - 'email': user.email, + 'email': user_email, 'domain': domain, 'site_name': site_name, 'uid': urlsafe_base64_encode(force_bytes(user.pk)), diff --git a/tests/auth_tests/test_forms.py b/tests/auth_tests/test_forms.py index 9528b5792c33..9c083f72b02e 100644 --- a/tests/auth_tests/test_forms.py +++ b/tests/auth_tests/test_forms.py @@ -385,6 +385,48 @@ class PasswordResetFormTest(TestCase): self.assertFalse(form.is_valid()) self.assertEqual(form['email'].errors, [_('Enter a valid email address.')]) + def test_user_email_unicode_collision(self): + User.objects.create_user('mike123', 'mike@example.org', 'test123') + User.objects.create_user('mike456', 'mıke@example.org', 'test123') + data = {'email': 'mıke@example.org'} + form = PasswordResetForm(data) + if six.PY2: + self.assertFalse(form.is_valid()) + else: + self.assertTrue(form.is_valid()) + form.save() + self.assertEqual(len(mail.outbox), 1) + self.assertEqual(mail.outbox[0].to, ['mıke@example.org']) + + def test_user_email_domain_unicode_collision(self): + User.objects.create_user('mike123', 'mike@ixample.org', 'test123') + User.objects.create_user('mike456', 'mike@ıxample.org', 'test123') + data = {'email': 'mike@ıxample.org'} + form = PasswordResetForm(data) + self.assertTrue(form.is_valid()) + form.save() + self.assertEqual(len(mail.outbox), 1) + self.assertEqual(mail.outbox[0].to, ['mike@ıxample.org']) + + def test_user_email_unicode_collision_nonexistent(self): + User.objects.create_user('mike123', 'mike@example.org', 'test123') + data = {'email': 'mıke@example.org'} + form = PasswordResetForm(data) + if six.PY2: + self.assertFalse(form.is_valid()) + else: + self.assertTrue(form.is_valid()) + form.save() + self.assertEqual(len(mail.outbox), 0) + + def test_user_email_domain_unicode_collision_nonexistent(self): + User.objects.create_user('mike123', 'mike@ixample.org', 'test123') + data = {'email': 'mike@ıxample.org'} + form = PasswordResetForm(data) + self.assertTrue(form.is_valid()) + form.save() + self.assertEqual(len(mail.outbox), 0) + def test_nonexistent_email(self): """ Test nonexistent email address. This should not fail because it would
Locations
Projects
Search
Status Monitor
Help
OpenBuildService.org
Documentation
API Documentation
Code of Conduct
Contact
Support
@OBShq
Terms
openSUSE Build Service is sponsored by
The Open Build Service is an
openSUSE project
.
Sign Up
Log In
Places
Places
All Projects
Status Monitor