On Tue, 9 Nov 2010 15:12:25 -0500
Simo Sorce <sso...@redhat.com> wrote:

> 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.
> 

This is a rebase of all the patches in this thread on top of current
master.

-- 
Simo Sorce * Red Hat, Inc * New York
>From b70eca77b31c4f6e5c7b2fce93f6badca230838c Mon Sep 17 00:00:00 2001
From: Simo Sorce <sso...@redhat.com>
Date: Mon, 1 Nov 2010 13:51:14 -0400
Subject: [PATCH 01/12] Use Realm as certs subject base name

Also use the realm name as nickname for the CA certificate
---
 install/tools/ipa-replica-install         |    2 +-
 install/tools/ipa-replica-prepare         |    6 +++---
 install/tools/ipa-server-certinstall      |    2 +-
 install/tools/ipa-server-install          |   14 ++++++++------
 install/tools/man/ipa-server-install.1    |    2 +-
 ipa-client/ipa-install/ipa-client-install |    4 ++--
 ipapython/certdb.py                       |    4 +++-
 ipaserver/install/cainstance.py           |   16 ++++++++++------
 ipaserver/install/certs.py                |   15 ++++++++++-----
 ipaserver/install/dsinstance.py           |    8 ++++----
 ipaserver/install/httpinstance.py         |   14 +++++++-------
 ipaserver/plugins/selfsign.py             |    5 +++--
 12 files changed, 53 insertions(+), 39 deletions(-)

diff --git a/install/tools/ipa-replica-install b/install/tools/ipa-replica-install
index e4aae4aa3c34e283b3da71c8fcd3193ab901ca83..2beadae812dbe87927855dab42e2a44802e38a02 100755
--- a/install/tools/ipa-replica-install
+++ b/install/tools/ipa-replica-install
@@ -48,7 +48,7 @@ class ReplicaConfig:
         self.host_name = ""
         self.repl_password = ""
         self.dir = ""
-        self.subject_base = "O=IPA"
+        self.subject_base = ""
 
 def parse_options():
     usage = "%prog [options] REPLICA_FILE"
diff --git a/install/tools/ipa-replica-prepare b/install/tools/ipa-replica-prepare
index 6e9d649ae6146c850ce6780efa7cccbe1f6100f4..059b011f9f60128ceba171cf5129e8c769e1eaf3 100755
--- a/install/tools/ipa-replica-prepare
+++ b/install/tools/ipa-replica-prepare
@@ -103,14 +103,14 @@ def export_certdb(realm_name, ds_dir, dir, passwd_fname, fname, hostname, subjec
     try:
         self_signed = certs.ipa_self_signed()
 
-        db = certs.CertDB(dir, subject_base=subject_base)
+        db = certs.CertDB(dir, realm_name, 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=api.env.host)
-        ca_db = certs.CertDB(httpinstance.NSS_DIR, host_name=api.env.host, subject_base=subject_base)
+        ca_db = certs.CertDB(httpinstance.NSS_DIR, realm_name, host_name=api.env.host, subject_base=subject_base)
         db.create_from_cacert(ca_db.cacert_fname)
         db.create_server_cert("Server-Cert", hostname, ca_db)
     except Exception, e:
@@ -148,7 +148,7 @@ def export_ra_pkcs12(dir, dm_password):
 
     try:
         try:
-            db = certs.CertDB(httpinstance.NSS_DIR, host_name=api.env.host)
+            db = certs.CertDB(httpinstance.NSS_DIR, api.env.realm, host_name=api.env.host)
 
             if db.has_nickname("ipaCert"):
                 pkcs12_fname = "%s/ra.p12" % dir
diff --git a/install/tools/ipa-server-certinstall b/install/tools/ipa-server-certinstall
index d853f71889e42d5f9570d153c44442b978ef9965..9d69853e51308234cb3330b18e9d5665de62f5ca 100755
--- a/install/tools/ipa-server-certinstall
+++ b/install/tools/ipa-server-certinstall
@@ -89,7 +89,7 @@ def choose_server_cert(server_certs):
     return server_certs[num - 1]
 
 def import_cert(dirname, pkcs12_fname, pkcs12_passwd, db_password):
-    cdb = certs.CertDB(dirname)
+    cdb = certs.CertDB(dirname, api.env.realm)
     cdb.create_passwd_file(db_password)
     cdb.create_certdbs()
     [pw_fd, pw_name] = tempfile.mkstemp()
diff --git a/install/tools/ipa-server-install b/install/tools/ipa-server-install
index c8a17c99d8eee92759d309874e7159095e1d5ca7..569079d5a60cd3b0af09965c952c05fdaca0a1b4 100755
--- a/install/tools/ipa-server-install
+++ b/install/tools/ipa-server-install
@@ -122,8 +122,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)")
+    parser.add_option("--subject", dest="subject",
+                      help="The certificate subject base (default O=<realm-name>)")
     parser.add_option("--no_hbac_allow", dest="hbac_allow", default=False,
                       action="store_true",
                       help="Don't install allow_all HBAC rule")
