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