|
|
ac7d03 |
From 5301e86fccfde6ab444a2c600a412487318fbd13 Mon Sep 17 00:00:00 2001
|
|
|
ac7d03 |
From: Jan Cholasta <jcholast@redhat.com>
|
|
|
ac7d03 |
Date: Wed, 3 May 2017 06:14:27 +0000
|
|
|
ac7d03 |
Subject: [PATCH] server install: fix KDC certificate validation in CA-less
|
|
|
ac7d03 |
|
|
|
ac7d03 |
Verify that the provided certificate has the extended key usage and subject
|
|
|
ac7d03 |
alternative name required for KDC.
|
|
|
ac7d03 |
|
|
|
ac7d03 |
https://pagure.io/freeipa/issue/6831
|
|
|
ac7d03 |
https://pagure.io/freeipa/issue/6869
|
|
|
ac7d03 |
|
|
|
ac7d03 |
Reviewed-By: Stanislav Laznicka <slaznick@redhat.com>
|
|
|
ac7d03 |
Reviewed-By: Martin Babinsky <mbabinsk@redhat.com>
|
|
|
ac7d03 |
---
|
|
|
ac7d03 |
ipapython/certdb.py | 42 ++++++++++++++++++++++++++++++
|
|
|
ac7d03 |
ipaserver/install/installutils.py | 24 +++++++++++------
|
|
|
ac7d03 |
ipaserver/install/server/install.py | 11 ++++++--
|
|
|
ac7d03 |
ipaserver/install/server/replicainstall.py | 11 ++++++--
|
|
|
ac7d03 |
4 files changed, 76 insertions(+), 12 deletions(-)
|
|
|
ac7d03 |
|
|
|
ac7d03 |
diff --git a/ipapython/certdb.py b/ipapython/certdb.py
|
|
|
ac7d03 |
index 1ee2603653452577476cf413e6af951cd29c273e..114c58340253141706afa461ecaf87797562ca1d 100644
|
|
|
ac7d03 |
--- a/ipapython/certdb.py
|
|
|
ac7d03 |
+++ b/ipapython/certdb.py
|
|
|
ac7d03 |
@@ -24,14 +24,17 @@ import pwd
|
|
|
ac7d03 |
import grp
|
|
|
ac7d03 |
import re
|
|
|
ac7d03 |
import tempfile
|
|
|
ac7d03 |
+from tempfile import NamedTemporaryFile
|
|
|
ac7d03 |
import shutil
|
|
|
ac7d03 |
import base64
|
|
|
ac7d03 |
from cryptography.hazmat.primitives import serialization
|
|
|
ac7d03 |
+import cryptography.x509
|
|
|
ac7d03 |
from nss import nss
|
|
|
ac7d03 |
from nss.error import NSPRError
|
|
|
ac7d03 |
|
|
|
ac7d03 |
from ipapython.dn import DN
|
|
|
ac7d03 |
from ipapython.ipa_log_manager import root_logger
|
|
|
ac7d03 |
+from ipapython.kerberos import Principal
|
|
|
ac7d03 |
from ipapython import ipautil
|
|
|
ac7d03 |
from ipalib import x509 # pylint: disable=ipa-forbidden-import
|
|
|
ac7d03 |
|
|
|
ac7d03 |
@@ -182,6 +185,38 @@ def unparse_trust_flags(trust_flags):
|
|
|
ac7d03 |
return trust_flags
|
|
|
ac7d03 |
|
|
|
ac7d03 |
|
|
|
ac7d03 |
+def verify_kdc_cert_validity(kdc_cert, ca_certs, realm):
|
|
|
ac7d03 |
+ pem_kdc_cert = kdc_cert.public_bytes(serialization.Encoding.PEM)
|
|
|
ac7d03 |
+ pem_ca_certs = '\n'.join(
|
|
|
ac7d03 |
+ cert.public_bytes(serialization.Encoding.PEM) for cert in ca_certs)
|
|
|
ac7d03 |
+
|
|
|
ac7d03 |
+ with NamedTemporaryFile() as kdc_file, NamedTemporaryFile() as ca_file:
|
|
|
ac7d03 |
+ kdc_file.write(pem_kdc_cert)
|
|
|
ac7d03 |
+ kdc_file.flush()
|
|
|
ac7d03 |
+ ca_file.write(pem_ca_certs)
|
|
|
ac7d03 |
+ ca_file.flush()
|
|
|
ac7d03 |
+
|
|
|
ac7d03 |
+ try:
|
|
|
ac7d03 |
+ ipautil.run(
|
|
|
ac7d03 |
+ [OPENSSL, 'verify', '-CAfile', ca_file.name, kdc_file.name])
|
|
|
ac7d03 |
+ eku = kdc_cert.extensions.get_extension_for_class(
|
|
|
ac7d03 |
+ cryptography.x509.ExtendedKeyUsage)
|
|
|
ac7d03 |
+ list(eku.value).index(
|
|
|
ac7d03 |
+ cryptography.x509.ObjectIdentifier(x509.EKU_PKINIT_KDC))
|
|
|
ac7d03 |
+ except (ipautil.CalledProcessError,
|
|
|
ac7d03 |
+ cryptography.x509.ExtensionNotFound,
|
|
|
ac7d03 |
+ ValueError):
|
|
|
ac7d03 |
+ raise ValueError("invalid for a KDC")
|
|
|
ac7d03 |
+
|
|
|
ac7d03 |
+ principal = str(Principal(['krbtgt', realm], realm))
|
|
|
ac7d03 |
+ gns = x509.process_othernames(x509.get_san_general_names(kdc_cert))
|
|
|
ac7d03 |
+ for gn in gns:
|
|
|
ac7d03 |
+ if isinstance(gn, x509.KRB5PrincipalName) and gn.name == principal:
|
|
|
ac7d03 |
+ break
|
|
|
ac7d03 |
+ else:
|
|
|
ac7d03 |
+ raise ValueError("invalid for realm %s" % realm)
|
|
|
ac7d03 |
+
|
|
|
ac7d03 |
+
|
|
|
ac7d03 |
class NSSDatabase(object):
|
|
|
ac7d03 |
"""A general-purpose wrapper around a NSS cert database
|
|
|
ac7d03 |
|
|
|
ac7d03 |
@@ -707,3 +742,10 @@ class NSSDatabase(object):
|
|
|
ac7d03 |
finally:
|
|
|
ac7d03 |
del certdb, cert
|
|
|
ac7d03 |
nss.nss_shutdown()
|
|
|
ac7d03 |
+
|
|
|
ac7d03 |
+ def verify_kdc_cert_validity(self, nickname, realm):
|
|
|
ac7d03 |
+ nicknames = self.get_trust_chain(nickname)
|
|
|
ac7d03 |
+ certs = [self.get_cert(nickname) for nickname in nicknames]
|
|
|
ac7d03 |
+ certs = [x509.load_certificate(cert, x509.DER) for cert in certs]
|
|
|
ac7d03 |
+
|
|
|
ac7d03 |
+ verify_kdc_cert_validity(certs[-1], certs[:-1], realm)
|
|
|
ac7d03 |
diff --git a/ipaserver/install/installutils.py b/ipaserver/install/installutils.py
|
|
|
ac7d03 |
index 5bce9894780bd920db11196b925492a7fe8f22d0..d2283af20485fd5d66bfd3cc49059d08d1802575 100644
|
|
|
ac7d03 |
--- a/ipaserver/install/installutils.py
|
|
|
ac7d03 |
+++ b/ipaserver/install/installutils.py
|
|
|
ac7d03 |
@@ -1001,7 +1001,7 @@ def handle_error(error, log_file_name=None):
|
|
|
ac7d03 |
|
|
|
ac7d03 |
|
|
|
ac7d03 |
def load_pkcs12(cert_files, key_password, key_nickname, ca_cert_files,
|
|
|
ac7d03 |
- host_name):
|
|
|
ac7d03 |
+ host_name=None, realm_name=None):
|
|
|
ac7d03 |
"""
|
|
|
ac7d03 |
Load and verify server certificate and private key from multiple files
|
|
|
ac7d03 |
|
|
|
ac7d03 |
@@ -1066,13 +1066,21 @@ def load_pkcs12(cert_files, key_password, key_nickname, ca_cert_files,
|
|
|
ac7d03 |
"CA certificate %s in %s is not valid: %s" %
|
|
|
ac7d03 |
(subject, ", ".join(cert_files), e))
|
|
|
ac7d03 |
|
|
|
ac7d03 |
- # Check server validity
|
|
|
ac7d03 |
- try:
|
|
|
ac7d03 |
- nssdb.verify_server_cert_validity(key_nickname, host_name)
|
|
|
ac7d03 |
- except ValueError as e:
|
|
|
ac7d03 |
- raise ScriptError(
|
|
|
ac7d03 |
- "The server certificate in %s is not valid: %s" %
|
|
|
ac7d03 |
- (", ".join(cert_files), e))
|
|
|
ac7d03 |
+ if host_name is not None:
|
|
|
ac7d03 |
+ try:
|
|
|
ac7d03 |
+ nssdb.verify_server_cert_validity(key_nickname, host_name)
|
|
|
ac7d03 |
+ except ValueError as e:
|
|
|
ac7d03 |
+ raise ScriptError(
|
|
|
ac7d03 |
+ "The server certificate in %s is not valid: %s" %
|
|
|
ac7d03 |
+ (", ".join(cert_files), e))
|
|
|
ac7d03 |
+
|
|
|
ac7d03 |
+ if realm_name is not None:
|
|
|
ac7d03 |
+ try:
|
|
|
ac7d03 |
+ nssdb.verify_kdc_cert_validity(key_nickname, realm_name)
|
|
|
ac7d03 |
+ except ValueError as e:
|
|
|
ac7d03 |
+ raise ScriptError(
|
|
|
ac7d03 |
+ "The KDC certificate in %s is not valid: %s" %
|
|
|
ac7d03 |
+ (", ".join(cert_files), e))
|
|
|
ac7d03 |
|
|
|
ac7d03 |
out_file = tempfile.NamedTemporaryFile()
|
|
|
ac7d03 |
out_password = ipautil.ipa_generate_password()
|
|
|
ac7d03 |
diff --git a/ipaserver/install/server/install.py b/ipaserver/install/server/install.py
|
|
|
ac7d03 |
index c1bdce6c8459dfeabd0096d105e535ec4ee56a2a..03380b8d0e9150224b014a1a174d7ea81ccdcf00 100644
|
|
|
ac7d03 |
--- a/ipaserver/install/server/install.py
|
|
|
ac7d03 |
+++ b/ipaserver/install/server/install.py
|
|
|
ac7d03 |
@@ -520,12 +520,12 @@ def install_check(installer):
|
|
|
ac7d03 |
if options.pkinit_pin is None:
|
|
|
ac7d03 |
raise ScriptError(
|
|
|
ac7d03 |
"Kerberos KDC private key unlock password required")
|
|
|
ac7d03 |
- pkinit_pkcs12_file, pkinit_pin, _pkinit_ca_cert = load_pkcs12(
|
|
|
ac7d03 |
+ pkinit_pkcs12_file, pkinit_pin, pkinit_ca_cert = load_pkcs12(
|
|
|
ac7d03 |
cert_files=options.pkinit_cert_files,
|
|
|
ac7d03 |
key_password=options.pkinit_pin,
|
|
|
ac7d03 |
key_nickname=options.pkinit_cert_name,
|
|
|
ac7d03 |
ca_cert_files=options.ca_cert_files,
|
|
|
ac7d03 |
- host_name=host_name)
|
|
|
ac7d03 |
+ realm_name=realm_name)
|
|
|
ac7d03 |
pkinit_pkcs12_info = (pkinit_pkcs12_file.name, pkinit_pin)
|
|
|
ac7d03 |
|
|
|
ac7d03 |
if (options.http_cert_files and options.dirsrv_cert_files and
|
|
|
ac7d03 |
@@ -534,6 +534,13 @@ def install_check(installer):
|
|
|
ac7d03 |
"Apache Server SSL certificate and Directory Server SSL "
|
|
|
ac7d03 |
"certificate are not signed by the same CA certificate")
|
|
|
ac7d03 |
|
|
|
ac7d03 |
+ if (options.http_cert_files and
|
|
|
ac7d03 |
+ options.pkinit_cert_files and
|
|
|
ac7d03 |
+ http_ca_cert != pkinit_ca_cert):
|
|
|
ac7d03 |
+ raise ScriptError(
|
|
|
ac7d03 |
+ "Apache Server SSL certificate and PKINIT KDC "
|
|
|
ac7d03 |
+ "certificate are not signed by the same CA certificate")
|
|
|
ac7d03 |
+
|
|
|
ac7d03 |
if not options.dm_password:
|
|
|
ac7d03 |
dm_password = read_dm_password()
|
|
|
ac7d03 |
|
|
|
ac7d03 |
diff --git a/ipaserver/install/server/replicainstall.py b/ipaserver/install/server/replicainstall.py
|
|
|
ac7d03 |
index 66d7ba44645aed69b12f0e5ea14f5080492fe5ef..6f71f0b51812943fea3fb1c576a0174c739a070b 100644
|
|
|
ac7d03 |
--- a/ipaserver/install/server/replicainstall.py
|
|
|
ac7d03 |
+++ b/ipaserver/install/server/replicainstall.py
|
|
|
ac7d03 |
@@ -1069,12 +1069,12 @@ def promote_check(installer):
|
|
|
ac7d03 |
if options.pkinit_pin is None:
|
|
|
ac7d03 |
raise ScriptError(
|
|
|
ac7d03 |
"Kerberos KDC private key unlock password required")
|
|
|
ac7d03 |
- pkinit_pkcs12_file, pkinit_pin, _pkinit_ca_cert = load_pkcs12(
|
|
|
ac7d03 |
+ pkinit_pkcs12_file, pkinit_pin, pkinit_ca_cert = load_pkcs12(
|
|
|
ac7d03 |
cert_files=options.pkinit_cert_files,
|
|
|
ac7d03 |
key_password=options.pkinit_pin,
|
|
|
ac7d03 |
key_nickname=options.pkinit_cert_name,
|
|
|
ac7d03 |
ca_cert_files=options.ca_cert_files,
|
|
|
ac7d03 |
- host_name=config.host_name)
|
|
|
ac7d03 |
+ realm_name=config.realm_name)
|
|
|
ac7d03 |
pkinit_pkcs12_info = (pkinit_pkcs12_file.name, pkinit_pin)
|
|
|
ac7d03 |
|
|
|
ac7d03 |
if (options.http_cert_files and options.dirsrv_cert_files and
|
|
|
ac7d03 |
@@ -1083,6 +1083,13 @@ def promote_check(installer):
|
|
|
ac7d03 |
"Server SSL certificate are not signed by the same"
|
|
|
ac7d03 |
" CA certificate")
|
|
|
ac7d03 |
|
|
|
ac7d03 |
+ if (options.http_cert_files and
|
|
|
ac7d03 |
+ options.pkinit_cert_files and
|
|
|
ac7d03 |
+ http_ca_cert != pkinit_ca_cert):
|
|
|
ac7d03 |
+ raise RuntimeError("Apache Server SSL certificate and PKINIT KDC "
|
|
|
ac7d03 |
+ "certificate are not signed by the same CA "
|
|
|
ac7d03 |
+ "certificate")
|
|
|
ac7d03 |
+
|
|
|
ac7d03 |
installutils.verify_fqdn(config.host_name, options.no_host_dns)
|
|
|
ac7d03 |
installutils.verify_fqdn(config.master_host_name, options.no_host_dns)
|
|
|
ac7d03 |
|
|
|
ac7d03 |
--
|
|
|
ac7d03 |
2.9.4
|
|
|
ac7d03 |
|