89cb6d
From 87afc31838166409e29d2de750f10622cf1ddc46 Mon Sep 17 00:00:00 2001
89cb6d
From: Fraser Tweedale <ftweedal@redhat.com>
89cb6d
Date: Thu, 11 Jun 2020 22:42:38 +1000
89cb6d
Subject: [PATCH] fix iPAddress cert issuance for >1 host/service
89cb6d
89cb6d
The 'cert_request' command accumulates DNS names from the CSR,
89cb6d
before checking that all IP addresses in the CSR are reachable from
89cb6d
those DNS names.  Before adding a DNS name to the set, we check that
89cb6d
that it corresponds to the FQDN of a known host/service principal
89cb6d
(including principal aliases).  When a DNS name maps to a
89cb6d
"alternative" principal (i.e.  not the one given via the 'principal'
89cb6d
argument), this check was not being performed correctly.
89cb6d
Specifically, we were looking for the 'krbprincipalname' field on
89cb6d
the RPC response object directly, instead of its 'result' field.
89cb6d
89cb6d
To resolve the issue, dereference the RPC response to its 'result'
89cb6d
field before invoking the '_dns_name_matches_principal' subroutine.
89cb6d
89cb6d
Fixes: https://pagure.io/freeipa/issue/8368
89cb6d
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
89cb6d
---
89cb6d
 ipaserver/plugins/cert.py                     |  6 +-
89cb6d
 .../test_cert_request_ip_address.py           | 64 +++++++++++++++++--
89cb6d
 2 files changed, 63 insertions(+), 7 deletions(-)
89cb6d
89cb6d
diff --git a/ipaserver/plugins/cert.py b/ipaserver/plugins/cert.py
89cb6d
index 57ad1327feb62d5f45266bc9d5c6b8fba75a81aa..4af5c97f5722a7799509764df93c2433661dba20 100644
89cb6d
--- a/ipaserver/plugins/cert.py
89cb6d
+++ b/ipaserver/plugins/cert.py
89cb6d
@@ -814,13 +814,13 @@ class cert_request(Create, BaseCertMethod, VirtualCommand):
89cb6d
                 try:
89cb6d
                     if principal_type == HOST:
89cb6d
                         alt_principal_obj = api.Command['host_show'](
89cb6d
-                            name, all=True)
89cb6d
+                            name, all=True)['result']
89cb6d
                     elif principal_type == KRBTGT:
89cb6d
                         alt_principal = kerberos.Principal(
89cb6d
                             (u'host', name), principal.realm)
89cb6d
                     elif principal_type == SERVICE:
89cb6d
                         alt_principal_obj = api.Command['service_show'](
89cb6d
-                            alt_principal, all=True)
89cb6d
+                            alt_principal, all=True)['result']
89cb6d
                 except errors.NotFound:
89cb6d
                     # We don't want to issue any certificates referencing
89cb6d
                     # machines we don't know about. Nothing is stored in this
89cb6d
@@ -853,7 +853,7 @@ class cert_request(Create, BaseCertMethod, VirtualCommand):
89cb6d
                         pass
89cb6d
 
89cb6d
                     # Now check write access and caacl
89cb6d
-                    altdn = alt_principal_obj['result']['dn']
89cb6d
+                    altdn = alt_principal_obj['dn']
89cb6d
                     if not ldap.can_write(altdn, "usercertificate"):
