diff options
-rw-r--r-- | base/common/python/pki/nssdb.py | 27 | ||||
-rw-r--r-- | base/server/python/pki/server/__init__.py | 106 | ||||
-rw-r--r-- | base/server/python/pki/server/cli/ca.py | 10 | ||||
-rw-r--r-- | base/server/python/pki/server/cli/instance.py | 185 | ||||
-rw-r--r-- | base/server/python/pki/server/cli/kra.py | 10 | ||||
-rw-r--r-- | base/server/python/pki/server/cli/ocsp.py | 7 | ||||
-rw-r--r-- | base/server/python/pki/server/cli/tks.py | 7 | ||||
-rw-r--r-- | base/server/python/pki/server/cli/tps.py | 7 | ||||
-rw-r--r-- | base/server/python/pki/server/deployment/scriptlets/configuration.py | 2 | ||||
-rw-r--r-- | base/server/python/pki/server/deployment/scriptlets/security_databases.py | 25 |
10 files changed, 362 insertions, 24 deletions
diff --git a/base/common/python/pki/nssdb.py b/base/common/python/pki/nssdb.py index b2cf9f1cf..8d0f96711 100644 --- a/base/common/python/pki/nssdb.py +++ b/base/common/python/pki/nssdb.py @@ -377,7 +377,8 @@ class NSSDatabase(object): subprocess.check_call(cmd) - def import_cert_chain(self, nickname, cert_chain_file, trust_attributes=None): + def import_cert_chain(self, nickname, cert_chain_file, + trust_attributes=None): tmpdir = tempfile.mkdtemp() @@ -389,16 +390,18 @@ class NSSDatabase(object): nickname=nickname, cert_file=cert_chain_file, trust_attributes=trust_attributes) - return self.get_cert( - nickname=nickname, - output_format='base64') + return ( + self.get_cert(nickname=nickname, output_format='base64'), + [nickname] + ) elif file_type == 'pkcs7': # import PKCS #7 cert chain - return self.import_pkcs7( + chain, nicks = self.import_pkcs7( pkcs7_file=cert_chain_file, nickname=nickname, trust_attributes=trust_attributes, output_format='base64') + return chain, nicks else: # import PKCS #7 data without header/footer with open(cert_chain_file, 'r') as f: @@ -409,17 +412,18 @@ class NSSDatabase(object): with open(tmp_cert_chain_file, 'w') as f: f.write(pkcs7_data) - self.import_pkcs7( + chain, nicks = self.import_pkcs7( pkcs7_file=tmp_cert_chain_file, nickname=nickname, trust_attributes=trust_attributes) - return base64_data + return base64_data, nicks finally: shutil.rmtree(tmpdir) - def import_pkcs7(self, pkcs7_file, nickname, trust_attributes=None, output_format='pem'): + def import_pkcs7(self, pkcs7_file, nickname, trust_attributes=None, + output_format='pem'): tmpdir = tempfile.mkdtemp() @@ -435,6 +439,7 @@ class NSSDatabase(object): # parse PEM output into separate PEM certificates certs = [] lines = [] + nicks = [] state = 'header' for line in output.splitlines(): @@ -476,6 +481,7 @@ class NSSDatabase(object): n = '%s #%d' % (nickname, counter) self.add_cert(n, cert_file, trust_attributes) + nicks.append(n) counter += 1 @@ -483,12 +489,13 @@ class NSSDatabase(object): with open(pkcs7_file, 'r') as f: data = f.read() - return convert_pkcs7(data, 'pem', output_format) + return convert_pkcs7(data, 'pem', output_format), nicks finally: shutil.rmtree(tmpdir) - def import_pkcs12(self, pkcs12_file, pkcs12_password=None, pkcs12_password_file=None): + def import_pkcs12(self, pkcs12_file, pkcs12_password=None, + pkcs12_password_file=None): tmpdir = tempfile.mkdtemp() diff --git a/base/server/python/pki/server/__init__.py b/base/server/python/pki/server/__init__.py index 2364131c6..abcce7167 100644 --- a/base/server/python/pki/server/__init__.py +++ b/base/server/python/pki/server/__init__.py @@ -360,6 +360,13 @@ class PKISubsystem(object): return str(self.instance) + '/' + self.name +class ExternalCert(object): + + def __init__(self, nickname=None, token=None): + self.nickname = nickname + self.token = token + + @functools.total_ordering class PKIInstance(object): @@ -375,6 +382,9 @@ class PKIInstance(object): self.conf_dir = os.path.join(self.base_dir, 'conf') self.password_conf = os.path.join(self.conf_dir, 'password.conf') + self.external_certs_conf = os.path.join( + self.conf_dir, 'external_certs.conf') + self.external_certs = [] self.nssdb_dir = os.path.join(self.base_dir, 'alias') self.lib_dir = os.path.join(self.base_dir, 'lib') @@ -462,6 +472,8 @@ class PKIInstance(object): value = parts[1] self.passwords[name] = value + self.load_external_certs(self.external_certs_conf) + # load subsystems for subsystem_name in os.listdir(self.registry_dir): if subsystem_name in SUBSYSTEM_TYPES: @@ -472,6 +484,30 @@ class PKIInstance(object): subsystem.load() self.subsystems.append(subsystem) + def load_external_certs(self, conf_file): + self.external_certs = PKIInstance.read_external_certs(conf_file) + + @staticmethod + def read_external_certs(conf_file): + external_certs = [] + # load external certs data + if os.path.exists(conf_file) and os.stat(conf_file).st_size > 0: + tmp_certs = {} + lines = open(conf_file).read().splitlines() + for line in lines: + m = re.search('(\\d+)\\.(\\w+)=(.*)', line) + if not m: + raise pki.PKIException('Error parsing %s' % conf_file) + indx = m.group(1) + attr = m.group(2) + value = m.group(3) + if indx not in tmp_certs: + tmp_certs[indx] = ExternalCert() + + setattr(tmp_certs[indx], attr, value) + external_certs = tmp_certs.values() + return external_certs + def get_password(self, name): if name in self.passwords: return self.passwords[name] @@ -487,6 +523,76 @@ class PKIInstance(object): token=token, password=self.get_password(token)) + def external_cert_exists(self, nickname, token): + for cert in self.external_certs: + if cert.nickname == nickname and cert.token == token: + return True + return False + + def add_external_cert(self, nickname, token): + if self.external_cert_exists(nickname, token): + return + self.external_certs.append(ExternalCert(nickname, token)) + self.save_external_cert_data() + + def delete_external_cert(self, nickname, token): + for cert in self.external_certs: + if cert.nickname == nickname and cert.token == token: + self.external_certs.remove(cert) + self.save_external_cert_data() + + def save_external_cert_data(self): + with io.open(self.external_certs_conf, 'wb') as f: + indx = 0 + for cert in self.external_certs: + f.write('%s.nickname=%s\n' % (str(indx), cert.nickname)) + f.write('%s.token=%s\n' % (str(indx), cert.token)) + indx += 1 + + def export_external_certs(self, pkcs12_file, pkcs12_password_file, + new_file=False): + for cert in self.external_certs: + nickname = cert.nickname + token = cert.token + if token == 'Internal Key Storage Token': + token = 'internal' + nssdb_password = self.get_password(token) + + tmpdir = tempfile.mkdtemp() + + try: + nssdb_password_file = os.path.join(tmpdir, 'password.txt') + with open(nssdb_password_file, 'w') as f: + f.write(nssdb_password) + + # add the certificate, key, and chain + cmd = [ + 'pki', + '-d', self.nssdb_dir, + '-C', nssdb_password_file + ] + + if token and token != 'internal': + cmd.extend(['--token', token]) + + cmd.extend([ + 'pkcs12-cert-add', + '--pkcs12', pkcs12_file, + '--pkcs12-password-file', pkcs12_password_file, + ]) + + if new_file: + cmd.extend(['--new-file']) + + cmd.extend([ + nickname + ]) + + subprocess.check_call(cmd) + + finally: + shutil.rmtree(tmpdir) + def get_subsystem(self, name): for subsystem in self.subsystems: if name == subsystem.name: diff --git a/base/server/python/pki/server/cli/ca.py b/base/server/python/pki/server/cli/ca.py index a47a293cf..54eabe299 100644 --- a/base/server/python/pki/server/cli/ca.py +++ b/base/server/python/pki/server/cli/ca.py @@ -397,9 +397,13 @@ class CAClonePrepareCLI(pki.cli.CLI): subsystem.export_system_cert( 'subsystem', pkcs12_file, pkcs12_password_file, new_file=True) - subsystem.export_system_cert('signing', pkcs12_file, pkcs12_password_file) - subsystem.export_system_cert('ocsp_signing', pkcs12_file, pkcs12_password_file) - subsystem.export_system_cert('audit_signing', pkcs12_file, pkcs12_password_file) + subsystem.export_system_cert( + 'signing', pkcs12_file, pkcs12_password_file) + subsystem.export_system_cert( + 'ocsp_signing', pkcs12_file, pkcs12_password_file) + subsystem.export_system_cert( + 'audit_signing', pkcs12_file, pkcs12_password_file) + instance.export_external_certs(pkcs12_file, pkcs12_password_file) finally: shutil.rmtree(tmpdir) diff --git a/base/server/python/pki/server/cli/instance.py b/base/server/python/pki/server/cli/instance.py index b5e6a5e41..5d1615329 100644 --- a/base/server/python/pki/server/cli/instance.py +++ b/base/server/python/pki/server/cli/instance.py @@ -26,6 +26,7 @@ import os import sys import pki.cli +import pki.nssdb import pki.server import pki.server.cli.nuxwdog @@ -44,6 +45,8 @@ class InstanceCLI(pki.cli.CLI): self.add_module(InstanceMigrateCLI()) self.add_module(InstanceNuxwdogEnableCLI()) self.add_module(InstanceNuxwdogDisableCLI()) + self.add_module(InstanceExternalCertAddCLI()) + self.add_module(InstanceExternalCertDeleteCLI()) @staticmethod def print_instance(instance): @@ -532,3 +535,185 @@ class InstanceNuxwdogDisableCLI(pki.cli.CLI): instance) # pylint: disable=no-member,maybe-no-member self.print_message('Nuxwdog disabled for instance %s.' % instance_name) + + +class InstanceExternalCertAddCLI(pki.cli.CLI): + + def __init__(self): + super(InstanceExternalCertAddCLI, self).__init__( + 'externalcert-add', + 'Add external certificate or chain to the instance') + + def print_help(self): + print('Usage: pki-server instance-externalcert-add [OPTIONS]') + print() + print(' -i, --instance <instance ID> Instance ID (default: pki-tomcat).') + print(' --cert-file <path> Input file containing the external certificate or certificate chain.') + print(' --trust-args <trust-args> Trust args (default \",,\").') + print(' --nickname <nickname> Nickname to be used.') + print(' --token <token_name> Token (default: internal).') + print(' -v, --verbose Run in verbose mode.') + print(' --help Show help message.') + print() + + def execute(self, argv): + try: + opts, _ = getopt.gnu_getopt(argv, 'i:v', [ + 'instance=', + 'cert-file=', 'trust-args=', 'nickname=','token=', + 'verbose', 'help']) + + except getopt.GetoptError as e: + print('ERROR: ' + str(e)) + self.print_help() + sys.exit(1) + + instance_name = 'pki-tomcat' + cert_file = None + trust_args = '\",,\"' + nickname = None + token = 'internal' + + for o, a in opts: + if o in ('-i', '--instance'): + instance_name = a + + elif o == '--cert-file': + cert_file = a + + elif o == '--trust-args': + trust_args = a + + elif o == '--nickname': + nickname = a + + elif o == '--token': + token = a + + elif o in ('-v', '--verbose'): + self.set_verbose(True) + + elif o == '--help': + self.print_help() + sys.exit() + + else: + print('ERROR: unknown option ' + o) + self.print_help() + sys.exit(1) + + if not cert_file: + print('ERROR: missing input file containing certificate') + self.print_help() + sys.exit(1) + + if not nickname: + print('ERROR: missing nickname') + self.print_help() + sys.exit(1) + + instance = pki.server.PKIInstance(instance_name) + instance.load() + + if instance.external_cert_exists(nickname, token): + print('ERROR: Certificate already imported for instance %s.' % + instance_name) + sys.exit(1) + + nicks = self.import_certs( + instance, cert_file, nickname, token, trust_args) + self.update_instance_config(instance, nicks, token) + + self.print_message('Certificate imported for instance %s.' % + instance_name) + + def import_certs(self, instance, cert_file, nickname, token, trust_args): + password = instance.get_password(token) + certdb = pki.nssdb.NSSDatabase( + directory=instance.nssdb_dir, + password=password, + token=token) + _chain, nicks = certdb.import_cert_chain( + nickname, cert_file, trust_attributes=trust_args) + return nicks + + def update_instance_config(self, instance, nicks, token): + for nickname in nicks: + instance.add_external_cert(nickname, token) + + +class InstanceExternalCertDeleteCLI(pki.cli.CLI): + + def __init__(self): + super(InstanceExternalCertDeleteCLI, self).__init__( + 'externalcert-del', + 'Delete external certificate from the instance') + + def print_help(self): + print('Usage: pki-server instance-externalcert-del [OPTIONS]') + print() + print(' -i, --instance <instance ID> Instance ID (default: pki-tomcat).') + print(' --nickname <nickname> Nickname to be used.') + print(' --token <token_name> Token (default: internal).') + print(' -v, --verbose Run in verbose mode.') + print(' --help Show help message.') + print() + + def execute(self, argv): + try: + opts, _ = getopt.gnu_getopt(argv, 'i:v', [ + 'instance=', 'nickname=','token=', + 'verbose', 'help']) + + except getopt.GetoptError as e: + print('ERROR: ' + str(e)) + self.print_help() + sys.exit(1) + + instance_name = 'pki-tomcat' + nickname = None + token = 'internal' + + for o, a in opts: + if o in ('-i', '--instance'): + instance_name = a + + elif o == '--nickname': + nickname = a + + elif o == '--token': + token = a + + elif o in ('-v', '--verbose'): + self.set_verbose(True) + + elif o == '--help': + self.print_help() + sys.exit() + + else: + print('ERROR: unknown option ' + o) + self.print_help() + sys.exit(1) + + if not nickname: + print('ERROR: missing nickname') + self.print_help() + sys.exit(1) + + instance = pki.server.PKIInstance(instance_name) + instance.load() + + self.remove_cert(instance, nickname, token) + instance.delete_external_cert(nickname, token) + + self.print_message('Certificate removed from instance %s.' % + instance_name) + + def remove_cert(self, instance, nickname, token): + password = instance.get_password(token) + certdb = pki.nssdb.NSSDatabase( + directory=instance.nssdb_dir, + password=password, + token=token) + certdb.remove_cert(nickname) diff --git a/base/server/python/pki/server/cli/kra.py b/base/server/python/pki/server/cli/kra.py index d1b27dbc1..ba1bf5a97 100644 --- a/base/server/python/pki/server/cli/kra.py +++ b/base/server/python/pki/server/cli/kra.py @@ -131,9 +131,13 @@ class KRAClonePrepareCLI(pki.cli.CLI): subsystem.export_system_cert( 'subsystem', pkcs12_file, pkcs12_password_file, new_file=True) - subsystem.export_system_cert('transport', pkcs12_file, pkcs12_password_file) - subsystem.export_system_cert('storage', pkcs12_file, pkcs12_password_file) - subsystem.export_system_cert('audit_signing', pkcs12_file, pkcs12_password_file) + subsystem.export_system_cert( + 'transport', pkcs12_file, pkcs12_password_file) + subsystem.export_system_cert( + 'storage', pkcs12_file, pkcs12_password_file) + subsystem.export_system_cert( + 'audit_signing', pkcs12_file, pkcs12_password_file) + instance.export_external_certs(pkcs12_file, pkcs12_password_file) finally: shutil.rmtree(tmpdir) diff --git a/base/server/python/pki/server/cli/ocsp.py b/base/server/python/pki/server/cli/ocsp.py index 7b1b43487..45d7fca83 100644 --- a/base/server/python/pki/server/cli/ocsp.py +++ b/base/server/python/pki/server/cli/ocsp.py @@ -131,8 +131,11 @@ class OCSPClonePrepareCLI(pki.cli.CLI): subsystem.export_system_cert( 'subsystem', pkcs12_file, pkcs12_password_file, new_file=True) - subsystem.export_system_cert('signing', pkcs12_file, pkcs12_password_file) - subsystem.export_system_cert('audit_signing', pkcs12_file, pkcs12_password_file) + subsystem.export_system_cert( + 'signing', pkcs12_file, pkcs12_password_file) + subsystem.export_system_cert( + 'audit_signing', pkcs12_file, pkcs12_password_file) + instance.export_external_certs(pkcs12_file, pkcs12_password_file) finally: shutil.rmtree(tmpdir) diff --git a/base/server/python/pki/server/cli/tks.py b/base/server/python/pki/server/cli/tks.py index 39343db98..2bdfce84a 100644 --- a/base/server/python/pki/server/cli/tks.py +++ b/base/server/python/pki/server/cli/tks.py @@ -131,8 +131,11 @@ class TKSClonePrepareCLI(pki.cli.CLI): subsystem.export_system_cert( 'subsystem', pkcs12_file, pkcs12_password_file, new_file=True) - subsystem.export_system_cert('signing', pkcs12_file, pkcs12_password_file) - subsystem.export_system_cert('audit_signing', pkcs12_file, pkcs12_password_file) + subsystem.export_system_cert( + 'signing', pkcs12_file, pkcs12_password_file) + subsystem.export_system_cert( + 'audit_signing', pkcs12_file, pkcs12_password_file) + instance.export_external_certs(pkcs12_file, pkcs12_password_file) finally: shutil.rmtree(tmpdir) diff --git a/base/server/python/pki/server/cli/tps.py b/base/server/python/pki/server/cli/tps.py index 05045cb0d..731b9720c 100644 --- a/base/server/python/pki/server/cli/tps.py +++ b/base/server/python/pki/server/cli/tps.py @@ -131,8 +131,11 @@ class TPSClonePrepareCLI(pki.cli.CLI): subsystem.export_system_cert( 'subsystem', pkcs12_file, pkcs12_password_file, new_file=True) - subsystem.export_system_cert('signing', pkcs12_file, pkcs12_password_file) - subsystem.export_system_cert('audit_signing', pkcs12_file, pkcs12_password_file) + subsystem.export_system_cert( + 'signing', pkcs12_file, pkcs12_password_file) + subsystem.export_system_cert( + 'audit_signing', pkcs12_file, pkcs12_password_file) + instance.export_external_certs(pkcs12_file, pkcs12_password_file) finally: shutil.rmtree(tmpdir) diff --git a/base/server/python/pki/server/deployment/scriptlets/configuration.py b/base/server/python/pki/server/deployment/scriptlets/configuration.py index d06d88f1d..79b66757a 100644 --- a/base/server/python/pki/server/deployment/scriptlets/configuration.py +++ b/base/server/python/pki/server/deployment/scriptlets/configuration.py @@ -162,7 +162,7 @@ class PkiScriptlet(pkiscriptlet.AbstractBasePkiScriptlet): deployer.mdict['pki_external_ca_cert_chain_nickname'] external_ca_cert_chain_file = deployer.mdict['pki_external_ca_cert_chain_path'] if external_ca_cert_chain_file: - cert_chain = nssdb.import_cert_chain( + cert_chain, _nicks = nssdb.import_cert_chain( nickname=external_ca_cert_chain_nickname, cert_chain_file=external_ca_cert_chain_file, trust_attributes='CT,C,C') diff --git a/base/server/python/pki/server/deployment/scriptlets/security_databases.py b/base/server/python/pki/server/deployment/scriptlets/security_databases.py index a723b1da9..027c4c4cf 100644 --- a/base/server/python/pki/server/deployment/scriptlets/security_databases.py +++ b/base/server/python/pki/server/deployment/scriptlets/security_databases.py @@ -20,7 +20,9 @@ from __future__ import absolute_import +import os import pki.nssdb +import pki.server # PKI Deployment Imports from .. import pkiconfig as config @@ -89,7 +91,8 @@ class PkiScriptlet(pkiscriptlet.AbstractBasePkiScriptlet): # importing system certificates - pki_server_pkcs12_password = deployer.mdict['pki_server_pkcs12_password'] + pki_server_pkcs12_password = deployer.mdict[ + 'pki_server_pkcs12_password'] if not pki_server_pkcs12_password: raise Exception('Missing pki_server_pkcs12_password property.') @@ -101,6 +104,11 @@ class PkiScriptlet(pkiscriptlet.AbstractBasePkiScriptlet): pkcs12_file=pki_server_pkcs12_path, pkcs12_password=pki_server_pkcs12_password) + # update external CA file (if needed) + external_cert_path = deployer.mdict['pki_server_external_cert_path'] + if external_cert_path is not None: + self.update_external_cert_conf(external_cert_path, deployer) + if len(deployer.instance.tomcat_instance_subsystems()) < 2: # only create a self signed cert for a new instance # @@ -175,6 +183,21 @@ class PkiScriptlet(pkiscriptlet.AbstractBasePkiScriptlet): deployer.file.delete(deployer.mdict['pki_shared_pfile']) return self.rv + def update_external_cert_conf(self, external_path, deployer): + external_certs = pki.server.PKIInstance.read_external_certs( + external_path) + + if len(external_certs) > 0: + instance = pki.server.PKIInstance( + deployer.mdict['pki_instance_name']) + instance.load_external_certs( + os.path.join(deployer.mdict['pki_instance_configuration_path'], + 'external_certs.conf') + ) + + for cert in external_certs: + instance.add_external_cert(cert.nickname, cert.token) + def destroy(self, deployer): config.pki_log.info(log.SECURITY_DATABASES_DESTROY_1, __name__, |