|
|
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 |
|