The Identity, Policy and Audit system
CentOS Sources
2016-02-16 e0ab38574a8cbb215f11d3bd2e50e7de2a4aa9d8
import ipa-4.2.0-15.el7_2.6
21 files added
1 files deleted
1 files modified
2139 ■■■■■ changed files
SOURCES/0160-Fix-version-comparison.patch 114 ●●●●● patch | view | raw | blame | history
SOURCES/0161-DNS-fix-file-permissions.patch 40 ●●●●● patch | view | raw | blame | history
SOURCES/0162-Explicitly-call-chmod-on-newly-created-directories.patch 121 ●●●●● patch | view | raw | blame | history
SOURCES/0163-Fix-replace-mkdir-with-chmod.patch 31 ●●●●● patch | view | raw | blame | history
SOURCES/0164-DNSSEC-Improve-error-reporting-from-ipa-ods-exporter.patch 38 ●●●●● patch | view | raw | blame | history
SOURCES/0165-DNSSEC-Make-sure-that-current-state-in-OpenDNSSEC-ma.patch 174 ●●●●● patch | view | raw | blame | history
SOURCES/0166-DNSSEC-Make-sure-that-current-key-state-in-LDAP-matc.patch 51 ●●●●● patch | view | raw | blame | history
SOURCES/0167-DNSSEC-remove-obsolete-TODO-note.patch 28 ●●●●● patch | view | raw | blame | history
SOURCES/0168-DNSSEC-add-debug-mode-to-ldapkeydb.py.patch 105 ●●●●● patch | view | raw | blame | history
SOURCES/0169-DNSSEC-logging-improvements-in-ipa-ods-exporter.patch 69 ●●●●● patch | view | raw | blame | history
SOURCES/0170-DNSSEC-remove-keys-purged-by-OpenDNSSEC-from-master-.patch 248 ●●●●● patch | view | raw | blame | history
SOURCES/0171-DNSSEC-ipa-dnskeysyncd-Skip-zones-with-old-DNSSEC-me.patch 125 ●●●●● patch | view | raw | blame | history
SOURCES/0172-DNSSEC-ipa-ods-exporter-add-ldap-cleanup-command.patch 142 ●●●●● patch | view | raw | blame | history
SOURCES/0173-DNSSEC-ipa-dnskeysyncd-call-ods-signer-ldap-cleanup-.patch 44 ●●●●● patch | view | raw | blame | history
SOURCES/0174-DNSSEC-Log-debug-messages-at-log-level-DEBUG.patch 29 ●●●●● patch | view | raw | blame | history
SOURCES/0175-Allow-to-used-mixed-case-for-sysrestore.patch 86 ●●●●● patch | view | raw | blame | history
SOURCES/0176-prevent-crash-of-CA-less-server-upgrade-due-to-absen.patch 77 ●●●●● patch | view | raw | blame | history
SOURCES/0177-Upgrade-Fix-upgrade-of-NIS-Server-configuration.patch 251 ●●●●● patch | view | raw | blame | history
SOURCES/0178-use-FFI-call-to-rpmvercmp-function-for-version-compa.patch 112 ●●●●● patch | view | raw | blame | history
SOURCES/0179-ipalib-assume-version-2.0-when-skip_version_check-is.patch 36 ●●●●● patch | view | raw | blame | history
SOURCES/0180-always-start-certmonger-during-IPA-server-configurat.patch 87 ●●●●● patch | view | raw | blame | history
SOURCES/ipa-centos-branding.patch 38 ●●●●● patch | view | raw | blame | history
SPECS/ipa.spec 93 ●●●● patch | view | raw | blame | history
SOURCES/0160-Fix-version-comparison.patch
New file
@@ -0,0 +1,114 @@
From 30902db32fcc04dccbaf40839f44cdf4f505f588 Mon Sep 17 00:00:00 2001
From: Martin Basti <mbasti@redhat.com>
Date: Wed, 9 Dec 2015 18:53:35 +0100
Subject: [PATCH] Fix version comparison
Use RPM library to compare vendor versions of IPA for redhat platform
https://fedorahosted.org/freeipa/ticket/5535
Reviewed-By: Tomas Babej <tbabej@redhat.com>
---
 freeipa.spec.in             |  1 +
 ipaplatform/redhat/tasks.py | 53 +++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 54 insertions(+)
diff --git a/freeipa.spec.in b/freeipa.spec.in
index 6527109b422a1e3065d5a540c3e2a3af670f2ebf..01d42bc621c83541af7517d6d91eb37fd5b5c5cc 100644
--- a/freeipa.spec.in
+++ b/freeipa.spec.in
@@ -159,6 +159,7 @@ Requires: p11-kit
 Requires: systemd-python
 Requires: %{etc_systemd_dir}
 Requires: gzip
+Requires: rpm-python
 Conflicts: %{alt_name}-server
 Obsoletes: %{alt_name}-server < %{version}
diff --git a/ipaplatform/redhat/tasks.py b/ipaplatform/redhat/tasks.py
index db31cd04cb234fac8fee97f3579ba2ca919f3262..2e894d776dcd5542e6c11cc0210add8ad9d90298 100644
--- a/ipaplatform/redhat/tasks.py
+++ b/ipaplatform/redhat/tasks.py
@@ -30,6 +30,8 @@ import socket
 import sys
 import urllib
 import base64
+import rpm
+from functools import total_ordering
 from subprocess import CalledProcessError
 from nss.error import NSPRError
@@ -46,6 +48,35 @@ from ipaplatform.redhat.authconfig import RedHatAuthConfig
 from ipaplatform.base.tasks import BaseTaskNamespace
+# copied from rpmUtils/miscutils.py
+def stringToVersion(verstring):
+    if verstring in [None, '']:
+        return (None, None, None)
+    i = verstring.find(':')
+    if i != -1:
+        try:
+            epoch = str(long(verstring[:i]))
+        except ValueError:
+            # look, garbage in the epoch field, how fun, kill it
+            epoch = '0' # this is our fallback, deal
+    else:
+        epoch = '0'
+    j = verstring.find('-')
+    if j != -1:
+        if verstring[i + 1:j] == '':
+            version = None
+        else:
+            version = verstring[i + 1:j]
+        release = verstring[j + 1:]
+    else:
+        if verstring[i + 1:] == '':
+            version = None
+        else:
+            version = verstring[i + 1:]
+        release = None
+    return (epoch, version, release)
+
+
 log = log_mgr.get_logger(__name__)
@@ -65,6 +96,21 @@ def selinux_enabled():
         return False
+@total_ordering
+class IPAVersion(object):
+
+    def __init__(self, version):
+        self.version_tuple = stringToVersion(version)
+
+    def __eq__(self, other):
+        assert isinstance(other, IPAVersion)
+        return rpm.labelCompare(self.version_tuple, other.version_tuple) == 0
+
+    def __lt__(self, other):
+        assert isinstance(other, IPAVersion)
+        return rpm.labelCompare(self.version_tuple, other.version_tuple) == -1
+
+
 class RedHatTaskNamespace(BaseTaskNamespace):
     def restore_context(self, filepath, restorecon=paths.SBIN_RESTORECON):
@@ -422,5 +468,12 @@ class RedHatTaskNamespace(BaseTaskNamespace):
         super(RedHatTaskNamespace, self).create_system_user(name, group,
             homedir, shell, uid, gid, comment, create_homedir)
+    def parse_ipa_version(self, version):
+        """
+        :param version: textual version
+        :return: object implementing proper __cmp__ method for version compare
+        """
+        return IPAVersion(version)
+
 tasks = RedHatTaskNamespace()
--
2.4.3
SOURCES/0161-DNS-fix-file-permissions.patch
New file
@@ -0,0 +1,40 @@
From 7aae209ba77a09de7cb09792d6ac16bb80683a2f Mon Sep 17 00:00:00 2001
From: Martin Basti <mbasti@redhat.com>
Date: Wed, 9 Dec 2015 12:12:22 +0100
Subject: [PATCH] DNS: fix file permissions
With non default umask named-pkcs11 cannot access the softhsm token storage
https://fedorahosted.org/freeipa/ticket/5520
Reviewed-By: Tomas Babej <tbabej@redhat.com>
---
 ipaserver/install/dnskeysyncinstance.py | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/ipaserver/install/dnskeysyncinstance.py b/ipaserver/install/dnskeysyncinstance.py
index 7d1351ccc57a5dbd7d537741545ad44d0dcd5eb1..590343c4e97fc882f296ac1aa69e43de9d35ed65 100644
--- a/ipaserver/install/dnskeysyncinstance.py
+++ b/ipaserver/install/dnskeysyncinstance.py
@@ -200,7 +200,9 @@ class DNSKeySyncInstance(service.Service):
         # create dnssec directory
         if not os.path.exists(paths.IPA_DNSSEC_DIR):
             self.logger.debug("Creating %s directory", paths.IPA_DNSSEC_DIR)
-            os.mkdir(paths.IPA_DNSSEC_DIR, 0770)
+            os.mkdir(paths.IPA_DNSSEC_DIR)
+            os.chmod(paths.IPA_DNSSEC_DIR, 0770)
+
             # chown ods:named
             os.chown(paths.IPA_DNSSEC_DIR, self.ods_uid, self.named_gid)
@@ -217,6 +219,7 @@ class DNSKeySyncInstance(service.Service):
         named_fd.truncate(0)
         named_fd.write(softhsm_conf_txt)
         named_fd.close()
+        os.chmod(paths.DNSSEC_SOFTHSM2_CONF, 0644)
         # setting up named to use softhsm2
         if not self.fstore.has_file(paths.SYSCONFIG_NAMED):
--
2.4.3
SOURCES/0162-Explicitly-call-chmod-on-newly-created-directories.patch
New file
@@ -0,0 +1,121 @@
From 7d7bb4789504a3f84e8ccf52abc06e8de109289a Mon Sep 17 00:00:00 2001
From: Martin Basti <mbasti@redhat.com>
Date: Wed, 9 Dec 2015 13:40:04 +0100
Subject: [PATCH] Explicitly call chmod on newly created directories
Without calling os.chmod(), umask is effective and may cause that
directory is created with permission that causes failure.
This can be related to https://fedorahosted.org/freeipa/ticket/5520
Reviewed-By: Tomas Babej <tbabej@redhat.com>
---
 ipaplatform/base/services.py             |  2 +-
 ipaserver/install/cainstance.py          |  1 +
 ipaserver/install/ipa_backup.py          |  7 ++++---
 ipaserver/install/ipa_replica_prepare.py |  3 ++-
 ipaserver/install/ipa_restore.py         | 10 ++++++----
 5 files changed, 14 insertions(+), 9 deletions(-)
diff --git a/ipaplatform/base/services.py b/ipaplatform/base/services.py
index 56e959e919e42281431240451071a2d4b8048e4a..b068a2f3b00549fffa20feffb6a3158382fc7e9a 100644
--- a/ipaplatform/base/services.py
+++ b/ipaplatform/base/services.py
@@ -421,7 +421,7 @@ class SystemdService(PlatformService):
             try:
                 if not ipautil.dir_exists(srv_tgt):
-                    os.mkdir(srv_tgt)
+                    os.mkdir(srv_tgt, 0755)
                 if os.path.exists(srv_lnk):
                     # Remove old link
                     os.unlink(srv_lnk)
diff --git a/ipaserver/install/cainstance.py b/ipaserver/install/cainstance.py
index c20bf39c12cff0777d90efad2b0d8d136ee37ec9..d9bf4f31af5a922dd6f977a5011f50ce7cea8896 100644
--- a/ipaserver/install/cainstance.py
+++ b/ipaserver/install/cainstance.py
@@ -978,6 +978,7 @@ class CAInstance(DogtagInstance):
         if not ipautil.dir_exists(self.ra_agent_db):
             os.mkdir(self.ra_agent_db)
+            os.chmod(self.ra_agent_db, 0755)
         # Create the password file for this db
         hex_str = binascii.hexlify(os.urandom(10))
diff --git a/ipaserver/install/ipa_backup.py b/ipaserver/install/ipa_backup.py
index 3bd2ef0203c1b5b596e092987acd894491ecae26..a5a4bef0a17f641fcea565d9a79c3e6887a064a7 100644
--- a/ipaserver/install/ipa_backup.py
+++ b/ipaserver/install/ipa_backup.py
@@ -279,8 +279,8 @@ class Backup(admintool.AdminTool):
         os.chown(self.top_dir, pent.pw_uid, pent.pw_gid)
         os.chmod(self.top_dir, 0750)
         self.dir = os.path.join(self.top_dir, "ipa")
-        os.mkdir(self.dir, 0750)
-
+        os.mkdir(self.dir)
+        os.chmod(self.dir, 0750)
         os.chown(self.dir, pent.pw_uid, pent.pw_gid)
         self.header = os.path.join(self.top_dir, 'header')
@@ -605,7 +605,8 @@ class Backup(admintool.AdminTool):
             backup_dir = os.path.join(paths.IPA_BACKUP_DIR, time.strftime('ipa-full-%Y-%m-%d-%H-%M-%S'))
             filename = os.path.join(backup_dir, "ipa-full.tar")
-        os.mkdir(backup_dir, 0700)
+        os.mkdir(backup_dir)
+        os.chmod(backup_dir, 0700)
         cwd = os.getcwd()
         os.chdir(self.dir)
diff --git a/ipaserver/install/ipa_replica_prepare.py b/ipaserver/install/ipa_replica_prepare.py
index 5246f5f5469c85571d04c99d872f38018802abaa..b9ae60e9bc9d40be5f86e312980846b2ad80f67d 100644
--- a/ipaserver/install/ipa_replica_prepare.py
+++ b/ipaserver/install/ipa_replica_prepare.py
@@ -345,7 +345,8 @@ class ReplicaPrepare(admintool.AdminTool):
         self.top_dir = tempfile.mkdtemp("ipa")
         self.dir = os.path.join(self.top_dir, "realm_info")
-        os.mkdir(self.dir, 0700)
+        os.mkdir(self.dir)
+        os.chmod(self.dir, 0700)
         try:
             self.copy_ds_certificate()
diff --git a/ipaserver/install/ipa_restore.py b/ipaserver/install/ipa_restore.py
index 57d5deb1e68af6e9ceb51f4dd751b8a59d9ac513..cdc460301ad8aeb658fec18da565238a376d1c0c 100644
--- a/ipaserver/install/ipa_restore.py
+++ b/ipaserver/install/ipa_restore.py
@@ -300,8 +300,8 @@ class Restore(admintool.AdminTool):
         os.chown(self.top_dir, pent.pw_uid, pent.pw_gid)
         os.chmod(self.top_dir, 0750)
         self.dir = os.path.join(self.top_dir, "ipa")
-        os.mkdir(self.dir, 0750)
-
+        os.mkdir(self.dir)
+        os.chmod(self.dir, 0750)
         os.chown(self.dir, pent.pw_uid, pent.pw_gid)
         cwd = os.getcwd()
@@ -527,7 +527,8 @@ class Restore(admintool.AdminTool):
         if not os.path.exists(ldifdir):
             pent = pwd.getpwnam(DS_USER)
