Blame SOURCES/0021-Use-the-subject-base-from-the-IPA-configuration-not-.patch

dd2c9c
From ed5322daad5dc456e4958228835b33a32c7d1608 Mon Sep 17 00:00:00 2001
dd2c9c
From: Rob Crittenden <rcritten@redhat.com>
dd2c9c
Date: Tue, 29 Mar 2022 12:58:01 -0400
dd2c9c
Subject: [PATCH] Use the subject base from the IPA configuration, not REALM
dd2c9c
dd2c9c
The expected certificates were hardcoded with O={REALM} which
dd2c9c
would return false-positives if the customer defined their
dd2c9c
own certificate subject base.
dd2c9c
dd2c9c
Also add a search filter to only retrieve the certificate(s) we
dd2c9c
want to examine rather than the entire contents.
dd2c9c
dd2c9c
Fixes: https://github.com/freeipa/freeipa-healthcheck/issues/253
dd2c9c
dd2c9c
Signed-off-by: Rob Crittenden <rcritten@redhat.com>
dd2c9c
---
dd2c9c
 src/ipahealthcheck/ipa/certs.py |  24 ++--
dd2c9c
 tests/test_ipa_cert_match.py    | 202 ++++++++++++++++++++++++--------
dd2c9c
 2 files changed, 166 insertions(+), 60 deletions(-)
dd2c9c
dd2c9c
diff --git a/src/ipahealthcheck/ipa/certs.py b/src/ipahealthcheck/ipa/certs.py
dd2c9c
index 2ef33f9..fd5e180 100644
dd2c9c
--- a/src/ipahealthcheck/ipa/certs.py
dd2c9c
+++ b/src/ipahealthcheck/ipa/certs.py
dd2c9c
@@ -707,12 +707,14 @@ class IPADogtagCertsMatchCheck(IPAPlugin):
dd2c9c
 
dd2c9c
         def match_ldap_nss_certs_by_subject(plugin, ldap, db, dn,
dd2c9c
                                             expected_nicks_subjects):
dd2c9c
-            entries = ldap.get_entries(dn)
dd2c9c
             all_ok = True
dd2c9c
             for nick, subject in expected_nicks_subjects.items():
dd2c9c
+                entries = ldap.get_entries(
dd2c9c
+                    dn,
dd2c9c
+                    filter=f'subjectname={subject}'
dd2c9c
+                )
dd2c9c
                 cert = db.get_cert_from_db(nick)
