From 3fcefc1b67e7afe0455267b3876d9e6ef47531cc Mon Sep 17 00:00:00 2001 From: Matthew Harmsen Date: Wed, 18 Jul 2012 17:48:11 -0700 Subject: PKI Deployment Scriptlets Saved Admin Certificate, imported it into NSS client security databases, and exported it to a PKCS #12 file such that it may be imported into a browser. TRAC Ticket #221 Dogtag 10: Create a PKCS #12 file containing the Admin Certificate (https://fedorahosted.org/pki/ticket/221) --- base/deploy/config/pkideployment.cfg | 2 + base/deploy/src/scriptlets/configuration.jy | 4 +- base/deploy/src/scriptlets/configuration.py | 24 ++++++- base/deploy/src/scriptlets/pkiconfig.py | 1 + base/deploy/src/scriptlets/pkihelper.py | 55 ++++++++++++--- base/deploy/src/scriptlets/pkijython.py | 86 +++++++++++++++++++++-- base/deploy/src/scriptlets/pkimessages.py | 8 +++ base/deploy/src/scriptlets/pkiparser.py | 103 ++++++++++++++++++++++++++-- 8 files changed, 260 insertions(+), 23 deletions(-) (limited to 'base') diff --git a/base/deploy/config/pkideployment.cfg b/base/deploy/config/pkideployment.cfg index 542fc5bef..a4513d712 100644 --- a/base/deploy/config/pkideployment.cfg +++ b/base/deploy/config/pkideployment.cfg @@ -10,6 +10,7 @@ [Sensitive] pki_admin_password= pki_backup_password= +pki_client_pkcs12_password= pki_ds_password= pki_pkcs12_password= pki_security_domain_password= @@ -32,6 +33,7 @@ pki_security_domain_password= [Optional] pki_admin_domain_name= pki_admin_email= +pki_admin_nickname= pki_admin_subject_dn= pki_audit_signing_nickname= pki_audit_signing_subject_dn= diff --git a/base/deploy/src/scriptlets/configuration.jy b/base/deploy/src/scriptlets/configuration.jy index a40e7c645..2e72f4060 100644 --- a/base/deploy/src/scriptlets/configuration.jy +++ b/base/deploy/src/scriptlets/configuration.jy @@ -163,9 +163,7 @@ def main(argv): # Formulate PKI Subsystem Configuration Data Response jyutil.rest_client.configure_pki_data(data, - master['pki_subsystem'], - master['pki_dry_run_flag'], - master['pki_jython_log_level']) + master) if __name__ == "__main__": diff --git a/base/deploy/src/scriptlets/configuration.py b/base/deploy/src/scriptlets/configuration.py index 421e08dc0..742a4ec33 100644 --- a/base/deploy/src/scriptlets/configuration.py +++ b/base/deploy/src/scriptlets/configuration.py @@ -35,7 +35,11 @@ class PkiScriptlet(pkiscriptlet.AbstractBasePkiScriptlet): config.pki_log.info(log.CONFIGURATION_SPAWN_1, __name__, extra=config.PKI_INDENTATION_LEVEL_1) if not config.pki_dry_run_flag: - util.directory.create(master['pki_client_path'], uid=0, gid=0) + # Place "slightly" less restrictive permissions on + # the top-level client directory ONLY + util.directory.create(master['pki_client_path'], + uid=0, gid=0, + perms=config.PKI_DEPLOYMENT_DEFAULT_CLIENT_DIR_PERMISSIONS) # Since 'certutil' does NOT strip the 'token=' portion of # the 'token=password' entries, create a client password file # which ONLY contains the 'password' for the purposes of @@ -43,6 +47,15 @@ class PkiScriptlet(pkiscriptlet.AbstractBasePkiScriptlet): util.password.create_password_conf( master['pki_client_password_conf'], master['pki_client_pin'], pin_sans_token=True) + util.file.modify(master['pki_client_password_conf'], + uid=0, gid=0) + # Similarly, create a simple password file containing the + # PKCS #12 password used when exporting the "Admin Certificate" + # into a PKCS #12 file + util.password.create_client_pkcs12_password_conf( + master['pki_client_pkcs12_password_conf']) + util.file.modify(master['pki_client_pkcs12_password_conf'], + uid=0, gid=0) util.directory.create(master['pki_client_database_path'], uid=0, gid=0) util.certutil.create_security_databases( @@ -61,6 +74,11 @@ class PkiScriptlet(pkiscriptlet.AbstractBasePkiScriptlet): util.password.create_password_conf( master['pki_client_password_conf'], master['pki_client_pin'], pin_sans_token=True) + # Similarly, create a simple password file containing the + # PKCS #12 password used when exporting the "Admin Certificate" + # into a PKCS #12 file + util.password.create_client_pkcs12_password_conf( + master['pki_client_pkcs12_password_conf']) util.certutil.create_security_databases( master['pki_client_database_path'], master['pki_client_cert_database'], @@ -112,6 +130,10 @@ class PkiScriptlet(pkiscriptlet.AbstractBasePkiScriptlet): def respawn(self): config.pki_log.info(log.CONFIGURATION_RESPAWN_1, __name__, extra=config.PKI_INDENTATION_LEVEL_1) + util.file.modify(master['pki_client_password_conf'], + uid=0, gid=0) + util.file.modify(master['pki_client_pkcs12_password_conf'], + uid=0, gid=0) # ALWAYS Restart this Apache/Tomcat PKI Process util.systemd.restart() return self.rv diff --git a/base/deploy/src/scriptlets/pkiconfig.py b/base/deploy/src/scriptlets/pkiconfig.py index 07537d7aa..59526e667 100644 --- a/base/deploy/src/scriptlets/pkiconfig.py +++ b/base/deploy/src/scriptlets/pkiconfig.py @@ -20,6 +20,7 @@ # # PKI Deployment Constants +PKI_DEPLOYMENT_DEFAULT_CLIENT_DIR_PERMISSIONS = 00755 PKI_DEPLOYMENT_DEFAULT_DIR_PERMISSIONS = 00770 PKI_DEPLOYMENT_DEFAULT_EXE_PERMISSIONS = 00770 PKI_DEPLOYMENT_DEFAULT_FILE_PERMISSIONS = 00660 diff --git a/base/deploy/src/scriptlets/pkihelper.py b/base/deploy/src/scriptlets/pkihelper.py index 7b77bcee5..7de6502a2 100644 --- a/base/deploy/src/scriptlets/pkihelper.py +++ b/base/deploy/src/scriptlets/pkihelper.py @@ -326,16 +326,22 @@ class configuration_file: extra=config.PKI_INDENTATION_LEVEL_2) sys.exit(1) # If required, verify existence of Backup Password - # (except for Clones) if config.str2bool(master['pki_backup_keys']): - if not config.str2bool(master['pki_clone']): - if not sensitive.has_key('pki_backup_password') or\ - not len(sensitive['pki_backup_password']): - config.pki_log.error( - log.PKIHELPER_UNDEFINED_BACKUP_PASSWORD_1, - config.pkideployment_cfg, - extra=config.PKI_INDENTATION_LEVEL_2) - sys.exit(1) + if not sensitive.has_key('pki_backup_password') or\ + not len(sensitive['pki_backup_password']): + config.pki_log.error( + log.PKIHELPER_UNDEFINED_BACKUP_PASSWORD_1, + config.pkideployment_cfg, + extra=config.PKI_INDENTATION_LEVEL_2) + sys.exit(1) + # Verify existence of Client PKCS #12 Password for Admin Cert + if not sensitive.has_key('pki_client_pkcs12_password') or\ + not len(sensitive['pki_client_pkcs12_password']): + config.pki_log.error( + log.PKIHELPER_UNDEFINED_CLIENT_PKCS12_PASSWORD_1, + config.pkideployment_cfg, + extra=config.PKI_INDENTATION_LEVEL_2) + sys.exit(1) # Verify existence of PKCS #12 Password (ONLY for Clones) if config.str2bool(master['pki_clone']): if not sensitive.has_key('pki_pkcs12_password') or\ @@ -1583,6 +1589,37 @@ class password: sys.exit(1) return + def create_client_pkcs12_password_conf(self, path, overwrite_flag=False, + critical_failure=True): + try: + if not config.pki_dry_run_flag: + if os.path.exists(path): + if overwrite_flag: + config.pki_log.info( + log.PKIHELPER_PASSWORD_CONF_1, path, + extra=config.PKI_INDENTATION_LEVEL_2) + # overwrite the existing 'pkcs12_password.conf' file + with open(path, "wt") as fd: + fd.write(sensitive['pki_client_pkcs12_password']) + fd.closed + else: + config.pki_log.info(log.PKIHELPER_PASSWORD_CONF_1, path, + extra=config.PKI_INDENTATION_LEVEL_2) + # create a new 'pkcs12_password.conf' file + with open(path, "wt") as fd: + fd.write(sensitive['pki_client_pkcs12_password']) + fd.closed + else: + if not os.path.exists(path) or overwrite_flag: + config.pki_log.info(log.PKIHELPER_PASSWORD_CONF_1, path, + extra=config.PKI_INDENTATION_LEVEL_2) + except OSError as exc: + config.pki_log.error(log.PKI_OSERROR_1, exc, + extra=config.PKI_INDENTATION_LEVEL_2) + if critical_failure == True: + sys.exit(1) + return + # PKI Deployment NSS 'certutil' Class class certutil: diff --git a/base/deploy/src/scriptlets/pkijython.py b/base/deploy/src/scriptlets/pkijython.py index 800826635..7856ba8c1 100644 --- a/base/deploy/src/scriptlets/pkijython.py +++ b/base/deploy/src/scriptlets/pkijython.py @@ -21,6 +21,7 @@ import jarray # System Python Imports import ConfigParser import os +import re import sys pki_python_module_path = os.path.join(sys.prefix, "lib", @@ -581,20 +582,21 @@ class rest_client: data.setSystemCerts(systemCerts) return data - def configure_pki_data(self, data, pki_subsystem, pki_dry_run_flag, - log_level): - if log_level >= config.PKI_JYTHON_INFO_LOG_LEVEL: + def configure_pki_data(self, data, master): + if master['pki_jython_log_level'] >= config.PKI_JYTHON_INFO_LOG_LEVEL: print "%s %s '%s'" %\ (log.PKI_JYTHON_INDENTATION_2, log.PKI_JYTHON_CONFIGURING_PKI_DATA, - pki_subsystem) - if not pki_dry_run_flag: + master['pki_subsystem']) + if not master['pki_dry_run_flag']: try: + sensitive = extract_sensitive_data(master['pki_deployment_cfg']) response = self.client.configure(data) javasystem.out.println(log.PKI_JYTHON_RESPONSE_STATUS +\ " " + response.getStatus()) + admin_cert = response.getAdminCert().getCert() javasystem.out.println(log.PKI_JYTHON_RESPONSE_ADMIN_CERT +\ - " " + response.getAdminCert().getCert()) + " " + admin_cert) certs = response.getSystemCerts() iterator = certs.iterator() while iterator.hasNext(): @@ -605,6 +607,78 @@ class rest_client: cdata.getCert()) javasystem.out.println(log.PKI_JYTHON_CDATA_REQUEST + " " +\ cdata.getRequest()) + # Store the Administration Certificate in a file + admin_cert_file = os.path.join(master['pki_client_path'], + master['pki_client_admin_cert']) + javasystem.out.println(log.PKI_JYTHON_ADMIN_CERT_SAVE +\ + " " + "'" + admin_cert_file + "'") + FILE = open(admin_cert_file, "w") + FILE.write(admin_cert) + FILE.close() + # Since Jython runs under Java, it does NOT support the + # following operating system specific command: + # + # os.chmod(admin_cert_file, + # config.PKI_DEPLOYMENT_DEFAULT_FILE_PERMISSIONS) + # + # Emulate it with a system call. + command = "chmod" + " " + "660" + " " + admin_cert_file + javasystem.out.println( + log.PKI_JYTHON_CHMOD +\ + " " + "'" + command + "'") + os.system(command) + # Import the Administration Certificate + # into the client NSS security database + command = "certutil" + " " +\ + "-A" + " " +\ + "-n" + " " + "\"" +\ + re.sub("'", "'", master['pki_admin_nickname']) +\ + "\"" + " " +\ + "-t" + " " +\ + "\"" + "u,u,u" + "\"" + " " +\ + "-f" + " " +\ + master['pki_client_password_conf'] + " " +\ + "-d" + " " +\ + master['pki_client_database_path'] + " " +\ + "-a" + " " +\ + "-i" + " " +\ + admin_cert_file + javasystem.out.println( + log.PKI_JYTHON_ADMIN_CERT_IMPORT +\ + " " + "'" + command + "'") + os.system(command) + # Export the Administration Certificate from the + # client NSS security database into a PKCS #12 file + command = "pk12util" + " " +\ + "-o" + " " +\ + master['pki_client_admin_cert_p12'] + " " +\ + "-n" + " " + "\"" +\ + re.sub("'", "'", master['pki_admin_nickname']) +\ + "\"" + " " +\ + "-d" + " " +\ + master['pki_client_database_path'] + " " +\ + "-k" + " " +\ + master['pki_client_password_conf'] + " " +\ + "-w" + " " +\ + master['pki_client_pkcs12_password_conf'] + javasystem.out.println( + log.PKI_JYTHON_ADMIN_CERT_EXPORT +\ + " " + "'" + command + "'") + os.system(command) + # Since Jython runs under Java, it does NOT support the + # following operating system specific command: + # + # os.chmod(master['pki_client_admin_cert_p12'], + # config.\ + # PKI_DEPLOYMENT_DEFAULT_SECURITY_DATABASE_PERMISSIONS) + # + # Emulate it with a system call. + command = "chmod" + " " + "664" + " " +\ + master['pki_client_admin_cert_p12'] + javasystem.out.println( + log.PKI_JYTHON_CHMOD +\ + " " + "'" + command + "'") + os.system(command) except Exception, e: javasystem.out.println( log.PKI_JYTHON_JAVA_CONFIGURATION_EXCEPTION + " " + str(e)) diff --git a/base/deploy/src/scriptlets/pkimessages.py b/base/deploy/src/scriptlets/pkimessages.py index d7d50a63e..58b09dca3 100644 --- a/base/deploy/src/scriptlets/pkimessages.py +++ b/base/deploy/src/scriptlets/pkimessages.py @@ -213,6 +213,8 @@ PKIHELPER_UNDEFINED_ADMIN_PASSWORD_1 =\ "A value for 'pki_admin_password' MUST be defined in '%s'" PKIHELPER_UNDEFINED_BACKUP_PASSWORD_1 =\ "A value for 'pki_backup_password' MUST be defined in '%s'" +PKIHELPER_UNDEFINED_CLIENT_PKCS12_PASSWORD_1 =\ + "A value for 'pki_client_pkcs12_password' MUST be defined in '%s'" PKIHELPER_UNDEFINED_DS_PASSWORD_1 =\ "A value for 'pki_ds_password' MUST be defined in '%s'" PKIHELPER_UNDEFINED_PKCS12_PASSWORD_1 =\ @@ -228,9 +230,15 @@ PKIHELPER_USER_ADD_UID_KEYERROR_1 = "KeyError: pki_uid %s" # PKI Deployment Jython "Scriptlet" Messages # (MUST contain NO embedded formats since Jython 2.2 does not support logging!) +PKI_JYTHON_ADMIN_CERT_EXPORT = "exporting Admin Certificate from "\ + "NSS client security database:" +PKI_JYTHON_ADMIN_CERT_IMPORT = "importing Admin Certificate into "\ + "NSS client security database:" +PKI_JYTHON_ADMIN_CERT_SAVE = "saving Admin Certificate to file:" PKI_JYTHON_CDATA_TAG = "tag:" PKI_JYTHON_CDATA_CERT = "cert:" PKI_JYTHON_CDATA_REQUEST = "request:" +PKI_JYTHON_CHMOD = "performing chmod:" PKI_JYTHON_CLONED_PKI_SUBSYSTEM = "Cloned" PKI_JYTHON_CONFIGURING_PKI_DATA = "configuring PKI configuration data for" PKI_JYTHON_CONSTRUCTING_PKI_DATA = "constructing PKI configuration data for" diff --git a/base/deploy/src/scriptlets/pkiparser.py b/base/deploy/src/scriptlets/pkiparser.py index 5abfdc064..6c4574add 100644 --- a/base/deploy/src/scriptlets/pkiparser.py +++ b/base/deploy/src/scriptlets/pkiparser.py @@ -1352,6 +1352,12 @@ def compose_pki_master_dictionary(): config.pki_master_dict['pki_subsystem_configuration_path'], "password.conf") # Client NSS security database name/value pairs + # + # The following variable is established via the specified PKI + # deployment configuration file and is NOT redefined below: + # + # config.pki_sensitive_dict['pki_client_pkcs12_password'] + # config.pki_master_dict['pki_client_path'] =\ os.path.join( "/tmp", @@ -1360,6 +1366,10 @@ def compose_pki_master_dictionary(): os.path.join( config.pki_master_dict['pki_client_path'], "password.conf") + config.pki_master_dict['pki_client_pkcs12_password_conf'] =\ + os.path.join( + config.pki_master_dict['pki_client_path'], + "pkcs12_password.conf") config.pki_master_dict['pki_client_database_path'] =\ os.path.join( config.pki_master_dict['pki_client_path'], @@ -1373,6 +1383,42 @@ def compose_pki_master_dictionary(): config.pki_master_dict['pki_client_secmod_database'] =\ os.path.join(config.pki_master_dict['pki_client_database_path'], "secmod.db") + if config.pki_master_dict['pki_subsystem'] == "CA": + config.pki_master_dict['pki_client_admin_cert'] = "ca_admin.cert" + config.pki_master_dict['pki_client_admin_cert_p12'] =\ + os.path.join( + config.pki_master_dict['pki_client_path'], + "ca_admin_cert.p12") + elif config.pki_master_dict['pki_subsystem'] == "KRA": + config.pki_master_dict['pki_client_admin_cert'] = "kra_admin.cert" + config.pki_master_dict['pki_client_admin_cert_p12'] =\ + os.path.join( + config.pki_master_dict['pki_client_path'], + "kra_admin_cert.p12") + elif config.pki_master_dict['pki_subsystem'] == "OCSP": + config.pki_master_dict['pki_client_admin_cert'] = "ocsp_admin.cert" + config.pki_master_dict['pki_client_admin_cert_p12'] =\ + os.path.join( + config.pki_master_dict['pki_client_path'], + "ocsp_admin_cert.p12") + elif config.pki_master_dict['pki_subsystem'] == "RA": + config.pki_master_dict['pki_client_admin_cert'] = "ra_admin.cert" + config.pki_master_dict['pki_client_admin_cert_p12'] =\ + os.path.join( + config.pki_master_dict['pki_client_path'], + "ra_admin_cert.p12") + elif config.pki_master_dict['pki_subsystem'] == "TKS": + config.pki_master_dict['pki_client_admin_cert'] = "tks_admin.cert" + config.pki_master_dict['pki_client_admin_cert_p12'] =\ + os.path.join( + config.pki_master_dict['pki_client_path'], + "tks_admin_cert.p12") + elif config.pki_master_dict['pki_subsystem'] == "TPS": + config.pki_master_dict['pki_client_admin_cert'] = "tps_admin.cert" + config.pki_master_dict['pki_client_admin_cert_p12'] =\ + os.path.join( + config.pki_master_dict['pki_client_path'], + "tps_admin_cert.p12") # Jython scriptlet name/value pairs config.pki_master_dict['pki_jython_configuration_scriptlet'] =\ os.path.join(sys.prefix, @@ -1405,7 +1451,7 @@ def compose_pki_master_dictionary(): # deployment configuration file and are NOT redefined below: # # config.pki_master_dict['pki_security_domain_https_port'] - # config.pki_master_dict['pki_security_domain_password'] + # config.pki_sensitive_dict['pki_security_domain_password'] # config.pki_master_dict['pki_security_domain_user'] # # The following variables are established via the specified PKI @@ -1474,7 +1520,7 @@ def compose_pki_master_dictionary(): # config.pki_master_dict['pki_ds_bind_dn'] # config.pki_master_dict['pki_ds_http_port'] # config.pki_master_dict['pki_ds_https_port'] - # config.pki_master_dict['pki_ds_password'] + # config.pki_sensitive_dict['pki_ds_password'] # config.pki_master_dict['pki_ds_remove_data'] # config.pki_master_dict['pki_ds_secure_connection'] # @@ -1507,7 +1553,7 @@ def compose_pki_master_dictionary(): # deployment configuration file and are NOT redefined below: # # config.pki_master_dict['pki_backup_keys'] - # config.pki_master_dict['pki_backup_password'] + # config.pki_sensitive_dict['pki_backup_password'] # # The following variables are established via the specified PKI # deployment configuration file and potentially overridden below: @@ -1566,13 +1612,14 @@ def compose_pki_master_dictionary(): # config.pki_master_dict['pki_admin_dualkey'] # config.pki_master_dict['pki_admin_keysize'] # config.pki_master_dict['pki_admin_name'] - # config.pki_master_dict['pki_admin_password'] + # config.pki_sensitive_dict['pki_admin_password'] # config.pki_master_dict['pki_admin_uid'] # # The following variables are established via the specified PKI # deployment configuration file and potentially overridden below: # # config.pki_master_dict['pki_admin_email'] + # config.pki_master_dict['pki_admin_nickname'] # config.pki_master_dict['pki_admin_subject_dn'] # config.pki_master_dict['pki_admin_profile_id'] = "caAdminCert" @@ -1580,6 +1627,54 @@ def compose_pki_master_dictionary(): config.pki_master_dict['pki_admin_email'] =\ config.pki_master_dict['pki_admin_name'] + "@" +\ config.pki_master_dict['pki_dns_domainname'] + if not len(config.pki_master_dict['pki_admin_nickname']): + if config.pki_subsystem in config.PKI_APACHE_SUBSYSTEMS: + if config.pki_master_dict['pki_subsystem'] == "RA": + # PKI RA + config.pki_master_dict['pki_admin_nickname'] =\ + "RA Administrator's" + " " +\ + config.pki_master_dict['pki_security_domain_name'] +\ + " " + "ID" + elif config.pki_master_dict['pki_subsystem'] == "TPS": + # PKI TPS + config.pki_master_dict['pki_admin_nickname'] =\ + "TPS Administrator's" + " " +\ + config.pki_master_dict['pki_security_domain_name'] +\ + " " + "ID" + elif config.pki_subsystem in config.PKI_TOMCAT_SUBSYSTEMS: + if not config.str2bool(config.pki_master_dict['pki_clone']): + if config.pki_master_dict['pki_subsystem'] == "CA": + # PKI CA, Subordinate CA, or External CA + config.pki_master_dict['pki_admin_nickname'] =\ + "CA Administrator of Instance" + " " +\ + config.pki_master_dict['pki_instance_id'] +\ + "'s" + " " +\ + config.pki_master_dict['pki_security_domain_name']\ + + " " + "ID" + elif config.pki_master_dict['pki_subsystem'] == "KRA": + # PKI KRA + config.pki_master_dict['pki_admin_nickname'] =\ + "KRA Administrator of Instance" + " " +\ + config.pki_master_dict['pki_instance_id'] +\ + "'s" + " " +\ + config.pki_master_dict['pki_security_domain_name']\ + + " " + "ID" + elif config.pki_master_dict['pki_subsystem'] == "OCSP": + # PKI OCSP + config.pki_master_dict['pki_admin_nickname'] =\ + "OCSP Administrator of Instance" + " " +\ + config.pki_master_dict['pki_instance_id'] +\ + "'s" + " " +\ + config.pki_master_dict['pki_security_domain_name']\ + + " " + "ID" + elif config.pki_master_dict['pki_subsystem'] == "TKS": + # PKI TKS + config.pki_master_dict['pki_admin_nickname'] =\ + "TKS Administrator of Instance" + " " +\ + config.pki_master_dict['pki_instance_id'] +\ + "'s" + " " +\ + config.pki_master_dict['pki_security_domain_name']\ + + " " + "ID" if not len(config.pki_master_dict['pki_admin_subject_dn']): if config.pki_subsystem in config.PKI_APACHE_SUBSYSTEMS: if config.pki_master_dict['pki_subsystem'] == "RA": -- cgit