-            os.mkdir(ldifdir, 0770)
+            os.mkdir(ldifdir)
+            os.chmod(ldifdir, 0770)
             os.chown(ldifdir, pent.pw_uid, pent.pw_gid)
         ipautil.backup_file(ldiffile)
@@ -804,7 +805,8 @@ class Restore(admintool.AdminTool):
         for dir in dirs:
             try:
                 self.log.debug('Creating %s' % dir)
-                os.mkdir(dir, 0770)
+                os.mkdir(dir)
+                os.chmod(dir, 0770)
                 os.chown(dir, pent.pw_uid, pent.pw_gid)
                 tasks.restore_context(dir)
             except Exception, e:
--
2.4.3
SOURCES/0163-Fix-replace-mkdir-with-chmod.patch
New file
@@ -0,0 +1,31 @@
From a600614ba36e192b168fcb4f9547c70fdc304707 Mon Sep 17 00:00:00 2001
From: Martin Basti <mbasti@redhat.com>
Date: Tue, 22 Dec 2015 16:34:32 +0100
Subject: [PATCH] Fix: replace mkdir with chmod
In original patches, extra mkdir has been added instead of chmod.
https://fedorahosted.org/freeipa/ticket/5520
Reviewed-By: Martin Basti <mbasti@redhat.com>
---
 ipaplatform/base/services.py | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/ipaplatform/base/services.py b/ipaplatform/base/services.py
index b068a2f3b00549fffa20feffb6a3158382fc7e9a..d280557c4f1c8ba353ba093736d577cc6c2a47a9 100644
--- a/ipaplatform/base/services.py
+++ b/ipaplatform/base/services.py
@@ -421,7 +421,8 @@ class SystemdService(PlatformService):
             try:
                 if not ipautil.dir_exists(srv_tgt):
-                    os.mkdir(srv_tgt, 0755)
+                    os.mkdir(srv_tgt)
+                    os.chmod(srv_tgt, 0755)
                 if os.path.exists(srv_lnk):
                     # Remove old link
                     os.unlink(srv_lnk)
--
2.4.3
SOURCES/0164-DNSSEC-Improve-error-reporting-from-ipa-ods-exporter.patch
New file
@@ -0,0 +1,38 @@
From 06df4a281c5e4b2be4840fb42129478279792a9d Mon Sep 17 00:00:00 2001
From: Petr Spacek <pspacek@redhat.com>
Date: Thu, 26 Nov 2015 14:56:00 +0100
Subject: [PATCH] DNSSEC: Improve error reporting from ipa-ods-exporter
https://fedorahosted.org/freeipa/ticket/5348
Reviewed-By: Martin Basti <mbasti@redhat.com>
Reviewed-By: Martin Basti <mbasti@redhat.com>
---
 daemons/dnssec/ipa-ods-exporter | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/daemons/dnssec/ipa-ods-exporter b/daemons/dnssec/ipa-ods-exporter
index f30a2253a713d857aa4e7566e52a0a45f7bd50c2..12a9294ae05d2ce8d206a2bbf74cc00d81259efa 100755
--- a/daemons/dnssec/ipa-ods-exporter
+++ b/daemons/dnssec/ipa-ods-exporter
@@ -31,6 +31,7 @@ import systemd.daemon
 import systemd.journal
 import sqlite3
 import time
+import traceback
 import ipalib
 from ipapython.dn import DN
@@ -576,7 +577,8 @@ try:
             sync_zone(log, ldap, dns_dn, zone_row['name'])
 except Exception as ex:
-    msg = "ipa-ods-exporter exception: %s" % ex
+    msg = "ipa-ods-exporter exception: %s" % traceback.format_exc(ex)
+    log.exception(ex)
     raise ex
 finally:
--
2.4.3
SOURCES/0165-DNSSEC-Make-sure-that-current-state-in-OpenDNSSEC-ma.patch
New file
@@ -0,0 +1,174 @@
From 6c012544655b3730ebb0a1551cdbce04ab686cfb Mon Sep 17 00:00:00 2001
From: Petr Spacek <pspacek@redhat.com>
Date: Tue, 24 Nov 2015 12:49:40 +0100
Subject: [PATCH] DNSSEC: Make sure that current state in OpenDNSSEC matches
 key state in LDAP
Previously we published timestamps of planned state changes in LDAP.
This led to situations where state transition in OpenDNSSEC was blocked
by an additional condition (or unavailability of OpenDNSSEC) but BIND
actually did the transition as planned.
Additionally key state mapping was incorrect for KSK so sometimes KSK
was not used for signing when it should.
Example (for code without this fix):
- Add a zone and let OpenDNSSEC to generate keys.
- Wait until keys are in state "published" and next state is "inactive".
- Shutdown OpenDNSSEC or break replication from DNSSEC key master.
- See that keys on DNS replicas will transition to state "inactive" even
  though it should not happen because OpenDNSSEC is not available
  (i.e. new keys may not be available).
- End result is that affected zone will not be signed anymore, even
  though it should stay signed with the old keys.
https://fedorahosted.org/freeipa/ticket/5348
Reviewed-By: Martin Basti <mbasti@redhat.com>
Reviewed-By: Martin Basti <mbasti@redhat.com>
---
 daemons/dnssec/ipa-ods-exporter | 105 ++++++++++++++++++++++++++++++++++++----
 1 file changed, 95 insertions(+), 10 deletions(-)
diff --git a/daemons/dnssec/ipa-ods-exporter b/daemons/dnssec/ipa-ods-exporter
index 12a9294ae05d2ce8d206a2bbf74cc00d81259efa..6ed7588847042e742abeef724940eec31f23ca8f 100755
--- a/daemons/dnssec/ipa-ods-exporter
+++ b/daemons/dnssec/ipa-ods-exporter
@@ -57,6 +57,14 @@ ODS_DB_LOCK_PATH = "%s%s" % (paths.OPENDNSSEC_KASP_DB, '.our_lock')
 SECRETKEY_WRAPPING_MECH = 'rsaPkcsOaep'
 PRIVKEY_WRAPPING_MECH = 'aesKeyWrapPad'