@@ -402,8 +402,8 @@ def uninstall():
     ntpinstance.NTPInstance(fstore).uninstall()
     if cainstance.CADSInstance().is_configured():
         cainstance.CADSInstance().uninstall()
-    if cainstance.CAInstance().is_configured():
-        cainstance.CAInstance().uninstall()
+    if cainstance.CAInstance(api.env.realm).is_configured():
+        cainstance.CAInstance(api.env.realm).uninstall()
     bindinstance.BindInstance(fstore).uninstall()
     httpinstance.HTTPInstance(fstore).uninstall()
     krbinstance.KrbInstance(fstore).uninstall()
@@ -465,7 +465,6 @@ def main():
     global fstore
     fstore = sysrestore.FileStore('/var/lib/ipa/sysrestore')
 
-
     # Configuration for ipalib, we will bootstrap and finalize later, after
     # we are sure we have the configuration file ready.
     cfg = dict(
@@ -610,6 +609,9 @@ def main():
     else:
         realm_name = options.realm_name.upper()
 
+    if not options.subject:
+        options.subject = "O=%s" % realm_name
+
     if not options.dm_password:
         dm_password = read_dm_password()
     else:
@@ -689,7 +691,7 @@ def main():
         if options.external_cert_file is None:
             cs = cainstance.CADSInstance()
             cs.create_instance("pkisrv", realm_name, host_name, domain_name, dm_password)
-        ca = cainstance.CAInstance()
+        ca = cainstance.CAInstance(realm_name)
         if external == 0:
             ca.configure_instance("pkiuser", host_name, dm_password, dm_password, subject_base=options.subject)
         elif external == 1:
diff --git a/install/tools/man/ipa-server-install.1 b/install/tools/man/ipa-server-install.1
index 943a420145518cc415e2005b0e7d26e6472594ad..0d4d8c523ecd4e3c74cbeaacb3f8da44ecbed8e7 100644
--- a/install/tools/man/ipa-server-install.1
+++ b/install/tools/man/ipa-server-install.1
@@ -102,7 +102,7 @@ The starting user id number (default random)
 The starting group id number (default random)
 .TP
 \fB\-\-subject\fR=\fISUBJECT\fR
- The certificate subject base (default O=IPA)
+ The certificate subject base (default O=REALM.NAME)
 .TP
 \fB\-\-no_hbac_allow\fR
 Don't install allow_all HBAC rule. This rule lets any user from any host access any service on any other host. It is expected that users will remove this rule before moving to production.
diff --git a/ipa-client/ipa-install/ipa-client-install b/ipa-client/ipa-install/ipa-client-install
index 8f4b9d2bb7d15d89ceaca45f100f8a8d2123129a..fa9d27566c79aea5fb5d43ad2aed5564ed6a4ee7 100755
--- a/ipa-client/ipa-install/ipa-client-install
+++ b/ipa-client/ipa-install/ipa-client-install
@@ -521,8 +521,7 @@ def main():
     cli_server = None
     cli_realm = None
     cli_basedn = None
-
-    subject_base = "O=IPA"
+    subject_base = None
 
     if options.unattended and (options.password is None and options.principal is None and options.prompt_password is False) and not options.on_master:
         print "One of password and principal are required."
@@ -597,6 +596,7 @@ def main():
 
     cli_realm = ds.getRealmName()
     cli_basedn = ds.getBaseDN()
+    subject_base = "O=%s" % ds.getRealmName()
 
     print "Realm: "+cli_realm
     print "DNS Domain: "+cli_domain
diff --git a/ipapython/certdb.py b/ipapython/certdb.py
index a2fbb0ec31fb8c7a2da05e4468de3c49f10d188f..4e838025ce69c17b4e7ef02874161fee26c7b349 100644
--- a/ipapython/certdb.py
+++ b/ipapython/certdb.py
@@ -25,7 +25,9 @@ from ipalib.compat import sha1
 import shutil
 import os
 
-CA_NICKNAME = "IPA CA"
+CA_NICKNAME_FMT = "%s IPA CA"
+def get_ca_nickname(realm, format=CA_NICKNAME_FMT):
+    return format % realm
 
 class CertDB(object):
     """
diff --git a/ipaserver/install/cainstance.py b/ipaserver/install/cainstance.py
index 1998928a373bce68f8a9860e3e567d5aba2ea2fe..5f13b721fd23689c4e19d7764d318cef4142641b 100644
--- a/ipaserver/install/cainstance.py
+++ b/ipaserver/install/cainstance.py
@@ -36,7 +36,7 @@ import urllib
 import xml.dom.minidom
 import stat
 from ipapython import dogtag
-from ipapython.certdb import CA_NICKNAME
+from ipapython.certdb import get_ca_nickname
 from ipalib import pkcs10
 import subprocess
 
@@ -365,8 +365,9 @@ class CAInstance(service.Service):
        2 = have signed cert, continue installation
     """
 
-    def __init__(self):
+    def __init__(self, realm):
         service.Service.__init__(self, "pki-cad")
+        self.realm = realm
         self.pki_user = "pkiuser"
         self.dm_password = None
         self.admin_password = None
@@ -382,7 +383,7 @@ class CAInstance(service.Service):
         # The same database is used for mod_nss because the NSS context
         # will already have been initialized by Apache by the time
         # mod_python wants to do things.
-        self.canickname = CA_NICKNAME
+        self.canickname = get_ca_nickname(realm)
         self.basedn = "o=ipaca"
         self.ca_agent_db = tempfile.mkdtemp(prefix = "tmp-")
         self.ra_agent_db = "/etc/httpd/alias"
@@ -400,7 +401,7 @@ class CAInstance(service.Service):
                            admin_password, ds_port=DEFAULT_DSPORT,
                            pkcs12_info=None, master_host=None, csr_file=None,
                            cert_file=None, cert_chain_file=None,
-                           subject_base="O=IPA"):
+                           subject_base=None):
         """Create a CA instance. This may involve creating the pki-ca instance
            dogtag instance.
 
@@ -420,7 +421,10 @@ class CAInstance(service.Service):
         if self.pkcs12_info is not None:
             self.clone = True
         self.master_host = master_host
-        self.subject_base = subject_base
+        if subject_base is None:
+            self.subject_base = "O=%s" % self.realm
+        else:
+            self.subject_base = subject_base
 
         # Determine if we are installing as an externally-signed CA and
         # what stage we're in.
@@ -1000,5 +1004,5 @@ if __name__ == "__main__":
     installutils.standard_logging_setup("install.log", False)
     cs = CADSInstance()
     cs.create_instance("dirsrv", "EXAMPLE.COM", "catest.example.com", "example.com", "password")
-    ca = CAInstance()
+    ca = CAInstance("EXAMPLE.COM")
     ca.configure_instance("pkiuser", "catest.example.com", "password", "password")
diff --git a/ipaserver/install/certs.py b/ipaserver/install/certs.py
index 4f8b4e708045a9c9ef1685a524e4386b99de9bcf..d4728b80e6831061c5e3c4848e5a3b4c82b009ee 100644
--- a/ipaserver/install/certs.py
+++ b/ipaserver/install/certs.py
@@ -33,7 +33,7 @@ from ipapython import dogtag
 from ipapython import sysrestore
 from ipapython import ipautil
 from ipapython import certmonger
-from ipapython.certdb import CA_NICKNAME
+from ipapython.certdb import get_ca_nickname
 from ipalib import pkcs10
 from ConfigParser import RawConfigParser, MissingSectionHeaderError
 import service
@@ -163,8 +163,9 @@ def next_replica(serial_file=CA_SERIALNO):
     return str(serial)
 
 class CertDB(object):
-    def __init__(self, nssdir, fstore=None, host_name=None, subject_base=None):
+    def __init__(self, nssdir, realm, fstore=None, host_name=None, subject_base=None):
         self.secdir = nssdir
+        self.realm = realm
 
         self.noise_fname = self.secdir + "/noise.txt"
         self.passwd_fname = self.secdir + "/pwdfile.txt"
@@ -191,7 +192,7 @@ class CertDB(object):
         else:
             self.subject_format = "CN=%s,O=IPA"
 
-        self.cacert_name = CA_NICKNAME
+        self.cacert_name = get_ca_nickname(self.realm)
         self.valid_months = "120"
         self.keysize = "1024"
 
@@ -345,10 +346,11 @@ class CertDB(object):
 
     def create_ca_cert(self):
         os.chdir(self.secdir)
+        subject = "cn=%s Certificate Authority" % self.realm
         p = subprocess.Popen(["/usr/bin/certutil",
                               "-d", self.secdir,
                               "-S", "-n", self.cacert_name,
-                              "-s", "cn=IPA Test Certificate Authority",
+                              "-s", subject,
                               "-x",
                               "-t", "CT,,C",
                               "-1",
@@ -853,7 +855,10 @@ class CertDB(object):
             else:
                 raise RuntimeError("unknown error import pkcs#12 file")
 
-    def export_pkcs12(self, pkcs12_fname, pkcs12_pwd_fname, nickname=CA_NICKNAME):
+    def export_pkcs12(self, pkcs12_fname, pkcs12_pwd_fname, nickname=None):
+        if nickname is None:
+            nickname = get_ca_nickname(api.env.realm)
+
         ipautil.run(["/usr/bin/pk12util", "-d", self.secdir,
                      "-o", pkcs12_fname,
                      "-n", nickname,
diff --git a/ipaserver/install/dsinstance.py b/ipaserver/install/dsinstance.py
index 89613bc3165a985d2efb413ae3a5cbf7a1a8a5df..48b6f551e4d46e017472f16d6da95c55a0e69e36 100644
--- a/ipaserver/install/dsinstance.py
+++ b/ipaserver/install/dsinstance.py
@@ -404,7 +404,7 @@ class DsInstance(service.Service):
 
     def __enable_ssl(self):
         dirname = config_dirname(self.serverid)
-        dsdb = certs.CertDB(dirname, subject_base=self.subject_base)
+        dsdb = certs.CertDB(dirname, self.realm_name, 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()
@@ -416,7 +416,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.fqdn, subject_base=self.subject_base)
+            cadb = certs.CertDB(httpinstance.NSS_DIR, self.realm_name, host_name=self.fqdn, subject_base=self.subject_base)
             if self.self_signed_ca:
                 cadb.create_self_signed()
                 dsdb.create_from_cacert(cadb.cacert_fname, passwd=None)
@@ -529,7 +529,7 @@ class DsInstance(service.Service):
             # drop the trailing / off the config_dirname so the directory
             # will match what is in certmonger
             dirname = config_dirname(serverid)[:-1]
-            dsdb = certs.CertDB(dirname)
+            dsdb = certs.CertDB(dirname, self.realm_name)
             dsdb.untrack_server_cert("Server-Cert")
             erase_ds_instance_data(serverid)
 
@@ -571,7 +571,7 @@ class DsInstance(service.Service):
         self.stop()
 
         dirname = config_dirname(realm_to_serverid(self.realm_name))
-        certdb = certs.CertDB(dirname, subject_base=self.subject_base)
+        certdb = certs.CertDB(dirname, self.realm_name, 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 13d7a6601dfb52055ce272684aeebf8e877a2480..f55995b197d4d3968f576f940854a0c1d8a6ad63 100644
--- a/ipaserver/install/httpinstance.py
+++ b/ipaserver/install/httpinstance.py
@@ -30,7 +30,7 @@ import dsinstance
 import installutils
 from ipapython import sysrestore
 from ipapython import ipautil
-from ipalib import util
+from ipalib import util, api
 
 HTTPD_DIR = "/etc/httpd"
 SSL_CONF = HTTPD_DIR + "/conf.d/ssl.conf"
@@ -164,10 +164,10 @@ class HTTPInstance(service.Service):
 
     def __setup_ssl(self):
         if self.self_signed_ca:
-            ca_db = certs.CertDB(NSS_DIR, subject_base=self.subject_base)
+            ca_db = certs.CertDB(NSS_DIR, self.realm, subject_base=self.subject_base)
         else:
-            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)
+            ca_db = certs.CertDB(NSS_DIR, self.realm, host_name=self.fqdn, subject_base=self.subject_base)
+        db = certs.CertDB(NSS_DIR, self.realm, 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()
@@ -223,7 +223,7 @@ class HTTPInstance(service.Service):
         prefs_fd.close()
 
         # The signing cert is generated in __setup_ssl
-        db = certs.CertDB(NSS_DIR, subject_base=self.subject_base)
+        db = certs.CertDB(NSS_DIR, self.realm, subject_base=self.subject_base)
 
         pwdfile = open(db.passwd_fname)
         pwd = pwdfile.read()
@@ -238,7 +238,7 @@ class HTTPInstance(service.Service):
         shutil.rmtree(tmpdir)
 
     def __publish_ca_cert(self):
-        ca_db = certs.CertDB(NSS_DIR)
+        ca_db = certs.CertDB(NSS_DIR, self.realm)
         shutil.copy(ca_db.cacert_fname, "/usr/share/ipa/html/ca.crt")
         os.chmod("/usr/share/ipa/html/ca.crt", 0444)
 
@@ -252,7 +252,7 @@ class HTTPInstance(service.Service):
         if not running is None:
             self.stop()
 
-        db = certs.CertDB(NSS_DIR)
+        db = certs.CertDB(NSS_DIR, api.env.realm)
         db.untrack_server_cert("Server-Cert")
         if not enabled is None and not enabled:
             self.chkconfig_off()
diff --git a/ipaserver/plugins/selfsign.py b/ipaserver/plugins/selfsign.py
index 9943f73d21c031682b2777dcad6adba26e8cea7c..741fb0dc40924f3321068eb7717509947b4e1d3e 100644
--- a/ipaserver/plugins/selfsign.py
+++ b/ipaserver/plugins/selfsign.py
@@ -39,7 +39,7 @@ from ipalib import Backend
 from ipalib import errors
 from ipalib import x509
 from ipalib import pkcs10
-from ipapython.certdb import CA_NICKNAME
+from ipapython.certdb import get_ca_nickname
 import subprocess
 import os
 import re
@@ -47,6 +47,7 @@ from ipaserver.plugins import rabase
 from ipaserver.install import certs
 import tempfile
 from ipalib import  _
+from ipalib import  api
 from ipalib.plugins.cert import get_csr_hostname
 from nss.error import NSPRError
 
@@ -157,7 +158,7 @@ class ra(rabase.rabase):
                 "/usr/bin/certutil",
                 "-C",
                 "-d", self.sec_dir,
-                "-c", CA_NICKNAME,
+                "-c", get_ca_nickname(api.env.realm),
                 "-i", csr_name,
                 "-o", cert_name,
                 "-m", str(serialno),
-- 
1.7.3.2

>From da82dc1aa27f4a9d24cbe81a02242707f9173c55 Mon Sep 17 00:00:00 2001
From: Simo Sorce <sso...@redhat.com>
Date: Fri, 29 Oct 2010 16:23:21 -0400
Subject: [PATCH 02/12] 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 569079d5a60cd3b0af09965c952c05fdaca0a1b4..0584c11181cd18942a01f4050700b30890ce2640 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 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

>From c07f0b1ab3a8d357d905158171ecaae937684220 Mon Sep 17 00:00:00 2001
From: Simo Sorce <sso...@redhat.com>
Date: Wed, 3 Nov 2010 17:53:59 -0400
Subject: [PATCH 03/12] pkinit: always configure pkinit_anchors in krb5.conf

---
 install/share/krb5.conf.template |    1 +
 1 files changed, 1 insertions(+), 0 deletions(-)

diff --git a/install/share/krb5.conf.template b/install/share/krb5.conf.template
index b81cedfeea24423a7176e5efb193d7c5770e4218..ab569714bc7d49370ac65587b63bc23e6bd46ca0 100644
--- a/install/share/krb5.conf.template
+++ b/install/share/krb5.conf.template
@@ -15,6 +15,7 @@
   kdc = $FQDN:88
   admin_server = $FQDN:749
   default_domain = $DOMAIN
+  pkinit_anchors = FILE:/etc/ipa/ca.crt
 }
 
 [domain_realm]
-- 
1.7.3.2

>From e0bf65c411362ae212f2943fa3e3612beafc3a66 Mon Sep 17 00:00:00 2001
From: Simo Sorce <sso...@redhat.com>
Date: Tue, 2 Nov 2010 18:02:59 -0400
Subject: [PATCH 04/12] anon-pkinit: add well known principal

leave it disabled for now
we can change this default once we will have some restriction on what services
this principal can get tickets for.
---
 ipaserver/install/krbinstance.py |   18 ++++++++++++++++++
 1 files changed, 18 insertions(+), 0 deletions(-)

diff --git a/ipaserver/install/krbinstance.py b/ipaserver/install/krbinstance.py
index bfcb869993bc8e7a7060047bbb3fbe4117680f71..f6650d80c841122997ebc760496c8d6967d1abd3 100644
--- a/ipaserver/install/krbinstance.py
+++ b/ipaserver/install/krbinstance.py
@@ -195,6 +195,7 @@ class KrbInstance(service.Service):
         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.step("creating principal for anonymous PKINIT", self.__add_anonymous_pkinit_principal)
 
         self.__common_post_setup()
 
@@ -521,6 +522,23 @@ class KrbInstance(service.Service):
         shutil.copyfile("/usr/share/ipa/html/ca.crt",
                         "/var/kerberos/krb5kdc/cacert.pem")
 
+    def __add_anonymous_pkinit_principal(self):
+        princ = "WELLKNOWN/ANONYMOUS"
+        princ_realm = "%...@%s" % (princ, self.realm)
+
+        # Create the special anonymous principal
+        installutils.kadmin_addprinc(princ_realm)
+        try:
+            conn = ipaldap.IPAdmin("127.0.0.1")
+            conn.simple_bind_s("cn=directory manager", self.admin_password)
+        except Exception, e:
+            logging.critical("Could not connect to the Directory Server on %s" % self.fqdn)
+            raise e
+
+        dn = "krbprincipalname=%s,cn=%s,cn=kerberos,%s" % (princ_realm, self.realm, self.suffix)
+        conn.inactivateEntry(dn, False)
+        conn.unbind()
+
     def uninstall(self):
         if self.is_configured():
             self.print_msg("Unconfiguring %s" % self.service_name)
-- 
1.7.3.2

>From 2a8f477e702ea164db0e1ee4335a0f1b05e4590e Mon Sep 17 00:00:00 2001
From: Simo Sorce <sso...@redhat.com>
Date: Tue, 2 Nov 2010 19:16:09 -0400
Subject: [PATCH 05/12] add plugin to enable/disable anonymous pkinit

---
 ipalib/plugins/pkinit.py |   98 ++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 98 insertions(+), 0 deletions(-)
 create mode 100644 ipalib/plugins/pkinit.py

diff --git a/ipalib/plugins/pkinit.py b/ipalib/plugins/pkinit.py
new file mode 100644
index 0000000000000000000000000000000000000000..f7c189cf89394a140d2f2ca504dd6a9aaa97fd4c
--- /dev/null
+++ b/ipalib/plugins/pkinit.py
@@ -0,0 +1,98 @@
+# Authors:
+#   Simo Sorce <sso...@redhat.com>
+#
+# Copyright (C) 2010  Red Hat
+# see file 'COPYING' for use and warranty information
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; version 2 only
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+"""
+Kerberos pkinit options
+
+Right now it enables only to control whether Anonymous PKINIT is enabled
+or not based on whether the wellknown principal is active or not.
+
+EXAMPLES:
+
+ Enable Anonymous pkinit:
+  ipa pkinit-anonymous enable
+
+ Disable Anonymous pkinit:
+  ipa pkinit-anonymous disable
+"""
+
+from ipalib import api, errors
+from ipalib import Int, Str
+from ipalib import Object, Command
+from ipalib import _
+
+
+class pkinit(Object):
+    """
+    PKINIT Options
+    """
+    object_name = 'pkinit'
+
+    label=_('PKINIT')
+
+api.register(pkinit)
+
+def valid_arg(ugettext, action):
+    """
+    Accepts only Enable/Disable.
+    """
+    a = action.lower()
+    if a != 'enable' and a != 'disable':
+        raise errors.ValidationError(
+            name='action',
+            error='Unknown command %s' % action
+        )
+
+class pkinit_anonymous(Command):
+    """
+    Enable or Disable Anonymous PKINIT
+    """
+    princ_name = 'WELLKNOWN/anonym...@%s' % api.env.realm
+    default_dn = 'krbprincipalname=%s,cn=%s,cn=kerberos,%s' % (
+        princ_name, api.env.realm, api.env.basedn
+    )
+
+    takes_args = (
+        Str('action', valid_arg),
+    )
+
+    def execute(self, action, **options):
+        ldap = self.api.Backend.ldap2
+        set_lock = False
+        lock = None
+
+        (dn, entry_attrs) = ldap.get_entry(self.default_dn, ['nsaccountlock'])
+
+        if 'nsaccountlock' in entry_attrs:
+            lock = entry_attrs['nsaccountlock'][0].lower()
+
+        if action.lower() == 'enable':
+            if lock == 'true':
+                set_lock = True
+                lock = None
+        elif action.lower() == 'disable':
+            if lock != 'true':
+                set_lock = True
+                lock = 'TRUE'
+
+        if set_lock:
+            ldap.update_entry(dn, {'nsaccountlock':lock})
+
+        return dict(result=True)
+
+api.register(pkinit_anonymous)
-- 
1.7.3.2

>From 1045c5c35a86f7b52724a7e2368e3d9fc580c4b9 Mon Sep 17 00:00:00 2001
From: Simo Sorce <sso...@redhat.com>
Date: Wed, 3 Nov 2010 18:17:36 -0400
Subject: [PATCH 06/12] pkinit-replica: create certificates for replicas too

altough the kdc certificate name is not tied to the fqdn we create separate
certs for each KDC so that renewal of each of them is done separately.
---
 install/tools/ipa-replica-install |   27 ++++++++++++--
 install/tools/ipa-replica-prepare |   72 +++++++++++++++++++++++++++++++++---
 ipaserver/install/certs.py        |   12 ++++++
 ipaserver/install/krbinstance.py  |   20 ++++++++---
 4 files changed, 117 insertions(+), 14 deletions(-)

diff --git a/install/tools/ipa-replica-install b/install/tools/ipa-replica-install
index 2beadae812dbe87927855dab42e2a44802e38a02..fb6dd46fbe8d632714ac7ad595ca19e6f6d1817e 100755
--- a/install/tools/ipa-replica-install
+++ b/install/tools/ipa-replica-install
@@ -68,6 +68,8 @@ def parse_options():
     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")
+    parser.add_option("--no-pkinit", dest="setup_pkinit", action="store_false",
+                      default=True, help="disables pkinit setup steps")
 
     options, args = parser.parse_args()
     safe_options = parser.get_safe_opts(options)
@@ -178,13 +180,21 @@ def install_ds(config):
 
     return ds
 
-def install_krb(config):
+def install_krb(config, setup_pkinit=False):
     krb = krbinstance.KrbInstance()
     ldappwd_filename = config.dir + "/ldappwd"
     kpasswd_filename = config.dir + "/kpasswd.keytab"
+
+    #pkinit files
+    pkcs12_info = None
+    if ipautil.file_exists(config.dir + "/pkinitcert.p12"):
+        pkcs12_info = (config.dir + "/pkinitcert.p12",
+                       config.dir + "/pkinit_pin.txt")
+
     krb.create_replica(config.ds_user, config.realm_name, config.host_name,
                        config.domain_name, config.dirman_password,
-                       ldappwd_filename, kpasswd_filename)
+                       ldappwd_filename, kpasswd_filename,
+                       setup_pkinit, pkcs12_info)
 
 def install_ca_cert(config):
     if ipautil.file_exists(config.dir + "/ca.crt"):
@@ -261,6 +271,11 @@ def check_bind():
         print "Aborting installation"
         sys.exit(1)
 
+def check_pkinit():
+    if not krbinstance.check_pkinit_plugin():
+        print "Aborting installation"
+        sys.exit(1)
+
 def main():
     safe_options, options, filename = parse_options()
     installutils.standard_logging_setup("/var/log/ipareplica-install.log", options.debug)
@@ -269,8 +284,14 @@ def main():
     if not ipautil.file_exists(filename):
         sys.exit("Replica file %s does not exist" % filename)
 
+    # check the bind is installed
     if options.setup_dns:
         check_bind()
+
+    # check the pkinit plugin is installed
+    if options.setup_pkinit:
+        check_pkinit()
+
     check_dirsrv()
 
     # get the directory manager password
@@ -367,7 +388,7 @@ def main():
     if ret != 0:
         raise RuntimeError("Failed to start replication")
 
-    install_krb(config)
+    install_krb(config, setup_pkinit=options.setup_pkinit)
     install_http(config)
     if CA:
         CA.import_ra_cert(dir + "/ra.p12")
diff --git a/install/tools/ipa-replica-prepare b/install/tools/ipa-replica-prepare
index 059b011f9f60128ceba171cf5129e8c769e1eaf3..af768015510f47eacfd7643359216a9f49497020 100755
--- a/install/tools/ipa-replica-prepare
+++ b/install/tools/ipa-replica-prepare
@@ -41,25 +41,39 @@ def parse_options():
                       help="install certificate for the directory server")
     parser.add_option("--http_pkcs12", dest="http_pkcs12",
                       help="install certificate for the http server")
+    parser.add_option("--pkinit_pkcs12", dest="pkinit_pkcs12",
+                      help="install certificate for the KDC")
     parser.add_option("--dirsrv_pin", dest="dirsrv_pin",
                       help="PIN for the Directory Server PKCS#12 file")
     parser.add_option("--http_pin", dest="http_pin",
                       help="PIN for the Apache Server PKCS#12 file")
+    parser.add_option("--pkinit_pin", dest="pkinit_pin",
+                      help="PIN for the KDC pkinit PKCS#12 file")
     parser.add_option("-p", "--password", dest="password", 
                       help="Directory Manager (existing master) password")
     parser.add_option("--ip-address", dest="ip_address",
                       help="Add A and PTR records of the future replica")
     parser.add_option("--ca", dest="ca_file", default="/root/cacert.p12",
                       help="Location of CA PKCS#12 file, default /root/cacert.p12")
+    parser.add_option("--no-pkinit", dest="setup_pkinit", action="store_false",
+                      default=True, help="disables pkinit setup steps")
 
     options, args = parser.parse_args()
 
     # If any of the PKCS#12 options are selected, all are required. Create a
     # list of the options and count it to enforce that all are required without
     # having a huge set of it blocks.
-    pkcs12 = [options.dirsrv_pkcs12, options.http_pkcs12, options.dirsrv_pin, options.http_pin]
+    if options.setup_pkinit:
+        pkcs12 = [options.dirsrv_pkcs12, options.dirsrv_pin,
+                  options.http_pkcs12, options.http_pin,
+                  options.pkinit_pkcs12, options.pkinit_pin]
+        num = 6
+    else:
+        pkcs12 = [options.dirsrv_pkcs12, options.dirsrv_pin,
+                  options.http_pkcs12, options.http_pin]
+        num = 4
     cnt = pkcs12.count(None)
-    if cnt > 0 and cnt < 4:
+    if cnt > 0 and cnt < num:
         parser.error("All PKCS#12 options are required if any are used.")
 
     if options.ip_address:
@@ -90,7 +104,7 @@ def check_ipa_configuration(realm_name):
         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, subject_base=None):
+def export_certdb(realm_name, ds_dir, dir, passwd_fname, fname, hostname, subject_base=None, is_kdc=False):
     """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.