dd2c9c
-                ok = any([cert in entry['userCertificate'] and
dd2c9c
-                          subject == entry['subjectName'][0]
dd2c9c
+                ok = any([cert in entry['userCertificate']
dd2c9c
                           for entry in entries
dd2c9c
                           if 'userCertificate' in entry])
dd2c9c
                 if not ok:
dd2c9c
@@ -740,26 +742,28 @@ class IPADogtagCertsMatchCheck(IPAPlugin):
dd2c9c
                                                       db, dn, 'CACertificate',
dd2c9c
                                                       casigning_nick)
dd2c9c
 
dd2c9c
+        config = api.Command.config_show()
dd2c9c
+        subject_base = config['result']['ipacertificatesubjectbase'][0]
dd2c9c
         expected_nicks_subjects = {
dd2c9c
             'ocspSigningCert cert-pki-ca':
dd2c9c
-                'CN=OCSP Subsystem,O=%s' % api.env.realm,
dd2c9c
+                f'CN=OCSP Subsystem,{subject_base}',
dd2c9c
             'subsystemCert cert-pki-ca':
dd2c9c
-                'CN=CA Subsystem,O=%s' % api.env.realm,
dd2c9c
+                f'CN=CA Subsystem,{subject_base}',
dd2c9c
             'auditSigningCert cert-pki-ca':
dd2c9c
-                'CN=CA Audit,O=%s' % api.env.realm,
dd2c9c
+                f'CN=CA Audit,{subject_base}',
dd2c9c
             'Server-Cert cert-pki-ca':
dd2c9c
-                'CN=%s,O=%s' % (api.env.host, api.env.realm),
dd2c9c
+                f'CN={api.env.host},{subject_base}',
dd2c9c
         }
dd2c9c
 
dd2c9c
         kra = krainstance.KRAInstance(api.env.realm)
dd2c9c
         if kra.is_installed():
dd2c9c
             kra_expected_nicks_subjects = {
dd2c9c
                 'transportCert cert-pki-kra':
dd2c9c
-                    'CN=KRA Transport Certificate,O=%s' % api.env.realm,
dd2c9c
+                    f'CN=KRA Transport Certificate,{subject_base}',
dd2c9c
                 'storageCert cert-pki-kra':
dd2c9c
-                    'CN=KRA Storage Certificate,O=%s' % api.env.realm,
dd2c9c
+                    f'CN=KRA Storage Certificate,{subject_base}',
dd2c9c
                 'auditSigningCert cert-pki-kra':
dd2c9c
-                    'CN=KRA Audit,O=%s' % api.env.realm,
dd2c9c
+                    f'CN=KRA Audit,{subject_base}',
dd2c9c
             }
dd2c9c
             expected_nicks_subjects.update(kra_expected_nicks_subjects)
dd2c9c
 
dd2c9c
diff --git a/tests/test_ipa_cert_match.py b/tests/test_ipa_cert_match.py
dd2c9c
index 460e61a..70ef59e 100644
dd2c9c
--- a/tests/test_ipa_cert_match.py
dd2c9c
+++ b/tests/test_ipa_cert_match.py
dd2c9c
@@ -44,8 +44,15 @@ class mock_ldap:
dd2c9c
     def get_entries(self, base_dn, scope=SCOPE_SUBTREE, filter=None,
dd2c9c
                     attrs_list=None, get_effective_rights=False, **kwargs):
dd2c9c
         if self.results is None:
dd2c9c
-            raise errors.NotFound(reason='test')
dd2c9c
-        return self.results.values()
dd2c9c
+            raise errors.NotFound(reason='None')
dd2c9c
+        if filter:
dd2c9c
+            (attr, value) = filter.split('=', maxsplit=1)
dd2c9c
+            for result in self.results.values():
dd2c9c
+                if result.get(attr)[0] == value:
dd2c9c
+                    return [result]
dd2c9c
+            raise errors.NotFound(reason='Not found %s' % filter)
dd2c9c
+
dd2c9c
+        return self.results
dd2c9c
 
dd2c9c
 
dd2c9c
 class mock_ldap_conn:
dd2c9c
@@ -82,6 +89,10 @@ class TestIPACertMatch(BaseTest):
dd2c9c
         Mock(return_value=mock_ldap_conn())
dd2c9c
     }
dd2c9c
 
dd2c9c
+    trust = {
dd2c9c
+        ('%s IPA CA' % m_api.env.realm): 'u,u,u'
dd2c9c
+    }
dd2c9c
+
dd2c9c
     @patch('ipalib.x509.load_certificate_list_from_file')
dd2c9c
     @patch('ipaserver.install.certs.CertDB')
dd2c9c
     def test_certs_match_ok(self, mock_certdb, mock_load_cert):
dd2c9c
@@ -92,11 +103,8 @@ class TestIPACertMatch(BaseTest):
dd2c9c
                                    'cn=certificates,cn=ipa,cn=etc',
dd2c9c
                                     m_api.env.basedn),
dd2c9c
                                 CACertificate=[IPACertificate()])
dd2c9c
-        trust = {
dd2c9c
-            ('%s IPA CA' % m_api.env.realm): 'u,u,u'
dd2c9c
-        }
dd2c9c
 
dd2c9c
-        mock_certdb.return_value = mock_CertDB(trust)
dd2c9c
+        mock_certdb.return_value = mock_CertDB(self.trust)
dd2c9c
         mock_load_cert.return_value = [IPACertificate()]
dd2c9c
 
dd2c9c
         framework = object()