+# Constants from OpenDNSSEC's enforcer/ksm/include/ksm/ksm.h
+KSM_STATE_PUBLISH    = 2
+KSM_STATE_READY      = 3
+KSM_STATE_ACTIVE     = 4
+KSM_STATE_RETIRE     = 5
+KSM_STATE_DEAD       = 6
+KSM_STATE_KEYPUBLISH = 10
+
 # DNSKEY flag constants
 dnskey_flag_by_value = {
     0x0001: 'SEP',
@@ -122,6 +130,77 @@ def sql2ldap_keyid(sql_keyid):
     #uri += '%'.join(sql_keyid[i:i+2] for i in range(0, len(sql_keyid), 2))
     return {"idnsSecKeyRef": uri}
+def ods2bind_timestamps(key_state, key_type, ods_times):
+    """Transform (timestamps and key states) from ODS to set of BIND timestamps
+    with equivalent meaning. At the same time, remove timestamps
+    for future/planned state transitions to prevent ODS & BIND
+    from desynchronizing.
+
+    OpenDNSSEC database may contain timestamps for state transitions planned
+    in the future, but timestamp itself is not sufficient information because
+    there could be some additional condition which is guaded by OpenDNSSEC
+    itself.
+
+    BIND works directly with timestamps without any additional conditions.
+    This difference causes problem when state transition planned in OpenDNSSEC
+    does not happen as originally planned for some reason.
+
+    At the same time, this difference causes problem when OpenDNSSEC on DNSSEC
+    key master and BIND instances on replicas are not synchronized. This
+    happens when DNSSEC key master is down, or a replication is down. Even
+    a temporary desynchronization could cause DNSSEC validation failures
+    which could have huge impact.
+
+    To prevent this problem, this function removes all timestamps corresponding
+    to future state transitions. As a result, BIND will not do state transition
+    until it happens in OpenDNSSEC first and until the change is replicated.
+
+    Also, timestamp mapping depends on key type and is not 1:1.
+    For detailed description of the mapping please see
+    https://fedorahosted.org/bind-dyndb-ldap/wiki/BIND9/Design/DNSSEC/OpenDNSSEC2BINDKeyStates
+    """
+    bind_times = {}
+    # idnsSecKeyCreated is equivalent to SQL column 'created'
+    bind_times['idnsSecKeyCreated'] = ods_times['idnsSecKeyCreated']
+
+    # set of key states where publishing in DNS zone is desired is taken from
+    # opendnssec/enforcer/ksm/ksm_request.c:KsmRequestIssueKeys()
+    # TODO: support for RFC 5011, requires OpenDNSSEC v1.4.8+
+    if ('idnsSecKeyPublish' in ods_times and
+        key_state in {KSM_STATE_PUBLISH, KSM_STATE_READY, KSM_STATE_ACTIVE,
+                      KSM_STATE_RETIRE, KSM_STATE_KEYPUBLISH}):
+        bind_times['idnsSecKeyPublish'] = ods_times['idnsSecKeyPublish']
+
+    # ZSK and KSK handling differs in enforcerd, see
+    # opendnssec/enforcer/enforcerd/enforcer.c:commKeyConfig()
+    if key_type == 'ZSK':
+        # idnsSecKeyActivate cannot be set before the key reaches ACTIVE state
+        if ('idnsSecKeyActivate' in ods_times and
+            key_state in {KSM_STATE_ACTIVE, KSM_STATE_RETIRE, KSM_STATE_DEAD}):
+                bind_times['idnsSecKeyActivate'] = ods_times['idnsSecKeyActivate']
+
+        # idnsSecKeyInactive cannot be set before the key reaches RETIRE state
+        if ('idnsSecKeyInactive' in ods_times and
+            key_state in {KSM_STATE_RETIRE, KSM_STATE_DEAD}):
+                bind_times['idnsSecKeyInactive'] = ods_times['idnsSecKeyInactive']
+
+    elif key_type == 'KSK':
+        # KSK is special: it is used for signing as long as it is in zone
+        if ('idnsSecKeyPublish' in ods_times and
+            key_state in {KSM_STATE_PUBLISH, KSM_STATE_READY, KSM_STATE_ACTIVE,
+                          KSM_STATE_RETIRE, KSM_STATE_KEYPUBLISH}):
+            bind_times['idnsSecKeyActivate'] = ods_times['idnsSecKeyPublish']
+        # idnsSecKeyInactive is ignored for KSK on purpose
+
+    else:
+        assert False, "unsupported key type %s" % key_type
+
+    # idnsSecKeyDelete is relevant only in DEAD state
+    if 'idnsSecKeyDelete' in ods_times and key_state == KSM_STATE_DEAD:
+        bind_times['idnsSecKeyDelete'] = ods_times['idnsSecKeyDelete']
+
+    return bind_times
+
 class ods_db_lock(object):
     def __enter__(self):
         self.f = open(ODS_DB_LOCK_PATH, 'w')
@@ -172,18 +251,20 @@ def get_ods_keys(zone_name):
     assert len(rows) == 1, "exactly one DNS zone should exist in ODS DB"
     zone_id = rows[0][0]
-    # get all keys for given zone ID
-    cur = db.execute("SELECT kp.HSMkey_id, kp.generate, kp.algorithm, dnsk.publish, dnsk.active, dnsk.retire, dnsk.dead, dnsk.keytype "
-             "FROM keypairs AS kp JOIN dnsseckeys AS dnsk ON kp.id = dnsk.keypair_id "
-             "WHERE dnsk.zone_id = ?", (zone_id,))
+    # get relevant keys for given zone ID:
+    # ignore keys which were generated but not used yet
+    # key state check is using constants from
+    # OpenDNSSEC's enforcer/ksm/include/ksm/ksm.h
+    # WARNING! OpenDNSSEC version 1 and 2 are using different constants!
+    cur = db.execute("SELECT kp.HSMkey_id, kp.generate, kp.algorithm, "
+                     "dnsk.publish, dnsk.active, dnsk.retire, dnsk.dead, "
+                     "dnsk.keytype, dnsk.state "
+                     "FROM keypairs AS kp "
+                     "JOIN dnsseckeys AS dnsk ON kp.id = dnsk.keypair_id "
+                     "WHERE dnsk.zone_id = ?", (zone_id,))
     keys = {}
     for row in cur:
-        key_data = sql2datetimes(row)
-        if 'idnsSecKeyDelete' in key_data \
-            and key_data['idnsSecKeyDelete'] > datetime.now():
-                continue  # ignore deleted keys
-
-        key_data.update(sql2ldap_flags(row['keytype']))
+        key_data = sql2ldap_flags(row['keytype'])
         assert key_data.get('idnsSecKeyZONE', None) == 'TRUE', \
                 'unexpected key type 0x%x' % row['keytype']
         if key_data.get('idnsSecKeySEP', 'FALSE') == 'TRUE':
@@ -191,6 +272,10 @@ def get_ods_keys(zone_name):
         else:
             key_type = 'ZSK'
+        # transform key state to timestamps for BIND with equivalent semantics
+        ods_times = sql2datetimes(row)
+        key_data.update(ods2bind_timestamps(row['state'], key_type, ods_times))
+
         key_data.update(sql2ldap_algorithm(row['algorithm']))
         key_id = "%s-%s-%s" % (key_type,
                                datetime2ldap(key_data['idnsSecKeyCreated']),
--
2.4.3
SOURCES/0166-DNSSEC-Make-sure-that-current-key-state-in-LDAP-matc.patch
New file
@@ -0,0 +1,51 @@
From 31a9cec3fc366954b3cb8943621834fdfce04bd3 Mon Sep 17 00:00:00 2001
From: Petr Spacek <pspacek@redhat.com>
Date: Thu, 26 Nov 2015 15:19:03 +0100
Subject: [PATCH] DNSSEC: Make sure that current key state in LDAP matches key
 state in BIND
We have to explicitly specify "none" value to prevent dnssec-keyfromlabel
utility from using current time for keys without "publish" and "activate"
timestamps.
Previously this lead to situation where key was in (intermediate) state
"generated" in OpenDNSSEC but BIND started to use this key for signing.
https://fedorahosted.org/freeipa/ticket/5348
Reviewed-By: Martin Basti <mbasti@redhat.com>
Reviewed-By: Martin Basti <mbasti@redhat.com>
---
 ipapython/dnssec/bindmgr.py | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/ipapython/dnssec/bindmgr.py b/ipapython/dnssec/bindmgr.py
index 2c6781609594fa27812af3a01d16318198a3e120..70caaf4ee74f594c652cd82bccb8964e172bc719 100644
--- a/ipapython/dnssec/bindmgr.py
+++ b/ipapython/dnssec/bindmgr.py
@@ -58,6 +58,8 @@ class BINDMgr(object):
         return dt.strftime(time_bindfmt)
     def dates2params(self, ldap_attrs):
+        """Convert LDAP timestamps to list of parameters suitable
+        for dnssec-keyfromlabel utility"""
         attr2param = {'idnsseckeypublish': '-P',
                 'idnsseckeyactivate': '-A',
                 'idnsseckeyinactive': '-I',
@@ -65,10 +67,12 @@ class BINDMgr(object):
         params = []
         for attr, param in attr2param.items():
+            params.append(param)
             if attr in ldap_attrs:
-                params.append(param)
                 assert len(ldap_attrs[attr]) == 1, 'Timestamp %s is expected to be single-valued' % attr
                 params.append(self.time_ldap2bindfmt(ldap_attrs[attr][0]))
+            else:
+                params.append('none')
         return params
--
2.4.3
SOURCES/0167-DNSSEC-remove-obsolete-TODO-note.patch
New file
@@ -0,0 +1,28 @@
From bb592115d2fbab6ca9a1b664c3d9bf1bcad5ba0f Mon Sep 17 00:00:00 2001
From: Petr Spacek <pspacek@redhat.com>
Date: Wed, 2 Dec 2015 12:58:23 +0100
Subject: [PATCH] DNSSEC: remove obsolete TODO note
https://fedorahosted.org/freeipa/ticket/5348
Reviewed-By: Martin Basti <mbasti@redhat.com>
Reviewed-By: Martin Basti <mbasti@redhat.com>
---
 ipapython/dnssec/ldapkeydb.py | 1 -
 1 file changed, 1 deletion(-)
diff --git a/ipapython/dnssec/ldapkeydb.py b/ipapython/dnssec/ldapkeydb.py
index 23e6b019c2f2d2644bd389ffec4772b99b8cfc37..74371ae19ca2fb7564a343cc79be20798b99f6d2 100644
--- a/ipapython/dnssec/ldapkeydb.py
+++ b/ipapython/dnssec/ldapkeydb.py
@@ -181,7 +181,6 @@ class MasterKey(Key):
         # TODO: replace this with 'autogenerate' to prevent collisions
         uuid_rdn = DN('ipk11UniqueId=%s' % uuid.uuid1())
         entry_dn = DN(uuid_rdn, self.ldapkeydb.base_dn)
-        # TODO: add ipaWrappingMech attribute
         entry = self.ldap.make_entry(entry_dn,
                    objectClass=['ipaSecretKeyObject', 'ipk11Object'],
                    ipaSecretKey=data,
--
2.4.3
SOURCES/0168-DNSSEC-add-debug-mode-to-ldapkeydb.py.patch
New file
@@ -0,0 +1,105 @@
From 3daffad0d0e14790147fb7a3ba9be7072b79f3e2 Mon Sep 17 00:00:00 2001
From: Petr Spacek <pspacek@redhat.com>
Date: Tue, 15 Dec 2015 14:13:23 +0100
Subject: [PATCH] DNSSEC: add debug mode to ldapkeydb.py
ldapkeydb.py can be executed directly now. In that case it will print
out key metadata as obtained using IPA LDAP API.
Kerberos credential cache has to be filled with principal posessing
appropriate access rights before the script is execured.
https://fedorahosted.org/freeipa/ticket/5348
Reviewed-By: Martin Basti <mbasti@redhat.com>
Reviewed-By: Martin Basti <mbasti@redhat.com>
---
 ipapython/dnssec/ldapkeydb.py | 54 +++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 52 insertions(+), 2 deletions(-)
diff --git a/ipapython/dnssec/ldapkeydb.py b/ipapython/dnssec/ldapkeydb.py
index 74371ae19ca2fb7564a343cc79be20798b99f6d2..54a1fba1d2db8f27c9c9b881ff42201365852587 100644
--- a/ipapython/dnssec/ldapkeydb.py
+++ b/ipapython/dnssec/ldapkeydb.py
@@ -4,6 +4,8 @@
 from binascii import hexlify
 import collections
+import logging
+from pprint import pprint
 import sys
 import time
@@ -11,6 +13,7 @@ import ipalib
 from ipapython.dn import DN
 from ipapython import ipaldap
 from ipapython import ipautil
+from ipapython import ipa_log_manager
 from ipaplatform.paths import paths
 from abshsm import attrs_name2id, attrs_id2name, bool_attr_names, populate_pkcs11_metadata, AbstractHSM
@@ -135,8 +138,12 @@ class Key(collections.MutableMapping):
     def __len__(self):
         return len(self.entry)
-    def __str__(self):
-        return str(self.entry)
+    def __repr__(self):
+        sanitized = dict(self.entry)
+        for attr in ['ipaPrivateKey', 'ipaPublicKey', 'ipk11publickeyinfo']:
+            if attr in sanitized:
+                del sanitized[attr]
+        return repr(sanitized)
     def _cleanup_key(self):
         """remove default values from LDAP entry"""
@@ -347,3 +354,46 @@ class LdapKeyDB(AbstractHSM):
                 '(&(objectClass=ipk11PrivateKey)(objectClass=ipaPrivateKeyObject)(objectClass=ipk11PublicKey)(objectClass=ipaPublicKeyObject))'))
         return self.cache_zone_keypairs
+
+if __name__ == '__main__':
+    # this is debugging mode
+    # print information we think are useful to stdout
+    # other garbage goes via logger to stderr
+    ipa_log_manager.standard_logging_setup(debug=True)
+    log = ipa_log_manager.root_logger
+
+    # IPA framework initialization
+    ipalib.api.bootstrap(in_server=True, log=None)  # no logging to file
+    ipalib.api.finalize()
+
+    # LDAP initialization
+    dns_dn = DN(ipalib.api.env.container_dns, ipalib.api.env.basedn)
+    ldap = ipaldap.LDAPClient(ipalib.api.env.ldap_uri)
+    log.debug('Connecting to LDAP')
+    # GSSAPI will be used, used has to be kinited already
+    ldap.gssapi_bind()
+    log.debug('Connected')
+
+    ldapkeydb = LdapKeyDB(log, ldap, DN(('cn', 'keys'), ('cn', 'sec'),
+                          ipalib.api.env.container_dns,
+                          ipalib.api.env.basedn))
+
+    print('replica public keys: CKA_WRAP = TRUE')
+    print('====================================')
+    for pubkey_id, pubkey in ldapkeydb.replica_pubkeys_wrap.items():
+        print(hexlify(pubkey_id))
+        pprint(pubkey)
+
+    print('')
+    print('master keys')
+    print('===========')
+    for mkey_id, mkey in ldapkeydb.master_keys.items():
+        print(hexlify(mkey_id))
+        pprint(mkey)
+
+    print('')
+    print('zone key pairs')
+    print('==============')
+    for key_id, key in ldapkeydb.zone_keypairs.items():
+        print(hexlify(key_id))
+        pprint(key)
--
2.4.3
SOURCES/0169-DNSSEC-logging-improvements-in-ipa-ods-exporter.patch
New file
@@ -0,0 +1,69 @@
From 7398819200c9a3a32effa52793240a054bc4b10f Mon Sep 17 00:00:00 2001
From: Petr Spacek <pspacek@redhat.com>
Date: Tue, 15 Dec 2015 14:16:52 +0100
Subject: [PATCH] DNSSEC: logging improvements in ipa-ods-exporter
https://fedorahosted.org/freeipa/ticket/5348
Reviewed-By: Martin Basti <mbasti@redhat.com>
Reviewed-By: Martin Basti <mbasti@redhat.com>
---
 daemons/dnssec/ipa-ods-exporter | 17 +++++++++++------
 1 file changed, 11 insertions(+), 6 deletions(-)
diff --git a/daemons/dnssec/ipa-ods-exporter b/daemons/dnssec/ipa-ods-exporter
index 6ed7588847042e742abeef724940eec31f23ca8f..051fa53a950f7afbea5e9b1e541a9435aa02bc17 100755
--- a/daemons/dnssec/ipa-ods-exporter
+++ b/daemons/dnssec/ipa-ods-exporter
@@ -491,6 +491,11 @@ def cmd2ods_zone_name(cmd):
     return zone_name
 def sync_zone(log, ldap, dns_dn, zone_name):
+    """synchronize metadata about zone keys for single DNS zone
+
+    Key material has to be synchronized elsewhere.
+    Keep in mind that keys could be shared among multiple zones!"""
+    log.getChild("%s.%s" % (__name__, zone_name))
     log.debug('synchronizing zone "%s"', zone_name)
     ods_keys = get_ods_keys(zone_name)
     ods_keys_id = set(ods_keys.keys())
@@ -523,30 +528,30 @@ def sync_zone(log, ldap, dns_dn, zone_name):
     ldap_keys_id = set(ldap_keys.keys())
     new_keys_id = ods_keys_id - ldap_keys_id
-    log.info('new keys from ODS: %s', new_keys_id)
+    log.info('new key metadata from ODS: %s', new_keys_id)
     for key_id in new_keys_id:
         cn = "cn=%s" % key_id
         key_dn = DN(cn, keys_dn)
-        log.debug('adding key "%s" to LDAP', key_dn)
+        log.debug('adding key metadata "%s" to LDAP', key_dn)
         ldap_key = ldap.make_entry(key_dn,
                                    objectClass=['idnsSecKey'],
                                    **ods_keys[key_id])
         ldap.add_entry(ldap_key)
     deleted_keys_id = ldap_keys_id - ods_keys_id
-    log.info('deleted keys in LDAP: %s', deleted_keys_id)
+    log.info('deleted key metadata in LDAP: %s', deleted_keys_id)
     for key_id in deleted_keys_id:
         cn = "cn=%s" % key_id
         key_dn = DN(cn, keys_dn)
-        log.debug('deleting key "%s" from LDAP', key_dn)
+        log.debug('deleting key metadata "%s" from LDAP', key_dn)
         ldap.delete_entry(key_dn)
     update_keys_id = ldap_keys_id.intersection(ods_keys_id)
-    log.info('keys in LDAP & ODS: %s', update_keys_id)
+    log.info('key metadata in LDAP & ODS: %s', update_keys_id)
     for key_id in update_keys_id:
         ldap_key = ldap_keys[key_id]
         ods_key = ods_keys[key_id]
-        log.debug('updating key "%s" in LDAP', ldap_key.dn)
+        log.debug('updating key metadata "%s" in LDAP', ldap_key.dn)
         ldap_key.update(ods_key)
         try:
             ldap.update_entry(ldap_key)
--
2.4.3
SOURCES/0170-DNSSEC-remove-keys-purged-by-OpenDNSSEC-from-master-.patch
New file
@@ -0,0 +1,248 @@
From db24ab9357ea63deaf25e8b9c5b3ad2d08a0c82b Mon Sep 17 00:00:00 2001
From: Petr Spacek <pspacek@redhat.com>
Date: Tue, 15 Dec 2015 15:22:45 +0100
Subject: [PATCH] DNSSEC: remove keys purged by OpenDNSSEC from master HSM from
 LDAP
Key purging has to be only only after key metadata purging so
ipa-dnskeysyncd on replices does not fail while dereferencing
non-existing keys.
https://fedorahosted.org/freeipa/ticket/5334
Reviewed-By: Martin Basti <mbasti@redhat.com>
Reviewed-By: Martin Basti <mbasti@redhat.com>
---
 daemons/dnssec/ipa-ods-exporter | 45 ++++++++++++++++++++++----
 ipapython/dnssec/ldapkeydb.py   | 72 ++++++++++++++++++++++++++++++++++-------
 2 files changed, 99 insertions(+), 18 deletions(-)
diff --git a/daemons/dnssec/ipa-ods-exporter b/daemons/dnssec/ipa-ods-exporter
index 051fa53a950f7afbea5e9b1e541a9435aa02bc17..2a1cc4315355569b24ec6ef42a68f4d64fee9f4f 100755
--- a/daemons/dnssec/ipa-ods-exporter
+++ b/daemons/dnssec/ipa-ods-exporter
@@ -387,7 +387,10 @@ def master2ldap_master_keys_sync(log, ldapkeydb, localhsm):
     ldapkeydb.flush()
 def master2ldap_zone_keys_sync(log, ldapkeydb, localhsm):
-    # synchroniza zone keys
+    """add and update zone key material from local HSM to LDAP
+
+    No key material will be removed, only new keys will be added or updated.
+    Key removal is hanled by master2ldap_zone_keys_purge()."""
     log = log.getChild('master2ldap_zone_keys')
     keypairs_ldap = ldapkeydb.zone_keypairs
     log.debug("zone keys in LDAP: %s", hex_set(keypairs_ldap))
@@ -396,10 +399,10 @@ def master2ldap_zone_keys_sync(log, ldapkeydb, localhsm):
     privkeys_local = localhsm.zone_privkeys
     log.debug("zone keys in local HSM: %s", hex_set(privkeys_local))
-    assert set(pubkeys_local) == set(privkeys_local), \
-            "IDs of private and public keys for DNS zones in local HSM does " \
-            "not match to key pairs: %s vs. %s" % \
-            (hex_set(pubkeys_local), hex_set(privkeys_local))
+    assert set(pubkeys_local) == set(privkeys_local), (
+            "IDs of private and public keys for DNS zones in local HSM does "
+            "not match to key pairs: %s vs. %s" %
+            (hex_set(pubkeys_local), hex_set(privkeys_local)))
     new_keys = set(pubkeys_local) - set(keypairs_ldap)
     log.debug("new zone keys in local HSM: %s", hex_set(new_keys))
@@ -420,6 +423,29 @@ def master2ldap_zone_keys_sync(log, ldapkeydb, localhsm):
     sync_set_metadata_2ldap(log, privkeys_local, keypairs_ldap)
     ldapkeydb.flush()
+def master2ldap_zone_keys_purge(log, ldapkeydb, localhsm):
+    """purge removed key material from LDAP (but not metadata)
+
+    Keys which are present in LDAP but not in local HSM will be removed.
+    Key metadata must be removed first so references to removed key material
+    are removed before actually removing the keys."""
+    keypairs_ldap = ldapkeydb.zone_keypairs
+    log.debug("zone keys in LDAP: %s", hex_set(keypairs_ldap))
+
+    pubkeys_local = localhsm.zone_pubkeys
+    privkeys_local = localhsm.zone_privkeys
+    log.debug("zone keys in local HSM: %s", hex_set(privkeys_local))
+    assert set(pubkeys_local) == set(privkeys_local), \
+            "IDs of private and public keys for DNS zones in local HSM does " \
+            "not match to key pairs: %s vs. %s" % \
+            (hex_set(pubkeys_local), hex_set(privkeys_local))
+
+    deleted_key_ids = set(keypairs_ldap) - set(pubkeys_local)
+    log.debug("zone keys deleted from local HSM but present in LDAP: %s",
+            hex_set(deleted_key_ids))
+    for zkey_id in deleted_key_ids:
+        keypairs_ldap[zkey_id].schedule_deletion()
+    ldapkeydb.flush()
 def hex_set(s):
     out = set()
@@ -600,7 +626,7 @@ ldap.connect(ccache=ccache_name)
 log.debug('Connected')
-### DNSSEC master: key synchronization
+### DNSSEC master: key material upload & synchronization (but not deletion)
 ldapkeydb = LdapKeyDB(log, ldap, DN(('cn', 'keys'), ('cn', 'sec'),
                                     ipalib.api.env.container_dns,
                                     ipalib.api.env.basedn))
@@ -612,7 +638,7 @@ master2ldap_master_keys_sync(log, ldapkeydb, localhsm)
 master2ldap_zone_keys_sync(log, ldapkeydb, localhsm)
-### DNSSEC master: DNSSEC key metadata upload
+### DNSSEC master: DNSSEC key metadata upload & synchronization & deletion
 # command receive is delayed so the command will stay in socket queue until
 # the problem with LDAP server or HSM is fixed
 try:
@@ -666,6 +692,11 @@ try:
         for zone_row in db.execute("SELECT name FROM zones"):
             sync_zone(log, ldap, dns_dn, zone_row['name'])
+    ### DNSSEC master: DNSSEC key material purging
+    # references to old key material were removed above in sync_zone()
+    # so now we can purge old key material from LDAP
+    master2ldap_zone_keys_purge(log, ldapkeydb, localhsm)
+
 except Exception as ex:
     msg = "ipa-ods-exporter exception: %s" % traceback.format_exc(ex)
     log.exception(ex)
diff --git a/ipapython/dnssec/ldapkeydb.py b/ipapython/dnssec/ldapkeydb.py
index 54a1fba1d2db8f27c9c9b881ff42201365852587..2131508cc6779d1cc99c417a31da866b7fdcf2c2 100644
--- a/ipapython/dnssec/ldapkeydb.py
+++ b/ipapython/dnssec/ldapkeydb.py
@@ -105,40 +105,56 @@ def get_default_attrs(object_classes):
         result.update(defaults[cls])
     return result
+
 class Key(collections.MutableMapping):
     """abstraction to hide LDAP entry weirdnesses:
         - non-normalized attribute names
         - boolean attributes returned as strings
+        - planned entry deletion prevents subsequent use of the instance
     """
     def __init__(self, entry, ldap, ldapkeydb):
         self.entry = entry
+        self._delentry = None  # indicates that object was deleted
         self.ldap = ldap
         self.ldapkeydb = ldapkeydb
         self.log = ldap.log.getChild(__name__)
+    def __assert_not_deleted(self):
+        assert self.entry and not self._delentry, (
+            "attempt to use to-be-deleted entry %s detected"
+            % self._delentry.dn)
+
     def __getitem__(self, key):
+        self.__assert_not_deleted()
         val = self.entry.single_value[key]
         if key.lower() in bool_attr_names:
             val = ldap_bool(val)
         return val
     def __setitem__(self, key, value):
+        self.__assert_not_deleted()
         self.entry[key] = value
     def __delitem__(self, key):
+        self.__assert_not_deleted()
         del self.entry[key]
     def __iter__(self):
         """generates list of ipa names of all PKCS#11 attributes present in the object"""
+        self.__assert_not_deleted()
         for ipa_name in self.entry.keys():
             lowercase = ipa_name.lower()
             if lowercase in attrs_name2id:
                 yield lowercase
     def __len__(self):
+        self.__assert_not_deleted()
         return len(self.entry)
     def __repr__(self):
+        if self._delentry:
+            return 'deleted entry: %s' % repr(self._delentry)
+
         sanitized = dict(self.entry)
         for attr in ['ipaPrivateKey', 'ipaPublicKey', 'ipk11publickeyinfo']:
             if attr in sanitized:
@@ -153,6 +169,49 @@ class Key(collections.MutableMapping):
             if self.get(attr, empty) == default_attrs[attr]:
                 del self[attr]
+    def _update_key(self):
+        """remove default values from LDAP entry and write back changes"""
+        if self._delentry:
+            self._delete_key()
+            return
+
+        self._cleanup_key()
+
+        try:
+            self.ldap.update_entry(self.entry)
+        except ipalib.errors.EmptyModlist:
+            pass
+
+    def _delete_key(self):
+        """remove key metadata entry from LDAP
+
+        After calling this, the python object is no longer valid and all
+        subsequent method calls on it will fail.
+        """
+        assert not self.entry, (
+            "Key._delete_key() called before Key.schedule_deletion()")
+        assert self._delentry, "Key._delete_key() called more than once"
+        self.log.debug('deleting key id 0x%s DN %s from LDAP',
+                       hexlify(self._delentry.single_value['ipk11id']),
+                       self._delentry.dn)
+        self.ldap.delete_entry(self._delentry)
+        self._delentry = None
+        self.ldap = None
+        self.ldapkeydb = None
+
+    def schedule_deletion(self):
+        """schedule key deletion from LDAP
+
+        Calling schedule_deletion() will make this object incompatible with
+        normal Key. After that the object must not be read or modified.
+        Key metadata will be actually deleted when LdapKeyDB.flush() is called.
+        """
+        assert not self._delentry, (
+            "Key.schedule_deletion() called more than once")
+        self._delentry = self.entry
+        self.entry = None
+
+
 class ReplicaKey(Key):
     # TODO: object class assert
     def __init__(self, entry, ldap, ldapkeydb):
@@ -239,21 +298,12 @@ class LdapKeyDB(AbstractHSM):
         self._update_keys()
         return keys
-    def _update_key(self, key):
-        """remove default values from LDAP entry and write back changes"""
-        key._cleanup_key()
-
-        try:
-            self.ldap.update_entry(key.entry)
-        except ipalib.errors.EmptyModlist:
-            pass
-
     def _update_keys(self):
         for cache in [self.cache_masterkeys, self.cache_replica_pubkeys_wrap,
-                self.cache_zone_keypairs]:
+                      self.cache_zone_keypairs]:
             if cache:
                 for key in cache.itervalues():
-                    self._update_key(key)
+                    key._update_key()
     def flush(self):
         """write back content of caches to LDAP"""
--
2.4.3
SOURCES/0171-DNSSEC-ipa-dnskeysyncd-Skip-zones-with-old-DNSSEC-me.patch
New file
@@ -0,0 +1,125 @@
From 96695bb8e414be941e07d43652516a99cfd89a24 Mon Sep 17 00:00:00 2001
From: Petr Spacek <pspacek@redhat.com>
Date: Sun, 20 Dec 2015 18:36:48 +0100
Subject: [PATCH] DNSSEC: ipa-dnskeysyncd: Skip zones with old DNSSEC metadata
 in LDAP
This filtering is useful in cases where LDAP contains DNS zones which
have old metadata objects and DNSSEC disabled. Such zones must be
ignored to prevent errors while calling dnssec-keyfromlabel or rndc.
https://fedorahosted.org/freeipa/ticket/5348
Reviewed-By: Martin Basti <mbasti@redhat.com>
Reviewed-By: Martin Basti <mbasti@redhat.com>
---
 ipapython/dnssec/bindmgr.py   | 16 +++++++++++++---
 ipapython/dnssec/keysyncer.py | 24 ++++++++++++++++++------
 2 files changed, 31 insertions(+), 9 deletions(-)
diff --git a/ipapython/dnssec/bindmgr.py b/ipapython/dnssec/bindmgr.py
index 70caaf4ee74f594c652cd82bccb8964e172bc719..d81dad940df49e79f4534c2224c10ecf192794ba 100644
--- a/ipapython/dnssec/bindmgr.py
+++ b/ipapython/dnssec/bindmgr.py
@@ -191,10 +191,20 @@ class BINDMgr(object):
         self.notify_zone(zone)
-    def sync(self):
-        """Synchronize list of zones in LDAP with BIND."""
+    def sync(self, dnssec_zones):
+        """Synchronize list of zones in LDAP with BIND.
+
+        dnssec_zones lists zones which should be processed. All other zones
+        will be ignored even though they were modified using ldap_event().
+
+        This filter is useful in cases where LDAP contains DNS zones which
+        have old metadata objects and DNSSEC disabled. Such zones must be
+        ignored to prevent errors while calling dnssec-keyfromlabel or rndc.
+        """
         self.log.debug('Key metadata in LDAP: %s' % self.ldap_keys)
-        for zone in self.modified_zones:
+        self.log.debug('Zones modified but skipped during bindmgr.sync: %s',
+                       self.modified_zones - dnssec_zones)
+        for zone in self.modified_zones.intersection(dnssec_zones):
             self.sync_zone(zone)
         self.modified_zones = set()
diff --git a/ipapython/dnssec/keysyncer.py b/ipapython/dnssec/keysyncer.py
index de5b5aa5f670db4c58fb92b989e181d45d887b55..5ba9baaab30b07d2f4e4ae3a3507898d08a0d6c1 100644
--- a/ipapython/dnssec/keysyncer.py
+++ b/ipapython/dnssec/keysyncer.py
@@ -6,6 +6,8 @@ import logging
 import ldap.dn
 import os
+import dns.name
+
 from ipaplatform.paths import paths
 from ipapython import ipautil
@@ -33,6 +35,7 @@ class KeySyncer(SyncReplConsumer):
         self.bindmgr = BINDMgr(self.api)
         self.init_done = False
+        self.dnssec_zones = set()
         SyncReplConsumer.__init__(self, *args, **kwargs)
     def _get_objclass(self, attrs):
@@ -112,7 +115,7 @@ class KeySyncer(SyncReplConsumer):
         self.ods_sync()
         self.hsm_replica_sync()
         self.hsm_master_sync()
-        self.bindmgr.sync()
+        self.bindmgr.sync(self.dnssec_zones)
     # idnsSecKey wrapper
     # Assumption: metadata points to the same key blob all the time,
@@ -121,23 +124,29 @@ class KeySyncer(SyncReplConsumer):
     def key_meta_add(self, uuid, dn, newattrs):
         self.hsm_replica_sync()
         self.bindmgr.ldap_event('add', uuid, newattrs)
-        self.bindmgr_sync()
+        self.bindmgr_sync(self.dnssec_zones)
     def key_meta_del(self, uuid, dn, oldattrs):
         self.bindmgr.ldap_event('del', uuid, oldattrs)
-        self.bindmgr_sync()
+        self.bindmgr_sync(self.dnssec_zones)
         self.hsm_replica_sync()
     def key_metadata_sync(self, uuid, dn, oldattrs, newattrs):
         self.bindmgr.ldap_event('mod', uuid, newattrs)
-        self.bindmgr_sync()
+        self.bindmgr_sync(self.dnssec_zones)
-    def bindmgr_sync(self):
+    def bindmgr_sync(self, dnssec_zones):
         if self.init_done:
-            self.bindmgr.sync()
+            self.bindmgr.sync(dnssec_zones)
     # idnsZone wrapper
     def zone_add(self, uuid, dn, newattrs):
+        zone = dns.name.from_text(newattrs['idnsname'][0])
+        if self.__is_dnssec_enabled(newattrs):
+            self.dnssec_zones.add(zone)
+        else:
+            self.dnssec_zones.discard(zone)
+
         if not self.ismaster:
             return
@@ -146,6 +155,9 @@ class KeySyncer(SyncReplConsumer):
         self.ods_sync()
     def zone_del(self, uuid, dn, oldattrs):
+        zone = dns.name.from_text(oldattrs['idnsname'][0])
+        self.dnssec_zones.discard(zone)
+
         if not self.ismaster:
             return
--
2.4.3
SOURCES/0172-DNSSEC-ipa-ods-exporter-add-ldap-cleanup-command.patch
New file
@@ -0,0 +1,142 @@
From a5687f3070877fc28435d9db4d5fed8c521dbf41 Mon Sep 17 00:00:00 2001
From: Petr Spacek <pspacek@redhat.com>
Date: Sun, 20 Dec 2015 19:19:28 +0100
Subject: [PATCH] DNSSEC: ipa-ods-exporter: add ldap-cleanup command
Command "ldap-cleanup <zone name>" will remove all key metadata from
LDAP. This can be used manually in sequence like:
ldap-cleanup <zone name>
update <zone name>
to delete all key metadata from LDAP and re-export them from OpenDNSSEC.
ldap-cleanup command should be called when disabling DNSSEC on a DNS
zone to remove stale key metadata from LDAP.
https://fedorahosted.org/freeipa/ticket/5348
Reviewed-By: Martin Basti <mbasti@redhat.com>
Reviewed-By: Martin Basti <mbasti@redhat.com>
---
 daemons/dnssec/ipa-ods-exporter | 60 ++++++++++++++++++++++++++++++++---------
 1 file changed, 48 insertions(+), 12 deletions(-)
diff --git a/daemons/dnssec/ipa-ods-exporter b/daemons/dnssec/ipa-ods-exporter
index 2a1cc4315355569b24ec6ef42a68f4d64fee9f4f..8abb5cf7c6d1e5e8ea996b8925d2e8cffc44133c 100755
--- a/daemons/dnssec/ipa-ods-exporter
+++ b/daemons/dnssec/ipa-ods-exporter
@@ -227,7 +227,9 @@ def get_ldap_zone(ldap, dns_base, name):
         except ipalib.errors.NotFound:
             continue
-    assert ldap_zone is not None, 'DNS zone "%s" should exist in LDAP' % name
+    if ldap_zone is None:
+        raise ipalib.errors.NotFound(
+            reason='DNS zone "%s" not found in LDAP' % name)
     return ldap_zone
@@ -481,25 +483,37 @@ def parse_command(cmd):
     if cmd == 'ipa-hsm-update':
         return (0,
                 'HSM synchronization finished, skipping zone synchronization.',
-                None)
+                None,
+                cmd)
     elif cmd == 'ipa-full-update':
         return (None,
                 'Synchronization of all zones was finished.',
-                None)
+                None,
+                cmd)
+
+    elif cmd.startswith('ldap-cleanup '):
+        zone_name = cmd2ods_zone_name(cmd)
+        return (None,
+                'Zone "%s" metadata will be removed from LDAP.\n' % zone_name,
+                zone_name,
+                'ldap-cleanup')
-    elif not cmd.startswith('update '):
+    elif cmd.startswith('update '):
+        zone_name = cmd2ods_zone_name(cmd)
+        return (None,
+                'Zone "%s" metadata will be updated in LDAP.\n' % zone_name,
+                zone_name,
+                'update')
+
+    else:
         return (0,
                 'Command "%s" is not supported by IPA; '
                 'HSM synchronization was finished and the command '
                 'will be ignored.' % cmd,
+                None,
                 None)