@@ -100,6 +114,12 @@ def export_certdb(realm_name, ds_dir, dir, passwd_fname, fname, hostname, subjec
 
        The subject is handled by certs.CertDB:create_server_cert()
     """
+
+    if is_kdc:
+        nickname = "KDC-Cert"
+    else:
+        nickname = "Server-Cert"
+
     try:
         self_signed = certs.ipa_self_signed()
 
@@ -111,15 +131,22 @@ def export_certdb(realm_name, ds_dir, dir, passwd_fname, fname, hostname, subjec
 #        else:
 #            ca_db = certs.CertDB(httpinstance.NSS_DIR, host_name=api.env.host)
         ca_db = certs.CertDB(httpinstance.NSS_DIR, realm_name, host_name=api.env.host, subject_base=subject_base)
-        db.create_from_cacert(ca_db.cacert_fname)
-        db.create_server_cert("Server-Cert", hostname, ca_db)
+        if is_kdc:
+            ca_db.create_kdc_cert("KDC-Cert", hostname, dir)
+        else:
+            db.create_from_cacert(ca_db.cacert_fname)
+            db.create_server_cert(nickname, hostname, ca_db)
     except Exception, e:
         raise e
 
     pkcs12_fname = dir + "/" + fname + ".p12"
 
     try:
-        db.export_pkcs12(pkcs12_fname, passwd_fname, "Server-Cert")
+        if is_kdc:
+            ca_db.export_pem_p12(pkcs12_fname, passwd_fname,
+                                 nickname, dir + "/kdc.pem")
+        else:
+            db.export_pkcs12(pkcs12_fname, passwd_fname, nickname)
     except ipautil.CalledProcessError, e:
         print "error exporting Server certificate: " + str(e)
         remove_file(pkcs12_fname)
@@ -129,6 +156,8 @@ def export_certdb(realm_name, ds_dir, dir, passwd_fname, fname, hostname, subjec
     remove_file(dir + "/key3.db")
     remove_file(dir + "/secmod.db")
     remove_file(dir + "/noise.txt")
+    if is_kdc:
+        remove_file(dir + "/kdc.pem")
     if ipautil.file_exists(passwd_fname + ".orig"):
         remove_file(passwd_fname + ".orig")
 
@@ -194,6 +223,8 @@ def copy_files(realm_name, dir):
         if ipautil.file_exists("/usr/share/ipa/html/preferences.html"):
             shutil.copy("/usr/share/ipa/html/preferences.html", dir + "/preferences.html")
             shutil.copy("/usr/share/ipa/html/configure.jar", dir + "/configure.jar")
+        if ipautil.file_exists("/var/kerberos/krb5kdc/cacert.pem"):
+            shutil.copy("/var/kerberos/krb5kdc/cacert.pem", dir + "/cacert.pem")
     except Exception, e:
         print "error copying files: " + str(e)
         sys.exit(1)
@@ -316,6 +347,35 @@ def main():
         print "Exporting RA certificate"
         export_ra_pkcs12(dir, dirman_password)
 
+    if options.setup_pkinit:
+        if options.pkinit_pin:
+            passwd = options.pkinit_pin
+        else:
+            passwd = ""
+
+        passwd_fname = dir + "/pkinit_pin.txt"
+        fd = open(passwd_fname, "w")
+        fd.write("%s\n" % passwd)
+        fd.close()
+
+        if options.pkinit_pkcs12:
+            print "Copying SSL certificate for the KDC from %s" % options.pkinit_pkcs12
+            try:
+                shutil.copy(options.pkinit_pkcs12, dir + "/pkinitcert.p12")
+            except IOError, e:
+                print "Copy failed %s" %  e
+                sys.exit(1)
+        else:
+            print "Creating SSL certificate for the KDC"
+            try:
+                export_certdb(api.env.realm, ds_dir, dir,
+                              passwd_fname, "pkinitcert",
+                              replica_fqdn, subject_base,
+                              is_kdc=True)
+            except errors.CertificateOperationError, e:
+                print "%s" % e
+                sys.exit(1)
+
     print "Copying additional files"
     copy_files(api.env.realm, dir)
     print "Finalizing configuration"
diff --git a/ipaserver/install/certs.py b/ipaserver/install/certs.py
index 3fa65207cbd1cbb206ea7f8294401664759bacfa..bd5c7bf9c8a85ce18aa353470b4452785ca627a7 100644
--- a/ipaserver/install/certs.py
+++ b/ipaserver/install/certs.py
@@ -865,6 +865,13 @@ class CertDB(object):
                      "-k", self.passwd_fname,
                      "-w", pkcs12_pwd_fname])
 
+    def export_pem_p12(self, pkcs12_fname, pkcs12_pwd_fname,
+                       nickname, pem_fname):
+        ipautil.run(["/usr/bin/openssl", "pkcs12",
+                     "-export", "-name", nickname,
+                     "-in", pem_fname, "-out", pkcs12_fname,
+                     "-passout", "file:" + pkcs12_pwd_fname])
+
     def create_self_signed(self, passwd=None):
         self.create_noise_file()
         self.create_passwd_file(passwd)
@@ -1017,6 +1024,11 @@ class CertDB(object):
         os.unlink(key_fname)
         os.unlink(cert_fname)
 
+    def install_pem_from_p12(self, p12_fname, p12_pwd_fname, pem_fname):
+        ipautil.run(["/usr/bin/openssl", "pkcs12", "-nodes",
+                     "-in", p12_fname, "-out", pem_fname,
+                     "-passin", "file:" + p12_pwd_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 f6650d80c841122997ebc760496c8d6967d1abd3..7454739e16c30972fb0cb90ed0673d6f7def43d6 100644
--- a/ipaserver/install/krbinstance.py
+++ b/ipaserver/install/krbinstance.py
@@ -205,7 +205,14 @@ class KrbInstance(service.Service):
 
         self.kpasswd.create_instance()
 
-    def create_replica(self, ds_user, realm_name, host_name, domain_name, admin_password, ldap_passwd_filename, kpasswd_filename):
+    def create_replica(self, ds_user, realm_name, host_name,
+                       domain_name, admin_password,
+                       ldap_passwd_filename, kpasswd_filename,
+                       setup_pkinit=False, pkcs12_info=None,
+                       self_signed_ca=False, subject_base=None):
+        self.pkcs12_info = pkcs12_info
+        self.self_signed_ca = self_signed_ca
+        self.subject_base = subject_base
         self.__copy_ldap_passwd(ldap_passwd_filename)
         self.__copy_kpasswd_keytab(kpasswd_filename)
 
@@ -217,6 +224,8 @@ class KrbInstance(service.Service):
         self.step("creating a keytab for the directory", self.__create_ds_keytab)
         self.step("creating a keytab for the machine", self.__create_host_keytab)
         self.step("adding the password extension to the directory", self.__add_pwd_extop_module)
+        if setup_pkinit:
+            self.step("installing X509 Certificate for PKINIT", self.__setup_pkinit)
 
         self.__common_post_setup()
 
@@ -506,16 +515,17 @@ class KrbInstance(service.Service):
             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")
-
+            ca_db.install_pem_from_p12(self.pkcs12_info[0],
+                                       self.pkcs12_info[1],
+                                       "/var/kerberos/krb5kdc/kdc.pem")
         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")
+                raise RuntimeError("PKI not supported yet\n")
 
         # Finally copy the cacert in the krb directory so we don't
         # have any selinux issues with the file context
-- 
1.7.3.2

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

Reply via email to