dd2c9c
@@ -121,11 +129,8 @@ class TestIPACertMatch(BaseTest):
dd2c9c
                                    'cn=certificates,cn=ipa,cn=etc',
dd2c9c
                                     m_api.env.basedn),
dd2c9c
                                 CACertificate=[IPACertificate()])
dd2c9c
-        trust = {
dd2c9c
-            ('%s IPA CA' % m_api.env.realm): 'u,u,u'
dd2c9c
-        }
dd2c9c
 
dd2c9c
-        mock_certdb.return_value = mock_CertDB(trust)
dd2c9c
+        mock_certdb.return_value = mock_CertDB(self.trust)
dd2c9c
         mock_load_cert.return_value = [IPACertificate(serial_number=2)]
dd2c9c
 
dd2c9c
         framework = object()
dd2c9c
@@ -155,15 +160,54 @@ class TestIPACertMatch(BaseTest):
dd2c9c
         assert len(self.results) == 0
dd2c9c
 
dd2c9c
 
dd2c9c
+default_subject_base = [{
dd2c9c
+    'result':
dd2c9c
+        {
dd2c9c
+            'ipacertificatesubjectbase': [f'O={m_api.env.realm}'],
dd2c9c
+        },
dd2c9c
+}]
dd2c9c
+
dd2c9c
+custom_subject_base = [{
dd2c9c
+    'result':
dd2c9c
+        {
dd2c9c
+            'ipacertificatesubjectbase': ['OU=Eng,O=ACME'],
dd2c9c
+        },
dd2c9c
+}]
dd2c9c
+
dd2c9c
+
dd2c9c
 class TestIPADogtagCertMatch(BaseTest):
dd2c9c
     patches = {
dd2c9c
         'ipaserver.install.krainstance.KRAInstance':
dd2c9c
         Mock(return_value=KRAInstance()),
dd2c9c
     }
dd2c9c
+    trust = {
dd2c9c
+        'ocspSigningCert cert-pki-ca': 'u,u,u',
dd2c9c
+        'caSigningCert cert-pki-ca': 'u,u,u',
dd2c9c
+        'subsystemCert cert-pki-ca': 'u,u,u',
dd2c9c
+        'auditSigningCert cert-pki-ca': 'u,u,Pu',
dd2c9c
+        'Server-Cert cert-pki-ca': 'u,u,u',
dd2c9c
+        'transportCert cert-pki-kra': 'u,u,u',
dd2c9c
+        'storageCert cert-pki-kra': 'u,u,u',
dd2c9c
+        'auditSigningCert cert-pki-kra': 'u,u,Pu',
dd2c9c
+    }
dd2c9c
+
dd2c9c
+    def get_dogtag_subjects(self, hostname, base):
dd2c9c
+        subject_base = base[0]['result']['ipacertificatesubjectbase'][0]
dd2c9c
+        return (
dd2c9c
+            f'CN=OCSP Subsystem,{subject_base}',
dd2c9c
+            f'CN=CA Subsystem,{subject_base}',
dd2c9c
+            f'CN=CA Audit,{subject_base}',
dd2c9c
+            f'CN=%s,{subject_base}',
dd2c9c
+            f'CN=KRA Transport Certificate,{subject_base}',
dd2c9c
+            f'CN=KRA Storage Certificate,{subject_base}',
dd2c9c
+            f'CN=KRA Audit,{subject_base}',
dd2c9c
+            f'CN={hostname},{subject_base}',
dd2c9c
+        )
dd2c9c
 
dd2c9c
     @patch('ipaserver.install.certs.CertDB')
dd2c9c
     def test_certs_match_ok(self, mock_certdb):
dd2c9c
         """ Ensure match check is ok"""
dd2c9c
+        m_api.Command.config_show.side_effect = default_subject_base
dd2c9c
         fake_conn = LDAPClient('ldap://localhost', no_schema=True)
