From 95ea9608892490ded4aa5b0e06c681f1fb3b7f7b Mon Sep 17 00:00:00 2001 From: CentOS Sources Date: Oct 30 2018 04:58:29 +0000 Subject: import ipa-4.6.4-10.el7 --- diff --git a/.gitignore b/.gitignore index 6174b45..26693ca 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,4 @@ -SOURCES/freeipa-4.5.4.tar.gz +SOURCES/freeipa-4.6.4.tar.gz SOURCES/header-logo.png SOURCES/login-screen-background.jpg SOURCES/login-screen-logo.png diff --git a/.ipa.metadata b/.ipa.metadata index 7ed498a..2d52a52 100644 --- a/.ipa.metadata +++ b/.ipa.metadata @@ -1,4 +1,4 @@ -2707c6f6de4ab05e1cbd741297b655e1d1ef1c24 SOURCES/freeipa-4.5.4.tar.gz +a3be356b119c9c5e223e4c077fad21e1dbc0ce92 SOURCES/freeipa-4.6.4.tar.gz 77c318cf1f4fc25cf847de0692a77859a767c0e3 SOURCES/header-logo.png 8727245558422bf966d60677568925f081b8e299 SOURCES/login-screen-background.jpg 24a29d79efbd0906777be4639957abda111fca4b SOURCES/login-screen-logo.png diff --git a/SOURCES/0001-Use-replace-instead-of-add-to-set-new-default-ipaSEL.patch b/SOURCES/0001-Use-replace-instead-of-add-to-set-new-default-ipaSEL.patch new file mode 100644 index 0000000..d3d5c74 --- /dev/null +++ b/SOURCES/0001-Use-replace-instead-of-add-to-set-new-default-ipaSEL.patch @@ -0,0 +1,89 @@ +From 463e5e73a27bb31e3549c9204efe20555b7cb8dd Mon Sep 17 00:00:00 2001 +From: Rob Crittenden +Date: Fri, 1 Jun 2018 15:19:35 -0400 +Subject: [PATCH] Use replace instead of add to set new default + ipaSELinuxUserMapOrder + +The add was in effect replacing whatever data was already there +causing any custom order to be lost on each run of +ipa-server-upgrade. + +https://pagure.io/freeipa/issue/6610 + +Signed-off-by: Rob Crittenden +Reviewed-By: Florence Blanc-Renaud +Reviewed-By: Florence Blanc-Renaud +--- + install/updates/50-ipaconfig.update | 2 +- + ipatests/test_integration/test_commands.py | 48 ++++++++++++++++++++++++++++++ + 2 files changed, 49 insertions(+), 1 deletion(-) + create mode 100644 ipatests/test_integration/test_commands.py + +diff --git a/install/updates/50-ipaconfig.update b/install/updates/50-ipaconfig.update +index 23d2919dbd976c34d9217fc31cca88a0df6c7f5b..18501cb7b8a87377a76bc53b7fe3c469c23e2d41 100644 +--- a/install/updates/50-ipaconfig.update ++++ b/install/updates/50-ipaconfig.update +@@ -1,5 +1,5 @@ + dn: cn=ipaConfig,cn=etc,$SUFFIX +-add:ipaSELinuxUserMapOrder: guest_u:s0$$xguest_u:s0$$user_u:s0$$staff_u:s0-s0:c0.c1023$$unconfined_u:s0-s0:c0.c1023 ++replace: ipaSELinuxUserMapOrder: guest_u:s0$$xguest_u:s0$$user_u:s0-s0:c0.c1023$$staff_u:s0-s0:c0.c1023$$unconfined_u:s0-s0:c0.c1023::ipaSELinuxUserMapOrder: guest_u:s0$$xguest_u:s0$$user_u:s0$$staff_u:s0-s0:c0.c1023$$unconfined_u:s0-s0:c0.c1023 + add:ipaSELinuxUserMapDefault: unconfined_u:s0-s0:c0.c1023 + add:ipaUserObjectClasses: ipasshuser + remove:ipaConfigString:AllowLMhash +diff --git a/ipatests/test_integration/test_commands.py b/ipatests/test_integration/test_commands.py +new file mode 100644 +index 0000000000000000000000000000000000000000..b2c0d5c710c9810cfd74216983f793808f4cf3c4 +--- /dev/null ++++ b/ipatests/test_integration/test_commands.py +@@ -0,0 +1,48 @@ ++# ++# Copyright (C) 2018 FreeIPA Contributors see COPYING for license ++# ++"""Misc test for 'ipa' CLI regressions ++""" ++from __future__ import absolute_import ++ ++from ipatests.test_integration.base import IntegrationTest ++ ++ ++class TestIPACommand(IntegrationTest): ++ """ ++ A lot of commands can be executed against a single IPA installation ++ so provide a generic class to execute one-off commands that need to be ++ tested without having to fire up a full server to run one command. ++ """ ++ topology = 'line' ++ ++ def test_change_selinuxusermaporder(self): ++ """ ++ An update file meant to ensure a more sane default was ++ overriding any customization done to the order. ++ """ ++ maporder = "unconfined_u:s0-s0:c0.c1023" ++ ++ # set a new default ++ result = self.master.run_command( ++ ["ipa", "config-mod", ++ "--ipaselinuxusermaporder={}".format(maporder)], ++ raiseonerr=False ++ ) ++ assert result.returncode == 0 ++ ++ # apply the update ++ result = self.master.run_command( ++ ["ipa-server-upgrade"], ++ raiseonerr=False ++ ) ++ assert result.returncode == 0 ++ ++ # ensure result is the same ++ result = self.master.run_command( ++ ["ipa", "config-show"], ++ raiseonerr=False ++ ) ++ assert result.returncode == 0 ++ assert "SELinux user map order: {}".format( ++ maporder) in result.stdout_text +-- +2.14.4 + diff --git a/SOURCES/0001-ds-ignore-time-skew-during-initial-replication-step.patch b/SOURCES/0001-ds-ignore-time-skew-during-initial-replication-step.patch deleted file mode 100644 index d641eb3..0000000 --- a/SOURCES/0001-ds-ignore-time-skew-during-initial-replication-step.patch +++ /dev/null @@ -1,86 +0,0 @@ -From a3bcb05ce1c554aa98af9343bec7335521db3a3e Mon Sep 17 00:00:00 2001 -From: Alexander Bokovoy -Date: Mon, 16 Oct 2017 13:32:38 +0300 -Subject: [PATCH] ds: ignore time skew during initial replication step - -Initial replica creation can go with ignoring time skew checks. -We should, however, force time skew checks during normal operation. - -Fixes https://pagure.io/freeipa/issue/7211 - -Reviewed-By: Rob Crittenden ---- - install/share/Makefile.am | 1 + - install/share/replica-prevent-time-skew.ldif | 4 ++++ - ipaserver/install/dsinstance.py | 24 ++++++++++++++++++++++++ - 3 files changed, 29 insertions(+) - create mode 100644 install/share/replica-prevent-time-skew.ldif - -diff --git a/install/share/Makefile.am b/install/share/Makefile.am -index 85a061c6976dcc55b0ba2250423a344e14f2ce97..46b3d77663113f770765c8bd1d8a916791d628f4 100644 ---- a/install/share/Makefile.am -+++ b/install/share/Makefile.am -@@ -38,6 +38,7 @@ dist_app_DATA = \ - default-trust-view.ldif \ - delegation.ldif \ - replica-acis.ldif \ -+ replica-prevent-time-skew.ldif \ - ds-nfiles.ldif \ - dns.ldif \ - dnssec.ldif \ -diff --git a/install/share/replica-prevent-time-skew.ldif b/install/share/replica-prevent-time-skew.ldif -new file mode 100644 -index 0000000000000000000000000000000000000000..5d301feddb56347f3b35be89edaae1a7d91e07de ---- /dev/null -+++ b/install/share/replica-prevent-time-skew.ldif -@@ -0,0 +1,4 @@ -+dn: cn=config -+changetype: modify -+replace: nsslapd-ignore-time-skew -+nsslapd-ignore-time-skew: $SKEWVALUE -diff --git a/ipaserver/install/dsinstance.py b/ipaserver/install/dsinstance.py -index c9db8ac28c3ca10539b745ca09f4d8aaece02e0c..7a88612997a3fa96cf394852401fb01e5e4501d5 100644 ---- a/ipaserver/install/dsinstance.py -+++ b/ipaserver/install/dsinstance.py -@@ -392,7 +392,21 @@ class DsInstance(service.Service): - self.step("restarting directory server", self.__restart_instance) - - self.step("creating DS keytab", self.request_service_keytab) -+ -+ # 389-ds allows to ignore time skew during replication. It is disabled -+ # by default to avoid issues with non-contiguous CSN values which -+ # derived from a time stamp when the change occurs. However, there are -+ # cases when we are interested only in the changes coming from the -+ # other side and should therefore allow ignoring the time skew. -+ # -+ # This helps with initial replication or force-sync because -+ # the receiving side has no valuable changes itself yet. -+ self.step("ignore time skew for initial replication", -+ self.__replica_ignore_initial_time_skew) -+ - self.step("setting up initial replication", self.__setup_replica) -+ self.step("prevent time skew after initial replication", -+ self.replica_manage_time_skew) - self.step("adding sasl mappings to the directory", self.__configure_sasl_mappings) - self.step("updating schema", self.__update_schema) - # See LDIFs for automember configuration during replica install -@@ -929,6 +943,16 @@ class DsInstance(service.Service): - def __add_replication_acis(self): - self._ldap_mod("replica-acis.ldif", self.sub_dict) - -+ def __replica_ignore_initial_time_skew(self): -+ self.replica_manage_time_skew(prevent=False) -+ -+ def replica_manage_time_skew(self, prevent=True): -+ if prevent: -+ self.sub_dict['SKEWVALUE'] = 'off' -+ else: -+ self.sub_dict['SKEWVALUE'] = 'on' -+ self._ldap_mod("replica-prevent-time-skew.ldif", self.sub_dict) -+ - def __setup_s4u2proxy(self): - self._ldap_mod("replica-s4u2proxy.ldif", self.sub_dict) - --- -2.9.5 - diff --git a/SOURCES/0002-Sort-and-shuffle-SRV-record-by-priority-and-weight.patch b/SOURCES/0002-Sort-and-shuffle-SRV-record-by-priority-and-weight.patch new file mode 100644 index 0000000..23b67a9 --- /dev/null +++ b/SOURCES/0002-Sort-and-shuffle-SRV-record-by-priority-and-weight.patch @@ -0,0 +1,438 @@ +From a56b9ef37dde90a593da6adbae2525048c5c8627 Mon Sep 17 00:00:00 2001 +From: Christian Heimes +Date: Fri, 15 Jun 2018 17:03:29 +0200 +Subject: [PATCH] Sort and shuffle SRV record by priority and weight + +On multiple occasions, SRV query answers were not properly sorted by +priority. Records with same priority weren't randomized and shuffled. +This caused FreeIPA to contact the same remote peer instead of +distributing the load across all available servers. + +Two new helper functions now take care of SRV queries. sort_prio_weight() +sorts SRV and URI records. query_srv() combines SRV lookup with +sort_prio_weight(). + +Fixes: https://pagure.io/freeipa/issue/7475 +Signed-off-by: Christian Heimes +Reviewed-By: Rob Crittenden +--- + ipaclient/install/ipadiscovery.py | 5 +- + ipalib/rpc.py | 21 +++---- + ipalib/util.py | 11 ++-- + ipapython/config.py | 8 +-- + ipapython/dnsutil.py | 92 ++++++++++++++++++++++++++- + ipaserver/dcerpc.py | 4 +- + ipatests/test_ipapython/test_dnsutil.py | 106 ++++++++++++++++++++++++++++++++ + 7 files changed, 217 insertions(+), 30 deletions(-) + create mode 100644 ipatests/test_ipapython/test_dnsutil.py + +diff --git a/ipaclient/install/ipadiscovery.py b/ipaclient/install/ipadiscovery.py +index 363970c86ba65b7ec49e403adc745fe61b2242aa..a8283d8e6ad070a8b6626a187ff55485bb74eba4 100644 +--- a/ipaclient/install/ipadiscovery.py ++++ b/ipaclient/install/ipadiscovery.py +@@ -20,7 +20,6 @@ + from __future__ import absolute_import + + import logging +-import operator + import socket + + import six +@@ -28,6 +27,7 @@ import six + from dns import resolver, rdatatype + from dns.exception import DNSException + from ipalib import errors ++from ipapython.dnsutil import query_srv + from ipapython import ipaldap + from ipaplatform.paths import paths + from ipapython.ipautil import valid_ip, realm_to_suffix +@@ -498,8 +498,7 @@ class IPADiscovery(object): + logger.debug("Search DNS for SRV record of %s", qname) + + try: +- answers = resolver.query(qname, rdatatype.SRV) +- answers = sorted(answers, key=operator.attrgetter('priority')) ++ answers = query_srv(qname) + except DNSException as e: + logger.debug("DNS record not found: %s", e.__class__.__name__) + answers = [] +diff --git a/ipalib/rpc.py b/ipalib/rpc.py +index c6a8989f5dc157f4c4e59637e6e7d114c5fa952c..17368f160148d6b821ec4934d31b4b4034a7e67b 100644 +--- a/ipalib/rpc.py ++++ b/ipalib/rpc.py +@@ -45,7 +45,6 @@ import gzip + from cryptography import x509 as crypto_x509 + + import gssapi +-from dns import resolver, rdatatype + from dns.exception import DNSException + from ssl import SSLError + import six +@@ -61,7 +60,7 @@ from ipalib.x509 import Encoding as x509_Encoding + from ipapython import ipautil + from ipapython import session_storage + from ipapython.cookie import Cookie +-from ipapython.dnsutil import DNSName ++from ipapython.dnsutil import DNSName, query_srv + from ipalib.text import _ + from ipalib.util import create_https_connection + from ipalib.krb_utils import KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN, KRB5KRB_AP_ERR_TKT_EXPIRED, \ +@@ -878,7 +877,7 @@ class RPCClient(Connectible): + name = '_ldap._tcp.%s.' % self.env.domain + + try: +- answers = resolver.query(name, rdatatype.SRV) ++ answers = query_srv(name) + except DNSException: + answers = [] + +@@ -886,17 +885,11 @@ class RPCClient(Connectible): + server = str(answer.target).rstrip(".") + servers.append('https://%s%s' % (ipautil.format_netloc(server), path)) + +- servers = list(set(servers)) +- # the list/set conversion won't preserve order so stick in the +- # local config file version here. +- cfg_server = rpc_uri +- if cfg_server in servers: +- # make sure the configured master server is there just once and +- # it is the first one +- servers.remove(cfg_server) +- servers.insert(0, cfg_server) +- else: +- servers.insert(0, cfg_server) ++ # make sure the configured master server is there just once and ++ # it is the first one. ++ if rpc_uri in servers: ++ servers.remove(rpc_uri) ++ servers.insert(0, rpc_uri) + + return servers + +diff --git a/ipalib/util.py b/ipalib/util.py +index ebf6eb3faf91cefc02514afa84ad9b63d0f82b0b..592821f9ff4f9c16fb2589d697e3e202443d0eba 100644 +--- a/ipalib/util.py ++++ b/ipalib/util.py +@@ -973,14 +973,13 @@ def detect_dns_zone_realm_type(api, domain): + + try: + # The presence of this record is enough, return foreign in such case +- result = resolver.query(ad_specific_record_name, rdatatype.SRV) +- return 'foreign' +- ++ resolver.query(ad_specific_record_name, rdatatype.SRV) + except DNSException: +- pass ++ # If we could not detect type with certainty, return unknown ++ return 'unknown' ++ else: ++ return 'foreign' + +- # If we could not detect type with certainity, return unknown +- return 'unknown' + + def has_managed_topology(api): + domainlevel = api.Command['domainlevel_get']().get('result', DOMAIN_LEVEL_0) +diff --git a/ipapython/config.py b/ipapython/config.py +index c3360779f1320e7d41f4d98b317d930a1d0b8242..f701122bd4828693a41848668e49052912042837 100644 +--- a/ipapython/config.py ++++ b/ipapython/config.py +@@ -26,7 +26,6 @@ from copy import copy + import socket + import functools + +-from dns import resolver, rdatatype + from dns.exception import DNSException + import dns.name + # pylint: disable=import-error +@@ -36,6 +35,7 @@ from six.moves.urllib.parse import urlsplit + + from ipaplatform.paths import paths + from ipapython.dn import DN ++from ipapython.dnsutil import query_srv + from ipapython.ipautil import CheckedIPAddress, CheckedIPAddressLoopback + + +@@ -210,7 +210,7 @@ def __discover_config(discover_server = True): + name = "_ldap._tcp." + domain + + try: +- servers = resolver.query(name, rdatatype.SRV) ++ servers = query_srv(name) + except DNSException: + # try cycling on domain components of FQDN + try: +@@ -225,7 +225,7 @@ def __discover_config(discover_server = True): + return False + name = "_ldap._tcp.%s" % domain + try: +- servers = resolver.query(name, rdatatype.SRV) ++ servers = query_srv(name) + break + except DNSException: + pass +@@ -236,7 +236,7 @@ def __discover_config(discover_server = True): + if not servers: + name = "_ldap._tcp.%s." % config.default_domain + try: +- servers = resolver.query(name, rdatatype.SRV) ++ servers = query_srv(name) + except DNSException: + pass + +diff --git a/ipapython/dnsutil.py b/ipapython/dnsutil.py +index b40302d0efbb32108626d2cc1216ad4858343407..6157183a0fa5802a5fb772f078ee4c1688857fc8 100644 +--- a/ipapython/dnsutil.py ++++ b/ipapython/dnsutil.py +@@ -17,12 +17,17 @@ + # along with this program. If not, see . + # + ++import copy + import logging ++import operator ++import random + + import dns.name + import dns.exception + import dns.resolver +-import copy ++import dns.rdataclass ++import dns.rdatatype ++ + + import six + +@@ -373,3 +378,88 @@ def check_zone_overlap(zone, raise_on_error=True): + if ns: + msg += u" and is handled by server(s): {0}".format(', '.join(ns)) + raise ValueError(msg) ++ ++ ++def _mix_weight(records): ++ """Weighted population sorting for records with same priority ++ """ ++ # trivial case ++ if len(records) <= 1: ++ return records ++ ++ # Optimization for common case: If all weights are the same (e.g. 0), ++ # just shuffle the records, which is about four times faster. ++ if all(rr.weight == records[0].weight for rr in records): ++ random.shuffle(records) ++ return records ++ ++ noweight = 0.01 # give records with 0 weight a small chance ++ result = [] ++ records = set(records) ++ while len(records) > 1: ++ # Compute the sum of the weights of those RRs. Then choose a ++ # uniform random number between 0 and the sum computed (inclusive). ++ urn = random.uniform(0, sum(rr.weight or noweight for rr in records)) ++ # Select the RR whose running sum value is the first in the selected ++ # order which is greater than or equal to the random number selected. ++ acc = 0. ++ for rr in records.copy(): ++ acc += rr.weight or noweight ++ if acc >= urn: ++ records.remove(rr) ++ result.append(rr) ++ if records: ++ result.append(records.pop()) ++ return result ++ ++ ++def sort_prio_weight(records): ++ """RFC 2782 sorting algorithm for SRV and URI records ++ ++ RFC 2782 defines a sorting algorithms for SRV records, that is also used ++ for sorting URI records. Records are sorted by priority and than randomly ++ shuffled according to weight. ++ ++ This implementation also removes duplicate entries. ++ """ ++ # order records by priority ++ records = sorted(records, key=operator.attrgetter("priority")) ++ ++ # remove duplicate entries ++ uniquerecords = [] ++ seen = set() ++ for rr in records: ++ # A SRV record has target and port, URI just has target. ++ target = (rr.target, getattr(rr, "port", None)) ++ if target not in seen: ++ uniquerecords.append(rr) ++ seen.add(target) ++ ++ # weighted randomization of entries with same priority ++ result = [] ++ sameprio = [] ++ for rr in uniquerecords: ++ # add all items with same priority in a bucket ++ if not sameprio or sameprio[0].priority == rr.priority: ++ sameprio.append(rr) ++ else: ++ # got different priority, shuffle bucket ++ result.extend(_mix_weight(sameprio)) ++ # start a new priority list ++ sameprio = [rr] ++ # add last batch of records with same priority ++ if sameprio: ++ result.extend(_mix_weight(sameprio)) ++ return result ++ ++ ++def query_srv(qname, resolver=None, **kwargs): ++ """Query SRV records and sort reply according to RFC 2782 ++ ++ :param qname: query name, _service._proto.domain. ++ :return: list of dns.rdtypes.IN.SRV.SRV instances ++ """ ++ if resolver is None: ++ resolver = dns.resolver ++ answer = resolver.query(qname, rdtype=dns.rdatatype.SRV, **kwargs) ++ return sort_prio_weight(answer) +diff --git a/ipaserver/dcerpc.py b/ipaserver/dcerpc.py +index e3aa9f6a6c239f8606f09bde06f21cb9cf64eb14..1e2d6fbbb5372bea7c6d68b73acededfe714a56a 100644 +--- a/ipaserver/dcerpc.py ++++ b/ipaserver/dcerpc.py +@@ -32,6 +32,7 @@ from ipalib import api, _ + from ipalib import errors + from ipapython import ipautil + from ipapython.dn import DN ++from ipapython.dnsutil import query_srv + from ipapython.ipaldap import ldap_initialize + from ipaserver.install import installutils + from ipaserver.dcerpc_common import (TRUST_BIDIRECTIONAL, +@@ -55,7 +56,6 @@ import samba + import ldap as _ldap + from ipapython import ipaldap + from ipapython.dnsutil import DNSName +-from dns import resolver, rdatatype + from dns.exception import DNSException + import pysss_nss_idmap + import pysss +@@ -802,7 +802,7 @@ class DomainValidator(object): + gc_name = '_gc._tcp.%s.' % info['dns_domain'] + + try: +- answers = resolver.query(gc_name, rdatatype.SRV) ++ answers = query_srv(gc_name) + except DNSException as e: + answers = [] + +diff --git a/ipatests/test_ipapython/test_dnsutil.py b/ipatests/test_ipapython/test_dnsutil.py +new file mode 100644 +index 0000000000000000000000000000000000000000..36adb077cf38f6d036aa1048b201dee7d08eb310 +--- /dev/null ++++ b/ipatests/test_ipapython/test_dnsutil.py +@@ -0,0 +1,106 @@ ++# ++# Copyright (C) 2018 FreeIPA Contributors. See COPYING for license ++# ++import dns.name ++import dns.rdataclass ++import dns.rdatatype ++from dns.rdtypes.IN.SRV import SRV ++from dns.rdtypes.ANY.URI import URI ++ ++from ipapython import dnsutil ++ ++import pytest ++ ++ ++def mksrv(priority, weight, port, target): ++ return SRV( ++ rdclass=dns.rdataclass.IN, ++ rdtype=dns.rdatatype.SRV, ++ priority=priority, ++ weight=weight, ++ port=port, ++ target=dns.name.from_text(target) ++ ) ++ ++ ++def mkuri(priority, weight, target): ++ return URI( ++ rdclass=dns.rdataclass.IN, ++ rdtype=dns.rdatatype.URI, ++ priority=priority, ++ weight=weight, ++ target=target ++ ) ++ ++ ++class TestSortSRV(object): ++ def test_empty(self): ++ assert dnsutil.sort_prio_weight([]) == [] ++ ++ def test_one(self): ++ h1 = mksrv(1, 0, 443, u"host1") ++ assert dnsutil.sort_prio_weight([h1]) == [h1] ++ ++ h2 = mksrv(10, 5, 443, u"host2") ++ assert dnsutil.sort_prio_weight([h2]) == [h2] ++ ++ def test_prio(self): ++ h1 = mksrv(1, 0, 443, u"host1") ++ h2 = mksrv(2, 0, 443, u"host2") ++ h3 = mksrv(3, 0, 443, u"host3") ++ assert dnsutil.sort_prio_weight([h3, h2, h1]) == [h1, h2, h3] ++ assert dnsutil.sort_prio_weight([h3, h3, h3]) == [h3] ++ assert dnsutil.sort_prio_weight([h2, h2, h1, h1]) == [h1, h2] ++ ++ h380 = mksrv(4, 0, 80, u"host3") ++ assert dnsutil.sort_prio_weight([h1, h3, h380]) == [h1, h3, h380] ++ ++ hs = mksrv(-1, 0, 443, u"special") ++ assert dnsutil.sort_prio_weight([h1, h2, hs]) == [hs, h1, h2] ++ ++ def assert_permutations(self, answers, permutations): ++ seen = set() ++ for _unused in range(1000): ++ result = tuple(dnsutil.sort_prio_weight(answers)) ++ assert result in permutations ++ seen.add(result) ++ if seen == permutations: ++ break ++ else: ++ pytest.fail("sorting didn't exhaust all permutations.") ++ ++ def test_sameprio(self): ++ h1 = mksrv(1, 0, 443, u"host1") ++ h2 = mksrv(1, 0, 443, u"host2") ++ permutations = { ++ (h1, h2), ++ (h2, h1), ++ } ++ self.assert_permutations([h1, h2], permutations) ++ ++ def test_weight(self): ++ h1 = mksrv(1, 0, 443, u"host1") ++ h2_w15 = mksrv(2, 15, 443, u"host2") ++ h3_w10 = mksrv(2, 10, 443, u"host3") ++ ++ permutations = { ++ (h1, h2_w15, h3_w10), ++ (h1, h3_w10, h2_w15), ++ } ++ self.assert_permutations([h1, h2_w15, h3_w10], permutations) ++ ++ def test_large(self): ++ records = tuple( ++ mksrv(1, i, 443, "host{}".format(i)) for i in range(1000) ++ ) ++ assert len(dnsutil.sort_prio_weight(records)) == len(records) ++ ++ ++class TestSortURI(object): ++ def test_prio(self): ++ h1 = mkuri(1, 0, u"https://host1/api") ++ h2 = mkuri(2, 0, u"https://host2/api") ++ h3 = mkuri(3, 0, u"https://host3/api") ++ assert dnsutil.sort_prio_weight([h3, h2, h1]) == [h1, h2, h3] ++ assert dnsutil.sort_prio_weight([h3, h3, h3]) == [h3] ++ assert dnsutil.sort_prio_weight([h2, h2, h1, h1]) == [h1, h2] +-- +2.14.4 + diff --git a/SOURCES/0002-ipa-replica-manage-implicitly-ignore-initial-time-sk.patch b/SOURCES/0002-ipa-replica-manage-implicitly-ignore-initial-time-sk.patch deleted file mode 100644 index ecde808..0000000 --- a/SOURCES/0002-ipa-replica-manage-implicitly-ignore-initial-time-sk.patch +++ /dev/null @@ -1,41 +0,0 @@ -From 469f8ba59eb369267a9d404291ce7794f996d9f4 Mon Sep 17 00:00:00 2001 -From: Alexander Bokovoy -Date: Mon, 16 Oct 2017 13:46:38 +0300 -Subject: [PATCH] ipa-replica-manage: implicitly ignore initial time skew in - force-sync - -When performing force synchronization, implicitly ignore initial -time skew (if any) and restore it afterwards. - -This also changes semantics of force-sync by waiting until the end of -the initial replication. - -Fixes https://pagure.io/freeipa/issue/7211 - -Reviewed-By: Rob Crittenden ---- - install/tools/ipa-replica-manage | 6 ++++++ - 1 file changed, 6 insertions(+) - -diff --git a/install/tools/ipa-replica-manage b/install/tools/ipa-replica-manage -index f802201b7f93facb1e78463aa02eab66a1ae23ea..c00d8ca3a0fa8228c5aa782a270991f14ee16974 100755 ---- a/install/tools/ipa-replica-manage -+++ b/install/tools/ipa-replica-manage -@@ -1231,8 +1231,14 @@ def force_sync(realm, thishost, fromhost, dirman_passwd, nolookup=False): - repl = replication.ReplicationManager(realm, thishost, dirman_passwd) - repl.force_sync(repl.conn, fromhost) - else: -+ ds = dsinstance.DsInstance(realm_name=realm) -+ ds.ldapi = os.getegid() == 0 -+ ds.replica_manage_time_skew(prevent=False) - repl = replication.ReplicationManager(realm, fromhost, dirman_passwd) - repl.force_sync(repl.conn, thishost) -+ agreement = repl.get_replication_agreement(thishost) -+ repl.wait_for_repl_init(repl.conn, agreement.dn) -+ ds.replica_manage_time_skew(prevent=True) - - def show_DNA_ranges(hostname, master, realm, dirman_passwd, nextrange=False, - nolookup=False): --- -2.9.5 - diff --git a/SOURCES/0003-Checks-if-replica-s4u2proxy.ldif-should-be-applied.patch b/SOURCES/0003-Checks-if-replica-s4u2proxy.ldif-should-be-applied.patch deleted file mode 100644 index 475d399..0000000 --- a/SOURCES/0003-Checks-if-replica-s4u2proxy.ldif-should-be-applied.patch +++ /dev/null @@ -1,51 +0,0 @@ -From 17fab4982fcb8b8af6c20130907dd3d4bad7f699 Mon Sep 17 00:00:00 2001 -From: Felipe Barreto -Date: Fri, 13 Oct 2017 09:19:43 +0200 -Subject: [PATCH] Checks if replica-s4u2proxy.ldif should be applied - -Before applying replica-s3u2proxy.ldif, we check -if the values are already there. The values can be -there if a replica installation was done in the past -and some info was left behind. Also, the code checks -the values independently. - -https://pagure.io/freeipa/issue/7174 - -Reviewed-By: Rob Crittenden ---- - ipaserver/install/dsinstance.py | 19 ++++++++++++++++++- - 1 file changed, 18 insertions(+), 1 deletion(-) - -diff --git a/ipaserver/install/dsinstance.py b/ipaserver/install/dsinstance.py -index 7a88612997a3fa96cf394852401fb01e5e4501d5..923f483340a26a614001701ce6c235dd73501501 100644 ---- a/ipaserver/install/dsinstance.py -+++ b/ipaserver/install/dsinstance.py -@@ -954,7 +954,24 @@ class DsInstance(service.Service): - self._ldap_mod("replica-prevent-time-skew.ldif", self.sub_dict) - - def __setup_s4u2proxy(self): -- self._ldap_mod("replica-s4u2proxy.ldif", self.sub_dict) -+ -+ def __add_principal(last_cn, principal, self): -+ dn = DN(('cn', last_cn), ('cn', 's4u2proxy'), -+ ('cn', 'etc'), self.suffix) -+ -+ value = '{principal}/{fqdn}@{realm}'.format(fqdn=self.fqdn, -+ realm=self.realm, -+ principal=principal) -+ -+ entry = api.Backend.ldap2.get_entry(dn, ['memberPrincipal']) -+ try: -+ entry['memberPrincipal'].append(value) -+ api.Backend.ldap2.update_entry(entry) -+ except errors.EmptyModlist: -+ pass -+ -+ __add_principal('ipa-http-delegation', 'HTTP', self) -+ __add_principal('ipa-ldap-delegation-targets', 'ldap', self) - - def __create_indices(self): - self._ldap_mod("indices.ldif") --- -2.9.5 - diff --git a/SOURCES/0003-Increase-WSGI-process-count-to-5-on-64bit.patch b/SOURCES/0003-Increase-WSGI-process-count-to-5-on-64bit.patch new file mode 100644 index 0000000..2c1575f --- /dev/null +++ b/SOURCES/0003-Increase-WSGI-process-count-to-5-on-64bit.patch @@ -0,0 +1,91 @@ +From f9d883b97b24efaf82e510601a4711bc9c0ea9c7 Mon Sep 17 00:00:00 2001 +From: Christian Heimes +Date: Thu, 14 Jun 2018 17:04:13 +0200 +Subject: [PATCH] Increase WSGI process count to 5 on 64bit + +Increase the WSGI daemon worker process count from 2 processes to 5 +processes. This allows IPA RPC to handle more parallel requests. The +additional processes increase memory consumption by approximante 250 MB +in total. + +Since memory is scarce on 32bit platforms, only 64bit platforms are +bumped to 5 workers. + +Fixes: https://pagure.io/freeipa/issue/7587 +Signed-off-by: Christian Heimes +Reviewed-By: Alexander Bokovoy +--- + install/conf/ipa.conf | 2 +- + ipaplatform/base/constants.py | 5 +++++ + ipaserver/install/httpinstance.py | 1 + + ipaserver/install/server/upgrade.py | 3 ++- + 4 files changed, 9 insertions(+), 2 deletions(-) + +diff --git a/install/conf/ipa.conf b/install/conf/ipa.conf +index 696e5aab1aca1875eb711b91351f41987a8379d1..912a63c2240e0681dfbeeac223a902b15b304716 100644 +--- a/install/conf/ipa.conf ++++ b/install/conf/ipa.conf +@@ -47,7 +47,7 @@ WSGISocketPrefix /run/httpd/wsgi + + + # Configure mod_wsgi handler for /ipa +-WSGIDaemonProcess ipa processes=2 threads=1 maximum-requests=500 \ ++WSGIDaemonProcess ipa processes=$WSGI_PROCESSES threads=1 maximum-requests=500 \ + user=ipaapi group=ipaapi display-name=%{GROUP} socket-timeout=2147483647 \ + lang=C.UTF-8 locale=C.UTF-8 + WSGIImportScript /usr/share/ipa/wsgi.py process-group=ipa application-group=ipa +diff --git a/ipaplatform/base/constants.py b/ipaplatform/base/constants.py +index ca4a12ec017e81b32dc796062608a96ce65a0235..075a3ba774e1286e50258b464bd7687d484f6029 100644 +--- a/ipaplatform/base/constants.py ++++ b/ipaplatform/base/constants.py +@@ -5,9 +5,11 @@ + ''' + This base platform module exports platform dependant constants. + ''' ++import sys + + + class BaseConstantsNamespace(object): ++ IS_64BITS = sys.maxsize > 2 ** 32 + DS_USER = 'dirsrv' + DS_GROUP = 'dirsrv' + HTTPD_USER = "apache" +@@ -42,6 +44,9 @@ class BaseConstantsNamespace(object): + # WSGI module override, only used on Fedora + MOD_WSGI_PYTHON2 = None + MOD_WSGI_PYTHON3 = None ++ # WSGIDaemonProcess process count. On 64bit platforms, each process ++ # consumes about 110 MB RSS, from which are about 35 MB shared. ++ WSGI_PROCESSES = 5 if IS_64BITS else 2 + + + constants = BaseConstantsNamespace() +diff --git a/ipaserver/install/httpinstance.py b/ipaserver/install/httpinstance.py +index 231bffa2c6ec1fd124738649912e4bd958e802b7..3764870ee77f2ba0da18ec004664e6f66c13bba1 100644 +--- a/ipaserver/install/httpinstance.py ++++ b/ipaserver/install/httpinstance.py +@@ -148,6 +148,7 @@ class HTTPInstance(service.Service): + DOMAIN=self.domain, + AUTOREDIR='' if auto_redirect else '#', + CRL_PUBLISH_PATH=paths.PKI_CA_PUBLISH_DIR, ++ WSGI_PROCESSES=constants.WSGI_PROCESSES, + ) + self.ca_file = ca_file + if ca_is_configured is not None: +diff --git a/ipaserver/install/server/upgrade.py b/ipaserver/install/server/upgrade.py +index ab09b98406df251f769c4747c9250075ab610362..ee3cedd78a2f45f33665bf562e9426cf68325544 100644 +--- a/ipaserver/install/server/upgrade.py ++++ b/ipaserver/install/server/upgrade.py +@@ -1681,7 +1681,8 @@ def upgrade_configuration(): + AUTOREDIR='' if auto_redirect else '#', + CRL_PUBLISH_PATH=paths.PKI_CA_PUBLISH_DIR, + DOGTAG_PORT=8009, +- CLONE='#' ++ CLONE='#', ++ WSGI_PROCESSES=constants.WSGI_PROCESSES, + ) + + subject_base = find_subject_base() +-- +2.14.4 + diff --git a/SOURCES/0004-Always-set-ca_host-when-installing-replica.patch b/SOURCES/0004-Always-set-ca_host-when-installing-replica.patch new file mode 100644 index 0000000..2023e60 --- /dev/null +++ b/SOURCES/0004-Always-set-ca_host-when-installing-replica.patch @@ -0,0 +1,43 @@ +From 5a5b232b721a68e37de2e25f134b8e585ad71393 Mon Sep 17 00:00:00 2001 +From: Christian Heimes +Date: Tue, 19 Jun 2018 19:10:27 +0200 +Subject: [PATCH] Always set ca_host when installing replica + +ipa-replica-install only set ca_host in its temporary +/etc/ipa/default.conf, when it wasn't installing a replica with CA. As a +consequence, the replica installer was picking a random CA server from +LDAP. + +Always set the replication peer as ca_host. This will ensure that the +installer uses the same replication peer for CA. In case the replication +peer is not a CA master, the installer will automatically pick another +host later. + +See: https://pagure.io/freeipa/issue/7566 +Signed-off-by: Christian Heimes +Reviewed-By: Fraser Tweedale +--- + ipaserver/install/server/replicainstall.py | 6 ++---- + 1 file changed, 2 insertions(+), 4 deletions(-) + +diff --git a/ipaserver/install/server/replicainstall.py b/ipaserver/install/server/replicainstall.py +index e2a37bc4c8305c525f224f2fb80cb2629e8ece24..33f3ae9e616b34a3ab0ff8e4257552855e817e7c 100644 +--- a/ipaserver/install/server/replicainstall.py ++++ b/ipaserver/install/server/replicainstall.py +@@ -241,11 +241,9 @@ def create_ipa_conf(fstore, config, ca_enabled, master=None): + gopts.extend([ + ipaconf.setOption('enable_ra', 'True'), + ipaconf.setOption('ra_plugin', 'dogtag'), +- ipaconf.setOption('dogtag_version', '10') ++ ipaconf.setOption('dogtag_version', '10'), ++ ipaconf.setOption('ca_host', config.ca_host_name) + ]) +- +- if not config.setup_ca: +- gopts.append(ipaconf.setOption('ca_host', config.ca_host_name)) + else: + gopts.extend([ + ipaconf.setOption('enable_ra', 'False'), +-- +2.14.4 + diff --git a/SOURCES/0004-ldap-limit-the-retro-changelog-to-dns-subtree.patch b/SOURCES/0004-ldap-limit-the-retro-changelog-to-dns-subtree.patch deleted file mode 100644 index 5fa10b1..0000000 --- a/SOURCES/0004-ldap-limit-the-retro-changelog-to-dns-subtree.patch +++ /dev/null @@ -1,32 +0,0 @@ -From d3c36fb83314c3fd1b87572a1c80687f06d7e2d5 Mon Sep 17 00:00:00 2001 -From: Tomas Krizek -Date: Mon, 23 Oct 2017 14:06:20 +0200 -Subject: [PATCH] ldap: limit the retro changelog to dns subtree - -The content synchronization plugin can be limited to the dns subtree in -Directory Server. This increases performance and helps to prevent some -potential issues. - -Fixes: https://pagure.io/freeipa/issue/6515 -Signed-off-by: Tomas Krizek -Reviewed-By: Rob Crittenden ---- - install/updates/20-syncrepl.update | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/install/updates/20-syncrepl.update b/install/updates/20-syncrepl.update -index faa13f645f492ea35824fe57632b56d52afa8a6e..318eda16870afa06d6c6d9098cbffdc085f2dba2 100644 ---- a/install/updates/20-syncrepl.update -+++ b/install/updates/20-syncrepl.update -@@ -4,7 +4,7 @@ only:nsslapd-pluginEnabled: on - # Remember original nsuniqueid for objects referenced from cn=changelog - add:nsslapd-attribute: nsuniqueid:targetUniqueId - add:nsslapd-changelogmaxage: 2d --add:nsslapd-exclude-suffix: o=ipaca -+add:nsslapd-include-suffix: cn=dns,$SUFFIX - - # Keep memberOf and referential integrity plugins away from cn=changelog. - # It is necessary for performance reasons because we don't have appropriate --- -2.9.5 - diff --git a/SOURCES/0005-Fix-ipa-replica-conncheck-when-called-with-principal.patch b/SOURCES/0005-Fix-ipa-replica-conncheck-when-called-with-principal.patch deleted file mode 100644 index f3f0bd7..0000000 --- a/SOURCES/0005-Fix-ipa-replica-conncheck-when-called-with-principal.patch +++ /dev/null @@ -1,45 +0,0 @@ -From 20f2650a8a23d288571fde552ed1c242cd972d88 Mon Sep 17 00:00:00 2001 -From: Florence Blanc-Renaud -Date: Fri, 27 Oct 2017 09:05:20 +0200 -Subject: [PATCH] Fix ipa-replica-conncheck when called with --principal - -ipa-replica-conncheck can be called with --principal / --password or -with an existing Kerberos credential cache in order to supply the -authorized identity logging in to the master machine (in -auto-master-check mode). - -In domain-level 0, the tool is called with --principal and password -and tries to obtain a TGT by performing kinit, but does not set the -env var KRB5CCNAME. Subsequent calls to IPA API do not use the -credential cache and fail. In this case, ipa-replica-conncheck falls -back to using SSH to check master connectivity instead of IPA API, -and the ssh check is less robust. - -The code should set the KRB5CCNAME env var for IPA API to use the -credential cache. - -Fixes: -https://pagure.io/freeipa/issue/7221 - -Reviewed-By: Rob Crittenden ---- - install/tools/ipa-replica-conncheck | 3 +++ - 1 file changed, 3 insertions(+) - -diff --git a/install/tools/ipa-replica-conncheck b/install/tools/ipa-replica-conncheck -index 03281d1c7b6ee9f1d4cabebceb0c7e64b09601c0..545cdf00ca74289e6532a40de4c9abad5af4cee0 100755 ---- a/install/tools/ipa-replica-conncheck -+++ b/install/tools/ipa-replica-conncheck -@@ -534,6 +534,9 @@ def main(): - if result.returncode != 0: - raise RuntimeError("Could not get ticket for master server: %s" % - result.error_output) -+ # Now that the cred cache file is initialized, -+ # use it for the IPA API calls -+ os.environ['KRB5CCNAME'] = CCACHE_FILE - - try: - root_logger.info("Check RPC connection to remote master") --- -2.9.5 - diff --git a/SOURCES/0005-ipaserver-config-plugin-Increase-search-records-mini.patch b/SOURCES/0005-ipaserver-config-plugin-Increase-search-records-mini.patch new file mode 100644 index 0000000..0944ea8 --- /dev/null +++ b/SOURCES/0005-ipaserver-config-plugin-Increase-search-records-mini.patch @@ -0,0 +1,139 @@ +From 2461d69242108fe6f4bc067cc8255e41f66c58aa Mon Sep 17 00:00:00 2001 +From: Armando Neto +Date: Mon, 18 Jun 2018 18:26:01 -0300 +Subject: [PATCH] ipaserver config plugin: Increase search records minimum + limit + +Check if the given search records value is greater than an arbitrary number that is not so close to zero. + +https://pagure.io/freeipa/issue/6617 + +Reviewed-By: Rob Crittenden +--- + ipaserver/plugins/config.py | 14 +++++- + ipatests/test_xmlrpc/test_config_plugin.py | 76 ++++++++++++++++++++++++++++++ + 2 files changed, 89 insertions(+), 1 deletion(-) + +diff --git a/ipaserver/plugins/config.py b/ipaserver/plugins/config.py +index 33ed38ba016567b9df57503f2f8418cf7c7fc794..d367c3c5aa421bb22d1630c88bbac846e7d84386 100644 +--- a/ipaserver/plugins/config.py ++++ b/ipaserver/plugins/config.py +@@ -85,6 +85,18 @@ EXAMPLES: + + register = Registry() + ++ ++def validate_search_records_limit(ugettext, value): ++ """Check if value is greater than a realistic minimum. ++ ++ Values 0 and -1 are valid, as they represent unlimited. ++ """ ++ if value in {-1, 0}: ++ return ++ if value < 10: ++ return _('must be at least 10') ++ ++ + @register() + class config(LDAPObject): + """ +@@ -161,10 +173,10 @@ class config(LDAPObject): + minvalue=-1, + ), + Int('ipasearchrecordslimit', ++ validate_search_records_limit, + cli_name='searchrecordslimit', + label=_('Search size limit'), + doc=_('Maximum number of records to search (-1 or 0 is unlimited)'), +- minvalue=-1, + ), + IA5Str('ipausersearchfields', + cli_name='usersearch', +diff --git a/ipatests/test_xmlrpc/test_config_plugin.py b/ipatests/test_xmlrpc/test_config_plugin.py +index c037224162e2c29f6dd76eabefe7fededc6f882d..666b7c2c87b4f0a1f7bde18c78780a1ea6072b71 100644 +--- a/ipatests/test_xmlrpc/test_config_plugin.py ++++ b/ipatests/test_xmlrpc/test_config_plugin.py +@@ -211,4 +211,80 @@ class test_config(Declarative): + summary=None, + ), + ), ++ dict( ++ desc='Set the number of search records to -1 (unlimited)', ++ command=( ++ 'config_mod', [], { ++ 'ipasearchrecordslimit': u'-1', ++ }, ++ ), ++ expected={ ++ 'result': lambda d: d['ipasearchrecordslimit'] == (u'-1',), ++ 'summary': None, ++ 'value': None, ++ }, ++ ), ++ dict( ++ desc='Set the number of search records to greater than 10', ++ command=( ++ 'config_mod', [], { ++ 'ipasearchrecordslimit': u'100', ++ }, ++ ), ++ expected={ ++ 'result': lambda d: d['ipasearchrecordslimit'] == (u'100',), ++ 'summary': None, ++ 'value': None, ++ }, ++ ), ++ dict( ++ desc='Set the number of search records to lower than -1', ++ command=( ++ 'config_mod', [], { ++ 'ipasearchrecordslimit': u'-10', ++ }, ++ ), ++ expected=errors.ValidationError( ++ name=u'searchrecordslimit', ++ error=u'must be at least 10', ++ ), ++ ), ++ dict( ++ desc='Set the number of search records to lower than 10', ++ command=( ++ 'config_mod', [], { ++ 'ipasearchrecordslimit': u'1', ++ }, ++ ), ++ expected=errors.ValidationError( ++ name=u'searchrecordslimit', ++ error=u'must be at least 10', ++ ), ++ ), ++ dict( ++ desc='Set the number of search records to zero (unlimited)', ++ command=( ++ 'config_mod', [], { ++ 'ipasearchrecordslimit': u'0', ++ }, ++ ), ++ expected={ ++ 'result': lambda d: d['ipasearchrecordslimit'] == (u'-1',), ++ 'summary': None, ++ 'value': None, ++ }, ++ ), ++ dict( ++ desc='Set the number of search records back to 100', ++ command=( ++ 'config_mod', [], { ++ 'ipasearchrecordslimit': u'100', ++ }, ++ ), ++ expected={ ++ 'result': lambda d: d['ipasearchrecordslimit'] == (u'100',), ++ 'summary': None, ++ 'value': None, ++ }, ++ ), + ] +-- +2.14.4 + diff --git a/SOURCES/0006-Improve-and-fix-timeout-bug-in-wait_for_entry.patch b/SOURCES/0006-Improve-and-fix-timeout-bug-in-wait_for_entry.patch new file mode 100644 index 0000000..dcff4e2 --- /dev/null +++ b/SOURCES/0006-Improve-and-fix-timeout-bug-in-wait_for_entry.patch @@ -0,0 +1,107 @@ +From b17a85bded822c4078a9b817720652807a939bc8 Mon Sep 17 00:00:00 2001 +From: Christian Heimes +Date: Fri, 22 Jun 2018 09:39:26 +0200 +Subject: [PATCH] Improve and fix timeout bug in wait_for_entry() + +replication.wait_for_entry() now can wait for an attribute value to +appear on a replica. + +Fixed timeout handling caused by bad rounding and comparison. For small +timeouts, the actual time was rounded down. For example for 60 seconds +timeout and fast replica, the query accumulated to about 0.45 seconds +plus 60 seconds sleep. 60.45 is large enough to terminate the loop +"while int(time.time()) < timeout", but not large enough to trigger the +exception in "if int(time.time()) > timeout", because int(60.65) == 60. + +See: https://pagure.io/freeipa/issue/7593 +Fixes: https://pagure.io/freeipa/issue/7595 +Signed-off-by: Christian Heimes +Reviewed-By: Fraser Tweedale +--- + ipaserver/install/replication.py | 54 +++++++++++++++++++++------------------- + 1 file changed, 29 insertions(+), 25 deletions(-) + +diff --git a/ipaserver/install/replication.py b/ipaserver/install/replication.py +index 2e7ce53fef89093a7f661d846aa138ee330961e6..6d9878e16faab1b88a5ab79649571de6802c8536 100644 +--- a/ipaserver/install/replication.py ++++ b/ipaserver/install/replication.py +@@ -20,6 +20,7 @@ + from __future__ import print_function, absolute_import + + import logging ++import itertools + + import six + import time +@@ -160,40 +161,43 @@ def wait_for_task(conn, dn): + return exit_code + + +-def wait_for_entry(connection, dn, timeout=7200, attr='', quiet=True): +- """Wait for entry and/or attr to show up""" +- +- filter = "(objectclass=*)" ++def wait_for_entry(connection, dn, timeout=7200, attr=None, attrvalue='*', ++ quiet=True): ++ """Wait for entry and/or attr to show up ++ """ ++ log = logger.debug if quiet else logger.info + attrlist = [] +- if attr: +- filter = "(%s=*)" % attr ++ if attr is not None: ++ filterstr = ipaldap.LDAPClient.make_filter_from_attr(attr, attrvalue) + attrlist.append(attr) +- timeout += int(time.time()) +- +- if not quiet: +- sys.stdout.write("Waiting for %s %s:%s " % (connection, dn, attr)) +- sys.stdout.flush() +- entry = None +- while not entry and int(time.time()) < timeout: ++ else: ++ filterstr = "(objectclass=*)" ++ log("Waiting for replication (%s) %s %s", connection, dn, filterstr) ++ entry = [] ++ deadline = time.time() + timeout ++ for i in itertools.count(start=1): + try: +- [entry] = connection.get_entries( +- dn, ldap.SCOPE_BASE, filter, attrlist) ++ entry = connection.get_entries( ++ dn, ldap.SCOPE_BASE, filterstr, attrlist) + except errors.NotFound: + pass # no entry yet + except Exception as e: # badness + logger.error("Error reading entry %s: %s", dn, e) + raise +- if not entry: +- if not quiet: +- sys.stdout.write(".") +- sys.stdout.flush() +- time.sleep(1) + +- if not entry and int(time.time()) > timeout: +- raise errors.NotFound( +- reason="wait_for_entry timeout for %s for %s" % (connection, dn)) +- elif entry and not quiet: +- logger.error("The waited for entry is: %s", entry) ++ if entry: ++ log("Entry found %r", entry) ++ return ++ elif time.time() > deadline: ++ raise errors.NotFound( ++ reason="wait_for_entry timeout on {} for {}".format( ++ connection, dn ++ ) ++ ) ++ else: ++ if i % 10 == 0: ++ logger.debug("Still waiting for replication of %s", dn) ++ time.sleep(1) + + + class ReplicationManager(object): +-- +2.14.4 + diff --git a/SOURCES/0006-Include-the-CA-basic-constraint-in-CSRs-when-renewin.patch b/SOURCES/0006-Include-the-CA-basic-constraint-in-CSRs-when-renewin.patch deleted file mode 100644 index 794cb5f..0000000 --- a/SOURCES/0006-Include-the-CA-basic-constraint-in-CSRs-when-renewin.patch +++ /dev/null @@ -1,75 +0,0 @@ -From 148e78d74206730c31dd7bc87eece5c5bd1440ac Mon Sep 17 00:00:00 2001 -From: Rob Crittenden -Date: Wed, 9 Aug 2017 17:28:35 -0400 -Subject: [PATCH] Include the CA basic constraint in CSRs when renewing a CA - -The CSR generated by `ipa-cacert-manage renew --external-ca` did -not include the CA basic constraint: - - X509v3 Basic Constraints: critical - CA:TRUE - -Add a flag to certmonger::resubmit_request to specify that a -CA is being requested. - -Note that this also sets pathlen to -1 which means an unlimited -pathlen. Leave it up to the issuing CA to set this. - -https://pagure.io/freeipa/issue/7088 - -Reviewed-By: Florence Blanc-Renaud -Reviewed-By: Florence Blanc-Renaud ---- - ipalib/install/certmonger.py | 13 +++++++++++-- - ipaserver/install/ipa_cacert_manage.py | 3 ++- - 2 files changed, 13 insertions(+), 3 deletions(-) - -diff --git a/ipalib/install/certmonger.py b/ipalib/install/certmonger.py -index c286996ee2318e241b4af190d1a01f42e28aa9f3..d2b782ddb0c746a3dfd96d0222bb31c6a960fdff 100644 ---- a/ipalib/install/certmonger.py -+++ b/ipalib/install/certmonger.py -@@ -519,16 +519,25 @@ def modify(request_id, ca=None, profile=None): - request.obj_if.modify(update) - - --def resubmit_request(request_id, ca=None, profile=None): -+def resubmit_request(request_id, ca=None, profile=None, is_ca=False): -+ """ -+ :param request_id: the certmonger numeric request ID -+ :param ca: the nickname for the certmonger CA, e.g. IPA or SelfSign -+ :param profile: the dogtag template profile to use, e.g. SubCA -+ :param is_ca: boolean that if True adds the CA basic constraint -+ """ - request = _get_request({'nickname': request_id}) - if request: -- if ca or profile: -+ if ca or profile or is_ca: - update = {} - if ca is not None: - cm = _certmonger() - update['CA'] = cm.obj_if.find_ca_by_nickname(ca) - if profile is not None: - update['template-profile'] = profile -+ if is_ca: -+ update['template-is-ca'] = True -+ update['template-ca-path-length'] = -1 # no path length - request.obj_if.modify(update) - request.obj_if.resubmit() - -diff --git a/ipaserver/install/ipa_cacert_manage.py b/ipaserver/install/ipa_cacert_manage.py -index fcbf09155a3abc9ce9481aa2519ed39aaa6aa9bb..9607620d6c3e63b70b9e586f94282bf478c8c53e 100644 ---- a/ipaserver/install/ipa_cacert_manage.py -+++ b/ipaserver/install/ipa_cacert_manage.py -@@ -310,7 +310,8 @@ class CACertManage(admintool.AdminTool): - timeout = api.env.startup_timeout + 60 - - self.log.debug("resubmitting certmonger request '%s'", self.request_id) -- certmonger.resubmit_request(self.request_id, ca=ca, profile=profile) -+ certmonger.resubmit_request(self.request_id, ca=ca, profile=profile, -+ is_ca=True) - try: - state = certmonger.wait_for_request(self.request_id, timeout) - except RuntimeError: --- -2.9.5 - diff --git a/SOURCES/0007-Use-common-replication-wait-timeout-of-5min.patch b/SOURCES/0007-Use-common-replication-wait-timeout-of-5min.patch new file mode 100644 index 0000000..d09ecc8 --- /dev/null +++ b/SOURCES/0007-Use-common-replication-wait-timeout-of-5min.patch @@ -0,0 +1,123 @@ +From 08d27e373976f82e054d17a76b8653221c56225c Mon Sep 17 00:00:00 2001 +From: Christian Heimes +Date: Fri, 22 Jun 2018 10:00:24 +0200 +Subject: [PATCH] Use common replication wait timeout of 5min + +Instead of multiple timeout values all over the code base, all +replication waits now use a common timeout value from api.env of 5 +minutes. Waiting for HTTP/replica principal takes 90 to 120 seconds, so +5 minutes seem like a sufficient value for slow setups. + +Fixes: https://pagure.io/freeipa/issue/7595 +Signed-off-by: Christian Heimes +Reviewed-By: Fraser Tweedale +--- + ipalib/constants.py | 2 ++ + ipaserver/install/custodiainstance.py | 4 +++- + ipaserver/install/httpinstance.py | 6 +++++- + ipaserver/install/krbinstance.py | 13 ++++++++----- + ipaserver/install/replication.py | 6 ++++-- + 5 files changed, 22 insertions(+), 9 deletions(-) + +diff --git a/ipalib/constants.py b/ipalib/constants.py +index 9ae6e0aaaee0577372fe458feb7660e05c7fed4d..38ed57b3cc35afe536f90b1f4245fd73ad4d3740 100644 +--- a/ipalib/constants.py ++++ b/ipalib/constants.py +@@ -149,6 +149,8 @@ DEFAULT_CONFIG = ( + ('startup_timeout', 300), + # How long http connection should wait for reply [seconds]. + ('http_timeout', 30), ++ # How long to wait for an entry to appear on a replica ++ ('replication_wait_timeout', 300), + + # Web Application mount points + ('mount_ipa', '/ipa/'), +diff --git a/ipaserver/install/custodiainstance.py b/ipaserver/install/custodiainstance.py +index c87306d2f48367031e888613b07c1091fc1a70f4..fcfe0908426bf71243974c384b0147eb763ad32f 100644 +--- a/ipaserver/install/custodiainstance.py ++++ b/ipaserver/install/custodiainstance.py +@@ -5,6 +5,7 @@ from __future__ import print_function, absolute_import + import enum + import logging + ++from ipalib import api + from ipaserver.secrets.kem import IPAKEMKeys, KEMLdap + from ipaserver.secrets.client import CustodiaClient + from ipaplatform.paths import paths +@@ -214,7 +215,8 @@ class CustodiaInstance(SimpleServiceInstance): + cli = self._get_custodia_client() + cli.fetch_key('dm/DMHash') + +- def _wait_keys(self, timeout=300): ++ def _wait_keys(self): ++ timeout = api.env.replication_wait_timeout + deadline = int(time.time()) + timeout + logger.info("Waiting up to %s seconds to see our keys " + "appear on host %s", timeout, self.ldap_uri) +diff --git a/ipaserver/install/httpinstance.py b/ipaserver/install/httpinstance.py +index 3764870ee77f2ba0da18ec004664e6f66c13bba1..bdd79b1dafda7de664eed664a18bf36c541212bc 100644 +--- a/ipaserver/install/httpinstance.py ++++ b/ipaserver/install/httpinstance.py +@@ -644,4 +644,8 @@ class HTTPInstance(service.Service): + else: + remote_ldap.simple_bind(ipaldap.DIRMAN_DN, + self.dm_password) +- replication.wait_for_entry(remote_ldap, service_dn, timeout=60) ++ replication.wait_for_entry( ++ remote_ldap, ++ service_dn, ++ timeout=api.env.replication_wait_timeout ++ ) +diff --git a/ipaserver/install/krbinstance.py b/ipaserver/install/krbinstance.py +index 4a5a52dcc831c4edfe0cd52c44fb18bcb6adaef7..a356d5e0c1b96dc6511c335fc22a326a2133bdd8 100644 +--- a/ipaserver/install/krbinstance.py ++++ b/ipaserver/install/krbinstance.py +@@ -399,13 +399,16 @@ class KrbInstance(service.Service): + def _wait_for_replica_kdc_entry(self): + master_dn = self.api.Object.server.get_dn(self.fqdn) + kdc_dn = DN(('cn', 'KDC'), master_dn) +- +- ldap_uri = 'ldap://{}'.format(self.master_fqdn) +- ++ ldap_uri = ipaldap.get_ldap_uri(self.master_fqdn) + with ipaldap.LDAPClient( +- ldap_uri, cacert=paths.IPA_CA_CRT) as remote_ldap: ++ ldap_uri, cacert=paths.IPA_CA_CRT, start_tls=True ++ ) as remote_ldap: + remote_ldap.gssapi_bind() +- replication.wait_for_entry(remote_ldap, kdc_dn, timeout=60) ++ replication.wait_for_entry( ++ remote_ldap, ++ kdc_dn, ++ timeout=api.env.replication_wait_timeout ++ ) + + def _call_certmonger(self, certmonger_ca='IPA'): + subject = str(DN(('cn', self.fqdn), self.subject_base)) +diff --git a/ipaserver/install/replication.py b/ipaserver/install/replication.py +index 6d9878e16faab1b88a5ab79649571de6802c8536..5ce8fa689c1a6fd3b9d4cbddbd5454d36334b729 100644 +--- a/ipaserver/install/replication.py ++++ b/ipaserver/install/replication.py +@@ -161,7 +161,7 @@ def wait_for_task(conn, dn): + return exit_code + + +-def wait_for_entry(connection, dn, timeout=7200, attr=None, attrvalue='*', ++def wait_for_entry(connection, dn, timeout, attr=None, attrvalue='*', + quiet=True): + """Wait for entry and/or attr to show up + """ +@@ -751,7 +751,9 @@ class ReplicationManager(object): + # that we will have to set the memberof fixup task + self.need_memberof_fixup = True + +- wait_for_entry(a_conn, entry.dn) ++ wait_for_entry( ++ a_conn, entry.dn, timeout=api.env.replication_wait_timeout ++ ) + + def needs_memberof_fixup(self): + return self.need_memberof_fixup +-- +2.14.4 + diff --git a/SOURCES/0007-ipa-extdom-extop-refactor-nsswitch-operations.patch b/SOURCES/0007-ipa-extdom-extop-refactor-nsswitch-operations.patch deleted file mode 100644 index b98db3e..0000000 --- a/SOURCES/0007-ipa-extdom-extop-refactor-nsswitch-operations.patch +++ /dev/null @@ -1,1728 +0,0 @@ -From 8a80363e07b5c9309d785bf3b41f506f32a750a5 Mon Sep 17 00:00:00 2001 -From: Alexander Bokovoy -Date: Tue, 14 Nov 2017 12:44:02 +0200 -Subject: [PATCH] ipa-extdom-extop: refactor nsswitch operations - -Refactor nsswitch operations in ipa-extdom-extop plugin to allow use -of timeout-enabled nsswitch calls provided by libsss_nss_idmap. - -Standard POSIX nsswitch API has no way to cancel requests which may -cause ipa-extdom-extop requests to hang far too long and potentially -exhaust LDAP server workers. In addition, glibc nsswitch API iterates -through all nsswitch modules one by one and with multiple parallel -requests a lock up may happen in an unrelated nsswitch module like -nss_files.so.2. - -A solution to the latter issue is to directly load nss_sss.so.2 plugin -and utilize it. This, however, does not solve a problem with lack of -cancellable API. - -With SSSD 1.16.1, libsss_nss_idmap provides a timeout-enabled variant of -nsswitch API that is directly integrated with SSSD client side machinery -used by nss_sss.so.2. As result, this API can be used instead of loading -nss_sss.so.2 directly. - -To support older SSSD version, both direct loading of nss_sss.so.2 and -new timeout-enabled API are supported by this changeset. An API to -abstract both is designed to be a mix between internal glibc nsswitch -API and external nsswitch API that libsss_nss_idmap mimics. API does not -expose per-call timeout. Instead, it allows to set a timeout per -nsswitch operation context to reduce requirements on information -a caller has to maintain. - -A choice which API to use is made at configure time. - -In order to test the API, a cmocka test is updated to explicitly load -nss_files.so.2 as a backend. Since use of nss_sss.so.2 would always -depend on availablility of SSSD, predictable testing would not be -possible without it otherwise. Also, cmocka test does not use -nss_wrapper anymore because nss_wrapper overrides higher level glibc -nsswitch API while we are loading an individual nsswitch module -directly. - -As result, cmocka test overrides fopen() call used by nss_files.so.2 to -load /etc/passwd and /etc/group. An overridden version changes paths to -/etc/passwd and /etc/group to a local test_data/passwd and -test_data/group. This way we can continue testing a backend API for -ipa-extdom-extop with the same data as with nss_wrapper. - -Fixes https://pagure.io/freeipa/issue/5464 - -Reviewed-By: Christian Heimes -Reviewed-By: Simo Sorce -Reviewed-By: Robbie Harwood ---- - configure.ac | 25 +- - .../ipa-slapi-plugins/ipa-extdom-extop/Makefile.am | 17 +- - .../ipa-extdom-extop/back_extdom.h | 79 ++++++ - .../ipa-extdom-extop/back_extdom_nss_sss.c | 276 +++++++++++++++++++++ - .../ipa-extdom-extop/back_extdom_sss_idmap.c | 260 +++++++++++++++++++ - .../ipa-extdom-extop/ipa_extdom.h | 13 +- - .../ipa-extdom-extop/ipa_extdom_cmocka_tests.c | 241 +++++++++++++++--- - .../ipa-extdom-extop/ipa_extdom_common.c | 242 +++++++++--------- - .../ipa-extdom-extop/ipa_extdom_extop.c | 17 ++ - .../ipa-extdom-extop/test_data/test_setup.sh | 3 - - freeipa.spec.in | 1 - - server.m4 | 10 + - 12 files changed, 994 insertions(+), 190 deletions(-) - create mode 100644 daemons/ipa-slapi-plugins/ipa-extdom-extop/back_extdom.h - create mode 100644 daemons/ipa-slapi-plugins/ipa-extdom-extop/back_extdom_nss_sss.c - create mode 100644 daemons/ipa-slapi-plugins/ipa-extdom-extop/back_extdom_sss_idmap.c - delete mode 100644 daemons/ipa-slapi-plugins/ipa-extdom-extop/test_data/test_setup.sh - -diff --git a/configure.ac b/configure.ac -index e7a8b11153209fdfb4903cd3876e21a871a92f03..8a99b028886790aca211ddf164772920221f3ec7 100644 ---- a/configure.ac -+++ b/configure.ac -@@ -140,30 +140,6 @@ PKG_CHECK_EXISTS(cmocka, - ) - AM_CONDITIONAL([HAVE_CMOCKA], [test x$have_cmocka = xyes]) - --dnl A macro to check presence of a cwrap (http://cwrap.org) wrapper on the system --dnl Usage: --dnl AM_CHECK_WRAPPER(name, conditional) --dnl If the cwrap library is found, sets the HAVE_$name conditional --AC_DEFUN([AM_CHECK_WRAPPER], --[ -- FOUND_WRAPPER=0 -- -- AC_MSG_CHECKING([for $1]) -- PKG_CHECK_EXISTS([$1], -- [ -- AC_MSG_RESULT([yes]) -- FOUND_WRAPPER=1 -- ], -- [ -- AC_MSG_RESULT([no]) -- AC_MSG_WARN([cwrap library $1 not found, some tests will not run]) -- ]) -- -- AM_CONDITIONAL($2, [ test x$FOUND_WRAPPER = x1]) --]) -- --AM_CHECK_WRAPPER(nss_wrapper, HAVE_NSS_WRAPPER) -- - dnl --------------------------------------------------------------------------- - dnl - Check for POPT - dnl --------------------------------------------------------------------------- -@@ -235,6 +211,7 @@ dnl --------------------------------------------------------------------------- - AM_COND_IF([ENABLE_SERVER], [ - m4_include(server.m4) - ]) -+AM_CONDITIONAL([USE_SSS_NSS_TIMEOUT], [test "x$ac_cv_have_decl_sss_nss_getpwnam_timeout" = xyes]) - - dnl --------------------------------------------------------------------------- - dnl - Check if IPA certauth plugin can be build -diff --git a/daemons/ipa-slapi-plugins/ipa-extdom-extop/Makefile.am b/daemons/ipa-slapi-plugins/ipa-extdom-extop/Makefile.am -index 1213965c96607bf14c6c92ce592585aed1a125db..cbdd570eabeb12b95fdc26213a64749f9ba9fdde 100644 ---- a/daemons/ipa-slapi-plugins/ipa-extdom-extop/Makefile.am -+++ b/daemons/ipa-slapi-plugins/ipa-extdom-extop/Makefile.am -@@ -25,6 +25,7 @@ libipa_extdom_extop_la_SOURCES = \ - ipa_extdom.h \ - ipa_extdom_extop.c \ - ipa_extdom_common.c \ -+ back_extdom.h \ - $(NULL) - - libipa_extdom_extop_la_LDFLAGS = -avoid-version -@@ -34,20 +35,29 @@ libipa_extdom_extop_la_LIBADD = \ - $(SSSNSSIDMAP_LIBS) \ - $(NULL) - -+# We have two backends for nss operations: -+# (1) directly loading nss_sss.so.2 -+# (2) using timeout-enabled API from libsss_nss_idmap -+# We prefer (2) if available -+if USE_SSS_NSS_TIMEOUT -+libipa_extdom_extop_la_SOURCES += back_extdom_sss_idmap.c -+else -+libipa_extdom_extop_la_SOURCES += back_extdom_nss_sss.c -+endif -+ -+ - TESTS = - check_PROGRAMS = - - if HAVE_CMOCKA --if HAVE_NSS_WRAPPER --TESTS_ENVIRONMENT = . ./test_data/test_setup.sh; - TESTS += extdom_cmocka_tests - check_PROGRAMS += extdom_cmocka_tests - endif --endif - - extdom_cmocka_tests_SOURCES = \ - ipa_extdom_cmocka_tests.c \ - ipa_extdom_common.c \ -+ back_extdom_nss_sss.c \ - $(NULL) - extdom_cmocka_tests_CFLAGS = $(CMOCKA_CFLAGS) - extdom_cmocka_tests_LDFLAGS = \ -@@ -58,6 +68,7 @@ extdom_cmocka_tests_LDADD = \ - $(LDAP_LIBS) \ - $(DIRSRV_LIBS) \ - $(SSSNSSIDMAP_LIBS) \ -+ -ldl \ - $(NULL) - - -diff --git a/daemons/ipa-slapi-plugins/ipa-extdom-extop/back_extdom.h b/daemons/ipa-slapi-plugins/ipa-extdom-extop/back_extdom.h -new file mode 100644 -index 0000000000000000000000000000000000000000..d2937c8c8ecf8b960b5b31e9449c719bfda86de4 ---- /dev/null -+++ b/daemons/ipa-slapi-plugins/ipa-extdom-extop/back_extdom.h -@@ -0,0 +1,79 @@ -+/* -+ * Copyright 2017 Red Hat, Inc. -+ * -+ * This Program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License as published by -+ * the Free Software Foundation; version 2 of the License. -+ * -+ * This Program is distributed in the hope that it will be useful, but -+ * WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -+ * General Public License for more details. -+ * -+ * You should have received a copy of the GNU General Public License -+ * along with this Program; if not, write to the -+ * -+ * Free Software Foundation, Inc. -+ * 59 Temple Place, Suite 330 -+ * Boston, MA 02111-1307 USA -+ * -+ */ -+ -+#ifndef BACK_EXTDOM_H -+#define BACK_EXTDOM_H -+#include -+#include -+#include -+ -+/* Possible results of lookup using a nss_* function. -+ * Note: don't include nss.h as its path gets overriden by NSS library */ -+enum nss_status { -+ NSS_STATUS_TRYAGAIN = -2, -+ NSS_STATUS_UNAVAIL, -+ NSS_STATUS_NOTFOUND, -+ NSS_STATUS_SUCCESS, -+ NSS_STATUS_RETURN -+}; -+ -+/* NSS backend operations implemented using either nss_sss.so.2 or libsss_nss_idmap API */ -+struct nss_ops_ctx; -+ -+int back_extdom_init_context(struct nss_ops_ctx **nss_context); -+void back_extdom_free_context(struct nss_ops_ctx **nss_context); -+void back_extdom_set_timeout(struct nss_ops_ctx *nss_context, -+ unsigned int timeout); -+void back_extdom_evict_user(struct nss_ops_ctx *nss_context, -+ const char *name); -+void back_extdom_evict_group(struct nss_ops_ctx *nss_context, -+ const char *name); -+ -+enum nss_status back_extdom_getpwnam(struct nss_ops_ctx *nss_context, -+ const char *name, struct passwd *pwd, -+ char *buffer, size_t buflen, -+ struct passwd **result, -+ int *lerrno); -+ -+enum nss_status back_extdom_getpwuid(struct nss_ops_ctx *nss_context, -+ uid_t uid, struct passwd *pwd, -+ char *buffer, size_t buflen, -+ struct passwd **result, -+ int *lerrno); -+ -+enum nss_status back_extdom_getgrnam(struct nss_ops_ctx *nss_context, -+ const char *name, struct group *grp, -+ char *buffer, size_t buflen, -+ struct group **result, -+ int *lerrno); -+ -+enum nss_status back_extdom_getgrgid(struct nss_ops_ctx *nss_context, -+ gid_t gid, struct group *grp, -+ char *buffer, size_t buflen, -+ struct group **result, -+ int *lerrno); -+ -+enum nss_status back_extdom_getgrouplist(struct nss_ops_ctx *nss_context, -+ const char *name, gid_t group, -+ gid_t *groups, int *ngroups, -+ int *lerrno); -+ -+#endif /* BACK_EXTDOM_H */ -diff --git a/daemons/ipa-slapi-plugins/ipa-extdom-extop/back_extdom_nss_sss.c b/daemons/ipa-slapi-plugins/ipa-extdom-extop/back_extdom_nss_sss.c -new file mode 100644 -index 0000000000000000000000000000000000000000..346c7d4301a607c7bc07ca5a9c53fe84618ac8ad ---- /dev/null -+++ b/daemons/ipa-slapi-plugins/ipa-extdom-extop/back_extdom_nss_sss.c -@@ -0,0 +1,276 @@ -+/* -+ * Copyright 2013-2017 Red Hat, Inc. -+ * -+ * This Program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License as published by -+ * the Free Software Foundation; version 2 of the License. -+ * -+ * This Program is distributed in the hope that it will be useful, but -+ * WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -+ * General Public License for more details. -+ * -+ * You should have received a copy of the GNU General Public License -+ * along with this Program; if not, write to the -+ * -+ * Free Software Foundation, Inc. -+ * 59 Temple Place, Suite 330 -+ * Boston, MA 02111-1307 USA -+ * -+ */ -+ -+#ifdef HAVE_CONFIG_H -+#include "config.h" -+#endif -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include "back_extdom.h" -+ -+struct nss_ops_ctx { -+ void *dl_handle; -+ long int initgroups_start; -+ -+ enum nss_status (*getpwnam_r)(const char *name, struct passwd *result, -+ char *buffer, size_t buflen, int *errnop); -+ enum nss_status (*getpwuid_r)(uid_t uid, struct passwd *result, -+ char *buffer, size_t buflen, int *errnop); -+ enum nss_status (*getgrnam_r)(const char *name, struct group *result, -+ char *buffer, size_t buflen, int *errnop); -+ enum nss_status (*getgrgid_r)(gid_t gid, struct group *result, -+ char *buffer, size_t buflen, int *errnop); -+ enum nss_status (*initgroups_dyn)(const char *user, gid_t group, -+ long int *start, long int *size, -+ gid_t **groups, long int limit, -+ int *errnop); -+}; -+ -+void back_extdom_free_context(struct nss_ops_ctx **nss_context) -+{ -+ if ((nss_context == NULL) || (*nss_context == NULL)) { -+ return; -+ } -+ -+ if ((*nss_context)->dl_handle != NULL) { -+ dlclose((*nss_context)->dl_handle); -+ } -+ -+ free((*nss_context)); -+ *nss_context = NULL; -+} -+ -+int back_extdom_init_context(struct nss_ops_ctx **nss_context) -+{ -+ struct nss_ops_ctx *ctx = NULL; -+ -+ if (nss_context == NULL) { -+ return EINVAL; -+ } -+ -+ ctx = calloc(1, sizeof(struct nss_ops_ctx)); -+ if (ctx == NULL) { -+ return ENOMEM; -+ } -+ *nss_context = ctx; -+ -+ ctx->dl_handle = dlopen("libnss_sss.so.2", RTLD_NOW); -+ if (ctx->dl_handle == NULL) { -+ goto fail; -+ } -+ -+ ctx->getpwnam_r = dlsym(ctx->dl_handle, "_nss_sss_getpwnam_r"); -+ if (ctx->getpwnam_r == NULL) { -+ goto fail; -+ } -+ -+ ctx->getpwuid_r = dlsym(ctx->dl_handle, "_nss_sss_getpwuid_r"); -+ if (ctx->getpwuid_r == NULL) { -+ goto fail; -+ } -+ -+ ctx->getgrnam_r = dlsym(ctx->dl_handle, "_nss_sss_getgrnam_r"); -+ if (ctx->getgrnam_r == NULL) { -+ goto fail; -+ } -+ -+ ctx->getgrgid_r = dlsym(ctx->dl_handle, "_nss_sss_getgrgid_r"); -+ if (ctx->getgrgid_r == NULL) { -+ goto fail; -+ } -+ -+ ctx->initgroups_dyn = dlsym(ctx->dl_handle, "_nss_sss_initgroups_dyn"); -+ if (ctx->initgroups_dyn == NULL) { -+ goto fail; -+ } -+ -+ return 0; -+ -+fail: -+ back_extdom_free_context(nss_context); -+ -+ return EINVAL; -+} -+ -+ -+/* Following three functions cannot be implemented with nss_sss.so.2 -+ * As result, we simply do nothing here */ -+ -+void back_extdom_set_timeout(struct nss_ops_ctx *nss_context, -+ unsigned int timeout) { -+ /* no operation */ -+} -+ -+void back_extdom_evict_user(struct nss_ops_ctx *nss_context, -+ const char *name) { -+ /* no operation */ -+} -+ -+void back_extdom_evict_group(struct nss_ops_ctx *nss_context, -+ const char *name) { -+ /* no operation */ -+} -+ -+enum nss_status back_extdom_getpwnam(struct nss_ops_ctx *nss_context, -+ const char *name, struct passwd *pwd, -+ char *buffer, size_t buflen, -+ struct passwd **result, -+ int *lerrno) { -+ enum nss_status ret; -+ -+ if (nss_context == NULL) { -+ return NSS_STATUS_UNAVAIL; -+ } -+ -+ ret = nss_context->getpwnam_r(name, pwd, -+ buffer, buflen, -+ lerrno); -+ -+ if ((ret == NSS_STATUS_SUCCESS) && (result != NULL)) { -+ *result = pwd; -+ *lerrno = 0; -+ } -+ -+ return ret; -+} -+ -+enum nss_status back_extdom_getpwuid(struct nss_ops_ctx *nss_context, -+ uid_t uid, struct passwd *pwd, -+ char *buffer, size_t buflen, -+ struct passwd **result, -+ int *lerrno) { -+ enum nss_status ret; -+ -+ if (nss_context == NULL) { -+ return NSS_STATUS_UNAVAIL; -+ } -+ -+ ret = nss_context->getpwuid_r(uid, pwd, -+ buffer, buflen, -+ lerrno); -+ -+ if ((ret == NSS_STATUS_SUCCESS) && (result != NULL)) { -+ *result = pwd; -+ *lerrno = 0; -+ } -+ -+ return ret; -+} -+ -+enum nss_status back_extdom_getgrnam(struct nss_ops_ctx *nss_context, -+ const char *name, struct group *grp, -+ char *buffer, size_t buflen, -+ struct group **result, -+ int *lerrno) { -+ enum nss_status ret; -+ -+ if (nss_context == NULL) { -+ return NSS_STATUS_UNAVAIL; -+ } -+ -+ ret = nss_context->getgrnam_r(name, grp, -+ buffer, buflen, -+ lerrno); -+ -+ if ((ret == NSS_STATUS_SUCCESS) && (result != NULL)) { -+ *result = grp; -+ *lerrno = 0; -+ } -+ -+ return ret; -+} -+ -+enum nss_status back_extdom_getgrgid(struct nss_ops_ctx *nss_context, -+ gid_t gid, struct group *grp, -+ char *buffer, size_t buflen, -+ struct group **result, -+ int *lerrno) { -+ -+ enum nss_status ret; -+ -+ if (nss_context == NULL) { -+ return NSS_STATUS_UNAVAIL; -+ } -+ -+ ret = nss_context->getgrgid_r(gid, grp, -+ buffer, buflen, -+ lerrno); -+ -+ if ((ret == NSS_STATUS_SUCCESS) && (result != NULL)) { -+ *result = grp; -+ *lerrno = 0; -+ } -+ -+ return ret; -+} -+ -+enum nss_status back_extdom_getgrouplist(struct nss_ops_ctx *nss_context, -+ const char *name, gid_t group, -+ gid_t *groups, int *ngroups, -+ int *lerrno) { -+ -+ enum nss_status ret = NSS_STATUS_UNAVAIL; -+ long int tsize = MAX (1, *ngroups); -+ gid_t *newgroups = NULL; -+ -+ if (nss_context == NULL) { -+ return NSS_STATUS_UNAVAIL; -+ } -+ -+ newgroups = (gid_t *) calloc (tsize, sizeof (gid_t)); -+ if (newgroups == NULL) { -+ *lerrno = ENOMEM; -+ return NSS_STATUS_TRYAGAIN; -+ } -+ -+ newgroups[0] = group; -+ nss_context->initgroups_start = 1; -+ -+ ret = nss_context->initgroups_dyn(name, group, -+ &nss_context->initgroups_start, -+ &tsize, &newgroups, -+ -1, lerrno); -+ -+ (void) memcpy(groups, newgroups, -+ MIN(*ngroups, nss_context->initgroups_start) * sizeof(gid_t)); -+ free(newgroups); -+ -+ if (*ngroups < nss_context->initgroups_start) { -+ ret = NSS_STATUS_TRYAGAIN; -+ *lerrno = ERANGE; -+ } -+ -+ *ngroups = (int) nss_context->initgroups_start; -+ -+ nss_context->initgroups_start = 0; -+ -+ return ret; -+} -+ -diff --git a/daemons/ipa-slapi-plugins/ipa-extdom-extop/back_extdom_sss_idmap.c b/daemons/ipa-slapi-plugins/ipa-extdom-extop/back_extdom_sss_idmap.c -new file mode 100644 -index 0000000000000000000000000000000000000000..89c58ca2de333b26954d916836b57aed5d7e18fb ---- /dev/null -+++ b/daemons/ipa-slapi-plugins/ipa-extdom-extop/back_extdom_sss_idmap.c -@@ -0,0 +1,260 @@ -+/* -+ * Copyright 2013-2017 Red Hat, Inc. -+ * -+ * This Program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License as published by -+ * the Free Software Foundation; version 2 of the License. -+ * -+ * This Program is distributed in the hope that it will be useful, but -+ * WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -+ * General Public License for more details. -+ * -+ * You should have received a copy of the GNU General Public License -+ * along with this Program; if not, write to the -+ * -+ * Free Software Foundation, Inc. -+ * 59 Temple Place, Suite 330 -+ * Boston, MA 02111-1307 USA -+ * -+ */ -+ -+#ifdef HAVE_CONFIG_H -+#include "config.h" -+#endif -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include "back_extdom.h" -+ -+/* SSSD only exposes *_timeout() variants if the following symbol is defined */ -+#define IPA_389DS_PLUGIN_HELPER_CALLS -+#include -+ -+struct nss_ops_ctx { -+ unsigned int timeout; -+}; -+ -+static enum nss_status __convert_sss_nss2nss_status(int errcode) { -+ switch(errcode) { -+ case 0: -+ return NSS_STATUS_SUCCESS; -+ case ENOENT: -+ return NSS_STATUS_NOTFOUND; -+ case ETIME: -+ /* fall-through */ -+ case ERANGE: -+ return NSS_STATUS_TRYAGAIN; -+ case ETIMEDOUT: -+ /* fall-through */ -+ default: -+ return NSS_STATUS_UNAVAIL; -+ } -+ return NSS_STATUS_UNAVAIL; -+} -+ -+int back_extdom_init_context(struct nss_ops_ctx **nss_context) -+{ -+ struct nss_ops_ctx *ctx = NULL; -+ -+ if (nss_context == NULL) { -+ return EINVAL; -+ } -+ -+ ctx = calloc(1, sizeof(struct nss_ops_ctx)); -+ -+ if (ctx == NULL) { -+ return ENOMEM; -+ } -+ *nss_context = ctx; -+ return 0; -+} -+ -+void back_extdom_free_context(struct nss_ops_ctx **nss_context) -+{ -+ if ((nss_context == NULL) || (*nss_context == NULL)) { -+ return; -+ } -+ -+ free((*nss_context)); -+ *nss_context = NULL; -+} -+ -+ -+void back_extdom_set_timeout(struct nss_ops_ctx *nss_context, -+ unsigned int timeout) { -+ if (nss_context == NULL) { -+ return; -+ } -+ -+ nss_context->timeout = timeout; -+} -+ -+void back_extdom_evict_user(struct nss_ops_ctx *nss_context, -+ const char *name) { -+ if (nss_context == NULL) { -+ return; -+ } -+ -+ (void) sss_nss_getpwnam_timeout(name, NULL, -+ NULL, 0, -+ NULL, -+ SSS_NSS_EX_FLAG_INVALIDATE_CACHE, -+ nss_context->timeout); -+} -+ -+void back_extdom_evict_group(struct nss_ops_ctx *nss_context, -+ const char *name) { -+ if (nss_context == NULL) { -+ return; -+ } -+ -+ (void) sss_nss_getgrnam_timeout(name, NULL, -+ NULL, 0, -+ NULL, -+ SSS_NSS_EX_FLAG_INVALIDATE_CACHE, -+ nss_context->timeout); -+} -+ -+enum nss_status back_extdom_getpwnam(struct nss_ops_ctx *nss_context, -+ const char *name, struct passwd *pwd, -+ char *buffer, size_t buflen, -+ struct passwd **result, -+ int *lerrno) { -+ int ret = 0; -+ -+ if (nss_context == NULL) { -+ return NSS_STATUS_UNAVAIL; -+ } -+ -+ ret = sss_nss_getpwnam_timeout(name, pwd, -+ buffer, buflen, -+ result, -+ SSS_NSS_EX_FLAG_NO_FLAGS, -+ nss_context->timeout); -+ -+ /* SSSD uses the same infrastructure to handle sss_nss_get* calls -+ * as nss_sss.so.2 module where 'int *errno' is passed to the helper -+ * but writes down errno into return code so we propagate it in case -+ * of error and translate the return code */ -+ if (lerrno != NULL) { -+ *lerrno = ret; -+ } -+ return __convert_sss_nss2nss_status(ret); -+} -+ -+enum nss_status back_extdom_getpwuid(struct nss_ops_ctx *nss_context, -+ uid_t uid, struct passwd *pwd, -+ char *buffer, size_t buflen, -+ struct passwd **result, -+ int *lerrno) { -+ -+ int ret = 0; -+ -+ if (nss_context == NULL) { -+ return NSS_STATUS_UNAVAIL; -+ } -+ -+ ret = sss_nss_getpwuid_timeout(uid, pwd, -+ buffer, buflen, -+ result, -+ SSS_NSS_EX_FLAG_NO_FLAGS, -+ nss_context->timeout); -+ -+ /* SSSD uses the same infrastructure to handle sss_nss_get* calls -+ * as nss_sss.so.2 module where 'int *errno' is passed to the helper -+ * but writes down errno into return code so we propagate it in case -+ * of error and translate the return code */ -+ if (lerrno != NULL) { -+ *lerrno = ret; -+ } -+ return __convert_sss_nss2nss_status(ret); -+} -+ -+enum nss_status back_extdom_getgrnam(struct nss_ops_ctx *nss_context, -+ const char *name, struct group *grp, -+ char *buffer, size_t buflen, -+ struct group **result, -+ int *lerrno) { -+ -+ int ret = 0; -+ -+ if (nss_context == NULL) { -+ return NSS_STATUS_UNAVAIL; -+ } -+ -+ ret = sss_nss_getgrnam_timeout(name, grp, -+ buffer, buflen, -+ result, -+ SSS_NSS_EX_FLAG_NO_FLAGS, -+ nss_context->timeout); -+ -+ /* SSSD uses the same infrastructure to handle sss_nss_get* calls -+ * as nss_sss.so.2 module where 'int *errno' is passed to the helper -+ * but writes down errno into return code so we propagate it in case -+ * of error and translate the return code */ -+ if (lerrno != NULL) { -+ *lerrno = ret; -+ } -+ return __convert_sss_nss2nss_status(ret); -+} -+ -+enum nss_status back_extdom_getgrgid(struct nss_ops_ctx *nss_context, -+ gid_t gid, struct group *grp, -+ char *buffer, size_t buflen, -+ struct group **result, -+ int *lerrno) { -+ -+ int ret = 0; -+ -+ if (nss_context == NULL) { -+ return NSS_STATUS_UNAVAIL; -+ } -+ -+ ret = sss_nss_getgrgid_timeout(gid, grp, -+ buffer, buflen, -+ result, -+ SSS_NSS_EX_FLAG_NO_FLAGS, -+ nss_context->timeout); -+ -+ /* SSSD uses the same infrastructure to handle sss_nss_get* calls -+ * as nss_sss.so.2 module where 'int *errno' is passed to the helper -+ * but writes down errno into return code so we propagate it in case -+ * of error and translate the return code */ -+ if (lerrno != NULL) { -+ *lerrno = ret; -+ } -+ return __convert_sss_nss2nss_status(ret); -+} -+ -+enum nss_status back_extdom_getgrouplist(struct nss_ops_ctx *nss_context, -+ const char *name, gid_t group, -+ gid_t *groups, int *ngroups, -+ int *lerrno) { -+ int ret = 0; -+ -+ if (nss_context == NULL) { -+ return NSS_STATUS_UNAVAIL; -+ } -+ -+ ret = sss_nss_getgrouplist_timeout(name, group, -+ groups, ngroups, -+ SSS_NSS_EX_FLAG_NO_FLAGS, -+ nss_context->timeout); -+ -+ /* SSSD uses the same infrastructure to handle sss_nss_get* calls -+ * as nss_sss.so.2 module where 'int *errno' is passed to the helper -+ * but writes down errno into return code so we propagate it in case -+ * of error and translate the return code */ -+ if (lerrno != NULL) { -+ *lerrno = ret; -+ } -+ return __convert_sss_nss2nss_status(ret); -+} -+ -diff --git a/daemons/ipa-slapi-plugins/ipa-extdom-extop/ipa_extdom.h b/daemons/ipa-slapi-plugins/ipa-extdom-extop/ipa_extdom.h -index bc29f069816b0ce13578c9ae14c61edb832d44e4..bbc574747e8bbe045dfc9882198cb34b0bb8cab9 100644 ---- a/daemons/ipa-slapi-plugins/ipa-extdom-extop/ipa_extdom.h -+++ b/daemons/ipa-slapi-plugins/ipa-extdom-extop/ipa_extdom.h -@@ -150,10 +150,13 @@ struct extdom_res { - } data; - }; - -+struct nss_ops_ctx; -+ - struct ipa_extdom_ctx { - Slapi_ComponentId *plugin_id; - char *base_dn; - size_t max_nss_buf_size; -+ struct nss_ops_ctx *nss_ctx; - }; - - struct domain_info { -@@ -179,15 +182,15 @@ int handle_request(struct ipa_extdom_ctx *ctx, struct extdom_req *req, - struct berval **berval); - int pack_response(struct extdom_res *res, struct berval **ret_val); - int get_buffer(size_t *_buf_len, char **_buf); --int getpwnam_r_wrapper(size_t buf_max, const char *name, -+int getpwnam_r_wrapper(struct ipa_extdom_ctx *ctx, const char *name, - struct passwd *pwd, char **_buf, size_t *_buf_len); --int getpwuid_r_wrapper(size_t buf_max, uid_t uid, -+int getpwuid_r_wrapper(struct ipa_extdom_ctx *ctx, uid_t uid, - struct passwd *pwd, char **_buf, size_t *_buf_len); --int getgrnam_r_wrapper(size_t buf_max, const char *name, -+int getgrnam_r_wrapper(struct ipa_extdom_ctx *ctx, const char *name, - struct group *grp, char **_buf, size_t *_buf_len); --int getgrgid_r_wrapper(size_t buf_max, gid_t gid, -+int getgrgid_r_wrapper(struct ipa_extdom_ctx *ctx, gid_t gid, - struct group *grp, char **_buf, size_t *_buf_len); --int get_user_grouplist(const char *name, gid_t gid, -+int get_user_grouplist(struct ipa_extdom_ctx *ctx, const char *name, gid_t gid, - size_t *_ngroups, gid_t **_groups); - int pack_ber_sid(const char *sid, struct berval **berval); - int pack_ber_name(const char *domain_name, const char *name, -diff --git a/daemons/ipa-slapi-plugins/ipa-extdom-extop/ipa_extdom_cmocka_tests.c b/daemons/ipa-slapi-plugins/ipa-extdom-extop/ipa_extdom_cmocka_tests.c -index 526f903d2416e62ee5781909c234bd5ee2d89183..29699cfa390f5469d7c009388b90e68616cbf984 100644 ---- a/daemons/ipa-slapi-plugins/ipa-extdom-extop/ipa_extdom_cmocka_tests.c -+++ b/daemons/ipa-slapi-plugins/ipa-extdom-extop/ipa_extdom_cmocka_tests.c -@@ -19,6 +19,7 @@ - You should have received a copy of the GNU General Public License - along with this program. If not, see . - */ -+#define _GNU_SOURCE - - #include - #include -@@ -31,24 +32,166 @@ - - - #include "ipa_extdom.h" -+#include "back_extdom.h" -+#include -+#include - - #define MAX_BUF (1024*1024*1024) -+struct test_data { -+ struct extdom_req *req; -+ struct ipa_extdom_ctx *ctx; -+}; -+ -+/* -+ * redefine logging for mocks -+ */ -+#ifdef __GNUC__ -+ __attribute__((format(printf, 3, 4))) -+#endif -+int slapi_log_error(int loglevel, char *subsystem, char *fmt, ...) -+{ -+ va_list ap; -+ va_start(ap, fmt); -+ vprint_error(fmt, ap); -+ va_end(ap); -+ return 0; -+} -+ -+ -+/* -+ * We cannot run cmocka tests against SSSD as that would require to set up SSSD -+ * and the rest of environment. Instead, we compile cmocka tests against -+ * back_extdom_nss_sss.c and re-define context initialization to use -+ * nsswrapper with our test data. -+ * -+ * This means we have to keep struct nss_ops_ctx definition in sync with tests! -+ */ -+ -+struct nss_ops_ctx { -+ void *dl_handle; -+ long int initgroups_start; -+ -+ enum nss_status (*getpwnam_r)(const char *name, struct passwd *result, -+ char *buffer, size_t buflen, int *errnop); -+ enum nss_status (*getpwuid_r)(uid_t uid, struct passwd *result, -+ char *buffer, size_t buflen, int *errnop); -+ enum nss_status (*getgrnam_r)(const char *name, struct group *result, -+ char *buffer, size_t buflen, int *errnop); -+ enum nss_status (*getgrgid_r)(gid_t gid, struct group *result, -+ char *buffer, size_t buflen, int *errnop); -+ enum nss_status (*initgroups_dyn)(const char *user, gid_t group, -+ long int *start, long int *size, -+ gid_t **groups, long int limit, -+ int *errnop); -+}; -+ -+int cmocka_extdom_init_context(struct nss_ops_ctx **nss_context) -+{ -+ struct nss_ops_ctx *ctx = NULL; -+ -+ if (nss_context == NULL) { -+ return -1; -+ } -+ -+ ctx = calloc(1, sizeof(struct nss_ops_ctx)); -+ -+ if (ctx == NULL) { -+ return ENOMEM; -+ } -+ *nss_context = ctx; -+ -+ ctx->dl_handle = dlopen("libnss_files.so.2", RTLD_NOW); -+ if (ctx->dl_handle == NULL) { -+ goto fail; -+ } -+ -+ ctx->getpwnam_r = dlsym(ctx->dl_handle, "_nss_files_getpwnam_r"); -+ if (ctx->getpwnam_r == NULL) { -+ goto fail; -+ } -+ -+ ctx->getpwuid_r = dlsym(ctx->dl_handle, "_nss_files_getpwuid_r"); -+ if (ctx->getpwuid_r == NULL) { -+ goto fail; -+ } -+ -+ ctx->getgrnam_r = dlsym(ctx->dl_handle, "_nss_files_getgrnam_r"); -+ if (ctx->getgrnam_r == NULL) { -+ goto fail; -+ } -+ -+ ctx->getgrgid_r = dlsym(ctx->dl_handle, "_nss_files_getgrgid_r"); -+ if (ctx->getgrgid_r == NULL) { -+ goto fail; -+ } -+ -+ ctx->initgroups_dyn = dlsym(ctx->dl_handle, "_nss_files_initgroups_dyn"); -+ if (ctx->initgroups_dyn == NULL) { -+ goto fail; -+ } -+ -+ return 0; -+ -+fail: -+ back_extdom_free_context(nss_context); -+ -+ return -1; -+} -+ -+struct { -+ const char *o, *n; -+} path_table[] = { -+ { .o = "/etc/passwd", .n = "./test_data/passwd"}, -+ { .o = "/etc/group", .n = "./test_data/group"}, -+ { .o = NULL, .n = NULL}}; -+ -+FILE *(*original_fopen)(const char*, const char*) = NULL; -+ -+FILE *fopen(const char *path, const char *mode) { -+ const char *_path = NULL; -+ -+ /* Do not handle before-main() cases */ -+ if (original_fopen == NULL) { -+ return NULL; -+ } -+ for(int i=0; path_table[i].o != NULL; i++) { -+ if (strcmp(path, path_table[i].o) == 0) { -+ _path = path_table[i].n; -+ break; -+ } -+ } -+ return (*original_fopen)(_path ? _path : path, mode); -+} -+ -+/* Attempt to initialize original_fopen before main() -+ * There is no explicit order when all initializers are called, -+ * so we might still be late here compared to a code in a shared -+ * library initializer, like libselinux */ -+void redefined_fopen_ctor (void) __attribute__ ((constructor)); -+void redefined_fopen_ctor(void) { -+ original_fopen = dlsym(RTLD_NEXT, "fopen"); -+} - - void test_getpwnam_r_wrapper(void **state) - { - int ret; - struct passwd pwd; - char *buf; -- size_t buf_len; -+ size_t buf_len, max_big_buf_len; -+ struct test_data *test_data; -+ -+ test_data = (struct test_data *) *state; - - ret = get_buffer(&buf_len, &buf); - assert_int_equal(ret, 0); - -- ret = getpwnam_r_wrapper(MAX_BUF, "non_exisiting_user", &pwd, &buf, -- &buf_len); -+ ret = getpwnam_r_wrapper(test_data->ctx, -+ "non_exisiting_user", &pwd, -+ &buf, &buf_len); - assert_int_equal(ret, ENOENT); - -- ret = getpwnam_r_wrapper(MAX_BUF, "user", &pwd, &buf, &buf_len); -+ ret = getpwnam_r_wrapper(test_data->ctx, -+ "user", &pwd, &buf, &buf_len); - assert_int_equal(ret, 0); - assert_string_equal(pwd.pw_name, "user"); - assert_string_equal(pwd.pw_passwd, "x"); -@@ -62,7 +205,8 @@ void test_getpwnam_r_wrapper(void **state) - ret = get_buffer(&buf_len, &buf); - assert_int_equal(ret, 0); - -- ret = getpwnam_r_wrapper(MAX_BUF, "user_big", &pwd, &buf, &buf_len); -+ ret = getpwnam_r_wrapper(test_data->ctx, -+ "user_big", &pwd, &buf, &buf_len); - assert_int_equal(ret, 0); - assert_string_equal(pwd.pw_name, "user_big"); - assert_string_equal(pwd.pw_passwd, "x"); -@@ -76,7 +220,11 @@ void test_getpwnam_r_wrapper(void **state) - ret = get_buffer(&buf_len, &buf); - assert_int_equal(ret, 0); - -- ret = getpwnam_r_wrapper(1024, "user_big", &pwd, &buf, &buf_len); -+ max_big_buf_len = test_data->ctx->max_nss_buf_size; -+ test_data->ctx->max_nss_buf_size = 1024; -+ ret = getpwnam_r_wrapper(test_data->ctx, -+ "user_big", &pwd, &buf, &buf_len); -+ test_data->ctx->max_nss_buf_size = max_big_buf_len; - assert_int_equal(ret, ERANGE); - free(buf); - } -@@ -86,15 +234,18 @@ void test_getpwuid_r_wrapper(void **state) - int ret; - struct passwd pwd; - char *buf; -- size_t buf_len; -+ size_t buf_len, max_big_buf_len; -+ struct test_data *test_data; -+ -+ test_data = (struct test_data *) *state; - - ret = get_buffer(&buf_len, &buf); - assert_int_equal(ret, 0); - -- ret = getpwuid_r_wrapper(MAX_BUF, 99999, &pwd, &buf, &buf_len); -+ ret = getpwuid_r_wrapper(test_data->ctx, 99999, &pwd, &buf, &buf_len); - assert_int_equal(ret, ENOENT); - -- ret = getpwuid_r_wrapper(MAX_BUF, 12345, &pwd, &buf, &buf_len); -+ ret = getpwuid_r_wrapper(test_data->ctx, 12345, &pwd, &buf, &buf_len); - assert_int_equal(ret, 0); - assert_string_equal(pwd.pw_name, "user"); - assert_string_equal(pwd.pw_passwd, "x"); -@@ -108,7 +259,7 @@ void test_getpwuid_r_wrapper(void **state) - ret = get_buffer(&buf_len, &buf); - assert_int_equal(ret, 0); - -- ret = getpwuid_r_wrapper(MAX_BUF, 12346, &pwd, &buf, &buf_len); -+ ret = getpwuid_r_wrapper(test_data->ctx, 12346, &pwd, &buf, &buf_len); - assert_int_equal(ret, 0); - assert_string_equal(pwd.pw_name, "user_big"); - assert_string_equal(pwd.pw_passwd, "x"); -@@ -122,7 +273,10 @@ void test_getpwuid_r_wrapper(void **state) - ret = get_buffer(&buf_len, &buf); - assert_int_equal(ret, 0); - -- ret = getpwuid_r_wrapper(1024, 12346, &pwd, &buf, &buf_len); -+ max_big_buf_len = test_data->ctx->max_nss_buf_size; -+ test_data->ctx->max_nss_buf_size = 1024; -+ ret = getpwuid_r_wrapper(test_data->ctx, 12346, &pwd, &buf, &buf_len); -+ test_data->ctx->max_nss_buf_size = max_big_buf_len; - assert_int_equal(ret, ERANGE); - free(buf); - } -@@ -132,15 +286,19 @@ void test_getgrnam_r_wrapper(void **state) - int ret; - struct group grp; - char *buf; -- size_t buf_len; -+ size_t buf_len, max_big_buf_len; -+ struct test_data *test_data; -+ -+ test_data = (struct test_data *) *state; - - ret = get_buffer(&buf_len, &buf); - assert_int_equal(ret, 0); - -- ret = getgrnam_r_wrapper(MAX_BUF, "non_exisiting_group", &grp, &buf, &buf_len); -+ ret = getgrnam_r_wrapper(test_data->ctx, -+ "non_exisiting_group", &grp, &buf, &buf_len); - assert_int_equal(ret, ENOENT); - -- ret = getgrnam_r_wrapper(MAX_BUF, "group", &grp, &buf, &buf_len); -+ ret = getgrnam_r_wrapper(test_data->ctx, "group", &grp, &buf, &buf_len); - assert_int_equal(ret, 0); - assert_string_equal(grp.gr_name, "group"); - assert_string_equal(grp.gr_passwd, "x"); -@@ -153,7 +311,7 @@ void test_getgrnam_r_wrapper(void **state) - ret = get_buffer(&buf_len, &buf); - assert_int_equal(ret, 0); - -- ret = getgrnam_r_wrapper(MAX_BUF, "group_big", &grp, &buf, &buf_len); -+ ret = getgrnam_r_wrapper(test_data->ctx, "group_big", &grp, &buf, &buf_len); - assert_int_equal(ret, 0); - assert_string_equal(grp.gr_name, "group_big"); - assert_string_equal(grp.gr_passwd, "x"); -@@ -165,7 +323,10 @@ void test_getgrnam_r_wrapper(void **state) - ret = get_buffer(&buf_len, &buf); - assert_int_equal(ret, 0); - -- ret = getgrnam_r_wrapper(1024, "group_big", &grp, &buf, &buf_len); -+ max_big_buf_len = test_data->ctx->max_nss_buf_size; -+ test_data->ctx->max_nss_buf_size = 1024; -+ ret = getgrnam_r_wrapper(test_data->ctx, "group_big", &grp, &buf, &buf_len); -+ test_data->ctx->max_nss_buf_size = max_big_buf_len; - assert_int_equal(ret, ERANGE); - free(buf); - } -@@ -175,15 +336,18 @@ void test_getgrgid_r_wrapper(void **state) - int ret; - struct group grp; - char *buf; -- size_t buf_len; -+ size_t buf_len, max_big_buf_len; -+ struct test_data *test_data; -+ -+ test_data = (struct test_data *) *state; - - ret = get_buffer(&buf_len, &buf); - assert_int_equal(ret, 0); - -- ret = getgrgid_r_wrapper(MAX_BUF, 99999, &grp, &buf, &buf_len); -+ ret = getgrgid_r_wrapper(test_data->ctx, 99999, &grp, &buf, &buf_len); - assert_int_equal(ret, ENOENT); - -- ret = getgrgid_r_wrapper(MAX_BUF, 11111, &grp, &buf, &buf_len); -+ ret = getgrgid_r_wrapper(test_data->ctx, 11111, &grp, &buf, &buf_len); - assert_int_equal(ret, 0); - assert_string_equal(grp.gr_name, "group"); - assert_string_equal(grp.gr_passwd, "x"); -@@ -196,7 +360,7 @@ void test_getgrgid_r_wrapper(void **state) - ret = get_buffer(&buf_len, &buf); - assert_int_equal(ret, 0); - -- ret = getgrgid_r_wrapper(MAX_BUF, 22222, &grp, &buf, &buf_len); -+ ret = getgrgid_r_wrapper(test_data->ctx, 22222, &grp, &buf, &buf_len); - assert_int_equal(ret, 0); - assert_string_equal(grp.gr_name, "group_big"); - assert_string_equal(grp.gr_passwd, "x"); -@@ -208,7 +372,10 @@ void test_getgrgid_r_wrapper(void **state) - ret = get_buffer(&buf_len, &buf); - assert_int_equal(ret, 0); - -- ret = getgrgid_r_wrapper(1024, 22222, &grp, &buf, &buf_len); -+ max_big_buf_len = test_data->ctx->max_nss_buf_size; -+ test_data->ctx->max_nss_buf_size = 1024; -+ ret = getgrgid_r_wrapper(test_data->ctx, 22222, &grp, &buf, &buf_len); -+ test_data->ctx->max_nss_buf_size = max_big_buf_len; - assert_int_equal(ret, ERANGE); - free(buf); - } -@@ -219,16 +386,21 @@ void test_get_user_grouplist(void **state) - size_t ngroups; - gid_t *groups; - size_t c; -+ struct test_data *test_data; -+ -+ test_data = (struct test_data *) *state; - - /* This is a bit odd behaviour of getgrouplist() it does not check if the - * user exists, only if memberships of the user can be found. */ -- ret = get_user_grouplist("non_exisiting_user", 23456, &ngroups, &groups); -+ ret = get_user_grouplist(test_data->ctx, -+ "non_exisiting_user", 23456, &ngroups, &groups); - assert_int_equal(ret, LDAP_SUCCESS); - assert_int_equal(ngroups, 1); - assert_int_equal(groups[0], 23456); - free(groups); - -- ret = get_user_grouplist("member0001", 23456, &ngroups, &groups); -+ ret = get_user_grouplist(test_data->ctx, -+ "member0001", 23456, &ngroups, &groups); - assert_int_equal(ret, LDAP_SUCCESS); - assert_int_equal(ngroups, 3); - assert_int_equal(groups[0], 23456); -@@ -236,14 +408,16 @@ void test_get_user_grouplist(void **state) - assert_int_equal(groups[2], 22222); - free(groups); - -- ret = get_user_grouplist("member0003", 23456, &ngroups, &groups); -+ ret = get_user_grouplist(test_data->ctx, -+ "member0003", 23456, &ngroups, &groups); - assert_int_equal(ret, LDAP_SUCCESS); - assert_int_equal(ngroups, 2); - assert_int_equal(groups[0], 23456); - assert_int_equal(groups[1], 22222); - free(groups); - -- ret = get_user_grouplist("user_big", 23456, &ngroups, &groups); -+ ret = get_user_grouplist(test_data->ctx, -+ "user_big", 23456, &ngroups, &groups); - assert_int_equal(ret, LDAP_SUCCESS); - assert_int_equal(ngroups, 1001); - assert_int_equal(groups[0], 23456); -@@ -253,11 +427,6 @@ void test_get_user_grouplist(void **state) - free(groups); - } - --struct test_data { -- struct extdom_req *req; -- struct ipa_extdom_ctx *ctx; --}; -- - static int extdom_req_setup(void **state) - { - struct test_data *test_data; -@@ -269,8 +438,14 @@ static int extdom_req_setup(void **state) - assert_non_null(test_data->req); - - test_data->ctx = calloc(sizeof(struct ipa_extdom_ctx), 1); -- assert_non_null(test_data->req); -+ assert_non_null(test_data->ctx); -+ -+ test_data->ctx->max_nss_buf_size = MAX_BUF; -+ -+ assert_int_equal(cmocka_extdom_init_context(&test_data->ctx->nss_ctx), 0); -+ assert_non_null(test_data->ctx->nss_ctx); - -+ back_extdom_set_timeout(test_data->ctx->nss_ctx, 10000); - *state = test_data; - - return 0; -@@ -283,6 +458,7 @@ static int extdom_req_teardown(void **state) - test_data = (struct test_data *) *state; - - free_req_data(test_data->req); -+ back_extdom_free_context(&test_data->ctx->nss_ctx); - free(test_data->ctx); - free(test_data); - -@@ -450,5 +626,6 @@ int main(int argc, const char *argv[]) - cmocka_unit_test(test_decode), - }; - -- return cmocka_run_group_tests(tests, NULL, NULL); -+ assert_non_null(original_fopen); -+ return cmocka_run_group_tests(tests, extdom_req_setup, extdom_req_teardown); - } -diff --git a/daemons/ipa-slapi-plugins/ipa-extdom-extop/ipa_extdom_common.c b/daemons/ipa-slapi-plugins/ipa-extdom-extop/ipa_extdom_common.c -index fe225fa86669a6728bec5014be41d80275f10717..86c6638ba73c2f59aff29191e3e68dc5c85d50fc 100644 ---- a/daemons/ipa-slapi-plugins/ipa-extdom-extop/ipa_extdom_common.c -+++ b/daemons/ipa-slapi-plugins/ipa-extdom-extop/ipa_extdom_common.c -@@ -43,11 +43,12 @@ - - #include - #include -+#include - - #include "ipa_extdom.h" -+#include "back_extdom.h" - #include "util.h" - --#define MAX(a,b) (((a)>(b))?(a):(b)) - #define SSSD_DOMAIN_SEPARATOR '@' - - int get_buffer(size_t *_buf_len, char **_buf) -@@ -97,134 +98,137 @@ static int inc_buffer(size_t buf_max, size_t *_buf_len, char **_buf) - return 0; - } - --int getpwnam_r_wrapper(size_t buf_max, const char *name, -- struct passwd *pwd, char **_buf, size_t *_buf_len) -+int __nss_to_err(enum nss_status errcode) - { -- char *buf = NULL; -- size_t buf_len = 0; -- int ret; -- struct passwd *result = NULL; -+ switch(errcode) { -+ case NSS_STATUS_SUCCESS: -+ return 0; -+ case NSS_STATUS_NOTFOUND: -+ return ENOENT; -+ case NSS_STATUS_TRYAGAIN: -+ return ERANGE; -+ case NSS_STATUS_UNAVAIL: -+ return ETIMEDOUT; -+ } - -- buf = *_buf; -- buf_len = *_buf_len; -+ return -1; -+} - -- while (buf != NULL -- && (ret = getpwnam_r(name, pwd, buf, buf_len, &result)) == ERANGE) { -- ret = inc_buffer(buf_max, &buf_len, &buf); -- if (ret != 0) { -- if (ret == ERANGE) { -- LOG("Buffer too small, increase ipaExtdomMaxNssBufSize.\n"); -- } -- goto done; -+int getpwnam_r_wrapper(struct ipa_extdom_ctx *ctx, const char *name, -+ struct passwd *pwd, char **buf, size_t *buf_len) -+{ -+ int ret, lerrno = 0; -+ struct passwd *result = NULL; -+ enum nss_status rc; -+ -+ for(rc = NSS_STATUS_TRYAGAIN; rc == NSS_STATUS_TRYAGAIN;) { -+ rc = back_extdom_getpwnam(ctx->nss_ctx, name, pwd, *buf, *buf_len, &result, &lerrno); -+ ret = __nss_to_err(rc); -+ if (ret == ERANGE) { -+ ret = inc_buffer(ctx->max_nss_buf_size, buf_len, buf); -+ if (ret != 0) goto done; - } - } - -- if (ret == 0 && result == NULL) { -- ret = ENOENT; -- } -- - done: -- *_buf = buf; -- *_buf_len = buf_len; -- -+ switch(ret) { -+ case 0: -+ if (result == NULL) ret = ENOENT; -+ break; -+ case ERANGE: -+ LOG("Buffer too small, increase ipaExtdomMaxNssBufSize.\n"); -+ default: -+ break; -+ } - return ret; - } - --int getpwuid_r_wrapper(size_t buf_max, uid_t uid, -- struct passwd *pwd, char **_buf, size_t *_buf_len) -+int getpwuid_r_wrapper(struct ipa_extdom_ctx *ctx, uid_t uid, -+ struct passwd *pwd, char **buf, size_t *buf_len) - { -- char *buf = NULL; -- size_t buf_len = 0; -- int ret; -+ int ret, lerrno; - struct passwd *result = NULL; -- -- buf = *_buf; -- buf_len = *_buf_len; -- -- while (buf != NULL -- && (ret = getpwuid_r(uid, pwd, buf, buf_len, &result)) == ERANGE) { -- ret = inc_buffer(buf_max, &buf_len, &buf); -- if (ret != 0) { -- if (ret == ERANGE) { -- LOG("Buffer too small, increase ipaExtdomMaxNssBufSize.\n"); -- } -- goto done; -+ enum nss_status rc; -+ -+ for(rc = NSS_STATUS_TRYAGAIN; rc == NSS_STATUS_TRYAGAIN;) { -+ rc = back_extdom_getpwuid(ctx->nss_ctx, uid, pwd, *buf, *buf_len, &result, &lerrno); -+ ret = __nss_to_err(rc); -+ if (ret == ERANGE) { -+ ret = inc_buffer(ctx->max_nss_buf_size, buf_len, buf); -+ if (ret != 0) goto done; - } - } - -- if (ret == 0 && result == NULL) { -- ret = ENOENT; -- } -- - done: -- *_buf = buf; -- *_buf_len = buf_len; -+ switch(ret) { -+ case 0: -+ if (result == NULL) ret = ENOENT; -+ break; -+ case ERANGE: -+ LOG("Buffer too small, increase ipaExtdomMaxNssBufSize.\n"); -+ default: -+ break; -+ } - - return ret; - } - --int getgrnam_r_wrapper(size_t buf_max, const char *name, -- struct group *grp, char **_buf, size_t *_buf_len) -+int getgrnam_r_wrapper(struct ipa_extdom_ctx *ctx, const char *name, -+ struct group *grp, char **buf, size_t *buf_len) - { -- char *buf = NULL; -- size_t buf_len = 0; -- int ret; -+ int ret, lerrno; - struct group *result = NULL; -- -- buf = *_buf; -- buf_len = *_buf_len; -- -- while (buf != NULL -- && (ret = getgrnam_r(name, grp, buf, buf_len, &result)) == ERANGE) { -- ret = inc_buffer(buf_max, &buf_len, &buf); -- if (ret != 0) { -- if (ret == ERANGE) { -- LOG("Buffer too small, increase ipaExtdomMaxNssBufSize.\n"); -- } -- goto done; -+ enum nss_status rc; -+ -+ for(rc = NSS_STATUS_TRYAGAIN; rc == NSS_STATUS_TRYAGAIN;) { -+ rc = back_extdom_getgrnam(ctx->nss_ctx, name, grp, *buf, *buf_len, &result, &lerrno); -+ ret = __nss_to_err(rc); -+ if (ret == ERANGE) { -+ ret = inc_buffer(ctx->max_nss_buf_size, buf_len, buf); -+ if (ret != 0) goto done; - } - } - -- if (ret == 0 && result == NULL) { -- ret = ENOENT; -- } -- - done: -- *_buf = buf; -- *_buf_len = buf_len; -+ switch(ret) { -+ case 0: -+ if (result == NULL) ret = ENOENT; -+ break; -+ case ERANGE: -+ LOG("Buffer too small, increase ipaExtdomMaxNssBufSize.\n"); -+ default: -+ break; -+ } - - return ret; - } - --int getgrgid_r_wrapper(size_t buf_max, gid_t gid, -- struct group *grp, char **_buf, size_t *_buf_len) -+int getgrgid_r_wrapper(struct ipa_extdom_ctx *ctx, gid_t gid, -+ struct group *grp, char **buf, size_t *buf_len) - { -- char *buf = NULL; -- size_t buf_len = 0; -- int ret; -+ int ret, lerrno; - struct group *result = NULL; -- -- buf = *_buf; -- buf_len = *_buf_len; -- -- while (buf != NULL -- && (ret = getgrgid_r(gid, grp, buf, buf_len, &result)) == ERANGE) { -- ret = inc_buffer(buf_max, &buf_len, &buf); -- if (ret != 0) { -- if (ret == ERANGE) { -- LOG("Buffer too small, increase ipaExtdomMaxNssBufSize.\n"); -- } -- goto done; -+ enum nss_status rc; -+ -+ for(rc = NSS_STATUS_TRYAGAIN; rc == NSS_STATUS_TRYAGAIN;) { -+ rc = back_extdom_getgrgid(ctx->nss_ctx, gid, grp, *buf, *buf_len, &result, &lerrno); -+ ret = __nss_to_err(rc); -+ if (ret == ERANGE) { -+ ret = inc_buffer(ctx->max_nss_buf_size, buf_len, buf); -+ if (ret != 0) goto done; - } - } - -- if (ret == 0 && result == NULL) { -- ret = ENOENT; -- } -- - done: -- *_buf = buf; -- *_buf_len = buf_len; -+ switch(ret) { -+ case 0: -+ if (result == NULL) ret = ENOENT; -+ break; -+ case ERANGE: -+ LOG("Buffer too small, increase ipaExtdomMaxNssBufSize.\n"); -+ default: -+ break; -+ } - - return ret; - } -@@ -406,13 +410,14 @@ int check_request(struct extdom_req *req, enum extdom_version version) - return LDAP_SUCCESS; - } - --int get_user_grouplist(const char *name, gid_t gid, -+int get_user_grouplist(struct ipa_extdom_ctx *ctx, const char *name, gid_t gid, - size_t *_ngroups, gid_t **_groups) - { -- int ret; -+ int lerrno; - int ngroups; - gid_t *groups; - gid_t *new_groups; -+ enum nss_status rc; - - ngroups = 128; - groups = malloc(ngroups * sizeof(gid_t)); -@@ -420,19 +425,18 @@ int get_user_grouplist(const char *name, gid_t gid, - return LDAP_OPERATIONS_ERROR; - } - -- ret = getgrouplist(name, gid, groups, &ngroups); -- if (ret == -1) { -- new_groups = realloc(groups, ngroups * sizeof(gid_t)); -- if (new_groups == NULL) { -- free(groups); -- return LDAP_OPERATIONS_ERROR; -- } -- groups = new_groups; -- -- ret = getgrouplist(name, gid, groups, &ngroups); -- if (ret == -1) { -- free(groups); -- return LDAP_OPERATIONS_ERROR; -+ for(rc = NSS_STATUS_TRYAGAIN; rc == NSS_STATUS_TRYAGAIN;) { -+ rc = back_extdom_getgrouplist(ctx->nss_ctx, name, gid, groups, &ngroups, &lerrno); -+ if (rc == NSS_STATUS_TRYAGAIN) { -+ new_groups = NULL; -+ if (lerrno == ERANGE) { -+ new_groups = realloc(groups, ngroups * sizeof(gid_t)); -+ } -+ if ((new_groups == NULL) || (lerrno == ENOMEM)) { -+ free(groups); -+ return LDAP_OPERATIONS_ERROR; -+ } -+ groups = new_groups; - } - } - -@@ -538,7 +542,7 @@ int pack_ber_user(struct ipa_extdom_ctx *ctx, - } - - if (response_type == RESP_USER_GROUPLIST) { -- ret = get_user_grouplist(user_name, gid, &ngroups, &groups); -+ ret = get_user_grouplist(ctx, user_name, gid, &ngroups, &groups); - if (ret != LDAP_SUCCESS) { - goto done; - } -@@ -561,7 +565,7 @@ int pack_ber_user(struct ipa_extdom_ctx *ctx, - } - - for (c = 0; c < ngroups; c++) { -- ret = getgrgid_r_wrapper(ctx->max_nss_buf_size, -+ ret = getgrgid_r_wrapper(ctx, - groups[c], &grp, &buf, &buf_len); - if (ret != 0) { - if (ret == ENOMEM || ret == ERANGE) { -@@ -841,8 +845,7 @@ static int handle_uid_request(struct ipa_extdom_ctx *ctx, - - ret = pack_ber_sid(sid_str, berval); - } else { -- ret = getpwuid_r_wrapper(ctx->max_nss_buf_size, uid, &pwd, &buf, -- &buf_len); -+ ret = getpwuid_r_wrapper(ctx, uid, &pwd, &buf, &buf_len); - if (ret != 0) { - if (ret == ENOMEM || ret == ERANGE) { - ret = LDAP_OPERATIONS_ERROR; -@@ -913,8 +916,7 @@ static int handle_gid_request(struct ipa_extdom_ctx *ctx, - - ret = pack_ber_sid(sid_str, berval); - } else { -- ret = getgrgid_r_wrapper(ctx->max_nss_buf_size, gid, &grp, &buf, -- &buf_len); -+ ret = getgrgid_r_wrapper(ctx, gid, &grp, &buf, &buf_len); - if (ret != 0) { - if (ret == ENOMEM || ret == ERANGE) { - ret = LDAP_OPERATIONS_ERROR; -@@ -1053,8 +1055,7 @@ static int handle_sid_request(struct ipa_extdom_ctx *ctx, - switch(id_type) { - case SSS_ID_TYPE_UID: - case SSS_ID_TYPE_BOTH: -- ret = getpwnam_r_wrapper(ctx->max_nss_buf_size, fq_name, &pwd, &buf, -- &buf_len); -+ ret = getpwnam_r_wrapper(ctx, fq_name, &pwd, &buf, &buf_len); - if (ret != 0) { - if (ret == ENOMEM || ret == ERANGE) { - ret = LDAP_OPERATIONS_ERROR; -@@ -1086,8 +1087,7 @@ static int handle_sid_request(struct ipa_extdom_ctx *ctx, - pwd.pw_shell, kv_list, berval); - break; - case SSS_ID_TYPE_GID: -- ret = getgrnam_r_wrapper(ctx->max_nss_buf_size, fq_name, &grp, &buf, -- &buf_len); -+ ret = getgrnam_r_wrapper(ctx, fq_name, &grp, &buf, &buf_len); - if (ret != 0) { - if (ret == ENOMEM || ret == ERANGE) { - ret = LDAP_OPERATIONS_ERROR; -@@ -1181,8 +1181,7 @@ static int handle_name_request(struct ipa_extdom_ctx *ctx, - goto done; - } - -- ret = getpwnam_r_wrapper(ctx->max_nss_buf_size, fq_name, &pwd, &buf, -- &buf_len); -+ ret = getpwnam_r_wrapper(ctx, fq_name, &pwd, &buf, &buf_len); - if (ret == 0) { - if (request_type == REQ_FULL_WITH_GROUPS) { - ret = sss_nss_getorigbyname(pwd.pw_name, &kv_list, &id_type); -@@ -1211,8 +1210,7 @@ static int handle_name_request(struct ipa_extdom_ctx *ctx, - * error codes which can indicate that the user was not found. To - * be on the safe side we fail back to the group lookup on all - * errors. */ -- ret = getgrnam_r_wrapper(ctx->max_nss_buf_size, fq_name, &grp, &buf, -- &buf_len); -+ ret = getgrnam_r_wrapper(ctx, fq_name, &grp, &buf, &buf_len); - if (ret != 0) { - if (ret == ENOMEM || ret == ERANGE) { - ret = LDAP_OPERATIONS_ERROR; -diff --git a/daemons/ipa-slapi-plugins/ipa-extdom-extop/ipa_extdom_extop.c b/daemons/ipa-slapi-plugins/ipa-extdom-extop/ipa_extdom_extop.c -index 5bc8c2f571e311c5ae4cc56e2e1ae7d4e2f77ee6..83c30e7e6aad72af603c0b4ed1c49b80fa57560f 100644 ---- a/daemons/ipa-slapi-plugins/ipa-extdom-extop/ipa_extdom_extop.c -+++ b/daemons/ipa-slapi-plugins/ipa-extdom-extop/ipa_extdom_extop.c -@@ -38,9 +38,11 @@ - * END COPYRIGHT BLOCK **/ - - #include "ipa_extdom.h" -+#include "back_extdom.h" - #include "util.h" - - #define DEFAULT_MAX_NSS_BUFFER (128*1024*1024) -+#define DEFAULT_MAX_NSS_TIMEOUT (10*1000) - - Slapi_PluginDesc ipa_extdom_plugin_desc = { - IPA_EXTDOM_FEATURE_DESC, -@@ -166,6 +168,7 @@ static int ipa_extdom_init_ctx(Slapi_PBlock *pb, struct ipa_extdom_ctx **_ctx) - struct ipa_extdom_ctx *ctx; - Slapi_Entry *e; - int ret; -+ unsigned int timeout; - - ctx = calloc(1, sizeof(struct ipa_extdom_ctx)); - if (!ctx) { -@@ -202,6 +205,20 @@ static int ipa_extdom_init_ctx(Slapi_PBlock *pb, struct ipa_extdom_ctx **_ctx) - } - LOG("Maximal nss buffer size set to [%zu]!\n", ctx->max_nss_buf_size); - -+ -+ ret = back_extdom_init_context(&ctx->nss_ctx); -+ if (ret != 0) { -+ LOG("Unable to initialize nss interface: returned [%d]!\n", ret); -+ goto done; -+ } -+ -+ timeout = slapi_entry_attr_get_uint(e, "ipaExtdomMaxNssTimeout"); -+ if (timeout == 0) { -+ timeout = DEFAULT_MAX_NSS_TIMEOUT; -+ } -+ back_extdom_set_timeout(ctx->nss_ctx, timeout); -+ LOG("Maximal nss timeout (in ms) set to [%u]!\n", timeout); -+ - ret = 0; - - done: -diff --git a/daemons/ipa-slapi-plugins/ipa-extdom-extop/test_data/test_setup.sh b/daemons/ipa-slapi-plugins/ipa-extdom-extop/test_data/test_setup.sh -deleted file mode 100644 -index ad839f340efe989a91cd6902f59c9a41483f68e0..0000000000000000000000000000000000000000 ---- a/daemons/ipa-slapi-plugins/ipa-extdom-extop/test_data/test_setup.sh -+++ /dev/null -@@ -1,3 +0,0 @@ --export LD_PRELOAD=$(pkg-config --libs nss_wrapper) --export NSS_WRAPPER_PASSWD=./test_data/passwd --export NSS_WRAPPER_GROUP=./test_data/group -diff --git a/freeipa.spec.in b/freeipa.spec.in -index a8b5ce81fcf9bdb61cd3707e6b68b6f2196e0776..80ae98c5515f64a8df8d981ad5e91b05c84e31c1 100644 ---- a/freeipa.spec.in -+++ b/freeipa.spec.in -@@ -246,7 +246,6 @@ BuildRequires: python3-augeas - # - %if ! %{ONLY_CLIENT} - BuildRequires: libcmocka-devel --BuildRequires: nss_wrapper - # Required by ipa_kdb_tests - BuildRequires: %{_libdir}/krb5/plugins/kdb/db2.so - %endif # ONLY_CLIENT -diff --git a/server.m4 b/server.m4 -index a9670c87372bb7b92d08dad634c0bda123a02597..f0a8bbcc778596dade89d9332abb2939b8a44143 100644 ---- a/server.m4 -+++ b/server.m4 -@@ -35,6 +35,16 @@ AC_CHECK_LIB([sss_nss_idmap], - [AC_MSG_ERROR([Required sss_nss_getlistbycert symbol in sss_nss_idmap not found])], - []) - -+dnl --- if sss_nss_idmap provides _timeout() API, use it -+bck_cflags="$CFLAGS" -+CFLAGS="$CFLAGS -DIPA_389DS_PLUGIN_HELPER_CALLS" -+AC_CHECK_DECLS([sss_nss_getpwnam_timeout], [], [], [[#include ]]) -+CFLAGS="$bck_cflags" -+ -+if test "x$ac_cv_have_decl_sss_nss_getpwnam_timeout" = xyes ; then -+ AC_DEFINE(USE_SSS_NSS_TIMEOUT,1,[Use extended NSS API provided by SSSD]) -+fi -+ - dnl -- sss_certmap and certauth.h are needed by the IPA KDB certauth plugin -- - PKG_CHECK_EXISTS([sss_certmap], - [PKG_CHECK_MODULES([SSSCERTMAP], [sss_certmap])], --- -2.14.3 - diff --git a/SOURCES/0008-Add-the-sub-operation-for-fqdn-index-config.patch b/SOURCES/0008-Add-the-sub-operation-for-fqdn-index-config.patch deleted file mode 100644 index b7f8792..0000000 --- a/SOURCES/0008-Add-the-sub-operation-for-fqdn-index-config.patch +++ /dev/null @@ -1,46 +0,0 @@ -From 8a1e04abede4c0bd5730781ef8c42c8a2e1bbcf9 Mon Sep 17 00:00:00 2001 -From: Stanislav Laznicka -Date: Fri, 3 Nov 2017 09:23:10 +0100 -Subject: [PATCH] Add the sub operation for fqdn index config - -This should improve performance of the host-find command. - -https://pagure.io/freeipa/issue/6371 - -Reviewed-By: Rob Crittenden ---- - install/share/indices.ldif | 1 + - install/updates/20-indices.update | 5 +++-- - 2 files changed, 4 insertions(+), 2 deletions(-) - -diff --git a/install/share/indices.ldif b/install/share/indices.ldif -index d853266025ae350dd7de83e11e463c6bb1ab9429..adb041d374d8fc48fff9d4b40208e7eda82857b3 100644 ---- a/install/share/indices.ldif -+++ b/install/share/indices.ldif -@@ -108,6 +108,7 @@ cn: fqdn - nsSystemIndex: false - nsIndexType: eq - nsIndexType: pres -+nsIndexType: sub - - dn: cn=macAddress,cn=index,cn=userRoot,cn=ldbm database,cn=plugins,cn=config - changetype: add -diff --git a/install/updates/20-indices.update b/install/updates/20-indices.update -index 74961d77875515d680f34af739c984a6533eb252..fb588b9ba8a2a89c9e7eab87ab5f224ca438645a 100644 ---- a/install/updates/20-indices.update -+++ b/install/updates/20-indices.update -@@ -70,8 +70,9 @@ default:cn: fqdn - default:ObjectClass: top - default:ObjectClass: nsIndex - default:nsSystemIndex: false --default:nsIndexType: eq --default:nsIndexType: pres -+only:nsIndexType: eq -+only:nsIndexType: pres -+only:nsIndexType: sub - - dn: cn=macAddress,cn=index,cn=userRoot,cn=ldbm database,cn=plugins,cn=config - default:cn: macAddress --- -2.14.3 - diff --git a/SOURCES/0008-Fix-replication-races-in-Dogtag-admin-code.patch b/SOURCES/0008-Fix-replication-races-in-Dogtag-admin-code.patch new file mode 100644 index 0000000..fccb1fd --- /dev/null +++ b/SOURCES/0008-Fix-replication-races-in-Dogtag-admin-code.patch @@ -0,0 +1,204 @@ +From ecd1fa6b42c6ae76ce620c5ac949e64ba11d375a Mon Sep 17 00:00:00 2001 +From: Christian Heimes +Date: Fri, 22 Jun 2018 10:04:38 +0200 +Subject: [PATCH] Fix replication races in Dogtag admin code + +DogtagInstance.setup_admin and related methods have multiple LDAP +replication race conditions. The bugs can cause parallel +ipa-replica-install to fail. + +The code from __add_admin_to_group() has been changed to use MOD_ADD +ather than search + MOD_REPLACE. The MOD_REPLACE approach can lead to +data loss, when more than one writer changes a group. + +setup_admin() now waits until both admin user and group membership have +been replicated to the master peer. The method also adds a new ACI to +allow querying group member in the replication check. + +Fixes: https://pagure.io/freeipa/issue/7593 +Signed-off-by: Christian Heimes +Reviewed-By: Fraser Tweedale +--- + ipaserver/install/cainstance.py | 1 + + ipaserver/install/dogtaginstance.py | 96 +++++++++++++++++++++++++++++-------- + ipaserver/install/krainstance.py | 1 + + 3 files changed, 77 insertions(+), 21 deletions(-) + +diff --git a/ipaserver/install/cainstance.py b/ipaserver/install/cainstance.py +index 5bdac82a57c2228b1b196337a2ceadb062fdc13c..b58fbb4c881d247d6b5fb661f4085ec82c3cc811 100644 +--- a/ipaserver/install/cainstance.py ++++ b/ipaserver/install/cainstance.py +@@ -392,6 +392,7 @@ class CAInstance(DogtagInstance): + # Setup Database + self.step("creating certificate server db", self.__create_ds_db) + self.step("setting up initial replication", self.__setup_replication) ++ self.step("creating ACIs for admin", self.add_ipaca_aci) + self.step("creating installation admin user", self.setup_admin) + self.step("configuring certificate server instance", + self.__spawn_instance) +diff --git a/ipaserver/install/dogtaginstance.py b/ipaserver/install/dogtaginstance.py +index 07e63024c6e138269634f0206b14c6a1710df936..5b2c30f8a1b7e932ce1cca3ca38f5962a3d54266 100644 +--- a/ipaserver/install/dogtaginstance.py ++++ b/ipaserver/install/dogtaginstance.py +@@ -21,6 +21,7 @@ from __future__ import absolute_import + + import base64 + import logging ++import time + + import ldap + import os +@@ -90,6 +91,16 @@ class DogtagInstance(service.Service): + tracking_reqs = None + server_cert_name = None + ++ ipaca_groups = DN(('ou', 'groups'), ('o', 'ipaca')) ++ ipaca_people = DN(('ou', 'people'), ('o', 'ipaca')) ++ groups_aci = ( ++ b'(targetfilter="(objectClass=groupOfUniqueNames)")' ++ b'(targetattr="cn || description || objectclass || uniquemember")' ++ b'(version 3.0; acl "Allow users from o=ipaca to read groups"; ' ++ b'allow (read, search, compare) ' ++ b'userdn="ldap:///uid=*,ou=people,o=ipaca";)' ++ ) ++ + def __init__(self, realm, subsystem, service_desc, host_name=None, + nss_db=paths.PKI_TOMCAT_ALIAS_DIR, service_prefix=None, + config=None): +@@ -108,10 +119,11 @@ class DogtagInstance(service.Service): + self.pkcs12_info = None + self.clone = False + +- self.basedn = DN(('o', 'ipa%s' % subsystem.lower())) ++ self.basedn = DN(('o', 'ipaca')) + self.admin_user = "admin" +- self.admin_dn = DN(('uid', self.admin_user), +- ('ou', 'people'), ('o', 'ipaca')) ++ self.admin_dn = DN( ++ ('uid', self.admin_user), self.ipaca_people ++ ) + self.admin_groups = None + self.tmp_agent_db = None + self.subsystem = subsystem +@@ -393,27 +405,31 @@ class DogtagInstance(service.Service): + + raise RuntimeError("%s configuration failed." % self.subsystem) + +- def __add_admin_to_group(self, group): +- dn = DN(('cn', group), ('ou', 'groups'), ('o', 'ipaca')) +- entry = api.Backend.ldap2.get_entry(dn) +- members = entry.get('uniqueMember', []) +- members.append(self.admin_dn) +- mod = [(ldap.MOD_REPLACE, 'uniqueMember', members)] ++ def add_ipaca_aci(self): ++ """Add ACI to allow ipaca users to read their own group information ++ ++ Dogtag users aren't allowed to enumerate their own groups. The ++ setup_admin() method needs the permission to wait, until all group ++ information has been replicated. ++ """ ++ dn = self.ipaca_groups ++ mod = [(ldap.MOD_ADD, 'aci', [self.groups_aci])] + try: + api.Backend.ldap2.modify_s(dn, mod) + except ldap.TYPE_OR_VALUE_EXISTS: +- # already there +- pass ++ logger.debug("%s already has ACI to read group information", dn) ++ else: ++ logger.debug("Added ACI to read groups to %s", dn) + + def setup_admin(self): + self.admin_user = "admin-%s" % self.fqdn + self.admin_password = ipautil.ipa_generate_password() +- self.admin_dn = DN(('uid', self.admin_user), +- ('ou', 'people'), ('o', 'ipaca')) +- ++ self.admin_dn = DN( ++ ('uid', self.admin_user), self.ipaca_people ++ ) + # remove user if left-over exists + try: +- entry = api.Backend.ldap2.delete_entry(self.admin_dn) ++ api.Backend.ldap2.delete_entry(self.admin_dn) + except errors.NotFound: + pass + +@@ -432,18 +448,56 @@ class DogtagInstance(service.Service): + ) + api.Backend.ldap2.add_entry(entry) + ++ wait_groups = [] + for group in self.admin_groups: +- self.__add_admin_to_group(group) ++ group_dn = DN(('cn', group), self.ipaca_groups) ++ mod = [(ldap.MOD_ADD, 'uniqueMember', [self.admin_dn])] ++ try: ++ api.Backend.ldap2.modify_s(group_dn, mod) ++ except ldap.TYPE_OR_VALUE_EXISTS: ++ # already there ++ return None ++ else: ++ wait_groups.append(group_dn) + + # Now wait until the other server gets replicated this data + ldap_uri = ipaldap.get_ldap_uri(self.master_host) +- master_conn = ipaldap.LDAPClient(ldap_uri) +- master_conn.gssapi_bind() +- replication.wait_for_entry(master_conn, entry.dn) +- del master_conn ++ master_conn = ipaldap.LDAPClient(ldap_uri, start_tls=True) ++ logger.debug( ++ "Waiting for %s to appear on %s", self.admin_dn, master_conn ++ ) ++ deadline = time.time() + api.env.replication_wait_timeout ++ while time.time() < deadline: ++ time.sleep(1) ++ try: ++ master_conn.simple_bind(self.admin_dn, self.admin_password) ++ except ldap.INVALID_CREDENTIALS: ++ pass ++ else: ++ logger.debug("Successfully logged in as %s", self.admin_dn) ++ break ++ else: ++ logger.error( ++ "Unable to log in as %s on %s", self.admin_dn, master_conn ++ ) ++ raise errors.NotFound( ++ reason="{} did not replicate to {}".format( ++ self.admin_dn, master_conn ++ ) ++ ) ++ ++ # wait for group membership ++ for group_dn in wait_groups: ++ replication.wait_for_entry( ++ master_conn, ++ group_dn, ++ timeout=api.env.replication_wait_timeout, ++ attr='uniqueMember', ++ attrvalue=self.admin_dn ++ ) + + def __remove_admin_from_group(self, group): +- dn = DN(('cn', group), ('ou', 'groups'), ('o', 'ipaca')) ++ dn = DN(('cn', group), self.ipaca_groups) + mod = [(ldap.MOD_DELETE, 'uniqueMember', self.admin_dn)] + try: + api.Backend.ldap2.modify_s(dn, mod) +diff --git a/ipaserver/install/krainstance.py b/ipaserver/install/krainstance.py +index b959398706d051a2584e722e176a9f2b0a9a0dc7..9483f0ec4edbabea0f7eff0dd5dd223377653536 100644 +--- a/ipaserver/install/krainstance.py ++++ b/ipaserver/install/krainstance.py +@@ -115,6 +115,7 @@ class KRAInstance(DogtagInstance): + "A Dogtag CA must be installed first") + + if promote: ++ self.step("creating ACIs for admin", self.add_ipaca_aci) + self.step("creating installation admin user", self.setup_admin) + self.step("configuring KRA instance", self.__spawn_instance) + if not self.clone: +-- +2.14.4 + diff --git a/SOURCES/0009-Add-indexing-to-improve-host-find-performance.patch b/SOURCES/0009-Add-indexing-to-improve-host-find-performance.patch deleted file mode 100644 index 8bc6e7f..0000000 --- a/SOURCES/0009-Add-indexing-to-improve-host-find-performance.patch +++ /dev/null @@ -1,121 +0,0 @@ -From ff597260ef2f5e953b55ae1a8511191853becca8 Mon Sep 17 00:00:00 2001 -From: Stanislav Laznicka -Date: Fri, 27 Oct 2017 09:34:38 +0200 -Subject: [PATCH] Add indexing to improve host-find performance - -host-find command performance gets deteriorated when -there's way too many hosts in the LDAP tree. We're adding indices -to try and mitigate this behavior. - -https://pagure.io/freeipa/issue/6371 - -Reviewed-By: Rob Crittenden ---- - install/share/indices.ldif | 45 +++++++++++++++++++++++++++++++++++++++ - install/updates/20-indices.update | 40 ++++++++++++++++++++++++++++++++++ - 2 files changed, 85 insertions(+) - -diff --git a/install/share/indices.ldif b/install/share/indices.ldif -index adb041d374d8fc48fff9d4b40208e7eda82857b3..7bd59d2774df9bdf56f6b8034236aa2c5658b366 100644 ---- a/install/share/indices.ldif -+++ b/install/share/indices.ldif -@@ -279,3 +279,48 @@ objectClass: nsIndex - nsSystemIndex: false - nsIndexType: eq - nsIndexType: sub -+ -+dn: cn=description,cn=index,cn=userroot,cn=ldbm database,cn=plugins,cn=config -+changetype: add -+cn: description -+objectClass: top -+objectClass: nsindex -+nssystemindex: false -+nsindextype: eq -+nsindextype: sub -+ -+dn: cn=l,cn=index,cn=userroot,cn=ldbm database,cn=plugins,cn=config -+changetype: add -+cn: l -+objectClass: top -+objectClass: nsindex -+nssystemindex: false -+nsindextype: eq -+nsindextype: sub -+ -+dn: cn=nsOsVersion,cn=index,cn=userroot,cn=ldbm database,cn=plugins,cn=config -+changetype: add -+cn: nsOsVersion -+objectClass: top -+objectClass: nsindex -+nssystemindex: false -+nsindextype: eq -+nsindextype: sub -+ -+dn: cn=nsHardwarePlatform,cn=index,cn=userroot,cn=ldbm database,cn=plugins,cn=config -+changetype: add -+cn: nsHardwarePlatform -+objectClass: top -+objectClass: nsindex -+nssystemindex: false -+nsindextype: eq -+nsindextype: sub -+ -+dn: cn=nsHostLocation,cn=index,cn=userroot,cn=ldbm database,cn=plugins,cn=config -+changetype: add -+cn: nsHostLocation -+objectClass: top -+objectClass: nsindex -+nssystemindex: false -+nsindextype: eq -+nsindextype: sub -diff --git a/install/updates/20-indices.update b/install/updates/20-indices.update -index fb588b9ba8a2a89c9e7eab87ab5f224ca438645a..016fbb6bedb6af69fba0a8a84f8c1c6622d4368c 100644 ---- a/install/updates/20-indices.update -+++ b/install/updates/20-indices.update -@@ -260,3 +260,43 @@ default: objectClass: nsIndex - only: nsSystemIndex: false - only: nsIndexType: eq - only: nsIndexType: sub -+ -+dn: cn=description,cn=index,cn=userroot,cn=ldbm database,cn=plugins,cn=config -+default: cn: description -+default: objectclass: top -+default: objectclass: nsindex -+default: nssystemindex: false -+default: nsindextype: eq -+default: nsindextype: sub -+ -+dn: cn=l,cn=index,cn=userroot,cn=ldbm database,cn=plugins,cn=config -+default: cn: l -+default: objectclass: top -+default: objectclass: nsindex -+default: nssystemindex: false -+default: nsindextype: eq -+default: nsindextype: sub -+ -+dn: cn=nsOsVersion,cn=index,cn=userroot,cn=ldbm database,cn=plugins,cn=config -+default: cn: nsOsVersion -+default: objectclass: top -+default: objectclass: nsindex -+default: nssystemindex: false -+default: nsindextype: eq -+default: nsindextype: sub -+ -+dn: cn=nsHardwarePlatform,cn=index,cn=userroot,cn=ldbm database,cn=plugins,cn=config -+default: cn: nsHardwarePlatform -+default: objectclass: top -+default: objectclass: nsindex -+default: nssystemindex: false -+default: nsindextype: eq -+default: nsindextype: sub -+ -+dn: cn=nsHostLocation,cn=index,cn=userroot,cn=ldbm database,cn=plugins,cn=config -+default: cn: nsHostLocation -+default: objectclass: top -+default: objectclass: nsindex -+default: nssystemindex: false -+default: nsindextype: eq -+default: nsindextype: sub --- -2.14.3 - diff --git a/SOURCES/0009-Allow-anonymous-access-to-ParentID-attribute.patch b/SOURCES/0009-Allow-anonymous-access-to-ParentID-attribute.patch new file mode 100644 index 0000000..94179ab --- /dev/null +++ b/SOURCES/0009-Allow-anonymous-access-to-ParentID-attribute.patch @@ -0,0 +1,43 @@ +From 34d06b2be71823bc8898732f1ced0185f83afb01 Mon Sep 17 00:00:00 2001 +From: Alexander Bokovoy +Date: Wed, 28 Mar 2018 12:39:12 +0300 +Subject: [PATCH] Allow anonymous access to parentID attribute + +Due to optimizations in 389-ds performed as result of +https://pagure.io/389-ds-base/issue/49372, LDAP search filter +is rewritten to include parentID information. It implies that parentID +has to be readable for a bound identity performing the search. This is +what 389-ds expects right now but FreeIPA DS instance does not allow it. + +As result, searches with a one-level scope fail to return results that +otherwise are matched in a sub scope search. + +While 389-ds developers are working on the fix for issue +https://pagure.io/389-ds-base/issue/49617, we can fix it by adding an +explicit ACI to allow reading parentID attribute at the suffix level. + +Fixes: https://pagure.io/freeipa/issue/7466 +Signed-off-by: Alexander Bokovoy +Reviewed-By: Christian Heimes +--- + install/updates/20-aci.update | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/install/updates/20-aci.update b/install/updates/20-aci.update +index dec2e16ee..b8a172eb5 100644 +--- a/install/updates/20-aci.update ++++ b/install/updates/20-aci.update +@@ -21,6 +21,10 @@ add:aci:(targetattr="ipasshpubkey")(version 3.0; acl "Hosts can manage other hos + dn: $SUFFIX + add:aci:(targetfilter="(objectclass=domain)")(targetattr="objectclass || dc || info || nisDomain || associatedDomain")(version 3.0; acl "Anonymous read access to DIT root"; allow(read, search, compare) userdn = "ldap:///anyone";) + ++# Read access to parentID information to allow filter optimizations in 389-ds ++dn: $SUFFIX ++add:aci:(targetattr="parentid")(version 3.0; acl "Anonymous read access to parentID information"; allow(read, search, compare) userdn = "ldap:///anyone";) ++ + # Read access to containers + dn: $SUFFIX + add:aci:(targetfilter="(&(objectclass=nsContainer)(!(objectclass=krbPwdPolicy)))")(target!="ldap:///cn=masters,cn=ipa,cn=etc,$SUFFIX")(targetattr="objectclass || cn")(version 3.0; acl "Anonymous read access to containers"; allow(read, search, compare) userdn = "ldap:///anyone";) +-- +2.17.1 + diff --git a/SOURCES/0010-Use-4-WSGI-workers-on-64bit-systems.patch b/SOURCES/0010-Use-4-WSGI-workers-on-64bit-systems.patch new file mode 100644 index 0000000..212eb15 --- /dev/null +++ b/SOURCES/0010-Use-4-WSGI-workers-on-64bit-systems.patch @@ -0,0 +1,32 @@ +From bf539f80ed34c30bc852a6061ce6444c5c308a26 Mon Sep 17 00:00:00 2001 +From: Christian Heimes +Date: Mon, 25 Jun 2018 10:59:18 +0200 +Subject: [PATCH] Use 4 WSGI workers on 64bit systems + +Commit f1d5ab3a03191dbb02e5f95308cf8c4f1971cdcf increases WSGI worker +count to five. This turned out to be a bit much for our test systems. +Four workers are good enough and still double the old amount. + +See: https://pagure.io/freeipa/issue/7587 +Signed-off-by: Christian Heimes +Reviewed-By: Tibor Dudlak +--- + ipaplatform/base/constants.py | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/ipaplatform/base/constants.py b/ipaplatform/base/constants.py +index 075a3ba774e1286e50258b464bd7687d484f6029..e9ce4378892a5cd498398e37cc52f295608152cd 100644 +--- a/ipaplatform/base/constants.py ++++ b/ipaplatform/base/constants.py +@@ -46,7 +46,7 @@ class BaseConstantsNamespace(object): + MOD_WSGI_PYTHON3 = None + # WSGIDaemonProcess process count. On 64bit platforms, each process + # consumes about 110 MB RSS, from which are about 35 MB shared. +- WSGI_PROCESSES = 5 if IS_64BITS else 2 ++ WSGI_PROCESSES = 4 if IS_64BITS else 2 + + + constants = BaseConstantsNamespace() +-- +2.17.1 + diff --git a/SOURCES/0010-ipa-getkeytab-man-page-add-more-details-about-the-r-.patch b/SOURCES/0010-ipa-getkeytab-man-page-add-more-details-about-the-r-.patch deleted file mode 100644 index f3509c2..0000000 --- a/SOURCES/0010-ipa-getkeytab-man-page-add-more-details-about-the-r-.patch +++ /dev/null @@ -1,87 +0,0 @@ -From 30e55883bd4822d4d3af061d646e7b1044880d52 Mon Sep 17 00:00:00 2001 -From: Florence Blanc-Renaud -Date: Tue, 7 Nov 2017 09:31:19 +0100 -Subject: [PATCH] ipa-getkeytab man page: add more details about the -r option - -The man page does not provide enough information about replicated -environments and the use of the -r option. -This fix adds an example how to use the same keytab on 2 different -hosts, and points to ipa {service/host}-allow-retrieve-keytab. - -Fixes: -https://pagure.io/freeipa/issue/7237 - -Reviewed-By: Alexander Bokovoy ---- - client/man/ipa-getkeytab.1 | 35 ++++++++++++++++++++++++++++++++++- - 1 file changed, 34 insertions(+), 1 deletion(-) - -diff --git a/client/man/ipa-getkeytab.1 b/client/man/ipa-getkeytab.1 -index 08f6ec40d362b88a974e6ec735ed37c271e01882..39ff0d5da85b5a641328a512feeb06bc9c1ab9d7 100644 ---- a/client/man/ipa-getkeytab.1 -+++ b/client/man/ipa-getkeytab.1 -@@ -44,10 +44,15 @@ provided, so the principal name is just the service - name and hostname (ldap/foo.example.com from the - example above). - -+ipa-getkeytab is used during IPA client enrollment to retrieve a host service principal and store it in /etc/krb5.keytab. It is possible to retrieve the keytab without Kerberos credentials if the host was pre\-created with a one\-time password. The keytab can be retrieved by binding as the host and authenticating with this one\-time password. The \fB\-D|\-\-binddn\fR and \fB\-w|\-\-bindpw\fR options are used for this authentication. -+ - \fBWARNING:\fR retrieving the keytab resets the secret for the Kerberos principal. - This renders all other keytabs for that principal invalid. -+When multiple hosts or services need to share the same key (for instance in high availability or load balancing clusters), the \fB\-r\fR option must be used to retrieve the existing key instead of generating a new one (please refer to the EXAMPLES section). -+ -+Note that the user or host calling \fBipa-getkeytab\fR needs to be allowed to generate the key with \fBipa host\-allow\-create\-keytab\fR or \fBipa service\-allow\-create\-keytab\fR, -+and the user or host calling \fBipa-getkeytab \-r\fR needs to be allowed to retrieve the keytab for the host or service with \fBipa host\-allow\-retrieve\-keytab\fR or \fBipa service\-allow\-retrieve\-keytab\fR. - --This is used during IPA client enrollment to retrieve a host service principal and store it in /etc/krb5.keytab. It is possible to retrieve the keytab without Kerberos credentials if the host was pre\-created with a one\-time password. The keytab can be retrieved by binding as the host and authenticating with this one\-time password. The \fB\-D|\-\-binddn\fR and \fB\-w|\-\-bindpw\fR options are used for this authentication. - .SH "OPTIONS" - .TP - \fB\-p principal\-name\fR -@@ -118,16 +123,44 @@ keytab must have access to the keys for this operation to succeed. - Add and retrieve a keytab for the NFS service principal on - the host foo.example.com and save it in the file /tmp/nfs.keytab and retrieve just the des\-cbc\-crc key. - -+.nf - # ipa\-getkeytab \-p nfs/foo.example.com \-k /tmp/nfs.keytab \-e des\-cbc\-crc -+.fi - - Add and retrieve a keytab for the ldap service principal on - the host foo.example.com and save it in the file /tmp/ldap.keytab. - -+.nf - # ipa\-getkeytab \-s ipaserver.example.com \-p ldap/foo.example.com \-k /tmp/ldap.keytab -+.fi - - Retrieve a keytab using LDAP credentials (this will typically be done by \fBipa\-join(1)\fR when enrolling a client using the \fBipa\-client\-install(1)\fR command: - -+.nf - # ipa\-getkeytab \-s ipaserver.example.com \-p host/foo.example.com \-k /etc/krb5.keytab \-D fqdn=foo.example.com,cn=computers,cn=accounts,dc=example,dc=com \-w password -+.fi -+ -+Add and retrieve a keytab for a clustered HTTP service deployed on client1.example.com and client2.example.com (already enrolled), using the client-frontend.example.com host name: -+ -+.nf -+ # ipa host-add client-frontend.example.com --ip-address 10.1.2.3 -+ # ipa service-add HTTP/client-frontend.example.com -+ # ipa service-allow-retrieve-keytab HTTP/client-frontend.example.com --hosts={client1.example.com,client2.example.com} -+ # ipa server-allow-create-keytab HTTP/client-frontend.example.com --hosts=client1.example.com -+.fi -+ -+ On client1, generate and retrieve a new keytab for client-frontend.example.com: -+.nf -+ # kinit -k -+ # ipa-getkeytab -p HTTP/client-frontend.example.com -k /tmp/http.keytab -+ -+.fi -+ On client2, retrieve the existing keytab for client-frontend.example.com: -+.nf -+ # kinit -k -+ # ipa-getkeytab -r -p HTTP/client-frontend.example.com -k /tmp/http.keytab -+.fi -+ - .SH "EXIT STATUS" - The exit status is 0 on success, nonzero on error. - --- -2.13.6 - diff --git a/SOURCES/0011-Don-t-allow-OTP-or-RADIUS-in-FIPS-mode.patch b/SOURCES/0011-Don-t-allow-OTP-or-RADIUS-in-FIPS-mode.patch deleted file mode 100644 index 74e0d92..0000000 --- a/SOURCES/0011-Don-t-allow-OTP-or-RADIUS-in-FIPS-mode.patch +++ /dev/null @@ -1,83 +0,0 @@ -From be18d6c15a2557e8f45e41efc81f1c005958c690 Mon Sep 17 00:00:00 2001 -From: Stanislav Laznicka -Date: Tue, 7 Nov 2017 14:42:12 +0100 -Subject: [PATCH] Don't allow OTP or RADIUS in FIPS mode - -RADIUS, which is also internally used in the process of OTP -authentication by ipa-otpd, requires MD5 checksums which -makes it impossible to be used in FIPS mode. Don't allow users -setting OTP or RADIUS authentication if in FIPS mode. - -https://pagure.io/freeipa/issue/7168 - -Reviewed-By: Alexander Bokovoy ---- - ipaserver/plugins/baseuser.py | 3 +++ - ipaserver/plugins/config.py | 16 ++++++++++++++++ - 2 files changed, 19 insertions(+) - -diff --git a/ipaserver/plugins/baseuser.py b/ipaserver/plugins/baseuser.py -index bf24dbf542d3b481671dfe4e8cee14a2edcc26e0..bb8a73ded0fed135d5829ec0b0829a936f2196fb 100644 ---- a/ipaserver/plugins/baseuser.py -+++ b/ipaserver/plugins/baseuser.py -@@ -32,6 +32,7 @@ from .baseldap import ( - add_missing_object_class) - from ipaserver.plugins.service import ( - validate_certificate, validate_realm, normalize_principal) -+from ipaserver.plugins.config import check_fips_auth_opts - from ipalib.request import context - from ipalib import _ - from ipalib.constants import PATTERN_GROUPUSER_NAME -@@ -477,6 +478,7 @@ class baseuser_add(LDAPCreate): - **options): - assert isinstance(dn, DN) - set_krbcanonicalname(entry_attrs) -+ check_fips_auth_opts(fips_mode=self.api.env.fips_mode, **options) - self.obj.convert_usercertificate_pre(entry_attrs) - - def post_common_callback(self, ldap, dn, entry_attrs, *keys, **options): -@@ -600,6 +602,7 @@ class baseuser_mod(LDAPUpdate): - assert isinstance(dn, DN) - add_sshpubkey_to_attrs_pre(self.context, attrs_list) - -+ check_fips_auth_opts(fips_mode=self.api.env.fips_mode, **options) - self.check_namelength(ldap, **options) - - self.check_mail(entry_attrs) -diff --git a/ipaserver/plugins/config.py b/ipaserver/plugins/config.py -index ce15e6096f5b84dc45ee21d5aecc73ecf86eba07..c9033fa8e7a2a0bfe77464fa4f9c62278bd814f6 100644 ---- a/ipaserver/plugins/config.py -+++ b/ipaserver/plugins/config.py -@@ -85,6 +85,20 @@ EXAMPLES: - - register = Registry() - -+ -+def check_fips_auth_opts(fips_mode, **options): -+ """ -+ OTP and RADIUS are not allowed in FIPS mode since they use MD5 -+ checksums (OTP uses our RADIUS responder daemon ipa-otpd). -+ """ -+ if 'ipauserauthtype' in options and fips_mode: -+ if ('otp' in options['ipauserauthtype'] or -+ 'radius' in options['ipauserauthtype']): -+ raise errors.InvocationError( -+ 'OTP and RADIUS authentication in FIPS is ' -+ 'not yet supported') -+ -+ - @register() - class config(LDAPObject): - """ -@@ -398,6 +412,8 @@ class config_mod(LDAPUpdate): - - def pre_callback(self, ldap, dn, entry_attrs, attrs_list, *keys, **options): - assert isinstance(dn, DN) -+ check_fips_auth_opts(fips_mode=self.api.env.fips_mode, **options) -+ - if 'ipadefaultprimarygroup' in entry_attrs: - group=entry_attrs['ipadefaultprimarygroup'] - try: --- -2.13.6 - diff --git a/SOURCES/0011-Query-for-server-role-IPA-master.patch b/SOURCES/0011-Query-for-server-role-IPA-master.patch new file mode 100644 index 0000000..f592f40 --- /dev/null +++ b/SOURCES/0011-Query-for-server-role-IPA-master.patch @@ -0,0 +1,108 @@ +From ef7fe9e24844f73a27650d4d2a4f118cc364caac Mon Sep 17 00:00:00 2001 +From: Christian Heimes +Date: Thu, 5 Jul 2018 23:50:37 +0200 +Subject: [PATCH] Query for server role IPA master + +server_find and server_role plugin were hiding IPA master role +information. It's now possible to fetch IPA master role information and +to filter by IPA master role, e.g. to ignore servers that have some +services configured but not (yet) enabled. + +See: https://pagure.io/freeipa/issue/7566 +Signed-off-by: Christian Heimes +Reviewed-By: Alexander Bokovoy +--- + API.txt | 3 ++- + ipaserver/plugins/server.py | 9 +++++++-- + ipaserver/plugins/serverrole.py | 18 +++++++++++++++--- + 3 files changed, 24 insertions(+), 6 deletions(-) + +diff --git a/API.txt b/API.txt +index 96c1548331750577e899f397f6f5a9a9d863248a..0e09e58a6ecaa4f724fb0c92b4faaf64df9fab5a 100644 +--- a/API.txt ++++ b/API.txt +@@ -4421,9 +4421,10 @@ output: Entry('result') + output: Output('summary', type=[, ]) + output: PrimaryKey('value') + command: server_role_find/1 +-args: 1,8,4 ++args: 1,9,4 + arg: Str('criteria?') + option: Flag('all', autofill=True, cli_name='all', default=False) ++option: Flag('include_master', autofill=True, default=False) + option: Flag('raw', autofill=True, cli_name='raw', default=False) + option: Str('role_servrole?', autofill=False, cli_name='role') + option: Str('server_server?', autofill=False, cli_name='server') +diff --git a/ipaserver/plugins/server.py b/ipaserver/plugins/server.py +index 4ea6f5b4b458db701c6c041d1c05cf4a7c6bc8a4..e265883e3637938e3df5ecf132f4add62413a997 100644 +--- a/ipaserver/plugins/server.py ++++ b/ipaserver/plugins/server.py +@@ -205,7 +205,10 @@ class server(LDAPObject): + return + + enabled_roles = self.api.Command.server_role_find( +- server_server=entry_attrs['cn'][0], status=ENABLED)['result'] ++ server_server=entry_attrs['cn'][0], ++ status=ENABLED, ++ include_master=True, ++ )['result'] + + enabled_role_names = [r[u'role_servrole'] for r in enabled_roles] + +@@ -339,7 +342,9 @@ class server_find(LDAPSearch): + role_status = self.api.Command.server_role_find( + server_server=None, + role_servrole=role, +- status=ENABLED)['result'] ++ status=ENABLED, ++ include_master=True, ++ )['result'] + + return set( + r[u'server_server'] for r in role_status) +diff --git a/ipaserver/plugins/serverrole.py b/ipaserver/plugins/serverrole.py +index 1b19c7e867a0223d1c2e72372d9f3dc65fc5f771..5b7ccfb342d0a54bfd6f2cdc53c7d31201ed5989 100644 +--- a/ipaserver/plugins/serverrole.py ++++ b/ipaserver/plugins/serverrole.py +@@ -5,7 +5,7 @@ + from ipalib.crud import Retrieve, Search + from ipalib.errors import NotFound + from ipalib.frontend import Object +-from ipalib.parameters import Int, Str, StrEnum ++from ipalib.parameters import Flag, Int, Str, StrEnum + from ipalib.plugable import Registry + from ipalib import _, ngettext + +@@ -129,6 +129,10 @@ class server_role_find(Search): + minvalue=0, + autofill=False, + ), ++ Flag( ++ 'include_master', ++ doc=_('Include IPA master entries'), ++ ) + ) + + def execute(self, *keys, **options): +@@ -151,8 +155,16 @@ class server_role_find(Search): + role_servrole=role_name, + status=status) + +- result = [ +- r for r in role_status if r[u'role_servrole'] != "IPA master"] ++ # Don't display "IPA master" information unless the role is ++ # requested explicitly. All servers are considered IPA masters, ++ # except for replicas during installation. ++ if options.get('include_master') or role_name == "IPA master": ++ result = role_status ++ else: ++ result = [ ++ r for r in role_status ++ if r[u'role_servrole'] != "IPA master" ++ ] + return dict( + result=result, + count=len(result), +-- +2.17.1 + diff --git a/SOURCES/0012-Fix-cert-find-for-CA-less-installations.patch b/SOURCES/0012-Fix-cert-find-for-CA-less-installations.patch deleted file mode 100644 index 106cea6..0000000 --- a/SOURCES/0012-Fix-cert-find-for-CA-less-installations.patch +++ /dev/null @@ -1,88 +0,0 @@ -From 6049b791567dad33be050b05bb08ef2040f473ee Mon Sep 17 00:00:00 2001 -From: Rob Crittenden -Date: Tue, 24 Oct 2017 15:43:08 -0400 -Subject: [PATCH] Fix cert-find for CA-less installations - -Change 49f9d799c171c7ae2ac546a33a353c2c40b4719c deferred the -detailed lookup until all certs were collected but introduced -a bug where the ra backend was always retrieved. This generated a -backtrace in a CA-less install because there is no ra backend in -the CA-less case. - -The deferral also removes the certificate value from the LDAP -search output resulting in only the serial number being displayed -unless --all is provided. Add a new class variable, -self.ca_enabled, to add an exception for the CA-less case. - -Fixes https://pagure.io/freeipa/issue/7202 - -Signed-off-by: Rob Crittenden -Reviewed-By: Stanislav Laznicka ---- - ipaserver/plugins/cert.py | 22 ++++++++++++++++++++-- - 1 file changed, 20 insertions(+), 2 deletions(-) - -diff --git a/ipaserver/plugins/cert.py b/ipaserver/plugins/cert.py -index bb11713317abad55577b1c280253ab5d6d68c508..c1d389217265f44e646ac27d9adc8d5524c74ce7 100644 ---- a/ipaserver/plugins/cert.py -+++ b/ipaserver/plugins/cert.py -@@ -1453,6 +1453,7 @@ class cert_find(Search, CertMethod): - - truncated = bool(truncated) - -+ ca_enabled = getattr(context, 'ca_enabled') - for entry in entries: - for attr in ('usercertificate', 'usercertificate;binary'): - for cert in entry.get(attr, []): -@@ -1466,7 +1467,12 @@ class cert_find(Search, CertMethod): - obj = result[issuer, serial_number] - except KeyError: - obj = {'serial_number': serial_number} -- if not pkey_only and all: -+ if not pkey_only and (all or not ca_enabled): -+ # Retrieving certificate details is now deferred -+ # until after all certificates are collected. -+ # For the case of CA-less we need to keep -+ # the certificate because getting it again later -+ # would require unnecessary LDAP searches. - obj['certificate'] = ( - base64.b64encode(cert).decode('ascii')) - result[issuer, serial_number] = obj -@@ -1480,6 +1486,11 @@ class cert_find(Search, CertMethod): - - def execute(self, criteria=None, all=False, raw=False, pkey_only=False, - no_members=True, timelimit=None, sizelimit=None, **options): -+ # Store ca_enabled status in the context to save making the API -+ # call multiple times. -+ ca_enabled = self.api.Command.ca_is_enabled()['result'] -+ setattr(context, 'ca_enabled', ca_enabled) -+ - if 'cacn' in options: - ca_obj = api.Command.ca_show(options['cacn'])['result'] - ca_sdn = unicode(ca_obj['ipacasubjectdn'][0]) -@@ -1534,7 +1545,8 @@ class cert_find(Search, CertMethod): - - if not pkey_only: - ca_objs = {} -- ra = self.api.Backend.ra -+ if ca_enabled: -+ ra = self.api.Backend.ra - - for key, obj in six.iteritems(result): - if all and 'cacn' in obj: -@@ -1561,6 +1573,12 @@ class cert_find(Search, CertMethod): - - if not raw: - self.obj._parse(obj, all) -+ if not ca_enabled and not all: -+ # For the case of CA-less don't display the full -+ # certificate unless requested. It is kept in the -+ # entry from _ldap_search() so its attributes can -+ # be retrieved. -+ obj.pop('certificate', None) - self.obj._fill_owners(obj) - - result = list(six.itervalues(result)) --- -2.13.6 - diff --git a/SOURCES/0012-Only-create-DNS-SRV-records-for-ready-server.patch b/SOURCES/0012-Only-create-DNS-SRV-records-for-ready-server.patch new file mode 100644 index 0000000..25c3bb8 --- /dev/null +++ b/SOURCES/0012-Only-create-DNS-SRV-records-for-ready-server.patch @@ -0,0 +1,49 @@ +From 7297060d59534cff6a703ad95c68bf20e53c14ae Mon Sep 17 00:00:00 2001 +From: Christian Heimes +Date: Thu, 5 Jul 2018 23:59:06 +0200 +Subject: [PATCH] Only create DNS SRV records for ready server + +When installing multiple replicas in parallel, one replica may create +SRV entries for other replicas, although the replicas aren't fully +installed yet. This may cause some services to connect to a server, that +isn't ready to serve requests. + +The DNS IPASystemRecords framework now skips all servers that aren't +ready IPA masters. + +See: https://pagure.io/freeipa/issue/7566 +Signed-off-by: Christian Heimes +Reviewed-By: Alexander Bokovoy +--- + ipaserver/dns_data_management.py | 8 ++++++-- + 1 file changed, 6 insertions(+), 2 deletions(-) + +diff --git a/ipaserver/dns_data_management.py b/ipaserver/dns_data_management.py +index bdf83de0cd76d8b571b5c2190ef8c4d63bcbf2d7..675dd481b461aa14d8adf8393a2168ac84ecac86 100644 +--- a/ipaserver/dns_data_management.py ++++ b/ipaserver/dns_data_management.py +@@ -96,7 +96,9 @@ class IPASystemRecords(object): + self.servers_data = {} + + servers_result = self.api_instance.Command.server_find( +- no_members=False)['result'] ++ no_members=False, ++ servrole=u"IPA master", # only active, fully installed masters ++ )['result'] + for s in servers_result: + weight, location, roles = self.__get_server_attrs(s) + self.servers_data[s['cn'][0]] = { +@@ -348,7 +350,9 @@ class IPASystemRecords(object): + zone_obj = zone.Zone(self.domain_abs, relativize=False) + if servers is None: + servers_result = self.api_instance.Command.server_find( +- pkey_only=True)['result'] ++ pkey_only=True, ++ servrole=u"IPA master", # only fully installed masters ++ )['result'] + servers = [s['cn'][0] for s in servers_result] + + locations_result = self.api_instance.Command.location_find()['result'] +-- +2.17.1 + diff --git a/SOURCES/0013-Delay-enabling-services-until-end-of-installer.patch b/SOURCES/0013-Delay-enabling-services-until-end-of-installer.patch new file mode 100644 index 0000000..b44176e --- /dev/null +++ b/SOURCES/0013-Delay-enabling-services-until-end-of-installer.patch @@ -0,0 +1,601 @@ +From 4caf0c14ad38a1dae494489b13f4109cdb3ba340 Mon Sep 17 00:00:00 2001 +From: Christian Heimes +Date: Fri, 6 Jul 2018 00:04:39 +0200 +Subject: [PATCH] Delay enabling services until end of installer + +Service entries in cn=FQDN,cn=masters,cn=ipa,cn=etc are no longer +created as enabled. Instead they are flagged as configuredService. At +the very end of the installer, the service entries are switched from +configured to enabled service. + +- SRV records are created at the very end of the installer. +- Dogtag installer only picks fully installed servers +- Certmonger ignores all configured but not yet enabled servers. + +Fixes: https://pagure.io/freeipa/issue/7566 +Signed-off-by: Christian Heimes +Reviewed-By: Alexander Bokovoy +--- + install/tools/ipa-adtrust-install | 6 +- + install/tools/ipa-ca-install | 12 ++- + install/tools/ipa-dns-install | 4 + + ipaserver/dns_data_management.py | 18 +++-- + ipaserver/install/adtrustinstance.py | 6 +- + ipaserver/install/bindinstance.py | 8 +- + ipaserver/install/cainstance.py | 2 +- + ipaserver/install/dnskeysyncinstance.py | 4 +- + ipaserver/install/httpinstance.py | 4 +- + ipaserver/install/ipa_kra_install.py | 7 ++ + ipaserver/install/krainstance.py | 2 +- + ipaserver/install/krbinstance.py | 4 +- + ipaserver/install/odsexporterinstance.py | 4 +- + ipaserver/install/opendnssecinstance.py | 6 +- + ipaserver/install/server/install.py | 18 +++-- + ipaserver/install/server/replicainstall.py | 8 +- + ipaserver/install/service.py | 88 ++++++++++++++++++++-- + ipaserver/plugins/serverrole.py | 7 +- + 18 files changed, 161 insertions(+), 47 deletions(-) + +diff --git a/install/tools/ipa-adtrust-install b/install/tools/ipa-adtrust-install +index d4e5d4c09cf6b7c1521bcecb79bb6fd7235fc799..a870d136e242affe6627cd4c44a173a80a9ab1c6 100755 +--- a/install/tools/ipa-adtrust-install ++++ b/install/tools/ipa-adtrust-install +@@ -32,7 +32,7 @@ import six + from optparse import SUPPRESS_HELP # pylint: disable=deprecated-module + + from ipalib.install import sysrestore +-from ipaserver.install import adtrust ++from ipaserver.install import adtrust, service + from ipaserver.install.installutils import ( + read_password, + check_server_configuration, +@@ -212,6 +212,10 @@ def main(): + adtrust.install_check(True, options, api) + adtrust.install(True, options, fstore, api) + ++ # Enable configured services and update DNS SRV records ++ service.enable_services(api.env.host) ++ api.Command.dns_update_system_records() ++ + print(""" + ============================================================================= + Setup complete +diff --git a/install/tools/ipa-ca-install b/install/tools/ipa-ca-install +index e4e24fb8c5261e77dd9d3e89cbe42dba519b932e..f78f43d94981d29939a247f3c492c5e7340298ea 100755 +--- a/install/tools/ipa-ca-install ++++ b/install/tools/ipa-ca-install +@@ -332,18 +332,26 @@ def main(): + ) + api.finalize() + api.Backend.ldap2.connect() +- + domain_level = dsinstance.get_domain_level(api) ++ + if domain_level > DOMAIN_LEVEL_0: + promote(safe_options, options, filename) + else: + install(safe_options, options, filename) + ++ # pki-spawn restarts 389-DS, reconnect ++ api.Backend.ldap2.close() ++ api.Backend.ldap2.connect() ++ ++ # Enable configured services and update DNS SRV records ++ service.enable_services(api.env.host) ++ api.Command.dns_update_system_records() ++ api.Backend.ldap2.disconnect() ++ + # execute ipactl to refresh services status + ipautil.run(['ipactl', 'start', '--ignore-service-failures'], + raiseonerr=False) + +- api.Backend.ldap2.disconnect() + + fail_message = ''' + Your system may be partly configured. +diff --git a/install/tools/ipa-dns-install b/install/tools/ipa-dns-install +index a7f136b16ab4871518f5fd776d49e55c2364c54e..57dde5a5da4fad162c93e9e0416b54961de4c1e3 100755 +--- a/install/tools/ipa-dns-install ++++ b/install/tools/ipa-dns-install +@@ -37,6 +37,7 @@ from ipapython.config import IPAOptionParser + from ipapython.ipa_log_manager import standard_logging_setup + + from ipaserver.install import dns as dns_installer ++from ipaserver.install import service + + logger = logging.getLogger(os.path.basename(__file__)) + +@@ -148,6 +149,9 @@ def main(): + + dns_installer.install_check(True, api, False, options, hostname=api.env.host) + dns_installer.install(True, False, options) ++ # Enable configured services and update DNS SRV records ++ service.enable_services(api.env.host) ++ api.Command.dns_update_system_records() + + # execute ipactl to refresh services status + ipautil.run(['ipactl', 'start', '--ignore-service-failures'], +diff --git a/ipaserver/dns_data_management.py b/ipaserver/dns_data_management.py +index 675dd481b461aa14d8adf8393a2168ac84ecac86..673397ef2b2252f431eec1f3e1f71dc45ff87511 100644 +--- a/ipaserver/dns_data_management.py ++++ b/ipaserver/dns_data_management.py +@@ -68,11 +68,11 @@ class IPASystemRecords(object): + PRIORITY_HIGH = 0 + PRIORITY_LOW = 50 + +- def __init__(self, api_instance): ++ def __init__(self, api_instance, all_servers=False): + self.api_instance = api_instance + self.domain_abs = DNSName(self.api_instance.env.domain).make_absolute() + self.servers_data = {} +- self.__init_data() ++ self.__init_data(all_servers=all_servers) + + def reload_data(self): + """ +@@ -92,14 +92,16 @@ class IPASystemRecords(object): + def __get_location_suffix(self, location): + return location + DNSName('_locations') + self.domain_abs + +- def __init_data(self): ++ def __init_data(self, all_servers=False): + self.servers_data = {} + +- servers_result = self.api_instance.Command.server_find( +- no_members=False, +- servrole=u"IPA master", # only active, fully installed masters +- )['result'] +- for s in servers_result: ++ kwargs = dict(no_members=False) ++ if not all_servers: ++ # only active, fully installed masters] ++ kwargs["servrole"] = u"IPA master" ++ servers = self.api_instance.Command.server_find(**kwargs) ++ ++ for s in servers['result']: + weight, location, roles = self.__get_server_attrs(s) + self.servers_data[s['cn'][0]] = { + 'weight': weight, +diff --git a/ipaserver/install/adtrustinstance.py b/ipaserver/install/adtrustinstance.py +index a075801ebec20ea8277445e0bac788c06e0b0a91..2da46d67495014fb38e5ee8c6a98ede93ef8762d 100644 +--- a/ipaserver/install/adtrustinstance.py ++++ b/ipaserver/install/adtrustinstance.py +@@ -581,7 +581,7 @@ class ADTRUSTInstance(service.Service): + self.print_msg(err_msg) + self.print_msg("Add the following service records to your DNS " \ + "server for DNS zone %s: " % zone) +- system_records = IPASystemRecords(api) ++ system_records = IPASystemRecords(api, all_servers=True) + adtrust_records = system_records.get_base_records( + [self.fqdn], ["AD trust controller"], + include_master_role=False, include_kerberos_realm=False) +@@ -736,12 +736,12 @@ class ADTRUSTInstance(service.Service): + # Note that self.dm_password is None for ADTrustInstance because + # we ensure to be called as root and using ldapi to use autobind + try: +- self.ldap_enable('ADTRUST', self.fqdn, None, self.suffix) ++ self.ldap_configure('ADTRUST', self.fqdn, None, self.suffix) + except (ldap.ALREADY_EXISTS, errors.DuplicateEntry): + logger.info("ADTRUST Service startup entry already exists.") + + try: +- self.ldap_enable('EXTID', self.fqdn, None, self.suffix) ++ self.ldap_configure('EXTID', self.fqdn, None, self.suffix) + except (ldap.ALREADY_EXISTS, errors.DuplicateEntry): + logger.info("EXTID Service startup entry already exists.") + +diff --git a/ipaserver/install/bindinstance.py b/ipaserver/install/bindinstance.py +index 203a6405f815d47c0dc33977e77012a2c85916ff..7c858aab4417ccf3a4999fcaaa1c7e0f93464e4d 100644 +--- a/ipaserver/install/bindinstance.py ++++ b/ipaserver/install/bindinstance.py +@@ -669,7 +669,7 @@ class BindInstance(service.Service): + return normalize_zone(self.host_domain) == normalize_zone(self.domain) + + def create_file_with_system_records(self): +- system_records = IPASystemRecords(self.api) ++ system_records = IPASystemRecords(self.api, all_servers=True) + text = u'\n'.join( + IPASystemRecords.records_list_from_zone( + system_records.get_base_records() +@@ -746,7 +746,7 @@ class BindInstance(service.Service): + # Instead we reply on the IPA init script to start only enabled + # components as found in our LDAP configuration tree + try: +- self.ldap_enable('DNS', self.fqdn, None, self.suffix) ++ self.ldap_configure('DNS', self.fqdn, None, self.suffix) + except errors.DuplicateEntry: + # service already exists (forced DNS reinstall) + # don't crash, just report error +@@ -1180,7 +1180,9 @@ class BindInstance(service.Service): + except ValueError as error: + logger.debug('%s', error) + +- # disabled by default, by ldap_enable() ++ installutils.rmtree(paths.BIND_LDAP_DNS_IPA_WORKDIR) ++ ++ # disabled by default, by ldap_configure() + if enabled: + self.enable() + else: +diff --git a/ipaserver/install/cainstance.py b/ipaserver/install/cainstance.py +index b58fbb4c881d247d6b5fb661f4085ec82c3cc811..51fdbe9c61e06ab9d72d78aee8786f9bceca137b 100644 +--- a/ipaserver/install/cainstance.py ++++ b/ipaserver/install/cainstance.py +@@ -1258,7 +1258,7 @@ class CAInstance(DogtagInstance): + config = ['caRenewalMaster'] + else: + config = [] +- self.ldap_enable('CA', self.fqdn, None, basedn, config) ++ self.ldap_configure('CA', self.fqdn, None, basedn, config) + + def setup_lightweight_ca_key_retrieval(self): + if sysupgrade.get_upgrade_state('dogtag', 'setup_lwca_key_retrieval'): +diff --git a/ipaserver/install/dnskeysyncinstance.py b/ipaserver/install/dnskeysyncinstance.py +index b865ee8aa79c17502a3784878f8f6f45d05213a6..2e773f3adae8130f578e6f3fbfe8c3a414d523cb 100644 +--- a/ipaserver/install/dnskeysyncinstance.py ++++ b/ipaserver/install/dnskeysyncinstance.py +@@ -382,8 +382,8 @@ class DNSKeySyncInstance(service.Service): + + def __enable(self): + try: +- self.ldap_enable('DNSKeySync', self.fqdn, None, +- self.suffix, self.extra_config) ++ self.ldap_configure('DNSKeySync', self.fqdn, None, ++ self.suffix, self.extra_config) + except errors.DuplicateEntry: + logger.error("DNSKeySync service already exists") + +diff --git a/ipaserver/install/httpinstance.py b/ipaserver/install/httpinstance.py +index bdd79b1dafda7de664eed664a18bf36c541212bc..0b7023c2f1b0feb996e0dd0adbefbd49c51da757 100644 +--- a/ipaserver/install/httpinstance.py ++++ b/ipaserver/install/httpinstance.py +@@ -196,7 +196,7 @@ class HTTPInstance(service.Service): + # We do not let the system start IPA components on its own, + # Instead we reply on the IPA init script to start only enabled + # components as found in our LDAP configuration tree +- self.ldap_enable('HTTP', self.fqdn, None, self.suffix) ++ self.ldap_configure('HTTP', self.fqdn, None, self.suffix) + + def configure_selinux_for_httpd(self): + try: +@@ -609,7 +609,7 @@ class HTTPInstance(service.Service): + if running: + self.restart() + +- # disabled by default, by ldap_enable() ++ # disabled by default, by ldap_configure() + if enabled: + self.enable() + +diff --git a/ipaserver/install/ipa_kra_install.py b/ipaserver/install/ipa_kra_install.py +index 07e11ea69ded8832015dd69ea43ff338c5f9df95..b536685f5f1f3fccab07fd37aa001958e2d38420 100644 +--- a/ipaserver/install/ipa_kra_install.py ++++ b/ipaserver/install/ipa_kra_install.py +@@ -227,4 +227,11 @@ class KRAInstaller(KRAInstall): + logger.error('%s', dedent(self.FAIL_MESSAGE)) + raise + ++ # pki-spawn restarts 389-DS, reconnect ++ api.Backend.ldap2.close() ++ api.Backend.ldap2.connect() ++ ++ # Enable configured services and update DNS SRV records ++ service.enable_services(api.env.host) ++ api.Command.dns_update_system_records() + api.Backend.ldap2.disconnect() +diff --git a/ipaserver/install/krainstance.py b/ipaserver/install/krainstance.py +index 9483f0ec4edbabea0f7eff0dd5dd223377653536..c1daa2869b7cba79e29d2db61c090c145304397f 100644 +--- a/ipaserver/install/krainstance.py ++++ b/ipaserver/install/krainstance.py +@@ -392,4 +392,4 @@ class KRAInstance(DogtagInstance): + directives[nickname], cert) + + def __enable_instance(self): +- self.ldap_enable('KRA', self.fqdn, None, self.suffix) ++ self.ldap_configure('KRA', self.fqdn, None, self.suffix) +diff --git a/ipaserver/install/krbinstance.py b/ipaserver/install/krbinstance.py +index a356d5e0c1b96dc6511c335fc22a326a2133bdd8..33d66fb94b0a1f7571b22120e5159a0e0ad2e675 100644 +--- a/ipaserver/install/krbinstance.py ++++ b/ipaserver/install/krbinstance.py +@@ -242,7 +242,7 @@ class KrbInstance(service.Service): + # We do not let the system start IPA components on its own, + # Instead we reply on the IPA init script to start only enabled + # components as found in our LDAP configuration tree +- self.ldap_enable('KDC', self.fqdn, None, self.suffix) ++ self.ldap_configure('KDC', self.fqdn, None, self.suffix) + + def __start_instance(self): + try: +@@ -607,7 +607,7 @@ class KrbInstance(service.Service): + except ValueError as error: + logger.debug("%s", error) + +- # disabled by default, by ldap_enable() ++ # disabled by default, by ldap_configure() + if enabled: + self.enable() + +diff --git a/ipaserver/install/odsexporterinstance.py b/ipaserver/install/odsexporterinstance.py +index b301a167f80d171c0dd0e6282a6021fcbdca8f9e..4856f5642d8e2c4c9e9089cd44a85e759c4c6ca0 100644 +--- a/ipaserver/install/odsexporterinstance.py ++++ b/ipaserver/install/odsexporterinstance.py +@@ -73,8 +73,8 @@ class ODSExporterInstance(service.Service): + def __enable(self): + + try: +- self.ldap_enable('DNSKeyExporter', self.fqdn, None, +- self.suffix) ++ self.ldap_configure('DNSKeyExporter', self.fqdn, None, ++ self.suffix) + except errors.DuplicateEntry: + logger.error("DNSKeyExporter service already exists") + +diff --git a/ipaserver/install/opendnssecinstance.py b/ipaserver/install/opendnssecinstance.py +index d608294cbbab9179d95b2333323f5d378940a936..0337bb22fea44f95ee9077423136353a991325db 100644 +--- a/ipaserver/install/opendnssecinstance.py ++++ b/ipaserver/install/opendnssecinstance.py +@@ -140,8 +140,8 @@ class OpenDNSSECInstance(service.Service): + + def __enable(self): + try: +- self.ldap_enable('DNSSEC', self.fqdn, None, +- self.suffix, self.extra_config) ++ self.ldap_configure('DNSSEC', self.fqdn, None, ++ self.suffix, self.extra_config) + except errors.DuplicateEntry: + logger.error("DNSSEC service already exists") + +@@ -372,7 +372,7 @@ class OpenDNSSECInstance(service.Service): + + self.restore_state("kasp_db_configured") # just eat state + +- # disabled by default, by ldap_enable() ++ # disabled by default, by ldap_configure() + if enabled: + self.enable() + +diff --git a/ipaserver/install/server/install.py b/ipaserver/install/server/install.py +index e96ae97c74ee1598683d1ef3f2570e8de93c9943..a341408f78f24055d807ae49c8a0cda81bfb3ec4 100644 +--- a/ipaserver/install/server/install.py ++++ b/ipaserver/install/server/install.py +@@ -870,14 +870,6 @@ def install(installer): + + if options.setup_dns: + dns.install(False, False, options) +- else: +- # Create a BIND instance +- bind = bindinstance.BindInstance(fstore) +- bind.setup(host_name, ip_addresses, realm_name, +- domain_name, (), 'first', (), +- zonemgr=options.zonemgr, +- no_dnssec_validation=options.no_dnssec_validation) +- bind.create_file_with_system_records() + + if options.setup_adtrust: + adtrust.install(False, options, fstore, api) +@@ -906,6 +898,16 @@ def install(installer): + except Exception: + raise ScriptError("Configuration of client side components failed!") + ++ # Enable configured services and update DNS SRV records ++ service.enable_services(host_name) ++ api.Command.dns_update_system_records() ++ ++ if not options.setup_dns: ++ # After DNS and AD trust are configured and services are ++ # enabled, create a dummy instance to dump DNS configuration. ++ bind = bindinstance.BindInstance(fstore) ++ bind.create_file_with_system_records() ++ + # Everything installed properly, activate ipa service. + services.knownservices.ipa.enable() + +diff --git a/ipaserver/install/server/replicainstall.py b/ipaserver/install/server/replicainstall.py +index 33f3ae9e616b34a3ab0ff8e4257552855e817e7c..0bf3568a300a133fa505dc8fc339c6677f9c5f73 100644 +--- a/ipaserver/install/server/replicainstall.py ++++ b/ipaserver/install/server/replicainstall.py +@@ -1520,14 +1520,11 @@ def install(installer): + + if options.setup_dns: + dns.install(False, True, options, api) +- else: +- api.Command.dns_update_system_records() + + if options.setup_adtrust: + adtrust.install(False, options, fstore, api) + + ca_servers = service.find_providing_servers('CA', api.Backend.ldap2, api) +- api.Backend.ldap2.disconnect() + + if not promote: + # Call client install script +@@ -1556,6 +1553,11 @@ def install(installer): + # remove the extracted replica file + remove_replica_info_dir(installer) + ++ # Enable configured services and update DNS SRV records ++ service.enable_services(config.host_name) ++ api.Command.dns_update_system_records() ++ api.Backend.ldap2.disconnect() ++ + # Everything installed properly, activate ipa service. + services.knownservices.ipa.enable() + +diff --git a/ipaserver/install/service.py b/ipaserver/install/service.py +index 4c320de9d64676373cd41ff839889f2448a19a46..0106379ea38e4a3fef8436256d6f315f524b8dee 100644 +--- a/ipaserver/install/service.py ++++ b/ipaserver/install/service.py +@@ -27,6 +27,7 @@ import socket + import datetime + import traceback + import tempfile ++import warnings + + import six + +@@ -62,6 +63,10 @@ SERVICE_LIST = { + 'DNSKeySync': ('ipa-dnskeysyncd', 110), + } + ++CONFIGURED_SERVICE = u'configuredService' ++ENABLED_SERVICE = 'enabledService' ++ ++ + def print_msg(message, output_fd=sys.stdout): + logger.debug("%s", message) + output_fd.write(message) +@@ -123,7 +128,7 @@ def find_providing_servers(svcname, conn, api): + """ + dn = DN(('cn', 'masters'), ('cn', 'ipa'), ('cn', 'etc'), api.env.basedn) + query_filter = conn.make_filter({'objectClass': 'ipaConfigObject', +- 'ipaConfigString': 'enabledService', ++ 'ipaConfigString': ENABLED_SERVICE, + 'cn': svcname}, rules='&') + try: + entries, _trunc = conn.find_entries(filter=query_filter, base_dn=dn) +@@ -232,6 +237,51 @@ def set_service_entry_config(name, fqdn, config_values, + raise e + + ++def enable_services(fqdn): ++ """Change all configured services to enabled ++ ++ Server.ldap_configure() only marks a service as configured. Services ++ are enabled at the very end of installation. ++ ++ Note: DNS records must be updated with dns_update_system_records, too. ++ ++ :param fqdn: hostname of server ++ """ ++ ldap2 = api.Backend.ldap2 ++ search_base = DN(('cn', fqdn), api.env.container_masters, api.env.basedn) ++ search_filter = ldap2.make_filter( ++ { ++ 'objectClass': 'ipaConfigObject', ++ 'ipaConfigString': CONFIGURED_SERVICE ++ }, ++ rules='&' ++ ) ++ entries = ldap2.get_entries( ++ search_base, ++ filter=search_filter, ++ scope=api.Backend.ldap2.SCOPE_ONELEVEL, ++ attrs_list=['cn', 'ipaConfigString'] ++ ) ++ for entry in entries: ++ name = entry['cn'] ++ cfgstrings = entry.setdefault('ipaConfigString', []) ++ for value in list(cfgstrings): ++ if value.lower() == CONFIGURED_SERVICE.lower(): ++ cfgstrings.remove(value) ++ if not case_insensitive_attr_has_value(cfgstrings, ENABLED_SERVICE): ++ cfgstrings.append(ENABLED_SERVICE) ++ ++ try: ++ ldap2.update_entry(entry) ++ except errors.EmptyModlist: ++ logger.debug("Nothing to do for service %s", name) ++ except Exception: ++ logger.exception("failed to set service %s config values", name) ++ raise ++ else: ++ logger.debug("Enabled service %s for %s", name, fqdn) ++ ++ + class Service(object): + def __init__(self, service_name, service_desc=None, sstore=None, + fstore=None, api=api, realm_name=None, +@@ -538,7 +588,35 @@ class Service(object): + self.steps = [] + + def ldap_enable(self, name, fqdn, dm_password=None, ldap_suffix='', +- config=[]): ++ config=()): ++ """Legacy function, all services should use ldap_configure() ++ """ ++ warnings.warn( ++ "ldap_enable is deprecated, use ldap_configure instead.", ++ DeprecationWarning, ++ stacklevel=2 ++ ) ++ self._ldap_enable(ENABLED_SERVICE, name, fqdn, ldap_suffix, config) ++ ++ def ldap_configure(self, name, fqdn, dm_password=None, ldap_suffix='', ++ config=()): ++ """Create or modify service entry in cn=masters,cn=ipa,cn=etc ++ ++ Contrary to ldap_enable(), the method only sets ++ ipaConfigString=configuredService. ipaConfigString=enabledService ++ is set at the very end of the installation process, to ensure that ++ other machines see this master/replica after it is fully installed. ++ ++ To switch all configured services to enabled, use:: ++ ++ ipaserver.install.service.enable_services(api.env.host) ++ api.Command.dns_update_system_records() ++ """ ++ self._ldap_enable( ++ CONFIGURED_SERVICE, name, fqdn, ldap_suffix, config ++ ) ++ ++ def _ldap_enable(self, value, name, fqdn, ldap_suffix, config): + extra_config_opts = [ + ' '.join([u'startOrder', unicode(SERVICE_LIST[name][1])]) + ] +@@ -549,7 +627,7 @@ class Service(object): + set_service_entry_config( + name, + fqdn, +- [u'enabledService'], ++ [value], + ldap_suffix=ldap_suffix, + post_add_config=extra_config_opts) + +@@ -575,7 +653,7 @@ class Service(object): + + # case insensitive + for value in entry.get('ipaConfigString', []): +- if value.lower() == u'enabledservice': ++ if value.lower() == ENABLED_SERVICE: + entry['ipaConfigString'].remove(value) + break + +@@ -688,7 +766,7 @@ class SimpleServiceInstance(Service): + if self.gensvc_name == None: + self.enable() + else: +- self.ldap_enable(self.gensvc_name, self.fqdn, None, self.suffix) ++ self.ldap_configure(self.gensvc_name, self.fqdn, None, self.suffix) + + def is_installed(self): + return self.service.is_installed() +diff --git a/ipaserver/plugins/serverrole.py b/ipaserver/plugins/serverrole.py +index 5b7ccfb342d0a54bfd6f2cdc53c7d31201ed5989..199978000ce8cf783bda50c46b7c9fa109f70ad6 100644 +--- a/ipaserver/plugins/serverrole.py ++++ b/ipaserver/plugins/serverrole.py +@@ -15,16 +15,21 @@ IPA server roles + """) + _(""" + Get status of roles (DNS server, CA, etc.) provided by IPA masters. + """) + _(""" ++The status of a role is either enabled, configured, or absent. ++""") + _(""" + EXAMPLES: + """) + _(""" + Show status of 'DNS server' role on a server: + ipa server-role-show ipa.example.com "DNS server" + """) + _(""" + Show status of all roles containing 'AD' on a server: +- ipa server-role-find --server ipa.example.com --role='AD' ++ ipa server-role-find --server ipa.example.com --role="AD trust controller" + """) + _(""" + Show status of all configured roles on a server: + ipa server-role-find ipa.example.com ++""") + _(""" ++ Show implicit IPA master role: ++ ipa server-role-find --include-master + """) + + +-- +2.17.1 + diff --git a/SOURCES/0013-Fix-ipa-restore-python2.patch b/SOURCES/0013-Fix-ipa-restore-python2.patch deleted file mode 100644 index 573fb00..0000000 --- a/SOURCES/0013-Fix-ipa-restore-python2.patch +++ /dev/null @@ -1,39 +0,0 @@ -From 276a0dfa90be417a0ce37b15027f716baa97a453 Mon Sep 17 00:00:00 2001 -From: Florence Blanc-Renaud -Date: Thu, 2 Nov 2017 09:34:43 +0100 -Subject: [PATCH] Fix ipa-restore (python2) - -In order to stop tracking LDAP server cert, ipa-restore is using -dse.ldif to find the certificate name. But when ipa-server-install ---uninstall has been called, the file does not exist, leading to a -IOError exception (regression introduced by 87540fe). - -The ipa-restore code properly catches the exception in python3 because -IOError is a subclass of OSError, but in python2 this is not the case. -The fix catches IOError and OSError to work properly with both version. - -Fixes: -https://pagure.io/freeipa/issue/7231 - -Reviewed-By: Rob Crittenden -Reviewed-By: Tomas Krizek ---- - ipaserver/install/ipa_restore.py | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/ipaserver/install/ipa_restore.py b/ipaserver/install/ipa_restore.py -index 96fc493c774f5de4c8149bf477cb66ec4960de4f..923b1d6696d33c0bb07ca018b53dd3dabcc191aa 100644 ---- a/ipaserver/install/ipa_restore.py -+++ b/ipaserver/install/ipa_restore.py -@@ -815,7 +815,7 @@ class Restore(admintool.AdminTool): - try: - dsinstance.DsInstance().stop_tracking_certificates( - installutils.realm_to_serverid(api.env.realm)) -- except OSError: -+ except (OSError, IOError): - # When IPA is not installed, DS NSS DB does not exist - pass - --- -2.13.6 - diff --git a/SOURCES/0014-Backup-ipa-custodia-conf-and-keys.patch b/SOURCES/0014-Backup-ipa-custodia-conf-and-keys.patch deleted file mode 100644 index fb72527..0000000 --- a/SOURCES/0014-Backup-ipa-custodia-conf-and-keys.patch +++ /dev/null @@ -1,154 +0,0 @@ -From 0e1b5a65ed06b2213deebb0ea1e5fb8422223426 Mon Sep 17 00:00:00 2001 -From: Christian Heimes -Date: Wed, 8 Nov 2017 15:15:30 +0100 -Subject: [PATCH] Backup ipa-custodia conf and keys - -https://pagure.io/freeipa/issue/7247 - -Signed-off-by: Christian Heimes -Reviewed-By: Simo Sorce ---- - install/share/custodia.conf.template | 2 +- - ipaplatform/base/paths.py | 1 + - ipapython/ipautil.py | 19 +++++++++++++++++++ - ipaserver/install/custodiainstance.py | 24 +++++++++++++----------- - ipaserver/install/ipa_backup.py | 2 ++ - ipatests/test_ipapython/test_ipautil.py | 7 +++++++ - 6 files changed, 43 insertions(+), 12 deletions(-) - -diff --git a/install/share/custodia.conf.template b/install/share/custodia.conf.template -index 855a1b3ba206e4ded8de80758b02473040096c7f..ee3c43ca7ec265aa09d250426bf4138bcfdf62b6 100644 ---- a/install/share/custodia.conf.template -+++ b/install/share/custodia.conf.template -@@ -16,7 +16,7 @@ header = GSS_NAME - handler = ipaserver.secrets.kem.IPAKEMKeys - paths = /keys - store = ipa --server_keys = $IPA_CUSTODIA_CONF_DIR/server.keys -+server_keys = $IPA_CUSTODIA_KEYS - - [store:ipa] - handler = ipaserver.secrets.store.IPASecStore -diff --git a/ipaplatform/base/paths.py b/ipaplatform/base/paths.py -index 804fddee60f787e161947bbe4b1914995257ceb4..42240a71066599ca8b36d10a9e5b23625f868977 100644 ---- a/ipaplatform/base/paths.py -+++ b/ipaplatform/base/paths.py -@@ -349,6 +349,7 @@ class BasePathNamespace(object): - NETWORK_MANAGER_CONFIG_DIR = '/etc/NetworkManager/conf.d' - IPA_CUSTODIA_CONF_DIR = '/etc/ipa/custodia' - IPA_CUSTODIA_CONF = '/etc/ipa/custodia/custodia.conf' -+ IPA_CUSTODIA_KEYS = '/etc/ipa/custodia/server.keys' - IPA_CUSTODIA_SOCKET = '/run/httpd/ipa-custodia.sock' - IPA_CUSTODIA_AUDIT_LOG = '/var/log/ipa-custodia.audit.log' - IPA_GETKEYTAB = '/usr/sbin/ipa-getkeytab' -diff --git a/ipapython/ipautil.py b/ipapython/ipautil.py -index cc52af6d9235cfbd597679231f63667b81a200b4..426b32ef05ab00dcbf37b1e58b6390accee33cb1 100644 ---- a/ipapython/ipautil.py -+++ b/ipapython/ipautil.py -@@ -307,6 +307,25 @@ def write_tmp_file(txt): - - return fd - -+ -+def flush_sync(f): -+ """Flush and fsync file to disk -+ -+ :param f: a file object with fileno and name -+ """ -+ # flush file buffer to file descriptor -+ f.flush() -+ # flush Kernel buffer to disk -+ os.fsync(f.fileno()) -+ # sync metadata in directory -+ dirname = os.path.dirname(os.path.abspath(f.name)) -+ dirfd = os.open(dirname, os.O_RDONLY | os.O_DIRECTORY) -+ try: -+ os.fsync(dirfd) -+ finally: -+ os.close(dirfd) -+ -+ - def shell_quote(string): - if isinstance(string, str): - return "'" + string.replace("'", "'\\''") + "'" -diff --git a/ipaserver/install/custodiainstance.py b/ipaserver/install/custodiainstance.py -index bc3cea7063dff183c85b4f6e8ced7567f691001d..0a90bb3954486b9773e3553e9981d2a8d0d4e44a 100644 ---- a/ipaserver/install/custodiainstance.py -+++ b/ipaserver/install/custodiainstance.py -@@ -25,8 +25,7 @@ class CustodiaInstance(SimpleServiceInstance): - def __init__(self, host_name=None, realm=None): - super(CustodiaInstance, self).__init__("ipa-custodia") - self.config_file = paths.IPA_CUSTODIA_CONF -- self.server_keys = os.path.join(paths.IPA_CUSTODIA_CONF_DIR, -- 'server.keys') -+ self.server_keys = paths.IPA_CUSTODIA_KEYS - self.ldap_uri = None - self.fqdn = host_name - self.realm = realm -@@ -35,16 +34,19 @@ class CustodiaInstance(SimpleServiceInstance): - template_file = os.path.basename(self.config_file) + '.template' - template = os.path.join(paths.USR_SHARE_IPA_DIR, template_file) - httpd_info = pwd.getpwnam(constants.HTTPD_USER) -- sub_dict = dict(IPA_CUSTODIA_CONF_DIR=paths.IPA_CUSTODIA_CONF_DIR, -- IPA_CUSTODIA_SOCKET=paths.IPA_CUSTODIA_SOCKET, -- IPA_CUSTODIA_AUDIT_LOG=paths.IPA_CUSTODIA_AUDIT_LOG, -- LDAP_URI=installutils.realm_to_ldapi_uri(self.realm), -- UID=httpd_info.pw_uid, GID=httpd_info.pw_gid) -+ sub_dict = dict( -+ IPA_CUSTODIA_CONF_DIR=paths.IPA_CUSTODIA_CONF_DIR, -+ IPA_CUSTODIA_KEYS=paths.IPA_CUSTODIA_KEYS, -+ IPA_CUSTODIA_SOCKET=paths.IPA_CUSTODIA_SOCKET, -+ IPA_CUSTODIA_AUDIT_LOG=paths.IPA_CUSTODIA_AUDIT_LOG, -+ LDAP_URI=installutils.realm_to_ldapi_uri(self.realm), -+ UID=httpd_info.pw_uid, -+ GID=httpd_info.pw_gid -+ ) - conf = ipautil.template_file(template, sub_dict) -- fd = open(self.config_file, "w+") -- fd.write(conf) -- fd.flush() -- fd.close() -+ with open(self.config_file, "w") as f: -+ f.write(conf) -+ ipautil.flush_sync(f) - - def create_instance(self): - suffix = ipautil.realm_to_suffix(self.realm) -diff --git a/ipaserver/install/ipa_backup.py b/ipaserver/install/ipa_backup.py -index f8cdd56d26636678279ba5afb423c5eef10c33d0..93b154330d3e6c8700c98860eb0c08f6841774bb 100644 ---- a/ipaserver/install/ipa_backup.py -+++ b/ipaserver/install/ipa_backup.py -@@ -181,6 +181,8 @@ class Backup(admintool.AdminTool): - paths.DNSSEC_SOFTHSM_PIN_SO, - paths.IPA_ODS_EXPORTER_KEYTAB, - paths.IPA_DNSKEYSYNCD_KEYTAB, -+ paths.IPA_CUSTODIA_KEYS, -+ paths.IPA_CUSTODIA_CONF, - paths.HOSTS, - ) + tuple( - os.path.join(paths.IPA_NSSDB_DIR, file) -diff --git a/ipatests/test_ipapython/test_ipautil.py b/ipatests/test_ipapython/test_ipautil.py -index 9c351bd0ed9cd96488ac74deadf97996668a75d2..5e1f58003e9f3cae2f0819ecc348ade2c367548b 100644 ---- a/ipatests/test_ipapython/test_ipautil.py -+++ b/ipatests/test_ipapython/test_ipautil.py -@@ -25,6 +25,7 @@ Test the `ipapython/ipautil.py` module. - import nose - import pytest - import six -+import tempfile - - from ipapython import ipautil - -@@ -478,3 +479,9 @@ def test_backcompat(): - assert rc is result.returncode - assert out is result.output - assert err is result.error_output -+ -+ -+def test_flush_sync(): -+ with tempfile.NamedTemporaryFile('wb+') as f: -+ f.write(b'data') -+ ipautil.flush_sync(f) --- -2.13.6 - diff --git a/SOURCES/0014-ipa-client-uninstall-clean-the-state-store-when-rest.patch b/SOURCES/0014-ipa-client-uninstall-clean-the-state-store-when-rest.patch new file mode 100644 index 0000000..4cd9653 --- /dev/null +++ b/SOURCES/0014-ipa-client-uninstall-clean-the-state-store-when-rest.patch @@ -0,0 +1,44 @@ +From 7f6f620ec43b5606413524d25d3cae1003930f55 Mon Sep 17 00:00:00 2001 +From: Florence Blanc-Renaud +Date: Fri, 6 Jul 2018 12:47:34 +0200 +Subject: [PATCH] ipa client uninstall: clean the state store when restoring + hostname + +When ipa client was installed with the --hostname= option, it stores +[network] +hostname = (current hostname) +in /var/lib/ipa-client/sysrestore/sysrestore.state and changes the hostname +from (current hostname) to the value provided in --hostname. + +During uninstall, the previous hostname is restored but the entry does +not get removed from sysrestore.state. As the uninstaller checks if all +entries from sysrestore.state have been restored, it warns that some +state has not been restored. + +The fix calls statestore.restore_state() instead of statestore.get_state() +as this method also clears the entry. + +https://pagure.io/freeipa/issue/7620 + +Reviewed-By: Rob Crittenden +Reviewed-By: Rob Crittenden +--- + ipaplatform/redhat/tasks.py | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/ipaplatform/redhat/tasks.py b/ipaplatform/redhat/tasks.py +index dcafd735c0161e5cd979ccb113863e7885a14ab3..6a4270defc9f444f76677bdf08d2a680649664bb 100644 +--- a/ipaplatform/redhat/tasks.py ++++ b/ipaplatform/redhat/tasks.py +@@ -380,7 +380,7 @@ class RedHatTaskNamespace(BaseTaskNamespace): + statestore.backup_state('network', 'hostname', old_hostname) + + def restore_hostname(self, fstore, statestore): +- old_hostname = statestore.get_state('network', 'hostname') ++ old_hostname = statestore.restore_state('network', 'hostname') + + if old_hostname is not None: + try: +-- +2.17.1 + diff --git a/SOURCES/0015-Fix-CA-topology-warning.patch b/SOURCES/0015-Fix-CA-topology-warning.patch new file mode 100644 index 0000000..c053de1 --- /dev/null +++ b/SOURCES/0015-Fix-CA-topology-warning.patch @@ -0,0 +1,65 @@ +From f637bcbe1066eec41e90b99d7a4ab12288f08a74 Mon Sep 17 00:00:00 2001 +From: Christian Heimes +Date: Fri, 6 Jul 2018 15:07:27 +0200 +Subject: [PATCH] Fix CA topology warning + +Commit 7284097eedef70dd556270732e6ab8e23501ce09 kept +find_providing_servers('CA') call before enable_services(). Therefore the +list of known CA servers did not contain the current replica. +ipa-replica-install on the first replica with --setup-ca still printed +the CA topology warning. + +See: https://pagure.io/freeipa/issue/7566 +Signed-off-by: Christian Heimes +Reviewed-By: Alexander Bokovoy +--- + ipaserver/install/server/replicainstall.py | 3 +-- + ipaserver/install/service.py | 4 ++-- + 2 files changed, 3 insertions(+), 4 deletions(-) + +diff --git a/ipaserver/install/server/replicainstall.py b/ipaserver/install/server/replicainstall.py +index 0bf3568a300a133fa505dc8fc339c6677f9c5f73..387d4ca85d9ad41db3ce2d9bc2ae67ba11836ada 100644 +--- a/ipaserver/install/server/replicainstall.py ++++ b/ipaserver/install/server/replicainstall.py +@@ -1524,8 +1524,6 @@ def install(installer): + if options.setup_adtrust: + adtrust.install(False, options, fstore, api) + +- ca_servers = service.find_providing_servers('CA', api.Backend.ldap2, api) +- + if not promote: + # Call client install script + service.print_msg("Configuring client side components") +@@ -1556,6 +1554,7 @@ def install(installer): + # Enable configured services and update DNS SRV records + service.enable_services(config.host_name) + api.Command.dns_update_system_records() ++ ca_servers = service.find_providing_servers('CA', api.Backend.ldap2, api) + api.Backend.ldap2.disconnect() + + # Everything installed properly, activate ipa service. +diff --git a/ipaserver/install/service.py b/ipaserver/install/service.py +index 0106379ea38e4a3fef8436256d6f315f524b8dee..39dc49e872c82961defc09c7240604bc1cba2228 100644 +--- a/ipaserver/install/service.py ++++ b/ipaserver/install/service.py +@@ -64,7 +64,7 @@ SERVICE_LIST = { + } + + CONFIGURED_SERVICE = u'configuredService' +-ENABLED_SERVICE = 'enabledService' ++ENABLED_SERVICE = u'enabledService' + + + def print_msg(message, output_fd=sys.stdout): +@@ -636,7 +636,7 @@ class Service(object): + + entry_dn = DN(('cn', name), ('cn', fqdn), ('cn', 'masters'), + ('cn', 'ipa'), ('cn', 'etc'), ldap_suffix) +- search_kw = {'ipaConfigString': u'enabledService'} ++ search_kw = {'ipaConfigString': ENABLED_SERVICE} + filter = api.Backend.ldap2.make_filter(search_kw) + try: + entries, _truncated = api.Backend.ldap2.find_entries( +-- +2.17.1 + diff --git a/SOURCES/0015-adtrust-filter-out-subdomains-when-defining-our-topo.patch b/SOURCES/0015-adtrust-filter-out-subdomains-when-defining-our-topo.patch deleted file mode 100644 index 8e7bbe8..0000000 --- a/SOURCES/0015-adtrust-filter-out-subdomains-when-defining-our-topo.patch +++ /dev/null @@ -1,66 +0,0 @@ -From efdbea05f716700d8ed659430a6b501b41de0e54 Mon Sep 17 00:00:00 2001 -From: Alexander Bokovoy -Date: Thu, 19 Oct 2017 13:21:05 +0300 -Subject: [PATCH] adtrust: filter out subdomains when defining our topology to - AD - -When definining a topology of a forest to be visible over a cross-forest -trust, we set *. as all-catch top level name already. - -This means that all DNS subdomains of the forest will already be matched -by this top level name (TLN). If we add more TLNs for subdomains, Active -Directory will respond with NT_STATUS_INVALID_PARAMETER. - -Filter out all subdomains of the forest root domain. All other realm -domains will be added with explicit TLN records. - -Also filter out single label domains. These aren't possible to add as -TLNs to Windows Server 2016 as it considers them incorrect. Given that -we do not allow single lable domains as part of freeIPA installs, this -is another layer of protection here. - -Fixes https://pagure.io/freeipa/issue/6666 - -Reviewed-By: Christian Heimes ---- - ipaserver/dcerpc.py | 16 ++++++++++++++++ - 1 file changed, 16 insertions(+) - -diff --git a/ipaserver/dcerpc.py b/ipaserver/dcerpc.py -index d684a17cabe43bbbd43d29f75f534b6e50fccd12..aa63cd9db0a1d47b5309cc6bed2ff7584760a39d 100644 ---- a/ipaserver/dcerpc.py -+++ b/ipaserver/dcerpc.py -@@ -50,6 +50,7 @@ import samba - - import ldap as _ldap - from ipapython import ipaldap -+from ipapython.dnsutil import DNSName - from dns import resolver, rdatatype - from dns.exception import DNSException - import pysss_nss_idmap -@@ -1589,7 +1590,22 @@ class TrustDomainJoins(object): - entry.single_value.get('modifytimestamp').timetuple() - )*1e7+116444736000000000) - -+ forest = DNSName(self.local_domain.info['dns_forest']) -+ # tforest is IPA forest. keep the line below for future checks -+ # tforest = DNSName(self.remote_domain.info['dns_forest']) - for dom in realm_domains['associateddomain']: -+ d = DNSName(dom) -+ -+ # We should skip all DNS subdomains of our forest -+ # because we are going to add *. TLN anyway -+ if forest.is_superdomain(d) and forest != d: -+ continue -+ -+ # We also should skip single label TLDs as they -+ # cannot be added as TLNs -+ if len(d.labels) == 1: -+ continue -+ - ftinfo = dict() - ftinfo['rec_name'] = dom - ftinfo['rec_time'] = trust_timestamp --- -2.13.6 - diff --git a/SOURCES/0016-Fix-ca-less-IPA-install-on-fips-mode.patch b/SOURCES/0016-Fix-ca-less-IPA-install-on-fips-mode.patch deleted file mode 100644 index 1f8d3dc..0000000 --- a/SOURCES/0016-Fix-ca-less-IPA-install-on-fips-mode.patch +++ /dev/null @@ -1,45 +0,0 @@ -From 8f35c1c705a7584cdcc9ad5c6fb15ba940ec3f4a Mon Sep 17 00:00:00 2001 -From: Florence Blanc-Renaud -Date: Thu, 23 Nov 2017 18:06:56 +0100 -Subject: [PATCH] Fix ca less IPA install on fips mode - -When ipa-server-install is run in fips mode and ca-less, the installer -fails when the keys are provided with --{http|dirsrv|pkinit}-cert-file -in a separate key file. - -The installer transforms the key into PKCS#8 format using -openssl pkcs8 -topk8 -but this command fails on a fips-enabled server, unless the options --v2 aes256 -v2prf hmacWithSHA256 -are also provided. - -Fixes: -https://pagure.io/freeipa/issue/7280 - -Reviewed-By: Christian Heimes -Reviewed-By: Christian Heimes ---- - ipapython/certdb.py | 4 ++++ - 1 file changed, 4 insertions(+) - -diff --git a/ipapython/certdb.py b/ipapython/certdb.py -index 114c58340253141706afa461ecaf87797562ca1d..f198811e0fd02c8925f0dcfa8764535b35ed29ed 100644 ---- a/ipapython/certdb.py -+++ b/ipapython/certdb.py -@@ -499,9 +499,13 @@ class NSSDatabase(object): - "Can't load private key from both %s and %s" % - (key_file, filename)) - -+ # the args -v2 aes256 -v2prf hmacWithSHA256 are needed -+ # on OpenSSL 1.0.2 (fips mode). As soon as FreeIPA -+ # requires OpenSSL 1.1.0 we'll be able to drop them - args = [ - OPENSSL, 'pkcs8', - '-topk8', -+ '-v2', 'aes256', '-v2prf', 'hmacWithSHA256', - '-passout', 'file:' + self.pwd_file, - ] - if ((label != 'PRIVATE KEY' and key_password) or --- -2.13.6 - diff --git a/SOURCES/0016-replicainstall-DS-SSL-replica-install-pick-right-cer.patch b/SOURCES/0016-replicainstall-DS-SSL-replica-install-pick-right-cer.patch new file mode 100644 index 0000000..32a3d45 --- /dev/null +++ b/SOURCES/0016-replicainstall-DS-SSL-replica-install-pick-right-cer.patch @@ -0,0 +1,55 @@ +From 44ec2e641c809afbed96bcde2a31388a37a7ec32 Mon Sep 17 00:00:00 2001 +From: Rob Crittenden +Date: Fri, 6 Jul 2018 09:26:19 -0400 +Subject: [PATCH] replicainstall: DS SSL replica install pick right certmonger + host + +Extend fix 0f31564b35aac250456233f98730811560eda664 to also move +the DS SSL setup so that the xmlrpc_uri is configured to point +to the remote master we are configuring against. + +https://pagure.io/freeipa/issue/7566 + +Signed-off-by: Rob Crittenden +Reviewed-By: Christian Heimes +--- + ipaserver/install/server/replicainstall.py | 14 +++++++------- + 1 file changed, 7 insertions(+), 7 deletions(-) + +diff --git a/ipaserver/install/server/replicainstall.py b/ipaserver/install/server/replicainstall.py +index 387d4ca85d9ad41db3ce2d9bc2ae67ba11836ada..542e1d4d145f266d6fd9ad8e0eaffcb12e8f6bc6 100644 +--- a/ipaserver/install/server/replicainstall.py ++++ b/ipaserver/install/server/replicainstall.py +@@ -1448,15 +1448,12 @@ def install(installer): + pkcs12_info=pkinit_pkcs12_info, + promote=promote) + +- # we now need to enable ssl on the ds +- ds.enable_ssl() +- + if promote: + # We need to point to the master when certmonger asks for +- # HTTP certificate. +- # During http installation, the HTTP/hostname principal is created +- # locally then the installer waits for the entry to appear on the +- # master selected for the installation. ++ # a DS or HTTP certificate. ++ # During http installation, the /hostname principal is ++ # created locally then the installer waits for the entry to appear ++ # on the master selected for the installation. + # In a later step, the installer requests a SSL certificate through + # Certmonger (and the op adds the principal if it does not exist yet). + # If xmlrpc_uri points to the soon-to-be replica, +@@ -1470,6 +1467,9 @@ def install(installer): + create_ipa_conf(fstore, config, ca_enabled, + master=config.master_host_name) + ++ # we now need to enable ssl on the ds ++ ds.enable_ssl() ++ + install_http( + config, + auto_redirect=not options.no_ui_redirect, +-- +2.17.1 + diff --git a/SOURCES/0017-Fix-race-condition-in-get_locations_records.patch b/SOURCES/0017-Fix-race-condition-in-get_locations_records.patch new file mode 100644 index 0000000..804894f --- /dev/null +++ b/SOURCES/0017-Fix-race-condition-in-get_locations_records.patch @@ -0,0 +1,80 @@ +From 72517c7b316f7cba93a3b2cdc6e2c69dad63a8b1 Mon Sep 17 00:00:00 2001 +From: Christian Heimes +Date: Fri, 6 Jul 2018 21:47:32 +0200 +Subject: [PATCH] Fix race condition in get_locations_records() + +The method IPASystemRecords.get_locations_records() has a race condition. +The IPASystemRecords object creates a mapping of server names to server +data. get_locations_records() uses server_find() again to get a list of +servers, but then operates on the cached dict of server names. + +In parallel replication case, the second server_find() call in +get_locations_records() can return additional servers. Since the rest of +the code operates on the cached data, the method then fails with a KeyError. + +server_data is now an OrderedDict to keep same sorting as with +server_find(). + +Fixes: https://pagure.io/freeipa/issue/7566 +Signed-off-by: Christian Heimes +Reviewed-By: Tibor Dudlak +--- + ipaserver/dns_data_management.py | 14 +++++--------- + 1 file changed, 5 insertions(+), 9 deletions(-) + +diff --git a/ipaserver/dns_data_management.py b/ipaserver/dns_data_management.py +index 673397ef2b2252f431eec1f3e1f71dc45ff87511..90c4d8536478599e5a35462c7f00ea4c958ff63a 100644 +--- a/ipaserver/dns_data_management.py ++++ b/ipaserver/dns_data_management.py +@@ -8,7 +8,7 @@ import logging + + import six + +-from collections import defaultdict ++from collections import defaultdict, OrderedDict + from dns import ( + rdata, + rdataclass, +@@ -71,7 +71,7 @@ class IPASystemRecords(object): + def __init__(self, api_instance, all_servers=False): + self.api_instance = api_instance + self.domain_abs = DNSName(self.api_instance.env.domain).make_absolute() +- self.servers_data = {} ++ self.servers_data = OrderedDict() + self.__init_data(all_servers=all_servers) + + def reload_data(self): +@@ -93,7 +93,7 @@ class IPASystemRecords(object): + return location + DNSName('_locations') + self.domain_abs + + def __init_data(self, all_servers=False): +- self.servers_data = {} ++ self.servers_data.clear() + + kwargs = dict(no_members=False) + if not all_servers: +@@ -328,7 +328,7 @@ class IPASystemRecords(object): + + zone_obj = zone.Zone(self.domain_abs, relativize=False) + if servers is None: +- servers = self.servers_data.keys() ++ servers = list(self.servers_data) + + for server in servers: + self._add_base_dns_records_for_server(zone_obj, server, +@@ -351,11 +351,7 @@ class IPASystemRecords(object): + """ + zone_obj = zone.Zone(self.domain_abs, relativize=False) + if servers is None: +- servers_result = self.api_instance.Command.server_find( +- pkey_only=True, +- servrole=u"IPA master", # only fully installed masters +- )['result'] +- servers = [s['cn'][0] for s in servers_result] ++ servers = list(self.servers_data) + + locations_result = self.api_instance.Command.location_find()['result'] + locations = [l['idnsname'][0] for l in locations_result] +-- +2.17.1 + diff --git a/SOURCES/0017-trust-detect-and-error-out-when-non-AD-trust-with-IP.patch b/SOURCES/0017-trust-detect-and-error-out-when-non-AD-trust-with-IP.patch deleted file mode 100644 index 8cf7475..0000000 --- a/SOURCES/0017-trust-detect-and-error-out-when-non-AD-trust-with-IP.patch +++ /dev/null @@ -1,281 +0,0 @@ -From a4e9e8f368c8a61d539e9da26e4a16a6234c138f Mon Sep 17 00:00:00 2001 -From: Alexander Bokovoy -Date: Fri, 17 Nov 2017 17:19:25 +0200 -Subject: [PATCH] trust: detect and error out when non-AD trust with IPA domain - name exists - -Quite often users choose wrong type of trust on Active Directory side -when setting up a trust to freeIPA. The trust type supported by freeIPA -is just a normal forest trust to another Active Directory. However, -some people follow old internet recipes that force using a trust to MIT -Kerberos realm. - -This is a wrong type of trust. Unfortunately, when someone used MIT -Kerberos realm trust, there is no way to programmatically remote the -trust from freeIPA side. As result, we have to detect such situation and -report an error. - -To do proper reporting, we need reuse some constants and trust type -names we use in IPA CLI/Web UI. These common components were moved to -a separate ipaserver/dcerpc_common.py module that is imported by both -ipaserver/plugins/trust.py and ipaserver/dcerpc.py. - -Fixes https://pagure.io/freeipa/issue/7264 - -Reviewed-By: Christian Heimes -Reviewed-By: Thierry Bordaz ---- - ipaserver/dcerpc.py | 37 +++++++++++++++-------- - ipaserver/dcerpc_common.py | 73 ++++++++++++++++++++++++++++++++++++++++++++++ - ipaserver/plugins/trust.py | 65 ++++++++++------------------------------- - 3 files changed, 113 insertions(+), 62 deletions(-) - create mode 100644 ipaserver/dcerpc_common.py - -diff --git a/ipaserver/dcerpc.py b/ipaserver/dcerpc.py -index aa63cd9db0a1d47b5309cc6bed2ff7584760a39d..ac1b2a34784df491a3851aa21bbadbec2297241c 100644 ---- a/ipaserver/dcerpc.py -+++ b/ipaserver/dcerpc.py -@@ -31,6 +31,10 @@ from ipapython import ipautil - from ipapython.ipa_log_manager import root_logger - from ipapython.dn import DN - from ipaserver.install import installutils -+from ipaserver.dcerpc_common import (TRUST_BIDIRECTIONAL, -+ TRUST_JOIN_EXTERNAL, -+ trust_type_string) -+ - from ipalib.util import normalize_name - - import os -@@ -77,15 +81,6 @@ The code in this module relies heavily on samba4-python package - and Samba4 python bindings. - """) - --# Both constants can be used as masks against trust direction --# because bi-directional has two lower bits set. --TRUST_ONEWAY = 1 --TRUST_BIDIRECTIONAL = 3 -- --# Trust join behavior --# External trust -- allow creating trust to a non-root domain in the forest --TRUST_JOIN_EXTERNAL = 1 -- - - def is_sid_valid(sid): - try: -@@ -151,6 +146,7 @@ pysss_type_key_translation_dict = { - pysss_nss_idmap.ID_BOTH: 'both', - } - -+ - class TrustTopologyConflictSolved(Exception): - """ - Internal trust error: raised when previously detected -@@ -1254,9 +1250,26 @@ class TrustDomainInstance(object): - dname = lsa.String() - dname.string = another_domain.info['dns_domain'] - res = self._pipe.QueryTrustedDomainInfoByName( -- self._policy_handle, -- dname, -- lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO) -+ self._policy_handle, -+ dname, -+ lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO -+ ) -+ if res.info_ex.trust_type != lsa.LSA_TRUST_TYPE_UPLEVEL: -+ msg = _('There is already a trust to {ipa_domain} with ' -+ 'unsupported type {trust_type}. Please remove ' -+ 'it manually on AD DC side.') -+ ttype = trust_type_string( -+ res.info_ex.trust_type, res.info_ex.trust_attributes -+ ) -+ err = unicode(msg).format( -+ ipa_domain=another_domain.info['dns_domain'], -+ trust_type=ttype) -+ -+ raise errors.ValidationError( -+ name=_('AD domain controller'), -+ error=err -+ ) -+ - self._pipe.DeleteTrustedDomain(self._policy_handle, - res.info_ex.sid) - except RuntimeError as e: -diff --git a/ipaserver/dcerpc_common.py b/ipaserver/dcerpc_common.py -new file mode 100644 -index 0000000000000000000000000000000000000000..526b025e3282c8a556088eb2ed1ba467b889b86c ---- /dev/null -+++ b/ipaserver/dcerpc_common.py -@@ -0,0 +1,73 @@ -+import six -+from ipalib import _ -+if six.PY3: -+ unicode = six.text_type -+ -+# Both constants can be used as masks against trust direction -+# because bi-directional has two lower bits set. -+TRUST_ONEWAY = 1 -+TRUST_BIDIRECTIONAL = 3 -+ -+# Trust join behavior -+# External trust -- allow creating trust to a non-root domain in the forest -+TRUST_JOIN_EXTERNAL = 1 -+ -+# We don't want to import any of Samba Python code here just for constants -+# Since these constants set in MS-ADTS, we can rely on their stability -+LSA_TRUST_ATTRIBUTE_NON_TRANSITIVE = 0x00000001 -+ -+_trust_direction_dict = { -+ 1: _('Trusting forest'), -+ 2: _('Trusted forest'), -+ 3: _('Two-way trust') -+} -+ -+_trust_status_dict = { -+ True: _('Established and verified'), -+ False: _('Waiting for confirmation by remote side') -+} -+ -+_trust_type_dict_unknown = _('Unknown') -+ -+# Trust type is a combination of ipanttrusttype and ipanttrustattributes -+# We shift trust attributes by 3 bits to left so bit 0 becomes bit 3 and -+# 2+(1 << 3) becomes 10. -+_trust_type_dict = { -+ 1: _('Non-Active Directory domain'), -+ 2: _('Active Directory domain'), -+ 3: _('RFC4120-compliant Kerberos realm'), -+ 10: _('Non-transitive external trust to a domain in ' -+ 'another Active Directory forest'), -+ 11: _('Non-transitive external trust to an RFC4120-' -+ 'compliant Kerberos realm') -+} -+ -+ -+def trust_type_string(level, attrs): -+ """ -+ Returns a string representing a type of the trust. -+ The original field is an enum: -+ LSA_TRUST_TYPE_DOWNLEVEL = 0x00000001, -+ LSA_TRUST_TYPE_UPLEVEL = 0x00000002, -+ LSA_TRUST_TYPE_MIT = 0x00000003 -+ """ -+ transitive = int(attrs) & LSA_TRUST_ATTRIBUTE_NON_TRANSITIVE -+ string = _trust_type_dict.get(int(level) | (transitive << 3), -+ _trust_type_dict_unknown) -+ return unicode(string) -+ -+ -+def trust_direction_string(level): -+ """ -+ Returns a string representing a direction of the trust. -+ The original field is a bitmask taking two bits in use -+ LSA_TRUST_DIRECTION_INBOUND = 0x00000001, -+ LSA_TRUST_DIRECTION_OUTBOUND = 0x00000002 -+ """ -+ string = _trust_direction_dict.get(int(level), _trust_type_dict_unknown) -+ return unicode(string) -+ -+ -+def trust_status_string(level): -+ string = _trust_status_dict.get(level, _trust_type_dict_unknown) -+ return unicode(string) -diff --git a/ipaserver/plugins/trust.py b/ipaserver/plugins/trust.py -index d0bbfbc47ca65c9c5229685fc9d202c293fe41cd..ecc5fa0b22f94de05cc5282758be093f0cfca13f 100644 ---- a/ipaserver/plugins/trust.py -+++ b/ipaserver/plugins/trust.py -@@ -44,6 +44,13 @@ from ipalib import errors - from ipalib import output - from ldap import SCOPE_SUBTREE - from time import sleep -+from ipaserver.dcerpc_common import (TRUST_ONEWAY, -+ TRUST_BIDIRECTIONAL, -+ TRUST_JOIN_EXTERNAL, -+ LSA_TRUST_ATTRIBUTE_NON_TRANSITIVE, -+ trust_type_string, -+ trust_direction_string, -+ trust_status_string) - - if six.PY3: - unicode = str -@@ -63,9 +70,6 @@ except Exception as e: - if api.env.in_server and api.env.context in ['lite', 'server']: - try: - import ipaserver.dcerpc -- from ipaserver.dcerpc import (TRUST_ONEWAY, -- TRUST_BIDIRECTIONAL, -- TRUST_JOIN_EXTERNAL) - import dbus - import dbus.mainloop.glib - _bindings_installed = True -@@ -157,28 +161,14 @@ particular type. - - register = Registry() - --# Trust type is a combination of ipanttrusttype and ipanttrustattributes --# We shift trust attributes by 3 bits to left so bit 0 becomes bit 3 and --# 2+(1 << 3) becomes 10. --_trust_type_dict = {1 : _('Non-Active Directory domain'), -- 2 : _('Active Directory domain'), -- 3 : _('RFC4120-compliant Kerberos realm'), -- 10: _('Non-transitive external trust to a domain in another Active Directory forest')} -- --_trust_direction_dict = {1 : _('Trusting forest'), -- 2 : _('Trusted forest'), -- 3 : _('Two-way trust')} --_trust_status_dict = {True : _('Established and verified'), -- False : _('Waiting for confirmation by remote side')} --_trust_type_dict_unknown = _('Unknown') -- --_trust_type_option = StrEnum('trust_type', -- cli_name='type', -- label=_('Trust type (ad for Active Directory, default)'), -- values=(u'ad',), -- default=u'ad', -- autofill=True, -- ) -+_trust_type_option = StrEnum( -+ 'trust_type', -+ cli_name='type', -+ label=_('Trust type (ad for Active Directory, default)'), -+ values=(u'ad',), -+ default=u'ad', -+ autofill=True, -+ ) - - DEFAULT_RANGE_SIZE = 200000 - -@@ -187,31 +177,6 @@ DBUS_IFACE_TRUST = 'com.redhat.idm.trust' - CRED_STYLE_SAMBA = 1 - CRED_STYLE_KERBEROS = 2 - --LSA_TRUST_ATTRIBUTE_NON_TRANSITIVE = 0x00000001 -- --def trust_type_string(level, attrs): -- """ -- Returns a string representing a type of the trust. The original field is an enum: -- LSA_TRUST_TYPE_DOWNLEVEL = 0x00000001, -- LSA_TRUST_TYPE_UPLEVEL = 0x00000002, -- LSA_TRUST_TYPE_MIT = 0x00000003 -- """ -- transitive = int(attrs) & LSA_TRUST_ATTRIBUTE_NON_TRANSITIVE -- string = _trust_type_dict.get(int(level) | (transitive << 3), _trust_type_dict_unknown) -- return unicode(string) -- --def trust_direction_string(level): -- """ -- Returns a string representing a direction of the trust. The original field is a bitmask taking two bits in use -- LSA_TRUST_DIRECTION_INBOUND = 0x00000001, -- LSA_TRUST_DIRECTION_OUTBOUND = 0x00000002 -- """ -- string = _trust_direction_dict.get(int(level), _trust_type_dict_unknown) -- return unicode(string) -- --def trust_status_string(level): -- string = _trust_status_dict.get(level, _trust_type_dict_unknown) -- return unicode(string) - - def make_trust_dn(env, trust_type, dn): - assert isinstance(dn, DN) --- -2.13.6 - diff --git a/SOURCES/0018-Tune-DS-replication-settings.patch b/SOURCES/0018-Tune-DS-replication-settings.patch new file mode 100644 index 0000000..bb37404 --- /dev/null +++ b/SOURCES/0018-Tune-DS-replication-settings.patch @@ -0,0 +1,336 @@ +From 4d99ab7ce65064aacf2a7429d8ebc956c5b43b34 Mon Sep 17 00:00:00 2001 +From: Christian Heimes +Date: Tue, 3 Jul 2018 19:40:05 +0200 +Subject: [PATCH] Tune DS replication settings + +Tune 389-DS replication settings to improve performance and avoid +timeouts. During installation of a replica, the value of +nsDS5ReplicaBindDnGroupCheckInterval is reduced to 2 seconds. At the end +of the installation, the value is increased sensible production +settings. This avoids long delays during replication. + +See: https://pagure.io/freeipa/issue/7617 +Signed-off-by: Christian Heimes +Reviewed-By: Tibor Dudlak +Reviewed-By: Tibor Dudlak +--- + ipaserver/install/cainstance.py | 8 +- + ipaserver/install/dsinstance.py | 48 ++++++---- + ipaserver/install/replication.py | 92 +++++++++++++++---- + ipaserver/install/server/replicainstall.py | 2 + + ipaserver/install/server/upgrade.py | 12 ++- + ipatests/test_integration/test_external_ca.py | 21 +++++ + 6 files changed, 140 insertions(+), 43 deletions(-) + +diff --git a/ipaserver/install/cainstance.py b/ipaserver/install/cainstance.py +index 51fdbe9c61e06ab9d72d78aee8786f9bceca137b..8193f3da854b3a20d175de523fbc453f5c5104d8 100644 +--- a/ipaserver/install/cainstance.py ++++ b/ipaserver/install/cainstance.py +@@ -410,6 +410,9 @@ class CAInstance(DogtagInstance): + self.teardown_admin) + self.step("starting certificate server instance", + self.start_instance) ++ if promote: ++ self.step("Finalize replication settings", ++ self.finalize_replica_config) + # Step 1 of external is getting a CSR so we don't need to do these + # steps until we get a cert back from the external CA. + if self.external != 1: +@@ -1245,13 +1248,16 @@ class CAInstance(DogtagInstance): + api.Backend.ldap2.add_entry(entry) + + def __setup_replication(self): +- + repl = replication.CAReplicationManager(self.realm, self.fqdn) + repl.setup_cs_replication(self.master_host) + + # Activate Topology for o=ipaca segments + self.__update_topology() + ++ def finalize_replica_config(self): ++ repl = replication.CAReplicationManager(self.realm, self.fqdn) ++ repl.finalize_replica_config(self.master_host) ++ + def __enable_instance(self): + basedn = ipautil.realm_to_suffix(self.realm) + if not self.clone: +diff --git a/ipaserver/install/dsinstance.py b/ipaserver/install/dsinstance.py +index 06c1273dc21a88f99a0f543cfd12bb6563c7e214..eefbde3356e1077d490d09c4ea47d961ce3ce8e6 100644 +--- a/ipaserver/install/dsinstance.py ++++ b/ipaserver/install/dsinstance.py +@@ -418,6 +418,20 @@ class DsInstance(service.Service): + + self.start_creation(runtime=30) + ++ def _get_replication_manager(self): ++ # Always connect to self over ldapi ++ ldap_uri = ipaldap.get_ldap_uri(protocol='ldapi', realm=self.realm) ++ conn = ipaldap.LDAPClient(ldap_uri) ++ conn.external_bind() ++ repl = replication.ReplicationManager( ++ self.realm, self.fqdn, self.dm_password, conn=conn ++ ) ++ if self.dm_password is not None and not self.promote: ++ bind_dn = DN(('cn', 'Directory Manager')) ++ bind_pw = self.dm_password ++ else: ++ bind_dn = bind_pw = None ++ return repl, bind_dn, bind_pw + + def __setup_replica(self): + """ +@@ -434,26 +448,24 @@ class DsInstance(service.Service): + self.realm, + self.dm_password) + +- # Always connect to self over ldapi +- ldap_uri = ipaldap.get_ldap_uri(protocol='ldapi', realm=self.realm) +- conn = ipaldap.LDAPClient(ldap_uri) +- conn.external_bind() +- repl = replication.ReplicationManager(self.realm, +- self.fqdn, +- self.dm_password, conn=conn) +- +- if self.dm_password is not None and not self.promote: +- bind_dn = DN(('cn', 'Directory Manager')) +- bind_pw = self.dm_password +- else: +- bind_dn = bind_pw = None +- +- repl.setup_promote_replication(self.master_fqdn, +- r_binddn=bind_dn, +- r_bindpw=bind_pw, +- cacert=self.ca_file) ++ repl, bind_dn, bind_pw = self._get_replication_manager() ++ repl.setup_promote_replication( ++ self.master_fqdn, ++ r_binddn=bind_dn, ++ r_bindpw=bind_pw, ++ cacert=self.ca_file ++ ) + self.run_init_memberof = repl.needs_memberof_fixup() + ++ def finalize_replica_config(self): ++ repl, bind_dn, bind_pw = self._get_replication_manager() ++ repl.finalize_replica_config( ++ self.master_fqdn, ++ r_binddn=bind_dn, ++ r_bindpw=bind_pw, ++ cacert=self.ca_file ++ ) ++ + def __configure_sasl_mappings(self): + # we need to remove any existing SASL mappings in the directory as otherwise they + # they may conflict. +diff --git a/ipaserver/install/replication.py b/ipaserver/install/replication.py +index 5ce8fa689c1a6fd3b9d4cbddbd5454d36334b729..d0f92c76c108d237104a81c72567250583ac4ff1 100644 +--- a/ipaserver/install/replication.py ++++ b/ipaserver/install/replication.py +@@ -75,6 +75,20 @@ STRIP_ATTRS = ('modifiersName', + 'internalModifiersName', + 'internalModifyTimestamp') + ++# settings for cn=replica,cn=$DB,cn=mapping tree,cn=config ++# during replica installation ++REPLICA_CREATION_SETTINGS = { ++ "nsds5ReplicaReleaseTimeout": ["20"], ++ "nsds5ReplicaBackoffMax": ["3"], ++ "nsDS5ReplicaBindDnGroupCheckInterval": ["2"] ++} ++# after replica installation ++REPLICA_FINAL_SETTINGS = { ++ "nsds5ReplicaReleaseTimeout": ["60"], ++ "nsds5ReplicaBackoffMax": ["300"], # default ++ "nsDS5ReplicaBindDnGroupCheckInterval": ["60"] ++} ++ + + def replica_conn_check(master_host, host_name, realm, check_ca, + dogtag_master_ds_port, admin_password=None, +@@ -201,9 +215,13 @@ def wait_for_entry(connection, dn, timeout, attr=None, attrvalue='*', + + + class ReplicationManager(object): +- """Manage replication agreements between DS servers, and sync +- agreements with Windows servers""" +- def __init__(self, realm, hostname, dirman_passwd, port=PORT, starttls=False, conn=None): ++ """Manage replication agreements ++ ++ between DS servers, and sync agreements with Windows servers ++ """ ++ ++ def __init__(self, realm, hostname, dirman_passwd=None, port=PORT, ++ starttls=False, conn=None): + self.hostname = hostname + self.port = port + self.dirman_passwd = dirman_passwd +@@ -481,22 +499,16 @@ class ReplicationManager(object): + except errors.NotFound: + pass + else: +- managers = {DN(m) for m in entry.get('nsDS5ReplicaBindDN', [])} +- +- mods = [] +- if replica_binddn not in managers: ++ binddns = entry.setdefault('nsDS5ReplicaBindDN', []) ++ if replica_binddn not in {DN(m) for m in binddns}: + # Add the new replication manager +- mods.append( +- (ldap.MOD_ADD, 'nsDS5ReplicaBindDN', replica_binddn) +- ) +- if 'nsds5replicareleasetimeout' not in entry: +- # See https://pagure.io/freeipa/issue/7488 +- mods.append( +- (ldap.MOD_ADD, 'nsds5replicareleasetimeout', ['60']) +- ) +- +- if mods: +- conn.modify_s(dn, mods) ++ binddns.append(replica_binddn) ++ for key, value in REPLICA_CREATION_SETTINGS.items(): ++ entry[key] = value ++ try: ++ conn.update_entry(entry) ++ except errors.EmptyModlist: ++ pass + + self.set_replica_binddngroup(conn, entry) + +@@ -515,9 +527,8 @@ class ReplicationManager(object): + nsds5flags=["1"], + nsds5replicabinddn=[replica_binddn], + nsds5replicabinddngroup=[self.repl_man_group_dn], +- nsds5replicabinddngroupcheckinterval=["60"], +- nsds5replicareleasetimeout=["60"], + nsds5replicalegacyconsumer=["off"], ++ **REPLICA_CREATION_SETTINGS + ) + conn.add_entry(entry) + +@@ -543,6 +554,47 @@ class ReplicationManager(object): + except errors.DuplicateEntry: + return + ++ def _finalize_replica_settings(self, conn): ++ """Change replica settings to final values ++ ++ During replica installation, some settings are configured for faster ++ replication. ++ """ ++ dn = self.replica_dn() ++ entry = conn.get_entry(dn) ++ for key, value in REPLICA_FINAL_SETTINGS.items(): ++ entry[key] = value ++ try: ++ conn.update_entry(entry) ++ except errors.EmptyModlist: ++ pass ++ ++ def finalize_replica_config(self, r_hostname, r_binddn=None, ++ r_bindpw=None, cacert=paths.IPA_CA_CRT): ++ """Apply final cn=replica settings ++ ++ replica_config() sets several attribute to fast cache invalidation ++ and fast reconnects to optimize replicat installation. For ++ production, longer timeouts and less aggressive cache invalidation ++ is sufficient. finalize_replica_config() sets the values on new ++ replica and the master. ++ ++ When installing multiple replicas in parallel, one replica may ++ finalize the values while another is still installing. ++ ++ See https://pagure.io/freeipa/issue/7617 ++ """ ++ self._finalize_replica_settings(self.conn) ++ ++ ldap_uri = ipaldap.get_ldap_uri(r_hostname) ++ r_conn = ipaldap.LDAPClient(ldap_uri, cacert=cacert) ++ if r_bindpw: ++ r_conn.simple_bind(r_binddn, r_bindpw) ++ else: ++ r_conn.gssapi_bind() ++ self._finalize_replica_settings(r_conn) ++ r_conn.close() ++ + def setup_chaining_backend(self, conn): + chaindn = DN(('cn', 'chaining database'), ('cn', 'plugins'), ('cn', 'config')) + benamebase = "chaindb" +diff --git a/ipaserver/install/server/replicainstall.py b/ipaserver/install/server/replicainstall.py +index 542e1d4d145f266d6fd9ad8e0eaffcb12e8f6bc6..8826da232a90380084b0e4f3dca783125a5500da 100644 +--- a/ipaserver/install/server/replicainstall.py ++++ b/ipaserver/install/server/replicainstall.py +@@ -1506,6 +1506,8 @@ def install(installer): + # Apply any LDAP updates. Needs to be done after the replica is synced-up + service.print_msg("Applying LDAP updates") + ds.apply_updates() ++ service.print_msg("Finalize replication settings") ++ ds.finalize_replica_config() + + if kra_enabled: + kra.install(api, config, options, custodia=custodia) +diff --git a/ipaserver/install/server/upgrade.py b/ipaserver/install/server/upgrade.py +index ee3cedd78a2f45f33665bf562e9426cf68325544..4e5096e598cd10e3bd98f91946b4d26377d0de6e 100644 +--- a/ipaserver/install/server/upgrade.py ++++ b/ipaserver/install/server/upgrade.py +@@ -45,6 +45,7 @@ from ipaserver.install import dnskeysyncinstance + from ipaserver.install import dogtaginstance + from ipaserver.install import krbinstance + from ipaserver.install import adtrustinstance ++from ipaserver.install import replication + from ipaserver.install.upgradeinstance import IPAUpgrade + from ipaserver.install.ldapupdate import BadSyntax + +@@ -1645,11 +1646,14 @@ def update_replica_config(db_suffix): + except ipalib.errors.NotFound: + return # entry does not exist until a replica is installed + +- if 'nsds5replicareleasetimeout' not in entry: +- # See https://pagure.io/freeipa/issue/7488 +- logger.info("Adding nsds5replicaReleaseTimeout=60 to %s", dn) +- entry['nsds5replicareleasetimeout'] = '60' ++ for key, value in replication.REPLICA_FINAL_SETTINGS.items(): ++ entry[key] = value ++ try: + api.Backend.ldap2.update_entry(entry) ++ except ipalib.errors.EmptyModlist: ++ pass ++ else: ++ logger.info("Updated entry %s", dn) + + + def upgrade_configuration(): +diff --git a/ipatests/test_integration/test_external_ca.py b/ipatests/test_integration/test_external_ca.py +index d21e6d543f51dcee1e548e322cbe01fbe0c13d48..2fc2478ae8bb49d95f7ec60270fb5bb6e582d6f5 100644 +--- a/ipatests/test_integration/test_external_ca.py ++++ b/ipatests/test_integration/test_external_ca.py +@@ -130,6 +130,27 @@ class TestExternalCA(IntegrationTest): + result = self.master.run_command(['ipa', 'user-show', 'admin']) + assert 'User login: admin' in result.stdout_text + ++ # check that we can also install replica ++ tasks.install_replica(self.master, self.replicas[0]) ++ ++ # check that nsds5ReplicaReleaseTimeout option was set ++ result = self.master.run_command([ ++ 'ldapsearch', ++ '-x', ++ '-D', ++ 'cn=directory manager', ++ '-w', self.master.config.dirman_password, ++ '-b', 'cn=mapping tree,cn=config', ++ '(cn=replica)', ++ '-LLL', ++ '-o', ++ 'ldif-wrap=no']) ++ # case insensitive match ++ text = result.stdout_text.lower() ++ # see ipaserver.install.replication.REPLICA_FINAL_SETTINGS ++ assert 'nsds5ReplicaReleaseTimeout: 60'.lower() in text ++ assert 'nsDS5ReplicaBindDnGroupCheckInterval: 60'.lower() in text ++ + def test_client_installation_with_otp(self): + # Test for issue 7526: client installation fails with one-time + # password when the master is installed with an externally signed +-- +2.17.1 + diff --git a/SOURCES/0018-ipaserver-plugins-trust.py-fix-some-indenting-issues.patch b/SOURCES/0018-ipaserver-plugins-trust.py-fix-some-indenting-issues.patch deleted file mode 100644 index a93887e..0000000 --- a/SOURCES/0018-ipaserver-plugins-trust.py-fix-some-indenting-issues.patch +++ /dev/null @@ -1,71 +0,0 @@ -From e553b66e54af3bf981501bca57fb22caaa8e8305 Mon Sep 17 00:00:00 2001 -From: Alexander Bokovoy -Date: Thu, 9 Nov 2017 09:57:47 +0200 -Subject: [PATCH] ipaserver/plugins/trust.py; fix some indenting issues - -Reviewed-By: Christian Heimes -Reviewed-By: Thierry Bordaz ---- - ipaserver/plugins/trust.py | 17 +++++++++-------- - 1 file changed, 9 insertions(+), 8 deletions(-) - -diff --git a/ipaserver/plugins/trust.py b/ipaserver/plugins/trust.py -index ecc5fa0b22f94de05cc5282758be093f0cfca13f..d01529ee022d4a4f9b671a8f06156ed450326041 100644 ---- a/ipaserver/plugins/trust.py -+++ b/ipaserver/plugins/trust.py -@@ -280,15 +280,17 @@ def generate_creds(trustinstance, style, **options): - elif style == CRED_STYLE_KERBEROS: - sp = admin_name.split('\\') - if len(sp) > 1: -- sp = [sp[1]] -+ sp = [sp[1]] - else: -- sp = admin_name.split(sep) -+ sp = admin_name.split(sep) - if len(sp) == 1: -- sp.append(trustinstance.remote_domain.info['dns_domain'].upper()) -+ sp.append(trustinstance.remote_domain -+ .info['dns_domain'].upper()) - creds = u"{name}%{password}".format(name=sep.join(sp), - password=password) - return creds - -+ - def add_range(myapi, trustinstance, range_name, dom_sid, *keys, **options): - """ - First, we try to derive the parameters of the ID range based on the -@@ -319,7 +321,7 @@ def add_range(myapi, trustinstance, range_name, dom_sid, *keys, **options): - # CN=ypservers,CN=ypServ30,CN=RpcServices,CN=System - info_filter = '(objectClass=msSFU30DomainInfo)' - info_dn = DN('CN=ypservers,CN=ypServ30,CN=RpcServices,CN=System')\ -- + basedn -+ + basedn - - # Get the domain validator - domain_validator = ipaserver.dcerpc.DomainValidator(myapi) -@@ -367,7 +369,7 @@ def add_range(myapi, trustinstance, range_name, dom_sid, *keys, **options): - - base_id = int(info.get('msSFU30OrderNumber')[0]) - range_size = (1 + (max_id - base_id) // DEFAULT_RANGE_SIZE)\ -- * DEFAULT_RANGE_SIZE -+ * DEFAULT_RANGE_SIZE - - # Second, options given via the CLI options take precedence to discovery - if options.get('range_type', None): -@@ -595,11 +597,10 @@ class trust(LDAPObject): - pass - else: - for entry in entries: -- add_message( -+ add_message( - options['version'], - result, -- BrokenTrust(domain=entry.single_value['cn']) -- ) -+ BrokenTrust(domain=entry.single_value['cn'])) - - - @register() --- -2.13.6 - diff --git a/SOURCES/0019-Fix-DNSSEC-install-regression.patch b/SOURCES/0019-Fix-DNSSEC-install-regression.patch new file mode 100644 index 0000000..de4e93b --- /dev/null +++ b/SOURCES/0019-Fix-DNSSEC-install-regression.patch @@ -0,0 +1,68 @@ +From 8110958d141392014bf3e3eae01654e7864d7647 Mon Sep 17 00:00:00 2001 +From: Christian Heimes +Date: Tue, 10 Jul 2018 12:51:36 +0200 +Subject: [PATCH] Fix DNSSEC install regression + +7284097eedef70dd556270732e6ab8e23501ce09 introduced a regression in +DNSSEC master installation. For standalone and replica installation, +services have to be enabled before checking bind config. + +Fixes: https://pagure.io/freeipa/issue/7635 +See: https://pagure.io/freeipa/issue/7566 +Signed-off-by: Christian Heimes +Reviewed-By: Tibor Dudlak +Reviewed-By: Tibor Dudlak +--- + install/tools/ipa-dns-install | 5 +---- + ipaserver/install/dns.py | 5 +++++ + 2 files changed, 6 insertions(+), 4 deletions(-) + +diff --git a/install/tools/ipa-dns-install b/install/tools/ipa-dns-install +index 57dde5a5da4fad162c93e9e0416b54961de4c1e3..32a17d223ae2bdd9a1ded62defcc272a40d2627b 100755 +--- a/install/tools/ipa-dns-install ++++ b/install/tools/ipa-dns-install +@@ -37,7 +37,6 @@ from ipapython.config import IPAOptionParser + from ipapython.ipa_log_manager import standard_logging_setup + + from ipaserver.install import dns as dns_installer +-from ipaserver.install import service + + logger = logging.getLogger(os.path.basename(__file__)) + +@@ -149,9 +148,7 @@ def main(): + + dns_installer.install_check(True, api, False, options, hostname=api.env.host) + dns_installer.install(True, False, options) +- # Enable configured services and update DNS SRV records +- service.enable_services(api.env.host) +- api.Command.dns_update_system_records() ++ # Services are enabled in dns_installer.install() + + # execute ipactl to refresh services status + ipautil.run(['ipactl', 'start', '--ignore-service-failures'], +diff --git a/ipaserver/install/dns.py b/ipaserver/install/dns.py +index e14b353e9cb655a6e7ef228d47dfc7a1badd7286..cac7a9213796d6618854b12da6c2a7fe60afdbf9 100644 +--- a/ipaserver/install/dns.py ++++ b/ipaserver/install/dns.py +@@ -44,6 +44,7 @@ from ipaserver.install import bindinstance + from ipaserver.install import dnskeysyncinstance + from ipaserver.install import odsexporterinstance + from ipaserver.install import opendnssecinstance ++from ipaserver.install import service + + if six.PY3: + unicode = str +@@ -357,6 +358,10 @@ def install(standalone, replica, options, api=api): + dnskeysyncd.start_dnskeysyncd() + bind.start_named() + ++ # Enable configured services for standalone check_global_configuration() ++ if standalone: ++ service.enable_services(api.env.host) ++ + # this must be done when bind is started and operational + bind.update_system_records() + +-- +2.17.1 + diff --git a/SOURCES/0019-ipaserver-plugins-trust.py-pep8-compliance.patch b/SOURCES/0019-ipaserver-plugins-trust.py-pep8-compliance.patch deleted file mode 100644 index 60c36be..0000000 --- a/SOURCES/0019-ipaserver-plugins-trust.py-pep8-compliance.patch +++ /dev/null @@ -1,786 +0,0 @@ -From aae0cc2fdaeead8ff33ade93e73d6aba25704659 Mon Sep 17 00:00:00 2001 -From: Alexander Bokovoy -Date: Fri, 17 Nov 2017 17:25:57 +0200 -Subject: [PATCH] ipaserver/plugins/trust.py: pep8 compliance - -Reviewed-By: Christian Heimes -Reviewed-By: Thierry Bordaz ---- - ipaserver/plugins/trust.py | 356 +++++++++++++++++++++++++++------------------ - 1 file changed, 214 insertions(+), 142 deletions(-) - -diff --git a/ipaserver/plugins/trust.py b/ipaserver/plugins/trust.py -index d01529ee022d4a4f9b671a8f06156ed450326041..73e137abcec9625997a619fe64d4f615743247b1 100644 ---- a/ipaserver/plugins/trust.py -+++ b/ipaserver/plugins/trust.py -@@ -81,10 +81,10 @@ Cross-realm trusts - - Manage trust relationship between IPA and Active Directory domains. - --In order to allow users from a remote domain to access resources in IPA --domain, trust relationship needs to be established. Currently IPA supports --only trusts between IPA and Active Directory domains under control of Windows --Server 2008 or later, with functional level 2008 or later. -+In order to allow users from a remote domain to access resources in IPA domain, -+trust relationship needs to be established. Currently IPA supports only trusts -+between IPA and Active Directory domains under control of Windows Server 2008 -+or later, with functional level 2008 or later. - - Please note that DNS on both IPA and Active Directory domain sides should be - configured properly to discover each other. Trust relationship relies on -@@ -95,7 +95,8 @@ Examples: - 1. Establish cross-realm trust with Active Directory using AD administrator - credentials: - -- ipa trust-add --type=ad --admin --password -+ ipa trust-add --type=ad --admin \ -+ --password - - 2. List all existing trust relationships: - -@@ -110,35 +111,39 @@ Examples: - ipa trust-del - - Once trust relationship is established, remote users will need to be mapped --to local POSIX groups in order to actually use IPA resources. The mapping should --be done via use of external membership of non-POSIX group and then this group --should be included into one of local POSIX groups. -+to local POSIX groups in order to actually use IPA resources. The mapping -+should be done via use of external membership of non-POSIX group and then -+this group should be included into one of local POSIX groups. - - Example: - --1. Create group for the trusted domain admins' mapping and their local POSIX group: -+1. Create group for the trusted domain admins' mapping and their local POSIX -+group: - -- ipa group-add --desc=' admins external map' ad_admins_external --external -+ ipa group-add --desc=' admins external map' \ -+ ad_admins_external --external - ipa group-add --desc=' admins' ad_admins - --2. Add security identifier of Domain Admins of the to the ad_admins_external -- group: -+2. Add security identifier of Domain Admins of the to the -+ ad_admins_external group: - - ipa group-add-member ad_admins_external --external 'AD\\Domain Admins' - --3. Allow members of ad_admins_external group to be associated with ad_admins POSIX group: -+3. Allow members of ad_admins_external group to be associated with -+ ad_admins POSIX group: - - ipa group-add-member ad_admins --groups ad_admins_external - --4. List members of external members of ad_admins_external group to see their SIDs: -+4. List members of external members of ad_admins_external group to see -+ their SIDs: - - ipa group-show ad_admins_external - - - GLOBAL TRUST CONFIGURATION - --When IPA AD trust subpackage is installed and ipa-adtrust-install is run, --a local domain configuration (SID, GUID, NetBIOS name) is generated. These -+When IPA AD trust subpackage is installed and ipa-adtrust-install is run, a -+local domain configuration (SID, GUID, NetBIOS name) is generated. These - identifiers are then used when communicating with a trusted domain of the - particular type. - -@@ -147,11 +152,11 @@ particular type. - ipa trustconfig-show --type ad - - 2. Modify global configuration for all trusts of Active Directory type and set -- a different fallback primary group (fallback primary group GID is used as -- a primary user GID if user authenticating to IPA domain does not have any other -- primary GID already set): -+ a different fallback primary group (fallback primary group GID is used as a -+ primary user GID if user authenticating to IPA domain does not have any -+ other primary GID already set): - -- ipa trustconfig-mod --type ad --fallback-primary-group "alternative AD group" -+ ipa trustconfig-mod --type ad --fallback-primary-group "another AD group" - - 3. Change primary fallback group back to default hidden group (any group with - posixGroup object class is allowed): -@@ -185,6 +190,7 @@ def make_trust_dn(env, trust_type, dn): - return DN(dn, container_dn) - return dn - -+ - def find_adtrust_masters(ldap, api): - """ - Returns a list of names of IPA servers with ADTRUST component configured. -@@ -200,6 +206,7 @@ def find_adtrust_masters(ldap, api): - - return [entry.dn[1].value for entry in entries] - -+ - def verify_samba_component_presence(ldap, api): - """ - Verifies that Samba is installed and configured on this particular master. -@@ -233,7 +240,7 @@ def verify_samba_component_presence(ldap, api): - - # First check for packages missing - elif not _bindings_installed: -- error_message=_( -+ error_message = _( - 'Cannot perform the selected command without Samba 4 support ' - 'installed. Make sure you have installed server-trust-ad ' - 'sub-package of IPA.' -@@ -243,7 +250,7 @@ def verify_samba_component_presence(ldap, api): - - # Packages present, but ADTRUST instance is not configured - elif not adtrust_present: -- error_message=_( -+ error_message = _( - 'Cannot perform the selected command without Samba 4 instance ' - 'configured on this machine. Make sure you have run ' - 'ipa-adtrust-install on this server.' -@@ -263,7 +270,8 @@ def generate_creds(trustinstance, style, **options): - **options -- options with realm_admin and realm_passwd keys - - Result: -- a string representing credentials with first % separating username and password -+ a string representing credentials with first % separating -+ username and password - None is returned if realm_passwd key returns nothing from options - """ - creds = None -@@ -284,8 +292,9 @@ def generate_creds(trustinstance, style, **options): - else: - sp = admin_name.split(sep) - if len(sp) == 1: -- sp.append(trustinstance.remote_domain -- .info['dns_domain'].upper()) -+ sp.append( -+ trustinstance.remote_domain.info['dns_domain'].upper() -+ ) - creds = u"{name}%{password}".format(name=sep.join(sp), - password=password) - return creds -@@ -334,7 +343,8 @@ def add_range(myapi, trustinstance, range_name, dom_sid, *keys, **options): - creds = None - if trustinstance: - # Re-use AD administrator credentials if they were provided -- creds = generate_creds(trustinstance, style=CRED_STYLE_KERBEROS, **options) -+ creds = generate_creds(trustinstance, -+ style=CRED_STYLE_KERBEROS, **options) - if creds: - domain_validator._admin_creds = creds - # KDC might not get refreshed data at the first time, -@@ -417,21 +427,32 @@ def fetch_trusted_domains_over_dbus(myapi, log, forest_name): - _stdout = '' - _stderr = '' - bus = dbus.SystemBus() -- intf = bus.get_object(DBUS_IFACE_TRUST,"/", follow_name_owner_changes=True) -- fetch_domains_method = intf.get_dbus_method('fetch_domains', dbus_interface=DBUS_IFACE_TRUST) -+ intf = bus.get_object(DBUS_IFACE_TRUST, "/", -+ follow_name_owner_changes=True) -+ fetch_domains_method = intf.get_dbus_method( -+ 'fetch_domains', -+ dbus_interface=DBUS_IFACE_TRUST) - (_ret, _stdout, _stderr) = fetch_domains_method(forest_name) - except dbus.DBusException as e: -- log.error('Failed to call %(iface)s.fetch_domains helper.' -- 'DBus exception is %(exc)s.' % dict(iface=DBUS_IFACE_TRUST, exc=str(e))) -+ log.error( -+ 'Failed to call %(iface)s.fetch_domains helper. ' -+ 'DBus exception is %(exc)s.' % dict(iface=DBUS_IFACE_TRUST, exc=str(e)) -+ ) - if _ret != 0: -- log.error('Helper was called for forest %(forest)s, return code is %(ret)d' % dict(forest=forest_name, ret=_ret)) -- log.error('Standard output from the helper:\n%s---\n' % (_stdout)) -- log.error('Error output from the helper:\n%s--\n' % (_stderr)) -- raise errors.ServerCommandError(server=myapi.env.host, -- error=_('Fetching domains from trusted forest failed. ' -- 'See details in the error_log')) -+ log.error( -+ 'Helper was called for forest %s, return code is %d', -+ forest_name, _ret -+ ) -+ log.error('Standard output from the helper:\n%s---\n', _stdout) -+ log.error('Error output from the helper:\n%s--\n', _stderr) -+ raise errors.ServerCommandError( -+ server=myapi.env.host, -+ error=_('Fetching domains from trusted forest failed. ' -+ 'See details in the error_log') -+ ) - return - -+ - @register() - class trust(LDAPObject): - """ -@@ -538,8 +559,8 @@ class trust(LDAPObject): - continue - for value in values: - if not ipaserver.dcerpc.is_sid_valid(value): -- raise errors.ValidationError(name=attr, -- error=_("invalid SID: %(value)s") % dict(value=value)) -+ err = unicode(_("invalid SID: {SID}")).format(SID=value) -+ raise errors.ValidationError(name=attr, error=err) - - def get_dn(self, *keys, **kwargs): - trust_type = kwargs.get('trust_type') -@@ -600,7 +621,8 @@ class trust(LDAPObject): - add_message( - options['version'], - result, -- BrokenTrust(domain=entry.single_value['cn'])) -+ BrokenTrust(domain=entry.single_value['cn']) -+ ) - - - @register() -@@ -622,7 +644,7 @@ sides. - range_types = { - u'ipa-ad-trust': unicode(_('Active Directory domain range')), - u'ipa-ad-trust-posix': unicode(_('Active Directory trust range with ' -- 'POSIX attributes')), -+ 'POSIX attributes')), - } - - takes_options = LDAPCreate.takes_options + ( -@@ -720,9 +742,10 @@ sides. - - trust_filter = "cn=%s" % result['value'] - trusts, _truncated = ldap.find_entries( -- base_dn=DN(self.api.env.container_trusts, self.api.env.basedn), -- filter=trust_filter, -- attrs_list=attrs_list) -+ base_dn=DN(self.api.env.container_trusts, self.api.env.basedn), -+ filter=trust_filter, -+ attrs_list=attrs_list -+ ) - - result['result'] = entry_to_dict(trusts[0], **options) - -@@ -731,10 +754,11 @@ sides. - # Note that add_new_domains_from_trust will add needed ranges for - # the algorithmic ID mapping case. - if (options.get('trust_type') == u'ad' and -- options.get('trust_secret') is None): -+ options.get('trust_secret') is None): -+ - if options.get('bidirectional') == True: -- # Bidirectional trust allows us to use cross-realm TGT, so we can -- # run the call under original user's credentials -+ # Bidirectional trust allows us to use cross-realm TGT, -+ # so we can run the call under original user's credentials - res = fetch_domains_from_trust(self.api, self.trustinstance, - **options) - add_new_domains_from_trust( -@@ -790,7 +814,9 @@ sides. - # If domain name and realm does not match, IPA server is not be able - # to establish trust with Active Directory. - -- realm_not_matching_domain = (self.api.env.domain.upper() != self.api.env.realm) -+ realm_not_matching_domain = ( -+ self.api.env.domain.upper() != self.api.env.realm -+ ) - - if options['trust_type'] == u'ad' and realm_not_matching_domain: - raise errors.ValidationError( -@@ -917,11 +943,12 @@ sides. - ) - - if range_type and range_type != old_range_type: -- raise errors.ValidationError(name=_('range type change'), -- error=_('ID range for the trusted domain already exists, ' -- 'but it has a different type. Please remove the ' -- 'old range manually, or do not enforce type ' -- 'via --range-type option.')) -+ raise errors.ValidationError( -+ name=_('range type change'), -+ error=_('ID range for the trusted domain already ' -+ 'exists, but it has a different type. Please ' -+ 'remove the old range manually, or do not ' -+ 'enforce type via --range-type option.')) - - return old_range, range_name, dom_sid - -@@ -956,33 +983,55 @@ sides. - trust_type - ) - except errors.NotFound: -- error_message=_("Unable to resolve domain controller for '%s' domain. ") % (keys[-1]) -- instructions=[] -+ _message = _("Unable to resolve domain controller for " -+ "{domain} domain. ") -+ error_message = unicode(_message).format(domain=keys[-1]) -+ instructions = [] -+ - if dns_container_exists(self.obj.backend): - try: -- dns_zone = self.api.Command.dnszone_show(keys[-1])['result'] -- if ('idnsforwardpolicy' in dns_zone) and dns_zone['idnsforwardpolicy'][0] == u'only': -- instructions.append(_("Forward policy is defined for it in IPA DNS, " -- "perhaps forwarder points to incorrect host?")) -+ dns_zone = self.api.Command.dnszone_show( -+ keys[-1])['result'] -+ -+ if (('idnsforwardpolicy' in dns_zone) and -+ dns_zone['idnsforwardpolicy'][0] == u'only'): -+ -+ instructions.append( -+ _("Forward policy is defined for it in " -+ "IPA DNS, perhaps forwarder points to " -+ "incorrect host?") -+ ) - except (errors.NotFound, KeyError): -- instructions.append(_("IPA manages DNS, please verify " -- "your DNS configuration and " -- "make sure that service records " -- "of the '%(domain)s' domain can " -- "be resolved. Examples how to " -- "configure DNS with CLI commands " -- "or the Web UI can be found in " -- "the documentation. " ) % -- dict(domain=keys[-1])) -+ _instruction = _( -+ "IPA manages DNS, please verify your DNS " -+ "configuration and make sure that service " -+ "records of the '{domain}' domain can be " -+ "resolved. Examples how to configure DNS " -+ "with CLI commands or the Web UI can be " -+ "found in the documentation. " -+ ) -+ instructions.append( -+ unicode(_instruction).format(domain=keys[-1]) -+ ) - else: -- instructions.append(_("Since IPA does not manage DNS records, ensure DNS " -- "is configured to resolve '%(domain)s' domain from " -- "IPA hosts and back.") % dict(domain=keys[-1])) -- raise errors.NotFound(reason=error_message, instructions=instructions) -+ _instruction = _( -+ "Since IPA does not manage DNS records, ensure " -+ "DNS is configured to resolve '{domain}' " -+ "domain from IPA hosts and back." -+ ) -+ instructions.append( -+ unicode(_instruction).format(domain=keys[-1]) -+ ) -+ raise errors.NotFound( -+ reason=error_message, -+ instructions=instructions -+ ) - - if result is None: -- raise errors.ValidationError(name=_('AD Trust setup'), -- error=_('Unable to verify write permissions to the AD')) -+ raise errors.ValidationError( -+ name=_('AD Trust setup'), -+ error=_('Unable to verify write permissions to the AD') -+ ) - - ret = dict( - value=pkey_to_value( -@@ -1019,12 +1068,14 @@ sides. - error=_('Not enough arguments specified to perform trust ' - 'setup')) - -+ - @register() - class trust_del(LDAPDelete): - __doc__ = _('Delete a trust.') - - msg_summary = _('Deleted trust "%(value)s"') - -+ - @register() - class trust_mod(LDAPUpdate): - __doc__ = _(""" -@@ -1037,13 +1088,14 @@ class trust_mod(LDAPUpdate): - msg_summary = _('Modified trust "%(value)s" ' - '(change will be effective in 60 seconds)') - -- def pre_callback(self, ldap, dn, entry_attrs, attrs_list, *keys, **options): -+ def pre_callback(self, ldap, dn, e_attrs, attrs_list, *keys, **options): - assert isinstance(dn, DN) - -- self.obj.validate_sid_blacklists(entry_attrs) -+ self.obj.validate_sid_blacklists(e_attrs) - - return dn - -+ - @register() - class trust_find(LDAPSearch): - __doc__ = _('Search for trusts.') -@@ -1054,9 +1106,10 @@ class trust_find(LDAPSearch): - '%(count)d trust matched', '%(count)d trusts matched', 0 - ) - -- # Since all trusts types are stored within separate containers under 'cn=trusts', -- # search needs to be done on a sub-tree scope -- def pre_callback(self, ldap, filters, attrs_list, base_dn, scope, *args, **options): -+ # Since all trusts types are stored within separate containers -+ # under 'cn=trusts', search needs to be done on a sub-tree scope -+ def pre_callback(self, ldap, filters, attrs_list, -+ base_dn, scope, *args, **options): - # list only trust, not trust domains - return (filters, base_dn, ldap.SCOPE_SUBTREE) - -@@ -1076,13 +1129,16 @@ class trust_find(LDAPSearch): - trust_type = attrs.single_value.get('ipanttrusttype', None) - attributes = attrs.single_value.get('ipanttrustattributes', 0) - if not options.get('raw', False) and trust_type is not None: -- attrs['trusttype'] = [trust_type_string(trust_type, attributes)] -+ attrs['trusttype'] = [ -+ trust_type_string(trust_type, attributes) -+ ] - del attrs['ipanttrusttype'] - if attributes: - del attrs['ipanttrustattributes'] - - return truncated - -+ - @register() - class trust_show(LDAPRetrieve): - __doc__ = _('Display information about a trust.') -@@ -1098,7 +1154,7 @@ class trust_show(LDAPRetrieve): - - return result - -- def post_callback(self, ldap, dn, entry_attrs, *keys, **options): -+ def post_callback(self, ldap, dn, e_attrs, *keys, **options): - - assert isinstance(dn, DN) - # Translate ipanttrusttype to trusttype -@@ -1106,25 +1162,28 @@ class trust_show(LDAPRetrieve): - # if --raw not used - - if not options.get('raw', False): -- trust_type = entry_attrs.single_value.get('ipanttrusttype', None) -- attributes = entry_attrs.single_value.get('ipanttrustattributes', 0) -+ trust_type = e_attrs.single_value.get('ipanttrusttype', None) -+ attributes = e_attrs.single_value.get('ipanttrustattributes', 0) - if trust_type is not None: -- entry_attrs['trusttype'] = [trust_type_string(trust_type, attributes)] -- del entry_attrs['ipanttrusttype'] -+ e_attrs['trusttype'] = [ -+ trust_type_string(trust_type, attributes) -+ ] -+ del e_attrs['ipanttrusttype'] - -- dir_str = entry_attrs.single_value.get('ipanttrustdirection', None) -+ dir_str = e_attrs.single_value.get('ipanttrustdirection', None) - if dir_str is not None: -- entry_attrs['trustdirection'] = [trust_direction_string(dir_str)] -- del entry_attrs['ipanttrustdirection'] -+ e_attrs['trustdirection'] = [trust_direction_string(dir_str)] -+ del e_attrs['ipanttrustdirection'] - - if attributes: -- del entry_attrs['ipanttrustattributes'] -+ del e_attrs['ipanttrustattributes'] - - return dn - - - _trustconfig_dn = { -- u'ad': DN(('cn', api.env.domain), api.env.container_cifsdomains, api.env.basedn), -+ u'ad': DN(('cn', api.env.domain), -+ api.env.container_cifsdomains, api.env.basedn), - } - - -@@ -1184,8 +1243,10 @@ class trustconfig(LDAPObject): - try: - return _trustconfig_dn[kwargs['trust_type']] - except KeyError: -- raise errors.ValidationError(name='trust_type', -- error=_("unsupported trust type")) -+ raise errors.ValidationError( -+ name='trust_type', -+ error=_("unsupported trust type") -+ ) - - def _normalize_groupdn(self, entry_attrs): - """ -@@ -1254,8 +1315,8 @@ class trustconfig_mod(LDAPUpdate): - msg_summary = _('Modified "%(value)s" trust configuration') - has_output = output.simple_entry - -- def pre_callback(self, ldap, dn, entry_attrs, attrs_list, *keys, **options): -- self.obj._normalize_groupdn(entry_attrs) -+ def pre_callback(self, ldap, dn, e_attrs, attrs_list, *keys, **options): -+ self.obj._normalize_groupdn(e_attrs) - return dn - - def execute(self, *keys, **options): -@@ -1263,14 +1324,13 @@ class trustconfig_mod(LDAPUpdate): - result['value'] = pkey_to_value(options['trust_type'], options) - return result - -- def post_callback(self, ldap, dn, entry_attrs, *keys, **options): -- self.obj._convert_groupdn(entry_attrs, options) -+ def post_callback(self, ldap, dn, e_attrs, *keys, **options): -+ self.obj._convert_groupdn(e_attrs, options) - self.api.Object.config.show_servroles_attributes( -- entry_attrs, "AD trust agent", "AD trust controller", **options) -+ e_attrs, "AD trust agent", "AD trust controller", **options) - return dn - - -- - @register() - class trustconfig_show(LDAPRetrieve): - __doc__ = _('Show global trust configuration.') -@@ -1293,18 +1353,21 @@ class trustconfig_show(LDAPRetrieve): - - if _nss_idmap_installed: - _idmap_type_dict = { -- pysss_nss_idmap.ID_USER : 'user', -- pysss_nss_idmap.ID_GROUP : 'group', -- pysss_nss_idmap.ID_BOTH : 'both', -+ pysss_nss_idmap.ID_USER: 'user', -+ pysss_nss_idmap.ID_GROUP: 'group', -+ pysss_nss_idmap.ID_BOTH: 'both', - } -+ - def idmap_type_string(level): - string = _idmap_type_dict.get(int(level), 'unknown') - return unicode(string) - -+ - @register() - class trust_resolve(Command): - NO_CLI = True -- __doc__ = _('Resolve security identifiers of users and groups in trusted domains') -+ __doc__ = _('Resolve security identifiers of users and groups ' -+ 'in trusted domains') - - takes_options = ( - Str('sids+', -@@ -1313,8 +1376,8 @@ class trust_resolve(Command): - ) - - has_output_params = ( -- Str('name', label= _('Name')), -- Str('sid', label= _('SID')), -+ Str('name', label=_('Name')), -+ Str('sid', label=_('SID')), - ) - - has_output = ( -@@ -1326,13 +1389,15 @@ class trust_resolve(Command): - if not _nss_idmap_installed: - return dict(result=result) - try: -+ NAME_KEY = pysss_nss_idmap.NAME_KEY -+ TYPE_KEY = pysss_nss_idmap.TYPE_KEY - sids = [str(x) for x in options['sids']] - xlate = pysss_nss_idmap.getnamebysid(sids) - for sid in xlate: - entry = dict() - entry['sid'] = [unicode(sid)] -- entry['name'] = [unicode(xlate[sid][pysss_nss_idmap.NAME_KEY])] -- entry['type'] = [idmap_type_string(xlate[sid][pysss_nss_idmap.TYPE_KEY])] -+ entry['name'] = [unicode(xlate[sid][NAME_KEY])] -+ entry['type'] = [idmap_type_string(xlate[sid][TYPE_KEY])] - result.append(entry) - except ValueError: - pass -@@ -1340,7 +1405,6 @@ class trust_resolve(Command): - return dict(result=result) - - -- - @register() - class adtrust_is_enabled(Command): - NO_CLI = True -@@ -1367,7 +1431,6 @@ class adtrust_is_enabled(Command): - return dict(result=True) - - -- - @register() - class compat_is_enabled(Command): - NO_CLI = True -@@ -1411,7 +1474,6 @@ class compat_is_enabled(Command): - return dict(result=True) - - -- - @register() - class sidgen_was_run(Command): - """ -@@ -1461,7 +1523,7 @@ class trustdomain(LDAPObject): - Object representing a domain of the AD trust. - """ - parent_object = 'trust' -- trust_type_idx = {'2':u'ad'} -+ trust_type_idx = {'2': u'ad'} - object_name = _('trust domain') - object_name_plural = _('trust domains') - object_class = ['ipaNTTrustedDomain'] -@@ -1478,40 +1540,39 @@ class trustdomain(LDAPObject): - Str('cn', - label=_('Domain name'), - cli_name='domain', -- primary_key=True -- ), -+ primary_key=True), - Str('ipantflatname?', - cli_name='flat_name', -- label=_('Domain NetBIOS name'), -- ), -+ label=_('Domain NetBIOS name')), - Str('ipanttrusteddomainsid?', - cli_name='sid', -- label=_('Domain Security Identifier'), -- ), -+ label=_('Domain Security Identifier')), - Flag('domain_enabled', -- label=_('Domain enabled'), -- flags={'virtual_attribute', 'no_create', 'no_update', 'no_search'}, -- ), -+ label=_('Domain enabled'), -+ flags={'virtual_attribute', -+ 'no_create', 'no_update', 'no_search'}), - ) - -- # LDAPObject.get_dn() only passes all but last element of keys and no kwargs -- # to the parent object's get_dn() no matter what you pass to it. Make own get_dn() -- # as we really need all elements to construct proper dn. -+ # LDAPObject.get_dn() only passes all but last element of keys and no -+ # kwargs to the parent object's get_dn() no matter what you pass to it. -+ # Make own get_dn() as we really need all elements to construct proper dn. - def get_dn(self, *keys, **kwargs): - sdn = [('cn', x) for x in keys] - sdn.reverse() - trust_type = kwargs.get('trust_type') - if not trust_type: -- trust_type=u'ad' -+ trust_type = u'ad' - -- dn=make_trust_dn(self.env, trust_type, DN(*sdn)) -+ dn = make_trust_dn(self.env, trust_type, DN(*sdn)) - return dn - -+ - @register() - class trustdomain_find(LDAPSearch): - __doc__ = _('Search domains of the trust') - -- def pre_callback(self, ldap, filters, attrs_list, base_dn, scope, *args, **options): -+ def pre_callback(self, ldap, filters, attrs_list, base_dn, -+ scope, *args, **options): - return (filters, base_dn, ldap.SCOPE_SUBTREE) - - def post_callback(self, ldap, entries, truncated, *args, **options): -@@ -1532,7 +1593,6 @@ class trustdomain_find(LDAPSearch): - return truncated - - -- - @register() - class trustdomain_mod(LDAPUpdate): - __doc__ = _('Modify trustdomain of the trust') -@@ -1540,31 +1600,36 @@ class trustdomain_mod(LDAPUpdate): - NO_CLI = True - takes_options = LDAPUpdate.takes_options + (_trust_type_option,) - -+ - @register() - class trustdomain_add(LDAPCreate): - __doc__ = _('Allow access from the trusted domain') - NO_CLI = True - - takes_options = LDAPCreate.takes_options + (_trust_type_option,) -- def pre_callback(self, ldap, dn, entry_attrs, attrs_list, *keys, **options): -- # ipaNTTrustPartner must always be set to the name of the trusted domain -- # See MS-ADTS 6.1.6.7.13 -- entry_attrs['ipanttrustpartner'] = [dn[0]['cn']] -+ -+ def pre_callback(self, ldap, dn, e_attrs, attrs_list, *keys, **options): -+ # ipaNTTrustPartner must always be set to the name of the trusted -+ # domain. See MS-ADTS 6.1.6.7.13 -+ e_attrs['ipanttrustpartner'] = [dn[0]['cn']] - return dn - - - @register() - class trustdomain_del(LDAPDelete): -- __doc__ = _('Remove information about the domain associated with the trust.') -+ __doc__ = _('Remove information about the domain associated ' -+ 'with the trust.') - -- msg_summary = _('Removed information about the trusted domain "%(value)s"') -+ msg_summary = _('Removed information about the trusted domain ' -+ '"%(value)s"') - - def execute(self, *keys, **options): - ldap = self.api.Backend.ldap2 - verify_samba_component_presence(ldap, self.api) - -- # Note that pre-/post- callback handling for LDAPDelete is causing pre_callback -- # to always receive empty keys. We need to catch the case when root domain is being deleted -+ # Note that pre-/post- callback handling for LDAPDelete is causing -+ # pre_callback to always receive empty keys. We need to catch the case -+ # when root domain is being deleted - - for domain in keys[1]: - try: -@@ -1603,10 +1668,10 @@ def fetch_domains_from_trust(myapi, trustinstance, **options): - forest_root_name = trustinstance.remote_domain.info['dns_forest'] - - # We want to use Kerberos if we have admin credentials even with SMB calls -- # as eventually use of NTLMSSP will be deprecated for trusted domain operations -- # If admin credentials are missing, 'creds' will be None and fetch_domains -- # will use HTTP/ipa.master@IPA.REALM principal, e.g. Kerberos authentication -- # as well. -+ # as eventually use of NTLMSSP will be deprecated for trusted domain -+ # operations If admin credentials are missing, 'creds' will be None and -+ # fetch_domains will use HTTP/ipa.master@IPA.REALM principal, e.g. Kerberos -+ # authentication as well. - creds = generate_creds(trustinstance, style=CRED_STYLE_KERBEROS, **options) - server = options.get('realm_server', None) - domains = ipaserver.dcerpc.fetch_domains( -@@ -1616,7 +1681,8 @@ def fetch_domains_from_trust(myapi, trustinstance, **options): - return domains - - --def add_new_domains_from_trust(myapi, trustinstance, trust_entry, domains, **options): -+def add_new_domains_from_trust(myapi, trustinstance, trust_entry, -+ domains, **options): - result = [] - if not domains: - return result -@@ -1728,8 +1794,11 @@ class trustdomain_enable(LDAPQuery): - verify_samba_component_presence(ldap, self.api) - - if keys[0].lower() == keys[1].lower(): -- raise errors.ValidationError(name='domain', -- error=_("Root domain of the trust is always enabled for the existing trust")) -+ raise errors.ValidationError( -+ name='domain', -+ error=_("Root domain of the trust is always enabled " -+ "for the existing trust") -+ ) - try: - trust_dn = self.obj.get_dn(keys[0], trust_type=u'ad') - trust_entry = ldap.get_entry(trust_dn) -@@ -1766,8 +1835,11 @@ class trustdomain_disable(LDAPQuery): - verify_samba_component_presence(ldap, self.api) - - if keys[0].lower() == keys[1].lower(): -- raise errors.ValidationError(name='domain', -- error=_("cannot disable root domain of the trust, use trust-del to delete the trust itself")) -+ raise errors.ValidationError( -+ name='domain', -+ error=_("cannot disable root domain of the trust, " -+ "use trust-del to delete the trust itself") -+ ) - try: - trust_dn = self.obj.get_dn(keys[0], trust_type=u'ad') - trust_entry = ldap.get_entry(trust_dn) --- -2.13.6 - diff --git a/SOURCES/0020-Don-t-use-admin-cert-during-KRA-installation.patch b/SOURCES/0020-Don-t-use-admin-cert-during-KRA-installation.patch deleted file mode 100644 index 3762985..0000000 --- a/SOURCES/0020-Don-t-use-admin-cert-during-KRA-installation.patch +++ /dev/null @@ -1,56 +0,0 @@ -From 6e0720dedc113bf82f3b38f2afb76976ed4e8c12 Mon Sep 17 00:00:00 2001 -From: Fraser Tweedale -Date: Wed, 15 Nov 2017 11:59:32 +1100 -Subject: [PATCH] Don't use admin cert during KRA installation - -KRA installation currently imports the admin cert. FreeIPA does not -track this cert and it may be expired, causing installation to fail. -Do not import the existing admin cert, and discard the new admin -cert that gets created during KRA installation. - -Part of: https://pagure.io/freeipa/issue/7287 - -Reviewed-By: Florence Blanc-Renaud -Reviewed-By: Florence Blanc-Renaud ---- - ipaserver/install/krainstance.py | 10 +++++++--- - 1 file changed, 7 insertions(+), 3 deletions(-) - -diff --git a/ipaserver/install/krainstance.py b/ipaserver/install/krainstance.py -index cdd25b9d05bcb1a30260475cc2341a258a3cf93c..990bb87ca2f0029d2450cbef47958399f534f2a6 100644 ---- a/ipaserver/install/krainstance.py -+++ b/ipaserver/install/krainstance.py -@@ -152,6 +152,10 @@ class KRAInstance(DogtagInstance): - prefix="tmp-", dir=paths.VAR_LIB_IPA) - tmp_agent_pwd = ipautil.ipa_generate_password() - -+ # Create a temporary file for the admin PKCS #12 file -+ (admin_p12_fd, admin_p12_file) = tempfile.mkstemp() -+ os.close(admin_p12_fd) -+ - # Create KRA configuration - config = ConfigParser() - config.optionxform = str -@@ -186,9 +190,8 @@ class KRAInstance(DogtagInstance): - config.set("KRA", "pki_admin_nickname", "ipa-ca-agent") - config.set("KRA", "pki_admin_subject_dn", - str(DN(('cn', 'ipa-ca-agent'), self.subject_base))) -- config.set("KRA", "pki_import_admin_cert", "True") -- config.set("KRA", "pki_admin_cert_file", paths.ADMIN_CERT_PATH) -- config.set("KRA", "pki_client_admin_cert_p12", paths.DOGTAG_ADMIN_P12) -+ config.set("KRA", "pki_import_admin_cert", "False") -+ config.set("KRA", "pki_client_admin_cert_p12", admin_p12_file) - - # Directory server - config.set("KRA", "pki_ds_ldap_port", "389") -@@ -291,6 +294,7 @@ class KRAInstance(DogtagInstance): - finally: - os.remove(p12_tmpfile_name) - os.remove(cfg_file) -+ os.remove(admin_p12_file) - - shutil.move(paths.KRA_BACKUP_KEYS_P12, paths.KRACERT_P12) - self.log.debug("completed creating KRA instance") --- -2.13.6 - diff --git a/SOURCES/0020-Handle-races-in-replica-config.patch b/SOURCES/0020-Handle-races-in-replica-config.patch new file mode 100644 index 0000000..466a33d --- /dev/null +++ b/SOURCES/0020-Handle-races-in-replica-config.patch @@ -0,0 +1,267 @@ +From 0598ed6e1c48c6600774c53065716aad759da5e7 Mon Sep 17 00:00:00 2001 +From: Christian Heimes +Date: Tue, 10 Jul 2018 14:03:28 +0200 +Subject: [PATCH] Handle races in replica config + +When multiple replicas are installed in parallel, two replicas may try +to create the cn=replica entry at the same time. This leads to a +conflict on one of the replicas. replica_config() and +ensure_replication_managers() now handle conflicts. + +ipaldap now maps TYPE_OR_VALUE_EXISTS to DuplicateEntry(). The type or +value exists exception is raised, when an attribute value or type is +already set. + +Fixes: https://pagure.io/freeipa/issue/7566 +Signed-off-by: Christian Heimes +Reviewed-By: Thierry Bordaz +--- + ipapython/ipaldap.py | 5 ++ + ipaserver/install/replication.py | 123 ++++++++++++++++++------------- + ipaserver/plugins/ldap2.py | 3 +- + 3 files changed, 79 insertions(+), 52 deletions(-) + +diff --git a/ipapython/ipaldap.py b/ipapython/ipaldap.py +index dcac32c31c670753b83f99eac7f6483fbad2b14d..53fdf4967868961effea7f3f64dfb3c0edfc75f3 100644 +--- a/ipapython/ipaldap.py ++++ b/ipapython/ipaldap.py +@@ -1016,7 +1016,12 @@ class LDAPClient(object): + except ldap.NO_SUCH_OBJECT: + raise errors.NotFound(reason=arg_desc or 'no such entry') + except ldap.ALREADY_EXISTS: ++ # entry already exists + raise errors.DuplicateEntry() ++ except ldap.TYPE_OR_VALUE_EXISTS: ++ # attribute type or attribute value already exists, usually only ++ # occurs, when two machines try to write at the same time. ++ raise errors.DuplicateEntry(message=unicode(desc)) + except ldap.CONSTRAINT_VIOLATION: + # This error gets thrown by the uniqueness plugin + _msg = 'Another entry with the same attribute value already exists' +diff --git a/ipaserver/install/replication.py b/ipaserver/install/replication.py +index d0f92c76c108d237104a81c72567250583ac4ff1..78c4a43cc9b8d9a6740d26209f347f64b879743e 100644 +--- a/ipaserver/install/replication.py ++++ b/ipaserver/install/replication.py +@@ -34,7 +34,7 @@ import ldap + from ipalib import api, errors + from ipalib.cli import textui + from ipalib.text import _ +-from ipapython import ipautil, ipaldap, kerberos ++from ipapython import ipautil, ipaldap + from ipapython.admintool import ScriptError + from ipapython.dn import DN + from ipapython.ipaldap import ldap_initialize +@@ -461,7 +461,7 @@ class ReplicationManager(object): + return DN(('cn', 'replica'), ('cn', self.db_suffix), + ('cn', 'mapping tree'), ('cn', 'config')) + +- def set_replica_binddngroup(self, r_conn, entry): ++ def _set_replica_binddngroup(self, r_conn, entry): + """ + Set nsds5replicabinddngroup attribute on remote master's replica entry. + Older masters (ipa < 3.3) may not support setting this attribute. In +@@ -476,11 +476,6 @@ class ReplicationManager(object): + mod.append((ldap.MOD_ADD, 'nsds5replicabinddngroup', + self.repl_man_group_dn)) + +- if 'nsds5replicabinddngroupcheckinterval' not in entry: +- mod.append( +- (ldap.MOD_ADD, +- 'nsds5replicabinddngroupcheckinterval', +- '60')) + if mod: + try: + r_conn.modify_s(entry.dn, mod) +@@ -488,49 +483,62 @@ class ReplicationManager(object): + logger.debug( + "nsds5replicabinddngroup attribute not supported on " + "remote master.") ++ except (ldap.ALREADY_EXISTS, ldap.CONSTRAINT_VIOLATION): ++ logger.debug("No update to %s necessary", entry.dn) + + def replica_config(self, conn, replica_id, replica_binddn): + assert isinstance(replica_binddn, DN) + dn = self.replica_dn() + assert isinstance(dn, DN) + ++ logger.debug("Add or update replica config %s", dn) + try: + entry = conn.get_entry(dn) + except errors.NotFound: +- pass +- else: +- binddns = entry.setdefault('nsDS5ReplicaBindDN', []) +- if replica_binddn not in {DN(m) for m in binddns}: +- # Add the new replication manager +- binddns.append(replica_binddn) +- for key, value in REPLICA_CREATION_SETTINGS.items(): +- entry[key] = value ++ # no entry, create new one ++ entry = conn.make_entry( ++ dn, ++ objectclass=["top", "nsds5replica", "extensibleobject"], ++ cn=["replica"], ++ nsds5replicaroot=[str(self.db_suffix)], ++ nsds5replicaid=[str(replica_id)], ++ nsds5replicatype=[self.get_replica_type()], ++ nsds5flags=["1"], ++ nsds5replicabinddn=[replica_binddn], ++ nsds5replicabinddngroup=[self.repl_man_group_dn], ++ nsds5replicalegacyconsumer=["off"], ++ **REPLICA_CREATION_SETTINGS ++ ) + try: +- conn.update_entry(entry) +- except errors.EmptyModlist: +- pass ++ conn.add_entry(entry) ++ except errors.DuplicateEntry: ++ logger.debug("Lost race against another replica, updating") ++ # fetch entry that have been added by another replica ++ entry = conn.get_entry(dn) ++ else: ++ logger.debug("Added replica config %s", dn) ++ # added entry successfully ++ return entry + +- self.set_replica_binddngroup(conn, entry) ++ # either existing entry or lost race ++ binddns = entry.setdefault('nsDS5ReplicaBindDN', []) ++ if replica_binddn not in {DN(m) for m in binddns}: ++ # Add the new replication manager ++ binddns.append(replica_binddn) + +- # replication is already configured +- return ++ for key, value in REPLICA_CREATION_SETTINGS.items(): ++ entry[key] = value + +- replica_type = self.get_replica_type() ++ try: ++ conn.update_entry(entry) ++ except errors.EmptyModlist: ++ logger.debug("No update to %s necessary", entry.dn) ++ else: ++ logger.debug("Update replica config %s", entry.dn) + +- entry = conn.make_entry( +- dn, +- objectclass=["top", "nsds5replica", "extensibleobject"], +- cn=["replica"], +- nsds5replicaroot=[str(self.db_suffix)], +- nsds5replicaid=[str(replica_id)], +- nsds5replicatype=[replica_type], +- nsds5flags=["1"], +- nsds5replicabinddn=[replica_binddn], +- nsds5replicabinddngroup=[self.repl_man_group_dn], +- nsds5replicalegacyconsumer=["off"], +- **REPLICA_CREATION_SETTINGS +- ) +- conn.add_entry(entry) ++ self._set_replica_binddngroup(conn, entry) ++ ++ return entry + + def setup_changelog(self, conn): + ent = conn.get_entry( +@@ -690,7 +698,10 @@ class ReplicationManager(object): + uid=["passsync"], + userPassword=[password], + ) +- conn.add_entry(entry) ++ try: ++ conn.add_entry(entry) ++ except errors.DuplicateEntry: ++ pass + + # Add the user to the list of users allowed to bypass password policy + extop_dn = DN(('cn', 'ipa_pwd_extop'), ('cn', 'plugins'), ('cn', 'config')) +@@ -1658,7 +1669,10 @@ class ReplicationManager(object): + objectclass=['top', 'groupofnames'], + cn=['replication managers'] + ) +- conn.add_entry(entry) ++ try: ++ conn.add_entry(entry) ++ except errors.DuplicateEntry: ++ pass + + def ensure_replication_managers(self, conn, r_hostname): + """ +@@ -1668,23 +1682,24 @@ class ReplicationManager(object): + On FreeIPA 3.x masters lacking support for nsds5ReplicaBinddnGroup + attribute, add replica bind DN directly into the replica entry. + """ +- my_princ = kerberos.Principal((u'ldap', unicode(self.hostname)), +- realm=self.realm) +- remote_princ = kerberos.Principal((u'ldap', unicode(r_hostname)), +- realm=self.realm) +- services_dn = DN(api.env.container_service, api.env.basedn) +- +- mydn, remote_dn = tuple( +- DN(('krbprincipalname', unicode(p)), services_dn) for p in ( +- my_princ, remote_princ)) ++ my_dn = DN( ++ ('krbprincipalname', u'ldap/%s@%s' % (self.hostname, self.realm)), ++ api.env.container_service, ++ api.env.basedn ++ ) ++ remote_dn = DN( ++ ('krbprincipalname', u'ldap/%s@%s' % (r_hostname, self.realm)), ++ api.env.container_service, ++ api.env.basedn ++ ) + + try: + conn.get_entry(self.repl_man_group_dn) + except errors.NotFound: +- self._add_replica_bind_dn(conn, mydn) ++ self._add_replica_bind_dn(conn, my_dn) + self._add_replication_managers(conn) + +- self._add_dn_to_replication_managers(conn, mydn) ++ self._add_dn_to_replication_managers(conn, my_dn) + self._add_dn_to_replication_managers(conn, remote_dn) + + def add_temp_sasl_mapping(self, conn, r_hostname): +@@ -1704,7 +1719,10 @@ class ReplicationManager(object): + + entry = conn.get_entry(self.replica_dn()) + entry['nsDS5ReplicaBindDN'].append(replica_binddn) +- conn.update_entry(entry) ++ try: ++ conn.update_entry(entry) ++ except errors.EmptyModlist: ++ pass + + entry = conn.make_entry( + DN(('cn', 'Peer Master'), ('cn', 'mapping'), ('cn', 'sasl'), +@@ -1716,7 +1734,10 @@ class ReplicationManager(object): + nsSaslMapFilterTemplate=['(cn=&@%s)' % self.realm], + nsSaslMapPriority=['1'], + ) +- conn.add_entry(entry) ++ try: ++ conn.add_entry(entry) ++ except errors.DuplicateEntry: ++ pass + + def remove_temp_replication_user(self, conn, r_hostname): + """ +diff --git a/ipaserver/plugins/ldap2.py b/ipaserver/plugins/ldap2.py +index 8745866ad11cf6248d2b9dcff560c0a5ee6d109b..5d39cdf628d7e20ce5b259d97b479ef7c8ed88d5 100644 +--- a/ipaserver/plugins/ldap2.py ++++ b/ipaserver/plugins/ldap2.py +@@ -419,7 +419,8 @@ class ldap2(CrudBackend, LDAPClient): + modlist = [(a, b, self.encode(c)) + for a, b, c in modlist] + self.conn.modify_s(str(group_dn), modlist) +- except errors.DatabaseError: ++ except errors.DuplicateEntry: ++ # TYPE_OR_VALUE_EXISTS + raise errors.AlreadyGroupMember() + + def remove_entry_from_group(self, dn, group_dn, member_attr='member'): +-- +2.17.1 + diff --git a/SOURCES/0021-389-ds-base-crashed-as-part-of-ipa-server-intall-in-.patch b/SOURCES/0021-389-ds-base-crashed-as-part-of-ipa-server-intall-in-.patch deleted file mode 100644 index ef027e3..0000000 --- a/SOURCES/0021-389-ds-base-crashed-as-part-of-ipa-server-intall-in-.patch +++ /dev/null @@ -1,72 +0,0 @@ -From b047d30b8aabad424fa2bd30872721f9fab9e325 Mon Sep 17 00:00:00 2001 -From: Thierry Bordaz -Date: Mon, 25 Sep 2017 16:41:51 +0200 -Subject: [PATCH] 389-ds-base crashed as part of ipa-server-intall in ipa-uuid - -Bug Description: - When adding an entry, ipa-uuid plugin may generate a unique value - for some of its attribute. - If the generated attribute is part of the RDN, the target DN - is replaced on the fly and the previous one freed. - Unfortunately, previous DN may be later used instead of - the new one. - -Fix Description: - Make sure to use only the current DN of the operation - -https://bugzilla.redhat.com/show_bug.cgi?id=1496226 -https://pagure.io/freeipa/issue/7227 - -Reviewed-By: Alexander Bokovoy ---- - daemons/ipa-slapi-plugins/ipa-uuid/ipa_uuid.c | 12 +++++++++--- - 1 file changed, 9 insertions(+), 3 deletions(-) - -diff --git a/daemons/ipa-slapi-plugins/ipa-uuid/ipa_uuid.c b/daemons/ipa-slapi-plugins/ipa-uuid/ipa_uuid.c -index ffade14672e8cd9e3f3e18d45a0a7095a6341d30..87d8be2d88d9ff9bbf7d47eab57b765063f7a230 100644 ---- a/daemons/ipa-slapi-plugins/ipa-uuid/ipa_uuid.c -+++ b/daemons/ipa-slapi-plugins/ipa-uuid/ipa_uuid.c -@@ -911,6 +911,7 @@ static int ipauuid_pre_op(Slapi_PBlock *pb, int modtype) - list != ipauuid_global_config; - list = PR_NEXT_LINK(list)) { - cfgentry = (struct configEntry *) list; -+ char *current_dn = NULL; - - generate = false; - set_attr = false; -@@ -920,16 +921,21 @@ static int ipauuid_pre_op(Slapi_PBlock *pb, int modtype) - cfgentry->attr)) { - continue; - } -+ /* Current DN may have been reset by -+ * slapi_pblock_set(pb, SLAPI_ADD_TARGET,..) see below -+ * need to reread it -+ */ -+ current_dn = ipauuid_get_dn(pb); - - /* is the entry in scope? */ - if (cfgentry->scope) { -- if (!slapi_dn_issuffix(dn, cfgentry->scope)) { -+ if (!slapi_dn_issuffix(current_dn, cfgentry->scope)) { - continue; - } - } - - if (cfgentry->exclude_subtree) { -- if (slapi_dn_issuffix(dn, cfgentry->exclude_subtree)) { -+ if (slapi_dn_issuffix(current_dn, cfgentry->exclude_subtree)) { - continue; - } - } -@@ -1108,7 +1114,7 @@ static int ipauuid_pre_op(Slapi_PBlock *pb, int modtype) - ret = LDAP_OPERATIONS_ERROR; - goto done; - } -- sdn = slapi_sdn_new_dn_byval(dn); -+ sdn = slapi_sdn_new_dn_byval(current_dn); - if (!sdn) { - LOG_OOM(); - ret = LDAP_OPERATIONS_ERROR; --- -2.13.6 - diff --git a/SOURCES/0021-Fix-regression-Handle-unicode-where-str-is-expected.patch b/SOURCES/0021-Fix-regression-Handle-unicode-where-str-is-expected.patch new file mode 100644 index 0000000..5e8c1ab --- /dev/null +++ b/SOURCES/0021-Fix-regression-Handle-unicode-where-str-is-expected.patch @@ -0,0 +1,33 @@ +From d12aa9762dcd841661cc4fb384a4700d53b5bef2 Mon Sep 17 00:00:00 2001 +From: Armando Neto +Date: Tue, 17 Jul 2018 16:08:49 -0300 +Subject: [PATCH] Fix regression: Handle unicode where str is expected + +Regression caused by 947ac4bc1f6f4016cf5baf2ecb4577e893bc3948 when +trying to fix a similar issue for clients running Python 3. However, +that fix broke Python 2 clients. + +Issue: https://pagure.io/freeipa/issue/7626 + +Signed-off-by: Armando Neto +Reviewed-By: Christian Heimes +--- + ipaclient/remote_plugins/schema.py | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/ipaclient/remote_plugins/schema.py b/ipaclient/remote_plugins/schema.py +index 863d8f19923898c0a531c68189dd01b401e531b4..9d2c8ca9d1d3e820154425c4f9e4e39cd64d6fd1 100644 +--- a/ipaclient/remote_plugins/schema.py ++++ b/ipaclient/remote_plugins/schema.py +@@ -602,7 +602,7 @@ def get_package(server_info, client): + s = topic['topic_topic'] + if isinstance(s, bytes): + s = s.decode('utf-8') +- module.topic = s.partition('/')[0] ++ module.topic = str(s).partition('/')[0] + else: + module.topic = None + +-- +2.17.1 + diff --git a/SOURCES/0022-In-IPA-4.4-when-updating-userpassword-with-ldapmodif.patch b/SOURCES/0022-In-IPA-4.4-when-updating-userpassword-with-ldapmodif.patch new file mode 100644 index 0000000..c4131cb --- /dev/null +++ b/SOURCES/0022-In-IPA-4.4-when-updating-userpassword-with-ldapmodif.patch @@ -0,0 +1,48 @@ +From 8d72395890fc47d0ec6f1d810b0ea063e28575f5 Mon Sep 17 00:00:00 2001 +From: Thierry Bordaz +Date: Tue, 24 Jul 2018 12:16:35 +0200 +Subject: [PATCH] In IPA 4.4 when updating userpassword with ldapmodify does + not update krbPasswordExpiration nor krbLastPwdChange + +When making ipa-pwd-extop TXN aware, some callbacks are call twice. +Particularily + ipapwd_pre_add is called during PRE_ADD and TXN_PRE_ADD + ipapwd_pre_mod is called during PRE_MOD and TXN_PRE_MOD + ipapwd_post_modadd is called during POST_ADD and TXN_POST_ADD + ipapwd_post_modadd is called during POST_MOD and TXN_POST_MOD +It is not the expected behavior and it results on some skipped updates krbPasswordExpiration +and krbLastPwdChange + +https://pagure.io/freeipa/issue/7601 + +Reviewed-By: Florence Blanc-Renaud +--- + daemons/ipa-slapi-plugins/ipa-pwd-extop/prepost.c | 4 ---- + 1 file changed, 4 deletions(-) + +diff --git a/daemons/ipa-slapi-plugins/ipa-pwd-extop/prepost.c b/daemons/ipa-slapi-plugins/ipa-pwd-extop/prepost.c +index c62eae33418f8368c831a91f611915addf7d423b..209d596255d05bfd03de432b7930e6406cc025dc 100644 +--- a/daemons/ipa-slapi-plugins/ipa-pwd-extop/prepost.c ++++ b/daemons/ipa-slapi-plugins/ipa-pwd-extop/prepost.c +@@ -1488,8 +1488,6 @@ int ipapwd_pre_init(Slapi_PBlock *pb) + ret = slapi_pblock_set(pb, SLAPI_PLUGIN_VERSION, SLAPI_PLUGIN_VERSION_01); + if (!ret) ret = slapi_pblock_set(pb, SLAPI_PLUGIN_DESCRIPTION, (void *)&ipapwd_plugin_desc); + if (!ret) ret = slapi_pblock_set(pb, SLAPI_PLUGIN_PRE_BIND_FN, (void *)ipapwd_pre_bind); +- if (!ret) ret = slapi_pblock_set(pb, SLAPI_PLUGIN_PRE_ADD_FN, (void *)ipapwd_pre_add); +- if (!ret) ret = slapi_pblock_set(pb, SLAPI_PLUGIN_PRE_MODIFY_FN, (void *)ipapwd_pre_mod); + + return ret; + } +@@ -1513,9 +1511,7 @@ int ipapwd_post_init(Slapi_PBlock *pb) + + ret = slapi_pblock_set(pb, SLAPI_PLUGIN_VERSION, SLAPI_PLUGIN_VERSION_01); + if (!ret) ret = slapi_pblock_set(pb, SLAPI_PLUGIN_DESCRIPTION, (void *)&ipapwd_plugin_desc); +- if (!ret) ret = slapi_pblock_set(pb, SLAPI_PLUGIN_POST_ADD_FN, (void *)ipapwd_post_modadd); + if (!ret) ret = slapi_pblock_set(pb, SLAPI_PLUGIN_POST_DELETE_FN, (void *)ipapwd_post_updatecfg); +- if (!ret) ret = slapi_pblock_set(pb, SLAPI_PLUGIN_POST_MODIFY_FN, (void *)ipapwd_post_modadd); + if (!ret) ret = slapi_pblock_set(pb, SLAPI_PLUGIN_POST_MODRDN_FN, (void *)ipapwd_post_updatecfg); + + return ret; +-- +2.17.1 + diff --git a/SOURCES/0022-Prevent-set_directive-from-clobbering-other-keys.patch b/SOURCES/0022-Prevent-set_directive-from-clobbering-other-keys.patch deleted file mode 100644 index 85e3e42..0000000 --- a/SOURCES/0022-Prevent-set_directive-from-clobbering-other-keys.patch +++ /dev/null @@ -1,84 +0,0 @@ -From 8ea1652c64956eea6dd0708f61b3330befcf1a31 Mon Sep 17 00:00:00 2001 -From: Fraser Tweedale -Date: Sat, 21 Nov 2020 08:47:57 +1100 -Subject: [PATCH] Prevent set_directive from clobbering other keys - -`set_directive` only looks for a prefix of the line matching the -given directive (key). If a directive is encountered for which the -given key is prefix, it will be vanquished. - -This occurs in the case of `{ca,kra}.sslserver.cert[req]`; the -`cert` directive gets updated after certificate renewal, and the -`certreq` directive gets clobbered. This can cause failures later -on during KRA installation, and possibly cloning. - -Match the whole directive to avoid this issue. - -Fixes: https://pagure.io/freeipa/issue/7288 -Reviewed-By: Florence Blanc-Renaud ---- - ipaserver/install/cainstance.py | 2 +- - ipaserver/install/dogtaginstance.py | 2 +- - ipaserver/install/installutils.py | 6 +++--- - 3 files changed, 5 insertions(+), 5 deletions(-) - -diff --git a/ipaserver/install/cainstance.py b/ipaserver/install/cainstance.py -index 62f79b28000b015edb66f4c39a270097ab3ed666..f45d2c8b89ba4b81be5acbbe85f256e85ef630fb 100644 ---- a/ipaserver/install/cainstance.py -+++ b/ipaserver/install/cainstance.py -@@ -931,7 +931,7 @@ class CAInstance(DogtagInstance): - installutils.set_directive(caconfig, 'ca.publish.rule.instance.FileCrlRule.enable', 'true', quotes=False, separator='=') - installutils.set_directive(caconfig, 'ca.publish.rule.instance.FileCrlRule.mapper', 'NoMap', quotes=False, separator='=') - installutils.set_directive(caconfig, 'ca.publish.rule.instance.FileCrlRule.pluginName', 'Rule', quotes=False, separator='=') -- installutils.set_directive(caconfig, 'ca.publish.rule.instance.FileCrlRule.predicate=', '', quotes=False, separator='') -+ installutils.set_directive(caconfig, 'ca.publish.rule.instance.FileCrlRule.predicate', '', quotes=False, separator='=') - installutils.set_directive(caconfig, 'ca.publish.rule.instance.FileCrlRule.publisher', 'FileBaseCRLPublisher', quotes=False, separator='=') - installutils.set_directive(caconfig, 'ca.publish.rule.instance.FileCrlRule.type', 'crl', quotes=False, separator='=') - -diff --git a/ipaserver/install/dogtaginstance.py b/ipaserver/install/dogtaginstance.py -index 1fdc3e50a46877374e4f1aa8435d09f6b4e62180..9470e1a13608a8a84aab8a36c269a708e3f3e9f4 100644 ---- a/ipaserver/install/dogtaginstance.py -+++ b/ipaserver/install/dogtaginstance.py -@@ -212,7 +212,7 @@ class DogtagInstance(service.Service): - separator='=') - # Remove internaldb password as is not needed anymore - installutils.set_directive(paths.PKI_TOMCAT_PASSWORD_CONF, -- 'internaldb', None) -+ 'internaldb', None, separator='=') - - def uninstall(self): - if self.is_installed(): -diff --git a/ipaserver/install/installutils.py b/ipaserver/install/installutils.py -index 01930c4de6f0edd16b31aeba1c926fe581e9635b..821609beb533fcc9064500a88ccd07b35142f1df 100644 ---- a/ipaserver/install/installutils.py -+++ b/ipaserver/install/installutils.py -@@ -433,7 +433,7 @@ def set_directive(filename, directive, value, quotes=True, separator=' '): - - A value of None means to drop the directive. - -- This has only been tested with nss.conf -+ Does not tolerate (or put) spaces around the separator. - - :param filename: input filename - :param directive: directive name -@@ -442,7 +442,7 @@ def set_directive(filename, directive, value, quotes=True, separator=' '): - any existing double quotes are first escaped to avoid - unparseable directives. - :param separator: character serving as separator between directive and -- value -+ value. Correct value required even when dropping a directive. - """ - - new_directive_value = "" -@@ -457,7 +457,7 @@ def set_directive(filename, directive, value, quotes=True, separator=' '): - fd = open(filename) - newfile = [] - for line in fd: -- if line.lstrip().startswith(directive): -+ if re.match(r'\s*{}'.format(re.escape(directive + separator)), line): - valueset = True - if value is not None: - newfile.append(new_directive_value) --- -2.13.6 - diff --git a/SOURCES/0023-Move-fips_enabled-to-a-common-library-to-share-acros.patch b/SOURCES/0023-Move-fips_enabled-to-a-common-library-to-share-acros.patch new file mode 100644 index 0000000..24c52bd --- /dev/null +++ b/SOURCES/0023-Move-fips_enabled-to-a-common-library-to-share-acros.patch @@ -0,0 +1,116 @@ +From 08ada3f8d7f80067a1b43e6172394d1326e3d178 Mon Sep 17 00:00:00 2001 +From: Alexander Bokovoy +Date: Wed, 8 Aug 2018 12:28:53 +0300 +Subject: [PATCH] Move fips_enabled to a common library to share across + different plugins + +Related: https://pagure.io/freeipa/issue/7659 +Reviewed-By: Robbie Harwood +--- + .../ipa-slapi-plugins/ipa-pwd-extop/common.c | 24 +----------------- + util/ipa_pwd.c | 25 +++++++++++++++++++ + util/ipa_pwd.h | 2 ++ + 3 files changed, 28 insertions(+), 23 deletions(-) + +diff --git a/daemons/ipa-slapi-plugins/ipa-pwd-extop/common.c b/daemons/ipa-slapi-plugins/ipa-pwd-extop/common.c +index 5efadac5b1fd57e5f91a886224fa2f1ab88305ac..db7183bf2b115dcb0c21f7a6bdb8e55db26224c4 100644 +--- a/daemons/ipa-slapi-plugins/ipa-pwd-extop/common.c ++++ b/daemons/ipa-slapi-plugins/ipa-pwd-extop/common.c +@@ -46,7 +46,6 @@ + /* Type of connection for this operation;*/ + #define LDAP_EXTOP_PASSMOD_CONN_SECURE + +-#define PROC_SYS_FIPS "/proc/sys/crypto/fips_enabled" + + /* Uncomment the following #undef FOR TESTING: + * allows non-SSL connections to use the password change extended op */ +@@ -64,27 +63,6 @@ static const char *ipapwd_def_encsalts[] = { + NULL + }; + +-static bool fips_enabled(void) +-{ +- int fd; +- ssize_t len; +- char buf[8]; +- +- fd = open(PROC_SYS_FIPS, O_RDONLY); +- if (fd != -1) { +- len = read(fd, buf, sizeof(buf)); +- close(fd); +- /* Assume FIPS in enabled if PROC_SYS_FIPS contains a non-0 value +- * similar to the is_fips_enabled() check in +- * ipaplatform/redhat/tasks.py */ +- if (!(len == 2 && buf[0] == '0' && buf[1] == '\n')) { +- return true; +- } +- } +- +- return false; +-} +- + static struct ipapwd_krbcfg *ipapwd_getConfig(void) + { + krb5_error_code krberr; +@@ -255,7 +233,7 @@ static struct ipapwd_krbcfg *ipapwd_getConfig(void) + + /* get the ipa etc/ipaConfig entry */ + config->allow_nt_hash = false; +- if (fips_enabled()) { ++ if (ipapwd_fips_enabled()) { + LOG("FIPS mode is enabled, NT hashes are not allowed.\n"); + } else { + ret = ipapwd_getEntry(ipa_etc_config_dn, &config_entry, NULL); +diff --git a/util/ipa_pwd.c b/util/ipa_pwd.c +index f6564c8021c656031d3f459dd50d830934c7143b..9890c980cacad08302fb5305c3aa9598a81ec681 100644 +--- a/util/ipa_pwd.c ++++ b/util/ipa_pwd.c +@@ -27,6 +27,8 @@ + #include + #include + #include ++#include ++#include + #include + #include + #include +@@ -656,3 +658,26 @@ done: + free(hash); + return ret; + } ++ ++#define PROC_SYS_FIPS "/proc/sys/crypto/fips_enabled" ++ ++bool ipapwd_fips_enabled(void) ++{ ++ int fd; ++ ssize_t len; ++ char buf[8]; ++ ++ fd = open(PROC_SYS_FIPS, O_RDONLY); ++ if (fd != -1) { ++ len = read(fd, buf, sizeof(buf)); ++ close(fd); ++ /* Assume FIPS in enabled if PROC_SYS_FIPS contains a non-0 value ++ * similar to the is_fips_enabled() check in ++ * ipaplatform/redhat/tasks.py */ ++ if (!(len == 2 && buf[0] == '0' && buf[1] == '\n')) { ++ return true; ++ } ++ } ++ ++ return false; ++} +diff --git a/util/ipa_pwd.h b/util/ipa_pwd.h +index b3ee75063adc4baa93bbd4991161bebe1d233bb8..664c8b1827591e716095d9ef1727e422c7d82680 100644 +--- a/util/ipa_pwd.h ++++ b/util/ipa_pwd.h +@@ -77,3 +77,5 @@ int ipapwd_generate_new_history(char *password, + int *new_pwd_hlen); + + int encode_nt_key(char *newPasswd, uint8_t *nt_key); ++ ++bool ipapwd_fips_enabled(void); +-- +2.17.1 + diff --git a/SOURCES/0023-pep8-reduce-line-lengths-in-CAInstance.__enable_crl_.patch b/SOURCES/0023-pep8-reduce-line-lengths-in-CAInstance.__enable_crl_.patch deleted file mode 100644 index e0f1a0c..0000000 --- a/SOURCES/0023-pep8-reduce-line-lengths-in-CAInstance.__enable_crl_.patch +++ /dev/null @@ -1,113 +0,0 @@ -From a87278422807aa4004b63e8e46ad38dab5a911f3 Mon Sep 17 00:00:00 2001 -From: Fraser Tweedale -Date: Thu, 30 Nov 2017 12:00:53 +1100 -Subject: [PATCH] pep8: reduce line lengths in CAInstance.__enable_crl_publish - -Part of: https://pagure.io/freeipa/issue/7288 - -Reviewed-By: Florence Blanc-Renaud ---- - ipaserver/install/cainstance.py | 71 ++++++++++++++++++++++++----------------- - 1 file changed, 41 insertions(+), 30 deletions(-) - -diff --git a/ipaserver/install/cainstance.py b/ipaserver/install/cainstance.py -index f45d2c8b89ba4b81be5acbbe85f256e85ef630fb..dd410d1c97cb4b27d35086bb2f511c42c02d022f 100644 ---- a/ipaserver/install/cainstance.py -+++ b/ipaserver/install/cainstance.py -@@ -907,52 +907,63 @@ class CAInstance(DogtagInstance): - - https://access.redhat.com/knowledge/docs/en-US/Red_Hat_Certificate_System/8.0/html/Admin_Guide/Setting_up_Publishing.html - """ -- caconfig = paths.CA_CS_CFG_PATH - -- publishdir = self.prepare_crl_publish_dir() -+ def put(k, v): -+ installutils.set_directive( -+ paths.CA_CS_CFG_PATH, k, v, quotes=False, separator='=') - - # Enable file publishing, disable LDAP -- installutils.set_directive(caconfig, 'ca.publish.enable', 'true', quotes=False, separator='=') -- installutils.set_directive(caconfig, 'ca.publish.ldappublish.enable', 'false', quotes=False, separator='=') -+ put('ca.publish.enable', 'true') -+ put('ca.publish.ldappublish.enable', 'false') - - # Create the file publisher, der only, not b64 -- installutils.set_directive(caconfig, 'ca.publish.publisher.impl.FileBasedPublisher.class','com.netscape.cms.publish.publishers.FileBasedPublisher', quotes=False, separator='=') -- installutils.set_directive(caconfig, 'ca.publish.publisher.instance.FileBaseCRLPublisher.crlLinkExt', 'bin', quotes=False, separator='=') -- installutils.set_directive(caconfig, 'ca.publish.publisher.instance.FileBaseCRLPublisher.directory', publishdir, quotes=False, separator='=') -- installutils.set_directive(caconfig, 'ca.publish.publisher.instance.FileBaseCRLPublisher.latestCrlLink', 'true', quotes=False, separator='=') -- installutils.set_directive(caconfig, 'ca.publish.publisher.instance.FileBaseCRLPublisher.pluginName', 'FileBasedPublisher', quotes=False, separator='=') -- installutils.set_directive(caconfig, 'ca.publish.publisher.instance.FileBaseCRLPublisher.timeStamp', 'LocalTime', quotes=False, separator='=') -- installutils.set_directive(caconfig, 'ca.publish.publisher.instance.FileBaseCRLPublisher.zipCRLs', 'false', quotes=False, separator='=') -- installutils.set_directive(caconfig, 'ca.publish.publisher.instance.FileBaseCRLPublisher.zipLevel', '9', quotes=False, separator='=') -- installutils.set_directive(caconfig, 'ca.publish.publisher.instance.FileBaseCRLPublisher.Filename.b64', 'false', quotes=False, separator='=') -- installutils.set_directive(caconfig, 'ca.publish.publisher.instance.FileBaseCRLPublisher.Filename.der', 'true', quotes=False, separator='=') -+ put('ca.publish.publisher.impl.FileBasedPublisher.class', -+ 'com.netscape.cms.publish.publishers.FileBasedPublisher') -+ put('ca.publish.publisher.instance.FileBaseCRLPublisher.crlLinkExt', -+ 'bin') -+ put('ca.publish.publisher.instance.FileBaseCRLPublisher.directory', -+ self.prepare_crl_publish_dir()) -+ put('ca.publish.publisher.instance.FileBaseCRLPublisher.latestCrlLink', -+ 'true') -+ put('ca.publish.publisher.instance.FileBaseCRLPublisher.pluginName', -+ 'FileBasedPublisher') -+ put('ca.publish.publisher.instance.FileBaseCRLPublisher.timeStamp', -+ 'LocalTime') -+ put('ca.publish.publisher.instance.FileBaseCRLPublisher.zipCRLs', -+ 'false') -+ put('ca.publish.publisher.instance.FileBaseCRLPublisher.zipLevel', '9') -+ put('ca.publish.publisher.instance.FileBaseCRLPublisher.Filename.b64', -+ 'false') -+ put('ca.publish.publisher.instance.FileBaseCRLPublisher.Filename.der', -+ 'true') - - # The publishing rule -- installutils.set_directive(caconfig, 'ca.publish.rule.instance.FileCrlRule.enable', 'true', quotes=False, separator='=') -- installutils.set_directive(caconfig, 'ca.publish.rule.instance.FileCrlRule.mapper', 'NoMap', quotes=False, separator='=') -- installutils.set_directive(caconfig, 'ca.publish.rule.instance.FileCrlRule.pluginName', 'Rule', quotes=False, separator='=') -- installutils.set_directive(caconfig, 'ca.publish.rule.instance.FileCrlRule.predicate', '', quotes=False, separator='=') -- installutils.set_directive(caconfig, 'ca.publish.rule.instance.FileCrlRule.publisher', 'FileBaseCRLPublisher', quotes=False, separator='=') -- installutils.set_directive(caconfig, 'ca.publish.rule.instance.FileCrlRule.type', 'crl', quotes=False, separator='=') -+ put('ca.publish.rule.instance.FileCrlRule.enable', 'true') -+ put('ca.publish.rule.instance.FileCrlRule.mapper', 'NoMap') -+ put('ca.publish.rule.instance.FileCrlRule.pluginName', 'Rule') -+ put('ca.publish.rule.instance.FileCrlRule.predicate', '') -+ put('ca.publish.rule.instance.FileCrlRule.publisher', -+ 'FileBaseCRLPublisher') -+ put('ca.publish.rule.instance.FileCrlRule.type', 'crl') - - # Now disable LDAP publishing -- installutils.set_directive(caconfig, 'ca.publish.rule.instance.LdapCaCertRule.enable', 'false', quotes=False, separator='=') -- installutils.set_directive(caconfig, 'ca.publish.rule.instance.LdapCrlRule.enable', 'false', quotes=False, separator='=') -- installutils.set_directive(caconfig, 'ca.publish.rule.instance.LdapUserCertRule.enable', 'false', quotes=False, separator='=') -- installutils.set_directive(caconfig, 'ca.publish.rule.instance.LdapXCertRule.enable', 'false', quotes=False, separator='=') -+ put('ca.publish.rule.instance.LdapCaCertRule.enable', 'false') -+ put('ca.publish.rule.instance.LdapCrlRule.enable', 'false') -+ put('ca.publish.rule.instance.LdapUserCertRule.enable', 'false') -+ put('ca.publish.rule.instance.LdapXCertRule.enable', 'false') - - # If we are the initial master then we are the CRL generator, otherwise - # we point to that master for CRLs. - if not self.clone: - # These next two are defaults, but I want to be explicit that the - # initial master is the CRL generator. -- installutils.set_directive(caconfig, 'ca.crl.MasterCRL.enableCRLCache', 'true', quotes=False, separator='=') -- installutils.set_directive(caconfig, 'ca.crl.MasterCRL.enableCRLUpdates', 'true', quotes=False, separator='=') -- installutils.set_directive(caconfig, 'ca.listenToCloneModifications', 'true', quotes=False, separator='=') -+ put('ca.crl.MasterCRL.enableCRLCache', 'true') -+ put('ca.crl.MasterCRL.enableCRLUpdates', 'true') -+ put('ca.listenToCloneModifications', 'true') - else: -- installutils.set_directive(caconfig, 'ca.crl.MasterCRL.enableCRLCache', 'false', quotes=False, separator='=') -- installutils.set_directive(caconfig, 'ca.crl.MasterCRL.enableCRLUpdates', 'false', quotes=False, separator='=') -- installutils.set_directive(caconfig, 'ca.listenToCloneModifications', 'false', quotes=False, separator='=') -+ put('ca.crl.MasterCRL.enableCRLCache', 'false') -+ put('ca.crl.MasterCRL.enableCRLUpdates', 'false') -+ put('ca.listenToCloneModifications', 'false') - - def uninstall(self): - # just eat state --- -2.13.6 - diff --git a/SOURCES/0024-installutils-refactor-set_directive.patch b/SOURCES/0024-installutils-refactor-set_directive.patch deleted file mode 100644 index 643edb9..0000000 --- a/SOURCES/0024-installutils-refactor-set_directive.patch +++ /dev/null @@ -1,92 +0,0 @@ -From 25a6726a350fd4192b45a78b6312ab8345b02586 Mon Sep 17 00:00:00 2001 -From: Fraser Tweedale -Date: Tue, 5 Dec 2017 13:43:04 +1100 -Subject: [PATCH] installutils: refactor set_directive - -To separate concerns and make it easier to test set_directive, -extract function ``set_directive_lines`` to do the line-wise -search/replace, leaving ``set_directive`` to deal with the file -handling. - -Part of: https://pagure.io/freeipa/issue/7288 - -Reviewed-By: Florence Blanc-Renaud ---- - ipaserver/install/installutils.py | 56 +++++++++++++++++++++++---------------- - 1 file changed, 33 insertions(+), 23 deletions(-) - -diff --git a/ipaserver/install/installutils.py b/ipaserver/install/installutils.py -index 821609beb533fcc9064500a88ccd07b35142f1df..b56cf591496c679e5fcf3e94f458c286216eb1e4 100644 ---- a/ipaserver/install/installutils.py -+++ b/ipaserver/install/installutils.py -@@ -444,34 +444,44 @@ def set_directive(filename, directive, value, quotes=True, separator=' '): - :param separator: character serving as separator between directive and - value. Correct value required even when dropping a directive. - """ -+ st = os.stat(filename) -+ with open(filename, 'r') as f: -+ lines = list(f) # read the whole file -+ new_lines = set_directive_lines( -+ quotes, separator, directive, value, lines) -+ with open(filename, 'w') as f: -+ # don't construct the whole string; write line-wise -+ for line in new_lines: -+ f.write(line) -+ os.chown(filename, st.st_uid, st.st_gid) # reset perms - -- new_directive_value = "" -- if value is not None: -- value_to_set = quote_directive_value(value, '"') if quotes else value - -- new_directive_value = "".join( -- [directive, separator, value_to_set, '\n']) -+def set_directive_lines(quotes, separator, k, v, lines): -+ """Set a name/value pair in a configuration (iterable of lines). - -- valueset = False -- st = os.stat(filename) -- fd = open(filename) -- newfile = [] -- for line in fd: -- if re.match(r'\s*{}'.format(re.escape(directive + separator)), line): -- valueset = True -- if value is not None: -- newfile.append(new_directive_value) -+ Replaces the value of the key if found, otherwise adds it at -+ end. If value is ``None``, remove the key if found. -+ -+ Takes an iterable of lines (with trailing newline). -+ Yields lines (with trailing newline). -+ -+ """ -+ new_line = "" -+ if v is not None: -+ v_quoted = quote_directive_value(v, '"') if quotes else v -+ new_line = ''.join([k, separator, v_quoted, '\n']) -+ -+ found = False -+ for line in lines: -+ if re.match(r'\s*{}'.format(re.escape(k + separator)), line): -+ found = True -+ if v is not None: -+ yield new_line - else: -- newfile.append(line) -- fd.close() -- if not valueset: -- if value is not None: -- newfile.append(new_directive_value) -+ yield line - -- fd = open(filename, "w") -- fd.write("".join(newfile)) -- fd.close() -- os.chown(filename, st.st_uid, st.st_gid) # reset perms -+ if not found and v is not None: -+ yield new_line - - - def get_directive(filename, directive, separator=' '): --- -2.13.6 - diff --git a/SOURCES/0024-ipasam-do-not-use-RC4-in-FIPS-mode.patch b/SOURCES/0024-ipasam-do-not-use-RC4-in-FIPS-mode.patch new file mode 100644 index 0000000..041ca91 --- /dev/null +++ b/SOURCES/0024-ipasam-do-not-use-RC4-in-FIPS-mode.patch @@ -0,0 +1,96 @@ +From 1a7538f0d73b4b35769c4df5ba32ed836e26a648 Mon Sep 17 00:00:00 2001 +From: Alexander Bokovoy +Date: Wed, 8 Aug 2018 13:04:04 +0300 +Subject: [PATCH] ipasam: do not use RC4 in FIPS mode + +When creating Kerberos keys for trusted domain object account, ipasam +module requests to generate keys using a series of well-known encryption +types. In FIPS mode it is not possible to generate RC4-HMAC key: +MIT Kerberos is using openssl crypto backend and openssl does not allow +use of RC4 in FIPS mode. + +Thus, we have to filter out RC4-HMAC encryption type when running in +FIPS mode. A side-effect is that a trust to Active Directory running +with Windows Server 2003 will not be possible anymore in FIPS mode. + +Resolves: https://pagure.io/freeipa/issue/7659 +Reviewed-By: Robbie Harwood +--- + daemons/ipa-sam/ipa_sam.c | 23 +++++++++++++++++++---- + 1 file changed, 19 insertions(+), 4 deletions(-) + +diff --git a/daemons/ipa-sam/ipa_sam.c b/daemons/ipa-sam/ipa_sam.c +index 0cd48d845b2edf9f328de0a949f80f98f9ef9025..675a511f0febf13cc5e00b547c18a050ac534f2e 100644 +--- a/daemons/ipa-sam/ipa_sam.c ++++ b/daemons/ipa-sam/ipa_sam.c +@@ -213,6 +213,7 @@ struct ipasam_private { + char *client_princ; + struct sss_idmap_ctx *idmap_ctx; + uint32_t supported_enctypes; ++ bool fips_enabled; + }; + + +@@ -1737,6 +1738,10 @@ static bool search_krb_princ(struct ipasam_private *ipasam_state, + return true; + } + ++/* Please keep ENCTYPE_ARCFOUR_HMAC the last in the list ++ * of the default encryption types so that we can exclude ++ * it when running in a FIPS mode where it is not allowed ++ */ + #define DEF_ENCTYPE_NUM 3 + long default_enctypes[DEF_ENCTYPE_NUM] = { + ENCTYPE_AES256_CTS_HMAC_SHA1_96, +@@ -1754,9 +1759,14 @@ static int set_cross_realm_pw(struct ipasam_private *ipasam_state, + struct berval reqdata = { 0 }; + struct berval *retdata = NULL; + char *retoid; ++ int enctypes_num = DEF_ENCTYPE_NUM; + ++ if (ipasam_state->fips_enabled) { ++ DEBUG(1, ("FIPS mode enabled: TDO account credentials will not have RC4-HMAC!\n")); ++ enctypes_num = DEF_ENCTYPE_NUM - 1; ++ } + ret = ipaasn1_enc_getkt(true, princ, pwd, +- default_enctypes, DEF_ENCTYPE_NUM, ++ default_enctypes, enctypes_num, + &buffer, &buflen); + if (!ret) goto done; + +@@ -3935,7 +3945,9 @@ static NTSTATUS ipasam_get_enctypes(struct ipasam_private *ipasam_state, + *enctypes |= KERB_ENCTYPE_DES_CBC_MD5; + break; + case ENCTYPE_ARCFOUR_HMAC: +- *enctypes |= KERB_ENCTYPE_RC4_HMAC_MD5; ++ if (!ipasam_state->fips_enabled) { ++ *enctypes |= KERB_ENCTYPE_RC4_HMAC_MD5; ++ } + break; + case ENCTYPE_AES128_CTS_HMAC_SHA1_96: + *enctypes |= KERB_ENCTYPE_AES128_CTS_HMAC_SHA1_96; +@@ -4563,6 +4575,7 @@ static NTSTATUS pdb_init_ipasam(struct pdb_methods **pdb_method, + return NT_STATUS_INVALID_PARAMETER; + } + ++ ipasam_state->fips_enabled = ipapwd_fips_enabled(); + ipasam_state->trust_dn = talloc_asprintf(ipasam_state, + "cn=ad,cn=trusts,%s", + ipasam_state->base_dn); +@@ -4684,9 +4697,11 @@ static NTSTATUS pdb_init_ipasam(struct pdb_methods **pdb_method, + &enctypes); + + if (!NT_STATUS_IS_OK(status)) { +- enctypes = KERB_ENCTYPE_RC4_HMAC_MD5 | +- KERB_ENCTYPE_AES128_CTS_HMAC_SHA1_96 | ++ enctypes = KERB_ENCTYPE_AES128_CTS_HMAC_SHA1_96 | + KERB_ENCTYPE_AES256_CTS_HMAC_SHA1_96; ++ if (!ipasam_state->fips_enabled) { ++ enctypes |= KERB_ENCTYPE_RC4_HMAC_MD5; ++ } + } + + ipasam_state->supported_enctypes = enctypes; +-- +2.17.1 + diff --git a/SOURCES/0025-Add-tests-for-installutils.set_directive.patch b/SOURCES/0025-Add-tests-for-installutils.set_directive.patch deleted file mode 100644 index da5f49a..0000000 --- a/SOURCES/0025-Add-tests-for-installutils.set_directive.patch +++ /dev/null @@ -1,79 +0,0 @@ -From 8ffdc78b211c025c19636a5f5dcd12d12191aee4 Mon Sep 17 00:00:00 2001 -From: Fraser Tweedale -Date: Tue, 5 Dec 2017 15:00:18 +1100 -Subject: [PATCH] Add tests for installutils.set_directive - -Part of: https://pagure.io/freeipa/issue/7288 - -Reviewed-By: Florence Blanc-Renaud ---- - .../test_install/test_installutils.py | 57 ++++++++++++++++++++++ - 1 file changed, 57 insertions(+) - create mode 100644 ipatests/test_ipaserver/test_install/test_installutils.py - -diff --git a/ipatests/test_ipaserver/test_install/test_installutils.py b/ipatests/test_ipaserver/test_install/test_installutils.py -new file mode 100644 -index 0000000000000000000000000000000000000000..cc8fd3cf3f82c2d9af48287f506a566ffbfc39f6 ---- /dev/null -+++ b/ipatests/test_ipaserver/test_install/test_installutils.py -@@ -0,0 +1,57 @@ -+# -+# Copyright (C) 2017 FreeIPA Contributors. See COPYING for license -+# -+ -+import os -+import tempfile -+ -+from ipaserver.install import installutils -+ -+EXAMPLE_CONFIG = [ -+ 'foo=1\n', -+ 'foobar=2\n', -+] -+ -+ -+class test_set_directive_lines(object): -+ def test_remove_directive(self): -+ lines = installutils.set_directive_lines( -+ False, '=', 'foo', None, EXAMPLE_CONFIG) -+ assert list(lines) == ['foobar=2\n'] -+ -+ def test_add_directive(self): -+ lines = installutils.set_directive_lines( -+ False, '=', 'baz', '4', EXAMPLE_CONFIG) -+ assert list(lines) == ['foo=1\n', 'foobar=2\n', 'baz=4\n'] -+ -+ def test_set_directive_does_not_clobber_suffix_key(self): -+ lines = installutils.set_directive_lines( -+ False, '=', 'foo', '3', EXAMPLE_CONFIG) -+ assert list(lines) == ['foo=3\n', 'foobar=2\n'] -+ -+ -+class test_set_directive(object): -+ def test_set_directive(self): -+ """Check that set_directive writes the new data and preserves mode.""" -+ fd, filename = tempfile.mkstemp() -+ try: -+ os.close(fd) -+ stat_pre = os.stat(filename) -+ -+ with open(filename, 'w') as f: -+ for line in EXAMPLE_CONFIG: -+ f.write(line) -+ -+ installutils.set_directive(filename, 'foo', '3', False, '=') -+ -+ stat_post = os.stat(filename) -+ with open(filename, 'r') as f: -+ lines = list(f) -+ -+ assert lines == ['foo=3\n', 'foobar=2\n'] -+ assert stat_pre.st_mode == stat_post.st_mode -+ assert stat_pre.st_uid == stat_post.st_uid -+ assert stat_pre.st_gid == stat_post.st_gid -+ -+ finally: -+ os.remove(filename) --- -2.13.6 - diff --git a/SOURCES/0025-Re-open-the-ldif-file-to-prevent-error-message.patch b/SOURCES/0025-Re-open-the-ldif-file-to-prevent-error-message.patch new file mode 100644 index 0000000..964792c --- /dev/null +++ b/SOURCES/0025-Re-open-the-ldif-file-to-prevent-error-message.patch @@ -0,0 +1,56 @@ +From fd54380d684494e13d8e797b17412a6713937b95 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Tibor=20Dudl=C3=A1k?= +Date: Thu, 2 Aug 2018 18:05:51 +0200 +Subject: [PATCH] Re-open the ldif file to prevent error message + +There was an issue with ipa-server-upgrade and it was +showing an error while upgrading: +DN... does not exists or haven't been updated, caused +by not moving pointer to file begining when re-reading. + +Resolves: https://pagure.io/freeipa/issue/7644 +Reviewed-By: Florence Blanc-Renaud +Reviewed-By: Christian Heimes +Reviewed-By: Rob Crittenden +--- + ipaserver/install/upgradeinstance.py | 21 +++++++++++++-------- + 1 file changed, 13 insertions(+), 8 deletions(-) + +diff --git a/ipaserver/install/upgradeinstance.py b/ipaserver/install/upgradeinstance.py +index 19a06a0a84536cb548822e47038142e97d8bee39..cb83d7bb1817d1fd9a36f2e97a5cc80bf806cabd 100644 +--- a/ipaserver/install/upgradeinstance.py ++++ b/ipaserver/install/upgradeinstance.py +@@ -237,17 +237,22 @@ class IPAUpgrade(service.Service): + + def __disable_schema_compat(self): + ldif_outfile = "%s.modified.out" % self.filename ++ ++ with open(self.filename, "r") as in_file: ++ parser = GetEntryFromLDIF(in_file, entries_dn=[COMPAT_DN]) ++ parser.parse() ++ ++ try: ++ compat_entry = parser.get_results()[COMPAT_DN] ++ except KeyError: ++ return ++ ++ if not compat_entry.get('nsslapd-pluginEnabled'): ++ return ++ + with open(ldif_outfile, "w") as out_file: + with open(self.filename, "r") as in_file: +- parser = GetEntryFromLDIF(in_file, entries_dn=[COMPAT_DN]) +- parser.parse() +- try: +- compat_entry = parser.get_results()[COMPAT_DN] +- except KeyError: +- return + parser = installutils.ModifyLDIF(in_file, out_file) +- if not compat_entry.get('nsslapd-pluginEnabled'): +- return + parser.remove_value(COMPAT_DN, "nsslapd-pluginEnabled") + parser.remove_value(COMPAT_DN, "nsslapd-pluginenabled") + parser.add_value(COMPAT_DN, "nsslapd-pluginEnabled", +-- +2.17.1 + diff --git a/SOURCES/0026-Add-safe-DirectiveSetter-context-manager.patch b/SOURCES/0026-Add-safe-DirectiveSetter-context-manager.patch deleted file mode 100644 index c142e7a..0000000 --- a/SOURCES/0026-Add-safe-DirectiveSetter-context-manager.patch +++ /dev/null @@ -1,365 +0,0 @@ -From dda1087a2e6e0f5fcb8623983f5296b8a426cfc5 Mon Sep 17 00:00:00 2001 -From: Christian Heimes -Date: Fri, 8 Dec 2017 12:38:41 +0100 -Subject: [PATCH] Add safe DirectiveSetter context manager - -installutils.set_directive() is both inefficient and potentially -dangerous. It does not ensure that the whole file is written and -properly synced to disk. In worst case it could lead to partially -written or destroyed config files. - -The new DirectiveSetter context manager wraps everything under an easy -to use interface. - -https://pagure.io/freeipa/issue/7312 - -Signed-off-by: Christian Heimes -Reviewed-By: Florence Blanc-Renaud ---- - ipaserver/install/cainstance.py | 109 ++++++++++----------- - ipaserver/install/installutils.py | 87 +++++++++++++++- - .../test_install/test_installutils.py | 67 +++++++++++++ - 3 files changed, 205 insertions(+), 58 deletions(-) - -diff --git a/ipaserver/install/cainstance.py b/ipaserver/install/cainstance.py -index dd410d1c97cb4b27d35086bb2f511c42c02d022f..20635eae22268ff72de73b8b9c430050114bb45b 100644 ---- a/ipaserver/install/cainstance.py -+++ b/ipaserver/install/cainstance.py -@@ -908,62 +908,61 @@ class CAInstance(DogtagInstance): - https://access.redhat.com/knowledge/docs/en-US/Red_Hat_Certificate_System/8.0/html/Admin_Guide/Setting_up_Publishing.html - """ - -- def put(k, v): -- installutils.set_directive( -- paths.CA_CS_CFG_PATH, k, v, quotes=False, separator='=') -+ with installutils.DirectiveSetter(paths.CA_CS_CFG_PATH, -+ quotes=False, separator='=') as ds: - -- # Enable file publishing, disable LDAP -- put('ca.publish.enable', 'true') -- put('ca.publish.ldappublish.enable', 'false') -- -- # Create the file publisher, der only, not b64 -- put('ca.publish.publisher.impl.FileBasedPublisher.class', -- 'com.netscape.cms.publish.publishers.FileBasedPublisher') -- put('ca.publish.publisher.instance.FileBaseCRLPublisher.crlLinkExt', -- 'bin') -- put('ca.publish.publisher.instance.FileBaseCRLPublisher.directory', -- self.prepare_crl_publish_dir()) -- put('ca.publish.publisher.instance.FileBaseCRLPublisher.latestCrlLink', -- 'true') -- put('ca.publish.publisher.instance.FileBaseCRLPublisher.pluginName', -- 'FileBasedPublisher') -- put('ca.publish.publisher.instance.FileBaseCRLPublisher.timeStamp', -- 'LocalTime') -- put('ca.publish.publisher.instance.FileBaseCRLPublisher.zipCRLs', -- 'false') -- put('ca.publish.publisher.instance.FileBaseCRLPublisher.zipLevel', '9') -- put('ca.publish.publisher.instance.FileBaseCRLPublisher.Filename.b64', -- 'false') -- put('ca.publish.publisher.instance.FileBaseCRLPublisher.Filename.der', -- 'true') -- -- # The publishing rule -- put('ca.publish.rule.instance.FileCrlRule.enable', 'true') -- put('ca.publish.rule.instance.FileCrlRule.mapper', 'NoMap') -- put('ca.publish.rule.instance.FileCrlRule.pluginName', 'Rule') -- put('ca.publish.rule.instance.FileCrlRule.predicate', '') -- put('ca.publish.rule.instance.FileCrlRule.publisher', -- 'FileBaseCRLPublisher') -- put('ca.publish.rule.instance.FileCrlRule.type', 'crl') -- -- # Now disable LDAP publishing -- put('ca.publish.rule.instance.LdapCaCertRule.enable', 'false') -- put('ca.publish.rule.instance.LdapCrlRule.enable', 'false') -- put('ca.publish.rule.instance.LdapUserCertRule.enable', 'false') -- put('ca.publish.rule.instance.LdapXCertRule.enable', 'false') -- -- # If we are the initial master then we are the CRL generator, otherwise -- # we point to that master for CRLs. -- if not self.clone: -- # These next two are defaults, but I want to be explicit that the -- # initial master is the CRL generator. -- put('ca.crl.MasterCRL.enableCRLCache', 'true') -- put('ca.crl.MasterCRL.enableCRLUpdates', 'true') -- put('ca.listenToCloneModifications', 'true') -- else: -- put('ca.crl.MasterCRL.enableCRLCache', 'false') -- put('ca.crl.MasterCRL.enableCRLUpdates', 'false') -- put('ca.listenToCloneModifications', 'false') -+ # Enable file publishing, disable LDAP -+ ds.set('ca.publish.enable', 'true') -+ ds.set('ca.publish.ldappublish.enable', 'false') -+ -+ # Create the file publisher, der only, not b64 -+ ds.set( -+ 'ca.publish.publisher.impl.FileBasedPublisher.class', -+ 'com.netscape.cms.publish.publishers.FileBasedPublisher' -+ ) -+ prefix = 'ca.publish.publisher.instance.FileBaseCRLPublisher.' -+ ds.set(prefix + 'crlLinkExt', 'bin') -+ ds.set(prefix + 'directory', self.prepare_crl_publish_dir()) -+ ds.set(prefix + 'latestCrlLink', 'true') -+ ds.set(prefix + 'pluginName', 'FileBasedPublisher') -+ ds.set(prefix + 'timeStamp', 'LocalTime') -+ ds.set(prefix + 'zipCRLs', 'false') -+ ds.set(prefix + 'zipLevel', '9') -+ ds.set(prefix + 'Filename.b64', 'false') -+ ds.set(prefix + 'Filename.der', 'true') -+ -+ # The publishing rule -+ ds.set('ca.publish.rule.instance.FileCrlRule.enable', 'true') -+ ds.set('ca.publish.rule.instance.FileCrlRule.mapper', 'NoMap') -+ ds.set('ca.publish.rule.instance.FileCrlRule.pluginName', 'Rule') -+ ds.set('ca.publish.rule.instance.FileCrlRule.predicate', '') -+ ds.set( -+ 'ca.publish.rule.instance.FileCrlRule.publisher', -+ 'FileBaseCRLPublisher' -+ ) -+ ds.set('ca.publish.rule.instance.FileCrlRule.type', 'crl') -+ -+ # Now disable LDAP publishing -+ ds.set('ca.publish.rule.instance.LdapCaCertRule.enable', 'false') -+ ds.set('ca.publish.rule.instance.LdapCrlRule.enable', 'false') -+ ds.set( -+ 'ca.publish.rule.instance.LdapUserCertRule.enable', -+ 'false' -+ ) -+ ds.set('ca.publish.rule.instance.LdapXCertRule.enable', 'false') -+ -+ # If we are the initial master then we are the CRL generator, -+ # otherwise we point to that master for CRLs. -+ if not self.clone: -+ # These next two are defaults, but I want to be explicit -+ # that the initial master is the CRL generator. -+ ds.set('ca.crl.MasterCRL.enableCRLCache', 'true') -+ ds.set('ca.crl.MasterCRL.enableCRLUpdates', 'true') -+ ds.set('ca.listenToCloneModifications', 'true') -+ else: -+ ds.set('ca.crl.MasterCRL.enableCRLCache', 'false') -+ ds.set('ca.crl.MasterCRL.enableCRLUpdates', 'false') -+ ds.set('ca.listenToCloneModifications', 'false') - - def uninstall(self): - # just eat state -diff --git a/ipaserver/install/installutils.py b/ipaserver/install/installutils.py -index b56cf591496c679e5fcf3e94f458c286216eb1e4..08e098f8656c8fe61a87fb046c01e1487c25da7f 100644 ---- a/ipaserver/install/installutils.py -+++ b/ipaserver/install/installutils.py -@@ -24,6 +24,7 @@ import errno - import socket - import getpass - import gssapi -+import io - import ldif - import os - import re -@@ -31,6 +32,7 @@ import fileinput - import sys - import tempfile - import shutil -+import stat # pylint: disable=bad-python3-import - import traceback - import textwrap - from contextlib import contextmanager -@@ -428,6 +430,82 @@ def unquote_directive_value(value, quote_char): - return unescaped_value - - -+_SENTINEL = object() -+ -+ -+class DirectiveSetter(object): -+ """Safe directive setter -+ -+ with DirectiveSetter('/path/to/conf') as ds: -+ ds.set(key, value) -+ """ -+ def __init__(self, filename, quotes=True, separator=' '): -+ self.filename = os.path.abspath(filename) -+ self.quotes = quotes -+ self.separator = separator -+ self.lines = None -+ self.stat = None -+ -+ def __enter__(self): -+ with io.open(self.filename) as f: -+ self.stat = os.fstat(f.fileno()) -+ self.lines = list(f) -+ return self -+ -+ def __exit__(self, exc_type, exc_val, exc_tb): -+ if exc_type is not None: -+ # something went wrong, reset -+ self.lines = None -+ self.stat = None -+ return -+ -+ directory, prefix = os.path.split(self.filename) -+ # use tempfile in same directory to have atomic rename -+ fd, name = tempfile.mkstemp(prefix=prefix, dir=directory, text=True) -+ with io.open(fd, mode='w', closefd=True) as f: -+ for line in self.lines: -+ if not isinstance(line, six.text_type): -+ line = line.decode('utf-8') -+ f.write(line) -+ self.lines = None -+ os.fchmod(f.fileno(), stat.S_IMODE(self.stat.st_mode)) -+ os.fchown(f.fileno(), self.stat.st_uid, self.stat.st_gid) -+ self.stat = None -+ # flush and sync tempfile inode -+ f.flush() -+ os.fsync(f.fileno()) -+ -+ # rename file and sync directory inode -+ os.rename(name, self.filename) -+ dirfd = os.open(directory, os.O_RDONLY | os.O_DIRECTORY) -+ try: -+ os.fsync(dirfd) -+ finally: -+ os.close(dirfd) -+ -+ def set(self, directive, value, quotes=_SENTINEL, separator=_SENTINEL): -+ """Set a single directive -+ """ -+ if quotes is _SENTINEL: -+ quotes = self.quotes -+ if separator is _SENTINEL: -+ separator = self.separator -+ # materialize lines -+ # set_directive_lines() modify item, shrink or enlage line count -+ self.lines = list(set_directive_lines( -+ quotes, separator, directive, value, self.lines -+ )) -+ -+ def setitems(self, items): -+ """Set multiple directives from a dict or list with key/value pairs -+ """ -+ if isinstance(items, dict): -+ # dict-like, use sorted for stable order -+ items = sorted(items.items()) -+ for k, v in items: -+ self.set(k, v) -+ -+ - def set_directive(filename, directive, value, quotes=True, separator=' '): - """Set a name/value pair directive in a configuration file. - -@@ -447,8 +525,10 @@ def set_directive(filename, directive, value, quotes=True, separator=' '): - st = os.stat(filename) - with open(filename, 'r') as f: - lines = list(f) # read the whole file -- new_lines = set_directive_lines( -- quotes, separator, directive, value, lines) -+ # materialize new list -+ new_lines = list(set_directive_lines( -+ quotes, separator, directive, value, lines -+ )) - with open(filename, 'w') as f: - # don't construct the whole string; write line-wise - for line in new_lines: -@@ -472,8 +552,9 @@ def set_directive_lines(quotes, separator, k, v, lines): - new_line = ''.join([k, separator, v_quoted, '\n']) - - found = False -+ matcher = re.compile(r'\s*{}'.format(re.escape(k + separator))) - for line in lines: -- if re.match(r'\s*{}'.format(re.escape(k + separator)), line): -+ if matcher.match(line): - found = True - if v is not None: - yield new_line -diff --git a/ipatests/test_ipaserver/test_install/test_installutils.py b/ipatests/test_ipaserver/test_install/test_installutils.py -index cc8fd3cf3f82c2d9af48287f506a566ffbfc39f6..3c992c9ab06ddc3af557329de81debe704b0fb16 100644 ---- a/ipatests/test_ipaserver/test_install/test_installutils.py -+++ b/ipatests/test_ipaserver/test_install/test_installutils.py -@@ -3,8 +3,11 @@ - # - - import os -+import shutil - import tempfile - -+import pytest -+ - from ipaserver.install import installutils - - EXAMPLE_CONFIG = [ -@@ -13,6 +16,17 @@ EXAMPLE_CONFIG = [ - ] - - -+@pytest.fixture -+def tempdir(request): -+ tempdir = tempfile.mkdtemp() -+ -+ def fin(): -+ shutil.rmtree(tempdir) -+ -+ request.addfinalizer(fin) -+ return tempdir -+ -+ - class test_set_directive_lines(object): - def test_remove_directive(self): - lines = installutils.set_directive_lines( -@@ -55,3 +69,56 @@ class test_set_directive(object): - - finally: - os.remove(filename) -+ -+ -+def test_directivesetter(tempdir): -+ filename = os.path.join(tempdir, 'example.conf') -+ with open(filename, 'w') as f: -+ for line in EXAMPLE_CONFIG: -+ f.write(line) -+ -+ ds = installutils.DirectiveSetter(filename) -+ assert ds.lines is None -+ with ds: -+ assert ds.lines == EXAMPLE_CONFIG -+ ds.set('foo', '3') # quoted, space separated, doesn't change 'foo=' -+ ds.set('foobar', None, separator='=') # remove -+ ds.set('baz', '4', False, '=') # add -+ ds.setitems([ -+ ('list1', 'value1'), -+ ('list2', 'value2'), -+ ]) -+ ds.setitems({ -+ 'dict1': 'value1', -+ 'dict2': 'value2', -+ }) -+ -+ with open(filename, 'r') as f: -+ lines = list(f) -+ -+ assert lines == [ -+ 'foo=1\n', -+ 'foo "3"\n', -+ 'baz=4\n', -+ 'list1 "value1"\n', -+ 'list2 "value2"\n', -+ 'dict1 "value1"\n', -+ 'dict2 "value2"\n', -+ ] -+ -+ with installutils.DirectiveSetter(filename, True, '=') as ds: -+ ds.set('foo', '4') # doesn't change 'foo ' -+ -+ with open(filename, 'r') as f: -+ lines = list(f) -+ -+ assert lines == [ -+ 'foo="4"\n', -+ 'foo "3"\n', -+ 'baz=4\n', -+ 'list1 "value1"\n', -+ 'list2 "value2"\n', -+ 'dict1 "value1"\n', -+ 'dict2 "value2"\n', -+ -+ ] --- -2.13.6 - diff --git a/SOURCES/0026-Catch-ACIError-instead-of-invalid-credentials.patch b/SOURCES/0026-Catch-ACIError-instead-of-invalid-credentials.patch new file mode 100644 index 0000000..9521355 --- /dev/null +++ b/SOURCES/0026-Catch-ACIError-instead-of-invalid-credentials.patch @@ -0,0 +1,36 @@ +From a24178a743e7a90ca80702207345a398bf8074ad Mon Sep 17 00:00:00 2001 +From: Christian Heimes +Date: Fri, 29 Jun 2018 11:08:45 +0200 +Subject: [PATCH] Catch ACIError instead of invalid credentials + +ipaldap's LDAPClient client turns INVALID_CREDENTIAL error into +ACIError. Catch the ACIError and wait until the user has been +replicated. + +Apparently no manual or automated test ran into the timeout during +testing. + +Fixes: Fixes: https://pagure.io/freeipa/issue/7593 +Signed-off-by: Christian Heimes +Reviewed-By: Alexander Bokovoy +--- + ipaserver/install/dogtaginstance.py | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/ipaserver/install/dogtaginstance.py b/ipaserver/install/dogtaginstance.py +index 5b2c30f8a1b7e932ce1cca3ca38f5962a3d54266..437029315cb6774ce9057baafda27cdb68454b49 100644 +--- a/ipaserver/install/dogtaginstance.py ++++ b/ipaserver/install/dogtaginstance.py +@@ -471,7 +471,8 @@ class DogtagInstance(service.Service): + time.sleep(1) + try: + master_conn.simple_bind(self.admin_dn, self.admin_password) +- except ldap.INVALID_CREDENTIALS: ++ except errors.ACIError: ++ # user not replicated yet + pass + else: + logger.debug("Successfully logged in as %s", self.admin_dn) +-- +2.17.1 + diff --git a/SOURCES/0027-Auto-retry-failed-certmonger-requests.patch b/SOURCES/0027-Auto-retry-failed-certmonger-requests.patch new file mode 100644 index 0000000..54e1655 --- /dev/null +++ b/SOURCES/0027-Auto-retry-failed-certmonger-requests.patch @@ -0,0 +1,195 @@ +From 3e8427f3aa392f961923f5c9d509bab64ce3c9ab Mon Sep 17 00:00:00 2001 +From: Christian Heimes +Date: Sun, 8 Jul 2018 11:53:58 +0200 +Subject: [PATCH] Auto-retry failed certmonger requests + +During parallel replica installation, a request sometimes fails with +CA_REJECTED or CA_UNREACHABLE. The error occur when the master is +either busy or some information haven't been replicated yet. Even +a stuck request can be recovered, e.g. when permission and group +information have been replicated. + +A new function request_and_retry_cert() automatically resubmits failing +requests until it times out. + +Fixes: https://pagure.io/freeipa/issue/7623 +Signed-off-by: Christian Heimes +Reviewed-By: Stanislav Laznicka +--- + ipalib/install/certmonger.py | 64 ++++++++++++++++++++++++------- + ipaserver/install/cainstance.py | 4 +- + ipaserver/install/certs.py | 19 ++++++--- + ipaserver/install/dsinstance.py | 4 +- + ipaserver/install/httpinstance.py | 5 ++- + ipaserver/install/krbinstance.py | 4 +- + 6 files changed, 76 insertions(+), 24 deletions(-) + +diff --git a/ipalib/install/certmonger.py b/ipalib/install/certmonger.py +index c07242412affa29eca3312fd27985f65869d3f7a..3e1862192eb0d9245797ebbe8abf2ff69d7e7767 100644 +--- a/ipalib/install/certmonger.py ++++ b/ipalib/install/certmonger.py +@@ -305,20 +305,56 @@ def add_subject(request_id, subject): + def request_and_wait_for_cert( + certpath, subject, principal, nickname=None, passwd_fname=None, + dns=None, ca='IPA', profile=None, +- pre_command=None, post_command=None, storage='NSSDB', perms=None): +- """ +- Execute certmonger to request a server certificate. +- +- The method also waits for the certificate to be available. +- """ +- reqId = request_cert(certpath, subject, principal, nickname, +- passwd_fname, dns, ca, profile, +- pre_command, post_command, storage, perms) +- state = wait_for_request(reqId, api.env.startup_timeout) +- ca_error = get_request_value(reqId, 'ca-error') +- if state != 'MONITORING' or ca_error: +- raise RuntimeError("Certificate issuance failed ({})".format(state)) +- return reqId ++ pre_command=None, post_command=None, storage='NSSDB', perms=None, ++ resubmit_timeout=0): ++ """Request certificate, wait and possibly resubmit failing requests ++ ++ Submit a cert request to certmonger and wait until the request has ++ finished. ++ ++ With timeout, a failed request is resubmitted. During parallel replica ++ installation, a request sometimes fails with CA_REJECTED or ++ CA_UNREACHABLE. The error occurs when the master is either busy or some ++ information haven't been replicated yet. Even a stuck request can be ++ recovered, e.g. when permission and group information have been ++ replicated. ++ """ ++ req_id = request_cert( ++ certpath, subject, principal, nickname, passwd_fname, dns, ca, ++ profile, pre_command, post_command, storage, perms ++ ) ++ ++ deadline = time.time() + resubmit_timeout ++ while True: # until success, timeout, or error ++ state = wait_for_request(req_id, api.env.replication_wait_timeout) ++ ca_error = get_request_value(req_id, 'ca-error') ++ if state == 'MONITORING' and ca_error is None: ++ # we got a winner, exiting ++ logger.debug("Cert request %s was successful", req_id) ++ return req_id ++ ++ logger.debug( ++ "Cert request %s failed: %s (%s)", req_id, state, ca_error ++ ) ++ if state not in {'CA_REJECTED', 'CA_UNREACHABLE'}: ++ # probably unrecoverable error ++ logger.debug("Giving up on cert request %s", req_id) ++ break ++ elif not resubmit_timeout: ++ # no resubmit ++ break ++ elif time.time() > deadline: ++ logger.debug("Request %s reached resubmit dead line", req_id) ++ break ++ else: ++ # sleep and resubmit ++ logger.debug("Sleep and resubmit cert request %s", req_id) ++ time.sleep(10) ++ resubmit_request(req_id) ++ ++ raise RuntimeError( ++ "Certificate issuance failed ({}: {})".format(state, ca_error) ++ ) + + + def request_cert( +diff --git a/ipaserver/install/cainstance.py b/ipaserver/install/cainstance.py +index 8193f3da854b3a20d175de523fbc453f5c5104d8..6dbf69b3e5833f220a4d7d640b66a8fcf824f445 100644 +--- a/ipaserver/install/cainstance.py ++++ b/ipaserver/install/cainstance.py +@@ -917,7 +917,9 @@ class CAInstance(DogtagInstance): + profile='caServerCert', + pre_command='renew_ra_cert_pre', + post_command='renew_ra_cert', +- storage="FILE") ++ storage="FILE", ++ resubmit_timeout=api.env.replication_wait_timeout ++ ) + self.__set_ra_cert_perms() + + self.requestId = str(reqId) +diff --git a/ipaserver/install/certs.py b/ipaserver/install/certs.py +index 3f843399f4f964223f52242d610e842a5dc473e8..30b2aa0d3e7b2cafbcc17ad3d04764a342ae8002 100644 +--- a/ipaserver/install/certs.py ++++ b/ipaserver/install/certs.py +@@ -600,12 +600,19 @@ class CertDB(object): + def export_pem_cert(self, nickname, location): + return self.nssdb.export_pem_cert(nickname, location) + +- def request_service_cert(self, nickname, principal, host): +- certmonger.request_and_wait_for_cert(certpath=self.secdir, +- nickname=nickname, +- principal=principal, +- subject=host, +- passwd_fname=self.passwd_fname) ++ def request_service_cert(self, nickname, principal, host, ++ resubmit_timeout=None): ++ if resubmit_timeout is None: ++ resubmit_timeout = api.env.replication_wait_timeout ++ return certmonger.request_and_wait_for_cert( ++ certpath=self.secdir, ++ storage='NSSDB', ++ nickname=nickname, ++ principal=principal, ++ subject=host, ++ passwd_fname=self.passwd_fname, ++ resubmit_timeout=resubmit_timeout ++ ) + + def is_ipa_issued_cert(self, api, nickname): + """ +diff --git a/ipaserver/install/dsinstance.py b/ipaserver/install/dsinstance.py +index eefbde3356e1077d490d09c4ea47d961ce3ce8e6..ac95f8c746477da375de518526dea0d02d51d984 100644 +--- a/ipaserver/install/dsinstance.py ++++ b/ipaserver/install/dsinstance.py +@@ -851,7 +851,9 @@ class DsInstance(service.Service): + ca='IPA', + profile=dogtag.DEFAULT_PROFILE, + dns=[self.fqdn], +- post_command=cmd) ++ post_command=cmd, ++ resubmit_timeout=api.env.replication_wait_timeout ++ ) + finally: + if prev_helper is not None: + certmonger.modify_ca_helper('IPA', prev_helper) +diff --git a/ipaserver/install/httpinstance.py b/ipaserver/install/httpinstance.py +index 0b7023c2f1b0feb996e0dd0adbefbd49c51da757..3f83248dd89118aeecfbf458c5079dde8b2cb93d 100644 +--- a/ipaserver/install/httpinstance.py ++++ b/ipaserver/install/httpinstance.py +@@ -447,7 +447,10 @@ class HTTPInstance(service.Service): + ca='IPA', + profile=dogtag.DEFAULT_PROFILE, + dns=[self.fqdn], +- post_command='restart_httpd') ++ post_command='restart_httpd', ++ storage='NSSDB', ++ resubmit_timeout=api.env.replication_wait_timeout ++ ) + finally: + if prev_helper is not None: + certmonger.modify_ca_helper('IPA', prev_helper) +diff --git a/ipaserver/install/krbinstance.py b/ipaserver/install/krbinstance.py +index 33d66fb94b0a1f7571b22120e5159a0e0ad2e675..09cafb7b84623594fe88083f5b914cee0f050409 100644 +--- a/ipaserver/install/krbinstance.py ++++ b/ipaserver/install/krbinstance.py +@@ -445,7 +445,9 @@ class KrbInstance(service.Service): + storage='FILE', + profile=KDC_PROFILE, + post_command='renew_kdc_cert', +- perms=(0o644, 0o600)) ++ perms=(0o644, 0o600), ++ resubmit_timeout=api.env.replication_wait_timeout ++ ) + except dbus.DBusException as e: + # if the certificate is already tracked, ignore the error + name = e.get_dbus_name() +-- +2.17.1 + diff --git a/SOURCES/0027-Old-pylint-doesn-t-support-bad-python3-option.patch b/SOURCES/0027-Old-pylint-doesn-t-support-bad-python3-option.patch deleted file mode 100644 index 820061b..0000000 --- a/SOURCES/0027-Old-pylint-doesn-t-support-bad-python3-option.patch +++ /dev/null @@ -1,27 +0,0 @@ -From cf61603340f05a78aff51bf7d2b17de559900636 Mon Sep 17 00:00:00 2001 -From: Christian Heimes -Date: Tue, 12 Dec 2017 15:03:01 +0100 -Subject: [PATCH] Old pylint doesn't support bad python3 option - -Signed-off-by: Christian Heimes -Reviewed-By: Florence Blanc-Renaud ---- - ipaserver/install/installutils.py | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/ipaserver/install/installutils.py b/ipaserver/install/installutils.py -index 08e098f8656c8fe61a87fb046c01e1487c25da7f..40ae9e19f42170a0552a07c9ec73f60b34bcb84d 100644 ---- a/ipaserver/install/installutils.py -+++ b/ipaserver/install/installutils.py -@@ -32,7 +32,7 @@ import fileinput - import sys - import tempfile - import shutil --import stat # pylint: disable=bad-python3-import -+import stat - import traceback - import textwrap - from contextlib import contextmanager --- -2.13.6 - diff --git a/SOURCES/0028-Wait-for-client-certificates.patch b/SOURCES/0028-Wait-for-client-certificates.patch new file mode 100644 index 0000000..e2f204a --- /dev/null +++ b/SOURCES/0028-Wait-for-client-certificates.patch @@ -0,0 +1,66 @@ +From fb346fab2495a9343ed68131c0ebf071e3e9654f Mon Sep 17 00:00:00 2001 +From: Christian Heimes +Date: Mon, 9 Jul 2018 13:53:44 +0200 +Subject: [PATCH] Wait for client certificates + +ipa-client-install --request-cert now waits until certmonger has +provided a host certificate. In case of an error, ipa-client-install no +longer pretents to success but fails with an error code. + +The --request-cert option also ensures that certmonger is enabled and +running. + +See: Fixes: https://pagure.io/freeipa/issue/7623 +Signed-off-by: Christian Heimes +Reviewed-By: Stanislav Laznicka +--- + ipaclient/install/client.py | 25 ++++++++++++++++++------- + 1 file changed, 18 insertions(+), 7 deletions(-) + +diff --git a/ipaclient/install/client.py b/ipaclient/install/client.py +index 5173d90bfeb61acab6032f2972dcc4a0d1344094..0fbe31b762561b3e2ee2f35a666a93de8857bced 100644 +--- a/ipaclient/install/client.py ++++ b/ipaclient/install/client.py +@@ -775,6 +775,7 @@ def configure_certmonger( + cmonger = services.knownservices.certmonger + try: + cmonger.enable() ++ cmonger.start() + except Exception as e: + logger.error( + "Failed to configure automatic startup of the %s daemon: %s", +@@ -786,14 +787,24 @@ def configure_certmonger( + subject = str(DN(('CN', hostname), subject_base)) + passwd_fname = os.path.join(paths.IPA_NSSDB_DIR, 'pwdfile.txt') + try: +- certmonger.request_cert( ++ certmonger.request_and_wait_for_cert( + certpath=paths.IPA_NSSDB_DIR, +- nickname='Local IPA host', subject=subject, dns=[hostname], +- principal=principal, passwd_fname=passwd_fname) +- except Exception as ex: +- logger.error( +- "%s request for host certificate failed: %s", +- cmonger.service_name, ex) ++ storage='NSSDB', ++ nickname='Local IPA host', ++ subject=subject, ++ dns=[hostname], ++ principal=principal, ++ passwd_fname=passwd_fname, ++ resubmit_timeout=120, ++ ) ++ except Exception as e: ++ logger.exception("certmonger request failed") ++ raise ScriptError( ++ "{} request for host certificate failed: {}".format( ++ cmonger.service_name, e ++ ), ++ rval=CLIENT_INSTALL_ERROR ++ ) + + + def configure_sssd_conf( +-- +2.17.1 + diff --git a/SOURCES/0028-WebUI-make-keytab-tables-on-service-and-host-pages-w.patch b/SOURCES/0028-WebUI-make-keytab-tables-on-service-and-host-pages-w.patch deleted file mode 100644 index 45cf8f9..0000000 --- a/SOURCES/0028-WebUI-make-keytab-tables-on-service-and-host-pages-w.patch +++ /dev/null @@ -1,158 +0,0 @@ -From 8c4561a1ea598f645c33a8ed2f0c841326b2373d Mon Sep 17 00:00:00 2001 -From: Pavel Vomacka -Date: Thu, 14 Dec 2017 15:14:03 +0100 -Subject: [PATCH] WebUI: make keytab tables on service and host pages writable - -There is no object class before adding the first item into tables, -therefore there are no ACI and WebUI is not able to figure out -whether table is writable or not. Adding flag 'w_if_no_aci' -tells "make it writable even if we have not ACIs and try to do -the API call. - -https://pagure.io/freeipa/issue/7111 - -Reviewed-By: Felipe Volpone ---- - install/ui/src/freeipa/host.js | 8 ++++++++ - install/ui/src/freeipa/service.js | 8 ++++++++ - 2 files changed, 16 insertions(+) - -diff --git a/install/ui/src/freeipa/host.js b/install/ui/src/freeipa/host.js -index ac434d8455384ded2cbebc28445deaecbafc46b5..acecff1e5b99c541b216a3e6789efb77eb262fef 100644 ---- a/install/ui/src/freeipa/host.js -+++ b/install/ui/src/freeipa/host.js -@@ -198,6 +198,7 @@ return { - $type: 'association_table', - id: 'host_ipaallowedtoperform_read_keys_user', - name: 'ipaallowedtoperform_read_keys_user', -+ flags: ['w_if_no_aci'], - add_method: 'allow_retrieve_keytab', - remove_method: 'disallow_retrieve_keytab', - add_title: '@i18n:keytab.add_retrive', -@@ -214,6 +215,7 @@ return { - $type: 'association_table', - id: 'host_ipaallowedtoperform_read_keys_group', - name: 'ipaallowedtoperform_read_keys_group', -+ flags: ['w_if_no_aci'], - add_method: 'allow_retrieve_keytab', - remove_method: 'disallow_retrieve_keytab', - add_title: '@i18n:keytab.add_retrive', -@@ -230,6 +232,7 @@ return { - $type: 'association_table', - id: 'service_ipaallowedtoperform_read_keys_host', - name: 'ipaallowedtoperform_read_keys_host', -+ flags: ['w_if_no_aci'], - add_method: 'allow_retrieve_keytab', - remove_method: 'disallow_retrieve_keytab', - add_title: '@i18n:keytab.add_retrive', -@@ -246,6 +249,7 @@ return { - $type: 'association_table', - id: 'service_ipaallowedtoperform_read_keys_hostgroup', - name: 'ipaallowedtoperform_read_keys_hostgroup', -+ flags: ['w_if_no_aci'], - add_method: 'allow_retrieve_keytab', - remove_method: 'disallow_retrieve_keytab', - add_title: '@i18n:keytab.add_retrive', -@@ -269,6 +273,7 @@ return { - $type: 'association_table', - id: 'host_ipaallowedtoperform_write_keys_user', - name: 'ipaallowedtoperform_write_keys_user', -+ flags: ['w_if_no_aci'], - add_method: 'allow_create_keytab', - remove_method: 'disallow_create_keytab', - add_title: '@i18n:keytab.add_create', -@@ -285,6 +290,7 @@ return { - $type: 'association_table', - id: 'host_ipaallowedtoperform_write_keys_group', - name: 'ipaallowedtoperform_write_keys_group', -+ flags: ['w_if_no_aci'], - add_method: 'allow_create_keytab', - remove_method: 'disallow_create_keytab', - add_title: '@i18n:keytab.add_create', -@@ -301,6 +307,7 @@ return { - $type: 'association_table', - id: 'service_ipaallowedtoperform_write_keys_host', - name: 'ipaallowedtoperform_write_keys_host', -+ flags: ['w_if_no_aci'], - add_method: 'allow_create_keytab', - remove_method: 'disallow_create_keytab', - add_title: '@i18n:keytab.add_create', -@@ -317,6 +324,7 @@ return { - $type: 'association_table', - id: 'service_ipaallowedtoperform_write_keys_hostgroup', - name: 'ipaallowedtoperform_write_keys_hostgroup', -+ flags: ['w_if_no_aci'], - add_method: 'allow_create_keytab', - remove_method: 'disallow_create_keytab', - add_title: '@i18n:keytab.add_create', -diff --git a/install/ui/src/freeipa/service.js b/install/ui/src/freeipa/service.js -index 752ff98e3e5290442ce5f011a4de53ccc0db8f8f..c798d2999fc909fdbc26b016e4752a3edf1f702e 100644 ---- a/install/ui/src/freeipa/service.js -+++ b/install/ui/src/freeipa/service.js -@@ -201,6 +201,7 @@ return { - $type: 'association_table', - id: 'service_ipaallowedtoperform_read_keys_user', - name: 'ipaallowedtoperform_read_keys_user', -+ flags: ['w_if_no_aci'], - add_method: 'allow_retrieve_keytab', - remove_method: 'disallow_retrieve_keytab', - add_title: '@i18n:keytab.add_retrive', -@@ -217,6 +218,7 @@ return { - $type: 'association_table', - id: 'service_ipaallowedtoperform_read_keys_group', - name: 'ipaallowedtoperform_read_keys_group', -+ flags: ['w_if_no_aci'], - add_method: 'allow_retrieve_keytab', - remove_method: 'disallow_retrieve_keytab', - add_title: '@i18n:keytab.add_retrive', -@@ -233,6 +235,7 @@ return { - $type: 'association_table', - id: 'service_ipaallowedtoperform_read_keys_host', - name: 'ipaallowedtoperform_read_keys_host', -+ flags: ['w_if_no_aci'], - add_method: 'allow_retrieve_keytab', - remove_method: 'disallow_retrieve_keytab', - add_title: '@i18n:keytab.add_retrive', -@@ -249,6 +252,7 @@ return { - $type: 'association_table', - id: 'service_ipaallowedtoperform_read_keys_hostgroup', - name: 'ipaallowedtoperform_read_keys_hostgroup', -+ flags: ['w_if_no_aci'], - add_method: 'allow_retrieve_keytab', - remove_method: 'disallow_retrieve_keytab', - add_title: '@i18n:keytab.add_retrive', -@@ -272,6 +276,7 @@ return { - $type: 'association_table', - id: 'service_ipaallowedtoperform_write_keys_user', - name: 'ipaallowedtoperform_write_keys_user', -+ flags: ['w_if_no_aci'], - add_method: 'allow_create_keytab', - remove_method: 'disallow_create_keytab', - add_title: '@i18n:keytab.add_create', -@@ -288,6 +293,7 @@ return { - $type: 'association_table', - id: 'service_ipaallowedtoperform_write_keys_group', - name: 'ipaallowedtoperform_write_keys_group', -+ flags: ['w_if_no_aci'], - add_method: 'allow_create_keytab', - remove_method: 'disallow_create_keytab', - add_title: '@i18n:keytab.add_create', -@@ -304,6 +310,7 @@ return { - $type: 'association_table', - id: 'service_ipaallowedtoperform_write_keys_host', - name: 'ipaallowedtoperform_write_keys_host', -+ flags: ['w_if_no_aci'], - add_method: 'allow_create_keytab', - remove_method: 'disallow_create_keytab', - add_title: '@i18n:keytab.add_create', -@@ -320,6 +327,7 @@ return { - $type: 'association_table', - id: 'service_ipaallowedtoperform_write_keys_hostgroup', - name: 'ipaallowedtoperform_write_keys_hostgroup', -+ flags: ['w_if_no_aci'], - add_method: 'allow_create_keytab', - remove_method: 'disallow_create_keytab', - add_title: '@i18n:keytab.add_create', --- -2.13.6 - diff --git a/SOURCES/0029-Fix-KRA-replica-installation-from-CA-master.patch b/SOURCES/0029-Fix-KRA-replica-installation-from-CA-master.patch new file mode 100644 index 0000000..689560f --- /dev/null +++ b/SOURCES/0029-Fix-KRA-replica-installation-from-CA-master.patch @@ -0,0 +1,46 @@ +From 609ccb601843b97b25f2fde3c4981839822af503 Mon Sep 17 00:00:00 2001 +From: Christian Heimes +Date: Tue, 17 Jul 2018 08:53:39 +0200 +Subject: [PATCH] Fix KRA replica installation from CA master + +ipa-replica-install --kra-install can fail when the topology already has +a KRA, but replica is installed from a master with just CA. In that +case, Custodia may pick a machine that doesn't have the KRA auditing and +signing certs in its NSSDB. + +Example: + * master with CA + * replica1 with CA and KRA + * new replica gets installed from master + +The replica installer now always picks a KRA peer. + +The change fixes test scenario TestInstallWithCA1::()::test_replica2_ipa_dns_install + +Fixes: https://pagure.io/freeipa/issue/7518 +See: https://pagure.io/freeipa/issue/7008 +Signed-off-by: Christian Heimes +Reviewed-By: Rob Crittenden +--- + ipaserver/install/server/replicainstall.py | 5 ++++- + 1 file changed, 4 insertions(+), 1 deletion(-) + +diff --git a/ipaserver/install/server/replicainstall.py b/ipaserver/install/server/replicainstall.py +index 8826da232a90380084b0e4f3dca783125a5500da..e78a2b992fbd44b8ee3ccd8183ebd6e13dfd1749 100644 +--- a/ipaserver/install/server/replicainstall.py ++++ b/ipaserver/install/server/replicainstall.py +@@ -1486,7 +1486,10 @@ def install(installer): + otpd.create_instance('OTPD', config.host_name, + ipautil.realm_to_suffix(config.realm_name)) + +- if ca_enabled: ++ if kra_enabled: ++ # A KRA peer always provides a CA, too. ++ mode = custodiainstance.CustodiaModes.KRA_PEER ++ elif ca_enabled: + mode = custodiainstance.CustodiaModes.CA_PEER + else: + mode = custodiainstance.CustodiaModes.MASTER_PEER +-- +2.17.1 + diff --git a/SOURCES/0029-Idviews-fix-objectclass-violation-on-idview-add.patch b/SOURCES/0029-Idviews-fix-objectclass-violation-on-idview-add.patch deleted file mode 100644 index 583ad41..0000000 --- a/SOURCES/0029-Idviews-fix-objectclass-violation-on-idview-add.patch +++ /dev/null @@ -1,108 +0,0 @@ -From 6d4676c4e3403df547ef03a2e716d6254c3c512e Mon Sep 17 00:00:00 2001 -From: Florence Blanc-Renaud -Date: Fri, 5 Jan 2018 09:50:26 +0100 -Subject: [PATCH] Idviews: fix objectclass violation on idview-add - -When the option --domain-resolution-order is used with the command -ipa idview-add, the resulting LDAP object stores the value in -ipadomainresolutionorder attribute. -The issue is that the add command does not add the needed object -class (ipaNameResolutionData) because it is part of -possible_objectclasses but not of object_class. - -The fix makes sure to add the objectclass when the option ---domain-resolution-order is used, and adds a non-regression test. - -Note that idview-mod does not have any issue as it correctly handles -the addition of missing possible objectclasses. - -Fixes: -https://pagure.io/freeipa/issue/7350 - -Reviewed-By: Alexander Bokovoy -Reviewed-By: Alexander Bokovoy ---- - ipaserver/plugins/idviews.py | 15 +++++++++---- - ipatests/test_xmlrpc/test_idviews_plugin.py | 35 +++++++++++++++++++++++++++++ - 2 files changed, 46 insertions(+), 4 deletions(-) - -diff --git a/ipaserver/plugins/idviews.py b/ipaserver/plugins/idviews.py -index a55c20bbf2466d9cb3a317d49a8bba3c9379f572..2b06cc54e4b04aac004efbf02a446464b8c89777 100644 ---- a/ipaserver/plugins/idviews.py -+++ b/ipaserver/plugins/idviews.py -@@ -22,10 +22,11 @@ import re - import six - - from .baseldap import (LDAPQuery, LDAPObject, LDAPCreate, -- LDAPDelete, LDAPUpdate, LDAPSearch, -- LDAPAddAttributeViaOption, -- LDAPRemoveAttributeViaOption, -- LDAPRetrieve, global_output_params) -+ LDAPDelete, LDAPUpdate, LDAPSearch, -+ LDAPAddAttributeViaOption, -+ LDAPRemoveAttributeViaOption, -+ LDAPRetrieve, global_output_params, -+ add_missing_object_class) - from .hostgroup import get_complete_hostgroup_member_list - from .service import validate_certificate - from ipalib import api, Str, Int, Bytes, Flag, _, ngettext, errors, output -@@ -169,6 +170,12 @@ class idview_add(LDAPCreate): - def pre_callback(self, ldap, dn, entry_attrs, attrs_list, *keys, **options): - self.api.Object.config.validate_domain_resolution_order(entry_attrs) - -+ # The objectclass ipaNameResolutionData may not be present on -+ # the id view. We need to add it if we define a new -+ # value for ipaDomainResolutionOrder -+ if 'ipadomainresolutionorder' in entry_attrs: -+ add_missing_object_class(ldap, u'ipanameresolutiondata', dn, -+ entry_attrs, update=False) - return dn - - -diff --git a/ipatests/test_xmlrpc/test_idviews_plugin.py b/ipatests/test_xmlrpc/test_idviews_plugin.py -index 35d31b37d8fb87384d9ae550182e353c1d6383cc..3d4cce5ea0505ef8b0cd8253fd74b037890ce18b 100644 ---- a/ipatests/test_xmlrpc/test_idviews_plugin.py -+++ b/ipatests/test_xmlrpc/test_idviews_plugin.py -@@ -1704,4 +1704,39 @@ class test_idviews(Declarative): - ), - ), - -+ # Delete the ID View -+ -+ dict( -+ desc='Delete ID View "%s"' % idview1, -+ command=('idview_del', [idview1], {}), -+ expected=dict( -+ result=dict(failed=[]), -+ summary=u'Deleted ID View "%s"' % idview1, -+ value=[idview1], -+ ), -+ ), -+ -+ # Test the creation of ID view with domain resolution order -+ # Non-regression test for issue 7350 -+ -+ dict( -+ desc='Create ID View "%s"' % idview1, -+ command=( -+ 'idview_add', -+ [idview1], -+ dict(ipadomainresolutionorder=u'%s' % api.env.domain) -+ ), -+ expected=dict( -+ value=idview1, -+ summary=u'Added ID View "%s"' % idview1, -+ result=dict( -+ dn=get_idview_dn(idview1), -+ objectclass=objectclasses.idview + -+ [u'ipanameresolutiondata'], -+ cn=[idview1], -+ ipadomainresolutionorder=[api.env.domain] -+ ) -+ ), -+ ), -+ - ] --- -2.13.6 - diff --git a/SOURCES/0030-Fixing-the-cert-request-comparing-whole-email-addres.patch b/SOURCES/0030-Fixing-the-cert-request-comparing-whole-email-addres.patch deleted file mode 100644 index 8b381a7..0000000 --- a/SOURCES/0030-Fixing-the-cert-request-comparing-whole-email-addres.patch +++ /dev/null @@ -1,107 +0,0 @@ -From 7d28e12612ec08e80cf1351ea523bf4a9adfc255 Mon Sep 17 00:00:00 2001 -From: Felipe Volpone -Date: Thu, 11 May 2017 10:20:02 -0300 -Subject: [PATCH] Fixing the cert-request comparing whole email address - case-sensitively. - -Now, the cert-request command compares the domain part of the -email case-insensitively. - -https://pagure.io/freeipa/issue/5919 - -Reviewed-By: Fraser Tweedale -Reviewed-By: Alexander Bokovoy ---- - ipaserver/plugins/cert.py | 27 ++++++++++++++++++++++++--- - ipatests/test_xmlrpc/test_cert_plugin.py | 23 +++++++++++++++++++++++ - 2 files changed, 47 insertions(+), 3 deletions(-) - -diff --git a/ipaserver/plugins/cert.py b/ipaserver/plugins/cert.py -index c1d389217265f44e646ac27d9adc8d5524c74ce7..501fc9015468c864215cfb604de37cdf6d805e52 100644 ---- a/ipaserver/plugins/cert.py -+++ b/ipaserver/plugins/cert.py -@@ -710,7 +710,9 @@ class cert_request(Create, BaseCertMethod, VirtualCommand): - # fail if any email addr from DN does not appear in ldap entry - email_addrs = csr_obj.subject.get_attributes_for_oid( - cryptography.x509.oid.NameOID.EMAIL_ADDRESS) -- if len(set(email_addrs) - set(principal_obj.get('mail', []))) > 0: -+ csr_emails = [attr.value for attr in email_addrs] -+ if not _emails_are_valid(csr_emails, -+ principal_obj.get('mail', [])): - raise errors.ValidationError( - name='csr', - error=_( -@@ -796,8 +798,8 @@ class cert_request(Create, BaseCertMethod, VirtualCommand): - "match requested principal") % gn.name) - elif isinstance(gn, cryptography.x509.general_name.RFC822Name): - if principal_type == USER: -- if principal_obj and gn.value not in principal_obj.get( -- 'mail', []): -+ if not _emails_are_valid([gn.value], -+ principal_obj.get('mail', [])): - raise errors.ValidationError( - name='csr', - error=_( -@@ -865,6 +867,25 @@ class cert_request(Create, BaseCertMethod, VirtualCommand): - ) - - -+def _emails_are_valid(csr_emails, principal_emails): -+ """ -+ Checks if any email address from certificate request does not -+ appear in ldap entry, comparing the domain part case-insensitively. -+ """ -+ -+ def lower_domain(email): -+ email_splitted = email.split('@', 1) -+ if len(email_splitted) > 1: -+ email_splitted[1] = email_splitted[1].lower() -+ -+ return '@'.join(email_splitted) -+ -+ principal_emails_lower = set(map(lower_domain, principal_emails)) -+ csr_emails_lower = set(map(lower_domain, csr_emails)) -+ -+ return csr_emails_lower.issubset(principal_emails_lower) -+ -+ - def principal_to_principal_type(principal): - if principal.is_user: - return USER -diff --git a/ipatests/test_xmlrpc/test_cert_plugin.py b/ipatests/test_xmlrpc/test_cert_plugin.py -index 0b8277b8a6d67777db2eb328116ed0a761914663..dc9e8cba7b40e7b655ea7c0e3bed7706ac78ed1a 100644 ---- a/ipatests/test_xmlrpc/test_cert_plugin.py -+++ b/ipatests/test_xmlrpc/test_cert_plugin.py -@@ -253,6 +253,29 @@ class test_cert(BaseCert): - res = api.Command['service_find'](self.service_princ) - assert res['count'] == 0 - -+ def test_00011_emails_are_valid(self): -+ """ -+ Verify the different scenarios when checking if any email addr -+ from DN or SAN extension does not appear in ldap entry. -+ """ -+ -+ from ipaserver.plugins.cert import _emails_are_valid -+ email_addrs = [u'any@EmAiL.CoM'] -+ result = _emails_are_valid(email_addrs, [u'any@email.com']) -+ assert True == result, result -+ -+ email_addrs = [u'any@EmAiL.CoM'] -+ result = _emails_are_valid(email_addrs, [u'any@email.com', -+ u'another@email.com']) -+ assert True == result, result -+ -+ result = _emails_are_valid([], [u'any@email.com']) -+ assert True == result, result -+ -+ email_addrs = [u'invalidEmailAddress'] -+ result = _emails_are_valid(email_addrs, []) -+ assert False == result, result -+ - - @pytest.mark.tier1 - class test_cert_find(XMLRPC_test): --- -2.13.6 - diff --git a/SOURCES/0030-ipaserver-plugins-cert.py-Added-reason-to-raise-of-e.patch b/SOURCES/0030-ipaserver-plugins-cert.py-Added-reason-to-raise-of-e.patch new file mode 100644 index 0000000..e744031 --- /dev/null +++ b/SOURCES/0030-ipaserver-plugins-cert.py-Added-reason-to-raise-of-e.patch @@ -0,0 +1,33 @@ +From f49068ecd3484b4898517854b9886bd3b0691691 Mon Sep 17 00:00:00 2001 +From: Thomas Woerner +Date: Fri, 27 Jul 2018 12:15:17 +0200 +Subject: [PATCH] ipaserver/plugins/cert.py: Added reason to raise of + errors.NotFound + +In the case that enabledService is not found ipaConfigString kdc entry, a +NotFound error was raised without setting the reason. This resulted in a +traceback. + +Fixes: https://pagure.io/freeipa/issue/7652 +Reviewed-By: Rob Crittenden +--- + ipaserver/plugins/cert.py | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/ipaserver/plugins/cert.py b/ipaserver/plugins/cert.py +index db624357af189753980927655215b17c06291a15..0663272c9b4fd73225f63fe52d8d31157d5cc690 100644 +--- a/ipaserver/plugins/cert.py ++++ b/ipaserver/plugins/cert.py +@@ -301,7 +301,8 @@ def ca_kdc_check(api_instance, hostname): + ipaconfigstring = {val.lower() for val in kdc_entry['ipaConfigString']} + + if 'enabledservice' not in ipaconfigstring: +- raise errors.NotFound() ++ raise errors.NotFound( ++ reason=_("enabledService not in ipaConfigString kdc entry")) + + except errors.NotFound: + raise errors.ACIError( +-- +2.17.1 + diff --git a/SOURCES/0031-Add-force-join-into-ipa-replica-install-manpage.patch b/SOURCES/0031-Add-force-join-into-ipa-replica-install-manpage.patch deleted file mode 100644 index 75c9b16..0000000 --- a/SOURCES/0031-Add-force-join-into-ipa-replica-install-manpage.patch +++ /dev/null @@ -1,29 +0,0 @@ -From c5b8c6256e65e5bb735bd89ab132c28c5a03dd64 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Tibor=20Dudl=C3=A1k?= -Date: Tue, 13 Jun 2017 12:27:01 +0200 -Subject: [PATCH] Add --force-join into ipa-replica-install manpage - -Resolves: https://pagure.io/freeipa/issue/7011 -Reviewed-By: Florence Blanc-Renaud -Reviewed-By: Stanislav Laznicka ---- - install/tools/man/ipa-replica-install.1 | 3 +++ - 1 file changed, 3 insertions(+) - -diff --git a/install/tools/man/ipa-replica-install.1 b/install/tools/man/ipa-replica-install.1 -index 7d241324818dd3a5294da5e84b67a19d0d9a31b6..a1284135ac67de2b67b322aec3f6bbfb05f1a8ec 100644 ---- a/install/tools/man/ipa-replica-install.1 -+++ b/install/tools/man/ipa-replica-install.1 -@@ -62,6 +62,9 @@ The Kerberos realm of an existing IPA deployment. - .TP - \fB\-\-hostname\fR - The hostname of this machine (FQDN). If specified, the hostname will be set and the system configuration will be updated to persist over reboot. -+.TP -+\fB\-\-force\-join\fR -+Join the host even if it is already enrolled. - - .SS "DOMAIN LEVEL 0 OPTIONS" - .TP --- -2.13.6 - diff --git a/SOURCES/0031-ipa-commands-print-IPA-is-not-configured-when-ipa-is.patch b/SOURCES/0031-ipa-commands-print-IPA-is-not-configured-when-ipa-is.patch new file mode 100644 index 0000000..b2c1b19 --- /dev/null +++ b/SOURCES/0031-ipa-commands-print-IPA-is-not-configured-when-ipa-is.patch @@ -0,0 +1,135 @@ +From 040df002df8c58c84a94d2fb3dec717beb6e5f0a Mon Sep 17 00:00:00 2001 +From: Florence Blanc-Renaud +Date: Wed, 22 Aug 2018 18:29:07 +0200 +Subject: [PATCH] ipa commands: print 'IPA is not configured' when ipa is not + setup + +Some commands print tracebacks or unclear error message when +they are called on a machine where ipa packages are installed but +IPA is not configured. +Consistently report 'IPA is not configured on this system' in this +case. + +Related to https://pagure.io/freeipa/issue/6261 + +Reviewed-By: Christian Heimes +--- + install/tools/ipa-compat-manage | 2 ++ + install/tools/ipa-csreplica-manage | 4 +++- + ipaserver/advise/base.py | 2 ++ + ipaserver/install/ipa_pkinit_manage.py | 2 ++ + ipaserver/install/ipa_server_upgrade.py | 2 ++ + ipaserver/install/ipa_winsync_migrate.py | 2 +- + 6 files changed, 12 insertions(+), 2 deletions(-) + +diff --git a/install/tools/ipa-compat-manage b/install/tools/ipa-compat-manage +index 6dd259d2aad870e575093d6732157de743502ac2..ce61b702689448dcfbddbf0dad6c2adde861e6e9 100755 +--- a/install/tools/ipa-compat-manage ++++ b/install/tools/ipa-compat-manage +@@ -82,6 +82,8 @@ def main(): + retval = 0 + files = [paths.SCHEMA_COMPAT_ULDIF] + ++ installutils.check_server_configuration() ++ + options, args = parse_options() + + if len(args) != 1: +diff --git a/install/tools/ipa-csreplica-manage b/install/tools/ipa-csreplica-manage +index 87f034d1205938c4551f939b0e8c0da292e90bc1..c7b62b293d68c95912ab641294180eeb74a98573 100755 +--- a/install/tools/ipa-csreplica-manage ++++ b/install/tools/ipa-csreplica-manage +@@ -32,6 +32,7 @@ from ipaserver.install import (replication, installutils, bindinstance, + from ipalib import api, errors + from ipalib.util import has_managed_topology + from ipapython import ipautil, ipaldap, version ++from ipapython.admintool import ScriptError + from ipapython.dn import DN + + logger = logging.getLogger(os.path.basename(__file__)) +@@ -408,6 +409,7 @@ def exit_on_managed_topology(what, hint="topologysegment"): + + + def main(): ++ installutils.check_server_configuration() + options, args = parse_options() + + # Just initialize the environment. This is so the installer can have +@@ -496,7 +498,7 @@ try: + main() + except KeyboardInterrupt: + sys.exit(1) +-except SystemExit as e: ++except (SystemExit, ScriptError) as e: + sys.exit(e) + except Exception as e: + sys.exit("unexpected error: %s" % e) +diff --git a/ipaserver/advise/base.py b/ipaserver/advise/base.py +index 40f2e65dc8f74a833ef6fe107eabf96ac6a3c12b..07b1431e84c31cb7ba2ecc58a9d152e596dd8b00 100644 +--- a/ipaserver/advise/base.py ++++ b/ipaserver/advise/base.py +@@ -30,6 +30,7 @@ from ipalib.errors import ValidationError + from ipaplatform.paths import paths + from ipapython import admintool + from ipapython.ipa_log_manager import Filter ++from ipaserver.install import installutils + + + """ +@@ -418,6 +419,7 @@ class IpaAdvise(admintool.AdminTool): + + def validate_options(self): + super(IpaAdvise, self).validate_options(needs_root=False) ++ installutils.check_server_configuration() + + if len(self.args) > 1: + raise self.option_parser.error("You can only provide one " +diff --git a/ipaserver/install/ipa_pkinit_manage.py b/ipaserver/install/ipa_pkinit_manage.py +index c54bb14f58b9bfd19ae860270a1c1955b4de1732..4a79bba5d1b636827a7a031965b49cf7b34c6330 100644 +--- a/ipaserver/install/ipa_pkinit_manage.py ++++ b/ipaserver/install/ipa_pkinit_manage.py +@@ -9,6 +9,7 @@ import logging + from ipalib import api + from ipaplatform.paths import paths + from ipapython.admintool import AdminTool ++from ipaserver.install import installutils + from ipaserver.install.krbinstance import KrbInstance, is_pkinit_enabled + + logger = logging.getLogger(__name__) +@@ -21,6 +22,7 @@ class PKINITManage(AdminTool): + + def validate_options(self): + super(PKINITManage, self).validate_options(needs_root=True) ++ installutils.check_server_configuration() + + option_parser = self.option_parser + +diff --git a/ipaserver/install/ipa_server_upgrade.py b/ipaserver/install/ipa_server_upgrade.py +index 1e52bca9765671c0b63bf08b0ad47d40477037ee..7643b97f22fbf6c6861317a171c6e2da377365cd 100644 +--- a/ipaserver/install/ipa_server_upgrade.py ++++ b/ipaserver/install/ipa_server_upgrade.py +@@ -35,6 +35,8 @@ class ServerUpgrade(admintool.AdminTool): + def validate_options(self): + super(ServerUpgrade, self).validate_options(needs_root=True) + ++ installutils.check_server_configuration() ++ + if self.options.force: + self.options.skip_version_check = True + +diff --git a/ipaserver/install/ipa_winsync_migrate.py b/ipaserver/install/ipa_winsync_migrate.py +index 0399b9b7b683cd92692094b53208dcc0f40fd392..43330ad502e736816a1af8e8c2a444e1eae05baa 100644 +--- a/ipaserver/install/ipa_winsync_migrate.py ++++ b/ipaserver/install/ipa_winsync_migrate.py +@@ -350,7 +350,7 @@ class WinsyncMigrate(admintool.AdminTool): + # Check if the IPA server is configured before attempting to migrate + try: + installutils.check_server_configuration() +- except RuntimeError as e: ++ except admintool.ScriptError as e: + sys.exit(e) + + # Finalize API +-- +2.17.1 + diff --git a/SOURCES/0032-Changed-ownership-of-ldiffile-to-DS_USER.patch b/SOURCES/0032-Changed-ownership-of-ldiffile-to-DS_USER.patch deleted file mode 100644 index d2e1476..0000000 --- a/SOURCES/0032-Changed-ownership-of-ldiffile-to-DS_USER.patch +++ /dev/null @@ -1,32 +0,0 @@ -From 780dc73f513cc312e87948b51e90ae885f29a8fb Mon Sep 17 00:00:00 2001 -From: Thorsten Scherf -Date: Thu, 1 Jun 2017 22:02:57 +0200 -Subject: [PATCH] Changed ownership of ldiffile to DS_USER - -Resolves: -https://pagure.io/freeipa/issue/7010 - -Reviewed-By: Martin Basti -Reviewed-By: Stanislav Laznicka ---- - ipaserver/install/ipa_restore.py | 4 ++++ - 1 file changed, 4 insertions(+) - -diff --git a/ipaserver/install/ipa_restore.py b/ipaserver/install/ipa_restore.py -index 923b1d6696d33c0bb07ca018b53dd3dabcc191aa..a3824df230857b02b47c12645fadee1200afdf66 100644 ---- a/ipaserver/install/ipa_restore.py -+++ b/ipaserver/install/ipa_restore.py -@@ -540,6 +540,10 @@ class Restore(admintool.AdminTool): - ldif_parser = RemoveRUVParser(in_file, ldif_writer, self.log) - ldif_parser.parse() - -+ # Make sure the modified ldiffile is owned by DS_USER -+ pent = pwd.getpwnam(constants.DS_USER) -+ os.chown(ldiffile, pent.pw_uid, pent.pw_gid) -+ - if online: - conn = self.get_connection() - ent = conn.make_entry( --- -2.13.6 - diff --git a/SOURCES/0032-DS-replication-settings-fix-regression-with-3.3-mast.patch b/SOURCES/0032-DS-replication-settings-fix-regression-with-3.3-mast.patch new file mode 100644 index 0000000..3b21d3c --- /dev/null +++ b/SOURCES/0032-DS-replication-settings-fix-regression-with-3.3-mast.patch @@ -0,0 +1,57 @@ +From e561f8bb163e68766b61bca72619a54cc5e8bc2d Mon Sep 17 00:00:00 2001 +From: Florence Blanc-Renaud +Date: Tue, 21 Aug 2018 11:37:17 +0200 +Subject: [PATCH] DS replication settings: fix regression with <3.3 master + +Commit 811b0fdb4620938963f1a29d3fdd22257327562c introduced a regression +when configuring replication with a master < 3.3 +Even if 389-ds schema is extended with nsds5ReplicaReleaseTimeout, +nsds5ReplicaBackoffMax and nsDS5ReplicaBindDnGroupCheckInterval +attributes, it will return UNWILLING_TO_PERFORM when a mod +operation is performed on the cn=replica entry. + +This patch ignores the error and logs a debug msg. + +See: https://pagure.io/freeipa/issue/7617 +Reviewed-By: Christian Heimes +--- + ipaserver/install/replication.py | 16 +++++++++++++++- + 1 file changed, 15 insertions(+), 1 deletion(-) + +diff --git a/ipaserver/install/replication.py b/ipaserver/install/replication.py +index 78c4a43cc9b8d9a6740d26209f347f64b879743e..92a99cd9482f86d6820230479bf94c871669572e 100644 +--- a/ipaserver/install/replication.py ++++ b/ipaserver/install/replication.py +@@ -22,6 +22,7 @@ from __future__ import print_function, absolute_import + import logging + import itertools + ++import re + import six + import time + import datetime +@@ -600,7 +601,20 @@ class ReplicationManager(object): + r_conn.simple_bind(r_binddn, r_bindpw) + else: + r_conn.gssapi_bind() +- self._finalize_replica_settings(r_conn) ++ # If the remote server has 389-ds < 1.3, it does not ++ # support the attributes we are trying to set. ++ # Find which 389-ds is installed ++ rootdse = r_conn.get_entry(DN(''), ['vendorVersion']) ++ version = rootdse.single_value.get('vendorVersion') ++ mo = re.search(r'(\d+)\.(\d+)\.(\d+)[\.\d]*', version) ++ vendor_version = tuple(int(v) for v in mo.groups()) ++ if vendor_version >= (1, 3, 0): ++ # 389-ds understands the replication attributes, ++ # we can safely modify them ++ self._finalize_replica_settings(r_conn) ++ else: ++ logger.debug("replication attributes not supported " ++ "on remote master, skipping update.") + r_conn.close() + + def setup_chaining_backend(self, conn): +-- +2.17.1 + diff --git a/SOURCES/0033-Checks-if-Dir-Server-is-installed-and-running-before.patch b/SOURCES/0033-Checks-if-Dir-Server-is-installed-and-running-before.patch deleted file mode 100644 index c6a3dba..0000000 --- a/SOURCES/0033-Checks-if-Dir-Server-is-installed-and-running-before.patch +++ /dev/null @@ -1,57 +0,0 @@ -From a81a4a502b020e0b0d91e6018914ea18b4e3e47e Mon Sep 17 00:00:00 2001 -From: Felipe Barreto -Date: Wed, 20 Sep 2017 09:51:44 -0300 -Subject: [PATCH] Checks if Dir Server is installed and running before IPA - installation - -In cases when IPA is installed in two steps (external CA), it's -necessary to check (in the second step) if Dir. Server is -running before continue with the installation. If it's not, -start Directory Server. - -https://pagure.io/freeipa/issue/6611 - -Reviewed-By: Fraser Tweedale -Reviewed-By: Christian Heimes ---- - ipaplatform/redhat/services.py | 4 ++++ - ipaserver/install/server/install.py | 8 ++++++++ - 2 files changed, 12 insertions(+) - -diff --git a/ipaplatform/redhat/services.py b/ipaplatform/redhat/services.py -index 8fae1f3cc5b12dba0fa0192f21bc6d2d369941eb..57aa15ad9a4d83366ff02e5a5ca6e4574561e1fa 100644 ---- a/ipaplatform/redhat/services.py -+++ b/ipaplatform/redhat/services.py -@@ -119,6 +119,10 @@ class RedHatDirectoryService(RedHatService): - - return True - -+ def is_installed(self, instance_name): -+ file_path = "{}/{}-{}".format(paths.ETC_DIRSRV, "slapd", instance_name) -+ return os.path.exists(file_path) -+ - def restart(self, instance_name="", capture_output=True, wait=True, - ldapi=False): - # We need to explicitly enable instances to install proper symlinks as -diff --git a/ipaserver/install/server/install.py b/ipaserver/install/server/install.py -index 97cbc6d8c84ee8fc21b6f8983c7897dc5d30c42d..422474fa915b4876530f304ef9424f6b31cf26cc 100644 ---- a/ipaserver/install/server/install.py -+++ b/ipaserver/install/server/install.py -@@ -616,6 +616,14 @@ def install_check(installer): - # check addresses here, dns module is doing own check - no_matching_interface_for_ip_address_warning(ip_addresses) - -+ instance_name = "-".join(realm_name.split(".")) -+ dirsrv = services.knownservices.dirsrv -+ if (options.external_cert_files -+ and dirsrv.is_installed(instance_name) -+ and not dirsrv.is_running(instance_name)): -+ root_logger.debug('Starting Directory Server') -+ services.knownservices.dirsrv.start(instance_name) -+ - if options.setup_adtrust: - adtrust.install_check(False, options, api) - --- -2.13.6 - diff --git a/SOURCES/0033-Disable-message-about-log-in-ipa-backup-if-IPA-is-no.patch b/SOURCES/0033-Disable-message-about-log-in-ipa-backup-if-IPA-is-no.patch new file mode 100644 index 0000000..6d0a2af --- /dev/null +++ b/SOURCES/0033-Disable-message-about-log-in-ipa-backup-if-IPA-is-no.patch @@ -0,0 +1,92 @@ +From 840e6144cd1ad75a7cf6ba5a8c936c6aa9bfc58d Mon Sep 17 00:00:00 2001 +From: Rob Crittenden +Date: Tue, 1 May 2018 11:15:18 -0400 +Subject: [PATCH] Disable message about log in ipa-backup if IPA is not + configured + +Introduce server installation constants similar to the client +but only tie in SERVER_NOT_CONFIGURED right now. + +For the case of not configured don't spit out the "See +for more information" because no logging was actually done. + +In the case of ipa-backup this could also be confusing if the +--log-file option was also passed in because it would not be +used. + +https://pagure.io/freeipa/issue/6843 + +Signed-off-by: Rob Crittenden +Reviewed-By: Christian Heimes +Reviewed-By: Christian Heimes +--- + install/tools/man/ipa-backup.1 | 2 ++ + ipapython/admintool.py | 8 +++++++- + ipaserver/install/installutils.py | 5 +++-- + 3 files changed, 12 insertions(+), 3 deletions(-) + +diff --git a/install/tools/man/ipa-backup.1 b/install/tools/man/ipa-backup.1 +index ff9759ec77d54f32532c4ececfa5081daab9ec15..9e2900f770880d3a554df5cd5d0430716e3bf70e 100644 +--- a/install/tools/man/ipa-backup.1 ++++ b/install/tools/man/ipa-backup.1 +@@ -69,6 +69,8 @@ Log to the given file + 0 if the command was successful + + 1 if an error occurred ++ ++2 if IPA is not configured + .SH "FILES" + .PP + \fI/var/lib/ipa/backup\fR +diff --git a/ipapython/admintool.py b/ipapython/admintool.py +index 329e20f3744e304626bebcadfbb187782451dd4f..5a5d3eb421b204944123e19a2d7303d7237492cb 100644 +--- a/ipapython/admintool.py ++++ b/ipapython/admintool.py +@@ -32,6 +32,10 @@ from ipapython import version + from ipapython import config + from ipapython.ipa_log_manager import standard_logging_setup + ++SUCCESS = 0 ++SERVER_INSTALL_ERROR = 1 ++SERVER_NOT_CONFIGURED = 2 ++ + logger = logging.getLogger(__name__) + + +@@ -301,7 +305,9 @@ class AdminTool(object): + if error_message: + logger.error('%s', error_message) + message = "The %s command failed." % self.command_name +- if self.log_file_name: ++ if self.log_file_name and return_value != 2: ++ # magic value because this is common between server and client ++ # but imports are not straigthforward + message += " See %s for more information" % self.log_file_name + logger.error('%s', message) + +diff --git a/ipaserver/install/installutils.py b/ipaserver/install/installutils.py +index 22889ed1b305b8df91eda0831c56c4b187cf6f86..6614da69a0a046cdfa1b309f77972ead9de1279f 100644 +--- a/ipaserver/install/installutils.py ++++ b/ipaserver/install/installutils.py +@@ -56,7 +56,7 @@ from ipalib.install import sysrestore + from ipalib.install.kinit import kinit_password + import ipaplatform + from ipapython import ipautil, admintool, version +-from ipapython.admintool import ScriptError ++from ipapython.admintool import ScriptError, SERVER_NOT_CONFIGURED # noqa: E402 + from ipapython.certdb import EXTERNAL_CA_TRUST_FLAGS + from ipapython.ipaldap import DIRMAN_DN, LDAPClient + from ipalib.util import validate_hostname +@@ -934,7 +934,8 @@ def check_server_configuration(): + """ + server_fstore = sysrestore.FileStore(paths.SYSRESTORE) + if not server_fstore.has_files(): +- raise RuntimeError("IPA is not configured on this system.") ++ raise ScriptError("IPA is not configured on this system.", ++ rval=SERVER_NOT_CONFIGURED) + + + def remove_file(filename): +-- +2.17.1 + diff --git a/SOURCES/0034-WebUI-Add-positive-number-validator.patch b/SOURCES/0034-WebUI-Add-positive-number-validator.patch deleted file mode 100644 index f4089e9..0000000 --- a/SOURCES/0034-WebUI-Add-positive-number-validator.patch +++ /dev/null @@ -1,96 +0,0 @@ -From a919a3ad3463eedee55b4aa2ae680c34241412b0 Mon Sep 17 00:00:00 2001 -From: Pavel Vomacka -Date: Tue, 11 Jul 2017 10:46:36 +0200 -Subject: [PATCH] WebUI: Add positive number validator - -Add new validator which inherits from integer validator -and checks whether the integer is positive. - -https://pagure.io/freeipa/issue/6980 - -Reviewed-By: Felipe Volpone -Reviewed-By: Felipe Barreto ---- - install/ui/src/freeipa/field.js | 43 +++++++++++++++++++++++++++++++++++++++++ - ipaserver/plugins/internal.py | 1 + - 2 files changed, 44 insertions(+) - -diff --git a/install/ui/src/freeipa/field.js b/install/ui/src/freeipa/field.js -index 76ce2533af5388ff5e1a2cfe8e35286f4e55b378..f998b578c38d91819fea418ea50e03589a691cbf 100644 ---- a/install/ui/src/freeipa/field.js -+++ b/install/ui/src/freeipa/field.js -@@ -1049,9 +1049,51 @@ field.validator = IPA.validator = function(spec) { - return that.true_result(); - }; - -+ that.integer_validate = that.validate; -+ - return that; - }; - -+ -+/** -+ * Javascript positive integer validator -+ * -+ * It allows to insert only positive integer. -+ * -+ * @class -+ * @alternateClassName IPA.positive_integer_validator -+ * @extends IPA.validator -+ */ -+ field.positive_integer_validator = IPA.positive_integer_validator = function(spec) { -+ -+ var that = IPA.integer_validator(spec); -+ -+ /** -+ * @inheritDoc -+ */ -+ -+ that.validate = function(value) { -+ -+ var integer_check = that.integer_validate(value); -+ -+ if (!integer_check.valid) { -+ return integer_check; -+ } -+ -+ var num = parseInt(value); -+ -+ if (num <= 0) { -+ return that.false_result( -+ text.get('@i18n:widget.validation.positive_number')); -+ } -+ -+ return that.true_result(); -+ }; -+ -+ return that; -+ }; -+ -+ - /** - * Metadata validator - * -@@ -1871,6 +1913,7 @@ field.register = function() { - v.register('unsupported', field.unsupported_validator); - v.register('same_password', field.same_password_validator); - v.register('integer', field.integer_validator); -+ v.register('positive_integer', field.positive_integer_validator); - - l.register('adapter', field.Adapter); - l.register('object_adapter', field.ObjectAdapter); -diff --git a/ipaserver/plugins/internal.py b/ipaserver/plugins/internal.py -index ff239db07ec1235c4174c6f9451c71195ab5a60a..c293e0b5e06677f09daa4b820ffd06a2671cd6e1 100644 ---- a/ipaserver/plugins/internal.py -+++ b/ipaserver/plugins/internal.py -@@ -982,6 +982,7 @@ class i18n_messages(Command): - "min_value": _("Minimum value is ${value}"), - "net_address": _("Not a valid network address (examples: 2001:db8::/64, 192.0.2.0/24)"), - "parse": _("Parse error"), -+ "positive_number": _("Must be a positive number"), - "port": _("'${port}' is not a valid port"), - "required": _("Required field"), - "unsupported": _("Unsupported value"), --- -2.13.6 - diff --git a/SOURCES/0034-uninstall-v-remove-Tracebacks.patch b/SOURCES/0034-uninstall-v-remove-Tracebacks.patch new file mode 100644 index 0000000..d498c81 --- /dev/null +++ b/SOURCES/0034-uninstall-v-remove-Tracebacks.patch @@ -0,0 +1,58 @@ +From da98cbda7b61792d956b21f0a9999d91894ae194 Mon Sep 17 00:00:00 2001 +From: Florence Blanc-Renaud +Date: Tue, 21 Aug 2018 17:55:45 +0200 +Subject: [PATCH] uninstall -v: remove Tracebacks + +ipa-server-install --uninstall -v -U prints Traceback in its log file. +This issue happens because it calls subprocess.Popen with close_fds=True +(which closes all file descriptors in the child process) +but it is trying to use the file logger in the child process +(preexec_fn is called in the child just before the child is executed). +The fix is using the logger only in the parent process. + +Fixes: https://pagure.io/freeipa/issue/7681 +Reviewed-By: Rob Crittenden +--- + ipapython/ipautil.py | 23 ++++++++++++----------- + 1 file changed, 12 insertions(+), 11 deletions(-) + +diff --git a/ipapython/ipautil.py b/ipapython/ipautil.py +index 0de5fe86374429bfb9666a0d257e079883a1c56b..42329ad7c1677ad16c8d3275d79666005571cbc7 100644 +--- a/ipapython/ipautil.py ++++ b/ipapython/ipautil.py +@@ -481,20 +481,21 @@ def run(args, stdin=None, raiseonerr=True, nolog=(), env=None, + logger.debug('Starting external process') + logger.debug('args=%s', arg_string) + +- def preexec_fn(): +- if runas is not None: +- pent = pwd.getpwnam(runas) ++ if runas is not None: ++ pent = pwd.getpwnam(runas) + +- suplementary_gids = [ +- grp.getgrnam(sgroup).gr_gid for sgroup in suplementary_groups +- ] ++ suplementary_gids = [ ++ grp.getgrnam(sgroup).gr_gid for sgroup in suplementary_groups ++ ] + +- logger.debug('runas=%s (UID %d, GID %s)', runas, +- pent.pw_uid, pent.pw_gid) +- if suplementary_groups: +- for group, gid in zip(suplementary_groups, suplementary_gids): +- logger.debug('suplementary_group=%s (GID %d)', group, gid) ++ logger.debug('runas=%s (UID %d, GID %s)', runas, ++ pent.pw_uid, pent.pw_gid) ++ if suplementary_groups: ++ for group, gid in zip(suplementary_groups, suplementary_gids): ++ logger.debug('suplementary_group=%s (GID %d)', group, gid) + ++ def preexec_fn(): ++ if runas is not None: + os.setgroups(suplementary_gids) + os.setregid(pent.pw_gid, pent.pw_gid) + os.setreuid(pent.pw_uid, pent.pw_uid) +-- +2.17.1 + diff --git a/SOURCES/0035-Do-not-set-ca_host-when-setup-ca-is-used.patch b/SOURCES/0035-Do-not-set-ca_host-when-setup-ca-is-used.patch new file mode 100644 index 0000000..27b11e1 --- /dev/null +++ b/SOURCES/0035-Do-not-set-ca_host-when-setup-ca-is-used.patch @@ -0,0 +1,86 @@ +From 06654aba40bd79eff8bd44ac629bb5bb9b8f9c26 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Tibor=20Dudl=C3=A1k?= +Date: Thu, 26 Jul 2018 11:46:55 +0200 +Subject: [PATCH] Do not set ca_host when --setup-ca is used + +Setting ca_host caused replication failures on DL0 +because it was trying to connect to wrong CA host. +Trying to avoid corner-case in ipaserver/plugins/dogtag.py +when api.env.host nor api.env.ca_host had not CA configured +and there was ca_host set to api.env.ca_host variable. + +See: https://pagure.io/freeipa/issue/7566 +Resolves: https://pagure.io/freeipa/issue/7629 +Reviewed-By: Florence Blanc-Renaud +Reviewed-By: Alexander Bokovoy +Reviewed-By: Christian Heimes +Reviewed-By: Rob Crittenden +--- + ipaserver/install/cainstance.py | 24 ++++++++++++++++++++++ + ipaserver/install/server/replicainstall.py | 7 +++++-- + 2 files changed, 29 insertions(+), 2 deletions(-) + +diff --git a/ipaserver/install/cainstance.py b/ipaserver/install/cainstance.py +index 6dbf69b3e5833f220a4d7d640b66a8fcf824f445..ffcebd719a16ebc5a991b35507b96411ad31eb10 100644 +--- a/ipaserver/install/cainstance.py ++++ b/ipaserver/install/cainstance.py +@@ -451,6 +451,11 @@ class CAInstance(DogtagInstance): + self.step("updating IPA configuration", update_ipa_conf) + self.step("enabling CA instance", self.__enable_instance) + if not promote: ++ if self.clone: ++ # DL0 workaround; see docstring of __expose_ca_in_ldap ++ self.step("exposing CA instance on LDAP", ++ self.__expose_ca_in_ldap) ++ + self.step("migrating certificate profiles to LDAP", + migrate_profiles_to_ldap) + self.step("importing IPA certificate profiles", +@@ -1268,6 +1273,25 @@ class CAInstance(DogtagInstance): + config = [] + self.ldap_configure('CA', self.fqdn, None, basedn, config) + ++ def __expose_ca_in_ldap(self): ++ """ ++ In a case when replica is created on DL0 we need to make ++ sure that query for CA service record of this replica in ++ ldap will succeed in time of installation. ++ This method is needed for sucessfull replica installation ++ on DL0 and should be removed alongside with code for DL0. ++ ++ To suppress deprecation warning message this method is ++ not invoking ldap_enable() but _ldap_enable() method. ++ """ ++ ++ basedn = ipautil.realm_to_suffix(self.realm) ++ if not self.clone: ++ config = ['caRenewalMaster'] ++ else: ++ config = [] ++ self._ldap_enable(u'enabledService', "CA", self.fqdn, basedn, config) ++ + def setup_lightweight_ca_key_retrieval(self): + if sysupgrade.get_upgrade_state('dogtag', 'setup_lwca_key_retrieval'): + return +diff --git a/ipaserver/install/server/replicainstall.py b/ipaserver/install/server/replicainstall.py +index e78a2b992fbd44b8ee3ccd8183ebd6e13dfd1749..42c723b57699340d7dfa67f581ab7d4d4fdcf551 100644 +--- a/ipaserver/install/server/replicainstall.py ++++ b/ipaserver/install/server/replicainstall.py +@@ -241,9 +241,12 @@ def create_ipa_conf(fstore, config, ca_enabled, master=None): + gopts.extend([ + ipaconf.setOption('enable_ra', 'True'), + ipaconf.setOption('ra_plugin', 'dogtag'), +- ipaconf.setOption('dogtag_version', '10'), +- ipaconf.setOption('ca_host', config.ca_host_name) ++ ipaconf.setOption('dogtag_version', '10') + ]) ++ ++ if not config.setup_ca: ++ gopts.append(ipaconf.setOption('ca_host', config.ca_host_name)) ++ + else: + gopts.extend([ + ipaconf.setOption('enable_ra', 'False'), +-- +2.17.1 + diff --git a/SOURCES/0035-WebUI-change-validator-of-page-size-settings.patch b/SOURCES/0035-WebUI-change-validator-of-page-size-settings.patch deleted file mode 100644 index 5f15079..0000000 --- a/SOURCES/0035-WebUI-change-validator-of-page-size-settings.patch +++ /dev/null @@ -1,34 +0,0 @@ -From e37b90a39420cd3e4f5b6e48d83aa331cd52c9da Mon Sep 17 00:00:00 2001 -From: Pavel Vomacka -Date: Tue, 11 Jul 2017 10:49:46 +0200 -Subject: [PATCH] WebUI: change validator of page size settings - -Previously, this configuration field was validated by integer_validator -which only checks that the input is number. -Now new positive_integer_validator can also check that -the inputed number positive. - -https://pagure.io/freeipa/issue/6980 - -Reviewed-By: Felipe Volpone -Reviewed-By: Felipe Barreto ---- - install/ui/src/freeipa/Application_controller.js | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/install/ui/src/freeipa/Application_controller.js b/install/ui/src/freeipa/Application_controller.js -index 5eb4e7a5104b780761b9a5179dbfd1501a8d1478..51f579e3cce2c28e8f1d2d231fa10711d0b2498d 100644 ---- a/install/ui/src/freeipa/Application_controller.js -+++ b/install/ui/src/freeipa/Application_controller.js -@@ -312,7 +312,7 @@ define([ - $type: 'text', - name: 'pagination_size', - label: '@i18n:customization.table_pagination', -- validators: ['integer'] -+ validators: ['positive_integer'] - } - ] - }); --- -2.13.6 - diff --git a/SOURCES/0036-Fix-ipa-replica-install-when-key-not-protected-by-PI.patch b/SOURCES/0036-Fix-ipa-replica-install-when-key-not-protected-by-PI.patch new file mode 100644 index 0000000..8e96414 --- /dev/null +++ b/SOURCES/0036-Fix-ipa-replica-install-when-key-not-protected-by-PI.patch @@ -0,0 +1,59 @@ +From 42bb4ee747a2f22db756bd037b2b0044853bb41d Mon Sep 17 00:00:00 2001 +From: Florence Blanc-Renaud +Date: Tue, 21 Nov 2017 14:49:46 +0100 +Subject: [PATCH] Fix ipa-replica-install when key not protected by PIN + +When ipa-replica-install is called in a CA-less environment, the certs, +keys and pins need to be provided with --{http|dirsrv|pkinit}-cert-file and +--{http|dirsrv|pkinit}-pin. If the pin is not provided in the CLI options, +and in interactive mode, the installer prompts for the PIN. +The issue happens when the keys are not protected by any PIN, the installer +does not accept an empty string and keeps on asking for a PIN. + +The fix makes sure that the installer accepts an empty PIN. A similar fix +was done for ipa-server-install in +https://pagure.io/freeipa/c/4ee426a68ec60370eee6f5aec917ecce444840c7 + +Fixes: +https://pagure.io/freeipa/issue/7274 + +Reviewed-By: Christian Heimes +Reviewed-By: Christian Heimes +--- + ipaserver/install/server/replicainstall.py | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +diff --git a/ipaserver/install/server/replicainstall.py b/ipaserver/install/server/replicainstall.py +index 42c723b57699340d7dfa67f581ab7d4d4fdcf551..396d6089449225cc83aa28552a2009b9057e65ab 100644 +--- a/ipaserver/install/server/replicainstall.py ++++ b/ipaserver/install/server/replicainstall.py +@@ -1057,7 +1057,7 @@ def promote_check(installer): + if options.http_pin is None: + options.http_pin = installutils.read_password( + "Enter Apache Server private key unlock", +- confirm=False, validate=False) ++ confirm=False, validate=False, retry=False) + if options.http_pin is None: + raise ScriptError( + "Apache Server private key unlock password required") +@@ -1073,7 +1073,7 @@ def promote_check(installer): + if options.dirsrv_pin is None: + options.dirsrv_pin = installutils.read_password( + "Enter Directory Server private key unlock", +- confirm=False, validate=False) ++ confirm=False, validate=False, retry=False) + if options.dirsrv_pin is None: + raise ScriptError( + "Directory Server private key unlock password required") +@@ -1089,7 +1089,7 @@ def promote_check(installer): + if options.pkinit_pin is None: + options.pkinit_pin = installutils.read_password( + "Enter Kerberos KDC private key unlock", +- confirm=False, validate=False) ++ confirm=False, validate=False, retry=False) + if options.pkinit_pin is None: + raise ScriptError( + "Kerberos KDC private key unlock password required") +-- +2.17.1 + diff --git a/SOURCES/0036-WebUI-fix-jslint-error.patch b/SOURCES/0036-WebUI-fix-jslint-error.patch deleted file mode 100644 index 93224f9..0000000 --- a/SOURCES/0036-WebUI-fix-jslint-error.patch +++ /dev/null @@ -1,30 +0,0 @@ -From ae6a8489b65fb28a3381267bcfacf4a1d42047c9 Mon Sep 17 00:00:00 2001 -From: Pavel Vomacka -Date: Fri, 21 Jul 2017 18:49:01 +0200 -Subject: [PATCH] WebUI: fix jslint error - -jslint warned about parsing string to integer without explicit radix. -This error was introduced in commit 3cac851 . - -Reviewed-By: Alexander Bokovoy -Reviewed-By: Felipe Barreto ---- - install/ui/src/freeipa/field.js | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/install/ui/src/freeipa/field.js b/install/ui/src/freeipa/field.js -index f998b578c38d91819fea418ea50e03589a691cbf..6c99ddbe2e08522c14980e42ad63e28e5b739794 100644 ---- a/install/ui/src/freeipa/field.js -+++ b/install/ui/src/freeipa/field.js -@@ -1080,7 +1080,7 @@ field.validator = IPA.validator = function(spec) { - return integer_check; - } - -- var num = parseInt(value); -+ var num = parseInt(value, 10); - - if (num <= 0) { - return that.false_result( --- -2.13.6 - diff --git a/SOURCES/0037-Clear-next-field-when-returnining-list-elements-in-q.patch b/SOURCES/0037-Clear-next-field-when-returnining-list-elements-in-q.patch new file mode 100644 index 0000000..73bbaf0 --- /dev/null +++ b/SOURCES/0037-Clear-next-field-when-returnining-list-elements-in-q.patch @@ -0,0 +1,64 @@ +From a099794ab890979dbd9fb567c44fcb105da229ff Mon Sep 17 00:00:00 2001 +From: Robbie Harwood +Date: Wed, 22 Aug 2018 15:32:16 -0400 +Subject: [PATCH] Clear next field when returnining list elements in queue.c + +The ipa-otpd code occasionally removes elements from one queue, +inspects and modifies them, and then inserts them into +another (possibly identical, possibly different) queue. When the next +pointer isn't cleared, this can result in element membership in both +queues, leading to double frees, or even self-referential elements, +causing infinite loops at traversal time. + +Rather than eliminating the pattern, make it safe by clearing the next +field any time an element enters or exits a queue. + +Related https://pagure.io/freeipa/issue/7262 + +Reviewed-By: Florence Blanc-Renaud +--- + daemons/ipa-otpd/queue.c | 7 +++++++ + 1 file changed, 7 insertions(+) + +diff --git a/daemons/ipa-otpd/queue.c b/daemons/ipa-otpd/queue.c +index 9e29fb238d5c7a7395bcf3860ce7445c27ca98ac..2944b7ea0db6f49d0a3230b5f33c7a89281fd8c6 100644 +--- a/daemons/ipa-otpd/queue.c ++++ b/daemons/ipa-otpd/queue.c +@@ -111,6 +111,8 @@ void otpd_queue_push(struct otpd_queue *q, struct otpd_queue_item *item) + q->head = q->tail = item; + else + q->tail = q->tail->next = item; ++ ++ item->next = NULL; + } + + void otpd_queue_push_head(struct otpd_queue *q, struct otpd_queue_item *item) +@@ -118,6 +120,8 @@ void otpd_queue_push_head(struct otpd_queue *q, struct otpd_queue_item *item) + if (item == NULL) + return; + ++ item->next = NULL; ++ + if (q->head == NULL) + q->tail = q->head = item; + else { +@@ -145,6 +149,8 @@ struct otpd_queue_item *otpd_queue_pop(struct otpd_queue *q) + if (q->head == NULL) + q->tail = NULL; + ++ if (item != NULL) ++ item->next = NULL; + return item; + } + +@@ -160,6 +166,7 @@ struct otpd_queue_item *otpd_queue_pop_msgid(struct otpd_queue *q, int msgid) + *prev = item->next; + if (q->head == NULL) + q->tail = NULL; ++ item->next = NULL; + return item; + } + } +-- +2.17.1 + diff --git a/SOURCES/0037-ipa-advise-for-smartcards-updated.patch b/SOURCES/0037-ipa-advise-for-smartcards-updated.patch deleted file mode 100644 index d3a9781..0000000 --- a/SOURCES/0037-ipa-advise-for-smartcards-updated.patch +++ /dev/null @@ -1,35 +0,0 @@ -From 1c18017e2fc7dc4e4d943611115e94dc0bc70263 Mon Sep 17 00:00:00 2001 -From: amitkuma -Date: Tue, 16 Jan 2018 15:56:25 +0530 -Subject: [PATCH] ipa-advise for smartcards updated - -...... -authconfig --enablesmartcard --smartcardmodule=sssd --updateall - -Advise is updated to: -authconfig --enablesssd --enablesssdauth --enablesmartcard --smartcardmodule=sssd ---smartcardaction=1 --updateall - -Resolves: https://pagure.io/freeipa/issue/7358 -Reviewed-By: Christian Heimes ---- - ipaserver/advise/plugins/smart_card_auth.py | 3 ++- - 1 file changed, 2 insertions(+), 1 deletion(-) - -diff --git a/ipaserver/advise/plugins/smart_card_auth.py b/ipaserver/advise/plugins/smart_card_auth.py -index fb328f29ca5051ad52c9c5e0000021ad5e8b94e8..109e9ba3815301a556a38f5185918992c4622148 100644 ---- a/ipaserver/advise/plugins/smart_card_auth.py -+++ b/ipaserver/advise/plugins/smart_card_auth.py -@@ -315,7 +315,8 @@ class config_client_for_smart_card_auth(common_smart_card_auth_config): - - def run_authconfig_to_configure_smart_card_auth(self): - self.log.exit_on_failed_command( -- 'authconfig --enablesmartcard --smartcardmodule=sssd --updateall', -+ 'authconfig --enablesssd --enablesssdauth --enablesmartcard ' -+ '--smartcardmodule=sssd --smartcardaction=1 --updateall', - [ - 'Failed to configure Smart Card authentication in SSSD' - ] --- -2.13.6 - diff --git a/SOURCES/0038-Add-a-notice-to-restart-ipa-services-after-certs-are.patch b/SOURCES/0038-Add-a-notice-to-restart-ipa-services-after-certs-are.patch deleted file mode 100644 index e94b45c..0000000 --- a/SOURCES/0038-Add-a-notice-to-restart-ipa-services-after-certs-are.patch +++ /dev/null @@ -1,59 +0,0 @@ -From accc490a5f1db734c94e739d9b9638d44d60d21c Mon Sep 17 00:00:00 2001 -From: Aleksei Slaikovskii -Date: Mon, 23 Oct 2017 11:17:32 +0200 -Subject: [PATCH] Add a notice to restart ipa services after certs are - installed - -Adding notice for user to restart services after -ipa-server-certinstall. - -https://pagure.io/freeipa/issue/7016 - -Reviewed-By: Florence Blanc-Renaud -Reviewed-By: Rob Crittenden -Reviewed-By: Alexander Bokovoy ---- - install/tools/man/ipa-server-certinstall.1 | 3 ++- - ipaserver/install/ipa_server_certinstall.py | 5 +++++ - 2 files changed, 7 insertions(+), 1 deletion(-) - -diff --git a/install/tools/man/ipa-server-certinstall.1 b/install/tools/man/ipa-server-certinstall.1 -index 35cd8c6c711119d7c782c6a89ac78b4894cec073..00fd03b6bc2184ec2bbc099fd9799551c07d2390 100644 ---- a/install/tools/man/ipa-server-certinstall.1 -+++ b/install/tools/man/ipa-server-certinstall.1 -@@ -28,7 +28,8 @@ PKCS#12 is a file format used to safely transport SSL certificates and public/pr - - They may be generated and managed using the NSS pk12util command or the OpenSSL pkcs12 command. - --The service(s) are not automatically restarted. In order to use the newly installed certificate(s) you will need to manually restart the Directory and/or Apache servers. -+The service(s) are not automatically restarted. In order to use the newly installed certificate(s) you will need to manually restart the Directory, Apache and/or Krb5kdc servers. -+ - .SH "OPTIONS" - .TP - \fB\-d\fR, \fB\-\-dirsrv\fR -diff --git a/ipaserver/install/ipa_server_certinstall.py b/ipaserver/install/ipa_server_certinstall.py -index 9c8f6e81a802e1a87bab1fd15f729e10676fe3a3..ec283705a4038239ddf0c6bacaac200936ed04e8 100644 ---- a/ipaserver/install/ipa_server_certinstall.py -+++ b/ipaserver/install/ipa_server_certinstall.py -@@ -17,6 +17,7 @@ - # You should have received a copy of the GNU General Public License - # along with this program. If not, see . - # -+from __future__ import print_function - - import os - import os.path -@@ -121,6 +122,10 @@ class ServerCertInstall(admintool.AdminTool): - if self.options.kdc: - self.install_kdc_cert() - -+ print( -+ "Please restart ipa services after installing certificate " -+ "(ipactl restart)") -+ - api.Backend.ldap2.disconnect() - - def install_dirsrv_cert(self): --- -2.14.3 - diff --git a/SOURCES/0038-Add-cmocka-unit-tests-for-ipa-otpd-queue-code.patch b/SOURCES/0038-Add-cmocka-unit-tests-for-ipa-otpd-queue-code.patch new file mode 100644 index 0000000..a1380c7 --- /dev/null +++ b/SOURCES/0038-Add-cmocka-unit-tests-for-ipa-otpd-queue-code.patch @@ -0,0 +1,253 @@ +From efd0a988a7431e3f31f5dde78f66eaec8c7f7f37 Mon Sep 17 00:00:00 2001 +From: Robbie Harwood +Date: Thu, 30 Aug 2018 15:34:31 -0400 +Subject: [PATCH] Add cmocka unit tests for ipa otpd queue code + +Reviewed-By: Florence Blanc-Renaud +--- + daemons/ipa-otpd/Makefile.am | 12 + + .../ipa-otpd/ipa_otpd_queue_cmocka_tests.c | 212 ++++++++++++++++++ + 2 files changed, 224 insertions(+) + create mode 100644 daemons/ipa-otpd/ipa_otpd_queue_cmocka_tests.c + +diff --git a/daemons/ipa-otpd/Makefile.am b/daemons/ipa-otpd/Makefile.am +index 923e16ebe127a36cea372d656dd913c4ffa09313..c1d6c2053354a59b1fd897f9a67cc7507eed5294 100644 +--- a/daemons/ipa-otpd/Makefile.am ++++ b/daemons/ipa-otpd/Makefile.am +@@ -21,3 +21,15 @@ ipa_otpd_SOURCES = bind.c forward.c main.c parse.c query.c queue.c stdio.c + $< > $@ + + CLEANFILES = $(systemdsystemunit_DATA) ++ ++TESTS = ++check_PROGRAMS = ++ ++if HAVE_CMOCKA ++TESTS += queue_tests ++check_PROGRAMS += queue_tests ++endif ++ ++queue_tests_SOURCES = ipa_otpd_queue_cmocka_tests.c queue.c ++queue_tests_CFLAGS = $(CMOCKA_CFLAGS) ++queue_tests_LDADD = $(CMOCKA_LIBS) +diff --git a/daemons/ipa-otpd/ipa_otpd_queue_cmocka_tests.c b/daemons/ipa-otpd/ipa_otpd_queue_cmocka_tests.c +new file mode 100644 +index 0000000000000000000000000000000000000000..068431e6475bb74b01acbcab22115915dec1a278 +--- /dev/null ++++ b/daemons/ipa-otpd/ipa_otpd_queue_cmocka_tests.c +@@ -0,0 +1,212 @@ ++/* ++ * FreeIPA 2FA companion daemon - internal queue tests ++ * ++ * Author: Robbie Harwood ++ * ++ * Copyright (C) 2018 Robbie Harwood, Red Hat ++ * see file 'COPYING' for use and warranty information ++ * ++ * This program is free software you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the Free ++ * Software Foundation, either version 3 of the License, or (at your option) ++ * any later version. ++ * ++ * This program is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ++ * more details. ++ * ++ * You should have received a copy of the GNU General Public License along ++ * with this program. If not, see . ++ */ ++ ++#include ++#include ++#include ++ ++#include ++ ++#include "internal.h" ++ ++/* Bypass otpd queue allocation/freeing to avoid calling into LDAP and ++ * krad. No effort is made to make the types match. */ ++static struct otpd_queue_item *new_elt(int id) ++{ ++ krb5_error_code ret; ++ struct otpd_queue_item *e = NULL; ++ ++ ret = otpd_queue_item_new(NULL, &e); ++ assert_int_equal(ret, 0); ++ assert_ptr_not_equal(e, NULL); ++ ++ e->msgid = id; ++ return e; ++} ++static void free_elt(struct otpd_queue_item **e) ++{ ++ assert_ptr_not_equal(e, NULL); ++ free(*e); ++ *e = NULL; ++} ++static void free_elts(struct otpd_queue *q) ++{ ++ assert_ptr_not_equal(q, NULL); ++ for (struct otpd_queue_item *e = otpd_queue_pop(q); e != NULL; ++ e = otpd_queue_pop(q)) ++ free_elt(&e); ++} ++#define otpd_queue_item_new new_elt ++#define otpd_queue_item_free free_elt ++#define otpd_queue_free_items free_elts ++ ++static void assert_elt_equal(struct otpd_queue_item *e1, ++ struct otpd_queue_item *e2) ++{ ++ if (e1 == NULL && e2 == NULL) ++ return; ++ assert_ptr_not_equal(e1, NULL); ++ assert_ptr_not_equal(e2, NULL); ++ assert_int_equal(e1->msgid, e2->msgid); ++} ++ ++static void test_single_insert() ++{ ++ struct otpd_queue q = { NULL }; ++ struct otpd_queue_item *ein, *eout; ++ ++ ein = new_elt(0); ++ otpd_queue_push(&q, ein); ++ ++ eout = otpd_queue_peek(&q); ++ assert_elt_equal(ein, eout); ++ ++ eout = otpd_queue_pop(&q); ++ assert_elt_equal(ein, eout); ++ free_elt(&eout); ++ ++ eout = otpd_queue_pop(&q); ++ assert_ptr_equal(eout, NULL); ++ ++ free_elts(&q); ++} ++ ++static void test_jump_insert() ++{ ++ struct otpd_queue q = { NULL }; ++ struct otpd_queue_item *echeck; ++ ++ for (int i = 0; i < 3; i++) { ++ struct otpd_queue_item *e = new_elt(i); ++ otpd_queue_push_head(&q, e); ++ ++ echeck = otpd_queue_peek(&q); ++ assert_elt_equal(e, echeck); ++ } ++ ++ free_elts(&q); ++} ++ ++static void test_garbage_insert() ++{ ++ struct otpd_queue q = { NULL }; ++ struct otpd_queue_item *e, *g; ++ ++ g = new_elt(0); ++ g->next = g; ++ otpd_queue_push(&q, g); ++ ++ e = otpd_queue_peek(&q); ++ assert_ptr_equal(e->next, NULL); ++ ++ free_elts(&q); ++} ++ ++static void test_removal() ++{ ++ struct otpd_queue q = { NULL }; ++ ++ for (int i = 0; i < 3; i++) { ++ struct otpd_queue_item *e = new_elt(i); ++ otpd_queue_push(&q, e); ++ } ++ for (int i = 0; i < 3; i++) { ++ struct otpd_queue_item *e = otpd_queue_pop(&q); ++ assert_ptr_not_equal(e, NULL); ++ assert_ptr_equal(e->next, NULL); ++ assert_int_equal(e->msgid, i); ++ free_elt(&e); ++ } ++} ++ ++static void pick_id(struct otpd_queue *q, int msgid) ++{ ++ struct otpd_queue_item *e; ++ ++ e = otpd_queue_pop_msgid(q, msgid); ++ assert_int_equal(e->msgid, msgid); ++ assert_ptr_equal(e->next, NULL); ++ free_elt(&e); ++ e = otpd_queue_pop_msgid(q, msgid); ++ assert_ptr_equal(e, NULL); ++} ++static void test_pick_removal() ++{ ++ struct otpd_queue q = { NULL }; ++ ++ for (int i = 0; i < 4; i++) { ++ struct otpd_queue_item *e = new_elt(i); ++ otpd_queue_push(&q, e); ++ } ++ ++ pick_id(&q, 0); /* first */ ++ pick_id(&q, 2); /* middle */ ++ pick_id(&q, 3); /* last */ ++ pick_id(&q, 1); /* singleton */ ++ ++ free_elts(&q); ++} ++ ++static void test_iter() ++{ ++ krb5_error_code ret; ++ struct otpd_queue q = { NULL }; ++ const struct otpd_queue *queues[3]; ++ struct otpd_queue_iter *iter = NULL; ++ const krad_packet *p = NULL; ++ ++ for (ptrdiff_t i = 1; i <= 3; i++) { ++ struct otpd_queue_item *e = new_elt(i); ++ e->req = (void *)i; ++ otpd_queue_push(&q, e); ++ } ++ ++ queues[0] = &q; ++ queues[1] = &q; ++ queues[2] = NULL; ++ ret = otpd_queue_iter_new(queues, &iter); ++ assert_int_equal(ret, 0); ++ assert_ptr_not_equal(iter, NULL); ++ ++ for (ptrdiff_t i = 0; i < 6; i++) { ++ p = otpd_queue_iter_func(iter, FALSE); ++ assert_ptr_equal(p, (void *) (i % 3 + 1)); ++ } ++ p = otpd_queue_iter_func(iter, FALSE); ++ assert_ptr_equal(p, NULL); ++ ++ free_elts(&q); ++} ++ ++int main(int argc, char *argv[]) ++{ ++ const struct CMUnitTest tests[] = { ++ cmocka_unit_test(test_single_insert), ++ cmocka_unit_test(test_jump_insert), ++ cmocka_unit_test(test_garbage_insert), ++ cmocka_unit_test(test_removal), ++ cmocka_unit_test(test_pick_removal), ++ cmocka_unit_test(test_iter), ++ }; ++ ++ return cmocka_run_group_tests(tests, NULL, NULL); ++} +-- +2.17.1 + diff --git a/SOURCES/0039-Fix-OTP-validation-in-FIPS-mode.patch b/SOURCES/0039-Fix-OTP-validation-in-FIPS-mode.patch deleted file mode 100644 index 0c0e1b7..0000000 --- a/SOURCES/0039-Fix-OTP-validation-in-FIPS-mode.patch +++ /dev/null @@ -1,93 +0,0 @@ -From 20ab0c731eea95327c8c2dc296461b612c6e98ae Mon Sep 17 00:00:00 2001 -From: Nathaniel McCallum -Date: Wed, 21 Feb 2018 23:39:55 -0500 -Subject: [PATCH] Fix OTP validation in FIPS mode - -NSS doesn't allow keys to be loaded directly in FIPS mode. To work around -this, we encrypt the input key using an ephemeral key and then unwrap the -encrypted key. - -https://pagure.io/freeipa/issue/7168 - -Reviewed-By: Rob Crittenden -Reviewed-By: Christian Heimes -Reviewed-By: Rob Crittenden ---- - daemons/ipa-slapi-plugins/libotp/hotp.c | 47 +++++++++++++++++++++++++++++++-- - 1 file changed, 45 insertions(+), 2 deletions(-) - -diff --git a/daemons/ipa-slapi-plugins/libotp/hotp.c b/daemons/ipa-slapi-plugins/libotp/hotp.c -index 619bc63ab1bee99d71c2f0fb887809762107c94c..0c9de96d37183e597867b736d6324db60fa1b3bb 100644 ---- a/daemons/ipa-slapi-plugins/libotp/hotp.c -+++ b/daemons/ipa-slapi-plugins/libotp/hotp.c -@@ -46,6 +46,7 @@ - #include - - #include -+#include - #include - #include - #include -@@ -66,6 +67,49 @@ static const struct { - { } - }; - -+static PK11SymKey * -+import_key(PK11SlotInfo *slot, CK_MECHANISM_TYPE mech, SECItem *key) -+{ -+ uint8_t ct[(key->len / AES_BLOCK_SIZE + 1) * AES_BLOCK_SIZE]; -+ uint8_t iv[AES_BLOCK_SIZE] = {}; -+ SECItem ivitem = { .data = iv, .len = sizeof(iv), .type = siBuffer }; -+ SECItem ctitem = { .data = ct, .len = sizeof(ct), .type = siBuffer }; -+ PK11SymKey *ekey = NULL; -+ PK11SymKey *skey = NULL; -+ -+ /* Try to import the key directly. */ -+ skey = PK11_ImportSymKey(slot, mech, PK11_OriginUnwrap, -+ CKA_SIGN, key, NULL); -+ if (skey) -+ return skey; -+ -+ /* If we get here, we are probably in FIPS mode. Let's encrypt the key so -+ * that we can unseal it instead of loading it directly. */ -+ -+ /* Generate an ephemeral key. */ -+ ekey = PK11_TokenKeyGenWithFlags(slot, CKM_AES_CBC_PAD, NULL, -+ AES_128_KEY_LENGTH, NULL, -+ CKF_ENCRYPT | CKF_UNWRAP, -+ PK11_ATTR_SESSION | -+ PK11_ATTR_PRIVATE | -+ PK11_ATTR_SENSITIVE, NULL); -+ if (!ekey) -+ goto egress; -+ -+ /* Encrypt the input key. */ -+ if (PK11_Encrypt(ekey, CKM_AES_CBC_PAD, &ivitem, ctitem.data, &ctitem.len, -+ ctitem.len, key->data, key->len) != SECSuccess) -+ goto egress; -+ -+ /* Unwrap the input key. */ -+ skey = PK11_UnwrapSymKey(ekey, CKM_AES_CBC_PAD, &ivitem, -+ &ctitem, mech, CKA_SIGN, key->len); -+ -+egress: -+ PK11_FreeSymKey(ekey); -+ return skey; -+} -+ - /* - * This code is mostly cargo-cult taken from here: - * http://www.mozilla.org/projects/security/pki/nss/tech-notes/tn5.html -@@ -90,8 +134,7 @@ static bool hmac(SECItem *key, CK_MECHANISM_TYPE mech, const SECItem *in, - } - } - -- symkey = PK11_ImportSymKey(slot, mech, PK11_OriginUnwrap, -- CKA_SIGN, key, NULL); -+ symkey = import_key(slot, mech, key); - if (symkey == NULL) - goto done; - --- -2.14.3 - diff --git a/SOURCES/0039-ipa-server-install-do-not-perform-forwarder-validati.patch b/SOURCES/0039-ipa-server-install-do-not-perform-forwarder-validati.patch new file mode 100644 index 0000000..37eef97 --- /dev/null +++ b/SOURCES/0039-ipa-server-install-do-not-perform-forwarder-validati.patch @@ -0,0 +1,34 @@ +From f387cbe6f9d1e21cb46c7b9752735bf6ded176b7 Mon Sep 17 00:00:00 2001 +From: Florence Blanc-Renaud +Date: Fri, 31 Aug 2018 10:09:15 +0200 +Subject: [PATCH] ipa-server-install: do not perform forwarder validation with + --no-dnssec-validation + +ipa-server-install is checking if the forwarder(s) specified with +--forwarder argument support DNSSEC. When the --no-dnssec-validation +option is added, the installer should not perform the check. + +Fixes: https://pagure.io/freeipa/issue/7666 +Reviewed-By: Tibor Dudlak +--- + ipaserver/install/dns.py | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/ipaserver/install/dns.py b/ipaserver/install/dns.py +index cac7a9213796d6618854b12da6c2a7fe60afdbf9..e4f73ac025dfe8aa19ef99c8d0ab9379caa32610 100644 +--- a/ipaserver/install/dns.py ++++ b/ipaserver/install/dns.py +@@ -293,8 +293,8 @@ def install_check(standalone, api, replica, options, hostname): + + # test DNSSEC forwarders + if options.forwarders: +- if (not bindinstance.check_forwarders(options.forwarders) +- and not options.no_dnssec_validation): ++ if not options.no_dnssec_validation \ ++ and not bindinstance.check_forwarders(options.forwarders): + options.no_dnssec_validation = True + print("WARNING: DNSSEC validation will be disabled") + +-- +2.17.1 + diff --git a/SOURCES/0040-Increase-the-default-token-key-size.patch b/SOURCES/0040-Increase-the-default-token-key-size.patch deleted file mode 100644 index 77a44f8..0000000 --- a/SOURCES/0040-Increase-the-default-token-key-size.patch +++ /dev/null @@ -1,34 +0,0 @@ -From ab2eaf607dd3746dd239595315dbaaebade06320 Mon Sep 17 00:00:00 2001 -From: Nathaniel McCallum -Date: Thu, 22 Feb 2018 14:04:10 -0500 -Subject: [PATCH] Increase the default token key size - -The previous default token key size would fail in FIPS mode for the sha384 -and sha512 algorithms. With the updated key size, the default will work in -all cases. - -https://pagure.io/freeipa/issue/7168 - -Reviewed-By: Rob Crittenden -Reviewed-By: Christian Heimes -Reviewed-By: Rob Crittenden ---- - ipaserver/plugins/otptoken.py | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/ipaserver/plugins/otptoken.py b/ipaserver/plugins/otptoken.py -index c66f0980f0fc2ed49b4224be40a18ce528a6da7b..a6e423f949659d8157c8471d0fbc3ee8a299ac98 100644 ---- a/ipaserver/plugins/otptoken.py -+++ b/ipaserver/plugins/otptoken.py -@@ -72,7 +72,7 @@ TOKEN_TYPES = { - } - - # NOTE: For maximum compatibility, KEY_LENGTH % 5 == 0 --KEY_LENGTH = 20 -+KEY_LENGTH = 35 - - class OTPTokenKey(Bytes): - """A binary password type specified in base32.""" --- -2.14.3 - diff --git a/SOURCES/0040-ipa-replica-install-fix-pkinit-setup.patch b/SOURCES/0040-ipa-replica-install-fix-pkinit-setup.patch new file mode 100644 index 0000000..8ef6481 --- /dev/null +++ b/SOURCES/0040-ipa-replica-install-fix-pkinit-setup.patch @@ -0,0 +1,46 @@ +From 7fe3cba3d4cbe62c23e0e74f6bf3c44c50feb985 Mon Sep 17 00:00:00 2001 +From: Florence Blanc-Renaud +Date: Tue, 4 Sep 2018 14:15:50 +0200 +Subject: [PATCH] ipa-replica-install: fix pkinit setup + +commit 7284097 (Delay enabling services until end of installer) +introduced a regression in replica installation. +When the replica requests a cert for PKINIT, a check is done +to ensure that the hostname corresponds to a machine with a +KDC service enabled (ipaconfigstring attribute of +cn=KDC,cn=,cn=masters,cn=ipa,cn=etc,$BASEDN must contain +'enabledService'). +With the commit mentioned above, the service is set to enabled only +at the end of the installation. + +The fix makes a less strict check, ensuring that 'enabledService' +or 'configuredService' is in ipaconfigstring. + +Fixes: https://pagure.io/freeipa/issue/7566 +Reviewed-By: Christian Heimes +Reviewed-By: Christian Heimes +--- + ipaserver/plugins/cert.py | 6 ++++-- + 1 file changed, 4 insertions(+), 2 deletions(-) + +diff --git a/ipaserver/plugins/cert.py b/ipaserver/plugins/cert.py +index 0663272c9b4fd73225f63fe52d8d31157d5cc690..ed78388c8b8b4688873a5b047fb1b67e417a8a6d 100644 +--- a/ipaserver/plugins/cert.py ++++ b/ipaserver/plugins/cert.py +@@ -300,9 +300,11 @@ def ca_kdc_check(api_instance, hostname): + + ipaconfigstring = {val.lower() for val in kdc_entry['ipaConfigString']} + +- if 'enabledservice' not in ipaconfigstring: ++ if 'enabledservice' not in ipaconfigstring \ ++ and 'configuredservice' not in ipaconfigstring: + raise errors.NotFound( +- reason=_("enabledService not in ipaConfigString kdc entry")) ++ reason=_("enabledService/configuredService not in " ++ "ipaConfigString kdc entry")) + + except errors.NotFound: + raise errors.ACIError( +-- +2.17.1 + diff --git a/SOURCES/0041-Revert-Don-t-allow-OTP-or-RADIUS-in-FIPS-mode.patch b/SOURCES/0041-Revert-Don-t-allow-OTP-or-RADIUS-in-FIPS-mode.patch deleted file mode 100644 index a8818c2..0000000 --- a/SOURCES/0041-Revert-Don-t-allow-OTP-or-RADIUS-in-FIPS-mode.patch +++ /dev/null @@ -1,86 +0,0 @@ -From 6d813f6b03811a285c3c6dae85942c0086b619a6 Mon Sep 17 00:00:00 2001 -From: Nathaniel McCallum -Date: Mon, 26 Feb 2018 09:48:22 -0500 -Subject: [PATCH] Revert "Don't allow OTP or RADIUS in FIPS mode" - -This reverts commit 16a952a0a44a0ebee97029ea1d2f6b7593dd2622. - -OTP now works in FIPS mode. RADIUS can be made to be compliant by wrapping -traffic in a VPN. - -https://pagure.io/freeipa/issue/7168 -https://pagure.io/freeipa/issue/7243 - -Reviewed-By: Rob Crittenden -Reviewed-By: Christian Heimes -Reviewed-By: Rob Crittenden ---- - ipaserver/plugins/baseuser.py | 3 --- - ipaserver/plugins/config.py | 16 ---------------- - 2 files changed, 19 deletions(-) - -diff --git a/ipaserver/plugins/baseuser.py b/ipaserver/plugins/baseuser.py -index bb8a73ded0fed135d5829ec0b0829a936f2196fb..bf24dbf542d3b481671dfe4e8cee14a2edcc26e0 100644 ---- a/ipaserver/plugins/baseuser.py -+++ b/ipaserver/plugins/baseuser.py -@@ -32,7 +32,6 @@ from .baseldap import ( - add_missing_object_class) - from ipaserver.plugins.service import ( - validate_certificate, validate_realm, normalize_principal) --from ipaserver.plugins.config import check_fips_auth_opts - from ipalib.request import context - from ipalib import _ - from ipalib.constants import PATTERN_GROUPUSER_NAME -@@ -478,7 +477,6 @@ class baseuser_add(LDAPCreate): - **options): - assert isinstance(dn, DN) - set_krbcanonicalname(entry_attrs) -- check_fips_auth_opts(fips_mode=self.api.env.fips_mode, **options) - self.obj.convert_usercertificate_pre(entry_attrs) - - def post_common_callback(self, ldap, dn, entry_attrs, *keys, **options): -@@ -602,7 +600,6 @@ class baseuser_mod(LDAPUpdate): - assert isinstance(dn, DN) - add_sshpubkey_to_attrs_pre(self.context, attrs_list) - -- check_fips_auth_opts(fips_mode=self.api.env.fips_mode, **options) - self.check_namelength(ldap, **options) - - self.check_mail(entry_attrs) -diff --git a/ipaserver/plugins/config.py b/ipaserver/plugins/config.py -index c9033fa8e7a2a0bfe77464fa4f9c62278bd814f6..ce15e6096f5b84dc45ee21d5aecc73ecf86eba07 100644 ---- a/ipaserver/plugins/config.py -+++ b/ipaserver/plugins/config.py -@@ -85,20 +85,6 @@ EXAMPLES: - - register = Registry() - -- --def check_fips_auth_opts(fips_mode, **options): -- """ -- OTP and RADIUS are not allowed in FIPS mode since they use MD5 -- checksums (OTP uses our RADIUS responder daemon ipa-otpd). -- """ -- if 'ipauserauthtype' in options and fips_mode: -- if ('otp' in options['ipauserauthtype'] or -- 'radius' in options['ipauserauthtype']): -- raise errors.InvocationError( -- 'OTP and RADIUS authentication in FIPS is ' -- 'not yet supported') -- -- - @register() - class config(LDAPObject): - """ -@@ -412,8 +398,6 @@ class config_mod(LDAPUpdate): - - def pre_callback(self, ldap, dn, entry_attrs, attrs_list, *keys, **options): - assert isinstance(dn, DN) -- check_fips_auth_opts(fips_mode=self.api.env.fips_mode, **options) -- - if 'ipadefaultprimarygroup' in entry_attrs: - group=entry_attrs['ipadefaultprimarygroup'] - try: --- -2.14.3 - diff --git a/SOURCES/0041-ipa-replica-install-properly-use-the-file-store.patch b/SOURCES/0041-ipa-replica-install-properly-use-the-file-store.patch new file mode 100644 index 0000000..b1e37b0 --- /dev/null +++ b/SOURCES/0041-ipa-replica-install-properly-use-the-file-store.patch @@ -0,0 +1,141 @@ +From d169b6fc759a7586c6b3372db7e81c7862b2f96e Mon Sep 17 00:00:00 2001 +From: Florence Blanc-Renaud +Date: Wed, 5 Sep 2018 17:36:16 +0200 +Subject: [PATCH] ipa-replica-install: properly use the file store + +In ipa-replica-install, many components use their own instance +of the FileStore to backup configuration files to the pre-install +state. This causes issues when the calls are mixed, like for +instance: +ds.do_task1_that_backups_file (using ds.filestore) +http.do_task2_that_backups_file (using http.filestore) +ds.do_task3_that_backups_file (using ds.filestore) + +because the list of files managed by ds.filestore does not include +the files managed by http.filestore, and the 3rd call would remove +any file added on 2nd call. + +The symptom of this bug is that ipa-replica-install does not save +/etc/httpd/conf.d/ssl.conf and subsequent uninstallation does not +restore the file, leading to a line referring to ipa-rewrite.conf +that prevents httpd startup. + +The installer should consistently use the same filestore. + +Fixes https://pagure.io/freeipa/issue/7684 + +Reviewed-By: Christian Heimes +Reviewed-By: Christian Heimes +--- + ipaserver/install/server/replicainstall.py | 31 +++++++++++++--------- + 1 file changed, 18 insertions(+), 13 deletions(-) + +diff --git a/ipaserver/install/server/replicainstall.py b/ipaserver/install/server/replicainstall.py +index 396d6089449225cc83aa28552a2009b9057e65ab..525a62c474c7429b7efee4853eb71e487e656bba 100644 +--- a/ipaserver/install/server/replicainstall.py ++++ b/ipaserver/install/server/replicainstall.py +@@ -79,7 +79,7 @@ def make_pkcs12_info(directory, cert_name, password_name): + + + def install_replica_ds(config, options, ca_is_configured, remote_api, +- ca_file, promote=False, pkcs12_info=None): ++ ca_file, promote=False, pkcs12_info=None, fstore=None): + dsinstance.check_ports() + + # if we have a pkcs12 file, create the cert db from +@@ -95,7 +95,8 @@ def install_replica_ds(config, options, ca_is_configured, remote_api, + ca_subject = installutils.default_ca_subject_dn(config.subject_base) + + ds = dsinstance.DsInstance( +- config_ldif=options.dirsrv_config_file) ++ config_ldif=options.dirsrv_config_file, ++ fstore=fstore) + ds.create_replica( + realm_name=config.realm_name, + master_fqdn=config.master_host_name, +@@ -115,8 +116,9 @@ def install_replica_ds(config, options, ca_is_configured, remote_api, + return ds + + +-def install_krb(config, setup_pkinit=False, pkcs12_info=None, promote=False): +- krb = krbinstance.KrbInstance() ++def install_krb(config, setup_pkinit=False, pkcs12_info=None, promote=False, ++ fstore=None): ++ krb = krbinstance.KrbInstance(fstore=fstore) + + # pkinit files + if pkcs12_info is None: +@@ -153,7 +155,8 @@ def install_ca_cert(ldap, base_dn, realm, cafile, destfile=paths.IPA_CA_CRT): + + def install_http(config, auto_redirect, ca_is_configured, ca_file, + promote=False, +- pkcs12_info=None): ++ pkcs12_info=None, ++ fstore=None): + # if we have a pkcs12 file, create the cert db from + # that. Otherwise the ds setup will create the CA + # cert +@@ -161,8 +164,7 @@ def install_http(config, auto_redirect, ca_is_configured, ca_file, + pkcs12_info = make_pkcs12_info(config.dir, "httpcert.p12", + "http_pin.txt") + +- +- http = httpinstance.HTTPInstance() ++ http = httpinstance.HTTPInstance(fstore=fstore) + http.create_instance( + config.realm_name, config.host_name, config.domain_name, + config.dirman_password, pkcs12_info, +@@ -173,14 +175,14 @@ def install_http(config, auto_redirect, ca_is_configured, ca_file, + return http + + +-def install_dns_records(config, options, remote_api): ++def install_dns_records(config, options, remote_api, fstore=None): + + if not bindinstance.dns_container_exists( + ipautil.realm_to_suffix(config.realm_name)): + return + + try: +- bind = bindinstance.BindInstance(api=remote_api) ++ bind = bindinstance.BindInstance(api=remote_api, fstore=fstore) + for ip in config.ips: + reverse_zone = bindinstance.find_reverse_zone(ip, remote_api) + +@@ -1425,10 +1427,11 @@ def install(installer): + remote_api, + ca_file=cafile, + promote=promote, +- pkcs12_info=dirsrv_pkcs12_info) ++ pkcs12_info=dirsrv_pkcs12_info, ++ fstore=fstore) + + # Always try to install DNS records +- install_dns_records(config, options, remote_api) ++ install_dns_records(config, options, remote_api, fstore=fstore) + + ntpinstance.ntp_ldap_enable(config.host_name, ds.suffix, + remote_api.env.realm) +@@ -1449,7 +1452,8 @@ def install(installer): + config, + setup_pkinit=not options.no_pkinit, + pkcs12_info=pkinit_pkcs12_info, +- promote=promote) ++ promote=promote, ++ fstore=fstore) + + if promote: + # We need to point to the master when certmonger asks for +@@ -1479,7 +1483,8 @@ def install(installer): + promote=promote, + pkcs12_info=http_pkcs12_info, + ca_is_configured=ca_enabled, +- ca_file=cafile) ++ ca_file=cafile, ++ fstore=fstore) + + if promote: + # Need to point back to ourself after the cert for HTTP is obtained +-- +2.17.1 + diff --git a/SOURCES/0042-Ensure-that-public-cert-and-CA-bundle-are-readable.patch b/SOURCES/0042-Ensure-that-public-cert-and-CA-bundle-are-readable.patch new file mode 100644 index 0000000..ce6f140 --- /dev/null +++ b/SOURCES/0042-Ensure-that-public-cert-and-CA-bundle-are-readable.patch @@ -0,0 +1,175 @@ +From e4084fc1608cd13e73ce201471ff518105f4b9f5 Mon Sep 17 00:00:00 2001 +From: Christian Heimes +Date: Fri, 22 Jun 2018 12:17:23 +0200 +Subject: [PATCH] Ensure that public cert and CA bundle are readable + +In CIS hardened mode, the process umask is 027. This results in some +files not being world readable. Ensure that write_certificate_list() +calls in client installer, server installer, and upgrader create cert +bundles with permission bits 0644. + +Fixes: https://pagure.io/freeipa/issue/7594 +Signed-off-by: Christian Heimes +Reviewed-By: Tibor Dudlak +Reviewed-By: Rob Crittenden +Reviewed-By: Rob Crittenden +--- + ipaclient/install/client.py | 10 +++++++--- + ipaclient/install/ipa_certupdate.py | 4 ++-- + ipalib/x509.py | 5 ++++- + ipaserver/install/cainstance.py | 2 +- + ipaserver/install/httpinstance.py | 2 +- + ipaserver/install/installutils.py | 6 +++++- + ipaserver/install/krbinstance.py | 2 +- + ipaserver/install/server/replicainstall.py | 2 +- + 8 files changed, 22 insertions(+), 11 deletions(-) + +diff --git a/ipaclient/install/client.py b/ipaclient/install/client.py +index 0fbe31b762561b3e2ee2f35a666a93de8857bced..a1c74b41f790917a302ab32f6be7a4d23a989708 100644 +--- a/ipaclient/install/client.py ++++ b/ipaclient/install/client.py +@@ -1837,7 +1837,7 @@ def get_ca_certs(fstore, options, server, basedn, realm): + + if ca_certs is not None: + try: +- x509.write_certificate_list(ca_certs, ca_file) ++ x509.write_certificate_list(ca_certs, ca_file, mode=0o644) + except Exception as e: + if os.path.exists(ca_file): + try: +@@ -2775,10 +2775,14 @@ def _install(options): + + x509.write_certificate_list( + [c for c, n, t, u in ca_certs if t is not False], +- paths.KDC_CA_BUNDLE_PEM) ++ paths.KDC_CA_BUNDLE_PEM, ++ mode=0o644 ++ ) + x509.write_certificate_list( + [c for c, n, t, u in ca_certs if t is not False], +- paths.CA_BUNDLE_PEM) ++ paths.CA_BUNDLE_PEM, ++ mode=0o644 ++ ) + + # Add the CA certificates to the IPA NSS database + logger.debug("Adding CA certificates to the IPA NSS database.") +diff --git a/ipaclient/install/ipa_certupdate.py b/ipaclient/install/ipa_certupdate.py +index 875cd7000c09aee044c5141a889617c9195182b1..878af250428fbf122c048d612fefc17e4ea674ff 100644 +--- a/ipaclient/install/ipa_certupdate.py ++++ b/ipaclient/install/ipa_certupdate.py +@@ -187,10 +187,10 @@ def update_server(certs): + update_file(paths.CACERT_PEM, certs) + + +-def update_file(filename, certs, mode=0o444): ++def update_file(filename, certs, mode=0o644): + certs = (c[0] for c in certs if c[2] is not False) + try: +- x509.write_certificate_list(certs, filename) ++ x509.write_certificate_list(certs, filename, mode=mode) + except Exception as e: + logger.error("failed to update %s: %s", filename, e) + +diff --git a/ipalib/x509.py b/ipalib/x509.py +index 67a9af4c5d3456f8cdd6d966373be75d7036f1b7..cfacfa6fc784a4bc1559d68da0cc505874346e22 100644 +--- a/ipalib/x509.py ++++ b/ipalib/x509.py +@@ -36,6 +36,7 @@ import datetime + import ipaddress + import ssl + import base64 ++import os + import re + + from cryptography import x509 as crypto_x509 +@@ -519,7 +520,7 @@ def write_certificate(cert, filename): + raise errors.FileError(reason=str(e)) + + +-def write_certificate_list(certs, filename): ++def write_certificate_list(certs, filename, mode=None): + """ + Write a list of certificates to a file in PEM format. + +@@ -529,6 +530,8 @@ def write_certificate_list(certs, filename): + + try: + with open(filename, 'wb') as f: ++ if mode is not None: ++ os.fchmod(f.fileno(), mode) + for cert in certs: + f.write(cert.public_bytes(Encoding.PEM)) + except (IOError, OSError) as e: +diff --git a/ipaserver/install/cainstance.py b/ipaserver/install/cainstance.py +index ffcebd719a16ebc5a991b35507b96411ad31eb10..d6e467097808594756d947fa721b8cf10fe7d043 100644 +--- a/ipaserver/install/cainstance.py ++++ b/ipaserver/install/cainstance.py +@@ -848,7 +848,7 @@ class CAInstance(DogtagInstance): + for path in [paths.IPA_CA_CRT, + paths.KDC_CA_BUNDLE_PEM, + paths.CA_BUNDLE_PEM]: +- x509.write_certificate_list(certlist, path) ++ x509.write_certificate_list(certlist, path, mode=0o644) + + def __request_ra_certificate(self): + """ +diff --git a/ipaserver/install/httpinstance.py b/ipaserver/install/httpinstance.py +index 3f83248dd89118aeecfbf458c5079dde8b2cb93d..05b88998353597aebc39b6dad5e1a688dca84f49 100644 +--- a/ipaserver/install/httpinstance.py ++++ b/ipaserver/install/httpinstance.py +@@ -481,7 +481,7 @@ class HTTPInstance(service.Service): + raise RuntimeError("HTTPD cert was issued by an unknown CA.") + # at this time we can assume any CA cert will be valid since this is + # only run during installation +- x509.write_certificate_list(certlist, paths.CA_CRT) ++ x509.write_certificate_list(certlist, paths.CA_CRT, mode=0o644) + + def is_kdcproxy_configured(self): + """Check if KDC proxy has already been configured in the past""" +diff --git a/ipaserver/install/installutils.py b/ipaserver/install/installutils.py +index 6614da69a0a046cdfa1b309f77972ead9de1279f..64beeffaebc4bcfe2b11dbf1109e54d7a9479d67 100644 +--- a/ipaserver/install/installutils.py ++++ b/ipaserver/install/installutils.py +@@ -1314,7 +1314,11 @@ def load_external_cert(files, ca_subject): + cert_file.flush() + + ca_file = tempfile.NamedTemporaryFile() +- x509.write_certificate_list(ca_cert_chain[1:], ca_file.name) ++ x509.write_certificate_list( ++ ca_cert_chain[1:], ++ ca_file.name, ++ mode=0o644 ++ ) + ca_file.flush() + + return cert_file, ca_file +diff --git a/ipaserver/install/krbinstance.py b/ipaserver/install/krbinstance.py +index 09cafb7b84623594fe88083f5b914cee0f050409..a3079bd6304a41116f9aa5e78b6c6c71d72d7aa6 100644 +--- a/ipaserver/install/krbinstance.py ++++ b/ipaserver/install/krbinstance.py +@@ -495,7 +495,7 @@ class KrbInstance(service.Service): + self.api.env.realm, + False) + ca_certs = [c for c, _n, t, _u in ca_certs if t is not False] +- x509.write_certificate_list(ca_certs, paths.CACERT_PEM) ++ x509.write_certificate_list(ca_certs, paths.CACERT_PEM, mode=0o644) + + def issue_selfsigned_pkinit_certs(self): + self._call_certmonger(certmonger_ca="SelfSign") +diff --git a/ipaserver/install/server/replicainstall.py b/ipaserver/install/server/replicainstall.py +index 525a62c474c7429b7efee4853eb71e487e656bba..eb354f81ba6e4cbc3848f9c24338fb85cc7639ae 100644 +--- a/ipaserver/install/server/replicainstall.py ++++ b/ipaserver/install/server/replicainstall.py +@@ -147,7 +147,7 @@ def install_ca_cert(ldap, base_dn, realm, cafile, destfile=paths.IPA_CA_CRT): + pass + else: + certs = [c[0] for c in certs if c[2] is not False] +- x509.write_certificate_list(certs, destfile) ++ x509.write_certificate_list(certs, destfile, mode=0o644) + except Exception as e: + raise ScriptError("error copying files: " + str(e)) + return destfile +-- +2.17.1 + diff --git a/SOURCES/0042-Log-errors-from-NSS-during-FIPS-OTP-key-import.patch b/SOURCES/0042-Log-errors-from-NSS-during-FIPS-OTP-key-import.patch deleted file mode 100644 index 4ee49e1..0000000 --- a/SOURCES/0042-Log-errors-from-NSS-during-FIPS-OTP-key-import.patch +++ /dev/null @@ -1,59 +0,0 @@ -From b9194a0292ce57418b3c9f5faf2ee5509f0fb749 Mon Sep 17 00:00:00 2001 -From: Robbie Harwood -Date: Thu, 1 Mar 2018 14:25:55 -0500 -Subject: [PATCH] Log errors from NSS during FIPS OTP key import - -Signed-off-by: Robbie Harwood -Reviewed-By: Christian Heimes -Reviewed-By: Petr Vobornik ---- - daemons/ipa-slapi-plugins/libotp/hotp.c | 16 ++++++++++++++-- - 1 file changed, 14 insertions(+), 2 deletions(-) - -diff --git a/daemons/ipa-slapi-plugins/libotp/hotp.c b/daemons/ipa-slapi-plugins/libotp/hotp.c -index 0c9de96d37183e597867b736d6324db60fa1b3bb..1b9110ebf89a705c9c670d4d33fc7ed615ad25f3 100644 ---- a/daemons/ipa-slapi-plugins/libotp/hotp.c -+++ b/daemons/ipa-slapi-plugins/libotp/hotp.c -@@ -49,7 +49,9 @@ - #include - #include - #include -+#include - #include -+#include - - struct digest_buffer { - uint8_t buf[SHA512_LENGTH]; -@@ -93,17 +95,27 @@ import_key(PK11SlotInfo *slot, CK_MECHANISM_TYPE mech, SECItem *key) - PK11_ATTR_SESSION | - PK11_ATTR_PRIVATE | - PK11_ATTR_SENSITIVE, NULL); -- if (!ekey) -+ if (!ekey) { -+ syslog(LOG_ERR, "libotp: in FIPS, PK11_TokenKeyGenWithFlags failed: %d", -+ PR_GetError()); - goto egress; -+ } - - /* Encrypt the input key. */ - if (PK11_Encrypt(ekey, CKM_AES_CBC_PAD, &ivitem, ctitem.data, &ctitem.len, -- ctitem.len, key->data, key->len) != SECSuccess) -+ ctitem.len, key->data, key->len) != SECSuccess) { -+ syslog(LOG_ERR, "libotp: in FIPS, PK11_Encrypt failed: %d", -+ PR_GetError()); - goto egress; -+ } - - /* Unwrap the input key. */ - skey = PK11_UnwrapSymKey(ekey, CKM_AES_CBC_PAD, &ivitem, - &ctitem, mech, CKA_SIGN, key->len); -+ if (!skey) { -+ syslog(LOG_ERR, "libotp: in FIPS, PK11_UnwrapSymKey failed: %d", -+ PR_GetError()); -+ } - - egress: - PK11_FreeSymKey(ekey); --- -2.14.3 - diff --git a/SOURCES/0043-Always-make-ipa.p11-kit-world-readable.patch b/SOURCES/0043-Always-make-ipa.p11-kit-world-readable.patch new file mode 100644 index 0000000..48c83c6 --- /dev/null +++ b/SOURCES/0043-Always-make-ipa.p11-kit-world-readable.patch @@ -0,0 +1,31 @@ +From 8ba1a1e89b34e587a9898a85f1c545dbd1c7765a Mon Sep 17 00:00:00 2001 +From: Christian Heimes +Date: Fri, 22 Jun 2018 12:22:06 +0200 +Subject: [PATCH] Always make ipa.p11-kit world-readable + +Ensure that ipa.p11-kit is always world-readable. + +Fixes: https://pagure.io/freeipa/issue/7594 +Signed-off-by: Christian Heimes +Reviewed-By: Tibor Dudlak +Reviewed-By: Rob Crittenden +Reviewed-By: Rob Crittenden +--- + ipaplatform/redhat/tasks.py | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/ipaplatform/redhat/tasks.py b/ipaplatform/redhat/tasks.py +index 6a4270defc9f444f76677bdf08d2a680649664bb..8fc8b54c146d540c988d97b7fb0927fced7c3e29 100644 +--- a/ipaplatform/redhat/tasks.py ++++ b/ipaplatform/redhat/tasks.py +@@ -269,6 +269,7 @@ class RedHatTaskNamespace(BaseTaskNamespace): + + try: + f = open(new_cacert_path, 'w') ++ os.fchmod(f.fileno(), 0o644) + except IOError as e: + logger.info("Failed to open %s: %s", new_cacert_path, e) + return False +-- +2.17.1 + diff --git a/SOURCES/0043-ipa-replica-install-make-sure-that-certmonger-picks-.patch b/SOURCES/0043-ipa-replica-install-make-sure-that-certmonger-picks-.patch deleted file mode 100644 index 0a475e2..0000000 --- a/SOURCES/0043-ipa-replica-install-make-sure-that-certmonger-picks-.patch +++ /dev/null @@ -1,111 +0,0 @@ -From 13d111faedfd5cbd0a7382e566edda7bd9ffc7ad Mon Sep 17 00:00:00 2001 -From: Florence Blanc-Renaud -Date: Wed, 14 Mar 2018 16:13:17 +0100 -Subject: [PATCH] ipa-replica-install: make sure that certmonger picks the - right master - -During ipa-replica-install, http installation first creates a service -principal for http/hostname (locally on the soon-to-be-replica), then -waits for this entry to be replicated on the master picked for the -install. -In a later step, the installer requests a certificate for HTTPd. The local -certmonger first tries the master defined in xmlrpc_uri (which is -pointing to the soon-to-be-replica), but fails because the service is not -up yet. Then certmonger tries to find a master by using the DNS and looking -for a ldap service. This step can pick a different master, where the -principal entry has not always be replicated yet. -As the certificate request adds the principal if it does not exist, we can -end by re-creating the principal and have a replication conflict. - -The replication conflict later causes kerberos issues, preventing -from installing a new replica. - -The proposed fix forces xmlrpc_uri to point to the same master as the one -picked for the installation, in order to make sure that the master already -contains the principal entry. - -https://pagure.io/freeipa/issue/7041 - -Reviewed-By: Rob Crittenden -Reviewed-By: Rob Crittenden ---- - ipaserver/install/server/replicainstall.py | 42 +++++++++++++++++++++++++++--- - 1 file changed, 39 insertions(+), 3 deletions(-) - -diff --git a/ipaserver/install/server/replicainstall.py b/ipaserver/install/server/replicainstall.py -index 6aa1157133423e854514de61a69810433e436d2f..5a37aea0ac913d5c9cb88346345ba5760a9e923d 100644 ---- a/ipaserver/install/server/replicainstall.py -+++ b/ipaserver/install/server/replicainstall.py -@@ -194,7 +194,16 @@ def install_dns_records(config, options, remote_api): - 'on master: %s', str(e)) - - --def create_ipa_conf(fstore, config, ca_enabled): -+def create_ipa_conf(fstore, config, ca_enabled, master=None): -+ """ -+ Create /etc/ipa/default.conf master configuration -+ :param fstore: sysrestore file store used for backup and restore of -+ the server configuration -+ :param config: replica config -+ :param ca_enabled: True if the topology includes a CA -+ :param master: if set, the xmlrpc_uri parameter will use the provided -+ master instead of this host -+ """ - # Save client file on Domain Level 1 - target_fname = paths.IPA_DEFAULT_CONF - fstore.backup_file(target_fname) -@@ -203,8 +212,12 @@ def create_ipa_conf(fstore, config, ca_enabled): - ipaconf.setOptionAssignment(" = ") - ipaconf.setSectionNameDelimiters(("[", "]")) - -- xmlrpc_uri = 'https://{0}/ipa/xml'.format( -- ipautil.format_netloc(config.host_name)) -+ if master: -+ xmlrpc_uri = 'https://{0}/ipa/xml'.format( -+ ipautil.format_netloc(master)) -+ else: -+ xmlrpc_uri = 'https://{0}/ipa/xml'.format( -+ ipautil.format_netloc(config.host_name)) - ldapi_uri = 'ldapi://%2fvar%2frun%2fslapd-{0}.socket\n'.format( - installutils.realm_to_serverid(config.realm_name)) - -@@ -1431,6 +1444,25 @@ def install(installer): - # we now need to enable ssl on the ds - ds.enable_ssl() - -+ if promote: -+ # We need to point to the master when certmonger asks for -+ # HTTP certificate. -+ # During http installation, the HTTP/hostname principal is created -+ # locally then the installer waits for the entry to appear on the -+ # master selected for the installation. -+ # In a later step, the installer requests a SSL certificate through -+ # Certmonger (and the op adds the principal if it does not exist yet). -+ # If xmlrpc_uri points to the soon-to-be replica, -+ # the httpd service is not ready yet to handle certmonger requests -+ # and certmonger tries to find another master. The master can be -+ # different from the one selected for the installation, and it is -+ # possible that the principal has not been replicated yet. This -+ # may lead to a replication conflict. -+ # This is why we need to force the use of the same master by -+ # setting xmlrpc_uri -+ create_ipa_conf(fstore, config, ca_enabled, -+ master=config.master_host_name) -+ - install_http( - config, - auto_redirect=not options.no_ui_redirect, -@@ -1439,6 +1471,10 @@ def install(installer): - ca_is_configured=ca_enabled, - ca_file=cafile) - -+ if promote: -+ # Need to point back to ourself after the cert for HTTP is obtained -+ create_ipa_conf(fstore, config, ca_enabled) -+ - otpd = otpdinstance.OtpdInstance() - otpd.create_instance('OTPD', config.host_name, - ipautil.realm_to_suffix(config.realm_name)) --- -2.14.3 - diff --git a/SOURCES/0044-Make-etc-httpd-alias-world-readable-executable.patch b/SOURCES/0044-Make-etc-httpd-alias-world-readable-executable.patch new file mode 100644 index 0000000..f7b0c12 --- /dev/null +++ b/SOURCES/0044-Make-etc-httpd-alias-world-readable-executable.patch @@ -0,0 +1,37 @@ +From 9bb9255161eef8da54842c0a6aeb1ddb0b20c0df Mon Sep 17 00:00:00 2001 +From: Christian Heimes +Date: Fri, 22 Jun 2018 12:25:33 +0200 +Subject: [PATCH] Make /etc/httpd/alias world readable & executable + +The directory /etc/httpd/alias contains public key material. It must be +world readable and executable, so any client can read public certs. + +Note: executable for a directory means, that a process is allowed to +traverse into the directory. + +Fixes: https://pagure.io/freeipa/issue/7594 +Signed-off-by: Christian Heimes +Reviewed-By: Tibor Dudlak +Reviewed-By: Rob Crittenden +Reviewed-By: Rob Crittenden +--- + ipaserver/install/httpinstance.py | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/ipaserver/install/httpinstance.py b/ipaserver/install/httpinstance.py +index 05b88998353597aebc39b6dad5e1a688dca84f49..3f8b18c4e8412c1767b6ad541da18d8b30ad59f7 100644 +--- a/ipaserver/install/httpinstance.py ++++ b/ipaserver/install/httpinstance.py +@@ -217,6 +217,9 @@ class HTTPInstance(service.Service): + self.update_httpd_service_ipa_conf() + self.update_httpd_wsgi_conf() + ++ # Must be world-readable / executable ++ os.chmod(paths.HTTPD_ALIAS_DIR, 0o755) ++ + target_fname = paths.HTTPD_IPA_CONF + http_txt = ipautil.template_file( + os.path.join(paths.USR_SHARE_IPA_DIR, "ipa.conf"), self.sub_dict) +-- +2.17.1 + diff --git a/SOURCES/0044-replica-install-pass-ip-address-to-client-install.patch b/SOURCES/0044-replica-install-pass-ip-address-to-client-install.patch deleted file mode 100644 index cdb3af3..0000000 --- a/SOURCES/0044-replica-install-pass-ip-address-to-client-install.patch +++ /dev/null @@ -1,38 +0,0 @@ -From d1506d6a44b4c4b85772cd0764113f2b20a147fe Mon Sep 17 00:00:00 2001 -From: Stanislav Laznicka -Date: Fri, 6 Apr 2018 09:10:20 +0200 -Subject: [PATCH] replica-install: pass --ip-address to client install - -In replica DL1 installation, the --ip-address option was not passed -down to the ipa-client-install script (when not promoting client). -This resulted in creating DNS records for all of the host's interface -IP adresses instead of just those specified. - -This patch passes all the --ip-address options down to the client -installation script. - -https://pagure.io/freeipa/issue/7405 - -Reviewed-By: Stanislav Laznicka ---- - ipaserver/install/server/replicainstall.py | 4 ++++ - 1 file changed, 4 insertions(+) - -diff --git a/ipaserver/install/server/replicainstall.py b/ipaserver/install/server/replicainstall.py -index 5a37aea0ac913d5c9cb88346345ba5760a9e923d..42e4615ad2dc1f604f5d8d14f8e57e3e4674bcb9 100644 ---- a/ipaserver/install/server/replicainstall.py -+++ b/ipaserver/install/server/replicainstall.py -@@ -942,6 +942,10 @@ def ensure_enrolled(installer): - args.append("--mkhomedir") - if installer.force_join: - args.append("--force-join") -+ if installer.ip_addresses: -+ for ip in installer.ip_addresses: -+ # installer.ip_addresses is of type [CheckedIPAddress] -+ args.extend(("--ip-address", str(ip))) - - try: - # Call client install script --- -2.14.3 - diff --git a/SOURCES/0045-Add-nsds5ReplicaReleaseTimeout-to-replica-config.patch b/SOURCES/0045-Add-nsds5ReplicaReleaseTimeout-to-replica-config.patch deleted file mode 100644 index 7dea73f..0000000 --- a/SOURCES/0045-Add-nsds5ReplicaReleaseTimeout-to-replica-config.patch +++ /dev/null @@ -1,107 +0,0 @@ -From 7076fd22e744fd51dbcc0c4e4a4089884a3dae48 Mon Sep 17 00:00:00 2001 -From: Christian Heimes -Date: Wed, 11 Apr 2018 13:34:41 +0200 -Subject: [PATCH] Add nsds5ReplicaReleaseTimeout to replica config - -The nsds5ReplicaReleaseTimeout setting prevents the monopolization of -replicas during initial or busy master-master replication. 389-DS -documentation suggets a timeout of 60 seconds to improve convergence of -replicas. - -See: http://directory.fedoraproject.org/docs/389ds/design/repl-conv-design.html -Fixes: https://pagure.io/freeipa/issue/7488 -Signed-off-by: Christian Heimes -Reviewed-By: Florence Blanc-Renaud -Reviewed-By: Fraser Tweedale -Reviewed-By: Florence Blanc-Renaud ---- - ipaserver/install/replication.py | 21 ++++++++++++++++----- - ipaserver/install/server/upgrade.py | 17 +++++++++++++++++ - 2 files changed, 33 insertions(+), 5 deletions(-) - -diff --git a/ipaserver/install/replication.py b/ipaserver/install/replication.py -index 3cd871e5df44398fd994504a3807a5648af5e048..e0055b792ce5b51fd411a7ea1f4316bb017984ba 100644 ---- a/ipaserver/install/replication.py -+++ b/ipaserver/install/replication.py -@@ -468,20 +468,30 @@ class ReplicationManager(object): - - try: - entry = conn.get_entry(dn) -+ except errors.NotFound: -+ pass -+ else: - managers = {DN(m) for m in entry.get('nsDS5ReplicaBindDN', [])} - -+ mods = [] - if replica_binddn not in managers: - # Add the new replication manager -- mod = [(ldap.MOD_ADD, 'nsDS5ReplicaBindDN', -- replica_binddn)] -- conn.modify_s(dn, mod) -+ mods.append( -+ (ldap.MOD_ADD, 'nsDS5ReplicaBindDN', replica_binddn) -+ ) -+ if 'nsds5replicareleasetimeout' not in entry: -+ # See https://pagure.io/freeipa/issue/7488 -+ mods.append( -+ (ldap.MOD_ADD, 'nsds5replicareleasetimeout', ['60']) -+ ) -+ -+ if mods: -+ conn.modify_s(dn, mods) - - self.set_replica_binddngroup(conn, entry) - - # replication is already configured - return -- except errors.NotFound: -- pass - - replica_type = self.get_replica_type() - -@@ -496,6 +506,7 @@ class ReplicationManager(object): - nsds5replicabinddn=[replica_binddn], - nsds5replicabinddngroup=[self.repl_man_group_dn], - nsds5replicabinddngroupcheckinterval=["60"], -+ nsds5replicareleasetimeout=["60"], - nsds5replicalegacyconsumer=["off"], - ) - conn.add_entry(entry) -diff --git a/ipaserver/install/server/upgrade.py b/ipaserver/install/server/upgrade.py -index 62665d56ca0fb2c632f8ba135f8b6191a18b6aa1..62a75510ad331923f468c28908ea407789ec380c 100644 ---- a/ipaserver/install/server/upgrade.py -+++ b/ipaserver/install/server/upgrade.py -@@ -1565,6 +1565,19 @@ def disable_httpd_system_trust(http): - db.add_cert(cert, nickname, trust_flags) - - -+def update_replica_config(db_suffix): -+ dn = DN( -+ ('cn', 'replica'), ('cn', db_suffix), ('cn', 'mapping tree'), -+ ('cn', 'config') -+ ) -+ entry = api.Backend.ldap2.get_entry(dn) -+ if 'nsds5replicareleasetimeout' not in entry: -+ # See https://pagure.io/freeipa/issue/7488 -+ root_logger.info("Adding nsds5replicaReleaseTimeout=60 to %s", dn) -+ entry['nsds5replicareleasetimeout'] = '60' -+ api.Backend.ldap2.update_entry(entry) -+ -+ - def upgrade_configuration(): - """ - Execute configuration upgrade of the IPA services -@@ -1681,6 +1694,10 @@ def upgrade_configuration(): - - ds.configure_dirsrv_ccache() - -+ update_replica_config(ipautil.realm_to_suffix(api.env.realm)) -+ if ca.is_configured(): -+ update_replica_config(DN(('o', 'ipaca'))) -+ - ntpinstance.ntp_ldap_enable(api.env.host, api.env.basedn, api.env.realm) - - ds.stop(ds_serverid) --- -2.14.3 - diff --git a/SOURCES/0045-Fix-permission-of-public-files-in-upgrader.patch b/SOURCES/0045-Fix-permission-of-public-files-in-upgrader.patch new file mode 100644 index 0000000..0b81a5c --- /dev/null +++ b/SOURCES/0045-Fix-permission-of-public-files-in-upgrader.patch @@ -0,0 +1,82 @@ +From 19bfd7c36d6d087f0cd7def5eb4d8850c395fb4b Mon Sep 17 00:00:00 2001 +From: Christian Heimes +Date: Fri, 22 Jun 2018 12:53:19 +0200 +Subject: [PATCH] Fix permission of public files in upgrader + +Make CA bundles, certs, and cert directories world-accessible in +upgrader. + +Fixes: https://pagure.io/freeipa/issue/7594 +Signed-off-by: Christian Heimes +Reviewed-By: Tibor Dudlak +Reviewed-By: Rob Crittenden +Reviewed-By: Rob Crittenden +--- + ipaserver/install/server/upgrade.py | 31 +++++++++++++++++++++++++++++ + 1 file changed, 31 insertions(+) + +diff --git a/ipaserver/install/server/upgrade.py b/ipaserver/install/server/upgrade.py +index 4e5096e598cd10e3bd98f91946b4d26377d0de6e..7faaacd5d2f0c39bcf744c288b283009ccb3ead5 100644 +--- a/ipaserver/install/server/upgrade.py ++++ b/ipaserver/install/server/upgrade.py +@@ -4,12 +4,14 @@ + + from __future__ import print_function, absolute_import + ++import errno + import logging + import re + import os + import shutil + import pwd + import fileinput ++import stat + import sys + import tempfile + from contextlib import contextmanager +@@ -1656,6 +1658,34 @@ def update_replica_config(db_suffix): + logger.info("Updated entry %s", dn) + + ++def fix_permissions(): ++ """Fix permission of public accessible files and directories ++ ++ In case IPA was installed with restricted umask, some public files and ++ directories may not be readable and accessible. ++ ++ See https://pagure.io/freeipa/issue/7594 ++ """ ++ candidates = [ ++ paths.HTTPD_ALIAS_DIR, ++ paths.CA_BUNDLE_PEM, ++ paths.KDC_CA_BUNDLE_PEM, ++ paths.IPA_CA_CRT, ++ paths.IPA_P11_KIT, ++ ] ++ for filename in candidates: ++ try: ++ s = os.stat(filename) ++ except OSError as e: ++ if e.errno != errno.ENOENT: ++ raise ++ continue ++ mode = 0o755 if stat.S_ISDIR(s.st_mode) else 0o644 ++ if mode != stat.S_IMODE(s.st_mode): ++ logger.debug("Fix permission of %s to %o", filename, mode) ++ os.chmod(filename, mode) ++ ++ + def upgrade_configuration(): + """ + Execute configuration upgrade of the IPA services +@@ -1677,6 +1707,7 @@ def upgrade_configuration(): + ds.start(ds_serverid) + + check_certs() ++ fix_permissions() + + auto_redirect = find_autoredirect(fqdn) + sub_dict = dict( +-- +2.17.1 + diff --git a/SOURCES/0046-Fix-upgrade-update_replica_config-in-single-master-m.patch b/SOURCES/0046-Fix-upgrade-update_replica_config-in-single-master-m.patch deleted file mode 100644 index 78b5617..0000000 --- a/SOURCES/0046-Fix-upgrade-update_replica_config-in-single-master-m.patch +++ /dev/null @@ -1,39 +0,0 @@ -From 6d868b2dd17ada072553c8d7440ee6fbd95a052a Mon Sep 17 00:00:00 2001 -From: Fraser Tweedale -Date: Mon, 16 Apr 2018 16:02:03 +1000 -Subject: [PATCH] Fix upgrade (update_replica_config) in single master mode - -Commit afc0d4b62d043cd568ce87400f60e8fa8273495f added an upgrade -step that add an attribute to a replica config entry. The entry -only exists after a replica has been added, so upgrade was broken -for standalone server. Catch and suppress the NotFound error. - -Related to: https://pagure.io/freeipa/issue/7488 - -Reviewed-By: Christian Heimes -Reviewed-By: Fraser Tweedale -Reviewed-By: Florence Blanc-Renaud ---- - ipaserver/install/server/upgrade.py | 6 +++++- - 1 file changed, 5 insertions(+), 1 deletion(-) - -diff --git a/ipaserver/install/server/upgrade.py b/ipaserver/install/server/upgrade.py -index 62a75510ad331923f468c28908ea407789ec380c..c55242a4af990c3218d8451fac7b082066a23be3 100644 ---- a/ipaserver/install/server/upgrade.py -+++ b/ipaserver/install/server/upgrade.py -@@ -1570,7 +1570,11 @@ def update_replica_config(db_suffix): - ('cn', 'replica'), ('cn', db_suffix), ('cn', 'mapping tree'), - ('cn', 'config') - ) -- entry = api.Backend.ldap2.get_entry(dn) -+ try: -+ entry = api.Backend.ldap2.get_entry(dn) -+ except ipalib.errors.NotFound: -+ return # entry does not exist until a replica is installed -+ - if 'nsds5replicareleasetimeout' not in entry: - # See https://pagure.io/freeipa/issue/7488 - root_logger.info("Adding nsds5replicaReleaseTimeout=60 to %s", dn) --- -2.14.3 - diff --git a/SOURCES/0047-Use-single-Custodia-instance-in-installers.patch b/SOURCES/0047-Use-single-Custodia-instance-in-installers.patch deleted file mode 100644 index 6ff3f36..0000000 --- a/SOURCES/0047-Use-single-Custodia-instance-in-installers.patch +++ /dev/null @@ -1,620 +0,0 @@ -From 11e597217fe6f1dcee3a918eef0bc3a0d2928e6d Mon Sep 17 00:00:00 2001 -From: Christian Heimes -Date: Thu, 26 Apr 2018 12:06:36 +0200 -Subject: [PATCH] Use single Custodia instance in installers - -Installers now pass a single CustodiaInstance object around, instead of -creating new instances on demand. In case of replica promotion with CA, -the instance gets all secrets from a master with CA present. Before, an -installer created multiple instances and may have requested CA key -material from a different machine than DM password hash. - -In case of Domain Level 1 and replica promotion, the CustodiaInstance no -longer adds the keys to the local instance and waits for replication to -other replica. Instead the installer directly uploads the new public -keys to the remote 389-DS instance. - -Without promotion, new Custodia public keys are still added to local -389-DS over LDAPI. - -Fixes: https://pagure.io/freeipa/issue/7518 -Signed-off-by: Christian Heimes -Reviewed-By: Simo Sorce -Reviewed-By: Simo Sorce ---- - install/tools/ipa-ca-install | 13 ++- - ipaserver/install/ca.py | 26 +++-- - ipaserver/install/cainstance.py | 9 +- - ipaserver/install/custodiainstance.py | 150 ++++++++++++++++++++++------- - ipaserver/install/ipa_kra_install.py | 10 +- - ipaserver/install/kra.py | 6 +- - ipaserver/install/server/install.py | 14 +-- - ipaserver/install/server/replicainstall.py | 15 +-- - 8 files changed, 165 insertions(+), 78 deletions(-) - -diff --git a/install/tools/ipa-ca-install b/install/tools/ipa-ca-install -index f84b4749a3e2a80aca002a2aa057b200e6187f18..a3694007e5815d1c44f642057c749879d336dfc5 100755 ---- a/install/tools/ipa-ca-install -+++ b/install/tools/ipa-ca-install -@@ -31,6 +31,7 @@ from ipaserver.install.installutils import create_replica_config - from ipaserver.install.installutils import check_creds, ReplicaConfig - from ipaserver.install import dsinstance, ca - from ipaserver.install import cainstance, service -+from ipaserver.install import custodiainstance - from ipapython import version - from ipalib import api - from ipalib.constants import DOMAIN_LEVEL_0 -@@ -193,13 +194,17 @@ def install_replica(safe_options, options, filename): - options.domain_name = config.domain_name - options.dm_password = config.dirman_password - options.host_name = config.host_name -+ options.ca_host_name = config.ca_host_name - if os.path.exists(cafile): - options.ca_cert_file = cafile - else: - options.ca_cert_file = None - - ca.install_check(True, config, options) -- ca.install(True, config, options) -+ -+ custodia = custodiainstance.get_custodia_instance( -+ options, custodiainstance.CustodiaModes.CA_PEER) -+ ca.install(True, config, options, custodia=custodia) - - - def install_master(safe_options, options): -@@ -228,7 +233,11 @@ def install_master(safe_options, options): - sys.exit("CA subject: {}".format(e.message)) - - ca.install_check(True, None, options) -- ca.install(True, None, options) -+ -+ # No CA peer available yet. -+ custodia = custodiainstance.get_custodia_instance( -+ options, custodiainstance.CustodiaModes.STANDALONE) -+ ca.install(True, None, options, custodia=custodia) - - - def install(safe_options, options, filename): -diff --git a/ipaserver/install/ca.py b/ipaserver/install/ca.py -index 52cb20f1cb3612394544a6a41f10e9e939bc0657..2ac4382ccd24d217832950b94baddd113f7a8930 100644 ---- a/ipaserver/install/ca.py -+++ b/ipaserver/install/ca.py -@@ -19,10 +19,7 @@ from ipalib.install.service import enroll_only, master_install_only, replica_ins - from ipaserver.install import sysupgrade - from ipapython.install import typing - from ipapython.install.core import group, knob, extend_knob --from ipaserver.install import (cainstance, -- custodiainstance, -- dsinstance, -- bindinstance) -+from ipaserver.install import cainstance, bindinstance, dsinstance - from ipapython import ipautil, certdb - from ipapython.admintool import ScriptError - from ipaplatform import services -@@ -201,12 +198,12 @@ def install_check(standalone, replica_config, options): - "cannot continue." % (subject, db.secdir)) - - --def install(standalone, replica_config, options): -- install_step_0(standalone, replica_config, options) -- install_step_1(standalone, replica_config, options) -+def install(standalone, replica_config, options, custodia): -+ install_step_0(standalone, replica_config, options, custodia=custodia) -+ install_step_1(standalone, replica_config, options, custodia=custodia) - - --def install_step_0(standalone, replica_config, options): -+def install_step_0(standalone, replica_config, options, custodia): - realm_name = options.realm_name - dm_password = options.dm_password - host_name = options.host_name -@@ -237,9 +234,6 @@ def install_step_0(standalone, replica_config, options): - else: - cafile = os.path.join(replica_config.dir, 'cacert.p12') - if options.promote: -- custodia = custodiainstance.CustodiaInstance( -- replica_config.host_name, -- replica_config.realm_name) - custodia.get_ca_keys( - replica_config.ca_host_name, - cafile, -@@ -266,7 +260,9 @@ def install_step_0(standalone, replica_config, options): - 'certmap.conf', 'subject_base', str(subject_base)) - dsinstance.write_certmap_conf(realm_name, ca_subject) - -- ca = cainstance.CAInstance(realm_name, host_name=host_name) -+ ca = cainstance.CAInstance( -+ realm=realm_name, host_name=host_name, custodia=custodia -+ ) - ca.configure_instance(host_name, dm_password, dm_password, - subject_base=subject_base, - ca_subject=ca_subject, -@@ -284,7 +280,7 @@ def install_step_0(standalone, replica_config, options): - use_ldaps=standalone) - - --def install_step_1(standalone, replica_config, options): -+def install_step_1(standalone, replica_config, options, custodia): - if replica_config is not None and not replica_config.setup_ca: - return - -@@ -293,7 +289,9 @@ def install_step_1(standalone, replica_config, options): - subject_base = options._subject_base - basedn = ipautil.realm_to_suffix(realm_name) - -- ca = cainstance.CAInstance(realm_name, host_name=host_name) -+ ca = cainstance.CAInstance( -+ realm=realm_name, host_name=host_name, custodia=custodia -+ ) - - ca.stop('pki-tomcat') - -diff --git a/ipaserver/install/cainstance.py b/ipaserver/install/cainstance.py -index 20635eae22268ff72de73b8b9c430050114bb45b..eefc30b6e01dcf744703b8607cbe169fbec5d859 100644 ---- a/ipaserver/install/cainstance.py -+++ b/ipaserver/install/cainstance.py -@@ -59,7 +59,6 @@ from ipapython.ipa_log_manager import log_mgr,\ - from ipaserver.secrets.kem import IPAKEMKeys - - from ipaserver.install import certs --from ipaserver.install import custodiainstance - from ipaserver.install import dsinstance - from ipaserver.install import installutils - from ipaserver.install import ldapupdate -@@ -285,7 +284,7 @@ class CAInstance(DogtagInstance): - 'caSigningCert cert-pki-ca') - server_cert_name = 'Server-Cert cert-pki-ca' - -- def __init__(self, realm=None, host_name=None): -+ def __init__(self, realm=None, host_name=None, custodia=None): - super(CAInstance, self).__init__( - realm=realm, - subsystem="CA", -@@ -310,6 +309,8 @@ class CAInstance(DogtagInstance): - self.no_db_setup = False - self.keytab = os.path.join( - paths.PKI_TOMCAT, self.service_prefix + '.keytab') -+ # Custodia instance for RA key retrieval -+ self._custodia = custodia - - def configure_instance(self, host_name, dm_password, admin_password, - pkcs12_info=None, master_host=None, csr_file=None, -@@ -711,9 +712,7 @@ class CAInstance(DogtagInstance): - self.configure_agent_renewal() - - def __import_ra_key(self): -- custodia = custodiainstance.CustodiaInstance(host_name=self.fqdn, -- realm=self.realm) -- custodia.import_ra_key(self.master_host) -+ self._custodia.import_ra_key(self.master_host) - self.__set_ra_cert_perms() - - self.configure_agent_renewal() -diff --git a/ipaserver/install/custodiainstance.py b/ipaserver/install/custodiainstance.py -index 0a90bb3954486b9773e3553e9981d2a8d0d4e44a..262ae5857c64719ff5cebde31b4b107e1245d458 100644 ---- a/ipaserver/install/custodiainstance.py -+++ b/ipaserver/install/custodiainstance.py -@@ -1,5 +1,9 @@ - # Copyright (C) 2015 FreeIPa Project Contributors, see 'COPYING' for license. - -+from __future__ import print_function, absolute_import -+ -+import enum -+ - from ipaserver.secrets.kem import IPAKEMKeys, KEMLdap - from ipaserver.secrets.client import CustodiaClient - from ipaplatform.paths import paths -@@ -21,12 +25,84 @@ import time - import pwd - - -+class CustodiaModes(enum.Enum): -+ # peer must have a CA -+ CA_PEER = 'Custodia CA peer' -+ # peer must have a CA, KRA preferred -+ KRA_PEER = 'Custodia KRA peer' -+ # any master will do -+ MASTER_PEER = 'Custodia master peer' -+ # standalone / local instance -+ STANDALONE = 'Custodia standalone' -+ -+ -+def get_custodia_instance(config, mode): -+ """Create Custodia instance -+ -+ :param config: configuration/installer object -+ :param mode: CustodiaModes member -+ :return: CustodiaInstance object -+ -+ The config object must have the following attribute -+ -+ *host_name* -+ FQDN of the new replica/master -+ *realm_name* -+ Kerberos realm -+ *promote* -+ True, when instance will be promoted from client to replica -+ *master_host_name* (for *CustodiaModes.MASTER_PEER*) -+ hostname of a master (may not have a CA) -+ *ca_host_name* (for *CustodiaModes.CA_PEER*) -+ hostname of a master with CA -+ *kra_host_name* (for *CustodiaModes.KRA_PEER*) -+ hostname of a master with KRA or CA -+ -+ For promotion, the instance will upload new keys and retrieve secrets -+ from the same host. Therefore it uses *ca_host_name* instead of -+ *master_host_name* to create a replica with CA. -+ """ -+ assert isinstance(mode, CustodiaModes) -+ root_logger.info( -+ "Custodia client for '%r' with promotion %s.", -+ mode, 'yes' if config.promote else 'no' -+ ) -+ if config.promote: -+ if mode == CustodiaModes.CA_PEER: -+ # In case we install replica with CA, prefer CA host as source for -+ # all Custodia secret material. -+ custodia_master = config.ca_host_name -+ elif mode == CustodiaModes.KRA_PEER: -+ custodia_master = config.kra_host_name -+ elif mode == CustodiaModes.MASTER_PEER: -+ custodia_master = config.master_host_name -+ elif mode == CustodiaModes.STANDALONE: -+ custodia_master = None -+ else: -+ custodia_master = None -+ -+ if custodia_master is None: -+ # use ldapi with local dirsrv instance -+ root_logger.info("Custodia uses LDAPI.") -+ ldap_uri = None -+ else: -+ root_logger.info("Custodia uses '%s' as master peer.", -+ custodia_master) -+ ldap_uri = 'ldap://{}'.format(custodia_master) -+ -+ return CustodiaInstance( -+ host_name=config.host_name, -+ realm=config.realm_name, -+ ldap_uri=ldap_uri -+ ) -+ -+ - class CustodiaInstance(SimpleServiceInstance): -- def __init__(self, host_name=None, realm=None): -+ def __init__(self, host_name=None, realm=None, ldap_uri=None): - super(CustodiaInstance, self).__init__("ipa-custodia") - self.config_file = paths.IPA_CUSTODIA_CONF - self.server_keys = paths.IPA_CUSTODIA_KEYS -- self.ldap_uri = None -+ self.ldap_uri = ldap_uri - self.fqdn = host_name - self.realm = realm - -@@ -49,14 +125,19 @@ class CustodiaInstance(SimpleServiceInstance): - ipautil.flush_sync(f) - - def create_instance(self): -- suffix = ipautil.realm_to_suffix(self.realm) -+ if self.ldap_uri is None or self.ldap_uri.startswith('ldapi://'): -+ # local case, ensure container exists -+ self.step("Making sure custodia container exists", -+ self.__create_container) -+ - self.step("Generating ipa-custodia config file", self.__config_file) -- self.step("Making sure custodia container exists", self.__create_container) - self.step("Generating ipa-custodia keys", self.__gen_keys) -- super(CustodiaInstance, self).create_instance(gensvc_name='KEYS', -- fqdn=self.fqdn, -- ldap_suffix=suffix, -- realm=self.realm) -+ super(CustodiaInstance, self).create_instance( -+ gensvc_name='KEYS', -+ fqdn=self.fqdn, -+ ldap_suffix=ipautil.realm_to_suffix(self.realm), -+ realm=self.realm -+ ) - sysupgrade.set_upgrade_state('custodia', 'installed', True) - - def __gen_keys(self): -@@ -81,18 +162,6 @@ class CustodiaInstance(SimpleServiceInstance): - root_logger.info("Secure server.keys mode") - os.chmod(self.server_keys, 0o600) - -- def create_replica(self, master_host_name): -- suffix = ipautil.realm_to_suffix(self.realm) -- self.ldap_uri = 'ldap://%s' % master_host_name -- self.master_host_name = master_host_name -- -- self.step("Generating ipa-custodia config file", self.__config_file) -- self.step("Generating ipa-custodia keys", self.__gen_keys) -- super(CustodiaInstance, self).create_instance(gensvc_name='KEYS', -- fqdn=self.fqdn, -- ldap_suffix=suffix, -- realm=self.realm) -- - def __create_container(self): - """ - Runs the custodia update file to ensure custodia container is present. -@@ -106,7 +175,7 @@ class CustodiaInstance(SimpleServiceInstance): - updater.update([os.path.join(paths.UPDATES_DIR, '73-custodia.update')]) - - def import_ra_key(self, master_host_name): -- cli = self.__CustodiaClient(server=master_host_name) -+ cli = self._get_custodia_client(server=master_host_name) - # please note that ipaCert part has to stay here for historical - # reasons (old servers expect you to ask for ra/ipaCert during - # replication as they store the RA agent cert in an NSS database -@@ -114,14 +183,14 @@ class CustodiaInstance(SimpleServiceInstance): - cli.fetch_key('ra/ipaCert') - - def import_dm_password(self, master_host_name): -- cli = self.__CustodiaClient(server=master_host_name) -+ cli = self._get_custodia_client(server=master_host_name) - cli.fetch_key('dm/DMHash') - -- def __wait_keys(self, host, timeout=300): -+ def _wait_keys(self, host, timeout=300): - ldap_uri = 'ldap://%s' % host - deadline = int(time.time()) + timeout -- root_logger.info("Waiting up to {} seconds to see our keys " -- "appear on host: {}".format(timeout, host)) -+ root_logger.info("Waiting up to %s seconds to see our keys " -+ "appear on host: %s", timeout, host) - - konn = KEMLdap(ldap_uri) - saved_e = None -@@ -129,6 +198,12 @@ class CustodiaInstance(SimpleServiceInstance): - try: - return konn.check_host_keys(self.fqdn) - except Exception as e: -+ # Print message to console only once for first error. -+ if saved_e is None: -+ # FIXME: Change once there's better way to show this -+ # message in installer output, -+ print(" Waiting for keys to appear on host: {}, please " -+ "wait until this has completed.".format(host)) - # log only once for the same error - if not isinstance(e, type(saved_e)): - root_logger.debug( -@@ -138,22 +213,23 @@ class CustodiaInstance(SimpleServiceInstance): - raise RuntimeError("Timed out trying to obtain keys.") - time.sleep(1) - -- def __CustodiaClient(self, server): -+ def _get_custodia_client(self, server): - # Before we attempt to fetch keys from this host, make sure our public - # keys have been replicated there. -- self.__wait_keys(server) -- -- return CustodiaClient('host@%s' % self.fqdn, self.server_keys, -- paths.KRB5_KEYTAB, server, realm=self.realm) -+ self._wait_keys(server) - -- def __get_keys(self, ca_host, cacerts_file, cacerts_pwd, data): -- # Fecth all needed certs one by one, then combine them in a single -- # p12 file -+ return CustodiaClient( -+ client_service='host@{}'.format(self.fqdn), -+ keyfile=self.server_keys, keytab=paths.KRB5_KEYTAB, -+ server=server, realm=self.realm -+ ) - -+ def _get_keys(self, ca_host, cacerts_file, cacerts_pwd, data): -+ # Fetch all needed certs one by one, then combine them in a single -+ # PKCS12 file - prefix = data['prefix'] - certlist = data['list'] -- -- cli = self.__CustodiaClient(server=ca_host) -+ cli = self._get_custodia_client(server=ca_host) - - # Temporary nssdb - tmpnssdir = tempfile.mkdtemp(dir=paths.TMP) -@@ -203,7 +279,7 @@ class CustodiaInstance(SimpleServiceInstance): - 'subsystemCert cert-pki-ca'] - data = {'prefix': 'ca', - 'list': certlist} -- self.__get_keys(ca_host, cacerts_file, cacerts_pwd, data) -+ self._get_keys(ca_host, cacerts_file, cacerts_pwd, data) - - def get_kra_keys(self, ca_host, cacerts_file, cacerts_pwd): - certlist = ['auditSigningCert cert-pki-kra', -@@ -212,7 +288,7 @@ class CustodiaInstance(SimpleServiceInstance): - 'transportCert cert-pki-kra'] - data = {'prefix': 'ca', - 'list': certlist} -- self.__get_keys(ca_host, cacerts_file, cacerts_pwd, data) -+ self._get_keys(ca_host, cacerts_file, cacerts_pwd, data) - - def __start(self): - super(CustodiaInstance, self).__start() -diff --git a/ipaserver/install/ipa_kra_install.py b/ipaserver/install/ipa_kra_install.py -index 3e08f4da94651b49876e1427daddbd957f0027ae..9ebabe057513141ee76d238a3f20e76a27dd932e 100644 ---- a/ipaserver/install/ipa_kra_install.py -+++ b/ipaserver/install/ipa_kra_install.py -@@ -32,6 +32,7 @@ from ipapython import admintool - from ipapython import ipautil - from ipaserver.install import service - from ipaserver.install import cainstance -+from ipaserver.install import custodiainstance - from ipaserver.install import krainstance - from ipaserver.install import dsinstance - from ipaserver.install import installutils -@@ -176,7 +177,6 @@ class KRAInstaller(KRAInstall): - - api.Backend.ldap2.connect() - -- config = None - if self.installing_replica: - if self.options.promote: - config = ReplicaConfig() -@@ -196,6 +196,7 @@ class KRAInstaller(KRAInstall): - config.kra_host_name = config.master_host_name - - config.setup_kra = True -+ config.promote = self.options.promote - - if config.subject_base is None: - attrs = api.Backend.ldap2.get_ipa_config() -@@ -204,6 +205,11 @@ class KRAInstaller(KRAInstall): - if config.kra_host_name is None: - config.kra_host_name = service.find_providing_server( - 'KRA', api.Backend.ldap2, api.env.ca_host) -+ custodia = custodiainstance.get_custodia_instance( -+ config, custodiainstance.CustodiaModes.KRA_PEER) -+ else: -+ config = None -+ custodia = None - - try: - kra.install_check(api, config, self.options) -@@ -213,7 +219,7 @@ class KRAInstaller(KRAInstall): - print(dedent(self.INSTALLER_START_MESSAGE)) - - try: -- kra.install(api, config, self.options) -+ kra.install(api, config, self.options, custodia=custodia) - except: - self.log.error(dedent(self.FAIL_MESSAGE)) - raise -diff --git a/ipaserver/install/kra.py b/ipaserver/install/kra.py -index 3545b301a977f4b7e7801ca1ef87d594bb3ba54f..3333970e8af9e36d6b7614286cc1e774f6eb9b25 100644 ---- a/ipaserver/install/kra.py -+++ b/ipaserver/install/kra.py -@@ -16,7 +16,6 @@ from ipaplatform.paths import paths - from ipapython import certdb - from ipapython import ipautil - from ipapython.install.core import group --from ipaserver.install import custodiainstance - from ipaserver.install import cainstance - from ipaserver.install import krainstance - from ipaserver.install import dsinstance -@@ -68,7 +67,7 @@ def install_check(api, replica_config, options): - "new replica file.") - - --def install(api, replica_config, options): -+def install(api, replica_config, options, custodia): - if replica_config is None: - if not options.setup_kra: - return -@@ -91,9 +90,6 @@ def install(api, replica_config, options): - 'host/{env.host}@{env.realm}'.format(env=api.env), - paths.KRB5_KEYTAB, - ccache) -- custodia = custodiainstance.CustodiaInstance( -- replica_config.host_name, -- replica_config.realm_name) - custodia.get_kra_keys( - replica_config.kra_host_name, - krafile, -diff --git a/ipaserver/install/server/install.py b/ipaserver/install/server/install.py -index 422474fa915b4876530f304ef9424f6b31cf26cc..f4338ed0428f7f44decd004f6a183258bef7f795 100644 ---- a/ipaserver/install/server/install.py -+++ b/ipaserver/install/server/install.py -@@ -709,6 +709,7 @@ def install(installer): - host_name = options.host_name - ip_addresses = options.ip_addresses - setup_ca = options.setup_ca -+ options.promote = False # first master, no promotion - - # Installation has started. No IPA sysrestore items are restored in case of - # failure to enable root cause investigation -@@ -789,6 +790,10 @@ def install(installer): - setup_pkinit=not options.no_pkinit, - subject_base=options.subject_base) - -+ custodia = custodiainstance.get_custodia_instance( -+ options, custodiainstance.CustodiaModes.MASTER_PEER) -+ custodia.create_instance() -+ - if setup_ca: - if not options.external_cert_files and options.external_ca: - # stage 1 of external CA installation -@@ -803,7 +808,7 @@ def install(installer): - if n in options.__dict__} - write_cache(cache_vars) - -- ca.install_step_0(False, None, options) -+ ca.install_step_0(False, None, options, custodia=custodia) - else: - # Put the CA cert where other instances expect it - x509.write_certificate(http_ca_cert, paths.IPA_CA_CRT) -@@ -823,15 +828,12 @@ def install(installer): - ds.enable_ssl() - - if setup_ca: -- ca.install_step_1(False, None, options) -+ ca.install_step_1(False, None, options, custodia=custodia) - - otpd = otpdinstance.OtpdInstance() - otpd.create_instance('OTPD', host_name, - ipautil.realm_to_suffix(realm_name)) - -- custodia = custodiainstance.CustodiaInstance(host_name, realm_name) -- custodia.create_instance() -- - # Create a HTTP instance - http = httpinstance.HTTPInstance(fstore) - if options.http_cert_files: -@@ -863,7 +865,7 @@ def install(installer): - krb.restart() - - if options.setup_kra: -- kra.install(api, None, options) -+ kra.install(api, None, options, custodia=custodia) - - if options.setup_dns: - dns.install(False, False, options) -diff --git a/ipaserver/install/server/replicainstall.py b/ipaserver/install/server/replicainstall.py -index 42e4615ad2dc1f604f5d8d14f8e57e3e4674bcb9..2ecc0abe0c5918cf5aefccc1bd6f09e70503baab 100644 ---- a/ipaserver/install/server/replicainstall.py -+++ b/ipaserver/install/server/replicainstall.py -@@ -1357,6 +1357,7 @@ def install(installer): - fstore = installer._fstore - sstore = installer._sstore - config = installer._config -+ config.promote = installer.promote - promote = installer.promote - cafile = installer._ca_file - dirsrv_pkcs12_info = installer._dirsrv_pkcs12_info -@@ -1483,19 +1484,19 @@ def install(installer): - otpd.create_instance('OTPD', config.host_name, - ipautil.realm_to_suffix(config.realm_name)) - -- custodia = custodiainstance.CustodiaInstance(config.host_name, -- config.realm_name) -- if promote: -- custodia.create_replica(config.master_host_name) -+ if ca_enabled: -+ mode = custodiainstance.CustodiaModes.CA_PEER - else: -- custodia.create_instance() -+ mode = custodiainstance.CustodiaModes.MASTER_PEER -+ custodia = custodiainstance.get_custodia_instance(config, mode) -+ custodia.create_instance() - - if ca_enabled: - options.realm_name = config.realm_name - options.domain_name = config.domain_name - options.host_name = config.host_name - options.dm_password = config.dirman_password -- ca.install(False, config, options) -+ ca.install(False, config, options, custodia=custodia) - - # configure PKINIT now that all required services are in place - krb.enable_ssl() -@@ -1505,7 +1506,7 @@ def install(installer): - ds.apply_updates() - - if kra_enabled: -- kra.install(api, config, options) -+ kra.install(api, config, options, custodia=custodia) - - service.print_msg("Restarting the KDC") - krb.restart() --- -2.14.3 - diff --git a/SOURCES/0048-Don-t-try-to-backup-CS.cfg-during-upgrade-if-CA-is-n.patch b/SOURCES/0048-Don-t-try-to-backup-CS.cfg-during-upgrade-if-CA-is-n.patch deleted file mode 100644 index cd44943..0000000 --- a/SOURCES/0048-Don-t-try-to-backup-CS.cfg-during-upgrade-if-CA-is-n.patch +++ /dev/null @@ -1,33 +0,0 @@ -From 56326828c7ccf75bd867330606c83a621affab9a Mon Sep 17 00:00:00 2001 -From: Rob Crittenden -Date: Mon, 26 Feb 2018 16:13:58 -0500 -Subject: [PATCH] Don't try to backup CS.cfg during upgrade if CA is not - configured - -https://pagure.io/freeipa/issue/7409 - -Signed-off-by: Rob Crittenden -Reviewed-By: Fraser Tweedale -Reviewed-By: Alexander Bokovoy -Reviewed-By: Fraser Tweedale ---- - ipaserver/install/server/upgrade.py | 3 ++- - 1 file changed, 2 insertions(+), 1 deletion(-) - -diff --git a/ipaserver/install/server/upgrade.py b/ipaserver/install/server/upgrade.py -index c55242a4af990c3218d8451fac7b082066a23be3..bf603acb5f931a4194320795874859f5bdc94647 100644 ---- a/ipaserver/install/server/upgrade.py -+++ b/ipaserver/install/server/upgrade.py -@@ -1630,7 +1630,8 @@ def upgrade_configuration(): - - with installutils.stopped_service('pki-tomcatd', 'pki-tomcat'): - # Dogtag must be stopped to be able to backup CS.cfg config -- ca.backup_config() -+ if ca.is_configured(): -+ ca.backup_config() - - # migrate CRL publish dir before the location in ipa.conf is updated - ca_restart = migrate_crl_publish_dir(ca) --- -2.14.3 - diff --git a/SOURCES/0049-Use-one-Custodia-peer-to-retrieve-all-secrets.patch b/SOURCES/0049-Use-one-Custodia-peer-to-retrieve-all-secrets.patch deleted file mode 100644 index 44a243a..0000000 --- a/SOURCES/0049-Use-one-Custodia-peer-to-retrieve-all-secrets.patch +++ /dev/null @@ -1,270 +0,0 @@ -From d3c09a6de06d8ae19d05c0b96cb9c2a5b789b472 Mon Sep 17 00:00:00 2001 -From: Christian Heimes -Date: Thu, 7 Jun 2018 18:17:20 +0200 -Subject: [PATCH] Use one Custodia peer to retrieve all secrets - -Fix 994f71ac8a1bb7ba6bc9caf0f6e4f59af44ad9c4 was incomplete. Under some -circumstancs the DM hash and CA keys were still retrieved from two different -machines. - -Custodia client now uses a single remote to upload keys and download all -secrets. - -Fixes: https://pagure.io/freeipa/issue/7518 -Signed-off-by: Christian Heimes -Reviewed-By: Simo Sorce ---- - ipaserver/install/ca.py | 1 - - ipaserver/install/cainstance.py | 2 +- - ipaserver/install/custodiainstance.py | 74 +++++++++++++++++------------- - ipaserver/install/kra.py | 1 - - ipaserver/install/server/install.py | 4 +- - ipaserver/install/server/replicainstall.py | 2 +- - 6 files changed, 46 insertions(+), 38 deletions(-) - -diff --git a/ipaserver/install/ca.py b/ipaserver/install/ca.py -index 2ac4382..1c7d375 100644 ---- a/ipaserver/install/ca.py -+++ b/ipaserver/install/ca.py -@@ -235,7 +235,6 @@ def install_step_0(standalone, replica_config, options, custodia): - cafile = os.path.join(replica_config.dir, 'cacert.p12') - if options.promote: - custodia.get_ca_keys( -- replica_config.ca_host_name, - cafile, - replica_config.dirman_password) - -diff --git a/ipaserver/install/cainstance.py b/ipaserver/install/cainstance.py -index eefc30b..cc0dbf3 100644 ---- a/ipaserver/install/cainstance.py -+++ b/ipaserver/install/cainstance.py -@@ -712,7 +712,7 @@ class CAInstance(DogtagInstance): - self.configure_agent_renewal() - - def __import_ra_key(self): -- self._custodia.import_ra_key(self.master_host) -+ self._custodia.import_ra_key() - self.__set_ra_cert_perms() - - self.configure_agent_renewal() -diff --git a/ipaserver/install/custodiainstance.py b/ipaserver/install/custodiainstance.py -index 262ae58..ada8d03 100644 ---- a/ipaserver/install/custodiainstance.py -+++ b/ipaserver/install/custodiainstance.py -@@ -71,41 +71,45 @@ def get_custodia_instance(config, mode): - if mode == CustodiaModes.CA_PEER: - # In case we install replica with CA, prefer CA host as source for - # all Custodia secret material. -- custodia_master = config.ca_host_name -+ custodia_peer = config.ca_host_name - elif mode == CustodiaModes.KRA_PEER: -- custodia_master = config.kra_host_name -+ custodia_peer = config.kra_host_name - elif mode == CustodiaModes.MASTER_PEER: -- custodia_master = config.master_host_name -+ custodia_peer = config.master_host_name - elif mode == CustodiaModes.STANDALONE: -- custodia_master = None -+ custodia_peer = None - else: -- custodia_master = None -+ custodia_peer = None - -- if custodia_master is None: -+ if custodia_peer is None: - # use ldapi with local dirsrv instance - root_logger.info("Custodia uses LDAPI.") -- ldap_uri = None - else: -- root_logger.info("Custodia uses '%s' as master peer.", -- custodia_master) -- ldap_uri = 'ldap://{}'.format(custodia_master) -+ root_logger.info("Custodia uses '%s' as master peer.", custodia_peer) - - return CustodiaInstance( - host_name=config.host_name, - realm=config.realm_name, -- ldap_uri=ldap_uri -+ custodia_peer=custodia_peer - ) - - - class CustodiaInstance(SimpleServiceInstance): -- def __init__(self, host_name=None, realm=None, ldap_uri=None): -+ def __init__(self, host_name=None, realm=None, custodia_peer=None): - super(CustodiaInstance, self).__init__("ipa-custodia") - self.config_file = paths.IPA_CUSTODIA_CONF - self.server_keys = paths.IPA_CUSTODIA_KEYS -- self.ldap_uri = ldap_uri -+ self.custodia_peer = custodia_peer - self.fqdn = host_name - self.realm = realm - -+ @property -+ def ldap_uri(self): -+ if self.custodia_peer is None: -+ return installutils.realm_to_ldapi_uri(self.realm) -+ else: -+ return "ldap://{}".format(self.custodia_peer) -+ - def __config_file(self): - template_file = os.path.basename(self.config_file) + '.template' - template = os.path.join(paths.USR_SHARE_IPA_DIR, template_file) -@@ -125,7 +129,7 @@ class CustodiaInstance(SimpleServiceInstance): - ipautil.flush_sync(f) - - def create_instance(self): -- if self.ldap_uri is None or self.ldap_uri.startswith('ldapi://'): -+ if self.ldap_uri.startswith('ldapi://'): - # local case, ensure container exists - self.step("Making sure custodia container exists", - self.__create_container) -@@ -174,25 +178,24 @@ class CustodiaInstance(SimpleServiceInstance): - updater = ldapupdate.LDAPUpdate(sub_dict=sub_dict) - updater.update([os.path.join(paths.UPDATES_DIR, '73-custodia.update')]) - -- def import_ra_key(self, master_host_name): -- cli = self._get_custodia_client(server=master_host_name) -+ def import_ra_key(self): -+ cli = self._get_custodia_client() - # please note that ipaCert part has to stay here for historical - # reasons (old servers expect you to ask for ra/ipaCert during - # replication as they store the RA agent cert in an NSS database - # with this nickname) - cli.fetch_key('ra/ipaCert') - -- def import_dm_password(self, master_host_name): -- cli = self._get_custodia_client(server=master_host_name) -+ def import_dm_password(self): -+ cli = self._get_custodia_client() - cli.fetch_key('dm/DMHash') - -- def _wait_keys(self, host, timeout=300): -- ldap_uri = 'ldap://%s' % host -+ def _wait_keys(self, timeout=300): - deadline = int(time.time()) + timeout - root_logger.info("Waiting up to %s seconds to see our keys " -- "appear on host: %s", timeout, host) -+ "appear on host %s", timeout, self.ldap_uri) - -- konn = KEMLdap(ldap_uri) -+ konn = KEMLdap(self.ldap_uri) - saved_e = None - while True: - try: -@@ -202,8 +205,11 @@ class CustodiaInstance(SimpleServiceInstance): - if saved_e is None: - # FIXME: Change once there's better way to show this - # message in installer output, -- print(" Waiting for keys to appear on host: {}, please " -- "wait until this has completed.".format(host)) -+ print( -+ " Waiting for keys to appear on host: {}, please " -+ "wait until this has completed.".format( -+ self.ldap_uri) -+ ) - # log only once for the same error - if not isinstance(e, type(saved_e)): - root_logger.debug( -@@ -213,23 +219,25 @@ class CustodiaInstance(SimpleServiceInstance): - raise RuntimeError("Timed out trying to obtain keys.") - time.sleep(1) - -- def _get_custodia_client(self, server): -+ def _get_custodia_client(self): -+ if self.custodia_peer is None: -+ raise ValueError("Can't replicate secrets without Custodia peer") - # Before we attempt to fetch keys from this host, make sure our public - # keys have been replicated there. -- self._wait_keys(server) -+ self._wait_keys() - - return CustodiaClient( - client_service='host@{}'.format(self.fqdn), - keyfile=self.server_keys, keytab=paths.KRB5_KEYTAB, -- server=server, realm=self.realm -+ server=self.custodia_peer, realm=self.realm - ) - -- def _get_keys(self, ca_host, cacerts_file, cacerts_pwd, data): -+ def _get_keys(self, cacerts_file, cacerts_pwd, data): - # Fetch all needed certs one by one, then combine them in a single - # PKCS12 file - prefix = data['prefix'] - certlist = data['list'] -- cli = self._get_custodia_client(server=ca_host) -+ cli = self._get_custodia_client() - - # Temporary nssdb - tmpnssdir = tempfile.mkdtemp(dir=paths.TMP) -@@ -272,23 +280,23 @@ class CustodiaInstance(SimpleServiceInstance): - finally: - shutil.rmtree(tmpnssdir) - -- def get_ca_keys(self, ca_host, cacerts_file, cacerts_pwd): -+ def get_ca_keys(self, cacerts_file, cacerts_pwd): - certlist = ['caSigningCert cert-pki-ca', - 'ocspSigningCert cert-pki-ca', - 'auditSigningCert cert-pki-ca', - 'subsystemCert cert-pki-ca'] - data = {'prefix': 'ca', - 'list': certlist} -- self._get_keys(ca_host, cacerts_file, cacerts_pwd, data) -+ self._get_keys(cacerts_file, cacerts_pwd, data) - -- def get_kra_keys(self, ca_host, cacerts_file, cacerts_pwd): -+ def get_kra_keys(self, cacerts_file, cacerts_pwd): - certlist = ['auditSigningCert cert-pki-kra', - 'storageCert cert-pki-kra', - 'subsystemCert cert-pki-ca', - 'transportCert cert-pki-kra'] - data = {'prefix': 'ca', - 'list': certlist} -- self._get_keys(ca_host, cacerts_file, cacerts_pwd, data) -+ self._get_keys(cacerts_file, cacerts_pwd, data) - - def __start(self): - super(CustodiaInstance, self).__start() -diff --git a/ipaserver/install/kra.py b/ipaserver/install/kra.py -index 3333970..d8773b9 100644 ---- a/ipaserver/install/kra.py -+++ b/ipaserver/install/kra.py -@@ -91,7 +91,6 @@ def install(api, replica_config, options, custodia): - paths.KRB5_KEYTAB, - ccache) - custodia.get_kra_keys( -- replica_config.kra_host_name, - krafile, - replica_config.dirman_password) - else: -diff --git a/ipaserver/install/server/install.py b/ipaserver/install/server/install.py -index f4338ed..3651cde 100644 ---- a/ipaserver/install/server/install.py -+++ b/ipaserver/install/server/install.py -@@ -1085,7 +1085,9 @@ def uninstall(installer): - dsinstance.DsInstance(fstore=fstore).uninstall() - if _server_trust_ad_installed: - adtrustinstance.ADTRUSTInstance(fstore).uninstall() -- custodiainstance.CustodiaInstance().uninstall() -+ # realm isn't used, but IPAKEMKeys parses /etc/ipa/default.conf -+ # otherwise, see https://pagure.io/freeipa/issue/7474 . -+ custodiainstance.CustodiaInstance(realm='REALM.INVALID').uninstall() - otpdinstance.OtpdInstance().uninstall() - tasks.restore_hostname(fstore, sstore) - fstore.restore_all_files() -diff --git a/ipaserver/install/server/replicainstall.py b/ipaserver/install/server/replicainstall.py -index ef61590..b9cd518 100644 ---- a/ipaserver/install/server/replicainstall.py -+++ b/ipaserver/install/server/replicainstall.py -@@ -1512,7 +1512,7 @@ def install(installer): - krb.restart() - - if promote: -- custodia.import_dm_password(config.master_host_name) -+ custodia.import_dm_password() - promote_sssd(config.host_name) - promote_openldap_conf(config.host_name, config.master_host_name) - --- -2.9.3 - diff --git a/SOURCES/0050-Fix-elements-not-being-removed-in-otpd_queue_pop_msg.patch b/SOURCES/0050-Fix-elements-not-being-removed-in-otpd_queue_pop_msg.patch deleted file mode 100644 index d0e2f25..0000000 --- a/SOURCES/0050-Fix-elements-not-being-removed-in-otpd_queue_pop_msg.patch +++ /dev/null @@ -1,31 +0,0 @@ -From 006986b0f004b4cdbc3620b417d5a7f62931ecb6 Mon Sep 17 00:00:00 2001 -From: Robbie Harwood -Date: Wed, 30 May 2018 14:54:54 -0400 -Subject: [PATCH] Fix elements not being removed in otpd_queue_pop_msgid() - -If the element being removed were not the queue head, -otpd_queue_pop_msgid() would not actually remove the element, leading -to potential double frees and request replays. - -Reviewed-By: Alexander Bokovoy -Reviewed-By: Stanislav Laznicka ---- - daemons/ipa-otpd/queue.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/daemons/ipa-otpd/queue.c b/daemons/ipa-otpd/queue.c -index 730bbc40b864b9dc65a69049c0a0e516e3daac0e..9e29fb238d5c7a7395bcf3860ce7445c27ca98ac 100644 ---- a/daemons/ipa-otpd/queue.c -+++ b/daemons/ipa-otpd/queue.c -@@ -155,7 +155,7 @@ struct otpd_queue_item *otpd_queue_pop_msgid(struct otpd_queue *q, int msgid) - - for (item = q->head, prev = &q->head; - item != NULL; -- item = item->next, prev = &item->next) { -+ prev = &item->next, item = item->next) { - if (item->msgid == msgid) { - *prev = item->next; - if (q->head == NULL) --- -2.17.1 - diff --git a/SOURCES/0051-Tune-DS-replication-settings.patch b/SOURCES/0051-Tune-DS-replication-settings.patch deleted file mode 100644 index 6776b5b..0000000 --- a/SOURCES/0051-Tune-DS-replication-settings.patch +++ /dev/null @@ -1,434 +0,0 @@ -From f86e77a8e6fd9e57472e6d1b63246254143f706d Mon Sep 17 00:00:00 2001 -From: Christian Heimes -Date: Tue, 3 Jul 2018 19:40:05 +0200 -Subject: [PATCH] Tune DS replication settings - -Tune 389-DS replication settings to improve performance and avoid -timeouts. During installation of a replica, the value of -nsDS5ReplicaBindDnGroupCheckInterval is reduced to 2 seconds. At the end -of the installation, the value is increased sensible production -settings. This avoids long delays during replication. - -See: https://pagure.io/freeipa/issue/7617 -Signed-off-by: Christian Heimes -Reviewed-By: Tibor Dudlak ---- - ipaserver/install/cainstance.py | 14 ++- - ipaserver/install/dsinstance.py | 48 +++++--- - ipaserver/install/replication.py | 92 +++++++++++---- - ipaserver/install/server/replicainstall.py | 2 + - ipaserver/install/server/upgrade.py | 12 +- - ipatests/test_integration/test_external_ca.py | 109 ++++++------------ - 6 files changed, 157 insertions(+), 120 deletions(-) - -diff --git a/ipaserver/install/cainstance.py b/ipaserver/install/cainstance.py -index eefc30b6e01dcf744703b8607cbe169fbec5d859..e72e706ffd2d9163a283f6bb09815e5aa60c889c 100644 ---- a/ipaserver/install/cainstance.py -+++ b/ipaserver/install/cainstance.py -@@ -388,8 +388,13 @@ class CAInstance(DogtagInstance): - self.step("set up CRL publishing", self.__enable_crl_publish) - self.step("enable PKIX certificate path discovery and validation", self.enable_pkix) - if promote: -- self.step("destroying installation admin user", self.teardown_admin) -- self.step("starting certificate server instance", self.start_instance) -+ self.step("destroying installation admin user", -+ self.teardown_admin) -+ self.step("starting certificate server instance", -+ self.start_instance) -+ if promote: -+ self.step("Finalize replication settings", -+ self.finalize_replica_config) - # Step 1 of external is getting a CSR so we don't need to do these - # steps until we get a cert back from the external CA. - if self.external != 1: -@@ -1196,13 +1201,16 @@ class CAInstance(DogtagInstance): - api.Backend.ldap2.add_entry(entry) - - def __setup_replication(self): -- - repl = replication.CAReplicationManager(self.realm, self.fqdn) - repl.setup_cs_replication(self.master_host) - - # Activate Topology for o=ipaca segments - self.__update_topology() - -+ def finalize_replica_config(self): -+ repl = replication.CAReplicationManager(self.realm, self.fqdn) -+ repl.finalize_replica_config(self.master_host) -+ - def __enable_instance(self): - basedn = ipautil.realm_to_suffix(self.realm) - if not self.clone: -diff --git a/ipaserver/install/dsinstance.py b/ipaserver/install/dsinstance.py -index 923f483340a26a614001701ce6c235dd73501501..7adaabd3c1280709150329003130f70233de37f4 100644 ---- a/ipaserver/install/dsinstance.py -+++ b/ipaserver/install/dsinstance.py -@@ -417,6 +417,20 @@ class DsInstance(service.Service): - - self.start_creation(runtime=30) - -+ def _get_replication_manager(self): -+ # Always connect to self over ldapi -+ ldap_uri = ipaldap.get_ldap_uri(protocol='ldapi', realm=self.realm) -+ conn = ipaldap.LDAPClient(ldap_uri) -+ conn.external_bind() -+ repl = replication.ReplicationManager( -+ self.realm, self.fqdn, self.dm_password, conn=conn -+ ) -+ if self.dm_password is not None and not self.promote: -+ bind_dn = DN(('cn', 'Directory Manager')) -+ bind_pw = self.dm_password -+ else: -+ bind_dn = bind_pw = None -+ return repl, bind_dn, bind_pw - - def __setup_replica(self): - """ -@@ -433,26 +447,24 @@ class DsInstance(service.Service): - self.realm, - self.dm_password) - -- # Always connect to self over ldapi -- ldap_uri = ipaldap.get_ldap_uri(protocol='ldapi', realm=self.realm) -- conn = ipaldap.LDAPClient(ldap_uri) -- conn.external_bind() -- repl = replication.ReplicationManager(self.realm, -- self.fqdn, -- self.dm_password, conn=conn) -- -- if self.dm_password is not None and not self.promote: -- bind_dn = DN(('cn', 'Directory Manager')) -- bind_pw = self.dm_password -- else: -- bind_dn = bind_pw = None -- -- repl.setup_promote_replication(self.master_fqdn, -- r_binddn=bind_dn, -- r_bindpw=bind_pw, -- cacert=self.ca_file) -+ repl, bind_dn, bind_pw = self._get_replication_manager() -+ repl.setup_promote_replication( -+ self.master_fqdn, -+ r_binddn=bind_dn, -+ r_bindpw=bind_pw, -+ cacert=self.ca_file -+ ) - self.run_init_memberof = repl.needs_memberof_fixup() - -+ def finalize_replica_config(self): -+ repl, bind_dn, bind_pw = self._get_replication_manager() -+ repl.finalize_replica_config( -+ self.master_fqdn, -+ r_binddn=bind_dn, -+ r_bindpw=bind_pw, -+ cacert=self.ca_file -+ ) -+ - def __configure_sasl_mappings(self): - # we need to remove any existing SASL mappings in the directory as otherwise they - # they may conflict. -diff --git a/ipaserver/install/replication.py b/ipaserver/install/replication.py -index e0055b792ce5b51fd411a7ea1f4316bb017984ba..3a76b70038bf6f739fe63aeac0233ccbfda2f016 100644 ---- a/ipaserver/install/replication.py -+++ b/ipaserver/install/replication.py -@@ -69,6 +69,20 @@ STRIP_ATTRS = ('modifiersName', - 'internalModifiersName', - 'internalModifyTimestamp') - -+# settings for cn=replica,cn=$DB,cn=mapping tree,cn=config -+# during replica installation -+REPLICA_CREATION_SETTINGS = { -+ "nsds5ReplicaReleaseTimeout": ["20"], -+ "nsds5ReplicaBackoffMax": ["3"], -+ "nsDS5ReplicaBindDnGroupCheckInterval": ["2"] -+} -+# after replica installation -+REPLICA_FINAL_SETTINGS = { -+ "nsds5ReplicaReleaseTimeout": ["60"], -+ "nsds5ReplicaBackoffMax": ["300"], # default -+ "nsDS5ReplicaBindDnGroupCheckInterval": ["60"] -+} -+ - - def replica_conn_check(master_host, host_name, realm, check_ca, - dogtag_master_ds_port, admin_password=None, -@@ -192,9 +206,13 @@ def wait_for_entry(connection, dn, timeout=7200, attr='', quiet=True): - - - class ReplicationManager(object): -- """Manage replication agreements between DS servers, and sync -- agreements with Windows servers""" -- def __init__(self, realm, hostname, dirman_passwd, port=PORT, starttls=False, conn=None): -+ """Manage replication agreements -+ -+ between DS servers, and sync agreements with Windows servers -+ """ -+ -+ def __init__(self, realm, hostname, dirman_passwd=None, port=PORT, -+ starttls=False, conn=None): - self.hostname = hostname - self.port = port - self.dirman_passwd = dirman_passwd -@@ -471,22 +489,16 @@ class ReplicationManager(object): - except errors.NotFound: - pass - else: -- managers = {DN(m) for m in entry.get('nsDS5ReplicaBindDN', [])} -- -- mods = [] -- if replica_binddn not in managers: -+ binddns = entry.setdefault('nsDS5ReplicaBindDN', []) -+ if replica_binddn not in {DN(m) for m in binddns}: - # Add the new replication manager -- mods.append( -- (ldap.MOD_ADD, 'nsDS5ReplicaBindDN', replica_binddn) -- ) -- if 'nsds5replicareleasetimeout' not in entry: -- # See https://pagure.io/freeipa/issue/7488 -- mods.append( -- (ldap.MOD_ADD, 'nsds5replicareleasetimeout', ['60']) -- ) -- -- if mods: -- conn.modify_s(dn, mods) -+ binddns.append(replica_binddn) -+ for key, value in REPLICA_CREATION_SETTINGS.items(): -+ entry[key] = value -+ try: -+ conn.update_entry(entry) -+ except errors.EmptyModlist: -+ pass - - self.set_replica_binddngroup(conn, entry) - -@@ -505,9 +517,8 @@ class ReplicationManager(object): - nsds5flags=["1"], - nsds5replicabinddn=[replica_binddn], - nsds5replicabinddngroup=[self.repl_man_group_dn], -- nsds5replicabinddngroupcheckinterval=["60"], -- nsds5replicareleasetimeout=["60"], - nsds5replicalegacyconsumer=["off"], -+ **REPLICA_CREATION_SETTINGS - ) - conn.add_entry(entry) - -@@ -533,6 +544,47 @@ class ReplicationManager(object): - except errors.DuplicateEntry: - return - -+ def _finalize_replica_settings(self, conn): -+ """Change replica settings to final values -+ -+ During replica installation, some settings are configured for faster -+ replication. -+ """ -+ dn = self.replica_dn() -+ entry = conn.get_entry(dn) -+ for key, value in REPLICA_FINAL_SETTINGS.items(): -+ entry[key] = value -+ try: -+ conn.update_entry(entry) -+ except errors.EmptyModlist: -+ pass -+ -+ def finalize_replica_config(self, r_hostname, r_binddn=None, -+ r_bindpw=None, cacert=paths.IPA_CA_CRT): -+ """Apply final cn=replica settings -+ -+ replica_config() sets several attribute to fast cache invalidation -+ and fast reconnects to optimize replicat installation. For -+ production, longer timeouts and less aggressive cache invalidation -+ is sufficient. finalize_replica_config() sets the values on new -+ replica and the master. -+ -+ When installing multiple replicas in parallel, one replica may -+ finalize the values while another is still installing. -+ -+ See https://pagure.io/freeipa/issue/7617 -+ """ -+ self._finalize_replica_settings(self.conn) -+ -+ ldap_uri = ipaldap.get_ldap_uri(r_hostname) -+ r_conn = ipaldap.LDAPClient(ldap_uri, cacert=cacert) -+ if r_bindpw: -+ r_conn.simple_bind(r_binddn, r_bindpw) -+ else: -+ r_conn.gssapi_bind() -+ self._finalize_replica_settings(r_conn) -+ r_conn.close() -+ - def setup_chaining_backend(self, conn): - chaindn = DN(('cn', 'chaining database'), ('cn', 'plugins'), ('cn', 'config')) - benamebase = "chaindb" -diff --git a/ipaserver/install/server/replicainstall.py b/ipaserver/install/server/replicainstall.py -index 2ecc0abe0c5918cf5aefccc1bd6f09e70503baab..27e5e9cde7788b8c9b78b27ed47f5748f34c90ff 100644 ---- a/ipaserver/install/server/replicainstall.py -+++ b/ipaserver/install/server/replicainstall.py -@@ -1504,6 +1504,8 @@ def install(installer): - # Apply any LDAP updates. Needs to be done after the replica is synced-up - service.print_msg("Applying LDAP updates") - ds.apply_updates() -+ service.print_msg("Finalize replication settings") -+ ds.finalize_replica_config() - - if kra_enabled: - kra.install(api, config, options, custodia=custodia) -diff --git a/ipaserver/install/server/upgrade.py b/ipaserver/install/server/upgrade.py -index bf603acb5f931a4194320795874859f5bdc94647..793092be86da687fd21cf2c0ef3608f32fcf9f16 100644 ---- a/ipaserver/install/server/upgrade.py -+++ b/ipaserver/install/server/upgrade.py -@@ -50,6 +50,7 @@ from ipaserver.install import dnskeysyncinstance - from ipaserver.install import dogtaginstance - from ipaserver.install import krbinstance - from ipaserver.install import adtrustinstance -+from ipaserver.install import replication - from ipaserver.install.upgradeinstance import IPAUpgrade - from ipaserver.install.ldapupdate import BadSyntax - -@@ -1575,11 +1576,14 @@ def update_replica_config(db_suffix): - except ipalib.errors.NotFound: - return # entry does not exist until a replica is installed - -- if 'nsds5replicareleasetimeout' not in entry: -- # See https://pagure.io/freeipa/issue/7488 -- root_logger.info("Adding nsds5replicaReleaseTimeout=60 to %s", dn) -- entry['nsds5replicareleasetimeout'] = '60' -+ for key, value in replication.REPLICA_FINAL_SETTINGS.items(): -+ entry[key] = value -+ try: - api.Backend.ldap2.update_entry(entry) -+ except ipalib.errors.EmptyModlist: -+ pass -+ else: -+ root_logger.info("Updated entry %s", dn) - - - def upgrade_configuration(): -diff --git a/ipatests/test_integration/test_external_ca.py b/ipatests/test_integration/test_external_ca.py -index 7fc89cc949ca6a7ccddde17aca434cf55f2918ed..b22fa05206204e3dc382dc2b02c23a73e5c1b80b 100644 ---- a/ipatests/test_integration/test_external_ca.py -+++ b/ipatests/test_integration/test_external_ca.py -@@ -31,86 +31,45 @@ class TestExternalCA(IntegrationTest): - """ - Test of FreeIPA server installation with exernal CA - """ -- def test_external_ca(self): -- # Step 1 of ipa-server-install -- self.master.run_command([ -- 'ipa-server-install', '-U', -- '-a', self.master.config.admin_password, -- '-p', self.master.config.dirman_password, -- '--setup-dns', '--no-forwarders', -- '-n', self.master.domain.name, -- '-r', self.master.domain.realm, -- '--domain-level=%i' % self.master.config.domain_level, -- '--external-ca' -- ]) -- -- nss_db = os.path.join(self.master.config.test_dir, 'testdb') -- external_cert_file = os.path.join(nss_db, 'ipa.crt') -- external_ca_file = os.path.join(nss_db, 'ca.crt') -- noisefile = os.path.join(self.master.config.test_dir, 'noise.txt') -- pwdfile = os.path.join(self.master.config.test_dir, 'pwdfile.txt') -- -- # Create noise and password files for NSS database -- self.master.run_command('date | sha256sum > %s' % noisefile) -- self.master.run_command('echo %s > %s' % -- (self.master.config.admin_password, pwdfile)) -- -- # Create NSS database -- self.master.run_command(['mkdir', nss_db]) -- self.master.run_command([ -- 'certutil', '-N', -- '-d', nss_db, -- '-f', pwdfile -- ]) -- -- # Create external CA -- self.master.run_command([ -- 'certutil', '-S', -- '-d', nss_db, -- '-f', pwdfile, -- '-n', 'external', -- '-s', 'CN=External CA, O=%s' % self.master.domain.name, -- '-x', -- '-t', 'CTu,CTu,CTu', -- '-g', '2048', -- '-m', '0', -- '-v', '60', -- '-z', noisefile, -- '-2', '-1', '-5', '--extSKID' -- ], stdin_text='5\n9\nn\ny\n10\ny\n{}\nn\n5\n6\n7\n9\nn\n' -- ''.format(EXTERNAL_CA_KEY_ID)) -+ num_replicas = 1 - -- # Sign IPA cert request using the external CA -- self.master.run_command([ -- 'certutil', '-C', -- '-d', nss_db, -- '-f', pwdfile, -- '-c', 'external', -- '-m', '1', -- '-v', '60', -- '-2', '-1', '-3', '--extSKID', -- '-i', '/root/ipa.csr', -- '-o', external_cert_file, -- '-a' -- ], stdin_text='0\n1\n5\n9\ny\ny\n\ny\ny\n{}\n-1\n\nn\n{}\nn\n' -- ''.format(EXTERNAL_CA_KEY_ID, IPA_CA_KEY_ID)) -+ @tasks.collect_logs -+ def test_external_ca(self): -+ # Step 1 of ipa-server-install. -+ result = install_server_external_ca_step1(self.master) -+ assert result.returncode == 0 - -- # Export external CA file -- self.master.run_command( -- 'certutil -L -d %s -n "external" -a > %s' % -- (nss_db, external_ca_file) -- ) -+ # Sign CA, transport it to the host and get ipa a root ca paths. -+ root_ca_fname, ipa_ca_fname = tasks.sign_ca_and_transport( -+ self.master, paths.ROOT_IPA_CSR, ROOT_CA, IPA_CA) - -- # Step 2 of ipa-server-install -- self.master.run_command([ -- 'ipa-server-install', -- '-a', self.master.config.admin_password, -- '-p', self.master.config.dirman_password, -- '--external-cert-file', external_cert_file, -- '--external-cert-file', external_ca_file -- ]) -+ # Step 2 of ipa-server-install. -+ result = install_server_external_ca_step2( -+ self.master, ipa_ca_fname, root_ca_fname) -+ assert result.returncode == 0 - - # Make sure IPA server is working properly - tasks.kinit_admin(self.master) - result = self.master.run_command(['ipa', 'user-show', 'admin']) - assert 'User login: admin' in result.stdout_text -+ -+ # check that we can also install replica -+ tasks.install_replica(self.master, self.replicas[0]) -+ -+ # check that nsds5ReplicaReleaseTimeout option was set -+ result = self.master.run_command([ -+ 'ldapsearch', -+ '-x', -+ '-D', -+ 'cn=directory manager', -+ '-w', self.master.config.dirman_password, -+ '-b', 'cn=mapping tree,cn=config', -+ '(cn=replica)', -+ '-LLL', -+ '-o', -+ 'ldif-wrap=no']) -+ # case insensitive match -+ text = result.stdout_text.lower() -+ # see ipaserver.install.replication.REPLICA_FINAL_SETTINGS -+ assert 'nsds5ReplicaReleaseTimeout: 60'.lower() in text -+ assert 'nsDS5ReplicaBindDnGroupCheckInterval: 60'.lower() in text --- -2.17.1 - diff --git a/SOURCES/0052-In-IPA-4.4-when-updating-userpassword-with-ldapmodif.patch b/SOURCES/0052-In-IPA-4.4-when-updating-userpassword-with-ldapmodif.patch deleted file mode 100644 index 69de466..0000000 --- a/SOURCES/0052-In-IPA-4.4-when-updating-userpassword-with-ldapmodif.patch +++ /dev/null @@ -1,48 +0,0 @@ -From 0335e799ce5f73a1a1e9901fb5e9c85aaa718ecd Mon Sep 17 00:00:00 2001 -From: Thierry Bordaz -Date: Tue, 24 Jul 2018 12:16:35 +0200 -Subject: [PATCH] In IPA 4.4 when updating userpassword with ldapmodify does - not update krbPasswordExpiration nor krbLastPwdChange - -When making ipa-pwd-extop TXN aware, some callbacks are call twice. -Particularily - ipapwd_pre_add is called during PRE_ADD and TXN_PRE_ADD - ipapwd_pre_mod is called during PRE_MOD and TXN_PRE_MOD - ipapwd_post_modadd is called during POST_ADD and TXN_POST_ADD - ipapwd_post_modadd is called during POST_MOD and TXN_POST_MOD -It is not the expected behavior and it results on some skipped updates krbPasswordExpiration -and krbLastPwdChange - -https://pagure.io/freeipa/issue/7601 - -Reviewed-By: Florence Blanc-Renaud ---- - daemons/ipa-slapi-plugins/ipa-pwd-extop/prepost.c | 4 ---- - 1 file changed, 4 deletions(-) - -diff --git a/daemons/ipa-slapi-plugins/ipa-pwd-extop/prepost.c b/daemons/ipa-slapi-plugins/ipa-pwd-extop/prepost.c -index c62eae33418f8368c831a91f611915addf7d423b..209d596255d05bfd03de432b7930e6406cc025dc 100644 ---- a/daemons/ipa-slapi-plugins/ipa-pwd-extop/prepost.c -+++ b/daemons/ipa-slapi-plugins/ipa-pwd-extop/prepost.c -@@ -1488,8 +1488,6 @@ int ipapwd_pre_init(Slapi_PBlock *pb) - ret = slapi_pblock_set(pb, SLAPI_PLUGIN_VERSION, SLAPI_PLUGIN_VERSION_01); - if (!ret) ret = slapi_pblock_set(pb, SLAPI_PLUGIN_DESCRIPTION, (void *)&ipapwd_plugin_desc); - if (!ret) ret = slapi_pblock_set(pb, SLAPI_PLUGIN_PRE_BIND_FN, (void *)ipapwd_pre_bind); -- if (!ret) ret = slapi_pblock_set(pb, SLAPI_PLUGIN_PRE_ADD_FN, (void *)ipapwd_pre_add); -- if (!ret) ret = slapi_pblock_set(pb, SLAPI_PLUGIN_PRE_MODIFY_FN, (void *)ipapwd_pre_mod); - - return ret; - } -@@ -1513,9 +1511,7 @@ int ipapwd_post_init(Slapi_PBlock *pb) - - ret = slapi_pblock_set(pb, SLAPI_PLUGIN_VERSION, SLAPI_PLUGIN_VERSION_01); - if (!ret) ret = slapi_pblock_set(pb, SLAPI_PLUGIN_DESCRIPTION, (void *)&ipapwd_plugin_desc); -- if (!ret) ret = slapi_pblock_set(pb, SLAPI_PLUGIN_POST_ADD_FN, (void *)ipapwd_post_modadd); - if (!ret) ret = slapi_pblock_set(pb, SLAPI_PLUGIN_POST_DELETE_FN, (void *)ipapwd_post_updatecfg); -- if (!ret) ret = slapi_pblock_set(pb, SLAPI_PLUGIN_POST_MODIFY_FN, (void *)ipapwd_post_modadd); - if (!ret) ret = slapi_pblock_set(pb, SLAPI_PLUGIN_POST_MODRDN_FN, (void *)ipapwd_post_updatecfg); - - return ret; --- -2.17.1 - diff --git a/SOURCES/0053-Tests-add-integration-test-for-password-changes-by-d.patch b/SOURCES/0053-Tests-add-integration-test-for-password-changes-by-d.patch deleted file mode 100644 index 4580cb4..0000000 --- a/SOURCES/0053-Tests-add-integration-test-for-password-changes-by-d.patch +++ /dev/null @@ -1,157 +0,0 @@ -From 97e0d55745a125a933a8d4f9dddd31a752977948 Mon Sep 17 00:00:00 2001 -From: Florence Blanc-Renaud -Date: Mon, 6 Aug 2018 18:25:16 +0200 -Subject: [PATCH] Tests: add integration test for password changes by dir mgr - -Add a test for issue 7601: -- add a user, perform kinit user to modify the password, read krblastpwdchange -and krbpasswordexpiration. -- perform a ldapmodify on the password as dir mgr -- make sure that krblastpwdchange and krbpasswordexpiration have been modified -- perform the same check with ldappasswd - -Related to: -https://pagure.io/freeipa/issue/7601 - -Reviewed-By: Thierry Bordaz ---- - ipatests/test_integration/test_commands.py | 127 +++++++++++++++++++++ - 1 file changed, 127 insertions(+) - create mode 100644 ipatests/test_integration/test_commands.py - -diff --git a/ipatests/test_integration/test_commands.py b/ipatests/test_integration/test_commands.py -new file mode 100644 -index 0000000000000000000000000000000000000000..e277e4a2fe4089392b08719d46b011e6444e8094 ---- /dev/null -+++ b/ipatests/test_integration/test_commands.py -@@ -0,0 +1,127 @@ -+# -+# Copyright (C) 2018 FreeIPA Contributors see COPYING for license -+# -+"""Misc test for 'ipa' CLI regressions -+""" -+from __future__ import print_function -+ -+import re -+from tempfile import NamedTemporaryFile -+import textwrap -+import time -+ -+from ipaplatform.paths import paths -+ -+from ipatests.test_integration.base import IntegrationTest -+from ipatests.pytest_plugins.integration import tasks -+ -+ -+class TestIPACommand(IntegrationTest): -+ """ -+ A lot of commands can be executed against a single IPA installation -+ so provide a generic class to execute one-off commands that need to be -+ tested without having to fire up a full server to run one command. -+ """ -+ topology = 'line' -+ -+ def test_ldapmodify_password_issue7601(self): -+ user = 'ipauser' -+ original_passwd = 'Secret123' -+ new_passwd = 'userPasswd123' -+ new_passwd2 = 'mynewPwd123' -+ master = self.master -+ base_dn = str(master.domain.basedn) # pylint: disable=no-member -+ -+ # Create a user with a password -+ tasks.kinit_admin(master) -+ add_password_stdin_text = "{pwd}\n{pwd}".format(pwd=original_passwd) -+ master.run_command(['ipa', 'user-add', user, -+ '--first', user, -+ '--last', user, -+ '--password'], -+ stdin_text=add_password_stdin_text) -+ # kinit as that user in order to modify the pwd -+ user_kinit_stdin_text = "{old}\n%{new}\n%{new}\n".format( -+ old=original_passwd, -+ new=original_passwd) -+ master.run_command(['kinit', user], stdin_text=user_kinit_stdin_text) -+ # Retrieve krblastpwdchange and krbpasswordexpiration -+ search_cmd = [ -+ 'ldapsearch', '-x', -+ '-D', 'cn=directory manager', -+ '-w', master.config.dirman_password, -+ '-s', 'base', -+ '-b', 'uid={user},cn=users,cn=accounts,{base_dn}'.format( -+ user=user, base_dn=base_dn), -+ '-o', 'ldif-wrap=no', -+ '-LLL', -+ 'krblastpwdchange', -+ 'krbpasswordexpiration'] -+ output = master.run_command(search_cmd).stdout_text.lower() -+ -+ # extract krblastpwdchange and krbpasswordexpiration -+ krbchg_pattern = 'krblastpwdchange: (.+)\n' -+ krbexp_pattern = 'krbpasswordexpiration: (.+)\n' -+ krblastpwdchange = re.findall(krbchg_pattern, output)[0] -+ krbexp = re.findall(krbexp_pattern, output)[0] -+ -+ # sleep 1 sec (krblastpwdchange and krbpasswordexpiration have at most -+ # a 1s precision) -+ time.sleep(1) -+ # perform ldapmodify on userpassword as dir mgr -+ mod = NamedTemporaryFile() -+ ldif_file = mod.name -+ entry_ldif = textwrap.dedent(""" -+ dn: uid={user},cn=users,cn=accounts,{base_dn} -+ changetype: modify -+ replace: userpassword -+ userpassword: {new_passwd} -+ """).format( -+ user=user, -+ base_dn=base_dn, -+ new_passwd=new_passwd) -+ master.put_file_contents(ldif_file, entry_ldif) -+ arg = ['ldapmodify', -+ '-h', master.hostname, -+ '-p', '389', '-D', -+ str(master.config.dirman_dn), # pylint: disable=no-member -+ '-w', master.config.dirman_password, -+ '-f', ldif_file] -+ master.run_command(arg) -+ -+ # Test new password with kinit -+ master.run_command(['kinit', user], stdin_text=new_passwd) -+ # Retrieve krblastpwdchange and krbpasswordexpiration -+ output = master.run_command(search_cmd).stdout_text.lower() -+ # extract krblastpwdchange and krbpasswordexpiration -+ newkrblastpwdchange = re.findall(krbchg_pattern, output)[0] -+ newkrbexp = re.findall(krbexp_pattern, output)[0] -+ -+ # both should have changed -+ assert newkrblastpwdchange != krblastpwdchange -+ assert newkrbexp != krbexp -+ -+ # Now test passwd modif with ldappasswd -+ time.sleep(1) -+ master.run_command([ -+ paths.LDAPPASSWD, -+ '-D', str(master.config.dirman_dn), # pylint: disable=no-member -+ '-w', master.config.dirman_password, -+ '-a', new_passwd, -+ '-s', new_passwd2, -+ '-x', '-ZZ', -+ '-H', 'ldap://{hostname}'.format(hostname=master.hostname), -+ 'uid={user},cn=users,cn=accounts,{base_dn}'.format( -+ user=user, base_dn=base_dn)] -+ ) -+ # Test new password with kinit -+ master.run_command(['kinit', user], stdin_text=new_passwd2) -+ # Retrieve krblastpwdchange and krbpasswordexpiration -+ output = master.run_command(search_cmd).stdout_text.lower() -+ # extract krblastpwdchange and krbpasswordexpiration -+ newkrblastpwdchange2 = re.findall(krbchg_pattern, output)[0] -+ newkrbexp2 = re.findall(krbexp_pattern, output)[0] -+ -+ # both should have changed -+ assert newkrblastpwdchange != newkrblastpwdchange2 -+ assert newkrbexp != newkrbexp2 --- -2.17.1 - diff --git a/SOURCES/0054-Check-if-replication-agreement-exist-before-enable-d.patch b/SOURCES/0054-Check-if-replication-agreement-exist-before-enable-d.patch deleted file mode 100644 index 82ede2d..0000000 --- a/SOURCES/0054-Check-if-replication-agreement-exist-before-enable-d.patch +++ /dev/null @@ -1,74 +0,0 @@ -From 6db737931822c188833dee8cd086f91c640710fd Mon Sep 17 00:00:00 2001 -From: Felipe Barreto -Date: Mon, 15 Jan 2018 17:12:15 -0200 -Subject: [PATCH] Check if replication agreement exist before enable/disable it - -If the replication agreement does not exist, a custom exception is -raised explaining the problem. - -https://pagure.io/freeipa/issue/7201 - -Reviewed-By: Rob Crittenden -Reviewed-By: Christian Heimes ---- - install/tools/ipa-replica-manage | 7 +++++-- - ipaserver/install/replication.py | 11 +++++++++++ - 2 files changed, 16 insertions(+), 2 deletions(-) - -diff --git a/install/tools/ipa-replica-manage b/install/tools/ipa-replica-manage -index c00d8ca3a0fa8228c5aa782a270991f14ee16974..019826f822b23e4e55dedb794ff6c1459a3d93c8 100755 ---- a/install/tools/ipa-replica-manage -+++ b/install/tools/ipa-replica-manage -@@ -1200,8 +1200,11 @@ def re_initialize(realm, thishost, fromhost, dirman_passwd, nolookup=False): - repl = replication.ReplicationManager(realm, fromhost, dirman_passwd) - agreement = repl.get_replication_agreement(thishost) - -- thisrepl.enable_agreement(fromhost) -- repl.enable_agreement(thishost) -+ try: -+ thisrepl.enable_agreement(fromhost) -+ repl.enable_agreement(thishost) -+ except errors.NotFound as e: -+ sys.exit(e) - - repl.force_sync(repl.conn, thishost) - -diff --git a/ipaserver/install/replication.py b/ipaserver/install/replication.py -index 3a76b70038bf6f739fe63aeac0233ccbfda2f016..d4b41caa45409fa1537ae10f182599307f3e0439 100644 ---- a/ipaserver/install/replication.py -+++ b/ipaserver/install/replication.py -@@ -31,6 +31,7 @@ import ldap - from ipalib import api, errors - from ipalib.cli import textui - from ipapython.ipa_log_manager import root_logger -+from ipalib.text import _ - from ipapython import ipautil, ipaldap, kerberos - from ipapython.admintool import ScriptError - from ipapython.dn import DN -@@ -1599,6 +1600,11 @@ class ReplicationManager(object): - Disable the replication agreement to hostname. - """ - entry = self.get_replication_agreement(hostname) -+ if not entry: -+ raise errors.NotFound(reason=_( -+ "Replication agreement for %(hostname)s not found") % { -+ 'hostname': hostname -+ }) - entry['nsds5ReplicaEnabled'] = 'off' - - try: -@@ -1613,6 +1619,11 @@ class ReplicationManager(object): - Note: for replication to work it needs to be enabled both ways. - """ - entry = self.get_replication_agreement(hostname) -+ if not entry: -+ raise errors.NotFound(reason=_( -+ "Replication agreement for %(hostname)s not found") % { -+ 'hostname': hostname -+ }) - entry['nsds5ReplicaEnabled'] = 'on' - - try: --- -2.17.1 - diff --git a/SOURCES/0055-Fix-ipa-restore-create-var-run-ipa-files.patch b/SOURCES/0055-Fix-ipa-restore-create-var-run-ipa-files.patch deleted file mode 100644 index c47f4b9..0000000 --- a/SOURCES/0055-Fix-ipa-restore-create-var-run-ipa-files.patch +++ /dev/null @@ -1,47 +0,0 @@ -From be17d3c929027a5ef9f5024aa1bb4112006bb6f6 Mon Sep 17 00:00:00 2001 -From: Florence Blanc-Renaud -Date: Mon, 6 Aug 2018 16:57:48 +0200 -Subject: [PATCH] Fix ipa-restore: create /var/run/ipa files - -In ipa 4.5.4, ipa-restore fails because the file /etc/tmpfiles.d/ipa.conf -is not restored => /var/run/ipa and /var/run/ipa/ccaches directories -are not created. - -The fix creates these directories in ipa-restore and creates ipa.conf. -With this approach, the fix allows to restore a backup done with 4.5.4 -prior to the fix. - -Note: the fix is specific to ipa-4-5, in ipa-4-6 and above version the file -/etc/tmpfiles.d/ipa.conf is created at package install time and not at -ipa server install time. - -Fixes: https://pagure.io/freeipa/issue/7571 -Reviewed-By: Christian Heimes ---- - ipaserver/install/ipa_restore.py | 3 +++ - 1 file changed, 3 insertions(+) - -diff --git a/ipaserver/install/ipa_restore.py b/ipaserver/install/ipa_restore.py -index a3824df230857b02b47c12645fadee1200afdf66..e069b8ba9b7a01e7602e66d4404063bbc2a1e795 100644 ---- a/ipaserver/install/ipa_restore.py -+++ b/ipaserver/install/ipa_restore.py -@@ -32,6 +32,7 @@ from six.moves.configparser import SafeConfigParser - from ipaclient.install.client import update_ipa_nssdb - from ipalib import api, errors - from ipalib.constants import FQDN -+from ipalib.constants import IPAAPI_USER - from ipapython import version, ipautil - from ipapython.ipautil import run, user_input - from ipapython import admintool -@@ -377,6 +378,8 @@ class Restore(admintool.AdminTool): - if restore_type == 'FULL': - self.remove_old_files() - self.cert_restore_prepare() -+ tasks.create_tmpfiles_dirs(IPAAPI_USER) -+ tasks.configure_tmpfiles() - self.file_restore(options.no_logs) - self.cert_restore() - if 'CA' in self.backup_services: --- -2.17.1 - diff --git a/SOURCES/0056-Sort-and-shuffle-SRV-record-by-priority-and-weight.patch b/SOURCES/0056-Sort-and-shuffle-SRV-record-by-priority-and-weight.patch deleted file mode 100644 index fcb6006..0000000 --- a/SOURCES/0056-Sort-and-shuffle-SRV-record-by-priority-and-weight.patch +++ /dev/null @@ -1,427 +0,0 @@ -From 9e2b0c4b026f281507d1879da8398841270bc20f Mon Sep 17 00:00:00 2001 -From: Christian Heimes -Date: Fri, 15 Jun 2018 17:03:29 +0200 -Subject: [PATCH] Sort and shuffle SRV record by priority and weight - -On multiple occasions, SRV query answers were not properly sorted by -priority. Records with same priority weren't randomized and shuffled. -This caused FreeIPA to contact the same remote peer instead of -distributing the load across all available servers. - -Two new helper functions now take care of SRV queries. sort_prio_weight() -sorts SRV and URI records. query_srv() combines SRV lookup with -sort_prio_weight(). - -Fixes: https://pagure.io/freeipa/issue/7475 -Signed-off-by: Christian Heimes -Reviewed-By: Rob Crittenden ---- - ipaclient/install/ipadiscovery.py | 3 +- - ipalib/rpc.py | 21 ++--- - ipalib/util.py | 11 ++- - ipapython/config.py | 8 +- - ipapython/dnsutil.py | 92 +++++++++++++++++++- - ipaserver/dcerpc.py | 4 +- - ipatests/test_ipapython/test_dnsutil.py | 106 ++++++++++++++++++++++++ - 7 files changed, 217 insertions(+), 28 deletions(-) - create mode 100644 ipatests/test_ipapython/test_dnsutil.py - -diff --git a/ipaclient/install/ipadiscovery.py b/ipaclient/install/ipadiscovery.py -index 46e05c971647b4f0fb4e6044ef74aff3e7919632..34142179a9f4957e842769d9d4036d2024130793 100644 ---- a/ipaclient/install/ipadiscovery.py -+++ b/ipaclient/install/ipadiscovery.py -@@ -25,6 +25,7 @@ from ipapython.ipa_log_manager import root_logger - from dns import resolver, rdatatype - from dns.exception import DNSException - from ipalib import errors -+from ipapython.dnsutil import query_srv - from ipapython import ipaldap - from ipaplatform.paths import paths - from ipapython.ipautil import valid_ip, realm_to_suffix -@@ -492,7 +493,7 @@ class IPADiscovery(object): - root_logger.debug("Search DNS for SRV record of %s", qname) - - try: -- answers = resolver.query(qname, rdatatype.SRV) -+ answers = query_srv(qname) - except DNSException as e: - root_logger.debug("DNS record not found: %s", e.__class__.__name__) - answers = [] -diff --git a/ipalib/rpc.py b/ipalib/rpc.py -index e3b8d67d69c084ad1a43390b5f93061826a27e1d..e74807d57955cd36aa8622b4441e08ee89cd313e 100644 ---- a/ipalib/rpc.py -+++ b/ipalib/rpc.py -@@ -43,7 +43,6 @@ import socket - import gzip - - import gssapi --from dns import resolver, rdatatype - from dns.exception import DNSException - from ssl import SSLError - import six -@@ -59,7 +58,7 @@ from ipapython.ipa_log_manager import root_logger - from ipapython import ipautil - from ipapython import session_storage - from ipapython.cookie import Cookie --from ipapython.dnsutil import DNSName -+from ipapython.dnsutil import DNSName, query_srv - from ipalib.text import _ - from ipalib.util import create_https_connection - from ipalib.krb_utils import KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN, KRB5KRB_AP_ERR_TKT_EXPIRED, \ -@@ -853,7 +852,7 @@ class RPCClient(Connectible): - name = '_ldap._tcp.%s.' % self.env.domain - - try: -- answers = resolver.query(name, rdatatype.SRV) -+ answers = query_srv(name) - except DNSException: - answers = [] - -@@ -861,17 +860,11 @@ class RPCClient(Connectible): - server = str(answer.target).rstrip(".") - servers.append('https://%s%s' % (ipautil.format_netloc(server), path)) - -- servers = list(set(servers)) -- # the list/set conversion won't preserve order so stick in the -- # local config file version here. -- cfg_server = rpc_uri -- if cfg_server in servers: -- # make sure the configured master server is there just once and -- # it is the first one -- servers.remove(cfg_server) -- servers.insert(0, cfg_server) -- else: -- servers.insert(0, cfg_server) -+ # make sure the configured master server is there just once and -+ # it is the first one. -+ if rpc_uri in servers: -+ servers.remove(rpc_uri) -+ servers.insert(0, rpc_uri) - - return servers - -diff --git a/ipalib/util.py b/ipalib/util.py -index 6ee65498b4de674fe4b2ee361541d3bfe648bba0..56db48638e8319859850fba449ed7c23b6e909ab 100644 ---- a/ipalib/util.py -+++ b/ipalib/util.py -@@ -934,14 +934,13 @@ def detect_dns_zone_realm_type(api, domain): - - try: - # The presence of this record is enough, return foreign in such case -- result = resolver.query(ad_specific_record_name, rdatatype.SRV) -- return 'foreign' -- -+ resolver.query(ad_specific_record_name, rdatatype.SRV) - except DNSException: -- pass -+ # If we could not detect type with certainty, return unknown -+ return 'unknown' -+ else: -+ return 'foreign' - -- # If we could not detect type with certainity, return unknown -- return 'unknown' - - def has_managed_topology(api): - domainlevel = api.Command['domainlevel_get']().get('result', DOMAIN_LEVEL_0) -diff --git a/ipapython/config.py b/ipapython/config.py -index 19abfc51ee354d2971be836fa6bad70eea3a6720..44c823b6b946c28a510e5f156061eba0b05aa059 100644 ---- a/ipapython/config.py -+++ b/ipapython/config.py -@@ -24,7 +24,6 @@ from optparse import ( - from copy import copy - import socket - --from dns import resolver, rdatatype - from dns.exception import DNSException - import dns.name - # pylint: disable=import-error -@@ -33,6 +32,7 @@ from six.moves.urllib.parse import urlsplit - # pylint: enable=import-error - - from ipapython.dn import DN -+from ipapython.dnsutil import query_srv - - try: - # pylint: disable=ipa-forbidden-import -@@ -195,7 +195,7 @@ def __discover_config(discover_server = True): - name = "_ldap._tcp." + domain - - try: -- servers = resolver.query(name, rdatatype.SRV) -+ servers = query_srv(name) - except DNSException: - # try cycling on domain components of FQDN - try: -@@ -210,7 +210,7 @@ def __discover_config(discover_server = True): - return False - name = "_ldap._tcp.%s" % domain - try: -- servers = resolver.query(name, rdatatype.SRV) -+ servers = query_srv(name) - break - except DNSException: - pass -@@ -221,7 +221,7 @@ def __discover_config(discover_server = True): - if not servers: - name = "_ldap._tcp.%s." % config.default_domain - try: -- servers = resolver.query(name, rdatatype.SRV) -+ servers = query_srv(name) - except DNSException: - pass - -diff --git a/ipapython/dnsutil.py b/ipapython/dnsutil.py -index 011b722dac3e181ac52f7d92d9f44d31c5e2e6bb..25435ba51e6e7c2c6581b60eb077dd133dd29724 100644 ---- a/ipapython/dnsutil.py -+++ b/ipapython/dnsutil.py -@@ -17,10 +17,15 @@ - # along with this program. If not, see . - # - -+import copy -+import operator -+import random -+ - import dns.name - import dns.exception - import dns.resolver --import copy -+import dns.rdataclass -+import dns.rdatatype - - import six - -@@ -369,3 +374,88 @@ def check_zone_overlap(zone, raise_on_error=True): - if ns: - msg += u" and is handled by server(s): {0}".format(', '.join(ns)) - raise ValueError(msg) -+ -+ -+def _mix_weight(records): -+ """Weighted population sorting for records with same priority -+ """ -+ # trivial case -+ if len(records) <= 1: -+ return records -+ -+ # Optimization for common case: If all weights are the same (e.g. 0), -+ # just shuffle the records, which is about four times faster. -+ if all(rr.weight == records[0].weight for rr in records): -+ random.shuffle(records) -+ return records -+ -+ noweight = 0.01 # give records with 0 weight a small chance -+ result = [] -+ records = set(records) -+ while len(records) > 1: -+ # Compute the sum of the weights of those RRs. Then choose a -+ # uniform random number between 0 and the sum computed (inclusive). -+ urn = random.uniform(0, sum(rr.weight or noweight for rr in records)) -+ # Select the RR whose running sum value is the first in the selected -+ # order which is greater than or equal to the random number selected. -+ acc = 0. -+ for rr in records.copy(): -+ acc += rr.weight or noweight -+ if acc >= urn: -+ records.remove(rr) -+ result.append(rr) -+ if records: -+ result.append(records.pop()) -+ return result -+ -+ -+def sort_prio_weight(records): -+ """RFC 2782 sorting algorithm for SRV and URI records -+ -+ RFC 2782 defines a sorting algorithms for SRV records, that is also used -+ for sorting URI records. Records are sorted by priority and than randomly -+ shuffled according to weight. -+ -+ This implementation also removes duplicate entries. -+ """ -+ # order records by priority -+ records = sorted(records, key=operator.attrgetter("priority")) -+ -+ # remove duplicate entries -+ uniquerecords = [] -+ seen = set() -+ for rr in records: -+ # A SRV record has target and port, URI just has target. -+ target = (rr.target, getattr(rr, "port", None)) -+ if target not in seen: -+ uniquerecords.append(rr) -+ seen.add(target) -+ -+ # weighted randomization of entries with same priority -+ result = [] -+ sameprio = [] -+ for rr in uniquerecords: -+ # add all items with same priority in a bucket -+ if not sameprio or sameprio[0].priority == rr.priority: -+ sameprio.append(rr) -+ else: -+ # got different priority, shuffle bucket -+ result.extend(_mix_weight(sameprio)) -+ # start a new priority list -+ sameprio = [rr] -+ # add last batch of records with same priority -+ if sameprio: -+ result.extend(_mix_weight(sameprio)) -+ return result -+ -+ -+def query_srv(qname, resolver=None, **kwargs): -+ """Query SRV records and sort reply according to RFC 2782 -+ -+ :param qname: query name, _service._proto.domain. -+ :return: list of dns.rdtypes.IN.SRV.SRV instances -+ """ -+ if resolver is None: -+ resolver = dns.resolver -+ answer = resolver.query(qname, rdtype=dns.rdatatype.SRV, **kwargs) -+ return sort_prio_weight(answer) -diff --git a/ipaserver/dcerpc.py b/ipaserver/dcerpc.py -index ac1b2a34784df491a3851aa21bbadbec2297241c..4e957b19292f51a7f6e3540dc38590737c7ae5e4 100644 ---- a/ipaserver/dcerpc.py -+++ b/ipaserver/dcerpc.py -@@ -30,6 +30,7 @@ from ipalib import errors - from ipapython import ipautil - from ipapython.ipa_log_manager import root_logger - from ipapython.dn import DN -+from ipapython.dnsutil import query_srv - from ipaserver.install import installutils - from ipaserver.dcerpc_common import (TRUST_BIDIRECTIONAL, - TRUST_JOIN_EXTERNAL, -@@ -55,7 +56,6 @@ import samba - import ldap as _ldap - from ipapython import ipaldap - from ipapython.dnsutil import DNSName --from dns import resolver, rdatatype - from dns.exception import DNSException - import pysss_nss_idmap - import pysss -@@ -795,7 +795,7 @@ class DomainValidator(object): - gc_name = '_gc._tcp.%s.' % info['dns_domain'] - - try: -- answers = resolver.query(gc_name, rdatatype.SRV) -+ answers = query_srv(gc_name) - except DNSException as e: - answers = [] - -diff --git a/ipatests/test_ipapython/test_dnsutil.py b/ipatests/test_ipapython/test_dnsutil.py -new file mode 100644 -index 0000000000000000000000000000000000000000..36adb077cf38f6d036aa1048b201dee7d08eb310 ---- /dev/null -+++ b/ipatests/test_ipapython/test_dnsutil.py -@@ -0,0 +1,106 @@ -+# -+# Copyright (C) 2018 FreeIPA Contributors. See COPYING for license -+# -+import dns.name -+import dns.rdataclass -+import dns.rdatatype -+from dns.rdtypes.IN.SRV import SRV -+from dns.rdtypes.ANY.URI import URI -+ -+from ipapython import dnsutil -+ -+import pytest -+ -+ -+def mksrv(priority, weight, port, target): -+ return SRV( -+ rdclass=dns.rdataclass.IN, -+ rdtype=dns.rdatatype.SRV, -+ priority=priority, -+ weight=weight, -+ port=port, -+ target=dns.name.from_text(target) -+ ) -+ -+ -+def mkuri(priority, weight, target): -+ return URI( -+ rdclass=dns.rdataclass.IN, -+ rdtype=dns.rdatatype.URI, -+ priority=priority, -+ weight=weight, -+ target=target -+ ) -+ -+ -+class TestSortSRV(object): -+ def test_empty(self): -+ assert dnsutil.sort_prio_weight([]) == [] -+ -+ def test_one(self): -+ h1 = mksrv(1, 0, 443, u"host1") -+ assert dnsutil.sort_prio_weight([h1]) == [h1] -+ -+ h2 = mksrv(10, 5, 443, u"host2") -+ assert dnsutil.sort_prio_weight([h2]) == [h2] -+ -+ def test_prio(self): -+ h1 = mksrv(1, 0, 443, u"host1") -+ h2 = mksrv(2, 0, 443, u"host2") -+ h3 = mksrv(3, 0, 443, u"host3") -+ assert dnsutil.sort_prio_weight([h3, h2, h1]) == [h1, h2, h3] -+ assert dnsutil.sort_prio_weight([h3, h3, h3]) == [h3] -+ assert dnsutil.sort_prio_weight([h2, h2, h1, h1]) == [h1, h2] -+ -+ h380 = mksrv(4, 0, 80, u"host3") -+ assert dnsutil.sort_prio_weight([h1, h3, h380]) == [h1, h3, h380] -+ -+ hs = mksrv(-1, 0, 443, u"special") -+ assert dnsutil.sort_prio_weight([h1, h2, hs]) == [hs, h1, h2] -+ -+ def assert_permutations(self, answers, permutations): -+ seen = set() -+ for _unused in range(1000): -+ result = tuple(dnsutil.sort_prio_weight(answers)) -+ assert result in permutations -+ seen.add(result) -+ if seen == permutations: -+ break -+ else: -+ pytest.fail("sorting didn't exhaust all permutations.") -+ -+ def test_sameprio(self): -+ h1 = mksrv(1, 0, 443, u"host1") -+ h2 = mksrv(1, 0, 443, u"host2") -+ permutations = { -+ (h1, h2), -+ (h2, h1), -+ } -+ self.assert_permutations([h1, h2], permutations) -+ -+ def test_weight(self): -+ h1 = mksrv(1, 0, 443, u"host1") -+ h2_w15 = mksrv(2, 15, 443, u"host2") -+ h3_w10 = mksrv(2, 10, 443, u"host3") -+ -+ permutations = { -+ (h1, h2_w15, h3_w10), -+ (h1, h3_w10, h2_w15), -+ } -+ self.assert_permutations([h1, h2_w15, h3_w10], permutations) -+ -+ def test_large(self): -+ records = tuple( -+ mksrv(1, i, 443, "host{}".format(i)) for i in range(1000) -+ ) -+ assert len(dnsutil.sort_prio_weight(records)) == len(records) -+ -+ -+class TestSortURI(object): -+ def test_prio(self): -+ h1 = mkuri(1, 0, u"https://host1/api") -+ h2 = mkuri(2, 0, u"https://host2/api") -+ h3 = mkuri(3, 0, u"https://host3/api") -+ assert dnsutil.sort_prio_weight([h3, h2, h1]) == [h1, h2, h3] -+ assert dnsutil.sort_prio_weight([h3, h3, h3]) == [h3] -+ assert dnsutil.sort_prio_weight([h2, h2, h1, h1]) == [h1, h2] --- -2.17.1 - diff --git a/SOURCES/0057-Always-set-ca_host-when-installing-replica.patch b/SOURCES/0057-Always-set-ca_host-when-installing-replica.patch deleted file mode 100644 index bb73ba1..0000000 --- a/SOURCES/0057-Always-set-ca_host-when-installing-replica.patch +++ /dev/null @@ -1,43 +0,0 @@ -From f084ab0a6828efd8d76b4495e800f7cc7158d909 Mon Sep 17 00:00:00 2001 -From: Christian Heimes -Date: Tue, 19 Jun 2018 19:10:27 +0200 -Subject: [PATCH] Always set ca_host when installing replica - -ipa-replica-install only set ca_host in its temporary -/etc/ipa/default.conf, when it wasn't installing a replica with CA. As a -consequence, the replica installer was picking a random CA server from -LDAP. - -Always set the replication peer as ca_host. This will ensure that the -installer uses the same replication peer for CA. In case the replication -peer is not a CA master, the installer will automatically pick another -host later. - -See: https://pagure.io/freeipa/issue/7566 -Signed-off-by: Christian Heimes -Reviewed-By: Fraser Tweedale ---- - ipaserver/install/server/replicainstall.py | 6 ++---- - 1 file changed, 2 insertions(+), 4 deletions(-) - -diff --git a/ipaserver/install/server/replicainstall.py b/ipaserver/install/server/replicainstall.py -index 8668859ba5da427ea6e752ed52af057635061f23..b10f761e3f643f9fa868451192fa4550b24b6b16 100644 ---- a/ipaserver/install/server/replicainstall.py -+++ b/ipaserver/install/server/replicainstall.py -@@ -236,11 +236,9 @@ def create_ipa_conf(fstore, config, ca_enabled, master=None): - gopts.extend([ - ipaconf.setOption('enable_ra', 'True'), - ipaconf.setOption('ra_plugin', 'dogtag'), -- ipaconf.setOption('dogtag_version', '10') -+ ipaconf.setOption('dogtag_version', '10'), -+ ipaconf.setOption('ca_host', config.ca_host_name) - ]) -- -- if not config.setup_ca: -- gopts.append(ipaconf.setOption('ca_host', config.ca_host_name)) - else: - gopts.extend([ - ipaconf.setOption('enable_ra', 'False'), --- -2.17.1 - diff --git a/SOURCES/0058-Improve-and-fix-timeout-bug-in-wait_for_entry.patch b/SOURCES/0058-Improve-and-fix-timeout-bug-in-wait_for_entry.patch deleted file mode 100644 index 78bf47d..0000000 --- a/SOURCES/0058-Improve-and-fix-timeout-bug-in-wait_for_entry.patch +++ /dev/null @@ -1,110 +0,0 @@ -From f52d626c166ad0f94f9d3752a4d8978dd3d9ccfc Mon Sep 17 00:00:00 2001 -From: Christian Heimes -Date: Fri, 22 Jun 2018 09:39:26 +0200 -Subject: [PATCH] Improve and fix timeout bug in wait_for_entry() - -replication.wait_for_entry() now can wait for an attribute value to -appear on a replica. - -Fixed timeout handling caused by bad rounding and comparison. For small -timeouts, the actual time was rounded down. For example for 60 seconds -timeout and fast replica, the query accumulated to about 0.45 seconds -plus 60 seconds sleep. 60.45 is large enough to terminate the loop -"while int(time.time()) < timeout", but not large enough to trigger the -exception in "if int(time.time()) > timeout", because int(60.65) == 60. - -See: https://pagure.io/freeipa/issue/7593 -Fixes: https://pagure.io/freeipa/issue/7595 -Signed-off-by: Christian Heimes -Reviewed-By: Fraser Tweedale ---- - ipaserver/install/replication.py | 57 +++++++++++++++++--------------- - 1 file changed, 31 insertions(+), 26 deletions(-) - -diff --git a/ipaserver/install/replication.py b/ipaserver/install/replication.py -index d4b41caa45409fa1537ae10f182599307f3e0439..5a491f248236c8d2166484d0db2acccb28ccf178 100644 ---- a/ipaserver/install/replication.py -+++ b/ipaserver/install/replication.py -@@ -17,7 +17,9 @@ - # along with this program. If not, see . - # - --from __future__ import print_function -+from __future__ import print_function, absolute_import -+ -+import itertools - - import six - import time -@@ -170,40 +172,43 @@ def wait_for_task(conn, dn): - return exit_code - - --def wait_for_entry(connection, dn, timeout=7200, attr='', quiet=True): -- """Wait for entry and/or attr to show up""" -- -- filter = "(objectclass=*)" -+def wait_for_entry(connection, dn, timeout=7200, attr=None, attrvalue='*', -+ quiet=True): -+ """Wait for entry and/or attr to show up -+ """ -+ log = root_logger.debug if quiet else root_logger.info - attrlist = [] -- if attr: -- filter = "(%s=*)" % attr -+ if attr is not None: -+ filterstr = ipaldap.LDAPClient.make_filter_from_attr(attr, attrvalue) - attrlist.append(attr) -- timeout += int(time.time()) -- -- if not quiet: -- sys.stdout.write("Waiting for %s %s:%s " % (connection, dn, attr)) -- sys.stdout.flush() -- entry = None -- while not entry and int(time.time()) < timeout: -+ else: -+ filterstr = "(objectclass=*)" -+ log("Waiting for replication (%s) %s %s", connection, dn, filterstr) -+ entry = [] -+ deadline = time.time() + timeout -+ for i in itertools.count(start=1): - try: -- [entry] = connection.get_entries( -- dn, ldap.SCOPE_BASE, filter, attrlist) -+ entry = connection.get_entries( -+ dn, ldap.SCOPE_BASE, filterstr, attrlist) - except errors.NotFound: - pass # no entry yet - except Exception as e: # badness - root_logger.error("Error reading entry %s: %s", dn, e) - raise -- if not entry: -- if not quiet: -- sys.stdout.write(".") -- sys.stdout.flush() -- time.sleep(1) - -- if not entry and int(time.time()) > timeout: -- raise errors.NotFound( -- reason="wait_for_entry timeout for %s for %s" % (connection, dn)) -- elif entry and not quiet: -- root_logger.error("The waited for entry is: %s", entry) -+ if entry: -+ log("Entry found %r", entry) -+ return -+ elif time.time() > deadline: -+ raise errors.NotFound( -+ reason="wait_for_entry timeout on {} for {}".format( -+ connection, dn -+ ) -+ ) -+ else: -+ if i % 10 == 0: -+ root_logger.debug("Still waiting for replication of %s", dn) -+ time.sleep(1) - - - class ReplicationManager(object): --- -2.17.1 - diff --git a/SOURCES/0059-Use-common-replication-wait-timeout-of-5min.patch b/SOURCES/0059-Use-common-replication-wait-timeout-of-5min.patch deleted file mode 100644 index d752feb..0000000 --- a/SOURCES/0059-Use-common-replication-wait-timeout-of-5min.patch +++ /dev/null @@ -1,123 +0,0 @@ -From 3bf0ee3a4128dc538183ee8e45bc22a3966cfd4b Mon Sep 17 00:00:00 2001 -From: Christian Heimes -Date: Fri, 22 Jun 2018 10:00:24 +0200 -Subject: [PATCH] Use common replication wait timeout of 5min - -Instead of multiple timeout values all over the code base, all -replication waits now use a common timeout value from api.env of 5 -minutes. Waiting for HTTP/replica principal takes 90 to 120 seconds, so -5 minutes seem like a sufficient value for slow setups. - -Fixes: https://pagure.io/freeipa/issue/7595 -Signed-off-by: Christian Heimes -Reviewed-By: Fraser Tweedale ---- - ipalib/constants.py | 2 ++ - ipaserver/install/custodiainstance.py | 4 +++- - ipaserver/install/httpinstance.py | 6 +++++- - ipaserver/install/krbinstance.py | 13 ++++++++----- - ipaserver/install/replication.py | 6 ++++-- - 5 files changed, 22 insertions(+), 9 deletions(-) - -diff --git a/ipalib/constants.py b/ipalib/constants.py -index ab466bab7fc17a563a849e2cd9bb89515caff77b..b6a79ce716b3ee0f832390f5bc895c3bb9d37e33 100644 ---- a/ipalib/constants.py -+++ b/ipalib/constants.py -@@ -142,6 +142,8 @@ DEFAULT_CONFIG = ( - ('startup_timeout', 300), - # How long http connection should wait for reply [seconds]. - ('http_timeout', 30), -+ # How long to wait for an entry to appear on a replica -+ ('replication_wait_timeout', 300), - - # Web Application mount points - ('mount_ipa', '/ipa/'), -diff --git a/ipaserver/install/custodiainstance.py b/ipaserver/install/custodiainstance.py -index ada8d03a6914e19c186264f68178cce2442945ca..b37032974e4825a3f3043929171533e4d94730e9 100644 ---- a/ipaserver/install/custodiainstance.py -+++ b/ipaserver/install/custodiainstance.py -@@ -4,6 +4,7 @@ from __future__ import print_function, absolute_import - - import enum - -+from ipalib import api - from ipaserver.secrets.kem import IPAKEMKeys, KEMLdap - from ipaserver.secrets.client import CustodiaClient - from ipaplatform.paths import paths -@@ -190,7 +191,8 @@ class CustodiaInstance(SimpleServiceInstance): - cli = self._get_custodia_client() - cli.fetch_key('dm/DMHash') - -- def _wait_keys(self, timeout=300): -+ def _wait_keys(self): -+ timeout = api.env.replication_wait_timeout - deadline = int(time.time()) + timeout - root_logger.info("Waiting up to %s seconds to see our keys " - "appear on host %s", timeout, self.ldap_uri) -diff --git a/ipaserver/install/httpinstance.py b/ipaserver/install/httpinstance.py -index 434c2549dd868554735f4bcedc4cdceb23eeccdd..e68bfc09b34e087dfb4872b6565b06c6c2188384 100644 ---- a/ipaserver/install/httpinstance.py -+++ b/ipaserver/install/httpinstance.py -@@ -617,4 +617,8 @@ class HTTPInstance(service.Service): - else: - remote_ldap.simple_bind(ipaldap.DIRMAN_DN, - self.dm_password) -- replication.wait_for_entry(remote_ldap, service_dn, timeout=60) -+ replication.wait_for_entry( -+ remote_ldap, -+ service_dn, -+ timeout=api.env.replication_wait_timeout -+ ) -diff --git a/ipaserver/install/krbinstance.py b/ipaserver/install/krbinstance.py -index 34fe46aa8ef297bf69eb74953c956ad9c3d30def..5971a30fc566f6e96ce0b08632772d33da5602d2 100644 ---- a/ipaserver/install/krbinstance.py -+++ b/ipaserver/install/krbinstance.py -@@ -390,13 +390,16 @@ class KrbInstance(service.Service): - def _wait_for_replica_kdc_entry(self): - master_dn = self.api.Object.server.get_dn(self.fqdn) - kdc_dn = DN(('cn', 'KDC'), master_dn) -- -- ldap_uri = 'ldap://{}'.format(self.master_fqdn) -- -+ ldap_uri = ipaldap.get_ldap_uri(self.master_fqdn) - with ipaldap.LDAPClient( -- ldap_uri, cacert=paths.IPA_CA_CRT) as remote_ldap: -+ ldap_uri, cacert=paths.IPA_CA_CRT, start_tls=True -+ ) as remote_ldap: - remote_ldap.gssapi_bind() -- replication.wait_for_entry(remote_ldap, kdc_dn, timeout=60) -+ replication.wait_for_entry( -+ remote_ldap, -+ kdc_dn, -+ timeout=api.env.replication_wait_timeout -+ ) - - def _call_certmonger(self, certmonger_ca='IPA'): - subject = str(DN(('cn', self.fqdn), self.subject_base)) -diff --git a/ipaserver/install/replication.py b/ipaserver/install/replication.py -index 5a491f248236c8d2166484d0db2acccb28ccf178..c017764468674830670a817b3d815c5e2d78728e 100644 ---- a/ipaserver/install/replication.py -+++ b/ipaserver/install/replication.py -@@ -172,7 +172,7 @@ def wait_for_task(conn, dn): - return exit_code - - --def wait_for_entry(connection, dn, timeout=7200, attr=None, attrvalue='*', -+def wait_for_entry(connection, dn, timeout, attr=None, attrvalue='*', - quiet=True): - """Wait for entry and/or attr to show up - """ -@@ -799,7 +799,9 @@ class ReplicationManager(object): - # that we will have to set the memberof fixup task - self.need_memberof_fixup = True - -- wait_for_entry(a_conn, entry.dn) -+ wait_for_entry( -+ a_conn, entry.dn, timeout=api.env.replication_wait_timeout -+ ) - - def needs_memberof_fixup(self): - return self.need_memberof_fixup --- -2.17.1 - diff --git a/SOURCES/0060-Fix-replication-races-in-Dogtag-admin-code.patch b/SOURCES/0060-Fix-replication-races-in-Dogtag-admin-code.patch deleted file mode 100644 index b4eae37..0000000 --- a/SOURCES/0060-Fix-replication-races-in-Dogtag-admin-code.patch +++ /dev/null @@ -1,209 +0,0 @@ -From 9a576f7303fd1b7faae3a419f6d54720f234585b Mon Sep 17 00:00:00 2001 -From: Christian Heimes -Date: Fri, 22 Jun 2018 10:04:38 +0200 -Subject: [PATCH] Fix replication races in Dogtag admin code - -DogtagInstance.setup_admin and related methods have multiple LDAP -replication race conditions. The bugs can cause parallel -ipa-replica-install to fail. - -The code from __add_admin_to_group() has been changed to use MOD_ADD -ather than search + MOD_REPLACE. The MOD_REPLACE approach can lead to -data loss, when more than one writer changes a group. - -setup_admin() now waits until both admin user and group membership have -been replicated to the master peer. The method also adds a new ACI to -allow querying group member in the replication check. - -Fixes: https://pagure.io/freeipa/issue/7593 -Signed-off-by: Christian Heimes -Reviewed-By: Fraser Tweedale ---- - ipaserver/install/cainstance.py | 1 + - ipaserver/install/dogtaginstance.py | 101 ++++++++++++++++++++++------ - ipaserver/install/krainstance.py | 1 + - 3 files changed, 82 insertions(+), 21 deletions(-) - -diff --git a/ipaserver/install/cainstance.py b/ipaserver/install/cainstance.py -index 3a3558a5ded6de3e578574d08a5cdb59338e99c4..0c4d9bf9ad8ae11ac88523857845e16eb62651b9 100644 ---- a/ipaserver/install/cainstance.py -+++ b/ipaserver/install/cainstance.py -@@ -377,6 +377,7 @@ class CAInstance(DogtagInstance): - # Setup Database - self.step("creating certificate server db", self.__create_ds_db) - self.step("setting up initial replication", self.__setup_replication) -+ self.step("creating ACIs for admin", self.add_ipaca_aci) - self.step("creating installation admin user", self.setup_admin) - self.step("configuring certificate server instance", - self.__spawn_instance) -diff --git a/ipaserver/install/dogtaginstance.py b/ipaserver/install/dogtaginstance.py -index 9470e1a13608a8a84aab8a36c269a708e3f3e9f4..960b8cc7ce495bf5ca359f72b46aa0d43ccec5c3 100644 ---- a/ipaserver/install/dogtaginstance.py -+++ b/ipaserver/install/dogtaginstance.py -@@ -18,6 +18,8 @@ - # - - import base64 -+import time -+ - import ldap - import os - import shutil -@@ -84,6 +86,16 @@ class DogtagInstance(service.Service): - tracking_reqs = None - server_cert_name = None - -+ ipaca_groups = DN(('ou', 'groups'), ('o', 'ipaca')) -+ ipaca_people = DN(('ou', 'people'), ('o', 'ipaca')) -+ groups_aci = ( -+ b'(targetfilter="(objectClass=groupOfUniqueNames)")' -+ b'(targetattr="cn || description || objectclass || uniquemember")' -+ b'(version 3.0; acl "Allow users from o=ipaca to read groups"; ' -+ b'allow (read, search, compare) ' -+ b'userdn="ldap:///uid=*,ou=people,o=ipaca";)' -+ ) -+ - def __init__(self, realm, subsystem, service_desc, host_name=None, - nss_db=paths.PKI_TOMCAT_ALIAS_DIR, service_prefix=None): - """Initializer""" -@@ -101,10 +113,11 @@ class DogtagInstance(service.Service): - self.pkcs12_info = None - self.clone = False - -- self.basedn = DN(('o', 'ipa%s' % subsystem.lower())) -+ self.basedn = DN(('o', 'ipaca')) - self.admin_user = "admin" -- self.admin_dn = DN(('uid', self.admin_user), -- ('ou', 'people'), ('o', 'ipaca')) -+ self.admin_dn = DN( -+ ('uid', self.admin_user), self.ipaca_people -+ ) - self.admin_groups = None - self.tmp_agent_db = None - self.subsystem = subsystem -@@ -385,27 +398,33 @@ class DogtagInstance(service.Service): - - raise RuntimeError("%s configuration failed." % self.subsystem) - -- def __add_admin_to_group(self, group): -- dn = DN(('cn', group), ('ou', 'groups'), ('o', 'ipaca')) -- entry = api.Backend.ldap2.get_entry(dn) -- members = entry.get('uniqueMember', []) -- members.append(self.admin_dn) -- mod = [(ldap.MOD_REPLACE, 'uniqueMember', members)] -+ def add_ipaca_aci(self): -+ """Add ACI to allow ipaca users to read their own group information -+ -+ Dogtag users aren't allowed to enumerate their own groups. The -+ setup_admin() method needs the permission to wait, until all group -+ information has been replicated. -+ """ -+ dn = self.ipaca_groups -+ mod = [(ldap.MOD_ADD, 'aci', [self.groups_aci])] - try: - api.Backend.ldap2.modify_s(dn, mod) - except ldap.TYPE_OR_VALUE_EXISTS: -- # already there -- pass -+ self.log.debug( -+ "%s already has ACI to read group information", dn -+ ) -+ else: -+ self.log.debug("Added ACI to read groups to %s", dn) - - def setup_admin(self): - self.admin_user = "admin-%s" % self.fqdn - self.admin_password = ipautil.ipa_generate_password() -- self.admin_dn = DN(('uid', self.admin_user), -- ('ou', 'people'), ('o', 'ipaca')) -- -+ self.admin_dn = DN( -+ ('uid', self.admin_user), self.ipaca_people -+ ) - # remove user if left-over exists - try: -- entry = api.Backend.ldap2.delete_entry(self.admin_dn) -+ api.Backend.ldap2.delete_entry(self.admin_dn) - except errors.NotFound: - pass - -@@ -424,18 +443,58 @@ class DogtagInstance(service.Service): - ) - api.Backend.ldap2.add_entry(entry) - -+ wait_groups = [] - for group in self.admin_groups: -- self.__add_admin_to_group(group) -+ group_dn = DN(('cn', group), self.ipaca_groups) -+ mod = [(ldap.MOD_ADD, 'uniqueMember', [self.admin_dn])] -+ try: -+ api.Backend.ldap2.modify_s(group_dn, mod) -+ except ldap.TYPE_OR_VALUE_EXISTS: -+ # already there -+ return None -+ else: -+ wait_groups.append(group_dn) - - # Now wait until the other server gets replicated this data - ldap_uri = ipaldap.get_ldap_uri(self.master_host) -- master_conn = ipaldap.LDAPClient(ldap_uri) -- master_conn.gssapi_bind() -- replication.wait_for_entry(master_conn, entry.dn) -- del master_conn -+ master_conn = ipaldap.LDAPClient( -+ ldap_uri, start_tls=True, cacert=paths.IPA_CA_CRT -+ ) -+ self.log.debug( -+ "Waiting for %s to appear on %s", self.admin_dn, master_conn -+ ) -+ deadline = time.time() + api.env.replication_wait_timeout -+ while time.time() < deadline: -+ time.sleep(1) -+ try: -+ master_conn.simple_bind(self.admin_dn, self.admin_password) -+ except ldap.INVALID_CREDENTIALS: -+ pass -+ else: -+ self.log.debug("Successfully logged in as %s", self.admin_dn) -+ break -+ else: -+ self.log.error( -+ "Unable to log in as %s on %s", self.admin_dn, master_conn -+ ) -+ raise errors.NotFound( -+ reason="{} did not replicate to {}".format( -+ self.admin_dn, master_conn -+ ) -+ ) -+ -+ # wait for group membership -+ for group_dn in wait_groups: -+ replication.wait_for_entry( -+ master_conn, -+ group_dn, -+ timeout=api.env.replication_wait_timeout, -+ attr='uniqueMember', -+ attrvalue=self.admin_dn -+ ) - - def __remove_admin_from_group(self, group): -- dn = DN(('cn', group), ('ou', 'groups'), ('o', 'ipaca')) -+ dn = DN(('cn', group), self.ipaca_groups) - mod = [(ldap.MOD_DELETE, 'uniqueMember', self.admin_dn)] - try: - api.Backend.ldap2.modify_s(dn, mod) -diff --git a/ipaserver/install/krainstance.py b/ipaserver/install/krainstance.py -index 990bb87ca2f0029d2450cbef47958399f534f2a6..915d3c3c6e038eeb6a8f94f1ed7f7008c0ef4ead 100644 ---- a/ipaserver/install/krainstance.py -+++ b/ipaserver/install/krainstance.py -@@ -111,6 +111,7 @@ class KRAInstance(DogtagInstance): - "A Dogtag CA must be installed first") - - if promote: -+ self.step("creating ACIs for admin", self.add_ipaca_aci) - self.step("creating installation admin user", self.setup_admin) - self.step("configuring KRA instance", self.__spawn_instance) - if not self.clone: --- -2.17.1 - diff --git a/SOURCES/0061-Increase-WSGI-process-count-to-5-on-64bit.patch b/SOURCES/0061-Increase-WSGI-process-count-to-5-on-64bit.patch deleted file mode 100644 index ff15210..0000000 --- a/SOURCES/0061-Increase-WSGI-process-count-to-5-on-64bit.patch +++ /dev/null @@ -1,88 +0,0 @@ -From 6110a949aee9c98ab077c9cd907881ad82be5f45 Mon Sep 17 00:00:00 2001 -From: Christian Heimes -Date: Thu, 14 Jun 2018 17:04:13 +0200 -Subject: [PATCH] Increase WSGI process count to 5 on 64bit - -Increase the WSGI daemon worker process count from 2 processes to 5 -processes. This allows IPA RPC to handle more parallel requests. The -additional processes increase memory consumption by approximante 250 MB -in total. - -Since memory is scarce on 32bit platforms, only 64bit platforms are -bumped to 5 workers. - -Fixes: https://pagure.io/freeipa/issue/7587 -Signed-off-by: Christian Heimes -Reviewed-By: Alexander Bokovoy ---- - install/conf/ipa.conf | 2 +- - ipaplatform/base/constants.py | 5 +++++ - ipaserver/install/httpinstance.py | 1 + - ipaserver/install/server/upgrade.py | 3 ++- - 4 files changed, 9 insertions(+), 2 deletions(-) - -diff --git a/install/conf/ipa.conf b/install/conf/ipa.conf -index 01bf9a4f97fc0cf197c0ad12743affa597b54911..34ced2ab9d91ae174a42a580e1c4f9436c1a8c3b 100644 ---- a/install/conf/ipa.conf -+++ b/install/conf/ipa.conf -@@ -51,7 +51,7 @@ WSGISocketPrefix /run/httpd/wsgi - - - # Configure mod_wsgi handler for /ipa --WSGIDaemonProcess ipa processes=2 threads=1 maximum-requests=500 \ -+WSGIDaemonProcess ipa processes=$WSGI_PROCESSES threads=1 maximum-requests=500 \ - user=ipaapi group=ipaapi display-name=%{GROUP} socket-timeout=2147483647 - WSGIImportScript /usr/share/ipa/wsgi.py process-group=ipa application-group=ipa - WSGIScriptAlias /ipa /usr/share/ipa/wsgi.py -diff --git a/ipaplatform/base/constants.py b/ipaplatform/base/constants.py -index dccb0e7191cb0d9644eb286b9ec061599afa3980..db250d9a40466e852453e7309c704a6897c6bcf8 100644 ---- a/ipaplatform/base/constants.py -+++ b/ipaplatform/base/constants.py -@@ -5,9 +5,11 @@ - ''' - This base platform module exports platform dependant constants. - ''' -+import sys - - - class BaseConstantsNamespace(object): -+ IS_64BITS = sys.maxsize > 2 ** 32 - DS_USER = 'dirsrv' - DS_GROUP = 'dirsrv' - HTTPD_USER = "apache" -@@ -28,3 +30,6 @@ class BaseConstantsNamespace(object): - # nfsd init variable used to enable kerberized NFS - SECURE_NFS_VAR = "SECURE_NFS" - SSSD_USER = "sssd" -+ # WSGIDaemonProcess process count. On 64bit platforms, each process -+ # consumes about 110 MB RSS, from which are about 35 MB shared. -+ WSGI_PROCESSES = 5 if IS_64BITS else 2 -diff --git a/ipaserver/install/httpinstance.py b/ipaserver/install/httpinstance.py -index e68bfc09b34e087dfb4872b6565b06c6c2188384..7081c7418e76afbd1b4ae28deafefb6b264c62f0 100644 ---- a/ipaserver/install/httpinstance.py -+++ b/ipaserver/install/httpinstance.py -@@ -152,6 +152,7 @@ class HTTPInstance(service.Service): - DOMAIN=self.domain, - AUTOREDIR='' if auto_redirect else '#', - CRL_PUBLISH_PATH=paths.PKI_CA_PUBLISH_DIR, -+ WSGI_PROCESSES=constants.WSGI_PROCESSES, - ) - self.ca_file = ca_file - if ca_is_configured is not None: -diff --git a/ipaserver/install/server/upgrade.py b/ipaserver/install/server/upgrade.py -index 793092be86da687fd21cf2c0ef3608f32fcf9f16..667b9d214ce76031b5d0f205e03ddb46178e9b2f 100644 ---- a/ipaserver/install/server/upgrade.py -+++ b/ipaserver/install/server/upgrade.py -@@ -1615,7 +1615,8 @@ def upgrade_configuration(): - AUTOREDIR='' if auto_redirect else '#', - CRL_PUBLISH_PATH=paths.PKI_CA_PUBLISH_DIR, - DOGTAG_PORT=8009, -- CLONE='#' -+ CLONE='#', -+ WSGI_PROCESSES=constants.WSGI_PROCESSES, - ) - - subject_base = find_subject_base() --- -2.17.1 - diff --git a/SOURCES/0062-Use-4-WSGI-workers-on-64bit-systems.patch b/SOURCES/0062-Use-4-WSGI-workers-on-64bit-systems.patch deleted file mode 100644 index eee7d99..0000000 --- a/SOURCES/0062-Use-4-WSGI-workers-on-64bit-systems.patch +++ /dev/null @@ -1,28 +0,0 @@ -From e9c4bf911675d88c300458faacdc32d2b80a189e Mon Sep 17 00:00:00 2001 -From: Christian Heimes -Date: Mon, 25 Jun 2018 10:59:18 +0200 -Subject: [PATCH] Use 4 WSGI workers on 64bit systems - -Commit f1d5ab3a03191dbb02e5f95308cf8c4f1971cdcf increases WSGI worker -count to five. This turned out to be a bit much for our test systems. -Four workers are good enough and still double the old amount. - -See: https://pagure.io/freeipa/issue/7587 -Signed-off-by: Christian Heimes ---- - ipaplatform/base/constants.py | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/ipaplatform/base/constants.py b/ipaplatform/base/constants.py -index db250d9a40466e852453e7309c704a6897c6bcf8..8cfd46290d6e0021238c2f8d96a795928b2913df 100644 ---- a/ipaplatform/base/constants.py -+++ b/ipaplatform/base/constants.py -@@ -32,4 +32,4 @@ class BaseConstantsNamespace(object): - SSSD_USER = "sssd" - # WSGIDaemonProcess process count. On 64bit platforms, each process - # consumes about 110 MB RSS, from which are about 35 MB shared. -- WSGI_PROCESSES = 5 if IS_64BITS else 2 -+ WSGI_PROCESSES = 4 if IS_64BITS else 2 --- -2.17.1 - diff --git a/SOURCES/0063-Catch-ACIError-instead-of-invalid-credentials.patch b/SOURCES/0063-Catch-ACIError-instead-of-invalid-credentials.patch deleted file mode 100644 index ac76cb3..0000000 --- a/SOURCES/0063-Catch-ACIError-instead-of-invalid-credentials.patch +++ /dev/null @@ -1,36 +0,0 @@ -From 699104cf8b8d0ec2570b3801e86c6a358343527a Mon Sep 17 00:00:00 2001 -From: Christian Heimes -Date: Fri, 29 Jun 2018 11:08:45 +0200 -Subject: [PATCH] Catch ACIError instead of invalid credentials - -ipaldap's LDAPClient client turns INVALID_CREDENTIAL error into -ACIError. Catch the ACIError and wait until the user has been -replicated. - -Apparently no manual or automated test ran into the timeout during -testing. - -Fixes: Fixes: https://pagure.io/freeipa/issue/7593 -Signed-off-by: Christian Heimes -Reviewed-By: Alexander Bokovoy ---- - ipaserver/install/dogtaginstance.py | 3 ++- - 1 file changed, 2 insertions(+), 1 deletion(-) - -diff --git a/ipaserver/install/dogtaginstance.py b/ipaserver/install/dogtaginstance.py -index 960b8cc7ce495bf5ca359f72b46aa0d43ccec5c3..1f9742b287f58ed117aba627ad85ac3ced4b2645 100644 ---- a/ipaserver/install/dogtaginstance.py -+++ b/ipaserver/install/dogtaginstance.py -@@ -468,7 +468,8 @@ class DogtagInstance(service.Service): - time.sleep(1) - try: - master_conn.simple_bind(self.admin_dn, self.admin_password) -- except ldap.INVALID_CREDENTIALS: -+ except errors.ACIError: -+ # user not replicated yet - pass - else: - self.log.debug("Successfully logged in as %s", self.admin_dn) --- -2.17.1 - diff --git a/SOURCES/0064-Query-for-server-role-IPA-master.patch b/SOURCES/0064-Query-for-server-role-IPA-master.patch deleted file mode 100644 index 1d184fb..0000000 --- a/SOURCES/0064-Query-for-server-role-IPA-master.patch +++ /dev/null @@ -1,109 +0,0 @@ -From 500be304c4b218b40acfa31cf987e541958b8985 Mon Sep 17 00:00:00 2001 -From: Christian Heimes -Date: Thu, 5 Jul 2018 23:50:37 +0200 -Subject: [PATCH] Query for server role IPA master - -server_find and server_role plugin were hiding IPA master role -information. It's now possible to fetch IPA master role information and -to filter by IPA master role, e.g. to ignore servers that have some -services configured but not (yet) enabled. - -See: https://pagure.io/freeipa/issue/7566 -Signed-off-by: Christian Heimes -Reviewed-By: Alexander Bokovoy -Reviewed-By: Fraser Tweedale ---- - API.txt | 3 ++- - ipaserver/plugins/server.py | 9 +++++++-- - ipaserver/plugins/serverrole.py | 18 +++++++++++++++--- - 3 files changed, 24 insertions(+), 6 deletions(-) - -diff --git a/API.txt b/API.txt -index 5feed54947e044a0a2c908e70b44fe59a86972ff..7262a4122be06ab3ca2296897de84bea458fcf0a 100644 ---- a/API.txt -+++ b/API.txt -@@ -4421,9 +4421,10 @@ output: Entry('result') - output: Output('summary', type=[, ]) - output: PrimaryKey('value') - command: server_role_find/1 --args: 1,8,4 -+args: 1,9,4 - arg: Str('criteria?') - option: Flag('all', autofill=True, cli_name='all', default=False) -+option: Flag('include_master', autofill=True, default=False) - option: Flag('raw', autofill=True, cli_name='raw', default=False) - option: Str('role_servrole?', autofill=False, cli_name='role') - option: Str('server_server?', autofill=False, cli_name='server') -diff --git a/ipaserver/plugins/server.py b/ipaserver/plugins/server.py -index e0dc953a1ef870c95fdcdb629fb6ab3103e8f999..eb776aa8cf676c26d80c22ec87f8b5a310d0c6dc 100644 ---- a/ipaserver/plugins/server.py -+++ b/ipaserver/plugins/server.py -@@ -199,7 +199,10 @@ class server(LDAPObject): - return - - enabled_roles = self.api.Command.server_role_find( -- server_server=entry_attrs['cn'][0], status=ENABLED)['result'] -+ server_server=entry_attrs['cn'][0], -+ status=ENABLED, -+ include_master=True, -+ )['result'] - - enabled_role_names = [r[u'role_servrole'] for r in enabled_roles] - -@@ -333,7 +336,9 @@ class server_find(LDAPSearch): - role_status = self.api.Command.server_role_find( - server_server=None, - role_servrole=role, -- status=ENABLED)['result'] -+ status=ENABLED, -+ include_master=True, -+ )['result'] - - return set( - r[u'server_server'] for r in role_status) -diff --git a/ipaserver/plugins/serverrole.py b/ipaserver/plugins/serverrole.py -index b5781b0dff4c5d6f433e6a5531fc3e830ffcd972..db88b3885c538c2800f6e4a1d649083859d43641 100644 ---- a/ipaserver/plugins/serverrole.py -+++ b/ipaserver/plugins/serverrole.py -@@ -5,7 +5,7 @@ - from ipalib.crud import Retrieve, Search - from ipalib.errors import NotFound - from ipalib.frontend import Object --from ipalib.parameters import Int, Str, StrEnum -+from ipalib.parameters import Flag, Int, Str, StrEnum - from ipalib.plugable import Registry - from ipalib import _, ngettext - -@@ -129,6 +129,10 @@ class server_role_find(Search): - minvalue=0, - autofill=False, - ), -+ Flag( -+ 'include_master', -+ doc=_('Include IPA master entries'), -+ ) - ) - - def execute(self, *keys, **options): -@@ -151,8 +155,16 @@ class server_role_find(Search): - role_servrole=role_name, - status=status) - -- result = [ -- r for r in role_status if r[u'role_servrole'] != "IPA master"] -+ # Don't display "IPA master" information unless the role is -+ # requested explicitly. All servers are considered IPA masters, -+ # except for replicas during installation. -+ if options.get('include_master') or role_name == "IPA master": -+ result = role_status -+ else: -+ result = [ -+ r for r in role_status -+ if r[u'role_servrole'] != "IPA master" -+ ] - return dict( - result=result, - count=len(result), --- -2.17.1 - diff --git a/SOURCES/0065-Only-create-DNS-SRV-records-for-ready-server.patch b/SOURCES/0065-Only-create-DNS-SRV-records-for-ready-server.patch deleted file mode 100644 index 61ef03b..0000000 --- a/SOURCES/0065-Only-create-DNS-SRV-records-for-ready-server.patch +++ /dev/null @@ -1,50 +0,0 @@ -From 0f6afe8ffa39804d7bb5e86e4aa447f4d56a4dfa Mon Sep 17 00:00:00 2001 -From: Christian Heimes -Date: Thu, 5 Jul 2018 23:59:06 +0200 -Subject: [PATCH] Only create DNS SRV records for ready server - -When installing multiple replicas in parallel, one replica may create -SRV entries for other replicas, although the replicas aren't fully -installed yet. This may cause some services to connect to a server, that -isn't ready to serve requests. - -The DNS IPASystemRecords framework now skips all servers that aren't -ready IPA masters. - -See: https://pagure.io/freeipa/issue/7566 -Signed-off-by: Christian Heimes -Reviewed-By: Alexander Bokovoy -Reviewed-By: Fraser Tweedale ---- - ipaserver/dns_data_management.py | 8 ++++++-- - 1 file changed, 6 insertions(+), 2 deletions(-) - -diff --git a/ipaserver/dns_data_management.py b/ipaserver/dns_data_management.py -index 2008ba6e7d387046b74e3de0af644d97b145ccb7..6016d8a0044d487c3118f43f199b2a433facfa9a 100644 ---- a/ipaserver/dns_data_management.py -+++ b/ipaserver/dns_data_management.py -@@ -93,7 +93,9 @@ class IPASystemRecords(object): - self.servers_data = {} - - servers_result = self.api_instance.Command.server_find( -- no_members=False)['result'] -+ no_members=False, -+ servrole=u"IPA master", # only active, fully installed masters -+ )['result'] - for s in servers_result: - weight, location, roles = self.__get_server_attrs(s) - self.servers_data[s['cn'][0]] = { -@@ -345,7 +347,9 @@ class IPASystemRecords(object): - zone_obj = zone.Zone(self.domain_abs, relativize=False) - if servers is None: - servers_result = self.api_instance.Command.server_find( -- pkey_only=True)['result'] -+ pkey_only=True, -+ servrole=u"IPA master", # only fully installed masters -+ )['result'] - servers = [s['cn'][0] for s in servers_result] - - locations_result = self.api_instance.Command.location_find()['result'] --- -2.17.1 - diff --git a/SOURCES/0066-Delay-enabling-services-until-end-of-installer.patch b/SOURCES/0066-Delay-enabling-services-until-end-of-installer.patch deleted file mode 100644 index 9395c45..0000000 --- a/SOURCES/0066-Delay-enabling-services-until-end-of-installer.patch +++ /dev/null @@ -1,602 +0,0 @@ -From f0d829754e2e35225f0dfba980c9f4bae011407e Mon Sep 17 00:00:00 2001 -From: Christian Heimes -Date: Fri, 6 Jul 2018 00:04:39 +0200 -Subject: [PATCH] Delay enabling services until end of installer - -Service entries in cn=FQDN,cn=masters,cn=ipa,cn=etc are no longer -created as enabled. Instead they are flagged as configuredService. At -the very end of the installer, the service entries are switched from -configured to enabled service. - -- SRV records are created at the very end of the installer. -- Dogtag installer only picks fully installed servers -- Certmonger ignores all configured but not yet enabled servers. - -Fixes: https://pagure.io/freeipa/issue/7566 -Signed-off-by: Christian Heimes -Reviewed-By: Alexander Bokovoy -Reviewed-By: Fraser Tweedale ---- - install/tools/ipa-adtrust-install | 6 +- - install/tools/ipa-ca-install | 12 ++- - install/tools/ipa-dns-install | 4 + - ipaserver/dns_data_management.py | 18 +++-- - ipaserver/install/adtrustinstance.py | 6 +- - ipaserver/install/bindinstance.py | 6 +- - ipaserver/install/cainstance.py | 2 +- - ipaserver/install/dnskeysyncinstance.py | 4 +- - ipaserver/install/httpinstance.py | 4 +- - ipaserver/install/ipa_kra_install.py | 7 ++ - ipaserver/install/krainstance.py | 2 +- - ipaserver/install/krbinstance.py | 4 +- - ipaserver/install/odsexporterinstance.py | 4 +- - ipaserver/install/opendnssecinstance.py | 6 +- - ipaserver/install/server/install.py | 18 +++-- - ipaserver/install/server/replicainstall.py | 9 ++- - ipaserver/install/service.py | 90 ++++++++++++++++++++-- - ipaserver/plugins/serverrole.py | 7 +- - 18 files changed, 161 insertions(+), 48 deletions(-) - -diff --git a/install/tools/ipa-adtrust-install b/install/tools/ipa-adtrust-install -index 1484598adba5b1237f00cc55e95167d45a6b40d7..4258f489873b4095a6672e20f2ac5f2858b71982 100755 ---- a/install/tools/ipa-adtrust-install -+++ b/install/tools/ipa-adtrust-install -@@ -31,7 +31,7 @@ import six - from optparse import SUPPRESS_HELP # pylint: disable=deprecated-module - - from ipalib.install import sysrestore --from ipaserver.install import adtrust -+from ipaserver.install import adtrust, service - from ipaserver.install.installutils import ( - read_password, - check_server_configuration, -@@ -210,6 +210,10 @@ def main(): - adtrust.install_check(True, options, api) - adtrust.install(True, options, fstore, api) - -+ # Enable configured services and update DNS SRV records -+ service.enable_services(api.env.host) -+ api.Command.dns_update_system_records() -+ - print(""" - ============================================================================= - Setup complete -diff --git a/install/tools/ipa-ca-install b/install/tools/ipa-ca-install -index a3694007e5815d1c44f642057c749879d336dfc5..215a60ae744577de73a7260bbf3dea5a09a4d118 100755 ---- a/install/tools/ipa-ca-install -+++ b/install/tools/ipa-ca-install -@@ -303,18 +303,26 @@ def main(): - ) - api.finalize() - api.Backend.ldap2.connect() -- - domain_level = dsinstance.get_domain_level(api) -+ - if domain_level > DOMAIN_LEVEL_0: - promote(safe_options, options, filename) - else: - install(safe_options, options, filename) - -+ # pki-spawn restarts 389-DS, reconnect -+ api.Backend.ldap2.close() -+ api.Backend.ldap2.connect() -+ -+ # Enable configured services and update DNS SRV records -+ service.enable_services(api.env.host) -+ api.Command.dns_update_system_records() -+ api.Backend.ldap2.disconnect() -+ - # execute ipactl to refresh services status - ipautil.run(['ipactl', 'start', '--ignore-service-failures'], - raiseonerr=False) - -- api.Backend.ldap2.disconnect() - - fail_message = ''' - Your system may be partly configured. -diff --git a/install/tools/ipa-dns-install b/install/tools/ipa-dns-install -index cb6c5d887f101135ca593ea6d4ed0caf51478a4c..04d1b140d2c79a0fa72d7df47d556643751bddb7 100755 ---- a/install/tools/ipa-dns-install -+++ b/install/tools/ipa-dns-install -@@ -36,6 +36,7 @@ from ipapython.config import IPAOptionParser - from ipapython.ipa_log_manager import standard_logging_setup, root_logger - - from ipaserver.install import dns as dns_installer -+from ipaserver.install import service - - log_file_name = paths.IPASERVER_INSTALL_LOG - -@@ -145,6 +146,9 @@ def main(): - - dns_installer.install_check(True, api, False, options, hostname=api.env.host) - dns_installer.install(True, False, options) -+ # Enable configured services and update DNS SRV records -+ service.enable_services(api.env.host) -+ api.Command.dns_update_system_records() - - # execute ipactl to refresh services status - ipautil.run(['ipactl', 'start', '--ignore-service-failures'], -diff --git a/ipaserver/dns_data_management.py b/ipaserver/dns_data_management.py -index 6016d8a0044d487c3118f43f199b2a433facfa9a..e5987b4bd7b43d3920e9da917258153e448206b7 100644 ---- a/ipaserver/dns_data_management.py -+++ b/ipaserver/dns_data_management.py -@@ -65,11 +65,11 @@ class IPASystemRecords(object): - PRIORITY_HIGH = 0 - PRIORITY_LOW = 50 - -- def __init__(self, api_instance): -+ def __init__(self, api_instance, all_servers=False): - self.api_instance = api_instance - self.domain_abs = DNSName(self.api_instance.env.domain).make_absolute() - self.servers_data = {} -- self.__init_data() -+ self.__init_data(all_servers=all_servers) - - def reload_data(self): - """ -@@ -89,14 +89,16 @@ class IPASystemRecords(object): - def __get_location_suffix(self, location): - return location + DNSName('_locations') + self.domain_abs - -- def __init_data(self): -+ def __init_data(self, all_servers=False): - self.servers_data = {} - -- servers_result = self.api_instance.Command.server_find( -- no_members=False, -- servrole=u"IPA master", # only active, fully installed masters -- )['result'] -- for s in servers_result: -+ kwargs = dict(no_members=False) -+ if not all_servers: -+ # only active, fully installed masters] -+ kwargs["servrole"] = u"IPA master" -+ servers = self.api_instance.Command.server_find(**kwargs) -+ -+ for s in servers['result']: - weight, location, roles = self.__get_server_attrs(s) - self.servers_data[s['cn'][0]] = { - 'weight': weight, -diff --git a/ipaserver/install/adtrustinstance.py b/ipaserver/install/adtrustinstance.py -index b4db055045823ce8ae7e3b264e1442a085f81b2d..a7261b92ac227228b5b6af41db621ed2e5e96668 100644 ---- a/ipaserver/install/adtrustinstance.py -+++ b/ipaserver/install/adtrustinstance.py -@@ -581,7 +581,7 @@ class ADTRUSTInstance(service.Service): - self.print_msg(err_msg) - self.print_msg("Add the following service records to your DNS " \ - "server for DNS zone %s: " % zone) -- system_records = IPASystemRecords(api) -+ system_records = IPASystemRecords(api, all_servers=True) - adtrust_records = system_records.get_base_records( - [self.fqdn], ["AD trust controller"], - include_master_role=False, include_kerberos_realm=False) -@@ -734,12 +734,12 @@ class ADTRUSTInstance(service.Service): - # Note that self.dm_password is None for ADTrustInstance because - # we ensure to be called as root and using ldapi to use autobind - try: -- self.ldap_enable('ADTRUST', self.fqdn, None, self.suffix) -+ self.ldap_configure('ADTRUST', self.fqdn, None, self.suffix) - except (ldap.ALREADY_EXISTS, errors.DuplicateEntry): - root_logger.info("ADTRUST Service startup entry already exists.") - - try: -- self.ldap_enable('EXTID', self.fqdn, None, self.suffix) -+ self.ldap_configure('EXTID', self.fqdn, None, self.suffix) - except (ldap.ALREADY_EXISTS, errors.DuplicateEntry): - root_logger.info("EXTID Service startup entry already exists.") - -diff --git a/ipaserver/install/bindinstance.py b/ipaserver/install/bindinstance.py -index 03dce56aa0610b3dc530e6b2a185515be7956e8b..771c6b0483d07b20fbc8470397eed306651f4a8f 100644 ---- a/ipaserver/install/bindinstance.py -+++ b/ipaserver/install/bindinstance.py -@@ -664,7 +664,7 @@ class BindInstance(service.Service): - return normalize_zone(self.host_domain) == normalize_zone(self.domain) - - def create_file_with_system_records(self): -- system_records = IPASystemRecords(self.api) -+ system_records = IPASystemRecords(self.api, all_servers=True) - text = u'\n'.join( - IPASystemRecords.records_list_from_zone( - system_records.get_base_records() -@@ -741,7 +741,7 @@ class BindInstance(service.Service): - # Instead we reply on the IPA init script to start only enabled - # components as found in our LDAP configuration tree - try: -- self.ldap_enable('DNS', self.fqdn, None, self.suffix) -+ self.ldap_configure('DNS', self.fqdn, None, self.suffix) - except errors.DuplicateEntry: - # service already exists (forced DNS reinstall) - # don't crash, just report error -@@ -1175,7 +1175,7 @@ class BindInstance(service.Service): - except ValueError as error: - root_logger.debug(error) - -- # disabled by default, by ldap_enable() -+ # disabled by default, by ldap_configure() - if enabled: - self.enable() - -diff --git a/ipaserver/install/cainstance.py b/ipaserver/install/cainstance.py -index 0c4d9bf9ad8ae11ac88523857845e16eb62651b9..62e9ad7de6f00eabb48f726a3931eb8acf0ba22b 100644 ---- a/ipaserver/install/cainstance.py -+++ b/ipaserver/install/cainstance.py -@@ -1218,7 +1218,7 @@ class CAInstance(DogtagInstance): - config = ['caRenewalMaster'] - else: - config = [] -- self.ldap_enable('CA', self.fqdn, None, basedn, config) -+ self.ldap_configure('CA', self.fqdn, None, basedn, config) - - def setup_lightweight_ca_key_retrieval(self): - if sysupgrade.get_upgrade_state('dogtag', 'setup_lwca_key_retrieval'): -diff --git a/ipaserver/install/dnskeysyncinstance.py b/ipaserver/install/dnskeysyncinstance.py -index 3849626e5a253667271913337d1a5aa4a72755bb..28468826d7194e7103e61c0ef3957b849e1be896 100644 ---- a/ipaserver/install/dnskeysyncinstance.py -+++ b/ipaserver/install/dnskeysyncinstance.py -@@ -384,8 +384,8 @@ class DNSKeySyncInstance(service.Service): - - def __enable(self): - try: -- self.ldap_enable('DNSKeySync', self.fqdn, None, -- self.suffix, self.extra_config) -+ self.ldap_configure('DNSKeySync', self.fqdn, None, -+ self.suffix, self.extra_config) - except errors.DuplicateEntry: - self.logger.error("DNSKeySync service already exists") - -diff --git a/ipaserver/install/httpinstance.py b/ipaserver/install/httpinstance.py -index 7081c7418e76afbd1b4ae28deafefb6b264c62f0..2df51eaa77d3ee3246027a6bcbc4023dbad61160 100644 ---- a/ipaserver/install/httpinstance.py -+++ b/ipaserver/install/httpinstance.py -@@ -200,7 +200,7 @@ class HTTPInstance(service.Service): - # We do not let the system start IPA components on its own, - # Instead we reply on the IPA init script to start only enabled - # components as found in our LDAP configuration tree -- self.ldap_enable('HTTP', self.fqdn, None, self.suffix) -+ self.ldap_configure('HTTP', self.fqdn, None, self.suffix) - - def configure_selinux_for_httpd(self): - try: -@@ -583,7 +583,7 @@ class HTTPInstance(service.Service): - if running: - self.restart() - -- # disabled by default, by ldap_enable() -+ # disabled by default, by ldap_configure() - if enabled: - self.enable() - -diff --git a/ipaserver/install/ipa_kra_install.py b/ipaserver/install/ipa_kra_install.py -index 9ebabe057513141ee76d238a3f20e76a27dd932e..3a639ac20f101293edd7449f8846a451469e2297 100644 ---- a/ipaserver/install/ipa_kra_install.py -+++ b/ipaserver/install/ipa_kra_install.py -@@ -224,4 +224,11 @@ class KRAInstaller(KRAInstall): - self.log.error(dedent(self.FAIL_MESSAGE)) - raise - -+ # pki-spawn restarts 389-DS, reconnect -+ api.Backend.ldap2.close() -+ api.Backend.ldap2.connect() -+ -+ # Enable configured services and update DNS SRV records -+ service.enable_services(api.env.host) -+ api.Command.dns_update_system_records() - api.Backend.ldap2.disconnect() -diff --git a/ipaserver/install/krainstance.py b/ipaserver/install/krainstance.py -index 915d3c3c6e038eeb6a8f94f1ed7f7008c0ef4ead..a23de3960f0789761b4b86227508236c80e97c2f 100644 ---- a/ipaserver/install/krainstance.py -+++ b/ipaserver/install/krainstance.py -@@ -376,4 +376,4 @@ class KRAInstance(DogtagInstance): - directives[nickname], cert, paths.KRA_CS_CFG_PATH) - - def __enable_instance(self): -- self.ldap_enable('KRA', self.fqdn, None, self.suffix) -+ self.ldap_configure('KRA', self.fqdn, None, self.suffix) -diff --git a/ipaserver/install/krbinstance.py b/ipaserver/install/krbinstance.py -index 5971a30fc566f6e96ce0b08632772d33da5602d2..4041d1b5fb3c3cf3db78b6cb282ce5f17793a0e3 100644 ---- a/ipaserver/install/krbinstance.py -+++ b/ipaserver/install/krbinstance.py -@@ -240,7 +240,7 @@ class KrbInstance(service.Service): - # We do not let the system start IPA components on its own, - # Instead we reply on the IPA init script to start only enabled - # components as found in our LDAP configuration tree -- self.ldap_enable('KDC', self.fqdn, None, self.suffix) -+ self.ldap_configure('KDC', self.fqdn, None, self.suffix) - - def __start_instance(self): - try: -@@ -598,7 +598,7 @@ class KrbInstance(service.Service): - except ValueError as error: - root_logger.debug(error) - -- # disabled by default, by ldap_enable() -+ # disabled by default, by ldap_configure() - if enabled: - self.enable() - -diff --git a/ipaserver/install/odsexporterinstance.py b/ipaserver/install/odsexporterinstance.py -index 59f27f578dab5663b1a7b734dff3699a6996084d..1694704f967b0b2a5debe76252ae859ae8a47f2b 100644 ---- a/ipaserver/install/odsexporterinstance.py -+++ b/ipaserver/install/odsexporterinstance.py -@@ -69,8 +69,8 @@ class ODSExporterInstance(service.Service): - def __enable(self): - - try: -- self.ldap_enable('DNSKeyExporter', self.fqdn, None, -- self.suffix) -+ self.ldap_configure('DNSKeyExporter', self.fqdn, None, -+ self.suffix) - except errors.DuplicateEntry: - root_logger.error("DNSKeyExporter service already exists") - -diff --git a/ipaserver/install/opendnssecinstance.py b/ipaserver/install/opendnssecinstance.py -index bc2974a2cf56e4ade1b778303c14f9ce05a8bf0f..92a46a9f6e318454084398ed625cf27a8250c2af 100644 ---- a/ipaserver/install/opendnssecinstance.py -+++ b/ipaserver/install/opendnssecinstance.py -@@ -136,8 +136,8 @@ class OpenDNSSECInstance(service.Service): - - def __enable(self): - try: -- self.ldap_enable('DNSSEC', self.fqdn, None, -- self.suffix, self.extra_config) -+ self.ldap_configure('DNSSEC', self.fqdn, None, -+ self.suffix, self.extra_config) - except errors.DuplicateEntry: - root_logger.error("DNSSEC service already exists") - -@@ -368,7 +368,7 @@ class OpenDNSSECInstance(service.Service): - - self.restore_state("kasp_db_configured") # just eat state - -- # disabled by default, by ldap_enable() -+ # disabled by default, by ldap_configure() - if enabled: - self.enable() - -diff --git a/ipaserver/install/server/install.py b/ipaserver/install/server/install.py -index 3651cde827ecf299e5570feed4936311f91749fb..dcdd9aabb746c4973b3f73934d94225503728f0b 100644 ---- a/ipaserver/install/server/install.py -+++ b/ipaserver/install/server/install.py -@@ -869,14 +869,6 @@ def install(installer): - - if options.setup_dns: - dns.install(False, False, options) -- else: -- # Create a BIND instance -- bind = bindinstance.BindInstance(fstore) -- bind.setup(host_name, ip_addresses, realm_name, -- domain_name, (), 'first', (), -- zonemgr=options.zonemgr, -- no_dnssec_validation=options.no_dnssec_validation) -- bind.create_file_with_system_records() - - if options.setup_adtrust: - adtrust.install(False, options, fstore, api) -@@ -908,6 +900,16 @@ def install(installer): - # Make sure the files we crated in /var/run are recreated at startup - tasks.configure_tmpfiles() - -+ # Enable configured services and update DNS SRV records -+ service.enable_services(host_name) -+ api.Command.dns_update_system_records() -+ -+ if not options.setup_dns: -+ # After DNS and AD trust are configured and services are -+ # enabled, create a dummy instance to dump DNS configuration. -+ bind = bindinstance.BindInstance(fstore) -+ bind.create_file_with_system_records() -+ - # Everything installed properly, activate ipa service. - services.knownservices.ipa.enable() - -diff --git a/ipaserver/install/server/replicainstall.py b/ipaserver/install/server/replicainstall.py -index b10f761e3f643f9fa868451192fa4550b24b6b16..59fec452c674b9941ce731748dd63985a08fefc0 100644 ---- a/ipaserver/install/server/replicainstall.py -+++ b/ipaserver/install/server/replicainstall.py -@@ -1518,14 +1518,10 @@ def install(installer): - - if options.setup_dns: - dns.install(False, True, options, api) -- else: -- api.Command.dns_update_system_records() - - if options.setup_adtrust: - adtrust.install(False, options, fstore, api) - -- api.Backend.ldap2.disconnect() -- - if not promote: - # Call client install script - service.print_msg("Configuring client side components") -@@ -1556,6 +1552,11 @@ def install(installer): - # Make sure the files we crated in /var/run are recreated at startup - tasks.configure_tmpfiles() - -+ # Enable configured services and update DNS SRV records -+ service.enable_services(config.host_name) -+ api.Command.dns_update_system_records() -+ api.Backend.ldap2.disconnect() -+ - # Everything installed properly, activate ipa service. - services.knownservices.ipa.enable() - -diff --git a/ipaserver/install/service.py b/ipaserver/install/service.py -index 0523e914aa7debf6aaa82ddcce9b7b26c1833cd3..4271ebe06be03199165894f7a884e17f311896cb 100644 ---- a/ipaserver/install/service.py -+++ b/ipaserver/install/service.py -@@ -24,6 +24,7 @@ import socket - import datetime - import traceback - import tempfile -+import warnings - - import six - -@@ -59,6 +60,10 @@ SERVICE_LIST = { - 'DNSKeySync': ('ipa-dnskeysyncd', 110), - } - -+CONFIGURED_SERVICE = u'configuredService' -+ENABLED_SERVICE = 'enabledService' -+ -+ - def print_msg(message, output_fd=sys.stdout): - root_logger.debug(message) - output_fd.write(message) -@@ -120,7 +125,7 @@ def find_providing_server(svcname, conn, host_name=None, api=api): - """ - dn = DN(('cn', 'masters'), ('cn', 'ipa'), ('cn', 'etc'), api.env.basedn) - query_filter = conn.make_filter({'objectClass': 'ipaConfigObject', -- 'ipaConfigString': 'enabledService', -+ 'ipaConfigString': ENABLED_SERVICE, - 'cn': svcname}, rules='&') - try: - entries, _trunc = conn.find_entries(filter=query_filter, base_dn=dn) -@@ -217,6 +222,53 @@ def set_service_entry_config(name, fqdn, config_values, - raise e - - -+def enable_services(fqdn): -+ """Change all configured services to enabled -+ -+ Server.ldap_configure() only marks a service as configured. Services -+ are enabled at the very end of installation. -+ -+ Note: DNS records must be updated with dns_update_system_records, too. -+ -+ :param fqdn: hostname of server -+ """ -+ ldap2 = api.Backend.ldap2 -+ search_base = DN(('cn', fqdn), api.env.container_masters, api.env.basedn) -+ search_filter = ldap2.make_filter( -+ { -+ 'objectClass': 'ipaConfigObject', -+ 'ipaConfigString': CONFIGURED_SERVICE -+ }, -+ rules='&' -+ ) -+ entries = ldap2.get_entries( -+ search_base, -+ filter=search_filter, -+ scope=api.Backend.ldap2.SCOPE_ONELEVEL, -+ attrs_list=['cn', 'ipaConfigString'] -+ ) -+ for entry in entries: -+ name = entry['cn'] -+ cfgstrings = entry.setdefault('ipaConfigString', []) -+ for value in list(cfgstrings): -+ if value.lower() == CONFIGURED_SERVICE.lower(): -+ cfgstrings.remove(value) -+ if not case_insensitive_attr_has_value(cfgstrings, ENABLED_SERVICE): -+ cfgstrings.append(ENABLED_SERVICE) -+ -+ try: -+ ldap2.update_entry(entry) -+ except errors.EmptyModlist: -+ root_logger.debug("Nothing to do for service %s", name) -+ except Exception: -+ root_logger.exception( -+ "failed to set service %s config values", name -+ ) -+ raise -+ else: -+ root_logger.debug("Enabled service %s for %s", name, fqdn) -+ -+ - class Service(object): - def __init__(self, service_name, service_desc=None, sstore=None, - fstore=None, api=api, realm_name=None, -@@ -522,7 +574,35 @@ class Service(object): - self.steps = [] - - def ldap_enable(self, name, fqdn, dm_password=None, ldap_suffix='', -- config=[]): -+ config=()): -+ """Legacy function, all services should use ldap_configure() -+ """ -+ warnings.warn( -+ "ldap_enable is deprecated, use ldap_configure instead.", -+ DeprecationWarning, -+ stacklevel=2 -+ ) -+ self._ldap_enable(ENABLED_SERVICE, name, fqdn, ldap_suffix, config) -+ -+ def ldap_configure(self, name, fqdn, dm_password=None, ldap_suffix='', -+ config=()): -+ """Create or modify service entry in cn=masters,cn=ipa,cn=etc -+ -+ Contrary to ldap_enable(), the method only sets -+ ipaConfigString=configuredService. ipaConfigString=enabledService -+ is set at the very end of the installation process, to ensure that -+ other machines see this master/replica after it is fully installed. -+ -+ To switch all configured services to enabled, use:: -+ -+ ipaserver.install.service.enable_services(api.env.host) -+ api.Command.dns_update_system_records() -+ """ -+ self._ldap_enable( -+ CONFIGURED_SERVICE, name, fqdn, ldap_suffix, config -+ ) -+ -+ def _ldap_enable(self, value, name, fqdn, ldap_suffix, config): - extra_config_opts = [ - ' '.join([u'startOrder', unicode(SERVICE_LIST[name][1])]) - ] -@@ -533,7 +613,7 @@ class Service(object): - set_service_entry_config( - name, - fqdn, -- [u'enabledService'], -+ [value], - ldap_suffix=ldap_suffix, - post_add_config=extra_config_opts) - -@@ -559,7 +639,7 @@ class Service(object): - - # case insensitive - for value in entry.get('ipaConfigString', []): -- if value.lower() == u'enabledservice': -+ if value.lower() == ENABLED_SERVICE: - entry['ipaConfigString'].remove(value) - break - -@@ -672,7 +752,7 @@ class SimpleServiceInstance(Service): - if self.gensvc_name == None: - self.enable() - else: -- self.ldap_enable(self.gensvc_name, self.fqdn, None, self.suffix) -+ self.ldap_configure(self.gensvc_name, self.fqdn, None, self.suffix) - - def is_installed(self): - return self.service.is_installed() -diff --git a/ipaserver/plugins/serverrole.py b/ipaserver/plugins/serverrole.py -index db88b3885c538c2800f6e4a1d649083859d43641..35b199387b4d3512d39f197d125c20571e23ad7a 100644 ---- a/ipaserver/plugins/serverrole.py -+++ b/ipaserver/plugins/serverrole.py -@@ -15,16 +15,21 @@ IPA server roles - """) + _(""" - Get status of roles (DNS server, CA, etc.) provided by IPA masters. - """) + _(""" -+The status of a role is either enabled, configured, or absent. -+""") + _(""" - EXAMPLES: - """) + _(""" - Show status of 'DNS server' role on a server: - ipa server-role-show ipa.example.com "DNS server" - """) + _(""" - Show status of all roles containing 'AD' on a server: -- ipa server-role-find --server ipa.example.com --role='AD' -+ ipa server-role-find --server ipa.example.com --role="AD trust controller" - """) + _(""" - Show status of all configured roles on a server: - ipa server-role-find ipa.example.com -+""") + _(""" -+ Show implicit IPA master role: -+ ipa server-role-find --include-master - """) - - --- -2.17.1 - diff --git a/SOURCES/0067-replicainstall-DS-SSL-replica-install-pick-right-cer.patch b/SOURCES/0067-replicainstall-DS-SSL-replica-install-pick-right-cer.patch deleted file mode 100644 index f4c2388..0000000 --- a/SOURCES/0067-replicainstall-DS-SSL-replica-install-pick-right-cer.patch +++ /dev/null @@ -1,55 +0,0 @@ -From ab325034a6d837cc51db2aa029498fa222e9d4e7 Mon Sep 17 00:00:00 2001 -From: Rob Crittenden -Date: Fri, 6 Jul 2018 09:26:19 -0400 -Subject: [PATCH] replicainstall: DS SSL replica install pick right certmonger - host - -Extend fix 0f31564b35aac250456233f98730811560eda664 to also move -the DS SSL setup so that the xmlrpc_uri is configured to point -to the remote master we are configuring against. - -https://pagure.io/freeipa/issue/7566 - -Signed-off-by: Rob Crittenden -Reviewed-By: Tibor Dudlak ---- - ipaserver/install/server/replicainstall.py | 14 +++++++------- - 1 file changed, 7 insertions(+), 7 deletions(-) - -diff --git a/ipaserver/install/server/replicainstall.py b/ipaserver/install/server/replicainstall.py -index 59fec452c674b9941ce731748dd63985a08fefc0..a47412e39b9e2c603206c56a935de17321c71e91 100644 ---- a/ipaserver/install/server/replicainstall.py -+++ b/ipaserver/install/server/replicainstall.py -@@ -1444,15 +1444,12 @@ def install(installer): - pkcs12_info=pkinit_pkcs12_info, - promote=promote) - -- # we now need to enable ssl on the ds -- ds.enable_ssl() -- - if promote: - # We need to point to the master when certmonger asks for -- # HTTP certificate. -- # During http installation, the HTTP/hostname principal is created -- # locally then the installer waits for the entry to appear on the -- # master selected for the installation. -+ # a DS or HTTP certificate. -+ # During http installation, the /hostname principal is -+ # created locally then the installer waits for the entry to appear -+ # on the master selected for the installation. - # In a later step, the installer requests a SSL certificate through - # Certmonger (and the op adds the principal if it does not exist yet). - # If xmlrpc_uri points to the soon-to-be replica, -@@ -1466,6 +1463,9 @@ def install(installer): - create_ipa_conf(fstore, config, ca_enabled, - master=config.master_host_name) - -+ # we now need to enable ssl on the ds -+ ds.enable_ssl() -+ - install_http( - config, - auto_redirect=not options.no_ui_redirect, --- -2.17.1 - diff --git a/SOURCES/0068-Fix-race-condition-in-get_locations_records.patch b/SOURCES/0068-Fix-race-condition-in-get_locations_records.patch deleted file mode 100644 index 50317ad..0000000 --- a/SOURCES/0068-Fix-race-condition-in-get_locations_records.patch +++ /dev/null @@ -1,80 +0,0 @@ -From 2347e7d3b9979533f471874919e1a39747bf4597 Mon Sep 17 00:00:00 2001 -From: Christian Heimes -Date: Fri, 6 Jul 2018 21:47:32 +0200 -Subject: [PATCH] Fix race condition in get_locations_records() - -The method IPASystemRecords.get_locations_records() has a race condition. -The IPASystemRecords object creates a mapping of server names to server -data. get_locations_records() uses server_find() again to get a list of -servers, but then operates on the cached dict of server names. - -In parallel replication case, the second server_find() call in -get_locations_records() can return additional servers. Since the rest of -the code operates on the cached data, the method then fails with a KeyError. - -server_data is now an OrderedDict to keep same sorting as with -server_find(). - -Fixes: https://pagure.io/freeipa/issue/7566 -Signed-off-by: Christian Heimes -Reviewed-By: Tibor Dudlak ---- - ipaserver/dns_data_management.py | 14 +++++--------- - 1 file changed, 5 insertions(+), 9 deletions(-) - -diff --git a/ipaserver/dns_data_management.py b/ipaserver/dns_data_management.py -index e5987b4bd7b43d3920e9da917258153e448206b7..c4f204c4941984d74e442c51ea206f11c05db2b9 100644 ---- a/ipaserver/dns_data_management.py -+++ b/ipaserver/dns_data_management.py -@@ -6,7 +6,7 @@ from __future__ import absolute_import - - import six - --from collections import defaultdict -+from collections import defaultdict, OrderedDict - from dns import ( - rdata, - rdataclass, -@@ -68,7 +68,7 @@ class IPASystemRecords(object): - def __init__(self, api_instance, all_servers=False): - self.api_instance = api_instance - self.domain_abs = DNSName(self.api_instance.env.domain).make_absolute() -- self.servers_data = {} -+ self.servers_data = OrderedDict() - self.__init_data(all_servers=all_servers) - - def reload_data(self): -@@ -90,7 +90,7 @@ class IPASystemRecords(object): - return location + DNSName('_locations') + self.domain_abs - - def __init_data(self, all_servers=False): -- self.servers_data = {} -+ self.servers_data.clear() - - kwargs = dict(no_members=False) - if not all_servers: -@@ -325,7 +325,7 @@ class IPASystemRecords(object): - - zone_obj = zone.Zone(self.domain_abs, relativize=False) - if servers is None: -- servers = self.servers_data.keys() -+ servers = list(self.servers_data) - - for server in servers: - self._add_base_dns_records_for_server(zone_obj, server, -@@ -348,11 +348,7 @@ class IPASystemRecords(object): - """ - zone_obj = zone.Zone(self.domain_abs, relativize=False) - if servers is None: -- servers_result = self.api_instance.Command.server_find( -- pkey_only=True, -- servrole=u"IPA master", # only fully installed masters -- )['result'] -- servers = [s['cn'][0] for s in servers_result] -+ servers = list(self.servers_data) - - locations_result = self.api_instance.Command.location_find()['result'] - locations = [l['idnsname'][0] for l in locations_result] --- -2.17.1 - diff --git a/SOURCES/0069-Auto-retry-failed-certmonger-requests.patch b/SOURCES/0069-Auto-retry-failed-certmonger-requests.patch deleted file mode 100644 index f92e7f5..0000000 --- a/SOURCES/0069-Auto-retry-failed-certmonger-requests.patch +++ /dev/null @@ -1,195 +0,0 @@ -From 83445df2894426cdaf2f09f8bf2ac4c829922620 Mon Sep 17 00:00:00 2001 -From: Christian Heimes -Date: Sun, 8 Jul 2018 11:53:58 +0200 -Subject: [PATCH] Auto-retry failed certmonger requests - -During parallel replica installation, a request sometimes fails with -CA_REJECTED or CA_UNREACHABLE. The error occur when the master is -either busy or some information haven't been replicated yet. Even -a stuck request can be recovered, e.g. when permission and group -information have been replicated. - -A new function request_and_retry_cert() automatically resubmits failing -requests until it times out. - -Fixes: https://pagure.io/freeipa/issue/7623 -Signed-off-by: Christian Heimes -Reviewed-By: Tibor Dudlak ---- - ipalib/install/certmonger.py | 64 ++++++++++++++++++++++++------- - ipaserver/install/cainstance.py | 4 +- - ipaserver/install/certs.py | 19 ++++++--- - ipaserver/install/dsinstance.py | 4 +- - ipaserver/install/httpinstance.py | 5 ++- - ipaserver/install/krbinstance.py | 4 +- - 6 files changed, 76 insertions(+), 24 deletions(-) - -diff --git a/ipalib/install/certmonger.py b/ipalib/install/certmonger.py -index d2b782ddb0c746a3dfd96d0222bb31c6a960fdff..d915cbf7638c68f3ad5170b2878a706ad39def62 100644 ---- a/ipalib/install/certmonger.py -+++ b/ipalib/install/certmonger.py -@@ -302,20 +302,56 @@ def add_subject(request_id, subject): - def request_and_wait_for_cert( - certpath, subject, principal, nickname=None, passwd_fname=None, - dns=None, ca='IPA', profile=None, -- pre_command=None, post_command=None, storage='NSSDB', perms=None): -- """ -- Execute certmonger to request a server certificate. -- -- The method also waits for the certificate to be available. -- """ -- reqId = request_cert(certpath, subject, principal, nickname, -- passwd_fname, dns, ca, profile, -- pre_command, post_command, storage, perms) -- state = wait_for_request(reqId, api.env.startup_timeout) -- ca_error = get_request_value(reqId, 'ca-error') -- if state != 'MONITORING' or ca_error: -- raise RuntimeError("Certificate issuance failed ({})".format(state)) -- return reqId -+ pre_command=None, post_command=None, storage='NSSDB', perms=None, -+ resubmit_timeout=0): -+ """Request certificate, wait and possibly resubmit failing requests -+ -+ Submit a cert request to certmonger and wait until the request has -+ finished. -+ -+ With timeout, a failed request is resubmitted. During parallel replica -+ installation, a request sometimes fails with CA_REJECTED or -+ CA_UNREACHABLE. The error occurs when the master is either busy or some -+ information haven't been replicated yet. Even a stuck request can be -+ recovered, e.g. when permission and group information have been -+ replicated. -+ """ -+ req_id = request_cert( -+ certpath, subject, principal, nickname, passwd_fname, dns, ca, -+ profile, pre_command, post_command, storage, perms -+ ) -+ -+ deadline = time.time() + resubmit_timeout -+ while True: # until success, timeout, or error -+ state = wait_for_request(req_id, api.env.replication_wait_timeout) -+ ca_error = get_request_value(req_id, 'ca-error') -+ if state == 'MONITORING' and ca_error is None: -+ # we got a winner, exiting -+ root_logger.debug("Cert request %s was successful", req_id) -+ return req_id -+ -+ root_logger.debug( -+ "Cert request %s failed: %s (%s)", req_id, state, ca_error -+ ) -+ if state not in {'CA_REJECTED', 'CA_UNREACHABLE'}: -+ # probably unrecoverable error -+ root_logger.debug("Giving up on cert request %s", req_id) -+ break -+ elif not resubmit_timeout: -+ # no resubmit -+ break -+ elif time.time() > deadline: -+ root_logger.debug("Request %s reached resubmit dead line", req_id) -+ break -+ else: -+ # sleep and resubmit -+ root_logger.debug("Sleep and resubmit cert request %s", req_id) -+ time.sleep(10) -+ resubmit_request(req_id) -+ -+ raise RuntimeError( -+ "Certificate issuance failed ({}: {})".format(state, ca_error) -+ ) - - - def request_cert( -diff --git a/ipaserver/install/cainstance.py b/ipaserver/install/cainstance.py -index 62e9ad7de6f00eabb48f726a3931eb8acf0ba22b..e207911814e3553c5aa5310694170d3575337c55 100644 ---- a/ipaserver/install/cainstance.py -+++ b/ipaserver/install/cainstance.py -@@ -863,7 +863,9 @@ class CAInstance(DogtagInstance): - profile='caServerCert', - pre_command='renew_ra_cert_pre', - post_command='renew_ra_cert', -- storage="FILE") -+ storage="FILE", -+ resubmit_timeout=api.env.replication_wait_timeout -+ ) - self.__set_ra_cert_perms() - - self.requestId = str(reqId) -diff --git a/ipaserver/install/certs.py b/ipaserver/install/certs.py -index de96318db51b03f2515814d574cfebf1b242b6a6..5670d468bb1b168af7ada7b2d8924b8ec9f5d9c1 100644 ---- a/ipaserver/install/certs.py -+++ b/ipaserver/install/certs.py -@@ -663,12 +663,19 @@ class CertDB(object): - def export_pem_cert(self, nickname, location): - return self.nssdb.export_pem_cert(nickname, location) - -- def request_service_cert(self, nickname, principal, host): -- certmonger.request_and_wait_for_cert(certpath=self.secdir, -- nickname=nickname, -- principal=principal, -- subject=host, -- passwd_fname=self.passwd_fname) -+ def request_service_cert(self, nickname, principal, host, -+ resubmit_timeout=None): -+ if resubmit_timeout is None: -+ resubmit_timeout = api.env.replication_wait_timeout -+ return certmonger.request_and_wait_for_cert( -+ certpath=self.secdir, -+ storage='NSSDB', -+ nickname=nickname, -+ principal=principal, -+ subject=host, -+ passwd_fname=self.passwd_fname, -+ resubmit_timeout=resubmit_timeout -+ ) - - def is_ipa_issued_cert(self, api, nickname): - """ -diff --git a/ipaserver/install/dsinstance.py b/ipaserver/install/dsinstance.py -index 7adaabd3c1280709150329003130f70233de37f4..8ec5cdd7a4a324a1a40dbe968defcc797db8f054 100644 ---- a/ipaserver/install/dsinstance.py -+++ b/ipaserver/install/dsinstance.py -@@ -847,7 +847,9 @@ class DsInstance(service.Service): - ca='IPA', - profile=dogtag.DEFAULT_PROFILE, - dns=[self.fqdn], -- post_command=cmd) -+ post_command=cmd, -+ resubmit_timeout=api.env.replication_wait_timeout -+ ) - finally: - if prev_helper is not None: - certmonger.modify_ca_helper('IPA', prev_helper) -diff --git a/ipaserver/install/httpinstance.py b/ipaserver/install/httpinstance.py -index 2df51eaa77d3ee3246027a6bcbc4023dbad61160..2366698bb0d6a7c9cd481ba9d5568d320742f6bb 100644 ---- a/ipaserver/install/httpinstance.py -+++ b/ipaserver/install/httpinstance.py -@@ -450,7 +450,10 @@ class HTTPInstance(service.Service): - ca='IPA', - profile=dogtag.DEFAULT_PROFILE, - dns=[self.fqdn], -- post_command='restart_httpd') -+ post_command='restart_httpd', -+ storage='NSSDB', -+ resubmit_timeout=api.env.replication_wait_timeout -+ ) - finally: - if prev_helper is not None: - certmonger.modify_ca_helper('IPA', prev_helper) -diff --git a/ipaserver/install/krbinstance.py b/ipaserver/install/krbinstance.py -index 4041d1b5fb3c3cf3db78b6cb282ce5f17793a0e3..62de118b7104fe7d72d2c1fd5577e9a76000c663 100644 ---- a/ipaserver/install/krbinstance.py -+++ b/ipaserver/install/krbinstance.py -@@ -436,7 +436,9 @@ class KrbInstance(service.Service): - storage='FILE', - profile=KDC_PROFILE, - post_command='renew_kdc_cert', -- perms=(0o644, 0o600)) -+ perms=(0o644, 0o600), -+ resubmit_timeout=api.env.replication_wait_timeout -+ ) - except dbus.DBusException as e: - # if the certificate is already tracked, ignore the error - name = e.get_dbus_name() --- -2.17.1 - diff --git a/SOURCES/0070-Wait-for-client-certificates.patch b/SOURCES/0070-Wait-for-client-certificates.patch deleted file mode 100644 index ebdea06..0000000 --- a/SOURCES/0070-Wait-for-client-certificates.patch +++ /dev/null @@ -1,66 +0,0 @@ -From 30b4300eb27ddeca50096687a9a4122e59d9b66d Mon Sep 17 00:00:00 2001 -From: Christian Heimes -Date: Mon, 9 Jul 2018 13:53:44 +0200 -Subject: [PATCH] Wait for client certificates - -ipa-client-install --request-cert now waits until certmonger has -provided a host certificate. In case of an error, ipa-client-install no -longer pretents to success but fails with an error code. - -The --request-cert option also ensures that certmonger is enabled and -running. - -See: Fixes: https://pagure.io/freeipa/issue/7623 -Signed-off-by: Christian Heimes -Reviewed-By: Tibor Dudlak ---- - ipaclient/install/client.py | 25 ++++++++++++++++++------- - 1 file changed, 18 insertions(+), 7 deletions(-) - -diff --git a/ipaclient/install/client.py b/ipaclient/install/client.py -index c88061320c29faba25374ba71c53407de8e71db2..dbada726280d9a90293842370f303de6a77ceb01 100644 ---- a/ipaclient/install/client.py -+++ b/ipaclient/install/client.py -@@ -771,6 +771,7 @@ def configure_certmonger( - cmonger = services.knownservices.certmonger - try: - cmonger.enable() -+ cmonger.start() - except Exception as e: - root_logger.error( - "Failed to configure automatic startup of the %s daemon: %s", -@@ -782,14 +783,24 @@ def configure_certmonger( - subject = str(DN(('CN', hostname), subject_base)) - passwd_fname = os.path.join(paths.IPA_NSSDB_DIR, 'pwdfile.txt') - try: -- certmonger.request_cert( -+ certmonger.request_and_wait_for_cert( - certpath=paths.IPA_NSSDB_DIR, -- nickname='Local IPA host', subject=subject, dns=[hostname], -- principal=principal, passwd_fname=passwd_fname) -- except Exception as ex: -- root_logger.error( -- "%s request for host certificate failed: %s", -- cmonger.service_name, ex) -+ storage='NSSDB', -+ nickname='Local IPA host', -+ subject=subject, -+ dns=[hostname], -+ principal=principal, -+ passwd_fname=passwd_fname, -+ resubmit_timeout=120, -+ ) -+ except Exception as e: -+ root_logger.exception("certmonger request failed") -+ raise ScriptError( -+ "{} request for host certificate failed: {}".format( -+ cmonger.service_name, e -+ ), -+ rval=CLIENT_INSTALL_ERROR -+ ) - - - def configure_sssd_conf( --- -2.17.1 - diff --git a/SOURCES/0071-Fix-DNSSEC-install-regression.patch b/SOURCES/0071-Fix-DNSSEC-install-regression.patch deleted file mode 100644 index 6c438f5..0000000 --- a/SOURCES/0071-Fix-DNSSEC-install-regression.patch +++ /dev/null @@ -1,68 +0,0 @@ -From bd769b979c26c0c1ff723faca167cd83a27420d5 Mon Sep 17 00:00:00 2001 -From: Christian Heimes -Date: Tue, 10 Jul 2018 12:51:36 +0200 -Subject: [PATCH] Fix DNSSEC install regression - -7284097eedef70dd556270732e6ab8e23501ce09 introduced a regression in -DNSSEC master installation. For standalone and replica installation, -services have to be enabled before checking bind config. - -Fixes: https://pagure.io/freeipa/issue/7635 -See: https://pagure.io/freeipa/issue/7566 -Signed-off-by: Christian Heimes -Reviewed-By: Tibor Dudlak -Reviewed-By: Tibor Dudlak ---- - install/tools/ipa-dns-install | 5 +---- - ipaserver/install/dns.py | 5 +++++ - 2 files changed, 6 insertions(+), 4 deletions(-) - -diff --git a/install/tools/ipa-dns-install b/install/tools/ipa-dns-install -index 04d1b140d2c79a0fa72d7df47d556643751bddb7..e7b6ea3a00468190b8dafe7c696f3bf212f81b9c 100755 ---- a/install/tools/ipa-dns-install -+++ b/install/tools/ipa-dns-install -@@ -36,7 +36,6 @@ from ipapython.config import IPAOptionParser - from ipapython.ipa_log_manager import standard_logging_setup, root_logger - - from ipaserver.install import dns as dns_installer --from ipaserver.install import service - - log_file_name = paths.IPASERVER_INSTALL_LOG - -@@ -146,9 +145,7 @@ def main(): - - dns_installer.install_check(True, api, False, options, hostname=api.env.host) - dns_installer.install(True, False, options) -- # Enable configured services and update DNS SRV records -- service.enable_services(api.env.host) -- api.Command.dns_update_system_records() -+ # Services are enabled in dns_installer.install() - - # execute ipactl to refresh services status - ipautil.run(['ipactl', 'start', '--ignore-service-failures'], -diff --git a/ipaserver/install/dns.py b/ipaserver/install/dns.py -index 1c1aac06a18fe3c1f63b5881c7887f6a4cfc9ac2..0046b78066cbb8e4f43e04af86da74118a5aa8c6 100644 ---- a/ipaserver/install/dns.py -+++ b/ipaserver/install/dns.py -@@ -43,6 +43,7 @@ from ipaserver.install import bindinstance - from ipaserver.install import dnskeysyncinstance - from ipaserver.install import odsexporterinstance - from ipaserver.install import opendnssecinstance -+from ipaserver.install import service - - if six.PY3: - unicode = str -@@ -355,6 +356,10 @@ def install(standalone, replica, options, api=api): - dnskeysyncd.start_dnskeysyncd() - bind.start_named() - -+ # Enable configured services for standalone check_global_configuration() -+ if standalone: -+ service.enable_services(api.env.host) -+ - # this must be done when bind is started and operational - bind.update_system_records() - --- -2.17.1 - diff --git a/SOURCES/0072-Handle-races-in-replica-config.patch b/SOURCES/0072-Handle-races-in-replica-config.patch deleted file mode 100644 index 4deee3a..0000000 --- a/SOURCES/0072-Handle-races-in-replica-config.patch +++ /dev/null @@ -1,270 +0,0 @@ -From 33f562bba3729bc596e07dc2805d78b80de55784 Mon Sep 17 00:00:00 2001 -From: Christian Heimes -Date: Tue, 10 Jul 2018 14:03:28 +0200 -Subject: [PATCH] Handle races in replica config - -When multiple replicas are installed in parallel, two replicas may try -to create the cn=replica entry at the same time. This leads to a -conflict on one of the replicas. replica_config() and -ensure_replication_managers() now handle conflicts. - -ipaldap now maps TYPE_OR_VALUE_EXISTS to DuplicateEntry(). The type or -value exists exception is raised, when an attribute value or type is -already set. - -Fixes: https://pagure.io/freeipa/issue/7566 -Signed-off-by: Christian Heimes -Reviewed-By: Thierry Bordaz -Reviewed-By: Thierry Bordaz ---- - ipapython/ipaldap.py | 5 ++ - ipaserver/install/replication.py | 125 ++++++++++++++++++------------- - ipaserver/plugins/ldap2.py | 3 +- - 3 files changed, 81 insertions(+), 52 deletions(-) - -diff --git a/ipapython/ipaldap.py b/ipapython/ipaldap.py -index 1b0aaddd63d92776448d1a7ae6c1bd26a02d5dcc..e2ff0626986aa20f3a2bc42721fba9fad3156498 100644 ---- a/ipapython/ipaldap.py -+++ b/ipapython/ipaldap.py -@@ -965,7 +965,12 @@ class LDAPClient(object): - except ldap.NO_SUCH_OBJECT: - raise errors.NotFound(reason=arg_desc or 'no such entry') - except ldap.ALREADY_EXISTS: -+ # entry already exists - raise errors.DuplicateEntry() -+ except ldap.TYPE_OR_VALUE_EXISTS: -+ # attribute type or attribute value already exists, usually only -+ # occurs, when two machines try to write at the same time. -+ raise errors.DuplicateEntry(message=unicode(desc)) - except ldap.CONSTRAINT_VIOLATION: - # This error gets thrown by the uniqueness plugin - _msg = 'Another entry with the same attribute value already exists' -diff --git a/ipaserver/install/replication.py b/ipaserver/install/replication.py -index c017764468674830670a817b3d815c5e2d78728e..ffda9a182f840317d96f1b3b914b38233022fb5b 100644 ---- a/ipaserver/install/replication.py -+++ b/ipaserver/install/replication.py -@@ -34,7 +34,7 @@ from ipalib import api, errors - from ipalib.cli import textui - from ipapython.ipa_log_manager import root_logger - from ipalib.text import _ --from ipapython import ipautil, ipaldap, kerberos -+from ipapython import ipautil, ipaldap - from ipapython.admintool import ScriptError - from ipapython.dn import DN - from ipaplatform.paths import paths -@@ -457,7 +457,7 @@ class ReplicationManager(object): - return DN(('cn', 'replica'), ('cn', self.db_suffix), - ('cn', 'mapping tree'), ('cn', 'config')) - -- def set_replica_binddngroup(self, r_conn, entry): -+ def _set_replica_binddngroup(self, r_conn, entry): - """ - Set nsds5replicabinddngroup attribute on remote master's replica entry. - Older masters (ipa < 3.3) may not support setting this attribute. In -@@ -472,11 +472,6 @@ class ReplicationManager(object): - mod.append((ldap.MOD_ADD, 'nsds5replicabinddngroup', - self.repl_man_group_dn)) - -- if 'nsds5replicabinddngroupcheckinterval' not in entry: -- mod.append( -- (ldap.MOD_ADD, -- 'nsds5replicabinddngroupcheckinterval', -- '60')) - if mod: - try: - r_conn.modify_s(entry.dn, mod) -@@ -484,49 +479,64 @@ class ReplicationManager(object): - root_logger.debug( - "nsds5replicabinddngroup attribute not supported on " - "remote master.") -+ except (ldap.ALREADY_EXISTS, ldap.CONSTRAINT_VIOLATION): -+ root_logger.debug("No update to %s necessary", entry.dn) - - def replica_config(self, conn, replica_id, replica_binddn): - assert isinstance(replica_binddn, DN) - dn = self.replica_dn() - assert isinstance(dn, DN) - -+ root_logger.debug("Add or update replica config %s", dn) - try: - entry = conn.get_entry(dn) - except errors.NotFound: -- pass -- else: -- binddns = entry.setdefault('nsDS5ReplicaBindDN', []) -- if replica_binddn not in {DN(m) for m in binddns}: -- # Add the new replication manager -- binddns.append(replica_binddn) -- for key, value in REPLICA_CREATION_SETTINGS.items(): -- entry[key] = value -+ # no entry, create new one -+ entry = conn.make_entry( -+ dn, -+ objectclass=["top", "nsds5replica", "extensibleobject"], -+ cn=["replica"], -+ nsds5replicaroot=[str(self.db_suffix)], -+ nsds5replicaid=[str(replica_id)], -+ nsds5replicatype=[self.get_replica_type()], -+ nsds5flags=["1"], -+ nsds5replicabinddn=[replica_binddn], -+ nsds5replicabinddngroup=[self.repl_man_group_dn], -+ nsds5replicalegacyconsumer=["off"], -+ **REPLICA_CREATION_SETTINGS -+ ) - try: -- conn.update_entry(entry) -- except errors.EmptyModlist: -- pass -+ conn.add_entry(entry) -+ except errors.DuplicateEntry: -+ root_logger.debug( -+ "Lost race against another replica, updating" -+ ) -+ # fetch entry that have been added by another replica -+ entry = conn.get_entry(dn) -+ else: -+ root_logger.debug("Added replica config %s", dn) -+ # added entry successfully -+ return entry - -- self.set_replica_binddngroup(conn, entry) -+ # either existing entry or lost race -+ binddns = entry.setdefault('nsDS5ReplicaBindDN', []) -+ if replica_binddn not in {DN(m) for m in binddns}: -+ # Add the new replication manager -+ binddns.append(replica_binddn) - -- # replication is already configured -- return -+ for key, value in REPLICA_CREATION_SETTINGS.items(): -+ entry[key] = value - -- replica_type = self.get_replica_type() -+ try: -+ conn.update_entry(entry) -+ except errors.EmptyModlist: -+ root_logger.debug("No update to %s necessary", entry.dn) -+ else: -+ root_logger.debug("Update replica config %s", entry.dn) - -- entry = conn.make_entry( -- dn, -- objectclass=["top", "nsds5replica", "extensibleobject"], -- cn=["replica"], -- nsds5replicaroot=[str(self.db_suffix)], -- nsds5replicaid=[str(replica_id)], -- nsds5replicatype=[replica_type], -- nsds5flags=["1"], -- nsds5replicabinddn=[replica_binddn], -- nsds5replicabinddngroup=[self.repl_man_group_dn], -- nsds5replicalegacyconsumer=["off"], -- **REPLICA_CREATION_SETTINGS -- ) -- conn.add_entry(entry) -+ self._set_replica_binddngroup(conn, entry) -+ -+ return entry - - def setup_changelog(self, conn): - ent = conn.get_entry( -@@ -686,7 +696,10 @@ class ReplicationManager(object): - uid=["passsync"], - userPassword=[password], - ) -- conn.add_entry(entry) -+ try: -+ conn.add_entry(entry) -+ except errors.DuplicateEntry: -+ pass - - # Add the user to the list of users allowed to bypass password policy - extop_dn = DN(('cn', 'ipa_pwd_extop'), ('cn', 'plugins'), ('cn', 'config')) -@@ -1644,7 +1657,10 @@ class ReplicationManager(object): - objectclass=['top', 'groupofnames'], - cn=['replication managers'] - ) -- conn.add_entry(entry) -+ try: -+ conn.add_entry(entry) -+ except errors.DuplicateEntry: -+ pass - - def ensure_replication_managers(self, conn, r_hostname): - """ -@@ -1654,23 +1670,24 @@ class ReplicationManager(object): - On FreeIPA 3.x masters lacking support for nsds5ReplicaBinddnGroup - attribute, add replica bind DN directly into the replica entry. - """ -- my_princ = kerberos.Principal((u'ldap', unicode(self.hostname)), -- realm=self.realm) -- remote_princ = kerberos.Principal((u'ldap', unicode(r_hostname)), -- realm=self.realm) -- services_dn = DN(api.env.container_service, api.env.basedn) -- -- mydn, remote_dn = tuple( -- DN(('krbprincipalname', unicode(p)), services_dn) for p in ( -- my_princ, remote_princ)) -+ my_dn = DN( -+ ('krbprincipalname', u'ldap/%s@%s' % (self.hostname, self.realm)), -+ api.env.container_service, -+ api.env.basedn -+ ) -+ remote_dn = DN( -+ ('krbprincipalname', u'ldap/%s@%s' % (r_hostname, self.realm)), -+ api.env.container_service, -+ api.env.basedn -+ ) - - try: - conn.get_entry(self.repl_man_group_dn) - except errors.NotFound: -- self._add_replica_bind_dn(conn, mydn) -+ self._add_replica_bind_dn(conn, my_dn) - self._add_replication_managers(conn) - -- self._add_dn_to_replication_managers(conn, mydn) -+ self._add_dn_to_replication_managers(conn, my_dn) - self._add_dn_to_replication_managers(conn, remote_dn) - - def add_temp_sasl_mapping(self, conn, r_hostname): -@@ -1690,7 +1707,10 @@ class ReplicationManager(object): - - entry = conn.get_entry(self.replica_dn()) - entry['nsDS5ReplicaBindDN'].append(replica_binddn) -- conn.update_entry(entry) -+ try: -+ conn.update_entry(entry) -+ except errors.EmptyModlist: -+ pass - - entry = conn.make_entry( - DN(('cn', 'Peer Master'), ('cn', 'mapping'), ('cn', 'sasl'), -@@ -1702,7 +1722,10 @@ class ReplicationManager(object): - nsSaslMapFilterTemplate=['(cn=&@%s)' % self.realm], - nsSaslMapPriority=['1'], - ) -- conn.add_entry(entry) -+ try: -+ conn.add_entry(entry) -+ except errors.DuplicateEntry: -+ pass - - def remove_temp_replication_user(self, conn, r_hostname): - """ -diff --git a/ipaserver/plugins/ldap2.py b/ipaserver/plugins/ldap2.py -index 3b1e4da57a8e16e3d9b27eea24025de2caa53216..c0a0ed251f98cd06ccd13c873f38809d04d1b5d5 100644 ---- a/ipaserver/plugins/ldap2.py -+++ b/ipaserver/plugins/ldap2.py -@@ -422,7 +422,8 @@ class ldap2(CrudBackend, LDAPClient): - modlist = [(a, b, self.encode(c)) - for a, b, c in modlist] - self.conn.modify_s(str(group_dn), modlist) -- except errors.DatabaseError: -+ except errors.DuplicateEntry: -+ # TYPE_OR_VALUE_EXISTS - raise errors.AlreadyGroupMember() - - def remove_entry_from_group(self, dn, group_dn, member_attr='member'): --- -2.17.1 - diff --git a/SOURCES/0073-Fix-KRA-replica-installation-from-CA-master.patch b/SOURCES/0073-Fix-KRA-replica-installation-from-CA-master.patch deleted file mode 100644 index 82559bb..0000000 --- a/SOURCES/0073-Fix-KRA-replica-installation-from-CA-master.patch +++ /dev/null @@ -1,46 +0,0 @@ -From 7746bb807a15137c6dbc36f9d0ea0c3e9377ab8c Mon Sep 17 00:00:00 2001 -From: Christian Heimes -Date: Tue, 17 Jul 2018 08:53:39 +0200 -Subject: [PATCH] Fix KRA replica installation from CA master - -ipa-replica-install --kra-install can fail when the topology already has -a KRA, but replica is installed from a master with just CA. In that -case, Custodia may pick a machine that doesn't have the KRA auditing and -signing certs in its NSSDB. - -Example: - * master with CA - * replica1 with CA and KRA - * new replica gets installed from master - -The replica installer now always picks a KRA peer. - -The change fixes test scenario TestInstallWithCA1::()::test_replica2_ipa_dns_install - -Fixes: https://pagure.io/freeipa/issue/7518 -See: https://pagure.io/freeipa/issue/7008 -Signed-off-by: Christian Heimes -Reviewed-By: Rob Crittenden ---- - ipaserver/install/server/replicainstall.py | 5 ++++- - 1 file changed, 4 insertions(+), 1 deletion(-) - -diff --git a/ipaserver/install/server/replicainstall.py b/ipaserver/install/server/replicainstall.py -index a47412e39b9e2c603206c56a935de17321c71e91..d8c55370d33d59efdf838f7ba01efedae7857406 100644 ---- a/ipaserver/install/server/replicainstall.py -+++ b/ipaserver/install/server/replicainstall.py -@@ -1482,7 +1482,10 @@ def install(installer): - otpd.create_instance('OTPD', config.host_name, - ipautil.realm_to_suffix(config.realm_name)) - -- if ca_enabled: -+ if kra_enabled: -+ # A KRA peer always provides a CA, too. -+ mode = custodiainstance.CustodiaModes.KRA_PEER -+ elif ca_enabled: - mode = custodiainstance.CustodiaModes.CA_PEER - else: - mode = custodiainstance.CustodiaModes.MASTER_PEER --- -2.17.1 - diff --git a/SOURCES/0074-DS-replication-settings-fix-regression-with-3.3-mast.patch b/SOURCES/0074-DS-replication-settings-fix-regression-with-3.3-mast.patch deleted file mode 100644 index 02929e0..0000000 --- a/SOURCES/0074-DS-replication-settings-fix-regression-with-3.3-mast.patch +++ /dev/null @@ -1,57 +0,0 @@ -From 35d8d61d53c9d99ae8a04365faa510535921ae48 Mon Sep 17 00:00:00 2001 -From: Florence Blanc-Renaud -Date: Tue, 21 Aug 2018 11:37:17 +0200 -Subject: [PATCH] DS replication settings: fix regression with <3.3 master - -Commit 811b0fdb4620938963f1a29d3fdd22257327562c introduced a regression -when configuring replication with a master < 3.3 -Even if 389-ds schema is extended with nsds5ReplicaReleaseTimeout, -nsds5ReplicaBackoffMax and nsDS5ReplicaBindDnGroupCheckInterval -attributes, it will return UNWILLING_TO_PERFORM when a mod -operation is performed on the cn=replica entry. - -This patch ignores the error and logs a debug msg. - -See: https://pagure.io/freeipa/issue/7617 -Reviewed-By: Christian Heimes ---- - ipaserver/install/replication.py | 16 +++++++++++++++- - 1 file changed, 15 insertions(+), 1 deletion(-) - -diff --git a/ipaserver/install/replication.py b/ipaserver/install/replication.py -index ffda9a182f840317d96f1b3b914b38233022fb5b..be738b249e36ca98fb2eea9e4730cefd0d30a3ee 100644 ---- a/ipaserver/install/replication.py -+++ b/ipaserver/install/replication.py -@@ -21,6 +21,7 @@ from __future__ import print_function, absolute_import - - import itertools - -+import re - import six - import time - import datetime -@@ -598,7 +599,20 @@ class ReplicationManager(object): - r_conn.simple_bind(r_binddn, r_bindpw) - else: - r_conn.gssapi_bind() -- self._finalize_replica_settings(r_conn) -+ # If the remote server has 389-ds < 1.3, it does not -+ # support the attributes we are trying to set. -+ # Find which 389-ds is installed -+ rootdse = r_conn.get_entry(DN(''), ['vendorVersion']) -+ version = rootdse.single_value.get('vendorVersion') -+ mo = re.search(r'(\d+)\.(\d+)\.(\d+)[\.\d]*', version) -+ vendor_version = tuple(int(v) for v in mo.groups()) -+ if vendor_version >= (1, 3, 0): -+ # 389-ds understands the replication attributes, -+ # we can safely modify them -+ self._finalize_replica_settings(r_conn) -+ else: -+ root_logger.debug("replication attributes not supported " -+ "on remote master, skipping update.") - r_conn.close() - - def setup_chaining_backend(self, conn): --- -2.17.1 - diff --git a/SOURCES/0075-Do-not-set-ca_host-when-setup-ca-is-used.patch b/SOURCES/0075-Do-not-set-ca_host-when-setup-ca-is-used.patch deleted file mode 100644 index 87363e1..0000000 --- a/SOURCES/0075-Do-not-set-ca_host-when-setup-ca-is-used.patch +++ /dev/null @@ -1,86 +0,0 @@ -From 37199834acaeee96136b096030198b2e9c8f16c8 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Tibor=20Dudl=C3=A1k?= -Date: Thu, 26 Jul 2018 11:46:55 +0200 -Subject: [PATCH] Do not set ca_host when --setup-ca is used - -Setting ca_host caused replication failures on DL0 -because it was trying to connect to wrong CA host. -Trying to avoid corner-case in ipaserver/plugins/dogtag.py -when api.env.host nor api.env.ca_host had not CA configured -and there was ca_host set to api.env.ca_host variable. - -See: https://pagure.io/freeipa/issue/7566 -Resolves: https://pagure.io/freeipa/issue/7629 -Reviewed-By: Florence Blanc-Renaud -Reviewed-By: Alexander Bokovoy -Reviewed-By: Christian Heimes -Reviewed-By: Rob Crittenden ---- - ipaserver/install/cainstance.py | 24 ++++++++++++++++++++++ - ipaserver/install/server/replicainstall.py | 7 +++++-- - 2 files changed, 29 insertions(+), 2 deletions(-) - -diff --git a/ipaserver/install/cainstance.py b/ipaserver/install/cainstance.py -index e207911814e3553c5aa5310694170d3575337c55..cb8dc43096b8851d7554447f28c4537a35b47852 100644 ---- a/ipaserver/install/cainstance.py -+++ b/ipaserver/install/cainstance.py -@@ -435,6 +435,11 @@ class CAInstance(DogtagInstance): - self.step("updating IPA configuration", update_ipa_conf) - self.step("enabling CA instance", self.__enable_instance) - if not promote: -+ if self.clone: -+ # DL0 workaround; see docstring of __expose_ca_in_ldap -+ self.step("exposing CA instance on LDAP", -+ self.__expose_ca_in_ldap) -+ - self.step("migrating certificate profiles to LDAP", - migrate_profiles_to_ldap) - self.step("importing IPA certificate profiles", -@@ -1222,6 +1227,25 @@ class CAInstance(DogtagInstance): - config = [] - self.ldap_configure('CA', self.fqdn, None, basedn, config) - -+ def __expose_ca_in_ldap(self): -+ """ -+ In a case when replica is created on DL0 we need to make -+ sure that query for CA service record of this replica in -+ ldap will succeed in time of installation. -+ This method is needed for sucessfull replica installation -+ on DL0 and should be removed alongside with code for DL0. -+ -+ To suppress deprecation warning message this method is -+ not invoking ldap_enable() but _ldap_enable() method. -+ """ -+ -+ basedn = ipautil.realm_to_suffix(self.realm) -+ if not self.clone: -+ config = ['caRenewalMaster'] -+ else: -+ config = [] -+ self._ldap_enable(u'enabledService', "CA", self.fqdn, basedn, config) -+ - def setup_lightweight_ca_key_retrieval(self): - if sysupgrade.get_upgrade_state('dogtag', 'setup_lwca_key_retrieval'): - return -diff --git a/ipaserver/install/server/replicainstall.py b/ipaserver/install/server/replicainstall.py -index d8c55370d33d59efdf838f7ba01efedae7857406..8a659a0fc2675df0a8fba1d3d7d8a629b376c93e 100644 ---- a/ipaserver/install/server/replicainstall.py -+++ b/ipaserver/install/server/replicainstall.py -@@ -236,9 +236,12 @@ def create_ipa_conf(fstore, config, ca_enabled, master=None): - gopts.extend([ - ipaconf.setOption('enable_ra', 'True'), - ipaconf.setOption('ra_plugin', 'dogtag'), -- ipaconf.setOption('dogtag_version', '10'), -- ipaconf.setOption('ca_host', config.ca_host_name) -+ ipaconf.setOption('dogtag_version', '10') - ]) -+ -+ if not config.setup_ca: -+ gopts.append(ipaconf.setOption('ca_host', config.ca_host_name)) -+ - else: - gopts.extend([ - ipaconf.setOption('enable_ra', 'False'), --- -2.17.1 - diff --git a/SOURCES/0076-Clear-next-field-when-returnining-list-elements-in-q.patch b/SOURCES/0076-Clear-next-field-when-returnining-list-elements-in-q.patch deleted file mode 100644 index 478b0c5..0000000 --- a/SOURCES/0076-Clear-next-field-when-returnining-list-elements-in-q.patch +++ /dev/null @@ -1,64 +0,0 @@ -From 81adf4db616f43704f0f1b8598149f6e2b123518 Mon Sep 17 00:00:00 2001 -From: Robbie Harwood -Date: Wed, 22 Aug 2018 15:32:16 -0400 -Subject: [PATCH] Clear next field when returnining list elements in queue.c - -The ipa-otpd code occasionally removes elements from one queue, -inspects and modifies them, and then inserts them into -another (possibly identical, possibly different) queue. When the next -pointer isn't cleared, this can result in element membership in both -queues, leading to double frees, or even self-referential elements, -causing infinite loops at traversal time. - -Rather than eliminating the pattern, make it safe by clearing the next -field any time an element enters or exits a queue. - -Related https://pagure.io/freeipa/issue/7262 - -Reviewed-By: Florence Blanc-Renaud ---- - daemons/ipa-otpd/queue.c | 7 +++++++ - 1 file changed, 7 insertions(+) - -diff --git a/daemons/ipa-otpd/queue.c b/daemons/ipa-otpd/queue.c -index 9e29fb238d5c7a7395bcf3860ce7445c27ca98ac..2944b7ea0db6f49d0a3230b5f33c7a89281fd8c6 100644 ---- a/daemons/ipa-otpd/queue.c -+++ b/daemons/ipa-otpd/queue.c -@@ -111,6 +111,8 @@ void otpd_queue_push(struct otpd_queue *q, struct otpd_queue_item *item) - q->head = q->tail = item; - else - q->tail = q->tail->next = item; -+ -+ item->next = NULL; - } - - void otpd_queue_push_head(struct otpd_queue *q, struct otpd_queue_item *item) -@@ -118,6 +120,8 @@ void otpd_queue_push_head(struct otpd_queue *q, struct otpd_queue_item *item) - if (item == NULL) - return; - -+ item->next = NULL; -+ - if (q->head == NULL) - q->tail = q->head = item; - else { -@@ -145,6 +149,8 @@ struct otpd_queue_item *otpd_queue_pop(struct otpd_queue *q) - if (q->head == NULL) - q->tail = NULL; - -+ if (item != NULL) -+ item->next = NULL; - return item; - } - -@@ -160,6 +166,7 @@ struct otpd_queue_item *otpd_queue_pop_msgid(struct otpd_queue *q, int msgid) - *prev = item->next; - if (q->head == NULL) - q->tail = NULL; -+ item->next = NULL; - return item; - } - } --- -2.17.1 - diff --git a/SOURCES/0077-Add-cmocka-unit-tests-for-ipa-otpd-queue-code.patch b/SOURCES/0077-Add-cmocka-unit-tests-for-ipa-otpd-queue-code.patch deleted file mode 100644 index 189974e..0000000 --- a/SOURCES/0077-Add-cmocka-unit-tests-for-ipa-otpd-queue-code.patch +++ /dev/null @@ -1,253 +0,0 @@ -From 1f47c51e592563339c13256ea02b8fa768b8fde2 Mon Sep 17 00:00:00 2001 -From: Robbie Harwood -Date: Thu, 30 Aug 2018 15:34:31 -0400 -Subject: [PATCH] Add cmocka unit tests for ipa otpd queue code - -Reviewed-By: Florence Blanc-Renaud ---- - daemons/ipa-otpd/Makefile.am | 12 + - .../ipa-otpd/ipa_otpd_queue_cmocka_tests.c | 212 ++++++++++++++++++ - 2 files changed, 224 insertions(+) - create mode 100644 daemons/ipa-otpd/ipa_otpd_queue_cmocka_tests.c - -diff --git a/daemons/ipa-otpd/Makefile.am b/daemons/ipa-otpd/Makefile.am -index 9ba6237566634cfa6a1e84c545ee1fc9cf2dc4f6..9ccc450a5e00db90b698ff9b25a496d09f0ff6e5 100644 ---- a/daemons/ipa-otpd/Makefile.am -+++ b/daemons/ipa-otpd/Makefile.am -@@ -20,3 +20,15 @@ ipa_otpd_SOURCES = bind.c forward.c main.c parse.c query.c queue.c stdio.c - $< > $@ - - CLEANFILES = $(systemdsystemunit_DATA) -+ -+TESTS = -+check_PROGRAMS = -+ -+if HAVE_CMOCKA -+TESTS += queue_tests -+check_PROGRAMS += queue_tests -+endif -+ -+queue_tests_SOURCES = ipa_otpd_queue_cmocka_tests.c queue.c -+queue_tests_CFLAGS = $(CMOCKA_CFLAGS) -+queue_tests_LDADD = $(CMOCKA_LIBS) -diff --git a/daemons/ipa-otpd/ipa_otpd_queue_cmocka_tests.c b/daemons/ipa-otpd/ipa_otpd_queue_cmocka_tests.c -new file mode 100644 -index 0000000000000000000000000000000000000000..068431e6475bb74b01acbcab22115915dec1a278 ---- /dev/null -+++ b/daemons/ipa-otpd/ipa_otpd_queue_cmocka_tests.c -@@ -0,0 +1,212 @@ -+/* -+ * FreeIPA 2FA companion daemon - internal queue tests -+ * -+ * Author: Robbie Harwood -+ * -+ * Copyright (C) 2018 Robbie Harwood, Red Hat -+ * see file 'COPYING' for use and warranty information -+ * -+ * This program is free software you can redistribute it and/or modify it -+ * under the terms of the GNU General Public License as published by the Free -+ * Software Foundation, either version 3 of the License, or (at your option) -+ * any later version. -+ * -+ * This program is distributed in the hope that it will be useful, but WITHOUT -+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for -+ * more details. -+ * -+ * You should have received a copy of the GNU General Public License along -+ * with this program. If not, see . -+ */ -+ -+#include -+#include -+#include -+ -+#include -+ -+#include "internal.h" -+ -+/* Bypass otpd queue allocation/freeing to avoid calling into LDAP and -+ * krad. No effort is made to make the types match. */ -+static struct otpd_queue_item *new_elt(int id) -+{ -+ krb5_error_code ret; -+ struct otpd_queue_item *e = NULL; -+ -+ ret = otpd_queue_item_new(NULL, &e); -+ assert_int_equal(ret, 0); -+ assert_ptr_not_equal(e, NULL); -+ -+ e->msgid = id; -+ return e; -+} -+static void free_elt(struct otpd_queue_item **e) -+{ -+ assert_ptr_not_equal(e, NULL); -+ free(*e); -+ *e = NULL; -+} -+static void free_elts(struct otpd_queue *q) -+{ -+ assert_ptr_not_equal(q, NULL); -+ for (struct otpd_queue_item *e = otpd_queue_pop(q); e != NULL; -+ e = otpd_queue_pop(q)) -+ free_elt(&e); -+} -+#define otpd_queue_item_new new_elt -+#define otpd_queue_item_free free_elt -+#define otpd_queue_free_items free_elts -+ -+static void assert_elt_equal(struct otpd_queue_item *e1, -+ struct otpd_queue_item *e2) -+{ -+ if (e1 == NULL && e2 == NULL) -+ return; -+ assert_ptr_not_equal(e1, NULL); -+ assert_ptr_not_equal(e2, NULL); -+ assert_int_equal(e1->msgid, e2->msgid); -+} -+ -+static void test_single_insert() -+{ -+ struct otpd_queue q = { NULL }; -+ struct otpd_queue_item *ein, *eout; -+ -+ ein = new_elt(0); -+ otpd_queue_push(&q, ein); -+ -+ eout = otpd_queue_peek(&q); -+ assert_elt_equal(ein, eout); -+ -+ eout = otpd_queue_pop(&q); -+ assert_elt_equal(ein, eout); -+ free_elt(&eout); -+ -+ eout = otpd_queue_pop(&q); -+ assert_ptr_equal(eout, NULL); -+ -+ free_elts(&q); -+} -+ -+static void test_jump_insert() -+{ -+ struct otpd_queue q = { NULL }; -+ struct otpd_queue_item *echeck; -+ -+ for (int i = 0; i < 3; i++) { -+ struct otpd_queue_item *e = new_elt(i); -+ otpd_queue_push_head(&q, e); -+ -+ echeck = otpd_queue_peek(&q); -+ assert_elt_equal(e, echeck); -+ } -+ -+ free_elts(&q); -+} -+ -+static void test_garbage_insert() -+{ -+ struct otpd_queue q = { NULL }; -+ struct otpd_queue_item *e, *g; -+ -+ g = new_elt(0); -+ g->next = g; -+ otpd_queue_push(&q, g); -+ -+ e = otpd_queue_peek(&q); -+ assert_ptr_equal(e->next, NULL); -+ -+ free_elts(&q); -+} -+ -+static void test_removal() -+{ -+ struct otpd_queue q = { NULL }; -+ -+ for (int i = 0; i < 3; i++) { -+ struct otpd_queue_item *e = new_elt(i); -+ otpd_queue_push(&q, e); -+ } -+ for (int i = 0; i < 3; i++) { -+ struct otpd_queue_item *e = otpd_queue_pop(&q); -+ assert_ptr_not_equal(e, NULL); -+ assert_ptr_equal(e->next, NULL); -+ assert_int_equal(e->msgid, i); -+ free_elt(&e); -+ } -+} -+ -+static void pick_id(struct otpd_queue *q, int msgid) -+{ -+ struct otpd_queue_item *e; -+ -+ e = otpd_queue_pop_msgid(q, msgid); -+ assert_int_equal(e->msgid, msgid); -+ assert_ptr_equal(e->next, NULL); -+ free_elt(&e); -+ e = otpd_queue_pop_msgid(q, msgid); -+ assert_ptr_equal(e, NULL); -+} -+static void test_pick_removal() -+{ -+ struct otpd_queue q = { NULL }; -+ -+ for (int i = 0; i < 4; i++) { -+ struct otpd_queue_item *e = new_elt(i); -+ otpd_queue_push(&q, e); -+ } -+ -+ pick_id(&q, 0); /* first */ -+ pick_id(&q, 2); /* middle */ -+ pick_id(&q, 3); /* last */ -+ pick_id(&q, 1); /* singleton */ -+ -+ free_elts(&q); -+} -+ -+static void test_iter() -+{ -+ krb5_error_code ret; -+ struct otpd_queue q = { NULL }; -+ const struct otpd_queue *queues[3]; -+ struct otpd_queue_iter *iter = NULL; -+ const krad_packet *p = NULL; -+ -+ for (ptrdiff_t i = 1; i <= 3; i++) { -+ struct otpd_queue_item *e = new_elt(i); -+ e->req = (void *)i; -+ otpd_queue_push(&q, e); -+ } -+ -+ queues[0] = &q; -+ queues[1] = &q; -+ queues[2] = NULL; -+ ret = otpd_queue_iter_new(queues, &iter); -+ assert_int_equal(ret, 0); -+ assert_ptr_not_equal(iter, NULL); -+ -+ for (ptrdiff_t i = 0; i < 6; i++) { -+ p = otpd_queue_iter_func(iter, FALSE); -+ assert_ptr_equal(p, (void *) (i % 3 + 1)); -+ } -+ p = otpd_queue_iter_func(iter, FALSE); -+ assert_ptr_equal(p, NULL); -+ -+ free_elts(&q); -+} -+ -+int main(int argc, char *argv[]) -+{ -+ const struct CMUnitTest tests[] = { -+ cmocka_unit_test(test_single_insert), -+ cmocka_unit_test(test_jump_insert), -+ cmocka_unit_test(test_garbage_insert), -+ cmocka_unit_test(test_removal), -+ cmocka_unit_test(test_pick_removal), -+ cmocka_unit_test(test_iter), -+ }; -+ -+ return cmocka_run_group_tests(tests, NULL, NULL); -+} --- -2.17.1 - diff --git a/SOURCES/0078-ipa-replica-install-fix-pkinit-setup.patch b/SOURCES/0078-ipa-replica-install-fix-pkinit-setup.patch deleted file mode 100644 index a1470c8..0000000 --- a/SOURCES/0078-ipa-replica-install-fix-pkinit-setup.patch +++ /dev/null @@ -1,42 +0,0 @@ -From 5aa15e551fe9b76c3e89862fe36b661825a807ce Mon Sep 17 00:00:00 2001 -From: Florence Blanc-Renaud -Date: Tue, 4 Sep 2018 14:15:50 +0200 -Subject: [PATCH] ipa-replica-install: fix pkinit setup - -commit 7284097 (Delay enabling services until end of installer) -introduced a regression in replica installation. -When the replica requests a cert for PKINIT, a check is done -to ensure that the hostname corresponds to a machine with a -KDC service enabled (ipaconfigstring attribute of -cn=KDC,cn=,cn=masters,cn=ipa,cn=etc,$BASEDN must contain -'enabledService'). -With the commit mentioned above, the service is set to enabled only -at the end of the installation. - -The fix makes a less strict check, ensuring that 'enabledService' -or 'configuredService' is in ipaconfigstring. - -Fixes: https://pagure.io/freeipa/issue/7566 -Reviewed-By: Christian Heimes -Reviewed-By: Christian Heimes ---- - ipaserver/plugins/cert.py | 3 ++- - 1 file changed, 2 insertions(+), 1 deletion(-) - -diff --git a/ipaserver/plugins/cert.py b/ipaserver/plugins/cert.py -index 501fc9015468c864215cfb604de37cdf6d805e52..60ad140da3b1483ffe5918239a489030ccc7fe96 100644 ---- a/ipaserver/plugins/cert.py -+++ b/ipaserver/plugins/cert.py -@@ -230,7 +230,8 @@ def ca_kdc_check(api_instance, hostname): - - ipaconfigstring = {val.lower() for val in kdc_entry['ipaConfigString']} - -- if 'enabledservice' not in ipaconfigstring: -+ if 'enabledservice' not in ipaconfigstring \ -+ and 'configuredservice' not in ipaconfigstring: - raise errors.NotFound() - - except errors.NotFound: --- -2.17.1 - diff --git a/SOURCES/1001-Change-branding-to-IPA-and-Identity-Management.patch b/SOURCES/1001-Change-branding-to-IPA-and-Identity-Management.patch index f3a7582..6e66d45 100644 --- a/SOURCES/1001-Change-branding-to-IPA-and-Identity-Management.patch +++ b/SOURCES/1001-Change-branding-to-IPA-and-Identity-Management.patch @@ -1,4 +1,4 @@ -From dfbf91e6463955a66e30eacf2bfbae4af586ef0a Mon Sep 17 00:00:00 2001 +From e94346d8c3d588056f04af1c1916617c962be4bc Mon Sep 17 00:00:00 2001 From: Jan Cholasta Date: Tue, 14 Mar 2017 15:48:07 +0000 Subject: [PATCH] Change branding to IPA and Identity Management @@ -12,7 +12,6 @@ Subject: [PATCH] Change branding to IPA and Identity Management client/man/ipa-join.1 | 2 +- client/man/ipa-rmkeytab.1 | 2 +- client/man/ipa.1 | 2 +- - install/html/browserconfig.html | 4 +- install/html/ssbrowser.html | 4 +- install/html/unauthorized.html | 4 +- install/migration/error.html | 4 +- @@ -47,21 +46,21 @@ Subject: [PATCH] Change branding to IPA and Identity Management install/tools/man/ipactl.8 | 2 +- install/ui/css/patternfly.css | 2 +- install/ui/index.html | 2 +- - install/ui/less/brand.less | 103 ++++++++++----------- - install/ui/less/patternfly.less | 48 ++++++++++ + install/ui/less/brand.less | 103 ++++++++++++++--------------- + install/ui/less/patternfly.less | 48 ++++++++++++++ install/ui/reset_password.html | 2 +- install/ui/src/freeipa/widgets/App.js | 2 +- install/ui/sync_otp.html | 2 +- - ipaserver/advise/plugins/legacy_clients.py | 8 +- + ipaserver/advise/plugins/legacy_clients.py | 8 +-- ipaserver/install/dns.py | 2 +- ipaserver/install/ipa_kra_install.py | 4 +- ipaserver/install/server/install.py | 2 +- ipaserver/install/server/replicainstall.py | 2 +- ipaserver/plugins/sudorule.py | 4 +- - 54 files changed, 167 insertions(+), 122 deletions(-) + 53 files changed, 165 insertions(+), 120 deletions(-) diff --git a/client/man/default.conf.5 b/client/man/default.conf.5 -index 35ce6bb9f871365ffbc74b66be46d49fdcb3f7ad..b519d15bca9b7ddf8d22a776fa4f4a8c7fac0ca8 100644 +index f21d9d5b7a02e9c9858bb44cf3f2f4c16655901a..d6c1e42d1af3a2085451f43240d7e719143bb10b 100644 --- a/client/man/default.conf.5 +++ b/client/man/default.conf.5 @@ -16,7 +16,7 @@ @@ -100,7 +99,7 @@ index 8b9989dec7a2e31f9ccbc0b79d336449bea9e70d..72562ab9582e987cde387583ba033464 ipa\-client\-automount \- Configure automount and NFS for IPA .SH "SYNOPSIS" diff --git a/client/man/ipa-client-install.1 b/client/man/ipa-client-install.1 -index 319952cb6ffe82339b578e8d7fe3eb7a83d53169..e631b89c6774b8ea43f5156293fee137c79dbb98 100644 +index b669b175af7df909f7b62dbce56cc219e154b153..9547288615698232458877afcd10a0de76f75259 100644 --- a/client/man/ipa-client-install.1 +++ b/client/man/ipa-client-install.1 @@ -1,7 +1,7 @@ @@ -161,7 +160,7 @@ index 53f775439dbdb5a4b9dfee7fe6c7277fce10893c..2c8218c94996b1411637a7c53f2f5bc5 ipa\-rmkeytab \- Remove a kerberos principal from a keytab .SH "SYNOPSIS" diff --git a/client/man/ipa.1 b/client/man/ipa.1 -index b843e7ba71ff400fa0712ba491b85e8b34f8a09f..0bd10a7edf8849dc6a77f364d662ee5841054d41 100644 +index d190677cf79af7a2b2225eb4a61f485120186e26..0988aaa93dd45f8a57af1aa23560b4844443c776 100644 --- a/client/man/ipa.1 +++ b/client/man/ipa.1 @@ -16,7 +16,7 @@ @@ -173,30 +172,8 @@ index b843e7ba71ff400fa0712ba491b85e8b34f8a09f..0bd10a7edf8849dc6a77f364d662ee58 .SH "NAME" ipa \- IPA command\-line interface .SH "SYNOPSIS" -diff --git a/install/html/browserconfig.html b/install/html/browserconfig.html -index 9c5cf68211281723e12b518f346aac43c1541cdc..14c4ca1f98a60cd8dfe486f8b942fcf9ae9de4c0 100644 ---- a/install/html/browserconfig.html -+++ b/install/html/browserconfig.html -@@ -2,7 +2,7 @@ - - - -- IPA: Identity Policy Audit -+ Identity Management - -