89cb6d
                         raise errors.ACIError(info=_(
89cb6d
                             "Insufficient privilege to create a certificate "
89cb6d
diff --git a/ipatests/test_xmlrpc/test_cert_request_ip_address.py b/ipatests/test_xmlrpc/test_cert_request_ip_address.py
89cb6d
index 560a82da318933a9f2b8bf2a498f4eb6659ac2b3..e657df03fd110a39be24e156bf7da69769d4e79a 100644
89cb6d
--- a/ipatests/test_xmlrpc/test_cert_request_ip_address.py
89cb6d
+++ b/ipatests/test_xmlrpc/test_cert_request_ip_address.py
89cb6d
@@ -29,10 +29,16 @@ from ipatests.test_xmlrpc.tracker.host_plugin import HostTracker
89cb6d
 from ipatests.test_xmlrpc.tracker.user_plugin import UserTracker
89cb6d
 from ipatests.test_xmlrpc.xmlrpc_test import XMLRPC_test
89cb6d
 
89cb6d
-host_fqdn = u'iptest.{}'.format(api.env.domain)
89cb6d
+host_shortname = u'iptest'
89cb6d
+host_fqdn = u'{}.{}'.format(host_shortname, api.env.domain)
89cb6d
 host_princ = u'host/{}'.format(host_fqdn)
89cb6d
 host_ptr = u'{}.'.format(host_fqdn)
89cb6d
 
89cb6d
+host2_shortname = u'iptest2'
89cb6d
+host2_fqdn = u'{}.{}'.format(host2_shortname, api.env.domain)
89cb6d
+host2_princ = u'host/{}'.format(host2_fqdn)
89cb6d
+host2_ptr = u'{}.'.format(host2_fqdn)
89cb6d
+
89cb6d
 other_fqdn = u'other.{}'.format(api.env.domain)
89cb6d
 other_ptr = u'{}.'.format(other_fqdn)
89cb6d
 
89cb6d
@@ -40,6 +46,10 @@ ipv4_address = u'169.254.0.42'
89cb6d
 ipv4_revzone_s = u'0.254.169.in-addr.arpa.'
89cb6d
 ipv4_revrec_s = u'42'
89cb6d
 
89cb6d
+host2_ipv4_address = u'169.254.0.43'
89cb6d
+host2_ipv4_revzone_s = u'0.254.169.in-addr.arpa.'
89cb6d
+host2_ipv4_revrec_s = u'43'
89cb6d
+
89cb6d
 ipv6_address = u'fe80::8f18:bdab:4299:95fa'
89cb6d
 ipv6_revzone_s = u'0.0.0.0.0.0.0.0.0.0.0.0.0.8.e.f.ip6.arpa.'
89cb6d
 ipv6_revrec_s = u'a.f.5.9.9.9.2.4.b.a.d.b.8.1.f.8'
89cb6d
@@ -47,7 +57,13 @@ ipv6_revrec_s = u'a.f.5.9.9.9.2.4.b.a.d.b.8.1.f.8'
89cb6d
 
89cb6d
 @pytest.fixture(scope='class')
89cb6d
 def host(request):
89cb6d
-    tr = HostTracker('iptest')
89cb6d
+    tr = HostTracker(host_shortname)
89cb6d
+    return tr.make_fixture(request)
89cb6d
+
89cb6d
+
89cb6d
+@pytest.fixture(scope='class')
89cb6d
+def host2(request):
89cb6d
+    tr = HostTracker(host2_shortname)
89cb6d
     return tr.make_fixture(request)
89cb6d
 
89cb6d
 
89cb6d
@@ -91,6 +107,13 @@ def ipv6_revzone(host):
89cb6d
         yield x
89cb6d
 
89cb6d
 
89cb6d
+@yield_fixture(scope='class')
89cb6d
+def host2_ipv4_ptr(host2, ipv4_revzone):
89cb6d
+    for x in _record_setup(
89cb6d
+            host2, ipv4_revzone, host2_ipv4_revrec_s, ptrrecord=host2_ptr):
89cb6d
+        yield x
89cb6d
+
89cb6d
+
89cb6d
 @yield_fixture(scope='class')
89cb6d
 def ipv4_ptr(host, ipv4_revzone):
89cb6d
     for x in _record_setup(
89cb6d
@@ -105,17 +128,24 @@ def ipv6_ptr(host, ipv6_revzone):
89cb6d
         yield x
89cb6d
 
89cb6d
 
89cb6d
+@yield_fixture(scope='class')
89cb6d
+def host2_ipv4_a(host2):
89cb6d
+    for x in _record_setup(
89cb6d
+            host2, api.env.domain, host2_shortname, arecord=host2_ipv4_address):
89cb6d
+        yield x
89cb6d
+
89cb6d
+
89cb6d
 @yield_fixture(scope='class')
89cb6d
 def ipv4_a(host):
89cb6d
     for x in _record_setup(
89cb6d
-            host, api.env.domain, u'iptest', arecord=ipv4_address):
89cb6d
+            host, api.env.domain, host_shortname, arecord=ipv4_address):
89cb6d
         yield x
89cb6d
 
89cb6d
 
89cb6d
 @yield_fixture(scope='class')
89cb6d
 def ipv6_aaaa(host):
89cb6d
     for x in _record_setup(
89cb6d
-            host, api.env.domain, u'iptest', aaaarecord=ipv6_address):
89cb6d
+            host, api.env.domain, host_shortname, aaaarecord=ipv6_address):
89cb6d
         yield x
89cb6d
 
89cb6d
 
89cb6d
@@ -221,6 +251,12 @@ csr_cname2 = csr([
89cb6d
     x509.DNSName(u'cname2.{}'.format(api.env.domain)),
89cb6d
     x509.IPAddress(ipaddress.ip_address(ipv4_address)),
89cb6d
 ])
89cb6d
+csr_two_dnsname_two_ip = csr([
89cb6d
+    x509.DNSName(host_fqdn),
89cb6d
+    x509.IPAddress(ipaddress.ip_address(ipv4_address)),
89cb6d
+    x509.DNSName(host2_fqdn),
89cb6d
+    x509.IPAddress(ipaddress.ip_address(host2_ipv4_address)),
89cb6d
+])
89cb6d
 
89cb6d
 
89cb6d
 @pytest.fixture
89cb6d
@@ -463,3 +499,23 @@ class TestIPAddressCNAME(XMLRPC_test):
89cb6d
     def test_two_levels(self, host, csr_cname2):
89cb6d
         with pytest.raises(errors.ValidationError, match=PAT_FWD):
89cb6d
             host.run_command('cert_request', csr_cname2, principal=host_princ)
89cb6d
+
89cb6d
+
89cb6d
+@pytest.mark.tier1
89cb6d
+class TestTwoHostsTwoIPAddresses(XMLRPC_test):
89cb6d
+    """
89cb6d
+    Test certificate issuance with CSR containing two hosts
89cb6d
+    and two IP addresses (one for each host).
89cb6d
+
89cb6d
+    """
89cb6d
+    def test_host_exists(
89cb6d
+        self, host, host2, ipv4_a, ipv4_ptr, host2_ipv4_a, host2_ipv4_ptr,
89cb6d
+    ):
89cb6d
+        # for convenience, this test also establishes the DNS
89cb6d
+        # record fixtures, which have class scope
89cb6d
+        host.ensure_exists()
89cb6d
+        host2.ensure_exists()
89cb6d
+
89cb6d
+    def test_issuance(self, host, csr_two_dnsname_two_ip):
89cb6d
+        host.run_command(
89cb6d
+            'cert_request', csr_two_dnsname_two_ip, principal=host_princ)
89cb6d
-- 
89cb6d
2.26.2
89cb6d