-    else:
-        zone_name = cmd2ods_zone_name(cmd)
-        return (None,
-                'Zone was "%s" updated.\n' % zone_name,
-                zone_name)
 def send_systemd_reply(conn, reply):
         # Reply & close connection early.
@@ -510,7 +524,7 @@ def send_systemd_reply(conn, reply):
 def cmd2ods_zone_name(cmd):
     # ODS stores zone name without trailing period
-    zone_name = cmd[7:].strip()
+    zone_name = cmd.split(' ', 1)[1].strip()
     if len(zone_name) > 1 and zone_name[-1] == '.':
         zone_name = zone_name[:-1]
@@ -584,6 +598,25 @@ def sync_zone(log, ldap, dns_dn, zone_name):
         except ipalib.errors.EmptyModlist:
             continue
+def cleanup_ldap_zone(log, ldap, dns_dn, zone_name):
+    """delete all key metadata about zone keys for single DNS zone
+
+    Key material has to be synchronized elsewhere.
+    Keep in mind that keys could be shared among multiple zones!"""
+    log = log.getChild("%s.%s" % (__name__, zone_name))
+    log.debug('cleaning up key metadata from zone "%s"', zone_name)
+
+    try:
+        ldap_zone = get_ldap_zone(ldap, dns_dn, zone_name)
+        ldap_keys = get_ldap_keys(ldap, ldap_zone.dn)
+    except ipalib.errors.NotFound as ex:
+        # zone or cn=keys container does not exist, we are done
+        log.debug(str(ex))
+        return
+
+    for ldap_key in ldap_keys:
+        log.debug('deleting key metadata "%s"', ldap_key.dn)
+        ldap.delete_entry(ldap_key)
 log = logging.getLogger('root')
 # this service is usually socket-activated