dd2c9c
         pkidbentry = LDAPEntry(fake_conn,
dd2c9c
                                DN('uid=pkidbuser,ou=people,o=ipaca'),
dd2c9c
@@ -177,25 +221,9 @@ class TestIPADogtagCertMatch(BaseTest):
dd2c9c
                                 userCertificate=[IPACertificate()],
dd2c9c
                                 subjectName=['test'])
dd2c9c
         ldap_entries = [pkidbentry, casignentry]
dd2c9c
-        trust = {
dd2c9c
-            'ocspSigningCert cert-pki-ca': 'u,u,u',
dd2c9c
-            'caSigningCert cert-pki-ca': 'u,u,u',
dd2c9c
-            'subsystemCert cert-pki-ca': 'u,u,u',
dd2c9c
-            'auditSigningCert cert-pki-ca': 'u,u,Pu',
dd2c9c
-            'Server-Cert cert-pki-ca': 'u,u,u',
dd2c9c
-            'transportCert cert-pki-kra': 'u,u,u',
dd2c9c
-            'storageCert cert-pki-kra': 'u,u,u',
dd2c9c
-            'auditSigningCert cert-pki-kra': 'u,u,Pu',
dd2c9c
-        }
dd2c9c
-
dd2c9c
-        dogtag_entries_subjects = (
dd2c9c
-            'CN=OCSP Subsystem,O=%s' % m_api.env.realm,
dd2c9c
-            'CN=CA Subsystem,O=%s' % m_api.env.realm,
dd2c9c
-            'CN=CA Audit,O=%s' % m_api.env.realm,
dd2c9c
-            'CN=%s,O=%s' % (m_api.env.host, m_api.env.realm),
dd2c9c
-            'CN=KRA Transport Certificate,O=%s' % m_api.env.realm,
dd2c9c
-            'CN=KRA Storage Certificate,O=%s' % m_api.env.realm,
dd2c9c
-            'CN=KRA Audit,O=%s' % m_api.env.realm,
dd2c9c
+
dd2c9c
+        dogtag_entries_subjects = self.get_dogtag_subjects(
dd2c9c
+            m_api.env.host, default_subject_base
dd2c9c
         )
dd2c9c
 
dd2c9c
         for i, subject in enumerate(dogtag_entries_subjects):
dd2c9c
@@ -206,7 +234,7 @@ class TestIPADogtagCertMatch(BaseTest):
dd2c9c
                               subjectName=[subject])
dd2c9c
             ldap_entries.append(entry)
dd2c9c
 
dd2c9c
-        mock_certdb.return_value = mock_CertDB(trust)
dd2c9c
+        mock_certdb.return_value = mock_CertDB(self.trust)
dd2c9c
 
dd2c9c
         framework = object()
dd2c9c
         registry.initialize(framework, config.Config())
dd2c9c
@@ -223,6 +251,7 @@ class TestIPADogtagCertMatch(BaseTest):
dd2c9c
     @patch('ipaserver.install.certs.CertDB')
dd2c9c
     def test_certs_mismatch(self, mock_certdb):
dd2c9c
         """ Ensure mismatches are detected"""
dd2c9c
+        m_api.Command.config_show.side_effect = default_subject_base
dd2c9c
         fake_conn = LDAPClient('ldap://localhost', no_schema=True)
dd2c9c
         pkidbentry = LDAPEntry(fake_conn,
dd2c9c
                                DN('uid=pkidbuser,ou=people,o=ipaca'),
dd2c9c
@@ -238,25 +267,9 @@ class TestIPADogtagCertMatch(BaseTest):
dd2c9c
                                 userCertificate=[IPACertificate()],
dd2c9c
                                 subjectName=['test'])
dd2c9c
         ldap_entries = [pkidbentry, casignentry]
