This set of patches implement the first part of ticket #55
The patchset only adds the ability to install pkinit with the
selfsigned CA (or with externally provided certs).

If you need to use/test dogtag you can pass the --no-pkinit option for
the time being as etting up pkinit is performed by default.

Patch 0003: change the install tools to use a subject base based on the
realm name and not O=IPA for all installs.

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.

Patch 0005: Always set pkinit_anchors so that all clients are
preconfigured to do anonymous pkinit including master. Even if the
client does not support pkinit adding the option does not cause any
harm so it is a safe default.

Patch 0006: Add the wellknown principal need to perform anonymous
pkinit AS requests. This principal is *DISABLED* by default.

Patch 0007: Ad a new plugin that allows to enable the wellknown
account, effectively allowing to get pkinit anonymous tickets.
(as a bonus implements disable too :)

Patch 0008: Add support for configuring pkinit certs on replicas too.


What is still missing is dogtag integration and certmonger tracking.
Couldn't work on the dogtag part yet because it won't work on f14 which
is the only fedora version that has a kerberos version recent enough to
support asking for anonynoums pkinit tickets.
Certmonger will need some thinking too as the KDC ticket requires a
different code path to be renewd (different commands in selfsign CA and
different profile with dogtag).

Simo.

-- 
Simo Sorce * Red Hat, Inc * New York
>From 9787b05a29eb7f0c80b60bf47d8d5acf213ceb8b Mon Sep 17 00:00:00 2001
From: Simo Sorce <sso...@redhat.com>
Date: Mon, 1 Nov 2010 13:51:14 -0400
Subject: [PATCH 1/6] 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 e54101202553395fab2515a4c67e1e5f0424b0ab..8cf8d84597588f86110958bc95ff4ad9015709da 100755
--- a/install/tools/ipa-replica-install
+++ b/install/tools/ipa-replica-install
@@ -47,7 +47,7 @@ class ReplicaConfig:
         self.host_name = ""
         self.repl_password = ""
         self.dir = ""
-        self.subject_base = "O=IPA"
+        self.subject_base = ""
 
 def parse_options():
     from optparse import OptionParser
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 ebf71666e63c48f18a61e26f336de68b9b2325bb..791437e4093afddf2cab139f8061381216b5a588 100755
--- a/install/tools/ipa-server-install
+++ b/install/tools/ipa-server-install
@@ -120,8 +120,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")
@@ -392,8 +392,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()
@@ -453,7 +453,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(
@@ -598,6 +597,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:
@@ -677,7 +679,7 @@ def main():
         if options.external_cert_file is None:
             cs = cainstance.CADSInstance()
             cs.create_instance(ds_user, 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 b1e001cd3560e9edfcd140197bd2f652757652de..f2be9226a117a0bfb44f7f7ced9c52f976f61822 100755
--- a/ipa-client/ipa-install/ipa-client-install
+++ b/ipa-client/ipa-install/ipa-client-install
@@ -519,8 +519,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."
@@ -595,6 +594,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 20f655acb3eabb0d443a055f1ba15e239d69d17a..57755662516c0034dfee71d0c136f11182e37a66 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)
@@ -527,7 +527,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)
 
@@ -569,7 +569,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 b4a9d3acd98ffb3f5eae41f84081d5192d226524 Mon Sep 17 00:00:00 2001
From: Simo Sorce <sso...@redhat.com>
Date: Fri, 29 Oct 2010 16:23:21 -0400
Subject: [PATCH 2/6] 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 06f81cb6461edaf03eaedf5d6e131fa940e5bef9..6d27a8af4beee8cea9acf7cdaac3e85131d3f7a9 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 791437e4093afddf2cab139f8061381216b5a588..7c58351ff5de4d814b94b0c034ba32a642fd66d1 100755
--- a/install/tools/ipa-server-install
+++ b/install/tools/ipa-server-install
@@ -104,14 +104,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",
                       help="The password of the Directory Server PKCS#12 file")
     parser.add_option("--http_pin", dest="http_pin",
                       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")
@@ -491,6 +497,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:"
@@ -517,6 +525,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
@@ -710,9 +724,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 b5d6598782fcd23801b494805f91c2c7c63a67d7 Mon Sep 17 00:00:00 2001
From: Simo Sorce <sso...@redhat.com>
Date: Wed, 3 Nov 2010 17:53:59 -0400
Subject: [PATCH 3/6] 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 95487396c7fd9b758576d5eae92e12f70109e675 Mon Sep 17 00:00:00 2001
From: Simo Sorce <sso...@redhat.com>
Date: Tue, 2 Nov 2010 18:02:59 -0400
Subject: [PATCH 4/6] 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 7f6f546f8eea40237017ddd11a770a76b0ff4375 Mon Sep 17 00:00:00 2001
From: Simo Sorce <sso...@redhat.com>
Date: Tue, 2 Nov 2010 19:16:09 -0400
Subject: [PATCH 5/6] 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 4b04fed69371b69492151f705548ff5878cb9adc Mon Sep 17 00:00:00 2001
From: Simo Sorce <sso...@redhat.com>
Date: Wed, 3 Nov 2010 18:17:36 -0400
Subject: [PATCH 6/6] 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 8cf8d84597588f86110958bc95ff4ad9015709da..0433f2d6a230d5b3b1833ccee6b7e54e88eb7339 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()
 
@@ -177,13 +179,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"):
@@ -260,6 +270,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():
     options, filename = parse_options()
     installutils.standard_logging_setup("/var/log/ipareplica-install.log", options.debug)
@@ -267,8 +282,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
@@ -365,7 +386,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