diff --git a/0001-Hide-AccountLocked-exception-from-end-users.patch b/0001-Hide-AccountLocked-exception-from-end-users.patch new file mode 100644 index 0000000000000000000000000000000000000000..fce59c3e9df4c8ba8e947058aeb8b842088dc1b5 --- /dev/null +++ b/0001-Hide-AccountLocked-exception-from-end-users.patch @@ -0,0 +1,182 @@ +From 8d596ec5ddcb4ce199afe22fb938215a30a7cffc Mon Sep 17 00:00:00 2001 +From: Gage Hugo +Date: Tue, 27 Oct 2020 15:22:04 -0500 +Subject: [PATCH] Hide AccountLocked exception from end users + +This change hides the AccountLocked exception from being returned +to the end user to hide sensitive information that a potential +malicious person could gain insight from. + +The notification handler catches the AccountLocked exception as +before, but after sending the audit notification, it instead +bubbles up Unauthorized rather than AccountLocked. + +Co-Authored-By: Samuel de Medeiros Queiroz + +Change-Id: Id51241989b22c52810391f3e8e1cadbf8613d873 +Related-Bug: #1688137 +(cherry picked from commit ac2631ae33445877094cdae796fbcdce8833a626) +(cherry picked from commit 1b573ae7d1c20e0ebfbde79bbe7538a09589c75d) +--- + keystone/notifications.py | 2 + + keystone/tests/unit/common/test_notifications.py | 2 +- + keystone/tests/unit/identity/test_backend_sql.py | 83 +++++++++++----------- + .../notes/bug-1688137-e4203c9a728690a7.yaml | 8 +++ + 4 files changed, 52 insertions(+), 43 deletions(-) + create mode 100644 releasenotes/notes/bug-1688137-e4203c9a728690a7.yaml + +diff --git a/keystone/notifications.py b/keystone/notifications.py +index 2a81c04..b604c3a 100644 +--- a/keystone/notifications.py ++++ b/keystone/notifications.py +@@ -506,6 +506,8 @@ class CadfNotificationWrapper(object): + taxonomy.OUTCOME_FAILURE, + target, self.event_type, + reason=audit_reason) ++ if isinstance(ex, exception.AccountLocked): ++ raise exception.Unauthorized + raise + except Exception: + # For authentication failure send a CADF event as well +diff --git a/keystone/tests/unit/common/test_notifications.py b/keystone/tests/unit/common/test_notifications.py +index 5a5b8d5..ee934db 100644 +--- a/keystone/tests/unit/common/test_notifications.py ++++ b/keystone/tests/unit/common/test_notifications.py +@@ -768,7 +768,7 @@ class CADFNotificationsForPCIDSSEvents(BaseNotificationTest): + password = uuid.uuid4().hex + new_password = uuid.uuid4().hex + expected_responses = [AssertionError, AssertionError, AssertionError, +- exception.AccountLocked] ++ exception.Unauthorized] + user_ref = unit.new_user_ref(domain_id=self.domain_id, + password=password) + user_ref = PROVIDERS.identity_api.create_user(user_ref) +diff --git a/keystone/tests/unit/identity/test_backend_sql.py b/keystone/tests/unit/identity/test_backend_sql.py +index e05cabd..40d5435 100644 +--- a/keystone/tests/unit/identity/test_backend_sql.py ++++ b/keystone/tests/unit/identity/test_backend_sql.py +@@ -578,7 +578,7 @@ class LockingOutUserTests(test_backend_sql.SqlTests): + ) + # test locking out user after max failed attempts + self._fail_auth_repeatedly(self.user['id']) +- self.assertRaises(exception.AccountLocked, ++ self.assertRaises(exception.Unauthorized, + PROVIDERS.identity_api.authenticate, + self.make_request(), + user_id=self.user['id'], +@@ -607,7 +607,7 @@ class LockingOutUserTests(test_backend_sql.SqlTests): + def test_set_enabled_unlocks_user(self): + # lockout user + self._fail_auth_repeatedly(self.user['id']) +- self.assertRaises(exception.AccountLocked, ++ self.assertRaises(exception.Unauthorized, + PROVIDERS.identity_api.authenticate, + self.make_request(), + user_id=self.user['id'], +@@ -624,50 +624,49 @@ class LockingOutUserTests(test_backend_sql.SqlTests): + def test_lockout_duration(self): + # freeze time + with freezegun.freeze_time(datetime.datetime.utcnow()) as frozen_time: +- # lockout user +- self._fail_auth_repeatedly(self.user['id']) +- self.assertRaises(exception.AccountLocked, +- PROVIDERS.identity_api.authenticate, +- self.make_request(), +- user_id=self.user['id'], +- password=uuid.uuid4().hex) +- # freeze time past the duration, user should be unlocked and failed +- # auth count should get reset +- frozen_time.tick(delta=datetime.timedelta( +- seconds=CONF.security_compliance.lockout_duration + 1)) +- PROVIDERS.identity_api.authenticate( +- self.make_request(), user_id=self.user['id'], +- password=self.password +- ) +- # test failed auth count was reset by authenticating with the wrong +- # password, should raise an assertion error and not account locked +- self.assertRaises(AssertionError, +- PROVIDERS.identity_api.authenticate, +- self.make_request(), +- user_id=self.user['id'], +- password=uuid.uuid4().hex) ++ with self.make_request(): ++ # lockout user ++ self._fail_auth_repeatedly(self.user['id']) ++ self.assertRaises(exception.Unauthorized, ++ PROVIDERS.identity_api.authenticate, ++ user_id=self.user['id'], ++ password=uuid.uuid4().hex) ++ # freeze time past the duration, user should be unlocked and ++ # failed auth count should get reset ++ frozen_time.tick(delta=datetime.timedelta( ++ seconds=CONF.security_compliance.lockout_duration + 1)) ++ PROVIDERS.identity_api.authenticate( ++ user_id=self.user['id'], ++ password=self.password ++ ) ++ # test failed auth count was reset by authenticating with the ++ # wrong password, should raise an assertion error and not ++ # account locked ++ self.assertRaises(AssertionError, ++ PROVIDERS.identity_api.authenticate, ++ user_id=self.user['id'], ++ password=uuid.uuid4().hex) + + def test_lockout_duration_failed_auth_cnt_resets(self): + # freeze time + with freezegun.freeze_time(datetime.datetime.utcnow()) as frozen_time: +- # lockout user +- self._fail_auth_repeatedly(self.user['id']) +- self.assertRaises(exception.AccountLocked, +- PROVIDERS.identity_api.authenticate, +- self.make_request(), +- user_id=self.user['id'], +- password=uuid.uuid4().hex) +- # freeze time past the duration, failed_auth_cnt should reset +- frozen_time.tick(delta=datetime.timedelta( +- seconds=CONF.security_compliance.lockout_duration + 1)) +- # repeat failed auth the max times +- self._fail_auth_repeatedly(self.user['id']) +- # test user account is locked +- self.assertRaises(exception.AccountLocked, +- PROVIDERS.identity_api.authenticate, +- self.make_request(), +- user_id=self.user['id'], +- password=uuid.uuid4().hex) ++ with self.make_request(): ++ # lockout user ++ self._fail_auth_repeatedly(self.user['id']) ++ self.assertRaises(exception.Unauthorized, ++ PROVIDERS.identity_api.authenticate, ++ user_id=self.user['id'], ++ password=uuid.uuid4().hex) ++ # freeze time past the duration, failed_auth_cnt should reset ++ frozen_time.tick(delta=datetime.timedelta( ++ seconds=CONF.security_compliance.lockout_duration + 1)) ++ # repeat failed auth the max times ++ self._fail_auth_repeatedly(self.user['id']) ++ # test user account is locked ++ self.assertRaises(exception.Unauthorized, ++ PROVIDERS.identity_api.authenticate, ++ user_id=self.user['id'], ++ password=uuid.uuid4().hex) + + def _fail_auth_repeatedly(self, user_id): + wrong_password = uuid.uuid4().hex +diff --git a/releasenotes/notes/bug-1688137-e4203c9a728690a7.yaml b/releasenotes/notes/bug-1688137-e4203c9a728690a7.yaml +new file mode 100644 +index 0000000..bd7a060 +--- /dev/null ++++ b/releasenotes/notes/bug-1688137-e4203c9a728690a7.yaml +@@ -0,0 +1,8 @@ ++--- ++fixes: ++ - | ++ [`bug 1688137 `_] ++ Fixed the AccountLocked exception being shown to the end user since ++ it provides some information that could be exploited by a ++ malicious user. The end user will now see Unauthorized instead of ++ AccountLocked, preventing user info oracle exploitation. +-- +2.6.2 + diff --git a/openstack-keystone.spec b/openstack-keystone.spec index 03b0ab87e712b8ae2b9ff6a9da4a07f4b4caf9de..060c212670f5ce5238711a5861e6120cfded3fb4 100644 --- a/openstack-keystone.spec +++ b/openstack-keystone.spec @@ -9,7 +9,7 @@ Keystone is a Python implementation of the OpenStack \ Name: openstack-keystone Version: 13.0.4 -Release: 2 +Release: 3 Summary: OpenStack Identity Service License: Apache-2.0 URL: http://keystone.openstack.org/ @@ -19,6 +19,8 @@ Source1: openstack-keystone.logrotate Source3: openstack-keystone.sysctl Source5: openstack-keystone-sample-data Source20: keystone-dist.conf +Patch0: 0001-Hide-AccountLocked-exception-from-end-users.patch + BuildArch: noarch BuildRequires: openstack-macros @@ -287,6 +289,8 @@ chmod 660 %{_localstatedir}/log/keystone/keystone.log %endif %changelog +* Mon Sep 06 2021 zhangfan - 13.0.4-3 +- Fix CVE-2021-38155 * Mon Jun 07 2021 wangxiyuan - Fix sqlalchemy-migrate require error * Thu May 27 2021 openstack-sig