0201d8
From 2805b17bb7f204c25ed27bea1ee4fb999ae3d9d7 Mon Sep 17 00:00:00 2001
0201d8
From: Jan Cholasta <jcholast@redhat.com>
0201d8
Date: Tue, 17 Mar 2015 09:28:47 +0000
0201d8
Subject: [PATCH] certstore: Make certificate retrieval more robust
0201d8
0201d8
https://fedorahosted.org/freeipa/ticket/4565
0201d8
0201d8
Reviewed-By: David Kupka <dkupka@redhat.com>
0201d8
---
0201d8
 ipalib/certstore.py | 74 +++++++++++++++++++++++++++++++++++++----------------
0201d8
 1 file changed, 52 insertions(+), 22 deletions(-)
0201d8
0201d8
diff --git a/ipalib/certstore.py b/ipalib/certstore.py
0201d8
index 8a9b41015127fcb1b2b5e01da10f0defcee18265..3a5555c95251fdf3384c7dd21cb19d2125a469f5 100644
0201d8
--- a/ipalib/certstore.py
0201d8
+++ b/ipalib/certstore.py
0201d8
@@ -239,6 +239,31 @@ def put_ca_cert(ldap, base_dn, dercert, nickname, trusted=None,
0201d8
         pass
0201d8
 
0201d8
 
0201d8
+def make_compat_ca_certs(certs, realm, ipa_ca_subject):
0201d8
+    """
0201d8
+    Make CA certificates and associated key policy from DER certificates.
0201d8
+    """
0201d8
+    result = []
0201d8
+
0201d8
+    for cert in certs:
0201d8
+        subject, issuer_serial, public_key_info = _parse_cert(cert)
0201d8
+        subject = DN(subject)
0201d8
+
0201d8
+        if ipa_ca_subject is not None and subject == DN(ipa_ca_subject):
0201d8
+            nickname = get_ca_nickname(realm)
0201d8
+            ext_key_usage = {x509.EKU_SERVER_AUTH,
0201d8
+                             x509.EKU_CLIENT_AUTH,
0201d8
+                             x509.EKU_EMAIL_PROTECTION,
0201d8
+                             x509.EKU_CODE_SIGNING}
0201d8
+        else:
0201d8
+            nickname = str(subject)
0201d8
+            ext_key_usage = {x509.EKU_SERVER_AUTH}
0201d8
+
0201d8
+        result.append((cert, nickname, True, ext_key_usage))
0201d8
+
0201d8
+    return result
0201d8
+
0201d8
+
0201d8
 def get_ca_certs(ldap, base_dn, compat_realm, compat_ipa_ca,
0201d8
                  filter_subject=None):
0201d8
     """
0201d8
@@ -250,6 +275,7 @@ def get_ca_certs(ldap, base_dn, compat_realm, compat_ipa_ca,
0201d8
         filter_subject = [str(subj).replace('\\;', '\\3b')
0201d8
                           for subj in filter_subject]
0201d8
 
0201d8
+    certs = []
0201d8
     config_dn = DN(('cn', 'ipa'), ('cn', 'etc'), base_dn)
0201d8
     container_dn = DN(('cn', 'certificates'), config_dn)
0201d8
     try:
0201d8
@@ -265,7 +291,6 @@ def get_ca_certs(ldap, base_dn, compat_realm, compat_ipa_ca,
0201d8
                         'ipaPublicKey', 'ipaKeyTrust', 'ipaKeyExtUsage',
0201d8
                         'cACertificate;binary'])
0201d8
 
0201d8
-        certs = []
0201d8
         for entry in result:
0201d8
             nickname = entry.single_value['cn']
0201d8
             trusted = entry.single_value.get('ipaKeyTrust', 'unknown').lower()
0201d8
@@ -281,34 +306,39 @@ def get_ca_certs(ldap, base_dn, compat_realm, compat_ipa_ca,
0201d8
                 ext_key_usage.discard(x509.EKU_PLACEHOLDER)
0201d8
 
0201d8
             for cert in entry.get('cACertificate;binary', []):
0201d8
+                try:
0201d8
+                    _parse_cert(cert)
0201d8
+                except ValueError:
0201d8
+                    certs = []
0201d8
+                    break
0201d8
                 certs.append((cert, nickname, trusted, ext_key_usage))
0201d8
-
0201d8
-        return certs
0201d8
     except errors.NotFound:
0201d8
         try:
0201d8
             ldap.get_entry(container_dn, [''])
0201d8
         except errors.NotFound:
0201d8
-            pass
0201d8
-        else:
0201d8
-            return []
0201d8
-
0201d8
-        # Fallback to cn=CAcert,cn=ipa,cn=etc,SUFFIX
0201d8
-        dn = DN(('cn', 'CAcert'), config_dn)
0201d8
-        entry = ldap.get_entry(dn, ['cACertificate;binary'])
0201d8
-
0201d8
-        cert = entry.single_value['cACertificate;binary']
0201d8
-        subject, issuer_serial, public_key_info = _parse_cert(cert)
0201d8
-        if filter_subject is not None and subject not in filter_subject:
0201d8
-            return []
0201d8
+            # Fallback to cn=CAcert,cn=ipa,cn=etc,SUFFIX
0201d8
+            dn = DN(('cn', 'CAcert'), config_dn)
0201d8
+            entry = ldap.get_entry(dn, ['cACertificate;binary'])
0201d8
+
0201d8
+            cert = entry.single_value['cACertificate;binary']
0201d8
+            try:
0201d8
+                subject, issuer_serial, public_key_info = _parse_cert(cert)
0201d8
+            except ValueError:
0201d8
+                pass
0201d8
+            else:
0201d8
+                if filter_subject is not None and subject not in filter_subject:
0201d8
+                    raise errors.NotFound(reason="no matching entry found")
0201d8
 
0201d8
-        nickname = get_ca_nickname(compat_realm)
0201d8
-        ext_key_usage = {x509.EKU_SERVER_AUTH}
0201d8
-        if compat_ipa_ca:
0201d8
-            ext_key_usage |= {x509.EKU_CLIENT_AUTH,
0201d8
-                              x509.EKU_EMAIL_PROTECTION,
0201d8
-                              x509.EKU_CODE_SIGNING}
0201d8
+                if compat_ipa_ca:
0201d8
+                    ca_subject = subject
0201d8
+                else:
0201d8
+                    ca_subject = None
0201d8
+                certs = make_compat_ca_certs([cert], compat_realm, ca_subject)
0201d8
 
0201d8
-        return [(cert, nickname, True, ext_key_usage)]
0201d8
+    if certs:
0201d8
+        return certs
0201d8
+    else:
0201d8
+        raise errors.NotFound(reason="no such entry")
0201d8
 
0201d8
 
0201d8
 def trust_flags_to_key_policy(trust_flags):
0201d8
-- 
0201d8
2.1.0
0201d8