dd2c9c
-        trust = {
dd2c9c
-            'ocspSigningCert cert-pki-ca': 'u,u,u',
dd2c9c
-            'caSigningCert cert-pki-ca': 'u,u,u',
dd2c9c
-            'subsystemCert cert-pki-ca': 'u,u,u',
dd2c9c
-            'auditSigningCert cert-pki-ca': 'u,u,Pu',
dd2c9c
-            'Server-Cert cert-pki-ca': 'u,u,u',
dd2c9c
-            'transportCert cert-pki-kra': 'u,u,u',
dd2c9c
-            'storageCert cert-pki-kra': 'u,u,u',
dd2c9c
-            'auditSigningCert cert-pki-kra': 'u,u,Pu',
dd2c9c
-        }
dd2c9c
-
dd2c9c
-        dogtag_entries_subjects = (
dd2c9c
-            'CN=OCSP Subsystem,O=%s' % m_api.env.realm,
dd2c9c
-            'CN=CA Subsystem,O=%s' % m_api.env.realm,
dd2c9c
-            'CN=CA Audit,O=%s' % m_api.env.realm,
dd2c9c
-            'CN=%s,O=%s' % (m_api.env.host, m_api.env.realm),
dd2c9c
-            'CN=KRA Transport Certificate,O=%s' % m_api.env.realm,
dd2c9c
-            'CN=KRA Storage Certificate,O=%s' % m_api.env.realm,
dd2c9c
-            'CN=KRA Audit,O=%s' % m_api.env.realm,
dd2c9c
+
dd2c9c
+        dogtag_entries_subjects = self.get_dogtag_subjects(
dd2c9c
+            m_api.env.host, default_subject_base
dd2c9c
         )
dd2c9c
 
dd2c9c
         for i, subject in enumerate(dogtag_entries_subjects):
dd2c9c
@@ -267,7 +280,7 @@ class TestIPADogtagCertMatch(BaseTest):
dd2c9c
                               subjectName=[subject])
dd2c9c
             ldap_entries.append(entry)
dd2c9c
 
dd2c9c
-        mock_certdb.return_value = mock_CertDB(trust)
dd2c9c
+        mock_certdb.return_value = mock_CertDB(self.trust)
dd2c9c
 
dd2c9c
         framework = object()
dd2c9c
         registry.initialize(framework, config.Config())
dd2c9c
@@ -280,3 +293,92 @@ class TestIPADogtagCertMatch(BaseTest):
dd2c9c
         assert result.result == constants.ERROR
dd2c9c
         assert result.source == 'ipahealthcheck.ipa.certs'
dd2c9c
         assert result.check == 'IPADogtagCertsMatchCheck'