@@ -656,7 +689,7 @@ except KeyError as e:
     conn = None
     cmd = sys.argv[1]
-exitcode, msg, zone_name = parse_command(cmd)
+exitcode, msg, zone_name, cmd = parse_command(cmd)
 if exitcode is not None:
     if conn:
@@ -686,7 +719,10 @@ try:
     if zone_name is not None:
         # only one zone should be processed
-        sync_zone(log, ldap, dns_dn, zone_name)
+        if cmd == 'update':
+            sync_zone(log, ldap, dns_dn, zone_name)
+        elif cmd == 'ldap-cleanup':
+            cleanup_ldap_zone(log, ldap, dns_dn, zone_name)
     else:
         # process all zones
         for zone_row in db.execute("SELECT name FROM zones"):
--
2.4.3
SOURCES/0173-DNSSEC-ipa-dnskeysyncd-call-ods-signer-ldap-cleanup-.patch
New file
@@ -0,0 +1,44 @@
From dbb7b91bf052bc4d5a4f545044e8949b67b511db Mon Sep 17 00:00:00 2001
From: Petr Spacek <pspacek@redhat.com>
Date: Sun, 20 Dec 2015 19:35:55 +0100
Subject: [PATCH] DNSSEC: ipa-dnskeysyncd: call ods-signer ldap-cleanup on zone
 removal
Command "ldap-cleanup <zone name>" is called to remove all key metadata from
LDAP. This command is now called when disabling DNSSEC on a DNS zone. The stale
metadata were causing problems when re-enabling DNSSEC on the same zone.
https://fedorahosted.org/freeipa/ticket/5348
Reviewed-By: Martin Basti <mbasti@redhat.com>
Reviewed-By: Martin Basti <mbasti@redhat.com>
---
 ipapython/dnssec/odsmgr.py | 6 ++++++
 1 file changed, 6 insertions(+)
diff --git a/ipapython/dnssec/odsmgr.py b/ipapython/dnssec/odsmgr.py
index efbe16cc6ebf050d9cf347ed97b2b2e4b37c8a6e..44d94ac3ab9e68feba067e8f14530894bda22855 100644
--- a/ipapython/dnssec/odsmgr.py
+++ b/ipapython/dnssec/odsmgr.py
@@ -152,12 +152,18 @@ class ODSMgr(object):
         output = self.ksmutil(cmd)
         self.log.info(output)
         self.notify_enforcer()
+        self.cleanup_signer(name)
     def notify_enforcer(self):
         cmd = ['notify']
         output = self.ksmutil(cmd)
         self.log.info(output)
