diff --git a/0013-ipatests-mark-test_ca_show_error_handling-as-xfail.patch b/0013-ipatests-mark-test_ca_show_error_handling-as-xfail.patch new file mode 100644 index 0000000..047d3f2 --- /dev/null +++ b/0013-ipatests-mark-test_ca_show_error_handling-as-xfail.patch @@ -0,0 +1,45 @@ +From 4521fe5f9125c74b4ad6e4e51f8c66c009079281 Mon Sep 17 00:00:00 2001 +From: Florence Blanc-Renaud +Date: Thu, 13 Jun 2024 10:39:54 +0200 +Subject: [PATCH] ipatests: mark test_ca_show_error_handling as xfail + +With PKI 11.5.0, the test + test_cert.py::TestCAShowErrorHandling::test_ca_show_error_handling +is failing with an exception and a different error message. +Mark as xfail until PKI provides a fix + +Related: https://pagure.io/freeipa/issue/9606 +Signed-off-by: Florence Blanc-Renaud +Reviewed-By: Francisco Trivino +--- + ipatests/test_integration/test_cert.py | 7 ++++++- + 1 file changed, 6 insertions(+), 1 deletion(-) + +diff --git a/ipatests/test_integration/test_cert.py b/ipatests/test_integration/test_cert.py +index 4dd1254a2d16420bb70686f9715497dfb9048ecf..91598b655a8cd6ff92c1a0cf2166c6548a7af758 100644 +--- a/ipatests/test_integration/test_cert.py ++++ b/ipatests/test_integration/test_cert.py +@@ -25,6 +25,7 @@ from pkg_resources import parse_version + + from ipatests.pytest_ipa.integration import tasks + from ipatests.test_integration.base import IntegrationTest ++from ipatests.util import xfail_context + + DEFAULT_RA_AGENT_SUBMITTED_VAL = '19700101000000' + +@@ -555,7 +556,11 @@ class TestCAShowErrorHandling(IntegrationTest): + ) + error_msg = 'ipa: ERROR: The certificate for ' \ + '{} is not available on this server.'.format(lwca) +- assert error_msg in result.stderr_text ++ bad_version = (tasks.get_pki_version(self.master) ++ >= tasks.parse_version('11.5.0')) ++ with xfail_context(bad_version, ++ reason="https://pagure.io/freeipa/issue/9606"): ++ assert error_msg in result.stderr_text + + def test_certmonger_empty_cert_not_segfault(self): + """Test empty cert request doesn't force certmonger to segfault +-- +2.45.2 + diff --git a/0014-ipa-migrate-remove-V-option.patch b/0014-ipa-migrate-remove-V-option.patch new file mode 100644 index 0000000..290733d --- /dev/null +++ b/0014-ipa-migrate-remove-V-option.patch @@ -0,0 +1,47 @@ +From efa57193630f244185b3f295ed0de17c6d08f75a Mon Sep 17 00:00:00 2001 +From: Mark Reynolds +Date: Mon, 8 Jul 2024 10:49:49 -0400 +Subject: [PATCH] ipa-migrate - remove -V option + +The versioning in ipa-migrate was removed, but the "-V" option to display the version was not removed. + +Fixes: https://pagure.io/freeipa/issue/9620 + +Signed-off-by: Mark Reynolds +Reviewed-By: Rob Crittenden +--- + install/tools/man/ipa-migrate.1 | 3 --- + ipaserver/install/ipa_migrate.py | 3 --- + 2 files changed, 6 deletions(-) + +diff --git a/install/tools/man/ipa-migrate.1 b/install/tools/man/ipa-migrate.1 +index 78881d1f8a9ea91d7824e5f8b13f50aecf5ebd16..2d9d2c650a4c44a2f397d1c2ccb42fb95eea2bae 100644 +--- a/install/tools/man/ipa-migrate.1 ++++ b/install/tools/man/ipa-migrate.1 +@@ -67,9 +67,6 @@ Reset the ID range for migrated users/groups. In "stage-mode" this is done autom + \fB\-F\fR, \fB\-\-force\fR + Ignore any errors and continue to proceed with migration effort. + .TP +-\fB\-V\fR, \fB\-\-version\fR +-Display the version of the migration tool. +-.TP + \fB\-q\fR, \fB\-\-quiet\fR + Only log errors during the migration process. + .TP +diff --git a/ipaserver/install/ipa_migrate.py b/ipaserver/install/ipa_migrate.py +index 58351af604b8d6f4ac31432a425718a4d45e0178..6be8d9ba23b36779bf6296df757c1aca551968c0 100644 +--- a/ipaserver/install/ipa_migrate.py ++++ b/ipaserver/install/ipa_migrate.py +@@ -389,9 +389,6 @@ class IPAMigrate(): + parser.add_argument('-F', '--force', + help='Ignore errors and continue with migration', + action='store_true', default=False) +- parser.add_argument('-V', '--version', +- help='Display verison of the migration tool', +- action='store_true', default=False) + parser.add_argument('-q', '--quiet', + help='Only display errors during the migration', + action='store_true', default=False) +-- +2.45.2 + diff --git a/0015-Fix-syntax-error-in-the-selinux-luna-postun-script.patch b/0015-Fix-syntax-error-in-the-selinux-luna-postun-script.patch new file mode 100644 index 0000000..6efea74 --- /dev/null +++ b/0015-Fix-syntax-error-in-the-selinux-luna-postun-script.patch @@ -0,0 +1,37 @@ +From 1b278de4ab9c5e00fb48dc2de1ea31d9bdfc94bc Mon Sep 17 00:00:00 2001 +From: Rob Crittenden +Date: Tue, 9 Jul 2024 14:35:25 -0400 +Subject: [PATCH] Fix syntax error in the selinux-luna %postun script + +It was missing a trailing fi. + +This bad syntax was preventing cleanup of the +{free}ipa-selinux-luna SELinux module: + +Running scriptlet: freeipa-selinux-luna-4.12.0.dev202402211727+git0ee 34/44 +/var/tmp/rpm-tmp.qoCDFi: line 16: syntax error: unexpected end of file +warning: %postun(freeipa-selinux-luna-4.12.0.dev202402211727+git0eeecdcec-0.fc37.noarch) scriptlet failed, exit status + +Fixes: https://pagure.io/freeipa/issue/9629 + +Signed-off-by: Rob Crittenden +Reviewed-By: Florence Blanc-Renaud +--- + freeipa.spec.in | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/freeipa.spec.in b/freeipa.spec.in +index 1e1a0c04728972c6c53beb47dafb25d7898ab0ea..b3b19cf8881db97307836513ff2263dc4fe4ca03 100755 +--- a/freeipa.spec.in ++++ b/freeipa.spec.in +@@ -1367,6 +1367,7 @@ fi + %postun selinux-luna + if [ $1 -eq 0 ]; then + %selinux_modules_uninstall -s %{selinuxtype} %{modulename}-luna ++fi + + %posttrans selinux + %selinux_relabel_post -s %{selinuxtype} +-- +2.45.2 + diff --git a/0016-Re-organize-HSM-validation-to-be-more-consistent-les.patch b/0016-Re-organize-HSM-validation-to-be-more-consistent-les.patch new file mode 100644 index 0000000..88cf761 --- /dev/null +++ b/0016-Re-organize-HSM-validation-to-be-more-consistent-les.patch @@ -0,0 +1,231 @@ +From 7ab1bcb2d364c26024db4ec99c707ebefffcd3e7 Mon Sep 17 00:00:00 2001 +From: Rob Crittenden +Date: Fri, 5 Jul 2024 15:00:59 -0400 +Subject: [PATCH] Re-organize HSM validation to be more consistent/less + duplication + +hsm_validator() was more or less bolted in place late in the +development cycle in in order to catch some of the more common +problems: bad token name, bad password, etc. + +There was a fair bit of duplication and had the side-effect of not +reading in the token password from the --token-password-file option +in some cases. + +This patch also re-adds a lost feature where an exception is raised if +both the --token-password and --token-password-file options are passed +in. + +This also needs to be enforced on initial server, replica and when +called by ipa-kra-install. Given that each has a unique subject of +options some duplication remains. + +Fixes: https://pagure.io/freeipa/issue/9603 + +Signed-off-by: Rob Crittenden +Reviewed-By: Florence Blanc-Renaud +--- + ipaserver/install/ca.py | 72 +++++++++++++++-------------- + ipaserver/install/kra.py | 56 ++++++++++++++++++++-- + ipaserver/install/server/install.py | 2 + + 3 files changed, 93 insertions(+), 37 deletions(-) + +diff --git a/ipaserver/install/ca.py b/ipaserver/install/ca.py +index dc4b47056f0e327d120ab6dad238deae3c26bbcd..b8155d9965712dbce4076e9d73d6712135309ce2 100644 +--- a/ipaserver/install/ca.py ++++ b/ipaserver/install/ca.py +@@ -193,6 +193,8 @@ def hsm_validator(token_name, token_library, token_password): + if not token_name: + logger.debug("No token name, assuming not an HSM install") + return ++ if not token_password: ++ raise ValueError("No token password provided") + val, pki_version = hsm_version() + if val is False: + raise ValueError( +@@ -361,17 +363,16 @@ def install_check(standalone, replica_config, options): + host_name = options.host_name + + if replica_config is None: +- if options.token_name: +- try: +- hsm_validator( +- options.token_name, options.token_library_path, +- options.token_password) +- except ValueError as e: +- raise ScriptError(str(e)) + options._subject_base = options.subject_base + options._ca_subject = options.ca_subject + options._random_serial_numbers = options.random_serial_numbers + token_name = options.token_name ++ token_library_path = options.token_library_path ++ if "setup_ca" in options.__dict__: ++ setup_ca = options.setup_ca ++ else: ++ # We got here through ipa-ca-install ++ setup_ca = True + else: + # during replica install, this gets invoked before local DS is + # available, so use the remote api. +@@ -399,33 +400,36 @@ def install_check(standalone, replica_config, options): + if replica_config.setup_ca and token_name: + if not options.token_library_path: + options.token_library_path = token_library_path +- if ( +- not options.token_password_file +- and not options.token_password +- ): +- if options.unattended: +- raise ScriptError("HSM token password required") +- token_password = installutils.read_password( +- f"HSM token '{token_name}'", confirm=False +- ) +- if token_password is None: +- raise ScriptError("HSM token password required") +- else: +- options.token_password = token_password +- +- if options.token_password_file: +- with open(options.token_password_file, "r") as fd: +- options.token_password = fd.readline().strip() +- try: +- hsm_validator( +- token_name, +- options.token_library_path +- if options.token_library_path +- else token_library_path, +- options.token_password, +- ) +- except ValueError as e: +- raise ScriptError(str(e)) ++ setup_ca = replica_config.setup_ca ++ ++ if setup_ca and token_name: ++ if (options.token_password_file and options.token_password): ++ raise ScriptError( ++ "token-password and token-password-file are mutually exclusive" ++ ) ++ if options.token_password_file: ++ with open(options.token_password_file, "r") as fd: ++ options.token_password = fd.readline().strip() ++ if ( ++ not options.token_password_file ++ and not options.token_password ++ ): ++ if options.unattended: ++ raise ScriptError("HSM token password required") ++ token_password = installutils.read_password( ++ f"HSM token '{token_name}'", confirm=False ++ ) ++ if token_password is None: ++ raise ScriptError("HSM token password required") ++ else: ++ options.token_password = token_password ++ ++ try: ++ hsm_validator( ++ token_name, token_library_path, ++ options.token_password) ++ except ValueError as e: ++ raise ScriptError(str(e)) + + if replica_config is not None and not replica_config.setup_ca: + return +diff --git a/ipaserver/install/kra.py b/ipaserver/install/kra.py +index 2c5b47590c26e37818f055cfd218c85d74e9b46c..dc3bc7c204394187bb7a5c4cc1b863a2091bdc49 100644 +--- a/ipaserver/install/kra.py ++++ b/ipaserver/install/kra.py +@@ -16,10 +16,12 @@ from ipalib.kinit import kinit_keytab + from ipaplatform import services + from ipaplatform.paths import paths + from ipapython import ipautil ++from ipapython.admintool import ScriptError + from ipapython.install.core import group + from ipaserver.install import ca, cainstance + from ipaserver.install import krainstance + from ipaserver.install import dsinstance ++from ipaserver.install import installutils + from ipaserver.install import service as _service + + from . import dogtag +@@ -58,13 +60,61 @@ def install_check(api, replica_config, options): + "KRA can not be installed when 'ca_host' is overriden in " + "IPA configuration file.") + ++ # There are three scenarios for installing a KRA ++ # 1. At install time of the initial server ++ # 2. Using ipa-kra-install ++ # 3. At install time of a replica ++ # ++ # These tests are done in reverse order. If we are doing a ++ # replica install we can check the remote CA. ++ # ++ # If we are running ipa-kra-install then there must be a CA ++ # use that. ++ # ++ # If initial install we either have the token options or we don't. ++ ++ cai = cainstance.CAInstance() ++ if replica_config is not None: ++ (token_name, token_library_path) = ca.lookup_hsm_configuration(api) ++ elif cai.is_configured() and cai.hsm_enabled: ++ (token_name, token_library_path) = ca.lookup_hsm_configuration(api) ++ elif 'token_name' in options.__dict__: ++ token_name = options.token_name ++ token_library_path = options.token_library_path ++ else: ++ token_name = None ++ ++ if replica_config is not None: ++ if ( ++ token_name ++ and options.token_password_file ++ and options.token_password ++ ): ++ raise ScriptError( ++ "token-password and token-password-file are mutually exclusive" ++ ) ++ + if options.token_password_file: + with open(options.token_password_file, "r") as fd: + options.token_password = fd.readline().strip() + +- if replica_config is not None: +- (token_name, token_library) = ca.lookup_hsm_configuration(api) +- ca.hsm_validator(token_name, token_library, options.token_password) ++ if ( ++ token_name ++ and not options.token_password_file ++ and not options.token_password ++ ): ++ if options.unattended: ++ raise ScriptError("HSM token password required") ++ token_password = installutils.read_password( ++ f"HSM token '{token_name}'", confirm=False ++ ) ++ if token_password is None: ++ raise ScriptError("HSM token password required") ++ else: ++ options.token_password = token_password ++ ++ if token_name: ++ ca.hsm_validator(token_name, token_library_path, options.token_password) + + + def install(api, replica_config, options, custodia): +diff --git a/ipaserver/install/server/install.py b/ipaserver/install/server/install.py +index 1b18873363cece5e187a7c772acfcbc6c565ee97..47db1314239906a10bb77e5fc0d4c1eddc02e2da 100644 +--- a/ipaserver/install/server/install.py ++++ b/ipaserver/install/server/install.py +@@ -663,6 +663,8 @@ def install_check(installer): + options.token_name is not None + ) + ): ++ if options.unattended: ++ raise ScriptError("HSM token password required") + token_password = read_password( + f"HSM token '{options.token_name}'" , confirm=False) + if token_password is None: +-- +2.45.2 + diff --git a/0017-ipatests-tests-related-to-token-password-file.patch b/0017-ipatests-tests-related-to-token-password-file.patch new file mode 100644 index 0000000..f297331 --- /dev/null +++ b/0017-ipatests-tests-related-to-token-password-file.patch @@ -0,0 +1,192 @@ +From 4ea1ad6acae910574a524403bc82c80d24b525d6 Mon Sep 17 00:00:00 2001 +From: Mohammad Rizwan +Date: Thu, 13 Jun 2024 14:07:57 +0530 +Subject: [PATCH] ipatests: tests related to --token-password-file + +Test automation added around the --token-password-file +option for server/replica/kra install. + +Related: https://pagure.io/freeipa/issue/9603 + +Signed-off-by: Mohammad Rizwan +Reviewed-By: Florence Blanc-Renaud +--- + ipatests/test_integration/test_hsm.py | 85 ++++++++++++++++++++++++--- + 1 file changed, 77 insertions(+), 8 deletions(-) + +diff --git a/ipatests/test_integration/test_hsm.py b/ipatests/test_integration/test_hsm.py +index b49af12492f7dce4bd41836b220d75d9fc99b5c2..3a33c3bda6d072aa16e361b04ac2d668902bb0e9 100644 +--- a/ipatests/test_integration/test_hsm.py ++++ b/ipatests/test_integration/test_hsm.py +@@ -163,6 +163,7 @@ class BaseHSMTest(IntegrationTest): + master_extra_args = [] + token_password = None + token_name = None ++ token_password_file = '/tmp/token_password' + random_serial = False + + @classmethod +@@ -191,7 +192,7 @@ class BaseHSMTest(IntegrationTest): + delete_hsm_token([cls.master] + cls.replicas, cls.token_name) + + @classmethod +- def sync_tokens(cls, source): ++ def sync_tokens(cls, source, token_name=None): + """Synchronize non-networked HSM tokens between machines + source: source host for the token data + """ +@@ -207,7 +208,8 @@ class BaseHSMTest(IntegrationTest): + for host in [cls.master] + cls.replicas: + if host == source: + continue +- copy_token_files(source, [host], cls.token_name) ++ copy_token_files(source, [host], ++ token_name if token_name else cls.token_name) + + + class TestHSMInstall(BaseHSMTest): +@@ -218,6 +220,10 @@ class TestHSMInstall(BaseHSMTest): + + def test_hsm_install_replica0_ca_less_install(self): + check_version(self.master) ++ ++ self.master.put_file_contents( ++ self.token_password_file, self.token_password ++ ) + tasks.install_replica( + self.master, self.replicas[0], setup_ca=False, + setup_dns=True, +@@ -307,6 +313,50 @@ class TestHSMInstall(BaseHSMTest): + assert returncode == 0 + assert output == "No issues found." + ++ def test_hsm_install_server_password_file(self): ++ check_version(self.master) ++ # cleanup before fresh install with password file ++ for client in self.clients: ++ tasks.uninstall_client(client) ++ ++ for replica in self.replicas: ++ tasks.uninstall_master(replica) ++ ++ tasks.uninstall_master(self.master) ++ ++ delete_hsm_token([self.master] + self.replicas, self.token_name) ++ self.token_name, self.token_password = get_hsm_token(self.master) ++ self.master.put_file_contents(self.token_password_file, ++ self.token_password) ++ self.replicas[0].put_file_contents(self.token_password_file, ++ self.token_password) ++ ++ tasks.install_master( ++ self.master, setup_dns=self.master_with_dns, ++ setup_kra=self.master_with_kra, ++ setup_adtrust=self.master_with_ad, ++ extra_args=( ++ '--token-name', self.token_name, ++ '--token-library-path', hsm_lib_path, ++ '--token-password-file', self.token_password_file ++ ) ++ ) ++ self.sync_tokens(self.master, token_name=self.token_name) ++ ++ def test_hsm_install_replica0_password_file(self): ++ check_version(self.master) ++ tasks.install_replica( ++ self.master, self.replicas[0], setup_ca=True, ++ extra_args=('--token-password-file', self.token_password_file,) ++ ) ++ ++ def test_hsm_install_replica0_kra_password_file(self): ++ check_version(self.master) ++ tasks.install_kra( ++ self.replicas[0], ++ extra_args=('--token-password-file', self.token_password_file,) ++ ) ++ + + class TestHSMInstallADTrustBase(BaseHSMTest): + """ +@@ -321,7 +371,7 @@ class TestHSMInstallADTrustBase(BaseHSMTest): + check_version(self.master) + tasks.install_replica( + self.master, self.replicas[0], setup_ca=True, +- setup_adtrust=True, setup_kra=True, setup_dns=True, ++ setup_adtrust=False, setup_kra=True, setup_dns=True, + nameservers='master' if self.master_with_dns else None, + extra_args=('--token-password', self.token_password,) + ) +@@ -356,7 +406,8 @@ class TestHSMcertRenewal(BaseHSMTest): + 'auditSigningCert cert-pki-ca': 'caauditSigningCert' + } + CA_TRACKING_REQS.update(KRA_TRACKING_REQS) +- self.master.put_file_contents('/tmp/token_passwd', self.token_password) ++ self.master.put_file_contents(self.token_password_file, ++ self.token_password) + for nickname in CA_TRACKING_REQS: + cert = tasks.certutil_fetch_cert( + self.master, +@@ -772,6 +823,7 @@ class TestHSMcertFixReplica(BaseHSMTest): + class TestHSMNegative(IntegrationTest): + + master_with_dns = False ++ token_password_file = '/tmp/token_password' + + @classmethod + def install(cls, mh): +@@ -792,7 +844,6 @@ class TestHSMNegative(IntegrationTest): + '--token-password', self.token_password + ) + ) +- # assert 'error message non existing token name' in result.stderr_text + assert result.returncode != 0 + + # wrong token password +@@ -804,7 +855,6 @@ class TestHSMNegative(IntegrationTest): + '--token-password', 'token_passwd' + ) + ) +- # assert 'error message wrong passwd' in result.stderr_text + assert result.returncode != 0 + + # wrong token lib +@@ -816,7 +866,6 @@ class TestHSMNegative(IntegrationTest): + '--token-password', self.token_password + ) + ) +- # assert 'error message non existing token lib' in result.stderr_text + assert result.returncode != 0 + + def test_hsm_negative_special_char_token_name(self): +@@ -842,7 +891,27 @@ class TestHSMNegative(IntegrationTest): + '--token-password', token_passwd + ) + ) +- # assert 'error message non existing token lib' in result.stderr_text ++ assert result.returncode != 0 ++ ++ def test_hsm_negative_token_password_and_file(self): ++ """Test token-password and token-password-file at same time ++ ++ Test if command fails when --token-password and --token-password-file ++ provided at the same time results into command failure. ++ """ ++ check_version(self.master) ++ self.master.put_file_contents( ++ self.token_password_file, self.token_password ++ ) ++ result = tasks.install_master( ++ self.master, raiseonerr=False, ++ extra_args=( ++ '--token-name', self.token_name, ++ '--token-library-path', hsm_lib_path, ++ '--token-password', self.token_password, ++ '--token-password-file', self.token_password_file ++ ) ++ ) + assert result.returncode != 0 + + +-- +2.45.2 + diff --git a/0018-Include-token-password-options-in-ipa-kra-install-ma.patch b/0018-Include-token-password-options-in-ipa-kra-install-ma.patch new file mode 100644 index 0000000..f55217b --- /dev/null +++ b/0018-Include-token-password-options-in-ipa-kra-install-ma.patch @@ -0,0 +1,36 @@ +From 6c53a22a2cacf7807df11e51492d1a2c42aeeda1 Mon Sep 17 00:00:00 2001 +From: Rob Crittenden +Date: Tue, 18 Jun 2024 11:16:07 -0400 +Subject: [PATCH] Include token password options in ipa-kra-install man page + +Related: https://pagure.io/freeipa/issue/9603 + +Signed-off-by: Rob Crittenden +Reviewed-By: Florence Blanc-Renaud +--- + install/tools/man/ipa-kra-install.1 | 9 +++++++++ + 1 file changed, 9 insertions(+) + +diff --git a/install/tools/man/ipa-kra-install.1 b/install/tools/man/ipa-kra-install.1 +index 5476a4e717584cd7c6f823e3c3cb4e4948f14875..955085bf7162863a0567356417a0886e733c0b42 100644 +--- a/install/tools/man/ipa-kra-install.1 ++++ b/install/tools/man/ipa-kra-install.1 +@@ -54,6 +54,15 @@ Log to the given file + .TP + \fB\-\-pki\-config\-override\fR=\fIFILE\fR + File containing overrides for KRA installation. ++.SS "HSM OPTIONS" ++The token name and library path are retrieved from the existing ++installation. ++.TP ++\fB\-\-token\-password\fR=\fITOKEN_PASSWORD\fR ++The PKCS#11 token password for the HSM. ++.TP ++\fB\-\-token\-password\-file\fR=\fITOKEN_PASSWORD_FILE\fR ++The full path to a file containing the PKCS#11 token password. + .SH "EXIT STATUS" + 0 if the command was successful + +-- +2.45.2 + diff --git a/0019-ipa-migrate-starttls-does-not-work.patch b/0019-ipa-migrate-starttls-does-not-work.patch new file mode 100644 index 0000000..954e92d --- /dev/null +++ b/0019-ipa-migrate-starttls-does-not-work.patch @@ -0,0 +1,86 @@ +From eeade50933cb2251b43ee34c642bcae69a216655 Mon Sep 17 00:00:00 2001 +From: Mark Reynolds +Date: Mon, 8 Jul 2024 10:20:47 -0400 +Subject: [PATCH] ipa-migrate - starttls does not work + +We were previousily taking the provided ca cert and creating a temporary +file from it. This was incorrect and caused the secure connection to +fail. Instead just use the file path provided. + +Fixes: https://pagure.io/freeipa/issue/9619 + +Signed-off-by: Mark Reynolds +Reviewed-By: Rob Crittenden +Reviewed-By: Florence Blanc-Renaud +--- + install/tools/man/ipa-migrate.1 | 2 +- + ipaserver/install/ipa_migrate.py | 25 +++++++++++++++++-------- + 2 files changed, 18 insertions(+), 9 deletions(-) + +diff --git a/install/tools/man/ipa-migrate.1 b/install/tools/man/ipa-migrate.1 +index 2d9d2c650a4c44a2f397d1c2ccb42fb95eea2bae..47ae47ea4afa3a5a6fe25dd9bbd14c27ab5f1fdb 100644 +--- a/install/tools/man/ipa-migrate.1 ++++ b/install/tools/man/ipa-migrate.1 +@@ -25,7 +25,7 @@ network interruptions) + In this mode everything will be migrated including the current user SIDs and + DNA ranges + .TP +-\fBstage\-mod\fR ++\fBstage\-mode\fR + In this mode, SIDs & DNA ranges are not migrated, and DNA attributes are reset + + .SH "COMMANDS" +diff --git a/ipaserver/install/ipa_migrate.py b/ipaserver/install/ipa_migrate.py +index 6be8d9ba23b36779bf6296df757c1aca551968c0..0e19b98b5be532c513876e165561f0af176baa27 100644 +--- a/ipaserver/install/ipa_migrate.py ++++ b/ipaserver/install/ipa_migrate.py +@@ -27,7 +27,6 @@ from ipalib.x509 import IPACertificate + from ipaplatform.paths import paths + from ipapython.dn import DN + from ipapython.ipaldap import LDAPClient, LDAPEntry, realm_to_ldapi_uri +-from ipapython.ipautil import write_tmp_file + from ipapython.ipa_log_manager import standard_logging_setup + from ipaserver.install.ipa_migrate_constants import ( + DS_CONFIG, DB_OBJECTS, DS_INDEXES, BIND_DN, LOG_FILE_NAME, +@@ -758,13 +757,19 @@ class IPAMigrate(): + insecure_bind = False + + if self.args.cacertfile is not None: +- # Store CA cert into file +- tmp_ca_cert_f = write_tmp_file(self.args.cacertfile) +- cacert = tmp_ca_cert_f.name +- + # Start TLS connection (START_TLS) +- ds_conn = LDAPClient(ldapuri, cacert=cacert, start_tls=True) +- tmp_ca_cert_f.close() ++ try: ++ ds_conn = LDAPClient(ldapuri, cacert=self.args.cacertfile, ++ start_tls=True) ++ except ( ++ ldap.LDAPError, ++ errors.NetworkError, ++ errors.DatabaseError, ++ IOError ++ ) as e: ++ self.handle_error( ++ f"Failed to connect to remote server: {str(e)}" ++ ) + else: + # LDAP (insecure) + ds_conn = LDAPClient(ldapuri) +@@ -773,7 +778,11 @@ class IPAMigrate(): + try: + ds_conn.simple_bind(DN(self.args.bind_dn), self.bindpw, + insecure_bind=insecure_bind) +- except (errors.NetworkError, errors.ACIError) as e: ++ except ( ++ errors.NetworkError, ++ errors.ACIError, ++ errors.DatabaseError ++ ) as e: + self.handle_error(f"Failed to bind to remote server: {str(e)}") + + # All set, stash the remote connection +-- +2.45.2 + diff --git a/0020-ipa-pwd-extop-differentiate-OTP-requirements-in-LDAP.patch b/0020-ipa-pwd-extop-differentiate-OTP-requirements-in-LDAP.patch new file mode 100644 index 0000000..62eaadb --- /dev/null +++ b/0020-ipa-pwd-extop-differentiate-OTP-requirements-in-LDAP.patch @@ -0,0 +1,232 @@ +From 051d61fdc301f2768ac78c45e93a5f9eeff8aa28 Mon Sep 17 00:00:00 2001 +From: Alexander Bokovoy +Date: Tue, 25 Jun 2024 14:27:24 +0300 +Subject: [PATCH] ipa-pwd-extop: differentiate OTP requirements in LDAP binds + +For users who has no OTP tokens defined (yet), a missing token should +not be seen as a failure. This is needed to allow a basic password +change. + +The logic around enforcement of OTP over LDAP bind is the following: +---------------------------------------------------------------------- +- when LDAP OTP control is requested by the LDAP client, OTP is + explicitly required +- when EnforceLDAPOTP is set in the IPA configuration, OTP is implicitly + required, regardless of the state of LDAP client + +In either case, only users with 'user-auth-type: otp' are allowed to +authenticate. + +If these users have no OTP token associated yet, they will be allowed to +authenticate with their password. This is to allow initial password +change and adding an OTP token. +---------------------------------------------------------------------- + +Implement test that simulates lifecycle for new user who get to change +their password before adding an OTP token. + +Related: https://pagure.io/freeipa/issue/5169 + +Signed-off-by: Alexander Bokovoy +Reviewed-By: Florence Blanc-Renaud +Reviewed-By: Rob Crittenden +--- + .../ipa-slapi-plugins/ipa-pwd-extop/prepost.c | 39 ++++++++++---- + ipatests/test_integration/test_otp.py | 52 ++++++++++++++++--- + 2 files changed, 76 insertions(+), 15 deletions(-) + +diff --git a/daemons/ipa-slapi-plugins/ipa-pwd-extop/prepost.c b/daemons/ipa-slapi-plugins/ipa-pwd-extop/prepost.c +index cc170fc4b81f8ecad88f4ff4401b5651c43aaf55..c967e2cfffbd920280639f3188783ec150523b47 100644 +--- a/daemons/ipa-slapi-plugins/ipa-pwd-extop/prepost.c ++++ b/daemons/ipa-slapi-plugins/ipa-pwd-extop/prepost.c +@@ -1212,13 +1212,20 @@ done: + * value at the end. This leaves only the password in creds for later + * validation. + */ ++typedef enum { ++ OTP_IS_NOT_REQUIRED = 0, ++ OTP_IS_REQUIRED_EXPLICITLY, ++ OTP_IS_REQUIRED_IMPLICITLY ++} otp_req_enum; + static bool ipapwd_pre_bind_otp(const char *bind_dn, Slapi_Entry *entry, +- struct berval *creds, bool otpreq) ++ struct berval *creds, otp_req_enum otpreq, ++ bool *notokens) + { + uint32_t auth_types; + + /* Get the configured authentication types. */ + auth_types = otp_config_auth_types(otp_config, entry); ++ *notokens = false; + + /* + * IMPORTANT SECTION! +@@ -1248,7 +1255,11 @@ static bool ipapwd_pre_bind_otp(const char *bind_dn, Slapi_Entry *entry, + /* With no tokens, succeed if tokens aren't required. */ + if (tokens[0] == NULL) { + otp_token_free_array(tokens); +- return !otpreq; ++ *notokens = true; ++ if (otpreq != OTP_IS_NOT_REQUIRED) ++ /* DENY: OTP is required, either explicitly or implicitly */ ++ return false; ++ return true; + } + + if (otp_token_validate_berval(tokens, creds, NULL)) { +@@ -1259,7 +1270,8 @@ static bool ipapwd_pre_bind_otp(const char *bind_dn, Slapi_Entry *entry, + otp_token_free_array(tokens); + } + +- return (auth_types & OTP_CONFIG_AUTH_TYPE_PASSWORD) && !otpreq; ++ return (auth_types & OTP_CONFIG_AUTH_TYPE_PASSWORD) && ++ (otpreq == OTP_IS_NOT_REQUIRED); + } + + static int ipapwd_authenticate(const char *dn, Slapi_Entry *entry, +@@ -1452,6 +1464,7 @@ static int ipapwd_pre_bind(Slapi_PBlock *pb) + struct tm expire_tm; + int rc = LDAP_INVALID_CREDENTIALS; + char *errMesg = NULL; ++ bool notokens = false; + + /* get BIND parameters */ + ret |= slapi_pblock_get(pb, SLAPI_BIND_TARGET_SDN, &target_sdn); +@@ -1510,8 +1523,9 @@ static int ipapwd_pre_bind(Slapi_PBlock *pb) + + /* Try to do OTP first. */ + syncreq = otpctrl_present(pb, OTP_SYNC_REQUEST_OID); +- otpreq = otpctrl_present(pb, OTP_REQUIRED_OID); +- if (!syncreq && !otpreq) { ++ otpreq = otpctrl_present(pb, OTP_REQUIRED_OID) ? ++ OTP_IS_REQUIRED_EXPLICITLY : OTP_IS_NOT_REQUIRED; ++ if (!syncreq && (otpreq == OTP_IS_NOT_REQUIRED)) { + ret = ipapwd_gen_checks(pb, &errMesg, &krbcfg, IPAPWD_CHECK_ONLY_CONFIG); + if (ret != 0) { + LOG_FATAL("ipapwd_gen_checks failed!?\n"); +@@ -1520,11 +1534,17 @@ static int ipapwd_pre_bind(Slapi_PBlock *pb) + return 0; + } + if (krbcfg->enforce_ldap_otp) { +- otpreq = true; ++ otpreq = OTP_IS_REQUIRED_IMPLICITLY; + } + } +- if (!syncreq && !ipapwd_pre_bind_otp(dn, entry, credentials, otpreq)) +- goto invalid_creds; ++ if (!syncreq && !ipapwd_pre_bind_otp(dn, entry, ++ credentials, otpreq, ¬okens)) { ++ /* We got here because ipapwd_pre_bind_otp() returned false, ++ * it means that either token verification failed or ++ * a rule for empty tokens failed current policy. */ ++ if (!(notokens || (otpreq == OTP_IS_NOT_REQUIRED))) ++ goto invalid_creds; ++ } + + /* Ensure that there is a password. */ + if (credentials->bv_len == 0) { +@@ -1561,7 +1581,8 @@ static int ipapwd_pre_bind(Slapi_PBlock *pb) + * for access log to notice multi-factor authentication has happened + * https://www.port389.org/docs/389ds/design/mfa-operation-note-design.html + */ +- if (!syncreq && otpreq) { ++ if (!syncreq && ++ ((otpreq != OTP_IS_NOT_REQUIRED) && !notokens)) { + slapi_pblock_set_flag_operation_notes(pb, SLAPI_OP_NOTE_MFA_AUTH); + } + #endif +diff --git a/ipatests/test_integration/test_otp.py b/ipatests/test_integration/test_otp.py +index d2dfca4cbf8c60955e888b6f92bd88a2608bb265..350371bfe1e4c1cc6dcc89f6584f813fcb0d32a0 100644 +--- a/ipatests/test_integration/test_otp.py ++++ b/ipatests/test_integration/test_otp.py +@@ -458,41 +458,81 @@ class TestOTPToken(IntegrationTest): + master = self.master + basedn = master.domain.basedn + USER1 = 'user-forced-otp' ++ TMP_PASSWORD = 'Secret1234509' + binddn = DN(f"uid={USER1},cn=users,cn=accounts,{basedn}") + +- tasks.create_active_user(master, USER1, PASSWORD) + tasks.kinit_admin(master) ++ master.run_command(['ipa', 'pwpolicy-mod', '--minlife', '0']) ++ tasks.user_add(master, USER1, password=TMP_PASSWORD) + # Enforce use of OTP token for this user + master.run_command(['ipa', 'user-mod', USER1, + '--user-auth-type=otp']) + try: ++ # Change initial password through the IPA endpoint ++ url = f'https://{master.hostname}/ipa/session/change_password' ++ master.run_command(['curl', '-d', f'user={USER1}', ++ '-d', f'old_password={TMP_PASSWORD}', ++ '-d', f'new_password={PASSWORD}', ++ '--referer', f'https://{master.hostname}/ipa', ++ url]) + conn = master.ldap_connect() + # First, attempt authenticating with a password but without LDAP + # control to enforce OTP presence and without server-side + # enforcement of the OTP presence check. + conn.simple_bind(binddn, f"{PASSWORD}") +- # Add an OTP token now +- otpuid, totp = add_otptoken(master, USER1, otptype="totp") + # Next, enforce Password+OTP for a user with OTP token + master.run_command(['ipa', 'config-mod', '--addattr', + 'ipaconfigstring=EnforceLDAPOTP']) ++ # Try to bind without OTP because there is no OTP token yet, ++ # the operation should succeed because OTP enforcement is implicit ++ # and there is no token yet, so it is allowed. ++ conn.simple_bind(binddn, f"{PASSWORD}") ++ conn.unbind() ++ # Add an OTP token now ++ otpuid, totp = add_otptoken(master, USER1, otptype="totp") + # Next, authenticate with Password+OTP and with the LDAP control + # this operation should succeed + otpvalue = totp.generate(int(time.time())).decode("ascii") ++ conn = master.ldap_connect() + conn.simple_bind(binddn, f"{PASSWORD}{otpvalue}", + client_controls=[ + BooleanControl( + controlType="2.16.840.1.113730.3.8.10.7", + booleanValue=True)]) +- # Remove token +- del_otptoken(self.master, otpuid) ++ conn.unbind() ++ # Sleep to make sure we are going to use a different token value ++ time.sleep(45) ++ # Use OTP token again, without LDAP control, should succeed ++ # because OTP enforcement is implicit ++ otpvalue = totp.generate(int(time.time())).decode("ascii") ++ conn = master.ldap_connect() ++ conn.simple_bind(binddn, f"{PASSWORD}{otpvalue}") ++ conn.unbind() + # Now, try to authenticate without otp and without control +- # this operation should fail ++ # this operation should fail because we have OTP token associated ++ # with the user account + try: ++ conn = master.ldap_connect() + conn.simple_bind(binddn, f"{PASSWORD}") ++ conn.unbind() + except errors.ACIError: + pass ++ # Sleep to make sure we are going to use a different token value ++ time.sleep(45) ++ # Use OTP token again, without LDAP control, should succeed ++ # because OTP enforcement is implicit ++ otpvalue = totp.generate(int(time.time())).decode("ascii") ++ # Finally, change password again, now that otp is present ++ master.run_command(['curl', '-d', f'user={USER1}', ++ '-d', f'old_password={PASSWORD}', ++ '-d', f'new_password={TMP_PASSWORD}0', ++ '-d', f'otp={otpvalue}', ++ '--referer', f'https://{master.hostname}/ipa', ++ url]) ++ # Remove token ++ del_otptoken(self.master, otpuid) + master.run_command(['ipa', 'config-mod', '--delattr', + 'ipaconfigstring=EnforceLDAPOTP']) + finally: ++ master.run_command(['ipa', 'pwpolicy-mod', '--minlife', '1']) + master.run_command(['ipa', 'user-del', USER1]) +-- +2.45.2 + diff --git a/0021-ipatests-Test-replica-installation-using-AD-admin.patch b/0021-ipatests-Test-replica-installation-using-AD-admin.patch new file mode 100644 index 0000000..9a8da53 --- /dev/null +++ b/0021-ipatests-Test-replica-installation-using-AD-admin.patch @@ -0,0 +1,74 @@ +From 8b703150a47bf509f37856bdc27cfa99e85e5e6b Mon Sep 17 00:00:00 2001 +From: Anuja More +Date: Mon, 24 Jun 2024 13:48:24 +0530 +Subject: [PATCH] ipatests: Test replica installation using AD admin. + +Test to verify that replica connection check is not failing when +the AD administrator Administrator@AD.EXAMPLE.COM is +used for the deployment or promotion of a replica + +Related: https://pagure.io/freeipa/issue/9542 + +Signed-off-by: Anuja More +Reviewed-By: Florence Blanc-Renaud +--- + .../test_replica_promotion.py | 46 +++++++++++++++++++ + 1 files changed, 46 insertions(+) + +diff --git a/ipatests/test_integration/test_replica_promotion.py b/ipatests/test_integration/test_replica_promotion.py +index 7ef44c571c8a4106577d27f4712f661be873dacc..c754cef88cb275987f5afdaad43f2ea07e3b7476 100644 +--- a/ipatests/test_integration/test_replica_promotion.py ++++ b/ipatests/test_integration/test_replica_promotion.py +@@ -1318,3 +1318,49 @@ class TestHiddenReplicaKRA(IntegrationTest): + self.replicas[0].hostname, '--state=hidden' + ]) + assert result.returncode == 0 ++ ++ ++class TestReplicaConn(IntegrationTest): ++ num_replicas = 1 ++ num_ad_domains = 1 ++ ++ @classmethod ++ def install(cls, mh): ++ cls.replica = cls.replicas[0] ++ cls.ad = cls.ads[0] ++ ad_domain = cls.ad.domain.name ++ cls.ad_admin = 'Administrator@{}'.format(ad_domain.upper()) ++ cls.adview = 'Default Trust View' ++ tasks.install_master(cls.master, setup_adtrust=True) ++ tasks.configure_dns_for_trust(cls.master, cls.ad) ++ tasks.establish_trust_with_ad(cls.master, cls.ad.domain.name) ++ tasks.install_client(cls.master, cls.replica) ++ ++ def test_replica_conncheck_ad_admin(self): ++ """ ++ Test to verify that replica installation is not failing for ++ replica connection check when AD administrator ++ Administrator@AD.EXAMPLE.COM is used for the deployment ++ or promotion of a replica. ++ ++ Related : https://pagure.io/freeipa/issue/9542 ++ """ ++ self.master.run_command( ++ ['ipa', 'idoverrideuser-add', self.adview, self.ad_admin] ++ ) ++ self.master.run_command( ++ ["ipa", "group-add-member", "admins", "--idoverrideusers", ++ self.ad_admin] ++ ) ++ tasks.clear_sssd_cache(self.master) ++ ++ self.replica.run_command( ++ ["ipa-replica-install", "--setup-ca", "-U", "--ip-address", ++ self.replica.ip, "--realm", self.replica.domain.realm, ++ "--domain", self.replica.domain.name, ++ "--principal={0}".format(self.ad_admin), ++ "--password", self.master.config.ad_admin_password] ++ ) ++ logs = self.replica.get_file_contents(paths.IPAREPLICA_CONNCHECK_LOG) ++ error = "not allowed to perform server connection check" ++ assert error.encode() not in logs +-- +2.45.2 + diff --git a/0022-Issue-9621-ipa-migrate-should-not-update-mapped-attr.patch b/0022-Issue-9621-ipa-migrate-should-not-update-mapped-attr.patch new file mode 100644 index 0000000..b0453bf --- /dev/null +++ b/0022-Issue-9621-ipa-migrate-should-not-update-mapped-attr.patch @@ -0,0 +1,46 @@ +From 85a853ba93c1d23d5bad13a1ae2bee802dc90131 Mon Sep 17 00:00:00 2001 +From: Mark Reynolds +Date: Mon, 8 Jul 2024 11:25:53 -0400 +Subject: [PATCH] Issue 9621 - ipa-migrate - should not update mapped + attributes in managed entries + +We should not migrate mmapped attributes (uidNumber, gidNumber) from +managed entries + +We should also not migrate DNA ranges in staging mode + +Fixes: https://pagure.io/freeipa/issue/9621 + +Signed-off-by: Mark Reynolds +Reviewed-By: Rob Crittenden +--- + ipaserver/install/ipa_migrate.py | 6 ++++++ + 1 file changed, 6 insertions(+) + +diff --git a/ipaserver/install/ipa_migrate.py b/ipaserver/install/ipa_migrate.py +index 0e19b98b5be532c513876e165561f0af176baa27..20f59f84db21022b66c0aa1ffd696d99aef85a44 100644 +--- a/ipaserver/install/ipa_migrate.py ++++ b/ipaserver/install/ipa_migrate.py +@@ -1322,6 +1322,9 @@ class IPAMigrate(): + self.args.reset_range + or self.mode == "stage-mode" + ) and attr.lower() in DNA_REGEN_ATTRS: ++ # Skip dna attributes from managed entries ++ if 'mepManagedBy' in local_entry: ++ break + # Ok, set the magic regen value + local_entry[attr] = [DNA_REGEN_VAL] + self.log_debug("Resetting the DNA range for: " +@@ -1816,6 +1819,9 @@ class IPAMigrate(): + # processing the entries + for entry in remote_dse: + for dse_item in DS_CONFIG.items(): ++ if dse_item[0] == "dna" and self.mode == "stage-mode": ++ # Do not migrate DNA ranges in staging mode ++ continue + dse = dse_item[1] + for dn in dse['dn']: + if DN(dn) == DN(entry['dn']): +-- +2.45.2 + diff --git a/freeipa.spec b/freeipa.spec index f89c109..f76151e 100644 --- a/freeipa.spec +++ b/freeipa.spec @@ -224,7 +224,7 @@ Name: %{package_name} Version: %{IPA_VERSION} -Release: 5%{?rc_version:.%rc_version}%{?dist} +Release: 6%{?rc_version:.%rc_version}%{?dist} Summary: The Identity, Policy and Audit system License: GPL-3.0-or-later @@ -260,6 +260,16 @@ Patch0009: 0009-PKINIT-certificate-fix-renewal-on-hidden-replica.patch Patch0010: 0010-ipatests-add-test-for-PKINIT-renewal-on-hidden-repli.patch Patch0011: 0011-ipatests-Tests-for-ipa-ipa-migration-tool.patch Patch0012: 0012-ipa_sidgen-Allow-sidgen_task-to-continue-after-findi.patch +Patch0013: 0013-ipatests-mark-test_ca_show_error_handling-as-xfail.patch +Patch0014: 0014-ipa-migrate-remove-V-option.patch +Patch0015: 0015-Fix-syntax-error-in-the-selinux-luna-postun-script.patch +Patch0016: 0016-Re-organize-HSM-validation-to-be-more-consistent-les.patch +Patch0017: 0017-ipatests-tests-related-to-token-password-file.patch +Patch0018: 0018-Include-token-password-options-in-ipa-kra-install-ma.patch +Patch0019: 0019-ipa-migrate-starttls-does-not-work.patch +Patch0020: 0020-ipa-pwd-extop-differentiate-OTP-requirements-in-LDAP.patch +Patch0021: 0021-ipatests-Test-replica-installation-using-AD-admin.patch +Patch0022: 0022-Issue-9621-ipa-migrate-should-not-update-mapped-attr.patch Patch1001: 1001-Change-branding-to-IPA-and-Identity-Management.patch %endif %endif @@ -1359,6 +1369,7 @@ fi %postun selinux-luna if [ $1 -eq 0 ]; then %selinux_modules_uninstall -s %{selinuxtype} %{modulename}-luna +fi %posttrans selinux %selinux_relabel_post -s %{selinuxtype} @@ -1872,6 +1883,15 @@ fi %endif %changelog +* Thu Jul 18 2024 Florence Blanc-Renaud - 4.12.0-6 +- Resolves: RHEL-47292 Include latest fixes in python3-ipatests packages +- Resolves: RHEL-47146 Syntax error uninstalling the selinux-luna subpackage +- Resolves: RHEL-46009 ipa-migrate with -Z option fails with ValueError: option error +- Resolves: RHEL-46003 ipa-migrate -V options fails to display version +- Resolves: RHEL-45463 ipa-migrate stage-mode is failing with error: Modifying a mapped attribute in a managed entry is not allowed +- Resolves: RHEL-40890 ipa-server-install: token_password_file read in kra.install_check after calling hsm_validator in ca.install_check +- Resolves: RHEL-40661 Adjust "ipa config-mod --addattr ipaconfigstring=EnforceLDAPOTP" to allow for non OTP users in some cases + * Mon Jul 08 2024 Florence Blanc-Renaud - 4.12.0-5 - Resolves: RHEL-37285 IPA Web UI not showing replication agreement for non-admin users - Resolves: RHEL-42703 PSKC.xml issues with ipa_otptoken_import.py