dd2c9c
+
dd2c9c
+    @patch('ipaserver.install.certs.CertDB')
dd2c9c
+    def test_certs_match_ok_subject(self, mock_certdb):
dd2c9c
+        """ Ensure match check is ok"""
dd2c9c
+        m_api.Command.config_show.side_effect = custom_subject_base
dd2c9c
+        fake_conn = LDAPClient('ldap://localhost', no_schema=True)
dd2c9c
+        pkidbentry = LDAPEntry(fake_conn,
dd2c9c
+                               DN('uid=pkidbuser,ou=people,o=ipaca'),
dd2c9c
+                               userCertificate=[IPACertificate()],
dd2c9c
+                               subjectName=['test'])
dd2c9c
+        casignentry = LDAPEntry(fake_conn,
dd2c9c
+                                DN('cn=%s IPA CA' % m_api.env.realm,
dd2c9c
+                                   'cn=certificates,cn=ipa,cn=etc',
dd2c9c
+                                    m_api.env.basedn),
dd2c9c
+                                CACertificate=[IPACertificate()],
dd2c9c
+                                userCertificate=[IPACertificate()],
dd2c9c
+                                subjectName=['test'])
dd2c9c
+        ldap_entries = [pkidbentry, casignentry]
dd2c9c
+
dd2c9c
+        dogtag_entries_subjects = self.get_dogtag_subjects(
dd2c9c
+            m_api.env.host, custom_subject_base
dd2c9c
+        )
dd2c9c
+
dd2c9c
+        for i, subject in enumerate(dogtag_entries_subjects):
dd2c9c
+            entry = LDAPEntry(fake_conn,
dd2c9c
+                              DN('cn=%i,ou=certificateRepository' % i,
dd2c9c
+                                 'ou=ca,o=ipaca'),
dd2c9c
+                              userCertificate=[IPACertificate()],
dd2c9c
+                              subjectName=[subject])
dd2c9c
+            ldap_entries.append(entry)
dd2c9c
+
dd2c9c
+        mock_certdb.return_value = mock_CertDB(self.trust)
dd2c9c
+
dd2c9c
+        framework = object()
dd2c9c
+        registry.initialize(framework, config.Config())
dd2c9c
+        f = IPADogtagCertsMatchCheck(registry)
dd2c9c
+        f.conn = mock_ldap(ldap_entries)
dd2c9c
+        self.results = capture_results(f)
dd2c9c
+
dd2c9c
+        assert len(self.results) == 3
dd2c9c
+        for result in self.results.results:
dd2c9c
+            assert result.result == constants.SUCCESS
dd2c9c
+            assert result.source == 'ipahealthcheck.ipa.certs'
dd2c9c
+            assert result.check == 'IPADogtagCertsMatchCheck'
dd2c9c
+
dd2c9c
+    @patch('ipaserver.install.certs.CertDB')
dd2c9c
+    def test_certs_mismatch_subject(self, mock_certdb):
dd2c9c
+        """ Ensure mismatches are detected"""
dd2c9c
+        m_api.Command.config_show.side_effect = custom_subject_base
dd2c9c
+        fake_conn = LDAPClient('ldap://localhost', no_schema=True)
dd2c9c
+        pkidbentry = LDAPEntry(fake_conn,
dd2c9c
+                               DN('uid=pkidbuser,ou=people,o=ipaca'),
dd2c9c
+                               userCertificate=[IPACertificate(
dd2c9c
+                                   serial_number=2
dd2c9c
+                               )],
dd2c9c
+                               subjectName=['test'])
dd2c9c
+        casignentry = LDAPEntry(fake_conn,
dd2c9c
+                                DN('cn=%s IPA CA' % m_api.env.realm,
dd2c9c
+                                   'cn=certificates,cn=ipa,cn=etc',
dd2c9c
+                                    m_api.env.basedn),
dd2c9c
+                                CACertificate=[IPACertificate()],
dd2c9c
+                                userCertificate=[IPACertificate()],
dd2c9c
+                                subjectName=['test'])
dd2c9c
+        ldap_entries = [pkidbentry, casignentry]
dd2c9c
+
dd2c9c
+        dogtag_entries_subjects = self.get_dogtag_subjects(
dd2c9c
+            m_api.env.host, custom_subject_base
dd2c9c
+        )
dd2c9c
+
dd2c9c
+        for i, subject in enumerate(dogtag_entries_subjects):
dd2c9c
+            entry = LDAPEntry(fake_conn,
dd2c9c
+                              DN('cn=%i,ou=certificateRepository' % i,
dd2c9c
+                                 'ou=ca,o=ipaca'),
dd2c9c
+                              userCertificate=[IPACertificate()],
dd2c9c
+                              subjectName=[subject])
dd2c9c
+            ldap_entries.append(entry)
dd2c9c
+
dd2c9c
+        mock_certdb.return_value = mock_CertDB(self.trust)
dd2c9c
+
dd2c9c
+        framework = object()
dd2c9c
+        registry.initialize(framework, config.Config())
dd2c9c
+        f = IPADogtagCertsMatchCheck(registry)
dd2c9c
+        f.conn = mock_ldap(ldap_entries)
dd2c9c
+        self.results = capture_results(f)
dd2c9c
+
dd2c9c
+        assert len(self.results) == 3
dd2c9c
+        result = self.results.results[0]
dd2c9c
+        assert result.result == constants.ERROR
dd2c9c
+        assert result.source == 'ipahealthcheck.ipa.certs'
dd2c9c
-- 
dd2c9c
2.31.1
dd2c9c