+    def cleanup_signer(self, zone_name):
+        cmd = ['ods-signer', 'ldap-cleanup', str(zone_name)]
+        output = ipautil.run(cmd, capture_output=True)
+        self.log.info(output)
+
     def ldap_event(self, op, uuid, attrs):
         """Record single LDAP event - zone addition or deletion.
--
2.4.3
SOURCES/0174-DNSSEC-Log-debug-messages-at-log-level-DEBUG.patch
New file
@@ -0,0 +1,29 @@
From d3c3377ecf597787af7d8975f6d0a822798585f9 Mon Sep 17 00:00:00 2001
From: Petr Spacek <pspacek@redhat.com>
Date: Tue, 24 Nov 2015 12:49:16 +0100
Subject: [PATCH] DNSSEC: Log debug messages at log level DEBUG
https://fedorahosted.org/freeipa/ticket/5348
Reviewed-By: Martin Basti <mbasti@redhat.com>
Reviewed-By: Martin Basti <mbasti@redhat.com>
---
 daemons/dnssec/ipa-ods-exporter | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/daemons/dnssec/ipa-ods-exporter b/daemons/dnssec/ipa-ods-exporter
index 8abb5cf7c6d1e5e8ea996b8925d2e8cffc44133c..2b71b0c38eeb9c5f61adecdb802c081433e3db27 100755
--- a/daemons/dnssec/ipa-ods-exporter
+++ b/daemons/dnssec/ipa-ods-exporter
@@ -310,7 +310,7 @@ def ldap2master_replica_keys_sync(log, ldapkeydb, localhsm):
     log.info("new replica keys in LDAP: %s", hex_set(new_replica_keys))
     for key_id in new_replica_keys:
         new_key_ldap = ldapkeydb.replica_pubkeys_wrap[key_id]
-        log.error('label=%s,  id=%s, data=%s',
+        log.debug('label=%s, id=%s, data=%s',
                 new_key_ldap['ipk11label'],
                 hexlify(new_key_ldap['ipk11id']),
                 hexlify(new_key_ldap['ipapublickey']))
--
2.4.3
SOURCES/0175-Allow-to-used-mixed-case-for-sysrestore.patch
New file
@@ -0,0 +1,86 @@
From f7e06707be5c590ce03a50117da483931a0680c6 Mon Sep 17 00:00:00 2001
From: Martin Basti <mbasti@redhat.com>
Date: Tue, 5 Jan 2016 17:58:49 +0100
Subject: [PATCH] Allow to used mixed case for sysrestore
This commit allows to use mixed case of keys for sysrestore, before this
commit all keys were saved in lowercase what prevents to accesing them.
Original usage of mixed case for sysretore key in opendssecinstance had
to be changed to lowercase to prevent issues on already installed
systems.
https://fedorahosted.org/freeipa/ticket/5574
Reviewed-By: Martin Babinsky <mbabinsk@redhat.com>
---
 ipapython/sysrestore.py                 | 4 ++++
 ipaserver/install/opendnssecinstance.py | 6 ++++--
 2 files changed, 8 insertions(+), 2 deletions(-)
diff --git a/ipapython/sysrestore.py b/ipapython/sysrestore.py
index 1a111258bc0f6dd503673028d3a990821f077fef..97f0d760ae58c8d4bd0409565bf47167a689a06c 100644
--- a/ipapython/sysrestore.py
+++ b/ipapython/sysrestore.py
@@ -67,6 +67,7 @@ class FileStore:
         self.files = {}
         p = ConfigParser.SafeConfigParser()
+        p.optionxform = str
         p.read(self._index)
         for section in p.sections():
@@ -88,6 +89,7 @@ class FileStore:
             return
         p = ConfigParser.SafeConfigParser()
+        p.optionxform = str
         p.add_section('files')
         for (key, value) in self.files.items():
@@ -324,6 +326,7 @@ class StateFile:
         self.modules = {}
         p = ConfigParser.SafeConfigParser()
+        p.optionxform = str
         p.read(self._path)
         for module in p.sections():
@@ -352,6 +355,7 @@ class StateFile:
             return
         p = ConfigParser.SafeConfigParser()
+        p.optionxform = str
         for module in self.modules.keys():
             p.add_section(module)
diff --git a/ipaserver/install/opendnssecinstance.py b/ipaserver/install/opendnssecinstance.py
index c5377d910d8f38a1ea0e05461ecf1b92f05ca2ca..2ab320b8e5db6c7c4e03850ecfe26e0e178d04a1 100644
--- a/ipaserver/install/opendnssecinstance.py
+++ b/ipaserver/install/opendnssecinstance.py
@@ -265,11 +265,11 @@ class OpenDNSSECInstance(service.Service):
     def __setup_dnssec(self):
         # run once only
-        if self.get_state("KASP_DB_configured") and not self.kasp_db_file:
+        if self.get_state("kasp_db_configured") and not self.kasp_db_file:
             root_logger.debug("Already configured, skipping step")
             return
-        self.backup_state("KASP_DB_configured", True)
+        self.backup_state("kasp_db_configured", True)
         if not self.fstore.has_file(paths.OPENDNSSEC_KASP_DB):
             self.fstore.backup_file(paths.OPENDNSSEC_KASP_DB)
@@ -368,6 +368,8 @@ class OpenDNSSECInstance(service.Service):
                 root_logger.debug(error)
                 pass
+        self.restore_state("kasp_db_configured")  # just eat state
+
         # disabled by default, by ldap_enable()
         if enabled:
             self.enable()
--
2.4.3
SOURCES/0176-prevent-crash-of-CA-less-server-upgrade-due-to-absen.patch
New file
@@ -0,0 +1,77 @@
From 61f54afcde1df217fec01aa9ab38b0b9b704c501 Mon Sep 17 00:00:00 2001
From: Martin Babinsky <mbabinsk@redhat.com>
Date: Tue, 5 Jan 2016 13:00:24 +0100
Subject: [PATCH] prevent crash of CA-less server upgrade due to absent
 certmonger
ipa-server-upgrade tests whether certmonger service is running before
attempting to upgrade IPA master. This causes the upgrader to always fail when
there is no CA installer and certmonger is not needed, effectively preventing
CA-less IPA master to upgrade succefuly.
This test is now skipped if CA is not enabled.
https://fedorahosted.org/freeipa/ticket/5519
Reviewed-By: Jan Cholasta <jcholast@redhat.com>
---
 ipaserver/install/server/upgrade.py | 29 +++++++++++++++++++++++++++--
 1 file changed, 27 insertions(+), 2 deletions(-)
diff --git a/ipaserver/install/server/upgrade.py b/ipaserver/install/server/upgrade.py
index 945cb3ebd63767cb1d57083e1da7c5605ac5a2f9..616fba5c1a5b3737481aecbb09ab5344641a3b04 100644
--- a/ipaserver/install/server/upgrade.py
+++ b/ipaserver/install/server/upgrade.py
@@ -292,6 +292,24 @@ def setup_firefox_extension(fstore):
     http.setup_firefox_extension(realm, domain)
+def is_ca_enabled():
+    """
+    check whether there is an active CA master
+    :return: True if there is an active CA in topology, False otherwise
+    """
+    ldap2 = api.Backend.ldap2
+    was_connected = ldap2.isconnected()
+
+    if not was_connected:
+        ldap2.connect()
+
+    try:
+        return api.Command.ca_is_enabled()['result']
+    finally:
+        if not was_connected:
+            ldap2.disconnect()
+
+
 def ca_configure_profiles_acl(ca):
     root_logger.info('[Authorizing RA Agent to modify profiles]')
@@ -1416,7 +1434,9 @@ def upgrade_configuration():
     http = httpinstance.HTTPInstance(fstore)
     http.configure_selinux_for_httpd()
     http.change_mod_nss_port_from_http()
-    http.configure_certmonger_renewal_guard()
+
+    if is_ca_enabled():
+        http.configure_certmonger_renewal_guard()
     ds.configure_dirsrv_ccache()
@@ -1562,7 +1582,12 @@ def upgrade_check(options):
         print unicode(e)
         sys.exit(1)
-    if not services.knownservices.certmonger.is_running():
+    try:
+        ca_is_enabled = is_ca_enabled()
+    except Exception as e:
+        raise RuntimeError("Cannot connect to LDAP server: {0}".format(e))
+
+    if not services.knownservices.certmonger.is_running() and ca_is_enabled:
         raise RuntimeError('Certmonger is not running. Start certmonger and run upgrade again.')
     if not options.skip_version_check:
--
2.4.3
SOURCES/0177-Upgrade-Fix-upgrade-of-NIS-Server-configuration.patch
New file
@@ -0,0 +1,251 @@
From e22b4320033803331ce8960d05113666fe9192ec Mon Sep 17 00:00:00 2001
From: Martin Basti <mbasti@redhat.com>
Date: Wed, 6 Jan 2016 19:47:22 +0100
Subject: [PATCH] Upgrade: Fix upgrade of NIS Server configuration
Former upgrade file always created the NIS Server container, that caused
the ipa-nis-manage did not set all required NIS maps. Default creation
of container has been removed.
Updating of NIS Server configuration and
NIS maps is done only if the NIS Server container exists.
https://fedorahosted.org/freeipa/ticket/5507
Reviewed-By: Alexander Bokovoy <abokovoy@redhat.com>
---
 install/share/Makefile.am               |  1 +
 install/share/nis-update.uldif          | 38 +++++++++++++++
 install/updates/50-nis.update           | 58 ++--------------------
 ipaplatform/base/paths.py               |  1 +
 ipaserver/install/plugins/update_nis.py | 86 +++++++++++++++++++++++++++++++++
 5 files changed, 129 insertions(+), 55 deletions(-)
 create mode 100644 install/share/nis-update.uldif
 create mode 100644 ipaserver/install/plugins/update_nis.py
diff --git a/install/share/Makefile.am b/install/share/Makefile.am
index e4cca8708ab0042d6cb37eba31341e53e3cdac4d..09a341ce177e16e14e7d606e5628e6ca21ddf872 100644
--- a/install/share/Makefile.am
+++ b/install/share/Makefile.am
@@ -60,6 +60,7 @@ app_DATA =                \
     memberof-task.ldif        \
     memberof-conf.ldif        \
     nis.uldif            \
+    nis-update.uldif            \
     opendnssec_conf.template    \
     opendnssec_kasp.template    \
     unique-attributes.ldif        \
diff --git a/install/share/nis-update.uldif b/install/share/nis-update.uldif
new file mode 100644
index 0000000000000000000000000000000000000000..e602c1de061fbcece349b2d86970c4db5051473b
--- /dev/null
+++ b/install/share/nis-update.uldif
@@ -0,0 +1,38 @@
+# Updates for NIS
+
+# Correct syntax error that caused users to not appear
+dn: nis-domain=$DOMAIN+nis-map=netgroup, cn=NIS Server, cn=plugins, cn=config
+replace:nis-value-format: %merge(" ","%{memberNisNetgroup}","(%link(\"%ifeq(\\\"hostCategory\\\",\\\"all\\\",\\\"\\\",\\\"%collect(\\\\\\\"%{externalHost}\\\\\\\",\\\\\\\"%deref(\\\\\\\\\\\\\\\"memberHost\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"fqdn\\\\\\\\\\\\\\\")\\\\\\\",\\\\\\\"%deref_r(\\\\\\\\\\\\\\\"member\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"fqdn\\\\\\\\\\\\\\\")\\\\\\\",\\\\\\\"%deref_r(\\\\\\\\\\\\\\\"memberHost\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"member\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"fqdn\\\\\\\\\\\\\\\")\\\\\\\")\\\")\",\"-\",\",\",\"%ifeq(\\\"userCategory\\\",\\\"all\\\",\\\"\\\",\\\"%collect(\\\"%deref(\\\\\\\"memberUser\\\\\\\",\\\\\\\"uid\\\\\\\")\\\",\\\"%deref_r(\\\\\\\"member\\\\\\\",\\\\\\\"uid\\\\\\\")\\\",\\\"%deref_r(\\\\\\\"memberUser\\\\\\\",\\\\\\\"member\\\\\\\",\\\\\\\"uid\\\\\\\")\\\")\\\")\",\"-\"),%{nisDomainName:-})")::%merge(" ","%{memberNisNetgroup}","(%link(\"%ifeq(\\\"hostCategory\\\",\\\"all\\\",\\\"\\\",\\\"%collect(\\\\\\\"%{externalHost}\\\\\\\",\\\\\\\"%deref(\\\\\\\\\\\\\\\"memberHost\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"fqdn\\\\\\\\\\\\\\\")\\\\\\\",\\\\\\\"%deref_r(\\\\\\\\\\\\\\\"member\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"fqdn\\\\\\\\\\\\\\\")\\\\\\\",\\\\\\\"%deref_r(\\\\\\\\\\\\\\\"memberHost\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"member\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"fqdn\\\\\\\\\\\\\\\")\\\\\\\")\\\")\",\"-\",\",\",\"%ifeq(\\\"userCategory\\\",\\\"all\\\",\\\"\\\",\\\"%collect(\\\\\\\"%deref(\\\\\\\\\\\\\\\"memberUser\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"uid\\\\\\\\\\\\\\\")\\\\\\\",\\\\\\\"%deref_r(\\\\\\\\\\\\\\\"member\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"uid\\\\\\\\\\\\\\\")\\\\\\\",\\\\\\\"%deref_r(\\\\\\\\\\\\\\\"memberUser\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"member\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"uid\\\\\\\\\\\\\\\")\\\\\\\")\\\")\",\"-\"),%{nisDomainName:-})")
+
+# Correct syntax error that caused nested netgroups to not work
+# https://bugzilla.redhat.com/show_bug.cgi?id=788625
+dn: nis-domain=$DOMAIN+nis-map=netgroup, cn=NIS Server, cn=plugins, cn=config
+replace:nis-value-format: %merge(" ","%{memberNisNetgroup}","(%link(\"%ifeq(\\\"hostCategory\\\",\\\"all\\\",\\\"\\\",\\\"%collect(\\\\\\\"%{externalHost}\\\\\\\",\\\\\\\"%deref(\\\\\\\\\\\\\\\"memberHost\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"fqdn\\\\\\\\\\\\\\\")\\\\\\\",\\\\\\\"%deref_r(\\\\\\\\\\\\\\\"member\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"fqdn\\\\\\\\\\\\\\\")\\\\\\\",\\\\\\\"%deref_r(\\\\\\\\\\\\\\\"memberHost\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"member\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"fqdn\\\\\\\\\\\\\\\")\\\\\\\")\\\")\",\"-\",\",\",\"%ifeq(\\\"userCategory\\\",\\\"all\\\",\\\"\\\",\\\"%collect(\\\\\\\"%deref(\\\\\\\\\\\\\\\"memberUser\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"uid\\\\\\\\\\\\\\\")\\\\\\\",\\\\\\\"%deref_r(\\\\\\\\\\\\\\\"member\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"uid\\\\\\\\\\\\\\\")\\\\\\\",\\\\\\\"%deref_r(\\\\\\\\\\\\\\\"memberUser\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"member\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"uid\\\\\\\\\\\\\\\")\\\\\\\")\\\")\",\"-\"),%{nisDomainName:-})")::%merge(" ","%deref_f(\"member\",\"(objectclass=ipanisNetgroup)\",\"cn\")","(%link(\"%ifeq(\\\"hostCategory\\\",\\\"all\\\",\\\"\\\",\\\"%collect(\\\\\\\"%{externalHost}\\\\\\\",\\\\\\\"%deref(\\\\\\\\\\\\\\\"memberHost\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"fqdn\\\\\\\\\\\\\\\")\\\\\\\",\\\\\\\"%deref_r(\\\\\\\\\\\\\\\"member\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"fqdn\\\\\\\\\\\\\\\")\\\\\\\",\\\\\\\"%deref_r(\\\\\\\\\\\\\\\"memberHost\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"member\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"fqdn\\\\\\\\\\\\\\\")\\\\\\\")\\\")\",\"-\",\",\",\"%ifeq(\\\"userCategory\\\",\\\"all\\\",\\\"\\\",\\\"%collect(\\\\\\\"%deref(\\\\\\\\\\\\\\\"memberUser\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"uid\\\\\\\\\\\\\\\")\\\\\\\",\\\\\\\"%deref_r(\\\\\\\\\\\\\\\"member\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"uid\\\\\\\\\\\\\\\")\\\\\\\",\\\\\\\"%deref_r(\\\\\\\\\\\\\\\"memberUser\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"member\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"uid\\\\\\\\\\\\\\\")\\\\\\\")\\\")\",\"-\"),%{nisDomainName:-})")
+
+# Make the padding an expression so usercat and hostcat always gets
+# evaluated when displaying entries.
+# https://bugzilla.redhat.com/show_bug.cgi?id=767372
+dn: nis-domain=$DOMAIN+nis-map=netgroup, cn=NIS Server, cn=plugins, cn=config
+replace:nis-value-format: %merge(" ","%deref_f(\"member\",\"(objectclass=ipanisNetgroup)\",\"cn\")","(%link(\"%ifeq(\\\"hostCategory\\\",\\\"all\\\",\\\"\\\",\\\"%collect(\\\\\\\"%{externalHost}\\\\\\\",\\\\\\\"%deref(\\\\\\\\\\\\\\\"memberHost\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"fqdn\\\\\\\\\\\\\\\")\\\\\\\",\\\\\\\"%deref_r(\\\\\\\\\\\\\\\"member\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"fqdn\\\\\\\\\\\\\\\")\\\\\\\",\\\\\\\"%deref_r(\\\\\\\\\\\\\\\"memberHost\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"member\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"fqdn\\\\\\\\\\\\\\\")\\\\\\\")\\\")\",\"-\",\",\",\"%ifeq(\\\"userCategory\\\",\\\"all\\\",\\\"\\\",\\\"%collect(\\\\\\\"%deref(\\\\\\\\\\\\\\\"memberUser\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"uid\\\\\\\\\\\\\\\")\\\\\\\",\\\\\\\"%deref_r(\\\\\\\\\\\\\\\"member\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"uid\\\\\\\\\\\\\\\")\\\\\\\",\\\\\\\"%deref_r(\\\\\\\\\\\\\\\"memberUser\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"member\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"uid\\\\\\\\\\\\\\\")\\\\\\\")\\\")\",\"-\"),%{nisDomainName:-})")::%merge(" ","%deref_f(\"member\",\"(objectclass=ipanisNetgroup)\",\"cn\")","(%link(\"%ifeq(\\\"hostCategory\\\",\\\"all\\\",\\\"\\\",\\\"%collect(\\\\\\\"%{externalHost}\\\\\\\",\\\\\\\"%deref(\\\\\\\\\\\\\\\"memberHost\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"fqdn\\\\\\\\\\\\\\\")\\\\\\\",\\\\\\\"%deref_r(\\\\\\\\\\\\\\\"member\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"fqdn\\\\\\\\\\\\\\\")\\\\\\\",\\\\\\\"%deref_r(\\\\\\\\\\\\\\\"memberHost\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"member\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"fqdn\\\\\\\\\\\\\\\")\\\\\\\")\\\")\",\"%ifeq(\\\"hostCategory\\\",\\\"all\\\",\\\"\\\",\\\"-\\\")\",\",\",\"%ifeq(\\\"userCategory\\\",\\\"all\\\",\\\"\\\",\\\"%collect(\\\\\\\"%deref(\\\\\\\\\\\\\\\"memberUser\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"uid\\\\\\\\\\\\\\\")\\\\\\\",\\\\\\\"%deref_r(\\\\\\\\\\\\\\\"member\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"uid\\\\\\\\\\\\\\\")\\\\\\\",\\\\\\\"%deref_r(\\\\\\\\\\\\\\\"memberUser\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"member\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"uid\\\\\\\\\\\\\\\")\\\\\\\")\\\")\",\"%ifeq(\\\"userCategory\\\",\\\"all\\\",\\\"\\\",\\\"-\\\")\"),%{nisDomainName:-})")
+
+dn: nis-domain=$DOMAIN+nis-map=ethers.byaddr, cn=NIS Server, cn=plugins, cn=config
+default:objectclass: top
+default:objectclass: extensibleObject
+default:nis-domain: $DOMAIN
+default:nis-map: ethers.byaddr
+default:nis-base: cn=computers, cn=accounts, $SUFFIX
+default:nis-filter: (&(macAddress=*)(fqdn=*)(objectClass=ipaHost))
+default:nis-keys-format: %mregsub("%{macAddress} %{fqdn}","(..)[:\\\|-](..)[:\\\|-](..)[:\\\|-](..)[:\\\|-](..)[:\\\|-](..) (.*)","%1:%2:%3:%4:%5:%6")
+default:nis-values-format: %mregsub("%{macAddress} %{fqdn}","(..)[:\\\|-](..)[:\\\|-](..)[:\\\|-](..)[:\\\|-](..)[:\\\|-](..) (.*)","%1:%2:%3:%4:%5:%6 %7")
+default:nis-secure: no
+
+dn: nis-domain=$DOMAIN+nis-map=ethers.byname, cn=NIS Server, cn=plugins, cn=config
+default:objectclass: top
+default:objectclass: extensibleObject
+default:nis-domain: $DOMAIN
+default:nis-map: ethers.byname
+default:nis-base: cn=computers, cn=accounts, $SUFFIX
+default:nis-filter: (&(macAddress=*)(fqdn=*)(objectClass=ipaHost))
+default:nis-keys-format: %mregsub("%{macAddress} %{fqdn}","(..)[:\\\|-](..)[:\\\|-](..)[:\\\|-](..)[:\\\|-](..)[:\\\|-](..) (.*)","%7")
+default:nis-values-format: %mregsub("%{macAddress} %{fqdn}","(..)[:\\\|-](..)[:\\\|-](..)[:\\\|-](..)[:\\\|-](..)[:\\\|-](..) (.*)","%1:%2:%3:%4:%5:%6 %7")
+default:nis-secure: no
diff --git a/install/updates/50-nis.update b/install/updates/50-nis.update
index 149889ec7bdb38073eb6df88628792526cfe58e6..05a166f003aefc50fc25f10f01f7364d752425bc 100644
--- a/install/updates/50-nis.update
+++ b/install/updates/50-nis.update
@@ -1,55 +1,3 @@
-# NIS Server plugin must be disabled by default
-# command 'ipa-nis-manage enable' enables NIS server
-dn: cn=NIS Server,cn=plugins,cn=config
-default:objectclass: top
-default:objectclass: nsSlapdPlugin
-default:objectclass: extensibleObject
-default:cn: NIS Server
-default:nsslapd-pluginpath: /usr/lib$LIBARCH/dirsrv/plugins/nisserver-plugin.so
-default:nsslapd-plugininitfunc: nis_plugin_init
-default:nsslapd-plugintype: object
-default:nsslapd-pluginbetxn: on
-default:nsslapd-pluginenabled: off
-default:nsslapd-pluginid: nis-server
-default:nsslapd-pluginversion: 0.10
-default:nsslapd-pluginvendor: redhat.com
-default:nsslapd-plugindescription: NIS Server Plugin
-default:nis-tcp-wrappers-name: nis-server
-
-# Correct syntax error that caused users to not appear
-dn: nis-domain=$DOMAIN+nis-map=netgroup, cn=NIS Server, cn=plugins, cn=config
-replace:nis-value-format: %merge(" ","%{memberNisNetgroup}","(%link(\"%ifeq(\\\"hostCategory\\\",\\\"all\\\",\\\"\\\",\\\"%collect(\\\\\\\"%{externalHost}\\\\\\\",\\\\\\\"%deref(\\\\\\\\\\\\\\\"memberHost\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"fqdn\\\\\\\\\\\\\\\")\\\\\\\",\\\\\\\"%deref_r(\\\\\\\\\\\\\\\"member\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"fqdn\\\\\\\\\\\\\\\")\\\\\\\",\\\\\\\"%deref_r(\\\\\\\\\\\\\\\"memberHost\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"member\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"fqdn\\\\\\\\\\\\\\\")\\\\\\\")\\\")\",\"-\",\",\",\"%ifeq(\\\"userCategory\\\",\\\"all\\\",\\\"\\\",\\\"%collect(\\\"%deref(\\\\\\\"memberUser\\\\\\\",\\\\\\\"uid\\\\\\\")\\\",\\\"%deref_r(\\\\\\\"member\\\\\\\",\\\\\\\"uid\\\\\\\")\\\",\\\"%deref_r(\\\\\\\"memberUser\\\\\\\",\\\\\\\"member\\\\\\\",\\\\\\\"uid\\\\\\\")\\\")\\\")\",\"-\"),%{nisDomainName:-})")::%merge(" ","%{memberNisNetgroup}","(%link(\"%ifeq(\\\"hostCategory\\\",\\\"all\\\",\\\"\\\",\\\"%collect(\\\\\\\"%{externalHost}\\\\\\\",\\\\\\\"%deref(\\\\\\\\\\\\\\\"memberHost\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"fqdn\\\\\\\\\\\\\\\")\\\\\\\",\\\\\\\"%deref_r(\\\\\\\\\\\\\\\"member\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"fqdn\\\\\\\\\\\\\\\")\\\\\\\",\\\\\\\"%deref_r(\\\\\\\\\\\\\\\"memberHost\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"member\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"fqdn\\\\\\\\\\\\\\\")\\\\\\\")\\\")\",\"-\",\",\",\"%ifeq(\\\"userCategory\\\",\\\"all\\\",\\\"\\\",\\\"%collect(\\\\\\\"%deref(\\\\\\\\\\\\\\\"memberUser\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"uid\\\\\\\\\\\\\\\")\\\\\\\",\\\\\\\"%deref_r(\\\\\\\\\\\\\\\"member\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"uid\\\\\\\\\\\\\\\")\\\\\\\",\\\\\\\"%deref_r(\\\\\\\\\\\\\\\"memberUser\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"member\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"uid\\\\\\\\\\\\\\\")\\\\\\\")\\\")\",\"-\"),%{nisDomainName:-})")
-
-# Correct syntax error that caused nested netgroups to not work
-# https://bugzilla.redhat.com/show_bug.cgi?id=788625
-dn: nis-domain=$DOMAIN+nis-map=netgroup, cn=NIS Server, cn=plugins, cn=config
-replace:nis-value-format: %merge(" ","%{memberNisNetgroup}","(%link(\"%ifeq(\\\"hostCategory\\\",\\\"all\\\",\\\"\\\",\\\"%collect(\\\\\\\"%{externalHost}\\\\\\\",\\\\\\\"%deref(\\\\\\\\\\\\\\\"memberHost\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"fqdn\\\\\\\\\\\\\\\")\\\\\\\",\\\\\\\"%deref_r(\\\\\\\\\\\\\\\"member\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"fqdn\\\\\\\\\\\\\\\")\\\\\\\",\\\\\\\"%deref_r(\\\\\\\\\\\\\\\"memberHost\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"member\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"fqdn\\\\\\\\\\\\\\\")\\\\\\\")\\\")\",\"-\",\",\",\"%ifeq(\\\"userCategory\\\",\\\"all\\\",\\\"\\\",\\\"%collect(\\\\\\\"%deref(\\\\\\\\\\\\\\\"memberUser\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"uid\\\\\\\\\\\\\\\")\\\\\\\",\\\\\\\"%deref_r(\\\\\\\\\\\\\\\"member\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"uid\\\\\\\\\\\\\\\")\\\\\\\",\\\\\\\"%deref_r(\\\\\\\\\\\\\\\"memberUser\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"member\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"uid\\\\\\\\\\\\\\\")\\\\\\\")\\\")\",\"-\"),%{nisDomainName:-})")::%merge(" ","%deref_f(\"member\",\"(objectclass=ipanisNetgroup)\",\"cn\")","(%link(\"%ifeq(\\\"hostCategory\\\",\\\"all\\\",\\\"\\\",\\\"%collect(\\\\\\\"%{externalHost}\\\\\\\",\\\\\\\"%deref(\\\\\\\\\\\\\\\"memberHost\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"fqdn\\\\\\\\\\\\\\\")\\\\\\\",\\\\\\\"%deref_r(\\\\\\\\\\\\\\\"member\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"fqdn\\\\\\\\\\\\\\\")\\\\\\\",\\\\\\\"%deref_r(\\\\\\\\\\\\\\\"memberHost\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"member\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"fqdn\\\\\\\\\\\\\\\")\\\\\\\")\\\")\",\"-\",\",\",\"%ifeq(\\\"userCategory\\\",\\\"all\\\",\\\"\\\",\\\"%collect(\\\\\\\"%deref(\\\\\\\\\\\\\\\"memberUser\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"uid\\\\\\\\\\\\\\\")\\\\\\\",\\\\\\\"%deref_r(\\\\\\\\\\\\\\\"member\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"uid\\\\\\\\\\\\\\\")\\\\\\\",\\\\\\\"%deref_r(\\\\\\\\\\\\\\\"memberUser\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"member\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"uid\\\\\\\\\\\\\\\")\\\\\\\")\\\")\",\"-\"),%{nisDomainName:-})")
-
-# Make the padding an expression so usercat and hostcat always gets
-# evaluated when displaying entries.
-# https://bugzilla.redhat.com/show_bug.cgi?id=767372
-dn: nis-domain=$DOMAIN+nis-map=netgroup, cn=NIS Server, cn=plugins, cn=config
-replace:nis-value-format: %merge(" ","%deref_f(\"member\",\"(objectclass=ipanisNetgroup)\",\"cn\")","(%link(\"%ifeq(\\\"hostCategory\\\",\\\"all\\\",\\\"\\\",\\\"%collect(\\\\\\\"%{externalHost}\\\\\\\",\\\\\\\"%deref(\\\\\\\\\\\\\\\"memberHost\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"fqdn\\\\\\\\\\\\\\\")\\\\\\\",\\\\\\\"%deref_r(\\\\\\\\\\\\\\\"member\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"fqdn\\\\\\\\\\\\\\\")\\\\\\\",\\\\\\\"%deref_r(\\\\\\\\\\\\\\\"memberHost\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"member\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"fqdn\\\\\\\\\\\\\\\")\\\\\\\")\\\")\",\"-\",\",\",\"%ifeq(\\\"userCategory\\\",\\\"all\\\",\\\"\\\",\\\"%collect(\\\\\\\"%deref(\\\\\\\\\\\\\\\"memberUser\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"uid\\\\\\\\\\\\\\\")\\\\\\\",\\\\\\\"%deref_r(\\\\\\\\\\\\\\\"member\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"uid\\\\\\\\\\\\\\\")\\\\\\\",\\\\\\\"%deref_r(\\\\\\\\\\\\\\\"memberUser\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"member\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"uid\\\\\\\\\\\\\\\")\\\\\\\")\\\")\",\"-\"),%{nisDomainName:-})")::%merge(" ","%deref_f(\"member\",\"(objectclass=ipanisNetgroup)\",\"cn\")","(%link(\"%ifeq(\\\"hostCategory\\\",\\\"all\\\",\\\"\\\",\\\"%collect(\\\\\\\"%{externalHost}\\\\\\\",\\\\\\\"%deref(\\\\\\\\\\\\\\\"memberHost\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"fqdn\\\\\\\\\\\\\\\")\\\\\\\",\\\\\\\"%deref_r(\\\\\\\\\\\\\\\"member\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"fqdn\\\\\\\\\\\\\\\")\\\\\\\",\\\\\\\"%deref_r(\\\\\\\\\\\\\\\"memberHost\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"member\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"fqdn\\\\\\\\\\\\\\\")\\\\\\\")\\\")\",\"%ifeq(\\\"hostCategory\\\",\\\"all\\\",\\\"\\\",\\\"-\\\")\",\",\",\"%ifeq(\\\"userCategory\\\",\\\"all\\\",\\\"\\\",\\\"%collect(\\\\\\\"%deref(\\\\\\\\\\\\\\\"memberUser\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"uid\\\\\\\\\\\\\\\")\\\\\\\",\\\\\\\"%deref_r(\\\\\\\\\\\\\\\"member\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"uid\\\\\\\\\\\\\\\")\\\\\\\",\\\\\\\"%deref_r(\\\\\\\\\\\\\\\"memberUser\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"member\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"uid\\\\\\\\\\\\\\\")\\\\\\\")\\\")\",\"%ifeq(\\\"userCategory\\\",\\\"all\\\",\\\"\\\",\\\"-\\\")\"),%{nisDomainName:-})")
-
-dn: nis-domain=$DOMAIN+nis-map=ethers.byaddr, cn=NIS Server, cn=plugins, cn=config
-default:objectclass: top
-default:objectclass: extensibleObject
-default:nis-domain: $DOMAIN
-default:nis-map: ethers.byaddr
-default:nis-base: cn=computers, cn=accounts, $SUFFIX
-default:nis-filter: (&(macAddress=*)(fqdn=*)(objectClass=ipaHost))
-default:nis-keys-format: %mregsub("%{macAddress} %{fqdn}","(..)[:\\\|-](..)[:\\\|-](..)[:\\\|-](..)[:\\\|-](..)[:\\\|-](..) (.*)","%1:%2:%3:%4:%5:%6")
-default:nis-values-format: %mregsub("%{macAddress} %{fqdn}","(..)[:\\\|-](..)[:\\\|-](..)[:\\\|-](..)[:\\\|-](..)[:\\\|-](..) (.*)","%1:%2:%3:%4:%5:%6 %7")
-default:nis-secure: no
-
-dn: nis-domain=$DOMAIN+nis-map=ethers.byname, cn=NIS Server, cn=plugins, cn=config
-default:objectclass: top
-default:objectclass: extensibleObject
-default:nis-domain: $DOMAIN
-default:nis-map: ethers.byname
-default:nis-base: cn=computers, cn=accounts, $SUFFIX
-default:nis-filter: (&(macAddress=*)(fqdn=*)(objectClass=ipaHost))
-default:nis-keys-format: %mregsub("%{macAddress} %{fqdn}","(..)[:\\\|-](..)[:\\\|-](..)[:\\\|-](..)[:\\\|-](..)[:\\\|-](..) (.*)","%7")
-default:nis-values-format: %mregsub("%{macAddress} %{fqdn}","(..)[:\\\|-](..)[:\\\|-](..)[:\\\|-](..)[:\\\|-](..)[:\\\|-](..) (.*)","%1:%2:%3:%4:%5:%6 %7")
-default:nis-secure: no
-
+# Updates are applied only if NIS plugin has been configured
+# update definitions are located in install/share/nis-update.uldif
+plugin: update_nis_configuration
diff --git a/ipaplatform/base/paths.py b/ipaplatform/base/paths.py
index 215caf90ea1ca4e5db8f43f8f09002ce5d5cd280..12eb50bb2edd1680313c1e23c7ec83ede9fbd70c 100644
--- a/ipaplatform/base/paths.py
+++ b/ipaplatform/base/paths.py
@@ -247,6 +247,7 @@ class BasePathNamespace(object):
     HTML_KRBREALM_CON = "/usr/share/ipa/html/krbrealm.con"
     PREFERENCES_HTML = "/usr/share/ipa/html/preferences.html"
     NIS_ULDIF = "/usr/share/ipa/nis.uldif"
+    NIS_UPDATE_ULDIF = "/usr/share/ipa/nis-update.uldif"
     IPA_PLUGINS = "/usr/share/ipa/plugins"
     SCHEMA_COMPAT_ULDIF = "/usr/share/ipa/schema_compat.uldif"
     IPA_JS_PLUGINS_DIR = "/usr/share/ipa/ui/js/plugins"
diff --git a/ipaserver/install/plugins/update_nis.py b/ipaserver/install/plugins/update_nis.py
new file mode 100644
index 0000000000000000000000000000000000000000..6e12fed8c26cd3b12052700f4d4be5734121fc64
--- /dev/null
+++ b/ipaserver/install/plugins/update_nis.py
@@ -0,0 +1,86 @@
+#
+# Copyright (C) 2015  FreeIPA Contributors see COPYING for license
+#
+
+from ipalib.plugable import Registry
+from ipalib import errors
+from ipalib import Updater
+from ipaplatform.paths import paths
+from ipapython.dn import DN
+from ipaserver.install import sysupgrade
+from ipaserver.install.ldapupdate import LDAPUpdate
+
+register = Registry()
+
+
+@register()
+class update_nis_configuration(Updater):
+    """Update NIS configuration
+
+    NIS configuration can be updated only if NIS Server was configured via
+    ipa-nis-manage command.
+    """
+
+    def __recover_from_missing_maps(self, ldap):
+        # https://fedorahosted.org/freeipa/ticket/5507
+        # if all following DNs are missing, but 'NIS Server' container exists
+        # we are experiencig bug and maps should be fixed
+
+        if sysupgrade.get_upgrade_state('nis',
+                                        'done_recover_from_missing_maps'):
+            # this recover must be done only once, a user may deleted some
+            # maps, we do not want to restore them again
+            return
+
+        self.log.debug("Recovering from missing NIS maps bug")
+
+        suffix = "cn=NIS Server,cn=plugins,cn=config"
+        domain = self.api.env.domain
+        missing_dn_list = [
+            DN(nis_map.format(domain=domain, suffix=suffix)) for nis_map in [
+                "nis-domain={domain}+nis-map=passwd.byname,{suffix}",
+                "nis-domain={domain}+nis-map=passwd.byuid,{suffix}",
+                "nis-domain={domain}+nis-map=group.byname,{suffix}",
+                "nis-domain={domain}+nis-map=group.bygid,{suffix}",
+                "nis-domain={domain}+nis-map=netid.byname,{suffix}",
+                "nis-domain={domain}+nis-map=netgroup,{suffix}",
+            ]
+        ]
+
+        for dn in missing_dn_list:
+            try:
+                ldap.get_entry(dn, attrs_list=['cn'])
+            except errors.NotFound:
+                pass
+            else:
+                # bug is not effective, at least one of 'possible missing'
+                # maps was detected
+                return
+
+        sysupgrade.set_upgrade_state('nis', 'done_recover_from_missing_maps',
+                                     True)
+
+        # bug is effective run update to recreate missing maps
+        ld = LDAPUpdate(sub_dict={}, ldapi=True)
+        ld.update([paths.NIS_ULDIF])
+
+    def execute(self, **options):
+        ldap = self.api.Backend.ldap2
+        dn = DN(('cn', 'NIS Server'), ('cn', 'plugins'), ('cn', 'config'))
+        try:
+            ldap.get_entry(dn, attrs_list=['cn'])
+        except errors.NotFound:
+            # NIS is not configured on system, do not execute update
+            self.log.debug("Skipping NIS update, NIS Server is not configured")
+
+            # container does not exist, bug #5507 is not effective
+            sysupgrade.set_upgrade_state(
+                'nis', 'done_recover_from_missing_maps', True)
+        else:
+            self.__recover_from_missing_maps(ldap)
+
+            self.log.debug("Executing NIS Server update")
+            ld = LDAPUpdate(sub_dict={}, ldapi=True)
+            ld.update([paths.NIS_UPDATE_ULDIF])
+
+        return False, ()
--
2.4.3
SOURCES/0178-use-FFI-call-to-rpmvercmp-function-for-version-compa.patch
New file
@@ -0,0 +1,112 @@
From fcdfeb962a0643dca7f2e1a32f0a5e9f8ff5595d Mon Sep 17 00:00:00 2001
From: Martin Babinsky <mbabinsk@redhat.com>
Date: Mon, 11 Jan 2016 16:22:40 +0100
Subject: [PATCH] use FFI call to rpmvercmp function for version comparison
Stop using rpm-python to compare package versions since the implicit NSS
initialization upon  the module import breaks NSS handling in IPA code. Call
rpm-libs C-API function via CFFI instead.
Big thanks to Martin Kosek <mkosek@redhat.com> for sharing the code snippet
that spurred this patch.
https://fedorahosted.org/freeipa/ticket/5572
Reviewed-By: Jan Cholasta <jcholast@redhat.com>
---
 freeipa.spec.in             |  2 +-
 ipaplatform/redhat/tasks.py | 44 ++++++++++++--------------------------------
 2 files changed, 13 insertions(+), 33 deletions(-)
diff --git a/freeipa.spec.in b/freeipa.spec.in
index 01d42bc621c83541af7517d6d91eb37fd5b5c5cc..cd26d4ce66e320f8b8bf6aaa3e738b4c11f89aa9 100644
--- a/freeipa.spec.in
+++ b/freeipa.spec.in
@@ -159,7 +159,7 @@ Requires: p11-kit
 Requires: systemd-python
 Requires: %{etc_systemd_dir}
 Requires: gzip
-Requires: rpm-python
+Requires: rpm-libs
 Conflicts: %{alt_name}-server
 Obsoletes: %{alt_name}-server < %{version}
diff --git a/ipaplatform/redhat/tasks.py b/ipaplatform/redhat/tasks.py
index 2e894d776dcd5542e6c11cc0210add8ad9d90298..9be3ef664e6d15c31e53b7a8123de7b6bb2b81fe 100644
--- a/ipaplatform/redhat/tasks.py
+++ b/ipaplatform/redhat/tasks.py
@@ -30,7 +30,8 @@ import socket
 import sys
 import urllib
 import base64
-import rpm
+from cffi import FFI
+from ctypes.util import find_library
 from functools import total_ordering
 from subprocess import CalledProcessError
@@ -47,35 +48,14 @@ from ipaplatform.paths import paths
 from ipaplatform.redhat.authconfig import RedHatAuthConfig
 from ipaplatform.base.tasks import BaseTaskNamespace
+_ffi = FFI()
+_ffi.cdef("""
+int rpmvercmp (const char *a, const char *b);
+""")
-# copied from rpmUtils/miscutils.py
-def stringToVersion(verstring):
-    if verstring in [None, '']:
-        return (None, None, None)
-    i = verstring.find(':')
-    if i != -1:
-        try:
-            epoch = str(long(verstring[:i]))
-        except ValueError:
-            # look, garbage in the epoch field, how fun, kill it
-            epoch = '0' # this is our fallback, deal
-    else:
-        epoch = '0'
-    j = verstring.find('-')
-    if j != -1:
-        if verstring[i + 1:j] == '':
-            version = None
-        else:
-            version = verstring[i + 1:j]
-        release = verstring[j + 1:]
-    else:
-        if verstring[i + 1:] == '':
-            version = None
-        else:
-            version = verstring[i + 1:]
-        release = None
-    return (epoch, version, release)
-
+# use ctypes loader to get correct librpm.so library version according to
+# https://cffi.readthedocs.org/en/latest/overview.html#id8
+_librpm = _ffi.dlopen(find_library("rpm"))
 log = log_mgr.get_logger(__name__)
@@ -100,15 +80,15 @@ def selinux_enabled():
 class IPAVersion(object):
     def __init__(self, version):
-        self.version_tuple = stringToVersion(version)
+        self.version = version
     def __eq__(self, other):
         assert isinstance(other, IPAVersion)
-        return rpm.labelCompare(self.version_tuple, other.version_tuple) == 0
+        return _librpm.rpmvercmp(self.version, other.version) == 0
     def __lt__(self, other):
         assert isinstance(other, IPAVersion)
-        return rpm.labelCompare(self.version_tuple, other.version_tuple) == -1
+        return _librpm.rpmvercmp(self.version, other.version) < 0
 class RedHatTaskNamespace(BaseTaskNamespace):
--
2.4.3
SOURCES/0179-ipalib-assume-version-2.0-when-skip_version_check-is.patch
New file
@@ -0,0 +1,36 @@
From 88c057bc798b2606bafbed5c0b76475b0640b5e8 Mon Sep 17 00:00:00 2001
From: Jan Cholasta <jcholast@redhat.com>
Date: Tue, 12 Jan 2016 15:41:43 +0100
Subject: [PATCH] ipalib: assume version 2.0 when skip_version_check is enabled
https://fedorahosted.org/freeipa/ticket/5601
Reviewed-By: Martin Basti <mbasti@redhat.com>
---
 ipalib/frontend.py | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/ipalib/frontend.py b/ipalib/frontend.py
index 2ca3aaea82ea63702052eedbd7e4081f239cbaed..8adcee925a24fd546d7000e9239353da273e0bc9 100644
--- a/ipalib/frontend.py
+++ b/ipalib/frontend.py
@@ -26,7 +26,6 @@ from distutils import version
 from ipapython.version import API_VERSION
 from ipapython.ipa_log_manager import root_logger
-from ipalib.capabilities import VERSION_WITHOUT_CAPABILITIES
 from base import NameSpace
 from plugable import Plugin
 from parameters import create_param, Param, Str, Flag, Password
@@ -425,7 +424,7 @@ class Command(HasParam):
         if version_provided:
             self.verify_client_version(unicode(options['version']))
         elif self.api.env.skip_version_check and not self.api.env.in_server:
-            options['version'] = VERSION_WITHOUT_CAPABILITIES
+            options['version'] = u'2.0'
         else:
             options['version'] = API_VERSION
         params = self.args_options_2_params(*args, **options)
--
2.4.3
SOURCES/0180-always-start-certmonger-during-IPA-server-configurat.patch
New file
@@ -0,0 +1,87 @@
From 80115a38d78db0c2a31dea06786af41eafb234f0 Mon Sep 17 00:00:00 2001
From: Martin Babinsky <mbabinsk@redhat.com>
Date: Mon, 1 Feb 2016 12:59:04 +0100
Subject: [PATCH] always start certmonger during IPA server configuration
 upgrade
This patch fixes a regression introduced by commit
bef0f4c5c38e7ff6415e8f8c96dc306ef7f0ce56. Instead of checking whether
there is CA installed in the topology, we should always start certmonger
service during upgrade regardless when CA was configured.
https://fedorahosted.org/freeipa/ticket/5655
Reviewed-By: Jan Cholasta <jcholast@redhat.com>
Reviewed-By: Martin Basti <mbasti@redhat.com>
---
 ipaserver/install/server/upgrade.py | 33 +++++----------------------------
 1 file changed, 5 insertions(+), 28 deletions(-)
diff --git a/ipaserver/install/server/upgrade.py b/ipaserver/install/server/upgrade.py
index 616fba5c1a5b3737481aecbb09ab5344641a3b04..1f1cfeb672809c0298c69c121ac38d6c7a482d11 100644
--- a/ipaserver/install/server/upgrade.py
+++ b/ipaserver/install/server/upgrade.py
@@ -292,24 +292,6 @@ def setup_firefox_extension(fstore):
     http.setup_firefox_extension(realm, domain)
-def is_ca_enabled():
-    """
-    check whether there is an active CA master
-    :return: True if there is an active CA in topology, False otherwise
-    """
-    ldap2 = api.Backend.ldap2
-    was_connected = ldap2.isconnected()
-
-    if not was_connected:
-        ldap2.connect()
-
-    try:
-        return api.Command.ca_is_enabled()['result']
-    finally:
-        if not was_connected:
-            ldap2.disconnect()
-
-
 def ca_configure_profiles_acl(ca):
     root_logger.info('[Authorizing RA Agent to modify profiles]')
@@ -1420,6 +1402,10 @@ def upgrade_configuration():
             )
         upgrade_pki(ca, fstore)
+    certmonger_service = services.knownservices.certmonger
+    if ca.is_configured() and not certmonger_service.is_running():
+        certmonger_service.start()
+
     ca.configure_certmonger_renewal_guard()
     update_dbmodules(api.env.realm)
@@ -1435,8 +1421,7 @@ def upgrade_configuration():
     http.configure_selinux_for_httpd()
     http.change_mod_nss_port_from_http()
-    if is_ca_enabled():
-        http.configure_certmonger_renewal_guard()
+    http.configure_certmonger_renewal_guard()
     ds.configure_dirsrv_ccache()
@@ -1582,14 +1567,6 @@ def upgrade_check(options):
         print unicode(e)
         sys.exit(1)
-    try:
-        ca_is_enabled = is_ca_enabled()
-    except Exception as e:
-        raise RuntimeError("Cannot connect to LDAP server: {0}".format(e))
-
-    if not services.knownservices.certmonger.is_running() and ca_is_enabled:
-        raise RuntimeError('Certmonger is not running. Start certmonger and run upgrade again.')
-
     if not options.skip_version_check:
         # check IPA version and data version
         try:
--
2.5.0
SOURCES/ipa-centos-branding.patch
File was deleted
SPECS/ipa.spec
@@ -35,7 +35,7 @@
Name:           ipa
Version:        4.2.0
Release:        15%{?dist}.3
Release:        15%{?dist}.6
Summary:        The Identity, Policy and Audit system
Group:          System Environment/Base
@@ -43,10 +43,10 @@
URL:            http://www.freeipa.org/
Source0:        http://www.freeipa.org/downloads/src/freeipa-%{VERSION}.tar.gz
# RHEL spec file only: START: Change branding to IPA and Identity-Management
#Source1:        header-logo.png
#Source2:        login-screen-background.jpg
#Source3:        login-screen-logo.png
#Source4:        product-name.png
Source1:        header-logo.png
Source2:        login-screen-background.jpg
Source3:        login-screen-logo.png
Source4:        product-name.png
# RHEL spec file only: END: Change branding to IPA and Identity-Management
BuildRoot:      %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
@@ -210,6 +210,27 @@
Patch0157:      0157-disconnect-ldap2-backend-after-adding-default-CA-ACL.patch
Patch0158:      0158-do-not-disconnect-when-using-existing-connection-to-.patch
Patch0159:      0159-Fix-upgrade-of-forwardzones-when-zone-is-in-realmdom.patch
Patch0160:      0160-Fix-version-comparison.patch
Patch0161:      0161-DNS-fix-file-permissions.patch
Patch0162:      0162-Explicitly-call-chmod-on-newly-created-directories.patch
Patch0163:      0163-Fix-replace-mkdir-with-chmod.patch
Patch0164:      0164-DNSSEC-Improve-error-reporting-from-ipa-ods-exporter.patch
Patch0165:      0165-DNSSEC-Make-sure-that-current-state-in-OpenDNSSEC-ma.patch
Patch0166:      0166-DNSSEC-Make-sure-that-current-key-state-in-LDAP-matc.patch
Patch0167:      0167-DNSSEC-remove-obsolete-TODO-note.patch
Patch0168:      0168-DNSSEC-add-debug-mode-to-ldapkeydb.py.patch
Patch0169:      0169-DNSSEC-logging-improvements-in-ipa-ods-exporter.patch
Patch0170:      0170-DNSSEC-remove-keys-purged-by-OpenDNSSEC-from-master-.patch
Patch0171:      0171-DNSSEC-ipa-dnskeysyncd-Skip-zones-with-old-DNSSEC-me.patch
Patch0172:      0172-DNSSEC-ipa-ods-exporter-add-ldap-cleanup-command.patch
Patch0173:      0173-DNSSEC-ipa-dnskeysyncd-call-ods-signer-ldap-cleanup-.patch
Patch0174:      0174-DNSSEC-Log-debug-messages-at-log-level-DEBUG.patch
Patch0175:      0175-Allow-to-used-mixed-case-for-sysrestore.patch
Patch0176:      0176-prevent-crash-of-CA-less-server-upgrade-due-to-absen.patch
Patch0177:      0177-Upgrade-Fix-upgrade-of-NIS-Server-configuration.patch
Patch0178:      0178-use-FFI-call-to-rpmvercmp-function-for-version-compa.patch
Patch0179:      0179-ipalib-assume-version-2.0-when-skip_version_check-is.patch
Patch0180:      0180-always-start-certmonger-during-IPA-server-configurat.patch
Patch1001:      1001-Hide-pkinit-functionality-from-production-version.patch
Patch1002:      1002-Remove-pkinit-plugin.patch
@@ -221,7 +242,6 @@
Patch1008:      1008-RCUE.patch
Patch1009:      1009-Do-not-allow-installation-in-FIPS-mode.patch
Patch1010:      1010-WebUI-add-API-browser-is-experimental-warning.patch
Patch1011:      ipa-centos-branding.patch
# RHEL spec file only: END
%if ! %{ONLY_CLIENT}
@@ -346,14 +366,17 @@
Requires(pre): 389-ds-base >= 1.3.4.0
Requires: fontawesome-fonts
Requires: open-sans-fonts
Requires: openssl
# RHEL spec file only: START
Requires(pre): openssl >= 1:1.0.1e-42
# RHEL spec file only: END
Requires: openssl >= 1:1.0.1e-42
Requires: softhsm >= 2.0.0rc1-1
Requires: p11-kit
Requires: systemd-python
Requires: %{etc_systemd_dir}
Requires: gzip
# RHEL spec file only: START
# Requires: redhat-access-plugin-ipa
Requires: redhat-access-plugin-ipa
# RHEL spec file only: END
Conflicts: %{alt_name}-server
@@ -562,10 +585,10 @@
done
# Red Hat's Identity Management branding
#cp %SOURCE1 install/ui/images/header-logo.png
#cp %SOURCE2 install/ui/images/login-screen-background.jpg
#cp %SOURCE3 install/ui/images/login-screen-logo.png
#cp %SOURCE4 install/ui/images/product-name.png
cp %SOURCE1 install/ui/images/header-logo.png
cp %SOURCE2 install/ui/images/login-screen-background.jpg
cp %SOURCE3 install/ui/images/login-screen-logo.png
cp %SOURCE4 install/ui/images/product-name.png
# RHEL spec file only: END
%build
@@ -1162,8 +1185,50 @@
# RHEL spec file only: DELETED: Do not build tests
%changelog
* Tue Dec 08 2015 CentOS Sources <bugs@centos.org> - 4.2.0-15.el7.centos.3
- Roll in CentOS Branding
* Tue Feb  2 2016 Jan Cholasta <jcholast@redhat.com> - 4.2.0-15.6
- Resolves: #1298103 ipa-server-upgrade fails if certmonger is not running
  - always start certmonger during IPA server configuration upgrade
* Wed Jan 27 2016 Jan Cholasta <jcholast@redhat.com> - 4.2.0-15.5
- Resolves: #1298097 IPA server upgrade fails from RHEL 7.0 to RHEL 7.2 using
  "yum update ipa* sssd"
  - Set minimal required version for openssl
* Tue Jan 12 2016 Jan Cholasta <jcholast@redhat.com> - 4.2.0-15.4
- Resolves: #1298097 IPA server upgrade fails from RHEL 7.0 to RHEL 7.2 using
  "yum update ipa* sssd"
  - Set minimal required version for openssl
- Resolves: #1298098 ipa-nis-manage does not update ldap with all NIS maps
  - Upgrade: Fix upgrade of NIS Server configuration
- Resolves: #1298099 umask setting causes named-pkcs11 issue with directory
  permissions on /var/lib/ipa/dnssec
  - DNS: fix file permissions
  - Explicitly call chmod on newly created directories
  - Fix: replace mkdir with chmod
- Resolves: #1298100 Broken 7.2.0 to 7.2.z upgrade - flawed version comparison
  - Fix version comparison
  - use FFI call to rpmvercmp function for version comparison
- Resolves: #1298101 Sysrestore did not restore state if a key is specified in
  mixed case
  - Allow to used mixed case for sysrestore
- Resolves: #1298102 DNSSEC key purging is not handled properly
  - DNSSEC: Improve error reporting from ipa-ods-exporter
  - DNSSEC: Make sure that current state in OpenDNSSEC matches key state in
    LDAP
  - DNSSEC: Make sure that current key state in LDAP matches key state in BIND
  - DNSSEC: remove obsolete TODO note
  - DNSSEC: add debug mode to ldapkeydb.py
  - DNSSEC: logging improvements in ipa-ods-exporter
  - DNSSEC: remove keys purged by OpenDNSSEC from master HSM from LDAP
  - DNSSEC: ipa-dnskeysyncd: Skip zones with old DNSSEC metadata in LDAP
  - DNSSEC: ipa-ods-exporter: add ldap-cleanup command
  - DNSSEC: ipa-dnskeysyncd: call ods-signer ldap-cleanup on zone removal
  - DNSSEC: Log debug messages at log level DEBUG
- Resolves: #1298103 ipa-server-upgrade fails if certmonger is not running
  - prevent crash of CA-less server upgrade due to absent certmonger
- Resolves: #1298104 The ipa -e skip_version_check=1 still issues
  incompatibility error when called against RHEL 6 server
  - ipalib: assume version 2.0 when skip_version_check is enabled
* Wed Nov 25 2015 Jan Cholasta <jcholast@redhat.com> - 4.2.0-15.3
- Resolves: #1284052 IPA DNS Zone/DNS Forward Zone details missing after