diff options
author | Simo Sorce <ssorce@redhat.com> | 2010-10-29 16:23:21 -0400 |
---|---|---|
committer | Simo Sorce <ssorce@redhat.com> | 2010-11-18 15:09:36 -0500 |
commit | 52a46d121bf760f6beca4622ace0a4554a679c3c (patch) | |
tree | 550a2bddf9ab3848da9ab33ca73529060b9e4c68 | |
parent | 74ba0cc7c1bdb9c560324a68c16593755bcda5d8 (diff) | |
download | freeipa-52a46d121bf760f6beca4622ace0a4554a679c3c.tar.gz freeipa-52a46d121bf760f6beca4622ace0a4554a679c3c.tar.xz freeipa-52a46d121bf760f6beca4622ace0a4554a679c3c.zip |
Add support for configuring KDC certs for PKINIT
This patch adds support only for the selfsign case.
Replica support is also still missing at this stage.
-rw-r--r-- | install/share/Makefile.am | 2 | ||||
-rw-r--r-- | install/share/kdc.conf.template | 2 | ||||
-rw-r--r-- | install/share/kdc_extensions.template | 32 | ||||
-rw-r--r-- | install/share/kdc_req.conf.template | 14 | ||||
-rwxr-xr-x | install/tools/ipa-server-install | 36 | ||||
-rw-r--r-- | ipaserver/install/certs.py | 88 | ||||
-rw-r--r-- | ipaserver/install/krbinstance.py | 46 |
7 files changed, 214 insertions, 6 deletions
diff --git a/install/share/Makefile.am b/install/share/Makefile.am index e4b6ca385..3423ce287 100644 --- a/install/share/Makefile.am +++ b/install/share/Makefile.am @@ -24,6 +24,8 @@ app_DATA = \ bind.zone.db.template \ certmap.conf.template \ kdc.conf.template \ + kdc_extensions.template \ + kdc_req.conf.template \ krb5.conf.template \ krb5.ini.template \ krb.con.template \ diff --git a/install/share/kdc.conf.template b/install/share/kdc.conf.template index 4a2cca412..f8e07c77b 100644 --- a/install/share/kdc.conf.template +++ b/install/share/kdc.conf.template @@ -12,4 +12,6 @@ dict_file = /usr/share/dict/words default_principal_flags = +preauth ; admin_keytab = /var/kerberos/krb5kdc/kadm5.keytab + pkinit_identity = FILE:/var/kerberos/krb5kdc/kdc.pem + pkinit_anchors = FILE:/var/kerberos/krb5kdc/cacert.pem } diff --git a/install/share/kdc_extensions.template b/install/share/kdc_extensions.template new file mode 100644 index 000000000..df992babd --- /dev/null +++ b/install/share/kdc_extensions.template @@ -0,0 +1,32 @@ +[ kdc_cert ] +basicConstraints=CA:FALSE + +# Here are some examples of the usage of nsCertType. If it is omitted +keyUsage = nonRepudiation, digitalSignature, keyEncipherment, keyAgreement + +#Pkinit EKU +extendedKeyUsage = 1.3.6.1.5.2.3.5 + +subjectKeyIdentifier=hash +authorityKeyIdentifier=keyid,issuer + +# Copy subject details + +issuerAltName=issuer:copy + +# Add id-pkinit-san (pkinit subjectAlternativeName) +# Also add the KDC fqdn, for good measure. +subjectAltName=otherName:1.3.6.1.5.2.2;SEQUENCE:kdc_princ_name,DNS:${ENV::HOST_FQDN} + +[kdc_princ_name] +realm = EXP:0, GeneralString:${ENV::REALM} +principal_name = EXP:1, SEQUENCE:kdc_principal_seq + +[kdc_principal_seq] +name_type = EXP:0, INTEGER:1 +name_string = EXP:1, SEQUENCE:kdc_principals + +[kdc_principals] +princ1 = GeneralString:krbtgt +princ2 = GeneralString:${ENV::REALM} + diff --git a/install/share/kdc_req.conf.template b/install/share/kdc_req.conf.template new file mode 100644 index 000000000..872852079 --- /dev/null +++ b/install/share/kdc_req.conf.template @@ -0,0 +1,14 @@ +[ req ] +default_bits = 2048 +distinguished_name = req_distinguished_name +attributes = req_attributes +prompt = no +output_password = $PASSWORD + +[ req_distinguished_name ] +$SUBJBASE +$CERTNAME + +[ req_attributes ] +challengePassword = A challenge password + diff --git a/install/tools/ipa-server-install b/install/tools/ipa-server-install index 569079d5a..0584c1118 100755 --- a/install/tools/ipa-server-install +++ b/install/tools/ipa-server-install @@ -106,14 +106,20 @@ def parse_options(): default=False, help="uninstall an existing installation") parser.add_option("-N", "--no-ntp", dest="conf_ntp", action="store_false", help="do not configure ntp", default=True) + parser.add_option("--no-pkinit", dest="setup_pkinit", action="store_false", + default=True, help="disables pkinit setup steps") parser.add_option("--dirsrv_pkcs12", dest="dirsrv_pkcs12", help="PKCS#12 file containing the Directory Server SSL certificate") parser.add_option("--http_pkcs12", dest="http_pkcs12", help="PKCS#12 file containing the Apache Server SSL certificate") + parser.add_option("--pkinit_pkcs12", dest="pkinit_pkcs12", + help="PKCS#12 file containing the Kerberos KDC SSL certificate") parser.add_option("--dirsrv_pin", dest="dirsrv_pin", sensitive=True, help="The password of the Directory Server PKCS#12 file") parser.add_option("--http_pin", dest="http_pin", sensitive=True, help="The password of the Apache Server PKCS#12 file") + parser.add_option("--pkinit_pin", dest="pkinit_pin", + help="The password of the Kerberos KDC PKCS#12 file") parser.add_option("--no-host-dns", dest="no_host_dns", action="store_true", default=False, help="Do not use DNS for hostname lookup during installation") @@ -503,6 +509,8 @@ def main(): print " * Configure Apache (httpd)" if options.setup_dns: print " * Configure DNS (bind)" + if options.setup_pkinit: + print " * Configure the KDC to enable PKINIT" if not options.conf_ntp: print "" print "Excluded by options:" @@ -529,6 +537,12 @@ def main(): print "Aborting installation" return 1 + # check the pkinit plugin is installed + if options.setup_pkinit: + if not krbinstance.check_pkinit_plugin(): + print "Aborting installation" + return 1 + # check the hostname is correctly configured, it must be as the kldap # utilities just use the hostname as returned by gethostbyname to set # up some of the standard entries @@ -722,9 +736,29 @@ def main(): else: ds.create_instance(ds_user, realm_name, host_name, domain_name, dm_password, self_signed_ca=options.selfsign, uidstart=options.uidstart, gidstart=options.gidstart, subject_base=options.subject, hbac_allow=not options.hbac_allow) + if options.pkinit_pin: + [pw_fd, pw_name] = tempfile.mkstemp() + os.write(pw_fd, options.dirsrv_pin) + os.close(pw_fd) + # Create a kerberos instance krb = krbinstance.KrbInstance(fstore) - krb.create_instance(ds_user, realm_name, host_name, domain_name, dm_password, master_password) + if options.pkinit_pkcs12: + pkcs12_info = (options.pkinit_pkcs12, pw_name) + krb.create_instance(ds_user, realm_name, host_name, domain_name, + dm_password, master_password, + setup_pkinit=options.setup_pkinit, + pkcs12_info=pkcs12_info, + subject_base=options.subject) + else: + krb.create_instance(ds_user, realm_name, host_name, domain_name, + dm_password, master_password, + setup_pkinit=options.setup_pkinit, + self_signed_ca=options.selfsign, + subject_base=options.subject) + + if options.pkinit_pin: + os.remove(pw_name) # The DS instance is created before the keytab, add the SSL cert we # generated diff --git a/ipaserver/install/certs.py b/ipaserver/install/certs.py index d4728b80e..3fa65207c 100644 --- a/ipaserver/install/certs.py +++ b/ipaserver/install/certs.py @@ -180,6 +180,7 @@ class CertDB(object): self.certreq_fname = None self.certder_fname = None self.host_name = host_name + self.subject_base = subject_base try: self.cwd = os.getcwd() except OSError, e: @@ -187,10 +188,9 @@ class CertDB(object): self.self_signed_ca = ipa_self_signed() - if subject_base: - self.subject_format = "CN=%%s,%s" % subject_base - else: - self.subject_format = "CN=%s,O=IPA" + if not subject_base: + self.subject_base = "O=IPA" + self.subject_format = "CN=%%s,%s" % self.subject_base self.cacert_name = get_ca_nickname(self.realm) self.valid_months = "120" @@ -937,6 +937,86 @@ class CertDB(object): except: pass + def create_kdc_cert(self, nickname, hostname, destdir): + """Create a new certificate with the spcial othername encoding needed + by a KDC certificate. + + nickname: the CN name set in the certificate + destdir: the location where cert and key are to be installed + + destdir will contain kdc.pem if the operation is successful + """ + + reqcfg = "kdc_req.conf" + extcfg = ipautil.SHARE_DIR + "kdc_extensions.template" + key_fname = destdir + "/kdckey.pem" + cert_fname = destdir + "/kdccert.pem" + key_cert_fname = destdir + "/kdc.pem" + + # Setup the temp dir + self.setup_cert_request() + + # Copy the CA password file because openssl apparently can't use + # the same file twice within the same command and throws an error + ca_pwd_file = self.reqdir + "pwdfile.txt" + shutil.copyfile(self.passwd_fname, ca_pwd_file) + + # Extract the cacert.pem file used by openssl to sign the certs + ipautil.run(["/usr/bin/openssl", "pkcs12", + "-in", self.pk12_fname, + "-passin", "file:" + self.passwd_fname, + "-passout", "file:" + ca_pwd_file, + "-out", "cacert.pem"]) + + # Create the kdc key + ipautil.run(["/usr/bin/openssl", "genrsa", + "-out", key_fname, "2048"]) + + # Prepare a simple cert request + req_dict = dict(PASSWORD=self.gen_password(), + SUBJBASE=self.subject_base, + CERTNAME="CN="+nickname) + req_template = ipautil.SHARE_DIR + reqcfg + ".template" + conf = ipautil.template_file(req_template, req_dict) + fd = open(reqcfg, "w+") + fd.write(conf) + fd.close() + + base = self.subject_base.replace(",", "/") + esc_subject = "CN=%s/%s" % (nickname, base) + + ipautil.run(["/usr/bin/openssl", "req", "-new", + "-config", reqcfg, + "-subj", esc_subject, + "-key", key_fname, + "-out", "kdc.req"]) + + # Finally, sign the cert using the extensions file to set the + # special name + ipautil.run(["/usr/bin/openssl", "x509", "-req", + "-CA", "cacert.pem", + "-extfile", extcfg, + "-extensions", "kdc_cert", + "-passin", "file:" + ca_pwd_file, + "-set_serial", next_serial(), + "-in", "kdc.req", + "-out", cert_fname], + env = { 'REALM':self.realm, 'HOST_FQDN':hostname }) + + # Merge key and cert in a single file + fd = open(key_fname, "r") + key = fd.read() + fd.close() + fd = open(cert_fname, "r") + cert = fd.read() + fd.close() + fd = open(key_cert_fname, "w") + fd.write(key) + fd.write(cert) + fd.close() + os.unlink(key_fname) + os.unlink(cert_fname) + def backup_files(self): self.fstore.backup_file(self.noise_fname) self.fstore.backup_file(self.passwd_fname) diff --git a/ipaserver/install/krbinstance.py b/ipaserver/install/krbinstance.py index d8a5eff24..bfcb86999 100644 --- a/ipaserver/install/krbinstance.py +++ b/ipaserver/install/krbinstance.py @@ -44,8 +44,21 @@ import pyasn1.codec.ber.encoder import pyasn1.codec.ber.decoder import struct +import certs +import httpinstance + KRBMKEY_DENY_ACI = '(targetattr = "krbMKey")(version 3.0; acl "No external access"; deny (read,write,search,compare) userdn != "ldap:///uid=kdc,cn=sysaccounts,cn=etc,$SUFFIX";)' +def check_pkinit_plugin(): + LIB32 = '/usr/lib/krb5/plugins/preauth/pkinit.so' + LIB64 = '/usr/lib64/krb5/plugins/preauth/pkinit.so' + if not os.path.exists(LIB32) and not os.path.exists(LIB64): + print "The pkinit plugin is missing" + print "Please install the 'krb5-pkinit-openssl' package and start the installation again" + return False + + return True + def update_key_val_in_file(filename, key, val): if os.path.exists(filename): pattern = "^[\s#]*%s\s*=\s*%s\s*" % (re.escape(key), re.escape(val)) @@ -83,6 +96,8 @@ class KrbInstance(service.Service): self.suffix = None self.kdc_password = None self.sub_dict = None + self.pkcs12_info = None + self.self_signed_ca = None if fstore: self.fstore = fstore @@ -158,8 +173,11 @@ class KrbInstance(service.Service): self.step("starting the KDC", self.__start_instance) self.step("configuring KDC to start on boot", self.__enable) - def create_instance(self, ds_user, realm_name, host_name, domain_name, admin_password, master_password): + def create_instance(self, ds_user, realm_name, host_name, domain_name, admin_password, master_password, setup_pkinit=False, pkcs12_info=None, self_signed_ca=False, subject_base=None): self.master_password = master_password + self.pkcs12_info = pkcs12_info + self.self_signed_ca = self_signed_ca + self.subject_base = subject_base self.__common_setup(ds_user, realm_name, host_name, domain_name, admin_password) @@ -175,6 +193,8 @@ class KrbInstance(service.Service): self.step("exporting the kadmin keytab", self.__export_kadmin_changepw_keytab) self.step("adding the password extension to the directory", self.__add_pwd_extop_module) self.step("adding the kerberos master key to the directory", self.__add_master_key) + if setup_pkinit: + self.step("creating X509 Certificate for PKINIT", self.__setup_pkinit) self.__common_post_setup() @@ -477,6 +497,30 @@ class KrbInstance(service.Service): self.fstore.backup_file("/etc/sysconfig/ipa_kpasswd") update_key_val_in_file("/etc/sysconfig/ipa_kpasswd", "export KRB5_KTNAME", "/var/kerberos/krb5kdc/kpasswd.keytab") + def __setup_pkinit(self): + if self.self_signed_ca: + ca_db = certs.CertDB(httpinstance.NSS_DIR, self.realm, + subject_base=self.subject_base) + else: + ca_db = certs.CertDB(httpinstance.NSS_DIR, self.realm, + host_name=self.fqdn, + subject_base=self.subject_base) + if self.pkcs12_info: + + raise RuntimeError("Using PKCS12 Certs not supported yet\n") + + else: + if self.self_signed_ca: + ca_db.create_kdc_cert("KDC-Cert", self.fqdn, + "/var/kerberos/krb5kdc") + else: + raise RuntimeError("Using PKCS12 Certs not supported yet\n") + + # Finally copy the cacert in the krb directory so we don't + # have any selinux issues with the file context + shutil.copyfile("/usr/share/ipa/html/ca.crt", + "/var/kerberos/krb5kdc/cacert.pem") + def uninstall(self): if self.is_configured(): self.print_msg("Unconfiguring %s" % self.service_name) |