diff --git a/0060-rpcserver-validate-Kerberos-principal-name-before-ru.patch b/0060-rpcserver-validate-Kerberos-principal-name-before-ru.patch new file mode 100644 index 0000000..32c913b --- /dev/null +++ b/0060-rpcserver-validate-Kerberos-principal-name-before-ru.patch @@ -0,0 +1,233 @@ +From 8b598814d1e51466ebbe3e0a392af92370d0c93b Mon Sep 17 00:00:00 2001 +From: Alexander Bokovoy +Date: Wed, 7 Feb 2024 13:09:54 +0200 +Subject: [PATCH] rpcserver: validate Kerberos principal name before running + kinit + +Do minimal validation of the Kerberos principal name when passing it to +kinit command line tool. Also pass it as the final argument to prevent +option injection. + +Accepted Kerberos principals are: + - user names, using the following regexp + (username with optional @realm, no spaces or slashes in the name): + "(?!^[0-9]+$)^[a-zA-Z0-9_.][a-zA-Z0-9_.-]*[a-zA-Z0-9_.$-]?@?[a-zA-Z0-9.-]*$" + + - service names (with slash in the name but no spaces). Validation of + the hostname is done. There is no validation of the service name. + +The regular expression above also covers cases where a principal name +starts with '-'. This prevents option injection as well. + +This fixes CVE-2024-1481 + +Fixes: https://pagure.io/freeipa/issue/9541 + +Signed-off-by: Alexander Bokovoy +Signed-off-by: Rob Crittenden +Reviewed-By: Florence Blanc-Renaud +Reviewed-By: Florence Blanc-Renaud +--- + ipalib/install/kinit.py | 47 ++++++++++++++++++- + ipaserver/rpcserver.py | 9 ++-- + ipatests/setup.py | 1 + + ipatests/test_ipalib_install/__init__.py | 0 + ipatests/test_ipalib_install/test_kinit.py | 29 ++++++++++++ + 5 files changed, 80 insertions(+), 6 deletions(-) + create mode 100644 ipatests/test_ipalib_install/__init__.py + create mode 100644 ipatests/test_ipalib_install/test_kinit.py + +diff --git a/ipalib/install/kinit.py b/ipalib/install/kinit.py +index cc839ec38d1dbcb1cf3b0334592d04baf0dba23b..4ad4eaa1c30f2fb0ab02be411917e304eb527d32 100644 +--- a/ipalib/install/kinit.py ++++ b/ipalib/install/kinit.py +@@ -6,12 +6,16 @@ from __future__ import absolute_import + + import logging + import os ++import re + import time + + import gssapi + + from ipaplatform.paths import paths + from ipapython.ipautil import run ++from ipalib.constants import PATTERN_GROUPUSER_NAME ++from ipalib.util import validate_hostname ++from ipalib import api + + logger = logging.getLogger(__name__) + +@@ -21,6 +25,40 @@ KRB5_KDC_UNREACH = 2529639068 + # A service is not available that s required to process the request + KRB5KDC_ERR_SVC_UNAVAILABLE = 2529638941 + ++PATTERN_REALM = '@?([a-zA-Z0-9.-]*)$' ++PATTERN_PRINCIPAL = '(' + PATTERN_GROUPUSER_NAME[:-1] + ')' + PATTERN_REALM ++PATTERN_SERVICE = '([a-zA-Z0-9.-]+)/([a-zA-Z0-9.-]+)' + PATTERN_REALM ++ ++user_pattern = re.compile(PATTERN_PRINCIPAL) ++service_pattern = re.compile(PATTERN_SERVICE) ++ ++ ++def validate_principal(principal): ++ if not isinstance(principal, str): ++ raise RuntimeError('Invalid principal: not a string') ++ if ('/' in principal) and (' ' in principal): ++ raise RuntimeError('Invalid principal: bad spacing') ++ else: ++ realm = None ++ match = user_pattern.match(principal) ++ if match is None: ++ match = service_pattern.match(principal) ++ if match is None: ++ raise RuntimeError('Invalid principal: cannot parse') ++ else: ++ # service = match[1] ++ hostname = match[2] ++ realm = match[3] ++ try: ++ validate_hostname(hostname) ++ except ValueError as e: ++ raise RuntimeError(str(e)) ++ else: # user match, validate realm ++ # username = match[1] ++ realm = match[2] ++ if realm and 'realm' in api.env and realm != api.env.realm: ++ raise RuntimeError('Invalid principal: realm mismatch') ++ + + def kinit_keytab(principal, keytab, ccache_name, config=None, attempts=1): + """ +@@ -29,6 +67,7 @@ def kinit_keytab(principal, keytab, ccache_name, config=None, attempts=1): + The optional parameter 'attempts' specifies how many times the credential + initialization should be attempted in case of non-responsive KDC. + """ ++ validate_principal(principal) + errors_to_retry = {KRB5KDC_ERR_SVC_UNAVAILABLE, + KRB5_KDC_UNREACH} + logger.debug("Initializing principal %s using keytab %s", +@@ -65,6 +104,7 @@ def kinit_keytab(principal, keytab, ccache_name, config=None, attempts=1): + + return None + ++ + def kinit_password(principal, password, ccache_name, config=None, + armor_ccache_name=None, canonicalize=False, + enterprise=False, lifetime=None): +@@ -73,8 +113,9 @@ def kinit_password(principal, password, ccache_name, config=None, + web-based authentication, use armor_ccache_path to specify http service + ccache. + """ ++ validate_principal(principal) + logger.debug("Initializing principal %s using password", principal) +- args = [paths.KINIT, principal, '-c', ccache_name] ++ args = [paths.KINIT, '-c', ccache_name] + if armor_ccache_name is not None: + logger.debug("Using armor ccache %s for FAST webauth", + armor_ccache_name) +@@ -91,6 +132,7 @@ def kinit_password(principal, password, ccache_name, config=None, + logger.debug("Using enterprise principal") + args.append('-E') + ++ args.extend(['--', principal]) + env = {'LC_ALL': 'C'} + if config is not None: + env['KRB5_CONFIG'] = config +@@ -154,6 +196,7 @@ def kinit_pkinit( + + :raises: CalledProcessError if PKINIT fails + """ ++ validate_principal(principal) + logger.debug( + "Initializing principal %s using PKINIT %s", principal, user_identity + ) +@@ -168,7 +211,7 @@ def kinit_pkinit( + assert pkinit_anchor.startswith(("FILE:", "DIR:", "ENV:")) + args.extend(["-X", f"X509_anchors={pkinit_anchor}"]) + args.extend(["-X", f"X509_user_identity={user_identity}"]) +- args.append(principal) ++ args.extend(['--', principal]) + + # this workaround enables us to capture stderr and put it + # into the raised exception in case of unsuccessful authentication +diff --git a/ipaserver/rpcserver.py b/ipaserver/rpcserver.py +index 198fc9e7dbae281f797dcccf96d21d475ff31e8c..4f65b7e057c3d184ffadd4f28872ec3cceb73077 100644 +--- a/ipaserver/rpcserver.py ++++ b/ipaserver/rpcserver.py +@@ -1135,10 +1135,6 @@ class login_password(Backend, KerberosSession): + canonicalize=True, + lifetime=self.api.env.kinit_lifetime) + +- if armor_path: +- logger.debug('Cleanup the armor ccache') +- ipautil.run([paths.KDESTROY, '-A', '-c', armor_path], +- env={'KRB5CCNAME': armor_path}, raiseonerr=False) + except RuntimeError as e: + if ('kinit: Cannot read password while ' + 'getting initial credentials') in str(e): +@@ -1156,6 +1152,11 @@ class login_password(Backend, KerberosSession): + raise KrbPrincipalWrongFAST(principal=principal) + raise InvalidSessionPassword(principal=principal, + message=unicode(e)) ++ finally: ++ if armor_path: ++ logger.debug('Cleanup the armor ccache') ++ ipautil.run([paths.KDESTROY, '-A', '-c', armor_path], ++ env={'KRB5CCNAME': armor_path}, raiseonerr=False) + + + class change_password(Backend, HTTP_Status): +diff --git a/ipatests/setup.py b/ipatests/setup.py +index 6217a1ba5d82ba7fa79cc4c073270abe307cd2ed..0aec4a70dbd75d416e4288e1204130daf46bda94 100644 +--- a/ipatests/setup.py ++++ b/ipatests/setup.py +@@ -41,6 +41,7 @@ if __name__ == '__main__': + "ipatests.test_integration", + "ipatests.test_ipaclient", + "ipatests.test_ipalib", ++ "ipatests.test_ipalib_install", + "ipatests.test_ipaplatform", + "ipatests.test_ipapython", + "ipatests.test_ipaserver", +diff --git a/ipatests/test_ipalib_install/__init__.py b/ipatests/test_ipalib_install/__init__.py +new file mode 100644 +index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 +diff --git a/ipatests/test_ipalib_install/test_kinit.py b/ipatests/test_ipalib_install/test_kinit.py +new file mode 100644 +index 0000000000000000000000000000000000000000..f89ea17d7874c28bad2524ebf456d2caeafddd1f +--- /dev/null ++++ b/ipatests/test_ipalib_install/test_kinit.py +@@ -0,0 +1,29 @@ ++# ++# Copyright (C) 2024 FreeIPA Contributors see COPYING for license ++# ++"""Tests for ipalib.install.kinit module ++""" ++ ++import pytest ++ ++from ipalib.install.kinit import validate_principal ++ ++ ++# None means no exception is expected ++@pytest.mark.parametrize('principal, exception', [ ++ ('testuser', None), ++ ('testuser@EXAMPLE.TEST', None), ++ ('test/ipa.example.test', None), ++ ('test/ipa.example.test@EXAMPLE.TEST', None), ++ ('test/ipa@EXAMPLE.TEST', RuntimeError), ++ ('test/-ipa.example.test@EXAMPLE.TEST', RuntimeError), ++ ('test/ipa.1example.test@EXAMPLE.TEST', RuntimeError), ++ ('test /ipa.example,test', RuntimeError), ++ ('testuser@OTHER.TEST', RuntimeError), ++ ('test/ipa.example.test@OTHER.TEST', RuntimeError), ++]) ++def test_validate_principal(principal, exception): ++ try: ++ validate_principal(principal) ++ except Exception as e: ++ assert e.__class__ == exception +-- +2.44.0 + diff --git a/0061-validate_principal-Don-t-try-to-verify-that-the-real.patch b/0061-validate_principal-Don-t-try-to-verify-that-the-real.patch new file mode 100644 index 0000000..1eb82f1 --- /dev/null +++ b/0061-validate_principal-Don-t-try-to-verify-that-the-real.patch @@ -0,0 +1,89 @@ +From 5781369e78fd83cee64a4d306198423c7a126ba0 Mon Sep 17 00:00:00 2001 +From: Rob Crittenden +Date: Thu, 22 Feb 2024 08:29:31 -0500 +Subject: [PATCH] validate_principal: Don't try to verify that the realm is + known + +The actual value is less important than whether it matches the +regular expression. A number of legal but difficult to know in +context realms could be passed in here (trust for example). + +This fixes CVE-2024-1481 + +Fixes: https://pagure.io/freeipa/issue/9541 + +Signed-off-by: Rob Crittenden +Reviewed-By: Florence Blanc-Renaud +--- + ipalib/install/kinit.py | 12 ++++-------- + ipatests/test_ipalib_install/test_kinit.py | 9 ++++++--- + 2 files changed, 10 insertions(+), 11 deletions(-) + +diff --git a/ipalib/install/kinit.py b/ipalib/install/kinit.py +index 4ad4eaa1c30f2fb0ab02be411917e304eb527d32..d5fb56bf041c6f61515fc3ce4cc1ca1cfbcdbab7 100644 +--- a/ipalib/install/kinit.py ++++ b/ipalib/install/kinit.py +@@ -15,7 +15,6 @@ from ipaplatform.paths import paths + from ipapython.ipautil import run + from ipalib.constants import PATTERN_GROUPUSER_NAME + from ipalib.util import validate_hostname +-from ipalib import api + + logger = logging.getLogger(__name__) + +@@ -39,7 +38,9 @@ def validate_principal(principal): + if ('/' in principal) and (' ' in principal): + raise RuntimeError('Invalid principal: bad spacing') + else: +- realm = None ++ # For a user match in the regex ++ # username = match[1] ++ # realm = match[2] + match = user_pattern.match(principal) + if match is None: + match = service_pattern.match(principal) +@@ -48,16 +49,11 @@ def validate_principal(principal): + else: + # service = match[1] + hostname = match[2] +- realm = match[3] ++ # realm = match[3] + try: + validate_hostname(hostname) + except ValueError as e: + raise RuntimeError(str(e)) +- else: # user match, validate realm +- # username = match[1] +- realm = match[2] +- if realm and 'realm' in api.env and realm != api.env.realm: +- raise RuntimeError('Invalid principal: realm mismatch') + + + def kinit_keytab(principal, keytab, ccache_name, config=None, attempts=1): +diff --git a/ipatests/test_ipalib_install/test_kinit.py b/ipatests/test_ipalib_install/test_kinit.py +index f89ea17d7874c28bad2524ebf456d2caeafddd1f..8289c4b75c9de3b17748a6abffe0538d08f2698f 100644 +--- a/ipatests/test_ipalib_install/test_kinit.py ++++ b/ipatests/test_ipalib_install/test_kinit.py +@@ -17,13 +17,16 @@ from ipalib.install.kinit import validate_principal + ('test/ipa.example.test@EXAMPLE.TEST', None), + ('test/ipa@EXAMPLE.TEST', RuntimeError), + ('test/-ipa.example.test@EXAMPLE.TEST', RuntimeError), +- ('test/ipa.1example.test@EXAMPLE.TEST', RuntimeError), ++ ('test/ipa.1example.test@EXAMPLE.TEST', None), + ('test /ipa.example,test', RuntimeError), +- ('testuser@OTHER.TEST', RuntimeError), +- ('test/ipa.example.test@OTHER.TEST', RuntimeError), ++ ('testuser@OTHER.TEST', None), ++ ('test/ipa.example.test@OTHER.TEST', None) + ]) + def test_validate_principal(principal, exception): + try: + validate_principal(principal) + except Exception as e: + assert e.__class__ == exception ++ else: ++ if exception is not None: ++ raise RuntimeError('Test should have failed') +-- +2.44.0 + diff --git a/0062-Vault-add-additional-fallback-to-RSA-OAEP-wrapping-a.patch b/0062-Vault-add-additional-fallback-to-RSA-OAEP-wrapping-a.patch new file mode 100644 index 0000000..5e424d0 --- /dev/null +++ b/0062-Vault-add-additional-fallback-to-RSA-OAEP-wrapping-a.patch @@ -0,0 +1,45 @@ +From ca561f72d05b937e727db76c42d807ba07661494 Mon Sep 17 00:00:00 2001 +From: Rob Crittenden +Date: Fri, 1 Mar 2024 15:12:33 -0500 +Subject: [PATCH] Vault: add additional fallback to RSA-OAEP wrapping algo + +There is a fallback when creating the wrapping key but one was missing +when trying to use the cached transport_cert. + +This allows, along with forcing keyWrap.useOAEP=true, vault creation +on an nCipher HSM. + +This can be seen in HSMs where the device doesn't support the +PKCS#1 v1.5 mechanism. It will error out with either "invalid +algorithm" or CKR_FUNCTION_FAILED. + +Related: https://pagure.io/freeipa/issue/9191 + +Signed-off-by: Rob Crittenden +Reviewed-By: Florence Blanc-Renaud +--- + ipaclient/plugins/vault.py | 8 ++++++-- + 1 file changed, 6 insertions(+), 2 deletions(-) + +diff --git a/ipaclient/plugins/vault.py b/ipaclient/plugins/vault.py +index a29bd6e5f437d9d07f2d995d7bc884e7f2419c27..96edf09a2060e7b39e1e96c6fa65ae095ec18e73 100644 +--- a/ipaclient/plugins/vault.py ++++ b/ipaclient/plugins/vault.py +@@ -755,8 +755,12 @@ class ModVaultData(Local): + Calls the internal counterpart of the command. + """ + # try call with cached transport certificate +- result = self._do_internal(algo, transport_cert, False, +- False, *args, **options) ++ try: ++ result = self._do_internal(algo, transport_cert, False, ++ False, *args, **options) ++ except errors.EncodingError: ++ result = self._do_internal(algo, transport_cert, False, ++ True, *args, **options) + if result is not None: + return result + +-- +2.44.0 + diff --git a/freeipa.spec b/freeipa.spec index 078c72d..e68c085 100644 --- a/freeipa.spec +++ b/freeipa.spec @@ -223,7 +223,7 @@ Name: %{package_name} Version: %{IPA_VERSION} -Release: 8%{?rc_version:.%rc_version}%{?dist} +Release: 9%{?rc_version:.%rc_version}%{?dist} Summary: The Identity, Policy and Audit system License: GPL-3.0-or-later @@ -306,6 +306,9 @@ Patch0056: 0056-Vault-add-support-for-RSA-OAEP-wrapping-algo.patch Patch0057: 0057-Vault-improve-vault-server-archival-retrieval-calls-.patch Patch0058: 0058-kra-set-RSA-OAEP-as-default-wrapping-algo-when-FIPS-.patch Patch0059: 0059-ipa-kdb-Fix-double-free-in-ipadb_reinit_mspac.patch +Patch0060: 0060-rpcserver-validate-Kerberos-principal-name-before-ru.patch +Patch0061: 0061-validate_principal-Don-t-try-to-verify-that-the-real.patch +Patch0062: 0062-Vault-add-additional-fallback-to-RSA-OAEP-wrapping-a.patch Patch1001: 1001-Change-branding-to-IPA-and-Identity-Management.patch %endif %endif @@ -1798,6 +1801,10 @@ fi %endif %changelog +* Thu Mar 07 2024 Florence Blanc-Renaud - 4.11.0-9 +- Resolves: RHEL-28258 vault fails on non-fips client if server is in FIPS mode +- Resolves: RHEL-26154 ipa: freeipa: specially crafted HTTP requests potentially lead to DoS or data exposure + * Tue Feb 20 2024 Florence Blanc-Renaud - 4.11.0-8 - Resolves: RHEL-12143 'ipa vault-add is failing with ipa: ERROR: an internal error has occurred in FIPS mode - Resolves: RHEL-25738 ipa-kdb: Cannot determine if PAC generator is available