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