diff --git a/install/share/60ipaconfig.ldif b/install/share/60ipaconfig.ldif index 3411e2c46..8cf211e96 100644 --- a/install/share/60ipaconfig.ldif +++ b/install/share/60ipaconfig.ldif @@ -39,9 +39,11 @@ attributeTypes: ( 2.16.840.1.113730.3.8.3.51 NAME 'ipaEscrowKeyCertificate' DESC attributeTypes: ( 2.16.840.1.113730.3.8.3.52 NAME 'ipaEscrowKey' DESC 'PKCS#12-formatted encrypted certificate and private key for encrypting escrow packets' SYNTAX 1.3.6.1.4.1.1466.115.121.1.5) # ipaMigrationEnabled - if TRUE allow adding user entries with pre-hashed passwords attributeTypes: ( 2.16.840.1.113730.3.8.1.16 NAME 'ipaMigrationEnabled' DESC 'Enable adding user entries with pre-hashed passwords.' SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 SINGLE-VALUE ) +attributetypes: ( 2.16.840.1.113730.3.8.1.14 NAME 'ipaCertificateSubjectBase' S +YNTAX 1.3.6.1.4.1.1466.115.121.1.15) ############################################### ## ## ObjectClasses ## ## ipaGuiConfig - GUI config parameters objectclass -objectClasses: ( 2.16.840.1.113730.3.8.2.1 NAME 'ipaGuiConfig' AUXILIARY MAY ( ipaUserSearchFields $ ipaGroupSearchFields $ ipaSearchTimeLimit $ ipaSearchRecordsLimit $ ipaCustomFields $ ipaHomesRootDir $ ipaDefaultLoginShell $ ipaDefaultPrimaryGroup $ ipaMaxUsernameLength $ ipaPwdExpAdvNotify $ ipaUserObjectClasses $ ipaGroupObjectClasses $ ipaDefaultEmailDomain $ ipaObsoleteEscrowPacketLifetime $ ipaEscrowKeyCertificate $ ipaEscrowKey $ ipaMigrationEnabled ) ) +objectClasses: ( 2.16.840.1.113730.3.8.2.1 NAME 'ipaGuiConfig' AUXILIARY MAY ( ipaUserSearchFields $ ipaGroupSearchFields $ ipaSearchTimeLimit $ ipaSearchRecordsLimit $ ipaCustomFields $ ipaHomesRootDir $ ipaDefaultLoginShell $ ipaDefaultPrimaryGroup $ ipaMaxUsernameLength $ ipaPwdExpAdvNotify $ ipaUserObjectClasses $ ipaGroupObjectClasses $ ipaDefaultEmailDomain $ ipaObsoleteEscrowPacketLifetime $ ipaEscrowKeyCertificate $ ipaEscrowKey $ ipaMigrationEnabled $ ipaCertificateSubjectBase) ) diff --git a/install/tools/ipa-replica-install b/install/tools/ipa-replica-install index 766957862..7131d6b76 100755 --- a/install/tools/ipa-replica-install +++ b/install/tools/ipa-replica-install @@ -48,6 +48,7 @@ class ReplicaConfig: self.host_name = "" self.repl_password = "" self.dir = "" + self.subject_base = "O=IPA" def parse_options(): from optparse import OptionParser @@ -106,6 +107,7 @@ def read_info(dir, rconfig): rconfig.ds_user = config.get("realm", "ds_user") rconfig.domain_name = config.get("realm", "domain_name") rconfig.host_name = config.get("realm", "destination_host") + rconfig.subject_base = config.get("realm", "subject_base") def get_host_name(): hostname = installutils.get_fqdn() @@ -150,9 +152,8 @@ def install_ca(config): cs = cainstance.CADSInstance() cs.create_instance(config.ds_user, config.realm_name, config.host_name, config.domain_name, config.dirman_password) - ca = cainstance.CAInstance() - ca.configure_instance("pkiuser", config.host_name, config.dirman_password, config.dirman_password, pkcs12_info=(cafile,), master_host=config.master_host_name) + ca.configure_instance("pkiuser", config.host_name, config.dirman_password, config.dirman_password, pkcs12_info=(cafile,), master_host=config.master_host_name, subject_base=config.subject_base) return ca @@ -346,6 +347,8 @@ def main(): CA.import_ra_cert(dir + "/ra.p12") CA.fix_ra_perms() service.restart("httpd") + service.print_msg("Setting the certificate subject base") + CA.set_subject_in_config(util.realm_to_suffix(config.realm_name)) # The DS instance is created before the keytab, add the SSL cert we # generated @@ -370,6 +373,7 @@ def main(): service.restart("dirsrv") service.restart("krb5kdc") + service.restart("httpd") if options.setup_dns: api.Backend.ldap2.connect(bind_dn="cn=Directory Manager", diff --git a/install/tools/ipa-replica-prepare b/install/tools/ipa-replica-prepare index bc86a41a3..f9977ecbb 100755 --- a/install/tools/ipa-replica-prepare +++ b/install/tools/ipa-replica-prepare @@ -33,6 +33,7 @@ from ipaserver import ipaldap from ipapython import version from ipalib.constants import DEFAULT_CONFIG from ipalib import api +from ipalib import util import ldap def parse_options(): @@ -94,13 +95,23 @@ def get_domain_name(): return domain_name +def get_subject_base(host_name, dm_password, suffix): + try: + conn = ipaldap.IPAdmin(host_name) + conn.do_simple_bind(bindpw=dm_password) + except Exception, e: + logging.critical("Could not connect to the Directory Server on %s" % host_name) + raise e + entry = conn.getEntry("cn=ipaConfig, cn=etc, %s" % suffix, ldap.SCOPE_SUBTREE) + return entry.getValue('ipacertificatesubjectbase') + def check_ipa_configuration(realm_name): config_dir = dsinstance.config_dirname(dsinstance.realm_to_serverid(realm_name)) if not ipautil.dir_exists(config_dir): logging.error("could not find directory instance: %s" % config_dir) sys.exit(1) -def export_certdb(realm_name, ds_dir, dir, passwd_fname, fname, hostname): +def export_certdb(realm_name, ds_dir, dir, passwd_fname, fname, hostname, subject_base=None): """realm is the kerberos realm for the IPA server. ds_dir is the location of the master DS we are creating a replica for. dir is the location of the files for the replica we are creating. @@ -113,14 +124,14 @@ def export_certdb(realm_name, ds_dir, dir, passwd_fname, fname, hostname): try: self_signed = certs.ipa_self_signed() - db = certs.CertDB(dir) + db = certs.CertDB(dir, subject_base=subject_base) db.create_passwd_file() # if self_signed: # ca_db = certs.CertDB(dsinstance.config_dirname(dsinstance.realm_to_serverid(realm_name))) # db.create_from_cacert(ca_db.cacert_fname) # else: # ca_db = certs.CertDB(httpinstance.NSS_DIR, host_name=get_host_name()) - ca_db = certs.CertDB(httpinstance.NSS_DIR, host_name=get_host_name()) + ca_db = certs.CertDB(httpinstance.NSS_DIR, host_name=get_host_name(), subject_base=subject_base) db.create_from_cacert(ca_db.cacert_fname) db.create_server_cert("Server-Cert", hostname, ca_db) except Exception, e: @@ -174,7 +185,8 @@ def get_ds_user(ds_dir): return user -def save_config(dir, realm_name, host_name, ds_user, domain_name, dest_host): +def save_config(dir, realm_name, host_name, ds_user, domain_name, dest_host, + subject_base): config = SafeConfigParser() config.add_section("realm") config.set("realm", "realm_name", realm_name) @@ -182,6 +194,7 @@ def save_config(dir, realm_name, host_name, ds_user, domain_name, dest_host): config.set("realm", "ds_user", ds_user) config.set("realm", "domain_name", domain_name) config.set("realm", "destination_host", dest_host) + config.set("realm", "subject_base", subject_base) fd = open(dir + "/realm_info", "w") config.write(fd) @@ -265,6 +278,8 @@ def main(): print "Preparing replica for %s from %s" % (replica_fqdn, host_name) + subject_base = get_subject_base(host_name, dirman_password, util.realm_to_suffix(realm_name)) + top_dir = tempfile.mkdtemp("ipa") dir = top_dir + "/realm_info" os.mkdir(dir, 0700) @@ -298,7 +313,7 @@ def main(): print "Copy failed %s" % e sys.exit(1) print "Creating SSL certificate for the Directory Server" - export_certdb(realm_name, ds_dir, dir, passwd_fname, "dscert", replica_fqdn) + export_certdb(realm_name, ds_dir, dir, passwd_fname, "dscert", replica_fqdn, subject_base) if options.http_pin: passwd = options.http_pin @@ -319,13 +334,15 @@ def main(): sys.exit(1) else: print "Creating SSL certificate for the Web Server" - export_certdb(realm_name, ds_dir, dir, passwd_fname, "httpcert", replica_fqdn) + export_certdb(realm_name, ds_dir, dir, passwd_fname, "httpcert", replica_fqdn, subject_base) print "Exporting RA certificate" export_ra_pkcs12(dir, dirman_password) + print "Copying additional files" copy_files(realm_name, dir) + print "Finalizing configuration" - save_config(dir, realm_name, host_name, ds_user, domain_name, replica_fqdn) + save_config(dir, realm_name, host_name, ds_user, domain_name, replica_fqdn, subject_base) replicafile = "/var/lib/ipa/replica-info-" + replica_fqdn encfile = replicafile+".gpg" diff --git a/install/tools/ipa-server-install b/install/tools/ipa-server-install index c92989a47..06bed03b3 100755 --- a/install/tools/ipa-server-install +++ b/install/tools/ipa-server-install @@ -35,6 +35,7 @@ import signal import shutil import glob import traceback +import ldap from optparse import OptionParser from ConfigParser import RawConfigParser import random @@ -49,6 +50,7 @@ from ipaserver.install import certs from ipaserver.install import service from ipapython import version from ipaserver.install.installutils import * +from ipaserver import ipaldap from ipapython import sysrestore from ipapython.ipautil import * @@ -117,6 +119,8 @@ def parse_options(): help="The starting uid value (default random)") parser.add_option("--gidstart", dest="gidstart", default=namespace, type=int, help="The starting gid value (default random)") + parser.add_option("--subject", dest="subject", default="O=IPA", + help="The certificate subject base (default O=IPA)") options, args = parser.parse_args() if not options.setup_dns: @@ -456,6 +460,20 @@ def render_assets(): ui = ipawebui.create_wsgi_app(api) ui.render_assets() +def set_subject_in_config(host_name, dm_password, suffix, subject_base): + try: + conn = ipaldap.IPAdmin(host_name) + conn.do_simple_bind(bindpw=dm_password) + except Exception, e: + logging.critical("Could not connect to the Directory Server on %s" % host_name) + raise e + entry = conn.getEntry("cn=ipaConfig, cn=etc, %s" % suffix, ldap.SCOPE_SUBTREE) + if entry.getValue('ipaCertificateSubjectBase') is None: + newentry = entry.toDict() + newentry['ipaCertificateSubjectBase'] = subject_base + conn.updateEntry(entry.dn, entry.toDict(), newentry) + + conn.unbind() def main(): global ds @@ -502,7 +520,7 @@ def main(): print "Aborting uninstall operation." sys.exit(1) - return uninstall(not certs.ipa_self_signed()) + return uninstall(not certs.ipa_self_signed() or options.ca) # This will override any settings passed in on the cmdline options._update_loose(read_cache()) @@ -702,12 +720,12 @@ def main(): cs.create_instance(ds_user, realm_name, host_name, domain_name, dm_password) ca = cainstance.CAInstance() if external == 0: - ca.configure_instance("pkiuser", host_name, dm_password, dm_password) + ca.configure_instance("pkiuser", host_name, dm_password, dm_password, subject_base=options.subject) elif external == 1: write_cache(options) - ca.configure_instance("pkiuser", host_name, dm_password, dm_password, csr_file="/root/ipa.csr") + ca.configure_instance("pkiuser", host_name, dm_password, dm_password, csr_file="/root/ipa.csr", subject_base=options.subject) else: - ca.configure_instance("pkiuser", host_name, dm_password, dm_password, cert_file=options.external_cert_file, cert_chain_file=options.external_ca_file) + ca.configure_instance("pkiuser", host_name, dm_password, dm_password, cert_file=options.external_cert_file, cert_chain_file=options.external_ca_file, subject_base=options.subject) # Configure ntpd if options.conf_ntp: @@ -719,11 +737,11 @@ def main(): if options.dirsrv_pkcs12: pkcs12_info = (options.dirsrv_pkcs12, pw_name) try: - ds.create_instance(ds_user, realm_name, host_name, domain_name, dm_password, pkcs12_info) + ds.create_instance(ds_user, realm_name, host_name, domain_name, dm_password, pkcs12_info, subject_base=options.subject) finally: os.remove(pw_name) else: - ds.create_instance(ds_user, realm_name, host_name, domain_name, dm_password, self_signed_ca=not options.ca, uidstart=options.uidstart, gidstart=options.gidstart) + ds.create_instance(ds_user, realm_name, host_name, domain_name, dm_password, self_signed_ca=not options.ca, uidstart=options.uidstart, gidstart=options.gidstart, subject_base=options.subject) # Create a kerberos instance krb = krbinstance.KrbInstance(fstore) @@ -747,10 +765,10 @@ def main(): http = httpinstance.HTTPInstance(fstore) if options.http_pkcs12: pkcs12_info = (options.http_pkcs12, pw_name) - http.create_instance(realm_name, host_name, domain_name, dm_password, autoconfig=False, pkcs12_info=pkcs12_info) + http.create_instance(realm_name, host_name, domain_name, dm_password, autoconfig=False, pkcs12_info=pkcs12_info, subject_base=options.subject) os.remove(pw_name) else: - http.create_instance(realm_name, host_name, domain_name, dm_password, autoconfig=True, self_signed_ca=not options.ca) + http.create_instance(realm_name, host_name, domain_name, dm_password, autoconfig=True, self_signed_ca=not options.ca, subject_base=options.subject) ipautil.run(["/sbin/restorecon", "/var/cache/ipa/sessions"]) # Create the management framework config file @@ -768,6 +786,11 @@ def main(): fd.write('webui_assets_dir=' + ASSETS_DIR + '\n') fd.close() + set_subject_in_config(host_name, dm_password, util.realm_to_suffix(realm_name), options.subject) + if options.ca: + service.print_msg("Setting the certificate subject base") + ca.set_subject_in_config(util.realm_to_suffix(realm_name)) + # Apply any LDAP updates. Needs to be done after the configuration file # is created service.print_msg("Applying LDAP updates") diff --git a/ipalib/plugins/config.py b/ipalib/plugins/config.py index 3030ce7c7..a3851e369 100644 --- a/ipalib/plugins/config.py +++ b/ipalib/plugins/config.py @@ -35,7 +35,7 @@ class config(LDAPObject): 'ipamaxusernamelength', 'ipahomesrootdir', 'ipadefaultloginshell', 'ipadefaultprimarygroup', 'ipadefaultdomain', 'ipasearchtimelimit', 'ipasearchrecordslimit', 'ipausersearchfields', 'ipagroupsearchfields', - 'ipamigrationenabled', + 'ipamigrationenabled', 'ipacertificatesubjectbase', ] attribute_names = { 'ipamaxusernamelength': 'maximum username length', @@ -48,52 +48,68 @@ class config(LDAPObject): 'ipausersearchfields': 'search fields for users', 'ipagroupsearchfields': 'search fields for groups', 'ipamigrationenabled': 'enable migration mode', + 'ipacertificatesubjectbase': 'base for certificate subjects', } takes_params = ( Int('ipamaxusernamelength?', cli_name='maxusername', + label='Max. Username length', doc='Max. Username length', minvalue=1, ), Str('ipahomesrootdir?', cli_name='homedirectory', + label='Home Directory base', doc='Default location of home directories', ), Str('ipadefaultloginshell?', cli_name='defaultshell', + label='Default shell', doc='Default shell for new users', ), Str('ipadefaultprimarygroup?', cli_name='defaultgroup', + label='Default users group', doc='Default group for new users', ), Str('ipadefaultemaildomain?', cli_name='emaildomain', + label='Default e-mail domain', doc='Default e-mail domain new users', ), Int('ipasearchtimelimit?', cli_name='searchtimelimit', + label='Search time limit', doc='Max. amount of time (sec.) for a search (-1 is unlimited)', minvalue=-1, ), Int('ipasearchrecordslimit?', cli_name='searchrecordslimit', + label='Search size limit', doc='Max. number of records to search (-1 is unlimited)', minvalue=-1, ), Str('ipausersearchfields?', cli_name='usersearch', + label='User search fields', doc='A comma-separated list of fields to search when searching for users', ), Str('ipagroupsearchfields?', cli_name='groupsearch', + label='Group search fields', doc='A comma-separated list of fields to search when searching for groups', ), Bool('ipamigrationenabled?', + doc='Migration mode', cli_name='enable_migration', doc='Enabled migration mode', ), + Str('ipacertificatesubjectbase?', + label='Certificate Subject base', + cli_name='subject', + doc='base for certificate subjects (OU=Test,O=Example)', + ), ) def get_dn(self, *keys, **kwargs): diff --git a/ipaserver/install/cainstance.py b/ipaserver/install/cainstance.py index 97ba833be..47183bb22 100644 --- a/ipaserver/install/cainstance.py +++ b/ipaserver/install/cainstance.py @@ -46,7 +46,6 @@ from ipapython import nsslib from ipaserver.install import service from ipaserver.install import installutils -from ipaserver import ipaldap from ipaserver.install import dsinstance from ipalib import util @@ -414,7 +413,8 @@ class CAInstance(service.Service): def configure_instance(self, pki_user, host_name, dm_password, admin_password, ds_port=DEFAULT_DSPORT, pkcs12_info=None, master_host=None, csr_file=None, - cert_file=None, cert_chain_file=None): + cert_file=None, cert_chain_file=None, + subject_base="O=IPA"): """Create a CA instance. This may involve creating the pki-ca instance dogtag instance. @@ -434,6 +434,7 @@ class CAInstance(service.Service): if self.pkcs12_info is not None: self.clone = True self.master_host = master_host + self.subject_base = subject_base # Determine if we are installing as an externally-signed CA and # what stage we're in. @@ -540,7 +541,7 @@ class CAInstance(service.Service): "-agent_name", "ipa-ca-agent", "-agent_key_size", "2048", "-agent_key_type", "rsa", - "-agent_cert_subject", "\"CN=ipa-ca-agent,O=" + self.domain_name + "\"", + "-agent_cert_subject", "\"CN=ipa-ca-agent,%s\"" % self.subject_base, "-ldap_host", self.host_name, "-ldap_port", str(self.ds_port), "-bind_dn", "\"cn=Directory Manager\"", @@ -553,11 +554,11 @@ class CAInstance(service.Service): "-backup_pwd", self.admin_password, "-subsystem_name", self.service_name, "-token_name", "internal", - "-ca_subsystem_cert_subject_name", "\"CN=CA Subsystem Certificate,O=" + self.domain_name + "\"", - "-ca_ocsp_cert_subject_name", "\"CN=OCSP Signing Certificate,O=" + self.domain_name + "\"", - "-ca_server_cert_subject_name", "CN=" + self.host_name + ",O=" + self.domain_name, - "-ca_audit_signing_cert_subject_name", "\"CN=CA Audit Signing Certificate,O=" + self.domain_name + "\"", - "-ca_sign_cert_subject_name", "\"CN=Certificate Authority,O=" + self.domain_name + "\"" ] + "-ca_subsystem_cert_subject_name", "\"CN=CA Subsystem,%s\"" % self.subject_base, + "-ca_ocsp_cert_subject_name", "\"CN=OCSP Subsystem,%s\"" % self.subject_base, + "-ca_server_cert_subject_name", "\"CN=%s,%s\"" % (self.host_name, self.subject_base), + "-ca_audit_signing_cert_subject_name", "\"CN=CA Audit,%s\"" % self.subject_base, + "-ca_sign_cert_subject_name", "\"CN=Certificate Authority,%s\"" % self.subject_base ] if self.external == 1: args.append("-external") args.append("true") @@ -770,7 +771,7 @@ class CAInstance(service.Service): ('usertype', "agentType"), ('userstate', "1"), ('userCertificate', decoded), - ('description', '2;%s;CN=Certificate Authority,O=%s;CN=RA Subsystem Certificate,OU=pki-ipa,O=%s' % (str(self.requestId), self.domain_name, self.domain_name)),] + ('description', '2;%s;CN=Certificate Authority,%s;CN=RA Subsystem,%s' % (str(self.requestId), self.subject_base, self.subject_base)),] ld.add_s(entry_dn, entry) @@ -886,7 +887,7 @@ class CAInstance(service.Service): # Generate our CSR. The result gets put into stdout try: - (stdout, stderr, returncode) = self.__run_certutil(["-R", "-k", "rsa", "-g", "2048", "-s", "CN=RA Subsystem Certificate,OU=pki-ipa,O=%s" % self.domain_name, "-z", noise_name, "-a"]) + (stdout, stderr, returncode) = self.__run_certutil(["-R", "-k", "rsa", "-g", "2048", "-s", "CN=RA Subsystem,%s" % self.subject_base, "-z", noise_name, "-a"]) finally: os.remove(noise_name) @@ -1000,6 +1001,13 @@ class CAInstance(service.Service): ipautil.run(["/usr/sbin/semodule", "-i", "/usr/share/selinux/targeted/ipa_dogtag.pp"]) + def set_subject_in_config(self, suffix): + # dogtag ships with an IPA-specific profile that forces a subject + # format. We need to update that template with our base subject + if installutils.update_file("/var/lib/%s/profiles/ca/caIPAserviceCert.cfg" % PKI_INSTANCE_NAME, 'OU=pki-ipa, O=IPA', self.subject_base): + print "Updating subject_base in CA template failed" + self.__restart_instance() + def uninstall(self): try: ipautil.run(["/usr/bin/pkiremove", "-pki_instance_root=/var/lib", diff --git a/ipaserver/install/certs.py b/ipaserver/install/certs.py index a1dffff24..6e7eb82d1 100644 --- a/ipaserver/install/certs.py +++ b/ipaserver/install/certs.py @@ -173,7 +173,7 @@ def next_replica(serial_file=CA_SERIALNO): return str(serial) class CertDB(object): - def __init__(self, nssdir, fstore=None, host_name=None): + def __init__(self, nssdir, fstore=None, host_name=None, subject_base=None): self.secdir = nssdir self.noise_fname = self.secdir + "/noise.txt" @@ -189,13 +189,14 @@ class CertDB(object): self.certreq_fname = None self.certder_fname = None self.host_name = host_name + self.cwd = os.getcwd() self.self_signed_ca = ipa_self_signed() - if self.self_signed_ca: - self.subject_format = "CN=%s,ou=test-ipa,O=IPA" + if subject_base: + self.subject_format = "CN=%%s,%s" % subject_base else: - self.subject_format = "CN=%s,OU=pki-ipa,O=IPA" + self.subject_format = "CN=%s,O=IPA" self.cacert_name = "CA certificate" self.valid_months = "120" @@ -218,6 +219,10 @@ class CertDB(object): def __del__(self): if self.reqdir is not None: shutil.rmtree(self.reqdir, ignore_errors=True) + try: + os.chdir(self.cwd) + except: + pass def setup_cert_request(self): """ @@ -234,6 +239,10 @@ class CertDB(object): self.certreq_fname = self.reqdir + "/tmpcertreq" self.certder_fname = self.reqdir + "/tmpcert.der" + # When certutil makes a request it creates a file in the cwd, make + # sure we are in a unique place when this happens + os.chdir(self.reqdir) + def set_serial_from_pkcs12(self): """A CA cert was loaded from a PKCS#12 file. Set up our serial file""" @@ -584,6 +593,9 @@ class CertDB(object): doc.unlink() conn.close() + # base64-decode the result + cert = base64.b64decode(cert) + # Write the certificate to a file. It will be imported in a later # step. f = open(cert_fname, "w") @@ -670,6 +682,9 @@ class CertDB(object): doc.unlink() conn.close() + # base64-decode the cert + cert = base64.b64decode(cert) + f = open(cert_fname, "w") f.write(cert) f.close() @@ -684,8 +699,6 @@ class CertDB(object): "-t", "u,u,u", "-i", cert_fname, "-f", self.passwd_fname] - if not self.self_signed_ca: - args.append("-a") self.run_certutil(args) def create_pin_file(self): diff --git a/ipaserver/install/dsinstance.py b/ipaserver/install/dsinstance.py index 33ff053c3..4fcb914cf 100644 --- a/ipaserver/install/dsinstance.py +++ b/ipaserver/install/dsinstance.py @@ -153,7 +153,7 @@ class DsInstance(service.Service): else: self.suffix = None - def create_instance(self, ds_user, realm_name, host_name, domain_name, dm_password, pkcs12_info=None, self_signed_ca=False, uidstart=1100, gidstart=1100): + def create_instance(self, ds_user, realm_name, host_name, domain_name, dm_password, pkcs12_info=None, self_signed_ca=False, uidstart=1100, gidstart=1100, subject_base=None): self.ds_user = ds_user self.realm_name = realm_name.upper() self.serverid = realm_to_serverid(self.realm_name) @@ -166,6 +166,7 @@ class DsInstance(service.Service): self.uidstart = uidstart self.gidstart = gidstart self.principal = "ldap/%s@%s" % (self.host_name, self.realm_name) + self.subject_base = subject_base self.__setup_sub_dict() self.step("creating directory server user", self.__create_ds_user) @@ -328,7 +329,7 @@ class DsInstance(service.Service): def __enable_ssl(self): dirname = config_dirname(self.serverid) - dsdb = certs.CertDB(dirname) + dsdb = certs.CertDB(dirname, subject_base=self.subject_base) if self.pkcs12_info: dsdb.create_from_pkcs12(self.pkcs12_info[0], self.pkcs12_info[1]) server_certs = dsdb.find_server_certs() @@ -340,7 +341,7 @@ class DsInstance(service.Service): self.dercert = dsdb.get_cert_from_db(nickname) else: nickname = "Server-Cert" - cadb = certs.CertDB(httpinstance.NSS_DIR, host_name=self.host_name) + cadb = certs.CertDB(httpinstance.NSS_DIR, host_name=self.host_name, subject_base=self.subject_base) if self.self_signed_ca: cadb.create_self_signed() dsdb.create_from_cacert(cadb.cacert_fname, passwd=None) @@ -466,7 +467,7 @@ class DsInstance(service.Service): self.stop() dirname = config_dirname(realm_to_serverid(self.realm_name)) - certdb = certs.CertDB(dirname) + certdb = certs.CertDB(dirname, subject_base=self.subject_base) if not cacert_name or len(cacert_name) == 0: cacert_name = "Imported CA" # we can't pass in the nickname, so we set the instance variable diff --git a/ipaserver/install/httpinstance.py b/ipaserver/install/httpinstance.py index ee62f81f2..3ff5cf8a6 100644 --- a/ipaserver/install/httpinstance.py +++ b/ipaserver/install/httpinstance.py @@ -56,7 +56,7 @@ class HTTPInstance(service.Service): else: self.fstore = sysrestore.FileStore('/var/lib/ipa/sysrestore') - def create_instance(self, realm, fqdn, domain_name, dm_password=None, autoconfig=True, pkcs12_info=None, self_signed_ca=False): + def create_instance(self, realm, fqdn, domain_name, dm_password=None, autoconfig=True, pkcs12_info=None, self_signed_ca=False, subject_base=None): self.fqdn = fqdn self.realm = realm self.domain = domain_name @@ -66,6 +66,7 @@ class HTTPInstance(service.Service): self.self_signed_ca = self_signed_ca self.principal = "HTTP/%s@%s" % (self.fqdn, self.realm) self.dercert = None + self.subject_base = subject_base self.sub_dict = { "REALM" : realm, "FQDN": fqdn, "DOMAIN" : self.domain } self.step("disabling mod_ssl in httpd", self.__disable_mod_ssl) @@ -164,10 +165,10 @@ class HTTPInstance(service.Service): def __setup_ssl(self): if self.self_signed_ca: - ca_db = certs.CertDB(NSS_DIR) + ca_db = certs.CertDB(NSS_DIR, subject_base=self.subject_base) else: - ca_db = certs.CertDB(NSS_DIR, host_name=self.fqdn) - db = certs.CertDB(NSS_DIR) + ca_db = certs.CertDB(NSS_DIR, host_name=self.fqdn, subject_base=self.subject_base) + db = certs.CertDB(NSS_DIR, subject_base=self.subject_base) if self.pkcs12_info: db.create_from_pkcs12(self.pkcs12_info[0], self.pkcs12_info[1], passwd="") server_certs = db.find_server_certs() @@ -221,7 +222,7 @@ class HTTPInstance(service.Service): prefs_fd.close() # The signing cert is generated in __setup_ssl - db = certs.CertDB(NSS_DIR) + db = certs.CertDB(NSS_DIR, subject_base=self.subject_base) pwdfile = open(db.passwd_fname) pwd = pwdfile.read() diff --git a/ipaserver/install/service.py b/ipaserver/install/service.py index 5e2eb63dc..5aee093ec 100644 --- a/ipaserver/install/service.py +++ b/ipaserver/install/service.py @@ -160,9 +160,15 @@ class Service: Add a certificate to a service This should be passed in DER format but we'll be nice and convert - a base64-encoded cert if needed. + a base64-encoded cert if needed (like when we add certs that come + from PKCS#12 files.) """ try: + s = self.dercert.find('-----BEGIN CERTIFICATE-----') + if s > -1: + e = self.dercert.find('-----END CERTIFICATE-----') + s = s + 27 + self.dercert = self.dercert[s:e] self.dercert = base64.b64decode(self.dercert) except Exception: pass diff --git a/ipaserver/plugins/selfsign.py b/ipaserver/plugins/selfsign.py index 7d5dafa7d..af832a610 100644 --- a/ipaserver/plugins/selfsign.py +++ b/ipaserver/plugins/selfsign.py @@ -38,13 +38,18 @@ if api.env.ra_plugin != 'selfsign': from ipalib import Backend from ipalib import errors from ipalib import x509 +from ipalib import pkcs10 import subprocess import os +import re from ipaserver.plugins import rabase from ipaserver.install import certs import tempfile from pyasn1 import error from ipalib.request import ugettext as _ +from pyasn1.codec.der import encoder +import base64 +from ipalib.plugins.cert import get_csr_hostname class ra(rabase.rabase): """ @@ -79,6 +84,28 @@ class ra(rabase.rabase): .. [2] Base64 encoded """ + try: + config = api.Command['config_show']()['result'] + subject_base = config.get('ipacertificatesubjectbase')[0] + hostname = get_csr_hostname(csr) + request = pkcs10.load_certificate_request(csr) + base = re.split(',\s*(?=\w+=)', subject_base) + base.reverse() + base.append("CN=%s" % hostname) + request_subject = request.get_subject().get_components() + new_request = [] + for r in request_subject: + new_request.append("%s=%s" % (r[0], r[1])) + + if str(base).lower() != str(new_request).lower(): + subject_base='CN=%s, %s' % (hostname, subject_base) + new_request.reverse() + raise errors.CertificateOperationError(error=_('Request subject \'%s\' does not match the form \'%s\'' % (", ".join(new_request), subject_base))) + except errors.CertificateOperationError, e: + raise e + except Exception, e: + raise errors.CertificateOperationError(error=_('unable to decode csr: %s' % e)) + # certutil wants the CSR to have have a header and footer. Add one # if it isn't there. s = csr.find('-----BEGIN NEW CERTIFICATE REQUEST-----') @@ -86,7 +113,7 @@ class ra(rabase.rabase): s = csr.find('-----BEGIN CERTIFICATE REQUEST-----') if s == -1: csr = '-----BEGIN NEW CERTIFICATE REQUEST-----\n' + csr + \ - '-----END NEW CERTIFICATE REQUEST-----\n' + '\n-----END NEW CERTIFICATE REQUEST-----\n' try: (csr_fd, csr_name) = tempfile.mkstemp()