On Mon, 8 Nov 2010 19:34:12 -0500
Simo Sorce <sso...@redhat.com> wrote:

> Patch 0004: Add basic certification creation for selfsigned CA and KDC
> configuration. opnessl had to be used because the NSS tools cannot
> deal with the special subjectaltName needed for the KDC certificate.

Rebased this one on top of master (there was a conflict after latest
pushes).

Simo.

-- 
Simo Sorce * Red Hat, Inc * New York
>From d3ab2c9a543a431a0c3accc160e96a41ce143bd0 Mon Sep 17 00:00:00 2001
From: Simo Sorce <sso...@redhat.com>
Date: Fri, 29 Oct 2010 16:23:21 -0400
Subject: [PATCH] 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.
---
 install/share/Makefile.am             |    2 +
 install/share/kdc.conf.template       |    2 +
 install/share/kdc_extensions.template |   32 ++++++++++++
 install/share/kdc_req.conf.template   |   14 +++++
 install/tools/ipa-server-install      |   36 +++++++++++++-
 ipaserver/install/certs.py            |   88 +++++++++++++++++++++++++++++++--
 ipaserver/install/krbinstance.py      |   46 +++++++++++++++++-
 7 files changed, 214 insertions(+), 6 deletions(-)
 create mode 100644 install/share/kdc_extensions.template
 create mode 100644 install/share/kdc_req.conf.template

diff --git a/install/share/Makefile.am b/install/share/Makefile.am
index e4b6ca385529e118a1a486eb8687b19e78f5f133..3423ce2874021e3380832c190ff10848d2e2c216 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 4a2cca412c7a5a1b8a45f6d114ec844aa02822ea..f8e07c77bdbffe3d73baf016aae1b9733adb7390 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 0000000000000000000000000000000000000000..df992babd406b0f7cffbfca1539d38a862b29f8c
--- /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 0000000000000000000000000000000000000000..872852079c09584d3db41d63788d95630029aab2
--- /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 b58675fb2dc7e938186e0da4e85e994d36e8fa57..464ebe640c5f78b5b4e93c7ac9dc4bc20171fbd3 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")
@@ -496,6 +502,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:"
@@ -522,6 +530,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
@@ -715,9 +729,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 d4728b80e6831061c5e3c4848e5a3b4c82b009ee..3fa65207cbd1cbb206ea7f8294401664759bacfa 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 d8a5eff246ff97b7c9dd0166090918dbbc1a29fb..bfcb869993bc8e7a7060047bbb3fbe4117680f71 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)
-- 
1.7.3.2

_______________________________________________
Freeipa-devel mailing list
Freeipa-devel@redhat.com
https://www.redhat.com/mailman/listinfo/freeipa-devel

Reply via email to