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