diff --git a/.gitignore b/.gitignore
index bae0ba3..5a65ef2 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1,3 @@
-SOURCES/sssd-1.15.2.tar.gz
+SOURCES/cert9.db
+SOURCES/key4.db
+SOURCES/sssd-1.16.0.tar.gz
diff --git a/.sssd.metadata b/.sssd.metadata
index bc1073e..d8d858c 100644
--- a/.sssd.metadata
+++ b/.sssd.metadata
@@ -1 +1,3 @@
-f94298ac05169cdf5a9082c3aba9f6a18513720a SOURCES/sssd-1.15.2.tar.gz
+52bc755199e2c92ae81a8f93a7c6f2e46715b771 SOURCES/cert9.db
+52770a7b7564ef8e0508ae60f5dc238b78e70b99 SOURCES/key4.db
+fff74b3798e163a0fe311bdfc4588524afe6dd87 SOURCES/sssd-1.16.0.tar.gz
diff --git a/SOURCES/0001-MAN-Mention-sssd-secrets-in-SEE-ALSO-section.patch b/SOURCES/0001-MAN-Mention-sssd-secrets-in-SEE-ALSO-section.patch
deleted file mode 100644
index e405016..0000000
--- a/SOURCES/0001-MAN-Mention-sssd-secrets-in-SEE-ALSO-section.patch
+++ /dev/null
@@ -1,36 +0,0 @@
-From 1bf225166db64e81fe94cbee0fd3b1dc124717f4 Mon Sep 17 00:00:00 2001
-From: Lukas Slebodnik <lslebodn@redhat.com>
-Date: Tue, 21 Mar 2017 12:27:16 +0100
-Subject: [PATCH 01/15] MAN: Mention sssd-secrets in "SEE ALSO" section
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Resolves:
-https://pagure.io/SSSD/sssd/issue/3344
-
-Reviewed-by: Fabiano Fidêncio <fidencio@redhat.com>
----
- src/man/include/seealso.xml | 6 ++++++
- 1 file changed, 6 insertions(+)
-
-diff --git a/src/man/include/seealso.xml b/src/man/include/seealso.xml
-index 25b421748fb19881552de8b6384af2c794063e11..2e9c646c475887bce3612472975ade375edbd819 100644
---- a/src/man/include/seealso.xml
-+++ b/src/man/include/seealso.xml
-@@ -28,6 +28,12 @@
-                     <manvolnum>5</manvolnum>
-                 </citerefentry>,
-             </phrase>
-+            <phrase condition="with_secrets">
-+                <citerefentry>
-+                    <refentrytitle>sssd-secrets</refentrytitle>
-+                    <manvolnum>5</manvolnum>
-+                </citerefentry>,
-+            </phrase>
-             <citerefentry>
-                 <refentrytitle>sss_cache</refentrytitle><manvolnum>8</manvolnum>
-             </citerefentry>,
--- 
-2.9.3
-
diff --git a/SOURCES/0001-NSS-Move-memcache-setup-to-separate-function.patch b/SOURCES/0001-NSS-Move-memcache-setup-to-separate-function.patch
new file mode 100644
index 0000000..0d2e9f4
--- /dev/null
+++ b/SOURCES/0001-NSS-Move-memcache-setup-to-separate-function.patch
@@ -0,0 +1,143 @@
+From 82d52dbfd51330cd1fda4734b9e901431e137211 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Michal=20=C5=BDidek?= <mzidek@redhat.com>
+Date: Thu, 19 Oct 2017 16:39:27 +0200
+Subject: [PATCH 01/21] NSS: Move memcache setup to separate function
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Related:
+https://pagure.io/SSSD/sssd/issue/3496
+
+Reviewed-by: Sumit Bose <sbose@redhat.com>
+Reviewed-by: Pavel Březina <pbrezina@redhat.com>
+Reviewed-by: Fabiano Fidêncio <fidencio@redhat.com>
+Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
+(cherry picked from commit 878b0d42aca5839fdc1d97a68ce181e280f1ed7b)
+---
+ src/responder/nss/nsssrv.c | 91 ++++++++++++++++++++++++++--------------------
+ 1 file changed, 51 insertions(+), 40 deletions(-)
+
+diff --git a/src/responder/nss/nsssrv.c b/src/responder/nss/nsssrv.c
+index d67b9fac8d770d113560e41b259e2d5edd219343..21dd198226da6cf14d7db4941806048662970fed 100644
+--- a/src/responder/nss/nsssrv.c
++++ b/src/responder/nss/nsssrv.c
+@@ -252,6 +252,56 @@ static void nss_dp_reconnect_init(struct sbus_connection *conn,
+     /* nss_shutdown(rctx); */
+ }
+ 
++static int setup_memcaches(struct nss_ctx *nctx)
++{
++    int ret;
++    int memcache_timeout;
++
++    /* Remove the CLEAR_MC_FLAG file if exists. */
++    ret = unlink(SSS_NSS_MCACHE_DIR"/"CLEAR_MC_FLAG);
++    if (ret != 0 && errno != ENOENT) {
++        ret = errno;
++        DEBUG(SSSDBG_CRIT_FAILURE,
++              "Failed to unlink file [%s]. This can cause memory cache to "
++               "be purged when next log rotation is requested. %d: %s\n",
++               SSS_NSS_MCACHE_DIR"/"CLEAR_MC_FLAG, ret, strerror(ret));
++    }
++
++    ret = confdb_get_int(nctx->rctx->cdb,
++                         CONFDB_NSS_CONF_ENTRY,
++                         CONFDB_MEMCACHE_TIMEOUT,
++                         300, &memcache_timeout);
++    if (ret != EOK) {
++        DEBUG(SSSDBG_FATAL_FAILURE,
++              "Failed to get 'memcache_timeout' option from confdb.\n");
++        return ret;
++    }
++
++    /* TODO: read cache sizes from configuration */
++    ret = sss_mmap_cache_init(nctx, "passwd", SSS_MC_PASSWD,
++                              SSS_MC_CACHE_ELEMENTS, (time_t)memcache_timeout,
++                              &nctx->pwd_mc_ctx);
++    if (ret) {
++        DEBUG(SSSDBG_CRIT_FAILURE, "passwd mmap cache is DISABLED\n");
++    }
++
++    ret = sss_mmap_cache_init(nctx, "group", SSS_MC_GROUP,
++                              SSS_MC_CACHE_ELEMENTS, (time_t)memcache_timeout,
++                              &nctx->grp_mc_ctx);
++    if (ret) {
++        DEBUG(SSSDBG_CRIT_FAILURE, "group mmap cache is DISABLED\n");
++    }
++
++    ret = sss_mmap_cache_init(nctx, "initgroups", SSS_MC_INITGROUPS,
++                              SSS_MC_CACHE_ELEMENTS, (time_t)memcache_timeout,
++                              &nctx->initgr_mc_ctx);
++    if (ret) {
++        DEBUG(SSSDBG_CRIT_FAILURE, "initgroups mmap cache is DISABLED\n");
++    }
++
++    return EOK;
++}
++
+ int nss_process_init(TALLOC_CTX *mem_ctx,
+                      struct tevent_context *ev,
+                      struct confdb_ctx *cdb)
+@@ -260,7 +310,6 @@ int nss_process_init(TALLOC_CTX *mem_ctx,
+     struct sss_cmd_table *nss_cmds;
+     struct be_conn *iter;
+     struct nss_ctx *nctx;
+-    int memcache_timeout;
+     int ret, max_retries;
+     enum idmap_error_code err;
+     int fd_limit;
+@@ -330,49 +379,11 @@ int nss_process_init(TALLOC_CTX *mem_ctx,
+         goto fail;
+     }
+ 
+-    /* create mmap caches */
+-    /* Remove the CLEAR_MC_FLAG file if exists. */
+-    ret = unlink(SSS_NSS_MCACHE_DIR"/"CLEAR_MC_FLAG);
+-    if (ret != 0 && errno != ENOENT) {
+-        ret = errno;
+-        DEBUG(SSSDBG_CRIT_FAILURE,
+-              "Failed to unlink file [%s]. This can cause memory cache to "
+-               "be purged when next log rotation is requested. %d: %s\n",
+-               SSS_NSS_MCACHE_DIR"/"CLEAR_MC_FLAG, ret, strerror(ret));
+-    }
+-
+-    ret = confdb_get_int(nctx->rctx->cdb,
+-                         CONFDB_NSS_CONF_ENTRY,
+-                         CONFDB_MEMCACHE_TIMEOUT,
+-                         300, &memcache_timeout);
++    ret = setup_memcaches(nctx);
+     if (ret != EOK) {
+-        DEBUG(SSSDBG_FATAL_FAILURE,
+-              "Failed to get 'memcache_timeout' option from confdb.\n");
+         goto fail;
+     }
+ 
+-    /* TODO: read cache sizes from configuration */
+-    ret = sss_mmap_cache_init(nctx, "passwd", SSS_MC_PASSWD,
+-                              SSS_MC_CACHE_ELEMENTS, (time_t)memcache_timeout,
+-                              &nctx->pwd_mc_ctx);
+-    if (ret) {
+-        DEBUG(SSSDBG_CRIT_FAILURE, "passwd mmap cache is DISABLED\n");
+-    }
+-
+-    ret = sss_mmap_cache_init(nctx, "group", SSS_MC_GROUP,
+-                              SSS_MC_CACHE_ELEMENTS, (time_t)memcache_timeout,
+-                              &nctx->grp_mc_ctx);
+-    if (ret) {
+-        DEBUG(SSSDBG_CRIT_FAILURE, "group mmap cache is DISABLED\n");
+-    }
+-
+-    ret = sss_mmap_cache_init(nctx, "initgroups", SSS_MC_INITGROUPS,
+-                              SSS_MC_CACHE_ELEMENTS, (time_t)memcache_timeout,
+-                              &nctx->initgr_mc_ctx);
+-    if (ret) {
+-        DEBUG(SSSDBG_CRIT_FAILURE, "initgroups mmap cache is DISABLED\n");
+-    }
+-
+     /* Set up file descriptor limits */
+     ret = confdb_get_int(nctx->rctx->cdb,
+                          CONFDB_NSS_CONF_ENTRY,
+-- 
+2.13.5
+
diff --git a/SOURCES/0002-NSS-Specify-memcache_timeout-0-semantics.patch b/SOURCES/0002-NSS-Specify-memcache_timeout-0-semantics.patch
new file mode 100644
index 0000000..6fb4dca
--- /dev/null
+++ b/SOURCES/0002-NSS-Specify-memcache_timeout-0-semantics.patch
@@ -0,0 +1,118 @@
+From f23a358915cfa27669c019fe0df21cce8851459e Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Michal=20=C5=BDidek?= <mzidek@redhat.com>
+Date: Thu, 19 Oct 2017 16:42:19 +0200
+Subject: [PATCH 02/21] NSS: Specify memcache_timeout=0 semantics
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+With this patch the memcache files will not be created when
+memcache_timeout is set to zero.
+
+Resolves:
+https://pagure.io/SSSD/sssd/issue/3496
+
+Reviewed-by: Sumit Bose <sbose@redhat.com>
+Reviewed-by: Pavel Březina <pbrezina@redhat.com>
+Reviewed-by: Fabiano Fidêncio <fidencio@redhat.com>
+Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
+(cherry picked from commit ffe29e570a9e885c2f0061c34bb6be2bbd6ab9e4)
+---
+ src/responder/nss/nsssrv.c          |  6 ++++
+ src/tests/intg/test_memory_cache.py | 59 +++++++++++++++++++++++++++++++++++++
+ 2 files changed, 65 insertions(+)
+
+diff --git a/src/responder/nss/nsssrv.c b/src/responder/nss/nsssrv.c
+index 21dd198226da6cf14d7db4941806048662970fed..32bfcd69bbb9b35e9932b70a826c4f99ab6a07f3 100644
+--- a/src/responder/nss/nsssrv.c
++++ b/src/responder/nss/nsssrv.c
+@@ -277,6 +277,12 @@ static int setup_memcaches(struct nss_ctx *nctx)
+         return ret;
+     }
+ 
++    if (memcache_timeout == 0) {
++        DEBUG(SSSDBG_CONF_SETTINGS,
++              "Fast in-memory cache will not be initialized.");
++        return EOK;
++    }
++
+     /* TODO: read cache sizes from configuration */
+     ret = sss_mmap_cache_init(nctx, "passwd", SSS_MC_PASSWD,
+                               SSS_MC_CACHE_ELEMENTS, (time_t)memcache_timeout,
+diff --git a/src/tests/intg/test_memory_cache.py b/src/tests/intg/test_memory_cache.py
+index c7ba72490174a6ec2257f9d317ac96b35c674779..cac9feb00459957650c5e455db1b2712e17ccd68 100644
+--- a/src/tests/intg/test_memory_cache.py
++++ b/src/tests/intg/test_memory_cache.py
+@@ -207,6 +207,32 @@ def fqname_case_insensitive_rfc2307(request, ldap_conn):
+     return None
+ 
+ 
++@pytest.fixture
++def zero_timeout_rfc2307(request, ldap_conn):
++    load_data_to_ldap(request, ldap_conn)
++
++    conf = unindent("""\
++        [sssd]
++        domains             = LDAP
++        services            = nss
++
++        [nss]
++        memcache_timeout = 0
++
++        [domain/LDAP]
++        ldap_auth_disable_tls_never_use_in_production = true
++        ldap_schema         = rfc2307
++        id_provider         = ldap
++        auth_provider       = ldap
++        sudo_provider       = ldap
++        ldap_uri            = {ldap_conn.ds_inst.ldap_url}
++        ldap_search_base    = {ldap_conn.ds_inst.base_dn}
++    """).format(**locals())
++    create_conf_fixture(request, conf)
++    create_sssd_fixture(request)
++    return None
++
++
+ def test_getpwnam(ldap_conn, sanity_rfc2307):
+     ent.assert_passwd_by_name(
+         'user1',
+@@ -778,3 +804,36 @@ def test_removed_mc(ldap_conn, sanity_rfc2307):
+         grp.getgrnam('group1')
+     with pytest.raises(KeyError):
+         grp.getgrgid(2001)
++
++
++def test_mc_zero_timeout(ldap_conn, zero_timeout_rfc2307):
++    """
++    Test that the memory cache is not created at all with memcache_timeout=0
++    """
++    # No memory cache files must be created
++    assert len(os.listdir(config.MCACHE_PATH)) == 0
++
++    ent.assert_passwd_by_name(
++        'user1',
++        dict(name='user1', passwd='*', uid=1001, gid=2001,
++             gecos='1001', shell='/bin/bash'))
++    ent.assert_passwd_by_uid(
++        1001,
++        dict(name='user1', passwd='*', uid=1001, gid=2001,
++             gecos='1001', shell='/bin/bash'))
++
++    ent.assert_group_by_name("group1", dict(name="group1", gid=2001))
++    ent.assert_group_by_gid(2001, dict(name="group1", gid=2001))
++    stop_sssd()
++
++    # sssd is stopped; so the memory cache should not be used
++    # in long living clients (py.test in this case)
++    with pytest.raises(KeyError):
++        pwd.getpwnam('user1')
++    with pytest.raises(KeyError):
++        pwd.getpwuid(1001)
++
++    with pytest.raises(KeyError):
++        grp.getgrnam('group1')
++    with pytest.raises(KeyError):
++        grp.getgrgid(2001)
+-- 
+2.13.5
+
diff --git a/SOURCES/0002-split_on_separator-move-to-a-separate-file.patch b/SOURCES/0002-split_on_separator-move-to-a-separate-file.patch
deleted file mode 100644
index 24961c7..0000000
--- a/SOURCES/0002-split_on_separator-move-to-a-separate-file.patch
+++ /dev/null
@@ -1,369 +0,0 @@
-From 4f98b36562fb02f95c9af7af6fde548334ce9c34 Mon Sep 17 00:00:00 2001
-From: Sumit Bose <sbose@redhat.com>
-Date: Wed, 8 Feb 2017 14:28:28 +0100
-Subject: [PATCH 02/15] split_on_separator: move to a separate file
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-To be able to include split_on_separator() without additional
-dependencies (only talloc), it is moved into a separate file.
-
-Related to https://pagure.io/SSSD/sssd/issue/3050
-
-Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
-Reviewed-by: Lukáš Slebodník <lslebodn@redhat.com>
----
- Makefile.am         |  30 ++++++++++---
- src/util/util.c     |  93 ----------------------------------------
- src/util/util_ext.c | 121 ++++++++++++++++++++++++++++++++++++++++++++++++++++
- 3 files changed, 144 insertions(+), 100 deletions(-)
- create mode 100644 src/util/util_ext.c
-
-diff --git a/Makefile.am b/Makefile.am
-index 45b04de2638a745a189c0b4e5794ccd29913b10d..6dae4f2dd7f2dee501add82c7ab4f15fcbcc59ac 100644
---- a/Makefile.am
-+++ b/Makefile.am
-@@ -987,6 +987,7 @@ libsss_util_la_SOURCES = \
-     src/sbus/sssd_dbus_common_signals.c \
-     src/sbus/sssd_dbus_utils.c \
-     src/util/util.c \
-+    src/util/util_ext.c \
-     src/util/memory.c \
-     src/util/safe-format-string.c \
-     src/util/server.c \
-@@ -2355,19 +2356,23 @@ test_authtok_SOURCES = \
-     src/tests/cmocka/test_authtok.c \
-     src/util/authtok.c \
-     src/util/authtok-utils.c \
--    src/util/util.c
-+    src/util/util.c \
-+    src/util/util_ext.c \
-+    $(NULL)
- test_authtok_CFLAGS = \
-     $(AM_CFLAGS) \
-     $(TALLOC_CFLAGS) \
-     $(POPT_CFLAGS) \
--    $(DHASH_CFLAGS)
-+    $(DHASH_CFLAGS) \
-+    $(NULL)
- test_authtok_LDADD = \
-     $(TALLOC_LIBS) \
-     $(CMOCKA_LIBS) \
-     $(DHASH_LIBS) \
-     $(POPT_LIBS) \
-     libsss_test_common.la \
--    libsss_debug.la
-+    libsss_debug.la \
-+    $(NULL)
- 
- sss_nss_idmap_tests_SOURCES = \
-     src/tests/cmocka/sss_nss_idmap-tests.c
-@@ -2839,6 +2844,7 @@ test_child_common_SOURCES = \
-     src/util/atomic_io.c \
-     src/util/util_errors.c \
-     src/util/util.c \
-+    src/util/util_ext.c \
-     $(NULL)
- test_child_common_CFLAGS = \
-     $(AM_CFLAGS) \
-@@ -3774,6 +3780,7 @@ krb5_child_SOURCES = \
-     src/util/authtok.c \
-     src/util/authtok-utils.c \
-     src/util/util.c \
-+    src/util/util_ext.c \
-     src/util/signal.c \
-     src/util/strtonum.c \
-     src/util/become_user.c \
-@@ -3807,6 +3814,7 @@ ldap_child_SOURCES = \
-     src/util/authtok.c \
-     src/util/authtok-utils.c \
-     src/util/util.c \
-+    src/util/util_ext.c \
-     src/util/signal.c \
-     src/util/become_user.c \
-     $(NULL)
-@@ -3827,6 +3835,7 @@ selinux_child_SOURCES = \
-     src/util/sss_semanage.c \
-     src/util/atomic_io.c \
-     src/util/util.c \
-+    src/util/util_ext.c \
-     $(NULL)
- selinux_child_CFLAGS = \
-     $(AM_CFLAGS) \
-@@ -3845,6 +3854,7 @@ gpo_child_SOURCES = \
-     src/providers/ad/ad_gpo_child.c \
-     src/util/atomic_io.c \
-     src/util/util.c \
-+    src/util/util_ext.c \
-     src/util/signal.c
- gpo_child_CFLAGS = \
-     $(AM_CFLAGS) \
-@@ -3876,6 +3886,7 @@ p11_child_SOURCES = \
-     src/p11_child/p11_child_nss.c \
-     src/util/atomic_io.c \
-     src/util/util.c \
-+    src/util/util_ext.c \
-     $(NULL)
- p11_child_CFLAGS = \
-     $(AM_CFLAGS) \
-@@ -3893,16 +3904,21 @@ p11_child_LDADD = \
- 
- memberof_la_SOURCES = \
-     src/ldb_modules/memberof.c \
--    src/util/util.c
-+    src/util/util.c \
-+    src/util/util_ext.c \
-+    $(NULL)
- memberof_la_CFLAGS = \
--    $(AM_CFLAGS)
-+    $(AM_CFLAGS) \
-+    $(NULL)
- memberof_la_LIBADD = \
-     libsss_debug.la \
-     $(LDB_LIBS) \
--    $(DHASH_LIBS)
-+    $(DHASH_LIBS) \
-+    $(NULL)
- memberof_la_LDFLAGS = \
-     -avoid-version \
--    -module
-+    -module \
-+    $(NULL)
- 
- if BUILD_KRB5_LOCATOR_PLUGIN
- sssd_krb5_locator_plugin_la_SOURCES = \
-diff --git a/src/util/util.c b/src/util/util.c
-index a528f0c0249c33bfc3d3275250e74d5edcef2e6f..9d6202f695d516f20d648621da81a2d5e746daa5 100644
---- a/src/util/util.c
-+++ b/src/util/util.c
-@@ -35,99 +35,6 @@
- int socket_activated = 0;
- int dbus_activated = 0;
- 
--int split_on_separator(TALLOC_CTX *mem_ctx, const char *str,
--                       const char sep, bool trim, bool skip_empty,
--                       char ***_list, int *size)
--{
--    int ret;
--    const char *substr_end = str;
--    const char *substr_begin = str;
--    const char *sep_pos = NULL;
--    size_t substr_len;
--    char **list = NULL;
--    int num_strings = 0;
--    TALLOC_CTX *tmp_ctx = NULL;
--
--    if (str == NULL || *str == '\0' || _list == NULL) {
--        return EINVAL;
--    }
--
--    tmp_ctx = talloc_new(NULL);
--    if (tmp_ctx == NULL) {
--        return ENOMEM;
--    }
--
--    do {
--        substr_len = 0;
--
--        /* If this is not the first substring, then move from the separator. */
--        if (sep_pos != NULL) {
--            substr_end = sep_pos + 1;
--            substr_begin = sep_pos + 1;
--        }
--
--        /* Find end of the first substring */
--        while (*substr_end != sep && *substr_end != '\0') {
--            substr_end++;
--            substr_len++;
--        }
--
--        sep_pos = substr_end;
--
--        if (trim) {
--            /* Trim leading whitespace */
--            while (isspace(*substr_begin) && substr_begin < substr_end) {
--                substr_begin++;
--                substr_len--;
--            }
--
--            /* Trim trailing whitespace */
--            while (substr_end - 1 > substr_begin && isspace(*(substr_end-1))) {
--                substr_end--;
--                substr_len--;
--            }
--        }
--
--        /* Copy the substring to the output list of strings */
--        if (skip_empty == false || substr_len > 0) {
--            list = talloc_realloc(tmp_ctx, list, char*, num_strings + 2);
--            if (list == NULL) {
--                ret = ENOMEM;
--                goto done;
--            }
--
--            /* empty string is stored for substr_len == 0 */
--            list[num_strings] = talloc_strndup(list, substr_begin, substr_len);
--            if (list[num_strings] == NULL) {
--                ret = ENOMEM;
--                goto done;
--            }
--            num_strings++;
--        }
--
--    } while (*sep_pos != '\0');
--
--    if (list == NULL) {
--        /* No allocations were done, make space for the NULL */
--        list = talloc(tmp_ctx, char *);
--        if (list == NULL) {
--            ret = ENOMEM;
--            goto done;
--        }
--    }
--    list[num_strings] = NULL;
--
--    if (size) {
--        *size = num_strings;
--    }
--
--    *_list = talloc_steal(mem_ctx, list);
--    ret = EOK;
--done:
--    talloc_free(tmp_ctx);
--    return ret;
--}
--
- static void free_args(char **args)
- {
-     int i;
-diff --git a/src/util/util_ext.c b/src/util/util_ext.c
-new file mode 100644
-index 0000000000000000000000000000000000000000..fceb8c873a26471d476b39d5d4e567c445ed8d0b
---- /dev/null
-+++ b/src/util/util_ext.c
-@@ -0,0 +1,121 @@
-+/*
-+   SSSD helper calls - can be used by libraries for external use as well
-+
-+    Authors:
-+        Simo Sorce <ssorce@redhat.com>
-+
-+    Copyright (C) 2017 Red Hat
-+
-+    This program is free software; you can redistribute it and/or modify
-+    it under the terms of the GNU General Public License as published by
-+    the Free Software Foundation; either version 3 of the License, or
-+    (at your option) any later version.
-+
-+    This program is distributed in the hope that it will be useful,
-+    but WITHOUT ANY WARRANTY; without even the implied warranty of
-+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+    GNU General Public License for more details.
-+
-+    You should have received a copy of the GNU General Public License
-+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
-+*/
-+
-+#include <talloc.h>
-+#include <stdbool.h>
-+#include <errno.h>
-+#include <ctype.h>
-+
-+#define EOK 0
-+
-+int split_on_separator(TALLOC_CTX *mem_ctx, const char *str,
-+                       const char sep, bool trim, bool skip_empty,
-+                       char ***_list, int *size)
-+{
-+    int ret;
-+    const char *substr_end = str;
-+    const char *substr_begin = str;
-+    const char *sep_pos = NULL;
-+    size_t substr_len;
-+    char **list = NULL;
-+    int num_strings = 0;
-+    TALLOC_CTX *tmp_ctx = NULL;
-+
-+    if (str == NULL || *str == '\0' || _list == NULL) {
-+        return EINVAL;
-+    }
-+
-+    tmp_ctx = talloc_new(NULL);
-+    if (tmp_ctx == NULL) {
-+        return ENOMEM;
-+    }
-+
-+    do {
-+        substr_len = 0;
-+
-+        /* If this is not the first substring, then move from the separator. */
-+        if (sep_pos != NULL) {
-+            substr_end = sep_pos + 1;
-+            substr_begin = sep_pos + 1;
-+        }
-+
-+        /* Find end of the first substring */
-+        while (*substr_end != sep && *substr_end != '\0') {
-+            substr_end++;
-+            substr_len++;
-+        }
-+
-+        sep_pos = substr_end;
-+
-+        if (trim) {
-+            /* Trim leading whitespace */
-+            while (isspace(*substr_begin) && substr_begin < substr_end) {
-+                substr_begin++;
-+                substr_len--;
-+            }
-+
-+            /* Trim trailing whitespace */
-+            while (substr_end - 1 > substr_begin && isspace(*(substr_end-1))) {
-+                substr_end--;
-+                substr_len--;
-+            }
-+        }
-+
-+        /* Copy the substring to the output list of strings */
-+        if (skip_empty == false || substr_len > 0) {
-+            list = talloc_realloc(tmp_ctx, list, char*, num_strings + 2);
-+            if (list == NULL) {
-+                ret = ENOMEM;
-+                goto done;
-+            }
-+
-+            /* empty string is stored for substr_len == 0 */
-+            list[num_strings] = talloc_strndup(list, substr_begin, substr_len);
-+            if (list[num_strings] == NULL) {
-+                ret = ENOMEM;
-+                goto done;
-+            }
-+            num_strings++;
-+        }
-+
-+    } while (*sep_pos != '\0');
-+
-+    if (list == NULL) {
-+        /* No allocations were done, make space for the NULL */
-+        list = talloc(tmp_ctx, char *);
-+        if (list == NULL) {
-+            ret = ENOMEM;
-+            goto done;
-+        }
-+    }
-+    list[num_strings] = NULL;
-+
-+    if (size) {
-+        *size = num_strings;
-+    }
-+
-+    *_list = talloc_steal(mem_ctx, list);
-+    ret = EOK;
-+done:
-+    talloc_free(tmp_ctx);
-+    return ret;
-+}
--- 
-2.9.3
-
diff --git a/SOURCES/0003-MAN-Document-memcache_timeout-0-meaning.patch b/SOURCES/0003-MAN-Document-memcache_timeout-0-meaning.patch
new file mode 100644
index 0000000..a64b5a5
--- /dev/null
+++ b/SOURCES/0003-MAN-Document-memcache_timeout-0-meaning.patch
@@ -0,0 +1,51 @@
+From 22bd144b48e83d812dd823298b723983c4e3288a Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Michal=20=C5=BDidek?= <mzidek@redhat.com>
+Date: Thu, 19 Oct 2017 16:46:43 +0200
+Subject: [PATCH 03/21] MAN: Document memcache_timeout=0 meaning
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Document that by setting memcache_timeout to 0 the in-memoory cache
+will be disabled.
+
+Related:
+https://pagure.io/SSSD/sssd/issue/3496
+
+Reviewed-by: Sumit Bose <sbose@redhat.com>
+Reviewed-by: Pavel Březina <pbrezina@redhat.com>
+Reviewed-by: Fabiano Fidêncio <fidencio@redhat.com>
+Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
+(cherry picked from commit 1becbb7bec29a3d418d8f19fc52433cf86bcf395)
+---
+ src/man/sssd.conf.5.xml | 9 ++++++++-
+ 1 file changed, 8 insertions(+), 1 deletion(-)
+
+diff --git a/src/man/sssd.conf.5.xml b/src/man/sssd.conf.5.xml
+index 7752e450835b5beba50ddc4c635ff985d38ca421..7443f718319e292842c670aaf47cfc537545d021 100644
+--- a/src/man/sssd.conf.5.xml
++++ b/src/man/sssd.conf.5.xml
+@@ -974,12 +974,19 @@ fallback_homedir = /home/%u
+                     <listitem>
+                         <para>
+                             Specifies time in seconds for which records
+-                            in the in-memory cache will be valid.
++                            in the in-memory cache will be valid. Setting this
++                            option to zero will disable the in-memory cache.
+                         </para>
+                         <para>
+                             Default: 300
+                         </para>
+                         <para>
++                            WARNING: Disabling the in-memory cache will
++                            have significant negative impact on SSSD's
++                            performance and should only be used for
++                            testing.
++                        </para>
++                        <para>
+                             NOTE: If the environment variable
+                             SSS_NSS_USE_MEMCACHE is set to "NO", client
+                             applications will not use the fast in-memory
+-- 
+2.13.5
+
diff --git a/SOURCES/0003-util-move-string_in_list-to-util_ext.patch b/SOURCES/0003-util-move-string_in_list-to-util_ext.patch
deleted file mode 100644
index f21cb03..0000000
--- a/SOURCES/0003-util-move-string_in_list-to-util_ext.patch
+++ /dev/null
@@ -1,91 +0,0 @@
-From 7bf6cf5632fbdf83a37c52c40b7b982094b5c668 Mon Sep 17 00:00:00 2001
-From: Sumit Bose <sbose@redhat.com>
-Date: Mon, 20 Feb 2017 17:28:51 +0100
-Subject: [PATCH 03/15] util: move string_in_list to util_ext
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-To be able to include string_in_list() without additional
-dependencies it is moved into a separate file.
-
-Related to https://pagure.io/SSSD/sssd/issue/3050
-
-Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
-Reviewed-by: Lukáš Slebodník <lslebodn@redhat.com>
----
- src/util/util.c     | 20 --------------------
- src/util/util_ext.c | 22 ++++++++++++++++++++++
- 2 files changed, 22 insertions(+), 20 deletions(-)
-
-diff --git a/src/util/util.c b/src/util/util.c
-index 9d6202f695d516f20d648621da81a2d5e746daa5..f0e8f9dd6a4bceed6befb74c57aa066b19a72bb7 100644
---- a/src/util/util.c
-+++ b/src/util/util.c
-@@ -617,26 +617,6 @@ errno_t add_string_to_list(TALLOC_CTX *mem_ctx, const char *string,
-     return EOK;
- }
- 
--bool string_in_list(const char *string, char **list, bool case_sensitive)
--{
--    size_t c;
--    int(*compare)(const char *s1, const char *s2);
--
--    if (string == NULL || list == NULL || *list == NULL) {
--        return false;
--    }
--
--    compare = case_sensitive ? strcmp : strcasecmp;
--
--    for (c = 0; list[c] != NULL; c++) {
--        if (compare(string, list[c]) == 0) {
--            return true;
--        }
--    }
--
--    return false;
--}
--
- void safezero(void *data, size_t size)
- {
-     volatile uint8_t *p = data;
-diff --git a/src/util/util_ext.c b/src/util/util_ext.c
-index fceb8c873a26471d476b39d5d4e567c445ed8d0b..04dc02a8adf32bd0590fe6eba230658e67d0a362 100644
---- a/src/util/util_ext.c
-+++ b/src/util/util_ext.c
-@@ -24,6 +24,8 @@
- #include <stdbool.h>
- #include <errno.h>
- #include <ctype.h>
-+#include <string.h>
-+#include <strings.h>
- 
- #define EOK 0
- 
-@@ -119,3 +121,23 @@ done:
-     talloc_free(tmp_ctx);
-     return ret;
- }
-+
-+bool string_in_list(const char *string, char **list, bool case_sensitive)
-+{
-+    size_t c;
-+    int(*compare)(const char *s1, const char *s2);
-+
-+    if (string == NULL || list == NULL || *list == NULL) {
-+        return false;
-+    }
-+
-+    compare = case_sensitive ? strcmp : strcasecmp;
-+
-+    for (c = 0; list[c] != NULL; c++) {
-+        if (compare(string, list[c]) == 0) {
-+            return true;
-+        }
-+    }
-+
-+    return false;
-+}
--- 
-2.9.3
-
diff --git a/SOURCES/0004-CONFIG-Add-a-new-option-auto_private_groups.patch b/SOURCES/0004-CONFIG-Add-a-new-option-auto_private_groups.patch
new file mode 100644
index 0000000..9f2a67d
--- /dev/null
+++ b/SOURCES/0004-CONFIG-Add-a-new-option-auto_private_groups.patch
@@ -0,0 +1,159 @@
+From 1deab05ac0820d9be261b55027a90078a758febd Mon Sep 17 00:00:00 2001
+From: Jakub Hrozek <jhrozek@redhat.com>
+Date: Tue, 3 Oct 2017 12:34:33 +0200
+Subject: [PATCH 04/21] CONFIG: Add a new option auto_private_groups
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+The auto_private_groups option is used to configure the domain->mpg flag
+which was already set automatically for subdomains, but for some time was
+not settable by the admin via the configuration file.
+
+The new option name, instead of the old magic_private_groups, was chosen
+purely because this name would hopefully be better understood by admins.
+
+The option doesn't do anything yet, it is just added to all the places a
+new option should be added to.
+
+Related:
+    https://pagure.io/SSSD/sssd/issue/1872
+
+Reviewed-by: Fabiano Fidêncio <fidencio@redhat.com>
+Reviewed-by: Pavel Březina <pbrezina@redhat.com>
+(cherry picked from commit d72ac2c58360cd272277b5ddde67bbff53106a74)
+---
+ src/confdb/confdb.c                  |  8 ++++++++
+ src/confdb/confdb.h                  |  1 +
+ src/config/SSSDConfig/__init__.py.in |  1 +
+ src/config/SSSDConfigTest.py         |  6 ++++--
+ src/config/cfg_rules.ini             |  1 +
+ src/config/etc/sssd.api.conf         |  1 +
+ src/man/sssd.conf.5.xml              | 20 ++++++++++++++++++++
+ 7 files changed, 36 insertions(+), 2 deletions(-)
+
+diff --git a/src/confdb/confdb.c b/src/confdb/confdb.c
+index fefecc03d554f6eca12efe07990bfae17033bd02..a028224817f12ace2a0c4165d7b9cb0bb80ce5a1 100644
+--- a/src/confdb/confdb.c
++++ b/src/confdb/confdb.c
+@@ -936,6 +936,14 @@ static int confdb_get_domain_internal(struct confdb_ctx *cdb,
+         goto done;
+     }
+ 
++    ret = get_entry_as_bool(res->msgs[0], &domain->mpg,
++                            CONFDB_DOMAIN_AUTO_UPG, 0);
++    if (ret != EOK) {
++        DEBUG(SSSDBG_FATAL_FAILURE,
++              "Invalid value for %s\n", CONFDB_DOMAIN_AUTO_UPG);
++        goto done;
++    }
++
+     if (strcasecmp(domain->provider, "local") == 0) {
+         /* If this is the local provider, we need to ensure that
+          * no other provider was specified for other types, since
+diff --git a/src/confdb/confdb.h b/src/confdb/confdb.h
+index bcea99ae49a3fa5f0393ce6b2c215b5b2d4bc3fc..2539b906993edbceb38aac9265e04deed69cf2e4 100644
+--- a/src/confdb/confdb.h
++++ b/src/confdb/confdb.h
+@@ -198,6 +198,7 @@
+ #define CONFDB_DEFAULT_CACHE_CREDS_MIN_FF_LENGTH 8
+ #define CONFDB_DOMAIN_LEGACY_PASS "store_legacy_passwords"
+ #define CONFDB_DOMAIN_MPG "magic_private_groups"
++#define CONFDB_DOMAIN_AUTO_UPG "auto_private_groups"
+ #define CONFDB_DOMAIN_FQ "use_fully_qualified_names"
+ #define CONFDB_DOMAIN_ENTRY_CACHE_TIMEOUT "entry_cache_timeout"
+ #define CONFDB_DOMAIN_ACCOUNT_CACHE_EXPIRATION "account_cache_expiration"
+diff --git a/src/config/SSSDConfig/__init__.py.in b/src/config/SSSDConfig/__init__.py.in
+index d99b718e09283d113f73639e0f94e7f1cec55f68..d2bb709d69c8790558b5c06a7e405463b508c189 100644
+--- a/src/config/SSSDConfig/__init__.py.in
++++ b/src/config/SSSDConfig/__init__.py.in
+@@ -195,6 +195,7 @@ option_strings = {
+     'cached_auth_timeout' : _('How long can cached credentials be used for cached authentication'),
+     'full_name_format' : _('Printf-compatible format for displaying fully-qualified names'),
+     're_expression' : _('Regex to parse username and domain'),
++    'auto_private_groups' : _('Whether to automatically create private groups for users'),
+ 
+     # [provider/ipa]
+     'ipa_domain' : _('IPA domain'),
+diff --git a/src/config/SSSDConfigTest.py b/src/config/SSSDConfigTest.py
+index 4a583bdd3124dc05a116d2e6bd48afb92aa0b54d..87d1f6e6410dfeafc77d578cf0b950dc71a1f0a2 100755
+--- a/src/config/SSSDConfigTest.py
++++ b/src/config/SSSDConfigTest.py
+@@ -624,7 +624,8 @@ class SSSDConfigTestSSSDDomain(unittest.TestCase):
+             'subdomain_homedir',
+             'full_name_format',
+             're_expression',
+-            'cached_auth_timeout']
++            'cached_auth_timeout',
++            'auto_private_groups']
+ 
+         self.assertTrue(type(options) == dict,
+                         "Options should be a dictionary")
+@@ -994,7 +995,8 @@ class SSSDConfigTestSSSDDomain(unittest.TestCase):
+             'subdomain_homedir',
+             'full_name_format',
+             're_expression',
+-            'cached_auth_timeout']
++            'cached_auth_timeout',
++            'auto_private_groups']
+ 
+         self.assertTrue(type(options) == dict,
+                         "Options should be a dictionary")
+diff --git a/src/config/cfg_rules.ini b/src/config/cfg_rules.ini
+index e49e8d43f4aead14d833866110784fd62382cc2b..4e70bf7b6f0fa7421a0c35bd4279830265bf3470 100644
+--- a/src/config/cfg_rules.ini
++++ b/src/config/cfg_rules.ini
+@@ -382,6 +382,7 @@ option = cached_auth_timeout
+ option = wildcard_limit
+ option = full_name_format
+ option = re_expression
++option = auto_private_groups
+ 
+ #Entry cache timeouts
+ option = entry_cache_user_timeout
+diff --git a/src/config/etc/sssd.api.conf b/src/config/etc/sssd.api.conf
+index 7f2b8977b7e67fcfc20df49056cda8ebe6da0be8..2be2e3e685ba3abd9a4a419f93332a89ff774262 100644
+--- a/src/config/etc/sssd.api.conf
++++ b/src/config/etc/sssd.api.conf
+@@ -185,6 +185,7 @@ subdomain_homedir = str, None, false
+ cached_auth_timeout = int, None, false
+ full_name_format = str, None, false
+ re_expression = str, None, false
++auto_private_groups = str, None, false
+ 
+ #Entry cache timeouts
+ entry_cache_user_timeout = int, None, false
+diff --git a/src/man/sssd.conf.5.xml b/src/man/sssd.conf.5.xml
+index 7443f718319e292842c670aaf47cfc537545d021..47da07c33bdcfbf2fa94ff932492e9ea4bbfe846 100644
+--- a/src/man/sssd.conf.5.xml
++++ b/src/man/sssd.conf.5.xml
+@@ -2823,6 +2823,26 @@ subdomain_inherit = ldap_purge_cache_timeout
+                         </para>
+                     </listitem>
+                 </varlistentry>
++                <varlistentry>
++                    <term>auto_private_groups (string)</term>
++                    <listitem>
++                        <para>
++                            If this option is enabled, SSSD will automatically
++                            create user private groups based on user's
++                            UID number. The GID number is ignored in this case.
++                        </para>
++                        <para>
++                            NOTE: Because the GID number and the user private group
++                            are inferred frm the UID number, it is not supported
++                            to have multiple entries with the same UID or GID number
++                            with this option. In other words, enabling this option
++                            enforces uniqueness across the ID space.
++                        </para>
++                        <para>
++                            Default: False
++                        </para>
++                    </listitem>
++                </varlistentry>
+             </variablelist>
+         </para>
+ 
+-- 
+2.13.5
+
diff --git a/SOURCES/0004-certmap-add-new-library-libsss_certmap.patch b/SOURCES/0004-certmap-add-new-library-libsss_certmap.patch
deleted file mode 100644
index 1a04fe7..0000000
--- a/SOURCES/0004-certmap-add-new-library-libsss_certmap.patch
+++ /dev/null
@@ -1,5787 +0,0 @@
-From 64d74f65ca66fafd67c99ac17c5e45e584f59d83 Mon Sep 17 00:00:00 2001
-From: Sumit Bose <sbose@redhat.com>
-Date: Thu, 2 Feb 2017 11:24:02 +0100
-Subject: [PATCH 04/15] certmap: add new library libsss_certmap
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-With this library it would be possible to map certificates and users not
-only by adding the full certificate to the user's LDAP object but by
-adding e.g. only parts like the issuer and subject name. Additionally
-the library is also able to flexible select/match certificates based on
-values in the certificate.
-
-Details about mapping and matching rules can be found in the included
-man page.
-
-Related to https://pagure.io/SSSD/sssd/issue/3050
-
-Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
-Reviewed-by: Lukáš Slebodník <lslebodn@redhat.com>
----
- Makefile.am                                |   55 ++
- configure.ac                               |    1 +
- contrib/sssd.spec.in                       |   32 +
- src/lib/certmap/sss_cert_content_nss.c     | 1014 +++++++++++++++++++
- src/lib/certmap/sss_certmap.c              |  993 +++++++++++++++++++
- src/lib/certmap/sss_certmap.doxy.in        |    3 +
- src/lib/certmap/sss_certmap.exports        |   13 +
- src/lib/certmap/sss_certmap.h              |  155 +++
- src/lib/certmap/sss_certmap.pc.in          |   11 +
- src/lib/certmap/sss_certmap_attr_names.c   |  107 +++
- src/lib/certmap/sss_certmap_int.h          |  187 ++++
- src/lib/certmap/sss_certmap_krb5_match.c   |  558 +++++++++++
- src/lib/certmap/sss_certmap_ldap_mapping.c |  367 +++++++
- src/man/Makefile.am                        |    2 +-
- src/man/po/po4a.cfg                        |    1 +
- src/man/sss-certmap.5.xml                  |  600 ++++++++++++
- src/tests/cmocka/test_certmap.c            | 1443 ++++++++++++++++++++++++++++
- src/tests/dlopen-tests.c                   |    1 +
- 18 files changed, 5542 insertions(+), 1 deletion(-)
- create mode 100644 src/lib/certmap/sss_cert_content_nss.c
- create mode 100644 src/lib/certmap/sss_certmap.c
- create mode 100644 src/lib/certmap/sss_certmap.doxy.in
- create mode 100644 src/lib/certmap/sss_certmap.exports
- create mode 100644 src/lib/certmap/sss_certmap.h
- create mode 100644 src/lib/certmap/sss_certmap.pc.in
- create mode 100644 src/lib/certmap/sss_certmap_attr_names.c
- create mode 100644 src/lib/certmap/sss_certmap_int.h
- create mode 100644 src/lib/certmap/sss_certmap_krb5_match.c
- create mode 100644 src/lib/certmap/sss_certmap_ldap_mapping.c
- create mode 100644 src/man/sss-certmap.5.xml
- create mode 100644 src/tests/cmocka/test_certmap.c
-
-diff --git a/Makefile.am b/Makefile.am
-index 6dae4f2dd7f2dee501add82c7ab4f15fcbcc59ac..8ca12c10d2713b6a72361d84b25486500c79f407 100644
---- a/Makefile.am
-+++ b/Makefile.am
-@@ -278,6 +278,7 @@ if HAVE_CMOCKA
-         simple-access-tests \
-         krb5_common_test \
-         test_iobuf \
-+        sss_certmap_test \
-         $(NULL)
- 
- if HAVE_LIBRESOLV
-@@ -1074,6 +1075,7 @@ SSSD_INTERNAL_LTLIBS = \
- lib_LTLIBRARIES = libipa_hbac.la \
-                   libsss_idmap.la \
-                   libsss_nss_idmap.la \
-+                  libsss_certmap.la \
-                   $(NULL)
- 
- pkgconfig_DATA += src/lib/ipa_hbac/ipa_hbac.pc
-@@ -1128,6 +1130,7 @@ include_HEADERS = \
-     src/lib/ipa_hbac/ipa_hbac.h \
-     src/lib/idmap/sss_idmap.h \
-     src/sss_client/idmap/sss_nss_idmap.h \
-+    src/lib/certmap/sss_certmap.h \
-     $(NULL)
- 
- if BUILD_LIBWBCLIENT
-@@ -1712,6 +1715,38 @@ sssd_check_socket_activated_responders_LDADD = \
-     $(NULL)
- endif
- 
-+if HAVE_NSS
-+pkgconfig_DATA += src/lib/certmap/sss_certmap.pc
-+libsss_certmap_la_DEPENDENCIES = src/lib/certmap/sss_certmap.exports
-+libsss_certmap_la_SOURCES = \
-+    src/lib/certmap/sss_certmap.c \
-+    src/lib/certmap/sss_certmap_attr_names.c \
-+    src/lib/certmap/sss_cert_content_nss.c \
-+    src/lib/certmap/sss_certmap_krb5_match.c \
-+    src/lib/certmap/sss_certmap_ldap_mapping.c \
-+    src/util/util_ext.c \
-+    src/util/cert/cert_common.c \
-+    src/util/crypto/nss/nss_base64.c \
-+    src/util/cert/nss/cert.c \
-+    src/util/crypto/nss/nss_util.c \
-+    $(NULL)
-+libsss_certmap_la_CFLAGS = \
-+    $(AM_CFLAGS) \
-+    $(TALLOC_CFLAGS) \
-+    $(NSS_CFLAGS) \
-+    $(NULL)
-+libsss_certmap_la_LIBADD = \
-+    $(TALLOC_LIBS) \
-+    $(NSS_LIBS) \
-+    $(NULL)
-+libsss_certmap_la_LDFLAGS = \
-+    -Wl,--version-script,$(srcdir)/src/lib/certmap/sss_certmap.exports \
-+    -version-info 0:0:0
-+
-+dist_noinst_DATA += src/lib/certmap/sss_certmap.exports
-+dist_noinst_HEADERS += src/lib/certmap/sss_certmap_int.h
-+endif
-+
- #################
- # Feature Tests #
- #################
-@@ -3245,6 +3280,25 @@ test_inotify_LDADD = \
-     libsss_test_common.la \
-     $(NULL)
- 
-+if HAVE_NSS
-+sss_certmap_test_SOURCES = \
-+    src/tests/cmocka/test_certmap.c \
-+    src/lib/certmap/sss_certmap_attr_names.c \
-+    $(NULL)
-+sss_certmap_test_CFLAGS = \
-+    $(AM_CFLAGS) \
-+    $(NSS_CFLAGS) \
-+    $(NULL)
-+sss_certmap_test_LDADD = \
-+    $(CMOCKA_LIBS) \
-+    $(POPT_LIBS) \
-+    $(TALLOC_LIBS) \
-+    $(NSS_LIBS) \
-+    $(SSSD_INTERNAL_LTLIBS) \
-+    libsss_test_common.la \
-+    libsss_certmap.la \
-+    $(NULL)
-+endif
- endif # HAVE_CMOCKA
- 
- noinst_PROGRAMS = pam_test_client
-@@ -4404,6 +4458,7 @@ docs:
- 	$(DOXYGEN) src/lib/ipa_hbac/ipa_hbac.doxy
- 	$(DOXYGEN) src/lib/idmap/sss_idmap.doxy
- 	$(DOXYGEN) src/sss_client/idmap/sss_nss_idmap.doxy
-+	$(DOXYGEN) src/lib/certmap/sss_certmap.doxy
- if BUILD_IFP
- 	$(DOXYGEN) src/lib/sifp/sss_simpleifp.doxy
- endif
-diff --git a/configure.ac b/configure.ac
-index e6a3b4e857bcbec16873f54008e6b42aeb9b7cd7..dd1012015a5fea9f25e5b5199b4868fbc0bc14c4 100644
---- a/configure.ac
-+++ b/configure.ac
-@@ -483,6 +483,7 @@ AC_CONFIG_FILES([Makefile contrib/sssd.spec src/examples/rwtab src/doxy.config
-                  src/tests/intg/Makefile
-                  src/lib/ipa_hbac/ipa_hbac.pc src/lib/ipa_hbac/ipa_hbac.doxy
-                  src/lib/idmap/sss_idmap.pc src/lib/idmap/sss_idmap.doxy
-+                 src/lib/certmap/sss_certmap.pc src/lib/certmap/sss_certmap.doxy
-                  src/sss_client/idmap/sss_nss_idmap.pc
-                  src/sss_client/idmap/sss_nss_idmap.doxy
-                  src/sss_client/libwbclient/wbclient_sssd.pc
-diff --git a/contrib/sssd.spec.in b/contrib/sssd.spec.in
-index 5bd2beb89014ba7c86b4231f0357a7a6e10a18a8..28ebe07a26a3112210b092b7831e7f6aae061c8d 100644
---- a/contrib/sssd.spec.in
-+++ b/contrib/sssd.spec.in
-@@ -658,6 +658,25 @@ The libnfsidmap sssd module provides a way for rpc.idmapd to call SSSD to map
- UIDs/GIDs to names and vice versa. It can be also used for mapping principal
- (user) name to IDs(UID or GID) or to obtain groups which user are member of.
- 
-+%package -n libsss_certmap
-+Summary: SSSD Certficate Mapping Library
-+Group: Development/Libraries
-+License: LGPLv3+
-+Requires(post): /sbin/ldconfig
-+Requires(postun): /sbin/ldconfig
-+
-+%description -n libsss_certmap
-+Library to map certificates to users based on rules
-+
-+%package -n libsss_certmap-devel
-+Summary: SSSD Certficate Mapping Library
-+Group: Development/Libraries
-+License: LGPLv3+
-+Requires: libsss_certmap = %{version}-%{release}
-+
-+%description -n libsss_certmap-devel
-+Library to map certificates to users based on rules
-+
- %prep
- %setup -q -n %{name}-%{version}
- 
-@@ -888,6 +907,7 @@ done
- %{_datadir}/sssd/sssd.api.d
- %{_mandir}/man1/sss_ssh_authorizedkeys.1*
- %{_mandir}/man1/sss_ssh_knownhostsproxy.1*
-+%{_mandir}/man5/sss-certmap.5*
- %{_mandir}/man5/sssd.conf.5*
- %{_mandir}/man5/sssd-simple.5*
- %{_mandir}/man5/sssd-sudo.5*
-@@ -1146,6 +1166,18 @@ done
- %files nfs-idmap
- %{_libdir}/libnfsidmap/sss.so
- 
-+%files -n libsss_certmap
-+%defattr(-,root,root,-)
-+%doc src/sss_client/COPYING src/sss_client/COPYING.LESSER
-+%{_libdir}/libsss_certmap.so.*
-+
-+%files -n libsss_certmap-devel
-+%defattr(-,root,root,-)
-+%doc certmap_doc/html
-+%{_includedir}/sss_certmap.h
-+%{_libdir}/libsss_certmap.so
-+%{_libdir}/pkgconfig/sss_certmap.pc
-+
- %pre common
- getent group sssd >/dev/null || groupadd -r sssd
- getent passwd sssd >/dev/null || useradd -r -g sssd -d / -s /sbin/nologin -c "User for sssd" sssd
-diff --git a/src/lib/certmap/sss_cert_content_nss.c b/src/lib/certmap/sss_cert_content_nss.c
-new file mode 100644
-index 0000000000000000000000000000000000000000..d3182895465706c87503b8abb0cea49b7677625d
---- /dev/null
-+++ b/src/lib/certmap/sss_cert_content_nss.c
-@@ -0,0 +1,1014 @@
-+/*
-+   SSSD - certificate handling utils - NSS version
-+   The calls defined here should be useable outside of SSSD as well, e.g. in
-+   libsss_certmap.
-+
-+   Copyright (C) Sumit Bose <sbose@redhat.com> 2017
-+
-+   This program is free software; you can redistribute it and/or modify
-+   it under the terms of the GNU General Public License as published by
-+   the Free Software Foundation; either version 3 of the License, or
-+   (at your option) any later version.
-+
-+   This program is distributed in the hope that it will be useful,
-+   but WITHOUT ANY WARRANTY; without even the implied warranty of
-+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+   GNU General Public License for more details.
-+
-+   You should have received a copy of the GNU General Public License
-+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
-+*/
-+
-+#include "config.h"
-+
-+#include <nss.h>
-+#include <cert.h>
-+#include <base64.h>
-+#include <prerror.h>
-+#include <secport.h>
-+#include <secerr.h>
-+#include <prprf.h>
-+#include <prnetdb.h>
-+#include <talloc.h>
-+
-+#include "util/crypto/sss_crypto.h"
-+#include "util/crypto/nss/nss_util.h"
-+#include "util/cert.h"
-+#include "lib/certmap/sss_certmap.h"
-+#include "lib/certmap/sss_certmap_int.h"
-+
-+
-+/* The following two functions are copied from NSS's lib/certdb/secname.c
-+ * becasue CERT_AddAVA is not exported. I just renamed it and made it static
-+ * to avoid issues if the call gets exported some time in future. */
-+
-+static void **
-+AddToArray(PLArenaPool *arena, void **array, void *element)
-+{
-+    unsigned count;
-+    void **ap;
-+
-+    /* Count up number of slots already in use in the array */
-+    count = 0;
-+    ap = array;
-+    if (ap) {
-+        while (*ap++) {
-+            count++;
-+        }
-+    }
-+
-+    if (array) {
-+        array = (void**) PORT_ArenaGrow(arena, array,
-+                                        (count + 1) * sizeof(void *),
-+                                        (count + 2) * sizeof(void *));
-+    } else {
-+        array = (void**) PORT_ArenaAlloc(arena, (count + 2) * sizeof(void *));
-+    }
-+    if (array) {
-+        array[count] = element;
-+        array[count+1] = 0;
-+    }
-+    return array;
-+}
-+
-+
-+static SECStatus
-+sss_CERT_AddAVA(PLArenaPool *arena, CERTRDN *rdn, CERTAVA *ava)
-+{
-+    rdn->avas = (CERTAVA**) AddToArray(arena, (void**) rdn->avas, ava);
-+    return rdn->avas ? SECSuccess : SECFailure;
-+}
-+
-+static SECItem *
-+cert_get_ext_by_tag(CERTCertificate *cert, SECOidTag tag)
-+{
-+    SECOidData *oid;
-+    int i;
-+
-+    oid = SECOID_FindOIDByTag(tag);
-+    for (i = 0;
-+         (cert->extensions != NULL) && (cert->extensions[i] != NULL);
-+         i++)
-+        if (SECITEM_ItemsAreEqual(&cert->extensions[i]->id, &oid->oid))
-+            return &cert->extensions[i]->value;
-+    return NULL;
-+}
-+
-+static int get_extended_key_usage_oids(TALLOC_CTX *mem_ctx,
-+                                       CERTCertificate *cert,
-+                                       const char ***_oids)
-+{
-+    PLArenaPool *pool;
-+    SECItem *ext;
-+    SECItem **oids = NULL;
-+    const char **oids_list = NULL;
-+    size_t c;
-+    SECStatus rv;
-+    char *tmp_str;
-+    int ret;
-+
-+    pool = PORT_NewArena(sizeof(double));
-+    ext = cert_get_ext_by_tag(cert, SEC_OID_X509_EXT_KEY_USAGE);
-+    if (ext != NULL) {
-+        rv = SEC_ASN1DecodeItem(pool, &oids,
-+                                SEC_ASN1_GET(SEC_SequenceOfObjectIDTemplate),
-+                                ext);
-+        if (rv != SECSuccess) {
-+            ret = EINVAL;
-+            goto done;
-+        }
-+    }
-+
-+    for (c = 0; (oids != NULL && oids[c] != NULL); c++);
-+    oids_list = talloc_zero_array(mem_ctx, const char *, c + 1);
-+    if (oids_list == NULL) {
-+        return ENOMEM;
-+    }
-+
-+    for (c = 0; (oids != NULL && oids[c] != NULL); c++) {
-+        tmp_str = CERT_GetOidString(oids[c]);
-+        /* is it expexted that NSS OID strings start with "OID." but we
-+         * prefer the plain dotted-decimal version so the prefix is skipped */
-+        if (tmp_str == NULL || strncmp(tmp_str, "OID.", 4) != 0) {
-+            PR_smprintf_free(tmp_str);
-+            ret = EINVAL;
-+            goto done;
-+        }
-+
-+        oids_list[c] = talloc_strdup(oids_list, tmp_str + 4);
-+        PR_smprintf_free(tmp_str);
-+        if(oids_list[c] == NULL) {
-+            ret = ENOMEM;
-+            goto done;
-+        }
-+    }
-+
-+    ret = 0;
-+
-+done:
-+    PORT_FreeArena(pool, PR_TRUE);
-+    if (ret == 0) {
-+        *_oids = oids_list;
-+    } else {
-+        talloc_free(oids_list);
-+    }
-+
-+    return ret;
-+
-+}
-+
-+static int get_rdn_str(TALLOC_CTX *mem_ctx, CERTAVA **avas,
-+                       const char **rdn_str)
-+{
-+    size_t c;
-+    char *tmp_name = NULL;
-+    const char *tmp_str = NULL;
-+    int ret;
-+    SECStatus rv;
-+    CERTRDN rdn = { 0 };
-+    CERTName *name = NULL;
-+    PLArenaPool *arena = NULL;
-+
-+    arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
-+    if (arena == NULL) {
-+        ret = ENOMEM;
-+        goto done;
-+    }
-+
-+
-+    /* Multiple AVAs should be avoided becasue there is no general ordering
-+     * rule and the RDN strings are not reproducible */
-+    for (c = 0; avas[c] != NULL; c++) {
-+        rv = sss_CERT_AddAVA(arena, &rdn, avas[c]);
-+        if (rv != SECSuccess) {
-+            ret = EIO;
-+            goto done;
-+        }
-+    }
-+
-+    name = CERT_CreateName(&rdn, NULL);
-+    if (name == NULL) {
-+        ret = EIO;
-+        goto done;
-+    }
-+
-+    tmp_name = CERT_NameToAscii(name);
-+    CERT_DestroyName(name);
-+    if (tmp_name == NULL) {
-+        ret = EIO;
-+        goto done;
-+    }
-+
-+    tmp_str = talloc_strdup(mem_ctx, tmp_name);
-+    PORT_Free(tmp_name);
-+    if (tmp_str == NULL) {
-+        ret = ENOMEM;
-+        goto done;
-+    }
-+
-+    ret = 0;
-+
-+done:
-+    if (ret == 0) {
-+        *rdn_str = tmp_str;
-+    } else {
-+        talloc_free(discard_const(tmp_str));
-+    }
-+    PORT_FreeArena(arena, PR_FALSE);
-+
-+    return ret;
-+}
-+
-+static int get_rdn_list(TALLOC_CTX *mem_ctx, CERTRDN **rdns,
-+                        const char ***rdn_list)
-+{
-+    int ret;
-+    size_t c;
-+    const char **list = NULL;
-+
-+    for (c = 0; rdns[c] != NULL; c++);
-+    list = talloc_zero_array(mem_ctx, const char *, c + 1);
-+    if (list == NULL) {
-+        ret = ENOMEM;
-+        goto done;
-+    }
-+    for (c = 0; rdns[c] != NULL; c++) {
-+        ret = get_rdn_str(list, rdns[c]->avas,
-+                          &(list[c]));
-+        if (ret != 0) {
-+            goto done;
-+        }
-+    }
-+
-+    ret = 0;
-+
-+done:
-+    if (ret == 0) {
-+        *rdn_list = list;
-+    } else {
-+        talloc_free(list);
-+    }
-+
-+    return ret;
-+}
-+
-+enum san_opt nss_name_type_to_san_opt(CERTGeneralNameType type)
-+{
-+    switch (type) {
-+    case certOtherName:
-+        return SAN_OTHER_NAME;
-+    case certRFC822Name:
-+        return SAN_RFC822_NAME;
-+    case certDNSName:
-+        return SAN_DNS_NAME;
-+    case certX400Address:
-+        return SAN_X400_ADDRESS;
-+    case certDirectoryName:
-+        return SAN_DIRECTORY_NAME;
-+    case certEDIPartyName:
-+        return SAN_EDIPART_NAME;
-+    case certURI:
-+        return SAN_URI;
-+    case certIPAddress:
-+        return SAN_IP_ADDRESS;
-+    case certRegisterID:
-+        return SAN_REGISTERED_ID;
-+    default:
-+        return SAN_INVALID;
-+    }
-+}
-+
-+static int add_to_san_list(TALLOC_CTX *mem_ctx, bool is_bin,
-+                           enum san_opt san_opt, uint8_t *data, size_t len,
-+                           struct san_list **item)
-+{
-+    struct san_list *i;
-+
-+    if (data == NULL || len == 0 || san_opt == SAN_INVALID) {
-+        return EINVAL;
-+    }
-+
-+    i = talloc_zero(mem_ctx, struct san_list);
-+    if (i == NULL) {
-+        return ENOMEM;
-+    }
-+
-+    i->san_opt = san_opt;
-+    if (is_bin) {
-+        i->bin_val = talloc_memdup(i, data, len);
-+        i->bin_val_len = len;
-+    } else {
-+        i->val = talloc_strndup(i, (char *) data, len);
-+    }
-+    if (i->val == NULL) {
-+        talloc_free(i);
-+        return ENOMEM;
-+    }
-+
-+    *item = i;
-+
-+    return 0;
-+}
-+
-+/* taken from pkinit_crypto_nss.c of MIT Kerberos */
-+/* KerberosString: RFC 4120, 5.2.1. */
-+static const SEC_ASN1Template kerberos_string_template[] = {
-+    {
-+        SEC_ASN1_GENERAL_STRING,
-+        0,
-+        NULL,
-+        sizeof(SECItem),
-+    }
-+};
-+
-+/* Realm: RFC 4120, 5.2.2. */
-+struct realm {
-+    SECItem name;
-+};
-+static const SEC_ASN1Template realm_template[] = {
-+    {
-+        SEC_ASN1_GENERAL_STRING,
-+        0,
-+        NULL,
-+        sizeof(SECItem),
-+    }
-+};
-+
-+/* PrincipalName: RFC 4120, 5.2.2. */
-+static const SEC_ASN1Template sequence_of_kerberos_string_template[] = {
-+    {
-+        SEC_ASN1_SEQUENCE_OF,
-+        0,
-+        &kerberos_string_template,
-+        0,
-+    }
-+};
-+
-+struct principal_name {
-+    SECItem name_type;
-+    SECItem **name_string;
-+};
-+static const SEC_ASN1Template principal_name_template[] = {
-+    {
-+        SEC_ASN1_SEQUENCE,
-+        0,
-+        NULL,
-+        sizeof(struct principal_name),
-+    },
-+    {
-+        SEC_ASN1_CONTEXT_SPECIFIC | 0 | SEC_ASN1_CONSTRUCTED | SEC_ASN1_EXPLICIT,
-+        offsetof(struct principal_name, name_type),
-+        &SEC_IntegerTemplate,
-+        sizeof(SECItem),
-+    },
-+    {
-+        SEC_ASN1_CONTEXT_SPECIFIC | 1 | SEC_ASN1_CONSTRUCTED | SEC_ASN1_EXPLICIT,
-+        offsetof(struct principal_name, name_string),
-+        sequence_of_kerberos_string_template,
-+        sizeof(struct SECItem **),
-+    },
-+    {0, 0, NULL, 0},
-+};
-+
-+/* KRB5PrincipalName: RFC 4556, 3.2.2. */
-+struct kerberos_principal_name {
-+    SECItem realm;
-+    struct principal_name principal_name;
-+};
-+static const SEC_ASN1Template kerberos_principal_name_template[] = {
-+    {
-+        SEC_ASN1_SEQUENCE,
-+        0,
-+        NULL,
-+        sizeof(struct kerberos_principal_name),
-+    },
-+    {
-+        SEC_ASN1_CONTEXT_SPECIFIC | 0 | SEC_ASN1_CONSTRUCTED | SEC_ASN1_EXPLICIT,
-+        offsetof(struct kerberos_principal_name, realm),
-+        &realm_template,
-+        sizeof(struct realm),
-+    },
-+    {
-+        SEC_ASN1_CONTEXT_SPECIFIC | 1 | SEC_ASN1_CONSTRUCTED | SEC_ASN1_EXPLICIT,
-+        offsetof(struct kerberos_principal_name, principal_name),
-+        &principal_name_template,
-+        sizeof(struct principal_name),
-+    },
-+    {0, 0, NULL, 0}
-+};
-+
-+#define PKINIT_OID "1.3.6.1.5.2.2"
-+#define NT_PRINCIPAL_OID "1.3.6.1.4.1.311.20.2.3"
-+
-+static int add_string_other_name_to_san_list(TALLOC_CTX *mem_ctx,
-+                                             enum san_opt san_opt,
-+                                             CERTGeneralName *current,
-+                                             struct san_list **item)
-+{
-+    struct san_list *i = NULL;
-+    int ret;
-+    char *tmp_str;
-+
-+    tmp_str = CERT_GetOidString(&(current->name.OthName.oid));
-+    /* is it expexted that NSS OID strings start with "OID." but we
-+     * prefer the plain dotted-decimal version so the prefix is skipped */
-+    if (tmp_str == NULL || strncmp(tmp_str, "OID.", 4) != 0) {
-+        PR_smprintf_free(tmp_str);
-+        return EINVAL;
-+    }
-+
-+    i = talloc_zero(mem_ctx, struct san_list);
-+    if (i == NULL) {
-+        PR_smprintf_free(tmp_str);
-+        return ENOMEM;
-+    }
-+    i->san_opt = san_opt;
-+
-+    i->other_name_oid = talloc_strdup(i, tmp_str + 4);
-+    PR_smprintf_free(tmp_str);
-+    if (i->other_name_oid == NULL) {
-+        ret = ENOMEM;
-+        goto done;
-+    }
-+
-+    i->bin_val = talloc_memdup(i, current->name.OthName.name.data,
-+                                        current->name.OthName.name.len);
-+    if (i->bin_val == NULL) {
-+        ret = ENOMEM;
-+        goto done;
-+    }
-+    i->bin_val_len = current->name.OthName.name.len;
-+
-+    ret = 0;
-+
-+done:
-+    if (ret == 0) {
-+        *item = i;
-+    } else {
-+        talloc_free(i);
-+    }
-+
-+    return ret;
-+}
-+
-+static int get_short_name(TALLOC_CTX *mem_ctx, const char *full_name,
-+                          char delim, char **short_name)
-+{
-+    char *at;
-+    char *s;
-+
-+    if (full_name == NULL || delim == '\0' || short_name == NULL) {
-+        return EINVAL;
-+    }
-+
-+    at = strchr(full_name, delim);
-+    if (at != NULL) {
-+        s = talloc_strndup(mem_ctx, full_name, (at - full_name));
-+    } else {
-+        s = talloc_strdup(mem_ctx, full_name);
-+    }
-+    if (s == NULL) {
-+        return ENOMEM;
-+    }
-+
-+    *short_name = s;
-+
-+    return 0;
-+}
-+
-+static int add_nt_princ_to_san_list(TALLOC_CTX *mem_ctx,
-+                                    PLArenaPool *pool,
-+                                    enum san_opt san_opt,
-+                                    CERTGeneralName *current,
-+                                    struct san_list **item)
-+{
-+    struct san_list *i = NULL;
-+    SECStatus rv;
-+    SECItem tmp_secitem = { 0 };
-+    int ret;
-+
-+    rv = SEC_ASN1DecodeItem(pool, &tmp_secitem,
-+                            SEC_ASN1_GET(SEC_UTF8StringTemplate),
-+                            &(current->name.OthName.name));
-+    if (rv != SECSuccess) {
-+        return EINVAL;
-+    }
-+
-+    i = talloc_zero(mem_ctx, struct san_list);
-+    if (i == NULL) {
-+        return ENOMEM;
-+    }
-+    i->san_opt = san_opt;
-+
-+    i->val = talloc_strndup(i, (char *) tmp_secitem.data,
-+                                              tmp_secitem.len);
-+    if (i->val == NULL) {
-+        ret = ENOMEM;
-+        goto done;
-+    }
-+
-+    ret = get_short_name(i, i->val, '@', &(i->short_name));
-+    if (ret != 0) {
-+        goto done;
-+    }
-+
-+    ret = 0;
-+
-+done:
-+    if (ret == 0) {
-+        *item = i;
-+    } else {
-+        talloc_free(i);
-+    }
-+
-+    return ret;
-+}
-+
-+static int add_pkinit_princ_to_san_list(TALLOC_CTX *mem_ctx,
-+                                        PLArenaPool *pool,
-+                                        enum san_opt san_opt,
-+                                        CERTGeneralName *current,
-+                                        struct san_list **item)
-+{
-+    struct san_list *i = NULL;
-+    SECStatus rv;
-+    struct kerberos_principal_name kname;
-+    int ret;
-+    size_t c;
-+
-+    rv = SEC_ASN1DecodeItem(pool, &kname,
-+                            kerberos_principal_name_template,
-+                            &(current->name.OthName.name));
-+    if (rv != SECSuccess) {
-+        return EINVAL;
-+    }
-+
-+    i = talloc_zero(mem_ctx, struct san_list);
-+    if (i == NULL) {
-+        return ENOMEM;
-+    }
-+    i->san_opt = san_opt;
-+
-+    if (kname.principal_name.name_string != NULL) {
-+        i->val = talloc_strdup(i, "");
-+        if (i->val == NULL) {
-+            ret = ENOMEM;
-+            goto done;
-+        }
-+        for (c = 0; kname.principal_name.name_string[c] != NULL; c++) {
-+            if (c > 0) {
-+                i->val = talloc_strdup_append(i->val, "/");
-+                if (i->val == NULL) {
-+                    ret = ENOMEM;
-+                    goto done;
-+                }
-+            }
-+            i->val = talloc_strndup_append(i->val,
-+                         (char *) kname.principal_name.name_string[c]->data,
-+                          kname.principal_name.name_string[c]->len);
-+            if (i->val == NULL) {
-+                ret = ENOMEM;
-+                goto done;
-+            }
-+        }
-+        i->val = talloc_strndup_append(i->val,
-+                                             (char *) kname.realm.data,
-+                                             kname.realm.len);
-+        if (i->val == NULL) {
-+            ret = ENOMEM;
-+            goto done;
-+        }
-+
-+        ret = get_short_name(i, i->val, '@', &(i->short_name));
-+        if (ret != 0) {
-+            goto done;
-+        }
-+    }
-+
-+    ret = 0;
-+
-+done:
-+    if (ret == 0) {
-+        *item = i;
-+    } else {
-+        talloc_free(i);
-+    }
-+
-+    return ret;
-+}
-+
-+static int add_oid_to_san_list(TALLOC_CTX *mem_ctx,
-+                               enum san_opt san_opt,
-+                               SECItem oid,
-+                               struct san_list **item)
-+{
-+    struct san_list *i = NULL;
-+    char *tmp_str;
-+
-+    tmp_str = CERT_GetOidString(&oid);
-+    /* is it expexted that NSS OID strings start with "OID." but we
-+     * prefer the plain dotted-decimal version so the prefix is skipped */
-+    if (tmp_str == NULL || strncmp(tmp_str, "OID.", 4) != 0) {
-+        PR_smprintf_free(tmp_str);
-+        return EINVAL;
-+    }
-+
-+    i = talloc_zero(mem_ctx, struct san_list);
-+    if (i == NULL) {
-+        PR_smprintf_free(tmp_str);
-+        return ENOMEM;
-+    }
-+    i->san_opt = san_opt;
-+
-+    i->val = talloc_strdup(i, tmp_str + 4);
-+    PR_smprintf_free(tmp_str);
-+    if (i->val == NULL) {
-+        talloc_free(i);
-+        return ENOMEM;
-+    }
-+
-+    *item = i;
-+    return 0;
-+}
-+
-+static int add_rdn_list_to_san_list(TALLOC_CTX *mem_ctx,
-+                                    enum san_opt san_opt,
-+                                    CERTName name,
-+                                    struct san_list **item)
-+{
-+    struct san_list *i = NULL;
-+    int ret;
-+
-+    i = talloc_zero(mem_ctx, struct san_list);
-+    if (i == NULL) {
-+        return ENOMEM;
-+    }
-+    i->san_opt = san_opt;
-+
-+    ret = get_rdn_list(i, name.rdns, &(i->rdn_list));
-+    if (ret != 0) {
-+        talloc_free(i);
-+        return ret;
-+    }
-+
-+    *item = i;
-+    return 0;
-+}
-+
-+static int add_ip_to_san_list(TALLOC_CTX *mem_ctx, enum san_opt san_opt,
-+                              uint8_t *data, size_t len,
-+                              struct san_list **item)
-+{
-+    struct san_list *i;
-+    PRStatus   st;
-+    PRNetAddr  addr;
-+    char       addrBuf[80];
-+
-+    if (data == NULL || len == 0 || san_opt == SAN_INVALID) {
-+        return EINVAL;
-+    }
-+
-+    /* taken from secu_PrintIPAddress() */
-+    memset(&addr, 0, sizeof addr);
-+    if (len == 4) {
-+        addr.inet.family = PR_AF_INET;
-+        memcpy(&addr.inet.ip, data, len);
-+    } else if (len == 16) {
-+        addr.ipv6.family = PR_AF_INET6;
-+        memcpy(addr.ipv6.ip.pr_s6_addr, data, len);
-+        if (PR_IsNetAddrType(&addr, PR_IpAddrV4Mapped)) {
-+            /* convert to IPv4.  */
-+            addr.inet.family = PR_AF_INET;
-+            memcpy(&addr.inet.ip, &addr.ipv6.ip.pr_s6_addr[12], 4);
-+            memset(&addr.inet.pad[0], 0, sizeof addr.inet.pad);
-+        }
-+    } else {
-+        return EINVAL;
-+    }
-+
-+    st = PR_NetAddrToString(&addr, addrBuf, sizeof addrBuf);
-+    if (st != PR_SUCCESS) {
-+        return EIO;
-+    }
-+
-+    i = talloc_zero(mem_ctx, struct san_list);
-+    if (i == NULL) {
-+        return ENOMEM;
-+    }
-+
-+    i->san_opt = san_opt;
-+    i->val = talloc_strdup(i, addrBuf);
-+    if (i->val == NULL) {
-+        talloc_free(i);
-+        return ENOMEM;
-+    }
-+
-+    *item = i;
-+    return 0;
-+}
-+static int add_principal_to_san_list(TALLOC_CTX *mem_ctx,
-+                                     enum san_opt san_opt,
-+                                     const char *princ,
-+                                     struct san_list **item)
-+{
-+    struct san_list *i = NULL;
-+    int ret;
-+
-+    i = talloc_zero(mem_ctx, struct san_list);
-+    if (i == NULL) {
-+        return ENOMEM;
-+    }
-+    i->san_opt = san_opt;
-+
-+    i->val = talloc_strdup(i, princ);
-+    if (i->val == NULL) {
-+        ret = ENOMEM;
-+        goto done;
-+    }
-+
-+    ret = get_short_name(i, i->val, '@', &(i->short_name));
-+    if (ret != 0) {
-+        goto done;
-+    }
-+
-+    ret = 0;
-+
-+done:
-+    if (ret == 0) {
-+        *item = i;
-+    } else {
-+        talloc_free(i);
-+    }
-+
-+    return ret;
-+}
-+
-+static int get_san(TALLOC_CTX *mem_ctx, CERTCertificate *cert,
-+                   struct san_list **san_list)
-+{
-+
-+    SECItem subAltName = { 0 };
-+    SECStatus rv;
-+    CERTGeneralName *name_list = NULL;
-+    CERTGeneralName *current;
-+    PLArenaPool *pool = NULL;
-+    int ret;
-+    struct san_list *list = NULL;
-+    struct san_list *item = NULL;
-+    struct san_list *item_s = NULL;
-+    struct san_list *item_p = NULL;
-+    struct san_list *item_pb = NULL;
-+
-+    rv = CERT_FindCertExtension(cert, SEC_OID_X509_SUBJECT_ALT_NAME,
-+                                &subAltName);
-+    if (rv != SECSuccess) {
-+        if (rv == SECFailure
-+                && PORT_GetError() == SEC_ERROR_EXTENSION_NOT_FOUND) {
-+            ret = EOK;
-+        } else {
-+            ret = EIO;
-+        }
-+        goto done;
-+    }
-+
-+    pool = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
-+    if (pool == NULL) {
-+        ret = ENOMEM;
-+        goto done;
-+    }
-+
-+    name_list = CERT_DecodeAltNameExtension(pool, &subAltName);
-+    if (name_list == NULL ) {
-+        ret = EIO;
-+        goto done;
-+    }
-+
-+    current = name_list;
-+    do {
-+        switch (current->type) {
-+        case certOtherName:
-+            ret = add_string_other_name_to_san_list(mem_ctx,
-+                                                    SAN_STRING_OTHER_NAME,
-+                                                    current, &item_s);
-+            if (ret != 0) {
-+                goto done;
-+            }
-+            DLIST_ADD(list, item_s);
-+
-+            item_p = NULL;
-+            if (strcmp(item_s->other_name_oid, NT_PRINCIPAL_OID) == 0) {
-+                ret = add_nt_princ_to_san_list(mem_ctx, pool, SAN_NT, current,
-+                                               &item_p);
-+                if (ret != 0) {
-+                    goto done;
-+                }
-+                DLIST_ADD(list, item_p);
-+            } else if (strcmp(item_s->other_name_oid, PKINIT_OID) == 0) {
-+                ret = add_pkinit_princ_to_san_list(mem_ctx, pool, SAN_PKINIT,
-+                                                   current, &item_p);
-+                if (ret != 0) {
-+                    goto done;
-+                }
-+                DLIST_ADD(list, item_p);
-+            }
-+
-+            if (item_p != NULL) {
-+                ret = add_principal_to_san_list(mem_ctx, SAN_PRINCIPAL,
-+                                                item_p->val, &item_pb);
-+                if (ret != 0) {
-+                    goto done;
-+                }
-+                DLIST_ADD(list, item_pb);
-+            }
-+
-+            break;
-+        case certRFC822Name:
-+        case certDNSName:
-+        case certURI:
-+            ret = add_to_san_list(mem_ctx, false,
-+                                  nss_name_type_to_san_opt(current->type),
-+                                  current->name.other.data,
-+                                  current->name.other.len, &item);
-+            if (ret != 0) {
-+                goto done;
-+            }
-+
-+            if (current->type == certRFC822Name
-+                    || current->type == certDNSName) {
-+                ret = get_short_name(item, item->val,
-+                                     (current->type == certRFC822Name
-+                                                          ? '@' : '.'),
-+                                     &(item->short_name));
-+                if (ret != 0) {
-+                    goto done;
-+                }
-+            }
-+
-+            DLIST_ADD(list, item);
-+            break;
-+        case certIPAddress:
-+            ret = add_ip_to_san_list(mem_ctx,
-+                                     nss_name_type_to_san_opt(current->type),
-+                                     current->name.other.data,
-+                                     current->name.other.len, &item);
-+            if (ret != 0) {
-+                goto done;
-+            }
-+            DLIST_ADD(list, item);
-+            break;
-+        case certDirectoryName:
-+            ret = add_rdn_list_to_san_list(mem_ctx,
-+                                        nss_name_type_to_san_opt(current->type),
-+                                        current->name.directoryName, &item);
-+            if (ret != 0) {
-+                goto done;
-+            }
-+            DLIST_ADD(list, item);
-+            break;
-+        case certRegisterID:
-+            ret = add_oid_to_san_list(mem_ctx,
-+                                      nss_name_type_to_san_opt(current->type),
-+                                      current->name.other, &item);
-+            if (ret != 0) {
-+                goto done;
-+            }
-+            DLIST_ADD(list, item);
-+            break;
-+        case certX400Address:
-+        case certEDIPartyName:
-+            ret = add_to_san_list(mem_ctx, true,
-+                                  nss_name_type_to_san_opt(current->type),
-+                                  current->name.other.data,
-+                                  current->name.other.len, &item);
-+            if (ret != 0) {
-+                goto done;
-+            }
-+            DLIST_ADD(list, item);
-+            break;
-+        default:
-+            ret = EINVAL;
-+        }
-+
-+        current = CERT_GetNextGeneralName(current);
-+        if (current == NULL) {
-+            ret = EIO;
-+            goto done;
-+        }
-+    } while (current != name_list);
-+
-+done:
-+
-+    /* Don't free nameList, it's part of the arena. */
-+
-+    if (pool != NULL) {
-+        PORT_FreeArena(pool, PR_FALSE);
-+    }
-+
-+    if (subAltName.data != NULL) {
-+        SECITEM_FreeItem(&subAltName, PR_FALSE);
-+    }
-+
-+    if (ret == EOK) {
-+        *san_list = list;
-+    }
-+    return ret;
-+}
-+
-+int sss_cert_get_content(TALLOC_CTX *mem_ctx,
-+                         const uint8_t *der_blob, size_t der_size,
-+                         struct sss_cert_content **content)
-+{
-+    int ret;
-+    struct sss_cert_content *cont = NULL;
-+    CERTCertDBHandle *handle;
-+    CERTCertificate *cert = NULL;
-+    SECItem der_item;
-+    NSSInitContext *nss_ctx;
-+
-+    if (der_blob == NULL || der_size == 0) {
-+        return EINVAL;
-+    }
-+
-+    nss_ctx = NSS_InitContext("", "", "", "", NULL, NSS_INIT_READONLY
-+                                                    |  NSS_INIT_NOCERTDB
-+                                                    | NSS_INIT_NOMODDB
-+                                                    | NSS_INIT_FORCEOPEN
-+                                                    | NSS_INIT_NOROOTINIT
-+                                                    |  NSS_INIT_OPTIMIZESPACE);
-+    if (nss_ctx == NULL) {
-+        return EIO;
-+    }
-+
-+    cont = talloc_zero(mem_ctx, struct sss_cert_content);
-+    if (cont == NULL) {
-+        return ENOMEM;
-+    }
-+
-+    handle = CERT_GetDefaultCertDB();
-+    der_item.len = der_size;
-+    der_item.data = discard_const(der_blob);
-+
-+    cert = CERT_NewTempCertificate(handle, &der_item, NULL, PR_FALSE, PR_TRUE);
-+    if (cert == NULL) {
-+        ret = EINVAL;
-+        goto done;
-+    }
-+
-+    cont->issuer_str = talloc_strdup(cont, cert->issuerName);
-+    if (cont->issuer_str == NULL) {
-+        ret = ENOMEM;
-+        goto done;
-+    }
-+
-+    ret = get_rdn_list(cont, cert->issuer.rdns, &cont->issuer_rdn_list);
-+    if (ret != 0) {
-+        goto done;
-+    }
-+
-+    cont->subject_str = talloc_strdup(cont, cert->subjectName);
-+    if (cont->subject_str == NULL) {
-+        ret = ENOMEM;
-+        goto done;
-+    }
-+
-+    ret = get_rdn_list(cont, cert->subject.rdns, &cont->subject_rdn_list);
-+    if (ret != 0) {
-+        goto done;
-+    }
-+
-+
-+    cont->key_usage = cert->keyUsage;
-+
-+    ret = get_extended_key_usage_oids(cont, cert,
-+                                      &(cont->extended_key_usage_oids));
-+    if (ret != 0) {
-+        goto done;
-+    }
-+
-+    ret = get_san(cont, cert, &(cont->san_list));
-+    if (ret != 0) {
-+        goto done;
-+    }
-+
-+    cont->cert_der = talloc_memdup(cont, der_blob, der_size);
-+    if (cont->cert_der == NULL) {
-+        ret = ENOMEM;
-+        goto done;
-+    }
-+
-+    cont->cert_der_size = der_size;
-+    ret = EOK;
-+
-+done:
-+
-+    CERT_DestroyCertificate(cert);
-+    NSS_ShutdownContext(nss_ctx);
-+
-+    if (ret == EOK) {
-+        *content = cont;
-+    } else {
-+        talloc_free(cont);
-+    }
-+
-+    return ret;
-+}
-diff --git a/src/lib/certmap/sss_certmap.c b/src/lib/certmap/sss_certmap.c
-new file mode 100644
-index 0000000000000000000000000000000000000000..080cab8ab585ce64a88c352a23a8062887fa720a
---- /dev/null
-+++ b/src/lib/certmap/sss_certmap.c
-@@ -0,0 +1,993 @@
-+/*
-+    SSSD
-+
-+    Library for rule based certificate to user mapping
-+
-+    Authors:
-+        Sumit Bose <sbose@redhat.com>
-+
-+    Copyright (C) 2017 Red Hat
-+
-+    This program is free software; you can redistribute it and/or modify
-+    it under the terms of the GNU General Public License as published by
-+    the Free Software Foundation; either version 3 of the License, or
-+    (at your option) any later version.
-+
-+    This program is distributed in the hope that it will be useful,
-+    but WITHOUT ANY WARRANTY; without even the implied warranty of
-+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+    GNU General Public License for more details.
-+
-+    You should have received a copy of the GNU General Public License
-+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
-+*/
-+
-+#include "util/util.h"
-+#include "util/cert.h"
-+#include "util/crypto/sss_crypto.h"
-+#include "lib/certmap/sss_certmap.h"
-+#include "lib/certmap/sss_certmap_int.h"
-+
-+int debug_level;
-+void sss_debug_fn(const char *file,
-+                  long line,
-+                  const char *function,
-+                  int level,
-+                  const char *format, ...)
-+{
-+    return;
-+}
-+
-+static int get_type_prefix(TALLOC_CTX *mem_ctx, const char *match_rule,
-+                           char **type, const char **rule_start)
-+{
-+    const char *c;
-+    char *delim;
-+
-+    *type = NULL;
-+    *rule_start = match_rule;
-+
-+    delim = strchr(match_rule, ':');
-+    if (delim == NULL) {
-+        /* no type prefix found */
-+        return 0;
-+    }
-+
-+    /* rule starts with ':', empty type */
-+    if (delim == match_rule) {
-+        *rule_start = delim + 1;
-+        return EOK;
-+    }
-+
-+    for (c = match_rule; c < delim; c++) {
-+        /* type prefix may only contain digits and upper-case ASCII characters */
-+        if (!(isascii(*c) && (isdigit(*c) || isupper(*c)))) {
-+            /* no type prefix found */
-+            return 0;
-+        }
-+    }
-+
-+    *rule_start = delim + 1;
-+    *type = talloc_strndup(mem_ctx, match_rule, (delim - match_rule));
-+    if (*type == NULL) {
-+        return ENOMEM;
-+    }
-+
-+    return 0;
-+}
-+
-+static int parse_match_rule(struct sss_certmap_ctx *ctx, const char *match_rule,
-+                            struct krb5_match_rule **parsed_match_rule)
-+{
-+    int ret;
-+    char *type;
-+    const char *rule_start;
-+
-+    ret = get_type_prefix(ctx, match_rule, &type, &rule_start);
-+    if (ret != EOK) {
-+        CM_DEBUG(ctx, "Failed to read rule type.");
-+        goto done;
-+    }
-+
-+    if (type == NULL || strcmp(type, "KRB5") == 0) {
-+        ret = parse_krb5_match_rule(ctx, rule_start, parsed_match_rule);
-+        if (ret != EOK) {
-+            CM_DEBUG(ctx, "Failed to parse KRB5 matching rule.");
-+            goto done;
-+        }
-+    } else {
-+        CM_DEBUG(ctx, "Unsupported matching rule type.");
-+        ret = ESRCH;
-+        goto done;
-+    }
-+
-+    ret = EOK;
-+
-+done:
-+    talloc_free(type);
-+
-+    return ret;
-+}
-+
-+static int parse_mapping_rule(struct sss_certmap_ctx *ctx,
-+                              const char *mapping_rule,
-+                              struct ldap_mapping_rule **parsed_mapping_rule)
-+{
-+    int ret;
-+    char *type;
-+    const char *rule_start;
-+
-+    ret = get_type_prefix(ctx, mapping_rule, &type, &rule_start);
-+    if (ret != EOK) {
-+        CM_DEBUG(ctx, "Failed to read rule type.");
-+        goto done;
-+    }
-+
-+    if (type == NULL || strcmp(type, "LDAP") == 0) {
-+        ret = parse_ldap_mapping_rule(ctx, rule_start, parsed_mapping_rule);
-+        if (ret != EOK) {
-+            CM_DEBUG(ctx, "Failed to parse LDAP mapping rule.");
-+            goto done;
-+        }
-+    } else {
-+        CM_DEBUG(ctx, "Unsupported mapping rule type.");
-+        ret = ESRCH;
-+        goto done;
-+    }
-+
-+    ret = EOK;
-+
-+done:
-+    talloc_free(type);
-+
-+    return ret;
-+}
-+
-+int sss_certmap_add_rule(struct sss_certmap_ctx *ctx,
-+                         uint32_t priority, const char *match_rule,
-+                         const char *map_rule, const char **domains)
-+{
-+    size_t c;
-+    int ret;
-+    struct match_map_rule *rule;
-+    struct TALLOC_CTX *tmp_ctx;
-+    struct priority_list *p;
-+    struct priority_list *p_new;
-+    struct krb5_match_rule *parsed_match_rule;
-+    struct ldap_mapping_rule *parsed_mapping_rule;
-+
-+    tmp_ctx = talloc_new(NULL);
-+    if (tmp_ctx == NULL) {
-+        return ENOMEM;
-+    }
-+
-+    rule = talloc_zero(tmp_ctx, struct match_map_rule);
-+    if (rule == NULL) {
-+        ret = ENOMEM;
-+        goto done;
-+    }
-+
-+    rule->priority = priority;
-+
-+    if (match_rule == NULL) {
-+        match_rule = DEFAULT_MATCH_RULE;
-+    }
-+    ret = parse_match_rule(ctx, match_rule, &parsed_match_rule);
-+    if (ret == 0) {
-+        rule->parsed_match_rule = talloc_steal(rule, parsed_match_rule);
-+        rule->match_rule = talloc_strdup(rule, match_rule);
-+        if (rule->match_rule == NULL) {
-+            ret = ENOMEM;
-+            goto done;
-+        }
-+    } else if (ret == ESRCH) {
-+        /* report unsupported rules */
-+        goto done;
-+    } else {
-+        goto done;
-+    }
-+
-+    if (map_rule == NULL) {
-+        map_rule = DEFAULT_MAP_RULE;
-+    }
-+    ret = parse_mapping_rule(ctx, map_rule, &parsed_mapping_rule);
-+    if (ret == 0) {
-+        rule->parsed_mapping_rule = talloc_steal(rule, parsed_mapping_rule);
-+        rule->map_rule = talloc_strdup(rule, map_rule);
-+        if (rule->map_rule == NULL) {
-+            ret = ENOMEM;
-+            goto done;
-+        }
-+    } else if (ret == ESRCH) {
-+        /* report unsupported rules */
-+        goto done;
-+    } else {
-+        goto done;
-+    }
-+
-+    if (domains != NULL && *domains != NULL) {
-+        for (c = 0; domains[c] != NULL; c++);
-+        rule->domains = talloc_zero_array(rule, char *, c + 1);
-+        if (rule->domains == NULL) {
-+            ret = ENOMEM;
-+            goto done;
-+        }
-+        for (c = 0; domains[c] != NULL; c++) {
-+            rule->domains[c] = talloc_strdup(rule->domains, domains[c]);
-+            if (rule->domains[c] == NULL) {
-+                ret = ENOMEM;
-+                goto done;
-+            }
-+        }
-+    }
-+
-+    if (ctx->prio_list == NULL) {
-+        ctx->prio_list = talloc_zero(ctx, struct priority_list);
-+        if (ctx->prio_list == NULL) {
-+            ret = ENOMEM;
-+            goto done;
-+        }
-+
-+        ctx->prio_list->priority = rule->priority;
-+        ctx->prio_list->rule_list = rule;
-+    } else {
-+        for (p = ctx->prio_list; p != NULL && p->priority < rule->priority;
-+                                                                   p = p->next);
-+        if (p != NULL && p->priority == priority) {
-+            DLIST_ADD(p->rule_list, rule);
-+        } else {
-+            p_new = talloc_zero(ctx, struct priority_list);
-+            if (p_new == NULL) {
-+                ret = ENOMEM;
-+                goto done;
-+            }
-+
-+            p_new->priority = rule->priority;
-+            p_new->rule_list = rule;
-+
-+            if (p == NULL) {
-+                DLIST_ADD_END(ctx->prio_list, p_new, struct priority_list *);
-+            } else if (p->prev == NULL) {
-+                DLIST_ADD(ctx->prio_list, p_new);
-+            } else {
-+                DLIST_ADD_AFTER(ctx->prio_list, p_new, p->prev);
-+            }
-+        }
-+    }
-+
-+    talloc_steal(ctx, rule);
-+
-+    ret = EOK;
-+
-+done:
-+    talloc_free(tmp_ctx);
-+
-+    return ret;
-+}
-+
-+static int expand_cert(struct sss_certmap_ctx *ctx,
-+                       struct parsed_template *parsed_template,
-+                       struct sss_cert_content *cert_content,
-+                       char **expanded)
-+{
-+    int ret;
-+    char *tmp_str = NULL;
-+
-+    if (parsed_template->conversion == NULL
-+            || strcmp(parsed_template->conversion, "bin") == 0) {
-+        ret = bin_to_ldap_filter_value(ctx, cert_content->cert_der,
-+                                       cert_content->cert_der_size, &tmp_str);
-+        if (ret != 0) {
-+            CM_DEBUG(ctx, "bin conversion failed.");
-+            goto done;
-+        }
-+    } else if (strcmp(parsed_template->conversion, "base64") == 0) {
-+        tmp_str = sss_base64_encode(ctx, cert_content->cert_der,
-+                                    cert_content->cert_der_size);
-+        if (tmp_str == NULL) {
-+            CM_DEBUG(ctx, "base64 conversion failed.");
-+            ret = ENOMEM;
-+            goto done;
-+        }
-+    } else {
-+        CM_DEBUG(ctx, "Unsupported conversion.");
-+        ret = EINVAL;
-+        goto done;
-+    }
-+
-+    ret = 0;
-+
-+done:
-+    if (ret == 0) {
-+        *expanded = tmp_str;
-+    } else {
-+        talloc_free(tmp_str);
-+    }
-+
-+    return ret;
-+}
-+
-+static int get_dn_str(struct sss_certmap_ctx *ctx, const char *conversion,
-+                      const char **rdn_list, char **result)
-+{
-+    char *str = NULL;
-+    size_t c;
-+    int ret;
-+    char *conv = NULL;
-+
-+    str = talloc_strdup(ctx, "");
-+    if (str == NULL) {
-+        ret = ENOMEM;
-+        goto done;
-+    }
-+    if (conversion == NULL || strcmp(conversion, "nss_ldap") == 0
-+                           || strcmp(conversion, "nss") == 0) {
-+        for (c = 0; rdn_list[c] != NULL; c++);
-+        while (c != 0) {
-+            c--;
-+            str = talloc_asprintf_append(str, "%s%s",
-+                                         (rdn_list[c + 1] == NULL) ? "" : ",",
-+                                         rdn_list[c]);
-+            if (str == NULL) {
-+                ret = ENOMEM;
-+                goto done;
-+            }
-+        };
-+    } else if (strcmp(conversion, "ad_ldap") == 0) {
-+        for (c = 0; rdn_list[c] != NULL; c++);
-+        while (c != 0) {
-+            c--;
-+            conv = check_ad_attr_name(str, rdn_list[c]);
-+            str = talloc_asprintf_append(str, "%s%s",
-+                                         (rdn_list[c + 1] == NULL) ? "" : ",",
-+                                         conv == NULL ? rdn_list[c] : conv);
-+            talloc_free(conv);
-+            conv = NULL;
-+            if (str == NULL) {
-+                ret = ENOMEM;
-+                goto done;
-+            }
-+        };
-+    } else if (strcmp(conversion, "nss_x500") == 0) {
-+        for (c = 0; rdn_list[c] != NULL; c++) {
-+            str = talloc_asprintf_append(str, "%s%s", (c == 0) ? "" : ",",
-+                                                       rdn_list[c]);
-+            if (str == NULL) {
-+                ret = ENOMEM;
-+                goto done;
-+            }
-+        }
-+    } else if (strcmp(conversion, "ad_x500") == 0
-+                        || strcmp(conversion, "ad") == 0) {
-+        for (c = 0; rdn_list[c] != NULL; c++) {
-+            conv = check_ad_attr_name(str, rdn_list[c]);
-+            str = talloc_asprintf_append(str, "%s%s",
-+                                         (c == 0) ? "" : ",",
-+                                         conv == NULL ? rdn_list[c] : conv);
-+            talloc_free(conv);
-+            conv = NULL;
-+            if (str == NULL) {
-+                ret = ENOMEM;
-+                goto done;
-+            }
-+        }
-+    } else {
-+        ret = EINVAL;
-+        goto done;
-+    }
-+
-+    ret = 0;
-+
-+done:
-+    if (ret == 0) {
-+        *result = str;
-+    } else {
-+        talloc_free(str);
-+    }
-+
-+    return ret;
-+}
-+
-+static int expand_san_blob(struct sss_certmap_ctx *ctx, enum san_opt san_opt,
-+                           struct san_list *san_list, char **expanded)
-+{
-+    struct san_list *item;
-+    char *exp;
-+    int ret;
-+
-+    DLIST_FOR_EACH(item, san_list) {
-+        if (item->san_opt == san_opt) {
-+            ret = bin_to_ldap_filter_value(ctx, item->bin_val,
-+                                           item->bin_val_len, &exp);
-+            if (ret != 0) {
-+                CM_DEBUG(ctx, "bin conversion failed.");
-+                return ret;
-+            }
-+
-+            *expanded = exp;
-+            return 0;
-+        }
-+    }
-+
-+    return ENOENT;
-+}
-+
-+static int expand_san_string(struct sss_certmap_ctx *ctx, enum san_opt san_opt,
-+                             struct san_list *san_list, const char *attr_name,
-+                             char **expanded)
-+{
-+    struct san_list *item;
-+    char *exp;
-+
-+    DLIST_FOR_EACH(item, san_list) {
-+        if (item->san_opt == san_opt) {
-+            if (attr_name == NULL) {
-+                exp = talloc_strdup(ctx, item->val);
-+            } else if (strcasecmp(attr_name, "short_name") == 0) {
-+                exp = talloc_strdup(ctx, item->short_name);
-+            } else {
-+                CM_DEBUG(ctx, "Unsupported attribute name [%s].", attr_name);
-+                return EINVAL;
-+            }
-+
-+            if (exp == NULL) {
-+                return ENOMEM;
-+            }
-+
-+            *expanded = exp;
-+            return 0;
-+        }
-+    }
-+
-+    return ENOENT;
-+}
-+
-+static int expand_san_rdn_list(struct sss_certmap_ctx *ctx,
-+                               enum san_opt san_opt,
-+                               struct san_list *san_list,
-+                               const char *conversion,
-+                               char **expanded)
-+{
-+    struct san_list *item;
-+    char *exp;
-+    int ret;
-+
-+    DLIST_FOR_EACH(item, san_list) {
-+        if (item->san_opt == san_opt) {
-+            ret = get_dn_str(ctx, conversion, item->rdn_list, &exp);
-+            if (ret != 0) {
-+                return ret;
-+            }
-+
-+            *expanded = exp;
-+            return 0;
-+        }
-+    }
-+
-+    return ENOENT;
-+}
-+
-+
-+static int expand_san(struct sss_certmap_ctx *ctx,
-+                        struct parsed_template *parsed_template,
-+                        struct san_list *san_list,
-+                        char **expanded)
-+{
-+    int ret;
-+
-+    if (strcmp("subject_rfc822_name", parsed_template->name) == 0) {
-+        ret = expand_san_string(ctx, SAN_RFC822_NAME, san_list,
-+                                parsed_template->attr_name, expanded);
-+    } else if (strcmp("subject_dns_name", parsed_template->name) == 0) {
-+        ret = expand_san_string(ctx, SAN_DNS_NAME, san_list,
-+                                parsed_template->attr_name, expanded);
-+    } else if (strcmp("subject_x400_address", parsed_template->name) == 0) {
-+        ret = expand_san_blob(ctx, SAN_X400_ADDRESS, san_list, expanded);
-+    } else if (strcmp("subject_directory_name", parsed_template->name) == 0) {
-+        ret = expand_san_rdn_list(ctx, SAN_DIRECTORY_NAME, san_list,
-+                                  parsed_template->conversion, expanded);
-+    } else if (strcmp("subject_ediparty_name", parsed_template->name) == 0) {
-+        ret = expand_san_blob(ctx, SAN_EDIPART_NAME, san_list, expanded);
-+    } else if (strcmp("subject_uri", parsed_template->name) == 0) {
-+        ret = expand_san_string(ctx, SAN_URI, san_list,
-+                                parsed_template->attr_name, expanded);
-+    } else if (strcmp("subject_ip_address", parsed_template->name) == 0) {
-+        ret = expand_san_string(ctx, SAN_IP_ADDRESS, san_list,
-+                                parsed_template->attr_name, expanded);
-+    } else if (strcmp("subject_registered_id", parsed_template->name) == 0) {
-+        ret = expand_san_string(ctx, SAN_REGISTERED_ID, san_list,
-+                                parsed_template->attr_name, expanded);
-+    } else if (strcmp("subject_pkinit_principal", parsed_template->name) == 0) {
-+        ret = expand_san_string(ctx, SAN_PKINIT, san_list,
-+                                parsed_template->attr_name, expanded);
-+    } else if (strcmp("subject_nt_principal", parsed_template->name) == 0) {
-+        ret = expand_san_string(ctx, SAN_NT, san_list,
-+                                parsed_template->attr_name, expanded);
-+    } else if (strcmp("subject_principal", parsed_template->name) == 0) {
-+        ret = expand_san_string(ctx, SAN_PRINCIPAL, san_list,
-+                                parsed_template->attr_name, expanded);
-+    } else {
-+        CM_DEBUG(ctx, "Unsupported template name [%s].n",
-+                      parsed_template->name);
-+        ret = EINVAL;
-+    }
-+
-+    return ret;
-+}
-+
-+static int expand_template(struct sss_certmap_ctx *ctx,
-+                           struct parsed_template *parsed_template,
-+                           struct sss_cert_content *cert_content,
-+                           char **expanded)
-+{
-+    int ret;
-+    char *exp = NULL;
-+
-+    if (strcmp("issuer_dn", parsed_template->name) == 0) {
-+        ret = get_dn_str(ctx, parsed_template->conversion,
-+                         cert_content->issuer_rdn_list, &exp);
-+    } else if (strcmp("subject_dn", parsed_template->name) == 0) {
-+        ret = get_dn_str(ctx, parsed_template->conversion,
-+                         cert_content->subject_rdn_list, &exp);
-+    } else if (strncmp("subject_", parsed_template->name, 8) == 0) {
-+        ret = expand_san(ctx, parsed_template, cert_content->san_list, &exp);
-+    } else if (strcmp("cert", parsed_template->name) == 0) {
-+        ret = expand_cert(ctx, parsed_template, cert_content, &exp);
-+    } else {
-+        CM_DEBUG(ctx, "Unsupported template name.");
-+        ret = EINVAL;
-+        goto done;
-+    }
-+    if (ret != 0) {
-+        CM_DEBUG(ctx, "Failed to expand [%s] template.", parsed_template->name);
-+        goto done;
-+    }
-+
-+    if (exp == NULL) {
-+        ret = ENOMEM;
-+        goto done;
-+    }
-+
-+    ret = 0;
-+
-+done:
-+    if (ret == 0) {
-+        *expanded = exp;
-+    } else {
-+        talloc_free(exp);
-+    }
-+
-+    return ret;
-+}
-+
-+static int get_filter(struct sss_certmap_ctx *ctx,
-+                      struct ldap_mapping_rule *parsed_mapping_rule,
-+                      struct sss_cert_content *cert_content,
-+                      char **filter)
-+{
-+    struct ldap_mapping_rule_comp *comp;
-+    char *result = NULL;
-+    char *expanded = NULL;
-+    int ret;
-+
-+    result = talloc_strdup(ctx, "");
-+    if (result == NULL) {
-+        return ENOMEM;
-+    }
-+
-+    for (comp = parsed_mapping_rule->list; comp != NULL; comp = comp->next) {
-+        if (comp->type == comp_string) {
-+            result = talloc_strdup_append(result, comp->val);
-+        } else if (comp->type == comp_template) {
-+            ret = expand_template(ctx, comp->parsed_template, cert_content,
-+                                  &expanded);
-+            if (ret != 0) {
-+                CM_DEBUG(ctx, "Failed to expanded template.");
-+                goto done;
-+            }
-+
-+            result = talloc_strdup_append(result, expanded);
-+            talloc_free(expanded);
-+            expanded = NULL;
-+            if (result == NULL) {
-+                ret = ENOMEM;
-+                goto done;
-+            }
-+        } else {
-+            ret = EINVAL;
-+            CM_DEBUG(ctx, "Unsupported component type.");
-+            goto done;
-+        }
-+    }
-+
-+    ret = 0;
-+done:
-+    talloc_free(expanded);
-+    if (ret == 0) {
-+        *filter = result;
-+    } else {
-+        talloc_free(result);
-+    }
-+
-+    return ret;
-+}
-+
-+static bool check_san_regexp(struct sss_certmap_ctx *ctx,
-+                             enum san_opt san_opt, regex_t regexp,
-+                             struct san_list *san_list)
-+{
-+    struct san_list *item;
-+    bool match = false;
-+    int ret;
-+    char *tmp_str = NULL;
-+
-+    DLIST_FOR_EACH(item, san_list) {
-+        if (item->san_opt == san_opt) {
-+            if (item->san_opt == SAN_DIRECTORY_NAME) {
-+                /* use LDAP order for matching */
-+                ret = get_dn_str(ctx, NULL, item->rdn_list, &tmp_str);
-+                if (ret != 0 || tmp_str == NULL) {
-+                    return false;
-+                }
-+                match = (regexec(&regexp, tmp_str, 0, NULL, 0) == 0);
-+                talloc_free(tmp_str);
-+            } else {
-+                match = (item->val != NULL
-+                            && regexec(&regexp, item->val, 0, NULL, 0) == 0);
-+            }
-+            if (!match) {
-+                return false;
-+            }
-+        }
-+    }
-+
-+    return match;
-+}
-+
-+static bool check_san_blob(enum san_opt san_opt,
-+                           uint8_t *bin_val, size_t bin_val_len,
-+                           struct san_list *san_list)
-+{
-+    struct san_list *item;
-+    bool match = false;
-+
-+    if (bin_val == NULL || bin_val_len == 0) {
-+        return false;
-+    }
-+
-+    DLIST_FOR_EACH(item, san_list) {
-+        if (item->san_opt == san_opt) {
-+            match = (item->bin_val != NULL && item->bin_val_len != 0
-+                        && memmem(item->bin_val, item->bin_val_len,
-+                                  bin_val, bin_val_len) != NULL);
-+            if (!match) {
-+                return false;
-+            }
-+        }
-+    }
-+
-+    return match;
-+}
-+
-+static bool check_san_str_other_name(enum san_opt san_opt,
-+                                     const char *str_other_name_oid,
-+                                     regex_t regexp,
-+                                     struct san_list *san_list)
-+{
-+    struct san_list *item;
-+    bool match = false;
-+    char *tmp_str;
-+
-+    if (str_other_name_oid == NULL) {
-+        return false;
-+    }
-+
-+    DLIST_FOR_EACH(item, san_list) {
-+        if (item->san_opt == san_opt
-+                && strcmp(item->other_name_oid, str_other_name_oid) == 0) {
-+            match = false;
-+            if (item->bin_val != NULL && item->bin_val_len != 0) {
-+                tmp_str = talloc_strndup(item, (char *) item->bin_val,
-+                                         item->bin_val_len);
-+                if (tmp_str != NULL) {
-+                    match = (regexec(&regexp, tmp_str, 0, NULL, 0) == 0);
-+                }
-+                talloc_free(tmp_str);
-+            }
-+            if (!match) {
-+                return false;
-+            }
-+        }
-+    }
-+
-+    return match;
-+}
-+
-+static bool do_san_match(struct sss_certmap_ctx *ctx,
-+                         struct component_list *comp,
-+                         struct san_list *san_list)
-+{
-+    switch (comp->san_opt) {
-+    case SAN_OTHER_NAME:
-+        return check_san_blob(SAN_STRING_OTHER_NAME,
-+                              comp->bin_val, comp->bin_val_len,
-+                              san_list);
-+        break;
-+    case SAN_X400_ADDRESS:
-+    case SAN_EDIPART_NAME:
-+        return check_san_blob(comp->san_opt, comp->bin_val, comp->bin_val_len,
-+                              san_list);
-+        break;
-+    case SAN_RFC822_NAME:
-+    case SAN_DNS_NAME:
-+    case SAN_DIRECTORY_NAME:
-+    case SAN_URI:
-+    case SAN_IP_ADDRESS:
-+    case SAN_REGISTERED_ID:
-+    case SAN_PKINIT:
-+    case SAN_NT:
-+    case SAN_PRINCIPAL:
-+        return check_san_regexp(ctx, comp->san_opt, comp->regexp, san_list);
-+        break;
-+    case SAN_STRING_OTHER_NAME:
-+        return check_san_str_other_name(comp->san_opt, comp->str_other_name_oid,
-+                                        comp->regexp, san_list);
-+        break;
-+    default:
-+        CM_DEBUG(ctx, "Unsupported SAN option [%d].", comp->san_opt);
-+        return false;
-+    }
-+}
-+
-+static int do_match(struct sss_certmap_ctx *ctx,
-+                    struct krb5_match_rule *parsed_match_rule,
-+                    struct sss_cert_content *cert_content)
-+{
-+    struct component_list *comp;
-+    bool match = false;
-+    size_t c;
-+
-+    if (parsed_match_rule == NULL || cert_content == NULL) {
-+        return EINVAL;
-+    }
-+
-+    /* Issuer */
-+    for (comp = parsed_match_rule->issuer; comp != NULL; comp = comp->next) {
-+        match = (cert_content->issuer_str != NULL
-+                    && regexec(&(comp->regexp), cert_content->issuer_str,
-+                               0, NULL, 0) == 0);
-+        if (match && parsed_match_rule->r == relation_or) {
-+            /* match */
-+            return 0;
-+        } else if (!match && parsed_match_rule->r == relation_and) {
-+            /* no match */
-+            return ENOENT;
-+        }
-+
-+    }
-+
-+    /* Subject */
-+    for (comp = parsed_match_rule->subject; comp != NULL; comp = comp->next) {
-+        match = (cert_content->subject_str != NULL
-+                    && regexec(&(comp->regexp), cert_content->subject_str,
-+                               0, NULL, 0) == 0);
-+        if (match && parsed_match_rule->r == relation_or) {
-+            /* match */
-+            return 0;
-+        } else if (!match && parsed_match_rule->r == relation_and) {
-+            /* no match */
-+            return ENOENT;
-+        }
-+
-+    }
-+
-+    /* Key Usage */
-+    for (comp = parsed_match_rule->ku; comp != NULL; comp = comp->next) {
-+        match = ((cert_content->key_usage & comp->ku) == comp->ku);
-+        if (match && parsed_match_rule->r == relation_or) {
-+            /* match */
-+            return 0;
-+        } else if (!match && parsed_match_rule->r == relation_and) {
-+            /* no match */
-+            return ENOENT;
-+        }
-+    }
-+
-+    /* Extended Key Usage */
-+    for (comp = parsed_match_rule->eku; comp != NULL; comp = comp->next) {
-+        for (c = 0; comp->eku_oid_list[c] != NULL; c++) {
-+            match = string_in_list(comp->eku_oid_list[c],
-+                                   discard_const(
-+                                         cert_content->extended_key_usage_oids),
-+                                   true);
-+            if (match && parsed_match_rule->r == relation_or) {
-+                /* match */
-+                return 0;
-+            } else if (!match && parsed_match_rule->r == relation_and) {
-+                /* no match */
-+                return ENOENT;
-+            }
-+        }
-+    }
-+
-+    /* SAN */
-+    for (comp = parsed_match_rule->san; comp != NULL; comp = comp->next) {
-+        match = do_san_match(ctx, comp, cert_content->san_list);
-+        if (match && parsed_match_rule->r == relation_or) {
-+            /* match */
-+            return 0;
-+        } else if (!match && parsed_match_rule->r == relation_and) {
-+            /* no match */
-+            return ENOENT;
-+        }
-+    }
-+
-+    if (match) {
-+        /* match */
-+        return 0;
-+    }
-+
-+    /* no match */
-+    return ENOENT;
-+}
-+
-+int sss_certmap_match_cert(struct sss_certmap_ctx *ctx,
-+                           const uint8_t *der_cert, size_t der_size)
-+{
-+    int ret;
-+    struct match_map_rule *r;
-+    struct priority_list *p;
-+    struct sss_cert_content *cert_content = NULL;
-+
-+    ret = sss_cert_get_content(ctx, der_cert, der_size, &cert_content);
-+    if (ret != 0) {
-+        CM_DEBUG(ctx, "Failed to get certificate content.");
-+        return ret;
-+    }
-+
-+    if (ctx->prio_list == NULL) {
-+        /* Match all certificates if there are no rules applied */
-+        ret = 0;
-+        goto done;
-+    }
-+
-+    for (p = ctx->prio_list; p != NULL; p = p->next) {
-+        for (r = p->rule_list; r != NULL; r = r->next) {
-+            ret = do_match(ctx, r->parsed_match_rule, cert_content);
-+            if (ret == 0) {
-+                /* match */
-+                goto done;
-+            }
-+        }
-+    }
-+
-+    ret = ENOENT;
-+done:
-+    talloc_free(cert_content);
-+
-+    return ret;
-+}
-+
-+int sss_certmap_get_search_filter(struct sss_certmap_ctx *ctx,
-+                                  const uint8_t *der_cert, size_t der_size,
-+                                  char **_filter, char ***_domains)
-+{
-+    int ret;
-+    struct match_map_rule *r;
-+    struct priority_list *p;
-+    struct sss_cert_content *cert_content = NULL;
-+    char *filter = NULL;
-+    char **domains = NULL;
-+    size_t c;
-+
-+    if (_filter == NULL || _domains == NULL) {
-+        return EINVAL;
-+    }
-+
-+    ret = sss_cert_get_content(ctx, der_cert, der_size, &cert_content);
-+    if (ret != 0) {
-+        CM_DEBUG(ctx, "Failed to get certificate content [%d].", ret);
-+        return ret;
-+    }
-+
-+    if (ctx->prio_list == NULL) {
-+        if (ctx->default_mapping_rule == NULL) {
-+            CM_DEBUG(ctx, "No matching or mapping rules available.");
-+            return EINVAL;
-+        }
-+
-+        ret = get_filter(ctx, ctx->default_mapping_rule, cert_content, &filter);
-+        goto done;
-+    }
-+
-+    for (p = ctx->prio_list; p != NULL; p = p->next) {
-+        for (r = p->rule_list; r != NULL; r = r->next) {
-+            ret = do_match(ctx, r->parsed_match_rule, cert_content);
-+            if (ret == 0) {
-+                /* match */
-+                ret = get_filter(ctx, r->parsed_mapping_rule, cert_content,
-+                                 &filter);
-+                if (ret != 0) {
-+                    CM_DEBUG(ctx, "Failed to get filter");
-+                    goto done;
-+                }
-+
-+                if (r->domains != NULL) {
-+                    for (c = 0; r->domains[c] != NULL; c++);
-+                    domains = talloc_zero_array(ctx, char *, c + 1);
-+                    if (domains == NULL) {
-+                        ret = ENOMEM;
-+                        goto done;
-+                    }
-+
-+                    for (c = 0; r->domains[c] != NULL; c++) {
-+                        domains[c] = talloc_strdup(domains, r->domains[c]);
-+                        if (domains[c] == NULL) {
-+                            ret = ENOMEM;
-+                            goto done;
-+                        }
-+                    }
-+                }
-+
-+                ret = 0;
-+                goto done;
-+            }
-+        }
-+    }
-+
-+    ret = ENOENT;
-+
-+done:
-+    talloc_free(cert_content);
-+    if (ret == 0) {
-+        *_filter = filter;
-+        *_domains = domains;
-+    } else {
-+        talloc_free(filter);
-+        talloc_free(domains);
-+    }
-+
-+    return ret;
-+}
-+
-+int sss_certmap_init(TALLOC_CTX *mem_ctx,
-+                     sss_certmap_ext_debug *debug, void *debug_priv,
-+                     struct sss_certmap_ctx **ctx)
-+{
-+    int ret;
-+
-+    if (ctx == NULL) {
-+        return EINVAL;
-+    }
-+
-+    *ctx = talloc_zero(mem_ctx, struct sss_certmap_ctx);
-+    if (*ctx == NULL) {
-+        return ENOMEM;
-+    }
-+
-+    (*ctx)->debug = debug;
-+    (*ctx)->debug_priv = debug_priv;
-+
-+    ret  = parse_mapping_rule(*ctx, DEFAULT_MAP_RULE,
-+                              &((*ctx)->default_mapping_rule));
-+    if (ret != 0) {
-+        CM_DEBUG((*ctx), "Failed to parse default mapping rule.");
-+        talloc_free(*ctx);
-+        *ctx = NULL;
-+        return ret;
-+    }
-+
-+    CM_DEBUG((*ctx), "sss_certmap initialized.");
-+    return EOK;
-+}
-+
-+void sss_certmap_free_ctx(struct sss_certmap_ctx *ctx)
-+{
-+    talloc_free(ctx);
-+}
-+
-+void sss_certmap_free_filter_and_domains(char *filter, char **domains)
-+{
-+    talloc_free(filter);
-+    talloc_free(domains);
-+}
-diff --git a/src/lib/certmap/sss_certmap.doxy.in b/src/lib/certmap/sss_certmap.doxy.in
-new file mode 100644
-index 0000000000000000000000000000000000000000..e8959e2099a0f517c314833e47d410306fb02702
---- /dev/null
-+++ b/src/lib/certmap/sss_certmap.doxy.in
-@@ -0,0 +1,3 @@
-+PROJECT_NAME            = sss_certmap
-+OUTPUT_DIRECTORY        = certmap_doc
-+INPUT                   = @abs_top_srcdir@/src/lib/certmap/sss_certmap.h
-diff --git a/src/lib/certmap/sss_certmap.exports b/src/lib/certmap/sss_certmap.exports
-new file mode 100644
-index 0000000000000000000000000000000000000000..8b5d5366697401649547c09c6b6f3db571c2b518
---- /dev/null
-+++ b/src/lib/certmap/sss_certmap.exports
-@@ -0,0 +1,13 @@
-+SSS_CERTMAP_0.0 {
-+    global:
-+        sss_certmap_init;
-+        sss_certmap_free_ctx;
-+        sss_certmap_err_msg;
-+        sss_certmap_add_rule;
-+        sss_certmap_match_cert;
-+        sss_certmap_get_search_filter;
-+        sss_cert_get_content;
-+        sss_certmap_free_filter_and_domains;
-+    local:
-+        *;
-+};
-diff --git a/src/lib/certmap/sss_certmap.h b/src/lib/certmap/sss_certmap.h
-new file mode 100644
-index 0000000000000000000000000000000000000000..55485cc35e8bd7cf2cb2b0c5a06a7521025e3c43
---- /dev/null
-+++ b/src/lib/certmap/sss_certmap.h
-@@ -0,0 +1,155 @@
-+/*
-+    SSSD
-+
-+    Library for rule based certificate to user mapping
-+
-+    Authors:
-+        Sumit Bose <sbose@redhat.com>
-+
-+    Copyright (C) 2017 Red Hat
-+
-+    This program is free software; you can redistribute it and/or modify
-+    it under the terms of the GNU General Public License as published by
-+    the Free Software Foundation; either version 3 of the License, or
-+    (at your option) any later version.
-+
-+    This program is distributed in the hope that it will be useful,
-+    but WITHOUT ANY WARRANTY; without even the implied warranty of
-+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+    GNU General Public License for more details.
-+
-+    You should have received a copy of the GNU General Public License
-+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
-+*/
-+
-+#ifndef _SSS_CERTMAP_H_
-+#define _SSS_CERTMAP_H_
-+
-+#include <stdlib.h>
-+#include <stdint.h>
-+#include <stdbool.h>
-+#include <sys/types.h>
-+
-+#include <talloc.h>
-+
-+/**
-+ * @defgroup sss_certmap Allow rule-based mapping of certificates to users
-+ * Libsss_certmap provides a mechanism to map X509 certificate to users based
-+ * on rules.
-+ * @{
-+ */
-+
-+/**
-+ * Opaque type for the idmap context
-+ */
-+struct sss_certmap_ctx;
-+
-+/**
-+ * Lowest priority of a rule
-+ */
-+#define SSS_CERTMAP_MIN_PRIO UINT32_MAX
-+
-+/**
-+ * Typedef for external debug callback
-+ */
-+typedef void (sss_certmap_ext_debug)(void *pvt,
-+                                     const char *file, long line,
-+                                     const char *function,
-+                                     const char *format, ...);
-+/**
-+ * @brief Initialize certmap context
-+ *
-+ * @param[in] mem_ctx    Talloc memory context, may be NULL
-+ * @param[in] debug      Callback to handle debug output, may be NULL
-+ * @param[in] debug_priv Private data for debugging callback, may be NULL
-+ * @param[out] ctx       New certmap context
-+ *
-+ * @return
-+ *  - 0:      success
-+ *  - ENOMEM: failed to allocate internal Talloc context
-+ *  - EINVAL: ctx is NULL
-+ */
-+int sss_certmap_init(TALLOC_CTX *mem_ctx,
-+                     sss_certmap_ext_debug *debug, void *debug_priv,
-+                     struct sss_certmap_ctx **ctx);
-+
-+/**
-+ * @brief Free certmap context
-+ *
-+ * @param[in] ctx certmap context previously initialized with
-+ *            @ref sss_certmap_init, may be NULL
-+ */
-+void sss_certmap_free_ctx(struct sss_certmap_ctx *ctx);
-+
-+/**
-+ * @brief Add a rule to the certmap context
-+ *
-+ * @param[in] ctx        certmap context previously initialized with
-+ *                       @ref sss_certmap_init
-+ * @param[in] priority   priority of the rule, 0 is the hightest priority, the
-+ *                       lowest is SSS_CERTMAP_MIN_PRIO
-+ * @param[in] match_rule String with the matching rule
-+ * @param[in] map_rule   String with the mapping rule
-+ * @param[in] domains    NULL-terminated string array with a list of domains
-+ *                       the rule should be valid for, i.e. only this domains
-+ *                       should be searched for matching users
-+ *
-+ * @return
-+ *  - 0:      success
-+ */
-+int sss_certmap_add_rule(struct sss_certmap_ctx *ctx,
-+                         uint32_t priority, const char *match_rule,
-+                         const char *map_rule, const char **domains);
-+
-+/**
-+ * @brief Check if a certificate matches any of the applied rules
-+ *
-+ * @param[in] ctx      certmap context previously initialized with
-+ *                     @ref sss_certmap_init
-+ * @param[in] der_cert binary blog with the DER encoded certificate
-+ * @param[in] der_size size of the certificate blob
-+ *
-+ * @return
-+ *  - 0:      certificate matches a rule
-+ *  - ENOENT: certificate does not match
-+ *  - EINVAL: internal error
-+ */
-+int sss_certmap_match_cert(struct sss_certmap_ctx *ctx,
-+                           const uint8_t *der_cert, size_t der_size);
-+
-+/**
-+ * @brief Get the LDAP filter string for a certificate
-+ *
-+ * @param[in] ctx      certmap context previously initialized with
-+ *                     @ref sss_certmap_init
-+ * @param[in] der_cert binary blog with the DER encoded certificate
-+ * @param[in] der_size size of the certificate blob
-+ * @param[out] filter  LDAP filter string, caller should free the data by
-+ *                     calling sss_certmap_free_filter_and_domains
-+ * @param[out] domains NULL-terminated array of strings with the domains the
-+ *                     rule applies, caller should free the data by calling
-+ *                     sss_certmap_free_filter_and_domains
-+ *
-+ * @return
-+ *  - 0:      certificate matches a rule
-+ *  - ENOENT: certificate does not match
-+ *  - EINVAL: internal error
-+ */
-+int sss_certmap_get_search_filter(struct sss_certmap_ctx *ctx,
-+                                  const uint8_t *der_cert, size_t der_size,
-+                                  char **filter, char ***domains);
-+
-+/**
-+ * @brief Free data returned by @ref sss_certmap_get_search_filter
-+ *
-+ * @param[in] filter  LDAP filter strings returned by
-+ *                    sss_certmap_get_search_filter
-+ * @param[in] domains string array of domains returned by
-+ *                     sss_certmap_get_search_filter
-+ */
-+void sss_certmap_free_filter_and_domains(char *filter, char **domains);
-+
-+/**
-+ * @}
-+ */
-+#endif /* _SSS_CERTMAP_H_ */
-diff --git a/src/lib/certmap/sss_certmap.pc.in b/src/lib/certmap/sss_certmap.pc.in
-new file mode 100644
-index 0000000000000000000000000000000000000000..f1a4432fce8ccd5642a622ca6a8d3a7954fc7ba3
---- /dev/null
-+++ b/src/lib/certmap/sss_certmap.pc.in
-@@ -0,0 +1,11 @@
-+prefix=@prefix@
-+exec_prefix=@exec_prefix@
-+libdir=@libdir@
-+includedir=@includedir@
-+
-+Name: sss_certmap
-+Description: SSS certificate mapping library
-+Version: @VERSION@
-+Libs: -L${libdir} -lsss_certmap
-+Cflags:
-+URL: https://pagure.io/SSSD/sssd/
-diff --git a/src/lib/certmap/sss_certmap_attr_names.c b/src/lib/certmap/sss_certmap_attr_names.c
-new file mode 100644
-index 0000000000000000000000000000000000000000..a28a464910728cdd1f316b2b979da84f440685ea
---- /dev/null
-+++ b/src/lib/certmap/sss_certmap_attr_names.c
-@@ -0,0 +1,107 @@
-+/*
-+    SSSD
-+
-+    Library for rule based certificate to user mapping - Attribute name
-+    mapping for different implementations
-+
-+    Authors:
-+        Sumit Bose <sbose@redhat.com>
-+
-+    Copyright (C) 2017 Red Hat
-+
-+    This program is free software; you can redistribute it and/or modify
-+    it under the terms of the GNU General Public License as published by
-+    the Free Software Foundation; either version 3 of the License, or
-+    (at your option) any later version.
-+
-+    This program is distributed in the hope that it will be useful,
-+    but WITHOUT ANY WARRANTY; without even the implied warranty of
-+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+    GNU General Public License for more details.
-+
-+    You should have received a copy of the GNU General Public License
-+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
-+*/
-+
-+/* NSS data taken from nss-utils:nss/lib/util/secoid.c and
-+ * nss:nss/lib/certdb/alg1485.c */
-+
-+/* AD data taken from
-+ * https://msdn.microsoft.com/en-us/library/windows/desktop/aa376556%28v=vs.85%29.aspx
-+ * and wine source code dlls/crypt32/oid.c  and include/wincrypt.h . */
-+
-+#include <stdbool.h>
-+#include <string.h>
-+#include <talloc.h>
-+
-+struct oid_attr_name_map {
-+    bool nss_ad_differ;
-+    const char *oid;
-+    const char *nss;
-+    const char *ad;
-+} oid_attr_name_map[] = {
-+    { false, "2.5.4.3",                    "CN",                  "CN"},
-+    { true,  "2.5.4.8",                    "ST",                  "S"},
-+    { false, "2.5.4.10",                   "O",                   "O"},
-+    { false, "2.5.4.11",                   "OU",                  "OU"},
-+    { false, "2.5.4.46",                   "dnQualifier",         "dnQualifier"},
-+    { false, "2.5.4.6",                    "C",                   "C"},
-+    { true,  "2.5.4.5",                    "serialNumber",        "SERIALNUMBER"},
-+    { false, "2.5.4.7",                    "L",                   "L"},
-+    { true,  "2.5.4.12",                   "title",               "T"},
-+    { false, "2.5.4.4",                    "SN",                  "SN"},
-+    { true,  "2.5.4.42",                   "givenName",           "G"},
-+    { true,  "2.5.4.43",                   "initials",            "I"},
-+    { true,  "2.5.4.44",                   "generationQualifier", "OID.2.5.4.44"},
-+    { false, "0.9.2342.19200300.100.1.25", "DC",                  "DC"},
-+    { true,  "0.9.2342.19200300.100.1.3",  "MAIL",                "OID,0.9.2342.19200300.100.1.3"},
-+    { true,  "0.9.2342.19200300.100.1.1",  "UID",                 "OID.0.9.2342.19200300.100.1.1"},
-+    { true,  "2.5.4.13",                    "OID.2.5.4.13",       "Description"},
-+    { true,  "2.5.4.16",                   "postalAddress",       "OID.2.5.4.16"},
-+    { true,  "2.5.4.17",                   "postalCode",          "PostalCode"},
-+    { true,  "2.5.4.18",                   "postOfficeBox",       "POBox"},
-+    { true,  "2.5.4.51",                   "houseIdentifier",     "OID.2.5.4.51"},
-+    { false, "1.2.840.113549.1.9.1",       "E",                   "E"},
-+    { false, "2.5.4.9",                    "STREET",              "STREET"},
-+    { true,  "2.5.4.65",                   "pseudonym",           "OID.2.5.4.65"},
-+    { true,  "2.5.4.15",                   "businessCategory",    "OID.2.5.4.15"},
-+    { true,  "2.5.4.41",                   "name",                "OID.2.5.4.41"},
-+
-+    { false, NULL, NULL, NULL}
-+};
-+
-+char *check_ad_attr_name(TALLOC_CTX *mem_ctx, const char *rdn)
-+{
-+    char *p;
-+    size_t c;
-+    size_t len;
-+
-+    if (rdn == NULL) {
-+        return NULL;
-+    }
-+
-+    p = strchr(rdn, '=');
-+    if (p == NULL) {
-+        return NULL;
-+    }
-+
-+    len = p - rdn;
-+    if (len == 0) {
-+        return NULL;
-+    }
-+
-+    for (c = 0; oid_attr_name_map[c].oid != NULL; c++) {
-+        if (!oid_attr_name_map[c].nss_ad_differ) {
-+            continue;
-+        }
-+
-+        if (strlen(oid_attr_name_map[c].nss) != len
-+                    || strncmp(rdn, oid_attr_name_map[c].nss, len) != 0) {
-+            continue;
-+        }
-+
-+        return talloc_asprintf(mem_ctx, "%s%s", oid_attr_name_map[c].ad, p);
-+    }
-+
-+    return NULL;
-+}
-diff --git a/src/lib/certmap/sss_certmap_int.h b/src/lib/certmap/sss_certmap_int.h
-new file mode 100644
-index 0000000000000000000000000000000000000000..28f1c596cfb5e78077b6a8e9baefa88b4900a022
---- /dev/null
-+++ b/src/lib/certmap/sss_certmap_int.h
-@@ -0,0 +1,187 @@
-+/*
-+    SSSD
-+
-+    Library for rule based certificate to user mapping
-+
-+    Authors:
-+        Sumit Bose <sbose@redhat.com>
-+
-+    Copyright (C) 2017 Red Hat
-+
-+    This program is free software; you can redistribute it and/or modify
-+    it under the terms of the GNU General Public License as published by
-+    the Free Software Foundation; either version 3 of the License, or
-+    (at your option) any later version.
-+
-+    This program is distributed in the hope that it will be useful,
-+    but WITHOUT ANY WARRANTY; without even the implied warranty of
-+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+    GNU General Public License for more details.
-+
-+    You should have received a copy of the GNU General Public License
-+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
-+*/
-+
-+#include <sys/types.h>
-+#include <regex.h>
-+
-+#ifndef __SSS_CERTMAP_INT_H__
-+#define __SSS_CERTMAP_INT_H__
-+
-+#define CM_DEBUG(cm_ctx, format, ...) do { \
-+    if (cm_ctx != NULL && cm_ctx->debug != NULL) { \
-+        cm_ctx->debug(cm_ctx->debug_priv, __FILE__, __LINE__, __FUNCTION__, \
-+                      format, ##__VA_ARGS__); \
-+    } \
-+} while (0)
-+
-+#define DEFAULT_MATCH_RULE "<KU>digitalSignature<EKU>clientAuth"
-+#define DEFAULT_MAP_RULE "LDAP:(userCertificate;binary={cert!bin})"
-+
-+enum san_opt {
-+    SAN_OTHER_NAME = 0,
-+    SAN_RFC822_NAME,
-+    SAN_DNS_NAME,
-+    SAN_X400_ADDRESS,
-+    SAN_DIRECTORY_NAME,
-+    SAN_EDIPART_NAME,
-+    SAN_URI,
-+    SAN_IP_ADDRESS,
-+    SAN_REGISTERED_ID,
-+    SAN_PKINIT,
-+    SAN_NT,
-+    SAN_PRINCIPAL,
-+    SAN_STRING_OTHER_NAME,
-+
-+    SAN_END,
-+    SAN_INVALID
-+};
-+
-+/* KRB5 matching rule */
-+enum relation_type {
-+    relation_none = 0,
-+    relation_and,
-+    relation_or
-+};
-+
-+struct component_list {
-+    char *val;
-+    regex_t regexp;
-+    uint32_t ku;
-+    const char **eku_oid_list;
-+    enum san_opt san_opt;
-+    char *str_other_name_oid;
-+    uint8_t *bin_val;
-+    size_t bin_val_len;
-+    struct component_list *prev;
-+    struct component_list *next;
-+};
-+
-+struct krb5_match_rule {
-+    enum relation_type r;
-+    struct component_list *issuer;
-+    struct component_list *subject;
-+    struct component_list *ku;
-+    struct component_list *eku;
-+    struct component_list *san;
-+};
-+
-+enum comp_type {
-+    comp_none = 0,
-+    comp_string,
-+    comp_template
-+};
-+
-+struct parsed_template {
-+    char *name;
-+    char *attr_name;
-+    char *conversion;
-+};
-+
-+struct ldap_mapping_rule_comp {
-+    enum comp_type type;
-+    char *val;
-+    struct parsed_template *parsed_template;
-+    struct ldap_mapping_rule_comp *prev;
-+    struct ldap_mapping_rule_comp *next;
-+};
-+
-+struct ldap_mapping_rule {
-+    struct ldap_mapping_rule_comp *list;
-+};
-+
-+struct match_map_rule {
-+    uint32_t priority;
-+    char *match_rule;
-+    struct krb5_match_rule *parsed_match_rule;
-+    char *map_rule;
-+    struct ldap_mapping_rule *parsed_mapping_rule;
-+    char **domains;
-+    struct match_map_rule *prev;
-+    struct match_map_rule *next;
-+};
-+
-+struct priority_list {
-+    uint32_t priority;
-+    struct match_map_rule *rule_list;
-+    struct priority_list *prev;
-+    struct priority_list *next;
-+};
-+
-+struct sss_certmap_ctx {
-+    struct priority_list *prio_list;
-+    sss_certmap_ext_debug *debug;
-+    void *debug_priv;
-+    struct ldap_mapping_rule *default_mapping_rule;
-+};
-+
-+struct san_list {
-+    enum san_opt san_opt;
-+    char *val;
-+    uint8_t *bin_val;
-+    size_t bin_val_len;
-+    char *other_name_oid;
-+    char *short_name;
-+    const char **rdn_list;
-+    struct san_list *prev;
-+    struct san_list *next;
-+};
-+
-+/* key usage flags, see RFC 3280 section 4.2.1.3 */
-+#define SSS_KU_DIGITAL_SIGNATURE    0x0080
-+#define SSS_KU_NON_REPUDIATION      0x0040
-+#define SSS_KU_KEY_ENCIPHERMENT     0x0020
-+#define SSS_KU_DATA_ENCIPHERMENT    0x0010
-+#define SSS_KU_KEY_AGREEMENT        0x0008
-+#define SSS_KU_KEY_CERT_SIGN        0x0004
-+#define SSS_KU_CRL_SIGN             0x0002
-+#define SSS_KU_ENCIPHER_ONLY        0x0001
-+#define SSS_KU_DECIPHER_ONLY        0x8000
-+
-+struct sss_cert_content {
-+    const char *issuer_str;
-+    const char **issuer_rdn_list;
-+    const char *subject_str;
-+    const char **subject_rdn_list;
-+    uint32_t key_usage;
-+    const char **extended_key_usage_oids;
-+    struct san_list *san_list;
-+
-+    uint8_t *cert_der;
-+    size_t cert_der_size;
-+};
-+
-+int sss_cert_get_content(TALLOC_CTX *mem_ctx,
-+                         const uint8_t *der_blob, size_t der_size,
-+                         struct sss_cert_content **content);
-+
-+char *check_ad_attr_name(TALLOC_CTX *mem_ctx, const char *rdn);
-+
-+int parse_krb5_match_rule(struct sss_certmap_ctx *ctx,
-+                          const char *rule_start,
-+                          struct krb5_match_rule **match_rule);
-+
-+int parse_ldap_mapping_rule(struct sss_certmap_ctx *ctx,
-+                            const char *rule_start,
-+                            struct ldap_mapping_rule **mapping_rule);
-+#endif /* __SSS_CERTMAP_INT_H__ */
-diff --git a/src/lib/certmap/sss_certmap_krb5_match.c b/src/lib/certmap/sss_certmap_krb5_match.c
-new file mode 100644
-index 0000000000000000000000000000000000000000..e40f17b8ace46e61087e0a2fa570a362a84cead2
---- /dev/null
-+++ b/src/lib/certmap/sss_certmap_krb5_match.c
-@@ -0,0 +1,558 @@
-+/*
-+    SSSD
-+
-+    Library for rule based certificate to user mapping - KRB5 matching rules
-+
-+    Authors:
-+        Sumit Bose <sbose@redhat.com>
-+
-+    Copyright (C) 2017 Red Hat
-+
-+    This program is free software; you can redistribute it and/or modify
-+    it under the terms of the GNU General Public License as published by
-+    the Free Software Foundation; either version 3 of the License, or
-+    (at your option) any later version.
-+
-+    This program is distributed in the hope that it will be useful,
-+    but WITHOUT ANY WARRANTY; without even the implied warranty of
-+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+    GNU General Public License for more details.
-+
-+    You should have received a copy of the GNU General Public License
-+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
-+*/
-+
-+#include "util/util.h"
-+#include "util/cert.h"
-+#include "util/crypto/sss_crypto.h"
-+#include "lib/certmap/sss_certmap.h"
-+#include "lib/certmap/sss_certmap_int.h"
-+
-+static bool is_dotted_decimal(const char *s, size_t len)
-+{
-+    size_t c = 0;
-+    bool has_dot = false;
-+
-+    if (s == NULL || !isdigit(s[c++])) {
-+        return false;
-+    }
-+
-+    while ((len == 0 && s[c] != '\0') || (len != 0 && c < len)) {
-+        if (s[c] != '.' && !isdigit(s[c])) {
-+            return false;
-+        }
-+        if (!has_dot && s[c] == '.') {
-+            has_dot = true;
-+        }
-+        c++;
-+    }
-+
-+    return (has_dot && isdigit(s[c - 1]));
-+}
-+
-+static int component_list_destructor(void *data)
-+{
-+    struct component_list *comp = talloc_get_type(data, struct component_list);
-+
-+    if (comp != NULL) {
-+        regfree(&(comp->regexp));
-+    }
-+
-+    return 0;
-+}
-+
-+/*
-+ * The syntax of the MIT Kerberos style matching rules is:
-+ *     [KRB5:][relation-operator]component-rule ...
-+ *
-+ * where:
-+ *
-+ *  relation-operator
-+ *   can be either &&, meaning all component rules must match, or ||,
-+ *   meaning only one component rule must match.  The default is &&.
-+ *
-+ *  component-rule
-+ *   can be one of the following.  Note that there is no punctuation or whitespace between component rules.
-+ *    <SUBJECT>regular-expression
-+ *    <ISSUER>regular-expression
-+ *    <SAN>regular-expression
-+ *    <EKU>extended-key-usage
-+ *    <KU>key-usage
-+ *
-+ *  see man sss-certmap for more details
-+ *
-+ */
-+
-+static int get_comp_value(TALLOC_CTX *mem_ctx,
-+                          struct sss_certmap_ctx *ctx,
-+                          const char **cur,
-+                          struct component_list **_comp)
-+
-+{
-+    struct component_list *comp = NULL;
-+    const char *end;
-+    int ret;
-+
-+    comp = talloc_zero(mem_ctx, struct component_list);
-+    if (comp == NULL) {
-+        ret = ENOMEM;
-+        goto done;
-+    }
-+
-+    talloc_set_destructor((TALLOC_CTX *) comp, component_list_destructor);
-+
-+    end = strchr(*cur, '<');
-+
-+    if (end == NULL) {
-+        comp->val = talloc_strdup(comp, *cur);
-+    } else {
-+        comp->val = talloc_strndup(comp, *cur, end - *cur);
-+    }
-+    if (comp->val == NULL) {
-+        ret = ENOMEM;
-+        goto done;
-+    }
-+    if (*(comp->val) == '\0') {
-+        CM_DEBUG(ctx, "Missing component value.");
-+        ret = EINVAL;
-+        goto done;
-+    }
-+
-+    *cur += strlen(comp->val);
-+    *_comp = comp;
-+    ret = 0;
-+
-+done:
-+    if (ret != 0) {
-+        talloc_free(comp);
-+    }
-+
-+    return ret;
-+}
-+
-+static int parse_krb5_get_eku_value(TALLOC_CTX *mem_ctx,
-+                                    struct sss_certmap_ctx *ctx,
-+                                    const char **cur,
-+                                    struct component_list **_comp)
-+{
-+    struct component_list *comp = NULL;
-+    int ret;
-+    char **eku_list;
-+    size_t c;
-+    size_t k;
-+    const char *o;
-+    size_t e = 0;
-+    int eku_list_size;
-+
-+    struct ext_key_usage {
-+        const char *name;
-+        const char *oid;
-+    } ext_key_usage[] = {
-+        /* RFC 3280 section 4.2.1.13 */
-+        {"serverAuth",      "1.3.6.1.5.5.7.3.1"},
-+        {"clientAuth",      "1.3.6.1.5.5.7.3.2"},
-+        {"codeSigning",     "1.3.6.1.5.5.7.3.3"},
-+        {"emailProtection", "1.3.6.1.5.5.7.3.4"},
-+        {"timeStamping",    "1.3.6.1.5.5.7.3.8"},
-+        {"OCSPSigning",     "1.3.6.1.5.5.7.3.9"},
-+
-+        /* RFC 4556 section 3.2.2 */
-+        {"KPClientAuth",    "1.3.6.1.5.2.3.4"},
-+        {"pkinit",          "1.3.6.1.5.2.3.4"},
-+
-+        /* https://support.microsoft.com/en-us/help/287547/object-ids-associated-with-microsoft-cryptography*/
-+        {"msScLogin",       "1.3.6.1.4.1.311.20.2.2"},
-+
-+        {NULL ,0}
-+    };
-+
-+    ret = get_comp_value(mem_ctx, ctx, cur, &comp);
-+    if (ret != 0) {
-+        CM_DEBUG(ctx, "Failed to parse regexp.");
-+        goto done;
-+    }
-+
-+    ret = split_on_separator(mem_ctx, comp->val, ',', true, true,
-+                             &eku_list, &eku_list_size);
-+    if (ret != 0) {
-+        CM_DEBUG(ctx, "Failed to split list.");
-+        goto done;
-+    }
-+
-+    for (c = 0; eku_list[c] != NULL; c++) {
-+        for (k = 0; ext_key_usage[k].name != NULL; k++) {
-+CM_DEBUG(ctx, "[%s][%s].", eku_list[c], ext_key_usage[k].name);
-+            if (strcasecmp(eku_list[c], ext_key_usage[k].name) == 0) {
-+                if (comp->eku_oid_list == NULL) {
-+                    comp->eku_oid_list = talloc_zero_array(comp, const char *,
-+                                                           eku_list_size + 1);
-+                    if (comp->eku_oid_list == NULL) {
-+                        ret = ENOMEM;
-+                        goto done;
-+                    }
-+                }
-+
-+                comp->eku_oid_list[e] = talloc_strdup(comp->eku_oid_list,
-+                                                      ext_key_usage[k].oid);
-+                if (comp->eku_oid_list[e] == NULL) {
-+                    ret = ENOMEM;
-+                    goto done;
-+                }
-+                e++;
-+                break;
-+            }
-+        }
-+
-+        if (ext_key_usage[k].name == NULL) {
-+            /* check for an dotted-decimal OID */
-+            if (*(eku_list[c]) != '.') {
-+                o = eku_list[c];
-+                if (is_dotted_decimal(o, 0)) {
-+                    /* looks like a OID, only '.' and digits */
-+                    comp->eku_oid_list[e] = talloc_strdup(comp->eku_oid_list,
-+                                                          eku_list[c]);
-+                    if (comp->eku_oid_list[e] == NULL) {
-+                        ret = ENOMEM;
-+                        goto done;
-+                    }
-+                    e++;
-+                    continue;
-+                }
-+            }
-+            CM_DEBUG(ctx, "No matching extended key usage found.");
-+            ret = EINVAL;
-+            goto done;
-+        }
-+    }
-+
-+    ret = 0;
-+
-+done:
-+    if (ret == 0) {
-+        *_comp = comp;
-+    } else {
-+        talloc_free(comp);
-+    }
-+
-+    return ret;
-+}
-+
-+static int parse_krb5_get_ku_value(TALLOC_CTX *mem_ctx,
-+                                   struct sss_certmap_ctx *ctx,
-+                                   const char **cur,
-+                                   struct component_list **_comp)
-+{
-+    struct component_list *comp = NULL;
-+    int ret;
-+    char **ku_list;
-+    size_t c;
-+    size_t k;
-+
-+    struct key_usage {
-+        const char *name;
-+        uint32_t flag;
-+    } key_usage[] = {
-+        {"digitalSignature" , SSS_KU_DIGITAL_SIGNATURE},
-+        {"nonRepudiation"   , SSS_KU_NON_REPUDIATION},
-+        {"keyEncipherment"  , SSS_KU_KEY_ENCIPHERMENT},
-+        {"dataEncipherment" , SSS_KU_DATA_ENCIPHERMENT},
-+        {"keyAgreement"     , SSS_KU_KEY_AGREEMENT},
-+        {"keyCertSign"      , SSS_KU_KEY_CERT_SIGN},
-+        {"cRLSign"          , SSS_KU_CRL_SIGN},
-+        {"encipherOnly"     , SSS_KU_ENCIPHER_ONLY},
-+        {"decipherOnly"     , SSS_KU_DECIPHER_ONLY},
-+        {NULL ,0}
-+    };
-+
-+
-+    ret = get_comp_value(mem_ctx, ctx, cur, &comp);
-+    if (ret != 0) {
-+        CM_DEBUG(ctx, "Failed to get value.");
-+        goto done;
-+    }
-+
-+    ret = split_on_separator(mem_ctx, comp->val, ',', true, true,
-+                             &ku_list, NULL);
-+    if (ret != 0) {
-+        CM_DEBUG(ctx, "Failed to split list.");
-+        goto done;
-+    }
-+
-+    for (c = 0; ku_list[c] != NULL; c++) {
-+        for (k = 0; key_usage[k].name != NULL; k++) {
-+            if (strcasecmp(ku_list[c], key_usage[k].name) == 0) {
-+                comp->ku |= key_usage[k].flag;
-+                break;
-+            }
-+        }
-+
-+        if (key_usage[k].name == NULL) {
-+            /* FIXME: add check for numerical ku */
-+            CM_DEBUG(ctx, "No matching key usage found.");
-+            ret = EINVAL;
-+            goto done;
-+        }
-+    }
-+
-+    ret = 0;
-+
-+done:
-+    if (ret == 0) {
-+        *_comp = comp;
-+    } else {
-+        talloc_free(comp);
-+    }
-+
-+    return ret;
-+}
-+
-+static int parse_krb5_get_component_value(TALLOC_CTX *mem_ctx,
-+                                          struct sss_certmap_ctx *ctx,
-+                                          const char **cur,
-+                                          struct component_list **_comp)
-+{
-+    struct component_list *comp = NULL;
-+    int ret;
-+
-+    ret = get_comp_value(mem_ctx, ctx, cur, &comp);
-+    if (ret != 0) {
-+        CM_DEBUG(ctx, "Failed to parse regexp.");
-+        goto done;
-+    }
-+
-+    ret = regcomp(&(comp->regexp), comp->val, REG_EXTENDED);
-+    if (ret != 0) {
-+        CM_DEBUG(ctx, "Failed to parse regexp.");
-+        goto done;
-+    }
-+
-+    ret = 0;
-+
-+done:
-+    if (ret == 0) {
-+        *_comp = comp;
-+    } else {
-+        talloc_free(comp);
-+    }
-+
-+    return ret;
-+}
-+
-+struct san_name {
-+    const char *name;
-+    enum san_opt san_opt;
-+    bool is_string;
-+} san_names[] = {
-+    /* https://www.ietf.org/rfc/rfc3280.txt section 4.2.1.7 */
-+    {"otherName", SAN_OTHER_NAME, false},
-+    {"rfc822Name", SAN_RFC822_NAME,true},
-+    {"dNSName", SAN_DNS_NAME, true},
-+    {"x400Address", SAN_X400_ADDRESS, false},
-+    {"directoryName", SAN_DIRECTORY_NAME, true},
-+    {"ediPartyName", SAN_EDIPART_NAME, false},
-+    {"uniformResourceIdentifier", SAN_URI, true},
-+    {"iPAddress", SAN_IP_ADDRESS, true},
-+    {"registeredID", SAN_REGISTERED_ID, true},
-+    /* https://www.ietf.org/rfc/rfc4556.txt section 3.2.2 */
-+    {"pkinitSAN", SAN_PKINIT, true},
-+    /* https://support.microsoft.com/en-us/help/287547/object-ids-associated-with-microsoft-cryptography */
-+    {"ntPrincipalName", SAN_NT, true},
-+    /* both previous principal types */
-+    {"Principal", SAN_PRINCIPAL, true},
-+    {"stringOtherName", SAN_STRING_OTHER_NAME, true},
-+    {NULL, SAN_END, false}
-+};
-+
-+static int parse_krb5_get_san_option(TALLOC_CTX *mem_ctx,
-+                                     struct sss_certmap_ctx *ctx,
-+                                     const char **cur,
-+                                     enum san_opt *option,
-+                                     char **str_other_name_oid)
-+{
-+    char *end;
-+    size_t c;
-+    size_t len;
-+
-+    end = strchr(*cur, '>');
-+    if (end == NULL) {
-+        CM_DEBUG(ctx, "Failed to parse SAN option.");
-+        return EINVAL;
-+    }
-+
-+    len = end - *cur;
-+
-+    if (len == 0) {
-+        c= SAN_PRINCIPAL;
-+    } else {
-+        for (c = 0; san_names[c].name != NULL; c++) {
-+            if (strncasecmp(*cur, san_names[c].name, len) == 0) {
-+                break;
-+            }
-+        }
-+        if (san_names[c].name == NULL) {
-+            if (is_dotted_decimal(*cur, len)) {
-+                c = SAN_STRING_OTHER_NAME;
-+                *str_other_name_oid = talloc_strndup(mem_ctx, *cur, len);
-+                if (*str_other_name_oid == NULL) {
-+                    CM_DEBUG(ctx, "talloc_strndup failed.");
-+                    return ENOMEM;
-+                }
-+            } else {
-+                CM_DEBUG(ctx, "Unknown SAN option.");
-+                return EINVAL;
-+            }
-+        }
-+    }
-+
-+    *option = san_names[c].san_opt;
-+    *cur = end + 1;
-+
-+    return 0;
-+}
-+
-+static int parse_krb5_get_san_value(TALLOC_CTX *mem_ctx,
-+                                    struct sss_certmap_ctx *ctx,
-+                                    const char **cur,
-+                                    struct component_list **_comp)
-+{
-+    struct component_list *comp = NULL;
-+    enum san_opt san_opt = SAN_PRINCIPAL;
-+    int ret;
-+    char *str_other_name_oid = NULL;
-+
-+    if (*(*cur - 1) == ':') {
-+        ret = parse_krb5_get_san_option(mem_ctx, ctx, cur, &san_opt,
-+                                        &str_other_name_oid);
-+        if (ret != 0) {
-+            goto done;
-+        }
-+    }
-+
-+    if (san_names[san_opt].is_string) {
-+        ret = parse_krb5_get_component_value(mem_ctx, ctx, cur, &comp);
-+        if (ret != 0) {
-+            goto done;
-+        }
-+    } else {
-+        ret = get_comp_value(mem_ctx, ctx, cur, &comp);
-+        if (ret != 0) {
-+            goto done;
-+        }
-+
-+        if (comp->val != NULL) {
-+            comp->bin_val = sss_base64_decode(comp, comp->val,
-+                                              &comp->bin_val_len);
-+            /* for some reasons the NSS version of sss_base64_decode might
-+             * return a non-NULL value on error but len is still 0, so better
-+             * check both. */
-+            if (comp->bin_val == NULL || comp->bin_val_len == 0) {
-+                CM_DEBUG(ctx, "Base64 decode failed.");
-+                ret = EINVAL;
-+                goto done;
-+            }
-+        }
-+    }
-+    comp->san_opt = san_opt;
-+
-+done:
-+    if (ret == 0) {
-+        comp->str_other_name_oid = talloc_steal(comp, str_other_name_oid);
-+        *_comp = comp;
-+    } else {
-+        talloc_free(comp);
-+        talloc_free(str_other_name_oid);
-+    }
-+
-+    return ret;
-+}
-+
-+int parse_krb5_match_rule(struct sss_certmap_ctx *ctx,
-+                          const char *rule_start,
-+                          struct krb5_match_rule **match_rule)
-+{
-+    const char *cur;
-+    struct krb5_match_rule *rule;
-+    struct component_list *comp;
-+    int ret;
-+
-+    rule = talloc_zero(ctx, struct krb5_match_rule);
-+    if (rule == NULL) {
-+        ret = ENOMEM;
-+        goto done;
-+    }
-+
-+    cur = rule_start;
-+    /* check relation */
-+    if (strncmp(cur, "&&", 2) == 0) {
-+        rule->r = relation_and;
-+        cur += 2;
-+    } else if (strncmp(cur, "||", 2) == 0) {
-+        rule->r = relation_or;
-+        cur += 2;
-+    } else {
-+        rule->r = relation_and;
-+    }
-+
-+    while (*cur != '\0') {
-+        /* new component must start with '<' */
-+        if (*cur != '<') {
-+            CM_DEBUG(ctx, "Invalid KRB5 matching rule.");
-+            ret = EINVAL;
-+            goto done;
-+        }
-+        cur++;
-+
-+        if (strncmp(cur, "ISSUER>", 7) == 0) {
-+            cur += 7;
-+            ret = parse_krb5_get_component_value(rule, ctx, &cur, &comp);
-+            if (ret != 0) {
-+                goto done;
-+            }
-+            DLIST_ADD(rule->issuer, comp);
-+        } else if (strncmp(cur, "SUBJECT>", 8) == 0) {
-+            cur += 8;
-+            ret = parse_krb5_get_component_value(rule, ctx, &cur, &comp);
-+            if (ret != 0) {
-+                goto done;
-+            }
-+            DLIST_ADD(rule->subject, comp);
-+        } else if (strncmp(cur, "KU>", 3) == 0) {
-+            cur += 3;
-+            ret = parse_krb5_get_ku_value(rule, ctx, &cur, &comp);
-+            if (ret != 0) {
-+                goto done;
-+            }
-+            DLIST_ADD(rule->ku, comp);
-+        } else if (strncmp(cur, "EKU>", 4) == 0) {
-+            cur += 4;
-+            ret = parse_krb5_get_eku_value(rule, ctx, &cur, &comp);
-+            if (ret != 0) {
-+                goto done;
-+            }
-+            DLIST_ADD(rule->eku, comp);
-+        } else if (strncmp(cur, "SAN>", 4) == 0
-+                        || strncmp(cur, "SAN:", 4) == 0) {
-+            cur += 4;
-+            ret = parse_krb5_get_san_value(rule, ctx, &cur, &comp);
-+            if (ret != 0) {
-+                goto done;
-+            }
-+            DLIST_ADD(rule->san, comp);
-+        } else {
-+            CM_DEBUG(ctx, "Invalid KRB5 matching rule.");
-+            ret = EINVAL;
-+            goto done;
-+        }
-+    }
-+
-+    ret = 0;
-+
-+done:
-+    if (ret == 0) {
-+        *match_rule = rule;
-+    } else {
-+        talloc_free(rule);
-+    }
-+
-+    return ret;
-+}
-diff --git a/src/lib/certmap/sss_certmap_ldap_mapping.c b/src/lib/certmap/sss_certmap_ldap_mapping.c
-new file mode 100644
-index 0000000000000000000000000000000000000000..c64c05b311f043b4d70f98f718780601c3e6a002
---- /dev/null
-+++ b/src/lib/certmap/sss_certmap_ldap_mapping.c
-@@ -0,0 +1,367 @@
-+/*
-+    SSSD
-+
-+    Library for rule based certificate to user mapping - LDAP mapping rules
-+
-+    Authors:
-+        Sumit Bose <sbose@redhat.com>
-+
-+    Copyright (C) 2017 Red Hat
-+
-+    This program is free software; you can redistribute it and/or modify
-+    it under the terms of the GNU General Public License as published by
-+    the Free Software Foundation; either version 3 of the License, or
-+    (at your option) any later version.
-+
-+    This program is distributed in the hope that it will be useful,
-+    but WITHOUT ANY WARRANTY; without even the implied warranty of
-+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+    GNU General Public License for more details.
-+
-+    You should have received a copy of the GNU General Public License
-+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
-+*/
-+
-+#include "util/util.h"
-+#include "util/cert.h"
-+#include "util/crypto/sss_crypto.h"
-+#include "lib/certmap/sss_certmap.h"
-+#include "lib/certmap/sss_certmap_int.h"
-+struct template_table {
-+    const char *name;
-+    const char **attr_name;
-+    const char **conversion;
-+};
-+
-+const char *empty[] = {NULL};
-+const char *name_attr[] = {"short_name", NULL};
-+const char *x500_conv[] = {"ad_x500",  "ad",  "ad_ldap",
-+                           "nss_x500", "nss", "nss_ldap", NULL};
-+const char *bin_conv[] = {"bin", "base64", NULL};
-+
-+struct template_table template_table[] = {
-+    {"issuer_dn", empty, x500_conv},
-+    {"subject_dn", empty, x500_conv},
-+    {"cert", empty, bin_conv},
-+    {"subject_rfc822_name", name_attr, empty},
-+    {"subject_dns_name", name_attr, empty},
-+    {"subject_x400_address", empty, empty},
-+    {"subject_directory_name", empty, empty},
-+    {"subject_ediparty_name", empty, empty},
-+    {"subject_uri", empty, empty},
-+    {"subject_ip_address", empty, empty},
-+    {"subject_registered_id", empty, empty},
-+    {"subject_pkinit_principal", name_attr, empty},
-+    {"subject_nt_principal", name_attr, empty},
-+    {"subject_principal", name_attr, empty},
-+    {NULL, NULL, NULL}};
-+
-+static int check_parsed_template(struct sss_certmap_ctx *ctx,
-+                                 struct parsed_template *parsed)
-+{
-+    size_t n;
-+    size_t a;
-+    size_t c;
-+    bool attr_name_valid = false;
-+    bool conversion_valid = false;
-+
-+    for (n = 0; template_table[n].name != NULL; n++) {
-+        if (strcmp(template_table[n].name, parsed->name) != 0) {
-+            continue;
-+        }
-+
-+        if (parsed->attr_name != NULL) {
-+            for (a = 0; template_table[n].attr_name[a] != NULL; a++) {
-+                if (strcmp(template_table[n].attr_name[a],
-+                           parsed->attr_name) == 0) {
-+                    attr_name_valid = true;
-+                    break;
-+                }
-+            }
-+        } else {
-+            attr_name_valid = true;
-+        }
-+
-+        if (parsed->conversion != NULL) {
-+            for (c = 0; template_table[n].conversion[c] != NULL; c++) {
-+                if (strcmp(template_table[n].conversion[c],
-+                           parsed->conversion) == 0) {
-+                    conversion_valid = true;
-+                    break;
-+                }
-+            }
-+        } else {
-+            conversion_valid = true;
-+        }
-+
-+        if (attr_name_valid && conversion_valid) {
-+            return 0;
-+        }
-+    }
-+
-+    return EINVAL;
-+}
-+
-+static int parse_template(TALLOC_CTX *mem_ctx, struct sss_certmap_ctx *ctx,
-+                          const char *template,
-+                          struct parsed_template **parsed_template)
-+{
-+    int ret;
-+    struct parsed_template *parsed = NULL;
-+    const char *dot;
-+    const char *excl;
-+    const char *p;
-+
-+    parsed = talloc_zero(mem_ctx, struct parsed_template);
-+    if (parsed == NULL) {
-+        ret = ENOMEM;
-+        goto done;
-+    }
-+
-+    dot = strchr(template, '.');
-+    if (dot != NULL) {
-+        p = strchr(dot + 1, '.');
-+        if (p != NULL) {
-+            CM_DEBUG(ctx, "Only one '.' allowed in template.");
-+            ret = EINVAL;
-+            goto done;
-+        }
-+
-+        if (dot == template) {
-+            CM_DEBUG(ctx, "Missing name in template.");
-+            ret = EINVAL;
-+            goto done;
-+        }
-+    }
-+
-+    excl = strchr(template, '!');
-+    if (excl != NULL) {
-+        p = strchr(excl + 1, '!');
-+        if (p != NULL) {
-+            CM_DEBUG(ctx, "Only one '!' allowed in template.");
-+            ret = EINVAL;
-+            goto done;
-+        }
-+
-+        if (excl == template) {
-+            CM_DEBUG(ctx, "Missing name in template.");
-+            ret = EINVAL;
-+            goto done;
-+        }
-+    }
-+
-+    if (excl != NULL && excl[1] != '\0') {
-+        parsed->conversion = talloc_strdup(parsed, excl + 1);
-+        if (parsed->conversion == NULL) {
-+            CM_DEBUG(ctx, "Memory allocation failed.");
-+            ret = ENOMEM;
-+            goto done;
-+        }
-+    }
-+
-+    if (dot != NULL && dot[1] != '\0' && dot[1] != '!') {
-+        if (excl == NULL) {
-+            parsed->attr_name = talloc_strdup(parsed, dot + 1);
-+        } else {
-+            parsed->attr_name = talloc_strndup(parsed, dot + 1,
-+                                               (excl - dot - 1));
-+        }
-+        if (parsed->attr_name == NULL) {
-+            CM_DEBUG(ctx, "Memory allocation failed.");
-+            ret = ENOMEM;
-+            goto done;
-+        }
-+    }
-+
-+    if (dot != NULL) {
-+        parsed->name = talloc_strndup(parsed, template, (dot - template));
-+    } else if (excl != NULL) {
-+        parsed->name = talloc_strndup(parsed, template, (excl - template));
-+    } else {
-+        parsed->name = talloc_strdup(parsed, template);
-+    }
-+    if (parsed->name == NULL) {
-+        ret = ENOMEM;
-+        goto done;
-+    }
-+
-+    ret = check_parsed_template(ctx, parsed);
-+    if (ret != 0) {
-+        CM_DEBUG(ctx, "Parse template invalid.");
-+        goto done;
-+    }
-+
-+    ret = 0;
-+
-+done:
-+    if (ret == 0) {
-+        *parsed_template = parsed;
-+    } else {
-+        talloc_free(parsed);
-+    }
-+
-+    return ret;
-+}
-+
-+static int add_comp(struct sss_certmap_ctx *ctx, struct ldap_mapping_rule *rule,
-+                    const char *string, enum comp_type type)
-+{
-+    int ret;
-+    struct ldap_mapping_rule_comp *comp;
-+
-+    comp = talloc_zero(rule, struct ldap_mapping_rule_comp);
-+    if (comp == NULL) {
-+        return ENOMEM;
-+    }
-+
-+    comp->type = type;
-+    comp->val = talloc_strdup(comp, string);
-+    if (comp->val == NULL) {
-+        talloc_free(comp);
-+        return ENOMEM;
-+    }
-+
-+    if (type == comp_template) {
-+        ret = parse_template(comp, ctx, string, &comp->parsed_template);
-+        if (ret != 0) {
-+            talloc_free(comp);
-+            return ret;
-+        }
-+    }
-+
-+    DLIST_ADD_END(rule->list, comp, struct ldap_mapping_rule_comp *);
-+
-+    return 0;
-+}
-+
-+static int add_string(struct sss_certmap_ctx *ctx,
-+                      struct ldap_mapping_rule *rule, const char *string)
-+{
-+    return add_comp(ctx, rule, string, comp_string);
-+}
-+
-+static int add_template(struct sss_certmap_ctx *ctx,
-+                        struct ldap_mapping_rule *rule, const char *string)
-+{
-+    return add_comp(ctx, rule, string, comp_template);
-+}
-+
-+int parse_ldap_mapping_rule(struct sss_certmap_ctx *ctx,
-+                            const char *rule_start,
-+                            struct ldap_mapping_rule **mapping_rule)
-+{
-+    size_t c;
-+    const char *cur;
-+    char *tmp_string = NULL;
-+    size_t tmp_string_size;
-+    struct ldap_mapping_rule *rule = NULL;
-+    int ret;
-+    bool in_template = false;
-+
-+    rule = talloc_zero(ctx, struct ldap_mapping_rule);
-+    if (rule == NULL) {
-+        ret = ENOMEM;
-+        goto done;
-+    }
-+
-+    tmp_string_size = strlen(rule_start) + 1;
-+    tmp_string = talloc_zero_size(ctx, tmp_string_size);
-+    if (tmp_string == NULL) {
-+        ret = ENOMEM;
-+        goto done;
-+    }
-+
-+    cur = rule_start;
-+    c = 0;
-+
-+    while (*cur != '\0') {
-+        if (c > tmp_string_size) {
-+            CM_DEBUG(ctx, "Cannot parse mapping rule.");
-+            ret = EIO;
-+            goto done;
-+        }
-+        switch (*cur) {
-+        case '{':
-+            if (in_template) {
-+                CM_DEBUG(ctx, "'{' not allowed in templates.");
-+                ret = EINVAL;
-+                goto done;
-+            }
-+            if (cur[1] == '{') {
-+                /* Add only a single '{' to the output */
-+                tmp_string[c] = '{';
-+                c++;
-+                cur += 2;
-+            } else {
-+                if (c != 0) {
-+                    ret = add_string(ctx, rule, tmp_string);
-+                    if (ret != 0) {
-+                        CM_DEBUG(ctx, "Failed to add string.");
-+                        ret = EINVAL;
-+                        goto done;
-+                    }
-+                    memset(tmp_string, 0, tmp_string_size);
-+                    c = 0;
-+                }
-+                cur++;
-+                in_template = true;
-+            }
-+            break;
-+        case '}':
-+            if (cur[1] == '}') {
-+                if (in_template) {
-+                    CM_DEBUG(ctx, "'}}' not allowed in templates.");
-+                    ret = EINVAL;
-+                    goto done;
-+                } else {
-+                    /* Add only a single '}' to the output */
-+                    tmp_string[c] = '}';
-+                    c++;
-+                    cur += 2;
-+                }
-+            } else {
-+                ret = add_template(ctx, rule, tmp_string);
-+                if (ret != 0) {
-+                    CM_DEBUG(ctx, "Failed to add template.");
-+                    ret = EINVAL;
-+                    goto done;
-+                }
-+                memset(tmp_string, 0, tmp_string_size);
-+                c = 0;
-+                cur++;
-+                in_template = false;
-+            }
-+            break;
-+        default:
-+            tmp_string[c] = *cur;
-+            c++;
-+            cur++;
-+        }
-+    }
-+    if (in_template) {
-+        CM_DEBUG(ctx, "Rule ended inside template.");
-+        ret = EINVAL;
-+        goto done;
-+    }
-+    if (c != 0) {
-+        ret = add_string(ctx, rule, tmp_string);
-+        if (ret != 0) {
-+            CM_DEBUG(ctx, "Failed to add string.");
-+            ret = EINVAL;
-+            goto done;
-+        }
-+    }
-+
-+    ret = 0;
-+
-+done:
-+    if (ret == 0) {
-+        *mapping_rule = rule;
-+    } else {
-+        talloc_free(rule);
-+    }
-+
-+    talloc_free(tmp_string);
-+
-+    return ret;
-+}
-diff --git a/src/man/Makefile.am b/src/man/Makefile.am
-index 215ce693b56e74db394dbc238c03c87f5f6efe99..142d6e2743f814294e3d92c8342070b8230bb3e5 100644
---- a/src/man/Makefile.am
-+++ b/src/man/Makefile.am
-@@ -59,7 +59,7 @@ man_MANS = \
-     sss_useradd.8 sss_userdel.8 sss_usermod.8 \
-     sss_groupadd.8 sss_groupdel.8 sss_groupmod.8 \
-     sssd.8 sssd.conf.5 sssd-ldap.5 \
--    sssd-krb5.5 sssd-simple.5 \
-+    sssd-krb5.5 sssd-simple.5 sss-certmap.5 \
-     sssd_krb5_locator_plugin.8 sss_groupshow.8 \
-     pam_sss.8 sss_obfuscate.8 sss_cache.8 sss_debuglevel.8 sss_seed.8 \
-     sss_override.8 idmap_sss.8 sssctl.8 \
-diff --git a/src/man/po/po4a.cfg b/src/man/po/po4a.cfg
-index ffcf9a2793da3c0115bc846744ccb9592a9a68ef..d1f6ac39f841c61ae3d2393fb3402dc21b9cbd69 100644
---- a/src/man/po/po4a.cfg
-+++ b/src/man/po/po4a.cfg
-@@ -6,6 +6,7 @@
- [type:docbook] pam_sss.8.xml $lang:$(builddir)/$lang/pam_sss.8.xml
- [type:docbook] sssd_krb5_locator_plugin.8.xml $lang:$(builddir)/$lang/sssd_krb5_locator_plugin.8.xml
- [type:docbook] sssd-simple.5.xml $lang:$(builddir)/$lang/sssd-simple.5.xml
-+[type:docbook] sss-certmap.5.xml $lang:$(builddir)/$lang/sss-certmap.5.xml
- [type:docbook] sssd-ipa.5.xml $lang:$(builddir)/$lang/sssd-ipa.5.xml
- [type:docbook] sssd-ad.5.xml $lang:$(builddir)/$lang/sssd-ad.5.xml
- [type:docbook] sssd-sudo.5.xml $lang:$(builddir)/$lang/sssd-sudo.5.xml
-diff --git a/src/man/sss-certmap.5.xml b/src/man/sss-certmap.5.xml
-new file mode 100644
-index 0000000000000000000000000000000000000000..bbe68509f2222613a7ed69599519d7fca0506df0
---- /dev/null
-+++ b/src/man/sss-certmap.5.xml
-@@ -0,0 +1,600 @@
-+<?xml version="1.0" encoding="UTF-8"?>
-+<!DOCTYPE reference PUBLIC "-//OASIS//DTD DocBook V4.4//EN"
-+"http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd">
-+<reference>
-+<title>SSSD Manual pages</title>
-+<refentry>
-+    <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="include/upstream.xml" />
-+
-+    <refmeta>
-+        <refentrytitle>sss-certmap</refentrytitle>
-+        <manvolnum>5</manvolnum>
-+        <refmiscinfo class="manual">File Formats and Conventions</refmiscinfo>
-+    </refmeta>
-+
-+    <refnamediv id='name'>
-+        <refname>sss-certmap</refname>
-+        <refpurpose>SSSD Certificate Matching and Mapping Rules</refpurpose>
-+    </refnamediv>
-+
-+    <refsect1 id='description'>
-+        <title>DESCRIPTION</title>
-+        <para>
-+            The manual page describes the rules which can be used by SSSD and
-+            other components to match X.509 certificates and map them to
-+            accounts.
-+        </para>
-+        <para>
-+            Each rule has four components, a <quote>priority</quote>, a
-+            <quote>matching rule</quote>, a <quote>mapping rule</quote> and a
-+            <quote>domain list</quote>. All components are optional. A missing
-+            <quote>priority</quote> will add the rule with the lowest priority.
-+            The default <quote>matching rule</quote> will match certificates with
-+            the digitalSignature key usage and clientAuth extended key usage. If
-+            the <quote>mapping rule</quote> is empty the certificates will be
-+            searched in the userCertificate attribute as DER encoded binary. If
-+            no domains are given only the local domain will be searched.
-+        </para>
-+    </refsect1>
-+
-+    <refsect1 id='components'>
-+        <title>RULE COMPONENTS</title>
-+    <refsect2 id='priority'>
-+        <title>PRIORITY</title>
-+        <para>
-+            The rules are process by priority while the number '0' (zero)
-+            indicates the highest priority. The higher the number the lower is
-+            the priority. A missing value indicates the lowest priority.
-+        </para>
-+        <para>
-+            Internally the priority is treated as unsigned 32bit integer, using
-+            a priority value larger than 4294967295 will cause an error.
-+        </para>
-+    </refsect2>
-+    <refsect2 id='match'>
-+        <title>MATCHING RULE</title>
-+        <para>
-+            The matching rule is used to select a certificate to which the
-+            mapping rule should be applied. It uses a system similar to the one
-+            used by <quote>pkinit_cert_match</quote> option of MIT Kerberos. It
-+            consists of a keyword enclosed by '&lt;' and '&gt;' which identified
-+            a certain part of the certificate and a pattern which should be
-+            found for the rule to match. Multiple keyword pattern pairs can be
-+            either joined with '&amp;&amp;' (and) or '&#124;&#124;' (or).
-+        </para>
-+        <para>
-+            The available options are:
-+            <variablelist>
-+                <varlistentry>
-+                    <term>&lt;SUBJECT&gt;regular-expression</term>
-+                    <listitem>
-+                    <para>
-+                        With this a part or the whole subject name of the
-+                        certificate can be matched. For the matching POSIX
-+                        Extended Regular Expression syntax is used, see regex(7)
-+                        for details.
-+                    </para>
-+                    <para>
-+                        For the matching the subject name stored in the
-+                        certificate in DER encoded ASN.1 is converted into a
-+                        string according to RFC 4514. This means the most
-+                        specific name component comes first. Please note that
-+                        not all possible attribute names are covered by RFC
-+                        4514. The names included are 'CN', 'L', 'ST', 'O',
-+                        'OU', 'C', 'STREET', 'DC' and 'UID'. Other attribute
-+                        names might be shown differently on different platform
-+                        and by different tools. To avoid confusion those
-+                        attribute names are best not used or covered by a
-+                        suitable regular-expression.
-+                    </para>
-+                    <para>
-+                        Example: &lt;SUBJECT&gt;.*,DC=MY,DC=DOMAIN
-+                    </para>
-+                    </listitem>
-+                </varlistentry>
-+                <varlistentry>
-+                    <term>&lt;ISSUER&gt;regular-expression</term>
-+                    <listitem>
-+                    <para>
-+                        With this a part or the whole issuer name of the
-+                        certificate can be matched. All comments for
-+                        &lt;SUBJECT&gt; apply her as well.
-+                    </para>
-+                    <para>
-+                        Example: &lt;ISSUER&gt;^CN=My-CA,DC=MY,DC=DOMAIN$
-+                    </para>
-+                    </listitem>
-+                </varlistentry>
-+                <varlistentry>
-+                    <term>&lt;KU&gt;key-usage</term>
-+                    <listitem>
-+                    <para>
-+                        This option can be used to specify which key usage
-+                        values the certificate should have. The following value
-+                        can be used in a comma separate list:
-+                        <itemizedlist>
-+                            <listitem><para>digitalSignature</para></listitem>
-+                            <listitem><para>nonRepudiation</para></listitem>
-+                            <listitem><para>keyEncipherment</para></listitem>
-+                            <listitem><para>dataEncipherment</para></listitem>
-+                            <listitem><para>keyAgreement</para></listitem>
-+                            <listitem><para>keyCertSign</para></listitem>
-+                            <listitem><para>cRLSign</para></listitem>
-+                            <listitem><para>encipherOnly</para></listitem>
-+                            <listitem><para>decipherOnly</para></listitem>
-+                        </itemizedlist>
-+                    </para>
-+                    <para>
-+                        A numerical value in the range of a 32bit unsigned
-+                        integer can be used as well to cover special use cases.
-+                    </para>
-+                    <para>
-+                        Example: &lt;KU&gt;digitalSignature,keyEncipherment
-+                    </para>
-+                    </listitem>
-+                </varlistentry>
-+                <varlistentry>
-+                    <term>&lt;EKU&gt;extended-key-usage</term>
-+                    <listitem>
-+                    <para>
-+                        This option can be used to specify which extended key
-+                        usage the certificate should have. The following value
-+                        can be used in a comma separated list:
-+                        <itemizedlist>
-+                            <listitem><para>serverAuth</para></listitem>
-+                            <listitem><para>clientAuth</para></listitem>
-+                            <listitem><para>codeSigning</para></listitem>
-+                            <listitem><para>emailProtection</para></listitem>
-+                            <listitem><para>timeStamping</para></listitem>
-+                            <listitem><para>OCSPSigning</para></listitem>
-+                            <listitem><para>KPClientAuth</para></listitem>
-+                            <listitem><para>pkinit</para></listitem>
-+                            <listitem><para>msScLogin</para></listitem>
-+                        </itemizedlist>
-+                    </para>
-+                    <para>
-+                        Extended key usages which are not listed above can be
-+                        specified with their OID in dotted-decimal notation.
-+                    </para>
-+                    <para>
-+                        Example: &lt;EKU&gt;clientAuth,1.3.6.1.5.2.3.4
-+                    </para>
-+                    </listitem>
-+                </varlistentry>
-+                <varlistentry>
-+                    <term>&lt;SAN&gt;regular-expression</term>
-+                    <listitem>
-+                    <para>
-+                        To be compatible with the usage of MIT Kerberos this
-+                        option will match the Kerberos principals in the PKINIT
-+                        or AD NT Principal SAN as &lt;SAN:Principal&gt; does.
-+                    </para>
-+                    <para>
-+                        Example: &lt;SAN&gt;.*@MY\.REALM
-+                    </para>
-+                    </listitem>
-+                </varlistentry>
-+                <varlistentry>
-+                    <term>&lt;SAN:Principal&gt;regular-expression</term>
-+                    <listitem>
-+                    <para>
-+                        Match the Kerberos principals in the PKINIT or AD NT
-+                        Principal SAN.
-+                    </para>
-+                    <para>
-+                        Example: &lt;SAN:Principal&gt;.*@MY\.REALM
-+                    </para>
-+                    </listitem>
-+                </varlistentry>
-+                <varlistentry>
-+                    <term>&lt;SAN:ntPrincipalName&gt;regular-expression</term>
-+                    <listitem>
-+                    <para>
-+                        Match the Kerberos principals from the AD NT Principal
-+                        SAN.
-+                    </para>
-+                    <para>
-+                        Example: &lt;SAN:ntPrincipalName&gt;.*@MY.AD.REALM
-+                    </para>
-+                    </listitem>
-+                </varlistentry>
-+                <varlistentry>
-+                    <term>&lt;SAN:pkinit&gt;regular-expression</term>
-+                    <listitem>
-+                    <para>
-+                        Match the Kerberos principals from the PKINIT SAN.
-+                    </para>
-+                    <para>
-+                        Example: &lt;SAN:ntPrincipalName&gt;.*@MY\.PKINIT\.REALM
-+                    </para>
-+                    </listitem>
-+                </varlistentry>
-+                <varlistentry>
-+                    <term>&lt;SAN:dotted-decimal-oid&gt;regular-expression</term>
-+                    <listitem>
-+                    <para>
-+                        Take the value of the otherName SAN component given by
-+                        the OID in dotted-decimal notation, interpret it as
-+                        string and try to match it against the regular
-+                        expression.
-+                    </para>
-+                    <para>
-+                        Example: &lt;SAN:1.2.3.4&gt;test
-+                    </para>
-+                    </listitem>
-+                </varlistentry>
-+                <varlistentry>
-+                    <term>&lt;SAN:otherName&gt;base64-string</term>
-+                    <listitem>
-+                    <para>
-+                        Do a binary match with the base64 encoded blob against
-+                        all otherName SAN components. With this option it is
-+                        possible to match against custom otherName components
-+                        with special encodings which could not be treated as
-+                        strings.
-+                    </para>
-+                    <para>
-+                        Example: &lt;SAN:otherName&gt;MTIz
-+                    </para>
-+                    </listitem>
-+                </varlistentry>
-+                <varlistentry>
-+                    <term>&lt;SAN:rfc822Name&gt;regular-expression</term>
-+                    <listitem>
-+                    <para>
-+                        Match the value of the rfc822Name SAN.
-+                    </para>
-+                    <para>
-+                        Example: &lt;SAN:rfc822Name&gt;.*@email\.domain
-+                    </para>
-+                    </listitem>
-+                </varlistentry>
-+                <varlistentry>
-+                    <term>&lt;SAN:dNSName&gt;regular-expression</term>
-+                    <listitem>
-+                    <para>
-+                        Match the value of the dNSName SAN.
-+                    </para>
-+                    <para>
-+                        Example: &lt;SAN:dNSName&gt;.*\.my\.dns\.domain
-+                    </para>
-+                    </listitem>
-+                </varlistentry>
-+                <varlistentry>
-+                    <term>&lt;SAN:x400Address&gt;base64-string</term>
-+                    <listitem>
-+                    <para>
-+                        Binary match the value of the x400Address SAN.
-+                    </para>
-+                    <para>
-+                        Example: &lt;SAN:x400Address&gt;MTIz
-+                    </para>
-+                    </listitem>
-+                </varlistentry>
-+                <varlistentry>
-+                    <term>&lt;SAN:directoryName&gt;regular-expression</term>
-+                    <listitem>
-+                    <para>
-+                        Match the value of the directoryName SAN. The same
-+                        comments as given for &lt;ISSUER&gt; and &lt;SUBJECT&gt;
-+                        apply here as well.
-+                    </para>
-+                    <para>
-+                        Example: &lt;SAN:directoryName&gt;.*,DC=com
-+                    </para>
-+                    </listitem>
-+                </varlistentry>
-+                <varlistentry>
-+                    <term>&lt;SAN:ediPartyName&gt;base64-string</term>
-+                    <listitem>
-+                    <para>
-+                        Binary match the value of the ediPartyName SAN.
-+                    </para>
-+                    <para>
-+                        Example: &lt;SAN:ediPartyName&gt;MTIz
-+                    </para>
-+                    </listitem>
-+                </varlistentry>
-+                <varlistentry>
-+                    <term>&lt;SAN:uniformResourceIdentifier&gt;regular-expression</term>
-+                    <listitem>
-+                    <para>
-+                        Match the value of the uniformResourceIdentifier SAN.
-+                    </para>
-+                    <para>
-+                        Example: &lt;SAN:uniformResourceIdentifier&gt;URN:.*
-+                    </para>
-+                    </listitem>
-+                </varlistentry>
-+                <varlistentry>
-+                    <term>&lt;SAN:iPAddress&gt;regular-expression</term>
-+                    <listitem>
-+                    <para>
-+                        Match the value of the iPAddress SAN.
-+                    </para>
-+                    <para>
-+                        Example: &lt;SAN:iPAddress&gt;192\.168\..*
-+                    </para>
-+                    </listitem>
-+                </varlistentry>
-+                <varlistentry>
-+                    <term>&lt;SAN:registeredID&gt;regular-expression</term>
-+                    <listitem>
-+                    <para>
-+                        Match the value of the registeredID SAN as
-+                        dotted-decimal string.
-+                    </para>
-+                    <para>
-+                        Example: &lt;SAN:registeredID&gt;1\.2\.3\..*
-+                    </para>
-+                    </listitem>
-+                </varlistentry>
-+            </variablelist>
-+        </para>
-+    </refsect2>
-+    <refsect2 id='map'>
-+        <title>MAPPING RULE</title>
-+        <para>
-+            The mapping rule is used to associate a certificate with one or more
-+            accounts. A Smartcard with the certificate and the matching private
-+            key can then be used to authenticate as one of those accounts.
-+        </para>
-+        <para>
-+            Currently SSSD basically only supports LDAP to lookup user
-+            information (the exception is the proxy provider which is not of
-+            relevance here). Because of this the mapping rule is based on LDAP
-+            search filter syntax with templates to add certificate content to
-+            the filter. It is expected that the filter will only contain the
-+            specific data needed for the mapping an that the caller will embed
-+            it in another filter to do the actual search. Because of this the
-+            filter string should start and stop with '(' and ')' respectively.
-+        </para>
-+        <para>
-+            In general it is recommended to use attributes from the certificate
-+            and add them to special attributes to the LDAP user object. E.g. the
-+            'altSecurityIdentities' attribute in AD or the 'ipaCertMapData'
-+            attribute for IPA can be used.
-+        </para>
-+        <para>
-+            This should be preferred to read user specific data from the
-+            certificate like e.g. an email address and search for it in the LDAP
-+            server. The reason is that the user specific data in LDAP might
-+            change for various reasons would would break the mapping. On the
-+            other hand it would be hard to break the mapping on purpose for a
-+            specific user.
-+        </para>
-+        <para>
-+            The templates to add certificate data to the search filter are based
-+            on Python-style formatting strings. They consists of a keyword in
-+            curly braces with an optional sub-component specifier separated by a
-+            '.' or an optional conversion/formatting option separated by a '!'.
-+            Allowed values are:
-+            <variablelist>
-+                <varlistentry>
-+                    <term>{issuer_dn[!((ad|ad_x500)|ad_ldap|nss_x500|(nss|nss_ldap))]}</term>
-+                    <listitem>
-+                    <para>
-+                        This template will add the full issuer DN converted to a
-+                        string according to RFC 4514. If X.500 ordering (most
-+                        specific RDN comes last) an option with the '_x500'
-+                        prefix should be used.
-+                    </para>
-+                    <para>
-+                        The conversion options starting with 'ad_' will use
-+                        attribute names as used by AD, e.g. 'S' instead of 'ST'.
-+                    </para>
-+                    <para>
-+                        The conversion options starting with 'nss_' will use
-+                        attribute names as used by NSS.
-+                    </para>
-+                    <para>
-+                        The default conversion option is 'nss', i.e. attribute
-+                        names according to NSS and LDAP/RFC 4514 ordering.
-+                    </para>
-+                    <para>
-+                        Example: (ipacertmapdata=X509:&lt;I&gt;{issuer_dn!ad}&lt;S&gt;{subject_dn!ad})
-+                    </para>
-+                    </listitem>
-+                </varlistentry>
-+                <varlistentry>
-+                    <term>{subject_dn[!((ad|ad_x500)|ad_ldap|nss_x500|(nss|nss_ldap))]}</term>
-+                    <listitem>
-+                    <para>
-+                        This template will add the full subject DN converted to
-+                        string according to RFC 4514. If X.500 ordering (most
-+                        specific RDN comes last) an option with the '_x500'
-+                        prefix should be used.
-+                    </para>
-+                    <para>
-+                        The conversion options starting with 'ad_' will use
-+                        attribute names as used by AD, e.g. 'S' instead of 'ST'.
-+                    </para>
-+                    <para>
-+                        The conversion options starting with 'nss_' will use
-+                        attribute names as used by NSS.
-+                    </para>
-+                    <para>
-+                        The default conversion option is 'nss', i.e. attribute
-+                        names according to NSS and LDAP/RFC 4514 ordering.
-+                    </para>
-+                    <para>
-+                        Example: (ipacertmapdata=X509:&lt;I&gt;{issuer_dn!nss_x500}&lt;S&gt;{subject_dn!nss_x500})
-+                    </para>
-+                    </listitem>
-+                </varlistentry>
-+                <varlistentry>
-+                    <term>{cert[!(bin|base64)]}</term>
-+                    <listitem>
-+                    <para>
-+                        This template will add the whole DER encoded certificate
-+                        as a string to the search filter. Depending on the
-+                        conversion option the binary certificate is either
-+                        converted to an escaped hex sequence '\xx' or base64.
-+                        The escaped hex sequence is the default and can e.g. be
-+                        used with the LDAP attribute 'userCertificate;binary'.
-+                    </para>
-+                    <para>
-+                        Example: (userCertificate;binary={cert!bin})
-+                    </para>
-+                    </listitem>
-+                </varlistentry>
-+                <varlistentry>
-+                    <term>{subject_principal[.short_name]}</term>
-+                    <listitem>
-+                    <para>
-+                        This template will add the Kerberos principal which is
-+                        taken either from the SAN used by pkinit or the one used
-+                        by AD. The 'short_name' component represent the first
-+                        part of the principal before the '@' sign.
-+                    </para>
-+                    <para>
-+                        Example: (|(userPrincipal={subject_principal})(samAccountName={subject_principal.short_name}))
-+                    </para>
-+                    </listitem>
-+                </varlistentry>
-+                <varlistentry>
-+                    <term>{subject_pkinit_principal[.short_name]}</term>
-+                    <listitem>
-+                    <para>
-+                        This template will add the Kerberos principal which is
-+                        given by then SAN used by pkinit. The 'short_name'
-+                        component represent the first part of the principal
-+                        before the '@' sign.
-+                    </para>
-+                    <para>
-+                        Example: (|(userPrincipal={subject_pkinit_principal})(uid={subject_pkinit_principal.short_name}))
-+                    </para>
-+                    </listitem>
-+                </varlistentry>
-+                <varlistentry>
-+                    <term>{subject_nt_principal[.short_name]}</term>
-+                    <listitem>
-+                    <para>
-+                        This template will add the Kerberos principal which is
-+                        given by then SAN used by AD. The 'short_name' component
-+                        represent the first part of the principal before the '@'
-+                        sign.
-+                    </para>
-+                    <para>
-+                        Example: (|(userPrincipal={subject_principal})(samAccountName={subject_principal.short_name}))
-+                    </para>
-+                    </listitem>
-+                </varlistentry>
-+                <varlistentry>
-+                    <term>{subject_rfc822_name[.short_name]}</term>
-+                    <listitem>
-+                    <para>
-+                        This template will add the string which is stored in the
-+                        rfc822Name component of the SAN, typically an email
-+                        address. The 'short_name' component represent the first
-+                        part of the address before the '@' sign.
-+                    </para>
-+                    <para>
-+                        Example: (|(mail={subject_rfc822_name})(uid={subject_rfc822_name.short_name}))
-+                    </para>
-+                    </listitem>
-+                </varlistentry>
-+                <varlistentry>
-+                    <term>{subject_dns_name[.short_name]}</term>
-+                    <listitem>
-+                    <para>
-+                        This template will add the string which is stored in the
-+                        dNSName component of the SAN, typically a fully-qualified host name.
-+                        The 'short_name' component represent the first
-+                        part of the name before the first '.' sign.
-+                    </para>
-+                    <para>
-+                        Example: (|(fqdn={subject_dns_name})(host={subject_dns_name.short_name}))
-+                    </para>
-+                    </listitem>
-+                </varlistentry>
-+                <varlistentry>
-+                    <term>{subject_uri}</term>
-+                    <listitem>
-+                    <para>
-+                        This template will add the string which is stored in the
-+                        uniformResourceIdentifier component of the SAN.
-+                    </para>
-+                    <para>
-+                        Example: (uri={subject_uri})
-+                    </para>
-+                    </listitem>
-+                </varlistentry>
-+                <varlistentry>
-+                    <term>{subject_ip_address}</term>
-+                    <listitem>
-+                    <para>
-+                        This template will add the string which is stored in the
-+                        iPAddress component of the SAN.
-+                    </para>
-+                    <para>
-+                        Example: (ip={subject_ip_address})
-+                    </para>
-+                    </listitem>
-+                </varlistentry>
-+                <varlistentry>
-+                    <term>{subject_x400_address}</term>
-+                    <listitem>
-+                    <para>
-+                        This template will add the value which is stored in the
-+                        x400Address component of the SAN as escaped hex
-+                        sequence.
-+                    </para>
-+                    <para>
-+                        Example: (attr:binary={subject_x400_address})
-+                    </para>
-+                    </listitem>
-+                </varlistentry>
-+                <varlistentry>
-+                    <term>{subject_directory_name[!((ad|ad_x500)|ad_ldap|nss_x500|(nss|nss_ldap))]}</term>
-+                    <listitem>
-+                    <para>
-+                        This template will add the DN string of the value which
-+                        is stored in the directoryName component of the SAN.
-+                    </para>
-+                    <para>
-+                        Example: (orig_dn={subject_directory_name})
-+                    </para>
-+                    </listitem>
-+                </varlistentry>
-+                <varlistentry>
-+                    <term>{subject_ediparty_name}</term>
-+                    <listitem>
-+                    <para>
-+                        This template will add the value which is stored in the
-+                        ediPartyName component of the SAN as escaped hex
-+                        sequence.
-+                    </para>
-+                    <para>
-+                        Example: (attr:binary={subject_ediparty_name})
-+                    </para>
-+                    </listitem>
-+                </varlistentry>
-+                <varlistentry>
-+                    <term>{subject_registered_id}</term>
-+                    <listitem>
-+                    <para>
-+                        This template will add the OID which is stored in the
-+                        registeredID component of the SAN as as dotted-decimal
-+                        string.
-+                    </para>
-+                    <para>
-+                        Example: (oid={subject_registered_id})
-+                    </para>
-+                    </listitem>
-+                </varlistentry>
-+            </variablelist>
-+        </para>
-+    </refsect2>
-+    <refsect2 id='domains'>
-+        <title>DOMAIN LIST</title>
-+        <para>
-+            If the domain list is not empty users mapped to a given certificate
-+            are not only searched in the local domain but in the listed domains
-+            as well as long as they are know by SSSD. Domains not know to SSSD
-+            will be ignored.
-+        </para>
-+    </refsect2>
-+    </refsect1>
-+</refentry>
-+</reference>
-diff --git a/src/tests/cmocka/test_certmap.c b/src/tests/cmocka/test_certmap.c
-new file mode 100644
-index 0000000000000000000000000000000000000000..c998443d086eaa72cc2a05c38ddfc5ba590a1ce7
---- /dev/null
-+++ b/src/tests/cmocka/test_certmap.c
-@@ -0,0 +1,1443 @@
-+/*
-+    SSSD
-+
-+    certmap - Tests for SSSD's certificate mapping library
-+
-+    Authors:
-+        Sumit Bose <sbose@redhat.com>
-+
-+    Copyright (C) 2017 Red Hat
-+
-+    This program is free software; you can redistribute it and/or modify
-+    it under the terms of the GNU General Public License as published by
-+    the Free Software Foundation; either version 3 of the License, or
-+    (at your option) any later version.
-+
-+    This program is distributed in the hope that it will be useful,
-+    but WITHOUT ANY WARRANTY; without even the implied warranty of
-+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+    GNU General Public License for more details.
-+
-+    You should have received a copy of the GNU General Public License
-+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
-+*/
-+
-+#include <stdarg.h>
-+#include <stddef.h>
-+#include <setjmp.h>
-+#include <cmocka.h>
-+#include <popt.h>
-+
-+#include "lib/certmap/sss_certmap.h"
-+#include "lib/certmap/sss_certmap_int.h"
-+
-+#include "util/crypto/sss_crypto.h"
-+
-+#include "tests/cmocka/common_mock.h"
-+#include "tests/common.h"
-+
-+#ifdef HAVE_NSS
-+#include "util/crypto/nss/nss_util.h"
-+#endif
-+
-+struct priv_sss_debug {
-+    int level;
-+};
-+
-+void ext_debug(void *private, const char *file, long line, const char *function,
-+               const char *format, ...)
-+{
-+    va_list ap;
-+    struct priv_sss_debug *data = private;
-+    int level = SSSDBG_OP_FAILURE;
-+
-+    if (data != NULL) {
-+        level = data->level;
-+    }
-+
-+    if (DEBUG_IS_SET(level)) {
-+        va_start(ap, format);
-+        sss_vdebug_fn(file, line, function, level, APPEND_LINE_FEED,
-+                      format, ap);
-+        va_end(ap);
-+    }
-+}
-+
-+static void test_sss_certmap_init(void **state)
-+{
-+    int ret;
-+    struct sss_certmap_ctx *ctx;
-+
-+    ret = sss_certmap_init(NULL, ext_debug, NULL, NULL);
-+    assert_int_equal(ret, EINVAL);
-+
-+    ret = sss_certmap_init(NULL, ext_debug, NULL, &ctx);
-+    assert_int_equal(ret, EOK);
-+
-+    sss_certmap_free_ctx(ctx);
-+}
-+
-+static struct sss_certmap_ctx *setup_prio(const int *l)
-+{
-+    int ret;
-+    size_t c;
-+    struct sss_certmap_ctx *ctx;
-+
-+    ret = sss_certmap_init(NULL, ext_debug, NULL, &ctx);
-+    assert_int_equal(ret, EOK);
-+    assert_non_null(ctx);
-+
-+    for (c = 0; c < 10; c++) {
-+        ret = sss_certmap_add_rule(ctx, l[c], NULL, NULL, NULL);
-+        assert_int_equal(ret, EOK);
-+    }
-+
-+    return ctx;
-+}
-+
-+static void test_sss_certmap_add_rule(void **state)
-+{
-+    struct sss_certmap_ctx *ctx;
-+    int i;
-+    struct priority_list *p;
-+    struct priority_list *last;
-+    size_t c;
-+
-+    const int tests_a[][10] = {{0, 1, 2, 3, 4, 5, 6, 7, 8, 9},
-+                               {9, 8, 7, 6, 5, 4, 3, 2, 1, 0},
-+                               {1, 3, 5 ,7, 9, 0, 2, 4, 6, 8},
-+                               {0, 2, 4, 6, 8, 1, 3, 5, 7, 9},
-+                               {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}};
-+
-+    const int tests_b[][10] = {{0, 0, 0, 0, 1, 1, 1, 2, 2, 2},
-+                               {2, 2, 2, 1, 1, 1, 0, 0, 0, 0},
-+                               {0, 1, 2, 0, 1, 2, 0, 1, 2, 0},
-+                               {0, 2, 1, 0, 2, 1, 0, 2, 1, 0},
-+                               {0, 1, 2, 0, 2, 1, 0, 0, 1, 2},
-+                               {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}};
-+
-+    for (c = 0; tests_a[c][0] != 0 || tests_a[c][9] != 0; c++) {
-+        ctx = setup_prio(tests_a[0]);
-+        assert_non_null(ctx);
-+        i = 0;
-+        for (p = ctx->prio_list; p != NULL; p = p->next) {
-+            assert_int_equal(i, p->priority);
-+            assert_non_null(p->rule_list);
-+            assert_int_equal(i, p->rule_list->priority);
-+            assert_null(p->rule_list->prev);
-+            assert_null(p->rule_list->next);
-+            i++;
-+        }
-+
-+        i = 9;
-+        for (last = ctx->prio_list; last->next != NULL; last = last->next);
-+        for (p = last; p != NULL; p = p->prev) {
-+            assert_int_equal(i, p->priority);
-+            assert_int_equal(i, p->rule_list->priority);
-+            i--;
-+        }
-+
-+        sss_certmap_free_ctx(ctx);
-+    }
-+    for (c = 0; tests_b[c][0] != 0 || tests_b[c][9] != 0; c++) {
-+        ctx = setup_prio(tests_b[0]);
-+        assert_non_null(ctx);
-+        i = 0;
-+        for (p = ctx->prio_list; p != NULL; p = p->next) {
-+            assert_int_equal(i, p->priority);
-+            assert_non_null(p->rule_list);
-+            assert_int_equal(i, p->rule_list->priority);
-+            assert_null(p->rule_list->prev);
-+            assert_non_null(p->rule_list->next);
-+            assert_ptr_equal(p->rule_list, p->rule_list->next->prev);
-+            assert_non_null(p->rule_list->next->next);
-+            assert_ptr_equal(p->rule_list->next,
-+                             p->rule_list->next->next->prev);
-+            if (i == 0) {
-+                assert_non_null(p->rule_list->next->next->next);
-+                assert_ptr_equal(p->rule_list->next->next,
-+                                 p->rule_list->next->next->next->prev);
-+                assert_null(p->rule_list->next->next->next->next);
-+            } else {
-+                assert_null(p->rule_list->next->next->next);
-+            }
-+            i++;
-+        }
-+        sss_certmap_free_ctx(ctx);
-+    }
-+}
-+
-+static void test_sss_certmap_add_matching_rule(void **state)
-+{
-+    struct sss_certmap_ctx *ctx;
-+    int ret;
-+
-+    ret = sss_certmap_init(NULL, ext_debug, NULL, &ctx);
-+    assert_int_equal(ret, EOK);
-+    assert_non_null(ctx);
-+    assert_null(ctx->prio_list);
-+
-+    ret = sss_certmap_add_rule(ctx, 1, "fsdf", NULL, NULL);
-+    assert_int_equal(ret, EINVAL);
-+    assert_null(ctx->prio_list);
-+
-+    ret = sss_certmap_add_rule(ctx, 1, "FDSF:fsdf", NULL, NULL);
-+    assert_int_equal(ret, ESRCH);
-+    assert_null(ctx->prio_list);
-+
-+    ret = sss_certmap_add_rule(ctx, 1, "<rgerge>", NULL, NULL);
-+    assert_int_equal(ret, EINVAL);
-+    assert_null(ctx->prio_list);
-+
-+    ret = sss_certmap_add_rule(ctx, 1, "KRB5:<rgerge>", NULL, NULL);
-+    assert_int_equal(ret, EINVAL);
-+    assert_null(ctx->prio_list);
-+
-+    ret = sss_certmap_add_rule(ctx, 1, "<ISSUER>", NULL, NULL);
-+    assert_int_equal(ret, EINVAL);
-+    assert_null(ctx->prio_list);
-+
-+    ret = sss_certmap_add_rule(ctx, 1, "<SUBJECT>", NULL, NULL);
-+    assert_int_equal(ret, EINVAL);
-+    assert_null(ctx->prio_list);
-+
-+    ret = sss_certmap_add_rule(ctx, 1, "<KU>", NULL, NULL);
-+    assert_int_equal(ret, EINVAL);
-+    assert_null(ctx->prio_list);
-+
-+    ret = sss_certmap_add_rule(ctx, 1, "<KU>ddqwdq", NULL, NULL);
-+    assert_int_equal(ret, EINVAL);
-+    assert_null(ctx->prio_list);
-+
-+    ret = sss_certmap_add_rule(ctx, 1, "<KU>digitalSignature,dddq", NULL, NULL);
-+    assert_int_equal(ret, EINVAL);
-+    assert_null(ctx->prio_list);
-+
-+
-+    ret = sss_certmap_add_rule(ctx, 1, "<EKU>", NULL, NULL);
-+    assert_int_equal(ret, EINVAL);
-+    assert_null(ctx->prio_list);
-+
-+    ret = sss_certmap_add_rule(ctx, 1, "<EKU>dwqwqw", NULL, NULL);
-+    assert_int_equal(ret, EINVAL);
-+    assert_null(ctx->prio_list);
-+
-+    ret = sss_certmap_add_rule(ctx, 1, "<EKU>.", NULL, NULL);
-+    assert_int_equal(ret, EINVAL);
-+    assert_null(ctx->prio_list);
-+
-+    ret = sss_certmap_add_rule(ctx, 1, "<EKU>.1.2.3", NULL, NULL);
-+    assert_int_equal(ret, EINVAL);
-+    assert_null(ctx->prio_list);
-+
-+    ret = sss_certmap_add_rule(ctx, 1, "<EKU>1.2.3.", NULL, NULL);
-+    assert_int_equal(ret, EINVAL);
-+    assert_null(ctx->prio_list);
-+
-+    ret = sss_certmap_add_rule(ctx, 1, "<EKU>1.a.3", NULL, NULL);
-+    assert_int_equal(ret, EINVAL);
-+    assert_null(ctx->prio_list);
-+
-+    ret = sss_certmap_add_rule(ctx, 1, "<SAN:fwfwef>", NULL, NULL);
-+    assert_int_equal(ret, EINVAL);
-+    assert_null(ctx->prio_list);
-+
-+    ret = sss_certmap_add_rule(ctx, 1, "<SAN:rfc822Name", NULL, NULL);
-+    assert_int_equal(ret, EINVAL);
-+    assert_null(ctx->prio_list);
-+
-+    /* invalid base64 input */
-+    ret = sss_certmap_add_rule(ctx, 1, "<SAN:ediPartyName>...", NULL, NULL);
-+    assert_int_equal(ret, EINVAL);
-+    assert_null(ctx->prio_list);
-+
-+    /* invalid OID input */
-+    ret = sss_certmap_add_rule(ctx, 1, "<SAN:.>dqq", NULL, NULL);
-+    assert_int_equal(ret, EINVAL);
-+    assert_null(ctx->prio_list);
-+
-+    ret = sss_certmap_add_rule(ctx, 1, "<SAN:.1>dqq", NULL, NULL);
-+    assert_int_equal(ret, EINVAL);
-+    assert_null(ctx->prio_list);
-+
-+    ret = sss_certmap_add_rule(ctx, 1, "<SAN:1.>dqq", NULL, NULL);
-+    assert_int_equal(ret, EINVAL);
-+    assert_null(ctx->prio_list);
-+
-+    ret = sss_certmap_add_rule(ctx, 1, "<SAN:11>dqq", NULL, NULL);
-+    assert_int_equal(ret, EINVAL);
-+    assert_null(ctx->prio_list);
-+
-+    ret = sss_certmap_add_rule(ctx, 1, "<ISSUER>a", NULL, NULL);
-+    assert_int_equal(ret, 0);
-+    assert_non_null(ctx->prio_list);
-+    assert_non_null(ctx->prio_list->rule_list);
-+    assert_non_null(ctx->prio_list->rule_list->parsed_match_rule);
-+    assert_int_equal(ctx->prio_list->rule_list->parsed_match_rule->r,
-+                     relation_and);
-+    assert_null(ctx->prio_list->rule_list->parsed_match_rule->subject);
-+    assert_non_null(ctx->prio_list->rule_list->parsed_match_rule->issuer);
-+    assert_string_equal("a",
-+                     ctx->prio_list->rule_list->parsed_match_rule->issuer->val);
-+    talloc_free(ctx);
-+
-+    ret = sss_certmap_init(NULL, ext_debug, NULL, &ctx);
-+    assert_int_equal(ret, EOK);
-+    assert_non_null(ctx);
-+    assert_null(ctx->prio_list);
-+    ret = sss_certmap_add_rule(ctx, 1, "&&<ISSUER>a", NULL, NULL);
-+    assert_int_equal(ret, 0);
-+    assert_non_null(ctx->prio_list);
-+    assert_non_null(ctx->prio_list->rule_list);
-+    assert_non_null(ctx->prio_list->rule_list->parsed_match_rule);
-+    assert_int_equal(ctx->prio_list->rule_list->parsed_match_rule->r,
-+                     relation_and);
-+    assert_null(ctx->prio_list->rule_list->parsed_match_rule->subject);
-+    assert_non_null(ctx->prio_list->rule_list->parsed_match_rule->issuer);
-+    assert_string_equal("a",
-+                     ctx->prio_list->rule_list->parsed_match_rule->issuer->val);
-+    talloc_free(ctx);
-+
-+    ret = sss_certmap_init(NULL, ext_debug, NULL, &ctx);
-+    assert_int_equal(ret, EOK);
-+    assert_non_null(ctx);
-+    assert_null(ctx->prio_list);
-+    ret = sss_certmap_add_rule(ctx, 1, "KRB5:||<ISSUER>a", NULL, NULL);
-+    assert_int_equal(ret, 0);
-+    assert_non_null(ctx->prio_list);
-+    assert_non_null(ctx->prio_list->rule_list);
-+    assert_non_null(ctx->prio_list->rule_list->parsed_match_rule);
-+    assert_int_equal(ctx->prio_list->rule_list->parsed_match_rule->r,
-+                     relation_or);
-+    assert_null(ctx->prio_list->rule_list->parsed_match_rule->subject);
-+    assert_non_null(ctx->prio_list->rule_list->parsed_match_rule->issuer);
-+    assert_string_equal("a",
-+                     ctx->prio_list->rule_list->parsed_match_rule->issuer->val);
-+    talloc_free(ctx);
-+
-+    ret = sss_certmap_init(NULL, ext_debug, NULL, &ctx);
-+    assert_int_equal(ret, EOK);
-+    assert_non_null(ctx);
-+    assert_null(ctx->prio_list);
-+    ret = sss_certmap_add_rule(ctx, 1, "KRB5:<ISSUER>a<SUBJECT>b", NULL, NULL);
-+    assert_int_equal(ret, 0);
-+    assert_non_null(ctx->prio_list);
-+    assert_non_null(ctx->prio_list->rule_list);
-+    assert_non_null(ctx->prio_list->rule_list->parsed_match_rule);
-+    assert_int_equal(ctx->prio_list->rule_list->parsed_match_rule->r,
-+                     relation_and);
-+    assert_non_null(ctx->prio_list->rule_list->parsed_match_rule->subject);
-+    assert_string_equal("b",
-+                    ctx->prio_list->rule_list->parsed_match_rule->subject->val);
-+    assert_non_null(ctx->prio_list->rule_list->parsed_match_rule->issuer);
-+    assert_string_equal("a",
-+                     ctx->prio_list->rule_list->parsed_match_rule->issuer->val);
-+    talloc_free(ctx);
-+
-+    ret = sss_certmap_init(NULL, ext_debug, NULL, &ctx);
-+    assert_int_equal(ret, EOK);
-+    assert_non_null(ctx);
-+    assert_null(ctx->prio_list);
-+    ret = sss_certmap_add_rule(ctx, 1000,
-+                               "KRB5:<ISSUER>a<SUBJECT>b<ISSUER>c<SUBJECT>d",
-+                               NULL, NULL);
-+    assert_int_equal(ret, 0);
-+    assert_non_null(ctx->prio_list);
-+    assert_non_null(ctx->prio_list->rule_list);
-+    assert_non_null(ctx->prio_list->rule_list->parsed_match_rule);
-+    assert_int_equal(ctx->prio_list->rule_list->parsed_match_rule->r,
-+                     relation_and);
-+    assert_non_null(ctx->prio_list->rule_list->parsed_match_rule->subject);
-+    assert_string_equal("d",
-+                    ctx->prio_list->rule_list->parsed_match_rule->subject->val);
-+    assert_string_equal("b",
-+              ctx->prio_list->rule_list->parsed_match_rule->subject->next->val);
-+    assert_non_null(ctx->prio_list->rule_list->parsed_match_rule->issuer);
-+    assert_string_equal("c",
-+                     ctx->prio_list->rule_list->parsed_match_rule->issuer->val);
-+    assert_string_equal("a",
-+               ctx->prio_list->rule_list->parsed_match_rule->issuer->next->val);
-+
-+    ret = sss_certmap_add_rule(ctx, 99,
-+                               "KRB5:<ISSUER>a<SUBJECT>b"
-+                               "<KU>dataEncipherment,cRLSign<ISSUER>c"
-+                               "<SUBJECT>d",
-+                               NULL, NULL);
-+    assert_int_equal(ret, 0);
-+    assert_non_null(ctx->prio_list);
-+    assert_non_null(ctx->prio_list->rule_list);
-+    assert_non_null(ctx->prio_list->rule_list->parsed_match_rule);
-+    assert_int_equal(ctx->prio_list->rule_list->parsed_match_rule->r,
-+                     relation_and);
-+    assert_non_null(ctx->prio_list->rule_list->parsed_match_rule->subject);
-+    assert_string_equal("d",
-+                    ctx->prio_list->rule_list->parsed_match_rule->subject->val);
-+    assert_string_equal("b",
-+              ctx->prio_list->rule_list->parsed_match_rule->subject->next->val);
-+    assert_non_null(ctx->prio_list->rule_list->parsed_match_rule->issuer);
-+    assert_string_equal("c",
-+                     ctx->prio_list->rule_list->parsed_match_rule->issuer->val);
-+    assert_string_equal("a",
-+               ctx->prio_list->rule_list->parsed_match_rule->issuer->next->val);
-+    assert_non_null(ctx->prio_list->rule_list->parsed_match_rule->ku);
-+    assert_int_equal(SSS_KU_CRL_SIGN|SSS_KU_DATA_ENCIPHERMENT,
-+               ctx->prio_list->rule_list->parsed_match_rule->ku->ku);
-+
-+    ret = sss_certmap_add_rule(ctx, 98,
-+                               "KRB5:<ISSUER>a<SUBJECT>b"
-+                               "<KU>dataEncipherment,cRLSign<ISSUER>c"
-+                               "<EKU>clientAuth,emailProtection"
-+                               "<SUBJECT>d",
-+                               NULL, NULL);
-+    assert_int_equal(ret, 0);
-+    assert_non_null(ctx->prio_list);
-+    assert_non_null(ctx->prio_list->rule_list);
-+    assert_non_null(ctx->prio_list->rule_list->parsed_match_rule);
-+    assert_int_equal(ctx->prio_list->rule_list->parsed_match_rule->r,
-+                     relation_and);
-+    assert_non_null(ctx->prio_list->rule_list->parsed_match_rule->subject);
-+    assert_string_equal("d",
-+                    ctx->prio_list->rule_list->parsed_match_rule->subject->val);
-+    assert_string_equal("b",
-+              ctx->prio_list->rule_list->parsed_match_rule->subject->next->val);
-+    assert_non_null(ctx->prio_list->rule_list->parsed_match_rule->issuer);
-+    assert_string_equal("c",
-+                     ctx->prio_list->rule_list->parsed_match_rule->issuer->val);
-+    assert_string_equal("a",
-+               ctx->prio_list->rule_list->parsed_match_rule->issuer->next->val);
-+    assert_non_null(ctx->prio_list->rule_list->parsed_match_rule->ku);
-+    assert_int_equal(SSS_KU_CRL_SIGN|SSS_KU_DATA_ENCIPHERMENT,
-+               ctx->prio_list->rule_list->parsed_match_rule->ku->ku);
-+    assert_non_null(ctx->prio_list->rule_list->parsed_match_rule->eku);
-+    assert_true(string_in_list("1.3.6.1.5.5.7.3.2",
-+              discard_const(
-+               ctx->prio_list->rule_list->parsed_match_rule->eku->eku_oid_list),
-+              true));
-+    assert_true(string_in_list("1.3.6.1.5.5.7.3.4",
-+              discard_const(
-+               ctx->prio_list->rule_list->parsed_match_rule->eku->eku_oid_list),
-+              true));
-+    assert_null(
-+            ctx->prio_list->rule_list->parsed_match_rule->eku->eku_oid_list[2]);
-+
-+    ret = sss_certmap_add_rule(ctx, 97,
-+                               "KRB5:<EKU>clientAuth,1.2.3,emailProtection",
-+                               NULL, NULL);
-+    assert_int_equal(ret, 0);
-+    assert_non_null(ctx->prio_list);
-+    assert_non_null(ctx->prio_list->rule_list);
-+    assert_non_null(ctx->prio_list->rule_list->parsed_match_rule);
-+    assert_int_equal(ctx->prio_list->rule_list->parsed_match_rule->r,
-+                     relation_and);
-+    assert_non_null(ctx->prio_list->rule_list->parsed_match_rule->eku);
-+    assert_true(string_in_list("1.3.6.1.5.5.7.3.2",
-+              discard_const(
-+               ctx->prio_list->rule_list->parsed_match_rule->eku->eku_oid_list),
-+              true));
-+    assert_true(string_in_list("1.3.6.1.5.5.7.3.4",
-+              discard_const(
-+               ctx->prio_list->rule_list->parsed_match_rule->eku->eku_oid_list),
-+              true));
-+    assert_true(string_in_list("1.2.3",
-+              discard_const(
-+               ctx->prio_list->rule_list->parsed_match_rule->eku->eku_oid_list),
-+              true));
-+    assert_null(
-+            ctx->prio_list->rule_list->parsed_match_rule->eku->eku_oid_list[3]);
-+
-+    /* SAN tests */
-+    ret = sss_certmap_add_rule(ctx, 89, "KRB5:<SAN>abc", NULL, NULL);
-+    assert_int_equal(ret, 0);
-+    assert_non_null(ctx->prio_list);
-+    assert_non_null(ctx->prio_list->rule_list);
-+    assert_non_null(ctx->prio_list->rule_list->parsed_match_rule);
-+    assert_int_equal(ctx->prio_list->rule_list->parsed_match_rule->r,
-+                     relation_and);
-+    assert_non_null(ctx->prio_list->rule_list->parsed_match_rule->san);
-+    assert_int_equal(ctx->prio_list->rule_list->parsed_match_rule->san->san_opt,
-+                     SAN_PRINCIPAL);
-+    assert_string_equal(ctx->prio_list->rule_list->parsed_match_rule->san->val,
-+                        "abc");
-+
-+    ret = sss_certmap_add_rule(ctx, 88, "KRB5:<SAN:dnsName>def", NULL, NULL);
-+    assert_int_equal(ret, 0);
-+    assert_non_null(ctx->prio_list);
-+    assert_non_null(ctx->prio_list->rule_list);
-+    assert_non_null(ctx->prio_list->rule_list->parsed_match_rule);
-+    assert_int_equal(ctx->prio_list->rule_list->parsed_match_rule->r,
-+                     relation_and);
-+    assert_non_null(ctx->prio_list->rule_list->parsed_match_rule->san);
-+    assert_int_equal(ctx->prio_list->rule_list->parsed_match_rule->san->san_opt,
-+                     SAN_DNS_NAME);
-+    assert_string_equal(ctx->prio_list->rule_list->parsed_match_rule->san->val,
-+                        "def");
-+
-+    ret = sss_certmap_add_rule(ctx, 87, "KRB5:<SAN:x400Address>aGlq",
-+                               NULL, NULL);
-+    assert_int_equal(ret, 0);
-+    assert_non_null(ctx->prio_list);
-+    assert_non_null(ctx->prio_list->rule_list);
-+    assert_non_null(ctx->prio_list->rule_list->parsed_match_rule);
-+    assert_int_equal(ctx->prio_list->rule_list->parsed_match_rule->r,
-+                     relation_and);
-+    assert_non_null(ctx->prio_list->rule_list->parsed_match_rule->san);
-+    assert_int_equal(ctx->prio_list->rule_list->parsed_match_rule->san->san_opt,
-+                     SAN_X400_ADDRESS);
-+    assert_int_equal(
-+                 ctx->prio_list->rule_list->parsed_match_rule->san->bin_val_len,
-+                 3);
-+    assert_memory_equal(
-+                     ctx->prio_list->rule_list->parsed_match_rule->san->bin_val,
-+                     "hij", 3);
-+
-+    ret = sss_certmap_add_rule(ctx, 86, "KRB5:<SAN:1.2.3.4>klm",
-+                               NULL, NULL);
-+    assert_int_equal(ret, 0);
-+    assert_non_null(ctx->prio_list);
-+    assert_non_null(ctx->prio_list->rule_list);
-+    assert_non_null(ctx->prio_list->rule_list->parsed_match_rule);
-+    assert_int_equal(ctx->prio_list->rule_list->parsed_match_rule->r,
-+                     relation_and);
-+    assert_non_null(ctx->prio_list->rule_list->parsed_match_rule->san);
-+    assert_int_equal(ctx->prio_list->rule_list->parsed_match_rule->san->san_opt,
-+                     SAN_STRING_OTHER_NAME);
-+    assert_string_equal(ctx->prio_list->rule_list->parsed_match_rule->san->val,
-+                        "klm");
-+    assert_string_equal("1.2.3.4",
-+         ctx->prio_list->rule_list->parsed_match_rule->san->str_other_name_oid);
-+
-+    talloc_free(ctx);
-+}
-+
-+static void test_check_ad_attr_name(void **state)
-+{
-+    char *res;
-+
-+    res = check_ad_attr_name(NULL, NULL);
-+    assert_null(res);
-+
-+    res = check_ad_attr_name(NULL, "");
-+    assert_null(res);
-+
-+    res = check_ad_attr_name(NULL, "dsddqwdas");
-+    assert_null(res);
-+
-+    res = check_ad_attr_name(NULL, "dsddq=wdas");
-+    assert_null(res);
-+
-+    res = check_ad_attr_name(NULL, "CN=abc");
-+    assert_null(res);
-+
-+    res = check_ad_attr_name(NULL, "O=xyz");
-+    assert_null(res);
-+
-+    res = check_ad_attr_name(NULL, "ST=def");
-+    assert_non_null(res);
-+    assert_string_equal(res, "S=def");
-+    talloc_free(res);
-+}
-+
-+const uint8_t test_cert_der[] = {
-+0x30, 0x82, 0x04, 0x09, 0x30, 0x82, 0x02, 0xf1, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x01, 0x09,
-+0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30,
-+0x34, 0x31, 0x12, 0x30, 0x10, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x0c, 0x09, 0x49, 0x50, 0x41, 0x2e,
-+0x44, 0x45, 0x56, 0x45, 0x4c, 0x31, 0x1e, 0x30, 0x1c, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x15,
-+0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x20, 0x41, 0x75, 0x74, 0x68,
-+0x6f, 0x72, 0x69, 0x74, 0x79, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x35, 0x30, 0x34, 0x32, 0x38, 0x31,
-+0x30, 0x32, 0x31, 0x31, 0x31, 0x5a, 0x17, 0x0d, 0x31, 0x37, 0x30, 0x34, 0x32, 0x38, 0x31, 0x30,
-+0x32, 0x31, 0x31, 0x31, 0x5a, 0x30, 0x32, 0x31, 0x12, 0x30, 0x10, 0x06, 0x03, 0x55, 0x04, 0x0a,
-+0x0c, 0x09, 0x49, 0x50, 0x41, 0x2e, 0x44, 0x45, 0x56, 0x45, 0x4c, 0x31, 0x1c, 0x30, 0x1a, 0x06,
-+0x03, 0x55, 0x04, 0x03, 0x0c, 0x13, 0x69, 0x70, 0x61, 0x2d, 0x64, 0x65, 0x76, 0x65, 0x6c, 0x2e,
-+0x69, 0x70, 0x61, 0x2e, 0x64, 0x65, 0x76, 0x65, 0x6c, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06,
-+0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f,
-+0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xb2, 0x32, 0x92, 0xab, 0x47, 0xb8,
-+0x0c, 0x13, 0x54, 0x4a, 0x1f, 0x1e, 0x29, 0x06, 0xff, 0xd0, 0x50, 0xcb, 0xf7, 0x5f, 0x79, 0x91,
-+0x65, 0xb1, 0x39, 0x01, 0x83, 0x6a, 0xad, 0x9e, 0x77, 0x3b, 0xf3, 0x0d, 0xd7, 0xb9, 0xf6, 0xdc,
-+0x9e, 0x4a, 0x49, 0xa7, 0xd0, 0x66, 0x72, 0xcc, 0xbf, 0x77, 0xd6, 0xde, 0xa9, 0xfe, 0x67, 0x96,
-+0xcc, 0x49, 0xf1, 0x37, 0x23, 0x2e, 0xc4, 0x50, 0xf4, 0xeb, 0xba, 0x62, 0xd4, 0x23, 0x4d, 0xf3,
-+0x37, 0x38, 0x82, 0xee, 0x3b, 0x3f, 0x2c, 0xd0, 0x80, 0x9b, 0x17, 0xaa, 0x9b, 0xeb, 0xa6, 0xdd,
-+0xf6, 0x15, 0xff, 0x06, 0xb2, 0xce, 0xff, 0xdf, 0x8a, 0x9e, 0x95, 0x85, 0x49, 0x1f, 0x84, 0xfd,
-+0x81, 0x26, 0xce, 0x06, 0x32, 0x0d, 0x36, 0xca, 0x7c, 0x15, 0x81, 0x68, 0x6b, 0x8f, 0x3e, 0xb3,
-+0xa2, 0xfc, 0xae, 0xaf, 0xc2, 0x44, 0x58, 0x15, 0x95, 0x40, 0xfc, 0x56, 0x19, 0x91, 0x80, 0xed,
-+0x42, 0x11, 0x66, 0x04, 0xef, 0x3c, 0xe0, 0x76, 0x33, 0x4b, 0x83, 0xfa, 0x7e, 0xb4, 0x47, 0xdc,
-+0xfb, 0xed, 0x46, 0xa5, 0x8d, 0x0a, 0x66, 0x87, 0xa5, 0xef, 0x7b, 0x74, 0x62, 0xac, 0xbe, 0x73,
-+0x36, 0xc9, 0xb4, 0xfe, 0x20, 0xc4, 0x81, 0xf3, 0xfe, 0x78, 0x19, 0xa8, 0xd0, 0xaf, 0x7f, 0x81,
-+0x72, 0x24, 0x61, 0xd9, 0x76, 0x93, 0xe3, 0x0b, 0xd2, 0x4f, 0x19, 0x17, 0x33, 0x57, 0xd4, 0x82,
-+0xb0, 0xf1, 0xa8, 0x03, 0xf6, 0x01, 0x99, 0xa9, 0xb8, 0x8c, 0x83, 0xc9, 0xba, 0x19, 0x87, 0xea,
-+0xd6, 0x3b, 0x06, 0xeb, 0x4c, 0xf7, 0xf1, 0xe5, 0x28, 0xa9, 0x10, 0xb6, 0x46, 0xde, 0xe1, 0xe1,
-+0x3f, 0xc1, 0xcc, 0x72, 0xbe, 0x2a, 0x43, 0xc6, 0xf6, 0xd0, 0xb5, 0xa0, 0xc4, 0x24, 0x6e, 0x4f,
-+0xbd, 0xec, 0x22, 0x8a, 0x07, 0x11, 0x3d, 0xf9, 0xd3, 0x15, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3,
-+0x82, 0x01, 0x26, 0x30, 0x82, 0x01, 0x22, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18,
-+0x30, 0x16, 0x80, 0x14, 0xf2, 0x9d, 0x42, 0x4e, 0x0f, 0xc4, 0x48, 0x25, 0x58, 0x2f, 0x1c, 0xce,
-+0x0f, 0xa1, 0x3f, 0x22, 0xc8, 0x55, 0xc8, 0x91, 0x30, 0x3b, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05,
-+0x05, 0x07, 0x01, 0x01, 0x04, 0x2f, 0x30, 0x2d, 0x30, 0x2b, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05,
-+0x05, 0x07, 0x30, 0x01, 0x86, 0x1f, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x69, 0x70, 0x61,
-+0x2d, 0x63, 0x61, 0x2e, 0x69, 0x70, 0x61, 0x2e, 0x64, 0x65, 0x76, 0x65, 0x6c, 0x2f, 0x63, 0x61,
-+0x2f, 0x6f, 0x63, 0x73, 0x70, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04,
-+0x04, 0x03, 0x02, 0x04, 0xf0, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x25, 0x04, 0x16, 0x30, 0x14,
-+0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x01, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05,
-+0x05, 0x07, 0x03, 0x02, 0x30, 0x74, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x6d, 0x30, 0x6b, 0x30,
-+0x69, 0xa0, 0x31, 0xa0, 0x2f, 0x86, 0x2d, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x69, 0x70,
-+0x61, 0x2d, 0x63, 0x61, 0x2e, 0x69, 0x70, 0x61, 0x2e, 0x64, 0x65, 0x76, 0x65, 0x6c, 0x2f, 0x69,
-+0x70, 0x61, 0x2f, 0x63, 0x72, 0x6c, 0x2f, 0x4d, 0x61, 0x73, 0x74, 0x65, 0x72, 0x43, 0x52, 0x4c,
-+0x2e, 0x62, 0x69, 0x6e, 0xa2, 0x34, 0xa4, 0x32, 0x30, 0x30, 0x31, 0x0e, 0x30, 0x0c, 0x06, 0x03,
-+0x55, 0x04, 0x0a, 0x0c, 0x05, 0x69, 0x70, 0x61, 0x63, 0x61, 0x31, 0x1e, 0x30, 0x1c, 0x06, 0x03,
-+0x55, 0x04, 0x03, 0x0c, 0x15, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65,
-+0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d,
-+0x0e, 0x04, 0x16, 0x04, 0x14, 0x2d, 0x2b, 0x3f, 0xcb, 0xf5, 0xb2, 0xff, 0x32, 0x2c, 0xa8, 0xc2,
-+0x1c, 0xdd, 0xbd, 0x8c, 0x80, 0x1e, 0xdd, 0x31, 0x82, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48,
-+0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x9a, 0x47, 0x2e,
-+0x50, 0xa7, 0x4d, 0x1d, 0x53, 0x0f, 0xc9, 0x71, 0x42, 0x0c, 0xe5, 0xda, 0x7d, 0x49, 0x64, 0xe7,
-+0xab, 0xc8, 0xdf, 0xdf, 0x02, 0xc1, 0x87, 0xd1, 0x5b, 0xde, 0xda, 0x6f, 0x2b, 0xe4, 0xf0, 0xbe,
-+0xba, 0x09, 0xdf, 0x02, 0x85, 0x0b, 0x8a, 0xe6, 0x9b, 0x06, 0x7d, 0x69, 0x38, 0x6c, 0x72, 0xff,
-+0x4c, 0x7b, 0x2a, 0x0d, 0x3f, 0x23, 0x2f, 0x16, 0x46, 0xff, 0x05, 0x93, 0xb0, 0xea, 0x24, 0x28,
-+0xd7, 0x12, 0xa1, 0x57, 0xb8, 0x59, 0x19, 0x25, 0xf3, 0x43, 0x0a, 0xd3, 0xfd, 0x0f, 0x37, 0x8d,
-+0xb8, 0xca, 0x15, 0xe7, 0x48, 0x8a, 0xa0, 0xc7, 0xc7, 0x4b, 0x7f, 0x01, 0x3c, 0x58, 0xd7, 0x37,
-+0xe5, 0xff, 0x7d, 0x2b, 0x01, 0xac, 0x0d, 0x9f, 0x51, 0x6a, 0xe5, 0x40, 0x24, 0xe6, 0x5e, 0x55,
-+0x0d, 0xf7, 0xb8, 0x2f, 0x42, 0xac, 0x6d, 0xe5, 0x29, 0x6b, 0xc6, 0x0b, 0xa4, 0xbf, 0x19, 0xbd,
-+0x39, 0x27, 0xee, 0xfe, 0xc5, 0xb3, 0xdb, 0x62, 0xd4, 0xbe, 0xd2, 0x47, 0xba, 0x96, 0x30, 0x5a,
-+0xfd, 0x62, 0x00, 0xb8, 0x27, 0x5d, 0x2f, 0x3a, 0x94, 0x0b, 0x95, 0x35, 0x85, 0x40, 0x2c, 0xbc,
-+0x67, 0xdf, 0x8a, 0xf9, 0xf1, 0x7b, 0x19, 0x96, 0x3e, 0x42, 0x48, 0x13, 0x23, 0x04, 0x95, 0xa9,
-+0x6b, 0x11, 0x33, 0x81, 0x47, 0x5a, 0x83, 0x72, 0xf6, 0x20, 0xfa, 0x8e, 0x41, 0x7b, 0x8f, 0x77,
-+0x47, 0x7c, 0xc7, 0x5d, 0x46, 0xf4, 0x4f, 0xfd, 0x81, 0x0a, 0xae, 0x39, 0x27, 0xb6, 0x6a, 0x26,
-+0x63, 0xb1, 0xd3, 0xbf, 0x55, 0x83, 0x82, 0x9b, 0x36, 0x6c, 0x33, 0x64, 0x0f, 0x50, 0xc0, 0x55,
-+0x94, 0x13, 0xc3, 0x85, 0xf4, 0xd5, 0x71, 0x65, 0xd0, 0xc0, 0xdd, 0xfc, 0xe6, 0xec, 0x9c, 0x5b,
-+0xf0, 0x11, 0xb5, 0x2c, 0xf3, 0x48, 0xc1, 0x36, 0x8c, 0xa2, 0x96, 0x48, 0x84};
-+
-+const uint8_t test_cert2_der[] = {
-+0x30, 0x82, 0x06, 0x98, 0x30, 0x82, 0x05, 0x80, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x0a, 0x61,
-+0x22, 0x88, 0xc2, 0x00, 0x00, 0x00, 0x00, 0x02, 0xa6, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48,
-+0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x45, 0x31, 0x15, 0x30, 0x13, 0x06, 0x0a,
-+0x09, 0x92, 0x26, 0x89, 0x93, 0xf2, 0x2c, 0x64, 0x01, 0x19, 0x16, 0x05, 0x64, 0x65, 0x76, 0x65,
-+0x6c, 0x31, 0x12, 0x30, 0x10, 0x06, 0x0a, 0x09, 0x92, 0x26, 0x89, 0x93, 0xf2, 0x2c, 0x64, 0x01,
-+0x19, 0x16, 0x02, 0x61, 0x64, 0x31, 0x18, 0x30, 0x16, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x0f,
-+0x61, 0x64, 0x2d, 0x41, 0x44, 0x2d, 0x53, 0x45, 0x52, 0x56, 0x45, 0x52, 0x2d, 0x43, 0x41, 0x30,
-+0x1e, 0x17, 0x0d, 0x31, 0x36, 0x31, 0x31, 0x31, 0x31, 0x31, 0x33, 0x35, 0x31, 0x31, 0x31, 0x5a,
-+0x17, 0x0d, 0x31, 0x37, 0x31, 0x31, 0x31, 0x31, 0x31, 0x33, 0x35, 0x31, 0x31, 0x31, 0x5a, 0x30,
-+0x70, 0x31, 0x15, 0x30, 0x13, 0x06, 0x0a, 0x09, 0x92, 0x26, 0x89, 0x93, 0xf2, 0x2c, 0x64, 0x01,
-+0x19, 0x16, 0x05, 0x64, 0x65, 0x76, 0x65, 0x6c, 0x31, 0x12, 0x30, 0x10, 0x06, 0x0a, 0x09, 0x92,
-+0x26, 0x89, 0x93, 0xf2, 0x2c, 0x64, 0x01, 0x19, 0x16, 0x02, 0x61, 0x64, 0x31, 0x0e, 0x30, 0x0c,
-+0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x05, 0x55, 0x73, 0x65, 0x72, 0x73, 0x31, 0x0c, 0x30, 0x0a,
-+0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x03, 0x74, 0x20, 0x75, 0x31, 0x25, 0x30, 0x23, 0x06, 0x09,
-+0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 0x01, 0x16, 0x16, 0x74, 0x65, 0x73, 0x74, 0x2e,
-+0x75, 0x73, 0x65, 0x72, 0x40, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x2e, 0x64, 0x6f, 0x6d, 0x61, 0x69,
-+0x6e, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01,
-+0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01,
-+0x01, 0x00, 0x9c, 0xcf, 0x36, 0x99, 0xde, 0x63, 0x74, 0x2b, 0x77, 0x25, 0x9e, 0x24, 0xd9, 0x77,
-+0x4b, 0x5f, 0x98, 0xc0, 0x8c, 0xd7, 0x20, 0x91, 0xc0, 0x1c, 0xe8, 0x37, 0x45, 0xbf, 0x3c, 0xd9,
-+0x33, 0xbd, 0xe9, 0xde, 0xc9, 0x5d, 0xd4, 0xcd, 0x06, 0x0a, 0x0d, 0xd4, 0xf1, 0x7c, 0x74, 0x5b,
-+0x29, 0xd5, 0x66, 0x9c, 0x2c, 0x9f, 0x6b, 0x1a, 0x0f, 0x0d, 0xe6, 0x6c, 0x62, 0xa5, 0x41, 0x4f,
-+0xc3, 0xa4, 0x88, 0x27, 0x11, 0x5d, 0xb7, 0xb1, 0xfb, 0xf8, 0x8d, 0xee, 0x43, 0x8d, 0x93, 0xb5,
-+0x8c, 0xb4, 0x34, 0x06, 0xf5, 0xe9, 0x2f, 0x5a, 0x26, 0x68, 0xd7, 0x43, 0x60, 0x82, 0x5e, 0x22,
-+0xa7, 0xc6, 0x34, 0x40, 0x19, 0xa5, 0x8e, 0xf0, 0x58, 0x9f, 0x16, 0x2d, 0x43, 0x3f, 0x0c, 0xda,
-+0xe2, 0x23, 0xf6, 0x09, 0x2a, 0x5e, 0xbd, 0x84, 0x27, 0xc8, 0xab, 0xd5, 0x70, 0xf8, 0x3d, 0x9c,
-+0x14, 0xc2, 0xc2, 0xa2, 0x77, 0xe8, 0x44, 0x73, 0x10, 0x01, 0x34, 0x40, 0x1f, 0xc6, 0x2f, 0xa0,
-+0x70, 0xee, 0x2f, 0xd5, 0x4b, 0xbe, 0x4c, 0xc7, 0x45, 0xf7, 0xac, 0x9c, 0xc3, 0x68, 0x5b, 0x1d,
-+0x5a, 0x4b, 0x77, 0x65, 0x76, 0xe4, 0xb3, 0x92, 0xf4, 0x84, 0x0a, 0x9e, 0x6a, 0x9c, 0xc9, 0x53,
-+0x42, 0x9f, 0x6d, 0xfe, 0xf9, 0xf5, 0xf2, 0x9a, 0x15, 0x50, 0x47, 0xef, 0xf4, 0x06, 0x59, 0xc8,
-+0x50, 0x48, 0x4b, 0x46, 0x95, 0x68, 0x25, 0xc5, 0xbd, 0x4f, 0x65, 0x34, 0x00, 0xfc, 0x31, 0x69,
-+0xf8, 0x3e, 0xe0, 0x20, 0x83, 0x41, 0x27, 0x0b, 0x5c, 0x46, 0x98, 0x14, 0xf0, 0x07, 0xde, 0x02,
-+0x17, 0xb1, 0xd2, 0x9c, 0xbe, 0x1c, 0x0d, 0x56, 0x22, 0x1b, 0x02, 0xfe, 0xda, 0x69, 0xb9, 0xef,
-+0x91, 0x37, 0x39, 0x7f, 0x24, 0xda, 0xc4, 0x81, 0x5e, 0x82, 0x31, 0x2f, 0x98, 0x1d, 0xf7, 0x73,
-+0x5b, 0x23, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82, 0x03, 0x5d, 0x30, 0x82, 0x03, 0x59, 0x30,
-+0x3d, 0x06, 0x09, 0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x15, 0x07, 0x04, 0x30, 0x30, 0x2e,
-+0x06, 0x26, 0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x15, 0x08, 0x87, 0x85, 0xa1, 0x23, 0x84,
-+0xc8, 0xb2, 0x26, 0x83, 0x9d, 0x9d, 0x21, 0x82, 0xd4, 0xa6, 0x1b, 0x86, 0xa3, 0xba, 0x37, 0x81,
-+0x10, 0x85, 0x89, 0xd5, 0x02, 0xd6, 0x8f, 0x24, 0x02, 0x01, 0x64, 0x02, 0x01, 0x02, 0x30, 0x29,
-+0x06, 0x03, 0x55, 0x1d, 0x25, 0x04, 0x22, 0x30, 0x20, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05,
-+0x07, 0x03, 0x02, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x04, 0x06, 0x0a, 0x2b,
-+0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x0a, 0x03, 0x04, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f,
-+0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x05, 0xa0, 0x30, 0x35, 0x06, 0x09, 0x2b, 0x06, 0x01,
-+0x04, 0x01, 0x82, 0x37, 0x15, 0x0a, 0x04, 0x28, 0x30, 0x26, 0x30, 0x0a, 0x06, 0x08, 0x2b, 0x06,
-+0x01, 0x05, 0x05, 0x07, 0x03, 0x02, 0x30, 0x0a, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07,
-+0x03, 0x04, 0x30, 0x0c, 0x06, 0x0a, 0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x0a, 0x03, 0x04,
-+0x30, 0x81, 0x94, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 0x0f, 0x04, 0x81,
-+0x86, 0x30, 0x81, 0x83, 0x30, 0x0b, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x01,
-+0x2a, 0x30, 0x0b, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x01, 0x2d, 0x30, 0x0b,
-+0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x01, 0x16, 0x30, 0x0b, 0x06, 0x09, 0x60,
-+0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x01, 0x19, 0x30, 0x0b, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01,
-+0x65, 0x03, 0x04, 0x01, 0x02, 0x30, 0x0b, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04,
-+0x01, 0x05, 0x30, 0x0a, 0x06, 0x08, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x03, 0x07, 0x30, 0x07,
-+0x06, 0x05, 0x2b, 0x0e, 0x03, 0x02, 0x07, 0x30, 0x0e, 0x06, 0x08, 0x2a, 0x86, 0x48, 0x86, 0xf7,
-+0x0d, 0x03, 0x02, 0x02, 0x02, 0x00, 0x80, 0x30, 0x0e, 0x06, 0x08, 0x2a, 0x86, 0x48, 0x86, 0xf7,
-+0x0d, 0x03, 0x04, 0x02, 0x02, 0x02, 0x00, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16,
-+0x04, 0x14, 0x49, 0xac, 0xad, 0xe0, 0x65, 0x30, 0xc4, 0xce, 0xa0, 0x09, 0x03, 0x5b, 0xad, 0x4a,
-+0x7b, 0x49, 0x5e, 0xc9, 0x6c, 0xb4, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30,
-+0x16, 0x80, 0x14, 0x62, 0x50, 0xb6, 0x8d, 0xa1, 0xe6, 0x2d, 0x91, 0xbf, 0xb0, 0x54, 0x4d, 0x8f,
-+0xa8, 0xca, 0x10, 0xae, 0xb8, 0xdd, 0x54, 0x30, 0x81, 0xcc, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04,
-+0x81, 0xc4, 0x30, 0x81, 0xc1, 0x30, 0x81, 0xbe, 0xa0, 0x81, 0xbb, 0xa0, 0x81, 0xb8, 0x86, 0x81,
-+0xb5, 0x6c, 0x64, 0x61, 0x70, 0x3a, 0x2f, 0x2f, 0x2f, 0x43, 0x4e, 0x3d, 0x61, 0x64, 0x2d, 0x41,
-+0x44, 0x2d, 0x53, 0x45, 0x52, 0x56, 0x45, 0x52, 0x2d, 0x43, 0x41, 0x2c, 0x43, 0x4e, 0x3d, 0x61,
-+0x64, 0x2d, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2c, 0x43, 0x4e, 0x3d, 0x43, 0x44, 0x50, 0x2c,
-+0x43, 0x4e, 0x3d, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x25, 0x32, 0x30, 0x4b, 0x65, 0x79, 0x25,
-+0x32, 0x30, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2c, 0x43, 0x4e, 0x3d, 0x53, 0x65,
-+0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2c, 0x43, 0x4e, 0x3d, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67,
-+0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2c, 0x44, 0x43, 0x3d, 0x61, 0x64, 0x2c, 0x44, 0x43,
-+0x3d, 0x64, 0x65, 0x76, 0x65, 0x6c, 0x3f, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61,
-+0x74, 0x65, 0x52, 0x65, 0x76, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4c, 0x69, 0x73, 0x74,
-+0x3f, 0x62, 0x61, 0x73, 0x65, 0x3f, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x43, 0x6c, 0x61, 0x73,
-+0x73, 0x3d, 0x63, 0x52, 0x4c, 0x44, 0x69, 0x73, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x69, 0x6f,
-+0x6e, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x30, 0x81, 0xbe, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05,
-+0x07, 0x01, 0x01, 0x04, 0x81, 0xb1, 0x30, 0x81, 0xae, 0x30, 0x81, 0xab, 0x06, 0x08, 0x2b, 0x06,
-+0x01, 0x05, 0x05, 0x07, 0x30, 0x02, 0x86, 0x81, 0x9e, 0x6c, 0x64, 0x61, 0x70, 0x3a, 0x2f, 0x2f,
-+0x2f, 0x43, 0x4e, 0x3d, 0x61, 0x64, 0x2d, 0x41, 0x44, 0x2d, 0x53, 0x45, 0x52, 0x56, 0x45, 0x52,
-+0x2d, 0x43, 0x41, 0x2c, 0x43, 0x4e, 0x3d, 0x41, 0x49, 0x41, 0x2c, 0x43, 0x4e, 0x3d, 0x50, 0x75,
-+0x62, 0x6c, 0x69, 0x63, 0x25, 0x32, 0x30, 0x4b, 0x65, 0x79, 0x25, 0x32, 0x30, 0x53, 0x65, 0x72,
-+0x76, 0x69, 0x63, 0x65, 0x73, 0x2c, 0x43, 0x4e, 0x3d, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65,
-+0x73, 0x2c, 0x43, 0x4e, 0x3d, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69,
-+0x6f, 0x6e, 0x2c, 0x44, 0x43, 0x3d, 0x61, 0x64, 0x2c, 0x44, 0x43, 0x3d, 0x64, 0x65, 0x76, 0x65,
-+0x6c, 0x3f, 0x63, 0x41, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x3f,
-+0x62, 0x61, 0x73, 0x65, 0x3f, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x43, 0x6c, 0x61, 0x73, 0x73,
-+0x3d, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x41, 0x75,
-+0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x30, 0x3f, 0x06, 0x03, 0x55, 0x1d, 0x11, 0x04, 0x38,
-+0x30, 0x36, 0xa0, 0x1c, 0x06, 0x0a, 0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x14, 0x02, 0x03,
-+0xa0, 0x0e, 0x0c, 0x0c, 0x74, 0x75, 0x31, 0x40, 0x61, 0x64, 0x2e, 0x64, 0x65, 0x76, 0x65, 0x6c,
-+0x81, 0x16, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x40, 0x65, 0x6d, 0x61, 0x69,
-+0x6c, 0x2e, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86,
-+0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x41, 0x45, 0x0a, 0x6d,
-+0xbb, 0x7f, 0x5c, 0x07, 0x0c, 0xc9, 0xb0, 0x39, 0x55, 0x6d, 0x7c, 0xb5, 0x02, 0xcd, 0xe8, 0xb2,
-+0xe5, 0x02, 0x94, 0x77, 0x60, 0xdb, 0xd1, 0xaf, 0x1d, 0xdb, 0x44, 0x5f, 0xce, 0x83, 0xdb, 0x80,
-+0x2e, 0xe2, 0xb2, 0x08, 0x25, 0x82, 0x14, 0xcb, 0x48, 0x95, 0x20, 0x13, 0x6c, 0xa9, 0xaa, 0xf8,
-+0x31, 0x56, 0xed, 0xc0, 0x3b, 0xd4, 0xae, 0x2e, 0xe3, 0x8f, 0x05, 0xfc, 0xab, 0x5f, 0x2a, 0x69,
-+0x23, 0xbc, 0xb8, 0x8c, 0xec, 0x2d, 0xa9, 0x0b, 0x86, 0x95, 0x73, 0x73, 0xdb, 0x17, 0xce, 0xc6,
-+0xae, 0xc5, 0xb4, 0xc1, 0x25, 0x87, 0x3b, 0x67, 0x43, 0x9e, 0x87, 0x5a, 0xe6, 0xb9, 0xa0, 0x28,
-+0x12, 0x3d, 0xa8, 0x2e, 0xd7, 0x5e, 0xef, 0x65, 0x2d, 0xe6, 0xa5, 0x67, 0x84, 0xac, 0xfd, 0x31,
-+0xc1, 0x78, 0xd8, 0x72, 0x51, 0xa2, 0x88, 0x55, 0x0f, 0x97, 0x47, 0x93, 0x07, 0xea, 0x8a, 0x53,
-+0x27, 0x4e, 0x34, 0x54, 0x34, 0x1f, 0xa0, 0x6a, 0x03, 0x44, 0xfb, 0x23, 0x61, 0x8e, 0x87, 0x8e,
-+0x3c, 0xd0, 0x8f, 0xae, 0xe4, 0xcf, 0xee, 0x65, 0xa8, 0xba, 0x96, 0x68, 0x08, 0x1c, 0x60, 0xe2,
-+0x4e, 0x11, 0xa3, 0x74, 0xb8, 0xa5, 0x4e, 0xea, 0x6a, 0x82, 0x4c, 0xc2, 0x4d, 0x63, 0x8e, 0x9f,
-+0x7c, 0x2f, 0xa8, 0xc0, 0x62, 0xf8, 0xf7, 0xd9, 0x25, 0xc4, 0x91, 0xab, 0x4d, 0x6a, 0x44, 0xaf,
-+0x75, 0x93, 0x53, 0x03, 0xa4, 0x99, 0xc8, 0xcd, 0x91, 0x89, 0x60, 0x75, 0x30, 0x99, 0x76, 0x05,
-+0x5a, 0xa0, 0x03, 0xa7, 0xa1, 0x2c, 0x03, 0x04, 0x8f, 0xd4, 0x5a, 0x31, 0x52, 0x28, 0x5a, 0xe6,
-+0xa2, 0xd3, 0x43, 0x21, 0x5b, 0xdc, 0xa2, 0x1d, 0x55, 0xa9, 0x48, 0xc5, 0xc4, 0xaa, 0xf3, 0x8b,
-+0xe6, 0x3e, 0x75, 0x96, 0xe4, 0x3e, 0x64, 0xaf, 0xe8, 0xa7, 0x6a, 0xb6};
-+
-+void test_sss_cert_get_content(void **state)
-+{
-+    int ret;
-+    struct sss_cert_content *content;
-+
-+    ret = sss_cert_get_content(NULL, test_cert_der, sizeof(test_cert_der),
-+                               &content);
-+    assert_int_equal(ret , 0);
-+    assert_non_null(content);
-+    assert_non_null(content->issuer_str);
-+    assert_string_equal(content->issuer_str, "CN=Certificate Authority,O=IPA.DEVEL");
-+    assert_non_null(content->subject_str);
-+    assert_string_equal(content->subject_str, "CN=ipa-devel.ipa.devel,O=IPA.DEVEL");
-+    assert_int_equal(content->key_usage, SSS_KU_DIGITAL_SIGNATURE
-+                                            |SSS_KU_NON_REPUDIATION
-+                                            |SSS_KU_KEY_ENCIPHERMENT
-+                                            |SSS_KU_DATA_ENCIPHERMENT);
-+    assert_non_null(content->extended_key_usage_oids);
-+    assert_non_null(content->extended_key_usage_oids[0]);
-+    assert_true(string_in_list("1.3.6.1.5.5.7.3.1",
-+                discard_const(content->extended_key_usage_oids), true));
-+    assert_true(string_in_list("1.3.6.1.5.5.7.3.2",
-+                discard_const(content->extended_key_usage_oids), true));
-+    assert_null(content->extended_key_usage_oids[2]);
-+    assert_int_equal(content->cert_der_size, sizeof(test_cert_der));
-+    assert_memory_equal(content->cert_der, test_cert_der, sizeof(test_cert_der));
-+
-+    assert_non_null(content->issuer_rdn_list);
-+    assert_string_equal(content->issuer_rdn_list[0], "O=IPA.DEVEL");
-+    assert_string_equal(content->issuer_rdn_list[1], "CN=Certificate Authority");
-+    assert_null(content->issuer_rdn_list[2]);
-+
-+    assert_non_null(content->subject_rdn_list);
-+    assert_string_equal(content->subject_rdn_list[0], "O=IPA.DEVEL");
-+    assert_string_equal(content->subject_rdn_list[1], "CN=ipa-devel.ipa.devel");
-+    assert_null(content->subject_rdn_list[2]);
-+
-+
-+    talloc_free(content);
-+}
-+
-+void test_sss_cert_get_content_2(void **state)
-+{
-+    int ret;
-+    struct sss_cert_content *content;
-+    struct san_list *i;
-+
-+    ret = sss_cert_get_content(NULL, test_cert2_der, sizeof(test_cert2_der),
-+                               &content);
-+    assert_int_equal(ret, 0);
-+    assert_non_null(content);
-+    assert_non_null(content->issuer_str);
-+    assert_string_equal(content->issuer_str,
-+                        "CN=ad-AD-SERVER-CA,DC=ad,DC=devel");
-+    assert_non_null(content->subject_str);
-+#if 0
-+FIXME:
-+    assert_string_equal(content->subject_str,
-+      "E=test.user@email.domain,CN=t u,CN=Users,DC=ad,DC=devel,DC=ad,DC=devel");
-+      //"CN=t u/emailAddress=test.user@email.domain,DC=ad,DC=devel");
-+#endif
-+    assert_int_equal(content->key_usage, SSS_KU_DIGITAL_SIGNATURE
-+                                            |SSS_KU_KEY_ENCIPHERMENT);
-+    assert_non_null(content->extended_key_usage_oids);
-+    assert_non_null(content->extended_key_usage_oids[0]);
-+    assert_true(string_in_list("1.3.6.1.5.5.7.3.2",
-+                discard_const(content->extended_key_usage_oids), true));
-+    assert_true(string_in_list("1.3.6.1.5.5.7.3.4",
-+                discard_const(content->extended_key_usage_oids), true));
-+    /* Can use Microsoft Encrypted File System OID */
-+    assert_true(string_in_list("1.3.6.1.4.1.311.10.3.4",
-+                discard_const(content->extended_key_usage_oids), true));
-+    assert_null(content->extended_key_usage_oids[3]);
-+    assert_int_equal(content->cert_der_size, sizeof(test_cert2_der));
-+    assert_memory_equal(content->cert_der, test_cert2_der,
-+                        sizeof(test_cert2_der));
-+
-+    assert_non_null(content->issuer_rdn_list);
-+    assert_string_equal(content->issuer_rdn_list[0], "DC=devel");
-+    assert_string_equal(content->issuer_rdn_list[1], "DC=ad");
-+    assert_string_equal(content->issuer_rdn_list[2], "CN=ad-AD-SERVER-CA");
-+    assert_null(content->issuer_rdn_list[3]);
-+
-+    assert_non_null(content->subject_rdn_list);
-+    assert_string_equal(content->subject_rdn_list[0], "DC=devel");
-+    assert_string_equal(content->subject_rdn_list[1], "DC=ad");
-+    assert_string_equal(content->subject_rdn_list[2], "CN=Users");
-+    assert_string_equal(content->subject_rdn_list[3], "CN=t u");
-+    assert_string_equal(content->subject_rdn_list[4],
-+                                                    "E=test.user@email.domain");
-+                     //"CN=t u/emailAddress=test.user@email.domain");
-+    assert_null(content->subject_rdn_list[5]);
-+
-+    assert_non_null(content->san_list);
-+
-+    DLIST_FOR_EACH(i, content->san_list) {
-+        switch (i->san_opt) {
-+        case SAN_RFC822_NAME:
-+            assert_string_equal(i->val, "test.user@email.domain");
-+            assert_string_equal(i->short_name, "test.user");
-+            break;
-+        case SAN_STRING_OTHER_NAME:
-+            assert_string_equal(i->other_name_oid, "1.3.6.1.4.1.311.20.2.3");
-+            assert_int_equal(i->bin_val_len, 14);
-+            assert_memory_equal(i->bin_val, "\f\ftu1@ad.devel", 14);
-+            break;
-+        case SAN_NT:
-+        case SAN_PRINCIPAL:
-+            assert_string_equal(i->val, "tu1@ad.devel");
-+            assert_string_equal(i->short_name, "tu1");
-+            break;
-+        default:
-+            assert_true(false);
-+        }
-+    }
-+
-+    talloc_free(content);
-+}
-+
-+static void test_sss_certmap_match_cert(void **state)
-+{
-+    struct sss_certmap_ctx *ctx;
-+    int ret;
-+    size_t c;
-+
-+    struct match_tests {
-+        const char *rule;
-+        int result;
-+    } match_tests[] = {
-+        {"KRB5:<KU>digitalSignature", 0},
-+        {"KRB5:<KU>digitalSignature,nonRepudiation", 0},
-+        {"KRB5:<KU>digitalSignature,cRLSign", ENOENT},
-+        {"KRB5:<EKU>clientAuth", 0},
-+        {"KRB5:<EKU>clientAuth,OCSPSigning", ENOENT},
-+        {"KRB5:<EKU>clientAuth,serverAuth", 0},
-+        {NULL, 0}
-+    };
-+
-+    struct match_tests match_tests_2[] = {
-+        {"KRB5:<KU>digitalSignature", 0},
-+        {"KRB5:<KU>keyEncipherment", 0},
-+        {"KRB5:<KU>digitalSignature,keyEncipherment", 0},
-+        {"KRB5:<KU>digitalSignature,keyEncipherment,cRLSign", ENOENT},
-+        {"KRB5:<EKU>clientAuth", 0},
-+        {"KRB5:<EKU>clientAuth,1.3.6.1.4.1.311.10.3.4", 0},
-+        {"KRB5:<EKU>clientAuth,1.3.6.1.4.1.311.10.3.41", ENOENT},
-+        {"KRB5:<SAN>tu1", 0},
-+        {"KRB5:<SAN:Principal>tu1", 0},
-+        {"KRB5:<SAN:ntPrincipalName>tu1", 0},
-+        {"KRB5:<SAN:pkinitSAN>tu1", ENOENT},
-+        {"KRB5:<SAN:Principal>^tu1@ad.devel$", 0},
-+        {"KRB5:<SAN:rfc822Name>tu", ENOENT},
-+        {"KRB5:<SAN:rfc822Name>test.user", 0},
-+        {"KRB5:<SAN:rfc822Name>test.user<SAN>tu1", 0},
-+        {"KRB5:||<SAN:rfc822Name>test.user<SAN>tu1", 0},
-+        {"KRB5:&&<SAN:rfc822Name>tu1<SAN>tu1", ENOENT},
-+        {"KRB5:||<SAN:rfc822Name>tu1<SAN>tu1", 0},
-+        {"KRB5:<SAN:otherName>MTIz", ENOENT}, /* 123 */
-+        {"KRB5:<SAN:otherName>DAx0dTFAYWQuZGV2ZWw=", 0}, /* "\f\ftu1@ad.devel" */
-+        {"KRB5:<SAN:otherName>DAx0dTFAYWQuZGV2ZWx4", ENOENT}, /* "\f\ftu1@ad.develx" */
-+        {"KRB5:<SAN:otherName>dHUxQGFkLmRldmVs", 0}, /* "tu1@ad.devel" */
-+        {"KRB5:<SAN:1.3.6.1.4.1.311.20.2.3>test", ENOENT},
-+        {"KRB5:<SAN:1.3.6.1.4.1.311.20.2.3>tu1@ad", 0},
-+        /* Fails becasue the NT principal SAN starts with binary values */
-+        {"KRB5:<SAN:1.3.6.1.4.1.311.20.2.3>^tu1@ad.devel$", ENOENT},
-+        {NULL, 0}
-+    };
-+
-+    ret = sss_certmap_init(NULL, ext_debug, NULL, &ctx);
-+    assert_int_equal(ret, EOK);
-+    assert_non_null(ctx);
-+    assert_null(ctx->prio_list);
-+
-+    ret = sss_certmap_add_rule(ctx, 1, "KRB5:<ISSUER>xyz<SUBJECT>xyz",
-+                               NULL, NULL);
-+    assert_int_equal(ret, EOK);
-+
-+    ret = sss_certmap_match_cert(ctx, discard_const(test_cert_der),
-+                                 sizeof(test_cert_der));
-+    assert_int_equal(ret, ENOENT);
-+
-+    ret = sss_certmap_add_rule(ctx, 1,
-+                            "KRB5:<ISSUER>CN=Certificate Authority,O=IPA.DEVEL",
-+                            NULL, NULL);
-+    assert_int_equal(ret, EOK);
-+
-+    ret = sss_certmap_match_cert(ctx, discard_const(test_cert_der),
-+                                 sizeof(test_cert_der));
-+    assert_int_equal(ret, 0);
-+
-+    sss_certmap_free_ctx(ctx);
-+
-+    for (c = 0; match_tests[c].rule != NULL; c++) {
-+        ret = sss_certmap_init(NULL, ext_debug, NULL, &ctx);
-+        assert_int_equal(ret, EOK);
-+        assert_non_null(ctx);
-+        assert_null(ctx->prio_list);
-+
-+        ret = sss_certmap_add_rule(ctx, 1, match_tests[c].rule, NULL, NULL);
-+        assert_int_equal(ret, EOK);
-+
-+        ret = sss_certmap_match_cert(ctx, discard_const(test_cert_der),
-+                                     sizeof(test_cert_der));
-+        assert_int_equal(ret, match_tests[c].result);
-+
-+        sss_certmap_free_ctx(ctx);
-+    }
-+
-+    for (c = 0; match_tests_2[c].rule != NULL; c++) {
-+        ret = sss_certmap_init(NULL, ext_debug, NULL, &ctx);
-+        assert_int_equal(ret, EOK);
-+        assert_non_null(ctx);
-+        assert_null(ctx->prio_list);
-+
-+        print_error("Checking matching rule [%s]\n", match_tests_2[c].rule);
-+
-+        ret = sss_certmap_add_rule(ctx, 1, match_tests_2[c].rule, NULL, NULL);
-+        assert_int_equal(ret, EOK);
-+
-+        ret = sss_certmap_match_cert(ctx, discard_const(test_cert2_der),
-+                                     sizeof(test_cert2_der));
-+        assert_int_equal(ret, match_tests_2[c].result);
-+
-+        sss_certmap_free_ctx(ctx);
-+    }
-+}
-+
-+static void test_sss_certmap_add_mapping_rule(void **state)
-+{
-+    struct sss_certmap_ctx *ctx;
-+    int ret;
-+
-+    ret = sss_certmap_init(NULL, ext_debug, NULL, &ctx);
-+    assert_int_equal(ret, EOK);
-+    assert_non_null(ctx);
-+    assert_null(ctx->prio_list);
-+
-+    ret = sss_certmap_add_rule(ctx, 1, NULL, "FWEAWEF:fwefwe", NULL);
-+    assert_int_equal(ret, ESRCH);
-+
-+    ret = sss_certmap_add_rule(ctx, 1, NULL, "LDAP:abc", NULL);
-+    assert_int_equal(ret, 0);
-+    assert_non_null(ctx->prio_list);
-+    assert_non_null(ctx->prio_list->rule_list);
-+    assert_non_null(ctx->prio_list->rule_list->parsed_mapping_rule);
-+    assert_non_null(ctx->prio_list->rule_list->parsed_mapping_rule->list);
-+    assert_int_equal(comp_string,
-+                    ctx->prio_list->rule_list->parsed_mapping_rule->list->type);
-+    assert_string_equal("abc",
-+                     ctx->prio_list->rule_list->parsed_mapping_rule->list->val);
-+    talloc_free(ctx);
-+
-+    ret = sss_certmap_init(NULL, ext_debug, NULL, &ctx);
-+    assert_int_equal(ret, EOK);
-+    assert_non_null(ctx);
-+    assert_null(ctx->prio_list);
-+    ret = sss_certmap_add_rule(ctx, 1, NULL, "LDAP:abc{issuer_dn}", NULL);
-+    assert_int_equal(ret, 0);
-+    assert_non_null(ctx->prio_list);
-+    assert_non_null(ctx->prio_list->rule_list);
-+    assert_non_null(ctx->prio_list->rule_list->parsed_mapping_rule);
-+    assert_non_null(ctx->prio_list->rule_list->parsed_mapping_rule->list);
-+    assert_int_equal(comp_string,
-+                    ctx->prio_list->rule_list->parsed_mapping_rule->list->type);
-+    assert_string_equal("abc",
-+                     ctx->prio_list->rule_list->parsed_mapping_rule->list->val);
-+    assert_int_equal(comp_template,
-+              ctx->prio_list->rule_list->parsed_mapping_rule->list->next->type);
-+    assert_string_equal("issuer_dn",
-+               ctx->prio_list->rule_list->parsed_mapping_rule->list->next->val);
-+    talloc_free(ctx);
-+
-+    ret = sss_certmap_init(NULL, ext_debug, NULL, &ctx);
-+    assert_int_equal(ret, EOK);
-+    assert_non_null(ctx);
-+    assert_null(ctx->prio_list);
-+    ret = sss_certmap_add_rule(ctx, 1, NULL, "{issuer_dn}a:b{{c}}", NULL);
-+    assert_int_equal(ret, 0);
-+    assert_non_null(ctx->prio_list);
-+    assert_non_null(ctx->prio_list->rule_list);
-+    assert_non_null(ctx->prio_list->rule_list->parsed_mapping_rule);
-+    assert_non_null(ctx->prio_list->rule_list->parsed_mapping_rule->list);
-+    assert_int_equal(comp_template,
-+                    ctx->prio_list->rule_list->parsed_mapping_rule->list->type);
-+    assert_string_equal("issuer_dn",
-+                     ctx->prio_list->rule_list->parsed_mapping_rule->list->val);
-+    assert_int_equal(comp_string,
-+              ctx->prio_list->rule_list->parsed_mapping_rule->list->next->type);
-+    assert_string_equal("a:b{c}",
-+               ctx->prio_list->rule_list->parsed_mapping_rule->list->next->val);
-+    talloc_free(ctx);
-+
-+    ret = sss_certmap_init(NULL, ext_debug, NULL, &ctx);
-+    assert_int_equal(ret, EOK);
-+    assert_non_null(ctx);
-+    assert_null(ctx->prio_list);
-+    ret = sss_certmap_add_rule(ctx, 1, NULL, "LDAP:{issuer_dn}{subject_dn}",
-+                               NULL);
-+    assert_int_equal(ret, 0);
-+    assert_non_null(ctx->prio_list);
-+    assert_non_null(ctx->prio_list->rule_list);
-+    assert_non_null(ctx->prio_list->rule_list->parsed_mapping_rule);
-+    assert_non_null(ctx->prio_list->rule_list->parsed_mapping_rule->list);
-+    assert_int_equal(comp_template,
-+                    ctx->prio_list->rule_list->parsed_mapping_rule->list->type);
-+    assert_string_equal("issuer_dn",
-+                     ctx->prio_list->rule_list->parsed_mapping_rule->list->val);
-+    assert_int_equal(comp_template,
-+              ctx->prio_list->rule_list->parsed_mapping_rule->list->next->type);
-+    assert_string_equal("subject_dn",
-+               ctx->prio_list->rule_list->parsed_mapping_rule->list->next->val);
-+    talloc_free(ctx);
-+}
-+
-+#define TEST_CERT_BIN \
-+            "\\30\\82\\04\\09\\30\\82\\02\\f1\\a0\\03\\02\\01\\02\\02\\01\\09" \
-+            "\\30\\0d\\06\\09\\2a\\86\\48\\86\\f7\\0d\\01\\01\\0b\\05\\00\\30" \
-+            "\\34\\31\\12\\30\\10\\06\\03\\55\\04\\0a\\0c\\09\\49\\50\\41\\2e" \
-+            "\\44\\45\\56\\45\\4c\\31\\1e\\30\\1c\\06\\03\\55\\04\\03\\0c\\15" \
-+            "\\43\\65\\72\\74\\69\\66\\69\\63\\61\\74\\65\\20\\41\\75\\74\\68" \
-+            "\\6f\\72\\69\\74\\79\\30\\1e\\17\\0d\\31\\35\\30\\34\\32\\38\\31" \
-+            "\\30\\32\\31\\31\\31\\5a\\17\\0d\\31\\37\\30\\34\\32\\38\\31\\30" \
-+            "\\32\\31\\31\\31\\5a\\30\\32\\31\\12\\30\\10\\06\\03\\55\\04\\0a" \
-+            "\\0c\\09\\49\\50\\41\\2e\\44\\45\\56\\45\\4c\\31\\1c\\30\\1a\\06" \
-+            "\\03\\55\\04\\03\\0c\\13\\69\\70\\61\\2d\\64\\65\\76\\65\\6c\\2e" \
-+            "\\69\\70\\61\\2e\\64\\65\\76\\65\\6c\\30\\82\\01\\22\\30\\0d\\06" \
-+            "\\09\\2a\\86\\48\\86\\f7\\0d\\01\\01\\01\\05\\00\\03\\82\\01\\0f" \
-+            "\\00\\30\\82\\01\\0a\\02\\82\\01\\01\\00\\b2\\32\\92\\ab\\47\\b8" \
-+            "\\0c\\13\\54\\4a\\1f\\1e\\29\\06\\ff\\d0\\50\\cb\\f7\\5f\\79\\91" \
-+            "\\65\\b1\\39\\01\\83\\6a\\ad\\9e\\77\\3b\\f3\\0d\\d7\\b9\\f6\\dc" \
-+            "\\9e\\4a\\49\\a7\\d0\\66\\72\\cc\\bf\\77\\d6\\de\\a9\\fe\\67\\96" \
-+            "\\cc\\49\\f1\\37\\23\\2e\\c4\\50\\f4\\eb\\ba\\62\\d4\\23\\4d\\f3" \
-+            "\\37\\38\\82\\ee\\3b\\3f\\2c\\d0\\80\\9b\\17\\aa\\9b\\eb\\a6\\dd" \
-+            "\\f6\\15\\ff\\06\\b2\\ce\\ff\\df\\8a\\9e\\95\\85\\49\\1f\\84\\fd" \
-+            "\\81\\26\\ce\\06\\32\\0d\\36\\ca\\7c\\15\\81\\68\\6b\\8f\\3e\\b3" \
-+            "\\a2\\fc\\ae\\af\\c2\\44\\58\\15\\95\\40\\fc\\56\\19\\91\\80\\ed" \
-+            "\\42\\11\\66\\04\\ef\\3c\\e0\\76\\33\\4b\\83\\fa\\7e\\b4\\47\\dc" \
-+            "\\fb\\ed\\46\\a5\\8d\\0a\\66\\87\\a5\\ef\\7b\\74\\62\\ac\\be\\73" \
-+            "\\36\\c9\\b4\\fe\\20\\c4\\81\\f3\\fe\\78\\19\\a8\\d0\\af\\7f\\81" \
-+            "\\72\\24\\61\\d9\\76\\93\\e3\\0b\\d2\\4f\\19\\17\\33\\57\\d4\\82" \
-+            "\\b0\\f1\\a8\\03\\f6\\01\\99\\a9\\b8\\8c\\83\\c9\\ba\\19\\87\\ea" \
-+            "\\d6\\3b\\06\\eb\\4c\\f7\\f1\\e5\\28\\a9\\10\\b6\\46\\de\\e1\\e1" \
-+            "\\3f\\c1\\cc\\72\\be\\2a\\43\\c6\\f6\\d0\\b5\\a0\\c4\\24\\6e\\4f" \
-+            "\\bd\\ec\\22\\8a\\07\\11\\3d\\f9\\d3\\15\\02\\03\\01\\00\\01\\a3" \
-+            "\\82\\01\\26\\30\\82\\01\\22\\30\\1f\\06\\03\\55\\1d\\23\\04\\18" \
-+            "\\30\\16\\80\\14\\f2\\9d\\42\\4e\\0f\\c4\\48\\25\\58\\2f\\1c\\ce" \
-+            "\\0f\\a1\\3f\\22\\c8\\55\\c8\\91\\30\\3b\\06\\08\\2b\\06\\01\\05" \
-+            "\\05\\07\\01\\01\\04\\2f\\30\\2d\\30\\2b\\06\\08\\2b\\06\\01\\05" \
-+            "\\05\\07\\30\\01\\86\\1f\\68\\74\\74\\70\\3a\\2f\\2f\\69\\70\\61" \
-+            "\\2d\\63\\61\\2e\\69\\70\\61\\2e\\64\\65\\76\\65\\6c\\2f\\63\\61" \
-+            "\\2f\\6f\\63\\73\\70\\30\\0e\\06\\03\\55\\1d\\0f\\01\\01\\ff\\04" \
-+            "\\04\\03\\02\\04\\f0\\30\\1d\\06\\03\\55\\1d\\25\\04\\16\\30\\14" \
-+            "\\06\\08\\2b\\06\\01\\05\\05\\07\\03\\01\\06\\08\\2b\\06\\01\\05" \
-+            "\\05\\07\\03\\02\\30\\74\\06\\03\\55\\1d\\1f\\04\\6d\\30\\6b\\30" \
-+            "\\69\\a0\\31\\a0\\2f\\86\\2d\\68\\74\\74\\70\\3a\\2f\\2f\\69\\70" \
-+            "\\61\\2d\\63\\61\\2e\\69\\70\\61\\2e\\64\\65\\76\\65\\6c\\2f\\69" \
-+            "\\70\\61\\2f\\63\\72\\6c\\2f\\4d\\61\\73\\74\\65\\72\\43\\52\\4c" \
-+            "\\2e\\62\\69\\6e\\a2\\34\\a4\\32\\30\\30\\31\\0e\\30\\0c\\06\\03" \
-+            "\\55\\04\\0a\\0c\\05\\69\\70\\61\\63\\61\\31\\1e\\30\\1c\\06\\03" \
-+            "\\55\\04\\03\\0c\\15\\43\\65\\72\\74\\69\\66\\69\\63\\61\\74\\65" \
-+            "\\20\\41\\75\\74\\68\\6f\\72\\69\\74\\79\\30\\1d\\06\\03\\55\\1d" \
-+            "\\0e\\04\\16\\04\\14\\2d\\2b\\3f\\cb\\f5\\b2\\ff\\32\\2c\\a8\\c2" \
-+            "\\1c\\dd\\bd\\8c\\80\\1e\\dd\\31\\82\\30\\0d\\06\\09\\2a\\86\\48" \
-+            "\\86\\f7\\0d\\01\\01\\0b\\05\\00\\03\\82\\01\\01\\00\\9a\\47\\2e" \
-+            "\\50\\a7\\4d\\1d\\53\\0f\\c9\\71\\42\\0c\\e5\\da\\7d\\49\\64\\e7" \
-+            "\\ab\\c8\\df\\df\\02\\c1\\87\\d1\\5b\\de\\da\\6f\\2b\\e4\\f0\\be" \
-+            "\\ba\\09\\df\\02\\85\\0b\\8a\\e6\\9b\\06\\7d\\69\\38\\6c\\72\\ff" \
-+            "\\4c\\7b\\2a\\0d\\3f\\23\\2f\\16\\46\\ff\\05\\93\\b0\\ea\\24\\28" \
-+            "\\d7\\12\\a1\\57\\b8\\59\\19\\25\\f3\\43\\0a\\d3\\fd\\0f\\37\\8d" \
-+            "\\b8\\ca\\15\\e7\\48\\8a\\a0\\c7\\c7\\4b\\7f\\01\\3c\\58\\d7\\37" \
-+            "\\e5\\ff\\7d\\2b\\01\\ac\\0d\\9f\\51\\6a\\e5\\40\\24\\e6\\5e\\55" \
-+            "\\0d\\f7\\b8\\2f\\42\\ac\\6d\\e5\\29\\6b\\c6\\0b\\a4\\bf\\19\\bd" \
-+            "\\39\\27\\ee\\fe\\c5\\b3\\db\\62\\d4\\be\\d2\\47\\ba\\96\\30\\5a" \
-+            "\\fd\\62\\00\\b8\\27\\5d\\2f\\3a\\94\\0b\\95\\35\\85\\40\\2c\\bc" \
-+            "\\67\\df\\8a\\f9\\f1\\7b\\19\\96\\3e\\42\\48\\13\\23\\04\\95\\a9" \
-+            "\\6b\\11\\33\\81\\47\\5a\\83\\72\\f6\\20\\fa\\8e\\41\\7b\\8f\\77" \
-+            "\\47\\7c\\c7\\5d\\46\\f4\\4f\\fd\\81\\0a\\ae\\39\\27\\b6\\6a\\26" \
-+            "\\63\\b1\\d3\\bf\\55\\83\\82\\9b\\36\\6c\\33\\64\\0f\\50\\c0\\55" \
-+            "\\94\\13\\c3\\85\\f4\\d5\\71\\65\\d0\\c0\\dd\\fc\\e6\\ec\\9c\\5b" \
-+            "\\f0\\11\\b5\\2c\\f3\\48\\c1\\36\\8c\\a2\\96\\48\\84"
-+
-+#define TEST_CERT2_BIN \
-+            "\\30\\82\\06\\98\\30\\82\\05\\80\\a0\\03\\02\\01\\02\\02\\0a\\61" \
-+            "\\22\\88\\c2\\00\\00\\00\\00\\02\\a6\\30\\0d\\06\\09\\2a\\86\\48" \
-+            "\\86\\f7\\0d\\01\\01\\05\\05\\00\\30\\45\\31\\15\\30\\13\\06\\0a" \
-+            "\\09\\92\\26\\89\\93\\f2\\2c\\64\\01\\19\\16\\05\\64\\65\\76\\65" \
-+            "\\6c\\31\\12\\30\\10\\06\\0a\\09\\92\\26\\89\\93\\f2\\2c\\64\\01" \
-+            "\\19\\16\\02\\61\\64\\31\\18\\30\\16\\06\\03\\55\\04\\03\\13\\0f" \
-+            "\\61\\64\\2d\\41\\44\\2d\\53\\45\\52\\56\\45\\52\\2d\\43\\41\\30" \
-+            "\\1e\\17\\0d\\31\\36\\31\\31\\31\\31\\31\\33\\35\\31\\31\\31\\5a" \
-+            "\\17\\0d\\31\\37\\31\\31\\31\\31\\31\\33\\35\\31\\31\\31\\5a\\30" \
-+            "\\70\\31\\15\\30\\13\\06\\0a\\09\\92\\26\\89\\93\\f2\\2c\\64\\01" \
-+            "\\19\\16\\05\\64\\65\\76\\65\\6c\\31\\12\\30\\10\\06\\0a\\09\\92" \
-+            "\\26\\89\\93\\f2\\2c\\64\\01\\19\\16\\02\\61\\64\\31\\0e\\30\\0c" \
-+            "\\06\\03\\55\\04\\03\\13\\05\\55\\73\\65\\72\\73\\31\\0c\\30\\0a" \
-+            "\\06\\03\\55\\04\\03\\13\\03\\74\\20\\75\\31\\25\\30\\23\\06\\09" \
-+            "\\2a\\86\\48\\86\\f7\\0d\\01\\09\\01\\16\\16\\74\\65\\73\\74\\2e" \
-+            "\\75\\73\\65\\72\\40\\65\\6d\\61\\69\\6c\\2e\\64\\6f\\6d\\61\\69" \
-+            "\\6e\\30\\82\\01\\22\\30\\0d\\06\\09\\2a\\86\\48\\86\\f7\\0d\\01" \
-+            "\\01\\01\\05\\00\\03\\82\\01\\0f\\00\\30\\82\\01\\0a\\02\\82\\01" \
-+            "\\01\\00\\9c\\cf\\36\\99\\de\\63\\74\\2b\\77\\25\\9e\\24\\d9\\77" \
-+            "\\4b\\5f\\98\\c0\\8c\\d7\\20\\91\\c0\\1c\\e8\\37\\45\\bf\\3c\\d9" \
-+            "\\33\\bd\\e9\\de\\c9\\5d\\d4\\cd\\06\\0a\\0d\\d4\\f1\\7c\\74\\5b" \
-+            "\\29\\d5\\66\\9c\\2c\\9f\\6b\\1a\\0f\\0d\\e6\\6c\\62\\a5\\41\\4f" \
-+            "\\c3\\a4\\88\\27\\11\\5d\\b7\\b1\\fb\\f8\\8d\\ee\\43\\8d\\93\\b5" \
-+            "\\8c\\b4\\34\\06\\f5\\e9\\2f\\5a\\26\\68\\d7\\43\\60\\82\\5e\\22" \
-+            "\\a7\\c6\\34\\40\\19\\a5\\8e\\f0\\58\\9f\\16\\2d\\43\\3f\\0c\\da" \
-+            "\\e2\\23\\f6\\09\\2a\\5e\\bd\\84\\27\\c8\\ab\\d5\\70\\f8\\3d\\9c" \
-+            "\\14\\c2\\c2\\a2\\77\\e8\\44\\73\\10\\01\\34\\40\\1f\\c6\\2f\\a0" \
-+            "\\70\\ee\\2f\\d5\\4b\\be\\4c\\c7\\45\\f7\\ac\\9c\\c3\\68\\5b\\1d" \
-+            "\\5a\\4b\\77\\65\\76\\e4\\b3\\92\\f4\\84\\0a\\9e\\6a\\9c\\c9\\53" \
-+            "\\42\\9f\\6d\\fe\\f9\\f5\\f2\\9a\\15\\50\\47\\ef\\f4\\06\\59\\c8" \
-+            "\\50\\48\\4b\\46\\95\\68\\25\\c5\\bd\\4f\\65\\34\\00\\fc\\31\\69" \
-+            "\\f8\\3e\\e0\\20\\83\\41\\27\\0b\\5c\\46\\98\\14\\f0\\07\\de\\02" \
-+            "\\17\\b1\\d2\\9c\\be\\1c\\0d\\56\\22\\1b\\02\\fe\\da\\69\\b9\\ef" \
-+            "\\91\\37\\39\\7f\\24\\da\\c4\\81\\5e\\82\\31\\2f\\98\\1d\\f7\\73" \
-+            "\\5b\\23\\02\\03\\01\\00\\01\\a3\\82\\03\\5d\\30\\82\\03\\59\\30" \
-+            "\\3d\\06\\09\\2b\\06\\01\\04\\01\\82\\37\\15\\07\\04\\30\\30\\2e" \
-+            "\\06\\26\\2b\\06\\01\\04\\01\\82\\37\\15\\08\\87\\85\\a1\\23\\84" \
-+            "\\c8\\b2\\26\\83\\9d\\9d\\21\\82\\d4\\a6\\1b\\86\\a3\\ba\\37\\81" \
-+            "\\10\\85\\89\\d5\\02\\d6\\8f\\24\\02\\01\\64\\02\\01\\02\\30\\29" \
-+            "\\06\\03\\55\\1d\\25\\04\\22\\30\\20\\06\\08\\2b\\06\\01\\05\\05" \
-+            "\\07\\03\\02\\06\\08\\2b\\06\\01\\05\\05\\07\\03\\04\\06\\0a\\2b" \
-+            "\\06\\01\\04\\01\\82\\37\\0a\\03\\04\\30\\0e\\06\\03\\55\\1d\\0f" \
-+            "\\01\\01\\ff\\04\\04\\03\\02\\05\\a0\\30\\35\\06\\09\\2b\\06\\01" \
-+            "\\04\\01\\82\\37\\15\\0a\\04\\28\\30\\26\\30\\0a\\06\\08\\2b\\06" \
-+            "\\01\\05\\05\\07\\03\\02\\30\\0a\\06\\08\\2b\\06\\01\\05\\05\\07" \
-+            "\\03\\04\\30\\0c\\06\\0a\\2b\\06\\01\\04\\01\\82\\37\\0a\\03\\04" \
-+            "\\30\\81\\94\\06\\09\\2a\\86\\48\\86\\f7\\0d\\01\\09\\0f\\04\\81" \
-+            "\\86\\30\\81\\83\\30\\0b\\06\\09\\60\\86\\48\\01\\65\\03\\04\\01" \
-+            "\\2a\\30\\0b\\06\\09\\60\\86\\48\\01\\65\\03\\04\\01\\2d\\30\\0b" \
-+            "\\06\\09\\60\\86\\48\\01\\65\\03\\04\\01\\16\\30\\0b\\06\\09\\60" \
-+            "\\86\\48\\01\\65\\03\\04\\01\\19\\30\\0b\\06\\09\\60\\86\\48\\01" \
-+            "\\65\\03\\04\\01\\02\\30\\0b\\06\\09\\60\\86\\48\\01\\65\\03\\04" \
-+            "\\01\\05\\30\\0a\\06\\08\\2a\\86\\48\\86\\f7\\0d\\03\\07\\30\\07" \
-+            "\\06\\05\\2b\\0e\\03\\02\\07\\30\\0e\\06\\08\\2a\\86\\48\\86\\f7" \
-+            "\\0d\\03\\02\\02\\02\\00\\80\\30\\0e\\06\\08\\2a\\86\\48\\86\\f7" \
-+            "\\0d\\03\\04\\02\\02\\02\\00\\30\\1d\\06\\03\\55\\1d\\0e\\04\\16" \
-+            "\\04\\14\\49\\ac\\ad\\e0\\65\\30\\c4\\ce\\a0\\09\\03\\5b\\ad\\4a" \
-+            "\\7b\\49\\5e\\c9\\6c\\b4\\30\\1f\\06\\03\\55\\1d\\23\\04\\18\\30" \
-+            "\\16\\80\\14\\62\\50\\b6\\8d\\a1\\e6\\2d\\91\\bf\\b0\\54\\4d\\8f" \
-+            "\\a8\\ca\\10\\ae\\b8\\dd\\54\\30\\81\\cc\\06\\03\\55\\1d\\1f\\04" \
-+            "\\81\\c4\\30\\81\\c1\\30\\81\\be\\a0\\81\\bb\\a0\\81\\b8\\86\\81" \
-+            "\\b5\\6c\\64\\61\\70\\3a\\2f\\2f\\2f\\43\\4e\\3d\\61\\64\\2d\\41" \
-+            "\\44\\2d\\53\\45\\52\\56\\45\\52\\2d\\43\\41\\2c\\43\\4e\\3d\\61" \
-+            "\\64\\2d\\73\\65\\72\\76\\65\\72\\2c\\43\\4e\\3d\\43\\44\\50\\2c" \
-+            "\\43\\4e\\3d\\50\\75\\62\\6c\\69\\63\\25\\32\\30\\4b\\65\\79\\25" \
-+            "\\32\\30\\53\\65\\72\\76\\69\\63\\65\\73\\2c\\43\\4e\\3d\\53\\65" \
-+            "\\72\\76\\69\\63\\65\\73\\2c\\43\\4e\\3d\\43\\6f\\6e\\66\\69\\67" \
-+            "\\75\\72\\61\\74\\69\\6f\\6e\\2c\\44\\43\\3d\\61\\64\\2c\\44\\43" \
-+            "\\3d\\64\\65\\76\\65\\6c\\3f\\63\\65\\72\\74\\69\\66\\69\\63\\61" \
-+            "\\74\\65\\52\\65\\76\\6f\\63\\61\\74\\69\\6f\\6e\\4c\\69\\73\\74" \
-+            "\\3f\\62\\61\\73\\65\\3f\\6f\\62\\6a\\65\\63\\74\\43\\6c\\61\\73" \
-+            "\\73\\3d\\63\\52\\4c\\44\\69\\73\\74\\72\\69\\62\\75\\74\\69\\6f" \
-+            "\\6e\\50\\6f\\69\\6e\\74\\30\\81\\be\\06\\08\\2b\\06\\01\\05\\05" \
-+            "\\07\\01\\01\\04\\81\\b1\\30\\81\\ae\\30\\81\\ab\\06\\08\\2b\\06" \
-+            "\\01\\05\\05\\07\\30\\02\\86\\81\\9e\\6c\\64\\61\\70\\3a\\2f\\2f" \
-+            "\\2f\\43\\4e\\3d\\61\\64\\2d\\41\\44\\2d\\53\\45\\52\\56\\45\\52" \
-+            "\\2d\\43\\41\\2c\\43\\4e\\3d\\41\\49\\41\\2c\\43\\4e\\3d\\50\\75" \
-+            "\\62\\6c\\69\\63\\25\\32\\30\\4b\\65\\79\\25\\32\\30\\53\\65\\72" \
-+            "\\76\\69\\63\\65\\73\\2c\\43\\4e\\3d\\53\\65\\72\\76\\69\\63\\65" \
-+            "\\73\\2c\\43\\4e\\3d\\43\\6f\\6e\\66\\69\\67\\75\\72\\61\\74\\69" \
-+            "\\6f\\6e\\2c\\44\\43\\3d\\61\\64\\2c\\44\\43\\3d\\64\\65\\76\\65" \
-+            "\\6c\\3f\\63\\41\\43\\65\\72\\74\\69\\66\\69\\63\\61\\74\\65\\3f" \
-+            "\\62\\61\\73\\65\\3f\\6f\\62\\6a\\65\\63\\74\\43\\6c\\61\\73\\73" \
-+            "\\3d\\63\\65\\72\\74\\69\\66\\69\\63\\61\\74\\69\\6f\\6e\\41\\75" \
-+            "\\74\\68\\6f\\72\\69\\74\\79\\30\\3f\\06\\03\\55\\1d\\11\\04\\38" \
-+            "\\30\\36\\a0\\1c\\06\\0a\\2b\\06\\01\\04\\01\\82\\37\\14\\02\\03" \
-+            "\\a0\\0e\\0c\\0c\\74\\75\\31\\40\\61\\64\\2e\\64\\65\\76\\65\\6c" \
-+            "\\81\\16\\74\\65\\73\\74\\2e\\75\\73\\65\\72\\40\\65\\6d\\61\\69" \
-+            "\\6c\\2e\\64\\6f\\6d\\61\\69\\6e\\30\\0d\\06\\09\\2a\\86\\48\\86" \
-+            "\\f7\\0d\\01\\01\\05\\05\\00\\03\\82\\01\\01\\00\\41\\45\\0a\\6d" \
-+            "\\bb\\7f\\5c\\07\\0c\\c9\\b0\\39\\55\\6d\\7c\\b5\\02\\cd\\e8\\b2" \
-+            "\\e5\\02\\94\\77\\60\\db\\d1\\af\\1d\\db\\44\\5f\\ce\\83\\db\\80" \
-+            "\\2e\\e2\\b2\\08\\25\\82\\14\\cb\\48\\95\\20\\13\\6c\\a9\\aa\\f8" \
-+            "\\31\\56\\ed\\c0\\3b\\d4\\ae\\2e\\e3\\8f\\05\\fc\\ab\\5f\\2a\\69" \
-+            "\\23\\bc\\b8\\8c\\ec\\2d\\a9\\0b\\86\\95\\73\\73\\db\\17\\ce\\c6" \
-+            "\\ae\\c5\\b4\\c1\\25\\87\\3b\\67\\43\\9e\\87\\5a\\e6\\b9\\a0\\28" \
-+            "\\12\\3d\\a8\\2e\\d7\\5e\\ef\\65\\2d\\e6\\a5\\67\\84\\ac\\fd\\31" \
-+            "\\c1\\78\\d8\\72\\51\\a2\\88\\55\\0f\\97\\47\\93\\07\\ea\\8a\\53" \
-+            "\\27\\4e\\34\\54\\34\\1f\\a0\\6a\\03\\44\\fb\\23\\61\\8e\\87\\8e" \
-+            "\\3c\\d0\\8f\\ae\\e4\\cf\\ee\\65\\a8\\ba\\96\\68\\08\\1c\\60\\e2" \
-+            "\\4e\\11\\a3\\74\\b8\\a5\\4e\\ea\\6a\\82\\4c\\c2\\4d\\63\\8e\\9f" \
-+            "\\7c\\2f\\a8\\c0\\62\\f8\\f7\\d9\\25\\c4\\91\\ab\\4d\\6a\\44\\af" \
-+            "\\75\\93\\53\\03\\a4\\99\\c8\\cd\\91\\89\\60\\75\\30\\99\\76\\05" \
-+            "\\5a\\a0\\03\\a7\\a1\\2c\\03\\04\\8f\\d4\\5a\\31\\52\\28\\5a\\e6" \
-+            "\\a2\\d3\\43\\21\\5b\\dc\\a2\\1d\\55\\a9\\48\\c5\\c4\\aa\\f3\\8b" \
-+            "\\e6\\3e\\75\\96\\e4\\3e\\64\\af\\e8\\a7\\6a\\b6"
-+
-+static void test_sss_certmap_get_search_filter(void **state)
-+{
-+    int ret;
-+    struct sss_certmap_ctx *ctx;
-+    char *filter;
-+    char **domains;
-+    const char *dom_list[] = {"test.dom", NULL};
-+
-+    ret = sss_certmap_init(NULL, ext_debug, NULL, &ctx);
-+    assert_int_equal(ret, EOK);
-+    assert_non_null(ctx);
-+    assert_null(ctx->prio_list);
-+
-+    ret = sss_certmap_add_rule(ctx, 100,
-+                            "KRB5:<ISSUER>CN=Certificate Authority,O=IPA.DEVEL",
-+                            "LDAP:rule100=<I>{issuer_dn}<S>{subject_dn}", NULL);
-+    assert_int_equal(ret, 0);
-+
-+    ret = sss_certmap_get_search_filter(ctx, discard_const(test_cert_der),
-+                                        sizeof(test_cert_der),
-+                                        &filter, &domains);
-+    assert_int_equal(ret, 0);
-+    assert_non_null(filter);
-+    assert_string_equal(filter, "rule100=<I>CN=Certificate Authority,O=IPA.DEVEL"
-+                                "<S>CN=ipa-devel.ipa.devel,O=IPA.DEVEL");
-+    assert_null(domains);
-+
-+    ret = sss_certmap_add_rule(ctx, 99,
-+                            "KRB5:<ISSUER>CN=Certificate Authority,O=IPA.DEVEL",
-+                            "LDAP:rule99=<I>{issuer_dn}<S>{subject_dn}",
-+                            dom_list);
-+    assert_int_equal(ret, 0);
-+    ret = sss_certmap_get_search_filter(ctx, discard_const(test_cert_der),
-+                                        sizeof(test_cert_der),
-+                                        &filter, &domains);
-+    assert_int_equal(ret, 0);
-+    assert_non_null(filter);
-+    assert_string_equal(filter, "rule99=<I>CN=Certificate Authority,O=IPA.DEVEL"
-+                                "<S>CN=ipa-devel.ipa.devel,O=IPA.DEVEL");
-+    assert_non_null(domains);
-+    assert_string_equal(domains[0], "test.dom");
-+    assert_null(domains[1]);
-+
-+    ret = sss_certmap_add_rule(ctx, 98,
-+                            "KRB5:<ISSUER>CN=Certificate Authority,O=IPA.DEVEL",
-+                            "LDAP:rule98=userCertificate;binary={cert!bin}",
-+                            dom_list);
-+    assert_int_equal(ret, 0);
-+    ret = sss_certmap_get_search_filter(ctx, discard_const(test_cert_der),
-+                                        sizeof(test_cert_der),
-+                                        &filter, &domains);
-+    assert_int_equal(ret, 0);
-+    assert_non_null(filter);
-+    assert_string_equal(filter, "rule98=userCertificate;binary=" TEST_CERT_BIN);
-+    assert_non_null(domains);
-+    assert_string_equal(domains[0], "test.dom");
-+    assert_null(domains[1]);
-+
-+    ret = sss_certmap_add_rule(ctx, 97,
-+                            "KRB5:<ISSUER>CN=Certificate Authority,O=IPA.DEVEL",
-+                            "LDAP:rule97=<I>{issuer_dn!nss_x500}<S>{subject_dn}",
-+                            dom_list);
-+    assert_int_equal(ret, 0);
-+    ret = sss_certmap_get_search_filter(ctx, discard_const(test_cert_der),
-+                                        sizeof(test_cert_der),
-+                                        &filter, &domains);
-+    assert_int_equal(ret, 0);
-+    assert_non_null(filter);
-+    assert_string_equal(filter, "rule97=<I>O=IPA.DEVEL,CN=Certificate Authority"
-+                                "<S>CN=ipa-devel.ipa.devel,O=IPA.DEVEL");
-+    assert_non_null(domains);
-+    assert_string_equal(domains[0], "test.dom");
-+    assert_null(domains[1]);
-+
-+    ret = sss_certmap_add_rule(ctx, 96,
-+                            "KRB5:<ISSUER>CN=Certificate Authority,O=IPA.DEVEL",
-+                            "LDAP:rule96=<I>{issuer_dn!nss_x500}<S>{subject_dn!nss_x500}",
-+                            dom_list);
-+    assert_int_equal(ret, 0);
-+    ret = sss_certmap_get_search_filter(ctx, discard_const(test_cert_der),
-+                                        sizeof(test_cert_der),
-+                                        &filter, &domains);
-+    assert_int_equal(ret, 0);
-+    assert_non_null(filter);
-+    assert_string_equal(filter, "rule96=<I>O=IPA.DEVEL,CN=Certificate Authority"
-+                                "<S>O=IPA.DEVEL,CN=ipa-devel.ipa.devel");
-+    assert_non_null(domains);
-+    assert_string_equal(domains[0], "test.dom");
-+    assert_null(domains[1]);
-+
-+    ret = sss_certmap_add_rule(ctx, 95,
-+                            "KRB5:<ISSUER>CN=Certificate Authority,O=IPA.DEVEL",
-+                            NULL, NULL);
-+    assert_int_equal(ret, 0);
-+    ret = sss_certmap_get_search_filter(ctx, discard_const(test_cert_der),
-+                                        sizeof(test_cert_der),
-+                                        &filter, &domains);
-+    assert_int_equal(ret, 0);
-+    assert_non_null(filter);
-+    assert_string_equal(filter, "(userCertificate;binary=" TEST_CERT_BIN ")");
-+    assert_null(domains);
-+
-+    ret = sss_certmap_add_rule(ctx, 94,
-+                      "KRB5:<ISSUER>CN=Certificate Authority,O=IPA.DEVEL",
-+                      "LDAP:rule94=<I>{issuer_dn!ad_x500}<S>{subject_dn!ad_x500}",
-+                            dom_list);
-+    assert_int_equal(ret, 0);
-+    ret = sss_certmap_get_search_filter(ctx, discard_const(test_cert_der),
-+                                        sizeof(test_cert_der),
-+                                        &filter, &domains);
-+    assert_int_equal(ret, 0);
-+    assert_non_null(filter);
-+    assert_string_equal(filter, "rule94=<I>O=IPA.DEVEL,CN=Certificate Authority"
-+                                "<S>O=IPA.DEVEL,CN=ipa-devel.ipa.devel");
-+    assert_non_null(domains);
-+    assert_string_equal(domains[0], "test.dom");
-+    assert_null(domains[1]);
-+
-+
-+    ret = sss_certmap_add_rule(ctx, 89, NULL,
-+                            "(rule89={subject_nt_principal})",
-+                            NULL);
-+    assert_int_equal(ret, 0);
-+    ret = sss_certmap_get_search_filter(ctx, discard_const(test_cert2_der),
-+                                        sizeof(test_cert2_der),
-+                                        &filter, &domains);
-+    assert_int_equal(ret, 0);
-+    assert_non_null(filter);
-+    assert_string_equal(filter, "(rule89=tu1@ad.devel)");
-+    assert_null(domains);
-+
-+    ret = sss_certmap_add_rule(ctx, 88, NULL,
-+                            "(rule88={subject_nt_principal.short_name})",
-+                            NULL);
-+    assert_int_equal(ret, 0);
-+    ret = sss_certmap_get_search_filter(ctx, discard_const(test_cert2_der),
-+                                        sizeof(test_cert2_der),
-+                                        &filter, &domains);
-+    assert_int_equal(ret, 0);
-+    assert_non_null(filter);
-+    assert_string_equal(filter, "(rule88=tu1)");
-+    assert_null(domains);
-+
-+    ret = sss_certmap_add_rule(ctx, 87, NULL,
-+                          "LDAP:rule87=<I>{issuer_dn!nss_x500}<S>{subject_dn!nss_x500}",
-+                          NULL);
-+    assert_int_equal(ret, 0);
-+    ret = sss_certmap_get_search_filter(ctx, discard_const(test_cert2_der),
-+                                        sizeof(test_cert2_der),
-+                                        &filter, &domains);
-+    assert_int_equal(ret, 0);
-+    assert_non_null(filter);
-+    assert_string_equal(filter, "rule87=<I>DC=devel,DC=ad,CN=ad-AD-SERVER-CA"
-+                  "<S>DC=devel,DC=ad,CN=Users,CN=t u,E=test.user@email.domain");
-+    assert_null(domains);
-+
-+    ret = sss_certmap_add_rule(ctx, 86, NULL,
-+                      "LDAP:rule86=<I>{issuer_dn!ad_x500}<S>{subject_dn!ad_x500}",
-+                      NULL);
-+    assert_int_equal(ret, 0);
-+    ret = sss_certmap_get_search_filter(ctx, discard_const(test_cert2_der),
-+                                        sizeof(test_cert2_der),
-+                                        &filter, &domains);
-+    assert_int_equal(ret, 0);
-+    assert_non_null(filter);
-+    assert_string_equal(filter, "rule86=<I>DC=devel,DC=ad,CN=ad-AD-SERVER-CA"
-+                  "<S>DC=devel,DC=ad,CN=Users,CN=t u,E=test.user@email.domain");
-+    assert_null(domains);
-+
-+
-+    sss_certmap_free_ctx(ctx);
-+
-+    /* check defaults when no rules are added yet */
-+    ret = sss_certmap_init(NULL, ext_debug, NULL, &ctx);
-+    assert_int_equal(ret, EOK);
-+    assert_non_null(ctx);
-+    assert_null(ctx->prio_list);
-+    ret = sss_certmap_get_search_filter(ctx, discard_const(test_cert2_der),
-+                                        sizeof(test_cert2_der),
-+                                        &filter, &domains);
-+    assert_int_equal(ret, 0);
-+    assert_non_null(filter);
-+    assert_string_equal(filter, "(userCertificate;binary=" TEST_CERT2_BIN")");
-+    assert_null(domains);
-+}
-+
-+int main(int argc, const char *argv[])
-+{
-+    int rv;
-+    poptContext pc;
-+    int opt;
-+    struct poptOption long_options[] = {
-+        POPT_AUTOHELP
-+        SSSD_DEBUG_OPTS
-+        POPT_TABLEEND
-+    };
-+
-+    const struct CMUnitTest tests[] = {
-+        cmocka_unit_test(test_sss_certmap_init),
-+        cmocka_unit_test(test_sss_certmap_add_rule),
-+        cmocka_unit_test(test_sss_certmap_add_matching_rule),
-+        cmocka_unit_test(test_check_ad_attr_name),
-+        cmocka_unit_test(test_sss_cert_get_content),
-+        cmocka_unit_test(test_sss_cert_get_content_2),
-+        cmocka_unit_test(test_sss_certmap_match_cert),
-+        cmocka_unit_test(test_sss_certmap_add_mapping_rule),
-+        cmocka_unit_test(test_sss_certmap_get_search_filter),
-+    };
-+
-+    /* Set debug level to invalid value so we can deside if -d 0 was used. */
-+    debug_level = SSSDBG_INVALID;
-+
-+    pc = poptGetContext(argv[0], argc, argv, long_options, 0);
-+    while((opt = poptGetNextOpt(pc)) != -1) {
-+        switch(opt) {
-+        default:
-+            fprintf(stderr, "\nInvalid option %s: %s\n\n",
-+                    poptBadOption(pc, 0), poptStrerror(opt));
-+            poptPrintUsage(pc, stderr, 0);
-+            return 1;
-+        }
-+    }
-+    poptFreeContext(pc);
-+
-+    DEBUG_CLI_INIT(debug_level);
-+
-+#ifdef HAVE_NSS
-+    nspr_nss_init();
-+#endif
-+
-+    tests_set_cwd();
-+    rv = cmocka_run_group_tests(tests, NULL, NULL);
-+
-+#ifdef HAVE_NSS
-+    /* Cleanup NSS and NSPR to make valgrind happy. */
-+    nspr_nss_cleanup();
-+#endif
-+
-+    return rv;
-+}
-diff --git a/src/tests/dlopen-tests.c b/src/tests/dlopen-tests.c
-index 419857cc739d197493e46629d00aa5fb6cfde824..3914317de90f870fab42d2d72d8e2eb5ea8d9e14 100644
---- a/src/tests/dlopen-tests.c
-+++ b/src/tests/dlopen-tests.c
-@@ -45,6 +45,7 @@ struct so {
-     { "libsss_idmap.so", { LIBPFX"libsss_idmap.so", NULL } },
-     { "libsss_nss_idmap.so", { LIBPFX"libsss_nss_idmap.so", NULL } },
-     { "libnss_sss.so", { LIBPFX"libnss_sss.so", NULL } },
-+    { "libsss_certmap.so", { LIBPFX"libsss_certmap.so", NULL } },
-     { "pam_sss.so", { LIBPFX"pam_sss.so", NULL } },
- #ifdef BUILD_LIBWBCLIENT
-     { "libwbclient.so", { LIBPFX"libwbclient.so", NULL } },
--- 
-2.9.3
-
diff --git a/SOURCES/0005-CONFDB-Remove-the-obsolete-option-magic_private_grou.patch b/SOURCES/0005-CONFDB-Remove-the-obsolete-option-magic_private_grou.patch
new file mode 100644
index 0000000..017e97c
--- /dev/null
+++ b/SOURCES/0005-CONFDB-Remove-the-obsolete-option-magic_private_grou.patch
@@ -0,0 +1,33 @@
+From 8e4858a044391cdfd7b6eae327daf043225c4536 Mon Sep 17 00:00:00 2001
+From: Jakub Hrozek <jhrozek@redhat.com>
+Date: Tue, 3 Oct 2017 12:36:02 +0200
+Subject: [PATCH 05/21] CONFDB: Remove the obsolete option magic_private_groups
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Since this confdb definition was completely unused across the codebase,
+this patch just removes the definition.
+
+Reviewed-by: Pavel Březina <pbrezina@redhat.com>
+Reviewed-by: Fabiano Fidêncio <fidencio@redhat.com>
+(cherry picked from commit 8fab9d6fa88824b20d3febe697147c407d31c160)
+---
+ src/confdb/confdb.h | 1 -
+ 1 file changed, 1 deletion(-)
+
+diff --git a/src/confdb/confdb.h b/src/confdb/confdb.h
+index 2539b906993edbceb38aac9265e04deed69cf2e4..1471949623e9dd7a8536e3ac3048a10227a5d857 100644
+--- a/src/confdb/confdb.h
++++ b/src/confdb/confdb.h
+@@ -197,7 +197,6 @@
+                                  "cache_credentials_minimal_first_factor_length"
+ #define CONFDB_DEFAULT_CACHE_CREDS_MIN_FF_LENGTH 8
+ #define CONFDB_DOMAIN_LEGACY_PASS "store_legacy_passwords"
+-#define CONFDB_DOMAIN_MPG "magic_private_groups"
+ #define CONFDB_DOMAIN_AUTO_UPG "auto_private_groups"
+ #define CONFDB_DOMAIN_FQ "use_fully_qualified_names"
+ #define CONFDB_DOMAIN_ENTRY_CACHE_TIMEOUT "entry_cache_timeout"
+-- 
+2.13.5
+
diff --git a/SOURCES/0005-certmap-add-placeholder-for-OpenSSL-implementation.patch b/SOURCES/0005-certmap-add-placeholder-for-OpenSSL-implementation.patch
deleted file mode 100644
index 99a2bd8..0000000
--- a/SOURCES/0005-certmap-add-placeholder-for-OpenSSL-implementation.patch
+++ /dev/null
@@ -1,151 +0,0 @@
-From b1336bdfeacf904c8fdec04e06d8b90ef9ad15b3 Mon Sep 17 00:00:00 2001
-From: Sumit Bose <sbose@redhat.com>
-Date: Wed, 15 Mar 2017 10:57:09 +0100
-Subject: [PATCH 05/15] certmap: add placeholder for OpenSSL implementation
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
-Reviewed-by: Lukáš Slebodník <lslebodn@redhat.com>
----
- Makefile.am                               | 30 +++++++++++++++++++++--------
- src/lib/certmap/sss_cert_content_crypto.c | 32 +++++++++++++++++++++++++++++++
- src/lib/certmap/sss_certmap_int.h         |  8 +++++---
- 3 files changed, 59 insertions(+), 11 deletions(-)
- create mode 100644 src/lib/certmap/sss_cert_content_crypto.c
-
-diff --git a/Makefile.am b/Makefile.am
-index 8ca12c10d2713b6a72361d84b25486500c79f407..7947b7a5fbe3ca1034baac1c13c53300994b1bf8 100644
---- a/Makefile.am
-+++ b/Makefile.am
-@@ -278,9 +278,12 @@ if HAVE_CMOCKA
-         simple-access-tests \
-         krb5_common_test \
-         test_iobuf \
--        sss_certmap_test \
-         $(NULL)
- 
-+if HAVE_NSS
-+non_interactive_cmocka_based_tests +=  sss_certmap_test
-+endif #HAVE_NSS
-+
- if HAVE_LIBRESOLV
- non_interactive_cmocka_based_tests += test_resolv_fake
- endif   # HAVE_LIBRESOLV
-@@ -1715,7 +1718,6 @@ sssd_check_socket_activated_responders_LDADD = \
-     $(NULL)
- endif
- 
--if HAVE_NSS
- pkgconfig_DATA += src/lib/certmap/sss_certmap.pc
- libsss_certmap_la_DEPENDENCIES = src/lib/certmap/sss_certmap.exports
- libsss_certmap_la_SOURCES = \
-@@ -1726,26 +1728,38 @@ libsss_certmap_la_SOURCES = \
-     src/lib/certmap/sss_certmap_ldap_mapping.c \
-     src/util/util_ext.c \
-     src/util/cert/cert_common.c \
--    src/util/crypto/nss/nss_base64.c \
--    src/util/cert/nss/cert.c \
--    src/util/crypto/nss/nss_util.c \
-     $(NULL)
- libsss_certmap_la_CFLAGS = \
-     $(AM_CFLAGS) \
-     $(TALLOC_CFLAGS) \
--    $(NSS_CFLAGS) \
-     $(NULL)
- libsss_certmap_la_LIBADD = \
-     $(TALLOC_LIBS) \
--    $(NSS_LIBS) \
-     $(NULL)
- libsss_certmap_la_LDFLAGS = \
-     -Wl,--version-script,$(srcdir)/src/lib/certmap/sss_certmap.exports \
-     -version-info 0:0:0
- 
-+if HAVE_NSS
-+libsss_certmap_la_SOURCES += \
-+    src/util/crypto/nss/nss_base64.c \
-+    src/util/cert/nss/cert.c \
-+    src/util/crypto/nss/nss_util.c \
-+    $(NULL)
-+libsss_certmap_la_CFLAGS += $(NSS_CFLAGS)
-+libsss_certmap_la_LIBADD += $(NSS_LIBS)
-+else
-+libsss_certmap_la_SOURCES += \
-+    src/util/crypto/libcrypto/crypto_base64.c \
-+    src/util/cert/libcrypto/cert.c \
-+    $(NULL)
-+
-+libsss_certmap_la_CFLAGS += $(CRYPTO_CFLAGS)
-+libsss_certmap_la_LIBADD += $(CRYPTO_LIBS)
-+endif
-+
- dist_noinst_DATA += src/lib/certmap/sss_certmap.exports
- dist_noinst_HEADERS += src/lib/certmap/sss_certmap_int.h
--endif
- 
- #################
- # Feature Tests #
-diff --git a/src/lib/certmap/sss_cert_content_crypto.c b/src/lib/certmap/sss_cert_content_crypto.c
-new file mode 100644
-index 0000000000000000000000000000000000000000..bddcf9bce986bd986aa0aa5f16a0744a97ab36d6
---- /dev/null
-+++ b/src/lib/certmap/sss_cert_content_crypto.c
-@@ -0,0 +1,32 @@
-+/*
-+   SSSD - certificate handling utils - OpenSSL version
-+   The calls defined here should be useable outside of SSSD as well, e.g. in
-+   libsss_certmap.
-+
-+   Copyright (C) Sumit Bose <sbose@redhat.com> 2017
-+
-+   This program is free software; you can redistribute it and/or modify
-+   it under the terms of the GNU General Public License as published by
-+   the Free Software Foundation; either version 3 of the License, or
-+   (at your option) any later version.
-+
-+   This program is distributed in the hope that it will be useful,
-+   but WITHOUT ANY WARRANTY; without even the implied warranty of
-+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+   GNU General Public License for more details.
-+
-+   You should have received a copy of the GNU General Public License
-+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
-+*/
-+
-+#include <errno.h>
-+
-+#include "lib/certmap/sss_certmap.h"
-+#include "lib/certmap/sss_certmap_int.h"
-+
-+int sss_cert_get_content(TALLOC_CTX *mem_ctx,
-+                         const uint8_t *der_blob, size_t der_size,
-+                         struct sss_cert_content **content)
-+{
-+    return EINVAL;
-+}
-diff --git a/src/lib/certmap/sss_certmap_int.h b/src/lib/certmap/sss_certmap_int.h
-index 28f1c596cfb5e78077b6a8e9baefa88b4900a022..0b4cda73639be9b323ac3388f97be90bc1a771f2 100644
---- a/src/lib/certmap/sss_certmap_int.h
-+++ b/src/lib/certmap/sss_certmap_int.h
-@@ -22,12 +22,14 @@
-     along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
- 
--#include <sys/types.h>
--#include <regex.h>
--
- #ifndef __SSS_CERTMAP_INT_H__
- #define __SSS_CERTMAP_INT_H__
- 
-+#include <sys/types.h>
-+#include <regex.h>
-+#include <stdint.h>
-+#include <talloc.h>
-+
- #define CM_DEBUG(cm_ctx, format, ...) do { \
-     if (cm_ctx != NULL && cm_ctx->debug != NULL) { \
-         cm_ctx->debug(cm_ctx->debug_priv, __FILE__, __LINE__, __FUNCTION__, \
--- 
-2.9.3
-
diff --git a/SOURCES/0006-SDAP-Allow-the-mpg-flag-for-the-main-domain.patch b/SOURCES/0006-SDAP-Allow-the-mpg-flag-for-the-main-domain.patch
new file mode 100644
index 0000000..29bb20b
--- /dev/null
+++ b/SOURCES/0006-SDAP-Allow-the-mpg-flag-for-the-main-domain.patch
@@ -0,0 +1,167 @@
+From c28d61203655dd41cd8eb69752e435d3241e63b2 Mon Sep 17 00:00:00 2001
+From: Jakub Hrozek <jhrozek@redhat.com>
+Date: Tue, 3 Oct 2017 12:34:49 +0200
+Subject: [PATCH 06/21] SDAP: Allow the mpg flag for the main domain
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+This commit allows saving the users in the MPG domain in the SDAP
+layer.
+
+The commit contains the following changes:
+    - abstracts the change where if the primary GID exists in the original
+      object, it is saved instead as the SYSDB_PRIMARY_GROUP_GIDNUM attribute,
+      which will allow the original primary GID to be exposed as a
+      secondary group
+
+    - if the primary GID does not exist, no SYSDB_PRIMARY_GROUP_GIDNUM
+      is added. This will allow to handle LDAP objects that only contain
+      the UID but no GID. Since this is a new use-case, a test is added
+      later
+
+    - a branch that handles the above is added to sdap_save_user() also
+      for joined domains that set the MPG flag. Previously, only
+      subdomains were handled.
+
+    - to allow passing GID=0 to the sysdb layer, the range check is
+      relaxed.
+
+Related:
+    https://pagure.io/SSSD/sssd/issue/1872
+
+Reviewed-by: Fabiano Fidêncio <fidencio@redhat.com>
+Reviewed-by: Pavel Březina <pbrezina@redhat.com>
+(cherry picked from commit cdb74b2cc6cc3fe52969712907c9eb4026c7a44f)
+---
+ src/providers/ldap/sdap_async_users.c | 83 +++++++++++++++++++++++++++++++----
+ 1 file changed, 75 insertions(+), 8 deletions(-)
+
+diff --git a/src/providers/ldap/sdap_async_users.c b/src/providers/ldap/sdap_async_users.c
+index 09d096e84cac6c9d52bcde0e1587c47dbd88b504..7338b4a15694b1d0a16723990130a23a7280af5f 100644
+--- a/src/providers/ldap/sdap_async_users.c
++++ b/src/providers/ldap/sdap_async_users.c
+@@ -136,6 +136,38 @@ static errno_t sdap_set_non_posix_flag(struct sysdb_attrs *attrs,
+     return EOK;
+ }
+ 
++static int sdap_user_set_mpg(struct sysdb_attrs *user_attrs,
++                             gid_t *_gid)
++{
++    errno_t ret;
++
++    if (_gid == NULL) {
++        return EINVAL;
++    }
++
++    if (*_gid == 0) {
++        /* The original entry had no GID number. This is OK, we just won't add
++         * the SYSDB_PRIMARY_GROUP_GIDNUM attribute
++         */
++        return EOK;
++    }
++
++    ret = sysdb_attrs_add_uint32(user_attrs,
++                                 SYSDB_PRIMARY_GROUP_GIDNUM,
++                                 (uint32_t) *_gid);
++    if (ret != EOK) {
++        DEBUG(SSSDBG_OP_FAILURE, "sysdb_attrs_add_uint32 failed.\n");
++        return ret;
++    }
++
++    /* We won't really store gidNumber=0, but the zero value tells
++     * the sysdb layer that no GID is set, which sysdb requires for
++     * MPG-enabled domains
++     */
++    *_gid = 0;
++    return EOK;
++}
++
+ /* FIXME: support storing additional attributes */
+ int sdap_save_user(TALLOC_CTX *memctx,
+                    struct sdap_options *opts,
+@@ -357,7 +389,7 @@ int sdap_save_user(TALLOC_CTX *memctx,
+             goto done;
+         }
+ 
+-        if (IS_SUBDOMAIN(dom)) {
++        if (IS_SUBDOMAIN(dom) || dom->mpg == true) {
+             /* For subdomain users, only create the private group as
+              * the subdomain is an MPG domain.
+              * But we have to save the GID of the original primary group
+@@ -365,14 +397,13 @@ int sdap_save_user(TALLOC_CTX *memctx,
+              * typically (Unix and AD) the user is not listed in his primary
+              * group as a member.
+              */
+-            ret = sysdb_attrs_add_uint32(user_attrs, SYSDB_PRIMARY_GROUP_GIDNUM,
+-                                         (uint32_t) gid);
++            ret = sdap_user_set_mpg(user_attrs, &gid);
+             if (ret != EOK) {
+-                DEBUG(SSSDBG_OP_FAILURE, "sysdb_attrs_add_uint32 failed.\n");
++                DEBUG(SSSDBG_OP_FAILURE,
++                      "sdap_user_set_mpg failed [%d]: %s\n", ret,
++                      sss_strerror(ret));
+                 goto done;
+             }
+-
+-            gid = 0;
+         }
+ 
+         /* Store the GID in the ldap_attrs so it doesn't get
+@@ -380,6 +411,41 @@ int sdap_save_user(TALLOC_CTX *memctx,
+         */
+         ret = sysdb_attrs_add_uint32(attrs, SYSDB_GIDNUM, gid);
+         if (ret != EOK) goto done;
++    } else if (dom->mpg) {
++        /* Likewise, if a domain is set to contain 'magic private groups', do
++         * not process the real GID, but save it in the cache as originalGID
++         * (if available)
++         */
++        ret = sysdb_attrs_get_uint32_t(attrs,
++                                       opts->user_map[SDAP_AT_USER_GID].sys_name,
++                                       &gid);
++        if (ret == ENOENT) {
++            DEBUG(SSSDBG_TRACE_LIBS,
++                  "Missing GID, won't save the %s attribute\n",
++                  SYSDB_PRIMARY_GROUP_GIDNUM);
++
++            /* Store the UID as GID (since we're in a MPG domain so that it doesn't
++             * get treated as a missing attribute and removed
++             */
++            ret = sdap_replace_id(attrs, SYSDB_GIDNUM, uid);
++            if (ret) {
++                DEBUG(SSSDBG_OP_FAILURE, "Cannot set the id-mapped UID\n");
++                goto done;
++            }
++            gid = 0;
++        } else if (ret != EOK) {
++            DEBUG(SSSDBG_MINOR_FAILURE,
++                  "Cannot retrieve GID, won't save the %s attribute\n",
++                  SYSDB_PRIMARY_GROUP_GIDNUM);
++            gid = 0;
++        }
++
++        ret = sdap_user_set_mpg(user_attrs, &gid);
++        if (ret != EOK) {
++            DEBUG(SSSDBG_OP_FAILURE,
++                  "sdap_user_set_mpg failed [%d]: %s\n", ret, sss_strerror(ret));
++            goto done;
++        }
+     } else {
+         ret = sysdb_attrs_get_uint32_t(attrs,
+                                        opts->user_map[SDAP_AT_USER_GID].sys_name,
+@@ -403,8 +469,9 @@ int sdap_save_user(TALLOC_CTX *memctx,
+     }
+ 
+     /* check that the gid is valid for this domain */
+-    if (is_posix == true && IS_SUBDOMAIN(dom) == false &&
+-            OUT_OF_ID_RANGE(gid, dom->id_min, dom->id_max)) {
++    if (is_posix == true && IS_SUBDOMAIN(dom) == false
++            && dom->mpg == false
++            && OUT_OF_ID_RANGE(gid, dom->id_min, dom->id_max)) {
+         DEBUG(SSSDBG_CRIT_FAILURE,
+               "User [%s] filtered out! (primary gid out of range)\n",
+                user_name);
+-- 
+2.13.5
+
diff --git a/SOURCES/0006-sysdb-add-sysdb_attrs_copy.patch b/SOURCES/0006-sysdb-add-sysdb_attrs_copy.patch
deleted file mode 100644
index f41c5e7..0000000
--- a/SOURCES/0006-sysdb-add-sysdb_attrs_copy.patch
+++ /dev/null
@@ -1,173 +0,0 @@
-From cae55d342a5f5c5ac22ac913b9251c2112b22c42 Mon Sep 17 00:00:00 2001
-From: Sumit Bose <sbose@redhat.com>
-Date: Mon, 21 Sep 2015 12:32:48 +0200
-Subject: [PATCH 06/15] sysdb: add sysdb_attrs_copy()
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Related to https://pagure.io/SSSD/sssd/issue/3050
-
-Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
-Reviewed-by: Lukáš Slebodník <lslebodn@redhat.com>
----
- src/db/sysdb.c          | 24 ++++++++++++++
- src/db/sysdb.h          |  1 +
- src/tests/sysdb-tests.c | 87 +++++++++++++++++++++++++++++++++++++++++++++++++
- 3 files changed, 112 insertions(+)
-
-diff --git a/src/db/sysdb.c b/src/db/sysdb.c
-index 5160e3df3810a113d4ec1371350e51a074aaa146..98b7afbfab5141fa9b63a4aab31c620545b3c1f2 100644
---- a/src/db/sysdb.c
-+++ b/src/db/sysdb.c
-@@ -752,6 +752,30 @@ done:
-     return ret;
- }
- 
-+errno_t sysdb_attrs_copy(struct sysdb_attrs *src, struct sysdb_attrs *dst)
-+{
-+    int ret;
-+    size_t c;
-+    size_t d;
-+
-+    if (src == NULL || dst == NULL) {
-+        return EINVAL;
-+    }
-+
-+    for (c = 0; c < src->num; c++) {
-+        for (d = 0; d < src->a[c].num_values; d++) {
-+            ret = sysdb_attrs_add_val_safe(dst, src->a[c].name,
-+                                           &src->a[c].values[d]);
-+            if (ret != EOK) {
-+                DEBUG(SSSDBG_OP_FAILURE, "sysdb_attrs_add_val failed.\n");
-+                return ret;
-+            }
-+        }
-+    }
-+
-+    return EOK;
-+}
-+
- int sysdb_attrs_users_from_str_list(struct sysdb_attrs *attrs,
-                                     const char *attr_name,
-                                     const char *domain,
-diff --git a/src/db/sysdb.h b/src/db/sysdb.h
-index 83d0d794c737c094d1fd52e7cc7f2113b5d9a7a0..c677957bb639e40db2f985205160612094302e78 100644
---- a/src/db/sysdb.h
-+++ b/src/db/sysdb.h
-@@ -352,6 +352,7 @@ int sysdb_attrs_add_lc_name_alias_safe(struct sysdb_attrs *attrs,
- int sysdb_attrs_copy_values(struct sysdb_attrs *src,
-                             struct sysdb_attrs *dst,
-                             const char *name);
-+errno_t sysdb_attrs_copy(struct sysdb_attrs *src, struct sysdb_attrs *dst);
- int sysdb_attrs_get_el(struct sysdb_attrs *attrs, const char *name,
-                        struct ldb_message_element **el);
- int sysdb_attrs_get_el_ext(struct sysdb_attrs *attrs, const char *name,
-diff --git a/src/tests/sysdb-tests.c b/src/tests/sysdb-tests.c
-index 013b01a9a68d9de87d796d3aff41d98cef8cccc3..c343c734a27a335303974b6866a5d9e88d4c307e 100644
---- a/src/tests/sysdb-tests.c
-+++ b/src/tests/sysdb-tests.c
-@@ -4997,6 +4997,92 @@ START_TEST(test_sysdb_attrs_add_string_safe)
- }
- END_TEST
- 
-+START_TEST(test_sysdb_attrs_copy)
-+{
-+    int ret;
-+    struct sysdb_attrs *src;
-+    struct sysdb_attrs *dst;
-+    TALLOC_CTX *tmp_ctx;
-+    const char *val;
-+    const char **array;
-+
-+    ret = sysdb_attrs_copy(NULL, NULL);
-+    fail_unless(ret == EINVAL, "Wrong return code");
-+
-+    tmp_ctx = talloc_new(NULL);
-+    fail_unless(tmp_ctx != NULL, "talloc_new failed");
-+
-+    src = sysdb_new_attrs(tmp_ctx);
-+    fail_unless(src != NULL, "sysdb_new_attrs failed");
-+
-+    ret = sysdb_attrs_copy(src, NULL);
-+    fail_unless(ret == EINVAL, "Wrong return code");
-+
-+    dst = sysdb_new_attrs(tmp_ctx);
-+    fail_unless(dst != NULL, "sysdb_new_attrs failed");
-+
-+    ret = sysdb_attrs_copy(NULL, dst);
-+    fail_unless(ret == EINVAL, "Wrong return code");
-+
-+    ret = sysdb_attrs_copy(src, dst);
-+    fail_unless(ret == EOK, "sysdb_attrs_copy failed");
-+    fail_unless(dst->num == 0, "Wrong number of elements");
-+
-+    ret = sysdb_attrs_add_string(src, TEST_ATTR_NAME, TEST_ATTR_VALUE);
-+    fail_unless(ret == EOK, "sysdb_attrs_add_val failed.");
-+
-+    ret = sysdb_attrs_copy(src, dst);
-+    fail_unless(ret == EOK, "sysdb_attrs_copy failed");
-+    fail_unless(dst->num == 1, "Wrong number of elements");
-+    ret = sysdb_attrs_get_string(dst, TEST_ATTR_NAME, &val);
-+    fail_unless(ret == EOK, "sysdb_attrs_get_string failed.\n");
-+    fail_unless(strcmp(val, TEST_ATTR_VALUE) == 0, "Wrong attribute value.");
-+
-+    /* Make sure the same entry is not copied twice */
-+    ret = sysdb_attrs_copy(src, dst);
-+    fail_unless(ret == EOK, "sysdb_attrs_copy failed");
-+    fail_unless(dst->num == 1, "Wrong number of elements");
-+    ret = sysdb_attrs_get_string(dst, TEST_ATTR_NAME, &val);
-+    fail_unless(ret == EOK, "sysdb_attrs_get_string failed.\n");
-+    fail_unless(strcmp(val, TEST_ATTR_VALUE) == 0, "Wrong attribute value.");
-+
-+    /* Add new value to existing attribute */
-+    ret = sysdb_attrs_add_string(src, TEST_ATTR_NAME, TEST_ATTR_VALUE"_2nd");
-+    fail_unless(ret == EOK, "sysdb_attrs_add_val failed.");
-+
-+    ret = sysdb_attrs_copy(src, dst);
-+    fail_unless(ret == EOK, "sysdb_attrs_copy failed");
-+    fail_unless(dst->num == 1, "Wrong number of elements");
-+    ret = sysdb_attrs_get_string_array(dst, TEST_ATTR_NAME, tmp_ctx, &array);
-+    fail_unless(ret == EOK, "sysdb_attrs_get_string_array failed.\n");
-+    fail_unless(strcmp(array[0], TEST_ATTR_VALUE) == 0,
-+                       "Wrong attribute value.");
-+    fail_unless(strcmp(array[1], TEST_ATTR_VALUE"_2nd") == 0,
-+                       "Wrong attribute value.");
-+    fail_unless(array[2] == NULL, "Wrong number of values.");
-+
-+    /* Add new attribute */
-+    ret = sysdb_attrs_add_string(src, TEST_ATTR_NAME"_2nd", TEST_ATTR_VALUE);
-+    fail_unless(ret == EOK, "sysdb_attrs_add_val failed.");
-+
-+    ret = sysdb_attrs_copy(src, dst);
-+    fail_unless(ret == EOK, "sysdb_attrs_copy failed");
-+    fail_unless(dst->num == 2, "Wrong number of elements");
-+    ret = sysdb_attrs_get_string_array(dst, TEST_ATTR_NAME, tmp_ctx, &array);
-+    fail_unless(ret == EOK, "sysdb_attrs_get_string_array failed.\n");
-+    fail_unless(strcmp(array[0], TEST_ATTR_VALUE) == 0,
-+                       "Wrong attribute value.");
-+    fail_unless(strcmp(array[1], TEST_ATTR_VALUE"_2nd") == 0,
-+                       "Wrong attribute value.");
-+    fail_unless(array[2] == NULL, "Wrong number of values.");
-+    ret = sysdb_attrs_get_string(dst, TEST_ATTR_NAME"_2nd", &val);
-+    fail_unless(ret == EOK, "sysdb_attrs_get_string failed.\n");
-+    fail_unless(strcmp(val, TEST_ATTR_VALUE) == 0, "Wrong attribute value.");
-+
-+    talloc_free(tmp_ctx);
-+}
-+END_TEST
-+
- START_TEST (test_sysdb_search_return_ENOENT)
- {
-     struct sysdb_test_ctx *test_ctx;
-@@ -6995,6 +7081,7 @@ Suite *create_sysdb_suite(void)
-     tcase_add_test(tc_sysdb, test_sysdb_attrs_add_val);
-     tcase_add_test(tc_sysdb, test_sysdb_attrs_add_val_safe);
-     tcase_add_test(tc_sysdb, test_sysdb_attrs_add_string_safe);
-+    tcase_add_test(tc_sysdb, test_sysdb_attrs_copy);
- 
- /* ===== Test search return empty result ===== */
-     tcase_add_test(tc_sysdb, test_sysdb_search_return_ENOENT);
--- 
-2.9.3
-
diff --git a/SOURCES/0007-LDAP-Turn-group-request-into-user-request-for-MPG-do.patch b/SOURCES/0007-LDAP-Turn-group-request-into-user-request-for-MPG-do.patch
new file mode 100644
index 0000000..12745ce
--- /dev/null
+++ b/SOURCES/0007-LDAP-Turn-group-request-into-user-request-for-MPG-do.patch
@@ -0,0 +1,222 @@
+From f048f210112a2ca6df52f10f9e47afac5996fc09 Mon Sep 17 00:00:00 2001
+From: Jakub Hrozek <jhrozek@redhat.com>
+Date: Tue, 3 Oct 2017 14:31:18 +0200
+Subject: [PATCH 07/21] LDAP: Turn group request into user request for MPG
+ domains if needed
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+If the primary group GID or the group name is requested before the user
+is, we need to also search the user space to save the user in the back
+end which then allows the responder to generate the group from the
+user entry.
+
+Related:
+    https://pagure.io/SSSD/sssd/issue/1872
+
+Reviewed-by: Pavel Březina <pbrezina@redhat.com>
+Reviewed-by: Fabiano Fidêncio <fidencio@redhat.com>
+(cherry picked from commit 057e8af379aa32f7d9ea48bfff22a3304c59444b)
+---
+ src/providers/ldap/ldap_id.c | 162 +++++++++++++++++++++++++++++++------------
+ 1 file changed, 118 insertions(+), 44 deletions(-)
+
+diff --git a/src/providers/ldap/ldap_id.c b/src/providers/ldap/ldap_id.c
+index 93204d35ea3782c9aa5d622a962c295869472631..e89fc6133316f684810afe4c1a0731b8a04f2931 100644
+--- a/src/providers/ldap/ldap_id.c
++++ b/src/providers/ldap/ldap_id.c
+@@ -694,6 +694,8 @@ struct groups_get_state {
+ static int groups_get_retry(struct tevent_req *req);
+ static void groups_get_connect_done(struct tevent_req *subreq);
+ static void groups_get_posix_check_done(struct tevent_req *subreq);
++static void groups_get_mpg_done(struct tevent_req *subreq);
++static errno_t groups_get_handle_no_group(struct tevent_req *req);
+ static void groups_get_search(struct tevent_req *req);
+ static void groups_get_done(struct tevent_req *subreq);
+ 
+@@ -1051,8 +1053,6 @@ static void groups_get_done(struct tevent_req *subreq)
+                                                       struct tevent_req);
+     struct groups_get_state *state = tevent_req_data(req,
+                                                      struct groups_get_state);
+-    char *endptr;
+-    gid_t gid;
+     int dp_error = DP_ERR_FATAL;
+     int ret;
+ 
+@@ -1078,49 +1078,33 @@ static void groups_get_done(struct tevent_req *subreq)
+         return;
+     }
+ 
+-    if (ret == ENOENT && state->noexist_delete == true) {
+-        switch (state->filter_type) {
+-        case BE_FILTER_ENUM:
+-            tevent_req_error(req, ret);
++    if (ret == ENOENT
++            && state->domain->mpg == true) {
++        /* The requested filter did not find a group. Before giving up, we must
++         * also check if the GID can be resolved through a primary group of a
++         * user
++         */
++        subreq = users_get_send(state,
++                                state->ev,
++                                state->ctx,
++                                state->sdom,
++                                state->conn,
++                                state->filter_value,
++                                state->filter_type,
++                                NULL,
++                                state->noexist_delete);
++        if (subreq == NULL) {
++            tevent_req_error(req, ENOMEM);
+             return;
+-        case BE_FILTER_NAME:
+-            ret = sysdb_delete_group(state->domain, state->filter_value, 0);
+-            if (ret != EOK && ret != ENOENT) {
+-                tevent_req_error(req, ret);
+-                return;
+-            }
+-            break;
+-
+-        case BE_FILTER_IDNUM:
+-            gid = (gid_t) strtouint32(state->filter_value, &endptr, 10);
+-            if (errno || *endptr || (state->filter_value == endptr)) {
+-                tevent_req_error(req, errno ? errno : EINVAL);
+-                return;
+-            }
+-
+-            ret = sysdb_delete_group(state->domain, NULL, gid);
+-            if (ret != EOK && ret != ENOENT) {
+-                tevent_req_error(req, ret);
+-                return;
+-            }
+-            break;
+-
+-        case BE_FILTER_SECID:
+-        case BE_FILTER_UUID:
+-            /* Since it is not clear if the SID/UUID belongs to a user or a
+-             * group we have nothing to do here. */
+-            break;
+-
+-        case BE_FILTER_WILDCARD:
+-            /* We can't know if all groups are up-to-date, especially in
+-             * a large environment. Do not delete any records, let the
+-             * responder fetch the entries they are requested in.
+-             */
+-            break;
+-
+-
+-        default:
+-            tevent_req_error(req, EINVAL);
++        }
++        tevent_req_set_callback(subreq, groups_get_mpg_done, req);
++        return;
++    } else if (ret == ENOENT && state->noexist_delete == true) {
++        ret = groups_get_handle_no_group(req);
++        if (ret != EOK) {
++            DEBUG(SSSDBG_OP_FAILURE,
++                  "Could not delete group [%d]: %s\n", ret, sss_strerror(ret));
++            tevent_req_error(req, ret);
+             return;
+         }
+     }
+@@ -1129,6 +1113,96 @@ static void groups_get_done(struct tevent_req *subreq)
+     tevent_req_done(req);
+ }
+ 
++static void groups_get_mpg_done(struct tevent_req *subreq)
++{
++    errno_t ret;
++    struct tevent_req *req = tevent_req_callback_data(subreq,
++                                                      struct tevent_req);
++    struct groups_get_state *state = tevent_req_data(req,
++                                                     struct groups_get_state);
++
++    ret = users_get_recv(subreq, &state->dp_error, &state->sdap_ret);
++    talloc_zfree(subreq);
++
++    if (ret != EOK) {
++        tevent_req_error(req, ret);
++        return;
++    }
++
++    if (state->sdap_ret == ENOENT && state->noexist_delete == true) {
++        ret = groups_get_handle_no_group(req);
++        if (ret != EOK) {
++            DEBUG(SSSDBG_OP_FAILURE,
++                  "Could not delete group [%d]: %s\n", ret, sss_strerror(ret));
++            tevent_req_error(req, ret);
++            return;
++        }
++    }
++
++    /* GID resolved to a user private group, done */
++    tevent_req_done(req);
++    return;
++}
++
++static errno_t groups_get_handle_no_group(struct tevent_req *req)
++{
++    struct groups_get_state *state = tevent_req_data(req,
++                                                     struct groups_get_state);
++    errno_t ret;
++    char *endptr;
++    gid_t gid;
++
++    switch (state->filter_type) {
++    case BE_FILTER_ENUM:
++        ret = ENOENT;
++        break;
++    case BE_FILTER_NAME:
++        ret = sysdb_delete_group(state->domain, state->filter_value, 0);
++        if (ret != EOK && ret != ENOENT) {
++            DEBUG(SSSDBG_OP_FAILURE,
++                  "Cannot delete group %s [%d]: %s\n",
++                  state->filter_value, ret, sss_strerror(ret));
++            return ret;
++        }
++        ret = EOK;
++        break;
++    case BE_FILTER_IDNUM:
++        gid = (gid_t) strtouint32(state->filter_value, &endptr, 10);
++        if (errno || *endptr || (state->filter_value == endptr)) {
++            ret = errno ? errno : EINVAL;
++            break;
++        }
++
++        ret = sysdb_delete_group(state->domain, NULL, gid);
++        if (ret != EOK && ret != ENOENT) {
++            DEBUG(SSSDBG_OP_FAILURE,
++                  "Cannot delete group %"SPRIgid" [%d]: %s\n",
++                  gid, ret, sss_strerror(ret));
++            return ret;
++        }
++        ret = EOK;
++        break;
++    case BE_FILTER_SECID:
++    case BE_FILTER_UUID:
++        /* Since it is not clear if the SID/UUID belongs to a user or a
++         * group we have nothing to do here. */
++        ret = EOK;
++        break;
++    case BE_FILTER_WILDCARD:
++        /* We can't know if all groups are up-to-date, especially in
++         * a large environment. Do not delete any records, let the
++         * responder fetch the entries they are requested in.
++         */
++        ret = EOK;
++        break;
++    default:
++        ret = EINVAL;
++        break;
++    }
++
++    return ret;
++}
++
+ int groups_get_recv(struct tevent_req *req, int *dp_error_out, int *sdap_ret)
+ {
+     struct groups_get_state *state = tevent_req_data(req,
+-- 
+2.13.5
+
diff --git a/SOURCES/0007-sdap_get_users_send-new-argument-mapped_attrs.patch b/SOURCES/0007-sdap_get_users_send-new-argument-mapped_attrs.patch
deleted file mode 100644
index 9292f78..0000000
--- a/SOURCES/0007-sdap_get_users_send-new-argument-mapped_attrs.patch
+++ /dev/null
@@ -1,316 +0,0 @@
-From af96fbe97576133ca6077c47f39b812e7e289040 Mon Sep 17 00:00:00 2001
-From: Sumit Bose <sbose@redhat.com>
-Date: Sun, 12 Mar 2017 18:31:03 +0100
-Subject: [PATCH 07/15] sdap_get_users_send(): new argument mapped_attrs
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-mapped_attrs can be a list of sysdb_attrs which are not available on
-the server side but should be store with the cached user entry. This is
-needed e.g. when the input to look up the user in LDAP is not an
-attribute which is stored in LDAP but some data where LDAP attributes
-are extracted from. The current use case is the certificate mapping
-library which can create LDAP search filters based on content of the
-certificate. To allow upcoming cache lookup to use the input directly it
-is stored in the user object in the cache.
-
-Related to https://pagure.io/SSSD/sssd/issue/3050
-
-Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
-Reviewed-by: Lukáš Slebodník <lslebodn@redhat.com>
----
- src/db/sysdb.h                             |  3 ++
- src/db/sysdb_ops.c                         | 61 ++++++++++++++++++++++++++++++
- src/providers/ldap/ldap_id.c               |  4 +-
- src/providers/ldap/sdap_async.h            |  3 +-
- src/providers/ldap/sdap_async_enum.c       |  2 +-
- src/providers/ldap/sdap_async_initgroups.c |  2 +-
- src/providers/ldap/sdap_async_private.h    |  1 +
- src/providers/ldap/sdap_async_users.c      | 41 +++++++++++++++++++-
- src/providers/ldap/sdap_users.h            |  1 +
- 9 files changed, 111 insertions(+), 7 deletions(-)
-
-diff --git a/src/db/sysdb.h b/src/db/sysdb.h
-index c677957bb639e40db2f985205160612094302e78..098f47f91187aac75c58c02f0af738c344765762 100644
---- a/src/db/sysdb.h
-+++ b/src/db/sysdb.h
-@@ -1246,6 +1246,9 @@ errno_t sysdb_search_user_by_cert(TALLOC_CTX *mem_ctx,
- errno_t sysdb_remove_cert(struct sss_domain_info *domain,
-                           const char *cert);
- 
-+errno_t sysdb_remove_mapped_data(struct sss_domain_info *domain,
-+                                 struct sysdb_attrs *mapped_attr);
-+
- /* === Functions related to GPOs === */
- 
- #define SYSDB_GPO_CONTAINER "cn=gpos,cn=ad,cn=custom"
-diff --git a/src/db/sysdb_ops.c b/src/db/sysdb_ops.c
-index 242d3ce3bb795691e329790a07c3493672e8f523..6c2254df2b75d3d3419528523103ad9cddb40c9d 100644
---- a/src/db/sysdb_ops.c
-+++ b/src/db/sysdb_ops.c
-@@ -4685,6 +4685,67 @@ errno_t sysdb_search_user_by_cert(TALLOC_CTX *mem_ctx,
-     return sysdb_search_object_by_cert(mem_ctx, domain, cert, user_attrs, res);
- }
- 
-+errno_t sysdb_remove_mapped_data(struct sss_domain_info *domain,
-+                                 struct sysdb_attrs *mapped_attr)
-+{
-+    int ret;
-+    char *val;
-+    char *filter;
-+    const char *attrs[] = {SYSDB_NAME, NULL};
-+    struct ldb_result *res = NULL;
-+    size_t c;
-+    bool all_ok = true;
-+
-+    if (mapped_attr->num != 1 || mapped_attr->a[0].num_values != 1) {
-+        DEBUG(SSSDBG_OP_FAILURE, "Unsupported number of attributes.\n");
-+        return EINVAL;
-+    }
-+
-+    ret = bin_to_ldap_filter_value(NULL, mapped_attr->a[0].values[0].data,
-+                                   mapped_attr->a[0].values[0].length, &val);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_OP_FAILURE, "bin_to_ldap_filter_value failed.\n");
-+        return ret;
-+    }
-+
-+    filter = talloc_asprintf(NULL, "(&("SYSDB_UC")(%s=%s))",
-+                             mapped_attr->a[0].name, val);
-+    talloc_free(val);
-+    if (filter == NULL) {
-+        DEBUG(SSSDBG_OP_FAILURE, "talloc_asprintf failed.\n");
-+        return ENOMEM;
-+    }
-+
-+    ret = sysdb_search_object_attr(NULL, domain, filter, attrs, false, &res);
-+    talloc_free(filter);
-+    if (ret == ENOENT || res == NULL) {
-+        DEBUG(SSSDBG_TRACE_ALL, "Mapped data not found.\n");
-+        talloc_free(res);
-+        return EOK;
-+    } else if (ret != EOK) {
-+        talloc_free(res);
-+        DEBUG(SSSDBG_OP_FAILURE, "sysdb_search_object_attr failed.\n");
-+        return ret;
-+    }
-+
-+    for (c = 0; c < res->count; c++) {
-+        DEBUG(SSSDBG_TRACE_ALL, "Removing mapped data from [%s].\n",
-+                                ldb_dn_get_linearized(res->msgs[c]->dn));
-+        /* The timestamp cache is skipped on purpose here. */
-+        ret = sysdb_set_cache_entry_attr(domain->sysdb->ldb, res->msgs[c]->dn,
-+                                         mapped_attr, SYSDB_MOD_DEL);
-+        if (ret != EOK) {
-+            all_ok = false;
-+            DEBUG(SSSDBG_OP_FAILURE,
-+                  "Failed to remove mapped data from [%s], skipping.\n",
-+                  ldb_dn_get_linearized(res->msgs[c]->dn));
-+        }
-+    }
-+    talloc_free(res);
-+
-+    return (all_ok ? EOK : EIO);
-+}
-+
- errno_t sysdb_remove_cert(struct sss_domain_info *domain,
-                           const char *cert)
- {
-diff --git a/src/providers/ldap/ldap_id.c b/src/providers/ldap/ldap_id.c
-index e9455b538daa2d65d944dbb68022a2773623d7b7..898ddb18689d55fcc3fdf021b38df0e574003eb2 100644
---- a/src/providers/ldap/ldap_id.c
-+++ b/src/providers/ldap/ldap_id.c
-@@ -442,7 +442,7 @@ static void users_get_search(struct tevent_req *req)
-                                  state->attrs, state->filter,
-                                  dp_opt_get_int(state->ctx->opts->basic,
-                                                 SDAP_SEARCH_TIMEOUT),
--                                 lookup_type);
-+                                 lookup_type, NULL);
-     if (!subreq) {
-         tevent_req_error(req, ENOMEM);
-         return;
-@@ -507,7 +507,7 @@ static void users_get_done(struct tevent_req *subreq)
-             ret = sdap_fallback_local_user(state, state->shortname, uid, &usr_attrs);
-             if (ret == EOK) {
-                 ret = sdap_save_user(state, state->ctx->opts, state->domain,
--                                     usr_attrs[0], NULL, 0);
-+                                     usr_attrs[0], NULL, NULL, 0);
-             }
-         }
-     }
-diff --git a/src/providers/ldap/sdap_async.h b/src/providers/ldap/sdap_async.h
-index 2ebde6b83646408e446c91cb324809cb767b2617..6e5800b42ba4a045fa7985b09a80b6b86b8c6055 100644
---- a/src/providers/ldap/sdap_async.h
-+++ b/src/providers/ldap/sdap_async.h
-@@ -90,7 +90,8 @@ struct tevent_req *sdap_get_users_send(TALLOC_CTX *memctx,
-                                        const char **attrs,
-                                        const char *filter,
-                                        int timeout,
--                                       enum sdap_entry_lookup_type lookup_type);
-+                                       enum sdap_entry_lookup_type lookup_type,
-+                                       struct sysdb_attrs *mapped_attrs);
- int sdap_get_users_recv(struct tevent_req *req,
-                         TALLOC_CTX *mem_ctx, char **timestamp);
- 
-diff --git a/src/providers/ldap/sdap_async_enum.c b/src/providers/ldap/sdap_async_enum.c
-index 387e53155b567ce106cc68009c7cb99e27d24a17..3f65059e18d5c8b548da0babec867d27c3a64198 100644
---- a/src/providers/ldap/sdap_async_enum.c
-+++ b/src/providers/ldap/sdap_async_enum.c
-@@ -635,7 +635,7 @@ static struct tevent_req *enum_users_send(TALLOC_CTX *memctx,
-                                  state->attrs, state->filter,
-                                  dp_opt_get_int(state->ctx->opts->basic,
-                                                 SDAP_ENUM_SEARCH_TIMEOUT),
--                                 SDAP_LOOKUP_ENUMERATE);
-+                                 SDAP_LOOKUP_ENUMERATE, NULL);
-     if (!subreq) {
-         ret = ENOMEM;
-         goto fail;
-diff --git a/src/providers/ldap/sdap_async_initgroups.c b/src/providers/ldap/sdap_async_initgroups.c
-index 8c7a65bf36abf341e077cf9eac18a234d3a07c07..79af7a3eda3fe8533933535c98c2b4b4698dfda2 100644
---- a/src/providers/ldap/sdap_async_initgroups.c
-+++ b/src/providers/ldap/sdap_async_initgroups.c
-@@ -2991,7 +2991,7 @@ static void sdap_get_initgr_user(struct tevent_req *subreq)
-     DEBUG(SSSDBG_TRACE_ALL, "Storing the user\n");
- 
-     ret = sdap_save_user(state, state->opts, state->dom, state->orig_user,
--                         NULL, 0);
-+                         NULL, NULL, 0);
-     if (ret) {
-         goto fail;
-     }
-diff --git a/src/providers/ldap/sdap_async_private.h b/src/providers/ldap/sdap_async_private.h
-index 266bc03115e2bdd6a283f5f7da565fd00d3a77be..72507442a9ffd5c0e24ccbd95d75d3ebf9bf0940 100644
---- a/src/providers/ldap/sdap_async_private.h
-+++ b/src/providers/ldap/sdap_async_private.h
-@@ -94,6 +94,7 @@ int sdap_save_users(TALLOC_CTX *memctx,
-                     struct sdap_options *opts,
-                     struct sysdb_attrs **users,
-                     int num_users,
-+                    struct sysdb_attrs *mapped_attrs,
-                     char **_usn_value);
- 
- int sdap_initgr_common_store(struct sysdb_ctx *sysdb,
-diff --git a/src/providers/ldap/sdap_async_users.c b/src/providers/ldap/sdap_async_users.c
-index 87d91d8247c37a4c6a1d83b7189399056528fc90..3d957ab584865f74499bc732395388a78965fe5f 100644
---- a/src/providers/ldap/sdap_async_users.c
-+++ b/src/providers/ldap/sdap_async_users.c
-@@ -117,6 +117,7 @@ int sdap_save_user(TALLOC_CTX *memctx,
-                    struct sdap_options *opts,
-                    struct sss_domain_info *dom,
-                    struct sysdb_attrs *attrs,
-+                   struct sysdb_attrs *mapped_attrs,
-                    char **_usn_value,
-                    time_t now)
- {
-@@ -511,6 +512,11 @@ int sdap_save_user(TALLOC_CTX *memctx,
-                            user_attrs, missing, cache_timeout, now);
-     if (ret) goto done;
- 
-+    if (mapped_attrs != NULL) {
-+        ret = sysdb_set_user_attr(dom, user_name, mapped_attrs, SYSDB_MOD_ADD);
-+        if (ret) return ret;
-+    }
-+
-     if (_usn_value) {
-         *_usn_value = talloc_steal(memctx, usn_value);
-     }
-@@ -537,6 +543,7 @@ int sdap_save_users(TALLOC_CTX *memctx,
-                     struct sdap_options *opts,
-                     struct sysdb_attrs **users,
-                     int num_users,
-+                    struct sysdb_attrs *mapped_attrs,
-                     char **_usn_value)
- {
-     TALLOC_CTX *tmpctx;
-@@ -565,11 +572,20 @@ int sdap_save_users(TALLOC_CTX *memctx,
-     }
-     in_transaction = true;
- 
-+    if (mapped_attrs != NULL) {
-+        ret = sysdb_remove_mapped_data(dom, mapped_attrs);
-+        if (ret != EOK) {
-+            DEBUG(SSSDBG_OP_FAILURE, "sysdb_remove_mapped_data failed, "
-+                  "some cached entries might contain invalid mapping data.\n");
-+        }
-+    }
-+
-     now = time(NULL);
-     for (i = 0; i < num_users; i++) {
-         usn_value = NULL;
- 
--        ret = sdap_save_user(tmpctx, opts, dom, users[i], &usn_value, now);
-+        ret = sdap_save_user(tmpctx, opts, dom, users[i], mapped_attrs,
-+                             &usn_value, now);
- 
-         /* Do not fail completely on errors.
-          * Just report the failure to save and go on */
-@@ -868,6 +884,7 @@ struct sdap_get_users_state {
- 
-     char *higher_usn;
-     struct sysdb_attrs **users;
-+    struct sysdb_attrs *mapped_attrs;
-     size_t count;
- };
- 
-@@ -883,7 +900,8 @@ struct tevent_req *sdap_get_users_send(TALLOC_CTX *memctx,
-                                        const char **attrs,
-                                        const char *filter,
-                                        int timeout,
--                                       enum sdap_entry_lookup_type lookup_type)
-+                                       enum sdap_entry_lookup_type lookup_type,
-+                                       struct sysdb_attrs *mapped_attrs)
- {
-     errno_t ret;
-     struct tevent_req *req;
-@@ -900,6 +918,23 @@ struct tevent_req *sdap_get_users_send(TALLOC_CTX *memctx,
-     state->filter = filter;
-     PROBE(SDAP_SEARCH_USER_SEND, state->filter);
- 
-+    if (mapped_attrs == NULL) {
-+        state->mapped_attrs = NULL;
-+    } else {
-+        state->mapped_attrs = sysdb_new_attrs(state);
-+        if (state->mapped_attrs == NULL) {
-+            DEBUG(SSSDBG_OP_FAILURE, "sysdb_new_attrs failed.\n");
-+            ret = ENOMEM;
-+            goto done;
-+        }
-+
-+        ret = sysdb_attrs_copy(mapped_attrs, state->mapped_attrs);
-+        if (ret != EOK) {
-+            DEBUG(SSSDBG_OP_FAILURE, "sysdb_attrs_copy failed.\n");
-+            goto done;
-+        }
-+    }
-+
-     subreq = sdap_search_user_send(state, ev, dom, opts, search_bases,
-                                    sh, attrs, filter, timeout, lookup_type);
-     if (subreq == NULL) {
-@@ -938,9 +973,11 @@ static void sdap_get_users_done(struct tevent_req *subreq)
-     }
- 
-     PROBE(SDAP_SEARCH_USER_SAVE_BEGIN, state->filter);
-+
-     ret = sdap_save_users(state, state->sysdb,
-                           state->dom, state->opts,
-                           state->users, state->count,
-+                          state->mapped_attrs,
-                           &state->higher_usn);
-     PROBE(SDAP_SEARCH_USER_SAVE_END, state->filter);
-     if (ret) {
-diff --git a/src/providers/ldap/sdap_users.h b/src/providers/ldap/sdap_users.h
-index 78dafb31a2a07e7289055daec77c5dc5da1bdeef..a6d088a6d7114db75b0f0ea22ef85c57da6fab0f 100644
---- a/src/providers/ldap/sdap_users.h
-+++ b/src/providers/ldap/sdap_users.h
-@@ -34,6 +34,7 @@ int sdap_save_user(TALLOC_CTX *memctx,
-                    struct sdap_options *opts,
-                    struct sss_domain_info *dom,
-                    struct sysdb_attrs *attrs,
-+                   struct sysdb_attrs *mapped_attrs,
-                    char **_usn_value,
-                    time_t now);
- 
--- 
-2.9.3
-
diff --git a/SOURCES/0008-LDAP-always-store-the-certificate-from-the-request.patch b/SOURCES/0008-LDAP-always-store-the-certificate-from-the-request.patch
deleted file mode 100644
index 108254d..0000000
--- a/SOURCES/0008-LDAP-always-store-the-certificate-from-the-request.patch
+++ /dev/null
@@ -1,178 +0,0 @@
-From a3cc501e36f5cf1e4a8187d723b53111f5481b36 Mon Sep 17 00:00:00 2001
-From: Sumit Bose <sbose@redhat.com>
-Date: Mon, 30 Nov 2015 12:14:55 +0100
-Subject: [PATCH 08/15] LDAP: always store the certificate from the request
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Store the certificate used to lookup a user as mapped attribute in the
-cached user object.
-
-Related to https://pagure.io/SSSD/sssd/issue/3050
-
-Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
-Reviewed-by: Lukáš Slebodník <lslebodn@redhat.com>
----
- src/db/sysdb.h                  |  1 +
- src/db/sysdb_ops.c              |  4 ++--
- src/providers/ldap/ldap_id.c    | 19 ++++++++++++++++++-
- src/tests/cmocka/test_nss_srv.c |  2 +-
- src/tests/cmocka/test_pam_srv.c |  6 +++---
- src/tests/sysdb-tests.c         |  4 ++--
- 6 files changed, 27 insertions(+), 9 deletions(-)
-
-diff --git a/src/db/sysdb.h b/src/db/sysdb.h
-index 098f47f91187aac75c58c02f0af738c344765762..3db22b3689bf6ffd9a48e29c229916e3fac9ca1b 100644
---- a/src/db/sysdb.h
-+++ b/src/db/sysdb.h
-@@ -139,6 +139,7 @@
- 
- #define SYSDB_AUTH_TYPE "authType"
- #define SYSDB_USER_CERT "userCertificate"
-+#define SYSDB_USER_MAPPED_CERT "userMappedCertificate"
- #define SYSDB_USER_EMAIL "mail"
- 
- #define SYSDB_SUBDOMAIN_REALM "realmName"
-diff --git a/src/db/sysdb_ops.c b/src/db/sysdb_ops.c
-index 6c2254df2b75d3d3419528523103ad9cddb40c9d..8ae25764478e522255b177f9e8de1d3ca1ad43fd 100644
---- a/src/db/sysdb_ops.c
-+++ b/src/db/sysdb_ops.c
-@@ -4660,7 +4660,7 @@ errno_t sysdb_search_object_by_cert(TALLOC_CTX *mem_ctx,
-     int ret;
-     char *user_filter;
- 
--    ret = sss_cert_derb64_to_ldap_filter(mem_ctx, cert, SYSDB_USER_CERT,
-+    ret = sss_cert_derb64_to_ldap_filter(mem_ctx, cert, SYSDB_USER_MAPPED_CERT,
-                                          &user_filter);
-     if (ret != EOK) {
-         DEBUG(SSSDBG_OP_FAILURE, "sss_cert_derb64_to_ldap_filter failed.\n");
-@@ -4749,7 +4749,7 @@ errno_t sysdb_remove_mapped_data(struct sss_domain_info *domain,
- errno_t sysdb_remove_cert(struct sss_domain_info *domain,
-                           const char *cert)
- {
--    struct ldb_message_element el = { 0, SYSDB_USER_CERT, 0, NULL };
-+    struct ldb_message_element el = { 0, SYSDB_USER_MAPPED_CERT, 0, NULL };
-     struct sysdb_attrs del_attrs = { 1, &el };
-     const char *attrs[] = {SYSDB_NAME, NULL};
-     struct ldb_result *res = NULL;
-diff --git a/src/providers/ldap/ldap_id.c b/src/providers/ldap/ldap_id.c
-index 898ddb18689d55fcc3fdf021b38df0e574003eb2..a8b4bc2cfc6e9d4e0d74b0e3e036afbcbf7eb26e 100644
---- a/src/providers/ldap/ldap_id.c
-+++ b/src/providers/ldap/ldap_id.c
-@@ -60,6 +60,7 @@ struct users_get_state {
-     int dp_error;
-     int sdap_ret;
-     bool noexist_delete;
-+    struct sysdb_attrs *extra_attrs;
- };
- 
- static int users_get_retry(struct tevent_req *req);
-@@ -99,6 +100,7 @@ struct tevent_req *users_get_send(TALLOC_CTX *memctx,
-     state->conn = conn;
-     state->dp_error = DP_ERR_FATAL;
-     state->noexist_delete = noexist_delete;
-+    state->extra_attrs = NULL;
- 
-     state->op = sdap_id_op_create(state, state->conn->conn_cache);
-     if (!state->op) {
-@@ -251,6 +253,21 @@ struct tevent_req *users_get_send(TALLOC_CTX *memctx,
-                   "sss_cert_derb64_to_ldap_filter failed.\n");
-             goto done;
-         }
-+
-+        state->extra_attrs = sysdb_new_attrs(state);
-+        if (state->extra_attrs == NULL) {
-+            DEBUG(SSSDBG_OP_FAILURE, "sysdb_new_attrs failed.\n");
-+            ret = ENOMEM;
-+            goto done;
-+        }
-+
-+        ret = sysdb_attrs_add_base64_blob(state->extra_attrs,
-+                                          SYSDB_USER_MAPPED_CERT, filter_value);
-+        if (ret != EOK) {
-+            DEBUG(SSSDBG_OP_FAILURE, "sysdb_attrs_add_base64_blob failed.\n");
-+            goto done;
-+        }
-+
-         break;
-     default:
-         ret = EINVAL;
-@@ -442,7 +459,7 @@ static void users_get_search(struct tevent_req *req)
-                                  state->attrs, state->filter,
-                                  dp_opt_get_int(state->ctx->opts->basic,
-                                                 SDAP_SEARCH_TIMEOUT),
--                                 lookup_type, NULL);
-+                                 lookup_type, state->extra_attrs);
-     if (!subreq) {
-         tevent_req_error(req, ENOMEM);
-         return;
-diff --git a/src/tests/cmocka/test_nss_srv.c b/src/tests/cmocka/test_nss_srv.c
-index 72bbaf9bf35ebb3fc4208afaa3c7af95922afcb0..76b9c6fb05673130de0957e93291919c263a28f3 100644
---- a/src/tests/cmocka/test_nss_srv.c
-+++ b/src/tests/cmocka/test_nss_srv.c
-@@ -3508,7 +3508,7 @@ static void test_nss_getnamebycert(void **state)
-     der = sss_base64_decode(nss_test_ctx, TEST_TOKEN_CERT, &der_size);
-     assert_non_null(der);
- 
--    ret = sysdb_attrs_add_mem(attrs, SYSDB_USER_CERT, der, der_size);
-+    ret = sysdb_attrs_add_mem(attrs, SYSDB_USER_MAPPED_CERT, der, der_size);
-     talloc_free(der);
-     assert_int_equal(ret, EOK);
- 
-diff --git a/src/tests/cmocka/test_pam_srv.c b/src/tests/cmocka/test_pam_srv.c
-index ae2e555f7024027d1c0063031f8882bf81a31905..847419658bb983e6548722d6fa6fb22c63ee86b8 100644
---- a/src/tests/cmocka/test_pam_srv.c
-+++ b/src/tests/cmocka/test_pam_srv.c
-@@ -1598,7 +1598,7 @@ static int test_lookup_by_cert_cb(void *pvt)
-         der = sss_base64_decode(pam_test_ctx, pvt, &der_size);
-         assert_non_null(der);
- 
--        ret = sysdb_attrs_add_mem(attrs, SYSDB_USER_CERT, der, der_size);
-+        ret = sysdb_attrs_add_mem(attrs, SYSDB_USER_MAPPED_CERT, der, der_size);
-         talloc_free(der);
-         assert_int_equal(ret, EOK);
- 
-@@ -1630,7 +1630,7 @@ static int test_lookup_by_cert_double_cb(void *pvt)
-         der = sss_base64_decode(pam_test_ctx, pvt, &der_size);
-         assert_non_null(der);
- 
--        ret = sysdb_attrs_add_mem(attrs, SYSDB_USER_CERT, der, der_size);
-+        ret = sysdb_attrs_add_mem(attrs, SYSDB_USER_MAPPED_CERT, der, der_size);
-         talloc_free(der);
-         assert_int_equal(ret, EOK);
- 
-@@ -1658,7 +1658,7 @@ static int test_lookup_by_cert_wrong_user_cb(void *pvt)
-         der = sss_base64_decode(pam_test_ctx, pvt, &der_size);
-         assert_non_null(der);
- 
--        ret = sysdb_attrs_add_mem(attrs, SYSDB_USER_CERT, der, der_size);
-+        ret = sysdb_attrs_add_mem(attrs, SYSDB_USER_MAPPED_CERT, der, der_size);
-         talloc_free(der);
-         assert_int_equal(ret, EOK);
- 
-diff --git a/src/tests/sysdb-tests.c b/src/tests/sysdb-tests.c
-index c343c734a27a335303974b6866a5d9e88d4c307e..5bdd631fbfa1b4463fb169e5f07b65fb2c784096 100644
---- a/src/tests/sysdb-tests.c
-+++ b/src/tests/sysdb-tests.c
-@@ -5721,7 +5721,7 @@ START_TEST(test_sysdb_search_user_by_cert)
-     val.data = sss_base64_decode(test_ctx, TEST_USER_CERT_DERB64, &val.length);
-     fail_unless(val.data != NULL, "sss_base64_decode failed.");
- 
--    ret = sysdb_attrs_add_val(data->attrs, SYSDB_USER_CERT, &val);
-+    ret = sysdb_attrs_add_val(data->attrs, SYSDB_USER_MAPPED_CERT, &val);
-     fail_unless(ret == EOK, "sysdb_attrs_add_val failed with [%d][%s].",
-                 ret, strerror(ret));
- 
-@@ -5750,7 +5750,7 @@ START_TEST(test_sysdb_search_user_by_cert)
-     data2 = test_data_new_user(test_ctx, 2345671);
-     fail_if(data2 == NULL);
- 
--    ret = sysdb_attrs_add_val(data2->attrs, SYSDB_USER_CERT, &val);
-+    ret = sysdb_attrs_add_val(data2->attrs, SYSDB_USER_MAPPED_CERT, &val);
-     fail_unless(ret == EOK, "sysdb_attrs_add_val failed with [%d][%s].",
-                 ret, strerror(ret));
- 
--- 
-2.9.3
-
diff --git a/SOURCES/0008-SYSDB-Prevent-users-and-groups-ID-collision-in-MPG-d.patch b/SOURCES/0008-SYSDB-Prevent-users-and-groups-ID-collision-in-MPG-d.patch
new file mode 100644
index 0000000..b8d60e1
--- /dev/null
+++ b/SOURCES/0008-SYSDB-Prevent-users-and-groups-ID-collision-in-MPG-d.patch
@@ -0,0 +1,97 @@
+From d75b796151973a5d94a79f5577c15cda6eecb5ee Mon Sep 17 00:00:00 2001
+From: Jakub Hrozek <jhrozek@redhat.com>
+Date: Thu, 19 Oct 2017 17:18:15 +0200
+Subject: [PATCH 08/21] SYSDB: Prevent users and groups ID collision in MPG
+ domains except for id_provider=local
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+This commit makes the check when adding an object in a MPG domain
+stricter in the sense that not only same names are allowed in a MPG
+domain, but also the same groups are not allowed either.
+
+This commit is a backwards-incompatible change, but one that is needed,
+otherwise requesting the duplicate group first and then requesting the
+user entry would yield two object when searching by GID.
+
+In order to keep backwards-compatibility, this uniqueness is NOT
+enforced with id_provider=local. This constraint can be removed in
+the future (or the local provider can be dropped altogether)
+
+Reviewed-by: Pavel Březina <pbrezina@redhat.com>
+Reviewed-by: Fabiano Fidêncio <fidencio@redhat.com>
+(cherry picked from commit ac962e2b286988d8666b3b81bf8b55b1705b9ac0)
+---
+ src/db/sysdb_ops.c | 41 ++++++++++++++++++++++++++++++++++++++---
+ 1 file changed, 38 insertions(+), 3 deletions(-)
+
+diff --git a/src/db/sysdb_ops.c b/src/db/sysdb_ops.c
+index 0e39a629a5823ff49ed02ec4c08a21b66119f06f..2f8e36c6c9a2c2cefe4af5fb78957763304d989a 100644
+--- a/src/db/sysdb_ops.c
++++ b/src/db/sysdb_ops.c
+@@ -1960,16 +1960,34 @@ int sysdb_add_user(struct sss_domain_info *domain,
+     }
+ 
+     if (domain->mpg) {
+-        /* In MPG domains you can't have groups with the same name as users,
+-         * search if a group with the same name exists.
++        /* In MPG domains you can't have groups with the same name or GID
++         * as users, search if a group with the same name exists.
+          * Don't worry about users, if we try to add a user with the same
+          * name the operation will fail */
+ 
+         ret = sysdb_search_group_by_name(tmp_ctx, domain, name, NULL, &msg);
+         if (ret != ENOENT) {
+-            if (ret == EOK) ret = EEXIST;
++            if (ret == EOK) {
++                DEBUG(SSSDBG_OP_FAILURE,
++                      "Group named %s already exists in an MPG domain\n",
++                      name);
++                ret = EEXIST;
++            }
+             goto done;
+         }
++
++        if (strcasecmp(domain->provider, "local") != 0) {
++            ret = sysdb_search_group_by_gid(tmp_ctx, domain, uid, NULL, &msg);
++            if (ret != ENOENT) {
++                if (ret == EOK) {
++                    DEBUG(SSSDBG_OP_FAILURE,
++                        "Group with GID [%"SPRIgid"] already exists in an "
++                        "MPG domain\n", gid);
++                    ret = EEXIST;
++                }
++                goto done;
++            }
++        }
+     }
+ 
+     /* check no other user with the same uid exist */
+@@ -2177,6 +2195,23 @@ int sysdb_add_group(struct sss_domain_info *domain,
+             }
+             goto done;
+         }
++
++        if (strcasecmp(domain->provider, "local") != 0) {
++            ret = sysdb_search_user_by_uid(tmp_ctx, domain, gid, NULL, &msg);
++            if (ret != ENOENT) {
++                if (ret == EOK) {
++                    DEBUG(SSSDBG_TRACE_LIBS,
++                          "User with the same UID exists in MPG domain: "
++                          "[%"SPRIgid"].\n", gid);
++                    ret = EEXIST;
++                } else {
++                    DEBUG(SSSDBG_TRACE_LIBS,
++                          "sysdb_search_user_by_uid failed for gid: "
++                          "[%"SPRIgid"].\n", gid);
++                }
++                goto done;
++            }
++        }
+     }
+ 
+     /* check no other groups with the same gid exist */
+-- 
+2.13.5
+
diff --git a/SOURCES/0009-TESTS-Add-integration-tests-for-the-auto_private_gro.patch b/SOURCES/0009-TESTS-Add-integration-tests-for-the-auto_private_gro.patch
new file mode 100644
index 0000000..a9c1356
--- /dev/null
+++ b/SOURCES/0009-TESTS-Add-integration-tests-for-the-auto_private_gro.patch
@@ -0,0 +1,346 @@
+From 95053cd058a9045c45c59e002fd6078048fdca76 Mon Sep 17 00:00:00 2001
+From: Jakub Hrozek <jhrozek@redhat.com>
+Date: Tue, 3 Oct 2017 16:55:40 +0200
+Subject: [PATCH 09/21] TESTS: Add integration tests for the
+ auto_private_groups option
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Related:
+    https://pagure.io/SSSD/sssd/issue/1872
+
+Reviewed-by: Fabiano Fidêncio <fidencio@redhat.com>
+Reviewed-by: Pavel Březina <pbrezina@redhat.com>
+(cherry picked from commit 6c802b2009c1b6dd0c3306ba97056e64acc0ec9e)
+---
+ src/tests/intg/test_enumeration.py |  79 +++++++++++++-
+ src/tests/intg/test_ldap.py        | 214 +++++++++++++++++++++++++++++++++++++
+ 2 files changed, 290 insertions(+), 3 deletions(-)
+
+diff --git a/src/tests/intg/test_enumeration.py b/src/tests/intg/test_enumeration.py
+index fdb8d376879f756957f8f25fd28b37d7178aeff5..c7d78155c64dc6c85cb4dc070b205bdcfceff6af 100644
+--- a/src/tests/intg/test_enumeration.py
++++ b/src/tests/intg/test_enumeration.py
+@@ -237,9 +237,7 @@ def sanity_rfc2307(request, ldap_conn):
+     create_sssd_fixture(request)
+     return None
+ 
+-
+-@pytest.fixture
+-def sanity_rfc2307_bis(request, ldap_conn):
++def populate_rfc2307bis(request, ldap_conn):
+     ent_list = ldap_ent.List(ldap_conn.ds_inst.base_dn)
+     ent_list.add_user("user1", 1001, 2001)
+     ent_list.add_user("user2", 1002, 2002)
+@@ -266,6 +264,11 @@ def sanity_rfc2307_bis(request, ldap_conn):
+                            [], ["one_user_group1", "one_user_group2"])
+ 
+     create_ldap_fixture(request, ldap_conn, ent_list)
++
++
++@pytest.fixture
++def sanity_rfc2307_bis(request, ldap_conn):
++    populate_rfc2307bis(request, ldap_conn)
+     conf = format_basic_conf(ldap_conn, SCHEMA_RFC2307_BIS)
+     create_conf_fixture(request, conf)
+     create_sssd_fixture(request)
+@@ -695,3 +698,73 @@ def test_vetoed_shells(vetoed_shells):
+                  shell="/bin/default")
+         )
+     )
++
++
++@pytest.fixture
++def sanity_rfc2307_bis_mpg(request, ldap_conn):
++    populate_rfc2307bis(request, ldap_conn)
++
++    ent_list = ldap_ent.List(ldap_conn.ds_inst.base_dn)
++    ent_list.add_group_bis("conflict1", 1001)
++    ent_list.add_group_bis("conflict2", 1002)
++    create_ldap_fixture(request, ldap_conn, ent_list)
++
++    conf = \
++        format_basic_conf(ldap_conn, SCHEMA_RFC2307_BIS) + \
++        unindent("""
++            [domain/LDAP]
++            auto_private_groups = True
++        """).format(**locals())
++    create_conf_fixture(request, conf)
++    create_sssd_fixture(request)
++    return None
++
++
++def test_ldap_auto_private_groups_enumerate(ldap_conn,
++                                            sanity_rfc2307_bis_mpg):
++    """
++    Test the auto_private_groups together with enumeration
++    """
++    passwd_pattern = ent.contains_only(
++        dict(name='user1', passwd='*', uid=1001, gid=1001, gecos='1001',
++             dir='/home/user1', shell='/bin/bash'),
++        dict(name='user2', passwd='*', uid=1002, gid=1002, gecos='1002',
++             dir='/home/user2', shell='/bin/bash'),
++        dict(name='user3', passwd='*', uid=1003, gid=1003, gecos='1003',
++             dir='/home/user3', shell='/bin/bash')
++    )
++    ent.assert_passwd(passwd_pattern)
++
++    group_pattern = ent.contains_only(
++        dict(name='user1', passwd='*', gid=1001, mem=ent.contains_only()),
++        dict(name='user2', passwd='*', gid=1002, mem=ent.contains_only()),
++        dict(name='user3', passwd='*', gid=1003, mem=ent.contains_only()),
++        dict(name='group1', passwd='*', gid=2001, mem=ent.contains_only()),
++        dict(name='group2', passwd='*', gid=2002, mem=ent.contains_only()),
++        dict(name='group3', passwd='*', gid=2003, mem=ent.contains_only()),
++        dict(name='empty_group1', passwd='*', gid=2010,
++             mem=ent.contains_only()),
++        dict(name='empty_group2', passwd='*', gid=2011,
++             mem=ent.contains_only()),
++        dict(name='two_user_group', passwd='*', gid=2012,
++             mem=ent.contains_only("user1", "user2")),
++        dict(name='group_empty_group', passwd='*', gid=2013,
++             mem=ent.contains_only()),
++        dict(name='group_two_empty_groups', passwd='*', gid=2014,
++             mem=ent.contains_only()),
++        dict(name='one_user_group1', passwd='*', gid=2015,
++             mem=ent.contains_only("user1")),
++        dict(name='one_user_group2', passwd='*', gid=2016,
++             mem=ent.contains_only("user2")),
++        dict(name='group_one_user_group', passwd='*', gid=2017,
++             mem=ent.contains_only("user1")),
++        dict(name='group_two_user_group', passwd='*', gid=2018,
++             mem=ent.contains_only("user1", "user2")),
++        dict(name='group_two_one_user_groups', passwd='*', gid=2019,
++             mem=ent.contains_only("user1", "user2"))
++    )
++    ent.assert_group(group_pattern)
++
++    with pytest.raises(KeyError):
++        grp.getgrnam("conflict1")
++    ent.assert_group_by_gid(1002, dict(name="user2", mem=ent.contains_only()))
+diff --git a/src/tests/intg/test_ldap.py b/src/tests/intg/test_ldap.py
+index f2467f1ffe9890049ad73bba6432102d029510e8..a6659b1b78df4d72eb98c208d67ee5d10c9c88ea 100644
+--- a/src/tests/intg/test_ldap.py
++++ b/src/tests/intg/test_ldap.py
+@@ -1169,3 +1169,217 @@ def test_nss_filters_cached(ldap_conn, sanity_nss_filter_cached):
+ 
+     res, _ = call_sssd_getgrgid(0)
+     assert res == NssReturnCode.NOTFOUND
++
++
++@pytest.fixture
++def mpg_setup(request, ldap_conn):
++    ent_list = ldap_ent.List(ldap_conn.ds_inst.base_dn)
++    ent_list.add_user("user1", 1001, 2001)
++    ent_list.add_user("user2", 1002, 2002)
++    ent_list.add_user("user3", 1003, 2003)
++
++    ent_list.add_group_bis("group1", 2001)
++    ent_list.add_group_bis("group2", 2002)
++    ent_list.add_group_bis("group3", 2003)
++
++    ent_list.add_group_bis("two_user_group", 2012, ["user1", "user2"])
++    ent_list.add_group_bis("one_user_group1", 2015, ["user1"])
++    ent_list.add_group_bis("one_user_group2", 2016, ["user2"])
++
++    create_ldap_entries(ldap_conn, ent_list)
++    create_ldap_cleanup(request, ldap_conn, None)
++
++    conf = \
++        format_basic_conf(ldap_conn, SCHEMA_RFC2307_BIS) + \
++        unindent("""
++            [domain/LDAP]
++            auto_private_groups = True
++        """).format(**locals())
++    create_conf_fixture(request, conf)
++    create_sssd_fixture(request)
++    return None
++
++
++def test_ldap_auto_private_groups_direct(ldap_conn, mpg_setup):
++    """
++    Integration test for auto_private_groups
++
++    See also ticket https://pagure.io/SSSD/sssd/issue/1872
++    """
++    # Make sure the user's GID is taken from their uidNumber
++    ent.assert_passwd_by_name("user1", dict(name="user1", uid=1001, gid=1001))
++    # Make sure the private group is resolvable by name and by GID
++    ent.assert_group_by_name("user1", dict(gid=1001, mem=ent.contains_only()))
++    ent.assert_group_by_gid(1001, dict(name="user1", mem=ent.contains_only()))
++
++    # The group referenced in user's gidNumber attribute should be still
++    # visible, but it's fine that it doesn't contain the user as a member
++    # as the group is currently added during the initgroups operation only
++    ent.assert_group_by_name("group1", dict(gid=2001, mem=ent.contains_only()))
++    ent.assert_group_by_gid(2001, dict(name="group1", mem=ent.contains_only()))
++
++    # The user's secondary groups list must be correct as well
++    # Note that the original GID is listed as well -- this is correct and expected
++    # because we save the original GID in the SYSDB_PRIMARY_GROUP_GIDNUM attribute
++    user1_expected_gids = [1001, 2001, 2012, 2015]
++    (res, errno, gids) = sssd_id.call_sssd_initgroups("user1", 1001)
++    assert res == sssd_id.NssReturnCode.SUCCESS
++
++    assert sorted(gids) == sorted(user1_expected_gids), \
++        "result: %s\n expected %s" % (
++            ", ".join(["%s" % s for s in sorted(gids)]),
++            ", ".join(["%s" % s for s in sorted(user1_expected_gids)])
++        )
++
++    # Request user2's private group by GID without resolving the user first.
++    # This must trigger user resolution through by-GID resolution, since the GID
++    # doesn't exist on its own in LDAP
++    ent.assert_group_by_gid(1002, dict(name="user2", mem=ent.contains_only()))
++
++    # Test supplementary groups for user2 as well
++    user1_expected_gids = [1002, 2002, 2012, 2016]
++    (res, errno, gids) = sssd_id.call_sssd_initgroups("user2", 1002)
++    assert res == sssd_id.NssReturnCode.SUCCESS
++
++    assert sorted(gids) == sorted(user1_expected_gids), \
++        "result: %s\n expected %s" % (
++            ", ".join(["%s" % s for s in sorted(gids)]),
++            ", ".join(["%s" % s for s in sorted(user1_expected_gids)])
++        )
++
++    # Request user3's private group by name without resolving the user first
++    # This must trigger user resolution through by-name resolution, since the
++    # name doesn't exist on its own in LDAP
++    ent.assert_group_by_name("user3", dict(gid=1003, mem=ent.contains_only()))
++
++    # Remove entries and request them again to make sure they are not
++    # resolvable anymore
++    cleanup_ldap_entries(ldap_conn, None)
++
++    if subprocess.call(["sss_cache", "-GU"]) != 0:
++        raise Exception("sssd_cache failed")
++
++    with pytest.raises(KeyError):
++        pwd.getpwnam("user1")
++    with pytest.raises(KeyError):
++        grp.getgrnam("user1")
++    with pytest.raises(KeyError):
++        grp.getgrgid(1002)
++    with pytest.raises(KeyError):
++        grp.getgrnam("user3")
++
++
++@pytest.fixture
++def mpg_setup_conflict(request, ldap_conn):
++    ent_list = ldap_ent.List(ldap_conn.ds_inst.base_dn)
++    ent_list.add_user("user1", 1001, 2001)
++    ent_list.add_user("user2", 1002, 2002)
++    ent_list.add_user("user3", 1003, 1003)
++    ent_list.add_group_bis("group1", 1001)
++    ent_list.add_group_bis("group2", 1002)
++    ent_list.add_group_bis("group3", 1003)
++    ent_list.add_group_bis("supp_group", 2015, ["user3"])
++    create_ldap_fixture(request, ldap_conn, ent_list)
++
++    conf = \
++        format_basic_conf(ldap_conn, SCHEMA_RFC2307_BIS) + \
++        unindent("""
++            [domain/LDAP]
++            auto_private_groups = True
++        """).format(**locals())
++    create_conf_fixture(request, conf)
++    create_sssd_fixture(request)
++    return None
++
++
++def test_ldap_auto_private_groups_conflict(ldap_conn, mpg_setup_conflict):
++    """
++    Make sure that conflicts between groups that are auto-created with the
++    help of the auto_private_groups option and between 'real' LDAP groups
++    are handled in a predictable manner.
++    """
++    # Make sure the user's GID is taken from their uidNumber
++    ent.assert_passwd_by_name("user1", dict(name="user1", uid=1001, gid=1001))
++    # Make sure the private group is resolvable by name and by GID
++    ent.assert_group_by_name("user1", dict(gid=1001, mem=ent.contains_only()))
++    ent.assert_group_by_gid(1001, dict(name="user1", mem=ent.contains_only()))
++
++    # Let's request the group with the same ID as user2's private group
++    # The request should match the 'real' group
++    ent.assert_group_by_gid(1002, dict(name="group2", mem=ent.contains_only()))
++    # But because of the GID conflict, the user cannot be resolved
++    with pytest.raises(KeyError):
++        pwd.getpwnam("user2")
++
++    # This user's GID is the same as the UID in this entry. The most important
++    # thing here is that the supplementary groups are correct and the GID
++    # resolves to the private group (as long as the user was requested first)
++    user3_expected_gids = [1003, 2015]
++    ent.assert_passwd_by_name("user3", dict(name="user3", uid=1003, gid=1003))
++    (res, errno, gids) = sssd_id.call_sssd_initgroups("user3", 1003)
++    assert res == sssd_id.NssReturnCode.SUCCESS
++
++    assert sorted(gids) == sorted(user3_expected_gids), \
++        "result: %s\n expected %s" % (
++            ", ".join(["%s" % s for s in sorted(gids)]),
++            ", ".join(["%s" % s for s in sorted(user3_expected_gids)])
++        )
++    # Make sure the private group is resolvable by name and by GID
++    ent.assert_group_by_gid(1003, dict(name="user3", mem=ent.contains_only()))
++    ent.assert_group_by_name("user3", dict(gid=1003, mem=ent.contains_only()))
++
++
++@pytest.fixture
++def mpg_setup_no_gid(request, ldap_conn):
++    ent_list = ldap_ent.List(ldap_conn.ds_inst.base_dn)
++    ent_list.add_user("user1", 1001, 2001)
++
++    ent_list.add_group_bis("group1", 2001)
++    ent_list.add_group_bis("one_user_group1", 2015, ["user1"])
++
++    create_ldap_entries(ldap_conn, ent_list)
++    create_ldap_cleanup(request, ldap_conn, None)
++
++    conf = \
++        format_basic_conf(ldap_conn, SCHEMA_RFC2307_BIS) + \
++        unindent("""
++            [domain/LDAP]
++            auto_private_groups = True
++            ldap_user_gid_number = no_such_attribute
++        """).format(**locals())
++    create_conf_fixture(request, conf)
++    create_sssd_fixture(request)
++    return None
++
++
++def test_ldap_auto_private_groups_direct_no_gid(ldap_conn, mpg_setup_no_gid):
++    """
++    Integration test for auto_private_groups - test that even a user with
++    no GID assigned at all can be resolved including their autogenerated
++    primary group.
++
++    See also ticket https://pagure.io/SSSD/sssd/issue/1872
++    """
++    # Make sure the user's GID is taken from their uidNumber
++    ent.assert_passwd_by_name("user1", dict(name="user1", uid=1001, gid=1001))
++    # Make sure the private group is resolvable by name and by GID
++    ent.assert_group_by_name("user1", dict(gid=1001, mem=ent.contains_only()))
++    ent.assert_group_by_gid(1001, dict(name="user1", mem=ent.contains_only()))
++
++    # The group referenced in user's gidNumber attribute should be still
++    # visible, but shouldn't have any relation to the user
++    ent.assert_group_by_name("group1", dict(gid=2001, mem=ent.contains_only()))
++    ent.assert_group_by_gid(2001, dict(name="group1", mem=ent.contains_only()))
++
++    # The user's secondary groups list must be correct as well. This time only
++    # the generated group and the explicit secondary group are added, since
++    # there is no original GID
++    user1_expected_gids = [1001, 2015]
++    (res, errno, gids) = sssd_id.call_sssd_initgroups("user1", 1001)
++    assert res == sssd_id.NssReturnCode.SUCCESS
++
++    assert sorted(gids) == sorted(user1_expected_gids), \
++        "result: %s\n expected %s" % (
++            ", ".join(["%s" % s for s in sorted(gids)]),
++            ", ".join(["%s" % s for s in sorted(user1_expected_gids)])
++        )
+-- 
+2.13.5
+
diff --git a/SOURCES/0009-sss_cert_derb64_to_ldap_filter-add-sss_certmap-suppo.patch b/SOURCES/0009-sss_cert_derb64_to_ldap_filter-add-sss_certmap-suppo.patch
deleted file mode 100644
index d53da7d..0000000
--- a/SOURCES/0009-sss_cert_derb64_to_ldap_filter-add-sss_certmap-suppo.patch
+++ /dev/null
@@ -1,235 +0,0 @@
-From 2e12cbdc8e2676b045a972045e9dae75b232dc76 Mon Sep 17 00:00:00 2001
-From: Sumit Bose <sbose@redhat.com>
-Date: Thu, 2 Feb 2017 16:34:32 +0100
-Subject: [PATCH 09/15] sss_cert_derb64_to_ldap_filter: add sss_certmap support
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Use certificate mapping library if available to lookup a user by
-certificate in LDAP.
-
-Related to https://pagure.io/SSSD/sssd/issue/3050
-
-Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
-Reviewed-by: Lukáš Slebodník <lslebodn@redhat.com>
----
- Makefile.am                        |  1 +
- src/db/sysdb_ops.c                 |  2 +-
- src/db/sysdb_views.c               |  4 +-
- src/providers/ipa/ipa_views.c      |  2 +-
- src/providers/ldap/ldap_id.c       |  2 +-
- src/tests/cmocka/test_cert_utils.c |  4 +-
- src/util/cert.h                    |  3 ++
- src/util/cert/cert_common.c        | 76 ++++++++++++++++++++++++++++++++------
- 8 files changed, 76 insertions(+), 18 deletions(-)
-
-diff --git a/Makefile.am b/Makefile.am
-index 7947b7a5fbe3ca1034baac1c13c53300994b1bf8..f262cc24832358910dbb92ccd46f93c9eda8a295 100644
---- a/Makefile.am
-+++ b/Makefile.am
-@@ -952,6 +952,7 @@ libsss_cert_la_LIBADD = \
-     $(TALLOC_LIBS) \
-     libsss_crypt.la \
-     libsss_debug.la \
-+    libsss_certmap.la \
-     $(NULL)
- libsss_cert_la_LDFLAGS = \
-     -avoid-version \
-diff --git a/src/db/sysdb_ops.c b/src/db/sysdb_ops.c
-index 8ae25764478e522255b177f9e8de1d3ca1ad43fd..919f22370ff87eff2bf0bb569ca90f1ee699a61e 100644
---- a/src/db/sysdb_ops.c
-+++ b/src/db/sysdb_ops.c
-@@ -4661,7 +4661,7 @@ errno_t sysdb_search_object_by_cert(TALLOC_CTX *mem_ctx,
-     char *user_filter;
- 
-     ret = sss_cert_derb64_to_ldap_filter(mem_ctx, cert, SYSDB_USER_MAPPED_CERT,
--                                         &user_filter);
-+                                         NULL, NULL, &user_filter);
-     if (ret != EOK) {
-         DEBUG(SSSDBG_OP_FAILURE, "sss_cert_derb64_to_ldap_filter failed.\n");
-         return ret;
-diff --git a/src/db/sysdb_views.c b/src/db/sysdb_views.c
-index 9dc48f5b6c414bbc7c64bcd1fe73553f388588bd..1c416dd14049237e9f35d52f154035e3ff861469 100644
---- a/src/db/sysdb_views.c
-+++ b/src/db/sysdb_views.c
-@@ -862,8 +862,8 @@ errno_t sysdb_search_override_by_cert(TALLOC_CTX *mem_ctx,
-         goto done;
-     }
- 
--    ret = sss_cert_derb64_to_ldap_filter(tmp_ctx, cert, SYSDB_USER_CERT,
--                                         &cert_filter);
-+    ret = sss_cert_derb64_to_ldap_filter(tmp_ctx, cert, SYSDB_USER_CERT, NULL,
-+                                         NULL, &cert_filter);
- 
-     if (ret != EOK) {
-         DEBUG(SSSDBG_OP_FAILURE, "sss_cert_derb64_to_ldap_filter failed.\n");
-diff --git a/src/providers/ipa/ipa_views.c b/src/providers/ipa/ipa_views.c
-index 29f589ec1fd05f59175dcc4592e6395941e6e034..5b6fcbc9b7c6f2ea7dbeecb01a5a3fd11b8a6854 100644
---- a/src/providers/ipa/ipa_views.c
-+++ b/src/providers/ipa/ipa_views.c
-@@ -156,7 +156,7 @@ static errno_t dp_id_data_to_override_filter(TALLOC_CTX *mem_ctx,
-         if ((ar->entry_type & BE_REQ_TYPE_MASK) == BE_REQ_BY_CERT) {
-             ret = sss_cert_derb64_to_ldap_filter(mem_ctx, ar->filter_value,
-                          ipa_opts->override_map[IPA_AT_OVERRIDE_USER_CERT].name,
--                         &cert_filter);
-+                         NULL, NULL, &cert_filter);
-             if (ret != EOK) {
-                 DEBUG(SSSDBG_OP_FAILURE,
-                       "sss_cert_derb64_to_ldap_filter failed.\n");
-diff --git a/src/providers/ldap/ldap_id.c b/src/providers/ldap/ldap_id.c
-index a8b4bc2cfc6e9d4e0d74b0e3e036afbcbf7eb26e..8e60769d09383ac8ebe33e5f64fd4fd9788e82cd 100644
---- a/src/providers/ldap/ldap_id.c
-+++ b/src/providers/ldap/ldap_id.c
-@@ -247,7 +247,7 @@ struct tevent_req *users_get_send(TALLOC_CTX *memctx,
-         }
- 
-         ret = sss_cert_derb64_to_ldap_filter(state, filter_value, attr_name,
--                                             &user_filter);
-+                                             NULL, NULL, &user_filter);
-         if (ret != EOK) {
-             DEBUG(SSSDBG_OP_FAILURE,
-                   "sss_cert_derb64_to_ldap_filter failed.\n");
-diff --git a/src/tests/cmocka/test_cert_utils.c b/src/tests/cmocka/test_cert_utils.c
-index 35e8cb7513968079861048a7e8b0631229f202c0..5830131754e4cf318273151b586ef36d6a349829 100644
---- a/src/tests/cmocka/test_cert_utils.c
-+++ b/src/tests/cmocka/test_cert_utils.c
-@@ -297,11 +297,11 @@ void test_sss_cert_derb64_to_ldap_filter(void **state)
-     struct test_state *ts = talloc_get_type_abort(*state, struct test_state);
-     assert_non_null(ts);
- 
--    ret = sss_cert_derb64_to_ldap_filter(ts, NULL, NULL, NULL);
-+    ret = sss_cert_derb64_to_ldap_filter(ts, NULL, NULL, NULL, NULL, NULL);
-     assert_int_equal(ret, EINVAL);
- 
-     ret = sss_cert_derb64_to_ldap_filter(ts, "AAECAwQFBgcICQ==", "attrName",
--                                         &filter);
-+                                         NULL, NULL, &filter);
-     assert_int_equal(ret, EOK);
-     assert_string_equal(filter,
-                         "(attrName=\\00\\01\\02\\03\\04\\05\\06\\07\\08\\09)");
-diff --git a/src/util/cert.h b/src/util/cert.h
-index bb64d0d7a0a48207df60f6e6e554da5e16a16b03..4598aa8df0cd860fed71d9cd2e4beec7f1910578 100644
---- a/src/util/cert.h
-+++ b/src/util/cert.h
-@@ -21,6 +21,7 @@
- #include <talloc.h>
- 
- #include "util/util.h"
-+#include "lib/certmap/sss_certmap.h"
- 
- #ifndef __CERT_H__
- #define __CERT_H__
-@@ -39,6 +40,8 @@ errno_t sss_cert_pem_to_derb64(TALLOC_CTX *mem_ctx, const char *pem,
- 
- errno_t sss_cert_derb64_to_ldap_filter(TALLOC_CTX *mem_ctx, const char *derb64,
-                                        const char *attr_name,
-+                                       struct sss_certmap_ctx *certmap_ctx,
-+                                       struct sss_domain_info *dom,
-                                        char **ldap_filter);
- 
- errno_t bin_to_ldap_filter_value(TALLOC_CTX *mem_ctx,
-diff --git a/src/util/cert/cert_common.c b/src/util/cert/cert_common.c
-index a29696ed3cd9f2168f47323fac97d44e9b49f921..766877089429ff1c01000a3986316c74583e3fa4 100644
---- a/src/util/cert/cert_common.c
-+++ b/src/util/cert/cert_common.c
-@@ -72,12 +72,17 @@ errno_t sss_cert_pem_to_derb64(TALLOC_CTX *mem_ctx, const char *pem,
- 
- errno_t sss_cert_derb64_to_ldap_filter(TALLOC_CTX *mem_ctx, const char *derb64,
-                                        const char *attr_name,
-+                                       struct sss_certmap_ctx *certmap_ctx,
-+                                       struct sss_domain_info *dom,
-                                        char **ldap_filter)
- {
-     int ret;
-     unsigned char *der;
-     size_t der_size;
-     char *val;
-+    char *filter = NULL;
-+    char **domains = NULL;
-+    size_t c;
- 
-     if (derb64 == NULL || attr_name == NULL) {
-         return EINVAL;
-@@ -89,18 +94,67 @@ errno_t sss_cert_derb64_to_ldap_filter(TALLOC_CTX *mem_ctx, const char *derb64,
-         return EINVAL;
-     }
- 
--    ret = bin_to_ldap_filter_value(mem_ctx, der, der_size, &val);
--    talloc_free(der);
--    if (ret != EOK) {
--            DEBUG(SSSDBG_OP_FAILURE, "bin_to_ldap_filter_value failed.\n");
--            return ret;
--    }
-+    if (certmap_ctx == NULL) {
-+        ret = bin_to_ldap_filter_value(mem_ctx, der, der_size, &val);
-+        talloc_free(der);
-+        if (ret != EOK) {
-+                DEBUG(SSSDBG_OP_FAILURE, "bin_to_ldap_filter_value failed.\n");
-+                return ret;
-+        }
- 
--    *ldap_filter = talloc_asprintf(mem_ctx, "(%s=%s)", attr_name, val);
--    talloc_free(val);
--    if (*ldap_filter == NULL) {
--            DEBUG(SSSDBG_OP_FAILURE, "talloc_asprintf failed.\n");
--            return ENOMEM;
-+        *ldap_filter = talloc_asprintf(mem_ctx, "(%s=%s)", attr_name, val);
-+        talloc_free(val);
-+        if (*ldap_filter == NULL) {
-+                DEBUG(SSSDBG_OP_FAILURE, "talloc_asprintf failed.\n");
-+                return ENOMEM;
-+        }
-+    } else {
-+        ret = sss_certmap_get_search_filter(certmap_ctx, der, der_size,
-+                                            &filter, &domains);
-+        talloc_free(der);
-+        if (ret != 0) {
-+            if (ret == ENOENT) {
-+                DEBUG(SSSDBG_OP_FAILURE,
-+                      "Certificate does not match matching-rules.\n");
-+            } else {
-+                DEBUG(SSSDBG_OP_FAILURE,
-+                      "sss_certmap_get_search_filter failed.\n");
-+            }
-+        } else {
-+            if (domains == NULL) {
-+                if (IS_SUBDOMAIN(dom)) {
-+                    DEBUG(SSSDBG_TRACE_FUNC,
-+                          "Rule applies only to local domain.\n");
-+                    ret = ENOENT;
-+                }
-+            } else {
-+                for (c = 0; domains[c] != NULL; c++) {
-+                    if (strcasecmp(dom->name, domains[c]) == 0) {
-+                        DEBUG(SSSDBG_TRACE_FUNC,
-+                              "Rule applies to current domain [%s].\n",
-+                              dom->name);
-+                        ret = EOK;
-+                        break;
-+                    }
-+                }
-+                if (domains[c] == NULL) {
-+                        DEBUG(SSSDBG_TRACE_FUNC,
-+                              "Rule does not apply to current domain [%s].\n",
-+                              dom->name);
-+                    ret = ENOENT;
-+                }
-+            }
-+        }
-+
-+        if (ret == EOK) {
-+            *ldap_filter = talloc_strdup(mem_ctx, filter);
-+            if (*ldap_filter == NULL) {
-+                DEBUG(SSSDBG_OP_FAILURE, "talloc_strdup failed.\n");
-+                ret = ENOMEM;
-+            }
-+        }
-+        sss_certmap_free_filter_and_domains(filter, domains);
-+        return ret;
-     }
- 
-     return EOK;
--- 
-2.9.3
-
diff --git a/SOURCES/0010-CACHE_REQ-Copy-the-cr_domain-list-for-each-request.patch b/SOURCES/0010-CACHE_REQ-Copy-the-cr_domain-list-for-each-request.patch
new file mode 100644
index 0000000..39c79de
--- /dev/null
+++ b/SOURCES/0010-CACHE_REQ-Copy-the-cr_domain-list-for-each-request.patch
@@ -0,0 +1,142 @@
+From ec3334b5a09328de492804c391654073553ff7e7 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Fabiano=20Fid=C3=AAncio?= <fidencio@redhat.com>
+Date: Fri, 20 Oct 2017 09:26:43 +0200
+Subject: [PATCH 10/21] CACHE_REQ: Copy the cr_domain list for each request
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Let's copy the cr_domain list for each request as this list may be
+free'd due to a refresh domains request.
+
+Resolves: https://pagure.io/SSSD/sssd/issue/3551
+
+Signed-off-by: Fabiano Fidêncio <fidencio@redhat.com>
+
+Reviewed-by: Pavel Březina <pbrezina@redhat.com>
+(cherry picked from commit 0f44eefe2ce75a0814c8688495477f6c57f3d39a)
+---
+ src/responder/common/cache_req/cache_req.c        | 14 +++++++--
+ src/responder/common/cache_req/cache_req_domain.c | 38 +++++++++++++++++++++++
+ src/responder/common/cache_req/cache_req_domain.h |  5 +++
+ 3 files changed, 55 insertions(+), 2 deletions(-)
+
+diff --git a/src/responder/common/cache_req/cache_req.c b/src/responder/common/cache_req/cache_req.c
+index abcb9cba351b06e833bacde26a504e5ee3445528..5fed7a2ab8beded2fee91f679a12f9a0ff6013ec 100644
+--- a/src/responder/common/cache_req/cache_req.c
++++ b/src/responder/common/cache_req/cache_req.c
+@@ -699,6 +699,7 @@ struct cache_req_state {
+     const char *domain_name;
+ 
+     /* work data */
++    struct cache_req_domain *cr_domains;
+     struct cache_req_result **results;
+     size_t num_results;
+     bool first_iteration;
+@@ -953,6 +954,7 @@ static errno_t cache_req_select_domains(struct tevent_req *req,
+     bool bypass_cache;
+     bool bypass_dp;
+     bool search;
++    errno_t ret;
+ 
+     state = tevent_req_data(req, struct cache_req_state);
+ 
+@@ -964,12 +966,20 @@ static errno_t cache_req_select_domains(struct tevent_req *req,
+         return EOK;
+     }
+ 
++    ret = cache_req_domain_copy_cr_domains(state,
++                                           state->cr->rctx->cr_domains,
++                                           &state->cr_domains);
++    if (ret != EOK) {
++        DEBUG(SSSDBG_CRIT_FAILURE, "cache_req_copy_cr_domains() failed\n");
++        return EINVAL;
++    }
++
+     if (domain_name != NULL) {
+         CACHE_REQ_DEBUG(SSSDBG_TRACE_FUNC, state->cr,
+                         "Performing a single domain search\n");
+ 
+         cr_domain = cache_req_domain_get_domain_by_name(
+-                                    state->cr->rctx->cr_domains, domain_name);
++                                    state->cr_domains, domain_name);
+         if (cr_domain == NULL) {
+             return ERR_DOMAIN_NOT_FOUND;
+         }
+@@ -978,7 +988,7 @@ static errno_t cache_req_select_domains(struct tevent_req *req,
+         CACHE_REQ_DEBUG(SSSDBG_TRACE_FUNC, state->cr,
+                         "Performing a multi-domain search\n");
+ 
+-        cr_domain = state->cr->rctx->cr_domains;
++        cr_domain = state->cr_domains;
+         check_next = true;
+     }
+ 
+diff --git a/src/responder/common/cache_req/cache_req_domain.c b/src/responder/common/cache_req/cache_req_domain.c
+index 7b58f7c94a77881429f870bc5162fb2fe0aa57c6..15893ba548f6d0e3979010d6d5bbf27441d5fa97 100644
+--- a/src/responder/common/cache_req/cache_req_domain.c
++++ b/src/responder/common/cache_req/cache_req_domain.c
+@@ -47,6 +47,44 @@ cache_req_domain_get_domain_by_name(struct cache_req_domain *domains,
+     return ret;
+ }
+ 
++errno_t
++cache_req_domain_copy_cr_domains(TALLOC_CTX *mem_ctx,
++                                 struct cache_req_domain *src,
++                                 struct cache_req_domain **_dest)
++{
++    struct cache_req_domain *cr_domains = NULL;
++    struct cache_req_domain *cr_domain;
++    struct cache_req_domain *iter;
++    errno_t ret;
++
++    if (src == NULL) {
++        return EINVAL;
++    }
++
++    DLIST_FOR_EACH(iter, src) {
++        cr_domain = talloc_zero(mem_ctx, struct cache_req_domain);
++        if (cr_domain == NULL) {
++            ret = ENOMEM;
++            goto done;
++        }
++
++        cr_domain->domain = iter->domain;
++        cr_domain->fqnames = iter->fqnames;
++
++        DLIST_ADD_END(cr_domains, cr_domain, struct cache_req_domain *);
++    }
++
++    *_dest = cr_domains;
++    ret = EOK;
++
++done:
++    if (ret != EOK) {
++        cache_req_domain_list_zfree(&cr_domains);
++    }
++
++    return ret;
++}
++
+ void cache_req_domain_list_zfree(struct cache_req_domain **cr_domains)
+ {
+     struct cache_req_domain *p, *q, *r;
+diff --git a/src/responder/common/cache_req/cache_req_domain.h b/src/responder/common/cache_req/cache_req_domain.h
+index 3780a5d8d88d76e100738d28d1dd0e697edf5eae..ebdc71dd635d5d8a5d06e30e96c5d4101b6d98bf 100644
+--- a/src/responder/common/cache_req/cache_req_domain.h
++++ b/src/responder/common/cache_req/cache_req_domain.h
+@@ -50,6 +50,11 @@ cache_req_domain_new_list_from_domain_resolution_order(
+                                         const char *domain_resolution_order,
+                                         struct cache_req_domain **_cr_domains);
+ 
++errno_t
++cache_req_domain_copy_cr_domains(TALLOC_CTX *mem_ctx,
++                                 struct cache_req_domain *src,
++                                 struct cache_req_domain **_dest);
++
+ void cache_req_domain_list_zfree(struct cache_req_domain **cr_domains);
+ 
+ 
+-- 
+2.13.5
+
diff --git a/SOURCES/0010-sysdb-add-certmap-related-calls.patch b/SOURCES/0010-sysdb-add-certmap-related-calls.patch
deleted file mode 100644
index 5b33d5f..0000000
--- a/SOURCES/0010-sysdb-add-certmap-related-calls.patch
+++ /dev/null
@@ -1,846 +0,0 @@
-From cfb6a115568ab24fe5df365d1436419b504111ec Mon Sep 17 00:00:00 2001
-From: Sumit Bose <sbose@redhat.com>
-Date: Mon, 6 Feb 2017 10:27:22 +0100
-Subject: [PATCH 10/15] sysdb: add certmap related calls
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Add sysdb calls to write and read data for the certificate mapping
-library to the cache.
-
-Related to https://pagure.io/SSSD/sssd/issue/3050
-
-Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
-Reviewed-by: Lukáš Slebodník <lslebodn@redhat.com>
----
- Makefile.am                           |  17 ++
- src/db/sysdb.h                        |  27 +++
- src/db/sysdb_certmap.c                | 425 ++++++++++++++++++++++++++++++++++
- src/tests/cmocka/test_sysdb_certmap.c | 260 +++++++++++++++++++++
- 4 files changed, 729 insertions(+)
- create mode 100644 src/db/sysdb_certmap.c
- create mode 100644 src/tests/cmocka/test_sysdb_certmap.c
-
-diff --git a/Makefile.am b/Makefile.am
-index f262cc24832358910dbb92ccd46f93c9eda8a295..bd0ca0d303e1742ad26c7648cd24e2c0135af34e 100644
---- a/Makefile.am
-+++ b/Makefile.am
-@@ -254,6 +254,7 @@ if HAVE_CMOCKA
-         test_sysdb_ts_cache \
-         test_sysdb_views \
-         test_sysdb_subdomains \
-+        test_sysdb_certmap \
-         test_sysdb_sudo \
-         test_sysdb_utils \
-         test_wbc_calls \
-@@ -974,6 +975,7 @@ libsss_util_la_SOURCES = \
-     src/db/sysdb_ranges.c \
-     src/db/sysdb_idmap.c \
-     src/db/sysdb_gpo.c \
-+    src/db/sysdb_certmap.c \
-     src/monitor/monitor_sbus.c \
-     src/providers/dp_auth_util.c \
-     src/providers/dp_pam_data_util.c \
-@@ -2773,6 +2775,21 @@ test_sysdb_subdomains_LDADD = \
-     libsss_test_common.la \
-     $(NULL)
- 
-+test_sysdb_certmap_SOURCES = \
-+    src/tests/cmocka/test_sysdb_certmap.c \
-+    $(NULL)
-+test_sysdb_certmap_CFLAGS = \
-+    $(AM_CFLAGS) \
-+    $(NULL)
-+test_sysdb_certmap_LDADD = \
-+    $(CMOCKA_LIBS) \
-+    $(LDB_LIBS) \
-+    $(POPT_LIBS) \
-+    $(TALLOC_LIBS) \
-+    $(SSSD_INTERNAL_LTLIBS) \
-+    libsss_test_common.la \
-+    $(NULL)
-+
- test_sysdb_sudo_SOURCES = \
-     src/tests/cmocka/test_sysdb_sudo.c \
-     $(NULL)
-diff --git a/src/db/sysdb.h b/src/db/sysdb.h
-index 3db22b3689bf6ffd9a48e29c229916e3fac9ca1b..0cbb2c5c02355e9e9a4e73b075f92d16e4855045 100644
---- a/src/db/sysdb.h
-+++ b/src/db/sysdb.h
-@@ -39,6 +39,7 @@
- #define SYSDB_NETGROUP_CONTAINER "cn=Netgroups"
- #define SYSDB_RANGE_CONTAINER "cn=ranges"
- #define SYSDB_VIEW_CONTAINER "cn=views"
-+#define SYSDB_CERTMAP_CONTAINER "cn=certmap"
- #define SYSDB_TMPL_USER_BASE SYSDB_USERS_CONTAINER","SYSDB_DOM_BASE
- #define SYSDB_TMPL_GROUP_BASE SYSDB_GROUPS_CONTAINER","SYSDB_DOM_BASE
- #define SYSDB_TMPL_CUSTOM_BASE SYSDB_CUSTOM_CONTAINER","SYSDB_DOM_BASE
-@@ -46,6 +47,7 @@
- #define SYSDB_TMPL_RANGE_BASE SYSDB_RANGE_CONTAINER","SYSDB_BASE
- #define SYSDB_TMPL_VIEW_BASE SYSDB_VIEW_CONTAINER","SYSDB_BASE
- #define SYSDB_TMPL_VIEW_SEARCH_BASE "cn=%s,"SYSDB_TMPL_VIEW_BASE
-+#define SYSDB_TMPL_CERTMAP_BASE SYSDB_CERTMAP_CONTAINER","SYSDB_BASE
- 
- #define SYSDB_SUBDOMAIN_CLASS "subdomain"
- #define SYSDB_USER_CLASS "user"
-@@ -58,6 +60,7 @@
- #define SYSDB_ID_RANGE_CLASS "idRange"
- #define SYSDB_DOMAIN_ID_RANGE_CLASS "domainIDRange"
- #define SYSDB_TRUSTED_AD_DOMAIN_RANGE_CLASS "TrustedADDomainRange"
-+#define SYSDB_CERTMAP_CLASS "certificateMappingRule"
- 
- #define SYSDB_DN "dn"
- #define SYSDB_NAME "name"
-@@ -158,6 +161,12 @@
- #define SYSDB_DOMAIN_ID "domainID"
- #define SYSDB_ID_RANGE_TYPE "idRangeType"
- 
-+#define SYSDB_CERTMAP_PRIORITY "priority"
-+#define SYSDB_CERTMAP_MATCHING_RULE "matchingRule"
-+#define SYSDB_CERTMAP_MAPPING_RULE "mappingRule"
-+#define SYSDB_CERTMAP_DOMAINS "domains"
-+#define SYSDB_CERTMAP_USER_NAME_HINT "userNameHint"
-+
- #define ORIGINALAD_PREFIX "originalAD"
- #define OVERRIDE_PREFIX "override"
- #define SYSDB_DEFAULT_OVERRIDE_NAME "defaultOverrideName"
-@@ -264,6 +273,7 @@
- #define SYSDB_TMPL_CUSTOM SYSDB_NAME"=%s,cn=%s,"SYSDB_TMPL_CUSTOM_BASE
- #define SYSDB_TMPL_RANGE SYSDB_NAME"=%s,"SYSDB_TMPL_RANGE_BASE
- #define SYSDB_TMPL_OVERRIDE SYSDB_OVERRIDE_ANCHOR_UUID"=%s,"SYSDB_TMPL_VIEW_SEARCH_BASE
-+#define SYSDB_TMPL_CERTMAP SYSDB_NAME"=%s,"SYSDB_TMPL_CERTMAP_BASE
- 
- #define SYSDB_MOD_ADD LDB_FLAG_MOD_ADD
- #define SYSDB_MOD_DEL LDB_FLAG_MOD_DELETE
-@@ -320,6 +330,15 @@ struct range_info {
-     char *range_type;
- };
- 
-+struct certmap_info {
-+    char *name;
-+    uint32_t priority;
-+    char *match_rule;
-+    char *map_rule;
-+    const char **domains;
-+};
-+
-+
- /* These attributes are stored in the timestamp cache */
- extern const char *sysdb_ts_cache_attrs[];
- 
-@@ -619,6 +638,14 @@ uint64_t sss_view_ldb_msg_find_attr_as_uint64(struct sss_domain_info *dom,
-                                               const char *attr_name,
-                                               uint64_t default_value);
- 
-+errno_t sysdb_update_certmap(struct sysdb_ctx *sysdb,
-+                             struct certmap_info **certmaps,
-+                             bool user_name_hint);
-+
-+errno_t sysdb_get_certmap(TALLOC_CTX *mem_ctx, struct sysdb_ctx *sysdb,
-+                          struct certmap_info ***certmaps,
-+                          bool *user_name_hint);
-+
- /* Sysdb initialization.
-  * call this function *only* once to initialize the database and get
-  * the sysdb ctx */
-diff --git a/src/db/sysdb_certmap.c b/src/db/sysdb_certmap.c
-new file mode 100644
-index 0000000000000000000000000000000000000000..4917796b11c3967b4d147ebee7c7e83f09b872ce
---- /dev/null
-+++ b/src/db/sysdb_certmap.c
-@@ -0,0 +1,425 @@
-+/*
-+   SSSD
-+
-+   System Database - certificate mapping rules related calls
-+
-+   Copyright (C) 2017 Sumit Bose <sbose@redhat.com>
-+
-+   This program is free software; you can redistribute it and/or modify
-+   it under the terms of the GNU General Public License as published by
-+   the Free Software Foundation; either version 3 of the License, or
-+   (at your option) any later version.
-+
-+   This program is distributed in the hope that it will be useful,
-+   but WITHOUT ANY WARRANTY; without even the implied warranty of
-+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+   GNU General Public License for more details.
-+
-+   You should have received a copy of the GNU General Public License
-+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
-+*/
-+
-+
-+#include "util/util.h"
-+#include "db/sysdb_private.h"
-+
-+static errno_t sysdb_create_certmap_container(struct sysdb_ctx *sysdb,
-+                                              bool user_name_hint)
-+{
-+    struct ldb_message *msg = NULL;
-+    errno_t ret;
-+
-+    msg = ldb_msg_new(sysdb);
-+    if (msg == NULL) {
-+        ret = ENOMEM;
-+        goto done;
-+    }
-+    msg->dn = ldb_dn_new(msg, sysdb->ldb, SYSDB_TMPL_CERTMAP_BASE);
-+    if (msg->dn == NULL) {
-+        ret = ENOMEM;
-+        goto done;
-+    }
-+    ret = ldb_msg_add_string(msg, "cn", "certmap");
-+    if (ret != LDB_SUCCESS) {
-+        ret = sysdb_error_to_errno(ret);
-+        goto done;
-+    }
-+
-+    ret = ldb_msg_add_string(msg, SYSDB_CERTMAP_USER_NAME_HINT,
-+                             user_name_hint ? "TRUE" : "FALSE");
-+    if (ret != LDB_SUCCESS) {
-+        ret = sysdb_error_to_errno(ret);
-+        goto done;
-+    }
-+
-+    /* do a synchronous add */
-+    ret = ldb_add(sysdb->ldb, msg);
-+    if (ret != LDB_SUCCESS) {
-+        DEBUG(SSSDBG_FATAL_FAILURE,
-+              "Failed to add certmap container (%d, [%s])!\n",
-+               ret, ldb_errstring(sysdb->ldb));
-+        ret = EIO;
-+        goto done;
-+    }
-+
-+    ret = EOK;
-+
-+done:
-+    talloc_free(msg);
-+
-+    return ret;
-+}
-+
-+static errno_t sysdb_certmap_add(struct sysdb_ctx *sysdb,
-+                                 struct certmap_info *certmap)
-+{
-+    struct ldb_message *msg;
-+    struct ldb_message_element *el;
-+    int ret;
-+    TALLOC_CTX *tmp_ctx;
-+    size_t c;
-+
-+    tmp_ctx = talloc_new(NULL);
-+    if (tmp_ctx == NULL) {
-+        DEBUG(SSSDBG_OP_FAILURE, "talloc_new failed");
-+        return ENOMEM;
-+    }
-+
-+    msg = ldb_msg_new(tmp_ctx);
-+    if (msg == NULL) {
-+        DEBUG(SSSDBG_OP_FAILURE, "ldb_msg_new failed.\n");
-+        ret = ENOMEM;
-+        goto done;
-+    }
-+
-+    msg->dn = ldb_dn_new_fmt(tmp_ctx, sysdb->ldb,
-+                             SYSDB_TMPL_CERTMAP, certmap->name);
-+    if (msg->dn == NULL) {
-+        DEBUG(SSSDBG_OP_FAILURE, "ldb_dn_new_fmt failed.\n");
-+        ret = ENOMEM;
-+        goto done;
-+    }
-+
-+    ret = sysdb_add_string(msg, SYSDB_OBJECTCLASS, SYSDB_CERTMAP_CLASS);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_OP_FAILURE, "sysdb_add_string failed.\n");
-+        goto done;
-+    }
-+
-+    ret = sysdb_add_string(msg, SYSDB_NAME, certmap->name);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_OP_FAILURE, "sysdb_add_string failed.\n");
-+        goto done;
-+    }
-+
-+    if (certmap->map_rule != NULL) {
-+        ret = sysdb_add_string(msg, SYSDB_CERTMAP_MAPPING_RULE,
-+                               certmap->map_rule);
-+        if (ret != EOK) {
-+            DEBUG(SSSDBG_OP_FAILURE, "sysdb_add_string failed.\n");
-+            goto done;
-+        }
-+    }
-+
-+    if (certmap->match_rule != NULL) {
-+        ret = sysdb_add_string(msg, SYSDB_CERTMAP_MATCHING_RULE,
-+                               certmap->match_rule);
-+        if (ret != EOK) {
-+            DEBUG(SSSDBG_OP_FAILURE, "sysdb_add_string failed.\n");
-+            goto done;
-+        }
-+    }
-+
-+    if (certmap->domains != NULL) {
-+        for (c = 0; certmap->domains[c] != NULL; c++);
-+        el = talloc_zero(tmp_ctx, struct ldb_message_element);
-+        if (el == NULL) {
-+            DEBUG(SSSDBG_OP_FAILURE, "talloc_zero failed.\n");
-+            ret = ENOMEM;
-+            goto done;
-+        }
-+
-+        el->name = talloc_strdup(el, SYSDB_CERTMAP_DOMAINS);
-+        if(el->name == NULL) {
-+            DEBUG(SSSDBG_OP_FAILURE, "talloc_strdup failed.\n");
-+            ret = ENOMEM;
-+            goto done;
-+        }
-+        el->num_values = c;
-+        el->values = talloc_zero_array(el, struct ldb_val, c + 1);
-+        if (el->values == NULL) {
-+            DEBUG(SSSDBG_OP_FAILURE, "talloc_zero_array failed.\n");
-+            ret = ENOMEM;
-+            goto done;
-+        }
-+
-+        for (c = 0; certmap->domains[c] != NULL; c++) {
-+            el->values[c].data = (uint8_t *) talloc_strdup(el->values,
-+                                                           certmap->domains[c]);
-+            if (el->values[c].data == NULL) {
-+                DEBUG(SSSDBG_OP_FAILURE, "talloc_strdup failed.\n");
-+                ret = ENOMEM;
-+                goto done;
-+            }
-+            el->values[c].length = strlen(certmap->domains[c]);
-+        }
-+
-+        ret = ldb_msg_add(msg, el, LDB_FLAG_MOD_ADD);
-+        if (ret != LDB_SUCCESS) {
-+            DEBUG(SSSDBG_OP_FAILURE, "ldb_msg_add failed.\n");
-+            ret = sysdb_error_to_errno(ret);
-+            goto done;
-+        }
-+    }
-+
-+    ret = sysdb_add_ulong(msg, SYSDB_CERTMAP_PRIORITY,
-+                          (unsigned long)certmap->priority);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_OP_FAILURE, "sysdb_add_ulong failed.\n");
-+        goto done;
-+    }
-+
-+    ret = ldb_add(sysdb->ldb, msg);
-+    if (ret != LDB_SUCCESS) {
-+        DEBUG(SSSDBG_OP_FAILURE, "ldb_add failed.\n");
-+        ret = sysdb_error_to_errno(ret);
-+        goto done;
-+    }
-+
-+    ret = EOK;
-+
-+done:
-+    if (ret) {
-+        DEBUG(SSSDBG_TRACE_FUNC, "Error: %d (%s)\n", ret, sss_strerror(ret));
-+    }
-+    talloc_zfree(tmp_ctx);
-+    return ret;
-+}
-+
-+errno_t sysdb_update_certmap(struct sysdb_ctx *sysdb,
-+                             struct certmap_info **certmaps,
-+                             bool user_name_hint)
-+{
-+    size_t c;
-+    struct ldb_dn *container_dn = NULL;
-+    bool in_transaction = false;
-+    int ret;
-+    int sret;
-+
-+    if (certmaps == NULL) {
-+        return EINVAL;
-+    }
-+
-+    container_dn = ldb_dn_new(sysdb, sysdb->ldb, SYSDB_TMPL_CERTMAP_BASE);
-+    if (container_dn == NULL) {
-+        DEBUG(SSSDBG_OP_FAILURE, "ldb_dn_new failed.\n");
-+        return ENOMEM;
-+    }
-+
-+    ret = sysdb_transaction_start(sysdb);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_OP_FAILURE, "sysdb_transaction_start failed.\n");
-+        goto done;
-+    }
-+    in_transaction = true;
-+
-+    ret = sysdb_delete_recursive(sysdb, container_dn, true);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_OP_FAILURE, "sysdb_delete_recursive failed.\n");
-+        goto done;
-+    }
-+    ret = sysdb_create_certmap_container(sysdb, user_name_hint);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_OP_FAILURE, "sysdb_create_certmap_container failed.\n");
-+        goto done;
-+    }
-+
-+    for (c = 0; certmaps[c] != NULL; c++) {
-+        ret = sysdb_certmap_add(sysdb, certmaps[c]);
-+        if (ret != EOK) {
-+            DEBUG(SSSDBG_OP_FAILURE, "sysdb_certmap_add failed.\n");
-+            goto done;
-+        }
-+    }
-+
-+    ret = sysdb_transaction_commit(sysdb);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_CRIT_FAILURE, "sysdb_transaction_commit failed.\n");
-+        goto done;
-+    }
-+    in_transaction = false;
-+
-+done:
-+    if (in_transaction) {
-+        sret = sysdb_transaction_cancel(sysdb);
-+        if (sret != EOK) {
-+            DEBUG(SSSDBG_CRIT_FAILURE, "Could not cancel transaction.\n");
-+        }
-+    }
-+
-+    talloc_free(container_dn);
-+
-+    return ret;
-+}
-+
-+errno_t sysdb_get_certmap(TALLOC_CTX *mem_ctx, struct sysdb_ctx *sysdb,
-+                          struct certmap_info ***certmaps, bool *user_name_hint)
-+{
-+    size_t c;
-+    size_t d;
-+    struct ldb_dn *container_dn = NULL;
-+    int ret;
-+    struct certmap_info **maps;
-+    TALLOC_CTX *tmp_ctx = NULL;
-+    struct ldb_result *res;
-+    const char *tmp_str;
-+    uint64_t tmp_uint;
-+    struct ldb_message_element *tmp_el;
-+    const char *attrs[] = {SYSDB_NAME,
-+                           SYSDB_CERTMAP_PRIORITY,
-+                           SYSDB_CERTMAP_MATCHING_RULE,
-+                           SYSDB_CERTMAP_MAPPING_RULE,
-+                           SYSDB_CERTMAP_DOMAINS,
-+                           NULL};
-+    const char *config_attrs[] = {SYSDB_CERTMAP_USER_NAME_HINT,
-+                                  NULL};
-+    size_t num_values;
-+    bool hint = false;
-+
-+    tmp_ctx = talloc_new(NULL);
-+    if (tmp_ctx == NULL) {
-+        ret = ENOMEM;
-+        goto done;
-+    }
-+
-+    container_dn = ldb_dn_new(tmp_ctx, sysdb->ldb, SYSDB_TMPL_CERTMAP_BASE);
-+    if (container_dn == NULL) {
-+        DEBUG(SSSDBG_OP_FAILURE, "ldb_dn_new failed.\n");
-+        ret = ENOMEM;
-+        goto done;
-+    }
-+
-+    ret = ldb_search(sysdb->ldb, tmp_ctx, &res, container_dn, LDB_SCOPE_BASE,
-+                     config_attrs, SYSDB_CERTMAP_USER_NAME_HINT"=*");
-+    if (ret != LDB_SUCCESS || res->count != 1) {
-+        DEBUG(SSSDBG_CRIT_FAILURE,
-+              "Failed to read certmap config, skipping.\n");
-+    } else {
-+        hint = ldb_msg_find_attr_as_bool(res->msgs[0],
-+                                         SYSDB_CERTMAP_USER_NAME_HINT, false);
-+    }
-+
-+    ret = ldb_search(sysdb->ldb, tmp_ctx, &res,
-+                     container_dn, LDB_SCOPE_SUBTREE,
-+                     attrs, "objectclass=%s", SYSDB_CERTMAP_CLASS);
-+    if (ret != LDB_SUCCESS) {
-+        DEBUG(SSSDBG_OP_FAILURE, "ldb_search failed.\n");
-+        ret = EIO;
-+        goto done;
-+    }
-+
-+    if (res->count == 0) {
-+        DEBUG(SSSDBG_TRACE_FUNC, "No certificate maps found.\n");
-+        ret = ENOENT;
-+        goto done;
-+    }
-+
-+    maps = talloc_zero_array(tmp_ctx, struct certmap_info *, res->count + 1);
-+    if (maps == NULL) {
-+        DEBUG(SSSDBG_OP_FAILURE, "talloc_zero_array failed.\n");
-+        ret = ENOMEM;
-+        goto done;
-+    }
-+
-+    for (c = 0; c < res->count; c++) {
-+        maps[c] = talloc_zero(maps, struct certmap_info);
-+        if (maps[c] == NULL) {
-+            ret = ENOMEM;
-+            goto done;
-+        }
-+        tmp_str = ldb_msg_find_attr_as_string(res->msgs[c], SYSDB_NAME, NULL);
-+        if (tmp_str == NULL) {
-+            DEBUG(SSSDBG_MINOR_FAILURE, "The object [%s] doesn't have a name.\n",
-+                                       ldb_dn_get_linearized(res->msgs[c]->dn));
-+            ret = EINVAL;
-+            goto done;
-+        }
-+
-+        maps[c]->name = talloc_strdup(maps, tmp_str);
-+        if (maps[c]->name == NULL) {
-+            ret = ENOMEM;
-+            goto done;
-+        }
-+
-+        tmp_str = ldb_msg_find_attr_as_string(res->msgs[c],
-+                                              SYSDB_CERTMAP_MAPPING_RULE, NULL);
-+        if (tmp_str != NULL) {
-+            maps[c]->map_rule = talloc_strdup(maps, tmp_str);
-+            if (maps[c]->map_rule == NULL) {
-+                DEBUG(SSSDBG_OP_FAILURE, "talloc_strdup failed.\n");
-+                ret = ENOMEM;
-+                goto done;
-+            }
-+        }
-+
-+        tmp_str = ldb_msg_find_attr_as_string(res->msgs[c],
-+                                              SYSDB_CERTMAP_MATCHING_RULE, NULL);
-+        if (tmp_str != NULL) {
-+            maps[c]->match_rule = talloc_strdup(maps, tmp_str);
-+            if (maps[c]->match_rule == NULL) {
-+                DEBUG(SSSDBG_OP_FAILURE, "talloc_strdup failed.\n");
-+                ret = ENOMEM;
-+                goto done;
-+            }
-+        }
-+
-+        tmp_uint = ldb_msg_find_attr_as_uint64(res->msgs[c],
-+                                               SYSDB_CERTMAP_PRIORITY,
-+                                               (uint64_t) -1);
-+        if (tmp_uint != (uint64_t) -1) {
-+            if (tmp_uint >= UINT32_MAX) {
-+                DEBUG(SSSDBG_OP_FAILURE, "Priority value [%lu] too large.\n",
-+                                         (unsigned long) tmp_uint);
-+                ret = EINVAL;
-+                goto done;
-+            }
-+
-+            maps[c]->priority = (uint32_t) tmp_uint;
-+        }
-+
-+        tmp_el = ldb_msg_find_element(res->msgs[c], SYSDB_CERTMAP_DOMAINS);
-+        if (tmp_el != NULL) {
-+            num_values = tmp_el->num_values;
-+        } else {
-+            num_values = 0;
-+        }
-+
-+        maps[c]->domains = talloc_zero_array(maps[c], const char *,
-+                                             num_values + 1);
-+        if (maps[c]->domains == NULL) {
-+            DEBUG(SSSDBG_OP_FAILURE, "talloc_zero_array failed.\n");
-+            ret = ENOMEM;
-+            goto done;
-+        }
-+
-+        for (d = 0; d < num_values; d++) {
-+            maps[c]->domains[d] = talloc_strndup(maps[c]->domains,
-+                                            (char *) tmp_el->values[d].data,
-+                                            tmp_el->values[d].length);
-+            if (maps[c]->domains[d] == NULL) {
-+                DEBUG(SSSDBG_OP_FAILURE, "talloc_strndup failed.\n");
-+                ret = ENOMEM;
-+                goto done;
-+            }
-+        }
-+    }
-+
-+    *certmaps = talloc_steal(mem_ctx, maps);
-+    *user_name_hint = hint;
-+    ret = EOK;
-+
-+done:
-+    talloc_free(tmp_ctx);
-+
-+    return ret;
-+}
-diff --git a/src/tests/cmocka/test_sysdb_certmap.c b/src/tests/cmocka/test_sysdb_certmap.c
-new file mode 100644
-index 0000000000000000000000000000000000000000..fb07165561779226935f436c308c85abfc305635
---- /dev/null
-+++ b/src/tests/cmocka/test_sysdb_certmap.c
-@@ -0,0 +1,260 @@
-+/*
-+    SSSD
-+
-+    sysdb_certmap - Tests for sysdb certmap realted calls
-+
-+    Authors:
-+        Jakub Hrozek <jhrozek@redhat.com>
-+
-+    Copyright (C) 2017 Red Hat
-+
-+    This program is free software; you can redistribute it and/or modify
-+    it under the terms of the GNU General Public License as published by
-+    the Free Software Foundation; either version 3 of the License, or
-+    (at your option) any later version.
-+
-+    This program is distributed in the hope that it will be useful,
-+    but WITHOUT ANY WARRANTY; without even the implied warranty of
-+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+    GNU General Public License for more details.
-+
-+    You should have received a copy of the GNU General Public License
-+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
-+*/
-+
-+#include <stdarg.h>
-+#include <stddef.h>
-+#include <setjmp.h>
-+#include <cmocka.h>
-+#include <popt.h>
-+
-+#include "tests/cmocka/common_mock.h"
-+#include "tests/common.h"
-+
-+#define TESTS_PATH "certmap_" BASE_FILE_STEM
-+#define TEST_CONF_DB "test_sysdb_certmap.ldb"
-+#define TEST_ID_PROVIDER "ldap"
-+#define TEST_DOM_NAME "certmap_test"
-+
-+struct certmap_test_ctx {
-+    struct sss_test_ctx *tctx;
-+};
-+
-+static int test_sysdb_setup(void **state)
-+{
-+    struct certmap_test_ctx *test_ctx;
-+    struct sss_test_conf_param params[] = {
-+        { NULL, NULL },             /* Sentinel */
-+    };
-+
-+    assert_true(leak_check_setup());
-+
-+    test_ctx = talloc_zero(global_talloc_context,
-+                           struct certmap_test_ctx);
-+    assert_non_null(test_ctx);
-+    check_leaks_push(test_ctx);
-+
-+    test_dom_suite_setup(TESTS_PATH);
-+
-+    test_ctx->tctx = create_dom_test_ctx(test_ctx, TESTS_PATH,
-+                                         TEST_CONF_DB, TEST_DOM_NAME,
-+                                         TEST_ID_PROVIDER, params);
-+    assert_non_null(test_ctx->tctx);
-+
-+    *state = test_ctx;
-+    return 0;
-+}
-+
-+static int test_sysdb_teardown(void **state)
-+{
-+    struct certmap_test_ctx *test_ctx =
-+        talloc_get_type(*state, struct certmap_test_ctx);
-+
-+    test_dom_suite_cleanup(TESTS_PATH, TEST_CONF_DB, TEST_DOM_NAME);
-+    talloc_free(test_ctx->tctx);
-+    assert_true(check_leaks_pop(test_ctx));
-+    talloc_free(test_ctx);
-+    assert_true(leak_check_teardown());
-+    return 0;
-+}
-+
-+static void test_sysdb_get_certmap_not_exists(void **state)
-+{
-+    int ret;
-+    struct certmap_info **certmap;
-+    bool user_name_hint;
-+    struct certmap_test_ctx *ctctx = talloc_get_type(*state,
-+                                                     struct certmap_test_ctx);
-+
-+    ret = sysdb_get_certmap(ctctx, ctctx->tctx->sysdb, &certmap,
-+                            &user_name_hint);
-+    assert_int_equal(ret, ENOENT);
-+
-+}
-+
-+static void check_certmap(struct certmap_info *m, struct certmap_info *r,
-+                          size_t exp_domains)
-+{
-+    size_t d;
-+
-+    assert_non_null(r);
-+    assert_non_null(m);
-+    assert_string_equal(m->name, r->name);
-+
-+    if (r->map_rule == NULL) {
-+        assert_null(m->map_rule);
-+    } else {
-+        assert_string_equal(m->map_rule, r->map_rule);
-+    }
-+
-+    if (r->match_rule == NULL) {
-+        assert_null(m->match_rule);
-+    } else {
-+        assert_string_equal(m->match_rule, r->match_rule);
-+    }
-+
-+    assert_int_equal(m->priority, r->priority);
-+    assert_non_null(m->domains);
-+    if (r->domains == NULL) {
-+        assert_null(m->domains[0]);
-+    } else {
-+        for (d = 0; r->domains[d]; d++) {
-+            assert_non_null(m->domains[d]);
-+            assert_true(string_in_list(m->domains[d], discard_const(r->domains),
-+                                       true));
-+        }
-+
-+        assert_int_equal(d, exp_domains);
-+    }
-+
-+}
-+
-+static void test_sysdb_update_certmap(void **state)
-+{
-+    int ret;
-+    const char *domains[] = { "dom1.test", "dom2.test", "dom3.test", NULL };
-+    struct certmap_info map_a = { discard_const("map_a"), 11, discard_const("abc"), discard_const("def"), NULL };
-+    struct certmap_info map_b = { discard_const("map_b"), 22, discard_const("abc"), NULL, domains };
-+    struct certmap_info *certmap_empty[] = { NULL };
-+    struct certmap_info *certmap_a[] = { &map_a, NULL };
-+    struct certmap_info *certmap_b[] = { &map_b, NULL };
-+    struct certmap_info *certmap_ab[] = { &map_a, &map_b, NULL };
-+    struct certmap_info **certmap;
-+    struct certmap_test_ctx *ctctx = talloc_get_type(*state,
-+                                                     struct certmap_test_ctx);
-+    bool user_name_hint;
-+
-+    ret = sysdb_update_certmap(ctctx->tctx->sysdb, NULL, false);
-+    assert_int_equal(ret, EINVAL);
-+
-+    ret = sysdb_update_certmap(ctctx->tctx->sysdb, certmap_empty, false);
-+    assert_int_equal(ret, EOK);
-+
-+    ret = sysdb_get_certmap(ctctx, ctctx->tctx->sysdb, &certmap,
-+                            &user_name_hint);
-+    assert_int_equal(ret, ENOENT);
-+
-+    ret = sysdb_update_certmap(ctctx->tctx->sysdb, certmap_a, false);
-+    assert_int_equal(ret, EOK);
-+
-+    ret = sysdb_get_certmap(ctctx, ctctx->tctx->sysdb, &certmap,
-+                            &user_name_hint);
-+    assert_int_equal(ret, EOK);
-+    assert_false(user_name_hint);
-+    assert_non_null(certmap);
-+    assert_non_null(certmap[0]);
-+    assert_string_equal(certmap[0]->name, map_a.name);
-+    assert_string_equal(certmap[0]->map_rule, map_a.map_rule);
-+    assert_string_equal(certmap[0]->match_rule, map_a.match_rule);
-+    assert_int_equal(certmap[0]->priority, map_a.priority);
-+    assert_non_null(certmap[0]->domains);
-+    assert_null(certmap[0]->domains[0]);
-+    assert_null(certmap[1]);
-+    check_certmap(certmap[0], &map_a, 0);
-+    talloc_free(certmap);
-+
-+    ret = sysdb_update_certmap(ctctx->tctx->sysdb, certmap_b, true);
-+    assert_int_equal(ret, EOK);
-+
-+    ret = sysdb_get_certmap(ctctx, ctctx->tctx->sysdb, &certmap,
-+                            &user_name_hint);
-+    assert_int_equal(ret, EOK);
-+    assert_true(user_name_hint);
-+    assert_non_null(certmap);
-+    assert_non_null(certmap[0]);
-+
-+    check_certmap(certmap[0], &map_b, 3);
-+    assert_null(certmap[1]);
-+    talloc_free(certmap);
-+
-+    ret = sysdb_update_certmap(ctctx->tctx->sysdb, certmap_ab, false);
-+    assert_int_equal(ret, EOK);
-+
-+    ret = sysdb_get_certmap(ctctx, ctctx->tctx->sysdb, &certmap,
-+                            &user_name_hint);
-+    assert_int_equal(ret, EOK);
-+    assert_false(user_name_hint);
-+    assert_non_null(certmap);
-+    assert_non_null(certmap[0]);
-+    assert_non_null(certmap[1]);
-+    assert_null(certmap[2]);
-+    if (strcmp(certmap[0]->name, "map_a") == 0) {
-+        check_certmap(certmap[0], &map_a, 0);
-+        check_certmap(certmap[1], &map_b, 3);
-+    } else {
-+        check_certmap(certmap[0], &map_b, 3);
-+        check_certmap(certmap[1], &map_a, 0);
-+    }
-+    talloc_free(certmap);
-+}
-+
-+int main(int argc, const char *argv[])
-+{
-+    int rv;
-+    int no_cleanup = 0;
-+    poptContext pc;
-+    int opt;
-+    struct poptOption long_options[] = {
-+        POPT_AUTOHELP
-+        SSSD_DEBUG_OPTS
-+        {"no-cleanup", 'n', POPT_ARG_NONE, &no_cleanup, 0,
-+         _("Do not delete the test database after a test run"), NULL },
-+        POPT_TABLEEND
-+    };
-+
-+    const struct CMUnitTest tests[] = {
-+        cmocka_unit_test_setup_teardown(test_sysdb_get_certmap_not_exists,
-+                                        test_sysdb_setup,
-+                                        test_sysdb_teardown),
-+        cmocka_unit_test_setup_teardown(test_sysdb_update_certmap,
-+                                        test_sysdb_setup,
-+                                        test_sysdb_teardown),
-+    };
-+
-+    /* Set debug level to invalid value so we can deside if -d 0 was used. */
-+    debug_level = SSSDBG_INVALID;
-+
-+    pc = poptGetContext(argv[0], argc, argv, long_options, 0);
-+    while((opt = poptGetNextOpt(pc)) != -1) {
-+        switch(opt) {
-+        default:
-+            fprintf(stderr, "\nInvalid option %s: %s\n\n",
-+                    poptBadOption(pc, 0), poptStrerror(opt));
-+            poptPrintUsage(pc, stderr, 0);
-+            return 1;
-+        }
-+    }
-+    poptFreeContext(pc);
-+
-+    DEBUG_CLI_INIT(debug_level);
-+
-+    tests_set_cwd();
-+    test_dom_suite_cleanup(TESTS_PATH, TEST_CONF_DB, LOCAL_SYSDB_FILE);
-+    test_dom_suite_setup(TESTS_PATH);
-+    rv = cmocka_run_group_tests(tests, NULL, NULL);
-+
-+    if (rv == 0 && no_cleanup == 0) {
-+        test_dom_suite_cleanup(TESTS_PATH, TEST_CONF_DB, LOCAL_SYSDB_FILE);
-+    }
-+    return rv;
-+}
--- 
-2.9.3
-
diff --git a/SOURCES/0011-IPA-add-certmap-support.patch b/SOURCES/0011-IPA-add-certmap-support.patch
deleted file mode 100644
index d2b8ff6..0000000
--- a/SOURCES/0011-IPA-add-certmap-support.patch
+++ /dev/null
@@ -1,484 +0,0 @@
-From d51754859a83e7fedf0cac90ad8bf5de09f35463 Mon Sep 17 00:00:00 2001
-From: Sumit Bose <sbose@redhat.com>
-Date: Mon, 6 Feb 2017 10:28:46 +0100
-Subject: [PATCH 11/15] IPA: add certmap support
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Read certificate mapping data from the IPA server and configure the
-certificate mapping library accordingly.
-
-Related to https://pagure.io/SSSD/sssd/issue/3050
-
-Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
-Reviewed-by: Lukáš Slebodník <lslebodn@redhat.com>
----
- src/providers/ipa/ipa_config.h            |   2 +
- src/providers/ipa/ipa_subdomains.c        | 354 ++++++++++++++++++++++++++++++
- src/providers/ipa/ipa_subdomains_server.c |   4 +
- src/providers/ldap/ldap_id.c              |   4 +-
- src/providers/ldap/sdap.h                 |   4 +
- 5 files changed, 367 insertions(+), 1 deletion(-)
-
-diff --git a/src/providers/ipa/ipa_config.h b/src/providers/ipa/ipa_config.h
-index 2f1e147d7edab0aca2a16269c6a73bc607b21bd5..60f2d5d7b71227a1d86889ceaf6f0f9ac868480d 100644
---- a/src/providers/ipa/ipa_config.h
-+++ b/src/providers/ipa/ipa_config.h
-@@ -37,6 +37,8 @@
- #define IPA_CONFIG_SEARCH_BASE_TEMPLATE "cn=etc,%s"
- #define IPA_CONFIG_FILTER "(&(cn=ipaConfig)(objectClass=ipaGuiConfig))"
- 
-+#define IPA_OC_CONFIG "ipaConfig"
-+
- struct tevent_req * ipa_get_config_send(TALLOC_CTX *mem_ctx,
-                                         struct tevent_context *ev,
-                                         struct sdap_handle *sh,
-diff --git a/src/providers/ipa/ipa_subdomains.c b/src/providers/ipa/ipa_subdomains.c
-index b2e96b204213a52014edcc6042ffa1ff8152b8bf..7537550606ef09c0b87a80932c75aa4f93c0efab 100644
---- a/src/providers/ipa/ipa_subdomains.c
-+++ b/src/providers/ipa/ipa_subdomains.c
-@@ -56,6 +56,24 @@
- 
- #define IPA_SUBDOMAIN_DISABLED_PERIOD 3600
- 
-+#define IPA_OC_CERTMAP_CONFIG_OBJECT "ipaCertMapConfigObject"
-+#define IPA_CERTMAP_PROMPT_USERNAME "ipaCertMapPromptUserName"
-+
-+#define IPA_OC_CERTMAP_RULE "ipaCertMapRule"
-+#define IPA_CERTMAP_MAPRULE "ipaCertMapMapRule"
-+#define IPA_CERTMAP_MATCHRULE "ipaCertMapMatchRule"
-+#define IPA_CERTMAP_PRIORITY "ipaCertMapPriority"
-+#define IPA_ENABLED_FLAG "ipaEnabledFlag"
-+#define IPA_TRUE_VALUE "TRUE"
-+#define IPA_ASSOCIATED_DOMAIN "associatedDomain"
-+
-+#define OBJECTCLASS "objectClass"
-+
-+#define CERTMAP_FILTER "(|(&("OBJECTCLASS"="IPA_OC_CERTMAP_RULE")" \
-+                              "("IPA_ENABLED_FLAG"="IPA_TRUE_VALUE"))" \
-+                          "("OBJECTCLASS"="IPA_OC_CERTMAP_CONFIG_OBJECT"))"
-+
-+
- struct ipa_subdomains_ctx {
-     struct be_ctx *be_ctx;
-     struct ipa_id_ctx *ipa_id_ctx;
-@@ -286,6 +304,193 @@ done:
-     return ret;
- }
- 
-+struct priv_sss_debug {
-+    int level;
-+};
-+
-+void ext_debug(void *private, const char *file, long line, const char *function,
-+               const char *format, ...)
-+{
-+    va_list ap;
-+    struct priv_sss_debug *data = private;
-+    int level = SSSDBG_OP_FAILURE;
-+
-+    if (data != NULL) {
-+        level = data->level;
-+    }
-+
-+    if (DEBUG_IS_SET(level)) {
-+        va_start(ap, format);
-+        sss_vdebug_fn(file, line, function, level, APPEND_LINE_FEED,
-+                      format, ap);
-+        va_end(ap);
-+    }
-+}
-+
-+static errno_t ipa_certmap_parse_results(TALLOC_CTX *mem_ctx,
-+                                         struct sss_domain_info *domain,
-+                                         struct sdap_options *sdap_opts,
-+                                         size_t count,
-+                                         struct sysdb_attrs **reply,
-+                                         struct certmap_info ***_certmap_list)
-+{
-+    struct certmap_info **certmap_list = NULL;
-+    struct certmap_info *m;
-+    const char *value;
-+    const char **values;
-+    size_t c;
-+    size_t lc = 0;
-+    int ret;
-+    struct sss_certmap_ctx *certmap_ctx = NULL;
-+    const char **ocs = NULL;
-+    bool user_name_hint = false;
-+
-+    certmap_list = talloc_zero_array(mem_ctx, struct certmap_info *, count + 1);
-+    if (certmap_list == NULL) {
-+        DEBUG(SSSDBG_OP_FAILURE, "talloc_array failed.\n");
-+        return ENOMEM;
-+    }
-+
-+    for (c = 0; c < count; c++) {
-+        ret = sysdb_attrs_get_string_array(reply[c], SYSDB_OBJECTCLASS, mem_ctx,
-+                                           &ocs);
-+        if (ret != EOK) {
-+            DEBUG(SSSDBG_OP_FAILURE,
-+                  "Missing objectclasses for config objects.\n");
-+            ret = EINVAL;
-+            goto done;
-+        }
-+
-+        if (string_in_list(IPA_OC_CERTMAP_CONFIG_OBJECT, discard_const(ocs),
-+                           false)) {
-+            ret = sysdb_attrs_get_bool(reply[c], IPA_CERTMAP_PROMPT_USERNAME,
-+                                       &user_name_hint);
-+            if (ret != EOK) {
-+                DEBUG(SSSDBG_CRIT_FAILURE,
-+                      "Failed to read user name hint option, skipping.\n");
-+            }
-+            continue;
-+        }
-+
-+        m = talloc_zero(certmap_list, struct certmap_info);
-+        if (m == NULL) {
-+            DEBUG(SSSDBG_OP_FAILURE, "talloc_zero failed.\n");
-+            ret = ENOMEM;
-+            goto done;
-+        }
-+
-+        ret = sysdb_attrs_get_string(reply[c], IPA_CN, &value);
-+        if (ret != EOK) {
-+            DEBUG(SSSDBG_OP_FAILURE, "sysdb_attrs_get_string failed.\n");
-+            goto done;
-+        }
-+
-+        m->name = talloc_strdup(m, value);
-+        if (m->name == NULL) {
-+            DEBUG(SSSDBG_OP_FAILURE, "talloc_strdup failed.\n");
-+            ret = ENOMEM;
-+            goto done;
-+        }
-+
-+        ret = sysdb_attrs_get_string(reply[c], IPA_CERTMAP_MATCHRULE, &value);
-+        if (ret == EOK) {
-+            m->match_rule = talloc_strdup(m, value);
-+            if (m->match_rule == NULL) {
-+                DEBUG(SSSDBG_OP_FAILURE, "talloc_strdup failed.\n");
-+                ret = ENOMEM;
-+                goto done;
-+            }
-+        } else if (ret != ENOENT) {
-+            DEBUG(SSSDBG_OP_FAILURE, "sysdb_attrs_get_string failed.\n");
-+            goto done;
-+        }
-+
-+        ret = sysdb_attrs_get_string(reply[c], IPA_CERTMAP_MAPRULE, &value);
-+        if (ret == EOK) {
-+            m->map_rule = talloc_strdup(m, value);
-+            if (m->map_rule == NULL) {
-+                DEBUG(SSSDBG_OP_FAILURE, "talloc_strdup failed.\n");
-+                ret = ENOMEM;
-+                goto done;
-+            }
-+        } else if (ret != ENOENT) {
-+            DEBUG(SSSDBG_OP_FAILURE, "sysdb_attrs_get_string failed.\n");
-+            goto done;
-+        }
-+
-+        ret = sysdb_attrs_get_string_array(reply[c], IPA_ASSOCIATED_DOMAIN, m,
-+                                           &values);
-+        if (ret == EOK) {
-+            m->domains = values;
-+        } else if (ret != ENOENT) {
-+            DEBUG(SSSDBG_OP_FAILURE, "sysdb_attrs_get_string failed.\n");
-+            goto done;
-+        }
-+
-+        ret = sysdb_attrs_get_uint32_t(reply[c], IPA_CERTMAP_PRIORITY,
-+                                       &m->priority);
-+        if (ret != EOK && ret != ENOENT) {
-+            DEBUG(SSSDBG_OP_FAILURE, "sysdb_attrs_get_string failed.\n");
-+            goto done;
-+        } else if (ret == ENOENT) {
-+            m->priority = SSS_CERTMAP_MIN_PRIO;
-+        }
-+
-+        certmap_list[lc++] = m;
-+    }
-+
-+    certmap_list[lc] = NULL;
-+
-+    ret = sss_certmap_init(mem_ctx, ext_debug, NULL, &certmap_ctx);
-+    if (ret != 0) {
-+        DEBUG(SSSDBG_OP_FAILURE, "sss_certmap_init failed.\n");
-+        goto done;
-+    }
-+
-+    for (c = 0; certmap_list[c] != NULL; c++) {
-+        DEBUG(SSSDBG_TRACE_ALL, "Trying to add rule [%s][%d][%s][%s].\n",
-+                                certmap_list[c]->name,
-+                                certmap_list[c]->priority,
-+                                certmap_list[c]->match_rule,
-+                                certmap_list[c]->map_rule);
-+
-+        ret = sss_certmap_add_rule(certmap_ctx, certmap_list[c]->priority,
-+                                   certmap_list[c]->match_rule,
-+                                   certmap_list[c]->map_rule,
-+                                   certmap_list[c]->domains);
-+        if (ret != 0) {
-+            DEBUG(SSSDBG_CRIT_FAILURE,
-+                  "sss_certmap_add_rule failed for rule [%s], skipping. "
-+                  "Please check for typos and if rule syntax is supported.\n",
-+                  certmap_list[c]->name);
-+            goto done;
-+        }
-+    }
-+
-+    ret = sysdb_update_certmap(domain->sysdb, certmap_list, user_name_hint);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_OP_FAILURE, "sysdb_update_certmap failed");
-+        goto done;
-+    }
-+
-+    sss_certmap_free_ctx(sdap_opts->certmap_ctx);
-+    sdap_opts->certmap_ctx = talloc_steal(sdap_opts, certmap_ctx);
-+
-+    if (_certmap_list != NULL) {
-+        *_certmap_list = certmap_list;
-+    }
-+    ret = EOK;
-+
-+done:
-+    talloc_free(ocs);
-+    if (ret != EOK) {
-+        sss_certmap_free_ctx(certmap_ctx);
-+        talloc_free(certmap_list);
-+    }
-+
-+    return ret;
-+}
-+
- static errno_t ipa_subdom_enumerates(struct sss_domain_info *parent,
-                                      struct sysdb_attrs *attrs,
-                                      bool *_enumerates)
-@@ -801,6 +1006,125 @@ static errno_t ipa_subdomains_ranges_recv(struct tevent_req *req)
-     return EOK;
- }
- 
-+#define IPA_CERTMAP_SEARCH_BASE_TEMPLATE "cn=certmap,%s"
-+
-+struct ipa_subdomains_certmap_state {
-+    struct sss_domain_info *domain;
-+    struct sdap_options *sdap_opts;
-+};
-+
-+static void ipa_subdomains_certmap_done(struct tevent_req *subreq);
-+
-+static struct tevent_req *
-+ipa_subdomains_certmap_send(TALLOC_CTX *mem_ctx,
-+                           struct tevent_context *ev,
-+                           struct ipa_subdomains_ctx *sd_ctx,
-+                           struct sdap_handle *sh)
-+{
-+    struct ipa_subdomains_certmap_state *state;
-+    struct tevent_req *subreq;
-+    struct tevent_req *req;
-+    errno_t ret;
-+    char *ldap_basedn;
-+    char *search_base;
-+    const char *attrs[] = { OBJECTCLASS, IPA_CN,
-+                            IPA_CERTMAP_MAPRULE, IPA_CERTMAP_MATCHRULE,
-+                            IPA_CERTMAP_PRIORITY, IPA_ASSOCIATED_DOMAIN,
-+                            IPA_CERTMAP_PROMPT_USERNAME,
-+                            NULL };
-+
-+    req = tevent_req_create(mem_ctx, &state,
-+                            struct ipa_subdomains_certmap_state);
-+    if (req == NULL) {
-+        DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create() failed\n");
-+        return NULL;
-+    }
-+
-+    state->domain = sd_ctx->be_ctx->domain;
-+    state->sdap_opts = sd_ctx->sdap_id_ctx->opts;
-+
-+    ret = domain_to_basedn(state, state->domain->name, &ldap_basedn);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_OP_FAILURE, "domain_to_basedn failed.\n");
-+        goto immediately;
-+    }
-+
-+    search_base = talloc_asprintf(state, IPA_CERTMAP_SEARCH_BASE_TEMPLATE,
-+                                  ldap_basedn);
-+    if (search_base == NULL) {
-+        DEBUG(SSSDBG_OP_FAILURE, "talloc_asprintf failed.\n");
-+        ret = ENOMEM;
-+        goto immediately;
-+    }
-+
-+    subreq = sdap_get_generic_send(state, ev, sd_ctx->sdap_id_ctx->opts, sh,
-+                                   search_base, LDAP_SCOPE_SUBTREE,
-+                                   CERTMAP_FILTER,
-+                                   attrs, NULL, 0, 0, false);
-+    if (subreq == NULL) {
-+        ret = ENOMEM;
-+        goto immediately;
-+    }
-+
-+    tevent_req_set_callback(subreq, ipa_subdomains_certmap_done, req);
-+
-+    return req;
-+
-+immediately:
-+    if (ret == EOK) {
-+        tevent_req_done(req);
-+    } else {
-+        tevent_req_error(req, ret);
-+    }
-+    tevent_req_post(req, ev);
-+
-+    return req;
-+}
-+
-+static void ipa_subdomains_certmap_done(struct tevent_req *subreq)
-+{
-+    struct ipa_subdomains_certmap_state *state;
-+    struct tevent_req *req;
-+    struct sysdb_attrs **reply;
-+    size_t reply_count;
-+    errno_t ret;
-+
-+    req = tevent_req_callback_data(subreq, struct tevent_req);
-+    state = tevent_req_data(req, struct ipa_subdomains_certmap_state);
-+
-+    ret = sdap_get_generic_recv(subreq, state, &reply_count, &reply);
-+    talloc_zfree(subreq);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_CRIT_FAILURE, "Unable to get data from LDAP [%d]: %s\n",
-+                      ret, sss_strerror(ret));
-+        goto done;
-+    }
-+
-+    ret = ipa_certmap_parse_results(state, state->domain,
-+                                    state->sdap_opts,
-+                                    reply_count, reply, NULL);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_OP_FAILURE, "Unable to parse certmap results [%d]: %s\n",
-+              ret, sss_strerror(ret));
-+        goto done;
-+    }
-+
-+done:
-+    if (ret != EOK) {
-+        tevent_req_error(req, ret);
-+        return;
-+    }
-+
-+    tevent_req_done(req);
-+}
-+
-+static errno_t ipa_subdomains_certmap_recv(struct tevent_req *req)
-+{
-+    TEVENT_REQ_RETURN_ON_ERROR(req);
-+
-+    return EOK;
-+}
-+
- struct ipa_subdomains_master_state {
-     struct sss_domain_info *domain;
-     struct ipa_options *ipa_options;
-@@ -1365,6 +1689,7 @@ struct ipa_subdomains_refresh_state {
- static errno_t ipa_subdomains_refresh_retry(struct tevent_req *req);
- static void ipa_subdomains_refresh_connect_done(struct tevent_req *subreq);
- static void ipa_subdomains_refresh_ranges_done(struct tevent_req *subreq);
-+static void ipa_subdomains_refresh_certmap_done(struct tevent_req *subreq);
- static void ipa_subdomains_refresh_master_done(struct tevent_req *subreq);
- static void ipa_subdomains_refresh_slave_done(struct tevent_req *subreq);
- static void ipa_subdomains_refresh_view_done(struct tevent_req *subreq);
-@@ -1487,6 +1812,35 @@ static void ipa_subdomains_refresh_ranges_done(struct tevent_req *subreq)
-         return;
-     }
- 
-+    subreq = ipa_subdomains_certmap_send(state, state->ev, state->sd_ctx,
-+                                         sdap_id_op_handle(state->sdap_op));
-+    if (subreq == NULL) {
-+        tevent_req_error(req, ENOMEM);
-+        return;
-+    }
-+
-+    tevent_req_set_callback(subreq, ipa_subdomains_refresh_certmap_done, req);
-+    return;
-+}
-+
-+static void ipa_subdomains_refresh_certmap_done(struct tevent_req *subreq)
-+{
-+    struct ipa_subdomains_refresh_state *state;
-+    struct tevent_req *req;
-+    errno_t ret;
-+
-+    req = tevent_req_callback_data(subreq, struct tevent_req);
-+    state = tevent_req_data(req, struct ipa_subdomains_refresh_state);
-+
-+    ret = ipa_subdomains_certmap_recv(subreq);
-+    talloc_zfree(subreq);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_CRIT_FAILURE, "Failed to read certificate mapping rules "
-+              "[%d]: %s\n", ret, sss_strerror(ret));
-+        tevent_req_error(req, ret);
-+        return;
-+    }
-+
-     subreq = ipa_subdomains_master_send(state, state->ev, state->sd_ctx,
-                                         sdap_id_op_handle(state->sdap_op));
-     if (subreq == NULL) {
-diff --git a/src/providers/ipa/ipa_subdomains_server.c b/src/providers/ipa/ipa_subdomains_server.c
-index 1af8676c5a9c49121d0f0118a46796c6637f04f9..ae3baf036e4278fb67d86b42742fb7e80b46724e 100644
---- a/src/providers/ipa/ipa_subdomains_server.c
-+++ b/src/providers/ipa/ipa_subdomains_server.c
-@@ -362,6 +362,10 @@ ipa_ad_ctx_new(struct be_ctx *be_ctx,
-     ad_id_ctx->sdap_id_ctx->opts->idmap_ctx =
-         id_ctx->sdap_id_ctx->opts->idmap_ctx;
- 
-+    /* Set up the certificate mapping context */
-+    ad_id_ctx->sdap_id_ctx->opts->certmap_ctx =
-+        id_ctx->sdap_id_ctx->opts->certmap_ctx;
-+
-     *_ad_id_ctx = ad_id_ctx;
-     return EOK;
- }
-diff --git a/src/providers/ldap/ldap_id.c b/src/providers/ldap/ldap_id.c
-index 8e60769d09383ac8ebe33e5f64fd4fd9788e82cd..0bee0ca8d71abece6749fdb8393b9ceacb64417d 100644
---- a/src/providers/ldap/ldap_id.c
-+++ b/src/providers/ldap/ldap_id.c
-@@ -247,7 +247,9 @@ struct tevent_req *users_get_send(TALLOC_CTX *memctx,
-         }
- 
-         ret = sss_cert_derb64_to_ldap_filter(state, filter_value, attr_name,
--                                             NULL, NULL, &user_filter);
-+                                             ctx->opts->certmap_ctx,
-+                                             state->domain,
-+                                             &user_filter);
-         if (ret != EOK) {
-             DEBUG(SSSDBG_OP_FAILURE,
-                   "sss_cert_derb64_to_ldap_filter failed.\n");
-diff --git a/src/providers/ldap/sdap.h b/src/providers/ldap/sdap.h
-index 6079a8bf62d0bdf23c8d462dc0f19c705e391a6e..afdc01948eefe9dda943c8c7ad01a42dd76a1da8 100644
---- a/src/providers/ldap/sdap.h
-+++ b/src/providers/ldap/sdap.h
-@@ -25,6 +25,7 @@
- #include "providers/backend.h"
- #include <ldap.h>
- #include "util/sss_ldap.h"
-+#include "lib/certmap/sss_certmap.h"
- 
- struct sdap_msg {
-     struct sdap_msg *next;
-@@ -478,6 +479,9 @@ struct sdap_options {
- 
-     bool support_matching_rule;
-     enum dc_functional_level dc_functional_level;
-+
-+    /* Certificate mapping support */
-+    struct sss_certmap_ctx *certmap_ctx;
- };
- 
- struct sdap_server_opts {
--- 
-2.9.3
-
diff --git a/SOURCES/0011-MAN-GPO-Security-Filtering-limitation.patch b/SOURCES/0011-MAN-GPO-Security-Filtering-limitation.patch
new file mode 100644
index 0000000..880f661
--- /dev/null
+++ b/SOURCES/0011-MAN-GPO-Security-Filtering-limitation.patch
@@ -0,0 +1,41 @@
+From 314c3a0efc276b74f7a2e39da4db29d8fbf43b12 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Michal=20=C5=BDidek?= <mzidek@redhat.com>
+Date: Thu, 26 Oct 2017 17:12:17 +0200
+Subject: [PATCH 11/21] MAN: GPO Security Filtering limitation
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Note in the man pages that current version of SSSD does not support
+host entries in the 'Security filtering' list.
+
+Resolves:
+https://pagure.io/SSSD/sssd/issue/3444
+
+Reviewed-by: Fabiano Fidêncio <fidencio@redhat.com>
+(cherry picked from commit 6c1661d2f4e860d1b547d6188a4fe2bd564e87cf)
+---
+ src/man/sssd-ad.5.xml | 7 +++++++
+ 1 file changed, 7 insertions(+)
+
+diff --git a/src/man/sssd-ad.5.xml b/src/man/sssd-ad.5.xml
+index 08c1dd09fb829c6cffb416250b9b518668ec5790..649042d587de3d3600fff59866681e302c721af8 100644
+--- a/src/man/sssd-ad.5.xml
++++ b/src/man/sssd-ad.5.xml
+@@ -346,6 +346,13 @@ DOM:dom1:(memberOf:1.2.840.113556.1.4.1941:=cn=nestedgroup,ou=groups,dc=example,
+                             host.
+                         </para>
+                         <para>
++                            NOTE: The current version of SSSD does not support
++                            host (computer) entries in the GPO 'Security
++                            Filtering' list. Only user and group entries are
++                            supported. Host entries in the list have no
++                            effect.
++                        </para>
++                        <para>
+                             NOTE: If the operation mode is set to enforcing, it
+                             is possible that users that were previously allowed
+                             logon access will now be denied logon access (as
+-- 
+2.13.5
+
diff --git a/SOURCES/0012-nss-idmap-add-sss_nss_getlistbycert.patch b/SOURCES/0012-nss-idmap-add-sss_nss_getlistbycert.patch
deleted file mode 100644
index f879ce1..0000000
--- a/SOURCES/0012-nss-idmap-add-sss_nss_getlistbycert.patch
+++ /dev/null
@@ -1,736 +0,0 @@
-From f2a81a22124e93a026ec0f06b77eab50998ecba5 Mon Sep 17 00:00:00 2001
-From: Sumit Bose <sbose@redhat.com>
-Date: Wed, 15 Mar 2017 14:21:26 +0100
-Subject: [PATCH 12/15] nss-idmap: add sss_nss_getlistbycert()
-
-This patch adds a getlistbycert() call to libsss_nss_idmap to make it on
-par with InfoPipe.
-
-Related to https://pagure.io/SSSD/sssd/issue/3050
-
-Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
----
- Makefile.am                                |   2 +-
- src/python/pysss_nss_idmap.c               | 103 ++++++++++++++++++-
- src/responder/nss/nss_cmd.c                |   7 ++
- src/responder/nss/nss_protocol.h           |   6 ++
- src/responder/nss/nss_protocol_sid.c       |  63 ++++++++++++
- src/sss_client/idmap/sss_nss_idmap.c       | 110 +++++++++++++++++++-
- src/sss_client/idmap/sss_nss_idmap.exports |   6 ++
- src/sss_client/idmap/sss_nss_idmap.h       |  17 +++-
- src/sss_client/sss_cli.h                   |   5 +
- src/tests/cmocka/test_nss_srv.c            | 158 +++++++++++++++++++++++++++++
- 10 files changed, 471 insertions(+), 6 deletions(-)
-
-diff --git a/Makefile.am b/Makefile.am
-index bd0ca0d303e1742ad26c7648cd24e2c0135af34e..7516338bc6fd95045d20db8155a0c82fd7003358 100644
---- a/Makefile.am
-+++ b/Makefile.am
-@@ -1128,7 +1128,7 @@ libsss_nss_idmap_la_LIBADD = \
-     $(CLIENT_LIBS)
- libsss_nss_idmap_la_LDFLAGS = \
-     -Wl,--version-script,$(srcdir)/src/sss_client/idmap/sss_nss_idmap.exports \
--    -version-info 2:0:2
-+    -version-info 3:0:3
- 
- dist_noinst_DATA += src/sss_client/idmap/sss_nss_idmap.exports
- 
-diff --git a/src/python/pysss_nss_idmap.c b/src/python/pysss_nss_idmap.c
-index c57cc10a86a7a9a22a791c1eae027a1aafa8f780..2e5851c7a6e48629fd93e428aada499fcbe36ebb 100644
---- a/src/python/pysss_nss_idmap.c
-+++ b/src/python/pysss_nss_idmap.c
-@@ -36,9 +36,37 @@ enum lookup_type {
-     SIDBYID,
-     NAMEBYSID,
-     IDBYSID,
--    NAMEBYCERT
-+    NAMEBYCERT,
-+    LISTBYCERT
- };
- 
-+static int add_dict_to_list(PyObject *py_list, PyObject *res_type,
-+                            PyObject *res, PyObject *id_type)
-+{
-+    int ret;
-+    PyObject *py_dict;
-+
-+    py_dict =  PyDict_New();
-+    if (py_dict == NULL) {
-+        return ENOMEM;
-+    }
-+
-+    ret = PyDict_SetItem(py_dict, res_type, res);
-+    if (ret != 0) {
-+        Py_XDECREF(py_dict);
-+        return ret;
-+    }
-+
-+    ret = PyDict_SetItem(py_dict, PyBytes_FromString(SSS_TYPE_KEY), id_type);
-+    if (ret != 0) {
-+        Py_XDECREF(py_dict);
-+        return ret;
-+    }
-+
-+    ret = PyList_Append(py_list, py_dict);
-+
-+    return ret;
-+}
- static int add_dict(PyObject *py_result, PyObject *key, PyObject *res_type,
-                     PyObject *res, PyObject *id_type)
- {
-@@ -191,6 +219,57 @@ static int do_getnamebycert(PyObject *py_result, PyObject *py_cert)
-     return ret;
- }
- 
-+static int do_getlistbycert(PyObject *py_result, PyObject *py_cert)
-+{
-+    int ret;
-+    const char *cert;
-+    char **names = NULL;
-+    enum sss_id_type *id_types = NULL;
-+    size_t c;
-+
-+    cert = py_string_or_unicode_as_string(py_cert);
-+    if (cert == NULL) {
-+        return EINVAL;
-+    }
-+
-+    ret = sss_nss_getlistbycert(cert, &names, &id_types);
-+    if (ret == 0) {
-+
-+        PyObject *py_list;
-+
-+        py_list =  PyList_New(0);
-+        if (py_list == NULL) {
-+            return ENOMEM;
-+        }
-+
-+        for (c = 0; names[c] != NULL; c++) {
-+            ret = add_dict_to_list(py_list,
-+                                   PyBytes_FromString(SSS_NAME_KEY),
-+                                   PyUnicode_FromString(names[c]),
-+                                   PYNUMBER_FROMLONG(id_types[c]));
-+            if (ret != 0) {
-+                goto done;
-+            }
-+        }
-+        ret = PyDict_SetItem(py_result, py_cert, py_list);
-+        if (ret != 0) {
-+            goto done;
-+        }
-+    }
-+
-+done:
-+    free(id_types);
-+    if (names != NULL) {
-+        for (c = 0; names[c] != NULL; c++) {
-+            free(names[c]);
-+        }
-+        free(names);
-+    }
-+
-+    return ret;
-+}
-+
-+
- static int do_getidbysid(PyObject *py_result, PyObject *py_sid)
- {
-     const char *sid;
-@@ -231,6 +310,9 @@ static int do_lookup(enum lookup_type type, PyObject *py_result,
-     case NAMEBYCERT:
-         return do_getnamebycert(py_result, py_inp);
-         break;
-+    case LISTBYCERT:
-+        return do_getlistbycert(py_result, py_inp);
-+        break;
-     default:
-         return ENOSYS;
-     }
-@@ -368,7 +450,7 @@ static PyObject * py_getidbysid(PyObject *module, PyObject *args)
- }
- 
- PyDoc_STRVAR(getnamebycert_doc,
--"getnamebycert(sid or list/tuple of certificates) -> dict(sid => dict(results))\n\
-+"getnamebycert(certificate or list/tuple of certificates) -> dict(certificate => dict(results))\n\
- \n\
- Returns a dictionary with a dictonary of results for each given certificates.\n\
- The result dictonary contain the name and the type of the object which can be\n\
-@@ -382,6 +464,21 @@ static PyObject * py_getnamebycert(PyObject *module, PyObject *args)
-     return check_args(NAMEBYCERT, args);
- }
- 
-+PyDoc_STRVAR(getlistbycert_doc,
-+"getnamebycert(certificate or list/tuple of certificates) -> dict(certificate => dict(results))\n\
-+\n\
-+Returns a dictionary with a dictonary of results for each given certificates.\n\
-+The result dictonary contain the name and the type of the object which can be\n\
-+accessed with the key constants NAME_KEY and TYPE_KEY, respectively.\n\
-+\n\
-+NOTE: getlistbycert currently works only with id_provider set as \"ad\" or \"ipa\""
-+);
-+
-+static PyObject * py_getlistbycert(PyObject *module, PyObject *args)
-+{
-+    return check_args(LISTBYCERT, args);
-+}
-+
- static PyMethodDef methods[] = {
-     { sss_py_const_p(char, "getsidbyname"), (PyCFunction) py_getsidbyname,
-       METH_VARARGS, getsidbyname_doc },
-@@ -393,6 +490,8 @@ static PyMethodDef methods[] = {
-       METH_VARARGS, getidbysid_doc },
-     { sss_py_const_p(char, "getnamebycert"), (PyCFunction) py_getnamebycert,
-       METH_VARARGS, getnamebycert_doc },
-+    { sss_py_const_p(char, "getlistbycert"), (PyCFunction) py_getlistbycert,
-+      METH_VARARGS, getlistbycert_doc },
-     { NULL,NULL, 0, NULL }
- };
- 
-diff --git a/src/responder/nss/nss_cmd.c b/src/responder/nss/nss_cmd.c
-index 08b3d32f2662efc1cc803f6e9e5f2d064f7d3033..1931bf62a686c7f30852dac547866609cf54a81b 100644
---- a/src/responder/nss/nss_cmd.c
-+++ b/src/responder/nss/nss_cmd.c
-@@ -932,6 +932,12 @@ static errno_t nss_cmd_getnamebycert(struct cli_ctx *cli_ctx)
-                           nss_protocol_fill_single_name);
- }
- 
-+static errno_t nss_cmd_getlistbycert(struct cli_ctx *cli_ctx)
-+{
-+    return nss_getby_cert(cli_ctx, CACHE_REQ_USER_BY_CERT,
-+                          nss_protocol_fill_name_list);
-+}
-+
- struct sss_cmd_table *get_nss_cmds(void)
- {
-     static struct sss_cmd_table nss_cmds[] = {
-@@ -961,6 +967,7 @@ struct sss_cmd_table *get_nss_cmds(void)
-         { SSS_NSS_GETIDBYSID, nss_cmd_getidbysid },
-         { SSS_NSS_GETORIGBYNAME, nss_cmd_getorigbyname },
-         { SSS_NSS_GETNAMEBYCERT, nss_cmd_getnamebycert },
-+        { SSS_NSS_GETLISTBYCERT, nss_cmd_getlistbycert },
-         { SSS_CLI_NULL, NULL }
-     };
- 
-diff --git a/src/responder/nss/nss_protocol.h b/src/responder/nss/nss_protocol.h
-index c94e7b911eb3c0f97b8c06b1766573311cde41ae..e4c0e52c0e642e885ef2c8423ea564beff7242cf 100644
---- a/src/responder/nss/nss_protocol.h
-+++ b/src/responder/nss/nss_protocol.h
-@@ -175,6 +175,12 @@ nss_protocol_fill_single_name(struct nss_ctx *nss_ctx,
-                               struct cache_req_result *result);
- 
- errno_t
-+nss_protocol_fill_name_list(struct nss_ctx *nss_ctx,
-+                            struct nss_cmd_ctx *cmd_ctx,
-+                            struct sss_packet *packet,
-+                            struct cache_req_result *result);
-+
-+errno_t
- nss_protocol_fill_id(struct nss_ctx *nss_ctx,
-                      struct nss_cmd_ctx *cmd_ctx,
-                      struct sss_packet *packet,
-diff --git a/src/responder/nss/nss_protocol_sid.c b/src/responder/nss/nss_protocol_sid.c
-index 0b97e65f75412d40832d861568d8e2f9de5e1732..a6a4e27d039c67ef98f6d5900d5e3fcadb3ee717 100644
---- a/src/responder/nss/nss_protocol_sid.c
-+++ b/src/responder/nss/nss_protocol_sid.c
-@@ -498,3 +498,66 @@ nss_protocol_fill_id(struct nss_ctx *nss_ctx,
- 
-     return EOK;
- }
-+
-+errno_t
-+nss_protocol_fill_name_list(struct nss_ctx *nss_ctx,
-+                            struct nss_cmd_ctx *cmd_ctx,
-+                            struct sss_packet *packet,
-+                            struct cache_req_result *result)
-+{
-+    enum sss_id_type *id_types;
-+    size_t rp = 0;
-+    size_t body_len;
-+    uint8_t *body;
-+    errno_t ret;
-+    struct sized_string *sz_names;
-+    size_t len;
-+    size_t c;
-+    const char *tmp_str;
-+
-+    sz_names = talloc_array(cmd_ctx, struct sized_string, result->count);
-+    if (sz_names == NULL) {
-+        return ENOMEM;
-+    }
-+
-+    id_types = talloc_array(cmd_ctx, enum sss_id_type, result->count);
-+    if (id_types == NULL) {
-+        return ENOMEM;
-+    }
-+
-+    len = 0;
-+    for (c = 0; c < result->count; c++) {
-+        ret = nss_get_id_type(cmd_ctx, result, &(id_types[c]));
-+        if (ret != EOK) {
-+            return ret;
-+        }
-+
-+        tmp_str = nss_get_name_from_msg(result->domain, result->msgs[c]);
-+        if (tmp_str == NULL) {
-+            return EINVAL;
-+        }
-+        to_sized_string(&(sz_names[c]), tmp_str);
-+
-+        len += sz_names[c].len;
-+    }
-+
-+    len += (2 + result->count) * sizeof(uint32_t);
-+
-+    ret = sss_packet_grow(packet, len);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_OP_FAILURE, "sss_packet_grow failed.\n");
-+        return ret;
-+    }
-+
-+    sss_packet_get_body(packet, &body, &body_len);
-+
-+    SAFEALIGN_SET_UINT32(&body[rp], result->count, &rp); /* Num results. */
-+    SAFEALIGN_SET_UINT32(&body[rp], 0, &rp); /* Reserved. */
-+    for (c = 0; c < result->count; c++) {
-+        SAFEALIGN_SET_UINT32(&body[rp], id_types[c], &rp);
-+        SAFEALIGN_SET_STRING(&body[rp], sz_names[c].str, sz_names[c].len,
-+                             &rp);
-+    }
-+
-+    return EOK;
-+}
-diff --git a/src/sss_client/idmap/sss_nss_idmap.c b/src/sss_client/idmap/sss_nss_idmap.c
-index fa5a499e3606f7e45a406de4d63002ba35365cb1..6f3af267a1e763e7dce77e3862be377ae2bfe984 100644
---- a/src/sss_client/idmap/sss_nss_idmap.c
-+++ b/src/sss_client/idmap/sss_nss_idmap.c
-@@ -31,6 +31,7 @@
- #include "util/strtonum.h"
- 
- #define DATA_START (3 * sizeof(uint32_t))
-+#define LIST_START (2 * sizeof(uint32_t))
- union input {
-     const char *str;
-     uint32_t id;
-@@ -38,10 +39,12 @@ union input {
- 
- struct output {
-     enum sss_id_type type;
-+    enum sss_id_type *types;
-     union {
-         char *str;
-         uint32_t id;
-         struct sss_nss_kv *kv_list;
-+        char **names;
-     } d;
- };
- 
-@@ -72,6 +75,63 @@ void sss_nss_free_kv(struct sss_nss_kv *kv_list)
-     }
- }
- 
-+void sss_nss_free_list(char **l)
-+{
-+    size_t c;
-+
-+    if (l != NULL) {
-+        for (c = 0; l[c] != NULL; c++) {
-+            free(l[c]);
-+        }
-+        free(l);
-+    }
-+}
-+
-+static int buf_to_name_type_list(uint8_t *buf, size_t buf_len, uint32_t num,
-+                                 char ***names, enum sss_id_type **types)
-+{
-+    int ret;
-+    size_t c;
-+    char **n = NULL;
-+    enum sss_id_type *t = NULL;
-+    size_t rp = 0;
-+
-+    n = calloc(num + 1, sizeof(char *));
-+    if (n == NULL) {
-+        ret = ENOMEM;
-+        goto done;
-+    }
-+
-+    t = calloc(num + 1, sizeof(enum sss_id_type));
-+    if (t == NULL) {
-+        ret = ENOMEM;
-+        goto done;
-+    }
-+
-+    for (c = 0; c < num; c++) {
-+        SAFEALIGN_COPY_UINT32(&(t[c]), buf + rp, &rp);
-+        n[c] = strdup((char *) buf + rp);
-+        if (n[c] == NULL) {
-+            ret = ENOMEM;
-+            goto done;
-+        }
-+        rp += strlen(n[c]) + 1;
-+    }
-+
-+    ret = EOK;
-+
-+done:
-+    if (ret != EOK) {
-+        sss_nss_free_list(n);
-+        free(t);
-+    } else {
-+        *names = n;
-+        *types = t;
-+    }
-+
-+    return ret;
-+}
-+
- static int  buf_to_kv_list(uint8_t *buf, size_t buf_len,
-                            struct sss_nss_kv **kv_list)
- {
-@@ -153,13 +213,14 @@ static int sss_nss_getyyybyxxx(union input inp, enum sss_cli_command cmd ,
-     size_t data_len;
-     uint32_t c;
-     struct sss_nss_kv *kv_list;
-+    char **names;
-+    enum sss_id_type *types;
- 
-     switch (cmd) {
-     case SSS_NSS_GETSIDBYNAME:
-     case SSS_NSS_GETNAMEBYSID:
-     case SSS_NSS_GETIDBYSID:
-     case SSS_NSS_GETORIGBYNAME:
--    case SSS_NSS_GETNAMEBYCERT:
-         ret = sss_strnlen(inp.str, 2048, &inp_len);
-         if (ret != EOK) {
-             return EINVAL;
-@@ -169,6 +230,17 @@ static int sss_nss_getyyybyxxx(union input inp, enum sss_cli_command cmd ,
-         rd.data = inp.str;
- 
-         break;
-+    case SSS_NSS_GETNAMEBYCERT:
-+    case SSS_NSS_GETLISTBYCERT:
-+        ret = sss_strnlen(inp.str, 10 * 1024 , &inp_len);
-+        if (ret != EOK) {
-+            return EINVAL;
-+        }
-+
-+        rd.len = inp_len + 1;
-+        rd.data = inp.str;
-+
-+        break;
-     case SSS_NSS_GETSIDBYID:
-         rd.len = sizeof(uint32_t);
-         rd.data = &inp.id;
-@@ -195,7 +267,7 @@ static int sss_nss_getyyybyxxx(union input inp, enum sss_cli_command cmd ,
-     if (num_results == 0) {
-         ret = ENOENT;
-         goto done;
--    } else if (num_results > 1) {
-+    } else if (num_results > 1 && cmd != SSS_NSS_GETLISTBYCERT) {
-         ret = EBADMSG;
-         goto done;
-     }
-@@ -237,6 +309,18 @@ static int sss_nss_getyyybyxxx(union input inp, enum sss_cli_command cmd ,
-         out->d.id = c;
- 
-         break;
-+    case SSS_NSS_GETLISTBYCERT:
-+        ret = buf_to_name_type_list(repbuf + LIST_START, replen - LIST_START,
-+                                    num_results,
-+                                    &names, &types);
-+        if (ret != EOK) {
-+            goto done;
-+        }
-+
-+        out->types = types;
-+        out->d.names = names;
-+
-+        break;
-     case SSS_NSS_GETORIGBYNAME:
-         ret = buf_to_kv_list(repbuf + DATA_START, data_len, &kv_list);
-         if (ret != EOK) {
-@@ -392,3 +476,25 @@ int sss_nss_getnamebycert(const char *cert, char **fq_name,
- 
-     return ret;
- }
-+
-+int sss_nss_getlistbycert(const char *cert, char ***fq_name,
-+                          enum sss_id_type **type)
-+{
-+    int ret;
-+    union input inp;
-+    struct output out;
-+
-+    if (fq_name == NULL || cert == NULL || *cert == '\0') {
-+        return EINVAL;
-+    }
-+
-+    inp.str = cert;
-+
-+    ret = sss_nss_getyyybyxxx(inp, SSS_NSS_GETLISTBYCERT, &out);
-+    if (ret == EOK) {
-+        *fq_name = out.d.names;
-+        *type = out.types;
-+    }
-+
-+    return ret;
-+}
-diff --git a/src/sss_client/idmap/sss_nss_idmap.exports b/src/sss_client/idmap/sss_nss_idmap.exports
-index bd5d80212017d38334c3cdeefa47d6029f42aebb..49dac6fc9351b0ca98cd46e83b85ec8ef0075a0d 100644
---- a/src/sss_client/idmap/sss_nss_idmap.exports
-+++ b/src/sss_client/idmap/sss_nss_idmap.exports
-@@ -25,3 +25,9 @@ SSS_NSS_IDMAP_0.2.0 {
-     global:
-         sss_nss_getnamebycert;
- } SSS_NSS_IDMAP_0.1.0;
-+
-+SSS_NSS_IDMAP_0.3.0 {
-+    # public functions
-+    global:
-+        sss_nss_getlistbycert;
-+} SSS_NSS_IDMAP_0.2.0;
-diff --git a/src/sss_client/idmap/sss_nss_idmap.h b/src/sss_client/idmap/sss_nss_idmap.h
-index 8a6299194e7b91e084b26c0c96e2f93875a832e7..cbf19479ff9ec6e0d6e07e1f7e48a1571e147740 100644
---- a/src/sss_client/idmap/sss_nss_idmap.h
-+++ b/src/sss_client/idmap/sss_nss_idmap.h
-@@ -130,7 +130,7 @@ int sss_nss_getorigbyname(const char *fq_name, struct sss_nss_kv **kv_list,
-  * @param[in] cert     base64 encoded certificate
-  * @param[out] fq_name Fully qualified name of a user or a group,
-  *                     must be freed by the caller
-- * @param[out] type    Type of the object related to the SID
-+ * @param[out] type    Type of the object related to the cert
-  *
-  * @return
-  *  - see #sss_nss_getsidbyname
-@@ -139,6 +139,21 @@ int sss_nss_getnamebycert(const char *cert, char **fq_name,
-                           enum sss_id_type *type);
- 
- /**
-+ * @brief Return a list of fully qualified names for the given base64 encoded
-+ * X.509 certificate in DER format
-+ *
-+ * @param[in] cert     base64 encoded certificate
-+ * @param[out] fq_name List of fully qualified name of users or groups,
-+ *                     must be freed by the caller
-+ * @param[out] type    List of types of the objects related to the cert
-+ *
-+ * @return
-+ *  - see #sss_nss_getsidbyname
-+ */
-+int sss_nss_getlistbycert(const char *cert, char ***fq_name,
-+                          enum sss_id_type **type);
-+
-+/**
-  * @brief Free key-value list returned by sss_nss_getorigbyname()
-  *
-  * @param[in] kv_list Key-value list returned by sss_nss_getorigbyname().
-diff --git a/src/sss_client/sss_cli.h b/src/sss_client/sss_cli.h
-index 8091e11515184dc9b7f32eed535055d9eee3143f..59fee7a4eceb2c185e156e812af7f2f4c6b2a0dd 100644
---- a/src/sss_client/sss_cli.h
-+++ b/src/sss_client/sss_cli.h
-@@ -260,6 +260,11 @@ SSS_NSS_GETNAMEBYCERT = 0x0116, /**< Takes the zero terminated string
-                                      of a X509 certificate and returns the zero
-                                      terminated fully qualified name of the
-                                      related object. */
-+SSS_NSS_GETLISTBYCERT = 0x0117, /**< Takes the zero terminated string
-+                                     of the base64 encoded DER representation
-+                                     of a X509 certificate and returns a list
-+                                     of zero terminated fully qualified names
-+                                     of the related objects. */
- };
- 
- /**
-diff --git a/src/tests/cmocka/test_nss_srv.c b/src/tests/cmocka/test_nss_srv.c
-index 76b9c6fb05673130de0957e93291919c263a28f3..50714715cc80338640f2a77ecbe17bd5e0d6e911 100644
---- a/src/tests/cmocka/test_nss_srv.c
-+++ b/src/tests/cmocka/test_nss_srv.c
-@@ -3454,6 +3454,16 @@ struct passwd testbycert = {
-     .pw_passwd = discard_const("*"),
- };
- 
-+struct passwd testbycert2 = {
-+    .pw_name = discard_const("testcertuser2"),
-+    .pw_uid = 23457,
-+    .pw_gid = 6890,
-+    .pw_dir = discard_const("/home/testcertuser2"),
-+    .pw_gecos = discard_const("test cert user2"),
-+    .pw_shell = discard_const("/bin/sh"),
-+    .pw_passwd = discard_const("*"),
-+};
-+
- #define TEST_TOKEN_CERT \
- "MIIECTCCAvGgAwIBAgIBCDANBgkqhkiG9w0BAQsFADA0MRIwEAYDVQQKDAlJUEEu" \
- "REVWRUwxHjAcBgNVBAMMFUNlcnRpZmljYXRlIEF1dGhvcml0eTAeFw0xNTA2MjMx" \
-@@ -3495,6 +3505,57 @@ static int test_nss_getnamebycert_check(uint32_t status, uint8_t *body, size_t b
-     return EOK;
- }
- 
-+static int test_nss_getlistbycert_check(uint32_t status, uint8_t *body, size_t blen)
-+{
-+    size_t rp = 0;
-+    uint32_t id_type;
-+    uint32_t num;
-+    uint32_t reserved;
-+    const char *name;
-+    int found = 0;
-+    const char *fq_name1 = "testcertuser@"TEST_DOM_NAME ;
-+    const char *fq_name2 = "testcertuser2@"TEST_DOM_NAME;
-+
-+    assert_int_equal(status, EOK);
-+
-+    /* num_results and reserved */
-+    SAFEALIGN_COPY_UINT32(&num, body + rp, &rp);
-+    assert_in_range(num, 1, 2);
-+    SAFEALIGN_COPY_UINT32(&reserved, body + rp, &rp);
-+    assert_int_equal(reserved, 0);
-+
-+    SAFEALIGN_COPY_UINT32(&id_type, body + rp, &rp);
-+    assert_int_equal(id_type, SSS_ID_TYPE_UID);
-+
-+    name = (const char *)body + rp;
-+    if (num == 1) {
-+        assert_string_equal(name, fq_name1);
-+        return EOK;
-+    }
-+
-+    rp += strlen(name) + 1;
-+    if (strcmp(name, fq_name1) == 0) {
-+        found = 1;
-+    } else if (strcmp(name, fq_name2) == 0) {
-+        found = 2;
-+    }
-+    assert_in_range(found, 1, 2);
-+
-+    SAFEALIGN_COPY_UINT32(&id_type, body + rp, &rp);
-+    assert_int_equal(id_type, SSS_ID_TYPE_UID);
-+
-+    name = (const char *)body + rp;
-+    if (found == 1) {
-+        assert_string_equal(name, fq_name2);
-+    } else {
-+        assert_string_equal(name, fq_name1);
-+    }
-+
-+
-+    return EOK;
-+}
-+
-+
- static void test_nss_getnamebycert(void **state)
- {
-     errno_t ret;
-@@ -3572,6 +3633,99 @@ void test_nss_getnamebycert_neg(void **state)
-     assert_int_equal(nss_test_ctx->ncache_hits, 1);
- }
- 
-+static void test_nss_getlistbycert(void **state)
-+{
-+    errno_t ret;
-+    struct sysdb_attrs *attrs;
-+    unsigned char *der = NULL;
-+    size_t der_size;
-+
-+    attrs = sysdb_new_attrs(nss_test_ctx);
-+    assert_non_null(attrs);
-+
-+    der = sss_base64_decode(nss_test_ctx, TEST_TOKEN_CERT, &der_size);
-+    assert_non_null(der);
-+
-+    ret = sysdb_attrs_add_mem(attrs, SYSDB_USER_CERT, der, der_size);
-+    talloc_free(der);
-+    assert_int_equal(ret, EOK);
-+
-+    /* Prime the cache with a valid user */
-+    ret = store_user(nss_test_ctx, nss_test_ctx->tctx->dom,
-+                     &testbycert, attrs, 0);
-+    assert_int_equal(ret, EOK);
-+    talloc_free(attrs);
-+
-+    mock_input_cert(TEST_TOKEN_CERT);
-+    will_return(__wrap_sss_packet_get_cmd, SSS_NSS_GETLISTBYCERT);
-+    mock_fill_bysid();
-+
-+    /* Query for that user, call a callback when command finishes */
-+    /* Should go straight to back end, without contacting DP. */
-+    /* If there is only a single user mapped the result will look like the */
-+    /* result of getnamebycert. */
-+    set_cmd_cb(test_nss_getlistbycert_check);
-+    ret = sss_cmd_execute(nss_test_ctx->cctx, SSS_NSS_GETLISTBYCERT,
-+                          nss_test_ctx->nss_cmds);
-+    assert_int_equal(ret, EOK);
-+
-+    /* Wait until the test finishes with EOK */
-+    ret = test_ev_loop(nss_test_ctx->tctx);
-+    assert_int_equal(ret, EOK);
-+}
-+
-+static void test_nss_getlistbycert_multi(void **state)
-+{
-+    errno_t ret;
-+    struct sysdb_attrs *attrs;
-+    unsigned char *der = NULL;
-+    size_t der_size;
-+
-+    der = sss_base64_decode(nss_test_ctx, TEST_TOKEN_CERT, &der_size);
-+    assert_non_null(der);
-+
-+    attrs = sysdb_new_attrs(nss_test_ctx);
-+    assert_non_null(attrs);
-+
-+    ret = sysdb_attrs_add_mem(attrs, SYSDB_USER_CERT, der, der_size);
-+    assert_int_equal(ret, EOK);
-+
-+    /* Prime the cache with two valid user */
-+    ret = store_user(nss_test_ctx, nss_test_ctx->tctx->dom,
-+                     &testbycert, attrs, 0);
-+    assert_int_equal(ret, EOK);
-+    talloc_free(attrs);
-+
-+    /* Looks like attrs is modified during store_user() makes sure we start
-+     * with fresh data. */
-+    attrs = sysdb_new_attrs(nss_test_ctx);
-+    assert_non_null(attrs);
-+
-+    ret = sysdb_attrs_add_mem(attrs, SYSDB_USER_CERT, der, der_size);
-+    talloc_free(der);
-+    assert_int_equal(ret, EOK);
-+
-+    ret = store_user(nss_test_ctx, nss_test_ctx->tctx->dom,
-+                     &testbycert2, attrs, 0);
-+    assert_int_equal(ret, EOK);
-+    talloc_free(attrs);
-+
-+    mock_input_cert(TEST_TOKEN_CERT);
-+    will_return(__wrap_sss_packet_get_cmd, SSS_NSS_GETLISTBYCERT);
-+    mock_fill_bysid();
-+
-+    /* Query for that user, call a callback when command finishes */
-+    /* Should go straight to back end, without contacting DP */
-+    set_cmd_cb(test_nss_getlistbycert_check);
-+    ret = sss_cmd_execute(nss_test_ctx->cctx, SSS_NSS_GETLISTBYCERT,
-+                          nss_test_ctx->nss_cmds);
-+    assert_int_equal(ret, EOK);
-+
-+    /* Wait until the test finishes with EOK */
-+    ret = test_ev_loop(nss_test_ctx->tctx);
-+    assert_int_equal(ret, EOK);
-+}
-+
- struct passwd sid_user = {
-     .pw_name = discard_const("testusersid"),
-     .pw_uid = 1234,
-@@ -3818,6 +3972,10 @@ int main(int argc, const char *argv[])
-                                         nss_test_setup, nss_test_teardown),
-         cmocka_unit_test_setup_teardown(test_nss_getnamebycert,
-                                         nss_test_setup, nss_test_teardown),
-+        cmocka_unit_test_setup_teardown(test_nss_getlistbycert,
-+                                        nss_test_setup, nss_test_teardown),
-+        cmocka_unit_test_setup_teardown(test_nss_getlistbycert_multi,
-+                                        nss_test_setup, nss_test_teardown),
-         cmocka_unit_test_setup_teardown(test_nss_getsidbyname,
-                                         nss_test_setup, nss_test_teardown),
-         cmocka_unit_test_setup_teardown(test_nss_getsidbyupn,
--- 
-2.9.3
-
diff --git a/SOURCES/0012-sudo-always-use-srv_opts-from-id-context.patch b/SOURCES/0012-sudo-always-use-srv_opts-from-id-context.patch
new file mode 100644
index 0000000..8c782f9
--- /dev/null
+++ b/SOURCES/0012-sudo-always-use-srv_opts-from-id-context.patch
@@ -0,0 +1,64 @@
+From 7738a74e6878536e155d9d589e7ec727c135f5a0 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pavel=20B=C5=99ezina?= <pbrezina@redhat.com>
+Date: Thu, 19 Oct 2017 10:39:21 +0200
+Subject: [PATCH 12/21] sudo: always use srv_opts from id context
+
+Prior this patch, we remember id_ctx->srv_opts in sudo request to switch
+the latest usn values. This works fine most of the time but it may cause
+a crash.
+
+If we have two concurrent sudo refresh and one of these fails, it causes
+failover to try the next server and possibly replacing the old srv_opts
+with new one and it causes an access after free in the other refresh.
+
+Resolves:
+https://pagure.io/SSSD/sssd/issue/3562
+
+Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
+(cherry picked from commit 2ee201dcf6bbe52abbbed3c2fc4c35ca2e0c8a43)
+---
+ src/providers/ldap/sdap_async_sudo.c | 7 +------
+ 1 file changed, 1 insertion(+), 6 deletions(-)
+
+diff --git a/src/providers/ldap/sdap_async_sudo.c b/src/providers/ldap/sdap_async_sudo.c
+index 3c69837fda313b2645c3a8497252670312f600ea..88a387422d5c9ae86cea583bb38dadf90cba37f3 100644
+--- a/src/providers/ldap/sdap_async_sudo.c
++++ b/src/providers/ldap/sdap_async_sudo.c
+@@ -279,7 +279,6 @@ done:
+ struct sdap_sudo_refresh_state {
+     struct sdap_sudo_ctx *sudo_ctx;
+     struct tevent_context *ev;
+-    struct sdap_server_opts *srv_opts;
+     struct sdap_options *opts;
+     struct sdap_id_op *sdap_op;
+     struct sysdb_ctx *sysdb;
+@@ -405,9 +404,6 @@ static void sdap_sudo_refresh_connect_done(struct tevent_req *subreq)
+ 
+     DEBUG(SSSDBG_TRACE_FUNC, "SUDO LDAP connection successful\n");
+ 
+-    /* Obtain srv_opts here in case of first connection. */
+-    state->srv_opts = state->sudo_ctx->id_ctx->srv_opts;
+-
+     /* Renew host information if needed. */
+     if (state->sudo_ctx->run_hostinfo) {
+         subreq = sdap_sudo_get_hostinfo_send(state, state->opts,
+@@ -586,7 +582,6 @@ static void sdap_sudo_refresh_done(struct tevent_req *subreq)
+         goto done;
+     }
+ 
+-
+     /* start transaction */
+     ret = sysdb_transaction_start(state->sysdb);
+     if (ret != EOK) {
+@@ -621,7 +616,7 @@ static void sdap_sudo_refresh_done(struct tevent_req *subreq)
+     /* remember new usn */
+     ret = sysdb_get_highest_usn(state, rules, rules_count, &usn);
+     if (ret == EOK) {
+-        sdap_sudo_set_usn(state->srv_opts, usn);
++        sdap_sudo_set_usn(state->sudo_ctx->id_ctx->srv_opts, usn);
+     } else {
+         DEBUG(SSSDBG_MINOR_FAILURE, "Unable to get highest USN [%d]: %s\n",
+               ret, sss_strerror(ret));
+-- 
+2.13.5
+
diff --git a/SOURCES/0013-AD-Remember-last-site-discovered.patch b/SOURCES/0013-AD-Remember-last-site-discovered.patch
new file mode 100644
index 0000000..f1ad8c2
--- /dev/null
+++ b/SOURCES/0013-AD-Remember-last-site-discovered.patch
@@ -0,0 +1,109 @@
+From 020d7f12f7c57e3a5c8f844de2b2d0cad020e662 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pavel=20B=C5=99ezina?= <pbrezina@redhat.com>
+Date: Wed, 18 Oct 2017 15:20:34 +0200
+Subject: [PATCH 13/21] AD: Remember last site discovered
+
+To discover Active Directory site for a client we must first contact any
+directory controller for an LDAP ping. This is done by searching
+domain-wide DNS tree which may however contain servers that are not
+reachable from current site and than we face long timeouts or failure.
+
+This patch makes sssd remember the last successfuly discovered site
+and use this for DNS search to lookup a site and forest again similar
+to what we do when ad_site option is set.
+
+Resolves:
+https://pagure.io/SSSD/sssd/issue/3265
+
+Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
+(cherry picked from commit f54d202db528207d7794870aabef0656b20369f1)
+---
+ src/providers/ad/ad_srv.c | 44 +++++++++++++++++++++++++++++++++++++++++++-
+ 1 file changed, 43 insertions(+), 1 deletion(-)
+
+diff --git a/src/providers/ad/ad_srv.c b/src/providers/ad/ad_srv.c
+index ff01ee95c4d2c6875a989394489f1a0495cc3003..be1ba0f237add894566ae713ce5e29fd202d414c 100644
+--- a/src/providers/ad/ad_srv.c
++++ b/src/providers/ad/ad_srv.c
+@@ -481,6 +481,7 @@ struct ad_srv_plugin_ctx {
+     const char *hostname;
+     const char *ad_domain;
+     const char *ad_site_override;
++    const char *current_site;
+ };
+ 
+ struct ad_srv_plugin_ctx *
+@@ -518,6 +519,11 @@ ad_srv_plugin_ctx_init(TALLOC_CTX *mem_ctx,
+         if (ctx->ad_site_override == NULL) {
+             goto fail;
+         }
++
++        ctx->current_site = talloc_strdup(ctx, ad_site_override);
++        if (ctx->current_site == NULL) {
++            goto fail;
++        }
+     }
+ 
+     return ctx;
+@@ -527,6 +533,32 @@ fail:
+     return NULL;
+ }
+ 
++static errno_t
++ad_srv_plugin_ctx_switch_site(struct ad_srv_plugin_ctx *ctx,
++                              const char *new_site)
++{
++    const char *site;
++    errno_t ret;
++
++    if (new_site == NULL) {
++        return EOK;
++    }
++
++    if (ctx->current_site != NULL && strcmp(ctx->current_site, new_site) == 0) {
++        return EOK;
++    }
++
++    site = talloc_strdup(ctx, new_site);
++    if (site == NULL) {
++        return ENOMEM;
++    }
++
++    talloc_zfree(ctx->current_site);
++    ctx->current_site = site;
++
++    return EOK;
++}
++
+ struct ad_srv_plugin_state {
+     struct tevent_context *ev;
+     struct ad_srv_plugin_ctx *ctx;
+@@ -613,7 +645,7 @@ struct tevent_req *ad_srv_plugin_send(TALLOC_CTX *mem_ctx,
+ 
+     subreq = ad_get_dc_servers_send(state, ev, ctx->be_res->resolv,
+                                     state->discovery_domain,
+-                                    state->ctx->ad_site_override);
++                                    state->ctx->current_site);
+     if (subreq == NULL) {
+         ret = ENOMEM;
+         goto immediately;
+@@ -709,6 +741,16 @@ static void ad_srv_plugin_site_done(struct tevent_req *subreq)
+     backup_domain = NULL;
+ 
+     if (ret == EOK) {
++        /* Remember current site so it can be used during next lookup so
++         * we can contact directory controllers within a known reachable
++         * site first. */
++        ret = ad_srv_plugin_ctx_switch_site(state->ctx, state->site);
++        if (ret != EOK) {
++            DEBUG(SSSDBG_CRIT_FAILURE, "Unable to set site [%d]: %s\n",
++                  ret, sss_strerror(ret));
++            goto done;
++        }
++
+         if (strcmp(state->service, "gc") == 0) {
+             if (state->forest != NULL) {
+                 if (state->site != NULL) {
+-- 
+2.13.5
+
diff --git a/SOURCES/0013-nss-allow-larger-buffer-for-certificate-based-reques.patch b/SOURCES/0013-nss-allow-larger-buffer-for-certificate-based-reques.patch
deleted file mode 100644
index d0dea12..0000000
--- a/SOURCES/0013-nss-allow-larger-buffer-for-certificate-based-reques.patch
+++ /dev/null
@@ -1,70 +0,0 @@
-From e7c9ff18f41d9951aff3c99dca7db1871e53cfaf Mon Sep 17 00:00:00 2001
-From: Sumit Bose <sbose@redhat.com>
-Date: Tue, 28 Feb 2017 14:19:53 +0100
-Subject: [PATCH 13/15] nss: allow larger buffer for certificate based requests
-
-To make sure larger certificates can be processed as well the maximal
-buffer size is increased for requests by certificate.
-
-Related to https://pagure.io/SSSD/sssd/issue/3050
-
-Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
----
- src/responder/common/responder_packet.c | 21 ++++++++++++++++++++-
- src/responder/common/responder_packet.h |  1 +
- 2 files changed, 21 insertions(+), 1 deletion(-)
-
-diff --git a/src/responder/common/responder_packet.c b/src/responder/common/responder_packet.c
-index 4f5e110837eb76609d31a77c62a00e00530ffc90..cc4d66995965cca4c86a80c31d2afd4c9ac3e0e4 100644
---- a/src/responder/common/responder_packet.c
-+++ b/src/responder/common/responder_packet.c
-@@ -179,6 +179,8 @@ int sss_packet_recv(struct sss_packet *packet, int fd)
-     size_t rb;
-     size_t len;
-     void *buf;
-+    size_t new_len;
-+    int ret;
- 
-     buf = (uint8_t *)packet->buffer + packet->iop;
-     if (packet->iop > 4) len = sss_packet_get_len(packet) - packet->iop;
-@@ -205,7 +207,24 @@ int sss_packet_recv(struct sss_packet *packet, int fd)
-     }
- 
-     if (sss_packet_get_len(packet) > packet->memsize) {
--        return EINVAL;
-+        /* Allow certificate based requests to use larger buffer but not
-+         * larger than SSS_CERT_PACKET_MAX_RECV_SIZE. Due to the way
-+         * sss_packet_grow() works the packet len must be set to '0' first and
-+         * then grow to the expected size. */
-+        if ((sss_packet_get_cmd(packet) == SSS_NSS_GETNAMEBYCERT
-+                    || sss_packet_get_cmd(packet) == SSS_NSS_GETLISTBYCERT)
-+                && packet->memsize < SSS_CERT_PACKET_MAX_RECV_SIZE
-+                && (new_len = sss_packet_get_len(packet))
-+                                   < SSS_CERT_PACKET_MAX_RECV_SIZE) {
-+            new_len = sss_packet_get_len(packet);
-+            sss_packet_set_len(packet, 0);
-+            ret = sss_packet_grow(packet, new_len);
-+            if (ret != EOK) {
-+                return ret;
-+            }
-+        } else {
-+            return EINVAL;
-+        }
-     }
- 
-     packet->iop += rb;
-diff --git a/src/responder/common/responder_packet.h b/src/responder/common/responder_packet.h
-index 3ad0eee28477e446c9e4996617beb55f32923d47..afceb4aaefa40fd86bdfde820c92c09b65cd8702 100644
---- a/src/responder/common/responder_packet.h
-+++ b/src/responder/common/responder_packet.h
-@@ -25,6 +25,7 @@
- #include "sss_client/sss_cli.h"
- 
- #define SSS_PACKET_MAX_RECV_SIZE 1024
-+#define SSS_CERT_PACKET_MAX_RECV_SIZE ( 10 * SSS_PACKET_MAX_RECV_SIZE )
- 
- struct sss_packet;
- 
--- 
-2.9.3
-
diff --git a/SOURCES/0014-IPA-Add-s2n-request-to-string-function.patch b/SOURCES/0014-IPA-Add-s2n-request-to-string-function.patch
deleted file mode 100644
index 287bfdf..0000000
--- a/SOURCES/0014-IPA-Add-s2n-request-to-string-function.patch
+++ /dev/null
@@ -1,44 +0,0 @@
-From 8820b7bba01312419171c4949a9f1c5c8c061a55 Mon Sep 17 00:00:00 2001
-From: Justin Stephenson <jstephen@redhat.com>
-Date: Mon, 20 Mar 2017 11:51:05 -0400
-Subject: [PATCH 14/15] IPA: Add s2n request to string function
-
-Add a function to convert request_types to string allowing the
-ability to print request type information for ipa_s2n functions during
-IPA client operations.
-
-Reviewed-by: Sumit Bose <sbose@redhat.com>
----
- src/providers/ipa/ipa_s2n_exop.c | 16 ++++++++++++++++
- 1 file changed, 16 insertions(+)
-
-diff --git a/src/providers/ipa/ipa_s2n_exop.c b/src/providers/ipa/ipa_s2n_exop.c
-index 07bbb2b4d252c8ca9ada4d890c36c903c9f75773..4fe20689fe4c0f2bb5217691dd05b37d2a1cc820 100644
---- a/src/providers/ipa/ipa_s2n_exop.c
-+++ b/src/providers/ipa/ipa_s2n_exop.c
-@@ -979,6 +979,22 @@ done:
-     return ret;
- }
- 
-+static const char *ipa_s2n_reqtype2str(enum request_types request_type)
-+{
-+    switch (request_type) {
-+    case REQ_SIMPLE:
-+        return "REQ_SIMPLE";
-+    case REQ_FULL:
-+        return "REQ_FULL";
-+    case REQ_FULL_WITH_MEMBERS:
-+        return "REQ_FULL_WITH_MEMBERS";
-+    default:
-+        break;
-+    }
-+
-+    return "Unknown request type";
-+}
-+
- struct ipa_s2n_get_list_state {
-     struct tevent_context *ev;
-     struct ipa_id_ctx *ipa_ctx;
--- 
-2.9.3
-
diff --git a/SOURCES/0014-sysdb-add-functions-to-get-set-client-site.patch b/SOURCES/0014-sysdb-add-functions-to-get-set-client-site.patch
new file mode 100644
index 0000000..7fc6ff1
--- /dev/null
+++ b/SOURCES/0014-sysdb-add-functions-to-get-set-client-site.patch
@@ -0,0 +1,206 @@
+From fafc90b8c225fd77e30e94d985c72f5f2980e59e Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pavel=20B=C5=99ezina?= <pbrezina@redhat.com>
+Date: Tue, 24 Oct 2017 12:09:39 +0200
+Subject: [PATCH 14/21] sysdb: add functions to get/set client site
+
+Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
+(cherry picked from commit e16539779668dacff868999bd59dbf33e3eab872)
+---
+ src/db/sysdb.h                           |  10 +++
+ src/db/sysdb_subdomains.c                | 108 +++++++++++++++++++++++++++++++
+ src/tests/cmocka/test_sysdb_subdomains.c |  28 ++++++++
+ 3 files changed, 146 insertions(+)
+
+diff --git a/src/db/sysdb.h b/src/db/sysdb.h
+index fbbe321072385bd43353ef2f7d0e30667887d128..4192f9085d941814eccd2ac60ce8fb6d4e1bfa67 100644
+--- a/src/db/sysdb.h
++++ b/src/db/sysdb.h
+@@ -154,6 +154,7 @@
+ #define SYSDB_SUBDOMAIN_FOREST "memberOfForest"
+ #define SYSDB_SUBDOMAIN_TRUST_DIRECTION "trustDirection"
+ #define SYSDB_UPN_SUFFIXES "upnSuffixes"
++#define SYSDB_SITE "site"
+ 
+ #define SYSDB_BASE_ID "baseID"
+ #define SYSDB_ID_RANGE_SIZE "idRangeSize"
+@@ -509,6 +510,15 @@ errno_t sysdb_domain_update_domain_resolution_order(
+                                         const char *domain_name,
+                                         const char *domain_resolution_order);
+ 
++errno_t
++sysdb_get_site(TALLOC_CTX *mem_ctx,
++               struct sss_domain_info *dom,
++               const char **_site);
++
++errno_t
++sysdb_set_site(struct sss_domain_info *dom,
++               const char *site);
++
+ errno_t sysdb_subdomain_store(struct sysdb_ctx *sysdb,
+                               const char *name, const char *realm,
+                               const char *flat_name, const char *domain_id,
+diff --git a/src/db/sysdb_subdomains.c b/src/db/sysdb_subdomains.c
+index 2789cc4949fb7be9ad272d7613ed18a64fa8a20a..cb5de1afe3e8c9692789c5d2679eb3a4e6e1cdb2 100644
+--- a/src/db/sysdb_subdomains.c
++++ b/src/db/sysdb_subdomains.c
+@@ -1284,3 +1284,111 @@ done:
+     talloc_free(tmp_ctx);
+     return ret;
+ }
++
++errno_t
++sysdb_get_site(TALLOC_CTX *mem_ctx,
++               struct sss_domain_info *dom,
++               const char **_site)
++{
++    TALLOC_CTX *tmp_ctx;
++    struct ldb_res *res;
++    struct ldb_dn *dn;
++    const char *attrs[] = { SYSDB_SITE, NULL };
++    errno_t ret;
++
++    tmp_ctx = talloc_new(NULL);
++    if (tmp_ctx == NULL) {
++        return ENOMEM;
++    }
++
++    dn = ldb_dn_new_fmt(tmp_ctx, dom->sysdb->ldb, SYSDB_DOM_BASE, dom->name);
++    if (dn == NULL) {
++        ret = ENOMEM;
++        goto done;
++    }
++
++    ret = ldb_search(dom->sysdb->ldb, tmp_ctx, &res, dn, LDB_SCOPE_BASE,
++                     attrs, NULL);
++    if (ret != LDB_SUCCESS) {
++        ret = sysdb_error_to_errno(ret);
++        goto done;
++    }
++
++    if (res->count == 0) {
++        *_site = NULL;
++        ret = EOK;
++        goto done;
++    } else if (res->count != 1) {
++        DEBUG(SSSDBG_CRIT_FAILURE,
++              "Got more than one reply for base search!\n");
++        ret = EIO;
++        goto done;
++    }
++
++    *_site = ldb_msg_find_attr_as_string(res->msgs[0], SYSDB_SITE, NULL);
++    talloc_steal(mem_ctx, *_site);
++
++    ret = EOK;
++
++done:
++    talloc_free(tmp_ctx);
++    return ret;
++}
++
++errno_t
++sysdb_set_site(struct sss_domain_info *dom,
++               const char *site)
++{
++    TALLOC_CTX *tmp_ctx;
++    struct ldb_message *msg;
++    struct ldb_dn *dn;
++    errno_t ret;
++
++    tmp_ctx = talloc_new(NULL);
++    if (tmp_ctx == NULL) {
++        return ENOMEM;
++    }
++
++    dn = ldb_dn_new_fmt(tmp_ctx, dom->sysdb->ldb, SYSDB_DOM_BASE, dom->name);
++    if (dn == NULL) {
++        ret = ENOMEM;
++        goto done;
++    }
++
++    msg = ldb_msg_new(tmp_ctx);
++    if (msg == NULL) {
++        ret = ENOMEM;
++        goto done;
++    }
++
++    msg->dn = dn;
++
++    ret = ldb_msg_add_empty(msg, SYSDB_SITE, LDB_FLAG_MOD_REPLACE, NULL);
++    if (ret != LDB_SUCCESS) {
++        ret = sysdb_error_to_errno(ret);
++        goto done;
++    }
++
++    if (site != NULL) {
++        ret = ldb_msg_add_string(msg, SYSDB_SITE, site);
++        if (ret != LDB_SUCCESS) {
++            ret = sysdb_error_to_errno(ret);
++            goto done;
++        }
++    }
++
++    ret = ldb_modify(dom->sysdb->ldb, msg);
++    if (ret != LDB_SUCCESS) {
++        DEBUG(SSSDBG_OP_FAILURE,
++              "ldb_modify()_failed: [%s][%d][%s]\n",
++              ldb_strerror(ret), ret, ldb_errstring(dom->sysdb->ldb));
++        ret = sysdb_error_to_errno(ret);
++        goto done;
++    }
++
++    ret = EOK;
++
++done:
++    talloc_free(tmp_ctx);
++    return ret;
++}
+diff --git a/src/tests/cmocka/test_sysdb_subdomains.c b/src/tests/cmocka/test_sysdb_subdomains.c
+index 84bcdc17b39dbc8822097c2006f157a09ea5e466..f8e3e1d915dba0f3a79adbf5af733980bf23a265 100644
+--- a/src/tests/cmocka/test_sysdb_subdomains.c
++++ b/src/tests/cmocka/test_sysdb_subdomains.c
+@@ -513,6 +513,31 @@ static void test_sysdb_link_ad_multidom(void **state)
+ 
+ }
+ 
++static void test_sysdb_set_and_get_site(void **state)
++{
++    TALLOC_CTX *tmp_ctx;
++    struct subdom_test_ctx *test_ctx =
++        talloc_get_type(*state, struct subdom_test_ctx);
++    const char *site;
++    errno_t ret;
++
++    tmp_ctx = talloc_new(NULL);
++    assert_non_null(test_ctx);
++
++    ret = sysdb_get_site(test_ctx, test_ctx->tctx->dom, &site);
++    assert_int_equal(ret, EOK);
++    assert_null(site);
++
++    ret = sysdb_set_site(test_ctx->tctx->dom, "TestSite");
++    assert_int_equal(ret, EOK);
++
++    ret = sysdb_get_site(tmp_ctx, test_ctx->tctx->dom, &site);
++    assert_int_equal(ret, EOK);
++    assert_string_equal(site, "TestSite");
++
++    talloc_free(tmp_ctx);
++}
++
+ int main(int argc, const char *argv[])
+ {
+     int rv;
+@@ -546,6 +571,9 @@ int main(int argc, const char *argv[])
+         cmocka_unit_test_setup_teardown(test_sysdb_link_ad_multidom,
+                                         test_sysdb_subdom_setup,
+                                         test_sysdb_subdom_teardown),
++        cmocka_unit_test_setup_teardown(test_sysdb_set_and_get_site,
++                                        test_sysdb_subdom_setup,
++                                        test_sysdb_subdom_teardown),
+     };
+ 
+     /* Set debug level to invalid value so we can deside if -d 0 was used. */
+-- 
+2.13.5
+
diff --git a/SOURCES/0015-AD-Remember-last-site-discovered-in-sysdb.patch b/SOURCES/0015-AD-Remember-last-site-discovered-in-sysdb.patch
new file mode 100644
index 0000000..9c14628
--- /dev/null
+++ b/SOURCES/0015-AD-Remember-last-site-discovered-in-sysdb.patch
@@ -0,0 +1,161 @@
+From 1be48c91f3d80b51dc2361217f5c840656e0b088 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pavel=20B=C5=99ezina?= <pbrezina@redhat.com>
+Date: Wed, 1 Nov 2017 14:57:17 +0100
+Subject: [PATCH 15/21] AD: Remember last site discovered in sysdb
+
+This can speed up sssd startup.
+
+Resolves:
+https://pagure.io/SSSD/sssd/issue/3265
+
+Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
+(cherry picked from commit fb0431b13a9fcd8ac31e622503acbd10d2b73ac9)
+---
+ src/db/sysdb_subdomains.c                 |  2 +-
+ src/providers/ad/ad_init.c                |  2 +-
+ src/providers/ad/ad_srv.c                 | 21 +++++++++++++++++++++
+ src/providers/ad/ad_srv.h                 |  1 +
+ src/providers/ad/ad_subdomains.c          |  2 +-
+ src/providers/ipa/ipa_subdomains_server.c |  2 +-
+ 6 files changed, 26 insertions(+), 4 deletions(-)
+
+diff --git a/src/db/sysdb_subdomains.c b/src/db/sysdb_subdomains.c
+index cb5de1afe3e8c9692789c5d2679eb3a4e6e1cdb2..353561765904efe4bd698c38949a1b290ecf0b80 100644
+--- a/src/db/sysdb_subdomains.c
++++ b/src/db/sysdb_subdomains.c
+@@ -1291,7 +1291,7 @@ sysdb_get_site(TALLOC_CTX *mem_ctx,
+                const char **_site)
+ {
+     TALLOC_CTX *tmp_ctx;
+-    struct ldb_res *res;
++    struct ldb_result *res;
+     struct ldb_dn *dn;
+     const char *attrs[] = { SYSDB_SITE, NULL };
+     errno_t ret;
+diff --git a/src/providers/ad/ad_init.c b/src/providers/ad/ad_init.c
+index 131e960d4c623398506f834742400df9c786b86b..e62025d4acd24844a5c7082d00c597516f35de16 100644
+--- a/src/providers/ad/ad_init.c
++++ b/src/providers/ad/ad_init.c
+@@ -199,7 +199,7 @@ static errno_t ad_init_srv_plugin(struct be_ctx *be_ctx,
+         return EOK;
+     }
+ 
+-    srv_ctx = ad_srv_plugin_ctx_init(be_ctx, be_ctx->be_res,
++    srv_ctx = ad_srv_plugin_ctx_init(be_ctx, be_ctx, be_ctx->be_res,
+                                      default_host_dbs, ad_options->id,
+                                      hostname, ad_domain,
+                                      ad_site_override);
+diff --git a/src/providers/ad/ad_srv.c b/src/providers/ad/ad_srv.c
+index be1ba0f237add894566ae713ce5e29fd202d414c..4fa1668605e131b2e31802b1401f49fc6e00a23b 100644
+--- a/src/providers/ad/ad_srv.c
++++ b/src/providers/ad/ad_srv.c
+@@ -34,6 +34,7 @@
+ #include "providers/fail_over_srv.h"
+ #include "providers/ldap/sdap.h"
+ #include "providers/ldap/sdap_async.h"
++#include "db/sysdb.h"
+ 
+ #define AD_SITE_DOMAIN_FMT "%s._sites.%s"
+ 
+@@ -475,6 +476,7 @@ int ad_get_client_site_recv(TALLOC_CTX *mem_ctx,
+ }
+ 
+ struct ad_srv_plugin_ctx {
++    struct be_ctx *be_ctx;
+     struct be_resolv_ctx *be_res;
+     enum host_database *host_dbs;
+     struct sdap_options *opts;
+@@ -486,6 +488,7 @@ struct ad_srv_plugin_ctx {
+ 
+ struct ad_srv_plugin_ctx *
+ ad_srv_plugin_ctx_init(TALLOC_CTX *mem_ctx,
++                       struct be_ctx *be_ctx,
+                        struct be_resolv_ctx *be_res,
+                        enum host_database *host_dbs,
+                        struct sdap_options *opts,
+@@ -494,12 +497,14 @@ ad_srv_plugin_ctx_init(TALLOC_CTX *mem_ctx,
+                        const char *ad_site_override)
+ {
+     struct ad_srv_plugin_ctx *ctx = NULL;
++    errno_t ret;
+ 
+     ctx = talloc_zero(mem_ctx, struct ad_srv_plugin_ctx);
+     if (ctx == NULL) {
+         return NULL;
+     }
+ 
++    ctx->be_ctx = be_ctx;
+     ctx->be_res = be_res;
+     ctx->host_dbs = host_dbs;
+     ctx->opts = opts;
+@@ -524,6 +529,15 @@ ad_srv_plugin_ctx_init(TALLOC_CTX *mem_ctx,
+         if (ctx->current_site == NULL) {
+             goto fail;
+         }
++    } else {
++        ret = sysdb_get_site(ctx, be_ctx->domain, &ctx->current_site);
++        if (ret != EOK) {
++            /* Not fatal. */
++            DEBUG(SSSDBG_MINOR_FAILURE,
++                  "Unable to get current site from cache [%d]: %s\n",
++                  ret, sss_strerror(ret));
++            ctx->current_site = NULL;
++        }
+     }
+ 
+     return ctx;
+@@ -556,6 +570,13 @@ ad_srv_plugin_ctx_switch_site(struct ad_srv_plugin_ctx *ctx,
+     talloc_zfree(ctx->current_site);
+     ctx->current_site = site;
+ 
++    ret = sysdb_set_site(ctx->be_ctx->domain, ctx->current_site);
++    if (ret != EOK) {
++        /* Not fatal. */
++        DEBUG(SSSDBG_MINOR_FAILURE, "Unable to store site information "
++              "[%d]: %s\n", ret, sss_strerror(ret));
++    }
++
+     return EOK;
+ }
+ 
+diff --git a/src/providers/ad/ad_srv.h b/src/providers/ad/ad_srv.h
+index ae5efe44755fa09f74064014cce749e35b1831da..fddef686762e57bb95d648247131d39a797aa516 100644
+--- a/src/providers/ad/ad_srv.h
++++ b/src/providers/ad/ad_srv.h
+@@ -25,6 +25,7 @@ struct ad_srv_plugin_ctx;
+ 
+ struct ad_srv_plugin_ctx *
+ ad_srv_plugin_ctx_init(TALLOC_CTX *mem_ctx,
++                       struct be_ctx *be_ctx,
+                        struct be_resolv_ctx *be_res,
+                        enum host_database *host_dbs,
+                        struct sdap_options *opts,
+diff --git a/src/providers/ad/ad_subdomains.c b/src/providers/ad/ad_subdomains.c
+index 280aa54c23bf61e60d23ea91bd44a39f9f43d155..3fb9b950f171d85817cce35ac92ad7c4974ccb68 100644
+--- a/src/providers/ad/ad_subdomains.c
++++ b/src/providers/ad/ad_subdomains.c
+@@ -245,7 +245,7 @@ ad_subdom_ad_ctx_new(struct be_ctx *be_ctx,
+     ad_options->id_ctx = ad_id_ctx;
+ 
+     /* use AD plugin */
+-    srv_ctx = ad_srv_plugin_ctx_init(be_ctx, be_ctx->be_res,
++    srv_ctx = ad_srv_plugin_ctx_init(be_ctx, be_ctx, be_ctx->be_res,
+                                      default_host_dbs,
+                                      ad_id_ctx->ad_options->id,
+                                      hostname,
+diff --git a/src/providers/ipa/ipa_subdomains_server.c b/src/providers/ipa/ipa_subdomains_server.c
+index 10166d162f746fde176e6c7c2bfbe3906b1bfddc..d670a156b37608d20d49d79131138f02e4abf82b 100644
+--- a/src/providers/ipa/ipa_subdomains_server.c
++++ b/src/providers/ipa/ipa_subdomains_server.c
+@@ -305,7 +305,7 @@ ipa_ad_ctx_new(struct be_ctx *be_ctx,
+     ad_site_override = dp_opt_get_string(ad_options->basic, AD_SITE);
+ 
+     /* use AD plugin */
+-    srv_ctx = ad_srv_plugin_ctx_init(be_ctx, be_ctx->be_res,
++    srv_ctx = ad_srv_plugin_ctx_init(be_ctx, be_ctx, be_ctx->be_res,
+                                      default_host_dbs,
+                                      ad_id_ctx->ad_options->id,
+                                      id_ctx->server_mode->hostname,
+-- 
+2.13.5
+
diff --git a/SOURCES/0015-IPA-Enhance-debug-logging-for-ipa-s2n-operations.patch b/SOURCES/0015-IPA-Enhance-debug-logging-for-ipa-s2n-operations.patch
deleted file mode 100644
index 8031b4c..0000000
--- a/SOURCES/0015-IPA-Enhance-debug-logging-for-ipa-s2n-operations.patch
+++ /dev/null
@@ -1,82 +0,0 @@
-From 359fe83281f2f54d71da65879eafec5ae383f33f Mon Sep 17 00:00:00 2001
-From: Justin Stephenson <jstephen@redhat.com>
-Date: Thu, 16 Mar 2017 14:46:55 -0400
-Subject: [PATCH 15/15] IPA: Enhance debug logging for ipa s2n operations
-
-Add log messages to provide useful debug logging surrounding
-IPA client extended operations to the IPA Server during AD trust
-requests to retrieve information. Print more details about the
-objects requested and received during the ipa_s2n operations.
-
-This will improve log analysis and troubleshooting efforts during AD
-trust user and group resolution failures on IPA clients, such as missing
-groups.
-
-Reviewed-by: Sumit Bose <sbose@redhat.com>
----
- src/providers/ipa/ipa_s2n_exop.c | 28 ++++++++++++++++++++++++++++
- 1 file changed, 28 insertions(+)
-
-diff --git a/src/providers/ipa/ipa_s2n_exop.c b/src/providers/ipa/ipa_s2n_exop.c
-index 4fe20689fe4c0f2bb5217691dd05b37d2a1cc820..c99312274073858e5e03f3e82c069dafc839eb61 100644
---- a/src/providers/ipa/ipa_s2n_exop.c
-+++ b/src/providers/ipa/ipa_s2n_exop.c
-@@ -1156,6 +1156,13 @@ static errno_t ipa_s2n_get_list_step(struct tevent_req *req)
-         need_v1 = true;
-     }
- 
-+    if (state->req_input.type == REQ_INP_NAME
-+            && state->req_input.inp.name != NULL) {
-+        DEBUG(SSSDBG_TRACE_FUNC, "Sending request_type: [%s] for group [%s].\n",
-+                                 ipa_s2n_reqtype2str(state->request_type),
-+                                 state->list[state->list_idx]);
-+    }
-+
-     subreq = ipa_s2n_exop_send(state, state->ev, state->sh, need_v1,
-                                state->exop_timeout, bv_req);
-     if (subreq == NULL) {
-@@ -1194,6 +1201,9 @@ static void ipa_s2n_get_list_next(struct tevent_req *subreq)
-         goto fail;
-     }
- 
-+    DEBUG(SSSDBG_TRACE_FUNC, "Received [%s] attributes from IPA server.\n",
-+                             state->attrs->a.name);
-+
-     if (is_default_view(state->ipa_ctx->view_name)) {
-         ret = ipa_s2n_get_list_save_step(req);
-         if (ret == EOK) {
-@@ -1375,6 +1385,11 @@ struct tevent_req *ipa_s2n_get_acct_info_send(TALLOC_CTX *mem_ctx,
-         goto fail;
-     }
- 
-+    DEBUG(SSSDBG_TRACE_FUNC, "Sending request_type: [%s] for trust user [%s] "
-+                            "to IPA server\n",
-+                            ipa_s2n_reqtype2str(state->request_type),
-+                            req_input->inp.name);
-+
-     subreq = ipa_s2n_exop_send(state, state->ev, state->sh, is_v1,
-                                state->exop_timeout, bv_req);
-     if (subreq == NULL) {
-@@ -1661,6 +1676,19 @@ static void ipa_s2n_get_user_done(struct tevent_req *subreq)
-         state->attrs = attrs;
- 
-         if (attrs->response_type == RESP_USER_GROUPLIST) {
-+
-+            if (DEBUG_IS_SET(SSSDBG_TRACE_FUNC)) {
-+                size_t c;
-+
-+                DEBUG(SSSDBG_TRACE_FUNC, "Received [%zu] groups in group list "
-+                                         "from IPA Server\n", attrs->ngroups);
-+
-+                for (c = 0; c < attrs->ngroups; c++) {
-+                    DEBUG(SSSDBG_TRACE_FUNC, "[%s].\n", attrs->groups[c]);
-+                }
-+            }
-+
-+
-             ret = get_group_dn_list(state, state->dom,
-                                     attrs->ngroups, attrs->groups,
-                                     &group_dn_list, &missing_list);
--- 
-2.9.3
-
diff --git a/SOURCES/0016-UTIL-Add-wrapper-function-to-configure-logger.patch b/SOURCES/0016-UTIL-Add-wrapper-function-to-configure-logger.patch
new file mode 100644
index 0000000..a4dea5f
--- /dev/null
+++ b/SOURCES/0016-UTIL-Add-wrapper-function-to-configure-logger.patch
@@ -0,0 +1,133 @@
+From 34fa82bc07c4da14606d7a8a9b68a872fe210a9f Mon Sep 17 00:00:00 2001
+From: Lukas Slebodnik <lslebodn@redhat.com>
+Date: Mon, 23 Oct 2017 14:58:14 +0200
+Subject: [PATCH 16/21] UTIL: Add wrapper function to configure logger
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Let's use one enum for logger type instead of many integers (debug_to_file,
+debug_to_stderr plus some weird combination for journald).
+Old variable were also transformed to enum for backward compatibility
+
+Reviewed-by: Fabiano Fidêncio <fidencio@redhat.com>
+(cherry picked from commit 09e3f0af96cecb94be69084c025f0355b3111993)
+---
+ src/util/debug.c | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ src/util/debug.h | 18 ++++++++++++++++++
+ 2 files changed, 72 insertions(+)
+
+diff --git a/src/util/debug.c b/src/util/debug.c
+index ca4fa4c6f5b150700a0a136d8a7ca9df30c29d73..4e469447e5ab8aa89cd57bcd6d00269875a12bc6 100644
+--- a/src/util/debug.c
++++ b/src/util/debug.c
+@@ -43,9 +43,63 @@ int debug_timestamps = SSSDBG_TIMESTAMP_UNRESOLVED;
+ int debug_microseconds = SSSDBG_MICROSECONDS_UNRESOLVED;
+ int debug_to_file = 0;
+ int debug_to_stderr = 0;
++enum sss_logger_t sss_logger;
+ const char *debug_log_file = "sssd";
+ FILE *debug_file = NULL;
+ 
++const char *sss_logger_str[] = {
++        [STDERR_LOGGER] = "stderr",
++        [FILES_LOGGER] = "files",
++#ifdef WITH_JOURNALD
++        [JOURNALD_LOGGER] = "journald",
++#endif
++        NULL,
++};
++
++#ifdef WITH_JOURNALD
++#define JOURNALD_STR " journald,"
++#else
++#define JOURNALD_STR ""
++#endif
++
++void sss_set_logger(const char *logger)
++{
++    /* use old flags */
++    if (logger == NULL) {
++        if (debug_to_stderr != 0) {
++            sss_logger = STDERR_LOGGER;
++        }
++        /* It is never described what should be used in case of
++         * debug_to_stderr == 1 && debug_to_file == 1. Because neither
++         * of binaries provide both command line arguments.
++         * Let files have higher priority.
++         */
++        if (debug_to_file != 0) {
++            sss_logger = FILES_LOGGER;
++        }
++#ifdef WITH_JOURNALD
++        if (debug_to_file == 0 && debug_to_stderr == 0) {
++            sss_logger = JOURNALD_LOGGER;
++        }
++#endif
++    } else {
++        if (strcmp(logger, "stderr") == 0) {
++            sss_logger = STDERR_LOGGER;
++        } else if (strcmp(logger, "files") == 0) {
++            sss_logger = FILES_LOGGER;
++#ifdef WITH_JOURNALD
++        } else if (strcmp(logger, "journald") == 0) {
++            sss_logger = JOURNALD_LOGGER;
++#endif
++        } else {
++            /* unexpected value */
++            fprintf(stderr, "Unexpected logger: %s\nExpected:%s stderr, "
++                            "files\n", logger, JOURNALD_STR);
++            sss_logger = STDERR_LOGGER;
++        }
++    }
++}
++
+ errno_t set_debug_file_from_fd(const int fd)
+ {
+     FILE *dummy;
+diff --git a/src/util/debug.h b/src/util/debug.h
+index 2a1bd4ffd30817d7128805996c21105fe40982a2..4adafb7cfc03f7381c4d03071eb44edad04bee00 100644
+--- a/src/util/debug.h
++++ b/src/util/debug.h
+@@ -31,13 +31,26 @@
+ 
+ #define APPEND_LINE_FEED 0x1
+ 
++enum sss_logger_t {
++    STDERR_LOGGER = 0,
++    FILES_LOGGER,
++#ifdef WITH_JOURNALD
++    JOURNALD_LOGGER,
++#endif
++};
++
++extern const char *sss_logger_str[];
+ extern const char *debug_prg_name;
+ extern int debug_level;
+ extern int debug_timestamps;
+ extern int debug_microseconds;
+ extern int debug_to_file;
+ extern int debug_to_stderr;
++extern enum sss_logger_t sss_logger;
+ extern const char *debug_log_file;
++
++void sss_set_logger(const char *logger);
++
+ void sss_vdebug_fn(const char *file,
+                    long line,
+                    const char *function,
+@@ -80,6 +93,11 @@ int get_fd_from_debug_file(void);
+ #define SSSDBG_MICROSECONDS_UNRESOLVED   -1
+ #define SSSDBG_MICROSECONDS_DEFAULT       0
+ 
++#define SSSD_LOGGER_OPTS \
++        {"logger", '\0', POPT_ARG_STRING, &opt_logger, 0, \
++         _("Set logger"), "stderr|files|journald"},
++
++
+ #define SSSD_DEBUG_OPTS \
+         {"debug-level", 'd', POPT_ARG_INT, &debug_level, 0, \
+          _("Debug level"), NULL}, \
+-- 
+2.13.5
+
diff --git a/SOURCES/0016-UTIL-iobuf-Make-input-parameter-for-the-readonly-ope.patch b/SOURCES/0016-UTIL-iobuf-Make-input-parameter-for-the-readonly-ope.patch
deleted file mode 100644
index 5ded3ab..0000000
--- a/SOURCES/0016-UTIL-iobuf-Make-input-parameter-for-the-readonly-ope.patch
+++ /dev/null
@@ -1,45 +0,0 @@
-From 4ea851ed034efdb06d13b34797b9f849e3dcec97 Mon Sep 17 00:00:00 2001
-From: Jakub Hrozek <jhrozek@redhat.com>
-Date: Wed, 15 Mar 2017 13:32:42 +0100
-Subject: [PATCH 16/36] UTIL: iobuf: Make input parameter for the readonly
- operation const
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Reviewed-by: Michal Židek <mzidek@redhat.com>
-Reviewed-by: Simo Sorce <simo@redhat.com>
----
- src/util/sss_iobuf.c | 2 +-
- src/util/sss_iobuf.h | 2 +-
- 2 files changed, 2 insertions(+), 2 deletions(-)
-
-diff --git a/src/util/sss_iobuf.c b/src/util/sss_iobuf.c
-index 7c72ea94d7a005dfd9671793b3ad470a6de7967a..900418b750a3455ebc2c3bb1893db726692260b8 100644
---- a/src/util/sss_iobuf.c
-+++ b/src/util/sss_iobuf.c
-@@ -49,7 +49,7 @@ struct sss_iobuf *sss_iobuf_init_empty(TALLOC_CTX *mem_ctx,
- }
- 
- struct sss_iobuf *sss_iobuf_init_readonly(TALLOC_CTX *mem_ctx,
--                                          uint8_t *data,
-+                                          const uint8_t *data,
-                                           size_t size)
- {
-     struct sss_iobuf *iobuf;
-diff --git a/src/util/sss_iobuf.h b/src/util/sss_iobuf.h
-index eae357a40f2948e63df189f2842edee68691a542..900faaa212230f72f52e344c085167e80ae2b465 100644
---- a/src/util/sss_iobuf.h
-+++ b/src/util/sss_iobuf.h
-@@ -47,7 +47,7 @@ struct sss_iobuf *sss_iobuf_init_empty(TALLOC_CTX *mem_ctx,
-  * @return The newly created buffer on success or NULL on an error.
-  */
- struct sss_iobuf *sss_iobuf_init_readonly(TALLOC_CTX *mem_ctx,
--                                          uint8_t *data,
-+                                          const uint8_t *data,
-                                           size_t size);
- 
- /*
--- 
-2.9.3
-
diff --git a/SOURCES/0017-Add-parameter-logger-to-daemons.patch b/SOURCES/0017-Add-parameter-logger-to-daemons.patch
new file mode 100644
index 0000000..1a547c3
--- /dev/null
+++ b/SOURCES/0017-Add-parameter-logger-to-daemons.patch
@@ -0,0 +1,830 @@
+From de26ea6f603e87ef306f08fa1b458b8a180bdf2d Mon Sep 17 00:00:00 2001
+From: Lukas Slebodnik <lslebodn@redhat.com>
+Date: Mon, 23 Oct 2017 15:18:47 +0200
+Subject: [PATCH 17/21] Add parameter --logger to daemons
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Different binary handled information about logging differently
+ e,g, --debug-to-files --debug-to-stderr
+And logging to journald was a special case of previous options
+(!debug_file && !debug_to_stderr). It was also tied to the monitor option
+"--daemon" and therefore loggind to stderr was used in interactive mode
++ systemd Type=notify.
+
+Resolves:
+https://pagure.io/SSSD/sssd/issue/3433
+
+Reviewed-by: Justin Stephenson <jstephen@redhat.com>
+Reviewed-by: Fabiano Fidêncio <fidencio@redhat.com>
+(cherry picked from commit cb75b275d15beedd1fdecc1f8ced657fba282218)
+---
+ src/man/sssd.8.xml                | 31 +++++++++++++++++++++++++
+ src/monitor/monitor.c             | 48 ++++++++++++---------------------------
+ src/p11_child/p11_child_nss.c     |  3 +++
+ src/providers/ad/ad_gpo_child.c   |  4 ++++
+ src/providers/data_provider_be.c  |  4 ++++
+ src/providers/ipa/selinux_child.c |  4 ++++
+ src/providers/krb5/krb5_child.c   |  4 ++++
+ src/providers/ldap/ldap_child.c   |  4 ++++
+ src/providers/proxy/proxy_auth.c  |  4 ++--
+ src/providers/proxy/proxy_child.c |  4 ++++
+ src/responder/autofs/autofssrv.c  |  4 ++++
+ src/responder/ifp/ifpsrv.c        |  4 ++++
+ src/responder/kcm/kcm.c           |  4 ++++
+ src/responder/nss/nsssrv.c        |  4 ++++
+ src/responder/pac/pacsrv.c        |  4 ++++
+ src/responder/pam/pamsrv.c        |  4 ++++
+ src/responder/secrets/secsrv.c    |  4 ++++
+ src/responder/ssh/sshsrv.c        |  4 ++++
+ src/responder/sudo/sudosrv.c      |  4 ++++
+ src/tests/cmocka/dummy_child.c    |  4 ++++
+ src/tests/debug-tests.c           | 10 ++++++++
+ src/util/child_common.c           |  2 +-
+ src/util/debug.c                  |  4 ++--
+ src/util/server.c                 | 12 ++++++----
+ 24 files changed, 135 insertions(+), 43 deletions(-)
+
+diff --git a/src/man/sssd.8.xml b/src/man/sssd.8.xml
+index 923da6824907f0d2d140d9ca83f87338e7664f83..0b725628ff93f48f832140dd5dc15b040a8b179f 100644
+--- a/src/man/sssd.8.xml
++++ b/src/man/sssd.8.xml
+@@ -94,6 +94,37 @@
+             </varlistentry>
+             <varlistentry>
+                 <term>
++                    <option>--logger=</option><replaceable>value</replaceable>
++                </term>
++                <listitem>
++                    <para>
++                        Location where SSSD will send log messages. This option
++                        overrides the value of the deprecated option
++                        <option>--debug-to-files</option>. The deprecated
++                        option will still work if the <option>--logger</option>
++                        is not used.
++                    </para>
++                    <para>
++                        <emphasis>stderr</emphasis>: Redirect debug messages to
++                        standard error output.
++                    </para>
++                    <para>
++                        <emphasis>files</emphasis>: Redirect debug messages to
++                        the log files. By default, the log files are stored in
++                        <filename>/var/log/sssd</filename> and there are
++                        separate log files for every SSSD service and domain.
++                    </para>
++                    <para>
++                        <emphasis>journald</emphasis>: Redirect debug messages
++                        to systemd-journald
++                    </para>
++                    <para>
++                        Default: not set
++                    </para>
++                </listitem>
++            </varlistentry>
++            <varlistentry>
++                <term>
+                     <option>-D</option>,<option>--daemon</option>
+                 </term>
+                 <listitem>
+diff --git a/src/monitor/monitor.c b/src/monitor/monitor.c
+index 7726548bbb666bb189667efc1de2295f8a001105..3c0b7ab2dac10fe15a8a5b807cb68ea4b7ab8461 100644
+--- a/src/monitor/monitor.c
++++ b/src/monitor/monitor.c
+@@ -1211,22 +1211,11 @@ static int get_service_config(struct mt_ctx *ctx, const char *name,
+             }
+         }
+ 
+-        if (debug_to_file) {
+-            svc->command = talloc_strdup_append(
+-                svc->command, " --debug-to-files"
+-            );
+-            if (!svc->command) {
+-                talloc_free(svc);
+-                return ENOMEM;
+-            }
+-        } else if (ctx->is_daemon == false) {
+-            svc->command = talloc_strdup_append(
+-                svc->command, " --debug-to-stderr"
+-            );
+-            if (!svc->command) {
+-                talloc_free(svc);
+-                return ENOMEM;
+-            }
++        svc->command = talloc_asprintf_append(
++            svc->command, " --logger=%s", sss_logger_str[sss_logger]);
++        if (!svc->command) {
++            talloc_free(svc);
++            return ENOMEM;
+         }
+     }
+ 
+@@ -1374,22 +1363,11 @@ static int get_provider_config(struct mt_ctx *ctx, const char *name,
+             }
+         }
+ 
+-        if (debug_to_file) {
+-            svc->command = talloc_strdup_append(
+-                svc->command, " --debug-to-files"
+-            );
+-            if (!svc->command) {
+-                talloc_free(svc);
+-                return ENOMEM;
+-            }
+-        } else if (ctx->is_daemon == false) {
+-            svc->command = talloc_strdup_append(
+-                svc->command, " --debug-to-stderr"
+-            );
+-            if (!svc->command) {
+-                talloc_free(svc);
+-                return ENOMEM;
+-            }
++        svc->command = talloc_asprintf_append(
++            svc->command, " --logger=%s", sss_logger_str[sss_logger]);
++        if (!svc->command) {
++            talloc_free(svc);
++            return ENOMEM;
+         }
+     }
+ 
+@@ -2454,6 +2432,7 @@ int main(int argc, const char *argv[])
+     int opt_version = 0;
+     int opt_netlinkoff = 0;
+     char *opt_config_file = NULL;
++    char *opt_logger = NULL;
+     char *config_file = NULL;
+     int flags = 0;
+     struct main_context *main_ctx;
+@@ -2465,6 +2444,7 @@ int main(int argc, const char *argv[])
+     struct poptOption long_options[] = {
+         POPT_AUTOHELP
+         SSSD_MAIN_OPTS
++        SSSD_LOGGER_OPTS
+         {"daemon", 'D', POPT_ARG_NONE, &opt_daemon, 0, \
+          _("Become a daemon (default)"), NULL }, \
+         {"interactive", 'i', POPT_ARG_NONE, &opt_interactive, 0, \
+@@ -2551,6 +2531,8 @@ int main(int argc, const char *argv[])
+         debug_to_stderr = 1;
+     }
+ 
++    sss_set_logger(opt_logger);
++
+     if (opt_config_file) {
+         config_file = talloc_strdup(tmp_ctx, opt_config_file);
+     } else {
+@@ -2575,7 +2557,7 @@ int main(int argc, const char *argv[])
+ 
+     /* Open before server_setup() does to have logging
+      * during configuration checking */
+-    if (debug_to_file) {
++    if (sss_logger == FILES_LOGGER) {
+         ret = open_debug_file();
+         if (ret) {
+             return 7;
+diff --git a/src/p11_child/p11_child_nss.c b/src/p11_child/p11_child_nss.c
+index f165b58e63d2b8a6f26acf8bd89e7b41713e7359..e7dbcb689220d1cd2585fbde5f26e84f8fa15cc2 100644
+--- a/src/p11_child/p11_child_nss.c
++++ b/src/p11_child/p11_child_nss.c
+@@ -537,6 +537,7 @@ int main(int argc, const char *argv[])
+     int opt;
+     poptContext pc;
+     int debug_fd = -1;
++    char *opt_logger = NULL;
+     errno_t ret;
+     TALLOC_CTX *main_ctx = NULL;
+     char *cert;
+@@ -564,6 +565,7 @@ int main(int argc, const char *argv[])
+         {"debug-to-stderr", 0, POPT_ARG_NONE | POPT_ARGFLAG_DOC_HIDDEN,
+          &debug_to_stderr, 0,
+          _("Send the debug output to stderr directly."), NULL },
++        SSSD_LOGGER_OPTS
+         {"auth", 0, POPT_ARG_NONE, NULL, 'a', _("Run in auth mode"), NULL},
+         {"pre", 0, POPT_ARG_NONE, NULL, 'p', _("Run in pre-auth mode"), NULL},
+         {"pin", 0, POPT_ARG_NONE, NULL, 'i', _("Expect PIN on stdin"), NULL},
+@@ -672,6 +674,7 @@ int main(int argc, const char *argv[])
+             DEBUG(SSSDBG_CRIT_FAILURE, "set_debug_file_from_fd failed.\n");
+         }
+     }
++    sss_set_logger(opt_logger);
+ 
+     DEBUG(SSSDBG_TRACE_FUNC, "p11_child started.\n");
+ 
+diff --git a/src/providers/ad/ad_gpo_child.c b/src/providers/ad/ad_gpo_child.c
+index 8e5e062547721567cb450f9d0f72f1ec8cb99f96..5375cc691e8649c289672b74c4bfe5266c8222c9 100644
+--- a/src/providers/ad/ad_gpo_child.c
++++ b/src/providers/ad/ad_gpo_child.c
+@@ -687,6 +687,7 @@ main(int argc, const char *argv[])
+     int opt;
+     poptContext pc;
+     int debug_fd = -1;
++    char *opt_logger = NULL;
+     errno_t ret;
+     int sysvol_gpt_version;
+     int result;
+@@ -710,6 +711,7 @@ main(int argc, const char *argv[])
+         {"debug-to-stderr", 0, POPT_ARG_NONE | POPT_ARGFLAG_DOC_HIDDEN,
+          &debug_to_stderr, 0,
+          _("Send the debug output to stderr directly."), NULL },
++        SSSD_LOGGER_OPTS
+         POPT_TABLEEND
+     };
+ 
+@@ -744,6 +746,8 @@ main(int argc, const char *argv[])
+         }
+     }
+ 
++    sss_set_logger(opt_logger);
++
+     DEBUG(SSSDBG_TRACE_FUNC, "gpo_child started.\n");
+ 
+     main_ctx = talloc_new(NULL);
+diff --git a/src/providers/data_provider_be.c b/src/providers/data_provider_be.c
+index 2e55dc4e3fe9ba1aa8c1c51c426efee00b9ae91d..56ddac112a209b6937313d3d3c94a73d2067331f 100644
+--- a/src/providers/data_provider_be.c
++++ b/src/providers/data_provider_be.c
+@@ -537,6 +537,7 @@ int main(int argc, const char *argv[])
+ {
+     int opt;
+     poptContext pc;
++    char *opt_logger = NULL;
+     char *be_domain = NULL;
+     char *srv_name = NULL;
+     struct main_context *main_ctx;
+@@ -548,6 +549,7 @@ int main(int argc, const char *argv[])
+     struct poptOption long_options[] = {
+         POPT_AUTOHELP
+         SSSD_MAIN_OPTS
++        SSSD_LOGGER_OPTS
+         SSSD_SERVER_OPTS(uid, gid)
+         {"domain", 0, POPT_ARG_STRING, &be_domain, 0,
+          _("Domain of the information provider (mandatory)"), NULL },
+@@ -582,6 +584,8 @@ int main(int argc, const char *argv[])
+     debug_log_file = talloc_asprintf(NULL, "sssd_%s", be_domain);
+     if (!debug_log_file) return 2;
+ 
++    sss_set_logger(opt_logger);
++
+     srv_name = talloc_asprintf(NULL, "sssd[be[%s]]", be_domain);
+     if (!srv_name) return 2;
+ 
+diff --git a/src/providers/ipa/selinux_child.c b/src/providers/ipa/selinux_child.c
+index 073475094ee491bd5453898c6ba65214fa14fe59..120492686963241b7e419413f489cc38953e32f2 100644
+--- a/src/providers/ipa/selinux_child.c
++++ b/src/providers/ipa/selinux_child.c
+@@ -206,6 +206,7 @@ int main(int argc, const char *argv[])
+     struct response *resp = NULL;
+     ssize_t written;
+     bool needs_update;
++    char *opt_logger = NULL;
+ 
+     struct poptOption long_options[] = {
+         POPT_AUTOHELP
+@@ -220,6 +221,7 @@ int main(int argc, const char *argv[])
+         {"debug-to-stderr", 0, POPT_ARG_NONE | POPT_ARGFLAG_DOC_HIDDEN,
+          &debug_to_stderr, 0,
+          _("Send the debug output to stderr directly."), NULL },
++        SSSD_LOGGER_OPTS
+         POPT_TABLEEND
+     };
+ 
+@@ -254,6 +256,8 @@ int main(int argc, const char *argv[])
+         }
+     }
+ 
++    sss_set_logger(opt_logger);
++
+     DEBUG(SSSDBG_TRACE_FUNC, "selinux_child started.\n");
+     DEBUG(SSSDBG_TRACE_INTERNAL,
+           "Running with effective IDs: [%"SPRIuid"][%"SPRIgid"].\n",
+diff --git a/src/providers/krb5/krb5_child.c b/src/providers/krb5/krb5_child.c
+index 888cc5d6f5c554901cc46d4315844d7bbbe582b8..700338e47a3f9ac6fcf11b4c92364dbdb4f9bcf7 100644
+--- a/src/providers/krb5/krb5_child.c
++++ b/src/providers/krb5/krb5_child.c
+@@ -3020,6 +3020,7 @@ int main(int argc, const char *argv[])
+     int opt;
+     poptContext pc;
+     int debug_fd = -1;
++    char *opt_logger = NULL;
+     errno_t ret;
+     krb5_error_code kerr;
+     uid_t fast_uid;
+@@ -3039,6 +3040,7 @@ int main(int argc, const char *argv[])
+         {"debug-to-stderr", 0, POPT_ARG_NONE | POPT_ARGFLAG_DOC_HIDDEN,
+          &debug_to_stderr, 0,
+          _("Send the debug output to stderr directly."), NULL },
++        SSSD_LOGGER_OPTS
+         {CHILD_OPT_FAST_CCACHE_UID, 0, POPT_ARG_INT, &fast_uid, 0,
+           _("The user to create FAST ccache as"), NULL},
+         {CHILD_OPT_FAST_CCACHE_GID, 0, POPT_ARG_INT, &fast_gid, 0,
+@@ -3097,6 +3099,8 @@ int main(int argc, const char *argv[])
+         }
+     }
+ 
++    sss_set_logger(opt_logger);
++
+     DEBUG(SSSDBG_TRACE_FUNC, "krb5_child started.\n");
+ 
+     kr = talloc_zero(NULL, struct krb5_req);
+diff --git a/src/providers/ldap/ldap_child.c b/src/providers/ldap/ldap_child.c
+index b796e5cae01517c85c2fc1605b1e5877454691dc..baeed239db5dc7ffa482edcbc155f25f718c8249 100644
+--- a/src/providers/ldap/ldap_child.c
++++ b/src/providers/ldap/ldap_child.c
+@@ -599,6 +599,7 @@ int main(int argc, const char *argv[])
+     int kerr;
+     int opt;
+     int debug_fd = -1;
++    char *opt_logger = NULL;
+     poptContext pc;
+     TALLOC_CTX *main_ctx = NULL;
+     uint8_t *buf = NULL;
+@@ -622,6 +623,7 @@ int main(int argc, const char *argv[])
+          _("An open file descriptor for the debug logs"), NULL},
+         {"debug-to-stderr", 0, POPT_ARG_NONE | POPT_ARGFLAG_DOC_HIDDEN, &debug_to_stderr, 0, \
+          _("Send the debug output to stderr directly."), NULL }, \
++        SSSD_LOGGER_OPTS
+         POPT_TABLEEND
+     };
+ 
+@@ -657,6 +659,8 @@ int main(int argc, const char *argv[])
+         }
+     }
+ 
++    sss_set_logger(opt_logger);
++
+     BlockSignals(false, SIGTERM);
+     CatchSignal(SIGTERM, sig_term_handler);
+ 
+diff --git a/src/providers/proxy/proxy_auth.c b/src/providers/proxy/proxy_auth.c
+index a05586e60b6ef894b0fcf1b8b3f30fdbf51a808d..665a29cf779290b8d35973245a36a1b5224bca78 100644
+--- a/src/providers/proxy/proxy_auth.c
++++ b/src/providers/proxy/proxy_auth.c
+@@ -178,9 +178,9 @@ static struct tevent_req *proxy_child_init_send(TALLOC_CTX *mem_ctx,
+ 
+     state->command = talloc_asprintf(req,
+             "%s/proxy_child -d %#.4x --debug-timestamps=%d "
+-            "--debug-microseconds=%d%s --domain %s --id %d",
++            "--debug-microseconds=%d --logger=%s --domain %s --id %d",
+             SSSD_LIBEXEC_PATH, debug_level, debug_timestamps,
+-            debug_microseconds, (debug_to_file ? " --debug-to-files" : ""),
++            debug_microseconds, sss_logger_str[sss_logger],
+             auth_ctx->be->domain->name,
+             child_ctx->id);
+     if (state->command == NULL) {
+diff --git a/src/providers/proxy/proxy_child.c b/src/providers/proxy/proxy_child.c
+index be58622eb8b26231eeb6699976d51f57dc44de98..ae4855adeb5cc68f1a19003355a5d94f5b1bb378 100644
+--- a/src/providers/proxy/proxy_child.c
++++ b/src/providers/proxy/proxy_child.c
+@@ -504,6 +504,7 @@ int main(int argc, const char *argv[])
+ {
+     int opt;
+     poptContext pc;
++    char *opt_logger = NULL;
+     char *domain = NULL;
+     char *srv_name = NULL;
+     char *conf_entry = NULL;
+@@ -517,6 +518,7 @@ int main(int argc, const char *argv[])
+     struct poptOption long_options[] = {
+         POPT_AUTOHELP
+         SSSD_MAIN_OPTS
++        SSSD_LOGGER_OPTS
+         SSSD_SERVER_OPTS(uid, gid)
+         {"domain", 0, POPT_ARG_STRING, &domain, 0,
+          _("Domain of the information provider (mandatory)"), NULL },
+@@ -561,6 +563,8 @@ int main(int argc, const char *argv[])
+     debug_log_file = talloc_asprintf(NULL, "proxy_child_%s", domain);
+     if (!debug_log_file) return 2;
+ 
++    sss_set_logger(opt_logger);
++
+     srv_name = talloc_asprintf(NULL, "sssd[proxy_child[%s]]", domain);
+     if (!srv_name) return 2;
+ 
+diff --git a/src/responder/autofs/autofssrv.c b/src/responder/autofs/autofssrv.c
+index cfb2233fdfc346bf27b128ee8c4261f4c73e3470..b0762a2b685a7c5ab3abfa281f0906ad8bfe1c88 100644
+--- a/src/responder/autofs/autofssrv.c
++++ b/src/responder/autofs/autofssrv.c
+@@ -185,6 +185,7 @@ int main(int argc, const char *argv[])
+ {
+     int opt;
+     poptContext pc;
++    char *opt_logger = NULL;
+     struct main_context *main_ctx;
+     int ret;
+     uid_t uid;
+@@ -193,6 +194,7 @@ int main(int argc, const char *argv[])
+     struct poptOption long_options[] = {
+         POPT_AUTOHELP
+         SSSD_MAIN_OPTS
++        SSSD_LOGGER_OPTS
+         SSSD_SERVER_OPTS(uid, gid)
+         SSSD_RESPONDER_OPTS
+         POPT_TABLEEND
+@@ -221,6 +223,8 @@ int main(int argc, const char *argv[])
+     /* set up things like debug, signals, daemonization, etc... */
+     debug_log_file = "sssd_autofs";
+ 
++    sss_set_logger(opt_logger);
++
+     ret = server_setup("sssd[autofs]", 0, uid, gid,
+                        CONFDB_AUTOFS_CONF_ENTRY, &main_ctx);
+     if (ret != EOK) {
+diff --git a/src/responder/ifp/ifpsrv.c b/src/responder/ifp/ifpsrv.c
+index 0dc61a42200cc79fc6f12515a8f581ad0201a043..85dfbacc217e2870dd7517e36a1d39e7f2054a8b 100644
+--- a/src/responder/ifp/ifpsrv.c
++++ b/src/responder/ifp/ifpsrv.c
+@@ -355,6 +355,7 @@ int main(int argc, const char *argv[])
+ {
+     int opt;
+     poptContext pc;
++    char *opt_logger = NULL;
+     struct main_context *main_ctx;
+     int ret;
+     uid_t uid;
+@@ -363,6 +364,7 @@ int main(int argc, const char *argv[])
+     struct poptOption long_options[] = {
+         POPT_AUTOHELP
+         SSSD_MAIN_OPTS
++        SSSD_LOGGER_OPTS
+         SSSD_SERVER_OPTS(uid, gid)
+         SSSD_RESPONDER_OPTS
+         POPT_TABLEEND
+@@ -391,6 +393,8 @@ int main(int argc, const char *argv[])
+     /* set up things like debug, signals, daemonization, etc... */
+     debug_log_file = "sssd_ifp";
+ 
++    sss_set_logger(opt_logger);
++
+     ret = server_setup("sssd[ifp]", 0, 0, 0,
+                        CONFDB_IFP_CONF_ENTRY, &main_ctx);
+     if (ret != EOK) return 2;
+diff --git a/src/responder/kcm/kcm.c b/src/responder/kcm/kcm.c
+index 2202f96381a2622a2c5433e281172287b325f960..358fcc18165dec7b41a7389a3ef22660ac04b4a8 100644
+--- a/src/responder/kcm/kcm.c
++++ b/src/responder/kcm/kcm.c
+@@ -258,6 +258,7 @@ int main(int argc, const char *argv[])
+ {
+     int opt;
+     poptContext pc;
++    char *opt_logger = NULL;
+     struct main_context *main_ctx;
+     int ret;
+     uid_t uid;
+@@ -266,6 +267,7 @@ int main(int argc, const char *argv[])
+     struct poptOption long_options[] = {
+         POPT_AUTOHELP
+         SSSD_MAIN_OPTS
++        SSSD_LOGGER_OPTS
+         SSSD_SERVER_OPTS(uid, gid)
+         POPT_TABLEEND
+     };
+@@ -293,6 +295,8 @@ int main(int argc, const char *argv[])
+     /* set up things like debug, signals, daemonization, etc... */
+     debug_log_file = "sssd_kcm";
+ 
++    sss_set_logger(opt_logger);
++
+     ret = server_setup("sssd[kcm]", 0, uid, gid, CONFDB_KCM_CONF_ENTRY,
+                        &main_ctx);
+     if (ret != EOK) return 2;
+diff --git a/src/responder/nss/nsssrv.c b/src/responder/nss/nsssrv.c
+index 32bfcd69bbb9b35e9932b70a826c4f99ab6a07f3..11d19fd30c86283d537623db12e52caa6cc4dcd3 100644
+--- a/src/responder/nss/nsssrv.c
++++ b/src/responder/nss/nsssrv.c
+@@ -422,6 +422,7 @@ int main(int argc, const char *argv[])
+ {
+     int opt;
+     poptContext pc;
++    char *opt_logger = NULL;
+     struct main_context *main_ctx;
+     int ret;
+     uid_t uid;
+@@ -430,6 +431,7 @@ int main(int argc, const char *argv[])
+     struct poptOption long_options[] = {
+         POPT_AUTOHELP
+         SSSD_MAIN_OPTS
++        SSSD_LOGGER_OPTS
+         SSSD_SERVER_OPTS(uid, gid)
+         SSSD_RESPONDER_OPTS
+         POPT_TABLEEND
+@@ -458,6 +460,8 @@ int main(int argc, const char *argv[])
+     /* set up things like debug, signals, daemonization, etc... */
+     debug_log_file = "sssd_nss";
+ 
++    sss_set_logger(opt_logger);
++
+     ret = server_setup("sssd[nss]", 0, uid, gid, CONFDB_NSS_CONF_ENTRY,
+                        &main_ctx);
+     if (ret != EOK) return 2;
+diff --git a/src/responder/pac/pacsrv.c b/src/responder/pac/pacsrv.c
+index 1f820c07f5c55fe8df75cce05b403c41075d9f94..b72e5c8d2a42bc85f0974dcb81a1290d3f740986 100644
+--- a/src/responder/pac/pacsrv.c
++++ b/src/responder/pac/pacsrv.c
+@@ -209,6 +209,7 @@ int main(int argc, const char *argv[])
+ {
+     int opt;
+     poptContext pc;
++    char *opt_logger = NULL;
+     struct main_context *main_ctx;
+     int ret;
+     uid_t uid;
+@@ -217,6 +218,7 @@ int main(int argc, const char *argv[])
+     struct poptOption long_options[] = {
+         POPT_AUTOHELP
+         SSSD_MAIN_OPTS
++        SSSD_LOGGER_OPTS
+         SSSD_SERVER_OPTS(uid, gid)
+         SSSD_RESPONDER_OPTS
+         POPT_TABLEEND
+@@ -245,6 +247,8 @@ int main(int argc, const char *argv[])
+     /* set up things like debug, signals, daemonization, etc... */
+     debug_log_file = "sssd_pac";
+ 
++    sss_set_logger(opt_logger);
++
+     ret = server_setup("sssd[pac]", 0, uid, gid,
+                        CONFDB_PAC_CONF_ENTRY, &main_ctx);
+     if (ret != EOK) return 2;
+diff --git a/src/responder/pam/pamsrv.c b/src/responder/pam/pamsrv.c
+index 79470823d18138da6ef9235e6336a3220ead1797..cc0e4bddcdbecfadabea78a6d2815d0ac6d651b6 100644
+--- a/src/responder/pam/pamsrv.c
++++ b/src/responder/pam/pamsrv.c
+@@ -355,6 +355,7 @@ int main(int argc, const char *argv[])
+ {
+     int opt;
+     poptContext pc;
++    char *opt_logger = NULL;
+     struct main_context *main_ctx;
+     int ret;
+     uid_t uid;
+@@ -365,6 +366,7 @@ int main(int argc, const char *argv[])
+     struct poptOption long_options[] = {
+         POPT_AUTOHELP
+         SSSD_MAIN_OPTS
++        SSSD_LOGGER_OPTS
+         SSSD_SERVER_OPTS(uid, gid)
+         SSSD_RESPONDER_OPTS
+         POPT_TABLEEND
+@@ -393,6 +395,8 @@ int main(int argc, const char *argv[])
+     /* set up things like debug, signals, daemonization, etc... */
+     debug_log_file = "sssd_pam";
+ 
++    sss_set_logger(opt_logger);
++
+     if (!is_socket_activated()) {
+         /* Crate pipe file descriptors here before privileges are dropped
+          * in server_setup() */
+diff --git a/src/responder/secrets/secsrv.c b/src/responder/secrets/secsrv.c
+index 2b661b165ef0c174557f53012b2dbaa236a6e359..59c0f3a56040a6fc0c092247fbd124a069f97153 100644
+--- a/src/responder/secrets/secsrv.c
++++ b/src/responder/secrets/secsrv.c
+@@ -324,6 +324,7 @@ int main(int argc, const char *argv[])
+ {
+     int opt;
+     poptContext pc;
++    char *opt_logger = NULL;
+     struct main_context *main_ctx;
+     int ret;
+     uid_t uid;
+@@ -332,6 +333,7 @@ int main(int argc, const char *argv[])
+     struct poptOption long_options[] = {
+         POPT_AUTOHELP
+         SSSD_MAIN_OPTS
++        SSSD_LOGGER_OPTS
+         SSSD_SERVER_OPTS(uid, gid)
+         POPT_TABLEEND
+     };
+@@ -359,6 +361,8 @@ int main(int argc, const char *argv[])
+     /* set up things like debug, signals, daemonization, etc... */
+     debug_log_file = "sssd_secrets";
+ 
++    sss_set_logger(opt_logger);
++
+     ret = server_setup("sssd[secrets]", 0, uid, gid, CONFDB_SEC_CONF_ENTRY,
+                        &main_ctx);
+     if (ret != EOK) return 2;
+diff --git a/src/responder/ssh/sshsrv.c b/src/responder/ssh/sshsrv.c
+index 440f0e2b9dc06e3dc52ff96d7207b8a3727865c0..8b0e7cc2d71044d7ab3bd2439041f678ddedb4cd 100644
+--- a/src/responder/ssh/sshsrv.c
++++ b/src/responder/ssh/sshsrv.c
+@@ -177,6 +177,7 @@ int main(int argc, const char *argv[])
+ {
+     int opt;
+     poptContext pc;
++    char *opt_logger = NULL;
+     struct main_context *main_ctx;
+     int ret;
+     uid_t uid;
+@@ -185,6 +186,7 @@ int main(int argc, const char *argv[])
+     struct poptOption long_options[] = {
+         POPT_AUTOHELP
+         SSSD_MAIN_OPTS
++        SSSD_LOGGER_OPTS
+         SSSD_SERVER_OPTS(uid, gid)
+         SSSD_RESPONDER_OPTS
+         POPT_TABLEEND
+@@ -213,6 +215,8 @@ int main(int argc, const char *argv[])
+     /* set up things like debug, signals, daemonization, etc... */
+     debug_log_file = "sssd_ssh";
+ 
++    sss_set_logger(opt_logger);
++
+     ret = server_setup("sssd[ssh]", 0, uid, gid,
+                        CONFDB_SSH_CONF_ENTRY, &main_ctx);
+     if (ret != EOK) {
+diff --git a/src/responder/sudo/sudosrv.c b/src/responder/sudo/sudosrv.c
+index dca70ea4afc0e6df6d1b1864338c7b1091a98fee..19058321a25022d7704556ec0ef79729db3ac1f2 100644
+--- a/src/responder/sudo/sudosrv.c
++++ b/src/responder/sudo/sudosrv.c
+@@ -178,6 +178,7 @@ int main(int argc, const char *argv[])
+ {
+     int opt;
+     poptContext pc;
++    char *opt_logger = NULL;
+     struct main_context *main_ctx;
+     int ret;
+     uid_t uid;
+@@ -186,6 +187,7 @@ int main(int argc, const char *argv[])
+     struct poptOption long_options[] = {
+         POPT_AUTOHELP
+         SSSD_MAIN_OPTS
++        SSSD_LOGGER_OPTS
+         SSSD_SERVER_OPTS(uid, gid)
+         SSSD_RESPONDER_OPTS
+         POPT_TABLEEND
+@@ -214,6 +216,8 @@ int main(int argc, const char *argv[])
+     /* set up things like debug, signals, daemonization, etc... */
+     debug_log_file = "sssd_sudo";
+ 
++    sss_set_logger(opt_logger);
++
+     ret = server_setup("sssd[sudo]", 0, uid, gid, CONFDB_SUDO_CONF_ENTRY,
+                        &main_ctx);
+     if (ret != EOK) {
+diff --git a/src/tests/cmocka/dummy_child.c b/src/tests/cmocka/dummy_child.c
+index bcaa9455037a0604422750bf7cc719a25cef4a99..811cb40490c89c4250401e0d8d3e9d1c277f57af 100644
+--- a/src/tests/cmocka/dummy_child.c
++++ b/src/tests/cmocka/dummy_child.c
+@@ -34,6 +34,7 @@ int main(int argc, const char *argv[])
+ {
+     int opt;
+     int debug_fd = -1;
++    char *opt_logger = NULL;
+     poptContext pc;
+     ssize_t len;
+     ssize_t written;
+@@ -55,6 +56,7 @@ int main(int argc, const char *argv[])
+          _("An open file descriptor for the debug logs"), NULL},
+         {"debug-to-stderr", 0, POPT_ARG_NONE | POPT_ARGFLAG_DOC_HIDDEN, &debug_to_stderr, 0, \
+          _("Send the debug output to stderr directly."), NULL },
++        SSSD_LOGGER_OPTS
+         {"guitar", 0, POPT_ARG_STRING, &guitar, 0, _("Who plays guitar"), NULL },
+         {"drums", 0, POPT_ARG_STRING, &drums, 0, _("Who plays drums"), NULL },
+         POPT_TABLEEND
+@@ -76,6 +78,8 @@ int main(int argc, const char *argv[])
+     }
+     poptFreeContext(pc);
+ 
++    sss_set_logger(opt_logger);
++
+     action = getenv("TEST_CHILD_ACTION");
+     if (action) {
+         if (strcasecmp(action, "check_extra_args") == 0) {
+diff --git a/src/tests/debug-tests.c b/src/tests/debug-tests.c
+index d904d7eb8b5418608023faca0d62067f3106d23b..1446ec0474ab4bf72e66b58831fef59defd7be76 100644
+--- a/src/tests/debug-tests.c
++++ b/src/tests/debug-tests.c
+@@ -343,6 +343,7 @@ START_TEST(test_debug_is_set_single_no_timestamp)
+     debug_microseconds = 0;
+     debug_to_file = 1;
+     debug_prg_name = "sssd";
++    sss_set_logger(sss_logger_str[FILES_LOGGER]);
+ 
+     for (i = 0; i <= 9; i++) {
+         debug_level = levels[i];
+@@ -385,6 +386,8 @@ START_TEST(test_debug_is_set_single_timestamp)
+     debug_microseconds = 0;
+     debug_to_file = 1;
+     debug_prg_name = "sssd";
++    sss_set_logger(sss_logger_str[FILES_LOGGER]);
++
+ 
+     for (i = 0; i <= 9; i++) {
+         debug_level = levels[i];
+@@ -432,6 +435,8 @@ START_TEST(test_debug_is_set_single_timestamp_microseconds)
+     debug_microseconds = 1;
+     debug_to_file = 1;
+     debug_prg_name = "sssd";
++    sss_set_logger(sss_logger_str[FILES_LOGGER]);
++
+ 
+     for (i = 0; i <= 9; i++) {
+         debug_level = levels[i];
+@@ -480,6 +485,8 @@ START_TEST(test_debug_is_notset_no_timestamp)
+     debug_microseconds = 0;
+     debug_to_file = 1;
+     debug_prg_name = "sssd";
++    sss_set_logger(sss_logger_str[FILES_LOGGER]);
++
+ 
+     for (i = 0; i <= 9; i++) {
+         debug_level = all_set & ~levels[i];
+@@ -525,6 +532,8 @@ START_TEST(test_debug_is_notset_timestamp)
+     debug_microseconds = 0;
+     debug_to_file = 1;
+     debug_prg_name = "sssd";
++    sss_set_logger(sss_logger_str[FILES_LOGGER]);
++
+ 
+     for (i = 0; i <= 9; i++) {
+         debug_level = all_set & ~levels[i];
+@@ -570,6 +579,7 @@ START_TEST(test_debug_is_notset_timestamp_microseconds)
+     debug_microseconds = 1;
+     debug_to_file = 1;
+     debug_prg_name = "sssd";
++    sss_set_logger(sss_logger_str[FILES_LOGGER]);
+ 
+     for (i = 0; i <= 9; i++) {
+         debug_level = all_set & ~levels[i];
+diff --git a/src/util/child_common.c b/src/util/child_common.c
+index b300d84bf432608db96de36e04637b5fb115212e..dc070f26446305e07cbb34edd1e4d72db72aedc5 100644
+--- a/src/util/child_common.c
++++ b/src/util/child_common.c
+@@ -676,7 +676,7 @@ static errno_t prepare_child_argv(TALLOC_CTX *mem_ctx,
+         }
+ 
+         if (child_debug_stderr) {
+-            argv[--argc] = talloc_strdup(argv, "--debug-to-stderr");
++            argv[--argc] = talloc_strdup(argv, "--logger=stderr");
+             if (argv[argc] == NULL) {
+                 ret = ENOMEM;
+                 goto fail;
+diff --git a/src/util/debug.c b/src/util/debug.c
+index 4e469447e5ab8aa89cd57bcd6d00269875a12bc6..30801fce7c27b115d1cafd4ed826a57c7d444a72 100644
+--- a/src/util/debug.c
++++ b/src/util/debug.c
+@@ -277,7 +277,7 @@ void sss_vdebug_fn(const char *file,
+     errno_t ret;
+     va_list ap_fallback;
+ 
+-    if (!debug_file && !debug_to_stderr) {
++    if (sss_logger == JOURNALD_LOGGER) {
+         /* If we are not outputting logs to files, we should be sending them
+          * to journald.
+          * NOTE: on modern systems, this is where stdout/stderr will end up
+@@ -470,7 +470,7 @@ int rotate_debug_files(void)
+     int ret;
+     errno_t error;
+ 
+-    if (!debug_to_file) return EOK;
++    if (sss_logger != FILES_LOGGER) return EOK;
+ 
+     do {
+         error = 0;
+diff --git a/src/util/server.c b/src/util/server.c
+index 0046c9737bc0d9aea7be59b4fed5e0f8930ff66e..26b8b27f0e5c8524520a2d4774e90fd6cb32bf7a 100644
+--- a/src/util/server.c
++++ b/src/util/server.c
+@@ -455,7 +455,7 @@ int server_setup(const char *name, int flags,
+     char *conf_db;
+     int ret = EOK;
+     bool dt;
+-    bool dl;
++    bool dl = false;
+     bool dm;
+     struct tevent_signal *tes;
+     struct logrotate_ctx *lctx;
+@@ -637,16 +637,18 @@ int server_setup(const char *name, int flags,
+     }
+ 
+     /* same for debug to file */
+-    dl = (debug_to_file != 0);
+     ret = confdb_get_bool(ctx->confdb_ctx, conf_entry,
+                           CONFDB_SERVICE_DEBUG_TO_FILES,
+-                          dl, &dl);
++                          false, &dl);
+     if (ret != EOK) {
+         DEBUG(SSSDBG_FATAL_FAILURE, "Error reading from confdb (%d) [%s]\n",
+                                      ret, strerror(ret));
+         return ret;
+     }
+-    if (dl) debug_to_file = 1;
++    if (dl) {
++        debug_to_file = 1;
++        sss_set_logger(sss_logger_str[FILES_LOGGER]);
++    }
+ 
+     /* before opening the log file set up log rotation */
+     lctx = talloc_zero(ctx, struct logrotate_ctx);
+@@ -662,7 +664,7 @@ int server_setup(const char *name, int flags,
+     }
+ 
+     /* open log file if told so */
+-    if (debug_to_file) {
++    if (sss_logger == FILES_LOGGER) {
+         ret = open_debug_file();
+         if (ret != EOK) {
+             DEBUG(SSSDBG_FATAL_FAILURE, "Error setting up logging (%d) "
+-- 
+2.13.5
+
diff --git a/SOURCES/0017-UTIL-Fix-a-typo-in-the-tcurl-test-tool.patch b/SOURCES/0017-UTIL-Fix-a-typo-in-the-tcurl-test-tool.patch
deleted file mode 100644
index c51e496..0000000
--- a/SOURCES/0017-UTIL-Fix-a-typo-in-the-tcurl-test-tool.patch
+++ /dev/null
@@ -1,32 +0,0 @@
-From c402799ea8b24d2e382d0ad7a06ee92861852972 Mon Sep 17 00:00:00 2001
-From: Jakub Hrozek <jhrozek@redhat.com>
-Date: Wed, 15 Mar 2017 13:42:03 +0100
-Subject: [PATCH 17/36] UTIL: Fix a typo in the tcurl test tool
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Reviewed-by: Michal Židek <mzidek@redhat.com>
-Reviewed-by: Simo Sorce <simo@redhat.com>
----
- src/tests/tcurl_test_tool.c | 4 ++--
- 1 file changed, 2 insertions(+), 2 deletions(-)
-
-diff --git a/src/tests/tcurl_test_tool.c b/src/tests/tcurl_test_tool.c
-index 35ea979780df47c92ed92bc5bba713faa8909b90..38cea432885c97ca3827c8f158bf7e3ebfc67b31 100644
---- a/src/tests/tcurl_test_tool.c
-+++ b/src/tests/tcurl_test_tool.c
-@@ -204,8 +204,8 @@ int main(int argc, const char *argv[])
-                               urls[i],
-                               headers,
-                               inbufs[i],
--                              10);
--        if (ctx == NULL) {
-+                              5);
-+        if (req == NULL) {
-             DEBUG(SSSDBG_FATAL_FAILURE, "Could not create request\n");
-             talloc_zfree(tool_ctx);
-             return 1;
--- 
-2.9.3
-
diff --git a/SOURCES/0018-SYSTEMD-Replace-parameter-debug-to-files-with-DEBUG_.patch b/SOURCES/0018-SYSTEMD-Replace-parameter-debug-to-files-with-DEBUG_.patch
new file mode 100644
index 0000000..a409fab
--- /dev/null
+++ b/SOURCES/0018-SYSTEMD-Replace-parameter-debug-to-files-with-DEBUG_.patch
@@ -0,0 +1,259 @@
+From 66cdb732268cc8dfd4f0800178d1cc186de83ef7 Mon Sep 17 00:00:00 2001
+From: Lukas Slebodnik <lslebodn@redhat.com>
+Date: Mon, 23 Oct 2017 18:03:46 +0200
+Subject: [PATCH 18/21] SYSTEMD: Replace parameter --debug-to-files with
+ ${DEBUG_LOGGER}
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Users can set variable DEBUG_LOGGER in environment files
+(/etc/sysconfig/sssd or /etc/default/sssd; depending on the distribution)
+to override default logging to files.
+
+e.g.
+DEBUG_LOGGER=--logger=stderr
+DEBUG_LOGGER=--logger=journald
+
+Resolves:
+https://pagure.io/SSSD/sssd/issue/3433
+
+Reviewed-by: Fabiano Fidêncio <fidencio@redhat.com>
+(cherry picked from commit a7277fecf7a65ab6c83b36f009c558cdfbf997d2)
+---
+ Makefile.am                              | 12 +-----------
+ contrib/sssd.spec.in                     |  4 ----
+ src/sysv/systemd/journal.conf.in         |  7 -------
+ src/sysv/systemd/sssd-autofs.service.in  |  3 ++-
+ src/sysv/systemd/sssd-ifp.service.in     |  3 ++-
+ src/sysv/systemd/sssd-kcm.service.in     |  3 ++-
+ src/sysv/systemd/sssd-nss.service.in     |  3 ++-
+ src/sysv/systemd/sssd-pac.service.in     |  3 ++-
+ src/sysv/systemd/sssd-pam.service.in     |  3 ++-
+ src/sysv/systemd/sssd-secrets.service.in |  3 ++-
+ src/sysv/systemd/sssd-ssh.service.in     |  3 ++-
+ src/sysv/systemd/sssd-sudo.service.in    |  3 ++-
+ src/sysv/systemd/sssd.service.in         |  3 ++-
+ 13 files changed, 21 insertions(+), 32 deletions(-)
+ delete mode 100644 src/sysv/systemd/journal.conf.in
+
+diff --git a/Makefile.am b/Makefile.am
+index 41a8f32f4e76fdcbd09ad833161f0bdada19e389..5483375167d99568e8313c9a0488900419be6ec3 100644
+--- a/Makefile.am
++++ b/Makefile.am
+@@ -91,7 +91,7 @@ sssdkcmdatadir = $(datadir)/sssd-kcm
+ deskprofilepath = $(sss_statedir)/deskprofile
+ 
+ if HAVE_SYSTEMD_UNIT
+-ifp_exec_cmd = $(sssdlibexecdir)/sssd_ifp --uid 0 --gid 0 --debug-to-files --dbus-activated
++ifp_exec_cmd = $(sssdlibexecdir)/sssd_ifp --uid 0 --gid 0 --dbus-activated
+ ifp_systemdservice = SystemdService=sssd-ifp.service
+ ifp_restart = Restart=on-failure
+ else
+@@ -4483,10 +4483,6 @@ if BUILD_KCM
+         src/sysv/systemd/sssd-kcm.service \
+         $(NULL)
+ endif
+-if WITH_JOURNALD
+-    systemdconf_DATA += \
+-        src/sysv/systemd/journal.conf
+-endif
+ else
+ if HAVE_SUSE
+     init_SCRIPTS += \
+@@ -4535,7 +4531,6 @@ replace_script = \
+ 
+ EXTRA_DIST += \
+     src/sysv/systemd/sssd.service.in \
+-    src/sysv/systemd/journal.conf.in \
+     src/sysv/systemd/sssd-nss.socket.in \
+     src/sysv/systemd/sssd-nss.service.in \
+     src/sysv/systemd/sssd-pam.socket.in \
+@@ -4585,10 +4580,6 @@ src/sysv/systemd/sssd.service: src/sysv/systemd/sssd.service.in Makefile
+ 	@$(MKDIR_P) src/sysv/systemd/
+ 	$(replace_script)
+ 
+-src/sysv/systemd/journal.conf: src/sysv/systemd/journal.conf.in Makefile
+-	@$(MKDIR_P) src/sysv/systemd/
+-	$(replace_script)
+-
+ src/sysv/systemd/sssd-nss.socket: src/sysv/systemd/sssd-nss.socket.in Makefile
+ 	@$(MKDIR_P) src/sysv/systemd/
+ 	$(replace_script)
+@@ -4924,7 +4915,6 @@ endif
+ 	rm -f $(builddir)/src/sysv/systemd/sssd-secrets.service
+ 	rm -f $(builddir)/src/sysv/systemd/sssd-kcm.socket
+ 	rm -f $(builddir)/src/sysv/systemd/sssd-kcm.service
+-	rm -f $(builddir)/src/sysv/systemd/journal.conf
+ 	rm -f $(builddir)/src/tools/wrappers/sss_debuglevel
+ 
+ CLEANFILES += *.X */*.X */*/*.X
+diff --git a/contrib/sssd.spec.in b/contrib/sssd.spec.in
+index e76b51833d5dfa3207d28add4af1016c00f25e1f..1ee64d5a2a64635984260fceced779f4804e8b31 100644
+--- a/contrib/sssd.spec.in
++++ b/contrib/sssd.spec.in
+@@ -971,10 +971,6 @@ done
+ %attr(711,sssd,sssd) %dir %{_sysconfdir}/sssd
+ %attr(711,sssd,sssd) %dir %{_sysconfdir}/sssd/conf.d
+ %ghost %attr(0600,sssd,sssd) %config(noreplace) %{_sysconfdir}/sssd/sssd.conf
+-%if (0%{?use_systemd} == 1)
+-%attr(755,root,root) %dir %{_sysconfdir}/systemd/system/sssd.service.d
+-%config(noreplace) %{_sysconfdir}/systemd/system/sssd.service.d/journal.conf
+-%endif
+ %dir %{_sysconfdir}/logrotate.d
+ %config(noreplace) %{_sysconfdir}/logrotate.d/sssd
+ %dir %{_sysconfdir}/rwtab.d
+diff --git a/src/sysv/systemd/journal.conf.in b/src/sysv/systemd/journal.conf.in
+deleted file mode 100644
+index 9ce170b4893629792516aab41573adea1fb741f0..0000000000000000000000000000000000000000
+--- a/src/sysv/systemd/journal.conf.in
++++ /dev/null
+@@ -1,7 +0,0 @@
+-[Service]
+-# Uncomment *both* of the following lines to enable debug logging
+-# to go to journald instead of /var/log/sssd. You will need to
+-# run 'systemctl daemon-reload' and then restart the SSSD service
+-# for this to take effect
+-#ExecStart=
+-#ExecStart=@sbindir@/sssd -i
+diff --git a/src/sysv/systemd/sssd-autofs.service.in b/src/sysv/systemd/sssd-autofs.service.in
+index 32ea6e19ca7f9aa65599c0cf296a8c5e73362271..c2dc254c8f3f56cb6ae4dc481781688aa702b102 100644
+--- a/src/sysv/systemd/sssd-autofs.service.in
++++ b/src/sysv/systemd/sssd-autofs.service.in
+@@ -9,8 +9,9 @@ RefuseManualStart=true
+ Also=sssd-autofs.socket
+ 
+ [Service]
++Environment=DEBUG_LOGGER=--logger=files
+ ExecStartPre=-/bin/chown @SSSD_USER@:@SSSD_USER@ @logpath@/sssd_autofs.log
+-ExecStart=@libexecdir@/sssd/sssd_autofs --debug-to-files --socket-activated
++ExecStart=@libexecdir@/sssd/sssd_autofs ${DEBUG_LOGGER} --socket-activated
+ Restart=on-failure
+ User=@SSSD_USER@
+ Group=@SSSD_USER@
+diff --git a/src/sysv/systemd/sssd-ifp.service.in b/src/sysv/systemd/sssd-ifp.service.in
+index 8e7abdb0e8c5ec83f9423c688daf845a16c57e7e..05a9a602b2d27c54a4faa79c58e0ecba90267100 100644
+--- a/src/sysv/systemd/sssd-ifp.service.in
++++ b/src/sysv/systemd/sssd-ifp.service.in
+@@ -5,7 +5,8 @@ After=sssd.service
+ BindsTo=sssd.service
+ 
+ [Service]
++Environment=DEBUG_LOGGER=--logger=files
+ Type=dbus
+ BusName=org.freedesktop.sssd.infopipe
+-ExecStart=@ifp_exec_cmd@
++ExecStart=@ifp_exec_cmd@ ${DEBUG_LOGGER}
+ @ifp_restart@
+diff --git a/src/sysv/systemd/sssd-kcm.service.in b/src/sysv/systemd/sssd-kcm.service.in
+index 1e2bee12dc3bedd17d41b86f91c9b2b52d985c40..92306f97ec73a775739bfdb4454df14956e5e133 100644
+--- a/src/sysv/systemd/sssd-kcm.service.in
++++ b/src/sysv/systemd/sssd-kcm.service.in
+@@ -6,4 +6,5 @@ Documentation=man:sssd-kcm(5)
+ Also=sssd-kcm.socket
+ 
+ [Service]
+-ExecStart=@libexecdir@/sssd/sssd_kcm --uid 0 --gid 0 --debug-to-files
++Environment=DEBUG_LOGGER=--logger=files
++ExecStart=@libexecdir@/sssd/sssd_kcm --uid 0 --gid 0 ${DEBUG_LOGGER}
+diff --git a/src/sysv/systemd/sssd-nss.service.in b/src/sysv/systemd/sssd-nss.service.in
+index 6a29078d5a36dff229e47bf7ce953e46443ce023..fe771ad0fa99968bb1d42037abf2f960271589b1 100644
+--- a/src/sysv/systemd/sssd-nss.service.in
++++ b/src/sysv/systemd/sssd-nss.service.in
+@@ -9,5 +9,6 @@ RefuseManualStart=true
+ Also=sssd-nss.socket
+ 
+ [Service]
+-ExecStart=@libexecdir@/sssd/sssd_nss --debug-to-files --socket-activated
++Environment=DEBUG_LOGGER=--logger=files
++ExecStart=@libexecdir@/sssd/sssd_nss ${DEBUG_LOGGER} --socket-activated
+ Restart=on-failure
+diff --git a/src/sysv/systemd/sssd-pac.service.in b/src/sysv/systemd/sssd-pac.service.in
+index ffbfdec030ba6d5cf75c989854c27bc46b6983a5..dbd25abc476f579c9d8cce171fdeafa06e567610 100644
+--- a/src/sysv/systemd/sssd-pac.service.in
++++ b/src/sysv/systemd/sssd-pac.service.in
+@@ -9,8 +9,9 @@ RefuseManualStart=true
+ Also=sssd-pac.socket
+ 
+ [Service]
++Environment=DEBUG_LOGGER=--logger=files
+ ExecStartPre=-/bin/chown @SSSD_USER@:@SSSD_USER@ @logpath@/sssd_pac.log
+-ExecStart=@libexecdir@/sssd/sssd_pac --debug-to-files --socket-activated
++ExecStart=@libexecdir@/sssd/sssd_pac ${DEBUG_LOGGER} --socket-activated
+ Restart=on-failure
+ User=@SSSD_USER@
+ Group=@SSSD_USER@
+diff --git a/src/sysv/systemd/sssd-pam.service.in b/src/sysv/systemd/sssd-pam.service.in
+index 6dec46f0c5d384c500268dafcd00af894088e0b6..df722d1f3014bf62cc60114c30331424d14f411b 100644
+--- a/src/sysv/systemd/sssd-pam.service.in
++++ b/src/sysv/systemd/sssd-pam.service.in
+@@ -9,8 +9,9 @@ RefuseManualStart=true
+ Also=sssd-pam.socket sssd-pam-priv.socket
+ 
+ [Service]
++Environment=DEBUG_LOGGER=--logger=files
+ ExecStartPre=-/bin/chown @SSSD_USER@:@SSSD_USER@ @logpath@/sssd_pam.log
+-ExecStart=@libexecdir@/sssd/sssd_pam --debug-to-files --socket-activated
++ExecStart=@libexecdir@/sssd/sssd_pam ${DEBUG_LOGGER} --socket-activated
+ Restart=on-failure
+ User=@SSSD_USER@
+ Group=@SSSD_USER@
+diff --git a/src/sysv/systemd/sssd-secrets.service.in b/src/sysv/systemd/sssd-secrets.service.in
+index f45d647677a62900c01c7eb103597f2b1387498c..a7b41e0b16a5fa882546b41047e616fd2140329f 100644
+--- a/src/sysv/systemd/sssd-secrets.service.in
++++ b/src/sysv/systemd/sssd-secrets.service.in
+@@ -6,4 +6,5 @@ Documentation=man:sssd-secrets(5)
+ Also=sssd-secrets.socket
+ 
+ [Service]
+-ExecStart=@libexecdir@/sssd/sssd_secrets --uid 0 --gid 0 --debug-to-files
++Environment=DEBUG_LOGGER=--logger=files
++ExecStart=@libexecdir@/sssd/sssd_secrets --uid 0 --gid 0 ${DEBUG_LOGGER}
+diff --git a/src/sysv/systemd/sssd-ssh.service.in b/src/sysv/systemd/sssd-ssh.service.in
+index 6f233b4854018d79cc0ad9d67d53ebd67a49f7b7..f41249ea0fe19e5044d5d06ba195ab604d8e6a29 100644
+--- a/src/sysv/systemd/sssd-ssh.service.in
++++ b/src/sysv/systemd/sssd-ssh.service.in
+@@ -9,8 +9,9 @@ RefuseManualStart=true
+ Also=sssd-ssh.socket
+ 
+ [Service]
++Environment=DEBUG_LOGGER=--logger=files
+ ExecStartPre=-/bin/chown @SSSD_USER@:@SSSD_USER@ @logpath@/sssd_ssh.log
+-ExecStart=@libexecdir@/sssd/sssd_ssh --debug-to-files --socket-activated
++ExecStart=@libexecdir@/sssd/sssd_ssh ${DEBUG_LOGGER} --socket-activated
+ Restart=on-failure
+ User=@SSSD_USER@
+ Group=@SSSD_USER@
+diff --git a/src/sysv/systemd/sssd-sudo.service.in b/src/sysv/systemd/sssd-sudo.service.in
+index b59bcbcd817c3986d7ee245b1083f90ff5a3775a..da022f768af91e360182fad0ff885fad43ecfdc0 100644
+--- a/src/sysv/systemd/sssd-sudo.service.in
++++ b/src/sysv/systemd/sssd-sudo.service.in
+@@ -9,8 +9,9 @@ RefuseManualStart=true
+ Also=sssd-sudo.socket
+ 
+ [Service]
++Environment=DEBUG_LOGGER=--logger=files
+ ExecStartPre=-/bin/chown @SSSD_USER@:@SSSD_USER@ @logpath@/sssd_sudo.log
+-ExecStart=@libexecdir@/sssd/sssd_sudo --debug-to-files --socket-activated
++ExecStart=@libexecdir@/sssd/sssd_sudo --socket-activated
+ Restart=on-failure
+ User=@SSSD_USER@
+ Group=@SSSD_USER@
+diff --git a/src/sysv/systemd/sssd.service.in b/src/sysv/systemd/sssd.service.in
+index 05cfd3705084dbff8b46fb07e736612612c58b70..cea848fac80303d6fae12dd84316a91dbc60072d 100644
+--- a/src/sysv/systemd/sssd.service.in
++++ b/src/sysv/systemd/sssd.service.in
+@@ -5,8 +5,9 @@ Before=systemd-user-sessions.service nss-user-lookup.target
+ Wants=nss-user-lookup.target
+ 
+ [Service]
++Environment=DEBUG_LOGGER=--logger=files
+ EnvironmentFile=-@environment_file@
+-ExecStart=@sbindir@/sssd -i -f
++ExecStart=@sbindir@/sssd -i ${DEBUG_LOGGER}
+ Type=notify
+ NotifyAccess=main
+ 
+-- 
+2.13.5
+
diff --git a/SOURCES/0018-UTIL-Add-SAFEALIGN_COPY_UINT8_CHECK.patch b/SOURCES/0018-UTIL-Add-SAFEALIGN_COPY_UINT8_CHECK.patch
deleted file mode 100644
index bb11f98..0000000
--- a/SOURCES/0018-UTIL-Add-SAFEALIGN_COPY_UINT8_CHECK.patch
+++ /dev/null
@@ -1,36 +0,0 @@
-From 4aecf8a2d3962d962da1e2f98b0bb3b84a8ae536 Mon Sep 17 00:00:00 2001
-From: Jakub Hrozek <jhrozek@redhat.com>
-Date: Thu, 23 Feb 2017 20:55:05 +0100
-Subject: [PATCH 18/36] UTIL: Add SAFEALIGN_COPY_UINT8_CHECK
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-This macro will be used later in the KCM code
-
-Reviewed-by: Michal Židek <mzidek@redhat.com>
-Reviewed-by: Simo Sorce <simo@redhat.com>
----
- src/util/util_safealign.h | 6 ++++++
- 1 file changed, 6 insertions(+)
-
-diff --git a/src/util/util_safealign.h b/src/util/util_safealign.h
-index 0d9a579cdbfafc30bf2d0a6ad2651c71428ebd93..57f04a17d4a38300b959c1593d756b351ebd89e8 100644
---- a/src/util/util_safealign.h
-+++ b/src/util/util_safealign.h
-@@ -130,6 +130,12 @@ safealign_memcpy(void *dest, const void *src, size_t n, size_t *counter)
-     safealign_memcpy(dest, src, srclen, pctr); \
- } while(0)
- 
-+#define SAFEALIGN_COPY_UINT8_CHECK(dest, src, len, pctr) do { \
-+    if ((*(pctr) + sizeof(uint8_t)) > (len) || \
-+        SIZE_T_OVERFLOW(*(pctr), sizeof(uint8_t))) { return EINVAL; } \
-+    safealign_memcpy(dest, src, sizeof(uint8_t), pctr); \
-+} while(0)
-+
- /* Aliases for backward compatibility. */
- #define SAFEALIGN_SET_VALUE SAFEALIGN_SETMEM_VALUE
- #define SAFEALIGN_SET_INT64 SAFEALIGN_SETMEM_INT64
--- 
-2.9.3
-
diff --git a/SOURCES/0019-SYSTEMD-Add-environment-file-to-responder-service-fi.patch b/SOURCES/0019-SYSTEMD-Add-environment-file-to-responder-service-fi.patch
new file mode 100644
index 0000000..060c43e
--- /dev/null
+++ b/SOURCES/0019-SYSTEMD-Add-environment-file-to-responder-service-fi.patch
@@ -0,0 +1,107 @@
+From cc4f298024b2ef08cc0740ebbaf916bd494ef892 Mon Sep 17 00:00:00 2001
+From: Lukas Slebodnik <lslebodn@redhat.com>
+Date: Tue, 24 Oct 2017 12:15:48 +0200
+Subject: [PATCH 19/21] SYSTEMD: Add environment file to responder service
+ files
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Reviewed-by: Fabiano Fidêncio <fidencio@redhat.com>
+(cherry picked from commit 115145f0fb7507c1b9c5489bc77398d422a66875)
+---
+ src/sysv/systemd/sssd-autofs.service.in | 1 +
+ src/sysv/systemd/sssd-ifp.service.in    | 1 +
+ src/sysv/systemd/sssd-nss.service.in    | 1 +
+ src/sysv/systemd/sssd-pac.service.in    | 1 +
+ src/sysv/systemd/sssd-pam.service.in    | 1 +
+ src/sysv/systemd/sssd-ssh.service.in    | 1 +
+ src/sysv/systemd/sssd-sudo.service.in   | 1 +
+ 7 files changed, 7 insertions(+)
+
+diff --git a/src/sysv/systemd/sssd-autofs.service.in b/src/sysv/systemd/sssd-autofs.service.in
+index c2dc254c8f3f56cb6ae4dc481781688aa702b102..7f920ad66a46bb0785c3f947bc26c15d0e370259 100644
+--- a/src/sysv/systemd/sssd-autofs.service.in
++++ b/src/sysv/systemd/sssd-autofs.service.in
+@@ -10,6 +10,7 @@ Also=sssd-autofs.socket
+ 
+ [Service]
+ Environment=DEBUG_LOGGER=--logger=files
++EnvironmentFile=-@environment_file@
+ ExecStartPre=-/bin/chown @SSSD_USER@:@SSSD_USER@ @logpath@/sssd_autofs.log
+ ExecStart=@libexecdir@/sssd/sssd_autofs ${DEBUG_LOGGER} --socket-activated
+ Restart=on-failure
+diff --git a/src/sysv/systemd/sssd-ifp.service.in b/src/sysv/systemd/sssd-ifp.service.in
+index 05a9a602b2d27c54a4faa79c58e0ecba90267100..f3bf92223ce8847858f57c2bb04b97c858be0ead 100644
+--- a/src/sysv/systemd/sssd-ifp.service.in
++++ b/src/sysv/systemd/sssd-ifp.service.in
+@@ -6,6 +6,7 @@ BindsTo=sssd.service
+ 
+ [Service]
+ Environment=DEBUG_LOGGER=--logger=files
++EnvironmentFile=-@environment_file@
+ Type=dbus
+ BusName=org.freedesktop.sssd.infopipe
+ ExecStart=@ifp_exec_cmd@ ${DEBUG_LOGGER}
+diff --git a/src/sysv/systemd/sssd-nss.service.in b/src/sysv/systemd/sssd-nss.service.in
+index fe771ad0fa99968bb1d42037abf2f960271589b1..c671280f2c8a7f85fd09a72983a21db0c30df3b9 100644
+--- a/src/sysv/systemd/sssd-nss.service.in
++++ b/src/sysv/systemd/sssd-nss.service.in
+@@ -10,5 +10,6 @@ Also=sssd-nss.socket
+ 
+ [Service]
+ Environment=DEBUG_LOGGER=--logger=files
++EnvironmentFile=-@environment_file@
+ ExecStart=@libexecdir@/sssd/sssd_nss ${DEBUG_LOGGER} --socket-activated
+ Restart=on-failure
+diff --git a/src/sysv/systemd/sssd-pac.service.in b/src/sysv/systemd/sssd-pac.service.in
+index dbd25abc476f579c9d8cce171fdeafa06e567610..590449b01223fe799eebb12b63229dfb8f2438f9 100644
+--- a/src/sysv/systemd/sssd-pac.service.in
++++ b/src/sysv/systemd/sssd-pac.service.in
+@@ -10,6 +10,7 @@ Also=sssd-pac.socket
+ 
+ [Service]
+ Environment=DEBUG_LOGGER=--logger=files
++EnvironmentFile=-@environment_file@
+ ExecStartPre=-/bin/chown @SSSD_USER@:@SSSD_USER@ @logpath@/sssd_pac.log
+ ExecStart=@libexecdir@/sssd/sssd_pac ${DEBUG_LOGGER} --socket-activated
+ Restart=on-failure
+diff --git a/src/sysv/systemd/sssd-pam.service.in b/src/sysv/systemd/sssd-pam.service.in
+index df722d1f3014bf62cc60114c30331424d14f411b..f2e938579c7ef4254bb2e05231bfe83d7e20f395 100644
+--- a/src/sysv/systemd/sssd-pam.service.in
++++ b/src/sysv/systemd/sssd-pam.service.in
+@@ -10,6 +10,7 @@ Also=sssd-pam.socket sssd-pam-priv.socket
+ 
+ [Service]
+ Environment=DEBUG_LOGGER=--logger=files
++EnvironmentFile=-@environment_file@
+ ExecStartPre=-/bin/chown @SSSD_USER@:@SSSD_USER@ @logpath@/sssd_pam.log
+ ExecStart=@libexecdir@/sssd/sssd_pam ${DEBUG_LOGGER} --socket-activated
+ Restart=on-failure
+diff --git a/src/sysv/systemd/sssd-ssh.service.in b/src/sysv/systemd/sssd-ssh.service.in
+index f41249ea0fe19e5044d5d06ba195ab604d8e6a29..1c185466dfa8c13804cc980bbbdbc997d4ebe955 100644
+--- a/src/sysv/systemd/sssd-ssh.service.in
++++ b/src/sysv/systemd/sssd-ssh.service.in
+@@ -10,6 +10,7 @@ Also=sssd-ssh.socket
+ 
+ [Service]
+ Environment=DEBUG_LOGGER=--logger=files
++EnvironmentFile=-@environment_file@
+ ExecStartPre=-/bin/chown @SSSD_USER@:@SSSD_USER@ @logpath@/sssd_ssh.log
+ ExecStart=@libexecdir@/sssd/sssd_ssh ${DEBUG_LOGGER} --socket-activated
+ Restart=on-failure
+diff --git a/src/sysv/systemd/sssd-sudo.service.in b/src/sysv/systemd/sssd-sudo.service.in
+index da022f768af91e360182fad0ff885fad43ecfdc0..f13d88107eccd9e80447390c9c0f8940ae933106 100644
+--- a/src/sysv/systemd/sssd-sudo.service.in
++++ b/src/sysv/systemd/sssd-sudo.service.in
+@@ -10,6 +10,7 @@ Also=sssd-sudo.socket
+ 
+ [Service]
+ Environment=DEBUG_LOGGER=--logger=files
++EnvironmentFile=-@environment_file@
+ ExecStartPre=-/bin/chown @SSSD_USER@:@SSSD_USER@ @logpath@/sssd_sudo.log
+ ExecStart=@libexecdir@/sssd/sssd_sudo --socket-activated
+ Restart=on-failure
+-- 
+2.13.5
+
diff --git a/SOURCES/0019-UTIL-Add-utility-macro-cli_creds_get_gid.patch b/SOURCES/0019-UTIL-Add-utility-macro-cli_creds_get_gid.patch
deleted file mode 100644
index 84752b5..0000000
--- a/SOURCES/0019-UTIL-Add-utility-macro-cli_creds_get_gid.patch
+++ /dev/null
@@ -1,34 +0,0 @@
-From 62acd53dc2880b23cfb221ce40537abfb6e011bb Mon Sep 17 00:00:00 2001
-From: Jakub Hrozek <jhrozek@redhat.com>
-Date: Tue, 20 Sep 2016 22:00:27 +0200
-Subject: [PATCH 19/36] UTIL: Add utility macro cli_creds_get_gid()
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-The KCM responder checks the owneship of the ccache based on both UID
-and GID of the peer. In order to reuse the already existing creds
-structure, let's just add a new macro that returns the GID from the
-creds structure.
-
-Reviewed-by: Michal Židek <mzidek@redhat.com>
-Reviewed-by: Simo Sorce <simo@redhat.com>
----
- src/util/util_creds.h | 1 +
- 1 file changed, 1 insertion(+)
-
-diff --git a/src/util/util_creds.h b/src/util/util_creds.h
-index 65468fa12b8c6921859574c40f5759c936a10e86..936b9965d1ccd2b437d93b38d789b6f8389f47a6 100644
---- a/src/util/util_creds.h
-+++ b/src/util/util_creds.h
-@@ -71,6 +71,7 @@ struct cli_creds {
- };
- 
- #define cli_creds_get_uid(x) x->ucred.uid
-+#define cli_creds_get_gid(x) x->ucred.gid
- 
- #else /* not HAVE_UCRED */
- struct cli_creds {
--- 
-2.9.3
-
diff --git a/SOURCES/0020-UTIL-Add-type-specific-getsetters-to-sss_iobuf.patch b/SOURCES/0020-UTIL-Add-type-specific-getsetters-to-sss_iobuf.patch
deleted file mode 100644
index 97c4775..0000000
--- a/SOURCES/0020-UTIL-Add-type-specific-getsetters-to-sss_iobuf.patch
+++ /dev/null
@@ -1,194 +0,0 @@
-From cef2ade5294bd9dc06f7eca26287c2e90e2c2ee1 Mon Sep 17 00:00:00 2001
-From: Jakub Hrozek <jhrozek@redhat.com>
-Date: Thu, 23 Feb 2017 21:57:13 +0100
-Subject: [PATCH 20/36] UTIL: Add type-specific getsetters to sss_iobuf
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-The KCM responder receives its input as unstructured data. To make the
-parsing easier, this commit adds several type-specific getsetters to the
-iobuf module.
-
-Reviewed-by: Michal Židek <mzidek@redhat.com>
-Reviewed-by: Simo Sorce <simo@redhat.com>
----
- src/util/sss_iobuf.c | 108 +++++++++++++++++++++++++++++++++++++++++++++++++++
- src/util/sss_iobuf.h |  33 ++++++++++++++++
- 2 files changed, 141 insertions(+)
-
-diff --git a/src/util/sss_iobuf.c b/src/util/sss_iobuf.c
-index 900418b750a3455ebc2c3bb1893db726692260b8..fc288d2df2bfaaba393dd490d4da8976de804cb5 100644
---- a/src/util/sss_iobuf.c
-+++ b/src/util/sss_iobuf.c
-@@ -184,6 +184,25 @@ errno_t sss_iobuf_read(struct sss_iobuf *iobuf,
-     return EOK;
- }
- 
-+errno_t sss_iobuf_read_len(struct sss_iobuf *iobuf,
-+                           size_t len,
-+                           uint8_t *_buf)
-+{
-+    size_t read;
-+    errno_t ret;
-+
-+    ret = sss_iobuf_read(iobuf, len, _buf, &read);
-+    if (ret != EOK) {
-+        return ret;
-+    }
-+
-+    if (read != len) {
-+        return ENOBUFS;
-+    }
-+
-+    return EOK;
-+}
-+
- errno_t sss_iobuf_write_len(struct sss_iobuf *iobuf,
-                             uint8_t *buf,
-                             size_t len)
-@@ -203,3 +222,92 @@ errno_t sss_iobuf_write_len(struct sss_iobuf *iobuf,
- 
-     return EOK;
- }
-+
-+errno_t sss_iobuf_read_uint32(struct sss_iobuf *iobuf,
-+                              uint32_t *_val)
-+{
-+    SAFEALIGN_COPY_UINT32_CHECK(_val, iobuf_ptr(iobuf),
-+                                iobuf->capacity, &iobuf->dp);
-+    return EOK;
-+}
-+
-+errno_t sss_iobuf_read_int32(struct sss_iobuf *iobuf,
-+                             int32_t *_val)
-+{
-+    SAFEALIGN_COPY_INT32_CHECK(_val, iobuf_ptr(iobuf),
-+                               iobuf->capacity, &iobuf->dp);
-+    return EOK;
-+}
-+
-+errno_t sss_iobuf_write_uint32(struct sss_iobuf *iobuf,
-+                               uint32_t val)
-+{
-+    errno_t ret;
-+
-+    ret = ensure_bytes(iobuf, sizeof(uint32_t));
-+    if (ret != EOK) {
-+        return ret;
-+    }
-+
-+    SAFEALIGN_SETMEM_UINT32(iobuf_ptr(iobuf), val, &iobuf->dp);
-+    return EOK;
-+}
-+
-+errno_t sss_iobuf_write_int32(struct sss_iobuf *iobuf,
-+                              int32_t val)
-+{
-+    errno_t ret;
-+
-+    ret = ensure_bytes(iobuf, sizeof(int32_t));
-+    if (ret != EOK) {
-+        return ret;
-+    }
-+
-+    SAFEALIGN_SETMEM_INT32(iobuf_ptr(iobuf), val, &iobuf->dp);
-+    return EOK;
-+}
-+
-+errno_t sss_iobuf_read_stringz(struct sss_iobuf *iobuf,
-+                               const char **_out)
-+{
-+    uint8_t *end;
-+    size_t len;
-+
-+    if (iobuf == NULL) {
-+        return EINVAL;
-+    }
-+
-+    if (_out == NULL) {
-+        return EINVAL;
-+    }
-+
-+    *_out = NULL;
-+
-+    end = memchr(iobuf_ptr(iobuf), '\0', sss_iobuf_get_size(iobuf));
-+    if (end == NULL) {
-+        return EINVAL;
-+    }
-+
-+    len = end + 1 - iobuf_ptr(iobuf);
-+    if (sss_iobuf_get_size(iobuf) < len) {
-+        return EINVAL;
-+    }
-+
-+    *_out = (const char *) iobuf_ptr(iobuf);
-+    iobuf->dp += len;
-+    return EOK;
-+}
-+
-+errno_t sss_iobuf_write_stringz(struct sss_iobuf *iobuf,
-+                                const char *str)
-+{
-+    if (iobuf == NULL || str == NULL) {
-+        return EINVAL;
-+    }
-+
-+    SAFEALIGN_MEMCPY_CHECK(iobuf_ptr(iobuf),
-+                           str, strlen(str)+1,
-+                           sss_iobuf_get_size(iobuf),
-+                           &iobuf->dp);
-+    return EOK;
-+}
-diff --git a/src/util/sss_iobuf.h b/src/util/sss_iobuf.h
-index 900faaa212230f72f52e344c085167e80ae2b465..cc3dfd1e98eeb49b979ac321bd0253bffa8a6dff 100644
---- a/src/util/sss_iobuf.h
-+++ b/src/util/sss_iobuf.h
-@@ -96,6 +96,22 @@ errno_t sss_iobuf_read(struct sss_iobuf *iobuf,
-                        size_t *_read);
- 
- /*
-+ * @brief Read an exact number of bytes from an IO buffer
-+ *
-+ * Read exactly len bytes from an IO buffer. If the buffer contains fewer
-+ * bytes than len, ENOBUFS is returned.
-+ *
-+ * @param[in]  iobuf        The IO buffer to read from
-+ * @param[in]  len          The maximum number of bytes to read
-+ * @param[out] _buf         The buffer to read data into from iobuf
-+ *
-+ * @return EOK on success, errno otherwise
-+ */
-+errno_t sss_iobuf_read_len(struct sss_iobuf *iobuf,
-+                           size_t len,
-+                           uint8_t *_buf);
-+
-+/*
-  * @brief Write into an IO buffer
-  *
-  * Attempts to write len bytes into the iobuf. If the capacity is exceeded,
-@@ -115,4 +131,21 @@ errno_t sss_iobuf_write_len(struct sss_iobuf *iobuf,
-                             uint8_t *buf,
-                             size_t len);
- 
-+errno_t sss_iobuf_read_uint32(struct sss_iobuf *iobuf,
-+                              uint32_t *_val);
-+
-+errno_t sss_iobuf_write_uint32(struct sss_iobuf *iobuf,
-+                               uint32_t val);
-+
-+errno_t sss_iobuf_read_int32(struct sss_iobuf *iobuf,
-+                             int32_t *_val);
-+
-+errno_t sss_iobuf_write_int32(struct sss_iobuf *iobuf,
-+                              int32_t val);
-+
-+errno_t sss_iobuf_read_stringz(struct sss_iobuf *iobuf,
-+                               const char **_out);
-+
-+errno_t sss_iobuf_write_stringz(struct sss_iobuf *iobuf,
-+                                const char *str);
- #endif /* __SSS_IOBUF_H_ */
--- 
-2.9.3
-
diff --git a/SOURCES/0020-UTIL-Hide-and-deprecate-parameter-debug-to-files.patch b/SOURCES/0020-UTIL-Hide-and-deprecate-parameter-debug-to-files.patch
new file mode 100644
index 0000000..8ade838
--- /dev/null
+++ b/SOURCES/0020-UTIL-Hide-and-deprecate-parameter-debug-to-files.patch
@@ -0,0 +1,47 @@
+From 65e47ac92a16c57058b955fe9ec2e17b838a37db Mon Sep 17 00:00:00 2001
+From: Lukas Slebodnik <lslebodn@redhat.com>
+Date: Tue, 24 Oct 2017 12:07:46 +0200
+Subject: [PATCH 20/21] UTIL: Hide and deprecate parameter --debug-to-files
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Reviewed-by: Justin Stephenson <jstephen@redhat.com>
+Reviewed-by: Fabiano Fidêncio <fidencio@redhat.com>
+(cherry picked from commit 18a47bcc463c866de6b6b0327e6d85ceb1e0f7d6)
+---
+ src/man/sssd.8.xml | 4 ++++
+ src/util/debug.h   | 2 +-
+ 2 files changed, 5 insertions(+), 1 deletion(-)
+
+diff --git a/src/man/sssd.8.xml b/src/man/sssd.8.xml
+index 0b725628ff93f48f832140dd5dc15b040a8b179f..f2cbe015b844579af98aebd864770bc651dcf4b1 100644
+--- a/src/man/sssd.8.xml
++++ b/src/man/sssd.8.xml
+@@ -90,6 +90,10 @@
+                         log files are stored in <filename>/var/log/sssd</filename> and
+                         there are separate log files for every SSSD service and domain.
+                     </para>
++                    <para>
++                        This option is deprecated. It is replaced by
++                        <option>--logger=files</option>.
++                    </para>
+                 </listitem>
+             </varlistentry>
+             <varlistentry>
+diff --git a/src/util/debug.h b/src/util/debug.h
+index 4adafb7cfc03f7381c4d03071eb44edad04bee00..09f50cc9f3122f02d8ba2092dfb7ee633332de9b 100644
+--- a/src/util/debug.h
++++ b/src/util/debug.h
+@@ -101,7 +101,7 @@ int get_fd_from_debug_file(void);
+ #define SSSD_DEBUG_OPTS \
+         {"debug-level", 'd', POPT_ARG_INT, &debug_level, 0, \
+          _("Debug level"), NULL}, \
+-        {"debug-to-files", 'f', POPT_ARG_NONE, &debug_to_file, 0, \
++        {"debug-to-files", 'f', POPT_ARG_NONE | POPT_ARGFLAG_DOC_HIDDEN, &debug_to_file, 0, \
+          _("Send the debug output to files instead of stderr"), NULL }, \
+         {"debug-to-stderr", 0, POPT_ARG_NONE | POPT_ARGFLAG_DOC_HIDDEN, &debug_to_stderr, 0, \
+          _("Send the debug output to stderr directly."), NULL }, \
+-- 
+2.13.5
+
diff --git a/SOURCES/0021-LDAP-Bind-to-the-LDAP-server-also-in-the-auth.patch b/SOURCES/0021-LDAP-Bind-to-the-LDAP-server-also-in-the-auth.patch
new file mode 100644
index 0000000..6f138f1
--- /dev/null
+++ b/SOURCES/0021-LDAP-Bind-to-the-LDAP-server-also-in-the-auth.patch
@@ -0,0 +1,213 @@
+From 7ebfab326f94e508ce2910c7242a8dd7652ec8a2 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Fabiano=20Fid=C3=AAncio?= <fidencio@redhat.com>
+Date: Wed, 25 Oct 2017 11:25:09 +0200
+Subject: [PATCH 21/21] LDAP: Bind to the LDAP server also in the auth
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+When dealing with id_provider not being the same as auth_provider, SSSD
+has to bind the DN of the user which wants to authenticate with the
+ldap_default_bind_dn and the password provided by the user.
+
+In order to do so, the least intrusive way is just by replacing
+sdap_connect*() functions by sdap_cli_connect*() functions in the LDAP's
+auth module.
+
+The simple change also allowed us to remove some code that is already
+executed as part of sdap_cli_connect*() and some functions had their
+names adapted to reflect better their new purpose.
+
+Resolves:
+https://pagure.io/SSSD/sssd/issue/3451
+
+Signed-off-by: Fabiano Fidêncio <fidencio@redhat.com>
+Reviewed-by: Sumit Bose <sbose@redhat.com>
+(cherry picked from commit add72860c7a7a2c418f4d8b6790b5caeaf7dfb7b)
+---
+ src/providers/ldap/ldap_auth.c | 114 +++++++++--------------------------------
+ 1 file changed, 25 insertions(+), 89 deletions(-)
+
+diff --git a/src/providers/ldap/ldap_auth.c b/src/providers/ldap/ldap_auth.c
+index 00ddd889b6294e457c13218491547b84f1468266..a3b1480aae4272d2e10f105a1eaf3a5816c3487c 100644
+--- a/src/providers/ldap/ldap_auth.c
++++ b/src/providers/ldap/ldap_auth.c
+@@ -619,14 +619,11 @@ struct auth_state {
+     char *dn;
+     enum pwexpire pw_expire_type;
+     void *pw_expire_data;
+-
+-    struct fo_server *srv;
+ };
+ 
+-static struct tevent_req *auth_get_server(struct tevent_req *req);
++static struct tevent_req *auth_connect_send(struct tevent_req *req);
+ static void auth_get_dn_done(struct tevent_req *subreq);
+ static void auth_do_bind(struct tevent_req *req);
+-static void auth_resolve_done(struct tevent_req *subreq);
+ static void auth_connect_done(struct tevent_req *subreq);
+ static void auth_bind_user_done(struct tevent_req *subreq);
+ 
+@@ -659,7 +656,6 @@ static struct tevent_req *auth_send(TALLOC_CTX *memctx,
+     state->ctx = ctx;
+     state->username = username;
+     state->authtok = authtok;
+-    state->srv = NULL;
+     if (try_chpass_service && ctx->chpass_service != NULL &&
+         ctx->chpass_service->name != NULL) {
+         state->sdap_service = ctx->chpass_service;
+@@ -667,7 +663,7 @@ static struct tevent_req *auth_send(TALLOC_CTX *memctx,
+         state->sdap_service = ctx->service;
+     }
+ 
+-    if (!auth_get_server(req)) goto fail;
++    if (!auth_connect_send(req)) goto fail;
+ 
+     return req;
+ 
+@@ -676,75 +672,37 @@ fail:
+     return NULL;
+ }
+ 
+-static struct tevent_req *auth_get_server(struct tevent_req *req)
++static struct tevent_req *auth_connect_send(struct tevent_req *req)
+ {
+-    struct tevent_req *next_req;
++    struct tevent_req *subreq;
+     struct auth_state *state = tevent_req_data(req,
+                                                struct auth_state);
+-
+-     /* NOTE: this call may cause service->uri to be refreshed
+-      * with a new valid server. Do not use service->uri before */
+-    next_req = be_resolve_server_send(state,
+-                                      state->ev,
+-                                      state->ctx->be,
+-                                      state->sdap_service->name,
+-                                      state->srv == NULL ? true : false);
+-    if (!next_req) {
+-        DEBUG(SSSDBG_CRIT_FAILURE, "be_resolve_server_send failed.\n");
+-        return NULL;
+-    }
+-
+-    tevent_req_set_callback(next_req, auth_resolve_done, req);
+-    return next_req;
+-}
+-
+-static void auth_resolve_done(struct tevent_req *subreq)
+-{
+-    struct tevent_req *req = tevent_req_callback_data(subreq,
+-                                                      struct tevent_req);
+-    struct auth_state *state = tevent_req_data(req,
+-                                                    struct auth_state);
+-    int ret;
+     bool use_tls;
+ 
+-    ret = be_resolve_server_recv(subreq, state, &state->srv);
+-    talloc_zfree(subreq);
+-    if (ret) {
+-        /* all servers have been tried and none
+-         * was found good, go offline */
+-        tevent_req_error(req, ETIMEDOUT);
+-        return;
++    /* Check for undocumented debugging feature to disable TLS
++     * for authentication. This should never be used in production
++     * for obvious reasons.
++     */
++    use_tls = !dp_opt_get_bool(state->ctx->opts->basic, SDAP_DISABLE_AUTH_TLS);
++    if (!use_tls) {
++        sss_log(SSS_LOG_ALERT, "LDAP authentication being performed over "
++                               "insecure connection. This should be done "
++                               "for debugging purposes only.");
+     }
+ 
+-    /* Determine whether we need to use TLS */
+-    if (sdap_is_secure_uri(state->ctx->service->uri)) {
+-        DEBUG(SSSDBG_TRACE_INTERNAL,
+-              "[%s] is a secure channel. No need to run START_TLS\n",
+-                  state->ctx->service->uri);
+-        use_tls = false;
+-    } else {
++    subreq = sdap_cli_connect_send(state, state->ev, state->ctx->opts,
++                                   state->ctx->be,
++                                   state->sdap_service, false,
++                                   use_tls ? CON_TLS_ON : CON_TLS_OFF, false);
+ 
+-        /* Check for undocumented debugging feature to disable TLS
+-         * for authentication. This should never be used in production
+-         * for obvious reasons.
+-         */
+-        use_tls = !dp_opt_get_bool(state->ctx->opts->basic, SDAP_DISABLE_AUTH_TLS);
+-        if (!use_tls) {
+-            sss_log(SSS_LOG_ALERT, "LDAP authentication being performed over "
+-                                   "insecure connection. This should be done "
+-                                   "for debugging purposes only.");
+-        }
+-    }
+-
+-    subreq = sdap_connect_send(state, state->ev, state->ctx->opts,
+-                               state->sdap_service->uri,
+-                               state->sdap_service->sockaddr, use_tls);
+-    if (!subreq) {
++    if (subreq == NULL) {
+         tevent_req_error(req, ENOMEM);
+-        return;
++        return NULL;
+     }
+ 
+     tevent_req_set_callback(subreq, auth_connect_done, req);
++
++    return subreq;
+ }
+ 
+ static void auth_connect_done(struct tevent_req *subreq)
+@@ -755,35 +713,13 @@ static void auth_connect_done(struct tevent_req *subreq)
+                                                     struct auth_state);
+     int ret;
+ 
+-    ret = sdap_connect_recv(subreq, state, &state->sh);
++    ret = sdap_cli_connect_recv(subreq, state, NULL, &state->sh, NULL);
+     talloc_zfree(subreq);
+-    if (ret) {
+-        if (state->srv) {
+-            /* mark this server as bad if connection failed */
+-            be_fo_set_port_status(state->ctx->be,
+-                                  state->sdap_service->name,
+-                                  state->srv, PORT_NOT_WORKING);
+-        }
+-
+-        if (auth_get_server(req) == NULL) {
++    if (ret != EOK) {
++        if (auth_connect_send(req) == NULL) {
+             tevent_req_error(req, ENOMEM);
+         }
+         return;
+-    } else if (state->srv) {
+-        be_fo_set_port_status(state->ctx->be, state->sdap_service->name,
+-                              state->srv, PORT_WORKING);
+-    }
+-
+-    /* In case the ID provider is set to proxy, this might be the first
+-     * LDAP operation at all, so we need to set the connection status
+-     */
+-    if (state->sh->connected == false) {
+-        ret = sdap_set_connected(state->sh, state->ev);
+-        if (ret) {
+-            DEBUG(SSSDBG_OP_FAILURE, "Cannot set connected status\n");
+-            tevent_req_error(req, ret);
+-            return;
+-        }
+     }
+ 
+     ret = get_user_dn(state, state->ctx->be->domain,
+@@ -870,7 +806,7 @@ static void auth_bind_user_done(struct tevent_req *subreq)
+         break;
+     case ETIMEDOUT:
+     case ERR_NETWORK_IO:
+-        if (auth_get_server(req) == NULL) {
++        if (auth_connect_send(req) == NULL) {
+             tevent_req_error(req, ENOMEM);
+         }
+         return;
+-- 
+2.13.5
+
diff --git a/SOURCES/0021-UTIL-krb5-principal-un-marshalling.patch b/SOURCES/0021-UTIL-krb5-principal-un-marshalling.patch
deleted file mode 100644
index c088f68..0000000
--- a/SOURCES/0021-UTIL-krb5-principal-un-marshalling.patch
+++ /dev/null
@@ -1,262 +0,0 @@
-From aa309f35905951c6fdd12d286bb3aeeb61a62088 Mon Sep 17 00:00:00 2001
-From: Jakub Hrozek <jhrozek@redhat.com>
-Date: Tue, 20 Sep 2016 22:03:30 +0200
-Subject: [PATCH 21/36] UTIL: krb5 principal (un)marshalling
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-The KCM responder needs to read the contents of the principal blob that
-the Kerberos library sends. Since libkrb5 doesn't export any API to do
-so, we need to implement marshalling and unmarshalling of the principal
-ourselves.
-
-In future, when the KCM server also supports renewals, we will also need
-to unmarshall the credentials, but until that is not really needed, the
-credentials will be stored as a blob.
-
-Reviewed-by: Michal Židek <mzidek@redhat.com>
-Reviewed-by: Simo Sorce <simo@redhat.com>
----
- src/util/sss_krb5.c | 195 ++++++++++++++++++++++++++++++++++++++++++++++++++++
- src/util/sss_krb5.h |   9 +++
- 2 files changed, 204 insertions(+)
-
-diff --git a/src/util/sss_krb5.c b/src/util/sss_krb5.c
-index 4808a7703d07bb4eba91f14a7a515aadaec1774b..d461cf881566af37f31524c16f6a5f1511a5dc89 100644
---- a/src/util/sss_krb5.c
-+++ b/src/util/sss_krb5.c
-@@ -24,6 +24,7 @@
- 
- #include "config.h"
- 
-+#include "util/sss_iobuf.h"
- #include "util/util.h"
- #include "util/sss_krb5.h"
- 
-@@ -1128,3 +1129,197 @@ done:
- 
-     return res;
- }
-+
-+static errno_t iobuf_read_uint32be(struct sss_iobuf *iobuf,
-+                                   uint32_t *_val)
-+{
-+    uint32_t beval;
-+    errno_t ret;
-+
-+    ret = sss_iobuf_read_uint32(iobuf, &beval);
-+    if (ret != EOK) {
-+        return ret;
-+    }
-+
-+    *_val = be32toh(beval);
-+    return EOK;
-+}
-+
-+static errno_t iobuf_write_uint32be(struct sss_iobuf *iobuf,
-+                                    uint32_t val)
-+{
-+    uint32_t beval;
-+
-+    beval = htobe32(val);
-+    return sss_iobuf_write_uint32(iobuf, beval);
-+}
-+
-+static errno_t iobuf_get_len_bytes(TALLOC_CTX *mem_ctx,
-+                                   struct sss_iobuf *iobuf,
-+                                   uint32_t *_nbytes,
-+                                   uint8_t **_bytes)
-+{
-+    errno_t ret;
-+    uint32_t nbytes;
-+    uint8_t *bytes = NULL;
-+
-+    ret = iobuf_read_uint32be(iobuf, &nbytes);
-+    if (ret != EOK) {
-+        return ret;
-+    }
-+
-+    bytes = talloc_zero_size(mem_ctx, nbytes);
-+    if (bytes == NULL) {
-+        return ENOMEM;
-+    }
-+
-+    ret = sss_iobuf_read_len(iobuf, nbytes, bytes);
-+    if (ret != EOK) {
-+        talloc_free(bytes);
-+        return ret;
-+    }
-+
-+    *_bytes = bytes;
-+    *_nbytes = nbytes;
-+    return EOK;
-+}
-+
-+static errno_t get_krb5_data(TALLOC_CTX *mem_ctx,
-+                             struct sss_iobuf *iobuf,
-+                             krb5_data *k5data)
-+{
-+    errno_t ret;
-+    uint32_t nbytes;
-+    uint8_t *bytes = NULL;
-+
-+    ret = iobuf_get_len_bytes(mem_ctx, iobuf,  &nbytes, &bytes);
-+    if (ret != EOK) {
-+        talloc_free(bytes);
-+        return ret;
-+    }
-+
-+    k5data->data = (char *) bytes; /* FIXME - the cast is ugly */
-+    k5data->length = nbytes;
-+    return EOK;
-+}
-+
-+static errno_t set_krb5_data(struct sss_iobuf *iobuf,
-+                             krb5_data *k5data)
-+{
-+    errno_t ret;
-+
-+    ret = iobuf_write_uint32be(iobuf, k5data->length);
-+    if (ret != EOK) {
-+        return ret;
-+    }
-+
-+    if (k5data->length > 0) {
-+        ret = sss_iobuf_write_len(iobuf,
-+                                  (uint8_t *) k5data->data,
-+                                  k5data->length);
-+        if (ret != EOK) {
-+            return ret;
-+        }
-+    }
-+
-+    return EOK;
-+}
-+
-+/* FIXME - it would be nice if Kerberos exported these APIs.. */
-+krb5_error_code sss_krb5_unmarshal_princ(TALLOC_CTX *mem_ctx,
-+                                         struct sss_iobuf *iobuf,
-+                                         krb5_principal *_princ)
-+{
-+    krb5_principal princ = NULL;
-+    krb5_error_code ret;
-+    uint32_t ncomps;
-+
-+    if (iobuf == NULL || _princ == NULL) {
-+        return EINVAL;
-+    }
-+
-+    princ = talloc_zero(mem_ctx, struct krb5_principal_data);
-+    if (princ == NULL) {
-+        return ENOMEM;
-+    }
-+
-+    princ->magic = KV5M_PRINCIPAL;
-+
-+    ret = iobuf_read_uint32be(iobuf, (uint32_t *) &princ->type);
-+    if (ret != EOK) {
-+        goto fail;
-+    }
-+
-+    ret = iobuf_read_uint32be(iobuf, &ncomps);
-+    if (ret != EOK) {
-+        goto fail;
-+    }
-+
-+    if (ncomps > sss_iobuf_get_capacity(iobuf)) {
-+        /* Sanity check to avoid large allocations */
-+        ret = EINVAL;
-+        goto fail;
-+    }
-+
-+    if (ncomps != 0) {
-+        princ->data = talloc_zero_array(princ, krb5_data, ncomps);
-+        if (princ->data == NULL) {
-+            ret = ENOMEM;
-+            goto fail;
-+        }
-+
-+        princ->length = ncomps;
-+    }
-+
-+    ret = get_krb5_data(princ, iobuf, &princ->realm);
-+    if (ret != EOK) {
-+        goto fail;
-+    }
-+
-+    for (size_t i = 0; i < ncomps; i++) {
-+        ret = get_krb5_data(princ->data, iobuf, &princ->data[i]);
-+        if (ret != EOK) {
-+            goto fail;
-+        }
-+    }
-+
-+    *_princ = princ;
-+    return 0;
-+
-+fail:
-+    talloc_free(princ);
-+    return ret;
-+}
-+
-+krb5_error_code sss_krb5_marshal_princ(krb5_principal princ,
-+                                       struct sss_iobuf *iobuf)
-+{
-+    krb5_error_code ret;
-+
-+    if (iobuf == NULL || princ == NULL) {
-+        return EINVAL;
-+    }
-+
-+    ret = iobuf_write_uint32be(iobuf, princ->type);
-+    if (ret != EOK) {
-+        return ret;
-+    }
-+
-+    ret = iobuf_write_uint32be(iobuf, princ->length);
-+    if (ret != EOK) {
-+        return ret;
-+    }
-+
-+    ret = set_krb5_data(iobuf, &princ->realm);
-+    if (ret != EOK) {
-+        return ret;
-+    }
-+
-+    for (int i = 0; i < princ->length; i++) {
-+        ret = set_krb5_data(iobuf, &princ->data[i]);
-+        if (ret != EOK) {
-+            return ret;
-+        }
-+    }
-+    return EOK;
-+}
-diff --git a/src/util/sss_krb5.h b/src/util/sss_krb5.h
-index ac0f6082c75a8878f72346733e592b7575d44089..0d9043be98749b1a21a1b74c68f07298fa27f230 100644
---- a/src/util/sss_krb5.h
-+++ b/src/util/sss_krb5.h
-@@ -32,6 +32,7 @@
- #include <krb5.h>
- #endif
- 
-+#include "util/sss_iobuf.h"
- #include "util/util.h"
- 
- #define KRB5_CHILD_LOG_FILE     "krb5_child"
-@@ -186,4 +187,12 @@ krb5_error_code sss_krb5_kt_have_content(krb5_context context,
-                                          krb5_keytab keytab);
- 
- bool sss_krb5_realm_has_proxy(const char *realm);
-+
-+krb5_error_code sss_krb5_marshal_princ(krb5_principal princ,
-+                                       struct sss_iobuf *iobuf);
-+
-+krb5_error_code sss_krb5_unmarshal_princ(TALLOC_CTX *mem_ctx,
-+                                         struct sss_iobuf *iobuf,
-+                                         krb5_principal *_princ);
-+
- #endif /* __SSS_KRB5_H__ */
--- 
-2.9.3
-
diff --git a/SOURCES/0022-KCM-Initial-responder-build-and-packaging.patch b/SOURCES/0022-KCM-Initial-responder-build-and-packaging.patch
deleted file mode 100644
index c2ba5a6..0000000
--- a/SOURCES/0022-KCM-Initial-responder-build-and-packaging.patch
+++ /dev/null
@@ -1,1013 +0,0 @@
-From 8cb263f039da9e616e907d25701593dca22b11ed Mon Sep 17 00:00:00 2001
-From: Jakub Hrozek <jhrozek@redhat.com>
-Date: Mon, 1 Aug 2016 12:52:07 +0200
-Subject: [PATCH 22/36] KCM: Initial responder build and packaging
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Adds the initial build of the Kerberos Cache Manager responder (KCM).
-
-This is a deamon that is capable of holding and storing Kerberos
-ccaches. When KCM is used, the kerberos libraries (invoked through e.g.
-kinit) are referred to as a 'client' and the KCM deamon is referred to
-as 'server'.
-
-At the moment, only the Heimdal implementation of Kerberos implements the
-KCM server:
-    https://www.h5l.org/manual/HEAD/info/heimdal/Credential-cache-server-_002d-KCM.html
-This patch adds a KCM server to SSSD.
-
-In MIT, only the 'client-side' support was added:
-    http://k5wiki.kerberos.org/wiki/Projects/KCM_client
-This page also describes the protocol between the client and the server.
-
-The client is capable of talking to the server over either UNIX sockets
-(Linux, most Unixes) or Mach RPC (macOS). Our server only implements the
-UNIX sockets way and should be socket-activated by systemd, although can
-in theory be also ran explicitly.
-
-The KCM server only builds if the configuration option "--with-kcm" is
-enabled. It is packaged in a new subpackage sssd-kcm in order to allow
-distributions to enable the KCM credential caches by installing this
-subpackage only, without the rest of the SSSD. The sssd-kcm subpackage
-also includes a krb5.conf.d snippet that allows the admin to just uncomment
-the KCM defaults and instructs them to start the socket.
-
-The server can be configured in sssd.conf in the "[kcm]" section.
-By default, the server only listens on the same socket path the Heimdal
-server uses, which is "/var/run/.heim_org.h5l.kcm-socket". This is,
-however, configurable.
-
-The file src/responder/kcm/kcm.h is more or less directly imported from
-the MIT Kerberos tree, with an additional sentinel code and some
-comments. Not all KCM operations are implemented, only those that also
-the MIT client implements. That said, this KCM server should also be
-usable with a Heimdal client, although no special testing was with this
-hybrid.
-
-The patch also adds several error codes that will be used in later
-patches.
-
-Related to:
-    https://pagure.io/SSSD/sssd/issue/2887
-
-Reviewed-by: Michal Židek <mzidek@redhat.com>
-Reviewed-by: Simo Sorce <simo@redhat.com>
----
- Makefile.am                          |  53 ++++++++
- configure.ac                         |  10 +-
- contrib/kcm_default_ccache           |  12 ++
- contrib/sssd.spec.in                 |  41 ++++++
- src/conf_macros.m4                   |  16 +++
- src/confdb/confdb.h                  |   3 +
- src/config/cfg_rules.ini             |  19 +++
- src/external/libcurl.m4              |   6 +-
- src/responder/kcm/kcm.c              | 254 +++++++++++++++++++++++++++++++++++
- src/responder/kcm/kcm.h              |  97 +++++++++++++
- src/responder/kcm/kcmsrv_cmd.c       |  65 +++++++++
- src/responder/kcm/kcmsrv_pvt.h       |  58 ++++++++
- src/sysv/systemd/sssd-kcm.service.in |   9 ++
- src/sysv/systemd/sssd-kcm.socket.in  |  10 ++
- src/util/util_errors.c               |   5 +
- src/util/util_errors.h               |   5 +
- 16 files changed, 658 insertions(+), 5 deletions(-)
- create mode 100644 contrib/kcm_default_ccache
- create mode 100644 src/responder/kcm/kcm.c
- create mode 100644 src/responder/kcm/kcm.h
- create mode 100644 src/responder/kcm/kcmsrv_cmd.c
- create mode 100644 src/responder/kcm/kcmsrv_pvt.h
- create mode 100644 src/sysv/systemd/sssd-kcm.service.in
- create mode 100644 src/sysv/systemd/sssd-kcm.socket.in
-
-diff --git a/Makefile.am b/Makefile.am
-index 7516338bc6fd95045d20db8155a0c82fd7003358..4248536e90370c1aab59549a9c18408ef314e6d4 100644
---- a/Makefile.am
-+++ b/Makefile.am
-@@ -87,6 +87,7 @@ sudolibdir = @sudolibpath@
- polkitdir = @polkitdir@
- pamconfdir = $(sysconfdir)/pam.d
- systemtap_tapdir = @tapset_dir@
-+krb5sysincludedir = $(sysconfdir)/krb5.conf.d
- 
- if HAVE_SYSTEMD_UNIT
- ifp_exec_cmd = $(sssdlibexecdir)/sssd_ifp --uid 0 --gid 0 --debug-to-files --dbus-activated
-@@ -186,6 +187,11 @@ endif
- if BUILD_SECRETS
- sssdlibexec_PROGRAMS += sssd_secrets
- endif
-+if BUILD_KCM
-+sssdlibexec_PROGRAMS += sssd_kcm
-+dist_krb5sysinclude_DATA = contrib/kcm_default_ccache
-+endif
-+
- 
- if BUILD_PAC_RESPONDER
-     sssdlibexec_PROGRAMS += sssd_pac
-@@ -703,6 +709,8 @@ dist_noinst_HEADERS = \
-     src/responder/secrets/secsrv_private.h \
-     src/responder/secrets/secsrv_local.h \
-     src/responder/secrets/secsrv_proxy.h \
-+    src/responder/kcm/kcm.h \
-+    src/responder/kcm/kcmsrv_pvt.h \
-     src/sbus/sbus_client.h \
-     src/sbus/sssd_dbus.h \
-     src/sbus/sssd_dbus_meta.h \
-@@ -1476,6 +1484,24 @@ sssd_secrets_LDADD = \
-     $(NULL)
- endif
- 
-+if BUILD_KCM
-+sssd_kcm_SOURCES = \
-+    src/responder/kcm/kcm.c \
-+    src/responder/kcm/kcmsrv_cmd.c \
-+    src/util/sss_sockets.c \
-+    $(SSSD_RESPONDER_OBJ) \
-+    $(NULL)
-+sssd_kcm_CFLAGS = \
-+    $(AM_CFLAGS) \
-+    $(KRB5_CFLAGS) \
-+    $(NULL)
-+sssd_kcm_LDADD = \
-+    $(KRB5_LIBS) \
-+    $(SSSD_LIBS) \
-+    $(SSSD_INTERNAL_LTLIBS) \
-+    $(NULL)
-+endif
-+
- sssd_be_SOURCES = \
-     src/providers/data_provider_be.c \
-     src/providers/data_provider_req.c \
-@@ -4259,6 +4285,12 @@ if BUILD_SUDO
-         src/sysv/systemd/sssd-sudo.service \
-         $(NULL)
- endif
-+if BUILD_KCM
-+    systemdunit_DATA += \
-+        src/sysv/systemd/sssd-kcm.socket \
-+        src/sysv/systemd/sssd-kcm.service \
-+        $(NULL)
-+endif
- if WITH_JOURNALD
-     systemdconf_DATA += \
-         src/sysv/systemd/journal.conf
-@@ -4350,6 +4382,12 @@ EXTRA_DIST += \
-     src/sysv/systemd/sssd-sudo.service.in \
-     $(NULL)
- endif
-+if BUILD_KCM
-+EXTRA_DIST += \
-+    src/sysv/systemd/sssd-kcm.socket.in \
-+    src/sysv/systemd/sssd-kcm.service.in \
-+    $(NULL)
-+endif
- 
- src/sysv/systemd/sssd.service: src/sysv/systemd/sssd.service.in Makefile
- 	@$(MKDIR_P) src/sysv/systemd/
-@@ -4433,6 +4471,16 @@ src/sysv/systemd/sssd-sudo.service: src/sysv/systemd/sssd-sudo.service.in Makefi
- 	$(replace_script)
- endif
- 
-+if BUILD_KCM
-+src/sysv/systemd/sssd-kcm.socket: src/sysv/systemd/sssd-kcm.socket.in Makefile
-+	@$(MKDIR_P) src/sysv/systemd/
-+	$(replace_script)
-+
-+src/sysv/systemd/sssd-kcm.service: src/sysv/systemd/sssd-kcm.service.in Makefile
-+	@$(MKDIR_P) src/sysv/systemd/
-+	$(replace_script)
-+endif
-+
- SSSD_USER_DIRS = \
-     $(DESTDIR)$(dbpath) \
-     $(DESTDIR)$(keytabdir) \
-@@ -4596,6 +4644,9 @@ install-data-hook:
- if BUILD_SAMBA
- 	mv $(DESTDIR)/$(winbindplugindir)/winbind_idmap_sss.so $(DESTDIR)/$(winbindplugindir)/sss.so
- endif
-+if BUILD_KCM
-+	$(MKDIR_P) $(DESTDIR)/$(krb5sysincludedir)
-+endif
- 
- uninstall-hook:
- 	if [ -f $(abs_builddir)/src/config/.files2 ]; then \
-@@ -4670,6 +4721,8 @@ endif
- 	rm -f $(builddir)/src/sysv/systemd/sssd-sudo.service
- 	rm -f $(builddir)/src/sysv/systemd/sssd-secrets.socket
- 	rm -f $(builddir)/src/sysv/systemd/sssd-secrets.service
-+	rm -f $(builddir)/src/sysv/systemd/sssd-kcm.socket
-+	rm -f $(builddir)/src/sysv/systemd/sssd-kcm.service
- 	rm -f $(builddir)/src/sysv/systemd/journal.conf
- 
- CLEANFILES += *.X */*.X */*/*.X
-diff --git a/configure.ac b/configure.ac
-index dd1012015a5fea9f25e5b5199b4868fbc0bc14c4..c363d48a806cc1998e85779a92b6b59b0e2a5c9c 100644
---- a/configure.ac
-+++ b/configure.ac
-@@ -155,6 +155,7 @@ WITH_SSSD_USER
- SSSD_RUNSTATEDIR
- WITH_SECRETS
- WITH_SECRETS_DB_PATH
-+WITH_KCM
- 
- m4_include([src/external/pkg.m4])
- m4_include([src/external/libpopt.m4])
-@@ -193,13 +194,20 @@ m4_include([src/external/libresolv.m4])
- m4_include([src/external/intgcheck.m4])
- m4_include([src/external/systemtap.m4])
- m4_include([src/external/service.m4])
--m4_include([src/external/libcurl.m4])
- 
- if test x$with_secrets = xyes; then
-     m4_include([src/external/libhttp_parser.m4])
-     m4_include([src/external/libjansson.m4])
- fi
- 
-+if test x$with_kcm = xyes; then
-+    m4_include([src/external/libcurl.m4])
-+fi
-+# This variable is defined by external/libcurl.m4, but conditionals
-+# must be always evaluated
-+AM_CONDITIONAL([BUILD_WITH_LIBCURL],
-+               [test x"$have_curlopt_unix_sockpath" = xyes])
-+
- WITH_UNICODE_LIB
- if test x$unicode_lib = xlibunistring; then
-     m4_include([src/external/libunistring.m4])
-diff --git a/contrib/kcm_default_ccache b/contrib/kcm_default_ccache
-new file mode 100644
-index 0000000000000000000000000000000000000000..ac88fca86b60b19f772912b5d9d14595a96d101d
---- /dev/null
-+++ b/contrib/kcm_default_ccache
-@@ -0,0 +1,12 @@
-+# This file should normally be installed by your distribution into a
-+# directory that is included from the Kerberos configuration file (/etc/krb5.conf)
-+# On Fedora/RHEL/CentOS, this is /etc/krb5.conf.d/
-+#
-+# To enable the KCM credential cache, uncomment the following lines and
-+# enable the KCM socket and the service:
-+#   systemctl enable sssd-kcm.socket
-+#   systemctl start sssd-kcm.socket
-+#   systemctl enable sssd-kcm.service
-+
-+#[libdefaults]
-+#    default_ccache_name = KCM:
-diff --git a/contrib/sssd.spec.in b/contrib/sssd.spec.in
-index 28ebe07a26a3112210b092b7831e7f6aae061c8d..5c7c2af521a84ef2ca6cca7b2d6cd1f9b3057056 100644
---- a/contrib/sssd.spec.in
-+++ b/contrib/sssd.spec.in
-@@ -112,6 +112,13 @@
-     %global enable_systemtap_opt --enable-systemtap
- %endif
- 
-+%if (0%{?fedora} >= 23 || 0%{?rhel} >= 7)
-+    %global with_kcm 1
-+    %global with_kcm_option --with-kcm
-+%else
-+    %global with_kcm_option --without-kcm
-+%endif
-+
- Name: @PACKAGE_NAME@
- Version: @PACKAGE_VERSION@
- Release: 0@PRERELEASE_VERSION@%{?dist}
-@@ -677,6 +684,18 @@ Requires: libsss_certmap = %{version}-%{release}
- %description -n libsss_certmap-devel
- Library to map certificates to users based on rules
- 
-+%if (0%{?with_kcm} == 1)
-+%package kcm
-+Summary: An implementation of a Kerberos KCM server
-+Group:  Applications/System
-+License: GPLv3+
-+Requires: sssd-common = %{version}-%{release}
-+
-+%description kcm
-+An implementation of a Kerberos KCM server. Use this package if you want to
-+use the KCM: Kerberos credentials cache.
-+%endif
-+
- %prep
- %setup -q -n %{name}-%{version}
- 
-@@ -706,6 +725,7 @@ autoreconf -ivf
-     %{?with_python3_option} \
-     %{?enable_polkit_rules_option} \
-     %{?enable_systemtap_opt} \
-+    %{?with_kcm_option} \
-     %{?experimental}
- 
- make %{?_smp_mflags} all
-@@ -1178,6 +1198,15 @@ done
- %{_libdir}/libsss_certmap.so
- %{_libdir}/pkgconfig/sss_certmap.pc
- 
-+%if (0%{?with_kcm} == 1)
-+%files kcm
-+%{_libexecdir}/%{servicename}/sssd_kcm
-+%dir %{_sysconfdir}/krb5.conf.d
-+%config(noreplace) %{_sysconfdir}/krb5.conf.d/kcm_default_ccache
-+%{_unitdir}/sssd-kcm.socket
-+%{_unitdir}/sssd-kcm.service
-+%endif
-+
- %pre common
- getent group sssd >/dev/null || groupadd -r sssd
- getent passwd sssd >/dev/null || useradd -r -g sssd -d / -s /sbin/nologin -c "User for sssd" sssd
-@@ -1274,6 +1303,18 @@ fi
- 
- %postun -n libsss_simpleifp -p /sbin/ldconfig
- 
-+%if (0%{?with_kcm} == 1)
-+%post kcm
-+%systemd_post sssd-kcm.socket
-+
-+%preun kcm
-+%systemd_preun sssd-kcm.socket
-+
-+%postun kcm
-+%systemd_postun_with_restart sssd-kcm.socket
-+%systemd_postun_with_restart sssd-kcm.service
-+%endif
-+
- %changelog
- * Mon Mar 15 2010 Stephen Gallagher <sgallagh@redhat.com> - @PACKAGE_VERSION@-0@PRERELEASE_VERSION@
- - Automated build of the SSSD
-diff --git a/src/conf_macros.m4 b/src/conf_macros.m4
-index 749e7694f4dd7086468e461194ef274be2094236..420997229cb3c244afd8fb21b074e43a21de0eda 100644
---- a/src/conf_macros.m4
-+++ b/src/conf_macros.m4
-@@ -887,6 +887,22 @@ AC_DEFUN([WITH_SECRETS],
-     AM_CONDITIONAL([BUILD_SECRETS], [test x"$with_secrets" = xyes])
-   ])
- 
-+AC_DEFUN([WITH_KCM],
-+  [ AC_ARG_WITH([kcm],
-+                [AC_HELP_STRING([--with-kcm],
-+                                [Whether to build with KCM server support [yes]]
-+                               )
-+                ],
-+                [with_kcm=$withval],
-+                with_kcm=yes
-+               )
-+
-+    if test x"$with_kcm" = xyes; then
-+        AC_DEFINE(BUILD_KCM, 1, [whether to build with KCM server support])
-+    fi
-+    AM_CONDITIONAL([BUILD_KCM], [test x"$with_kcm" = xyes])
-+  ])
-+
- AC_DEFUN([WITH_SECRETS_DB_PATH],
-   [ AC_ARG_WITH([secrets-db-path],
-                 [AC_HELP_STRING([--with-secrets-db-path=PATH],
-diff --git a/src/confdb/confdb.h b/src/confdb/confdb.h
-index c05b1cee45ece748bf8e2b1e1ecf3dc28979e48b..c443e869a7a6782265b42c4ad122867c4e3dd8e0 100644
---- a/src/confdb/confdb.h
-+++ b/src/confdb/confdb.h
-@@ -231,6 +231,9 @@
- #define CONFDB_SEC_MAX_SECRETS "max_secrets"
- #define CONFDB_SEC_MAX_PAYLOAD_SIZE "max_payload_size"
- 
-+/* KCM Service */
-+#define CONFDB_KCM_CONF_ENTRY "config/kcm"
-+#define CONFDB_KCM_SOCKET "socket_path"
- 
- struct confdb_ctx;
- struct config_file_ctx;
-diff --git a/src/config/cfg_rules.ini b/src/config/cfg_rules.ini
-index c287328828cae2f0ad8a5a105f1c2b3e05353021..5e789c51658c51c0af1338d23d6c0f30f40bf119 100644
---- a/src/config/cfg_rules.ini
-+++ b/src/config/cfg_rules.ini
-@@ -9,6 +9,7 @@ section = ssh
- section = pac
- section = ifp
- section = secrets
-+section = kcm
- section_re = ^secrets/users/[0-9]\+$
- section_re = ^domain/.*$
- 
-@@ -262,6 +263,24 @@ option = forward_headers
- option = username
- option = password
- 
-+# KCM responder
-+[rule/allowed_kcm_options]
-+validator = ini_allowed_options
-+section_re = ^kcm$
-+
-+option = timeout
-+option = debug
-+option = debug_level
-+option = debug_timestamps
-+option = debug_microseconds
-+option = debug_to_files
-+option = command
-+option = reconnection_retries
-+option = fd_limit
-+option = client_idle_timeout
-+option = description
-+option = socket_path
-+
- [rule/allowed_domain_options]
- validator = ini_allowed_options
- section_re = ^domain/.*$
-diff --git a/src/external/libcurl.m4 b/src/external/libcurl.m4
-index 3bc303ca4e1dea8a04117e32b8c4466b80d885b1..b420b04ad806bd1251f086b773ffe480d39f8bd3 100644
---- a/src/external/libcurl.m4
-+++ b/src/external/libcurl.m4
-@@ -9,8 +9,8 @@ AS_IF([test x$enable_libcurl = xyes],
-       [PKG_CHECK_MODULES([CURL],
-                          [libcurl],
-                          [found_libcurl=yes],
--                         [AC_MSG_WARN([
--The libcurl development library was not found. Some features will be disabled.])
-+                         [AC_MSG_ERROR([
-+The libcurl development library was not found.])
-       ])])
- 
- AS_IF([test x"$found_libcurl" = xyes],
-@@ -32,7 +32,5 @@ AS_IF([test x"$found_libcurl" = xyes],
- AC_SUBST(CURL_LIBS)
- AC_SUBST(CURL_CFLAGS)
- 
--AM_CONDITIONAL([BUILD_WITH_LIBCURL],
--               [test x"$have_curlopt_unix_sockpath" = xyes])
- AM_COND_IF([BUILD_WITH_LIBCURL],
-            [AC_DEFINE_UNQUOTED(HAVE_LIBCURL, 1, [Build with libcurl support])])
-diff --git a/src/responder/kcm/kcm.c b/src/responder/kcm/kcm.c
-new file mode 100644
-index 0000000000000000000000000000000000000000..90a6999c5e39d48a1a2ea8168d171612a65077d5
---- /dev/null
-+++ b/src/responder/kcm/kcm.c
-@@ -0,0 +1,254 @@
-+/*
-+   SSSD
-+
-+   KCM Server - the mainloop and server setup
-+
-+   Copyright (C) Red Hat, 2016
-+
-+   This program is free software; you can redistribute it and/or modify
-+   it under the terms of the GNU General Public License as published by
-+   the Free Software Foundation; either version 3 of the License, or
-+   (at your option) any later version.
-+
-+   This program is distributed in the hope that it will be useful,
-+   but WITHOUT ANY WARRANTY; without even the implied warranty of
-+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+   GNU General Public License for more details.
-+
-+   You should have received a copy of the GNU General Public License
-+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
-+*/
-+
-+#include "config.h"
-+
-+#include <popt.h>
-+#include <krb5/krb5.h>
-+
-+#include "responder/kcm/kcm.h"
-+#include "responder/kcm/kcmsrv_pvt.h"
-+#include "responder/common/responder.h"
-+#include "util/util.h"
-+
-+#define DEFAULT_KCM_FD_LIMIT 2048
-+
-+#ifndef SSS_KCM_SOCKET_NAME
-+#define SSS_KCM_SOCKET_NAME DEFAULT_KCM_SOCKET_PATH
-+#endif
-+
-+static int kcm_responder_ctx_destructor(void *ptr)
-+{
-+    struct resp_ctx *rctx = talloc_get_type(ptr, struct resp_ctx);
-+
-+    /* mark that we are shutting down the responder, so it is propagated
-+     * into underlying contexts that are freed right before rctx */
-+    DEBUG(SSSDBG_TRACE_FUNC, "Responder is being shut down\n");
-+    rctx->shutting_down = true;
-+
-+    return 0;
-+}
-+
-+static int kcm_get_config(struct kcm_ctx *kctx)
-+{
-+    int ret;
-+    char *sock_name;
-+
-+    ret = confdb_get_int(kctx->rctx->cdb,
-+                         CONFDB_KCM_CONF_ENTRY,
-+                         CONFDB_SERVICE_FD_LIMIT,
-+                         DEFAULT_KCM_FD_LIMIT,
-+                         &kctx->fd_limit);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_FATAL_FAILURE,
-+              "Failed to get file descriptors limit\n");
-+        goto done;
-+    }
-+
-+    ret = confdb_get_int(kctx->rctx->cdb,
-+                         kctx->rctx->confdb_service_path,
-+                         CONFDB_RESPONDER_CLI_IDLE_TIMEOUT,
-+                         CONFDB_RESPONDER_CLI_IDLE_DEFAULT_TIMEOUT,
-+                         &kctx->rctx->client_idle_timeout);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_OP_FAILURE,
-+              "Cannot get the client idle timeout [%d]: %s\n",
-+               ret, strerror(ret));
-+        goto done;
-+    }
-+
-+    /* Ensure that the client timeout is at least ten seconds */
-+    if (kctx->rctx->client_idle_timeout < 10) {
-+        kctx->rctx->client_idle_timeout = 10;
-+    }
-+
-+    ret = confdb_get_string(kctx->rctx->cdb,
-+                            kctx->rctx,
-+                            kctx->rctx->confdb_service_path,
-+                            CONFDB_KCM_SOCKET,
-+                            SSS_KCM_SOCKET_NAME,
-+                            &sock_name);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_OP_FAILURE,
-+              "Cannot get the client idle timeout [%d]: %s\n",
-+               ret, strerror(ret));
-+        goto done;
-+    }
-+    kctx->rctx->sock_name = sock_name;
-+
-+    ret = EOK;
-+
-+done:
-+    return ret;
-+}
-+
-+static int kcm_data_destructor(void *ptr)
-+{
-+    struct kcm_resp_ctx *kcm_data = talloc_get_type(ptr, struct kcm_resp_ctx);
-+
-+    if (kcm_data != NULL) {
-+        krb5_free_context(kcm_data->k5c);
-+    }
-+    return 0;
-+}
-+
-+static struct kcm_resp_ctx *kcm_data_setup(TALLOC_CTX *mem_ctx)
-+{
-+    struct kcm_resp_ctx *kcm_data;
-+    krb5_error_code kret;
-+
-+    kcm_data = talloc_zero(mem_ctx, struct kcm_resp_ctx);
-+    if (kcm_data == NULL) {
-+        DEBUG(SSSDBG_FATAL_FAILURE, "fatal error initializing kcm data\n");
-+        return NULL;
-+    }
-+
-+    kret = krb5_init_context(&kcm_data->k5c);
-+    if (kret != EOK) {
-+        talloc_free(kcm_data);
-+        return NULL;
-+    }
-+    talloc_set_destructor((TALLOC_CTX*)kcm_data, kcm_data_destructor);
-+
-+    return kcm_data;
-+}
-+
-+static int kcm_process_init(TALLOC_CTX *mem_ctx,
-+                            struct tevent_context *ev,
-+                            struct confdb_ctx *cdb)
-+{
-+    struct resp_ctx *rctx;
-+    struct kcm_ctx *kctx;
-+    int ret;
-+
-+    rctx = talloc_zero(mem_ctx, struct resp_ctx);
-+    if (rctx == NULL) {
-+        DEBUG(SSSDBG_FATAL_FAILURE, "fatal error initializing resp_ctx\n");
-+        return ENOMEM;
-+    }
-+    rctx->ev = ev;
-+    rctx->cdb = cdb;
-+    rctx->confdb_service_path = CONFDB_KCM_CONF_ENTRY;
-+    rctx->shutting_down = false;
-+    rctx->lfd = -1;
-+    rctx->priv_lfd = -1;
-+
-+    talloc_set_destructor((TALLOC_CTX*)rctx, kcm_responder_ctx_destructor);
-+
-+    kctx = talloc_zero(rctx, struct kcm_ctx);
-+    if (kctx == NULL) {
-+        DEBUG(SSSDBG_FATAL_FAILURE, "fatal error initializing kcm_ctx\n");
-+        ret = ENOMEM;
-+        goto fail;
-+    }
-+
-+    kctx->rctx = rctx;
-+    kctx->rctx->pvt_ctx = kctx;
-+
-+    ret = kcm_get_config(kctx);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_FATAL_FAILURE, "fatal error getting KCM config\n");
-+        goto fail;
-+    }
-+
-+    kctx->kcm_data = kcm_data_setup(kctx);
-+    if (kctx->kcm_data == NULL) {
-+        DEBUG(SSSDBG_FATAL_FAILURE,
-+              "fatal error initializing responder data\n");
-+        ret = EIO;
-+        goto fail;
-+    }
-+
-+    /* Set up file descriptor limits */
-+    responder_set_fd_limit(kctx->fd_limit);
-+
-+    ret = activate_unix_sockets(rctx, kcm_connection_setup);
-+    if (ret != EOK) goto fail;
-+
-+    DEBUG(SSSDBG_TRACE_FUNC, "KCM Initialization complete\n");
-+
-+    return EOK;
-+
-+fail:
-+    talloc_free(rctx);
-+    return ret;
-+}
-+
-+int main(int argc, const char *argv[])
-+{
-+    int opt;
-+    poptContext pc;
-+    struct main_context *main_ctx;
-+    int ret;
-+    uid_t uid;
-+    gid_t gid;
-+
-+    struct poptOption long_options[] = {
-+        POPT_AUTOHELP
-+        SSSD_MAIN_OPTS
-+        SSSD_SERVER_OPTS(uid, gid)
-+        POPT_TABLEEND
-+    };
-+
-+    /* Set debug level to invalid value so we can deside if -d 0 was used. */
-+    debug_level = SSSDBG_INVALID;
-+
-+    umask(DFL_RSP_UMASK);
-+
-+    pc = poptGetContext(argv[0], argc, argv, long_options, 0);
-+    while((opt = poptGetNextOpt(pc)) != -1) {
-+        switch(opt) {
-+        default:
-+            fprintf(stderr, "\nInvalid option %s: %s\n\n",
-+                  poptBadOption(pc, 0), poptStrerror(opt));
-+            poptPrintUsage(pc, stderr, 0);
-+            return 1;
-+        }
-+    }
-+
-+    poptFreeContext(pc);
-+
-+    DEBUG_INIT(debug_level);
-+
-+    /* set up things like debug, signals, daemonization, etc... */
-+    debug_log_file = "sssd_kcm";
-+
-+    ret = server_setup("sssd[kcm]", 0, uid, gid, CONFDB_KCM_CONF_ENTRY,
-+                       &main_ctx);
-+    if (ret != EOK) return 2;
-+
-+    ret = die_if_parent_died();
-+    if (ret != EOK) {
-+        /* This is not fatal, don't return */
-+        DEBUG(SSSDBG_OP_FAILURE,
-+              "Could not set up to exit when parent process does\n");
-+    }
-+
-+    ret = kcm_process_init(main_ctx,
-+                           main_ctx->event_ctx,
-+                           main_ctx->confdb_ctx);
-+    if (ret != EOK) return 3;
-+
-+    /* loop on main */
-+    server_loop(main_ctx);
-+
-+    return 0;
-+}
-diff --git a/src/responder/kcm/kcm.h b/src/responder/kcm/kcm.h
-new file mode 100644
-index 0000000000000000000000000000000000000000..1ea7e9bbca754dca2eeb72a08830fa2f95713b4f
---- /dev/null
-+++ b/src/responder/kcm/kcm.h
-@@ -0,0 +1,97 @@
-+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
-+/* include/kcm.h - Kerberos cache manager protocol declarations */
-+/*
-+ * Copyright (C) 2014 by the Massachusetts Institute of Technology.
-+ * All rights reserved.
-+ *
-+ * Redistribution and use in source and binary forms, with or without
-+ * modification, are permitted provided that the following conditions
-+ * are met:
-+ *
-+ * * Redistributions of source code must retain the above copyright
-+ *   notice, this list of conditions and the following disclaimer.
-+ *
-+ * * Redistributions in binary form must reproduce the above copyright
-+ *   notice, this list of conditions and the following disclaimer in
-+ *   the documentation and/or other materials provided with the
-+ *   distribution.
-+ *
-+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
-+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
-+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
-+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
-+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
-+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
-+ * OF THE POSSIBILITY OF SUCH DAMAGE.
-+ */
-+
-+#ifndef KCM_H
-+#define KCM_H
-+
-+#define KCM_PROTOCOL_VERSION_MAJOR 2
-+#define KCM_PROTOCOL_VERSION_MINOR 0
-+
-+#define KCM_UUID_LEN 16
-+
-+/* This should ideally be in RUNSTATEDIR, but Heimdal uses a hardcoded
-+ * /var/run, and we need to use the same default path. */
-+#define DEFAULT_KCM_SOCKET_PATH "/var/run/.heim_org.h5l.kcm-socket"
-+#define DEFAULT_KCM_MACH_SERVICE "org.h5l.kcm"
-+
-+/*
-+ * All requests begin with:
-+ *   major version (1 bytes)
-+ *   minor version (1 bytes)
-+ *   opcode (16-bit big-endian)
-+ *
-+ * All replies begin with a 32-bit big-endian reply code.
-+ *
-+ * Parameters are appended to the request or reply with no delimiters.  Flags
-+ * and time offsets are stored as 32-bit big-endian integers.  Names are
-+ * marshalled as zero-terminated strings.  Principals and credentials are
-+ * marshalled in the v4 FILE ccache format.  UUIDs are 16 bytes.  UUID lists
-+ * are not delimited, so nothing can come after them.
-+ */
-+
-+/* Opcodes without comments are currently unused in the MIT client
-+ * implementation. */
-+typedef enum kcm_opcode {
-+    KCM_OP_NOOP,
-+    KCM_OP_GET_NAME,
-+    KCM_OP_RESOLVE,
-+    KCM_OP_GEN_NEW,             /* 0x3                 () -> (name)      */
-+    KCM_OP_INITIALIZE,          /* 0x4      (name, princ) -> ()          */
-+    KCM_OP_DESTROY,             /* 0x4             (name) -> ()          */
-+    KCM_OP_STORE,               /* 0x6       (name, cred) -> ()          */
-+    KCM_OP_RETRIEVE,
-+    KCM_OP_GET_PRINCIPAL,       /* 0x8             (name) -> (princ)     */
-+    KCM_OP_GET_CRED_UUID_LIST,  /* 0x9             (name) -> (uuid, ...) */
-+    KCM_OP_GET_CRED_BY_UUID,    /* 0xa       (name, uuid) -> (cred)      */
-+    KCM_OP_REMOVE_CRED,         /* (name, flags, credtag) -> ()          */
-+    KCM_OP_SET_FLAGS,
-+    KCM_OP_CHOWN,
-+    KCM_OP_CHMOD,
-+    KCM_OP_GET_INITIAL_TICKET,
-+    KCM_OP_GET_TICKET,
-+    KCM_OP_MOVE_CACHE,
-+    KCM_OP_GET_CACHE_UUID_LIST, /* 0x12                () -> (uuid, ...) */
-+    KCM_OP_GET_CACHE_BY_UUID,   /* 0x13            (uuid) -> (name)      */
-+    KCM_OP_GET_DEFAULT_CACHE,   /* 0x14                () -> (name)      */
-+    KCM_OP_SET_DEFAULT_CACHE,   /* 0x15            (name) -> ()          */
-+    KCM_OP_GET_KDC_OFFSET,      /* 0x16            (name) -> (offset)    */
-+    KCM_OP_SET_KDC_OFFSET,      /* 0x17    (name, offset) -> ()          */
-+    KCM_OP_ADD_NTLM_CRED,
-+    KCM_OP_HAVE_NTLM_CRED,
-+    KCM_OP_DEL_NTLM_CRED,
-+    KCM_OP_DO_NTLM_AUTH,
-+    KCM_OP_GET_NTLM_USER_LIST,
-+
-+    KCM_OP_SENTINEL,            /* SSSD addition, not in the MIT header */
-+} kcm_opcode;
-+
-+#endif /* KCM_H */
-diff --git a/src/responder/kcm/kcmsrv_cmd.c b/src/responder/kcm/kcmsrv_cmd.c
-new file mode 100644
-index 0000000000000000000000000000000000000000..e9a03cbd41169c93e00b0630dc1e05e205881ec9
---- /dev/null
-+++ b/src/responder/kcm/kcmsrv_cmd.c
-@@ -0,0 +1,65 @@
-+/*
-+   SSSD
-+
-+   KCM Server - the KCM server request and reply parsing and dispatching
-+
-+   Copyright (C) Red Hat, 2016
-+
-+   This program is free software; you can redistribute it and/or modify
-+   it under the terms of the GNU General Public License as published by
-+   the Free Software Foundation; either version 3 of the License, or
-+   (at your option) any later version.
-+
-+   This program is distributed in the hope that it will be useful,
-+   but WITHOUT ANY WARRANTY; without even the implied warranty of
-+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+   GNU General Public License for more details.
-+
-+   You should have received a copy of the GNU General Public License
-+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
-+*/
-+
-+#include "config.h"
-+#include "util/util.h"
-+#include "responder/common/responder.h"
-+
-+struct kcm_proto_ctx {
-+    void *unused;
-+};
-+
-+static void kcm_fd_handler(struct tevent_context *ev,
-+                           struct tevent_fd *fde,
-+                           uint16_t flags, void *ptr)
-+{
-+    errno_t ret;
-+    struct cli_ctx *cctx = talloc_get_type(ptr, struct cli_ctx);
-+
-+    /* Always reset the idle timer on any activity */
-+    ret = reset_client_idle_timer(cctx);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_CRIT_FAILURE,
-+              "Could not create idle timer for client. "
-+               "This connection may not auto-terminate\n");
-+        /* Non-fatal, continue */
-+    }
-+}
-+
-+int kcm_connection_setup(struct cli_ctx *cctx)
-+{
-+    struct kcm_proto_ctx *protocol_ctx;
-+
-+    protocol_ctx = talloc_zero(cctx, struct kcm_proto_ctx);
-+    if (protocol_ctx == NULL) {
-+        return ENOMEM;
-+    }
-+
-+    cctx->protocol_ctx = protocol_ctx;
-+    cctx->cfd_handler = kcm_fd_handler;
-+    return EOK;
-+}
-+
-+/* Dummy, not used here but required to link to other responder files */
-+struct cli_protocol_version *register_cli_protocol_version(void)
-+{
-+    return NULL;
-+}
-diff --git a/src/responder/kcm/kcmsrv_pvt.h b/src/responder/kcm/kcmsrv_pvt.h
-new file mode 100644
-index 0000000000000000000000000000000000000000..a7c9d062c17f09986d894064176c3a461d396ac0
---- /dev/null
-+++ b/src/responder/kcm/kcmsrv_pvt.h
-@@ -0,0 +1,58 @@
-+/*
-+   SSSD
-+
-+   KCM Server - private header file
-+
-+   Copyright (C) Red Hat, 2016
-+
-+   This program is free software; you can redistribute it and/or modify
-+   it under the terms of the GNU General Public License as published by
-+   the Free Software Foundation; either version 3 of the License, or
-+   (at your option) any later version.
-+
-+   This program is distributed in the hope that it will be useful,
-+   but WITHOUT ANY WARRANTY; without even the implied warranty of
-+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+   GNU General Public License for more details.
-+
-+   You should have received a copy of the GNU General Public License
-+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
-+*/
-+
-+#ifndef __KCMSRV_PVT_H__
-+#define __KCMSRV_PVT_H__
-+
-+#include "config.h"
-+
-+#include <sys/types.h>
-+#include "responder/common/responder.h"
-+
-+/* KCM IO structure */
-+struct kcm_data {
-+    uint8_t *data;
-+    size_t length;
-+};
-+
-+/* To avoid leaking the sssd-specific responder data to other
-+ * modules, the ccache databases and other KCM specific data
-+ * are kept separately
-+ */
-+struct kcm_resp_ctx {
-+    krb5_context k5c;
-+};
-+
-+/* responder context that contains both the responder data,
-+ * like the ccaches and the sssd-specific stuff like the
-+ * generic responder ctx
-+ */
-+struct kcm_ctx {
-+    struct resp_ctx *rctx;
-+    int fd_limit;
-+    char *socket_path;
-+
-+    struct kcm_resp_ctx *kcm_data;
-+};
-+
-+int kcm_connection_setup(struct cli_ctx *cctx);
-+
-+#endif /* __KCMSRV_PVT_H__ */
-diff --git a/src/sysv/systemd/sssd-kcm.service.in b/src/sysv/systemd/sssd-kcm.service.in
-new file mode 100644
-index 0000000000000000000000000000000000000000..1e2bee12dc3bedd17d41b86f91c9b2b52d985c40
---- /dev/null
-+++ b/src/sysv/systemd/sssd-kcm.service.in
-@@ -0,0 +1,9 @@
-+[Unit]
-+Description=SSSD Kerberos Cache Manager
-+Documentation=man:sssd-kcm(5)
-+
-+[Install]
-+Also=sssd-kcm.socket
-+
-+[Service]
-+ExecStart=@libexecdir@/sssd/sssd_kcm --uid 0 --gid 0 --debug-to-files
-diff --git a/src/sysv/systemd/sssd-kcm.socket.in b/src/sysv/systemd/sssd-kcm.socket.in
-new file mode 100644
-index 0000000000000000000000000000000000000000..80ec1c0c8f190e83de0b603df8e90aa49d2ec181
---- /dev/null
-+++ b/src/sysv/systemd/sssd-kcm.socket.in
-@@ -0,0 +1,10 @@
-+[Unit]
-+Description=SSSD Secrets Service responder socket
-+Documentation=man:sssd-kcm(8)
-+Requires=sssd-secrets.socket
-+
-+[Socket]
-+ListenStream=@localstatedir@/run/.heim_org.h5l.kcm-socket
-+
-+[Install]
-+WantedBy=sockets.target
-diff --git a/src/util/util_errors.c b/src/util/util_errors.c
-index 17388c997db5315c2491af1021e75aff07632488..23cfdf9c6116a2c8e569a041e8289b65a112fd08 100644
---- a/src/util/util_errors.c
-+++ b/src/util/util_errors.c
-@@ -40,6 +40,7 @@ struct err_string error_to_str[] = {
-     { "Credentials are expired, old ccache was removed" }, /* ERR_CREDS_EXPIRED_CCACHE */
-     { "Failure setting user credentials"}, /* ERR_CREDS_INVALID */
-     { "No cached credentials available" }, /* ERR_NO_CACHED_CREDS */
-+    { "No matching credentials found" }, /* ERR_NO_MATCHING_CREDS */
-     { "Cached credentials are expired" }, /* ERR_CACHED_CREDS_EXPIRED */
-     { "Authentication Denied" }, /* ERR_AUTH_DENIED */
-     { "Authentication Failed" }, /* ERR_AUTH_FAILED */
-@@ -104,6 +105,10 @@ struct err_string error_to_str[] = {
-     { "The secret payload size is too large" }, /* ERR_SEC_PAYLOAD_SIZE_IS_TOO_LARGE */
-     { "No authentication methode available" }, /* ERR_NO_AUTH_METHOD_AVAILABLE */
-     { "Smartcard authentication not supported" }, /* ERR_SC_AUTH_NOT_SUPPORTED */
-+    { "Malformed input KCM packet" }, /* ERR_KCM_MALFORMED_IN_PKT */
-+    { "KCM operation not implemented" }, /* ERR_KCM_OP_NOT_IMPLEMENTED */
-+    { "End of credential cache reached" }, /* ERR_KCM_CC_END */
-+    { "Credential cache name not allowed" }, /* ERR_KCM_WRONG_CCNAME_FORMAT */
-     { "ERR_LAST" } /* ERR_LAST */
- };
- 
-diff --git a/src/util/util_errors.h b/src/util/util_errors.h
-index 7aacad26084a3a2af6333988f07db865f6a4d299..387d481616db1ed5e22b73fae82632a582fae946 100644
---- a/src/util/util_errors.h
-+++ b/src/util/util_errors.h
-@@ -62,6 +62,7 @@ enum sssd_errors {
-     ERR_CREDS_EXPIRED_CCACHE,
-     ERR_CREDS_INVALID,
-     ERR_NO_CACHED_CREDS,
-+    ERR_NO_MATCHING_CREDS,
-     ERR_CACHED_CREDS_EXPIRED,
-     ERR_AUTH_DENIED,
-     ERR_AUTH_FAILED,
-@@ -126,6 +127,10 @@ enum sssd_errors {
-     ERR_SEC_PAYLOAD_SIZE_IS_TOO_LARGE,
-     ERR_NO_AUTH_METHOD_AVAILABLE,
-     ERR_SC_AUTH_NOT_SUPPORTED,
-+    ERR_KCM_MALFORMED_IN_PKT,
-+    ERR_KCM_OP_NOT_IMPLEMENTED,
-+    ERR_KCM_CC_END,
-+    ERR_KCM_WRONG_CCNAME_FORMAT,
-     ERR_LAST            /* ALWAYS LAST */
- };
- 
--- 
-2.9.3
-
diff --git a/SOURCES/0022-sss_client-create-nss_common.h.patch b/SOURCES/0022-sss_client-create-nss_common.h.patch
new file mode 100644
index 0000000..f695b2b
--- /dev/null
+++ b/SOURCES/0022-sss_client-create-nss_common.h.patch
@@ -0,0 +1,143 @@
+From 22c5575eb442f20230081cc06528d685397c8914 Mon Sep 17 00:00:00 2001
+From: Sumit Bose <sbose@redhat.com>
+Date: Fri, 29 Sep 2017 21:38:54 +0200
+Subject: [PATCH 22/31] sss_client: create nss_common.h
+
+This patch makes sss_nss_getpw_readrep() and sss_nss_getgr_readrep()
+calls which parse SSSD's replies for user and group requests available
+to other components.
+
+Related to https://pagure.io/SSSD/sssd/issue/2478
+
+Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
+(cherry picked from commit 7449b236523409cc8766fb957d6cba051fdfb483)
+---
+ Makefile.am                 |  1 +
+ src/sss_client/nss_common.h | 43 +++++++++++++++++++++++++++++++++++++++++++
+ src/sss_client/nss_group.c  | 10 +++-------
+ src/sss_client/nss_passwd.c | 11 +++--------
+ 4 files changed, 50 insertions(+), 15 deletions(-)
+ create mode 100644 src/sss_client/nss_common.h
+
+diff --git a/Makefile.am b/Makefile.am
+index 5483375167d99568e8313c9a0488900419be6ec3..dc2f4b1857ce5bd376544488348731be29b6b293 100644
+--- a/Makefile.am
++++ b/Makefile.am
+@@ -3623,6 +3623,7 @@ libnss_sss_la_SOURCES = \
+     src/sss_client/nss_services.c \
+     src/sss_client/sss_cli.h \
+     src/sss_client/nss_compat.h \
++    src/sss_client/nss_common.h \
+     src/sss_client/nss_mc_common.c \
+     src/util/io.c \
+     src/util/murmurhash3.c \
+diff --git a/src/sss_client/nss_common.h b/src/sss_client/nss_common.h
+new file mode 100644
+index 0000000000000000000000000000000000000000..e83b4f95a3136b5aa711194a4d37389eebfb607a
+--- /dev/null
++++ b/src/sss_client/nss_common.h
+@@ -0,0 +1,43 @@
++/*
++   SSSD
++
++   Common routines for classical and enhanced NSS interface
++
++   Authors:
++        Sumit Bose <sbose@redhat.com>
++
++   Copyright (C) Red Hat, Inc 2007
++
++   This program is free software; you can redistribute it and/or modify
++   it under the terms of the GNU Lesser General Public License as published by
++   the Free Software Foundation; either version 3 of the License, or
++   (at your option) any later version.
++
++   This program is distributed in the hope that it will be useful,
++   but WITHOUT ANY WARRANTY; without even the implied warranty of
++   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++   GNU Lesser General Public License for more details.
++
++   You should have received a copy of the GNU Lesser General Public License
++   along with this program.  If not, see <http://www.gnu.org/licenses/>.
++*/
++
++
++
++struct sss_nss_pw_rep {
++    struct passwd *result;
++    char *buffer;
++    size_t buflen;
++};
++
++int sss_nss_getpw_readrep(struct sss_nss_pw_rep *pr,
++                          uint8_t *buf, size_t *len);
++
++struct sss_nss_gr_rep {
++    struct group *result;
++    char *buffer;
++    size_t buflen;
++};
++
++int sss_nss_getgr_readrep(struct sss_nss_gr_rep *pr,
++                          uint8_t *buf, size_t *len);
+diff --git a/src/sss_client/nss_group.c b/src/sss_client/nss_group.c
+index 0e686af43aeb84a5938315e3922e9fcf2fef4e83..42fba6242d23fc1d52cfd7be10cf10383010f091 100644
+--- a/src/sss_client/nss_group.c
++++ b/src/sss_client/nss_group.c
+@@ -29,6 +29,7 @@
+ #include <stdbool.h>
+ #include "sss_cli.h"
+ #include "nss_mc.h"
++#include "nss_common.h"
+ 
+ static struct sss_nss_getgrent_data {
+     size_t len;
+@@ -190,14 +191,9 @@ done:
+  *
+  *  FIXME: do we need to pad so that each result is 32 bit aligned ?
+  */
+-struct sss_nss_gr_rep {
+-    struct group *result;
+-    char *buffer;
+-    size_t buflen;
+-};
+ 
+-static int sss_nss_getgr_readrep(struct sss_nss_gr_rep *pr,
+-                                 uint8_t *buf, size_t *len)
++int sss_nss_getgr_readrep(struct sss_nss_gr_rep *pr,
++                          uint8_t *buf, size_t *len)
+ {
+     errno_t ret;
+     size_t i, l, slen, ptmem, pad, dlen, glen;
+diff --git a/src/sss_client/nss_passwd.c b/src/sss_client/nss_passwd.c
+index c43f9bc50f43599b541e97f5a5aa60de036a5cdf..61e2a567e684fbc7664b5d425e81cfa28a98e845 100644
+--- a/src/sss_client/nss_passwd.c
++++ b/src/sss_client/nss_passwd.c
+@@ -28,6 +28,7 @@
+ #include <string.h>
+ #include "sss_cli.h"
+ #include "nss_mc.h"
++#include "nss_common.h"
+ 
+ static struct sss_nss_getpwent_data {
+     size_t len;
+@@ -63,14 +64,8 @@ static void sss_nss_getpwent_data_clean(void) {
+  *  8-X: sequence of 5, 0 terminated, strings (name, passwd, gecos, dir, shell)
+  */
+ 
+-struct sss_nss_pw_rep {
+-    struct passwd *result;
+-    char *buffer;
+-    size_t buflen;
+-};
+-
+-static int sss_nss_getpw_readrep(struct sss_nss_pw_rep *pr,
+-                                 uint8_t *buf, size_t *len)
++int sss_nss_getpw_readrep(struct sss_nss_pw_rep *pr,
++                          uint8_t *buf, size_t *len)
+ {
+     errno_t ret;
+     size_t i, slen, dlen;
+-- 
+2.13.6
+
diff --git a/SOURCES/0023-KCM-request-parsing-and-sending-a-reply.patch b/SOURCES/0023-KCM-request-parsing-and-sending-a-reply.patch
deleted file mode 100644
index 5e59dc2..0000000
--- a/SOURCES/0023-KCM-request-parsing-and-sending-a-reply.patch
+++ /dev/null
@@ -1,578 +0,0 @@
-From aface2604c53db717299ac3dfe798dfd0c540f99 Mon Sep 17 00:00:00 2001
-From: Jakub Hrozek <jhrozek@redhat.com>
-Date: Fri, 23 Sep 2016 14:00:10 +0200
-Subject: [PATCH 23/36] KCM: request parsing and sending a reply
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Implements parsing the KCM client request into per-client buffers and
-sending a response for both the failure case and for success.
-
-The protocol is documented at:
-    http://k5wiki.kerberos.org/wiki/Projects/KCM_client
-
-Several places don't use the sss_iobuf structure, because they don't
-parse variable-length data from the buffer and it's much more efficient
-to just allocate the needed request and reply structure on the stack.
-
-Reviewed-by: Michal Židek <mzidek@redhat.com>
-Reviewed-by: Simo Sorce <simo@redhat.com>
----
- src/responder/kcm/kcmsrv_cmd.c | 467 ++++++++++++++++++++++++++++++++++++++++-
- src/responder/kcm/kcmsrv_pvt.h |  21 +-
- 2 files changed, 474 insertions(+), 14 deletions(-)
-
-diff --git a/src/responder/kcm/kcmsrv_cmd.c b/src/responder/kcm/kcmsrv_cmd.c
-index e9a03cbd41169c93e00b0630dc1e05e205881ec9..cbf70353730d8a4e03d8f75c97395f4ef007e77f 100644
---- a/src/responder/kcm/kcmsrv_cmd.c
-+++ b/src/responder/kcm/kcmsrv_cmd.c
-@@ -19,14 +19,430 @@
-    along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
- 
-+#include <krb5/krb5.h>
-+
- #include "config.h"
- #include "util/util.h"
-+#include "util/sss_iobuf.h"
- #include "responder/common/responder.h"
-+#include "responder/kcm/kcmsrv_pvt.h"
-+#include "responder/kcm/kcm.h"
- 
--struct kcm_proto_ctx {
--    void *unused;
-+/* The first four bytes of a message is always the size */
-+#define KCM_MSG_LEN_SIZE 4
-+
-+/* The return code is 32bits */
-+#define KCM_RETCODE_SIZE 4
-+
-+/* The maximum length of a request or reply as defined by the RPC
-+ * protocol. This is the same constant size as MIT KRB5 uses
-+ */
-+#define KCM_PACKET_MAX_SIZE 2048
-+
-+/* KCM operation, its raw input and raw output and result */
-+struct kcm_op_io {
-+    struct kcm_op *op;
-+    struct kcm_data request;
-+    struct sss_iobuf *reply;
-+};
-+
-+/**
-+ * KCM IO-vector operations
-+ */
-+struct kcm_iovec {
-+    /* We don't use iovec b/c void pointers don't allow for
-+     * pointer arithmetics and it's convenient to keep track
-+     * of processed bytes
-+     */
-+    uint8_t *kiov_base;
-+    size_t kiov_len;
-+    size_t nprocessed;
-+};
-+
-+static errno_t kcm_iovec_op(int fd, struct kcm_iovec *kiov, bool do_read)
-+{
-+    ssize_t len;
-+    struct iovec iov[1];
-+
-+    iov[0].iov_base = kiov->kiov_base + kiov->nprocessed;
-+    iov[0].iov_len = kiov->kiov_len - kiov->nprocessed;
-+    if (iov[0].iov_len == 0) {
-+        /* This iovec is full (read) or depleted (write), proceed to the next one */
-+        return EOK;
-+    }
-+
-+    if (do_read) {
-+        len = readv(fd, iov, 1);
-+    } else {
-+        len = writev(fd, iov, 1);
-+    }
-+
-+    if (len == -1) {
-+        if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) {
-+            return EAGAIN;
-+        } else {
-+            return errno;
-+        }
-+    }
-+
-+    if (len == 0) {
-+        /* Read event on fd that doesn't yield data? error */
-+        return ENODATA;
-+    }
-+
-+    /* Decrease the amount of available free space in the iovec */
-+    kiov->nprocessed += len;
-+    return EOK;
-+}
-+
-+static errno_t kcm_read_iovec(int fd, struct kcm_iovec *kiov)
-+{
-+    return kcm_iovec_op(fd, kiov, true);
-+}
-+
-+static errno_t kcm_write_iovec(int fd, struct kcm_iovec *kiov)
-+{
-+    return kcm_iovec_op(fd, kiov, false);
-+}
-+
-+/**
-+ * Parsing KCM input
-+ *
-+ * The request is received as two IO vectors:
-+ *
-+ * first iovec:
-+ *  length                      32-bit big-endian integer
-+ *
-+ * second iovec:
-+ *  major protocol number       8-bit big-endian integer
-+ *  minor protocol number       8-bit big-endian integer
-+ *  opcode                      16-bit big-endian integer
-+ *  message payload             buffer
-+ */
-+struct kcm_reqbuf {
-+    uint8_t lenbuf[KCM_MSG_LEN_SIZE];
-+    struct kcm_iovec v_len;
-+
-+    /* Includes the major, minor versions etc */
-+    uint8_t msgbuf[KCM_PACKET_MAX_SIZE];
-+    struct kcm_iovec v_msg;
-+};
-+
-+static errno_t kcm_input_parse(struct kcm_reqbuf *reqbuf,
-+                               struct kcm_op_io *op_io)
-+{
-+    size_t lc = 0;
-+    size_t mc = 0;
-+    uint16_t opcode = 0;
-+    uint16_t opcode_be = 0;
-+    uint32_t len_be = 0;
-+    uint32_t msglen;
-+    uint8_t proto_maj = 0;
-+    uint8_t proto_min = 0;
-+
-+    /* The first 4 bytes before the payload is message length */
-+    SAFEALIGN_COPY_UINT32_CHECK(&len_be,
-+                                reqbuf->v_len.kiov_base,
-+                                reqbuf->v_len.kiov_len,
-+                                &lc);
-+    msglen = be32toh(len_be);
-+    DEBUG(SSSDBG_TRACE_LIBS,
-+          "Received message with length %"PRIu32"\n", msglen);
-+
-+    if (msglen == 0) {
-+        DEBUG(SSSDBG_CRIT_FAILURE, "Illegal zero-length message\n");
-+        return EBADMSG;
-+    }
-+
-+    if (msglen != reqbuf->v_msg.nprocessed) {
-+        DEBUG(SSSDBG_CRIT_FAILURE,
-+              "Sender claims the message is %"PRIu32" bytes, "
-+              "but received %zu\n",
-+              msglen, reqbuf->v_msg.nprocessed);
-+        return EBADMSG;
-+    }
-+
-+    /* First 16 bits are 8 bit major and 8bit major protocol version */
-+    SAFEALIGN_COPY_UINT8_CHECK(&proto_maj,
-+                               reqbuf->v_msg.kiov_base + mc,
-+                               reqbuf->v_msg.kiov_len,
-+                               &mc);
-+    SAFEALIGN_COPY_UINT8_CHECK(&proto_min,
-+                               reqbuf->v_msg.kiov_base + mc,
-+                               reqbuf->v_msg.kiov_len,
-+                               &mc);
-+
-+    if (proto_maj != KCM_PROTOCOL_VERSION_MAJOR) {
-+        DEBUG(SSSDBG_CRIT_FAILURE,
-+              "Expected major version %d, got %"PRIu16"\n",
-+              KCM_PROTOCOL_VERSION_MAJOR, (uint16_t) proto_maj);
-+        return ERR_KCM_MALFORMED_IN_PKT;
-+    }
-+
-+    if (proto_min != KCM_PROTOCOL_VERSION_MINOR) {
-+        DEBUG(SSSDBG_CRIT_FAILURE,
-+              "Expected minor version %d, got %"PRIu16"\n",
-+              KCM_PROTOCOL_VERSION_MINOR, (uint16_t) proto_maj);
-+        return ERR_KCM_MALFORMED_IN_PKT;
-+    }
-+
-+    SAFEALIGN_COPY_UINT16_CHECK(&opcode_be,
-+                                reqbuf->v_msg.kiov_base + mc,
-+                                reqbuf->v_msg.kiov_len,
-+                                &mc);
-+
-+    opcode = be16toh(opcode_be);
-+    DEBUG(SSSDBG_TRACE_LIBS, "Received operation code %"PRIu16"\n", opcode);
-+
-+    return EOK;
-+}
-+
-+/**
-+ * Constructing a reply for failure and success
-+ *
-+ * The reply consists of three IO vectors:
-+ * 1) length iovec:
-+ *  length:     32-bit big-endian
-+ *
-+ * 2) return code iovec:
-+ *  retcode:    32-bit big-endian. Non-zero on failure in the KCM server,
-+ *              zero if the KCM operation ran (even if the operation itself
-+ *              failed)
-+ *
-+ * 3) reply iovec
-+ *  message:    buffer, first 32-bits of the buffer is the return code of
-+ *              the KCM operation, the rest depends on the operation itself.
-+ *              The buffer's length is specified by the first integer in the
-+ *              reply (very intuitive, right?)
-+ *
-+ *  The client always reads the length and return code iovectors. However, the
-+ *  client reads the reply iovec only if retcode is 0 in the return code iovector
-+ *  (see kcmio_unix_socket_read() in the MIT tree)
-+ */
-+struct kcm_repbuf {
-+    uint8_t lenbuf[KCM_MSG_LEN_SIZE];
-+    struct kcm_iovec v_len;
-+
-+    uint8_t rcbuf[KCM_RETCODE_SIZE];
-+    struct kcm_iovec v_rc;
-+
-+    uint8_t msgbuf[KCM_PACKET_MAX_SIZE];
-+    struct kcm_iovec v_msg;
-+};
-+
-+static errno_t kcm_failbuf_construct(errno_t ret,
-+                                     struct kcm_repbuf *repbuf)
-+{
-+    size_t c;
-+
-+    c = 0;
-+    SAFEALIGN_SETMEM_UINT32(repbuf->lenbuf, 0, &c);
-+    c = 0;
-+    SAFEALIGN_SETMEM_UINT32(repbuf->rcbuf, htobe32(ret), &c);
-+
-+    return EOK;
-+}
-+
-+/**
-+ * Construct a reply buffer and send it to the KCM client
-+ */
-+static void kcm_reply_error(struct cli_ctx *cctx,
-+                            errno_t retcode,
-+                            struct kcm_repbuf *repbuf)
-+{
-+    errno_t ret;
-+    krb5_error_code kerr;
-+
-+    DEBUG(SSSDBG_OP_FAILURE,
-+          "KCM operation returs failure [%d]: %s\n",
-+          retcode, sss_strerror(retcode));
-+    kerr = sss2krb5_error(retcode);
-+
-+    ret = kcm_failbuf_construct(kerr, repbuf);
-+    if (ret != EOK) {
-+        /* If we can't construct the reply buffer, just terminate the client */
-+        talloc_free(cctx);
-+        return;
-+    }
-+
-+    TEVENT_FD_WRITEABLE(cctx->cfde);
-+}
-+
-+/**
-+ * Request-reply dispatcher
-+ */
-+struct kcm_req_ctx {
-+    /* client context owns per-client buffers including this one */
-+    struct cli_ctx *cctx;
-+
-+    /* raw IO buffers */
-+    struct kcm_reqbuf reqbuf;
-+    struct kcm_repbuf repbuf;
-+
-+    /* long-lived responder structures */
-+    struct kcm_ctx *kctx;
-+
-+    struct kcm_op_io op_io;
- };
- 
-+static errno_t kcm_recv_data(int fd, struct kcm_reqbuf *reqbuf)
-+{
-+    errno_t ret;
-+
-+    ret = kcm_read_iovec(fd, &reqbuf->v_len);
-+    if (ret != EOK) {
-+        return ret;
-+    }
-+
-+    ret = kcm_read_iovec(fd, &reqbuf->v_msg);
-+    if (ret != EOK) {
-+        return ret;
-+    }
-+
-+    return EOK;
-+}
-+
-+static struct kcm_req_ctx *kcm_new_req(TALLOC_CTX *mem_ctx,
-+                                       struct cli_ctx *cctx,
-+                                       struct kcm_ctx *kctx)
-+{
-+    struct kcm_req_ctx *req;
-+
-+    req = talloc_zero(cctx, struct kcm_req_ctx);
-+    if (req == NULL) {
-+        return NULL;
-+    }
-+
-+    req->reqbuf.v_len.kiov_base = req->reqbuf.lenbuf;
-+    req->reqbuf.v_len.kiov_len = KCM_MSG_LEN_SIZE;
-+
-+    req->reqbuf.v_msg.kiov_base = req->reqbuf.msgbuf;
-+    req->reqbuf.v_msg.kiov_len = KCM_PACKET_MAX_SIZE;
-+
-+    req->repbuf.v_len.kiov_base = req->repbuf.lenbuf;
-+    req->repbuf.v_len.kiov_len = KCM_MSG_LEN_SIZE;
-+
-+    req->repbuf.v_rc.kiov_base = req->repbuf.rcbuf;
-+    req->repbuf.v_rc.kiov_len = KCM_RETCODE_SIZE;
-+
-+    req->repbuf.v_msg.kiov_base = req->repbuf.msgbuf;
-+    /* Length of the msg iobuf will be adjusted later, so far use the full
-+     * length so that constructing the reply can use that capacity
-+     */
-+    req->repbuf.v_msg.kiov_len = KCM_PACKET_MAX_SIZE;
-+
-+    req->cctx = cctx;
-+    req->kctx = kctx;
-+
-+    return req;
-+}
-+
-+static void kcm_recv(struct cli_ctx *cctx)
-+{
-+    struct kcm_req_ctx *req;
-+    struct kcm_ctx *kctx;
-+    int ret;
-+
-+    kctx = talloc_get_type(cctx->rctx->pvt_ctx, struct kcm_ctx);
-+    req = talloc_get_type(cctx->state_ctx, struct kcm_req_ctx);
-+    if (req == NULL) {
-+        /* A new request comes in, setup data structures */
-+        req = kcm_new_req(cctx, cctx, kctx);
-+        if (req == NULL) {
-+            DEBUG(SSSDBG_CRIT_FAILURE,
-+                  "Cannot set up client connection\n");
-+            talloc_free(cctx);
-+            return;
-+        }
-+
-+        cctx->state_ctx = req;
-+    }
-+
-+    ret = kcm_recv_data(cctx->cfd, &req->reqbuf);
-+    switch (ret) {
-+    case ENODATA:
-+        DEBUG(SSSDBG_TRACE_ALL, "Client closed connection.\n");
-+        talloc_free(cctx);
-+        return;
-+    case EAGAIN:
-+        DEBUG(SSSDBG_TRACE_ALL, "Retry later\n");
-+        return;
-+    case EOK:
-+        /* all fine */
-+        break;
-+    default:
-+        DEBUG(SSSDBG_FATAL_FAILURE,
-+              "Failed to receive data (%d, %s), aborting client\n",
-+              ret, sss_strerror(ret));
-+        talloc_free(cctx);
-+        return;
-+    }
-+
-+    ret = kcm_input_parse(&req->reqbuf, &req->op_io);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_FATAL_FAILURE,
-+            "Failed to parse data (%d, %s), aborting client\n",
-+            ret, sss_strerror(ret));
-+        goto fail;
-+    }
-+
-+    /* do not read anymore, client is done sending */
-+    TEVENT_FD_NOT_READABLE(cctx->cfde);
-+
-+    kcm_reply_error(cctx, ret, &req->repbuf);
-+    return;
-+
-+fail:
-+    /* Fail with reply */
-+    kcm_reply_error(cctx, ret, &req->repbuf);
-+}
-+
-+static int kcm_send_data(struct cli_ctx *cctx)
-+{
-+    struct kcm_req_ctx *req;
-+    errno_t ret;
-+
-+    req = talloc_get_type(cctx->state_ctx, struct kcm_req_ctx);
-+
-+    ret = kcm_write_iovec(cctx->cfd, &req->repbuf.v_len);
-+    if (ret != EOK) {
-+        return ret;
-+    }
-+
-+    ret = kcm_write_iovec(cctx->cfd, &req->repbuf.v_rc);
-+    if (ret != EOK) {
-+        return ret;
-+    }
-+
-+    ret = kcm_write_iovec(cctx->cfd, &req->repbuf.v_msg);
-+    if (ret != EOK) {
-+        return ret;
-+    }
-+
-+    return EOK;
-+}
-+
-+static void kcm_send(struct cli_ctx *cctx)
-+{
-+    errno_t ret;
-+
-+    ret = kcm_send_data(cctx);
-+    if (ret == EAGAIN) {
-+        /* not all data was sent, loop again */
-+        return;
-+    } else if (ret != EOK) {
-+        DEBUG(SSSDBG_FATAL_FAILURE, "Failed to send data, aborting client!\n");
-+        talloc_free(cctx);
-+        return;
-+    }
-+
-+    /* ok all sent */
-+    TEVENT_FD_NOT_WRITEABLE(cctx->cfde);
-+    TEVENT_FD_READABLE(cctx->cfde);
-+    talloc_zfree(cctx->state_ctx);
-+    return;
-+}
-+
- static void kcm_fd_handler(struct tevent_context *ev,
-                            struct tevent_fd *fde,
-                            uint16_t flags, void *ptr)
-@@ -39,25 +455,54 @@ static void kcm_fd_handler(struct tevent_context *ev,
-     if (ret != EOK) {
-         DEBUG(SSSDBG_CRIT_FAILURE,
-               "Could not create idle timer for client. "
--               "This connection may not auto-terminate\n");
-+              "This connection may not auto-terminate\n");
-         /* Non-fatal, continue */
-     }
-+
-+    if (flags & TEVENT_FD_READ) {
-+        kcm_recv(cctx);
-+        return;
-+    }
-+    if (flags & TEVENT_FD_WRITE) {
-+        kcm_send(cctx);
-+        return;
-+    }
- }
- 
- int kcm_connection_setup(struct cli_ctx *cctx)
- {
--    struct kcm_proto_ctx *protocol_ctx;
--
--    protocol_ctx = talloc_zero(cctx, struct kcm_proto_ctx);
--    if (protocol_ctx == NULL) {
--        return ENOMEM;
--    }
--
--    cctx->protocol_ctx = protocol_ctx;
-     cctx->cfd_handler = kcm_fd_handler;
-     return EOK;
- }
- 
-+krb5_error_code sss2krb5_error(errno_t err)
-+{
-+    switch (err) {
-+    case EOK:
-+        return 0;
-+    case ENOMEM:
-+        return KRB5_CC_NOMEM;
-+    case EACCES:
-+        return KRB5_FCC_PERM;
-+    case ERR_KCM_OP_NOT_IMPLEMENTED:
-+        return KRB5_CC_NOSUPP;
-+    case ERR_WRONG_NAME_FORMAT:
-+        return KRB5_CC_BADNAME;
-+    case ERR_NO_MATCHING_CREDS:
-+        return KRB5_FCC_NOFILE;
-+    case ERR_NO_CREDS:
-+        return KRB5_CC_NOTFOUND;
-+    case ERR_KCM_CC_END:
-+        return KRB5_CC_END;
-+    case ERR_KCM_MALFORMED_IN_PKT:
-+    case EINVAL:
-+    case EIO:
-+        return KRB5_CC_IO;
-+    }
-+
-+    return KRB5_FCC_INTERNAL;
-+}
-+
- /* Dummy, not used here but required to link to other responder files */
- struct cli_protocol_version *register_cli_protocol_version(void)
- {
-diff --git a/src/responder/kcm/kcmsrv_pvt.h b/src/responder/kcm/kcmsrv_pvt.h
-index a7c9d062c17f09986d894064176c3a461d396ac0..fd1fd9fa32d59a323d465def68999f24f84e3923 100644
---- a/src/responder/kcm/kcmsrv_pvt.h
-+++ b/src/responder/kcm/kcmsrv_pvt.h
-@@ -27,13 +27,20 @@
- #include <sys/types.h>
- #include "responder/common/responder.h"
- 
--/* KCM IO structure */
-+/*
-+ * KCM IO structure
-+ *
-+ * In theory we cold use sss_iobuf there, but since iobuf was
-+ * made opaque, this allows it to allocate the structures on
-+ * the stack in one go.
-+ * */
- struct kcm_data {
-     uint8_t *data;
-     size_t length;
- };
- 
--/* To avoid leaking the sssd-specific responder data to other
-+/*
-+ * To avoid leaking the sssd-specific responder data to other
-  * modules, the ccache databases and other KCM specific data
-  * are kept separately
-  */
-@@ -41,7 +48,8 @@ struct kcm_resp_ctx {
-     krb5_context k5c;
- };
- 
--/* responder context that contains both the responder data,
-+/*
-+ * responder context that contains both the responder data,
-  * like the ccaches and the sssd-specific stuff like the
-  * generic responder ctx
-  */
-@@ -55,4 +63,11 @@ struct kcm_ctx {
- 
- int kcm_connection_setup(struct cli_ctx *cctx);
- 
-+/*
-+ * Internally in SSSD-KCM we use SSSD-internal error codes so that we
-+ * can always the same sss_strerror() functions to format the errors
-+ * nicely, but the client expects libkrb5 error codes.
-+ */
-+krb5_error_code sss2krb5_error(errno_t err);
-+
- #endif /* __KCMSRV_PVT_H__ */
--- 
-2.9.3
-
diff --git a/SOURCES/0023-nss-idmap-add-nss-like-calls-with-timeout-and-flags.patch b/SOURCES/0023-nss-idmap-add-nss-like-calls-with-timeout-and-flags.patch
new file mode 100644
index 0000000..d6f9b89
--- /dev/null
+++ b/SOURCES/0023-nss-idmap-add-nss-like-calls-with-timeout-and-flags.patch
@@ -0,0 +1,901 @@
+From 15d7f1aeb541615314b914b6be1149f6e289d3e2 Mon Sep 17 00:00:00 2001
+From: Sumit Bose <sbose@redhat.com>
+Date: Fri, 29 Sep 2017 16:16:01 +0200
+Subject: [PATCH 23/31] nss-idmap: add nss like calls with timeout and flags
+
+This patch adds new calls to libsss_nss_idmap to get NSS like user and
+group information directly from SSSD without using the system's NSS
+interfaces.
+
+Additionally a timeout and a flags options are added which are not
+available for system's NSS.
+
+Related to https://pagure.io/SSSD/sssd/issue/2478
+
+Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
+(cherry picked from commit 5e6622722e84d594298a8324f3685a1bda2b5868)
+---
+ Makefile.am                                  |  22 +-
+ configure.ac                                 |  13 +
+ src/sss_client/common.c                      |   9 +-
+ src/sss_client/common_private.h              |  41 +++
+ src/sss_client/idmap/common_ex.c             | 105 +++++++
+ src/sss_client/idmap/sss_nss_ex.c            | 402 +++++++++++++++++++++++++++
+ src/sss_client/idmap/sss_nss_idmap.exports   |  10 +
+ src/sss_client/idmap/sss_nss_idmap.h         | 135 +++++++++
+ src/sss_client/idmap/sss_nss_idmap_private.h |  30 ++
+ 9 files changed, 757 insertions(+), 10 deletions(-)
+ create mode 100644 src/sss_client/common_private.h
+ create mode 100644 src/sss_client/idmap/common_ex.c
+ create mode 100644 src/sss_client/idmap/sss_nss_ex.c
+ create mode 100644 src/sss_client/idmap/sss_nss_idmap_private.h
+
+diff --git a/Makefile.am b/Makefile.am
+index dc2f4b1857ce5bd376544488348731be29b6b293..dd25d1f7ea1be66388aa1b393bac290c4d7501a2 100644
+--- a/Makefile.am
++++ b/Makefile.am
+@@ -1159,13 +1159,28 @@ pkgconfig_DATA += src/sss_client/idmap/sss_nss_idmap.pc
+ libsss_nss_idmap_la_DEPENDENCIES = src/sss_client/idmap/sss_nss_idmap.exports
+ libsss_nss_idmap_la_SOURCES = \
+     src/sss_client/idmap/sss_nss_idmap.c \
++    src/sss_client/idmap/sss_nss_ex.c \
++    src/sss_client/idmap/sss_nss_idmap_private.h \
+     src/sss_client/common.c \
+-    src/util/strtonum.c
++    src/sss_client/idmap/common_ex.c \
++    src/sss_client/nss_mc_passwd.c \
++    src/sss_client/nss_passwd.c \
++    src/sss_client/nss_mc_group.c \
++    src/sss_client/nss_group.c \
++    src/sss_client/nss_mc_initgr.c \
++    src/sss_client/nss_mc_common.c \
++    src/util/strtonum.c \
++    src/util/murmurhash3.c \
++    src/util/io.c \
++    $(NULL)
+ libsss_nss_idmap_la_LIBADD = \
+-    $(CLIENT_LIBS)
++    $(LIBCLOCK_GETTIME) \
++    $(CLIENT_LIBS) \
++    -lpthread \
++    $(NULL)
+ libsss_nss_idmap_la_LDFLAGS = \
+     -Wl,--version-script,$(srcdir)/src/sss_client/idmap/sss_nss_idmap.exports \
+-    -version-info 3:0:3
++    -version-info 4:0:4
+ 
+ dist_noinst_DATA += src/sss_client/idmap/sss_nss_idmap.exports
+ 
+@@ -3624,6 +3639,7 @@ libnss_sss_la_SOURCES = \
+     src/sss_client/sss_cli.h \
+     src/sss_client/nss_compat.h \
+     src/sss_client/nss_common.h \
++    src/sss_client/common_private.h \
+     src/sss_client/nss_mc_common.c \
+     src/util/io.c \
+     src/util/murmurhash3.c \
+diff --git a/configure.ac b/configure.ac
+index 7037927b5f7045b29d3774c85758e00e35e6def6..7e699d33e342c70d210d3f320c8a29a99e0c78a6 100644
+--- a/configure.ac
++++ b/configure.ac
+@@ -75,6 +75,19 @@ AC_SEARCH_LIBS([timer_create], [rt posix4],
+ AC_SUBST([LIBADD_TIMER])
+ LIBS=$SAVE_LIBS
+ 
++# Check library for the clock_gettime function
++SAVE_LIBS=$LIBS
++LIBS=
++LIBCLOCK_GETTIME=
++AC_SEARCH_LIBS([clock_gettime], [rt posix4],
++    [AC_DEFINE([HAVE_LIBRT], [1],
++         [Define if you have the librt library or equivalent.])
++     LIBCLOCK_GETTIME="$LIBS"],
++    [AC_MSG_ERROR([unable to find library for the clock_gettime() function])])
++
++AC_SUBST([LIBCLOCK_GETTIME])
++LIBS=$SAVE_LIBS
++
+ # Check for presence of modern functions for setting file timestamps
+ AC_CHECK_FUNCS([ utimensat \
+                  futimens ])
+diff --git a/src/sss_client/common.c b/src/sss_client/common.c
+index e5e0cbf854e4c977c03f9b1ca1ac90bfd8cbdb77..40252a35281dc4e94c712c3e7f8253af8b19b35a 100644
+--- a/src/sss_client/common.c
++++ b/src/sss_client/common.c
+@@ -43,6 +43,7 @@
+ #include <libintl.h>
+ #define _(STRING) dgettext (PACKAGE, STRING)
+ #include "sss_cli.h"
++#include "common_private.h"
+ 
+ #if HAVE_PTHREAD
+ #include <pthread.h>
+@@ -1113,13 +1114,7 @@ errno_t sss_strnlen(const char *str, size_t maxlen, size_t *len)
+ #if HAVE_PTHREAD
+ typedef void (*sss_mutex_init)(void);
+ 
+-struct sss_mutex {
+-    pthread_mutex_t mtx;
+-
+-    int old_cancel_state;
+-};
+-
+-static struct sss_mutex sss_nss_mtx = { .mtx  = PTHREAD_MUTEX_INITIALIZER };
++struct sss_mutex sss_nss_mtx = { .mtx  = PTHREAD_MUTEX_INITIALIZER };
+ 
+ static struct sss_mutex sss_pam_mtx = { .mtx  = PTHREAD_MUTEX_INITIALIZER };
+ 
+diff --git a/src/sss_client/common_private.h b/src/sss_client/common_private.h
+new file mode 100644
+index 0000000000000000000000000000000000000000..a98d2c062caeecdbab02ecdaa6ae44d688a791bb
+--- /dev/null
++++ b/src/sss_client/common_private.h
+@@ -0,0 +1,41 @@
++/*
++    SSSD
++
++    SSS client - private calls
++
++    Authors:
++        Sumit Bose <sbose@redhat.com>
++
++    Copyright (C) 2017 Red Hat
++
++    This program is free software; you can redistribute it and/or modify
++    it under the terms of the GNU General Public License as published by
++    the Free Software Foundation; either version 3 of the License, or
++    (at your option) any later version.
++
++    This program is distributed in the hope that it will be useful,
++    but WITHOUT ANY WARRANTY; without even the implied warranty of
++    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++    GNU General Public License for more details.
++
++    You should have received a copy of the GNU General Public License
++    along with this program.  If not, see <http://www.gnu.org/licenses/>.
++*/
++
++#ifndef COMMON_PRIVATE_H_
++#define COMMON_PRIVATE_H_
++
++#include "config.h"
++
++#if HAVE_PTHREAD
++#include <pthread.h>
++
++struct sss_mutex {
++    pthread_mutex_t mtx;
++
++    int old_cancel_state;
++};
++
++#endif /* HAVE_PTHREAD */
++
++#endif /* COMMON_PRIVATE_H_ */
+diff --git a/src/sss_client/idmap/common_ex.c b/src/sss_client/idmap/common_ex.c
+new file mode 100644
+index 0000000000000000000000000000000000000000..5efe9fabed7896ce674615472dbb256c4eae2144
+--- /dev/null
++++ b/src/sss_client/idmap/common_ex.c
+@@ -0,0 +1,105 @@
++/*
++    Authors:
++        Sumit Bose <sbose@redhat.com>
++
++    Copyright (C) 2017 Red Hat
++
++    SSSD's enhanced NSS API
++
++    This program is free software; you can redistribute it and/or modify
++    it under the terms of the GNU General Public License as published by
++    the Free Software Foundation; either version 3 of the License, or
++    (at your option) any later version.
++
++    This program is distributed in the hope that it will be useful,
++    but WITHOUT ANY WARRANTY; without even the implied warranty of
++    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++    GNU General Public License for more details.
++
++    You should have received a copy of the GNU General Public License
++    along with this program.  If not, see <http://www.gnu.org/licenses/>.
++*/
++
++#include <time.h>
++#include <errno.h>
++
++#include "sss_cli.h"
++#include "common_private.h"
++
++extern struct sss_mutex sss_nss_mtx;
++
++#define SEC_FROM_MSEC(ms) ((ms) / 1000)
++#define NSEC_FROM_MSEC(ms) (((ms) % 1000) * 1000 * 1000)
++
++/* adopted from timersub() defined in /usr/include/sys/time.h */
++#define TIMESPECSUB(a, b, result)                                             \
++  do {                                                                        \
++    (result)->tv_sec = (a)->tv_sec - (b)->tv_sec;                             \
++    (result)->tv_nsec = (a)->tv_nsec - (b)->tv_nsec;                          \
++    if ((result)->tv_nsec < 0) {                                              \
++      --(result)->tv_sec;                                                     \
++      (result)->tv_nsec += 1000000000;                                        \
++    }                                                                         \
++  } while (0)
++
++#define TIMESPEC_TO_MS(ts) (  ((ts)->tv_sec * 1000) \
++                            + ((ts)->tv_nsec) / (1000 * 1000) )
++
++static int sss_mt_timedlock(struct sss_mutex *m, struct timespec *endtime)
++{
++    int ret;
++
++    ret = pthread_mutex_timedlock(&m->mtx, endtime);
++    if (ret != 0) {
++        return ret;
++    }
++    pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &m->old_cancel_state);
++
++    return 0;
++}
++
++int sss_nss_timedlock(unsigned int timeout_ms, int *time_left_ms)
++{
++    int ret;
++    int left;
++    struct timespec starttime;
++    struct timespec endtime;
++    struct timespec diff;
++
++    /* make sure there is no overrun when calculating the time left */
++    if (timeout_ms > INT_MAX) {
++        timeout_ms = INT_MAX;
++    }
++
++    ret = clock_gettime(CLOCK_REALTIME, &starttime);
++    if (ret != 0) {
++        return ret;
++    }
++    endtime.tv_sec = starttime.tv_sec + SEC_FROM_MSEC(timeout_ms);
++    endtime.tv_nsec = starttime.tv_nsec + NSEC_FROM_MSEC(timeout_ms);
++
++    ret = sss_mt_timedlock(&sss_nss_mtx, &endtime);
++
++    if (ret == 0) {
++        ret = clock_gettime(CLOCK_REALTIME, &endtime);
++        if (ret != 0) {
++            return ret;
++        }
++
++        if (timeout_ms == 0) {
++            *time_left_ms = 0;
++        } else {
++            TIMESPECSUB(&endtime, &starttime, &diff);
++            left = timeout_ms - TIMESPEC_TO_MS(&diff);
++            if (left <= 0) {
++                return EIO;
++            } else if (left > SSS_CLI_SOCKET_TIMEOUT) {
++                *time_left_ms = SSS_CLI_SOCKET_TIMEOUT;
++            } else {
++                *time_left_ms = left;
++            }
++        }
++    }
++
++    return ret;
++}
+diff --git a/src/sss_client/idmap/sss_nss_ex.c b/src/sss_client/idmap/sss_nss_ex.c
+new file mode 100644
+index 0000000000000000000000000000000000000000..582d1373ec35305cf128e04fd3d705457d304789
+--- /dev/null
++++ b/src/sss_client/idmap/sss_nss_ex.c
+@@ -0,0 +1,402 @@
++/*
++    SSSD
++
++    Extended NSS Responder Interface
++
++    Authors:
++        Sumit Bose <sbose@redhat.com>
++
++    Copyright (C) 2017 Red Hat
++
++    This program is free software; you can redistribute it and/or modify
++    it under the terms of the GNU General Public License as published by
++    the Free Software Foundation; either version 3 of the License, or
++    (at your option) any later version.
++
++    This program is distributed in the hope that it will be useful,
++    but WITHOUT ANY WARRANTY; without even the implied warranty of
++    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++    GNU General Public License for more details.
++
++    You should have received a copy of the GNU General Public License
++    along with this program.  If not, see <http://www.gnu.org/licenses/>.
++*/
++#include <stdlib.h>
++#include <errno.h>
++
++#include <sys/param.h> /* for MIN() */
++
++#include "sss_client/sss_cli.h"
++#include "sss_client/nss_mc.h"
++#include "sss_client/nss_common.h"
++#include "sss_client/idmap/sss_nss_idmap.h"
++#include "sss_client/idmap/sss_nss_idmap_private.h"
++
++#ifndef discard_const
++#define discard_const(ptr) ((void *)((uintptr_t)(ptr)))
++#endif
++
++struct sss_nss_initgr_rep {
++    gid_t *groups;
++    long int *ngroups;
++    long int *start;
++};
++
++struct nss_input {
++    union {
++        const char *name;
++        uid_t uid;
++        gid_t gid;
++    } input;
++    struct sss_cli_req_data rd;
++    enum sss_cli_command cmd;
++    union {
++        struct sss_nss_pw_rep pwrep;
++        struct sss_nss_gr_rep grrep;
++        struct sss_nss_initgr_rep initgrrep;
++    } result;
++};
++
++errno_t sss_nss_mc_get(struct nss_input *inp)
++{
++    switch(inp->cmd) {
++    case SSS_NSS_GETPWNAM:
++        return sss_nss_mc_getpwnam(inp->input.name, (inp->rd.len - 1),
++                                   inp->result.pwrep.result,
++                                   inp->result.pwrep.buffer,
++                                   inp->result.pwrep.buflen);
++        break;
++    case SSS_NSS_GETPWUID:
++        return sss_nss_mc_getpwuid(inp->input.uid,
++                                   inp->result.pwrep.result,
++                                   inp->result.pwrep.buffer,
++                                   inp->result.pwrep.buflen);
++        break;
++    case SSS_NSS_GETGRNAM:
++        return sss_nss_mc_getgrnam(inp->input.name, (inp->rd.len - 1),
++                                   inp->result.grrep.result,
++                                   inp->result.grrep.buffer,
++                                   inp->result.grrep.buflen);
++        break;
++    case SSS_NSS_GETGRGID:
++        return sss_nss_mc_getgrgid(inp->input.gid,
++                                   inp->result.grrep.result,
++                                   inp->result.grrep.buffer,
++                                   inp->result.grrep.buflen);
++        break;
++    case SSS_NSS_INITGR:
++        return sss_nss_mc_initgroups_dyn(inp->input.name, (inp->rd.len - 1),
++                                         -1 /* currently ignored */,
++                                         inp->result.initgrrep.start,
++                                         inp->result.initgrrep.ngroups,
++                                         &(inp->result.initgrrep.groups),
++                                         *(inp->result.initgrrep.ngroups));
++        break;
++    default:
++        return EINVAL;
++    }
++}
++
++int sss_get_ex(struct nss_input *inp, uint32_t flags, unsigned int timeout)
++{
++    uint8_t *repbuf = NULL;
++    size_t replen;
++    size_t len;
++    uint32_t num_results;
++    int ret;
++    int time_left;
++    int errnop;
++    size_t c;
++    gid_t *new_groups;
++    size_t idx;
++
++    ret = sss_nss_mc_get(inp);
++    switch (ret) {
++    case 0:
++        return 0;
++    case ERANGE:
++        return ERANGE;
++    case ENOENT:
++        /* fall through, we need to actively ask the parent
++         * if no entry is found */
++        break;
++    default:
++        /* if using the mmaped cache failed,
++         * fall back to socket based comms */
++        break;
++    }
++
++    sss_nss_timedlock(timeout, &time_left);
++
++    /* previous thread might already initialize entry in mmap cache */
++    ret = sss_nss_mc_get(inp);
++    switch (ret) {
++    case 0:
++        ret = 0;
++        goto out;
++    case ERANGE:
++        ret = ERANGE;
++        goto out;
++    case ENOENT:
++        /* fall through, we need to actively ask the parent
++         * if no entry is found */
++        break;
++    default:
++        /* if using the mmaped cache failed,
++         * fall back to socket based comms */
++        break;
++    }
++
++    ret = sss_nss_make_request_timeout(inp->cmd, &inp->rd, time_left,
++                                       &repbuf, &replen, &errnop);
++    if (ret != NSS_STATUS_SUCCESS) {
++        ret = errnop != 0 ? errnop : EIO;
++        goto out;
++    }
++
++    /* Get number of results from repbuf. */
++    SAFEALIGN_COPY_UINT32(&num_results, repbuf, NULL);
++
++    /* no results if not found */
++    if (num_results == 0) {
++        ret = ENOENT;
++        goto out;
++    }
++
++    if (inp->cmd == SSS_NSS_INITGR) {
++        if ((*(inp->result.initgrrep.ngroups) - *(inp->result.initgrrep.start))
++                    < num_results) {
++            new_groups = realloc(inp->result.initgrrep.groups,
++                                 (num_results + *(inp->result.initgrrep.start))
++                                    * sizeof(gid_t));
++            if (new_groups == NULL) {
++                ret = ENOMEM;
++                goto out;
++            }
++
++            inp->result.initgrrep.groups = new_groups;
++        }
++        *(inp->result.initgrrep.ngroups) = num_results
++                                            + *(inp->result.initgrrep.start);
++
++        idx = 2 * sizeof(uint32_t);
++        for (c = 0; c < num_results; c++) {
++            SAFEALIGN_COPY_UINT32(
++                &(inp->result.initgrrep.groups[*(inp->result.initgrrep.start)]),
++                repbuf + idx, &idx);
++            *(inp->result.initgrrep.start) += 1;
++        }
++
++        ret = 0;
++        goto out;
++    }
++
++    /* only 1 result is accepted for this function */
++    if (num_results != 1) {
++        ret = EBADMSG;
++        goto out;
++    }
++
++    len = replen - 8;
++    if (inp->cmd == SSS_NSS_GETPWNAM || inp->cmd == SSS_NSS_GETPWUID) {
++        ret = sss_nss_getpw_readrep(&(inp->result.pwrep), repbuf+8, &len);
++    } else if (inp->cmd == SSS_NSS_GETGRNAM || inp->cmd == SSS_NSS_GETGRGID) {
++        ret = sss_nss_getgr_readrep(&(inp->result.grrep), repbuf+8, &len);
++    } else {
++        ret = EINVAL;
++        goto out;
++    }
++    if (ret) {
++        goto out;
++    }
++
++    if (len == 0) {
++        /* no extra data */
++        ret = 0;
++        goto out;
++    }
++
++out:
++    free(repbuf);
++
++    sss_nss_unlock();
++    return ret;
++}
++
++int sss_nss_getpwnam_timeout(const char *name, struct passwd *pwd,
++                             char *buffer, size_t buflen,
++                             struct passwd **result,
++                             uint32_t flags, unsigned int timeout)
++{
++    int ret;
++    struct nss_input inp = {
++        .input.name = name,
++        .cmd = SSS_NSS_GETPWNAM,
++        .rd.data = name,
++        .result.pwrep.result = pwd,
++        .result.pwrep.buffer = buffer,
++        .result.pwrep.buflen = buflen};
++
++    if (buffer == NULL || buflen == 0) {
++        return ERANGE;
++    }
++
++    ret = sss_strnlen(name, SSS_NAME_MAX, &inp.rd.len);
++    if (ret != 0) {
++        return EINVAL;
++    }
++    inp.rd.len++;
++
++    *result = NULL;
++
++    ret = sss_get_ex(&inp, flags, timeout);
++    if (ret == 0) {
++        *result = inp.result.pwrep.result;
++    }
++    return ret;
++}
++
++int sss_nss_getpwuid_timeout(uid_t uid, struct passwd *pwd,
++                             char *buffer, size_t buflen,
++                             struct passwd **result,
++                             uint32_t flags, unsigned int timeout)
++{
++    int ret;
++    uint32_t user_uid = uid;
++    struct nss_input inp = {
++        .input.uid = uid,
++        .cmd = SSS_NSS_GETPWUID,
++        .rd.len = sizeof(uint32_t),
++        .rd.data = &user_uid,
++        .result.pwrep.result = pwd,
++        .result.pwrep.buffer = buffer,
++        .result.pwrep.buflen = buflen};
++
++    if (buffer == NULL || buflen == 0) {
++        return ERANGE;
++    }
++
++    *result = NULL;
++
++    ret = sss_get_ex(&inp, flags, timeout);
++    if (ret == 0) {
++        *result = inp.result.pwrep.result;
++    }
++    return ret;
++}
++
++int sss_nss_getgrnam_timeout(const char *name, struct group *grp,
++                             char *buffer, size_t buflen, struct group **result,
++                             uint32_t flags, unsigned int timeout)
++{
++    int ret;
++    struct nss_input inp = {
++        .input.name = name,
++        .cmd = SSS_NSS_GETGRNAM,
++        .rd.data = name,
++        .result.grrep.result = grp,
++        .result.grrep.buffer = buffer,
++        .result.grrep.buflen = buflen};
++
++    if (buffer == NULL || buflen == 0) {
++        return ERANGE;
++    }
++
++    ret = sss_strnlen(name, SSS_NAME_MAX, &inp.rd.len);
++    if (ret != 0) {
++        return EINVAL;
++    }
++    inp.rd.len++;
++
++    *result = NULL;
++
++    ret = sss_get_ex(&inp, flags, timeout);
++    if (ret == 0) {
++        *result = inp.result.grrep.result;
++    }
++    return ret;
++}
++
++int sss_nss_getgrgid_timeout(gid_t gid, struct group *grp,
++                             char *buffer, size_t buflen, struct group **result,
++                             uint32_t flags, unsigned int timeout)
++{
++    int ret;
++    uint32_t group_gid = gid;
++    struct nss_input inp = {
++        .input.gid = gid,
++        .cmd = SSS_NSS_GETGRGID,
++        .rd.len = sizeof(uint32_t),
++        .rd.data = &group_gid,
++        .result.grrep.result = grp,
++        .result.grrep.buffer = buffer,
++        .result.grrep.buflen = buflen};
++
++    if (buffer == NULL || buflen == 0) {
++        return ERANGE;
++    }
++
++    *result = NULL;
++
++    ret = sss_get_ex(&inp, flags, timeout);
++    if (ret == 0) {
++        *result = inp.result.grrep.result;
++    }
++    return ret;
++}
++
++int sss_nss_getgrouplist_timeout(const char *name, gid_t group,
++                                 gid_t *groups, int *ngroups,
++                                 uint32_t flags, unsigned int timeout)
++{
++    int ret;
++    gid_t *new_groups;
++    long int new_ngroups;
++    long int start = 1;
++    struct nss_input inp = {
++        .input.name = name,
++        .cmd = SSS_NSS_INITGR,
++        .rd.data = name};
++
++    if (groups == NULL || ngroups == NULL || *ngroups == 0) {
++        return EINVAL;
++    }
++
++    ret = sss_strnlen(name, SSS_NAME_MAX, &inp.rd.len);
++    if (ret != 0) {
++        return ret;
++    }
++    inp.rd.len++;
++
++    new_ngroups = MAX(1, *ngroups);
++    new_groups = malloc(new_ngroups * sizeof(gid_t));
++    if (new_groups == NULL) {
++        free(discard_const(inp.rd.data));
++        return ENOMEM;
++    }
++    new_groups[0] = group;
++
++    inp.result.initgrrep.groups = new_groups,
++    inp.result.initgrrep.ngroups = &new_ngroups;
++    inp.result.initgrrep.start = &start;
++
++
++    ret = sss_get_ex(&inp, flags, timeout);
++    free(discard_const(inp.rd.data));
++    if (ret != 0) {
++        free(new_groups);
++        return ret;
++    }
++
++    memcpy(groups, new_groups, MIN(*ngroups, start) * sizeof(gid_t));
++    free(new_groups);
++
++    if (start > *ngroups) {
++        ret = ERANGE;
++    } else {
++        ret = 0;
++    }
++    *ngroups = start;
++
++    return ret;
++}
+diff --git a/src/sss_client/idmap/sss_nss_idmap.exports b/src/sss_client/idmap/sss_nss_idmap.exports
+index 49dac6fc9351b0ca98cd46e83b85ec8ef0075a0d..788d05ecc3bd56fa88e68a98b9c8096cf7140a09 100644
+--- a/src/sss_client/idmap/sss_nss_idmap.exports
++++ b/src/sss_client/idmap/sss_nss_idmap.exports
+@@ -31,3 +31,13 @@ SSS_NSS_IDMAP_0.3.0 {
+     global:
+         sss_nss_getlistbycert;
+ } SSS_NSS_IDMAP_0.2.0;
++
++SSS_NSS_IDMAP_0.4.0 {
++    # public functions
++    global:
++        sss_nss_getpwnam_timeout;
++        sss_nss_getpwuid_timeout;
++        sss_nss_getgrnam_timeout;
++        sss_nss_getgrgid_timeout;
++        sss_nss_getgrouplist_timeout;
++} SSS_NSS_IDMAP_0.3.0;
+diff --git a/src/sss_client/idmap/sss_nss_idmap.h b/src/sss_client/idmap/sss_nss_idmap.h
+index cbf19479ff9ec6e0d6e07e1f7e48a1571e147740..2334b6cb3fb8ef62e4ce3a7187c7affaeaa034e7 100644
+--- a/src/sss_client/idmap/sss_nss_idmap.h
++++ b/src/sss_client/idmap/sss_nss_idmap.h
+@@ -26,6 +26,9 @@
+ #define SSS_NSS_IDMAP_H_
+ 
+ #include <stdint.h>
++#include <sys/types.h>
++#include <pwd.h>
++#include <grp.h>
+ 
+ /**
+  * Object types
+@@ -159,4 +162,136 @@ int sss_nss_getlistbycert(const char *cert, char ***fq_name,
+  * @param[in] kv_list Key-value list returned by sss_nss_getorigbyname().
+  */
+ void sss_nss_free_kv(struct sss_nss_kv *kv_list);
++
++/**
++ * Flags to control the behavior and the results for sss_*_ex() calls
++ */
++
++#define SSS_NSS_EX_FLAG_NO_FLAGS 0
++
++#ifdef IPA_389DS_PLUGIN_HELPER_CALLS
++
++/**
++ * @brief Return user information based on the user name
++ *
++ * @param[in]  name       same as for getpwnam_r(3)
++ * @param[in]  pwd        same as for getpwnam_r(3)
++ * @param[in]  buffer     same as for getpwnam_r(3)
++ * @param[in]  buflen     same as for getpwnam_r(3)
++ * @param[out] result     same as for getpwnam_r(3)
++ * @param[in]  flags      flags to control the behavior and the results of the
++ *                        call
++ * @param[in]  timeout    timeout in milliseconds
++ *
++ * @return
++ *  - 0:
++ *  - ENOENT:    no user with the given name found
++ *  - ERANGE:    Insufficient buffer space supplied
++ *  - ETIME:     request timed out but was send to SSSD
++ *  - ETIMEDOUT: request timed out but was not send to SSSD
++ */
++int sss_nss_getpwnam_timeout(const char *name, struct passwd *pwd,
++                             char *buffer, size_t buflen,
++                             struct passwd **result,
++                             uint32_t flags, unsigned int timeout);
++
++/**
++ * @brief Return user information based on the user uid
++ *
++ * @param[in]  uid        same as for getpwuid_r(3)
++ * @param[in]  pwd        same as for getpwuid_r(3)
++ * @param[in]  buffer     same as for getpwuid_r(3)
++ * @param[in]  buflen     same as for getpwuid_r(3)
++ * @param[out] result     same as for getpwuid_r(3)
++ * @param[in]  flags      flags to control the behavior and the results of the
++ *                        call
++ * @param[in]  timeout    timeout in milliseconds
++ *
++ * @return
++ *  - 0:
++ *  - ENOENT:    no user with the given uid found
++ *  - ERANGE:    Insufficient buffer space supplied
++ *  - ETIME:     request timed out but was send to SSSD
++ *  - ETIMEDOUT: request timed out but was not send to SSSD
++ */
++int sss_nss_getpwuid_timeout(uid_t uid, struct passwd *pwd,
++                             char *buffer, size_t buflen,
++                             struct passwd **result,
++                             uint32_t flags, unsigned int timeout);
++
++/**
++ * @brief Return group information based on the group name
++ *
++ * @param[in]  name       same as for getgrnam_r(3)
++ * @param[in]  pwd        same as for getgrnam_r(3)
++ * @param[in]  buffer     same as for getgrnam_r(3)
++ * @param[in]  buflen     same as for getgrnam_r(3)
++ * @param[out] result     same as for getgrnam_r(3)
++ * @param[in]  flags      flags to control the behavior and the results of the
++ *                        call
++ * @param[in]  timeout    timeout in milliseconds
++ *
++ * @return
++ *  - 0:
++ *  - ENOENT:    no group with the given name found
++ *  - ERANGE:    Insufficient buffer space supplied
++ *  - ETIME:     request timed out but was send to SSSD
++ *  - ETIMEDOUT: request timed out but was not send to SSSD
++ */
++int sss_nss_getgrnam_timeout(const char *name, struct group *grp,
++                             char *buffer, size_t buflen, struct group **result,
++                             uint32_t flags, unsigned int timeout);
++
++/**
++ * @brief Return group information based on the group gid
++ *
++ * @param[in]  gid        same as for getgrgid_r(3)
++ * @param[in]  pwd        same as for getgrgid_r(3)
++ * @param[in]  buffer     same as for getgrgid_r(3)
++ * @param[in]  buflen     same as for getgrgid_r(3)
++ * @param[out] result     same as for getgrgid_r(3)
++ * @param[in]  flags      flags to control the behavior and the results of the
++ *                        call
++ * @param[in]  timeout    timeout in milliseconds
++ *
++ * @return
++ *  - 0:
++ *  - ENOENT:    no group with the given gid found
++ *  - ERANGE:    Insufficient buffer space supplied
++ *  - ETIME:     request timed out but was send to SSSD
++ *  - ETIMEDOUT: request timed out but was not send to SSSD
++ */
++int sss_nss_getgrgid_timeout(gid_t gid, struct group *grp,
++                             char *buffer, size_t buflen, struct group **result,
++                             uint32_t flags, unsigned int timeout);
++
++/**
++ * @brief Return a list of groups to which a user belongs
++ *
++ * @param[in]      name       name of the user
++ * @param[in]      group      same as second argument of getgrouplist(3)
++ * @param[in]      groups     array of gid_t of size ngroups, will be filled
++ *                            with GIDs of groups the user belongs to
++ * @param[in,out]  ngroups    size of the groups array on input. On output it
++ *                            will contain the actual number of groups the
++ *                            user belongs to. With a return value of 0 the
++ *                            groups array was large enough to hold all group.
++ *                            With a return valu of ERANGE the array was not
++ *                            large enough and ngroups will have the needed
++ *                            size.
++ * @param[in]  flags          flags to control the behavior and the results of
++ *                            the call
++ * @param[in]  timeout        timeout in milliseconds
++ *
++ * @return
++ *  - 0:         success
++ *  - ENOENT:    no user with the given name found
++ *  - ERANGE:    Insufficient buffer space supplied
++ *  - ETIME:     request timed out but was send to SSSD
++ *  - ETIMEDOUT: request timed out but was not send to SSSD
++ */
++int sss_nss_getgrouplist_timeout(const char *name, gid_t group,
++                                 gid_t *groups, int *ngroups,
++                                 uint32_t flags, unsigned int timeout);
++#endif /* IPA_389DS_PLUGIN_HELPER_CALLS */
+ #endif /* SSS_NSS_IDMAP_H_ */
+diff --git a/src/sss_client/idmap/sss_nss_idmap_private.h b/src/sss_client/idmap/sss_nss_idmap_private.h
+new file mode 100644
+index 0000000000000000000000000000000000000000..afcd8e355981b9a2dc29a62bab143756b39ed654
+--- /dev/null
++++ b/src/sss_client/idmap/sss_nss_idmap_private.h
+@@ -0,0 +1,30 @@
++/*
++    SSSD
++
++    NSS Responder ID-mapping interface - private calls
++
++    Authors:
++        Sumit Bose <sbose@redhat.com>
++
++    Copyright (C) 2017 Red Hat
++
++    This program is free software; you can redistribute it and/or modify
++    it under the terms of the GNU General Public License as published by
++    the Free Software Foundation; either version 3 of the License, or
++    (at your option) any later version.
++
++    This program is distributed in the hope that it will be useful,
++    but WITHOUT ANY WARRANTY; without even the implied warranty of
++    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++    GNU General Public License for more details.
++
++    You should have received a copy of the GNU General Public License
++    along with this program.  If not, see <http://www.gnu.org/licenses/>.
++*/
++
++#ifndef SSS_NSS_IDMAP_PRIVATE_H_
++#define SSS_NSS_IDMAP_PRIVATE_H_
++
++int sss_nss_timedlock(unsigned int timeout_ms, int *time_left_ms);
++
++#endif /* SSS_NSS_IDMAP_PRIVATE_H_ */
+-- 
+2.13.6
+
diff --git a/SOURCES/0024-KCM-Implement-an-internal-ccache-storage-and-retriev.patch b/SOURCES/0024-KCM-Implement-an-internal-ccache-storage-and-retriev.patch
deleted file mode 100644
index a5e0fc2..0000000
--- a/SOURCES/0024-KCM-Implement-an-internal-ccache-storage-and-retriev.patch
+++ /dev/null
@@ -1,2153 +0,0 @@
-From 79f6ccd2dc3f0c1369d5a93678c88ee76ec761e0 Mon Sep 17 00:00:00 2001
-From: Jakub Hrozek <jhrozek@redhat.com>
-Date: Tue, 7 Mar 2017 13:49:21 +0100
-Subject: [PATCH 24/36] KCM: Implement an internal ccache storage and retrieval
- API
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-In order for the KCM server to work with ccaches stored in different
-locations, implement a middle-man between the KCM server and the ccache
-storage.
-
-This module has asynchronous API because we can't assume anything about
-where the ccaches are stored.
-
-Reviewed-by: Michal Židek <mzidek@redhat.com>
-Reviewed-by: Simo Sorce <simo@redhat.com>
----
- Makefile.am                           |    9 +
- configure.ac                          |    1 +
- contrib/sssd.spec.in                  |    1 +
- src/external/libuuid.m4               |   17 +
- src/responder/kcm/kcmsrv_ccache.c     | 1423 +++++++++++++++++++++++++++++++++
- src/responder/kcm/kcmsrv_ccache.h     |  306 +++++++
- src/responder/kcm/kcmsrv_ccache_be.h  |  204 +++++
- src/responder/kcm/kcmsrv_ccache_pvt.h |   62 ++
- src/responder/kcm/kcmsrv_pvt.h        |    1 +
- 9 files changed, 2024 insertions(+)
- create mode 100644 src/external/libuuid.m4
- create mode 100644 src/responder/kcm/kcmsrv_ccache.c
- create mode 100644 src/responder/kcm/kcmsrv_ccache.h
- create mode 100644 src/responder/kcm/kcmsrv_ccache_be.h
- create mode 100644 src/responder/kcm/kcmsrv_ccache_pvt.h
-
-diff --git a/Makefile.am b/Makefile.am
-index 4248536e90370c1aab59549a9c18408ef314e6d4..a2b9dc49e95fa2d025f5174d2902866fab180a78 100644
---- a/Makefile.am
-+++ b/Makefile.am
-@@ -711,6 +711,9 @@ dist_noinst_HEADERS = \
-     src/responder/secrets/secsrv_proxy.h \
-     src/responder/kcm/kcm.h \
-     src/responder/kcm/kcmsrv_pvt.h \
-+    src/responder/kcm/kcmsrv_ccache.h \
-+    src/responder/kcm/kcmsrv_ccache_pvt.h \
-+    src/responder/kcm/kcmsrv_ccache_be.h \
-     src/sbus/sbus_client.h \
-     src/sbus/sssd_dbus.h \
-     src/sbus/sssd_dbus_meta.h \
-@@ -1488,16 +1491,22 @@ if BUILD_KCM
- sssd_kcm_SOURCES = \
-     src/responder/kcm/kcm.c \
-     src/responder/kcm/kcmsrv_cmd.c \
-+    src/responder/kcm/kcmsrv_ccache.c \
-     src/util/sss_sockets.c \
-+    src/util/sss_krb5.c \
-+    src/util/sss_iobuf.c \
-     $(SSSD_RESPONDER_OBJ) \
-     $(NULL)
- sssd_kcm_CFLAGS = \
-     $(AM_CFLAGS) \
-     $(KRB5_CFLAGS) \
-+    $(UUID_CFLAGS) \
-     $(NULL)
- sssd_kcm_LDADD = \
-     $(KRB5_LIBS) \
-     $(SSSD_LIBS) \
-+    $(UUID_LIBS) \
-+    $(SYSTEMD_DAEMON_LIBS) \
-     $(SSSD_INTERNAL_LTLIBS) \
-     $(NULL)
- endif
-diff --git a/configure.ac b/configure.ac
-index c363d48a806cc1998e85779a92b6b59b0e2a5c9c..cf5e2557ef0a1bd6374200aa33abea6c509d03aa 100644
---- a/configure.ac
-+++ b/configure.ac
-@@ -202,6 +202,7 @@ fi
- 
- if test x$with_kcm = xyes; then
-     m4_include([src/external/libcurl.m4])
-+    m4_include([src/external/libuuid.m4])
- fi
- # This variable is defined by external/libcurl.m4, but conditionals
- # must be always evaluated
-diff --git a/contrib/sssd.spec.in b/contrib/sssd.spec.in
-index 5c7c2af521a84ef2ca6cca7b2d6cd1f9b3057056..52d33b4de281dc1d91a9027ac1c8c878e66fb396 100644
---- a/contrib/sssd.spec.in
-+++ b/contrib/sssd.spec.in
-@@ -222,6 +222,7 @@ BuildRequires: systemtap-sdt-devel
- %endif
- BuildRequires: http-parser-devel
- BuildRequires: jansson-devel
-+BuildRequires: libuuid-devel
- 
- %description
- Provides a set of daemons to manage access to remote directories and
-diff --git a/src/external/libuuid.m4 b/src/external/libuuid.m4
-new file mode 100644
-index 0000000000000000000000000000000000000000..55411a2118bd787c9d50ba61f9cb791e1c76088d
---- /dev/null
-+++ b/src/external/libuuid.m4
-@@ -0,0 +1,17 @@
-+AC_SUBST(UUID_LIBS)
-+AC_SUBST(UUID_CFLAGS)
-+
-+PKG_CHECK_MODULES([UUID], [uuid], [found_uuid=yes], [found_uuid=no])
-+
-+SSS_AC_EXPAND_LIB_DIR()
-+AS_IF([test x"$found_uuid" = xyes],
-+    [AC_CHECK_HEADERS([uuid/uuid.h],
-+        [AC_CHECK_LIB([uuid],
-+                      [uuid_generate],
-+                      [UUID_LIBS="-L$sss_extra_libdir -luuid"],
-+                      [AC_MSG_ERROR([libuuid missing uuid_generate])],
-+                      [-L$sss_extra_libdir -luuid])],
-+        [AC_MSG_ERROR([
-+You must have the header file uuid.h installed to build sssd
-+with KCM responder. If you want to build sssd without KCM responder
-+then specify --without-kcm when running configure.])])])
-diff --git a/src/responder/kcm/kcmsrv_ccache.c b/src/responder/kcm/kcmsrv_ccache.c
-new file mode 100644
-index 0000000000000000000000000000000000000000..2c565b8378e3ec297faf655d3c48d7ab902713d3
---- /dev/null
-+++ b/src/responder/kcm/kcmsrv_ccache.c
-@@ -0,0 +1,1423 @@
-+/*
-+   SSSD
-+
-+   KCM Server - the KCM ccache operations
-+
-+   Copyright (C) Red Hat, 2016
-+
-+   This program is free software; you can redistribute it and/or modify
-+   it under the terms of the GNU General Public License as published by
-+   the Free Software Foundation; either version 3 of the License, or
-+   (at your option) any later version.
-+
-+   This program is distributed in the hope that it will be useful,
-+   but WITHOUT ANY WARRANTY; without even the implied warranty of
-+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+   GNU General Public License for more details.
-+
-+   You should have received a copy of the GNU General Public License
-+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
-+*/
-+
-+#include "config.h"
-+
-+#include "util/crypto/sss_crypto.h"
-+#include "util/util.h"
-+#include "util/sss_krb5.h"
-+#include "responder/kcm/kcmsrv_ccache.h"
-+#include "responder/kcm/kcmsrv_ccache_pvt.h"
-+#include "responder/kcm/kcmsrv_ccache_be.h"
-+
-+static int kcm_cc_destructor(struct kcm_ccache *cc)
-+{
-+    if (cc == NULL) {
-+        return 0;
-+    }
-+
-+    krb5_free_principal(NULL, cc->client);
-+    return 0;
-+}
-+
-+errno_t kcm_cc_new(TALLOC_CTX *mem_ctx,
-+                   krb5_context k5c,
-+                   struct cli_creds *owner,
-+                   const char *name,
-+                   krb5_principal princ,
-+                   struct kcm_ccache **_cc)
-+{
-+    struct kcm_ccache *cc;
-+    krb5_error_code kret;
-+    errno_t ret;
-+
-+    cc = talloc_zero(mem_ctx, struct kcm_ccache);
-+    if (cc == NULL) {
-+        return ENOMEM;
-+    }
-+
-+    ret = kcm_check_name(name, owner);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_CRIT_FAILURE, "Name %s is malformed\n", name);
-+        return ret;
-+    }
-+
-+    cc->name = talloc_strdup(cc, name);
-+    if (cc->name == NULL) {
-+        talloc_free(cc);
-+        return ENOMEM;
-+    }
-+
-+    uuid_generate(cc->uuid);
-+
-+    kret = krb5_copy_principal(k5c, princ, &cc->client);
-+    if (kret != 0) {
-+        const char *err_msg = sss_krb5_get_error_message(k5c, kret);
-+        DEBUG(SSSDBG_OP_FAILURE,
-+              "krb5_copy_principal failed: [%d][%s]\n", kret, err_msg);
-+        sss_krb5_free_error_message(k5c, err_msg);
-+        talloc_free(cc);
-+        return ERR_INTERNAL;
-+    }
-+
-+    cc->owner.uid = cli_creds_get_uid(owner);
-+    cc->owner.gid = cli_creds_get_gid(owner);
-+    cc->kdc_offset = INT32_MAX;
-+
-+    talloc_set_destructor(cc, kcm_cc_destructor);
-+    *_cc = cc;
-+    return EOK;
-+}
-+
-+const char *kcm_cc_get_name(struct kcm_ccache *cc)
-+{
-+    return cc ? cc->name : NULL;
-+}
-+
-+errno_t kcm_cc_get_uuid(struct kcm_ccache *cc, uuid_t _uuid)
-+{
-+    if (cc == NULL) {
-+        return EINVAL;
-+    }
-+    uuid_copy(_uuid, cc->uuid);
-+    return EOK;
-+}
-+
-+krb5_principal kcm_cc_get_client_principal(struct kcm_ccache *cc)
-+{
-+    return cc ? cc->client : NULL;
-+}
-+
-+bool kcm_cc_access(struct kcm_ccache *cc,
-+                   struct cli_creds *client)
-+{
-+    bool ok;
-+    uid_t uid = cli_creds_get_uid(client);
-+    gid_t gid = cli_creds_get_gid(client);
-+
-+    if (cc == NULL) {
-+        return false;
-+    }
-+
-+    if (uid == 0 && gid == 0) {
-+        /* root can access any ccache */
-+        return true;
-+    }
-+
-+    ok = ((cc->owner.uid == uid) && (cc->owner.gid == gid));
-+    if (!ok) {
-+        DEBUG(SSSDBG_MINOR_FAILURE,
-+              "Client %"SPRIuid":%"SPRIgid" has no access to ccache %s\n",
-+              cli_creds_get_uid(client),
-+              cli_creds_get_gid(client),
-+              cc->name);
-+    }
-+    return ok;
-+}
-+
-+int32_t kcm_cc_get_offset(struct kcm_ccache *cc)
-+{
-+    return cc ? cc->kdc_offset : INT32_MAX;
-+}
-+
-+errno_t kcm_cc_store_cred_blob(struct kcm_ccache *cc,
-+                               struct sss_iobuf *cred_blob)
-+{
-+    struct kcm_cred *kcreds;
-+    uuid_t uuid;
-+    errno_t ret;
-+
-+    if (cc == NULL || cred_blob == NULL) {
-+        return EINVAL;
-+    }
-+
-+    uuid_generate(uuid);
-+    kcreds = kcm_cred_new(cc, uuid, cred_blob);
-+    if (kcreds == NULL) {
-+        return ENOMEM;
-+    }
-+
-+    ret = kcm_cc_store_creds(cc, kcreds);
-+    if (ret != EOK) {
-+        return ret;
-+    }
-+
-+    return EOK;
-+}
-+
-+struct kcm_cred *kcm_cc_get_cred(struct kcm_ccache *cc)
-+{
-+    if (cc == NULL) {
-+        return NULL;
-+    }
-+
-+    return cc->creds;
-+}
-+
-+struct kcm_cred *kcm_cc_next_cred(struct kcm_cred *crd)
-+{
-+    if (crd == NULL) {
-+        return NULL;
-+    }
-+
-+    return crd->next;
-+}
-+
-+struct kcm_cred *kcm_cred_new(TALLOC_CTX *mem_ctx,
-+                              uuid_t uuid,
-+                              struct sss_iobuf *cred_blob)
-+{
-+    struct kcm_cred *kcreds;
-+
-+    kcreds = talloc_zero(mem_ctx, struct kcm_cred);
-+    if (kcreds == NULL) {
-+        return NULL;
-+    }
-+
-+    uuid_copy(kcreds->uuid, uuid);
-+    kcreds->cred_blob = talloc_steal(kcreds, cred_blob);
-+    return kcreds;
-+}
-+
-+/* Add a cred to ccache */
-+errno_t kcm_cc_store_creds(struct kcm_ccache *cc,
-+                           struct kcm_cred *crd)
-+{
-+    DLIST_ADD(cc->creds, crd);
-+    talloc_steal(cc, crd);
-+    return EOK;
-+}
-+
-+errno_t kcm_cred_get_uuid(struct kcm_cred *crd, uuid_t _uuid)
-+{
-+    if (crd == NULL) {
-+        return EINVAL;
-+    }
-+    uuid_copy(_uuid, crd->uuid);
-+    return EOK;
-+}
-+
-+struct sss_iobuf *kcm_cred_get_creds(struct kcm_cred *crd)
-+{
-+    return crd ? crd->cred_blob : NULL;
-+}
-+
-+struct kcm_ccdb *kcm_ccdb_init(TALLOC_CTX *mem_ctx,
-+                               struct tevent_context *ev,
-+                               enum kcm_ccdb_be cc_be)
-+{
-+    errno_t ret;
-+    struct kcm_ccdb *ccdb = NULL;
-+
-+    if (ev == NULL) {
-+        return NULL;
-+    }
-+
-+    ccdb = talloc_zero(mem_ctx, struct kcm_ccdb);
-+    if (ccdb == NULL) {
-+        return NULL;
-+    }
-+    ccdb->ev = ev;
-+
-+    switch (cc_be) {
-+    case CCDB_BE_MEMORY:
-+        DEBUG(SSSDBG_FUNC_DATA, "KCM back end: memory\n");
-+        /* Not implemented yet */
-+        break;
-+    case CCDB_BE_SECRETS:
-+        DEBUG(SSSDBG_FUNC_DATA, "KCM back end: sssd-secrets\n");
-+        /* Not implemented yet */
-+        break;
-+    default:
-+        DEBUG(SSSDBG_CRIT_FAILURE, "Unknown ccache database\n");
-+        break;
-+    }
-+
-+    if (ccdb->ops == NULL) {
-+        DEBUG(SSSDBG_CRIT_FAILURE, "Ccache database not initialized\n");
-+        talloc_free(ccdb);
-+        return NULL;
-+    }
-+
-+    ret = ccdb->ops->init(ccdb);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_CRIT_FAILURE, "Cannot initialize ccache database\n");
-+        talloc_free(ccdb);
-+        return NULL;
-+    }
-+
-+    return ccdb;
-+}
-+
-+struct kcm_ccdb_nextid_state {
-+    char *next_cc;
-+    struct kcm_ccdb *db;
-+    struct cli_creds *client;
-+};
-+
-+static void kcm_ccdb_nextid_done(struct tevent_req *subreq);
-+
-+struct tevent_req *kcm_ccdb_nextid_send(TALLOC_CTX *mem_ctx,
-+                                        struct tevent_context *ev,
-+                                        struct kcm_ccdb *db,
-+                                        struct cli_creds *client)
-+{
-+    struct tevent_req *req = NULL;
-+    struct tevent_req *subreq = NULL;
-+    struct kcm_ccdb_nextid_state *state = NULL;
-+    errno_t ret;
-+
-+    req = tevent_req_create(mem_ctx, &state, struct kcm_ccdb_nextid_state);
-+    if (req == NULL) {
-+        return NULL;
-+    }
-+    state->db = db;
-+    state->client = client;
-+
-+    if (ev == NULL || db == NULL || client == NULL) {
-+        ret = EINVAL;
-+        goto immediate;
-+    }
-+
-+    subreq = state->db->ops->nextid_send(mem_ctx, ev, state->db, client);
-+    if (subreq == NULL) {
-+        ret = ENOMEM;
-+        goto immediate;
-+    }
-+    tevent_req_set_callback(subreq, kcm_ccdb_nextid_done, req);
-+    return req;
-+
-+immediate:
-+    tevent_req_error(req, ret);
-+    tevent_req_post(req, ev);
-+    return req;
-+}
-+
-+static void kcm_ccdb_nextid_done(struct tevent_req *subreq)
-+{
-+    struct tevent_req *req = tevent_req_callback_data(subreq,
-+                                                      struct tevent_req);
-+    struct kcm_ccdb_nextid_state *state = tevent_req_data(req,
-+                                                struct kcm_ccdb_nextid_state);
-+    errno_t ret;
-+    unsigned int nextid;
-+
-+    ret = state->db->ops->nextid_recv(subreq, &nextid);
-+    talloc_zfree(subreq);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_OP_FAILURE,
-+              "Failed to generate next UID [%d]: %s\n",
-+              ret, sss_strerror(ret));
-+        tevent_req_error(req, ret);
-+        return;
-+    }
-+
-+    state->next_cc = talloc_asprintf(state, "%"SPRIuid":%u",
-+                                     cli_creds_get_uid(state->client),
-+                                     nextid);
-+    if (state->next_cc == NULL) {
-+        DEBUG(SSSDBG_CRIT_FAILURE, "talloc_asprintf failed\n");
-+        tevent_req_error(req, ENOMEM);
-+        return;
-+    }
-+
-+    DEBUG(SSSDBG_TRACE_LIBS, "generated %s\n", state->next_cc);
-+    tevent_req_done(req);
-+}
-+
-+errno_t kcm_ccdb_nextid_recv(struct tevent_req *req,
-+                             TALLOC_CTX *mem_ctx,
-+                             char **_next_cc)
-+{
-+    struct kcm_ccdb_nextid_state *state = tevent_req_data(req,
-+                                                struct kcm_ccdb_nextid_state);
-+    TEVENT_REQ_RETURN_ON_ERROR(req);
-+    *_next_cc = talloc_steal(mem_ctx, state->next_cc);
-+    return EOK;
-+}
-+
-+struct kcm_ccdb_list_state {
-+    struct kcm_ccdb *db;
-+    struct cli_creds *client;
-+
-+    uuid_t *uuid_list;
-+};
-+
-+static void kcm_ccdb_list_done(struct tevent_req *subreq);
-+
-+struct tevent_req *kcm_ccdb_list_send(TALLOC_CTX *mem_ctx,
-+                                      struct tevent_context *ev,
-+                                      struct kcm_ccdb *db,
-+                                      struct cli_creds *client)
-+{
-+    struct tevent_req *req = NULL;
-+    struct tevent_req *subreq = NULL;
-+    struct kcm_ccdb_list_state *state = NULL;
-+    errno_t ret;
-+
-+    req = tevent_req_create(mem_ctx, &state, struct kcm_ccdb_list_state);
-+    if (req == NULL) {
-+        return NULL;
-+    }
-+    state->db = db;
-+    state->client = client;
-+
-+    if (ev == NULL || db == NULL || client == NULL) {
-+        ret = EINVAL;
-+        goto immediate;
-+    }
-+
-+    subreq = state->db->ops->list_send(mem_ctx,
-+                                       ev,
-+                                       state->db,
-+                                       client);
-+    if (subreq == NULL) {
-+        ret = ENOMEM;
-+        goto immediate;
-+    }
-+    tevent_req_set_callback(subreq, kcm_ccdb_list_done, req);
-+    return req;
-+
-+immediate:
-+    tevent_req_error(req, ret);
-+    tevent_req_post(req, ev);
-+    return req;
-+}
-+
-+static void kcm_ccdb_list_done(struct tevent_req *subreq)
-+{
-+    struct tevent_req *req = tevent_req_callback_data(subreq,
-+                                                      struct tevent_req);
-+    struct kcm_ccdb_list_state *state = tevent_req_data(req,
-+                                                struct kcm_ccdb_list_state);
-+    errno_t ret;
-+
-+    ret = state->db->ops->list_recv(subreq, state, &state->uuid_list);
-+    talloc_zfree(subreq);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_OP_FAILURE,
-+              "Failed to list all ccaches [%d]: %s\n",
-+              ret, sss_strerror(ret));
-+        tevent_req_error(req, ret);
-+        return;
-+    }
-+
-+    tevent_req_done(req);
-+}
-+
-+errno_t kcm_ccdb_list_recv(struct tevent_req *req,
-+                           TALLOC_CTX *mem_ctx,
-+                           uuid_t **_uuid_list)
-+{
-+    struct kcm_ccdb_list_state *state = tevent_req_data(req,
-+                                                struct kcm_ccdb_list_state);
-+    TEVENT_REQ_RETURN_ON_ERROR(req);
-+    *_uuid_list = talloc_steal(mem_ctx, state->uuid_list);
-+    return EOK;
-+}
-+
-+struct kcm_ccdb_get_default_state {
-+    struct kcm_ccdb *db;
-+    uuid_t uuid;
-+};
-+
-+static void kcm_ccdb_get_default_done(struct tevent_req *subreq);
-+
-+struct tevent_req *kcm_ccdb_get_default_send(TALLOC_CTX *mem_ctx,
-+                                             struct tevent_context *ev,
-+                                             struct kcm_ccdb *db,
-+                                             struct cli_creds *client)
-+{
-+    struct tevent_req *req = NULL;
-+    struct tevent_req *subreq = NULL;
-+    struct kcm_ccdb_get_default_state *state = NULL;
-+    errno_t ret;
-+
-+    req = tevent_req_create(mem_ctx, &state, struct kcm_ccdb_get_default_state);
-+    if (req == NULL) {
-+        return NULL;
-+    }
-+    state->db = db;
-+
-+    if (ev == NULL || db == NULL || client == NULL) {
-+        ret = EINVAL;
-+        goto immediate;
-+    }
-+
-+    subreq = db->ops->get_default_send(mem_ctx, ev, db, client);
-+    if (subreq == NULL) {
-+        ret = ENOMEM;
-+        goto immediate;
-+    }
-+    tevent_req_set_callback(subreq, kcm_ccdb_get_default_done, req);
-+    return req;
-+
-+immediate:
-+    tevent_req_error(req, ret);
-+    tevent_req_post(req, ev);
-+    return req;
-+}
-+
-+static void kcm_ccdb_get_default_done(struct tevent_req *subreq)
-+{
-+    struct tevent_req *req = tevent_req_callback_data(subreq,
-+                                                      struct tevent_req);
-+    struct kcm_ccdb_get_default_state *state = tevent_req_data(req,
-+                                                struct kcm_ccdb_get_default_state);
-+    errno_t ret;
-+
-+    ret = state->db->ops->get_default_recv(subreq, state->uuid);
-+    talloc_zfree(subreq);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_OP_FAILURE,
-+              "Failed to get the default ccache [%d]: %s\n",
-+              ret, sss_strerror(ret));
-+        tevent_req_error(req, ret);
-+        return;
-+    }
-+
-+    tevent_req_done(req);
-+}
-+
-+errno_t kcm_ccdb_get_default_recv(struct tevent_req *req,
-+                                  uuid_t *uuid)
-+{
-+    struct kcm_ccdb_get_default_state *state = tevent_req_data(req,
-+                                                struct kcm_ccdb_get_default_state);
-+    TEVENT_REQ_RETURN_ON_ERROR(req);
-+
-+    if (uuid != NULL) {
-+        /* The caller might supply a NULL dfl to just check if there is
-+         * some default ccache
-+         */
-+        uuid_copy(*uuid, state->uuid);
-+    }
-+
-+    return EOK;
-+}
-+
-+struct kcm_ccdb_set_default_state {
-+    struct tevent_context *ev;
-+    struct kcm_ccdb *db;
-+    struct cli_creds *client;
-+    uuid_t uuid;
-+};
-+
-+static void kcm_ccdb_set_default_uuid_resolved(struct tevent_req *subreq);
-+static void kcm_ccdb_set_default_done(struct tevent_req *subreq);
-+
-+struct tevent_req *kcm_ccdb_set_default_send(TALLOC_CTX *mem_ctx,
-+                                             struct tevent_context *ev,
-+                                             struct kcm_ccdb *db,
-+                                             struct cli_creds *client,
-+                                             uuid_t uuid)
-+{
-+    struct tevent_req *req = NULL;
-+    struct tevent_req *subreq = NULL;
-+    struct kcm_ccdb_set_default_state *state = NULL;
-+    errno_t ret;
-+
-+    req = tevent_req_create(mem_ctx, &state, struct kcm_ccdb_set_default_state);
-+    if (req == NULL) {
-+        return NULL;
-+    }
-+    state->db = db;
-+    state->ev = ev;
-+    state->client = client;
-+    uuid_copy(state->uuid, uuid);
-+
-+    if (ev == NULL || db == NULL || client == NULL) {
-+        ret = EINVAL;
-+        goto immediate;
-+    }
-+
-+    if (uuid_is_null(uuid)) {
-+        /* NULL UUID means to just reset the default to 'no default' */
-+        subreq = state->db->ops->set_default_send(state,
-+                                                state->ev,
-+                                                state->db,
-+                                                state->client,
-+                                                state->uuid);
-+        if (subreq == NULL) {
-+            ret = ENOMEM;
-+            goto immediate;
-+        }
-+        tevent_req_set_callback(subreq, kcm_ccdb_set_default_done, req);
-+    } else {
-+        /* Otherwise we need to check if the client can access the UUID
-+         * about to be set as default
-+         */
-+        subreq = db->ops->getbyuuid_send(state, ev, db, client, uuid);
-+        if (subreq == NULL) {
-+            ret = ENOMEM;
-+            goto immediate;
-+        }
-+        tevent_req_set_callback(subreq, kcm_ccdb_set_default_uuid_resolved, req);
-+    }
-+
-+    return req;
-+
-+immediate:
-+    tevent_req_error(req, ret);
-+    tevent_req_post(req, ev);
-+    return req;
-+}
-+
-+static void kcm_ccdb_set_default_uuid_resolved(struct tevent_req *subreq)
-+{
-+    struct tevent_req *req = tevent_req_callback_data(subreq,
-+                                                      struct tevent_req);
-+    struct kcm_ccdb_set_default_state *state = tevent_req_data(req,
-+                                                struct kcm_ccdb_set_default_state);
-+    errno_t ret;
-+    bool ok;
-+    struct kcm_ccache *cc;
-+
-+    ret = state->db->ops->getbyuuid_recv(subreq, state, &cc);
-+    talloc_zfree(subreq);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_OP_FAILURE,
-+              "Failed to get cache by UUID [%d]: %s\n",
-+              ret, sss_strerror(ret));
-+        tevent_req_error(req, ret);
-+        return;
-+    }
-+
-+    if (cc == NULL) {
-+        DEBUG(SSSDBG_TRACE_LIBS, "No cache found by UUID\n");
-+        tevent_req_error(req, ERR_KCM_CC_END);
-+        return;
-+    }
-+
-+    ok = kcm_cc_access(cc, state->client);
-+    if (!ok) {
-+        tevent_req_error(req, EACCES);
-+        return;
-+    }
-+
-+    subreq = state->db->ops->set_default_send(state,
-+                                              state->ev,
-+                                              state->db,
-+                                              state->client,
-+                                              state->uuid);
-+    if (subreq == NULL) {
-+        tevent_req_error(req, ENOMEM);
-+        return;
-+    }
-+    tevent_req_set_callback(subreq, kcm_ccdb_set_default_done, req);
-+}
-+
-+static void kcm_ccdb_set_default_done(struct tevent_req *subreq)
-+{
-+    struct tevent_req *req = tevent_req_callback_data(subreq,
-+                                                      struct tevent_req);
-+    struct kcm_ccdb_set_default_state *state = tevent_req_data(req,
-+                                                struct kcm_ccdb_set_default_state);
-+    errno_t ret;
-+
-+    ret = state->db->ops->set_default_recv(subreq);
-+    talloc_zfree(subreq);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_OP_FAILURE,
-+              "Failed to set the default ccache [%d]: %s\n",
-+              ret, sss_strerror(ret));
-+        tevent_req_error(req, ret);
-+        return;
-+    }
-+
-+    tevent_req_done(req);
-+}
-+
-+errno_t kcm_ccdb_set_default_recv(struct tevent_req *req)
-+{
-+    TEVENT_REQ_RETURN_ON_ERROR(req);
-+    return EOK;
-+}
-+
-+struct kcm_ccdb_getbyname_state {
-+    struct kcm_ccdb *db;
-+    struct cli_creds *client;
-+
-+    struct kcm_ccache *cc;
-+};
-+
-+static void kcm_ccdb_getbyname_done(struct tevent_req *subreq);
-+
-+struct tevent_req *kcm_ccdb_getbyname_send(TALLOC_CTX *mem_ctx,
-+                                           struct tevent_context *ev,
-+                                           struct kcm_ccdb *db,
-+                                           struct cli_creds *client,
-+                                           const char *name)
-+{
-+    struct tevent_req *req = NULL;
-+    struct tevent_req *subreq = NULL;
-+    struct kcm_ccdb_getbyname_state *state = NULL;
-+    errno_t ret;
-+
-+    req = tevent_req_create(mem_ctx, &state, struct kcm_ccdb_getbyname_state);
-+    if (req == NULL) {
-+        return NULL;
-+    }
-+    state->db = db;
-+    state->client = client;
-+
-+    if (ev == NULL || db == NULL || client == NULL || name == NULL) {
-+        ret = EINVAL;
-+        goto immediate;
-+    }
-+
-+    subreq = db->ops->getbyname_send(state, ev, db, client, name);
-+    if (subreq == NULL) {
-+        ret = ENOMEM;
-+        goto immediate;
-+    }
-+    tevent_req_set_callback(subreq, kcm_ccdb_getbyname_done, req);
-+    return req;
-+
-+immediate:
-+    tevent_req_error(req, ret);
-+    tevent_req_post(req, ev);
-+    return req;
-+}
-+
-+static void kcm_ccdb_getbyname_done(struct tevent_req *subreq)
-+{
-+    struct tevent_req *req = tevent_req_callback_data(subreq,
-+                                                      struct tevent_req);
-+    struct kcm_ccdb_getbyname_state *state = tevent_req_data(req,
-+                                                struct kcm_ccdb_getbyname_state);
-+    errno_t ret;
-+    bool ok;
-+
-+    ret = state->db->ops->getbyname_recv(subreq, state, &state->cc);
-+    talloc_zfree(subreq);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_OP_FAILURE,
-+              "Failed to get cache by name [%d]: %s\n",
-+              ret, sss_strerror(ret));
-+        tevent_req_error(req, ret);
-+        return;
-+    }
-+
-+    if (state->cc == NULL) {
-+        DEBUG(SSSDBG_TRACE_LIBS, "No cache found by name\n");
-+        tevent_req_done(req);
-+        return;
-+    }
-+
-+    ok = kcm_cc_access(state->cc, state->client);
-+    if (!ok) {
-+        tevent_req_error(req, EACCES);
-+        return;
-+    }
-+
-+    tevent_req_done(req);
-+}
-+
-+errno_t kcm_ccdb_getbyname_recv(struct tevent_req *req,
-+                                TALLOC_CTX *mem_ctx,
-+                                struct kcm_ccache **_cc)
-+{
-+    struct kcm_ccdb_getbyname_state *state = tevent_req_data(req,
-+                                            struct kcm_ccdb_getbyname_state);
-+    TEVENT_REQ_RETURN_ON_ERROR(req);
-+    *_cc = talloc_steal(mem_ctx, state->cc);
-+    return EOK;
-+}
-+
-+struct kcm_ccdb_getbyuuid_state {
-+    struct kcm_ccdb *db;
-+    struct cli_creds *client;
-+
-+    struct kcm_ccache *cc;
-+};
-+
-+static void kcm_ccdb_getbyuuid_done(struct tevent_req *subreq);
-+
-+struct tevent_req *kcm_ccdb_getbyuuid_send(TALLOC_CTX *mem_ctx,
-+                                           struct tevent_context *ev,
-+                                           struct kcm_ccdb *db,
-+                                           struct cli_creds *client,
-+                                           uuid_t uuid)
-+{
-+    struct tevent_req *req = NULL;
-+    struct tevent_req *subreq = NULL;
-+    struct kcm_ccdb_getbyuuid_state *state = NULL;
-+    errno_t ret;
-+
-+    req = tevent_req_create(mem_ctx, &state, struct kcm_ccdb_getbyuuid_state);
-+    if (req == NULL) {
-+        return NULL;
-+    }
-+    state->db = db;
-+    state->client = client;
-+
-+    if (ev == NULL || db == NULL || client == NULL) {
-+        ret = EINVAL;
-+        goto immediate;
-+    }
-+
-+    subreq = db->ops->getbyuuid_send(state, ev, db, client, uuid);
-+    if (subreq == NULL) {
-+        ret = ENOMEM;
-+        goto immediate;
-+    }
-+    tevent_req_set_callback(subreq, kcm_ccdb_getbyuuid_done, req);
-+    return req;
-+
-+immediate:
-+    tevent_req_error(req, ret);
-+    tevent_req_post(req, ev);
-+    return req;
-+}
-+
-+static void kcm_ccdb_getbyuuid_done(struct tevent_req *subreq)
-+{
-+    struct tevent_req *req = tevent_req_callback_data(subreq,
-+                                                      struct tevent_req);
-+    struct kcm_ccdb_getbyuuid_state *state = tevent_req_data(req,
-+                                                struct kcm_ccdb_getbyuuid_state);
-+    errno_t ret;
-+    bool ok;
-+
-+    ret = state->db->ops->getbyuuid_recv(subreq, state, &state->cc);
-+    talloc_zfree(subreq);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_OP_FAILURE,
-+              "Failed to get cache by UUID [%d]: %s\n",
-+              ret, sss_strerror(ret));
-+        tevent_req_error(req, ret);
-+        return;
-+    }
-+
-+    if (state->cc == NULL) {
-+        DEBUG(SSSDBG_TRACE_LIBS, "No cache found by UUID\n");
-+        tevent_req_done(req);
-+        return;
-+    }
-+
-+    ok = kcm_cc_access(state->cc, state->client);
-+    if (!ok) {
-+        tevent_req_error(req, EACCES);
-+        return;
-+    }
-+
-+    tevent_req_done(req);
-+}
-+
-+errno_t kcm_ccdb_getbyuuid_recv(struct tevent_req *req,
-+                                TALLOC_CTX *mem_ctx,
-+                                struct kcm_ccache **_cc)
-+{
-+    struct kcm_ccdb_getbyuuid_state *state = tevent_req_data(req,
-+                                            struct kcm_ccdb_getbyuuid_state);
-+    TEVENT_REQ_RETURN_ON_ERROR(req);
-+    *_cc = talloc_steal(mem_ctx, state->cc);
-+    return EOK;
-+}
-+
-+struct kcm_ccdb_name_by_uuid_state {
-+    struct kcm_ccdb *db;
-+    struct cli_creds *client;
-+
-+    const char *name;
-+};
-+
-+static void kcm_ccdb_name_by_uuid_done(struct tevent_req *subreq);
-+
-+struct tevent_req *kcm_ccdb_name_by_uuid_send(TALLOC_CTX *mem_ctx,
-+                                              struct tevent_context *ev,
-+                                              struct kcm_ccdb *db,
-+                                              struct cli_creds *client,
-+                                              uuid_t uuid)
-+{
-+    struct tevent_req *req = NULL;
-+    struct tevent_req *subreq = NULL;
-+    struct kcm_ccdb_name_by_uuid_state *state = NULL;
-+    errno_t ret;
-+
-+    req = tevent_req_create(mem_ctx,
-+                            &state,
-+                            struct kcm_ccdb_name_by_uuid_state);
-+    if (req == NULL) {
-+        return NULL;
-+    }
-+    state->db = db;
-+    state->client = client;
-+
-+    if (ev == NULL || db == NULL || client == NULL || uuid_is_null(uuid)) {
-+        ret = EINVAL;
-+        goto immediate;
-+    }
-+
-+    subreq = db->ops->name_by_uuid_send(state, ev, db, client, uuid);
-+    if (subreq == NULL) {
-+        ret = ENOMEM;
-+        goto immediate;
-+    }
-+    tevent_req_set_callback(subreq, kcm_ccdb_name_by_uuid_done, req);
-+    return req;
-+
-+immediate:
-+    tevent_req_error(req, ret);
-+    tevent_req_post(req, ev);
-+    return req;
-+}
-+
-+static void kcm_ccdb_name_by_uuid_done(struct tevent_req *subreq)
-+{
-+    struct tevent_req *req = tevent_req_callback_data(subreq,
-+                                                      struct tevent_req);
-+    struct kcm_ccdb_name_by_uuid_state *state = tevent_req_data(req,
-+                                        struct kcm_ccdb_name_by_uuid_state);
-+    errno_t ret;
-+
-+    ret = state->db->ops->name_by_uuid_recv(subreq, state, &state->name);
-+    talloc_zfree(subreq);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_OP_FAILURE,
-+              "Failed to resolve cache by UUID [%d]: %s\n",
-+              ret, sss_strerror(ret));
-+        tevent_req_error(req, ret);
-+        return;
-+    }
-+
-+    tevent_req_done(req);
-+}
-+
-+errno_t kcm_ccdb_name_by_uuid_recv(struct tevent_req *req,
-+                                   TALLOC_CTX *mem_ctx,
-+                                   const char **_name)
-+{
-+    struct kcm_ccdb_name_by_uuid_state *state = tevent_req_data(req,
-+                                        struct kcm_ccdb_name_by_uuid_state);
-+    TEVENT_REQ_RETURN_ON_ERROR(req);
-+    *_name = talloc_steal(mem_ctx, state->name);
-+    return EOK;
-+}
-+
-+struct kcm_ccdb_uuid_by_name_state {
-+    struct kcm_ccdb *db;
-+    struct cli_creds *client;
-+
-+    uuid_t uuid;
-+};
-+
-+static void kcm_ccdb_uuid_by_name_done(struct tevent_req *subreq);
-+
-+struct tevent_req *kcm_ccdb_uuid_by_name_send(TALLOC_CTX *mem_ctx,
-+                                              struct tevent_context *ev,
-+                                              struct kcm_ccdb *db,
-+                                              struct cli_creds *client,
-+                                              const char *name)
-+{
-+    struct tevent_req *req = NULL;
-+    struct tevent_req *subreq = NULL;
-+    struct kcm_ccdb_uuid_by_name_state *state = NULL;
-+    errno_t ret;
-+
-+    req = tevent_req_create(mem_ctx,
-+                            &state,
-+                            struct kcm_ccdb_uuid_by_name_state);
-+    if (req == NULL) {
-+        return NULL;
-+    }
-+    state->db = db;
-+    state->client = client;
-+
-+    if (ev == NULL || db == NULL || client == NULL || name == NULL) {
-+        ret = EINVAL;
-+        goto immediate;
-+    }
-+
-+    subreq = db->ops->uuid_by_name_send(state, ev, db, client, name);
-+    if (subreq == NULL) {
-+        ret = ENOMEM;
-+        goto immediate;
-+    }
-+    tevent_req_set_callback(subreq, kcm_ccdb_uuid_by_name_done, req);
-+    return req;
-+
-+immediate:
-+    tevent_req_error(req, ret);
-+    tevent_req_post(req, ev);
-+    return req;
-+}
-+
-+static void kcm_ccdb_uuid_by_name_done(struct tevent_req *subreq)
-+{
-+    struct tevent_req *req = tevent_req_callback_data(subreq,
-+                                                      struct tevent_req);
-+    struct kcm_ccdb_uuid_by_name_state *state = tevent_req_data(req,
-+                                        struct kcm_ccdb_uuid_by_name_state);
-+    errno_t ret;
-+
-+    ret = state->db->ops->uuid_by_name_recv(subreq, state, state->uuid);
-+    talloc_zfree(subreq);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_OP_FAILURE,
-+              "Failed to resolve cache by UUID [%d]: %s\n",
-+              ret, sss_strerror(ret));
-+        tevent_req_error(req, ret);
-+        return;
-+    }
-+
-+    tevent_req_done(req);
-+}
-+
-+errno_t kcm_ccdb_uuid_by_name_recv(struct tevent_req *req,
-+                                   TALLOC_CTX *mem_ctx,
-+                                   uuid_t _uuid)
-+{
-+    struct kcm_ccdb_uuid_by_name_state *state = tevent_req_data(req,
-+                                        struct kcm_ccdb_uuid_by_name_state);
-+    TEVENT_REQ_RETURN_ON_ERROR(req);
-+    uuid_copy(_uuid, state->uuid);
-+    return EOK;
-+}
-+
-+struct kcm_ccdb_create_cc_state {
-+    struct kcm_ccdb *db;
-+};
-+
-+static void kcm_ccdb_create_done(struct tevent_req *subreq);
-+
-+struct tevent_req *kcm_ccdb_create_cc_send(TALLOC_CTX *mem_ctx,
-+                                           struct tevent_context *ev,
-+                                           struct kcm_ccdb *db,
-+                                           struct cli_creds *client,
-+                                           struct kcm_ccache *cc)
-+{
-+    struct tevent_req *req = NULL;
-+    struct tevent_req *subreq = NULL;
-+    struct kcm_ccdb_create_cc_state *state = NULL;
-+    errno_t ret;
-+    bool ok;
-+
-+    req = tevent_req_create(mem_ctx, &state, struct kcm_ccdb_create_cc_state);
-+    if (req == NULL) {
-+        return NULL;
-+    }
-+    state->db = db;
-+
-+    if (ev == NULL || db == NULL || client == NULL || cc == NULL) {
-+        ret = EINVAL;
-+        goto immediate;
-+    }
-+
-+    ok = kcm_cc_access(cc, client);
-+    if (!ok) {
-+        ret = EACCES;
-+        goto immediate;
-+    }
-+
-+    subreq = state->db->ops->create_send(mem_ctx,
-+                                         ev,
-+                                         state->db,
-+                                         client,
-+                                         cc);
-+    if (subreq == NULL) {
-+        ret = ENOMEM;
-+        goto immediate;
-+    }
-+    tevent_req_set_callback(subreq, kcm_ccdb_create_done, req);
-+    return req;
-+
-+immediate:
-+    tevent_req_error(req, ret);
-+    tevent_req_post(req, ev);
-+    return req;
-+}
-+
-+static void kcm_ccdb_create_done(struct tevent_req *subreq)
-+{
-+    struct tevent_req *req = tevent_req_callback_data(subreq,
-+                                                      struct tevent_req);
-+    struct kcm_ccdb_create_cc_state *state = tevent_req_data(req,
-+                                                struct kcm_ccdb_create_cc_state);
-+    errno_t ret;
-+
-+    ret = state->db->ops->create_recv(subreq);
-+    talloc_zfree(subreq);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_OP_FAILURE,
-+              "Failed to create ccache [%d]: %s\n",
-+              ret, sss_strerror(ret));
-+        tevent_req_error(req, ret);
-+        return;
-+    }
-+
-+    tevent_req_done(req);
-+}
-+
-+errno_t kcm_ccdb_create_cc_recv(struct tevent_req *req)
-+{
-+    TEVENT_REQ_RETURN_ON_ERROR(req);
-+    return EOK;
-+}
-+
-+void kcm_mod_ctx_clear(struct kcm_mod_ctx *mod_ctx)
-+{
-+    if (mod_ctx == NULL) {
-+        return;
-+    }
-+
-+    mod_ctx->kdc_offset = INT32_MAX;
-+}
-+
-+void kcm_mod_cc(struct kcm_ccache *cc, struct kcm_mod_ctx *mod_ctx)
-+{
-+    if (cc == NULL || mod_ctx == NULL) {
-+        return;
-+    }
-+
-+    if (mod_ctx->kdc_offset != INT32_MAX) {
-+        cc->kdc_offset = mod_ctx->kdc_offset;
-+    }
-+
-+}
-+
-+struct kcm_ccdb_mod_cc_state {
-+    struct kcm_ccdb *db;
-+};
-+
-+static void kcm_ccdb_mod_done(struct tevent_req *subreq);
-+
-+struct tevent_req *kcm_ccdb_mod_cc_send(TALLOC_CTX *mem_ctx,
-+                                        struct tevent_context *ev,
-+                                        struct kcm_ccdb *db,
-+                                        struct cli_creds *client,
-+                                        uuid_t uuid,
-+                                        struct kcm_mod_ctx *mod_cc)
-+{
-+    struct tevent_req *req = NULL;
-+    struct tevent_req *subreq = NULL;
-+    struct kcm_ccdb_mod_cc_state *state = NULL;
-+    errno_t ret;
-+
-+    req = tevent_req_create(mem_ctx, &state, struct kcm_ccdb_mod_cc_state);
-+    if (req == NULL) {
-+        return NULL;
-+    }
-+    state->db = db;
-+
-+    if (ev == NULL || db == NULL || client == NULL || mod_cc == NULL) {
-+        ret = EINVAL;
-+        goto immediate;
-+    }
-+
-+    subreq = state->db->ops->mod_send(mem_ctx,
-+                                      ev,
-+                                      state->db,
-+                                      client,
-+                                      uuid,
-+                                      mod_cc);
-+    if (subreq == NULL) {
-+        ret = ENOMEM;
-+        goto immediate;
-+    }
-+    tevent_req_set_callback(subreq, kcm_ccdb_mod_done, req);
-+    return req;
-+
-+immediate:
-+    tevent_req_error(req, ret);
-+    tevent_req_post(req, ev);
-+    return req;
-+}
-+
-+static void kcm_ccdb_mod_done(struct tevent_req *subreq)
-+{
-+    struct tevent_req *req = tevent_req_callback_data(subreq,
-+                                                      struct tevent_req);
-+    struct kcm_ccdb_mod_cc_state *state = tevent_req_data(req,
-+                                                struct kcm_ccdb_mod_cc_state);
-+    errno_t ret;
-+
-+    ret = state->db->ops->mod_recv(subreq);
-+    talloc_zfree(subreq);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_OP_FAILURE,
-+              "Failed to create ccache [%d]: %s\n",
-+              ret, sss_strerror(ret));
-+        tevent_req_error(req, ret);
-+        return;
-+    }
-+
-+    tevent_req_done(req);
-+}
-+
-+errno_t kcm_ccdb_mod_cc_recv(struct tevent_req *req)
-+{
-+    TEVENT_REQ_RETURN_ON_ERROR(req);
-+    return EOK;
-+}
-+
-+struct kcm_ccdb_store_cred_blob_state {
-+    struct kcm_ccdb *db;
-+};
-+
-+static void kcm_ccdb_store_cred_blob_done(struct tevent_req *subreq);
-+
-+struct tevent_req *kcm_ccdb_store_cred_blob_send(TALLOC_CTX *mem_ctx,
-+                                                 struct tevent_context *ev,
-+                                                 struct kcm_ccdb *db,
-+                                                 struct cli_creds *client,
-+                                                 uuid_t uuid,
-+                                                 struct sss_iobuf *cred_blob)
-+{
-+    struct tevent_req *req = NULL;
-+    struct tevent_req *subreq = NULL;
-+    struct kcm_ccdb_store_cred_blob_state *state = NULL;
-+    errno_t ret;
-+
-+    req = tevent_req_create(mem_ctx, &state, struct kcm_ccdb_store_cred_blob_state);
-+    if (req == NULL) {
-+        return NULL;
-+    }
-+    state->db = db;
-+
-+    if (ev == NULL || db == NULL || client == NULL || cred_blob == NULL) {
-+        ret = EINVAL;
-+        goto immediate;
-+    }
-+
-+    subreq = state->db->ops->store_cred_send(mem_ctx,
-+                                             ev,
-+                                             state->db,
-+                                             client,
-+                                             uuid,
-+                                             cred_blob);
-+    if (subreq == NULL) {
-+        ret = ENOMEM;
-+        goto immediate;
-+    }
-+    tevent_req_set_callback(subreq, kcm_ccdb_store_cred_blob_done, req);
-+    return req;
-+
-+immediate:
-+    tevent_req_error(req, ret);
-+    tevent_req_post(req, ev);
-+    return req;
-+}
-+
-+static void kcm_ccdb_store_cred_blob_done(struct tevent_req *subreq)
-+{
-+    struct tevent_req *req = tevent_req_callback_data(subreq,
-+                                                      struct tevent_req);
-+    struct kcm_ccdb_store_cred_blob_state *state = tevent_req_data(req,
-+                                                struct kcm_ccdb_store_cred_blob_state);
-+    errno_t ret;
-+
-+    ret = state->db->ops->store_cred_recv(subreq);
-+    talloc_zfree(subreq);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_OP_FAILURE,
-+              "Failed to create ccache [%d]: %s\n",
-+              ret, sss_strerror(ret));
-+        tevent_req_error(req, ret);
-+        return;
-+    }
-+
-+    tevent_req_done(req);
-+}
-+
-+errno_t kcm_ccdb_store_cred_blob_recv(struct tevent_req *req)
-+{
-+    TEVENT_REQ_RETURN_ON_ERROR(req);
-+    return EOK;
-+}
-+
-+struct kcm_ccdb_delete_cc_state {
-+    struct tevent_context *ev;
-+    struct kcm_ccdb *db;
-+    struct cli_creds *client;
-+    uuid_t uuid;
-+};
-+
-+static void kcm_ccdb_delete_done(struct tevent_req *subreq);
-+static void kcm_ccdb_delete_get_default_done(struct tevent_req *subreq);
-+static void kcm_ccdb_delete_default_reset_done(struct tevent_req *subreq);
-+
-+struct tevent_req *kcm_ccdb_delete_cc_send(TALLOC_CTX *mem_ctx,
-+                                           struct tevent_context *ev,
-+                                           struct kcm_ccdb *db,
-+                                           struct cli_creds *client,
-+                                           uuid_t uuid)
-+{
-+    struct tevent_req *req = NULL;
-+    struct tevent_req *subreq = NULL;
-+    struct kcm_ccdb_delete_cc_state *state = NULL;
-+    errno_t ret;
-+
-+    req = tevent_req_create(mem_ctx, &state, struct kcm_ccdb_delete_cc_state);
-+    if (req == NULL) {
-+        return NULL;
-+    }
-+    state->db = db;
-+    state->ev = ev;
-+    state->client = client;
-+    uuid_copy(state->uuid, uuid);
-+
-+    if (ev == NULL || db == NULL || client == NULL) {
-+        ret = EINVAL;
-+        goto immediate;
-+    }
-+
-+    subreq = state->db->ops->delete_send(state,
-+                                         state->ev,
-+                                         state->db,
-+                                         state->client,
-+                                         state->uuid);
-+    tevent_req_set_callback(subreq, kcm_ccdb_delete_done, req);
-+
-+    return req;
-+
-+immediate:
-+    tevent_req_error(req, ret);
-+    tevent_req_post(req, ev);
-+    return req;
-+}
-+
-+static void kcm_ccdb_delete_done(struct tevent_req *subreq)
-+{
-+    struct tevent_req *req = tevent_req_callback_data(subreq,
-+                                                      struct tevent_req);
-+    struct kcm_ccdb_delete_cc_state *state = tevent_req_data(req,
-+                                                struct kcm_ccdb_delete_cc_state);
-+    errno_t ret;
-+
-+    ret = state->db->ops->delete_recv(subreq);
-+    talloc_zfree(subreq);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_OP_FAILURE,
-+              "Failed to delete ccache [%d]: %s\n",
-+              ret, sss_strerror(ret));
-+        tevent_req_error(req, ret);
-+        return;
-+    }
-+
-+    /* The delete operation must also check if the deleted ccache was
-+     * the default and reset the default if it was
-+     */
-+    subreq = state->db->ops->get_default_send(state,
-+                                              state->ev,
-+                                              state->db,
-+                                              state->client);
-+    if (subreq == NULL) {
-+        tevent_req_error(req, ENOMEM);
-+        return;
-+    }
-+    tevent_req_set_callback(subreq, kcm_ccdb_delete_get_default_done, req);
-+}
-+
-+static void kcm_ccdb_delete_get_default_done(struct tevent_req *subreq)
-+{
-+    struct tevent_req *req = tevent_req_callback_data(subreq,
-+                                                      struct tevent_req);
-+    struct kcm_ccdb_delete_cc_state *state = tevent_req_data(req,
-+                                                struct kcm_ccdb_delete_cc_state);
-+    errno_t ret;
-+    uuid_t dfl_uuid;
-+    uuid_t null_uuid;
-+
-+    ret = state->db->ops->get_default_recv(subreq, dfl_uuid);
-+    talloc_zfree(subreq);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_OP_FAILURE,
-+              "Failed to get the default ccache [%d]: %s\n",
-+              ret, sss_strerror(ret));
-+        tevent_req_error(req, ret);
-+        return;
-+    }
-+
-+    if (uuid_compare(dfl_uuid, state->uuid) != 0) {
-+        /* The ccache about to be deleted was not the default, quit */
-+        tevent_req_done(req);
-+        return;
-+    }
-+
-+    /* If we deleted the default ccache, reset the default ccache to 'none' */
-+    uuid_clear(null_uuid);
-+
-+    subreq = state->db->ops->set_default_send(state,
-+                                              state->ev,
-+                                              state->db,
-+                                              state->client,
-+                                              null_uuid);
-+    if (subreq == NULL) {
-+        tevent_req_error(req, ENOMEM);
-+        return;
-+    }
-+    tevent_req_set_callback(subreq, kcm_ccdb_delete_default_reset_done, req);
-+}
-+
-+static void kcm_ccdb_delete_default_reset_done(struct tevent_req *subreq)
-+{
-+    struct tevent_req *req = tevent_req_callback_data(subreq,
-+                                                      struct tevent_req);
-+    struct kcm_ccdb_delete_cc_state *state = tevent_req_data(req,
-+                                                struct kcm_ccdb_delete_cc_state);
-+    errno_t ret;
-+
-+    ret = state->db->ops->set_default_recv(subreq);
-+    talloc_zfree(subreq);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_OP_FAILURE,
-+              "Failed to NULL the default ccache [%d]: %s\n",
-+              ret, sss_strerror(ret));
-+        tevent_req_error(req, ret);
-+        return;
-+    }
-+
-+    tevent_req_done(req);
-+}
-+
-+errno_t kcm_ccdb_delete_cc_recv(struct tevent_req *req)
-+{
-+    TEVENT_REQ_RETURN_ON_ERROR(req);
-+    return EOK;
-+}
-+
-+void kcm_debug_uuid(uuid_t uuid)
-+{
-+    char dbgbuf[UUID_STR_SIZE];
-+
-+    if (!(debug_level & SSSDBG_TRACE_ALL) || uuid == NULL) {
-+        return;
-+    }
-+
-+    uuid_unparse(uuid, dbgbuf);
-+    DEBUG(SSSDBG_TRACE_ALL, "UUID: %s\n", dbgbuf);
-+}
-+
-+errno_t kcm_check_name(const char *name, struct cli_creds *client)
-+{
-+    char prefix[64];
-+    size_t prefix_len;
-+
-+    prefix_len = snprintf(prefix, sizeof(prefix),
-+                          "%"SPRIuid, cli_creds_get_uid(client));
-+
-+    if (strncmp(name, prefix, prefix_len) != 0) {
-+        return ERR_KCM_WRONG_CCNAME_FORMAT;
-+    }
-+    return EOK;
-+}
-diff --git a/src/responder/kcm/kcmsrv_ccache.h b/src/responder/kcm/kcmsrv_ccache.h
-new file mode 100644
-index 0000000000000000000000000000000000000000..130ae48ae30d5e1e2ab238a647a9b9dc76cc4945
---- /dev/null
-+++ b/src/responder/kcm/kcmsrv_ccache.h
-@@ -0,0 +1,306 @@
-+/*
-+   SSSD
-+
-+   KCM Server - the KCM ccache operations
-+
-+   Copyright (C) Red Hat, 2016
-+
-+   This program is free software; you can redistribute it and/or modify
-+   it under the terms of the GNU General Public License as published by
-+   the Free Software Foundation; either version 3 of the License, or
-+   (at your option) any later version.
-+
-+   This program is distributed in the hope that it will be useful,
-+   but WITHOUT ANY WARRANTY; without even the implied warranty of
-+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+   GNU General Public License for more details.
-+
-+   You should have received a copy of the GNU General Public License
-+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
-+*/
-+#ifndef _KCMSRV_CCACHE_H_
-+#define _KCMSRV_CCACHE_H_
-+
-+#include "config.h"
-+
-+#include <krb5/krb5.h>
-+#include <uuid/uuid.h>
-+
-+#include "util/util.h"
-+#include "util/sss_iobuf.h"
-+#include "util/util_creds.h"
-+
-+#define UUID_BYTES    16
-+#define UUID_STR_SIZE 37
-+
-+/*
-+ * Credentials are opaque to the KCM server
-+ *
-+ * Each ccache has a unique UUID.
-+ */
-+struct kcm_cred;
-+
-+/*
-+ * An opaque ccache type and its operations
-+ *
-+ * Contains zero or some KCM credentials. One credential in the cache
-+ * is marked as the default one. The client can set and get the default
-+ * cache (e.g. with kswitch) but one cache is always the default -- we
-+ * fall back to the one created first.
-+ *
-+ * Each cache has a name and a UUID. Heimdal allows the name to be changed,
-+ * we don't (yet, because the MIT client doesn't allow that either)
-+ *
-+ * Each ccache also stores a client principal.
-+ */
-+struct kcm_ccache;
-+
-+/*
-+ * Create a new KCM ccache owned by mem_ctx on the
-+ * memory level.
-+ *
-+ * When created, the ccache contains no credendials
-+ */
-+errno_t kcm_cc_new(TALLOC_CTX *mem_ctx,
-+                   krb5_context k5c,
-+                   struct cli_creds *owner,
-+                   const char *name,
-+                   krb5_principal princ,
-+                   struct kcm_ccache **_cc);
-+
-+/*
-+ * Returns true if a client can access a ccache.
-+ *
-+ * Note that root can access any ccache */
-+bool kcm_cc_access(struct kcm_ccache *cc,
-+                   struct cli_creds *client);
-+
-+/*
-+ * Since the kcm_ccache structure is opaque, the kcmsrv_ccache
-+ * layer contains a number of getsetters to read and write
-+ * properties of the kcm_ccache structure
-+ */
-+const char *kcm_cc_get_name(struct kcm_ccache *cc);
-+errno_t kcm_cc_get_uuid(struct kcm_ccache *cc, uuid_t _uuid);
-+krb5_principal kcm_cc_get_client_principal(struct kcm_ccache *cc);
-+int32_t kcm_cc_get_offset(struct kcm_ccache *cc);
-+
-+/* Mainly useful for creating a cred structure from a persistent
-+ * storage
-+ */
-+struct kcm_cred *kcm_cred_new(TALLOC_CTX *mem_ctx,
-+                              uuid_t uuid,
-+                              struct sss_iobuf *cred_blob);
-+
-+/* Add a cred to ccache */
-+errno_t kcm_cc_store_creds(struct kcm_ccache *cc,
-+                           struct kcm_cred *crd);
-+
-+errno_t kcm_cred_get_uuid(struct kcm_cred *crd, uuid_t uuid);
-+
-+/*
-+ * At the moment, the credentials are stored without unmarshalling
-+ * them, just as the clients sends the credentials.
-+ */
-+struct sss_iobuf *kcm_cred_get_creds(struct kcm_cred *crd);
-+errno_t kcm_cc_store_cred_blob(struct kcm_ccache *cc,
-+                               struct sss_iobuf *cred_blob);
-+ /*
-+ * The KCM server can call kcm_cred_get_creds to fetch the first
-+ * credential, then iterate over the credentials with
-+ * kcm_cc_next_cred until it returns NULL
-+ */
-+struct kcm_cred *kcm_cc_get_cred(struct kcm_ccache *cc);
-+struct kcm_cred *kcm_cc_next_cred(struct kcm_cred *crd);
-+
-+enum kcm_ccdb_be {
-+    CCDB_BE_MEMORY,
-+    CCDB_BE_SECRETS,
-+};
-+
-+/* An opaque database that contains all the ccaches */
-+struct kcm_ccdb;
-+
-+/*
-+ * Initialize a ccache database of type cc_be
-+ */
-+struct kcm_ccdb *kcm_ccdb_init(TALLOC_CTX *mem_ctx,
-+                               struct tevent_context *ev,
-+                               enum kcm_ccdb_be cc_be);
-+
-+/*
-+ * In KCM, each ccache name is usually in the form of "UID:<num>
-+ *
-+ * The <num> is generated by the KCM ccache database. Use this function
-+ * to retrieve the next number
-+ */
-+struct tevent_req *kcm_ccdb_nextid_send(TALLOC_CTX *mem_ctx,
-+                                        struct tevent_context *ev,
-+                                        struct kcm_ccdb *db,
-+                                        struct cli_creds *client);
-+errno_t kcm_ccdb_nextid_recv(struct tevent_req *req,
-+                             TALLOC_CTX *mem_ctx,
-+                             char **_nextid);
-+
-+/*
-+ * List all ccaches that belong to a given client
-+ *
-+ * The cc_list the recv function returns is NULL-terminated.
-+ *
-+ * NOTE: Contrary to how Heimdal behaves, root CAN NOT list all ccaches
-+ * of all users. This is a deliberate decision to treat root as any other
-+ * user, except it can access a ccache of another user by name, just not
-+ * list them.
-+ *
-+ * If a client has no ccaches, the function returns OK, but an empty list
-+ * containing just the NULL sentinel.
-+ */
-+struct tevent_req *kcm_ccdb_list_send(TALLOC_CTX *mem_ctx,
-+                                      struct tevent_context *ev,
-+                                      struct kcm_ccdb *db,
-+                                      struct cli_creds *client);
-+errno_t kcm_ccdb_list_recv(struct tevent_req *req,
-+                           TALLOC_CTX *mem_ctx,
-+                           uuid_t **_uuid_list);
-+
-+/*
-+ * Retrieve a ccache by name.
-+ *
-+ * If there is no such ccache, return EOK, but a NULL _cc pointer
-+ */
-+struct tevent_req *kcm_ccdb_getbyname_send(TALLOC_CTX *mem_ctx,
-+                                           struct tevent_context *ev,
-+                                           struct kcm_ccdb *db,
-+                                           struct cli_creds *client,
-+                                           const char *name);
-+errno_t kcm_ccdb_getbyname_recv(struct tevent_req *req,
-+                                TALLOC_CTX *mem_ctx,
-+                                struct kcm_ccache **_cc);
-+
-+/*
-+ * Retrieve a ccache by UUID
-+ *
-+ * If there is no such ccache, return EOK, but a NULL _cc pointer
-+ */
-+struct tevent_req *kcm_ccdb_getbyuuid_send(TALLOC_CTX *mem_ctx,
-+                                           struct tevent_context *ev,
-+                                           struct kcm_ccdb *db,
-+                                           struct cli_creds *client,
-+                                           uuid_t uuid);
-+errno_t kcm_ccdb_getbyuuid_recv(struct tevent_req *req,
-+                                TALLOC_CTX *mem_ctx,
-+                                struct kcm_ccache **_cc);
-+
-+/*
-+ * Retrieve the default ccache. If there is no default cache,
-+ * return EOK, but a NULL UUID.
-+ */
-+struct tevent_req *kcm_ccdb_get_default_send(TALLOC_CTX *mem_ctx,
-+                                             struct tevent_context *ev,
-+                                             struct kcm_ccdb *db,
-+                                             struct cli_creds *client);
-+errno_t kcm_ccdb_get_default_recv(struct tevent_req *req,
-+                                  uuid_t *uuid);
-+
-+/*
-+ * Translating name to UUID is often considerably faster than doing a full
-+ * CC retrieval, hence this function and the converse. If the UUID cannot
-+ * be found in the database, return ERR_KCM_CC_END
-+ */
-+struct tevent_req *kcm_ccdb_name_by_uuid_send(TALLOC_CTX *mem_ctx,
-+                                              struct tevent_context *ev,
-+                                              struct kcm_ccdb *db,
-+                                              struct cli_creds *client,
-+                                              uuid_t uuid);
-+errno_t kcm_ccdb_name_by_uuid_recv(struct tevent_req *req,
-+                                   TALLOC_CTX *mem_ctx,
-+                                   const char **_name);
-+
-+/*
-+ * Translating UUID to name is often considerably faster than doing a full
-+ * CC retrieval, hence this function and the converse. If the UUID cannot
-+ * be found in the database, return ERR_KCM_CC_END
-+ */
-+struct tevent_req *kcm_ccdb_uuid_by_name_send(TALLOC_CTX *mem_ctx,
-+                                              struct tevent_context *ev,
-+                                              struct kcm_ccdb *db,
-+                                              struct cli_creds *client,
-+                                              const char *name);
-+errno_t kcm_ccdb_uuid_by_name_recv(struct tevent_req *req,
-+                                   TALLOC_CTX *mem_ctx,
-+                                   uuid_t _uuid);
-+
-+/*
-+ * Set the default ccache. Passing a NULL UUID is a legal operation
-+ * that 'unsets' the default ccache.
-+ */
-+struct tevent_req *kcm_ccdb_set_default_send(TALLOC_CTX *mem_ctx,
-+                                             struct tevent_context *ev,
-+                                             struct kcm_ccdb *db,
-+                                             struct cli_creds *client,
-+                                             uuid_t uuid);
-+errno_t kcm_ccdb_set_default_recv(struct tevent_req *req);
-+
-+/*
-+ * Add a ccache to the database.
-+ */
-+struct tevent_req *kcm_ccdb_create_cc_send(TALLOC_CTX *mem_ctx,
-+                                           struct tevent_context *ev,
-+                                           struct kcm_ccdb *db,
-+                                           struct cli_creds *client,
-+                                           struct kcm_ccache *cc);
-+errno_t kcm_ccdb_create_cc_recv(struct tevent_req *req);
-+
-+/*
-+ * Modify cache properties in a db
-+ */
-+struct kcm_mod_ctx {
-+    int32_t kdc_offset;
-+    /* More settable properties (like name, when we support renames
-+     * will be added later
-+     */
-+};
-+
-+void kcm_mod_ctx_clear(struct kcm_mod_ctx *mod_ctx);
-+void kcm_mod_cc(struct kcm_ccache *cc, struct kcm_mod_ctx *mod_ctx);
-+
-+struct tevent_req *kcm_ccdb_mod_cc_send(TALLOC_CTX *mem_ctx,
-+                                        struct tevent_context *ev,
-+                                        struct kcm_ccdb *db,
-+                                        struct cli_creds *client,
-+                                        uuid_t uuid,
-+                                        struct kcm_mod_ctx *mod_cc);
-+errno_t kcm_ccdb_mod_cc_recv(struct tevent_req *req);
-+
-+/*
-+ * Store a credential in a cache
-+ */
-+struct tevent_req *kcm_ccdb_store_cred_blob_send(TALLOC_CTX *mem_ctx,
-+                                                 struct tevent_context *ev,
-+                                                 struct kcm_ccdb *db,
-+                                                 struct cli_creds *client,
-+                                                 uuid_t uuid,
-+                                                 struct sss_iobuf *cred_blob);
-+errno_t kcm_ccdb_store_cred_blob_recv(struct tevent_req *req);
-+
-+/*
-+ * Delete a ccache from the database
-+ */
-+struct tevent_req *kcm_ccdb_delete_cc_send(TALLOC_CTX *mem_ctx,
-+                                           struct tevent_context *ev,
-+                                           struct kcm_ccdb *db,
-+                                           struct cli_creds *client,
-+                                           uuid_t uuid);
-+errno_t kcm_ccdb_delete_cc_recv(struct tevent_req *req);
-+
-+void kcm_debug_uuid(uuid_t uuid);
-+
-+/*
-+ * The KCM clients are not allowed (except root) to create ccaches
-+ * with arbitrary names. Instead, we assert that the ccache name
-+ * begins with UID where UID is the stringified representation of
-+ * the client's UID number
-+ */
-+errno_t kcm_check_name(const char *name, struct cli_creds *client);
-+
-+#endif /* _KCMSRV_CCACHE_H_ */
-diff --git a/src/responder/kcm/kcmsrv_ccache_be.h b/src/responder/kcm/kcmsrv_ccache_be.h
-new file mode 100644
-index 0000000000000000000000000000000000000000..1bd2b6981e227675866e82e0a5389445cac4df66
---- /dev/null
-+++ b/src/responder/kcm/kcmsrv_ccache_be.h
-@@ -0,0 +1,204 @@
-+/*
-+   SSSD
-+
-+   KCM Server - the KCM ccache database interface
-+
-+   This file should only be included from the ccache.c module.
-+
-+   Copyright (C) Red Hat, 2016
-+
-+   This program is free software; you can redistribute it and/or modify
-+   it under the terms of the GNU General Public License as published by
-+   the Free Software Foundation; either version 3 of the License, or
-+   (at your option) any later version.
-+
-+   This program is distributed in the hope that it will be useful,
-+   but WITHOUT ANY WARRANTY; without even the implied warranty of
-+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+   GNU General Public License for more details.
-+
-+   You should have received a copy of the GNU General Public License
-+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
-+*/
-+
-+#ifndef _KCMSRV_CCACHE_BE_
-+#define _KCMSRV_CCACHE_BE_
-+
-+#include "config.h"
-+
-+#include <talloc.h>
-+#include "responder/kcm/kcmsrv_ccache.h"
-+
-+typedef errno_t
-+(*ccdb_init_fn)(struct kcm_ccdb *db);
-+
-+typedef struct tevent_req *
-+(*ccdb_nextid_send_fn)(TALLOC_CTX *mem_ctx,
-+                       struct tevent_context *ev,
-+                       struct kcm_ccdb *db,
-+                       struct cli_creds *client);
-+typedef errno_t
-+(*ccdb_nextid_recv_fn)(struct tevent_req *req,
-+                       unsigned int *_nextid);
-+
-+typedef struct tevent_req *
-+(*ccdb_set_default_send_fn)(TALLOC_CTX *mem_ctx,
-+                            struct tevent_context *ev,
-+                            struct kcm_ccdb *db,
-+                            struct cli_creds *client,
-+                            uuid_t uuid);
-+typedef errno_t
-+(*ccdb_set_default_recv_fn)(struct tevent_req *req);
-+
-+typedef struct tevent_req *
-+(*ccdb_get_default_send_fn)(TALLOC_CTX *mem_ctx,
-+                            struct tevent_context *ev,
-+                            struct kcm_ccdb *db,
-+                            struct cli_creds *client);
-+typedef errno_t
-+(*ccdb_get_default_recv_fn)(struct tevent_req *req,
-+                            uuid_t dfl);
-+
-+
-+typedef struct tevent_req *
-+(*ccdb_list_send_fn)(TALLOC_CTX *mem_ctx,
-+                     struct tevent_context *ev,
-+                     struct kcm_ccdb *db,
-+                     struct cli_creds *client);
-+typedef errno_t
-+(*ccdb_list_recv_fn)(struct tevent_req *req,
-+                     TALLOC_CTX *mem_ctx,
-+                     uuid_t **_uuid_list);
-+
-+typedef struct tevent_req *
-+(*ccdb_getbyname_send_fn)(TALLOC_CTX *mem_ctx,
-+                          struct tevent_context *ev,
-+                          struct kcm_ccdb *db,
-+                          struct cli_creds *client,
-+                          const char *name);
-+typedef errno_t
-+(*ccdb_getbyname_recv_fn)(struct tevent_req *req,
-+                          TALLOC_CTX *mem_ctx,
-+                          struct kcm_ccache **_cc);
-+
-+typedef struct tevent_req *
-+(*ccdb_getbyuuid_send_fn)(TALLOC_CTX *mem_ctx,
-+                          struct tevent_context *ev,
-+                          struct kcm_ccdb *db,
-+                          struct cli_creds *client,
-+                          uuid_t uuid);
-+typedef errno_t
-+(*ccdb_getbyuuid_recv_fn)(struct tevent_req *req,
-+                          TALLOC_CTX *mem_ctx,
-+                          struct kcm_ccache **_cc);
-+
-+typedef struct tevent_req *
-+(*ccdb_name_by_uuid_send_fn)(TALLOC_CTX *mem_ctx,
-+                             struct tevent_context *ev,
-+                             struct kcm_ccdb *db,
-+                             struct cli_creds *client,
-+                             uuid_t uuid);
-+typedef errno_t
-+(*ccdb_name_by_uuid_recv_fn)(struct tevent_req *req,
-+                             TALLOC_CTX *mem_ctx,
-+                             const char **_name);
-+
-+typedef struct tevent_req *
-+(*ccdb_uuid_by_name_send_fn)(TALLOC_CTX *mem_ctx,
-+                             struct tevent_context *ev,
-+                             struct kcm_ccdb *db,
-+                             struct cli_creds *client,
-+                             const char *name);
-+typedef errno_t
-+(*ccdb_uuid_by_name_recv_fn)(struct tevent_req *req,
-+                             TALLOC_CTX *mem_ctx,
-+                             uuid_t _uuid);
-+
-+typedef struct tevent_req *
-+(*ccdb_create_send_fn)(TALLOC_CTX *mem_ctx,
-+                       struct tevent_context *ev,
-+                       struct kcm_ccdb *db,
-+                       struct cli_creds *client,
-+                       struct kcm_ccache *cc);
-+typedef errno_t
-+(*ccdb_create_recv_fn)(struct tevent_req *req);
-+
-+typedef struct tevent_req *
-+(*ccdb_mod_send_fn)(TALLOC_CTX *mem_ctx,
-+                    struct tevent_context *ev,
-+                    struct kcm_ccdb *db,
-+                    struct cli_creds *client,
-+                    uuid_t uuid,
-+                    struct kcm_mod_ctx *mod_cc);
-+typedef errno_t
-+(*ccdb_mod_recv_fn)(struct tevent_req *req);
-+
-+typedef struct tevent_req *
-+(*kcm_ccdb_store_cred_blob_send_fn)(TALLOC_CTX *mem_ctx,
-+                                    struct tevent_context *ev,
-+                                    struct kcm_ccdb *db,
-+                                    struct cli_creds *client,
-+                                    uuid_t uuid,
-+                                    struct sss_iobuf *cred_blob);
-+typedef errno_t
-+(*kcm_ccdb_store_cred_blob_recv_fn)(struct tevent_req *req);
-+
-+typedef struct tevent_req *
-+(*ccdb_delete_send_fn)(TALLOC_CTX *mem_ctx,
-+                      struct tevent_context *ev,
-+                      struct kcm_ccdb *db,
-+                      struct cli_creds *client,
-+                      uuid_t uuid);
-+typedef errno_t
-+(*ccdb_delete_recv_fn)(struct tevent_req *req);
-+
-+/*
-+ * Each ccache back end (for example memory or secrets) must implement
-+ * all these functions. The functions are wrapped by the kcm_ccdb
-+ * interface that performs additional sanity checks or contains shared
-+ * logic such as access checks but in general doesn't assume anything
-+ * about how the operations work.
-+ */
-+struct kcm_ccdb_ops {
-+    ccdb_init_fn init;
-+
-+    ccdb_nextid_send_fn nextid_send;
-+    ccdb_nextid_recv_fn nextid_recv;
-+
-+    ccdb_set_default_send_fn set_default_send;
-+    ccdb_set_default_recv_fn set_default_recv;
-+
-+    ccdb_get_default_send_fn get_default_send;
-+    ccdb_get_default_recv_fn get_default_recv;
-+
-+    ccdb_list_send_fn list_send;
-+    ccdb_list_recv_fn list_recv;
-+
-+    ccdb_getbyname_send_fn getbyname_send;
-+    ccdb_getbyname_recv_fn getbyname_recv;
-+
-+    ccdb_getbyuuid_send_fn getbyuuid_send;
-+    ccdb_getbyuuid_recv_fn getbyuuid_recv;
-+
-+    ccdb_name_by_uuid_send_fn name_by_uuid_send;
-+    ccdb_name_by_uuid_recv_fn name_by_uuid_recv;
-+
-+    ccdb_uuid_by_name_send_fn uuid_by_name_send;
-+    ccdb_uuid_by_name_recv_fn uuid_by_name_recv;
-+
-+    ccdb_create_send_fn create_send;
-+    ccdb_create_recv_fn create_recv;
-+
-+    ccdb_mod_send_fn mod_send;
-+    ccdb_mod_recv_fn mod_recv;
-+
-+    kcm_ccdb_store_cred_blob_send_fn store_cred_send;
-+    kcm_ccdb_store_cred_blob_recv_fn store_cred_recv;
-+
-+    ccdb_delete_send_fn delete_send;
-+    ccdb_delete_recv_fn delete_recv;
-+};
-+
-+extern const struct kcm_ccdb_ops ccdb_mem_ops;
-+
-+#endif /* _KCMSRV_CCACHE_BE_ */
-diff --git a/src/responder/kcm/kcmsrv_ccache_pvt.h b/src/responder/kcm/kcmsrv_ccache_pvt.h
-new file mode 100644
-index 0000000000000000000000000000000000000000..0cc24c2b8cd4d44080d2aa4384f7b2a73520c5a0
---- /dev/null
-+++ b/src/responder/kcm/kcmsrv_ccache_pvt.h
-@@ -0,0 +1,62 @@
-+/*
-+   SSSD
-+
-+   KCM Server - the KCM ccache operations - private structures
-+
-+   Should be accessed only from the ccache layer.
-+
-+   Copyright (C) Red Hat, 2016
-+
-+   This program is free software; you can redistribute it and/or modify
-+   it under the terms of the GNU General Public License as published by
-+   the Free Software Foundation; either version 3 of the License, or
-+   (at your option) any later version.
-+
-+   This program is distributed in the hope that it will be useful,
-+   but WITHOUT ANY WARRANTY; without even the implied warranty of
-+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+   GNU General Public License for more details.
-+
-+   You should have received a copy of the GNU General Public License
-+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
-+*/
-+#ifndef _KCMSRV_CCACHE_PVT_H
-+#define _KCMSRV_CCACHE_PVT_H
-+
-+#include "responder/kcm/kcmsrv_ccache.h"
-+#include "responder/kcm/kcmsrv_ccache_be.h"
-+
-+struct kcm_ccache_owner {
-+    uid_t uid;
-+    gid_t gid;
-+};
-+
-+struct kcm_cred {
-+    struct sss_iobuf *cred_blob;
-+    /* Randomly generated 16 bytes */
-+    uuid_t uuid;
-+
-+    struct kcm_cred *next;
-+    struct kcm_cred *prev;
-+};
-+
-+struct kcm_ccdb {
-+    enum kcm_ccdb_be cc_be_type;
-+    struct tevent_context *ev;
-+
-+    void *db_handle;
-+    const struct kcm_ccdb_ops *ops;
-+};
-+
-+struct kcm_ccache {
-+    const char *name;
-+    struct kcm_ccache_owner owner;
-+    uuid_t uuid;
-+
-+    krb5_principal client;
-+    int32_t kdc_offset;
-+
-+    struct kcm_cred *creds;
-+};
-+
-+#endif /* _KCMSRV_CCACHE_PVT_H */
-diff --git a/src/responder/kcm/kcmsrv_pvt.h b/src/responder/kcm/kcmsrv_pvt.h
-index fd1fd9fa32d59a323d465def68999f24f84e3923..a29680246c1e616da75e1bbff951ce2fad66fb65 100644
---- a/src/responder/kcm/kcmsrv_pvt.h
-+++ b/src/responder/kcm/kcmsrv_pvt.h
-@@ -46,6 +46,7 @@ struct kcm_data {
-  */
- struct kcm_resp_ctx {
-     krb5_context k5c;
-+    struct kcm_ccdb *db;
- };
- 
- /*
--- 
-2.9.3
-
diff --git a/SOURCES/0024-NSS-add-_EX-version-of-some-requests.patch b/SOURCES/0024-NSS-add-_EX-version-of-some-requests.patch
new file mode 100644
index 0000000..e97d92d
--- /dev/null
+++ b/SOURCES/0024-NSS-add-_EX-version-of-some-requests.patch
@@ -0,0 +1,606 @@
+From 8c6cb61cc65af7d3f243476dca66fb8f4750df80 Mon Sep 17 00:00:00 2001
+From: Sumit Bose <sbose@redhat.com>
+Date: Wed, 11 Oct 2017 14:54:54 +0200
+Subject: [PATCH 24/31] NSS: add *_EX version of some requests
+
+To be able to send the flags to the SSSD responder new request types
+with an _EX postfix are added which expect and additional 32bit flag
+field after the name or the id of the requested object.
+
+Related to https://pagure.io/SSSD/sssd/issue/2478
+
+Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
+(cherry picked from commit cf93f7c2f2031078bbbff095dae01eb4f8deff85)
+---
+ src/responder/nss/nss_cmd.c       |  76 +++++++++++++++++++++-----
+ src/responder/nss/nss_protocol.c  |  81 ++++++++++++++++++++++++++++
+ src/responder/nss/nss_protocol.h  |   8 +++
+ src/sss_client/idmap/sss_nss_ex.c | 110 +++++++++++++++++++++++++++-----------
+ src/sss_client/sss_cli.h          |   7 +++
+ 5 files changed, 237 insertions(+), 45 deletions(-)
+
+diff --git a/src/responder/nss/nss_cmd.c b/src/responder/nss/nss_cmd.c
+index ebf66dfe0444b83aed20d58d36ddf70d2f4fa1f9..974eaccc93cea3a330007735676da69eb9b84141 100644
+--- a/src/responder/nss/nss_cmd.c
++++ b/src/responder/nss/nss_cmd.c
+@@ -54,6 +54,7 @@ static void nss_getby_done(struct tevent_req *subreq);
+ static void nss_getlistby_done(struct tevent_req *subreq);
+ 
+ static errno_t nss_getby_name(struct cli_ctx *cli_ctx,
++                              bool ex_version,
+                               enum cache_req_type type,
+                               const char **attrs,
+                               enum sss_mc_type memcache,
+@@ -64,6 +65,7 @@ static errno_t nss_getby_name(struct cli_ctx *cli_ctx,
+     struct tevent_req *subreq;
+     const char *rawname;
+     errno_t ret;
++    uint32_t flags = 0;
+ 
+     cmd_ctx = nss_cmd_ctx_create(cli_ctx, cli_ctx, type, fill_fn);
+     if (cmd_ctx == NULL) {
+@@ -71,7 +73,11 @@ static errno_t nss_getby_name(struct cli_ctx *cli_ctx,
+         goto done;
+     }
+ 
+-    ret = nss_protocol_parse_name(cli_ctx, &rawname);
++    if (ex_version) {
++        ret = nss_protocol_parse_name_ex(cli_ctx, &rawname, &flags);
++    } else {
++        ret = nss_protocol_parse_name(cli_ctx, &rawname);
++    }
+     if (ret != EOK) {
+         DEBUG(SSSDBG_CRIT_FAILURE, "Invalid request message!\n");
+         goto done;
+@@ -108,6 +114,7 @@ done:
+ }
+ 
+ static errno_t nss_getby_id(struct cli_ctx *cli_ctx,
++                            bool ex_version,
+                             enum cache_req_type type,
+                             const char **attrs,
+                             enum sss_mc_type memcache,
+@@ -118,6 +125,7 @@ static errno_t nss_getby_id(struct cli_ctx *cli_ctx,
+     struct tevent_req *subreq;
+     uint32_t id;
+     errno_t ret;
++    uint32_t flags = 0;
+ 
+     cmd_ctx = nss_cmd_ctx_create(cli_ctx, cli_ctx, type, fill_fn);
+     if (cmd_ctx == NULL) {
+@@ -125,7 +133,11 @@ static errno_t nss_getby_id(struct cli_ctx *cli_ctx,
+         goto done;
+     }
+ 
+-    ret = nss_protocol_parse_id(cli_ctx, &id);
++    if (ex_version) {
++        ret = nss_protocol_parse_id_ex(cli_ctx, &id, &flags);
++    } else {
++        ret = nss_protocol_parse_id(cli_ctx, &id);
++    }
+     if (ret != EOK) {
+         DEBUG(SSSDBG_CRIT_FAILURE, "Invalid request message!\n");
+         goto done;
+@@ -766,14 +778,26 @@ static errno_t nss_endent(struct cli_ctx *cli_ctx,
+ 
+ static errno_t nss_cmd_getpwnam(struct cli_ctx *cli_ctx)
+ {
+-    return nss_getby_name(cli_ctx, CACHE_REQ_USER_BY_NAME, NULL, SSS_MC_PASSWD,
+-                          nss_protocol_fill_pwent);
++    return nss_getby_name(cli_ctx, false, CACHE_REQ_USER_BY_NAME, NULL,
++                          SSS_MC_PASSWD, nss_protocol_fill_pwent);
+ }
+ 
+ static errno_t nss_cmd_getpwuid(struct cli_ctx *cli_ctx)
+ {
+-    return nss_getby_id(cli_ctx, CACHE_REQ_USER_BY_ID, NULL, SSS_MC_PASSWD,
+-                        nss_protocol_fill_pwent);
++    return nss_getby_id(cli_ctx, false, CACHE_REQ_USER_BY_ID, NULL,
++                        SSS_MC_PASSWD, nss_protocol_fill_pwent);
++}
++
++static errno_t nss_cmd_getpwnam_ex(struct cli_ctx *cli_ctx)
++{
++    return nss_getby_name(cli_ctx, true, CACHE_REQ_USER_BY_NAME, NULL,
++                          SSS_MC_PASSWD, nss_protocol_fill_pwent);
++}
++
++static errno_t nss_cmd_getpwuid_ex(struct cli_ctx *cli_ctx)
++{
++    return nss_getby_id(cli_ctx, true, CACHE_REQ_USER_BY_ID, NULL,
++                        SSS_MC_PASSWD, nss_protocol_fill_pwent);
+ }
+ 
+ static errno_t nss_cmd_setpwent(struct cli_ctx *cli_ctx)
+@@ -809,16 +833,29 @@ static errno_t nss_cmd_endpwent(struct cli_ctx *cli_ctx)
+ 
+ static errno_t nss_cmd_getgrnam(struct cli_ctx *cli_ctx)
+ {
+-    return nss_getby_name(cli_ctx, CACHE_REQ_GROUP_BY_NAME, NULL, SSS_MC_GROUP,
+-                          nss_protocol_fill_grent);
++    return nss_getby_name(cli_ctx, false, CACHE_REQ_GROUP_BY_NAME, NULL,
++                          SSS_MC_GROUP, nss_protocol_fill_grent);
+ }
+ 
+ static errno_t nss_cmd_getgrgid(struct cli_ctx *cli_ctx)
+ {
+-    return nss_getby_id(cli_ctx, CACHE_REQ_GROUP_BY_ID, NULL, SSS_MC_GROUP,
+-                        nss_protocol_fill_grent);
++    return nss_getby_id(cli_ctx, false, CACHE_REQ_GROUP_BY_ID, NULL,
++                        SSS_MC_GROUP, nss_protocol_fill_grent);
+ }
+ 
++static errno_t nss_cmd_getgrnam_ex(struct cli_ctx *cli_ctx)
++{
++    return nss_getby_name(cli_ctx, true, CACHE_REQ_GROUP_BY_NAME, NULL,
++                          SSS_MC_GROUP, nss_protocol_fill_grent);
++}
++
++static errno_t nss_cmd_getgrgid_ex(struct cli_ctx *cli_ctx)
++{
++    return nss_getby_id(cli_ctx, true, CACHE_REQ_GROUP_BY_ID, NULL,
++                        SSS_MC_GROUP, nss_protocol_fill_grent);
++}
++
++
+ static errno_t nss_cmd_setgrent(struct cli_ctx *cli_ctx)
+ {
+     struct nss_ctx *nss_ctx;
+@@ -852,7 +889,13 @@ static errno_t nss_cmd_endgrent(struct cli_ctx *cli_ctx)
+ 
+ static errno_t nss_cmd_initgroups(struct cli_ctx *cli_ctx)
+ {
+-    return nss_getby_name(cli_ctx, CACHE_REQ_INITGROUPS, NULL,
++    return nss_getby_name(cli_ctx, false, CACHE_REQ_INITGROUPS, NULL,
++                          SSS_MC_INITGROUPS, nss_protocol_fill_initgr);
++}
++
++static errno_t nss_cmd_initgroups_ex(struct cli_ctx *cli_ctx)
++{
++    return nss_getby_name(cli_ctx, true, CACHE_REQ_INITGROUPS, NULL,
+                           SSS_MC_INITGROUPS, nss_protocol_fill_initgr);
+ }
+ 
+@@ -943,7 +986,7 @@ static errno_t nss_cmd_getsidbyname(struct cli_ctx *cli_ctx)
+ {
+     const char *attrs[] = { SYSDB_SID_STR, NULL };
+ 
+-    return nss_getby_name(cli_ctx, CACHE_REQ_OBJECT_BY_NAME, attrs,
++    return nss_getby_name(cli_ctx, false, CACHE_REQ_OBJECT_BY_NAME, attrs,
+                           SSS_MC_NONE, nss_protocol_fill_sid);
+ }
+ 
+@@ -951,7 +994,7 @@ static errno_t nss_cmd_getsidbyid(struct cli_ctx *cli_ctx)
+ {
+     const char *attrs[] = { SYSDB_SID_STR, NULL };
+ 
+-    return nss_getby_id(cli_ctx, CACHE_REQ_OBJECT_BY_ID, attrs,
++    return nss_getby_id(cli_ctx, false, CACHE_REQ_OBJECT_BY_ID, attrs,
+                         SSS_MC_NONE, nss_protocol_fill_sid);
+ }
+ 
+@@ -1006,7 +1049,7 @@ static errno_t nss_cmd_getorigbyname(struct cli_ctx *cli_ctx)
+         attrs = defattrs;
+     }
+ 
+-    return nss_getby_name(cli_ctx, CACHE_REQ_OBJECT_BY_NAME, attrs,
++    return nss_getby_name(cli_ctx, false, CACHE_REQ_OBJECT_BY_NAME, attrs,
+                           SSS_MC_NONE, nss_protocol_fill_orig);
+ }
+ 
+@@ -1051,6 +1094,11 @@ struct sss_cmd_table *get_nss_cmds(void)
+         { SSS_NSS_GETORIGBYNAME, nss_cmd_getorigbyname },
+         { SSS_NSS_GETNAMEBYCERT, nss_cmd_getnamebycert },
+         { SSS_NSS_GETLISTBYCERT, nss_cmd_getlistbycert },
++        { SSS_NSS_GETPWNAM_EX, nss_cmd_getpwnam_ex },
++        { SSS_NSS_GETPWUID_EX, nss_cmd_getpwuid_ex },
++        { SSS_NSS_GETGRNAM_EX, nss_cmd_getgrnam_ex },
++        { SSS_NSS_GETGRGID_EX, nss_cmd_getgrgid_ex },
++        { SSS_NSS_INITGR_EX, nss_cmd_initgroups_ex },
+         { SSS_CLI_NULL, NULL }
+     };
+ 
+diff --git a/src/responder/nss/nss_protocol.c b/src/responder/nss/nss_protocol.c
+index 06fc4d00a7877a7990402f4f2633ddc0bd640b41..17bfc4f4e71960a72e9e04622eac95b94a865ec7 100644
+--- a/src/responder/nss/nss_protocol.c
++++ b/src/responder/nss/nss_protocol.c
+@@ -134,6 +134,61 @@ nss_protocol_parse_name(struct cli_ctx *cli_ctx, const char **_rawname)
+ }
+ 
+ errno_t
++nss_protocol_parse_name_ex(struct cli_ctx *cli_ctx, const char **_rawname,
++                           uint32_t *_flags)
++{
++    struct cli_protocol *pctx;
++    const char *rawname;
++    uint8_t *body;
++    size_t blen;
++    uint8_t *p;
++    uint32_t flags;
++
++    pctx = talloc_get_type(cli_ctx->protocol_ctx, struct cli_protocol);
++
++    sss_packet_get_body(pctx->creq->in, &body, &blen);
++
++    if (blen < 1 + sizeof(uint32_t)) {
++        DEBUG(SSSDBG_CRIT_FAILURE, "Body too short!\n");
++        return EINVAL;
++    }
++
++    /* If first argument not terminated fail. */
++    if (body[blen - 1 - sizeof(uint32_t)] != '\0') {
++        DEBUG(SSSDBG_CRIT_FAILURE, "Body is not null terminated!\n");
++        return EINVAL;
++    }
++
++    p = memchr(body, '\0', blen);
++
++    /* If the body isn't valid UTF-8, fail */
++    if (!sss_utf8_check(body, (p - body))) {
++        DEBUG(SSSDBG_CRIT_FAILURE, "First argument is not UTF-8 string!\n");
++        return EINVAL;
++    }
++
++    rawname = (const char *)body;
++    if (rawname[0] == '\0') {
++        DEBUG(SSSDBG_CRIT_FAILURE, "An empty name was provided!\n");
++        return EINVAL;
++    }
++
++    p++;
++    if ((p - body) + sizeof(uint32_t) != blen) {
++        DEBUG(SSSDBG_CRIT_FAILURE, "Body has unexpected size!\n");
++        return EINVAL;
++    }
++
++    SAFEALIGN_COPY_UINT32(&flags, p, NULL);
++    p += sizeof(uint32_t);
++
++    *_rawname = rawname;
++    *_flags = flags;
++
++    return EOK;
++}
++
++errno_t
+ nss_protocol_parse_id(struct cli_ctx *cli_ctx, uint32_t *_id)
+ {
+     struct cli_protocol *pctx;
+@@ -157,6 +212,32 @@ nss_protocol_parse_id(struct cli_ctx *cli_ctx, uint32_t *_id)
+ }
+ 
+ errno_t
++nss_protocol_parse_id_ex(struct cli_ctx *cli_ctx, uint32_t *_id,
++                         uint32_t *_flags)
++{
++    struct cli_protocol *pctx;
++    uint8_t *body;
++    size_t blen;
++    uint32_t id;
++    uint32_t flags;
++
++    pctx = talloc_get_type(cli_ctx->protocol_ctx, struct cli_protocol);
++
++    sss_packet_get_body(pctx->creq->in, &body, &blen);
++
++    if (blen != 2 * sizeof(uint32_t)) {
++        return EINVAL;
++    }
++
++    SAFEALIGN_COPY_UINT32(&id, body, NULL);
++    SAFEALIGN_COPY_UINT32(&flags, body + sizeof(uint32_t), NULL);
++
++    *_id = id;
++
++    return EOK;
++}
++
++errno_t
+ nss_protocol_parse_limit(struct cli_ctx *cli_ctx, uint32_t *_limit)
+ {
+     return nss_protocol_parse_id(cli_ctx, _limit);
+diff --git a/src/responder/nss/nss_protocol.h b/src/responder/nss/nss_protocol.h
+index 417b0891615dcb8771d49f7b2f4d276342ca3150..ca5b040237dc18acdca9a7a3a7a7dbb64265aa95 100644
+--- a/src/responder/nss/nss_protocol.h
++++ b/src/responder/nss/nss_protocol.h
+@@ -89,9 +89,17 @@ errno_t
+ nss_protocol_parse_name(struct cli_ctx *cli_ctx, const char **_rawname);
+ 
+ errno_t
++nss_protocol_parse_name_ex(struct cli_ctx *cli_ctx, const char **_rawname,
++                           uint32_t *_flags);
++
++errno_t
+ nss_protocol_parse_id(struct cli_ctx *cli_ctx, uint32_t *_id);
+ 
+ errno_t
++nss_protocol_parse_id_ex(struct cli_ctx *cli_ctx, uint32_t *_id,
++                         uint32_t *_flags);
++
++errno_t
+ nss_protocol_parse_limit(struct cli_ctx *cli_ctx, uint32_t *_limit);
+ 
+ errno_t
+diff --git a/src/sss_client/idmap/sss_nss_ex.c b/src/sss_client/idmap/sss_nss_ex.c
+index 582d1373ec35305cf128e04fd3d705457d304789..dc7610a4e528b5126f0d25d84cd3c1a22f683b75 100644
+--- a/src/sss_client/idmap/sss_nss_ex.c
++++ b/src/sss_client/idmap/sss_nss_ex.c
+@@ -61,31 +61,37 @@ errno_t sss_nss_mc_get(struct nss_input *inp)
+ {
+     switch(inp->cmd) {
+     case SSS_NSS_GETPWNAM:
+-        return sss_nss_mc_getpwnam(inp->input.name, (inp->rd.len - 1),
++    case SSS_NSS_GETPWNAM_EX:
++        return sss_nss_mc_getpwnam(inp->input.name, strlen(inp->input.name),
+                                    inp->result.pwrep.result,
+                                    inp->result.pwrep.buffer,
+                                    inp->result.pwrep.buflen);
+         break;
+     case SSS_NSS_GETPWUID:
++    case SSS_NSS_GETPWUID_EX:
+         return sss_nss_mc_getpwuid(inp->input.uid,
+                                    inp->result.pwrep.result,
+                                    inp->result.pwrep.buffer,
+                                    inp->result.pwrep.buflen);
+         break;
+     case SSS_NSS_GETGRNAM:
+-        return sss_nss_mc_getgrnam(inp->input.name, (inp->rd.len - 1),
++    case SSS_NSS_GETGRNAM_EX:
++        return sss_nss_mc_getgrnam(inp->input.name, strlen(inp->input.name),
+                                    inp->result.grrep.result,
+                                    inp->result.grrep.buffer,
+                                    inp->result.grrep.buflen);
+         break;
+     case SSS_NSS_GETGRGID:
++    case SSS_NSS_GETGRGID_EX:
+         return sss_nss_mc_getgrgid(inp->input.gid,
+                                    inp->result.grrep.result,
+                                    inp->result.grrep.buffer,
+                                    inp->result.grrep.buflen);
+         break;
+     case SSS_NSS_INITGR:
+-        return sss_nss_mc_initgroups_dyn(inp->input.name, (inp->rd.len - 1),
++    case SSS_NSS_INITGR_EX:
++        return sss_nss_mc_initgroups_dyn(inp->input.name,
++                                         strlen(inp->input.name),
+                                          -1 /* currently ignored */,
+                                          inp->result.initgrrep.start,
+                                          inp->result.initgrrep.ngroups,
+@@ -163,7 +169,7 @@ int sss_get_ex(struct nss_input *inp, uint32_t flags, unsigned int timeout)
+         goto out;
+     }
+ 
+-    if (inp->cmd == SSS_NSS_INITGR) {
++    if (inp->cmd == SSS_NSS_INITGR || inp->cmd == SSS_NSS_INITGR_EX) {
+         if ((*(inp->result.initgrrep.ngroups) - *(inp->result.initgrrep.start))
+                     < num_results) {
+             new_groups = realloc(inp->result.initgrrep.groups,
+@@ -198,15 +204,24 @@ int sss_get_ex(struct nss_input *inp, uint32_t flags, unsigned int timeout)
+     }
+ 
+     len = replen - 8;
+-    if (inp->cmd == SSS_NSS_GETPWNAM || inp->cmd == SSS_NSS_GETPWUID) {
++
++    switch(inp->cmd) {
++    case SSS_NSS_GETPWNAM:
++    case SSS_NSS_GETPWUID:
++    case SSS_NSS_GETPWNAM_EX:
++    case SSS_NSS_GETPWUID_EX:
+         ret = sss_nss_getpw_readrep(&(inp->result.pwrep), repbuf+8, &len);
+-    } else if (inp->cmd == SSS_NSS_GETGRNAM || inp->cmd == SSS_NSS_GETGRGID) {
++        break;
++    case SSS_NSS_GETGRNAM:
++    case SSS_NSS_GETGRGID:
++    case SSS_NSS_GETGRNAM_EX:
++    case SSS_NSS_GETGRGID_EX:
+         ret = sss_nss_getgr_readrep(&(inp->result.grrep), repbuf+8, &len);
+-    } else {
++        break;
++    default:
+         ret = EINVAL;
+-        goto out;
+     }
+-    if (ret) {
++    if (ret != 0) {
+         goto out;
+     }
+ 
+@@ -223,6 +238,39 @@ out:
+     return ret;
+ }
+ 
++static int make_name_flag_req_data(const char *name, uint32_t flags,
++                                   struct sss_cli_req_data *rd)
++{
++    size_t len;
++    size_t name_len;
++    uint8_t *data;
++    int ret;
++
++    if (name == NULL) {
++        return EINVAL;
++    }
++
++    ret = sss_strnlen(name, SSS_NAME_MAX, &name_len);
++    if (ret != 0) {
++        return ret;
++    }
++    name_len++;
++
++    len = name_len + sizeof(uint32_t);
++    data = malloc(len);
++    if (data == NULL) {
++        return ENOMEM;
++    }
++
++    memcpy(data, name, name_len);
++    SAFEALIGN_COPY_UINT32(data + name_len, &flags, NULL);
++
++    rd->len = len;
++    rd->data = data;
++
++    return 0;
++}
++
+ int sss_nss_getpwnam_timeout(const char *name, struct passwd *pwd,
+                              char *buffer, size_t buflen,
+                              struct passwd **result,
+@@ -231,8 +279,7 @@ int sss_nss_getpwnam_timeout(const char *name, struct passwd *pwd,
+     int ret;
+     struct nss_input inp = {
+         .input.name = name,
+-        .cmd = SSS_NSS_GETPWNAM,
+-        .rd.data = name,
++        .cmd = SSS_NSS_GETPWNAM_EX,
+         .result.pwrep.result = pwd,
+         .result.pwrep.buffer = buffer,
+         .result.pwrep.buflen = buflen};
+@@ -241,15 +288,15 @@ int sss_nss_getpwnam_timeout(const char *name, struct passwd *pwd,
+         return ERANGE;
+     }
+ 
+-    ret = sss_strnlen(name, SSS_NAME_MAX, &inp.rd.len);
++    ret = make_name_flag_req_data(name, flags, &inp.rd);
+     if (ret != 0) {
+-        return EINVAL;
++        return ret;
+     }
+-    inp.rd.len++;
+ 
+     *result = NULL;
+ 
+     ret = sss_get_ex(&inp, flags, timeout);
++    free(discard_const(inp.rd.data));
+     if (ret == 0) {
+         *result = inp.result.pwrep.result;
+     }
+@@ -262,12 +309,12 @@ int sss_nss_getpwuid_timeout(uid_t uid, struct passwd *pwd,
+                              uint32_t flags, unsigned int timeout)
+ {
+     int ret;
+-    uint32_t user_uid = uid;
++    uint32_t req_data[2];
+     struct nss_input inp = {
+         .input.uid = uid,
+-        .cmd = SSS_NSS_GETPWUID,
+-        .rd.len = sizeof(uint32_t),
+-        .rd.data = &user_uid,
++        .cmd = SSS_NSS_GETPWUID_EX,
++        .rd.len = 2 * sizeof(uint32_t),
++        .rd.data = &req_data,
+         .result.pwrep.result = pwd,
+         .result.pwrep.buffer = buffer,
+         .result.pwrep.buflen = buflen};
+@@ -276,6 +323,8 @@ int sss_nss_getpwuid_timeout(uid_t uid, struct passwd *pwd,
+         return ERANGE;
+     }
+ 
++    SAFEALIGN_COPY_UINT32(&req_data[0], &uid, NULL);
++    SAFEALIGN_COPY_UINT32(&req_data[1], &flags, NULL);
+     *result = NULL;
+ 
+     ret = sss_get_ex(&inp, flags, timeout);
+@@ -292,8 +341,7 @@ int sss_nss_getgrnam_timeout(const char *name, struct group *grp,
+     int ret;
+     struct nss_input inp = {
+         .input.name = name,
+-        .cmd = SSS_NSS_GETGRNAM,
+-        .rd.data = name,
++        .cmd = SSS_NSS_GETGRNAM_EX,
+         .result.grrep.result = grp,
+         .result.grrep.buffer = buffer,
+         .result.grrep.buflen = buflen};
+@@ -302,15 +350,15 @@ int sss_nss_getgrnam_timeout(const char *name, struct group *grp,
+         return ERANGE;
+     }
+ 
+-    ret = sss_strnlen(name, SSS_NAME_MAX, &inp.rd.len);
++    ret = make_name_flag_req_data(name, flags, &inp.rd);
+     if (ret != 0) {
+-        return EINVAL;
++        return ret;
+     }
+-    inp.rd.len++;
+ 
+     *result = NULL;
+ 
+     ret = sss_get_ex(&inp, flags, timeout);
++    free(discard_const(inp.rd.data));
+     if (ret == 0) {
+         *result = inp.result.grrep.result;
+     }
+@@ -322,12 +370,12 @@ int sss_nss_getgrgid_timeout(gid_t gid, struct group *grp,
+                              uint32_t flags, unsigned int timeout)
+ {
+     int ret;
+-    uint32_t group_gid = gid;
++    uint32_t req_data[2];
+     struct nss_input inp = {
+         .input.gid = gid,
+-        .cmd = SSS_NSS_GETGRGID,
+-        .rd.len = sizeof(uint32_t),
+-        .rd.data = &group_gid,
++        .cmd = SSS_NSS_GETGRGID_EX,
++        .rd.len = 2 * sizeof(uint32_t),
++        .rd.data = &req_data,
+         .result.grrep.result = grp,
+         .result.grrep.buffer = buffer,
+         .result.grrep.buflen = buflen};
+@@ -336,6 +384,8 @@ int sss_nss_getgrgid_timeout(gid_t gid, struct group *grp,
+         return ERANGE;
+     }
+ 
++    SAFEALIGN_COPY_UINT32(&req_data[0], &gid, NULL);
++    SAFEALIGN_COPY_UINT32(&req_data[1], &flags, NULL);
+     *result = NULL;
+ 
+     ret = sss_get_ex(&inp, flags, timeout);
+@@ -355,18 +405,16 @@ int sss_nss_getgrouplist_timeout(const char *name, gid_t group,
+     long int start = 1;
+     struct nss_input inp = {
+         .input.name = name,
+-        .cmd = SSS_NSS_INITGR,
+-        .rd.data = name};
++        .cmd = SSS_NSS_INITGR_EX};
+ 
+     if (groups == NULL || ngroups == NULL || *ngroups == 0) {
+         return EINVAL;
+     }
+ 
+-    ret = sss_strnlen(name, SSS_NAME_MAX, &inp.rd.len);
++    ret = make_name_flag_req_data(name, flags, &inp.rd);
+     if (ret != 0) {
+         return ret;
+     }
+-    inp.rd.len++;
+ 
+     new_ngroups = MAX(1, *ngroups);
+     new_groups = malloc(new_ngroups * sizeof(gid_t));
+diff --git a/src/sss_client/sss_cli.h b/src/sss_client/sss_cli.h
+index 5329651a9385d138b8ea7237cb5cf4e2b8e5f371..9d2cc00c9957f5680548461129e3e6b777da5091 100644
+--- a/src/sss_client/sss_cli.h
++++ b/src/sss_client/sss_cli.h
+@@ -79,6 +79,9 @@ enum sss_cli_command {
+     SSS_NSS_GETPWENT       = 0x0014,
+     SSS_NSS_ENDPWENT       = 0x0015,
+ 
++    SSS_NSS_GETPWNAM_EX    = 0x0019,
++    SSS_NSS_GETPWUID_EX    = 0x001A,
++
+ /* group */
+ 
+     SSS_NSS_GETGRNAM       = 0x0021,
+@@ -88,6 +91,10 @@ enum sss_cli_command {
+     SSS_NSS_ENDGRENT       = 0x0025,
+     SSS_NSS_INITGR         = 0x0026,
+ 
++    SSS_NSS_GETGRNAM_EX    = 0x0029,
++    SSS_NSS_GETGRGID_EX    = 0x002A,
++    SSS_NSS_INITGR_EX      = 0x002E,
++
+ #if 0
+ /* aliases */
+ 
+-- 
+2.13.6
+
diff --git a/SOURCES/0025-KCM-Add-a-in-memory-credential-storage.patch b/SOURCES/0025-KCM-Add-a-in-memory-credential-storage.patch
deleted file mode 100644
index 6bfffe6..0000000
--- a/SOURCES/0025-KCM-Add-a-in-memory-credential-storage.patch
+++ /dev/null
@@ -1,907 +0,0 @@
-From e7aa9061532b1ac139e155e7e9881c2447675e3c Mon Sep 17 00:00:00 2001
-From: Jakub Hrozek <jhrozek@redhat.com>
-Date: Tue, 7 Mar 2017 13:49:43 +0100
-Subject: [PATCH 25/36] KCM: Add a in-memory credential storage
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Implements a simple back end for the ccache module that lets the KCM
-server store credentials directly in memory.
-
-Reviewed-by: Michal Židek <mzidek@redhat.com>
-Reviewed-by: Simo Sorce <simo@redhat.com>
----
- Makefile.am                           |   1 +
- src/responder/kcm/kcm.c               |  13 +-
- src/responder/kcm/kcmsrv_ccache.c     |   2 +-
- src/responder/kcm/kcmsrv_ccache_mem.c | 805 ++++++++++++++++++++++++++++++++++
- 4 files changed, 817 insertions(+), 4 deletions(-)
- create mode 100644 src/responder/kcm/kcmsrv_ccache_mem.c
-
-diff --git a/Makefile.am b/Makefile.am
-index a2b9dc49e95fa2d025f5174d2902866fab180a78..5605c1a53c44fd9e83394e80b7f71828df1d39b6 100644
---- a/Makefile.am
-+++ b/Makefile.am
-@@ -1492,6 +1492,7 @@ sssd_kcm_SOURCES = \
-     src/responder/kcm/kcm.c \
-     src/responder/kcm/kcmsrv_cmd.c \
-     src/responder/kcm/kcmsrv_ccache.c \
-+    src/responder/kcm/kcmsrv_ccache_mem.c \
-     src/util/sss_sockets.c \
-     src/util/sss_krb5.c \
-     src/util/sss_iobuf.c \
-diff --git a/src/responder/kcm/kcm.c b/src/responder/kcm/kcm.c
-index 90a6999c5e39d48a1a2ea8168d171612a65077d5..2c12ef215ce3967df183e51c20590c5f439d278f 100644
---- a/src/responder/kcm/kcm.c
-+++ b/src/responder/kcm/kcm.c
-@@ -22,9 +22,9 @@
- #include "config.h"
- 
- #include <popt.h>
--#include <krb5/krb5.h>
- 
- #include "responder/kcm/kcm.h"
-+#include "responder/kcm/kcmsrv_ccache.h"
- #include "responder/kcm/kcmsrv_pvt.h"
- #include "responder/common/responder.h"
- #include "util/util.h"
-@@ -110,7 +110,8 @@ static int kcm_data_destructor(void *ptr)
-     return 0;
- }
- 
--static struct kcm_resp_ctx *kcm_data_setup(TALLOC_CTX *mem_ctx)
-+static struct kcm_resp_ctx *kcm_data_setup(TALLOC_CTX *mem_ctx,
-+                                           struct tevent_context *ev)
- {
-     struct kcm_resp_ctx *kcm_data;
-     krb5_error_code kret;
-@@ -121,6 +122,12 @@ static struct kcm_resp_ctx *kcm_data_setup(TALLOC_CTX *mem_ctx)
-         return NULL;
-     }
- 
-+    kcm_data->db = kcm_ccdb_init(kcm_data, ev, CCDB_BE_MEMORY);
-+    if (kcm_data->db == NULL) {
-+        talloc_free(kcm_data);
-+        return NULL;
-+    }
-+
-     kret = krb5_init_context(&kcm_data->k5c);
-     if (kret != EOK) {
-         talloc_free(kcm_data);
-@@ -169,7 +176,7 @@ static int kcm_process_init(TALLOC_CTX *mem_ctx,
-         goto fail;
-     }
- 
--    kctx->kcm_data = kcm_data_setup(kctx);
-+    kctx->kcm_data = kcm_data_setup(kctx, ev);
-     if (kctx->kcm_data == NULL) {
-         DEBUG(SSSDBG_FATAL_FAILURE,
-               "fatal error initializing responder data\n");
-diff --git a/src/responder/kcm/kcmsrv_ccache.c b/src/responder/kcm/kcmsrv_ccache.c
-index 2c565b8378e3ec297faf655d3c48d7ab902713d3..2ae120269b0c62275ba2acdff6d6daa8b7077708 100644
---- a/src/responder/kcm/kcmsrv_ccache.c
-+++ b/src/responder/kcm/kcmsrv_ccache.c
-@@ -240,7 +240,7 @@ struct kcm_ccdb *kcm_ccdb_init(TALLOC_CTX *mem_ctx,
-     switch (cc_be) {
-     case CCDB_BE_MEMORY:
-         DEBUG(SSSDBG_FUNC_DATA, "KCM back end: memory\n");
--        /* Not implemented yet */
-+        ccdb->ops = &ccdb_mem_ops;
-         break;
-     case CCDB_BE_SECRETS:
-         DEBUG(SSSDBG_FUNC_DATA, "KCM back end: sssd-secrets\n");
-diff --git a/src/responder/kcm/kcmsrv_ccache_mem.c b/src/responder/kcm/kcmsrv_ccache_mem.c
-new file mode 100644
-index 0000000000000000000000000000000000000000..1c4f3df8d3b35b0428a143d4b545562d9cc0e574
---- /dev/null
-+++ b/src/responder/kcm/kcmsrv_ccache_mem.c
-@@ -0,0 +1,805 @@
-+/*
-+   SSSD
-+
-+   KCM Server - ccache in-memory storage
-+
-+   Copyright (C) Red Hat, 2016
-+
-+   This program is free software; you can redistribute it and/or modify
-+   it under the terms of the GNU General Public License as published by
-+   the Free Software Foundation; either version 3 of the License, or
-+   (at your option) any later version.
-+
-+   This program is distributed in the hope that it will be useful,
-+   but WITHOUT ANY WARRANTY; without even the implied warranty of
-+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+   GNU General Public License for more details.
-+
-+   You should have received a copy of the GNU General Public License
-+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
-+*/
-+
-+#include "config.h"
-+
-+#include <talloc.h>
-+#include <stdio.h>
-+
-+#include "util/util.h"
-+#include "responder/kcm/kcmsrv_ccache_pvt.h"
-+#include "responder/kcm/kcmsrv_ccache_be.h"
-+
-+struct ccdb_mem;
-+
-+/*
-+ * The KCM memory database is just a double-linked list of kcm_ccache structures
-+ */
-+struct ccache_mem_wrap {
-+    struct kcm_ccache *cc;
-+    bool is_default;
-+
-+    struct ccache_mem_wrap *next;
-+    struct ccache_mem_wrap *prev;
-+
-+    struct ccdb_mem *mem_be;
-+};
-+
-+struct ccdb_mem {
-+    /* Both ccaches and the next-id are kept in memory */
-+    struct ccache_mem_wrap *head;
-+    unsigned int nextid;
-+};
-+
-+/* In order to provide a consistent interface, we need to let the caller
-+ * of getbyXXX own the ccache, therefore the memory back end returns a shallow
-+ * copy of the ccache
-+ */
-+static struct kcm_ccache *kcm_ccache_dup(TALLOC_CTX *mem_ctx,
-+                                         struct kcm_ccache *in)
-+{
-+    struct kcm_ccache *out;
-+
-+    out = talloc_zero(mem_ctx, struct kcm_ccache);
-+    if (out == NULL) {
-+        return NULL;
-+    }
-+    memcpy(out, in, sizeof(struct kcm_ccache));
-+
-+    return out;
-+}
-+
-+static struct ccache_mem_wrap *memdb_get_by_uuid(struct ccdb_mem *memdb,
-+                                                 struct cli_creds *client,
-+                                                 uuid_t uuid)
-+{
-+    uid_t uid;
-+    struct ccache_mem_wrap *ccwrap = NULL;
-+    struct ccache_mem_wrap *out = NULL;
-+
-+    uid = cli_creds_get_uid(client);
-+
-+    DLIST_FOR_EACH(ccwrap, memdb->head) {
-+        if (ccwrap->cc == NULL) {
-+            /* since KCM stores ccaches, better not crash.. */
-+            DEBUG(SSSDBG_CRIT_FAILURE, "BUG: ccwrap contains NULL cc\n");
-+            continue;
-+        }
-+
-+        if (ccwrap->cc->owner.uid == uid) {
-+            if (uuid_compare(uuid, ccwrap->cc->uuid) == 0) {
-+                out = ccwrap;
-+                break;
-+            }
-+        }
-+    }
-+
-+    return out;
-+}
-+
-+static struct ccache_mem_wrap *memdb_get_by_name(struct ccdb_mem *memdb,
-+                                                 struct cli_creds *client,
-+                                                 const char *name)
-+{
-+    uid_t uid;
-+    struct ccache_mem_wrap *ccwrap = NULL;
-+    struct ccache_mem_wrap *out = NULL;
-+
-+    uid = cli_creds_get_uid(client);
-+
-+    DLIST_FOR_EACH(ccwrap, memdb->head) {
-+        if (ccwrap->cc == NULL) {
-+            /* since KCM stores ccaches, better not crash.. */
-+            DEBUG(SSSDBG_CRIT_FAILURE, "BUG: ccwrap contains NULL cc\n");
-+            continue;
-+        }
-+
-+        if (ccwrap->cc->owner.uid == uid) {
-+            if (strcmp(ccwrap->cc->name, name) == 0) {
-+                out = ccwrap;
-+                break;
-+            }
-+        }
-+    }
-+
-+    return out;
-+}
-+
-+/* Since with the in-memory database, the database operations are just
-+ * fake-async wrappers around otherwise sync operations, we don't often
-+ * need any state, so we use this empty structure instead
-+ */
-+struct ccdb_mem_dummy_state {
-+};
-+
-+static int ccwrap_destructor(void *ptr)
-+{
-+    struct ccache_mem_wrap *ccwrap = talloc_get_type(ptr, struct ccache_mem_wrap);
-+
-+    if (ccwrap == NULL) {
-+        return 0;
-+    }
-+
-+    if (ccwrap->cc != NULL) {
-+        if (ccwrap->cc->creds) {
-+            safezero(sss_iobuf_get_data(ccwrap->cc->creds->cred_blob),
-+                     sss_iobuf_get_size(ccwrap->cc->creds->cred_blob));
-+        }
-+    }
-+
-+
-+    DLIST_REMOVE(ccwrap->mem_be->head, ccwrap);
-+
-+    return 0;
-+}
-+
-+static errno_t ccdb_mem_init(struct kcm_ccdb *db)
-+{
-+    struct ccdb_mem *memdb = NULL;
-+
-+    memdb = talloc_zero(db, struct ccdb_mem);
-+    if (memdb == NULL) {
-+        return ENOMEM;
-+    }
-+    db->db_handle = memdb;
-+
-+    return EOK;
-+}
-+
-+struct ccdb_mem_nextid_state {
-+    unsigned int nextid;
-+};
-+
-+static struct tevent_req *ccdb_mem_nextid_send(TALLOC_CTX *mem_ctx,
-+                                               struct tevent_context *ev,
-+                                               struct kcm_ccdb *db,
-+                                               struct cli_creds *client)
-+{
-+    struct tevent_req *req = NULL;
-+    struct ccdb_mem_nextid_state *state = NULL;
-+    struct ccdb_mem *memdb = NULL;
-+    errno_t ret;
-+
-+    req = tevent_req_create(mem_ctx, &state, struct ccdb_mem_nextid_state);
-+    if (req == NULL) {
-+        return NULL;
-+    }
-+
-+    memdb = talloc_get_type(db->db_handle, struct ccdb_mem);
-+    if (memdb == NULL) {
-+        ret = EIO;
-+        goto immediate;
-+    }
-+
-+    state->nextid = memdb->nextid++;
-+
-+    ret = EOK;
-+immediate:
-+    if (ret == EOK) {
-+        tevent_req_done(req);
-+    } else {
-+        tevent_req_error(req, ret);
-+    }
-+    tevent_req_post(req, ev);
-+    return req;
-+}
-+
-+static errno_t ccdb_mem_nextid_recv(struct tevent_req *req,
-+                                    unsigned int *_nextid)
-+{
-+    struct ccdb_mem_nextid_state *state = tevent_req_data(req,
-+                                                struct ccdb_mem_nextid_state);
-+
-+    TEVENT_REQ_RETURN_ON_ERROR(req);
-+    *_nextid = state->nextid;
-+    return EOK;
-+}
-+
-+struct ccdb_mem_list_state {
-+    uuid_t *uuid_list;
-+};
-+
-+static struct tevent_req *ccdb_mem_list_send(TALLOC_CTX *mem_ctx,
-+                                             struct tevent_context *ev,
-+                                             struct kcm_ccdb *db,
-+                                             struct cli_creds *client)
-+{
-+    struct tevent_req *req = NULL;
-+    struct ccache_mem_wrap *ccwrap = NULL;
-+    struct ccdb_mem_list_state *state = NULL;
-+    struct ccdb_mem *memdb = talloc_get_type(db->db_handle, struct ccdb_mem);
-+    size_t num_ccaches = 0;
-+    size_t cc_index = 0;
-+    errno_t ret;
-+    uid_t uid;
-+
-+    req = tevent_req_create(mem_ctx, &state, struct ccdb_mem_list_state);
-+    if (req == NULL) {
-+        return NULL;
-+    }
-+
-+    uid = cli_creds_get_uid(client);
-+
-+    DLIST_FOR_EACH(ccwrap, memdb->head) {
-+        if (ccwrap->cc->owner.uid == uid) {
-+            num_ccaches++;
-+        }
-+    }
-+
-+    state->uuid_list = talloc_zero_array(state, uuid_t, num_ccaches+1);
-+    if (state->uuid_list == NULL) {
-+        ret = ENOMEM;
-+        goto immediate;
-+    }
-+
-+    cc_index = 0;
-+    DLIST_FOR_EACH(ccwrap, memdb->head) {
-+        if (ccwrap->cc->owner.uid == uid) {
-+            uuid_copy(state->uuid_list[cc_index], ccwrap->cc->uuid);
-+            cc_index++;
-+        }
-+    }
-+    uuid_clear(state->uuid_list[num_ccaches]);
-+
-+    ret = EOK;
-+immediate:
-+    if (ret == EOK) {
-+        tevent_req_done(req);
-+    } else {
-+        tevent_req_error(req, ret);
-+    }
-+    tevent_req_post(req, ev);
-+    return req;
-+}
-+
-+static errno_t ccdb_mem_list_recv(struct tevent_req *req,
-+                                  TALLOC_CTX *mem_ctx,
-+                                  uuid_t **_uuid_list)
-+{
-+    struct ccdb_mem_list_state *state = tevent_req_data(req,
-+                                                struct ccdb_mem_list_state);
-+
-+    TEVENT_REQ_RETURN_ON_ERROR(req);
-+    *_uuid_list = talloc_steal(mem_ctx, state->uuid_list);
-+    return EOK;
-+}
-+
-+static struct tevent_req *ccdb_mem_set_default_send(TALLOC_CTX *mem_ctx,
-+                                                    struct tevent_context *ev,
-+                                                    struct kcm_ccdb *db,
-+                                                    struct cli_creds *client,
-+                                                    uuid_t uuid)
-+{
-+    struct tevent_req *req = NULL;
-+    struct ccdb_mem_dummy_state *state = NULL;
-+    struct ccdb_mem *memdb = talloc_get_type(db->db_handle, struct ccdb_mem);
-+    struct ccache_mem_wrap *ccwrap = NULL;
-+    uid_t uid = cli_creds_get_uid(client);
-+
-+    req = tevent_req_create(mem_ctx, &state, struct ccdb_mem_dummy_state);
-+    if (req == NULL) {
-+        return NULL;
-+    }
-+
-+    /* Reset all ccache defaults first */
-+    DLIST_FOR_EACH(ccwrap, memdb->head) {
-+        if (ccwrap->cc == NULL) {
-+            /* since KCM stores ccaches, better not crash.. */
-+            DEBUG(SSSDBG_CRIT_FAILURE, "BUG: ccwrap contains NULL cc\n");
-+            continue;
-+        }
-+
-+        if (ccwrap->cc->owner.uid == uid) {
-+            ccwrap->is_default = false;
-+        }
-+    }
-+
-+    /* Then set the default for the right ccache. This also allows to
-+     * pass a null uuid to just reset the old ccache (for example after
-+     * deleting the default
-+     */
-+    ccwrap = memdb_get_by_uuid(memdb, client, uuid);
-+    if (ccwrap != NULL) {
-+        ccwrap->is_default = true;
-+    }
-+
-+    tevent_req_done(req);
-+    tevent_req_post(req, ev);
-+    return req;
-+}
-+
-+static errno_t ccdb_mem_set_default_recv(struct tevent_req *req)
-+{
-+    TEVENT_REQ_RETURN_ON_ERROR(req);
-+    return EOK;
-+}
-+
-+struct ccdb_mem_get_default_state {
-+    uuid_t dfl_uuid;
-+};
-+
-+static struct tevent_req *ccdb_mem_get_default_send(TALLOC_CTX *mem_ctx,
-+                                                    struct tevent_context *ev,
-+                                                    struct kcm_ccdb *db,
-+                                                    struct cli_creds *client)
-+{
-+    struct tevent_req *req = NULL;
-+    struct ccdb_mem_get_default_state *state = NULL;
-+    struct ccache_mem_wrap *ccwrap = NULL;
-+    struct ccdb_mem *memdb = talloc_get_type(db->db_handle, struct ccdb_mem);
-+    uid_t uid = cli_creds_get_uid(client);
-+
-+    req = tevent_req_create(mem_ctx, &state, struct ccdb_mem_get_default_state);
-+    if (req == NULL) {
-+        return NULL;
-+    }
-+
-+
-+    /* Reset all ccache defaults first */
-+    DLIST_FOR_EACH(ccwrap, memdb->head) {
-+        if (ccwrap->cc == NULL) {
-+            /* since KCM stores ccaches, better not crash.. */
-+            DEBUG(SSSDBG_CRIT_FAILURE, "BUG: ccwrap contains NULL cc\n");
-+            continue;
-+        }
-+
-+        if (ccwrap->cc->owner.uid == uid && ccwrap->is_default == true) {
-+            break;
-+        }
-+    }
-+
-+    if (ccwrap == NULL) {
-+        DEBUG(SSSDBG_TRACE_FUNC,
-+               "No ccache marked as default, returning null ccache\n");
-+        uuid_clear(state->dfl_uuid);
-+    } else {
-+        uuid_copy(state->dfl_uuid, ccwrap->cc->uuid);
-+    }
-+
-+    tevent_req_done(req);
-+    tevent_req_post(req, ev);
-+    return req;
-+}
-+
-+static errno_t ccdb_mem_get_default_recv(struct tevent_req *req,
-+                                         uuid_t dfl)
-+{
-+    struct ccdb_mem_get_default_state *state = tevent_req_data(req,
-+                                                struct ccdb_mem_get_default_state);
-+
-+    TEVENT_REQ_RETURN_ON_ERROR(req);
-+
-+    uuid_copy(dfl, state->dfl_uuid);
-+    return EOK;
-+}
-+
-+struct ccdb_mem_getbyuuid_state {
-+    struct kcm_ccache *cc;
-+};
-+
-+static struct tevent_req *ccdb_mem_getbyuuid_send(TALLOC_CTX *mem_ctx,
-+                                                  struct tevent_context *ev,
-+                                                  struct kcm_ccdb *db,
-+                                                  struct cli_creds *client,
-+                                                  uuid_t uuid)
-+{
-+    struct tevent_req *req = NULL;
-+    struct ccdb_mem_getbyuuid_state *state = NULL;
-+    struct ccdb_mem *memdb = talloc_get_type(db->db_handle, struct ccdb_mem);
-+    struct ccache_mem_wrap *ccwrap = NULL;
-+
-+    req = tevent_req_create(mem_ctx, &state, struct ccdb_mem_getbyuuid_state);
-+    if (req == NULL) {
-+        return NULL;
-+    }
-+
-+    ccwrap = memdb_get_by_uuid(memdb, client, uuid);
-+    if (ccwrap != NULL) {
-+        state->cc = kcm_ccache_dup(state, ccwrap->cc);
-+    }
-+
-+    tevent_req_done(req);
-+    tevent_req_post(req, ev);
-+    return req;
-+}
-+
-+static errno_t ccdb_mem_getbyuuid_recv(struct tevent_req *req,
-+                                       TALLOC_CTX *mem_ctx,
-+                                       struct kcm_ccache **_cc)
-+{
-+    struct ccdb_mem_getbyuuid_state *state = tevent_req_data(req,
-+                                                struct ccdb_mem_getbyuuid_state);
-+
-+    TEVENT_REQ_RETURN_ON_ERROR(req);
-+    *_cc = talloc_steal(mem_ctx, state->cc);
-+    return EOK;
-+}
-+
-+struct ccdb_mem_getbyname_state {
-+    struct kcm_ccache *cc;
-+};
-+
-+static struct tevent_req *ccdb_mem_getbyname_send(TALLOC_CTX *mem_ctx,
-+                                                  struct tevent_context *ev,
-+                                                  struct kcm_ccdb *db,
-+                                                  struct cli_creds *client,
-+                                                  const char *name)
-+{
-+    struct tevent_req *req = NULL;
-+    struct ccdb_mem_getbyname_state *state = NULL;
-+    struct ccache_mem_wrap *ccwrap = NULL;
-+    struct ccdb_mem *memdb = talloc_get_type(db->db_handle, struct ccdb_mem);
-+
-+    req = tevent_req_create(mem_ctx, &state, struct ccdb_mem_getbyname_state);
-+    if (req == NULL) {
-+        return NULL;
-+    }
-+
-+    ccwrap = memdb_get_by_name(memdb, client, name);
-+    if (ccwrap != NULL) {
-+        state->cc = kcm_ccache_dup(state, ccwrap->cc);
-+    }
-+
-+    tevent_req_done(req);
-+    tevent_req_post(req, ev);
-+    return req;
-+}
-+
-+static errno_t ccdb_mem_getbyname_recv(struct tevent_req *req,
-+                                       TALLOC_CTX *mem_ctx,
-+                                       struct kcm_ccache **_cc)
-+{
-+    struct ccdb_mem_getbyname_state *state = tevent_req_data(req,
-+                                                struct ccdb_mem_getbyname_state);
-+
-+    TEVENT_REQ_RETURN_ON_ERROR(req);
-+    *_cc = talloc_steal(mem_ctx, state->cc);
-+    return EOK;
-+}
-+
-+struct ccdb_mem_name_by_uuid_state {
-+    const char *name;
-+};
-+
-+struct tevent_req *ccdb_mem_name_by_uuid_send(TALLOC_CTX *mem_ctx,
-+                                              struct tevent_context *ev,
-+                                              struct kcm_ccdb *db,
-+                                              struct cli_creds *client,
-+                                              uuid_t uuid)
-+{
-+    struct tevent_req *req = NULL;
-+    struct ccdb_mem_name_by_uuid_state *state = NULL;
-+    struct ccdb_mem *memdb = talloc_get_type(db->db_handle, struct ccdb_mem);
-+    struct ccache_mem_wrap *ccwrap = NULL;
-+    errno_t ret;
-+
-+    req = tevent_req_create(mem_ctx, &state, struct ccdb_mem_name_by_uuid_state);
-+    if (req == NULL) {
-+        return NULL;
-+    }
-+
-+    ccwrap = memdb_get_by_uuid(memdb, client, uuid);
-+    if (ccwrap == NULL) {
-+        ret = ERR_KCM_CC_END;
-+        goto immediate;
-+    }
-+
-+    state->name = talloc_strdup(state, ccwrap->cc->name);
-+    if (state->name == NULL) {
-+        ret = ENOMEM;
-+        goto immediate;
-+    }
-+
-+    ret = EOK;
-+immediate:
-+    if (ret == EOK) {
-+        tevent_req_done(req);
-+    } else {
-+        tevent_req_error(req, ret);
-+    }
-+    tevent_req_post(req, ev);
-+    return req;
-+}
-+
-+errno_t ccdb_mem_name_by_uuid_recv(struct tevent_req *req,
-+                                   TALLOC_CTX *mem_ctx,
-+                                   const char **_name)
-+{
-+    struct ccdb_mem_name_by_uuid_state *state = tevent_req_data(req,
-+                                                struct ccdb_mem_name_by_uuid_state);
-+    TEVENT_REQ_RETURN_ON_ERROR(req);
-+    *_name = talloc_steal(mem_ctx, state->name);
-+    return EOK;
-+}
-+
-+struct ccdb_mem_uuid_by_name_state {
-+    uuid_t uuid;
-+};
-+
-+struct tevent_req *ccdb_mem_uuid_by_name_send(TALLOC_CTX *mem_ctx,
-+                                              struct tevent_context *ev,
-+                                              struct kcm_ccdb *db,
-+                                              struct cli_creds *client,
-+                                              const char *name)
-+{
-+    struct tevent_req *req = NULL;
-+    struct ccdb_mem_uuid_by_name_state *state = NULL;
-+    struct ccdb_mem *memdb = talloc_get_type(db->db_handle, struct ccdb_mem);
-+    struct ccache_mem_wrap *ccwrap = NULL;
-+    errno_t ret;
-+
-+    req = tevent_req_create(mem_ctx, &state, struct ccdb_mem_uuid_by_name_state);
-+    if (req == NULL) {
-+        return NULL;
-+    }
-+
-+    ccwrap = memdb_get_by_name(memdb, client, name);
-+    if (ccwrap == NULL) {
-+        ret = ERR_KCM_CC_END;
-+        goto immediate;
-+    }
-+
-+    uuid_copy(state->uuid, ccwrap->cc->uuid);
-+
-+    ret = EOK;
-+immediate:
-+    if (ret == EOK) {
-+        tevent_req_done(req);
-+    } else {
-+        tevent_req_error(req, ret);
-+    }
-+    tevent_req_post(req, ev);
-+    return req;
-+}
-+
-+errno_t ccdb_mem_uuid_by_name_recv(struct tevent_req *req,
-+                                   TALLOC_CTX *mem_ctx,
-+                                   uuid_t _uuid)
-+{
-+    struct ccdb_mem_uuid_by_name_state *state = tevent_req_data(req,
-+                                                struct ccdb_mem_uuid_by_name_state);
-+    TEVENT_REQ_RETURN_ON_ERROR(req);
-+    uuid_copy(_uuid, state->uuid);
-+    return EOK;
-+}
-+
-+static struct tevent_req *ccdb_mem_create_send(TALLOC_CTX *mem_ctx,
-+                                               struct tevent_context *ev,
-+                                               struct kcm_ccdb *db,
-+                                               struct cli_creds *client,
-+                                               struct kcm_ccache *cc)
-+{
-+    struct tevent_req *req = NULL;
-+    struct ccdb_mem_dummy_state *state = NULL;
-+    struct ccache_mem_wrap *ccwrap;
-+    struct ccdb_mem *memdb = talloc_get_type(db->db_handle, struct ccdb_mem);
-+    errno_t ret;
-+
-+    req = tevent_req_create(mem_ctx, &state, struct ccdb_mem_dummy_state);
-+    if (req == NULL) {
-+        return NULL;
-+    }
-+
-+    ccwrap = talloc_zero(memdb, struct ccache_mem_wrap);
-+    if (ccwrap == NULL) {
-+        ret = ENOMEM;
-+        goto immediate;
-+    }
-+    ccwrap->cc = cc;
-+    ccwrap->mem_be = memdb;
-+    talloc_steal(ccwrap, cc);
-+
-+    DLIST_ADD(memdb->head, ccwrap);
-+    talloc_set_destructor((TALLOC_CTX *) ccwrap, ccwrap_destructor);
-+
-+    ret = EOK;
-+immediate:
-+    if (ret == EOK) {
-+        tevent_req_done(req);
-+    } else {
-+        tevent_req_error(req, ret);
-+    }
-+    tevent_req_post(req, ev);
-+    return req;
-+}
-+
-+static errno_t ccdb_mem_create_recv(struct tevent_req *req)
-+{
-+    TEVENT_REQ_RETURN_ON_ERROR(req);
-+    return EOK;
-+}
-+
-+static struct tevent_req *ccdb_mem_mod_send(TALLOC_CTX *mem_ctx,
-+                                            struct tevent_context *ev,
-+                                            struct kcm_ccdb *db,
-+                                            struct cli_creds *client,
-+                                            uuid_t uuid,
-+                                            struct kcm_mod_ctx *mod_cc)
-+{
-+    errno_t ret;
-+    struct tevent_req *req = NULL;
-+    struct ccdb_mem_dummy_state *state = NULL;
-+    struct ccache_mem_wrap *ccwrap = NULL;
-+    struct ccdb_mem *memdb = talloc_get_type(db->db_handle, struct ccdb_mem);
-+
-+    req = tevent_req_create(mem_ctx, &state, struct ccdb_mem_dummy_state);
-+    if (req == NULL) {
-+        return NULL;
-+    }
-+
-+    /* UUID is immutable, so search by that */
-+    ccwrap = memdb_get_by_uuid(memdb, client, uuid);
-+    if (ccwrap == NULL) {
-+        ret = ERR_KCM_CC_END;
-+        goto immediate;
-+    }
-+
-+    kcm_mod_cc(ccwrap->cc, mod_cc);
-+
-+    ret = EOK;
-+immediate:
-+    if (ret == EOK) {
-+        tevent_req_done(req);
-+    } else {
-+        tevent_req_error(req, ret);
-+    }
-+    tevent_req_post(req, ev);
-+    return req;
-+}
-+
-+static errno_t ccdb_mem_mod_recv(struct tevent_req *req)
-+{
-+    TEVENT_REQ_RETURN_ON_ERROR(req);
-+    return EOK;
-+}
-+
-+static struct tevent_req *ccdb_mem_store_cred_send(TALLOC_CTX *mem_ctx,
-+                                                   struct tevent_context *ev,
-+                                                   struct kcm_ccdb *db,
-+                                                   struct cli_creds *client,
-+                                                   uuid_t uuid,
-+                                                   struct sss_iobuf *cred_blob)
-+{
-+    struct tevent_req *req = NULL;
-+    struct ccdb_mem_dummy_state *state = NULL;
-+    struct ccdb_mem *memdb = talloc_get_type(db->db_handle, struct ccdb_mem);
-+    struct ccache_mem_wrap *ccwrap = NULL;
-+    errno_t ret;
-+
-+    req = tevent_req_create(mem_ctx, &state, struct ccdb_mem_dummy_state);
-+    if (req == NULL) {
-+        return NULL;
-+    }
-+
-+    ccwrap = memdb_get_by_uuid(memdb, client, uuid);
-+    if (ccwrap == NULL) {
-+        ret = ERR_KCM_CC_END;
-+        goto immediate;
-+    }
-+
-+    ret = kcm_cc_store_cred_blob(ccwrap->cc, cred_blob);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_OP_FAILURE,
-+              "Cannot store credentials to ccache [%d]: %s\n",
-+              ret, sss_strerror(ret));
-+        goto immediate;
-+    }
-+
-+    ret = EOK;
-+immediate:
-+    if (ret == EOK) {
-+        tevent_req_done(req);
-+    } else {
-+        tevent_req_error(req, ret);
-+    }
-+    tevent_req_post(req, ev);
-+    return req;
-+}
-+
-+static errno_t ccdb_mem_store_cred_recv(struct tevent_req *req)
-+{
-+    TEVENT_REQ_RETURN_ON_ERROR(req);
-+    return EOK;
-+}
-+
-+static struct tevent_req *ccdb_mem_delete_send(TALLOC_CTX *mem_ctx,
-+                                               struct tevent_context *ev,
-+                                               struct kcm_ccdb *db,
-+                                               struct cli_creds *client,
-+                                               uuid_t uuid)
-+{
-+    struct tevent_req *req = NULL;
-+    struct ccdb_mem_dummy_state *state = NULL;
-+    struct ccache_mem_wrap *ccwrap;
-+    struct ccdb_mem *memdb = talloc_get_type(db->db_handle, struct ccdb_mem);
-+    errno_t ret;
-+
-+    req = tevent_req_create(mem_ctx, &state, struct ccdb_mem_dummy_state);
-+    if (req == NULL) {
-+        return NULL;
-+    }
-+
-+    ccwrap = memdb_get_by_uuid(memdb, client, uuid);
-+    if (ccwrap == NULL) {
-+        DEBUG(SSSDBG_MINOR_FAILURE,
-+              "BUG: Attempting to free unknown ccache\n");
-+        ret = ERR_KCM_CC_END;
-+        goto immediate;
-+    }
-+
-+    ret = EOK;
-+    /* Destructor takes care of everything */
-+    talloc_free(ccwrap);
-+immediate:
-+    if (ret == EOK) {
-+        tevent_req_done(req);
-+    } else {
-+        tevent_req_error(req, ret);
-+    }
-+    tevent_req_post(req, ev);
-+    return req;
-+}
-+
-+static errno_t ccdb_mem_delete_recv(struct tevent_req *req)
-+{
-+    TEVENT_REQ_RETURN_ON_ERROR(req);
-+    return EOK;
-+}
-+
-+const struct kcm_ccdb_ops ccdb_mem_ops = {
-+    .init = ccdb_mem_init,
-+
-+    .nextid_send = ccdb_mem_nextid_send,
-+    .nextid_recv = ccdb_mem_nextid_recv,
-+
-+    .set_default_send = ccdb_mem_set_default_send,
-+    .set_default_recv = ccdb_mem_set_default_recv,
-+
-+    .get_default_send = ccdb_mem_get_default_send,
-+    .get_default_recv = ccdb_mem_get_default_recv,
-+
-+    .list_send = ccdb_mem_list_send,
-+    .list_recv = ccdb_mem_list_recv,
-+
-+    .getbyname_send = ccdb_mem_getbyname_send,
-+    .getbyname_recv = ccdb_mem_getbyname_recv,
-+
-+    .getbyuuid_send = ccdb_mem_getbyuuid_send,
-+    .getbyuuid_recv = ccdb_mem_getbyuuid_recv,
-+
-+    .name_by_uuid_send = ccdb_mem_name_by_uuid_send,
-+    .name_by_uuid_recv = ccdb_mem_name_by_uuid_recv,
-+
-+    .uuid_by_name_send = ccdb_mem_uuid_by_name_send,
-+    .uuid_by_name_recv = ccdb_mem_uuid_by_name_recv,
-+
-+    .create_send = ccdb_mem_create_send,
-+    .create_recv = ccdb_mem_create_recv,
-+
-+    .mod_send = ccdb_mem_mod_send,
-+    .mod_recv = ccdb_mem_mod_recv,
-+
-+    .store_cred_send = ccdb_mem_store_cred_send,
-+    .store_cred_recv = ccdb_mem_store_cred_recv,
-+
-+    .delete_send = ccdb_mem_delete_send,
-+    .delete_recv = ccdb_mem_delete_recv,
-+};
--- 
-2.9.3
-
diff --git a/SOURCES/0025-NSS-add-support-for-SSS_NSS_EX_FLAG_NO_CACHE.patch b/SOURCES/0025-NSS-add-support-for-SSS_NSS_EX_FLAG_NO_CACHE.patch
new file mode 100644
index 0000000..637d39b
--- /dev/null
+++ b/SOURCES/0025-NSS-add-support-for-SSS_NSS_EX_FLAG_NO_CACHE.patch
@@ -0,0 +1,149 @@
+From 21633dc4ad13c0ebae0f2b4e4f4188556202113e Mon Sep 17 00:00:00 2001
+From: Sumit Bose <sbose@redhat.com>
+Date: Thu, 12 Oct 2017 10:42:41 +0200
+Subject: [PATCH 25/31] NSS: add support for SSS_NSS_EX_FLAG_NO_CACHE
+
+If SSS_NSS_EX_FLAG_NO_CACHE is set the object is refresh by directly
+looking it up in the backend.
+
+Related to https://pagure.io/SSSD/sssd/issue/2478
+
+Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
+(cherry picked from commit ac6b267ff3df6d0417062a128ec16b184ea2c1b7)
+---
+ src/responder/nss/nss_cmd.c          |  8 ++++
+ src/sss_client/idmap/sss_nss_ex.c    | 71 ++++++++++++++++++++----------------
+ src/sss_client/idmap/sss_nss_idmap.h |  4 ++
+ 3 files changed, 52 insertions(+), 31 deletions(-)
+
+diff --git a/src/responder/nss/nss_cmd.c b/src/responder/nss/nss_cmd.c
+index 974eaccc93cea3a330007735676da69eb9b84141..c5ddd2f2cc2122cd169ea991b94a14eb5bad095f 100644
+--- a/src/responder/nss/nss_cmd.c
++++ b/src/responder/nss/nss_cmd.c
+@@ -92,6 +92,10 @@ static errno_t nss_getby_name(struct cli_ctx *cli_ctx,
+         goto done;
+     }
+ 
++    if ((flags & SSS_NSS_EX_FLAG_NO_CACHE) != 0) {
++        cache_req_data_set_bypass_cache(data, true);
++    }
++
+     subreq = nss_get_object_send(cmd_ctx, cli_ctx->ev, cli_ctx,
+                                  data, memcache, rawname, 0);
+     if (subreq == NULL) {
+@@ -152,6 +156,10 @@ static errno_t nss_getby_id(struct cli_ctx *cli_ctx,
+         goto done;
+     }
+ 
++    if ((flags & SSS_NSS_EX_FLAG_NO_CACHE) != 0) {
++        cache_req_data_set_bypass_cache(data, true);
++    }
++
+     subreq = nss_get_object_send(cmd_ctx, cli_ctx->ev, cli_ctx,
+                                  data, memcache, NULL, id);
+     if (subreq == NULL) {
+diff --git a/src/sss_client/idmap/sss_nss_ex.c b/src/sss_client/idmap/sss_nss_ex.c
+index dc7610a4e528b5126f0d25d84cd3c1a22f683b75..edb3ea652ef7032b76c8f815b9f83fe185a669ea 100644
+--- a/src/sss_client/idmap/sss_nss_ex.c
++++ b/src/sss_client/idmap/sss_nss_ex.c
+@@ -115,42 +115,51 @@ int sss_get_ex(struct nss_input *inp, uint32_t flags, unsigned int timeout)
+     size_t c;
+     gid_t *new_groups;
+     size_t idx;
++    bool skip_mc = false;
+ 
+-    ret = sss_nss_mc_get(inp);
+-    switch (ret) {
+-    case 0:
+-        return 0;
+-    case ERANGE:
+-        return ERANGE;
+-    case ENOENT:
+-        /* fall through, we need to actively ask the parent
+-         * if no entry is found */
+-        break;
+-    default:
+-        /* if using the mmaped cache failed,
+-         * fall back to socket based comms */
+-        break;
++    if ((flags & SSS_NSS_EX_FLAG_NO_CACHE) != 0) {
++        skip_mc = true;
++    }
++
++    if (!skip_mc) {
++        ret = sss_nss_mc_get(inp);
++        switch (ret) {
++        case 0:
++            return 0;
++        case ERANGE:
++            return ERANGE;
++        case ENOENT:
++            /* fall through, we need to actively ask the parent
++             * if no entry is found */
++            break;
++        default:
++            /* if using the mmaped cache failed,
++             * fall back to socket based comms */
++            break;
++        }
+     }
+ 
+     sss_nss_timedlock(timeout, &time_left);
+ 
+-    /* previous thread might already initialize entry in mmap cache */
+-    ret = sss_nss_mc_get(inp);
+-    switch (ret) {
+-    case 0:
+-        ret = 0;
+-        goto out;
+-    case ERANGE:
+-        ret = ERANGE;
+-        goto out;
+-    case ENOENT:
+-        /* fall through, we need to actively ask the parent
+-         * if no entry is found */
+-        break;
+-    default:
+-        /* if using the mmaped cache failed,
+-         * fall back to socket based comms */
+-        break;
++    if (!skip_mc) {
++        /* previous thread might already initialize entry in mmap cache */
++        ret = sss_nss_mc_get(inp);
++        switch (ret) {
++        case 0:
++            ret = 0;
++            goto out;
++        case ERANGE:
++            ret = ERANGE;
++            goto out;
++        case ENOENT:
++            /* fall through, we need to actively ask the parent
++             * if no entry is found */
++            break;
++        default:
++            /* if using the mmaped cache failed,
++             * fall back to socket based comms */
++            break;
++        }
+     }
+ 
+     ret = sss_nss_make_request_timeout(inp->cmd, &inp->rd, time_left,
+diff --git a/src/sss_client/idmap/sss_nss_idmap.h b/src/sss_client/idmap/sss_nss_idmap.h
+index 2334b6cb3fb8ef62e4ce3a7187c7affaeaa034e7..1649830afbb80c617fd339f054aef8bc8e585fb9 100644
+--- a/src/sss_client/idmap/sss_nss_idmap.h
++++ b/src/sss_client/idmap/sss_nss_idmap.h
+@@ -169,6 +169,10 @@ void sss_nss_free_kv(struct sss_nss_kv *kv_list);
+ 
+ #define SSS_NSS_EX_FLAG_NO_FLAGS 0
+ 
++/** Always request data from the server side, client must be privileged to do
++ *  so, see nss_trusted_users option in man sssd.conf for details */
++#define SSS_NSS_EX_FLAG_NO_CACHE (1 << 0)
++
+ #ifdef IPA_389DS_PLUGIN_HELPER_CALLS
+ 
+ /**
+-- 
+2.13.6
+
diff --git a/SOURCES/0026-CACHE_REQ-Add-cache_req_data_set_bypass_dp.patch b/SOURCES/0026-CACHE_REQ-Add-cache_req_data_set_bypass_dp.patch
new file mode 100644
index 0000000..e0dd792
--- /dev/null
+++ b/SOURCES/0026-CACHE_REQ-Add-cache_req_data_set_bypass_dp.patch
@@ -0,0 +1,112 @@
+From 102ea8cf91207d3dc05537d5c558d98e4756027a Mon Sep 17 00:00:00 2001
+From: Sumit Bose <sbose@redhat.com>
+Date: Mon, 23 Oct 2017 18:26:55 +0200
+Subject: [PATCH 26/31] CACHE_REQ: Add cache_req_data_set_bypass_dp()
+
+Similar to cache_req_data_set_bypass_cache()
+cache_req_data_set_bypass_dp() can be used to control how the cache_req
+framework performs the lookup. If cache_req_data_set_bypass_dp() is used
+with 'true' only a cache lookup is performed and no request is send to
+the backend even if no entry was found.
+
+Related to https://pagure.io/SSSD/sssd/issue/2478
+
+Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
+(cherry picked from commit 52e675ec4b160720515c81ae8c0e5a95feb50c57)
+---
+ src/responder/common/cache_req/cache_req.c         | 15 +++++++++++++++
+ src/responder/common/cache_req/cache_req.h         |  3 +++
+ src/responder/common/cache_req/cache_req_data.c    | 12 ++++++++++++
+ src/responder/common/cache_req/cache_req_private.h |  2 ++
+ 4 files changed, 32 insertions(+)
+
+diff --git a/src/responder/common/cache_req/cache_req.c b/src/responder/common/cache_req/cache_req.c
+index 5fed7a2ab8beded2fee91f679a12f9a0ff6013ec..110df561101be538e3f0496addfa2e14e42ea918 100644
+--- a/src/responder/common/cache_req/cache_req.c
++++ b/src/responder/common/cache_req/cache_req.c
+@@ -142,6 +142,13 @@ cache_req_create(TALLOC_CTX *mem_ctx,
+ 
+     cr->cache_first = rctx->cache_first;
+     cr->bypass_cache = cr->plugin->bypass_cache || cr->data->bypass_cache;
++    cr->bypass_dp = cr->data->bypass_dp;
++    if (cr->bypass_cache && cr->bypass_dp) {
++        CACHE_REQ_DEBUG(SSSDBG_CRIT_FAILURE, cr,
++                        "Cannot bypass cache and dp at the same time!");
++        talloc_free(cr);
++        return NULL;
++    }
+ 
+     return cr;
+ }
+@@ -661,6 +668,14 @@ static bool cache_req_search_schema(struct cache_req *cr,
+         if (!first_iteration) {
+             return false;
+         }
++    } else if (cr->bypass_dp) {
++        /* The caller wants to lookup only in the cache */
++        bypass_cache = false;
++        bypass_dp = true;
++
++        if (!first_iteration) {
++            return false;
++        }
+     } else if (input_domain != NULL) {
+         /* We will search only one domain. */
+         bypass_cache = false;
+diff --git a/src/responder/common/cache_req/cache_req.h b/src/responder/common/cache_req/cache_req.h
+index c04b2fba6f0445dcfcc9cfe1b5963ac975c39118..2c88853887fc816bba2182d9d9beaa32fa384158 100644
+--- a/src/responder/common/cache_req/cache_req.h
++++ b/src/responder/common/cache_req/cache_req.h
+@@ -127,6 +127,9 @@ void
+ cache_req_data_set_bypass_cache(struct cache_req_data *data,
+                                 bool bypass_cache);
+ 
++void
++cache_req_data_set_bypass_dp(struct cache_req_data *data,
++                             bool bypass_dp);
+ /* Output data. */
+ 
+ struct cache_req_result {
+diff --git a/src/responder/common/cache_req/cache_req_data.c b/src/responder/common/cache_req/cache_req_data.c
+index 48264a321dc603f9708ba71c44542363b11a71ba..ed378274a9a0a68ede8ac99805f3ea4a041382e6 100644
+--- a/src/responder/common/cache_req/cache_req_data.c
++++ b/src/responder/common/cache_req/cache_req_data.c
+@@ -365,3 +365,15 @@ cache_req_data_set_bypass_cache(struct cache_req_data *data,
+ 
+     data->bypass_cache = bypass_cache;
+ }
++
++void
++cache_req_data_set_bypass_dp(struct cache_req_data *data,
++                             bool bypass_dp)
++{
++    if (data == NULL) {
++        DEBUG(SSSDBG_CRIT_FAILURE, "cache_req_data should never be NULL\n");
++        return;
++    }
++
++    data->bypass_dp = bypass_dp;
++}
+diff --git a/src/responder/common/cache_req/cache_req_private.h b/src/responder/common/cache_req/cache_req_private.h
+index 9b706ff7d678f543effb77089857a7e8a42a9c51..0f630542d38a277d1819063fa4134bd7d2525c90 100644
+--- a/src/responder/common/cache_req/cache_req_private.h
++++ b/src/responder/common/cache_req/cache_req_private.h
+@@ -42,6 +42,7 @@ struct cache_req {
+     struct sss_domain_info *domain;
+     bool cache_first;
+     bool bypass_cache;
++    bool bypass_dp;
+     /* Only contact domains with this type */
+     enum cache_req_dom_type req_dom_type;
+ 
+@@ -90,6 +91,7 @@ struct cache_req_data {
+     } svc;
+ 
+     bool bypass_cache;
++    bool bypass_dp;
+ };
+ 
+ struct tevent_req *
+-- 
+2.13.6
+
diff --git a/SOURCES/0026-KCM-Implement-KCM-server-operations.patch b/SOURCES/0026-KCM-Implement-KCM-server-operations.patch
deleted file mode 100644
index 56f486a..0000000
--- a/SOURCES/0026-KCM-Implement-KCM-server-operations.patch
+++ /dev/null
@@ -1,2326 +0,0 @@
-From 5287b672cbba97a5c30ca79954101bd134a30eca Mon Sep 17 00:00:00 2001
-From: Jakub Hrozek <jhrozek@redhat.com>
-Date: Fri, 23 Sep 2016 14:49:09 +0200
-Subject: [PATCH 26/36] KCM: Implement KCM server operations
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Implements the actual KCM server operations. On a high level, each
-operation unmarhalls the needed data from the input buffer, calls into
-the ccache db and marshalls a response.
-
-Only the operations that are also implemented by the MIT client are
-implemented by our KCM server.
-
-Resolves:
-    https://pagure.io/SSSD/sssd/issue/2887
-
-Reviewed-by: Michal Židek <mzidek@redhat.com>
-Reviewed-by: Simo Sorce <simo@redhat.com>
----
- Makefile.am                    |    2 +
- src/responder/kcm/kcmsrv_cmd.c |  151 +++-
- src/responder/kcm/kcmsrv_ops.c | 1954 ++++++++++++++++++++++++++++++++++++++++
- src/responder/kcm/kcmsrv_ops.h |   45 +
- 4 files changed, 2143 insertions(+), 9 deletions(-)
- create mode 100644 src/responder/kcm/kcmsrv_ops.c
- create mode 100644 src/responder/kcm/kcmsrv_ops.h
-
-diff --git a/Makefile.am b/Makefile.am
-index 5605c1a53c44fd9e83394e80b7f71828df1d39b6..49b4cabf9ee3ce1417f955c972376894f3709b33 100644
---- a/Makefile.am
-+++ b/Makefile.am
-@@ -714,6 +714,7 @@ dist_noinst_HEADERS = \
-     src/responder/kcm/kcmsrv_ccache.h \
-     src/responder/kcm/kcmsrv_ccache_pvt.h \
-     src/responder/kcm/kcmsrv_ccache_be.h \
-+    src/responder/kcm/kcmsrv_ops.h \
-     src/sbus/sbus_client.h \
-     src/sbus/sssd_dbus.h \
-     src/sbus/sssd_dbus_meta.h \
-@@ -1493,6 +1494,7 @@ sssd_kcm_SOURCES = \
-     src/responder/kcm/kcmsrv_cmd.c \
-     src/responder/kcm/kcmsrv_ccache.c \
-     src/responder/kcm/kcmsrv_ccache_mem.c \
-+    src/responder/kcm/kcmsrv_ops.c \
-     src/util/sss_sockets.c \
-     src/util/sss_krb5.c \
-     src/util/sss_iobuf.c \
-diff --git a/src/responder/kcm/kcmsrv_cmd.c b/src/responder/kcm/kcmsrv_cmd.c
-index cbf70353730d8a4e03d8f75c97395f4ef007e77f..537e88953fd1a190a9a73bcdd430d8e0db8f9291 100644
---- a/src/responder/kcm/kcmsrv_cmd.c
-+++ b/src/responder/kcm/kcmsrv_cmd.c
-@@ -23,10 +23,10 @@
- 
- #include "config.h"
- #include "util/util.h"
--#include "util/sss_iobuf.h"
- #include "responder/common/responder.h"
- #include "responder/kcm/kcmsrv_pvt.h"
- #include "responder/kcm/kcm.h"
-+#include "responder/kcm/kcmsrv_ops.h"
- 
- /* The first four bytes of a message is always the size */
- #define KCM_MSG_LEN_SIZE 4
-@@ -133,7 +133,6 @@ static errno_t kcm_input_parse(struct kcm_reqbuf *reqbuf,
- {
-     size_t lc = 0;
-     size_t mc = 0;
--    uint16_t opcode = 0;
-     uint16_t opcode_be = 0;
-     uint32_t len_be = 0;
-     uint32_t msglen;
-@@ -162,7 +161,7 @@ static errno_t kcm_input_parse(struct kcm_reqbuf *reqbuf,
-         return EBADMSG;
-     }
- 
--    /* First 16 bits are 8 bit major and 8bit major protocol version */
-+    /* First 16 bits are 8 bit major and 8bit minor protocol version */
-     SAFEALIGN_COPY_UINT8_CHECK(&proto_maj,
-                                reqbuf->v_msg.kiov_base + mc,
-                                reqbuf->v_msg.kiov_len,
-@@ -191,8 +190,16 @@ static errno_t kcm_input_parse(struct kcm_reqbuf *reqbuf,
-                                 reqbuf->v_msg.kiov_len,
-                                 &mc);
- 
--    opcode = be16toh(opcode_be);
--    DEBUG(SSSDBG_TRACE_LIBS, "Received operation code %"PRIu16"\n", opcode);
-+    op_io->op = kcm_get_opt(be16toh(opcode_be));
-+    if (op_io->op == NULL) {
-+        DEBUG(SSSDBG_CRIT_FAILURE,
-+              "Did not find a KCM operation handler for the requested opcode\n");
-+        return ERR_KCM_MALFORMED_IN_PKT;
-+    }
-+
-+    /* The operation only receives the payload, not the opcode or the protocol info */
-+    op_io->request.data = reqbuf->v_msg.kiov_base + mc;
-+    op_io->request.length = reqbuf->v_msg.nprocessed - mc;
- 
-     return EOK;
- }
-@@ -240,6 +247,46 @@ static errno_t kcm_failbuf_construct(errno_t ret,
-     c = 0;
-     SAFEALIGN_SETMEM_UINT32(repbuf->rcbuf, htobe32(ret), &c);
- 
-+    DEBUG(SSSDBG_TRACE_LIBS, "Sent reply with error %d\n", ret);
-+    return EOK;
-+}
-+
-+/* retcode is 0 if the operation at least ran, non-zero if there
-+ * was some kind of internal KCM error, like input couldn't be parsed
-+ */
-+static errno_t kcm_output_construct(struct kcm_op_io *op_io,
-+                                    struct kcm_repbuf *repbuf)
-+{
-+    size_t c;
-+    size_t replen;
-+
-+    replen = sss_iobuf_get_len(op_io->reply);
-+    if (replen > KCM_PACKET_MAX_SIZE) {
-+        DEBUG(SSSDBG_CRIT_FAILURE,
-+              "Reply exceeds the KCM protocol limit, aborting\n");
-+        return E2BIG;
-+    }
-+
-+    DEBUG(SSSDBG_TRACE_LIBS,
-+          "Sending a reply with %zu bytes of payload\n", replen);
-+    c = 0;
-+    SAFEALIGN_SETMEM_UINT32(repbuf->lenbuf, htobe32(replen), &c);
-+
-+    c = 0;
-+    SAFEALIGN_SETMEM_UINT32(repbuf->rcbuf, 0, &c);
-+
-+    if (replen > 0) {
-+        c = 0;
-+        SAFEALIGN_MEMCPY_CHECK(repbuf->msgbuf,
-+                               sss_iobuf_get_data(op_io->reply),
-+                               replen,
-+                               repbuf->v_msg.kiov_len,
-+                               &c);
-+
-+        /* Length of the buffer to send to KCM client */
-+        repbuf->v_msg.kiov_len = replen;
-+    }
-+
-     return EOK;
- }
- 
-@@ -260,7 +307,8 @@ static void kcm_reply_error(struct cli_ctx *cctx,
- 
-     ret = kcm_failbuf_construct(kerr, repbuf);
-     if (ret != EOK) {
--        /* If we can't construct the reply buffer, just terminate the client */
-+        DEBUG(SSSDBG_CRIT_FAILURE,
-+              "Cannot construct the reply buffer, terminating client\n");
-         talloc_free(cctx);
-         return;
-     }
-@@ -268,6 +316,24 @@ static void kcm_reply_error(struct cli_ctx *cctx,
-     TEVENT_FD_WRITEABLE(cctx->cfde);
- }
- 
-+static void kcm_send_reply(struct cli_ctx *cctx,
-+                           struct kcm_op_io *op_io,
-+                           struct kcm_repbuf *repbuf)
-+{
-+    errno_t ret;
-+
-+    DEBUG(SSSDBG_TRACE_INTERNAL, "Sending a reply\n");
-+    ret = kcm_output_construct(op_io, repbuf);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_CRIT_FAILURE,
-+              "Cannot construct the reply buffer, terminating client\n");
-+        kcm_reply_error(cctx, ret, repbuf);
-+        return;
-+    }
-+
-+    TEVENT_FD_WRITEABLE(cctx->cfde);
-+}
-+
- /**
-  * Request-reply dispatcher
-  */
-@@ -285,17 +351,67 @@ struct kcm_req_ctx {
-     struct kcm_op_io op_io;
- };
- 
-+static void kcm_cmd_request_done(struct tevent_req *req);
-+
-+static errno_t kcm_cmd_dispatch(struct kcm_req_ctx *req_ctx)
-+{
-+    struct tevent_req *req;
-+    struct cli_ctx *cctx;
-+
-+    cctx = req_ctx->cctx;
-+
-+    req = kcm_cmd_send(req_ctx, cctx->ev, req_ctx->kctx->kcm_data,
-+                       req_ctx->cctx->creds,
-+                       &req_ctx->op_io.request,
-+                       req_ctx->op_io.op);
-+    if (req == NULL) {
-+        DEBUG(SSSDBG_CRIT_FAILURE, "Failed to schedule KCM operation.\n");
-+        return ENOMEM;
-+    }
-+
-+    tevent_req_set_callback(req, kcm_cmd_request_done, req_ctx);
-+    return EOK;
-+}
-+
-+static void kcm_cmd_request_done(struct tevent_req *req)
-+{
-+    struct kcm_req_ctx *req_ctx;
-+    struct cli_ctx *cctx;
-+    errno_t ret;
-+
-+    req_ctx = tevent_req_callback_data(req, struct kcm_req_ctx);
-+    cctx = req_ctx->cctx;
-+
-+    ret = kcm_cmd_recv(req_ctx, req,
-+                       &req_ctx->op_io.reply);
-+    talloc_free(req);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_OP_FAILURE,
-+              "KCM operation failed [%d]: %s\n", ret, sss_strerror(ret));
-+        kcm_reply_error(cctx, ret, &req_ctx->repbuf);
-+        return;
-+    }
-+
-+    kcm_send_reply(cctx, &req_ctx->op_io, &req_ctx->repbuf);
-+}
-+
- static errno_t kcm_recv_data(int fd, struct kcm_reqbuf *reqbuf)
- {
-     errno_t ret;
- 
-     ret = kcm_read_iovec(fd, &reqbuf->v_len);
-     if (ret != EOK) {
-+        /* Not all errors are fatal, hence we don't print DEBUG messages
-+         * here, but in the caller
-+         */
-         return ret;
-     }
- 
-     ret = kcm_read_iovec(fd, &reqbuf->v_msg);
-     if (ret != EOK) {
-+        /* Not all errors are fatal, hence we don't print DEBUG messages
-+         * here, but in the caller
-+         */
-         return ret;
-     }
- 
-@@ -389,7 +505,15 @@ static void kcm_recv(struct cli_ctx *cctx)
-     /* do not read anymore, client is done sending */
-     TEVENT_FD_NOT_READABLE(cctx->cfde);
- 
--    kcm_reply_error(cctx, ret, &req->repbuf);
-+    ret = kcm_cmd_dispatch(req);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_FATAL_FAILURE,
-+              "Failed to dispatch KCM operation [%d]: %s\n",
-+              ret, sss_strerror(ret));
-+        goto fail;
-+    }
-+
-+    /* Dispatched request resumes in kcm_cmd_request_done */
-     return;
- 
- fail:
-@@ -406,16 +530,25 @@ static int kcm_send_data(struct cli_ctx *cctx)
- 
-     ret = kcm_write_iovec(cctx->cfd, &req->repbuf.v_len);
-     if (ret != EOK) {
-+        DEBUG(SSSDBG_CRIT_FAILURE,
-+              "Failed to write the length iovec [%d]: %s\n",
-+              ret, sss_strerror(ret));
-         return ret;
-     }
- 
-     ret = kcm_write_iovec(cctx->cfd, &req->repbuf.v_rc);
-     if (ret != EOK) {
-+        DEBUG(SSSDBG_CRIT_FAILURE,
-+              "Failed to write the retcode iovec [%d]: %s\n",
-+              ret, sss_strerror(ret));
-         return ret;
-     }
- 
-     ret = kcm_write_iovec(cctx->cfd, &req->repbuf.v_msg);
-     if (ret != EOK) {
-+        DEBUG(SSSDBG_CRIT_FAILURE,
-+              "Failed to write the msg iovec [%d]: %s\n",
-+              ret, sss_strerror(ret));
-         return ret;
-     }
- 
-@@ -428,7 +561,7 @@ static void kcm_send(struct cli_ctx *cctx)
- 
-     ret = kcm_send_data(cctx);
-     if (ret == EAGAIN) {
--        /* not all data was sent, loop again */
-+        DEBUG(SSSDBG_TRACE_ALL, "Sending data again..\n");
-         return;
-     } else if (ret != EOK) {
-         DEBUG(SSSDBG_FATAL_FAILURE, "Failed to send data, aborting client!\n");
-@@ -436,7 +569,7 @@ static void kcm_send(struct cli_ctx *cctx)
-         return;
-     }
- 
--    /* ok all sent */
-+    DEBUG(SSSDBG_TRACE_INTERNAL, "All data sent!\n");
-     TEVENT_FD_NOT_WRITEABLE(cctx->cfde);
-     TEVENT_FD_READABLE(cctx->cfde);
-     talloc_zfree(cctx->state_ctx);
-diff --git a/src/responder/kcm/kcmsrv_ops.c b/src/responder/kcm/kcmsrv_ops.c
-new file mode 100644
-index 0000000000000000000000000000000000000000..50e8cc635424e15d53e3c8d122c5525044f59c8a
---- /dev/null
-+++ b/src/responder/kcm/kcmsrv_ops.c
-@@ -0,0 +1,1954 @@
-+/*
-+   SSSD
-+
-+   KCM Server - the KCM server operations
-+
-+   Copyright (C) Red Hat, 2016
-+
-+   This program is free software; you can redistribute it and/or modify
-+   it under the terms of the GNU General Public License as published by
-+   the Free Software Foundation; either version 3 of the License, or
-+   (at your option) any later version.
-+
-+   This program is distributed in the hope that it will be useful,
-+   but WITHOUT ANY WARRANTY; without even the implied warranty of
-+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+   GNU General Public License for more details.
-+
-+   You should have received a copy of the GNU General Public License
-+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
-+*/
-+
-+#include "config.h"
-+
-+#include <krb5/krb5.h>
-+
-+#include "util/sss_iobuf.h"
-+#include "util/sss_krb5.h"
-+#include "util/util_creds.h"
-+#include "responder/kcm/kcm.h"
-+#include "responder/kcm/kcmsrv_pvt.h"
-+#include "responder/kcm/kcmsrv_ops.h"
-+#include "responder/kcm/kcmsrv_ccache.h"
-+
-+#define KCM_REPLY_MAX   2048
-+
-+struct kcm_op_ctx {
-+    struct kcm_resp_ctx *kcm_data;
-+    struct cli_creds *client;
-+
-+    struct sss_iobuf *input;
-+    struct sss_iobuf *reply;
-+};
-+
-+/* Each operation follows the same pattern and is implemented using
-+ * functions with this prototype. The operation receives an op_ctx
-+ * that serves as a state of the operation and can be used to keep
-+ * track of any temporary data. The operation writes its output data
-+ * into the op_ctx reply IO buffer and returns the op_ret status code
-+ * separately.
-+ *
-+ * The operation always returns EOK unless an internal error occurs,
-+ * the result of the operation is stored in the op_ret variable
-+ */
-+typedef struct tevent_req*
-+(*kcm_srv_send_method)(TALLOC_CTX *mem_ctx,
-+                       struct tevent_context *ev,
-+                       struct kcm_op_ctx *op_ctx);
-+typedef errno_t
-+(*kcm_srv_recv_method)(struct tevent_req *req,
-+                       uint32_t *_op_ret);
-+
-+struct kcm_op {
-+    const char *name;
-+    kcm_srv_send_method fn_send;
-+    kcm_srv_recv_method fn_recv;
-+};
-+
-+struct kcm_cmd_state {
-+    struct kcm_op *op;
-+
-+    struct kcm_op_ctx *op_ctx;
-+    struct sss_iobuf *reply;
-+
-+    uint32_t op_ret;
-+};
-+
-+static void kcm_cmd_done(struct tevent_req *subreq);
-+
-+struct tevent_req *kcm_cmd_send(TALLOC_CTX *mem_ctx,
-+                                struct tevent_context *ev,
-+                                struct kcm_resp_ctx *kcm_data,
-+                                struct cli_creds *client,
-+                                struct kcm_data *input,
-+                                struct kcm_op *op)
-+{
-+    struct tevent_req *req = NULL;
-+    struct tevent_req *subreq = NULL;
-+    struct kcm_cmd_state *state = NULL;
-+    errno_t ret;
-+
-+    req = tevent_req_create(mem_ctx, &state, struct kcm_cmd_state);
-+    if (req == NULL) {
-+        return NULL;
-+    }
-+    state->op = op;
-+
-+    if (op == NULL) {
-+        ret = EINVAL;
-+        goto immediate;
-+    }
-+
-+    DEBUG(SSSDBG_TRACE_FUNC, "KCM operation %s\n", op->name);
-+    DEBUG(SSSDBG_TRACE_LIBS, "%zu bytes on KCM input\n", input->length);
-+
-+    state->reply = sss_iobuf_init_empty(state,
-+                                        KCM_REPLY_MAX,
-+                                        KCM_REPLY_MAX);
-+    if (state->reply == NULL) {
-+        ret = ENOMEM;
-+        goto immediate;
-+    }
-+
-+    if (op->fn_send == NULL) {
-+        DEBUG(SSSDBG_CRIT_FAILURE,
-+              "KCM op %s has no handler\n", kcm_opt_name(op));
-+        ret = ERR_KCM_OP_NOT_IMPLEMENTED;
-+        goto immediate;
-+    }
-+
-+    /* Allocating op_ctx on the heap makes it possible for operations to use
-+     * op_ctx as their temporary context and avoid tmp_ctx altogether
-+     */
-+    state->op_ctx = talloc_zero(state, struct kcm_op_ctx);
-+    if (state->op_ctx == NULL) {
-+        ret = ENOMEM;
-+        goto immediate;
-+    }
-+
-+    state->op_ctx->kcm_data = kcm_data;
-+    state->op_ctx->client = client;
-+
-+    state->op_ctx->input = sss_iobuf_init_readonly(state->op_ctx,
-+                                                   input->data,
-+                                                   input->length);
-+    if (state->op_ctx->input == NULL) {
-+        ret = ENOMEM;
-+        goto immediate;
-+    }
-+
-+    /*
-+     * The internal operation returns the opcode and the buffer separately.
-+     * The KCM server reply to the client also always contains zero if the
-+     * operation ran to completion, both are uint32_t.
-+     * FIXME:
-+     * Alternatively, we could extend iobuf API so that we can just pass
-+     * the reply's buffer+sizeof(2*uint32_t) and avoid the useless allocations
-+     */
-+    state->op_ctx->reply = sss_iobuf_init_empty(
-+                                        state,
-+                                        KCM_REPLY_MAX - 2*sizeof(uint32_t),
-+                                        KCM_REPLY_MAX - 2*sizeof(uint32_t));
-+    if (state->reply == NULL) {
-+        ret = ENOMEM;
-+        goto immediate;
-+    }
-+
-+    subreq = op->fn_send(state, ev, state->op_ctx);
-+    if (subreq == NULL) {
-+        ret = ENOMEM;
-+        goto immediate;
-+    }
-+    tevent_req_set_callback(subreq, kcm_cmd_done, req);
-+    return req;
-+
-+immediate:
-+    tevent_req_error(req, ret);
-+    tevent_req_post(req, ev);
-+    return req;
-+}
-+
-+static void kcm_cmd_done(struct tevent_req *subreq)
-+{
-+    struct tevent_req *req = tevent_req_callback_data(subreq, struct tevent_req);
-+    struct kcm_cmd_state *state = tevent_req_data(req, struct kcm_cmd_state);
-+    errno_t ret;
-+    krb5_error_code kerr;
-+
-+    ret = state->op->fn_recv(subreq, &state->op_ret);
-+    talloc_free(subreq);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_OP_FAILURE,
-+              "op receive function failed [%d]: %s\n",
-+              ret, sss_strerror(ret));
-+        tevent_req_error(req, ret);
-+        return;
-+    }
-+
-+    DEBUG(SSSDBG_TRACE_FUNC,
-+          "KCM operation %s returned [%d]: %s\n",
-+          kcm_opt_name(state->op), state->op_ret, sss_strerror(state->op_ret));
-+
-+    kerr = sss2krb5_error(state->op_ret);
-+
-+    /* The first four bytes of the reply is the operation status code */
-+    ret = sss_iobuf_write_uint32(state->reply, htobe32(kerr));
-+    if (ret != EOK) {
-+        tevent_req_error(req, ret);
-+        return;
-+    }
-+
-+    ret = sss_iobuf_write_len(state->reply,
-+                              sss_iobuf_get_data(state->op_ctx->reply),
-+                              sss_iobuf_get_len(state->op_ctx->reply));
-+    if (ret != EOK) {
-+        tevent_req_error(req, ret);
-+        return;
-+    }
-+
-+    tevent_req_done(req);
-+}
-+
-+errno_t kcm_cmd_recv(TALLOC_CTX *mem_ctx,
-+                     struct tevent_req *req,
-+                     struct sss_iobuf **_reply)
-+{
-+    struct kcm_cmd_state *state = NULL;
-+
-+    TEVENT_REQ_RETURN_ON_ERROR(req);
-+
-+    state = tevent_req_data(req, struct kcm_cmd_state);
-+
-+    *_reply = talloc_steal(mem_ctx, state->reply);
-+    return EOK;
-+}
-+
-+/* ======= KCM operations ======= */
-+
-+/* Operations that don't return any extra information except for the op_ret
-+ * can use this macro in the _recv function to avoid code duplication
-+ */
-+#define KCM_OP_RET_FROM_TYPE(req, state_type, _op_ret_out) do {    \
-+    state_type *state = NULL;                                    \
-+    state = tevent_req_data(req, state_type);                    \
-+    TEVENT_REQ_RETURN_ON_ERROR(req);                             \
-+    *_op_ret_out = state->op_ret;                                \
-+    return EOK;                                                  \
-+} while(0);
-+
-+struct kcm_op_common_state {
-+    uint32_t op_ret;
-+    struct kcm_op_ctx *op_ctx;
-+    struct tevent_context *ev;
-+};
-+
-+static errno_t kcm_op_common_recv(struct tevent_req *req,
-+                                  uint32_t *_op_ret)
-+{
-+    struct kcm_op_common_state *state = tevent_req_data(req,
-+                                                struct kcm_op_common_state);
-+    TEVENT_REQ_RETURN_ON_ERROR(req);
-+    *_op_ret = state->op_ret;
-+    return EOK;
-+}
-+
-+/* () -> (name) */
-+static void kcm_op_gen_new_done(struct tevent_req *subreq);
-+
-+static struct tevent_req *kcm_op_gen_new_send(TALLOC_CTX *mem_ctx,
-+                                              struct tevent_context *ev,
-+                                              struct kcm_op_ctx *op_ctx)
-+{
-+    struct tevent_req *req = NULL;
-+    struct tevent_req *subreq = NULL;
-+    struct kcm_op_common_state *state = NULL;
-+    errno_t ret;
-+
-+    req = tevent_req_create(mem_ctx, &state, struct kcm_op_common_state);
-+    if (req == NULL) {
-+        return NULL;
-+    }
-+    state->op_ctx = op_ctx;
-+
-+    subreq = kcm_ccdb_nextid_send(state, ev,
-+                                  op_ctx->kcm_data->db,
-+                                  op_ctx->client);
-+    if (subreq == NULL) {
-+        ret = ENOMEM;
-+        goto immediate;
-+    }
-+    tevent_req_set_callback(subreq, kcm_op_gen_new_done, req);
-+    return req;
-+
-+immediate:
-+    tevent_req_error(req, ret);
-+    tevent_req_post(req, ev);
-+    return req;
-+}
-+
-+static void kcm_op_gen_new_done(struct tevent_req *subreq)
-+{
-+    errno_t ret;
-+    char *newid;
-+    struct tevent_req *req = tevent_req_callback_data(subreq,
-+                                                      struct tevent_req);
-+    struct kcm_op_common_state *state = tevent_req_data(req,
-+                                                struct kcm_op_common_state);
-+
-+    ret = kcm_ccdb_nextid_recv(subreq, state, &newid);
-+    talloc_zfree(subreq);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_OP_FAILURE,
-+              "Cannot generate a new ID [%d]: %s\n",
-+              ret, sss_strerror(ret));
-+        tevent_req_error(req, ret);
-+        return;
-+    }
-+
-+    DEBUG(SSSDBG_TRACE_LIBS, "Generated a new ID %s\n", newid);
-+
-+    ret = sss_iobuf_write_stringz(state->op_ctx->reply, newid);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_OP_FAILURE,
-+              "Cannot write generated ID %d: %s\n",
-+              ret, sss_strerror(ret));
-+        tevent_req_error(req, ret);
-+        return;
-+    }
-+
-+    state->op_ret = EOK;
-+    tevent_req_done(req);
-+}
-+
-+/* (princ) -> () */
-+struct kcm_op_initialize_state {
-+    uint32_t op_ret;
-+    struct kcm_op_ctx *op_ctx;
-+    struct tevent_context *ev;
-+
-+    struct kcm_ccache *new_cc;
-+    const char *name;
-+    krb5_principal princ;
-+};
-+
-+static void kcm_op_initialize_got_byname(struct tevent_req *subreq);
-+static void kcm_op_initialize_cc_create_done(struct tevent_req *subreq);
-+static void kcm_op_initialize_cc_delete_done(struct tevent_req *subreq);
-+static void kcm_op_initialize_create_step(struct tevent_req *req);
-+static void kcm_op_initialize_got_default(struct tevent_req *subreq);
-+static void kcm_op_initialize_set_default_done(struct tevent_req *subreq);
-+
-+static struct tevent_req *kcm_op_initialize_send(TALLOC_CTX *mem_ctx,
-+                                                 struct tevent_context *ev,
-+                                                 struct kcm_op_ctx *op_ctx)
-+{
-+    struct tevent_req *req = NULL;
-+    struct tevent_req *subreq = NULL;
-+    struct kcm_op_initialize_state *state = NULL;
-+    errno_t ret;
-+
-+    req = tevent_req_create(mem_ctx, &state, struct kcm_op_initialize_state);
-+    if (req == NULL) {
-+        return NULL;
-+    }
-+    state->op_ctx = op_ctx;
-+    state->ev = ev;
-+
-+    ret = sss_iobuf_read_stringz(op_ctx->input, &state->name);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_CRIT_FAILURE,
-+              "Cannot read input name [%d]: %s\n",
-+              ret, sss_strerror(ret));
-+        goto immediate;
-+    }
-+    DEBUG(SSSDBG_TRACE_LIBS, "Initializing ccache %s\n", state->name);
-+
-+    ret = kcm_check_name(state->name, op_ctx->client);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_CRIT_FAILURE,
-+              "Name %s is malformed [%d]: %s\n",
-+              state->name, ret, sss_strerror(ret));
-+        goto immediate;
-+    }
-+
-+    ret = sss_krb5_unmarshal_princ(op_ctx, op_ctx->input, &state->princ);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_CRIT_FAILURE,
-+              "Cannot unmarshal principal [%d]: %s\n",
-+              ret, sss_strerror(ret));
-+        goto immediate;
-+    }
-+
-+    subreq = kcm_ccdb_getbyname_send(state, ev,
-+                                     op_ctx->kcm_data->db,
-+                                     op_ctx->client,
-+                                     state->name);
-+    if (subreq == NULL) {
-+        ret = ENOMEM;
-+        goto immediate;
-+    }
-+    tevent_req_set_callback(subreq, kcm_op_initialize_got_byname, req);
-+    return req;
-+
-+immediate:
-+    tevent_req_error(req, ret);
-+    tevent_req_post(req, ev);
-+    return req;
-+}
-+
-+static void kcm_op_initialize_got_byname(struct tevent_req *subreq)
-+{
-+    errno_t ret;
-+    struct tevent_req *req = tevent_req_callback_data(subreq,
-+                                                      struct tevent_req);
-+    struct kcm_op_initialize_state *state = tevent_req_data(req,
-+                                            struct kcm_op_initialize_state);
-+    bool ok;
-+    uuid_t uuid;
-+
-+    ret = kcm_ccdb_getbyname_recv(subreq, state, &state->new_cc);
-+    talloc_zfree(subreq);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_OP_FAILURE,
-+              "Cannot get ccache by name [%d]: %s\n",
-+              ret, sss_strerror(ret));
-+        tevent_req_error(req, ret);
-+        return;
-+    }
-+
-+    if (state->new_cc != NULL) {
-+        ok = kcm_cc_access(state->new_cc, state->op_ctx->client);
-+        if (!ok) {
-+            state->op_ret = EACCES;
-+            tevent_req_done(req);
-+            return;
-+        }
-+
-+        ret = kcm_cc_get_uuid(state->new_cc, uuid);
-+        if (ret != EOK) {
-+            DEBUG(SSSDBG_OP_FAILURE,
-+              "Cannot get new ccache UUID [%d]: %s\n",
-+              ret, sss_strerror(ret));
-+            return;
-+        }
-+
-+        /* Nuke any previous cache and its contents during initialization */
-+        subreq = kcm_ccdb_delete_cc_send(state,
-+                                         state->ev,
-+                                         state->op_ctx->kcm_data->db,
-+                                         state->op_ctx->client,
-+                                         uuid);
-+        if (subreq == NULL) {
-+            tevent_req_error(req, ret);
-+            return;
-+        }
-+        tevent_req_set_callback(subreq, kcm_op_initialize_cc_delete_done, req);
-+        return;
-+    }
-+
-+    kcm_op_initialize_create_step(req);
-+}
-+
-+static void kcm_op_initialize_cc_delete_done(struct tevent_req *subreq)
-+{
-+    struct tevent_req *req = tevent_req_callback_data(subreq,
-+                                                      struct tevent_req);
-+    errno_t ret;
-+
-+    ret = kcm_ccdb_delete_cc_recv(subreq);
-+    talloc_zfree(subreq);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_OP_FAILURE,
-+              "Cannot delete ccache from the db %d: %s\n",
-+              ret, sss_strerror(ret));
-+        tevent_req_error(req, ret);
-+        return;
-+    }
-+
-+    kcm_op_initialize_create_step(req);
-+}
-+
-+static void kcm_op_initialize_create_step(struct tevent_req *req)
-+{
-+    struct tevent_req *subreq;
-+    struct kcm_op_initialize_state *state = tevent_req_data(req,
-+                                            struct kcm_op_initialize_state);
-+    errno_t ret;
-+
-+    ret = kcm_cc_new(state->op_ctx,
-+                     state->op_ctx->kcm_data->k5c,
-+                     state->op_ctx->client,
-+                     state->name,
-+                     state->princ,
-+                     &state->new_cc);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_OP_FAILURE,
-+              "Cannot create new ccache %d: %s\n", ret, sss_strerror(ret));
-+        tevent_req_error(req, ret);
-+        return;
-+    }
-+
-+    subreq = kcm_ccdb_create_cc_send(state,
-+                                     state->ev,
-+                                     state->op_ctx->kcm_data->db,
-+                                     state->op_ctx->client,
-+                                     state->new_cc);
-+    if (subreq == NULL) {
-+        tevent_req_error(req, ret);
-+        return;
-+    }
-+    tevent_req_set_callback(subreq, kcm_op_initialize_cc_create_done, req);
-+}
-+
-+static void kcm_op_initialize_cc_create_done(struct tevent_req *subreq)
-+{
-+    struct tevent_req *req = tevent_req_callback_data(subreq,
-+                                                      struct tevent_req);
-+    struct kcm_op_initialize_state *state = tevent_req_data(req,
-+                                            struct kcm_op_initialize_state);
-+    errno_t ret;
-+
-+    ret = kcm_ccdb_create_cc_recv(subreq);
-+    talloc_zfree(subreq);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_OP_FAILURE,
-+              "Cannot add ccache to db %d: %s\n", ret, sss_strerror(ret));
-+        tevent_req_error(req, ret);
-+        return;
-+    }
-+
-+    /* If there was no previous default ccache, set this one as default */
-+    subreq = kcm_ccdb_get_default_send(state, state->ev,
-+                                       state->op_ctx->kcm_data->db,
-+                                       state->op_ctx->client);
-+    if (subreq == NULL) {
-+        tevent_req_error(req, ret);
-+        return;
-+    }
-+    tevent_req_set_callback(subreq, kcm_op_initialize_got_default, req);
-+}
-+
-+static void kcm_op_initialize_got_default(struct tevent_req *subreq)
-+{
-+    struct tevent_req *req = tevent_req_callback_data(subreq,
-+                                                      struct tevent_req);
-+    struct kcm_op_initialize_state *state = tevent_req_data(req,
-+                                            struct kcm_op_initialize_state);
-+    errno_t ret;
-+    uuid_t dfl_uuid;
-+    uuid_t old_dfl_uuid;
-+
-+    ret = kcm_ccdb_get_default_recv(subreq, &old_dfl_uuid);
-+    talloc_zfree(subreq);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_OP_FAILURE,
-+              "Cannot get default ccache [%d]: %s\n",
-+              ret, sss_strerror(ret));
-+        tevent_req_error(req, ret);
-+        return;
-+    }
-+
-+    if (uuid_is_null(old_dfl_uuid) == false) {
-+        /* If there was a previous default ccache, switch to the initialized
-+         * one by default
-+         */
-+        ret = kcm_cc_get_uuid(state->new_cc, dfl_uuid);
-+        if (ret != EOK) {
-+            DEBUG(SSSDBG_OP_FAILURE,
-+              "Cannot get new ccache UUID [%d]: %s\n",
-+              ret, sss_strerror(ret));
-+            return;
-+        }
-+
-+        subreq = kcm_ccdb_set_default_send(state,
-+                                           state->ev,
-+                                           state->op_ctx->kcm_data->db,
-+                                           state->op_ctx->client,
-+                                           dfl_uuid);
-+        if (subreq == NULL) {
-+            tevent_req_error(req, ENOMEM);
-+            return;
-+        }
-+        tevent_req_set_callback(subreq, kcm_op_initialize_set_default_done, req);
-+        return;
-+    }
-+
-+    /* ENOENT, done */
-+    state->op_ret = EOK;
-+    tevent_req_done(req);
-+}
-+
-+static void kcm_op_initialize_set_default_done(struct tevent_req *subreq)
-+{
-+    struct tevent_req *req = tevent_req_callback_data(subreq,
-+                                                      struct tevent_req);
-+    struct kcm_op_initialize_state *state = tevent_req_data(req,
-+                                            struct kcm_op_initialize_state);
-+    errno_t ret;
-+
-+    ret = kcm_ccdb_set_default_recv(subreq);
-+    talloc_zfree(subreq);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_OP_FAILURE,
-+              "Cannot set default ccache %d: %s\n", ret, sss_strerror(ret));
-+        tevent_req_error(req, ret);
-+        return;
-+    }
-+
-+    state->op_ret = EOK;
-+    tevent_req_done(req);
-+}
-+
-+static errno_t kcm_op_initialize_recv(struct tevent_req *req,
-+                                      uint32_t *_op_ret)
-+{
-+    KCM_OP_RET_FROM_TYPE(req, struct kcm_op_initialize_state, _op_ret);
-+}
-+
-+/* (name) -> () */
-+static void kcm_op_destroy_getbyname_done(struct tevent_req *subreq);
-+static void kcm_op_destroy_delete_done(struct tevent_req *subreq);
-+
-+static struct tevent_req *kcm_op_destroy_send(TALLOC_CTX *mem_ctx,
-+                                              struct tevent_context *ev,
-+                                              struct kcm_op_ctx *op_ctx)
-+{
-+    struct tevent_req *req = NULL;
-+    struct tevent_req *subreq = NULL;
-+    struct kcm_op_common_state *state = NULL;
-+    errno_t ret;
-+    const char *name;
-+
-+    req = tevent_req_create(mem_ctx, &state, struct kcm_op_common_state);
-+    if (req == NULL) {
-+        return NULL;
-+    }
-+    state->op_ctx = op_ctx;
-+    state->ev = ev;
-+
-+    ret = sss_iobuf_read_stringz(op_ctx->input, &name);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_OP_FAILURE,
-+              "Cannot unmarshall input name [%d]: %s\n",
-+              ret, sss_strerror(ret));
-+        goto immediate;
-+    }
-+
-+    DEBUG(SSSDBG_TRACE_LIBS, "Destroying credentials of %s\n", name);
-+
-+    subreq = kcm_ccdb_uuid_by_name_send(state, ev,
-+                                        op_ctx->kcm_data->db,
-+                                        op_ctx->client,
-+                                        name);
-+    if (subreq == NULL) {
-+        ret = ENOMEM;
-+        goto immediate;
-+    }
-+    tevent_req_set_callback(subreq, kcm_op_destroy_getbyname_done, req);
-+    return req;
-+
-+immediate:
-+    tevent_req_error(req, ret);
-+    tevent_req_post(req, ev);
-+    return req;
-+}
-+
-+static void kcm_op_destroy_getbyname_done(struct tevent_req *subreq)
-+{
-+    errno_t ret;
-+    struct tevent_req *req = tevent_req_callback_data(subreq,
-+                                                      struct tevent_req);
-+    struct kcm_op_common_state *state = tevent_req_data(req,
-+                                                struct kcm_op_common_state);
-+    uuid_t uuid;
-+
-+    ret = kcm_ccdb_uuid_by_name_recv(subreq, state, uuid);
-+    talloc_zfree(subreq);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_OP_FAILURE,
-+              "Cannot get matching ccache [%d]: %s\n",
-+              ret, sss_strerror(ret));
-+        tevent_req_error(req, ret);
-+        return;
-+    }
-+
-+    subreq = kcm_ccdb_delete_cc_send(state,
-+                                     state->ev,
-+                                     state->op_ctx->kcm_data->db,
-+                                     state->op_ctx->client,
-+                                     uuid);
-+    if (subreq == NULL) {
-+        tevent_req_error(req, ret);
-+        return;
-+    }
-+    tevent_req_set_callback(subreq, kcm_op_destroy_delete_done, req);
-+}
-+
-+static void kcm_op_destroy_delete_done(struct tevent_req *subreq)
-+{
-+    errno_t ret;
-+    struct tevent_req *req = tevent_req_callback_data(subreq,
-+                                                      struct tevent_req);
-+    struct kcm_op_common_state *state = tevent_req_data(req,
-+                                                struct kcm_op_common_state);
-+
-+    ret = kcm_ccdb_delete_cc_recv(subreq);
-+    talloc_zfree(subreq);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_OP_FAILURE,
-+              "Cannot delete ccache from the db [%d]: %s\n",
-+              ret, sss_strerror(ret));
-+        tevent_req_error(req, ret);
-+        return;
-+    }
-+
-+    state->op_ret = EOK;
-+    tevent_req_done(req);
-+}
-+
-+/* (name, cred) -> () */
-+struct kcm_op_store_state {
-+    uint32_t op_ret;
-+    struct kcm_op_ctx *op_ctx;
-+    struct tevent_context *ev;
-+
-+    struct sss_iobuf *cred_blob;
-+};
-+
-+static void kcm_op_store_getbyname_done(struct tevent_req *subreq);
-+static void kcm_op_store_done(struct tevent_req *subreq);
-+
-+static struct tevent_req *kcm_op_store_send(TALLOC_CTX *mem_ctx,
-+                                            struct tevent_context *ev,
-+                                            struct kcm_op_ctx *op_ctx)
-+{
-+    struct tevent_req *req = NULL;
-+    struct tevent_req *subreq = NULL;
-+    struct kcm_op_store_state *state = NULL;
-+    errno_t ret;
-+    const char *name;
-+    size_t creds_len;
-+
-+    req = tevent_req_create(mem_ctx, &state, struct kcm_op_store_state);
-+    if (req == NULL) {
-+        return NULL;
-+    }
-+    state->op_ctx = op_ctx;
-+    state->ev = ev;
-+
-+    ret = sss_iobuf_read_stringz(op_ctx->input, &name);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_OP_FAILURE,
-+              "Cannot unmarshall input name [%d]: %s\n",
-+              ret, sss_strerror(ret));
-+        goto immediate;
-+    }
-+
-+    DEBUG(SSSDBG_TRACE_LIBS, "Storing credentials for %s\n", name);
-+
-+    creds_len = sss_iobuf_get_size(op_ctx->input) - strlen(name) -1;
-+    if (creds_len > KCM_REPLY_MAX) {
-+        /* Protects against underflows and in general adds sanity */
-+        ret = E2BIG;
-+        goto immediate;
-+    }
-+
-+    state->cred_blob = sss_iobuf_init_empty(state,
-+                                            creds_len,
-+                                            creds_len);
-+    if (state->cred_blob == NULL) {
-+        ret = ENOMEM;
-+        goto immediate;
-+    }
-+
-+    ret = sss_iobuf_read(op_ctx->input,
-+                         creds_len,
-+                         sss_iobuf_get_data(state->cred_blob),
-+                         NULL);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_OP_FAILURE,
-+              "Cannot unmarshall input cred blob [%d]: %s\n",
-+              ret, sss_strerror(ret));
-+        goto immediate;
-+    }
-+
-+    subreq = kcm_ccdb_uuid_by_name_send(state, ev,
-+                                        op_ctx->kcm_data->db,
-+                                        op_ctx->client,
-+                                        name);
-+    if (subreq == NULL) {
-+        ret = ENOMEM;
-+        goto immediate;
-+    }
-+    tevent_req_set_callback(subreq, kcm_op_store_getbyname_done, req);
-+    return req;
-+
-+immediate:
-+    tevent_req_error(req, ret);
-+    tevent_req_post(req, ev);
-+    return req;
-+}
-+
-+static void kcm_op_store_getbyname_done(struct tevent_req *subreq)
-+{
-+    errno_t ret;
-+    struct tevent_req *req = tevent_req_callback_data(subreq,
-+                                                      struct tevent_req);
-+    struct kcm_op_store_state *state = tevent_req_data(req,
-+                                                struct kcm_op_store_state);
-+    uuid_t uuid;
-+
-+    ret = kcm_ccdb_uuid_by_name_recv(subreq, state, uuid);
-+    talloc_zfree(subreq);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_OP_FAILURE,
-+              "Cannot get ccache by name [%d]: %s\n",
-+              ret, sss_strerror(ret));
-+        tevent_req_error(req, ret);
-+        return;
-+    }
-+
-+    subreq = kcm_ccdb_store_cred_blob_send(state, state->ev,
-+                                           state->op_ctx->kcm_data->db,
-+                                           state->op_ctx->client,
-+                                           uuid,
-+                                           state->cred_blob);
-+    if (subreq == NULL) {
-+        tevent_req_error(req, ENOMEM);
-+        return;
-+    }
-+    tevent_req_set_callback(subreq, kcm_op_store_done, req);
-+}
-+
-+static void kcm_op_store_done(struct tevent_req *subreq)
-+{
-+    errno_t ret;
-+    struct tevent_req *req = tevent_req_callback_data(subreq,
-+                                                      struct tevent_req);
-+    struct kcm_op_store_state *state = tevent_req_data(req,
-+                                                struct kcm_op_store_state);
-+
-+    ret = kcm_ccdb_store_cred_blob_recv(subreq);
-+    talloc_zfree(subreq);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_OP_FAILURE,
-+              "Cannot store credentials [%d]: %s\n",
-+              ret, sss_strerror(ret));
-+        tevent_req_error(req, ret);
-+        return;
-+    }
-+
-+    state->op_ret = EOK;
-+    tevent_req_done(req);
-+}
-+
-+static errno_t kcm_op_store_recv(struct tevent_req *req,
-+                                 uint32_t *_op_ret)
-+{
-+    KCM_OP_RET_FROM_TYPE(req, struct kcm_op_store_state, _op_ret);
-+}
-+
-+/* (name) -> (princ) */
-+static void kcm_op_get_principal_getbyname_done(struct tevent_req *subreq);
-+
-+static struct tevent_req *kcm_op_get_principal_send(TALLOC_CTX *mem_ctx,
-+                                                    struct tevent_context *ev,
-+                                                    struct kcm_op_ctx *op_ctx)
-+{
-+    struct tevent_req *req = NULL;
-+    struct tevent_req *subreq = NULL;
-+    struct kcm_op_common_state *state = NULL;
-+    errno_t ret;
-+    const char *name;
-+
-+    req = tevent_req_create(mem_ctx, &state, struct kcm_op_common_state);
-+    if (req == NULL) {
-+        return NULL;
-+    }
-+    state->op_ctx = op_ctx;
-+
-+    ret = sss_iobuf_read_stringz(op_ctx->input, &name);
-+    if (ret != EOK) {
-+        goto immediate;
-+    }
-+    DEBUG(SSSDBG_TRACE_LIBS, "Requested principal %s\n", name);
-+
-+    subreq = kcm_ccdb_getbyname_send(state, ev,
-+                                     op_ctx->kcm_data->db,
-+                                     op_ctx->client,
-+                                     name);
-+    if (subreq == NULL) {
-+        ret = ENOMEM;
-+        goto immediate;
-+    }
-+    tevent_req_set_callback(subreq, kcm_op_get_principal_getbyname_done, req);
-+    return req;
-+
-+immediate:
-+    tevent_req_error(req, ret);
-+    tevent_req_post(req, ev);
-+    return req;
-+}
-+
-+static void kcm_op_get_principal_getbyname_done(struct tevent_req *subreq)
-+{
-+    errno_t ret;
-+    struct kcm_ccache *cc;
-+    krb5_principal princ;
-+    struct tevent_req *req = tevent_req_callback_data(subreq,
-+                                                      struct tevent_req);
-+    struct kcm_op_common_state *state = tevent_req_data(req,
-+                                                struct kcm_op_common_state);
-+
-+    ret = kcm_ccdb_getbyname_recv(subreq, state, &cc);
-+    talloc_zfree(subreq);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_OP_FAILURE,
-+              "Cannot get ccache by name [%d]: %s\n",
-+              ret, sss_strerror(ret));
-+        tevent_req_error(req, ret);
-+        return;
-+    }
-+
-+    if (cc == NULL) {
-+        DEBUG(SSSDBG_MINOR_FAILURE, "No credentials by that name\n");
-+        state->op_ret = ERR_NO_MATCHING_CREDS;
-+        tevent_req_done(req);
-+        return;
-+    }
-+
-+    /* Marshall the principal to the reply */
-+    princ = kcm_cc_get_client_principal(cc);
-+    if (princ == NULL) {
-+        DEBUG(SSSDBG_OP_FAILURE,
-+              "Credentials with no principal?\n");
-+        tevent_req_error(req, EIO);
-+        return;
-+    }
-+
-+    ret = sss_krb5_marshal_princ(princ, state->op_ctx->reply);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_OP_FAILURE,
-+              "Cannot marshall principal [%d]: %s\n",
-+              ret, sss_strerror(ret));
-+        tevent_req_error(req, ret);
-+        return;
-+    }
-+
-+    state->op_ret = EOK;
-+    tevent_req_done(req);
-+}
-+
-+/* (name) -> (uuid, ...) */
-+static void kcm_op_get_cred_uuid_getbyname_done(struct tevent_req *subreq);
-+
-+static struct tevent_req *
-+kcm_op_get_cred_uuid_list_send(TALLOC_CTX *mem_ctx,
-+                               struct tevent_context *ev,
-+                               struct kcm_op_ctx *op_ctx)
-+{
-+    struct tevent_req *req = NULL;
-+    struct tevent_req *subreq = NULL;
-+    struct kcm_op_common_state *state = NULL;
-+    errno_t ret;
-+    const char *name;
-+
-+    req = tevent_req_create(mem_ctx, &state, struct kcm_op_common_state);
-+    if (req == NULL) {
-+        return NULL;
-+    }
-+    state->op_ctx = op_ctx;
-+
-+    ret = sss_iobuf_read_stringz(op_ctx->input, &name);
-+    if (ret != EOK) {
-+        goto immediate;
-+    }
-+
-+    DEBUG(SSSDBG_TRACE_LIBS, "Returning UUID list for %s\n", name);
-+
-+    subreq = kcm_ccdb_getbyname_send(state, ev,
-+                                     op_ctx->kcm_data->db,
-+                                     op_ctx->client,
-+                                     name);
-+    if (subreq == NULL) {
-+        ret = ENOMEM;
-+        goto immediate;
-+    }
-+    tevent_req_set_callback(subreq, kcm_op_get_cred_uuid_getbyname_done, req);
-+    return req;
-+
-+immediate:
-+    tevent_req_error(req, ret);
-+    tevent_req_post(req, ev);
-+    return req;
-+}
-+
-+static void kcm_op_get_cred_uuid_getbyname_done(struct tevent_req *subreq)
-+{
-+    errno_t ret;
-+    struct kcm_ccache *cc;
-+    struct kcm_cred *crd;
-+    uuid_t uuid;
-+    struct tevent_req *req = tevent_req_callback_data(subreq,
-+                                                      struct tevent_req);
-+    struct kcm_op_common_state *state = tevent_req_data(req,
-+                                                struct kcm_op_common_state);
-+
-+    ret = kcm_ccdb_getbyname_recv(subreq, state, &cc);
-+    talloc_zfree(subreq);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_OP_FAILURE,
-+              "Cannot get ccache by name [%d]: %s\n",
-+              ret, sss_strerror(ret));
-+        tevent_req_error(req, ret);
-+        return;
-+    }
-+
-+    if (cc == NULL) {
-+        DEBUG(SSSDBG_MINOR_FAILURE, "No credentials by that UUID\n");
-+        state->op_ret = ERR_NO_CREDS;
-+        tevent_req_done(req);
-+        return;
-+    }
-+
-+    for (crd = kcm_cc_get_cred(cc);
-+         crd != NULL;
-+         crd = kcm_cc_next_cred(crd)) {
-+        ret = kcm_cred_get_uuid(crd, uuid);
-+        if (ret != EOK) {
-+            DEBUG(SSSDBG_MINOR_FAILURE, "Credential has no UUID, skipping\n");
-+            continue;
-+        }
-+
-+        kcm_debug_uuid(uuid);
-+
-+        ret = sss_iobuf_write_len(state->op_ctx->reply,
-+                                  uuid, UUID_BYTES);
-+        if (ret != EOK) {
-+            char uuid_errbuf[UUID_STR_SIZE];
-+            uuid_parse(uuid_errbuf, uuid);
-+            DEBUG(SSSDBG_MINOR_FAILURE,
-+                  "Cannot marshall UUID %s [%d]: %s\n",
-+                  uuid_errbuf, ret, sss_strerror(ret));
-+            continue;
-+        }
-+    }
-+    state->op_ret = EOK;
-+    tevent_req_done(req);
-+}
-+
-+/* (name, uuid) -> (cred) */
-+static void kcm_op_get_cred_by_uuid_getbyname_done(struct tevent_req *subreq);
-+
-+static struct tevent_req *
-+kcm_op_get_cred_by_uuid_send(TALLOC_CTX *mem_ctx,
-+                             struct tevent_context *ev,
-+                             struct kcm_op_ctx *op_ctx)
-+{
-+    struct tevent_req *req = NULL;
-+    struct tevent_req *subreq = NULL;
-+    struct kcm_op_common_state *state = NULL;
-+    errno_t ret;
-+    const char *name;
-+
-+    req = tevent_req_create(mem_ctx, &state, struct kcm_op_common_state);
-+    if (req == NULL) {
-+        return NULL;
-+    }
-+    state->op_ctx = op_ctx;
-+
-+    ret = sss_iobuf_read_stringz(op_ctx->input, &name);
-+    if (ret != EOK) {
-+        goto immediate;
-+    }
-+    DEBUG(SSSDBG_TRACE_LIBS, "Returning creds by UUID for %s\n", name);
-+
-+    subreq = kcm_ccdb_getbyname_send(state, ev,
-+                                     op_ctx->kcm_data->db,
-+                                     op_ctx->client,
-+                                     name);
-+    if (subreq == NULL) {
-+        ret = ENOMEM;
-+        goto immediate;
-+    }
-+    tevent_req_set_callback(subreq, kcm_op_get_cred_by_uuid_getbyname_done, req);
-+    return req;
-+
-+immediate:
-+    tevent_req_error(req, ret);
-+    tevent_req_post(req, ev);
-+    return req;
-+}
-+
-+static void kcm_op_get_cred_by_uuid_getbyname_done(struct tevent_req *subreq)
-+{
-+    struct tevent_req *req = tevent_req_callback_data(subreq,
-+                                                      struct tevent_req);
-+    struct kcm_op_common_state *state = tevent_req_data(req,
-+                                                struct kcm_op_common_state);
-+    errno_t ret;
-+    struct kcm_ccache *cc;
-+    struct kcm_cred *crd;
-+    uuid_t uuid_in;
-+    uuid_t uuid;
-+    struct sss_iobuf *cred_blob;
-+
-+    ret = kcm_ccdb_getbyname_recv(subreq, state, &cc);
-+    talloc_zfree(subreq);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_OP_FAILURE,
-+              "Cannot get ccache by name [%d]: %s\n",
-+              ret, sss_strerror(ret));
-+        tevent_req_error(req, ret);
-+        return;
-+    }
-+
-+    if (cc == NULL) {
-+        DEBUG(SSSDBG_MINOR_FAILURE, "No credentials by that name\n");
-+        state->op_ret = ERR_NO_MATCHING_CREDS;
-+        tevent_req_done(req);
-+        return;
-+    }
-+
-+    ret = sss_iobuf_read_len(state->op_ctx->input,
-+                             UUID_BYTES, uuid_in);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_OP_FAILURE,
-+              "Cannot read input UUID [%d]: %s\n",
-+              ret, sss_strerror(ret));
-+        tevent_req_error(req, ret);
-+        return;
-+    }
-+
-+    for (crd = kcm_cc_get_cred(cc);
-+         crd != NULL;
-+         crd = kcm_cc_next_cred(crd)) {
-+        ret = kcm_cred_get_uuid(crd, uuid);
-+        if (ret != EOK) {
-+            DEBUG(SSSDBG_MINOR_FAILURE,
-+                  "Cannot get UUID from creds, skipping\n");
-+            continue;
-+        }
-+
-+        if (uuid_compare(uuid, uuid_in) == 0) {
-+            break;
-+        }
-+        kcm_debug_uuid(uuid);
-+    }
-+
-+    if (crd == NULL) {
-+        state->op_ret = ERR_KCM_CC_END;
-+        DEBUG(SSSDBG_MINOR_FAILURE, "No credentials by that UUID\n");
-+        tevent_req_done(req);
-+        return;
-+    }
-+
-+    cred_blob = kcm_cred_get_creds(crd);
-+    if (cred_blob == NULL) {
-+        DEBUG(SSSDBG_CRIT_FAILURE, "Credentials lack the creds blob\n");
-+        state->op_ret = ERR_NO_CREDS;
-+        tevent_req_done(req);
-+        return;
-+    }
-+
-+    ret = sss_iobuf_write_len(state->op_ctx->reply,
-+                              sss_iobuf_get_data(cred_blob),
-+                              sss_iobuf_get_size(cred_blob));
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_OP_FAILURE,
-+              "Cannot write ccache blob [%d]: %s\n",
-+              ret, sss_strerror(ret));
-+        tevent_req_error(req, ret);
-+        return;
-+    }
-+
-+    state->op_ret = EOK;
-+    tevent_req_done(req);
-+}
-+
-+/* (name, flags, credtag) -> () */
-+/* FIXME */
-+static struct tevent_req *
-+kcm_op_remove_cred_send(TALLOC_CTX *mem_ctx,
-+                        struct tevent_context *ev,
-+                        struct kcm_op_ctx *op_ctx)
-+{
-+    struct tevent_req *req = NULL;
-+    struct kcm_op_common_state *state = NULL;
-+
-+    req = tevent_req_create(mem_ctx, &state, struct kcm_op_common_state);
-+    if (req == NULL) {
-+        return NULL;
-+    }
-+    state->op_ctx = op_ctx;
-+
-+    state->op_ret = ERR_KCM_OP_NOT_IMPLEMENTED;
-+    tevent_req_post(req, ev);
-+    tevent_req_done(req);
-+    return req;
-+}
-+
-+/* () -> (uuid, ...) */
-+static void kcm_op_get_cache_uuid_list_done(struct tevent_req *subreq);
-+
-+static struct tevent_req *
-+kcm_op_get_cache_uuid_list_send(TALLOC_CTX *mem_ctx,
-+                                struct tevent_context *ev,
-+                                struct kcm_op_ctx *op_ctx)
-+{
-+    struct tevent_req *req = NULL;
-+    struct tevent_req *subreq = NULL;
-+    struct kcm_op_common_state *state = NULL;
-+    errno_t ret;
-+
-+    req = tevent_req_create(mem_ctx, &state, struct kcm_op_common_state);
-+    if (req == NULL) {
-+        return NULL;
-+    }
-+    state->op_ctx = op_ctx;
-+
-+    DEBUG(SSSDBG_TRACE_LIBS, "Returning full UUID list\n");
-+
-+    subreq = kcm_ccdb_list_send(state, ev,
-+                                op_ctx->kcm_data->db,
-+                                op_ctx->client);
-+    if (subreq == NULL) {
-+        ret = ENOMEM;
-+        goto immediate;
-+    }
-+    tevent_req_set_callback(subreq, kcm_op_get_cache_uuid_list_done, req);
-+    return req;
-+
-+immediate:
-+    tevent_req_error(req, ret);
-+    tevent_req_post(req, ev);
-+    return req;
-+}
-+
-+static void kcm_op_get_cache_uuid_list_done(struct tevent_req *subreq)
-+{
-+    struct tevent_req *req = tevent_req_callback_data(subreq,
-+                                                      struct tevent_req);
-+    struct kcm_op_common_state *state = tevent_req_data(req,
-+                                                struct kcm_op_common_state);
-+    errno_t ret;
-+    uuid_t *uuid_list;
-+
-+    ret = kcm_ccdb_list_recv(subreq, state, &uuid_list);
-+    talloc_zfree(subreq);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_OP_FAILURE,
-+              "Cannot list the ccache DB [%d]: %s\n",
-+              ret, sss_strerror(ret));
-+        tevent_req_error(req, ret);
-+        return;
-+    }
-+
-+    if (uuid_list == NULL || uuid_list[0] == NULL) {
-+        DEBUG(SSSDBG_MINOR_FAILURE, "Nothing to list\n");
-+        state->op_ret = ERR_NO_MATCHING_CREDS;
-+        tevent_req_done(req);
-+        return;
-+    }
-+
-+    for (int i = 0;
-+         uuid_is_null(uuid_list[i]) == false;
-+         i++) {
-+        kcm_debug_uuid(uuid_list[i]);
-+
-+        ret = sss_iobuf_write_len(state->op_ctx->reply,
-+                                  uuid_list[i],
-+                                  UUID_BYTES);
-+        if (ret != EOK) {
-+            char uuid_errbuf[UUID_STR_SIZE];
-+            uuid_parse(uuid_errbuf, uuid_list[i]);
-+            DEBUG(SSSDBG_MINOR_FAILURE,
-+                  "Cannot marshall UUID %s [%d]: %s\n",
-+                  uuid_errbuf, ret, sss_strerror(ret));
-+            tevent_req_done(req);
-+            return;
-+        }
-+    }
-+
-+    tevent_req_done(req);
-+}
-+
-+/* (uuid) -> (name) */
-+static void kcm_op_get_cache_by_uuid_done(struct tevent_req *subreq);
-+
-+static struct tevent_req *
-+kcm_op_get_cache_by_uuid_send(TALLOC_CTX *mem_ctx,
-+                              struct tevent_context *ev,
-+                              struct kcm_op_ctx *op_ctx)
-+{
-+    struct tevent_req *req = NULL;
-+    struct tevent_req *subreq = NULL;
-+    struct kcm_op_common_state *state = NULL;
-+    errno_t ret;
-+    uuid_t uuid_in;
-+
-+    req = tevent_req_create(mem_ctx, &state, struct kcm_op_common_state);
-+    if (req == NULL) {
-+        return NULL;
-+    }
-+    state->op_ctx = op_ctx;
-+
-+    DEBUG(SSSDBG_TRACE_LIBS, "Retrieving cache by UUID\n");
-+
-+    ret = sss_iobuf_read_len(op_ctx->input,
-+                             UUID_BYTES, uuid_in);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_OP_FAILURE,
-+              "Cannot read input UUID [%d]: %s\n",
-+              ret, sss_strerror(ret));
-+        goto immediate;
-+    }
-+    kcm_debug_uuid(uuid_in);
-+
-+    subreq = kcm_ccdb_getbyuuid_send(state, ev,
-+                                     op_ctx->kcm_data->db,
-+                                     op_ctx->client,
-+                                     uuid_in);
-+    if (subreq == NULL) {
-+        ret = ENOMEM;
-+        goto immediate;
-+    }
-+    tevent_req_set_callback(subreq, kcm_op_get_cache_by_uuid_done, req);
-+    return req;
-+
-+immediate:
-+    tevent_req_error(req, ret);
-+    tevent_req_post(req, ev);
-+    return req;
-+}
-+
-+static void kcm_op_get_cache_by_uuid_done(struct tevent_req *subreq)
-+{
-+    errno_t ret;
-+    struct kcm_ccache *cc;
-+    struct tevent_req *req = tevent_req_callback_data(subreq,
-+                                                      struct tevent_req);
-+    struct kcm_op_common_state *state = tevent_req_data(req,
-+                                                struct kcm_op_common_state);
-+    const char *name;
-+
-+    ret = kcm_ccdb_getbyuuid_recv(subreq, state, &cc);
-+    talloc_zfree(subreq);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_OP_FAILURE,
-+              "Cannot get ccahe by UUID [%d]: %s\n",
-+              ret, sss_strerror(ret));
-+        tevent_req_error(req, ret);
-+        return;
-+    }
-+
-+    if (cc == NULL) {
-+        state->op_ret = ERR_KCM_CC_END;
-+        tevent_req_done(req);
-+        return;
-+    }
-+
-+    name = kcm_cc_get_name(cc);
-+    DEBUG(SSSDBG_TRACE_INTERNAL, "Found %s by UUID\n", name);
-+
-+    ret = sss_iobuf_write_stringz(state->op_ctx->reply,
-+                                  name);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_OP_FAILURE,
-+              "Cannot write output name [%d]: %s\n",
-+              ret, sss_strerror(ret));
-+        tevent_req_error(req, ret);
-+        return;
-+    }
-+
-+    state->op_ret = EOK;
-+    tevent_req_done(req);
-+}
-+
-+/* () -> (name) */
-+struct kcm_op_get_default_ccache_state {
-+    uint32_t op_ret;
-+    struct kcm_op_ctx *op_ctx;
-+    struct tevent_context *ev;
-+
-+    const char *name;
-+};
-+
-+static void kcm_op_get_get_default_done(struct tevent_req *subreq);
-+static void kcm_op_get_default_ccache_byuuid_done(struct tevent_req *subreq);
-+static void kcm_op_get_default_ccache_list_done(struct tevent_req *subreq);
-+static errno_t
-+kcm_op_get_default_ccache_reply_step(struct kcm_op_get_default_ccache_state *state);
-+
-+static struct tevent_req *
-+kcm_op_get_default_ccache_send(TALLOC_CTX *mem_ctx,
-+                               struct tevent_context *ev,
-+                               struct kcm_op_ctx *op_ctx)
-+{
-+    struct tevent_req *req = NULL;
-+    struct tevent_req *subreq = NULL;
-+    struct kcm_op_get_default_ccache_state *state = NULL;
-+    errno_t ret;
-+
-+    req = tevent_req_create(mem_ctx, &state,
-+                            struct kcm_op_get_default_ccache_state);
-+    if (req == NULL) {
-+        return NULL;
-+    }
-+    state->op_ctx = op_ctx;
-+    state->ev = ev;
-+
-+    DEBUG(SSSDBG_TRACE_LIBS, "Getting client's default ccache\n");
-+
-+    subreq = kcm_ccdb_get_default_send(state, ev,
-+                                       state->op_ctx->kcm_data->db,
-+                                       state->op_ctx->client);
-+    if (subreq == NULL) {
-+        ret = ENOMEM;
-+        goto immediate;
-+    }
-+    tevent_req_set_callback(subreq, kcm_op_get_get_default_done, req);
-+    return req;
-+
-+immediate:
-+    tevent_req_error(req, ret);
-+    tevent_req_post(req, ev);
-+    return req;
-+}
-+
-+static void kcm_op_get_get_default_done(struct tevent_req *subreq)
-+{
-+    struct tevent_req *req = tevent_req_callback_data(subreq, struct tevent_req);
-+    struct kcm_op_get_default_ccache_state *state = tevent_req_data(req,
-+                                    struct kcm_op_get_default_ccache_state);
-+    errno_t ret;
-+    uuid_t dfl_uuid;
-+
-+    ret = kcm_ccdb_get_default_recv(subreq, &dfl_uuid);
-+    talloc_zfree(subreq);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_OP_FAILURE,
-+              "Cannot get default ccache [%d]: %s\n",
-+              ret, sss_strerror(ret));
-+        tevent_req_error(req, ret);
-+        return;
-+    }
-+
-+    if (uuid_is_null(dfl_uuid) == true) {
-+        /* No cache marked as default -- get an existing ccache for ID
-+         * and treat the default as simply the first one
-+         */
-+        subreq = kcm_ccdb_list_send(state, state->ev,
-+                                    state->op_ctx->kcm_data->db,
-+                                    state->op_ctx->client);
-+        if (subreq == NULL) {
-+            tevent_req_error(req, ENOMEM);
-+            return;
-+        }
-+        tevent_req_set_callback(subreq, kcm_op_get_default_ccache_list_done, req);
-+        return;
-+    }
-+
-+    /* Existing default */
-+    subreq = kcm_ccdb_name_by_uuid_send(state,
-+                                        state->ev,
-+                                        state->op_ctx->kcm_data->db,
-+                                        state->op_ctx->client,
-+                                        dfl_uuid);
-+    if (subreq == NULL) {
-+        tevent_req_error(req, ENOMEM);
-+        return;
-+    }
-+    tevent_req_set_callback(subreq, kcm_op_get_default_ccache_byuuid_done, req);
-+    return;
-+}
-+
-+static void kcm_op_get_default_ccache_byuuid_done(struct tevent_req *subreq)
-+{
-+    struct tevent_req *req = tevent_req_callback_data(subreq, struct tevent_req);
-+    struct kcm_op_get_default_ccache_state *state = tevent_req_data(req,
-+                                    struct kcm_op_get_default_ccache_state);
-+    errno_t ret;
-+
-+    ret = kcm_ccdb_name_by_uuid_recv(subreq, state, &state->name);
-+    talloc_zfree(subreq);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_OP_FAILURE,
-+              "Cannot get ccahe by UUID [%d]: %s\n",
-+              ret, sss_strerror(ret));
-+        tevent_req_error(req, ret);
-+        return;
-+    }
-+
-+    ret = kcm_op_get_default_ccache_reply_step(state);
-+    if (ret != EOK) {
-+        tevent_req_error(req, ret);
-+        return;
-+    }
-+
-+    tevent_req_done(req);
-+}
-+
-+static void kcm_op_get_default_ccache_list_done(struct tevent_req *subreq)
-+{
-+    struct tevent_req *req = tevent_req_callback_data(subreq, struct tevent_req);
-+    struct kcm_op_get_default_ccache_state *state = tevent_req_data(req,
-+                                    struct kcm_op_get_default_ccache_state);
-+    errno_t ret;
-+    uuid_t *uuid_list;
-+
-+    ret = kcm_ccdb_list_recv(subreq, state, &uuid_list);
-+    talloc_zfree(subreq);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_OP_FAILURE,
-+              "Cannot list ccaches [%d]: %s\n",
-+              ret, sss_strerror(ret));
-+        tevent_req_error(req, ret);
-+        return;
-+    }
-+
-+    if (uuid_list == NULL || uuid_is_null(uuid_list[0])) {
-+        /* No cache at all, just send back a reply */
-+        ret = kcm_op_get_default_ccache_reply_step(state);
-+        if (ret != EOK) {
-+            tevent_req_error(req, ret);
-+            return;
-+        }
-+
-+        tevent_req_done(req);
-+        return;
-+    }
-+
-+    /* Otherwise resolve the first cache and use it as a default */
-+    subreq = kcm_ccdb_name_by_uuid_send(state,
-+                                        state->ev,
-+                                        state->op_ctx->kcm_data->db,
-+                                        state->op_ctx->client,
-+                                        uuid_list[0]);
-+    if (subreq == NULL) {
-+        tevent_req_error(req, ENOMEM);
-+        return;
-+    }
-+    tevent_req_set_callback(subreq, kcm_op_get_default_ccache_byuuid_done, req);
-+    return;
-+}
-+
-+static errno_t
-+kcm_op_get_default_ccache_reply_step(struct kcm_op_get_default_ccache_state *state)
-+{
-+    errno_t ret;
-+
-+    if (state->name == NULL) {
-+        state->name = talloc_asprintf(state,
-+                                      "%"SPRIuid,
-+                                      cli_creds_get_uid(state->op_ctx->client));
-+        if (state->name == NULL) {
-+            return ENOMEM;
-+        }
-+    }
-+    DEBUG(SSSDBG_TRACE_INTERNAL, "The default ccache is %s\n", state->name);
-+
-+    ret = sss_iobuf_write_stringz(state->op_ctx->reply, state->name);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_OP_FAILURE,
-+              "Cannot write output name [%d]: %s\n",
-+              ret, sss_strerror(ret));
-+        return ret;
-+    }
-+
-+    return EOK;
-+}
-+
-+static errno_t kcm_op_get_default_ccache_recv(struct tevent_req *req,
-+                                              uint32_t *_op_ret)
-+{
-+    KCM_OP_RET_FROM_TYPE(req, struct kcm_op_get_default_ccache_state, _op_ret);
-+}
-+
-+/* (name) -> () */
-+static void kcm_op_set_default_ccache_getbyname_done(struct tevent_req *subreq);
-+static void kcm_op_set_default_done(struct tevent_req *subreq);
-+
-+static struct tevent_req *
-+kcm_op_set_default_ccache_send(TALLOC_CTX *mem_ctx,
-+                               struct tevent_context *ev,
-+                               struct kcm_op_ctx *op_ctx)
-+{
-+    struct tevent_req *req = NULL;
-+    struct tevent_req *subreq = NULL;
-+    struct kcm_op_common_state *state = NULL;
-+    errno_t ret;
-+    const char *name;
-+
-+    req = tevent_req_create(mem_ctx, &state, struct kcm_op_common_state);
-+    if (req == NULL) {
-+        return NULL;
-+    }
-+    state->op_ctx = op_ctx;
-+    state->ev = ev;
-+
-+    ret = sss_iobuf_read_stringz(op_ctx->input, &name);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_CRIT_FAILURE,
-+              "Cannot read input name [%d]: %s\n",
-+              ret, sss_strerror(ret));
-+        goto immediate;
-+    }
-+    DEBUG(SSSDBG_TRACE_LIBS, "Setting default ccache %s\n", name);
-+
-+    subreq = kcm_ccdb_uuid_by_name_send(state, ev,
-+                                        op_ctx->kcm_data->db,
-+                                        op_ctx->client,
-+                                        name);
-+    if (subreq == NULL) {
-+        ret = ENOMEM;
-+        goto immediate;
-+    }
-+    tevent_req_set_callback(subreq, kcm_op_set_default_ccache_getbyname_done, req);
-+    return req;
-+
-+immediate:
-+    tevent_req_error(req, ret);
-+    tevent_req_post(req, ev);
-+    return req;
-+}
-+
-+static void kcm_op_set_default_ccache_getbyname_done(struct tevent_req *subreq)
-+{
-+    errno_t ret;
-+    struct tevent_req *req = tevent_req_callback_data(subreq,
-+                                                      struct tevent_req);
-+    struct kcm_op_common_state *state = tevent_req_data(req,
-+                                                struct kcm_op_common_state);
-+    uuid_t dfl_uuid;
-+
-+    ret = kcm_ccdb_uuid_by_name_recv(subreq, state, dfl_uuid);
-+    talloc_zfree(subreq);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_OP_FAILURE,
-+              "Cannot get ccache by name [%d]: %s\n",
-+              ret, sss_strerror(ret));
-+        tevent_req_error(req, ret);
-+        return;
-+    }
-+
-+    subreq = kcm_ccdb_set_default_send(state,
-+                                       state->ev,
-+                                       state->op_ctx->kcm_data->db,
-+                                       state->op_ctx->client,
-+                                       dfl_uuid);
-+    if (subreq == NULL) {
-+        tevent_req_error(req, ENOMEM);
-+        return;
-+    }
-+    tevent_req_set_callback(subreq, kcm_op_set_default_done, req);
-+    return;
-+}
-+
-+static void kcm_op_set_default_done(struct tevent_req *subreq)
-+{
-+    errno_t ret;
-+    struct tevent_req *req = tevent_req_callback_data(subreq,
-+                                                      struct tevent_req);
-+    struct kcm_op_common_state *state = tevent_req_data(req,
-+                                                struct kcm_op_common_state);
-+
-+    ret = kcm_ccdb_set_default_recv(subreq);
-+    talloc_zfree(subreq);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_OP_FAILURE,
-+              "Cannot set default ccache %d: %s\n", ret, sss_strerror(ret));
-+        tevent_req_error(req, ret);
-+        return;
-+    }
-+
-+    state->op_ret = EOK;
-+    tevent_req_done(req);
-+}
-+
-+/* (name) -> (offset) */
-+static void kcm_op_get_kdc_offset_getbyname_done(struct tevent_req *subreq);
-+
-+static struct tevent_req *
-+kcm_op_get_kdc_offset_send(TALLOC_CTX *mem_ctx,
-+                           struct tevent_context *ev,
-+                           struct kcm_op_ctx *op_ctx)
-+{
-+    struct tevent_req *req = NULL;
-+    struct tevent_req *subreq = NULL;
-+    struct kcm_op_common_state *state = NULL;
-+    errno_t ret;
-+    const char *name;
-+
-+    req = tevent_req_create(mem_ctx, &state, struct kcm_op_common_state);
-+    if (req == NULL) {
-+        return NULL;
-+    }
-+    state->op_ctx = op_ctx;
-+
-+    ret = sss_iobuf_read_stringz(op_ctx->input, &name);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_OP_FAILURE,
-+              "Cannot read input name [%d]: %s\n",
-+              ret, sss_strerror(ret));
-+        goto immediate;
-+    }
-+    DEBUG(SSSDBG_TRACE_LIBS, "Requested offset for principal %s\n", name);
-+
-+    subreq = kcm_ccdb_getbyname_send(state, ev,
-+                                     op_ctx->kcm_data->db,
-+                                     op_ctx->client,
-+                                     name);
-+    if (subreq == NULL) {
-+        ret = ENOMEM;
-+        goto immediate;
-+    }
-+    tevent_req_set_callback(subreq, kcm_op_get_kdc_offset_getbyname_done, req);
-+    return req;
-+
-+immediate:
-+    tevent_req_error(req, ret);
-+    tevent_req_post(req, ev);
-+    return req;
-+}
-+
-+static void kcm_op_get_kdc_offset_getbyname_done(struct tevent_req *subreq)
-+{
-+    errno_t ret;
-+    struct kcm_ccache *cc;
-+    int32_t offset;
-+    int32_t offset_be;
-+    struct tevent_req *req = tevent_req_callback_data(subreq,
-+                                                      struct tevent_req);
-+    struct kcm_op_common_state *state = tevent_req_data(req,
-+                                                struct kcm_op_common_state);
-+
-+    ret = kcm_ccdb_getbyname_recv(subreq, state, &cc);
-+    talloc_zfree(subreq);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_OP_FAILURE,
-+              "Cannot get matching ccache [%d]: %s\n",
-+              ret, sss_strerror(ret));
-+        tevent_req_error(req, ret);
-+        return;
-+    }
-+
-+    if (cc == NULL) {
-+        DEBUG(SSSDBG_MINOR_FAILURE, "No matching credentials\n");
-+        state->op_ret = ERR_NO_MATCHING_CREDS;
-+        tevent_req_done(req);
-+        return;
-+    }
-+
-+    offset = kcm_cc_get_offset(cc);
-+    DEBUG(SSSDBG_TRACE_LIBS, "KDC offset: %"PRIu32"\n", offset);
-+
-+    offset_be = htobe32(offset);
-+    ret = sss_iobuf_write_int32(state->op_ctx->reply, offset_be);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_OP_FAILURE,
-+              "Cannot write KDC offset [%d]: %s\n",
-+              ret, sss_strerror(ret));
-+        tevent_req_error(req, ret);
-+        return;
-+    }
-+
-+    state->op_ret = EOK;
-+    tevent_req_done(req);
-+}
-+
-+/* (name, offset) -> () */
-+/* () -> (name) */
-+struct kcm_op_set_kdc_offset_state {
-+    uint32_t op_ret;
-+    struct kcm_op_ctx *op_ctx;
-+    struct tevent_context *ev;
-+};
-+
-+static void kcm_op_set_kdc_offset_getbyname_done(struct tevent_req *subreq);
-+static void kcm_op_set_kdc_offset_mod_done(struct tevent_req *subreq);
-+
-+static struct tevent_req *
-+kcm_op_set_kdc_offset_send(TALLOC_CTX *mem_ctx,
-+                           struct tevent_context *ev,
-+                           struct kcm_op_ctx *op_ctx)
-+{
-+    struct tevent_req *req = NULL;
-+    struct tevent_req *subreq = NULL;
-+    struct kcm_op_set_kdc_offset_state *state = NULL;
-+    errno_t ret;
-+    const char *name;
-+
-+    req = tevent_req_create(mem_ctx, &state, struct kcm_op_set_kdc_offset_state);
-+    if (req == NULL) {
-+        return NULL;
-+    }
-+    state->op_ctx = op_ctx;
-+    state->ev = ev;
-+
-+    ret = sss_iobuf_read_stringz(op_ctx->input, &name);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_OP_FAILURE,
-+              "Cannot read input name [%d]: %s\n",
-+              ret, sss_strerror(ret));
-+        goto immediate;
-+    }
-+    DEBUG(SSSDBG_TRACE_LIBS, "Setting offset for principal %s\n", name);
-+
-+    subreq = kcm_ccdb_uuid_by_name_send(state, ev,
-+                                        op_ctx->kcm_data->db,
-+                                        op_ctx->client,
-+                                        name);
-+    if (subreq == NULL) {
-+        ret = ENOMEM;
-+        goto immediate;
-+    }
-+    tevent_req_set_callback(subreq, kcm_op_set_kdc_offset_getbyname_done, req);
-+    return req;
-+
-+immediate:
-+    tevent_req_error(req, ret);
-+    tevent_req_post(req, ev);
-+    return req;
-+}
-+
-+static void kcm_op_set_kdc_offset_getbyname_done(struct tevent_req *subreq)
-+{
-+    errno_t ret;
-+    struct kcm_mod_ctx *mod_ctx;
-+    int32_t offset_be;
-+    uuid_t uuid;
-+    struct tevent_req *req = tevent_req_callback_data(subreq,
-+                                                      struct tevent_req);
-+    struct kcm_op_set_kdc_offset_state *state = tevent_req_data(req,
-+                                                struct kcm_op_set_kdc_offset_state);
-+
-+    ret = kcm_ccdb_uuid_by_name_recv(subreq, state, uuid);
-+    talloc_zfree(subreq);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_OP_FAILURE,
-+              "Cannot get matching ccache [%d]: %s\n",
-+              ret, sss_strerror(ret));
-+        tevent_req_error(req, ret);
-+        return;
-+    }
-+
-+    ret = sss_iobuf_read_int32(state->op_ctx->input, &offset_be);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_OP_FAILURE,
-+              "Cannot read KDC offset [%d]: %s\n",
-+              ret, sss_strerror(ret));
-+        tevent_req_error(req, ret);
-+        return;
-+    }
-+
-+    mod_ctx = talloc(state, struct kcm_mod_ctx);
-+    if (mod_ctx == NULL) {
-+        tevent_req_error(req, ENOMEM);
-+        return;
-+    }
-+
-+    kcm_mod_ctx_clear(mod_ctx);
-+    mod_ctx->kdc_offset = be32toh(offset_be);
-+
-+    subreq = kcm_ccdb_mod_cc_send(state,
-+                                  state->ev,
-+                                  state->op_ctx->kcm_data->db,
-+                                  state->op_ctx->client,
-+                                  uuid,
-+                                  mod_ctx);
-+    if (subreq == NULL) {
-+        tevent_req_error(req, ENOMEM);
-+        return;
-+    }
-+    tevent_req_set_callback(subreq, kcm_op_set_kdc_offset_mod_done, req);
-+}
-+
-+static void kcm_op_set_kdc_offset_mod_done(struct tevent_req *subreq)
-+{
-+    errno_t ret;
-+    struct tevent_req *req = tevent_req_callback_data(subreq,
-+                                                      struct tevent_req);
-+    struct kcm_op_set_kdc_offset_state *state = tevent_req_data(req,
-+                                                struct kcm_op_set_kdc_offset_state);
-+
-+    ret = kcm_ccdb_mod_cc_recv(subreq);
-+    talloc_zfree(subreq);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_OP_FAILURE,
-+              "Cannot modify ccache [%d]: %s\n",
-+              ret, sss_strerror(ret));
-+        tevent_req_error(req, ret);
-+        return;
-+    }
-+
-+    state->op_ret = EOK;
-+    tevent_req_done(req);
-+}
-+
-+static errno_t kcm_op_set_kdc_offset_recv(struct tevent_req *req,
-+                                          uint32_t *_op_ret)
-+{
-+    KCM_OP_RET_FROM_TYPE(req, struct kcm_op_set_kdc_offset_state, _op_ret);
-+}
-+
-+static struct kcm_op kcm_optable[] = {
-+    { "NOOP",                NULL, NULL },
-+    { "GET_NAME",            NULL, NULL },
-+    { "RESOLVE",             NULL, NULL },
-+    { "GEN_NEW",             kcm_op_gen_new_send, NULL },
-+    { "INITIALIZE",          kcm_op_initialize_send, kcm_op_initialize_recv },
-+    { "DESTROY",             kcm_op_destroy_send, NULL },
-+    { "STORE",               kcm_op_store_send, kcm_op_store_recv },
-+    { "RETRIEVE",            NULL, NULL },
-+    { "GET_PRINCIPAL",       kcm_op_get_principal_send, NULL },
-+    { "GET_CRED_UUID_LIST",  kcm_op_get_cred_uuid_list_send, NULL },
-+    { "GET_CRED_BY_UUID",    kcm_op_get_cred_by_uuid_send, NULL },
-+    { "REMOVE_CRED",         kcm_op_remove_cred_send, NULL },
-+    { "SET_FLAGS",           NULL, NULL },
-+    { "CHOWN",               NULL, NULL },
-+    { "CHMOD",               NULL, NULL },
-+    { "GET_INITIAL_TICKET",  NULL, NULL },
-+    { "GET_TICKET",          NULL, NULL },
-+    { "MOVE_CACHE",          NULL, NULL },
-+    { "GET_CACHE_UUID_LIST", kcm_op_get_cache_uuid_list_send, NULL },
-+    { "GET_CACHE_BY_UUID",   kcm_op_get_cache_by_uuid_send, NULL },
-+    { "GET_DEFAULT_CACHE",   kcm_op_get_default_ccache_send, kcm_op_get_default_ccache_recv },
-+    { "SET_DEFAULT_CACHE",   kcm_op_set_default_ccache_send, NULL },
-+    { "GET_KDC_OFFSET",      kcm_op_get_kdc_offset_send, NULL },
-+    { "SET_KDC_OFFSET",      kcm_op_set_kdc_offset_send, kcm_op_set_kdc_offset_recv },
-+    { "ADD_NTLM_CRED",       NULL, NULL },
-+    { "HAVE_NTLM_CRED",      NULL, NULL },
-+    { "DEL_NTLM_CRED",       NULL, NULL },
-+    { "DO_NTLM_AUTH",        NULL, NULL },
-+    { "GET_NTLM_USER_LIST",  NULL, NULL },
-+
-+    { NULL, NULL, NULL }
-+};
-+
-+struct kcm_op *kcm_get_opt(uint16_t opcode)
-+{
-+    struct kcm_op *op;
-+
-+    DEBUG(SSSDBG_TRACE_INTERNAL,
-+          "The client requested operation %"PRIu16"\n", opcode);
-+
-+    if (opcode >= KCM_OP_SENTINEL) {
-+        return NULL;
-+    }
-+
-+    op = &kcm_optable[opcode];
-+    if (op->fn_recv == NULL) {
-+        op->fn_recv = kcm_op_common_recv;
-+    }
-+    return op;
-+}
-+
-+const char *kcm_opt_name(struct kcm_op *op)
-+{
-+    if (op == NULL || op->name == NULL) {
-+        return "Unknown operation";
-+    }
-+
-+    return op->name;
-+}
-diff --git a/src/responder/kcm/kcmsrv_ops.h b/src/responder/kcm/kcmsrv_ops.h
-new file mode 100644
-index 0000000000000000000000000000000000000000..8e6feaf56a10b73c8b6375aea9ef26c392b5b492
---- /dev/null
-+++ b/src/responder/kcm/kcmsrv_ops.h
-@@ -0,0 +1,45 @@
-+/*
-+   SSSD
-+
-+   KCM Server - private header file
-+
-+   Copyright (C) Red Hat, 2016
-+
-+   This program is free software; you can redistribute it and/or modify
-+   it under the terms of the GNU General Public License as published by
-+   the Free Software Foundation; either version 3 of the License, or
-+   (at your option) any later version.
-+
-+   This program is distributed in the hope that it will be useful,
-+   but WITHOUT ANY WARRANTY; without even the implied warranty of
-+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+   GNU General Public License for more details.
-+
-+   You should have received a copy of the GNU General Public License
-+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
-+*/
-+
-+#ifndef __KCMSRV_OPS_H__
-+#define __KCMSRV_OPS_H__
-+
-+#include "config.h"
-+
-+#include <sys/types.h>
-+#include "util/sss_iobuf.h"
-+#include "responder/kcm/kcmsrv_pvt.h"
-+
-+struct kcm_op;
-+struct kcm_op *kcm_get_opt(uint16_t opcode);
-+const char *kcm_opt_name(struct kcm_op *op);
-+
-+struct tevent_req *kcm_cmd_send(TALLOC_CTX *mem_ctx,
-+                                struct tevent_context *ev,
-+                                struct kcm_resp_ctx *kcm_data,
-+                                struct cli_creds *client,
-+                                struct kcm_data *input,
-+                                struct kcm_op *op);
-+errno_t kcm_cmd_recv(TALLOC_CTX *mem_ctx,
-+                     struct tevent_req *req,
-+                     struct sss_iobuf **_reply);
-+
-+#endif /* __KCMSRV_OPS_H__ */
--- 
-2.9.3
-
diff --git a/SOURCES/0027-MAN-Add-a-manual-page-for-sssd-kcm.patch b/SOURCES/0027-MAN-Add-a-manual-page-for-sssd-kcm.patch
deleted file mode 100644
index 39a1629..0000000
--- a/SOURCES/0027-MAN-Add-a-manual-page-for-sssd-kcm.patch
+++ /dev/null
@@ -1,278 +0,0 @@
-From 14d42e26c2050c1941874e83761fae69585ddc27 Mon Sep 17 00:00:00 2001
-From: Jakub Hrozek <jhrozek@redhat.com>
-Date: Wed, 8 Mar 2017 17:46:09 +0100
-Subject: [PATCH 27/36] MAN: Add a manual page for sssd-kcm
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Reviewed-by: Michal Židek <mzidek@redhat.com>
-Reviewed-by: Simo Sorce <simo@redhat.com>
----
- contrib/sssd.spec.in   |   1 +
- src/man/Makefile.am    |   9 ++-
- src/man/po/po4a.cfg    |   1 +
- src/man/sssd-kcm.8.xml | 193 +++++++++++++++++++++++++++++++++++++++++++++++++
- 4 files changed, 203 insertions(+), 1 deletion(-)
- create mode 100644 src/man/sssd-kcm.8.xml
-
-diff --git a/contrib/sssd.spec.in b/contrib/sssd.spec.in
-index 52d33b4de281dc1d91a9027ac1c8c878e66fb396..1d4d020415ee28292bb4d88c78de205465d812f1 100644
---- a/contrib/sssd.spec.in
-+++ b/contrib/sssd.spec.in
-@@ -1206,6 +1206,7 @@ done
- %config(noreplace) %{_sysconfdir}/krb5.conf.d/kcm_default_ccache
- %{_unitdir}/sssd-kcm.socket
- %{_unitdir}/sssd-kcm.service
-+%{_mandir}/man8/sssd-kcm.8*
- %endif
- 
- %pre common
-diff --git a/src/man/Makefile.am b/src/man/Makefile.am
-index 142d6e2743f814294e3d92c8342070b8230bb3e5..3a063614f085691652db32d76315375466e0d3de 100644
---- a/src/man/Makefile.am
-+++ b/src/man/Makefile.am
-@@ -27,6 +27,9 @@ endif
- if BUILD_SECRETS
- SEC_CONDS = ;with_secrets
- endif
-+if BUILD_SECRETS
-+KCM_CONDS = ;with_kcm
-+endif
- if GPO_DEFAULT_ENFORCING
- GPO_CONDS = ;gpo_default_enforcing
- else
-@@ -40,7 +43,7 @@ FILES_CONDS = ;enable_files_domain
- else
- FILES_CONDS = ;no_enable_files_domain
- endif
--CONDS = with_false$(SUDO_CONDS)$(AUTOFS_CONDS)$(SSH_CONDS)$(PAC_RESPONDER_CONDS)$(IFP_CONDS)$(GPO_CONDS)$(SEC_CONDS)$(SYSTEMD_CONDS)$(FILES_CONDS)
-+CONDS = with_false$(SUDO_CONDS)$(AUTOFS_CONDS)$(SSH_CONDS)$(PAC_RESPONDER_CONDS)$(IFP_CONDS)$(GPO_CONDS)$(SEC_CONDS)$(SYSTEMD_CONDS)$(FILES_CONDS)$(KCM_CONDS)
- 
- 
- #Special Rules:
-@@ -85,6 +88,10 @@ if BUILD_SECRETS
- man_MANS += sssd-secrets.5
- endif
- 
-+if BUILD_KCM
-+man_MANS += sssd-kcm.8
-+endif
-+
- if BUILD_NFS_IDMAP
- man_MANS += sss_rpcidmapd.5
- endif
-diff --git a/src/man/po/po4a.cfg b/src/man/po/po4a.cfg
-index d1f6ac39f841c61ae3d2393fb3402dc21b9cbd69..a02f97e777fa76615e4d5cbcfc788956706d8cd0 100644
---- a/src/man/po/po4a.cfg
-+++ b/src/man/po/po4a.cfg
-@@ -31,6 +31,7 @@
- [type:docbook] sssctl.8.xml $lang:$(builddir)/$lang/sssctl.8.xml
- [type:docbook] sssd-files.5.xml $lang:$(builddir)/$lang/sssd-files.5.xml
- [type:docbook] sssd-secrets.5.xml $lang:$(builddir)/$lang/sssd-secrets.5.xml
-+[type:docbook] sssd-kcm.8.xml $lang:$(builddir)/$lang/sssd-kcm.8.xml
- [type:docbook] include/service_discovery.xml $lang:$(builddir)/$lang/include/service_discovery.xml opt:"-k 0"
- [type:docbook] include/upstream.xml $lang:$(builddir)/$lang/include/upstream.xml opt:"-k 0"
- [type:docbook] include/failover.xml $lang:$(builddir)/$lang/include/failover.xml opt:"-k 0"
-diff --git a/src/man/sssd-kcm.8.xml b/src/man/sssd-kcm.8.xml
-new file mode 100644
-index 0000000000000000000000000000000000000000..5dc93838e48723bdb470c0a9c8575bd17c7593e8
---- /dev/null
-+++ b/src/man/sssd-kcm.8.xml
-@@ -0,0 +1,193 @@
-+<?xml version="1.0" encoding="UTF-8"?>
-+<!DOCTYPE reference PUBLIC "-//OASIS//DTD DocBook V4.4//EN"
-+"http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd">
-+<reference>
-+<title>SSSD Manual pages</title>
-+<refentry>
-+    <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="include/upstream.xml" />
-+
-+    <refmeta>
-+        <refentrytitle>sssd-kcm</refentrytitle>
-+        <manvolnum>8</manvolnum>
-+        <refmiscinfo class="manual">File Formats and Conventions</refmiscinfo>
-+    </refmeta>
-+
-+    <refnamediv id='name'>
-+        <refname>sssd-kcm</refname>
-+        <refpurpose>SSSD Kerberos Cache Manager</refpurpose>
-+    </refnamediv>
-+
-+    <refsect1 id='description'>
-+        <title>DESCRIPTION</title>
-+        <para>
-+            This manual page describes the configuration of the SSSD Kerberos
-+            Cache Manager (KCM). KCM is a process that stores, tracks and
-+            manages Kerberos credential caches. It originates in the Heimdal
-+            Kerberos project, although the MIT Kerberos library also provides
-+            client side (more details on that below) support for the KCM
-+            credential cache.
-+        </para>
-+        <para>
-+            In a setup where Kerberos caches are managed by KCM, the
-+            Kerberos library (typically used through an application, like
-+            e.g.,
-+            <citerefentry>
-+                <refentrytitle>kinit</refentrytitle><manvolnum>1</manvolnum>
-+            </citerefentry>,
-+            is a <quote>"KCM client"</quote> and the KCM daemon
-+            is being referred to as a <quote>"KCM server"</quote>. The client
-+            and server communicate over a UNIX socket.
-+        </para>
-+        <para>
-+            The KCM server keeps track of each credential caches's owner and
-+            performs access check control based on the UID and GID of the
-+            KCM client. The root user has access to all credential caches.
-+        </para>
-+        <para>
-+            The KCM credential cache has several interesting properties:
-+            <itemizedlist>
-+                <listitem>
-+                    <para>
-+                        since the process runs in userspace, it is subject to UID namespacing, ulike the kernel keyring
-+                    </para>
-+                </listitem>
-+                <listitem>
-+                    <para>
-+                        unlike the kernel keyring-based cache, which is shared between all containers, the KCM server is a separate process whose entry point is a UNIX socket
-+                    </para>
-+                </listitem>
-+                <listitem>
-+                    <para>
-+                        the SSSD implementation stores the ccaches in the SSSD
-+                        <citerefentry>
-+                            <refentrytitle>sssd-secrets</refentrytitle><manvolnum>5</manvolnum>
-+                        </citerefentry>
-+                        secrets store, allowing the ccaches to survive KCM server restarts or machine reboots.
-+                    </para>
-+                </listitem>
-+            </itemizedlist>
-+            This allows the system to use a collection-aware credential
-+            cache, yet share the credential cache between some or no
-+            containers by bind-mounting the socket.
-+        </para>
-+    </refsect1>
-+
-+    <refsect1 id='usage'>
-+        <title>USING THE KCM CREDENTIAL CACHE</title>
-+        <para>
-+            In order to use KCM credential cache, it must be selected as the default
-+            credential type in
-+            <citerefentry>
-+                <refentrytitle>krb5.conf</refentrytitle><manvolnum>5</manvolnum>
-+            </citerefentry>,
-+            The credentials cache name must be only <quote>KCM:</quote>
-+            without any template expansions.  For example:
-+            <programlisting>
-+[libdefaults]
-+    default_ccache_name = KCM:
-+            </programlisting>
-+        </para>
-+        <para>
-+            Next, make sure the Kerberos client libraries and the KCM server must agree
-+            on the UNIX socket path. By default, both use the same path
-+            <replaceable>/var/run/.heim_org.h5l.kcm-socket</replaceable>. To configure
-+            the Kerberos library, change its <quote>kcm_socket</quote> option which
-+            is described in the
-+            <citerefentry>
-+                <refentrytitle>krb5.conf</refentrytitle><manvolnum>5</manvolnum>
-+            </citerefentry>
-+            manual page.
-+        </para>
-+        <para>
-+            Finally, make sure the SSSD KCM server can be contacted.
-+            The KCM service is typically socket-activated by
-+            <citerefentry>
-+                <refentrytitle>systemd</refentrytitle>
-+                <manvolnum>1</manvolnum>
-+            </citerefentry>.
-+            Unlike
-+            other SSSD services, it cannot be started by adding the
-+            <quote>kcm</quote> string to the <quote>service</quote>
-+            directive.
-+            <programlisting>
-+systemctl start sssd-kcm.socket
-+systemctl enable sssd-kcm.socket
-+systemctl enable sssd-kcm.service
-+            </programlisting>
-+            Please note your distribution may already configure the units
-+            for you.
-+        </para>
-+    </refsect1>
-+
-+    <refsect1 id='storage'>
-+        <title>THE CREDENTIAL CACHE STORAGE</title>
-+        <para>
-+            The credential caches are stored in the SSSD secrets service (see
-+            <citerefentry>
-+                <refentrytitle>sssd-secrets</refentrytitle><manvolnum>5</manvolnum>
-+            </citerefentry>
-+            for more details). Therefore it is important that also the sssd-secrets
-+            service is enabled and its socket is started:
-+            <programlisting>
-+systemctl start sssd-secrets.socket
-+systemctl enable sssd-secrets.socket
-+systemctl enable sssd-secrets.service
-+            </programlisting>
-+            Your distribution should already set the dependencies between the services.
-+        </para>
-+    </refsect1>
-+
-+    <refsect1 id='options'>
-+        <title>CONFIGURATION OPTIONS</title>
-+        <para>
-+            The KCM service is configured in the <quote>kcm</quote>
-+            section of the sssd.conf file. Please note that currently,
-+            is it not sufficient to restart the sssd-kcm service, because
-+            the sssd configuration is only parsed and read to an internal
-+            configuration database by the sssd service. Therefore you
-+            must restart the sssd service if you change anything in the
-+            <quote>kcm</quote> section of sssd.conf.
-+            For a detailed syntax reference, refer to the <quote>FILE FORMAT</quote> section of the
-+            <citerefentry>
-+                <refentrytitle>sssd.conf</refentrytitle>
-+                <manvolnum>5</manvolnum>
-+            </citerefentry> manual page.
-+        </para>
-+        <para>
-+            The generic SSSD service options such as
-+            <quote>debug_level</quote> or <quote>fd_limit</quote> are
-+            accepted by the kcm service.  Please refer to the
-+            <citerefentry>
-+                <refentrytitle>sssd.conf</refentrytitle>
-+                <manvolnum>5</manvolnum>
-+            </citerefentry> manual page for a complete list. In addition,
-+            there are some KCM-specific options as well.
-+        </para>
-+        <variablelist>
-+            <varlistentry>
-+                <term>socket_path (string)</term>
-+                <listitem>
-+                    <para>
-+                        The socket the KCM service will listen on.
-+                    </para>
-+                    <para>
-+                        Default: <replaceable>/var/run/.heim_org.h5l.kcm-socket</replaceable>
-+                    </para>
-+                </listitem>
-+            </varlistentry>
-+        </variablelist>
-+    </refsect1>
-+
-+    <refsect1 id='see_also'>
-+        <title>SEE ALSO</title>
-+        <para>
-+            <citerefentry>
-+                <refentrytitle>sssd</refentrytitle><manvolnum>8</manvolnum>
-+            </citerefentry>,
-+            <citerefentry>
-+                <refentrytitle>sssd.conf</refentrytitle><manvolnum>5</manvolnum>
-+            </citerefentry>,
-+        </para>
-+    </refsect1>
-+</refentry>
-+</reference>
--- 
-2.9.3
-
diff --git a/SOURCES/0027-nss-make-memcache_delete_entry-public.patch b/SOURCES/0027-nss-make-memcache_delete_entry-public.patch
new file mode 100644
index 0000000..6d0c026
--- /dev/null
+++ b/SOURCES/0027-nss-make-memcache_delete_entry-public.patch
@@ -0,0 +1,49 @@
+From fd798bd98d932bc847afc60817f5fdb744eee2a4 Mon Sep 17 00:00:00 2001
+From: Sumit Bose <sbose@redhat.com>
+Date: Tue, 24 Oct 2017 12:50:43 +0200
+Subject: [PATCH 27/31] nss: make memcache_delete_entry() public
+
+Related to https://pagure.io/SSSD/sssd/issue/2478
+
+Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
+(cherry picked from commit a7d6ca275d6b2e5d396cbefb18d0ee880011e271)
+---
+ src/responder/nss/nss_get_object.c | 2 +-
+ src/responder/nss/nss_private.h    | 8 ++++++++
+ 2 files changed, 9 insertions(+), 1 deletion(-)
+
+diff --git a/src/responder/nss/nss_get_object.c b/src/responder/nss/nss_get_object.c
+index e56480af5e3369963d2e8bb17d74d1603af8e014..15faced006f754134415e766284377f0c86af0ac 100644
+--- a/src/responder/nss/nss_get_object.c
++++ b/src/responder/nss/nss_get_object.c
+@@ -86,7 +86,7 @@ memcache_delete_entry_by_id(struct nss_ctx *nss_ctx,
+     return ret;
+ }
+ 
+-static errno_t
++errno_t
+ memcache_delete_entry(struct nss_ctx *nss_ctx,
+                       struct resp_ctx *rctx,
+                       struct sss_domain_info *domain,
+diff --git a/src/responder/nss/nss_private.h b/src/responder/nss/nss_private.h
+index a0b573d6ecba2d8ba6f55db0adcd7ee29cbec991..5fc19d26be9adda4d967086e7b239e49a78866ee 100644
+--- a/src/responder/nss/nss_private.h
++++ b/src/responder/nss/nss_private.h
+@@ -92,6 +92,14 @@ struct sss_cmd_table *get_nss_cmds(void);
+ 
+ int nss_connection_setup(struct cli_ctx *cli_ctx);
+ 
++errno_t
++memcache_delete_entry(struct nss_ctx *nss_ctx,
++                      struct resp_ctx *rctx,
++                      struct sss_domain_info *domain,
++                      const char *name,
++                      uint32_t id,
++                      enum sss_mc_type type);
++
+ struct tevent_req *
+ nss_get_object_send(TALLOC_CTX *mem_ctx,
+                     struct tevent_context *ev,
+-- 
+2.13.6
+
diff --git a/SOURCES/0028-NSS-add-support-for-SSS_NSS_EX_FLAG_INVALIDATE_CACHE.patch b/SOURCES/0028-NSS-add-support-for-SSS_NSS_EX_FLAG_INVALIDATE_CACHE.patch
new file mode 100644
index 0000000..04cc5a0
--- /dev/null
+++ b/SOURCES/0028-NSS-add-support-for-SSS_NSS_EX_FLAG_INVALIDATE_CACHE.patch
@@ -0,0 +1,365 @@
+From fe54de0824cac1822d6f9485165adc64bf4e0fa7 Mon Sep 17 00:00:00 2001
+From: Sumit Bose <sbose@redhat.com>
+Date: Tue, 24 Oct 2017 14:10:53 +0200
+Subject: [PATCH 28/31] NSS: add support for SSS_NSS_EX_FLAG_INVALIDATE_CACHE
+
+The patch adds support for the SSS_NSS_EX_FLAG_INVALIDATE_CACHE flag and
+makes the existing code more flexible and handle additional flags.
+
+If SSS_NSS_EX_FLAG_INVALIDATE_CACHE is set the requested object is only
+looked up in the cache and if it was found on-disk and memory cache
+entries will be invalidated.
+
+Related to https://pagure.io/SSSD/sssd/issue/2478
+
+Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
+(cherry picked from commit 55f7d8034d783c01789d76a2b9ffc901045e8af8)
+---
+ src/responder/nss/nss_cmd.c            | 141 +++++++++++++++++++++++++++++++--
+ src/responder/nss/nss_protocol.c       |   1 +
+ src/responder/nss/nss_protocol.h       |   1 +
+ src/responder/nss/nss_protocol_grent.c |   9 ++-
+ src/responder/nss/nss_protocol_pwent.c |   6 +-
+ src/sss_client/idmap/sss_nss_ex.c      |  20 ++++-
+ src/sss_client/idmap/sss_nss_idmap.h   |   8 +-
+ 7 files changed, 171 insertions(+), 15 deletions(-)
+
+diff --git a/src/responder/nss/nss_cmd.c b/src/responder/nss/nss_cmd.c
+index c5ddd2f2cc2122cd169ea991b94a14eb5bad095f..545257a0be7e91e9de767a57848bb77c5791db4e 100644
+--- a/src/responder/nss/nss_cmd.c
++++ b/src/responder/nss/nss_cmd.c
+@@ -50,6 +50,26 @@ nss_cmd_ctx_create(TALLOC_CTX *mem_ctx,
+     return cmd_ctx;
+ }
+ 
++static errno_t eval_flags(struct nss_cmd_ctx *cmd_ctx,
++                          struct cache_req_data *data)
++{
++    if ((cmd_ctx->flags & SSS_NSS_EX_FLAG_NO_CACHE) != 0
++            && (cmd_ctx->flags & SSS_NSS_EX_FLAG_INVALIDATE_CACHE) != 0) {
++        DEBUG(SSSDBG_CRIT_FAILURE, "Flags SSS_NSS_EX_FLAG_NO_CACHE and "
++                                   "SSS_NSS_EX_FLAG_INVALIDATE_CACHE are "
++                                   "mutually exclusive.\n");
++        return EINVAL;
++    }
++
++    if ((cmd_ctx->flags & SSS_NSS_EX_FLAG_NO_CACHE) != 0) {
++        cache_req_data_set_bypass_cache(data, true);
++    } else if ((cmd_ctx->flags & SSS_NSS_EX_FLAG_INVALIDATE_CACHE) != 0) {
++        cache_req_data_set_bypass_dp(data, true);
++    }
++
++    return EOK;
++}
++
+ static void nss_getby_done(struct tevent_req *subreq);
+ static void nss_getlistby_done(struct tevent_req *subreq);
+ 
+@@ -65,7 +85,6 @@ static errno_t nss_getby_name(struct cli_ctx *cli_ctx,
+     struct tevent_req *subreq;
+     const char *rawname;
+     errno_t ret;
+-    uint32_t flags = 0;
+ 
+     cmd_ctx = nss_cmd_ctx_create(cli_ctx, cli_ctx, type, fill_fn);
+     if (cmd_ctx == NULL) {
+@@ -73,8 +92,9 @@ static errno_t nss_getby_name(struct cli_ctx *cli_ctx,
+         goto done;
+     }
+ 
++    cmd_ctx->flags = 0;
+     if (ex_version) {
+-        ret = nss_protocol_parse_name_ex(cli_ctx, &rawname, &flags);
++        ret = nss_protocol_parse_name_ex(cli_ctx, &rawname, &cmd_ctx->flags);
+     } else {
+         ret = nss_protocol_parse_name(cli_ctx, &rawname);
+     }
+@@ -92,8 +112,10 @@ static errno_t nss_getby_name(struct cli_ctx *cli_ctx,
+         goto done;
+     }
+ 
+-    if ((flags & SSS_NSS_EX_FLAG_NO_CACHE) != 0) {
+-        cache_req_data_set_bypass_cache(data, true);
++    ret = eval_flags(cmd_ctx, data);
++    if (ret != EOK) {
++        DEBUG(SSSDBG_OP_FAILURE, "eval_flags failed.\n");
++        goto done;
+     }
+ 
+     subreq = nss_get_object_send(cmd_ctx, cli_ctx->ev, cli_ctx,
+@@ -129,7 +151,6 @@ static errno_t nss_getby_id(struct cli_ctx *cli_ctx,
+     struct tevent_req *subreq;
+     uint32_t id;
+     errno_t ret;
+-    uint32_t flags = 0;
+ 
+     cmd_ctx = nss_cmd_ctx_create(cli_ctx, cli_ctx, type, fill_fn);
+     if (cmd_ctx == NULL) {
+@@ -138,7 +159,7 @@ static errno_t nss_getby_id(struct cli_ctx *cli_ctx,
+     }
+ 
+     if (ex_version) {
+-        ret = nss_protocol_parse_id_ex(cli_ctx, &id, &flags);
++        ret = nss_protocol_parse_id_ex(cli_ctx, &id, &cmd_ctx->flags);
+     } else {
+         ret = nss_protocol_parse_id(cli_ctx, &id);
+     }
+@@ -156,8 +177,10 @@ static errno_t nss_getby_id(struct cli_ctx *cli_ctx,
+         goto done;
+     }
+ 
+-    if ((flags & SSS_NSS_EX_FLAG_NO_CACHE) != 0) {
+-        cache_req_data_set_bypass_cache(data, true);
++    ret = eval_flags(cmd_ctx, data);
++    if (ret != EOK) {
++        DEBUG(SSSDBG_OP_FAILURE, "eval_flags failed.\n");
++        goto done;
+     }
+ 
+     subreq = nss_get_object_send(cmd_ctx, cli_ctx->ev, cli_ctx,
+@@ -425,6 +448,98 @@ done:
+     return EOK;
+ }
+ 
++static errno_t invalidate_cache(struct nss_cmd_ctx *cmd_ctx,
++                                struct cache_req_result *result)
++{
++    int ret;
++    enum sss_mc_type memcache_type;
++    const char *name;
++    char *output_name = NULL;
++    bool is_user;
++    struct sysdb_attrs *attrs = NULL;
++
++    switch (cmd_ctx->type) {
++    case CACHE_REQ_INITGROUPS:
++    case CACHE_REQ_INITGROUPS_BY_UPN:
++        memcache_type = SSS_MC_INITGROUPS;
++        is_user = true;
++        break;
++    case CACHE_REQ_USER_BY_NAME:
++    case CACHE_REQ_USER_BY_ID:
++        memcache_type = SSS_MC_PASSWD;
++        is_user = true;
++        break;
++    case CACHE_REQ_GROUP_BY_NAME:
++    case CACHE_REQ_GROUP_BY_ID:
++        memcache_type = SSS_MC_GROUP;
++        is_user = false;
++        break;
++    default:
++        /* nothing to do */
++        return EOK;
++    }
++
++    /* Find output name to invalidate memory cache entry*/
++    name = sss_get_name_from_msg(result->domain, result->msgs[0]);
++    if (name == NULL) {
++        DEBUG(SSSDBG_CRIT_FAILURE, "Found object has no name.\n");
++        return EINVAL;
++    }
++    ret = sss_output_fqname(cmd_ctx, result->domain, name,
++                            cmd_ctx->nss_ctx->rctx->override_space,
++                            &output_name);
++    if (ret != EOK) {
++        DEBUG(SSSDBG_OP_FAILURE, "sss_output_fqname failed.\n");
++        return ret;
++    }
++
++    memcache_delete_entry(cmd_ctx->nss_ctx, cmd_ctx->nss_ctx->rctx, NULL,
++                          output_name, 0, memcache_type);
++    if (memcache_type == SSS_MC_INITGROUPS) {
++        /* Invalidate the passwd data as well */
++        memcache_delete_entry(cmd_ctx->nss_ctx, cmd_ctx->nss_ctx->rctx,
++                              result->domain, output_name, 0, SSS_MC_PASSWD);
++    }
++    talloc_free(output_name);
++
++    /* Use sysdb name to invalidate disk cache entry */
++    name = ldb_msg_find_attr_as_string(result->msgs[0], SYSDB_NAME, NULL);
++    if (name == NULL) {
++        DEBUG(SSSDBG_CRIT_FAILURE, "Found object has no name.\n");
++        return EINVAL;
++    }
++
++    if (memcache_type == SSS_MC_INITGROUPS) {
++        attrs = sysdb_new_attrs(cmd_ctx);
++        if (attrs == NULL) {
++            DEBUG(SSSDBG_OP_FAILURE, "sysdb_new_attrs failed.\n");
++            return ENOMEM;
++        }
++
++        ret = sysdb_attrs_add_time_t(attrs, SYSDB_INITGR_EXPIRE, 1);
++        if (ret != EOK) {
++            talloc_free(attrs);
++            DEBUG(SSSDBG_OP_FAILURE, "sysdb_attrs_add_time_t failed.\n");
++            return ret;
++        }
++
++        ret = sysdb_set_user_attr(result->domain, name, attrs, SYSDB_MOD_REP);
++        talloc_free(attrs);
++        if (ret != EOK) {
++            DEBUG(SSSDBG_OP_FAILURE, "sysdb_set_user_attr failed.\n");
++            return ret;
++        }
++    }
++
++    ret = sysdb_invalidate_cache_entry(result->domain, name, is_user);
++    if (ret != EOK) {
++        DEBUG(SSSDBG_OP_FAILURE, "sysdb_invalidate_cache_entry failed.\n");
++        return ret;
++    }
++
++    return EOK;
++}
++
+ static void nss_getby_done(struct tevent_req *subreq)
+ {
+     struct cache_req_result *result;
+@@ -440,6 +555,16 @@ static void nss_getby_done(struct tevent_req *subreq)
+         goto done;
+     }
+ 
++    if ((cmd_ctx->flags & SSS_NSS_EX_FLAG_INVALIDATE_CACHE) != 0) {
++        ret = invalidate_cache(cmd_ctx, result);
++        if (ret != EOK) {
++            DEBUG(SSSDBG_OP_FAILURE, "Failed to invalidate cache for [%s].\n",
++                                     cmd_ctx->rawname);
++            nss_protocol_done(cmd_ctx->cli_ctx, ret);
++            goto done;
++        }
++    }
++
+     nss_protocol_reply(cmd_ctx->cli_ctx, cmd_ctx->nss_ctx, cmd_ctx,
+                        result, cmd_ctx->fill_fn);
+ 
+diff --git a/src/responder/nss/nss_protocol.c b/src/responder/nss/nss_protocol.c
+index 17bfc4f4e71960a72e9e04622eac95b94a865ec7..2655386498754c46fbb363bdd1f976f9ded6a434 100644
+--- a/src/responder/nss/nss_protocol.c
++++ b/src/responder/nss/nss_protocol.c
+@@ -233,6 +233,7 @@ nss_protocol_parse_id_ex(struct cli_ctx *cli_ctx, uint32_t *_id,
+     SAFEALIGN_COPY_UINT32(&flags, body + sizeof(uint32_t), NULL);
+ 
+     *_id = id;
++    *_flags = flags;
+ 
+     return EOK;
+ }
+diff --git a/src/responder/nss/nss_protocol.h b/src/responder/nss/nss_protocol.h
+index ca5b040237dc18acdca9a7a3a7a7dbb64265aa95..76724d2b2db7b11c9147fa927e39abab731328b2 100644
+--- a/src/responder/nss/nss_protocol.h
++++ b/src/responder/nss/nss_protocol.h
+@@ -50,6 +50,7 @@ struct nss_cmd_ctx {
+     struct nss_ctx *nss_ctx;
+     struct nss_state_ctx *state_ctx;
+     nss_protocol_fill_packet_fn fill_fn;
++    uint32_t flags;
+ 
+     /* For initgroups- */
+     const char *rawname;
+diff --git a/src/responder/nss/nss_protocol_grent.c b/src/responder/nss/nss_protocol_grent.c
+index ee228c722a153a1ba7aa8a1b30a1e551108424bb..6f6ae57dd97b000ad3cf174b0f649d46981563e2 100644
+--- a/src/responder/nss/nss_protocol_grent.c
++++ b/src/responder/nss/nss_protocol_grent.c
+@@ -274,8 +274,10 @@ nss_protocol_fill_grent(struct nss_ctx *nss_ctx,
+ 
+         num_results++;
+ 
+-        /* Do not store entry in memory cache during enumeration. */
+-        if (!cmd_ctx->enumeration) {
++        /* Do not store entry in memory cache during enumeration or when
++         * requested. */
++        if (!cmd_ctx->enumeration
++                && (cmd_ctx->flags & SSS_NSS_EX_FLAG_INVALIDATE_CACHE) == 0) {
+             members = (char *)&body[rp_members];
+             members_size = body_len - rp_members;
+             ret = sss_mmap_cache_gr_store(&nss_ctx->grp_mc_ctx, name, &pwfield,
+@@ -390,7 +392,8 @@ nss_protocol_fill_initgr(struct nss_ctx *nss_ctx,
+         num_results++;
+     }
+ 
+-    if (nss_ctx->initgr_mc_ctx) {
++    if (nss_ctx->initgr_mc_ctx
++                && (cmd_ctx->flags & SSS_NSS_EX_FLAG_INVALIDATE_CACHE) == 0) {
+         to_sized_string(&rawname, cmd_ctx->rawname);
+         to_sized_string(&unique_name, result->lookup_name);
+ 
+diff --git a/src/responder/nss/nss_protocol_pwent.c b/src/responder/nss/nss_protocol_pwent.c
+index db5c071e2ff172a2267c08c9817fecfbcc7cabc3..f449ec69b6a86a6db2aaed368e217c1a791faaa2 100644
+--- a/src/responder/nss/nss_protocol_pwent.c
++++ b/src/responder/nss/nss_protocol_pwent.c
+@@ -295,8 +295,10 @@ nss_protocol_fill_pwent(struct nss_ctx *nss_ctx,
+ 
+         num_results++;
+ 
+-        /* Do not store entry in memory cache during enumeration. */
+-        if (!cmd_ctx->enumeration) {
++        /* Do not store entry in memory cache during enumeration or when
++         * requested. */
++        if (!cmd_ctx->enumeration
++                && (cmd_ctx->flags & SSS_NSS_EX_FLAG_INVALIDATE_CACHE) == 0) {
+             ret = sss_mmap_cache_pw_store(&nss_ctx->pwd_mc_ctx, name, &pwfield,
+                                           uid, gid, &gecos, &homedir, &shell);
+             if (ret != EOK) {
+diff --git a/src/sss_client/idmap/sss_nss_ex.c b/src/sss_client/idmap/sss_nss_ex.c
+index edb3ea652ef7032b76c8f815b9f83fe185a669ea..148eb7b35ec236b6272dd203a0035399cfdef73d 100644
+--- a/src/sss_client/idmap/sss_nss_ex.c
++++ b/src/sss_client/idmap/sss_nss_ex.c
+@@ -103,6 +103,18 @@ errno_t sss_nss_mc_get(struct nss_input *inp)
+     }
+ }
+ 
++static int check_flags(uint32_t flags)
++{
++    /* SSS_NSS_EX_FLAG_NO_CACHE and SSS_NSS_EX_FLAG_INVALIDATE_CACHE are
++     * mutually exclusive */
++    if ((flags & SSS_NSS_EX_FLAG_NO_CACHE) != 0
++            && (flags & SSS_NSS_EX_FLAG_INVALIDATE_CACHE) != 0) {
++        return EINVAL;
++    }
++
++    return 0;
++}
++
+ int sss_get_ex(struct nss_input *inp, uint32_t flags, unsigned int timeout)
+ {
+     uint8_t *repbuf = NULL;
+@@ -117,7 +129,13 @@ int sss_get_ex(struct nss_input *inp, uint32_t flags, unsigned int timeout)
+     size_t idx;
+     bool skip_mc = false;
+ 
+-    if ((flags & SSS_NSS_EX_FLAG_NO_CACHE) != 0) {
++    ret = check_flags(flags);
++    if (ret != 0) {
++        return ret;
++    }
++
++    if ((flags & SSS_NSS_EX_FLAG_NO_CACHE) != 0
++            || (flags & SSS_NSS_EX_FLAG_INVALIDATE_CACHE) != 0) {
+         skip_mc = true;
+     }
+ 
+diff --git a/src/sss_client/idmap/sss_nss_idmap.h b/src/sss_client/idmap/sss_nss_idmap.h
+index 1649830afbb80c617fd339f054aef8bc8e585fb9..3755643312f05a31d1cf1aa76dfc22848ef1e3ec 100644
+--- a/src/sss_client/idmap/sss_nss_idmap.h
++++ b/src/sss_client/idmap/sss_nss_idmap.h
+@@ -170,9 +170,15 @@ void sss_nss_free_kv(struct sss_nss_kv *kv_list);
+ #define SSS_NSS_EX_FLAG_NO_FLAGS 0
+ 
+ /** Always request data from the server side, client must be privileged to do
+- *  so, see nss_trusted_users option in man sssd.conf for details */
++ *  so, see nss_trusted_users option in man sssd.conf for details.
++ *  This flag cannot be used together with SSS_NSS_EX_FLAG_INVALIDATE_CACHE */
+ #define SSS_NSS_EX_FLAG_NO_CACHE (1 << 0)
+ 
++/** Invalidate the data in the caches, client must be privileged to do
++ *  so, see nss_trusted_users option in man sssd.conf for details.
++ *  This flag cannot be used together with SSS_NSS_EX_FLAG_NO_CACHE */
++#define SSS_NSS_EX_FLAG_INVALIDATE_CACHE (1 << 1)
++
+ #ifdef IPA_389DS_PLUGIN_HELPER_CALLS
+ 
+ /**
+-- 
+2.13.6
+
diff --git a/SOURCES/0028-TESTS-Add-integration-tests-for-the-KCM-responder.patch b/SOURCES/0028-TESTS-Add-integration-tests-for-the-KCM-responder.patch
deleted file mode 100644
index 776bf94..0000000
--- a/SOURCES/0028-TESTS-Add-integration-tests-for-the-KCM-responder.patch
+++ /dev/null
@@ -1,799 +0,0 @@
-From ad820beebae89c886f1ba4f0d2ddac4ca36857b7 Mon Sep 17 00:00:00 2001
-From: Jakub Hrozek <jhrozek@redhat.com>
-Date: Tue, 13 Dec 2016 17:17:16 +0100
-Subject: [PATCH 28/36] TESTS: Add integration tests for the KCM responder
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Reviewed-by: Michal Židek <mzidek@redhat.com>
-Reviewed-by: Simo Sorce <simo@redhat.com>
-Reviewed-by: Lukáš Slebodník <lslebodn@redhat.com>
----
- contrib/ci/configure.sh     |   7 +
- contrib/ci/deps.sh          |   6 +
- src/tests/intg/Makefile.am  |   4 +
- src/tests/intg/kdc.py       | 175 +++++++++++++++++++++
- src/tests/intg/krb5utils.py | 156 +++++++++++++++++++
- src/tests/intg/test_kcm.py  | 361 ++++++++++++++++++++++++++++++++++++++++++++
- 6 files changed, 709 insertions(+)
- create mode 100644 src/tests/intg/kdc.py
- create mode 100644 src/tests/intg/krb5utils.py
- create mode 100644 src/tests/intg/test_kcm.py
-
-diff --git a/contrib/ci/configure.sh b/contrib/ci/configure.sh
-index 8e779cfe634a7555e0e8e3b52f42c07e62980fbc..7590743c2aa5fe31bcdf1a3e92a3f482dbec699b 100644
---- a/contrib/ci/configure.sh
-+++ b/contrib/ci/configure.sh
-@@ -38,6 +38,13 @@ if [[ "$DISTRO_BRANCH" == -redhat-redhatenterprise*-6.*- ||
-         "--disable-cifs-idmap-plugin"
-         "--with-syslog=syslog"
-         "--without-python3-bindings"
-+        "--without-kcm"
-+    )
-+fi
-+
-+if [[ "$DISTRO_BRANCH" == -redhat-fedora-2[0-2]* ]]; then
-+    CONFIGURE_ARG_LIST+=(
-+        "--without-kcm"
-     )
- fi
- 
-diff --git a/contrib/ci/deps.sh b/contrib/ci/deps.sh
-index c525e62e8c1d5b9fa042dee4ad03790dbceba242..4467e117c3a896a7f01ef7cb9e94fe28c2ea2838 100644
---- a/contrib/ci/deps.sh
-+++ b/contrib/ci/deps.sh
-@@ -47,6 +47,8 @@ if [[ "$DISTRO_BRANCH" == -redhat-* ]]; then
-         uid_wrapper
-         python-requests
-         curl-devel
-+        krb5-server
-+        krb5-workstation
-     )
-     _DEPS_LIST_SPEC=`
-         sed -e 's/@PACKAGE_VERSION@/0/g' \
-@@ -122,6 +124,10 @@ if [[ "$DISTRO_BRANCH" == -debian-* ]]; then
-         libhttp-parser-dev
-         libjansson-dev
-         libcurl4-openssl-dev
-+        krb5-kdc
-+        krb5-admin-server
-+        krb5-user
-+        uuid-dev
-     )
-     DEPS_INTGCHECK_SATISFIED=true
- fi
-diff --git a/src/tests/intg/Makefile.am b/src/tests/intg/Makefile.am
-index 1d36fa0d2d50307fbc871f5b2a6f0cb1cc95db81..8526beace09b15c99aa27ac98d5038d1980f6a71 100644
---- a/src/tests/intg/Makefile.am
-+++ b/src/tests/intg/Makefile.am
-@@ -26,6 +26,9 @@ dist_noinst_DATA = \
-     files_ops.py \
-     test_files_ops.py \
-     test_files_provider.py \
-+    kdc.py \
-+    krb5utils.py \
-+    test_kcm.py \
-     $(NULL)
- 
- config.py: config.py.m4
-@@ -80,5 +83,6 @@ intgcheck-installed: config.py passwd group
- 	NSS_WRAPPER_MODULE_FN_PREFIX="sss" \
- 	UID_WRAPPER=1 \
- 	UID_WRAPPER_ROOT=1 \
-+	NON_WRAPPED_UID=$$(echo $$UID) \
- 	    fakeroot $(PYTHON2) $(PYTEST) -v --tb=native $(INTGCHECK_PYTEST_ARGS) .
- 	rm -f $(DESTDIR)$(logpath)/*
-diff --git a/src/tests/intg/kdc.py b/src/tests/intg/kdc.py
-new file mode 100644
-index 0000000000000000000000000000000000000000..dec33a979916c0979561afb22dc39d6eb8894ff3
---- /dev/null
-+++ b/src/tests/intg/kdc.py
-@@ -0,0 +1,175 @@
-+#
-+# MIT Kerberos server class
-+#
-+# Copyright (c) 2016 Red Hat, Inc.
-+#
-+# This is free software; you can redistribute it and/or modify it
-+# under the terms of the GNU General Public License as published by
-+# the Free Software Foundation; version 2 only
-+#
-+# This program is distributed in the hope that it will be useful, but
-+# WITHOUT ANY WARRANTY; without even the implied warranty of
-+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-+# General Public License for more details.
-+#
-+# You should have received a copy of the GNU General Public License
-+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
-+#
-+import os
-+import signal
-+import shutil
-+import subprocess
-+
-+from util import *
-+
-+
-+class KDC(object):
-+    """
-+    MIT Kerberos KDC instance
-+    """
-+
-+    def __init__(self, basedir, realm,
-+                 includedir=None,
-+                 kdc_port=10088,
-+                 kadmin_port=10749,
-+                 master_key='master'):
-+        self.basedir = basedir
-+        self.realm = realm
-+        self.kdc_port = kdc_port
-+        self.kadmin_port = kadmin_port
-+        self.master_key = master_key
-+
-+        self.kdc_basedir = self.basedir + "/var/krb5kdc"
-+        self.includedir = includedir or (self.kdc_basedir + "/include")
-+        self.kdc_logdir = self.kdc_basedir + "/log"
-+        self.kdc_conf_path = self.kdc_basedir + "/kdc.conf"
-+        self.krb5_conf_path = self.kdc_basedir + "/krb5.conf"
-+
-+        self.kdc_pid_file = self.kdc_basedir + "/kdc.pid"
-+
-+        self.acl_file = self.kdc_basedir + "/kadm5.acl"
-+
-+        self.admin_princ = "admin/admin@" + self.realm
-+
-+    def start_kdc(self, extra_args=[]):
-+        args = ["krb5kdc", '-P', self.kdc_pid_file] + extra_args
-+        return self._run_in_env(args, self.get_krb5_env())
-+
-+    def stop_kdc(self):
-+        try:
-+            with open(self.kdc_pid_file, "r") as pid_file:
-+                os.kill(int(pid_file.read()), signal.SIGTERM)
-+        except IOError as ioex:
-+            if ioex.errno == 2:
-+                pass
-+            else:
-+                raise ioex
-+
-+    def teardown(self):
-+        self.stop_kdc()
-+        shutil.rmtree(self.kdc_basedir)
-+
-+    def set_up(self):
-+        self._create_config()
-+        self._create_acl()
-+        self._create_kdb()
-+
-+    def get_krb5_env(self):
-+        my_env = os.environ
-+        my_env['KRB5_CONFIG'] = self.krb5_conf_path
-+        my_env['KRB5_KDC_PROFILE'] = self.kdc_conf_path
-+        return my_env
-+
-+    def add_config(self, include_files):
-+        for name, contents in include_files.items():
-+            include_fpath = os.path.join(self.includedir, name)
-+            with open(include_fpath, 'w') as include_file:
-+                include_file.write(contents)
-+
-+    def add_principal(self, princ, password=None):
-+        args = ["kadmin.local", "-q"]
-+        if password is None:
-+            args += ["addprinc -randkey %s" % (princ)]
-+        else:
-+            args += ["addprinc -pw %s %s" % (password, princ)]
-+        return self._run_in_env(args, self.get_krb5_env())
-+
-+    def _run_in_env(self, args, env):
-+        cmd = subprocess.Popen(args, env=env)
-+        out, err = cmd.communicate()
-+        return cmd.returncode, out, err
-+
-+    def _create_config(self):
-+        try:
-+            os.makedirs(self.kdc_basedir)
-+            os.makedirs(self.kdc_logdir)
-+            os.makedirs(self.includedir)
-+        except OSError as osex:
-+            if osex.errno == 17:
-+                pass
-+
-+        kdc_conf = self._format_kdc_conf()
-+        with open(self.kdc_conf_path, 'w') as kdc_conf_file:
-+            kdc_conf_file.write(kdc_conf)
-+
-+        krb5_conf = self._format_krb5_conf()
-+        with open(self.krb5_conf_path, 'w') as krb5_conf_file:
-+            krb5_conf_file.write(krb5_conf)
-+
-+    def _create_acl(self):
-+        with open(self.acl_file, 'w') as acl_fobject:
-+            acl_fobject.write(self.admin_princ)
-+
-+    def _create_kdb(self):
-+        self._run_in_env(
-+            ['kdb5_util', 'create', '-W', '-s', '-P', self.master_key],
-+            self.get_krb5_env()
-+        )
-+
-+    def _format_kdc_conf(self):
-+        database_path = self.kdc_basedir + "/principal"
-+        key_stash = self.kdc_basedir + "/stash." + self.realm
-+
-+        kdc_logfile = "FILE:" + self.kdc_logdir + "/krb5kdc.log"
-+        kadmin_logfile = "FILE:" + self.kdc_logdir + "/kadmin.log"
-+        libkrb5_logfile = "FILE:" + self.kdc_logdir + "/libkrb5.log"
-+
-+        kdc_conf = unindent("""
-+        [kdcdefaults]
-+        kdc_ports = {self.kdc_port}
-+        kdc_tcp_ports = {self.kdc_port}
-+
-+        [realms]
-+            {self.realm} = {{
-+                kadmind_port = {self.kadmin_port}
-+                database_name = {database_path}
-+                key_stash_file = {key_stash}
-+                acl_file = {self.acl_file}
-+            }}
-+
-+        [logging]
-+            kdc = {kdc_logfile}
-+            admin_server = {kadmin_logfile}
-+            default = {libkrb5_logfile}
-+        """).format(**locals())
-+        return kdc_conf
-+
-+    def _format_krb5_conf(self):
-+        kdc_uri = "localhost:%d" % self.kdc_port
-+        kadmin_uri = "localhost:%d" % self.kadmin_port
-+
-+        krb5_conf = unindent("""
-+        includedir {self.includedir}
-+
-+        [libdefaults]
-+        default_realm = {self.realm}
-+        dns_lookup_kdc = false
-+        dns_lookup_realm = false
-+
-+        [realms]
-+            {self.realm} = {{
-+                kdc = {kdc_uri}
-+                admin_server = {kadmin_uri}
-+            }}
-+        """).format(**locals())
-+        return krb5_conf
-diff --git a/src/tests/intg/krb5utils.py b/src/tests/intg/krb5utils.py
-new file mode 100644
-index 0000000000000000000000000000000000000000..775cffd0bbfa011f2d8ffc1169dccfef96d78fab
---- /dev/null
-+++ b/src/tests/intg/krb5utils.py
-@@ -0,0 +1,156 @@
-+#
-+# MIT Kerberos server class
-+#
-+# Copyright (c) 2016 Red Hat, Inc.
-+#
-+# This is free software; you can redistribute it and/or modify it
-+# under the terms of the GNU General Public License as published by
-+# the Free Software Foundation; version 2 only
-+#
-+# This program is distributed in the hope that it will be useful, but
-+# WITHOUT ANY WARRANTY; without even the implied warranty of
-+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-+# General Public License for more details.
-+#
-+# You should have received a copy of the GNU General Public License
-+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
-+#
-+import os
-+import subprocess
-+
-+
-+class NoPrincipals(Exception):
-+    def __init__(self):
-+        Exception.__init__(self, 'No principals in the collection')
-+
-+
-+class PrincNotFound(Exception):
-+    def __init__(self, principal):
-+        Exception.__init__(self, 'Principal %s not found' % principal)
-+
-+
-+class Krb5Utils(object):
-+    """
-+    Helper class to test Kerberos command line utilities
-+    """
-+    def __init__(self, krb5_conf_path):
-+        self.krb5_conf_path = krb5_conf_path
-+
-+    def _run_in_env(self, args, stdin=None, extra_env=None):
-+        my_env = os.environ
-+        my_env['KRB5_CONFIG'] = self.krb5_conf_path
-+
-+        if 'KRB5CCNAME' in my_env:
-+            del my_env['KRB5CCNAME']
-+        if extra_env is not None:
-+            my_env.update(extra_env)
-+
-+        cmd = subprocess.Popen(args,
-+                               env=my_env,
-+                               stdin=subprocess.PIPE,
-+                               stdout=subprocess.PIPE,
-+                               stderr=subprocess.PIPE)
-+        out, err = cmd.communicate(stdin)
-+        return cmd.returncode, out.decode('utf-8'), err.decode('utf-8')
-+
-+    def kinit(self, principal, password, env=None):
-+        args = ["kinit", principal]
-+        return self._run_in_env(args, password.encode('utf-8'), env)
-+
-+    def kvno(self, principal, env=None):
-+        args = ["kvno", principal]
-+        return self._run_in_env(args, env)
-+
-+    def kdestroy(self, all_ccaches=False, env=None):
-+        args = ["kdestroy"]
-+        if all_ccaches is True:
-+            args += ["-A"]
-+        retval, _, _ = self._run_in_env(args, env)
-+        return retval
-+
-+    def kswitch(self, principal, env=None):
-+        args = ["kswitch", '-p', principal]
-+        retval, _, _ = self._run_in_env(args, env)
-+        return retval
-+
-+    def _check_klist_l(self, line, exp_principal, exp_cache):
-+        try:
-+            princ, cache = line.split()
-+        except ValueError:
-+            return False
-+
-+        if exp_cache is not None and cache != exp_cache:
-+            return False
-+
-+        if exp_principal != princ:
-+            return False
-+
-+        return True
-+
-+    def num_princs(self, env=None):
-+        args = ["klist", "-l"]
-+        retval, out, err = self._run_in_env(args, extra_env=env)
-+        if retval != 0:
-+            return 0
-+
-+        outlines = [l for l in out.split('\n') if len(l) > 1]
-+        return len(outlines) - 2
-+
-+    def list_princs(self, env=None):
-+        args = ["klist", "-l"]
-+        retval, out, err = self._run_in_env(args, extra_env=env)
-+        if retval == 1:
-+            raise NoPrincipals
-+        elif retval != 0:
-+            raise Exception("klist failed: %d: %s\n", retval, err)
-+
-+        outlines = out.split('\n')
-+        if len(outlines) < 2:
-+            raise Exception("Not enough output from klist -l")
-+
-+        return [l for l in outlines[2:] if len(l) > 0]
-+
-+    def has_principal(self, exp_principal, exp_cache=None, env=None):
-+        try:
-+            princlist = self.list_princs(env)
-+        except NoPrincipals:
-+            return False
-+
-+        for line in princlist:
-+            matches = self._check_klist_l(line, exp_principal, exp_cache)
-+            if matches is True:
-+                return True
-+
-+        return False
-+
-+    def default_principal(self, env=None):
-+        principals = self.list_princs(env)
-+        return principals[0].split()[0]
-+
-+    def _parse_klist_a(self, out):
-+        dflprinc = None
-+        thisrealm = None
-+        ccache_dict = dict()
-+
-+        for line in [l for l in out.split('\n') if len(l) > 0]:
-+            if line.startswith("Default principal"):
-+                dflprinc = line.split()[2]
-+                thisrealm = '@' + dflprinc.split('@')[1]
-+            elif thisrealm is not None and line.endswith(thisrealm):
-+                svc = line.split()[-1]
-+                if dflprinc in ccache_dict:
-+                    ccache_dict[dflprinc].append(svc)
-+                else:
-+                    ccache_dict[dflprinc] = [svc]
-+
-+        return ccache_dict
-+
-+    def list_all_princs(self, env=None):
-+        args = ["klist", "-A"]
-+        retval, out, err = self._run_in_env(args, extra_env=env)
-+        if retval == 1:
-+            raise NoPrincipals
-+        elif retval != 0:
-+            raise Exception("klist -A failed: %d: %s\n", retval, err)
-+
-+        return self._parse_klist_a(out)
-diff --git a/src/tests/intg/test_kcm.py b/src/tests/intg/test_kcm.py
-new file mode 100644
-index 0000000000000000000000000000000000000000..ad1e4923bfe339cb040464757431d2ef3bf57ce1
---- /dev/null
-+++ b/src/tests/intg/test_kcm.py
-@@ -0,0 +1,361 @@
-+#
-+# KCM responder integration tests
-+#
-+# Copyright (c) 2016 Red Hat, Inc.
-+#
-+# This is free software; you can redistribute it and/or modify it
-+# under the terms of the GNU General Public License as published by
-+# the Free Software Foundation; version 2 only
-+#
-+# This program is distributed in the hope that it will be useful, but
-+# WITHOUT ANY WARRANTY; without even the implied warranty of
-+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-+# General Public License for more details.
-+#
-+# You should have received a copy of the GNU General Public License
-+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
-+#
-+import os
-+import os.path
-+import stat
-+import subprocess
-+import pytest
-+import socket
-+import time
-+import signal
-+
-+import kdc
-+import krb5utils
-+import config
-+from util import unindent, run_shell
-+
-+class KcmTestEnv(object):
-+    def __init__(self, k5kdc, k5util):
-+        self.k5kdc = k5kdc
-+        self.k5util = k5util
-+        self.counter = 0
-+
-+    def my_uid(self):
-+        s_myuid = os.environ['NON_WRAPPED_UID']
-+        return int(s_myuid)
-+
-+    def ccname(self, my_uid=None):
-+        if my_uid is None:
-+            my_uid = self.my_uid()
-+
-+        return "KCM:%d" % my_uid
-+
-+
-+@pytest.fixture(scope="module")
-+def kdc_instance(request):
-+    """Kerberos server instance fixture"""
-+    kdc_instance = kdc.KDC(config.PREFIX, "KCMTEST")
-+    try:
-+        kdc_instance.set_up()
-+        kdc_instance.start_kdc()
-+    except:
-+        kdc_instance.teardown()
-+        raise
-+    request.addfinalizer(kdc_instance.teardown)
-+    return kdc_instance
-+
-+
-+def create_conf_fixture(request, contents):
-+    """Generate sssd.conf and add teardown for removing it"""
-+    with open(config.CONF_PATH, "w") as conf:
-+        conf.write(contents)
-+    os.chmod(config.CONF_PATH, stat.S_IRUSR | stat.S_IWUSR)
-+    request.addfinalizer(lambda: os.unlink(config.CONF_PATH))
-+
-+
-+def create_sssd_kcm_fixture(sock_path, request):
-+    if subprocess.call(['sssd', "--genconf"]) != 0:
-+        raise Exception("failed to regenerate confdb")
-+
-+    resp_path = os.path.join(config.LIBEXEC_PATH, "sssd", "sssd_kcm")
-+    if not os.access(resp_path, os.X_OK):
-+        # It would be cleaner to use pytest.mark.skipif on the package level
-+        # but upstream insists on supporting RHEL-6..
-+        pytest.skip("No KCM responder, skipping")
-+
-+    kcm_pid = os.fork()
-+    assert kcm_pid >= 0
-+
-+    if kcm_pid == 0:
-+        if subprocess.call([resp_path, "--uid=0", "--gid=0"]) != 0:
-+            print("sssd_kcm failed to start")
-+            sys.exit(99)
-+    else:
-+        abs_sock_path = os.path.join(config.RUNSTATEDIR, sock_path)
-+        sck = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
-+        for _ in range(1, 10):
-+            try:
-+                sck.connect(abs_sock_path)
-+            except:
-+                time.sleep(0.1)
-+            else:
-+                break
-+        sck.close()
-+        assert os.path.exists(abs_sock_path)
-+
-+    def kcm_teardown():
-+        if kcm_pid == 0:
-+            return
-+        os.kill(kcm_pid, signal.SIGTERM)
-+
-+    request.addfinalizer(kcm_teardown)
-+    return kcm_pid
-+
-+
-+@pytest.fixture
-+def setup_for_kcm(request, kdc_instance):
-+    """
-+    Just set up the local provider for tests and enable the KCM
-+    responder
-+    """
-+    kcm_path = os.path.join(config.RUNSTATEDIR, "kcm.socket")
-+
-+    sssd_conf = unindent("""\
-+        [sssd]
-+        domains = local
-+        services = nss
-+
-+        [domain/local]
-+        id_provider = local
-+
-+        [kcm]
-+        socket_path = {kcm_path}
-+    """).format(**locals())
-+
-+    kcm_socket_include = unindent("""
-+    [libdefaults]
-+    default_ccache_name = KCM:
-+    kcm_socket = {kcm_path}
-+    """).format(**locals())
-+    kdc_instance.add_config({'kcm_socket': kcm_socket_include})
-+
-+    create_conf_fixture(request, sssd_conf)
-+    create_sssd_kcm_fixture(kcm_path, request)
-+
-+    k5util = krb5utils.Krb5Utils(kdc_instance.krb5_conf_path)
-+
-+    return KcmTestEnv(kdc_instance, k5util)
-+
-+
-+def test_kcm_init_list_destroy(setup_for_kcm):
-+    """
-+    Test that kinit, kdestroy and klist work with KCM
-+    """
-+    testenv = setup_for_kcm
-+    testenv.k5kdc.add_principal("kcmtest", "Secret123")
-+
-+    ok = testenv.k5util.has_principal("kcmtest@KCMTEST")
-+    assert ok is False
-+    nprincs = testenv.k5util.num_princs()
-+    assert nprincs == 0
-+
-+    out, _, _ = testenv.k5util.kinit("kcmtest", "Secret123")
-+    assert out == 0
-+    nprincs = testenv.k5util.num_princs()
-+    assert nprincs == 1
-+
-+    exp_ccname = testenv.ccname()
-+    ok = testenv.k5util.has_principal("kcmtest@KCMTEST", exp_ccname)
-+    assert ok is True
-+
-+    out = testenv.k5util.kdestroy()
-+    assert out == 0
-+
-+    ok = testenv.k5util.has_principal("kcmtest@KCMTEST")
-+    assert ok is False
-+    nprincs = testenv.k5util.num_princs()
-+    assert nprincs == 0
-+
-+
-+def test_kcm_overwrite(setup_for_kcm):
-+    """
-+    That that reusing a ccache reinitializes the cache and doesn't
-+    add the same principal twice
-+    """
-+    testenv = setup_for_kcm
-+    testenv.k5kdc.add_principal("kcmtest", "Secret123")
-+    exp_ccache = {'kcmtest@KCMTEST': ['krbtgt/KCMTEST@KCMTEST']}
-+
-+    assert testenv.k5util.num_princs() == 0
-+
-+    out, _, _ = testenv.k5util.kinit("kcmtest", "Secret123")
-+    assert out == 0
-+    assert exp_ccache == testenv.k5util.list_all_princs()
-+
-+    out, _, _ = testenv.k5util.kinit("kcmtest", "Secret123")
-+    assert out == 0
-+    assert exp_ccache == testenv.k5util.list_all_princs()
-+
-+
-+def test_collection_init_list_destroy(setup_for_kcm):
-+    """
-+    Test that multiple principals and service tickets can be stored
-+    in a collection.
-+    """
-+    testenv = setup_for_kcm
-+    testenv.k5kdc.add_principal("alice", "alicepw")
-+    testenv.k5kdc.add_principal("bob", "bobpw")
-+    testenv.k5kdc.add_principal("carol", "carolpw")
-+    testenv.k5kdc.add_principal("host/somehostname")
-+
-+    assert testenv.k5util.num_princs() == 0
-+
-+    out, _, _ = testenv.k5util.kinit("alice", "alicepw")
-+    assert out == 0
-+    assert testenv.k5util.default_principal() == 'alice@KCMTEST'
-+    cc_coll = testenv.k5util.list_all_princs()
-+    assert len(cc_coll) == 1
-+    assert cc_coll['alice@KCMTEST'] == ['krbtgt/KCMTEST@KCMTEST']
-+    assert 'bob@KCMTEST' not in cc_coll
-+    assert 'carol@KCMTEST' not in cc_coll
-+
-+    out, _, _ = testenv.k5util.kinit("bob", "bobpw")
-+    assert out == 0
-+    assert testenv.k5util.default_principal() == 'bob@KCMTEST'
-+    cc_coll = testenv.k5util.list_all_princs()
-+    assert len(cc_coll) == 2
-+    assert cc_coll['alice@KCMTEST'] == ['krbtgt/KCMTEST@KCMTEST']
-+    assert cc_coll['bob@KCMTEST'] == ['krbtgt/KCMTEST@KCMTEST']
-+    assert 'carol@KCMTEST' not in cc_coll
-+
-+    out, _, _ = testenv.k5util.kinit("carol", "carolpw")
-+    assert out == 0
-+    assert testenv.k5util.default_principal() == 'carol@KCMTEST'
-+    cc_coll = testenv.k5util.list_all_princs()
-+    assert len(cc_coll) == 3
-+    assert cc_coll['alice@KCMTEST'] == ['krbtgt/KCMTEST@KCMTEST']
-+    assert cc_coll['bob@KCMTEST'] == ['krbtgt/KCMTEST@KCMTEST']
-+    assert cc_coll['carol@KCMTEST'] == ['krbtgt/KCMTEST@KCMTEST']
-+
-+    out, _, _ = testenv.k5util.kvno('host/somehostname')
-+    assert out == 0
-+    cc_coll = testenv.k5util.list_all_princs()
-+    assert len(cc_coll) == 3
-+    assert set(cc_coll['carol@KCMTEST']) == set(['krbtgt/KCMTEST@KCMTEST',
-+                                                 'host/somehostname@KCMTEST'])
-+
-+    out = testenv.k5util.kdestroy()
-+    assert out == 0
-+    assert testenv.k5util.default_principal() == 'bob@KCMTEST'
-+    cc_coll = testenv.k5util.list_all_princs()
-+    assert len(cc_coll) == 2
-+    assert cc_coll['alice@KCMTEST'] == ['krbtgt/KCMTEST@KCMTEST']
-+    assert cc_coll['bob@KCMTEST'] == ['krbtgt/KCMTEST@KCMTEST']
-+    assert 'carol@KCMTEST' not in cc_coll
-+
-+    # FIXME - a bug in libkrb5?
-+    #out = testenv.k5util.kdestroy(all_ccaches=True)
-+    #assert out == 0
-+    #cc_coll = testenv.k5util.list_all_princs()
-+    #assert len(cc_coll) == 0
-+
-+
-+def test_kswitch(setup_for_kcm):
-+    """
-+    Test switching between principals
-+    """
-+    testenv = setup_for_kcm
-+    testenv.k5kdc.add_principal("alice", "alicepw")
-+    testenv.k5kdc.add_principal("bob", "bobpw")
-+    testenv.k5kdc.add_principal("host/somehostname")
-+    testenv.k5kdc.add_principal("host/differenthostname")
-+
-+    out, _, _ = testenv.k5util.kinit("alice", "alicepw")
-+    assert out == 0
-+    assert testenv.k5util.default_principal() == 'alice@KCMTEST'
-+
-+    out, _, _ = testenv.k5util.kinit("bob", "bobpw")
-+    assert out == 0
-+    assert testenv.k5util.default_principal() == 'bob@KCMTEST'
-+
-+    cc_coll = testenv.k5util.list_all_princs()
-+    assert len(cc_coll) == 2
-+    assert cc_coll['alice@KCMTEST'] == ['krbtgt/KCMTEST@KCMTEST']
-+    assert cc_coll['bob@KCMTEST'] == ['krbtgt/KCMTEST@KCMTEST']
-+
-+    out = testenv.k5util.kswitch("alice@KCMTEST")
-+    assert testenv.k5util.default_principal() == 'alice@KCMTEST'
-+    out, _, _ = testenv.k5util.kvno('host/somehostname')
-+    assert out == 0
-+    cc_coll = testenv.k5util.list_all_princs()
-+    assert len(cc_coll) == 2
-+    assert set(cc_coll['alice@KCMTEST']) == set(['krbtgt/KCMTEST@KCMTEST',
-+                                                 'host/somehostname@KCMTEST'])
-+    assert cc_coll['bob@KCMTEST'] == ['krbtgt/KCMTEST@KCMTEST']
-+
-+    out = testenv.k5util.kswitch("bob@KCMTEST")
-+    assert testenv.k5util.default_principal() == 'bob@KCMTEST'
-+    out, _, _ = testenv.k5util.kvno('host/differenthostname')
-+    assert out == 0
-+    cc_coll = testenv.k5util.list_all_princs()
-+    assert len(cc_coll) == 2
-+    assert set(cc_coll['alice@KCMTEST']) == set(['krbtgt/KCMTEST@KCMTEST',
-+                                                 'host/somehostname@KCMTEST'])
-+    assert set(cc_coll['bob@KCMTEST']) == set([
-+                                    'krbtgt/KCMTEST@KCMTEST',
-+                                    'host/differenthostname@KCMTEST'])
-+
-+
-+def test_subsidiaries(setup_for_kcm):
-+    """
-+    Test that subsidiary caches are usable and KCM: without specifying UID
-+    can be used to identify the collection
-+    """
-+    testenv = setup_for_kcm
-+    testenv.k5kdc.add_principal("alice", "alicepw")
-+    testenv.k5kdc.add_principal("bob", "bobpw")
-+    testenv.k5kdc.add_principal("host/somehostname")
-+    testenv.k5kdc.add_principal("host/differenthostname")
-+
-+    out, _, _ = testenv.k5util.kinit("alice", "alicepw")
-+    assert out == 0
-+    out, _, _ = testenv.k5util.kvno('host/somehostname')
-+
-+    out, _, _ = testenv.k5util.kinit("bob", "bobpw")
-+    assert out == 0
-+    out, _, _ = testenv.k5util.kvno('host/differenthostname')
-+
-+    exp_cc_coll = dict()
-+    exp_cc_coll['alice@KCMTEST'] = 'host/somehostname@KCMTEST'
-+    exp_cc_coll['bob@KCMTEST'] = 'host/differenthostname@KCMTEST'
-+
-+    klist_l = testenv.k5util.list_princs()
-+    princ_ccache = dict()
-+    for line in klist_l:
-+        princ, subsidiary = line.split()
-+        princ_ccache[princ] = subsidiary
-+
-+    for princ, subsidiary in princ_ccache.items():
-+        env = {'KRB5CCNAME': subsidiary}
-+        cc_coll = testenv.k5util.list_all_princs(env=env)
-+        assert len(cc_coll) == 1
-+        assert princ in cc_coll
-+        assert exp_cc_coll[princ] in cc_coll[princ]
-+
-+    cc_coll = testenv.k5util.list_all_princs(env={'KRB5CCNAME': 'KCM:'})
-+    assert len(cc_coll) == 2
-+    assert set(cc_coll['alice@KCMTEST']) == set(['krbtgt/KCMTEST@KCMTEST',
-+                                                 'host/somehostname@KCMTEST'])
-+    assert set(cc_coll['bob@KCMTEST']) == set([
-+                                            'krbtgt/KCMTEST@KCMTEST',
-+                                            'host/differenthostname@KCMTEST'])
-+
-+
-+def test_kdestroy_nocache(setup_for_kcm):
-+    """
-+    Destroying a non-existing ccache should not throw an error
-+    """
-+    testenv = setup_for_kcm
-+    testenv.k5kdc.add_principal("alice", "alicepw")
-+    out, _, _ = testenv.k5util.kinit("alice", "alicepw")
-+    assert out == 0
-+
-+    testenv.k5util.kdestroy()
-+    assert out == 0
-+    out = testenv.k5util.kdestroy()
-+    assert out == 0
--- 
-2.9.3
-
diff --git a/SOURCES/0029-NSS-TESTS-add-unit-tests-for-_EX-requests.patch b/SOURCES/0029-NSS-TESTS-add-unit-tests-for-_EX-requests.patch
new file mode 100644
index 0000000..384ca17
--- /dev/null
+++ b/SOURCES/0029-NSS-TESTS-add-unit-tests-for-_EX-requests.patch
@@ -0,0 +1,590 @@
+From e0f1d81bc24416da1d6d646a0cd3a14bd7e3e02d Mon Sep 17 00:00:00 2001
+From: Sumit Bose <sbose@redhat.com>
+Date: Wed, 25 Oct 2017 21:31:54 +0200
+Subject: [PATCH 29/31] NSS/TESTS: add unit tests for *_EX requests
+
+The patch adds unit tests for the new *_EX requests with different input
+types and flags.
+
+Related to https://pagure.io/SSSD/sssd/issue/2478
+
+Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
+(cherry picked from commit 85da8a5e90bffc8b0fef5e0ea364a8d3cb50de86)
+---
+ src/tests/cmocka/test_nss_srv.c | 539 ++++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 539 insertions(+)
+
+diff --git a/src/tests/cmocka/test_nss_srv.c b/src/tests/cmocka/test_nss_srv.c
+index ccedf96beaecfaa4232bbe456d5e5a8394098483..6aa726153183b5a871a75d398727ea7132358ca6 100644
+--- a/src/tests/cmocka/test_nss_srv.c
++++ b/src/tests/cmocka/test_nss_srv.c
+@@ -255,6 +255,45 @@ static void mock_input_user_or_group(const char *input)
+     mock_parse_inp(shortname, domname, EOK);
+ }
+ 
++static void mock_input_user_or_group_ex(bool do_parse_inp, const char *input,
++                                        uint32_t flags)
++{
++    const char *copy;
++    const char *shortname;
++    const char *domname;
++    char *separator;
++    uint8_t *data;
++    size_t len;
++
++    len = strlen(input);
++    len++;
++    data = talloc_size(nss_test_ctx, len + sizeof(uint32_t));
++    assert_non_null(data);
++    memcpy(data, input, len);
++    SAFEALIGN_COPY_UINT32(data + len, &flags, NULL);
++
++    will_return(__wrap_sss_packet_get_body, WRAP_CALL_WRAPPER);
++    will_return(__wrap_sss_packet_get_body, data);
++    will_return(__wrap_sss_packet_get_body, len + sizeof(uint32_t));
++
++    if (do_parse_inp) {
++        copy = talloc_strdup(nss_test_ctx, input);
++        assert_non_null(copy);
++
++        separator = strrchr(copy, '@');
++        if (separator == NULL) {
++            shortname = input;
++            domname = NULL;
++        } else {
++            *separator = '\0';
++            shortname = copy;
++            domname = separator + 1;
++        }
++
++        mock_parse_inp(shortname, domname, EOK);
++    }
++}
++
+ static void mock_input_upn(const char *upn)
+ {
+     will_return(__wrap_sss_packet_get_body, WRAP_CALL_WRAPPER);
+@@ -291,6 +330,20 @@ static void mock_input_id(TALLOC_CTX *mem_ctx, uint32_t id)
+     will_return(__wrap_sss_packet_get_body, sizeof(uint32_t));
+ }
+ 
++static void mock_input_id_ex(TALLOC_CTX *mem_ctx, uint32_t id, uint32_t flags)
++{
++    uint8_t *body;
++
++    body = talloc_zero_array(mem_ctx, uint8_t, 8);
++    if (body == NULL) return;
++
++    SAFEALIGN_SETMEM_UINT32(body, id, NULL);
++    SAFEALIGN_SETMEM_UINT32(body + sizeof(uint32_t), flags, NULL);
++    will_return(__wrap_sss_packet_get_body, WRAP_CALL_WRAPPER);
++    will_return(__wrap_sss_packet_get_body, body);
++    will_return(__wrap_sss_packet_get_body, 2 * sizeof(uint32_t));
++}
++
+ static void mock_fill_user(void)
+ {
+     /* One packet for the entry and one for num entries */
+@@ -4143,6 +4196,482 @@ void test_nss_getsidbyname_neg(void **state)
+     assert_int_equal(ret, ENOENT);
+ }
+ 
++static int test_nss_EINVAL_check(uint32_t status, uint8_t *body, size_t blen)
++{
++    assert_int_equal(status, EINVAL);
++    assert_int_equal(blen, 0);
++
++    return EOK;
++}
++
++#define RESET_TCTX do { \
++    nss_test_ctx->tctx->done = false; \
++    nss_test_ctx->tctx->error = EIO; \
++} while (0)
++
++void test_nss_getpwnam_ex(void **state)
++{
++    errno_t ret;
++
++    ret = store_user(nss_test_ctx, nss_test_ctx->tctx->dom,
++                     &getpwnam_usr, NULL, 0);
++    assert_int_equal(ret, EOK);
++
++    mock_input_user_or_group_ex(true, "testuser", 0);
++    will_return(__wrap_sss_packet_get_cmd, SSS_NSS_GETPWNAM_EX);
++    mock_fill_user();
++
++    /* Query for that user, call a callback when command finishes */
++    set_cmd_cb(test_nss_getpwnam_check);
++    ret = sss_cmd_execute(nss_test_ctx->cctx, SSS_NSS_GETPWNAM_EX,
++                          nss_test_ctx->nss_cmds);
++    assert_int_equal(ret, EOK);
++
++    /* Wait until the test finishes with EOK */
++    ret = test_ev_loop(nss_test_ctx->tctx);
++    assert_int_equal(ret, EOK);
++    RESET_TCTX;
++
++    /* Use old input format, expect EINVAL */
++    will_return(__wrap_sss_packet_get_body, WRAP_CALL_WRAPPER);
++    will_return(__wrap_sss_packet_get_body, "testuser");
++    will_return(__wrap_sss_packet_get_body, 0);
++    will_return(__wrap_sss_packet_get_cmd, SSS_NSS_GETPWNAM_EX);
++
++    set_cmd_cb(test_nss_EINVAL_check);
++    ret = sss_cmd_execute(nss_test_ctx->cctx, SSS_NSS_GETPWNAM_EX,
++                          nss_test_ctx->nss_cmds);
++    assert_int_equal(ret, EOK);
++
++    ret = test_ev_loop(nss_test_ctx->tctx);
++    assert_int_equal(ret, EOK);
++    RESET_TCTX;
++
++    /* Use unsupported flag combination, expect EINVAL */
++    mock_input_user_or_group_ex(false, "testuser",
++                                SSS_NSS_EX_FLAG_NO_CACHE
++                                    |SSS_NSS_EX_FLAG_INVALIDATE_CACHE);
++    will_return(__wrap_sss_packet_get_cmd, SSS_NSS_GETPWNAM_EX);
++
++    set_cmd_cb(test_nss_EINVAL_check);
++    ret = sss_cmd_execute(nss_test_ctx->cctx, SSS_NSS_GETPWNAM_EX,
++                          nss_test_ctx->nss_cmds);
++    assert_int_equal(ret, EOK);
++
++    /* Wait until the test finishes with EOK */
++    ret = test_ev_loop(nss_test_ctx->tctx);
++    assert_int_equal(ret, EOK);
++    RESET_TCTX;
++
++    /* Use flag SSS_NSS_EX_FLAG_NO_CACHE,
++     * will cause a backend lookup -> mock_account_recv_simple() */
++    mock_input_user_or_group_ex(true, "testuser", SSS_NSS_EX_FLAG_NO_CACHE);
++    will_return(__wrap_sss_packet_get_cmd, SSS_NSS_GETPWNAM_EX);
++    mock_fill_user();
++    mock_account_recv_simple();
++
++    set_cmd_cb(test_nss_getpwnam_check);
++    ret = sss_cmd_execute(nss_test_ctx->cctx, SSS_NSS_GETPWNAM_EX,
++                          nss_test_ctx->nss_cmds);
++    assert_int_equal(ret, EOK);
++
++    /* Wait until the test finishes with EOK */
++    ret = test_ev_loop(nss_test_ctx->tctx);
++    assert_int_equal(ret, EOK);
++    RESET_TCTX;
++
++    /* Use flag SSS_NSS_EX_FLAG_INVALIDATE_CACHE */
++    mock_input_user_or_group_ex(true, "testuser",
++                                SSS_NSS_EX_FLAG_INVALIDATE_CACHE);
++    will_return(__wrap_sss_packet_get_cmd, SSS_NSS_GETPWNAM_EX);
++    mock_fill_user();
++
++    set_cmd_cb(test_nss_getpwnam_check);
++    ret = sss_cmd_execute(nss_test_ctx->cctx, SSS_NSS_GETPWNAM_EX,
++                          nss_test_ctx->nss_cmds);
++    assert_int_equal(ret, EOK);
++
++    /* Wait until the test finishes with EOK */
++    ret = test_ev_loop(nss_test_ctx->tctx);
++    assert_int_equal(ret, EOK);
++}
++
++void test_nss_getpwuid_ex(void **state)
++{
++    errno_t ret;
++    uint32_t id = 101;
++
++    /* Prime the cache with a valid user */
++    ret = store_user(nss_test_ctx, nss_test_ctx->tctx->dom,
++                     &getpwuid_usr, NULL, 0);
++    assert_int_equal(ret, EOK);
++
++    mock_input_id_ex(nss_test_ctx, id, 0);
++    will_return(__wrap_sss_packet_get_cmd, SSS_NSS_GETPWUID_EX);
++    mock_fill_user();
++
++    /* Query for that id, call a callback when command finishes */
++    set_cmd_cb(test_nss_getpwuid_check);
++    ret = sss_cmd_execute(nss_test_ctx->cctx, SSS_NSS_GETPWUID_EX,
++                          nss_test_ctx->nss_cmds);
++    assert_int_equal(ret, EOK);
++
++    /* Wait until the test finishes with EOK */
++    ret = test_ev_loop(nss_test_ctx->tctx);
++    assert_int_equal(ret, EOK);
++    RESET_TCTX;
++
++    /* Use old input format, expect failure */
++    mock_input_id(nss_test_ctx, id);
++    will_return(__wrap_sss_packet_get_cmd, SSS_NSS_GETPWUID_EX);
++
++    set_cmd_cb(test_nss_EINVAL_check);
++    ret = sss_cmd_execute(nss_test_ctx->cctx, SSS_NSS_GETPWUID_EX,
++                          nss_test_ctx->nss_cmds);
++    assert_int_equal(ret, EOK);
++
++    /* Wait until the test finishes with EOK */
++    ret = test_ev_loop(nss_test_ctx->tctx);
++    assert_int_equal(ret, EOK);
++    RESET_TCTX;
++
++    /* Use unsupported flag combination, expect EINVAL */
++    mock_input_id_ex(nss_test_ctx, id, SSS_NSS_EX_FLAG_NO_CACHE
++                                            |SSS_NSS_EX_FLAG_INVALIDATE_CACHE);
++    will_return(__wrap_sss_packet_get_cmd, SSS_NSS_GETPWUID_EX);
++
++    set_cmd_cb(test_nss_EINVAL_check);
++    ret = sss_cmd_execute(nss_test_ctx->cctx, SSS_NSS_GETPWUID_EX,
++                          nss_test_ctx->nss_cmds);
++    assert_int_equal(ret, EOK);
++
++    /* Wait until the test finishes with EOK */
++    ret = test_ev_loop(nss_test_ctx->tctx);
++    assert_int_equal(ret, EOK);
++    RESET_TCTX;
++
++    /* Use flag SSS_NSS_EX_FLAG_NO_CACHE,
++     * will cause a backend lookup -> mock_account_recv_simple() */
++    mock_input_id_ex(nss_test_ctx, id, SSS_NSS_EX_FLAG_NO_CACHE);
++    will_return(__wrap_sss_packet_get_cmd, SSS_NSS_GETPWUID_EX);
++    mock_fill_user();
++    mock_account_recv_simple();
++
++    set_cmd_cb(test_nss_getpwuid_check);
++    ret = sss_cmd_execute(nss_test_ctx->cctx, SSS_NSS_GETPWUID_EX,
++                          nss_test_ctx->nss_cmds);
++    assert_int_equal(ret, EOK);
++
++    /* Wait until the test finishes with EOK */
++    ret = test_ev_loop(nss_test_ctx->tctx);
++    assert_int_equal(ret, EOK);
++    RESET_TCTX;
++
++    /* Use flag SSS_NSS_EX_FLAG_INVALIDATE_CACHE */
++    mock_input_id_ex(nss_test_ctx, id, SSS_NSS_EX_FLAG_INVALIDATE_CACHE);
++    will_return(__wrap_sss_packet_get_cmd, SSS_NSS_GETPWUID_EX);
++    mock_fill_user();
++
++    set_cmd_cb(test_nss_getpwuid_check);
++    ret = sss_cmd_execute(nss_test_ctx->cctx, SSS_NSS_GETPWUID_EX,
++                          nss_test_ctx->nss_cmds);
++    assert_int_equal(ret, EOK);
++
++    /* Wait until the test finishes with EOK */
++    ret = test_ev_loop(nss_test_ctx->tctx);
++    assert_int_equal(ret, EOK);
++}
++
++void test_nss_getgrnam_ex_no_members(void **state)
++{
++    errno_t ret;
++
++    /* Test group is still in the cache */
++
++    mock_input_user_or_group_ex(true, getgrnam_no_members.gr_name, 0);
++    will_return(__wrap_sss_packet_get_cmd, SSS_NSS_GETGRNAM_EX);
++    will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL);
++    will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL);
++    will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL);
++    will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL);
++
++    /* Query for that group, call a callback when command finishes */
++    set_cmd_cb(test_nss_getgrnam_no_members_check);
++    ret = sss_cmd_execute(nss_test_ctx->cctx, SSS_NSS_GETGRNAM_EX,
++                          nss_test_ctx->nss_cmds);
++    assert_int_equal(ret, EOK);
++
++    /* Wait until the test finishes with EOK */
++    ret = test_ev_loop(nss_test_ctx->tctx);
++    assert_int_equal(ret, EOK);
++    RESET_TCTX;
++
++    /* Use old input format, expect failure */
++    will_return(__wrap_sss_packet_get_body, WRAP_CALL_WRAPPER);
++    will_return(__wrap_sss_packet_get_body, "testgroup");
++    will_return(__wrap_sss_packet_get_body, 0);
++    will_return(__wrap_sss_packet_get_cmd, SSS_NSS_GETGRNAM_EX);
++
++    set_cmd_cb(test_nss_EINVAL_check);
++    ret = sss_cmd_execute(nss_test_ctx->cctx, SSS_NSS_GETGRNAM_EX,
++                          nss_test_ctx->nss_cmds);
++    assert_int_equal(ret, EOK);
++
++    ret = test_ev_loop(nss_test_ctx->tctx);
++    assert_int_equal(ret, EOK);
++    RESET_TCTX;
++
++    /* Use unsupported flag combination, expect EINVAL */
++    mock_input_user_or_group_ex(false, getgrnam_no_members.gr_name,
++                                SSS_NSS_EX_FLAG_NO_CACHE
++                                    |SSS_NSS_EX_FLAG_INVALIDATE_CACHE);
++    will_return(__wrap_sss_packet_get_cmd, SSS_NSS_GETGRNAM_EX);
++
++    set_cmd_cb(test_nss_EINVAL_check);
++    ret = sss_cmd_execute(nss_test_ctx->cctx, SSS_NSS_GETGRNAM_EX,
++                          nss_test_ctx->nss_cmds);
++    assert_int_equal(ret, EOK);
++
++    /* Wait until the test finishes with EOK */
++    ret = test_ev_loop(nss_test_ctx->tctx);
++    assert_int_equal(ret, EOK);
++    RESET_TCTX;
++
++    /* Use flag SSS_NSS_EX_FLAG_NO_CACHE,
++     * will cause a backend lookup -> mock_account_recv_simple() */
++    mock_input_user_or_group_ex(true, getgrnam_no_members.gr_name,
++                                SSS_NSS_EX_FLAG_NO_CACHE);
++    will_return(__wrap_sss_packet_get_cmd, SSS_NSS_GETGRNAM_EX);
++    will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL);
++    will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL);
++    will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL);
++    will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL);
++    mock_account_recv_simple();
++
++    set_cmd_cb(test_nss_getgrnam_no_members_check);
++    ret = sss_cmd_execute(nss_test_ctx->cctx, SSS_NSS_GETGRNAM_EX,
++                          nss_test_ctx->nss_cmds);
++    assert_int_equal(ret, EOK);
++
++    /* Wait until the test finishes with EOK */
++    ret = test_ev_loop(nss_test_ctx->tctx);
++    assert_int_equal(ret, EOK);
++    RESET_TCTX;
++
++    /* Use flag SSS_NSS_EX_FLAG_INVALIDATE_CACHE */
++    mock_input_user_or_group_ex(true, getgrnam_no_members.gr_name,
++                                SSS_NSS_EX_FLAG_INVALIDATE_CACHE);
++    will_return(__wrap_sss_packet_get_cmd, SSS_NSS_GETGRNAM_EX);
++    will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL);
++    will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL);
++    will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL);
++    will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL);
++
++    set_cmd_cb(test_nss_getgrnam_no_members_check);
++    ret = sss_cmd_execute(nss_test_ctx->cctx, SSS_NSS_GETGRNAM_EX,
++                          nss_test_ctx->nss_cmds);
++    assert_int_equal(ret, EOK);
++
++    /* Wait until the test finishes with EOK */
++    ret = test_ev_loop(nss_test_ctx->tctx);
++    assert_int_equal(ret, EOK);
++}
++
++void test_nss_getgrgid_ex_no_members(void **state)
++{
++    errno_t ret;
++
++    /* Test group is still in the cache */
++
++    mock_input_id_ex(nss_test_ctx, getgrnam_no_members.gr_gid, 0);
++    will_return(__wrap_sss_packet_get_cmd, SSS_NSS_GETGRGID_EX);
++    will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL);
++    will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL);
++    will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL);
++    will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL);
++    mock_account_recv_simple();
++
++    /* Query for that group, call a callback when command finishes */
++    set_cmd_cb(test_nss_getgrnam_no_members_check);
++    ret = sss_cmd_execute(nss_test_ctx->cctx, SSS_NSS_GETGRGID_EX,
++                          nss_test_ctx->nss_cmds);
++    assert_int_equal(ret, EOK);
++
++    /* Wait until the test finishes with EOK */
++    ret = test_ev_loop(nss_test_ctx->tctx);
++    assert_int_equal(ret, EOK);
++    RESET_TCTX;
++
++    /* Use old input format, expect failure */
++    mock_input_id(nss_test_ctx, getgrnam_no_members.gr_gid);
++    will_return(__wrap_sss_packet_get_cmd, SSS_NSS_GETGRGID_EX);
++
++    set_cmd_cb(test_nss_EINVAL_check);
++    ret = sss_cmd_execute(nss_test_ctx->cctx, SSS_NSS_GETGRGID_EX,
++                          nss_test_ctx->nss_cmds);
++    assert_int_equal(ret, EOK);
++
++    /* Wait until the test finishes with EOK */
++    ret = test_ev_loop(nss_test_ctx->tctx);
++    assert_int_equal(ret, EOK);
++    RESET_TCTX;
++
++    /* Use unsupported flag combination, expect EINVAL */
++    mock_input_id_ex(nss_test_ctx, getgrnam_no_members.gr_gid,
++                     SSS_NSS_EX_FLAG_NO_CACHE
++                        |SSS_NSS_EX_FLAG_INVALIDATE_CACHE);
++    will_return(__wrap_sss_packet_get_cmd, SSS_NSS_GETGRGID_EX);
++
++    set_cmd_cb(test_nss_EINVAL_check);
++    ret = sss_cmd_execute(nss_test_ctx->cctx, SSS_NSS_GETGRGID_EX,
++                          nss_test_ctx->nss_cmds);
++    assert_int_equal(ret, EOK);
++
++    /* Wait until the test finishes with EOK */
++    ret = test_ev_loop(nss_test_ctx->tctx);
++    assert_int_equal(ret, EOK);
++    RESET_TCTX;
++
++    /* Use flag SSS_NSS_EX_FLAG_NO_CACHE,
++     * will cause a backend lookup -> mock_account_recv_simple() */
++    mock_input_id_ex(nss_test_ctx, getgrnam_no_members.gr_gid,
++                     SSS_NSS_EX_FLAG_NO_CACHE);
++    will_return(__wrap_sss_packet_get_cmd, SSS_NSS_GETGRGID_EX);
++    will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL);
++    will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL);
++    will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL);
++    will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL);
++    mock_account_recv_simple();
++    mock_account_recv_simple();
++
++    set_cmd_cb(test_nss_getgrnam_no_members_check);
++    ret = sss_cmd_execute(nss_test_ctx->cctx, SSS_NSS_GETGRGID_EX,
++                          nss_test_ctx->nss_cmds);
++    assert_int_equal(ret, EOK);
++
++    /* Wait until the test finishes with EOK */
++    ret = test_ev_loop(nss_test_ctx->tctx);
++    assert_int_equal(ret, EOK);
++    RESET_TCTX;
++
++    /* Use flag SSS_NSS_EX_FLAG_INVALIDATE_CACHE */
++    mock_input_id_ex(nss_test_ctx, getgrnam_no_members.gr_gid,
++                     SSS_NSS_EX_FLAG_INVALIDATE_CACHE);
++    will_return(__wrap_sss_packet_get_cmd, SSS_NSS_GETGRGID_EX);
++    will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL);
++    will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL);
++    will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL);
++    will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL);
++
++    set_cmd_cb(test_nss_getgrnam_no_members_check);
++    ret = sss_cmd_execute(nss_test_ctx->cctx, SSS_NSS_GETGRGID_EX,
++                          nss_test_ctx->nss_cmds);
++    assert_int_equal(ret, EOK);
++
++    /* Wait until the test finishes with EOK */
++    ret = test_ev_loop(nss_test_ctx->tctx);
++    assert_int_equal(ret, EOK);
++}
++
++void test_nss_initgroups_ex(void **state)
++{
++    errno_t ret;
++    struct sysdb_attrs *attrs;
++
++    attrs = sysdb_new_attrs(nss_test_ctx);
++    assert_non_null(attrs);
++
++    ret = sysdb_attrs_add_time_t(attrs, SYSDB_INITGR_EXPIRE,
++                                 time(NULL) + 300);
++    assert_int_equal(ret, EOK);
++
++    ret = sysdb_attrs_add_string(attrs, SYSDB_UPN, "upninitgr@upndomain.test");
++    assert_int_equal(ret, EOK);
++
++    ret = store_user(nss_test_ctx, nss_test_ctx->tctx->dom,
++                     &testinitgr_usr, attrs, 0);
++    assert_int_equal(ret, EOK);
++
++    mock_input_user_or_group_ex(true, "testinitgr", 0);
++    will_return(__wrap_sss_packet_get_cmd, SSS_NSS_INITGR_EX);
++    mock_fill_user();
++
++    /* Query for that user, call a callback when command finishes */
++    set_cmd_cb(test_nss_initgr_check);
++    ret = sss_cmd_execute(nss_test_ctx->cctx, SSS_NSS_INITGR_EX,
++                          nss_test_ctx->nss_cmds);
++    assert_int_equal(ret, EOK);
++
++    /* Wait until the test finishes with EOK */
++    ret = test_ev_loop(nss_test_ctx->tctx);
++    assert_int_equal(ret, EOK);
++    RESET_TCTX;
++
++    /* Use old input format, expect failure */
++    will_return(__wrap_sss_packet_get_body, WRAP_CALL_WRAPPER);
++    will_return(__wrap_sss_packet_get_body, "testinitgr");
++    will_return(__wrap_sss_packet_get_body, 0);
++    will_return(__wrap_sss_packet_get_cmd, SSS_NSS_INITGR_EX);
++
++    set_cmd_cb(test_nss_EINVAL_check);
++    ret = sss_cmd_execute(nss_test_ctx->cctx, SSS_NSS_INITGR_EX,
++                          nss_test_ctx->nss_cmds);
++    assert_int_equal(ret, EOK);
++
++    ret = test_ev_loop(nss_test_ctx->tctx);
++    assert_int_equal(ret, EOK);
++    RESET_TCTX;
++
++    /* Use unsupported flag combination, expect EINVAL */
++    mock_input_user_or_group_ex(false, "testinitgr",
++                                SSS_NSS_EX_FLAG_NO_CACHE
++                                    |SSS_NSS_EX_FLAG_INVALIDATE_CACHE);
++    will_return(__wrap_sss_packet_get_cmd, SSS_NSS_INITGR_EX);
++
++    set_cmd_cb(test_nss_EINVAL_check);
++    ret = sss_cmd_execute(nss_test_ctx->cctx, SSS_NSS_INITGR_EX,
++                          nss_test_ctx->nss_cmds);
++    assert_int_equal(ret, EOK);
++
++    /* Wait until the test finishes with EOK */
++    ret = test_ev_loop(nss_test_ctx->tctx);
++    assert_int_equal(ret, EOK);
++    RESET_TCTX;
++
++    /* Use flag SSS_NSS_EX_FLAG_NO_CACHE,
++     * will cause a backend lookup -> mock_account_recv_simple() */
++    mock_input_user_or_group_ex(true, "testinitgr",
++                                SSS_NSS_EX_FLAG_NO_CACHE);
++    will_return(__wrap_sss_packet_get_cmd, SSS_NSS_INITGR_EX);
++    mock_fill_user();
++    mock_account_recv_simple();
++
++    set_cmd_cb(test_nss_initgr_check);
++    ret = sss_cmd_execute(nss_test_ctx->cctx, SSS_NSS_INITGR_EX,
++                          nss_test_ctx->nss_cmds);
++    assert_int_equal(ret, EOK);
++
++    /* Wait until the test finishes with EOK */
++    ret = test_ev_loop(nss_test_ctx->tctx);
++    assert_int_equal(ret, EOK);
++    RESET_TCTX;
++
++    /* Use flag SSS_NSS_EX_FLAG_INVALIDATE_CACHE */
++    mock_input_user_or_group_ex(true, "testinitgr",
++                                SSS_NSS_EX_FLAG_INVALIDATE_CACHE);
++    will_return(__wrap_sss_packet_get_cmd, SSS_NSS_INITGR_EX);
++    mock_fill_user();
++
++    set_cmd_cb(test_nss_initgr_check);
++    ret = sss_cmd_execute(nss_test_ctx->cctx, SSS_NSS_INITGR_EX,
++                          nss_test_ctx->nss_cmds);
++    assert_int_equal(ret, EOK);
++
++    /* Wait until the test finishes with EOK */
++    ret = test_ev_loop(nss_test_ctx->tctx);
++    assert_int_equal(ret, EOK);
++}
++
+ int main(int argc, const char *argv[])
+ {
+     int rv;
+@@ -4288,6 +4817,16 @@ int main(int argc, const char *argv[])
+                                         nss_test_setup, nss_test_teardown),
+         cmocka_unit_test_setup_teardown(test_nss_getsidbyname_neg,
+                                         nss_test_setup, nss_test_teardown),
++        cmocka_unit_test_setup_teardown(test_nss_getpwnam_ex,
++                                        nss_test_setup, nss_test_teardown),
++        cmocka_unit_test_setup_teardown(test_nss_getpwuid_ex,
++                                        nss_test_setup, nss_test_teardown),
++        cmocka_unit_test_setup_teardown(test_nss_getgrnam_ex_no_members,
++                                        nss_test_setup, nss_test_teardown),
++        cmocka_unit_test_setup_teardown(test_nss_getgrgid_ex_no_members,
++                                        nss_test_setup, nss_test_teardown),
++        cmocka_unit_test_setup_teardown(test_nss_initgroups_ex,
++                                        nss_test_setup, nss_test_teardown),
+     };
+ 
+     /* Set debug level to invalid value so we can deside if -d 0 was used. */
+-- 
+2.13.6
+
diff --git a/SOURCES/0029-SECRETS-Create-DB-path-before-the-operation-itself.patch b/SOURCES/0029-SECRETS-Create-DB-path-before-the-operation-itself.patch
deleted file mode 100644
index 3b1bee7..0000000
--- a/SOURCES/0029-SECRETS-Create-DB-path-before-the-operation-itself.patch
+++ /dev/null
@@ -1,405 +0,0 @@
-From 27e11e8f03e1bad5d1be276efaf1406b16b11625 Mon Sep 17 00:00:00 2001
-From: Jakub Hrozek <jhrozek@redhat.com>
-Date: Tue, 3 Jan 2017 16:00:38 +0100
-Subject: [PATCH 29/36] SECRETS: Create DB path before the operation itself
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-This is a refactoring where instead of creating the ldb path in the
-operation itself, we create the ldb path when creating the local db request
-and pass the path to the operation.
-
-This would allow us to store different kind of objects in the secrets
-storage later.
-
-Reviewed-by: Michal Židek <mzidek@redhat.com>
-Reviewed-by: Simo Sorce <simo@redhat.com>
----
- src/responder/secrets/local.c | 170 +++++++++++++++++++++---------------------
- 1 file changed, 84 insertions(+), 86 deletions(-)
-
-diff --git a/src/responder/secrets/local.c b/src/responder/secrets/local.c
-index ed70193bcb27d84eaf449f6f7571c94f466c9896..9dcdd9925e542499d3a962b4998103b07c26a5ab 100644
---- a/src/responder/secrets/local.c
-+++ b/src/responder/secrets/local.c
-@@ -199,39 +199,36 @@ static char *local_dn_to_path(TALLOC_CTX *mem_ctx,
-     return path;
- }
- 
-+struct local_db_req {
-+    char *path;
-+    struct ldb_dn *basedn;
-+};
-+
- #define LOCAL_SIMPLE_FILTER "(type=simple)"
- #define LOCAL_CONTAINER_FILTER "(type=container)"
- 
- static int local_db_get_simple(TALLOC_CTX *mem_ctx,
-                                struct local_context *lctx,
--                               const char *req_path,
-+                               struct local_db_req *lc_req,
-                                char **secret)
- {
-     TALLOC_CTX *tmp_ctx;
-     static const char *attrs[] = { "secret", "enctype", NULL };
-     struct ldb_result *res;
--    struct ldb_dn *dn;
-     const char *attr_secret;
-     const char *attr_enctype;
-     int ret;
- 
--    DEBUG(SSSDBG_TRACE_FUNC, "Retrieving a secret from [%s]\n", req_path);
-+    DEBUG(SSSDBG_TRACE_FUNC, "Retrieving a secret from [%s]\n", lc_req->path);
- 
-     tmp_ctx = talloc_new(mem_ctx);
-     if (!tmp_ctx) return ENOMEM;
- 
--    ret = local_db_dn(tmp_ctx, lctx->ldb, req_path, &dn);
--    if (ret != EOK) {
--        DEBUG(SSSDBG_OP_FAILURE,
--              "local_db_dn failed [%d]: %s\n", ret, sss_strerror(ret));
--        goto done;
--    }
--
-     DEBUG(SSSDBG_TRACE_INTERNAL,
-           "Searching for [%s] at [%s] with scope=base\n",
--          LOCAL_SIMPLE_FILTER, ldb_dn_get_linearized(dn));
-+          LOCAL_SIMPLE_FILTER, ldb_dn_get_linearized(lc_req->basedn));
- 
--    ret = ldb_search(lctx->ldb, tmp_ctx, &res, dn, LDB_SCOPE_BASE,
-+    ret = ldb_search(lctx->ldb, tmp_ctx, &res, lc_req->basedn, LDB_SCOPE_BASE,
-                      attrs, "%s", LOCAL_SIMPLE_FILTER);
-     if (ret != EOK) {
-         DEBUG(SSSDBG_TRACE_LIBS,
-@@ -278,34 +275,26 @@ done:
- 
- static int local_db_list_keys(TALLOC_CTX *mem_ctx,
-                               struct local_context *lctx,
--                              const char *req_path,
-+                              struct local_db_req *lc_req,
-                               char ***_keys,
-                               int *num_keys)
- {
-     TALLOC_CTX *tmp_ctx;
-     static const char *attrs[] = { "secret", NULL };
-     struct ldb_result *res;
--    struct ldb_dn *dn;
-     char **keys;
-     int ret;
- 
-     tmp_ctx = talloc_new(mem_ctx);
-     if (!tmp_ctx) return ENOMEM;
- 
--    DEBUG(SSSDBG_TRACE_FUNC, "Listing keys at [%s]\n", req_path);
--
--    ret = local_db_dn(tmp_ctx, lctx->ldb, req_path, &dn);
--    if (ret != EOK) {
--        DEBUG(SSSDBG_OP_FAILURE,
--              "local_db_dn failed [%d]: %s\n", ret, sss_strerror(ret));
--        goto done;
--    }
-+    DEBUG(SSSDBG_TRACE_FUNC, "Listing keys at [%s]\n", lc_req->path);
- 
-     DEBUG(SSSDBG_TRACE_INTERNAL,
-           "Searching for [%s] at [%s] with scope=subtree\n",
--          LOCAL_SIMPLE_FILTER, ldb_dn_get_linearized(dn));
-+          LOCAL_SIMPLE_FILTER, ldb_dn_get_linearized(lc_req->basedn));
- 
--    ret = ldb_search(lctx->ldb, tmp_ctx, &res, dn, LDB_SCOPE_SUBTREE,
-+    ret = ldb_search(lctx->ldb, tmp_ctx, &res, lc_req->basedn, LDB_SCOPE_SUBTREE,
-                      attrs, "%s", LOCAL_SIMPLE_FILTER);
-     if (ret != EOK) {
-         DEBUG(SSSDBG_TRACE_LIBS,
-@@ -327,7 +316,7 @@ static int local_db_list_keys(TALLOC_CTX *mem_ctx,
-     }
- 
-     for (unsigned i = 0; i < res->count; i++) {
--        keys[i] = local_dn_to_path(keys, dn, res->msgs[i]->dn);
-+        keys[i] = local_dn_to_path(keys, lc_req->basedn, res->msgs[i]->dn);
-         if (!keys[i]) {
-             ret = ENOMEM;
-             goto done;
-@@ -474,7 +463,7 @@ static int local_check_max_payload_size(struct local_context *lctx,
- 
- static int local_db_put_simple(TALLOC_CTX *mem_ctx,
-                                struct local_context *lctx,
--                               const char *req_path,
-+                               struct local_db_req *lc_req,
-                                const char *secret)
- {
-     struct ldb_message *msg;
-@@ -482,20 +471,14 @@ static int local_db_put_simple(TALLOC_CTX *mem_ctx,
-     char *enc_secret;
-     int ret;
- 
-+    DEBUG(SSSDBG_TRACE_FUNC, "Adding a secret to [%s]\n", lc_req->path);
-+
-     msg = ldb_msg_new(mem_ctx);
-     if (!msg) {
-         ret = ENOMEM;
-         goto done;
-     }
--
--    DEBUG(SSSDBG_TRACE_FUNC, "Adding a secret to [%s]\n", req_path);
--
--    ret = local_db_dn(msg, lctx->ldb, req_path, &msg->dn);
--    if (ret != EOK) {
--        DEBUG(SSSDBG_OP_FAILURE,
--              "local_db_dn failed [%d]: %s\n", ret, sss_strerror(ret));
--        goto done;
--    }
-+    msg->dn = lc_req->basedn;
- 
-     /* make sure containers exist */
-     ret = local_db_check_containers(msg, lctx, msg->dn);
-@@ -585,32 +568,24 @@ done:
- 
- static int local_db_delete(TALLOC_CTX *mem_ctx,
-                            struct local_context *lctx,
--                           const char *req_path)
-+                           struct local_db_req *lc_req)
- {
-     TALLOC_CTX *tmp_ctx;
--    struct ldb_dn *dn;
-     static const char *attrs[] = { NULL };
-     struct ldb_result *res;
-     int ret;
- 
--    DEBUG(SSSDBG_TRACE_FUNC, "Removing a secret from [%s]\n", req_path);
-+    DEBUG(SSSDBG_TRACE_FUNC, "Removing a secret from [%s]\n", lc_req->path);
- 
-     tmp_ctx = talloc_new(mem_ctx);
-     if (!tmp_ctx) return ENOMEM;
- 
--    ret = local_db_dn(mem_ctx, lctx->ldb, req_path, &dn);
--    if (ret != EOK) {
--        DEBUG(SSSDBG_OP_FAILURE,
--              "local_db_dn failed [%d]: %s\n", ret, sss_strerror(ret));
--        goto done;
--    }
--
-     DEBUG(SSSDBG_TRACE_INTERNAL,
-           "Searching for [%s] at [%s] with scope=base\n",
--          LOCAL_CONTAINER_FILTER, ldb_dn_get_linearized(dn));
-+          LOCAL_CONTAINER_FILTER, ldb_dn_get_linearized(lc_req->basedn));
- 
--    ret = ldb_search(lctx->ldb, tmp_ctx, &res, dn, LDB_SCOPE_BASE,
--                    attrs, LOCAL_CONTAINER_FILTER);
-+    ret = ldb_search(lctx->ldb, tmp_ctx, &res, lc_req->basedn, LDB_SCOPE_BASE,
-+                     attrs, LOCAL_CONTAINER_FILTER);
-     if (ret != EOK) {
-         DEBUG(SSSDBG_TRACE_LIBS,
-               "ldb_search returned %d: %s\n", ret, ldb_strerror(ret));
-@@ -619,8 +594,8 @@ static int local_db_delete(TALLOC_CTX *mem_ctx,
- 
-     if (res->count == 1) {
-         DEBUG(SSSDBG_TRACE_INTERNAL,
--              "Searching for children of [%s]\n", ldb_dn_get_linearized(dn));
--        ret = ldb_search(lctx->ldb, tmp_ctx, &res, dn, LDB_SCOPE_ONELEVEL,
-+              "Searching for children of [%s]\n", ldb_dn_get_linearized(lc_req->basedn));
-+        ret = ldb_search(lctx->ldb, tmp_ctx, &res, lc_req->basedn, LDB_SCOPE_ONELEVEL,
-                          attrs, NULL);
-         if (ret != EOK) {
-             DEBUG(SSSDBG_TRACE_LIBS,
-@@ -632,13 +607,13 @@ static int local_db_delete(TALLOC_CTX *mem_ctx,
-             ret = EEXIST;
-             DEBUG(SSSDBG_OP_FAILURE,
-                   "Failed to remove '%s': Container is not empty\n",
--                  ldb_dn_get_linearized(dn));
-+                  ldb_dn_get_linearized(lc_req->basedn));
- 
-             goto done;
-         }
-     }
- 
--    ret = ldb_delete(lctx->ldb, dn);
-+    ret = ldb_delete(lctx->ldb, lc_req->basedn);
-     if (ret != EOK) {
-         DEBUG(SSSDBG_TRACE_LIBS,
-               "ldb_delete returned %d: %s\n", ret, ldb_strerror(ret));
-@@ -653,25 +628,19 @@ done:
- 
- static int local_db_create(TALLOC_CTX *mem_ctx,
-                            struct local_context *lctx,
--                           const char *req_path)
-+                           struct local_db_req *lc_req)
- {
-     struct ldb_message *msg;
-     int ret;
- 
-+    DEBUG(SSSDBG_TRACE_FUNC, "Creating a container at [%s]\n", lc_req->path);
-+
-     msg = ldb_msg_new(mem_ctx);
-     if (!msg) {
-         ret = ENOMEM;
-         goto done;
-     }
--
--    DEBUG(SSSDBG_TRACE_FUNC, "Creating a container at [%s]\n", req_path);
--
--    ret = local_db_dn(msg, lctx->ldb, req_path, &msg->dn);
--    if (ret != EOK) {
--        DEBUG(SSSDBG_OP_FAILURE,
--              "local_db_dn failed [%d]: %s\n", ret, sss_strerror(ret));
--        goto done;
--    }
-+    msg->dn = lc_req->basedn;
- 
-     /* make sure containers exist */
-     ret = local_db_check_containers(msg, lctx, msg->dn);
-@@ -724,10 +693,13 @@ done:
- }
- 
- static int local_secrets_map_path(TALLOC_CTX *mem_ctx,
-+                                  struct ldb_context *ldb,
-                                   struct sec_req_ctx *secreq,
--                                  char **local_db_path)
-+                                  struct local_db_req **_lc_req)
- {
-     int ret;
-+    struct local_db_req *lc_req;
-+    const char *basedn;
- 
-     /* be strict for now */
-     if (secreq->parsed_url.fragment != NULL) {
-@@ -755,20 +727,46 @@ static int local_secrets_map_path(TALLOC_CTX *mem_ctx,
-         }
-     }
- 
--    /* drop SEC_BASEPATH prefix */
--    *local_db_path =
--        talloc_strdup(mem_ctx, &secreq->mapped_path[sizeof(SEC_BASEPATH) - 1]);
--    if (!*local_db_path) {
--        DEBUG(SSSDBG_CRIT_FAILURE,
--              "Failed to map request to local db path\n");
-+    lc_req = talloc(mem_ctx, struct local_db_req);
-+    if (lc_req == NULL) {
-         return ENOMEM;
-     }
- 
--    DEBUG(SSSDBG_TRACE_LIBS, "Local DB path is %s\n", *local_db_path);
--    return EOK;
-+    /* drop the prefix and select a basedn instead */
-+    if (strncmp(secreq->mapped_path,
-+                SEC_BASEPATH, sizeof(SEC_BASEPATH) - 1) == 0) {
-+        lc_req->path = talloc_strdup(lc_req,
-+                                     secreq->mapped_path + (sizeof(SEC_BASEPATH) - 1));
-+        basedn = SECRETS_BASEDN;
-+    } else {
-+        ret = EINVAL;
-+        goto done;
-+    }
-+
-+    if (lc_req->path == NULL) {
-+        DEBUG(SSSDBG_CRIT_FAILURE,
-+              "Failed to map request to local db path\n");
-+        ret = ENOMEM;
-+        goto done;
-+    }
-+
-+    ret = local_db_dn(mem_ctx, ldb, basedn, lc_req->path, &lc_req->basedn);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_CRIT_FAILURE,
-+              "Failed to map request to local db DN\n");
-+        goto done;
-+    }
-+
-+    DEBUG(SSSDBG_TRACE_LIBS, "Local DB path is %s\n", lc_req->path);
-+    ret = EOK;
-+    *_lc_req = lc_req;
-+done:
-+    if (ret != EOK) {
-+        talloc_free(lc_req);
-+    }
-+    return ret;
- }
- 
--
- struct local_secret_state {
-     struct tevent_context *ev;
-     struct sec_req_ctx *secreq;
-@@ -785,7 +783,7 @@ static struct tevent_req *local_secret_req(TALLOC_CTX *mem_ctx,
-     struct sec_data body = { 0 };
-     const char *content_type;
-     bool body_is_json;
--    char *req_path;
-+    struct local_db_req *lc_req;
-     char *secret;
-     char **keys;
-     int nkeys;
-@@ -821,14 +819,14 @@ static struct tevent_req *local_secret_req(TALLOC_CTX *mem_ctx,
-     }
-     DEBUG(SSSDBG_TRACE_LIBS, "Content-Type: %s\n", content_type);
- 
--    ret = local_secrets_map_path(state, secreq, &req_path);
-+    ret = local_secrets_map_path(state, lctx->ldb, secreq, &lc_req);
-     if (ret) goto done;
- 
-     switch (secreq->method) {
-     case HTTP_GET:
--        DEBUG(SSSDBG_TRACE_LIBS, "Processing HTTP GET at [%s]\n", req_path);
--        if (req_path[strlen(req_path) - 1] == '/') {
--            ret = local_db_list_keys(state, lctx, req_path, &keys, &nkeys);
-+        DEBUG(SSSDBG_TRACE_LIBS, "Processing HTTP GET at [%s]\n", lc_req->path);
-+        if (lc_req->path[strlen(lc_req->path) - 1] == '/') {
-+            ret = local_db_list_keys(state, lctx, lc_req, &keys, &nkeys);
-             if (ret) goto done;
- 
-             ret = sec_array_to_json(state, keys, nkeys, &body.data);
-@@ -838,7 +836,7 @@ static struct tevent_req *local_secret_req(TALLOC_CTX *mem_ctx,
-             break;
-         }
- 
--        ret = local_db_get_simple(state, lctx, req_path, &secret);
-+        ret = local_db_get_simple(state, lctx, lc_req, &secret);
-         if (ret) goto done;
- 
-         if (body_is_json) {
-@@ -855,7 +853,7 @@ static struct tevent_req *local_secret_req(TALLOC_CTX *mem_ctx,
-         break;
- 
-     case HTTP_PUT:
--        DEBUG(SSSDBG_TRACE_LIBS, "Processing HTTP PUT at [%s]\n", req_path);
-+        DEBUG(SSSDBG_TRACE_LIBS, "Processing HTTP PUT at [%s]\n", lc_req->path);
-         if (body_is_json) {
-             ret = sec_json_to_simple_secret(state, secreq->body.data,
-                                             &secret);
-@@ -866,27 +864,27 @@ static struct tevent_req *local_secret_req(TALLOC_CTX *mem_ctx,
-         }
-         if (ret) goto done;
- 
--        ret = local_db_put_simple(state, lctx, req_path, secret);
-+        ret = local_db_put_simple(state, lctx, lc_req, secret);
-         if (ret) goto done;
-         break;
- 
-     case HTTP_DELETE:
--        ret = local_db_delete(state, lctx, req_path);
-+        ret = local_db_delete(state, lctx, lc_req);
-         if (ret) goto done;
-         break;
- 
-     case HTTP_POST:
--        DEBUG(SSSDBG_TRACE_LIBS, "Processing HTTP POST at [%s]\n", req_path);
--        plen = strlen(req_path);
-+        DEBUG(SSSDBG_TRACE_LIBS, "Processing HTTP POST at [%s]\n", lc_req->path);
-+        plen = strlen(lc_req->path);
- 
--        if (req_path[plen - 1] != '/') {
-+        if (lc_req->path[plen - 1] != '/') {
-             ret = EINVAL;
-             goto done;
-         }
- 
--        req_path[plen - 1] = '\0';
-+        lc_req->path[plen - 1] = '\0';
- 
--        ret = local_db_create(state, lctx, req_path);
-+        ret = local_db_create(state, lctx, lc_req);
-         if (ret) goto done;
-         break;
- 
--- 
-2.9.3
-
diff --git a/SOURCES/0030-SECRETS-Return-a-nicer-error-message-on-request-with.patch b/SOURCES/0030-SECRETS-Return-a-nicer-error-message-on-request-with.patch
deleted file mode 100644
index 2c596dc..0000000
--- a/SOURCES/0030-SECRETS-Return-a-nicer-error-message-on-request-with.patch
+++ /dev/null
@@ -1,40 +0,0 @@
-From ddda18a37ac4b732ad109dbb129255dc3edd8fbb Mon Sep 17 00:00:00 2001
-From: Jakub Hrozek <jhrozek@redhat.com>
-Date: Fri, 3 Feb 2017 14:33:47 +0100
-Subject: [PATCH 30/36] SECRETS: Return a nicer error message on request with
- no PUT data
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-I managed to create this pathological situation with the tcurl tool
-which didn't send any PUT data. The error in sssd-secrets was quite
-strange (ENOMEM). This patch just adds a safeguard sooner so that we
-return a graceful error.
-
-Reviewed-by: Michal Židek <mzidek@redhat.com>
-Reviewed-by: Simo Sorce <simo@redhat.com>
----
- src/responder/secrets/local.c | 6 ++++++
- 1 file changed, 6 insertions(+)
-
-diff --git a/src/responder/secrets/local.c b/src/responder/secrets/local.c
-index 9dcdd9925e542499d3a962b4998103b07c26a5ab..26c97a2849febbf0ac482d526cf927bfc103b4f2 100644
---- a/src/responder/secrets/local.c
-+++ b/src/responder/secrets/local.c
-@@ -853,6 +853,12 @@ static struct tevent_req *local_secret_req(TALLOC_CTX *mem_ctx,
-         break;
- 
-     case HTTP_PUT:
-+        if (secreq->body.length == 0) {
-+            DEBUG(SSSDBG_OP_FAILURE, "PUT with no data\n");
-+            ret = EINVAL;
-+            goto done;
-+        }
-+
-         DEBUG(SSSDBG_TRACE_LIBS, "Processing HTTP PUT at [%s]\n", lc_req->path);
-         if (body_is_json) {
-             ret = sec_json_to_simple_secret(state, secreq->body.data,
--- 
-2.9.3
-
diff --git a/SOURCES/0030-nss-idmap-add-timeout-version-of-old-sss_nss_-calls.patch b/SOURCES/0030-nss-idmap-add-timeout-version-of-old-sss_nss_-calls.patch
new file mode 100644
index 0000000..caf0dc0
--- /dev/null
+++ b/SOURCES/0030-nss-idmap-add-timeout-version-of-old-sss_nss_-calls.patch
@@ -0,0 +1,494 @@
+From a12e6ac8001025174cf201bcaa2143edb1b0c017 Mon Sep 17 00:00:00 2001
+From: Sumit Bose <sbose@redhat.com>
+Date: Thu, 2 Nov 2017 10:32:41 +0100
+Subject: [PATCH 30/31] nss-idmap: add timeout version of old sss_nss_* calls
+
+Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
+(cherry picked from commit e54db68cbb9c12d8a6867f2c7766fb2115ab0997)
+---
+ Makefile.am                                   |   2 +-
+ src/sss_client/idmap/sss_nss_idmap.c          | 126 ++++++++++++++++++--------
+ src/sss_client/idmap/sss_nss_idmap.exports    |   7 ++
+ src/sss_client/idmap/sss_nss_idmap.h          | 124 +++++++++++++++++++++++++
+ src/sss_client/idmap/sss_nss_idmap.unit_tests |   2 +-
+ src/tests/cmocka/sss_nss_idmap-tests.c        |  13 +--
+ 6 files changed, 229 insertions(+), 45 deletions(-)
+
+diff --git a/Makefile.am b/Makefile.am
+index dd25d1f7ea1be66388aa1b393bac290c4d7501a2..286ba47e3c421864362717be5258de960efca9f2 100644
+--- a/Makefile.am
++++ b/Makefile.am
+@@ -2974,7 +2974,6 @@ test_sysdb_domain_resolution_order_LDADD = \
+ 
+ test_wbc_calls_SOURCES = \
+     src/tests/cmocka/test_wbc_calls.c \
+-    src/sss_client/idmap/sss_nss_idmap.c \
+     src/sss_client/libwbclient/wbc_sid_sssd.c \
+     src/sss_client/libwbclient/wbclient_common.c \
+     src/sss_client/libwbclient/wbc_sid_common.c \
+@@ -2993,6 +2992,7 @@ test_wbc_calls_LDADD = \
+     $(TALLOC_LIBS) \
+     $(SSSD_INTERNAL_LTLIBS) \
+     libsss_test_common.la \
++    libsss_nss_idmap.la \
+     $(NULL)
+ 
+ test_be_ptask_SOURCES = \
+diff --git a/src/sss_client/idmap/sss_nss_idmap.c b/src/sss_client/idmap/sss_nss_idmap.c
+index 6f3af267a1e763e7dce77e3862be377ae2bfe984..6e7685d2b1d80956b6a6668e9bbb146abd9e86ed 100644
+--- a/src/sss_client/idmap/sss_nss_idmap.c
++++ b/src/sss_client/idmap/sss_nss_idmap.c
+@@ -28,10 +28,13 @@
+ 
+ #include "sss_client/sss_cli.h"
+ #include "sss_client/idmap/sss_nss_idmap.h"
++#include "sss_client/idmap/sss_nss_idmap_private.h"
+ #include "util/strtonum.h"
+ 
+ #define DATA_START (3 * sizeof(uint32_t))
+ #define LIST_START (2 * sizeof(uint32_t))
++#define NO_TIMEOUT ((unsigned int) -1)
++
+ union input {
+     const char *str;
+     uint32_t id;
+@@ -198,8 +201,8 @@ done:
+     return ret;
+ }
+ 
+-static int sss_nss_getyyybyxxx(union input inp, enum sss_cli_command cmd ,
+-                               struct output *out)
++static int sss_nss_getyyybyxxx(union input inp, enum sss_cli_command cmd,
++                               unsigned int timeout, struct output *out)
+ {
+     int ret;
+     size_t inp_len;
+@@ -215,6 +218,7 @@ static int sss_nss_getyyybyxxx(union input inp, enum sss_cli_command cmd ,
+     struct sss_nss_kv *kv_list;
+     char **names;
+     enum sss_id_type *types;
++    int time_left = SSS_CLI_SOCKET_TIMEOUT;
+ 
+     switch (cmd) {
+     case SSS_NSS_GETSIDBYNAME:
+@@ -250,9 +254,14 @@ static int sss_nss_getyyybyxxx(union input inp, enum sss_cli_command cmd ,
+         return EINVAL;
+     }
+ 
+-    sss_nss_lock();
++    if (timeout == NO_TIMEOUT) {
++        sss_nss_lock();
++    } else {
++        sss_nss_timedlock(timeout, &time_left);
++    }
+ 
+-    nret = sss_nss_make_request(cmd, &rd, &repbuf, &replen, &errnop);
++    nret = sss_nss_make_request_timeout(cmd, &rd, time_left, &repbuf, &replen,
++                                        &errnop);
+     if (nret != NSS_STATUS_SUCCESS) {
+         ret = nss_status_to_errno(nret);
+         goto done;
+@@ -347,8 +356,8 @@ done:
+     return ret;
+ }
+ 
+-int sss_nss_getsidbyname(const char *fq_name, char **sid,
+-                         enum sss_id_type *type)
++int sss_nss_getsidbyname_timeout(const char *fq_name, unsigned int timeout,
++                                 char **sid, enum sss_id_type *type)
+ {
+     int ret;
+     union input inp;
+@@ -360,7 +369,7 @@ int sss_nss_getsidbyname(const char *fq_name, char **sid,
+ 
+     inp.str = fq_name;
+ 
+-    ret = sss_nss_getyyybyxxx(inp, SSS_NSS_GETSIDBYNAME, &out);
++    ret = sss_nss_getyyybyxxx(inp, SSS_NSS_GETSIDBYNAME, timeout, &out);
+     if (ret == EOK) {
+         *sid = out.d.str;
+         *type = out.type;
+@@ -369,7 +378,14 @@ int sss_nss_getsidbyname(const char *fq_name, char **sid,
+     return ret;
+ }
+ 
+-int sss_nss_getsidbyid(uint32_t id, char **sid, enum sss_id_type *type)
++int sss_nss_getsidbyname(const char *fq_name, char **sid,
++                         enum sss_id_type *type)
++{
++    return sss_nss_getsidbyname_timeout(fq_name, NO_TIMEOUT, sid, type);
++}
++
++int sss_nss_getsidbyid_timeout(uint32_t id, unsigned int timeout,
++                               char **sid, enum sss_id_type *type)
+ {
+     int ret;
+     union input inp;
+@@ -381,7 +397,7 @@ int sss_nss_getsidbyid(uint32_t id, char **sid, enum sss_id_type *type)
+ 
+     inp.id = id;
+ 
+-    ret = sss_nss_getyyybyxxx(inp, SSS_NSS_GETSIDBYID, &out);
++    ret = sss_nss_getyyybyxxx(inp, SSS_NSS_GETSIDBYID, timeout, &out);
+     if (ret == EOK) {
+         *sid = out.d.str;
+         *type = out.type;
+@@ -390,8 +406,13 @@ int sss_nss_getsidbyid(uint32_t id, char **sid, enum sss_id_type *type)
+     return ret;
+ }
+ 
+-int sss_nss_getnamebysid(const char *sid, char **fq_name,
+-                         enum sss_id_type *type)
++int sss_nss_getsidbyid(uint32_t id, char **sid, enum sss_id_type *type)
++{
++    return sss_nss_getsidbyid_timeout(id, NO_TIMEOUT, sid, type);
++}
++
++int sss_nss_getnamebysid_timeout(const char *sid, unsigned int timeout,
++                                 char **fq_name, enum sss_id_type *type)
+ {
+     int ret;
+     union input inp;
+@@ -403,7 +424,7 @@ int sss_nss_getnamebysid(const char *sid, char **fq_name,
+ 
+     inp.str = sid;
+ 
+-    ret = sss_nss_getyyybyxxx(inp, SSS_NSS_GETNAMEBYSID, &out);
++    ret = sss_nss_getyyybyxxx(inp, SSS_NSS_GETNAMEBYSID, timeout, &out);
+     if (ret == EOK) {
+         *fq_name = out.d.str;
+         *type = out.type;
+@@ -412,7 +433,14 @@ int sss_nss_getnamebysid(const char *sid, char **fq_name,
+     return ret;
+ }
+ 
+-int sss_nss_getidbysid(const char *sid, uint32_t *id, enum sss_id_type *id_type)
++int sss_nss_getnamebysid(const char *sid, char **fq_name,
++                         enum sss_id_type *type)
++{
++    return sss_nss_getnamebysid_timeout(sid, NO_TIMEOUT, fq_name, type);
++}
++
++int sss_nss_getidbysid_timeout(const char *sid, unsigned int timeout,
++                               uint32_t *id, enum sss_id_type *id_type)
+ {
+     int ret;
+     union input inp;
+@@ -424,7 +452,7 @@ int sss_nss_getidbysid(const char *sid, uint32_t *id, enum sss_id_type *id_type)
+ 
+     inp.str = sid;
+ 
+-    ret = sss_nss_getyyybyxxx(inp, SSS_NSS_GETIDBYSID, &out);
++    ret = sss_nss_getyyybyxxx(inp, SSS_NSS_GETIDBYSID, timeout, &out);
+     if (ret == EOK) {
+         *id = out.d.id;
+         *id_type = out.type;
+@@ -433,8 +461,14 @@ int sss_nss_getidbysid(const char *sid, uint32_t *id, enum sss_id_type *id_type)
+     return ret;
+ }
+ 
+-int sss_nss_getorigbyname(const char *fq_name, struct sss_nss_kv **kv_list,
+-                         enum sss_id_type *type)
++int sss_nss_getidbysid(const char *sid, uint32_t *id, enum sss_id_type *id_type)
++{
++    return sss_nss_getidbysid_timeout(sid, NO_TIMEOUT, id, id_type);
++}
++
++int sss_nss_getorigbyname_timeout(const char *fq_name, unsigned int timeout,
++                                  struct sss_nss_kv **kv_list,
++                                  enum sss_id_type *type)
+ {
+     int ret;
+     union input inp;
+@@ -446,7 +480,7 @@ int sss_nss_getorigbyname(const char *fq_name, struct sss_nss_kv **kv_list,
+ 
+     inp.str = fq_name;
+ 
+-    ret = sss_nss_getyyybyxxx(inp, SSS_NSS_GETORIGBYNAME, &out);
++    ret = sss_nss_getyyybyxxx(inp, SSS_NSS_GETORIGBYNAME, timeout, &out);
+     if (ret == EOK) {
+         *kv_list = out.d.kv_list;
+         *type = out.type;
+@@ -455,30 +489,42 @@ int sss_nss_getorigbyname(const char *fq_name, struct sss_nss_kv **kv_list,
+     return ret;
+ }
+ 
++int sss_nss_getorigbyname(const char *fq_name, struct sss_nss_kv **kv_list,
++                          enum sss_id_type *type)
++{
++    return sss_nss_getorigbyname_timeout(fq_name, NO_TIMEOUT, kv_list, type);
++}
++
++int sss_nss_getnamebycert_timeout(const char *cert, unsigned int timeout,
++                                  char **fq_name, enum sss_id_type *type)
++{
++    int ret;
++    union input inp;
++    struct output out;
++
++    if (fq_name == NULL || cert == NULL || *cert == '\0') {
++        return EINVAL;
++    }
++
++    inp.str = cert;
++
++    ret = sss_nss_getyyybyxxx(inp, SSS_NSS_GETNAMEBYCERT, timeout, &out);
++    if (ret == EOK) {
++        *fq_name = out.d.str;
++        *type = out.type;
++    }
++
++    return ret;
++}
++
+ int sss_nss_getnamebycert(const char *cert, char **fq_name,
+                           enum sss_id_type *type)
+ {
+-    int ret;
+-    union input inp;
+-    struct output out;
+-
+-    if (fq_name == NULL || cert == NULL || *cert == '\0') {
+-        return EINVAL;
+-    }
+-
+-    inp.str = cert;
+-
+-    ret = sss_nss_getyyybyxxx(inp, SSS_NSS_GETNAMEBYCERT, &out);
+-    if (ret == EOK) {
+-        *fq_name = out.d.str;
+-        *type = out.type;
+-    }
+-
+-    return ret;
++    return sss_nss_getnamebycert_timeout(cert, NO_TIMEOUT, fq_name, type);
+ }
+ 
+-int sss_nss_getlistbycert(const char *cert, char ***fq_name,
+-                          enum sss_id_type **type)
++int sss_nss_getlistbycert_timeout(const char *cert, unsigned int timeout,
++                                  char ***fq_name, enum sss_id_type **type)
+ {
+     int ret;
+     union input inp;
+@@ -490,7 +536,7 @@ int sss_nss_getlistbycert(const char *cert, char ***fq_name,
+ 
+     inp.str = cert;
+ 
+-    ret = sss_nss_getyyybyxxx(inp, SSS_NSS_GETLISTBYCERT, &out);
++    ret = sss_nss_getyyybyxxx(inp, SSS_NSS_GETLISTBYCERT, timeout, &out);
+     if (ret == EOK) {
+         *fq_name = out.d.names;
+         *type = out.types;
+@@ -498,3 +544,9 @@ int sss_nss_getlistbycert(const char *cert, char ***fq_name,
+ 
+     return ret;
+ }
++
++int sss_nss_getlistbycert(const char *cert, char ***fq_name,
++                          enum sss_id_type **type)
++{
++    return sss_nss_getlistbycert_timeout(cert, NO_TIMEOUT, fq_name, type);
++}
+diff --git a/src/sss_client/idmap/sss_nss_idmap.exports b/src/sss_client/idmap/sss_nss_idmap.exports
+index 788d05ecc3bd56fa88e68a98b9c8096cf7140a09..8d0a24f42aa3fb3dd9c2ed125bf79e2c7792993f 100644
+--- a/src/sss_client/idmap/sss_nss_idmap.exports
++++ b/src/sss_client/idmap/sss_nss_idmap.exports
+@@ -40,4 +40,11 @@ SSS_NSS_IDMAP_0.4.0 {
+         sss_nss_getgrnam_timeout;
+         sss_nss_getgrgid_timeout;
+         sss_nss_getgrouplist_timeout;
++        sss_nss_getsidbyname_timeout;
++        sss_nss_getsidbyid_timeout;
++        sss_nss_getnamebysid_timeout;
++        sss_nss_getidbysid_timeout;
++        sss_nss_getorigbyname_timeout;
++        sss_nss_getnamebycert_timeout;
++        sss_nss_getlistbycert_timeout;
+ } SSS_NSS_IDMAP_0.3.0;
+diff --git a/src/sss_client/idmap/sss_nss_idmap.h b/src/sss_client/idmap/sss_nss_idmap.h
+index 3755643312f05a31d1cf1aa76dfc22848ef1e3ec..125e72a6486f5916f90d37f27e1743d181bfa3e5 100644
+--- a/src/sss_client/idmap/sss_nss_idmap.h
++++ b/src/sss_client/idmap/sss_nss_idmap.h
+@@ -303,5 +303,129 @@ int sss_nss_getgrgid_timeout(gid_t gid, struct group *grp,
+ int sss_nss_getgrouplist_timeout(const char *name, gid_t group,
+                                  gid_t *groups, int *ngroups,
+                                  uint32_t flags, unsigned int timeout);
++/**
++ * @brief Find SID by fully qualified name with timeout
++ *
++ * @param[in] fq_name  Fully qualified name of a user or a group
++ * @param[in] timeout  timeout in milliseconds
++ * @param[out] sid     String representation of the SID of the requested user
++ *                     or group, must be freed by the caller
++ * @param[out] type    Type of the object related to the given name
++ *
++ * @return
++ *  - 0 (EOK): success, sid contains the requested SID
++ *  - ENOENT: requested object was not found in the domain extracted from the given name
++ *  - ENETUNREACH: SSSD does not know how to handle the domain extracted from the given name
++ *  - ENOSYS: this call is not supported by the configured provider
++ *  - EINVAL: input cannot be parsed
++ *  - EIO: remote servers cannot be reached
++ *  - EFAULT: any other error
++ *  - ETIME:     request timed out but was send to SSSD
++ *  - ETIMEDOUT: request timed out but was not send to SSSD
++ */
++int sss_nss_getsidbyname_timeout(const char *fq_name, unsigned int timeout,
++                                 char **sid, enum sss_id_type *type);
++
++/**
++ * @brief Find SID by a POSIX UID or GID with timeout
++ *
++ * @param[in] id       POSIX UID or GID
++ * @param[in] timeout  timeout in milliseconds
++ * @param[out] sid     String representation of the SID of the requested user
++ *                     or group, must be freed by the caller
++ * @param[out] type    Type of the object related to the given ID
++ *
++ * @return
++ *  - see #sss_nss_getsidbyname_timeout
++ */
++int sss_nss_getsidbyid_timeout(uint32_t id, unsigned int timeout,
++                               char **sid, enum sss_id_type *type);
++
++/**
++ * @brief Return the fully qualified name for the given SID with timeout
++ *
++ * @param[in] sid      String representation of the SID
++ * @param[in] timeout  timeout in milliseconds
++ * @param[out] fq_name Fully qualified name of a user or a group,
++ *                     must be freed by the caller
++ * @param[out] type    Type of the object related to the SID
++ *
++ * @return
++ *  - see #sss_nss_getsidbyname_timeout
++ */
++int sss_nss_getnamebysid_timeout(const char *sid, unsigned int timeout,
++                                 char **fq_name, enum sss_id_type *type);
++
++/**
++ * @brief Return the POSIX ID for the given SID with timeout
++ *
++ * @param[in] sid      String representation of the SID
++ * @param[in] timeout  timeout in milliseconds
++ * @param[out] id      POSIX ID related to the SID
++ * @param[out] id_type Type of the object related to the SID
++ *
++ * @return
++ *  - see #sss_nss_getsidbyname_timeout
++ */
++int sss_nss_getidbysid_timeout(const char *sid, unsigned int timeout,
++                               uint32_t *id, enum sss_id_type *id_type);
++
++/**
++ * @brief Find original data by fully qualified name with timeout
++ *
++ * @param[in] fq_name  Fully qualified name of a user or a group
++ * @param[in] timeout  timeout in milliseconds
++ * @param[out] kv_list A NULL terminate list of key-value pairs where the key
++ *                     is the attribute name in the cache of SSSD,
++ *                     must be freed by the caller with sss_nss_free_kv()
++ * @param[out] type    Type of the object related to the given name
++ *
++ * @return
++ *  - 0 (EOK): success, sid contains the requested SID
++ *  - ENOENT: requested object was not found in the domain extracted from the given name
++ *  - ENETUNREACH: SSSD does not know how to handle the domain extracted from the given name
++ *  - ENOSYS: this call is not supported by the configured provider
++ *  - EINVAL: input cannot be parsed
++ *  - EIO: remote servers cannot be reached
++ *  - EFAULT: any other error
++ *  - ETIME:     request timed out but was send to SSSD
++ *  - ETIMEDOUT: request timed out but was not send to SSSD
++ */
++int sss_nss_getorigbyname_timeout(const char *fq_name, unsigned int timeout,
++                                  struct sss_nss_kv **kv_list,
++                                  enum sss_id_type *type);
++
++/**
++ * @brief Return the fully qualified name for the given base64 encoded
++ * X.509 certificate in DER format with timeout
++ *
++ * @param[in] cert     base64 encoded certificate
++ * @param[in] timeout  timeout in milliseconds
++ * @param[out] fq_name Fully qualified name of a user or a group,
++ *                     must be freed by the caller
++ * @param[out] type    Type of the object related to the cert
++ *
++ * @return
++ *  - see #sss_nss_getsidbyname_timeout
++ */
++int sss_nss_getnamebycert_timeout(const char *cert, unsigned int timeout,
++                                  char **fq_name, enum sss_id_type *type);
++
++/**
++ * @brief Return a list of fully qualified names for the given base64 encoded
++ * X.509 certificate in DER format with timeout
++ *
++ * @param[in] cert     base64 encoded certificate
++ * @param[in] timeout  timeout in milliseconds
++ * @param[out] fq_name List of fully qualified name of users or groups,
++ *                     must be freed by the caller
++ * @param[out] type    List of types of the objects related to the cert
++ *
++ * @return
++ *  - see #sss_nss_getsidbyname_timeout
++ */
++int sss_nss_getlistbycert_timeout(const char *cert, unsigned int timeout,
++                                  char ***fq_name, enum sss_id_type **type);
++
+ #endif /* IPA_389DS_PLUGIN_HELPER_CALLS */
+ #endif /* SSS_NSS_IDMAP_H_ */
+diff --git a/src/sss_client/idmap/sss_nss_idmap.unit_tests b/src/sss_client/idmap/sss_nss_idmap.unit_tests
+index 361cc3b134ead52cf458afe27c055739d6728441..05c474f008e1d59aae5976acfd81613c3c3e6540 100644
+--- a/src/sss_client/idmap/sss_nss_idmap.unit_tests
++++ b/src/sss_client/idmap/sss_nss_idmap.unit_tests
+@@ -2,5 +2,5 @@
+ UNIT_TEST_ONLY {
+     # should not be part of installed library
+     global:
+-        sss_nss_make_request;
++        sss_nss_make_request_timeout;
+ };
+diff --git a/src/tests/cmocka/sss_nss_idmap-tests.c b/src/tests/cmocka/sss_nss_idmap-tests.c
+index 8807eca619d7b07d919168e5629042cf38f654ac..2e37040d2d3523bea157804706685fa0b36df16a 100644
+--- a/src/tests/cmocka/sss_nss_idmap-tests.c
++++ b/src/tests/cmocka/sss_nss_idmap-tests.c
+@@ -61,10 +61,11 @@ uint8_t buf_orig1[] = {0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0
+  #error "unknow endianess"
+ #endif
+ 
+-enum nss_status sss_nss_make_request(enum sss_cli_command cmd,
+-                      struct sss_cli_req_data *rd,
+-                      uint8_t **repbuf, size_t *replen,
+-                      int *errnop)
++enum nss_status sss_nss_make_request_timeout(enum sss_cli_command cmd,
++                                             struct sss_cli_req_data *rd,
++                                             int timeout,
++                                             uint8_t **repbuf, size_t *replen,
++                                             int *errnop)
+ {
+     struct sss_nss_make_request_test_data *d;
+ 
+@@ -114,7 +115,7 @@ void test_getsidbyname(void **state)
+     sid = NULL;
+ 
+     for (c = 0; d[c].d.repbuf != NULL; c++) {
+-        will_return(sss_nss_make_request, &d[0].d);
++        will_return(sss_nss_make_request_timeout, &d[0].d);
+ 
+         ret = sss_nss_getsidbyname("test", &sid, &type);
+         assert_int_equal(ret, d[0].ret);
+@@ -134,7 +135,7 @@ void test_getorigbyname(void **state)
+     enum sss_id_type type;
+     struct sss_nss_make_request_test_data d = {buf_orig1, sizeof(buf_orig1), 0, NSS_STATUS_SUCCESS};
+ 
+-    will_return(sss_nss_make_request, &d);
++    will_return(sss_nss_make_request_timeout, &d);
+     ret = sss_nss_getorigbyname("test", &kv_list, &type);
+     assert_int_equal(ret, EOK);
+     assert_int_equal(type, SSS_ID_TYPE_UID);
+-- 
+2.13.6
+
diff --git a/SOURCES/0031-SECRETS-Store-ccaches-in-secrets-for-the-KCM-respond.patch b/SOURCES/0031-SECRETS-Store-ccaches-in-secrets-for-the-KCM-respond.patch
deleted file mode 100644
index 09d388a..0000000
--- a/SOURCES/0031-SECRETS-Store-ccaches-in-secrets-for-the-KCM-respond.patch
+++ /dev/null
@@ -1,206 +0,0 @@
-From 91c099a993252680f103084431b1d0f5798d8a24 Mon Sep 17 00:00:00 2001
-From: Jakub Hrozek <jhrozek@redhat.com>
-Date: Tue, 21 Mar 2017 14:14:42 +0100
-Subject: [PATCH 31/36] SECRETS: Store ccaches in secrets for the KCM responder
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Adds a new "hive" to the secrets responder whose base path is /kcm. Only
-root can contact the /kcm hive, because the KCM responder only runs as
-root and it must impersonate other users and store ccaches on their behalf.
-
-Reviewed-by: Michal Židek <mzidek@redhat.com>
-Reviewed-by: Simo Sorce <simo@redhat.com>
----
- src/responder/secrets/local.c          | 16 +++++++-
- src/responder/secrets/providers.c      | 71 ++++++++++++++++++++++++++++++----
- src/responder/secrets/secsrv_private.h | 10 ++++-
- 3 files changed, 86 insertions(+), 11 deletions(-)
-
-diff --git a/src/responder/secrets/local.c b/src/responder/secrets/local.c
-index 26c97a2849febbf0ac482d526cf927bfc103b4f2..02007ada8b673071ecba033df0eb3f81af93fcbd 100644
---- a/src/responder/secrets/local.c
-+++ b/src/responder/secrets/local.c
-@@ -26,6 +26,9 @@
- 
- #define MKEY_SIZE (256 / 8)
- 
-+#define SECRETS_BASEDN  "cn=secrets"
-+#define KCM_BASEDN      "cn=kcm"
-+
- struct local_context {
-     struct ldb_context *ldb;
-     struct sec_data master_key;
-@@ -119,6 +122,7 @@ static int local_encrypt(struct local_context *lctx, TALLOC_CTX *mem_ctx,
- 
- static int local_db_dn(TALLOC_CTX *mem_ctx,
-                        struct ldb_context *ldb,
-+                       const char *basedn,
-                        const char *req_path,
-                        struct ldb_dn **req_dn)
- {
-@@ -126,7 +130,7 @@ static int local_db_dn(TALLOC_CTX *mem_ctx,
-     const char *s, *e;
-     int ret;
- 
--    dn = ldb_dn_new(mem_ctx, ldb, "cn=secrets");
-+    dn = ldb_dn_new(mem_ctx, ldb, basedn);
-     if (!dn) {
-         ret = ENOMEM;
-         goto done;
-@@ -738,6 +742,11 @@ static int local_secrets_map_path(TALLOC_CTX *mem_ctx,
-         lc_req->path = talloc_strdup(lc_req,
-                                      secreq->mapped_path + (sizeof(SEC_BASEPATH) - 1));
-         basedn = SECRETS_BASEDN;
-+    } else if (strncmp(secreq->mapped_path,
-+               SEC_KCM_BASEPATH, sizeof(SEC_KCM_BASEPATH) - 1) == 0) {
-+        lc_req->path = talloc_strdup(lc_req,
-+                                     secreq->mapped_path + (sizeof(SEC_KCM_BASEPATH) - 1));
-+        basedn = KCM_BASEDN;
-     } else {
-         ret = EINVAL;
-         goto done;
-@@ -820,7 +829,10 @@ static struct tevent_req *local_secret_req(TALLOC_CTX *mem_ctx,
-     DEBUG(SSSDBG_TRACE_LIBS, "Content-Type: %s\n", content_type);
- 
-     ret = local_secrets_map_path(state, lctx->ldb, secreq, &lc_req);
--    if (ret) goto done;
-+    if (ret) {
-+        DEBUG(SSSDBG_OP_FAILURE, "Cannot map request path to local path\n");
-+        goto done;
-+    }
- 
-     switch (secreq->method) {
-     case HTTP_GET:
-diff --git a/src/responder/secrets/providers.c b/src/responder/secrets/providers.c
-index eba555d2e422d08db211979422a2957e48b51589..94831c73036d269addca45c0117811a2c68873fd 100644
---- a/src/responder/secrets/providers.c
-+++ b/src/responder/secrets/providers.c
-@@ -24,6 +24,14 @@
- #include "responder/secrets/secsrv_proxy.h"
- #include <jansson.h>
- 
-+typedef int (*url_mapper_fn)(struct sec_req_ctx *secreq,
-+                             char **mapped_path);
-+
-+struct url_pfx_router {
-+    const char *prefix;
-+    url_mapper_fn mapper_fn;
-+};
-+
- static int sec_map_url_to_user_path(struct sec_req_ctx *secreq,
-                                     char **mapped_path)
- {
-@@ -42,10 +50,43 @@ static int sec_map_url_to_user_path(struct sec_req_ctx *secreq,
-         return ENOMEM;
-     }
- 
--    DEBUG(SSSDBG_TRACE_LIBS, "User-specific path is [%s]\n", *mapped_path);
-+    DEBUG(SSSDBG_TRACE_LIBS,
-+          "User-specific secrets path is [%s]\n", *mapped_path);
-     return EOK;
- }
- 
-+static int kcm_map_url_to_path(struct sec_req_ctx *secreq,
-+                               char **mapped_path)
-+{
-+    uid_t c_euid;
-+
-+    c_euid = client_euid(secreq->cctx->creds);
-+    if (c_euid != KCM_PEER_UID) {
-+        DEBUG(SSSDBG_CRIT_FAILURE,
-+              "UID %"SPRIuid" is not allowed to access "
-+              "the "SEC_KCM_BASEPATH" hive\n",
-+              c_euid);
-+        return EPERM;
-+    }
-+
-+    *mapped_path = talloc_strdup(secreq, secreq->parsed_url.path );
-+    if (!*mapped_path) {
-+        DEBUG(SSSDBG_CRIT_FAILURE,
-+              "Failed to map request to user specific url\n");
-+        return ENOMEM;
-+    }
-+
-+    DEBUG(SSSDBG_TRACE_LIBS,
-+          "User-specific KCM path is [%s]\n", *mapped_path);
-+    return EOK;
-+}
-+
-+static struct url_pfx_router secrets_url_mapping[] = {
-+    { SEC_BASEPATH, sec_map_url_to_user_path },
-+    { SEC_KCM_BASEPATH, kcm_map_url_to_path },
-+    { NULL, NULL },
-+};
-+
- int sec_req_routing(TALLOC_CTX *mem_ctx, struct sec_req_ctx *secreq,
-                     struct provider_handle **handle)
- {
-@@ -55,21 +96,35 @@ int sec_req_routing(TALLOC_CTX *mem_ctx, struct sec_req_ctx *secreq,
-     char *provider;
-     int num_sections;
-     int ret;
-+    url_mapper_fn mapper_fn = NULL;
- 
-     sctx = talloc_get_type(secreq->cctx->rctx->pvt_ctx, struct sec_ctx);
- 
--    /* patch must start with /secrets/ for now */
--    ret = strncasecmp(secreq->parsed_url.path,
--                      SEC_BASEPATH, sizeof(SEC_BASEPATH) - 1);
--    if (ret != 0) {
-+    for (int i = 0; secrets_url_mapping[i].prefix != NULL; i++) {
-+        if (strncasecmp(secreq->parsed_url.path,
-+                        secrets_url_mapping[i].prefix,
-+                        strlen(secrets_url_mapping[i].prefix)) == 0) {
-+            DEBUG(SSSDBG_TRACE_LIBS,
-+                  "Mapping prefix %s\n", secrets_url_mapping[i].prefix);
-+            mapper_fn = secrets_url_mapping[i].mapper_fn;
-+            break;
-+        }
-+    }
-+
-+    if (mapper_fn == NULL) {
-         DEBUG(SSSDBG_CRIT_FAILURE,
--              "Path [%s] does not start with "SEC_BASEPATH"\n",
-+              "Path [%s] does not start with any allowed prefix\n",
-               secreq->parsed_url.path);
-         return EPERM;
-     }
- 
--    ret = sec_map_url_to_user_path(secreq, &secreq->mapped_path);
--    if (ret) return ret;
-+    ret = mapper_fn(secreq, &secreq->mapped_path);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_CRIT_FAILURE,
-+              "Failed to map the user path [%d]: %s\n",
-+              ret, sss_strerror(ret));
-+        return ret;
-+    }
- 
-     /* source default provider */
-     ret = confdb_get_string(secreq->cctx->rctx->cdb, mem_ctx,
-diff --git a/src/responder/secrets/secsrv_private.h b/src/responder/secrets/secsrv_private.h
-index 1c3fbd8eadb237551233f048503ddc01b4ba00ae..a8544f656517a17fe4576247779bff4850beaf97 100644
---- a/src/responder/secrets/secsrv_private.h
-+++ b/src/responder/secrets/secsrv_private.h
-@@ -101,7 +101,15 @@ int sec_get_provider(struct sec_ctx *sctx, const char *name,
-                      struct provider_handle **out_handle);
- int sec_add_provider(struct sec_ctx *sctx, struct provider_handle *handle);
- 
--#define SEC_BASEPATH "/secrets/"
-+#define SEC_BASEPATH            "/secrets/"
-+#define SEC_KCM_BASEPATH        "/kcm/"
-+
-+/* The KCM responder must "impersonate" the owner of the credentials.
-+ * Only a trusted UID can do that -- root by default, but unit
-+ * tests might choose otherwise */
-+#ifndef KCM_PEER_UID
-+#define KCM_PEER_UID            0
-+#endif /* KCM_PEER_UID */
- 
- /* providers.c */
- int sec_req_routing(TALLOC_CTX *mem_ctx, struct sec_req_ctx *secreq,
--- 
-2.9.3
-
diff --git a/SOURCES/0031-nss-idmap-allow-empty-buffer-with-SSS_NSS_EX_FLAG_IN.patch b/SOURCES/0031-nss-idmap-allow-empty-buffer-with-SSS_NSS_EX_FLAG_IN.patch
new file mode 100644
index 0000000..7724a87
--- /dev/null
+++ b/SOURCES/0031-nss-idmap-allow-empty-buffer-with-SSS_NSS_EX_FLAG_IN.patch
@@ -0,0 +1,184 @@
+From 464a19ecef7c4a0aad22cd9d2c7b2364e3680351 Mon Sep 17 00:00:00 2001
+From: Sumit Bose <sbose@redhat.com>
+Date: Thu, 2 Nov 2017 11:09:20 +0100
+Subject: [PATCH 31/31] nss-idmap: allow empty buffer with
+ SSS_NSS_EX_FLAG_INVALIDATE_CACHE
+
+Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
+(cherry picked from commit 859bddc2bf51dc426a3dc56bd9f365e9c5722b65)
+---
+ src/sss_client/idmap/sss_nss_ex.c | 89 ++++++++++++++++++++++++++-------------
+ 1 file changed, 60 insertions(+), 29 deletions(-)
+
+diff --git a/src/sss_client/idmap/sss_nss_ex.c b/src/sss_client/idmap/sss_nss_ex.c
+index 148eb7b35ec236b6272dd203a0035399cfdef73d..dcd9619a8b07ced7498f61b7e809fa46ebffe09e 100644
+--- a/src/sss_client/idmap/sss_nss_ex.c
++++ b/src/sss_client/idmap/sss_nss_ex.c
+@@ -103,8 +103,11 @@ errno_t sss_nss_mc_get(struct nss_input *inp)
+     }
+ }
+ 
+-static int check_flags(uint32_t flags)
++static int check_flags(struct nss_input *inp, uint32_t flags,
++                       bool *skip_mc, bool *skip_data)
+ {
++    bool no_data = false;
++
+     /* SSS_NSS_EX_FLAG_NO_CACHE and SSS_NSS_EX_FLAG_INVALIDATE_CACHE are
+      * mutually exclusive */
+     if ((flags & SSS_NSS_EX_FLAG_NO_CACHE) != 0
+@@ -112,6 +115,52 @@ static int check_flags(uint32_t flags)
+         return EINVAL;
+     }
+ 
++    *skip_mc = false;
++    if ((flags & SSS_NSS_EX_FLAG_NO_CACHE) != 0
++            || (flags & SSS_NSS_EX_FLAG_INVALIDATE_CACHE) != 0) {
++        *skip_mc = true;
++    }
++
++    switch(inp->cmd) {
++    case SSS_NSS_GETPWNAM:
++    case SSS_NSS_GETPWNAM_EX:
++    case SSS_NSS_GETPWUID:
++    case SSS_NSS_GETPWUID_EX:
++        if (inp->result.pwrep.buffer == NULL
++                || inp->result.pwrep.buflen == 0) {
++            no_data = true;
++        }
++        break;
++    case SSS_NSS_GETGRNAM:
++    case SSS_NSS_GETGRNAM_EX:
++    case SSS_NSS_GETGRGID:
++    case SSS_NSS_GETGRGID_EX:
++        if (inp->result.grrep.buffer == NULL
++                || inp->result.grrep.buflen == 0) {
++            no_data = true;
++        }
++        break;
++    case SSS_NSS_INITGR:
++    case SSS_NSS_INITGR_EX:
++        if (inp->result.initgrrep.ngroups == 0
++                || inp->result.initgrrep.groups == NULL) {
++            return EINVAL;
++        }
++        break;
++    default:
++        return EINVAL;
++    }
++
++    *skip_data = false;
++    /* Allow empty buffer with SSS_NSS_EX_FLAG_INVALIDATE_CACHE */
++    if (no_data) {
++        if ((flags & SSS_NSS_EX_FLAG_INVALIDATE_CACHE) != 0) {
++            *skip_data = true;
++        } else {
++            return ERANGE;
++        }
++    }
++
+     return 0;
+ }
+ 
+@@ -128,18 +177,14 @@ int sss_get_ex(struct nss_input *inp, uint32_t flags, unsigned int timeout)
+     gid_t *new_groups;
+     size_t idx;
+     bool skip_mc = false;
++    bool skip_data = false;
+ 
+-    ret = check_flags(flags);
++    ret = check_flags(inp, flags, &skip_mc, &skip_data);
+     if (ret != 0) {
+         return ret;
+     }
+ 
+-    if ((flags & SSS_NSS_EX_FLAG_NO_CACHE) != 0
+-            || (flags & SSS_NSS_EX_FLAG_INVALIDATE_CACHE) != 0) {
+-        skip_mc = true;
+-    }
+-
+-    if (!skip_mc) {
++    if (!skip_mc && !skip_data) {
+         ret = sss_nss_mc_get(inp);
+         switch (ret) {
+         case 0:
+@@ -159,7 +204,7 @@ int sss_get_ex(struct nss_input *inp, uint32_t flags, unsigned int timeout)
+ 
+     sss_nss_timedlock(timeout, &time_left);
+ 
+-    if (!skip_mc) {
++    if (!skip_mc && !skip_data) {
+         /* previous thread might already initialize entry in mmap cache */
+         ret = sss_nss_mc_get(inp);
+         switch (ret) {
+@@ -196,6 +241,12 @@ int sss_get_ex(struct nss_input *inp, uint32_t flags, unsigned int timeout)
+         goto out;
+     }
+ 
++    if (skip_data) {
++        /* No data requested, just return the return code */
++        ret = 0;
++        goto out;
++    }
++
+     if (inp->cmd == SSS_NSS_INITGR || inp->cmd == SSS_NSS_INITGR_EX) {
+         if ((*(inp->result.initgrrep.ngroups) - *(inp->result.initgrrep.start))
+                     < num_results) {
+@@ -311,10 +362,6 @@ int sss_nss_getpwnam_timeout(const char *name, struct passwd *pwd,
+         .result.pwrep.buffer = buffer,
+         .result.pwrep.buflen = buflen};
+ 
+-    if (buffer == NULL || buflen == 0) {
+-        return ERANGE;
+-    }
+-
+     ret = make_name_flag_req_data(name, flags, &inp.rd);
+     if (ret != 0) {
+         return ret;
+@@ -346,10 +393,6 @@ int sss_nss_getpwuid_timeout(uid_t uid, struct passwd *pwd,
+         .result.pwrep.buffer = buffer,
+         .result.pwrep.buflen = buflen};
+ 
+-    if (buffer == NULL || buflen == 0) {
+-        return ERANGE;
+-    }
+-
+     SAFEALIGN_COPY_UINT32(&req_data[0], &uid, NULL);
+     SAFEALIGN_COPY_UINT32(&req_data[1], &flags, NULL);
+     *result = NULL;
+@@ -373,10 +416,6 @@ int sss_nss_getgrnam_timeout(const char *name, struct group *grp,
+         .result.grrep.buffer = buffer,
+         .result.grrep.buflen = buflen};
+ 
+-    if (buffer == NULL || buflen == 0) {
+-        return ERANGE;
+-    }
+-
+     ret = make_name_flag_req_data(name, flags, &inp.rd);
+     if (ret != 0) {
+         return ret;
+@@ -407,10 +446,6 @@ int sss_nss_getgrgid_timeout(gid_t gid, struct group *grp,
+         .result.grrep.buffer = buffer,
+         .result.grrep.buflen = buflen};
+ 
+-    if (buffer == NULL || buflen == 0) {
+-        return ERANGE;
+-    }
+-
+     SAFEALIGN_COPY_UINT32(&req_data[0], &gid, NULL);
+     SAFEALIGN_COPY_UINT32(&req_data[1], &flags, NULL);
+     *result = NULL;
+@@ -434,10 +469,6 @@ int sss_nss_getgrouplist_timeout(const char *name, gid_t group,
+         .input.name = name,
+         .cmd = SSS_NSS_INITGR_EX};
+ 
+-    if (groups == NULL || ngroups == NULL || *ngroups == 0) {
+-        return EINVAL;
+-    }
+-
+     ret = make_name_flag_req_data(name, flags, &inp.rd);
+     if (ret != 0) {
+         return ret;
+-- 
+2.13.6
+
diff --git a/SOURCES/0032-BUILD-Properly-expand-variables-in-sssd-ifp.service.patch b/SOURCES/0032-BUILD-Properly-expand-variables-in-sssd-ifp.service.patch
new file mode 100644
index 0000000..be96f01
--- /dev/null
+++ b/SOURCES/0032-BUILD-Properly-expand-variables-in-sssd-ifp.service.patch
@@ -0,0 +1,51 @@
+From 1925a1bb1e0ea210bd40ec3374726948fb6e4760 Mon Sep 17 00:00:00 2001
+From: Lukas Slebodnik <lslebodn@redhat.com>
+Date: Tue, 7 Nov 2017 17:11:52 +0100
+Subject: [PATCH 32/35] BUILD: Properly expand variables in sssd-ifp.service
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+systemd[1]: [/usr/lib/systemd/system/sssd-ifp.service:9]
+    Path '-@environment_file@' is not absolute, ignoring.
+
+sh-4.2# systemctl cat sssd-ifp.service
+ # /usr/lib/systemd/system/sssd-ifp.service
+[Unit]
+Description=SSSD IFP Service responder
+Documentation=man:sssd-ifp(5)
+After=sssd.service
+BindsTo=sssd.service
+
+[Service]
+Environment=DEBUG_LOGGER=--logger=files
+EnvironmentFile=-@environment_file@
+Type=dbus
+BusName=org.freedesktop.sssd.infopipe
+ExecStart=/usr/libexec/sssd/sssd_ifp --uid 0 --gid 0 --dbus-activated ${DEBUG_LOGGER}
+
+Resolves:
+https://pagure.io/SSSD/sssd/issue/3433
+
+Reviewed-by: Fabiano Fidêncio <fidencio@redhat.com>
+(cherry picked from commit b495522f3eadde9ad4bb8d125fd70b0d5f07596a)
+---
+ Makefile.am | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/Makefile.am b/Makefile.am
+index 286ba47e3c421864362717be5258de960efca9f2..bbc90d9bad4d22ca0284ea95281a487d42399c05 100644
+--- a/Makefile.am
++++ b/Makefile.am
+@@ -1491,7 +1491,7 @@ EXTRA_DIST += \
+     src/responder/ifp/org.freedesktop.sssd.infopipe.service.in \
+     $(NULL)
+ 
+-ifp_edit_cmd = $(SED) \
++ifp_edit_cmd = $(edit_cmd) \
+         -e 's|@ifp_exec_cmd[@]|$(ifp_exec_cmd)|g' \
+         -e 's|@ifp_systemdservice[@]|$(ifp_systemdservice)|g' \
+         -e 's|@ifp_restart[@]|$(ifp_restart)|g'
+-- 
+2.13.6
+
diff --git a/SOURCES/0032-TCURL-Support-HTTP-POST-for-creating-containers.patch b/SOURCES/0032-TCURL-Support-HTTP-POST-for-creating-containers.patch
deleted file mode 100644
index c94fe07..0000000
--- a/SOURCES/0032-TCURL-Support-HTTP-POST-for-creating-containers.patch
+++ /dev/null
@@ -1,129 +0,0 @@
-From 2777ccdcc9038d8f62be81a24ae885639fe6ea9a Mon Sep 17 00:00:00 2001
-From: Jakub Hrozek <jhrozek@redhat.com>
-Date: Tue, 14 Mar 2017 15:34:57 +0100
-Subject: [PATCH 32/36] TCURL: Support HTTP POST for creating containers
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-The curl integration must allow us to create containers, therefore we
-also add support of the POST HTTP request type.
-
-Reviewed-by: Michal Židek <mzidek@redhat.com>
-Reviewed-by: Simo Sorce <simo@redhat.com>
----
- src/tests/intg/test_secrets.py | 28 ++++++++++++++++++++++++++++
- src/tests/tcurl_test_tool.c    |  5 +++++
- src/util/tev_curl.c            |  7 +++++++
- src/util/tev_curl.h            |  1 +
- 4 files changed, 41 insertions(+)
-
-diff --git a/src/tests/intg/test_secrets.py b/src/tests/intg/test_secrets.py
-index cbc1a1f06d2abb826bc0a880cb5a842f577657ea..d71c1904558cc6f8a6eee36c4049582705bc30ac 100644
---- a/src/tests/intg/test_secrets.py
-+++ b/src/tests/intg/test_secrets.py
-@@ -271,6 +271,34 @@ def test_curlwrap_crd_ops(setup_for_secrets,
-                        'http://localhost/secrets/foo'],
-                       404)
- 
-+    # Create a container
-+    run_curlwrap_tool([curlwrap_tool, '-o',
-+                       '-v', '-s', sock_path,
-+                       'http://localhost/secrets/cont/'],
-+                      200)
-+
-+    # set a secret foo:bar
-+    run_curlwrap_tool([curlwrap_tool, '-p',
-+                       '-v', '-s', sock_path,
-+                       'http://localhost/secrets/cont/cfoo',
-+                       'foo_under_cont'],
-+                      200)
-+
-+    # list secrets
-+    output = run_curlwrap_tool([curlwrap_tool,
-+                                '-v', '-s', sock_path,
-+                                'http://localhost/secrets/cont/'],
-+                               200)
-+    assert "cfoo" in output
-+
-+    # get the foo secret
-+    output = run_curlwrap_tool([curlwrap_tool,
-+                                '-v', '-s', sock_path,
-+                                'http://localhost/secrets/cont/cfoo'],
-+                               200)
-+    assert "foo_under_cont" in output
-+
-+
- 
- def test_curlwrap_parallel(setup_for_secrets,
-                            curlwrap_tool):
-diff --git a/src/tests/tcurl_test_tool.c b/src/tests/tcurl_test_tool.c
-index 38cea432885c97ca3827c8f158bf7e3ebfc67b31..2af950ebb76a22bdf4a6dfd58442b10486e64293 100644
---- a/src/tests/tcurl_test_tool.c
-+++ b/src/tests/tcurl_test_tool.c
-@@ -88,6 +88,7 @@ int main(int argc, const char *argv[])
-         { "get", 'g', POPT_ARG_NONE, NULL, 'g', "Perform a HTTP GET (default)", NULL },
-         { "put", 'p', POPT_ARG_NONE, NULL, 'p', "Perform a HTTP PUT", NULL },
-         { "del", 'd', POPT_ARG_NONE, NULL, 'd', "Perform a HTTP DELETE", NULL },
-+        { "post", 'o', POPT_ARG_NONE, NULL, 'o', "Perform a HTTP POST", NULL },
-         { "verbose", 'v', POPT_ARG_NONE, NULL, 'v', "Print response code and body", NULL },
-         POPT_TABLEEND
-     };
-@@ -118,6 +119,9 @@ int main(int argc, const char *argv[])
-         case 'd':
-             req_type = TCURL_HTTP_DELETE;
-             break;
-+        case 'o':
-+            req_type = TCURL_HTTP_POST;
-+            break;
-         case 'v':
-             pc_verbose = 1;
-             break;
-@@ -145,6 +149,7 @@ int main(int argc, const char *argv[])
-         switch (req_type) {
-         case TCURL_HTTP_GET:
-         case TCURL_HTTP_DELETE:
-+        case TCURL_HTTP_POST:
-             urls[n_reqs++] = extra_arg_ptr;
-             break;
-         case TCURL_HTTP_PUT:
-diff --git a/src/util/tev_curl.c b/src/util/tev_curl.c
-index fd436653b5aeb611a9648a8b81a330fd3fcfe875..645d1182d10f825f209f48e0ba7e6804dde1971c 100644
---- a/src/util/tev_curl.c
-+++ b/src/util/tev_curl.c
-@@ -154,6 +154,8 @@ static const char *http_req2str(enum tcurl_http_request req)
-         return "PUT";
-     case TCURL_HTTP_DELETE:
-         return "DELETE";
-+    case TCURL_HTTP_POST:
-+        return "POST";
-     }
- 
-     return "Uknown request type";
-@@ -815,6 +817,11 @@ static errno_t tcurl_set_options(struct tcurl_http_state *state,
-     }
- 
-     switch (req_type) {
-+    case TCURL_HTTP_POST:
-+        crv = curl_easy_setopt(state->http_handle,
-+                               CURLOPT_CUSTOMREQUEST,
-+                               "POST");
-+        break;
-     case TCURL_HTTP_PUT:
-         /* CURLOPT_UPLOAD enables HTTP_PUT */
-         crv = curl_easy_setopt(state->http_handle,
-diff --git a/src/util/tev_curl.h b/src/util/tev_curl.h
-index de0601df4327d97001a8a825cd4709936f6c8466..444eb286e09d189b4588e2b2152b5202df3914d8 100644
---- a/src/util/tev_curl.h
-+++ b/src/util/tev_curl.h
-@@ -34,6 +34,7 @@ enum tcurl_http_request {
-     TCURL_HTTP_GET,
-     TCURL_HTTP_PUT,
-     TCURL_HTTP_DELETE,
-+    TCURL_HTTP_POST,
- };
- 
- /**
--- 
-2.9.3
-
diff --git a/SOURCES/0033-KCM-Store-ccaches-in-secrets.patch b/SOURCES/0033-KCM-Store-ccaches-in-secrets.patch
deleted file mode 100644
index d08a786..0000000
--- a/SOURCES/0033-KCM-Store-ccaches-in-secrets.patch
+++ /dev/null
@@ -1,3787 +0,0 @@
-From 8c30024303436d98818977288e28511ed74c018a Mon Sep 17 00:00:00 2001
-From: Jakub Hrozek <jhrozek@redhat.com>
-Date: Mon, 20 Mar 2017 11:49:43 +0100
-Subject: [PATCH 33/36] KCM: Store ccaches in secrets
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Adds a new KCM responder ccache back end that forwards all requests to
-sssd-secrets.
-
-Reviewed-by: Michal Židek <mzidek@redhat.com>
-Reviewed-by: Simo Sorce <simo@redhat.com>
----
- Makefile.am                                  |   40 +-
- contrib/sssd.spec.in                         |    1 +
- src/responder/kcm/kcmsrv_ccache.h            |   49 +
- src/responder/kcm/kcmsrv_ccache_json.c       |  921 +++++++++++
- src/responder/kcm/kcmsrv_ccache_secrets.c    | 2169 ++++++++++++++++++++++++++
- src/tests/cmocka/test_kcm_json_marshalling.c |  234 +++
- src/tests/intg/test_kcm.py                   |  132 +-
- src/util/util_errors.c                       |    2 +
- src/util/util_errors.h                       |    2 +
- 9 files changed, 3525 insertions(+), 25 deletions(-)
- create mode 100644 src/responder/kcm/kcmsrv_ccache_json.c
- create mode 100644 src/responder/kcm/kcmsrv_ccache_secrets.c
- create mode 100644 src/tests/cmocka/test_kcm_json_marshalling.c
-
-diff --git a/Makefile.am b/Makefile.am
-index 49b4cabf9ee3ce1417f955c972376894f3709b33..e9eaa312c91e3aee40bcf13c90a0ad8c683045d5 100644
---- a/Makefile.am
-+++ b/Makefile.am
-@@ -303,6 +303,10 @@ if HAVE_INOTIFY
- non_interactive_cmocka_based_tests += test_inotify
- endif   # HAVE_INOTIFY
- 
-+if BUILD_KCM
-+non_interactive_cmocka_based_tests += test_kcm_json
-+endif   # BUILD_KCM
-+
- if BUILD_SAMBA
- non_interactive_cmocka_based_tests += \
-     ad_access_filter_tests \
-@@ -1494,19 +1498,26 @@ sssd_kcm_SOURCES = \
-     src/responder/kcm/kcmsrv_cmd.c \
-     src/responder/kcm/kcmsrv_ccache.c \
-     src/responder/kcm/kcmsrv_ccache_mem.c \
-+    src/responder/kcm/kcmsrv_ccache_json.c \
-+    src/responder/kcm/kcmsrv_ccache_secrets.c \
-     src/responder/kcm/kcmsrv_ops.c \
-     src/util/sss_sockets.c \
-     src/util/sss_krb5.c \
-     src/util/sss_iobuf.c \
-+    src/util/tev_curl.c \
-     $(SSSD_RESPONDER_OBJ) \
-     $(NULL)
- sssd_kcm_CFLAGS = \
-     $(AM_CFLAGS) \
-     $(KRB5_CFLAGS) \
-     $(UUID_CFLAGS) \
-+    $(CURL_CFLAGS) \
-+    $(JANSSON_CFLAGS) \
-     $(NULL)
- sssd_kcm_LDADD = \
-     $(KRB5_LIBS) \
-+    $(CURL_LIBS) \
-+    $(JANSSON_LIBS) \
-     $(SSSD_LIBS) \
-     $(UUID_LIBS) \
-     $(SYSTEMD_DAEMON_LIBS) \
-@@ -3369,6 +3380,30 @@ sss_certmap_test_LDADD = \
-     libsss_certmap.la \
-     $(NULL)
- endif
-+
-+if BUILD_KCM
-+test_kcm_json_SOURCES = \
-+    src/tests/cmocka/test_kcm_json_marshalling.c \
-+    src/responder/kcm/kcmsrv_ccache_json.c \
-+    src/responder/kcm/kcmsrv_ccache.c \
-+    src/util/sss_krb5.c \
-+    src/util/sss_iobuf.c \
-+    $(NULL)
-+test_kcm_json_CFLAGS = \
-+    $(AM_CFLAGS) \
-+    $(UUID_CFLAGS) \
-+    $(NULL)
-+test_kcm_json_LDADD = \
-+    $(JANSSON_LIBS) \
-+    $(UUID_LIBS) \
-+    $(KRB5_LIBS) \
-+    $(CMOCKA_LIBS) \
-+    $(SSSD_LIBS) \
-+    $(SSSD_INTERNAL_LTLIBS) \
-+    libsss_test_common.la \
-+    $(NULL)
-+endif # BUILD_KCM
-+
- endif # HAVE_CMOCKA
- 
- noinst_PROGRAMS = pam_test_client
-@@ -3431,8 +3466,9 @@ intgcheck-prepare:
- 	    --enable-intgcheck-reqs \
- 	    --without-semanage \
- 	    --enable-files-domain \
--	    $(INTGCHECK_CONFIGURE_FLAGS); \
--	$(MAKE) $(AM_MAKEFLAGS); \
-+	    $(INTGCHECK_CONFIGURE_FLAGS) \
-+	    CFLAGS="$$CFLAGS $(AM_CFLAGS) -DKCM_PEER_UID=$$(id -u)"; \
-+	$(MAKE) $(AM_MAKEFLAGS) ; \
- 	: Force single-thread install to workaround concurrency issues; \
- 	$(MAKE) $(AM_MAKEFLAGS) -j1 install; \
- 	: Remove .la files from LDB module directory to avoid loader warnings; \
-diff --git a/contrib/sssd.spec.in b/contrib/sssd.spec.in
-index 1d4d020415ee28292bb4d88c78de205465d812f1..af14d4e3d6b9ffeb4696f1517113b8daa575cb99 100644
---- a/contrib/sssd.spec.in
-+++ b/contrib/sssd.spec.in
-@@ -223,6 +223,7 @@ BuildRequires: systemtap-sdt-devel
- BuildRequires: http-parser-devel
- BuildRequires: jansson-devel
- BuildRequires: libuuid-devel
-+BuildRequires: libcurl-devel
- 
- %description
- Provides a set of daemons to manage access to remote directories and
-diff --git a/src/responder/kcm/kcmsrv_ccache.h b/src/responder/kcm/kcmsrv_ccache.h
-index 130ae48ae30d5e1e2ab238a647a9b9dc76cc4945..18c8c47ad4ecb24521a85a1833b239c7a2a8bb45 100644
---- a/src/responder/kcm/kcmsrv_ccache.h
-+++ b/src/responder/kcm/kcmsrv_ccache.h
-@@ -303,4 +303,53 @@ void kcm_debug_uuid(uuid_t uuid);
-  */
- errno_t kcm_check_name(const char *name, struct cli_creds *client);
- 
-+/*
-+ * ccahe marshalling to and from JSON. This is used when the ccaches
-+ * are stored in the secrets store
-+ */
-+
-+/*
-+ * The secrets store is a key-value store at heart. We store the UUID
-+ * and the name in the key to allow easy lookups be either key
-+ */
-+bool sec_key_match_name(const char *sec_key,
-+                        const char *name);
-+
-+bool sec_key_match_uuid(const char *sec_key,
-+                        uuid_t uuid);
-+
-+const char *sec_key_get_name(const char *sec_key);
-+
-+errno_t sec_key_get_uuid(const char *sec_key,
-+                         uuid_t uuid);
-+
-+/* Create a URL for the default client's ccache */
-+const char *sec_dfl_url_create(TALLOC_CTX *mem_ctx,
-+                               struct cli_creds *client);
-+
-+/* Create a URL for the client's ccache container */
-+const char *sec_container_url_create(TALLOC_CTX *mem_ctx,
-+                                     struct cli_creds *client);
-+
-+const char *sec_cc_url_create(TALLOC_CTX *mem_ctx,
-+                              struct cli_creds *client,
-+                              const char *sec_key);
-+
-+/*
-+ * sec_key is a concatenation of the ccache's UUID and name
-+ * sec_value is the JSON dump of the ccache contents
-+ */
-+errno_t sec_kv_to_ccache(TALLOC_CTX *mem_ctx,
-+                         const char *sec_key,
-+                         const char *sec_value,
-+                         struct cli_creds *client,
-+                         struct kcm_ccache **_cc);
-+
-+/* Convert a kcm_ccache to a key-value pair to be stored in secrets */
-+errno_t kcm_ccache_to_sec_input(TALLOC_CTX *mem_ctx,
-+                                struct kcm_ccache *cc,
-+                                struct cli_creds *client,
-+                                const char **_url,
-+                                struct sss_iobuf **_payload);
-+
- #endif /* _KCMSRV_CCACHE_H_ */
-diff --git a/src/responder/kcm/kcmsrv_ccache_json.c b/src/responder/kcm/kcmsrv_ccache_json.c
-new file mode 100644
-index 0000000000000000000000000000000000000000..40b64861c209206d6f60ccd0843857edee24a844
---- /dev/null
-+++ b/src/responder/kcm/kcmsrv_ccache_json.c
-@@ -0,0 +1,921 @@
-+/*
-+   SSSD
-+
-+   KCM Server - ccache JSON (un)marshalling for storing ccaches in
-+                sssd-secrets
-+
-+   Copyright (C) Red Hat, 2017
-+
-+   This program is free software; you can redistribute it and/or modify
-+   it under the terms of the GNU General Public License as published by
-+   the Free Software Foundation; either version 3 of the License, or
-+   (at your option) any later version.
-+
-+   This program is distributed in the hope that it will be useful,
-+   but WITHOUT ANY WARRANTY; without even the implied warranty of
-+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+   GNU General Public License for more details.
-+
-+   You should have received a copy of the GNU General Public License
-+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
-+*/
-+
-+#include "config.h"
-+
-+#include <stdio.h>
-+#include <talloc.h>
-+#include <jansson.h>
-+
-+#include "util/util.h"
-+#include "util/util_creds.h"
-+#include "util/crypto/sss_crypto.h"
-+#include "responder/kcm/kcmsrv_ccache_pvt.h"
-+
-+/* The base for storing secrets is:
-+ *  http://localhost/kcm/persistent/$uid
-+ *
-+ * Under $base, there are two containers:
-+ *  /ccache     - stores the ccaches
-+ *  /ntlm       - stores NTLM creds [Not implement yet]
-+ *
-+ * There is also a special entry that contains the UUID of the default
-+ * cache for this UID:
-+ *  /default    - stores the UUID of the default ccache for this UID
-+ *
-+ * Each ccache has a name and an UUID. On the secrets level, the 'secret'
-+ * is a concatenation of the stringified UUID and the name separated
-+ * by a plus-sign.
-+ */
-+#define KCM_SEC_URL        "http://localhost/kcm/persistent"
-+#define KCM_SEC_BASE_FMT    KCM_SEC_URL"/%"SPRIuid"/"
-+#define KCM_SEC_CCACHE_FMT  KCM_SEC_BASE_FMT"ccache/"
-+#define KCM_SEC_DFL_FMT     KCM_SEC_BASE_FMT"default"
-+
-+
-+/*
-+ * We keep the JSON representation of the ccache versioned to allow
-+ * us to modify the format in a future version
-+ */
-+#define KS_JSON_VERSION     1
-+
-+/*
-+ * The secrets store is a key-value store at heart. We store the UUID
-+ * and the name in the key to allow easy lookups be either key
-+ */
-+#define SEC_KEY_SEPARATOR   '-'
-+
-+/* Compat definition of json_array_foreach for older systems */
-+#ifndef json_array_foreach
-+#define json_array_foreach(array, idx, value) \
-+    for(idx = 0; \
-+            idx < json_array_size(array) && (value = json_array_get(array, idx)); \
-+            idx++)
-+#endif
-+
-+const char *sec_container_url_create(TALLOC_CTX *mem_ctx,
-+                                     struct cli_creds *client)
-+{
-+    return talloc_asprintf(mem_ctx,
-+                           KCM_SEC_CCACHE_FMT,
-+                           cli_creds_get_uid(client));
-+}
-+
-+const char *sec_cc_url_create(TALLOC_CTX *mem_ctx,
-+                              struct cli_creds *client,
-+                              const char *sec_key)
-+{
-+    return talloc_asprintf(mem_ctx,
-+                           KCM_SEC_CCACHE_FMT"%s",
-+                           cli_creds_get_uid(client),
-+                           sec_key);
-+}
-+
-+const char *sec_dfl_url_create(TALLOC_CTX *mem_ctx,
-+                               struct cli_creds *client)
-+{
-+    return talloc_asprintf(mem_ctx,
-+                           KCM_SEC_DFL_FMT,
-+                           cli_creds_get_uid(client));
-+}
-+
-+static const char *sec_key_create(TALLOC_CTX *mem_ctx,
-+                                  const char *name,
-+                                  uuid_t uuid)
-+{
-+    char uuid_str[UUID_STR_SIZE];
-+
-+    uuid_unparse(uuid, uuid_str);
-+    return talloc_asprintf(mem_ctx,
-+                           "%s%c%s", uuid_str, SEC_KEY_SEPARATOR, name);
-+}
-+
-+static errno_t sec_key_parse(TALLOC_CTX *mem_ctx,
-+                             const char *sec_key,
-+                             const char **_name,
-+                             uuid_t uuid)
-+{
-+    char uuid_str[UUID_STR_SIZE];
-+
-+    if (strlen(sec_key) < UUID_STR_SIZE + 2) {
-+        /* One char for separator and at least one for the name */
-+        DEBUG(SSSDBG_CRIT_FAILURE, "Key %s is too short\n", sec_key);
-+        return EINVAL;
-+    }
-+
-+    strncpy(uuid_str, sec_key, sizeof(uuid_str)-1);
-+    if (sec_key[UUID_STR_SIZE - 1] != SEC_KEY_SEPARATOR) {
-+        DEBUG(SSSDBG_CRIT_FAILURE, "Key doesn't contain the separator\n");
-+        return EINVAL;
-+    }
-+    uuid_str[UUID_STR_SIZE-1] = '\0';
-+
-+    *_name = talloc_strdup(mem_ctx, sec_key + UUID_STR_SIZE);
-+    if (*_name == NULL) {
-+        return ENOMEM;
-+    }
-+    uuid_parse(uuid_str, uuid);
-+
-+    return EOK;
-+}
-+
-+errno_t sec_key_get_uuid(const char *sec_key,
-+                         uuid_t uuid)
-+{
-+    char uuid_str[UUID_STR_SIZE];
-+
-+    if (strlen(sec_key) < UUID_STR_SIZE + 2) {
-+        /* One char for separator and at least one for the name */
-+        DEBUG(SSSDBG_CRIT_FAILURE, "Key %s is too short\n", sec_key);
-+        return EINVAL;
-+    }
-+
-+    if (sec_key[UUID_STR_SIZE-1] != SEC_KEY_SEPARATOR) {
-+        DEBUG(SSSDBG_CRIT_FAILURE, "Key doesn't contain the separator\n");
-+        return EINVAL;
-+    }
-+
-+    strncpy(uuid_str, sec_key, UUID_STR_SIZE-1);
-+    uuid_str[UUID_STR_SIZE-1] = '\0';
-+    uuid_parse(uuid_str, uuid);
-+    return EOK;
-+}
-+
-+const char *sec_key_get_name(const char *sec_key)
-+{
-+    if (strlen(sec_key) < UUID_STR_SIZE + 2) {
-+        /* One char for separator and at least one for the name */
-+        DEBUG(SSSDBG_CRIT_FAILURE, "Key %s is too short\n", sec_key);
-+        return NULL;
-+    }
-+
-+    return sec_key + UUID_STR_SIZE;
-+}
-+
-+bool sec_key_match_name(const char *sec_key,
-+                        const char *name)
-+{
-+    if (strlen(sec_key) < UUID_STR_SIZE + 2) {
-+        /* One char for separator and at least one for the name */
-+        DEBUG(SSSDBG_MINOR_FAILURE, "Key %s is too short\n", sec_key);
-+        return false;
-+    }
-+
-+    return strcmp(sec_key + UUID_STR_SIZE, name) == 0;
-+}
-+
-+bool sec_key_match_uuid(const char *sec_key,
-+                        uuid_t uuid)
-+{
-+    errno_t ret;
-+    uuid_t key_uuid;
-+
-+    ret = sec_key_get_uuid(sec_key, key_uuid);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_MINOR_FAILURE, "Cannot convert key to UUID\n");
-+        return false;
-+    }
-+
-+    return uuid_compare(key_uuid, uuid) == 0;
-+}
-+
-+/*
-+ * Creates an array of principal elements that will be used later
-+ * in the form of:
-+ *          "componenets": [ "elem1", "elem2", ...]
-+ */
-+static json_t *princ_data_to_json(TALLOC_CTX *mem_ctx,
-+                                  krb5_principal princ)
-+{
-+    json_t *jdata = NULL;
-+    json_t *data_array = NULL;
-+    int ret;
-+    char *str_princ_data;
-+
-+    data_array = json_array();
-+    if (data_array == NULL) {
-+        return NULL;
-+    }
-+
-+    for (ssize_t i = 0; i < princ->length; i++) {
-+        /* FIXME - it might be cleaner to use stringn here, but the libjansson
-+         * version on RHEL-7 doesn't support that
-+         */
-+        str_princ_data = talloc_zero_array(mem_ctx,
-+                                           char,
-+                                           princ->data[i].length + 1);
-+        if (str_princ_data == NULL) {
-+            return NULL;
-+        }
-+        memcpy(str_princ_data, princ->data[i].data, princ->data[i].length);
-+        str_princ_data[princ->data[i].length] = '\0';
-+
-+        jdata = json_string(str_princ_data);
-+        talloc_free(str_princ_data);
-+        if (jdata == NULL) {
-+            DEBUG(SSSDBG_CRIT_FAILURE,
-+                  "Cannot convert principal data to string\n");
-+            json_decref(data_array);
-+            return NULL;
-+        }
-+
-+        ret = json_array_append_new(data_array, jdata);
-+        if (ret != 0) {
-+            DEBUG(SSSDBG_CRIT_FAILURE,
-+                  "Cannot append principal data to array\n");
-+            json_decref(jdata);
-+            json_decref(data_array);
-+            return NULL;
-+        }
-+        /* data_array now owns the reference to jdata */
-+    }
-+
-+    return data_array;
-+}
-+
-+/* Creates:
-+ *      {
-+ *          "type": "number",
-+ *          "realm": "string",
-+ *          "componenents": [ "elem1", "elem2", ...]
-+ *      }
-+ */
-+static json_t *princ_to_json(TALLOC_CTX *mem_ctx,
-+                             krb5_principal princ)
-+{
-+    json_t *jprinc = NULL;
-+    json_t *components = NULL;
-+    json_error_t error;
-+    char *str_realm_data;
-+
-+    components = princ_data_to_json(mem_ctx, princ);
-+    if (components == NULL) {
-+        DEBUG(SSSDBG_CRIT_FAILURE,
-+              "Cannot convert principal data to JSON\n");
-+        return NULL;
-+    }
-+
-+    /* FIXME - it might be cleaner to use the s% specifier here, but the libjansson
-+     * version on RHEL-7 doesn't support that
-+     */
-+    str_realm_data = talloc_zero_array(mem_ctx,
-+                                        char,
-+                                        princ->realm.length + 1);
-+    if (str_realm_data == NULL) {
-+        return NULL;
-+    }
-+    memcpy(str_realm_data, princ->realm.data, princ->realm.length);
-+    str_realm_data[princ->realm.length] = '\0';
-+
-+    jprinc = json_pack_ex(&error,
-+                          JSON_STRICT,
-+                          "{s:i, s:s, s:o}",
-+                          "type", princ->type,
-+                          "realm", str_realm_data,
-+                          "components", components);
-+    talloc_free(str_realm_data);
-+    if (jprinc == NULL) {
-+        DEBUG(SSSDBG_CRIT_FAILURE,
-+              "Failed to pack JSON princ structure on line %d: %s\n",
-+              error.line, error.text);
-+        json_decref(components);
-+        return NULL;
-+    }
-+
-+    return jprinc;
-+}
-+
-+/* Creates:
-+ *          {
-+ *              "uuid": <data>,
-+ *              "payload": <data>,
-+ *          },
-+ */
-+static json_t *cred_to_json(struct kcm_cred *crd)
-+{
-+    char uuid_str[UUID_STR_SIZE];
-+    uint8_t *cred_blob_data;
-+    size_t cred_blob_size;
-+    json_t *jcred;
-+    json_error_t error;
-+    char *base64_cred_blob;
-+
-+    uuid_unparse(crd->uuid, uuid_str);
-+    cred_blob_data = sss_iobuf_get_data(crd->cred_blob);
-+    cred_blob_size = sss_iobuf_get_size(crd->cred_blob);
-+
-+    base64_cred_blob = sss_base64_encode(crd, cred_blob_data, cred_blob_size);
-+    if (base64_cred_blob == NULL) {
-+        DEBUG(SSSDBG_CRIT_FAILURE,
-+              "Cannot base64 encode the certificate blob\n");
-+        return NULL;
-+    }
-+
-+    jcred = json_pack_ex(&error,
-+                         JSON_STRICT,
-+                         "{s:s, s:s}",
-+                         "uuid", uuid_str,
-+                         "payload", base64_cred_blob);
-+    talloc_free(base64_cred_blob);
-+    if (jcred == NULL) {
-+        DEBUG(SSSDBG_CRIT_FAILURE,
-+              "Failed to pack JSON cred structure on line %d: %s\n",
-+              error.line, error.text);
-+        return NULL;
-+    }
-+    return jcred;
-+}
-+
-+/*
-+ * Creates:
-+ *      [
-+ *          {
-+ *              "uuid": <data>,
-+ *              "payload": <data>,
-+ *          },
-+ *          ...
-+ *      ]
-+ */
-+static json_t *creds_to_json_array(struct kcm_cred *creds)
-+{
-+    struct kcm_cred *crd;
-+    json_t *array;
-+    json_t *jcred;
-+
-+    array = json_array();
-+    if (array == NULL) {
-+        return NULL;
-+    }
-+
-+    DLIST_FOR_EACH(crd, creds) {
-+        jcred = cred_to_json(crd);
-+        if (jcred == NULL) {
-+            DEBUG(SSSDBG_CRIT_FAILURE,
-+                  "Cannot convert credentials to JSON\n");
-+            json_decref(array);
-+            return NULL;
-+        }
-+
-+        json_array_append_new(array, jcred);
-+        /* array now owns jcred */
-+        jcred = NULL;
-+    }
-+
-+    return array;
-+}
-+
-+/*
-+ * The ccache is formatted in JSON as:
-+ * {
-+ *      version: number
-+ *      kdc_offset: number
-+ *      principal : {
-+ *          "type": "number",
-+ *          "realm": "string",
-+ *          "componenents": [ "elem1", "elem2", ...]
-+ *      }
-+ *      creds : [
-+ *                  {
-+ *                      "uuid": <data>,
-+ *                      "payload": <data>,
-+ *                  },
-+ *                  {
-+ *                      ...
-+ *                  }
-+ *             ]
-+ *      }
-+ * }
-+ */
-+static json_t *ccache_to_json(struct kcm_ccache *cc)
-+{
-+    json_t *princ = NULL;
-+    json_t *creds = NULL;
-+    json_t *jcc = NULL;
-+    json_error_t error;
-+
-+    princ = princ_to_json(cc, cc->client);
-+    if (princ == NULL) {
-+        DEBUG(SSSDBG_CRIT_FAILURE,
-+              "Cannot convert princ to JSON\n");
-+        return NULL;
-+    }
-+
-+    creds = creds_to_json_array(cc->creds);
-+    if (creds == NULL) {
-+        DEBUG(SSSDBG_CRIT_FAILURE,
-+              "Cannot convert creds to JSON array\n");
-+        json_decref(princ);
-+        return NULL;
-+    }
-+
-+    jcc = json_pack_ex(&error,
-+                       JSON_STRICT,
-+                       "{s:i, s:i, s:o, s:o}",
-+                       "version", KS_JSON_VERSION,
-+                       "kdc_offset", cc->kdc_offset,
-+                       "principal", princ,
-+                       "creds", creds);
-+    if (jcc == NULL) {
-+        DEBUG(SSSDBG_CRIT_FAILURE,
-+              "Failed to pack JSON ccache structure on line %d: %s\n",
-+              error.line, error.text);
-+        json_decref(creds);
-+        json_decref(princ);
-+        return NULL;
-+    }
-+
-+    return jcc;
-+}
-+
-+static errno_t ccache_to_sec_kv(TALLOC_CTX *mem_ctx,
-+                                struct kcm_ccache *cc,
-+                                const char **_sec_key,
-+                                const char **_sec_value)
-+{
-+    json_t *jcc = NULL;
-+    char *jdump;
-+
-+    jcc = ccache_to_json(cc);
-+    if (jcc == NULL) {
-+        DEBUG(SSSDBG_CRIT_FAILURE,
-+              "Cannot convert ccache to JSON\n");
-+        return ERR_JSON_ENCODING;
-+    }
-+
-+    /* it would be more efficient to learn the size with json_dumpb and
-+     * a NULL buffer, but that's only available since 2.10
-+     */
-+    jdump = json_dumps(jcc, JSON_INDENT(4) | JSON_ENSURE_ASCII);
-+    if (jdump == NULL) {
-+        DEBUG(SSSDBG_CRIT_FAILURE,
-+              "Cannot dump JSON\n");
-+        return ERR_JSON_ENCODING;
-+    }
-+
-+    *_sec_key = sec_key_create(mem_ctx, cc->name, cc->uuid);
-+    *_sec_value = talloc_strdup(mem_ctx, jdump);
-+    free(jdump);
-+    json_decref(jcc);
-+    if (*_sec_key == NULL || *_sec_value == NULL) {
-+        return ENOMEM;
-+    }
-+
-+    return EOK;
-+}
-+
-+errno_t kcm_ccache_to_sec_input(TALLOC_CTX *mem_ctx,
-+                                struct kcm_ccache *cc,
-+                                struct cli_creds *client,
-+                                const char **_url,
-+                                struct sss_iobuf **_payload)
-+{
-+    errno_t ret;
-+    const char *key;
-+    const char *value;
-+    const char *url;
-+    struct sss_iobuf *payload;
-+    TALLOC_CTX *tmp_ctx;
-+
-+    tmp_ctx = talloc_new(mem_ctx);
-+    if (tmp_ctx == NULL) {
-+        return ENOMEM;
-+    }
-+
-+    ret = ccache_to_sec_kv(mem_ctx, cc, &key, &value);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_CRIT_FAILURE,
-+              "Cannot convert cache %s to JSON [%d]: %s\n",
-+              cc->name, ret, sss_strerror(ret));
-+        goto done;
-+    }
-+
-+    url = sec_cc_url_create(tmp_ctx, client, key);
-+    if (url == NULL) {
-+        ret = ENOMEM;
-+        goto done;
-+    }
-+
-+    payload = sss_iobuf_init_readonly(tmp_ctx,
-+                                      (const uint8_t *) value,
-+                                      strlen(value)+1);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_CRIT_FAILURE,
-+              "Cannot create payload buffer\n");
-+        goto done;
-+    }
-+
-+    ret = EOK;
-+    *_url = talloc_steal(mem_ctx, url);
-+    *_payload = talloc_steal(mem_ctx, payload);
-+done:
-+    talloc_free(tmp_ctx);
-+    return ret;
-+}
-+
-+static errno_t sec_value_to_json(const char *input,
-+                                 json_t **_root)
-+{
-+    json_t *root = NULL;
-+    json_error_t error;
-+    int ok;
-+
-+    root = json_loads(input, 0, &error);
-+    if (root == NULL) {
-+        DEBUG(SSSDBG_CRIT_FAILURE,
-+              "Failed to parse JSON payload on line %d: %s\n",
-+              error.line, error.text);
-+        return ERR_JSON_DECODING;
-+    }
-+
-+    ok = json_is_object(root);
-+    if (!ok) {
-+        DEBUG(SSSDBG_CRIT_FAILURE, "Json data is not an object.\n");
-+        json_decref(root);
-+        return ERR_JSON_DECODING;
-+    }
-+
-+    *_root = root;
-+    return EOK;
-+}
-+
-+/*
-+ * ccache unmarshalling from JSON
-+ */
-+static errno_t json_element_to_krb5_data(TALLOC_CTX *mem_ctx,
-+                                         json_t *element,
-+                                         krb5_data *data)
-+{
-+    const char *str_value;
-+    size_t str_len;
-+
-+    /* FIXME - it might be cleaner to use stringn here, but the libjansson
-+     * version on RHEL-7 doesn't support that
-+     */
-+    str_value = json_string_value(element);
-+    if (str_value == NULL) {
-+        DEBUG(SSSDBG_CRIT_FAILURE, "JSON element not a string\n");
-+        return EINVAL;
-+    }
-+    str_len = strlen(str_value);
-+
-+    data->data = talloc_strndup(mem_ctx, str_value, str_len);
-+    if (data->data == NULL) {
-+        return ENOMEM;
-+    }
-+    data->length = str_len;
-+
-+    return EOK;
-+}
-+
-+static errno_t json_array_to_krb5_data(TALLOC_CTX *mem_ctx,
-+                                       json_t *array,
-+                                       krb5_data **_data,
-+                                       size_t *_len)
-+{
-+    errno_t ret;
-+    int ok;
-+    size_t len;
-+    size_t idx;
-+    json_t *element;
-+    krb5_data *data;
-+
-+    ok = json_is_array(array);
-+    if (!ok) {
-+        DEBUG(SSSDBG_CRIT_FAILURE, "Json object is not an array.\n");
-+        return ERR_JSON_DECODING;
-+    }
-+
-+    len = json_array_size(array);
-+    if (len == 0) {
-+        *_data = NULL;
-+        *_len = 0;
-+        return EOK;
-+    }
-+
-+    data = talloc_zero_array(mem_ctx, krb5_data, len);
-+    if (data == NULL) {
-+        return ENOMEM;
-+    }
-+
-+    json_array_foreach(array, idx, element) {
-+        ret = json_element_to_krb5_data(data, element, &data[idx]);
-+        if (ret != EOK) {
-+            DEBUG(SSSDBG_CRIT_FAILURE,
-+                  "Cannot convert krb5 data element from JSON");
-+            talloc_free(data);
-+            return ret;
-+        }
-+    }
-+
-+    *_data = data;
-+    *_len = len;
-+    return EOK;
-+}
-+
-+static errno_t json_to_princ(TALLOC_CTX *mem_ctx,
-+                             json_t *js_princ,
-+                             krb5_principal *_princ)
-+{
-+    errno_t ret;
-+    json_t *components = NULL;
-+    int ok;
-+    krb5_principal princ = NULL;
-+    TALLOC_CTX *tmp_ctx = NULL;
-+    char *realm_str;
-+    size_t realm_size;
-+    json_error_t error;
-+
-+    ok = json_is_object(js_princ);
-+    if (!ok) {
-+        DEBUG(SSSDBG_CRIT_FAILURE, "Json principal is not an object.\n");
-+        ret = ERR_JSON_DECODING;
-+        goto done;
-+    }
-+
-+    tmp_ctx = talloc_new(mem_ctx);
-+    if (tmp_ctx == NULL) {
-+        ret = ENOMEM;
-+        goto done;
-+    }
-+
-+    princ = talloc_zero(tmp_ctx, struct krb5_principal_data);
-+    if (princ == NULL) {
-+        return ENOMEM;
-+    }
-+    princ->magic = KV5M_PRINCIPAL;
-+
-+    /* FIXME - it might be cleaner to use the s% specifier here, but the libjansson
-+     * version on RHEL-7 doesn't support that
-+     */
-+    ret = json_unpack_ex(js_princ,
-+                         &error,
-+                         JSON_STRICT,
-+                         "{s:i, s:s, s:o}",
-+                         "type", &princ->type,
-+                         "realm", &realm_str,
-+                         "components", &components);
-+    if (ret != 0) {
-+        DEBUG(SSSDBG_CRIT_FAILURE,
-+              "Failed to unpack JSON princ structure on line %d: %s\n",
-+              error.line, error.text);
-+        ret = EINVAL;
-+        goto done;
-+    }
-+
-+    realm_size = strlen(realm_str);
-+
-+    princ->realm.data = talloc_strndup(mem_ctx, realm_str, realm_size);
-+    if (princ->realm.data == NULL) {
-+        return ENOMEM;
-+    }
-+    princ->realm.length = realm_size;
-+    princ->realm.magic = 0;
-+
-+    ret = json_array_to_krb5_data(princ, components,
-+                                  &princ->data,
-+                                  (size_t *) &princ->length);
-+    if (ret != EOK) {
-+            DEBUG(SSSDBG_CRIT_FAILURE,
-+                  "Cannot convert principal from JSON");
-+        ret = EINVAL;
-+        goto done;
-+    }
-+
-+    *_princ = talloc_steal(mem_ctx, princ);
-+    ret = EOK;
-+done:
-+    talloc_free(tmp_ctx);
-+    return ret;
-+}
-+
-+static errno_t json_elem_to_cred(TALLOC_CTX *mem_ctx,
-+                                 json_t *element,
-+                                 struct kcm_cred **_crd)
-+{
-+    errno_t ret;
-+    char *uuid_str;
-+    json_error_t error;
-+    uuid_t uuid;
-+    struct sss_iobuf *cred_blob;
-+    const char *base64_cred_blob;
-+    struct kcm_cred *crd;
-+    uint8_t *outbuf;
-+    size_t outbuf_size;
-+    TALLOC_CTX *tmp_ctx = NULL;
-+
-+    ret = json_unpack_ex(element,
-+                         &error,
-+                         JSON_STRICT,
-+                         "{s:s, s:s}",
-+                         "uuid", &uuid_str,
-+                         "payload", &base64_cred_blob);
-+    if (ret != 0) {
-+        DEBUG(SSSDBG_CRIT_FAILURE,
-+              "Failed to unpack JSON cred structure on line %d: %s\n",
-+              error.line, error.text);
-+        return EINVAL;
-+    }
-+
-+    uuid_parse(uuid_str, uuid);
-+
-+    tmp_ctx = talloc_new(mem_ctx);
-+    if (tmp_ctx == NULL) {
-+        ret = ENOMEM;
-+        goto done;
-+    }
-+
-+    outbuf = sss_base64_decode(tmp_ctx, base64_cred_blob, &outbuf_size);
-+    if (outbuf == NULL) {
-+        DEBUG(SSSDBG_CRIT_FAILURE, "Cannot decode cred blob\n");
-+        ret = EIO;
-+        goto done;
-+    }
-+
-+    cred_blob = sss_iobuf_init_readonly(tmp_ctx, outbuf, outbuf_size);
-+    if (cred_blob == NULL) {
-+        ret = ENOMEM;
-+        goto done;
-+    }
-+
-+    crd = kcm_cred_new(tmp_ctx, uuid, cred_blob);
-+    if (crd == NULL) {
-+        ret = ENOMEM;
-+        goto done;
-+    }
-+
-+    ret = EOK;
-+    *_crd = talloc_steal(mem_ctx, crd);
-+done:
-+    talloc_free(tmp_ctx);
-+    return ret;
-+}
-+
-+static errno_t json_to_creds(struct kcm_ccache *cc,
-+                             json_t *jcreds)
-+{
-+    errno_t ret;
-+    int ok;
-+    size_t idx;
-+    json_t *value;
-+    struct kcm_cred *crd;
-+
-+    ok = json_is_array(jcreds);
-+    if (!ok) {
-+        DEBUG(SSSDBG_CRIT_FAILURE, "Json creds object is not an array.\n");
-+        return ERR_JSON_DECODING;
-+    }
-+
-+    json_array_foreach(jcreds, idx, value) {
-+        ret = json_elem_to_cred(cc, value, &crd);
-+        if (ret != EOK) {
-+            DEBUG(SSSDBG_CRIT_FAILURE,
-+                  "Cannot convert JSON cred element [%d]: %s\n",
-+                  ret, sss_strerror(ret));
-+            return ret;
-+        }
-+
-+        ret = kcm_cc_store_creds(cc, crd);
-+        if (ret != EOK) {
-+            DEBUG(SSSDBG_CRIT_FAILURE,
-+                  "Cannot store creds in ccache [%d]: %s\n",
-+                  ret, sss_strerror(ret));
-+            return ret;
-+        }
-+    }
-+
-+    return EOK;
-+}
-+
-+static errno_t sec_json_value_to_ccache(struct kcm_ccache *cc,
-+                                        json_t *root)
-+{
-+    errno_t ret;
-+    json_t *princ = NULL;
-+    json_t *creds = NULL;
-+    json_error_t error;
-+    int version;
-+
-+    ret = json_unpack_ex(root,
-+                         &error,
-+                         JSON_STRICT,
-+                         "{s:i, s:i, s:o, s:o}",
-+                         "version", &version,
-+                         "kdc_offset", &cc->kdc_offset,
-+                         "principal", &princ,
-+                         "creds", &creds);
-+    if (ret != 0) {
-+        DEBUG(SSSDBG_CRIT_FAILURE,
-+              "Failed to unpack JSON creds structure on line %d: %s\n",
-+              error.line, error.text);
-+        return EINVAL;
-+    }
-+
-+    if (version != KS_JSON_VERSION) {
-+        DEBUG(SSSDBG_CRIT_FAILURE,
-+              "Expected version %d, received version %d\n",
-+              KS_JSON_VERSION, version);
-+        return EINVAL;
-+    }
-+
-+    ret = json_to_princ(cc, princ, &cc->client);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_CRIT_FAILURE,
-+              "Cannot store JSON to principal [%d]: %s\n",
-+              ret, sss_strerror(ret));
-+        return ret;
-+    }
-+
-+    ret = json_to_creds(cc, creds);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_CRIT_FAILURE,
-+              "Cannot store JSON to creds [%d]: %s\n",
-+              ret, sss_strerror(ret));
-+        return EOK;
-+    }
-+
-+    return EOK;
-+}
-+
-+/*
-+ * sec_key is a concatenation of the ccache's UUID and name
-+ * sec_value is the JSON dump of the ccache contents
-+ */
-+errno_t sec_kv_to_ccache(TALLOC_CTX *mem_ctx,
-+                         const char *sec_key,
-+                         const char *sec_value,
-+                         struct cli_creds *client,
-+                         struct kcm_ccache **_cc)
-+{
-+    errno_t ret;
-+    json_t *root = NULL;
-+    struct kcm_ccache *cc = NULL;
-+    TALLOC_CTX *tmp_ctx = NULL;
-+
-+    ret = sec_value_to_json(sec_value, &root);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_CRIT_FAILURE,
-+              "Cannot store secret to JSN [%d]: %s\n",
-+              ret, sss_strerror(ret));
-+        goto done;
-+    }
-+
-+    tmp_ctx = talloc_new(mem_ctx);
-+    if (tmp_ctx == NULL) {
-+        ret = ENOMEM;
-+        goto done;
-+    }
-+
-+    cc = talloc_zero(tmp_ctx, struct kcm_ccache);
-+    if (cc == NULL) {
-+        ret = ENOMEM;
-+        goto done;
-+    }
-+
-+    /* We rely on sssd-secrets only searching the user's subtree so we
-+     * set the ownership to the client
-+     */
-+    cc->owner.uid = cli_creds_get_uid(client);
-+    cc->owner.gid = cli_creds_get_gid(client);
-+
-+    ret = sec_key_parse(cc, sec_key, &cc->name, cc->uuid);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_CRIT_FAILURE,
-+              "Cannt parse secret key [%d]: %s\n",
-+              ret, sss_strerror(ret));
-+        goto done;
-+    }
-+
-+    ret = sec_json_value_to_ccache(cc, root);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_CRIT_FAILURE,
-+              "Cannt parse secret value [%d]: %s\n",
-+              ret, sss_strerror(ret));
-+        goto done;
-+    }
-+
-+    ret = EOK;
-+    *_cc = talloc_steal(mem_ctx, cc);
-+done:
-+    talloc_free(tmp_ctx);
-+    json_decref(root);
-+    return ret;
-+}
-diff --git a/src/responder/kcm/kcmsrv_ccache_secrets.c b/src/responder/kcm/kcmsrv_ccache_secrets.c
-new file mode 100644
-index 0000000000000000000000000000000000000000..8be7daea59bd6e04ab8393aae9f8adc29df11c21
---- /dev/null
-+++ b/src/responder/kcm/kcmsrv_ccache_secrets.c
-@@ -0,0 +1,2169 @@
-+/*
-+   SSSD
-+
-+   KCM Server - ccache storage in sssd-secrets
-+
-+   Copyright (C) Red Hat, 2016
-+
-+   This program is free software; you can redistribute it and/or modify
-+   it under the terms of the GNU General Public License as published by
-+   the Free Software Foundation; either version 3 of the License, or
-+   (at your option) any later version.
-+
-+   This program is distributed in the hope that it will be useful,
-+   but WITHOUT ANY WARRANTY; without even the implied warranty of
-+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+   GNU General Public License for more details.
-+
-+   You should have received a copy of the GNU General Public License
-+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
-+*/
-+
-+#include "config.h"
-+
-+#include <stdio.h>
-+#include <talloc.h>
-+#include <jansson.h>
-+
-+#include "util/util.h"
-+#include "util/crypto/sss_crypto.h"
-+#include "util/tev_curl.h"
-+#include "responder/kcm/kcmsrv_ccache_pvt.h"
-+#include "responder/kcm/kcmsrv_ccache_be.h"
-+
-+#ifndef SSSD_SECRETS_SOCKET
-+#define SSSD_SECRETS_SOCKET VARDIR"/run/secrets.socket"
-+#endif  /* SSSD_SECRETS_SOCKET */
-+
-+#ifndef SEC_TIMEOUT
-+#define SEC_TIMEOUT         5
-+#endif /* SEC_TIMEOUT */
-+
-+/* Just to keep the name of the ccache readable */
-+#define MAX_CC_NUM          99999
-+
-+/* Compat definition of json_array_foreach for older systems */
-+#ifndef json_array_foreach
-+#define json_array_foreach(array, idx, value) \
-+    for(idx = 0; \
-+            idx < json_array_size(array) && (value = json_array_get(array, idx)); \
-+            idx++)
-+#endif
-+
-+static const char *find_by_name(const char **sec_key_list,
-+                                const char *name)
-+{
-+    const char *sec_name = NULL;
-+
-+    if (sec_key_list == NULL) {
-+        return NULL;
-+    }
-+
-+    for (int i = 0; sec_key_list[i]; i++) {
-+        if (sec_key_match_name(sec_key_list[i], name)) {
-+            sec_name = sec_key_list[i];
-+            break;
-+        }
-+    }
-+
-+    return sec_name;
-+}
-+
-+static const char *find_by_uuid(const char **sec_key_list,
-+                                uuid_t uuid)
-+{
-+    const char *sec_name = NULL;
-+
-+    if (sec_key_list == NULL) {
-+        return NULL;
-+    }
-+
-+    for (int i = 0; sec_key_list[i]; i++) {
-+        if (sec_key_match_uuid(sec_key_list[i], uuid)) {
-+            sec_name = sec_key_list[i];
-+            break;
-+        }
-+    }
-+
-+    return sec_name;
-+}
-+
-+static const char *sec_headers[] = {
-+    "Content-type: application/octet-stream",
-+    NULL,
-+};
-+
-+struct ccdb_sec {
-+    struct tcurl_ctx *tctx;
-+};
-+
-+static errno_t http2errno(int http_code)
-+{
-+    if (http_code != 200) {
-+        DEBUG(SSSDBG_OP_FAILURE, "HTTP request returned %d\n", http_code);
-+    }
-+
-+    switch (http_code) {
-+    case 200:
-+        return EOK;
-+    case 404:
-+        return ERR_NO_CREDS;
-+    case 400:
-+        return ERR_INPUT_PARSE;
-+    case 403:
-+        return EACCES;
-+    case 409:
-+        return EEXIST;
-+    case 413:
-+        return E2BIG;
-+    case 507:
-+        return ENOSPC;
-+    }
-+
-+    return EIO;
-+}
-+
-+/*
-+ * Helper request to list all UUID+name pairs
-+ */
-+struct sec_list_state {
-+    const char **sec_key_list;
-+    size_t sec_key_list_len;
-+};
-+
-+static void sec_list_done(struct tevent_req *subreq);
-+static errno_t sec_list_parse(struct sss_iobuf *outbuf,
-+                              TALLOC_CTX *mem_ctx,
-+                              const char ***_list,
-+                              size_t *_list_len);
-+
-+static struct tevent_req *sec_list_send(TALLOC_CTX *mem_ctx,
-+                                        struct tevent_context *ev,
-+                                        struct ccdb_sec *secdb,
-+                                        struct cli_creds *client)
-+{
-+    struct tevent_req *req = NULL;
-+    struct tevent_req *subreq = NULL;
-+    struct sec_list_state *state = NULL;
-+    errno_t ret;
-+    const char *container_url;
-+
-+    req = tevent_req_create(mem_ctx, &state, struct sec_list_state);
-+    if (req == NULL) {
-+        return NULL;
-+    }
-+
-+    DEBUG(SSSDBG_TRACE_INTERNAL, "Listing all ccaches in the secrets store\n");
-+    container_url = sec_container_url_create(state, client);
-+    if (container_url == NULL) {
-+        ret = ENOMEM;
-+        goto immediate;
-+    }
-+
-+    subreq = tcurl_http_send(state, ev, secdb->tctx,
-+                             TCURL_HTTP_GET,
-+                             SSSD_SECRETS_SOCKET,
-+                             container_url,
-+                             sec_headers,
-+                             NULL,
-+                             SEC_TIMEOUT);
-+    if (subreq == NULL) {
-+        ret = ENOMEM;
-+        goto immediate;
-+    }
-+    tevent_req_set_callback(subreq, sec_list_done, req);
-+    return req;
-+
-+immediate:
-+    tevent_req_error(req, ret);
-+    tevent_req_post(req, ev);
-+    return req;
-+}
-+
-+static void sec_list_done(struct tevent_req *subreq)
-+{
-+    errno_t ret;
-+    struct tevent_req *req = tevent_req_callback_data(subreq, struct tevent_req);
-+    struct sec_list_state *state = tevent_req_data(req,
-+                                                struct sec_list_state);
-+    struct sss_iobuf *outbuf;
-+    int http_code;
-+
-+    ret = tcurl_http_recv(state, subreq, &http_code, &outbuf);
-+    talloc_zfree(subreq);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_OP_FAILURE,
-+              "list HTTP request failed [%d]: %s\n", ret, sss_strerror(ret));
-+        tevent_req_error(req, ret);
-+        return;
-+    }
-+
-+    if (http_code == 404) {
-+        DEBUG(SSSDBG_TRACE_INTERNAL, "Nothing to list\n");
-+        /* If no ccaches are found, return an empty list */
-+        state->sec_key_list = talloc_zero_array(state, const char *, 1);
-+        if (state->sec_key_list == NULL) {
-+            tevent_req_error(req, ENOMEM);
-+            return;
-+        }
-+    } else if (http_code == 200) {
-+        DEBUG(SSSDBG_TRACE_INTERNAL, "Found %zu items\n", state->sec_key_list_len);
-+        ret = sec_list_parse(outbuf, state,
-+                             &state->sec_key_list,
-+                             &state->sec_key_list_len);
-+        if (ret != EOK) {
-+            tevent_req_error(req, ret);
-+            return;
-+        }
-+    } else {
-+        tevent_req_error(req, http2errno(http_code));
-+        return;
-+    }
-+
-+    DEBUG(SSSDBG_TRACE_INTERNAL, "list done\n");
-+    tevent_req_done(req);
-+}
-+
-+static errno_t sec_list_parse(struct sss_iobuf *outbuf,
-+                              TALLOC_CTX *mem_ctx,
-+                              const char ***_list,
-+                              size_t *_list_len)
-+{
-+    json_t *root;
-+    uint8_t *sec_http_list;
-+    json_error_t error;
-+    json_t *element;
-+    errno_t ret;
-+    int ok;
-+    size_t idx;
-+    const char **list = NULL;
-+    size_t list_len;
-+
-+    sec_http_list = sss_iobuf_get_data(outbuf);
-+    if (sec_http_list == NULL) {
-+        DEBUG(SSSDBG_CRIT_FAILURE, "No data in output buffer?\n");
-+        return EINVAL;
-+    }
-+
-+    root = json_loads((const char *) sec_http_list, 0, &error);
-+    if (root == NULL) {
-+        DEBUG(SSSDBG_CRIT_FAILURE,
-+                "Failed to parse JSON payload on line %d: %s\n",
-+                error.line, error.text);
-+        return ERR_JSON_DECODING;
-+    }
-+
-+    ok = json_is_array(root);
-+    if (!ok) {
-+        DEBUG(SSSDBG_CRIT_FAILURE, "list reply is not an object.\n");
-+        ret = ERR_JSON_DECODING;
-+        goto done;
-+    }
-+
-+    list_len = json_array_size(root);
-+    list = talloc_zero_array(mem_ctx, const char *, list_len + 1);
-+    if (list == NULL) {
-+        ret = ENOMEM;
-+        goto done;
-+    }
-+
-+    json_array_foreach(root, idx, element) {
-+        list[idx] = talloc_strdup(list, json_string_value(element));
-+        if (list[idx] == NULL) {
-+            ret = ENOMEM;
-+            goto done;
-+        }
-+    }
-+
-+    ret = EOK;
-+    *_list = list;
-+    *_list_len = list_len;
-+done:
-+    if (ret != EOK) {
-+        talloc_free(list);
-+    }
-+    json_decref(root);
-+    return ret;
-+}
-+
-+static errno_t sec_list_recv(struct tevent_req *req,
-+                             TALLOC_CTX *mem_ctx,
-+                             const char ***_sec_key_list,
-+                             size_t *_sec_key_list_len)
-+
-+{
-+    struct sec_list_state *state = tevent_req_data(req,
-+                                                struct sec_list_state);
-+
-+    TEVENT_REQ_RETURN_ON_ERROR(req);
-+
-+    if (_sec_key_list != NULL) {
-+        *_sec_key_list = talloc_steal(mem_ctx, state->sec_key_list);
-+    }
-+    if (_sec_key_list_len != NULL) {
-+        *_sec_key_list_len = state->sec_key_list_len;
-+    }
-+    return EOK;
-+}
-+
-+/*
-+ * Helper request to get a ccache by key
-+ */
-+struct sec_get_state {
-+    const char *sec_key;
-+    struct cli_creds *client;
-+
-+    struct kcm_ccache *cc;
-+};
-+
-+static void sec_get_done(struct tevent_req *subreq);
-+
-+static struct tevent_req *sec_get_send(TALLOC_CTX *mem_ctx,
-+                                       struct tevent_context *ev,
-+                                       struct ccdb_sec *secdb,
-+                                       struct cli_creds *client,
-+                                       const char *sec_key)
-+{
-+    struct tevent_req *req = NULL;
-+    struct tevent_req *subreq = NULL;
-+    struct sec_get_state *state = NULL;
-+    errno_t ret;
-+    const char *cc_url;
-+
-+    req = tevent_req_create(mem_ctx, &state, struct sec_get_state);
-+    if (req == NULL) {
-+        return NULL;
-+    }
-+    state->sec_key = sec_key;
-+    state->client = client;
-+
-+    DEBUG(SSSDBG_TRACE_INTERNAL, "Retrieving ccache %s\n", sec_key);
-+
-+    cc_url = sec_cc_url_create(state, state->client, state->sec_key);
-+    if (cc_url == NULL) {
-+        ret = ENOMEM;
-+        goto immediate;
-+    }
-+
-+    subreq = tcurl_http_send(state,
-+                             ev,
-+                             secdb->tctx,
-+                             TCURL_HTTP_GET,
-+                             SSSD_SECRETS_SOCKET,
-+                             cc_url,
-+                             sec_headers,
-+                             NULL,
-+                             SEC_TIMEOUT);
-+    if (subreq == NULL) {
-+        ret = ENOMEM;
-+        goto immediate;
-+    }
-+    tevent_req_set_callback(subreq, sec_get_done, req);
-+    return req;
-+
-+immediate:
-+    tevent_req_error(req, ret);
-+    tevent_req_post(req, ev);
-+    return req;
-+}
-+
-+static void sec_get_done(struct tevent_req *subreq)
-+{
-+    errno_t ret;
-+    struct tevent_req *req = tevent_req_callback_data(subreq, struct tevent_req);
-+    struct sec_get_state *state = tevent_req_data(req,
-+                                                struct sec_get_state);
-+    struct sss_iobuf *outbuf;
-+    const char *sec_value;
-+    int http_code;
-+
-+    ret = tcurl_http_recv(state, subreq, &http_code, &outbuf);
-+    talloc_zfree(subreq);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_OP_FAILURE,
-+              "GET HTTP request failed [%d]: %s\n", ret, sss_strerror(ret));
-+        tevent_req_error(req, ret);
-+        return;
-+    }
-+
-+    if (http_code != 200) {
-+        DEBUG(SSSDBG_OP_FAILURE,
-+              "GET operation returned HTTP error %d\n", http_code);
-+        ret = http2errno(http_code);
-+        tevent_req_error(req, ret);
-+        return;
-+    }
-+
-+    sec_value = (const char *) sss_iobuf_get_data(outbuf);
-+    if (sec_value == NULL) {
-+        DEBUG(SSSDBG_CRIT_FAILURE, "No data in output buffer\n");
-+        tevent_req_error(req, EINVAL);
-+        return;
-+    }
-+
-+    ret = sec_kv_to_ccache(state,
-+                           state->sec_key,
-+                           sec_value,
-+                           state->client,
-+                           &state->cc);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_OP_FAILURE,
-+              "Cannot convert JSON keyval to ccache blob [%d]: %s\n",
-+              ret, sss_strerror(ret));
-+        tevent_req_error(req, ret);
-+        return;
-+    }
-+
-+    DEBUG(SSSDBG_TRACE_INTERNAL, "GET done\n");
-+    tevent_req_done(req);
-+}
-+
-+static errno_t sec_get_recv(struct tevent_req *req,
-+                            TALLOC_CTX *mem_ctx,
-+                            struct kcm_ccache **_cc)
-+{
-+    struct sec_get_state *state = tevent_req_data(req, struct sec_get_state);
-+
-+    TEVENT_REQ_RETURN_ON_ERROR(req);
-+    *_cc = talloc_steal(mem_ctx, state->cc);
-+    return EOK;
-+}
-+
-+/*
-+ * Helper request to get a ccache name or ID
-+ */
-+struct sec_get_ccache_state {
-+    struct tevent_context *ev;
-+    struct ccdb_sec *secdb;
-+    struct cli_creds *client;
-+    const char *name;
-+    uuid_t uuid;
-+
-+    const char *sec_key;
-+
-+    struct kcm_ccache *cc;
-+};
-+
-+static void sec_get_ccache_list_done(struct tevent_req *subreq);
-+static void sec_get_ccache_done(struct tevent_req *subreq);
-+
-+static struct tevent_req *sec_get_ccache_send(TALLOC_CTX *mem_ctx,
-+                                              struct tevent_context *ev,
-+                                              struct ccdb_sec *secdb,
-+                                              struct cli_creds *client,
-+                                              const char *name,
-+                                              uuid_t uuid)
-+{
-+    struct tevent_req *req = NULL;
-+    struct tevent_req *subreq = NULL;
-+    struct sec_get_ccache_state *state = NULL;
-+    errno_t ret;
-+
-+    req = tevent_req_create(mem_ctx, &state, struct sec_get_ccache_state);
-+    if (req == NULL) {
-+        return NULL;
-+    }
-+    state->ev = ev;
-+    state->secdb = secdb;
-+    state->client = client;
-+    state->name = name;
-+    uuid_copy(state->uuid, uuid);
-+
-+    if ((name == NULL && uuid_is_null(uuid))
-+            || (name != NULL && !uuid_is_null(uuid))) {
-+        DEBUG(SSSDBG_OP_FAILURE, "Expected one of name, uuid to be set\n");
-+        ret = EINVAL;
-+        goto immediate;
-+    }
-+
-+    subreq = sec_list_send(state, ev, secdb, client);
-+    if (subreq == NULL) {
-+        ret = ENOMEM;
-+        goto immediate;
-+    }
-+    tevent_req_set_callback(subreq, sec_get_ccache_list_done, req);
-+    return req;
-+
-+
-+immediate:
-+    tevent_req_error(req, ret);
-+    tevent_req_post(req, ev);
-+    return req;
-+}
-+
-+static void sec_get_ccache_list_done(struct tevent_req *subreq)
-+{
-+    errno_t ret;
-+    struct tevent_req *req = tevent_req_callback_data(subreq, struct tevent_req);
-+    struct sec_get_ccache_state *state = tevent_req_data(req,
-+                                                struct sec_get_ccache_state);
-+    const char **sec_key_list;
-+
-+    ret = sec_list_recv(subreq, state, &sec_key_list, NULL);
-+    talloc_zfree(subreq);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_OP_FAILURE,
-+              "Cannot list keys [%d]: %s\n",
-+              ret, sss_strerror(ret));
-+        tevent_req_error(req, ret);
-+        return;
-+    }
-+
-+    if (state->name != NULL) {
-+        state->sec_key = find_by_name(sec_key_list, state->name);
-+    } else {
-+        state->sec_key = find_by_uuid(sec_key_list, state->uuid);
-+    }
-+
-+    if (state->sec_key == NULL) {
-+        DEBUG(SSSDBG_MINOR_FAILURE,
-+              "Cannot find item in the ccache list\n");
-+        /* Don't error out, just return an empty list */
-+        tevent_req_done(req);
-+        return;
-+    }
-+
-+    subreq = sec_get_send(state,
-+                          state->ev,
-+                          state->secdb,
-+                          state->client,
-+                          state->sec_key);
-+    if (subreq == NULL) {
-+        tevent_req_error(req, ENOMEM);
-+        return;
-+    }
-+    tevent_req_set_callback(subreq, sec_get_ccache_done, req);
-+}
-+
-+static void sec_get_ccache_done(struct tevent_req *subreq)
-+{
-+    errno_t ret;
-+    struct tevent_req *req = tevent_req_callback_data(subreq,
-+                                                struct tevent_req);
-+    struct sec_get_ccache_state *state = tevent_req_data(req,
-+                                                struct sec_get_ccache_state);
-+
-+    ret = sec_get_recv(subreq, state, &state->cc);
-+    talloc_zfree(subreq);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_OP_FAILURE,
-+              "Cannot resolve key to ccache [%d]: %s\n",
-+              ret, sss_strerror(ret));
-+        tevent_req_error(req, ret);
-+        return;
-+    }
-+
-+    tevent_req_done(req);
-+}
-+
-+static errno_t sec_get_ccache_recv(struct tevent_req *req,
-+                                   TALLOC_CTX *mem_ctx,
-+                                   struct kcm_ccache **_cc)
-+{
-+    struct sec_get_ccache_state *state = tevent_req_data(req,
-+                                                struct sec_get_ccache_state);
-+
-+    TEVENT_REQ_RETURN_ON_ERROR(req);
-+    *_cc = talloc_steal(mem_ctx, state->cc);
-+    return EOK;
-+}
-+
-+/*
-+ * The actual sssd-secrets back end
-+ */
-+static errno_t ccdb_sec_init(struct kcm_ccdb *db)
-+{
-+    struct ccdb_sec *secdb = NULL;
-+
-+    secdb = talloc_zero(db, struct ccdb_sec);
-+    if (secdb == NULL) {
-+        return ENOMEM;
-+    }
-+
-+    secdb->tctx = tcurl_init(secdb, db->ev);
-+    if (secdb->tctx == NULL) {
-+        DEBUG(SSSDBG_FATAL_FAILURE, "Cannot initialize tcurl\n");
-+        talloc_zfree(secdb);
-+        return ENOMEM;
-+   }
-+
-+    /* We just need the random numbers to generate pseudo-random ccache names
-+     * and avoid conflicts */
-+    srand(time(NULL));
-+
-+    db->db_handle = secdb;
-+    return EOK;
-+}
-+
-+/*
-+ * Helper request to get a ccache by key
-+ */
-+struct sec_patch_state {
-+    struct tevent_context *ev;
-+    struct ccdb_sec *secdb;
-+    struct cli_creds *client;
-+
-+    const char *sec_key_url;
-+    struct sss_iobuf *sec_value;
-+};
-+
-+static void sec_patch_del_done(struct tevent_req *subreq);
-+static void sec_patch_put_done(struct tevent_req *subreq);
-+
-+static struct tevent_req *sec_patch_send(TALLOC_CTX *mem_ctx,
-+                                         struct tevent_context *ev,
-+                                         struct ccdb_sec *secdb,
-+                                         struct cli_creds *client,
-+                                         const char *sec_key_url,
-+                                         struct sss_iobuf *sec_value)
-+{
-+    struct tevent_req *req = NULL;
-+    struct tevent_req *subreq = NULL;
-+    struct sec_patch_state *state = NULL;
-+    errno_t ret;
-+
-+    req = tevent_req_create(mem_ctx, &state, struct sec_patch_state);
-+    if (req == NULL) {
-+        return NULL;
-+    }
-+    state->ev = ev;
-+    state->secdb = secdb;
-+    state->client = client;
-+    state->sec_key_url = sec_key_url;
-+    state->sec_value = sec_value;
-+
-+    subreq = tcurl_http_send(state, state->ev,
-+                             state->secdb->tctx,
-+                             TCURL_HTTP_DELETE,
-+                             SSSD_SECRETS_SOCKET,
-+                             state->sec_key_url,
-+                             sec_headers,
-+                             NULL,
-+                             SEC_TIMEOUT);
-+    if (subreq == NULL) {
-+        ret = ENOMEM;
-+        goto immediate;
-+    }
-+    tevent_req_set_callback(subreq, sec_patch_del_done, req);
-+    return req;
-+
-+immediate:
-+    tevent_req_error(req, ret);
-+    tevent_req_post(req, ev);
-+    return req;
-+}
-+
-+static void sec_patch_del_done(struct tevent_req *subreq)
-+{
-+    errno_t ret;
-+    struct tevent_req *req = tevent_req_callback_data(subreq,
-+                                                      struct tevent_req);
-+    struct sec_patch_state *state = tevent_req_data(req,
-+                                                struct sec_patch_state);
-+    int http_code;
-+
-+    ret = tcurl_http_recv(state, subreq, &http_code, NULL);
-+    talloc_zfree(subreq);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_OP_FAILURE,
-+              "Cannot delete key [%d]: %s\n", ret, sss_strerror(ret));
-+        tevent_req_error(req, ret);
-+        return;
-+    }
-+
-+    if (http_code == 404) {
-+        DEBUG(SSSDBG_TRACE_LIBS,
-+              "Key %s does not exist, moving on\n", state->sec_key_url);
-+    } else if (http_code != 200) {
-+        ret = http2errno(http_code);
-+        tevent_req_error(req, ret);
-+        return;
-+    }
-+
-+    DEBUG(SSSDBG_TRACE_INTERNAL, "Adding new payload\n");
-+
-+    subreq = tcurl_http_send(state,
-+                             state->ev,
-+                             state->secdb->tctx,
-+                             TCURL_HTTP_PUT,
-+                             SSSD_SECRETS_SOCKET,
-+                             state->sec_key_url,
-+                             sec_headers,
-+                             state->sec_value,
-+                             SEC_TIMEOUT);
-+    if (subreq == NULL) {
-+        tevent_req_error(req, ENOMEM);
-+        return;
-+    }
-+    tevent_req_set_callback(subreq, sec_patch_put_done, req);
-+}
-+
-+static void sec_patch_put_done(struct tevent_req *subreq)
-+{
-+    errno_t ret;
-+    struct tevent_req *req = tevent_req_callback_data(subreq,
-+                                                      struct tevent_req);
-+    struct sec_patch_state *state = tevent_req_data(req,
-+                                                struct sec_patch_state);
-+    int http_code;
-+
-+    ret = tcurl_http_recv(state, subreq, &http_code, NULL);
-+    talloc_zfree(subreq);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_OP_FAILURE,
-+              "Cannot put new value [%d]: %s\n", ret, sss_strerror(ret));
-+        tevent_req_error(req, ret);
-+        return;
-+    }
-+
-+    if (http_code != 200) {
-+        DEBUG(SSSDBG_OP_FAILURE, "Failed to add the payload\n");
-+        ret = http2errno(http_code);
-+        tevent_req_error(req, ret);
-+        return;
-+    }
-+
-+    DEBUG(SSSDBG_TRACE_INTERNAL, "payload created\n");
-+    tevent_req_done(req);
-+}
-+
-+
-+static errno_t sec_patch_recv(struct tevent_req *req)
-+{
-+    TEVENT_REQ_RETURN_ON_ERROR(req);
-+    return EOK;
-+}
-+
-+/* The operations between the KCM and sssd-secrets */
-+
-+struct ccdb_sec_nextid_state {
-+    struct tevent_context *ev;
-+    struct ccdb_sec *secdb;
-+    struct cli_creds *client;
-+
-+    unsigned int nextid;
-+    char *nextid_name;
-+
-+    int maxtries;
-+    int numtry;
-+};
-+
-+static errno_t ccdb_sec_nextid_generate(struct tevent_req *req);
-+static void ccdb_sec_nextid_list_done(struct tevent_req *subreq);
-+
-+/* Generate a unique ID */
-+/* GET the name from secrets, if doesn't exist, OK, if exists, try again */
-+static struct tevent_req *ccdb_sec_nextid_send(TALLOC_CTX *mem_ctx,
-+                                               struct tevent_context *ev,
-+                                               struct kcm_ccdb *db,
-+                                               struct cli_creds *client)
-+{
-+    struct tevent_req *req = NULL;
-+    struct ccdb_sec_nextid_state *state = NULL;
-+    struct ccdb_sec *secdb = talloc_get_type(db->db_handle, struct ccdb_sec);
-+    errno_t ret;
-+
-+    req = tevent_req_create(mem_ctx, &state, struct ccdb_sec_nextid_state);
-+    if (req == NULL) {
-+        return NULL;
-+    }
-+    state->ev = ev;
-+    state->secdb = secdb;
-+    state->client = client;
-+
-+    state->maxtries = 3;
-+    state->numtry = 0;
-+
-+    ret = ccdb_sec_nextid_generate(req);
-+    if (ret != EOK) {
-+        goto immediate;
-+    }
-+
-+    return req;
-+
-+immediate:
-+    tevent_req_error(req, ret);
-+    tevent_req_post(req, ev);
-+    return req;
-+}
-+
-+static errno_t ccdb_sec_nextid_generate(struct tevent_req *req)
-+{
-+    struct tevent_req *subreq = NULL;
-+    struct ccdb_sec_nextid_state *state = tevent_req_data(req,
-+                                                struct ccdb_sec_nextid_state);
-+
-+    if (state->numtry >= state->maxtries) {
-+        DEBUG(SSSDBG_CRIT_FAILURE,
-+              "Failed to find a random ccache in %d tries\n", state->numtry);
-+        return EBUSY;
-+    }
-+
-+    state->nextid = rand() % MAX_CC_NUM;
-+    state->nextid_name = talloc_asprintf(state, "%"SPRIuid":%u",
-+                                         cli_creds_get_uid(state->client),
-+                                         state->nextid);
-+    if (state->nextid_name == NULL) {
-+        return ENOMEM;
-+    }
-+
-+    subreq = sec_list_send(state, state->ev, state->secdb, state->client);
-+    if (subreq == NULL) {
-+        return ENOMEM;
-+    }
-+    tevent_req_set_callback(subreq, ccdb_sec_nextid_list_done, req);
-+
-+    state->numtry++;
-+    return EOK;
-+}
-+
-+static void ccdb_sec_nextid_list_done(struct tevent_req *subreq)
-+{
-+    errno_t ret;
-+    struct tevent_req *req = tevent_req_callback_data(subreq,
-+                                                      struct tevent_req);
-+    struct ccdb_sec_nextid_state *state = tevent_req_data(req,
-+                                                struct ccdb_sec_nextid_state);
-+    const char **sec_key_list;
-+    size_t sec_key_list_len;
-+    size_t i;
-+
-+    ret = sec_list_recv(subreq, state, &sec_key_list, &sec_key_list_len);
-+    talloc_zfree(subreq);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_OP_FAILURE,
-+              "Cannot list keys [%d]: %s\n",
-+              ret, sss_strerror(ret));
-+        tevent_req_error(req, ret);
-+        return;
-+    }
-+
-+    for (i = 0; i < sec_key_list_len; i++) {
-+        if (sec_key_match_name(sec_key_list[i], state->nextid_name) == true) {
-+            break;
-+        }
-+    }
-+
-+    DEBUG(SSSDBG_MINOR_FAILURE,
-+          "Failed to find a random key, trying again..\n");
-+    if (i < sec_key_list_len) {
-+        /* Try again */
-+        ret = ccdb_sec_nextid_generate(req);
-+        if (ret != EOK) {
-+            tevent_req_error(req, ret);
-+            return;
-+        }
-+
-+        return;
-+    }
-+
-+    DEBUG(SSSDBG_TRACE_INTERNAL,
-+          "Generated new ccache name %u\n", state->nextid);
-+    tevent_req_done(req);
-+}
-+
-+static errno_t ccdb_sec_nextid_recv(struct tevent_req *req,
-+                                    unsigned int *_nextid)
-+{
-+    struct ccdb_sec_nextid_state *state = tevent_req_data(req,
-+                                                struct ccdb_sec_nextid_state);
-+
-+    TEVENT_REQ_RETURN_ON_ERROR(req);
-+    *_nextid = state->nextid;
-+    return EOK;
-+}
-+
-+/* IN:  HTTP PUT $base/default -d 'uuid' */
-+/* We chose only UUID here to avoid issues later with renaming */
-+struct ccdb_sec_set_default_state {
-+};
-+
-+static void ccdb_sec_set_default_done(struct tevent_req *subreq);
-+
-+static struct tevent_req *ccdb_sec_set_default_send(TALLOC_CTX *mem_ctx,
-+                                                    struct tevent_context *ev,
-+                                                    struct kcm_ccdb *db,
-+                                                    struct cli_creds *client,
-+                                                    uuid_t uuid)
-+{
-+    struct tevent_req *req = NULL;
-+    struct tevent_req *subreq = NULL;
-+    struct ccdb_sec_set_default_state *state = NULL;
-+    struct ccdb_sec *secdb = talloc_get_type(db->db_handle, struct ccdb_sec);
-+    struct sss_iobuf *uuid_iobuf;
-+    errno_t ret;
-+    const char *url;
-+    char uuid_str[UUID_STR_SIZE];
-+
-+    req = tevent_req_create(mem_ctx, &state, struct ccdb_sec_set_default_state);
-+    if (req == NULL) {
-+        return NULL;
-+    }
-+
-+    uuid_unparse(uuid, uuid_str);
-+    DEBUG(SSSDBG_TRACE_INTERNAL,
-+          "Setting the default ccache to %s\n", uuid_str);
-+
-+    url = sec_dfl_url_create(state, client);
-+    if (url == NULL) {
-+        ret = ENOMEM;
-+        goto immediate;
-+    }
-+
-+    uuid_iobuf = sss_iobuf_init_readonly(state,
-+                                         (uint8_t *) uuid_str,
-+                                         UUID_STR_SIZE);
-+    if (uuid_iobuf == NULL) {
-+        ret = ENOMEM;
-+        goto immediate;
-+    }
-+
-+    subreq = sec_patch_send(state, ev, secdb, client, url, uuid_iobuf);
-+    if (subreq == NULL) {
-+        ret = ENOMEM;
-+        goto immediate;
-+    }
-+    tevent_req_set_callback(subreq, ccdb_sec_set_default_done, req);
-+    return req;
-+
-+immediate:
-+    tevent_req_error(req, ret);
-+    tevent_req_post(req, ev);
-+    return req;
-+}
-+
-+static void ccdb_sec_set_default_done(struct tevent_req *subreq)
-+{
-+    errno_t ret;
-+    struct tevent_req *req = tevent_req_callback_data(subreq, struct tevent_req);
-+
-+    ret = sec_patch_recv(subreq);
-+    talloc_zfree(subreq);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_OP_FAILURE,
-+              "sec_patch request failed [%d]: %s\n",
-+              ret, sss_strerror(ret));
-+        tevent_req_error(req, ret);
-+        return;
-+    }
-+
-+    DEBUG(SSSDBG_TRACE_INTERNAL, "Set the default ccache\n");
-+    tevent_req_done(req);
-+}
-+
-+static errno_t ccdb_sec_set_default_recv(struct tevent_req *req)
-+{
-+    TEVENT_REQ_RETURN_ON_ERROR(req);
-+    return EOK;
-+}
-+
-+/* IN:  HTTP GET $base/default  */
-+/* OUT: uuid */
-+struct ccdb_sec_get_default_state {
-+    uuid_t uuid;
-+};
-+
-+static void ccdb_sec_get_default_done(struct tevent_req *subreq);
-+
-+static struct tevent_req *ccdb_sec_get_default_send(TALLOC_CTX *mem_ctx,
-+                                                    struct tevent_context *ev,
-+                                                    struct kcm_ccdb *db,
-+                                                    struct cli_creds *client)
-+{
-+    struct tevent_req *req = NULL;
-+    struct tevent_req *subreq = NULL;
-+    struct ccdb_sec_get_default_state *state = NULL;
-+    struct ccdb_sec *secdb = talloc_get_type(db->db_handle, struct ccdb_sec);
-+    const char *url;
-+    errno_t ret;
-+
-+    req = tevent_req_create(mem_ctx, &state, struct ccdb_sec_get_default_state);
-+    if (req == NULL) {
-+        return NULL;
-+    }
-+
-+    DEBUG(SSSDBG_TRACE_INTERNAL, "Getting the default ccache\n");
-+    url = sec_dfl_url_create(state, client);
-+    if (url == NULL) {
-+        ret = ENOMEM;
-+        goto immediate;
-+    }
-+
-+    subreq = tcurl_http_send(state, ev, secdb->tctx,
-+                             TCURL_HTTP_GET,
-+                             SSSD_SECRETS_SOCKET,
-+                             url,
-+                             sec_headers,
-+                             NULL,
-+                             SEC_TIMEOUT);
-+    if (subreq == NULL) {
-+        ret = ENOMEM;
-+        goto immediate;
-+    }
-+    tevent_req_set_callback(subreq, ccdb_sec_get_default_done, req);
-+    return req;
-+
-+immediate:
-+    tevent_req_error(req, ret);
-+    tevent_req_post(req, ev);
-+    return req;
-+}
-+
-+static void ccdb_sec_get_default_done(struct tevent_req *subreq)
-+{
-+    errno_t ret;
-+    int http_code;
-+    struct tevent_req *req = tevent_req_callback_data(subreq, struct tevent_req);
-+    struct ccdb_sec_get_default_state *state = tevent_req_data(req,
-+                                                struct ccdb_sec_get_default_state);
-+    struct sss_iobuf *outbuf;
-+    size_t uuid_size;
-+
-+    ret = tcurl_http_recv(state, subreq, &http_code, &outbuf);
-+    talloc_zfree(subreq);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_OP_FAILURE,
-+              "Communication with the secrets responder failed [%d]: %s\n",
-+              ret, sss_strerror(ret));
-+        tevent_req_error(req, ret);
-+        return;
-+    }
-+
-+    if (http_code == 404) {
-+        /* Return a NULL uuid */
-+        uuid_clear(state->uuid);
-+        tevent_req_done(req);
-+        return;
-+    } else if (http_code != 200) {
-+        ret = http2errno(http_code);
-+        tevent_req_error(req, ret);
-+        return;
-+    }
-+
-+    uuid_size = sss_iobuf_get_len(outbuf);
-+    if (uuid_size != UUID_STR_SIZE) {
-+        DEBUG(SSSDBG_OP_FAILURE, "Unexpected UUID size %zu\n", uuid_size);
-+        tevent_req_error(req, EIO);
-+        return;
-+    }
-+
-+    uuid_parse((const char *) sss_iobuf_get_data(outbuf), state->uuid);
-+    DEBUG(SSSDBG_TRACE_INTERNAL, "Got the default ccache\n");
-+    tevent_req_done(req);
-+}
-+
-+static errno_t ccdb_sec_get_default_recv(struct tevent_req *req,
-+                                         uuid_t uuid)
-+{
-+    struct ccdb_sec_get_default_state *state = tevent_req_data(req,
-+                                                struct ccdb_sec_get_default_state);
-+
-+    TEVENT_REQ_RETURN_ON_ERROR(req);
-+
-+    uuid_copy(uuid, state->uuid);
-+    return EOK;
-+}
-+
-+/* HTTP GET $base/ccache/  */
-+/* OUT: a list of <uuid:name, uuid:name> */
-+struct ccdb_sec_list_state {
-+    uuid_t *uuid_list;
-+};
-+
-+static void ccdb_sec_list_done(struct tevent_req *subreq);
-+
-+static struct tevent_req *ccdb_sec_list_send(TALLOC_CTX *mem_ctx,
-+                                             struct tevent_context *ev,
-+                                             struct kcm_ccdb *db,
-+                                             struct cli_creds *client)
-+{
-+    struct tevent_req *req = NULL;
-+    struct tevent_req *subreq = NULL;
-+    struct ccdb_sec_list_state *state = NULL;
-+    struct ccdb_sec *secdb = talloc_get_type(db->db_handle, struct ccdb_sec);
-+    errno_t ret;
-+
-+    req = tevent_req_create(mem_ctx, &state, struct ccdb_sec_list_state);
-+    if (req == NULL) {
-+        return NULL;
-+    }
-+
-+    DEBUG(SSSDBG_TRACE_INTERNAL, "Listing all ccaches\n");
-+
-+    subreq = sec_list_send(state, ev, secdb, client);
-+    if (subreq == NULL) {
-+        ret = ENOMEM;
-+        goto immediate;
-+    }
-+    tevent_req_set_callback(subreq, ccdb_sec_list_done, req);
-+    return req;
-+
-+immediate:
-+    tevent_req_error(req, ret);
-+    tevent_req_post(req, ev);
-+    return req;
-+}
-+
-+static void ccdb_sec_list_done(struct tevent_req *subreq)
-+{
-+    errno_t ret;
-+    struct tevent_req *req = tevent_req_callback_data(subreq,
-+                                                      struct tevent_req);
-+    struct ccdb_sec_list_state *state = tevent_req_data(req,
-+                                                struct ccdb_sec_list_state);
-+    const char **sec_key_list;
-+    size_t sec_key_list_len;
-+
-+    ret = sec_list_recv(subreq, state, &sec_key_list, &sec_key_list_len);
-+    talloc_zfree(subreq);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_OP_FAILURE,
-+              "Communication with the secrets responder failed [%d]: %s\n",
-+              ret, sss_strerror(ret));
-+        tevent_req_error(req, ret);
-+        return;
-+    }
-+
-+    DEBUG(SSSDBG_TRACE_INTERNAL, "Found %zu ccaches\n", sec_key_list_len);
-+
-+    state->uuid_list = talloc_array(state, uuid_t, sec_key_list_len + 1);
-+    if (state->uuid_list == NULL) {
-+        tevent_req_error(req, ENOMEM);
-+        return;
-+    }
-+
-+    for (size_t i = 0; i < sec_key_list_len; i++) {
-+        ret = sec_key_get_uuid(sec_key_list[i],
-+                               state->uuid_list[i]);
-+        if (ret != EOK) {
-+            tevent_req_error(req, ret);
-+            return;
-+        }
-+    }
-+    /* Sentinel */
-+    uuid_clear(state->uuid_list[sec_key_list_len]);
-+
-+    DEBUG(SSSDBG_TRACE_INTERNAL, "Listing all caches done\n");
-+    tevent_req_done(req);
-+}
-+
-+static errno_t ccdb_sec_list_recv(struct tevent_req *req,
-+                                  TALLOC_CTX *mem_ctx,
-+                                  uuid_t **_uuid_list)
-+{
-+    struct ccdb_sec_list_state *state = tevent_req_data(req,
-+                                                struct ccdb_sec_list_state);
-+
-+    TEVENT_REQ_RETURN_ON_ERROR(req);
-+    *_uuid_list = talloc_steal(mem_ctx, state->uuid_list);
-+    return EOK;
-+}
-+
-+struct ccdb_sec_getbyuuid_state {
-+    struct kcm_ccache *cc;
-+};
-+
-+
-+/* HTTP GET $base/ccache/  */
-+/* OUT: a list of <uuid:name, uuid:name> */
-+/* for each item in list, compare with the uuid: portion */
-+/* HTTP GET $base/ccache/uuid:name  */
-+/* return result */
-+static void ccdb_sec_getbyuuid_done(struct tevent_req *subreq);
-+
-+static struct tevent_req *ccdb_sec_getbyuuid_send(TALLOC_CTX *mem_ctx,
-+                                                  struct tevent_context *ev,
-+                                                  struct kcm_ccdb *db,
-+                                                  struct cli_creds *client,
-+                                                  uuid_t uuid)
-+{
-+    errno_t ret;
-+    struct tevent_req *req = NULL;
-+    struct tevent_req *subreq = NULL;
-+    struct ccdb_sec_getbyuuid_state *state = NULL;
-+    struct ccdb_sec *secdb = talloc_get_type(db->db_handle, struct ccdb_sec);
-+
-+    req = tevent_req_create(mem_ctx, &state, struct ccdb_sec_getbyuuid_state);
-+    if (req == NULL) {
-+        return NULL;
-+    }
-+
-+    DEBUG(SSSDBG_TRACE_INTERNAL, "Getting ccache by UUID\n");
-+
-+    subreq = sec_get_ccache_send(state, ev, secdb, client, NULL, uuid);
-+    if (subreq == NULL) {
-+        ret = ENOMEM;
-+        goto immediate;
-+    }
-+    tevent_req_set_callback(subreq, ccdb_sec_getbyuuid_done, req);
-+    return req;
-+
-+immediate:
-+    tevent_req_error(req, ret);
-+    tevent_req_post(req, ev);
-+    return req;
-+}
-+
-+static void ccdb_sec_getbyuuid_done(struct tevent_req *subreq)
-+{
-+    errno_t ret;
-+    struct tevent_req *req = tevent_req_callback_data(subreq,
-+                                                      struct tevent_req);
-+    struct ccdb_sec_getbyuuid_state *state = tevent_req_data(req,
-+                                            struct ccdb_sec_getbyuuid_state);
-+
-+    ret = sec_get_ccache_recv(subreq, state, &state->cc);
-+    talloc_zfree(subreq);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_OP_FAILURE,
-+              "Cannot retrieve the ccache [%d]: %s\n", ret, sss_strerror(ret));
-+        tevent_req_error(req, ret);
-+        return;
-+    }
-+
-+    DEBUG(SSSDBG_TRACE_INTERNAL, "Got ccache by UUID\n");
-+    tevent_req_done(req);
-+}
-+
-+static errno_t ccdb_sec_getbyuuid_recv(struct tevent_req *req,
-+                                       TALLOC_CTX *mem_ctx,
-+                                       struct kcm_ccache **_cc)
-+{
-+    struct ccdb_sec_getbyuuid_state *state = tevent_req_data(req,
-+                                            struct ccdb_sec_getbyuuid_state);
-+
-+    TEVENT_REQ_RETURN_ON_ERROR(req);
-+    *_cc = talloc_steal(mem_ctx, state->cc);
-+    return EOK;
-+}
-+
-+/* HTTP GET $base/ccache/  */
-+/* OUT: a list of <uuid:name, uuid:name> */
-+/* for each item in list, compare with the :name portion */
-+/* HTTP GET $base/ccache/uuid:name  */
-+/* return result */
-+struct ccdb_sec_getbyname_state {
-+    struct kcm_ccache *cc;
-+};
-+
-+static void ccdb_sec_getbyname_done(struct tevent_req *subreq);
-+
-+static struct tevent_req *ccdb_sec_getbyname_send(TALLOC_CTX *mem_ctx,
-+                                                  struct tevent_context *ev,
-+                                                  struct kcm_ccdb *db,
-+                                                  struct cli_creds *client,
-+                                                  const char *name)
-+{
-+    errno_t ret;
-+    struct tevent_req *req = NULL;
-+    struct tevent_req *subreq = NULL;
-+    struct ccdb_sec_getbyname_state *state = NULL;
-+    struct ccdb_sec *secdb = talloc_get_type(db->db_handle, struct ccdb_sec);
-+    uuid_t null_uuid;
-+
-+    req = tevent_req_create(mem_ctx, &state, struct ccdb_sec_getbyname_state);
-+    if (req == NULL) {
-+        return NULL;
-+    }
-+    uuid_clear(null_uuid);
-+
-+    DEBUG(SSSDBG_TRACE_INTERNAL, "Getting ccache by name\n");
-+
-+    subreq = sec_get_ccache_send(state, ev, secdb, client, name, null_uuid);
-+    if (subreq == NULL) {
-+        ret = ENOMEM;
-+        goto immediate;
-+    }
-+    tevent_req_set_callback(subreq, ccdb_sec_getbyname_done, req);
-+    return req;
-+
-+immediate:
-+    tevent_req_error(req, ret);
-+    tevent_req_post(req, ev);
-+    return req;
-+}
-+
-+static void ccdb_sec_getbyname_done(struct tevent_req *subreq)
-+{
-+    errno_t ret;
-+    struct tevent_req *req = tevent_req_callback_data(subreq,
-+                                                      struct tevent_req);
-+    struct ccdb_sec_getbyname_state *state = tevent_req_data(req,
-+                                            struct ccdb_sec_getbyname_state);
-+
-+    ret = sec_get_ccache_recv(subreq, state, &state->cc);
-+    talloc_zfree(subreq);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_OP_FAILURE,
-+              "Cannot retrieve the ccache [%d]: %s\n", ret, sss_strerror(ret));
-+        tevent_req_error(req, ret);
-+        return;
-+    }
-+
-+    DEBUG(SSSDBG_TRACE_INTERNAL, "Got ccache by UUID\n");
-+    tevent_req_done(req);
-+}
-+
-+static errno_t ccdb_sec_getbyname_recv(struct tevent_req *req,
-+                                       TALLOC_CTX *mem_ctx,
-+                                       struct kcm_ccache **_cc)
-+{
-+    struct ccdb_sec_getbyname_state *state = tevent_req_data(req,
-+                                                struct ccdb_sec_getbyname_state);
-+
-+    TEVENT_REQ_RETURN_ON_ERROR(req);
-+    *_cc = talloc_steal(mem_ctx, state->cc);
-+    return EOK;
-+}
-+
-+struct ccdb_sec_name_by_uuid_state {
-+    struct tevent_context *ev;
-+    struct ccdb_sec *secdb;
-+    struct cli_creds *client;
-+
-+    uuid_t uuid;
-+
-+    const char *name;
-+};
-+
-+static void ccdb_sec_name_by_uuid_done(struct tevent_req *subreq);
-+
-+struct tevent_req *ccdb_sec_name_by_uuid_send(TALLOC_CTX *sec_ctx,
-+                                              struct tevent_context *ev,
-+                                              struct kcm_ccdb *db,
-+                                              struct cli_creds *client,
-+                                              uuid_t uuid)
-+{
-+    struct tevent_req *req = NULL;
-+    struct tevent_req *subreq = NULL;
-+    struct ccdb_sec_name_by_uuid_state *state = NULL;
-+    struct ccdb_sec *secdb = talloc_get_type(db->db_handle, struct ccdb_sec);
-+    errno_t ret;
-+
-+    req = tevent_req_create(sec_ctx, &state, struct ccdb_sec_name_by_uuid_state);
-+    if (req == NULL) {
-+        return NULL;
-+    }
-+    state->ev = ev;
-+    state->secdb = secdb;
-+    state->client = client;
-+    uuid_copy(state->uuid, uuid);
-+
-+    DEBUG(SSSDBG_TRACE_INTERNAL, "Translating UUID to name\n");
-+
-+    subreq = sec_list_send(state, state->ev, state->secdb, state->client);
-+    if (subreq == NULL) {
-+        ret = ENOMEM;
-+        goto immediate;
-+    }
-+    tevent_req_set_callback(subreq, ccdb_sec_name_by_uuid_done, req);
-+    return req;
-+
-+immediate:
-+    tevent_req_error(req, ret);
-+    tevent_req_post(req, ev);
-+    return req;
-+}
-+
-+static void ccdb_sec_name_by_uuid_done(struct tevent_req *subreq)
-+{
-+    errno_t ret;
-+    struct tevent_req *req = tevent_req_callback_data(subreq,
-+                                                      struct tevent_req);
-+    struct ccdb_sec_name_by_uuid_state *state = tevent_req_data(req,
-+                                                struct ccdb_sec_name_by_uuid_state);
-+    const char **sec_key_list;
-+    const char *name;
-+    size_t sec_key_list_len;
-+    size_t i;
-+
-+    ret = sec_list_recv(subreq, state, &sec_key_list, &sec_key_list_len);
-+    talloc_zfree(subreq);
-+    if (ret != EOK) {
-+        tevent_req_error(req, ret);
-+        return;
-+    }
-+
-+    for (i = 0; i < sec_key_list_len; i++) {
-+        if (sec_key_match_uuid(sec_key_list[i], state->uuid) == true) {
-+            /* Match, copy name */
-+            name = sec_key_get_name(sec_key_list[i]);
-+            if (name == NULL) {
-+                DEBUG(SSSDBG_CRIT_FAILURE,
-+                       "Malformed key, cannot get name\n");
-+                tevent_req_error(req, EINVAL);
-+                return;
-+            }
-+
-+            state->name = talloc_strdup(state, name);
-+            if (state->name == NULL) {
-+                tevent_req_error(req, ENOMEM);
-+                return;
-+            }
-+
-+            DEBUG(SSSDBG_TRACE_INTERNAL, "Got ccache by UUID\n");
-+            tevent_req_done(req);
-+            return;
-+        }
-+    }
-+
-+    DEBUG(SSSDBG_TRACE_INTERNAL, "No such UUID\n");
-+    tevent_req_error(req, ERR_NO_CREDS);
-+    return;
-+}
-+
-+errno_t ccdb_sec_name_by_uuid_recv(struct tevent_req *req,
-+                                   TALLOC_CTX *sec_ctx,
-+                                   const char **_name)
-+{
-+    struct ccdb_sec_name_by_uuid_state *state = tevent_req_data(req,
-+                                                struct ccdb_sec_name_by_uuid_state);
-+    TEVENT_REQ_RETURN_ON_ERROR(req);
-+    *_name = talloc_steal(sec_ctx, state->name);
-+    return EOK;
-+}
-+
-+struct ccdb_sec_uuid_by_name_state {
-+    struct tevent_context *ev;
-+    struct ccdb_sec *secdb;
-+    struct cli_creds *client;
-+
-+    const char *name;
-+
-+    uuid_t uuid;
-+};
-+
-+static void ccdb_sec_uuid_by_name_done(struct tevent_req *subreq);
-+
-+struct tevent_req *ccdb_sec_uuid_by_name_send(TALLOC_CTX *sec_ctx,
-+                                              struct tevent_context *ev,
-+                                              struct kcm_ccdb *db,
-+                                              struct cli_creds *client,
-+                                              const char *name)
-+{
-+    struct tevent_req *req = NULL;
-+    struct tevent_req *subreq = NULL;
-+    struct ccdb_sec_uuid_by_name_state *state = NULL;
-+    struct ccdb_sec *secdb = talloc_get_type(db->db_handle, struct ccdb_sec);
-+    errno_t ret;
-+
-+    req = tevent_req_create(sec_ctx, &state, struct ccdb_sec_uuid_by_name_state);
-+    if (req == NULL) {
-+        return NULL;
-+    }
-+    state->ev = ev;
-+    state->secdb = secdb;
-+    state->client = client;
-+    state->name = name;
-+
-+    DEBUG(SSSDBG_TRACE_INTERNAL, "Translating name to UUID\n");
-+
-+    subreq = sec_list_send(state, state->ev, state->secdb, state->client);
-+    if (subreq == NULL) {
-+        ret = ENOMEM;
-+        goto immediate;
-+    }
-+    tevent_req_set_callback(subreq, ccdb_sec_uuid_by_name_done, req);
-+    return req;
-+
-+immediate:
-+    tevent_req_error(req, ret);
-+    tevent_req_post(req, ev);
-+    return req;
-+}
-+
-+static void ccdb_sec_uuid_by_name_done(struct tevent_req *subreq)
-+{
-+    errno_t ret;
-+    struct tevent_req *req = tevent_req_callback_data(subreq,
-+                                                      struct tevent_req);
-+    struct ccdb_sec_uuid_by_name_state *state = tevent_req_data(req,
-+                                                struct ccdb_sec_uuid_by_name_state);
-+    const char **sec_key_list;
-+    size_t sec_key_list_len;
-+    size_t i;
-+
-+    ret = sec_list_recv(subreq, state, &sec_key_list, &sec_key_list_len);
-+    talloc_zfree(subreq);
-+    if (ret != EOK) {
-+        tevent_req_error(req, ret);
-+        return;
-+    }
-+
-+    for (i = 0; i < sec_key_list_len; i++) {
-+        if (sec_key_match_name(sec_key_list[i], state->name) == true) {
-+            /* Match, copy UUID */
-+            ret = sec_key_get_uuid(sec_key_list[i], state->uuid);
-+            if (ret != EOK) {
-+                DEBUG(SSSDBG_CRIT_FAILURE,
-+                       "Malformed key, cannot get UUID\n");
-+                tevent_req_error(req, ret);
-+                return;
-+            }
-+
-+            DEBUG(SSSDBG_TRACE_INTERNAL, "Got ccache by name\n");
-+            tevent_req_done(req);
-+            return;
-+        }
-+    }
-+
-+    DEBUG(SSSDBG_TRACE_INTERNAL, "No such name\n");
-+    tevent_req_error(req, ERR_NO_CREDS);
-+    return;
-+}
-+
-+errno_t ccdb_sec_uuid_by_name_recv(struct tevent_req *req,
-+                                   TALLOC_CTX *sec_ctx,
-+                                   uuid_t _uuid)
-+{
-+    struct ccdb_sec_uuid_by_name_state *state = tevent_req_data(req,
-+                                                struct ccdb_sec_uuid_by_name_state);
-+    TEVENT_REQ_RETURN_ON_ERROR(req);
-+    uuid_copy(_uuid, state->uuid);
-+    return EOK;
-+}
-+
-+/* HTTP POST $base to create the container */
-+/* HTTP PUT $base to create the container. Since PUT errors out on duplicates, at least
-+ * we fail consistently here and don't overwrite the ccache on concurrent requests
-+ */
-+struct ccdb_sec_create_state {
-+    struct tevent_context *ev;
-+    struct ccdb_sec *secdb;
-+
-+    const char *key_url;
-+    struct sss_iobuf *ccache_payload;
-+};
-+
-+static void ccdb_sec_container_done(struct tevent_req *subreq);
-+static void ccdb_sec_ccache_done(struct tevent_req *subreq);
-+
-+static struct tevent_req *ccdb_sec_create_send(TALLOC_CTX *mem_ctx,
-+                                               struct tevent_context *ev,
-+                                               struct kcm_ccdb *db,
-+                                               struct cli_creds *client,
-+                                               struct kcm_ccache *cc)
-+{
-+    struct tevent_req *subreq = NULL;
-+    struct tevent_req *req = NULL;
-+    struct ccdb_sec_create_state *state = NULL;
-+    struct ccdb_sec *secdb = talloc_get_type(db->db_handle, struct ccdb_sec);
-+    errno_t ret;
-+    const char *container_url;
-+
-+    req = tevent_req_create(mem_ctx, &state, struct ccdb_sec_create_state);
-+    if (req == NULL) {
-+        return NULL;
-+    }
-+    state->ev = ev;
-+    state->secdb = secdb;
-+
-+    DEBUG(SSSDBG_TRACE_INTERNAL, "Creating ccache storage for %s\n", cc->name);
-+
-+    /* Do the encoding asap so that if we fail, we don't even attempt any
-+     * writes */
-+    ret = kcm_ccache_to_sec_input(state, cc, client, &state->key_url, &state->ccache_payload);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_CRIT_FAILURE,
-+              "Cannot convert cache %s to JSON [%d]: %s\n",
-+              cc->name, ret, sss_strerror(ret));
-+        goto immediate;
-+    }
-+
-+    container_url = sec_container_url_create(state, client);
-+    if (container_url == NULL) {
-+        ret = ENOMEM;
-+        goto immediate;
-+    }
-+
-+    DEBUG(SSSDBG_TRACE_INTERNAL, "Creating the ccache container\n");
-+    subreq = tcurl_http_send(state, ev, secdb->tctx,
-+                             TCURL_HTTP_POST,
-+                             SSSD_SECRETS_SOCKET,
-+                             container_url,
-+                             sec_headers,
-+                             NULL,
-+                             SEC_TIMEOUT);
-+    if (subreq == NULL) {
-+        ret = ENOMEM;
-+        goto immediate;
-+    }
-+    tevent_req_set_callback(subreq, ccdb_sec_container_done, req);
-+    return req;
-+
-+immediate:
-+    tevent_req_error(req, ret);
-+    tevent_req_post(req, ev);
-+    return req;
-+}
-+
-+static void ccdb_sec_container_done(struct tevent_req *subreq)
-+{
-+    errno_t ret;
-+    int http_code;
-+    struct tevent_req *req = tevent_req_callback_data(subreq, struct tevent_req);
-+    struct ccdb_sec_create_state *state = tevent_req_data(req,
-+                                                struct ccdb_sec_create_state);
-+
-+    ret = tcurl_http_recv(state, subreq, &http_code, NULL);
-+    talloc_zfree(subreq);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_OP_FAILURE,
-+              "Communication with the secrets responder failed [%d]: %s\n",
-+              ret, sss_strerror(ret));
-+        tevent_req_error(req, ret);
-+        return;
-+    }
-+
-+    /* Conflict is not an error as multiple ccaches are under the same
-+     * container */
-+    if (http_code == 409) {
-+        DEBUG(SSSDBG_TRACE_INTERNAL, "Container already exists, ignoring\n");
-+    } else if (http_code != 200) {
-+        DEBUG(SSSDBG_OP_FAILURE, "Failed to create the ccache container\n");
-+        ret = http2errno(http_code);
-+        tevent_req_error(req, ret);
-+        return;
-+    }
-+
-+    DEBUG(SSSDBG_TRACE_INTERNAL, "ccache container created\n");
-+    DEBUG(SSSDBG_TRACE_INTERNAL, "creating empty ccache payload\n");
-+
-+    subreq = tcurl_http_send(state,
-+                             state->ev,
-+                             state->secdb->tctx,
-+                             TCURL_HTTP_PUT,
-+                             SSSD_SECRETS_SOCKET,
-+                             state->key_url,
-+                             sec_headers,
-+                             state->ccache_payload,
-+                             SEC_TIMEOUT);
-+    if (subreq == NULL) {
-+        tevent_req_error(req, ENOMEM);
-+        return;
-+    }
-+    tevent_req_set_callback(subreq, ccdb_sec_ccache_done, req);
-+}
-+
-+static void ccdb_sec_ccache_done(struct tevent_req *subreq)
-+{
-+    errno_t ret;
-+    int http_code;
-+    struct tevent_req *req = tevent_req_callback_data(subreq, struct tevent_req);
-+    struct ccdb_sec_create_state *state = tevent_req_data(req,
-+                                                struct ccdb_sec_create_state);
-+
-+    ret = tcurl_http_recv(state, subreq, &http_code, NULL);
-+    talloc_zfree(subreq);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_OP_FAILURE,
-+              "Communication with the secrets responder failed [%d]: %s\n",
-+              ret, sss_strerror(ret));
-+        tevent_req_error(req, ret);
-+        return;
-+    }
-+
-+    if (http_code != 200) {
-+        DEBUG(SSSDBG_OP_FAILURE, "Failed to add the payload\n");
-+        ret = http2errno(http_code);
-+        tevent_req_error(req, ret);
-+        return;
-+    }
-+
-+    DEBUG(SSSDBG_TRACE_INTERNAL, "payload created\n");
-+    tevent_req_done(req);
-+}
-+
-+static errno_t ccdb_sec_create_recv(struct tevent_req *req)
-+{
-+    TEVENT_REQ_RETURN_ON_ERROR(req);
-+    return EOK;
-+}
-+
-+struct ccdb_sec_mod_cred_state {
-+    struct tevent_context *ev;
-+    struct kcm_ccdb *db;
-+    struct cli_creds *client;
-+    struct kcm_mod_ctx *mod_cc;
-+
-+    struct ccdb_sec *secdb;
-+};
-+
-+static void ccdb_sec_mod_cred_get_done(struct tevent_req *subreq);
-+static void ccdb_sec_mod_cred_patch_done(struct tevent_req *subreq);
-+
-+static struct tevent_req *ccdb_sec_mod_send(TALLOC_CTX *mem_ctx,
-+                                            struct tevent_context *ev,
-+                                            struct kcm_ccdb *db,
-+                                            struct cli_creds *client,
-+                                            uuid_t uuid,
-+                                            struct kcm_mod_ctx *mod_cc)
-+{
-+    errno_t ret;
-+    struct tevent_req *req = NULL;
-+    struct tevent_req *subreq = NULL;
-+    struct ccdb_sec_mod_cred_state *state = NULL;
-+    struct ccdb_sec *secdb = talloc_get_type(db->db_handle, struct ccdb_sec);
-+
-+    req = tevent_req_create(mem_ctx, &state, struct ccdb_sec_mod_cred_state);
-+    if (req == NULL) {
-+        return NULL;
-+    }
-+    state->ev = ev;
-+    state->db =db;
-+    state->client = client;
-+    state->secdb = secdb;
-+    state->mod_cc = mod_cc;
-+
-+    DEBUG(SSSDBG_TRACE_INTERNAL, "Modifying ccache\n");
-+
-+    subreq = sec_get_ccache_send(state, ev, secdb, client, NULL, uuid);
-+    if (subreq == NULL) {
-+        ret = ENOMEM;
-+        goto immediate;
-+    }
-+    tevent_req_set_callback(subreq, *ccdb_sec_mod_cred_get_done, req);
-+    return req;
-+
-+immediate:
-+    tevent_req_error(req, ret);
-+    tevent_req_post(req, ev);
-+    return req;
-+}
-+
-+static void ccdb_sec_mod_cred_get_done(struct tevent_req *subreq)
-+{
-+    errno_t ret;
-+    struct tevent_req *req = tevent_req_callback_data(subreq,
-+                                                      struct tevent_req);
-+    struct ccdb_sec_mod_cred_state *state = tevent_req_data(req,
-+                                            struct ccdb_sec_mod_cred_state);
-+    struct kcm_ccache *cc;
-+    const char *url;
-+    struct sss_iobuf *payload;
-+
-+    ret = sec_get_ccache_recv(subreq, state, &cc);
-+    talloc_zfree(subreq);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_OP_FAILURE,
-+              "Cannot retrieve the ccache [%d]: %s\n", ret, sss_strerror(ret));
-+        tevent_req_error(req, ret);
-+        return;
-+    }
-+
-+    if (cc == NULL) {
-+        DEBUG(SSSDBG_MINOR_FAILURE, "No such ccache\n");
-+        tevent_req_error(req, ERR_NO_CREDS);
-+        return;
-+    }
-+
-+    kcm_mod_cc(cc, state->mod_cc);
-+
-+    ret = kcm_ccache_to_sec_input(state, cc, state->client, &url, &payload);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_OP_FAILURE,
-+              "Failed to marshall modified ccache to payload [%d]: %s\n",
-+              ret, sss_strerror(ret));
-+        tevent_req_error(req, ret);
-+        return;
-+    }
-+
-+    subreq = sec_patch_send(state,
-+                            state->ev,
-+                            state->secdb,
-+                            state->client,
-+                            url,
-+                            payload);
-+    if (subreq == NULL) {
-+        tevent_req_error(req, ENOMEM);
-+        return;
-+    }
-+    tevent_req_set_callback(subreq, ccdb_sec_mod_cred_patch_done, req);
-+}
-+
-+static void ccdb_sec_mod_cred_patch_done(struct tevent_req *subreq)
-+{
-+    errno_t ret;
-+    struct tevent_req *req = tevent_req_callback_data(subreq,
-+                                                      struct tevent_req);
-+
-+    ret = sec_patch_recv(subreq);
-+    talloc_zfree(subreq);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_OP_FAILURE,
-+              "sec_patch request failed [%d]: %s\n", ret, sss_strerror(ret));
-+        tevent_req_error(req, ret);
-+        return;
-+    }
-+
-+    DEBUG(SSSDBG_TRACE_INTERNAL, "ccache modified\n");
-+    tevent_req_done(req);
-+}
-+
-+static errno_t ccdb_sec_mod_recv(struct tevent_req *req)
-+{
-+    TEVENT_REQ_RETURN_ON_ERROR(req);
-+    return EOK;
-+}
-+
-+struct ccdb_sec_store_cred_state {
-+    struct tevent_context *ev;
-+    struct kcm_ccdb *db;
-+    struct cli_creds *client;
-+    struct sss_iobuf *cred_blob;
-+
-+    struct ccdb_sec *secdb;
-+};
-+
-+static void ccdb_sec_store_cred_get_done(struct tevent_req *subreq);
-+static void ccdb_sec_store_cred_patch_done(struct tevent_req *subreq);
-+
-+/* HTTP DEL/PUT $base/ccache/uuid:name */
-+static struct tevent_req *ccdb_sec_store_cred_send(TALLOC_CTX *mem_ctx,
-+                                                   struct tevent_context *ev,
-+                                                   struct kcm_ccdb *db,
-+                                                   struct cli_creds *client,
-+                                                   uuid_t uuid,
-+                                                   struct sss_iobuf *cred_blob)
-+{
-+    errno_t ret;
-+    struct tevent_req *req = NULL;
-+    struct tevent_req *subreq = NULL;
-+    struct ccdb_sec_store_cred_state *state = NULL;
-+    struct ccdb_sec *secdb = talloc_get_type(db->db_handle, struct ccdb_sec);
-+
-+    req = tevent_req_create(mem_ctx, &state, struct ccdb_sec_store_cred_state);
-+    if (req == NULL) {
-+        return NULL;
-+    }
-+    state->ev = ev;
-+    state->db =db;
-+    state->client = client;
-+    state->cred_blob = cred_blob;
-+    state->secdb = secdb;
-+
-+    DEBUG(SSSDBG_TRACE_INTERNAL, "Storing creds in ccache\n");
-+
-+    subreq = sec_get_ccache_send(state, ev, secdb, client, NULL, uuid);
-+    if (subreq == NULL) {
-+        ret = ENOMEM;
-+        goto immediate;
-+    }
-+    tevent_req_set_callback(subreq, *ccdb_sec_store_cred_get_done, req);
-+    return req;
-+
-+immediate:
-+    tevent_req_error(req, ret);
-+    tevent_req_post(req, ev);
-+    return req;
-+}
-+
-+static void ccdb_sec_store_cred_get_done(struct tevent_req *subreq)
-+{
-+    errno_t ret;
-+    struct tevent_req *req = tevent_req_callback_data(subreq,
-+                                                      struct tevent_req);
-+    struct ccdb_sec_store_cred_state *state = tevent_req_data(req,
-+                                            struct ccdb_sec_store_cred_state);
-+    struct kcm_ccache *cc;
-+    const char *url;
-+    struct sss_iobuf *payload;
-+
-+    ret = sec_get_ccache_recv(subreq, state, &cc);
-+    talloc_zfree(subreq);
-+    if (ret != EOK) {
-+        tevent_req_error(req, ret);
-+        return;
-+    }
-+
-+    ret = kcm_cc_store_cred_blob(cc, state->cred_blob);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_OP_FAILURE,
-+              "Cannot store credentials to ccache [%d]: %s\n",
-+              ret, sss_strerror(ret));
-+        tevent_req_error(req, ret);
-+        return;
-+    }
-+
-+    ret = kcm_ccache_to_sec_input(state, cc, state->client, &url, &payload);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_OP_FAILURE,
-+              "Failed to marshall modified ccache to payload [%d]: %s\n",
-+              ret, sss_strerror(ret));
-+        tevent_req_error(req, ret);
-+        return;
-+    }
-+
-+    subreq = sec_patch_send(state,
-+                            state->ev,
-+                            state->secdb,
-+                            state->client,
-+                            url,
-+                            payload);
-+    if (subreq == NULL) {
-+        tevent_req_error(req, ENOMEM);
-+        return;
-+    }
-+    tevent_req_set_callback(subreq, ccdb_sec_store_cred_patch_done, req);
-+}
-+
-+static void ccdb_sec_store_cred_patch_done(struct tevent_req *subreq)
-+{
-+    errno_t ret;
-+    struct tevent_req *req = tevent_req_callback_data(subreq,
-+                                                      struct tevent_req);
-+
-+    ret = sec_patch_recv(subreq);
-+    talloc_zfree(subreq);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_OP_FAILURE,
-+              "sec_patch request failed [%d]: %s\n", ret, sss_strerror(ret));
-+        tevent_req_error(req, ret);
-+        return;
-+    }
-+
-+    DEBUG(SSSDBG_TRACE_INTERNAL, "ccache creds stored\n");
-+    tevent_req_done(req);
-+}
-+
-+static errno_t ccdb_sec_store_cred_recv(struct tevent_req *req)
-+{
-+    TEVENT_REQ_RETURN_ON_ERROR(req);
-+    return EOK;
-+}
-+
-+/* HTTP DELETE $base/ccache/uuid:name */
-+struct ccdb_sec_delete_state {
-+    struct tevent_context *ev;
-+    struct ccdb_sec *secdb;
-+    struct cli_creds *client;
-+    uuid_t uuid;
-+
-+    size_t sec_key_list_len;
-+};
-+
-+static void ccdb_sec_delete_list_done(struct tevent_req *subreq);
-+static void ccdb_sec_delete_cc_done(struct tevent_req *subreq);
-+static void ccdb_sec_delete_container_done(struct tevent_req *subreq);
-+
-+static struct tevent_req *ccdb_sec_delete_send(TALLOC_CTX *mem_ctx,
-+                                               struct tevent_context *ev,
-+                                               struct kcm_ccdb *db,
-+                                               struct cli_creds *client,
-+                                               uuid_t uuid)
-+{
-+    errno_t ret;
-+    struct tevent_req *req = NULL;
-+    struct tevent_req *subreq = NULL;
-+    struct ccdb_sec_delete_state *state = NULL;
-+    struct ccdb_sec *secdb = talloc_get_type(db->db_handle, struct ccdb_sec);
-+
-+    req = tevent_req_create(mem_ctx, &state, struct ccdb_sec_delete_state);
-+    if (req == NULL) {
-+        return NULL;
-+    }
-+    state->ev = ev;
-+    state->secdb = secdb;
-+    state->client = client;
-+    uuid_copy(state->uuid, uuid);
-+
-+    DEBUG(SSSDBG_TRACE_INTERNAL, "Deleting ccache\n");
-+
-+    subreq = sec_list_send(state, ev, secdb, client);
-+    if (subreq == NULL) {
-+        ret = ENOMEM;
-+        goto immediate;
-+    }
-+    tevent_req_set_callback(subreq, ccdb_sec_delete_list_done, req);
-+    return req;
-+
-+immediate:
-+    tevent_req_error(req, ret);
-+    tevent_req_post(req, ev);
-+    return req;
-+}
-+
-+static void ccdb_sec_delete_list_done(struct tevent_req *subreq)
-+{
-+    errno_t ret;
-+    struct tevent_req *req = tevent_req_callback_data(subreq,
-+                                                      struct tevent_req);
-+    struct ccdb_sec_delete_state *state = tevent_req_data(req,
-+                                                struct ccdb_sec_delete_state);
-+    const char **sec_key_list;
-+    const char *sec_key;
-+    const char *cc_url;
-+
-+    ret = sec_list_recv(subreq,
-+                        state,
-+                        &sec_key_list,
-+                        &state->sec_key_list_len);
-+    talloc_zfree(subreq);
-+    if (ret != EOK) {
-+        tevent_req_error(req, ret);
-+        return;
-+    }
-+
-+    if (sec_key_list == 0) {
-+        DEBUG(SSSDBG_MINOR_FAILURE, "No ccaches to delete\n");
-+        tevent_req_done(req);
-+        return;
-+    }
-+
-+    sec_key = find_by_uuid(sec_key_list, state->uuid);
-+    if (sec_key == NULL) {
-+        DEBUG(SSSDBG_MINOR_FAILURE, "Cannot find ccache by UUID\n");
-+        tevent_req_done(req);
-+        return;
-+    }
-+
-+    cc_url = sec_cc_url_create(state, state->client, sec_key);
-+    if (cc_url == NULL) {
-+        tevent_req_error(req, ENOMEM);
-+        return;
-+    }
-+
-+    subreq = tcurl_http_send(state, state->ev,
-+                             state->secdb->tctx,
-+                             TCURL_HTTP_DELETE,
-+                             SSSD_SECRETS_SOCKET,
-+                             cc_url,
-+                             sec_headers,
-+                             NULL,
-+                             SEC_TIMEOUT);
-+    if (subreq == NULL) {
-+        tevent_req_error(req, ENOMEM);
-+        return;
-+    }
-+    tevent_req_set_callback(subreq, ccdb_sec_delete_cc_done, req);
-+}
-+
-+static void ccdb_sec_delete_cc_done(struct tevent_req *subreq)
-+{
-+    errno_t ret;
-+    struct tevent_req *req = tevent_req_callback_data(subreq,
-+                                                      struct tevent_req);
-+    struct ccdb_sec_delete_state *state = tevent_req_data(req,
-+                                                struct ccdb_sec_delete_state);
-+    int http_code;
-+    const char *container_url;
-+
-+    ret = tcurl_http_recv(state, subreq, &http_code, NULL);
-+    talloc_zfree(subreq);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_OP_FAILURE,
-+              "Cannot delete ccache [%d]: %s\n",
-+              ret, sss_strerror(ret));
-+        tevent_req_error(req, ret);
-+        return;
-+    }
-+
-+    if (http_code != 200) {
-+        ret = http2errno(http_code);
-+        tevent_req_error(req, ret);
-+        return;
-+    }
-+
-+    if (state->sec_key_list_len != 1) {
-+        DEBUG(SSSDBG_TRACE_INTERNAL, "There are other ccaches, done\n");
-+        tevent_req_done(req);
-+        return;
-+    }
-+
-+    DEBUG(SSSDBG_TRACE_INTERNAL, "Removing ccache container\n");
-+
-+    container_url = sec_container_url_create(state, state->client);
-+    if (container_url == NULL) {
-+        tevent_req_error(req, ENOMEM);
-+        return;
-+    }
-+
-+    subreq = tcurl_http_send(state, state->ev,
-+                             state->secdb->tctx,
-+                             TCURL_HTTP_DELETE,
-+                             SSSD_SECRETS_SOCKET,
-+                             container_url,
-+                             sec_headers,
-+                             NULL,
-+                             SEC_TIMEOUT);
-+    if (subreq == NULL) {
-+        tevent_req_error(req, ENOMEM);
-+        return;
-+    }
-+    tevent_req_set_callback(subreq, ccdb_sec_delete_container_done, req);
-+}
-+
-+static void ccdb_sec_delete_container_done(struct tevent_req *subreq)
-+{
-+    errno_t ret;
-+    struct tevent_req *req = tevent_req_callback_data(subreq,
-+                                                      struct tevent_req);
-+    struct ccdb_sec_delete_state *state = tevent_req_data(req,
-+                                                struct ccdb_sec_delete_state);
-+    int http_code;
-+
-+    ret = tcurl_http_recv(state, subreq, &http_code, NULL);
-+    talloc_zfree(subreq);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_OP_FAILURE,
-+              "Cannot delete ccache container [%d]: %s\n",
-+              ret, sss_strerror(ret));
-+        tevent_req_error(req, ret);
-+        return;
-+    }
-+
-+    if (http_code != 200) {
-+        ret = http2errno(http_code);
-+        tevent_req_error(req, ret);
-+        return;
-+    }
-+
-+    DEBUG(SSSDBG_TRACE_INTERNAL, "Removed ccache container\n");
-+    tevent_req_done(req);
-+}
-+
-+static errno_t ccdb_sec_delete_recv(struct tevent_req *req)
-+{
-+    TEVENT_REQ_RETURN_ON_ERROR(req);
-+    return EOK;
-+}
-+
-+const struct kcm_ccdb_ops ccdb_sec_ops = {
-+    .init = ccdb_sec_init,
-+
-+    .nextid_send = ccdb_sec_nextid_send,
-+    .nextid_recv = ccdb_sec_nextid_recv,
-+
-+    .set_default_send = ccdb_sec_set_default_send,
-+    .set_default_recv = ccdb_sec_set_default_recv,
-+
-+    .get_default_send = ccdb_sec_get_default_send,
-+    .get_default_recv = ccdb_sec_get_default_recv,
-+
-+    .list_send = ccdb_sec_list_send,
-+    .list_recv = ccdb_sec_list_recv,
-+
-+    .getbyname_send = ccdb_sec_getbyname_send,
-+    .getbyname_recv = ccdb_sec_getbyname_recv,
-+
-+    .getbyuuid_send = ccdb_sec_getbyuuid_send,
-+    .getbyuuid_recv = ccdb_sec_getbyuuid_recv,
-+
-+    .name_by_uuid_send = ccdb_sec_name_by_uuid_send,
-+    .name_by_uuid_recv = ccdb_sec_name_by_uuid_recv,
-+
-+    .uuid_by_name_send = ccdb_sec_uuid_by_name_send,
-+    .uuid_by_name_recv = ccdb_sec_uuid_by_name_recv,
-+
-+    .create_send = ccdb_sec_create_send,
-+    .create_recv = ccdb_sec_create_recv,
-+
-+    .mod_send = ccdb_sec_mod_send,
-+    .mod_recv = ccdb_sec_mod_recv,
-+
-+    .store_cred_send = ccdb_sec_store_cred_send,
-+    .store_cred_recv = ccdb_sec_store_cred_recv,
-+
-+    .delete_send = ccdb_sec_delete_send,
-+    .delete_recv = ccdb_sec_delete_recv,
-+};
-diff --git a/src/tests/cmocka/test_kcm_json_marshalling.c b/src/tests/cmocka/test_kcm_json_marshalling.c
-new file mode 100644
-index 0000000000000000000000000000000000000000..8eff2f501066c70a8730cd3d4dc41b92d7a03e4c
---- /dev/null
-+++ b/src/tests/cmocka/test_kcm_json_marshalling.c
-@@ -0,0 +1,234 @@
-+/*
-+    Copyright (C) 2017 Red Hat
-+
-+    SSSD tests: Test KCM JSON marshalling
-+
-+    This program is free software; you can redistribute it and/or modify
-+    it under the terms of the GNU General Public License as published by
-+    the Free Software Foundation; either version 3 of the License, or
-+    (at your option) any later version.
-+
-+    This program is distributed in the hope that it will be useful,
-+    but WITHOUT ANY WARRANTY; without even the implied warranty of
-+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+    GNU General Public License for more details.
-+
-+    You should have received a copy of the GNU General Public License
-+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
-+*/
-+
-+#include "config.h"
-+
-+#include <stdio.h>
-+#include <popt.h>
-+
-+#include "util/util_creds.h"
-+#include "responder/kcm/kcmsrv_ccache.h"
-+#include "responder/kcm/kcmsrv_ccache_be.h"
-+#include "tests/cmocka/common_mock.h"
-+
-+#define TEST_REALM                "TESTREALM"
-+#define TEST_PRINC_COMPONENT      "PRINC_NAME"
-+
-+#define TEST_CREDS                "TESTCREDS"
-+
-+const struct kcm_ccdb_ops ccdb_mem_ops;
-+const struct kcm_ccdb_ops ccdb_sec_ops;
-+
-+struct kcm_marshalling_test_ctx {
-+    krb5_context kctx;
-+    krb5_principal princ;
-+};
-+
-+static int setup_kcm_marshalling(void **state)
-+{
-+    struct kcm_marshalling_test_ctx *test_ctx;
-+    krb5_error_code kerr;
-+
-+    test_ctx = talloc_zero(NULL, struct kcm_marshalling_test_ctx);
-+    assert_non_null(test_ctx);
-+
-+    kerr = krb5_init_context(&test_ctx->kctx);
-+    assert_int_equal(kerr, 0);
-+
-+    kerr = krb5_build_principal(test_ctx->kctx,
-+                                &test_ctx->princ,
-+                                sizeof(TEST_REALM)-1, TEST_REALM,
-+                                TEST_PRINC_COMPONENT, NULL);
-+    assert_int_equal(kerr, 0);
-+
-+    *state = test_ctx;
-+    return 0;
-+}
-+
-+static int teardown_kcm_marshalling(void **state)
-+{
-+    struct kcm_marshalling_test_ctx *test_ctx = talloc_get_type(*state,
-+                                        struct kcm_marshalling_test_ctx);
-+    assert_non_null(test_ctx);
-+
-+    krb5_free_principal(test_ctx->kctx, test_ctx->princ);
-+    krb5_free_context(test_ctx->kctx);
-+    talloc_free(test_ctx);
-+    return 0;
-+}
-+
-+static void assert_cc_name_equal(struct kcm_ccache *cc1,
-+                                 struct kcm_ccache *cc2)
-+{
-+    const char *name1, *name2;
-+
-+    name1 = kcm_cc_get_name(cc1);
-+    name2 = kcm_cc_get_name(cc2);
-+    assert_string_equal(name1, name2);
-+}
-+
-+static void assert_cc_uuid_equal(struct kcm_ccache *cc1,
-+                                 struct kcm_ccache *cc2)
-+{
-+    uuid_t u1, u2;
-+    errno_t ret;
-+
-+    ret = kcm_cc_get_uuid(cc1, u1);
-+    assert_int_equal(ret, EOK);
-+    ret = kcm_cc_get_uuid(cc2, u2);
-+    assert_int_equal(ret, EOK);
-+    ret = uuid_compare(u1, u2);
-+    assert_int_equal(ret, 0);
-+}
-+
-+static void assert_cc_princ_equal(struct kcm_ccache *cc1,
-+                                  struct kcm_ccache *cc2)
-+{
-+    krb5_principal p1;
-+    krb5_principal p2;
-+    char *name1;
-+    char *name2;
-+    krb5_error_code kerr;
-+
-+    p1 = kcm_cc_get_client_principal(cc1);
-+    p2 = kcm_cc_get_client_principal(cc1);
-+
-+    kerr = krb5_unparse_name(NULL, p1, &name1);
-+    assert_int_equal(kerr, 0);
-+    kerr = krb5_unparse_name(NULL, p2, &name2);
-+    assert_int_equal(kerr, 0);
-+
-+    assert_string_equal(name1, name2);
-+    krb5_free_unparsed_name(NULL, name1);
-+    krb5_free_unparsed_name(NULL, name2);
-+}
-+
-+static void assert_cc_offset_equal(struct kcm_ccache *cc1,
-+                                   struct kcm_ccache *cc2)
-+{
-+    int32_t off1;
-+    int32_t off2;
-+
-+    off1 = kcm_cc_get_offset(cc1);
-+    off2 = kcm_cc_get_offset(cc2);
-+    assert_int_equal(off1, off2);
-+}
-+
-+static void assert_cc_equal(struct kcm_ccache *cc1,
-+                            struct kcm_ccache *cc2)
-+{
-+    assert_cc_name_equal(cc1, cc2);
-+    assert_cc_uuid_equal(cc1, cc2);
-+    assert_cc_princ_equal(cc1, cc2);
-+    assert_cc_offset_equal(cc1, cc2);
-+}
-+
-+static void test_kcm_ccache_marshall_unmarshall(void **state)
-+{
-+    struct kcm_marshalling_test_ctx *test_ctx = talloc_get_type(*state,
-+                                        struct kcm_marshalling_test_ctx);
-+    errno_t ret;
-+    struct cli_creds owner;
-+    struct kcm_ccache *cc;
-+    struct kcm_ccache *cc2;
-+    const char *url;
-+    struct sss_iobuf *payload;
-+    const char *name;
-+    const char *key;
-+    uint8_t *data;
-+
-+    owner.ucred.uid = getuid();
-+    owner.ucred.gid = getuid();
-+
-+    name = talloc_asprintf(test_ctx, "%"SPRIuid, getuid());
-+    assert_non_null(name);
-+
-+    ret = kcm_cc_new(test_ctx,
-+                     test_ctx->kctx,
-+                     &owner,
-+                     name,
-+                     test_ctx->princ,
-+                     &cc);
-+    assert_int_equal(ret, EOK);
-+
-+    ret = kcm_ccache_to_sec_input(test_ctx,
-+                                  cc,
-+                                  &owner,
-+                                  &url,
-+                                  &payload);
-+    assert_int_equal(ret, EOK);
-+
-+    key = strrchr(url, '/') + 1;
-+    assert_non_null(key);
-+
-+    data = sss_iobuf_get_data(payload);
-+    assert_non_null(data);
-+
-+    ret = sec_kv_to_ccache(test_ctx,
-+                           key,
-+                           (const char *) data,
-+                           &owner,
-+                           &cc2);
-+    assert_int_equal(ret, EOK);
-+
-+    assert_cc_equal(cc, cc2);
-+}
-+
-+int main(int argc, const char *argv[])
-+{
-+    poptContext pc;
-+    int opt;
-+    int rv;
-+    struct poptOption long_options[] = {
-+        POPT_AUTOHELP
-+        SSSD_DEBUG_OPTS
-+        POPT_TABLEEND
-+    };
-+
-+    const struct CMUnitTest tests[] = {
-+        cmocka_unit_test_setup_teardown(test_kcm_ccache_marshall_unmarshall,
-+                                        setup_kcm_marshalling,
-+                                        teardown_kcm_marshalling),
-+    };
-+
-+    /* Set debug level to invalid value so we can deside if -d 0 was used. */
-+    debug_level = SSSDBG_INVALID;
-+
-+    pc = poptGetContext(argv[0], argc, argv, long_options, 0);
-+    while((opt = poptGetNextOpt(pc)) != -1) {
-+        switch(opt) {
-+        default:
-+            fprintf(stderr, "\nInvalid option %s: %s\n\n",
-+                    poptBadOption(pc, 0), poptStrerror(opt));
-+            poptPrintUsage(pc, stderr, 0);
-+            return 1;
-+        }
-+    }
-+    poptFreeContext(pc);
-+
-+    DEBUG_CLI_INIT(debug_level);
-+
-+    /* Even though normally the tests should clean up after themselves
-+     * they might not after a failed run. Remove the old db to be sure */
-+    tests_set_cwd();
-+
-+    rv = cmocka_run_group_tests(tests, NULL, NULL);
-+
-+    return rv;
-+}
-diff --git a/src/tests/intg/test_kcm.py b/src/tests/intg/test_kcm.py
-index ad1e4923bfe339cb040464757431d2ef3bf57ce1..11f80a1803b4ad9b8e8857bf9a8a244d4816f0a2 100644
---- a/src/tests/intg/test_kcm.py
-+++ b/src/tests/intg/test_kcm.py
-@@ -27,7 +27,8 @@ import signal
- import kdc
- import krb5utils
- import config
--from util import unindent, run_shell
-+from util import unindent
-+from test_secrets import create_sssd_secrets_fixture
- 
- class KcmTestEnv(object):
-     def __init__(self, k5kdc, k5util):
-@@ -107,15 +108,8 @@ def create_sssd_kcm_fixture(sock_path, request):
-     return kcm_pid
- 
- 
--@pytest.fixture
--def setup_for_kcm(request, kdc_instance):
--    """
--    Just set up the local provider for tests and enable the KCM
--    responder
--    """
--    kcm_path = os.path.join(config.RUNSTATEDIR, "kcm.socket")
--
--    sssd_conf = unindent("""\
-+def create_sssd_conf(kcm_path, ccache_storage):
-+    return unindent("""\
-         [sssd]
-         domains = local
-         services = nss
-@@ -125,8 +119,11 @@ def setup_for_kcm(request, kdc_instance):
- 
-         [kcm]
-         socket_path = {kcm_path}
-+        ccache_storage = {ccache_storage}
-     """).format(**locals())
- 
-+
-+def common_setup_for_kcm_mem(request, kdc_instance, kcm_path, sssd_conf):
-     kcm_socket_include = unindent("""
-     [libdefaults]
-     default_ccache_name = KCM:
-@@ -142,11 +139,35 @@ def setup_for_kcm(request, kdc_instance):
-     return KcmTestEnv(kdc_instance, k5util)
- 
- 
--def test_kcm_init_list_destroy(setup_for_kcm):
-+@pytest.fixture
-+def setup_for_kcm_mem(request, kdc_instance):
-+    """
-+    Just set up the local provider for tests and enable the KCM
-+    responder
-+    """
-+    kcm_path = os.path.join(config.RUNSTATEDIR, "kcm.socket")
-+    sssd_conf = create_sssd_conf(kcm_path, "memory")
-+    return common_setup_for_kcm_mem(request, kdc_instance, kcm_path, sssd_conf)
-+
-+@pytest.fixture
-+def setup_secrets(request):
-+    create_sssd_secrets_fixture(request)
-+
-+@pytest.fixture
-+def setup_for_kcm_sec(request, kdc_instance):
-+    """
-+    Just set up the local provider for tests and enable the KCM
-+    responder
-+    """
-+    kcm_path = os.path.join(config.RUNSTATEDIR, "kcm.socket")
-+    sssd_conf = create_sssd_conf(kcm_path, "secrets")
-+    return common_setup_for_kcm_mem(request, kdc_instance, kcm_path, sssd_conf)
-+
-+
-+def kcm_init_list_destroy(testenv):
-     """
-     Test that kinit, kdestroy and klist work with KCM
-     """
--    testenv = setup_for_kcm
-     testenv.k5kdc.add_principal("kcmtest", "Secret123")
- 
-     ok = testenv.k5util.has_principal("kcmtest@KCMTEST")
-@@ -172,12 +193,22 @@ def test_kcm_init_list_destroy(setup_for_kcm):
-     assert nprincs == 0
- 
- 
--def test_kcm_overwrite(setup_for_kcm):
-+def test_kcm_mem_init_list_destroy(setup_for_kcm_mem):
-+    testenv = setup_for_kcm_mem
-+    kcm_init_list_destroy(testenv)
-+
-+
-+def test_kcm_sec_init_list_destroy(setup_for_kcm_sec,
-+                                   setup_secrets):
-+    testenv = setup_for_kcm_sec
-+    kcm_init_list_destroy(testenv)
-+
-+
-+def kcm_overwrite(testenv):
-     """
-     That that reusing a ccache reinitializes the cache and doesn't
-     add the same principal twice
-     """
--    testenv = setup_for_kcm
-     testenv.k5kdc.add_principal("kcmtest", "Secret123")
-     exp_ccache = {'kcmtest@KCMTEST': ['krbtgt/KCMTEST@KCMTEST']}
- 
-@@ -192,12 +223,22 @@ def test_kcm_overwrite(setup_for_kcm):
-     assert exp_ccache == testenv.k5util.list_all_princs()
- 
- 
--def test_collection_init_list_destroy(setup_for_kcm):
-+def test_kcm_mem_overwrite(setup_for_kcm_mem):
-+    testenv = setup_for_kcm_mem
-+    kcm_overwrite(testenv)
-+
-+
-+def test_kcm_sec_overwrite(setup_for_kcm_sec,
-+                           setup_secrets):
-+    testenv = setup_for_kcm_sec
-+    kcm_overwrite(testenv)
-+
-+
-+def collection_init_list_destroy(testenv):
-     """
-     Test that multiple principals and service tickets can be stored
-     in a collection.
-     """
--    testenv = setup_for_kcm
-     testenv.k5kdc.add_principal("alice", "alicepw")
-     testenv.k5kdc.add_principal("bob", "bobpw")
-     testenv.k5kdc.add_principal("carol", "carolpw")
-@@ -241,7 +282,11 @@ def test_collection_init_list_destroy(setup_for_kcm):
- 
-     out = testenv.k5util.kdestroy()
-     assert out == 0
--    assert testenv.k5util.default_principal() == 'bob@KCMTEST'
-+    # If the default is removed, KCM just uses whetever is the first entry
-+    # in the collection as the default. And sine the KCM back ends don't
-+    # guarantee if they are FIFO or LIFO, just check for either alice or bob
-+    assert testenv.k5util.default_principal() in \
-+            ['alice@KCMTEST', 'bob@KCMTEST']
-     cc_coll = testenv.k5util.list_all_princs()
-     assert len(cc_coll) == 2
-     assert cc_coll['alice@KCMTEST'] == ['krbtgt/KCMTEST@KCMTEST']
-@@ -255,11 +300,21 @@ def test_collection_init_list_destroy(setup_for_kcm):
-     #assert len(cc_coll) == 0
- 
- 
--def test_kswitch(setup_for_kcm):
-+def test_kcm_mem_collection_init_list_destroy(setup_for_kcm_mem):
-+    testenv = setup_for_kcm_mem
-+    collection_init_list_destroy(testenv)
-+
-+
-+def test_kcm_sec_collection_init_list_destroy(setup_for_kcm_sec,
-+                                              setup_secrets):
-+    testenv = setup_for_kcm_sec
-+    collection_init_list_destroy(testenv)
-+
-+
-+def exercise_kswitch(testenv):
-     """
-     Test switching between principals
-     """
--    testenv = setup_for_kcm
-     testenv.k5kdc.add_principal("alice", "alicepw")
-     testenv.k5kdc.add_principal("bob", "bobpw")
-     testenv.k5kdc.add_principal("host/somehostname")
-@@ -301,12 +356,22 @@ def test_kswitch(setup_for_kcm):
-                                     'host/differenthostname@KCMTEST'])
- 
- 
--def test_subsidiaries(setup_for_kcm):
-+def test_kcm_mem_kswitch(setup_for_kcm_mem):
-+    testenv = setup_for_kcm_mem
-+    exercise_kswitch(testenv)
-+
-+
-+def test_kcm_sec_kswitch(setup_for_kcm_sec,
-+                         setup_secrets):
-+    testenv = setup_for_kcm_sec
-+    exercise_kswitch(testenv)
-+
-+
-+def exercise_subsidiaries(testenv):
-     """
-     Test that subsidiary caches are usable and KCM: without specifying UID
-     can be used to identify the collection
-     """
--    testenv = setup_for_kcm
-     testenv.k5kdc.add_principal("alice", "alicepw")
-     testenv.k5kdc.add_principal("bob", "bobpw")
-     testenv.k5kdc.add_principal("host/somehostname")
-@@ -346,11 +411,21 @@ def test_subsidiaries(setup_for_kcm):
-                                             'host/differenthostname@KCMTEST'])
- 
- 
--def test_kdestroy_nocache(setup_for_kcm):
-+def test_kcm_mem_subsidiaries(setup_for_kcm_mem):
-+    testenv = setup_for_kcm_mem
-+    exercise_subsidiaries(testenv)
-+
-+
-+def test_kcm_sec_subsidiaries(setup_for_kcm_sec,
-+                              setup_secrets):
-+    testenv = setup_for_kcm_sec
-+    exercise_subsidiaries(testenv)
-+
-+
-+def kdestroy_nocache(testenv):
-     """
-     Destroying a non-existing ccache should not throw an error
-     """
--    testenv = setup_for_kcm
-     testenv.k5kdc.add_principal("alice", "alicepw")
-     out, _, _ = testenv.k5util.kinit("alice", "alicepw")
-     assert out == 0
-@@ -359,3 +434,14 @@ def test_kdestroy_nocache(setup_for_kcm):
-     assert out == 0
-     out = testenv.k5util.kdestroy()
-     assert out == 0
-+
-+
-+def test_kcm_mem_kdestroy_nocache(setup_for_kcm_mem):
-+    testenv = setup_for_kcm_mem
-+    exercise_subsidiaries(testenv)
-+
-+
-+def test_kcm_sec_kdestroy_nocache(setup_for_kcm_sec,
-+                                  setup_secrets):
-+    testenv = setup_for_kcm_sec
-+    exercise_subsidiaries(testenv)
-diff --git a/src/util/util_errors.c b/src/util/util_errors.c
-index 23cfdf9c6116a2c8e569a041e8289b65a112fd08..60c2f439b3e39b1dbff353e429114cb5a3070052 100644
---- a/src/util/util_errors.c
-+++ b/src/util/util_errors.c
-@@ -109,6 +109,8 @@ struct err_string error_to_str[] = {
-     { "KCM operation not implemented" }, /* ERR_KCM_OP_NOT_IMPLEMENTED */
-     { "End of credential cache reached" }, /* ERR_KCM_CC_END */
-     { "Credential cache name not allowed" }, /* ERR_KCM_WRONG_CCNAME_FORMAT */
-+    { "Cannot encode a JSON object to string" }, /* ERR_JSON_ENCODING */
-+    { "Cannot decode a JSON object from string" }, /* ERR_JSON_DECODING */
-     { "ERR_LAST" } /* ERR_LAST */
- };
- 
-diff --git a/src/util/util_errors.h b/src/util/util_errors.h
-index 387d481616db1ed5e22b73fae82632a582fae946..4e9da814702e2cd46edc52fd5c2ae5f640602609 100644
---- a/src/util/util_errors.h
-+++ b/src/util/util_errors.h
-@@ -131,6 +131,8 @@ enum sssd_errors {
-     ERR_KCM_OP_NOT_IMPLEMENTED,
-     ERR_KCM_CC_END,
-     ERR_KCM_WRONG_CCNAME_FORMAT,
-+    ERR_JSON_ENCODING,
-+    ERR_JSON_DECODING,
-     ERR_LAST            /* ALWAYS LAST */
- };
- 
--- 
-2.9.3
-
diff --git a/SOURCES/0033-SYSTEMD-Clean-pid-file-in-corner-cases.patch b/SOURCES/0033-SYSTEMD-Clean-pid-file-in-corner-cases.patch
new file mode 100644
index 0000000..ce1c4ec
--- /dev/null
+++ b/SOURCES/0033-SYSTEMD-Clean-pid-file-in-corner-cases.patch
@@ -0,0 +1,39 @@
+From 7f4a89ff4e7dc7c3ffac24ac87187dca1e9e7d1e Mon Sep 17 00:00:00 2001
+From: Lukas Slebodnik <lslebodn@redhat.com>
+Date: Wed, 8 Nov 2017 14:09:36 +0100
+Subject: [PATCH 33/35] SYSTEMD: Clean pid file in corner cases
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+SSSD can cleanup pid file in case of standard stopping of daemon.
+It's done in function monitor_cleanup. However monitor does not have a
+change to cleanup file in case of OOM or sending SIGKILL to monitor.
+
+Even though PIDFile is not necessary for services with Type notify
+we should let systemd to clean this file in unexpected situations.
+
+Resolves:
+https://pagure.io/SSSD/sssd/issue/3528
+
+Reviewed-by: Fabiano Fidêncio <fidencio@redhat.com>
+(cherry picked from commit f4b808c83ecbaf36c7069440535d62990e32d55d)
+---
+ src/sysv/systemd/sssd.service.in | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/src/sysv/systemd/sssd.service.in b/src/sysv/systemd/sssd.service.in
+index cea848fac80303d6fae12dd84316a91dbc60072d..0c515d34caaa3ea397c4c7e95eef0188df170840 100644
+--- a/src/sysv/systemd/sssd.service.in
++++ b/src/sysv/systemd/sssd.service.in
+@@ -10,6 +10,7 @@ EnvironmentFile=-@environment_file@
+ ExecStart=@sbindir@/sssd -i ${DEBUG_LOGGER}
+ Type=notify
+ NotifyAccess=main
++PIDFile=@localstatedir@/run/sssd.pid
+ 
+ [Install]
+ WantedBy=multi-user.target
+-- 
+2.13.6
+
diff --git a/SOURCES/0034-CHILD-Pass-information-about-logger-to-children.patch b/SOURCES/0034-CHILD-Pass-information-about-logger-to-children.patch
new file mode 100644
index 0000000..a254cd8
--- /dev/null
+++ b/SOURCES/0034-CHILD-Pass-information-about-logger-to-children.patch
@@ -0,0 +1,198 @@
+From 3ada67750d3d0dc6188c03cc996b98e7578971a4 Mon Sep 17 00:00:00 2001
+From: Lukas Slebodnik <lslebodn@redhat.com>
+Date: Wed, 8 Nov 2017 08:13:02 +0100
+Subject: [PATCH 34/35] CHILD: Pass information about logger to children
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Variables debug_to_file or debug_to_stderr were not set
+because back-end already user parameter --logger=%s.
+And therefore logs were not sent to files.
+
+It could only work in case of direct usage of --debug-to-files in back-end via
+command configuration option.
+
+Resolves:
+https://pagure.io/SSSD/sssd/issue/3433
+
+Reviewed-by: Fabiano Fidêncio <fidencio@redhat.com>
+(cherry picked from commit a24954cc19285b197fb287bfa7aa01949c92b17d)
+---
+ src/p11_child/p11_child_nss.c     |  4 +++-
+ src/providers/ad/ad_gpo_child.c   |  3 ++-
+ src/providers/ipa/selinux_child.c |  3 ++-
+ src/providers/krb5/krb5_child.c   |  3 ++-
+ src/providers/ldap/ldap_child.c   |  3 ++-
+ src/util/child_common.c           | 24 ++++++++++--------------
+ 6 files changed, 21 insertions(+), 19 deletions(-)
+
+diff --git a/src/p11_child/p11_child_nss.c b/src/p11_child/p11_child_nss.c
+index e7dbcb689220d1cd2585fbde5f26e84f8fa15cc2..b0ec69be321c4b4186ce851c07bfcc3e1afe9694 100644
+--- a/src/p11_child/p11_child_nss.c
++++ b/src/p11_child/p11_child_nss.c
+@@ -537,7 +537,7 @@ int main(int argc, const char *argv[])
+     int opt;
+     poptContext pc;
+     int debug_fd = -1;
+-    char *opt_logger = NULL;
++    const char *opt_logger = NULL;
+     errno_t ret;
+     TALLOC_CTX *main_ctx = NULL;
+     char *cert;
+@@ -673,7 +673,9 @@ int main(int argc, const char *argv[])
+         if (ret != EOK) {
+             DEBUG(SSSDBG_CRIT_FAILURE, "set_debug_file_from_fd failed.\n");
+         }
++        opt_logger = sss_logger_str[FILES_LOGGER];
+     }
++
+     sss_set_logger(opt_logger);
+ 
+     DEBUG(SSSDBG_TRACE_FUNC, "p11_child started.\n");
+diff --git a/src/providers/ad/ad_gpo_child.c b/src/providers/ad/ad_gpo_child.c
+index 5375cc691e8649c289672b74c4bfe5266c8222c9..a0bd6e13a31fe0f92924d49302d1b8b17bac4d67 100644
+--- a/src/providers/ad/ad_gpo_child.c
++++ b/src/providers/ad/ad_gpo_child.c
+@@ -687,7 +687,7 @@ main(int argc, const char *argv[])
+     int opt;
+     poptContext pc;
+     int debug_fd = -1;
+-    char *opt_logger = NULL;
++    const char *opt_logger = NULL;
+     errno_t ret;
+     int sysvol_gpt_version;
+     int result;
+@@ -744,6 +744,7 @@ main(int argc, const char *argv[])
+         if (ret != EOK) {
+             DEBUG(SSSDBG_CRIT_FAILURE, "set_debug_file_from_fd failed.\n");
+         }
++        opt_logger = sss_logger_str[FILES_LOGGER];
+     }
+ 
+     sss_set_logger(opt_logger);
+diff --git a/src/providers/ipa/selinux_child.c b/src/providers/ipa/selinux_child.c
+index 120492686963241b7e419413f489cc38953e32f2..a7e20f715626d0f3ecef7cc06f3de5d44b6a15c1 100644
+--- a/src/providers/ipa/selinux_child.c
++++ b/src/providers/ipa/selinux_child.c
+@@ -206,7 +206,7 @@ int main(int argc, const char *argv[])
+     struct response *resp = NULL;
+     ssize_t written;
+     bool needs_update;
+-    char *opt_logger = NULL;
++    const char *opt_logger = NULL;
+ 
+     struct poptOption long_options[] = {
+         POPT_AUTOHELP
+@@ -254,6 +254,7 @@ int main(int argc, const char *argv[])
+         if (ret != EOK) {
+             DEBUG(SSSDBG_CRIT_FAILURE, "set_debug_file_from_fd failed.\n");
+         }
++        opt_logger = sss_logger_str[FILES_LOGGER];
+     }
+ 
+     sss_set_logger(opt_logger);
+diff --git a/src/providers/krb5/krb5_child.c b/src/providers/krb5/krb5_child.c
+index 700338e47a3f9ac6fcf11b4c92364dbdb4f9bcf7..7b56002377ac22472c3eea1aef687109757c22db 100644
+--- a/src/providers/krb5/krb5_child.c
++++ b/src/providers/krb5/krb5_child.c
+@@ -3020,7 +3020,7 @@ int main(int argc, const char *argv[])
+     int opt;
+     poptContext pc;
+     int debug_fd = -1;
+-    char *opt_logger = NULL;
++    const char *opt_logger = NULL;
+     errno_t ret;
+     krb5_error_code kerr;
+     uid_t fast_uid;
+@@ -3097,6 +3097,7 @@ int main(int argc, const char *argv[])
+         if (ret != EOK) {
+             DEBUG(SSSDBG_CRIT_FAILURE, "set_debug_file_from_fd failed.\n");
+         }
++        opt_logger = sss_logger_str[FILES_LOGGER];
+     }
+ 
+     sss_set_logger(opt_logger);
+diff --git a/src/providers/ldap/ldap_child.c b/src/providers/ldap/ldap_child.c
+index baeed239db5dc7ffa482edcbc155f25f718c8249..c0618d6d8828f102c32cf56731995e2b370590e7 100644
+--- a/src/providers/ldap/ldap_child.c
++++ b/src/providers/ldap/ldap_child.c
+@@ -599,7 +599,7 @@ int main(int argc, const char *argv[])
+     int kerr;
+     int opt;
+     int debug_fd = -1;
+-    char *opt_logger = NULL;
++    const char *opt_logger = NULL;
+     poptContext pc;
+     TALLOC_CTX *main_ctx = NULL;
+     uint8_t *buf = NULL;
+@@ -657,6 +657,7 @@ int main(int argc, const char *argv[])
+         if (ret != EOK) {
+             DEBUG(SSSDBG_CRIT_FAILURE, "set_debug_file_from_fd failed.\n");
+         }
++        opt_logger = sss_logger_str[FILES_LOGGER];
+     }
+ 
+     sss_set_logger(opt_logger);
+diff --git a/src/util/child_common.c b/src/util/child_common.c
+index dc070f26446305e07cbb34edd1e4d72db72aedc5..203c115f9e7c4ecc2178b5660473d4f960fbbb6d 100644
+--- a/src/util/child_common.c
++++ b/src/util/child_common.c
+@@ -630,14 +630,11 @@ static errno_t prepare_child_argv(TALLOC_CTX *mem_ctx,
+     }
+ 
+     /* Save the current state in case an interrupt changes it */
+-    bool child_debug_to_file = debug_to_file;
+     bool child_debug_timestamps = debug_timestamps;
+     bool child_debug_microseconds = debug_microseconds;
+-    bool child_debug_stderr = debug_to_stderr;
+ 
+     if (!extra_args_only) {
+-        if (child_debug_to_file) argc++;
+-        if (child_debug_stderr) argc++;
++        argc++;
+     }
+ 
+     if (extra_argv) {
+@@ -675,21 +672,20 @@ static errno_t prepare_child_argv(TALLOC_CTX *mem_ctx,
+             goto fail;
+         }
+ 
+-        if (child_debug_stderr) {
+-            argv[--argc] = talloc_strdup(argv, "--logger=stderr");
+-            if (argv[argc] == NULL) {
+-                ret = ENOMEM;
+-                goto fail;
+-            }
+-        }
+-
+-        if (child_debug_to_file) {
++        if (sss_logger == FILES_LOGGER) {
+             argv[--argc] = talloc_asprintf(argv, "--debug-fd=%d",
+                                            child_debug_fd);
+             if (argv[argc] == NULL) {
+                 ret = ENOMEM;
+                 goto fail;
+             }
++        } else {
++            argv[--argc] = talloc_asprintf(argv, "--logger=%s",
++                                           sss_logger_str[sss_logger]);
++            if (argv[argc] == NULL) {
++                ret = ENOMEM;
++                goto fail;
++            }
+         }
+ 
+         argv[--argc] = talloc_asprintf(argv, "--debug-timestamps=%d",
+@@ -816,7 +812,7 @@ errno_t child_debug_init(const char *logfile, int *debug_fd)
+         return EOK;
+     }
+ 
+-    if (debug_to_file != 0 && *debug_fd == -1) {
++    if (sss_logger == FILES_LOGGER && *debug_fd == -1) {
+         ret = open_debug_file_ex(logfile, &debug_filep, false);
+         if (ret != EOK) {
+             DEBUG(SSSDBG_FATAL_FAILURE, "Error setting up logging (%d) [%s]\n",
+-- 
+2.13.6
+
diff --git a/SOURCES/0034-KCM-Make-the-secrets-ccache-back-end-configurable-ma.patch b/SOURCES/0034-KCM-Make-the-secrets-ccache-back-end-configurable-ma.patch
deleted file mode 100644
index 2d65a32..0000000
--- a/SOURCES/0034-KCM-Make-the-secrets-ccache-back-end-configurable-ma.patch
+++ /dev/null
@@ -1,219 +0,0 @@
-From 6236b14d20151053f5ccad1fc8ee9b669d4b0ffb Mon Sep 17 00:00:00 2001
-From: Jakub Hrozek <jhrozek@redhat.com>
-Date: Tue, 14 Mar 2017 11:17:05 +0100
-Subject: [PATCH 34/36] KCM: Make the secrets ccache back end configurable,
- make secrets the default
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Adds a new option 'ccache_storage' that allows to select either the
-memory back end or the secrets back end. The secrets back end is the
-default one and this option is even undocumented.
-
-Reviewed-by: Michal Židek <mzidek@redhat.com>
-Reviewed-by: Simo Sorce <simo@redhat.com>
----
- src/confdb/confdb.h                  |  1 +
- src/config/cfg_rules.ini             |  1 +
- src/responder/kcm/kcm.c              | 49 ++++++++++++++++++++++++++++++++----
- src/responder/kcm/kcmsrv_ccache.c    |  2 +-
- src/responder/kcm/kcmsrv_ccache.h    |  6 +----
- src/responder/kcm/kcmsrv_ccache_be.h |  1 +
- src/responder/kcm/kcmsrv_pvt.h       |  7 ++++++
- 7 files changed, 56 insertions(+), 11 deletions(-)
-
-diff --git a/src/confdb/confdb.h b/src/confdb/confdb.h
-index c443e869a7a6782265b42c4ad122867c4e3dd8e0..fb60675ca8beb2c2a157bf021ed9cad362742988 100644
---- a/src/confdb/confdb.h
-+++ b/src/confdb/confdb.h
-@@ -234,6 +234,7 @@
- /* KCM Service */
- #define CONFDB_KCM_CONF_ENTRY "config/kcm"
- #define CONFDB_KCM_SOCKET "socket_path"
-+#define CONFDB_KCM_DB "ccache_storage" /* Undocumented on purpose */
- 
- struct confdb_ctx;
- struct config_file_ctx;
-diff --git a/src/config/cfg_rules.ini b/src/config/cfg_rules.ini
-index 5e789c51658c51c0af1338d23d6c0f30f40bf119..67a5d1f5ad447a942b437ffd04a7f5d7cfe77d7f 100644
---- a/src/config/cfg_rules.ini
-+++ b/src/config/cfg_rules.ini
-@@ -280,6 +280,7 @@ option = fd_limit
- option = client_idle_timeout
- option = description
- option = socket_path
-+option = ccache_storage
- 
- [rule/allowed_domain_options]
- validator = ini_allowed_options
-diff --git a/src/responder/kcm/kcm.c b/src/responder/kcm/kcm.c
-index 2c12ef215ce3967df183e51c20590c5f439d278f..063c27b915b4b92f6259496feee891aa94a498b6 100644
---- a/src/responder/kcm/kcm.c
-+++ b/src/responder/kcm/kcm.c
-@@ -47,6 +47,37 @@ static int kcm_responder_ctx_destructor(void *ptr)
-     return 0;
- }
- 
-+static errno_t kcm_get_ccdb_be(struct kcm_ctx *kctx)
-+{
-+    errno_t ret;
-+    char *str_db;
-+
-+    ret = confdb_get_string(kctx->rctx->cdb,
-+                            kctx->rctx,
-+                            kctx->rctx->confdb_service_path,
-+                            CONFDB_KCM_DB,
-+                            "secrets",
-+                            &str_db);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_OP_FAILURE,
-+              "Cannot get the KCM database type [%d]: %s\n",
-+               ret, strerror(ret));
-+        return ret;
-+    }
-+
-+    DEBUG(SSSDBG_CONF_SETTINGS, "KCM database type: %s\n", str_db);
-+    if (strcasecmp(str_db, "memory") == 0) {
-+        kctx->cc_be = CCDB_BE_MEMORY;
-+        return EOK;
-+    } else if (strcasecmp(str_db, "secrets") == 0) {
-+        kctx->cc_be = CCDB_BE_SECRETS;
-+        return EOK;
-+    }
-+
-+    DEBUG(SSSDBG_FATAL_FAILURE, "Unexpected KCM database type %s\n", str_db);
-+    return EOK;
-+}
-+
- static int kcm_get_config(struct kcm_ctx *kctx)
- {
-     int ret;
-@@ -88,14 +119,21 @@ static int kcm_get_config(struct kcm_ctx *kctx)
-                             &sock_name);
-     if (ret != EOK) {
-         DEBUG(SSSDBG_OP_FAILURE,
--              "Cannot get the client idle timeout [%d]: %s\n",
-+              "Cannot get KCM socket path [%d]: %s\n",
-                ret, strerror(ret));
-         goto done;
-     }
-     kctx->rctx->sock_name = sock_name;
- 
-+    ret = kcm_get_ccdb_be(kctx);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_OP_FAILURE,
-+              "Cannot get KCM ccache DB [%d]: %s\n",
-+               ret, strerror(ret));
-+        goto done;
-+    }
-+
-     ret = EOK;
--
- done:
-     return ret;
- }
-@@ -111,7 +149,8 @@ static int kcm_data_destructor(void *ptr)
- }
- 
- static struct kcm_resp_ctx *kcm_data_setup(TALLOC_CTX *mem_ctx,
--                                           struct tevent_context *ev)
-+                                           struct tevent_context *ev,
-+                                           enum kcm_ccdb_be cc_be)
- {
-     struct kcm_resp_ctx *kcm_data;
-     krb5_error_code kret;
-@@ -122,7 +161,7 @@ static struct kcm_resp_ctx *kcm_data_setup(TALLOC_CTX *mem_ctx,
-         return NULL;
-     }
- 
--    kcm_data->db = kcm_ccdb_init(kcm_data, ev, CCDB_BE_MEMORY);
-+    kcm_data->db = kcm_ccdb_init(kcm_data, ev, cc_be);
-     if (kcm_data->db == NULL) {
-         talloc_free(kcm_data);
-         return NULL;
-@@ -176,7 +215,7 @@ static int kcm_process_init(TALLOC_CTX *mem_ctx,
-         goto fail;
-     }
- 
--    kctx->kcm_data = kcm_data_setup(kctx, ev);
-+    kctx->kcm_data = kcm_data_setup(kctx, ev, kctx->cc_be);
-     if (kctx->kcm_data == NULL) {
-         DEBUG(SSSDBG_FATAL_FAILURE,
-               "fatal error initializing responder data\n");
-diff --git a/src/responder/kcm/kcmsrv_ccache.c b/src/responder/kcm/kcmsrv_ccache.c
-index 2ae120269b0c62275ba2acdff6d6daa8b7077708..a22184e0f2b1300f3678bb343b6a110bf144a36b 100644
---- a/src/responder/kcm/kcmsrv_ccache.c
-+++ b/src/responder/kcm/kcmsrv_ccache.c
-@@ -244,7 +244,7 @@ struct kcm_ccdb *kcm_ccdb_init(TALLOC_CTX *mem_ctx,
-         break;
-     case CCDB_BE_SECRETS:
-         DEBUG(SSSDBG_FUNC_DATA, "KCM back end: sssd-secrets\n");
--        /* Not implemented yet */
-+        ccdb->ops = &ccdb_sec_ops;
-         break;
-     default:
-         DEBUG(SSSDBG_CRIT_FAILURE, "Unknown ccache database\n");
-diff --git a/src/responder/kcm/kcmsrv_ccache.h b/src/responder/kcm/kcmsrv_ccache.h
-index 18c8c47ad4ecb24521a85a1833b239c7a2a8bb45..36c481c5335d557318f0ed0204d93e533b4b6c41 100644
---- a/src/responder/kcm/kcmsrv_ccache.h
-+++ b/src/responder/kcm/kcmsrv_ccache.h
-@@ -29,6 +29,7 @@
- #include "util/util.h"
- #include "util/sss_iobuf.h"
- #include "util/util_creds.h"
-+#include "responder/kcm/kcmsrv_pvt.h"
- 
- #define UUID_BYTES    16
- #define UUID_STR_SIZE 37
-@@ -113,11 +114,6 @@ errno_t kcm_cc_store_cred_blob(struct kcm_ccache *cc,
- struct kcm_cred *kcm_cc_get_cred(struct kcm_ccache *cc);
- struct kcm_cred *kcm_cc_next_cred(struct kcm_cred *crd);
- 
--enum kcm_ccdb_be {
--    CCDB_BE_MEMORY,
--    CCDB_BE_SECRETS,
--};
--
- /* An opaque database that contains all the ccaches */
- struct kcm_ccdb;
- 
-diff --git a/src/responder/kcm/kcmsrv_ccache_be.h b/src/responder/kcm/kcmsrv_ccache_be.h
-index 1bd2b6981e227675866e82e0a5389445cac4df66..a0796c298bae15a01adf612a6195a494ba6b4d23 100644
---- a/src/responder/kcm/kcmsrv_ccache_be.h
-+++ b/src/responder/kcm/kcmsrv_ccache_be.h
-@@ -200,5 +200,6 @@ struct kcm_ccdb_ops {
- };
- 
- extern const struct kcm_ccdb_ops ccdb_mem_ops;
-+extern const struct kcm_ccdb_ops ccdb_sec_ops;
- 
- #endif /* _KCMSRV_CCACHE_BE_ */
-diff --git a/src/responder/kcm/kcmsrv_pvt.h b/src/responder/kcm/kcmsrv_pvt.h
-index a29680246c1e616da75e1bbff951ce2fad66fb65..74f30c00014105ed533744779b02c5d42523722d 100644
---- a/src/responder/kcm/kcmsrv_pvt.h
-+++ b/src/responder/kcm/kcmsrv_pvt.h
-@@ -49,6 +49,12 @@ struct kcm_resp_ctx {
-     struct kcm_ccdb *db;
- };
- 
-+/* Supported ccache back ends */
-+enum kcm_ccdb_be {
-+    CCDB_BE_MEMORY,
-+    CCDB_BE_SECRETS,
-+};
-+
- /*
-  * responder context that contains both the responder data,
-  * like the ccaches and the sssd-specific stuff like the
-@@ -58,6 +64,7 @@ struct kcm_ctx {
-     struct resp_ctx *rctx;
-     int fd_limit;
-     char *socket_path;
-+    enum kcm_ccdb_be cc_be;
- 
-     struct kcm_resp_ctx *kcm_data;
- };
--- 
-2.9.3
-
diff --git a/SOURCES/0035-KCM-Queue-requests-by-the-same-UID.patch b/SOURCES/0035-KCM-Queue-requests-by-the-same-UID.patch
deleted file mode 100644
index 338c394..0000000
--- a/SOURCES/0035-KCM-Queue-requests-by-the-same-UID.patch
+++ /dev/null
@@ -1,909 +0,0 @@
-From 688e8d8ffe331a1dd75a78002bf212277f2d7664 Mon Sep 17 00:00:00 2001
-From: Jakub Hrozek <jhrozek@redhat.com>
-Date: Tue, 21 Mar 2017 13:25:11 +0100
-Subject: [PATCH 35/36] KCM: Queue requests by the same UID
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-In order to avoid race conditions, we queue requests towards the KCM
-responder coming from the same client UID.
-
-Reviewed-by: Michal Židek <mzidek@redhat.com>
-Reviewed-by: Simo Sorce <simo@redhat.com>
-Reviewed-by: Lukáš Slebodník <lslebodn@redhat.com>
----
- Makefile.am                         |  21 ++-
- src/responder/kcm/kcm.c             |   7 +
- src/responder/kcm/kcmsrv_cmd.c      |  10 +-
- src/responder/kcm/kcmsrv_op_queue.c | 264 ++++++++++++++++++++++++++
- src/responder/kcm/kcmsrv_ops.c      |  44 ++++-
- src/responder/kcm/kcmsrv_ops.h      |   1 +
- src/responder/kcm/kcmsrv_pvt.h      |  20 ++
- src/tests/cmocka/test_kcm_queue.c   | 365 ++++++++++++++++++++++++++++++++++++
- 8 files changed, 721 insertions(+), 11 deletions(-)
- create mode 100644 src/responder/kcm/kcmsrv_op_queue.c
- create mode 100644 src/tests/cmocka/test_kcm_queue.c
-
-diff --git a/Makefile.am b/Makefile.am
-index e9eaa312c91e3aee40bcf13c90a0ad8c683045d5..91afdd669aa11a3cc316588d3b51d7e8e9c91cb8 100644
---- a/Makefile.am
-+++ b/Makefile.am
-@@ -304,7 +304,10 @@ non_interactive_cmocka_based_tests += test_inotify
- endif   # HAVE_INOTIFY
- 
- if BUILD_KCM
--non_interactive_cmocka_based_tests += test_kcm_json
-+non_interactive_cmocka_based_tests += \
-+	test_kcm_json \
-+	test_kcm_queue \
-+        $(NULL)
- endif   # BUILD_KCM
- 
- if BUILD_SAMBA
-@@ -1501,6 +1504,7 @@ sssd_kcm_SOURCES = \
-     src/responder/kcm/kcmsrv_ccache_json.c \
-     src/responder/kcm/kcmsrv_ccache_secrets.c \
-     src/responder/kcm/kcmsrv_ops.c \
-+    src/responder/kcm/kcmsrv_op_queue.c \
-     src/util/sss_sockets.c \
-     src/util/sss_krb5.c \
-     src/util/sss_iobuf.c \
-@@ -3402,6 +3406,21 @@ test_kcm_json_LDADD = \
-     $(SSSD_INTERNAL_LTLIBS) \
-     libsss_test_common.la \
-     $(NULL)
-+
-+test_kcm_queue_SOURCES = \
-+    src/tests/cmocka/test_kcm_queue.c \
-+    src/responder/kcm/kcmsrv_op_queue.c \
-+    $(NULL)
-+test_kcm_queue_CFLAGS = \
-+    $(AM_CFLAGS) \
-+    $(NULL)
-+test_kcm_queue_LDADD = \
-+    $(CMOCKA_LIBS) \
-+    $(SSSD_LIBS) \
-+    $(SSSD_INTERNAL_LTLIBS) \
-+    libsss_test_common.la \
-+    $(NULL)
-+
- endif # BUILD_KCM
- 
- endif # HAVE_CMOCKA
-diff --git a/src/responder/kcm/kcm.c b/src/responder/kcm/kcm.c
-index 063c27b915b4b92f6259496feee891aa94a498b6..3ee978066c589a5cc38b0ae358f741d389d00e7a 100644
---- a/src/responder/kcm/kcm.c
-+++ b/src/responder/kcm/kcm.c
-@@ -133,6 +133,13 @@ static int kcm_get_config(struct kcm_ctx *kctx)
-         goto done;
-     }
- 
-+    kctx->qctx = kcm_ops_queue_create(kctx);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_OP_FAILURE,
-+              "Cannot create KCM request queue [%d]: %s\n",
-+               ret, strerror(ret));
-+        goto done;
-+    }
-     ret = EOK;
- done:
-     return ret;
-diff --git a/src/responder/kcm/kcmsrv_cmd.c b/src/responder/kcm/kcmsrv_cmd.c
-index 537e88953fd1a190a9a73bcdd430d8e0db8f9291..81015de4a91617de3dca444cde95b636c8d5c0d1 100644
---- a/src/responder/kcm/kcmsrv_cmd.c
-+++ b/src/responder/kcm/kcmsrv_cmd.c
-@@ -353,14 +353,18 @@ struct kcm_req_ctx {
- 
- static void kcm_cmd_request_done(struct tevent_req *req);
- 
--static errno_t kcm_cmd_dispatch(struct kcm_req_ctx *req_ctx)
-+static errno_t kcm_cmd_dispatch(struct kcm_ctx *kctx,
-+                                struct kcm_req_ctx *req_ctx)
- {
-     struct tevent_req *req;
-     struct cli_ctx *cctx;
- 
-     cctx = req_ctx->cctx;
- 
--    req = kcm_cmd_send(req_ctx, cctx->ev, req_ctx->kctx->kcm_data,
-+    req = kcm_cmd_send(req_ctx,
-+                       cctx->ev,
-+                       kctx->qctx,
-+                       req_ctx->kctx->kcm_data,
-                        req_ctx->cctx->creds,
-                        &req_ctx->op_io.request,
-                        req_ctx->op_io.op);
-@@ -505,7 +509,7 @@ static void kcm_recv(struct cli_ctx *cctx)
-     /* do not read anymore, client is done sending */
-     TEVENT_FD_NOT_READABLE(cctx->cfde);
- 
--    ret = kcm_cmd_dispatch(req);
-+    ret = kcm_cmd_dispatch(kctx, req);
-     if (ret != EOK) {
-         DEBUG(SSSDBG_FATAL_FAILURE,
-               "Failed to dispatch KCM operation [%d]: %s\n",
-diff --git a/src/responder/kcm/kcmsrv_op_queue.c b/src/responder/kcm/kcmsrv_op_queue.c
-new file mode 100644
-index 0000000000000000000000000000000000000000..f6c425dd5b64877c8b7401e488dd6565157fc9b5
---- /dev/null
-+++ b/src/responder/kcm/kcmsrv_op_queue.c
-@@ -0,0 +1,264 @@
-+/*
-+   SSSD
-+
-+   KCM Server - the KCM operations wait queue
-+
-+   Copyright (C) Red Hat, 2017
-+
-+   This program is free software; you can redistribute it and/or modify
-+   it under the terms of the GNU General Public License as published by
-+   the Free Software Foundation; either version 3 of the License, or
-+   (at your option) any later version.
-+
-+   This program is distributed in the hope that it will be useful,
-+   but WITHOUT ANY WARRANTY; without even the implied warranty of
-+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+   GNU General Public License for more details.
-+
-+   You should have received a copy of the GNU General Public License
-+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
-+*/
-+
-+#include "util/util.h"
-+#include "util/util_creds.h"
-+#include "responder/kcm/kcmsrv_pvt.h"
-+
-+#define QUEUE_HASH_SIZE      32
-+
-+struct kcm_ops_queue_entry {
-+    struct tevent_req *req;
-+    uid_t uid;
-+
-+    hash_table_t *wait_queue_hash;
-+
-+    struct kcm_ops_queue_entry *head;
-+    struct kcm_ops_queue_entry *next;
-+    struct kcm_ops_queue_entry *prev;
-+};
-+
-+struct kcm_ops_queue_ctx {
-+    /* UID: dlist of kcm_ops_queue_entry */
-+    hash_table_t *wait_queue_hash;
-+};
-+
-+/*
-+ * Per-UID wait queue
-+ *
-+ * They key in the hash table is the UID of the peer. The value of each
-+ * hash table entry is a linked list of kcm_ops_queue_entry structures
-+ * which primarily hold the tevent request being queued.
-+ */
-+struct kcm_ops_queue_ctx *kcm_ops_queue_create(TALLOC_CTX *mem_ctx)
-+{
-+    errno_t ret;
-+    struct kcm_ops_queue_ctx *queue_ctx;
-+
-+    queue_ctx = talloc_zero(mem_ctx, struct kcm_ops_queue_ctx);
-+    if (queue_ctx == NULL) {
-+        return NULL;
-+    }
-+
-+    ret = sss_hash_create_ex(mem_ctx, QUEUE_HASH_SIZE,
-+                             &queue_ctx->wait_queue_hash, 0, 0, 0, 0,
-+                             NULL, NULL);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_CRIT_FAILURE,
-+              "sss_hash_create failed [%d]: %s\n", ret, sss_strerror(ret));
-+        talloc_free(queue_ctx);
-+        return NULL;
-+    }
-+
-+    return queue_ctx;
-+}
-+
-+static int kcm_op_queue_entry_destructor(struct kcm_ops_queue_entry *entry)
-+{
-+    int ret;
-+    struct kcm_ops_queue_entry *next_entry;
-+    hash_key_t key;
-+
-+    if (entry == NULL) {
-+        return 1;
-+    }
-+
-+    /* Take the next entry from the queue */
-+    next_entry = entry->next;
-+
-+    /* Remove the current entry from the queue */
-+    DLIST_REMOVE(entry->head, entry);
-+
-+    if (next_entry == NULL) {
-+        key.type = HASH_KEY_ULONG;
-+        key.ul = entry->uid;
-+
-+        /* If this was the last entry, remove the key (the UID) from the
-+         * hash table to signal the queue is empty
-+         */
-+        ret = hash_delete(entry->wait_queue_hash, &key);
-+        if (ret != HASH_SUCCESS) {
-+            DEBUG(SSSDBG_CRIT_FAILURE,
-+                  "Failed to remove wait queue for user %"SPRIuid"\n",
-+                  entry->uid);
-+            return 1;
-+        }
-+        return 0;
-+    }
-+
-+    /* Otherwise, mark the current head as done to run the next request */
-+    tevent_req_done(next_entry->req);
-+    return 0;
-+}
-+
-+static errno_t kcm_op_queue_add(hash_table_t *wait_queue_hash,
-+                                struct kcm_ops_queue_entry *entry,
-+                                uid_t uid)
-+{
-+    errno_t ret;
-+    hash_key_t key;
-+    hash_value_t value;
-+    struct kcm_ops_queue_entry *head = NULL;
-+
-+    key.type = HASH_KEY_ULONG;
-+    key.ul = uid;
-+
-+    ret = hash_lookup(wait_queue_hash, &key, &value);
-+    switch (ret) {
-+    case HASH_SUCCESS:
-+        /* The key with this UID already exists. Its value is request queue
-+         * for the UID, so let's just add the current request to the end
-+         * of the queue and wait for the previous requests to finish
-+         */
-+        if (value.type != HASH_VALUE_PTR) {
-+            DEBUG(SSSDBG_CRIT_FAILURE, "Unexpected hash value type.\n");
-+            return EINVAL;
-+        }
-+
-+        head = talloc_get_type(value.ptr, struct kcm_ops_queue_entry);
-+        if (head == NULL) {
-+            DEBUG(SSSDBG_CRIT_FAILURE, "Invalid queue pointer\n");
-+            return EINVAL;
-+        }
-+
-+        entry->head = head;
-+        DLIST_ADD_END(head, entry, struct kcm_ops_queue_entry *);
-+
-+        DEBUG(SSSDBG_TRACE_LIBS, "Waiting in queue\n");
-+        ret = EAGAIN;
-+        break;
-+
-+    case HASH_ERROR_KEY_NOT_FOUND:
-+        /* No request for this UID yet. Enqueue this request in case
-+         * another one comes in and return EOK to run the current request
-+         * immediatelly
-+         */
-+        entry->head = entry;
-+
-+        value.type = HASH_VALUE_PTR;
-+        value.ptr = entry;
-+
-+        ret = hash_enter(wait_queue_hash, &key, &value);
-+        if (ret != HASH_SUCCESS) {
-+            DEBUG(SSSDBG_CRIT_FAILURE, "hash_enter failed.\n");
-+            return EIO;
-+        }
-+
-+        DEBUG(SSSDBG_TRACE_LIBS,
-+              "Added a first request to the queue, running immediately\n");
-+        ret = EOK;
-+        break;
-+
-+    default:
-+        DEBUG(SSSDBG_CRIT_FAILURE, "hash_lookup failed.\n");
-+        return EIO;
-+    }
-+
-+    talloc_steal(wait_queue_hash, entry);
-+    talloc_set_destructor(entry, kcm_op_queue_entry_destructor);
-+    return ret;
-+}
-+
-+struct kcm_op_queue_state {
-+    struct kcm_ops_queue_entry *entry;
-+};
-+
-+/*
-+ * Enqueue a request.
-+ *
-+ * If the request queue /for the given ID/ is empty, that is, if this
-+ * request is the first one in the queue, run the request immediatelly.
-+ *
-+ * Otherwise just add it to the queue and wait until the previous request
-+ * finishes and only at that point mark the current request as done, which
-+ * will trigger calling the recv function and allow the request to continue.
-+ */
-+struct tevent_req *kcm_op_queue_send(TALLOC_CTX *mem_ctx,
-+                                     struct tevent_context *ev,
-+                                     struct kcm_ops_queue_ctx *qctx,
-+                                     struct cli_creds *client)
-+{
-+    errno_t ret;
-+    struct tevent_req *req;
-+    struct kcm_op_queue_state *state;
-+    uid_t uid;
-+
-+    uid = cli_creds_get_uid(client);
-+
-+    req = tevent_req_create(mem_ctx, &state, struct kcm_op_queue_state);
-+    if (req == NULL) {
-+        return NULL;
-+    }
-+
-+    state->entry = talloc_zero(state, struct kcm_ops_queue_entry);
-+    if (state->entry == NULL) {
-+        ret = ENOMEM;
-+        goto immediate;
-+    }
-+    state->entry->req = req;
-+    state->entry->uid = uid;
-+    state->entry->wait_queue_hash = qctx->wait_queue_hash;
-+
-+    DEBUG(SSSDBG_FUNC_DATA,
-+          "Adding request by %"SPRIuid" to the wait queue\n", uid);
-+
-+    ret = kcm_op_queue_add(qctx->wait_queue_hash, state->entry, uid);
-+    if (ret == EOK) {
-+        DEBUG(SSSDBG_TRACE_LIBS,
-+              "Wait queue was empty, running immediately\n");
-+        goto immediate;
-+    } else if (ret != EAGAIN) {
-+        DEBUG(SSSDBG_OP_FAILURE,
-+              "Cannot enqueue request [%d]: %s\n", ret, sss_strerror(ret));
-+        goto immediate;
-+    }
-+
-+    DEBUG(SSSDBG_TRACE_LIBS, "Waiting our turn in the queue\n");
-+    return req;
-+
-+immediate:
-+    if (ret == EOK) {
-+        tevent_req_done(req);
-+    } else {
-+        tevent_req_error(req, ret);
-+    }
-+    tevent_req_post(req, ev);
-+    return req;
-+}
-+
-+/*
-+ * The queue recv function is called when this request is 'activated'. The queue
-+ * entry should be allocated on the same memory context as the enqueued request
-+ * to trigger freeing the kcm_ops_queue_entry structure destructor when the
-+ * parent request is done and its tevent_req freed. This would in turn unblock
-+ * the next request in the queue
-+ */
-+errno_t kcm_op_queue_recv(struct tevent_req *req,
-+                          TALLOC_CTX *mem_ctx,
-+                          struct kcm_ops_queue_entry **_entry)
-+{
-+    struct kcm_op_queue_state *state = tevent_req_data(req,
-+                                                struct kcm_op_queue_state);
-+
-+    TEVENT_REQ_RETURN_ON_ERROR(req);
-+    *_entry = talloc_steal(mem_ctx, state->entry);
-+    return EOK;
-+}
-diff --git a/src/responder/kcm/kcmsrv_ops.c b/src/responder/kcm/kcmsrv_ops.c
-index 50e8cc635424e15d53e3c8d122c5525044f59c8a..2feaf51f227ce9d90f706229ce7ac201b282dc6f 100644
---- a/src/responder/kcm/kcmsrv_ops.c
-+++ b/src/responder/kcm/kcmsrv_ops.c
-@@ -67,17 +67,21 @@ struct kcm_op {
- 
- struct kcm_cmd_state {
-     struct kcm_op *op;
-+    struct tevent_context *ev;
- 
-+    struct kcm_ops_queue_entry *queue_entry;
-     struct kcm_op_ctx *op_ctx;
-     struct sss_iobuf *reply;
- 
-     uint32_t op_ret;
- };
- 
-+static void kcm_cmd_queue_done(struct tevent_req *subreq);
- static void kcm_cmd_done(struct tevent_req *subreq);
- 
- struct tevent_req *kcm_cmd_send(TALLOC_CTX *mem_ctx,
-                                 struct tevent_context *ev,
-+                                struct kcm_ops_queue_ctx *qctx,
-                                 struct kcm_resp_ctx *kcm_data,
-                                 struct cli_creds *client,
-                                 struct kcm_data *input,
-@@ -93,6 +97,7 @@ struct tevent_req *kcm_cmd_send(TALLOC_CTX *mem_ctx,
-         return NULL;
-     }
-     state->op = op;
-+    state->ev = ev;
- 
-     if (op == NULL) {
-         ret = EINVAL;
-@@ -154,18 +159,43 @@ struct tevent_req *kcm_cmd_send(TALLOC_CTX *mem_ctx,
-         goto immediate;
-     }
- 
--    subreq = op->fn_send(state, ev, state->op_ctx);
-+    subreq = kcm_op_queue_send(state, ev, qctx, client);
-     if (subreq == NULL) {
-         ret = ENOMEM;
-         goto immediate;
-     }
-+    tevent_req_set_callback(subreq, kcm_cmd_queue_done, req);
-+    return req;
-+
-+immediate:
-+    tevent_req_error(req, ret);
-+    tevent_req_post(req, ev);
-+    return req;
-+}
-+
-+static void kcm_cmd_queue_done(struct tevent_req *subreq)
-+{
-+    struct tevent_req *req = tevent_req_callback_data(subreq, struct tevent_req);
-+    struct kcm_cmd_state *state = tevent_req_data(req, struct kcm_cmd_state);
-+    errno_t ret;
-+
-+    /* When this request finishes, it frees the queue_entry which unblocks
-+     * other requests by the same UID
-+     */
-+    ret = kcm_op_queue_recv(subreq, state, &state->queue_entry);
-+    talloc_zfree(subreq);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_CRIT_FAILURE, "Cannot acquire queue slot\n");
-+        tevent_req_error(req, ret);
-+        return;
-+    }
-+
-+    subreq = state->op->fn_send(state, state->ev, state->op_ctx);
-+    if (subreq == NULL) {
-+        tevent_req_error(req, ENOMEM);
-+        return;
-+    }
-     tevent_req_set_callback(subreq, kcm_cmd_done, req);
--    return req;
--
--immediate:
--    tevent_req_error(req, ret);
--    tevent_req_post(req, ev);
--    return req;
- }
- 
- static void kcm_cmd_done(struct tevent_req *subreq)
-diff --git a/src/responder/kcm/kcmsrv_ops.h b/src/responder/kcm/kcmsrv_ops.h
-index 8e6feaf56a10b73c8b6375aea9ef26c392b5b492..67d9f86026bf949548471f2280c130ebefd2f865 100644
---- a/src/responder/kcm/kcmsrv_ops.h
-+++ b/src/responder/kcm/kcmsrv_ops.h
-@@ -34,6 +34,7 @@ const char *kcm_opt_name(struct kcm_op *op);
- 
- struct tevent_req *kcm_cmd_send(TALLOC_CTX *mem_ctx,
-                                 struct tevent_context *ev,
-+                                struct kcm_ops_queue_ctx *qctx,
-                                 struct kcm_resp_ctx *kcm_data,
-                                 struct cli_creds *client,
-                                 struct kcm_data *input,
-diff --git a/src/responder/kcm/kcmsrv_pvt.h b/src/responder/kcm/kcmsrv_pvt.h
-index 74f30c00014105ed533744779b02c5d42523722d..f081a6bf0c6e40d2f8a83b07f9bbc2abacff359d 100644
---- a/src/responder/kcm/kcmsrv_pvt.h
-+++ b/src/responder/kcm/kcmsrv_pvt.h
-@@ -25,6 +25,7 @@
- #include "config.h"
- 
- #include <sys/types.h>
-+#include <krb5/krb5.h>
- #include "responder/common/responder.h"
- 
- /*
-@@ -65,6 +66,7 @@ struct kcm_ctx {
-     int fd_limit;
-     char *socket_path;
-     enum kcm_ccdb_be cc_be;
-+    struct kcm_ops_queue_ctx *qctx;
- 
-     struct kcm_resp_ctx *kcm_data;
- };
-@@ -78,4 +80,22 @@ int kcm_connection_setup(struct cli_ctx *cctx);
-  */
- krb5_error_code sss2krb5_error(errno_t err);
- 
-+/* We enqueue all requests by the same UID to avoid concurrency issues
-+ * especially when performing multiple round-trips to sssd-secrets. In
-+ * future, we should relax the queue to allow multiple read-only operations
-+ * if no write operations are in progress.
-+ */
-+struct kcm_ops_queue_entry;
-+
-+struct kcm_ops_queue_ctx *kcm_ops_queue_create(TALLOC_CTX *mem_ctx);
-+
-+struct tevent_req *kcm_op_queue_send(TALLOC_CTX *mem_ctx,
-+                                     struct tevent_context *ev,
-+                                     struct kcm_ops_queue_ctx *qctx,
-+                                     struct cli_creds *client);
-+
-+errno_t kcm_op_queue_recv(struct tevent_req *req,
-+                          TALLOC_CTX *mem_ctx,
-+                          struct kcm_ops_queue_entry **_entry);
-+
- #endif /* __KCMSRV_PVT_H__ */
-diff --git a/src/tests/cmocka/test_kcm_queue.c b/src/tests/cmocka/test_kcm_queue.c
-new file mode 100644
-index 0000000000000000000000000000000000000000..ba0d2405629960df5c623848f3207b7c80fa948d
---- /dev/null
-+++ b/src/tests/cmocka/test_kcm_queue.c
-@@ -0,0 +1,365 @@
-+/*
-+    Copyright (C) 2017 Red Hat
-+
-+    SSSD tests: Test KCM wait queue
-+
-+    This program is free software; you can redistribute it and/or modify
-+    it under the terms of the GNU General Public License as published by
-+    the Free Software Foundation; either version 3 of the License, or
-+    (at your option) any later version.
-+
-+    This program is distributed in the hope that it will be useful,
-+    but WITHOUT ANY WARRANTY; without even the implied warranty of
-+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+    GNU General Public License for more details.
-+
-+    You should have received a copy of the GNU General Public License
-+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
-+*/
-+
-+#include "config.h"
-+
-+#include <stdio.h>
-+#include <popt.h>
-+
-+#include "util/util.h"
-+#include "util/util_creds.h"
-+#include "tests/cmocka/common_mock.h"
-+#include "responder/kcm/kcmsrv_pvt.h"
-+
-+#define INVALID_ID      -1
-+#define FAST_REQ_ID     0
-+#define SLOW_REQ_ID     1
-+
-+#define FAST_REQ_DELAY  1
-+#define SLOW_REQ_DELAY  2
-+
-+struct timed_request_state {
-+    struct tevent_context *ev;
-+    struct kcm_ops_queue_ctx *qctx;
-+    struct cli_creds *client;
-+    int delay;
-+    int req_id;
-+
-+    struct kcm_ops_queue_entry *queue_entry;
-+};
-+
-+static void timed_request_start(struct tevent_req *subreq);
-+static void timed_request_done(struct tevent_context *ev,
-+                               struct tevent_timer *te,
-+                               struct timeval current_time,
-+                               void *pvt);
-+
-+static struct tevent_req *timed_request_send(TALLOC_CTX *mem_ctx,
-+                                             struct tevent_context *ev,
-+                                             struct kcm_ops_queue_ctx *qctx,
-+                                             struct cli_creds *client,
-+                                             int delay,
-+                                             int req_id)
-+{
-+    struct tevent_req *req;
-+    struct tevent_req *subreq;
-+    struct timed_request_state *state;
-+
-+    req = tevent_req_create(mem_ctx, &state, struct timed_request_state);
-+    if (req == NULL) {
-+        return NULL;
-+    }
-+    state->ev = ev;
-+    state->qctx = qctx;
-+    state->client = client;
-+    state->delay = delay;
-+    state->req_id = req_id;
-+
-+    DEBUG(SSSDBG_TRACE_ALL, "Request %p with delay %d\n", req, delay);
-+
-+    subreq = kcm_op_queue_send(state, ev, qctx, client);
-+    if (subreq == NULL) {
-+        return NULL;
-+    }
-+    tevent_req_set_callback(subreq, timed_request_start, req);
-+
-+    return req;
-+}
-+
-+static void timed_request_start(struct tevent_req *subreq)
-+{
-+    struct timeval tv;
-+    struct tevent_timer *timeout = NULL;
-+    struct tevent_req *req = tevent_req_callback_data(subreq,
-+                                                      struct tevent_req);
-+    struct timed_request_state *state = tevent_req_data(req,
-+                                                struct timed_request_state);
-+    errno_t ret;
-+
-+    ret = kcm_op_queue_recv(subreq, state, &state->queue_entry);
-+    talloc_zfree(subreq);
-+    if (ret != EOK) {
-+        tevent_req_error(req, ret);
-+        return;
-+    }
-+
-+    tv = tevent_timeval_current_ofs(state->delay, 0);
-+    timeout = tevent_add_timer(state->ev, state, tv, timed_request_done, req);
-+    if (timeout == NULL) {
-+        tevent_req_error(req, ENOMEM);
-+        return;
-+    }
-+
-+    return;
-+}
-+
-+static void timed_request_done(struct tevent_context *ev,
-+                               struct tevent_timer *te,
-+                               struct timeval current_time,
-+                               void *pvt)
-+{
-+    struct tevent_req *req = talloc_get_type(pvt, struct tevent_req);
-+    DEBUG(SSSDBG_TRACE_ALL, "Request %p done\n", req);
-+    tevent_req_done(req);
-+}
-+
-+static errno_t timed_request_recv(struct tevent_req *req,
-+                                  int *req_id)
-+{
-+    struct timed_request_state *state = tevent_req_data(req,
-+                                                struct timed_request_state);
-+
-+    TEVENT_REQ_RETURN_ON_ERROR(req);
-+    *req_id = state->req_id;
-+    return EOK;
-+}
-+
-+struct test_ctx {
-+    struct kcm_ops_queue_ctx *qctx;
-+    struct tevent_context *ev;
-+
-+    int *req_ids;
-+
-+    int num_requests;
-+    int finished_requests;
-+    bool done;
-+    errno_t error;
-+};
-+
-+static int setup_kcm_queue(void **state)
-+{
-+    struct test_ctx *tctx;
-+
-+    tctx = talloc_zero(NULL, struct test_ctx);
-+    assert_non_null(tctx);
-+
-+    tctx->ev = tevent_context_init(tctx);
-+    assert_non_null(tctx->ev);
-+
-+    tctx->qctx = kcm_ops_queue_create(tctx);
-+    assert_non_null(tctx->qctx);
-+
-+    *state = tctx;
-+    return 0;
-+}
-+
-+static int teardown_kcm_queue(void **state)
-+{
-+    struct test_ctx *tctx = talloc_get_type(*state, struct test_ctx);
-+    talloc_free(tctx);
-+    return 0;
-+}
-+
-+static void test_kcm_queue_done(struct tevent_req *req)
-+{
-+    struct test_ctx *test_ctx = tevent_req_callback_data(req,
-+                                                struct test_ctx);
-+    int req_id = INVALID_ID;
-+
-+    test_ctx->error = timed_request_recv(req, &req_id);
-+    talloc_zfree(req);
-+    if (test_ctx->error != EOK) {
-+        test_ctx->done = true;
-+        return;
-+    }
-+
-+    if (test_ctx->req_ids[test_ctx->finished_requests] != req_id) {
-+        DEBUG(SSSDBG_CRIT_FAILURE,
-+              "Request %d finished, expected %d\n",
-+              req_id, test_ctx->req_ids[test_ctx->finished_requests]);
-+        test_ctx->error = EIO;
-+        test_ctx->done = true;
-+        return;
-+    }
-+
-+    test_ctx->finished_requests++;
-+    if (test_ctx->finished_requests == test_ctx->num_requests) {
-+        test_ctx->done = true;
-+        return;
-+    }
-+}
-+
-+/*
-+ * Just make sure that a single pass through the queue works
-+ */
-+static void test_kcm_queue_single(void **state)
-+{
-+    struct test_ctx *test_ctx = talloc_get_type(*state, struct test_ctx);
-+    struct tevent_req *req;
-+    struct cli_creds client;
-+    static int req_ids[] = { 0 };
-+
-+    client.ucred.uid = getuid();
-+    client.ucred.gid = getgid();
-+
-+    req = timed_request_send(test_ctx,
-+                             test_ctx->ev,
-+                             test_ctx->qctx,
-+                             &client, 1, 0);
-+    assert_non_null(req);
-+    tevent_req_set_callback(req, test_kcm_queue_done, test_ctx);
-+
-+    test_ctx->num_requests = 1;
-+    test_ctx->req_ids = req_ids;
-+
-+    while (test_ctx->done == false) {
-+        tevent_loop_once(test_ctx->ev);
-+    }
-+    assert_int_equal(test_ctx->error, EOK);
-+}
-+
-+/*
-+ * Test that multiple requests from the same ID wait for one another
-+ */
-+static void test_kcm_queue_multi_same_id(void **state)
-+{
-+    struct test_ctx *test_ctx = talloc_get_type(*state, struct test_ctx);
-+    struct tevent_req *req;
-+    struct cli_creds client;
-+    /* The slow request will finish first because request from
-+     * the same ID are serialized
-+     */
-+    static int req_ids[] = { SLOW_REQ_ID, FAST_REQ_ID };
-+
-+    client.ucred.uid = getuid();
-+    client.ucred.gid = getgid();
-+
-+    req = timed_request_send(test_ctx,
-+                             test_ctx->ev,
-+                             test_ctx->qctx,
-+                             &client,
-+                             SLOW_REQ_DELAY,
-+                             SLOW_REQ_ID);
-+    assert_non_null(req);
-+    tevent_req_set_callback(req, test_kcm_queue_done, test_ctx);
-+
-+    req = timed_request_send(test_ctx,
-+                             test_ctx->ev,
-+                             test_ctx->qctx,
-+                             &client,
-+                             FAST_REQ_DELAY,
-+                             FAST_REQ_ID);
-+    assert_non_null(req);
-+    tevent_req_set_callback(req, test_kcm_queue_done, test_ctx);
-+
-+    test_ctx->num_requests = 2;
-+    test_ctx->req_ids = req_ids;
-+
-+    while (test_ctx->done == false) {
-+        tevent_loop_once(test_ctx->ev);
-+    }
-+    assert_int_equal(test_ctx->error, EOK);
-+}
-+
-+/*
-+ * Test that multiple requests from different IDs don't wait for one
-+ * another and can run concurrently
-+ */
-+static void test_kcm_queue_multi_different_id(void **state)
-+{
-+    struct test_ctx *test_ctx = talloc_get_type(*state, struct test_ctx);
-+    struct tevent_req *req;
-+    struct cli_creds client;
-+    /* In this test, the fast request will finish sooner because
-+     * both requests are from different IDs, allowing them to run
-+     * concurrently
-+     */
-+    static int req_ids[] = { FAST_REQ_ID, SLOW_REQ_ID };
-+
-+    client.ucred.uid = getuid();
-+    client.ucred.gid = getgid();
-+
-+    req = timed_request_send(test_ctx,
-+                             test_ctx->ev,
-+                             test_ctx->qctx,
-+                             &client,
-+                             SLOW_REQ_DELAY,
-+                             SLOW_REQ_ID);
-+    assert_non_null(req);
-+    tevent_req_set_callback(req, test_kcm_queue_done, test_ctx);
-+
-+    client.ucred.uid = getuid() + 1;
-+    client.ucred.gid = getgid() + 1;
-+
-+    req = timed_request_send(test_ctx,
-+                             test_ctx->ev,
-+                             test_ctx->qctx,
-+                             &client,
-+                             FAST_REQ_DELAY,
-+                             FAST_REQ_ID);
-+    assert_non_null(req);
-+    tevent_req_set_callback(req, test_kcm_queue_done, test_ctx);
-+
-+    test_ctx->num_requests = 2;
-+    test_ctx->req_ids = req_ids;
-+
-+    while (test_ctx->done == false) {
-+        tevent_loop_once(test_ctx->ev);
-+    }
-+    assert_int_equal(test_ctx->error, EOK);
-+}
-+
-+int main(int argc, const char *argv[])
-+{
-+    poptContext pc;
-+    int opt;
-+    int rv;
-+    struct poptOption long_options[] = {
-+        POPT_AUTOHELP
-+        SSSD_DEBUG_OPTS
-+        POPT_TABLEEND
-+    };
-+
-+    const struct CMUnitTest tests[] = {
-+        cmocka_unit_test_setup_teardown(test_kcm_queue_single,
-+                                        setup_kcm_queue,
-+                                        teardown_kcm_queue),
-+        cmocka_unit_test_setup_teardown(test_kcm_queue_multi_same_id,
-+                                        setup_kcm_queue,
-+                                        teardown_kcm_queue),
-+        cmocka_unit_test_setup_teardown(test_kcm_queue_multi_different_id,
-+                                        setup_kcm_queue,
-+                                        teardown_kcm_queue),
-+    };
-+
-+    /* Set debug level to invalid value so we can deside if -d 0 was used. */
-+    debug_level = SSSDBG_INVALID;
-+
-+    pc = poptGetContext(argv[0], argc, argv, long_options, 0);
-+    while((opt = poptGetNextOpt(pc)) != -1) {
-+        switch(opt) {
-+        default:
-+            fprintf(stderr, "\nInvalid option %s: %s\n\n",
-+                    poptBadOption(pc, 0), poptStrerror(opt));
-+            poptPrintUsage(pc, stderr, 0);
-+            return 1;
-+        }
-+    }
-+    poptFreeContext(pc);
-+
-+    DEBUG_CLI_INIT(debug_level);
-+
-+    /* Even though normally the tests should clean up after themselves
-+     * they might not after a failed run. Remove the old db to be sure */
-+    tests_set_cwd();
-+
-+    rv = cmocka_run_group_tests(tests, NULL, NULL);
-+
-+    return rv;
-+}
--- 
-2.9.3
-
diff --git a/SOURCES/0035-LDAP-Improve-error-treatment-from-sdap_cli_connect-i.patch b/SOURCES/0035-LDAP-Improve-error-treatment-from-sdap_cli_connect-i.patch
new file mode 100644
index 0000000..0854ab3
--- /dev/null
+++ b/SOURCES/0035-LDAP-Improve-error-treatment-from-sdap_cli_connect-i.patch
@@ -0,0 +1,58 @@
+From 1f46fa6760913de0f757e39106936d24e5736912 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Fabiano=20Fid=C3=AAncio?= <fidencio@redhat.com>
+Date: Tue, 7 Nov 2017 23:34:42 +0100
+Subject: [PATCH 35/35] LDAP: Improve error treatment from sdap_cli_connect()
+ in ldap_auth
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Because we weren't treating the errors coming from
+sdap_cli_connect_recv() properly we ended up introducing a regression in
+the commit add72860c7, related to offline authentication.
+
+From now on, let's properly treat errors coming from auth_connect_send(),
+which were treated before by going offline when be_resolve_server_recv()
+failed, and propagate ETIMEDOUT to the request, thus going offline and
+allowing offline authentication on those cases.
+
+Related:
+https://pagure.io/SSSD/sssd/issue/3451
+
+Signed-off-by: Fabiano Fidêncio <fidencio@redhat.com>
+Reviewed-by: Sumit Bose <sbose@redhat.com>
+(cherry picked from commit 20d18db36096e3fa2636143a83a12a2e3a7f26d6)
+---
+ src/providers/ldap/ldap_auth.c | 16 ++++++++++++++--
+ 1 file changed, 14 insertions(+), 2 deletions(-)
+
+diff --git a/src/providers/ldap/ldap_auth.c b/src/providers/ldap/ldap_auth.c
+index a3b1480aae4272d2e10f105a1eaf3a5816c3487c..2e0e2cfd6f8af2bf0c9ad15bd956a55a34777a3c 100644
+--- a/src/providers/ldap/ldap_auth.c
++++ b/src/providers/ldap/ldap_auth.c
+@@ -716,8 +716,20 @@ static void auth_connect_done(struct tevent_req *subreq)
+     ret = sdap_cli_connect_recv(subreq, state, NULL, &state->sh, NULL);
+     talloc_zfree(subreq);
+     if (ret != EOK) {
+-        if (auth_connect_send(req) == NULL) {
+-            tevent_req_error(req, ENOMEM);
++        /* As sdap_cli_connect_recv() returns EIO in case all the servers are
++         * down and we have to go offline, let's treat it accordingly here and
++         * allow the PAM responder to with to offline authentication.
++         *
++         * Unfortunately, there's not much pattern within our code and the way
++         * to indicate we're going down in this part of the code is returning
++         * an ETIMEDOUT.
++         */
++        if (ret == EIO) {
++            tevent_req_error(req, ETIMEDOUT);
++        } else {
++            if (auth_connect_send(req) == NULL) {
++                tevent_req_error(req, ENOMEM);
++            }
+         }
+         return;
+     }
+-- 
+2.13.6
+
diff --git a/SOURCES/0036-KCM-Idle-terminate-the-responder-if-the-secrets-back.patch b/SOURCES/0036-KCM-Idle-terminate-the-responder-if-the-secrets-back.patch
deleted file mode 100644
index a466a17..0000000
--- a/SOURCES/0036-KCM-Idle-terminate-the-responder-if-the-secrets-back.patch
+++ /dev/null
@@ -1,55 +0,0 @@
-From 7e6a8e7a6c37122fce8781e5f8e82458905960b3 Mon Sep 17 00:00:00 2001
-From: Jakub Hrozek <jhrozek@redhat.com>
-Date: Tue, 21 Mar 2017 14:26:54 +0100
-Subject: [PATCH 36/36] KCM: Idle-terminate the responder if the secrets back
- end is used
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Existing with memory database would be fatal as we keep the ccaches in
-memory then, but if the ccaches are stored in sssd-secrets, we can just
-exit on idle.
-
-Reviewed-by: Michal Židek <mzidek@redhat.com>
-Reviewed-by: Simo Sorce <simo@redhat.com>
----
- src/config/cfg_rules.ini | 1 +
- src/responder/kcm/kcm.c  | 9 +++++++++
- 2 files changed, 10 insertions(+)
-
-diff --git a/src/config/cfg_rules.ini b/src/config/cfg_rules.ini
-index 67a5d1f5ad447a942b437ffd04a7f5d7cfe77d7f..933ebccd828189d923d2186753dfbc0b5c0814ce 100644
---- a/src/config/cfg_rules.ini
-+++ b/src/config/cfg_rules.ini
-@@ -281,6 +281,7 @@ option = client_idle_timeout
- option = description
- option = socket_path
- option = ccache_storage
-+option = responder_idle_timeout
- 
- [rule/allowed_domain_options]
- validator = ini_allowed_options
-diff --git a/src/responder/kcm/kcm.c b/src/responder/kcm/kcm.c
-index 3ee978066c589a5cc38b0ae358f741d389d00e7a..2202f96381a2622a2c5433e281172287b325f960 100644
---- a/src/responder/kcm/kcm.c
-+++ b/src/responder/kcm/kcm.c
-@@ -133,6 +133,15 @@ static int kcm_get_config(struct kcm_ctx *kctx)
-         goto done;
-     }
- 
-+    if (kctx->cc_be == CCDB_BE_SECRETS) {
-+        ret = responder_setup_idle_timeout_config(kctx->rctx);
-+        if (ret != EOK) {
-+            DEBUG(SSSDBG_MINOR_FAILURE,
-+                  "Cannot set up idle responder timeout\n");
-+            /* Not fatal */
-+        }
-+    }
-+
-     kctx->qctx = kcm_ops_queue_create(kctx);
-     if (ret != EOK) {
-         DEBUG(SSSDBG_OP_FAILURE,
--- 
-2.9.3
-
diff --git a/SOURCES/0036-p11_child-return-multiple-certs.patch b/SOURCES/0036-p11_child-return-multiple-certs.patch
new file mode 100644
index 0000000..d504f7f
--- /dev/null
+++ b/SOURCES/0036-p11_child-return-multiple-certs.patch
@@ -0,0 +1,598 @@
+From 426dd731fe7c804c4b9d96c62d1a60a9022dcb09 Mon Sep 17 00:00:00 2001
+From: Sumit Bose <sbose@redhat.com>
+Date: Tue, 17 Jan 2017 15:55:18 +0100
+Subject: [PATCH 36/46] p11_child: return multiple certs
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+This patch refactors the handling of certificates in p11_child. Not only
+the first but all certificates suitable for authentication are returned.
+The PAM responder component calling p11_child is refactored to handle
+multiple certificate returned by p11_child but so far only returns the
+first one to its callers.
+
+Related to https://pagure.io/SSSD/sssd/issue/3560
+
+Reviewed-by: Fabiano Fidêncio <fidencio@redhat.com>
+Tested-by: Scott Poore <spoore@redhat.com>
+(cherry picked from commit 39fd336e4390ece3a8465714735ef4203f329e54)
+---
+ src/p11_child/p11_child_nss.c  | 131 +++++++++++++---------
+ src/responder/pam/pamsrv.h     |   2 +
+ src/responder/pam/pamsrv_p11.c | 242 +++++++++++++++++++++++------------------
+ 3 files changed, 219 insertions(+), 156 deletions(-)
+
+diff --git a/src/p11_child/p11_child_nss.c b/src/p11_child/p11_child_nss.c
+index b0ec69be321c4b4186ce851c07bfcc3e1afe9694..50bde2f4f91f6c00260b0db383d0962112686ebc 100644
+--- a/src/p11_child/p11_child_nss.c
++++ b/src/p11_child/p11_child_nss.c
+@@ -72,8 +72,7 @@ static char *password_passthrough(PK11SlotInfo *slot, PRBool retry, void *arg)
+ int do_work(TALLOC_CTX *mem_ctx, const char *nss_db, const char *slot_name_in,
+             enum op_mode mode, const char *pin,
+             struct cert_verify_opts *cert_verify_opts,
+-            char **cert, char **token_name_out, char **module_name_out,
+-            char **key_id_out)
++            char **_multi)
+ {
+     int ret;
+     SECStatus rv;
+@@ -110,7 +109,10 @@ int do_work(TALLOC_CTX *mem_ctx, const char *nss_db, const char *slot_name_in,
+     PK11SlotListElement *le;
+     SECItem *key_id = NULL;
+     char *key_id_str = NULL;
+-
++    CERTCertList *valid_certs = NULL;
++    char *cert_b64 = NULL;
++    char *multi = NULL;
++    PRCList *node;
+ 
+     nss_ctx = NSS_InitContext(nss_db, "", "", SECMOD_DB, &parameters, flags);
+     if (nss_ctx == NULL) {
+@@ -303,6 +305,14 @@ int do_work(TALLOC_CTX *mem_ctx, const char *nss_db, const char *slot_name_in,
+     }
+ 
+     found_cert = NULL;
++    valid_certs = CERT_NewCertList();
++    if (valid_certs == NULL) {
++        DEBUG(SSSDBG_OP_FAILURE, "CERT_NewCertList failed [%d].\n",
++                                 PR_GetError());
++        ret = ENOMEM;
++        goto done;
++    }
++
+     DEBUG(SSSDBG_TRACE_ALL, "Filtered certificates:\n");
+     for (cert_list_node = CERT_LIST_HEAD(cert_list);
+                 !CERT_LIST_END(cert_list_node, cert_list);
+@@ -326,6 +336,13 @@ int do_work(TALLOC_CTX *mem_ctx, const char *nss_db, const char *slot_name_in,
+                 }
+             }
+ 
++            rv = CERT_AddCertToListTail(valid_certs, cert_list_node->cert);
++            if (rv != SECSuccess) {
++                DEBUG(SSSDBG_OP_FAILURE,
++                      "CERT_AddCertToListTail failed [%d].\n", PR_GetError());
++                ret = EIO;
++                goto done;
++            }
+ 
+             if (found_cert == NULL) {
+                 found_cert = cert_list_node->cert;
+@@ -352,9 +369,7 @@ int do_work(TALLOC_CTX *mem_ctx, const char *nss_db, const char *slot_name_in,
+ 
+     if (found_cert == NULL) {
+         DEBUG(SSSDBG_TRACE_ALL, "No certificate found.\n");
+-        *cert = NULL;
+-        *token_name_out = NULL;
+-        *module_name_out = NULL;
++        *_multi = NULL;
+         ret = EOK;
+         goto done;
+     }
+@@ -421,51 +436,55 @@ int do_work(TALLOC_CTX *mem_ctx, const char *nss_db, const char *slot_name_in,
+               "Certificate verified and validated.\n");
+     }
+ 
+-    key_id = PK11_GetLowLevelKeyIDForCert(slot, found_cert, NULL);
+-    if (key_id == NULL) {
+-        DEBUG(SSSDBG_OP_FAILURE, "PK11_GetLowLevelKeyIDForCert failed [%d].\n",
+-                                 PR_GetError());
+-        ret = EINVAL;
+-        goto done;
+-    }
+-
+-    key_id_str = CERT_Hexify(key_id, PR_FALSE);
+-    if (key_id_str == NULL) {
+-        DEBUG(SSSDBG_OP_FAILURE, "CERT_Hexify failed [%d].\n", PR_GetError());
++    multi = talloc_strdup(mem_ctx, "");
++    if (multi == NULL) {
++        DEBUG(SSSDBG_CRIT_FAILURE, "Failed to create output string.\n");
+         ret = ENOMEM;
+         goto done;
+     }
+ 
+-    DEBUG(SSSDBG_TRACE_ALL, "Found certificate has key id [%s].\n", key_id_str);
++    for (cert_list_node = CERT_LIST_HEAD(valid_certs);
++                !CERT_LIST_END(cert_list_node, valid_certs);
++                cert_list_node = CERT_LIST_NEXT(cert_list_node)) {
+ 
+-    *key_id_out = talloc_strdup(mem_ctx, key_id_str);
+-    if (*key_id_out == NULL) {
+-        DEBUG(SSSDBG_CRIT_FAILURE, "Failed to copy key id.\n");
+-        ret = ENOMEM;
+-        goto done;
+-    }
++        found_cert = cert_list_node->cert;
+ 
+-    *cert = sss_base64_encode(mem_ctx, found_cert->derCert.data,
+-                                       found_cert->derCert.len);
+-    if (*cert == NULL) {
+-        DEBUG(SSSDBG_OP_FAILURE, "sss_base64_encode failed.\n");
+-        ret = ENOMEM;
+-        goto done;
+-    }
++        SECITEM_FreeItem(key_id, PR_TRUE);
++        PORT_Free(key_id_str);
++        key_id = PK11_GetLowLevelKeyIDForCert(slot, found_cert, NULL);
++        if (key_id == NULL) {
++            DEBUG(SSSDBG_OP_FAILURE,
++                  "PK11_GetLowLevelKeyIDForCert failed [%d].\n",
++                  PR_GetError());
++            ret = EINVAL;
++            goto done;
++        }
+ 
+-    *token_name_out = talloc_strdup(mem_ctx, token_name);
+-    if (*token_name_out == NULL) {
+-        DEBUG(SSSDBG_CRIT_FAILURE, "Failed to copy slot name.\n");
+-        ret = ENOMEM;
+-        goto done;
+-    }
++        key_id_str = CERT_Hexify(key_id, PR_FALSE);
++        if (key_id_str == NULL) {
++            DEBUG(SSSDBG_OP_FAILURE, "CERT_Hexify failed [%d].\n",
++                  PR_GetError());
++            ret = ENOMEM;
++            goto done;
++        }
+ 
+-    *module_name_out = talloc_strdup(mem_ctx, module_name);
+-    if (*module_name_out == NULL) {
+-        DEBUG(SSSDBG_CRIT_FAILURE, "Failed to copy module_name_out name.\n");
+-        ret = ENOMEM;
+-        goto done;
++        talloc_free(cert_b64);
++        cert_b64 = sss_base64_encode(mem_ctx, found_cert->derCert.data,
++                                     found_cert->derCert.len);
++        if (cert_b64 == NULL) {
++            DEBUG(SSSDBG_OP_FAILURE, "sss_base64_encode failed.\n");
++            ret = ENOMEM;
++            goto done;
++        }
++
++        DEBUG(SSSDBG_TRACE_ALL, "Found certificate has key id [%s].\n",
++              key_id_str);
++
++        multi = talloc_asprintf_append(multi, "%s\n%s\n%s\n%s\n",
++                                       token_name, module_name, key_id_str,
++                                       cert_b64);
+     }
++    *_multi = multi;
+ 
+     ret = EOK;
+ 
+@@ -474,6 +493,18 @@ done:
+         PK11_FreeSlot(slot);
+     }
+ 
++    if (valid_certs != NULL) {
++        /* The certificates can be found in valid_certs and cert_list and
++         * CERT_DestroyCertList() will free the certificates as well. To avoid
++         * a double free the nodes from valid_certs are removed first because
++         * valid_certs will only have a sub-set of the certificates. */
++        while (!PR_CLIST_IS_EMPTY(&valid_certs->list)) {
++            node = PR_LIST_HEAD(&valid_certs->list);
++            PR_REMOVE_LINK(node);
++        }
++        CERT_DestroyCertList(valid_certs);
++    }
++
+     if (cert_list != NULL) {
+         CERT_DestroyCertList(cert_list);
+     }
+@@ -483,6 +514,8 @@ done:
+ 
+     PORT_Free(signed_random_value.data);
+ 
++    talloc_free(cert_b64);
++
+     rv = NSS_ShutdownContext(nss_ctx);
+     if (rv != SECSuccess) {
+         DEBUG(SSSDBG_OP_FAILURE, "NSS_ShutdownContext failed [%d].\n",
+@@ -540,17 +573,14 @@ int main(int argc, const char *argv[])
+     const char *opt_logger = NULL;
+     errno_t ret;
+     TALLOC_CTX *main_ctx = NULL;
+-    char *cert;
+     enum op_mode mode = OP_NONE;
+     enum pin_mode pin_mode = PIN_NONE;
+     char *pin = NULL;
+     char *slot_name_in = NULL;
+-    char *token_name_out = NULL;
+-    char *module_name_out = NULL;
+-    char *key_id_out = NULL;
+     char *nss_db = NULL;
+     struct cert_verify_opts *cert_verify_opts;
+     char *verify_opts = NULL;
++    char *multi = NULL;
+ 
+     struct poptOption long_options[] = {
+         POPT_AUTOHELP
+@@ -715,17 +745,14 @@ int main(int argc, const char *argv[])
+     }
+ 
+     ret = do_work(main_ctx, nss_db, slot_name_in, mode, pin, cert_verify_opts,
+-                  &cert, &token_name_out, &module_name_out, &key_id_out);
++                  &multi);
+     if (ret != EOK) {
+         DEBUG(SSSDBG_OP_FAILURE, "do_work failed.\n");
+         goto fail;
+     }
+ 
+-    if (cert != NULL) {
+-        fprintf(stdout, "%s\n", token_name_out);
+-        fprintf(stdout, "%s\n", module_name_out);
+-        fprintf(stdout, "%s\n", key_id_out);
+-        fprintf(stdout, "%s\n", cert);
++    if (multi != NULL) {
++        fprintf(stdout, "%s", multi);
+     }
+ 
+     talloc_free(main_ctx);
+diff --git a/src/responder/pam/pamsrv.h b/src/responder/pam/pamsrv.h
+index 57a37b72594f030995f5e22255eb7a8fcd63d10e..896f71befbc9947a53b5eb20cba0bb3d104c4cf2 100644
+--- a/src/responder/pam/pamsrv.h
++++ b/src/responder/pam/pamsrv.h
+@@ -88,6 +88,8 @@ int LOCAL_pam_handler(struct pam_auth_req *preq);
+ 
+ errno_t p11_child_init(struct pam_ctx *pctx);
+ 
++struct cert_auth_info;
++
+ struct tevent_req *pam_check_cert_send(TALLOC_CTX *mem_ctx,
+                                        struct tevent_context *ev,
+                                        int child_debug_fd,
+diff --git a/src/responder/pam/pamsrv_p11.c b/src/responder/pam/pamsrv_p11.c
+index 4dce43800c3c6b026c545df35c846269cbb49610..ff32d1e726808caa36ca7cca557220866ef1a9ab 100644
+--- a/src/responder/pam/pamsrv_p11.c
++++ b/src/responder/pam/pamsrv_p11.c
+@@ -35,6 +35,15 @@
+ #define P11_CHILD_LOG_FILE "p11_child"
+ #define P11_CHILD_PATH SSSD_LIBEXEC_PATH"/p11_child"
+ 
++struct cert_auth_info {
++    char *cert;
++    char *token_name;
++    char *module_name;
++    char *key_id;
++    struct cert_auth_info *prev;
++    struct cert_auth_info *next;
++};
++
+ errno_t p11_child_init(struct pam_ctx *pctx)
+ {
+     return child_debug_init(P11_CHILD_LOG_FILE, &pctx->p11_child_debug_fd);
+@@ -132,18 +141,15 @@ static errno_t get_p11_child_write_buffer(TALLOC_CTX *mem_ctx,
+ }
+ 
+ static errno_t parse_p11_child_response(TALLOC_CTX *mem_ctx, uint8_t *buf,
+-                                        ssize_t buf_len, char **_cert,
+-                                        char **_token_name, char **_module_name,
+-                                        char **_key_id)
++                                        ssize_t buf_len,
++                                        struct cert_auth_info **_cert_list)
+ {
+     int ret;
+     TALLOC_CTX *tmp_ctx = NULL;
+     uint8_t *p;
+     uint8_t *pn;
+-    char *cert = NULL;
+-    char *token_name = NULL;
+-    char *module_name = NULL;
+-    char *key_id = NULL;
++    struct cert_auth_info *cert_list = NULL;
++    struct cert_auth_info *cert_auth_info;
+ 
+     if (buf_len < 0) {
+         DEBUG(SSSDBG_CRIT_FAILURE,
+@@ -157,108 +163,132 @@ static errno_t parse_p11_child_response(TALLOC_CTX *mem_ctx, uint8_t *buf,
+         goto done;
+     }
+ 
+-    p = memchr(buf, '\n', buf_len);
+-    if (p == NULL) {
+-        DEBUG(SSSDBG_OP_FAILURE, "Missing new-line in p11_child response.\n");
+-        return EINVAL;
+-    }
+-    if (p == buf) {
+-        DEBUG(SSSDBG_OP_FAILURE, "Missing counter in p11_child response.\n");
+-        return EINVAL;
+-    }
+-
+     tmp_ctx = talloc_new(NULL);
+     if (tmp_ctx == NULL) {
+         DEBUG(SSSDBG_OP_FAILURE, "talloc_new failed.\n");
+         return ENOMEM;
+     }
+ 
+-    token_name = talloc_strndup(tmp_ctx, (char*) buf, (p - buf));
+-    if (token_name == NULL) {
+-        DEBUG(SSSDBG_OP_FAILURE, "talloc_strndup failed.\n");
+-        ret = ENOMEM;
+-        goto done;
+-    }
++    p = buf;
+ 
+-    p++;
+-    pn = memchr(p, '\n', buf_len - (p - buf));
+-    if (pn == NULL) {
+-        DEBUG(SSSDBG_OP_FAILURE,
+-              "Missing new-line in p11_child response.\n");
+-        ret = EINVAL;
+-        goto done;
+-    }
++    do {
++        cert_auth_info = talloc_zero(tmp_ctx, struct cert_auth_info);
++        if (cert_auth_info == NULL) {
++            DEBUG(SSSDBG_OP_FAILURE, "talloc_zero failed.\n");
++            return ENOMEM;
++        }
+ 
+-    if (pn == p) {
+-        DEBUG(SSSDBG_OP_FAILURE,
+-              "Missing module name in p11_child response.\n");
+-        ret = EINVAL;
+-        goto done;
+-    }
++        pn = memchr(p, '\n', buf_len - (p - buf));
++        if (pn == NULL) {
++            DEBUG(SSSDBG_OP_FAILURE,
++                  "Missing new-line in p11_child response.\n");
++            return EINVAL;
++        }
++        if (pn == p) {
++            DEBUG(SSSDBG_OP_FAILURE,
++                  "Missing counter in p11_child response.\n");
++            return EINVAL;
++        }
+ 
+-    module_name = talloc_strndup(tmp_ctx, (char *) p, (pn - p));
+-    if (module_name == NULL) {
+-        DEBUG(SSSDBG_OP_FAILURE, "talloc_strndup failed.\n");
+-        ret = ENOMEM;
+-        goto done;
+-    }
+-    DEBUG(SSSDBG_TRACE_ALL, "Found module name [%s].\n", module_name);
++        cert_auth_info->token_name = talloc_strndup(cert_auth_info, (char *)p,
++                                                    (pn - p));
++        if (cert_auth_info->token_name == NULL) {
++            DEBUG(SSSDBG_OP_FAILURE, "talloc_strndup failed.\n");
++            ret = ENOMEM;
++            goto done;
++        }
++        DEBUG(SSSDBG_TRACE_ALL, "Found token name [%s].\n",
++              cert_auth_info->token_name);
+ 
+-    p = ++pn;
+-    pn = memchr(p, '\n', buf_len - (p - buf));
+-    if (pn == NULL) {
+-        DEBUG(SSSDBG_OP_FAILURE,
+-              "Missing new-line in p11_child response.\n");
+-        ret = EINVAL;
+-        goto done;
+-    }
++        p = ++pn;
++        pn = memchr(p, '\n', buf_len - (p - buf));
++        if (pn == NULL) {
++            DEBUG(SSSDBG_OP_FAILURE,
++                  "Missing new-line in p11_child response.\n");
++            ret = EINVAL;
++            goto done;
++        }
+ 
+-    if (pn == p) {
+-        DEBUG(SSSDBG_OP_FAILURE,
+-              "Missing key id in p11_child response.\n");
+-        ret = EINVAL;
+-        goto done;
+-    }
++        if (pn == p) {
++            DEBUG(SSSDBG_OP_FAILURE,
++                  "Missing module name in p11_child response.\n");
++            ret = EINVAL;
++            goto done;
++        }
+ 
+-    key_id = talloc_strndup(tmp_ctx, (char *) p, (pn - p));
+-    if (key_id == NULL) {
+-        DEBUG(SSSDBG_OP_FAILURE, "talloc_strndup failed.\n");
+-        ret = ENOMEM;
+-        goto done;
+-    }
+-    DEBUG(SSSDBG_TRACE_ALL, "Found key id [%s].\n", key_id);
++        cert_auth_info->module_name = talloc_strndup(cert_auth_info, (char *)p,
++                                                     (pn - p));
++        if (cert_auth_info->module_name == NULL) {
++            DEBUG(SSSDBG_OP_FAILURE, "talloc_strndup failed.\n");
++            ret = ENOMEM;
++            goto done;
++        }
++        DEBUG(SSSDBG_TRACE_ALL, "Found module name [%s].\n",
++              cert_auth_info->module_name);
+ 
+-    p = pn + 1;
+-    pn = memchr(p, '\n', buf_len - (p - buf));
+-    if (pn == NULL) {
+-        DEBUG(SSSDBG_OP_FAILURE,
+-              "Missing new-line in p11_child response.\n");
+-        ret = EINVAL;
+-        goto done;
+-    }
++        p = ++pn;
++        pn = memchr(p, '\n', buf_len - (p - buf));
++        if (pn == NULL) {
++            DEBUG(SSSDBG_OP_FAILURE,
++                  "Missing new-line in p11_child response.\n");
++            ret = EINVAL;
++            goto done;
++        }
+ 
+-    if (pn == p) {
+-        DEBUG(SSSDBG_OP_FAILURE, "Missing cert in p11_child response.\n");
+-        ret = EINVAL;
+-        goto done;
+-    }
++        if (pn == p) {
++            DEBUG(SSSDBG_OP_FAILURE,
++                  "Missing key id in p11_child response.\n");
++            ret = EINVAL;
++            goto done;
++        }
+ 
+-    cert = talloc_strndup(tmp_ctx, (char *) p, (pn - p));
+-    if(cert == NULL) {
+-        DEBUG(SSSDBG_OP_FAILURE, "talloc_strndup failed.\n");
+-        ret = ENOMEM;
+-        goto done;
+-    }
+-    DEBUG(SSSDBG_TRACE_ALL, "Found cert [%s].\n", cert);
++        cert_auth_info->key_id = talloc_strndup(cert_auth_info, (char *)p,
++                                                (pn - p));
++        if (cert_auth_info->key_id == NULL) {
++            DEBUG(SSSDBG_OP_FAILURE, "talloc_strndup failed.\n");
++            ret = ENOMEM;
++            goto done;
++        }
++        DEBUG(SSSDBG_TRACE_ALL, "Found key id [%s].\n", cert_auth_info->key_id);
++
++        p = ++pn;
++        pn = memchr(p, '\n', buf_len - (p - buf));
++        if (pn == NULL) {
++            DEBUG(SSSDBG_OP_FAILURE,
++                  "Missing new-line in p11_child response.\n");
++            ret = EINVAL;
++            goto done;
++        }
++
++        if (pn == p) {
++            DEBUG(SSSDBG_OP_FAILURE, "Missing cert in p11_child response.\n");
++            ret = EINVAL;
++            goto done;
++        }
++
++        cert_auth_info->cert = talloc_strndup(cert_auth_info, (char *)p,
++                                              (pn - p));
++        if (cert_auth_info->cert == NULL) {
++            DEBUG(SSSDBG_OP_FAILURE, "talloc_strndup failed.\n");
++            ret = ENOMEM;
++            goto done;
++        }
++        DEBUG(SSSDBG_TRACE_ALL, "Found cert [%s].\n", cert_auth_info->cert);
++
++        DLIST_ADD(cert_list, cert_auth_info);
++
++        p = ++pn;
++    } while ((pn - buf) < buf_len);
+ 
+     ret = EOK;
+ 
+ done:
+     if (ret == EOK) {
+-        *_token_name = talloc_steal(mem_ctx, token_name);
+-        *_cert = talloc_steal(mem_ctx, cert);
+-        *_module_name = talloc_steal(mem_ctx, module_name);
+-        *_key_id = talloc_steal(mem_ctx, key_id);
++        DLIST_FOR_EACH(cert_auth_info, cert_list) {
++            talloc_steal(mem_ctx, cert_auth_info);
++        }
++
++        *_cert_list = cert_list;
+     }
+ 
+     talloc_free(tmp_ctx);
+@@ -273,10 +303,8 @@ struct pam_check_cert_state {
+     struct tevent_context *ev;
+ 
+     struct child_io_fds *io;
+-    char *cert;
+-    char *token_name;
+-    char *module_name;
+-    char *key_id;
++
++    struct cert_auth_info *cert_list;
+ };
+ 
+ static void p11_child_write_done(struct tevent_req *subreq);
+@@ -349,9 +377,6 @@ struct tevent_req *pam_check_cert_send(TALLOC_CTX *mem_ctx,
+ 
+     state->ev = ev;
+     state->child_status = EFAULT;
+-    state->cert = NULL;
+-    state->token_name = NULL;
+-    state->module_name = NULL;
+     state->io = talloc(state, struct child_io_fds);
+     if (state->io == NULL) {
+         DEBUG(SSSDBG_CRIT_FAILURE, "talloc failed.\n");
+@@ -514,11 +539,9 @@ static void p11_child_done(struct tevent_req *subreq)
+ 
+     PIPE_FD_CLOSE(state->io->read_from_child_fd);
+ 
+-    ret = parse_p11_child_response(state, buf, buf_len, &state->cert,
+-                                   &state->token_name, &state->module_name,
+-                                   &state->key_id);
++    ret = parse_p11_child_response(state, buf, buf_len, &state->cert_list);
+     if (ret != EOK) {
+-        DEBUG(SSSDBG_OP_FAILURE, "parse_p11_child_respose failed.\n");
++        DEBUG(SSSDBG_OP_FAILURE, "parse_p11_child_response failed.\n");
+         tevent_req_error(req, ret);
+         return;
+     }
+@@ -551,20 +574,31 @@ errno_t pam_check_cert_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
+ 
+     TEVENT_REQ_RETURN_ON_ERROR(req);
+ 
++    if (state->cert_list == NULL) {
++        *token_name = NULL;
++        *cert = NULL;
++        *module_name = NULL;
++        *key_id = NULL;
++    }
++
+     if (cert != NULL) {
+-        *cert = talloc_steal(mem_ctx, state->cert);
++        *cert = (state->cert_list == NULL) ? NULL
++                                : talloc_steal(mem_ctx, state->cert_list->cert);
+     }
+ 
+     if (token_name != NULL) {
+-        *token_name = talloc_steal(mem_ctx, state->token_name);
++        *token_name = (state->cert_list == NULL) ? NULL
++                          : talloc_steal(mem_ctx, state->cert_list->token_name);
+     }
+ 
+     if (module_name != NULL) {
+-        *module_name = talloc_steal(mem_ctx, state->module_name);
++        *module_name = (state->cert_list == NULL) ? NULL
++                         : talloc_steal(mem_ctx, state->cert_list->module_name);
+     }
+ 
+     if (key_id != NULL) {
+-        *key_id = talloc_steal(mem_ctx, state->key_id);
++        *key_id = (state->cert_list == NULL) ? NULL
++                              : talloc_steal(mem_ctx, state->cert_list->key_id);
+     }
+ 
+     return EOK;
+-- 
+2.13.6
+
diff --git a/SOURCES/0037-CONFIGURE-Fix-fallback-if-pkg-config-for-uuid-is-mis.patch b/SOURCES/0037-CONFIGURE-Fix-fallback-if-pkg-config-for-uuid-is-mis.patch
deleted file mode 100644
index bdf9ef7..0000000
--- a/SOURCES/0037-CONFIGURE-Fix-fallback-if-pkg-config-for-uuid-is-mis.patch
+++ /dev/null
@@ -1,30 +0,0 @@
-From bb7c93869d53a412ce2537180752158861755ac4 Mon Sep 17 00:00:00 2001
-From: Lukas Slebodnik <lslebodn@redhat.com>
-Date: Mon, 27 Mar 2017 11:59:01 +0200
-Subject: [PATCH 37/54] CONFIGURE: Fix fallback if pkg-config for uuid is
- missing
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Reviewed-by: Fabiano Fidêncio <fidencio@redhat.com>
----
- src/external/libuuid.m4 | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
-diff --git a/src/external/libuuid.m4 b/src/external/libuuid.m4
-index 55411a2118bd787c9d50ba61f9cb791e1c76088d..323521c9224e443f40a15b417038d2dcea9b66f3 100644
---- a/src/external/libuuid.m4
-+++ b/src/external/libuuid.m4
-@@ -4,7 +4,7 @@ AC_SUBST(UUID_CFLAGS)
- PKG_CHECK_MODULES([UUID], [uuid], [found_uuid=yes], [found_uuid=no])
- 
- SSS_AC_EXPAND_LIB_DIR()
--AS_IF([test x"$found_uuid" = xyes],
-+AS_IF([test x"$found_uuid" != xyes],
-     [AC_CHECK_HEADERS([uuid/uuid.h],
-         [AC_CHECK_LIB([uuid],
-                       [uuid_generate],
--- 
-2.9.3
-
diff --git a/SOURCES/0037-PAM-handled-multiple-certs-in-the-responder.patch b/SOURCES/0037-PAM-handled-multiple-certs-in-the-responder.patch
new file mode 100644
index 0000000..dfac98b
--- /dev/null
+++ b/SOURCES/0037-PAM-handled-multiple-certs-in-the-responder.patch
@@ -0,0 +1,1074 @@
+From f4cac6544b5b6fb094d2088bf75f443fb74028bc Mon Sep 17 00:00:00 2001
+From: Sumit Bose <sbose@redhat.com>
+Date: Fri, 25 Aug 2017 12:51:09 +0200
+Subject: [PATCH 37/47] PAM: handled multiple certs in the responder
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+This patch refactors the handling of the certificate and the attributes
+to address the certificate on the Smartcard (module name, token name and
+key id). Instead of using individual variables the values are put into a
+new struct cert_auth_info. Since the new struct can be used as a list
+the PAM responder can now handle multiple certificates on the Smartcard
+and can send the needed data to pam_sss with multiple SSS_PAM_CERT_INFO
+messages.
+
+Unit tests are added to confirm the expected behavior.
+
+Related to https://pagure.io/SSSD/sssd/issue/3560
+
+Reviewed-by: Fabiano Fidêncio <fidencio@redhat.com>
+Tested-by: Scott Poore <spoore@redhat.com>
+(cherry picked from commit 0bdd8800c16f39b8fe308d20694ad905c669dff3)
+
+The following binaries have been removed from the original patch:
+- src/tests/cmocka/p11_nssdb_2certs/cert9.db
+- src/tests/cmocka/p11_nssdb_2certs/key4.db
+
+The reason for that is that we can't apply a patch which is a binary
+file using rpm, thus removing the patches here and adding them as source
+files in the sssd.spec seems to be the best solution.
+---
+ Makefile.am                                  |   2 +
+ src/responder/pam/pamsrv.h                   |  25 ++-
+ src/responder/pam/pamsrv_cmd.c               | 257 ++++++++++++++++++---------
+ src/responder/pam/pamsrv_p11.c               | 181 ++++++++++++++-----
+ src/tests/cmocka/p11_nssdb_2certs/pkcs11.txt |   4 +
+ src/tests/cmocka/test_pam_srv.c              | 216 +++++++++++++++++++++-
+ src/tests/whitespace_test                    |   2 +-
+ 7 files changed, 538 insertions(+), 149 deletions(-)
+ create mode 100644 src/tests/cmocka/p11_nssdb_2certs/pkcs11.txt
+
+diff --git a/Makefile.am b/Makefile.am
+index bbc90d9bad4d22ca0284ea95281a487d42399c05..4ed872a532daf9b934537cc5f64ce77778121e2a 100644
+--- a/Makefile.am
++++ b/Makefile.am
+@@ -481,6 +481,8 @@ dist_noinst_DATA = \
+     contrib/ci/sssd.supp \
+     src/tests/cmocka/p11_nssdb/cert9.db \
+     src/tests/cmocka/p11_nssdb/key4.db \
++    src/tests/cmocka/p11_nssdb_2certs/cert9.db \
++    src/tests/cmocka/p11_nssdb_2certs/key4.db \
+     $(SYSTEMTAP_PROBES) \
+     $(NULL)
+ 
+diff --git a/src/responder/pam/pamsrv.h b/src/responder/pam/pamsrv.h
+index 896f71befbc9947a53b5eb20cba0bb3d104c4cf2..f15f7f19f1f38626288416c9f2038371c6f58b47 100644
+--- a/src/responder/pam/pamsrv.h
++++ b/src/responder/pam/pamsrv.h
+@@ -73,10 +73,8 @@ struct pam_auth_req {
+     struct pam_auth_dp_req *dpreq_spy;
+ 
+     struct ldb_message *user_obj;
+-    struct ldb_result *cert_user_objs;
+-    char *token_name;
+-    char *module_name;
+-    char *key_id;
++    struct cert_auth_info *cert_list;
++    struct cert_auth_info *current_cert;
+     bool cert_auth_local;
+ };
+ 
+@@ -89,6 +87,16 @@ int LOCAL_pam_handler(struct pam_auth_req *preq);
+ errno_t p11_child_init(struct pam_ctx *pctx);
+ 
+ struct cert_auth_info;
++const char *sss_cai_get_cert(struct cert_auth_info *i);
++const char *sss_cai_get_token_name(struct cert_auth_info *i);
++const char *sss_cai_get_module_name(struct cert_auth_info *i);
++const char *sss_cai_get_key_id(struct cert_auth_info *i);
++struct cert_auth_info *sss_cai_get_next(struct cert_auth_info *i);
++struct ldb_result *sss_cai_get_cert_user_objs(struct cert_auth_info *i);
++void sss_cai_set_cert_user_objs(struct cert_auth_info *i,
++                                struct ldb_result *cert_user_objs);
++void sss_cai_check_users(struct cert_auth_info **list, size_t *_cert_count,
++                         size_t *_cert_user_count);
+ 
+ struct tevent_req *pam_check_cert_send(TALLOC_CTX *mem_ctx,
+                                        struct tevent_context *ev,
+@@ -98,12 +106,11 @@ struct tevent_req *pam_check_cert_send(TALLOC_CTX *mem_ctx,
+                                        const char *verify_opts,
+                                        struct pam_data *pd);
+ errno_t pam_check_cert_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
+-                            char **cert, char **token_name, char **module_name,
+-                            char **key_id);
++                            struct cert_auth_info **cert_list);
+ 
+-errno_t add_pam_cert_response(struct pam_data *pd, const char *user,
+-                              const char *token_name, const char *module_name,
+-                              const char *key_id, enum response_type type);
++errno_t add_pam_cert_response(struct pam_data *pd, const char *sysdb_username,
++                              struct cert_auth_info *cert_info,
++                              enum response_type type);
+ 
+ bool may_do_cert_auth(struct pam_ctx *pctx, struct pam_data *pd);
+ 
+diff --git a/src/responder/pam/pamsrv_cmd.c b/src/responder/pam/pamsrv_cmd.c
+index 51d8185650cf823da289a3398b10133065d82ae4..8b2c086e206796ad4c977495be957c56b3255e7f 100644
+--- a/src/responder/pam/pamsrv_cmd.c
++++ b/src/responder/pam/pamsrv_cmd.c
+@@ -1389,21 +1389,17 @@ done:
+     return pam_check_user_done(preq, ret);
+ }
+ 
++static errno_t pam_user_by_cert_step(struct pam_auth_req *preq);
+ static void pam_forwarder_lookup_by_cert_done(struct tevent_req *req);
+ static void pam_forwarder_cert_cb(struct tevent_req *req)
+ {
+     struct pam_auth_req *preq = tevent_req_callback_data(req,
+                                                          struct pam_auth_req);
+-    struct cli_ctx *cctx = preq->cctx;
+     struct pam_data *pd;
+     errno_t ret = EOK;
+-    char *cert;
+-    struct pam_ctx *pctx =
+-            talloc_get_type(preq->cctx->rctx->pvt_ctx, struct pam_ctx);
++    const char *cert;
+ 
+-    ret = pam_check_cert_recv(req, preq, &cert, &preq->token_name,
+-                                                &preq->module_name,
+-                                                &preq->key_id);
++    ret = pam_check_cert_recv(req, preq, &preq->cert_list);
+     talloc_free(req);
+     if (ret != EOK) {
+         DEBUG(SSSDBG_OP_FAILURE, "get_cert request failed.\n");
+@@ -1412,6 +1408,8 @@ static void pam_forwarder_cert_cb(struct tevent_req *req)
+ 
+     pd = preq->pd;
+ 
++    cert = sss_cai_get_cert(preq->cert_list);
++
+     if (cert == NULL) {
+         if (pd->logon_name == NULL) {
+             DEBUG(SSSDBG_CRIT_FAILURE,
+@@ -1431,21 +1429,42 @@ static void pam_forwarder_cert_cb(struct tevent_req *req)
+         goto done;
+     }
+ 
++    preq->current_cert = preq->cert_list;
++    ret = pam_user_by_cert_step(preq);
++    if (ret != EOK) {
++        DEBUG(SSSDBG_OP_FAILURE, "pam_user_by_cert_step failed.\n");
++        goto done;
++    }
++
++    return;
++
++done:
++    pam_check_user_done(preq, ret);
++}
++
++static errno_t pam_user_by_cert_step(struct pam_auth_req *preq)
++{
++    struct cli_ctx *cctx = preq->cctx;
++    struct tevent_req *req;
++    struct pam_ctx *pctx =
++            talloc_get_type(preq->cctx->rctx->pvt_ctx, struct pam_ctx);
++
++    if (preq->current_cert == NULL) {
++        DEBUG(SSSDBG_CRIT_FAILURE, "Missing certificate data.\n");
++        return EINVAL;
++    }
+ 
+     req = cache_req_user_by_cert_send(preq, cctx->ev, cctx->rctx,
+                                       pctx->rctx->ncache, 0,
+                                       preq->req_dom_type, NULL,
+-                                      cert);
++                                      sss_cai_get_cert(preq->current_cert));
+     if (req == NULL) {
+         DEBUG(SSSDBG_OP_FAILURE, "cache_req_user_by_cert_send failed.\n");
+-        ret = ENOMEM;
+-        goto done;
++        return ENOMEM;
+     }
++
+     tevent_req_set_callback(req, pam_forwarder_lookup_by_cert_done, preq);
+-    return;
+-
+-done:
+-    pam_check_user_done(preq, ret);
++    return EOK;
+ }
+ 
+ static errno_t get_results_from_all_domains(TALLOC_CTX *mem_ctx,
+@@ -1511,6 +1530,9 @@ static void pam_forwarder_lookup_by_cert_done(struct tevent_req *req)
+     struct pam_auth_req *preq = tevent_req_callback_data(req,
+                                                          struct pam_auth_req);
+     const char *cert_user = NULL;
++    size_t cert_count = 0;
++    size_t cert_user_count = 0;
++    struct ldb_result *cert_user_objs;
+ 
+     ret = cache_req_recv(preq, req, &results);
+     talloc_zfree(req);
+@@ -1521,12 +1543,39 @@ static void pam_forwarder_lookup_by_cert_done(struct tevent_req *req)
+ 
+     if (ret == EOK) {
+         ret = get_results_from_all_domains(preq, results,
+-                                           &preq->cert_user_objs);
++                                           &cert_user_objs);
+         if (ret != EOK) {
+             DEBUG(SSSDBG_OP_FAILURE, "get_results_from_all_domains failed.\n");
+             goto done;
+         }
+ 
++        sss_cai_set_cert_user_objs(preq->current_cert, cert_user_objs);
++    }
++
++    preq->current_cert = sss_cai_get_next(preq->current_cert);
++    if (preq->current_cert != NULL) {
++        ret = pam_user_by_cert_step(preq);
++        if (ret != EOK) {
++            DEBUG(SSSDBG_OP_FAILURE, "pam_user_by_cert_step failed.\n");
++            goto done;
++        }
++        return;
++    }
++
++    sss_cai_check_users(&preq->cert_list, &cert_count, &cert_user_count);
++    DEBUG(SSSDBG_TRACE_ALL,
++          "Found [%zu] certificates and [%zu] related users.\n",
++          cert_count, cert_user_count);
++
++    if (cert_user_count == 0) {
++        if (preq->pd->logon_name == NULL) {
++            DEBUG(SSSDBG_CRIT_FAILURE,
++                  "Missing logon name and no certificate user found.\n");
++            ret = ENOENT;
++            goto done;
++        }
++    } else {
++
+         if (preq->pd->logon_name == NULL) {
+             if (preq->pd->cmd != SSS_PAM_PREAUTH) {
+                 DEBUG(SSSDBG_CRIT_FAILURE,
+@@ -1535,9 +1584,39 @@ static void pam_forwarder_lookup_by_cert_done(struct tevent_req *req)
+                 goto done;
+             }
+ 
+-            if (preq->cert_user_objs->count == 1) {
++            if (cert_count > 1) {
++                for (preq->current_cert = preq->cert_list;
++                     preq->current_cert != NULL;
++                     preq->current_cert = sss_cai_get_next(preq->current_cert)) {
++
++                    ret = add_pam_cert_response(preq->pd, "",
++                                       preq->current_cert,
++                                       preq->cctx->rctx->domains->user_name_hint
++                                            ? SSS_PAM_CERT_INFO_WITH_HINT
++                                            : SSS_PAM_CERT_INFO);
++                    if (ret != EOK) {
++                        DEBUG(SSSDBG_OP_FAILURE,
++                              "add_pam_cert_response failed.\n");
++                        preq->pd->pam_status = PAM_AUTHINFO_UNAVAIL;
++                    }
++                }
++
++                ret = EOK;
++                preq->pd->pam_status = PAM_SUCCESS;
++                pam_reply(preq);
++                goto done;
++            }
++
++            if (cert_user_count == 1) {
++                cert_user_objs = sss_cai_get_cert_user_objs(preq->cert_list);
++                if (cert_user_objs == NULL) {
++                    DEBUG(SSSDBG_CRIT_FAILURE, "Missing certificate user.\n");
++                    ret = ENOENT;
++                    goto done;
++                }
++
+                 cert_user = ldb_msg_find_attr_as_string(
+-                                                  preq->cert_user_objs->msgs[0],
++                                                  cert_user_objs->msgs[0],
+                                                   SYSDB_NAME, NULL);
+                 if (cert_user == NULL) {
+                     DEBUG(SSSDBG_CRIT_FAILURE,
+@@ -1564,9 +1643,7 @@ static void pam_forwarder_lookup_by_cert_done(struct tevent_req *req)
+ 
+             if (preq->cctx->rctx->domains->user_name_hint) {
+                 ret = add_pam_cert_response(preq->pd, cert_user,
+-                                            preq->token_name,
+-                                            preq->module_name,
+-                                            preq->key_id,
++                                            preq->cert_list,
+                                             SSS_PAM_CERT_INFO_WITH_HINT);
+                 preq->pd->pam_status = PAM_SUCCESS;
+                 if (ret != EOK) {
+@@ -1596,13 +1673,6 @@ static void pam_forwarder_lookup_by_cert_done(struct tevent_req *req)
+                 goto done;
+             }
+         }
+-    } else {
+-        if (preq->pd->logon_name == NULL) {
+-            DEBUG(SSSDBG_CRIT_FAILURE,
+-                  "Missing logon name and no certificate user found.\n");
+-            ret = ENOENT;
+-            goto done;
+-        }
+     }
+ 
+     if (preq->user_obj == NULL) {
+@@ -1884,7 +1954,9 @@ static void pam_dom_forwarder(struct pam_auth_req *preq)
+     struct pam_ctx *pctx =
+             talloc_get_type(preq->cctx->rctx->pvt_ctx, struct pam_ctx);
+     const char *cert_user;
++    struct ldb_result *cert_user_objs;
+     size_t c;
++    bool found = false;
+ 
+     if (!preq->pd->domain) {
+         preq->pd->domain = preq->domain->name;
+@@ -1921,76 +1993,87 @@ static void pam_dom_forwarder(struct pam_auth_req *preq)
+         return;
+     }
+ 
+-    if (may_do_cert_auth(pctx, preq->pd) && preq->cert_user_objs != NULL) {
++    if (may_do_cert_auth(pctx, preq->pd) && preq->cert_list != NULL) {
+         /* Check if user matches certificate user */
+-        for (c = 0; c < preq->cert_user_objs->count; c++) {
+-            cert_user = ldb_msg_find_attr_as_string(
+-                                                  preq->cert_user_objs->msgs[c],
+-                                                  SYSDB_NAME,
+-                                                  NULL);
+-            if (cert_user == NULL) {
+-                /* Even if there might be other users mapped to the
+-                 * certificate a missing SYSDB_NAME indicates some critical
+-                 * condition which justifies that the whole request is aborted
+-                 * */
+-                DEBUG(SSSDBG_CRIT_FAILURE,
+-                      "Certificate user object has no name.\n");
+-                preq->pd->pam_status = PAM_USER_UNKNOWN;
+-                pam_reply(preq);
+-                return;
++        found = false;
++        for (preq->current_cert = preq->cert_list;
++             preq->current_cert != NULL;
++             preq->current_cert = sss_cai_get_next(preq->current_cert)) {
++
++            cert_user_objs = sss_cai_get_cert_user_objs(preq->current_cert);
++            if (cert_user_objs == NULL) {
++                DEBUG(SSSDBG_OP_FAILURE,
++                      "Unexpteced missing certificate user, "
++                      "trying next certificate.\n");
++                continue;
+             }
+ 
+-            /* pam_check_user_search() calls pd_set_primary_name() is the search
+-             * was successful, so pd->user contains the canonical sysdb name
+-             * as well */
+-            if (ldb_dn_compare(preq->cert_user_objs->msgs[c]->dn,
+-                               preq->user_obj->dn) == 0) {
+-
+-                if (preq->pd->cmd == SSS_PAM_PREAUTH) {
+-                    ret = sss_authtok_set_sc(preq->pd->authtok,
+-                                             SSS_AUTHTOK_TYPE_SC_PIN, NULL, 0,
+-                                             preq->token_name, 0,
+-                                             preq->module_name, 0,
+-                                             preq->key_id, 0);
+-                    if (ret != EOK) {
+-                        DEBUG(SSSDBG_OP_FAILURE, "sss_authtok_set_sc failed, "
+-                                                 "Smartcard authentication "
+-                                                 "detection might fail in the "
+-                                                 "backend.\n");
+-                    }
+-
+-                    ret = add_pam_cert_response(preq->pd, cert_user,
+-                                                preq->token_name,
+-                                                preq->module_name,
+-                                                preq->key_id,
+-                                                SSS_PAM_CERT_INFO);
+-                    if (ret != EOK) {
+-                        DEBUG(SSSDBG_OP_FAILURE, "add_pam_cert_response failed.\n");
+-                        preq->pd->pam_status = PAM_AUTHINFO_UNAVAIL;
+-                    }
+-                }
+-
+-                /* We are done if we do not have to call the backend */
+-                if (preq->pd->cmd == SSS_PAM_AUTHENTICATE
+-                        && preq->cert_auth_local) {
+-                    preq->pd->pam_status = PAM_SUCCESS;
+-                    preq->callback = pam_reply;
++            for (c = 0; c < cert_user_objs->count; c++) {
++                cert_user = ldb_msg_find_attr_as_string(cert_user_objs->msgs[c],
++                                                        SYSDB_NAME, NULL);
++                if (cert_user == NULL) {
++                    /* Even if there might be other users mapped to the
++                     * certificate a missing SYSDB_NAME indicates some critical
++                     * condition which justifies that the whole request is aborted
++                     * */
++                    DEBUG(SSSDBG_CRIT_FAILURE,
++                          "Certificate user object has no name.\n");
++                    preq->pd->pam_status = PAM_USER_UNKNOWN;
+                     pam_reply(preq);
+                     return;
+                 }
++
++                if (ldb_dn_compare(cert_user_objs->msgs[c]->dn,
++                                   preq->user_obj->dn) == 0) {
++                    found = true;
++                    if (preq->pd->cmd == SSS_PAM_PREAUTH) {
++                        ret = sss_authtok_set_sc(preq->pd->authtok,
++                                 SSS_AUTHTOK_TYPE_SC_PIN, NULL, 0,
++                                 sss_cai_get_token_name(preq->current_cert), 0,
++                                 sss_cai_get_module_name(preq->current_cert), 0,
++                                 sss_cai_get_key_id(preq->current_cert), 0);
++                        if (ret != EOK) {
++                            DEBUG(SSSDBG_OP_FAILURE,
++                                  "sss_authtok_set_sc failed, Smartcard "
++                                  "authentication detection might fail in "
++                                  "the backend.\n");
++                        }
++
++                        /* FIXME: use the right cert info */
++                        ret = add_pam_cert_response(preq->pd, cert_user,
++                                                    preq->current_cert,
++                                                    SSS_PAM_CERT_INFO);
++                        if (ret != EOK) {
++                            DEBUG(SSSDBG_OP_FAILURE, "add_pam_cert_response failed.\n");
++                            preq->pd->pam_status = PAM_AUTHINFO_UNAVAIL;
++                        }
++                    }
++
++                }
+             }
+         }
+ 
+-        if (preq->pd->cmd == SSS_PAM_PREAUTH) {
+-            DEBUG(SSSDBG_TRACE_FUNC,
+-                  "User and certificate user do not match, "
+-                  "continue with other authentication methods.\n");
++        if (found) {
++            /* We are done if we do not have to call the backend */
++            if (preq->pd->cmd == SSS_PAM_AUTHENTICATE
++                    && preq->cert_auth_local) {
++                preq->pd->pam_status = PAM_SUCCESS;
++                preq->callback = pam_reply;
++                pam_reply(preq);
++                return;
++            }
+         } else {
+-            DEBUG(SSSDBG_CRIT_FAILURE,
+-                  "User and certificate user do not match.\n");
+-            preq->pd->pam_status = PAM_AUTH_ERR;
+-            pam_reply(preq);
+-            return;
++            if (preq->pd->cmd == SSS_PAM_PREAUTH) {
++                DEBUG(SSSDBG_TRACE_FUNC,
++                      "User and certificate user do not match, "
++                      "continue with other authentication methods.\n");
++            } else {
++                DEBUG(SSSDBG_CRIT_FAILURE,
++                      "User and certificate user do not match.\n");
++                preq->pd->pam_status = PAM_AUTH_ERR;
++                pam_reply(preq);
++                return;
++            }
+         }
+     }
+ 
+diff --git a/src/responder/pam/pamsrv_p11.c b/src/responder/pam/pamsrv_p11.c
+index ff32d1e726808caa36ca7cca557220866ef1a9ab..57c8e1e464f4262f2d78f869c52ca48bd469d90a 100644
+--- a/src/responder/pam/pamsrv_p11.c
++++ b/src/responder/pam/pamsrv_p11.c
+@@ -40,10 +40,80 @@ struct cert_auth_info {
+     char *token_name;
+     char *module_name;
+     char *key_id;
++    struct ldb_result *cert_user_objs;
+     struct cert_auth_info *prev;
+     struct cert_auth_info *next;
+ };
+ 
++const char *sss_cai_get_cert(struct cert_auth_info *i)
++{
++    return i != NULL ? i->cert : NULL;
++}
++
++const char *sss_cai_get_token_name(struct cert_auth_info *i)
++{
++    return i != NULL ? i->token_name : NULL;
++}
++
++const char *sss_cai_get_module_name(struct cert_auth_info *i)
++{
++    return i != NULL ? i->module_name : NULL;
++}
++
++const char *sss_cai_get_key_id(struct cert_auth_info *i)
++{
++    return i != NULL ? i->key_id : NULL;
++}
++
++struct cert_auth_info *sss_cai_get_next(struct cert_auth_info *i)
++{
++    return i != NULL ? i->next : NULL;
++}
++
++struct ldb_result *sss_cai_get_cert_user_objs(struct cert_auth_info *i)
++{
++    return i != NULL ? i->cert_user_objs : NULL;
++}
++
++void sss_cai_set_cert_user_objs(struct cert_auth_info *i,
++                                struct ldb_result *cert_user_objs)
++{
++    if (i->cert_user_objs != NULL) {
++        talloc_free(i->cert_user_objs);
++    }
++    i->cert_user_objs = talloc_steal(i, cert_user_objs);
++}
++
++void sss_cai_check_users(struct cert_auth_info **list, size_t *_cert_count,
++                         size_t *_cert_user_count)
++{
++    struct cert_auth_info *c;
++    struct cert_auth_info *tmp;
++    size_t cert_count = 0;
++    size_t cert_user_count = 0;
++    struct ldb_result *user_objs;
++
++    DLIST_FOR_EACH_SAFE(c, tmp, *list) {
++        user_objs = sss_cai_get_cert_user_objs(c);
++        if (user_objs != NULL) {
++            cert_count++;
++            cert_user_count += user_objs->count;
++        } else {
++            DLIST_REMOVE(*list, c);
++        }
++    }
++
++    if (_cert_count != NULL) {
++        *_cert_count = cert_count;
++    }
++
++    if (_cert_user_count != NULL) {
++        *_cert_user_count = cert_user_count;
++    }
++
++    return;
++}
++
+ errno_t p11_child_init(struct pam_ctx *pctx)
+ {
+     return child_debug_init(P11_CHILD_LOG_FILE, &pctx->p11_child_debug_fd);
+@@ -566,39 +636,71 @@ static void p11_child_timeout(struct tevent_context *ev,
+ }
+ 
+ errno_t pam_check_cert_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
+-                            char **cert, char **token_name, char **module_name,
+-                            char **key_id)
++                            struct cert_auth_info **cert_list)
+ {
++    struct cert_auth_info *tmp_cert_auth_info;
+     struct pam_check_cert_state *state =
+                               tevent_req_data(req, struct pam_check_cert_state);
+ 
+     TEVENT_REQ_RETURN_ON_ERROR(req);
+ 
+-    if (state->cert_list == NULL) {
+-        *token_name = NULL;
+-        *cert = NULL;
+-        *module_name = NULL;
+-        *key_id = NULL;
++    if (cert_list != NULL) {
++        DLIST_FOR_EACH(tmp_cert_auth_info, state->cert_list) {
++            talloc_steal(mem_ctx, tmp_cert_auth_info);
++        }
++
++        *cert_list = state->cert_list;
+     }
+ 
+-    if (cert != NULL) {
+-        *cert = (state->cert_list == NULL) ? NULL
+-                                : talloc_steal(mem_ctx, state->cert_list->cert);
++    return EOK;
++}
++
++static errno_t pack_cert_data(TALLOC_CTX *mem_ctx, const char *sysdb_username,
++                              struct cert_auth_info *cert_info,
++                              uint8_t **_msg, size_t *_msg_len)
++{
++    uint8_t *msg = NULL;
++    size_t msg_len;
++    const char *token_name;
++    const char *module_name;
++    const char *key_id;
++    size_t user_len;
++    size_t token_len;
++    size_t module_len;
++    size_t key_id_len;
++    const char *username = "";
++
++    if (sysdb_username != NULL) {
++        username = sysdb_username;
+     }
+ 
+-    if (token_name != NULL) {
+-        *token_name = (state->cert_list == NULL) ? NULL
+-                          : talloc_steal(mem_ctx, state->cert_list->token_name);
++    token_name = sss_cai_get_token_name(cert_info);
++    module_name = sss_cai_get_module_name(cert_info);
++    key_id = sss_cai_get_key_id(cert_info);
++
++    user_len = strlen(username) + 1;
++    token_len = strlen(token_name) + 1;
++    module_len = strlen(module_name) + 1;
++    key_id_len = strlen(key_id) + 1;
++    msg_len = user_len + token_len + module_len + key_id_len;
++
++    msg = talloc_zero_size(mem_ctx, msg_len);
++    if (msg == NULL) {
++        DEBUG(SSSDBG_OP_FAILURE, "talloc_zero_size failed.\n");
++        return ENOMEM;
+     }
+ 
+-    if (module_name != NULL) {
+-        *module_name = (state->cert_list == NULL) ? NULL
+-                         : talloc_steal(mem_ctx, state->cert_list->module_name);
++    memcpy(msg, username, user_len);
++    memcpy(msg + user_len, token_name, token_len);
++    memcpy(msg + user_len + token_len, module_name, module_len);
++    memcpy(msg + user_len + token_len + module_len, key_id, key_id_len);
++
++    if (_msg != NULL) {
++        *_msg = msg;
+     }
+ 
+-    if (key_id != NULL) {
+-        *key_id = (state->cert_list == NULL) ? NULL
+-                              : talloc_steal(mem_ctx, state->cert_list->key_id);
++    if (_msg_len != NULL) {
++        *_msg_len = msg_len;
+     }
+ 
+     return EOK;
+@@ -613,18 +715,13 @@ errno_t pam_check_cert_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
+ #define PKCS11_LOGIN_TOKEN_ENV_NAME "PKCS11_LOGIN_TOKEN_NAME"
+ 
+ errno_t add_pam_cert_response(struct pam_data *pd, const char *sysdb_username,
+-                              const char *token_name, const char *module_name,
+-                              const char *key_id, enum response_type type)
++                              struct cert_auth_info *cert_info,
++                              enum response_type type)
+ {
+     uint8_t *msg = NULL;
+     char *env = NULL;
+-    size_t user_len;
+     size_t msg_len;
+-    size_t slot_len;
+-    size_t module_len;
+-    size_t key_id_len;
+     int ret;
+-    const char *username = "";
+ 
+     if (type != SSS_PAM_CERT_INFO && type != SSS_PAM_CERT_INFO_WITH_HINT) {
+         DEBUG(SSSDBG_CRIT_FAILURE, "Invalid response type [%d].\n", type);
+@@ -632,26 +729,14 @@ errno_t add_pam_cert_response(struct pam_data *pd, const char *sysdb_username,
+     }
+ 
+     if ((type == SSS_PAM_CERT_INFO && sysdb_username == NULL)
+-            || token_name == NULL || module_name == NULL || key_id == NULL) {
++            || cert_info == NULL
++            || sss_cai_get_token_name(cert_info) == NULL
++            || sss_cai_get_module_name(cert_info) == NULL
++            || sss_cai_get_key_id(cert_info) == NULL) {
+         DEBUG(SSSDBG_CRIT_FAILURE, "Missing mandatory user or slot name.\n");
+         return EINVAL;
+     }
+ 
+-    if (sysdb_username != NULL) {
+-        username = sysdb_username;
+-    }
+-    user_len = strlen(username) + 1;
+-    slot_len = strlen(token_name) + 1;
+-    module_len = strlen(module_name) + 1;
+-    key_id_len = strlen(key_id) + 1;
+-    msg_len = user_len + slot_len + module_len + key_id_len;
+-
+-    msg = talloc_zero_size(pd, msg_len);
+-    if (msg == NULL) {
+-        DEBUG(SSSDBG_OP_FAILURE, "talloc_zero_size failed.\n");
+-        return ENOMEM;
+-    }
+-
+     /* sysdb_username is a fully-qualified name which is used by pam_sss when
+      * prompting the user for the PIN and as login name if it wasn't set by
+      * the PAM caller but has to be determined based on the inserted
+@@ -659,10 +744,12 @@ errno_t add_pam_cert_response(struct pam_data *pd, const char *sysdb_username,
+      * re_expression config option was set in a way that user@domain cannot be
+      * handled anymore some more logic has to be added here. But for the time
+      * being I think using sysdb_username is fine. */
+-    memcpy(msg, username, user_len);
+-    memcpy(msg + user_len, token_name, slot_len);
+-    memcpy(msg + user_len + slot_len, module_name, module_len);
+-    memcpy(msg + user_len + slot_len + module_len, key_id, key_id_len);
++
++    ret = pack_cert_data(pd, sysdb_username, cert_info, &msg, &msg_len);
++    if (ret != EOK) {
++        DEBUG(SSSDBG_OP_FAILURE, "pack_cert_data failed.\n");
++        return ret;
++    }
+ 
+     ret = pam_add_response(pd, type, msg_len, msg);
+     talloc_free(msg);
+@@ -674,7 +761,7 @@ errno_t add_pam_cert_response(struct pam_data *pd, const char *sysdb_username,
+ 
+     if (strcmp(pd->service, "gdm-smartcard") == 0) {
+         env = talloc_asprintf(pd, "%s=%s", PKCS11_LOGIN_TOKEN_ENV_NAME,
+-                              token_name);
++                              sss_cai_get_token_name(cert_info));
+         if (env == NULL) {
+             DEBUG(SSSDBG_OP_FAILURE, "talloc_asprintf failed.\n");
+             return ENOMEM;
+diff --git a/src/tests/cmocka/p11_nssdb_2certs/pkcs11.txt b/src/tests/cmocka/p11_nssdb_2certs/pkcs11.txt
+new file mode 100644
+index 0000000000000000000000000000000000000000..73f5279c338dffe25ad2fad8c9cafae2f3c4cdfe
+--- /dev/null
++++ b/src/tests/cmocka/p11_nssdb_2certs/pkcs11.txt
+@@ -0,0 +1,4 @@
++library=
++name=NSS Internal PKCS #11 Module
++parameters=configdir='sql:../src/tests/cmocka/p11_nssdb' certPrefix='' keyPrefix='' secmod='secmod.db' flags= updatedir='' updateCertPrefix='' updateKeyPrefix='' updateid='' updateTokenDescription=''
++NSS=Flags=internal,critical trustOrder=75 cipherOrder=100 slotParams=(1={slotFlags=[RSA,DSA,DH,RC2,RC4,DES,RANDOM,SHA1,MD5,MD2,SSL,TLS,AES,Camellia,SEED,SHA256,SHA512] askpw=any timeout=30})
+diff --git a/src/tests/cmocka/test_pam_srv.c b/src/tests/cmocka/test_pam_srv.c
+index 351067eb664431cda159f73590de772920504380..7f0ed706512ffe0866c0e1fb7e6baa16bec942d8 100644
+--- a/src/tests/cmocka/test_pam_srv.c
++++ b/src/tests/cmocka/test_pam_srv.c
+@@ -47,6 +47,9 @@
+ #define NSS_DB_PATH TESTS_PATH
+ #define NSS_DB "sql:"NSS_DB_PATH
+ 
++#define NSS_DB_PATH_2CERTS TESTS_PATH "_2certs"
++#define NSS_DB_2CERTS "sql:"NSS_DB_PATH_2CERTS
++
+ #define TEST_TOKEN_NAME "SSSD Test Token"
+ #define TEST_MODULE_NAME "NSS-Internal"
+ #define TEST_KEY_ID "A5EF7DEE625CA5996C8D1BA7D036708161FD49E7"
+@@ -74,6 +77,28 @@
+ "8Z+9gqZhCa7FEKJOPNR9RVtJs0qUUutMZrp1zpyx0GTmXQBA7LbgPxy8L68uymEQ" \
+ "XyQBwOYRORlnfGyu+Yc9c3E0Wx8Tlznz0lqPR9g="
+ 
++#define TEST2_KEY_ID "C8D60E009EB195D01A7083EE1D5419251AA87C2C"
++#define TEST_TOKEN_2ND_CERT \
++"MIIDazCCAlOgAwIBAgIBBzANBgkqhkiG9w0BAQsFADA0MRIwEAYDVQQKDAlJUEEu" \
++"REVWRUwxHjAcBgNVBAMMFUNlcnRpZmljYXRlIEF1dGhvcml0eTAeFw0xNjA1MjMx" \
++"NDEzMDFaFw0xODA1MTMxNDEzMDFaMCUxEjAQBgNVBAoMCUlQQS5ERVZFTDEPMA0G" \
++"A1UEAwwGSVBBIFJBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3abE" \
++"8LmIc6QN16VVxsMlN/rrCOoZKyyJolSzpP4+K66t+KZUiW/1j1MZogjyYyD39U1F" \
++"zpa2H+pID74XYrdiqP7sp+uE9/k2XOv/nN3FobXDt+fSINLDriCmxNhUZqpgo2uq" \
++"Mmka+yx2iJZwkntEoJTcd3aynoa2Sa2ZZbkMBy5p6/pUQKwnD6scOwe6mUDppIBK" \
++"+ZZRm+u/NDdIRFI5wfKLRR1r/ONaJA9nz1TxSEsgLsjG/1m+Zbb6lGG4pePIFkQ9" \
++"Iotpi64obBh93oIxzQR29lBG/FMjQVHlPIbx+xuGx11Vtp5pAomgFz0HRrj0leI7" \
++"bROE+jnC/VGPLQD2aQIDAQABo4GWMIGTMB8GA1UdIwQYMBaAFPci/0Km5D/L5z7Y" \
++"qwEc7E1/GwgcMEEGCCsGAQUFBwEBBDUwMzAxBggrBgEFBQcwAYYlaHR0cDovL2lw" \
++"YS1kZXZlbC5pcGEuZGV2ZWw6ODAvY2Evb2NzcDAOBgNVHQ8BAf8EBAMCBPAwHQYD" \
++"VR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMA0GCSqGSIb3DQEBCwUAA4IBAQBg" \
++"4Sppx2C3eXPJ4Pd9XElkQPOaBReXf1vV0uk/GlK+rG+aAqAkA2Lryx5PK/iAuzAU" \
++"M6JUpELuQYgqugoCgBXMgsMlpAO/0C3CFq4ZH3KgIsRlRngKPrt6RG0UPMRD1CE2" \
++"tSVkwUWvyK83lDiu2BbWDXyMyz5eZOlp7uHusf5BKvob8jEndHj1YzaNTmVSsDM5" \
++"kiIwf8qgFhsO1HCq08PtAnbVHhqkcvnmIJN98eNWNfTKodDmFVbN8gB0wK+WB5ii" \
++"WVOw7+3/zF1QgqnYX3t+kPLRryip/wvTZkzXWwMNj/W6UHgjNF/4gWGoBgCHu+u3" \
++"EvjMmbVSrEkesibpGQS5"
++
+ 
+ static char CACHED_AUTH_TIMEOUT_STR[] = "4";
+ static const int CACHED_AUTH_TIMEOUT = 4;
+@@ -111,6 +136,13 @@ static errno_t setup_nss_db(void)
+         return ret;
+     }
+ 
++    ret = mkdir(NSS_DB_PATH_2CERTS, 0775);
++    if (ret != EOK) {
++        DEBUG(SSSDBG_FATAL_FAILURE,
++              "Failed to create " NSS_DB_PATH_2CERTS ".\n");
++        return ret;
++    }
++
+     child_pid = fork();
+     if (child_pid == 0) { /* child */
+         ret = execlp("certutil", "certutil", "-N", "--empty-password", "-d",
+@@ -127,6 +159,22 @@ static errno_t setup_nss_db(void)
+         return ret;
+     }
+ 
++    child_pid = fork();
++    if (child_pid == 0) { /* child */
++        ret = execlp("certutil", "certutil", "-N", "--empty-password", "-d",
++                     NSS_DB_2CERTS, NULL);
++        if (ret == -1) {
++            DEBUG(SSSDBG_FATAL_FAILURE, "execl() failed.\n");
++            exit(-1);
++        }
++    } else if (child_pid > 0) {
++        wait(&status);
++    } else {
++        ret = errno;
++        DEBUG(SSSDBG_FATAL_FAILURE, "fork() failed\n");
++        return ret;
++    }
++
+     fp = fopen(NSS_DB_PATH"/pkcs11.txt", "w");
+     if (fp == NULL) {
+         DEBUG(SSSDBG_FATAL_FAILURE, "fopen() failed.\n");
+@@ -148,6 +196,27 @@ static errno_t setup_nss_db(void)
+         return ret;
+     }
+ 
++    fp = fopen(NSS_DB_PATH_2CERTS"/pkcs11.txt", "w");
++    if (fp == NULL) {
++        DEBUG(SSSDBG_FATAL_FAILURE, "fopen() failed.\n");
++        return ret;
++    }
++    ret = fprintf(fp, "library=libsoftokn3.so\nname=soft\n");
++    if (ret < 0) {
++        DEBUG(SSSDBG_FATAL_FAILURE, "fprintf() failed.\n");
++        return ret;
++    }
++    ret = fprintf(fp, "parameters=configdir='sql:%s/src/tests/cmocka/p11_nssdb_2certs' dbSlotDescription='SSSD Test Slot' dbTokenDescription='SSSD Test Token' secmod='secmod.db' flags=readOnly \n\n", ABS_SRC_DIR);
++    if (ret < 0) {
++        DEBUG(SSSDBG_FATAL_FAILURE, "fprintf() failed.\n");
++        return ret;
++    }
++    ret = fclose(fp);
++    if (ret != 0) {
++        DEBUG(SSSDBG_FATAL_FAILURE, "fclose() failed.\n");
++        return ret;
++    }
++
+     return EOK;
+ }
+ 
+@@ -174,6 +243,26 @@ static void cleanup_nss_db(void)
+     if (ret != EOK) {
+         DEBUG(SSSDBG_OP_FAILURE, "Failed to remove " NSS_DB_PATH "\n");
+     }
++
++    ret = unlink(NSS_DB_PATH_2CERTS"/cert9.db");
++    if (ret != EOK) {
++        DEBUG(SSSDBG_OP_FAILURE, "Failed to remove cert9.db.\n");
++    }
++
++    ret = unlink(NSS_DB_PATH_2CERTS"/key4.db");
++    if (ret != EOK) {
++        DEBUG(SSSDBG_OP_FAILURE, "Failed to remove key4.db.\n");
++    }
++
++    ret = unlink(NSS_DB_PATH_2CERTS"/pkcs11.txt");
++    if (ret != EOK) {
++        DEBUG(SSSDBG_OP_FAILURE, "Failed to remove pkcs11.db.\n");
++    }
++
++    ret = rmdir(NSS_DB_PATH_2CERTS);
++    if (ret != EOK) {
++        DEBUG(SSSDBG_OP_FAILURE, "Failed to remove " NSS_DB_PATH "\n");
++    }
+ }
+ 
+ struct pam_ctx *mock_pctx(TALLOC_CTX *mem_ctx)
+@@ -749,7 +838,8 @@ static int test_pam_cert_check_gdm_smartcard(uint32_t status, uint8_t *body,
+ }
+ 
+ static int test_pam_cert_check_ex(uint32_t status, uint8_t *body, size_t blen,
+-                                  enum response_type type, const char *name)
++                                  enum response_type type, const char *name,
++                                  const char *name2)
+ {
+     size_t rp = 0;
+     uint32_t val;
+@@ -763,7 +853,11 @@ static int test_pam_cert_check_ex(uint32_t status, uint8_t *body, size_t blen,
+     if (name == NULL || *name == '\0') {
+         assert_int_equal(val, 1);
+     } else {
+-        assert_int_equal(val, 2);
++        if (name2 == NULL || *name2 == '\0') {
++            assert_int_equal(val, 2);
++        } else {
++            assert_int_equal(val, 3);
++        }
+ 
+         SAFEALIGN_COPY_UINT32(&val, body + rp, &rp);
+         assert_int_equal(val, SSS_PAM_DOMAIN_NAME);
+@@ -801,6 +895,33 @@ static int test_pam_cert_check_ex(uint32_t status, uint8_t *body, size_t blen,
+     assert_string_equal(body + rp, TEST_KEY_ID);
+     rp += sizeof(TEST_KEY_ID);
+ 
++    if (name2 != NULL && *name2 != '\0') {
++        SAFEALIGN_COPY_UINT32(&val, body + rp, &rp);
++        assert_int_equal(val, type);
++
++        SAFEALIGN_COPY_UINT32(&val, body + rp, &rp);
++        assert_int_equal(val, (strlen(name) + 1
++                                    + sizeof(TEST_TOKEN_NAME)
++                                    + sizeof(TEST_MODULE_NAME)
++                                    + sizeof(TEST2_KEY_ID)));
++
++        assert_int_equal(*(body + rp + strlen(name)), 0);
++        assert_string_equal(body + rp, name);
++        rp += strlen(name) + 1;
++
++        assert_int_equal(*(body + rp + sizeof(TEST_TOKEN_NAME) - 1), 0);
++        assert_string_equal(body + rp, TEST_TOKEN_NAME);
++        rp += sizeof(TEST_TOKEN_NAME);
++
++        assert_int_equal(*(body + rp + sizeof(TEST_MODULE_NAME) - 1), 0);
++        assert_string_equal(body + rp, TEST_MODULE_NAME);
++        rp += sizeof(TEST_MODULE_NAME);
++
++        assert_int_equal(*(body + rp + sizeof(TEST2_KEY_ID) - 1), 0);
++        assert_string_equal(body + rp, TEST2_KEY_ID);
++        rp += sizeof(TEST2_KEY_ID);
++    }
++
+     assert_int_equal(rp, blen);
+ 
+     return EOK;
+@@ -809,7 +930,8 @@ static int test_pam_cert_check_ex(uint32_t status, uint8_t *body, size_t blen,
+ static int test_pam_cert_check(uint32_t status, uint8_t *body, size_t blen)
+ {
+     return test_pam_cert_check_ex(status, body, blen,
+-                                  SSS_PAM_CERT_INFO, "pamuser@"TEST_DOM_NAME);
++                                  SSS_PAM_CERT_INFO, "pamuser@"TEST_DOM_NAME,
++                                  NULL);
+ }
+ 
+ static int test_pam_cert_check_with_hint(uint32_t status, uint8_t *body,
+@@ -817,14 +939,22 @@ static int test_pam_cert_check_with_hint(uint32_t status, uint8_t *body,
+ {
+     return test_pam_cert_check_ex(status, body, blen,
+                                   SSS_PAM_CERT_INFO_WITH_HINT,
+-                                  "pamuser@"TEST_DOM_NAME);
++                                  "pamuser@"TEST_DOM_NAME, NULL);
+ }
+ 
+ static int test_pam_cert_check_with_hint_no_user(uint32_t status, uint8_t *body,
+                                                  size_t blen)
+ {
+     return test_pam_cert_check_ex(status, body, blen,
+-                                  SSS_PAM_CERT_INFO_WITH_HINT, "");
++                                  SSS_PAM_CERT_INFO_WITH_HINT, "", NULL);
++}
++
++static int test_pam_cert_check_2certs(uint32_t status, uint8_t *body,
++                                      size_t blen)
++{
++    return test_pam_cert_check_ex(status, body, blen,
++                                  SSS_PAM_CERT_INFO, "pamuser@"TEST_DOM_NAME,
++                                  "pamuser@"TEST_DOM_NAME);
+ }
+ 
+ static int test_pam_offline_chauthtok_check(uint32_t status,
+@@ -1737,6 +1867,33 @@ static int test_lookup_by_cert_cb(void *pvt)
+ 
+     return EOK;
+ }
++static int test_lookup_by_cert_cb_2nd_cert_same_user(void *pvt)
++{
++    int ret;
++    struct sysdb_attrs *attrs;
++    unsigned char *der = NULL;
++    size_t der_size;
++
++    test_lookup_by_cert_cb(pvt);
++
++    attrs = sysdb_new_attrs(pam_test_ctx);
++    assert_non_null(attrs);
++
++    der = sss_base64_decode(pam_test_ctx, TEST_TOKEN_2ND_CERT, &der_size);
++    assert_non_null(der);
++
++    ret = sysdb_attrs_add_mem(attrs, SYSDB_USER_MAPPED_CERT, der, der_size);
++    talloc_free(der);
++    assert_int_equal(ret, EOK);
++
++    ret = sysdb_set_user_attr(pam_test_ctx->tctx->dom,
++                              pam_test_ctx->pam_user_fqdn,
++                              attrs,
++                              LDB_FLAG_MOD_ADD);
++    assert_int_equal(ret, EOK);
++
++    return EOK;
++}
+ 
+ static int test_lookup_by_cert_double_cb(void *pvt)
+ {
+@@ -2094,6 +2251,51 @@ void test_pam_cert_auth_double_cert(void **state)
+     assert_int_equal(ret, EOK);
+ }
+ 
++void test_pam_cert_preauth_2certs_one_mapping(void **state)
++{
++    int ret;
++
++    set_cert_auth_param(pam_test_ctx->pctx, NSS_DB_2CERTS);
++
++    mock_input_pam_cert(pam_test_ctx, "pamuser", NULL, NULL,
++                        test_lookup_by_cert_cb, TEST_TOKEN_CERT, false);
++
++    will_return(__wrap_sss_packet_get_cmd, SSS_PAM_PREAUTH);
++    will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL);
++
++    set_cmd_cb(test_pam_cert_check);
++    ret = sss_cmd_execute(pam_test_ctx->cctx, SSS_PAM_PREAUTH,
++                          pam_test_ctx->pam_cmds);
++    assert_int_equal(ret, EOK);
++
++    /* Wait until the test finishes with EOK */
++    ret = test_ev_loop(pam_test_ctx->tctx);
++    assert_int_equal(ret, EOK);
++}
++
++void test_pam_cert_preauth_2certs_two_mappings(void **state)
++{
++    int ret;
++
++    set_cert_auth_param(pam_test_ctx->pctx, NSS_DB_2CERTS);
++
++    mock_input_pam_cert(pam_test_ctx, "pamuser", NULL, NULL,
++                        test_lookup_by_cert_cb_2nd_cert_same_user,
++                        TEST_TOKEN_CERT, false);
++
++    will_return(__wrap_sss_packet_get_cmd, SSS_PAM_PREAUTH);
++    will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL);
++
++    set_cmd_cb(test_pam_cert_check_2certs);
++    ret = sss_cmd_execute(pam_test_ctx->cctx, SSS_PAM_PREAUTH,
++                          pam_test_ctx->pam_cmds);
++    assert_int_equal(ret, EOK);
++
++    /* Wait until the test finishes with EOK */
++    ret = test_ev_loop(pam_test_ctx->tctx);
++    assert_int_equal(ret, EOK);
++}
++
+ void test_filter_response(void **state)
+ {
+     int ret;
+@@ -2523,6 +2725,10 @@ int main(int argc, const char *argv[])
+                                         pam_test_teardown),
+         cmocka_unit_test_setup_teardown(test_pam_cert_auth_double_cert,
+                                         pam_test_setup, pam_test_teardown),
++        cmocka_unit_test_setup_teardown(test_pam_cert_preauth_2certs_one_mapping,
++                                        pam_test_setup, pam_test_teardown),
++        cmocka_unit_test_setup_teardown(test_pam_cert_preauth_2certs_two_mappings,
++                                        pam_test_setup, pam_test_teardown),
+ #endif /* HAVE_NSS */
+ 
+         cmocka_unit_test_setup_teardown(test_filter_response,
+diff --git a/src/tests/whitespace_test b/src/tests/whitespace_test
+index 799e35358b1d5ae4b10c4405068fb507cb234b6f..f055ed4c255db4001194844f45a9df7cda774b38 100755
+--- a/src/tests/whitespace_test
++++ b/src/tests/whitespace_test
+@@ -39,7 +39,7 @@ fi
+ declare found_file=false
+ while read file; do
+     [[ $file == "src/config/testconfigs/noparse.api.conf" ]] && continue
+-    [[ $file =~ ^src/tests/cmocka/p11_nssdb/.*db ]] && continue
++    [[ $file =~ ^src/tests/cmocka/p11_nssdb.*/.*db ]] && continue
+     test `tail -c 1 $ABS_TOP_SRCDIR/$file` && \
+         echo "Missing new line at the eof: $file" && \
+         found_file=true
+-- 
+2.13.6
+
diff --git a/SOURCES/0038-intg-fix-configure-failure-with-strict-cflags.patch b/SOURCES/0038-intg-fix-configure-failure-with-strict-cflags.patch
deleted file mode 100644
index 5c7c3be..0000000
--- a/SOURCES/0038-intg-fix-configure-failure-with-strict-cflags.patch
+++ /dev/null
@@ -1,52 +0,0 @@
-From 076bd32668f7ea194389ddd526ea81f9bf12fb0e Mon Sep 17 00:00:00 2001
-From: Lukas Slebodnik <lslebodn@redhat.com>
-Date: Tue, 28 Mar 2017 12:18:13 +0200
-Subject: [PATCH 38/54] intg: fix configure failure with strict cflags
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-The warning -Wstrict-prototypes is a part of AM_CFLAGS which was appended
-for CFLAGS in make target intgcheck-prepare. And combination with
-strict CFLAGS in environment variable (e.g. -Werror) caused failures.
-
-sh$ CFLAGS="-Werror" make intgcheck-prepare
-
-checking for gcc... gcc
-checking whether the C compiler works... no
-configure: error: in `/home/build/sssd/ci-build-debug/intg/bld':
-configure: error: C compiler cannot create executables
-
-configure:3719: checking whether the C compiler works
-configure:3741: gcc -g3 -O2 -Werror -D_FILE_OFFSET_BITS=64
-                -D_LARGEFILE_SOURCE -Wall -Wshadow -Wstrict-prototypes
-                -Wpointer-arith -Wcast-qual -Wcast-align -Wwrite-strings
-                -Wundef -Werror-implicit-function-declaration
-                -Winit-self -Wmissing-include-dirs -fno-strict-aliasing
-                -std=gnu99 -DKCM_PEER_UID=1000   conftest.c  >&5
-conftest.c:11:1: error: function declaration isn't a prototype [-Werror=strict-prototypes]
- main ()
- ^~~~
-cc1: all warnings being treated as errors
-
-Reviewed-by: Pavel Březina <pbrezina@redhat.com>
----
- Makefile.am | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
-diff --git a/Makefile.am b/Makefile.am
-index 91afdd669aa11a3cc316588d3b51d7e8e9c91cb8..359feddef298b0013c726409b7ba8b86504abf09 100644
---- a/Makefile.am
-+++ b/Makefile.am
-@@ -3486,7 +3486,7 @@ intgcheck-prepare:
- 	    --without-semanage \
- 	    --enable-files-domain \
- 	    $(INTGCHECK_CONFIGURE_FLAGS) \
--	    CFLAGS="$$CFLAGS $(AM_CFLAGS) -DKCM_PEER_UID=$$(id -u)"; \
-+	    CFLAGS="$$CFLAGS -DKCM_PEER_UID=$$(id -u)"; \
- 	$(MAKE) $(AM_MAKEFLAGS) ; \
- 	: Force single-thread install to workaround concurrency issues; \
- 	$(MAKE) $(AM_MAKEFLAGS) -j1 install; \
--- 
-2.9.3
-
diff --git a/SOURCES/0038-pam_sss-refactoring-use-struct-cert_auth_info.patch b/SOURCES/0038-pam_sss-refactoring-use-struct-cert_auth_info.patch
new file mode 100644
index 0000000..fc2b370
--- /dev/null
+++ b/SOURCES/0038-pam_sss-refactoring-use-struct-cert_auth_info.patch
@@ -0,0 +1,680 @@
+From cee84ed12721092bc40bc02dc66ce3efbb2bac74 Mon Sep 17 00:00:00 2001
+From: Sumit Bose <sbose@redhat.com>
+Date: Mon, 16 Oct 2017 14:13:10 +0200
+Subject: [PATCH 38/46] pam_sss: refactoring, use struct cert_auth_info
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Similar as in the PAM responder this patch replaces the individual
+certificate authentication related attributes by a struct which can be
+used as a list. With the pam_sss can handle multiple SSS_PAM_CERT_INFO
+message and place the data in individual list items.
+
+If multiple certificates are returned before prompting for the PIN a
+dialog to select a certificate is shown to the users. If available a GDM
+PAM extension is used to let the user choose from a list. All coded
+needed at runtime to check if the extension is available and handle the
+data is provided by GDM as macros. This means that there are no
+additional run-time requirements.
+
+Related to https://pagure.io/SSSD/sssd/issue/3560
+
+Reviewed-by: Fabiano Fidêncio <fidencio@redhat.com>
+Tested-by: Scott Poore <spoore@redhat.com>
+(cherry picked from commit 122830e67472390b41edc73f0cfcd5c5705b726b)
+---
+ contrib/sssd.spec.in         |   9 +
+ src/external/pam.m4          |  12 ++
+ src/sss_client/pam_message.h |   8 +-
+ src/sss_client/pam_sss.c     | 439 ++++++++++++++++++++++++++++++++++---------
+ 4 files changed, 370 insertions(+), 98 deletions(-)
+
+diff --git a/contrib/sssd.spec.in b/contrib/sssd.spec.in
+index 1ee64d5a2a64635984260fceced779f4804e8b31..d9323bf1a2d84f4219f8ab11886e5ce87b401c15 100644
+--- a/contrib/sssd.spec.in
++++ b/contrib/sssd.spec.in
+@@ -121,6 +121,12 @@
+     %global with_kcm_option --without-kcm
+ %endif
+ 
++%if (0%{?fedora} >= 27 || (0%{?rhel} >= 7 && 0%{?rhel7_minor} > 4))
++    %global with_gdm_pam_extensions 1
++%else
++    %global with_gdm_pam_extensions 0
++%endif
++
+ Name: @PACKAGE_NAME@
+ Version: @PACKAGE_VERSION@
+ Release: 0@PRERELEASE_VERSION@%{?dist}
+@@ -233,6 +239,9 @@ BuildRequires: libuuid-devel
+ BuildRequires: jansson-devel
+ BuildRequires: libcurl-devel
+ %endif
++%if (0%{?with_gdm_pam_extensions} == 1)
++BuildRequires: gdm-devel
++%endif
+ 
+ %description
+ Provides a set of daemons to manage access to remote directories and
+diff --git a/src/external/pam.m4 b/src/external/pam.m4
+index 4776b6ae338409f0a2729dfc4cf5962463a40dfd..0dc7f19d0df6a4588cf893ecff6e518111462433 100644
+--- a/src/external/pam.m4
++++ b/src/external/pam.m4
+@@ -27,3 +27,15 @@ AC_CHECK_FUNCS(pam_modutil_getlogin pam_vsyslog)
+ 
+ dnl restore LIBS
+ LIBS="$save_LIBS"
++
++PKG_CHECK_MODULES([GDM_PAM_EXTENSIONS], [gdm-pam-extensions],
++                  [found_gdm_pam_extensions=yes],
++                  [AC_MSG_NOTICE([gdm-pam-extensions were not found. gdm support
++for multiple certificates will not be build.
++])])
++
++AC_SUBST(GDM_PAM_EXTENSIONS_CFLAGS)
++
++AS_IF([test x"$found_gdm_pam_extensions" = xyes],
++      [AC_DEFINE_UNQUOTED(HAVE_GDM_PAM_EXTENSIONS, 1,
++                          [Build with gdm-pam-extensions support])])
+diff --git a/src/sss_client/pam_message.h b/src/sss_client/pam_message.h
+index f215392f6879f01a0ca12abc8807bac5fc1f1cbb..11526a80a767ff5602b194d14765ff261e8f9707 100644
+--- a/src/sss_client/pam_message.h
++++ b/src/sss_client/pam_message.h
+@@ -29,6 +29,8 @@
+ 
+ #include "sss_client/sss_cli.h"
+ 
++struct cert_auth_info;
++
+ struct pam_items {
+     const char *pam_service;
+     const char *pam_user;
+@@ -59,11 +61,9 @@ struct pam_items {
+     char *first_factor;
+     bool password_prompting;
+ 
+-    char *cert_user;
+-    char *token_name;
+-    char *module_name;
+-    char *key_id;
+     bool user_name_hint;
++    struct cert_auth_info *cert_list;
++    struct cert_auth_info *selected_cert;
+ };
+ 
+ int pack_message_v3(struct pam_items *pi, size_t *size, uint8_t **buffer);
+diff --git a/src/sss_client/pam_sss.c b/src/sss_client/pam_sss.c
+index 303809b9ea05b5a8709c05ae230d5f289b57de31..c147d4b3d76443d69e27eb2da042f8eebd1ae6ab 100644
+--- a/src/sss_client/pam_sss.c
++++ b/src/sss_client/pam_sss.c
+@@ -36,6 +36,10 @@
+ #include <security/pam_modules.h>
+ #include <security/pam_appl.h>
+ 
++#ifdef HAVE_GDM_PAM_EXTENSIONS
++#include <gdm/gdm-pam-extensions.h>
++#endif
++
+ #include "sss_pam_compat.h"
+ #include "sss_pam_macros.h"
+ 
+@@ -43,6 +47,7 @@
+ #include "pam_message.h"
+ #include "util/atomic_io.h"
+ #include "util/authtok-utils.h"
++#include "util/dlinklist.h"
+ 
+ #include <libintl.h>
+ #define _(STRING) dgettext (PACKAGE, STRING)
+@@ -118,6 +123,40 @@ static void close_fd(pam_handle_t *pamh, void *ptr, int err)
+     sss_pam_close_fd();
+ }
+ 
++struct cert_auth_info {
++    char *cert_user;
++    char *cert;
++    char *token_name;
++    char *module_name;
++    char *key_id;
++    struct cert_auth_info *prev;
++    struct cert_auth_info *next;
++};
++
++static void free_cai(struct cert_auth_info *cai)
++{
++    if (cai != NULL) {
++        free(cai->cert_user);
++        free(cai->cert);
++        free(cai->token_name);
++        free(cai->key_id);
++        free(cai);
++    }
++}
++
++static void free_cert_list(struct cert_auth_info *list)
++{
++    struct cert_auth_info *cai;
++    struct cert_auth_info *cai_next;
++
++    if (list != NULL) {
++        DLIST_FOR_EACH_SAFE(cai, cai_next, list) {
++            DLIST_REMOVE(list, cai);
++            free_cai(cai);
++        }
++    }
++}
++
+ static void overwrite_and_free_authtoks(struct pam_items *pi)
+ {
+     if (pi->pam_authtok != NULL) {
+@@ -158,17 +197,9 @@ static void overwrite_and_free_pam_items(struct pam_items *pi)
+     free(pi->otp_challenge);
+     pi->otp_challenge = NULL;
+ 
+-    free(pi->cert_user);
+-    pi->cert_user = NULL;
+-
+-    free(pi->token_name);
+-    pi->token_name = NULL;
+-
+-    free(pi->module_name);
+-    pi->module_name = NULL;
+-
+-    free(pi->key_id);
+-    pi->key_id = NULL;
++    free_cert_list(pi->cert_list);
++    pi->cert_list = NULL;
++    pi->selected_cert = NULL;
+ }
+ 
+ static int null_strcmp(const char *s1, const char *s2) {
+@@ -821,6 +852,90 @@ static int eval_user_info_response(pam_handle_t *pamh, size_t buflen,
+     return ret;
+ }
+ 
++static int parse_cert_info(struct pam_items *pi, uint8_t *buf, size_t len,
++                           size_t *p, const char **cert_user)
++{
++    struct cert_auth_info *cai = NULL;
++    size_t offset;
++    int ret;
++
++    if (buf[*p + (len - 1)] != '\0') {
++        D(("cert info does not end with \\0."));
++        return EINVAL;
++    }
++
++    cai = calloc(1, sizeof(struct cert_auth_info));
++    if (cai == NULL) {
++        return ENOMEM;
++    }
++
++    cai->cert_user = strdup((char *) &buf[*p]);
++    if (cai->cert_user == NULL) {
++        D(("strdup failed"));
++        ret = ENOMEM;
++        goto done;
++    }
++    if (cert_user != NULL) {
++        *cert_user = cai->cert_user;
++    }
++
++    offset = strlen(cai->cert_user) + 1;
++    if (offset >= len) {
++        D(("Cert message size mismatch"));
++        ret = EINVAL;
++        goto done;
++    }
++
++    cai->token_name = strdup((char *) &buf[*p + offset]);
++    if (cai->token_name == NULL) {
++        D(("strdup failed"));
++        ret = ENOMEM;
++        goto done;
++    }
++
++    offset += strlen(cai->token_name) + 1;
++    if (offset >= len) {
++        D(("Cert message size mismatch"));
++        ret = EINVAL;
++        goto done;
++    }
++
++    cai->module_name = strdup((char *) &buf[*p + offset]);
++    if (cai->module_name == NULL) {
++        D(("strdup failed"));
++        ret = ENOMEM;
++        goto done;
++    }
++
++    offset += strlen(cai->module_name) + 1;
++    if (offset >= len) {
++        D(("Cert message size mismatch"));
++        ret = EINVAL;
++        goto done;
++    }
++
++    cai->key_id = strdup((char *) &buf[*p + offset]);
++    if (cai->key_id == NULL) {
++        D(("strdup failed"));
++        ret = ENOMEM;
++        goto done;
++    }
++
++    D(("cert user: [%s] token name: [%s] module: [%s] key id: [%s]",
++       cai->cert_user, cai->token_name, cai->module_name,
++       cai->key_id));
++
++    DLIST_ADD(pi->cert_list, cai);
++    ret = 0;
++
++done:
++    if (ret != 0) {
++        free_cai(cai);
++    }
++
++    return ret;
++}
++
+ static int eval_response(pam_handle_t *pamh, size_t buflen, uint8_t *buf,
+                          struct pam_items *pi)
+ {
+@@ -832,6 +947,7 @@ static int eval_response(pam_handle_t *pamh, size_t buflen, uint8_t *buf,
+     int32_t len;
+     int32_t pam_status;
+     size_t offset;
++    const char *cert_user;
+ 
+     if (buflen < (2*sizeof(int32_t))) {
+         D(("response buffer is too small"));
+@@ -988,27 +1104,21 @@ static int eval_response(pam_handle_t *pamh, size_t buflen, uint8_t *buf,
+                     break;
+                 }
+ 
+-                free(pi->cert_user);
+-                pi->cert_user = strdup((char *) &buf[p]);
+-                if (pi->cert_user == NULL) {
+-                    D(("strdup failed"));
+-                    break;
+-                }
+-
+-                if (type == SSS_PAM_CERT_INFO && *pi->cert_user == '\0') {
+-                    D(("Invalid CERT message"));
+-                    break;
+-                }
+-
+                 if (type == SSS_PAM_CERT_INFO_WITH_HINT) {
+                     pi->user_name_hint = true;
+                 } else {
+                     pi->user_name_hint = false;
+                 }
+ 
++                ret = parse_cert_info(pi, buf, len, &p, &cert_user);
++                if (ret != 0) {
++                    D(("Failed to parse cert info"));
++                    break;
++                }
++
+                 if ((pi->pam_user == NULL || *(pi->pam_user) == '\0')
+-                        && *pi->cert_user != '\0') {
+-                    ret = pam_set_item(pamh, PAM_USER, pi->cert_user);
++                        && *cert_user != '\0') {
++                    ret = pam_set_item(pamh, PAM_USER, cert_user);
+                     if (ret != PAM_SUCCESS) {
+                         D(("Failed to set PAM_USER during "
+                            "Smartcard authentication [%s]",
+@@ -1027,59 +1137,6 @@ static int eval_response(pam_handle_t *pamh, size_t buflen, uint8_t *buf,
+ 
+                     pi->pam_user_size = strlen(pi->pam_user) + 1;
+                 }
+-
+-                offset = strlen(pi->cert_user) + 1;
+-                if (offset >= len) {
+-                    D(("Cert message size mismatch"));
+-                    free(pi->cert_user);
+-                    pi->cert_user = NULL;
+-                    break;
+-                }
+-                free(pi->token_name);
+-                pi->token_name = strdup((char *) &buf[p + offset]);
+-                if (pi->token_name == NULL) {
+-                    D(("strdup failed"));
+-                    free(pi->cert_user);
+-                    pi->cert_user = NULL;
+-                    break;
+-                }
+-
+-                offset += strlen(pi->token_name) + 1;
+-                if (offset >= len) {
+-                    D(("Cert message size mismatch"));
+-                    free(pi->cert_user);
+-                    pi->cert_user = NULL;
+-                    free(pi->token_name);
+-                    pi->token_name = NULL;
+-                    break;
+-                }
+-                free(pi->module_name);
+-                pi->module_name = strdup((char *) &buf[p + offset]);
+-                if (pi->module_name == NULL) {
+-                    D(("strdup failed"));
+-                    break;
+-                }
+-
+-                offset += strlen(pi->module_name) + 1;
+-                if (offset >= len) {
+-                    D(("Cert message size mismatch"));
+-                    free(pi->cert_user);
+-                    pi->cert_user = NULL;
+-                    free(pi->token_name);
+-                    pi->token_name = NULL;
+-                    free(pi->module_name);
+-                    pi->module_name = NULL;
+-                    break;
+-                }
+-                free(pi->key_id);
+-                pi->key_id = strdup((char *) &buf[p + offset]);
+-                if (pi->key_id == NULL) {
+-                    D(("strdup failed"));
+-                    break;
+-                }
+-                D(("cert user: [%s] token name: [%s] module: [%s] key id: [%s]",
+-                    pi->cert_user, pi->token_name, pi->module_name,
+-                    pi->key_id));
+                 break;
+             case SSS_PASSWORD_PROMPTING:
+                 D(("Password prompting available."));
+@@ -1175,10 +1232,8 @@ static int get_pam_items(pam_handle_t *pamh, uint32_t flags,
+     pi->otp_challenge = NULL;
+     pi->password_prompting = false;
+ 
+-    pi->cert_user = NULL;
+-    pi->token_name = NULL;
+-    pi->module_name = NULL;
+-    pi->key_id = NULL;
++    pi->cert_list = NULL;
++    pi->selected_cert = NULL;
+ 
+     return PAM_SUCCESS;
+ }
+@@ -1484,6 +1539,184 @@ done:
+ 
+ #define SC_PROMPT_FMT "PIN for %s"
+ 
++#ifndef discard_const
++#define discard_const(ptr) ((void *)((uintptr_t)(ptr)))
++#endif
++
++#define CERT_SEL_PROMPT_FMT "Certificate: %s"
++#define SEL_TITLE discard_const("Please select a certificate")
++
++static int prompt_multi_cert_gdm(pam_handle_t *pamh, struct pam_items *pi)
++{
++#ifdef HAVE_GDM_PAM_EXTENSIONS
++    int ret;
++    size_t cert_count = 0;
++    size_t c;
++    const struct pam_conv *conv;
++    struct cert_auth_info *cai;
++    GdmPamExtensionChoiceListRequest *request = NULL;
++    GdmPamExtensionChoiceListResponse *response = NULL;
++    struct pam_message prompt_message;
++    const struct pam_message *prompt_messages[1];
++    struct pam_response *reply = NULL;
++    char *prompt;
++
++    if (!GDM_PAM_EXTENSION_SUPPORTED(GDM_PAM_EXTENSION_CHOICE_LIST)) {
++        return ENOTSUP;
++    }
++
++    if (pi->cert_list == NULL) {
++        return EINVAL;
++    }
++
++    DLIST_FOR_EACH(cai, pi->cert_list) {
++        cert_count++;
++    }
++
++    ret = pam_get_item(pamh, PAM_CONV, (const void **)&conv);
++    if (ret != PAM_SUCCESS) {
++        ret = EIO;
++        return ret;
++    }
++
++    request = calloc(1, GDM_PAM_EXTENSION_CHOICE_LIST_REQUEST_SIZE(cert_count));
++    if (request == NULL) {
++        ret = ENOMEM;
++        goto done;
++    }
++    GDM_PAM_EXTENSION_CHOICE_LIST_REQUEST_INIT(request, SEL_TITLE, cert_count);
++
++    c = 0;
++    DLIST_FOR_EACH(cai, pi->cert_list) {
++        ret = asprintf(&prompt, CERT_SEL_PROMPT_FMT, cai->key_id);
++        if (ret == -1) {
++            ret = ENOMEM;
++            goto done;
++        }
++        request->list.items[c].key = cai->key_id;
++        request->list.items[c++].text = prompt;
++    }
++
++    GDM_PAM_EXTENSION_MESSAGE_TO_BINARY_PROMPT_MESSAGE(request,
++                                                       &prompt_message);
++    prompt_messages[0] = &prompt_message;
++
++    ret = conv->conv(1, prompt_messages, &reply, conv->appdata_ptr);
++    if (ret != PAM_SUCCESS) {
++        ret = EIO;
++        goto done;
++    }
++
++    ret = EIO;
++    response = GDM_PAM_EXTENSION_REPLY_TO_CHOICE_LIST_RESPONSE(reply);
++    if (response->key == NULL) {
++        goto done;
++    }
++
++    DLIST_FOR_EACH(cai, pi->cert_list) {
++        if (strcmp(response->key, cai->key_id) == 0) {
++            pam_info(pamh, "Certificate ‘%s’ selected", cai->key_id);
++            pi->selected_cert = cai;
++            ret = 0;
++            break;
++        }
++    }
++
++done:
++    if (request != NULL) {
++        for (c = 0; c < cert_count; c++) {
++            free(discard_const(request->list.items[c++].text));
++        }
++        free(request);
++    }
++    free(response);
++
++    return ret;
++#else
++    return ENOTSUP;
++#endif
++}
++
++#define TEXT_CERT_SEL_PROMPT_FMT "%s[%zu] Certificate: %s\n"
++#define TEXT_SEL_TITLE discard_const("Please select a certificate by typing " \
++                                     "the corresponding number\n")
++static int prompt_multi_cert(pam_handle_t *pamh, struct pam_items *pi)
++{
++    int ret;
++    size_t cert_count = 0;
++    size_t tries = 0;
++    long int resp = -1;
++    struct cert_auth_info *cai;
++    char *prompt;
++    char *tmp;
++    char *answer;
++    char *ep;
++
++    /* First check if gdm extension is supported */
++    ret = prompt_multi_cert_gdm(pamh, pi);
++    if (ret != ENOTSUP) {
++        return ret;
++    }
++
++    if (pi->cert_list == NULL) {
++        return EINVAL;
++    }
++
++    prompt = strdup(TEXT_SEL_TITLE);
++    if (prompt == NULL) {
++        return ENOMEM;
++    }
++
++    DLIST_FOR_EACH(cai, pi->cert_list) {
++        cert_count++;
++        ret = asprintf(&tmp, TEXT_CERT_SEL_PROMPT_FMT, prompt, cert_count,
++                                                       cai->key_id);
++        free(prompt);
++        if (ret == -1) {
++            return ENOMEM;
++        }
++
++        prompt = tmp;
++    }
++
++    do {
++        ret = do_pam_conversation(pamh, PAM_PROMPT_ECHO_ON, prompt, NULL,
++                                  &answer);
++        if (ret != PAM_SUCCESS) {
++            D(("do_pam_conversation failed."));
++            break;
++        }
++
++        errno = 0;
++        resp = strtol(answer, &ep, 10);
++        if (errno == 0 && *ep == '\0' && resp > 0 && resp <= cert_count) {
++            /* do not free answer ealier because ep is pointing to it */
++            free(answer);
++            break;
++        }
++        free(answer);
++        resp = -1;
++    } while (++tries < 5);
++    free(prompt);
++
++    pi->selected_cert = NULL;
++    ret = ENOENT;
++    if (resp > 0 && resp <= cert_count) {
++        cert_count = 0;
++        DLIST_FOR_EACH(cai, pi->cert_list) {
++            cert_count++;
++            if (resp == cert_count) {
++                pam_info(pamh, "Certificate ‘%s’ selected", cai->key_id);
++                pi->selected_cert = cai;
++                ret = 0;
++                break;
++            }
++        }
++    }
++
++    return ret;
++}
++
+ static int prompt_sc_pin(pam_handle_t *pamh, struct pam_items *pi)
+ {
+     int ret;
+@@ -1495,19 +1728,20 @@ static int prompt_sc_pin(pam_handle_t *pamh, struct pam_items *pi)
+     const struct pam_message *mesg[2] = { NULL, NULL };
+     struct pam_message m[2] = { { 0 }, { 0 } };
+     struct pam_response *resp = NULL;
++    struct cert_auth_info *cai = pi->selected_cert;
+ 
+-    if (pi->token_name == NULL || *pi->token_name == '\0') {
++    if (cai == NULL || cai->token_name == NULL || *cai->token_name == '\0') {
+         return EINVAL;
+     }
+ 
+-    size = sizeof(SC_PROMPT_FMT) + strlen(pi->token_name);
++    size = sizeof(SC_PROMPT_FMT) + strlen(cai->token_name);
+     prompt = malloc(size);
+     if (prompt == NULL) {
+         D(("malloc failed."));
+         return ENOMEM;
+     }
+ 
+-    ret = snprintf(prompt, size, SC_PROMPT_FMT, pi->token_name);
++    ret = snprintf(prompt, size, SC_PROMPT_FMT, cai->token_name);
+     if (ret < 0 || ret >= size) {
+         D(("snprintf failed."));
+         free(prompt);
+@@ -1604,9 +1838,9 @@ static int prompt_sc_pin(pam_handle_t *pamh, struct pam_items *pi)
+         pi->pam_authtok_size=0;
+     } else {
+ 
+-        ret = sss_auth_pack_sc_blob(answer, 0, pi->token_name, 0,
+-                                    pi->module_name, 0,
+-                                    pi->key_id, 0,
++        ret = sss_auth_pack_sc_blob(answer, 0, cai->token_name, 0,
++                                    cai->module_name, 0,
++                                    cai->key_id, 0,
+                                     NULL, 0, &needed_size);
+         if (ret != EAGAIN) {
+             D(("sss_auth_pack_sc_blob failed."));
+@@ -1621,9 +1855,9 @@ static int prompt_sc_pin(pam_handle_t *pamh, struct pam_items *pi)
+             goto done;
+         }
+ 
+-        ret = sss_auth_pack_sc_blob(answer, 0, pi->token_name, 0,
+-                                    pi->module_name, 0,
+-                                    pi->key_id, 0,
++        ret = sss_auth_pack_sc_blob(answer, 0, cai->token_name, 0,
++                                    cai->module_name, 0,
++                                    cai->key_id, 0,
+                                     (uint8_t *) pi->pam_authtok, needed_size,
+                                     &needed_size);
+         if (ret != EOK) {
+@@ -1786,7 +2020,17 @@ static int get_authtok_for_authentication(pam_handle_t *pamh,
+                 ret = prompt_2fa(pamh, pi, _("First Factor: "),
+                                  _("Second Factor: "));
+             }
+-        } else if (pi->token_name != NULL && *(pi->token_name) != '\0') {
++        } else if (pi->cert_list != NULL) {
++            if (pi->cert_list->next == NULL) {
++                /* Only one certificate */
++                pi->selected_cert = pi->cert_list;
++            } else {
++                ret = prompt_multi_cert(pamh, pi);
++                if (ret != 0) {
++                    D(("Failed to select certificate"));
++                    return PAM_AUTHTOK_ERR;
++                }
++            }
+             ret = prompt_sc_pin(pamh, pi);
+         } else {
+             ret = prompt_password(pamh, pi, _("Password: "));
+@@ -1905,14 +2149,21 @@ static int check_login_token_name(pam_handle_t *pamh, struct pam_items *pi,
+     char *prompt = NULL;
+     size_t size;
+     char *answer = NULL;
++    /* TODO: check multiple cert case */
++    struct cert_auth_info *cai = pi->cert_list;
++
++    if (cai == NULL) {
++        D(("No certificate information available"));
++        return EINVAL;
++    }
+ 
+     login_token_name = getenv("PKCS11_LOGIN_TOKEN_NAME");
+     if (login_token_name == NULL) {
+         return PAM_SUCCESS;
+     }
+ 
+-    while (pi->token_name == NULL
+-            || strcmp(login_token_name, pi->token_name) != 0) {
++    while (cai->token_name == NULL
++               || strcmp(login_token_name, cai->token_name) != 0) {
+         size = sizeof(SC_ENTER_FMT) + strlen(login_token_name);
+         prompt = malloc(size);
+         if (prompt == NULL) {
+-- 
+2.13.6
+
diff --git a/SOURCES/0039-intg-Remove-bashism-from-intgcheck-prepare.patch b/SOURCES/0039-intg-Remove-bashism-from-intgcheck-prepare.patch
deleted file mode 100644
index d10c770..0000000
--- a/SOURCES/0039-intg-Remove-bashism-from-intgcheck-prepare.patch
+++ /dev/null
@@ -1,54 +0,0 @@
-From c49fc8fded9ed87e37189bf877f04ef462974420 Mon Sep 17 00:00:00 2001
-From: Lukas Slebodnik <lslebodn@redhat.com>
-Date: Mon, 27 Mar 2017 14:44:29 +0200
-Subject: [PATCH 39/54] intg: Remove bashism from intgcheck-prepare
-
-env variable UID is not defined in all shells (eg. dash)
-We also need to move invocation of "id -u" before nss_wraper
-is enabled otherwise we would get root instead of real user.
-
-=================================== FAILURES ===================================
-________________________ test_kcm_mem_init_list_destroy ________________________
-Traceback (most recent call last):
-  File "/home/build/sssd/src/tests/intg/test_kcm.py", line 198, in test_kcm_mem_init_list_destroy
-    kcm_init_list_destroy(testenv)
-  File "/home/build/sssd/src/tests/intg/test_kcm.py", line 183, in kcm_init_list_destroy
-    exp_ccname = testenv.ccname()
-  File "/home/build/sssd/src/tests/intg/test_kcm.py", line 45, in ccname
-    my_uid = self.my_uid()
-  File "/home/build/sssd/src/tests/intg/test_kcm.py", line 41, in my_uid
-    return int(s_myuid)
-ValueError: invalid literal for int() with base 10: ''
-
-And we already use different approach in top level Makefile.am
-3488)      $(INTGCHECK_CONFIGURE_FLAGS) \
-3489)      CFLAGS="$$CFLAGS $(AM_CFLAGS) -DKCM_PEER_UID=$$(id -u)"; \
-3490)  $(MAKE) $(AM_MAKEFLAGS) ; \
-
-Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
----
- src/tests/intg/Makefile.am | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
-diff --git a/src/tests/intg/Makefile.am b/src/tests/intg/Makefile.am
-index 8526beace09b15c99aa27ac98d5038d1980f6a71..8566106e9017a8d3c9e7a3898a3a886e2966e346 100644
---- a/src/tests/intg/Makefile.am
-+++ b/src/tests/intg/Makefile.am
-@@ -76,6 +76,7 @@ intgcheck-installed: config.py passwd group
- 	PATH="$(abs_builddir):$(abs_srcdir):$$PATH" \
- 	PYTHONPATH="$(abs_builddir):$(abs_srcdir)" \
- 	LDB_MODULES_PATH="$(DESTDIR)$(ldblibdir)" \
-+	NON_WRAPPED_UID=$$(id -u) \
- 	LD_PRELOAD="$$nss_wrapper $$uid_wrapper" \
- 	NSS_WRAPPER_PASSWD="$(abs_builddir)/passwd" \
- 	NSS_WRAPPER_GROUP="$(abs_builddir)/group" \
-@@ -83,6 +84,5 @@ intgcheck-installed: config.py passwd group
- 	NSS_WRAPPER_MODULE_FN_PREFIX="sss" \
- 	UID_WRAPPER=1 \
- 	UID_WRAPPER_ROOT=1 \
--	NON_WRAPPED_UID=$$(echo $$UID) \
- 	    fakeroot $(PYTHON2) $(PYTEST) -v --tb=native $(INTGCHECK_PYTEST_ARGS) .
- 	rm -f $(DESTDIR)$(logpath)/*
--- 
-2.9.3
-
diff --git a/SOURCES/0039-p11_child-use-options-to-select-certificate-for-auth.patch b/SOURCES/0039-p11_child-use-options-to-select-certificate-for-auth.patch
new file mode 100644
index 0000000..4b476fc
--- /dev/null
+++ b/SOURCES/0039-p11_child-use-options-to-select-certificate-for-auth.patch
@@ -0,0 +1,609 @@
+From e857005c207e514c487ba75daf20ca4be5321c38 Mon Sep 17 00:00:00 2001
+From: Sumit Bose <sbose@redhat.com>
+Date: Fri, 27 Oct 2017 10:13:36 +0200
+Subject: [PATCH 39/46] p11_child: use options to select certificate for
+ authentication
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+New options are added to p11_child to select a specific certificate
+during authentication.
+
+The related unit tests are updated by adding the needed attributes to
+the requests. The was not necessary before because although the
+attribute were already send by pam_sss they were not used in the PAM
+responder but only forwarded to the back where they were used by the
+PKINIT code to select the expected certificate.
+
+Related to https://pagure.io/SSSD/sssd/issue/3560
+
+Reviewed-by: Fabiano Fidêncio <fidencio@redhat.com>
+Tested-by: Scott Poore <spoore@redhat.com>
+(cherry picked from commit 0a8024af282b271ad2185f68703d9f4e766d2bdc)
+---
+ src/p11_child/p11_child_nss.c   | 213 ++++++++++++++++++++++++++--------------
+ src/responder/pam/pamsrv_p11.c  |  30 +++++-
+ src/tests/cmocka/test_pam_srv.c |  64 ++++++++----
+ 3 files changed, 210 insertions(+), 97 deletions(-)
+
+diff --git a/src/p11_child/p11_child_nss.c b/src/p11_child/p11_child_nss.c
+index 50bde2f4f91f6c00260b0db383d0962112686ebc..c676375cf7f6677a1d7f38f09b9bb5fd820d60c5 100644
+--- a/src/p11_child/p11_child_nss.c
++++ b/src/p11_child/p11_child_nss.c
+@@ -67,12 +67,34 @@ static char *password_passthrough(PK11SlotInfo *slot, PRBool retry, void *arg)
+   return PL_strdup((char *)arg);
+ }
+ 
++static char *get_key_id_str(PK11SlotInfo *slot, CERTCertificate *cert)
++{
++    SECItem *key_id = NULL;
++    char *key_id_str = NULL;
+ 
++    key_id = PK11_GetLowLevelKeyIDForCert(slot, cert, NULL);
++    if (key_id == NULL) {
++        DEBUG(SSSDBG_OP_FAILURE,
++              "PK11_GetLowLevelKeyIDForCert failed [%d].\n",
++              PR_GetError());
++        return NULL;
++    }
+ 
+-int do_work(TALLOC_CTX *mem_ctx, const char *nss_db, const char *slot_name_in,
++    key_id_str = CERT_Hexify(key_id, PR_FALSE);
++    SECITEM_FreeItem(key_id, PR_TRUE);
++    if (key_id_str == NULL) {
++        DEBUG(SSSDBG_OP_FAILURE, "CERT_Hexify failed [%d].\n", PR_GetError());
++        return NULL;
++    }
++
++    return key_id_str;
++}
++
++int do_work(TALLOC_CTX *mem_ctx, const char *nss_db,
+             enum op_mode mode, const char *pin,
+             struct cert_verify_opts *cert_verify_opts,
+-            char **_multi)
++            const char *module_name_in, const char *token_name_in,
++            const char *key_id_in, char **_multi)
+ {
+     int ret;
+     SECStatus rv;
+@@ -153,42 +175,31 @@ int do_work(TALLOC_CTX *mem_ctx, const char *nss_db, const char *slot_name_in,
+                                 mod_list_item->module->dllName);
+     }
+ 
+-    if (slot_name_in != NULL) {
+-        slot = PK11_FindSlotByName(slot_name_in);
+-        if (slot == NULL) {
+-            DEBUG(SSSDBG_OP_FAILURE, "PK11_FindSlotByName failed for [%s]: [%d].\n",
+-                                     slot_name_in, PR_GetError());
+-            return EIO;
+-        }
+-    } else {
+-
+-        list = PK11_GetAllTokens(CKM_INVALID_MECHANISM, PR_FALSE, PR_TRUE,
+-                                 NULL);
+-        if (list == NULL) {
+-            DEBUG(SSSDBG_OP_FAILURE, "PK11_GetAllTokens failed.\n");
+-            return EIO;
+-        }
++    list = PK11_GetAllTokens(CKM_INVALID_MECHANISM, PR_FALSE, PR_TRUE,
++                             NULL);
++    if (list == NULL) {
++        DEBUG(SSSDBG_OP_FAILURE, "PK11_GetAllTokens failed.\n");
++        return EIO;
++    }
+ 
+-        for (le = list->head; le; le = le->next) {
+-            CK_SLOT_INFO slInfo;
++    for (le = list->head; le; le = le->next) {
++        CK_SLOT_INFO slInfo;
+ 
+-            slInfo.flags = 0;
+-            rv = PK11_GetSlotInfo(le->slot, &slInfo);
+-            DEBUG(SSSDBG_TRACE_ALL,
+-                  "Description [%s] Manufacturer [%s] flags [%lu].\n",
+-                  slInfo.slotDescription, slInfo.manufacturerID, slInfo.flags);
+-            if (rv == SECSuccess && (slInfo.flags & CKF_REMOVABLE_DEVICE)) {
+-                slot = PK11_ReferenceSlot(le->slot);
+-                break;
+-           }
+-        }
+-        PK11_FreeSlotList(list);
+-        if (slot == NULL) {
+-            DEBUG(SSSDBG_OP_FAILURE, "No removable slots found.\n");
+-            return EIO;
++        slInfo.flags = 0;
++        rv = PK11_GetSlotInfo(le->slot, &slInfo);
++        DEBUG(SSSDBG_TRACE_ALL,
++              "Description [%s] Manufacturer [%s] flags [%lu].\n",
++              slInfo.slotDescription, slInfo.manufacturerID, slInfo.flags);
++        if (rv == SECSuccess && (slInfo.flags & CKF_REMOVABLE_DEVICE)) {
++            slot = PK11_ReferenceSlot(le->slot);
++            break;
+         }
+     }
+-
++    PK11_FreeSlotList(list);
++    if (slot == NULL) {
++        DEBUG(SSSDBG_OP_FAILURE, "No removable slots found.\n");
++        return EIO;
++    }
+ 
+     slot_id = PK11_GetSlotID(slot);
+     module_id = PK11_GetModuleID(slot);
+@@ -317,24 +328,60 @@ int do_work(TALLOC_CTX *mem_ctx, const char *nss_db, const char *slot_name_in,
+     for (cert_list_node = CERT_LIST_HEAD(cert_list);
+                 !CERT_LIST_END(cert_list_node, cert_list);
+                 cert_list_node = CERT_LIST_NEXT(cert_list_node)) {
+-        if (cert_list_node->cert) {
+-            DEBUG(SSSDBG_TRACE_ALL, "found cert[%s][%s]\n",
+-                             cert_list_node->cert->nickname,
+-                             cert_list_node->cert->subjectName);
++        if (cert_list_node->cert == NULL) {
++            DEBUG(SSSDBG_TRACE_ALL, "--- empty cert list node ---\n");
++            continue;
++        }
+ 
+-            if (cert_verify_opts->do_verification) {
+-                rv = CERT_VerifyCertificateNow(handle, cert_list_node->cert,
+-                                               PR_TRUE,
+-                                               certificateUsageSSLClient,
+-                                               NULL, NULL);
+-                if (rv != SECSuccess) {
+-                    DEBUG(SSSDBG_OP_FAILURE,
+-                          "Certificate [%s][%s] not valid [%d], skipping.\n",
+-                          cert_list_node->cert->nickname,
+-                          cert_list_node->cert->subjectName, PR_GetError());
+-                    continue;
+-                }
++        DEBUG(SSSDBG_TRACE_ALL,
++              "found cert[%s][%s]\n",
++              cert_list_node->cert->nickname,
++              cert_list_node->cert->subjectName);
++
++        if (cert_verify_opts->do_verification) {
++            rv = CERT_VerifyCertificateNow(handle, cert_list_node->cert,
++                                           PR_TRUE,
++                                           certificateUsageSSLClient,
++                                           NULL, NULL);
++            if (rv != SECSuccess) {
++                DEBUG(SSSDBG_OP_FAILURE,
++                      "Certificate [%s][%s] not valid [%d], skipping.\n",
++                      cert_list_node->cert->nickname,
++                      cert_list_node->cert->subjectName, PR_GetError());
++                continue;
+             }
++        }
++
++        if (key_id_in != NULL) {
++            PORT_Free(key_id_str);
++            key_id_str = NULL;
++            key_id_str = get_key_id_str(slot, cert_list_node->cert);
++        }
++        /* Check if we found the certificates we needed for authentication or
++         * the requested ones for pre-auth. For authentication all attributes
++         * must be given and match, for pre-auth only the given ones must
++         * match. */
++        DEBUG(SSSDBG_TRACE_ALL, "%s %s %s %s %s %s.\n",
++              module_name_in, module_name, token_name_in, token_name,
++              key_id_in, key_id_str);
++        if ((mode == OP_AUTH
++                && module_name_in != NULL
++                && token_name_in != NULL
++                && key_id_in != NULL
++                && key_id_str != NULL
++                && strcmp(key_id_in, key_id_str) == 0
++                && strcmp(token_name_in, token_name) == 0
++                && strcmp(module_name_in, module_name) == 0)
++            || (mode == OP_PREAUTH
++                && (module_name_in == NULL
++                    || (module_name_in != NULL
++                        && strcmp(module_name_in, module_name) == 0))
++                && (token_name_in == NULL
++                    || (token_name_in != NULL
++                        && strcmp(token_name_in, token_name) == 0))
++                && (key_id_in == NULL
++                    || (key_id_in != NULL && key_id_str != NULL
++                        && strcmp(key_id_in, key_id_str) == 0)))) {
+ 
+             rv = CERT_AddCertToListTail(valid_certs, cert_list_node->cert);
+             if (rv != SECSuccess) {
+@@ -343,15 +390,6 @@ int do_work(TALLOC_CTX *mem_ctx, const char *nss_db, const char *slot_name_in,
+                 ret = EIO;
+                 goto done;
+             }
+-
+-            if (found_cert == NULL) {
+-                found_cert = cert_list_node->cert;
+-            } else {
+-                DEBUG(SSSDBG_TRACE_ALL, "More than one certificate found, " \
+-                                        "using just the first one.\n");
+-            }
+-        } else {
+-            DEBUG(SSSDBG_TRACE_ALL, "--- empty cert list node ---\n");
+         }
+     }
+ 
+@@ -367,7 +405,7 @@ int do_work(TALLOC_CTX *mem_ctx, const char *nss_db, const char *slot_name_in,
+         }
+     }
+ 
+-    if (found_cert == NULL) {
++    if (CERT_LIST_EMPTY(valid_certs)) {
+         DEBUG(SSSDBG_TRACE_ALL, "No certificate found.\n");
+         *_multi = NULL;
+         ret = EOK;
+@@ -375,6 +413,23 @@ int do_work(TALLOC_CTX *mem_ctx, const char *nss_db, const char *slot_name_in,
+     }
+ 
+     if (mode == OP_AUTH) {
++        cert_list_node = CERT_LIST_HEAD(valid_certs);
++        if (!CERT_LIST_END(CERT_LIST_NEXT(cert_list_node), valid_certs)) {
++            DEBUG(SSSDBG_FATAL_FAILURE,
++                  "More than one certificate found for authentication, "
++                  "aborting!\n");
++            ret = EINVAL;
++            goto done;
++        }
++
++        found_cert = cert_list_node->cert;
++        if (found_cert == NULL) {
++            DEBUG(SSSDBG_FATAL_FAILURE,
++                  "No certificate found for authentication, aborting!\n");
++            ret = EINVAL;
++            goto done;
++        }
++
+         rv = PK11_GenerateRandom(random_value, sizeof(random_value));
+         if (rv != SECSuccess) {
+             DEBUG(SSSDBG_OP_FAILURE,
+@@ -449,21 +504,10 @@ int do_work(TALLOC_CTX *mem_ctx, const char *nss_db, const char *slot_name_in,
+ 
+         found_cert = cert_list_node->cert;
+ 
+-        SECITEM_FreeItem(key_id, PR_TRUE);
+         PORT_Free(key_id_str);
+-        key_id = PK11_GetLowLevelKeyIDForCert(slot, found_cert, NULL);
+-        if (key_id == NULL) {
+-            DEBUG(SSSDBG_OP_FAILURE,
+-                  "PK11_GetLowLevelKeyIDForCert failed [%d].\n",
+-                  PR_GetError());
+-            ret = EINVAL;
+-            goto done;
+-        }
+-
+-        key_id_str = CERT_Hexify(key_id, PR_FALSE);
++        key_id_str = get_key_id_str(slot, found_cert);
+         if (key_id_str == NULL) {
+-            DEBUG(SSSDBG_OP_FAILURE, "CERT_Hexify failed [%d].\n",
+-                  PR_GetError());
++            DEBUG(SSSDBG_OP_FAILURE, "get_key_id_str [%d].\n", PR_GetError());
+             ret = ENOMEM;
+             goto done;
+         }
+@@ -576,11 +620,13 @@ int main(int argc, const char *argv[])
+     enum op_mode mode = OP_NONE;
+     enum pin_mode pin_mode = PIN_NONE;
+     char *pin = NULL;
+-    char *slot_name_in = NULL;
+     char *nss_db = NULL;
+     struct cert_verify_opts *cert_verify_opts;
+     char *verify_opts = NULL;
+     char *multi = NULL;
++    char *module_name = NULL;
++    char *token_name = NULL;
++    char *key_id = NULL;
+ 
+     struct poptOption long_options[] = {
+         POPT_AUTOHELP
+@@ -605,6 +651,12 @@ int main(int argc, const char *argv[])
+          NULL},
+         {"nssdb", 0, POPT_ARG_STRING, &nss_db, 0, _("NSS DB to use"),
+          NULL},
++        {"module_name", 0, POPT_ARG_STRING, &module_name, 0,
++         _("Module name for authentication"), NULL},
++        {"token_name", 0, POPT_ARG_STRING, &token_name, 0,
++         _("Token name for authentication"), NULL},
++        {"key_id", 0, POPT_ARG_STRING, &key_id, 0,
++         _("Key ID for authentication"), NULL},
+         POPT_TABLEEND
+     };
+ 
+@@ -730,6 +782,15 @@ int main(int argc, const char *argv[])
+     }
+     talloc_steal(main_ctx, debug_prg_name);
+ 
++    if (mode == OP_AUTH && (module_name == NULL || token_name == NULL
++                                || key_id == NULL)) {
++        DEBUG(SSSDBG_FATAL_FAILURE,
++              "--module_name, --token_name and --key_id must be for "
++              "authentication");
++        ret = EINVAL;
++        goto fail;
++    }
++
+     ret = parse_cert_verify_opts(main_ctx, verify_opts, &cert_verify_opts);
+     if (ret != EOK) {
+         DEBUG(SSSDBG_FATAL_FAILURE, "Failed to parse verifiy option.\n");
+@@ -744,8 +805,8 @@ int main(int argc, const char *argv[])
+         }
+     }
+ 
+-    ret = do_work(main_ctx, nss_db, slot_name_in, mode, pin, cert_verify_opts,
+-                  &multi);
++    ret = do_work(main_ctx, nss_db, mode, pin, cert_verify_opts, module_name,
++                  token_name, key_id, &multi);
+     if (ret != EOK) {
+         DEBUG(SSSDBG_OP_FAILURE, "do_work failed.\n");
+         goto fail;
+diff --git a/src/responder/pam/pamsrv_p11.c b/src/responder/pam/pamsrv_p11.c
+index 57c8e1e464f4262f2d78f869c52ca48bd469d90a..4d5572164763ed0b3a842019f820680a4dc2dfdc 100644
+--- a/src/responder/pam/pamsrv_p11.c
++++ b/src/responder/pam/pamsrv_p11.c
+@@ -399,10 +399,13 @@ struct tevent_req *pam_check_cert_send(TALLOC_CTX *mem_ctx,
+     struct timeval tv;
+     int pipefd_to_child[2] = PIPE_INIT;
+     int pipefd_from_child[2] = PIPE_INIT;
+-    const char *extra_args[7] = { NULL };
++    const char *extra_args[13] = { NULL };
+     uint8_t *write_buf = NULL;
+     size_t write_buf_len = 0;
+     size_t arg_c;
++    const char *module_name = NULL;
++    const char *token_name = NULL;
++    const char *key_id = NULL;
+ 
+     req = tevent_req_create(mem_ctx, &state, struct pam_check_cert_state);
+     if (req == NULL) {
+@@ -423,6 +426,30 @@ struct tevent_req *pam_check_cert_send(TALLOC_CTX *mem_ctx,
+         extra_args[arg_c++] = verify_opts;
+         extra_args[arg_c++] = "--verify";
+     }
++
++    if (sss_authtok_get_type(pd->authtok) == SSS_AUTHTOK_TYPE_SC_PIN
++            || sss_authtok_get_type(pd->authtok) == SSS_AUTHTOK_TYPE_SC_KEYPAD) {
++        ret = sss_authtok_get_sc(pd->authtok, NULL, NULL, &token_name, NULL,
++                                 &module_name, NULL, &key_id, NULL);
++        if (ret != EOK) {
++            DEBUG(SSSDBG_OP_FAILURE, "sss_authtok_get_sc failed.\n");
++            goto done;
++        }
++
++        if (module_name != NULL && *module_name != '\0') {
++            extra_args[arg_c++] = module_name;
++            extra_args[arg_c++] = "--module_name";
++        }
++        if (token_name != NULL && *token_name != '\0') {
++            extra_args[arg_c++] = token_name;
++            extra_args[arg_c++] = "--token_name";
++        }
++        if (key_id != NULL && *key_id != '\0') {
++            extra_args[arg_c++] = key_id;
++            extra_args[arg_c++] = "--key_id";
++        }
++    }
++
+     if (pd->cmd == SSS_PAM_AUTHENTICATE) {
+         extra_args[arg_c++] = "--auth";
+         switch (sss_authtok_get_type(pd->authtok)) {
+@@ -437,6 +464,7 @@ struct tevent_req *pam_check_cert_send(TALLOC_CTX *mem_ctx,
+             ret = EINVAL;
+             goto done;
+         }
++
+     } else if (pd->cmd == SSS_PAM_PREAUTH) {
+         extra_args[arg_c++] = "--pre";
+     } else {
+diff --git a/src/tests/cmocka/test_pam_srv.c b/src/tests/cmocka/test_pam_srv.c
+index 7f0ed706512ffe0866c0e1fb7e6baa16bec942d8..5c1f621ccead75717d1721714d953d7d4d415d7b 100644
+--- a/src/tests/cmocka/test_pam_srv.c
++++ b/src/tests/cmocka/test_pam_srv.c
+@@ -687,7 +687,9 @@ static void mock_input_pam(TALLOC_CTX *mem_ctx,
+ }
+ 
+ static void mock_input_pam_cert(TALLOC_CTX *mem_ctx, const char *name,
+-                                const char *pin, const char *service,
++                                const char *pin, const char *token_name,
++                                const char *module_name, const char *key_id,
++                                const char *service,
+                                 acct_cb_t acct_cb, const char *cert,
+                                 bool only_one_provider_call)
+ {
+@@ -697,6 +699,7 @@ static void mock_input_pam_cert(TALLOC_CTX *mem_ctx, const char *name,
+     struct pam_items pi = { 0 };
+     int ret;
+     bool already_mocked = false;
++    size_t needed_size;
+ 
+     if (name != NULL) {
+         pi.pam_user = name;
+@@ -707,9 +710,21 @@ static void mock_input_pam_cert(TALLOC_CTX *mem_ctx, const char *name,
+     }
+ 
+     if (pin != NULL) {
+-        pi.pam_authtok = discard_const(pin);
+-        pi.pam_authtok_size = strlen(pi.pam_authtok) + 1;
++        ret = sss_auth_pack_sc_blob(pin, 0, token_name, 0, module_name, 0,
++                                    key_id, 0, NULL, 0, &needed_size);
++        assert_int_equal(ret, EAGAIN);
++
++        pi.pam_authtok = malloc(needed_size);
++        assert_non_null(pi.pam_authtok);
++
++        ret = sss_auth_pack_sc_blob(pin, 0, token_name, 0, module_name, 0,
++                                    key_id, 0,
++                                    (uint8_t *)pi.pam_authtok, needed_size,
++                                    &needed_size);
++        assert_int_equal(ret, EOK);
++
+         pi.pam_authtok_type = SSS_AUTHTOK_TYPE_SC_PIN;
++        pi.pam_authtok_size = needed_size;
+     }
+ 
+     pi.pam_service = service == NULL ? "login" : service;
+@@ -724,6 +739,7 @@ static void mock_input_pam_cert(TALLOC_CTX *mem_ctx, const char *name,
+     pi.cli_pid = 12345;
+ 
+     ret = pack_message_v3(&pi, &buf_size, &m_buf);
++    free(pi.pam_authtok);
+     assert_int_equal(ret, 0);
+ 
+     buf = talloc_memdup(mem_ctx, m_buf, buf_size);
+@@ -1732,7 +1748,8 @@ void test_pam_preauth_no_logon_name(void **state)
+ {
+     int ret;
+ 
+-    mock_input_pam_cert(pam_test_ctx, NULL, NULL, NULL, NULL, NULL, false);
++    mock_input_pam_cert(pam_test_ctx, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
++                        NULL, false);
+ 
+     will_return(__wrap_sss_packet_get_cmd, SSS_PAM_PREAUTH);
+     will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL);
+@@ -1824,7 +1841,8 @@ void test_pam_preauth_cert_nocert(void **state)
+ 
+     set_cert_auth_param(pam_test_ctx->pctx, "/no/path");
+ 
+-    mock_input_pam_cert(pam_test_ctx, "pamuser", NULL, NULL, NULL, NULL, false);
++    mock_input_pam_cert(pam_test_ctx, "pamuser", NULL, NULL, NULL, NULL, NULL,
++                        NULL, NULL, false);
+ 
+     will_return(__wrap_sss_packet_get_cmd, SSS_PAM_PREAUTH);
+     will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL);
+@@ -1962,7 +1980,7 @@ void test_pam_preauth_cert_nomatch(void **state)
+ 
+     set_cert_auth_param(pam_test_ctx->pctx, NSS_DB);
+ 
+-    mock_input_pam_cert(pam_test_ctx, "pamuser", NULL, NULL,
++    mock_input_pam_cert(pam_test_ctx, "pamuser", NULL, NULL, NULL, NULL, NULL,
+                         test_lookup_by_cert_cb, NULL, false);
+ 
+     will_return(__wrap_sss_packet_get_cmd, SSS_PAM_PREAUTH);
+@@ -1984,7 +2002,7 @@ void test_pam_preauth_cert_match(void **state)
+ 
+     set_cert_auth_param(pam_test_ctx->pctx, NSS_DB);
+ 
+-    mock_input_pam_cert(pam_test_ctx, "pamuser", NULL, NULL,
++    mock_input_pam_cert(pam_test_ctx, "pamuser", NULL, NULL, NULL, NULL, NULL,
+                         test_lookup_by_cert_cb, TEST_TOKEN_CERT, false);
+ 
+     will_return(__wrap_sss_packet_get_cmd, SSS_PAM_PREAUTH);
+@@ -2007,8 +2025,9 @@ void test_pam_preauth_cert_match_gdm_smartcard(void **state)
+ 
+     set_cert_auth_param(pam_test_ctx->pctx, NSS_DB);
+ 
+-    mock_input_pam_cert(pam_test_ctx, "pamuser", NULL, "gdm-smartcard",
+-                        test_lookup_by_cert_cb, TEST_TOKEN_CERT, false);
++    mock_input_pam_cert(pam_test_ctx, "pamuser", NULL, NULL, NULL, NULL,
++                        "gdm-smartcard", test_lookup_by_cert_cb,
++                        TEST_TOKEN_CERT, false);
+ 
+     will_return(__wrap_sss_packet_get_cmd, SSS_PAM_PREAUTH);
+     will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL);
+@@ -2029,7 +2048,7 @@ void test_pam_preauth_cert_match_wrong_user(void **state)
+ 
+     set_cert_auth_param(pam_test_ctx->pctx, NSS_DB);
+ 
+-    mock_input_pam_cert(pam_test_ctx, "pamuser", NULL, NULL,
++    mock_input_pam_cert(pam_test_ctx, "pamuser", NULL, NULL, NULL, NULL, NULL,
+                         test_lookup_by_cert_wrong_user_cb,
+                         TEST_TOKEN_CERT, false);
+ 
+@@ -2061,7 +2080,7 @@ void test_pam_preauth_cert_no_logon_name(void **state)
+      * Additionally sss_parse_inp_recv() must be mocked because the cache
+      * request will be done with the username found by the certificate
+      * lookup. */
+-    mock_input_pam_cert(pam_test_ctx, NULL, NULL, NULL,
++    mock_input_pam_cert(pam_test_ctx, NULL, NULL, NULL, NULL, NULL, NULL,
+                         test_lookup_by_cert_cb, TEST_TOKEN_CERT, false);
+     mock_account_recv_simple();
+     mock_parse_inp("pamuser", NULL, EOK);
+@@ -2090,7 +2109,7 @@ void test_pam_preauth_cert_no_logon_name_with_hint(void **state)
+      * Since user name hint is enabled we do not have to search the user
+      * during pre-auth and there is no need for an extra mocked response as in
+      * test_pam_preauth_cert_no_logon_name. */
+-    mock_input_pam_cert(pam_test_ctx, NULL, NULL, NULL,
++    mock_input_pam_cert(pam_test_ctx, NULL, NULL, NULL, NULL, NULL, NULL,
+                         test_lookup_by_cert_cb, TEST_TOKEN_CERT, false);
+ 
+     will_return(__wrap_sss_packet_get_cmd, SSS_PAM_PREAUTH);
+@@ -2112,7 +2131,7 @@ void test_pam_preauth_cert_no_logon_name_double_cert(void **state)
+ 
+     set_cert_auth_param(pam_test_ctx->pctx, NSS_DB);
+ 
+-    mock_input_pam_cert(pam_test_ctx, NULL, NULL, NULL,
++    mock_input_pam_cert(pam_test_ctx, NULL, NULL, NULL, NULL, NULL, NULL,
+                         test_lookup_by_cert_double_cb, TEST_TOKEN_CERT, false);
+ 
+     will_return(__wrap_sss_packet_get_cmd, SSS_PAM_PREAUTH);
+@@ -2135,7 +2154,7 @@ void test_pam_preauth_cert_no_logon_name_double_cert_with_hint(void **state)
+     set_cert_auth_param(pam_test_ctx->pctx, NSS_DB);
+     pam_test_ctx->rctx->domains->user_name_hint = true;
+ 
+-    mock_input_pam_cert(pam_test_ctx, NULL, NULL, NULL,
++    mock_input_pam_cert(pam_test_ctx, NULL, NULL, NULL, NULL, NULL, NULL,
+                         test_lookup_by_cert_double_cb, TEST_TOKEN_CERT, false);
+ 
+     will_return(__wrap_sss_packet_get_cmd, SSS_PAM_PREAUTH);
+@@ -2157,7 +2176,8 @@ void test_pam_preauth_no_cert_no_logon_name(void **state)
+ 
+     set_cert_auth_param(pam_test_ctx->pctx, "/no/path");
+ 
+-    mock_input_pam_cert(pam_test_ctx, NULL, NULL, NULL, NULL, NULL, false);
++    mock_input_pam_cert(pam_test_ctx, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
++                        NULL, false);
+ 
+     will_return(__wrap_sss_packet_get_cmd, SSS_PAM_PREAUTH);
+     will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL);
+@@ -2178,7 +2198,7 @@ void test_pam_preauth_cert_no_logon_name_no_match(void **state)
+ 
+     set_cert_auth_param(pam_test_ctx->pctx, NSS_DB);
+ 
+-    mock_input_pam_cert(pam_test_ctx, NULL, NULL, NULL,
++    mock_input_pam_cert(pam_test_ctx, NULL, NULL, NULL, NULL, NULL, NULL,
+                         test_lookup_by_cert_cb, NULL, false);
+ 
+     will_return(__wrap_sss_packet_get_cmd, SSS_PAM_PREAUTH);
+@@ -2206,7 +2226,9 @@ void test_pam_cert_auth(void **state)
+      * is looked up. Since the first mocked reply already adds the certificate
+      * to the user entry the lookup by certificate will already find the user
+      * in the cache and no second request to the backend is needed. */
+-    mock_input_pam_cert(pam_test_ctx, "pamuser", "123456", NULL,
++    mock_input_pam_cert(pam_test_ctx, "pamuser", "123456", "SSSD Test Token",
++                        "NSS-Internal",
++                        "A5EF7DEE625CA5996C8D1BA7D036708161FD49E7", NULL,
+                         test_lookup_by_cert_cb, TEST_TOKEN_CERT, true);
+ 
+     will_return(__wrap_sss_packet_get_cmd, SSS_PAM_AUTHENTICATE);
+@@ -2232,7 +2254,9 @@ void test_pam_cert_auth_double_cert(void **state)
+ 
+     set_cert_auth_param(pam_test_ctx->pctx, NSS_DB);
+ 
+-    mock_input_pam_cert(pam_test_ctx, "pamuser", "123456", NULL,
++    mock_input_pam_cert(pam_test_ctx, "pamuser", "123456", "SSSD Test Token",
++                        "NSS-Internal",
++                        "A5EF7DEE625CA5996C8D1BA7D036708161FD49E7", NULL,
+                         test_lookup_by_cert_double_cb, TEST_TOKEN_CERT, true);
+ 
+     will_return(__wrap_sss_packet_get_cmd, SSS_PAM_AUTHENTICATE);
+@@ -2257,7 +2281,7 @@ void test_pam_cert_preauth_2certs_one_mapping(void **state)
+ 
+     set_cert_auth_param(pam_test_ctx->pctx, NSS_DB_2CERTS);
+ 
+-    mock_input_pam_cert(pam_test_ctx, "pamuser", NULL, NULL,
++    mock_input_pam_cert(pam_test_ctx, "pamuser", NULL, NULL, NULL, NULL, NULL,
+                         test_lookup_by_cert_cb, TEST_TOKEN_CERT, false);
+ 
+     will_return(__wrap_sss_packet_get_cmd, SSS_PAM_PREAUTH);
+@@ -2279,7 +2303,7 @@ void test_pam_cert_preauth_2certs_two_mappings(void **state)
+ 
+     set_cert_auth_param(pam_test_ctx->pctx, NSS_DB_2CERTS);
+ 
+-    mock_input_pam_cert(pam_test_ctx, "pamuser", NULL, NULL,
++    mock_input_pam_cert(pam_test_ctx, "pamuser", NULL, NULL, NULL, NULL, NULL,
+                         test_lookup_by_cert_cb_2nd_cert_same_user,
+                         TEST_TOKEN_CERT, false);
+ 
+-- 
+2.13.6
+
diff --git a/SOURCES/0040-UTIL-Introduce-subdomain_create_conf_path.patch b/SOURCES/0040-UTIL-Introduce-subdomain_create_conf_path.patch
deleted file mode 100644
index 0dc6e0c..0000000
--- a/SOURCES/0040-UTIL-Introduce-subdomain_create_conf_path.patch
+++ /dev/null
@@ -1,127 +0,0 @@
-From ddfa743159541de498816764c06bf4b13fb923f7 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Michal=20=C5=BDidek?= <mzidek@redhat.com>
-Date: Tue, 28 Mar 2017 18:33:46 +0200
-Subject: [PATCH 40/54] UTIL: Introduce subdomain_create_conf_path()
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-This is a utility function that replaces the create_subdom_conf_path().
-Differently than the latter, it only takes one parameter and is going to
-be used in a few different places (thus adding it to util.h).
-
-Reviewed-by: Fabiano Fidêncio <fidencio@redhat.com>
-Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
-Reviewed-by: Lukas Slebodnik <lslebodn@redhat.com>
----
- src/providers/ad/ad_common.c              |  7 -------
- src/providers/ad/ad_common.h              |  4 ----
- src/providers/ad/ad_subdomains.c          |  4 +---
- src/providers/ipa/ipa_subdomains_server.c |  4 +---
- src/util/domain_info_utils.c              | 15 +++++++++++++++
- src/util/util.h                           |  3 +++
- 6 files changed, 20 insertions(+), 17 deletions(-)
-
-diff --git a/src/providers/ad/ad_common.c b/src/providers/ad/ad_common.c
-index ec952d3bb4587516ea26fd27c212d5620e2f3dda..f893b748a2ddcff1eab6e8d919d2aa950b825446 100644
---- a/src/providers/ad/ad_common.c
-+++ b/src/providers/ad/ad_common.c
-@@ -33,13 +33,6 @@ errno_t ad_set_search_bases(struct sdap_options *id_opts);
- static errno_t ad_set_sdap_options(struct ad_options *ad_opts,
-                                    struct sdap_options *id_opts);
- 
--char *create_subdom_conf_path(TALLOC_CTX *mem_ctx,
--                              const char *conf_path,
--                              const char *subdom_name)
--{
--    return talloc_asprintf(mem_ctx, "%s/%s", conf_path, subdom_name);
--}
--
- static struct sdap_options *
- ad_create_default_sdap_options(TALLOC_CTX *mem_ctx)
- {
-diff --git a/src/providers/ad/ad_common.h b/src/providers/ad/ad_common.h
-index e02b932cd2da737254de8417d5c82fcdcf14e8d7..2981550f6c390929501ec8942e861b16ea0a5cb0 100644
---- a/src/providers/ad/ad_common.h
-+++ b/src/providers/ad/ad_common.h
-@@ -99,10 +99,6 @@ struct ad_options {
-     struct be_nsupdate_ctx *dyndns_ctx;
- };
- 
--char *create_subdom_conf_path(TALLOC_CTX *mem_ctx,
--                              const char *conf_path,
--                              const char *subdom_name);
--
- errno_t
- ad_get_common_options(TALLOC_CTX *mem_ctx,
-                       struct confdb_ctx *cdb,
-diff --git a/src/providers/ad/ad_subdomains.c b/src/providers/ad/ad_subdomains.c
-index 156ecab4272029d69c8b596eff041498a7524ce4..eecae9c9ca82ad67874c13a3c7b7c617d6232d5c 100644
---- a/src/providers/ad/ad_subdomains.c
-+++ b/src/providers/ad/ad_subdomains.c
-@@ -171,9 +171,7 @@ ad_subdom_ad_ctx_new(struct be_ctx *be_ctx,
-         return EINVAL;
-     }
- 
--    subdom_conf_path = create_subdom_conf_path(id_ctx,
--                                               be_ctx->conf_path,
--                                               subdom->name);
-+    subdom_conf_path = subdomain_create_conf_path(id_ctx, subdom);
-     if (subdom_conf_path == NULL) {
-         DEBUG(SSSDBG_CRIT_FAILURE, "subdom_conf_path failed\n");
-         return ENOMEM;
-diff --git a/src/providers/ipa/ipa_subdomains_server.c b/src/providers/ipa/ipa_subdomains_server.c
-index ae3baf036e4278fb67d86b42742fb7e80b46724e..e8ee30392d84f84e30bcdaa3d2110ba130b1ad73 100644
---- a/src/providers/ipa/ipa_subdomains_server.c
-+++ b/src/providers/ipa/ipa_subdomains_server.c
-@@ -176,9 +176,7 @@ static struct ad_options *ipa_ad_options_new(struct be_ctx *be_ctx,
-     forest_realm = subdom->forest_root->realm;
-     forest = subdom->forest_root->forest;
- 
--    subdom_conf_path = create_subdom_conf_path(id_ctx,
--                                               be_ctx->conf_path,
--                                               subdom->name);
-+    subdom_conf_path = subdomain_create_conf_path(id_ctx, subdom);
-     if (subdom_conf_path == NULL) {
-         DEBUG(SSSDBG_CRIT_FAILURE, "subdom_conf_path failed\n");
-         return NULL;
-diff --git a/src/util/domain_info_utils.c b/src/util/domain_info_utils.c
-index 6ef6bcfb8c078a360673b6bdd2364fc2918cb324..a7f118842aa8ba870143b2f2b425a3e3c0ea5a78 100644
---- a/src/util/domain_info_utils.c
-+++ b/src/util/domain_info_utils.c
-@@ -870,3 +870,18 @@ bool is_email_from_domain(const char *email, struct sss_domain_info *dom)
- 
-     return false;
- }
-+
-+char *subdomain_create_conf_path(TALLOC_CTX *mem_ctx,
-+                                 struct sss_domain_info *subdomain)
-+{
-+    if (!IS_SUBDOMAIN(subdomain)) {
-+        DEBUG(SSSDBG_OP_FAILURE,
-+              "The domain \"%s\" is not a subdomain.\n",
-+              subdomain->name);
-+        return NULL;
-+    }
-+
-+    return talloc_asprintf(mem_ctx, CONFDB_DOMAIN_PATH_TMPL "/%s",
-+                           subdomain->parent->name,
-+                           subdomain->name);
-+}
-diff --git a/src/util/util.h b/src/util/util.h
-index a2dc89b8ddb999437eda551ac17af28672d8759c..82760940269ad8883e725e3a5cf463486c9cfd36 100644
---- a/src/util/util.h
-+++ b/src/util/util.h
-@@ -551,6 +551,9 @@ find_domain_by_object_name(struct sss_domain_info *domain,
- bool subdomain_enumerates(struct sss_domain_info *parent,
-                           const char *sd_name);
- 
-+char *subdomain_create_conf_path(TALLOC_CTX *mem_ctx,
-+                                 struct sss_domain_info *subdomain);
-+
- errno_t sssd_domain_init(TALLOC_CTX *mem_ctx,
-                          struct confdb_ctx *cdb,
-                          const char *domain_name,
--- 
-2.9.3
-
diff --git a/SOURCES/0040-pam-add-prompt-string-for-certificate-authentication.patch b/SOURCES/0040-pam-add-prompt-string-for-certificate-authentication.patch
new file mode 100644
index 0000000..8d94ce2
--- /dev/null
+++ b/SOURCES/0040-pam-add-prompt-string-for-certificate-authentication.patch
@@ -0,0 +1,337 @@
+From 4f09838b50cc771d52c7b00cc47fb3362d8ecda2 Mon Sep 17 00:00:00 2001
+From: Sumit Bose <sbose@redhat.com>
+Date: Mon, 30 Oct 2017 08:03:42 +0100
+Subject: [PATCH 40/46] pam: add prompt string for certificate authentication
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+A new certificate attribute is added which contains a string which is
+used in the certificate selection list displayed to the user. The
+Subject-DN of the certificate is used here because it is present in all
+certificate and in general differs for certificate with different usage.
+libsss_certmap is used to extract the subject-DN from the certificate
+and convert it into a string.
+
+Related to https://pagure.io/SSSD/sssd/issue/3560
+
+Reviewed-by: Fabiano Fidêncio <fidencio@redhat.com>
+Tested-by: Scott Poore <spoore@redhat.com>
+(cherry picked from commit 06c2300353faf3983e38fecb1d6afe1f6cc8fe32)
+---
+ Makefile.am                     |  2 ++
+ src/responder/pam/pamsrv_p11.c  | 65 ++++++++++++++++++++++++++++++++++++++++-
+ src/sss_client/pam_sss.c        | 31 ++++++++++++++++----
+ src/tests/cmocka/test_pam_srv.c | 23 +++++++++++++--
+ 4 files changed, 111 insertions(+), 10 deletions(-)
+
+diff --git a/Makefile.am b/Makefile.am
+index 4ed872a532daf9b934537cc5f64ce77778121e2a..16bcb4efc028b05c1196249245f4f3091b9366af 100644
+--- a/Makefile.am
++++ b/Makefile.am
+@@ -1400,6 +1400,7 @@ sssd_pam_LDADD = \
+     $(SELINUX_LIBS) \
+     $(PAM_LIBS) \
+     $(SYSTEMD_DAEMON_LIBS) \
++    libsss_certmap.la \
+     $(SSSD_INTERNAL_LTLIBS) \
+     $(NULL)
+ 
+@@ -2423,6 +2424,7 @@ pam_srv_tests_LDADD = \
+     $(SYSTEMD_DAEMON_LIBS) \
+     libsss_test_common.la \
+     libsss_idmap.la \
++    libsss_certmap.la \
+     $(NULL)
+ 
+ EXTRA_responder_get_domains_tests_DEPENDENCIES = \
+diff --git a/src/responder/pam/pamsrv_p11.c b/src/responder/pam/pamsrv_p11.c
+index 4d5572164763ed0b3a842019f820680a4dc2dfdc..5a3eeff0ec977829a9ad8c80b4fc6b2e06857097 100644
+--- a/src/responder/pam/pamsrv_p11.c
++++ b/src/responder/pam/pamsrv_p11.c
+@@ -26,6 +26,8 @@
+ #include "util/child_common.h"
+ #include "util/strtonum.h"
+ #include "responder/pam/pamsrv.h"
++#include "lib/certmap/sss_certmap.h"
++#include "util/crypto/sss_crypto.h"
+ 
+ 
+ #ifndef SSSD_LIBEXEC_PATH
+@@ -683,6 +685,54 @@ errno_t pam_check_cert_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
+     return EOK;
+ }
+ 
++static char *get_cert_prompt(TALLOC_CTX *mem_ctx, const char *cert)
++{
++    int ret;
++    struct sss_certmap_ctx *ctx = NULL;
++    unsigned char *der = NULL;
++    size_t der_size;
++    char *prompt = NULL;
++    char *filter = NULL;
++    char **domains = NULL;
++
++    ret = sss_certmap_init(mem_ctx, NULL, NULL, &ctx);
++    if (ret != 0) {
++        DEBUG(SSSDBG_OP_FAILURE, "sss_certmap_init failed.\n");
++        return NULL;
++    }
++
++    ret = sss_certmap_add_rule(ctx, 10, "KRB5:<ISSUER>.*",
++                               "LDAP:{subject_dn!nss}", NULL);
++    if (ret != 0) {
++        DEBUG(SSSDBG_OP_FAILURE, "sss_certmap_add_rule failed.\n");
++        goto done;
++    }
++
++    der = sss_base64_decode(mem_ctx, cert, &der_size);
++    if (der == NULL) {
++        DEBUG(SSSDBG_OP_FAILURE, "sss_base64_decode failed.\n");
++        goto done;
++    }
++
++    ret = sss_certmap_get_search_filter(ctx, der, der_size, &filter, &domains);
++    if (ret != 0) {
++        DEBUG(SSSDBG_OP_FAILURE, "sss_certmap_get_search_filter failed.\n");
++        goto done;
++    }
++
++    prompt = talloc_strdup(mem_ctx, filter);
++    if (prompt == NULL) {
++        DEBUG(SSSDBG_OP_FAILURE, "talloc_strdup failed.\n");
++    }
++
++done:
++    sss_certmap_free_filter_and_domains(filter, domains);
++    sss_certmap_free_ctx(ctx);
++    talloc_free(der);
++
++    return prompt;
++}
++
+ static errno_t pack_cert_data(TALLOC_CTX *mem_ctx, const char *sysdb_username,
+                               struct cert_auth_info *cert_info,
+                               uint8_t **_msg, size_t *_msg_len)
+@@ -692,16 +742,24 @@ static errno_t pack_cert_data(TALLOC_CTX *mem_ctx, const char *sysdb_username,
+     const char *token_name;
+     const char *module_name;
+     const char *key_id;
++    char *prompt;
+     size_t user_len;
+     size_t token_len;
+     size_t module_len;
+     size_t key_id_len;
++    size_t prompt_len;
+     const char *username = "";
+ 
+     if (sysdb_username != NULL) {
+         username = sysdb_username;
+     }
+ 
++    prompt = get_cert_prompt(mem_ctx, sss_cai_get_cert(cert_info));
++    if (prompt == NULL) {
++        DEBUG(SSSDBG_OP_FAILURE, "get_cert_prompt failed.\n");
++        return EIO;
++    }
++
+     token_name = sss_cai_get_token_name(cert_info);
+     module_name = sss_cai_get_module_name(cert_info);
+     key_id = sss_cai_get_key_id(cert_info);
+@@ -710,10 +768,12 @@ static errno_t pack_cert_data(TALLOC_CTX *mem_ctx, const char *sysdb_username,
+     token_len = strlen(token_name) + 1;
+     module_len = strlen(module_name) + 1;
+     key_id_len = strlen(key_id) + 1;
+-    msg_len = user_len + token_len + module_len + key_id_len;
++    prompt_len = strlen(prompt) + 1;
++    msg_len = user_len + token_len + module_len + key_id_len + prompt_len;
+ 
+     msg = talloc_zero_size(mem_ctx, msg_len);
+     if (msg == NULL) {
++        talloc_free(prompt);
+         DEBUG(SSSDBG_OP_FAILURE, "talloc_zero_size failed.\n");
+         return ENOMEM;
+     }
+@@ -722,6 +782,9 @@ static errno_t pack_cert_data(TALLOC_CTX *mem_ctx, const char *sysdb_username,
+     memcpy(msg + user_len, token_name, token_len);
+     memcpy(msg + user_len + token_len, module_name, module_len);
+     memcpy(msg + user_len + token_len + module_len, key_id, key_id_len);
++    memcpy(msg + user_len + token_len + module_len + key_id_len,
++           prompt, prompt_len);
++    talloc_free(prompt);
+ 
+     if (_msg != NULL) {
+         *_msg = msg;
+diff --git a/src/sss_client/pam_sss.c b/src/sss_client/pam_sss.c
+index c147d4b3d76443d69e27eb2da042f8eebd1ae6ab..1dc51ea0536a92a63ec2f4d97f65dbb02604dbb3 100644
+--- a/src/sss_client/pam_sss.c
++++ b/src/sss_client/pam_sss.c
+@@ -129,6 +129,7 @@ struct cert_auth_info {
+     char *token_name;
+     char *module_name;
+     char *key_id;
++    char *prompt_str;
+     struct cert_auth_info *prev;
+     struct cert_auth_info *next;
+ };
+@@ -140,6 +141,7 @@ static void free_cai(struct cert_auth_info *cai)
+         free(cai->cert);
+         free(cai->token_name);
+         free(cai->key_id);
++        free(cai->prompt_str);
+         free(cai);
+     }
+ }
+@@ -921,9 +923,25 @@ static int parse_cert_info(struct pam_items *pi, uint8_t *buf, size_t len,
+         goto done;
+     }
+ 
+-    D(("cert user: [%s] token name: [%s] module: [%s] key id: [%s]",
++    offset += strlen(cai->key_id) + 1;
++    if (offset >= len) {
++        D(("Cert message size mismatch"));
++        ret = EINVAL;
++        goto done;
++    }
++
++    cai->prompt_str = strdup((char *) &buf[*p + offset]);
++    if (cai->prompt_str == NULL) {
++        D(("strdup failed"));
++        ret = ENOMEM;
++        goto done;
++    }
++
++
++    D(("cert user: [%s] token name: [%s] module: [%s] key id: [%s] "
++       "prompt: [%s]",
+        cai->cert_user, cai->token_name, cai->module_name,
+-       cai->key_id));
++       cai->key_id, cai->prompt_str));
+ 
+     DLIST_ADD(pi->cert_list, cai);
+     ret = 0;
+@@ -1543,7 +1561,7 @@ done:
+ #define discard_const(ptr) ((void *)((uintptr_t)(ptr)))
+ #endif
+ 
+-#define CERT_SEL_PROMPT_FMT "Certificate: %s"
++#define CERT_SEL_PROMPT_FMT "%s"
+ #define SEL_TITLE discard_const("Please select a certificate")
+ 
+ static int prompt_multi_cert_gdm(pam_handle_t *pamh, struct pam_items *pi)
+@@ -1588,7 +1606,7 @@ static int prompt_multi_cert_gdm(pam_handle_t *pamh, struct pam_items *pi)
+ 
+     c = 0;
+     DLIST_FOR_EACH(cai, pi->cert_list) {
+-        ret = asprintf(&prompt, CERT_SEL_PROMPT_FMT, cai->key_id);
++        ret = asprintf(&prompt, CERT_SEL_PROMPT_FMT, cai->prompt_str);
+         if (ret == -1) {
+             ret = ENOMEM;
+             goto done;
+@@ -1637,9 +1655,10 @@ done:
+ #endif
+ }
+ 
+-#define TEXT_CERT_SEL_PROMPT_FMT "%s[%zu] Certificate: %s\n"
++#define TEXT_CERT_SEL_PROMPT_FMT "%s\n[%zu]:\n%s\n"
+ #define TEXT_SEL_TITLE discard_const("Please select a certificate by typing " \
+                                      "the corresponding number\n")
++
+ static int prompt_multi_cert(pam_handle_t *pamh, struct pam_items *pi)
+ {
+     int ret;
+@@ -1670,7 +1689,7 @@ static int prompt_multi_cert(pam_handle_t *pamh, struct pam_items *pi)
+     DLIST_FOR_EACH(cai, pi->cert_list) {
+         cert_count++;
+         ret = asprintf(&tmp, TEXT_CERT_SEL_PROMPT_FMT, prompt, cert_count,
+-                                                       cai->key_id);
++                                                       cai->prompt_str);
+         free(prompt);
+         if (ret == -1) {
+             return ENOMEM;
+diff --git a/src/tests/cmocka/test_pam_srv.c b/src/tests/cmocka/test_pam_srv.c
+index 5c1f621ccead75717d1721714d953d7d4d415d7b..50d3ed005468375ff02c60bebd1c61047ca1c6d4 100644
+--- a/src/tests/cmocka/test_pam_srv.c
++++ b/src/tests/cmocka/test_pam_srv.c
+@@ -53,6 +53,7 @@
+ #define TEST_TOKEN_NAME "SSSD Test Token"
+ #define TEST_MODULE_NAME "NSS-Internal"
+ #define TEST_KEY_ID "A5EF7DEE625CA5996C8D1BA7D036708161FD49E7"
++#define TEST_SUBJECT_DN "CN=ipa-devel.ipa.devel,O=IPA.DEVEL"
+ #define TEST_TOKEN_CERT \
+ "MIIECTCCAvGgAwIBAgIBCTANBgkqhkiG9w0BAQsFADA0MRIwEAYDVQQKDAlJUEEu" \
+ "REVWRUwxHjAcBgNVBAMMFUNlcnRpZmljYXRlIEF1dGhvcml0eTAeFw0xNjA1MjMx" \
+@@ -78,6 +79,7 @@
+ "XyQBwOYRORlnfGyu+Yc9c3E0Wx8Tlznz0lqPR9g="
+ 
+ #define TEST2_KEY_ID "C8D60E009EB195D01A7083EE1D5419251AA87C2C"
++#define TEST2_SUBJECT_DN "CN=IPA RA,O=IPA.DEVEL"
+ #define TEST_TOKEN_2ND_CERT \
+ "MIIDazCCAlOgAwIBAgIBBzANBgkqhkiG9w0BAQsFADA0MRIwEAYDVQQKDAlJUEEu" \
+ "REVWRUwxHjAcBgNVBAMMFUNlcnRpZmljYXRlIEF1dGhvcml0eTAeFw0xNjA1MjMx" \
+@@ -831,7 +833,8 @@ static int test_pam_cert_check_gdm_smartcard(uint32_t status, uint8_t *body,
+     assert_int_equal(val, (sizeof("pamuser@"TEST_DOM_NAME)
+                                 + sizeof(TEST_TOKEN_NAME)
+                                 + sizeof(TEST_MODULE_NAME)
+-                                + sizeof(TEST_KEY_ID)));
++                                + sizeof(TEST_KEY_ID)
++                                + sizeof(TEST_SUBJECT_DN)));
+ 
+     assert_int_equal(*(body + rp + sizeof("pamuser@"TEST_DOM_NAME) - 1), 0);
+     assert_string_equal(body + rp, "pamuser@"TEST_DOM_NAME);
+@@ -849,6 +852,10 @@ static int test_pam_cert_check_gdm_smartcard(uint32_t status, uint8_t *body,
+     assert_string_equal(body + rp, TEST_KEY_ID);
+     rp += sizeof(TEST_KEY_ID);
+ 
++    assert_int_equal(*(body + rp + sizeof(TEST_SUBJECT_DN) - 1), 0);
++    assert_string_equal(body + rp, TEST_SUBJECT_DN);
++    rp += sizeof(TEST_SUBJECT_DN);
++
+     assert_int_equal(rp, blen);
+     return EOK;
+ }
+@@ -893,7 +900,8 @@ static int test_pam_cert_check_ex(uint32_t status, uint8_t *body, size_t blen,
+     assert_int_equal(val, (strlen(name) + 1
+                                 + sizeof(TEST_TOKEN_NAME)
+                                 + sizeof(TEST_MODULE_NAME)
+-                                + sizeof(TEST_KEY_ID)));
++                                + sizeof(TEST_KEY_ID)
++                                + sizeof(TEST_SUBJECT_DN)));
+ 
+     assert_int_equal(*(body + rp + strlen(name)), 0);
+     assert_string_equal(body + rp, name);
+@@ -911,6 +919,10 @@ static int test_pam_cert_check_ex(uint32_t status, uint8_t *body, size_t blen,
+     assert_string_equal(body + rp, TEST_KEY_ID);
+     rp += sizeof(TEST_KEY_ID);
+ 
++    assert_int_equal(*(body + rp + sizeof(TEST_SUBJECT_DN) - 1), 0);
++    assert_string_equal(body + rp, TEST_SUBJECT_DN);
++    rp += sizeof(TEST_SUBJECT_DN);
++
+     if (name2 != NULL && *name2 != '\0') {
+         SAFEALIGN_COPY_UINT32(&val, body + rp, &rp);
+         assert_int_equal(val, type);
+@@ -919,7 +931,8 @@ static int test_pam_cert_check_ex(uint32_t status, uint8_t *body, size_t blen,
+         assert_int_equal(val, (strlen(name) + 1
+                                     + sizeof(TEST_TOKEN_NAME)
+                                     + sizeof(TEST_MODULE_NAME)
+-                                    + sizeof(TEST2_KEY_ID)));
++                                    + sizeof(TEST2_KEY_ID)
++                                    + sizeof(TEST2_SUBJECT_DN)));
+ 
+         assert_int_equal(*(body + rp + strlen(name)), 0);
+         assert_string_equal(body + rp, name);
+@@ -936,6 +949,10 @@ static int test_pam_cert_check_ex(uint32_t status, uint8_t *body, size_t blen,
+         assert_int_equal(*(body + rp + sizeof(TEST2_KEY_ID) - 1), 0);
+         assert_string_equal(body + rp, TEST2_KEY_ID);
+         rp += sizeof(TEST2_KEY_ID);
++
++        assert_int_equal(*(body + rp + sizeof(TEST2_SUBJECT_DN) - 1), 0);
++        assert_string_equal(body + rp, TEST2_SUBJECT_DN);
++        rp += sizeof(TEST2_SUBJECT_DN);
+     }
+ 
+     assert_int_equal(rp, blen);
+-- 
+2.13.6
+
diff --git a/SOURCES/0041-PAM-allow-missing-logon_name-during-certificate-auth.patch b/SOURCES/0041-PAM-allow-missing-logon_name-during-certificate-auth.patch
new file mode 100644
index 0000000..669c063
--- /dev/null
+++ b/SOURCES/0041-PAM-allow-missing-logon_name-during-certificate-auth.patch
@@ -0,0 +1,256 @@
+From f6e57537cbeaf6e3f313e700f08e0022a32a7d6c Mon Sep 17 00:00:00 2001
+From: Sumit Bose <sbose@redhat.com>
+Date: Mon, 30 Oct 2017 17:11:56 +0100
+Subject: [PATCH 41/46] PAM: allow missing logon_name during certificate
+ authentication
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+If only one certificate is available and the logon_name is the user is
+not given the PAM responder already tried to find the name during the
+pre-auth step. With multiple certificates this might cause useless extra
+effort and the name should be determined after the certificate is
+selected in the authentication step. This might currently only happen
+with GDM because all other PAM clients will prompt for the user name
+unconditionally.
+
+New unit tests are added to cover this new case.
+
+Related to https://pagure.io/SSSD/sssd/issue/3560
+
+Reviewed-by: Fabiano Fidêncio <fidencio@redhat.com>
+Tested-by: Scott Poore <spoore@redhat.com>
+(cherry picked from commit fd6f4047b58686bd4057c9859c3c804a77b136d8)
+---
+ src/responder/pam/pamsrv_cmd.c  | 63 ++++++++++++++++++++++++++-----
+ src/tests/cmocka/test_pam_srv.c | 82 +++++++++++++++++++++++++++++++++++++++++
+ 2 files changed, 135 insertions(+), 10 deletions(-)
+
+diff --git a/src/responder/pam/pamsrv_cmd.c b/src/responder/pam/pamsrv_cmd.c
+index 8b2c086e206796ad4c977495be957c56b3255e7f..caf6c99489b8378d2e850473191223709920cd79 100644
+--- a/src/responder/pam/pamsrv_cmd.c
++++ b/src/responder/pam/pamsrv_cmd.c
+@@ -1151,6 +1151,7 @@ static errno_t pam_forwarder_parse_data(struct cli_ctx *cctx, struct pam_data *p
+     size_t blen;
+     errno_t ret;
+     uint32_t terminator;
++    const char *key_id;
+ 
+     prctx = talloc_get_type(cctx->protocol_ctx, struct cli_protocol);
+ 
+@@ -1191,9 +1192,33 @@ static errno_t pam_forwarder_parse_data(struct cli_ctx *cctx, struct pam_data *p
+                                          pd->logon_name,
+                                          &pd->domain, &pd->user);
+     } else {
+-        /* Only SSS_PAM_PREAUTH request may have a missing name, e.g. if the
+-         * name is determined with the help of a certificate */
+-        if (pd->cmd == SSS_PAM_PREAUTH
++        /* SSS_PAM_PREAUTH request may have a missing name, e.g. if the
++         * name is determined with the help of a certificate. During
++         * SSS_PAM_AUTHENTICATE at least a key ID is needed to identify the
++         * selected certificate. */
++        if (pd->cmd == SSS_PAM_AUTHENTICATE
++                && may_do_cert_auth(talloc_get_type(cctx->rctx->pvt_ctx,
++                                                    struct pam_ctx), pd)
++                && (sss_authtok_get_type(pd->authtok) == SSS_AUTHTOK_TYPE_SC_PIN
++                    || sss_authtok_get_type(pd->authtok)
++                                               == SSS_AUTHTOK_TYPE_SC_KEYPAD)) {
++            ret = sss_authtok_get_sc(pd->authtok, NULL, NULL, NULL, NULL, NULL,
++                                     NULL, &key_id, NULL);
++            if (ret != EOK) {
++                DEBUG(SSSDBG_OP_FAILURE, "sss_authtok_get_sc failed.\n");
++                goto done;
++            }
++
++            if (key_id == NULL || *key_id == '\0') {
++                DEBUG(SSSDBG_CRIT_FAILURE,
++                      "Missing logon and Smartcard key ID during "
++                      "authentication.\n");
++                ret = ERR_NO_CREDS;
++                goto done;
++            }
++
++            ret = EOK;
++        } else if (pd->cmd == SSS_PAM_PREAUTH
+                 && may_do_cert_auth(talloc_get_type(cctx->rctx->pvt_ctx,
+                                                     struct pam_ctx), pd)) {
+             ret = EOK;
+@@ -1375,9 +1400,12 @@ static int pam_forwarder(struct cli_ctx *cctx, int pam_cmd)
+     /* Determine what domain type to contact */
+     preq->req_dom_type = get_domain_request_type(preq, pctx);
+ 
+-    /* try backend first for authentication before doing local Smartcard
+-     * authentication */
+-    if (pd->cmd != SSS_PAM_AUTHENTICATE && may_do_cert_auth(pctx, pd)) {
++    /* Try backend first for authentication before doing local Smartcard
++     * authentication if a logon name is available. Otherwise try to derive
++     * the logon name from the certificate first. */
++    if ((pd->cmd != SSS_PAM_AUTHENTICATE
++                || (pd->cmd == SSS_PAM_AUTHENTICATE && pd->logon_name == NULL))
++            && may_do_cert_auth(pctx, pd)) {
+         ret = check_cert(cctx, cctx->ev, pctx, preq, pd);
+         /* Finish here */
+         goto done;
+@@ -1577,9 +1605,10 @@ static void pam_forwarder_lookup_by_cert_done(struct tevent_req *req)
+     } else {
+ 
+         if (preq->pd->logon_name == NULL) {
+-            if (preq->pd->cmd != SSS_PAM_PREAUTH) {
++            if (preq->pd->cmd != SSS_PAM_PREAUTH
++                    && preq->pd->cmd != SSS_PAM_AUTHENTICATE) {
+                 DEBUG(SSSDBG_CRIT_FAILURE,
+-                      "Missing logon name only allowed during pre-auth.\n");
++                      "Missing logon name only allowed during (pre-)auth.\n");
+                 ret = ENOENT;
+                 goto done;
+             }
+@@ -1641,7 +1670,8 @@ static void pam_forwarder_lookup_by_cert_done(struct tevent_req *req)
+                 }
+             }
+ 
+-            if (preq->cctx->rctx->domains->user_name_hint) {
++            if (preq->cctx->rctx->domains->user_name_hint
++                    && preq->pd->cmd == SSS_PAM_PREAUTH) {
+                 ret = add_pam_cert_response(preq->pd, cert_user,
+                                             preq->cert_list,
+                                             SSS_PAM_CERT_INFO_WITH_HINT);
+@@ -1664,6 +1694,20 @@ static void pam_forwarder_lookup_by_cert_done(struct tevent_req *req)
+                 goto done;
+             }
+ 
++            /* If logon_name was not given during authentication add a
++             * SSS_PAM_CERT_INFO message to send the name to the caller. */
++            if (preq->pd->cmd == SSS_PAM_AUTHENTICATE
++                    && preq->pd->logon_name == NULL) {
++                ret = add_pam_cert_response(preq->pd, cert_user,
++                                            preq->cert_list,
++                                            SSS_PAM_CERT_INFO);
++                if (ret != EOK) {
++                    DEBUG(SSSDBG_OP_FAILURE, "add_pam_cert_response failed.\n");
++                    preq->pd->pam_status = PAM_AUTHINFO_UNAVAIL;
++                    goto done;
++                }
++            }
++
+             /* cert_user will be returned to the PAM client as user name, so
+              * we can use it here already e.g. to set in initgroups timeout */
+             preq->pd->logon_name = talloc_strdup(preq->pd, cert_user);
+@@ -2039,7 +2083,6 @@ static void pam_dom_forwarder(struct pam_auth_req *preq)
+                                   "the backend.\n");
+                         }
+ 
+-                        /* FIXME: use the right cert info */
+                         ret = add_pam_cert_response(preq->pd, cert_user,
+                                                     preq->current_cert,
+                                                     SSS_PAM_CERT_INFO);
+diff --git a/src/tests/cmocka/test_pam_srv.c b/src/tests/cmocka/test_pam_srv.c
+index 50d3ed005468375ff02c60bebd1c61047ca1c6d4..b6845320ca41d6933280aa2836a3d984dacfcc5e 100644
+--- a/src/tests/cmocka/test_pam_srv.c
++++ b/src/tests/cmocka/test_pam_srv.c
+@@ -967,6 +967,16 @@ static int test_pam_cert_check(uint32_t status, uint8_t *body, size_t blen)
+                                   NULL);
+ }
+ 
++static int test_pam_cert_check_auth_success(uint32_t status, uint8_t *body,
++                                            size_t blen)
++{
++    assert_int_equal(pam_test_ctx->exp_pam_status, PAM_BAD_ITEM);
++    pam_test_ctx->exp_pam_status = PAM_SUCCESS;
++    return test_pam_cert_check_ex(status, body, blen,
++                                  SSS_PAM_CERT_INFO, "pamuser@"TEST_DOM_NAME,
++                                  NULL);
++}
++
+ static int test_pam_cert_check_with_hint(uint32_t status, uint8_t *body,
+                                          size_t blen)
+ {
+@@ -2265,6 +2275,74 @@ void test_pam_cert_auth(void **state)
+     assert_int_equal(ret, EOK);
+ }
+ 
++void test_pam_cert_auth_no_logon_name(void **state)
++{
++    int ret;
++
++    set_cert_auth_param(pam_test_ctx->pctx, NSS_DB);
++
++    /* Here the last option must be set to true because the backend is only
++     * connected once. During authentication the backend is connected first to
++     * see if it can handle Smartcard authentication, but before that the user
++     * is looked up. Since the first mocked reply already adds the certificate
++     * to the user entry the lookup by certificate will already find the user
++     * in the cache and no second request to the backend is needed. */
++    mock_input_pam_cert(pam_test_ctx, NULL, "123456", "SSSD Test Token",
++                        "NSS-Internal",
++                        "A5EF7DEE625CA5996C8D1BA7D036708161FD49E7", NULL,
++                        test_lookup_by_cert_cb, TEST_TOKEN_CERT, true);
++
++    mock_account_recv_simple();
++    mock_parse_inp("pamuser", NULL, EOK);
++
++    will_return(__wrap_sss_packet_get_cmd, SSS_PAM_AUTHENTICATE);
++    will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL);
++
++    /* Assume backend cannot handle Smartcard credentials */
++    pam_test_ctx->exp_pam_status = PAM_BAD_ITEM;
++
++    set_cmd_cb(test_pam_cert_check_auth_success);
++    ret = sss_cmd_execute(pam_test_ctx->cctx, SSS_PAM_AUTHENTICATE,
++                          pam_test_ctx->pam_cmds);
++    assert_int_equal(ret, EOK);
++
++    /* Wait until the test finishes with EOK */
++    ret = test_ev_loop(pam_test_ctx->tctx);
++    assert_int_equal(ret, EOK);
++}
++
++void test_pam_cert_auth_no_logon_name_no_key_id(void **state)
++{
++    int ret;
++
++    set_cert_auth_param(pam_test_ctx->pctx, NSS_DB);
++
++    /* Here the last option must be set to true because the backend is only
++     * connected once. During authentication the backend is connected first to
++     * see if it can handle Smartcard authentication, but before that the user
++     * is looked up. Since the first mocked reply already adds the certificate
++     * to the user entry the lookup by certificate will already find the user
++     * in the cache and no second request to the backend is needed. */
++    mock_input_pam_cert(pam_test_ctx, NULL, "123456", "SSSD Test Token",
++                        "NSS-Internal", NULL, NULL,
++                        NULL, NULL, false);
++
++    will_return(__wrap_sss_packet_get_cmd, SSS_PAM_AUTHENTICATE);
++    will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL);
++
++    /* Assume backend cannot handle Smartcard credentials */
++    pam_test_ctx->exp_pam_status = PAM_BAD_ITEM;
++
++    set_cmd_cb(test_pam_creds_insufficient_check);
++    ret = sss_cmd_execute(pam_test_ctx->cctx, SSS_PAM_AUTHENTICATE,
++                          pam_test_ctx->pam_cmds);
++    assert_int_equal(ret, EOK);
++
++    /* Wait until the test finishes with EOK */
++    ret = test_ev_loop(pam_test_ctx->tctx);
++    assert_int_equal(ret, EOK);
++}
++
+ void test_pam_cert_auth_double_cert(void **state)
+ {
+     int ret;
+@@ -2770,6 +2848,10 @@ int main(int argc, const char *argv[])
+                                         pam_test_setup, pam_test_teardown),
+         cmocka_unit_test_setup_teardown(test_pam_cert_preauth_2certs_two_mappings,
+                                         pam_test_setup, pam_test_teardown),
++        cmocka_unit_test_setup_teardown(test_pam_cert_auth_no_logon_name,
++                                        pam_test_setup, pam_test_teardown),
++        cmocka_unit_test_setup_teardown(test_pam_cert_auth_no_logon_name_no_key_id,
++                                        pam_test_setup, pam_test_teardown),
+ #endif /* HAVE_NSS */
+ 
+         cmocka_unit_test_setup_teardown(test_filter_response,
+-- 
+2.13.6
+
diff --git a/SOURCES/0041-SUBDOMAINS-Allow-use_fully_qualified_names-for-subdo.patch b/SOURCES/0041-SUBDOMAINS-Allow-use_fully_qualified_names-for-subdo.patch
deleted file mode 100644
index f9a0fe3..0000000
--- a/SOURCES/0041-SUBDOMAINS-Allow-use_fully_qualified_names-for-subdo.patch
+++ /dev/null
@@ -1,531 +0,0 @@
-From 887b53d8833ab91835cb3afbdadcbf9d091dafcd Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Michal=20=C5=BDidek?= <mzidek@redhat.com>
-Date: Thu, 23 Mar 2017 13:14:56 +0100
-Subject: [PATCH 41/54] SUBDOMAINS: Allow use_fully_qualified_names for
- subdomains
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Allow option use_fully_qualified_names in subdomain section.
-This option was recently added to subdomain_inherit.
-
-Resolves:
-https://pagure.io/SSSD/sssd/issue/3337
-
-Reviewed-by: Fabiano Fidêncio <fidencio@redhat.com>
----
- src/db/sysdb.h                                |  3 +-
- src/db/sysdb_private.h                        |  3 +-
- src/db/sysdb_subdomains.c                     | 63 +++++++++++++++++++++++++--
- src/man/sssd.conf.5.xml                       |  3 +-
- src/providers/ad/ad_subdomains.c              |  3 +-
- src/providers/ipa/ipa_subdomains.c            | 10 +++--
- src/responder/common/responder_get_domains.c  |  9 ++--
- src/tests/cmocka/test_fqnames.c               |  2 +-
- src/tests/cmocka/test_ipa_subdomains_server.c |  2 +-
- src/tests/cmocka/test_nss_srv.c               |  6 ++-
- src/tests/cmocka/test_sysdb_subdomains.c      | 25 ++++++-----
- src/tests/sysdb-tests.c                       | 14 +++---
- src/tools/common/sss_tools.c                  |  2 +-
- src/tools/sss_cache.c                         |  2 +-
- 14 files changed, 107 insertions(+), 40 deletions(-)
-
-diff --git a/src/db/sysdb.h b/src/db/sysdb.h
-index 0cbb2c5c02355e9e9a4e73b075f92d16e4855045..6762b51bee02911fb97d5d393fad2495504ee5ad 100644
---- a/src/db/sysdb.h
-+++ b/src/db/sysdb.h
-@@ -494,7 +494,8 @@ errno_t sysdb_subdomain_store(struct sysdb_ctx *sysdb,
-                               uint32_t trust_direction,
-                               struct ldb_message_element *upn_suffixes);
- 
--errno_t sysdb_update_subdomains(struct sss_domain_info *domain);
-+errno_t sysdb_update_subdomains(struct sss_domain_info *domain,
-+                                struct confdb_ctx *confdb);
- 
- errno_t sysdb_master_domain_update(struct sss_domain_info *domain);
- 
-diff --git a/src/db/sysdb_private.h b/src/db/sysdb_private.h
-index bfd24799950ab3b31d57df11b8f91c0b2572f13a..dfddd2dda9e593bd02d52dee7d06f520a11bbdf6 100644
---- a/src/db/sysdb_private.h
-+++ b/src/db/sysdb_private.h
-@@ -191,7 +191,8 @@ struct sss_domain_info *new_subdomain(TALLOC_CTX *mem_ctx,
-                                       bool enumerate,
-                                       const char *forest,
-                                       const char **upn_suffixes,
--                                      uint32_t trust_direction);
-+                                      uint32_t trust_direction,
-+                                      struct confdb_ctx *confdb);
- 
- /* Helper functions to deal with the timestamp cache should not be used
-  * outside the sysdb itself. The timestamp cache should be completely
-diff --git a/src/db/sysdb_subdomains.c b/src/db/sysdb_subdomains.c
-index 01f49763b712769f4f74df47961526e5b1514cd4..916dbba153d8c08837425f6fd29a20f5a6aa9fc9 100644
---- a/src/db/sysdb_subdomains.c
-+++ b/src/db/sysdb_subdomains.c
-@@ -23,6 +23,10 @@
- #include "util/util.h"
- #include "db/sysdb_private.h"
- 
-+static errno_t
-+check_subdom_config_file(struct confdb_ctx *confdb,
-+                         struct sss_domain_info *subdomain);
-+
- struct sss_domain_info *new_subdomain(TALLOC_CTX *mem_ctx,
-                                       struct sss_domain_info *parent,
-                                       const char *name,
-@@ -33,10 +37,12 @@ struct sss_domain_info *new_subdomain(TALLOC_CTX *mem_ctx,
-                                       bool enumerate,
-                                       const char *forest,
-                                       const char **upn_suffixes,
--                                      uint32_t trust_direction)
-+                                      uint32_t trust_direction,
-+                                      struct confdb_ctx *confdb)
- {
-     struct sss_domain_info *dom;
-     bool inherit_option;
-+    errno_t ret;
- 
-     DEBUG(SSSDBG_TRACE_FUNC,
-           "Creating [%s] as subdomain of [%s]!\n", name, parent->name);
-@@ -160,6 +166,17 @@ struct sss_domain_info *new_subdomain(TALLOC_CTX *mem_ctx,
-     }
-     dom->sysdb = parent->sysdb;
- 
-+    if (confdb != NULL) {
-+        /* If confdb was provided, also check for sssd.conf */
-+        ret = check_subdom_config_file(confdb, dom);
-+        if (ret != EOK) {
-+            DEBUG(SSSDBG_CRIT_FAILURE,
-+                  "Failed to read subdomain configuration [%d]: %s",
-+                   ret, sss_strerror(ret));
-+            goto fail;
-+        }
-+    }
-+
-     return dom;
- 
- fail:
-@@ -167,6 +184,45 @@ fail:
-     return NULL;
- }
- 
-+static errno_t
-+check_subdom_config_file(struct confdb_ctx *confdb,
-+                         struct sss_domain_info *subdomain)
-+{
-+    char *sd_conf_path;
-+    TALLOC_CTX *tmp_ctx;
-+    errno_t ret;
-+
-+    tmp_ctx = talloc_new(NULL);
-+    if (tmp_ctx == NULL) {
-+        return ENOMEM;
-+    }
-+
-+    sd_conf_path = subdomain_create_conf_path(tmp_ctx, subdomain);
-+    if (sd_conf_path == NULL) {
-+        ret = ENOMEM;
-+        goto done;
-+    }
-+
-+    /* use_fully_qualified_names */
-+    ret = confdb_get_bool(confdb, sd_conf_path, CONFDB_DOMAIN_FQ,
-+                          true, &subdomain->fqnames);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_OP_FAILURE,
-+              "Failed to get %s option for the subdomain: %s\n",
-+              CONFDB_DOMAIN_FQ, subdomain->name);
-+        goto done;
-+    }
-+
-+    DEBUG(SSSDBG_CONF_SETTINGS, "%s/%s has value %s\n",
-+          sd_conf_path, CONFDB_DOMAIN_FQ,
-+          subdomain->fqnames ? "TRUE" : "FALSE");
-+
-+    ret = EOK;
-+done:
-+    talloc_free(tmp_ctx);
-+    return ret;
-+}
-+
- static bool is_forest_root(struct sss_domain_info *d)
- {
-     if (d->forest == NULL) {
-@@ -232,7 +288,8 @@ static void link_forest_roots(struct sss_domain_info *domain)
-     }
- }
- 
--errno_t sysdb_update_subdomains(struct sss_domain_info *domain)
-+errno_t sysdb_update_subdomains(struct sss_domain_info *domain,
-+                                struct confdb_ctx *confdb)
- {
-     int i;
-     errno_t ret;
-@@ -451,7 +508,7 @@ errno_t sysdb_update_subdomains(struct sss_domain_info *domain)
-         if (dom == NULL) {
-             dom = new_subdomain(domain, domain, name, realm,
-                                 flat, id, mpg, enumerate, forest,
--                                upn_suffixes, trust_direction);
-+                                upn_suffixes, trust_direction, confdb);
-             if (dom == NULL) {
-                 ret = ENOMEM;
-                 goto done;
-diff --git a/src/man/sssd.conf.5.xml b/src/man/sssd.conf.5.xml
-index 284402bc00d37c6c33bf195d2bd719300f265851..1c27742cf0c1b6ffad23ab5b044bf4a168ed8f69 100644
---- a/src/man/sssd.conf.5.xml
-+++ b/src/man/sssd.conf.5.xml
-@@ -2780,7 +2780,8 @@ subdomain_inherit = ldap_purge_cache_timeout
-             <para>ldap_service_search_base,</para>
-             <para>ad_server,</para>
-             <para>ad_backup_server,</para>
--            <para>ad_site.</para>
-+            <para>ad_site,</para>
-+            <para>use_fully_qualified_names</para>
-         <para>
-             For more details about these options see their individual description
-             in the manual page.
-diff --git a/src/providers/ad/ad_subdomains.c b/src/providers/ad/ad_subdomains.c
-index eecae9c9ca82ad67874c13a3c7b7c617d6232d5c..bc659b2cb0a02723437d24d0021ec3592381e84c 100644
---- a/src/providers/ad/ad_subdomains.c
-+++ b/src/providers/ad/ad_subdomains.c
-@@ -656,7 +656,8 @@ static errno_t ad_subdom_reinit(struct ad_subdomains_ctx *subdoms_ctx)
-         /* Just continue */
-     }
- 
--    ret = sysdb_update_subdomains(subdoms_ctx->be_ctx->domain);
-+    ret = sysdb_update_subdomains(subdoms_ctx->be_ctx->domain,
-+                                  subdoms_ctx->be_ctx->cdb);
-     if (ret != EOK) {
-         DEBUG(SSSDBG_OP_FAILURE, "sysdb_update_subdomains failed.\n");
-         return ret;
-diff --git a/src/providers/ipa/ipa_subdomains.c b/src/providers/ipa/ipa_subdomains.c
-index 7537550606ef09c0b87a80932c75aa4f93c0efab..a07b88fe2f499353293ba90345552413c9792f4b 100644
---- a/src/providers/ipa/ipa_subdomains.c
-+++ b/src/providers/ipa/ipa_subdomains.c
-@@ -126,7 +126,7 @@ ipa_subdom_reinit(struct ipa_subdomains_ctx *ctx)
-         return ret;
-     }
- 
--    ret = sysdb_update_subdomains(ctx->be_ctx->domain);
-+    ret = sysdb_update_subdomains(ctx->be_ctx->domain, ctx->be_ctx->cdb);
-     if (ret != EOK) {
-         DEBUG(SSSDBG_OP_FAILURE, "sysdb_update_subdomains failed.\n");
-         return ret;
-@@ -780,7 +780,8 @@ done:
- static errno_t ipa_apply_view(struct sss_domain_info *domain,
-                               struct ipa_id_ctx *ipa_id_ctx,
-                               const char *view_name,
--                              bool read_at_init)
-+                              bool read_at_init,
-+                              struct confdb_ctx *confdb)
- {
-     const char *current = ipa_id_ctx->view_name;
-     struct sysdb_ctx *sysdb = domain->sysdb;
-@@ -876,7 +877,7 @@ static errno_t ipa_apply_view(struct sss_domain_info *domain,
-             goto done;
-         }
- 
--        ret = sysdb_update_subdomains(domain);
-+        ret = sysdb_update_subdomains(domain, confdb);
-         if (ret != EOK) {
-             DEBUG(SSSDBG_OP_FAILURE, "sysdb_update_subdomains failed "
-                   "[%d]: %s\n", ret, sss_strerror(ret));
-@@ -1654,7 +1655,8 @@ static void ipa_subdomains_view_name_done(struct tevent_req *subreq)
- 
-     ret = ipa_apply_view(state->sd_ctx->be_ctx->domain,
-                          state->sd_ctx->ipa_id_ctx, view_name,
--                         state->sd_ctx->view_read_at_init);
-+                         state->sd_ctx->view_read_at_init,
-+                         state->sd_ctx->be_ctx->cdb);
-     if (ret != EOK) {
-         DEBUG(SSSDBG_CRIT_FAILURE, "Unable to set view [%d]: %s\n",
-               ret, sss_strerror(ret));
-diff --git a/src/responder/common/responder_get_domains.c b/src/responder/common/responder_get_domains.c
-index 0f39d107dad6c458785b1b8d708e60d7c34e3901..0f9c01214631200f9687635f6302fa5c07e8a1fe 100644
---- a/src/responder/common/responder_get_domains.c
-+++ b/src/responder/common/responder_get_domains.c
-@@ -126,7 +126,8 @@ get_next_domain_recv(TALLOC_CTX *mem_ctx,
- }
- 
- /* ====== Iterate over all domains, searching for their subdomains  ======= */
--static errno_t process_subdomains(struct sss_domain_info *dom);
-+static errno_t process_subdomains(struct sss_domain_info *dom,
-+                                  struct confdb_ctx *confdb);
- static void set_time_of_last_request(struct resp_ctx *rctx);
- static errno_t check_last_request(struct resp_ctx *rctx, const char *hint);
- 
-@@ -234,7 +235,7 @@ sss_dp_get_domains_process(struct tevent_req *subreq)
-         goto fail;
-     }
- 
--    ret = process_subdomains(state->dom);
-+    ret = process_subdomains(state->dom, state->rctx->cdb);
-     if (ret != EOK) {
-         DEBUG(SSSDBG_OP_FAILURE, "process_subdomains failed, "
-                                   "trying next domain.\n");
-@@ -270,7 +271,7 @@ fail:
- }
- 
- static errno_t
--process_subdomains(struct sss_domain_info *domain)
-+process_subdomains(struct sss_domain_info *domain, struct confdb_ctx *confdb)
- {
-     int ret;
- 
-@@ -288,7 +289,7 @@ process_subdomains(struct sss_domain_info *domain)
-     /* Retrieve all subdomains of this domain from sysdb
-      * and create their struct sss_domain_info representations
-      */
--    ret = sysdb_update_subdomains(domain);
-+    ret = sysdb_update_subdomains(domain, confdb);
-     if (ret != EOK) {
-         DEBUG(SSSDBG_FUNC_DATA, "sysdb_update_subdomains failed.\n");
-         goto done;
-diff --git a/src/tests/cmocka/test_fqnames.c b/src/tests/cmocka/test_fqnames.c
-index 19788248a39774bb4509363145ac4ce0815b7d28..0ed42a597b7787635c4971b4f1c3d9976949ccd2 100644
---- a/src/tests/cmocka/test_fqnames.c
-+++ b/src/tests/cmocka/test_fqnames.c
-@@ -309,7 +309,7 @@ static int parse_name_test_setup(void **state)
-      * discovered
-      */
-     test_ctx->subdom = new_subdomain(dom, dom, SUBDOMNAME, NULL, SUBFLATNAME,
--                                     NULL, false, false, NULL, NULL, 0);
-+                                     NULL, false, false, NULL, NULL, 0, NULL);
-     assert_non_null(test_ctx->subdom);
- 
-     check_leaks_push(test_ctx);
-diff --git a/src/tests/cmocka/test_ipa_subdomains_server.c b/src/tests/cmocka/test_ipa_subdomains_server.c
-index 123cf11c01ef4687eecad31a9d73120a87c643e1..ca48425aca69e58358f5fd37e4b8238bfa9efe15 100644
---- a/src/tests/cmocka/test_ipa_subdomains_server.c
-+++ b/src/tests/cmocka/test_ipa_subdomains_server.c
-@@ -263,7 +263,7 @@ static void add_test_subdomains(struct trust_test_ctx *test_ctx,
-                                 direction, NULL);
-     assert_int_equal(ret, EOK);
- 
--    ret = sysdb_update_subdomains(test_ctx->tctx->dom);
-+    ret = sysdb_update_subdomains(test_ctx->tctx->dom, test_ctx->tctx->confdb);
-     assert_int_equal(ret, EOK);
- 
- }
-diff --git a/src/tests/cmocka/test_nss_srv.c b/src/tests/cmocka/test_nss_srv.c
-index 50714715cc80338640f2a77ecbe17bd5e0d6e911..3d7e0382197401cb2264671712152fe0709296b6 100644
---- a/src/tests/cmocka/test_nss_srv.c
-+++ b/src/tests/cmocka/test_nss_srv.c
-@@ -3206,7 +3206,8 @@ static int nss_subdom_test_setup(void **state)
- 
-     subdomain = new_subdomain(nss_test_ctx, nss_test_ctx->tctx->dom,
-                               testdom[0], testdom[1], testdom[2], testdom[3],
--                              false, false, NULL, NULL, 0);
-+                              false, false, NULL, NULL, 0,
-+                              nss_test_ctx->tctx->confdb);
-     assert_non_null(subdomain);
- 
-     ret = sysdb_subdomain_store(nss_test_ctx->tctx->sysdb,
-@@ -3214,7 +3215,8 @@ static int nss_subdom_test_setup(void **state)
-                                 false, false, NULL, 0, NULL);
-     assert_int_equal(ret, EOK);
- 
--    ret = sysdb_update_subdomains(nss_test_ctx->tctx->dom);
-+    ret = sysdb_update_subdomains(nss_test_ctx->tctx->dom,
-+                                  nss_test_ctx->tctx->confdb);
-     assert_int_equal(ret, EOK);
- 
-     nss_test_ctx->subdom = subdomain;
-diff --git a/src/tests/cmocka/test_sysdb_subdomains.c b/src/tests/cmocka/test_sysdb_subdomains.c
-index 49f44998a06740d1df70ac354ee741824acd8f50..84bcdc17b39dbc8822097c2006f157a09ea5e466 100644
---- a/src/tests/cmocka/test_sysdb_subdomains.c
-+++ b/src/tests/cmocka/test_sysdb_subdomains.c
-@@ -103,7 +103,7 @@ static void test_sysdb_subdomain_create(void **state)
-                                 false, false, NULL, 0, NULL);
-     assert_int_equal(ret, EOK);
- 
--    ret = sysdb_update_subdomains(test_ctx->tctx->dom);
-+    ret = sysdb_update_subdomains(test_ctx->tctx->dom, test_ctx->tctx->confdb);
-     assert_int_equal(ret, EOK);
- 
-     assert_non_null(test_ctx->tctx->dom->subdomains);
-@@ -115,7 +115,7 @@ static void test_sysdb_subdomain_create(void **state)
-                                 false, false, NULL, 1, NULL);
-     assert_int_equal(ret, EOK);
- 
--    ret = sysdb_update_subdomains(test_ctx->tctx->dom);
-+    ret = sysdb_update_subdomains(test_ctx->tctx->dom, test_ctx->tctx->confdb);
-     assert_int_equal(ret, EOK);
- 
-     assert_non_null(test_ctx->tctx->dom->subdomains->next);
-@@ -133,7 +133,7 @@ static void test_sysdb_subdomain_create(void **state)
-                                 false, false, NULL, 0, NULL);
-     assert_int_equal(ret, EOK);
- 
--    ret = sysdb_update_subdomains(test_ctx->tctx->dom);
-+    ret = sysdb_update_subdomains(test_ctx->tctx->dom, test_ctx->tctx->confdb);
-     assert_int_equal(ret, EOK);
- 
-     assert_int_equal(test_ctx->tctx->dom->subdomains->trust_direction, 1);
-@@ -145,7 +145,7 @@ static void test_sysdb_subdomain_create(void **state)
-     ret = sysdb_subdomain_delete(test_ctx->tctx->sysdb, dom1[0]);
-     assert_int_equal(ret, EOK);
- 
--    ret = sysdb_update_subdomains(test_ctx->tctx->dom);
-+    ret = sysdb_update_subdomains(test_ctx->tctx->dom, test_ctx->tctx->confdb);
-     assert_int_equal(ret, EOK);
- 
-     assert_int_equal(sss_domain_get_state(test_ctx->tctx->dom->subdomains),
-@@ -235,11 +235,11 @@ static void test_sysdb_link_forest_root_ipa(void **state)
-                                 0, NULL);
-     assert_int_equal(ret, EOK);
- 
--    ret = sysdb_update_subdomains(test_ctx->tctx->dom);
-+    ret = sysdb_update_subdomains(test_ctx->tctx->dom, test_ctx->tctx->confdb);
-     assert_int_equal(ret, EOK);
- 
-     /* Also update dom2 */
--    ret = sysdb_update_subdomains(test_ctx->tctx->dom->next);
-+    ret = sysdb_update_subdomains(test_ctx->tctx->dom->next, test_ctx->tctx->confdb);
-     assert_int_equal(ret, EOK);
- 
-     sub = find_domain_by_name(test_ctx->tctx->dom, dom1[0], true);
-@@ -315,11 +315,11 @@ static void test_sysdb_link_forest_root_ad(void **state)
-                                 0, NULL);
-     assert_int_equal(ret, EOK);
- 
--    ret = sysdb_update_subdomains(test_ctx->tctx->dom);
-+    ret = sysdb_update_subdomains(test_ctx->tctx->dom, test_ctx->tctx->confdb);
-     assert_int_equal(ret, EOK);
- 
-     /* Also update dom2 */
--    ret = sysdb_update_subdomains(test_ctx->tctx->dom->next);
-+    ret = sysdb_update_subdomains(test_ctx->tctx->dom->next, test_ctx->tctx->confdb);
-     assert_int_equal(ret, EOK);
- 
-     assert_non_null(test_ctx->tctx->dom->forest_root);
-@@ -395,14 +395,15 @@ static void test_sysdb_link_forest_member_ad(void **state)
-     ret = sysdb_master_domain_update(test_ctx->tctx->dom);
-     assert_int_equal(ret, EOK);
- 
--    ret = sysdb_update_subdomains(test_ctx->tctx->dom);
-+    ret = sysdb_update_subdomains(test_ctx->tctx->dom, test_ctx->tctx->confdb);
-     assert_int_equal(ret, EOK);
- 
-     /* Also update dom2 */
-     ret = sysdb_master_domain_update(test_ctx->tctx->dom->next);
-     assert_int_equal(ret, EOK);
- 
--    ret = sysdb_update_subdomains(test_ctx->tctx->dom->next);
-+    ret = sysdb_update_subdomains(test_ctx->tctx->dom->next,
-+                                  test_ctx->tctx->confdb);
-     assert_int_equal(ret, EOK);
- 
-     /* Checks */
-@@ -472,7 +473,7 @@ static void test_sysdb_link_ad_multidom(void **state)
-     ret = sysdb_master_domain_update(main_dom1);
-     assert_int_equal(ret, EOK);
- 
--    ret = sysdb_update_subdomains(main_dom1);
-+    ret = sysdb_update_subdomains(main_dom1, NULL);
-     assert_int_equal(ret, EOK);
- 
-     ret = sysdb_master_domain_add_info(main_dom2,
-@@ -492,7 +493,7 @@ static void test_sysdb_link_ad_multidom(void **state)
-     ret = sysdb_master_domain_update(main_dom2);
-     assert_int_equal(ret, EOK);
- 
--    ret = sysdb_update_subdomains(main_dom2);
-+    ret = sysdb_update_subdomains(main_dom2, NULL);
-     assert_int_equal(ret, EOK);
- 
-     main_dom1 = find_domain_by_name(test_ctx->tctx->dom, TEST_DOM1_NAME, true);
-diff --git a/src/tests/sysdb-tests.c b/src/tests/sysdb-tests.c
-index 5bdd631fbfa1b4463fb169e5f07b65fb2c784096..1767dc3c734c6b2e5f74564debd603e2442f491b 100644
---- a/src/tests/sysdb-tests.c
-+++ b/src/tests/sysdb-tests.c
-@@ -1395,7 +1395,7 @@ START_TEST (test_sysdb_get_user_attr_subdomain)
-     /* Create subdomain */
-     subdomain = new_subdomain(test_ctx, test_ctx->domain,
-                               "test.sub", "TEST.SUB", "test", "S-3",
--                              false, false, NULL, NULL, 0);
-+                              false, false, NULL, NULL, 0, NULL);
-     fail_if(subdomain == NULL, "Failed to create new subdomain.");
- 
-     ret = sss_names_init_from_args(test_ctx,
-@@ -5821,14 +5821,14 @@ START_TEST(test_sysdb_subdomain_store_user)
- 
-     subdomain = new_subdomain(test_ctx, test_ctx->domain,
-                               testdom[0], testdom[1], testdom[2], testdom[3],
--                              false, false, NULL, NULL, 0);
-+                              false, false, NULL, NULL, 0, NULL);
-     fail_unless(subdomain != NULL, "Failed to create new subdomin.");
-     ret = sysdb_subdomain_store(test_ctx->sysdb,
-                                 testdom[0], testdom[1], testdom[2], testdom[3],
-                                 false, false, NULL, 0, NULL);
-     fail_if(ret != EOK, "Could not set up the test (test subdom)");
- 
--    ret = sysdb_update_subdomains(test_ctx->domain);
-+    ret = sysdb_update_subdomains(test_ctx->domain, NULL);
-     fail_unless(ret == EOK, "sysdb_update_subdomains failed with [%d][%s]",
-                             ret, strerror(ret));
- 
-@@ -5900,14 +5900,14 @@ START_TEST(test_sysdb_subdomain_user_ops)
- 
-     subdomain = new_subdomain(test_ctx, test_ctx->domain,
-                               testdom[0], testdom[1], testdom[2], testdom[3],
--                              false, false, NULL, NULL, 0);
-+                              false, false, NULL, NULL, 0, NULL);
-     fail_unless(subdomain != NULL, "Failed to create new subdomin.");
-     ret = sysdb_subdomain_store(test_ctx->sysdb,
-                                 testdom[0], testdom[1], testdom[2], testdom[3],
-                                 false, false, NULL, 0, NULL);
-     fail_if(ret != EOK, "Could not set up the test (test subdom)");
- 
--    ret = sysdb_update_subdomains(test_ctx->domain);
-+    ret = sysdb_update_subdomains(test_ctx->domain, NULL);
-     fail_unless(ret == EOK, "sysdb_update_subdomains failed with [%d][%s]",
-                             ret, strerror(ret));
- 
-@@ -5973,14 +5973,14 @@ START_TEST(test_sysdb_subdomain_group_ops)
- 
-     subdomain = new_subdomain(test_ctx, test_ctx->domain,
-                               testdom[0], testdom[1], testdom[2], testdom[3],
--                              false, false, NULL, NULL, 0);
-+                              false, false, NULL, NULL, 0, NULL);
-     fail_unless(subdomain != NULL, "Failed to create new subdomin.");
-     ret = sysdb_subdomain_store(test_ctx->sysdb,
-                                 testdom[0], testdom[1], testdom[2], testdom[3],
-                                 false, false, NULL, 0, NULL);
-     fail_if(ret != EOK, "Could not set up the test (test subdom)");
- 
--    ret = sysdb_update_subdomains(test_ctx->domain);
-+    ret = sysdb_update_subdomains(test_ctx->domain, NULL);
-     fail_unless(ret == EOK, "sysdb_update_subdomains failed with [%d][%s]",
-                             ret, strerror(ret));
- 
-diff --git a/src/tools/common/sss_tools.c b/src/tools/common/sss_tools.c
-index 0f4f46894130daf722641f25a4cdfaae220252cc..97a3caab3bec88c5727eea2f08b200f1d3b23f0c 100644
---- a/src/tools/common/sss_tools.c
-+++ b/src/tools/common/sss_tools.c
-@@ -154,7 +154,7 @@ static errno_t sss_tool_domains_init(TALLOC_CTX *mem_ctx,
-             }
- 
-             /* Update list of subdomains for this domain */
--            ret = sysdb_update_subdomains(dom);
-+            ret = sysdb_update_subdomains(dom, confdb);
-             if (ret != EOK) {
-                 DEBUG(SSSDBG_MINOR_FAILURE,
-                       "Failed to update subdomains for domain %s.\n",
-diff --git a/src/tools/sss_cache.c b/src/tools/sss_cache.c
-index 59e49a8aa92e3a08ec80e0597304f1a4af0a02be..8a40b38c07f7e76cde5b98e5916816581fea7973 100644
---- a/src/tools/sss_cache.c
-+++ b/src/tools/sss_cache.c
-@@ -158,7 +158,7 @@ int main(int argc, const char *argv[])
-             dinfo = get_next_domain(dinfo, SSS_GND_DESCEND)) {
-         if (!IS_SUBDOMAIN(dinfo)) {
-             /* Update list of subdomains for this domain */
--            ret = sysdb_update_subdomains(dinfo);
-+            ret = sysdb_update_subdomains(dinfo, tctx->confdb);
-             if (ret != EOK) {
-                 DEBUG(SSSDBG_MINOR_FAILURE,
-                       "Failed to update subdomains for domain %s.\n", dinfo->name);
--- 
-2.9.3
-
diff --git a/SOURCES/0042-CACHE_REQ-Descend-into-subdomains-on-lookups.patch b/SOURCES/0042-CACHE_REQ-Descend-into-subdomains-on-lookups.patch
deleted file mode 100644
index 583b606..0000000
--- a/SOURCES/0042-CACHE_REQ-Descend-into-subdomains-on-lookups.patch
+++ /dev/null
@@ -1,172 +0,0 @@
-From 73b3b167e86a87263563aa8aac2b45cdf3668765 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Fabiano=20Fid=C3=AAncio?= <fidencio@redhat.com>
-Date: Wed, 1 Mar 2017 08:34:57 +0000
-Subject: [PATCH 42/54] CACHE_REQ: Descend into subdomains on lookups
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Let's make all plugins, but the "host_by_name", to descend into the
-subdomains on lookups.
-
-This patch basically prepares the field for the coming up patches that
-will allow group/user resolution in all domains (or a subset of the
-domains) to be possible by only using the short names without the domain
-component.
-
-The "host_by_name" plugin was not changed as it's a specific IPA plugin
-and won't find anything on its subdomains.
-
-Related:
-https://pagure.io/SSSD/sssd/issue/3001
-
-Signed-off-by: Fabiano Fidêncio <fidencio@redhat.com>
-
-Reviewed-by: Pavel Březina <pbrezina@redhat.com>
-Reviewed-by: Sumit Bose <sbose@redhat.com>
----
- src/responder/common/cache_req/plugins/cache_req_enum_svc.c           | 2 +-
- src/responder/common/cache_req/plugins/cache_req_group_by_filter.c    | 2 +-
- src/responder/common/cache_req/plugins/cache_req_group_by_name.c      | 2 +-
- src/responder/common/cache_req/plugins/cache_req_initgroups_by_name.c | 2 +-
- src/responder/common/cache_req/plugins/cache_req_netgroup_by_name.c   | 2 +-
- src/responder/common/cache_req/plugins/cache_req_object_by_name.c     | 2 +-
- src/responder/common/cache_req/plugins/cache_req_svc_by_name.c        | 2 +-
- src/responder/common/cache_req/plugins/cache_req_svc_by_port.c        | 2 +-
- src/responder/common/cache_req/plugins/cache_req_user_by_filter.c     | 2 +-
- src/responder/common/cache_req/plugins/cache_req_user_by_name.c       | 2 +-
- 10 files changed, 10 insertions(+), 10 deletions(-)
-
-diff --git a/src/responder/common/cache_req/plugins/cache_req_enum_svc.c b/src/responder/common/cache_req/plugins/cache_req_enum_svc.c
-index 2c4917cde750c9063d898c16d3a58ca8c179bc70..28dea33c601f500b9c7af0de3eb9e1c342f03522 100644
---- a/src/responder/common/cache_req/plugins/cache_req_enum_svc.c
-+++ b/src/responder/common/cache_req/plugins/cache_req_enum_svc.c
-@@ -68,7 +68,7 @@ const struct cache_req_plugin cache_req_enum_svc = {
-     .allow_missing_fqn = true,
-     .allow_switch_to_upn = false,
-     .upn_equivalent = CACHE_REQ_SENTINEL,
--    .get_next_domain_flags = 0,
-+    .get_next_domain_flags = SSS_GND_DESCEND,
- 
-     .is_well_known_fn = NULL,
-     .prepare_domain_data_fn = NULL,
-diff --git a/src/responder/common/cache_req/plugins/cache_req_group_by_filter.c b/src/responder/common/cache_req/plugins/cache_req_group_by_filter.c
-index 88e1137a3976308aaf404b684c6d88cc43708bca..6ce6ae0d63967ac50b813a47ac938251619948da 100644
---- a/src/responder/common/cache_req/plugins/cache_req_group_by_filter.c
-+++ b/src/responder/common/cache_req/plugins/cache_req_group_by_filter.c
-@@ -123,7 +123,7 @@ const struct cache_req_plugin cache_req_group_by_filter = {
-     .allow_missing_fqn = false,
-     .allow_switch_to_upn = false,
-     .upn_equivalent = CACHE_REQ_SENTINEL,
--    .get_next_domain_flags = 0,
-+    .get_next_domain_flags = SSS_GND_DESCEND,
- 
-     .is_well_known_fn = NULL,
-     .prepare_domain_data_fn = cache_req_group_by_filter_prepare_domain_data,
-diff --git a/src/responder/common/cache_req/plugins/cache_req_group_by_name.c b/src/responder/common/cache_req/plugins/cache_req_group_by_name.c
-index be1eb9bd8552156d777e934b0be397b0e66df7cc..af6f23ccfd68f952027462ba3e74ed7219d04651 100644
---- a/src/responder/common/cache_req/plugins/cache_req_group_by_name.c
-+++ b/src/responder/common/cache_req/plugins/cache_req_group_by_name.c
-@@ -186,7 +186,7 @@ const struct cache_req_plugin cache_req_group_by_name = {
-     .allow_missing_fqn = false,
-     .allow_switch_to_upn = false,
-     .upn_equivalent = CACHE_REQ_SENTINEL,
--    .get_next_domain_flags = 0,
-+    .get_next_domain_flags = SSS_GND_DESCEND,
- 
-     .is_well_known_fn = NULL,
-     .prepare_domain_data_fn = cache_req_group_by_name_prepare_domain_data,
-diff --git a/src/responder/common/cache_req/plugins/cache_req_initgroups_by_name.c b/src/responder/common/cache_req/plugins/cache_req_initgroups_by_name.c
-index 10fb67cbf6e78cfae33bc7208585cb80ea6a9bc4..307b65a24282838b99c472b50a71f06865aed3f0 100644
---- a/src/responder/common/cache_req/plugins/cache_req_initgroups_by_name.c
-+++ b/src/responder/common/cache_req/plugins/cache_req_initgroups_by_name.c
-@@ -201,7 +201,7 @@ const struct cache_req_plugin cache_req_initgroups_by_name = {
-     .allow_missing_fqn = false,
-     .allow_switch_to_upn = true,
-     .upn_equivalent = CACHE_REQ_INITGROUPS_BY_UPN,
--    .get_next_domain_flags = 0,
-+    .get_next_domain_flags = SSS_GND_DESCEND,
- 
-     .is_well_known_fn = NULL,
-     .prepare_domain_data_fn = cache_req_initgroups_by_name_prepare_domain_data,
-diff --git a/src/responder/common/cache_req/plugins/cache_req_netgroup_by_name.c b/src/responder/common/cache_req/plugins/cache_req_netgroup_by_name.c
-index bc6fc9a8f476f97cc4bc5004bc19ba35258a2b6d..e49d6d84a41ce8dabf18c87373826f8e7b684bda 100644
---- a/src/responder/common/cache_req/plugins/cache_req_netgroup_by_name.c
-+++ b/src/responder/common/cache_req/plugins/cache_req_netgroup_by_name.c
-@@ -120,7 +120,7 @@ const struct cache_req_plugin cache_req_netgroup_by_name = {
-     .allow_missing_fqn = true,
-     .allow_switch_to_upn = false,
-     .upn_equivalent = CACHE_REQ_SENTINEL,
--    .get_next_domain_flags = 0,
-+    .get_next_domain_flags = SSS_GND_DESCEND,
- 
-     .is_well_known_fn = NULL,
-     .prepare_domain_data_fn = cache_req_netgroup_by_name_prepare_domain_data,
-diff --git a/src/responder/common/cache_req/plugins/cache_req_object_by_name.c b/src/responder/common/cache_req/plugins/cache_req_object_by_name.c
-index 2b2caeea172b23b1b1b226def5d926e26c5c0090..74d2b3dea287e890b38e4d5bb176ad2dc6337b7e 100644
---- a/src/responder/common/cache_req/plugins/cache_req_object_by_name.c
-+++ b/src/responder/common/cache_req/plugins/cache_req_object_by_name.c
-@@ -196,7 +196,7 @@ const struct cache_req_plugin cache_req_object_by_name = {
-     .allow_missing_fqn = false,
-     .allow_switch_to_upn = true,
-     .upn_equivalent = CACHE_REQ_USER_BY_UPN,
--    .get_next_domain_flags = 0,
-+    .get_next_domain_flags = SSS_GND_DESCEND,
- 
-     .is_well_known_fn = cache_req_object_by_name_well_known,
-     .prepare_domain_data_fn = cache_req_object_by_name_prepare_domain_data,
-diff --git a/src/responder/common/cache_req/plugins/cache_req_svc_by_name.c b/src/responder/common/cache_req/plugins/cache_req_svc_by_name.c
-index cbb186df04c7ca7c02dceb98bd5700c984285a4d..ef13f097a8ae78ec9db5b7f6e14924b511578b34 100644
---- a/src/responder/common/cache_req/plugins/cache_req_svc_by_name.c
-+++ b/src/responder/common/cache_req/plugins/cache_req_svc_by_name.c
-@@ -144,7 +144,7 @@ const struct cache_req_plugin cache_req_svc_by_name = {
-     .allow_missing_fqn = false,
-     .allow_switch_to_upn = false,
-     .upn_equivalent = CACHE_REQ_SENTINEL,
--    .get_next_domain_flags = 0,
-+    .get_next_domain_flags = SSS_GND_DESCEND,
- 
-     .is_well_known_fn = NULL,
-     .prepare_domain_data_fn = cache_req_svc_by_name_prepare_domain_data,
-diff --git a/src/responder/common/cache_req/plugins/cache_req_svc_by_port.c b/src/responder/common/cache_req/plugins/cache_req_svc_by_port.c
-index 1da23d4505a1dad3b2425a996134f8298c03518a..afa2eeeda12794de26e798aee4b88900bc87ed93 100644
---- a/src/responder/common/cache_req/plugins/cache_req_svc_by_port.c
-+++ b/src/responder/common/cache_req/plugins/cache_req_svc_by_port.c
-@@ -117,7 +117,7 @@ const struct cache_req_plugin cache_req_svc_by_port = {
-     .allow_missing_fqn = false,
-     .allow_switch_to_upn = false,
-     .upn_equivalent = CACHE_REQ_SENTINEL,
--    .get_next_domain_flags = 0,
-+    .get_next_domain_flags = SSS_GND_DESCEND,
- 
-     .is_well_known_fn = NULL,
-     .prepare_domain_data_fn = cache_req_svc_by_port_prepare_domain_data,
-diff --git a/src/responder/common/cache_req/plugins/cache_req_user_by_filter.c b/src/responder/common/cache_req/plugins/cache_req_user_by_filter.c
-index ee7e69399e318b9835f1623bddc635bf09aa7a1c..eb71b42dad3a805298df0c8425409d571befb31b 100644
---- a/src/responder/common/cache_req/plugins/cache_req_user_by_filter.c
-+++ b/src/responder/common/cache_req/plugins/cache_req_user_by_filter.c
-@@ -123,7 +123,7 @@ const struct cache_req_plugin cache_req_user_by_filter = {
-     .allow_missing_fqn = false,
-     .allow_switch_to_upn = false,
-     .upn_equivalent = CACHE_REQ_SENTINEL,
--    .get_next_domain_flags = 0,
-+    .get_next_domain_flags = SSS_GND_DESCEND,
- 
-     .is_well_known_fn = NULL,
-     .prepare_domain_data_fn = cache_req_user_by_filter_prepare_domain_data,
-diff --git a/src/responder/common/cache_req/plugins/cache_req_user_by_name.c b/src/responder/common/cache_req/plugins/cache_req_user_by_name.c
-index 4289f5fd4c79f0e512f0249abe4422589fa800a0..0670febdce2d51e0373045570dd07f56255db7bc 100644
---- a/src/responder/common/cache_req/plugins/cache_req_user_by_name.c
-+++ b/src/responder/common/cache_req/plugins/cache_req_user_by_name.c
-@@ -191,7 +191,7 @@ const struct cache_req_plugin cache_req_user_by_name = {
-     .allow_missing_fqn = false,
-     .allow_switch_to_upn = true,
-     .upn_equivalent = CACHE_REQ_USER_BY_UPN,
--    .get_next_domain_flags = 0,
-+    .get_next_domain_flags = SSS_GND_DESCEND,
- 
-     .is_well_known_fn = NULL,
-     .prepare_domain_data_fn = cache_req_user_by_name_prepare_domain_data,
--- 
-2.9.3
-
diff --git a/SOURCES/0042-p11_child-add-descriptions-for-error-codes-to-debug-.patch b/SOURCES/0042-p11_child-add-descriptions-for-error-codes-to-debug-.patch
new file mode 100644
index 0000000..37350b5
--- /dev/null
+++ b/SOURCES/0042-p11_child-add-descriptions-for-error-codes-to-debug-.patch
@@ -0,0 +1,276 @@
+From aa476a78b67a60d4ca2433091268a7790b4d62f7 Mon Sep 17 00:00:00 2001
+From: Sumit Bose <sbose@redhat.com>
+Date: Mon, 30 Oct 2017 10:22:33 +0100
+Subject: [PATCH 42/46] p11_child: add descriptions for error codes to debug
+ messages
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Additionally to the NSS erro code a text message describing the error is
+added. This will help to see why p11_child ignores specific
+certificates. For example it would be more obvious why the certificate
+is not valid (expired, missing CA cert, failed OCSP etc).
+
+Related to https://pagure.io/SSSD/sssd/issue/3560
+
+Reviewed-by: Fabiano Fidêncio <fidencio@redhat.com>
+Tested-by: Scott Poore <spoore@redhat.com>
+(cherry picked from commit 08d1f8c0d6eece6a48201d7f8824b282eac3458d)
+---
+ src/p11_child/p11_child_nss.c | 91 ++++++++++++++++++++++++-------------------
+ 1 file changed, 50 insertions(+), 41 deletions(-)
+
+diff --git a/src/p11_child/p11_child_nss.c b/src/p11_child/p11_child_nss.c
+index c676375cf7f6677a1d7f38f09b9bb5fd820d60c5..5f289688e41f4ea610292b907036e05cf95eb29d 100644
+--- a/src/p11_child/p11_child_nss.c
++++ b/src/p11_child/p11_child_nss.c
+@@ -75,15 +75,16 @@ static char *get_key_id_str(PK11SlotInfo *slot, CERTCertificate *cert)
+     key_id = PK11_GetLowLevelKeyIDForCert(slot, cert, NULL);
+     if (key_id == NULL) {
+         DEBUG(SSSDBG_OP_FAILURE,
+-              "PK11_GetLowLevelKeyIDForCert failed [%d].\n",
+-              PR_GetError());
++              "PK11_GetLowLevelKeyIDForCert failed [%d][%s].\n",
++              PR_GetError(), PORT_ErrorToString(PR_GetError()));
+         return NULL;
+     }
+ 
+     key_id_str = CERT_Hexify(key_id, PR_FALSE);
+     SECITEM_FreeItem(key_id, PR_TRUE);
+     if (key_id_str == NULL) {
+-        DEBUG(SSSDBG_OP_FAILURE, "CERT_Hexify failed [%d].\n", PR_GetError());
++        DEBUG(SSSDBG_OP_FAILURE, "CERT_Hexify failed [%d][%s].\n",
++              PR_GetError(), PORT_ErrorToString(PR_GetError()));
+         return NULL;
+     }
+ 
+@@ -138,8 +139,8 @@ int do_work(TALLOC_CTX *mem_ctx, const char *nss_db,
+ 
+     nss_ctx = NSS_InitContext(nss_db, "", "", SECMOD_DB, &parameters, flags);
+     if (nss_ctx == NULL) {
+-        DEBUG(SSSDBG_OP_FAILURE, "NSS_InitContext failed [%d].\n",
+-                                 PR_GetError());
++        DEBUG(SSSDBG_OP_FAILURE, "NSS_InitContext failed [%d][%s].\n",
++              PR_GetError(), PORT_ErrorToString(PR_GetError()));
+         return EIO;
+     }
+ 
+@@ -232,8 +233,8 @@ int do_work(TALLOC_CTX *mem_ctx, const char *nss_db,
+         if (pin != NULL) {
+             rv = PK11_Authenticate(slot, PR_FALSE, discard_const(pin));
+             if (rv !=  SECSuccess) {
+-                DEBUG(SSSDBG_OP_FAILURE, "PK11_Authenticate failed: [%d].\n",
+-                                         PR_GetError());
++                DEBUG(SSSDBG_OP_FAILURE, "PK11_Authenticate failed: [%d][%s].\n",
++                      PR_GetError(), PORT_ErrorToString(PR_GetError()));
+                 return EIO;
+             }
+         } else {
+@@ -246,8 +247,8 @@ int do_work(TALLOC_CTX *mem_ctx, const char *nss_db,
+ 
+     cert_list = PK11_ListCertsInSlot(slot);
+     if (cert_list == NULL) {
+-        DEBUG(SSSDBG_OP_FAILURE, "PK11_ListCertsInSlot failed: [%d].\n",
+-                                 PR_GetError());
++        DEBUG(SSSDBG_OP_FAILURE, "PK11_ListCertsInSlot failed: [%d][%s].\n",
++              PR_GetError(), PORT_ErrorToString(PR_GetError()));
+         return EIO;
+     }
+ 
+@@ -265,31 +266,33 @@ int do_work(TALLOC_CTX *mem_ctx, const char *nss_db,
+ 
+     rv = CERT_FilterCertListByUsage(cert_list, certUsageSSLClient, PR_FALSE);
+     if (rv != SECSuccess) {
+-        DEBUG(SSSDBG_OP_FAILURE, "CERT_FilterCertListByUsage failed: [%d].\n",
+-                                 PR_GetError());
++        DEBUG(SSSDBG_OP_FAILURE, "CERT_FilterCertListByUsage failed: [%d][%s].\n",
++              PR_GetError(), PORT_ErrorToString(PR_GetError()));
+         return EIO;
+     }
+ 
+     rv = CERT_FilterCertListForUserCerts(cert_list);
+     if (rv != SECSuccess) {
+-        DEBUG(SSSDBG_OP_FAILURE, "CERT_FilterCertListForUserCerts failed: [%d].\n",
+-                                 PR_GetError());
++        DEBUG(SSSDBG_OP_FAILURE,
++              "CERT_FilterCertListForUserCerts failed: [%d][%s].\n",
++              PR_GetError(), PORT_ErrorToString(PR_GetError()));
+         return EIO;
+     }
+ 
+ 
+     handle = CERT_GetDefaultCertDB();
+     if (handle == NULL) {
+-        DEBUG(SSSDBG_OP_FAILURE, "CERT_GetDefaultCertDB failed: [%d].\n",
+-                                 PR_GetError());
++        DEBUG(SSSDBG_OP_FAILURE, "CERT_GetDefaultCertDB failed: [%d][%s].\n",
++              PR_GetError(), PORT_ErrorToString(PR_GetError()));
+         return EIO;
+     }
+ 
+     if (cert_verify_opts->do_ocsp) {
+         rv = CERT_EnableOCSPChecking(handle);
+         if (rv != SECSuccess) {
+-            DEBUG(SSSDBG_OP_FAILURE, "CERT_EnableOCSPChecking failed: [%d].\n",
+-                                     PR_GetError());
++            DEBUG(SSSDBG_OP_FAILURE,
++                  "CERT_EnableOCSPChecking failed: [%d][%s].\n",
++                  PR_GetError(), PORT_ErrorToString(PR_GetError()));
+             return EIO;
+         }
+ 
+@@ -300,16 +303,16 @@ int do_work(TALLOC_CTX *mem_ctx, const char *nss_db,
+                          cert_verify_opts->ocsp_default_responder_signing_cert);
+             if (rv != SECSuccess) {
+                 DEBUG(SSSDBG_OP_FAILURE,
+-                      "CERT_SetOCSPDefaultResponder failed: [%d].\n",
+-                      PR_GetError());
++                      "CERT_SetOCSPDefaultResponder failed: [%d][%s].\n",
++                      PR_GetError(), PORT_ErrorToString(PR_GetError()));
+                 return EIO;
+             }
+ 
+             rv = CERT_EnableOCSPDefaultResponder(handle);
+             if (rv != SECSuccess) {
+                 DEBUG(SSSDBG_OP_FAILURE,
+-                      "CERT_EnableOCSPDefaultResponder failed: [%d].\n",
+-                      PR_GetError());
++                      "CERT_EnableOCSPDefaultResponder failed: [%d][%s].\n",
++                      PR_GetError(), PORT_ErrorToString(PR_GetError()));
+                 return EIO;
+             }
+         }
+@@ -318,8 +321,8 @@ int do_work(TALLOC_CTX *mem_ctx, const char *nss_db,
+     found_cert = NULL;
+     valid_certs = CERT_NewCertList();
+     if (valid_certs == NULL) {
+-        DEBUG(SSSDBG_OP_FAILURE, "CERT_NewCertList failed [%d].\n",
+-                                 PR_GetError());
++        DEBUG(SSSDBG_OP_FAILURE, "CERT_NewCertList failed [%d][%s].\n",
++              PR_GetError(), PORT_ErrorToString(PR_GetError()));
+         ret = ENOMEM;
+         goto done;
+     }
+@@ -345,9 +348,10 @@ int do_work(TALLOC_CTX *mem_ctx, const char *nss_db,
+                                            NULL, NULL);
+             if (rv != SECSuccess) {
+                 DEBUG(SSSDBG_OP_FAILURE,
+-                      "Certificate [%s][%s] not valid [%d], skipping.\n",
++                      "Certificate [%s][%s] not valid [%d][%s], skipping.\n",
+                       cert_list_node->cert->nickname,
+-                      cert_list_node->cert->subjectName, PR_GetError());
++                      cert_list_node->cert->subjectName,
++                      PR_GetError(), PORT_ErrorToString(PR_GetError()));
+                 continue;
+             }
+         }
+@@ -386,7 +390,8 @@ int do_work(TALLOC_CTX *mem_ctx, const char *nss_db,
+             rv = CERT_AddCertToListTail(valid_certs, cert_list_node->cert);
+             if (rv != SECSuccess) {
+                 DEBUG(SSSDBG_OP_FAILURE,
+-                      "CERT_AddCertToListTail failed [%d].\n", PR_GetError());
++                      "CERT_AddCertToListTail failed [%d][%s].\n",
++                      PR_GetError(), PORT_ErrorToString(PR_GetError()));
+                 ret = EIO;
+                 goto done;
+             }
+@@ -400,8 +405,8 @@ int do_work(TALLOC_CTX *mem_ctx, const char *nss_db,
+         rv = CERT_DisableOCSPDefaultResponder(handle);
+         if (rv != SECSuccess) {
+             DEBUG(SSSDBG_OP_FAILURE,
+-                  "CERT_DisableOCSPDefaultResponder failed: [%d].\n",
+-                  PR_GetError());
++                  "CERT_DisableOCSPDefaultResponder failed: [%d][%s].\n",
++                  PR_GetError(), PORT_ErrorToString(PR_GetError()));
+         }
+     }
+ 
+@@ -433,15 +438,17 @@ int do_work(TALLOC_CTX *mem_ctx, const char *nss_db,
+         rv = PK11_GenerateRandom(random_value, sizeof(random_value));
+         if (rv != SECSuccess) {
+             DEBUG(SSSDBG_OP_FAILURE,
+-                  "PK11_GenerateRandom failed [%d].\n", PR_GetError());
++                  "PK11_GenerateRandom failed [%d][%s].\n",
++                  PR_GetError(), PORT_ErrorToString(PR_GetError()));
+             return EIO;
+         }
+ 
+         priv_key = PK11_FindPrivateKeyFromCert(slot, found_cert, NULL);
+         if (priv_key == NULL) {
+             DEBUG(SSSDBG_OP_FAILURE,
+-                  "PK11_FindPrivateKeyFromCert failed [%d]." \
+-                  "Maybe pin is missing.\n", PR_GetError());
++                  "PK11_FindPrivateKeyFromCert failed [%d][%s]."
++                  "Maybe pin is missing.\n",
++                  PR_GetError(), PORT_ErrorToString(PR_GetError()));
+             ret = EIO;
+             goto done;
+         }
+@@ -451,8 +458,8 @@ int do_work(TALLOC_CTX *mem_ctx, const char *nss_db,
+         if (algtag == SEC_OID_UNKNOWN) {
+             SECKEY_DestroyPrivateKey(priv_key);
+             DEBUG(SSSDBG_OP_FAILURE,
+-                  "SEC_GetSignatureAlgorithmOidTag failed [%d].\n",
+-                  PR_GetError());
++                  "SEC_GetSignatureAlgorithmOidTag failed [%d][%s].\n",
++                  PR_GetError(), PORT_ErrorToString(PR_GetError()));
+             ret = EIO;
+             goto done;
+         }
+@@ -462,8 +469,8 @@ int do_work(TALLOC_CTX *mem_ctx, const char *nss_db,
+                           priv_key, algtag);
+         SECKEY_DestroyPrivateKey(priv_key);
+         if (rv != SECSuccess) {
+-            DEBUG(SSSDBG_OP_FAILURE, "SEC_SignData failed [%d].\n",
+-                                     PR_GetError());
++            DEBUG(SSSDBG_OP_FAILURE, "SEC_SignData failed [%d][%s].\n",
++                  PR_GetError(), PORT_ErrorToString(PR_GetError()));
+             ret = EIO;
+             goto done;
+         }
+@@ -471,7 +478,8 @@ int do_work(TALLOC_CTX *mem_ctx, const char *nss_db,
+         pub_key = CERT_ExtractPublicKey(found_cert);
+         if (pub_key == NULL) {
+             DEBUG(SSSDBG_OP_FAILURE,
+-                  "CERT_ExtractPublicKey failed [%d].\n", PR_GetError());
++                  "CERT_ExtractPublicKey failed [%d][%s].\n",
++                  PR_GetError(), PORT_ErrorToString(PR_GetError()));
+             ret = EIO;
+             goto done;
+         }
+@@ -481,8 +489,8 @@ int do_work(TALLOC_CTX *mem_ctx, const char *nss_db,
+                             NULL);
+         SECKEY_DestroyPublicKey(pub_key);
+         if (rv != SECSuccess) {
+-            DEBUG(SSSDBG_OP_FAILURE, "VFY_VerifyData failed [%d].\n",
+-                                     PR_GetError());
++            DEBUG(SSSDBG_OP_FAILURE, "VFY_VerifyData failed [%d][%s].\n",
++                  PR_GetError(), PORT_ErrorToString(PR_GetError()));
+             ret = EACCES;
+             goto done;
+         }
+@@ -507,7 +515,8 @@ int do_work(TALLOC_CTX *mem_ctx, const char *nss_db,
+         PORT_Free(key_id_str);
+         key_id_str = get_key_id_str(slot, found_cert);
+         if (key_id_str == NULL) {
+-            DEBUG(SSSDBG_OP_FAILURE, "get_key_id_str [%d].\n", PR_GetError());
++            DEBUG(SSSDBG_OP_FAILURE, "get_key_id_str [%d][%s].\n",
++                  PR_GetError(), PORT_ErrorToString(PR_GetError()));
+             ret = ENOMEM;
+             goto done;
+         }
+@@ -562,8 +571,8 @@ done:
+ 
+     rv = NSS_ShutdownContext(nss_ctx);
+     if (rv != SECSuccess) {
+-        DEBUG(SSSDBG_OP_FAILURE, "NSS_ShutdownContext failed [%d].\n",
+-                                 PR_GetError());
++        DEBUG(SSSDBG_OP_FAILURE, "NSS_ShutdownContext failed [%d][%s].\n",
++              PR_GetError(), PORT_ErrorToString(PR_GetError()));
+     }
+ 
+     return ret;
+-- 
+2.13.6
+
diff --git a/SOURCES/0043-NSS-TESTS-Fix-subdomains-attribution.patch b/SOURCES/0043-NSS-TESTS-Fix-subdomains-attribution.patch
deleted file mode 100644
index e142203..0000000
--- a/SOURCES/0043-NSS-TESTS-Fix-subdomains-attribution.patch
+++ /dev/null
@@ -1,35 +0,0 @@
-From 5e3ea50bba1fee75cc0788bcd77ae4991828608e Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Pavel=20B=C5=99ezina?= <pbrezina@redhat.com>
-Date: Wed, 1 Mar 2017 13:21:19 +0000
-Subject: [PATCH 43/54] NSS/TESTS: Fix subdomains attribution
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Related:
-https://pagure.io/SSSD/sssd/issue/3001
-
-Reviewed-by: Fabiano Fidêncio <fidencio@redhat.com>
-
-Reviewed-by: Sumit Bose <sbose@redhat.com>
-Reviewed-by: Pavel Březina <pbrezina@redhat.com>
----
- src/tests/cmocka/test_nss_srv.c | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
-diff --git a/src/tests/cmocka/test_nss_srv.c b/src/tests/cmocka/test_nss_srv.c
-index 3d7e0382197401cb2264671712152fe0709296b6..cbe0dccdc1d883eae1a9621f12997ef43d05178e 100644
---- a/src/tests/cmocka/test_nss_srv.c
-+++ b/src/tests/cmocka/test_nss_srv.c
-@@ -3219,7 +3219,7 @@ static int nss_subdom_test_setup(void **state)
-                                   nss_test_ctx->tctx->confdb);
-     assert_int_equal(ret, EOK);
- 
--    nss_test_ctx->subdom = subdomain;
-+    nss_test_ctx->subdom = nss_test_ctx->tctx->dom->subdomains;
-     return 0;
- }
- 
--- 
-2.9.3
-
diff --git a/SOURCES/0043-pam-filter-certificates-in-the-responder-not-in-the-.patch b/SOURCES/0043-pam-filter-certificates-in-the-responder-not-in-the-.patch
new file mode 100644
index 0000000..ac52d99
--- /dev/null
+++ b/SOURCES/0043-pam-filter-certificates-in-the-responder-not-in-the-.patch
@@ -0,0 +1,357 @@
+From 69c820abacd963a3699fc9ea84a17bb99f9eaf3a Mon Sep 17 00:00:00 2001
+From: Sumit Bose <sbose@redhat.com>
+Date: Mon, 6 Nov 2017 15:26:38 +0100
+Subject: [PATCH 43/46] pam: filter certificates in the responder not in the
+ child
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+With the new selection option and the handling of multiple certificates
+in the PAM responder it is not needed anymore to filter the certificates
+in p11_child but the matching rules can be applied by the PAM responder
+directly.
+
+Related to https://pagure.io/SSSD/sssd/issue/3560
+
+Reviewed-by: Fabiano Fidêncio <fidencio@redhat.com>
+Tested-by: Scott Poore <spoore@redhat.com>
+(cherry picked from commit 177ab84f0e336b75289a3ac0b2df25bd5ab5198b)
+---
+ src/p11_child/p11_child_nss.c   |  18 +-----
+ src/responder/pam/pamsrv.h      |   6 ++
+ src/responder/pam/pamsrv_cmd.c  |  10 ++-
+ src/responder/pam/pamsrv_p11.c  | 135 +++++++++++++++++++++++++++++++++++++++-
+ src/tests/cmocka/test_pam_srv.c |   3 +
+ 5 files changed, 152 insertions(+), 20 deletions(-)
+
+diff --git a/src/p11_child/p11_child_nss.c b/src/p11_child/p11_child_nss.c
+index 5f289688e41f4ea610292b907036e05cf95eb29d..e59aba0d1561f58206252f7251ecd88315836b1b 100644
+--- a/src/p11_child/p11_child_nss.c
++++ b/src/p11_child/p11_child_nss.c
+@@ -264,22 +264,6 @@ int do_work(TALLOC_CTX *mem_ctx, const char *nss_db,
+         }
+     }
+ 
+-    rv = CERT_FilterCertListByUsage(cert_list, certUsageSSLClient, PR_FALSE);
+-    if (rv != SECSuccess) {
+-        DEBUG(SSSDBG_OP_FAILURE, "CERT_FilterCertListByUsage failed: [%d][%s].\n",
+-              PR_GetError(), PORT_ErrorToString(PR_GetError()));
+-        return EIO;
+-    }
+-
+-    rv = CERT_FilterCertListForUserCerts(cert_list);
+-    if (rv != SECSuccess) {
+-        DEBUG(SSSDBG_OP_FAILURE,
+-              "CERT_FilterCertListForUserCerts failed: [%d][%s].\n",
+-              PR_GetError(), PORT_ErrorToString(PR_GetError()));
+-        return EIO;
+-    }
+-
+-
+     handle = CERT_GetDefaultCertDB();
+     if (handle == NULL) {
+         DEBUG(SSSDBG_OP_FAILURE, "CERT_GetDefaultCertDB failed: [%d][%s].\n",
+@@ -344,7 +328,7 @@ int do_work(TALLOC_CTX *mem_ctx, const char *nss_db,
+         if (cert_verify_opts->do_verification) {
+             rv = CERT_VerifyCertificateNow(handle, cert_list_node->cert,
+                                            PR_TRUE,
+-                                           certificateUsageSSLClient,
++                                           certificateUsageCheckAllUsages,
+                                            NULL, NULL);
+             if (rv != SECSuccess) {
+                 DEBUG(SSSDBG_OP_FAILURE,
+diff --git a/src/responder/pam/pamsrv.h b/src/responder/pam/pamsrv.h
+index f15f7f19f1f38626288416c9f2038371c6f58b47..0bc229212844602ed461d1c7db48bf51ac2e2194 100644
+--- a/src/responder/pam/pamsrv.h
++++ b/src/responder/pam/pamsrv.h
+@@ -27,6 +27,7 @@
+ #include "sbus/sssd_dbus.h"
+ #include "responder/common/responder.h"
+ #include "responder/common/cache_req/cache_req.h"
++#include "lib/certmap/sss_certmap.h"
+ 
+ struct pam_auth_req;
+ 
+@@ -49,6 +50,7 @@ struct pam_ctx {
+     bool cert_auth;
+     int p11_child_debug_fd;
+     char *nss_db;
++    struct sss_certmap_ctx *sss_certmap_ctx;
+ };
+ 
+ struct pam_auth_dp_req {
+@@ -104,6 +106,7 @@ struct tevent_req *pam_check_cert_send(TALLOC_CTX *mem_ctx,
+                                        const char *nss_db,
+                                        time_t timeout,
+                                        const char *verify_opts,
++                                       struct sss_certmap_ctx *sss_certmap_ctx,
+                                        struct pam_data *pd);
+ errno_t pam_check_cert_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
+                             struct cert_auth_info **cert_list);
+@@ -114,6 +117,9 @@ errno_t add_pam_cert_response(struct pam_data *pd, const char *sysdb_username,
+ 
+ bool may_do_cert_auth(struct pam_ctx *pctx, struct pam_data *pd);
+ 
++errno_t p11_refresh_certmap_ctx(struct pam_ctx *pctx,
++                                struct certmap_info **certmap_list);
++
+ errno_t
+ pam_set_last_online_auth_with_curr_token(struct sss_domain_info *domain,
+                                          const char *username,
+diff --git a/src/responder/pam/pamsrv_cmd.c b/src/responder/pam/pamsrv_cmd.c
+index caf6c99489b8378d2e850473191223709920cd79..0e76c9e772f1775635677f35b870e9613b2faf64 100644
+--- a/src/responder/pam/pamsrv_cmd.c
++++ b/src/responder/pam/pamsrv_cmd.c
+@@ -1336,7 +1336,8 @@ static errno_t check_cert(TALLOC_CTX *mctx,
+ 
+     req = pam_check_cert_send(mctx, ev, pctx->p11_child_debug_fd,
+                               pctx->nss_db, p11_child_timeout,
+-                              cert_verification_opts, pd);
++                              cert_verification_opts, pctx->sss_certmap_ctx,
++                              pd);
+     if (req == NULL) {
+         DEBUG(SSSDBG_OP_FAILURE, "pam_check_cert_send failed.\n");
+         return ENOMEM;
+@@ -1749,6 +1750,13 @@ static void pam_forwarder_cb(struct tevent_req *req)
+         goto done;
+     }
+ 
++    ret = p11_refresh_certmap_ctx(pctx, pctx->rctx->domains->certmaps);
++    if (ret != EOK) {
++        DEBUG(SSSDBG_OP_FAILURE,
++              "p11_refresh_certmap_ctx failed, "
++              "certificate matching might not work as expected");
++    }
++
+     pd = preq->pd;
+ 
+     ret = pam_forwarder_parse_data(cctx, pd);
+diff --git a/src/responder/pam/pamsrv_p11.c b/src/responder/pam/pamsrv_p11.c
+index 5a3eeff0ec977829a9ad8c80b4fc6b2e06857097..ec52c5ae7163d41144fe082643a201b766a1e201 100644
+--- a/src/responder/pam/pamsrv_p11.c
++++ b/src/responder/pam/pamsrv_p11.c
+@@ -36,6 +36,7 @@
+ 
+ #define P11_CHILD_LOG_FILE "p11_child"
+ #define P11_CHILD_PATH SSSD_LIBEXEC_PATH"/p11_child"
++#define CERT_AUTH_DEFAULT_MATCHING_RULE "KRB5:<EKU>clientAuth"
+ 
+ struct cert_auth_info {
+     char *cert;
+@@ -116,8 +117,110 @@ void sss_cai_check_users(struct cert_auth_info **list, size_t *_cert_count,
+     return;
+ }
+ 
++struct priv_sss_debug {
++    int level;
++};
++
++static void ext_debug(void *private, const char *file, long line,
++                      const char *function, const char *format, ...)
++{
++    va_list ap;
++    struct priv_sss_debug *data = private;
++    int level = SSSDBG_OP_FAILURE;
++
++    if (data != NULL) {
++        level = data->level;
++    }
++
++    if (DEBUG_IS_SET(level)) {
++        va_start(ap, format);
++        sss_vdebug_fn(file, line, function, level, APPEND_LINE_FEED,
++                      format, ap);
++        va_end(ap);
++    }
++}
++
++errno_t p11_refresh_certmap_ctx(struct pam_ctx *pctx,
++                                struct certmap_info **certmap_list)
++{
++    int ret;
++    struct sss_certmap_ctx *sss_certmap_ctx = NULL;
++    size_t c;
++
++    ret = sss_certmap_init(pctx, ext_debug, NULL, &sss_certmap_ctx);
++    if (ret != EOK) {
++        DEBUG(SSSDBG_OP_FAILURE, "sss_certmap_init failed.\n");
++        goto done;
++    }
++
++    if (certmap_list == NULL || *certmap_list == NULL) {
++        /* Try to add default matching rule */
++        ret = sss_certmap_add_rule(sss_certmap_ctx, SSS_CERTMAP_MIN_PRIO,
++                                   CERT_AUTH_DEFAULT_MATCHING_RULE, NULL, NULL);
++        if (ret != 0) {
++            DEBUG(SSSDBG_CRIT_FAILURE,
++                  "Failed to add default matching rule.\n");
++        }
++
++        goto done;
++    }
++
++    for (c = 0; certmap_list[c] != NULL; c++) {
++        DEBUG(SSSDBG_TRACE_ALL,
++              "Trying to add rule [%s][%d][%s][%s].\n",
++              certmap_list[c]->name, certmap_list[c]->priority,
++              certmap_list[c]->match_rule, certmap_list[c]->map_rule);
++
++        ret = sss_certmap_add_rule(sss_certmap_ctx, certmap_list[c]->priority,
++                                   certmap_list[c]->match_rule,
++                                   certmap_list[c]->map_rule,
++                                   certmap_list[c]->domains);
++        if (ret != 0) {
++            DEBUG(SSSDBG_CRIT_FAILURE,
++                  "sss_certmap_add_rule failed for rule [%s] "
++                  "with error [%d][%s], skipping. "
++                  "Please check for typos and if rule syntax is supported.\n",
++                  certmap_list[c]->name, ret, sss_strerror(ret));
++            continue;
++        }
++    }
++
++    ret = EOK;
++
++done:
++    if (ret == EOK) {
++        sss_certmap_free_ctx(pctx->sss_certmap_ctx);
++        pctx->sss_certmap_ctx = sss_certmap_ctx;
++    } else {
++        sss_certmap_free_ctx(sss_certmap_ctx);
++    }
++
++    return ret;
++}
++
+ errno_t p11_child_init(struct pam_ctx *pctx)
+ {
++    int ret;
++    struct certmap_info **certmaps;
++    bool user_name_hint;
++    struct sss_domain_info *dom = pctx->rctx->domains;
++
++    ret = sysdb_get_certmap(dom, dom->sysdb, &certmaps, &user_name_hint);
++    if (ret != EOK) {
++        DEBUG(SSSDBG_OP_FAILURE, "sysdb_get_certmap failed.\n");
++        return ret;
++    }
++
++    dom->user_name_hint = user_name_hint;
++    talloc_free(dom->certmaps);
++    dom->certmaps = certmaps;
++
++    ret = p11_refresh_certmap_ctx(pctx, dom->certmaps);
++    if (ret != EOK) {
++        DEBUG(SSSDBG_OP_FAILURE, "p11_refresh_certmap_ctx failed.\n");
++        return ret;
++    }
++
+     return child_debug_init(P11_CHILD_LOG_FILE, &pctx->p11_child_debug_fd);
+ }
+ 
+@@ -214,6 +317,7 @@ static errno_t get_p11_child_write_buffer(TALLOC_CTX *mem_ctx,
+ 
+ static errno_t parse_p11_child_response(TALLOC_CTX *mem_ctx, uint8_t *buf,
+                                         ssize_t buf_len,
++                                        struct sss_certmap_ctx *sss_certmap_ctx,
+                                         struct cert_auth_info **_cert_list)
+ {
+     int ret;
+@@ -222,6 +326,8 @@ static errno_t parse_p11_child_response(TALLOC_CTX *mem_ctx, uint8_t *buf,
+     uint8_t *pn;
+     struct cert_auth_info *cert_list = NULL;
+     struct cert_auth_info *cert_auth_info;
++    unsigned char *der = NULL;
++    size_t der_size;
+ 
+     if (buf_len < 0) {
+         DEBUG(SSSDBG_CRIT_FAILURE,
+@@ -347,7 +453,22 @@ static errno_t parse_p11_child_response(TALLOC_CTX *mem_ctx, uint8_t *buf,
+         }
+         DEBUG(SSSDBG_TRACE_ALL, "Found cert [%s].\n", cert_auth_info->cert);
+ 
+-        DLIST_ADD(cert_list, cert_auth_info);
++        der = sss_base64_decode(tmp_ctx, cert_auth_info->cert, &der_size);
++        if (der == NULL) {
++            DEBUG(SSSDBG_OP_FAILURE, "sss_base64_decode failed.\n");
++            ret = EIO;
++            goto done;
++        }
++
++        ret = sss_certmap_match_cert(sss_certmap_ctx, der, der_size);
++        if (ret == 0) {
++            DLIST_ADD(cert_list, cert_auth_info);
++        } else {
++            DEBUG(SSSDBG_TRACE_LIBS,
++                  "Cert [%s] does not match matching rules and is ignored.\n",
++                  cert_auth_info->cert);
++            talloc_free(cert_auth_info);
++        }
+ 
+         p = ++pn;
+     } while ((pn - buf) < buf_len);
+@@ -373,6 +494,7 @@ struct pam_check_cert_state {
+     struct sss_child_ctx_old *child_ctx;
+     struct tevent_timer *timeout_handler;
+     struct tevent_context *ev;
++    struct sss_certmap_ctx *sss_certmap_ctx;
+ 
+     struct child_io_fds *io;
+ 
+@@ -391,6 +513,7 @@ struct tevent_req *pam_check_cert_send(TALLOC_CTX *mem_ctx,
+                                        const char *nss_db,
+                                        time_t timeout,
+                                        const char *verify_opts,
++                                       struct sss_certmap_ctx *sss_certmap_ctx,
+                                        struct pam_data *pd)
+ {
+     errno_t ret;
+@@ -420,6 +543,12 @@ struct tevent_req *pam_check_cert_send(TALLOC_CTX *mem_ctx,
+         goto done;
+     }
+ 
++    if (sss_certmap_ctx == NULL) {
++        DEBUG(SSSDBG_CRIT_FAILURE, "Missing certificate matching context.\n");
++        ret = EINVAL;
++        goto done;
++    }
++
+     /* extra_args are added in revers order */
+     arg_c = 0;
+     extra_args[arg_c++] = nss_db;
+@@ -476,6 +605,7 @@ struct tevent_req *pam_check_cert_send(TALLOC_CTX *mem_ctx,
+     }
+ 
+     state->ev = ev;
++    state->sss_certmap_ctx = sss_certmap_ctx;
+     state->child_status = EFAULT;
+     state->io = talloc(state, struct child_io_fds);
+     if (state->io == NULL) {
+@@ -639,7 +769,8 @@ static void p11_child_done(struct tevent_req *subreq)
+ 
+     PIPE_FD_CLOSE(state->io->read_from_child_fd);
+ 
+-    ret = parse_p11_child_response(state, buf, buf_len, &state->cert_list);
++    ret = parse_p11_child_response(state, buf, buf_len, state->sss_certmap_ctx,
++                                   &state->cert_list);
+     if (ret != EOK) {
+         DEBUG(SSSDBG_OP_FAILURE, "parse_p11_child_response failed.\n");
+         tevent_req_error(req, ret);
+diff --git a/src/tests/cmocka/test_pam_srv.c b/src/tests/cmocka/test_pam_srv.c
+index b6845320ca41d6933280aa2836a3d984dacfcc5e..bccf9972dacbb414076904a783772198620fd73c 100644
+--- a/src/tests/cmocka/test_pam_srv.c
++++ b/src/tests/cmocka/test_pam_srv.c
+@@ -287,6 +287,9 @@ struct pam_ctx *mock_pctx(TALLOC_CTX *mem_ctx)
+         return NULL;
+     }
+ 
++    ret = p11_refresh_certmap_ctx(pctx, NULL);
++    assert_int_equal(ret, 0);
++
+     return pctx;
+ }
+ 
+-- 
+2.13.6
+
diff --git a/SOURCES/0044-NSS-TESTS-Improve-setup-teardown-for-subdomains-test.patch b/SOURCES/0044-NSS-TESTS-Improve-setup-teardown-for-subdomains-test.patch
deleted file mode 100644
index 9eb42a4..0000000
--- a/SOURCES/0044-NSS-TESTS-Improve-setup-teardown-for-subdomains-test.patch
+++ /dev/null
@@ -1,274 +0,0 @@
-From f318eff5277d783972ef0d585ff05c473db44714 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Fabiano=20Fid=C3=AAncio?= <fidencio@redhat.com>
-Date: Wed, 1 Mar 2017 20:46:10 +0000
-Subject: [PATCH 44/54] NSS/TESTS: Improve setup/teardown for subdomains tests
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-This patch basically makes the getgrnam_members_subdom(),
-getgrnam_mix_dom(), getgrnam_mix_dom_fqdn() and getgrnam_mix_subdom()
-more independent of each other.
-
-Related:
-https://pagure.io/SSSD/sssd/issue/3001
-
-Signed-off-by: Fabiano Fidêncio <fidencio@redhat.com>
-
-Reviewed-by: Sumit Bose <sbose@redhat.com>
-Reviewed-by: Pavel Březina <pbrezina@redhat.com>
----
- src/tests/cmocka/test_nss_srv.c | 182 +++++++++++++++++++++++++++++++++-------
- 1 file changed, 150 insertions(+), 32 deletions(-)
-
-diff --git a/src/tests/cmocka/test_nss_srv.c b/src/tests/cmocka/test_nss_srv.c
-index cbe0dccdc1d883eae1a9621f12997ef43d05178e..b468204fb1729618830513322f0d901c4c801e94 100644
---- a/src/tests/cmocka/test_nss_srv.c
-+++ b/src/tests/cmocka/test_nss_srv.c
-@@ -418,6 +418,26 @@ static errno_t store_user(struct nss_test_ctx *ctx,
-     return ret;
- }
- 
-+static errno_t delete_user(struct nss_test_ctx *ctx,
-+                           struct sss_domain_info *dom,
-+                           struct passwd *user)
-+{
-+    errno_t ret;
-+    char *fqname;
-+
-+    fqname = sss_create_internal_fqname(ctx,
-+                                        user->pw_name,
-+                                        dom->name);
-+    if (fqname == NULL) {
-+        return ENOMEM;
-+    }
-+
-+    ret = sysdb_delete_user(dom, fqname, user->pw_uid);
-+
-+    talloc_free(fqname);
-+    return ret;
-+}
-+
- static errno_t set_user_attr(struct nss_test_ctx *ctx,
-                              struct sss_domain_info *dom,
-                              struct passwd *user,
-@@ -491,6 +511,27 @@ static errno_t store_group(struct nss_test_ctx *ctx,
-     return ret;
- }
- 
-+static errno_t delete_group(struct nss_test_ctx *ctx,
-+                            struct sss_domain_info *dom,
-+                            struct group *group)
-+{
-+    errno_t ret;
-+    char *fqname;
-+
-+    fqname = sss_create_internal_fqname(ctx,
-+                                        group->gr_name,
-+                                        dom->name);
-+
-+    if (fqname == NULL) {
-+        return ENOMEM;
-+    }
-+
-+    ret = sysdb_delete_group(dom, fqname, group->gr_gid);
-+
-+    talloc_free(fqname);
-+    return ret;
-+}
-+
- static void assert_groups_equal(struct group *expected,
-                                 struct group *gr, const int nmem)
- {
-@@ -540,6 +581,42 @@ static errno_t store_group_member(struct nss_test_ctx *ctx,
-     return ret;
- }
- 
-+static errno_t remove_group_member(struct nss_test_ctx *ctx,
-+                                   const char *shortname_group,
-+                                   struct sss_domain_info *group_dom,
-+                                   const char *shortname_member,
-+                                   struct sss_domain_info *member_dom,
-+                                   enum sysdb_member_type type)
-+{
-+    errno_t ret;
-+    char *group_fqname = NULL;
-+    char *member_fqname = NULL;
-+
-+    group_fqname = sss_create_internal_fqname(ctx,
-+                                        shortname_group,
-+                                        group_dom->name);
-+    if (group_fqname == NULL) {
-+        return ENOMEM;
-+    }
-+
-+    member_fqname = sss_create_internal_fqname(ctx,
-+                                        shortname_member,
-+                                        member_dom->name);
-+    if (member_fqname == NULL) {
-+        talloc_free(group_fqname);
-+        return ENOMEM;
-+    }
-+
-+    ret = sysdb_remove_group_member(group_dom,
-+                                    group_fqname,
-+                                    member_fqname,
-+                                    type,
-+                                    false);
-+
-+    talloc_free(group_fqname);
-+    talloc_free(member_fqname);
-+    return ret;
-+}
- 
- /* ====================== The tests =============================== */
- struct passwd getpwnam_usr = {
-@@ -1599,34 +1676,6 @@ void test_nss_getgrnam_members_subdom(void **state)
- {
-     errno_t ret;
- 
--    ret = store_group(nss_test_ctx, nss_test_ctx->subdom,
--                      &testsubdomgroup, 0);
--    assert_int_equal(ret, EOK);
--
--    ret = store_user(nss_test_ctx, nss_test_ctx->subdom,
--                     &submember1, NULL, 0);
--    assert_int_equal(ret, EOK);
--
--    ret = store_user(nss_test_ctx, nss_test_ctx->subdom,
--                     &submember2, NULL, 0);
--    assert_int_equal(ret, EOK);
--
--    ret = store_group_member(nss_test_ctx,
--                             testsubdomgroup.gr_name,
--                             nss_test_ctx->subdom,
--                             submember1.pw_name,
--                             nss_test_ctx->subdom,
--                             SYSDB_MEMBER_USER);
--    assert_int_equal(ret, EOK);
--
--    ret = store_group_member(nss_test_ctx,
--                             testsubdomgroup.gr_name,
--                             nss_test_ctx->subdom,
--                             submember2.pw_name,
--                             nss_test_ctx->subdom,
--                             SYSDB_MEMBER_USER);
--    assert_int_equal(ret, EOK);
--
-     mock_input_user_or_group("testsubdomgroup@"TEST_SUBDOM_NAME);
-     will_return(__wrap_sss_packet_get_cmd, SSS_NSS_GETGRNAM);
-     will_return_always(__wrap_sss_packet_get_body, WRAP_CALL_REAL);
-@@ -1757,6 +1806,14 @@ void test_nss_getgrnam_mix_dom_fqdn(void **state)
- {
-     errno_t ret;
- 
-+    ret = store_group_member(nss_test_ctx,
-+                             testgroup_members.gr_name,
-+                             nss_test_ctx->tctx->dom,
-+                             submember1.pw_name,
-+                             nss_test_ctx->subdom,
-+                             SYSDB_MEMBER_USER);
-+    assert_int_equal(ret, EOK);
-+
-     nss_test_ctx->tctx->dom->fqnames = true;
- 
-     mock_input_user_or_group("testgroup_members@"TEST_DOM_NAME);
-@@ -3220,6 +3277,35 @@ static int nss_subdom_test_setup(void **state)
-     assert_int_equal(ret, EOK);
- 
-     nss_test_ctx->subdom = nss_test_ctx->tctx->dom->subdomains;
-+
-+    ret = store_group(nss_test_ctx, nss_test_ctx->subdom,
-+                      &testsubdomgroup, 0);
-+    assert_int_equal(ret, EOK);
-+
-+    ret = store_user(nss_test_ctx, nss_test_ctx->subdom,
-+                     &submember1, NULL, 0);
-+    assert_int_equal(ret, EOK);
-+
-+    ret = store_user(nss_test_ctx, nss_test_ctx->subdom,
-+                     &submember2, NULL, 0);
-+    assert_int_equal(ret, EOK);
-+
-+    ret = store_group_member(nss_test_ctx,
-+                             testsubdomgroup.gr_name,
-+                             nss_test_ctx->subdom,
-+                             submember1.pw_name,
-+                             nss_test_ctx->subdom,
-+                             SYSDB_MEMBER_USER);
-+    assert_int_equal(ret, EOK);
-+
-+    ret = store_group_member(nss_test_ctx,
-+                             testsubdomgroup.gr_name,
-+                             nss_test_ctx->subdom,
-+                             submember2.pw_name,
-+                             nss_test_ctx->subdom,
-+                             SYSDB_MEMBER_USER);
-+    assert_int_equal(ret, EOK);
-+
-     return 0;
- }
- 
-@@ -3241,6 +3327,38 @@ static int nss_test_teardown(void **state)
-     return 0;
- }
- 
-+static int nss_subdom_test_teardown(void **state)
-+{
-+    errno_t ret;
-+
-+    ret = remove_group_member(nss_test_ctx,
-+                              testsubdomgroup.gr_name,
-+                              nss_test_ctx->subdom,
-+                              submember2.pw_name,
-+                              nss_test_ctx->subdom,
-+                              SYSDB_MEMBER_USER);
-+    assert_int_equal(ret, EOK);
-+
-+    ret = remove_group_member(nss_test_ctx,
-+                              testsubdomgroup.gr_name,
-+                              nss_test_ctx->subdom,
-+                              submember1.pw_name,
-+                              nss_test_ctx->subdom,
-+                              SYSDB_MEMBER_USER);
-+    assert_int_equal(ret, EOK);
-+
-+    ret = delete_user(nss_test_ctx, nss_test_ctx->subdom, &submember2);
-+    assert_int_equal(ret, EOK);
-+
-+    ret = delete_user(nss_test_ctx, nss_test_ctx->subdom, &submember1);
-+    assert_int_equal(ret, EOK);
-+
-+    ret = delete_group(nss_test_ctx, nss_test_ctx->subdom, &testsubdomgroup);
-+    assert_int_equal(ret, EOK);
-+
-+    return nss_test_teardown(state);
-+}
-+
- struct passwd testbysid = {
-     .pw_name = discard_const("testsiduser"),
-     .pw_uid = 12345,
-@@ -3904,16 +4022,16 @@ int main(int argc, const char *argv[])
-                                         nss_fqdn_test_setup, nss_test_teardown),
-         cmocka_unit_test_setup_teardown(test_nss_getgrnam_members_subdom,
-                                         nss_subdom_test_setup,
--                                        nss_test_teardown),
-+                                        nss_subdom_test_teardown),
-         cmocka_unit_test_setup_teardown(test_nss_getgrnam_mix_dom,
-                                         nss_subdom_test_setup,
--                                        nss_test_teardown),
-+                                        nss_subdom_test_teardown),
-         cmocka_unit_test_setup_teardown(test_nss_getgrnam_mix_dom_fqdn,
-                                         nss_subdom_test_setup,
--                                        nss_test_teardown),
-+                                        nss_subdom_test_teardown),
-         cmocka_unit_test_setup_teardown(test_nss_getgrnam_mix_subdom,
-                                         nss_subdom_test_setup,
--                                        nss_test_teardown),
-+                                        nss_subdom_test_teardown),
-         cmocka_unit_test_setup_teardown(test_nss_getgrnam_space,
-                                         nss_test_setup, nss_test_teardown),
-         cmocka_unit_test_setup_teardown(test_nss_getgrnam_space_sub,
--- 
-2.9.3
-
diff --git a/SOURCES/0044-PAM-add-certificate-s-label-to-the-selection-prompt.patch b/SOURCES/0044-PAM-add-certificate-s-label-to-the-selection-prompt.patch
new file mode 100644
index 0000000..4ec3f7b
--- /dev/null
+++ b/SOURCES/0044-PAM-add-certificate-s-label-to-the-selection-prompt.patch
@@ -0,0 +1,274 @@
+From 35790511fd43b0c33f3b410b20a31e007b3e5d20 Mon Sep 17 00:00:00 2001
+From: Sumit Bose <sbose@redhat.com>
+Date: Tue, 7 Nov 2017 09:52:56 +0100
+Subject: [PATCH 44/46] PAM: add certificate's label to the selection prompt
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Some types of Smartcards contain multiple certificate with the same
+subject-DN for different usages. To make it easier to choose between
+them in case the matching rules allow more than one of them for
+authentication the label assigned to the certificate on the Smartcard is
+shown in the selection prompt as well.
+
+Related to https://pagure.io/SSSD/sssd/issue/3560
+
+Reviewed-by: Fabiano Fidêncio <fidencio@redhat.com>
+Tested-by: Scott Poore <spoore@redhat.com>
+(cherry picked from commit 57cefea8305a57c1c0491afb739813b7f17d5a25)
+---
+ src/p11_child/p11_child_nss.c   | 18 ++++++++++++++----
+ src/responder/pam/pamsrv.h      |  1 +
+ src/responder/pam/pamsrv_p11.c  | 41 +++++++++++++++++++++++++++++++++++++----
+ src/tests/cmocka/test_pam_srv.c | 28 ++++++++++++++--------------
+ 4 files changed, 66 insertions(+), 22 deletions(-)
+
+diff --git a/src/p11_child/p11_child_nss.c b/src/p11_child/p11_child_nss.c
+index e59aba0d1561f58206252f7251ecd88315836b1b..21c508eb1b1b68b3606d0a5eed36573b01f27a19 100644
+--- a/src/p11_child/p11_child_nss.c
++++ b/src/p11_child/p11_child_nss.c
+@@ -130,7 +130,7 @@ int do_work(TALLOC_CTX *mem_ctx, const char *nss_db,
+     CERTCertificate *found_cert = NULL;
+     PK11SlotList *list = NULL;
+     PK11SlotListElement *le;
+-    SECItem *key_id = NULL;
++    const char *label;
+     char *key_id_str = NULL;
+     CERTCertList *valid_certs = NULL;
+     char *cert_b64 = NULL;
+@@ -505,6 +505,17 @@ int do_work(TALLOC_CTX *mem_ctx, const char *nss_db,
+             goto done;
+         }
+ 
++        /* The NSS nickname is typically token_name:label, so the label starts
++         * after the ':'. */
++        if (found_cert->nickname != NULL) {
++            if ((label = strchr(found_cert->nickname, ':')) == NULL) {
++                label = found_cert->nickname;
++            } else {
++                label++;
++            }
++        } else {
++            label = "- no label found -";
++        }
+         talloc_free(cert_b64);
+         cert_b64 = sss_base64_encode(mem_ctx, found_cert->derCert.data,
+                                      found_cert->derCert.len);
+@@ -517,9 +528,9 @@ int do_work(TALLOC_CTX *mem_ctx, const char *nss_db,
+         DEBUG(SSSDBG_TRACE_ALL, "Found certificate has key id [%s].\n",
+               key_id_str);
+ 
+-        multi = talloc_asprintf_append(multi, "%s\n%s\n%s\n%s\n",
++        multi = talloc_asprintf_append(multi, "%s\n%s\n%s\n%s\n%s\n",
+                                        token_name, module_name, key_id_str,
+-                                       cert_b64);
++                                       label, cert_b64);
+     }
+     *_multi = multi;
+ 
+@@ -546,7 +557,6 @@ done:
+         CERT_DestroyCertList(cert_list);
+     }
+ 
+-    SECITEM_FreeItem(key_id, PR_TRUE);
+     PORT_Free(key_id_str);
+ 
+     PORT_Free(signed_random_value.data);
+diff --git a/src/responder/pam/pamsrv.h b/src/responder/pam/pamsrv.h
+index 0bc229212844602ed461d1c7db48bf51ac2e2194..dfd982178446d6327e09afc652018886c08fd88a 100644
+--- a/src/responder/pam/pamsrv.h
++++ b/src/responder/pam/pamsrv.h
+@@ -93,6 +93,7 @@ const char *sss_cai_get_cert(struct cert_auth_info *i);
+ const char *sss_cai_get_token_name(struct cert_auth_info *i);
+ const char *sss_cai_get_module_name(struct cert_auth_info *i);
+ const char *sss_cai_get_key_id(struct cert_auth_info *i);
++const char *sss_cai_get_label(struct cert_auth_info *i);
+ struct cert_auth_info *sss_cai_get_next(struct cert_auth_info *i);
+ struct ldb_result *sss_cai_get_cert_user_objs(struct cert_auth_info *i);
+ void sss_cai_set_cert_user_objs(struct cert_auth_info *i,
+diff --git a/src/responder/pam/pamsrv_p11.c b/src/responder/pam/pamsrv_p11.c
+index ec52c5ae7163d41144fe082643a201b766a1e201..fa2435543ea305f7cdb1e18753525beb373eaf4c 100644
+--- a/src/responder/pam/pamsrv_p11.c
++++ b/src/responder/pam/pamsrv_p11.c
+@@ -43,6 +43,7 @@ struct cert_auth_info {
+     char *token_name;
+     char *module_name;
+     char *key_id;
++    char *label;
+     struct ldb_result *cert_user_objs;
+     struct cert_auth_info *prev;
+     struct cert_auth_info *next;
+@@ -68,6 +69,11 @@ const char *sss_cai_get_key_id(struct cert_auth_info *i)
+     return i != NULL ? i->key_id : NULL;
+ }
+ 
++const char *sss_cai_get_label(struct cert_auth_info *i)
++{
++    return i != NULL ? i->label : NULL;
++}
++
+ struct cert_auth_info *sss_cai_get_next(struct cert_auth_info *i)
+ {
+     return i != NULL ? i->next : NULL;
+@@ -439,6 +445,31 @@ static errno_t parse_p11_child_response(TALLOC_CTX *mem_ctx, uint8_t *buf,
+         }
+ 
+         if (pn == p) {
++            DEBUG(SSSDBG_OP_FAILURE,
++                  "Missing label in p11_child response.\n");
++            ret = EINVAL;
++            goto done;
++        }
++
++        cert_auth_info->label = talloc_strndup(cert_auth_info, (char *) p,
++                                               (pn - p));
++        if (cert_auth_info->label == NULL) {
++            DEBUG(SSSDBG_OP_FAILURE, "talloc_strndup failed.\n");
++            ret = ENOMEM;
++            goto done;
++        }
++        DEBUG(SSSDBG_TRACE_ALL, "Found label [%s].\n", cert_auth_info->label);
++
++        p = ++pn;
++        pn = memchr(p, '\n', buf_len - (p - buf));
++        if (pn == NULL) {
++            DEBUG(SSSDBG_OP_FAILURE,
++                  "Missing new-line in p11_child response.\n");
++            ret = EINVAL;
++            goto done;
++        }
++
++        if (pn == p) {
+             DEBUG(SSSDBG_OP_FAILURE, "Missing cert in p11_child response.\n");
+             ret = EINVAL;
+             goto done;
+@@ -816,7 +847,8 @@ errno_t pam_check_cert_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
+     return EOK;
+ }
+ 
+-static char *get_cert_prompt(TALLOC_CTX *mem_ctx, const char *cert)
++static char *get_cert_prompt(TALLOC_CTX *mem_ctx,
++                             struct cert_auth_info *cert_info)
+ {
+     int ret;
+     struct sss_certmap_ctx *ctx = NULL;
+@@ -839,7 +871,7 @@ static char *get_cert_prompt(TALLOC_CTX *mem_ctx, const char *cert)
+         goto done;
+     }
+ 
+-    der = sss_base64_decode(mem_ctx, cert, &der_size);
++    der = sss_base64_decode(mem_ctx, sss_cai_get_cert(cert_info), &der_size);
+     if (der == NULL) {
+         DEBUG(SSSDBG_OP_FAILURE, "sss_base64_decode failed.\n");
+         goto done;
+@@ -851,7 +883,8 @@ static char *get_cert_prompt(TALLOC_CTX *mem_ctx, const char *cert)
+         goto done;
+     }
+ 
+-    prompt = talloc_strdup(mem_ctx, filter);
++    prompt = talloc_asprintf(mem_ctx, "%s\n%s", sss_cai_get_label(cert_info),
++                                                filter);
+     if (prompt == NULL) {
+         DEBUG(SSSDBG_OP_FAILURE, "talloc_strdup failed.\n");
+     }
+@@ -885,7 +918,7 @@ static errno_t pack_cert_data(TALLOC_CTX *mem_ctx, const char *sysdb_username,
+         username = sysdb_username;
+     }
+ 
+-    prompt = get_cert_prompt(mem_ctx, sss_cai_get_cert(cert_info));
++    prompt = get_cert_prompt(mem_ctx, cert_info);
+     if (prompt == NULL) {
+         DEBUG(SSSDBG_OP_FAILURE, "get_cert_prompt failed.\n");
+         return EIO;
+diff --git a/src/tests/cmocka/test_pam_srv.c b/src/tests/cmocka/test_pam_srv.c
+index bccf9972dacbb414076904a783772198620fd73c..4752648796ab4c863706780a2f470853cddbcc11 100644
+--- a/src/tests/cmocka/test_pam_srv.c
++++ b/src/tests/cmocka/test_pam_srv.c
+@@ -53,7 +53,7 @@
+ #define TEST_TOKEN_NAME "SSSD Test Token"
+ #define TEST_MODULE_NAME "NSS-Internal"
+ #define TEST_KEY_ID "A5EF7DEE625CA5996C8D1BA7D036708161FD49E7"
+-#define TEST_SUBJECT_DN "CN=ipa-devel.ipa.devel,O=IPA.DEVEL"
++#define TEST_PROMPT "Server-Cert\nCN=ipa-devel.ipa.devel,O=IPA.DEVEL"
+ #define TEST_TOKEN_CERT \
+ "MIIECTCCAvGgAwIBAgIBCTANBgkqhkiG9w0BAQsFADA0MRIwEAYDVQQKDAlJUEEu" \
+ "REVWRUwxHjAcBgNVBAMMFUNlcnRpZmljYXRlIEF1dGhvcml0eTAeFw0xNjA1MjMx" \
+@@ -79,7 +79,7 @@
+ "XyQBwOYRORlnfGyu+Yc9c3E0Wx8Tlznz0lqPR9g="
+ 
+ #define TEST2_KEY_ID "C8D60E009EB195D01A7083EE1D5419251AA87C2C"
+-#define TEST2_SUBJECT_DN "CN=IPA RA,O=IPA.DEVEL"
++#define TEST2_PROMPT "ipaCert\nCN=IPA RA,O=IPA.DEVEL"
+ #define TEST_TOKEN_2ND_CERT \
+ "MIIDazCCAlOgAwIBAgIBBzANBgkqhkiG9w0BAQsFADA0MRIwEAYDVQQKDAlJUEEu" \
+ "REVWRUwxHjAcBgNVBAMMFUNlcnRpZmljYXRlIEF1dGhvcml0eTAeFw0xNjA1MjMx" \
+@@ -837,7 +837,7 @@ static int test_pam_cert_check_gdm_smartcard(uint32_t status, uint8_t *body,
+                                 + sizeof(TEST_TOKEN_NAME)
+                                 + sizeof(TEST_MODULE_NAME)
+                                 + sizeof(TEST_KEY_ID)
+-                                + sizeof(TEST_SUBJECT_DN)));
++                                + sizeof(TEST_PROMPT)));
+ 
+     assert_int_equal(*(body + rp + sizeof("pamuser@"TEST_DOM_NAME) - 1), 0);
+     assert_string_equal(body + rp, "pamuser@"TEST_DOM_NAME);
+@@ -855,9 +855,9 @@ static int test_pam_cert_check_gdm_smartcard(uint32_t status, uint8_t *body,
+     assert_string_equal(body + rp, TEST_KEY_ID);
+     rp += sizeof(TEST_KEY_ID);
+ 
+-    assert_int_equal(*(body + rp + sizeof(TEST_SUBJECT_DN) - 1), 0);
+-    assert_string_equal(body + rp, TEST_SUBJECT_DN);
+-    rp += sizeof(TEST_SUBJECT_DN);
++    assert_int_equal(*(body + rp + sizeof(TEST_PROMPT) - 1), 0);
++    assert_string_equal(body + rp, TEST_PROMPT);
++    rp += sizeof(TEST_PROMPT);
+ 
+     assert_int_equal(rp, blen);
+     return EOK;
+@@ -904,7 +904,7 @@ static int test_pam_cert_check_ex(uint32_t status, uint8_t *body, size_t blen,
+                                 + sizeof(TEST_TOKEN_NAME)
+                                 + sizeof(TEST_MODULE_NAME)
+                                 + sizeof(TEST_KEY_ID)
+-                                + sizeof(TEST_SUBJECT_DN)));
++                                + sizeof(TEST_PROMPT)));
+ 
+     assert_int_equal(*(body + rp + strlen(name)), 0);
+     assert_string_equal(body + rp, name);
+@@ -922,9 +922,9 @@ static int test_pam_cert_check_ex(uint32_t status, uint8_t *body, size_t blen,
+     assert_string_equal(body + rp, TEST_KEY_ID);
+     rp += sizeof(TEST_KEY_ID);
+ 
+-    assert_int_equal(*(body + rp + sizeof(TEST_SUBJECT_DN) - 1), 0);
+-    assert_string_equal(body + rp, TEST_SUBJECT_DN);
+-    rp += sizeof(TEST_SUBJECT_DN);
++    assert_int_equal(*(body + rp + sizeof(TEST_PROMPT) - 1), 0);
++    assert_string_equal(body + rp, TEST_PROMPT);
++    rp += sizeof(TEST_PROMPT);
+ 
+     if (name2 != NULL && *name2 != '\0') {
+         SAFEALIGN_COPY_UINT32(&val, body + rp, &rp);
+@@ -935,7 +935,7 @@ static int test_pam_cert_check_ex(uint32_t status, uint8_t *body, size_t blen,
+                                     + sizeof(TEST_TOKEN_NAME)
+                                     + sizeof(TEST_MODULE_NAME)
+                                     + sizeof(TEST2_KEY_ID)
+-                                    + sizeof(TEST2_SUBJECT_DN)));
++                                    + sizeof(TEST2_PROMPT)));
+ 
+         assert_int_equal(*(body + rp + strlen(name)), 0);
+         assert_string_equal(body + rp, name);
+@@ -953,9 +953,9 @@ static int test_pam_cert_check_ex(uint32_t status, uint8_t *body, size_t blen,
+         assert_string_equal(body + rp, TEST2_KEY_ID);
+         rp += sizeof(TEST2_KEY_ID);
+ 
+-        assert_int_equal(*(body + rp + sizeof(TEST2_SUBJECT_DN) - 1), 0);
+-        assert_string_equal(body + rp, TEST2_SUBJECT_DN);
+-        rp += sizeof(TEST2_SUBJECT_DN);
++        assert_int_equal(*(body + rp + sizeof(TEST2_PROMPT) - 1), 0);
++        assert_string_equal(body + rp, TEST2_PROMPT);
++        rp += sizeof(TEST2_PROMPT);
+     }
+ 
+     assert_int_equal(rp, blen);
+-- 
+2.13.6
+
diff --git a/SOURCES/0045-NSS-TESTS-Include-searches-for-non-fqnames-members-o.patch b/SOURCES/0045-NSS-TESTS-Include-searches-for-non-fqnames-members-o.patch
deleted file mode 100644
index 08acdce..0000000
--- a/SOURCES/0045-NSS-TESTS-Include-searches-for-non-fqnames-members-o.patch
+++ /dev/null
@@ -1,355 +0,0 @@
-From 510971d2abc3b76799048cd608511d693f5c3edc Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Fabiano=20Fid=C3=AAncio?= <fidencio@redhat.com>
-Date: Wed, 1 Mar 2017 08:33:06 +0000
-Subject: [PATCH 45/54] NSS/TESTS: Include searches for non-fqnames members of
- a subdomain
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Let's extend the NSS tests in order to also test looking up users, from
-a subdomain, by their short names (non fully qualified names).
-
-Related:
-https://pagure.io/SSSD/sssd/issue/3001
-
-Signed-off-by: Fabiano Fidêncio <fidencio@redhat.com>
-
-Reviewed-by: Sumit Bose <sbose@redhat.com>
-Reviewed-by: Pavel Březina <pbrezina@redhat.com>
----
- src/tests/cmocka/test_nss_srv.c | 246 ++++++++++++++++++++++++++++++++++------
- 1 file changed, 211 insertions(+), 35 deletions(-)
-
-diff --git a/src/tests/cmocka/test_nss_srv.c b/src/tests/cmocka/test_nss_srv.c
-index b468204fb1729618830513322f0d901c4c801e94..ede72b341b60842ad470df2794aa90ea9797e999 100644
---- a/src/tests/cmocka/test_nss_srv.c
-+++ b/src/tests/cmocka/test_nss_srv.c
-@@ -1648,16 +1648,29 @@ static int test_nss_getgrnam_members_check_subdom(uint32_t status,
-     tmp_ctx = talloc_new(nss_test_ctx);
-     assert_non_null(tmp_ctx);
- 
--    exp_members[0] = sss_tc_fqname(tmp_ctx, nss_test_ctx->subdom->names,
--                                   nss_test_ctx->subdom, submember1.pw_name);
--    assert_non_null(exp_members[0]);
--    exp_members[1] = sss_tc_fqname(tmp_ctx, nss_test_ctx->subdom->names,
--                                   nss_test_ctx->subdom, submember2.pw_name);
--    assert_non_null(exp_members[1]);
-+    if (nss_test_ctx->subdom->fqnames) {
-+        exp_members[0] = sss_tc_fqname(tmp_ctx,
-+                                       nss_test_ctx->subdom->names,
-+                                       nss_test_ctx->subdom,
-+                                       submember1.pw_name);
-+        assert_non_null(exp_members[0]);
- 
--    expected.gr_name = sss_tc_fqname(tmp_ctx, nss_test_ctx->subdom->names,
--                                     nss_test_ctx->subdom, testsubdomgroup.gr_name);
--    assert_non_null(expected.gr_name);
-+        exp_members[1] = sss_tc_fqname(tmp_ctx,
-+                                       nss_test_ctx->subdom->names,
-+                                       nss_test_ctx->subdom,
-+                                       submember2.pw_name);
-+        assert_non_null(exp_members[1]);
-+
-+        expected.gr_name = sss_tc_fqname(tmp_ctx,
-+                                         nss_test_ctx->subdom->names,
-+                                         nss_test_ctx->subdom,
-+                                         testsubdomgroup.gr_name);
-+        assert_non_null(expected.gr_name);
-+    } else {
-+        exp_members[0] = submember1.pw_name;
-+        exp_members[1] = submember2.pw_name;
-+        expected.gr_name = testsubdomgroup.gr_name;
-+    }
- 
-     assert_int_equal(status, EOK);
- 
-@@ -1692,6 +1705,29 @@ void test_nss_getgrnam_members_subdom(void **state)
-     assert_int_equal(ret, EOK);
- }
- 
-+void test_nss_getgrnam_members_subdom_nonfqnames(void **state)
-+{
-+    errno_t ret;
-+
-+    nss_test_ctx->subdom->fqnames = false;
-+
-+    mock_input_user_or_group("testsubdomgroup");
-+    mock_account_recv_simple();
-+    will_return(__wrap_sss_packet_get_cmd, SSS_NSS_GETGRNAM);
-+    will_return_always(__wrap_sss_packet_get_body, WRAP_CALL_REAL);
-+
-+    /* Query for that group, call a callback when command finishes */
-+    set_cmd_cb(test_nss_getgrnam_members_check_subdom);
-+    ret = sss_cmd_execute(nss_test_ctx->cctx, SSS_NSS_GETGRNAM,
-+                          nss_test_ctx->nss_cmds);
-+    assert_int_equal(ret, EOK);
-+
-+    /* Wait until the test finishes with EOK */
-+    ret = test_ev_loop(nss_test_ctx->tctx);
-+
-+    assert_int_equal(ret, EOK);
-+}
-+
- static int test_nss_getgrnam_check_mix_dom(uint32_t status,
-                                            uint8_t *body, size_t blen)
- {
-@@ -1710,9 +1746,15 @@ static int test_nss_getgrnam_check_mix_dom(uint32_t status,
-     tmp_ctx = talloc_new(nss_test_ctx);
-     assert_non_null(tmp_ctx);
- 
--    exp_members[0] = sss_tc_fqname(tmp_ctx, nss_test_ctx->subdom->names,
--                                   nss_test_ctx->subdom, submember1.pw_name);
--    assert_non_null(exp_members[0]);
-+    if (nss_test_ctx->subdom->fqnames) {
-+        exp_members[0] = sss_tc_fqname(tmp_ctx,
-+                                       nss_test_ctx->subdom->names,
-+                                       nss_test_ctx->subdom,
-+                                       submember1.pw_name);
-+        assert_non_null(exp_members[0]);
-+    } else {
-+        exp_members[0] = submember1.pw_name;
-+    }
-     exp_members[1] = testmember1.pw_name;
-     exp_members[2] = testmember2.pw_name;
- 
-@@ -1756,6 +1798,35 @@ void test_nss_getgrnam_mix_dom(void **state)
-     assert_int_equal(ret, EOK);
- }
- 
-+void test_nss_getgrnam_mix_dom_nonfqnames(void **state)
-+{
-+    errno_t ret;
-+
-+    nss_test_ctx->subdom->fqnames = false;
-+
-+    ret = store_group_member(nss_test_ctx,
-+                             testgroup_members.gr_name,
-+                             nss_test_ctx->tctx->dom,
-+                             submember1.pw_name,
-+                             nss_test_ctx->subdom,
-+                             SYSDB_MEMBER_USER);
-+    assert_int_equal(ret, EOK);
-+
-+    mock_input_user_or_group("testgroup_members");
-+    will_return(__wrap_sss_packet_get_cmd, SSS_NSS_GETGRNAM);
-+    will_return_always(__wrap_sss_packet_get_body, WRAP_CALL_REAL);
-+
-+    /* Query for that group, call a callback when command finishes */
-+    set_cmd_cb(test_nss_getgrnam_check_mix_dom);
-+    ret = sss_cmd_execute(nss_test_ctx->cctx, SSS_NSS_GETGRNAM,
-+                          nss_test_ctx->nss_cmds);
-+    assert_int_equal(ret, EOK);
-+
-+    /* Wait until the test finishes with EOK */
-+    ret = test_ev_loop(nss_test_ctx->tctx);
-+    assert_int_equal(ret, EOK);
-+}
-+
- static int test_nss_getgrnam_check_mix_dom_fqdn(uint32_t status,
-                                                 uint8_t *body, size_t blen)
- {
-@@ -1773,21 +1844,33 @@ static int test_nss_getgrnam_check_mix_dom_fqdn(uint32_t status,
-     tmp_ctx = talloc_new(nss_test_ctx);
-     assert_non_null(tmp_ctx);
- 
--    exp_members[0] = sss_tc_fqname(tmp_ctx, nss_test_ctx->subdom->names,
--                                   nss_test_ctx->subdom, submember1.pw_name);
--    assert_non_null(exp_members[0]);
--    exp_members[1] = sss_tc_fqname(tmp_ctx, nss_test_ctx->tctx->dom->names,
--                                   nss_test_ctx->tctx->dom, testmember1.pw_name);
--    assert_non_null(exp_members[1]);
--    exp_members[2] = sss_tc_fqname(tmp_ctx, nss_test_ctx->tctx->dom->names,
--                                   nss_test_ctx->tctx->dom, testmember2.pw_name);
--    assert_non_null(exp_members[2]);
-+    if (nss_test_ctx->subdom->fqnames) {
-+        exp_members[0] = sss_tc_fqname(tmp_ctx,
-+                                       nss_test_ctx->subdom->names,
-+                                       nss_test_ctx->subdom,
-+                                       submember1.pw_name);
-+        assert_non_null(exp_members[0]);
-+    } else {
-+        exp_members[0] = submember1.pw_name;
-+    }
-+    if (nss_test_ctx->tctx->dom->fqnames) {
-+        exp_members[1] = sss_tc_fqname(tmp_ctx, nss_test_ctx->tctx->dom->names,
-+                                       nss_test_ctx->tctx->dom, testmember1.pw_name);
-+        assert_non_null(exp_members[1]);
-+        exp_members[2] = sss_tc_fqname(tmp_ctx, nss_test_ctx->tctx->dom->names,
-+                                       nss_test_ctx->tctx->dom, testmember2.pw_name);
-+        assert_non_null(exp_members[2]);
- 
--    expected.gr_name = sss_tc_fqname(tmp_ctx,
--                                     nss_test_ctx->tctx->dom->names,
--                                     nss_test_ctx->tctx->dom,
--                                     testgroup_members.gr_name);
--    assert_non_null(expected.gr_name);
-+        expected.gr_name = sss_tc_fqname(tmp_ctx,
-+                                         nss_test_ctx->tctx->dom->names,
-+                                         nss_test_ctx->tctx->dom,
-+                                         testgroup_members.gr_name);
-+        assert_non_null(expected.gr_name);
-+    } else {
-+        exp_members[1] = testmember1.pw_name;
-+        exp_members[2] = testmember2.pw_name;
-+        expected.gr_name = testgroup_members.gr_name;
-+    }
- 
-     assert_int_equal(status, EOK);
- 
-@@ -1834,6 +1917,40 @@ void test_nss_getgrnam_mix_dom_fqdn(void **state)
-     assert_int_equal(ret, EOK);
- }
- 
-+void test_nss_getgrnam_mix_dom_fqdn_nonfqnames(void **state)
-+{
-+    errno_t ret;
-+
-+    ret = store_group_member(nss_test_ctx,
-+                             testgroup_members.gr_name,
-+                             nss_test_ctx->tctx->dom,
-+                             submember1.pw_name,
-+                             nss_test_ctx->subdom,
-+                             SYSDB_MEMBER_USER);
-+    assert_int_equal(ret, EOK);
-+
-+    nss_test_ctx->tctx->dom->fqnames = false;
-+    nss_test_ctx->subdom->fqnames = false;
-+
-+
-+    mock_input_user_or_group("testgroup_members");
-+    will_return(__wrap_sss_packet_get_cmd, SSS_NSS_GETGRNAM);
-+    will_return_always(__wrap_sss_packet_get_body, WRAP_CALL_REAL);
-+
-+    /* Query for that group, call a callback when command finishes */
-+    set_cmd_cb(test_nss_getgrnam_check_mix_dom_fqdn);
-+    ret = sss_cmd_execute(nss_test_ctx->cctx, SSS_NSS_GETGRNAM,
-+                          nss_test_ctx->nss_cmds);
-+    assert_int_equal(ret, EOK);
-+
-+    /* Wait until the test finishes with EOK */
-+    ret = test_ev_loop(nss_test_ctx->tctx);
-+
-+    /* Restore FQDN settings */
-+    nss_test_ctx->tctx->dom->fqnames = false;
-+    assert_int_equal(ret, EOK);
-+}
-+
- static int test_nss_getgrnam_check_mix_subdom(uint32_t status,
-                                               uint8_t *body, size_t blen)
- {
-@@ -1851,20 +1968,37 @@ static int test_nss_getgrnam_check_mix_subdom(uint32_t status,
-     tmp_ctx = talloc_new(nss_test_ctx);
-     assert_non_null(tmp_ctx);
- 
--    exp_members[0] = sss_tc_fqname(tmp_ctx, nss_test_ctx->subdom->names,
--                                   nss_test_ctx->subdom, submember1.pw_name);
--    assert_non_null(exp_members[0]);
--    exp_members[1] = sss_tc_fqname(tmp_ctx, nss_test_ctx->subdom->names,
--                                   nss_test_ctx->subdom, submember2.pw_name);
--    assert_non_null(exp_members[1]);
-+    if (nss_test_ctx->subdom->fqnames) {
-+        exp_members[0] = sss_tc_fqname(tmp_ctx,
-+                                       nss_test_ctx->subdom->names,
-+                                       nss_test_ctx->subdom,
-+                                       submember1.pw_name);
-+        assert_non_null(exp_members[0]);
-+
-+        exp_members[1] = sss_tc_fqname(tmp_ctx,
-+                                       nss_test_ctx->subdom->names,
-+                                       nss_test_ctx->subdom,
-+                                       submember2.pw_name);
-+        assert_non_null(exp_members[1]);
-+    } else {
-+        exp_members[0] = submember1.pw_name;
-+        exp_members[1] = submember2.pw_name;
-+    }
-+
-     /* Important: this member is from a non-qualified domain, so his name will
-      * not be qualified either
-      */
-     exp_members[2] = testmember1.pw_name;
- 
--    expected.gr_name = sss_tc_fqname(tmp_ctx, nss_test_ctx->subdom->names,
--                                     nss_test_ctx->subdom, testsubdomgroup.gr_name);
--    assert_non_null(expected.gr_name);
-+    if (nss_test_ctx->subdom->fqnames) {
-+        expected.gr_name = sss_tc_fqname(tmp_ctx,
-+                                         nss_test_ctx->subdom->names,
-+                                         nss_test_ctx->subdom,
-+                                         testsubdomgroup.gr_name);
-+        assert_non_null(expected.gr_name);
-+    } else {
-+        expected.gr_name = testsubdomgroup.gr_name;
-+    }
- 
-     assert_int_equal(status, EOK);
- 
-@@ -1906,6 +2040,36 @@ void test_nss_getgrnam_mix_subdom(void **state)
-     assert_int_equal(ret, EOK);
- }
- 
-+void test_nss_getgrnam_mix_subdom_nonfqnames(void **state)
-+{
-+    errno_t ret;
-+
-+    nss_test_ctx->subdom->fqnames = false;
-+
-+    ret = store_group_member(nss_test_ctx,
-+                             testsubdomgroup.gr_name,
-+                             nss_test_ctx->subdom,
-+                             testmember1.pw_name,
-+                             nss_test_ctx->tctx->dom,
-+                             SYSDB_MEMBER_USER);
-+    assert_int_equal(ret, EOK);
-+
-+    mock_input_user_or_group("testsubdomgroup");
-+    mock_account_recv_simple();
-+    will_return(__wrap_sss_packet_get_cmd, SSS_NSS_GETGRNAM);
-+    will_return_always(__wrap_sss_packet_get_body, WRAP_CALL_REAL);
-+
-+    /* Query for that group, call a callback when command finishes */
-+    set_cmd_cb(test_nss_getgrnam_check_mix_subdom);
-+    ret = sss_cmd_execute(nss_test_ctx->cctx, SSS_NSS_GETGRNAM,
-+                          nss_test_ctx->nss_cmds);
-+    assert_int_equal(ret, EOK);
-+
-+    /* Wait until the test finishes with EOK */
-+    ret = test_ev_loop(nss_test_ctx->tctx);
-+    assert_int_equal(ret, EOK);
-+}
-+
- struct group space_group = {
-     .gr_gid = 2123,
-     .gr_name = discard_const("space group"),
-@@ -4023,15 +4187,27 @@ int main(int argc, const char *argv[])
-         cmocka_unit_test_setup_teardown(test_nss_getgrnam_members_subdom,
-                                         nss_subdom_test_setup,
-                                         nss_subdom_test_teardown),
-+        cmocka_unit_test_setup_teardown(test_nss_getgrnam_members_subdom_nonfqnames,
-+                                        nss_subdom_test_setup,
-+                                        nss_subdom_test_teardown),
-         cmocka_unit_test_setup_teardown(test_nss_getgrnam_mix_dom,
-                                         nss_subdom_test_setup,
-                                         nss_subdom_test_teardown),
-+        cmocka_unit_test_setup_teardown(test_nss_getgrnam_mix_dom_nonfqnames,
-+                                        nss_subdom_test_setup,
-+                                        nss_subdom_test_teardown),
-         cmocka_unit_test_setup_teardown(test_nss_getgrnam_mix_dom_fqdn,
-                                         nss_subdom_test_setup,
-                                         nss_subdom_test_teardown),
-+        cmocka_unit_test_setup_teardown(test_nss_getgrnam_mix_dom_fqdn_nonfqnames,
-+                                        nss_subdom_test_setup,
-+                                        nss_subdom_test_teardown),
-         cmocka_unit_test_setup_teardown(test_nss_getgrnam_mix_subdom,
-                                         nss_subdom_test_setup,
-                                         nss_subdom_test_teardown),
-+        cmocka_unit_test_setup_teardown(test_nss_getgrnam_mix_subdom_nonfqnames,
-+                                        nss_subdom_test_setup,
-+                                        nss_subdom_test_teardown),
-         cmocka_unit_test_setup_teardown(test_nss_getgrnam_space,
-                                         nss_test_setup, nss_test_teardown),
-         cmocka_unit_test_setup_teardown(test_nss_getgrnam_space_sub,
--- 
-2.9.3
-
diff --git a/SOURCES/0045-SYSDB-Remove-code-causing-a-covscan-warning.patch b/SOURCES/0045-SYSDB-Remove-code-causing-a-covscan-warning.patch
new file mode 100644
index 0000000..c2e1d8c
--- /dev/null
+++ b/SOURCES/0045-SYSDB-Remove-code-causing-a-covscan-warning.patch
@@ -0,0 +1,128 @@
+From 075f45980ea004201b2d13a3ecfe3bfb1478046d Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Fabiano=20Fid=C3=AAncio?= <fidencio@redhat.com>
+Date: Mon, 13 Nov 2017 08:29:53 +0100
+Subject: [PATCH 45/46] SYSDB: Remove code causing a covscan warning
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+There's no reason to check for both ret != EOK and sanitized == NULL, as
+the second should never ever happen.
+
+This check is causing a clang warning in our code:
+
+ Defect type: CLANG_WARNING
+ 1. sssd-1.16.0/src/db/sysdb_ops.c:4223:9: warning: Dereference of undefined pointer value
+ #     if (res->count > 1) {
+ #         ^~~~~~~~~~
+ 4. sssd-1.16.0/src/db/sysdb_ops.c:4199:5: note: 'res' declared without an initial value
+ #     struct ldb_result *res;
+ #     ^~~~~~~~~~~~~~~~~~~~~~
+ 7. sssd-1.16.0/src/db/sysdb_ops.c:4202:9: note: Assuming 'sid_str' is non-null
+ #     if (!sid_str) return EINVAL;
+ #         ^~~~~~~~
+ 10. sssd-1.16.0/src/db/sysdb_ops.c:4202:5: note: Taking false branch
+ #     if (!sid_str) return EINVAL;
+ #     ^
+ 13. sssd-1.16.0/src/db/sysdb_ops.c:4205:9: note: Assuming 'tmp_ctx' is non-null
+ #     if (!tmp_ctx) {
+ #         ^~~~~~~~
+ 16. sssd-1.16.0/src/db/sysdb_ops.c:4205:5: note: Taking false branch
+ #     if (!tmp_ctx) {
+ #     ^
+ 19. sssd-1.16.0/src/db/sysdb_ops.c:4209:11: note: Calling 'sysdb_search_object_by_sid'
+ #     ret = sysdb_search_object_by_sid(tmp_ctx, domain, sid_str, NULL, &res);
+ #           ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ 22. sssd-1.16.0/src/db/sysdb_ops.c:4960:12: note: Calling 'sysdb_search_object_by_str_attr'
+ #     return sysdb_search_object_by_str_attr(mem_ctx, domain, SYSDB_SID_FILTER,
+ #            ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ 25. sssd-1.16.0/src/db/sysdb_ops.c:4872:5: note: Taking false branch
+ #     if (str == NULL) {
+ #     ^
+ 28. sssd-1.16.0/src/db/sysdb_ops.c:4877:9: note: Assuming 'ret' is equal to 0
+ #     if (ret != EOK || sanitized == NULL) {
+ #         ^~~~~~~~~~
+ 31. sssd-1.16.0/src/db/sysdb_ops.c:4877:9: note: Left side of '||' is false
+ 32. sssd-1.16.0/src/db/sysdb_ops.c:4877:23: note: Assuming 'sanitized' is equal to null
+ #     if (ret != EOK || sanitized == NULL) {
+ #                       ^~~~~~~~~~~~~~~~~
+ 35. sssd-1.16.0/src/db/sysdb_ops.c:4877:5: note: Taking true branch
+ #     if (ret != EOK || sanitized == NULL) {
+ #     ^
+ 38. sssd-1.16.0/src/db/sysdb_ops.c:4878:9: note: Left side of '||' is false
+ #         DEBUG(SSSDBG_OP_FAILURE, "sss_filter_sanitize failed.\n");
+ #         ^
+ 41. sssd-1.16.0/src/util/debug.h:123:9: note: expanded from macro 'DEBUG'
+ #     if (DEBUG_IS_SET(__debug_macro_level)) { \
+ #         ^
+ 44. sssd-1.16.0/src/util/debug.h:135:30: note: expanded from macro 'DEBUG_IS_SET'
+ # #define DEBUG_IS_SET(level) (debug_level & (level) || \
+ #                              ^
+ 47. sssd-1.16.0/src/db/sysdb_ops.c:4878:9: note: Assuming 'debug_level' is not equal to 0
+ #         DEBUG(SSSDBG_OP_FAILURE, "sss_filter_sanitize failed.\n");
+ #         ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ 50. sssd-1.16.0/src/util/debug.h:123:9: note: expanded from macro 'DEBUG'
+ #     if (DEBUG_IS_SET(__debug_macro_level)) { \
+ #         ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ 53. sssd-1.16.0/src/util/debug.h:136:30: note: expanded from macro 'DEBUG_IS_SET'
+ #                             (debug_level == SSSDBG_UNRESOLVED && \
+ #                              ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ 56. sssd-1.16.0/src/db/sysdb_ops.c:4878:9: note: Left side of '&&' is false
+ 57. sssd-1.16.0/src/util/debug.h:123:9: note: expanded from macro 'DEBUG'
+ #     if (DEBUG_IS_SET(__debug_macro_level)) { \
+ #         ^
+ 60. sssd-1.16.0/src/util/debug.h:136:63: note: expanded from macro 'DEBUG_IS_SET'
+ #                             (debug_level == SSSDBG_UNRESOLVED && \
+ #                                                               ^
+ 63. sssd-1.16.0/src/db/sysdb_ops.c:4878:9: note: Loop condition is false. Exiting loop
+ 64. sssd-1.16.0/src/util/debug.h:121:35: note: expanded from macro 'DEBUG'
+ # #define DEBUG(level, format, ...) do { \
+ #                                   ^
+ 67. sssd-1.16.0/src/db/sysdb_ops.c:4879:9: note: Control jumps to line 4892
+ #         goto done;
+ #         ^
+ 70. sssd-1.16.0/src/db/sysdb_ops.c:4960:12: note: Returning from 'sysdb_search_object_by_str_attr'
+ #     return sysdb_search_object_by_str_attr(mem_ctx, domain, SYSDB_SID_FILTER,
+ #            ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ 73. sssd-1.16.0/src/db/sysdb_ops.c:4209:11: note: Returning from 'sysdb_search_object_by_sid'
+ #     ret = sysdb_search_object_by_sid(tmp_ctx, domain, sid_str, NULL, &res);
+ #           ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ 76. sssd-1.16.0/src/db/sysdb_ops.c:4211:5: note: Taking false branch
+ #     if (ret == ENOENT) {
+ #     ^
+ 79. sssd-1.16.0/src/db/sysdb_ops.c:4217:12: note: Taking false branch
+ #     } else if (ret != EOK) {
+ #            ^
+ 82. sssd-1.16.0/src/db/sysdb_ops.c:4223:9: note: Dereference of undefined pointer value
+ #     if (res->count > 1) {
+ #         ^~~~~~~~~~
+ #  4221|       }
+ #  4222|
+ #  4223|->     if (res->count > 1) {
+ #  4224|           DEBUG(SSSDBG_FATAL_FAILURE, "getbysid call returned more than one " \
+ #  4225|                                        "result !?!\n");
+
+Signed-off-by: Fabiano Fidêncio <fidencio@redhat.com>
+
+Reviewed-by: Pavel Březina <pbrezina@redhat.com>
+(cherry picked from commit b739b3e767c053bb3a7e6651514896b30502d838)
+---
+ src/db/sysdb_ops.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/src/db/sysdb_ops.c b/src/db/sysdb_ops.c
+index 2f8e36c6c9a2c2cefe4af5fb78957763304d989a..635c7db51f516e2217c93016409499e49289004c 100644
+--- a/src/db/sysdb_ops.c
++++ b/src/db/sysdb_ops.c
+@@ -4874,7 +4874,7 @@ static errno_t sysdb_search_object_by_str_attr(TALLOC_CTX *mem_ctx,
+     }
+ 
+     ret = sss_filter_sanitize(NULL, str, &sanitized);
+-    if (ret != EOK || sanitized == NULL) {
++    if (ret != EOK) {
+         DEBUG(SSSDBG_OP_FAILURE, "sss_filter_sanitize failed.\n");
+         goto done;
+     }
+-- 
+2.13.6
+
diff --git a/SOURCES/0046-SYSDB-Add-methods-to-deal-with-the-domain-s-resoluti.patch b/SOURCES/0046-SYSDB-Add-methods-to-deal-with-the-domain-s-resoluti.patch
deleted file mode 100644
index b2a6305..0000000
--- a/SOURCES/0046-SYSDB-Add-methods-to-deal-with-the-domain-s-resoluti.patch
+++ /dev/null
@@ -1,289 +0,0 @@
-From b601cae66c441163a00f73c64d00a29e0840d44e Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Fabiano=20Fid=C3=AAncio?= <fidencio@redhat.com>
-Date: Fri, 24 Mar 2017 15:29:23 +0100
-Subject: [PATCH 46/54] SYSDB: Add methods to deal with the domain's resolution
- order
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-In the following-up patches those newly introduced methods will be used
-to deal with the domainResolutionOrder attribute.
-
-The sysdb_update_domain_resolution_order() method is purposely not
-checking whether a value has changed or not before writing to sysdb and
-while may not be optimal, the readability of the code has increased a
-lot by keeping it as simple as possible.
-
-Tests for these new methods are part of the next commit.
-
-Related:
-https://pagure.io/SSSD/sssd/issue/3001
-
-Signed-off-by: Fabiano Fidêncio <fidencio@redhat.com>
-
-Reviewed-by: Sumit Bose <sbose@redhat.com>
-Reviewed-by: Pavel Březina <pbrezina@redhat.com>
----
- Makefile.am                            |   2 +
- src/db/sysdb.h                         |   2 +
- src/db/sysdb_domain_resolution_order.c | 169 +++++++++++++++++++++++++++++++++
- src/db/sysdb_domain_resolution_order.h |  37 ++++++++
- 4 files changed, 210 insertions(+)
- create mode 100644 src/db/sysdb_domain_resolution_order.c
- create mode 100644 src/db/sysdb_domain_resolution_order.h
-
-diff --git a/Makefile.am b/Makefile.am
-index 359feddef298b0013c726409b7ba8b86504abf09..8052150be32d89813764e9bc436dfcb211a738d6 100644
---- a/Makefile.am
-+++ b/Makefile.am
-@@ -736,6 +736,7 @@ dist_noinst_HEADERS = \
-     src/db/sysdb_private.h \
-     src/db/sysdb_services.h \
-     src/db/sysdb_ssh.h \
-+    src/db/sysdb_domain_resolution_order.h \
-     src/confdb/confdb.h \
-     src/confdb/confdb_private.h \
-     src/confdb/confdb_setup.h \
-@@ -995,6 +996,7 @@ libsss_util_la_SOURCES = \
-     src/db/sysdb_idmap.c \
-     src/db/sysdb_gpo.c \
-     src/db/sysdb_certmap.c \
-+    src/db/sysdb_domain_resolution_order.c \
-     src/monitor/monitor_sbus.c \
-     src/providers/dp_auth_util.c \
-     src/providers/dp_pam_data_util.c \
-diff --git a/src/db/sysdb.h b/src/db/sysdb.h
-index 6762b51bee02911fb97d5d393fad2495504ee5ad..42d2857ed7765c17e7d84b0da93ed07758fbe012 100644
---- a/src/db/sysdb.h
-+++ b/src/db/sysdb.h
-@@ -184,6 +184,8 @@
- #define SYSDB_OVERRIDE_GROUP_CLASS "groupOverride"
- #define SYSDB_OVERRIDE_DN "overrideDN"
- #define SYSDB_OVERRIDE_OBJECT_DN "overrideObjectDN"
-+#define SYSDB_USE_DOMAIN_RESOLUTION_ORDER "useDomainResolutionOrder"
-+#define SYSDB_DOMAIN_RESOLUTION_ORDER "domainResolutionOrder"
- 
- #define SYSDB_NEXTID_FILTER "("SYSDB_NEXTID"=*)"
- 
-diff --git a/src/db/sysdb_domain_resolution_order.c b/src/db/sysdb_domain_resolution_order.c
-new file mode 100644
-index 0000000000000000000000000000000000000000..63774461a1e9f3dc863220d418e29e06d6e6e6df
---- /dev/null
-+++ b/src/db/sysdb_domain_resolution_order.c
-@@ -0,0 +1,169 @@
-+/*
-+    Authors:
-+        Fabiano Fidêncio <fidencio@redhat.com>
-+
-+    Copyright (C) 2017 Red Hat
-+
-+    This program is free software; you can redistribute it and/or modify
-+    it under the terms of the GNU General Public License as published by
-+    the Free Software Foundation; either version 3 of the License, or
-+    (at your option) any later version.
-+
-+    This program is distributed in the hope that it will be useful,
-+    but WITHOUT ANY WARRANTY; without even the implied warranty of
-+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+    GNU General Public License for more details.
-+
-+    You should have received a copy of the GNU General Public License
-+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
-+*/
-+
-+#include <ldb.h>
-+
-+#include "db/sysdb.h"
-+#include "db/sysdb_private.h"
-+
-+static errno_t
-+sysdb_get_domain_resolution_order_string_attr(TALLOC_CTX *mem_ctx,
-+                                              struct sysdb_ctx *sysdb,
-+                                              struct ldb_dn *dn,
-+                                              const char *const *attrs,
-+                                              const char **_attr)
-+{
-+    TALLOC_CTX *tmp_ctx;
-+    struct ldb_result *res;
-+    const char *attr;
-+    errno_t ret;
-+
-+    tmp_ctx = talloc_new(NULL);
-+    if (tmp_ctx == NULL) {
-+        return ENOMEM;
-+    }
-+
-+    ret = ldb_search(sysdb->ldb, tmp_ctx, &res, dn, LDB_SCOPE_BASE, attrs,
-+                     NULL);
-+    if (ret != LDB_SUCCESS) {
-+        ret = EIO;
-+        goto done;
-+    }
-+
-+    if (res->count > 1) {
-+        DEBUG(SSSDBG_OP_FAILURE,
-+              "Base search returned [%d] results, expected 1.\n", res->count);
-+        ret = EINVAL;
-+        goto done;
-+    } else if (res->count == 0) {
-+        ret = ENOENT;
-+        goto done;
-+    } else {
-+        /* res->count == 1 */
-+        attr = ldb_msg_find_attr_as_string(res->msgs[0], attrs[0], NULL);
-+        if (attr == NULL) {
-+            ret = ENOENT;
-+            goto done;
-+        }
-+    }
-+
-+    *_attr = talloc_steal(mem_ctx, attr);
-+
-+    ret = EOK;
-+
-+done:
-+    talloc_free(tmp_ctx);
-+    return ret;
-+}
-+
-+errno_t
-+sysdb_get_domain_resolution_order(TALLOC_CTX *mem_ctx,
-+                                  struct sysdb_ctx *sysdb,
-+                                  struct ldb_dn *dn,
-+                                  const char **_domain_resolution_order)
-+{
-+    TALLOC_CTX *tmp_ctx;
-+    const char *domain_resolution_order = NULL;
-+    const char *attrs[] = { SYSDB_DOMAIN_RESOLUTION_ORDER, NULL };
-+    errno_t ret;
-+
-+    tmp_ctx = talloc_new(NULL);
-+    if (tmp_ctx == NULL) {
-+        return ENOMEM;
-+    }
-+
-+    ret = sysdb_get_domain_resolution_order_string_attr(
-+            tmp_ctx, sysdb, dn, attrs, &domain_resolution_order);
-+    if (ret != EOK && ret != ENOENT) {
-+        DEBUG(SSSDBG_OP_FAILURE,
-+              "sysdb_get_domain_resolution_order_string_attr() failed "
-+              "[%d]: [%s]",
-+              ret, sss_strerror(ret));
-+        goto done;
-+    } else if (ret == ENOENT) {
-+        *_domain_resolution_order = NULL;
-+        goto done;
-+    } else {
-+        /* ret == EOK */
-+        *_domain_resolution_order = talloc_steal(mem_ctx,
-+                                                 domain_resolution_order);
-+    }
-+
-+    ret = EOK;
-+
-+done:
-+    talloc_free(tmp_ctx);
-+    return ret;
-+}
-+
-+errno_t
-+sysdb_update_domain_resolution_order(struct sysdb_ctx *sysdb,
-+                                     struct ldb_dn *dn,
-+                                     const char *domain_resolution_order)
-+{
-+    TALLOC_CTX *tmp_ctx;
-+    struct ldb_message *msg;
-+    errno_t ret;
-+
-+    tmp_ctx = talloc_new(NULL);
-+    if (tmp_ctx == NULL) {
-+        return ENOMEM;
-+    }
-+
-+    msg = ldb_msg_new(tmp_ctx);
-+    if (msg == NULL) {
-+        ret = ENOMEM;
-+        goto done;
-+    }
-+
-+    msg->dn = dn;
-+
-+    ret = ldb_msg_add_empty(msg, SYSDB_DOMAIN_RESOLUTION_ORDER,
-+                            LDB_FLAG_MOD_REPLACE, NULL);
-+    if (ret != LDB_SUCCESS) {
-+        ret = sysdb_error_to_errno(ret);
-+        goto done;
-+    }
-+
-+    if (domain_resolution_order != NULL) {
-+        ret = ldb_msg_add_string(msg, SYSDB_DOMAIN_RESOLUTION_ORDER,
-+                                 domain_resolution_order);
-+        if (ret != LDB_SUCCESS) {
-+            ret = sysdb_error_to_errno(ret);
-+            goto done;
-+        }
-+    }
-+
-+    ret = ldb_modify(sysdb->ldb, msg);
-+    if (ret != LDB_SUCCESS) {
-+        DEBUG(SSSDBG_OP_FAILURE,
-+              "ldb_modify()_failed: [%s][%d][%s]\n",
-+              ldb_strerror(ret), ret, ldb_errstring(sysdb->ldb));
-+        ret = sysdb_error_to_errno(ret);
-+        goto done;
-+    }
-+
-+
-+    ret = EOK;
-+
-+done:
-+    talloc_free(tmp_ctx);
-+    return ret;
-+}
-diff --git a/src/db/sysdb_domain_resolution_order.h b/src/db/sysdb_domain_resolution_order.h
-new file mode 100644
-index 0000000000000000000000000000000000000000..45d2ea63f6bc14cd3184994530846ee6f762d4d0
---- /dev/null
-+++ b/src/db/sysdb_domain_resolution_order.h
-@@ -0,0 +1,37 @@
-+/*
-+    Authors:
-+        Fabiano Fidêncio <fidencio@redhat.com>
-+
-+    Copyright (C) 2017 Red Hat
-+
-+    This program is free software; you can redistribute it and/or modify
-+    it under the terms of the GNU General Public License as published by
-+    the Free Software Foundation; either version 3 of the License, or
-+    (at your option) any later version.
-+
-+    This program is distributed in the hope that it will be useful,
-+    but WITHOUT ANY WARRANTY; without even the implied warranty of
-+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+    GNU General Public License for more details.
-+
-+    You should have received a copy of the GNU General Public License
-+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
-+*/
-+
-+#ifndef _SYSDB_DOMAIN_RESOLUTION_ORDER_H_
-+#define _SYSDB_DOMAIN_RESOLUTION_ORDER_H_
-+
-+#include "db/sysdb.h"
-+
-+errno_t
-+sysdb_get_domain_resolution_order(TALLOC_CTX *mem_ctx,
-+                                  struct sysdb_ctx *sysdb,
-+                                  struct ldb_dn *dn,
-+                                  const char **_domain_resolution_order);
-+
-+errno_t
-+sysdb_update_domain_resolution_order(struct sysdb_ctx *sysdb,
-+                                     struct ldb_dn *dn,
-+                                     const char *domain_resolution_order);
-+
-+#endif /* _SYSDB_DOMAIN_RESOLUTION_ORDER_H_ */
--- 
-2.9.3
-
diff --git a/SOURCES/0046-SYSDB-Better-debugging-for-email-conflicts.patch b/SOURCES/0046-SYSDB-Better-debugging-for-email-conflicts.patch
new file mode 100644
index 0000000..283ef2a
--- /dev/null
+++ b/SOURCES/0046-SYSDB-Better-debugging-for-email-conflicts.patch
@@ -0,0 +1,94 @@
+From de3b178ada423c10c8f14194a64c299ad96e7bf1 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Michal=20=C5=BDidek?= <mzidek@redhat.com>
+Date: Thu, 26 Oct 2017 18:38:42 +0200
+Subject: [PATCH 46/46] SYSDB: Better debugging for email conflicts
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Add DEBUG message when conflicts in FQ names or emails
+are detected.
+
+Also improve man page to hint on how to work around issue
+with conflicting emails.
+
+Note: We store emails in two different attributes in sysdb:
+- SYSDB_USER_EMAIL
+- SYSDB_NAME_ALIAS - this one is lowercased and used in getpwnam
+                     searches.
+
+Resolves:
+https://fedorahosted.org/sssd/ticket/3293
+
+Reviewed-by: Fabiano Fidêncio <fidencio@redhat.com>
+(cherry picked from commit 39d6a3be119b050b0690152b6b443117c8617b1c)
+---
+ src/db/sysdb_ops.c      |  4 +++-
+ src/db/sysdb_search.c   | 15 +++++++++++++++
+ src/man/sssd-ldap.5.xml |  9 +++++++++
+ 3 files changed, 27 insertions(+), 1 deletion(-)
+
+diff --git a/src/db/sysdb_ops.c b/src/db/sysdb_ops.c
+index 635c7db51f516e2217c93016409499e49289004c..1539c41c93e7d6ebd1e544abbb1707df5578cd72 100644
+--- a/src/db/sysdb_ops.c
++++ b/src/db/sysdb_ops.c
+@@ -640,7 +640,9 @@ int sysdb_search_user_by_upn_res(TALLOC_CTX *mem_ctx,
+         goto done;
+     } else if (res->count > 1) {
+         DEBUG(SSSDBG_OP_FAILURE,
+-              "Search for upn [%s] returns more than one result.\n", upn);
++              "Search for upn [%s] returns more than one result. One of the "
++              "possible reasons can be that several users share the same "
++              "email address.\n", upn);
+         ret = EINVAL;
+         goto done;
+     }
+diff --git a/src/db/sysdb_search.c b/src/db/sysdb_search.c
+index f488442afcc6eef114437a7110722759f86fe19e..8083966900429b268a3b984f1cad3d47d1099198 100644
+--- a/src/db/sysdb_search.c
++++ b/src/db/sysdb_search.c
+@@ -218,6 +218,21 @@ int sysdb_getpwnam(TALLOC_CTX *mem_ctx,
+         goto done;
+     }
+ 
++    if (res->count > 1) {
++        /* We expected either 0 or 1 result for search with
++         * SYSDB_PWNAM_FILTER, but we got more. This error
++         * is handled individually depending on what function
++         * called sysdb_getpwnam, so we just print a message
++         * here and let the caller decide what error code to
++         * propagate based on res->count > 1. */
++        DEBUG(SSSDBG_CRIT_FAILURE,
++              "Search for [%s] returned multiple results. It can be an email "
++              "address shared among multiple users or an email address of a "
++              "user that conflicts with another user's fully qualified name. "
++              "SSSD will not be able to handle those users properly.\n",
++              sanitized_name);
++    }
++
+     /* Merge in the timestamps from the fast ts db */
+     ret = sysdb_merge_res_ts_attrs(domain->sysdb, res, attrs);
+     if (ret != EOK) {
+diff --git a/src/man/sssd-ldap.5.xml b/src/man/sssd-ldap.5.xml
+index d38bac3607d294c53ea692130a6b93ced9b0ab82..de596f0da62be9eb61b880b6e1d4a0f33689e25a 100644
+--- a/src/man/sssd-ldap.5.xml
++++ b/src/man/sssd-ldap.5.xml
+@@ -878,6 +878,15 @@
+                             address of the user.
+                         </para>
+                         <para>
++                            Note: If an email address of a user conflicts with
++                            an email address or fully qualified name of another
++                            user, then SSSD will not be able to serve those
++                            users properly. If for some reason several users
++                            need to share the same email address then set
++                            this option to a nonexistent attribute name in
++                            order to disable user lookup/login by email.
++                        </para>
++                        <para>
+                             Default: mail
+                         </para>
+                     </listitem>
+-- 
+2.13.6
+
diff --git a/SOURCES/0047-NSS-Use-enum_ctx-as-memory_context-in-_setnetgrent_s.patch b/SOURCES/0047-NSS-Use-enum_ctx-as-memory_context-in-_setnetgrent_s.patch
new file mode 100644
index 0000000..28b8715
--- /dev/null
+++ b/SOURCES/0047-NSS-Use-enum_ctx-as-memory_context-in-_setnetgrent_s.patch
@@ -0,0 +1,39 @@
+From 3b4479b63ae812c1ef355c1c697caddd882b1b8f Mon Sep 17 00:00:00 2001
+From: Sumit Bose <sbose@redhat.com>
+Date: Mon, 6 Nov 2017 17:03:19 +0100
+Subject: [PATCH 47/47] NSS: Use enum_ctx as memory_context in
+ _setnetgrent_set_timeout()
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+We've noticed some crashes that happened because enum_ctx is already
+freed, but the timeout handler is still called. In order to avoid that,
+let's remove the timeout handler when enum_ctx is freed at other places.
+
+Resolves: https://pagure.io/SSSD/sssd/issue/3523
+
+Signed-off-by: Fabiano Fidêncio <fidencio@redhat.com>
+
+Reviewed-by: Pavel Březina <pbrezina@redhat.com>
+(cherry picked from commit f6a1cef87abdd983d6b5349cd341c9a249826577)
+---
+ src/responder/nss/nss_enum.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/src/responder/nss/nss_enum.c b/src/responder/nss/nss_enum.c
+index aa7d8428f37e943a6b5904495c40ad4b8011b767..da844fbced529f606a3e98669fb7b95e0696ce00 100644
+--- a/src/responder/nss/nss_enum.c
++++ b/src/responder/nss/nss_enum.c
+@@ -283,7 +283,7 @@ nss_setnetgrent_set_timeout(struct tevent_context *ev,
+     timeout = enum_ctx->result[0]->domain->netgroup_timeout;
+ 
+     tv = tevent_timeval_current_ofs(timeout, 0);
+-    te = tevent_add_timer(ev, nss_ctx, tv, nss_setnetgrent_timeout, enum_ctx);
++    te = tevent_add_timer(ev, enum_ctx, tv, nss_setnetgrent_timeout, enum_ctx);
+     if (te == NULL) {
+         DEBUG(SSSDBG_CRIT_FAILURE,
+               "Could not set up life timer for enumeration object.\n");
+-- 
+2.13.6
+
diff --git a/SOURCES/0047-SYSDB-TESTS-Add-tests-for-the-domain-s-resolution-or.patch b/SOURCES/0047-SYSDB-TESTS-Add-tests-for-the-domain-s-resolution-or.patch
deleted file mode 100644
index 459a50f..0000000
--- a/SOURCES/0047-SYSDB-TESTS-Add-tests-for-the-domain-s-resolution-or.patch
+++ /dev/null
@@ -1,259 +0,0 @@
-From 22a10ea2b6b8a56fc040d852867040dce067548a Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Fabiano=20Fid=C3=AAncio?= <fidencio@redhat.com>
-Date: Fri, 24 Mar 2017 23:15:04 +0100
-Subject: [PATCH 47/54] SYSDB/TESTS: Add tests for the domain's resolution
- order methods
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Introduce a new and small set of tests for these new helper methods that
-are going to be used in different parts of the code in the follow-up
-patches.
-
-Related:
-https://pagure.io/SSSD/sssd/issue/3001
-
-Signed-off-by: Fabiano Fidêncio <fidencio@redhat.com>
-
-Reviewed-by: Sumit Bose <sbose@redhat.com>
-Reviewed-by: Pavel Březina <pbrezina@redhat.com>
----
- Makefile.am                                        |  16 ++
- .../cmocka/test_sysdb_domain_resolution_order.c    | 190 +++++++++++++++++++++
- 2 files changed, 206 insertions(+)
- create mode 100644 src/tests/cmocka/test_sysdb_domain_resolution_order.c
-
-diff --git a/Makefile.am b/Makefile.am
-index 8052150be32d89813764e9bc436dfcb211a738d6..450785bf4c482cce1e1440f1336879150537888e 100644
---- a/Makefile.am
-+++ b/Makefile.am
-@@ -263,6 +263,7 @@ if HAVE_CMOCKA
-         test_sysdb_certmap \
-         test_sysdb_sudo \
-         test_sysdb_utils \
-+        test_sysdb_domain_resolution_order \
-         test_wbc_calls \
-         test_be_ptask \
-         test_copy_ccache \
-@@ -2875,6 +2876,21 @@ test_sysdb_utils_LDADD = \
-     libsss_test_common.la \
-     $(NULL)
- 
-+test_sysdb_domain_resolution_order_SOURCES = \
-+    src/tests/cmocka/test_sysdb_domain_resolution_order.c \
-+    $(NULL)
-+test_sysdb_domain_resolution_order_CFLAGS = \
-+    $(AM_CFLAGS) \
-+    $(NULL)
-+test_sysdb_domain_resolution_order_LDADD = \
-+    $(CMOCKA_LIBS) \
-+    $(LDB_LIBS) \
-+    $(POPT_LIBS) \
-+    $(TALLOC_LIBS) \
-+    $(SSSD_INTERNAL_LTLIBS) \
-+    libsss_test_common.la \
-+    $(NULL)
-+
- test_wbc_calls_SOURCES = \
-     src/tests/cmocka/test_wbc_calls.c \
-     src/sss_client/idmap/sss_nss_idmap.c \
-diff --git a/src/tests/cmocka/test_sysdb_domain_resolution_order.c b/src/tests/cmocka/test_sysdb_domain_resolution_order.c
-new file mode 100644
-index 0000000000000000000000000000000000000000..59a85ce431be9ac27c1e8e6b5e4e5f8300af549e
---- /dev/null
-+++ b/src/tests/cmocka/test_sysdb_domain_resolution_order.c
-@@ -0,0 +1,190 @@
-+/*
-+    SSSD
-+
-+    sysdb_domain_resolution_order - Tests for domain resolution order calls
-+
-+    Authors:
-+        Fabiano Fidêncio <fidencio@redhat.com>
-+
-+    Copyright (C) 2017 Red Hat
-+
-+    This program is free software; you can redistribute it and/or modify
-+    it under the terms of the GNU General Public License as published by
-+    the Free Software Foundation; either version 3 of the License, or
-+    (at your option) any later version.
-+
-+    This program is distributed in the hope that it will be useful,
-+    but WITHOUT ANY WARRANTY; without even the implied warranty of
-+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+    GNU General Public License for more details.
-+
-+    You should have received a copy of the GNU General Public License
-+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
-+*/
-+
-+#include <stdarg.h>
-+#include <stddef.h>
-+#include <setjmp.h>
-+#include <cmocka.h>
-+#include <popt.h>
-+
-+#include "tests/cmocka/common_mock.h"
-+#include "tests/common.h"
-+#include "db/sysdb_domain_resolution_order.h"
-+#include "db/sysdb_private.h" /* for sysdb->ldb member */
-+
-+#define TESTS_PATH "tp_" BASE_FILE_STEM
-+#define TEST_CONF_DB "test_sysdb_domain_resolution_order.ldb"
-+
-+#define TEST_DOM_NAME "test_sysdb_domain_resolution_order"
-+
-+#define TEST_ID_PROVIDER "ldap"
-+
-+struct domain_resolution_order_test_ctx {
-+    struct sss_test_ctx *tctx;
-+};
-+
-+static int test_sysdb_domain_resolution_order_setup(void **state)
-+{
-+    struct domain_resolution_order_test_ctx *test_ctx;
-+
-+    assert_true(leak_check_setup());
-+
-+    test_ctx = talloc_zero(global_talloc_context,
-+                           struct domain_resolution_order_test_ctx);
-+    assert_non_null(test_ctx);
-+
-+    test_dom_suite_setup(TESTS_PATH);
-+
-+    test_ctx->tctx = create_dom_test_ctx(test_ctx, TESTS_PATH,
-+                                         TEST_CONF_DB, TEST_DOM_NAME,
-+                                         TEST_ID_PROVIDER, NULL);
-+    assert_non_null(test_ctx->tctx);
-+
-+    *state = test_ctx;
-+    return 0;
-+}
-+
-+static int test_sysdb_domain_resolution_order_teardown(void **state)
-+{
-+    struct domain_resolution_order_test_ctx *test_ctx =
-+        talloc_get_type(*state, struct domain_resolution_order_test_ctx);
-+
-+    test_dom_suite_cleanup(TESTS_PATH, TEST_CONF_DB, TEST_DOM_NAME);
-+    talloc_free(test_ctx);
-+    assert_true(leak_check_teardown());
-+    return 0;
-+}
-+
-+static void test_sysdb_domain_resolution_order_ops(void **state)
-+{
-+    errno_t ret;
-+    struct domain_resolution_order_test_ctx *test_ctx =
-+        talloc_get_type(*state, struct domain_resolution_order_test_ctx);
-+    const char *domains_in = NULL;
-+    const char *domains_out = NULL;
-+    struct ldb_dn *dn;
-+
-+    dn = ldb_dn_new_fmt(test_ctx, test_ctx->tctx->dom->sysdb->ldb,
-+                        SYSDB_DOM_BASE, test_ctx->tctx->dom->name);
-+
-+    /* Adding domainResolutionOrder for the first time */
-+    domains_in = "foo:bar:foobar";
-+    ret = sysdb_update_domain_resolution_order(test_ctx->tctx->dom->sysdb,
-+                                               dn, domains_in);
-+    assert_int_equal(ret, EOK);
-+
-+    ret = sysdb_get_domain_resolution_order(test_ctx,
-+                                            test_ctx->tctx->dom->sysdb, dn,
-+                                            &domains_out);
-+    assert_int_equal(ret, EOK);
-+    assert_true(strcmp(domains_in, domains_out) == 0);
-+
-+    /* Setting the domainResolutionOrder to ":" ...
-+     *
-+     * It means, the domainResolutionOrder is set, but if there's another
-+     * domainResolutionOrder with lower precedence those must be ignored.
-+     */
-+    domains_in = ":";
-+    ret = sysdb_update_domain_resolution_order(test_ctx->tctx->dom->sysdb,
-+                                               dn, domains_in);
-+    assert_int_equal(ret, EOK);
-+
-+    ret = sysdb_get_domain_resolution_order(test_ctx,
-+                                            test_ctx->tctx->dom->sysdb, dn,
-+                                            &domains_out);
-+    assert_int_equal(ret, EOK);
-+    assert_true(strcmp(domains_in, domains_out) == 0);
-+
-+    /* Changing the domainResolutionOrder */
-+    domains_in = "bar:foobar:foo";
-+    ret = sysdb_update_domain_resolution_order(test_ctx->tctx->dom->sysdb,
-+                                               dn, domains_in);
-+    assert_int_equal(ret, EOK);
-+
-+    ret = sysdb_get_domain_resolution_order(test_ctx,
-+                                            test_ctx->tctx->dom->sysdb, dn,
-+                                            &domains_out);
-+    assert_int_equal(ret, EOK);
-+    assert_true(strcmp(domains_out, domains_out) == 0);
-+
-+    /* Removing the domainResolutionOrder attribute */
-+    domains_in = NULL;
-+    ret = sysdb_update_domain_resolution_order(test_ctx->tctx->dom->sysdb,
-+                                               dn, domains_in);
-+    assert_int_equal(ret, EOK);
-+
-+    ret = sysdb_get_domain_resolution_order(test_ctx,
-+                                            test_ctx->tctx->dom->sysdb, dn,
-+                                            &domains_out);
-+    assert_int_equal(ret, ENOENT);
-+    assert_true(domains_out == NULL);
-+}
-+
-+int main(int argc, const char *argv[])
-+{
-+    int rv;
-+    int no_cleanup = 0;
-+    poptContext pc;
-+    int opt;
-+    struct poptOption long_options[] = {
-+        POPT_AUTOHELP
-+        SSSD_DEBUG_OPTS
-+        {"no-cleanup", 'n', POPT_ARG_NONE, &no_cleanup, 0,
-+         _("Do not delete the test database after a test run"), NULL },
-+        POPT_TABLEEND
-+    };
-+
-+    const struct CMUnitTest tests[] = {
-+        cmocka_unit_test_setup_teardown(test_sysdb_domain_resolution_order_ops,
-+                                        test_sysdb_domain_resolution_order_setup,
-+                                        test_sysdb_domain_resolution_order_teardown),
-+    };
-+
-+    /* Set debug level to invalid value so we can deside if -d 0 was used. */
-+    debug_level = SSSDBG_INVALID;
-+
-+    pc = poptGetContext(argv[0], argc, argv, long_options, 0);
-+    while((opt = poptGetNextOpt(pc)) != -1) {
-+        switch(opt) {
-+        default:
-+            fprintf(stderr, "\nInvalid option %s: %s\n\n",
-+                    poptBadOption(pc, 0), poptStrerror(opt));
-+            poptPrintUsage(pc, stderr, 0);
-+            return 1;
-+        }
-+    }
-+    poptFreeContext(pc);
-+
-+    DEBUG_CLI_INIT(debug_level);
-+
-+    tests_set_cwd();
-+    test_dom_suite_cleanup(TESTS_PATH, TEST_CONF_DB, LOCAL_SYSDB_FILE);
-+    test_dom_suite_setup(TESTS_PATH);
-+    rv = cmocka_run_group_tests(tests, NULL, NULL);
-+
-+    if (rv == 0 && no_cleanup == 0) {
-+        test_dom_suite_cleanup(TESTS_PATH, TEST_CONF_DB, LOCAL_SYSDB_FILE);
-+    }
-+    return rv;
-+}
--- 
-2.9.3
-
diff --git a/SOURCES/0048-IPA-Get-ipaDomainsResolutionOrder-from-ipaConfig.patch b/SOURCES/0048-IPA-Get-ipaDomainsResolutionOrder-from-ipaConfig.patch
deleted file mode 100644
index 3dcbc82..0000000
--- a/SOURCES/0048-IPA-Get-ipaDomainsResolutionOrder-from-ipaConfig.patch
+++ /dev/null
@@ -1,369 +0,0 @@
-From 4ff821a9a37cb43f9c34faef4b5ccbdc8dc6a7e8 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Fabiano=20Fid=C3=AAncio?= <fidencio@redhat.com>
-Date: Wed, 22 Mar 2017 13:40:20 +0100
-Subject: [PATCH 48/54] IPA: Get ipaDomainsResolutionOrder from ipaConfig
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-ipaDomainsResolutionOrder provides a list of domains that have to be
-looked up firstly during cache_req searches.
-
-This commit only fetches this list from the server and stores its value
-at sysdb so we can make use of it later on this patch series.
-
-There are no tests for newly introduced sysdb methods are those are
-basically only calling sysdb_update_domain_resolution_order(),
-sysdb_get_domain_resolution_order() and
-sysdb_get_use_domain_resolution_order() which are have tests written
-for.
-
-Related:
-https://pagure.io/SSSD/sssd/issue/3001
-
-Signed-off-by: Fabiano Fidêncio <fidencio@redhat.com>
-
-Reviewed-by: Sumit Bose <sbose@redhat.com>
-Reviewed-by: Pavel Březina <pbrezina@redhat.com>
----
- src/db/sysdb.h                     |  11 +++
- src/db/sysdb_subdomains.c          |  67 +++++++++++++++
- src/providers/ipa/ipa_subdomains.c | 168 ++++++++++++++++++++++++++++++++++---
- 3 files changed, 234 insertions(+), 12 deletions(-)
-
-diff --git a/src/db/sysdb.h b/src/db/sysdb.h
-index 42d2857ed7765c17e7d84b0da93ed07758fbe012..75a07d4d2effb028ec654342113f8478e1eba10e 100644
---- a/src/db/sysdb.h
-+++ b/src/db/sysdb.h
-@@ -489,6 +489,17 @@ int sysdb_transaction_cancel(struct sysdb_ctx *sysdb);
- /* functions related to subdomains */
- errno_t sysdb_domain_create(struct sysdb_ctx *sysdb, const char *domain_name);
- 
-+errno_t sysdb_domain_get_domain_resolution_order(
-+                                        TALLOC_CTX *mem_ctx,
-+                                        struct sysdb_ctx *sysdb,
-+                                        const char *domain_name,
-+                                        const char **_domain_resolution_order);
-+
-+errno_t sysdb_domain_update_domain_resolution_order(
-+                                        struct sysdb_ctx *sysdb,
-+                                        const char *domain_name,
-+                                        const char *domain_resolution_order);
-+
- errno_t sysdb_subdomain_store(struct sysdb_ctx *sysdb,
-                               const char *name, const char *realm,
-                               const char *flat_name, const char *domain_id,
-diff --git a/src/db/sysdb_subdomains.c b/src/db/sysdb_subdomains.c
-index 916dbba153d8c08837425f6fd29a20f5a6aa9fc9..e2a4f7bb1fcdf20b6b7e04efc7f396d1c3d08f0f 100644
---- a/src/db/sysdb_subdomains.c
-+++ b/src/db/sysdb_subdomains.c
-@@ -22,6 +22,7 @@
- 
- #include "util/util.h"
- #include "db/sysdb_private.h"
-+#include "db/sysdb_domain_resolution_order.h"
- 
- static errno_t
- check_subdom_config_file(struct confdb_ctx *confdb,
-@@ -1210,3 +1211,69 @@ done:
-     talloc_free(tmp_ctx);
-     return ret;
- }
-+
-+errno_t
-+sysdb_domain_get_domain_resolution_order(TALLOC_CTX *mem_ctx,
-+                                         struct sysdb_ctx *sysdb,
-+                                         const char *domain_name,
-+                                         const char **_domain_resolution_order)
-+{
-+    TALLOC_CTX *tmp_ctx;
-+    struct ldb_dn *dn;
-+    errno_t ret;
-+
-+    tmp_ctx = talloc_new(NULL);
-+    if (tmp_ctx == NULL) {
-+        return ENOMEM;
-+    }
-+
-+    dn = ldb_dn_new_fmt(tmp_ctx, sysdb->ldb, SYSDB_DOM_BASE, domain_name);
-+    if (dn == NULL) {
-+        ret = ENOMEM;
-+        goto done;
-+    }
-+
-+    ret = sysdb_get_domain_resolution_order(mem_ctx, sysdb, dn,
-+                                            _domain_resolution_order);
-+
-+done:
-+    talloc_free(tmp_ctx);
-+    return ret;
-+}
-+
-+errno_t
-+sysdb_domain_update_domain_resolution_order(struct sysdb_ctx *sysdb,
-+                                            const char *domain_name,
-+                                            const char *domain_resolution_order)
-+{
-+
-+    TALLOC_CTX *tmp_ctx;
-+    struct ldb_dn *dn;
-+    errno_t ret;
-+
-+    tmp_ctx = talloc_new(NULL);
-+    if (tmp_ctx == NULL) {
-+        return ENOMEM;
-+    }
-+
-+    dn = ldb_dn_new_fmt(tmp_ctx, sysdb->ldb, SYSDB_DOM_BASE, domain_name);
-+    if (dn == NULL) {
-+        ret = ENOMEM;
-+        goto done;
-+    }
-+
-+    ret = sysdb_update_domain_resolution_order(sysdb, dn,
-+                                               domain_resolution_order);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_OP_FAILURE,
-+              "sysdb_update_domain_resolution_order() failed [%d]: [%s].\n",
-+              ret, sss_strerror(ret));
-+        goto done;
-+    }
-+
-+    ret = EOK;
-+
-+done:
-+    talloc_free(tmp_ctx);
-+    return ret;
-+}
-diff --git a/src/providers/ipa/ipa_subdomains.c b/src/providers/ipa/ipa_subdomains.c
-index a07b88fe2f499353293ba90345552413c9792f4b..01a0ce812d861b24565d2f71f27d6b8ceb2965bc 100644
---- a/src/providers/ipa/ipa_subdomains.c
-+++ b/src/providers/ipa/ipa_subdomains.c
-@@ -29,6 +29,7 @@
- #include "providers/ipa/ipa_common.h"
- #include "providers/ipa/ipa_id.h"
- #include "providers/ipa/ipa_opts.h"
-+#include "providers/ipa/ipa_config.h"
- 
- #include <ctype.h>
- 
-@@ -51,6 +52,8 @@
- 
- #define IPA_ASSIGNED_ID_VIEW "ipaAssignedIDView"
- 
-+#define IPA_DOMAIN_RESOLUTION_ORDER "ipaDomainResolutionOrder"
-+
- /* do not refresh more often than every 5 seconds for now */
- #define IPA_SUBDOMAIN_REFRESH_LIMIT 5
- 
-@@ -1681,6 +1684,117 @@ static errno_t ipa_subdomains_view_name_recv(struct tevent_req *req)
-     return EOK;
- }
- 
-+struct ipa_domain_resolution_order_state {
-+    struct sss_domain_info *domain;
-+};
-+
-+static void ipa_domain_resolution_order_done(struct tevent_req *subreq);
-+
-+static struct tevent_req *
-+ipa_domain_resolution_order_send(TALLOC_CTX *mem_ctx,
-+                                 struct tevent_context *ev,
-+                                 struct ipa_subdomains_ctx *sd_ctx,
-+                                 struct sdap_handle *sh)
-+{
-+    struct ipa_domain_resolution_order_state *state;
-+    struct tevent_req *subreq;
-+    struct tevent_req *req;
-+    const char *attrs[] = {IPA_DOMAIN_RESOLUTION_ORDER, NULL};
-+    errno_t ret;
-+
-+    req = tevent_req_create(mem_ctx, &state,
-+                            struct ipa_domain_resolution_order_state);
-+    if (req == NULL) {
-+        DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create() failed\n");
-+        return NULL;
-+    }
-+
-+    state->domain = sd_ctx->be_ctx->domain;
-+
-+    subreq = ipa_get_config_send(state, ev, sh, sd_ctx->sdap_id_ctx->opts,
-+                                 state->domain->name, attrs);
-+    if (subreq == NULL) {
-+        ret = ENOMEM;
-+        goto immediately;
-+    }
-+
-+    tevent_req_set_callback(subreq, ipa_domain_resolution_order_done, req);
-+
-+    return req;
-+
-+immediately:
-+    if (ret == EOK) {
-+        tevent_req_done(req);
-+    } else {
-+        tevent_req_error(req, ret);
-+    }
-+    tevent_req_post(req, ev);
-+
-+    return req;
-+}
-+
-+static void ipa_domain_resolution_order_done(struct tevent_req *subreq)
-+{
-+    struct ipa_domain_resolution_order_state *state;
-+    struct tevent_req *req;
-+    struct sysdb_attrs *config = NULL;
-+    const char *domain_resolution_order = NULL;
-+    errno_t ret;
-+
-+    req = tevent_req_callback_data(subreq, struct tevent_req);
-+    state = tevent_req_data(req, struct ipa_domain_resolution_order_state);
-+
-+    ret = ipa_get_config_recv(subreq, state, &config);
-+    talloc_zfree(subreq);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_OP_FAILURE,
-+              "Failed to get the domains' resolution order configuration "
-+              "from the server [%d]: %s\n",
-+              ret, sss_strerror(ret));
-+        goto done;
-+    }
-+
-+    if (config != NULL) {
-+        ret = sysdb_attrs_get_string(config, IPA_DOMAIN_RESOLUTION_ORDER,
-+                                     &domain_resolution_order);
-+        if (ret != EOK && ret != ENOENT) {
-+            DEBUG(SSSDBG_OP_FAILURE,
-+                  "Failed to get the domains' resolution order configuration "
-+                  "value [%d]: %s\n",
-+                  ret, sss_strerror(ret));
-+            goto done;
-+        } else if (ret == ENOENT) {
-+            domain_resolution_order = NULL;
-+        }
-+    }
-+
-+    ret = sysdb_domain_update_domain_resolution_order(
-+                        state->domain->sysdb, state->domain->name,
-+                        domain_resolution_order);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_OP_FAILURE,
-+              "sysdb_domain_update_resolution_order() [%d]: [%s].\n",
-+              ret, sss_strerror(ret));
-+        goto done;
-+    }
-+
-+    ret = EOK;
-+
-+done:
-+    if (ret != EOK) {
-+        tevent_req_error(req, ret);
-+        return;
-+    }
-+
-+    tevent_req_done(req);
-+}
-+
-+static errno_t ipa_domain_resolution_order_recv(struct tevent_req *req)
-+{
-+    TEVENT_REQ_RETURN_ON_ERROR(req);
-+
-+    return EOK;
-+}
- 
- struct ipa_subdomains_refresh_state {
-     struct tevent_context *ev;
-@@ -1695,6 +1809,7 @@ static void ipa_subdomains_refresh_certmap_done(struct tevent_req *subreq);
- static void ipa_subdomains_refresh_master_done(struct tevent_req *subreq);
- static void ipa_subdomains_refresh_slave_done(struct tevent_req *subreq);
- static void ipa_subdomains_refresh_view_done(struct tevent_req *subreq);
-+static void ipa_domain_refresh_resolution_order_done(struct tevent_req *subreq);
- 
- static struct tevent_req *
- ipa_subdomains_refresh_send(TALLOC_CTX *mem_ctx,
-@@ -1916,7 +2031,6 @@ static void ipa_subdomains_refresh_view_done(struct tevent_req *subreq)
- {
-     struct ipa_subdomains_refresh_state *state;
-     struct tevent_req *req;
--    int dp_error;
-     errno_t ret;
- 
-     req = tevent_req_callback_data(subreq, struct tevent_req);
-@@ -1924,24 +2038,55 @@ static void ipa_subdomains_refresh_view_done(struct tevent_req *subreq)
- 
-     ret = ipa_subdomains_view_name_recv(subreq);
-     talloc_zfree(subreq);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_CRIT_FAILURE,
-+              "Unable to get view name [%d]: %s\n",
-+              ret, sss_strerror(ret));
-+        tevent_req_error(req, ret);
-+        return;
-+    }
-+
-+    subreq = ipa_domain_resolution_order_send(state, state->ev, state->sd_ctx,
-+                                            sdap_id_op_handle(state->sdap_op));
-+    if (subreq == NULL) {
-+        tevent_req_error(req, ENOMEM);
-+        return;
-+    }
-+
-+    tevent_req_set_callback(subreq,
-+                            ipa_domain_refresh_resolution_order_done,
-+                            req);
-+}
-+
-+static void
-+ipa_domain_refresh_resolution_order_done(struct tevent_req *subreq)
-+{
-+    struct ipa_subdomains_refresh_state *state;
-+    struct tevent_req *req;
-+    int dp_error;
-+    errno_t ret;
-+
-+    req = tevent_req_callback_data(subreq, struct tevent_req);
-+    state = tevent_req_data(req, struct ipa_subdomains_refresh_state);
-+
-+    ret = ipa_domain_resolution_order_recv(subreq);
-+    talloc_zfree(subreq);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_MINOR_FAILURE,
-+              "Unable to get the domains order resolution [%d]: %s\n",
-+              ret, sss_strerror(ret));
-+        tevent_req_error(req, ret);
-+        return;
-+    }
-+
-     ret = sdap_id_op_done(state->sdap_op, ret, &dp_error);
-     if (dp_error == DP_ERR_OK && ret != EOK) {
-         /* retry */
-         ret = ipa_subdomains_refresh_retry(req);
--        if (ret != EOK) {
--            goto done;
--        }
--        return;
-     } else if (dp_error == DP_ERR_OFFLINE) {
-         ret = ERR_OFFLINE;
--        goto done;
--    } else if (ret != EOK) {
--        DEBUG(SSSDBG_CRIT_FAILURE, "Unable to get view name "
--              "[%d]: %s\n", ret, sss_strerror(ret));
--        goto done;
-     }
- 
--done:
-     if (ret != EOK) {
-         DEBUG(SSSDBG_TRACE_FUNC, "Unable to refresh subdomains [%d]: %s\n",
-               ret, sss_strerror(ret));
-@@ -1949,7 +2094,6 @@ done:
-         return;
-     }
- 
--    DEBUG(SSSDBG_TRACE_FUNC, "Subdomains refreshed.\n");
-     tevent_req_done(req);
- }
- 
--- 
-2.9.3
-
diff --git a/SOURCES/0048-TOOLS-Add-a-new-sssctl-command-access-report.patch b/SOURCES/0048-TOOLS-Add-a-new-sssctl-command-access-report.patch
new file mode 100644
index 0000000..5788df9
--- /dev/null
+++ b/SOURCES/0048-TOOLS-Add-a-new-sssctl-command-access-report.patch
@@ -0,0 +1,504 @@
+From 73a04a5c53c0e7701aa7753fd459ffbea52e28b8 Mon Sep 17 00:00:00 2001
+From: Jakub Hrozek <jhrozek@redhat.com>
+Date: Mon, 23 Oct 2017 18:08:12 +0200
+Subject: [PATCH 48/57] TOOLS: Add a new sssctl command access-report
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Resolves:
+https://pagure.io/SSSD/sssd/issue/2840
+
+Reviewed-by: Pavel Březina <pbrezina@redhat.com>
+Reviewed-by: Fabiano Fidêncio <fidencio@redhat.com>
+(cherry picked from commit 3ee8659bc6a77a78bc6c61b9650a36bd18ea95c8)
+---
+ Makefile.am                             |   1 +
+ src/tools/sssctl/sssctl.c               |   1 +
+ src/tools/sssctl/sssctl.h               |   5 +
+ src/tools/sssctl/sssctl_access_report.c | 435 ++++++++++++++++++++++++++++++++
+ 4 files changed, 442 insertions(+)
+ create mode 100644 src/tools/sssctl/sssctl_access_report.c
+
+diff --git a/Makefile.am b/Makefile.am
+index 16bcb4efc028b05c1196249245f4f3091b9366af..5917bd904054055a259eb69217282e4fb914c700 100644
+--- a/Makefile.am
++++ b/Makefile.am
+@@ -1754,6 +1754,7 @@ sssctl_SOURCES = \
+     src/tools/sssctl/sssctl_sifp.c \
+     src/tools/sssctl/sssctl_config.c \
+     src/tools/sssctl/sssctl_user_checks.c \
++    src/tools/sssctl/sssctl_access_report.c \
+     $(SSSD_TOOLS_OBJ) \
+     $(NULL)
+ sssctl_LDADD = \
+diff --git a/src/tools/sssctl/sssctl.c b/src/tools/sssctl/sssctl.c
+index 1e061c00d2238bf34adff4183e560dc127dd62c7..eee2d613966a5dda81627d2e225bfdc9bade4041 100644
+--- a/src/tools/sssctl/sssctl.c
++++ b/src/tools/sssctl/sssctl.c
+@@ -264,6 +264,7 @@ int main(int argc, const char **argv)
+         SSS_TOOL_COMMAND("domain-list", "List available domains", 0, sssctl_domain_list),
+         SSS_TOOL_COMMAND("domain-status", "Print information about domain", 0, sssctl_domain_status),
+         SSS_TOOL_COMMAND("user-checks", "Print information about a user and check authentication", 0, sssctl_user_checks),
++        SSS_TOOL_COMMAND("access-report", "Generate access report for a domain", 0, sssctl_access_report),
+         SSS_TOOL_DELIMITER("Information about cached content:"),
+         SSS_TOOL_COMMAND("user-show", "Information about cached user", 0, sssctl_user_show),
+         SSS_TOOL_COMMAND("group-show", "Information about cached group", 0, sssctl_group_show),
+diff --git a/src/tools/sssctl/sssctl.h b/src/tools/sssctl/sssctl.h
+index 22ca5d41e2c084e64b58bc5aa066414b002e7e8b..70fc19eff07317c264978a1ecb9159ae3acdfced 100644
+--- a/src/tools/sssctl/sssctl.h
++++ b/src/tools/sssctl/sssctl.h
+@@ -133,4 +133,9 @@ errno_t sssctl_config_check(struct sss_cmdline *cmdline,
+ errno_t sssctl_user_checks(struct sss_cmdline *cmdline,
+                            struct sss_tool_ctx *tool_ctx,
+                            void *pvt);
++
++errno_t sssctl_access_report(struct sss_cmdline *cmdline,
++                             struct sss_tool_ctx *tool_ctx,
++                             void *pvt);
++
+ #endif /* _SSSCTL_H_ */
+diff --git a/src/tools/sssctl/sssctl_access_report.c b/src/tools/sssctl/sssctl_access_report.c
+new file mode 100644
+index 0000000000000000000000000000000000000000..11172329817b4dedaca480ab8a4537149853c330
+--- /dev/null
++++ b/src/tools/sssctl/sssctl_access_report.c
+@@ -0,0 +1,435 @@
++/*
++    Copyright (C) 2017 Red Hat
++
++    This program is free software; you can redistribute it and/or modify
++    it under the terms of the GNU Lesser General Public License as published by
++    the Free Software Foundation; either version 3 of the License, or
++    (at your option) any later version.
++
++    This program is distributed in the hope that it will be useful,
++    but WITHOUT ANY WARRANTY; without even the implied warranty of
++    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++    GNU Lesser General Public License for more details.
++
++    You should have received a copy of the GNU Lesser General Public License
++    along with this program.  If not, see <http://www.gnu.org/licenses/>.
++*/
++
++#include <security/pam_appl.h>
++
++#include "util/util.h"
++#include "tools/common/sss_tools.h"
++#include "tools/sssctl/sssctl.h"
++
++/*
++ * We're searching the cache directly..
++ */
++#include "providers/ipa/ipa_hbac_private.h"
++#include "providers/ipa/ipa_rules_common.h"
++
++#ifdef HAVE_SECURITY_PAM_MISC_H
++# include <security/pam_misc.h>
++#elif defined(HAVE_SECURITY_OPENPAM_H)
++# include <security/openpam.h>
++#endif
++
++#ifdef HAVE_SECURITY_PAM_MISC_H
++static struct pam_conv conv = {
++    misc_conv,
++    NULL
++};
++#elif defined(HAVE_SECURITY_OPENPAM_H)
++static struct pam_conv conv = {
++    openpam_ttyconv,
++    NULL
++};
++#else
++# error "Missing text based pam conversation function"
++#endif
++
++#ifndef DEFAULT_SERVICE
++#define DEFAULT_SERVICE "system-auth"
++#endif /* DEFAULT_SERVICE */
++
++#ifndef DEFAULT_USER
++#define DEFAULT_USER "admin"
++#endif /* DEFAULT_USER */
++
++typedef errno_t (*sssctl_dom_access_reporter_fn)(struct sss_tool_ctx *tool_ctx,
++                                                 const char *user,
++                                                 const char *service,
++                                                 struct sss_domain_info *domain);
++
++static errno_t run_pam_acct(struct sss_tool_ctx *tool_ctx,
++                            const char *user,
++                            const char *service,
++                            struct sss_domain_info *domain)
++{
++    errno_t ret;
++    pam_handle_t *pamh;
++
++    ret = pam_start(service, user, &conv, &pamh);
++    if (ret != PAM_SUCCESS) {
++        ERROR("pam_start failed: %s\n", pam_strerror(pamh, ret));
++        return EIO;
++    }
++
++    ret = pam_acct_mgmt(pamh, 0);
++    pam_end(pamh, ret);
++    return ret;
++}
++
++static errno_t get_rdn_value(TALLOC_CTX *mem_ctx,
++                             struct sss_domain_info *dom,
++                             const char *dn_attr,
++                             const char **_rdn_value)
++{
++    errno_t ret;
++    TALLOC_CTX *tmp_ctx;
++    struct ldb_dn *dn = NULL;
++    const struct ldb_val *rdn_val;
++    const char *rdn_str;
++
++    tmp_ctx = talloc_new(NULL);
++    if (tmp_ctx == NULL) {
++        return ENOMEM;
++    }
++
++    dn = ldb_dn_new(tmp_ctx, sysdb_ctx_get_ldb(dom->sysdb), dn_attr);
++    if (dn == NULL) {
++        ret = ENOMEM;
++        goto done;
++    }
++
++    rdn_val = ldb_dn_get_rdn_val(dn);
++    if (rdn_val == NULL) {
++        DEBUG(SSSDBG_OP_FAILURE, "No RDN value?\n");
++        ret = ENOMEM;
++        goto done;
++    }
++
++    rdn_str = talloc_strndup(tmp_ctx,
++                               (const char *)rdn_val->data,
++                               rdn_val->length);
++    if (rdn_str == NULL) {
++        ret = ENOMEM;
++        goto done;
++    }
++
++    ret = EOK;
++    *_rdn_value = talloc_steal(mem_ctx, rdn_str);
++done:
++    talloc_zfree(tmp_ctx);
++    return ret;
++}
++
++static errno_t is_member_group(struct sss_domain_info *dom,
++                               const char *dn_attr,
++                               const char *group_rdn,
++                               bool *_is_group)
++{
++    const char *comp_name;
++    const struct ldb_val *comp_val;
++    TALLOC_CTX *tmp_ctx;
++    bool is_group = false;
++    errno_t ret;
++    struct ldb_dn *dn = NULL;
++
++    tmp_ctx = talloc_new(NULL);
++    if (tmp_ctx == NULL) {
++        return ENOMEM;
++    }
++
++    dn = ldb_dn_new(tmp_ctx, sysdb_ctx_get_ldb(dom->sysdb), dn_attr);
++    if (dn == NULL) {
++        ret = ENOMEM;
++        goto done;
++    }
++
++    comp_name = ldb_dn_get_component_name(dn, 1);
++    comp_val = ldb_dn_get_component_val(dn, 1);
++    if (strcasecmp("cn", comp_name) == 0
++            && strncasecmp(group_rdn,
++                           (const char *) comp_val->data,
++                           comp_val->length) == 0) {
++        is_group = true;
++    }
++
++    ret = EOK;
++done:
++    *_is_group = is_group;
++    talloc_zfree(tmp_ctx);
++    return ret;
++}
++
++static void print_category(struct sss_domain_info *domain,
++                           struct ldb_message *rule_msg,
++                           const char *category_attr_name,
++                           const char *category_label)
++{
++    struct ldb_message_element *category_attr;
++
++    category_attr = ldb_msg_find_element(rule_msg, category_attr_name);
++    if (category_attr == NULL) {
++        DEBUG(SSSDBG_MINOR_FAILURE, "Cannot find %s\n", category_attr_name);
++        return;
++    }
++
++    if (category_attr->num_values > 0) {
++        PRINT("\t%s: ", category_label);
++        for (unsigned i = 0; i < category_attr->num_values; i++) {
++            PRINT("%s%s",
++                  i > 0 ? ", " : "",
++                  (const char *) category_attr->values[i].data);
++        }
++        PRINT("\n");
++    }
++}
++
++static void print_member_attr(struct sss_domain_info *domain,
++                              struct ldb_message *rule_msg,
++                              const char *member_attr_name,
++                              const char *group_rdn,
++                              const char *object_label,
++                              const char *group_label)
++{
++    errno_t ret;
++    TALLOC_CTX *tmp_ctx = NULL;
++    const char **member_names = NULL;
++    size_t name_count = 0;
++    const char **member_group_names = NULL;
++    size_t group_count = 0;
++    struct ldb_message_element *member_attr = NULL;
++
++    tmp_ctx = talloc_new(NULL);
++    if (tmp_ctx == NULL) {
++        return;
++    }
++
++    member_attr = ldb_msg_find_element(rule_msg, member_attr_name);
++    if (member_attr == NULL) {
++        DEBUG(SSSDBG_MINOR_FAILURE, "Cannot find %s\n", member_attr_name);
++        goto done;
++    }
++
++    member_names = talloc_zero_array(tmp_ctx,
++                                      const char *,
++                                      member_attr->num_values + 1);
++    member_group_names = talloc_zero_array(tmp_ctx,
++                                           const char *,
++                                           member_attr->num_values + 1);
++    if (member_names == NULL || member_group_names == NULL) {
++        DEBUG(SSSDBG_CRIT_FAILURE, "OOM?\n");
++        goto done;
++    }
++
++    for (size_t i = 0; i < member_attr->num_values; i++) {
++        bool is_group;
++        const char *rdn_string;
++        const char *dn_attr;
++
++        dn_attr = (const char *) member_attr->values[i].data;
++
++        ret = is_member_group(domain, dn_attr, group_rdn, &is_group);
++        if (ret != EOK) {
++            continue;
++        }
++
++        ret = get_rdn_value(tmp_ctx, domain, dn_attr, &rdn_string);
++        if (ret != EOK) {
++            continue;
++        }
++
++        if (is_group == false) {
++            member_names[name_count] = talloc_steal(member_names,
++                                                    rdn_string);
++            if (member_names[name_count] == NULL) {
++                goto done;
++            }
++            name_count++;
++        } else {
++            member_group_names[group_count] = talloc_strdup(member_group_names,
++                                                            rdn_string);
++            if (member_group_names[group_count] == NULL) {
++                goto done;
++            }
++            group_count++;
++        }
++    }
++
++    if (member_names[0] != NULL) {
++        PRINT("\t%s: ", object_label);
++        for (int i = 0; member_names[i]; i++) {
++            PRINT("%s%s", i > 0 ? ", " : "", member_names[i]);
++        }
++        PRINT("\n");
++    }
++
++    if (member_group_names[0] != NULL) {
++        PRINT("\t%s: ", group_label);
++        for (int i = 0; member_group_names[i]; i++) {
++            PRINT("%s%s", i > 0 ? ", " : "", member_group_names[i]);
++        }
++        PRINT("\n");
++    }
++
++done:
++    talloc_free(tmp_ctx);
++}
++
++static void print_ipa_hbac_rule(struct sss_domain_info *domain,
++                                struct ldb_message *rule_msg)
++{
++    struct ldb_message_element *el;
++
++    el = ldb_msg_find_element(rule_msg, IPA_CN);
++    if (el == NULL || el->num_values < 1) {
++        DEBUG(SSSDBG_MINOR_FAILURE, "A rule with no name\n");
++        return;
++    }
++
++    PRINT("Rule name: %1$s\n", el->values[0].data);
++
++    print_member_attr(domain,
++                      rule_msg,
++                      IPA_MEMBER_USER,
++                      "groups",
++                      _("Member users"),
++                      _("Member groups"));
++    print_category(domain,
++                   rule_msg,
++                   IPA_USER_CATEGORY,
++                   _("User category"));
++
++    print_member_attr(domain,
++                      rule_msg,
++                      IPA_MEMBER_SERVICE,
++                      "hbacservicegroups",
++                      _("Member services"),
++                      _("Member service groups"));
++    print_category(domain,
++                   rule_msg,
++                   IPA_SERVICE_CATEGORY,
++                   _("Service category"));
++
++    PRINT("\n");
++}
++
++static errno_t sssctl_ipa_access_report(struct sss_tool_ctx *tool_ctx,
++                                        const char *user,
++                                        const char *service,
++                                        struct sss_domain_info *domain)
++{
++    TALLOC_CTX *tmp_ctx = NULL;
++    const char *filter = NULL;
++    errno_t ret;
++    const char *attrs[] = {
++        OBJECTCLASS,
++        IPA_CN,
++        IPA_MEMBER_USER,
++        IPA_USER_CATEGORY,
++        IPA_MEMBER_SERVICE,
++        IPA_SERVICE_CATEGORY,
++        IPA_MEMBER_HOST,
++        IPA_HOST_CATEGORY,
++        NULL,
++    };
++    size_t rule_count;
++    struct ldb_message **msgs = NULL;
++
++    /* Run the pam account phase to make sure the rules are fetched by SSSD */
++    ret = run_pam_acct(tool_ctx, user, service, domain);
++    if (ret != PAM_SUCCESS && ret != PAM_PERM_DENIED) {
++        ERROR("Cannot run the PAM account phase, reporting stale rules\n");
++        /* Non-fatal */
++    }
++
++    tmp_ctx = talloc_new(tool_ctx);
++    if (tmp_ctx == NULL) {
++        return ENOMEM;
++    }
++
++    filter = talloc_asprintf(tmp_ctx, "(objectClass=%s)", IPA_HBAC_RULE);
++    if (filter == NULL) {
++        ret = ENOMEM;
++        goto done;
++    }
++
++    ret = sysdb_search_custom(tmp_ctx, domain, filter,
++                              HBAC_RULES_SUBDIR, attrs,
++                              &rule_count, &msgs);
++    if (ret != EOK && ret != ENOENT) {
++        DEBUG(SSSDBG_CRIT_FAILURE, "Error looking up HBAC rules\n");
++        goto done;
++    }
++
++    if (ret == ENOENT) {
++        PRINT("No cached rules. All users will be denied access\n");
++        ret = EOK;
++        goto done;
++    }
++
++    PRINT("%1$zu rules cached\n\n", rule_count);
++
++    for (size_t i = 0; i < rule_count; i++) {
++        print_ipa_hbac_rule(domain, msgs[i]);
++    }
++
++    ret = EOK;
++done:
++    talloc_zfree(tmp_ctx);
++    return ret;
++}
++
++sssctl_dom_access_reporter_fn get_report_fn(const char *provider)
++{
++    if (strcmp(provider, "ipa") == 0) {
++        return sssctl_ipa_access_report;
++    }
++
++    return NULL;
++}
++
++errno_t sssctl_access_report(struct sss_cmdline *cmdline,
++                             struct sss_tool_ctx *tool_ctx,
++                             void *pvt)
++{
++    errno_t ret;
++    const char *domname = NULL;
++    sssctl_dom_access_reporter_fn reporter;
++    struct sss_domain_info *dom;
++    const char *user = DEFAULT_USER;
++    const char *service = DEFAULT_SERVICE;
++
++    /* Parse command line. */
++    struct poptOption options[] = {
++        { "user", 'u', POPT_ARG_STRING, &user, 0,
++          _("PAM user, default: " DEFAULT_USER), NULL },
++        { "service", 's', POPT_ARG_STRING, &service, 0,
++          _("PAM service, default: " DEFAULT_SERVICE), NULL },
++        POPT_TABLEEND
++    };
++
++    ret = sss_tool_popt_ex(cmdline, options, SSS_TOOL_OPT_OPTIONAL,
++                           NULL, NULL, "DOMAIN", _("Specify domain name."),
++                           &domname, NULL);
++    if (ret != EOK) {
++        DEBUG(SSSDBG_CRIT_FAILURE, "Unable to parse command arguments\n");
++        return ret;
++    }
++
++    dom = find_domain_by_name(tool_ctx->domains, domname, true);
++    if (dom == NULL) {
++        ERROR("Cannot find domain %1$s\n", domname);
++        return ERR_DOMAIN_NOT_FOUND;
++    }
++
++    reporter = get_report_fn(dom->provider);
++    if (reporter == NULL) {
++        ERROR("Access report not implemented for domains of type %1$s\n",
++              dom->provider);
++        return ret;
++    }
++
++    return reporter(tool_ctx, user, service, dom);
++}
+-- 
+2.14.3
+
diff --git a/SOURCES/0049-IPA_SUBDOMAINS-Rename-_refresh_view-to-_refresh_view.patch b/SOURCES/0049-IPA_SUBDOMAINS-Rename-_refresh_view-to-_refresh_view.patch
deleted file mode 100644
index 71624c8..0000000
--- a/SOURCES/0049-IPA_SUBDOMAINS-Rename-_refresh_view-to-_refresh_view.patch
+++ /dev/null
@@ -1,54 +0,0 @@
-From 33190863b66f90cac410b7a9b9cc95e4f9891013 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Fabiano=20Fid=C3=AAncio?= <fidencio@redhat.com>
-Date: Fri, 24 Mar 2017 08:08:58 +0100
-Subject: [PATCH 49/54] IPA_SUBDOMAINS: Rename _refresh_view() to
- _refresh_view_name()
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-This method got renamed in order to match better with what it does
-currently.
-
-Related:
-https://pagure.io/SSSD/sssd/issue/3001
-
-Signed-off-by: Fabiano Fidêncio <fidencio@redhat.com>
-
-Reviewed-by: Sumit Bose <sbose@redhat.com>
-Reviewed-by: Pavel Březina <pbrezina@redhat.com>
----
- src/providers/ipa/ipa_subdomains.c | 7 ++++---
- 1 file changed, 4 insertions(+), 3 deletions(-)
-
-diff --git a/src/providers/ipa/ipa_subdomains.c b/src/providers/ipa/ipa_subdomains.c
-index 01a0ce812d861b24565d2f71f27d6b8ceb2965bc..bf6f6ab1fa8bfff7ea102dd219c9ddbbab065b2b 100644
---- a/src/providers/ipa/ipa_subdomains.c
-+++ b/src/providers/ipa/ipa_subdomains.c
-@@ -1808,7 +1808,7 @@ static void ipa_subdomains_refresh_ranges_done(struct tevent_req *subreq);
- static void ipa_subdomains_refresh_certmap_done(struct tevent_req *subreq);
- static void ipa_subdomains_refresh_master_done(struct tevent_req *subreq);
- static void ipa_subdomains_refresh_slave_done(struct tevent_req *subreq);
--static void ipa_subdomains_refresh_view_done(struct tevent_req *subreq);
-+static void ipa_subdomains_refresh_view_name_done(struct tevent_req *subreq);
- static void ipa_domain_refresh_resolution_order_done(struct tevent_req *subreq);
- 
- static struct tevent_req *
-@@ -2023,11 +2023,12 @@ static void ipa_subdomains_refresh_slave_done(struct tevent_req *subreq)
-         return;
-     }
- 
--    tevent_req_set_callback(subreq, ipa_subdomains_refresh_view_done, req);
-+    tevent_req_set_callback(subreq, ipa_subdomains_refresh_view_name_done,
-+                            req);
-     return;
- }
- 
--static void ipa_subdomains_refresh_view_done(struct tevent_req *subreq)
-+static void ipa_subdomains_refresh_view_name_done(struct tevent_req *subreq)
- {
-     struct ipa_subdomains_refresh_state *state;
-     struct tevent_req *req;
--- 
-2.9.3
-
diff --git a/SOURCES/0049-dp-use-void-to-express-empty-output-argument-list.patch b/SOURCES/0049-dp-use-void-to-express-empty-output-argument-list.patch
new file mode 100644
index 0000000..8d23c72
--- /dev/null
+++ b/SOURCES/0049-dp-use-void-to-express-empty-output-argument-list.patch
@@ -0,0 +1,50 @@
+From ae4435d84bae06c592d7bed16ce7aa2b07823be9 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pavel=20B=C5=99ezina?= <pbrezina@redhat.com>
+Date: Thu, 2 Nov 2017 14:58:05 +0100
+Subject: [PATCH 49/57] dp: use void * to express empty output argument list
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Since we cannot use plain void type is function definition.
+
+Related:
+https://pagure.io/SSSD/sssd/issue/2840
+
+Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
+Reviewed-by: Fabiano Fidêncio <fidencio@redhat.com>
+(cherry picked from commit 6211a202301e6f61d46cdb2bf0be332a70c7fdea)
+---
+ src/providers/data_provider/dp_private.h       | 2 +-
+ src/providers/data_provider/dp_request_reply.c | 2 +-
+ 2 files changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/src/providers/data_provider/dp_private.h b/src/providers/data_provider/dp_private.h
+index 2e71a373fdc8886fccb23bfb8283dc5bc341b1b0..028070f7f1866854c145a148e44c2cb108d2fc58 100644
+--- a/src/providers/data_provider/dp_private.h
++++ b/src/providers/data_provider/dp_private.h
+@@ -136,7 +136,7 @@ typedef void (*dp_req_reply_fn)(const char *req_name,
+ 
+ void dp_req_reply_default(const char *req_name,
+                           struct sbus_request *sbus_req,
+-                          void *data);
++                          void **data);
+ 
+ /* Data provider request table. */
+ 
+diff --git a/src/providers/data_provider/dp_request_reply.c b/src/providers/data_provider/dp_request_reply.c
+index 27d9654bad76a099b004846463f035bf2e6d1243..34440fda7f28f0026d63af1af9958dcea3c6aaec 100644
+--- a/src/providers/data_provider/dp_request_reply.c
++++ b/src/providers/data_provider/dp_request_reply.c
+@@ -31,7 +31,7 @@
+ 
+ void dp_req_reply_default(const char *req_name,
+                           struct sbus_request *sbus_req,
+-                          void *data)
++                          void **data)
+ {
+     DP_REQ_DEBUG(SSSDBG_TRACE_FUNC, req_name, "Replying with empty message");
+ 
+-- 
+2.14.3
+
diff --git a/SOURCES/0050-IPA-Get-ipaDomainsResolutionOrder-from-IPA-ID-View.patch b/SOURCES/0050-IPA-Get-ipaDomainsResolutionOrder-from-IPA-ID-View.patch
deleted file mode 100644
index a1da431..0000000
--- a/SOURCES/0050-IPA-Get-ipaDomainsResolutionOrder-from-IPA-ID-View.patch
+++ /dev/null
@@ -1,347 +0,0 @@
-From d36c2acde1f29865c2cefedebc214ba48bb227e7 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Fabiano=20Fid=C3=AAncio?= <fidencio@redhat.com>
-Date: Fri, 24 Mar 2017 17:46:04 +0100
-Subject: [PATCH 50/54] IPA: Get ipaDomainsResolutionOrder from IPA ID View
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-ipaDomainsResolutionOrder provides a list of domains that have to be
-looked up firstly during cache_req searches.
-
-This commit only fetches this list from the server and stores its value
-at sysdb so we can make use of it later on this patch series.
-
-There are no tests for newly introduced sysdb methods are those are
-basically only calling sysdb_update_domain_resolution_order(),
-sysdb_get_domain_resolution_order() and
-sysdb_get_use_domain_resolution_order() which are have tests written
-for.
-
-Related:
-https://pagure.io/SSSD/sssd/issue/3001
-
-Signed-off-by: Fabiano Fidêncio <fidencio@redhat.com>
-
-Reviewed-by: Sumit Bose <sbose@redhat.com>
-Reviewed-by: Pavel Březina <pbrezina@redhat.com>
----
- src/db/sysdb.h                     |   9 ++
- src/db/sysdb_views.c               |  66 ++++++++++++++
- src/providers/ipa/ipa_subdomains.c | 182 +++++++++++++++++++++++++++++++++++++
- 3 files changed, 257 insertions(+)
-
-diff --git a/src/db/sysdb.h b/src/db/sysdb.h
-index 75a07d4d2effb028ec654342113f8478e1eba10e..62c561be9452a284a8ddf8ebb45720265852c8b0 100644
---- a/src/db/sysdb.h
-+++ b/src/db/sysdb.h
-@@ -533,6 +533,15 @@ errno_t sysdb_update_view_name(struct sysdb_ctx *sysdb, const char *view_name);
- errno_t sysdb_get_view_name(TALLOC_CTX *mem_ctx, struct sysdb_ctx *sysdb,
-                             char **view_name);
- 
-+errno_t sysdb_update_view_domain_resolution_order(
-+                                        struct sysdb_ctx *sysdb,
-+                                        const char *domain_resolution_order);
-+
-+errno_t sysdb_get_view_domain_resolution_order(
-+                                        TALLOC_CTX *mem_ctx,
-+                                        struct sysdb_ctx *sysdb,
-+                                        const char **_domain_resolution_order);
-+
- static inline bool is_default_view(const char *view_name)
- {
-     /* NULL is treated as default */
-diff --git a/src/db/sysdb_views.c b/src/db/sysdb_views.c
-index 1c416dd14049237e9f35d52f154035e3ff861469..20db9b06183d68b33bb19f498513d7f5cf84b1cf 100644
---- a/src/db/sysdb_views.c
-+++ b/src/db/sysdb_views.c
-@@ -22,6 +22,9 @@
- #include "util/util.h"
- #include "util/cert.h"
- #include "db/sysdb_private.h"
-+#include "db/sysdb_domain_resolution_order.h"
-+
-+#define SYSDB_VIEWS_BASE "cn=views,cn=sysdb"
- 
- /* In general is should not be possible that there is a view container without
-  * a view name set. But to be on the safe side we return both information
-@@ -179,6 +182,69 @@ done:
-     return ret;
- }
- 
-+errno_t
-+sysdb_get_view_domain_resolution_order(TALLOC_CTX *mem_ctx,
-+                                       struct sysdb_ctx *sysdb,
-+                                       const char **_domain_resolution_order)
-+{
-+    TALLOC_CTX *tmp_ctx;
-+    struct ldb_dn *dn;
-+    errno_t ret;
-+
-+    tmp_ctx = talloc_new(NULL);
-+    if (tmp_ctx == NULL) {
-+        return ENOMEM;
-+    }
-+
-+    dn = ldb_dn_new(tmp_ctx, sysdb->ldb, SYSDB_VIEWS_BASE);
-+    if (dn == NULL) {
-+        ret = ENOMEM;
-+        goto done;
-+    }
-+
-+    ret = sysdb_get_domain_resolution_order(mem_ctx, sysdb, dn,
-+                                            _domain_resolution_order);
-+
-+done:
-+    talloc_free(tmp_ctx);
-+    return ret;
-+}
-+
-+errno_t
-+sysdb_update_view_domain_resolution_order(struct sysdb_ctx *sysdb,
-+                                          const char *domain_resolution_order)
-+{
-+    TALLOC_CTX *tmp_ctx;
-+    struct ldb_dn *dn;
-+    errno_t ret;
-+
-+    tmp_ctx = talloc_new(NULL);
-+    if (tmp_ctx == NULL) {
-+        return ENOMEM;
-+    }
-+
-+    dn = ldb_dn_new(tmp_ctx, sysdb->ldb, SYSDB_VIEWS_BASE);
-+    if (dn == NULL) {
-+        ret = ENOMEM;
-+        goto done;
-+    }
-+
-+    ret = sysdb_update_domain_resolution_order(sysdb, dn,
-+                                               domain_resolution_order);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_OP_FAILURE,
-+              "sysdb_update_domain_resolution_order() failed [%d]: [%s].\n",
-+              ret, sss_strerror(ret));
-+        goto done;
-+    }
-+
-+    ret = EOK;
-+
-+done:
-+    talloc_free(tmp_ctx);
-+    return ret;
-+}
-+
- errno_t sysdb_delete_view_tree(struct sysdb_ctx *sysdb, const char *view_name)
- {
-     struct ldb_dn *dn;
-diff --git a/src/providers/ipa/ipa_subdomains.c b/src/providers/ipa/ipa_subdomains.c
-index bf6f6ab1fa8bfff7ea102dd219c9ddbbab065b2b..ef348adf4a36e870f44387bd700f5c2beea3bfd6 100644
---- a/src/providers/ipa/ipa_subdomains.c
-+++ b/src/providers/ipa/ipa_subdomains.c
-@@ -1684,6 +1684,151 @@ static errno_t ipa_subdomains_view_name_recv(struct tevent_req *req)
-     return EOK;
- }
- 
-+struct ipa_subdomains_view_domain_resolution_order_state {
-+    struct sss_domain_info *domain;
-+    const char *view_name;
-+};
-+
-+static void
-+ipa_subdomains_view_domain_resolution_order_done(struct tevent_req *subreq);
-+
-+static struct tevent_req *
-+ipa_subdomains_view_domain_resolution_order_send(
-+                                            TALLOC_CTX *mem_ctx,
-+                                            struct tevent_context *ev,
-+                                            struct ipa_subdomains_ctx *sd_ctx,
-+                                            struct sdap_handle *sh)
-+{
-+    struct ipa_subdomains_view_domain_resolution_order_state *state;
-+    struct tevent_req *subreq;
-+    struct tevent_req *req;
-+    const char *attrs[] = { IPA_DOMAIN_RESOLUTION_ORDER, NULL };
-+    char *ldap_basedn;
-+    char *base;
-+    errno_t ret;
-+
-+    req = tevent_req_create(mem_ctx, &state,
-+                    struct ipa_subdomains_view_domain_resolution_order_state);
-+    if (req == NULL) {
-+        DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create() failed\n");
-+        return NULL;
-+    }
-+
-+    state->domain = sd_ctx->be_ctx->domain;
-+    state->view_name = sd_ctx->ipa_id_ctx->view_name;
-+
-+    ret = domain_to_basedn(state, sd_ctx->be_ctx->domain->name, &ldap_basedn);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_OP_FAILURE, "domain_to_basedn failed.\n");
-+        goto immediately;
-+    }
-+
-+    base = talloc_asprintf(state, "cn=%s,cn=views,cn=accounts,%s",
-+                           sd_ctx->ipa_id_ctx->view_name, ldap_basedn);
-+    if (base == NULL) {
-+        DEBUG(SSSDBG_OP_FAILURE, "talloc_asprintf failed.\n");
-+        ret = ENOMEM;
-+        goto immediately;
-+    }
-+
-+    subreq = sdap_get_generic_send(
-+                            state, ev, sd_ctx->sdap_id_ctx->opts, sh,
-+                            base, LDAP_SCOPE_BASE, NULL, attrs, NULL, 0,
-+                            dp_opt_get_int(sd_ctx->sdap_id_ctx->opts->basic,
-+                                           SDAP_ENUM_SEARCH_TIMEOUT),
-+                            false);
-+    if (subreq == NULL) {
-+        ret = ENOMEM;
-+        goto immediately;
-+    }
-+
-+    tevent_req_set_callback(subreq, ipa_subdomains_view_domain_resolution_order_done,
-+                            req);
-+
-+    return req;
-+
-+immediately:
-+    if (ret == EOK) {
-+        tevent_req_done(req);
-+    } else {
-+        tevent_req_error(req, ret);
-+    }
-+    tevent_req_post(req, ev);
-+
-+    return req;
-+}
-+
-+static void
-+ipa_subdomains_view_domain_resolution_order_done(struct tevent_req *subreq)
-+{
-+    struct ipa_subdomains_view_domain_resolution_order_state *state;
-+    struct tevent_req *req;
-+    size_t reply_count;
-+    struct sysdb_attrs **reply;
-+    const char *domain_resolution_order;
-+    errno_t ret;
-+
-+    req = tevent_req_callback_data(subreq, struct tevent_req);
-+    state = tevent_req_data(req,
-+                    struct ipa_subdomains_view_domain_resolution_order_state);
-+
-+    ret = sdap_get_generic_recv(subreq, state, &reply_count, &reply);
-+    talloc_zfree(subreq);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_OP_FAILURE, "Unable to get view name [%d]: %s\n",
-+              ret, sss_strerror(ret));
-+        goto done;
-+    }
-+
-+    if (reply_count > 1) {
-+        DEBUG(SSSDBG_CRIT_FAILURE, "More than one object returned.\n");
-+        ret = EINVAL;
-+        goto done;
-+    } else if (reply_count == 0) {
-+        domain_resolution_order = NULL;
-+    } else {
-+        /* reply_count == 1 */
-+        ret = sysdb_attrs_get_string(reply[0], IPA_DOMAIN_RESOLUTION_ORDER,
-+                                     &domain_resolution_order);
-+        if (ret != EOK && ret != ENOENT) {
-+            DEBUG(SSSDBG_OP_FAILURE,
-+                  "Failed to get the view domains' resolution order "
-+                  "configuration value for view [%s] [%d]: %s\n",
-+                  state->view_name, ret, sss_strerror(ret));
-+            goto done;
-+        } else if (ret == ENOENT) {
-+            domain_resolution_order = NULL;
-+        }
-+    }
-+
-+    ret = sysdb_update_view_domain_resolution_order(state->domain->sysdb,
-+                                                    domain_resolution_order);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_OP_FAILURE,
-+              "sysdb_update_view_domain_resolution_order() [%d]: [%s].\n",
-+              ret, sss_strerror(ret));
-+        goto done;
-+    }
-+
-+    ret = EOK;
-+
-+done:
-+    if (ret != EOK) {
-+        tevent_req_error(req, ret);
-+        return;
-+    }
-+
-+    tevent_req_done(req);
-+}
-+
-+static errno_t
-+ipa_subdomains_view_domain_resolution_order_recv(struct tevent_req *req)
-+{
-+    TEVENT_REQ_RETURN_ON_ERROR(req);
-+
-+    return EOK;
-+}
-+
- struct ipa_domain_resolution_order_state {
-     struct sss_domain_info *domain;
- };
-@@ -1809,6 +1954,8 @@ static void ipa_subdomains_refresh_certmap_done(struct tevent_req *subreq);
- static void ipa_subdomains_refresh_master_done(struct tevent_req *subreq);
- static void ipa_subdomains_refresh_slave_done(struct tevent_req *subreq);
- static void ipa_subdomains_refresh_view_name_done(struct tevent_req *subreq);
-+static void ipa_subdomains_refresh_view_domain_resolution_order_done(
-+                                                    struct tevent_req *subreq);
- static void ipa_domain_refresh_resolution_order_done(struct tevent_req *subreq);
- 
- static struct tevent_req *
-@@ -2047,6 +2194,41 @@ static void ipa_subdomains_refresh_view_name_done(struct tevent_req *subreq)
-         return;
-     }
- 
-+    subreq = ipa_subdomains_view_domain_resolution_order_send(
-+                                            state,
-+                                            state->ev,
-+                                            state->sd_ctx,
-+                                            sdap_id_op_handle(state->sdap_op));
-+    if (subreq == NULL) {
-+        tevent_req_error(req, ENOMEM);
-+        return;
-+    }
-+
-+    tevent_req_set_callback(subreq,
-+                    ipa_subdomains_refresh_view_domain_resolution_order_done,
-+                    req);
-+}
-+
-+static void
-+ipa_subdomains_refresh_view_domain_resolution_order_done(struct tevent_req *subreq)
-+{
-+    struct ipa_subdomains_refresh_state *state;
-+    struct tevent_req *req;
-+    errno_t ret;
-+
-+    req = tevent_req_callback_data(subreq, struct tevent_req);
-+    state = tevent_req_data(req, struct ipa_subdomains_refresh_state);
-+
-+    ret = ipa_subdomains_view_domain_resolution_order_recv(subreq);
-+    talloc_zfree(subreq);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_CRIT_FAILURE,
-+              "Unable to get view domain_resolution order [%d]: %s\n",
-+              ret, sss_strerror(ret));
-+        tevent_req_error(req, ret);
-+        return;
-+    }
-+
-     subreq = ipa_domain_resolution_order_send(state, state->ev, state->sd_ctx,
-                                             sdap_id_op_handle(state->sdap_op));
-     if (subreq == NULL) {
--- 
-2.9.3
-
diff --git a/SOURCES/0050-dp-add-method-to-refresh-access-control-rules.patch b/SOURCES/0050-dp-add-method-to-refresh-access-control-rules.patch
new file mode 100644
index 0000000..cb7b567
--- /dev/null
+++ b/SOURCES/0050-dp-add-method-to-refresh-access-control-rules.patch
@@ -0,0 +1,192 @@
+From 7de7fd7a9378461ce57f9b5174938af9440381ca Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pavel=20B=C5=99ezina?= <pbrezina@redhat.com>
+Date: Thu, 2 Nov 2017 14:58:38 +0100
+Subject: [PATCH 50/57] dp: add method to refresh access control rules
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Related:
+https://pagure.io/SSSD/sssd/issue/2840
+
+Reviewed-by: Fabiano Fidêncio <fidencio@redhat.com>
+Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
+(cherry picked from commit e737cdfa225e0d455c0e574bcb82c2cc16a17d9d)
+---
+ src/providers/data_provider/dp.h                 |  2 ++
+ src/providers/data_provider/dp_iface.c           |  6 ++++++
+ src/providers/data_provider/dp_iface.h           |  4 ++++
+ src/providers/data_provider/dp_iface.xml         |  6 ++++++
+ src/providers/data_provider/dp_iface_generated.c | 27 ++++++++++++++++++++++++
+ src/providers/data_provider/dp_iface_generated.h | 16 ++++++++++++++
+ src/providers/data_provider/dp_target_auth.c     | 14 ++++++++++++
+ 7 files changed, 75 insertions(+)
+
+diff --git a/src/providers/data_provider/dp.h b/src/providers/data_provider/dp.h
+index 9cdbe5b3a56ba159f9a10df6e010e616e4aefcac..aa5b781158c54545b26034602bb25db46b189e87 100644
+--- a/src/providers/data_provider/dp.h
++++ b/src/providers/data_provider/dp.h
+@@ -83,6 +83,8 @@ enum dp_methods {
+     DPM_DOMAINS_HANDLER,
+     DPM_SESSION_HANDLER,
+ 
++    DPM_REFRESH_ACCESS_RULES,
++
+     DP_METHOD_SENTINEL
+ };
+ 
+diff --git a/src/providers/data_provider/dp_iface.c b/src/providers/data_provider/dp_iface.c
+index 4b2b0ddca68be8899f7285b4d881a91444b99362..28d70e686f63a3572ac595f493aa1d59436c563f 100644
+--- a/src/providers/data_provider/dp_iface.c
++++ b/src/providers/data_provider/dp_iface.c
+@@ -48,10 +48,16 @@ struct iface_dp_failover iface_dp_failover = {
+     .ListServers = dp_failover_list_servers
+ };
+ 
++struct iface_dp_access_control iface_dp_access_control = {
++    { &iface_dp_access_control_meta, 0 },
++    .RefreshRules = dp_access_control_refresh_rules_handler
++};
++
+ static struct sbus_iface_map dp_map[] = {
+     { DP_PATH, &iface_dp.vtable },
+     { DP_PATH, &iface_dp_backend.vtable },
+     { DP_PATH, &iface_dp_failover.vtable },
++    { DP_PATH, &iface_dp_access_control.vtable },
+     { NULL, NULL }
+ };
+ 
+diff --git a/src/providers/data_provider/dp_iface.h b/src/providers/data_provider/dp_iface.h
+index 8ae7a2ad7a61f82b000493f3309926cd932211f6..759b9e6c9eb7f53836ae0b641b34e6c31e65779f 100644
+--- a/src/providers/data_provider/dp_iface.h
++++ b/src/providers/data_provider/dp_iface.h
+@@ -76,4 +76,8 @@ errno_t dp_failover_list_servers(struct sbus_request *sbus_req,
+                                  void *dp_cli,
+                                  const char *service_name);
+ 
++/* org.freedesktop.sssd.DataProvider.AccessControl */
++errno_t dp_access_control_refresh_rules_handler(struct sbus_request *sbus_req,
++                                                void *dp_cli);
++
+ #endif /* DP_IFACE_H_ */
+diff --git a/src/providers/data_provider/dp_iface.xml b/src/providers/data_provider/dp_iface.xml
+index a3969873ad1660c71ebdcae7a951757f5254c865..2bfa9dfa7e9d02d2d12c3358967f6969438a97a2 100644
+--- a/src/providers/data_provider/dp_iface.xml
++++ b/src/providers/data_provider/dp_iface.xml
+@@ -32,6 +32,12 @@
+         </method>
+     </interface>
+ 
++    <interface name="org.freedesktop.sssd.DataProvider.AccessControl">
++        <annotation value="iface_dp_access_control" name="org.freedesktop.DBus.GLib.CSymbol"/>
++        <method name="RefreshRules">
++        </method>
++    </interface>
++
+     <interface name="org.freedesktop.sssd.dataprovider">
+         <annotation value="iface_dp" name="org.freedesktop.DBus.GLib.CSymbol"/>
+         <method name="pamHandler">
+diff --git a/src/providers/data_provider/dp_iface_generated.c b/src/providers/data_provider/dp_iface_generated.c
+index e2e0216bd98c498b2b34c524ba615b70564420a2..11ee2e24a69cc8d4d19fdbeed613e76081aef15d 100644
+--- a/src/providers/data_provider/dp_iface_generated.c
++++ b/src/providers/data_provider/dp_iface_generated.c
+@@ -187,6 +187,33 @@ const struct sbus_interface_meta iface_dp_failover_meta = {
+     sbus_invoke_get_all, /* GetAll invoker */
+ };
+ 
++int iface_dp_access_control_RefreshRules_finish(struct sbus_request *req)
++{
++   return sbus_request_return_and_finish(req,
++                                         DBUS_TYPE_INVALID);
++}
++
++/* methods for org.freedesktop.sssd.DataProvider.AccessControl */
++const struct sbus_method_meta iface_dp_access_control__methods[] = {
++    {
++        "RefreshRules", /* name */
++        NULL, /* no in_args */
++        NULL, /* no out_args */
++        offsetof(struct iface_dp_access_control, RefreshRules),
++        NULL, /* no invoker */
++    },
++    { NULL, }
++};
++
++/* interface info for org.freedesktop.sssd.DataProvider.AccessControl */
++const struct sbus_interface_meta iface_dp_access_control_meta = {
++    "org.freedesktop.sssd.DataProvider.AccessControl", /* name */
++    iface_dp_access_control__methods,
++    NULL, /* no signals */
++    NULL, /* no properties */
++    sbus_invoke_get_all, /* GetAll invoker */
++};
++
+ /* arguments for org.freedesktop.sssd.dataprovider.autofsHandler */
+ const struct sbus_arg_meta iface_dp_autofsHandler__in[] = {
+     { "dp_flags", "u" },
+diff --git a/src/providers/data_provider/dp_iface_generated.h b/src/providers/data_provider/dp_iface_generated.h
+index b7f63fb438d7b3024a0f66de0a5d15cc3d426f44..541a90b0b5a5bc0a346cbd04974d33c8bb0983c5 100644
+--- a/src/providers/data_provider/dp_iface_generated.h
++++ b/src/providers/data_provider/dp_iface_generated.h
+@@ -26,6 +26,10 @@
+ #define IFACE_DP_FAILOVER_ACTIVESERVER "ActiveServer"
+ #define IFACE_DP_FAILOVER_LISTSERVERS "ListServers"
+ 
++/* constants for org.freedesktop.sssd.DataProvider.AccessControl */
++#define IFACE_DP_ACCESS_CONTROL "org.freedesktop.sssd.DataProvider.AccessControl"
++#define IFACE_DP_ACCESS_CONTROL_REFRESHRULES "RefreshRules"
++
+ /* constants for org.freedesktop.sssd.dataprovider */
+ #define IFACE_DP "org.freedesktop.sssd.dataprovider"
+ #define IFACE_DP_PAMHANDLER "pamHandler"
+@@ -88,6 +92,15 @@ int iface_dp_failover_ActiveServer_finish(struct sbus_request *req, const char *
+ /* finish function for ListServers */
+ int iface_dp_failover_ListServers_finish(struct sbus_request *req, const char *arg_servers[], int len_servers);
+ 
++/* vtable for org.freedesktop.sssd.DataProvider.AccessControl */
++struct iface_dp_access_control {
++    struct sbus_vtable vtable; /* derive from sbus_vtable */
++    int (*RefreshRules)(struct sbus_request *req, void *data);
++};
++
++/* finish function for RefreshRules */
++int iface_dp_access_control_RefreshRules_finish(struct sbus_request *req);
++
+ /* vtable for org.freedesktop.sssd.dataprovider */
+ struct iface_dp {
+     struct sbus_vtable vtable; /* derive from sbus_vtable */
+@@ -130,6 +143,9 @@ extern const struct sbus_interface_meta iface_dp_backend_meta;
+ /* interface info for org.freedesktop.sssd.DataProvider.Failover */
+ extern const struct sbus_interface_meta iface_dp_failover_meta;
+ 
++/* interface info for org.freedesktop.sssd.DataProvider.AccessControl */
++extern const struct sbus_interface_meta iface_dp_access_control_meta;
++
+ /* interface info for org.freedesktop.sssd.dataprovider */
+ extern const struct sbus_interface_meta iface_dp_meta;
+ 
+diff --git a/src/providers/data_provider/dp_target_auth.c b/src/providers/data_provider/dp_target_auth.c
+index 6bb3313b2de002466e5ca84464c962acd2412bfa..4b47975569a04a4d79aef4c16fcacf92c295de25 100644
+--- a/src/providers/data_provider/dp_target_auth.c
++++ b/src/providers/data_provider/dp_target_auth.c
+@@ -306,3 +306,17 @@ void dp_pam_handler_selinux_done(struct tevent_req *req)
+     dp_pam_reply(state->sbus_req, state->request_name, pd);
+     return;
+ }
++
++errno_t dp_access_control_refresh_rules_handler(struct sbus_request *sbus_req,
++                                                void *dp_cli)
++{
++    const char *key;
++
++    key = "RefreshRules";
++
++    dp_req_with_reply(dp_cli, NULL, "Refresh Access Control Rules", key,
++                      sbus_req, DPT_ACCESS, DPM_REFRESH_ACCESS_RULES, 0, NULL,
++                      dp_req_reply_default, void *);
++
++    return EOK;
++}
+-- 
+2.14.3
+
diff --git a/SOURCES/0051-DLINKLIST-Add-DLIST_FOR_EACH_SAFE-macro.patch b/SOURCES/0051-DLINKLIST-Add-DLIST_FOR_EACH_SAFE-macro.patch
deleted file mode 100644
index b75b73d..0000000
--- a/SOURCES/0051-DLINKLIST-Add-DLIST_FOR_EACH_SAFE-macro.patch
+++ /dev/null
@@ -1,37 +0,0 @@
-From 8c7c97d1b3af8c99af43dcaff7ae1d9315a03835 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Fabiano=20Fid=C3=AAncio?= <fidencio@redhat.com>
-Date: Tue, 21 Mar 2017 20:56:38 +0100
-Subject: [PATCH 51/54] DLINKLIST: Add DLIST_FOR_EACH_SAFE macro
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-This macro, as DLIST_FOR_EACH, iterates over the whole list. The main
-difference between both is that in the _SAFE version the pointer to the
-next list node is stored, allowing us to delete the current node safely.
-
-Signed-off-by: Fabiano Fidêncio <fidencio@redhat.com>
-
-Reviewed-by: Sumit Bose <sbose@redhat.com>
-Reviewed-by: Pavel Březina <pbrezina@redhat.com>
----
- src/util/dlinklist.h | 5 +++++
- 1 file changed, 5 insertions(+)
-
-diff --git a/src/util/dlinklist.h b/src/util/dlinklist.h
-index 4f6aef830e914c22654970081263d43461c1750f..017c60468e66dbec15724d5f4832da412f42136b 100644
---- a/src/util/dlinklist.h
-+++ b/src/util/dlinklist.h
-@@ -147,4 +147,9 @@ do { \
- #define DLIST_FOR_EACH(p, list) \
-     for ((p) = (list); (p) != NULL; (p) = (p)->next)
- 
-+#define DLIST_FOR_EACH_SAFE(p, q, list) \
-+    for ((p) = (list), (q) = (p) != NULL ? (p)->next : NULL; \
-+         (p) != NULL; \
-+         (p) = (q), (q) = (p) != NULL ? (p)->next : NULL)
-+
- #endif /* _DLINKLIST_H */
--- 
-2.9.3
-
diff --git a/SOURCES/0051-ipa-implement-method-to-refresh-HBAC-rules.patch b/SOURCES/0051-ipa-implement-method-to-refresh-HBAC-rules.patch
new file mode 100644
index 0000000..5ccb7fd
--- /dev/null
+++ b/SOURCES/0051-ipa-implement-method-to-refresh-HBAC-rules.patch
@@ -0,0 +1,140 @@
+From 42f16ffa434de2efcdb9010df39dfe7cc619dfb0 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pavel=20B=C5=99ezina?= <pbrezina@redhat.com>
+Date: Thu, 2 Nov 2017 14:59:19 +0100
+Subject: [PATCH 51/57] ipa: implement method to refresh HBAC rules
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Related:
+https://pagure.io/SSSD/sssd/issue/2840
+
+Reviewed-by: Fabiano Fidêncio <fidencio@redhat.com>
+Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
+(cherry picked from commit 2754a8dcfa937d45b024a2e57419248bfd4c4919)
+---
+ src/providers/ipa/ipa_access.c | 68 ++++++++++++++++++++++++++++++++++++++++--
+ src/providers/ipa/ipa_access.h | 10 +++++++
+ src/providers/ipa/ipa_init.c   |  4 +++
+ 3 files changed, 80 insertions(+), 2 deletions(-)
+
+diff --git a/src/providers/ipa/ipa_access.c b/src/providers/ipa/ipa_access.c
+index 32ccf541c9436b633e7724b2c44ee545810a7fb8..de9f68170b6e9c38fd8b6d23f1d565250bbf78d2 100644
+--- a/src/providers/ipa/ipa_access.c
++++ b/src/providers/ipa/ipa_access.c
+@@ -682,8 +682,8 @@ done:
+ 
+ errno_t
+ ipa_pam_access_handler_recv(TALLOC_CTX *mem_ctx,
+-                             struct tevent_req *req,
+-                             struct pam_data **_data)
++                            struct tevent_req *req,
++                            struct pam_data **_data)
+ {
+     struct ipa_pam_access_handler_state *state = NULL;
+ 
+@@ -695,3 +695,67 @@ ipa_pam_access_handler_recv(TALLOC_CTX *mem_ctx,
+ 
+     return EOK;
+ }
++
++struct ipa_refresh_access_rules_state {
++    int dummy;
++};
++
++static void ipa_refresh_access_rules_done(struct tevent_req *subreq);
++
++struct tevent_req *
++ipa_refresh_access_rules_send(TALLOC_CTX *mem_ctx,
++                              struct ipa_access_ctx *access_ctx,
++                              void *no_input_data,
++                              struct dp_req_params *params)
++{
++    struct ipa_refresh_access_rules_state *state;
++    struct tevent_req *subreq;
++    struct tevent_req *req;
++
++    DEBUG(SSSDBG_TRACE_FUNC, "Refreshing HBAC rules\n");
++
++    req = tevent_req_create(mem_ctx, &state,
++                            struct ipa_refresh_access_rules_state);
++    if (req == NULL) {
++        DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create tevent request!\n");
++        return NULL;
++    }
++
++    subreq = ipa_fetch_hbac_send(state, params->ev, params->be_ctx, access_ctx);
++    if (subreq == NULL) {
++        tevent_req_error(req, ENOMEM);
++        tevent_req_post(req, params->ev);
++        return req;
++    }
++
++    tevent_req_set_callback(subreq, ipa_refresh_access_rules_done, req);
++
++    return req;
++}
++
++static void ipa_refresh_access_rules_done(struct tevent_req *subreq)
++{
++    struct tevent_req *req;
++    errno_t ret;
++
++    req = tevent_req_callback_data(subreq, struct tevent_req);
++
++    ret = ipa_fetch_hbac_recv(subreq);
++    talloc_zfree(subreq);
++    if (ret != EOK) {
++        tevent_req_error(req, ret);
++        return;
++    }
++
++    tevent_req_done(req);
++    return;
++}
++
++errno_t ipa_refresh_access_rules_recv(TALLOC_CTX *mem_ctx,
++                                      struct tevent_req *req,
++                                      void **_no_output_data)
++{
++    TEVENT_REQ_RETURN_ON_ERROR(req);
++
++    return EOK;
++}
+diff --git a/src/providers/ipa/ipa_access.h b/src/providers/ipa/ipa_access.h
+index de690350218bd47165a2b48c10059b8de96b718a..9cec0d1063fd39380a77093526e3240523752075 100644
+--- a/src/providers/ipa/ipa_access.h
++++ b/src/providers/ipa/ipa_access.h
+@@ -63,4 +63,14 @@ ipa_pam_access_handler_recv(TALLOC_CTX *mem_ctx,
+                              struct tevent_req *req,
+                              struct pam_data **_data);
+ 
++struct tevent_req *
++ipa_refresh_access_rules_send(TALLOC_CTX *mem_ctx,
++                              struct ipa_access_ctx *access_ctx,
++                              void *no_input_data,
++                              struct dp_req_params *params);
++
++errno_t ipa_refresh_access_rules_recv(TALLOC_CTX *mem_ctx,
++                                      struct tevent_req *req,
++                                      void **_no_output_data);
++
+ #endif /* _IPA_ACCESS_H_ */
+diff --git a/src/providers/ipa/ipa_init.c b/src/providers/ipa/ipa_init.c
+index 5b7c8e1348f561901782c872078a0e7391d4ff75..f335d51fd65959d256c54a5d92c594a24e895b7c 100644
+--- a/src/providers/ipa/ipa_init.c
++++ b/src/providers/ipa/ipa_init.c
+@@ -831,6 +831,10 @@ errno_t sssm_ipa_access_init(TALLOC_CTX *mem_ctx,
+                   ipa_pam_access_handler_send, ipa_pam_access_handler_recv, access_ctx,
+                   struct ipa_access_ctx, struct pam_data, struct pam_data *);
+ 
++    dp_set_method(dp_methods, DPM_REFRESH_ACCESS_RULES,
++                      ipa_refresh_access_rules_send, ipa_refresh_access_rules_recv, access_ctx,
++                      struct ipa_access_ctx, void, void *);
++
+     ret = EOK;
+ 
+ done:
+-- 
+2.14.3
+
diff --git a/SOURCES/0052-CACHE_REQ-Make-use-of-domainResolutionOrder.patch b/SOURCES/0052-CACHE_REQ-Make-use-of-domainResolutionOrder.patch
deleted file mode 100644
index 4086ace..0000000
--- a/SOURCES/0052-CACHE_REQ-Make-use-of-domainResolutionOrder.patch
+++ /dev/null
@@ -1,795 +0,0 @@
-From 5091507c13dfdbde29aa75d6e90eda9ddaa89cff Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Fabiano=20Fid=C3=AAncio?= <fidencio@redhat.com>
-Date: Sun, 26 Mar 2017 00:27:50 +0100
-Subject: [PATCH 52/54] CACHE_REQ: Make use of domainResolutionOrder
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-domainResolutionOrder has been introduced in the previous commits and
-allows the admin to set up a specific order which the domains will be
-resolved during a lookup and with this patch we can take advantage of
-this.
-
-In order to have it working a new structure has been added
-(struct domain_resolution_order) to the responder context and will be
-used by the cache_req to perform the lookups based on this list.
-
-As the ipaDomainResolutionOrder may be set globally on IPA or per View,
-SSSD does respect the following precedence order: View > Globally.
-
-The way the list is built is quite simple, basically having the domains
-present on ipaDomainResolutionOrder as the first domains (in that
-specific order) and then appending the remaining domains to this list.
-The final result is a completely flat list with all the domains
-respecting the specified order (it's important to remember that the
-domains not specified won't follow any specific order, they're just
-"random" based on the domains list present in the responder context.
-
-Related:
-https://pagure.io/SSSD/sssd/issue/3001
-
-Signed-off-by: Fabiano Fidêncio <fidencio@redhat.com>
-
-Reviewed-by: Sumit Bose <sbose@redhat.com>
-Reviewed-by: Pavel Březina <pbrezina@redhat.com>
----
- Makefile.am                                       |   3 +
- src/responder/common/cache_req/cache_req.c        |  89 +++++++-----
- src/responder/common/cache_req/cache_req_domain.c | 166 ++++++++++++++++++++++
- src/responder/common/cache_req/cache_req_domain.h |  46 ++++++
- src/responder/common/responder.h                  |   5 +
- src/responder/common/responder_common.c           | 153 ++++++++++++++++++++
- src/responder/common/responder_get_domains.c      |  14 ++
- src/tests/cmocka/common_mock_resp.c               |   6 +
- src/tests/cmocka/common_mock_resp_dp.c            |   7 +
- src/tests/cmocka/test_nss_srv.c                   |   4 +
- src/tests/cwrap/Makefile.am                       |   1 +
- 11 files changed, 457 insertions(+), 37 deletions(-)
- create mode 100644 src/responder/common/cache_req/cache_req_domain.c
- create mode 100644 src/responder/common/cache_req/cache_req_domain.h
-
-diff --git a/Makefile.am b/Makefile.am
-index 450785bf4c482cce1e1440f1336879150537888e..573b37c52fdeab1add4ea057e1e1844ea4d348a5 100644
---- a/Makefile.am
-+++ b/Makefile.am
-@@ -528,6 +528,7 @@ SSSD_CACHE_REQ_OBJ = \
- 	src/responder/common/cache_req/cache_req_result.c \
- 	src/responder/common/cache_req/cache_req_search.c \
- 	src/responder/common/cache_req/cache_req_data.c \
-+	src/responder/common/cache_req/cache_req_domain.c \
- 	src/responder/common/cache_req/plugins/cache_req_common.c \
- 	src/responder/common/cache_req/plugins/cache_req_enum_users.c \
- 	src/responder/common/cache_req/plugins/cache_req_enum_groups.c \
-@@ -689,6 +690,7 @@ dist_noinst_HEADERS = \
-     src/responder/common/iface/responder_iface.h \
-     src/responder/common/iface/responder_iface_generated.h \
-     src/responder/common/cache_req/cache_req.h \
-+    src/responder/common/cache_req/cache_req_domain.h \
-     src/responder/common/cache_req/cache_req_plugin.h \
-     src/responder/common/cache_req/cache_req_private.h \
-     src/responder/common/data_provider/rdp.h \
-@@ -2199,6 +2201,7 @@ responder_socket_access_tests_SOURCES = \
-     src/responder/common/responder_common.c \
-     src/responder/common/responder_packet.c \
-     src/responder/common/responder_cmd.c \
-+    src/responder/common/cache_req/cache_req_domain.c \
-     src/responder/common/data_provider/rdp_message.c \
-     src/responder/common/data_provider/rdp_client.c \
-     $(SSSD_RESPONDER_IFACE_OBJ) \
-diff --git a/src/responder/common/cache_req/cache_req.c b/src/responder/common/cache_req/cache_req.c
-index aca150d69b398ceb1a52e5cd6a87d35dbc87020b..483126396f8addbad744ae03bfc739801cd0c18b 100644
---- a/src/responder/common/cache_req/cache_req.c
-+++ b/src/responder/common/cache_req/cache_req.c
-@@ -24,6 +24,7 @@
- #include <errno.h>
- 
- #include "util/util.h"
-+#include "responder/common/responder.h"
- #include "responder/common/cache_req/cache_req_private.h"
- #include "responder/common/cache_req/cache_req_plugin.h"
- 
-@@ -316,7 +317,7 @@ struct cache_req_search_domains_state {
-     struct cache_req *cr;
- 
-     /* work data */
--    struct sss_domain_info *domain;
-+    struct cache_req_domain *cr_domain;
-     struct sss_domain_info *selected_domain;
-     struct cache_req_result **results;
-     size_t num_results;
-@@ -330,13 +331,14 @@ static errno_t cache_req_search_domains_next(struct tevent_req *req);
- 
- static void cache_req_search_domains_done(struct tevent_req *subreq);
- 
--struct tevent_req *cache_req_search_domains_send(TALLOC_CTX *mem_ctx,
--                                                 struct tevent_context *ev,
--                                                 struct cache_req *cr,
--                                                 struct sss_domain_info *domain,
--                                                 bool check_next,
--                                                 bool bypass_cache,
--                                                 bool bypass_dp)
-+struct tevent_req *
-+cache_req_search_domains_send(TALLOC_CTX *mem_ctx,
-+                              struct tevent_context *ev,
-+                              struct cache_req *cr,
-+                              struct cache_req_domain *cr_domain,
-+                              bool check_next,
-+                              bool bypass_cache,
-+                              bool bypass_dp)
- {
-     struct tevent_req *req;
-     struct cache_req_search_domains_state *state = NULL;
-@@ -352,7 +354,7 @@ struct tevent_req *cache_req_search_domains_send(TALLOC_CTX *mem_ctx,
-     state->ev = ev;
-     state->cr = cr;
- 
--    state->domain = domain;
-+    state->cr_domain = cr_domain;
-     state->check_next = check_next;
-     state->dp_success = true;
-     state->bypass_cache = bypass_cache;
-@@ -378,6 +380,7 @@ static errno_t cache_req_search_domains_next(struct tevent_req *req)
-     struct cache_req_search_domains_state *state;
-     struct tevent_req *subreq;
-     struct cache_req *cr;
-+    struct sss_domain_info *domain;
-     uint32_t next_domain_flag;
-     bool is_domain_valid;
-     bool allow_no_fqn;
-@@ -389,11 +392,21 @@ static errno_t cache_req_search_domains_next(struct tevent_req *req)
-     next_domain_flag = cr->plugin->get_next_domain_flags;
-     allow_no_fqn = cr->plugin->allow_missing_fqn;
- 
--    while (state->domain != NULL) {
-+    while (state->cr_domain != NULL) {
-+        domain = state->cr_domain->domain;
-+        /* As the cr_domain list is a flatten version of the domains
-+         * list, we have to ensure to only go through the subdomains in
-+         * case it's specified in the plugin to do so.
-+         */
-+        if (next_domain_flag == 0 && IS_SUBDOMAIN(domain)) {
-+            state->cr_domain = state->cr_domain->next;
-+            continue;
-+        }
-+
-         /* Check if this domain is valid for this request. */
--        is_domain_valid = cache_req_validate_domain(cr, state->domain);
-+        is_domain_valid = cache_req_validate_domain(cr, domain);
-         if (!is_domain_valid) {
--            state->domain = get_next_domain(state->domain, next_domain_flag);
-+            state->cr_domain = state->cr_domain->next;
-             continue;
-         }
- 
-@@ -401,18 +414,18 @@ static errno_t cache_req_search_domains_next(struct tevent_req *req)
-          * qualified names on domain less search. We do not descend into
-          * subdomains here since those are implicitly qualified.
-          */
--        if (state->check_next && !allow_no_fqn && state->domain->fqnames) {
--            state->domain = get_next_domain(state->domain, 0);
-+        if (state->check_next && !allow_no_fqn && domain->fqnames) {
-+            state->cr_domain = state->cr_domain->next;
-             continue;
-         }
- 
--        state->selected_domain = state->domain;
-+        state->selected_domain = domain;
- 
--        if (state->domain == NULL) {
-+        if (domain == NULL) {
-             break;
-         }
- 
--        ret = cache_req_set_domain(cr, state->domain);
-+        ret = cache_req_set_domain(cr, domain);
-         if (ret != EOK) {
-             return ret;
-         }
-@@ -427,8 +440,7 @@ static errno_t cache_req_search_domains_next(struct tevent_req *req)
- 
-         /* we will continue with the following domain the next time */
-         if (state->check_next) {
--            state->domain = get_next_domain(state->domain,
--                                            cr->plugin->get_next_domain_flags);
-+            state->cr_domain = state->cr_domain->next;
-         }
- 
-         return EAGAIN;
-@@ -625,11 +637,12 @@ static void cache_req_input_parsed(struct tevent_req *subreq);
- static errno_t cache_req_select_domains(struct tevent_req *req,
-                                         const char *domain_name);
- 
--static errno_t cache_req_search_domains(struct tevent_req *req,
--                                        struct sss_domain_info *domain,
--                                        bool check_next,
--                                        bool bypass_cache,
--                                        bool bypass_dp);
-+static errno_t
-+cache_req_search_domains(struct tevent_req *req,
-+                         struct cache_req_domain *oredered_domain,
-+                         bool check_next,
-+                         bool bypass_cache,
-+                         bool bypass_dp);
- 
- static void cache_req_done(struct tevent_req *subreq);
- 
-@@ -778,7 +791,7 @@ static errno_t cache_req_select_domains(struct tevent_req *req,
-                                         const char *domain_name)
- {
-     struct cache_req_state *state = NULL;
--    struct sss_domain_info *domain;
-+    struct cache_req_domain *cr_domain;
-     bool check_next;
-     bool bypass_cache;
-     bool bypass_dp;
-@@ -798,29 +811,30 @@ static errno_t cache_req_select_domains(struct tevent_req *req,
-         CACHE_REQ_DEBUG(SSSDBG_TRACE_FUNC, state->cr,
-                         "Performing a single domain search\n");
- 
--        domain = responder_get_domain(state->cr->rctx, domain_name);
--        if (domain == NULL) {
-+        cr_domain = cache_req_domain_get_domain_by_name(
-+                                    state->cr->rctx->cr_domains, domain_name);
-+        if (cr_domain == NULL) {
-             return ERR_DOMAIN_NOT_FOUND;
-         }
--
-         check_next = false;
-     } else {
-         CACHE_REQ_DEBUG(SSSDBG_TRACE_FUNC, state->cr,
-                         "Performing a multi-domain search\n");
- 
--        domain = state->cr->rctx->domains;
-+        cr_domain = state->cr->rctx->cr_domains;
-         check_next = true;
-     }
- 
--    return cache_req_search_domains(req, domain, check_next,
-+    return cache_req_search_domains(req, cr_domain, check_next,
-                                     bypass_cache, bypass_dp);
- }
- 
--static errno_t cache_req_search_domains(struct tevent_req *req,
--                                        struct sss_domain_info *domain,
--                                        bool check_next,
--                                        bool bypass_cache,
--                                        bool bypass_dp)
-+static errno_t
-+cache_req_search_domains(struct tevent_req *req,
-+                         struct cache_req_domain *cr_domain,
-+                         bool check_next,
-+                         bool bypass_cache,
-+                         bool bypass_dp)
- {
-     struct tevent_req *subreq;
-     struct cache_req_state *state = NULL;
-@@ -832,8 +846,9 @@ static errno_t cache_req_search_domains(struct tevent_req *req,
-                     bypass_cache ? "bypass" : "check",
-                     bypass_dp ? "bypass" : "check");
- 
--    subreq = cache_req_search_domains_send(state, state->ev, state->cr, domain,
--                                           check_next, bypass_cache, bypass_dp);
-+    subreq = cache_req_search_domains_send(state, state->ev, state->cr,
-+                                           cr_domain, check_next,
-+                                           bypass_cache, bypass_dp);
-     if (subreq == NULL) {
-         return ENOMEM;
-     }
-diff --git a/src/responder/common/cache_req/cache_req_domain.c b/src/responder/common/cache_req/cache_req_domain.c
-new file mode 100644
-index 0000000000000000000000000000000000000000..bbabd695f1c6b6c29b7e61f571382ab9adfb0ea2
---- /dev/null
-+++ b/src/responder/common/cache_req/cache_req_domain.c
-@@ -0,0 +1,166 @@
-+/*
-+    Authors:
-+        Fabiano Fidêncio <fidencio@redhat.com>
-+
-+    Copyright (C) 2017 Red Hat
-+
-+    This program is free software; you can redistribute it and/or modify
-+    it under the terms of the GNU General Public License as published by
-+    the Free Software Foundation; either version 3 of the License, or
-+    (at your option) any later version.
-+
-+    This program is distributed in the hope that it will be useful,
-+    but WITHOUT ANY WARRANTY; without even the implied warranty of
-+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+    GNU General Public License for more details.
-+
-+    You should have received a copy of the GNU General Public License
-+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
-+*/
-+
-+#include "responder/common/cache_req/cache_req_domain.h"
-+
-+struct cache_req_domain *
-+cache_req_domain_get_domain_by_name(struct cache_req_domain *domains,
-+                                    const char *name)
-+{
-+    struct cache_req_domain *dom;
-+    struct cache_req_domain *ret = NULL;
-+
-+    DLIST_FOR_EACH(dom, domains) {
-+        if (sss_domain_get_state(dom->domain) == DOM_DISABLED) {
-+            continue;
-+        }
-+
-+        if (strcasecmp(dom->domain->name, name) == 0 ||
-+            (dom->domain->flat_name != NULL &&
-+             strcasecmp(dom->domain->flat_name, name) == 0)) {
-+            ret = dom;
-+            break;
-+        }
-+    }
-+
-+    if (ret == NULL) {
-+        DEBUG(SSSDBG_OP_FAILURE, "Unknown domains [%s].\n", name);
-+    }
-+
-+    return ret;
-+}
-+
-+void cache_req_domain_list_zfree(struct cache_req_domain **cr_domains)
-+{
-+    struct cache_req_domain *p, *q, *r;
-+
-+    DLIST_FOR_EACH_SAFE(p, q, *cr_domains) {
-+        r = p;
-+        DLIST_REMOVE(*cr_domains, p);
-+        talloc_zfree(r);
-+    }
-+
-+    *cr_domains = NULL;
-+}
-+
-+static struct cache_req_domain *
-+cache_req_domain_new_list_from_string_list(TALLOC_CTX *mem_ctx,
-+                                           struct sss_domain_info *domains,
-+                                           char **resolution_order)
-+{
-+    struct cache_req_domain *cr_domains = NULL;
-+    struct cache_req_domain *cr_domain;
-+    struct sss_domain_info *dom;
-+    char *name;
-+    int flag = SSS_GND_ALL_DOMAINS;
-+    int i;
-+    errno_t ret;
-+
-+    if (resolution_order != NULL) {
-+        for (i = 0; resolution_order[i] != NULL; i++) {
-+            name = resolution_order[i];
-+            for (dom = domains; dom; dom = get_next_domain(dom, flag)) {
-+                if (strcasecmp(name, dom->name) != 0) {
-+                    continue;
-+                }
-+
-+                cr_domain = talloc_zero(mem_ctx, struct cache_req_domain);
-+                if (cr_domain == NULL) {
-+                    ret = ENOMEM;
-+                    goto done;
-+                }
-+                cr_domain->domain = dom;
-+
-+                DLIST_ADD_END(cr_domains, cr_domain,
-+                              struct cache_req_domain *);
-+                break;
-+            }
-+        }
-+    }
-+
-+    for (dom = domains; dom; dom = get_next_domain(dom, flag)) {
-+        if (string_in_list(dom->name, resolution_order, false)) {
-+            continue;
-+        }
-+
-+        cr_domain = talloc_zero(mem_ctx, struct cache_req_domain);
-+        if (cr_domain == NULL) {
-+            ret = ENOMEM;
-+            goto done;
-+        }
-+        cr_domain->domain = dom;
-+
-+        DLIST_ADD_END(cr_domains, cr_domain, struct cache_req_domain *);
-+    }
-+
-+    ret = EOK;
-+
-+done:
-+    if (ret != EOK) {
-+        cache_req_domain_list_zfree(&cr_domains);
-+    }
-+
-+    return cr_domains;
-+}
-+
-+struct cache_req_domain *
-+cache_req_domain_new_list_from_domain_resolution_order(
-+                                        TALLOC_CTX *mem_ctx,
-+                                        struct sss_domain_info *domains,
-+                                        const char *domain_resolution_order)
-+{
-+    TALLOC_CTX *tmp_ctx;
-+    struct cache_req_domain *cr_domains = NULL;
-+    char **list = NULL;
-+    errno_t ret;
-+
-+    tmp_ctx = talloc_new(NULL);
-+    if (tmp_ctx == NULL) {
-+        return NULL;
-+    }
-+
-+    if (domain_resolution_order != NULL) {
-+        if (strcmp(domain_resolution_order, ":") != 0) {
-+            ret = split_on_separator(tmp_ctx, domain_resolution_order, ':',
-+                                     true, true, &list, NULL);
-+            if (ret != EOK) {
-+                DEBUG(SSSDBG_MINOR_FAILURE,
-+                        "split_on_separator() failed [%d]: [%s].\n",
-+                        ret, sss_strerror(ret));
-+                goto done;
-+            }
-+        }
-+    }
-+
-+    cr_domains = cache_req_domain_new_list_from_string_list(mem_ctx, domains,
-+                                                            list);
-+    if (cr_domains == NULL) {
-+        ret = ENOMEM;
-+        DEBUG(SSSDBG_OP_FAILURE,
-+              "cache_req_domain_new_list_from_domain_resolution_order() "
-+              "failed [%d]: [%s].\n",
-+              ret, sss_strerror(ret));
-+        goto done;
-+    }
-+
-+done:
-+    talloc_free(tmp_ctx);
-+    return cr_domains;
-+}
-diff --git a/src/responder/common/cache_req/cache_req_domain.h b/src/responder/common/cache_req/cache_req_domain.h
-new file mode 100644
-index 0000000000000000000000000000000000000000..41c50e8c293d7b032cb2f05482c40e93e4f723dc
---- /dev/null
-+++ b/src/responder/common/cache_req/cache_req_domain.h
-@@ -0,0 +1,46 @@
-+/*
-+    Authors:
-+        Fabiano Fidêncio <fidencio@redhat.com>
-+
-+    Copyright (C) 2017 Red Hat
-+
-+    This program is free software; you can redistribute it and/or modify
-+    it under the terms of the GNU General Public License as published by
-+    the Free Software Foundation; either version 3 of the License, or
-+    (at your option) any later version.
-+
-+    This program is distributed in the hope that it will be useful,
-+    but WITHOUT ANY WARRANTY; without even the implied warranty of
-+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+    GNU General Public License for more details.
-+
-+    You should have received a copy of the GNU General Public License
-+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
-+*/
-+
-+#ifndef _CACHE_REQ_DOMAIN_H_
-+#define _CACHE_REQ_DOMAIN_H_
-+
-+#include "responder/common/responder.h"
-+
-+struct cache_req_domain {
-+    struct sss_domain_info *domain;
-+
-+    struct cache_req_domain *prev;
-+    struct cache_req_domain *next;
-+};
-+
-+struct cache_req_domain *
-+cache_req_domain_get_domain_by_name(struct cache_req_domain *domains,
-+                                    const char *name);
-+
-+struct cache_req_domain *
-+cache_req_domain_new_list_from_domain_resolution_order(
-+                                        TALLOC_CTX *mem_ctx,
-+                                        struct sss_domain_info *domains,
-+                                        const char *domain_resolution_order);
-+
-+void cache_req_domain_list_zfree(struct cache_req_domain **cr_domains);
-+
-+
-+#endif /* _CACHE_REQ_DOMAIN_H_ */
-diff --git a/src/responder/common/responder.h b/src/responder/common/responder.h
-index 4d1048a1e0c9be2cad91317d51baf670ecb3307e..29e3f95caf484f43307c9c28d4abd3f50f360a95 100644
---- a/src/responder/common/responder.h
-+++ b/src/responder/common/responder.h
-@@ -37,6 +37,7 @@
- #include "sbus/sssd_dbus.h"
- #include "responder/common/negcache.h"
- #include "sss_client/sss_cli.h"
-+#include "responder/common/cache_req/cache_req_domain.h"
- 
- extern hash_table_t *dp_requests;
- 
-@@ -113,6 +114,8 @@ struct resp_ctx {
-     int domains_timeout;
-     int client_idle_timeout;
- 
-+    struct cache_req_domain *cr_domains;
-+
-     time_t last_request_time;
-     int idle_timeout;
-     struct tevent_timer *idle;
-@@ -387,4 +390,6 @@ char *sss_resp_create_fqname(TALLOC_CTX *mem_ctx,
-                              bool name_is_upn,
-                              const char *orig_name);
- 
-+errno_t sss_resp_populate_cr_domains(struct resp_ctx *rctx);
-+
- #endif /* __SSS_RESPONDER_H__ */
-diff --git a/src/responder/common/responder_common.c b/src/responder/common/responder_common.c
-index 76f43609651217e537ffa515aaf5b5caa98a2e90..1792a4c3771fa326c7cca31e1981dce315c03758 100644
---- a/src/responder/common/responder_common.c
-+++ b/src/responder/common/responder_common.c
-@@ -1453,3 +1453,156 @@ fail:
-     return ret;
- 
- }
-+
-+/* ====== Helper functions for the domain resolution order ======= */
-+static struct cache_req_domain *
-+sss_resp_new_cr_domains_from_ipa_id_view(TALLOC_CTX *mem_ctx,
-+                                         struct sss_domain_info *domains,
-+                                         struct sysdb_ctx *sysdb)
-+{
-+    TALLOC_CTX *tmp_ctx;
-+    struct cache_req_domain *cr_domains = NULL;
-+    const char *domain_resolution_order = NULL;
-+    errno_t ret;
-+
-+    tmp_ctx = talloc_new(NULL);
-+    if (tmp_ctx == NULL) {
-+        return NULL;
-+    }
-+
-+    ret = sysdb_get_view_domain_resolution_order(tmp_ctx, sysdb,
-+                                                 &domain_resolution_order);
-+    if (ret != EOK && ret != ENOENT) {
-+        DEBUG(SSSDBG_MINOR_FAILURE,
-+              "sysdb_get_view_cache_req_domain() failed [%d]: [%s].\n",
-+              ret, sss_strerror(ret));
-+        goto done;
-+    }
-+
-+    /* Using mem_ctx (which is rctx) directly here to avoid copying
-+     * this memory around. */
-+    cr_domains = cache_req_domain_new_list_from_domain_resolution_order(
-+                                    mem_ctx, domains, domain_resolution_order);
-+    if (cr_domains == NULL) {
-+        ret = ENOMEM;
-+        DEBUG(SSSDBG_DEFAULT,
-+              "cache_req_domain_new_list_from_domain_resolution_order() "
-+              "failed [%d]: [%s].\n",
-+              ret, sss_strerror(ret));
-+        goto done;
-+    }
-+
-+done:
-+    talloc_free(tmp_ctx);
-+    return cr_domains;
-+}
-+
-+static struct cache_req_domain *
-+sss_resp_new_cr_domains_from_ipa_config(TALLOC_CTX *mem_ctx,
-+                                        struct sss_domain_info *domains,
-+                                        struct sysdb_ctx *sysdb,
-+                                        const char *domain)
-+{
-+    TALLOC_CTX *tmp_ctx;
-+    struct cache_req_domain *cr_domains = NULL;
-+    const char *domain_resolution_order = NULL;
-+    errno_t ret;
-+
-+    tmp_ctx = talloc_new(NULL);
-+    if (tmp_ctx == NULL) {
-+        return NULL;
-+    }
-+
-+    ret = sysdb_domain_get_domain_resolution_order(tmp_ctx, sysdb, domain,
-+                                                   &domain_resolution_order);
-+
-+    if (ret != EOK && ret != ENOENT) {
-+        DEBUG(SSSDBG_MINOR_FAILURE,
-+              "sysdb_domain_get_cache_req_domain() failed [%d]: [%s].\n",
-+              ret, sss_strerror(ret));
-+        goto done;
-+    }
-+
-+    /* Using mem_ctx (which is rctx) directly here to avoid copying
-+     * this memory around. */
-+    cr_domains = cache_req_domain_new_list_from_domain_resolution_order(
-+                                    mem_ctx, domains, domain_resolution_order);
-+    if (cr_domains == NULL) {
-+        DEBUG(SSSDBG_DEFAULT,
-+              "cache_req_domain_new_list_from_domain_resolution_order() "
-+              "failed [%d]: [%s].\n",
-+              ret, sss_strerror(ret));
-+        goto done;
-+    }
-+
-+done:
-+    talloc_free(tmp_ctx);
-+    return cr_domains;
-+}
-+
-+errno_t sss_resp_populate_cr_domains(struct resp_ctx *rctx)
-+{
-+    struct cache_req_domain *cr_domains = NULL;
-+    struct sss_domain_info *dom;
-+    errno_t ret;
-+
-+    for (dom = rctx->domains; dom != NULL; dom = dom->next) {
-+        if (dom->provider != NULL && strcmp(dom->provider, "ipa") == 0) {
-+            break;
-+        }
-+    }
-+
-+    if (dom == NULL) {
-+        cr_domains = cache_req_domain_new_list_from_domain_resolution_order(
-+                                                    rctx, rctx->domains, NULL);
-+        if (cr_domains == NULL) {
-+            DEBUG(SSSDBG_CRIT_FAILURE,
-+                  "Failed to flatten the list of domains.\n");
-+        }
-+        goto done;
-+    }
-+
-+    if (dom->has_views) {
-+        cr_domains = sss_resp_new_cr_domains_from_ipa_id_view(rctx,
-+                                                              rctx->domains,
-+                                                              dom->sysdb);
-+        if (cr_domains == NULL) {
-+            DEBUG(SSSDBG_MINOR_FAILURE,
-+                  "Failed to use ipaDomainResolutionOrder set for the "
-+                  "view \"%s\".\n"
-+                  "Trying to fallback to use ipaDomainOrderResolution "
-+                  "set in ipaConfig for the domain: %s.\n",
-+                  dom->view_name, dom->name);
-+        } else {
-+            goto done;
-+        }
-+    }
-+
-+    cr_domains = sss_resp_new_cr_domains_from_ipa_config(rctx, rctx->domains,
-+                                                         dom->sysdb,
-+                                                         dom->name);
-+    if (cr_domains == NULL) {
-+        DEBUG(SSSDBG_MINOR_FAILURE,
-+              "Failed to use ipaDomainResolutionOrder set in ipaConfig "
-+              "for the domain: \"%s\".\n"
-+              "No ipaDomainResolutionOrder will be followed.\n",
-+              dom->name);
-+    } else {
-+        goto done;
-+    }
-+
-+    cr_domains = cache_req_domain_new_list_from_domain_resolution_order(
-+                                                    rctx, rctx->domains, NULL);
-+    if (cr_domains == NULL) {
-+        DEBUG(SSSDBG_CRIT_FAILURE, "Failed to flatten the list of domains.\n");
-+        goto done;
-+    }
-+
-+done:
-+    ret = cr_domains != NULL ? EOK : ENOMEM;
-+
-+    cache_req_domain_list_zfree(&rctx->cr_domains);
-+    rctx->cr_domains = cr_domains;
-+
-+    return ret;
-+}
-diff --git a/src/responder/common/responder_get_domains.c b/src/responder/common/responder_get_domains.c
-index 0f9c01214631200f9687635f6302fa5c07e8a1fe..8c90b7773e248e1dd6d846c5050e1931fc50c786 100644
---- a/src/responder/common/responder_get_domains.c
-+++ b/src/responder/common/responder_get_domains.c
-@@ -192,6 +192,13 @@ struct tevent_req *sss_dp_get_domains_send(TALLOC_CTX *mem_ctx,
- 
-     if (state->dom == NULL) {
-         /* All domains were local */
-+        ret = sss_resp_populate_cr_domains(state->rctx);
-+        if (ret != EOK) {
-+            DEBUG(SSSDBG_CRIT_FAILURE,
-+                  "sss_resp_populate_cr_domains() failed [%d]: [%s]\n",
-+                  ret, sss_strerror(ret));
-+            goto immediately;
-+        }
-         ret = EOK;
-         goto immediately;
-     }
-@@ -253,6 +260,13 @@ sss_dp_get_domains_process(struct tevent_req *subreq)
-     if (state->dom == NULL) {
-         /* All domains were local */
-         set_time_of_last_request(state->rctx);
-+        ret = sss_resp_populate_cr_domains(state->rctx);
-+        if (ret != EOK) {
-+            DEBUG(SSSDBG_CRIT_FAILURE,
-+                  "sss_resp_populate_cr_domains() failed [%d]: [%s]\n",
-+                  ret, sss_strerror(ret));
-+            goto fail;
-+        }
-         tevent_req_done(req);
-         return;
-     }
-diff --git a/src/tests/cmocka/common_mock_resp.c b/src/tests/cmocka/common_mock_resp.c
-index 88808b1b9394b7a9ee7e58b30b4fbd9d467493d3..175101fc51fd395d792b1fccaecdb327caef2b64 100644
---- a/src/tests/cmocka/common_mock_resp.c
-+++ b/src/tests/cmocka/common_mock_resp.c
-@@ -51,6 +51,12 @@ mock_rctx(TALLOC_CTX *mem_ctx,
-     rctx->ev = ev;
-     rctx->domains = domains;
-     rctx->pvt_ctx = pvt_ctx;
-+    if (domains != NULL) {
-+        ret = sss_resp_populate_cr_domains(rctx);
-+        if (ret != EOK) {
-+            return NULL;
-+        }
-+    }
-     return rctx;
- }
- 
-diff --git a/src/tests/cmocka/common_mock_resp_dp.c b/src/tests/cmocka/common_mock_resp_dp.c
-index 5db5255ab61231870982c4b78a39504ae8954bcd..4b38a38e6f53499132f9fe14a0ec0af157cf85ca 100644
---- a/src/tests/cmocka/common_mock_resp_dp.c
-+++ b/src/tests/cmocka/common_mock_resp_dp.c
-@@ -21,6 +21,7 @@
- */
- 
- #include "util/util.h"
-+#include "responder/common/responder.h"
- #include "tests/cmocka/common_mock_resp.h"
- 
- /* Mock DP requests that finish immediatelly and return
-@@ -165,6 +166,12 @@ sss_dp_get_domains_send(TALLOC_CTX *mem_ctx,
-                         bool force,
-                         const char *hint)
- {
-+    errno_t ret;
-+    ret = sss_resp_populate_cr_domains(rctx);
-+    if (ret != EOK) {
-+        return NULL;
-+    }
-+
-     return test_req_succeed_send(mem_ctx, rctx->ev);
- }
- 
-diff --git a/src/tests/cmocka/test_nss_srv.c b/src/tests/cmocka/test_nss_srv.c
-index ede72b341b60842ad470df2794aa90ea9797e999..2f526660cbbbf2443dbae4e213c1336feb6c661e 100644
---- a/src/tests/cmocka/test_nss_srv.c
-+++ b/src/tests/cmocka/test_nss_srv.c
-@@ -3440,6 +3440,10 @@ static int nss_subdom_test_setup(void **state)
-                                   nss_test_ctx->tctx->confdb);
-     assert_int_equal(ret, EOK);
- 
-+    ret = sss_resp_populate_cr_domains(nss_test_ctx->rctx);
-+    assert_int_equal(ret, EOK);
-+    assert_non_null(nss_test_ctx->rctx->cr_domains);
-+
-     nss_test_ctx->subdom = nss_test_ctx->tctx->dom->subdomains;
- 
-     ret = store_group(nss_test_ctx, nss_test_ctx->subdom,
-diff --git a/src/tests/cwrap/Makefile.am b/src/tests/cwrap/Makefile.am
-index 4a4090df9296aadde308249f533e7ba246e92f93..c99ebde5f0fc18d1283392cbb307434579d5d811 100644
---- a/src/tests/cwrap/Makefile.am
-+++ b/src/tests/cwrap/Makefile.am
-@@ -41,6 +41,7 @@ SSSD_CACHE_REQ_OBJ = \
-     ../../../src/responder/common/cache_req/cache_req_result.c \
-     ../../../src/responder/common/cache_req/cache_req_search.c \
-     ../../../src/responder/common/cache_req/cache_req_data.c \
-+    ../../../src/responder/common/cache_req/cache_req_domain.c \
-     ../../../src/responder/common/cache_req/plugins/cache_req_common.c \
-     ../../../src/responder/common/cache_req/plugins/cache_req_enum_users.c \
-     ../../../src/responder/common/cache_req/plugins/cache_req_enum_groups.c \
--- 
-2.9.3
-
diff --git a/SOURCES/0052-ifp-add-method-to-refresh-access-control-rules-in-do.patch b/SOURCES/0052-ifp-add-method-to-refresh-access-control-rules-in-do.patch
new file mode 100644
index 0000000..55e224d
--- /dev/null
+++ b/SOURCES/0052-ifp-add-method-to-refresh-access-control-rules-in-do.patch
@@ -0,0 +1,158 @@
+From c6e02c84a567127b37b1c036abf7952f1d36783a Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pavel=20B=C5=99ezina?= <pbrezina@redhat.com>
+Date: Thu, 2 Nov 2017 14:59:57 +0100
+Subject: [PATCH 52/57] ifp: add method to refresh access control rules in
+ domain
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Related:
+https://pagure.io/SSSD/sssd/issue/2840
+
+Reviewed-by: Fabiano Fidêncio <fidencio@redhat.com>
+Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
+(cherry picked from commit c6cf752337f5977ce3753b7113dc1a2342c86319)
+---
+ src/responder/ifp/ifp_domains.c         | 22 ++++++++++++++++++++++
+ src/responder/ifp/ifp_domains.h         |  3 +++
+ src/responder/ifp/ifp_iface.c           |  3 ++-
+ src/responder/ifp/ifp_iface.xml         |  3 +++
+ src/responder/ifp/ifp_iface_generated.c | 13 +++++++++++++
+ src/responder/ifp/ifp_iface_generated.h |  5 +++++
+ 6 files changed, 48 insertions(+), 1 deletion(-)
+
+diff --git a/src/responder/ifp/ifp_domains.c b/src/responder/ifp/ifp_domains.c
+index 977bbfcbe818f08873ce072d34fdcf900cabf52f..cd7e2fc7aeff5467514269e5b078b0da88ab4f50 100644
+--- a/src/responder/ifp/ifp_domains.c
++++ b/src/responder/ifp/ifp_domains.c
+@@ -630,3 +630,25 @@ int ifp_domains_domain_list_servers(struct sbus_request *sbus_req,
+ 
+     return EOK;
+ }
++
++int ifp_domains_domain_refresh_access_rules(struct sbus_request *sbus_req,
++                                            void *data)
++{
++    struct ifp_ctx *ifp_ctx;
++    struct sss_domain_info *dom;
++
++    ifp_ctx = talloc_get_type(data, struct ifp_ctx);
++
++    dom = get_domain_info_from_req(sbus_req, data);
++    if (dom == NULL) {
++        sbus_request_reply_error(sbus_req, SBUS_ERROR_UNKNOWN_DOMAIN,
++                                 "Unknown domain");
++        return EOK;
++    }
++
++    rdp_message_send_and_reply(sbus_req, ifp_ctx->rctx, dom, DP_PATH,
++                               IFACE_DP_ACCESS_CONTROL,
++                               IFACE_DP_ACCESS_CONTROL_REFRESHRULES);
++
++    return EOK;
++}
+diff --git a/src/responder/ifp/ifp_domains.h b/src/responder/ifp/ifp_domains.h
+index 621ba6158e285911cb8298cef212219dfd3afec8..d8cc9d34c92cd04b6db432c1fc0e179a717001da 100644
+--- a/src/responder/ifp/ifp_domains.h
++++ b/src/responder/ifp/ifp_domains.h
+@@ -108,4 +108,7 @@ int ifp_domains_domain_list_servers(struct sbus_request *sbus_req,
+                                     void *data,
+                                     const char *service);
+ 
++int ifp_domains_domain_refresh_access_rules(struct sbus_request *sbus_req,
++                                            void *data);
++
+ #endif /* IFP_DOMAINS_H_ */
+diff --git a/src/responder/ifp/ifp_iface.c b/src/responder/ifp/ifp_iface.c
+index 3293b92d750d33b2ecf77a03098c5169d052c924..f995e28f99f9489ca17fbc51fa6894d458f9e21f 100644
+--- a/src/responder/ifp/ifp_iface.c
++++ b/src/responder/ifp/ifp_iface.c
+@@ -79,7 +79,8 @@ struct iface_ifp_domains_domain iface_ifp_domains_domain = {
+     .IsOnline = ifp_domains_domain_is_online,
+     .ListServices = ifp_domains_domain_list_services,
+     .ActiveServer = ifp_domains_domain_active_server,
+-    .ListServers = ifp_domains_domain_list_servers
++    .ListServers = ifp_domains_domain_list_servers,
++    .RefreshAccessRules = ifp_domains_domain_refresh_access_rules
+ };
+ 
+ struct iface_ifp_users iface_ifp_users = {
+diff --git a/src/responder/ifp/ifp_iface.xml b/src/responder/ifp/ifp_iface.xml
+index 39385e866f31131c7860001ae4d6e6b51105aa52..1aa7eac03f0a3dc86f1d25883ac37f2fabf6b9e8 100644
+--- a/src/responder/ifp/ifp_iface.xml
++++ b/src/responder/ifp/ifp_iface.xml
+@@ -112,6 +112,9 @@
+             <arg name="service_name" type="s" direction="in" />
+             <arg name="servers" type="as" direction="out" />
+         </method>
++
++        <method name="RefreshAccessRules">
++        </method>
+     </interface>
+ 
+     <interface name="org.freedesktop.sssd.infopipe.Cache">
+diff --git a/src/responder/ifp/ifp_iface_generated.c b/src/responder/ifp/ifp_iface_generated.c
+index 6943e38e3b6d2fc9e09ade1a863905c8d81a39ba..c2cdbf5b0ef3d59068aeed7a8f45099c14c4a94a 100644
+--- a/src/responder/ifp/ifp_iface_generated.c
++++ b/src/responder/ifp/ifp_iface_generated.c
+@@ -552,6 +552,12 @@ int iface_ifp_domains_domain_ListServers_finish(struct sbus_request *req, const
+                                          DBUS_TYPE_INVALID);
+ }
+ 
++int iface_ifp_domains_domain_RefreshAccessRules_finish(struct sbus_request *req)
++{
++   return sbus_request_return_and_finish(req,
++                                         DBUS_TYPE_INVALID);
++}
++
+ /* methods for org.freedesktop.sssd.infopipe.Domains.Domain */
+ const struct sbus_method_meta iface_ifp_domains_domain__methods[] = {
+     {
+@@ -582,6 +588,13 @@ const struct sbus_method_meta iface_ifp_domains_domain__methods[] = {
+         offsetof(struct iface_ifp_domains_domain, ListServers),
+         invoke_s_method,
+     },
++    {
++        "RefreshAccessRules", /* name */
++        NULL, /* no in_args */
++        NULL, /* no out_args */
++        offsetof(struct iface_ifp_domains_domain, RefreshAccessRules),
++        NULL, /* no invoker */
++    },
+     { NULL, }
+ };
+ 
+diff --git a/src/responder/ifp/ifp_iface_generated.h b/src/responder/ifp/ifp_iface_generated.h
+index 30752bf063de1f2530c7451f01cc22ad3e863185..f1e6c80bab27d0ed581abc566a178e6857794805 100644
+--- a/src/responder/ifp/ifp_iface_generated.h
++++ b/src/responder/ifp/ifp_iface_generated.h
+@@ -57,6 +57,7 @@
+ #define IFACE_IFP_DOMAINS_DOMAIN_LISTSERVICES "ListServices"
+ #define IFACE_IFP_DOMAINS_DOMAIN_ACTIVESERVER "ActiveServer"
+ #define IFACE_IFP_DOMAINS_DOMAIN_LISTSERVERS "ListServers"
++#define IFACE_IFP_DOMAINS_DOMAIN_REFRESHACCESSRULES "RefreshAccessRules"
+ 
+ /* constants for org.freedesktop.sssd.infopipe.Cache */
+ #define IFACE_IFP_CACHE "org.freedesktop.sssd.infopipe.Cache"
+@@ -209,6 +210,7 @@ struct iface_ifp_domains_domain {
+     int (*ListServices)(struct sbus_request *req, void *data);
+     int (*ActiveServer)(struct sbus_request *req, void *data, const char *arg_service);
+     int (*ListServers)(struct sbus_request *req, void *data, const char *arg_service_name);
++    int (*RefreshAccessRules)(struct sbus_request *req, void *data);
+ };
+ 
+ /* finish function for IsOnline */
+@@ -223,6 +225,9 @@ int iface_ifp_domains_domain_ActiveServer_finish(struct sbus_request *req, const
+ /* finish function for ListServers */
+ int iface_ifp_domains_domain_ListServers_finish(struct sbus_request *req, const char *arg_servers[], int len_servers);
+ 
++/* finish function for RefreshAccessRules */
++int iface_ifp_domains_domain_RefreshAccessRules_finish(struct sbus_request *req);
++
+ /* vtable for org.freedesktop.sssd.infopipe.Cache */
+ struct iface_ifp_cache {
+     struct sbus_vtable vtable; /* derive from sbus_vtable */
+-- 
+2.14.3
+
diff --git a/SOURCES/0053-UTIL-Expose-replace_char-as-sss_replace_char.patch b/SOURCES/0053-UTIL-Expose-replace_char-as-sss_replace_char.patch
deleted file mode 100644
index 0db08c7..0000000
--- a/SOURCES/0053-UTIL-Expose-replace_char-as-sss_replace_char.patch
+++ /dev/null
@@ -1,81 +0,0 @@
-From 3a3b761bc89aa860ca7e6af323c3e0425306014c Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Fabiano=20Fid=C3=AAncio?= <fidencio@redhat.com>
-Date: Sun, 26 Mar 2017 01:49:53 +0100
-Subject: [PATCH 53/54] UTIL: Expose replace_char() as sss_replace_char()
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-This method is going to be used in the follow-up patch for replacing ','
-by ':' so we can keep the domain resolution order option consitent with
-the way it's set on IPA side and still keep consistent with the way
-lists are represented on sssd.conf file.
-
-Related:
-https://pagure.io/SSSD/sssd/issue/3001
-
-Signed-off-by: Fabiano Fidêncio <fidencio@redhat.com>
-
-Reviewed-by: Sumit Bose <sbose@redhat.com>
-Reviewed-by: Pavel Březina <pbrezina@redhat.com>
----
- src/util/string_utils.c | 12 ++++++------
- src/util/util.h         |  5 +++++
- 2 files changed, 11 insertions(+), 6 deletions(-)
-
-diff --git a/src/util/string_utils.c b/src/util/string_utils.c
-index 872b7e29e55e8628085affd07f3363019aae5ee9..1215ec96a57089a13f455812adf5a0b0812afa6d 100644
---- a/src/util/string_utils.c
-+++ b/src/util/string_utils.c
-@@ -22,10 +22,10 @@
- 
- #include "util/util.h"
- 
--static char *replace_char(TALLOC_CTX *mem_ctx,
--                          const char *in,
--                          const char match,
--                          const char sub)
-+char *sss_replace_char(TALLOC_CTX *mem_ctx,
-+                       const char *in,
-+                       const char match,
-+                       const char sub)
- {
-     char *p;
-     char *out;
-@@ -63,7 +63,7 @@ char * sss_replace_space(TALLOC_CTX *mem_ctx,
-         return talloc_strdup(mem_ctx, orig_name);
-     }
- 
--    return replace_char(mem_ctx, orig_name, ' ', subst);
-+    return sss_replace_char(mem_ctx, orig_name, ' ', subst);
- }
- 
- char * sss_reverse_replace_space(TALLOC_CTX *mem_ctx,
-@@ -81,7 +81,7 @@ char * sss_reverse_replace_space(TALLOC_CTX *mem_ctx,
-         return talloc_strdup(mem_ctx, orig_name);
-     }
- 
--    return replace_char(mem_ctx, orig_name, subst, ' ');
-+    return sss_replace_char(mem_ctx, orig_name, subst, ' ');
- }
- 
- errno_t guid_blob_to_string_buf(const uint8_t *blob, char *str_buf,
-diff --git a/src/util/util.h b/src/util/util.h
-index 82760940269ad8883e725e3a5cf463486c9cfd36..2170c5fb7cffda3910d2b58e33ec7abe3ec4a7d4 100644
---- a/src/util/util.h
-+++ b/src/util/util.h
-@@ -600,6 +600,11 @@ errno_t name_to_well_known_sid(const char *dom, const char *name,
-                                const char **sid);
- 
- /* from string_utils.c */
-+char *sss_replace_char(TALLOC_CTX *mem_ctx,
-+                       const char *in,
-+                       const char match,
-+                       const char sub);
-+
- char * sss_replace_space(TALLOC_CTX *mem_ctx,
-                          const char *orig_name,
-                          const char replace_char);
--- 
-2.9.3
-
diff --git a/SOURCES/0053-sssctl-call-dbus-instead-of-pam-to-refresh-HBAC-rule.patch b/SOURCES/0053-sssctl-call-dbus-instead-of-pam-to-refresh-HBAC-rule.patch
new file mode 100644
index 0000000..4c2404e
--- /dev/null
+++ b/SOURCES/0053-sssctl-call-dbus-instead-of-pam-to-refresh-HBAC-rule.patch
@@ -0,0 +1,200 @@
+From cbe1f1f8fa207eded53260a0fb288c5b31b18c96 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pavel=20B=C5=99ezina?= <pbrezina@redhat.com>
+Date: Thu, 2 Nov 2017 15:00:17 +0100
+Subject: [PATCH 53/57] sssctl: call dbus instead of pam to refresh HBAC rules
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Related:
+https://pagure.io/SSSD/sssd/issue/2840
+
+Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
+Reviewed-by: Fabiano Fidêncio <fidencio@redhat.com>
+(cherry picked from commit be804178d5e5fee64be2b080e73f4ce7b0074f76)
+---
+ src/tools/sssctl/sssctl_access_report.c | 127 +++++++++++++++-----------------
+ 1 file changed, 58 insertions(+), 69 deletions(-)
+
+diff --git a/src/tools/sssctl/sssctl_access_report.c b/src/tools/sssctl/sssctl_access_report.c
+index 11172329817b4dedaca480ab8a4537149853c330..8cf1a8a871b27827c317d658c0f93f34773c4841 100644
+--- a/src/tools/sssctl/sssctl_access_report.c
++++ b/src/tools/sssctl/sssctl_access_report.c
+@@ -15,11 +15,11 @@
+     along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+ 
+-#include <security/pam_appl.h>
+-
+ #include "util/util.h"
+ #include "tools/common/sss_tools.h"
+ #include "tools/sssctl/sssctl.h"
++#include "sbus/sssd_dbus.h"
++#include "responder/ifp/ifp_iface.h"
+ 
+ /*
+  * We're searching the cache directly..
+@@ -27,58 +27,9 @@
+ #include "providers/ipa/ipa_hbac_private.h"
+ #include "providers/ipa/ipa_rules_common.h"
+ 
+-#ifdef HAVE_SECURITY_PAM_MISC_H
+-# include <security/pam_misc.h>
+-#elif defined(HAVE_SECURITY_OPENPAM_H)
+-# include <security/openpam.h>
+-#endif
+-
+-#ifdef HAVE_SECURITY_PAM_MISC_H
+-static struct pam_conv conv = {
+-    misc_conv,
+-    NULL
+-};
+-#elif defined(HAVE_SECURITY_OPENPAM_H)
+-static struct pam_conv conv = {
+-    openpam_ttyconv,
+-    NULL
+-};
+-#else
+-# error "Missing text based pam conversation function"
+-#endif
+-
+-#ifndef DEFAULT_SERVICE
+-#define DEFAULT_SERVICE "system-auth"
+-#endif /* DEFAULT_SERVICE */
+-
+-#ifndef DEFAULT_USER
+-#define DEFAULT_USER "admin"
+-#endif /* DEFAULT_USER */
+-
+ typedef errno_t (*sssctl_dom_access_reporter_fn)(struct sss_tool_ctx *tool_ctx,
+-                                                 const char *user,
+-                                                 const char *service,
+                                                  struct sss_domain_info *domain);
+ 
+-static errno_t run_pam_acct(struct sss_tool_ctx *tool_ctx,
+-                            const char *user,
+-                            const char *service,
+-                            struct sss_domain_info *domain)
+-{
+-    errno_t ret;
+-    pam_handle_t *pamh;
+-
+-    ret = pam_start(service, user, &conv, &pamh);
+-    if (ret != PAM_SUCCESS) {
+-        ERROR("pam_start failed: %s\n", pam_strerror(pamh, ret));
+-        return EIO;
+-    }
+-
+-    ret = pam_acct_mgmt(pamh, 0);
+-    pam_end(pamh, ret);
+-    return ret;
+-}
+-
+ static errno_t get_rdn_value(TALLOC_CTX *mem_ctx,
+                              struct sss_domain_info *dom,
+                              const char *dn_attr,
+@@ -315,9 +266,58 @@ static void print_ipa_hbac_rule(struct sss_domain_info *domain,
+     PRINT("\n");
+ }
+ 
++static errno_t refresh_hbac_rules(struct sss_tool_ctx *tool_ctx,
++                                  struct sss_domain_info *domain)
++{
++    TALLOC_CTX *tmp_ctx;
++    sss_sifp_error error;
++    sss_sifp_ctx *sifp;
++    DBusMessage *reply;
++    const char *path;
++    errno_t ret;
++
++    tmp_ctx = talloc_new(NULL);
++    if (tmp_ctx == NULL) {
++        DEBUG(SSSDBG_CRIT_FAILURE, "talloc_new() failed\n");
++        return ENOMEM;
++    }
++
++    path = sbus_opath_compose(tmp_ctx, IFP_PATH_DOMAINS, domain->name);
++    if (path == NULL) {
++        printf(_("Out of memory!\n"));
++        ret = ENOMEM;
++        goto done;
++    }
++
++    error = sssctl_sifp_init(tool_ctx, &sifp);
++    if (error != SSS_SIFP_OK) {
++        sssctl_sifp_error(sifp, error, "Unable to connect to the InfoPipe");
++        ret = EIO;
++        goto done;
++    }
++
++    error = sssctl_sifp_send(tmp_ctx, sifp, &reply, path,
++                             IFACE_IFP_DOMAINS_DOMAIN,
++                             IFACE_IFP_DOMAINS_DOMAIN_REFRESHACCESSRULES);
++    if (error != SSS_SIFP_OK) {
++        sssctl_sifp_error(sifp, error, "Unable to refresh HBAC rules");
++        ret = EIO;
++        goto done;
++    }
++
++    ret = sbus_parse_reply(reply);
++    if (ret != EOK) {
++        goto done;
++    }
++
++    ret = EOK;
++
++done:
++    talloc_free(tmp_ctx);
++    return ret;
++}
++
+ static errno_t sssctl_ipa_access_report(struct sss_tool_ctx *tool_ctx,
+-                                        const char *user,
+-                                        const char *service,
+                                         struct sss_domain_info *domain)
+ {
+     TALLOC_CTX *tmp_ctx = NULL;
+@@ -338,9 +338,9 @@ static errno_t sssctl_ipa_access_report(struct sss_tool_ctx *tool_ctx,
+     struct ldb_message **msgs = NULL;
+ 
+     /* Run the pam account phase to make sure the rules are fetched by SSSD */
+-    ret = run_pam_acct(tool_ctx, user, service, domain);
+-    if (ret != PAM_SUCCESS && ret != PAM_PERM_DENIED) {
+-        ERROR("Cannot run the PAM account phase, reporting stale rules\n");
++    ret = refresh_hbac_rules(tool_ctx, domain);
++    if (ret != EOK) {
++        ERROR("Unable to refresh HBAC rules, using cached content\n");
+         /* Non-fatal */
+     }
+ 
+@@ -398,19 +398,8 @@ errno_t sssctl_access_report(struct sss_cmdline *cmdline,
+     const char *domname = NULL;
+     sssctl_dom_access_reporter_fn reporter;
+     struct sss_domain_info *dom;
+-    const char *user = DEFAULT_USER;
+-    const char *service = DEFAULT_SERVICE;
+ 
+-    /* Parse command line. */
+-    struct poptOption options[] = {
+-        { "user", 'u', POPT_ARG_STRING, &user, 0,
+-          _("PAM user, default: " DEFAULT_USER), NULL },
+-        { "service", 's', POPT_ARG_STRING, &service, 0,
+-          _("PAM service, default: " DEFAULT_SERVICE), NULL },
+-        POPT_TABLEEND
+-    };
+-
+-    ret = sss_tool_popt_ex(cmdline, options, SSS_TOOL_OPT_OPTIONAL,
++    ret = sss_tool_popt_ex(cmdline, NULL, SSS_TOOL_OPT_OPTIONAL,
+                            NULL, NULL, "DOMAIN", _("Specify domain name."),
+                            &domname, NULL);
+     if (ret != EOK) {
+@@ -431,5 +420,5 @@ errno_t sssctl_access_report(struct sss_cmdline *cmdline,
+         return ret;
+     }
+ 
+-    return reporter(tool_ctx, user, service, dom);
++    return reporter(tool_ctx, dom);
+ }
+-- 
+2.14.3
+
diff --git a/SOURCES/0054-Add-domain_resolution_order-config-option.patch b/SOURCES/0054-Add-domain_resolution_order-config-option.patch
deleted file mode 100644
index e91960d..0000000
--- a/SOURCES/0054-Add-domain_resolution_order-config-option.patch
+++ /dev/null
@@ -1,205 +0,0 @@
-From 26b838f2229483952aeec92a3446acef828244c4 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Fabiano=20Fid=C3=AAncio?= <fidencio@redhat.com>
-Date: Sun, 26 Mar 2017 03:00:14 +0200
-Subject: [PATCH 54/54] Add domain_resolution_order config option
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-This is the local equivalent of option of ipaDomainResolutionOrder and
-has precedence over the ones set on IPA side making the precedence order
-to be like: Local > View > Globally.
-
-As done for the IPA side configurations, the domains which were not
-explicitly set up will be apennded to the final of the
-domain_resolution_order list in the very same order they're presented in
-the "domains" option of [sssd] section in the config file. There's no
-guarantee of order for the subdomains though.
-
-It's also important to mention that no expansion magic is performed on
-our side. It means that if 'example.com' is set it does *not* stand for
-all its subdomains DNS wise (like 'foo.example.com', 'bar.example.com',
-etc).
-
-Related:
-https://pagure.io/SSSD/sssd/issue/3001
-
-Signed-off-by: Fabiano Fidêncio <fidencio@redhat.com>
-
-Reviewed-by: Sumit Bose <sbose@redhat.com>
-Reviewed-by: Pavel Březina <pbrezina@redhat.com>
----
- src/confdb/confdb.h                     |  1 +
- src/config/SSSDConfig/__init__.py.in    |  1 +
- src/config/SSSDConfigTest.py            |  7 ++++++-
- src/config/cfg_rules.ini                |  1 +
- src/config/etc/sssd.api.conf            |  1 +
- src/man/sssd.conf.5.xml                 | 20 ++++++++++++++++++++
- src/responder/common/responder.h        |  1 +
- src/responder/common/responder_common.c | 27 +++++++++++++++++++++++++++
- 8 files changed, 58 insertions(+), 1 deletion(-)
-
-diff --git a/src/confdb/confdb.h b/src/confdb/confdb.h
-index fb60675ca8beb2c2a157bf021ed9cad362742988..56a603652d6c8256735e7f8b125300ff7b254645 100644
---- a/src/confdb/confdb.h
-+++ b/src/confdb/confdb.h
-@@ -74,6 +74,7 @@
- #define CONFDB_MONITOR_CERT_VERIFICATION "certificate_verification"
- #define CONFDB_MONITOR_DISABLE_NETLINK "disable_netlink"
- #define CONFDB_MONITOR_ENABLE_FILES_DOM "enable_files_domain"
-+#define CONFDB_MONITOR_DOMAIN_RESOLUTION_ORDER "domain_resolution_order"
- 
- /* Both monitor and domains */
- #define CONFDB_NAME_REGEX   "re_expression"
-diff --git a/src/config/SSSDConfig/__init__.py.in b/src/config/SSSDConfig/__init__.py.in
-index 03a1a43336604bb815626e64cb54052bdf87acf2..e7fb7673d393d4f12910f355d3edf33f4390c1f1 100644
---- a/src/config/SSSDConfig/__init__.py.in
-+++ b/src/config/SSSDConfig/__init__.py.in
-@@ -66,6 +66,7 @@ option_strings = {
-     'override_space': _('All spaces in group or user names will be replaced with this character'),
-     'disable_netlink' : _('Tune sssd to honor or ignore netlink state changes'),
-     'enable_files_domain' : _('Enable or disable the implicit files domain'),
-+    'domain_resolution_order': _('A specific order of the domains to be looked up'),
- 
-     # [nss]
-     'enum_cache_timeout' : _('Enumeration cache timeout length (seconds)'),
-diff --git a/src/config/SSSDConfigTest.py b/src/config/SSSDConfigTest.py
-index 457a6f0a09e7139a05f29f8bef7e475fe3b58ec2..6899bf8ae04bf210546c8cbdba8235f094e23dc0 100755
---- a/src/config/SSSDConfigTest.py
-+++ b/src/config/SSSDConfigTest.py
-@@ -94,6 +94,10 @@ class SSSDConfigTestValid(unittest.TestCase):
-         self.assertTrue('default_domain_suffix' in new_options)
-         self.assertEquals(new_options['default_domain_suffix'][0], str)
- 
-+        self.assertTrue('domain_resolution_order' in new_options)
-+        self.assertEquals(new_options['domain_resolution_order'][0], list)
-+        self.assertEquals(new_options['domain_resolution_order'][1], str)
-+
-         del sssdconfig
- 
-     def testDomains(self):
-@@ -314,7 +318,8 @@ class SSSDConfigTestSSSDService(unittest.TestCase):
-             'certificate_verification',
-             'override_space',
-             'disable_netlink',
--            'enable_files_domain']
-+            'enable_files_domain',
-+            'domain_resolution_order']
- 
-         self.assertTrue(type(options) == dict,
-                         "Options should be a dictionary")
-diff --git a/src/config/cfg_rules.ini b/src/config/cfg_rules.ini
-index 933ebccd828189d923d2186753dfbc0b5c0814ce..41efcea552a82c5492a0d21a8d0797ee42cdc8c7 100644
---- a/src/config/cfg_rules.ini
-+++ b/src/config/cfg_rules.ini
-@@ -43,6 +43,7 @@ option = override_space
- option = config_file_version
- option = disable_netlink
- option = enable_files_domain
-+option = domain_resolution_order
- 
- [rule/allowed_nss_options]
- validator = ini_allowed_options
-diff --git a/src/config/etc/sssd.api.conf b/src/config/etc/sssd.api.conf
-index 08cecf00367aaaab3794a48bd1e728421a996e49..6965028e1ca748f8b6677d9fc1faa66d5c307a0c 100644
---- a/src/config/etc/sssd.api.conf
-+++ b/src/config/etc/sssd.api.conf
-@@ -32,6 +32,7 @@ certificate_verification = str, None, false
- override_space = str, None, false
- disable_netlink = bool, None, false
- enable_files_domain = str, None, false
-+domain_resolution_order = list, str, false
- 
- [nss]
- # Name service
-diff --git a/src/man/sssd.conf.5.xml b/src/man/sssd.conf.5.xml
-index 1c27742cf0c1b6ffad23ab5b044bf4a168ed8f69..4fe13b85d511fb6a2ccc9b4de956710b05bc898c 100644
---- a/src/man/sssd.conf.5.xml
-+++ b/src/man/sssd.conf.5.xml
-@@ -542,6 +542,26 @@
-                             </para>
-                         </listitem>
-                     </varlistentry>
-+                    <varlistentry>
-+                        <term>domain_resolution_order</term>
-+                        <listitem>
-+                            <para>
-+                                Comma separated list of domains and subdomains
-+                                representing the lookup order that will be
-+                                followed.
-+                                The list doesn't have to include all possible
-+                                domains as the missing domains will be looked
-+                                up based on the order they're presented in the
-+                                <quote>domains</quote> configuration option.
-+                                The subdomains which are not listed as part of
-+                                <quote>lookup_order</quote> will be looked up
-+                                in a random order for each parent domain.
-+                            </para>
-+                            <para>
-+                                Default: Not set
-+                            </para>
-+                        </listitem>
-+                    </varlistentry>
-                 </variablelist>
-             </para>
-         </refsect2>
-diff --git a/src/responder/common/responder.h b/src/responder/common/responder.h
-index 29e3f95caf484f43307c9c28d4abd3f50f360a95..4210307489fe25829a1674f254ecc7d185029698 100644
---- a/src/responder/common/responder.h
-+++ b/src/responder/common/responder.h
-@@ -115,6 +115,7 @@ struct resp_ctx {
-     int client_idle_timeout;
- 
-     struct cache_req_domain *cr_domains;
-+    const char *domain_resolution_order;
- 
-     time_t last_request_time;
-     int idle_timeout;
-diff --git a/src/responder/common/responder_common.c b/src/responder/common/responder_common.c
-index 1792a4c3771fa326c7cca31e1981dce315c03758..154d7dc7718c437d10e152fcba98161e2034fb14 100644
---- a/src/responder/common/responder_common.c
-+++ b/src/responder/common/responder_common.c
-@@ -1163,6 +1163,19 @@ int sss_process_init(TALLOC_CTX *mem_ctx,
-         rctx->override_space = tmp[0];
-     }
- 
-+    ret = confdb_get_string(rctx->cdb, rctx,
-+                            CONFDB_MONITOR_CONF_ENTRY,
-+                            CONFDB_MONITOR_DOMAIN_RESOLUTION_ORDER, NULL,
-+                            &tmp);
-+    if (ret == EOK) {
-+        rctx->domain_resolution_order = sss_replace_char(rctx, tmp, ',', ':');
-+    } else {
-+        DEBUG(SSSDBG_MINOR_FAILURE,
-+              "Cannot get the \"domain_resolution_order\" option.\n"
-+              "The set up lookup_order won't be followed [%d]: %s.\n",
-+              ret, sss_strerror(ret));
-+    }
-+
-     ret = sss_monitor_init(rctx, rctx->ev, monitor_intf,
-                            svc_name, svc_version, MT_SVC_SERVICE,
-                            rctx, &rctx->last_request_time,
-@@ -1546,6 +1559,20 @@ errno_t sss_resp_populate_cr_domains(struct resp_ctx *rctx)
-     struct sss_domain_info *dom;
-     errno_t ret;
- 
-+    if (rctx->domain_resolution_order != NULL) {
-+        cr_domains = cache_req_domain_new_list_from_domain_resolution_order(
-+                            rctx, rctx->domains, rctx->domain_resolution_order);
-+
-+        if (cr_domains == NULL) {
-+            DEBUG(SSSDBG_MINOR_FAILURE,
-+                  "Failed to use domain_resolution_order set in the config file.\n"
-+                  "Trying to fallback to use ipaDomainOrderResolution setup by "
-+                  "IPA.\n");
-+        } else {
-+            goto done;
-+        }
-+    }
-+
-     for (dom = rctx->domains; dom != NULL; dom = dom->next) {
-         if (dom->provider != NULL && strcmp(dom->provider, "ipa") == 0) {
-             break;
--- 
-2.9.3
-
diff --git a/SOURCES/0054-sysdb-be_refresh_get_values_ex-remove-unused-option.patch b/SOURCES/0054-sysdb-be_refresh_get_values_ex-remove-unused-option.patch
new file mode 100644
index 0000000..a4f566f
--- /dev/null
+++ b/SOURCES/0054-sysdb-be_refresh_get_values_ex-remove-unused-option.patch
@@ -0,0 +1,66 @@
+From e32a5bd6fa76c7146a7024d42efeb26895337048 Mon Sep 17 00:00:00 2001
+From: Sumit Bose <sbose@redhat.com>
+Date: Wed, 8 Nov 2017 14:04:40 +0100
+Subject: [PATCH 54/57] sysdb: be_refresh_get_values_ex() remove unused option
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+The objectclass argument is not used in be_refresh_get_values_ex()
+anymore.
+
+Related to https://pagure.io/SSSD/sssd/issue/3503
+
+Reviewed-by: Fabiano Fidêncio <fidencio@redhat.com>
+Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
+(cherry picked from commit 0cce3d3ad14caf406303cf2ce6bf80171b708a93)
+---
+ src/providers/be_refresh.c | 7 +------
+ 1 file changed, 1 insertion(+), 6 deletions(-)
+
+diff --git a/src/providers/be_refresh.c b/src/providers/be_refresh.c
+index 81f2c5d1d368987bf9135fce730d917a32c1e24f..e8cf5da75847cd6ab647e91865b36ec6e4e59822 100644
+--- a/src/providers/be_refresh.c
++++ b/src/providers/be_refresh.c
+@@ -32,7 +32,6 @@
+ static errno_t be_refresh_get_values_ex(TALLOC_CTX *mem_ctx,
+                                         struct sss_domain_info *domain,
+                                         time_t period,
+-                                        const char *objectclass,
+                                         struct ldb_dn *base_dn,
+                                         const char *attr,
+                                         char ***_values)
+@@ -96,21 +95,17 @@ static errno_t be_refresh_get_values(TALLOC_CTX *mem_ctx,
+                                      char ***_values)
+ {
+     struct ldb_dn *base_dn = NULL;
+-    const char *class = NULL;
+     errno_t ret;
+ 
+     switch (type) {
+     case BE_REFRESH_TYPE_USERS:
+         base_dn = sysdb_user_base_dn(mem_ctx, domain);
+-        class = SYSDB_USER_CLASS;
+         break;
+     case BE_REFRESH_TYPE_GROUPS:
+         base_dn = sysdb_group_base_dn(mem_ctx, domain);
+-        class = SYSDB_GROUP_CLASS;
+         break;
+     case BE_REFRESH_TYPE_NETGROUPS:
+         base_dn = sysdb_netgroup_base_dn(mem_ctx, domain);
+-        class = SYSDB_NETGROUP_CLASS;
+         break;
+     case BE_REFRESH_TYPE_SENTINEL:
+         return ERR_INTERNAL;
+@@ -121,7 +116,7 @@ static errno_t be_refresh_get_values(TALLOC_CTX *mem_ctx,
+         return ENOMEM;
+     }
+ 
+-    ret = be_refresh_get_values_ex(mem_ctx, domain, period, class,
++    ret = be_refresh_get_values_ex(mem_ctx, domain, period,
+                                    base_dn, SYSDB_NAME, _values);
+ 
+     talloc_free(base_dn);
+-- 
+2.14.3
+
diff --git a/SOURCES/0055-ssh-handle-binary-keys-correctly.patch b/SOURCES/0055-ssh-handle-binary-keys-correctly.patch
deleted file mode 100644
index 05f87b1..0000000
--- a/SOURCES/0055-ssh-handle-binary-keys-correctly.patch
+++ /dev/null
@@ -1,48 +0,0 @@
-From 3ba9f82ac428f509df33e509a39eb783480f5d19 Mon Sep 17 00:00:00 2001
-From: Sumit Bose <sbose@redhat.com>
-Date: Thu, 16 Mar 2017 12:38:08 +0100
-Subject: [PATCH 55/60] ssh: handle binary keys correctly
-
-Related to https://pagure.io/SSSD/sssd/issue/3332
-
-Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
----
- src/responder/ssh/ssh_reply.c | 8 +++++++-
- 1 file changed, 7 insertions(+), 1 deletion(-)
-
-diff --git a/src/responder/ssh/ssh_reply.c b/src/responder/ssh/ssh_reply.c
-index 807f4ee079128b4a3f1719de890ffac6e0d5b2e0..7093e47253b5687bab387feed5299c2d0841d43c 100644
---- a/src/responder/ssh/ssh_reply.c
-+++ b/src/responder/ssh/ssh_reply.c
-@@ -32,6 +32,11 @@
- #include "responder/common/cache_req/cache_req.h"
- #include "responder/ssh/ssh_private.h"
- 
-+/* Locally used flag for libldb's ldb_message_element structure to indicate
-+ * binary data. Since the related data is only used in memory it is safe. If
-+ * should be used with care if libldb's I/O operations are involved. */
-+#define SSS_EL_FLAG_BIN_DATA (1<<4)
-+
- static errno_t get_valid_certs_keys(TALLOC_CTX *mem_ctx,
-                                     struct ssh_ctx *ssh_ctx,
-                                     struct ldb_message_element *el_cert,
-@@ -148,7 +153,7 @@ static errno_t decode_and_add_base64_data(struct sss_packet *packet,
-     }
- 
-     for (d = 0; d < el->num_values; d++) {
--        if (skip_base64_decode) {
-+        if (skip_base64_decode || (el->flags & SSS_EL_FLAG_BIN_DATA)) {
-             key = el->values[d].data;
-             key_len = el->values[d].length;
-         } else  {
-@@ -233,6 +238,7 @@ ssh_get_output_keys(TALLOC_CTX *mem_ctx,
-         }
- 
-         if (elements[i] != NULL) {
-+            elements[i]->flags |= SSS_EL_FLAG_BIN_DATA;
-             num_keys += elements[i]->num_values;
-             i++;
-         }
--- 
-2.9.3
-
diff --git a/SOURCES/0055-sysdb-do-not-use-objectClass-for-users-and-groups.patch b/SOURCES/0055-sysdb-do-not-use-objectClass-for-users-and-groups.patch
new file mode 100644
index 0000000..4d94916
--- /dev/null
+++ b/SOURCES/0055-sysdb-do-not-use-objectClass-for-users-and-groups.patch
@@ -0,0 +1,758 @@
+From e4ba8c5c6c08f86d51485d49c27635d7079efe13 Mon Sep 17 00:00:00 2001
+From: Sumit Bose <sbose@redhat.com>
+Date: Wed, 8 Nov 2017 15:14:58 +0100
+Subject: [PATCH 55/57] sysdb: do not use objectClass for users and groups
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+The majority of the object in the SSSD cache are users and groups. If
+there are many user and groups in the cache the index objects of the
+objectclass attributes 'user' and 'group' become  large because the
+must hold references to all objects of those object classes.
+
+As a result the management of these index objects becomes costly because
+they must be parsed and split apart quite often. Additionally they are
+mostly useless because user and groups are lookup up by more specific
+attributes in general.
+
+Only when enumerating all user or groups this kind of index might be
+useful.
+
+There are two way of removing this kind of index from the user and group
+objects. Either by removing objectClass from the list of indexes and add
+a new attribute to all other type of object we want and index for. Or by
+replacing objectClass with a different attribute for the user and group
+objects. After some testing I think the latter one is the more reliable
+one and implemented it in this patch.
+
+Related to https://pagure.io/SSSD/sssd/issue/3503
+
+Reviewed-by: Fabiano Fidêncio <fidencio@redhat.com>
+Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
+(cherry picked from commit 0e238c259c066cf997aaa940d33d6bda96c15925)
+---
+ src/db/sysdb.h                                     | 10 ++--
+ src/db/sysdb_init.c                                |  5 +-
+ src/db/sysdb_ops.c                                 |  6 +--
+ src/db/sysdb_search.c                              | 11 +++--
+ src/db/sysdb_upgrade.c                             |  4 ++
+ src/ldb_modules/memberof.c                         |  6 +--
+ src/providers/ad/ad_pac.c                          |  2 +-
+ src/providers/ipa/ipa_id.c                         |  9 ++--
+ src/providers/ipa/ipa_subdomains_ext_groups.c      |  2 +-
+ src/providers/ipa/ipa_subdomains_id.c              |  2 +-
+ src/providers/krb5/krb5_renew_tgt.c                |  3 +-
+ src/providers/ldap/ldap_id_cleanup.c               |  2 +-
+ src/providers/ldap/sdap_async_groups.c             |  8 ++--
+ src/providers/ldap/sdap_async_initgroups.c         |  2 +-
+ src/providers/ldap/sdap_async_initgroups_ad.c      |  2 +-
+ src/providers/ldap/sdap_async_nested_groups.c      |  2 +-
+ .../common/cache_req/plugins/cache_req_common.c    |  2 +-
+ src/responder/ifp/ifp_cache.c                      |  4 +-
+ src/responder/ifp/ifp_groups.c                     |  4 +-
+ src/responder/ifp/ifp_users.c                      |  2 +-
+ src/responder/nss/nss_cmd.c                        |  2 +-
+ src/responder/nss/nss_protocol_grent.c             |  2 +-
+ src/responder/nss/nss_protocol_sid.c               |  4 +-
+ src/tests/cmocka/test_ad_common.c                  |  2 +-
+ src/tests/cmocka/test_ipa_subdomains_server.c      | 54 ++++++++++++++++------
+ src/tests/sysdb-tests.c                            | 20 +++++---
+ src/tools/sssctl/sssctl_cache.c                    |  3 +-
+ 27 files changed, 110 insertions(+), 65 deletions(-)
+
+diff --git a/src/db/sysdb.h b/src/db/sysdb.h
+index 4192f9085d941814eccd2ac60ce8fb6d4e1bfa67..fd18ecefed2b2c5f35060fa47fd160a8968e073b 100644
+--- a/src/db/sysdb.h
++++ b/src/db/sysdb.h
+@@ -192,9 +192,10 @@
+ 
+ #define SYSDB_NEXTID_FILTER "("SYSDB_NEXTID"=*)"
+ 
+-#define SYSDB_UC "objectclass="SYSDB_USER_CLASS
+-#define SYSDB_GC "objectclass="SYSDB_GROUP_CLASS
+-#define SYSDB_NC "objectclass="SYSDB_NETGROUP_CLASS
++#define SYSDB_OBJECTCATEGORY "objectCategory"
++#define SYSDB_UC SYSDB_OBJECTCATEGORY"="SYSDB_USER_CLASS
++#define SYSDB_GC SYSDB_OBJECTCATEGORY"="SYSDB_GROUP_CLASS
++#define SYSDB_NC SYSDB_OBJECTCLASS"="SYSDB_NETGROUP_CLASS
+ #define SYSDB_MPGC "|("SYSDB_UC")("SYSDB_GC")"
+ 
+ #define SYSDB_PWNAM_FILTER "(&("SYSDB_UC")(|("SYSDB_NAME_ALIAS"=%s)("SYSDB_NAME_ALIAS"=%s)("SYSDB_NAME"=%s)))"
+@@ -227,7 +228,8 @@
+ #define SYSDB_DEFAULT_ATTRS SYSDB_LAST_UPDATE, \
+                             SYSDB_CACHE_EXPIRE, \
+                             SYSDB_INITGR_EXPIRE, \
+-                            SYSDB_OBJECTCLASS
++                            SYSDB_OBJECTCLASS, \
++                            SYSDB_OBJECTCATEGORY
+ 
+ #define SYSDB_PW_ATTRS {SYSDB_NAME, SYSDB_UIDNUM, \
+                         SYSDB_GIDNUM, SYSDB_GECOS, \
+diff --git a/src/db/sysdb_init.c b/src/db/sysdb_init.c
+index e246a165ec1d654dba19aa771ed97bfc3a07f245..44a7918f603fe1368b7d81738666de6bb47b83d0 100644
+--- a/src/db/sysdb_init.c
++++ b/src/db/sysdb_init.c
+@@ -31,11 +31,12 @@
+ #define LDB_MODULES_PATH "LDB_MODULES_PATH"
+ 
+ /* If an entry differs only in these attributes, they are written to
+- * the timestamp cache only. In addition, objectclass is added so that
+- * we can distinguish between users and groups.
++ * the timestamp cache only. In addition, objectclass/objectcategory is added
++ * so that we can distinguish between users and groups.
+  */
+ const char *sysdb_ts_cache_attrs[] = {
+     SYSDB_OBJECTCLASS,
++    SYSDB_OBJECTCATEGORY,
+     SYSDB_LAST_UPDATE,
+     SYSDB_CACHE_EXPIRE,
+     SYSDB_ORIG_MODSTAMP,
+diff --git a/src/db/sysdb_ops.c b/src/db/sysdb_ops.c
+index 1539c41c93e7d6ebd1e544abbb1707df5578cd72..024683317cab99743681db804f7026c8dbb33a38 100644
+--- a/src/db/sysdb_ops.c
++++ b/src/db/sysdb_ops.c
+@@ -958,7 +958,7 @@ static struct sysdb_attrs *ts_obj_attrs(TALLOC_CTX *mem_ctx,
+         return NULL;
+     }
+ 
+-    ret = sysdb_attrs_add_string(attrs, SYSDB_OBJECTCLASS, oc);
++    ret = sysdb_attrs_add_string(attrs, SYSDB_OBJECTCATEGORY, oc);
+     if (ret != EOK) {
+         talloc_free(attrs);
+         return NULL;
+@@ -1667,7 +1667,7 @@ int sysdb_add_basic_user(struct sss_domain_info *domain,
+         ERROR_OUT(ret, ENOMEM, done);
+     }
+ 
+-    ret = sysdb_add_string(msg, SYSDB_OBJECTCLASS, SYSDB_USER_CLASS);
++    ret = sysdb_add_string(msg, SYSDB_OBJECTCATEGORY, SYSDB_USER_CLASS);
+     if (ret) goto done;
+ 
+     ret = sysdb_add_string(msg, SYSDB_NAME, name);
+@@ -2120,7 +2120,7 @@ int sysdb_add_basic_group(struct sss_domain_info *domain,
+         ERROR_OUT(ret, ENOMEM, done);
+     }
+ 
+-    ret = sysdb_add_string(msg, SYSDB_OBJECTCLASS, SYSDB_GROUP_CLASS);
++    ret = sysdb_add_string(msg, SYSDB_OBJECTCATEGORY, SYSDB_GROUP_CLASS);
+     if (ret) goto done;
+ 
+     ret = sysdb_add_string(msg, SYSDB_NAME, name);
+diff --git a/src/db/sysdb_search.c b/src/db/sysdb_search.c
+index 8083966900429b268a3b984f1cad3d47d1099198..a6a81e23d257331614085403b4dca8ded860600b 100644
+--- a/src/db/sysdb_search.c
++++ b/src/db/sysdb_search.c
+@@ -114,10 +114,11 @@ static errno_t merge_msg_ts_attrs(struct sysdb_ctx *sysdb,
+         return EIO;
+     }
+ 
+-    /* Deliberately start from 1 in order to not merge objectclass and avoid
+-     * breaking MPGs where the OC might be made up
++    /* Deliberately start from 2 in order to not merge
++     * objectclass/objectcategory and avoid breaking MPGs where the OC might
++     * be made up
+      */
+-    for (size_t c = 1; sysdb_ts_cache_attrs[c]; c++) {
++    for (size_t c = 2; sysdb_ts_cache_attrs[c]; c++) {
+         ret = merge_ts_attr(ts_msgs[0], sysdb_msg,
+                             sysdb_ts_cache_attrs[c], attrs);
+         if (ret != EOK) {
+@@ -751,7 +752,7 @@ static int mpg_convert(struct ldb_message *msg)
+     struct ldb_val *val = NULL;
+     int i;
+ 
+-    el = ldb_msg_find_element(msg, "objectClass");
++    el = ldb_msg_find_element(msg, SYSDB_OBJECTCATEGORY);
+     if (!el) return EINVAL;
+ 
+     /* see if this is a user to convert to a group */
+@@ -2088,7 +2089,7 @@ errno_t sysdb_get_direct_parents(TALLOC_CTX *mem_ctx,
+     }
+ 
+     member_filter = talloc_asprintf(tmp_ctx, "(&(%s=%s)(%s=%s))",
+-                                    SYSDB_OBJECTCLASS, SYSDB_GROUP_CLASS,
++                                    SYSDB_OBJECTCATEGORY, SYSDB_GROUP_CLASS,
+                                     SYSDB_MEMBER, sanitized_dn);
+     if (!member_filter) {
+         ret = ENOMEM;
+diff --git a/src/db/sysdb_upgrade.c b/src/db/sysdb_upgrade.c
+index 040c91ca6276e7c51a126eefe034aa5fe9d0433f..365d45f7ebd78523ca9ec4b9c2158cc09acb5489 100644
+--- a/src/db/sysdb_upgrade.c
++++ b/src/db/sysdb_upgrade.c
+@@ -149,6 +149,7 @@ int sysdb_upgrade_01(struct ldb_context *ldb, const char **ver)
+     struct ldb_dn *mem_dn;
+     struct ldb_message *msg;
+     const struct ldb_val *val;
++    /* No change needed because this version has objectclass group */
+     const char *filter = "(&(memberUid=*)(objectclass=group))";
+     const char *attrs[] = { "memberUid", NULL };
+     const char *mdn;
+@@ -1041,6 +1042,7 @@ int sysdb_upgrade_10(struct sysdb_ctx *sysdb, struct sss_domain_info *domain,
+     struct ldb_message_element *memberof_el;
+     const char *name;
+     struct ldb_dn *basedn;
++    /* No change needed because version 10 has objectclass user */
+     const char *filter = "(&(objectClass=user)(!(uidNumber=*))(memberOf=*))";
+     const char *attrs[] = { "name", "memberof", NULL };
+     struct upgrade_ctx *ctx;
+@@ -2082,6 +2084,7 @@ static void qualify_users(struct upgrade_ctx *ctx,
+                           struct sss_names_ctx *names,
+                           struct ldb_dn *base_dn)
+ {
++    /* No change needed because this version has objectclass user */
+     const char *user_filter = "objectclass=user";
+     const char *user_name_attrs[] = { SYSDB_NAME,
+                                       SYSDB_NAME_ALIAS,
+@@ -2107,6 +2110,7 @@ static void qualify_groups(struct upgrade_ctx *ctx,
+                            struct sss_names_ctx *names,
+                            struct ldb_dn *base_dn)
+ {
++    /* No change needed because this version has objectclass group */
+     const char *group_filter = "objectclass=group";
+     const char *group_name_attrs[] = { SYSDB_NAME,
+                                        SYSDB_NAME_ALIAS,
+diff --git a/src/ldb_modules/memberof.c b/src/ldb_modules/memberof.c
+index af7147ee7cc9299d4040d63a637373842dcee02a..327a38c5f75afcde1b997796afd1217d45acbde2 100644
+--- a/src/ldb_modules/memberof.c
++++ b/src/ldb_modules/memberof.c
+@@ -31,7 +31,7 @@
+ #define DB_USER_CLASS "user"
+ #define DB_GROUP_CLASS "group"
+ #define DB_CACHE_EXPIRE "dataExpireTimestamp"
+-#define DB_OC "objectClass"
++#define DB_OC "objectCategory"
+ 
+ #ifndef MAX
+ #define MAX(a,b) (((a) > (b)) ? (a) : (b))
+@@ -3928,7 +3928,7 @@ static int memberof_recompute_task(struct ldb_module *module,
+ {
+     struct ldb_context *ldb = ldb_module_get_ctx(module);
+     static const char *attrs[] = { DB_NAME, DB_MEMBEROF, NULL };
+-    static const char *filter = "(objectclass=user)";
++    static const char *filter = "("DB_OC"="DB_USER_CLASS")";
+     struct mbof_rcmp_context *ctx;
+     struct ldb_request *src_req;
+     int ret;
+@@ -4035,7 +4035,7 @@ static int mbof_rcmp_search_groups(struct mbof_rcmp_context *ctx)
+     struct ldb_context *ldb = ldb_module_get_ctx(ctx->module);
+     static const char *attrs[] = { DB_MEMBEROF, DB_MEMBERUID,
+                                    DB_NAME, DB_MEMBER, NULL };
+-    static const char *filter = "(objectclass=group)";
++    static const char *filter = "("DB_OC"="DB_GROUP_CLASS")";
+     struct ldb_request *req;
+     int ret;
+ 
+diff --git a/src/providers/ad/ad_pac.c b/src/providers/ad/ad_pac.c
+index ed002e1f9bf8f15d5b5d4b1c55392a34d18575e4..6b47462cf79a81b9258e3508914c043432edfed3 100644
+--- a/src/providers/ad/ad_pac.c
++++ b/src/providers/ad/ad_pac.c
+@@ -31,7 +31,7 @@ static errno_t find_user_entry(TALLOC_CTX *mem_ctx, struct sss_domain_info *dom,
+                                struct dp_id_data *ar,
+                                struct ldb_message **_msg)
+ {
+-    const char *user_attrs[] = { SYSDB_NAME, SYSDB_OBJECTCLASS,
++    const char *user_attrs[] = { SYSDB_NAME, SYSDB_OBJECTCATEGORY,
+                                  SYSDB_PAC_BLOB, SYSDB_PAC_BLOB_EXPIRE,
+                                  NULL };
+     struct ldb_message *msg;
+diff --git a/src/providers/ipa/ipa_id.c b/src/providers/ipa/ipa_id.c
+index 5044577f0faa95b19de9233240e92aa60f029774..8f8759f64b758aae7e45c88588e97a1bcf16ad79 100644
+--- a/src/providers/ipa/ipa_id.c
++++ b/src/providers/ipa/ipa_id.c
+@@ -431,7 +431,8 @@ static errno_t ipa_id_get_group_uuids(TALLOC_CTX *mem_ctx,
+     }
+ 
+     filter = talloc_asprintf(tmp_ctx,
+-                             "(&(objectclass=%s)(!(%s=*))(%s=*))",
++                             "(&(%s=%s)(!(%s=*))(%s=*))",
++                             SYSDB_OBJECTCATEGORY,
+                              SYSDB_GROUP_CLASS, SYSDB_OVERRIDE_DN,
+                              SYSDB_UUID);
+     if (filter == NULL) {
+@@ -733,7 +734,7 @@ static void ipa_id_get_account_info_orig_done(struct tevent_req *subreq)
+     const char *attrs[] = { SYSDB_NAME,
+                             SYSDB_UIDNUM,
+                             SYSDB_SID_STR,
+-                            SYSDB_OBJECTCLASS,
++                            SYSDB_OBJECTCATEGORY,
+                             SYSDB_UUID,
+                             SYSDB_GHOST,
+                             SYSDB_HOMEDIR,
+@@ -819,7 +820,7 @@ static int ipa_id_get_account_info_post_proc_step(struct tevent_req *req)
+     struct ipa_id_get_account_info_state *state = tevent_req_data(req,
+                                           struct ipa_id_get_account_info_state);
+ 
+-    class = ldb_msg_find_attr_as_string(state->obj_msg, SYSDB_OBJECTCLASS,
++    class = ldb_msg_find_attr_as_string(state->obj_msg, SYSDB_OBJECTCATEGORY,
+                                         NULL);
+     if (class == NULL) {
+         DEBUG(SSSDBG_CRIT_FAILURE, "Cannot find an objectclass.\n");
+@@ -957,7 +958,7 @@ static void ipa_id_get_account_info_done(struct tevent_req *subreq)
+         goto fail;
+     }
+ 
+-    class = ldb_msg_find_attr_as_string(state->obj_msg, SYSDB_OBJECTCLASS,
++    class = ldb_msg_find_attr_as_string(state->obj_msg, SYSDB_OBJECTCATEGORY,
+                                         NULL);
+     if (class == NULL) {
+         DEBUG(SSSDBG_CRIT_FAILURE, "Cannot find an objectclass.\n");
+diff --git a/src/providers/ipa/ipa_subdomains_ext_groups.c b/src/providers/ipa/ipa_subdomains_ext_groups.c
+index 0359e0dedeef8db0da71d16a6f0044e43a7a9840..9e1d6c3a9bdeda56b421a2dc9198dff0b84c54ce 100644
+--- a/src/providers/ipa/ipa_subdomains_ext_groups.c
++++ b/src/providers/ipa/ipa_subdomains_ext_groups.c
+@@ -940,7 +940,7 @@ search_user_or_group_by_sid_str(TALLOC_CTX *mem_ctx,
+     const char *attrs[] = { SYSDB_NAME,
+                             SYSDB_SID_STR,
+                             SYSDB_ORIG_DN,
+-                            SYSDB_OBJECTCLASS,
++                            SYSDB_OBJECTCATEGORY,
+                             SYSDB_CACHE_EXPIRE,
+                             NULL };
+     TALLOC_CTX *tmp_ctx = NULL;
+diff --git a/src/providers/ipa/ipa_subdomains_id.c b/src/providers/ipa/ipa_subdomains_id.c
+index 3530af94ef59397db72465fcb0c4a03117a4d8bd..2ba9813a44b4d914d9c2ef7a1a7504546f52954c 100644
+--- a/src/providers/ipa/ipa_subdomains_id.c
++++ b/src/providers/ipa/ipa_subdomains_id.c
+@@ -888,7 +888,7 @@ apply_subdomain_homedir(TALLOC_CTX *mem_ctx, struct sss_domain_info *dom,
+     struct ldb_message_element *msg_el = NULL;
+     size_t c;
+ 
+-    msg_el = ldb_msg_find_element(msg, SYSDB_OBJECTCLASS);
++    msg_el = ldb_msg_find_element(msg, SYSDB_OBJECTCATEGORY);
+     if (msg_el == NULL) {
+         DEBUG(SSSDBG_OP_FAILURE, "ldb_msg_find_element failed.\n");
+         ret = ENOENT;
+diff --git a/src/providers/krb5/krb5_renew_tgt.c b/src/providers/krb5/krb5_renew_tgt.c
+index ea6b39deb8dacdfa9211058a54a57b6e9f6b7d9d..549c08c6f105276fa9913568c228d3ff627623ae 100644
+--- a/src/providers/krb5/krb5_renew_tgt.c
++++ b/src/providers/krb5/krb5_renew_tgt.c
+@@ -385,8 +385,7 @@ static errno_t check_ccache_files(struct renew_tgt_ctx *renew_tgt_ctx)
+ {
+     TALLOC_CTX *tmp_ctx;
+     int ret;
+-    const char *ccache_filter = "(&("SYSDB_CCACHE_FILE"=*)" \
+-                                  "("SYSDB_OBJECTCLASS"="SYSDB_USER_CLASS"))";
++    const char *ccache_filter = "(&("SYSDB_CCACHE_FILE"=*)("SYSDB_UC"))";
+     const char *ccache_attrs[] = { SYSDB_CCACHE_FILE, SYSDB_UPN, SYSDB_NAME,
+                                    SYSDB_CANONICAL_UPN, NULL };
+     size_t msgs_count = 0;
+diff --git a/src/providers/ldap/ldap_id_cleanup.c b/src/providers/ldap/ldap_id_cleanup.c
+index c85ce45918cf938a95ff85c31bfe0541f9ddd052..8c0f0c18ba587e9bbfec144abe9c172cd5e0465b 100644
+--- a/src/providers/ldap/ldap_id_cleanup.c
++++ b/src/providers/ldap/ldap_id_cleanup.c
+@@ -438,7 +438,7 @@ static int cleanup_groups(TALLOC_CTX *memctx,
+              */
+             gid = (gid_t) ldb_msg_find_attr_as_uint(msgs[i], SYSDB_GIDNUM, 0);
+             subfilter = talloc_asprintf(tmpctx, "(&(%s=%s)(|(%s=%s)(%s=%lu)))",
+-                                        SYSDB_OBJECTCLASS, SYSDB_USER_CLASS,
++                                        SYSDB_OBJECTCATEGORY, SYSDB_USER_CLASS,
+                                         SYSDB_MEMBEROF, sanitized_dn,
+                                         SYSDB_GIDNUM, (long unsigned) gid);
+         } else {
+diff --git a/src/providers/ldap/sdap_async_groups.c b/src/providers/ldap/sdap_async_groups.c
+index 536e3f13744c5350eed518c9bd35fd89e0899dc6..b1cfb7e4a4c054e5d365da5fca65da27c9ef5461 100644
+--- a/src/providers/ldap/sdap_async_groups.c
++++ b/src/providers/ldap/sdap_async_groups.c
+@@ -39,7 +39,7 @@ static int sdap_find_entry_by_origDN(TALLOC_CTX *memctx,
+                                      bool *_is_group)
+ {
+     TALLOC_CTX *tmpctx;
+-    const char *attrs[] = {SYSDB_OBJECTCLASS,  NULL};
++    const char *attrs[] = {SYSDB_OBJECTCLASS, SYSDB_OBJECTCATEGORY, NULL};
+     struct ldb_dn *base_dn;
+     char *filter;
+     struct ldb_message **msgs;
+@@ -90,11 +90,11 @@ static int sdap_find_entry_by_origDN(TALLOC_CTX *memctx,
+     }
+ 
+     if (_is_group != NULL) {
+-        objectclass = ldb_msg_find_attr_as_string(msgs[0], SYSDB_OBJECTCLASS,
++        objectclass = ldb_msg_find_attr_as_string(msgs[0], SYSDB_OBJECTCATEGORY,
+                                                   NULL);
+         if (objectclass == NULL) {
+-            DEBUG(SSSDBG_OP_FAILURE, "An antry without a %s?\n",
+-                  SYSDB_OBJECTCLASS);
++            DEBUG(SSSDBG_OP_FAILURE, "An entry without a %s?\n",
++                  SYSDB_OBJECTCATEGORY);
+             ret = EINVAL;
+             goto done;
+         }
+diff --git a/src/providers/ldap/sdap_async_initgroups.c b/src/providers/ldap/sdap_async_initgroups.c
+index a33975cde4bc359cfe8395c0de04fd1774b8763d..f8a84474749e08349b539c774d68c876167cfdf1 100644
+--- a/src/providers/ldap/sdap_async_initgroups.c
++++ b/src/providers/ldap/sdap_async_initgroups.c
+@@ -2341,7 +2341,7 @@ static errno_t rfc2307bis_nested_groups_step(struct tevent_req *req)
+     }
+ 
+     ret = sysdb_attrs_get_string(state->groups[state->group_iter],
+-                                 SYSDB_OBJECTCLASS, &class);
++                                 SYSDB_OBJECTCATEGORY, &class);
+     if (ret == EOK) {
+         /* If there is a objectClass attribute the object is coming from the
+          * cache and the name attribute of the object already has the primary
+diff --git a/src/providers/ldap/sdap_async_initgroups_ad.c b/src/providers/ldap/sdap_async_initgroups_ad.c
+index 2831be9776293260aeec0e2ff85160f1938bdb32..61aa69a2dfbe22cac37a5b7fddc07473527e5de5 100644
+--- a/src/providers/ldap/sdap_async_initgroups_ad.c
++++ b/src/providers/ldap/sdap_async_initgroups_ad.c
+@@ -1606,7 +1606,7 @@ sdap_ad_get_domain_local_groups_parse_parents(TALLOC_CTX *mem_ctx,
+         goto done;
+     }
+ 
+-    ret = sysdb_attrs_get_string(gr->group, SYSDB_OBJECTCLASS, &class);
++    ret = sysdb_attrs_get_string(gr->group, SYSDB_OBJECTCATEGORY, &class);
+     if (ret != EOK) {
+         /* If objectclass is missing gr->group is a nested parent found during
+          * the nested group lookup. It might not already stored in the cache.
+diff --git a/src/providers/ldap/sdap_async_nested_groups.c b/src/providers/ldap/sdap_async_nested_groups.c
+index 9271d8cfe38d11fb1ea14960a997f0deee175b27..b1f9753d7cdf5f6e278c54394d4f306cc21a42ab 100644
+--- a/src/providers/ldap/sdap_async_nested_groups.c
++++ b/src/providers/ldap/sdap_async_nested_groups.c
+@@ -1686,7 +1686,7 @@ static errno_t sdap_nested_group_get_ipa_user(TALLOC_CTX *mem_ctx,
+         goto done;
+     }
+ 
+-    ret = sysdb_attrs_add_string(user, SYSDB_OBJECTCLASS, SYSDB_USER_CLASS);
++    ret = sysdb_attrs_add_string(user, SYSDB_OBJECTCATEGORY, SYSDB_USER_CLASS);
+     if (ret != EOK) {
+         goto done;
+     }
+diff --git a/src/responder/common/cache_req/plugins/cache_req_common.c b/src/responder/common/cache_req/plugins/cache_req_common.c
+index b80f310feeebbdbc824db441ff5313632585d3fb..1f86258bc14c7a382712959f24a4ec4c153572d4 100644
+--- a/src/responder/common/cache_req/plugins/cache_req_common.c
++++ b/src/responder/common/cache_req/plugins/cache_req_common.c
+@@ -53,7 +53,7 @@ cache_req_well_known_sid_msg(TALLOC_CTX *mem_ctx,
+         goto done;
+     }
+ 
+-    ldberr = ldb_msg_add_string(msg, SYSDB_OBJECTCLASS, SYSDB_GROUP_CLASS);
++    ldberr = ldb_msg_add_string(msg, SYSDB_OBJECTCATEGORY, SYSDB_GROUP_CLASS);
+     if (ldberr != LDB_SUCCESS) {
+         goto done;
+     }
+diff --git a/src/responder/ifp/ifp_cache.c b/src/responder/ifp/ifp_cache.c
+index 8ea2d8008d40bc0a28f3871b511690af677c5c5e..f84cb14de48b5c86acb027f275edded4eb73e192 100644
+--- a/src/responder/ifp/ifp_cache.c
++++ b/src/responder/ifp/ifp_cache.c
+@@ -100,7 +100,7 @@ ifp_cache_get_cached_objects(TALLOC_CTX *mem_ctx,
+     errno_t ret;
+     int ldb_ret;
+     int i;
+-    const char *attrs[] = {SYSDB_OBJECTCLASS, SYSDB_UIDNUM,
++    const char *attrs[] = {SYSDB_OBJECTCATEGORY, SYSDB_UIDNUM,
+                            SYSDB_GIDNUM, NULL};
+ 
+     tmp_ctx = talloc_new(NULL);
+@@ -117,7 +117,7 @@ ifp_cache_get_cached_objects(TALLOC_CTX *mem_ctx,
+ 
+     ldb_ret = ldb_search(sysdb_ctx_get_ldb(domain->sysdb), tmp_ctx, &result,
+                          base_dn, LDB_SCOPE_SUBTREE, attrs,
+-                         "(&(objectClass=%s)(%s=TRUE))", class,
++                         "(&(%s=%s)(%s=TRUE))", SYSDB_OBJECTCATEGORY, class,
+                          SYSDB_IFP_CACHED);
+     if (ldb_ret != LDB_SUCCESS) {
+         DEBUG(SSSDBG_CRIT_FAILURE, "Unable to search the cache\n");
+diff --git a/src/responder/ifp/ifp_groups.c b/src/responder/ifp/ifp_groups.c
+index 7503254238eafdafbe2d90fbf7416587be49e1b7..b274b8f52d7908165acc10b91a7d6afe638f1a82 100644
+--- a/src/responder/ifp/ifp_groups.c
++++ b/src/responder/ifp/ifp_groups.c
+@@ -841,7 +841,7 @@ ifp_groups_group_get_members(TALLOC_CTX *mem_ctx,
+     int num_groups;
+     int i;
+     errno_t ret;
+-    const char *attrs[] = {SYSDB_OBJECTCLASS, SYSDB_UIDNUM,
++    const char *attrs[] = {SYSDB_OBJECTCATEGORY, SYSDB_UIDNUM,
+                            SYSDB_GIDNUM, NULL};
+ 
+     tmp_ctx = talloc_new(NULL);
+@@ -888,7 +888,7 @@ ifp_groups_group_get_members(TALLOC_CTX *mem_ctx,
+     num_users = 0;
+     num_groups = 0;
+     for (i = 0; i < num_members; i++) {
+-        class = ldb_msg_find_attr_as_string(members[i], SYSDB_OBJECTCLASS,
++        class = ldb_msg_find_attr_as_string(members[i], SYSDB_OBJECTCATEGORY,
+                                             NULL);
+         if (class == NULL) {
+             ret = ERR_INTERNAL;
+diff --git a/src/responder/ifp/ifp_users.c b/src/responder/ifp/ifp_users.c
+index 86a1f43a2c6e7d785c9d34e350c71f242ff7182f..cb342a245ef6545168a7a60c252505f50576fdf7 100644
+--- a/src/responder/ifp/ifp_users.c
++++ b/src/responder/ifp/ifp_users.c
+@@ -1441,7 +1441,7 @@ void ifp_users_user_get_extra_attributes(struct sbus_request *sbus_req,
+     }
+ 
+     filter = talloc_asprintf(sbus_req, "(&(%s=%s)(%s=%s))",
+-                             SYSDB_OBJECTCLASS, SYSDB_USER_CLASS,
++                             SYSDB_OBJECTCATEGORY, SYSDB_USER_CLASS,
+                              SYSDB_NAME, name);
+     if (filter == NULL) {
+         DEBUG(SSSDBG_CRIT_FAILURE, "talloc_asprintf() failed\n");
+diff --git a/src/responder/nss/nss_cmd.c b/src/responder/nss/nss_cmd.c
+index 545257a0be7e91e9de767a57848bb77c5791db4e..956ee53cb88dd24faaa95ac39c8d9540af66cfb2 100644
+--- a/src/responder/nss/nss_cmd.c
++++ b/src/responder/nss/nss_cmd.c
+@@ -1148,7 +1148,7 @@ static errno_t nss_cmd_getorigbyname(struct cli_ctx *cli_ctx)
+     errno_t ret;
+     struct nss_ctx *nss_ctx;
+     const char **attrs;
+-    static const char *defattrs[] = { SYSDB_NAME, SYSDB_OBJECTCLASS,
++    static const char *defattrs[] = { SYSDB_NAME, SYSDB_OBJECTCATEGORY,
+                                       SYSDB_SID_STR,
+                                       ORIGINALAD_PREFIX SYSDB_NAME,
+                                       ORIGINALAD_PREFIX SYSDB_UIDNUM,
+diff --git a/src/responder/nss/nss_protocol_grent.c b/src/responder/nss/nss_protocol_grent.c
+index 6f6ae57dd97b000ad3cf174b0f649d46981563e2..3550c3f0d375b305d4dbdf3ea19613696448da35 100644
+--- a/src/responder/nss/nss_protocol_grent.c
++++ b/src/responder/nss/nss_protocol_grent.c
+@@ -33,7 +33,7 @@ nss_get_grent(TALLOC_CTX *mem_ctx,
+     errno_t ret;
+ 
+     /* Check object class. */
+-    if (!ldb_msg_check_string_attribute(msg, "objectClass",
++    if (!ldb_msg_check_string_attribute(msg, SYSDB_OBJECTCATEGORY,
+                                         SYSDB_GROUP_CLASS)) {
+         DEBUG(SSSDBG_MINOR_FAILURE, "Wrong object (%s) found on stack!\n",
+               ldb_dn_get_linearized(msg->dn));
+diff --git a/src/responder/nss/nss_protocol_sid.c b/src/responder/nss/nss_protocol_sid.c
+index 61357c2bf92e2f15d978b64a15ad5bd5aa354445..3f60967d750eea3135257ccb597efaa5aa1e2de3 100644
+--- a/src/responder/nss/nss_protocol_sid.c
++++ b/src/responder/nss/nss_protocol_sid.c
+@@ -30,9 +30,9 @@ find_sss_id_type(struct ldb_message *msg,
+     struct ldb_message_element *el;
+     struct ldb_val *val = NULL;
+ 
+-    el = ldb_msg_find_element(msg, SYSDB_OBJECTCLASS);
++    el = ldb_msg_find_element(msg, SYSDB_OBJECTCATEGORY);
+     if (el == NULL) {
+-        DEBUG(SSSDBG_OP_FAILURE, "Objectclass attribute not found.\n");
++        DEBUG(SSSDBG_OP_FAILURE, "Objectcategory attribute not found.\n");
+         return EINVAL;
+     }
+ 
+diff --git a/src/tests/cmocka/test_ad_common.c b/src/tests/cmocka/test_ad_common.c
+index 3187af1b004cf3d1ffc1746950faa842f3a05fbc..80b3bb5599a95578b7734d5dfcd20a2a7428a084 100644
+--- a/src/tests/cmocka/test_ad_common.c
++++ b/src/tests/cmocka/test_ad_common.c
+@@ -336,7 +336,7 @@ static void test_ad_get_pac_data_from_user_entry(void **state)
+ 
+     ret = ldb_msg_add_string(user_msg, SYSDB_NAME, "username");
+     assert_int_equal(ret, EOK);
+-    ret = ldb_msg_add_string(user_msg, SYSDB_OBJECTCLASS, "user");
++    ret = ldb_msg_add_string(user_msg, SYSDB_OBJECTCATEGORY, SYSDB_USER_CLASS);
+     assert_int_equal(ret, EOK);
+     ret = ldb_msg_add_string(user_msg, SYSDB_PAC_BLOB_EXPIRE, "12345");
+     assert_int_equal(ret, EOK);
+diff --git a/src/tests/cmocka/test_ipa_subdomains_server.c b/src/tests/cmocka/test_ipa_subdomains_server.c
+index eccfc2fe1e2a224b2cec8ea3184796a23d32febe..1e492e86c1caf26d8890bfa37ebb21321afca366 100644
+--- a/src/tests/cmocka/test_ipa_subdomains_server.c
++++ b/src/tests/cmocka/test_ipa_subdomains_server.c
+@@ -455,6 +455,8 @@ static void test_ipa_server_create_trusts_twoway(struct tevent_req *req)
+         tevent_req_callback_data(req, struct trust_test_ctx);
+     errno_t ret;
+     struct sss_domain_info *child_dom;
++    struct ipa_ad_server_ctx *s_trust;
++    struct ipa_ad_server_ctx *c_trust;
+ 
+     ret = ipa_server_create_trusts_recv(req);
+     talloc_zfree(req);
+@@ -462,9 +464,18 @@ static void test_ipa_server_create_trusts_twoway(struct tevent_req *req)
+ 
+     /* Trust object should be around now */
+     assert_non_null(test_ctx->ipa_ctx->server_mode->trusts);
++    assert_non_null(test_ctx->ipa_ctx->server_mode->trusts->next);
+ 
++    if (strcmp(test_ctx->ipa_ctx->server_mode->trusts->dom->name,
++               SUBDOM_NAME) == 0) {
++        s_trust = test_ctx->ipa_ctx->server_mode->trusts;
++        c_trust = test_ctx->ipa_ctx->server_mode->trusts->next;
++    } else {
++        s_trust = test_ctx->ipa_ctx->server_mode->trusts->next;
++        c_trust = test_ctx->ipa_ctx->server_mode->trusts;
++    }
+     /* Two-way trusts should use the system realm */
+-    assert_trust_object(test_ctx->ipa_ctx->server_mode->trusts,
++    assert_trust_object(c_trust,
+                         CHILD_NAME,
+                         DOM_REALM,
+                         CHILD_SID,
+@@ -472,9 +483,8 @@ static void test_ipa_server_create_trusts_twoway(struct tevent_req *req)
+                         TEST_AUTHID,
+                         DOM_REALM);
+ 
+-    assert_non_null(test_ctx->ipa_ctx->server_mode->trusts->next);
+ 
+-    assert_trust_object(test_ctx->ipa_ctx->server_mode->trusts->next,
++    assert_trust_object(s_trust,
+                         SUBDOM_NAME,
+                         DOM_REALM,
+                         SUBDOM_SID,
+@@ -523,6 +533,8 @@ static void test_ipa_server_trust_init(void **state)
+     errno_t ret;
+     struct tevent_timer *timeout_handler;
+     struct timeval tv;
++    struct ipa_ad_server_ctx *s_trust;
++    struct ipa_ad_server_ctx *c_trust;
+ 
+     add_test_2way_subdomains(test_ctx);
+ 
+@@ -537,13 +549,21 @@ static void test_ipa_server_trust_init(void **state)
+     ret = test_ev_loop(test_ctx->tctx);
+     assert_int_equal(ret, ERR_OK);
+ 
+-    assert_non_null(test_ctx->ipa_ctx->server_mode->trusts);
+-
+     /* Trust object should be around now */
+     assert_non_null(test_ctx->ipa_ctx->server_mode->trusts);
++    assert_non_null(test_ctx->ipa_ctx->server_mode->trusts->next);
++
++    if (strcmp(test_ctx->ipa_ctx->server_mode->trusts->dom->name,
++               SUBDOM_NAME) == 0) {
++        s_trust = test_ctx->ipa_ctx->server_mode->trusts;
++        c_trust = test_ctx->ipa_ctx->server_mode->trusts->next;
++    } else {
++        s_trust = test_ctx->ipa_ctx->server_mode->trusts->next;
++        c_trust = test_ctx->ipa_ctx->server_mode->trusts;
++    }
+ 
+     /* Two-way trusts should use the system realm */
+-    assert_trust_object(test_ctx->ipa_ctx->server_mode->trusts,
++    assert_trust_object(c_trust,
+                         CHILD_NAME,
+                         DOM_REALM,
+                         CHILD_SID,
+@@ -551,9 +571,7 @@ static void test_ipa_server_trust_init(void **state)
+                         TEST_AUTHID,
+                         DOM_REALM);
+ 
+-    assert_non_null(test_ctx->ipa_ctx->server_mode->trusts->next);
+-
+-    assert_trust_object(test_ctx->ipa_ctx->server_mode->trusts->next,
++    assert_trust_object(s_trust,
+                         SUBDOM_NAME,
+                         DOM_REALM,
+                         SUBDOM_SID,
+@@ -708,6 +726,8 @@ static void test_ipa_server_create_trusts_oneway(struct tevent_req *req)
+     struct trust_test_ctx *test_ctx = \
+         tevent_req_callback_data(req, struct trust_test_ctx);
+     errno_t ret;
++    struct ipa_ad_server_ctx *s_trust;
++    struct ipa_ad_server_ctx *c_trust;
+ 
+     ret = ipa_server_create_trusts_recv(req);
+     talloc_zfree(req);
+@@ -720,9 +740,19 @@ static void test_ipa_server_create_trusts_oneway(struct tevent_req *req)
+ 
+     /* Trust object should be around now */
+     assert_non_null(test_ctx->ipa_ctx->server_mode->trusts);
++    assert_non_null(test_ctx->ipa_ctx->server_mode->trusts->next);
++
++    if (strcmp(test_ctx->ipa_ctx->server_mode->trusts->dom->name,
++               SUBDOM_NAME) == 0) {
++        s_trust = test_ctx->ipa_ctx->server_mode->trusts;
++        c_trust = test_ctx->ipa_ctx->server_mode->trusts->next;
++    } else {
++        s_trust = test_ctx->ipa_ctx->server_mode->trusts->next;
++        c_trust = test_ctx->ipa_ctx->server_mode->trusts;
++    }
+ 
+     assert_trust_object(
+-        test_ctx->ipa_ctx->server_mode->trusts,
++        c_trust,
+         CHILD_NAME,    /* AD domain name */
+         CHILD_REALM,   /* AD realm can be child if SDAP realm is parent's */
+         CHILD_SID,
+@@ -730,10 +760,8 @@ static void test_ipa_server_create_trusts_oneway(struct tevent_req *req)
+         ONEWAY_PRINC,     /* Principal shared with parent AD dom */
+         SUBDOM_REALM); /* SDAP realm must be AD root domain */
+ 
+-    assert_non_null(test_ctx->ipa_ctx->server_mode->trusts->next);
+-
+     /* Here all properties point to the AD domain */
+-    assert_trust_object(test_ctx->ipa_ctx->server_mode->trusts->next,
++    assert_trust_object(s_trust,
+                         SUBDOM_NAME,
+                         SUBDOM_REALM,
+                         SUBDOM_SID,
+diff --git a/src/tests/sysdb-tests.c b/src/tests/sysdb-tests.c
+index 4652661087238c18f7fabb398d054db99f77d6cf..fc9936968bcde8370c7054ba303de4463b35e15a 100644
+--- a/src/tests/sysdb-tests.c
++++ b/src/tests/sysdb-tests.c
+@@ -503,7 +503,7 @@ static int test_search_all_users(struct test_data *data)
+     }
+ 
+     ret = sysdb_search_entry(data, data->ctx->sysdb, base_dn,
+-                             LDB_SCOPE_SUBTREE, "objectClass=user",
++                             LDB_SCOPE_SUBTREE, SYSDB_UC,
+                              data->attrlist, &data->msgs_count, &data->msgs);
+     return ret;
+ }
+@@ -2219,6 +2219,7 @@ START_TEST (test_sysdb_search_all_users)
+     struct test_data *data;
+     int ret;
+     int i;
++    int j;
+     char *uid_str;
+ 
+     /* Setup */
+@@ -2253,8 +2254,15 @@ START_TEST (test_sysdb_search_all_users)
+                     "wrong number of values, found [%d] expected [1]",
+                     data->msgs[i]->elements[0].num_values);
+ 
+-        uid_str = talloc_asprintf(data, "%d", 27010 + i);
+-        fail_unless(uid_str != NULL, "talloc_asprintf failed.");
++        for (j = 0; j < data->msgs_count; j++) {
++            uid_str = talloc_asprintf(data, "%d", 27010 + j);
++            fail_unless(uid_str != NULL, "talloc_asprintf failed.");
++            if (strncmp(uid_str,
++                        (char *) data->msgs[i]->elements[0].values[0].data,
++                        data->msgs[i]->elements[0].values[0].length)  == 0) {
++                break;
++            }
++        }
+         fail_unless(strncmp(uid_str,
+                             (char *) data->msgs[i]->elements[0].values[0].data,
+                             data->msgs[i]->elements[0].values[0].length)  == 0,
+@@ -4411,7 +4419,7 @@ START_TEST(test_SSS_LDB_SEARCH)
+ 
+     /* Non-empty filter */
+     SSS_LDB_SEARCH(ret, test_ctx->sysdb->ldb, test_ctx, &res, group_dn,
+-                   LDB_SCOPE_BASE, NULL, "objectClass=group");
++                   LDB_SCOPE_BASE, NULL, SYSDB_GC);
+ 
+     fail_unless(ret == EOK, "SSS_LDB_SEARCH error [%d][%s]",
+                 ret, strerror(ret));
+@@ -5203,7 +5211,7 @@ START_TEST (test_sysdb_search_return_ENOENT)
+ 
+     ret = sysdb_search_entry(test_ctx, test_ctx->sysdb,
+                              user_dn, LDB_SCOPE_SUBTREE,
+-                             "objectClass=user", NULL,
++                             SYSDB_UC, NULL,
+                              &count, &msgs);
+     fail_unless(ret == ENOENT, "sysdb_search_entry failed: %d, %s",
+                                ret, strerror(ret));
+@@ -5215,7 +5223,7 @@ START_TEST (test_sysdb_search_return_ENOENT)
+                             data->username);
+     fail_if(user_dn == NULL, "sysdb_user_dn failed");
+     SSS_LDB_SEARCH(ret, test_ctx->sysdb->ldb, test_ctx, &res, user_dn,
+-                   LDB_SCOPE_BASE, NULL, "objectClass=user");
++                   LDB_SCOPE_BASE, NULL, SYSDB_UC);
+ 
+     fail_unless(ret == ENOENT, "SSS_LDB_SEARCH failed: %d, %s",
+                                ret, strerror(ret));
+diff --git a/src/tools/sssctl/sssctl_cache.c b/src/tools/sssctl/sssctl_cache.c
+index 80f65bb55df42d0b123023bb9b1efdb2353b8e20..42a2a60fd31631b3c86d17ddbdd8027a8468366d 100644
+--- a/src/tools/sssctl/sssctl_cache.c
++++ b/src/tools/sssctl/sssctl_cache.c
+@@ -335,7 +335,8 @@ static const char *sssctl_create_filter(TALLOC_CTX *mem_ctx,
+         talloc_free(filter_value_old);
+     }
+ 
+-    filter = talloc_asprintf(mem_ctx, "(&(objectClass=%s)(|(%s=%s)(%s=%s)))",
++    filter = talloc_asprintf(mem_ctx, "(&(%s=%s)(|(%s=%s)(%s=%s)))",
++                             obj_type == CACHED_NETGROUP ? SYSDB_OBJECTCLASS : SYSDB_OBJECTCATEGORY,
+                              class, attr_name, filter_value,
+                              SYSDB_NAME_ALIAS, filter_value);
+ 
+-- 
+2.14.3
+
diff --git a/SOURCES/0056-ssh-add-support-for-certificates-from-non-default-vi.patch b/SOURCES/0056-ssh-add-support-for-certificates-from-non-default-vi.patch
deleted file mode 100644
index cbeb244..0000000
--- a/SOURCES/0056-ssh-add-support-for-certificates-from-non-default-vi.patch
+++ /dev/null
@@ -1,52 +0,0 @@
-From 61c2661fe7445531f53ef298a98a21ae0278397c Mon Sep 17 00:00:00 2001
-From: Sumit Bose <sbose@redhat.com>
-Date: Thu, 16 Mar 2017 13:00:48 +0100
-Subject: [PATCH 56/60] ssh: add support for certificates from non-default
- views
-
-Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
----
- src/responder/ssh/ssh_reply.c | 20 +++++++++++++++++++-
- 1 file changed, 19 insertions(+), 1 deletion(-)
-
-diff --git a/src/responder/ssh/ssh_reply.c b/src/responder/ssh/ssh_reply.c
-index 7093e47253b5687bab387feed5299c2d0841d43c..1bb9d331868cc18488718c24fd82f32af780b525 100644
---- a/src/responder/ssh/ssh_reply.c
-+++ b/src/responder/ssh/ssh_reply.c
-@@ -204,7 +204,7 @@ ssh_get_output_keys(TALLOC_CTX *mem_ctx,
-     uint32_t i = 0;
-     errno_t ret;
- 
--    elements = talloc_zero_array(mem_ctx, struct ldb_message_element *, 5);
-+    elements = talloc_zero_array(mem_ctx, struct ldb_message_element *, 6);
-     if (elements == NULL) {
-         return ENOMEM;
-     }
-@@ -244,6 +244,24 @@ ssh_get_output_keys(TALLOC_CTX *mem_ctx,
-         }
-     }
- 
-+    if (DOM_HAS_VIEWS(domain)) {
-+        user_cert = ldb_msg_find_element(msg, OVERRIDE_PREFIX SYSDB_USER_CERT);
-+        if (user_cert != NULL) {
-+            ret = get_valid_certs_keys(elements, ssh_ctx, user_cert,
-+                                       &elements[i]);
-+            if (ret != EOK) {
-+                DEBUG(SSSDBG_OP_FAILURE, "get_valid_certs_keys failed.\n");
-+                goto done;
-+            }
-+
-+            if (elements[i] != NULL) {
-+                elements[i]->flags |= SSS_EL_FLAG_BIN_DATA;
-+                num_keys += elements[i]->num_values;
-+                i++;
-+            }
-+        }
-+    }
-+
-     *_elements = elements;
-     *_num_keys = num_keys;
- 
--- 
-2.9.3
-
diff --git a/SOURCES/0056-sysdb-do-not-use-LDB_SCOPE_ONELEVEL.patch b/SOURCES/0056-sysdb-do-not-use-LDB_SCOPE_ONELEVEL.patch
new file mode 100644
index 0000000..e0f0758
--- /dev/null
+++ b/SOURCES/0056-sysdb-do-not-use-LDB_SCOPE_ONELEVEL.patch
@@ -0,0 +1,83 @@
+From c36d5d56e62f879dbc1c58155097dfc26746a7f4 Mon Sep 17 00:00:00 2001
+From: Sumit Bose <sbose@redhat.com>
+Date: Tue, 14 Nov 2017 13:09:18 +0100
+Subject: [PATCH 56/57] sysdb: do not use LDB_SCOPE_ONELEVEL
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Currently the index for one-level searches is a huge blob which maps all
+parents with all it children. Handling this blob is costly and since all
+searches using LDB_SCOPE_ONELEVEL also have a filter with indexed
+attributes a sub-tree search would be more efficient. But since libldb
+currently first looks at the scope and hence use the one-level index
+blob we have to explicitly use LDB_SCOPE_SUBTREE in the callers to use
+the more efficient attribute based inxed.
+
+Related to https://pagure.io/SSSD/sssd/issue/3503
+
+Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
+Reviewed-by: Fabiano Fidêncio <fidencio@redhat.com>
+(cherry picked from commit 98195e591c4d97caa6125e8214879660b740973f)
+---
+ src/db/sysdb_autofs.c         | 2 +-
+ src/db/sysdb_ranges.c         | 2 +-
+ src/db/sysdb_subdomains.c     | 2 +-
+ src/responder/ifp/ifp_users.c | 2 +-
+ 4 files changed, 4 insertions(+), 4 deletions(-)
+
+diff --git a/src/db/sysdb_autofs.c b/src/db/sysdb_autofs.c
+index b3e9b4ec83b66ec65a72ab7a3180106e2293d8a5..89803a778c370c899611ee5e15b7ae1a48e82cb9 100644
+--- a/src/db/sysdb_autofs.c
++++ b/src/db/sysdb_autofs.c
+@@ -384,7 +384,7 @@ sysdb_autofs_entries_by_map(TALLOC_CTX *mem_ctx,
+         goto done;
+     }
+ 
+-    ret = sysdb_search_entry(tmp_ctx, domain->sysdb, mapdn, LDB_SCOPE_ONELEVEL,
++    ret = sysdb_search_entry(tmp_ctx, domain->sysdb, mapdn, LDB_SCOPE_SUBTREE,
+                              filter, attrs, &count, &msgs);
+     if (ret != EOK && ret != ENOENT) {
+         DEBUG(SSSDBG_OP_FAILURE, "sysdb search failed: %d\n", ret);
+diff --git a/src/db/sysdb_ranges.c b/src/db/sysdb_ranges.c
+index 511e4785d9aa68b2a33b440e1c5ee62e5ccf7ce4..be3a0d37220f833d27417808ddfef0e74c0ba9b2 100644
+--- a/src/db/sysdb_ranges.c
++++ b/src/db/sysdb_ranges.c
+@@ -71,7 +71,7 @@ errno_t sysdb_get_ranges(TALLOC_CTX *mem_ctx, struct sysdb_ctx *sysdb,
+         goto done;
+     }
+     ret = ldb_search(sysdb->ldb, tmp_ctx, &res,
+-                     basedn, LDB_SCOPE_ONELEVEL,
++                     basedn, LDB_SCOPE_SUBTREE,
+                      attrs, "objectclass=%s", SYSDB_ID_RANGE_CLASS);
+     if (ret != LDB_SUCCESS) {
+         ret = EIO;
+diff --git a/src/db/sysdb_subdomains.c b/src/db/sysdb_subdomains.c
+index 353561765904efe4bd698c38949a1b290ecf0b80..0dd05c24c963f12a28ef6f6b64dc40faa7fcc649 100644
+--- a/src/db/sysdb_subdomains.c
++++ b/src/db/sysdb_subdomains.c
+@@ -338,7 +338,7 @@ errno_t sysdb_update_subdomains(struct sss_domain_info *domain,
+         goto done;
+     }
+     ret = ldb_search(domain->sysdb->ldb, tmp_ctx, &res,
+-                     basedn, LDB_SCOPE_ONELEVEL,
++                     basedn, LDB_SCOPE_SUBTREE,
+                      attrs, "objectclass=%s", SYSDB_SUBDOMAIN_CLASS);
+     if (ret != LDB_SUCCESS) {
+         ret = EIO;
+diff --git a/src/responder/ifp/ifp_users.c b/src/responder/ifp/ifp_users.c
+index cb342a245ef6545168a7a60c252505f50576fdf7..f66587b8cf81a555dd1cb7aff12e0c8347c250b1 100644
+--- a/src/responder/ifp/ifp_users.c
++++ b/src/responder/ifp/ifp_users.c
+@@ -1449,7 +1449,7 @@ void ifp_users_user_get_extra_attributes(struct sbus_request *sbus_req,
+     }
+ 
+     ret = sysdb_search_entry(sbus_req, domain->sysdb, basedn,
+-                             LDB_SCOPE_ONELEVEL, filter,
++                             LDB_SCOPE_SUBTREE, filter,
+                              extra, &count, &user);
+     if (ret != EOK) {
+         DEBUG(SSSDBG_CRIT_FAILURE, "Unable to lookup user [%d]: %s\n",
+-- 
+2.14.3
+
diff --git a/SOURCES/0057-krb5-return-to-responder-that-pkinit-is-not-availabl.patch b/SOURCES/0057-krb5-return-to-responder-that-pkinit-is-not-availabl.patch
deleted file mode 100644
index db56ae9..0000000
--- a/SOURCES/0057-krb5-return-to-responder-that-pkinit-is-not-availabl.patch
+++ /dev/null
@@ -1,60 +0,0 @@
-From 01ed8c7d7fcd9090d0953f85ef0604cbcad4f48b Mon Sep 17 00:00:00 2001
-From: Sumit Bose <sbose@redhat.com>
-Date: Thu, 16 Mar 2017 20:43:08 +0100
-Subject: [PATCH 57/60] krb5: return to responder that pkinit is not available
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-If pkinit is not available for a user but other authentication methods
-are SSSD should still fall back to local certificate based
-authentication if Smartcard credentials are provided.
-
-Resolves https://pagure.io/SSSD/sssd/issue/3343
-
-Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
-Reviewed-by: Lukáš Slebodník <lslebodn@redhat.com>
----
- src/providers/krb5/krb5_child.c | 17 +++++++++++++----
- 1 file changed, 13 insertions(+), 4 deletions(-)
-
-diff --git a/src/providers/krb5/krb5_child.c b/src/providers/krb5/krb5_child.c
-index 777a25f2a0ea434dde12d2396f6a35c2a1b86cd0..a4128dda6b0861a95dba223047d66c4158b1afb6 100644
---- a/src/providers/krb5/krb5_child.c
-+++ b/src/providers/krb5/krb5_child.c
-@@ -42,6 +42,10 @@
- 
- #define SSSD_KRB5_CHANGEPW_PRINCIPAL "kadmin/changepw"
- 
-+#define IS_SC_AUTHTOK(tok) ( \
-+    sss_authtok_get_type((tok)) == SSS_AUTHTOK_TYPE_SC_PIN \
-+        || sss_authtok_get_type((tok)) == SSS_AUTHTOK_TYPE_SC_KEYPAD)
-+
- enum k5c_fast_opt {
-     K5C_FAST_NEVER,
-     K5C_FAST_TRY,
-@@ -1529,12 +1533,17 @@ static krb5_error_code get_and_save_tgt(struct krb5_req *kr,
-              * pre-auth module is missing or no Smartcard is inserted and only
-              * pkinit is available KRB5_PREAUTH_FAILED is returned.
-              * ERR_NO_AUTH_METHOD_AVAILABLE is used to indicate to the
--             * frontend that local authentication might be tried. */
-+             * frontend that local authentication might be tried.
-+             * Same is true if Smartcard credentials are given but only other
-+             * authentication methods are available. */
-             if (kr->pd->cmd == SSS_PAM_AUTHENTICATE
-                     && kerr == KRB5_PREAUTH_FAILED
--                    && kr->password_prompting == false
--                    && kr->otp == false
--                    && kr->pkinit_prompting == false) {
-+                    && kr->pkinit_prompting == false
-+                    && (( kr->password_prompting == false
-+                              && kr->otp == false)
-+                            || ((kr->otp == true
-+                                    || kr->password_prompting == true)
-+                              && IS_SC_AUTHTOK(kr->pd->authtok))) ) {
-                 return ERR_NO_AUTH_METHOD_AVAILABLE;
-             }
-             return kerr;
--- 
-2.9.3
-
diff --git a/SOURCES/0057-sysdb-remove-IDXONE-and-objectClass-from-users-and-g.patch b/SOURCES/0057-sysdb-remove-IDXONE-and-objectClass-from-users-and-g.patch
new file mode 100644
index 0000000..278541e
--- /dev/null
+++ b/SOURCES/0057-sysdb-remove-IDXONE-and-objectClass-from-users-and-g.patch
@@ -0,0 +1,384 @@
+From 0f907d8501387ec32dbb00e1c38d5da25e698f90 Mon Sep 17 00:00:00 2001
+From: Sumit Bose <sbose@redhat.com>
+Date: Tue, 14 Nov 2017 13:14:14 +0100
+Subject: [PATCH 57/57] sysdb: remove IDXONE and objectClass from users and
+ groups
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+This patch does the needed sysdb update for the previous to patches. It
+removes the one-level search index IDXONE and replaces objectClass with
+objectCategory in the user and group objects.
+
+Related to https://pagure.io/SSSD/sssd/issue/3503
+
+Reviewed-by: Fabiano Fidêncio <fidencio@redhat.com>
+Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
+(cherry picked from commit 2927da49dd8a16fff6312d89ad43cc355655800c)
+---
+ src/db/sysdb_init.c    |  52 +++++++++++-
+ src/db/sysdb_private.h |  11 ++-
+ src/db/sysdb_upgrade.c | 217 +++++++++++++++++++++++++++++++++++++++++++++++++
+ 3 files changed, 274 insertions(+), 6 deletions(-)
+
+diff --git a/src/db/sysdb_init.c b/src/db/sysdb_init.c
+index 44a7918f603fe1368b7d81738666de6bb47b83d0..74ad23f3050da0ae14fa495d2302f4a858fcd3c5 100644
+--- a/src/db/sysdb_init.c
++++ b/src/db/sysdb_init.c
+@@ -359,8 +359,48 @@ static errno_t sysdb_ts_cache_upgrade(TALLOC_CTX *mem_ctx,
+                                       const char *cur_version,
+                                       const char **_new_version)
+ {
+-    /* Currently the sysdb cache only has one version */
+-    return EFAULT;
++    errno_t ret;
++    TALLOC_CTX *tmp_ctx;
++    const char *version;
++    struct ldb_context *save_ldb;
++
++    tmp_ctx = talloc_new(NULL);
++    if (tmp_ctx == NULL) {
++        return ENOMEM;
++    }
++
++    /* The upgrade process depends on having ldb around, yet the upgrade
++     * function shouldn't set the ldb pointer, only the connect function
++     * should after it's successful. To avoid hard refactoring, save the
++     * ldb pointer here and restore in the 'done' handler
++     */
++    save_ldb = sysdb->ldb;
++    sysdb->ldb = ldb;
++
++    version = talloc_strdup(tmp_ctx, cur_version);
++    if (version == NULL) {
++        ret = ENOMEM;
++        goto done;
++    }
++
++    DEBUG(SSSDBG_CONF_SETTINGS,
++          "Upgrading timstamp cache of DB [%s] from version: %s\n",
++          domain->name, version);
++
++    if (strcmp(version, SYSDB_TS_VERSION_0_1) == 0) {
++        ret = sysdb_ts_upgrade_01(sysdb, &version);
++        if (ret != EOK) {
++            goto done;
++        }
++    }
++
++    ret = EOK;
++
++done:
++    sysdb->ldb = save_ldb;
++    *_new_version = version;
++    talloc_free(tmp_ctx);
++    return ret;
+ }
+ 
+ static errno_t sysdb_domain_cache_upgrade(TALLOC_CTX *mem_ctx,
+@@ -511,6 +551,14 @@ static errno_t sysdb_domain_cache_upgrade(TALLOC_CTX *mem_ctx,
+         }
+     }
+ 
++    if (strcmp(version, SYSDB_VERSION_0_19) == 0) {
++        ret = sysdb_upgrade_19(sysdb, &version);
++        if (ret != EOK) {
++            goto done;
++        }
++    }
++
++
+     ret = EOK;
+ done:
+     sysdb->ldb = save_ldb;
+diff --git a/src/db/sysdb_private.h b/src/db/sysdb_private.h
+index dbd75615bc212e73c4338a76dceaa68a5889ed1d..cac06ba46da23080d1ab661502d0792bd37b9291 100644
+--- a/src/db/sysdb_private.h
++++ b/src/db/sysdb_private.h
+@@ -23,6 +23,7 @@
+ #ifndef __INT_SYS_DB_H__
+ #define __INT_SYS_DB_H__
+ 
++#define SYSDB_VERSION_0_20 "0.20"
+ #define SYSDB_VERSION_0_19 "0.19"
+ #define SYSDB_VERSION_0_18 "0.18"
+ #define SYSDB_VERSION_0_17 "0.17"
+@@ -43,7 +44,7 @@
+ #define SYSDB_VERSION_0_2 "0.2"
+ #define SYSDB_VERSION_0_1 "0.1"
+ 
+-#define SYSDB_VERSION SYSDB_VERSION_0_19
++#define SYSDB_VERSION SYSDB_VERSION_0_20
+ 
+ #define SYSDB_BASE_LDIF \
+      "dn: @ATTRIBUTES\n" \
+@@ -72,7 +73,6 @@
+      "@IDXATTR: sudoUser\n" \
+      "@IDXATTR: sshKnownHostsExpire\n" \
+      "@IDXATTR: objectSIDString\n" \
+-     "@IDXONE: 1\n" \
+      "@IDXATTR: ghost\n" \
+      "@IDXATTR: userPrincipalName\n" \
+      "@IDXATTR: canonicalUserPrincipalName\n" \
+@@ -92,9 +92,10 @@
+      "\n"
+ 
+ /* The timestamp cache has its own versioning */
++#define SYSDB_TS_VERSION_0_2 "0.2"
+ #define SYSDB_TS_VERSION_0_1 "0.1"
+ 
+-#define SYSDB_TS_VERSION SYSDB_TS_VERSION_0_1
++#define SYSDB_TS_VERSION SYSDB_TS_VERSION_0_2
+ 
+ #define SYSDB_TS_BASE_LDIF \
+      "dn: @ATTRIBUTES\n" \
+@@ -103,7 +104,6 @@
+      "dn: @INDEXLIST\n" \
+      "@IDXATTR: lastUpdate\n" \
+      "@IDXATTR: dataExpireTimestamp\n" \
+-     "@IDXONE: 1\n" \
+      "\n" \
+      "dn: cn=sysdb\n" \
+      "cn: sysdb\n" \
+@@ -169,6 +169,9 @@ int sysdb_upgrade_17(struct sysdb_ctx *sysdb,
+                      struct sysdb_dom_upgrade_ctx *upgrade_ctx,
+                      const char **ver);
+ int sysdb_upgrade_18(struct sysdb_ctx *sysdb, const char **ver);
++int sysdb_upgrade_19(struct sysdb_ctx *sysdb, const char **ver);
++
++int sysdb_ts_upgrade_01(struct sysdb_ctx *sysdb, const char **ver);
+ 
+ int sysdb_add_string(struct ldb_message *msg,
+                      const char *attr, const char *value);
+diff --git a/src/db/sysdb_upgrade.c b/src/db/sysdb_upgrade.c
+index 365d45f7ebd78523ca9ec4b9c2158cc09acb5489..bc157a24664239bc1255e49a1825243a07acc90f 100644
+--- a/src/db/sysdb_upgrade.c
++++ b/src/db/sysdb_upgrade.c
+@@ -2317,6 +2317,223 @@ done:
+     return ret;
+ }
+ 
++static errno_t add_object_category(struct ldb_context *ldb,
++                                   struct upgrade_ctx *ctx)
++{
++    errno_t ret;
++    struct ldb_result *objects = NULL;
++    const char *attrs[] = { SYSDB_OBJECTCLASS, NULL };
++    struct ldb_dn *base_dn;
++    size_t c;
++    const char *class_name;
++    struct ldb_message *msg = NULL;
++    struct ldb_message *del_msg = NULL;
++
++    base_dn = ldb_dn_new(ctx, ldb, SYSDB_BASE);
++    if (base_dn == NULL) {
++        DEBUG(SSSDBG_CRIT_FAILURE, "Failed create base dn.\n");
++        return ENOMEM;
++    }
++
++    ret = ldb_search(ldb, ctx, &objects, base_dn,
++                     LDB_SCOPE_SUBTREE, attrs,
++                     "(|("SYSDB_OBJECTCLASS"="SYSDB_USER_CLASS")"
++                       "("SYSDB_OBJECTCLASS"="SYSDB_GROUP_CLASS"))");
++    talloc_free(base_dn);
++    if (ret != LDB_SUCCESS) {
++        DEBUG(SSSDBG_CRIT_FAILURE, "Failed to search objects: %d\n", ret);
++        ret = sysdb_error_to_errno(ret);
++        goto done;
++    }
++
++    if (objects == NULL || objects->count == 0) {
++        DEBUG(SSSDBG_TRACE_LIBS, "No objects found, nothing to do.");
++        ret = EOK;
++        goto done;
++    }
++
++    del_msg = ldb_msg_new(ctx);
++    if (del_msg == NULL) {
++        DEBUG(SSSDBG_OP_FAILURE, "ldb_msg_new failed.\n");
++        ret = ENOMEM;
++        goto done;
++    }
++    ret = ldb_msg_add_empty(del_msg, SYSDB_OBJECTCLASS, LDB_FLAG_MOD_DELETE,
++                            NULL);
++    if (ret != LDB_SUCCESS) {
++        DEBUG(SSSDBG_OP_FAILURE, "ldb_msg_add_empty failed.\n");
++        ret = sysdb_error_to_errno(ret);
++        goto done;
++    }
++
++    DEBUG(SSSDBG_TRACE_ALL, "Found [%d] objects.\n", objects->count);
++    for (c = 0; c < objects->count; c++) {
++        DEBUG(SSSDBG_TRACE_ALL, "Updating [%s].\n",
++              ldb_dn_get_linearized(objects->msgs[c]->dn));
++
++        class_name = ldb_msg_find_attr_as_string(objects->msgs[c],
++                                                 SYSDB_OBJECTCLASS, NULL);
++        if (class_name == NULL) {
++            DEBUG(SSSDBG_OP_FAILURE, "Searched objects by objectClass, "
++                                     "but result does not have one.\n");
++            ret = EINVAL;
++            goto done;
++        }
++
++        talloc_free(msg);
++        msg = ldb_msg_new(ctx);
++        if (msg == NULL) {
++            DEBUG(SSSDBG_OP_FAILURE, "ldb_msg_new failed.\n");
++            ret = ENOMEM;
++            goto done;
++        }
++
++        msg->dn = objects->msgs[c]->dn;
++        del_msg->dn = objects->msgs[c]->dn;
++
++        ret = ldb_msg_add_empty(msg, SYSDB_OBJECTCATEGORY, LDB_FLAG_MOD_ADD,
++                                NULL);
++        if (ret != LDB_SUCCESS) {
++            DEBUG(SSSDBG_OP_FAILURE, "ldb_msg_add_empty failed.\n");
++            ret = sysdb_error_to_errno(ret);
++            goto done;
++        }
++
++        ret = ldb_msg_add_string(msg, SYSDB_OBJECTCATEGORY, class_name);
++        if (ret != LDB_SUCCESS) {
++            DEBUG(SSSDBG_OP_FAILURE, "ldb_msg_add_string failed.\n");
++            ret = sysdb_error_to_errno(ret);
++            goto done;
++        }
++
++        DEBUG(SSSDBG_TRACE_ALL, "Adding [%s] to [%s].\n", class_name,
++              ldb_dn_get_linearized(objects->msgs[c]->dn));
++        ret = ldb_modify(ldb, msg);
++        if (ret != LDB_SUCCESS) {
++            DEBUG(SSSDBG_OP_FAILURE,
++                  "Failed to add objectCategory to %s: %d.\n",
++                  ldb_dn_get_linearized(objects->msgs[c]->dn),
++                  sysdb_error_to_errno(ret));
++            ret = sysdb_error_to_errno(ret);
++            goto done;
++        }
++
++        ret = ldb_modify(ldb, del_msg);
++        if (ret != LDB_SUCCESS) {
++            DEBUG(SSSDBG_OP_FAILURE,
++                  "Failed to remove objectClass from %s: %d.\n",
++                  ldb_dn_get_linearized(objects->msgs[c]->dn),
++                  sysdb_error_to_errno(ret));
++            ret = sysdb_error_to_errno(ret);
++            goto done;
++        }
++    }
++
++    ret = EOK;
++
++done:
++    talloc_free(msg);
++    talloc_free(del_msg);
++    talloc_free(objects);
++
++    return ret;
++}
++
++int sysdb_upgrade_19(struct sysdb_ctx *sysdb, const char **ver)
++{
++    struct upgrade_ctx *ctx;
++    errno_t ret;
++    struct ldb_message *msg = NULL;
++
++    ret = commence_upgrade(sysdb, sysdb->ldb, SYSDB_VERSION_0_20, &ctx);
++    if (ret) {
++        return ret;
++    }
++
++    ret = add_object_category(sysdb->ldb, ctx);
++    if (ret != EOK) {
++        DEBUG(SSSDBG_CRIT_FAILURE, "add_object_category failed.\n");
++        goto done;
++    }
++
++    /* Remove @IDXONE from index */
++    msg = ldb_msg_new(ctx);
++    if (msg == NULL) {
++        ret = ENOMEM;
++        goto done;
++    }
++
++    msg->dn = ldb_dn_new(msg, sysdb->ldb, "@INDEXLIST");
++    if (msg->dn == NULL) {
++        ret = ENOMEM;
++        goto done;
++    }
++
++    ret = ldb_msg_add_empty(msg, "@IDXONE", LDB_FLAG_MOD_DELETE, NULL);
++    if (ret != LDB_SUCCESS) {
++        ret = ENOMEM;
++        goto done;
++    }
++
++    ret = ldb_modify(sysdb->ldb, msg);
++    if (ret != LDB_SUCCESS) {
++        ret = sysdb_error_to_errno(ret);
++        goto done;
++    }
++
++    /* conversion done, update version number */
++    ret = update_version(ctx);
++
++done:
++    ret = finish_upgrade(ret, &ctx, ver);
++    return ret;
++}
++
++int sysdb_ts_upgrade_01(struct sysdb_ctx *sysdb, const char **ver)
++{
++    struct upgrade_ctx *ctx;
++    errno_t ret;
++    struct ldb_message *msg = NULL;
++
++    ret = commence_upgrade(sysdb, sysdb->ldb, SYSDB_TS_VERSION_0_2, &ctx);
++    if (ret) {
++        return ret;
++    }
++
++    /* Remove @IDXONE from index */
++    talloc_free(msg);
++    msg = ldb_msg_new(ctx);
++    if (msg == NULL) {
++        ret = ENOMEM;
++        goto done;
++    }
++
++    msg->dn = ldb_dn_new(msg, sysdb->ldb, "@INDEXLIST");
++    if (msg->dn == NULL) {
++        ret = ENOMEM;
++        goto done;
++    }
++
++    ret = ldb_msg_add_empty(msg, "@IDXONE", LDB_FLAG_MOD_DELETE, NULL);
++    if (ret != LDB_SUCCESS) {
++        ret = ENOMEM;
++        goto done;
++    }
++
++    ret = ldb_modify(sysdb->ldb, msg);
++    if (ret != LDB_SUCCESS) {
++        ret = sysdb_error_to_errno(ret);
++        goto done;
++    }
++
++    /* conversion done, update version number */
++    ret = update_version(ctx);
++
++done:
++    ret = finish_upgrade(ret, &ctx, ver);
++    return ret;
++}
++
+ /*
+  * Example template for future upgrades.
+  * Copy and change version numbers as appropriate.
+-- 
+2.14.3
+
diff --git a/SOURCES/0058-IPA-add-mapped-attributes-to-user-from-trusted-domai.patch b/SOURCES/0058-IPA-add-mapped-attributes-to-user-from-trusted-domai.patch
deleted file mode 100644
index 2b6616e..0000000
--- a/SOURCES/0058-IPA-add-mapped-attributes-to-user-from-trusted-domai.patch
+++ /dev/null
@@ -1,153 +0,0 @@
-From b8a36e1be5cdd2c61ddf8e40970270bb878d26a3 Mon Sep 17 00:00:00 2001
-From: Sumit Bose <sbose@redhat.com>
-Date: Wed, 22 Mar 2017 14:13:05 +0100
-Subject: [PATCH 58/60] IPA: add mapped attributes to user from trusted domains
-
-Allow the usage of the mapped attribute for the lookup of AD users on
-IPA clients as already used for the normal LDAP lookup.
-
-Related to https://pagure.io/SSSD/sssd/issue/3050
-
-Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
----
- src/providers/ipa/ipa_s2n_exop.c | 33 ++++++++++++++++++++++++---------
- 1 file changed, 24 insertions(+), 9 deletions(-)
-
-diff --git a/src/providers/ipa/ipa_s2n_exop.c b/src/providers/ipa/ipa_s2n_exop.c
-index c99312274073858e5e03f3e82c069dafc839eb61..05c32a24d61947e62884f460069083fb81f40fe0 100644
---- a/src/providers/ipa/ipa_s2n_exop.c
-+++ b/src/providers/ipa/ipa_s2n_exop.c
-@@ -761,6 +761,7 @@ static errno_t ipa_s2n_save_objects(struct sss_domain_info *dom,
-                                     struct resp_attrs *simple_attrs,
-                                     const char *view_name,
-                                     struct sysdb_attrs *override_attrs,
-+                                    struct sysdb_attrs *mapped_attrs,
-                                     bool update_initgr_timeout);
- 
- static errno_t s2n_response_to_attrs(TALLOC_CTX *mem_ctx,
-@@ -1009,6 +1010,7 @@ struct ipa_s2n_get_list_state {
-     struct resp_attrs *attrs;
-     struct sss_domain_info *obj_domain;
-     struct sysdb_attrs *override_attrs;
-+    struct sysdb_attrs *mapped_attrs;
- };
- 
- static errno_t ipa_s2n_get_list_step(struct tevent_req *req);
-@@ -1025,7 +1027,8 @@ static struct tevent_req *ipa_s2n_get_list_send(TALLOC_CTX *mem_ctx,
-                                                 int entry_type,
-                                                 enum request_types request_type,
-                                                 enum req_input_type list_type,
--                                                char **list)
-+                                                char **list,
-+                                                struct sysdb_attrs *mapped_attrs)
- {
-     int ret;
-     struct ipa_s2n_get_list_state *state;
-@@ -1057,6 +1060,7 @@ static struct tevent_req *ipa_s2n_get_list_send(TALLOC_CTX *mem_ctx,
-     state->request_type = request_type;
-     state->attrs = NULL;
-     state->override_attrs = NULL;
-+    state->mapped_attrs = mapped_attrs;
- 
-     ret = ipa_s2n_get_list_step(req);
-     if (ret != EOK) {
-@@ -1288,7 +1292,8 @@ static errno_t ipa_s2n_get_list_save_step(struct tevent_req *req)
- 
-     ret = ipa_s2n_save_objects(state->dom, &state->req_input, state->attrs,
-                                NULL, state->ipa_ctx->view_name,
--                               state->override_attrs, false);
-+                               state->override_attrs, state->mapped_attrs,
-+                               false);
-     if (ret != EOK) {
-         DEBUG(SSSDBG_OP_FAILURE, "ipa_s2n_save_objects failed.\n");
-         return ret;
-@@ -1704,7 +1709,7 @@ static void ipa_s2n_get_user_done(struct tevent_req *subreq)
-                                                  BE_REQ_GROUP,
-                                                  REQ_FULL_WITH_MEMBERS,
-                                                  REQ_INP_NAME,
--                                                 missing_list);
-+                                                 missing_list, NULL);
-                 if (subreq == NULL) {
-                     DEBUG(SSSDBG_OP_FAILURE,
-                           "ipa_s2n_get_list_send failed.\n");
-@@ -1732,7 +1737,7 @@ static void ipa_s2n_get_user_done(struct tevent_req *subreq)
-                                                  BE_REQ_USER,
-                                                  REQ_FULL_WITH_MEMBERS,
-                                                  REQ_INP_NAME,
--                                                 missing_list);
-+                                                 missing_list, NULL);
-                 if (subreq == NULL) {
-                     DEBUG(SSSDBG_OP_FAILURE,
-                           "ipa_s2n_get_list_send failed.\n");
-@@ -1810,7 +1815,7 @@ static void ipa_s2n_get_user_done(struct tevent_req *subreq)
- 
-     if (ret == ENOENT || is_default_view(state->ipa_ctx->view_name)) {
-         ret = ipa_s2n_save_objects(state->dom, state->req_input, state->attrs,
--                                   state->simple_attrs, NULL, NULL, true);
-+                                   state->simple_attrs, NULL, NULL, NULL, true);
-         if (ret != EOK) {
-             DEBUG(SSSDBG_OP_FAILURE, "ipa_s2n_save_objects failed.\n");
-             goto done;
-@@ -1978,6 +1983,7 @@ static errno_t ipa_s2n_save_objects(struct sss_domain_info *dom,
-                                     struct resp_attrs *simple_attrs,
-                                     const char *view_name,
-                                     struct sysdb_attrs *override_attrs,
-+                                    struct sysdb_attrs *mapped_attrs,
-                                     bool update_initgr_timeout)
- {
-     int ret;
-@@ -2305,6 +2311,15 @@ static errno_t ipa_s2n_save_objects(struct sss_domain_info *dom,
-                 goto done;
-             }
- 
-+            if (mapped_attrs != NULL) {
-+                ret = sysdb_set_user_attr(dom, name, mapped_attrs,
-+                                          SYSDB_MOD_ADD);
-+                if (ret != EOK) {
-+                    DEBUG(SSSDBG_OP_FAILURE, "sysdb_set_user_attr failed.\n");
-+                    goto done;
-+                }
-+            }
-+
-             if (gid_override_attrs != NULL) {
-                 ret = sysdb_set_user_attr(dom, name, gid_override_attrs,
-                                           SYSDB_MOD_REP);
-@@ -2487,7 +2502,7 @@ static void ipa_s2n_get_list_done(struct tevent_req  *subreq)
-                                  &sid_str);
-     if (ret == ENOENT) {
-         ret = ipa_s2n_save_objects(state->dom, state->req_input, state->attrs,
--                                   state->simple_attrs, NULL, NULL, true);
-+                                   state->simple_attrs, NULL, NULL, NULL, true);
-         if (ret != EOK) {
-             DEBUG(SSSDBG_OP_FAILURE, "ipa_s2n_save_objects failed.\n");
-             goto fail;
-@@ -2525,7 +2540,7 @@ static void ipa_s2n_get_list_done(struct tevent_req  *subreq)
-         ret = ipa_s2n_save_objects(state->dom, state->req_input, state->attrs,
-                                    state->simple_attrs,
-                                    state->ipa_ctx->view_name,
--                                   state->override_attrs, true);
-+                                   state->override_attrs, NULL, true);
-         if (ret != EOK) {
-             DEBUG(SSSDBG_OP_FAILURE, "ipa_s2n_save_objects failed.\n");
-             tevent_req_error(req, ret);
-@@ -2561,7 +2576,7 @@ static void ipa_s2n_get_user_get_override_done(struct tevent_req *subreq)
- 
-     ret = ipa_s2n_save_objects(state->dom, state->req_input, state->attrs,
-                                state->simple_attrs, state->ipa_ctx->view_name,
--                               override_attrs, true);
-+                               override_attrs, NULL, true);
-     if (ret != EOK) {
-         DEBUG(SSSDBG_OP_FAILURE, "ipa_s2n_save_objects failed.\n");
-         tevent_req_error(req, ret);
-@@ -2662,7 +2677,7 @@ struct tevent_req *ipa_get_subdom_acct_process_pac_send(TALLOC_CTX *mem_ctx,
-                                dp_opt_get_int(ipa_ctx->sdap_id_ctx->opts->basic,
-                                               SDAP_SEARCH_TIMEOUT),
-                                BE_REQ_BY_SECID, REQ_FULL, REQ_INP_SECID,
--                               state->missing_sids);
-+                               state->missing_sids, NULL);
-     if (subreq == NULL) {
-         DEBUG(SSSDBG_OP_FAILURE, "ipa_s2n_get_list_send failed.\n");
-         ret = ENOMEM;
--- 
-2.9.3
-
diff --git a/SOURCES/0058-mmap_cache-make-checks-independent-of-input-size.patch b/SOURCES/0058-mmap_cache-make-checks-independent-of-input-size.patch
new file mode 100644
index 0000000..cc91c58
--- /dev/null
+++ b/SOURCES/0058-mmap_cache-make-checks-independent-of-input-size.patch
@@ -0,0 +1,175 @@
+From 531c0ad3e13ddc1f5c31a620aa1f8b91aa8a4053 Mon Sep 17 00:00:00 2001
+From: Sumit Bose <sbose@redhat.com>
+Date: Fri, 17 Nov 2017 10:51:44 +0100
+Subject: [PATCH 58/59] mmap_cache: make checks independent of input size
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Currently the consistency checks for the mmap_cache payload data on the
+client and the responder side include the length of the input string of
+the current request. Since there might be hash collisions which other
+much longer or much shorter names those checks might fail although there
+is no data corruption.
+
+This patch removes the checks using the length of the input and adds a
+check if the name found in the payload is zero-terminated inside of the
+payload data.
+
+Resolves https://pagure.io/SSSD/sssd/issue/3571
+
+Reviewed-by: Michal Židek <mzidek@redhat.com>
+Reviewed-by: Lukáš Slebodník <lslebodn@redhat.com>
+(cherry picked from commit 4382047490dd4f80b407cc1e618da048f13e5f8f)
+---
+ src/responder/nss/nsssrv_mmap_cache.c | 34 ++++++++++++++++++++++++----------
+ src/sss_client/nss_mc_group.c         | 14 ++++++++------
+ src/sss_client/nss_mc_initgr.c        | 14 +++++++++-----
+ src/sss_client/nss_mc_passwd.c        | 14 ++++++++------
+ 4 files changed, 49 insertions(+), 27 deletions(-)
+
+diff --git a/src/responder/nss/nsssrv_mmap_cache.c b/src/responder/nss/nsssrv_mmap_cache.c
+index a87ad646f9b741db3eb18680678697032fc420ba..ad5adbce15e50c065d4d16e626be97fd23d06643 100644
+--- a/src/responder/nss/nsssrv_mmap_cache.c
++++ b/src/responder/nss/nsssrv_mmap_cache.c
+@@ -547,18 +547,32 @@ static struct sss_mc_rec *sss_mc_find_record(struct sss_mc_ctx *mcc,
+             return NULL;
+         }
+ 
++        if (key->len > strs_len) {
++            /* The string cannot be in current record */
++            slot = sss_mc_next_slot_with_hash(rec, hash);
++            continue;
++        }
++
+         safealign_memcpy(&name_ptr, rec->data, sizeof(rel_ptr_t), NULL);
+-        if (key->len > strs_len
+-            || (name_ptr + key->len) > (strs_offset + strs_len)
+-            || (uint8_t *)rec->data + strs_offset + strs_len > max_addr) {
+-            DEBUG(SSSDBG_FATAL_FAILURE,
+-                  "Corrupted fastcache. name_ptr value is %u.\n", name_ptr);
+-            sss_mc_save_corrupted(mcc);
+-            sss_mmap_cache_reset(mcc);
+-            return NULL;
+-        }
+-
+         t_key = (char *)rec->data + name_ptr;
++        /* name_ptr must point to some data in the strs/gids area of the data
++         * payload. Since it is a pointer relative to rec->data it must larger
++         * equal strs_offset and must be smaller then strs_offset + strs_len.
++         * Additionally the area must not end outside of the data table and
++         * t_key must be a zero-terminates string. */
++        if (name_ptr < strs_offset
++                || name_ptr >= strs_offset + strs_len
++                || (uint8_t *)rec->data > max_addr
++                || strs_offset > max_addr - (uint8_t *)rec->data
++                || strs_len > max_addr - (uint8_t *)rec->data - strs_offset) {
++            DEBUG(SSSDBG_FATAL_FAILURE,
++                  "Corrupted fastcache entry at slot %u. "
++                  "name_ptr value is %u.\n", slot, name_ptr);
++            sss_mc_save_corrupted(mcc);
++            sss_mmap_cache_reset(mcc);
++            return NULL;
++        }
++
+         if (strcmp(key->str, t_key) == 0) {
+             break;
+         }
+diff --git a/src/sss_client/nss_mc_group.c b/src/sss_client/nss_mc_group.c
+index ce88d42fdaf4f19e78fc43e187bc28651cdc3c4e..4b1601a171a3af700b6f0d2bfedb3a6198e6df6d 100644
+--- a/src/sss_client/nss_mc_group.c
++++ b/src/sss_client/nss_mc_group.c
+@@ -148,20 +148,22 @@ errno_t sss_nss_mc_getgrnam(const char *name, size_t name_len,
+         }
+ 
+         data = (struct sss_mc_grp_data *)rec->data;
++        rec_name = (char *)data + data->name;
+         /* Integrity check
+-         * - name_len cannot be longer than all strings
+          * - data->name cannot point outside strings
+          * - all strings must be within copy of record
+-         * - size of record must be lower that data table size */
+-        if (name_len > data->strs_len
+-            || (data->name + name_len) > (strs_offset + data->strs_len)
++         * - record must not end outside data table
++         * - rec_name is a zero-terminated string */
++        if (data->name < strs_offset
++            || data->name >= strs_offset + data->strs_len
+             || data->strs_len > rec->len
+-            || rec->len > data_size) {
++            || (uint8_t *) rec + rec->len > gr_mc_ctx.data_table + data_size
++            || memchr(rec_name, '\0',
++                      (strs_offset + data->strs_len) - data->name) == NULL) {
+             ret = ENOENT;
+             goto done;
+         }
+ 
+-        rec_name = (char *)data + data->name;
+         if (strcmp(name, rec_name) == 0) {
+             break;
+         }
+diff --git a/src/sss_client/nss_mc_initgr.c b/src/sss_client/nss_mc_initgr.c
+index a77088d849ad3601cb3edb55fc5ea4ae4c52fe38..d8c01f52ea2d23515d0b462541657dc9416b0915 100644
+--- a/src/sss_client/nss_mc_initgr.c
++++ b/src/sss_client/nss_mc_initgr.c
+@@ -131,15 +131,19 @@ errno_t sss_nss_mc_initgroups_dyn(const char *name, size_t name_len,
+         data = (struct sss_mc_initgr_data *)rec->data;
+         rec_name = (char *)data + data->name;
+         /* Integrity check
+-         * - name_len cannot be longer than all strings or data
++         * - data->name cannot point outside all strings or data
+          * - all data must be within copy of record
+          * - size of record must be lower that data table size
+-         * - data->strs cannot point outside strings */
+-        if (name_len > data->strs_len
++         * - data->strs cannot point outside strings
++         * - rec_name is a zero-terminated string */
++        if (data->name < data_offset
++            || data->name >= data_offset + data->data_len
+             || data->strs_len > data->data_len
+             || data->data_len > rec->len
+-            || rec->len > data_size
+-            || (data->strs + name_len) > (data_offset + data->data_len)) {
++            || (uint8_t *) rec + rec->len
++                                       > initgr_mc_ctx.data_table + data_size
++            || memchr(rec_name, '\0',
++                      (data_offset + data->data_len) - data->name) == NULL) {
+             ret = ENOENT;
+             goto done;
+         }
+diff --git a/src/sss_client/nss_mc_passwd.c b/src/sss_client/nss_mc_passwd.c
+index 0da7ad0aeece7d38ca34bb3fde64adc898eaf0ae..868427f03a7ec0c8bd7401c8547a6f6bead7af28 100644
+--- a/src/sss_client/nss_mc_passwd.c
++++ b/src/sss_client/nss_mc_passwd.c
+@@ -141,20 +141,22 @@ errno_t sss_nss_mc_getpwnam(const char *name, size_t name_len,
+         }
+ 
+         data = (struct sss_mc_pwd_data *)rec->data;
++        rec_name = (char *)data + data->name;
+         /* Integrity check
+-         * - name_len cannot be longer than all strings
+          * - data->name cannot point outside strings
+          * - all strings must be within copy of record
+-         * - size of record must be lower that data table size */
+-        if (name_len > data->strs_len
+-            || (data->name + name_len) > (strs_offset + data->strs_len)
++         * - record must not end outside data table
++         * - rec_name is a zero-terminated string */
++        if (data->name < strs_offset
++            || data->name >= strs_offset + data->strs_len
+             || data->strs_len > rec->len
+-            || rec->len > data_size) {
++            || (uint8_t *) rec + rec->len > pw_mc_ctx.data_table + data_size
++            || memchr(rec_name, '\0',
++                      (strs_offset + data->strs_len) - data->name) == NULL ) {
+             ret = ENOENT;
+             goto done;
+         }
+ 
+-        rec_name = (char *)data + data->name;
+         if (strcmp(name, rec_name) == 0) {
+             break;
+         }
+-- 
+2.14.3
+
diff --git a/SOURCES/0059-IPA-lookup-AD-users-by-certificates-on-IPA-clients.patch b/SOURCES/0059-IPA-lookup-AD-users-by-certificates-on-IPA-clients.patch
deleted file mode 100644
index 1440219..0000000
--- a/SOURCES/0059-IPA-lookup-AD-users-by-certificates-on-IPA-clients.patch
+++ /dev/null
@@ -1,209 +0,0 @@
-From 537e057ef3bd140e418381f2ce74397ab8c34a73 Mon Sep 17 00:00:00 2001
-From: Sumit Bose <sbose@redhat.com>
-Date: Fri, 24 Mar 2017 15:40:41 +0100
-Subject: [PATCH 59/60] IPA: lookup AD users by certificates on IPA clients
-
-Get a list of users mapped to a certificate back from the IPA server,
-look them up and store them together with the certificate used for the
-search as mapped attribute to the cache.
-
-Related to https://pagure.io/SSSD/sssd/issue/3050
-
-Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
----
- src/providers/ipa/ipa_s2n_exop.c | 109 +++++++++++++++++++++++++++++++++++++--
- 1 file changed, 105 insertions(+), 4 deletions(-)
-
-diff --git a/src/providers/ipa/ipa_s2n_exop.c b/src/providers/ipa/ipa_s2n_exop.c
-index 05c32a24d61947e62884f460069083fb81f40fe0..8a3391b4093f1547d84fe44a0f24b1d063d1e28c 100644
---- a/src/providers/ipa/ipa_s2n_exop.c
-+++ b/src/providers/ipa/ipa_s2n_exop.c
-@@ -52,7 +52,8 @@ enum response_types {
-     RESP_USER,
-     RESP_GROUP,
-     RESP_USER_GROUPLIST,
--    RESP_GROUP_MEMBERS
-+    RESP_GROUP_MEMBERS,
-+    RESP_NAME_LIST
- };
- 
- /* ==Sid2Name Extended Operation============================================= */
-@@ -366,8 +367,8 @@ static errno_t s2n_encode_request(TALLOC_CTX *mem_ctx,
-             break;
-         case BE_REQ_BY_CERT:
-             if (req_input->type == REQ_INP_CERT) {
--            ret = ber_printf(ber, "{ees}", INP_CERT, request_type,
--                                           req_input->inp.cert);
-+                ret = ber_printf(ber, "{ees}", INP_CERT, request_type,
-+                                               req_input->inp.cert);
-             } else {
-                 DEBUG(SSSDBG_OP_FAILURE, "Unexpected input type [%d].\n",
-                                           req_input->type);
-@@ -463,6 +464,11 @@ done:
-  * GroupMemberList ::= SEQUENCE OF OCTET STRING
-  */
- 
-+struct name_list {
-+    char *domain_name;
-+    char *name;
-+};
-+
- struct resp_attrs {
-     enum response_types response_type;
-     char *domain_name;
-@@ -475,6 +481,7 @@ struct resp_attrs {
-     size_t ngroups;
-     char **groups;
-     struct sysdb_attrs *sysdb_attrs;
-+    char **name_list;
- };
- 
- static errno_t get_extra_attrs(BerElement *ber, struct resp_attrs *resp_attrs)
-@@ -782,6 +789,9 @@ static errno_t s2n_response_to_attrs(TALLOC_CTX *mem_ctx,
-     struct resp_attrs *attrs = NULL;
-     char *sid_str;
-     bool is_v1 = false;
-+    char **name_list = NULL;
-+    ber_len_t ber_len;
-+    char *fq_name = NULL;
- 
-     if (retoid == NULL || retdata == NULL) {
-         DEBUG(SSSDBG_OP_FAILURE, "Missing OID or data.\n");
-@@ -947,6 +957,53 @@ static errno_t s2n_response_to_attrs(TALLOC_CTX *mem_ctx,
-                 goto done;
-             }
-             break;
-+        case RESP_NAME_LIST:
-+            tag = ber_scanf(ber, "{");
-+            if (tag == LBER_ERROR) {
-+                DEBUG(SSSDBG_OP_FAILURE, "ber_scanf failed.\n");
-+                ret = EINVAL;
-+                goto done;
-+            }
-+
-+            while (ber_peek_tag(ber, &ber_len) ==  LBER_SEQUENCE) {
-+                tag = ber_scanf(ber, "{aa}", &domain_name, &name);
-+                if (tag == LBER_ERROR) {
-+                    DEBUG(SSSDBG_OP_FAILURE, "ber_scanf failed.\n");
-+                    ret = EINVAL;
-+                    goto done;
-+                }
-+
-+                fq_name = sss_create_internal_fqname(attrs, name, domain_name);
-+                if (fq_name == NULL) {
-+                    DEBUG(SSSDBG_OP_FAILURE,
-+                          "sss_create_internal_fqname failed.\n");
-+                    ret = ENOMEM;
-+                    goto done;
-+                }
-+                DEBUG(SSSDBG_TRACE_ALL, "[%s][%s][%s].\n", domain_name, name,
-+                                                           fq_name);
-+
-+                ret = add_string_to_list(attrs, fq_name, &name_list);
-+                ber_memfree(domain_name);
-+                ber_memfree(name);
-+                talloc_free(fq_name);
-+                domain_name = NULL;
-+                name = NULL;
-+                fq_name = NULL;
-+                if (ret != EOK) {
-+                    DEBUG(SSSDBG_OP_FAILURE, "add_to_name_list failed.\n");
-+                    goto done;
-+                }
-+            }
-+
-+            tag = ber_scanf(ber, "}}");
-+            if (tag == LBER_ERROR) {
-+                DEBUG(SSSDBG_OP_FAILURE, "ber_scanf failed.\n");
-+                ret = EINVAL;
-+                goto done;
-+            }
-+            attrs->name_list = name_list;
-+            break;
-         default:
-             DEBUG(SSSDBG_OP_FAILURE, "Unexpected response type [%d].\n",
-                                       type);
-@@ -955,7 +1012,7 @@ static errno_t s2n_response_to_attrs(TALLOC_CTX *mem_ctx,
-     }
- 
-     attrs->response_type = type;
--    if (type != RESP_SID) {
-+    if (type != RESP_SID && type != RESP_NAME_LIST) {
-         attrs->domain_name = talloc_strdup(attrs, domain_name);
-         if (attrs->domain_name == NULL) {
-             DEBUG(SSSDBG_OP_FAILURE, "talloc_strdup failed.\n");
-@@ -969,6 +1026,7 @@ static errno_t s2n_response_to_attrs(TALLOC_CTX *mem_ctx,
- done:
-     ber_memfree(domain_name);
-     ber_memfree(name);
-+    talloc_free(fq_name);
-     ber_free(ber, 1);
- 
-     if (ret == EOK) {
-@@ -1332,6 +1390,7 @@ struct ipa_s2n_get_user_state {
-     struct resp_attrs *attrs;
-     struct resp_attrs *simple_attrs;
-     struct sysdb_attrs *override_attrs;
-+    struct sysdb_attrs *mapped_attrs;
-     int exop_timeout;
- };
- 
-@@ -1384,6 +1443,11 @@ struct tevent_req *ipa_s2n_get_acct_info_send(TALLOC_CTX *mem_ctx,
-         goto fail;
-     }
- 
-+    if (entry_type == BE_REQ_BY_CERT) {
-+        /* Only REQ_SIMPLE is supported for BE_REQ_BY_CERT */
-+        state->request_type = REQ_SIMPLE;
-+    }
-+
-     ret = s2n_encode_request(state, dom->name, entry_type, state->request_type,
-                              req_input, &bv_req);
-     if (ret != EOK) {
-@@ -1785,6 +1849,43 @@ static void ipa_s2n_get_user_done(struct tevent_req *subreq)
-             goto done;
-         }
- 
-+        if (state->simple_attrs->response_type == RESP_NAME_LIST
-+                && state->req_input->type == REQ_INP_CERT) {
-+            state->mapped_attrs = sysdb_new_attrs(state);
-+            if (state->mapped_attrs == NULL) {
-+                DEBUG(SSSDBG_OP_FAILURE, "sysdb_new_attrs failed.\n");
-+                ret = ENOMEM;
-+                goto done;
-+            }
-+
-+            ret = sysdb_attrs_add_base64_blob(state->mapped_attrs,
-+                                              SYSDB_USER_MAPPED_CERT,
-+                                              state->req_input->inp.cert);
-+            if (ret != EOK) {
-+                DEBUG(SSSDBG_OP_FAILURE, "sysdb_attrs_add_base64_blob failed.\n");
-+                goto done;
-+            }
-+
-+            subreq = ipa_s2n_get_list_send(state, state->ev,
-+                                           state->ipa_ctx, state->dom,
-+                                           state->sh, state->exop_timeout,
-+                                           BE_REQ_USER,
-+                                           REQ_FULL_WITH_MEMBERS,
-+                                           REQ_INP_NAME,
-+                                           state->simple_attrs->name_list,
-+                                           state->mapped_attrs);
-+            if (subreq == NULL) {
-+                DEBUG(SSSDBG_OP_FAILURE,
-+                      "ipa_s2n_get_list_send failed.\n");
-+                ret = ENOMEM;
-+                goto done;
-+            }
-+            tevent_req_set_callback(subreq, ipa_s2n_get_list_done,
-+                                    req);
-+
-+            return;
-+        }
-+
-         break;
-     default:
-         DEBUG(SSSDBG_CRIT_FAILURE, "Unexpected request type.\n");
--- 
-2.9.3
-
diff --git a/SOURCES/0059-NSS-Fix-covscan-warning.patch b/SOURCES/0059-NSS-Fix-covscan-warning.patch
new file mode 100644
index 0000000..2dfd5be
--- /dev/null
+++ b/SOURCES/0059-NSS-Fix-covscan-warning.patch
@@ -0,0 +1,54 @@
+From 2fd201a6e8f263f30fb3aeb3d7f826a06321e58e Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Fabiano=20Fid=C3=AAncio?= <fidencio@redhat.com>
+Date: Tue, 21 Nov 2017 16:12:24 +0100
+Subject: [PATCH 59/59] NSS: Fix covscan warning
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+ Error: NULL_RETURNS (CWE-476): [#def1]
+ sssd-1.16.1/src/responder/nss/nss_protocol.c:162: returned_null: "memchr" returns null (checked 7 out of 8 times).
+ sssd-1.16.1/src/responder/nss/nsssrv_mmap_cache.c:557: example_checked: Example 1: "memchr(t_key, 0, strs_offset + strs_len - name_ptr)" has its value checked in "memchr(t_key, 0, strs_offset + strs_len - name_ptr) == NULL".
+ sssd-1.16.1/src/sss_client/idmap/sss_nss_idmap.c:171: example_assign: Example 2: Assigning: "p" = return value from "memchr(p, 0, buf_len - (p - buf))".
+ sssd-1.16.1/src/sss_client/idmap/sss_nss_idmap.c:172: example_checked: Example 2 (cont.): "p" has its value checked in "p == NULL".
+ sssd-1.16.1/src/sss_client/nss_mc_group.c:157: example_checked: Example 3: "memchr(rec_name, 0, 16UL + data->strs_len - data->name)" has its value checked in "memchr(rec_name, 0, 16UL + data->strs_len - data->name) == NULL".
+ sssd-1.16.1/src/sss_client/nss_mc_initgr.c:139: example_checked: Example 4: "memchr(rec_name, 0, 24UL + data->data_len - data->name)" has its value checked in "memchr(rec_name, 0, 24UL + data->data_len - data->name) == NULL".
+ sssd-1.16.1/src/sss_client/nss_mc_passwd.c:150: example_checked: Example 5: "memchr(rec_name, 0, 16UL + data->strs_len - data->name)" has its value checked in "memchr(rec_name, 0, 16UL + data->strs_len - data->name) == NULL".
+ sssd-1.16.1/src/responder/nss/nss_protocol.c:162: var_assigned: Assigning: "p" = null return value from "memchr".
+ sssd-1.16.1/src/responder/nss/nss_protocol.c:176: dereference: Incrementing a pointer which might be null: "p".
+ #  174|       }
+ #  175|
+ #  176|->     p++;
+ #  177|       if ((p - body) + sizeof(uint32_t) != blen) {
+ #  178|           DEBUG(SSSDBG_CRIT_FAILURE, "Body has unexpected size!\n");
+
+Signed-off-by: Fabiano Fidêncio <fidencio@redhat.com>
+
+Reviewed-by: Lukáš Slebodník <lslebodn@redhat.com>
+Reviewed-by: Michal Židek <mzidek@redhat.com>
+(cherry picked from commit 1d88a0591ce8445ea3b6a88845a5997d61c915b4)
+---
+ src/responder/nss/nss_protocol.c | 7 +++++++
+ 1 file changed, 7 insertions(+)
+
+diff --git a/src/responder/nss/nss_protocol.c b/src/responder/nss/nss_protocol.c
+index 2655386498754c46fbb363bdd1f976f9ded6a434..13f6d1541b79bf5494e1560841f027bf98bef72b 100644
+--- a/src/responder/nss/nss_protocol.c
++++ b/src/responder/nss/nss_protocol.c
+@@ -160,6 +160,13 @@ nss_protocol_parse_name_ex(struct cli_ctx *cli_ctx, const char **_rawname,
+     }
+ 
+     p = memchr(body, '\0', blen);
++    /* Although body for sure is null terminated, let's add this check here
++     * so static analyzers are happier. */
++    if (p == NULL) {
++        DEBUG(SSSDBG_CRIT_FAILURE,
++              "memchr() returned NULL, body is not null terminated!\n");
++        return EINVAL;
++    }
+ 
+     /* If the body isn't valid UTF-8, fail */
+     if (!sss_utf8_check(body, (p - body))) {
+-- 
+2.14.3
+
diff --git a/SOURCES/0060-IPA-enable-AD-user-lookup-by-certificate.patch b/SOURCES/0060-IPA-enable-AD-user-lookup-by-certificate.patch
deleted file mode 100644
index e6ed62e..0000000
--- a/SOURCES/0060-IPA-enable-AD-user-lookup-by-certificate.patch
+++ /dev/null
@@ -1,30 +0,0 @@
-From 1f29c3d5302dc4ca9f5f9c6fe64dc8de5381041f Mon Sep 17 00:00:00 2001
-From: Sumit Bose <sbose@redhat.com>
-Date: Fri, 24 Mar 2017 15:41:37 +0100
-Subject: [PATCH 60/60] IPA: enable AD user lookup by certificate
-
-Without this the lookup by certificate for AD users on an IPA client
-will just error out.
-
-Related to https://pagure.io/SSSD/sssd/issue/3050
-
-Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
----
- src/providers/ipa/ipa_subdomains_id.c | 1 +
- 1 file changed, 1 insertion(+)
-
-diff --git a/src/providers/ipa/ipa_subdomains_id.c b/src/providers/ipa/ipa_subdomains_id.c
-index 4777d7cfd97fed39b807a659fd1f9000c7ff8625..3530af94ef59397db72465fcb0c4a03117a4d8bd 100644
---- a/src/providers/ipa/ipa_subdomains_id.c
-+++ b/src/providers/ipa/ipa_subdomains_id.c
-@@ -399,6 +399,7 @@ struct tevent_req *ipa_get_subdom_acct_send(TALLOC_CTX *memctx,
-         case BE_REQ_USER:
-         case BE_REQ_GROUP:
-         case BE_REQ_BY_SECID:
-+        case BE_REQ_BY_CERT:
-         case BE_REQ_USER_AND_GROUP:
-             ret = EOK;
-             break;
--- 
-2.9.3
-
diff --git a/SOURCES/0060-responder-Fix-talloc-hierarchy-in-sized_output_name.patch b/SOURCES/0060-responder-Fix-talloc-hierarchy-in-sized_output_name.patch
new file mode 100644
index 0000000..dc76425
--- /dev/null
+++ b/SOURCES/0060-responder-Fix-talloc-hierarchy-in-sized_output_name.patch
@@ -0,0 +1,59 @@
+From af95dd657586d3fc5680a7f8b493a5502640235e Mon Sep 17 00:00:00 2001
+From: Lukas Slebodnik <lslebodn@redhat.com>
+Date: Tue, 28 Nov 2017 12:19:54 +0100
+Subject: [PATCH 60/67] responder: Fix talloc hierarchy in sized_output_name
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+sized_output_name was a called with NULL context in
+memcache_delete_entry but returned data from sized_output_name
+didn't have proper talloc hierarchy and we could not release all
+all returned data.
+
+==00:01:01:29.871 10088== 934,414 bytes in 8,731 blocks are definitely lost in loss record 121 of 121
+==00:01:01:29.871 10088==    at 0x4C29BE3: malloc (vg_replace_malloc.c:299)
+==00:01:01:29.871 10088==    by 0x8FF4EAB: talloc_strdup (in /usr/lib64/libtalloc.so.2.1.9)
+==00:01:01:29.871 10088==    by 0x52933B9: sss_output_name (usertools.c:808)
+==00:01:01:29.871 10088==    by 0x5293550: sss_output_fqname (usertools.c:863)
+==00:01:01:29.871 10088==    by 0x1211F9: sized_output_name (responder_common.c:1708)
+==00:01:01:29.871 10088==    by 0x1137E6: memcache_delete_entry (nss_get_object.c:112)
+==00:01:01:29.871 10088==    by 0x113BB6: nss_get_object_done (nss_get_object.c:245)
+==00:01:01:29.871 10088==    by 0x8DE5291: _tevent_req_error (in /usr/lib64/libtevent.so.0.9.31)
+==00:01:01:29.871 10088==    by 0x1276CE: cache_req_done (cache_req.c:1047)
+==00:01:01:29.871 10088==    by 0x8DE5291: _tevent_req_error (in /usr/lib64/libtevent.so.0.9.31)
+==00:01:01:29.871 10088==    by 0x126AF6: cache_req_search_domains_done (cache_req.c:607)
+==00:01:01:29.871 10088==    by 0x8DE4AB9: tevent_common_loop_immediate (in /usr/lib64/libtevent.so.0.9.31)
+==00:01:01:29.871 10088==    by 0x8DE9C9C: ??? (in /usr/lib64/libtevent.so.0.9.31)
+==00:01:01:29.871 10088==    by 0x8DE82A6: ??? (in /usr/lib64/libtevent.so.0.9.31)
+==00:01:01:29.871 10088==    by 0x8DE40CC: _tevent_loop_once (in /usr/lib64/libtevent.so.0.9.31)
+==00:01:01:29.871 10088==    by 0x8DE42FA: tevent_common_loop_wait (in /usr/lib64/libtevent.so.0.9.31)
+==00:01:01:29.871 10088==    by 0x8DE8246: ??? (in /usr/lib64/libtevent.so.0.9.31)
+==00:01:01:29.871 10088==    by 0x5291B32: server_loop (server.c:718)
+==00:01:01:29.871 10088==    by 0x11004C: main (nsssrv.c:560)
+
+Resolves:
+https://pagure.io/SSSD/sssd/issue/3588
+
+Reviewed-by: Fabiano Fidêncio <fidencio@redhat.com>
+(cherry picked from commit 06e741a9bf23a18a998f366d9a8990b887a01638)
+---
+ src/responder/common/responder_common.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/src/responder/common/responder_common.c b/src/responder/common/responder_common.c
+index 6b4d2d9e5936c79944b6f883e9fe46fd03ff32f6..e1100ce4b1eaae8bc561246699dc9bacc96133c8 100644
+--- a/src/responder/common/responder_common.c
++++ b/src/responder/common/responder_common.c
+@@ -1815,7 +1815,7 @@ int sized_output_name(TALLOC_CTX *mem_ctx,
+         goto done;
+     }
+ 
+-    ret = sss_output_fqname(mem_ctx, name_dom, orig_name,
++    ret = sss_output_fqname(name, name_dom, orig_name,
+                             rctx->override_space, &name_str);
+     if (ret != EOK) {
+         goto done;
+-- 
+2.14.3
+
diff --git a/SOURCES/0061-CONFDB-Introduce-SSSD-domain-type-to-distinguish-POS.patch b/SOURCES/0061-CONFDB-Introduce-SSSD-domain-type-to-distinguish-POS.patch
deleted file mode 100644
index 31f541e..0000000
--- a/SOURCES/0061-CONFDB-Introduce-SSSD-domain-type-to-distinguish-POS.patch
+++ /dev/null
@@ -1,242 +0,0 @@
-From 75a8d8e7996c35fd9bef504f2f4d3e308b7553c8 Mon Sep 17 00:00:00 2001
-From: Jakub Hrozek <jhrozek@redhat.com>
-Date: Wed, 22 Mar 2017 12:53:17 +0100
-Subject: [PATCH 61/72] CONFDB: Introduce SSSD domain type to distinguish POSIX
- and application domains
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Related to:
-https://pagure.io/SSSD/sssd/issue/3310
-
-Adds a new option that allows to distinguish domains that do contain
-POSIX users and groups and those that don't. The POSIX domains are the
-default. The non-POSIX domains are selected by selecting an
-"application" type domain.
-
-Reviewed-by: Sumit Bose <sbose@redhat.com>
-Reviewed-by: Pavel Březina <pbrezina@redhat.com>
----
- src/confdb/confdb.c                  | 18 +++++++++++++++++-
- src/confdb/confdb.h                  | 15 +++++++++++++++
- src/config/SSSDConfig/__init__.py.in |  1 +
- src/config/SSSDConfigTest.py         |  2 ++
- src/config/cfg_rules.ini             |  1 +
- src/config/etc/sssd.api.conf         |  1 +
- src/man/sssd.conf.5.xml              | 33 +++++++++++++++++++++++++++++++++
- src/util/domain_info_utils.c         | 14 ++++++++++++++
- src/util/util.h                      |  1 +
- 9 files changed, 85 insertions(+), 1 deletion(-)
-
-diff --git a/src/confdb/confdb.c b/src/confdb/confdb.c
-index d82fd98ee02928b3c20df014528bd869ec946f92..70a1eb7b2c7e83dfa9d217a15c7d3d4c8580b891 100644
---- a/src/confdb/confdb.c
-+++ b/src/confdb/confdb.c
-@@ -1367,6 +1367,22 @@ static int confdb_get_domain_internal(struct confdb_ctx *cdb,
-         }
-     }
- 
-+    domain->type = DOM_TYPE_POSIX;
-+    tmp = ldb_msg_find_attr_as_string(res->msgs[0],
-+                                      CONFDB_DOMAIN_TYPE,
-+                                      CONFDB_DOMAIN_TYPE_POSIX);
-+    if (tmp != NULL) {
-+        if (strcasecmp(tmp, CONFDB_DOMAIN_TYPE_POSIX) == 0) {
-+            domain->type = DOM_TYPE_POSIX;
-+        } else if (strcasecmp(tmp, CONFDB_DOMAIN_TYPE_APP) == 0) {
-+            domain->type = DOM_TYPE_APPLICATION;
-+        } else {
-+            DEBUG(SSSDBG_FATAL_FAILURE,
-+                  "Invalid value %s for [%s]\n", tmp, CONFDB_DOMAIN_TYPE);
-+            goto done;
-+        }
-+    }
-+
-     ret = get_entry_as_uint32(res->msgs[0], &domain->subdomain_refresh_interval,
-                               CONFDB_DOMAIN_SUBDOMAIN_REFRESH, 14400);
-     if (ret != EOK || domain->subdomain_refresh_interval == 0) {
-@@ -1444,7 +1460,7 @@ int confdb_get_domains(struct confdb_ctx *cdb,
-         if (ret) {
-             DEBUG(SSSDBG_FATAL_FAILURE,
-                   "Error (%d [%s]) retrieving domain [%s], skipping!\n",
--                      ret, sss_strerror(ret), domlist[i]);
-+                  ret, sss_strerror(ret), domlist[i]);
-             continue;
-         }
- 
-diff --git a/src/confdb/confdb.h b/src/confdb/confdb.h
-index 56a603652d6c8256735e7f8b125300ff7b254645..a4046610f3cdbdb832de8924bf4397fb0018f2db 100644
---- a/src/confdb/confdb.h
-+++ b/src/confdb/confdb.h
-@@ -209,6 +209,9 @@
- #define CONFDB_DOMAIN_OFFLINE_TIMEOUT "offline_timeout"
- #define CONFDB_DOMAIN_SUBDOMAIN_INHERIT "subdomain_inherit"
- #define CONFDB_DOMAIN_CACHED_AUTH_TIMEOUT "cached_auth_timeout"
-+#define CONFDB_DOMAIN_TYPE "domain_type"
-+#define CONFDB_DOMAIN_TYPE_POSIX "posix"
-+#define CONFDB_DOMAIN_TYPE_APP "application"
- 
- /* Local Provider */
- #define CONFDB_LOCAL_DEFAULT_SHELL   "default_shell"
-@@ -261,11 +264,23 @@ enum sss_domain_state {
-     DOM_INCONSISTENT,
- };
- 
-+/** Whether the domain only supports looking up POSIX entries */
-+enum sss_domain_type {
-+    /** This is the default domain type. It resolves only entries
-+     * with the full POSIX set of attributes
-+     */
-+    DOM_TYPE_POSIX,
-+    /** In this mode, entries are typically resolved only by name */
-+    DOM_TYPE_APPLICATION,
-+};
-+
- /**
-  * Data structure storing all of the basic features
-  * of a domain.
-  */
- struct sss_domain_info {
-+    enum sss_domain_type type;
-+
-     char *name;
-     char *conn_name;
-     char *provider;
-diff --git a/src/config/SSSDConfig/__init__.py.in b/src/config/SSSDConfig/__init__.py.in
-index e7fb7673d393d4f12910f355d3edf33f4390c1f1..806611b6076048c08ce08c772dbd3cea5fdd656c 100644
---- a/src/config/SSSDConfig/__init__.py.in
-+++ b/src/config/SSSDConfig/__init__.py.in
-@@ -148,6 +148,7 @@ option_strings = {
-     'selinux_provider' : _('SELinux provider'),
- 
-     # [domain]
-+    'domain_type' : _('Whether the domain is usable by the OS or by applications'),
-     'min_id' : _('Minimum user ID'),
-     'max_id' : _('Maximum user ID'),
-     'enumerate' : _('Enable enumerating all users/groups'),
-diff --git a/src/config/SSSDConfigTest.py b/src/config/SSSDConfigTest.py
-index 6899bf8ae04bf210546c8cbdba8235f094e23dc0..9b3175962c697e314b3d5d94c2bc5beda537b66e 100755
---- a/src/config/SSSDConfigTest.py
-+++ b/src/config/SSSDConfigTest.py
-@@ -510,6 +510,7 @@ class SSSDConfigTestSSSDDomain(unittest.TestCase):
-             'debug',
-             'debug_level',
-             'debug_timestamps',
-+            'domain_type',
-             'min_id',
-             'max_id',
-             'timeout',
-@@ -878,6 +879,7 @@ class SSSDConfigTestSSSDDomain(unittest.TestCase):
-             'debug',
-             'debug_level',
-             'debug_timestamps',
-+            'domain_type',
-             'min_id',
-             'max_id',
-             'timeout',
-diff --git a/src/config/cfg_rules.ini b/src/config/cfg_rules.ini
-index 41efcea552a82c5492a0d21a8d0797ee42cdc8c7..3c857236eaa55b313d176bc4bb479918163b60d5 100644
---- a/src/config/cfg_rules.ini
-+++ b/src/config/cfg_rules.ini
-@@ -311,6 +311,7 @@ option = subdomains_provider
- option = selinux_provider
- 
- # Options available to all domains
-+option = domain_type
- option = min_id
- option = max_id
- option = timeout
-diff --git a/src/config/etc/sssd.api.conf b/src/config/etc/sssd.api.conf
-index 6965028e1ca748f8b6677d9fc1faa66d5c307a0c..a38b24208f89e4502e41625c540ea9958d5bbffe 100644
---- a/src/config/etc/sssd.api.conf
-+++ b/src/config/etc/sssd.api.conf
-@@ -129,6 +129,7 @@ selinux_provider = str, None, false
- [domain]
- # Options available to all domains
- description = str, None, false
-+domain_type = str, None, false
- debug = int, None, false
- debug_level = int, None, false
- debug_timestamps = bool, None, false
-diff --git a/src/man/sssd.conf.5.xml b/src/man/sssd.conf.5.xml
-index 4fe13b85d511fb6a2ccc9b4de956710b05bc898c..9abcff84a95ea1b27e36845e830cc125fdc89f90 100644
---- a/src/man/sssd.conf.5.xml
-+++ b/src/man/sssd.conf.5.xml
-@@ -1512,6 +1512,39 @@ pam_account_locked_message = Account locked, please contact help desk.
-             <quote>[domain/<replaceable>NAME</replaceable>]</quote>
-             <variablelist>
-                 <varlistentry>
-+                    <term>domain_type (string)</term>
-+                    <listitem>
-+                        <para>
-+                            Specifies whether the domain is meant to be used
-+                            by POSIX-aware clients such as the Name Service Switch
-+                            or by applications that do not need POSIX data to be
-+                            present or generated. Only objects from POSIX domains
-+                            are available to the operating system interfaces and
-+                            utilities.
-+                        </para>
-+                        <para>
-+                            Allowed values for this option are <quote>posix</quote>
-+                            and <quote>application</quote>.
-+                        </para>
-+                        <para>
-+                            POSIX domains are reachable by all services. Application
-+                            domains are only reachable from the InfoPipe responder (see
-+                            <citerefentry>
-+                                <refentrytitle>sssd-ifp</refentrytitle>
-+                                <manvolnum>5</manvolnum>
-+                            </citerefentry>) and the PAM responder.
-+                        </para>
-+                        <para>
-+                            NOTE: The application domains are currently well tested with
-+                            <quote>id_provider=ldap</quote> only.
-+                        </para>
-+                        <para>
-+                            Default: posix
-+                        </para>
-+                    </listitem>
-+                </varlistentry>
-+
-+                <varlistentry>
-                     <term>min_id,max_id (integer)</term>
-                     <listitem>
-                         <para>
-diff --git a/src/util/domain_info_utils.c b/src/util/domain_info_utils.c
-index a7f118842aa8ba870143b2f2b425a3e3c0ea5a78..2af7852f03f89b61f5b9fd8a244e98fb27b7e6a2 100644
---- a/src/util/domain_info_utils.c
-+++ b/src/util/domain_info_utils.c
-@@ -885,3 +885,17 @@ char *subdomain_create_conf_path(TALLOC_CTX *mem_ctx,
-                            subdomain->parent->name,
-                            subdomain->name);
- }
-+
-+const char *sss_domain_type_str(struct sss_domain_info *dom)
-+{
-+    if (dom == NULL) {
-+        return "BUG: Invalid domain";
-+    }
-+    switch (dom->type) {
-+    case DOM_TYPE_POSIX:
-+        return "POSIX";
-+    case DOM_TYPE_APPLICATION:
-+        return "Application";
-+    }
-+    return "Unknown";
-+}
-diff --git a/src/util/util.h b/src/util/util.h
-index 2170c5fb7cffda3910d2b58e33ec7abe3ec4a7d4..436550f5078cc173b8ed8cb58836d366f813146b 100644
---- a/src/util/util.h
-+++ b/src/util/util.h
-@@ -539,6 +539,7 @@ enum sss_domain_state sss_domain_get_state(struct sss_domain_info *dom);
- void sss_domain_set_state(struct sss_domain_info *dom,
-                           enum sss_domain_state state);
- bool is_email_from_domain(const char *email, struct sss_domain_info *dom);
-+const char *sss_domain_type_str(struct sss_domain_info *dom);
- 
- struct sss_domain_info*
- sss_get_domain_by_sid_ldap_fallback(struct sss_domain_info *domain,
--- 
-2.9.3
-
diff --git a/SOURCES/0061-test_responder-Check-memory-leak-in-sized_output_nam.patch b/SOURCES/0061-test_responder-Check-memory-leak-in-sized_output_nam.patch
new file mode 100644
index 0000000..79da5fd
--- /dev/null
+++ b/SOURCES/0061-test_responder-Check-memory-leak-in-sized_output_nam.patch
@@ -0,0 +1,58 @@
+From d624420972d061f72b08727bd7b2e227ce047272 Mon Sep 17 00:00:00 2001
+From: Lukas Slebodnik <lslebodn@redhat.com>
+Date: Tue, 28 Nov 2017 12:20:26 +0100
+Subject: [PATCH 61/67] test_responder: Check memory leak in sized_output_name
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Resolves:
+https://pagure.io/SSSD/sssd/issue/3588
+
+Reviewed-by: Fabiano Fidêncio <fidencio@redhat.com>
+(cherry picked from commit 051e0fc7cc86fb4e4b3a9323a61684ad3a6fa589)
+---
+ src/tests/cmocka/test_responder_common.c | 20 ++++++++++++++++++++
+ 1 file changed, 20 insertions(+)
+
+diff --git a/src/tests/cmocka/test_responder_common.c b/src/tests/cmocka/test_responder_common.c
+index fb7e4ee500570319999e6e85ee14a05cddea8de3..5441167caeb284982ee76926117da029966ec997 100644
+--- a/src/tests/cmocka/test_responder_common.c
++++ b/src/tests/cmocka/test_responder_common.c
+@@ -316,6 +316,23 @@ void test_schedule_get_domains_task(void **state)
+     talloc_free(dummy_ncache_ptr);
+ }
+ 
++void test_sss_output_fqname(void **state)
++{
++    struct parse_inp_test_ctx *parse_inp_ctx = talloc_get_type(*state,
++                                                   struct parse_inp_test_ctx);
++    errno_t ret;
++    struct sized_string *res = NULL;
++
++    ret = sized_output_name(parse_inp_ctx, parse_inp_ctx->rctx, "dummy",
++                            parse_inp_ctx->tctx->dom, &res);
++    assert_int_equal(ret, EOK);
++    assert_non_null(res);
++    assert_string_equal("dummy", res->str);
++    assert_int_equal(6, res->len);
++
++    talloc_zfree(res);
++}
++
+ int main(int argc, const char *argv[])
+ {
+     int rv;
+@@ -346,6 +363,9 @@ int main(int argc, const char *argv[])
+         cmocka_unit_test_setup_teardown(test_schedule_get_domains_task,
+                                         parse_inp_test_setup,
+                                         parse_inp_test_teardown),
++        cmocka_unit_test_setup_teardown(test_sss_output_fqname,
++                                        parse_inp_test_setup,
++                                        parse_inp_test_teardown),
+     };
+ 
+     /* Set debug level to invalid value so we can deside if -d 0 was used. */
+-- 
+2.14.3
+
diff --git a/SOURCES/0062-CONFDB-Allow-configuring-application-sections-as-non.patch b/SOURCES/0062-CONFDB-Allow-configuring-application-sections-as-non.patch
deleted file mode 100644
index 24ecd56..0000000
--- a/SOURCES/0062-CONFDB-Allow-configuring-application-sections-as-non.patch
+++ /dev/null
@@ -1,531 +0,0 @@
-From 05ae58c86eae80c7e69fb809dc3cd89d0b7418f4 Mon Sep 17 00:00:00 2001
-From: Jakub Hrozek <jhrozek@redhat.com>
-Date: Mon, 27 Mar 2017 09:48:46 +0200
-Subject: [PATCH 62/72] CONFDB: Allow configuring [application] sections as
- non-POSIX domains
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Related to:
-https://pagure.io/SSSD/sssd/issue/3310
-
-Allows to add a new section:
-    [application/$name]
-
-This section internally (on the confdb level) expands to:
-    [domain/$name]
-    domain_type = application
-
-The reasons to add this new section is two-fold. One, to make the
-configuration of application domains more explicit and two, to make it
-possible to share configuration between two domains, one POSIX and one
-non-POSIX by application domain's inherit_from option:
-    [application/$name]
-    inherit_from = posix_domain_name
-
-Reviewed-by: Sumit Bose <sbose@redhat.com>
-Reviewed-by: Pavel Březina <pbrezina@redhat.com>
----
- src/confdb/confdb.c      | 288 ++++++++++++++++++++++++++++++++++++++++++++---
- src/confdb/confdb.h      |   4 +
- src/config/cfg_rules.ini |   9 +-
- src/man/sssd.conf.5.xml  |  77 +++++++++++++
- src/monitor/monitor.c    |   8 ++
- 5 files changed, 368 insertions(+), 18 deletions(-)
-
-diff --git a/src/confdb/confdb.c b/src/confdb/confdb.c
-index 70a1eb7b2c7e83dfa9d217a15c7d3d4c8580b891..88e114457deac3ca50c291a131122624fb6f6fe4 100644
---- a/src/confdb/confdb.c
-+++ b/src/confdb/confdb.c
-@@ -813,6 +813,50 @@ done:
-     return ret;
- }
- 
-+static int confdb_get_domain_section(TALLOC_CTX *mem_ctx,
-+                                     struct confdb_ctx *cdb,
-+                                     const char *section,
-+                                     const char *name,
-+                                     struct ldb_result **_res)
-+{
-+    TALLOC_CTX *tmp_ctx;
-+    int ret;
-+    struct ldb_result *res;
-+    struct ldb_dn *dn;
-+
-+    tmp_ctx = talloc_new(NULL);
-+    if (tmp_ctx == NULL) {
-+        return ENOMEM;
-+    }
-+
-+    dn = ldb_dn_new_fmt(tmp_ctx, cdb->ldb, "cn=%s,%s", name, section);
-+    if (dn == NULL) {
-+        ret = ENOMEM;
-+        goto done;
-+    }
-+
-+    ret = ldb_search(cdb->ldb, tmp_ctx, &res, dn,
-+                     LDB_SCOPE_BASE, NULL, NULL);
-+    if (ret != LDB_SUCCESS) {
-+        ret = sysdb_error_to_errno(ret);
-+        goto done;
-+    }
-+
-+    if (res->count == 0) {
-+        ret = ENOENT;
-+        goto done;
-+    } else if (res->count > 1) {
-+        ret = E2BIG;
-+        goto done;
-+    }
-+
-+    *_res = talloc_steal(mem_ctx, res);
-+    ret = EOK;
-+done:
-+    talloc_free(tmp_ctx);
-+    return ret;
-+}
-+
- static int confdb_get_domain_internal(struct confdb_ctx *cdb,
-                                       TALLOC_CTX *mem_ctx,
-                                       const char *name,
-@@ -821,7 +865,6 @@ static int confdb_get_domain_internal(struct confdb_ctx *cdb,
-     struct sss_domain_info *domain;
-     struct ldb_result *res;
-     TALLOC_CTX *tmp_ctx;
--    struct ldb_dn *dn;
-     const char *tmp;
-     int ret, val;
-     uint32_t entry_cache_timeout;
-@@ -833,23 +876,15 @@ static int confdb_get_domain_internal(struct confdb_ctx *cdb,
-     tmp_ctx = talloc_new(mem_ctx);
-     if (!tmp_ctx) return ENOMEM;
- 
--    dn = ldb_dn_new_fmt(tmp_ctx, cdb->ldb,
--                        "cn=%s,%s", name, CONFDB_DOMAIN_BASEDN);
--    if (!dn) {
--        ret = ENOMEM;
--        goto done;
--    }
--
--    ret = ldb_search(cdb->ldb, tmp_ctx, &res, dn,
--                     LDB_SCOPE_BASE, NULL, NULL);
--    if (ret != LDB_SUCCESS) {
--        ret = EIO;
--        goto done;
--    }
--
--    if (res->count != 1) {
-+    ret = confdb_get_domain_section(tmp_ctx, cdb, CONFDB_DOMAIN_BASEDN,
-+                                    name, &res);
-+    if (ret == ENOENT) {
-         DEBUG(SSSDBG_FATAL_FAILURE, "Unknown domain [%s]\n", name);
--        ret = ENOENT;
-+        goto done;
-+    } else if (ret != EOK) {
-+        DEBUG(SSSDBG_FATAL_FAILURE,
-+              "Error %d: %s while retrieving %s\n",
-+              ret, sss_strerror(ret), name);
-         goto done;
-     }
- 
-@@ -1841,3 +1876,222 @@ int confdb_ensure_files_domain(struct confdb_ctx *cdb,
-     return activate_files_domain(cdb, implicit_files_dom_name);
- #endif /* ADD_FILES_DOMAIN */
- }
-+
-+static int confdb_get_parent_domain(TALLOC_CTX *mem_ctx,
-+                                    const char *name,
-+                                    struct confdb_ctx *cdb,
-+                                    struct ldb_result *app_dom,
-+                                    struct ldb_result **_parent_dom)
-+{
-+    const char *inherit_from;
-+
-+    inherit_from = ldb_msg_find_attr_as_string(app_dom->msgs[0],
-+                                               CONFDB_DOMAIN_INHERIT_FROM, NULL);
-+    if (inherit_from == NULL) {
-+        DEBUG(SSSDBG_CONF_SETTINGS,
-+              "%s does not inherit from any POSIX domain\n", name);
-+        *_parent_dom = NULL;
-+        return EOK;
-+    }
-+
-+    return confdb_get_domain_section(mem_ctx, cdb,
-+                                     CONFDB_DOMAIN_BASEDN, inherit_from,
-+                                     _parent_dom);
-+}
-+
-+static int confdb_add_app_domain(TALLOC_CTX *mem_ctx,
-+                                 struct confdb_ctx *cdb,
-+                                 const char *name)
-+{
-+    char *cdb_path = NULL;
-+    const char *val[2] = { NULL, NULL };
-+    int ret;
-+
-+    cdb_path = talloc_asprintf(mem_ctx, CONFDB_DOMAIN_PATH_TMPL, name);
-+    if (cdb_path == NULL) {
-+    return ENOMEM;
-+    }
-+
-+    val[0] = CONFDB_DOMAIN_TYPE_APP;
-+    ret = confdb_add_param(cdb, true, cdb_path, CONFDB_DOMAIN_TYPE, val);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_CRIT_FAILURE, "Unable to add id_provider [%d]: %s\n",
-+              ret, sss_strerror(ret));
-+        return ret;
-+    }
-+
-+    return EOK;
-+}
-+
-+static int confdb_merge_parent_domain(const char *name,
-+                                      struct confdb_ctx *cdb,
-+                                      struct ldb_result *app_section)
-+{
-+    int ret;
-+    int ldb_flag;
-+    struct ldb_result *parent_domain = NULL;
-+    struct ldb_message *replace_msg = NULL;
-+    struct ldb_message *app_msg = NULL;
-+    struct ldb_dn *domain_dn;
-+    TALLOC_CTX *tmp_ctx = NULL;
-+
-+    tmp_ctx = talloc_new(NULL);
-+    if (tmp_ctx == NULL) {
-+        DEBUG(SSSDBG_CRIT_FAILURE, "talloc_new() failed\n");
-+        return ENOMEM;
-+    }
-+
-+    domain_dn = ldb_dn_new_fmt(tmp_ctx,
-+                               cdb->ldb,
-+                               "%s=%s,%s",
-+                               CONFDB_DOMAIN_ATTR,
-+                               name,
-+                               CONFDB_DOMAIN_BASEDN);
-+    if (domain_dn == NULL) {
-+        ret = ENOMEM;
-+        goto done;
-+    }
-+
-+    /* Copy the parent domain parameters */
-+    ret = confdb_get_parent_domain(tmp_ctx, name, cdb,
-+                                   app_section, &parent_domain);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_OP_FAILURE,
-+              "Cannot retrieve the parent domain [%d]: %s\n",
-+              ret, sss_strerror(ret));
-+        goto done;
-+    }
-+
-+    if (parent_domain != NULL) {
-+        replace_msg = ldb_msg_copy(tmp_ctx, parent_domain->msgs[0]);
-+        if (replace_msg == NULL) {
-+            ret = ENOMEM;
-+            goto done;
-+        }
-+        replace_msg->dn = domain_dn;
-+
-+        for (unsigned i = 0; i < replace_msg->num_elements; i++) {
-+            replace_msg->elements[i].flags = LDB_FLAG_MOD_ADD;
-+        }
-+
-+        ret = ldb_modify(cdb->ldb, replace_msg);
-+        if (ret != LDB_SUCCESS) {
-+            ret = sysdb_error_to_errno(ret);
-+            DEBUG(SSSDBG_OP_FAILURE,
-+                "Inheriting options from parent domain failed [%d]: %s\n",
-+                ret, sss_strerror(ret));
-+            goto done;
-+        }
-+    }
-+
-+    /* Finally, add any app-domain specific overrides */
-+    app_msg = ldb_msg_new(tmp_ctx);
-+    if (app_msg == NULL) {
-+        ret = ENOMEM;
-+        goto done;
-+    }
-+    app_msg->dn = domain_dn;
-+
-+    for (unsigned i = 0; i < app_section->msgs[0]->num_elements; i++) {
-+        struct ldb_message_element *el = NULL;
-+
-+        if (replace_msg != NULL) {
-+            el = ldb_msg_find_element(replace_msg,
-+                                      app_section->msgs[0]->elements[i].name);
-+            if (el == NULL) {
-+                /* Adding an element */
-+                ldb_flag = LDB_FLAG_MOD_ADD;
-+            } else {
-+                /* Overriding an element */
-+                ldb_flag = LDB_FLAG_MOD_REPLACE;
-+            }
-+        } else {
-+            /* If there was no domain to inherit from, just add all */
-+            ldb_flag = LDB_FLAG_MOD_ADD;
-+        }
-+
-+        ret = ldb_msg_add(app_msg,
-+                          &app_section->msgs[0]->elements[i],
-+                          ldb_flag);
-+        if (ret != EOK) {
-+            continue;
-+        }
-+    }
-+
-+    ret = ldb_modify(cdb->ldb, app_msg);
-+    if (ret != LDB_SUCCESS) {
-+        ret = sysdb_error_to_errno(ret);
-+        DEBUG(SSSDBG_OP_FAILURE,
-+              "Adding app-specific options failed [%d]: %s\n",
-+              ret, sss_strerror(ret));
-+        goto done;
-+    }
-+
-+    DEBUG(SSSDBG_TRACE_LIBS, "Added a domain section for %s\n", name);
-+    ret = EOK;
-+done:
-+    talloc_free(tmp_ctx);
-+    return ret;
-+}
-+
-+int confdb_expand_app_domains(struct confdb_ctx *cdb)
-+{
-+    int ret;
-+    char **domlist;
-+    TALLOC_CTX *tmp_ctx;
-+    struct ldb_result *app_domain = NULL;
-+
-+    tmp_ctx = talloc_new(NULL);
-+    if (tmp_ctx == NULL) {
-+        return ENOMEM;
-+    }
-+
-+    ret = confdb_get_string_as_list(cdb, tmp_ctx,
-+                                    CONFDB_MONITOR_CONF_ENTRY,
-+                                    CONFDB_MONITOR_ACTIVE_DOMAINS,
-+                                    &domlist);
-+    if (ret == ENOENT) {
-+        DEBUG(SSSDBG_FATAL_FAILURE, "No domains configured, fatal error!\n");
-+        goto done;
-+    } else if (ret != EOK ) {
-+        DEBUG(SSSDBG_FATAL_FAILURE, "Fatal error retrieving domains list!\n");
-+        goto done;
-+    }
-+
-+    for (int i = 0; domlist[i]; i++) {
-+        ret = confdb_get_domain_section(tmp_ctx, cdb,
-+                                        CONFDB_APP_DOMAIN_BASEDN, domlist[i],
-+                                        &app_domain);
-+        if (ret == ENOENT) {
-+            DEBUG(SSSDBG_TRACE_INTERNAL,
-+                  "%s is not an app domain\n", domlist[i]);
-+            continue;
-+        } else if (ret != EOK) {
-+            DEBUG(SSSDBG_FATAL_FAILURE,
-+                  "Error %d: %s while retrieving %s\n",
-+                  ret, sss_strerror(ret), domlist[i]);
-+            goto done;
-+        }
-+
-+        ret = confdb_add_app_domain(tmp_ctx, cdb, domlist[i]);
-+        if (ret != EOK) {
-+            DEBUG(SSSDBG_OP_FAILURE,
-+                  "Cannot add the app domain section [%d]: %s\n",
-+                  ret, sss_strerror(ret));
-+            goto done;
-+        }
-+
-+        ret = confdb_merge_parent_domain(domlist[i], cdb, app_domain);
-+        if (ret != EOK) {
-+            DEBUG(SSSDBG_OP_FAILURE,
-+                  "Cannot add options into the app domain section [%d]: %s\n",
-+                  ret, sss_strerror(ret));
-+            goto done;
-+        }
-+    }
-+
-+    ret = EOK;
-+done:
-+    talloc_free(tmp_ctx);
-+    return ret;
-+}
-diff --git a/src/confdb/confdb.h b/src/confdb/confdb.h
-index a4046610f3cdbdb832de8924bf4397fb0018f2db..5a8d377c312f641f544b1c7cf38826192462ea3c 100644
---- a/src/confdb/confdb.h
-+++ b/src/confdb/confdb.h
-@@ -164,6 +164,7 @@
- /* Domains */
- #define CONFDB_DOMAIN_PATH_TMPL "config/domain/%s"
- #define CONFDB_DOMAIN_BASEDN "cn=domain,cn=config"
-+#define CONFDB_APP_DOMAIN_BASEDN "cn=application,cn=config"
- #define CONFDB_DOMAIN_ID_PROVIDER "id_provider"
- #define CONFDB_DOMAIN_AUTH_PROVIDER "auth_provider"
- #define CONFDB_DOMAIN_ACCESS_PROVIDER "access_provider"
-@@ -212,6 +213,7 @@
- #define CONFDB_DOMAIN_TYPE "domain_type"
- #define CONFDB_DOMAIN_TYPE_POSIX "posix"
- #define CONFDB_DOMAIN_TYPE_APP "application"
-+#define CONFDB_DOMAIN_INHERIT_FROM "inherit_from"
- 
- /* Local Provider */
- #define CONFDB_LOCAL_DEFAULT_SHELL   "default_shell"
-@@ -398,6 +400,8 @@ int confdb_get_domains(struct confdb_ctx *cdb,
- int confdb_ensure_files_domain(struct confdb_ctx *cdb,
-                                const char *implicit_files_dom_name);
- 
-+int confdb_expand_app_domains(struct confdb_ctx *cdb);
-+
- /**
-  * Get a null-terminated linked-list of all domain names
-  * @param[in] mem_ctx The parent memory context for the value list
-diff --git a/src/config/cfg_rules.ini b/src/config/cfg_rules.ini
-index 3c857236eaa55b313d176bc4bb479918163b60d5..8fd2d2c5236246394353a88c50d1510bd6233f77 100644
---- a/src/config/cfg_rules.ini
-+++ b/src/config/cfg_rules.ini
-@@ -12,6 +12,7 @@ section = secrets
- section = kcm
- section_re = ^secrets/users/[0-9]\+$
- section_re = ^domain/.*$
-+section_re = ^application/.*$
- 
- [rule/allowed_sssd_options]
- validator = ini_allowed_options
-@@ -286,7 +287,7 @@ option = responder_idle_timeout
- 
- [rule/allowed_domain_options]
- validator = ini_allowed_options
--section_re = ^domain/.*$
-+section_re = ^(domain|application)/.*$
- 
- option = debug
- option = debug_level
-@@ -684,3 +685,9 @@ option = ldap_user_ssh_public_key
- option = ldap_user_uid_number
- option = ldap_user_uuid
- option = ldap_use_tokengroups
-+
-+[rule/allowed_application_options]
-+validator = ini_allowed_options
-+section_re = ^application/.*$
-+
-+option = inherit_from
-diff --git a/src/man/sssd.conf.5.xml b/src/man/sssd.conf.5.xml
-index 9abcff84a95ea1b27e36845e830cc125fdc89f90..8294793c765bfa6bf481693c7d7f206950454681 100644
---- a/src/man/sssd.conf.5.xml
-+++ b/src/man/sssd.conf.5.xml
-@@ -1539,6 +1539,10 @@ pam_account_locked_message = Account locked, please contact help desk.
-                             <quote>id_provider=ldap</quote> only.
-                         </para>
-                         <para>
-+                            For an easy way to configure a non-POSIX domains, please
-+                            see the <quote>Application domains</quote> section.
-+                        </para>
-+                        <para>
-                             Default: posix
-                         </para>
-                     </listitem>
-@@ -2692,6 +2696,79 @@ subdomain_inherit = ldap_purge_cache_timeout
-             </variablelist>
-         </para>
- 
-+        <refsect2 id='app_domains'>
-+            <title>Application domains</title>
-+            <para>
-+                SSSD, with its D-Bus interface (see
-+                <citerefentry>
-+                    <refentrytitle>sssd-ifp</refentrytitle>
-+                    <manvolnum>5</manvolnum>
-+                </citerefentry>) is appealing to applications
-+                as a gateway to an LDAP directory where users and groups
-+                are stored. However, contrary to the traditional SSSD
-+                deployment where all users and groups either have POSIX
-+                attributes or those attributes can be inferred from the
-+                Windows SIDs, in many cases the users and groups in the
-+                application support scenario have no POSIX attributes.
-+                Instead of setting a
-+                <quote>[domain/<replaceable>NAME</replaceable>]</quote>
-+                section, the administrator can set up an
-+                <quote>[application/<replaceable>NAME</replaceable>]</quote>
-+                section that internally represents a domain with type
-+                <quote>application</quote> optionally inherits settings
-+                from a tradition SSSD domain.
-+            </para>
-+            <para>
-+                Please note that the application domain must still be
-+                explicitly enabled in the <quote>domains</quote> parameter
-+                so that the lookup order between the application domain
-+                and its POSIX sibling domain is set correctly.
-+            </para>
-+            <variablelist>
-+                <title>Application domain parameters</title>
-+                <varlistentry>
-+                    <term>inherit_from (string)</term>
-+                    <listitem>
-+                        <para>
-+                            The SSSD POSIX-type domain the application
-+                            domain inherits all settings from. The
-+                            application domain can moreover add its own
-+                            settings to the application settings that augment
-+                            or override the <quote>sibling</quote>
-+                            domain settings.
-+                        </para>
-+                        <para>
-+                            Default: Not set
-+                        </para>
-+                    </listitem>
-+                </varlistentry>
-+            </variablelist>
-+            <para>
-+                The following example illustrates the use of an application
-+                domain. In this setup, the POSIX domain is connected to an LDAP
-+                server and is used by the OS through the NSS responder. In addition,
-+                the application domains also requests the telephoneNumber attribute,
-+                stores it as the phone attribute in the cache and makes the phone
-+                attribute reachable through the D-Bus interface.
-+            </para>
-+<programlisting>
-+[sssd]
-+domains = appdom, posixdom
-+
-+[ifp]
-+user_attributes = +phone
-+
-+[domain/posixdom]
-+id_provider = ldap
-+ldap_uri = ldap://ldap.example.com
-+ldap_search_base = dc=example,dc=com
-+
-+[application/appdom]
-+inherit_from = posixdom
-+ldap_user_extra_attrs = phone:telephoneNumber
-+</programlisting>
-+        </refsect2>
-+
-         <refsect2 id='local_domain'>
-             <title>The local domain section</title>
-             <para>
-diff --git a/src/monitor/monitor.c b/src/monitor/monitor.c
-index 7e7b5a07d11aecf1c0b11592213b90d385fd5076..2753b46667f7ae0b022776862c67a327d3356d6d 100644
---- a/src/monitor/monitor.c
-+++ b/src/monitor/monitor.c
-@@ -1064,6 +1064,14 @@ static int get_monitor_config(struct mt_ctx *ctx)
-         /* Not fatal */
-     }
- 
-+    ret = confdb_expand_app_domains(ctx->cdb);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_FATAL_FAILURE, "Failed to expand application domains\n");
-+        /* This must not be fatal so that SSSD keeps running and lets
-+         * admin correct the error.
-+         */
-+    }
-+
-     ret = confdb_get_domains(ctx->cdb, &ctx->domains);
-     if (ret != EOK) {
-         DEBUG(SSSDBG_FATAL_FAILURE, "No domains configured.\n");
--- 
-2.9.3
-
diff --git a/SOURCES/0062-UTIL-add-find_domain_by_object_name_ex.patch b/SOURCES/0062-UTIL-add-find_domain_by_object_name_ex.patch
new file mode 100644
index 0000000..321cedc
--- /dev/null
+++ b/SOURCES/0062-UTIL-add-find_domain_by_object_name_ex.patch
@@ -0,0 +1,82 @@
+From ab9a8db7539bea30effe398d9bd82b1ecadd8a6f Mon Sep 17 00:00:00 2001
+From: Sumit Bose <sbose@redhat.com>
+Date: Mon, 20 Nov 2017 12:08:30 +0100
+Subject: [PATCH 62/67] UTIL: add find_domain_by_object_name_ex()
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+The _ex version of find_domain_by_object_name() has a additional option
+'strict'. If set to 'true' NULL is return instead to domain from the
+first argument. This way the caller can see if the provider object name
+really contains a known domain.
+
+Related to https://pagure.io/SSSD/sssd/issue/3579
+
+Reviewed-by: Fabiano Fidêncio <fidencio@redhat.com>
+(cherry picked from commit b6d3da6cfe78c6d0ddb854088bc23e293b336401)
+---
+ src/util/domain_info_utils.c | 17 ++++++++++++++---
+ src/util/util.h              |  4 ++++
+ 2 files changed, 18 insertions(+), 3 deletions(-)
+
+diff --git a/src/util/domain_info_utils.c b/src/util/domain_info_utils.c
+index 3a3f5130a32e2c5fe4b81819bf2de697a4474111..66077092a40111967a98b0937506d9e4472f50d5 100644
+--- a/src/util/domain_info_utils.c
++++ b/src/util/domain_info_utils.c
+@@ -174,8 +174,8 @@ sss_get_domain_by_sid_ldap_fallback(struct sss_domain_info *domain,
+ }
+ 
+ struct sss_domain_info *
+-find_domain_by_object_name(struct sss_domain_info *domain,
+-                           const char *object_name)
++find_domain_by_object_name_ex(struct sss_domain_info *domain,
++                              const char *object_name, bool strict)
+ {
+     TALLOC_CTX *tmp_ctx;
+     struct sss_domain_info *dom = NULL;
+@@ -197,7 +197,11 @@ find_domain_by_object_name(struct sss_domain_info *domain,
+     }
+ 
+     if (domainname == NULL) {
+-        dom = domain;
++        if (strict) {
++            dom = NULL;
++        } else {
++            dom = domain;
++        }
+     } else {
+         dom = find_domain_by_name(domain, domainname, true);
+     }
+@@ -207,6 +211,13 @@ done:
+     return dom;
+ }
+ 
++struct sss_domain_info *
++find_domain_by_object_name(struct sss_domain_info *domain,
++                           const char *object_name)
++{
++    return find_domain_by_object_name_ex(domain, object_name, false);
++}
++
+ errno_t sssd_domain_init(TALLOC_CTX *mem_ctx,
+                          struct confdb_ctx *cdb,
+                          const char *domain_name,
+diff --git a/src/util/util.h b/src/util/util.h
+index 37383011763a9a2a3c2c066215e3ed94aca77308..2521b1789b0b8701b1fbcce33890eedb7fe18d5e 100644
+--- a/src/util/util.h
++++ b/src/util/util.h
+@@ -551,6 +551,10 @@ struct sss_domain_info *
+ find_domain_by_object_name(struct sss_domain_info *domain,
+                            const char *object_name);
+ 
++struct sss_domain_info *
++find_domain_by_object_name_ex(struct sss_domain_info *domain,
++                              const char *object_name, bool strict);
++
+ bool subdomain_enumerates(struct sss_domain_info *parent,
+                           const char *sd_name);
+ 
+-- 
+2.14.3
+
diff --git a/SOURCES/0063-CACHE_REQ-Domain-type-selection-in-cache_req.patch b/SOURCES/0063-CACHE_REQ-Domain-type-selection-in-cache_req.patch
deleted file mode 100644
index 86b6c73..0000000
--- a/SOURCES/0063-CACHE_REQ-Domain-type-selection-in-cache_req.patch
+++ /dev/null
@@ -1,976 +0,0 @@
-From 5519295726bb2a0e88475e1d8deff0b8c0f65119 Mon Sep 17 00:00:00 2001
-From: Jakub Hrozek <jhrozek@redhat.com>
-Date: Fri, 24 Mar 2017 10:39:12 +0100
-Subject: [PATCH 63/72] CACHE_REQ: Domain type selection in cache_req
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Related to:
-    https://pagure.io/SSSD/sssd/issue/3310
-
-Adds a new enumeration cache_req_dom_type. It is a tri-state that
-allows the caller to select which domains can be contacted - either only
-POSIX, only application domains or any type.
-
-Not all plugins of cache_req have the new parameter added -- only those
-that are usable/useful in a non-POSIX environment. For example, it makes
-no sense to allow the selection for calls by ID because those are
-inherently POSIX-specific. Also, services or netgroups are supported
-only coming from POSIX domains.
-
-At the moment, the patch should not change any behaviour as all calls
-default to contacting POSIX domains only.
-
-Reviewed-by: Pavel Březina <pbrezina@redhat.com>
----
- src/responder/common/cache_req/cache_req.c         | 80 ++++++++++++++++++++--
- src/responder/common/cache_req/cache_req.h         | 19 +++++
- src/responder/common/cache_req/cache_req_private.h |  3 +
- .../cache_req/plugins/cache_req_enum_groups.c      |  4 +-
- .../common/cache_req/plugins/cache_req_enum_svc.c  |  3 +-
- .../cache_req/plugins/cache_req_enum_users.c       |  4 +-
- .../cache_req/plugins/cache_req_group_by_filter.c  |  5 +-
- .../cache_req/plugins/cache_req_group_by_id.c      |  4 +-
- .../cache_req/plugins/cache_req_group_by_name.c    |  5 +-
- .../cache_req/plugins/cache_req_host_by_name.c     |  4 +-
- .../plugins/cache_req_initgroups_by_name.c         |  5 +-
- .../cache_req/plugins/cache_req_netgroup_by_name.c |  4 +-
- .../cache_req/plugins/cache_req_object_by_id.c     |  4 +-
- .../cache_req/plugins/cache_req_object_by_name.c   |  4 +-
- .../cache_req/plugins/cache_req_object_by_sid.c    |  4 +-
- .../cache_req/plugins/cache_req_svc_by_name.c      |  4 +-
- .../cache_req/plugins/cache_req_svc_by_port.c      |  4 +-
- .../cache_req/plugins/cache_req_user_by_cert.c     |  4 +-
- .../cache_req/plugins/cache_req_user_by_filter.c   |  5 +-
- .../cache_req/plugins/cache_req_user_by_id.c       |  4 +-
- .../cache_req/plugins/cache_req_user_by_name.c     |  9 ++-
- src/responder/ifp/ifp_groups.c                     | 14 +++-
- src/responder/ifp/ifp_users.c                      | 19 +++--
- src/responder/ifp/ifpsrv_cmd.c                     |  3 +-
- src/responder/nss/nss_enum.c                       |  2 +-
- src/responder/nss/nss_get_object.c                 |  3 +-
- src/responder/pam/pamsrv_cmd.c                     |  5 +-
- src/responder/sudo/sudosrv_get_sudorules.c         |  3 +-
- src/tests/cmocka/test_responder_cache_req.c        | 62 ++++++++++++++---
- 29 files changed, 246 insertions(+), 47 deletions(-)
-
-diff --git a/src/responder/common/cache_req/cache_req.c b/src/responder/common/cache_req/cache_req.c
-index 483126396f8addbad744ae03bfc739801cd0c18b..3a5fecf34427437bbf95317e05c5bd8b07b4537d 100644
---- a/src/responder/common/cache_req/cache_req.c
-+++ b/src/responder/common/cache_req/cache_req.c
-@@ -89,12 +89,31 @@ static errno_t cache_req_set_plugin(struct cache_req *cr,
-     return EOK;
- }
- 
-+static const char *
-+cache_req_dom_type_as_str(struct cache_req *cr)
-+{
-+    if (cr == NULL) {
-+        return "BUG: Invalid cache_req pointer\n";
-+    }
-+    switch (cr->req_dom_type) {
-+    case CACHE_REQ_POSIX_DOM:
-+        return "POSIX-only";
-+    case CACHE_REQ_APPLICATION_DOM:
-+        return "Application-only";
-+    case CACHE_REQ_ANY_DOM:
-+        return "Any";
-+    }
-+
-+    return "Unknown";
-+}
-+
- static struct cache_req *
- cache_req_create(TALLOC_CTX *mem_ctx,
-                  struct resp_ctx *rctx,
-                  struct cache_req_data *data,
-                  struct sss_nc_ctx *ncache,
--                 int midpoint)
-+                 int midpoint,
-+                 enum cache_req_dom_type req_dom_type)
- {
-     struct cache_req *cr;
-     errno_t ret;
-@@ -108,6 +127,7 @@ cache_req_create(TALLOC_CTX *mem_ctx,
-     cr->data = data;
-     cr->ncache = ncache;
-     cr->midpoint = midpoint;
-+    cr->req_dom_type = req_dom_type;
-     cr->req_start = time(NULL);
- 
-     /* It is perfectly fine to just overflow here. */
-@@ -145,8 +165,8 @@ cache_req_set_name(struct cache_req *cr, const char *name)
- }
- 
- static bool
--cache_req_validate_domain(struct cache_req *cr,
--                          struct sss_domain_info *domain)
-+cache_req_validate_domain_enumeration(struct cache_req *cr,
-+                                      struct sss_domain_info *domain)
- {
-     if (!cr->plugin->require_enumeration) {
-         return true;
-@@ -164,6 +184,52 @@ cache_req_validate_domain(struct cache_req *cr,
-     return true;
- }
- 
-+static bool
-+cache_req_validate_domain_type(struct cache_req *cr,
-+                               struct sss_domain_info *domain)
-+{
-+    bool valid = false;
-+
-+    switch (cr->req_dom_type) {
-+    case CACHE_REQ_POSIX_DOM:
-+        valid = domain->type == DOM_TYPE_POSIX ? true : false;
-+        break;
-+    case CACHE_REQ_APPLICATION_DOM:
-+        valid = domain->type == DOM_TYPE_APPLICATION ? true : false;
-+        break;
-+    case CACHE_REQ_ANY_DOM:
-+        valid = true;
-+        break;
-+    }
-+
-+    DEBUG(SSSDBG_TRACE_INTERNAL,
-+          "Request type %s for domain %s type %s is %svalid\n",
-+          cache_req_dom_type_as_str(cr),
-+          domain->name,
-+          sss_domain_type_str(domain),
-+          valid ? "" : "not ");
-+    return valid;
-+}
-+
-+static bool
-+cache_req_validate_domain(struct cache_req *cr,
-+                          struct sss_domain_info *domain)
-+{
-+    bool ok;
-+
-+    ok = cache_req_validate_domain_enumeration(cr, domain);
-+    if (ok == false) {
-+        return false;
-+    }
-+
-+    ok = cache_req_validate_domain_type(cr, domain);
-+    if (ok == false) {
-+        return false;
-+    }
-+
-+    return true;
-+}
-+
- static errno_t
- cache_req_is_well_known_object(TALLOC_CTX *mem_ctx,
-                                struct cache_req *cr,
-@@ -651,6 +717,7 @@ struct tevent_req *cache_req_send(TALLOC_CTX *mem_ctx,
-                                   struct resp_ctx *rctx,
-                                   struct sss_nc_ctx *ncache,
-                                   int midpoint,
-+                                  enum cache_req_dom_type req_dom_type,
-                                   const char *domain,
-                                   struct cache_req_data *data)
- {
-@@ -667,7 +734,8 @@ struct tevent_req *cache_req_send(TALLOC_CTX *mem_ctx,
-     }
- 
-     state->ev = ev;
--    state->cr = cr = cache_req_create(state, rctx, data, ncache, midpoint);
-+    state->cr = cr = cache_req_create(state, rctx, data,
-+                                      ncache, midpoint, req_dom_type);
-     if (state->cr == NULL) {
-         ret = ENOMEM;
-         goto done;
-@@ -952,13 +1020,15 @@ cache_req_steal_data_and_send(TALLOC_CTX *mem_ctx,
-                               struct resp_ctx *rctx,
-                               struct sss_nc_ctx *ncache,
-                               int cache_refresh_percent,
-+                              enum cache_req_dom_type req_dom_type,
-                               const char *domain,
-                               struct cache_req_data *data)
- {
-     struct tevent_req *req;
- 
-     req = cache_req_send(mem_ctx, ev, rctx, ncache,
--                         cache_refresh_percent, domain, data);
-+                         cache_refresh_percent,
-+                         req_dom_type, domain, data);
-     if (req == NULL) {
-         talloc_zfree(data);
-         return NULL;
-diff --git a/src/responder/common/cache_req/cache_req.h b/src/responder/common/cache_req/cache_req.h
-index d0e5ff43921467fc191fd5cc7d5b49cc039b7f67..c04b2fba6f0445dcfcc9cfe1b5963ac975c39118 100644
---- a/src/responder/common/cache_req/cache_req.h
-+++ b/src/responder/common/cache_req/cache_req.h
-@@ -57,6 +57,18 @@ enum cache_req_type {
-     CACHE_REQ_SENTINEL
- };
- 
-+/* Whether to limit the request type to a certain domain type
-+ * (POSIX/non-POSIX)
-+ */
-+enum cache_req_dom_type {
-+    /* Only look up data in POSIX domains */
-+    CACHE_REQ_POSIX_DOM,
-+    /* Only look up data in application domains */
-+    CACHE_REQ_APPLICATION_DOM,
-+    /* Look up data in any domain type */
-+    CACHE_REQ_ANY_DOM
-+};
-+
- /* Input data. */
- 
- struct cache_req_data;
-@@ -172,6 +184,7 @@ struct tevent_req *cache_req_send(TALLOC_CTX *mem_ctx,
-                                   struct resp_ctx *rctx,
-                                   struct sss_nc_ctx *ncache,
-                                   int midpoint,
-+                                  enum cache_req_dom_type req_dom_type,
-                                   const char *domain,
-                                   struct cache_req_data *data);
- 
-@@ -191,6 +204,7 @@ cache_req_user_by_name_send(TALLOC_CTX *mem_ctx,
-                             struct resp_ctx *rctx,
-                             struct sss_nc_ctx *ncache,
-                             int cache_refresh_percent,
-+                            enum cache_req_dom_type req_dom_type,
-                             const char *domain,
-                             const char *name);
- 
-@@ -228,6 +242,7 @@ cache_req_user_by_cert_send(TALLOC_CTX *mem_ctx,
-                             struct resp_ctx *rctx,
-                             struct sss_nc_ctx *ncache,
-                             int cache_refresh_percent,
-+                            enum cache_req_dom_type req_dom_type,
-                             const char *domain,
-                             const char *pem_cert);
- 
-@@ -240,6 +255,7 @@ cache_req_group_by_name_send(TALLOC_CTX *mem_ctx,
-                              struct resp_ctx *rctx,
-                              struct sss_nc_ctx *ncache,
-                              int cache_refresh_percent,
-+                             enum cache_req_dom_type req_dom_type,
-                              const char *domain,
-                              const char *name);
- 
-@@ -264,6 +280,7 @@ cache_req_initgr_by_name_send(TALLOC_CTX *mem_ctx,
-                               struct resp_ctx *rctx,
-                               struct sss_nc_ctx *ncache,
-                               int cache_refresh_percent,
-+                              enum cache_req_dom_type req_dom_type,
-                               const char *domain,
-                               const char *name);
- 
-@@ -274,6 +291,7 @@ struct tevent_req *
- cache_req_user_by_filter_send(TALLOC_CTX *mem_ctx,
-                               struct tevent_context *ev,
-                               struct resp_ctx *rctx,
-+                              enum cache_req_dom_type req_dom_type,
-                               const char *domain,
-                               const char *filter);
- 
-@@ -284,6 +302,7 @@ struct tevent_req *
- cache_req_group_by_filter_send(TALLOC_CTX *mem_ctx,
-                               struct tevent_context *ev,
-                               struct resp_ctx *rctx,
-+                              enum cache_req_dom_type req_dom_type,
-                               const char *domain,
-                               const char *filter);
- 
-diff --git a/src/responder/common/cache_req/cache_req_private.h b/src/responder/common/cache_req/cache_req_private.h
-index 2d3c1870795e4fd5667f603280edcee24f926220..851005c389f994b1bd2d04cda9b68df8b18492cc 100644
---- a/src/responder/common/cache_req/cache_req_private.h
-+++ b/src/responder/common/cache_req/cache_req_private.h
-@@ -42,6 +42,8 @@ struct cache_req {
-     struct sss_domain_info *domain;
-     bool cache_first;
-     bool bypass_cache;
-+    /* Only contact domains with this type */
-+    enum cache_req_dom_type req_dom_type;
- 
-     /* Debug information */
-     uint32_t reqid;
-@@ -108,6 +110,7 @@ cache_req_steal_data_and_send(TALLOC_CTX *mem_ctx,
-                               struct resp_ctx *rctx,
-                               struct sss_nc_ctx *ncache,
-                               int cache_refresh_percent,
-+                              enum cache_req_dom_type req_dom_type,
-                               const char *domain,
-                               struct cache_req_data *data);
- 
-diff --git a/src/responder/common/cache_req/plugins/cache_req_enum_groups.c b/src/responder/common/cache_req/plugins/cache_req_enum_groups.c
-index dbb40c98339cc9295e3678e05340396aff51ac78..49ce3508e678862e4389657187b9659ce90fbd1c 100644
---- a/src/responder/common/cache_req/plugins/cache_req_enum_groups.c
-+++ b/src/responder/common/cache_req/plugins/cache_req_enum_groups.c
-@@ -96,5 +96,7 @@ cache_req_enum_groups_send(TALLOC_CTX *mem_ctx,
-     }
- 
-     return cache_req_steal_data_and_send(mem_ctx, ev, rctx, ncache,
--                                         cache_refresh_percent, domain, data);
-+                                         cache_refresh_percent,
-+                                         CACHE_REQ_POSIX_DOM, domain,
-+                                         data);
- }
-diff --git a/src/responder/common/cache_req/plugins/cache_req_enum_svc.c b/src/responder/common/cache_req/plugins/cache_req_enum_svc.c
-index 28dea33c601f500b9c7af0de3eb9e1c342f03522..499b994738d62707b4e86d5a8383e3e2b82e8c57 100644
---- a/src/responder/common/cache_req/plugins/cache_req_enum_svc.c
-+++ b/src/responder/common/cache_req/plugins/cache_req_enum_svc.c
-@@ -97,5 +97,6 @@ cache_req_enum_svc_send(TALLOC_CTX *mem_ctx,
-     }
- 
-     return cache_req_steal_data_and_send(mem_ctx, ev, rctx, ncache,
--                                         cache_refresh_percent, domain, data);
-+                                         cache_refresh_percent,
-+                                         CACHE_REQ_POSIX_DOM, domain, data);
- }
-diff --git a/src/responder/common/cache_req/plugins/cache_req_enum_users.c b/src/responder/common/cache_req/plugins/cache_req_enum_users.c
-index 3b1a85841e3ed853cd329dfa9d762cb7a05cbd43..b635354be6e9d2e2e2af1a6f867ac68e6cf7f085 100644
---- a/src/responder/common/cache_req/plugins/cache_req_enum_users.c
-+++ b/src/responder/common/cache_req/plugins/cache_req_enum_users.c
-@@ -96,5 +96,7 @@ cache_req_enum_users_send(TALLOC_CTX *mem_ctx,
-     }
- 
-     return cache_req_steal_data_and_send(mem_ctx, ev, rctx, ncache,
--                                         cache_refresh_percent, domain, data);
-+                                         cache_refresh_percent,
-+                                         CACHE_REQ_POSIX_DOM, domain,
-+                                         data);
- }
-diff --git a/src/responder/common/cache_req/plugins/cache_req_group_by_filter.c b/src/responder/common/cache_req/plugins/cache_req_group_by_filter.c
-index 6ce6ae0d63967ac50b813a47ac938251619948da..4377a476c36e5e03c8533bc62335b84fa1cee3ff 100644
---- a/src/responder/common/cache_req/plugins/cache_req_group_by_filter.c
-+++ b/src/responder/common/cache_req/plugins/cache_req_group_by_filter.c
-@@ -140,6 +140,7 @@ struct tevent_req *
- cache_req_group_by_filter_send(TALLOC_CTX *mem_ctx,
-                                struct tevent_context *ev,
-                                struct resp_ctx *rctx,
-+                               enum cache_req_dom_type req_dom_type,
-                                const char *domain,
-                                const char *filter)
- {
-@@ -151,5 +152,7 @@ cache_req_group_by_filter_send(TALLOC_CTX *mem_ctx,
-     }
- 
-     return cache_req_steal_data_and_send(mem_ctx, ev, rctx, NULL,
--                                         0, domain, data);
-+                                         0,
-+                                         req_dom_type, domain,
-+                                         data);
- }
-diff --git a/src/responder/common/cache_req/plugins/cache_req_group_by_id.c b/src/responder/common/cache_req/plugins/cache_req_group_by_id.c
-index e98f76f8cd20742b81ae247df61db159d2584a17..ad5b7d890a42f29b586ab8e0943fef3dfab1162d 100644
---- a/src/responder/common/cache_req/plugins/cache_req_group_by_id.c
-+++ b/src/responder/common/cache_req/plugins/cache_req_group_by_id.c
-@@ -166,5 +166,7 @@ cache_req_group_by_id_send(TALLOC_CTX *mem_ctx,
-     }
- 
-     return cache_req_steal_data_and_send(mem_ctx, ev, rctx, ncache,
--                                         cache_refresh_percent, domain, data);
-+                                         cache_refresh_percent,
-+                                         CACHE_REQ_POSIX_DOM, domain,
-+                                         data);
- }
-diff --git a/src/responder/common/cache_req/plugins/cache_req_group_by_name.c b/src/responder/common/cache_req/plugins/cache_req_group_by_name.c
-index af6f23ccfd68f952027462ba3e74ed7219d04651..de1e8f9442273acf386a2278b06f28ee63a7e3c6 100644
---- a/src/responder/common/cache_req/plugins/cache_req_group_by_name.c
-+++ b/src/responder/common/cache_req/plugins/cache_req_group_by_name.c
-@@ -205,6 +205,7 @@ cache_req_group_by_name_send(TALLOC_CTX *mem_ctx,
-                              struct resp_ctx *rctx,
-                              struct sss_nc_ctx *ncache,
-                              int cache_refresh_percent,
-+                             enum cache_req_dom_type req_dom_type,
-                              const char *domain,
-                              const char *name)
- {
-@@ -216,5 +217,7 @@ cache_req_group_by_name_send(TALLOC_CTX *mem_ctx,
-     }
- 
-     return cache_req_steal_data_and_send(mem_ctx, ev, rctx, ncache,
--                                         cache_refresh_percent, domain, data);
-+                                         cache_refresh_percent,
-+                                         req_dom_type, domain,
-+                                         data);
- }
-diff --git a/src/responder/common/cache_req/plugins/cache_req_host_by_name.c b/src/responder/common/cache_req/plugins/cache_req_host_by_name.c
-index 77b46831fec3abc4126ef9d9be67221469801094..1171cd63fac5cc1d36b31bf8a069f059705cae90 100644
---- a/src/responder/common/cache_req/plugins/cache_req_host_by_name.c
-+++ b/src/responder/common/cache_req/plugins/cache_req_host_by_name.c
-@@ -117,5 +117,7 @@ cache_req_host_by_name_send(TALLOC_CTX *mem_ctx,
-     }
- 
-     return cache_req_steal_data_and_send(mem_ctx, ev, rctx, ncache,
--                                         cache_refresh_percent, domain, data);
-+                                         cache_refresh_percent,
-+                                         CACHE_REQ_POSIX_DOM, domain,
-+                                         data);
- }
-diff --git a/src/responder/common/cache_req/plugins/cache_req_initgroups_by_name.c b/src/responder/common/cache_req/plugins/cache_req_initgroups_by_name.c
-index 307b65a24282838b99c472b50a71f06865aed3f0..f100aefe5c92279cde7e3209c7f48f5e2b35f135 100644
---- a/src/responder/common/cache_req/plugins/cache_req_initgroups_by_name.c
-+++ b/src/responder/common/cache_req/plugins/cache_req_initgroups_by_name.c
-@@ -220,6 +220,7 @@ cache_req_initgr_by_name_send(TALLOC_CTX *mem_ctx,
-                               struct resp_ctx *rctx,
-                               struct sss_nc_ctx *ncache,
-                               int cache_refresh_percent,
-+                              enum cache_req_dom_type req_dom_type,
-                               const char *domain,
-                               const char *name)
- {
-@@ -231,5 +232,7 @@ cache_req_initgr_by_name_send(TALLOC_CTX *mem_ctx,
-     }
- 
-     return cache_req_steal_data_and_send(mem_ctx, ev, rctx, ncache,
--                                         cache_refresh_percent, domain, data);
-+                                         cache_refresh_percent,
-+                                         req_dom_type, domain,
-+                                         data);
- }
-diff --git a/src/responder/common/cache_req/plugins/cache_req_netgroup_by_name.c b/src/responder/common/cache_req/plugins/cache_req_netgroup_by_name.c
-index e49d6d84a41ce8dabf18c87373826f8e7b684bda..ab3e553d3ecb8ae09094dcfc938ed0ac01925327 100644
---- a/src/responder/common/cache_req/plugins/cache_req_netgroup_by_name.c
-+++ b/src/responder/common/cache_req/plugins/cache_req_netgroup_by_name.c
-@@ -150,5 +150,7 @@ cache_req_netgroup_by_name_send(TALLOC_CTX *mem_ctx,
-     }
- 
-     return cache_req_steal_data_and_send(mem_ctx, ev, rctx, ncache,
--                                         cache_refresh_percent, domain, data);
-+                                         cache_refresh_percent,
-+                                         CACHE_REQ_POSIX_DOM, domain,
-+                                         data);
- }
-diff --git a/src/responder/common/cache_req/plugins/cache_req_object_by_id.c b/src/responder/common/cache_req/plugins/cache_req_object_by_id.c
-index 046e313c83d1d4c75237b047be779201b8a5d3c0..9557bd15270b2eb1a0671f9ef91033efac29c3ac 100644
---- a/src/responder/common/cache_req/plugins/cache_req_object_by_id.c
-+++ b/src/responder/common/cache_req/plugins/cache_req_object_by_id.c
-@@ -134,5 +134,7 @@ cache_req_object_by_id_send(TALLOC_CTX *mem_ctx,
-     }
- 
-     return cache_req_steal_data_and_send(mem_ctx, ev, rctx, ncache,
--                                         cache_refresh_percent, domain, data);
-+                                         cache_refresh_percent,
-+                                         CACHE_REQ_POSIX_DOM, domain,
-+                                         data);
- }
-diff --git a/src/responder/common/cache_req/plugins/cache_req_object_by_name.c b/src/responder/common/cache_req/plugins/cache_req_object_by_name.c
-index 74d2b3dea287e890b38e4d5bb176ad2dc6337b7e..e236d1fa4aadcd87b192d34ebaf5f9ad8908b6c2 100644
---- a/src/responder/common/cache_req/plugins/cache_req_object_by_name.c
-+++ b/src/responder/common/cache_req/plugins/cache_req_object_by_name.c
-@@ -228,5 +228,7 @@ cache_req_object_by_name_send(TALLOC_CTX *mem_ctx,
-     }
- 
-     return cache_req_steal_data_and_send(mem_ctx, ev, rctx, ncache,
--                                         cache_refresh_percent, domain, data);
-+                                         cache_refresh_percent,
-+                                         CACHE_REQ_POSIX_DOM, domain,
-+                                         data);
- }
-diff --git a/src/responder/common/cache_req/plugins/cache_req_object_by_sid.c b/src/responder/common/cache_req/plugins/cache_req_object_by_sid.c
-index ab577663111cfd424e7f46308b2621af7f1ca264..dfec79da07d669165205a767cab22c2254686134 100644
---- a/src/responder/common/cache_req/plugins/cache_req_object_by_sid.c
-+++ b/src/responder/common/cache_req/plugins/cache_req_object_by_sid.c
-@@ -143,5 +143,7 @@ cache_req_object_by_sid_send(TALLOC_CTX *mem_ctx,
-     }
- 
-     return cache_req_steal_data_and_send(mem_ctx, ev, rctx, ncache,
--                                         cache_refresh_percent, domain, data);
-+                                         cache_refresh_percent,
-+                                         CACHE_REQ_POSIX_DOM, domain,
-+                                         data);
- }
-diff --git a/src/responder/common/cache_req/plugins/cache_req_svc_by_name.c b/src/responder/common/cache_req/plugins/cache_req_svc_by_name.c
-index ef13f097a8ae78ec9db5b7f6e14924b511578b34..b2bfb26ffed1a60ed8389fa89b0e728c8c6cf76c 100644
---- a/src/responder/common/cache_req/plugins/cache_req_svc_by_name.c
-+++ b/src/responder/common/cache_req/plugins/cache_req_svc_by_name.c
-@@ -175,5 +175,7 @@ cache_req_svc_by_name_send(TALLOC_CTX *mem_ctx,
-     }
- 
-     return cache_req_steal_data_and_send(mem_ctx, ev, rctx, ncache,
--                                         cache_refresh_percent, domain, data);
-+                                         cache_refresh_percent,
-+                                         CACHE_REQ_POSIX_DOM, domain,
-+                                         data);
- }
-diff --git a/src/responder/common/cache_req/plugins/cache_req_svc_by_port.c b/src/responder/common/cache_req/plugins/cache_req_svc_by_port.c
-index afa2eeeda12794de26e798aee4b88900bc87ed93..0e48437f4b64d26112be88af1eebc20f012b70fd 100644
---- a/src/responder/common/cache_req/plugins/cache_req_svc_by_port.c
-+++ b/src/responder/common/cache_req/plugins/cache_req_svc_by_port.c
-@@ -149,5 +149,7 @@ cache_req_svc_by_port_send(TALLOC_CTX *mem_ctx,
-     }
- 
-     return cache_req_steal_data_and_send(mem_ctx, ev, rctx, ncache,
--                                         cache_refresh_percent, domain, data);
-+                                         cache_refresh_percent,
-+                                         CACHE_REQ_POSIX_DOM, domain,
-+                                         data);
- }
-diff --git a/src/responder/common/cache_req/plugins/cache_req_user_by_cert.c b/src/responder/common/cache_req/plugins/cache_req_user_by_cert.c
-index f237c8d0fe3faf5aea553480f3f92eb279209a20..286a34db276e0098060982c572e2a68ceceebf60 100644
---- a/src/responder/common/cache_req/plugins/cache_req_user_by_cert.c
-+++ b/src/responder/common/cache_req/plugins/cache_req_user_by_cert.c
-@@ -105,6 +105,7 @@ cache_req_user_by_cert_send(TALLOC_CTX *mem_ctx,
-                             struct resp_ctx *rctx,
-                             struct sss_nc_ctx *ncache,
-                             int cache_refresh_percent,
-+                            enum cache_req_dom_type req_dom_type,
-                             const char *domain,
-                             const char *pem_cert)
- {
-@@ -117,5 +118,6 @@ cache_req_user_by_cert_send(TALLOC_CTX *mem_ctx,
- 
-     return cache_req_steal_data_and_send(mem_ctx, ev, rctx, ncache,
-                                          cache_refresh_percent,
--                                         domain, data);
-+                                         req_dom_type, domain,
-+                                         data);
- }
-diff --git a/src/responder/common/cache_req/plugins/cache_req_user_by_filter.c b/src/responder/common/cache_req/plugins/cache_req_user_by_filter.c
-index eb71b42dad3a805298df0c8425409d571befb31b..c476814373cd784bf8dbbea1da7b010afe5bb4e4 100644
---- a/src/responder/common/cache_req/plugins/cache_req_user_by_filter.c
-+++ b/src/responder/common/cache_req/plugins/cache_req_user_by_filter.c
-@@ -140,6 +140,7 @@ struct tevent_req *
- cache_req_user_by_filter_send(TALLOC_CTX *mem_ctx,
-                               struct tevent_context *ev,
-                               struct resp_ctx *rctx,
-+                              enum cache_req_dom_type req_dom_type,
-                               const char *domain,
-                               const char *filter)
- {
-@@ -151,5 +152,7 @@ cache_req_user_by_filter_send(TALLOC_CTX *mem_ctx,
-     }
- 
-     return cache_req_steal_data_and_send(mem_ctx, ev, rctx, NULL,
--                                         0, domain, data);
-+                                         0,
-+                                         req_dom_type, domain,
-+                                         data);
- }
-diff --git a/src/responder/common/cache_req/plugins/cache_req_user_by_id.c b/src/responder/common/cache_req/plugins/cache_req_user_by_id.c
-index fa783714b7c67ca029d18a223b64a3a69e3e6929..9ba73292e5dc518e86c6e00e7e493d6871f28e70 100644
---- a/src/responder/common/cache_req/plugins/cache_req_user_by_id.c
-+++ b/src/responder/common/cache_req/plugins/cache_req_user_by_id.c
-@@ -166,5 +166,7 @@ cache_req_user_by_id_send(TALLOC_CTX *mem_ctx,
-     }
- 
-     return cache_req_steal_data_and_send(mem_ctx, ev, rctx, ncache,
--                                         cache_refresh_percent, domain, data);
-+                                         cache_refresh_percent,
-+                                         CACHE_REQ_POSIX_DOM, domain,
-+                                         data);
- }
-diff --git a/src/responder/common/cache_req/plugins/cache_req_user_by_name.c b/src/responder/common/cache_req/plugins/cache_req_user_by_name.c
-index 0670febdce2d51e0373045570dd07f56255db7bc..15da7d0d20b1ac97511a226daecc8ef7e7d2e7e4 100644
---- a/src/responder/common/cache_req/plugins/cache_req_user_by_name.c
-+++ b/src/responder/common/cache_req/plugins/cache_req_user_by_name.c
-@@ -210,6 +210,7 @@ cache_req_user_by_name_send(TALLOC_CTX *mem_ctx,
-                             struct resp_ctx *rctx,
-                             struct sss_nc_ctx *ncache,
-                             int cache_refresh_percent,
-+                            enum cache_req_dom_type req_dom_type,
-                             const char *domain,
-                             const char *name)
- {
-@@ -221,7 +222,9 @@ cache_req_user_by_name_send(TALLOC_CTX *mem_ctx,
-     }
- 
-     return cache_req_steal_data_and_send(mem_ctx, ev, rctx, ncache,
--                                         cache_refresh_percent, domain, data);
-+                                         cache_refresh_percent,
-+                                         req_dom_type, domain,
-+                                         data);
- }
- 
- struct tevent_req *
-@@ -243,5 +246,7 @@ cache_req_user_by_name_attrs_send(TALLOC_CTX *mem_ctx,
-     }
- 
-     return cache_req_steal_data_and_send(mem_ctx, ev, rctx, ncache,
--                                         cache_refresh_percent, domain, data);
-+                                         cache_refresh_percent,
-+                                         CACHE_REQ_POSIX_DOM, domain,
-+                                         data);
- }
-diff --git a/src/responder/ifp/ifp_groups.c b/src/responder/ifp/ifp_groups.c
-index 94d1e84cc9de75727d3c47c0a5a24790d21ce132..99908e96bd971bce4b4e9064a77d8413f837d743 100644
---- a/src/responder/ifp/ifp_groups.c
-+++ b/src/responder/ifp/ifp_groups.c
-@@ -118,7 +118,9 @@ int ifp_groups_find_by_name(struct sbus_request *sbus_req,
-     }
- 
-     req = cache_req_group_by_name_send(sbus_req, ctx->rctx->ev, ctx->rctx,
--                                       ctx->rctx->ncache, 0, NULL, name);
-+                                       ctx->rctx->ncache, 0,
-+                                       CACHE_REQ_POSIX_DOM, NULL,
-+                                       name);
-     if (req == NULL) {
-         return ENOMEM;
-     }
-@@ -271,6 +273,7 @@ static int ifp_groups_list_by_name_step(struct ifp_list_ctx *list_ctx)
-     req = cache_req_group_by_filter_send(list_ctx,
-                                         list_ctx->ctx->rctx->ev,
-                                         list_ctx->ctx->rctx,
-+                                        CACHE_REQ_POSIX_DOM,
-                                         list_ctx->dom->name,
-                                         list_ctx->filter);
-     if (req == NULL) {
-@@ -355,7 +358,8 @@ int ifp_groups_list_by_domain_and_name(struct sbus_request *sbus_req,
-     }
- 
-     req = cache_req_group_by_filter_send(list_ctx, ctx->rctx->ev, ctx->rctx,
--                                        domain, filter);
-+                                         CACHE_REQ_POSIX_DOM,
-+                                         domain, filter);
-     if (req == NULL) {
-         return ENOMEM;
-     }
-@@ -522,7 +526,10 @@ static struct tevent_req *resolv_ghosts_send(TALLOC_CTX *mem_ctx,
-     }
- 
-     subreq = cache_req_group_by_name_send(state, ev, ctx->rctx,
--                                          ctx->rctx->ncache, 0, domain->name, name);
-+                                          ctx->rctx->ncache, 0,
-+                                          CACHE_REQ_POSIX_DOM,
-+                                          domain->name,
-+                                          name);
-     if (subreq == NULL) {
-         ret = ENOMEM;
-         goto immediately;
-@@ -601,6 +608,7 @@ errno_t resolv_ghosts_step(struct tevent_req *req)
- 
-     subreq = cache_req_user_by_name_send(state, state->ev, state->ctx->rctx,
-                                          state->ctx->rctx->ncache, 0,
-+                                         CACHE_REQ_POSIX_DOM,
-                                          state->domain->name,
-                                          state->ghosts[state->index]);
-     if (subreq == NULL) {
-diff --git a/src/responder/ifp/ifp_users.c b/src/responder/ifp/ifp_users.c
-index cc78300f31e863fcb5366e18a14abc597c6f7ddb..436bb268fa9c78d72fb744e0d338aa561a7d8764 100644
---- a/src/responder/ifp/ifp_users.c
-+++ b/src/responder/ifp/ifp_users.c
-@@ -99,7 +99,9 @@ int ifp_users_find_by_name(struct sbus_request *sbus_req,
-     }
- 
-     req = cache_req_user_by_name_send(sbus_req, ctx->rctx->ev, ctx->rctx,
--                                      ctx->rctx->ncache, 0, NULL, name);
-+                                      ctx->rctx->ncache, 0,
-+                                      CACHE_REQ_POSIX_DOM,
-+                                      NULL, name);
-     if (req == NULL) {
-         return ENOMEM;
-     }
-@@ -253,7 +255,9 @@ int ifp_users_find_by_cert(struct sbus_request *sbus_req, void *data,
-     }
- 
-     req = cache_req_user_by_cert_send(sbus_req, ctx->rctx->ev, ctx->rctx,
--                                      ctx->rctx->ncache, 0, NULL, derb64);
-+                                      ctx->rctx->ncache, 0,
-+                                      CACHE_REQ_POSIX_DOM, NULL,
-+                                      derb64);
-     if (req == NULL) {
-         return ENOMEM;
-     }
-@@ -367,6 +371,7 @@ static int ifp_users_list_by_cert_step(struct ifp_list_ctx *list_ctx)
-                                       list_ctx->ctx->rctx,
-                                       list_ctx->ctx->rctx->ncache,
-                                       0,
-+                                      CACHE_REQ_POSIX_DOM,
-                                       list_ctx->dom->name,
-                                       list_ctx->filter);
-     if (req == NULL) {
-@@ -532,7 +537,9 @@ int ifp_users_find_by_name_and_cert(struct sbus_request *sbus_req, void *data,
- 
-     if (name_and_cert_ctx->name != NULL) {
-         req = cache_req_user_by_name_send(sbus_req, ctx->rctx->ev, ctx->rctx,
--                                          ctx->rctx->ncache, 0, NULL,
-+                                          ctx->rctx->ncache, 0,
-+                                          CACHE_REQ_POSIX_DOM,
-+                                          NULL,
-                                           name_and_cert_ctx->name);
-         if (req == NULL) {
-             return ENOMEM;
-@@ -614,6 +621,7 @@ static int ifp_users_find_by_name_and_cert_step(
-                                       list_ctx->ctx->rctx,
-                                       list_ctx->ctx->rctx->ncache,
-                                       0,
-+                                      CACHE_REQ_POSIX_DOM,
-                                       list_ctx->dom->name,
-                                       list_ctx->filter);
-     if (req == NULL) {
-@@ -774,6 +782,7 @@ static int ifp_users_list_by_name_step(struct ifp_list_ctx *list_ctx)
-     req = cache_req_user_by_filter_send(list_ctx,
-                                         list_ctx->ctx->rctx->ev,
-                                         list_ctx->ctx->rctx,
-+                                        CACHE_REQ_POSIX_DOM,
-                                         list_ctx->dom->name,
-                                         list_ctx->filter);
-     if (req == NULL) {
-@@ -858,6 +867,7 @@ int ifp_users_list_by_domain_and_name(struct sbus_request *sbus_req,
-     }
- 
-     req = cache_req_user_by_filter_send(list_ctx, ctx->rctx->ev, ctx->rctx,
-+                                        CACHE_REQ_POSIX_DOM,
-                                         domain, filter);
-     if (req == NULL) {
-         return ENOMEM;
-@@ -1102,7 +1112,8 @@ int ifp_users_user_update_groups_list(struct sbus_request *sbus_req,
-     }
- 
-     req = cache_req_initgr_by_name_send(sbus_req, ctx->rctx->ev, ctx->rctx,
--                                        ctx->rctx->ncache, 0, domain->name,
-+                                        ctx->rctx->ncache, 0,
-+                                        CACHE_REQ_POSIX_DOM, domain->name,
-                                         username);
-     if (req == NULL) {
-         return ENOMEM;
-diff --git a/src/responder/ifp/ifpsrv_cmd.c b/src/responder/ifp/ifpsrv_cmd.c
-index 07edcddffa1091f8bbcf79a25962aadc791bb890..118b5083b14bf5692c6fdd7ba90668fe514aa89d 100644
---- a/src/responder/ifp/ifpsrv_cmd.c
-+++ b/src/responder/ifp/ifpsrv_cmd.c
-@@ -509,7 +509,8 @@ ifp_user_get_attr_lookup(struct tevent_req *subreq)
-     }
- 
-     subreq = cache_req_send(state, state->rctx->ev, state->rctx,
--                            state->ncache, 0, state->domname, data);
-+                            state->ncache, 0, CACHE_REQ_POSIX_DOM,
-+                            state->domname, data);
-     if (subreq == NULL) {
-         tevent_req_error(req, ENOMEM);
-         return;
-diff --git a/src/responder/nss/nss_enum.c b/src/responder/nss/nss_enum.c
-index b1cce2cde43ece735285c132d0127c142ee83cb0..aa7d8428f37e943a6b5904495c40ad4b8011b767 100644
---- a/src/responder/nss/nss_enum.c
-+++ b/src/responder/nss/nss_enum.c
-@@ -93,7 +93,7 @@ nss_setent_internal_send(TALLOC_CTX *mem_ctx,
-     /* Create new object. */
-     state->enum_ctx->is_ready = false;
-     subreq = cache_req_send(req, ev, cli_ctx->rctx, cli_ctx->rctx->ncache,
--                            0, NULL, data);
-+                            0, CACHE_REQ_POSIX_DOM, NULL, data);
-     if (subreq == NULL) {
-         DEBUG(SSSDBG_CRIT_FAILURE, "Unable to send cache request!\n");
-         ret = ENOMEM;
-diff --git a/src/responder/nss/nss_get_object.c b/src/responder/nss/nss_get_object.c
-index f83dd393c0e333d8914802e005672a15a51d5939..9058793ea2d72b57003a7219414af6a0f0c5b89e 100644
---- a/src/responder/nss/nss_get_object.c
-+++ b/src/responder/nss/nss_get_object.c
-@@ -190,7 +190,8 @@ nss_get_object_send(TALLOC_CTX *mem_ctx,
-     }
- 
-     subreq = cache_req_send(req, ev, cli_ctx->rctx, cli_ctx->rctx->ncache,
--                            state->nss_ctx->cache_refresh_percent, NULL, data);
-+                            state->nss_ctx->cache_refresh_percent,
-+                            CACHE_REQ_POSIX_DOM, NULL, data);
-     if (subreq == NULL) {
-         DEBUG(SSSDBG_CRIT_FAILURE, "Unable to send cache request!\n");
-         ret = ENOMEM;
-diff --git a/src/responder/pam/pamsrv_cmd.c b/src/responder/pam/pamsrv_cmd.c
-index ba2563c11885ff39681c2ef432acaedf26702b64..fa6d2cc10fe1404196f9d9221a469d7a9a768211 100644
---- a/src/responder/pam/pamsrv_cmd.c
-+++ b/src/responder/pam/pamsrv_cmd.c
-@@ -1315,7 +1315,9 @@ static void pam_forwarder_cert_cb(struct tevent_req *req)
- 
- 
-     req = cache_req_user_by_cert_send(preq, cctx->ev, cctx->rctx,
--                                      pctx->rctx->ncache, 0, NULL, cert);
-+                                      pctx->rctx->ncache, 0,
-+                                      CACHE_REQ_POSIX_DOM, NULL,
-+                                      cert);
-     if (req == NULL) {
-         DEBUG(SSSDBG_OP_FAILURE, "cache_req_user_by_cert_send failed.\n");
-         ret = ENOMEM;
-@@ -1507,6 +1509,7 @@ static int pam_check_user_search(struct pam_auth_req *preq)
-                            preq->cctx->rctx,
-                            preq->cctx->rctx->ncache,
-                            0,
-+                           CACHE_REQ_POSIX_DOM,
-                            preq->pd->domain,
-                            data);
-     if (!dpreq) {
-diff --git a/src/responder/sudo/sudosrv_get_sudorules.c b/src/responder/sudo/sudosrv_get_sudorules.c
-index 52dfd5c709e48f0d4610a4b384962fbc2869312b..cfdbfc9c9c66d96f774822d6a4d4aaaf1327abe3 100644
---- a/src/responder/sudo/sudosrv_get_sudorules.c
-+++ b/src/responder/sudo/sudosrv_get_sudorules.c
-@@ -644,7 +644,8 @@ struct tevent_req *sudosrv_get_rules_send(TALLOC_CTX *mem_ctx,
-     DEBUG(SSSDBG_TRACE_FUNC, "Running initgroups for [%s]\n", username);
- 
-     subreq = cache_req_initgr_by_name_send(state, ev, sudo_ctx->rctx,
--                                           sudo_ctx->rctx->ncache, 0, NULL,
-+                                           sudo_ctx->rctx->ncache, 0,
-+                                           CACHE_REQ_POSIX_DOM, NULL,
-                                            username);
-     if (subreq == NULL) {
-         ret = ENOMEM;
-diff --git a/src/tests/cmocka/test_responder_cache_req.c b/src/tests/cmocka/test_responder_cache_req.c
-index 5f1e5350eaf1d85de376c94731f0bd678f884a1d..80086232fd437876c2b190fb972c2ee3194d9efd 100644
---- a/src/tests/cmocka/test_responder_cache_req.c
-+++ b/src/tests/cmocka/test_responder_cache_req.c
-@@ -84,6 +84,28 @@ struct test_group {
-     talloc_free(req_mem_ctx);                                               \
- } while (0)
- 
-+#define run_cache_req_domtype(ctx, send_fn, done_fn, dom, crp, domtype, lookup, expret) do { \
-+    TALLOC_CTX *req_mem_ctx;                                                                 \
-+    struct tevent_req *req;                                                                  \
-+    errno_t ret;                                                                             \
-+                                                                                             \
-+    req_mem_ctx = talloc_new(global_talloc_context);                                         \
-+    check_leaks_push(req_mem_ctx);                                                           \
-+                                                                                             \
-+    req = send_fn(req_mem_ctx, ctx->tctx->ev, ctx->rctx,                                     \
-+                  ctx->ncache, crp,                                                          \
-+                  domtype,                                                                   \
-+                  (dom == NULL ? NULL : dom->name), lookup);                                 \
-+    assert_non_null(req);                                                                    \
-+    tevent_req_set_callback(req, done_fn, ctx);                                              \
-+                                                                                             \
-+    ret = test_ev_loop(ctx->tctx);                                                           \
-+    assert_int_equal(ret, expret);                                                           \
-+    assert_true(check_leaks_pop(req_mem_ctx));                                               \
-+                                                                                             \
-+    talloc_free(req_mem_ctx);                                                                \
-+} while (0)
-+
- struct cache_req_test_ctx {
-     struct sss_test_ctx *tctx;
-     struct resp_ctx *rctx;
-@@ -211,9 +233,11 @@ static void run_user_by_name(struct cache_req_test_ctx *test_ctx,
-                              int cache_refresh_percent,
-                              errno_t exp_ret)
- {
--    run_cache_req(test_ctx, cache_req_user_by_name_send,
--                  cache_req_user_by_name_test_done, domain,
--                  cache_refresh_percent, users[0].short_name, exp_ret);
-+    run_cache_req_domtype(test_ctx, cache_req_user_by_name_send,
-+                          cache_req_user_by_name_test_done, domain,
-+                          cache_refresh_percent,
-+                          CACHE_REQ_POSIX_DOM,
-+                          users[0].short_name, exp_ret);
- }
- 
- static void run_user_by_upn(struct cache_req_test_ctx *test_ctx,
-@@ -221,9 +245,11 @@ static void run_user_by_upn(struct cache_req_test_ctx *test_ctx,
-                             int cache_refresh_percent,
-                             errno_t exp_ret)
- {
--    run_cache_req(test_ctx, cache_req_user_by_name_send,
--                  cache_req_user_by_name_test_done, domain,
--                  cache_refresh_percent, users[0].upn, exp_ret);
-+    run_cache_req_domtype(test_ctx, cache_req_user_by_name_send,
-+                          cache_req_user_by_name_test_done, domain,
-+                          cache_refresh_percent,
-+                          CACHE_REQ_POSIX_DOM,
-+                          users[0].upn, exp_ret);
- }
- 
- static void run_user_by_id(struct cache_req_test_ctx *test_ctx,
-@@ -318,9 +344,11 @@ static void run_group_by_name(struct cache_req_test_ctx *test_ctx,
-                               int cache_refresh_percent,
-                               errno_t exp_ret)
- {
--    run_cache_req(test_ctx, cache_req_group_by_name_send,
--                  cache_req_group_by_name_test_done, domain,
--                  cache_refresh_percent, groups[0].short_name, exp_ret);
-+    run_cache_req_domtype(test_ctx, cache_req_group_by_name_send,
-+                          cache_req_group_by_name_test_done, domain,
-+                          cache_refresh_percent,
-+                          CACHE_REQ_POSIX_DOM,
-+                          groups[0].short_name, exp_ret);
- }
- 
- static void run_group_by_id(struct cache_req_test_ctx *test_ctx,
-@@ -605,7 +633,9 @@ void test_user_by_name_multiple_domains_parse(void **state)
-     check_leaks_push(req_mem_ctx);
- 
-     req = cache_req_user_by_name_send(req_mem_ctx, test_ctx->tctx->ev,
--                                      test_ctx->rctx, test_ctx->ncache, 0,
-+                                      test_ctx->rctx, test_ctx->ncache,
-+                                      CACHE_REQ_POSIX_DOM,
-+                                      0,
-                                       NULL, input_fqn);
-     assert_non_null(req);
-     tevent_req_set_callback(req, cache_req_user_by_name_test_done, test_ctx);
-@@ -1119,7 +1149,8 @@ void test_group_by_name_multiple_domains_parse(void **state)
- 
-     req = cache_req_group_by_name_send(req_mem_ctx, test_ctx->tctx->ev,
-                                        test_ctx->rctx, test_ctx->ncache, 0,
--                                       NULL, input_fqn);
-+                                       CACHE_REQ_POSIX_DOM, NULL,
-+                                       input_fqn);
-     assert_non_null(req);
-     tevent_req_set_callback(req, cache_req_group_by_name_test_done, test_ctx);
- 
-@@ -1421,6 +1452,7 @@ void test_user_by_recent_filter_valid(void **state)
-     /* User TEST_USER is created with a DP callback. */
-     req = cache_req_user_by_filter_send(req_mem_ctx, test_ctx->tctx->ev,
-                                         test_ctx->rctx,
-+                                        CACHE_REQ_POSIX_DOM,
-                                         test_ctx->tctx->dom->name,
-                                         TEST_USER_PREFIX);
-     assert_non_null(req);
-@@ -1463,6 +1495,7 @@ void test_users_by_recent_filter_valid(void **state)
-     /* User TEST_USER1 and TEST_USER2 are created with a DP callback. */
-     req = cache_req_user_by_filter_send(req_mem_ctx, test_ctx->tctx->ev,
-                                         test_ctx->rctx,
-+                                        CACHE_REQ_POSIX_DOM,
-                                         test_ctx->tctx->dom->name,
-                                         TEST_USER_PREFIX);
-     assert_non_null(req);
-@@ -1524,6 +1557,7 @@ void test_users_by_filter_filter_old(void **state)
- 
-     req = cache_req_user_by_filter_send(req_mem_ctx, test_ctx->tctx->ev,
-                                         test_ctx->rctx,
-+                                        CACHE_REQ_POSIX_DOM,
-                                         test_ctx->tctx->dom->name,
-                                         TEST_USER_PREFIX);
-     assert_non_null(req);
-@@ -1559,6 +1593,7 @@ void test_users_by_filter_notfound(void **state)
- 
-     req = cache_req_user_by_filter_send(req_mem_ctx, test_ctx->tctx->ev,
-                                         test_ctx->rctx,
-+                                        CACHE_REQ_POSIX_DOM,
-                                         test_ctx->tctx->dom->name,
-                                         "nosuchuser*");
-     assert_non_null(req);
-@@ -1592,6 +1627,7 @@ static void test_users_by_filter_multiple_domains_notfound(void **state)
- 
-     req = cache_req_user_by_filter_send(req_mem_ctx, test_ctx->tctx->ev,
-                                         test_ctx->rctx,
-+                                        CACHE_REQ_POSIX_DOM,
-                                         domain->name,
-                                         "nosuchuser*");
-     assert_non_null(req);
-@@ -1636,6 +1672,7 @@ void test_group_by_recent_filter_valid(void **state)
-     /* Group TEST_GROUP is created with a DP callback. */
-     req = cache_req_group_by_filter_send(req_mem_ctx, test_ctx->tctx->ev,
-                                          test_ctx->rctx,
-+                                         CACHE_REQ_POSIX_DOM,
-                                          test_ctx->tctx->dom->name,
-                                          TEST_USER_PREFIX);
-     assert_non_null(req);
-@@ -1680,6 +1717,7 @@ void test_groups_by_recent_filter_valid(void **state)
-     /* Group TEST_GROUP1 and TEST_GROUP2 are created with a DP callback. */
-     req = cache_req_group_by_filter_send(req_mem_ctx, test_ctx->tctx->ev,
-                                          test_ctx->rctx,
-+                                         CACHE_REQ_POSIX_DOM,
-                                          test_ctx->tctx->dom->name,
-                                          TEST_USER_PREFIX);
-     assert_non_null(req);
-@@ -1738,6 +1776,7 @@ void test_groups_by_filter_notfound(void **state)
- 
-     req = cache_req_group_by_filter_send(req_mem_ctx, test_ctx->tctx->ev,
-                                         test_ctx->rctx,
-+                                        CACHE_REQ_POSIX_DOM,
-                                         test_ctx->tctx->dom->name,
-                                         "nosuchgroup*");
-     assert_non_null(req);
-@@ -1770,6 +1809,7 @@ void test_groups_by_filter_multiple_domains_notfound(void **state)
- 
-     req = cache_req_group_by_filter_send(req_mem_ctx, test_ctx->tctx->ev,
-                                         test_ctx->rctx,
-+                                        CACHE_REQ_POSIX_DOM,
-                                         domain->name,
-                                         "nosuchgroup*");
-     assert_non_null(req);
--- 
-2.9.3
-
diff --git a/SOURCES/0063-ipa-handle-users-from-different-domains-in-ipa_resol.patch b/SOURCES/0063-ipa-handle-users-from-different-domains-in-ipa_resol.patch
new file mode 100644
index 0000000..68c09b2
--- /dev/null
+++ b/SOURCES/0063-ipa-handle-users-from-different-domains-in-ipa_resol.patch
@@ -0,0 +1,76 @@
+From d8d4e9fb842444eb3bd4e1a116fce00aba557707 Mon Sep 17 00:00:00 2001
+From: Sumit Bose <sbose@redhat.com>
+Date: Mon, 20 Nov 2017 12:04:50 +0100
+Subject: [PATCH 63/67] ipa: handle users from different domains in
+ ipa_resolve_user_list_send()
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Instead of assuming that all users in the list can be found in the
+provided domain with this patch the domain name part of the user name is
+preferred. The provided domain name is used as a fallback.
+
+Related to https://pagure.io/SSSD/sssd/issue/3579
+
+Reviewed-by: Fabiano Fidêncio <fidencio@redhat.com>
+(cherry picked from commit 7988988aab5bd0249476671b850eb3909aa753f8)
+---
+ src/providers/ipa/ipa_id.c | 20 ++++++++++++++++----
+ 1 file changed, 16 insertions(+), 4 deletions(-)
+
+diff --git a/src/providers/ipa/ipa_id.c b/src/providers/ipa/ipa_id.c
+index 8f8759f64b758aae7e45c88588e97a1bcf16ad79..2b4386584192d6b5ef0372099292ed73b77177bd 100644
+--- a/src/providers/ipa/ipa_id.c
++++ b/src/providers/ipa/ipa_id.c
+@@ -63,6 +63,8 @@ struct ipa_resolve_user_list_state {
+     struct ipa_id_ctx *ipa_ctx;
+     struct ldb_message_element *users;
+     const char *domain_name;
++    struct sss_domain_info *domain;
++    struct sss_domain_info *user_domain;
+     size_t user_idx;
+ 
+     int dp_error;
+@@ -91,6 +93,8 @@ ipa_resolve_user_list_send(TALLOC_CTX *memctx, struct tevent_context *ev,
+     state->ev = ev;
+     state->ipa_ctx = ipa_ctx;
+     state->domain_name = domain_name;
++    state->domain = find_domain_by_name(state->ipa_ctx->sdap_id_ctx->be->domain,
++                                        state->domain_name, true);
+     state->users = users;
+     state->user_idx = 0;
+     state->dp_error = DP_ERR_FATAL;
+@@ -132,8 +136,17 @@ static errno_t ipa_resolve_user_list_get_user_step(struct tevent_req *req)
+ 
+     DEBUG(SSSDBG_TRACE_ALL, "Trying to resolve user [%s].\n", ar->filter_value);
+ 
+-    if (strcasecmp(state->domain_name,
+-                   state->ipa_ctx->sdap_id_ctx->be->domain->name) != 0) {
++    state->user_domain = find_domain_by_object_name_ex(
++                                        state->ipa_ctx->sdap_id_ctx->be->domain,
++                                        ar->filter_value, true);
++    /* Use provided domain as as fallback is no known domain was found in the
++     * user name. */
++    if (state->user_domain == NULL) {
++        state->user_domain = state->domain;
++    }
++    ar->domain = state->user_domain->name;
++
++    if (state->user_domain != state->ipa_ctx->sdap_id_ctx->be->domain) {
+         subreq = ipa_subdomain_account_send(state, state->ev, state->ipa_ctx,
+                                             ar);
+     } else {
+@@ -158,8 +171,7 @@ static void ipa_resolve_user_list_get_user_done(struct tevent_req *subreq)
+                                             struct ipa_resolve_user_list_state);
+     int ret;
+ 
+-    if (strcasecmp(state->domain_name,
+-                   state->ipa_ctx->sdap_id_ctx->be->domain->name) != 0) {
++    if (state->user_domain != state->ipa_ctx->sdap_id_ctx->be->domain) {
+         ret = ipa_subdomain_account_recv(subreq, &state->dp_error);
+     } else {
+         ret = ipa_id_get_account_info_recv(subreq, &state->dp_error);
+-- 
+2.14.3
+
diff --git a/SOURCES/0064-IFP-Search-both-POSIX-and-non-POSIX-domains.patch b/SOURCES/0064-IFP-Search-both-POSIX-and-non-POSIX-domains.patch
deleted file mode 100644
index c956b23..0000000
--- a/SOURCES/0064-IFP-Search-both-POSIX-and-non-POSIX-domains.patch
+++ /dev/null
@@ -1,705 +0,0 @@
-From bab9c21c9ec7ad39555db52511f0f2e425decd94 Mon Sep 17 00:00:00 2001
-From: Jakub Hrozek <jhrozek@redhat.com>
-Date: Fri, 24 Mar 2017 12:44:09 +0100
-Subject: [PATCH 64/72] IFP: Search both POSIX and non-POSIX domains
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Related to:
-https://pagure.io/SSSD/sssd/issue/3310
-
-Changes the behaviour of the InfoPipe responder so that both application
-and POSIX domains are searched. In general, the IFP responder uses the
-CACHE_REQ_ANY_DOM lookup type because we can't presume the intention of
-the caller. Therefore, deployments that combine both POSIX and non-POSIX
-domains must use fully qualified names or select the right domain order
-manually.
-
-There is one change between the POSIX and non-POSIX users or groups -
-the object path. For the POSIX users, the object path includes the UID
-or GID. Because we don't have that for the non-POSIX objects, the object
-name is used in the path instead.
-
-Reviewed-by: Sumit Bose <sbose@redhat.com>
-Reviewed-by: Pavel Březina <pbrezina@redhat.com>
----
- src/responder/ifp/ifp_groups.c | 135 ++++++++++++++++++++++-------------
- src/responder/ifp/ifp_users.c  | 158 ++++++++++++++++++++++++++---------------
- src/responder/ifp/ifpsrv_cmd.c |   6 +-
- 3 files changed, 194 insertions(+), 105 deletions(-)
-
-diff --git a/src/responder/ifp/ifp_groups.c b/src/responder/ifp/ifp_groups.c
-index 99908e96bd971bce4b4e9064a77d8413f837d743..c568c62009cd4b777919dea048fd381a91bd3460 100644
---- a/src/responder/ifp/ifp_groups.c
-+++ b/src/responder/ifp/ifp_groups.c
-@@ -35,25 +35,33 @@ char * ifp_groups_build_path_from_msg(TALLOC_CTX *mem_ctx,
-                                       struct sss_domain_info *domain,
-                                       struct ldb_message *msg)
- {
--    const char *gid;
-+    const char *key = NULL;
- 
--    gid = ldb_msg_find_attr_as_string(msg, SYSDB_GIDNUM, NULL);
-+    switch (domain->type) {
-+    case DOM_TYPE_APPLICATION:
-+        key = ldb_msg_find_attr_as_string(msg, SYSDB_NAME, NULL);
-+        break;
-+    case DOM_TYPE_POSIX:
-+        key = ldb_msg_find_attr_as_string(msg, SYSDB_GIDNUM, NULL);
-+        break;
-+    }
- 
--    if (gid == NULL) {
-+
-+    if (key == NULL) {
-         return NULL;
-     }
- 
--    return sbus_opath_compose(mem_ctx, IFP_PATH_GROUPS, domain->name, gid);
-+    return sbus_opath_compose(mem_ctx, IFP_PATH_GROUPS, domain->name, key);
- }
- 
--static errno_t ifp_groups_decompose_path(struct sss_domain_info *domains,
-+static errno_t ifp_groups_decompose_path(TALLOC_CTX *mem_ctx,
-+                                         struct sss_domain_info *domains,
-                                          const char *path,
-                                          struct sss_domain_info **_domain,
--                                         gid_t *_gid)
-+                                         char **_key)
- {
-     char **parts = NULL;
-     struct sss_domain_info *domain;
--    gid_t gid;
-     errno_t ret;
- 
-     ret = sbus_opath_decompose_exact(NULL, path, IFP_PATH_GROUPS, 2, &parts);
-@@ -67,14 +75,8 @@ static errno_t ifp_groups_decompose_path(struct sss_domain_info *domains,
-         goto done;
-     }
- 
--    gid = strtouint32(parts[1], NULL, 10);
--    ret = errno;
--    if (ret != EOK) {
--        goto done;
--    }
--
-     *_domain = domain;
--    *_gid = gid;
-+    *_key = talloc_steal(mem_ctx, parts[1]);
- 
- done:
-     talloc_free(parts);
-@@ -119,7 +121,7 @@ int ifp_groups_find_by_name(struct sbus_request *sbus_req,
- 
-     req = cache_req_group_by_name_send(sbus_req, ctx->rctx->ev, ctx->rctx,
-                                        ctx->rctx->ncache, 0,
--                                       CACHE_REQ_POSIX_DOM, NULL,
-+                                       CACHE_REQ_ANY_DOM, NULL,
-                                        name);
-     if (req == NULL) {
-         return ENOMEM;
-@@ -273,7 +275,7 @@ static int ifp_groups_list_by_name_step(struct ifp_list_ctx *list_ctx)
-     req = cache_req_group_by_filter_send(list_ctx,
-                                         list_ctx->ctx->rctx->ev,
-                                         list_ctx->ctx->rctx,
--                                        CACHE_REQ_POSIX_DOM,
-+                                        CACHE_REQ_ANY_DOM,
-                                         list_ctx->dom->name,
-                                         list_ctx->filter);
-     if (req == NULL) {
-@@ -358,7 +360,7 @@ int ifp_groups_list_by_domain_and_name(struct sbus_request *sbus_req,
-     }
- 
-     req = cache_req_group_by_filter_send(list_ctx, ctx->rctx->ev, ctx->rctx,
--                                         CACHE_REQ_POSIX_DOM,
-+                                         CACHE_REQ_ANY_DOM,
-                                          domain, filter);
-     if (req == NULL) {
-         return ENOMEM;
-@@ -412,16 +414,65 @@ done:
- }
- 
- static errno_t
-+ifp_groups_get_from_cache(struct sbus_request *sbus_req,
-+                         struct sss_domain_info *domain,
-+                         const char *key,
-+                         struct ldb_message **_group)
-+{
-+    struct ldb_result *group_res;
-+    errno_t ret;
-+    gid_t gid;
-+
-+    switch (domain->type) {
-+    case DOM_TYPE_POSIX:
-+        gid = strtouint32(key, NULL, 10);
-+        ret = errno;
-+        if (ret != EOK) {
-+            DEBUG(SSSDBG_CRIT_FAILURE, "Invalid UID value\n");
-+            return ret;
-+        }
-+
-+        ret = sysdb_getgrgid_with_views(sbus_req, domain, gid, &group_res);
-+        if (ret == EOK && group_res->count == 0) {
-+            *_group = NULL;
-+            return ENOENT;
-+        } else if (ret != EOK) {
-+            DEBUG(SSSDBG_CRIT_FAILURE, "Unable to lookup group %u@%s [%d]: %s\n",
-+                  gid, domain->name, ret, sss_strerror(ret));
-+            return ret;
-+        }
-+        break;
-+    case DOM_TYPE_APPLICATION:
-+        ret = sysdb_getgrnam_with_views(sbus_req, domain, key, &group_res);
-+        if (ret == EOK && group_res->count == 0) {
-+            *_group = NULL;
-+            return ENOENT;
-+        } else if (ret != EOK) {
-+            DEBUG(SSSDBG_CRIT_FAILURE, "Unable to lookup group %s@%s [%d]: %s\n",
-+                  key, domain->name, ret, sss_strerror(ret));
-+            return ret;
-+        }
-+        break;
-+    }
-+
-+    if (group_res->count > 1) {
-+        DEBUG(SSSDBG_CRIT_FAILURE, "More groups matched by the single key\n");
-+        return EIO;
-+    }
-+
-+    *_group = group_res->msgs[0];
-+    return EOK;
-+}
-+
-+static errno_t
- ifp_groups_group_get(struct sbus_request *sbus_req,
-                      void *data,
--                     gid_t *_gid,
-                      struct sss_domain_info **_domain,
-                      struct ldb_message **_group)
- {
-     struct ifp_ctx *ctx;
-     struct sss_domain_info *domain;
--    struct ldb_result *res;
--    uid_t gid;
-+    char *key;
-     errno_t ret;
- 
-     ctx = talloc_get_type(data, struct ifp_ctx);
-@@ -430,8 +481,9 @@ ifp_groups_group_get(struct sbus_request *sbus_req,
-         return ERR_INTERNAL;
-     }
- 
--    ret = ifp_groups_decompose_path(ctx->rctx->domains, sbus_req->path,
--                                    &domain, &gid);
-+    ret = ifp_groups_decompose_path(sbus_req,
-+                                    ctx->rctx->domains, sbus_req->path,
-+                                    &domain, &key);
-     if (ret != EOK) {
-         DEBUG(SSSDBG_CRIT_FAILURE, "Unable to decompose object path"
-               "[%s] [%d]: %s\n", sbus_req->path, ret, sss_strerror(ret));
-@@ -439,28 +491,15 @@ ifp_groups_group_get(struct sbus_request *sbus_req,
-     }
- 
-     if (_group != NULL) {
--        ret = sysdb_getgrgid_with_views(sbus_req, domain, gid, &res);
--        if (ret == EOK && res->count == 0) {
--            *_group = NULL;
--            ret = ENOENT;
--        }
--
--        if (ret != EOK) {
--            DEBUG(SSSDBG_CRIT_FAILURE, "Unable to lookup group %u@%s [%d]: %s\n",
--                  gid, domain->name, ret, sss_strerror(ret));
--        } else {
--            *_group = res->msgs[0];
--        }
-+        ret = ifp_groups_get_from_cache(sbus_req, domain, key, _group);
-     }
- 
-     if (ret == EOK || ret == ENOENT) {
--        if (_gid != NULL) {
--            *_gid = gid;
--        }
--
-         if (_domain != NULL) {
-             *_domain = domain;
-         }
-+    } else if (ret != EOK) {
-+        DEBUG(SSSDBG_OP_FAILURE, "Unable to retrieve group from cache\n");
-     }
- 
-     return ret;
-@@ -513,7 +552,7 @@ static struct tevent_req *resolv_ghosts_send(TALLOC_CTX *mem_ctx,
-     state->ctx = ctx;
-     state->data = data;
- 
--    ret = ifp_groups_group_get(sbus_req, data, NULL, &domain, &group);
-+    ret = ifp_groups_group_get(sbus_req, data, &domain, &group);
-     if (ret != EOK) {
-         goto immediately;
-     }
-@@ -527,7 +566,7 @@ static struct tevent_req *resolv_ghosts_send(TALLOC_CTX *mem_ctx,
- 
-     subreq = cache_req_group_by_name_send(state, ev, ctx->rctx,
-                                           ctx->rctx->ncache, 0,
--                                          CACHE_REQ_POSIX_DOM,
-+                                          CACHE_REQ_ANY_DOM,
-                                           domain->name,
-                                           name);
-     if (subreq == NULL) {
-@@ -561,7 +600,7 @@ static void resolv_ghosts_group_done(struct tevent_req *subreq)
-     req = tevent_req_callback_data(subreq, struct tevent_req);
-     state = tevent_req_data(req, struct resolv_ghosts_state);
- 
--    ret = ifp_groups_group_get(state->sbus_req, state->data, NULL,
-+    ret = ifp_groups_group_get(state->sbus_req, state->data,
-                                &state->domain, &group);
-     if (ret != EOK) {
-         goto done;
-@@ -608,7 +647,7 @@ errno_t resolv_ghosts_step(struct tevent_req *req)
- 
-     subreq = cache_req_user_by_name_send(state, state->ev, state->ctx->rctx,
-                                          state->ctx->rctx->ncache, 0,
--                                         CACHE_REQ_POSIX_DOM,
-+                                         CACHE_REQ_ANY_DOM,
-                                          state->domain->name,
-                                          state->ghosts[state->index]);
-     if (subreq == NULL) {
-@@ -719,7 +758,7 @@ void ifp_groups_group_get_name(struct sbus_request *sbus_req,
-         return;
-     }
- 
--    ret = ifp_groups_group_get(sbus_req, data, NULL, &domain, &msg);
-+    ret = ifp_groups_group_get(sbus_req, data, &domain, &msg);
-     if (ret != EOK) {
-         *_out = NULL;
-         return;
-@@ -744,7 +783,7 @@ void ifp_groups_group_get_gid_number(struct sbus_request *sbus_req,
-     struct sss_domain_info *domain;
-     errno_t ret;
- 
--    ret = ifp_groups_group_get(sbus_req, data, NULL, &domain, &msg);
-+    ret = ifp_groups_group_get(sbus_req, data, &domain, &msg);
-     if (ret != EOK) {
-         *_out = 0;
-         return;
-@@ -763,7 +802,7 @@ void ifp_groups_group_get_unique_id(struct sbus_request *sbus_req,
-     struct sss_domain_info *domain;
-     errno_t ret;
- 
--    ret = ifp_groups_group_get(sbus_req, data, NULL, &domain, &msg);
-+    ret = ifp_groups_group_get(sbus_req, data, &domain, &msg);
-     if (ret != EOK) {
-         *_out = 0;
-         return;
-@@ -803,7 +842,7 @@ ifp_groups_group_get_members(TALLOC_CTX *mem_ctx,
-         return ENOMEM;
-     }
- 
--    ret = ifp_groups_group_get(sbus_req, data, NULL, &domain, &group);
-+    ret = ifp_groups_group_get(sbus_req, data, &domain, &group);
-     if (ret != EOK) {
-         goto done;
-     }
-@@ -954,7 +993,7 @@ int ifp_cache_object_store_group(struct sbus_request *sbus_req,
-     struct ldb_message *group;
-     errno_t ret;
- 
--    ret = ifp_groups_group_get(sbus_req, data, NULL, &domain, &group);
-+    ret = ifp_groups_group_get(sbus_req, data, &domain, &group);
-     if (ret != EOK) {
-         error = sbus_error_new(sbus_req, DBUS_ERROR_FAILED, "Failed to fetch "
-                                "group [%d]: %s\n", ret, sss_strerror(ret));
-@@ -973,7 +1012,7 @@ int ifp_cache_object_remove_group(struct sbus_request *sbus_req,
-     struct ldb_message *group;
-     errno_t ret;
- 
--    ret = ifp_groups_group_get(sbus_req, data, NULL, &domain, &group);
-+    ret = ifp_groups_group_get(sbus_req, data, &domain, &group);
-     if (ret != EOK) {
-         error = sbus_error_new(sbus_req, DBUS_ERROR_FAILED, "Failed to fetch "
-                                "group [%d]: %s\n", ret, sss_strerror(ret));
-diff --git a/src/responder/ifp/ifp_users.c b/src/responder/ifp/ifp_users.c
-index 436bb268fa9c78d72fb744e0d338aa561a7d8764..ce9557f94351b730ee46f3cbce31613cb5901942 100644
---- a/src/responder/ifp/ifp_users.c
-+++ b/src/responder/ifp/ifp_users.c
-@@ -37,25 +37,33 @@ char * ifp_users_build_path_from_msg(TALLOC_CTX *mem_ctx,
-                                      struct sss_domain_info *domain,
-                                      struct ldb_message *msg)
- {
--    const char *uid;
-+    const char *key = NULL;
- 
--    uid = ldb_msg_find_attr_as_string(msg, SYSDB_UIDNUM, NULL);
-+    switch (domain->type) {
-+    case DOM_TYPE_APPLICATION:
-+        key = ldb_msg_find_attr_as_string(msg, SYSDB_NAME, NULL);
-+        break;
-+    case DOM_TYPE_POSIX:
-+        key = ldb_msg_find_attr_as_string(msg, SYSDB_UIDNUM, NULL);
-+        break;
-+    }
- 
--    if (uid == NULL) {
-+
-+    if (key == NULL) {
-         return NULL;
-     }
- 
--    return sbus_opath_compose(mem_ctx, IFP_PATH_USERS, domain->name, uid);
-+    return sbus_opath_compose(mem_ctx, IFP_PATH_USERS, domain->name, key);
- }
- 
--static errno_t ifp_users_decompose_path(struct sss_domain_info *domains,
-+static errno_t ifp_users_decompose_path(TALLOC_CTX *mem_ctx,
-+                                        struct sss_domain_info *domains,
-                                         const char *path,
-                                         struct sss_domain_info **_domain,
--                                        uid_t *_uid)
-+                                        char **_key)
- {
-     char **parts = NULL;
-     struct sss_domain_info *domain;
--    uid_t uid;
-     errno_t ret;
- 
-     ret = sbus_opath_decompose_exact(NULL, path, IFP_PATH_USERS, 2, &parts);
-@@ -69,14 +77,8 @@ static errno_t ifp_users_decompose_path(struct sss_domain_info *domains,
-         goto done;
-     }
- 
--    uid = strtouint32(parts[1], NULL, 10);
--    ret = errno;
--    if (ret != EOK) {
--        goto done;
--    }
--
-     *_domain = domain;
--    *_uid = uid;
-+    *_key = talloc_steal(mem_ctx, parts[1]);
- 
- done:
-     talloc_free(parts);
-@@ -100,7 +102,7 @@ int ifp_users_find_by_name(struct sbus_request *sbus_req,
- 
-     req = cache_req_user_by_name_send(sbus_req, ctx->rctx->ev, ctx->rctx,
-                                       ctx->rctx->ncache, 0,
--                                      CACHE_REQ_POSIX_DOM,
-+                                      CACHE_REQ_ANY_DOM,
-                                       NULL, name);
-     if (req == NULL) {
-         return ENOMEM;
-@@ -256,7 +258,7 @@ int ifp_users_find_by_cert(struct sbus_request *sbus_req, void *data,
- 
-     req = cache_req_user_by_cert_send(sbus_req, ctx->rctx->ev, ctx->rctx,
-                                       ctx->rctx->ncache, 0,
--                                      CACHE_REQ_POSIX_DOM, NULL,
-+                                      CACHE_REQ_ANY_DOM, NULL,
-                                       derb64);
-     if (req == NULL) {
-         return ENOMEM;
-@@ -371,7 +373,7 @@ static int ifp_users_list_by_cert_step(struct ifp_list_ctx *list_ctx)
-                                       list_ctx->ctx->rctx,
-                                       list_ctx->ctx->rctx->ncache,
-                                       0,
--                                      CACHE_REQ_POSIX_DOM,
-+                                      CACHE_REQ_ANY_DOM,
-                                       list_ctx->dom->name,
-                                       list_ctx->filter);
-     if (req == NULL) {
-@@ -538,7 +540,7 @@ int ifp_users_find_by_name_and_cert(struct sbus_request *sbus_req, void *data,
-     if (name_and_cert_ctx->name != NULL) {
-         req = cache_req_user_by_name_send(sbus_req, ctx->rctx->ev, ctx->rctx,
-                                           ctx->rctx->ncache, 0,
--                                          CACHE_REQ_POSIX_DOM,
-+                                          CACHE_REQ_ANY_DOM,
-                                           NULL,
-                                           name_and_cert_ctx->name);
-         if (req == NULL) {
-@@ -621,7 +623,7 @@ static int ifp_users_find_by_name_and_cert_step(
-                                       list_ctx->ctx->rctx,
-                                       list_ctx->ctx->rctx->ncache,
-                                       0,
--                                      CACHE_REQ_POSIX_DOM,
-+                                      CACHE_REQ_ANY_DOM,
-                                       list_ctx->dom->name,
-                                       list_ctx->filter);
-     if (req == NULL) {
-@@ -782,7 +784,7 @@ static int ifp_users_list_by_name_step(struct ifp_list_ctx *list_ctx)
-     req = cache_req_user_by_filter_send(list_ctx,
-                                         list_ctx->ctx->rctx->ev,
-                                         list_ctx->ctx->rctx,
--                                        CACHE_REQ_POSIX_DOM,
-+                                        CACHE_REQ_ANY_DOM,
-                                         list_ctx->dom->name,
-                                         list_ctx->filter);
-     if (req == NULL) {
-@@ -867,7 +869,7 @@ int ifp_users_list_by_domain_and_name(struct sbus_request *sbus_req,
-     }
- 
-     req = cache_req_user_by_filter_send(list_ctx, ctx->rctx->ev, ctx->rctx,
--                                        CACHE_REQ_POSIX_DOM,
-+                                        CACHE_REQ_ANY_DOM,
-                                         domain, filter);
-     if (req == NULL) {
-         return ENOMEM;
-@@ -930,19 +932,69 @@ done:
- }
- 
- static errno_t
-+ifp_users_get_from_cache(struct sbus_request *sbus_req,
-+                         struct sss_domain_info *domain,
-+                         const char *key,
-+                         struct ldb_message **_user)
-+{
-+    struct ldb_result *user_res;
-+    errno_t ret;
-+    uid_t uid;
-+
-+    switch (domain->type) {
-+    case DOM_TYPE_POSIX:
-+        uid = strtouint32(key, NULL, 10);
-+        ret = errno;
-+        if (ret != EOK) {
-+            DEBUG(SSSDBG_CRIT_FAILURE, "Invalid UID value\n");
-+            return ret;
-+        }
-+
-+        ret = sysdb_getpwuid_with_views(sbus_req, domain, uid, &user_res);
-+        if (ret == EOK && user_res->count == 0) {
-+            *_user = NULL;
-+            return ENOENT;
-+        } else if (ret != EOK) {
-+            DEBUG(SSSDBG_CRIT_FAILURE, "Unable to lookup user %u@%s [%d]: %s\n",
-+                  uid, domain->name, ret, sss_strerror(ret));
-+            return ret;
-+        }
-+        break;
-+    case DOM_TYPE_APPLICATION:
-+        ret = sysdb_getpwnam_with_views(sbus_req, domain, key, &user_res);
-+        if (ret == EOK && user_res->count == 0) {
-+            *_user = NULL;
-+            return ENOENT;
-+        } else if (ret != EOK) {
-+            DEBUG(SSSDBG_CRIT_FAILURE, "Unable to lookup user %s@%s [%d]: %s\n",
-+                  key, domain->name, ret, sss_strerror(ret));
-+            return ret;
-+        }
-+        break;
-+    }
-+
-+    if (user_res->count > 1) {
-+        DEBUG(SSSDBG_CRIT_FAILURE, "More users matched by the single key\n");
-+        return EIO;
-+    }
-+
-+    *_user = user_res->msgs[0];
-+    return EOK;
-+}
-+
-+static errno_t
- ifp_users_user_get(struct sbus_request *sbus_req,
-                    struct ifp_ctx *ifp_ctx,
--                   uid_t *_uid,
-                    struct sss_domain_info **_domain,
-                    struct ldb_message **_user)
- {
-     struct sss_domain_info *domain;
--    struct ldb_result *res;
--    uid_t uid;
-+    char *key;
-     errno_t ret;
- 
--    ret = ifp_users_decompose_path(ifp_ctx->rctx->domains, sbus_req->path,
--                                   &domain, &uid);
-+    ret = ifp_users_decompose_path(sbus_req,
-+                                   ifp_ctx->rctx->domains, sbus_req->path,
-+                                   &domain, &key);
-     if (ret != EOK) {
-         DEBUG(SSSDBG_CRIT_FAILURE, "Unable to decompose object path"
-               "[%s] [%d]: %s\n", sbus_req->path, ret, sss_strerror(ret));
-@@ -950,28 +1002,15 @@ ifp_users_user_get(struct sbus_request *sbus_req,
-     }
- 
-     if (_user != NULL) {
--        ret = sysdb_getpwuid_with_views(sbus_req, domain, uid, &res);
--        if (ret == EOK && res->count == 0) {
--            *_user = NULL;
--            ret = ENOENT;
--        }
--
--        if (ret != EOK) {
--            DEBUG(SSSDBG_CRIT_FAILURE, "Unable to lookup user %u@%s [%d]: %s\n",
--                  uid, domain->name, ret, sss_strerror(ret));
--        } else {
--            *_user = res->msgs[0];
--        }
-+        ret = ifp_users_get_from_cache(sbus_req, domain, key, _user);
-     }
- 
-     if (ret == EOK || ret == ENOENT) {
--        if (_uid != NULL) {
--            *_uid = uid;
--        }
--
-         if (_domain != NULL) {
-             *_domain = domain;
-         }
-+    } else if (ret != EOK) {
-+        DEBUG(SSSDBG_OP_FAILURE, "Unable to retrieve user from cache\n");
-     }
- 
-     return ret;
-@@ -1000,7 +1039,7 @@ static void ifp_users_get_as_string(struct sbus_request *sbus_req,
-         return;
-     }
- 
--    ret = ifp_users_user_get(sbus_req, ifp_ctx, NULL, &domain, &msg);
-+    ret = ifp_users_user_get(sbus_req, ifp_ctx, &domain, &msg);
-     if (ret != EOK) {
-         return;
-     }
-@@ -1034,7 +1073,7 @@ static void ifp_users_get_name(struct sbus_request *sbus_req,
-         return;
-     }
- 
--    ret = ifp_users_user_get(sbus_req, ifp_ctx, NULL, &domain, &msg);
-+    ret = ifp_users_user_get(sbus_req, ifp_ctx, &domain, &msg);
-     if (ret != EOK) {
-         return;
-     }
-@@ -1072,7 +1111,7 @@ static void ifp_users_get_as_uint32(struct sbus_request *sbus_req,
-         return;
-     }
- 
--    ret = ifp_users_user_get(sbus_req, ifp_ctx, NULL, &domain, &msg);
-+    ret = ifp_users_user_get(sbus_req, ifp_ctx, &domain, &msg);
-     if (ret != EOK) {
-         return;
-     }
-@@ -1100,7 +1139,7 @@ int ifp_users_user_update_groups_list(struct sbus_request *sbus_req,
-         return ERR_INTERNAL;
-     }
- 
--    ret = ifp_users_user_get(sbus_req, data, NULL, &domain, &user);
-+    ret = ifp_users_user_get(sbus_req, data, &domain, &user);
-     if (ret != EOK) {
-         return ret;
-     }
-@@ -1113,7 +1152,7 @@ int ifp_users_user_update_groups_list(struct sbus_request *sbus_req,
- 
-     req = cache_req_initgr_by_name_send(sbus_req, ctx->rctx->ev, ctx->rctx,
-                                         ctx->rctx->ncache, 0,
--                                        CACHE_REQ_POSIX_DOM, domain->name,
-+                                        CACHE_REQ_ANY_DOM, domain->name,
-                                         username);
-     if (req == NULL) {
-         return ENOMEM;
-@@ -1235,7 +1274,7 @@ void ifp_users_user_get_groups(struct sbus_request *sbus_req,
-         return;
-     }
- 
--    ret = ifp_users_user_get(sbus_req, ifp_ctx, NULL, &domain, &user);
-+    ret = ifp_users_user_get(sbus_req, ifp_ctx, &domain, &user);
-     if (ret != EOK) {
-         return;
-     }
-@@ -1268,7 +1307,7 @@ void ifp_users_user_get_groups(struct sbus_request *sbus_req,
-     for (i = 0; i < res->count; i++) {
-         gid = sss_view_ldb_msg_find_attr_as_uint64(domain, res->msgs[i],
-                                                    SYSDB_GIDNUM, 0);
--        if (gid == 0) {
-+        if (gid == 0 && domain->type == DOM_TYPE_POSIX) {
-             continue;
-         }
- 
-@@ -1293,11 +1332,12 @@ void ifp_users_user_get_extra_attributes(struct sbus_request *sbus_req,
- {
-     struct ifp_ctx *ifp_ctx;
-     struct sss_domain_info *domain;
-+    struct ldb_message *base_user;
-+    const char *name;
-     struct ldb_message **user;
-     struct ldb_message_element *el;
-     struct ldb_dn *basedn;
-     size_t count;
--    uid_t uid;
-     const char *filter;
-     const char **extra;
-     hash_table_t *table;
-@@ -1322,7 +1362,7 @@ void ifp_users_user_get_extra_attributes(struct sbus_request *sbus_req,
-         return;
-     }
- 
--    ret = ifp_users_user_get(sbus_req, data, &uid, &domain, NULL);
-+    ret = ifp_users_user_get(sbus_req, data, &domain, &base_user);
-     if (ret != EOK) {
-         return;
-     }
-@@ -1333,9 +1373,15 @@ void ifp_users_user_get_extra_attributes(struct sbus_request *sbus_req,
-         return;
-     }
- 
--    filter = talloc_asprintf(sbus_req, "(&(%s=%s)(%s=%u))",
-+    name = ldb_msg_find_attr_as_string(base_user, SYSDB_NAME, NULL);
-+    if (name == NULL) {
-+        DEBUG(SSSDBG_CRIT_FAILURE, "A user with no name\n");
-+        return;
-+    }
-+
-+    filter = talloc_asprintf(sbus_req, "(&(%s=%s)(%s=%s))",
-                              SYSDB_OBJECTCLASS, SYSDB_USER_CLASS,
--                             SYSDB_UIDNUM, uid);
-+                             SYSDB_NAME, name);
-     if (filter == NULL) {
-         DEBUG(SSSDBG_CRIT_FAILURE, "talloc_asprintf() failed\n");
-         return;
-@@ -1351,7 +1397,7 @@ void ifp_users_user_get_extra_attributes(struct sbus_request *sbus_req,
-     }
- 
-     if (count == 0) {
--        DEBUG(SSSDBG_TRACE_FUNC, "User %u not found!\n", uid);
-+        DEBUG(SSSDBG_TRACE_FUNC, "User %s not found!\n", name);
-         return;
-     } else if (count > 1) {
-         DEBUG(SSSDBG_CRIT_FAILURE, "More than one entry found!\n");
-@@ -1421,7 +1467,7 @@ int ifp_cache_object_store_user(struct sbus_request *sbus_req,
-     struct ldb_message *user;
-     errno_t ret;
- 
--    ret = ifp_users_user_get(sbus_req, data, NULL, &domain, &user);
-+    ret = ifp_users_user_get(sbus_req, data, &domain, &user);
-     if (ret != EOK) {
-         error = sbus_error_new(sbus_req, DBUS_ERROR_FAILED, "Failed to fetch "
-                                "user [%d]: %s\n", ret, sss_strerror(ret));
-@@ -1440,7 +1486,7 @@ int ifp_cache_object_remove_user(struct sbus_request *sbus_req,
-     struct ldb_message *user;
-     errno_t ret;
- 
--    ret = ifp_users_user_get(sbus_req, data, NULL, &domain, &user);
-+    ret = ifp_users_user_get(sbus_req, data, &domain, &user);
-     if (ret != EOK) {
-         error = sbus_error_new(sbus_req, DBUS_ERROR_FAILED, "Failed to fetch "
-                                "user [%d]: %s\n", ret, sss_strerror(ret));
-diff --git a/src/responder/ifp/ifpsrv_cmd.c b/src/responder/ifp/ifpsrv_cmd.c
-index 118b5083b14bf5692c6fdd7ba90668fe514aa89d..d10f35e41dbb1623a0b9de37a4c43363cbefc1a3 100644
---- a/src/responder/ifp/ifpsrv_cmd.c
-+++ b/src/responder/ifp/ifpsrv_cmd.c
-@@ -508,8 +508,12 @@ ifp_user_get_attr_lookup(struct tevent_req *subreq)
-         return;
-     }
- 
-+    /* IFP serves both POSIX and application domains. Requests that need
-+     * to differentiate between the two must be qualified
-+     */
-     subreq = cache_req_send(state, state->rctx->ev, state->rctx,
--                            state->ncache, 0, CACHE_REQ_POSIX_DOM,
-+                            state->ncache, 0,
-+                            CACHE_REQ_ANY_DOM,
-                             state->domname, data);
-     if (subreq == NULL) {
-         tevent_req_error(req, ENOMEM);
--- 
-2.9.3
-
diff --git a/SOURCES/0064-overrides-fixes-for-sysdb_invalidate_overrides.patch b/SOURCES/0064-overrides-fixes-for-sysdb_invalidate_overrides.patch
new file mode 100644
index 0000000..c8d9de9
--- /dev/null
+++ b/SOURCES/0064-overrides-fixes-for-sysdb_invalidate_overrides.patch
@@ -0,0 +1,203 @@
+From c0263b48a3512d8b6984693c4b8e772844215f9e Mon Sep 17 00:00:00 2001
+From: Sumit Bose <sbose@redhat.com>
+Date: Mon, 20 Nov 2017 15:51:27 +0100
+Subject: [PATCH 64/67] overrides: fixes for sysdb_invalidate_overrides()
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+There were two issues in sysdb_invalidate_overrides().
+
+First, SYSDB_CACHE_EXPIRE was only reset for the entry in the data cache
+but not in the timestamp cache.
+
+Second, if one of the steps in the combined replace and delete operation
+failed no change was committed to the cache. If, for whatever reasons,
+a user or group object didn't had SYSDB_OVERRIDE_DN set the delete
+failed and hence SYSDB_CACHE_EXPIRE wasn't reset as well. To make sure
+the cache is in a consistent state after a view change the replace and
+the delete operations are don in two steps.
+
+Related to https://pagure.io/SSSD/sssd/issue/3579
+
+Reviewed-by: Fabiano Fidêncio <fidencio@redhat.com>
+(cherry picked from commit 4671acb949c65c5c080532e03b1b6f1c9377a6a5)
+---
+ src/db/sysdb_views.c | 111 +++++++++++++++++++++++++++++++++++++--------------
+ 1 file changed, 80 insertions(+), 31 deletions(-)
+
+diff --git a/src/db/sysdb_views.c b/src/db/sysdb_views.c
+index afc7852ecf402ef144beca9c1b94fbe3cc4bbb6a..70082d8db9b25c11e8c0823d4e5da2ba0c0d10d1 100644
+--- a/src/db/sysdb_views.c
++++ b/src/db/sysdb_views.c
+@@ -279,6 +279,45 @@ done:
+     return ret;
+ }
+ 
++static errno_t invalidate_entry_override(struct sysdb_ctx *sysdb,
++                                         struct ldb_dn *dn,
++                                         struct ldb_message *msg_del,
++                                         struct ldb_message *msg_repl)
++{
++    int ret;
++
++    msg_del->dn = dn;
++    msg_repl->dn = dn;
++
++    ret = ldb_modify(sysdb->ldb, msg_del);
++    if (ret != LDB_SUCCESS && ret != LDB_ERR_NO_SUCH_ATTRIBUTE) {
++        DEBUG(SSSDBG_OP_FAILURE,
++              "ldb_modify failed: [%s](%d)[%s]\n",
++              ldb_strerror(ret), ret, ldb_errstring(sysdb->ldb));
++        return sysdb_error_to_errno(ret);
++    }
++
++    ret = ldb_modify(sysdb->ldb, msg_repl);
++    if (ret != LDB_SUCCESS && ret != LDB_ERR_NO_SUCH_ATTRIBUTE) {
++        DEBUG(SSSDBG_OP_FAILURE,
++              "ldb_modify failed: [%s](%d)[%s]\n",
++              ldb_strerror(ret), ret, ldb_errstring(sysdb->ldb));
++        return sysdb_error_to_errno(ret);
++    }
++
++    if (sysdb->ldb_ts != NULL) {
++        ret = ldb_modify(sysdb->ldb_ts, msg_repl);
++        if (ret != LDB_SUCCESS && ret != LDB_ERR_NO_SUCH_ATTRIBUTE) {
++            DEBUG(SSSDBG_OP_FAILURE,
++                  "ldb_modify failed: [%s](%d)[%s]\n",
++                  ldb_strerror(ret), ret, ldb_errstring(sysdb->ldb_ts));
++            return sysdb_error_to_errno(ret);
++        }
++    }
++
++    return EOK;
++}
++
+ errno_t sysdb_invalidate_overrides(struct sysdb_ctx *sysdb)
+ {
+     int ret;
+@@ -287,22 +326,23 @@ errno_t sysdb_invalidate_overrides(struct sysdb_ctx *sysdb)
+     bool in_transaction = false;
+     struct ldb_result *res;
+     size_t c;
+-    struct ldb_message *msg;
++    struct ldb_message *msg_del;
++    struct ldb_message *msg_repl;
+     struct ldb_dn *base_dn;
+ 
++    if (sysdb->ldb_ts == NULL) {
++        DEBUG(SSSDBG_CRIT_FAILURE,
++              "Timestamp cache context not available, cache might not be "
++              "invalidated completely. Please call 'sss_cache -E' or remove "
++              "the cache file if there are issues after a view name change.\n");
++    }
++
+     tmp_ctx = talloc_new(NULL);
+     if (tmp_ctx == NULL) {
+         DEBUG(SSSDBG_OP_FAILURE, "talloc_new failed.\n");
+         return ENOMEM;
+     }
+ 
+-    msg = ldb_msg_new(tmp_ctx);
+-    if (msg == NULL) {
+-        DEBUG(SSSDBG_OP_FAILURE, "ldb_msg_new failed.\n");
+-        ret = ENOMEM;
+-        goto done;
+-    }
+-
+     base_dn = ldb_dn_new(tmp_ctx, sysdb->ldb, SYSDB_BASE);
+     if (base_dn == NULL) {
+         DEBUG(SSSDBG_OP_FAILURE, "ldb_dn_new failed\n");
+@@ -310,27 +350,40 @@ errno_t sysdb_invalidate_overrides(struct sysdb_ctx *sysdb)
+         goto done;
+     }
+ 
+-    ret = ldb_msg_add_empty(msg, SYSDB_CACHE_EXPIRE, LDB_FLAG_MOD_REPLACE,
++    msg_del = ldb_msg_new(tmp_ctx);
++    if (msg_del == NULL) {
++        DEBUG(SSSDBG_OP_FAILURE, "ldb_msg_new failed.\n");
++        ret = ENOMEM;
++        goto done;
++    }
++    ret = ldb_msg_add_empty(msg_del, SYSDB_OVERRIDE_DN, LDB_FLAG_MOD_DELETE,
+                             NULL);
+     if (ret != LDB_SUCCESS) {
+         DEBUG(SSSDBG_OP_FAILURE, "ldb_msg_add_empty failed.\n");
+         ret = sysdb_error_to_errno(ret);
+         goto done;
+     }
+-    ret = ldb_msg_add_string(msg, SYSDB_CACHE_EXPIRE, "1");
++
++    msg_repl = ldb_msg_new(tmp_ctx);
++    if (msg_repl == NULL) {
++        DEBUG(SSSDBG_OP_FAILURE, "ldb_msg_new failed.\n");
++        ret = ENOMEM;
++        goto done;
++    }
++    ret = ldb_msg_add_empty(msg_repl, SYSDB_CACHE_EXPIRE,
++                            LDB_FLAG_MOD_REPLACE, NULL);
++    if (ret != LDB_SUCCESS) {
++        DEBUG(SSSDBG_OP_FAILURE, "ldb_msg_add_empty failed.\n");
++        ret = sysdb_error_to_errno(ret);
++        goto done;
++    }
++    ret = ldb_msg_add_string(msg_repl, SYSDB_CACHE_EXPIRE, "1");
+     if (ret != LDB_SUCCESS) {
+         DEBUG(SSSDBG_OP_FAILURE, "ldb_msg_add_string failed.\n");
+         ret = sysdb_error_to_errno(ret);
+         goto done;
+     }
+ 
+-    ret = ldb_msg_add_empty(msg, SYSDB_OVERRIDE_DN, LDB_FLAG_MOD_DELETE, NULL);
+-    if (ret != LDB_SUCCESS) {
+-        DEBUG(SSSDBG_OP_FAILURE, "ldb_msg_add_empty failed.\n");
+-        ret = sysdb_error_to_errno(ret);
+-        goto done;
+-    }
+-
+     ret = sysdb_transaction_start(sysdb);
+     if (ret != EOK) {
+         DEBUG(SSSDBG_OP_FAILURE, "sysdb_transaction_start failed.\n");
+@@ -347,14 +400,12 @@ errno_t sysdb_invalidate_overrides(struct sysdb_ctx *sysdb)
+     }
+ 
+     for (c = 0; c < res->count; c++) {
+-        msg->dn = res->msgs[c]->dn;
+-
+-        ret = ldb_modify(sysdb->ldb, msg);
+-        if (ret != LDB_SUCCESS && ret != LDB_ERR_NO_SUCH_ATTRIBUTE) {
++        ret = invalidate_entry_override(sysdb, res->msgs[c]->dn, msg_del,
++                                                                 msg_repl);
++        if (ret != EOK) {
+             DEBUG(SSSDBG_OP_FAILURE,
+-                  "ldb_modify failed: [%s](%d)[%s]\n",
+-                  ldb_strerror(ret), ret, ldb_errstring(sysdb->ldb));
+-            ret = sysdb_error_to_errno(ret);
++                  "invalidate_entry_override failed [%d][%s].\n",
++                  ret, sss_strerror(ret));
+             goto done;
+         }
+     }
+@@ -370,14 +421,12 @@ errno_t sysdb_invalidate_overrides(struct sysdb_ctx *sysdb)
+     }
+ 
+     for (c = 0; c < res->count; c++) {
+-        msg->dn = res->msgs[c]->dn;
+-
+-        ret = ldb_modify(sysdb->ldb, msg);
+-        if (ret != LDB_SUCCESS && ret != LDB_ERR_NO_SUCH_ATTRIBUTE) {
++        ret = invalidate_entry_override(sysdb, res->msgs[c]->dn, msg_del,
++                                                                 msg_repl);
++        if (ret != EOK) {
+             DEBUG(SSSDBG_OP_FAILURE,
+-                  "ldb_modify failed: [%s](%d)[%s]\n",
+-                  ldb_strerror(ret), ret, ldb_errstring(sysdb->ldb));
+-            ret = sysdb_error_to_errno(ret);
++                  "invalidate_entry_override failed [%d][%s].\n",
++                  ret, sss_strerror(ret));
+             goto done;
+         }
+     }
+-- 
+2.14.3
+
diff --git a/SOURCES/0065-IFP-ListByName-Don-t-crash-when-no-results-are-found.patch b/SOURCES/0065-IFP-ListByName-Don-t-crash-when-no-results-are-found.patch
deleted file mode 100644
index f062ffa..0000000
--- a/SOURCES/0065-IFP-ListByName-Don-t-crash-when-no-results-are-found.patch
+++ /dev/null
@@ -1,57 +0,0 @@
-From c9268488cd24fe8e13580d6c4ea2fa237faededa Mon Sep 17 00:00:00 2001
-From: Jakub Hrozek <jhrozek@redhat.com>
-Date: Tue, 28 Mar 2017 14:07:29 +0200
-Subject: [PATCH 65/72] IFP: ListByName: Don't crash when no results are found
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-If no results were found using the List command, the results variable
-was undefined which resulted in a crash.
-
-Instead, only copy the results of the cache_req lookup returns EOK and
-we can presume that the results are valid.
-
-Reviewed-by: Pavel Březina <pbrezina@redhat.com>
-Reviewed-by: Sumit Bose <sbose@redhat.com>
----
- src/responder/ifp/ifp_users.c | 16 +++++++++-------
- 1 file changed, 9 insertions(+), 7 deletions(-)
-
-diff --git a/src/responder/ifp/ifp_users.c b/src/responder/ifp/ifp_users.c
-index ce9557f94351b730ee46f3cbce31613cb5901942..188194f2ab356d0e67b0f26b003f3a9ce48e6acd 100644
---- a/src/responder/ifp/ifp_users.c
-+++ b/src/responder/ifp/ifp_users.c
-@@ -801,7 +801,7 @@ static void ifp_users_list_by_name_done(struct tevent_req *req)
-     DBusError *error;
-     struct ifp_list_ctx *list_ctx;
-     struct sbus_request *sbus_req;
--    struct cache_req_result *result;
-+    struct cache_req_result *result = NULL;
-     errno_t ret;
- 
-     list_ctx = tevent_req_callback_data(req, struct ifp_list_ctx);
-@@ -816,12 +816,14 @@ static void ifp_users_list_by_name_done(struct tevent_req *req)
-         return;
-     }
- 
--    ret = ifp_users_list_copy(list_ctx, result->ldb_result);
--    if (ret != EOK) {
--        error = sbus_error_new(sbus_req, SBUS_ERROR_INTERNAL,
--                               "Failed to copy domain result");
--        sbus_request_fail_and_finish(sbus_req, error);
--        return;
-+    if (ret == EOK) {
-+        ret = ifp_users_list_copy(list_ctx, result->ldb_result);
-+        if (ret != EOK) {
-+            error = sbus_error_new(sbus_req, SBUS_ERROR_INTERNAL,
-+                                "Failed to copy domain result");
-+            sbus_request_fail_and_finish(sbus_req, error);
-+            return;
-+        }
-     }
- 
-     list_ctx->dom = get_next_domain(list_ctx->dom, SSS_GND_DESCEND);
--- 
-2.9.3
-
diff --git a/SOURCES/0065-ipa-check-for-SYSDB_OVERRIDE_DN-in-process_members-a.patch b/SOURCES/0065-ipa-check-for-SYSDB_OVERRIDE_DN-in-process_members-a.patch
new file mode 100644
index 0000000..74f5f5e
--- /dev/null
+++ b/SOURCES/0065-ipa-check-for-SYSDB_OVERRIDE_DN-in-process_members-a.patch
@@ -0,0 +1,254 @@
+From 53c6201539d24f8b929120565ca661977ecbb1a4 Mon Sep 17 00:00:00 2001
+From: Sumit Bose <sbose@redhat.com>
+Date: Mon, 20 Nov 2017 16:12:58 +0100
+Subject: [PATCH 65/67] ipa: check for SYSDB_OVERRIDE_DN in process_members and
+ get_group_dn_list
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+process_members() and get_group_dn_list() are used on an IPA client to
+determine a list of users or groups which are missing in the cache and
+are needed to properly add a group or user object to the cache
+respectively.
+
+If a non-default view is assigned to the client the SYSDB_OVERRIDE_DN
+must be set for all user and group objects to indicate that it was
+already checked if there is an id-override defined for the object or
+not. There a circumstances were SYSDB_OVERRIDE_DN is not set, e.g. after
+a view name change. To make sure the cache is in a consistent state with
+this patch  user and group entries without SYSDB_OVERRIDE_DN are
+considered as missing is a non-default view is assigned to the client.
+
+Related to https://pagure.io/SSSD/sssd/issue/3579
+
+Reviewed-by: Fabiano Fidêncio <fidencio@redhat.com>
+(cherry picked from commit 919b5d76057d31877e0c25ca495711ff76c713d6)
+---
+ src/providers/ipa/ipa_s2n_exop.c | 145 ++++++++++++++++++++++-----------------
+ 1 file changed, 83 insertions(+), 62 deletions(-)
+
+diff --git a/src/providers/ipa/ipa_s2n_exop.c b/src/providers/ipa/ipa_s2n_exop.c
+index 39ed17cbf0e8c523212084197e9f2963fed88dc8..c6132f509dcc8e7af84e03e8bfe20701107d1392 100644
+--- a/src/providers/ipa/ipa_s2n_exop.c
++++ b/src/providers/ipa/ipa_s2n_exop.c
+@@ -1523,6 +1523,7 @@ fail:
+ }
+ 
+ static errno_t process_members(struct sss_domain_info *domain,
++                               bool is_default_view,
+                                struct sysdb_attrs *group_attrs,
+                                char **members,
+                                TALLOC_CTX *mem_ctx, char ***_missing_members)
+@@ -1536,6 +1537,7 @@ static errno_t process_members(struct sss_domain_info *domain,
+     struct sss_domain_info *parent_domain;
+     char **missing_members = NULL;
+     size_t miss_count = 0;
++    const char *attrs[] = {SYSDB_NAME, SYSDB_OVERRIDE_DN, NULL};
+ 
+     if (members == NULL) {
+         DEBUG(SSSDBG_TRACE_INTERNAL, "No members\n");
+@@ -1572,53 +1574,59 @@ static errno_t process_members(struct sss_domain_info *domain,
+             goto done;
+         }
+ 
+-        ret = sysdb_search_user_by_name(tmp_ctx, obj_domain, members[c], NULL,
++        ret = sysdb_search_user_by_name(tmp_ctx, obj_domain, members[c], attrs,
+                                         &msg);
+-        if (ret == EOK) {
+-            if (group_attrs != NULL) {
+-                dn_str = ldb_dn_get_linearized(msg->dn);
+-                if (dn_str == NULL) {
+-                    DEBUG(SSSDBG_OP_FAILURE, "ldb_dn_get_linearized failed.\n");
+-                    ret = EINVAL;
+-                    goto done;
+-                }
+-
+-                DEBUG(SSSDBG_TRACE_ALL, "Adding member [%s][%s]\n",
+-                                        members[c], dn_str);
++        if (ret == EOK || ret == ENOENT) {
++            if (ret == ENOENT
++                    || (!is_default_view
++                        && ldb_msg_find_attr_as_string(msg, SYSDB_OVERRIDE_DN,
++                                                       NULL) == NULL)) {
++                /* only add ghost if the member is really missing */
++                if (group_attrs != NULL && ret == ENOENT) {
++                    DEBUG(SSSDBG_TRACE_ALL, "Adding ghost member [%s]\n",
++                                            members[c]);
+ 
+-                ret = sysdb_attrs_add_string_safe(group_attrs, SYSDB_MEMBER,
+-                                                  dn_str);
+-                if (ret != EOK) {
+-                    DEBUG(SSSDBG_OP_FAILURE,
+-                          "sysdb_attrs_add_string_safe failed.\n");
+-                    goto done;
++                    /* There were cases where the server returned the same user
++                     * multiple times */
++                    ret = sysdb_attrs_add_string_safe(group_attrs, SYSDB_GHOST,
++                                                      members[c]);
++                    if (ret != EOK) {
++                        DEBUG(SSSDBG_OP_FAILURE,
++                              "sysdb_attrs_add_string failed.\n");
++                        goto done;
++                    }
+                 }
+-            }
+-        } else if (ret == ENOENT) {
+-            if (group_attrs != NULL) {
+-                DEBUG(SSSDBG_TRACE_ALL, "Adding ghost member [%s]\n",
+-                                        members[c]);
+ 
+-                /* There were cases where the server returned the same user
+-                 * multiple times */
+-                ret = sysdb_attrs_add_string_safe(group_attrs, SYSDB_GHOST,
+-                                                  members[c]);
+-                if (ret != EOK) {
+-                    DEBUG(SSSDBG_OP_FAILURE,
+-                          "sysdb_attrs_add_string failed.\n");
+-                    goto done;
++                if (missing_members != NULL) {
++                    missing_members[miss_count] = talloc_strdup(missing_members,
++                                                                members[c]);
++                    if (missing_members[miss_count] == NULL) {
++                        DEBUG(SSSDBG_OP_FAILURE, "talloc_strdup failed.\n");
++                        ret = ENOMEM;
++                        goto done;
++                    }
++                    miss_count++;
+                 }
+-            }
++            } else {
++                if (group_attrs != NULL) {
++                    dn_str = ldb_dn_get_linearized(msg->dn);
++                    if (dn_str == NULL) {
++                        DEBUG(SSSDBG_OP_FAILURE, "ldb_dn_get_linearized failed.\n");
++                        ret = EINVAL;
++                        goto done;
++                    }
++
++                    DEBUG(SSSDBG_TRACE_ALL, "Adding member [%s][%s]\n",
++                                            members[c], dn_str);
+ 
+-            if (missing_members != NULL) {
+-                missing_members[miss_count] = talloc_strdup(missing_members,
+-                                                            members[c]);
+-                if (missing_members[miss_count] == NULL) {
+-                    DEBUG(SSSDBG_OP_FAILURE, "talloc_strdup failed.\n");
+-                    ret = ENOMEM;
+-                    goto done;
++                    ret = sysdb_attrs_add_string_safe(group_attrs, SYSDB_MEMBER,
++                                                      dn_str);
++                    if (ret != EOK) {
++                        DEBUG(SSSDBG_OP_FAILURE,
++                              "sysdb_attrs_add_string_safe failed.\n");
++                        goto done;
++                    }
+                 }
+-                miss_count++;
+             }
+         } else {
+             DEBUG(SSSDBG_OP_FAILURE, "sysdb_search_user_by_name failed.\n");
+@@ -1649,6 +1657,7 @@ done:
+ }
+ 
+ static errno_t get_group_dn_list(TALLOC_CTX *mem_ctx,
++                                 bool is_default_view,
+                                  struct sss_domain_info *dom,
+                                  size_t ngroups, char **groups,
+                                  struct ldb_dn ***_dn_list,
+@@ -1664,6 +1673,7 @@ static errno_t get_group_dn_list(TALLOC_CTX *mem_ctx,
+     size_t n_missing = 0;
+     struct sss_domain_info *obj_domain;
+     struct sss_domain_info *parent_domain;
++    const char *attrs[] = {SYSDB_NAME, SYSDB_OVERRIDE_DN, NULL};
+ 
+     tmp_ctx = talloc_new(NULL);
+     if (tmp_ctx == NULL) {
+@@ -1689,25 +1699,31 @@ static errno_t get_group_dn_list(TALLOC_CTX *mem_ctx,
+             goto done;
+         }
+ 
+-        ret = sysdb_search_group_by_name(tmp_ctx, obj_domain, groups[c], NULL,
++        ret = sysdb_search_group_by_name(tmp_ctx, obj_domain, groups[c], attrs,
+                                          &msg);
+-        if (ret == EOK) {
+-            dn_list[n_dns] = ldb_dn_copy(dn_list, msg->dn);
+-            if (dn_list[n_dns] == NULL) {
+-                DEBUG(SSSDBG_OP_FAILURE, "ldb_dn_copy failed.\n");
+-                ret = ENOMEM;
+-                goto done;
++        if (ret == EOK || ret == ENOENT) {
++            if (ret == ENOENT
++                    || (!is_default_view
++                        && ldb_msg_find_attr_as_string(msg, SYSDB_OVERRIDE_DN,
++                                                       NULL) == NULL)) {
++                missing_groups[n_missing] = talloc_strdup(missing_groups,
++                                                          groups[c]);
++                if (missing_groups[n_missing] == NULL) {
++                    DEBUG(SSSDBG_OP_FAILURE, "talloc_strdup failed.\n");
++                    ret = ENOMEM;
++                    goto done;
++                }
++                n_missing++;
++
++            } else {
++                dn_list[n_dns] = ldb_dn_copy(dn_list, msg->dn);
++                if (dn_list[n_dns] == NULL) {
++                    DEBUG(SSSDBG_OP_FAILURE, "ldb_dn_copy failed.\n");
++                    ret = ENOMEM;
++                    goto done;
++                }
++                n_dns++;
+             }
+-            n_dns++;
+-        } else if (ret == ENOENT) {
+-            missing_groups[n_missing] = talloc_strdup(missing_groups,
+-                                                      groups[c]);
+-            if (missing_groups[n_missing] == NULL) {
+-                DEBUG(SSSDBG_OP_FAILURE, "talloc_strdup failed.\n");
+-                ret = ENOMEM;
+-                goto done;
+-            }
+-            n_missing++;
+         } else {
+             DEBUG(SSSDBG_OP_FAILURE, "sysdb_search_group_by_name failed.\n");
+             goto done;
+@@ -1803,7 +1819,9 @@ static void ipa_s2n_get_user_done(struct tevent_req *subreq)
+             }
+ 
+ 
+-            ret = get_group_dn_list(state, state->dom,
++            ret = get_group_dn_list(state,
++                                    is_default_view(state->ipa_ctx->view_name),
++                                    state->dom,
+                                     attrs->ngroups, attrs->groups,
+                                     &group_dn_list, &missing_list);
+             if (ret != EOK) {
+@@ -1832,8 +1850,10 @@ static void ipa_s2n_get_user_done(struct tevent_req *subreq)
+             }
+             break;
+         } else if (attrs->response_type == RESP_GROUP_MEMBERS) {
+-            ret = process_members(state->dom, NULL, attrs->a.group.gr_mem,
+-                                  state, &missing_list);
++            ret = process_members(state->dom,
++                                  is_default_view(state->ipa_ctx->view_name),
++                                  NULL, attrs->a.group.gr_mem, state,
++                                  &missing_list);
+             if (ret != EOK) {
+                 DEBUG(SSSDBG_OP_FAILURE, "process_members failed.\n");
+                 goto done;
+@@ -2572,8 +2592,9 @@ static errno_t ipa_s2n_save_objects(struct sss_domain_info *dom,
+                 }
+             }
+ 
+-            ret = process_members(dom, attrs->sysdb_attrs,
+-                                  attrs->a.group.gr_mem, NULL, NULL);
++            ret = process_members(dom, is_default_view(view_name),
++                                  attrs->sysdb_attrs, attrs->a.group.gr_mem,
++                                  NULL, NULL);
+             if (ret != EOK) {
+                 DEBUG(SSSDBG_OP_FAILURE, "process_members failed.\n");
+                 goto done;
+-- 
+2.14.3
+
diff --git a/SOURCES/0066-IPA-use-cache-searches-in-get_groups_dns.patch b/SOURCES/0066-IPA-use-cache-searches-in-get_groups_dns.patch
new file mode 100644
index 0000000..5268e1a
--- /dev/null
+++ b/SOURCES/0066-IPA-use-cache-searches-in-get_groups_dns.patch
@@ -0,0 +1,70 @@
+From 3500a7766f5443c9ec50f9c8de27e2dea8c0c234 Mon Sep 17 00:00:00 2001
+From: Sumit Bose <sbose@redhat.com>
+Date: Mon, 20 Nov 2017 16:41:29 +0100
+Subject: [PATCH 66/67] IPA: use cache searches in get_groups_dns()
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+If the group name is overridden in the default view we have to search
+for the name and cannot construct it because the extdom plugin will
+return the overridden name but the DN of the related group object in the
+cache will contain the original name.
+
+Related to https://pagure.io/SSSD/sssd/issue/3579
+
+Reviewed-by: Fabiano Fidêncio <fidencio@redhat.com>
+(cherry picked from commit f29040342a6d69e170f4543662621f2e27221f91)
+---
+ src/providers/ipa/ipa_s2n_exop.c | 27 +++++++++++++++++++--------
+ 1 file changed, 19 insertions(+), 8 deletions(-)
+
+diff --git a/src/providers/ipa/ipa_s2n_exop.c b/src/providers/ipa/ipa_s2n_exop.c
+index c6132f509dcc8e7af84e03e8bfe20701107d1392..49c393e9a1eb19ab683949cf633a6838274bc0fe 100644
+--- a/src/providers/ipa/ipa_s2n_exop.c
++++ b/src/providers/ipa/ipa_s2n_exop.c
+@@ -2038,6 +2038,7 @@ static errno_t get_groups_dns(TALLOC_CTX *mem_ctx, struct sss_domain_info *dom,
+     int c;
+     struct sss_domain_info *root_domain;
+     char **dn_list;
++    struct ldb_message *msg;
+ 
+     if (name_list == NULL) {
+         *_dn_list = NULL;
+@@ -2082,15 +2083,25 @@ static errno_t get_groups_dns(TALLOC_CTX *mem_ctx, struct sss_domain_info *dom,
+             goto done;
+         }
+ 
+-        /* This might fail if some unexpected cases are used. But current
+-         * sysdb code which handles group membership constructs DNs this way
+-         * as well, IPA names are lowercased and AD names by default will be
+-         * lowercased as well. If there are really use-cases which cause an
+-         * issue here, sysdb_group_strdn() has to be replaced by a proper
+-         * search. */
+-        dn_list[c] = sysdb_group_strdn(dn_list, dom->name, name_list[c]);
++        /* If the group name is overridden in the default view we have to
++         * search for the name and cannot construct it because the extdom
++         * plugin will return the overridden name but the DN of the related
++         * group object in the cache will contain the original name. */
++
++        ret = sysdb_search_group_by_name(tmp_ctx, dom, name_list[c], NULL,
++                                         &msg);
++        if (ret == EOK) {
++            dn_list[c] = ldb_dn_alloc_linearized(dn_list, msg->dn);
++        } else {
++            /* best effort, try to construct the DN */
++            DEBUG(SSSDBG_TRACE_FUNC,
++                  "sysdb_search_group_by_name failed with [%d], "
++                  "generating DN for [%s] in domain [%s].\n",
++                  ret, name_list[c], dom->name);
++            dn_list[c] = sysdb_group_strdn(dn_list, dom->name, name_list[c]);
++        }
+         if (dn_list[c] == NULL) {
+-            DEBUG(SSSDBG_OP_FAILURE, "sysdb_group_strdn failed.\n");
++            DEBUG(SSSDBG_OP_FAILURE, "ldb_dn_alloc_linearized failed.\n");
+             ret = ENOMEM;
+             goto done;
+         }
+-- 
+2.14.3
+
diff --git a/SOURCES/0066-PAM-Remove-unneeded-memory-context.patch b/SOURCES/0066-PAM-Remove-unneeded-memory-context.patch
deleted file mode 100644
index 64ba6af..0000000
--- a/SOURCES/0066-PAM-Remove-unneeded-memory-context.patch
+++ /dev/null
@@ -1,58 +0,0 @@
-From d11e7faa2a3464ed921ccf88a02e0a48871484b4 Mon Sep 17 00:00:00 2001
-From: Jakub Hrozek <jhrozek@redhat.com>
-Date: Fri, 24 Mar 2017 20:36:06 +0100
-Subject: [PATCH 66/72] PAM: Remove unneeded memory context
-
-Since we only store data into pam_ctx in get_public_domains(), it
-doesn't make sense to allow passing a separate memory context. It is
-always going to be pam_ctx, otherwise the memory hierarchy will cause
-issues anyway.
-
-Reviewed-by: Sumit Bose <sbose@redhat.com>
----
- src/responder/pam/pamsrv.c | 8 ++++----
- 1 file changed, 4 insertions(+), 4 deletions(-)
-
-diff --git a/src/responder/pam/pamsrv.c b/src/responder/pam/pamsrv.c
-index 816f2293130ff8761ca94b4a42ca93063c11ea35..ab3f4545520f3fcb2492a6089a039c46f0fb847f 100644
---- a/src/responder/pam/pamsrv.c
-+++ b/src/responder/pam/pamsrv.c
-@@ -122,7 +122,7 @@ done:
-     return ret;
- }
- 
--static errno_t get_public_domains(TALLOC_CTX *mem_ctx, struct pam_ctx *pctx)
-+static errno_t get_public_domains(struct pam_ctx *pctx)
- {
-     char *domains_str = NULL;
-     errno_t ret;
-@@ -137,7 +137,7 @@ static errno_t get_public_domains(TALLOC_CTX *mem_ctx, struct pam_ctx *pctx)
- 
-     if (strcmp(domains_str, ALL_DOMAIMS_ARE_PUBLIC) == 0) { /* all */
-         /* copy all domains */
--        ret = get_dom_names(mem_ctx,
-+        ret = get_dom_names(pctx,
-                             pctx->rctx->domains,
-                             &pctx->public_domains,
-                             &pctx->public_domains_count);
-@@ -149,7 +149,7 @@ static errno_t get_public_domains(TALLOC_CTX *mem_ctx, struct pam_ctx *pctx)
-         pctx->public_domains = NULL;
-         pctx->public_domains_count = 0;
-     } else {
--        ret = split_on_separator(mem_ctx, domains_str, ',', true, false,
-+        ret = split_on_separator(pctx, domains_str, ',', true, false,
-                                  &pctx->public_domains,
-                                  &pctx->public_domains_count);
-         if (ret != EOK) {
-@@ -212,7 +212,7 @@ static int pam_process_init(TALLOC_CTX *mem_ctx,
-         goto done;
-     }
- 
--    ret = get_public_domains(pctx, pctx);
-+    ret = get_public_domains(pctx);
-     if (ret != EOK) {
-         DEBUG(SSSDBG_FATAL_FAILURE, "get_public_domains failed: %d:[%s].\n",
-               ret, sss_strerror(ret));
--- 
-2.9.3
-
diff --git a/SOURCES/0067-PAM-Add-application-services.patch b/SOURCES/0067-PAM-Add-application-services.patch
deleted file mode 100644
index 2c8fddd..0000000
--- a/SOURCES/0067-PAM-Add-application-services.patch
+++ /dev/null
@@ -1,452 +0,0 @@
-From 855201c70f69f2b1dbcb3faef780fbdb84354f18 Mon Sep 17 00:00:00 2001
-From: Jakub Hrozek <jhrozek@redhat.com>
-Date: Sun, 26 Mar 2017 18:28:41 +0200
-Subject: [PATCH 67/72] PAM: Add application services
-
-Related to:
-https://pagure.io/SSSD/sssd/issue/3310
-
-Adds a new PAM responder option 'pam_app_services'. This option can hold
-a list of PAM services that are allowed to contact the application
-non-POSIX domains. These services are NOT allowed to contact any of the
-POSIX domains.
-
-Reviewed-by: Sumit Bose <sbose@redhat.com>
----
- src/confdb/confdb.h                  |   1 +
- src/config/SSSDConfig/__init__.py.in |   1 +
- src/config/cfg_rules.ini             |   1 +
- src/config/etc/sssd.api.conf         |   1 +
- src/man/sssd.conf.5.xml              |  12 +++
- src/responder/pam/pamsrv.c           |  33 +++++++
- src/responder/pam/pamsrv.h           |   5 ++
- src/responder/pam/pamsrv_cmd.c       |  26 +++++-
- src/tests/cmocka/test_pam_srv.c      | 167 ++++++++++++++++++++++++++++++++++-
- 9 files changed, 241 insertions(+), 6 deletions(-)
-
-diff --git a/src/confdb/confdb.h b/src/confdb/confdb.h
-index 5a8d377c312f641f544b1c7cf38826192462ea3c..8719c239362b371fcdb1b78956bcddde871f141b 100644
---- a/src/confdb/confdb.h
-+++ b/src/confdb/confdb.h
-@@ -129,6 +129,7 @@
- #define CONFDB_PAM_CERT_AUTH "pam_cert_auth"
- #define CONFDB_PAM_CERT_DB_PATH "pam_cert_db_path"
- #define CONFDB_PAM_P11_CHILD_TIMEOUT "p11_child_timeout"
-+#define CONFDB_PAM_APP_SERVICES "pam_app_services"
- 
- /* SUDO */
- #define CONFDB_SUDO_CONF_ENTRY "config/sudo"
-diff --git a/src/config/SSSDConfig/__init__.py.in b/src/config/SSSDConfig/__init__.py.in
-index 806611b6076048c08ce08c772dbd3cea5fdd656c..211338778e81c1c60ffb3cdbc67c9619343d7798 100644
---- a/src/config/SSSDConfig/__init__.py.in
-+++ b/src/config/SSSDConfig/__init__.py.in
-@@ -102,6 +102,7 @@ option_strings = {
-     'pam_cert_auth' : _('Allow certificate based/Smartcard authentication.'),
-     'pam_cert_db_path' : _('Path to certificate databse with PKCS#11 modules.'),
-     'p11_child_timeout' : _('How many seconds will pam_sss wait for p11_child to finish'),
-+    'pam_app_services' : _('Which PAM services are permitted to contact application domains'),
- 
-     # [sudo]
-     'sudo_timed' : _('Whether to evaluate the time-based attributes in sudo rules'),
-diff --git a/src/config/cfg_rules.ini b/src/config/cfg_rules.ini
-index 8fd2d2c5236246394353a88c50d1510bd6233f77..1a749db754cedd87f263f7ae596d6f8238bb4357 100644
---- a/src/config/cfg_rules.ini
-+++ b/src/config/cfg_rules.ini
-@@ -119,6 +119,7 @@ option = pam_account_locked_message
- option = pam_cert_auth
- option = pam_cert_db_path
- option = p11_child_timeout
-+option = pam_app_services
- 
- [rule/allowed_sudo_options]
- validator = ini_allowed_options
-diff --git a/src/config/etc/sssd.api.conf b/src/config/etc/sssd.api.conf
-index a38b24208f89e4502e41625c540ea9958d5bbffe..a1a0c2992925a4c7df86832117eec2a0cf7894c9 100644
---- a/src/config/etc/sssd.api.conf
-+++ b/src/config/etc/sssd.api.conf
-@@ -73,6 +73,7 @@ pam_account_locked_message = str, None, false
- pam_cert_auth = bool, None, false
- pam_cert_db_path = str, None, false
- p11_child_timeout = int, None, false
-+pam_app_services = str, None, false
- 
- [sudo]
- # sudo service
-diff --git a/src/man/sssd.conf.5.xml b/src/man/sssd.conf.5.xml
-index 8294793c765bfa6bf481693c7d7f206950454681..c4e30396f16c40db37af2f56ac218b6e37201ef7 100644
---- a/src/man/sssd.conf.5.xml
-+++ b/src/man/sssd.conf.5.xml
-@@ -1325,6 +1325,18 @@ pam_account_locked_message = Account locked, please contact help desk.
-                         </para>
-                     </listitem>
-                 </varlistentry>
-+                <varlistentry>
-+                    <term>pam_app_services (string)</term>
-+                    <listitem>
-+                        <para>
-+                            Which PAM services are permitted to contact
-+                            domains of type <quote>application</quote>
-+                        </para>
-+                        <para>
-+                            Default: Not set
-+                        </para>
-+                    </listitem>
-+                </varlistentry>
- 
-             </variablelist>
-         </refsect2>
-diff --git a/src/responder/pam/pamsrv.c b/src/responder/pam/pamsrv.c
-index ab3f4545520f3fcb2492a6089a039c46f0fb847f..79470823d18138da6ef9235e6336a3220ead1797 100644
---- a/src/responder/pam/pamsrv.c
-+++ b/src/responder/pam/pamsrv.c
-@@ -166,6 +166,32 @@ done:
-     return ret;
- }
- 
-+static errno_t get_app_services(struct pam_ctx *pctx)
-+{
-+    errno_t ret;
-+
-+    ret = confdb_get_string_as_list(pctx->rctx->cdb, pctx,
-+                                    CONFDB_PAM_CONF_ENTRY,
-+                                    CONFDB_PAM_APP_SERVICES,
-+                                    &pctx->app_services);
-+    if (ret == ENOENT) {
-+        pctx->app_services = talloc_zero_array(pctx, char *, 1);
-+        if (pctx->app_services == NULL) {
-+            return ENOMEM;
-+        }
-+        /* Allocating an empty array makes it easier for the consumer
-+         * to iterate over it
-+         */
-+    } else if (ret != EOK) {
-+        DEBUG(SSSDBG_CRIT_FAILURE,
-+              "Cannot read "CONFDB_PAM_APP_SERVICES" [%d]: %s\n",
-+              ret, sss_strerror(ret));
-+        return ret;
-+    }
-+
-+    return EOK;
-+}
-+
- static int pam_process_init(TALLOC_CTX *mem_ctx,
-                             struct tevent_context *ev,
-                             struct confdb_ctx *cdb,
-@@ -219,6 +245,13 @@ static int pam_process_init(TALLOC_CTX *mem_ctx,
-         goto done;
-     }
- 
-+    ret = get_app_services(pctx);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_FATAL_FAILURE, "get_app_services failed: %d:[%s].\n",
-+              ret, sss_strerror(ret));
-+        goto done;
-+    }
-+
-     /* Enable automatic reconnection to the Data Provider */
- 
-     /* FIXME: "retries" is too generic, either get it from a global config
-diff --git a/src/responder/pam/pamsrv.h b/src/responder/pam/pamsrv.h
-index b3eb56441048ecdba82866a95f1d6d6d5e786c60..b569748fe2a2005cee5df34bef55e803175492a9 100644
---- a/src/responder/pam/pamsrv.h
-+++ b/src/responder/pam/pamsrv.h
-@@ -26,6 +26,7 @@
- #include "util/util.h"
- #include "sbus/sssd_dbus.h"
- #include "responder/common/responder.h"
-+#include "responder/common/cache_req/cache_req.h"
- 
- struct pam_auth_req;
- 
-@@ -42,6 +43,9 @@ struct pam_ctx {
-     char **public_domains;
-     int public_domains_count;
- 
-+    /* What services are permitted to access application domains */
-+    char **app_services;
-+
-     bool cert_auth;
-     int p11_child_debug_fd;
-     char *nss_db;
-@@ -54,6 +58,7 @@ struct pam_auth_dp_req {
- struct pam_auth_req {
-     struct cli_ctx *cctx;
-     struct sss_domain_info *domain;
-+    enum cache_req_dom_type req_dom_type;
- 
-     struct pam_data *pd;
- 
-diff --git a/src/responder/pam/pamsrv_cmd.c b/src/responder/pam/pamsrv_cmd.c
-index fa6d2cc10fe1404196f9d9221a469d7a9a768211..f2b3c74b483e527932dda42279d14a9ac184b475 100644
---- a/src/responder/pam/pamsrv_cmd.c
-+++ b/src/responder/pam/pamsrv_cmd.c
-@@ -1161,6 +1161,25 @@ static bool is_domain_public(char *name,
-     return false;
- }
- 
-+static enum cache_req_dom_type
-+get_domain_request_type(struct pam_auth_req *preq,
-+                        struct pam_ctx *pctx)
-+{
-+    enum cache_req_dom_type req_dom_type;
-+
-+    /* By default, only POSIX domains are to be contacted */
-+    req_dom_type = CACHE_REQ_POSIX_DOM;
-+
-+    for (int i = 0; pctx->app_services[i]; i++) {
-+        if (strcmp(pctx->app_services[i], preq->pd->service) == 0) {
-+            req_dom_type = CACHE_REQ_APPLICATION_DOM;
-+            break;
-+        }
-+    }
-+
-+    return req_dom_type;
-+}
-+
- static errno_t check_cert(TALLOC_CTX *mctx,
-                           struct tevent_context *ev,
-                           struct pam_ctx *pctx,
-@@ -1257,6 +1276,9 @@ static int pam_forwarder(struct cli_ctx *cctx, int pam_cmd)
-         goto done;
-     }
- 
-+    /* Determine what domain type to contact */
-+    preq->req_dom_type = get_domain_request_type(preq, pctx);
-+
-     /* try backend first for authentication before doing local Smartcard
-      * authentication */
-     if (pd->cmd != SSS_PAM_AUTHENTICATE && may_do_cert_auth(pctx, pd)) {
-@@ -1316,7 +1338,7 @@ static void pam_forwarder_cert_cb(struct tevent_req *req)
- 
-     req = cache_req_user_by_cert_send(preq, cctx->ev, cctx->rctx,
-                                       pctx->rctx->ncache, 0,
--                                      CACHE_REQ_POSIX_DOM, NULL,
-+                                      preq->req_dom_type, NULL,
-                                       cert);
-     if (req == NULL) {
-         DEBUG(SSSDBG_OP_FAILURE, "cache_req_user_by_cert_send failed.\n");
-@@ -1509,7 +1531,7 @@ static int pam_check_user_search(struct pam_auth_req *preq)
-                            preq->cctx->rctx,
-                            preq->cctx->rctx->ncache,
-                            0,
--                           CACHE_REQ_POSIX_DOM,
-+                           preq->req_dom_type,
-                            preq->pd->domain,
-                            data);
-     if (!dpreq) {
-diff --git a/src/tests/cmocka/test_pam_srv.c b/src/tests/cmocka/test_pam_srv.c
-index 847419658bb983e6548722d6fa6fb22c63ee86b8..d249b8f1ea48f1c17b461c3add9e8c63774e5f88 100644
---- a/src/tests/cmocka/test_pam_srv.c
-+++ b/src/tests/cmocka/test_pam_srv.c
-@@ -186,6 +186,15 @@ struct pam_ctx *mock_pctx(TALLOC_CTX *mem_ctx)
-     ret = sss_hash_create(pctx, 10, &pctx->id_table);
-     assert_int_equal(ret, EOK);
- 
-+    /* Two NULLs so that tests can just assign a const to the first slot
-+     * should they need it. The code iterates until first NULL anyway
-+     */
-+    pctx->app_services = talloc_zero_array(pctx, char *, 2);
-+    if (pctx->app_services == NULL) {
-+        talloc_free(pctx);
-+        return NULL;
-+    }
-+
-     return pctx;
- }
- 
-@@ -495,8 +504,12 @@ int __wrap_pam_dp_send_req(struct pam_auth_req *preq, int timeout)
-     return EOK;
- }
- 
--static void mock_input_pam(TALLOC_CTX *mem_ctx, const char *name,
--                           const char *pwd, const char *fa2)
-+static void mock_input_pam_ex(TALLOC_CTX *mem_ctx,
-+                              const char *name,
-+                              const char *pwd,
-+                              const char *fa2,
-+                              const char *svc,
-+                              bool contact_dp)
- {
-     size_t buf_size;
-     uint8_t *m_buf;
-@@ -536,7 +549,10 @@ static void mock_input_pam(TALLOC_CTX *mem_ctx, const char *name,
-         }
-     }
- 
--    pi.pam_service = "pam_test_service";
-+    if (svc == NULL) {
-+        svc = "pam_test_service";
-+    }
-+    pi.pam_service = svc;
-     pi.pam_service_size = strlen(pi.pam_service) + 1;
-     pi.pam_tty = "/dev/tty";
-     pi.pam_tty_size = strlen(pi.pam_tty) + 1;
-@@ -559,7 +575,17 @@ static void mock_input_pam(TALLOC_CTX *mem_ctx, const char *name,
-     will_return(__wrap_sss_packet_get_body, buf_size);
- 
-     mock_parse_inp(name, NULL, EOK);
--    mock_account_recv_simple();
-+    if (contact_dp) {
-+        mock_account_recv_simple();
-+    }
-+}
-+
-+static void mock_input_pam(TALLOC_CTX *mem_ctx,
-+                           const char *name,
-+                           const char *pwd,
-+                           const char *fa2)
-+{
-+    return mock_input_pam_ex(mem_ctx, name, pwd, fa2, NULL, true);
- }
- 
- static void mock_input_pam_cert(TALLOC_CTX *mem_ctx, const char *name,
-@@ -2097,6 +2123,127 @@ void test_filter_response(void **state)
-     talloc_free(pd);
- }
- 
-+static int pam_test_setup_appsvc_posix_dom(void **state)
-+{
-+    int ret;
-+
-+    ret = pam_test_setup(state);
-+    if (ret != EOK) {
-+        return ret;
-+    }
-+
-+    /* This config option is only read on startup, which is not executed
-+     * in test, so we can't just pass in a param
-+     */
-+    pam_test_ctx->pctx->app_services[0] = discard_const("app_svc");
-+    return 0;
-+}
-+
-+void test_appsvc_posix_dom(void **state)
-+{
-+    int ret;
-+
-+    /* The domain is POSIX, the request will skip over it */
-+    mock_input_pam_ex(pam_test_ctx, "pamuser", NULL, NULL, "app_svc", false);
-+    pam_test_ctx->exp_pam_status = PAM_USER_UNKNOWN;
-+
-+    will_return(__wrap_sss_packet_get_cmd, SSS_PAM_AUTHENTICATE);
-+    will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL);
-+
-+    set_cmd_cb(test_pam_user_unknown_check);
-+    ret = sss_cmd_execute(pam_test_ctx->cctx, SSS_PAM_AUTHENTICATE,
-+                          pam_test_ctx->pam_cmds);
-+    assert_int_equal(ret, EOK);
-+
-+    ret = test_ev_loop(pam_test_ctx->tctx);
-+    assert_int_equal(ret, EOK);
-+}
-+
-+void test_not_appsvc_posix_dom(void **state)
-+{
-+    int ret;
-+
-+    /* A different service than the app one can authenticate against a POSIX domain */
-+    mock_input_pam_ex(pam_test_ctx, "pamuser", NULL, NULL, "not_app_svc", true);
-+
-+    will_return(__wrap_sss_packet_get_cmd, SSS_PAM_AUTHENTICATE);
-+    will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL);
-+
-+    set_cmd_cb(test_pam_simple_check);
-+    ret = sss_cmd_execute(pam_test_ctx->cctx, SSS_PAM_AUTHENTICATE,
-+                          pam_test_ctx->pam_cmds);
-+    assert_int_equal(ret, EOK);
-+
-+    /* Wait until the test finishes with EOK */
-+    ret = test_ev_loop(pam_test_ctx->tctx);
-+    assert_int_equal(ret, EOK);
-+}
-+
-+static int pam_test_setup_appsvc_app_dom(void **state)
-+{
-+    struct sss_test_conf_param dom_params[] = {
-+        { "domain_type", "application" },
-+        { NULL, NULL },             /* Sentinel */
-+    };
-+    struct sss_test_conf_param pam_params[] = {
-+        { NULL, NULL },             /* Sentinel */
-+    };
-+    struct sss_test_conf_param monitor_params[] = {
-+        { NULL, NULL },             /* Sentinel */
-+    };
-+
-+
-+    test_pam_setup(dom_params, pam_params, monitor_params, state);
-+    pam_test_setup_common();
-+
-+    /* This config option is only read on startup, which is not executed
-+     * in test, so we can't just pass in a param
-+     */
-+    pam_test_ctx->pctx->app_services[0] = discard_const("app_svc");
-+    return 0;
-+}
-+
-+void test_appsvc_app_dom(void **state)
-+{
-+    int ret;
-+
-+    /* The domain is POSIX, the request will skip over it */
-+    mock_input_pam_ex(pam_test_ctx, "pamuser", NULL, NULL, "app_svc", true);
-+
-+    will_return(__wrap_sss_packet_get_cmd, SSS_PAM_AUTHENTICATE);
-+    will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL);
-+
-+    set_cmd_cb(test_pam_simple_check);
-+    ret = sss_cmd_execute(pam_test_ctx->cctx, SSS_PAM_AUTHENTICATE,
-+                          pam_test_ctx->pam_cmds);
-+    assert_int_equal(ret, EOK);
-+
-+    /* Wait until the test finishes with EOK */
-+    ret = test_ev_loop(pam_test_ctx->tctx);
-+    assert_int_equal(ret, EOK);
-+}
-+
-+void test_not_appsvc_app_dom(void **state)
-+{
-+    int ret;
-+
-+    /* A different service than the app one can authenticate against a POSIX domain */
-+    mock_input_pam_ex(pam_test_ctx, "pamuser", NULL, NULL, "not_app_svc", false);
-+
-+    pam_test_ctx->exp_pam_status = PAM_USER_UNKNOWN;
-+
-+    will_return(__wrap_sss_packet_get_cmd, SSS_PAM_AUTHENTICATE);
-+    will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL);
-+
-+    set_cmd_cb(test_pam_user_unknown_check);
-+    ret = sss_cmd_execute(pam_test_ctx->cctx, SSS_PAM_AUTHENTICATE,
-+                          pam_test_ctx->pam_cmds);
-+    assert_int_equal(ret, EOK);
-+
-+    ret = test_ev_loop(pam_test_ctx->tctx);
-+    assert_int_equal(ret, EOK);
-+}
-+
- int main(int argc, const char *argv[])
- {
-     int rv;
-@@ -2216,6 +2363,18 @@ int main(int argc, const char *argv[])
- 
-         cmocka_unit_test_setup_teardown(test_filter_response,
-                                         pam_test_setup, pam_test_teardown),
-+        cmocka_unit_test_setup_teardown(test_appsvc_posix_dom,
-+                                        pam_test_setup_appsvc_posix_dom,
-+                                        pam_test_teardown),
-+        cmocka_unit_test_setup_teardown(test_not_appsvc_posix_dom,
-+                                        pam_test_setup_appsvc_posix_dom,
-+                                        pam_test_teardown),
-+        cmocka_unit_test_setup_teardown(test_appsvc_app_dom,
-+                                        pam_test_setup_appsvc_app_dom,
-+                                        pam_test_teardown),
-+        cmocka_unit_test_setup_teardown(test_not_appsvc_app_dom,
-+                                        pam_test_setup_appsvc_posix_dom,
-+                                        pam_test_teardown),
-     };
- 
-     /* Set debug level to invalid value so we can deside if -d 0 was used. */
--- 
-2.9.3
-
diff --git a/SOURCES/0067-ipa-compare-DNs-instead-of-group-names-in-ipa_s2n_sa.patch b/SOURCES/0067-ipa-compare-DNs-instead-of-group-names-in-ipa_s2n_sa.patch
new file mode 100644
index 0000000..cc19a9e
--- /dev/null
+++ b/SOURCES/0067-ipa-compare-DNs-instead-of-group-names-in-ipa_s2n_sa.patch
@@ -0,0 +1,86 @@
+From 118860519777791368520f4e92ecbf2ef60cb7db Mon Sep 17 00:00:00 2001
+From: Sumit Bose <sbose@redhat.com>
+Date: Mon, 20 Nov 2017 16:45:45 +0100
+Subject: [PATCH 67/67] ipa: compare DNs instead of group names in
+ ipa_s2n_save_objects()
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+If group names are used to compare the current list of group memberships
+returned by the server with the one from the cache some groups might end
+up in the wrong result list if group names are overridden. This
+ambiguity can be resolved by using the DNs of the cached objects.
+
+Related to https://pagure.io/SSSD/sssd/issue/3579
+
+Reviewed-by: Fabiano Fidêncio <fidencio@redhat.com>
+(cherry picked from commit a52226c651308a0a7732544b492eb4db56b84f1d)
+---
+ src/providers/ipa/ipa_s2n_exop.c | 31 ++++++++++++-------------------
+ 1 file changed, 12 insertions(+), 19 deletions(-)
+
+diff --git a/src/providers/ipa/ipa_s2n_exop.c b/src/providers/ipa/ipa_s2n_exop.c
+index 49c393e9a1eb19ab683949cf633a6838274bc0fe..8b97f78620f19b0708e8a480cb72fd7f12d96dfb 100644
+--- a/src/providers/ipa/ipa_s2n_exop.c
++++ b/src/providers/ipa/ipa_s2n_exop.c
+@@ -2185,10 +2185,9 @@ static errno_t ipa_s2n_save_objects(struct sss_domain_info *dom,
+     struct ldb_result *res;
+     enum sysdb_member_type type;
+     char **sysdb_grouplist;
+-    char **add_groups;
+     char **add_groups_dns;
+-    char **del_groups;
+     char **del_groups_dns;
++    char **groups_dns;
+     bool in_transaction = false;
+     int tret;
+     struct sysdb_attrs *gid_override_attrs = NULL;
+@@ -2514,33 +2513,27 @@ static errno_t ipa_s2n_save_objects(struct sss_domain_info *dom,
+             }
+ 
+             if (attrs->response_type == RESP_USER_GROUPLIST) {
+-                ret = get_sysdb_grouplist(tmp_ctx, dom->sysdb, dom, name,
+-                                          &sysdb_grouplist);
++                ret = get_sysdb_grouplist_dn(tmp_ctx, dom->sysdb, dom, name,
++                                             &sysdb_grouplist);
+                 if (ret != EOK) {
+                     DEBUG(SSSDBG_OP_FAILURE, "get_sysdb_grouplist failed.\n");
+                     goto done;
+                 }
+ 
+-                ret = diff_string_lists(tmp_ctx, attrs->groups,
+-                                        sysdb_grouplist, &add_groups,
+-                                        &del_groups, NULL);
++                ret = get_groups_dns(tmp_ctx, dom, attrs->groups, &groups_dns);
++                if (ret != EOK) {
++                    DEBUG(SSSDBG_OP_FAILURE, "get_groups_dns failed.\n");
++                    goto done;
++                }
++
++                ret = diff_string_lists(tmp_ctx, groups_dns,
++                                        sysdb_grouplist, &add_groups_dns,
++                                        &del_groups_dns, NULL);
+                 if (ret != EOK) {
+                     DEBUG(SSSDBG_OP_FAILURE, "diff_string_lists failed.\n");
+                     goto done;
+                 }
+ 
+-                ret = get_groups_dns(tmp_ctx, dom, add_groups, &add_groups_dns);
+-                if (ret != EOK) {
+-                    DEBUG(SSSDBG_OP_FAILURE, "get_groups_dns failed.\n");
+-                    goto done;
+-                }
+-
+-                ret = get_groups_dns(tmp_ctx, dom, del_groups, &del_groups_dns);
+-                if (ret != EOK) {
+-                    DEBUG(SSSDBG_OP_FAILURE, "get_groups_dns failed.\n");
+-                    goto done;
+-                }
+-
+                 DEBUG(SSSDBG_TRACE_INTERNAL, "Updating memberships for %s\n",
+                                              name);
+                 ret = sysdb_update_members_dn(dom, name, SYSDB_MEMBER_USER,
+-- 
+2.14.3
+
diff --git a/SOURCES/0068-SDAP-Split-out-utility-function-sdap_get_object_doma.patch b/SOURCES/0068-SDAP-Split-out-utility-function-sdap_get_object_doma.patch
new file mode 100644
index 0000000..24ee010
--- /dev/null
+++ b/SOURCES/0068-SDAP-Split-out-utility-function-sdap_get_object_doma.patch
@@ -0,0 +1,92 @@
+From 0e5d9f481daeeaecefeb68cdc03e45a11dfd7091 Mon Sep 17 00:00:00 2001
+From: Jakub Hrozek <jhrozek@redhat.com>
+Date: Tue, 7 Nov 2017 17:03:13 +0100
+Subject: [PATCH 68/83] SDAP: Split out utility function
+ sdap_get_object_domain() from sdap_object_in_domain()
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+The DP request that returns a domain of an entry to responder will need
+this functionality in order to map the original DN of the entry found
+to a domain name.
+
+Reviewed-by: Pavel Březina <pbrezina@redhat.com>
+Reviewed-by: Sumit Bose <sbose@redhat.com>
+(cherry picked from commit 37fdd9dc1ad5968067f8e3c43a51ed2ac9f3b104)
+---
+ src/providers/ldap/sdap.c | 26 ++++++++++++++++++++------
+ src/providers/ldap/sdap.h |  4 ++++
+ 2 files changed, 24 insertions(+), 6 deletions(-)
+
+diff --git a/src/providers/ldap/sdap.c b/src/providers/ldap/sdap.c
+index b6b1c91cb7507ebb95cd559634a77ed44dfb5fc0..59d24fed53cc35751b5c24679e247a42f82e1d0a 100644
+--- a/src/providers/ldap/sdap.c
++++ b/src/providers/ldap/sdap.c
+@@ -1673,9 +1673,9 @@ char *sdap_make_oc_list(TALLOC_CTX *mem_ctx, struct sdap_attr_map *map)
+     }
+ }
+ 
+-bool sdap_object_in_domain(struct sdap_options *opts,
+-                           struct sysdb_attrs *obj,
+-                           struct sss_domain_info *dom)
++struct sss_domain_info *sdap_get_object_domain(struct sdap_options *opts,
++                                               struct sysdb_attrs *obj,
++                                               struct sss_domain_info *dom)
+ {
+     errno_t ret;
+     const char *original_dn = NULL;
+@@ -1685,7 +1685,7 @@ bool sdap_object_in_domain(struct sdap_options *opts,
+     if (ret) {
+         DEBUG(SSSDBG_FUNC_DATA,
+               "The group has no original DN, assuming our domain\n");
+-        return true;
++        return dom;
+     }
+ 
+     sdmatch = sdap_domain_get_by_dn(opts, original_dn);
+@@ -1693,10 +1693,24 @@ bool sdap_object_in_domain(struct sdap_options *opts,
+         DEBUG(SSSDBG_FUNC_DATA,
+               "The original DN of the group cannot "
+               "be related to any search base\n");
+-        return true;
++        return dom;
+     }
+ 
+-    return (sdmatch->dom == dom);
++    return sdmatch->dom;
++}
++
++bool sdap_object_in_domain(struct sdap_options *opts,
++                           struct sysdb_attrs *obj,
++                           struct sss_domain_info *dom)
++{
++    struct sss_domain_info *obj_dom;
++
++    obj_dom = sdap_get_object_domain(opts, obj, dom);
++    if (obj_dom == NULL) {
++        return false;
++    }
++
++    return (obj_dom == dom);
+ }
+ 
+ size_t sdap_steal_objects_in_dom(struct sdap_options *opts,
+diff --git a/src/providers/ldap/sdap.h b/src/providers/ldap/sdap.h
+index 2ba016ff52313198287ac5196e24517333882099..8b0f1f0ce0fef59554270f0f31cfd2d5f0aa57f5 100644
+--- a/src/providers/ldap/sdap.h
++++ b/src/providers/ldap/sdap.h
+@@ -644,6 +644,10 @@ size_t sdap_steal_objects_in_dom(struct sdap_options *opts,
+                                  size_t count,
+                                  bool filter);
+ 
++struct sss_domain_info *sdap_get_object_domain(struct sdap_options *opts,
++                                               struct sysdb_attrs *obj,
++                                               struct sss_domain_info *dom);
++
+ bool sdap_object_in_domain(struct sdap_options *opts,
+                            struct sysdb_attrs *obj,
+                            struct sss_domain_info *dom);
+-- 
+2.14.3
+
diff --git a/SOURCES/0068-SYSDB-Allow-storing-non-POSIX-users.patch b/SOURCES/0068-SYSDB-Allow-storing-non-POSIX-users.patch
deleted file mode 100644
index 8ed534b..0000000
--- a/SOURCES/0068-SYSDB-Allow-storing-non-POSIX-users.patch
+++ /dev/null
@@ -1,152 +0,0 @@
-From ea8a4436b66877bbae1a73d11917ecdb3bf72718 Mon Sep 17 00:00:00 2001
-From: Jakub Hrozek <jhrozek@redhat.com>
-Date: Wed, 22 Mar 2017 13:00:31 +0100
-Subject: [PATCH 68/72] SYSDB: Allow storing non-POSIX users
-
-Related to:
-https://pagure.io/SSSD/sssd/issue/3310
-
-We already do the same for groups. If the user does not have UID number
-set but does have the POSIX: false attribute set, then we save the user
-with zero UID and the non-POSIX flag.
-
-Reviewed-by: Sumit Bose <sbose@redhat.com>
----
- src/db/sysdb_ops.c      | 32 ++++++++++++++++++++--------
- src/tests/sysdb-tests.c | 56 +++++++++++++++++++++++++++++++++++++++++++++++++
- 2 files changed, 79 insertions(+), 9 deletions(-)
-
-diff --git a/src/db/sysdb_ops.c b/src/db/sysdb_ops.c
-index 919f22370ff87eff2bf0bb569ca90f1ee699a61e..3cf9d903f25b9ccd506d7957c94040bdc7d658a3 100644
---- a/src/db/sysdb_ops.c
-+++ b/src/db/sysdb_ops.c
-@@ -1855,6 +1855,7 @@ int sysdb_add_user(struct sss_domain_info *domain,
-     struct sysdb_attrs *id_attrs;
-     uint32_t id;
-     int ret;
-+    bool posix;
- 
-     if (domain->mpg) {
-         if (gid != 0) {
-@@ -1926,7 +1927,28 @@ int sysdb_add_user(struct sss_domain_info *domain,
-         /* Not fatal */
-     }
- 
--    if (uid == 0) {
-+    if (!attrs) {
-+        attrs = sysdb_new_attrs(tmp_ctx);
-+        if (!attrs) {
-+            ret = ENOMEM;
-+            goto done;
-+        }
-+    }
-+
-+    ret = sysdb_attrs_get_bool(attrs, SYSDB_POSIX, &posix);
-+    if (ret == ENOENT) {
-+        posix = true;
-+        ret = sysdb_attrs_add_bool(attrs, SYSDB_POSIX, true);
-+        if (ret) {
-+            DEBUG(SSSDBG_TRACE_LIBS, "Failed to add posix attribute.\n");
-+            goto done;
-+        }
-+    } else if (ret != EOK) {
-+        DEBUG(SSSDBG_TRACE_LIBS, "Failed to get posix attribute.\n");
-+        goto done;
-+    }
-+
-+    if (uid == 0 && posix == true) {
-         ret = sysdb_get_new_id(domain, &id);
-         if (ret) goto done;
- 
-@@ -1948,14 +1970,6 @@ int sysdb_add_user(struct sss_domain_info *domain,
-         if (ret) goto done;
-     }
- 
--    if (!attrs) {
--        attrs = sysdb_new_attrs(tmp_ctx);
--        if (!attrs) {
--            ret = ENOMEM;
--            goto done;
--        }
--    }
--
-     if (!now) {
-         now = time(NULL);
-     }
-diff --git a/src/tests/sysdb-tests.c b/src/tests/sysdb-tests.c
-index 1767dc3c734c6b2e5f74564debd603e2442f491b..6ec82ce4ca5c4f918bc9f3144c21f33b270ea47e 100644
---- a/src/tests/sysdb-tests.c
-+++ b/src/tests/sysdb-tests.c
-@@ -1428,6 +1428,59 @@ START_TEST (test_sysdb_get_user_attr_subdomain)
- }
- END_TEST
- 
-+START_TEST (test_sysdb_add_nonposix_user)
-+{
-+    struct sysdb_test_ctx *test_ctx;
-+    const char *get_attrs[] = { SYSDB_GIDNUM,
-+                                SYSDB_UIDNUM,
-+                                SYSDB_POSIX,
-+                                NULL };
-+    struct ldb_result *res;
-+    const char *attrval;
-+    const char *username = "test_sysdb_add_nonposix_user";
-+    const char *fq_name;
-+    struct sysdb_attrs *user_attrs;
-+    int ret;
-+    uint64_t id;
-+
-+    /* Setup */
-+    ret = setup_sysdb_tests(&test_ctx);
-+    fail_if(ret != EOK, "Could not set up the test");
-+
-+    /* Create user */
-+    fq_name = sss_create_internal_fqname(test_ctx, username, test_ctx->domain->name);
-+    fail_if(fq_name == NULL, "Failed to create fq name.");
-+
-+    user_attrs = sysdb_new_attrs(test_ctx);
-+    fail_if(user_attrs == NULL);
-+
-+    ret = sysdb_attrs_add_bool(user_attrs, SYSDB_POSIX, false);
-+    fail_if(ret != EOK, "Could not add attribute");
-+
-+    ret = sysdb_add_user(test_ctx->domain, fq_name, 0, 0, "Gecos",
-+                         "/home/userhome", "/bin/bash", NULL, user_attrs, 0, 0);
-+    fail_if(ret != EOK, "sysdb_add_user failed.");
-+
-+    /* Test */
-+    ret = sysdb_get_user_attr(test_ctx, test_ctx->domain, fq_name,
-+                              get_attrs, &res);
-+    fail_if(ret != EOK, "Could not get user attributes.");
-+    fail_if(res->count != 1, "Invalid number of entries, expected 1, got %d",
-+            res->count);
-+
-+    attrval = ldb_msg_find_attr_as_string(res->msgs[0], SYSDB_POSIX, NULL);
-+    fail_if(strcasecmp(attrval, "false") != 0, "Got bad attribute value.");
-+
-+    id = ldb_msg_find_attr_as_uint64(res->msgs[0], SYSDB_UIDNUM, 123);
-+    fail_unless(id == 0, "Wrong UID value");
-+
-+    id = ldb_msg_find_attr_as_uint64(res->msgs[0], SYSDB_GIDNUM, 123);
-+    fail_unless(id == 0, "Wrong GID value");
-+
-+    talloc_free(test_ctx);
-+}
-+END_TEST
-+
- START_TEST (test_sysdb_add_group_member)
- {
-     struct sysdb_test_ctx *test_ctx;
-@@ -7044,6 +7097,9 @@ Suite *create_sysdb_suite(void)
-     /* Test GetUserAttr with subdomain user */
-     tcase_add_test(tc_sysdb, test_sysdb_get_user_attr_subdomain);
- 
-+    /* Test adding a non-POSIX user */
-+    tcase_add_test(tc_sysdb, test_sysdb_add_nonposix_user);
-+
- /* ===== NETGROUP TESTS ===== */
- 
-     /* Create a new netgroup */
--- 
-2.9.3
-
diff --git a/SOURCES/0069-LDAP-Extract-the-check-whether-to-run-a-POSIX-check-.patch b/SOURCES/0069-LDAP-Extract-the-check-whether-to-run-a-POSIX-check-.patch
new file mode 100644
index 0000000..1b64e0d
--- /dev/null
+++ b/SOURCES/0069-LDAP-Extract-the-check-whether-to-run-a-POSIX-check-.patch
@@ -0,0 +1,115 @@
+From 880552cc45e55c7ef9f81423aff8fe867451d752 Mon Sep 17 00:00:00 2001
+From: Jakub Hrozek <jhrozek@redhat.com>
+Date: Thu, 30 Nov 2017 11:47:30 +0100
+Subject: [PATCH 69/83] LDAP: Extract the check whether to run a POSIX check to
+ a function
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+This will reduce the code duplication in the following patches and will
+allow to keep all the logic on one place so that when/if we change the
+code in the future, we only have to change the single place.
+
+Reviewed-by: Pavel Březina <pbrezina@redhat.com>
+Reviewed-by: Sumit Bose <sbose@redhat.com>
+(cherry picked from commit 8e93ebb2a6f7644c389c1d1f4e92a21c4d0b2b45)
+---
+ src/providers/ldap/ldap_common.c     | 15 +++++++++++++++
+ src/providers/ldap/ldap_common.h     |  4 ++++
+ src/providers/ldap/ldap_id.c         | 15 ++++++---------
+ src/providers/ldap/sdap_async_enum.c |  7 +++----
+ 4 files changed, 28 insertions(+), 13 deletions(-)
+
+diff --git a/src/providers/ldap/ldap_common.c b/src/providers/ldap/ldap_common.c
+index 0597e91f7fade47aeb34565597c730ac406e0cfc..3eff3515d95043d4b59cb0d9953cf050355a0ca5 100644
+--- a/src/providers/ldap/ldap_common.c
++++ b/src/providers/ldap/ldap_common.c
+@@ -971,3 +971,18 @@ sdap_id_ctx_new(TALLOC_CTX *mem_ctx, struct be_ctx *bectx,
+ 
+     return sdap_ctx;
+ }
++
++bool should_run_posix_check(struct sdap_id_ctx *ctx,
++                            bool use_id_mapping,
++                            bool posix_request)
++{
++    if (use_id_mapping == false &&
++            posix_request == true &&
++            ctx->opts->schema_type == SDAP_SCHEMA_AD &&
++            ctx->srv_opts &&
++            ctx->srv_opts->posix_checked == false) {
++        return true;
++    }
++
++    return false;
++}
+diff --git a/src/providers/ldap/ldap_common.h b/src/providers/ldap/ldap_common.h
+index 0510b7d5ab5121bd96f699e8e59520a2a18a604f..fa7cda4df9d7334f6f0f5baccae0cba0478bfbea 100644
+--- a/src/providers/ldap/ldap_common.h
++++ b/src/providers/ldap/ldap_common.h
+@@ -304,6 +304,10 @@ char *get_enterprise_principal_string_filter(TALLOC_CTX *mem_ctx,
+                                              const char *princ,
+                                              struct dp_option *sdap_basic_opts);
+ 
++bool should_run_posix_check(struct sdap_id_ctx *ctx,
++                            bool id_mapping,
++                            bool posix_request);
++
+ char *sdap_get_access_filter(TALLOC_CTX *mem_ctx,
+                              const char *base_filter);
+ 
+diff --git a/src/providers/ldap/ldap_id.c b/src/providers/ldap/ldap_id.c
+index e89fc6133316f684810afe4c1a0731b8a04f2931..6ab9e0aa1db3eed32deb75211ded30a4cb48ca30 100644
+--- a/src/providers/ldap/ldap_id.c
++++ b/src/providers/ldap/ldap_id.c
+@@ -411,11 +411,9 @@ static void users_get_connect_done(struct tevent_req *subreq)
+     /* If POSIX attributes have been requested with an AD server and we
+      * have no idea about POSIX attributes support, run a one-time check
+      */
+-    if (state->use_id_mapping == false &&
+-            state->non_posix == false &&
+-            state->ctx->opts->schema_type == SDAP_SCHEMA_AD &&
+-            state->ctx->srv_opts &&
+-            state->ctx->srv_opts->posix_checked == false) {
++    if (should_run_posix_check(state->ctx,
++                               state->use_id_mapping,
++                               !state->non_posix)) {
+         subreq = sdap_posix_check_send(state, state->ev, state->ctx->opts,
+                                        sdap_id_op_handle(state->op),
+                                        state->sdom->user_search_bases,
+@@ -958,10 +956,9 @@ static void groups_get_connect_done(struct tevent_req *subreq)
+     /* If POSIX attributes have been requested with an AD server and we
+      * have no idea about POSIX attributes support, run a one-time check
+      */
+-    if (state->use_id_mapping == false &&
+-            state->ctx->opts->schema_type == SDAP_SCHEMA_AD &&
+-            state->ctx->srv_opts &&
+-            state->ctx->srv_opts->posix_checked == false) {
++    if (should_run_posix_check(state->ctx,
++                               state->use_id_mapping,
++                               !state->non_posix)) {
+         subreq = sdap_posix_check_send(state, state->ev, state->ctx->opts,
+                                        sdap_id_op_handle(state->op),
+                                        state->sdom->user_search_bases,
+diff --git a/src/providers/ldap/sdap_async_enum.c b/src/providers/ldap/sdap_async_enum.c
+index 91e481c4e694126900c729e86d187fba355de0b8..2cef4eb886f982ba388a34955bdd38468fe68200 100644
+--- a/src/providers/ldap/sdap_async_enum.c
++++ b/src/providers/ldap/sdap_async_enum.c
+@@ -196,10 +196,9 @@ static void sdap_dom_enum_ex_get_users(struct tevent_req *subreq)
+     /* If POSIX attributes have been requested with an AD server and we
+      * have no idea about POSIX attributes support, run a one-time check
+      */
+-    if (use_id_mapping == false &&
+-            state->ctx->opts->schema_type == SDAP_SCHEMA_AD &&
+-            state->ctx->srv_opts &&
+-            state->ctx->srv_opts->posix_checked == false) {
++    if (should_run_posix_check(state->ctx,
++                               use_id_mapping,
++                               true)) {
+         subreq = sdap_posix_check_send(state, state->ev, state->ctx->opts,
+                                        sdap_id_op_handle(state->user_op),
+                                        state->sdom->user_search_bases,
+-- 
+2.14.3
+
diff --git a/SOURCES/0069-SYSDB-Only-generate-new-UID-in-local-domain.patch b/SOURCES/0069-SYSDB-Only-generate-new-UID-in-local-domain.patch
deleted file mode 100644
index c6a76f3..0000000
--- a/SOURCES/0069-SYSDB-Only-generate-new-UID-in-local-domain.patch
+++ /dev/null
@@ -1,36 +0,0 @@
-From ee344275c041f68e943360c975e3356ba251cef8 Mon Sep 17 00:00:00 2001
-From: Jakub Hrozek <jhrozek@redhat.com>
-Date: Tue, 28 Mar 2017 14:49:31 +0200
-Subject: [PATCH 69/72] SYSDB: Only generate new UID in local domain
-
-To avoid issues where a user with no UID but without the posix=false
-flag was passed to sysdb, we only allow generating the new ID in the
-local domain. This might prevent bugs where non-POSIX users would get a
-UID created by sysdb which might allow accessing resources owned by that
-UID.
-
-Reviewed-by: Sumit Bose <sbose@redhat.com>
----
- src/db/sysdb_ops.c | 6 ++++++
- 1 file changed, 6 insertions(+)
-
-diff --git a/src/db/sysdb_ops.c b/src/db/sysdb_ops.c
-index 3cf9d903f25b9ccd506d7957c94040bdc7d658a3..4d7b2abd8026c90aaf4e7be687102e459cf3690e 100644
---- a/src/db/sysdb_ops.c
-+++ b/src/db/sysdb_ops.c
-@@ -1422,6 +1422,12 @@ int sysdb_get_new_id(struct sss_domain_info *domain,
-         return ENOMEM;
-     }
- 
-+    if (strcasecmp(domain->provider, "local") != 0) {
-+        DEBUG(SSSDBG_CRIT_FAILURE,
-+              "Generating new ID is only supported in the local domain!\n");
-+        return ENOTSUP;
-+    }
-+
-     base_dn = sysdb_domain_dn(tmp_ctx, domain);
-     if (!base_dn) {
-         talloc_zfree(tmp_ctx);
--- 
-2.9.3
-
diff --git a/SOURCES/0070-LDAP-Only-run-the-POSIX-check-with-a-GC-connection.patch b/SOURCES/0070-LDAP-Only-run-the-POSIX-check-with-a-GC-connection.patch
new file mode 100644
index 0000000..f4be7e3
--- /dev/null
+++ b/SOURCES/0070-LDAP-Only-run-the-POSIX-check-with-a-GC-connection.patch
@@ -0,0 +1,95 @@
+From 405f08eabf5017cc00891fb2090be80306c8aeae Mon Sep 17 00:00:00 2001
+From: Jakub Hrozek <jhrozek@redhat.com>
+Date: Thu, 30 Nov 2017 12:01:51 +0100
+Subject: [PATCH 70/83] LDAP: Only run the POSIX check with a GC connection
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Previously, we used to run the POSIX check also with an LDAP connection.
+This was wasteful, but worked, so the waste wasn't the biggest problem
+-- the approach would only cause problems with the following patch which
+uses a NULL search base to search the Global Catalog, because searching
+with a SUBTREE scope and a NULL base returns a referral with an LDAP
+connection.
+
+Instead, this patch uses a heuristics (whether the connection ignores
+the offline state) to check if the connection is a POSIX one and if it
+is NOT, then skips the POSIX check.
+
+Reviewed-by: Pavel Březina <pbrezina@redhat.com>
+Reviewed-by: Sumit Bose <sbose@redhat.com>
+(cherry picked from commit dacfe74113dde62ddaaa7f9abf9d2b6448d89db6)
+---
+ src/providers/ldap/ldap_common.c     | 2 ++
+ src/providers/ldap/ldap_common.h     | 1 +
+ src/providers/ldap/ldap_id.c         | 2 ++
+ src/providers/ldap/sdap_async_enum.c | 1 +
+ 4 files changed, 6 insertions(+)
+
+diff --git a/src/providers/ldap/ldap_common.c b/src/providers/ldap/ldap_common.c
+index 3eff3515d95043d4b59cb0d9953cf050355a0ca5..36e79b9d6ca23ef5e21a8b0bedc7f05db8f4fc98 100644
+--- a/src/providers/ldap/ldap_common.c
++++ b/src/providers/ldap/ldap_common.c
+@@ -973,12 +973,14 @@ sdap_id_ctx_new(TALLOC_CTX *mem_ctx, struct be_ctx *bectx,
+ }
+ 
+ bool should_run_posix_check(struct sdap_id_ctx *ctx,
++                            struct sdap_id_conn_ctx *conn,
+                             bool use_id_mapping,
+                             bool posix_request)
+ {
+     if (use_id_mapping == false &&
+             posix_request == true &&
+             ctx->opts->schema_type == SDAP_SCHEMA_AD &&
++            conn->ignore_mark_offline == true &&
+             ctx->srv_opts &&
+             ctx->srv_opts->posix_checked == false) {
+         return true;
+diff --git a/src/providers/ldap/ldap_common.h b/src/providers/ldap/ldap_common.h
+index fa7cda4df9d7334f6f0f5baccae0cba0478bfbea..44dbc3fb0678412f46366321e0be836313380949 100644
+--- a/src/providers/ldap/ldap_common.h
++++ b/src/providers/ldap/ldap_common.h
+@@ -305,6 +305,7 @@ char *get_enterprise_principal_string_filter(TALLOC_CTX *mem_ctx,
+                                              struct dp_option *sdap_basic_opts);
+ 
+ bool should_run_posix_check(struct sdap_id_ctx *ctx,
++                            struct sdap_id_conn_ctx *conn,
+                             bool id_mapping,
+                             bool posix_request);
+ 
+diff --git a/src/providers/ldap/ldap_id.c b/src/providers/ldap/ldap_id.c
+index 6ab9e0aa1db3eed32deb75211ded30a4cb48ca30..47969a9749253721334a20f46230f7aecea64882 100644
+--- a/src/providers/ldap/ldap_id.c
++++ b/src/providers/ldap/ldap_id.c
+@@ -412,6 +412,7 @@ static void users_get_connect_done(struct tevent_req *subreq)
+      * have no idea about POSIX attributes support, run a one-time check
+      */
+     if (should_run_posix_check(state->ctx,
++                               state->conn,
+                                state->use_id_mapping,
+                                !state->non_posix)) {
+         subreq = sdap_posix_check_send(state, state->ev, state->ctx->opts,
+@@ -957,6 +958,7 @@ static void groups_get_connect_done(struct tevent_req *subreq)
+      * have no idea about POSIX attributes support, run a one-time check
+      */
+     if (should_run_posix_check(state->ctx,
++                               state->conn,
+                                state->use_id_mapping,
+                                !state->non_posix)) {
+         subreq = sdap_posix_check_send(state, state->ev, state->ctx->opts,
+diff --git a/src/providers/ldap/sdap_async_enum.c b/src/providers/ldap/sdap_async_enum.c
+index 2cef4eb886f982ba388a34955bdd38468fe68200..baa039d63c71cc5054e6af6538d34d04cde6b858 100644
+--- a/src/providers/ldap/sdap_async_enum.c
++++ b/src/providers/ldap/sdap_async_enum.c
+@@ -197,6 +197,7 @@ static void sdap_dom_enum_ex_get_users(struct tevent_req *subreq)
+      * have no idea about POSIX attributes support, run a one-time check
+      */
+     if (should_run_posix_check(state->ctx,
++                               state->user_conn,
+                                use_id_mapping,
+                                true)) {
+         subreq = sdap_posix_check_send(state, state->ev, state->ctx->opts,
+-- 
+2.14.3
+
diff --git a/SOURCES/0070-LDAP-save-non-POSIX-users-in-application-domains.patch b/SOURCES/0070-LDAP-save-non-POSIX-users-in-application-domains.patch
deleted file mode 100644
index d70a026..0000000
--- a/SOURCES/0070-LDAP-save-non-POSIX-users-in-application-domains.patch
+++ /dev/null
@@ -1,141 +0,0 @@
-From 3abbd7569f96a980676e0323d95301c50acdf062 Mon Sep 17 00:00:00 2001
-From: Jakub Hrozek <jhrozek@redhat.com>
-Date: Wed, 22 Mar 2017 13:06:08 +0100
-Subject: [PATCH 70/72] LDAP: save non-POSIX users in application domains
-
-Related to:
-https://pagure.io/SSSD/sssd/issue/3310
-
-If a user being saved by the LDAP provider does not have a UID or GID
-and the domain type is application, we save the user entry as non-POSIX.
-
-Reviewed-by: Sumit Bose <sbose@redhat.com>
----
- src/providers/ldap/sdap_async_users.c | 72 +++++++++++++++++++++++++++--------
- 1 file changed, 57 insertions(+), 15 deletions(-)
-
-diff --git a/src/providers/ldap/sdap_async_users.c b/src/providers/ldap/sdap_async_users.c
-index 3d957ab584865f74499bc732395388a78965fe5f..265cd7e4f7929c295d5bdcfbd781221b74601f13 100644
---- a/src/providers/ldap/sdap_async_users.c
-+++ b/src/providers/ldap/sdap_async_users.c
-@@ -112,6 +112,28 @@ done:
-     return ret;
- }
- 
-+static errno_t sdap_set_non_posix_flag(struct sysdb_attrs *attrs,
-+                                       const char *pkey)
-+{
-+    errno_t ret;
-+
-+    ret = sysdb_attrs_add_uint32(attrs, pkey, 0);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_CRIT_FAILURE,
-+              "Failed to add a zero ID to a non-posix object!\n");
-+        return ret;
-+    }
-+
-+    ret = sysdb_attrs_add_bool(attrs, SYSDB_POSIX, false);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_OP_FAILURE,
-+              "Error: Failed to mark objects as non-posix!\n");
-+        return ret;
-+    }
-+
-+    return EOK;
-+}
-+
- /* FIXME: support storing additional attributes */
- int sdap_save_user(TALLOC_CTX *memctx,
-                    struct sdap_options *opts,
-@@ -130,8 +152,8 @@ int sdap_save_user(TALLOC_CTX *memctx,
-     const char *homedir;
-     const char *shell;
-     const char *orig_dn = NULL;
--    uid_t uid;
--    gid_t gid;
-+    uid_t uid = 0;
-+    gid_t gid = 0;
-     struct sysdb_attrs *user_attrs;
-     char *upn = NULL;
-     size_t i;
-@@ -146,6 +168,7 @@ int sdap_save_user(TALLOC_CTX *memctx,
-     size_t c;
-     char *p1;
-     char *p2;
-+    bool is_posix = true;
- 
-     DEBUG(SSSDBG_TRACE_FUNC, "Save user\n");
- 
-@@ -295,19 +318,29 @@ int sdap_save_user(TALLOC_CTX *memctx,
-         ret = sysdb_attrs_get_uint32_t(attrs,
-                                        opts->user_map[SDAP_AT_USER_UID].sys_name,
-                                        &uid);
--        if (ret != EOK) {
-+        if (ret == ENOENT && dom->type == DOM_TYPE_APPLICATION) {
-+            DEBUG(SSSDBG_TRACE_INTERNAL,
-+                  "Marking object as non-posix and setting ID=0!\n");
-+            ret = sdap_set_non_posix_flag(user_attrs,
-+                    opts->user_map[SDAP_AT_USER_UID].sys_name);
-+            if (ret != EOK) {
-+                goto done;
-+            }
-+            is_posix = false;
-+        } else if (ret != EOK) {
-             DEBUG(SSSDBG_CRIT_FAILURE,
--                  "no uid provided for [%s] in domain [%s].\n",
-+                  "Cannot retrieve UID for [%s] in domain [%s].\n",
-                    user_name, dom->name);
--            ret = EINVAL;
-+            ret = ERR_NO_POSIX;
-             goto done;
-         }
-     }
--    /* check that the uid is valid for this domain */
--    if (OUT_OF_ID_RANGE(uid, dom->id_min, dom->id_max)) {
--            DEBUG(SSSDBG_OP_FAILURE,
--                  "User [%s] filtered out! (uid out of range)\n",
--                      user_name);
-+
-+    /* check that the uid is valid for this domain if the user is a POSIX one */
-+    if (is_posix == true && OUT_OF_ID_RANGE(uid, dom->id_min, dom->id_max)) {
-+        DEBUG(SSSDBG_OP_FAILURE,
-+              "User [%s] filtered out! (uid out of range)\n",
-+              user_name);
-         ret = EINVAL;
-         goto done;
-     }
-@@ -349,17 +382,26 @@ int sdap_save_user(TALLOC_CTX *memctx,
-         ret = sysdb_attrs_get_uint32_t(attrs,
-                                        opts->user_map[SDAP_AT_USER_GID].sys_name,
-                                        &gid);
--        if (ret != EOK) {
-+        if (ret == ENOENT && dom->type == DOM_TYPE_APPLICATION) {
-+            DEBUG(SSSDBG_TRACE_INTERNAL,
-+                  "Marking object as non-posix and setting ID=0!\n");
-+            ret = sdap_set_non_posix_flag(attrs,
-+                    opts->user_map[SDAP_AT_USER_GID].sys_name);
-+            if (ret != EOK) {
-+                goto done;
-+            }
-+            is_posix = false;
-+        } else if (ret != EOK) {
-             DEBUG(SSSDBG_CRIT_FAILURE,
--                  "no gid provided for [%s] in domain [%s].\n",
--                  user_name, dom->name);
--            ret = EINVAL;
-+                  "Cannot retrieve GID for [%s] in domain [%s].\n",
-+                   user_name, dom->name);
-+            ret = ERR_NO_POSIX;
-             goto done;
-         }
-     }
- 
-     /* check that the gid is valid for this domain */
--    if (IS_SUBDOMAIN(dom) == false &&
-+    if (is_posix == true && IS_SUBDOMAIN(dom) == false &&
-             OUT_OF_ID_RANGE(gid, dom->id_min, dom->id_max)) {
-         DEBUG(SSSDBG_CRIT_FAILURE,
-               "User [%s] filtered out! (primary gid out of range)\n",
--- 
-2.9.3
-
diff --git a/SOURCES/0071-LDAP-Relax-search-filters-in-application-domains.patch b/SOURCES/0071-LDAP-Relax-search-filters-in-application-domains.patch
deleted file mode 100644
index 73f2862..0000000
--- a/SOURCES/0071-LDAP-Relax-search-filters-in-application-domains.patch
+++ /dev/null
@@ -1,254 +0,0 @@
-From b2a823cf415a12416dca9ff019666906d61cfc2f Mon Sep 17 00:00:00 2001
-From: Jakub Hrozek <jhrozek@redhat.com>
-Date: Wed, 22 Mar 2017 13:06:14 +0100
-Subject: [PATCH 71/72] LDAP: Relax search filters in application domains
-
-Related to:
-https://pagure.io/SSSD/sssd/issue/3310
-
-If a request comes towards an application domain, we can drop the part
-of the filter that asserts that the object has a valid UID/GID. Instead,
-we just search by name.
-
-Reviewed-by: Sumit Bose <sbose@redhat.com>
----
- src/providers/ldap/ldap_id.c               | 35 ++++++++++++++++++++++++----
- src/providers/ldap/sdap_async_enum.c       |  7 +++++-
- src/providers/ldap/sdap_async_initgroups.c | 37 ++++++++++++++++++++++++------
- 3 files changed, 66 insertions(+), 13 deletions(-)
-
-diff --git a/src/providers/ldap/ldap_id.c b/src/providers/ldap/ldap_id.c
-index 0bee0ca8d71abece6749fdb8393b9ceacb64417d..7400dc1f57e30cc6ae5f939ffa628a1e9dd47e06 100644
---- a/src/providers/ldap/ldap_id.c
-+++ b/src/providers/ldap/ldap_id.c
-@@ -56,6 +56,7 @@ struct users_get_state {
-     char *filter;
-     const char **attrs;
-     bool use_id_mapping;
-+    bool non_posix;
- 
-     int dp_error;
-     int sdap_ret;
-@@ -114,6 +115,10 @@ struct tevent_req *users_get_send(TALLOC_CTX *memctx,
-     state->filter_value = filter_value;
-     state->filter_type = filter_type;
- 
-+    if (state->domain->type == DOM_TYPE_APPLICATION) {
-+        state->non_posix = true;
-+    }
-+
-     state->use_id_mapping = sdap_idmap_domain_has_algorithmic_mapping(
-                                                           ctx->opts->idmap_ctx,
-                                                           sdom->dom->name,
-@@ -292,7 +297,13 @@ struct tevent_req *users_get_send(TALLOC_CTX *memctx,
-         }
-     }
- 
--    if (state->use_id_mapping || filter_type == BE_FILTER_SECID) {
-+    if (state->non_posix) {
-+        state->filter = talloc_asprintf(state,
-+                                        "(&%s(objectclass=%s)(%s=*))",
-+                                        user_filter,
-+                                        ctx->opts->user_map[SDAP_OC_USER].name,
-+                                        ctx->opts->user_map[SDAP_AT_USER_NAME].name);
-+    } else if (state->use_id_mapping || filter_type == BE_FILTER_SECID) {
-         /* When mapping IDs or looking for SIDs, we don't want to limit
-          * ourselves to users with a UID value. But there must be a SID to map
-          * from.
-@@ -304,7 +315,8 @@ struct tevent_req *users_get_send(TALLOC_CTX *memctx,
-                                         ctx->opts->user_map[SDAP_AT_USER_NAME].name,
-                                         ctx->opts->user_map[SDAP_AT_USER_OBJECTSID].name);
-     } else {
--        /* When not ID-mapping, make sure there is a non-NULL UID */
-+        /* When not ID-mapping or looking up POSIX users,
-+         * make sure there is a non-NULL UID */
-         state->filter = talloc_asprintf(state,
-                                         "(&%s(objectclass=%s)(%s=*)(&(%s=*)(!(%s=0))))",
-                                         user_filter,
-@@ -380,6 +392,7 @@ static void users_get_connect_done(struct tevent_req *subreq)
-      * have no idea about POSIX attributes support, run a one-time check
-      */
-     if (state->use_id_mapping == false &&
-+            state->non_posix == false &&
-             state->ctx->opts->schema_type == SDAP_SCHEMA_AD &&
-             state->ctx->srv_opts &&
-             state->ctx->srv_opts->posix_checked == false) {
-@@ -650,6 +663,7 @@ struct groups_get_state {
-     char *filter;
-     const char **attrs;
-     bool use_id_mapping;
-+    bool non_posix;
- 
-     int dp_error;
-     int sdap_ret;
-@@ -709,6 +723,10 @@ struct tevent_req *groups_get_send(TALLOC_CTX *memctx,
-     state->filter_value = filter_value;
-     state->filter_type = filter_type;
- 
-+    if (state->domain->type == DOM_TYPE_APPLICATION) {
-+        state->non_posix = true;
-+    }
-+
-     state->use_id_mapping = sdap_idmap_domain_has_algorithmic_mapping(
-                                                           ctx->opts->idmap_ctx,
-                                                           sdom->dom->name,
-@@ -827,9 +845,11 @@ struct tevent_req *groups_get_send(TALLOC_CTX *memctx,
-         goto done;
-     }
- 
--    if (state->use_id_mapping || filter_type == BE_FILTER_SECID) {
--        /* When mapping IDs or looking for SIDs, we don't want to limit
--         * ourselves to groups with a GID value
-+    if (state->non_posix
-+            || state->use_id_mapping
-+            || filter_type == BE_FILTER_SECID) {
-+        /* When mapping IDs or looking for SIDs, or when in a non-POSIX domain,
-+         * we don't want to limit ourselves to groups with a GID value
-          */
- 
-         state->filter = talloc_asprintf(state,
-@@ -1123,6 +1143,7 @@ struct groups_by_user_state {
-     int filter_type;
-     const char *extra_value;
-     const char **attrs;
-+    bool non_posix;
- 
-     int dp_error;
-     int sdap_ret;
-@@ -1204,6 +1225,10 @@ static struct tevent_req *groups_by_user_send(TALLOC_CTX *memctx,
-     state->domain = sdom->dom;
-     state->sysdb = sdom->dom->sysdb;
- 
-+    if (state->domain->type == DOM_TYPE_APPLICATION) {
-+        state->non_posix = true;
-+    }
-+
-     ret = build_attrs_from_map(state, ctx->opts->group_map, SDAP_OPTS_GROUP,
-                                NULL, &state->attrs, NULL);
-     if (ret != EOK) goto fail;
-diff --git a/src/providers/ldap/sdap_async_enum.c b/src/providers/ldap/sdap_async_enum.c
-index 3f65059e18d5c8b548da0babec867d27c3a64198..91e481c4e694126900c729e86d187fba355de0b8 100644
---- a/src/providers/ldap/sdap_async_enum.c
-+++ b/src/providers/ldap/sdap_async_enum.c
-@@ -717,6 +717,7 @@ static struct tevent_req *enum_groups_send(TALLOC_CTX *memctx,
-     struct enum_groups_state *state;
-     int ret;
-     bool use_mapping;
-+    bool non_posix = false;
-     char *oc_list;
- 
-     req = tevent_req_create(memctx, &state, struct enum_groups_state);
-@@ -727,6 +728,10 @@ static struct tevent_req *enum_groups_send(TALLOC_CTX *memctx,
-     state->ctx = ctx;
-     state->op = op;
- 
-+    if (sdom->dom->type == DOM_TYPE_APPLICATION) {
-+        non_posix = true;
-+    }
-+
-     use_mapping = sdap_idmap_domain_has_algorithmic_mapping(
-                                                         ctx->opts->idmap_ctx,
-                                                         sdom->dom->name,
-@@ -749,7 +754,7 @@ static struct tevent_req *enum_groups_send(TALLOC_CTX *memctx,
-         goto fail;
-     }
- 
--    if (use_mapping) {
-+    if (!non_posix && use_mapping) {
-         /* If we're ID-mapping, check for the objectSID as well */
-         state->filter = talloc_asprintf_append_buffer(
-                 state->filter, "(%s=*)",
-diff --git a/src/providers/ldap/sdap_async_initgroups.c b/src/providers/ldap/sdap_async_initgroups.c
-index 79af7a3eda3fe8533933535c98c2b4b4698dfda2..c926ddcbefe471daa80505e139c3f19efa33b9ba 100644
---- a/src/providers/ldap/sdap_async_initgroups.c
-+++ b/src/providers/ldap/sdap_async_initgroups.c
-@@ -376,7 +376,7 @@ struct sdap_initgr_rfc2307_state {
-     struct sdap_handle *sh;
-     const char **attrs;
-     const char *name;
--    const char *base_filter;
-+    char *base_filter;
-     const char *orig_dn;
-     char *filter;
-     int timeout;
-@@ -473,18 +473,32 @@ struct tevent_req *sdap_initgr_rfc2307_send(TALLOC_CTX *memctx,
-     }
- 
-     state->base_filter = talloc_asprintf(state,
--                             "(&(%s=%s)(%s)(%s=*)(&(%s=*)(!(%s=0))))",
-+                             "(&(%s=%s)(%s)(%s=*)",
-                              opts->group_map[SDAP_AT_GROUP_MEMBER].name,
-                              clean_name, oc_list,
--                             opts->group_map[SDAP_AT_GROUP_NAME].name,
--                             opts->group_map[SDAP_AT_GROUP_GID].name,
--                             opts->group_map[SDAP_AT_GROUP_GID].name);
-+                             opts->group_map[SDAP_AT_GROUP_NAME].name);
-     if (!state->base_filter) {
-         talloc_zfree(req);
-         return NULL;
-     }
-     talloc_zfree(clean_name);
- 
-+    switch (domain->type) {
-+    case DOM_TYPE_APPLICATION:
-+        state->base_filter = talloc_asprintf_append(state->base_filter, ")");
-+        break;
-+    case DOM_TYPE_POSIX:
-+        state->base_filter = talloc_asprintf_append(state->base_filter,
-+                                        "(&(%s=*)(!(%s=0))))",
-+                                        opts->group_map[SDAP_AT_GROUP_GID].name,
-+                                        opts->group_map[SDAP_AT_GROUP_GID].name);
-+        break;
-+    }
-+    if (!state->base_filter) {
-+        ret = ENOMEM;
-+        goto done;
-+    }
-+
-     ret = sdap_initgr_rfc2307_next_base(req);
- 
- done:
-@@ -2666,6 +2680,7 @@ struct sdap_get_initgr_state {
-     char *shortname;
-     char *filter;
-     int timeout;
-+    bool non_posix;
- 
-     struct sysdb_attrs *orig_user;
- 
-@@ -2724,6 +2739,10 @@ struct tevent_req *sdap_get_initgr_send(TALLOC_CTX *memctx,
-         goto done;
-     }
- 
-+    if (state->dom->type == DOM_TYPE_APPLICATION) {
-+        state->non_posix = true;
-+    }
-+
-     use_id_mapping = sdap_idmap_domain_has_algorithmic_mapping(
-                                                           id_ctx->opts->idmap_ctx,
-                                                           sdom->dom->name,
-@@ -2813,7 +2832,10 @@ struct tevent_req *sdap_get_initgr_send(TALLOC_CTX *memctx,
-         }
-     }
- 
--    if (use_id_mapping) {
-+    if (state->non_posix) {
-+        state->user_base_filter = talloc_asprintf_append(state->user_base_filter,
-+                                                         ")");
-+    } else if (use_id_mapping) {
-         /* When mapping IDs or looking for SIDs, we don't want to limit
-          * ourselves to users with a UID value. But there must be a SID to map
-          * from.
-@@ -2822,7 +2844,8 @@ struct tevent_req *sdap_get_initgr_send(TALLOC_CTX *memctx,
-                                         "(%s=*))",
-                                         id_ctx->opts->user_map[SDAP_AT_USER_OBJECTSID].name);
-     } else {
--        /* When not ID-mapping, make sure there is a non-NULL UID */
-+        /* When not ID-mapping or looking up app users, make sure there
-+         * is a non-NULL UID */
-         state->user_base_filter = talloc_asprintf_append(state->user_base_filter,
-                                         "(&(%s=*)(!(%s=0))))",
-                                         id_ctx->opts->user_map[SDAP_AT_USER_UID].name,
--- 
-2.9.3
-
diff --git a/SOURCES/0071-SDAP-Search-with-a-NULL-search-base-when-looking-up-.patch b/SOURCES/0071-SDAP-Search-with-a-NULL-search-base-when-looking-up-.patch
new file mode 100644
index 0000000..5be8736
--- /dev/null
+++ b/SOURCES/0071-SDAP-Search-with-a-NULL-search-base-when-looking-up-.patch
@@ -0,0 +1,191 @@
+From c7003e815aca1c28953c3dc55311ffc3f2d4ab28 Mon Sep 17 00:00:00 2001
+From: Jakub Hrozek <jhrozek@redhat.com>
+Date: Sun, 12 Nov 2017 19:24:01 +0100
+Subject: [PATCH 71/83] SDAP: Search with a NULL search base when looking up an
+ ID in the Global Catalog
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+The posix_check request is used to determine whether domains in the forest
+replicate the POSIX attributes into the Global Catalog. And since the
+schema modification that replicates the attributes is not per-domain, but
+per-forest, we don't need to iterate over search bases when checking for
+the POSIX attribute presence. It is OK to just search with a NULL search
+base (and it's what Windows clients do, too).
+
+Additionally, searching over the whole GC will come handy when implementing
+the request that located an account's domain.
+
+Reviewed-by: Pavel Březina <pbrezina@redhat.com>
+Reviewed-by: Sumit Bose <sbose@redhat.com>
+(cherry picked from commit 6ae22d9adc0b075361defc99b8f14480ba8e7b46)
+---
+ src/providers/ldap/ldap_id.c         |  2 --
+ src/providers/ldap/sdap_async.c      | 51 +++++++-----------------------------
+ src/providers/ldap/sdap_async.h      |  1 -
+ src/providers/ldap/sdap_async_enum.c |  1 -
+ 4 files changed, 10 insertions(+), 45 deletions(-)
+
+diff --git a/src/providers/ldap/ldap_id.c b/src/providers/ldap/ldap_id.c
+index 47969a9749253721334a20f46230f7aecea64882..b5ac3a749113a281fe8a5564ac341ced0570eded 100644
+--- a/src/providers/ldap/ldap_id.c
++++ b/src/providers/ldap/ldap_id.c
+@@ -417,7 +417,6 @@ static void users_get_connect_done(struct tevent_req *subreq)
+                                !state->non_posix)) {
+         subreq = sdap_posix_check_send(state, state->ev, state->ctx->opts,
+                                        sdap_id_op_handle(state->op),
+-                                       state->sdom->user_search_bases,
+                                        dp_opt_get_int(state->ctx->opts->basic,
+                                                       SDAP_SEARCH_TIMEOUT));
+         if (subreq == NULL) {
+@@ -963,7 +962,6 @@ static void groups_get_connect_done(struct tevent_req *subreq)
+                                !state->non_posix)) {
+         subreq = sdap_posix_check_send(state, state->ev, state->ctx->opts,
+                                        sdap_id_op_handle(state->op),
+-                                       state->sdom->user_search_bases,
+                                        dp_opt_get_int(state->ctx->opts->basic,
+                                                       SDAP_SEARCH_TIMEOUT));
+         if (subreq == NULL) {
+diff --git a/src/providers/ldap/sdap_async.c b/src/providers/ldap/sdap_async.c
+index 246e12a1f386da1841963d5c1d1c4d2870cc1b6b..1df0b85f4bda6442d8da66784ad7424306b1f051 100644
+--- a/src/providers/ldap/sdap_async.c
++++ b/src/providers/ldap/sdap_async.c
+@@ -2573,7 +2573,6 @@ int sdap_asq_search_recv(struct tevent_req *req,
+ }
+ 
+ /* ==Posix attribute presence test================================= */
+-static errno_t sdap_posix_check_next(struct tevent_req *req);
+ static void sdap_posix_check_done(struct tevent_req *subreq);
+ static errno_t sdap_posix_check_parse(struct sdap_handle *sh,
+                                       struct sdap_msg *msg,
+@@ -2583,12 +2582,10 @@ struct sdap_posix_check_state {
+     struct tevent_context *ev;
+     struct sdap_options *opts;
+     struct sdap_handle *sh;
+-    struct sdap_search_base **search_bases;
+     int timeout;
+ 
+     const char **attrs;
+     const char *filter;
+-    size_t base_iter;
+ 
+     bool has_posix;
+ };
+@@ -2596,10 +2593,10 @@ struct sdap_posix_check_state {
+ struct tevent_req *
+ sdap_posix_check_send(TALLOC_CTX *memctx, struct tevent_context *ev,
+                       struct sdap_options *opts, struct sdap_handle *sh,
+-                      struct sdap_search_base **search_bases,
+                       int timeout)
+ {
+     struct tevent_req *req = NULL;
++    struct tevent_req *subreq = NULL;
+     struct sdap_posix_check_state *state;
+     errno_t ret;
+ 
+@@ -2610,7 +2607,6 @@ sdap_posix_check_send(TALLOC_CTX *memctx, struct tevent_context *ev,
+     state->ev = ev;
+     state->sh = sh;
+     state->opts = opts;
+-    state->search_bases = search_bases;
+     state->timeout = timeout;
+ 
+     state->attrs = talloc_array(state, const char *, 4);
+@@ -2634,43 +2630,26 @@ sdap_posix_check_send(TALLOC_CTX *memctx, struct tevent_context *ev,
+         goto fail;
+     }
+ 
+-    ret = sdap_posix_check_next(req);
+-    if (ret != EOK) {
+-        goto fail;
+-    }
+-
+-    return req;
+-
+-fail:
+-    tevent_req_error(req, ret);
+-    tevent_req_post(req, ev);
+-    return req;
+-}
+-
+-static errno_t sdap_posix_check_next(struct tevent_req *req)
+-{
+-    struct tevent_req *subreq = NULL;
+-    struct sdap_posix_check_state *state =
+-        tevent_req_data(req, struct sdap_posix_check_state);
+-
+-    DEBUG(SSSDBG_TRACE_FUNC,
+-          "Searching for POSIX attributes with base [%s]\n",
+-           state->search_bases[state->base_iter]->basedn);
+-
+     subreq = sdap_get_generic_ext_send(state, state->ev, state->opts,
+                                  state->sh,
+-                                 state->search_bases[state->base_iter]->basedn,
++                                 "",
+                                  LDAP_SCOPE_SUBTREE, state->filter,
+                                  state->attrs,
+                                  NULL, NULL, 1, state->timeout,
+                                  sdap_posix_check_parse, state,
+                                  SDAP_SRCH_FLG_SIZELIMIT_SILENT);
+     if (subreq == NULL) {
+-        return ENOMEM;
++        ret = ENOMEM;
++        goto fail;
+     }
+     tevent_req_set_callback(subreq, sdap_posix_check_done, req);
+ 
+-    return EOK;
++    return req;
++
++fail:
++    tevent_req_error(req, ret);
++    tevent_req_post(req, ev);
++    return req;
+ }
+ 
+ static errno_t sdap_posix_check_parse(struct sdap_handle *sh,
+@@ -2746,16 +2725,6 @@ static void sdap_posix_check_done(struct tevent_req *subreq)
+         return;
+     }
+ 
+-    state->base_iter++;
+-    if (state->search_bases[state->base_iter]) {
+-        /* There are more search bases to try */
+-        ret = sdap_posix_check_next(req);
+-        if (ret != EOK) {
+-            tevent_req_error(req, ret);
+-        }
+-        return;
+-    }
+-
+     /* All bases done! */
+     DEBUG(SSSDBG_TRACE_LIBS, "Cycled through all bases\n");
+     tevent_req_done(req);
+diff --git a/src/providers/ldap/sdap_async.h b/src/providers/ldap/sdap_async.h
+index 6e5800b42ba4a045fa7985b09a80b6b86b8c6055..7216ba032e551196cf5258b4e58fbfc8cfe417ea 100644
+--- a/src/providers/ldap/sdap_async.h
++++ b/src/providers/ldap/sdap_async.h
+@@ -269,7 +269,6 @@ int sdap_deref_search_recv(struct tevent_req *req,
+ struct tevent_req *
+ sdap_posix_check_send(TALLOC_CTX *memctx, struct tevent_context *ev,
+                       struct sdap_options *opts, struct sdap_handle *sh,
+-                      struct sdap_search_base **search_bases,
+                       int timeout);
+ 
+ int sdap_posix_check_recv(struct tevent_req *req,
+diff --git a/src/providers/ldap/sdap_async_enum.c b/src/providers/ldap/sdap_async_enum.c
+index baa039d63c71cc5054e6af6538d34d04cde6b858..ec0c679823a8cd9820bb978f77799a3f86621271 100644
+--- a/src/providers/ldap/sdap_async_enum.c
++++ b/src/providers/ldap/sdap_async_enum.c
+@@ -202,7 +202,6 @@ static void sdap_dom_enum_ex_get_users(struct tevent_req *subreq)
+                                true)) {
+         subreq = sdap_posix_check_send(state, state->ev, state->ctx->opts,
+                                        sdap_id_op_handle(state->user_op),
+-                                       state->sdom->user_search_bases,
+                                        dp_opt_get_int(state->ctx->opts->basic,
+                                                       SDAP_SEARCH_TIMEOUT));
+         if (subreq == NULL) {
+-- 
+2.14.3
+
diff --git a/SOURCES/0072-KRB5-Authenticate-users-in-a-non-POSIX-domain-using-.patch b/SOURCES/0072-KRB5-Authenticate-users-in-a-non-POSIX-domain-using-.patch
deleted file mode 100644
index 05693f1..0000000
--- a/SOURCES/0072-KRB5-Authenticate-users-in-a-non-POSIX-domain-using-.patch
+++ /dev/null
@@ -1,353 +0,0 @@
-From 3f32e79858f268ce6501de44e5158e8c12f688dd Mon Sep 17 00:00:00 2001
-From: Jakub Hrozek <jhrozek@redhat.com>
-Date: Wed, 22 Mar 2017 13:01:18 +0100
-Subject: [PATCH 72/72] KRB5: Authenticate users in a non-POSIX domain using a
- MEMORY ccache
-
-Related to:
-https://pagure.io/SSSD/sssd/issue/3310
-
-The following changes were done to the Kerberos authentication code
-in order to support authentication in a non-POSIX environment:
-    - delayed authentication is disabled in non-POSIX domains
-    - when a user logs in in a non-POSIX domain, SSSD uses a
-      MEMORY:$username ccache and destroys is then krb5_child finishes
-      so that just the numeric result is used
-    - krb5_child doesn't drop privileges in this configuration because
-      there is nothing to drop privileges to
-
-Reviewed-by: Sumit Bose <sbose@redhat.com>
----
- src/providers/krb5/krb5_auth.c                     | 62 ++++++++++++++++------
- src/providers/krb5/krb5_auth.h                     |  2 +
- src/providers/krb5/krb5_child.c                    | 32 +++++++++--
- src/providers/krb5/krb5_child_handler.c            | 15 +++++-
- .../krb5/krb5_delayed_online_authentication.c      |  7 +++
- src/providers/krb5/krb5_init.c                     |  3 ++
- 6 files changed, 99 insertions(+), 22 deletions(-)
-
-diff --git a/src/providers/krb5/krb5_auth.c b/src/providers/krb5/krb5_auth.c
-index c2d6d7eeacc1f766024c4d629f25fd0f0be24e5e..2faf18d17a735476c20f9cc27b15be4a39cadc5c 100644
---- a/src/providers/krb5/krb5_auth.c
-+++ b/src/providers/krb5/krb5_auth.c
-@@ -42,6 +42,8 @@
- #include "providers/krb5/krb5_utils.h"
- #include "providers/krb5/krb5_ccache.h"
- 
-+#define  NON_POSIX_CCNAME_FMT       "MEMORY:sssd_nonposix_dummy_%u"
-+
- static int krb5_mod_ccname(TALLOC_CTX *mem_ctx,
-                            struct sysdb_ctx *sysdb,
-                            struct sss_domain_info *domain,
-@@ -200,6 +202,7 @@ errno_t krb5_setup(TALLOC_CTX *mem_ctx,
-     talloc_set_destructor((TALLOC_CTX *) kr, krb5_cleanup);
- 
-     kr->pd = pd;
-+    kr->dom = dom;
-     kr->krb5_ctx = krb5_ctx;
- 
-     ret = get_krb_primary(krb5_ctx->name_to_primary,
-@@ -275,8 +278,11 @@ static void krb5_auth_cache_creds(struct krb5_ctx *krb5_ctx,
-         return;
-     }
- 
--    ret = add_user_to_delayed_online_authentication(krb5_ctx, pd, uid);
--    if (ret != EOK) {
-+    ret = add_user_to_delayed_online_authentication(krb5_ctx, domain, pd, uid);
-+    if (ret == ENOTSUP) {
-+        /* This error is not fatal */
-+        DEBUG(SSSDBG_MINOR_FAILURE, "Delayed authentication not supported\n");
-+    } else if (ret != EOK) {
-         /* This error is not fatal */
-         DEBUG(SSSDBG_CRIT_FAILURE,
-               "add_user_to_delayed_online_authentication failed.\n");
-@@ -291,21 +297,43 @@ static errno_t krb5_auth_prepare_ccache_name(struct krb5child_req *kr,
- {
-     const char *ccname_template;
- 
--    ccname_template = dp_opt_get_cstring(kr->krb5_ctx->opts, KRB5_CCNAME_TMPL);
-+    switch (kr->dom->type) {
-+    case DOM_TYPE_POSIX:
-+        ccname_template = dp_opt_get_cstring(kr->krb5_ctx->opts, KRB5_CCNAME_TMPL);
- 
--    kr->ccname = expand_ccname_template(kr, kr, ccname_template,
--                                        kr->krb5_ctx->illegal_path_re, true,
--                                        be_ctx->domain->case_sensitive);
--    if (kr->ccname == NULL) {
--        DEBUG(SSSDBG_CRIT_FAILURE, "expand_ccname_template failed.\n");
--        return ENOMEM;
--    }
-+        kr->ccname = expand_ccname_template(kr, kr, ccname_template,
-+                                            kr->krb5_ctx->illegal_path_re, true,
-+                                            be_ctx->domain->case_sensitive);
-+        if (kr->ccname == NULL) {
-+            DEBUG(SSSDBG_CRIT_FAILURE, "expand_ccname_template failed.\n");
-+            return ENOMEM;
-+        }
- 
--    kr->old_ccname = ldb_msg_find_attr_as_string(user_msg,
--                                                 SYSDB_CCACHE_FILE, NULL);
--    if (kr->old_ccname == NULL) {
--        DEBUG(SSSDBG_TRACE_LIBS,
--                "No ccache file for user [%s] found.\n", kr->pd->user);
-+        kr->old_ccname = ldb_msg_find_attr_as_string(user_msg,
-+                                                    SYSDB_CCACHE_FILE, NULL);
-+        if (kr->old_ccname == NULL) {
-+            DEBUG(SSSDBG_TRACE_LIBS,
-+                    "No ccache file for user [%s] found.\n", kr->pd->user);
-+        }
-+        break;
-+    case DOM_TYPE_APPLICATION:
-+        DEBUG(SSSDBG_TRACE_FUNC,
-+               "Domain type application, will use in-memory ccache\n");
-+        /* We don't care about using cryptographic randomness, just
-+         * a non-predictable ccname, so using rand() here is fine
-+         */
-+        kr->ccname = talloc_asprintf(kr,
-+                                     NON_POSIX_CCNAME_FMT,
-+                                     rand() % UINT_MAX);
-+        if (kr->ccname == NULL) {
-+            DEBUG(SSSDBG_CRIT_FAILURE, "talloc_asprintf failed.\n");
-+            return ENOMEM;
-+        }
-+
-+        break;
-+    default:
-+        DEBUG(SSSDBG_FATAL_FAILURE, "Unsupported domain type\n");
-+        return EINVAL;
-     }
- 
-     return EOK;
-@@ -617,7 +645,7 @@ struct tevent_req *krb5_auth_send(TALLOC_CTX *mem_ctx,
-         kr->uid = sss_view_ldb_msg_find_attr_as_uint64(state->domain,
-                                                        res->msgs[0],
-                                                        SYSDB_UIDNUM, 0);
--        if (kr->uid == 0) {
-+        if (kr->uid == 0 && state->domain->type == DOM_TYPE_POSIX) {
-             DEBUG(SSSDBG_CONF_SETTINGS,
-                   "UID for user [%s] not known.\n", pd->user);
-             ret = ENOENT;
-@@ -627,7 +655,7 @@ struct tevent_req *krb5_auth_send(TALLOC_CTX *mem_ctx,
-         kr->gid = sss_view_ldb_msg_find_attr_as_uint64(state->domain,
-                                                        res->msgs[0],
-                                                        SYSDB_GIDNUM, 0);
--        if (kr->gid == 0) {
-+        if (kr->gid == 0 && state->domain->type == DOM_TYPE_POSIX) {
-             DEBUG(SSSDBG_CONF_SETTINGS,
-                   "GID for user [%s] not known.\n", pd->user);
-             ret = ENOENT;
-diff --git a/src/providers/krb5/krb5_auth.h b/src/providers/krb5/krb5_auth.h
-index 75ad916e79b29043120543ab3c4c1bd27e09d913..8ad3aeff21e58f9055ae144eaa450992c6391ba6 100644
---- a/src/providers/krb5/krb5_auth.h
-+++ b/src/providers/krb5/krb5_auth.h
-@@ -50,6 +50,7 @@
- struct krb5child_req {
-     struct pam_data *pd;
-     struct krb5_ctx *krb5_ctx;
-+    struct sss_domain_info *dom;
- 
-     const char *ccname;
-     const char *old_ccname;
-@@ -118,6 +119,7 @@ parse_krb5_child_response(TALLOC_CTX *mem_ctx, uint8_t *buf, ssize_t len,
-                           struct krb5_child_response **_res);
- 
- errno_t add_user_to_delayed_online_authentication(struct krb5_ctx *krb5_ctx,
-+                                                  struct sss_domain_info *domain,
-                                                   struct pam_data *pd,
-                                                   uid_t uid);
- errno_t init_delayed_online_authentication(struct krb5_ctx *krb5_ctx,
-diff --git a/src/providers/krb5/krb5_child.c b/src/providers/krb5/krb5_child.c
-index a4128dda6b0861a95dba223047d66c4158b1afb6..cbbc892bee0365892ac66d3654c974d325166b60 100644
---- a/src/providers/krb5/krb5_child.c
-+++ b/src/providers/krb5/krb5_child.c
-@@ -80,6 +80,7 @@ struct krb5_req {
-     char *ccname;
-     char *keytab;
-     bool validate;
-+    bool posix_domain;
-     bool send_pac;
-     bool use_enterprise_princ;
-     char *fast_ccname;
-@@ -102,6 +103,16 @@ struct krb5_req {
- static krb5_context krb5_error_ctx;
- #define KRB5_CHILD_DEBUG(level, error) KRB5_DEBUG(level, krb5_error_ctx, error)
- 
-+static errno_t k5c_become_user(uid_t uid, gid_t gid, bool is_posix)
-+{
-+    if (is_posix == false) {
-+        DEBUG(SSSDBG_TRACE_FUNC,
-+              "Will not drop privileges for a non-POSIX user\n");
-+        return EOK;
-+    }
-+    return become_user(uid, gid);
-+}
-+
- static krb5_error_code set_lifetime_options(struct cli_opts *cli_opts,
-                                             krb5_get_init_creds_opt *options)
- {
-@@ -1561,6 +1572,15 @@ static krb5_error_code get_and_save_tgt(struct krb5_req *kr,
-         DEBUG(SSSDBG_CONF_SETTINGS, "TGT validation is disabled.\n");
-     }
- 
-+    /* In a non-POSIX environment, we only care about the return code from
-+     * krb5_child, so let's not even attempt to create the ccache
-+     */
-+    if (kr->posix_domain == false) {
-+        DEBUG(SSSDBG_TRACE_LIBS,
-+              "Finished authentication in a non-POSIX domain\n");
-+        goto done;
-+    }
-+
-     /* If kr->ccname is cache collection (DIR:/...), we want to work
-      * directly with file ccache (DIR::/...), but cache collection
-      * should be returned back to back end.
-@@ -2146,6 +2166,7 @@ static errno_t unpack_buffer(uint8_t *buf, size_t size,
-     size_t p = 0;
-     uint32_t len;
-     uint32_t validate;
-+    uint32_t posix_domain;
-     uint32_t send_pac;
-     uint32_t use_enterprise_princ;
-     struct pam_data *pd;
-@@ -2167,6 +2188,8 @@ static errno_t unpack_buffer(uint8_t *buf, size_t size,
-     SAFEALIGN_COPY_UINT32_CHECK(&kr->gid, buf + p, size, &p);
-     SAFEALIGN_COPY_UINT32_CHECK(&validate, buf + p, size, &p);
-     kr->validate = (validate == 0) ? false : true;
-+    SAFEALIGN_COPY_UINT32_CHECK(&posix_domain, buf + p, size, &p);
-+    kr->posix_domain = (posix_domain == 0) ? false : true;
-     SAFEALIGN_COPY_UINT32_CHECK(offline, buf + p, size, &p);
-     SAFEALIGN_COPY_UINT32_CHECK(&send_pac, buf + p, size, &p);
-     kr->send_pac = (send_pac == 0) ? false : true;
-@@ -2331,6 +2354,7 @@ static krb5_error_code check_fast_ccache(TALLOC_CTX *mem_ctx,
-                                          krb5_context ctx,
-                                          uid_t fast_uid,
-                                          gid_t fast_gid,
-+                                         bool posix_domain,
-                                          struct cli_opts *cli_opts,
-                                          const char *primary,
-                                          const char *realm,
-@@ -2420,7 +2444,7 @@ static krb5_error_code check_fast_ccache(TALLOC_CTX *mem_ctx,
-                 /* Try to carry on */
-             }
- 
--            kerr = become_user(fast_uid, fast_gid);
-+            kerr = k5c_become_user(fast_uid, fast_gid, posix_domain);
-             if (kerr != 0) {
-                 DEBUG(SSSDBG_CRIT_FAILURE, "become_user failed: %d\n", kerr);
-                 exit(1);
-@@ -2572,7 +2596,7 @@ static int k5c_setup_fast(struct krb5_req *kr, bool demand)
-     }
- 
-     kerr = check_fast_ccache(kr, kr->ctx, kr->fast_uid, kr->fast_gid,
--                             kr->cli_opts,
-+                             kr->posix_domain, kr->cli_opts,
-                              fast_principal, fast_principal_realm,
-                              kr->keytab, &kr->fast_ccname);
-     if (kerr != 0) {
-@@ -2773,7 +2797,7 @@ static int k5c_setup(struct krb5_req *kr, uint32_t offline)
-          * the user who is logging in. The same applies to the offline case
-          * the user who is logging in. The same applies to the offline case.
-          */
--        kerr = become_user(kr->uid, kr->gid);
-+        kerr = k5c_become_user(kr->uid, kr->gid, kr->posix_domain);
-         if (kerr != 0) {
-             DEBUG(SSSDBG_CRIT_FAILURE, "become_user failed.\n");
-             return kerr;
-@@ -3075,7 +3099,7 @@ int main(int argc, const char *argv[])
-     if ((sss_authtok_get_type(kr->pd->authtok) != SSS_AUTHTOK_TYPE_SC_PIN
-             && sss_authtok_get_type(kr->pd->authtok)
-                                         != SSS_AUTHTOK_TYPE_SC_KEYPAD)) {
--        kerr = become_user(kr->uid, kr->gid);
-+        kerr = k5c_become_user(kr->uid, kr->gid, kr->posix_domain);
-         if (kerr != 0) {
-             DEBUG(SSSDBG_CRIT_FAILURE, "become_user failed.\n");
-             ret = EFAULT;
-diff --git a/src/providers/krb5/krb5_child_handler.c b/src/providers/krb5/krb5_child_handler.c
-index 680e67b089fcb32280352af24aae35af133a52f3..87e79a06e917aadb622455bccfc2e9c6769f70c2 100644
---- a/src/providers/krb5/krb5_child_handler.c
-+++ b/src/providers/krb5/krb5_child_handler.c
-@@ -107,6 +107,7 @@ static errno_t create_send_buffer(struct krb5child_req *kr,
-     uint32_t validate;
-     uint32_t send_pac;
-     uint32_t use_enterprise_principal;
-+    uint32_t posix_domain;
-     size_t username_len = 0;
-     errno_t ret;
- 
-@@ -131,6 +132,17 @@ static errno_t create_send_buffer(struct krb5child_req *kr,
-             break;
-     }
- 
-+    switch (kr->dom->type) {
-+    case DOM_TYPE_POSIX:
-+        posix_domain = 1;
-+        break;
-+    case DOM_TYPE_APPLICATION:
-+        posix_domain = 0;
-+        break;
-+    default:
-+        return EINVAL;
-+    }
-+
-     if (kr->pd->cmd == SSS_CMD_RENEW || kr->is_offline) {
-         use_enterprise_principal = false;
-     } else {
-@@ -151,7 +163,7 @@ static errno_t create_send_buffer(struct krb5child_req *kr,
-         kr->pd->cmd == SSS_CMD_RENEW ||
-         kr->pd->cmd == SSS_PAM_CHAUTHTOK_PRELIM ||
-         kr->pd->cmd == SSS_PAM_CHAUTHTOK) {
--        buf->size += 4*sizeof(uint32_t) + strlen(kr->ccname) + strlen(keytab) +
-+        buf->size += 5*sizeof(uint32_t) + strlen(kr->ccname) + strlen(keytab) +
-                      sss_authtok_get_size(kr->pd->authtok);
- 
-         buf->size += sizeof(uint32_t);
-@@ -182,6 +194,7 @@ static errno_t create_send_buffer(struct krb5child_req *kr,
-     SAFEALIGN_COPY_UINT32(&buf->data[rp], &kr->uid, &rp);
-     SAFEALIGN_COPY_UINT32(&buf->data[rp], &kr->gid, &rp);
-     SAFEALIGN_COPY_UINT32(&buf->data[rp], &validate, &rp);
-+    SAFEALIGN_COPY_UINT32(&buf->data[rp], &posix_domain, &rp);
-     SAFEALIGN_COPY_UINT32(&buf->data[rp], &kr->is_offline, &rp);
-     SAFEALIGN_COPY_UINT32(&buf->data[rp], &send_pac, &rp);
-     SAFEALIGN_COPY_UINT32(&buf->data[rp], &use_enterprise_principal, &rp);
-diff --git a/src/providers/krb5/krb5_delayed_online_authentication.c b/src/providers/krb5/krb5_delayed_online_authentication.c
-index bf2ef775573ba6bad79a99ad43b5d9748516e794..1cb7eade0e4cb9afbc4d031a07b3519ba08456d6 100644
---- a/src/providers/krb5/krb5_delayed_online_authentication.c
-+++ b/src/providers/krb5/krb5_delayed_online_authentication.c
-@@ -234,6 +234,7 @@ static void delayed_online_authentication_callback(void *private_data)
- }
- 
- errno_t add_user_to_delayed_online_authentication(struct krb5_ctx *krb5_ctx,
-+                                                  struct sss_domain_info *domain,
-                                                   struct pam_data *pd,
-                                                   uid_t uid)
- {
-@@ -242,6 +243,12 @@ errno_t add_user_to_delayed_online_authentication(struct krb5_ctx *krb5_ctx,
-     hash_value_t value;
-     struct pam_data *new_pd;
- 
-+    if (domain->type != DOM_TYPE_POSIX) {
-+        DEBUG(SSSDBG_MINOR_FAILURE,
-+              "Domain type does not support delayed authentication\n");
-+        return ENOTSUP;
-+    }
-+
-     if (krb5_ctx->deferred_auth_ctx == NULL) {
-         DEBUG(SSSDBG_CRIT_FAILURE,
-               "Missing context for delayed online authentication.\n");
-diff --git a/src/providers/krb5/krb5_init.c b/src/providers/krb5/krb5_init.c
-index 12c8dfcc49af75de619ec0858aaff81504698273..66ae68fb4773af3987f2062246bc6493107c74d5 100644
---- a/src/providers/krb5/krb5_init.c
-+++ b/src/providers/krb5/krb5_init.c
-@@ -136,6 +136,9 @@ errno_t sssm_krb5_init(TALLOC_CTX *mem_ctx,
-         return ENOMEM;
-     }
- 
-+    /* Only needed to generate random ccache names for non-POSIX domains */
-+    srand(time(NULL) * getpid());
-+
-     ret = sss_krb5_get_options(ctx, be_ctx->cdb, be_ctx->conf_path, &ctx->opts);
-     if (ret != EOK) {
-         DEBUG(SSSDBG_CRIT_FAILURE, "Unable to get krb5 options [%d]: %s\n",
--- 
-2.9.3
-
diff --git a/SOURCES/0072-SDAP-Rename-sdap_posix_check-to-sdap_gc_posix_check.patch b/SOURCES/0072-SDAP-Rename-sdap_posix_check-to-sdap_gc_posix_check.patch
new file mode 100644
index 0000000..3335d35
--- /dev/null
+++ b/SOURCES/0072-SDAP-Rename-sdap_posix_check-to-sdap_gc_posix_check.patch
@@ -0,0 +1,251 @@
+From ada45cd38a73b1b196db459849fcc19781bc06fc Mon Sep 17 00:00:00 2001
+From: Jakub Hrozek <jhrozek@redhat.com>
+Date: Wed, 6 Dec 2017 16:26:15 +0100
+Subject: [PATCH 72/83] SDAP: Rename sdap_posix_check to sdap_gc_posix_check
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Because searching the LDAP port of Active Directory server with a NULL
+search base yields an error:
+    https://technet.microsoft.com/en-us/library/cc755809(v=ws.10).aspx
+we changed the POSIX check request to only run against a GC connection
+in a previous patch. To make it clearer to the caller that this request
+should only be used with a GC connection, this patch renames the
+request.
+
+There are no functional changes in this patch.
+
+Reviewed-by: Pavel Březina <pbrezina@redhat.com>
+Reviewed-by: Sumit Bose <sbose@redhat.com>
+(cherry picked from commit ba8a92bbd59f189bd1323dd0c4010cdfc694be35)
+---
+ src/providers/ldap/ldap_id.c         | 20 +++++++--------
+ src/providers/ldap/sdap_async.c      | 48 ++++++++++++++++++------------------
+ src/providers/ldap/sdap_async.h      | 16 ++++++++----
+ src/providers/ldap/sdap_async_enum.c | 10 ++++----
+ 4 files changed, 50 insertions(+), 44 deletions(-)
+
+diff --git a/src/providers/ldap/ldap_id.c b/src/providers/ldap/ldap_id.c
+index b5ac3a749113a281fe8a5564ac341ced0570eded..3824f8f9aa8d2892664f1182376bedf6fb8627f6 100644
+--- a/src/providers/ldap/ldap_id.c
++++ b/src/providers/ldap/ldap_id.c
+@@ -415,10 +415,10 @@ static void users_get_connect_done(struct tevent_req *subreq)
+                                state->conn,
+                                state->use_id_mapping,
+                                !state->non_posix)) {
+-        subreq = sdap_posix_check_send(state, state->ev, state->ctx->opts,
+-                                       sdap_id_op_handle(state->op),
+-                                       dp_opt_get_int(state->ctx->opts->basic,
+-                                                      SDAP_SEARCH_TIMEOUT));
++        subreq = sdap_gc_posix_check_send(state, state->ev, state->ctx->opts,
++                                          sdap_id_op_handle(state->op),
++                                          dp_opt_get_int(state->ctx->opts->basic,
++                                                         SDAP_SEARCH_TIMEOUT));
+         if (subreq == NULL) {
+             tevent_req_error(req, ENOMEM);
+             return;
+@@ -441,7 +441,7 @@ static void users_get_posix_check_done(struct tevent_req *subreq)
+     struct users_get_state *state = tevent_req_data(req,
+                                                     struct users_get_state);
+ 
+-    ret = sdap_posix_check_recv(subreq, &has_posix);
++    ret = sdap_gc_posix_check_recv(subreq, &has_posix);
+     talloc_zfree(subreq);
+     if (ret != EOK) {
+         /* We can only finish the id_op on error as the connection
+@@ -960,10 +960,10 @@ static void groups_get_connect_done(struct tevent_req *subreq)
+                                state->conn,
+                                state->use_id_mapping,
+                                !state->non_posix)) {
+-        subreq = sdap_posix_check_send(state, state->ev, state->ctx->opts,
+-                                       sdap_id_op_handle(state->op),
+-                                       dp_opt_get_int(state->ctx->opts->basic,
+-                                                      SDAP_SEARCH_TIMEOUT));
++        subreq = sdap_gc_posix_check_send(state, state->ev, state->ctx->opts,
++                                          sdap_id_op_handle(state->op),
++                                          dp_opt_get_int(state->ctx->opts->basic,
++                                                         SDAP_SEARCH_TIMEOUT));
+         if (subreq == NULL) {
+             tevent_req_error(req, ENOMEM);
+             return;
+@@ -985,7 +985,7 @@ static void groups_get_posix_check_done(struct tevent_req *subreq)
+     struct groups_get_state *state = tevent_req_data(req,
+                                                      struct groups_get_state);
+ 
+-    ret = sdap_posix_check_recv(subreq, &has_posix);
++    ret = sdap_gc_posix_check_recv(subreq, &has_posix);
+     talloc_zfree(subreq);
+     if (ret != EOK) {
+         /* We can only finish the id_op on error as the connection
+diff --git a/src/providers/ldap/sdap_async.c b/src/providers/ldap/sdap_async.c
+index 1df0b85f4bda6442d8da66784ad7424306b1f051..a9bea4f80903aeb9d0fdb4d2b8f2acb36d81d6fe 100644
+--- a/src/providers/ldap/sdap_async.c
++++ b/src/providers/ldap/sdap_async.c
+@@ -2573,12 +2573,12 @@ int sdap_asq_search_recv(struct tevent_req *req,
+ }
+ 
+ /* ==Posix attribute presence test================================= */
+-static void sdap_posix_check_done(struct tevent_req *subreq);
+-static errno_t sdap_posix_check_parse(struct sdap_handle *sh,
+-                                      struct sdap_msg *msg,
+-                                      void *pvt);
++static void sdap_gc_posix_check_done(struct tevent_req *subreq);
++static errno_t sdap_gc_posix_check_parse(struct sdap_handle *sh,
++                                         struct sdap_msg *msg,
++                                         void *pvt);
+ 
+-struct sdap_posix_check_state {
++struct sdap_gc_posix_check_state {
+     struct tevent_context *ev;
+     struct sdap_options *opts;
+     struct sdap_handle *sh;
+@@ -2591,16 +2591,16 @@ struct sdap_posix_check_state {
+ };
+ 
+ struct tevent_req *
+-sdap_posix_check_send(TALLOC_CTX *memctx, struct tevent_context *ev,
+-                      struct sdap_options *opts, struct sdap_handle *sh,
+-                      int timeout)
++sdap_gc_posix_check_send(TALLOC_CTX *memctx, struct tevent_context *ev,
++                         struct sdap_options *opts, struct sdap_handle *sh,
++                         int timeout)
+ {
+     struct tevent_req *req = NULL;
+     struct tevent_req *subreq = NULL;
+-    struct sdap_posix_check_state *state;
++    struct sdap_gc_posix_check_state *state;
+     errno_t ret;
+ 
+-    req = tevent_req_create(memctx, &state, struct sdap_posix_check_state);
++    req = tevent_req_create(memctx, &state, struct sdap_gc_posix_check_state);
+     if (req == NULL) {
+         return NULL;
+     }
+@@ -2636,13 +2636,13 @@ sdap_posix_check_send(TALLOC_CTX *memctx, struct tevent_context *ev,
+                                  LDAP_SCOPE_SUBTREE, state->filter,
+                                  state->attrs,
+                                  NULL, NULL, 1, state->timeout,
+-                                 sdap_posix_check_parse, state,
++                                 sdap_gc_posix_check_parse, state,
+                                  SDAP_SRCH_FLG_SIZELIMIT_SILENT);
+     if (subreq == NULL) {
+         ret = ENOMEM;
+         goto fail;
+     }
+-    tevent_req_set_callback(subreq, sdap_posix_check_done, req);
++    tevent_req_set_callback(subreq, sdap_gc_posix_check_done, req);
+ 
+     return req;
+ 
+@@ -2652,13 +2652,13 @@ fail:
+     return req;
+ }
+ 
+-static errno_t sdap_posix_check_parse(struct sdap_handle *sh,
+-                                      struct sdap_msg *msg,
+-                                      void *pvt)
++static errno_t sdap_gc_posix_check_parse(struct sdap_handle *sh,
++                                         struct sdap_msg *msg,
++                                         void *pvt)
+ {
+     struct berval **vals = NULL;
+-    struct sdap_posix_check_state *state =
+-        talloc_get_type(pvt, struct sdap_posix_check_state);
++    struct sdap_gc_posix_check_state *state =
++        talloc_get_type(pvt, struct sdap_gc_posix_check_state);
+     char *dn;
+     char *endptr;
+ 
+@@ -2700,12 +2700,12 @@ done:
+     return EOK;
+ }
+ 
+-static void sdap_posix_check_done(struct tevent_req *subreq)
++static void sdap_gc_posix_check_done(struct tevent_req *subreq)
+ {
+     struct tevent_req *req = tevent_req_callback_data(subreq,
+                                                       struct tevent_req);
+-    struct sdap_posix_check_state *state =
+-        tevent_req_data(req, struct sdap_posix_check_state);
++    struct sdap_gc_posix_check_state *state =
++        tevent_req_data(req, struct sdap_gc_posix_check_state);
+     errno_t ret;
+ 
+     ret = sdap_get_generic_ext_recv(subreq, NULL, NULL, NULL);
+@@ -2730,11 +2730,11 @@ static void sdap_posix_check_done(struct tevent_req *subreq)
+     tevent_req_done(req);
+ }
+ 
+-int sdap_posix_check_recv(struct tevent_req *req,
+-                          bool *_has_posix)
++int sdap_gc_posix_check_recv(struct tevent_req *req,
++                             bool *_has_posix)
+ {
+-    struct sdap_posix_check_state *state = tevent_req_data(req,
+-                                            struct sdap_posix_check_state);
++    struct sdap_gc_posix_check_state *state = tevent_req_data(req,
++                                            struct sdap_gc_posix_check_state);
+ 
+     TEVENT_REQ_RETURN_ON_ERROR(req);
+ 
+diff --git a/src/providers/ldap/sdap_async.h b/src/providers/ldap/sdap_async.h
+index 7216ba032e551196cf5258b4e58fbfc8cfe417ea..26f13e38bf6dff08a8cd0e6b3b5282effda80c9e 100644
+--- a/src/providers/ldap/sdap_async.h
++++ b/src/providers/ldap/sdap_async.h
+@@ -266,13 +266,19 @@ int sdap_deref_search_recv(struct tevent_req *req,
+                            size_t *reply_count,
+                            struct sdap_deref_attrs ***reply);
+ 
++/*
++ * This request should only be ran against a Global Catalog connection
++ * because it uses a NULL search base to search all domains in the forest,
++ * which would return an error with an LDAP port:
++ *  https://technet.microsoft.com/en-us/library/cc755809(v=ws.10).aspx
++ */
+ struct tevent_req *
+-sdap_posix_check_send(TALLOC_CTX *memctx, struct tevent_context *ev,
+-                      struct sdap_options *opts, struct sdap_handle *sh,
+-                      int timeout);
++sdap_gc_posix_check_send(TALLOC_CTX *memctx, struct tevent_context *ev,
++                         struct sdap_options *opts, struct sdap_handle *sh,
++                         int timeout);
+ 
+-int sdap_posix_check_recv(struct tevent_req *req,
+-                          bool *_has_posix);
++int sdap_gc_posix_check_recv(struct tevent_req *req,
++                             bool *_has_posix);
+ 
+ struct tevent_req *
+ sdap_sd_search_send(TALLOC_CTX *memctx,
+diff --git a/src/providers/ldap/sdap_async_enum.c b/src/providers/ldap/sdap_async_enum.c
+index ec0c679823a8cd9820bb978f77799a3f86621271..ea9d51adc7f94145cd7e689893bf7fd81028c5bb 100644
+--- a/src/providers/ldap/sdap_async_enum.c
++++ b/src/providers/ldap/sdap_async_enum.c
+@@ -200,10 +200,10 @@ static void sdap_dom_enum_ex_get_users(struct tevent_req *subreq)
+                                state->user_conn,
+                                use_id_mapping,
+                                true)) {
+-        subreq = sdap_posix_check_send(state, state->ev, state->ctx->opts,
+-                                       sdap_id_op_handle(state->user_op),
+-                                       dp_opt_get_int(state->ctx->opts->basic,
+-                                                      SDAP_SEARCH_TIMEOUT));
++        subreq = sdap_gc_posix_check_send(state, state->ev, state->ctx->opts,
++                                          sdap_id_op_handle(state->user_op),
++                                          dp_opt_get_int(state->ctx->opts->basic,
++                                                         SDAP_SEARCH_TIMEOUT));
+         if (subreq == NULL) {
+             tevent_req_error(req, ENOMEM);
+             return;
+@@ -233,7 +233,7 @@ static void sdap_dom_enum_ex_posix_check_done(struct tevent_req *subreq)
+     struct sdap_dom_enum_ex_state *state = tevent_req_data(req,
+                                                 struct sdap_dom_enum_ex_state);
+ 
+-    ret = sdap_posix_check_recv(subreq, &has_posix);
++    ret = sdap_gc_posix_check_recv(subreq, &has_posix);
+     talloc_zfree(subreq);
+     if (ret != EOK && ret != ERR_NO_POSIX) {
+         /* We can only finish the id_op on error as the connection
+-- 
+2.14.3
+
diff --git a/SOURCES/0073-DP-Create-a-new-handler-function-getAccountDomain.patch b/SOURCES/0073-DP-Create-a-new-handler-function-getAccountDomain.patch
new file mode 100644
index 0000000..72a0f99
--- /dev/null
+++ b/SOURCES/0073-DP-Create-a-new-handler-function-getAccountDomain.patch
@@ -0,0 +1,455 @@
+From 1bed72e4faa2734b0eef6a107b2dc24bf052e576 Mon Sep 17 00:00:00 2001
+From: Jakub Hrozek <jhrozek@redhat.com>
+Date: Mon, 30 Oct 2017 20:50:41 +0100
+Subject: [PATCH 73/83] DP: Create a new handler function getAccountDomain()
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Adds a new method getAccountDomain() which is a bit similar to
+getAccountInfo, except it doesn't fetch, parse and store the entry, but
+just returns the domain or a subdomain the entry was found in.
+
+At the moment, the method only supports requests by ID.
+
+A default handler is provided (and in this patch used by all the
+domains) which returns ERR_GET_ACCT_DOM_NOT_SUPPORTED. This return
+code should be evaluated by the responder so that this DP method is
+not called again, because it's not supported by the back end type.
+
+Reviewed-by: Pavel Březina <pbrezina@redhat.com>
+Reviewed-by: Sumit Bose <sbose@redhat.com>
+(cherry picked from commit c0f9f5a0f6d71a1596ee3cef549b4b02295313c3)
+---
+ src/providers/ad/ad_init.c                       |   4 +
+ src/providers/data_provider/dp.h                 |  20 ++++
+ src/providers/data_provider/dp_custom_data.h     |   6 ++
+ src/providers/data_provider/dp_iface.c           |   3 +-
+ src/providers/data_provider/dp_iface.h           |  17 ++++
+ src/providers/data_provider/dp_iface.xml         |   7 ++
+ src/providers/data_provider/dp_iface_generated.c |  31 +++++++
+ src/providers/data_provider/dp_iface_generated.h |   5 +
+ src/providers/data_provider/dp_target_id.c       | 113 +++++++++++++++++++++++
+ src/providers/files/files_init.c                 |   6 ++
+ src/providers/ipa/ipa_init.c                     |   4 +
+ src/providers/ldap/ldap_init.c                   |   4 +
+ src/providers/proxy/proxy_init.c                 |   4 +
+ src/util/util_errors.c                           |   1 +
+ src/util/util_errors.h                           |   1 +
+ 15 files changed, 225 insertions(+), 1 deletion(-)
+
+diff --git a/src/providers/ad/ad_init.c b/src/providers/ad/ad_init.c
+index e62025d4acd24844a5c7082d00c597516f35de16..7efb6aa71cbd2551422c87e0b0c5c1fe91390375 100644
+--- a/src/providers/ad/ad_init.c
++++ b/src/providers/ad/ad_init.c
+@@ -510,6 +510,10 @@ errno_t sssm_ad_id_init(TALLOC_CTX *mem_ctx,
+                   sdap_online_check_handler_send, sdap_online_check_handler_recv, id_ctx->sdap_id_ctx,
+                   struct sdap_id_ctx, void, struct dp_reply_std);
+ 
++    dp_set_method(dp_methods, DPM_ACCT_DOMAIN_HANDLER,
++                  default_account_domain_send, default_account_domain_recv, NULL,
++                  void, struct dp_get_acct_domain_data, struct dp_reply_std);
++
+     return EOK;
+ }
+ 
+diff --git a/src/providers/data_provider/dp.h b/src/providers/data_provider/dp.h
+index aa5b781158c54545b26034602bb25db46b189e87..ceb49da53b88142924e1792c6f64a22ec369677b 100644
+--- a/src/providers/data_provider/dp.h
++++ b/src/providers/data_provider/dp.h
+@@ -82,6 +82,7 @@ enum dp_methods {
+     DPM_HOSTID_HANDLER,
+     DPM_DOMAINS_HANDLER,
+     DPM_SESSION_HANDLER,
++    DPM_ACCT_DOMAIN_HANDLER,
+ 
+     DPM_REFRESH_ACCESS_RULES,
+ 
+@@ -179,4 +180,23 @@ void dp_sbus_reset_users_memcache(struct data_provider *provider);
+ void dp_sbus_reset_groups_memcache(struct data_provider *provider);
+ void dp_sbus_reset_initgr_memcache(struct data_provider *provider);
+ 
++/*
++ * A dummy handler for DPM_ACCT_DOMAIN_HANDLER.
++ *
++ * Its purpose is to always return ERR_GET_ACCT_DOM_NOT_SUPPORTED
++ * which the responder should evaluate as "this back end does not
++ * support locating entries' domain" and never call
++ * DPM_ACCT_DOMAIN_HANDLER again
++ *
++ * This request cannot fail, except for critical errors like OOM.
++ */
++struct tevent_req *
++default_account_domain_send(TALLOC_CTX *mem_ctx,
++                            void *unused_ctx,
++                            struct dp_get_acct_domain_data *data,
++                            struct dp_req_params *params);
++errno_t default_account_domain_recv(TALLOC_CTX *mem_ctx,
++                                    struct tevent_req *req,
++                                    struct dp_reply_std *data);
++
+ #endif /* _DP_H_ */
+diff --git a/src/providers/data_provider/dp_custom_data.h b/src/providers/data_provider/dp_custom_data.h
+index d9de288b62f4f6763ceb205dc596876cfc58bf6c..7c64bde4513961e79200e852c7277f77e064d5a3 100644
+--- a/src/providers/data_provider/dp_custom_data.h
++++ b/src/providers/data_provider/dp_custom_data.h
+@@ -43,6 +43,12 @@ struct dp_subdomains_data {
+     const char *domain_hint;
+ };
+ 
++struct dp_get_acct_domain_data {
++    uint32_t entry_type;
++    uint32_t filter_type;
++    const char *filter_value;
++};
++
+ struct dp_id_data {
+     uint32_t entry_type;
+     uint32_t filter_type;
+diff --git a/src/providers/data_provider/dp_iface.c b/src/providers/data_provider/dp_iface.c
+index 28d70e686f63a3572ac595f493aa1d59436c563f..124be0048f38a93d06561ff7b0d1916838587103 100644
+--- a/src/providers/data_provider/dp_iface.c
++++ b/src/providers/data_provider/dp_iface.c
+@@ -33,7 +33,8 @@ struct iface_dp iface_dp = {
+     .autofsHandler = dp_autofs_handler,
+     .hostHandler = dp_host_handler,
+     .getDomains = dp_subdomains_handler,
+-    .getAccountInfo = dp_get_account_info_handler
++    .getAccountInfo = dp_get_account_info_handler,
++    .getAccountDomain = dp_get_account_domain_handler,
+ };
+ 
+ struct iface_dp_backend iface_dp_backend = {
+diff --git a/src/providers/data_provider/dp_iface.h b/src/providers/data_provider/dp_iface.h
+index 759b9e6c9eb7f53836ae0b641b34e6c31e65779f..0a2f81eb5c108aa7596c974157b0dfafb041869f 100644
+--- a/src/providers/data_provider/dp_iface.h
++++ b/src/providers/data_provider/dp_iface.h
+@@ -58,6 +58,23 @@ errno_t dp_subdomains_handler(struct sbus_request *sbus_req,
+                               void *dp_cli,
+                               const char *domain_hint);
+ 
++/*
++ * Return a domain the account belongs to.
++ *
++ * The request uses the dp_reply_std structure for reply, with the following
++ * semantics:
++ *  - DP_ERR_OK - it is expected that the string message contains the domain name
++ *                the entry was found in. A 'negative' reply where the
++ *                request returns DP_ERR_OK, but no domain should be treated
++ *                as authoritative, as if the entry does not exist.
++ *  - DP_ERR_*  - the string message contains error string that corresponds
++ *                to the errno field in dp_reply_std().
++ */
++errno_t dp_get_account_domain_handler(struct sbus_request *sbus_req,
++                                      void *dp_cli,
++                                      uint32_t entry_type,
++                                      const char *filter);
++
+ /* org.freedesktop.sssd.DataProvider.Backend */
+ errno_t dp_backend_is_online(struct sbus_request *sbus_req,
+                              void *dp_cli,
+diff --git a/src/providers/data_provider/dp_iface.xml b/src/providers/data_provider/dp_iface.xml
+index 2bfa9dfa7e9d02d2d12c3358967f6969438a97a2..c2431850bca4baa529fb18e0480e781308b12dd6 100644
+--- a/src/providers/data_provider/dp_iface.xml
++++ b/src/providers/data_provider/dp_iface.xml
+@@ -79,5 +79,12 @@
+             <arg name="error" type="u" direction="out" />
+             <arg name="error_message" type="s" direction="out" />
+         </method>
++        <method name="getAccountDomain">
++            <arg name="entry_type" type="u" direction="in" />
++            <arg name="filter" type="s" direction="in" />
++            <arg name="dp_error" type="q" direction="out" />
++            <arg name="error" type="u" direction="out" />
++            <arg name="domain_name" type="s" direction="out" />
++        </method>
+     </interface>
+ </node>
+diff --git a/src/providers/data_provider/dp_iface_generated.c b/src/providers/data_provider/dp_iface_generated.c
+index 11ee2e24a69cc8d4d19fdbeed613e76081aef15d..4d093444536b15d8a17f7e507b93948e1df6ffee 100644
+--- a/src/providers/data_provider/dp_iface_generated.c
++++ b/src/providers/data_provider/dp_iface_generated.c
+@@ -313,6 +313,30 @@ int iface_dp_getAccountInfo_finish(struct sbus_request *req, uint16_t arg_dp_err
+                                          DBUS_TYPE_INVALID);
+ }
+ 
++/* arguments for org.freedesktop.sssd.dataprovider.getAccountDomain */
++const struct sbus_arg_meta iface_dp_getAccountDomain__in[] = {
++    { "entry_type", "u" },
++    { "filter", "s" },
++    { NULL, }
++};
++
++/* arguments for org.freedesktop.sssd.dataprovider.getAccountDomain */
++const struct sbus_arg_meta iface_dp_getAccountDomain__out[] = {
++    { "dp_error", "q" },
++    { "error", "u" },
++    { "domain_name", "s" },
++    { NULL, }
++};
++
++int iface_dp_getAccountDomain_finish(struct sbus_request *req, uint16_t arg_dp_error, uint32_t arg_error, const char *arg_domain_name)
++{
++   return sbus_request_return_and_finish(req,
++                                         DBUS_TYPE_UINT16, &arg_dp_error,
++                                         DBUS_TYPE_UINT32, &arg_error,
++                                         DBUS_TYPE_STRING, &arg_domain_name,
++                                         DBUS_TYPE_INVALID);
++}
++
+ /* methods for org.freedesktop.sssd.dataprovider */
+ const struct sbus_method_meta iface_dp__methods[] = {
+     {
+@@ -357,6 +381,13 @@ const struct sbus_method_meta iface_dp__methods[] = {
+         offsetof(struct iface_dp, getAccountInfo),
+         invoke_uusss_method,
+     },
++    {
++        "getAccountDomain", /* name */
++        iface_dp_getAccountDomain__in,
++        iface_dp_getAccountDomain__out,
++        offsetof(struct iface_dp, getAccountDomain),
++        invoke_us_method,
++    },
+     { NULL, }
+ };
+ 
+diff --git a/src/providers/data_provider/dp_iface_generated.h b/src/providers/data_provider/dp_iface_generated.h
+index 541a90b0b5a5bc0a346cbd04974d33c8bb0983c5..b629ec77487328a41615f3ca7e812088730f40c4 100644
+--- a/src/providers/data_provider/dp_iface_generated.h
++++ b/src/providers/data_provider/dp_iface_generated.h
+@@ -38,6 +38,7 @@
+ #define IFACE_DP_HOSTHANDLER "hostHandler"
+ #define IFACE_DP_GETDOMAINS "getDomains"
+ #define IFACE_DP_GETACCOUNTINFO "getAccountInfo"
++#define IFACE_DP_GETACCOUNTDOMAIN "getAccountDomain"
+ 
+ /* ------------------------------------------------------------------------
+  * DBus handlers
+@@ -110,6 +111,7 @@ struct iface_dp {
+     int (*hostHandler)(struct sbus_request *req, void *data, uint32_t arg_dp_flags, const char *arg_name, const char *arg_alias);
+     int (*getDomains)(struct sbus_request *req, void *data, const char *arg_domain_hint);
+     int (*getAccountInfo)(struct sbus_request *req, void *data, uint32_t arg_dp_flags, uint32_t arg_entry_type, const char *arg_filter, const char *arg_domain, const char *arg_extra);
++    int (*getAccountDomain)(struct sbus_request *req, void *data, uint32_t arg_entry_type, const char *arg_filter);
+ };
+ 
+ /* finish function for autofsHandler */
+@@ -124,6 +126,9 @@ int iface_dp_getDomains_finish(struct sbus_request *req, uint16_t arg_dp_error,
+ /* finish function for getAccountInfo */
+ int iface_dp_getAccountInfo_finish(struct sbus_request *req, uint16_t arg_dp_error, uint32_t arg_error, const char *arg_error_message);
+ 
++/* finish function for getAccountDomain */
++int iface_dp_getAccountDomain_finish(struct sbus_request *req, uint16_t arg_dp_error, uint32_t arg_error, const char *arg_domain_name);
++
+ /* ------------------------------------------------------------------------
+  * DBus Interface Metadata
+  *
+diff --git a/src/providers/data_provider/dp_target_id.c b/src/providers/data_provider/dp_target_id.c
+index 820a6574cb3a224cce4b7d8286af306f234454a3..11a36e9ce9b1aefcabb04dfe51395b6f4e4bc899 100644
+--- a/src/providers/data_provider/dp_target_id.c
++++ b/src/providers/data_provider/dp_target_id.c
+@@ -490,3 +490,116 @@ done:
+ 
+     return ret;
+ }
++
++static bool
++check_and_parse_acct_domain_filter(struct dp_get_acct_domain_data *data,
++                                   const char *filter)
++{
++    /* We will use sizeof() to determine the length of a string so we don't
++     * call strlen over and over again with each request. Not a bottleneck,
++     * but unnecessary and simple to avoid. */
++    static struct {
++        const char *name;
++        size_t lenght;
++        uint32_t type;
++    } types[] = {FILTER_TYPE("idnumber", BE_FILTER_IDNUM),
++                 {0, 0, 0}};
++    int i;
++
++    if (SBUS_IS_STRING_EMPTY(filter)) {
++        return false;
++    }
++
++    for (i = 0; types[i].name != NULL; i++) {
++        if (strncmp(filter, types[i].name, types[i].lenght) == 0) {
++            data->filter_type = types[i].type;
++            data->filter_value = SBUS_SET_STRING(&filter[types[i].lenght]);
++            return true;
++        }
++    }
++
++    if (strcmp(filter, ENUM_INDICATOR) == 0) {
++        data->filter_type = BE_FILTER_ENUM;
++        data->filter_value = NULL;
++        return true;
++    }
++
++    return false;
++}
++
++errno_t dp_get_account_domain_handler(struct sbus_request *sbus_req,
++                                      void *dp_cli,
++                                      uint32_t entry_type,
++                                      const char *filter)
++{
++    struct dp_get_acct_domain_data *data;
++    const char *key = NULL;
++    errno_t ret;
++
++    data = talloc_zero(sbus_req, struct dp_get_acct_domain_data);
++    if (data == NULL) {
++        return ENOMEM;
++    }
++    data->entry_type = entry_type;
++
++    if (!check_and_parse_acct_domain_filter(data, filter)) {
++        ret = EINVAL;
++        goto done;
++    }
++
++    dp_req_with_reply(dp_cli, NULL, "AccountDomain", key, sbus_req,
++                      DPT_ID, DPM_ACCT_DOMAIN_HANDLER, 0, data,
++                      dp_req_reply_std, struct dp_reply_std);
++
++    ret = EOK;
++
++done:
++    if (ret != EOK) {
++        talloc_free(data);
++    }
++
++    return ret;
++}
++
++struct default_account_domain_state {
++    struct dp_reply_std reply;
++};
++
++struct tevent_req *
++default_account_domain_send(TALLOC_CTX *mem_ctx,
++                            void *unused_ctx,
++                            struct dp_get_acct_domain_data *data,
++                            struct dp_req_params *params)
++{
++    struct default_account_domain_state *state;
++    struct tevent_req *req;
++
++    req = tevent_req_create(mem_ctx, &state,
++                            struct default_account_domain_state);
++    if (req == NULL) {
++        DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create() failed\n");
++        return NULL;
++    }
++
++    dp_reply_std_set(&state->reply,
++                     DP_ERR_DECIDE, ERR_GET_ACCT_DOM_NOT_SUPPORTED,
++                     NULL);
++    tevent_req_done(req);
++    tevent_req_post(req, params->ev);
++    return req;
++}
++
++errno_t default_account_domain_recv(TALLOC_CTX *mem_ctx,
++                                    struct tevent_req *req,
++                                    struct dp_reply_std *data)
++{
++    struct default_account_domain_state *state = NULL;
++
++    state = tevent_req_data(req, struct default_account_domain_state);
++
++    TEVENT_REQ_RETURN_ON_ERROR(req);
++
++    *data = state->reply;
++
++    return EOK;
++}
+diff --git a/src/providers/files/files_init.c b/src/providers/files/files_init.c
+index b91dfbac9bf9d4b678ebdfa6b1cb0971b4477dd9..8e5cd4cf913b79653616120d6ed6540e62ade932 100644
+--- a/src/providers/files/files_init.c
++++ b/src/providers/files/files_init.c
+@@ -88,5 +88,11 @@ int sssm_files_id_init(TALLOC_CTX *mem_ctx,
+                   ctx, struct files_id_ctx,
+                   struct dp_id_data, struct dp_reply_std);
+ 
++    dp_set_method(dp_methods, DPM_ACCT_DOMAIN_HANDLER,
++                  default_account_domain_send,
++                  default_account_domain_recv,
++                  NULL, void,
++                  struct dp_get_acct_domain_data, struct dp_reply_std);
++
+     return EOK;
+ }
+diff --git a/src/providers/ipa/ipa_init.c b/src/providers/ipa/ipa_init.c
+index f335d51fd65959d256c54a5d92c594a24e895b7c..754e5315c3a7f84ac2901986ecdda73e4dad26bc 100644
+--- a/src/providers/ipa/ipa_init.c
++++ b/src/providers/ipa/ipa_init.c
+@@ -754,6 +754,10 @@ errno_t sssm_ipa_id_init(TALLOC_CTX *mem_ctx,
+                   sdap_online_check_handler_send, sdap_online_check_handler_recv, id_ctx->sdap_id_ctx,
+                   struct sdap_id_ctx, void, struct dp_reply_std);
+ 
++    dp_set_method(dp_methods, DPM_ACCT_DOMAIN_HANDLER,
++                  default_account_domain_send, default_account_domain_recv, NULL,
++                  void, struct dp_get_acct_domain_data, struct dp_reply_std);
++
+     return EOK;
+ }
+ 
+diff --git a/src/providers/ldap/ldap_init.c b/src/providers/ldap/ldap_init.c
+index 43d905893081c31ed659fd1ef8343f965bdc5af0..c0ede8941ee8480c2ec4765f89d12e903edcf012 100644
+--- a/src/providers/ldap/ldap_init.c
++++ b/src/providers/ldap/ldap_init.c
+@@ -531,6 +531,10 @@ errno_t sssm_ldap_id_init(TALLOC_CTX *mem_ctx,
+                   sdap_online_check_handler_send, sdap_online_check_handler_recv, id_ctx,
+                   struct sdap_id_ctx, void, struct dp_reply_std);
+ 
++    dp_set_method(dp_methods, DPM_ACCT_DOMAIN_HANDLER,
++                  default_account_domain_send, default_account_domain_recv, NULL,
++                  void, struct dp_get_acct_domain_data, struct dp_reply_std);
++
+     return EOK;
+ }
+ 
+diff --git a/src/providers/proxy/proxy_init.c b/src/providers/proxy/proxy_init.c
+index 7c9d3dafbdf1f9448cc8f8b473aea15cf4206afc..7d997cb16ee62f10f4b86c9c3ab373a48676fe75 100644
+--- a/src/providers/proxy/proxy_init.c
++++ b/src/providers/proxy/proxy_init.c
+@@ -351,6 +351,10 @@ errno_t sssm_proxy_id_init(TALLOC_CTX *mem_ctx,
+                   proxy_account_info_handler_send, proxy_account_info_handler_recv, ctx,
+                   struct proxy_id_ctx, struct dp_id_data, struct dp_reply_std);
+ 
++    dp_set_method(dp_methods, DPM_ACCT_DOMAIN_HANDLER,
++                  default_account_domain_send, default_account_domain_recv, NULL,
++                  void, struct dp_get_acct_domain_data, struct dp_reply_std);
++
+     ret = EOK;
+ 
+ done:
+diff --git a/src/util/util_errors.c b/src/util/util_errors.c
+index 5a92a2dcf6e65f93bc9732cebf562756357123b6..9a9ba3f3063cab4afb538c3a58527a2d2ed3fffd 100644
+--- a/src/util/util_errors.c
++++ b/src/util/util_errors.c
+@@ -115,6 +115,7 @@ struct err_string error_to_str[] = {
+     { "Unable to initialize SSL" }, /* ERR_SSL_FAILURE */
+     { "Unable to verify peer" }, /* ERR_UNABLE_TO_VERIFY_PEER */
+     { "Unable to resolve host" }, /* ERR_UNABLE_TO_RESOLVE_HOST */
++    { "GetAccountDomain() not supported" }, /* ERR_GET_ACCT_DOM_NOT_SUPPORTED */
+     { "ERR_LAST" } /* ERR_LAST */
+ };
+ 
+diff --git a/src/util/util_errors.h b/src/util/util_errors.h
+index 509ccb805fb97e59f9da0ea2f991ece2f2030ca4..5ee9862c3f2f60c078693b1b85a40f15436e818c 100644
+--- a/src/util/util_errors.h
++++ b/src/util/util_errors.h
+@@ -137,6 +137,7 @@ enum sssd_errors {
+     ERR_SSL_FAILURE,
+     ERR_UNABLE_TO_VERIFY_PEER,
+     ERR_UNABLE_TO_RESOLVE_HOST,
++    ERR_GET_ACCT_DOM_NOT_SUPPORTED,
+     ERR_LAST            /* ALWAYS LAST */
+ };
+ 
+-- 
+2.14.3
+
diff --git a/SOURCES/0073-KCM-Fix-off-by-one-error-in-secrets-key-parsing.patch b/SOURCES/0073-KCM-Fix-off-by-one-error-in-secrets-key-parsing.patch
deleted file mode 100644
index 0530278..0000000
--- a/SOURCES/0073-KCM-Fix-off-by-one-error-in-secrets-key-parsing.patch
+++ /dev/null
@@ -1,211 +0,0 @@
-From 088be07a9e5aae54379a7f98e9e4615cd4451501 Mon Sep 17 00:00:00 2001
-From: Jakub Hrozek <jhrozek@redhat.com>
-Date: Wed, 29 Mar 2017 22:49:09 +0200
-Subject: [PATCH 73/90] KCM: Fix off-by-one error in secrets key parsing
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-When parsing the secrets key, the code tried to protect against malformed keys
-or keys that are too short, but it did an error - the UUID stringified
-form is 36 bytes long, so the UUID_STR_SIZE is 37 because UUID_STR_SIZE
-accounts for the null terminator.
-
-But the code, that was trying to assert that there are two characters after
-the UUID string (separator and at least a single character for the name)
-didn't take the NULL terminator (which strlen() doesn't return) into
-account and ended up rejecting all ccaches whose name is only a single
-character.
-
-Reviewed-by: Fabiano Fidêncio <fidencio@redhat.com>
-(cherry picked from commit 7d73049884e3a96ca3b00b5bd4104f4edd6287ab)
----
- src/responder/kcm/kcmsrv_ccache_json.c       | 43 +++++++++-------
- src/tests/cmocka/test_kcm_json_marshalling.c | 75 ++++++++++++++++++++++++++++
- 2 files changed, 101 insertions(+), 17 deletions(-)
-
-diff --git a/src/responder/kcm/kcmsrv_ccache_json.c b/src/responder/kcm/kcmsrv_ccache_json.c
-index 40b64861c209206d6f60ccd0843857edee24a844..8199bc613e4204859438e1cd820f3f4b2123dd7e 100644
---- a/src/responder/kcm/kcmsrv_ccache_json.c
-+++ b/src/responder/kcm/kcmsrv_ccache_json.c
-@@ -109,6 +109,28 @@ static const char *sec_key_create(TALLOC_CTX *mem_ctx,
-                            "%s%c%s", uuid_str, SEC_KEY_SEPARATOR, name);
- }
- 
-+static bool sec_key_valid(const char *sec_key)
-+{
-+    if (sec_key == NULL) {
-+        return false;
-+    }
-+
-+    if (strlen(sec_key) < UUID_STR_SIZE + 1) {
-+        /* One char for separator (at UUID_STR_SIZE, because strlen doesn't
-+         * include the '\0', but UUID_STR_SIZE does) and at least one for
-+         * the name */
-+        DEBUG(SSSDBG_CRIT_FAILURE, "Key %s is too short\n", sec_key);
-+        return false;
-+    }
-+
-+    if (sec_key[UUID_STR_SIZE - 1] != SEC_KEY_SEPARATOR) {
-+        DEBUG(SSSDBG_CRIT_FAILURE, "Key doesn't contain the separator\n");
-+        return false;
-+    }
-+
-+    return true;
-+}
-+
- static errno_t sec_key_parse(TALLOC_CTX *mem_ctx,
-                              const char *sec_key,
-                              const char **_name,
-@@ -116,9 +138,7 @@ static errno_t sec_key_parse(TALLOC_CTX *mem_ctx,
- {
-     char uuid_str[UUID_STR_SIZE];
- 
--    if (strlen(sec_key) < UUID_STR_SIZE + 2) {
--        /* One char for separator and at least one for the name */
--        DEBUG(SSSDBG_CRIT_FAILURE, "Key %s is too short\n", sec_key);
-+    if (!sec_key_valid(sec_key)) {
-         return EINVAL;
-     }
- 
-@@ -143,14 +163,7 @@ errno_t sec_key_get_uuid(const char *sec_key,
- {
-     char uuid_str[UUID_STR_SIZE];
- 
--    if (strlen(sec_key) < UUID_STR_SIZE + 2) {
--        /* One char for separator and at least one for the name */
--        DEBUG(SSSDBG_CRIT_FAILURE, "Key %s is too short\n", sec_key);
--        return EINVAL;
--    }
--
--    if (sec_key[UUID_STR_SIZE-1] != SEC_KEY_SEPARATOR) {
--        DEBUG(SSSDBG_CRIT_FAILURE, "Key doesn't contain the separator\n");
-+    if (!sec_key_valid(sec_key)) {
-         return EINVAL;
-     }
- 
-@@ -162,9 +175,7 @@ errno_t sec_key_get_uuid(const char *sec_key,
- 
- const char *sec_key_get_name(const char *sec_key)
- {
--    if (strlen(sec_key) < UUID_STR_SIZE + 2) {
--        /* One char for separator and at least one for the name */
--        DEBUG(SSSDBG_CRIT_FAILURE, "Key %s is too short\n", sec_key);
-+    if (!sec_key_valid(sec_key)) {
-         return NULL;
-     }
- 
-@@ -174,9 +185,7 @@ const char *sec_key_get_name(const char *sec_key)
- bool sec_key_match_name(const char *sec_key,
-                         const char *name)
- {
--    if (strlen(sec_key) < UUID_STR_SIZE + 2) {
--        /* One char for separator and at least one for the name */
--        DEBUG(SSSDBG_MINOR_FAILURE, "Key %s is too short\n", sec_key);
-+    if (!sec_key_valid(sec_key) || name == NULL) {
-         return false;
-     }
- 
-diff --git a/src/tests/cmocka/test_kcm_json_marshalling.c b/src/tests/cmocka/test_kcm_json_marshalling.c
-index 8eff2f501066c70a8730cd3d4dc41b92d7a03e4c..108eaf55628029a6de8c23cd6486bdccc42c0364 100644
---- a/src/tests/cmocka/test_kcm_json_marshalling.c
-+++ b/src/tests/cmocka/test_kcm_json_marshalling.c
-@@ -32,6 +32,12 @@
- 
- #define TEST_CREDS                "TESTCREDS"
- 
-+#define TEST_UUID_STR             "5f8f296b-02be-4e86-9235-500e82354186"
-+#define TEST_SEC_KEY_ONEDIGIT     TEST_UUID_STR"-0"
-+#define TEST_SEC_KEY_MULTIDIGITS  TEST_UUID_STR"-123456"
-+
-+#define TEST_SEC_KEY_NOSEP        TEST_UUID_STR"+0"
-+
- const struct kcm_ccdb_ops ccdb_mem_ops;
- const struct kcm_ccdb_ops ccdb_sec_ops;
- 
-@@ -188,6 +194,72 @@ static void test_kcm_ccache_marshall_unmarshall(void **state)
-     assert_int_equal(ret, EOK);
- 
-     assert_cc_equal(cc, cc2);
-+
-+    /* This key is exactly one byte shorter than it should be */
-+    ret = sec_kv_to_ccache(test_ctx,
-+                           TEST_UUID_STR"-",
-+                           (const char *) data,
-+                           &owner,
-+                           &cc2);
-+    assert_int_equal(ret, EINVAL);
-+}
-+
-+void test_sec_key_get_uuid(void **state)
-+{
-+    errno_t ret;
-+    uuid_t uuid;
-+    char str_uuid[UUID_STR_SIZE];
-+
-+    uuid_clear(uuid);
-+    ret = sec_key_get_uuid(TEST_SEC_KEY_ONEDIGIT, uuid);
-+    assert_int_equal(ret, EOK);
-+    uuid_unparse(uuid, str_uuid);
-+    assert_string_equal(TEST_UUID_STR, str_uuid);
-+
-+    ret = sec_key_get_uuid(TEST_SEC_KEY_NOSEP, uuid);
-+    assert_int_equal(ret, EINVAL);
-+
-+    ret = sec_key_get_uuid(TEST_UUID_STR, uuid);
-+    assert_int_equal(ret, EINVAL);
-+
-+    ret = sec_key_get_uuid(NULL, uuid);
-+    assert_int_equal(ret, EINVAL);
-+}
-+
-+void test_sec_key_get_name(void **state)
-+{
-+    const char *name;
-+
-+    name = sec_key_get_name(TEST_SEC_KEY_ONEDIGIT);
-+    assert_non_null(name);
-+    assert_string_equal(name, "0");
-+
-+    name = sec_key_get_name(TEST_SEC_KEY_MULTIDIGITS);
-+    assert_non_null(name);
-+    assert_string_equal(name, "123456");
-+
-+    name = sec_key_get_name(TEST_UUID_STR);
-+    assert_null(name);
-+
-+    name = sec_key_get_name(TEST_SEC_KEY_NOSEP);
-+    assert_null(name);
-+
-+    name = sec_key_get_name(NULL);
-+    assert_null(name);
-+}
-+
-+void test_sec_key_match_name(void **state)
-+{
-+    assert_true(sec_key_match_name(TEST_SEC_KEY_ONEDIGIT, "0"));
-+    assert_true(sec_key_match_name(TEST_SEC_KEY_MULTIDIGITS, "123456"));
-+
-+    assert_false(sec_key_match_name(TEST_SEC_KEY_MULTIDIGITS, "0"));
-+    assert_false(sec_key_match_name(TEST_SEC_KEY_ONEDIGIT, "123456"));
-+
-+    assert_false(sec_key_match_name(TEST_UUID_STR, "0"));
-+    assert_false(sec_key_match_name(TEST_SEC_KEY_NOSEP, "0"));
-+    assert_false(sec_key_match_name(TEST_SEC_KEY_ONEDIGIT, NULL));
-+    assert_false(sec_key_match_name(NULL, "0"));
- }
- 
- int main(int argc, const char *argv[])
-@@ -205,6 +277,9 @@ int main(int argc, const char *argv[])
-         cmocka_unit_test_setup_teardown(test_kcm_ccache_marshall_unmarshall,
-                                         setup_kcm_marshalling,
-                                         teardown_kcm_marshalling),
-+        cmocka_unit_test(test_sec_key_get_uuid),
-+        cmocka_unit_test(test_sec_key_get_name),
-+        cmocka_unit_test(test_sec_key_match_name),
-     };
- 
-     /* Set debug level to invalid value so we can deside if -d 0 was used. */
--- 
-2.9.3
-
diff --git a/SOURCES/0074-AD-Implement-a-real-getAccountDomain-handler-for-the.patch b/SOURCES/0074-AD-Implement-a-real-getAccountDomain-handler-for-the.patch
new file mode 100644
index 0000000..980917f
--- /dev/null
+++ b/SOURCES/0074-AD-Implement-a-real-getAccountDomain-handler-for-the.patch
@@ -0,0 +1,553 @@
+From 427a1f162e0ceb97e4e9491f81048646bd144910 Mon Sep 17 00:00:00 2001
+From: Jakub Hrozek <jhrozek@redhat.com>
+Date: Tue, 7 Nov 2017 17:01:34 +0100
+Subject: [PATCH 74/83] AD: Implement a real getAccountDomain handler for the
+ AD provider
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+After this patch, the AD provider drops the default getAccountDomain
+handler in favor of the handler added in this patch.
+
+The handler first checks if the domain is eligible for locating
+the domain of an ID with the help of the Global Catalog at all, which
+only happens if:
+    - the Global Catalog is enabled
+    - POSIX IDs are used, not ID-mapping
+    - the Global catalog contains some POSIX IDs
+
+If all these hold true, then the Global Catalog is searched with
+an empty search base, which searches the whole GC. If a single entry
+is returned, its original DN is converted to a domain name and returned.
+
+Reviewed-by: Pavel Březina <pbrezina@redhat.com>
+Reviewed-by: Sumit Bose <sbose@redhat.com>
+(cherry picked from commit 095844d6b48aef483c33e5a369a405ae686e044d)
+---
+ src/providers/ad/ad_id.c   | 469 +++++++++++++++++++++++++++++++++++++++++++++
+ src/providers/ad/ad_id.h   |  10 +
+ src/providers/ad/ad_init.c |   4 +-
+ 3 files changed, 481 insertions(+), 2 deletions(-)
+
+diff --git a/src/providers/ad/ad_id.c b/src/providers/ad/ad_id.c
+index e14ada386f16851a65097952c85e57b7acda14aa..0b8f49819405c7dbbfa18b5359f7743441dc65e5 100644
+--- a/src/providers/ad/ad_id.c
++++ b/src/providers/ad/ad_id.c
+@@ -27,6 +27,7 @@
+ #include "providers/ad/ad_pac.h"
+ #include "providers/ldap/sdap_async_enum.h"
+ #include "providers/ldap/sdap_idmap.h"
++#include "providers/ldap/sdap_async.h"
+ 
+ static void
+ disable_gc(struct ad_options *ad_options)
+@@ -1076,3 +1077,471 @@ ad_enumeration_recv(struct tevent_req *req)
+     return EOK;
+ }
+ 
++static errno_t ad_get_account_domain_prepare_search(struct tevent_req *req);
++static errno_t ad_get_account_domain_connect_retry(struct tevent_req *req);
++static void ad_get_account_domain_connect_done(struct tevent_req *subreq);
++static void ad_get_account_domain_posix_check_done(struct tevent_req *subreq);
++static void ad_get_account_domain_search(struct tevent_req *req);
++static void ad_get_account_domain_search_done(struct tevent_req *subreq);
++static void ad_get_account_domain_evaluate(struct tevent_req *req);
++
++struct ad_get_account_domain_state {
++    struct tevent_context *ev;
++    struct ad_id_ctx *id_ctx;
++    struct sdap_id_ctx *sdap_id_ctx;
++    struct sdap_domain *sdom;
++    uint32_t entry_type;
++    uint32_t filter_type;
++    char *clean_filter;
++
++    bool twopass;
++
++    struct sdap_search_base **search_bases;
++    size_t base_iter;
++    const char *base_filter;
++    char *filter;
++    const char **attrs;
++    int dp_error;
++    struct dp_reply_std reply;
++    struct sdap_id_op *op;
++    struct sysdb_attrs **objects;
++    size_t count;
++
++    const char *found_domain_name;
++};
++
++struct tevent_req *
++ad_get_account_domain_send(TALLOC_CTX *mem_ctx,
++                           struct ad_id_ctx *id_ctx,
++                           struct dp_get_acct_domain_data *data,
++                           struct dp_req_params *params)
++{
++    struct ad_get_account_domain_state *state;
++    struct tevent_req *req;
++    errno_t ret;
++    bool use_id_mapping;
++
++    req = tevent_req_create(mem_ctx, &state,
++                            struct ad_get_account_domain_state);
++    if (req == NULL) {
++        DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create() failed\n");
++        return NULL;
++    }
++    state->ev = params->ev;
++    state->id_ctx = id_ctx;
++    state->sdap_id_ctx = id_ctx->sdap_id_ctx;
++    state->entry_type = data->entry_type & BE_REQ_TYPE_MASK;
++    state->filter_type = data->filter_type;
++    state->attrs = talloc_array(state, const char *, 2);
++    if (state->attrs == NULL) {
++        ret = ENOMEM;
++        goto immediately;
++    }
++    state->attrs[0] = "objectclass";
++    state->attrs[1] = NULL;
++
++    if (params->be_ctx->domain->mpg == true
++            || state->entry_type == BE_REQ_USER_AND_GROUP) {
++        state->twopass = true;
++        if (state->entry_type == BE_REQ_USER_AND_GROUP) {
++            state->entry_type = BE_REQ_GROUP;
++        }
++    }
++
++    /* The get-account-domain request only works with GC */
++    if (dp_opt_get_bool(id_ctx->ad_options->basic, AD_ENABLE_GC) == false) {
++        DEBUG(SSSDBG_CONF_SETTINGS,
++              "Global catalog support is not enabled, "
++              "cannot locate the account domain\n");
++        ret = ERR_GET_ACCT_DOM_NOT_SUPPORTED;
++        goto immediately;
++    }
++
++    state->sdom = sdap_domain_get(id_ctx->sdap_id_ctx->opts,
++                                  params->be_ctx->domain);
++    if (state->sdom == NULL) {
++        DEBUG(SSSDBG_CRIT_FAILURE, "Cannot find sdap_domain\n");
++        ret = EIO;
++        goto immediately;
++    }
++
++    /* Currently we only support locating the account domain
++     * if ID mapping is disabled. With ID mapping enabled, we can
++     * already shortcut the 'real' ID request
++     */
++    use_id_mapping = sdap_idmap_domain_has_algorithmic_mapping(
++                                        state->sdap_id_ctx->opts->idmap_ctx,
++                                        state->sdom->dom->name,
++                                        state->sdom->dom->domain_id);
++    if (use_id_mapping == true) {
++        DEBUG(SSSDBG_CONF_SETTINGS,
++              "No point in locating domain with GC if ID-mapping "
++              "is enabled\n");
++        ret = ERR_GET_ACCT_DOM_NOT_SUPPORTED;
++        goto immediately;
++    }
++
++    ret = sss_filter_sanitize(state, data->filter_value, &state->clean_filter);
++    if (ret != EOK) {
++        DEBUG(SSSDBG_OP_FAILURE,
++              "Cannot sanitize filter [%d]: %s\n", ret, sss_strerror(ret));
++        goto immediately;
++    }
++
++    ret = ad_get_account_domain_prepare_search(req);
++    if (ret != EOK) {
++        goto immediately;
++    }
++
++    /* FIXME - should gc_ctx always default to ignore_offline on creation
++     * time rather than setting the flag on first use?
++     */
++    id_ctx->gc_ctx->ignore_mark_offline = true;
++    state->op = sdap_id_op_create(state, id_ctx->gc_ctx->conn_cache);
++    if (state->op == NULL) {
++        DEBUG(SSSDBG_OP_FAILURE, "sdap_id_op_create failed\n");
++        ret = ENOMEM;
++        goto immediately;
++    }
++
++    ret = ad_get_account_domain_connect_retry(req);
++    if (ret != EOK) {
++        DEBUG(SSSDBG_OP_FAILURE, "Connection error");
++        goto immediately;
++    }
++
++    return req;
++
++immediately:
++    dp_reply_std_set(&state->reply, DP_ERR_DECIDE, ret, NULL);
++
++    /* TODO For backward compatibility we always return EOK to DP now. */
++    tevent_req_done(req);
++    tevent_req_post(req, params->ev);
++
++    return req;
++}
++
++static errno_t ad_get_account_domain_prepare_search(struct tevent_req *req)
++{
++    struct ad_get_account_domain_state *state = tevent_req_data(req,
++                                          struct ad_get_account_domain_state);
++    const char *attr_name = NULL;
++    const char *objectclass = NULL;
++
++    switch (state->entry_type) {
++    case BE_REQ_USER:
++        state->search_bases = state->sdom->user_search_bases;
++        attr_name = state->sdap_id_ctx->opts->user_map[SDAP_AT_USER_UID].name;
++        objectclass = state->sdap_id_ctx->opts->user_map[SDAP_OC_USER].name;
++        break;
++    case BE_REQ_GROUP:
++        state->search_bases = state->sdom->group_search_bases;
++        attr_name = state->sdap_id_ctx->opts->group_map[SDAP_AT_GROUP_GID].name;
++        objectclass = state->sdap_id_ctx->opts->group_map[SDAP_OC_GROUP].name;
++        break;
++    default:
++        DEBUG(SSSDBG_OP_FAILURE,
++              "Unsupported request type %X\n",
++              state->entry_type & BE_REQ_TYPE_MASK);
++        return EINVAL;
++    }
++
++    switch (state->filter_type) {
++    case BE_FILTER_IDNUM:
++        break;
++    default:
++        DEBUG(SSSDBG_OP_FAILURE,
++              "Unsupported filter type %X\n", state->filter_type);
++        return EINVAL;
++    }
++
++    talloc_zfree(state->base_filter);
++    state->base_filter = talloc_asprintf(state,
++                                         "(&(%s=%s)(objectclass=%s))",
++                                         attr_name,
++                                         state->clean_filter,
++                                         objectclass);
++    if (state->base_filter == NULL) {
++        return ENOMEM;
++    }
++
++    return EOK;
++}
++
++static errno_t ad_get_account_domain_connect_retry(struct tevent_req *req)
++{
++    struct ad_get_account_domain_state *state = tevent_req_data(req,
++                                          struct ad_get_account_domain_state);
++    struct tevent_req *subreq;
++    errno_t ret;
++
++    subreq = sdap_id_op_connect_send(state->op, state, &ret);
++    if (subreq == NULL) {
++        return ENOMEM;
++    }
++
++    tevent_req_set_callback(subreq, ad_get_account_domain_connect_done, req);
++    return ret;
++}
++
++static void ad_get_account_domain_connect_done(struct tevent_req *subreq)
++{
++    struct tevent_req *req = tevent_req_callback_data(subreq,
++                                                      struct tevent_req);
++    struct ad_get_account_domain_state *state = tevent_req_data(req,
++                                          struct ad_get_account_domain_state);
++    int dp_error = DP_ERR_FATAL;
++    errno_t ret;
++
++    ret = sdap_id_op_connect_recv(subreq, &dp_error);
++    talloc_zfree(subreq);
++
++    if (ret != EOK) {
++        state->dp_error = dp_error;
++        tevent_req_error(req, ret);
++        return;
++    }
++
++    /* If POSIX attributes have been requested with an AD server and we
++     * have no idea about POSIX attributes support, run a one-time check
++     */
++    if (state->sdap_id_ctx->srv_opts &&
++        state->sdap_id_ctx->srv_opts->posix_checked == false) {
++        subreq = sdap_gc_posix_check_send(state,
++                                          state->ev,
++                                          state->sdap_id_ctx->opts,
++                                          sdap_id_op_handle(state->op),
++                                          dp_opt_get_int(
++                                              state->sdap_id_ctx->opts->basic,
++                                              SDAP_SEARCH_TIMEOUT));
++        if (subreq == NULL) {
++            tevent_req_error(req, ENOMEM);
++            return;
++        }
++        tevent_req_set_callback(subreq, ad_get_account_domain_posix_check_done, req);
++        return;
++    }
++
++    ad_get_account_domain_search(req);
++}
++
++static void ad_get_account_domain_posix_check_done(struct tevent_req *subreq)
++{
++    struct tevent_req *req = tevent_req_callback_data(subreq,
++                                                      struct tevent_req);
++    struct ad_get_account_domain_state *state = tevent_req_data(req,
++                                          struct ad_get_account_domain_state);
++    int dp_error = DP_ERR_FATAL;
++    bool has_posix;
++    errno_t ret;
++    errno_t ret2;
++
++    ret = sdap_gc_posix_check_recv(subreq, &has_posix);
++    talloc_zfree(subreq);
++    if (ret != EOK) {
++        /* We can only finish the id_op on error as the connection
++         * is re-used by the real search
++         */
++        ret2 = sdap_id_op_done(state->op, ret, &dp_error);
++        if (dp_error == DP_ERR_OK && ret2 != EOK) {
++            /* retry */
++            ret = ad_get_account_domain_connect_retry(req);
++            if (ret != EOK) {
++                tevent_req_error(req, ret);
++            }
++            return;
++        }
++
++        tevent_req_error(req, ret);
++        return;
++    }
++
++    state->sdap_id_ctx->srv_opts->posix_checked = true;
++
++    /*
++     * If the GC has no POSIX attributes, there is nothing we can do.
++     * Return an error and let the responders disable the functionality
++     * from now on.
++     */
++    if (has_posix == false) {
++        DEBUG(SSSDBG_CONF_SETTINGS,
++              "The Global Catalog has no POSIX attributes\n");
++
++        disable_gc(state->id_ctx->ad_options);
++        dp_reply_std_set(&state->reply,
++                         DP_ERR_DECIDE, ERR_GET_ACCT_DOM_NOT_SUPPORTED,
++                         NULL);
++        tevent_req_done(req);
++        return;
++    }
++
++    ad_get_account_domain_search(req);
++}
++
++static void ad_get_account_domain_search(struct tevent_req *req)
++{
++    struct ad_get_account_domain_state *state = tevent_req_data(req,
++                                          struct ad_get_account_domain_state);
++    struct tevent_req *subreq;
++
++    talloc_zfree(state->filter);
++    state->filter = sdap_combine_filters(state, state->base_filter,
++                        state->search_bases[state->base_iter]->filter);
++    if (state->filter == NULL) {
++        tevent_req_error(req, ENOMEM);
++        return;
++    }
++
++    subreq = sdap_get_generic_send(state, state->ev, state->sdap_id_ctx->opts,
++                                   sdap_id_op_handle(state->op),
++                                   "",
++                                   LDAP_SCOPE_SUBTREE,
++                                   state->filter,
++                                   state->attrs, NULL, 0,
++                                   dp_opt_get_int(state->sdap_id_ctx->opts->basic,
++                                                  SDAP_SEARCH_TIMEOUT),
++                                   false);
++
++    if (subreq == NULL) {
++        DEBUG(SSSDBG_OP_FAILURE, "sdap_get_generic_send failed.\n");
++        tevent_req_error(req, EIO);
++        return;
++    }
++
++    tevent_req_set_callback(subreq, ad_get_account_domain_search_done, req);
++}
++
++static void ad_get_account_domain_search_done(struct tevent_req *subreq)
++{
++    struct tevent_req *req = tevent_req_callback_data(subreq,
++                                                      struct tevent_req);
++    struct ad_get_account_domain_state *state = tevent_req_data(req,
++                                          struct ad_get_account_domain_state);
++    size_t count;
++    struct sysdb_attrs **objects;
++    errno_t ret;
++
++    ret = sdap_get_generic_recv(subreq, state,
++                                &count, &objects);
++    talloc_zfree(subreq);
++    if (ret) {
++        tevent_req_error(req, ret);
++        return;
++    }
++
++    DEBUG(SSSDBG_TRACE_FUNC,
++          "Search returned %zu results.\n", count);
++
++    if (count > 0) {
++        size_t copied;
++
++        state->objects =
++                talloc_realloc(state,
++                               state->objects,
++                               struct sysdb_attrs *,
++                               state->count + count + 1);
++        if (!state->objects) {
++            tevent_req_error(req, ENOMEM);
++            return;
++        }
++
++        copied = sdap_steal_objects_in_dom(state->sdap_id_ctx->opts,
++                                           state->objects,
++                                           state->count,
++                                           NULL,
++                                           objects, count,
++                                           false);
++
++        state->count += copied;
++        state->objects[state->count] = NULL;
++    }
++
++    /* Even though we search with an empty search base (=across all domains)
++     * the reason we iterate over search bases is that the search bases can
++     * also contain a filter which might restrict the IDs we find
++     */
++    state->base_iter++;
++    if (state->search_bases[state->base_iter]) {
++        /* There are more search bases to try */
++        ad_get_account_domain_search(req);
++        return;
++    }
++
++    /* No more searches, evaluate results */
++    ad_get_account_domain_evaluate(req);
++}
++
++static void ad_get_account_domain_evaluate(struct tevent_req *req)
++{
++    struct ad_get_account_domain_state *state = tevent_req_data(req,
++                                          struct ad_get_account_domain_state);
++    struct sss_domain_info *obj_dom;
++    errno_t ret;
++
++    if (state->count == 0) {
++        if (state->twopass
++                && state->entry_type != BE_REQ_USER) {
++            DEBUG(SSSDBG_TRACE_FUNC, "Retrying search\n");
++
++            state->entry_type = BE_REQ_USER;
++            state->base_iter = 0;
++            ret = ad_get_account_domain_prepare_search(req);
++            if (ret != EOK) {
++                DEBUG(SSSDBG_OP_FAILURE, "Cannot retry search\n");
++                tevent_req_error(req, ret);
++                return;
++            }
++
++            ad_get_account_domain_search(req);
++            return;
++        }
++
++        DEBUG(SSSDBG_TRACE_FUNC, "Not found\n");
++        dp_reply_std_set(&state->reply, DP_ERR_DECIDE, ERR_NOT_FOUND, NULL);
++        tevent_req_done(req);
++        return;
++    } else if (state->count > 1) {
++        /* FIXME: If more than one entry was found, return error for now
++         * as the account requsts have no way of returning multiple
++         * messages back until we switch to the rdp_* requests
++         * from the responder side
++         */
++        DEBUG(SSSDBG_OP_FAILURE, "Multiple entries found, error!\n");
++        dp_reply_std_set(&state->reply, DP_ERR_DECIDE, ERANGE, NULL);
++        tevent_req_done(req);
++        return;
++    }
++
++    /* Exactly one entry was found */
++    obj_dom = sdap_get_object_domain(state->sdap_id_ctx->opts,
++                                     state->objects[0],
++                                     state->sdom->dom);
++    if (obj_dom == NULL) {
++        DEBUG(SSSDBG_OP_FAILURE,
++              "Could not match entry with domain!\n");
++        dp_reply_std_set(&state->reply, DP_ERR_DECIDE, ERR_NOT_FOUND, NULL);
++        tevent_req_done(req);
++        return;
++    }
++
++    DEBUG(SSSDBG_TRACE_INTERNAL,
++          "Found object in domain %s\n", obj_dom->name);
++    dp_reply_std_set(&state->reply, DP_ERR_DECIDE, EOK, obj_dom->name);
++    tevent_req_done(req);
++}
++
++errno_t ad_get_account_domain_recv(TALLOC_CTX *mem_ctx,
++                                   struct tevent_req *req,
++                                   struct dp_reply_std *data)
++{
++    struct ad_get_account_domain_state *state = NULL;
++
++    state = tevent_req_data(req, struct ad_get_account_domain_state);
++
++    TEVENT_REQ_RETURN_ON_ERROR(req);
++
++    *data = state->reply;
++
++    return EOK;
++}
+diff --git a/src/providers/ad/ad_id.h b/src/providers/ad/ad_id.h
+index 145fdc8f2dfdeda5a17b0ce5892a547da934c244..5154393c5f125f472c92155006aac14d04bbca1a 100644
+--- a/src/providers/ad/ad_id.h
++++ b/src/providers/ad/ad_id.h
+@@ -54,4 +54,14 @@ ad_enumeration_send(TALLOC_CTX *mem_ctx,
+ errno_t
+ ad_enumeration_recv(struct tevent_req *req);
+ 
++struct tevent_req *
++ad_get_account_domain_send(TALLOC_CTX *mem_ctx,
++                           struct ad_id_ctx *id_ctx,
++                           struct dp_get_acct_domain_data *data,
++                           struct dp_req_params *params);
++
++errno_t ad_get_account_domain_recv(TALLOC_CTX *mem_ctx,
++                                   struct tevent_req *req,
++                                   struct dp_reply_std *data);
++
+ #endif /* AD_ID_H_ */
+diff --git a/src/providers/ad/ad_init.c b/src/providers/ad/ad_init.c
+index 7efb6aa71cbd2551422c87e0b0c5c1fe91390375..22a3ecf7e5a020da88b6c9164f5999d13a9aa5e3 100644
+--- a/src/providers/ad/ad_init.c
++++ b/src/providers/ad/ad_init.c
+@@ -511,8 +511,8 @@ errno_t sssm_ad_id_init(TALLOC_CTX *mem_ctx,
+                   struct sdap_id_ctx, void, struct dp_reply_std);
+ 
+     dp_set_method(dp_methods, DPM_ACCT_DOMAIN_HANDLER,
+-                  default_account_domain_send, default_account_domain_recv, NULL,
+-                  void, struct dp_get_acct_domain_data, struct dp_reply_std);
++                  ad_get_account_domain_send, ad_get_account_domain_recv, id_ctx,
++                  struct ad_id_ctx, struct dp_get_acct_domain_data, struct dp_reply_std);
+ 
+     return EOK;
+ }
+-- 
+2.14.3
+
diff --git a/SOURCES/0074-tcurl-add-support-for-ssl-and-raw-output.patch b/SOURCES/0074-tcurl-add-support-for-ssl-and-raw-output.patch
deleted file mode 100644
index 6351c10..0000000
--- a/SOURCES/0074-tcurl-add-support-for-ssl-and-raw-output.patch
+++ /dev/null
@@ -1,1478 +0,0 @@
-From 2ca9a394063baac075e05b14fcc6c027027ab8f9 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Pavel=20B=C5=99ezina?= <pbrezina@redhat.com>
-Date: Fri, 24 Feb 2017 10:40:43 +0100
-Subject: [PATCH 74/90] tcurl: add support for ssl and raw output
-
-At first, this patch separates curl_easy handle from the multi-handle
-processing and makes it encapsulated in custom tcurl_request structure.
-This allows us to separate protocol initialization from its asynchonous
-logic which gives us the ability to set different options for each
-request without over-extending the parameter list.
-
-In this patch we implement options for peer verification for TLS-enabled
-protocols and to return response with body and headers together.
-
-Reviewed-by: Simo Sorce <simo@redhat.com>
-Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
-(cherry picked from commit 300b9e9217ee1ed8d845ed2370c5ccf5c87afb36)
----
- src/tests/tcurl_test_tool.c |  41 +-
- src/util/tev_curl.c         | 992 +++++++++++++++++++++++++-------------------
- src/util/tev_curl.h         | 172 +++++++-
- src/util/util_errors.c      |   4 +
- src/util/util_errors.h      |   4 +
- 5 files changed, 755 insertions(+), 458 deletions(-)
-
-diff --git a/src/tests/tcurl_test_tool.c b/src/tests/tcurl_test_tool.c
-index 2af950ebb76a22bdf4a6dfd58442b10486e64293..9a6266f89131ffd3a561e857af85df9854c44949 100644
---- a/src/tests/tcurl_test_tool.c
-+++ b/src/tests/tcurl_test_tool.c
-@@ -42,9 +42,7 @@ static void request_done(struct tevent_req *req)
-     struct tool_ctx *tool_ctx = tevent_req_callback_data(req,
-                                                          struct tool_ctx);
- 
--    tool_ctx->error = tcurl_http_recv(tool_ctx, req,
--                                      &http_code,
--                                      &outbuf);
-+    tool_ctx->error = tcurl_request_recv(tool_ctx, req, &outbuf, &http_code);
-     talloc_zfree(req);
- 
-     if (tool_ctx->error != EOK) {
-@@ -87,16 +85,17 @@ int main(int argc, const char *argv[])
-           "The path to the HTTP server socket", NULL },
-         { "get", 'g', POPT_ARG_NONE, NULL, 'g', "Perform a HTTP GET (default)", NULL },
-         { "put", 'p', POPT_ARG_NONE, NULL, 'p', "Perform a HTTP PUT", NULL },
--        { "del", 'd', POPT_ARG_NONE, NULL, 'd', "Perform a HTTP DELETE", NULL },
-         { "post", 'o', POPT_ARG_NONE, NULL, 'o', "Perform a HTTP POST", NULL },
-+        { "del", 'd', POPT_ARG_NONE, NULL, 'd', "Perform a HTTP DELETE", NULL },
-         { "verbose", 'v', POPT_ARG_NONE, NULL, 'v', "Print response code and body", NULL },
-         POPT_TABLEEND
-     };
- 
-     struct tevent_req *req;
-     struct tevent_context *ev;
--    enum tcurl_http_request req_type = TCURL_HTTP_GET;
-+    enum tcurl_http_method method = TCURL_HTTP_GET;
-     struct tcurl_ctx *ctx;
-+    struct tcurl_request *tcurl_req;
-     struct tool_ctx *tool_ctx;
- 
-     const char *urls[MAXREQ] = { 0 };
-@@ -111,16 +110,16 @@ int main(int argc, const char *argv[])
-     while ((opt = poptGetNextOpt(pc)) > 0) {
-         switch (opt) {
-         case 'g':
--            req_type = TCURL_HTTP_GET;
-+            method = TCURL_HTTP_GET;
-             break;
-         case 'p':
--            req_type = TCURL_HTTP_PUT;
--            break;
--        case 'd':
--            req_type = TCURL_HTTP_DELETE;
-+            method = TCURL_HTTP_PUT;
-             break;
-         case 'o':
--            req_type = TCURL_HTTP_POST;
-+            method = TCURL_HTTP_POST;
-+            break;
-+        case 'd':
-+            method = TCURL_HTTP_DELETE;
-             break;
-         case 'v':
-             pc_verbose = 1;
-@@ -146,7 +145,7 @@ int main(int argc, const char *argv[])
-     }
- 
-     while ((extra_arg_ptr = poptGetArg(pc)) != NULL) {
--        switch (req_type) {
-+        switch(method) {
-         case TCURL_HTTP_GET:
-         case TCURL_HTTP_DELETE:
-         case TCURL_HTTP_POST:
-@@ -203,14 +202,16 @@ int main(int argc, const char *argv[])
-     }
- 
-     for (size_t i = 0; i < n_reqs; i++) {
--        req = tcurl_http_send(tool_ctx, ev, ctx,
--                              req_type,
--                              socket_path,
--                              urls[i],
--                              headers,
--                              inbufs[i],
--                              5);
--        if (req == NULL) {
-+        tcurl_req = tcurl_http(tool_ctx, method, socket_path,
-+                               urls[i], headers, inbufs[i]);
-+        if (tcurl_req == NULL) {
-+            DEBUG(SSSDBG_FATAL_FAILURE, "Unable to create TCURL request\n");
-+            talloc_zfree(tool_ctx);
-+            return 1;
-+        }
-+
-+        req = tcurl_request_send(tool_ctx, ev, ctx, tcurl_req, 10);
-+        if (ctx == NULL) {
-             DEBUG(SSSDBG_FATAL_FAILURE, "Could not create request\n");
-             talloc_zfree(tool_ctx);
-             return 1;
-diff --git a/src/util/tev_curl.c b/src/util/tev_curl.c
-index 645d1182d10f825f209f48e0ba7e6804dde1971c..c155f4c038d4215933ee30d41c694ad4a14ae132 100644
---- a/src/util/tev_curl.c
-+++ b/src/util/tev_curl.c
-@@ -34,8 +34,8 @@
- #include "util/util.h"
- #include "util/tev_curl.h"
- 
--#define IOBUF_CHUNK   1024
--#define IOBUF_MAX     4096
-+#define TCURL_IOBUF_CHUNK   1024
-+#define TCURL_IOBUF_MAX     4096
- 
- static bool global_is_curl_initialized;
- 
-@@ -71,39 +71,12 @@ struct tcurl_sock {
-     struct tevent_fd *fde;      /* tevent tracker of the fd events */
- };
- 
--/**
-- * @brief A state of one curl transfer
-- *
-- * Intentionally breaking the tevent coding style here and making the struct available
-- * in the whole module so that the structure is available to curl callbacks that
-- * need to access the state of the transfer.
-- *
-- * @see handle_curlmsg_done()
-- */
--struct tcurl_http_state {
--    /* Input parameters */
--    struct tcurl_ctx *tctx;
--    const char *socket_path;
--    const char *url;
--    int timeout;
--    struct sss_iobuf *inbuf;
--
--    /* Internal state */
--    CURL *http_handle;
--    struct curl_slist *curl_headers;
--
--    /* Output data */
--    struct sss_iobuf *outbuf;
--    long http_code;
--};
-+static void tcurl_request_done(struct tevent_req *req,
-+                               errno_t process_error,
-+                               int response_code);
- 
- static errno_t curl_code2errno(CURLcode crv)
- {
--    if (crv != CURLE_OK) {
--        DEBUG(SSSDBG_OP_FAILURE,
--              "curl error %d: %s\n", crv, curl_easy_strerror(crv));
--    }
--
-     switch (crv) {
-     /* HTTP error does not fail the whole request, just returns the error
-      * separately
-@@ -121,6 +94,47 @@ static errno_t curl_code2errno(CURLcode crv)
-         return ENOMEM;
-     case CURLE_OPERATION_TIMEDOUT:
-         return ETIMEDOUT;
-+    case CURLE_SSL_ISSUER_ERROR:
-+    case CURLE_SSL_CACERT_BADFILE:
-+    case CURLE_SSL_CACERT:
-+    case CURLE_SSL_CERTPROBLEM:
-+        return ERR_INVALID_CERT;
-+
-+    case CURLE_SSL_CRL_BADFILE:
-+    case CURLE_SSL_SHUTDOWN_FAILED:
-+    case CURLE_SSL_ENGINE_INITFAILED:
-+    case CURLE_USE_SSL_FAILED:
-+    case CURLE_SSL_CIPHER:
-+    case CURLE_SSL_ENGINE_SETFAILED:
-+    case CURLE_SSL_ENGINE_NOTFOUND:
-+    case CURLE_SSL_CONNECT_ERROR:
-+        return ERR_SSL_FAILURE;
-+    case CURLE_PEER_FAILED_VERIFICATION:
-+        return ERR_UNABLE_TO_VERIFY_PEER;
-+    case CURLE_COULDNT_RESOLVE_HOST:
-+        return ERR_UNABLE_TO_RESOLVE_HOST;
-+    default:
-+        break;
-+    }
-+
-+    return EIO;
-+}
-+
-+static errno_t curlm_code2errno(CURLcode crv)
-+{
-+    switch (crv) {
-+    case CURLM_OK:
-+        return EOK;
-+    case CURLM_BAD_SOCKET:
-+        return EPIPE;
-+    case CURLM_OUT_OF_MEMORY:
-+        return ENOMEM;
-+    case CURLM_BAD_HANDLE:
-+    case CURLM_BAD_EASY_HANDLE:
-+    case CURLM_UNKNOWN_OPTION:
-+        return EINVAL;
-+    case CURLM_INTERNAL_ERROR:
-+        return ERR_INTERNAL;
-     default:
-         break;
-     }
-@@ -145,22 +159,6 @@ static errno_t tcurl_global_init(void)
-     return EOK;
- }
- 
--static const char *http_req2str(enum tcurl_http_request req)
--{
--    switch (req) {
--    case TCURL_HTTP_GET:
--        return "GET";
--    case TCURL_HTTP_PUT:
--        return "PUT";
--    case TCURL_HTTP_DELETE:
--        return "DELETE";
--    case TCURL_HTTP_POST:
--        return "POST";
--    }
--
--    return "Uknown request type";
--}
--
- static int curl2tev_flags(int curlflags)
- {
-     int flags = 0;
-@@ -185,9 +183,9 @@ static void handle_curlmsg_done(CURLMsg *message)
-     CURL *easy_handle;
-     CURLcode crv;
-     struct tevent_req *req;
-+    long response_code = 0;
-     char *done_url;
-     errno_t ret;
--    struct tcurl_http_state *state;
- 
-     easy_handle = message->easy_handle;
-     if (easy_handle == NULL) {
-@@ -198,9 +196,8 @@ static void handle_curlmsg_done(CURLMsg *message)
-     if (DEBUG_IS_SET(SSSDBG_TRACE_FUNC)) {
-         crv = curl_easy_getinfo(easy_handle, CURLINFO_EFFECTIVE_URL, &done_url);
-         if (crv != CURLE_OK) {
--            DEBUG(SSSDBG_MINOR_FAILURE,
--                  "Cannot get CURLINFO_EFFECTIVE_URL [%d]: %s\n",
--                  crv, curl_easy_strerror(crv));
-+            DEBUG(SSSDBG_MINOR_FAILURE, "Cannot get CURLINFO_EFFECTIVE_URL "
-+                  "[%d]: %s\n", crv, curl_easy_strerror(crv));
-             /* not fatal since we need this only for debugging */
-         } else {
-             DEBUG(SSSDBG_TRACE_FUNC, "Handled %s\n", done_url);
-@@ -209,38 +206,32 @@ static void handle_curlmsg_done(CURLMsg *message)
- 
-     crv = curl_easy_getinfo(easy_handle, CURLINFO_PRIVATE, (void *) &req);
-     if (crv != CURLE_OK) {
--        DEBUG(SSSDBG_CRIT_FAILURE,
--              "Cannot get CURLINFO_PRIVATE [%d]: %s\n",
-+        DEBUG(SSSDBG_CRIT_FAILURE, "Cannot get CURLINFO_PRIVATE [%d]: %s\n",
-               crv, curl_easy_strerror(crv));
--        return;
--    }
--
--    state = tevent_req_data(req, struct tcurl_http_state);
--    if (state == NULL) {
--        DEBUG(SSSDBG_CRIT_FAILURE, "BUG: request has no state\n");
--        tevent_req_error(req, EFAULT);
--        return;
-+        ret = curl_code2errno(crv);
-+        goto done;
-     }
- 
-     ret = curl_code2errno(message->data.result);
-     if (ret != EOK) {
--        DEBUG(SSSDBG_OP_FAILURE,
--              "curl operation failed [%d]: %s\n", ret, sss_strerror(ret));
--        tevent_req_error(req, ret);
--        return;
-+        DEBUG(SSSDBG_OP_FAILURE, "CURL operation failed [%d]: %s\n",
-+              ret, sss_strerror(ret));
-+        goto done;
-     }
- 
--    /* If there was no fatal error, let's read the HTTP error code and mark
--     * the request as done
--     */
--    crv = curl_easy_getinfo(easy_handle, CURLINFO_RESPONSE_CODE, &state->http_code);
-+    /* If there was no fatal error, let's read the response code
-+     * and mark the request as done */
-+    crv = curl_easy_getinfo(easy_handle, CURLINFO_RESPONSE_CODE, &response_code);
-     if (crv != CURLE_OK) {
--        DEBUG(SSSDBG_OP_FAILURE, "Cannot get HTTP status code\n");
--        tevent_req_error(req, EFAULT);
--        return;
-+        DEBUG(SSSDBG_OP_FAILURE, "Cannot get response code\n");
-+        ret = curl_code2errno(crv);
-+        goto done;
-     }
- 
--    tevent_req_done(req);
-+    ret = EOK;
-+
-+done:
-+    tcurl_request_done(req, ret, response_code);
- }
- 
- static void process_curl_activity(struct tcurl_ctx *tctx)
-@@ -551,346 +542,42 @@ fail:
-     return NULL;
- }
- 
--static errno_t tcurl_add_headers(struct tcurl_http_state *state,
--                                 const char *headers[]);
--
--static errno_t tcurl_set_options(struct tcurl_http_state *state,
--                                 struct tevent_req *req,
--                                 enum tcurl_http_request req_type);
--
--static int tcurl_http_cleanup_handle(TALLOC_CTX *ptr);
--
--static size_t tcurl_http_write_data(char *ptr,
--                                    size_t size,
--                                    size_t nmemb,
--                                    void *userdata);
--
--static size_t tcurl_http_read_data(void *ptr,
--                                   size_t size,
--                                   size_t nmemb,
--                                   void *userdata);
--
--struct tevent_req *tcurl_http_send(TALLOC_CTX *mem_ctx,
--                                   struct tevent_context *ev,
--                                   struct tcurl_ctx *tctx,
--                                   enum tcurl_http_request req_type,
--                                   const char *socket_path,
--                                   const char *url,
--                                   const char *headers[],
--                                   struct sss_iobuf *req_data,
--                                   int timeout)
--{
--    errno_t ret;
--    struct tevent_req *req;
--    struct tcurl_http_state *state;
--
--    req = tevent_req_create(mem_ctx, &state, struct tcurl_http_state);
--    if (req == NULL) {
--        return NULL;
--    }
--
--    state->tctx = tctx;
--    state->socket_path = socket_path;
--    state->url = url;
--    state->inbuf = req_data;
--    state->timeout = timeout;
--
--    state->outbuf = sss_iobuf_init_empty(state, IOBUF_CHUNK, IOBUF_MAX);
--    if (state->outbuf == NULL) {
--        ret = ENOMEM;
--        goto fail;
--    }
--
--    DEBUG(SSSDBG_TRACE_FUNC,
--          "HTTP request %s for URL %s\n", http_req2str(req_type), url);
--    talloc_set_destructor((TALLOC_CTX *) state, tcurl_http_cleanup_handle);
--
--    /* All transfer share the same multi handle, but each trasfer has its own
--     * easy handle we can use to set per-transfer options
--     */
--    state->http_handle = curl_easy_init();
--    if (state->http_handle == NULL) {
--        DEBUG(SSSDBG_CRIT_FAILURE, "curl_easy_init failed\n");
--        ret = EIO;
--        goto fail;
--    }
--
--    ret = tcurl_add_headers(state, headers);
--    if (ret != EOK) {
--        DEBUG(SSSDBG_CRIT_FAILURE,
--              "Failed to set CURL headers [%d]: %s\n", ret, sss_strerror(ret));
--        goto fail;
--    }
--
--    ret = tcurl_set_options(state, req, req_type);
--    if (ret != EOK) {
--        DEBUG(SSSDBG_CRIT_FAILURE,
--              "Failed to set CURL options [%d]: %s\n", ret, sss_strerror(ret));
--        goto fail;
--    }
--
--    /* Pass control to the curl handling which will mark the request as
--     * done
--     */
--    curl_multi_add_handle(tctx->multi_handle, state->http_handle);
--
--    return req;
--
--fail:
--    tevent_req_error(req, ret);
--    tevent_req_post(req, ev);
--    return req;
--}
--
--static int tcurl_http_cleanup_handle(TALLOC_CTX *ptr)
--{
--    struct tcurl_http_state *state = talloc_get_type(ptr, struct tcurl_http_state);
--
--    if (state == NULL) {
--        return 0;
--    }
--
--    /* it is safe to pass NULL here */
--    curl_multi_remove_handle(state->tctx->multi_handle, state->http_handle);
--    curl_slist_free_all(state->curl_headers);
--    curl_easy_cleanup(state->http_handle);
--    return 0;
--}
--
--static errno_t tcurl_add_headers(struct tcurl_http_state *state,
--                                 const char *headers[])
--{
--    if (headers == NULL) {
--        return EOK;
--    }
--
--    /* The headers will be freed later in tcurl_http_cleanup_handle */
--    for (int i = 0; headers[i] != NULL; i++) {
--        state->curl_headers = curl_slist_append(state->curl_headers, headers[i]);
--        if (state->curl_headers == NULL) {
--            DEBUG(SSSDBG_CRIT_FAILURE, "Cannot add header %s\n", headers[i]);
--            return ENOMEM;
--        }
--    }
--
--    /* Add a dummy header to suppress libcurl adding Expect 100-continue which
--     * was causing libcurl to always wait for the internal timeout when sending
--     * a PUT/PATCH request
--     */
--    state->curl_headers = curl_slist_append(state->curl_headers, "Expect:");
--    if (state->curl_headers == NULL) {
--        DEBUG(SSSDBG_CRIT_FAILURE, "Cannot add the dummy expect header\n");
--        return ENOMEM;
--    }
--
--    return EOK;
--}
--
--static errno_t tcurl_set_common_options(struct tcurl_http_state *state,
--                                        struct tevent_req *req)
--{
--    CURLcode crv;
--
--    crv = curl_easy_setopt(state->http_handle,
--                           CURLOPT_HTTPHEADER,
--                           state->curl_headers);
--    if (crv != CURLE_OK) {
--        DEBUG(SSSDBG_OP_FAILURE,
--              "Failed to set HTTP headers [%d]: %s\n",
--              crv, curl_easy_strerror(crv));
--        return EIO;
--    }
--
--    crv = curl_easy_setopt(state->http_handle,
--                           CURLOPT_UNIX_SOCKET_PATH,
--                           state->socket_path);
--    if (crv != CURLE_OK) {
--        DEBUG(SSSDBG_OP_FAILURE,
--              "Failed to set UNIX socket path %s [%d]: %s\n",
--              state->socket_path, crv, curl_easy_strerror(crv));
--        return EIO;
--    }
--
--    crv = curl_easy_setopt(state->http_handle, CURLOPT_URL, state->url);
--    if (crv != CURLE_OK) {
--        DEBUG(SSSDBG_OP_FAILURE,
--              "Failed to set URL %s [%d]: %s\n",
--              state->url, crv, curl_easy_strerror(crv));
--        return EIO;
--    }
--
--    crv = curl_easy_setopt(state->http_handle, CURLOPT_PRIVATE, req);
--    if (crv != CURLE_OK) {
--        DEBUG(SSSDBG_OP_FAILURE,
--              "Failed to set private data [%d]: %s\n",
--              crv, curl_easy_strerror(crv));
--        return EIO;
--    }
--
--    if (state->timeout > 0) {
--        crv = curl_easy_setopt(state->http_handle,
--                               CURLOPT_TIMEOUT,
--                               state->timeout);
--        if (crv != CURLE_OK) {
--            DEBUG(SSSDBG_OP_FAILURE,
--                  "Failed to set timeout [%d]: %s\n",
--                  crv, curl_easy_strerror(crv));
--            return EIO;
--        }
--    }
--
--    return EOK;
--}
--
--static errno_t tcurl_set_write_options(struct tcurl_http_state *state)
--{
--    CURLcode crv;
--
--    crv = curl_easy_setopt(state->http_handle,
--                           CURLOPT_WRITEFUNCTION,
--                           tcurl_http_write_data);
--    if (crv != CURLE_OK) {
--        DEBUG(SSSDBG_OP_FAILURE,
--              "Failed to set write function [%d]: %s\n",
--              crv, curl_easy_strerror(crv));
--        return EIO;
--    }
--
--    crv = curl_easy_setopt(state->http_handle,
--                           CURLOPT_WRITEDATA,
--                           state->outbuf);
--    if (crv != CURLE_OK) {
--        DEBUG(SSSDBG_OP_FAILURE,
--              "Failed to set write data [%d]: %s\n",
--              crv, curl_easy_strerror(crv));
--        return EIO;
--    }
--
--    return EOK;
--}
--
--static errno_t tcurl_set_read_options(struct tcurl_http_state *state)
--{
--    CURLcode crv;
--
--    crv = curl_easy_setopt(state->http_handle,
--                           CURLOPT_READFUNCTION,
--                           tcurl_http_read_data);
--    if (crv != CURLE_OK) {
--        DEBUG(SSSDBG_OP_FAILURE,
--              "Failed to set read function [%d]: %s\n",
--              crv, curl_easy_strerror(crv));
--        return EIO;
--    }
--
--    crv = curl_easy_setopt(state->http_handle,
--                           CURLOPT_READDATA,
--                           state->inbuf);
--    if (crv != CURLE_OK) {
--        DEBUG(SSSDBG_OP_FAILURE,
--              "Failed to set read data [%d]: %s\n",
--              crv, curl_easy_strerror(crv));
--        return EIO;
--    }
--
--    return EOK;
--}
--
--static errno_t tcurl_set_options(struct tcurl_http_state *state,
--                                 struct tevent_req *req,
--                                 enum tcurl_http_request req_type)
--{
--    CURLcode crv;
--    errno_t ret;
--
--    ret = tcurl_set_common_options(state, req);
--    if (ret != EOK) {
--        return ret;
--    }
--
--    ret = tcurl_set_write_options(state);
--    if (ret != EOK) {
--        DEBUG(SSSDBG_CRIT_FAILURE,
--              "Failed to set write callbacks [%d]: %s\n",
--              ret, sss_strerror(ret));
--        return ret;
--    }
--
--    switch (req_type) {
--    case TCURL_HTTP_POST:
--        crv = curl_easy_setopt(state->http_handle,
--                               CURLOPT_CUSTOMREQUEST,
--                               "POST");
--        break;
--    case TCURL_HTTP_PUT:
--        /* CURLOPT_UPLOAD enables HTTP_PUT */
--        crv = curl_easy_setopt(state->http_handle,
--                               CURLOPT_UPLOAD,
--                               1L);
--        if (crv != CURLE_OK) {
--            DEBUG(SSSDBG_OP_FAILURE,
--                  "Failed to set the uplodad option [%d]: %s\n",
--                  crv, curl_easy_strerror(crv));
--            return EIO;
--        }
--
--        /* Causes libcurl to add a sane Content-Length header */
--        crv = curl_easy_setopt(state->http_handle,
--                               CURLOPT_INFILESIZE_LARGE,
--                               (curl_off_t) sss_iobuf_get_size(state->inbuf));
--        if (crv != CURLE_OK) {
--            DEBUG(SSSDBG_OP_FAILURE,
--                  "Failed to set CURLOPT_INFILESIZE_LARGE [%d]: %s\n",
--                  crv, curl_easy_strerror(crv));
--            return EIO;
--        }
--
--        ret = tcurl_set_read_options(state);
--        if (ret != EOK) {
--            DEBUG(SSSDBG_CRIT_FAILURE,
--                  "Failed to set write callbacks [%d]: %s\n",
--                  ret, sss_strerror(ret));
--            return ret;
--        }
--        break;
--    case TCURL_HTTP_GET:
--        /* GET just needs the write callbacks, nothing to do here.. */
--        break;
--    case TCURL_HTTP_DELETE:
--        crv = curl_easy_setopt(state->http_handle,
--                               CURLOPT_CUSTOMREQUEST,
--                               "DELETE");
--        if (crv != CURLE_OK) {
--            DEBUG(SSSDBG_OP_FAILURE,
--                  "Failed to set the uplodad option [%d]: %s\n",
--                  crv, curl_easy_strerror(crv));
--            return EIO;
--        }
--        break;
--    default:
--        return EFAULT;
--    }
--
--    return EOK;
--}
--
--static size_t tcurl_http_write_data(char *ptr,
--                                    size_t size,
--                                    size_t nmemb,
--                                    void *userdata)
-+#define tcurl_set_option(tcurl_req, option, value)                          \
-+({                                                                          \
-+    CURLcode __curl_code;                                                   \
-+    errno_t __ret;                                                          \
-+                                                                            \
-+    __curl_code = curl_easy_setopt((tcurl_req)->curl_easy_handle,           \
-+                                   (option), (value));                      \
-+    if (__curl_code == CURLE_OK) {                                          \
-+        __ret = EOK;                                                        \
-+    } else {                                                                \
-+        DEBUG(SSSDBG_OP_FAILURE, "Failed to set CURL option %s [%d]: %s\n", \
-+              #option, __curl_code, curl_easy_strerror(__curl_code));       \
-+        __ret = curl_code2errno(__curl_code);                               \
-+    }                                                                       \
-+    __ret;                                                                  \
-+})
-+
-+static size_t tcurl_write_data(char *ptr,
-+                               size_t size,
-+                               size_t nmemb,
-+                               void *userdata)
- {
-     errno_t ret;
-     size_t realsize = size * nmemb;
--    struct sss_iobuf *outbuf = talloc_get_type(userdata, struct sss_iobuf);
-+    struct sss_iobuf *outbuf;
-+
-+    outbuf = talloc_get_type(userdata, struct sss_iobuf);
- 
-     DEBUG(SSSDBG_TRACE_INTERNAL, "---> begin libcurl data\n");
-     DEBUG(SSSDBG_TRACE_INTERNAL, "%s\n", ptr);
-     DEBUG(SSSDBG_TRACE_INTERNAL, "<--- end libcurl data\n");
- 
--    ret = sss_iobuf_write_len(outbuf, (uint8_t *) ptr, realsize);
-+    ret = sss_iobuf_write_len(outbuf, (uint8_t *)ptr, realsize);
-     if (ret != EOK) {
--        DEBUG(SSSDBG_CRIT_FAILURE,
--              "Failed to write data to buffer [%d]: %s\n", ret, sss_strerror(ret));
-+        DEBUG(SSSDBG_CRIT_FAILURE, "Failed to write data to buffer [%d]: %s\n",
-+              ret, sss_strerror(ret));
-         /* zero signifies an EOF */
-         return 0;
-     }
-@@ -898,14 +585,16 @@ static size_t tcurl_http_write_data(char *ptr,
-     return realsize;
- }
- 
--static size_t tcurl_http_read_data(void *ptr,
--                                   size_t size,
--                                   size_t nmemb,
--                                   void *userdata)
-+static size_t tcurl_read_data(void *ptr,
-+                              size_t size,
-+                              size_t nmemb,
-+                              void *userdata)
- {
-     errno_t ret;
-     size_t readbytes;
--    struct sss_iobuf *inbuf = (struct sss_iobuf *) userdata;
-+    struct sss_iobuf *inbuf;
-+
-+    inbuf = talloc_get_type(userdata, struct sss_iobuf);
- 
-     if (inbuf == NULL) {
-         return CURL_READFUNC_ABORT;
-@@ -919,22 +608,487 @@ static size_t tcurl_http_read_data(void *ptr,
-     return readbytes;
- }
- 
--int tcurl_http_recv(TALLOC_CTX *mem_ctx,
--                    struct tevent_req *req,
--                    int *_http_code,
--                    struct sss_iobuf **_outbuf)
-+
-+struct tcurl_request {
-+    CURL *curl_easy_handle;
-+
-+    struct sss_iobuf *body;
-+    struct curl_slist *headers;
-+
-+    const char *url;
-+    const char *socket;
-+
-+    /* Associated tcurl context if this request is in progress. */
-+    struct tcurl_ctx *tcurl_ctx;
-+};
-+
-+struct tcurl_request_state {
-+    struct tcurl_request *tcurl_req;
-+    struct sss_iobuf *response;
-+    int response_code;
-+};
-+
-+struct tevent_req *
-+tcurl_request_send(TALLOC_CTX *mem_ctx,
-+                   struct tevent_context *ev,
-+                   struct tcurl_ctx *tcurl_ctx,
-+                   struct tcurl_request *tcurl_req,
-+                   long int timeout)
- {
--    struct tcurl_http_state *state = tevent_req_data(req, struct tcurl_http_state);
-+    struct tcurl_request_state *state;
-+    struct tevent_req *req;
-+    CURLMcode curl_code;
-+    errno_t ret;
-+
-+    req = tevent_req_create(mem_ctx, &state, struct tcurl_request_state);
-+    if (req == NULL) {
-+        DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create tevent request!\n");
-+        return NULL;
-+    }
-+
-+    DEBUG(SSSDBG_TRACE_FUNC, "Sending TCURL request for %s, at socket %s\n",
-+          tcurl_req->url == NULL ? "<none>" : tcurl_req->url,
-+          tcurl_req->socket == NULL ? "<none>" : tcurl_req->socket);
-+
-+    state->tcurl_req = talloc_steal(state, tcurl_req);
-+
-+    state->response = sss_iobuf_init_empty(state, TCURL_IOBUF_CHUNK, TCURL_IOBUF_MAX);
-+    if (state->response == NULL) {
-+        ret = ENOMEM;
-+        goto done;
-+    }
-+
-+    ret = tcurl_set_option(tcurl_req, CURLOPT_PRIVATE, req);
-+    if (ret != EOK) {
-+        goto done;
-+    }
-+
-+    ret = tcurl_set_option(tcurl_req, CURLOPT_TIMEOUT, timeout);
-+    if (ret != EOK) {
-+        goto done;
-+    }
-+
-+    ret = tcurl_set_option(tcurl_req, CURLOPT_WRITEFUNCTION, tcurl_write_data);
-+    if (ret != EOK) {
-+        goto done;
-+    }
-+
-+    ret = tcurl_set_option(tcurl_req, CURLOPT_WRITEDATA, state->response);
-+    if (ret != EOK) {
-+        goto done;
-+    }
-+
-+    if (tcurl_req->body != NULL) {
-+        ret = tcurl_set_option(tcurl_req, CURLOPT_READFUNCTION, tcurl_read_data);
-+        if (ret != EOK) {
-+            goto done;
-+        }
-+
-+        ret = tcurl_set_option(tcurl_req, CURLOPT_READDATA, tcurl_req->body);
-+        if (ret != EOK) {
-+            goto done;
-+        }
-+    }
-+
-+    curl_code = curl_multi_add_handle(tcurl_ctx->multi_handle,
-+                                      tcurl_req->curl_easy_handle);
-+    if (curl_code != CURLM_OK) {
-+        ret = curlm_code2errno(curl_code);
-+        goto done;
-+    }
-+
-+    tcurl_req->tcurl_ctx = tcurl_ctx;
-+
-+    ret = EAGAIN;
-+
-+done:
-+    if (ret == EOK) {
-+        tevent_req_done(req);
-+        tevent_req_post(req, ev);
-+    } else if (ret != EAGAIN) {
-+        tevent_req_error(req, ret);
-+        tevent_req_post(req, ev);
-+    }
-+
-+    return req;
-+}
-+
-+static void tcurl_request_done(struct tevent_req *req,
-+                               errno_t process_error,
-+                               int response_code)
-+{
-+    struct tcurl_request_state *state;
-+
-+    DEBUG(SSSDBG_TRACE_FUNC, "TCURL request finished [%d]: %s\n",
-+          process_error, sss_strerror(process_error));
-+
-+    if (req == NULL) {
-+        /* To handle case where we fail to obtain request from private data. */
-+        DEBUG(SSSDBG_MINOR_FAILURE, "No tevent request provided!\n");
-+        return;
-+    }
-+
-+    state = tevent_req_data(req, struct tcurl_request_state);
-+
-+    curl_multi_remove_handle(state->tcurl_req->tcurl_ctx->multi_handle,
-+                             state->tcurl_req->curl_easy_handle);
-+
-+    /* This request is no longer associated with tcurl context. */
-+    state->tcurl_req->tcurl_ctx = NULL;
-+
-+    if (process_error != EOK) {
-+        tevent_req_error(req, process_error);
-+        return;
-+    }
-+
-+    state->response_code = response_code;
-+
-+    tevent_req_done(req);
-+    return;
-+}
-+
-+errno_t tcurl_request_recv(TALLOC_CTX *mem_ctx,
-+                           struct tevent_req *req,
-+                           struct sss_iobuf **_response,
-+                           int *_response_code)
-+{
-+    struct tcurl_request_state *state;
-+    state = tevent_req_data(req, struct tcurl_request_state);
- 
-     TEVENT_REQ_RETURN_ON_ERROR(req);
- 
--    if (_http_code != NULL) {
--        *_http_code = state->http_code;
-+    if (_response != NULL) {
-+        *_response = talloc_steal(mem_ctx, state->response);
-     }
- 
--    if (_outbuf != NULL) {
--        *_outbuf = talloc_steal(mem_ctx, state->outbuf);
-+    if (_response_code != NULL) {
-+        *_response_code = state->response_code;
-+    }
-+
-+    return EOK;
-+}
-+
-+static struct curl_slist *
-+tcurl_add_header(struct curl_slist *slist, const char *header)
-+{
-+    struct curl_slist *new;
-+
-+    new = curl_slist_append(slist, header);
-+    if (new == NULL) {
-+        DEBUG(SSSDBG_CRIT_FAILURE, "Cannot add header %s\n", header);
-+        if (slist != NULL) {
-+            curl_slist_free_all(slist);
-+        }
-+
-+        return NULL;
-+    }
-+
-+    return new;
-+}
-+
-+static errno_t
-+tcurl_construct_headers(const char **headers,
-+                        struct curl_slist **_slist)
-+{
-+    struct curl_slist *slist = NULL;
-+    int i;
-+
-+    if (headers == NULL || headers[0] == NULL) {
-+        *_slist = NULL;
-+        return EOK;
-+    }
-+
-+    for (i = 0; headers[i] != NULL; i++) {
-+        slist = tcurl_add_header(slist, headers[i]);
-+        if (slist == NULL) {
-+            return ENOMEM;
-+        }
-+    }
-+
-+    /* Add a dummy header to suppress libcurl adding Expect 100-continue which
-+     * was causing libcurl to always wait for the internal timeout when sending
-+     * a PUT/POST request because secrets responder does not implement this.
-+     */
-+    slist = tcurl_add_header(slist, "Expect: ");
-+    if (slist == NULL) {
-+        return ENOMEM;
-+    }
-+
-+    *_slist = slist;
-+
-+    return EOK;
-+}
-+
-+static int
-+tcurl_request_destructor(struct tcurl_request *tcurl_req)
-+{
-+    if (tcurl_req->tcurl_ctx != NULL) {
-+        DEBUG(SSSDBG_MINOR_FAILURE, "Terminating TCURL request...\n");
-+        curl_multi_remove_handle(tcurl_req->tcurl_ctx->multi_handle,
-+                                 tcurl_req->curl_easy_handle);
-+    }
-+
-+    if (tcurl_req->headers != NULL) {
-+        curl_slist_free_all(tcurl_req->headers);
-+    }
-+
-+    if (tcurl_req->curl_easy_handle != NULL) {
-+        curl_easy_cleanup(tcurl_req->curl_easy_handle);
-     }
- 
-     return 0;
- }
-+
-+static struct tcurl_request *
-+tcurl_request_create(TALLOC_CTX *mem_ctx,
-+                     const char *socket_path,
-+                     const char *url,
-+                     const char **headers,
-+                     struct sss_iobuf *body)
-+{
-+    struct tcurl_request *tcurl_req;
-+    errno_t ret;
-+
-+    tcurl_req = talloc_zero(mem_ctx, struct tcurl_request);
-+    if (tcurl_req == NULL) {
-+        DEBUG(SSSDBG_CRIT_FAILURE, "Out of memory!\n");
-+        return NULL;
-+    }
-+
-+    if (url == NULL) {
-+        DEBUG(SSSDBG_CRIT_FAILURE, "URL cannot be NULL!\n");
-+        ret = EINVAL;
-+        goto done;
-+    }
-+
-+    /* Setup a curl easy handle. This handle contains state for the request
-+     * and is later associated with curl multi handle which performs
-+     * asynchronous processing. */
-+    tcurl_req->curl_easy_handle = curl_easy_init();
-+    if (tcurl_req->curl_easy_handle == NULL) {
-+        DEBUG(SSSDBG_CRIT_FAILURE, "Unable to initialize curl easy handle!\n");
-+        ret = ENOMEM;
-+        goto done;
-+    }
-+
-+    tcurl_req->url = talloc_strdup(tcurl_req, url);
-+    if (tcurl_req->url == NULL) {
-+        DEBUG(SSSDBG_CRIT_FAILURE, "Out of memory!\n");
-+        ret = ENOMEM;
-+        goto done;
-+    }
-+
-+    if (socket_path != NULL) {
-+        tcurl_req->socket = talloc_strdup(tcurl_req, socket_path);
-+        if (tcurl_req->socket == NULL) {
-+            DEBUG(SSSDBG_CRIT_FAILURE, "Out of memory!\n");
-+            ret = ENOMEM;
-+            goto done;
-+        }
-+    }
-+
-+    ret = tcurl_construct_headers(headers, &tcurl_req->headers);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_CRIT_FAILURE, "Unable to construct headers [%d]: %s\n",
-+              ret, sss_strerror(ret));
-+        ret = ENOMEM;
-+        goto done;
-+    }
-+
-+    tcurl_req->body = body;
-+
-+    talloc_set_destructor(tcurl_req, tcurl_request_destructor);
-+
-+    ret = tcurl_set_option(tcurl_req, CURLOPT_URL, url);
-+    if (ret != EOK) {
-+        goto done;
-+    }
-+
-+    if (socket_path != NULL) {
-+        ret = tcurl_set_option(tcurl_req, CURLOPT_UNIX_SOCKET_PATH, socket_path);
-+        if (ret != EOK) {
-+            goto done;
-+        }
-+    }
-+
-+    if (body != NULL) {
-+        /* Curl will tell the underlying protocol about incoming data length.
-+         * In case of HTTP it will add a sane Content-Length header. */
-+        ret = tcurl_set_option(tcurl_req, CURLOPT_INFILESIZE_LARGE,
-+                               (curl_off_t)sss_iobuf_get_size(body));
-+        if (ret != EOK) {
-+            goto done;
-+        }
-+    }
-+
-+    ret = EOK;
-+
-+done:
-+    if (ret != EOK) {
-+        talloc_free(tcurl_req);
-+        return NULL;
-+    }
-+
-+    return tcurl_req;
-+}
-+
-+struct tcurl_request *tcurl_http(TALLOC_CTX *mem_ctx,
-+                                 enum tcurl_http_method method,
-+                                 const char *socket_path,
-+                                 const char *url,
-+                                 const char **headers,
-+                                 struct sss_iobuf *body)
-+{
-+    struct tcurl_request *tcurl_req;
-+    errno_t ret;
-+
-+    tcurl_req = tcurl_request_create(mem_ctx, socket_path, url, headers, body);
-+    if (tcurl_req == NULL) {
-+        return NULL;
-+    }
-+
-+    /* Set HTTP specific options. */
-+
-+    ret = tcurl_set_option(tcurl_req, CURLOPT_HTTPHEADER, tcurl_req->headers);
-+    if (ret != EOK) {
-+        goto done;
-+    }
-+
-+    switch (method) {
-+    case TCURL_HTTP_GET:
-+        /* Nothing to do here. GET is default. */
-+        break;
-+    case TCURL_HTTP_PUT:
-+        ret = tcurl_set_option(tcurl_req, CURLOPT_UPLOAD, 1L);
-+        if (ret != EOK) {
-+            goto done;
-+        }
-+        break;
-+    case TCURL_HTTP_POST:
-+        ret = tcurl_set_option(tcurl_req, CURLOPT_CUSTOMREQUEST, "POST");
-+        if (ret != EOK) {
-+            goto done;
-+        }
-+        break;
-+    case TCURL_HTTP_DELETE:
-+        ret = tcurl_set_option(tcurl_req, CURLOPT_CUSTOMREQUEST, "DELETE");
-+        if (ret != EOK) {
-+            goto done;
-+        }
-+        break;
-+    }
-+
-+    ret = EOK;
-+
-+done:
-+    if (ret != EOK) {
-+        talloc_free(tcurl_req);
-+        return NULL;
-+    }
-+
-+    return tcurl_req;
-+}
-+
-+struct tevent_req *tcurl_http_send(TALLOC_CTX *mem_ctx,
-+                                   struct tevent_context *ev,
-+                                   struct tcurl_ctx *tcurl_ctx,
-+                                   enum tcurl_http_method method,
-+                                   const char *socket_path,
-+                                   const char *url,
-+                                   const char **headers,
-+                                   struct sss_iobuf *body,
-+                                   int timeout)
-+{
-+    struct tcurl_request *tcurl_req;
-+    struct tevent_req *req;
-+
-+    tcurl_req = tcurl_http(mem_ctx, method, socket_path, url, headers, body);
-+    if (tcurl_req == NULL) {
-+        return NULL;
-+    }
-+
-+    req = tcurl_request_send(mem_ctx, ev, tcurl_ctx, tcurl_req, timeout);
-+    if (req == NULL) {
-+        talloc_free(tcurl_req);
-+    }
-+
-+    return req;
-+}
-+
-+errno_t tcurl_http_recv(TALLOC_CTX *mem_ctx,
-+                        struct tevent_req *req,
-+                        int *_http_code,
-+                        struct sss_iobuf **_response)
-+{
-+    return tcurl_request_recv(mem_ctx, req, _response, _http_code);
-+}
-+
-+errno_t tcurl_req_enable_rawoutput(struct tcurl_request *tcurl_req)
-+{
-+    return tcurl_set_option(tcurl_req, CURLOPT_HEADER, 1L);
-+}
-+
-+errno_t tcurl_req_verify_peer(struct tcurl_request *tcurl_req,
-+                              const char *capath,
-+                              const char *cacert,
-+                              bool verify_peer,
-+                              bool verify_host)
-+{
-+    errno_t ret;
-+
-+    long peer = verify_peer ? 1L : 0L;
-+    long host = verify_host ? 2L : 0L;
-+
-+    ret = tcurl_set_option(tcurl_req, CURLOPT_SSL_VERIFYPEER, peer);
-+    if (ret != EOK) {
-+        return ret;
-+    }
-+
-+    ret = tcurl_set_option(tcurl_req, CURLOPT_SSL_VERIFYHOST, host);
-+    if (ret != EOK) {
-+        return ret;
-+    }
-+
-+    if (capath != NULL) {
-+        ret = tcurl_set_option(tcurl_req, CURLOPT_CAPATH, capath);
-+        if (ret != EOK) {
-+            return ret;
-+        }
-+    }
-+
-+    if (cacert != NULL) {
-+        ret = tcurl_set_option(tcurl_req, CURLOPT_CAINFO, cacert);
-+        if (ret != EOK) {
-+            return ret;
-+        }
-+    }
-+
-+    return EOK;
-+}
-+
-+errno_t tcurl_req_set_client_cert(struct tcurl_request *tcurl_req,
-+                                  const char *cert,
-+                                  const char *key)
-+{
-+    errno_t ret;
-+
-+    if (cert == NULL) {
-+        DEBUG(SSSDBG_CRIT_FAILURE, "You must specify client certificate!\n");
-+        return EINVAL;
-+    }
-+
-+    ret = tcurl_set_option(tcurl_req, CURLOPT_SSLCERT, cert);
-+    if (ret != EOK) {
-+        return ret;
-+    }
-+
-+    if (key != NULL) {
-+        /* If client's private key is in separate file. */
-+        ret = tcurl_set_option(tcurl_req, CURLOPT_SSLKEY, key);
-+        if (ret != EOK) {
-+            return ret;
-+        }
-+    }
-+
-+    return EOK;
-+}
-diff --git a/src/util/tev_curl.h b/src/util/tev_curl.h
-index 444eb286e09d189b4588e2b2152b5202df3914d8..933abcb9b531412737e8fcf391644d828b125cf8 100644
---- a/src/util/tev_curl.h
-+++ b/src/util/tev_curl.h
-@@ -27,14 +27,16 @@
- 
- #include "util/sss_iobuf.h"
- 
-+struct tcurl_request;
-+
- /**
-- * @brief Supported HTTP requests
-+ * @brief Supported HTTP methods
-  */
--enum tcurl_http_request {
-+enum tcurl_http_method {
-     TCURL_HTTP_GET,
-     TCURL_HTTP_PUT,
--    TCURL_HTTP_DELETE,
-     TCURL_HTTP_POST,
-+    TCURL_HTTP_DELETE,
- };
- 
- /**
-@@ -46,16 +48,95 @@ struct tcurl_ctx *tcurl_init(TALLOC_CTX *mem_ctx,
-                              struct tevent_context *ev);
- 
- /**
-+ * @brief Run a single asynchronous TCURL request.
-+ *
-+ * If the libcurl processing succeeds but we obtain a protocol error we still
-+ * mark the tevent request as successful. The protocol error is return from
-+ * @tcurl_request_recv as an output parameter.
-+ *
-+ * @param[in]  mem_ctx      The talloc context that owns the request
-+ * @param[in]  ev           Event loop context
-+ * @param[in]  tctx         Use tcurl_init to get this context
-+ * @param[in]  tcurl_req    TCURL request
-+ * @param[in]  timeout      The request timeout in seconds. Use 0 if you want
-+ *                          to use the default libcurl timeout.
-+ *
-+ * @returns A tevent request or NULL on allocation error. On other errors, we
-+ * try to set the errno as event error code and run it to completion so that
-+ * the programmer can use tcurl_request_recv to read the error code.
-+ *
-+ * @see tcurl_init
-+ * @see tcurl_http
-+ * @see tcurl_request_recv
-+ */
-+struct tevent_req *
-+tcurl_request_send(TALLOC_CTX *mem_ctx,
-+                   struct tevent_context *ev,
-+                   struct tcurl_ctx *tcurl_ctx,
-+                   struct tcurl_request *tcurl_req,
-+                   long int timeout);
-+
-+/**
-+ * @brief Receive a result of a single asynchronous TCURL request.
-+ *
-+ * @param[in]  mem_ctx         The talloc context that owns the response
-+ * @param[in]  req             The request previously obtained with tcurl_request_send
-+ * @param[out] _response       Response to the request
-+ * @param[out] _response_code  Protocol response code (may indicate a protocl error)
-+ *
-+ * @returns The error code of the curl request (not the HTTP code!)
-+ */
-+errno_t tcurl_request_recv(TALLOC_CTX *mem_ctx,
-+                           struct tevent_req *req,
-+                           struct sss_iobuf **_response,
-+                           int *_response_code);
-+
-+/**
-+ * @brief Create a HTTP request.
-+ *
-+ * Use this if you need better control over the request options.
-+ *
-+ * Headers are a NULL-terminated array of strings such as:
-+ *   static const char *headers[] = {
-+ *       "Content-type: application/octet-stream",
-+ *       NULL,
-+ *   };
-+ *
-+ * @param[in]  mem_ctx      The talloc context that owns the tcurl_request
-+ * @param[in]  method       TCURL HTTP method
-+ * @param[in]  socket_path  The path to the UNIX socket to forward the
-+ *                          request to, may be NULL.
-+ * @param[in]  url          The request URL, cannot be NULL.
-+ * @param[in]  headers      A NULL-terminated array of strings to use
-+ *                          as additional HTTP headers. Pass NULL if you
-+ *                          don't need any additional headers.
-+ * @param[in]  body         The HTTP request input data. For some request
-+ *                          types like DELETE, this is OK to leave as NULL.
-+ *
-+ * @returns A tcurl_request that can be later started with tcurl_request_send
-+ * or NULL on error.
-+ *
-+ * @see tcurl_init
-+ * @see tcurl_request_send
-+ * @see tcurl_request_recv
-+ */
-+struct tcurl_request *tcurl_http(TALLOC_CTX *mem_ctx,
-+                                 enum tcurl_http_method method,
-+                                 const char *socket_path,
-+                                 const char *url,
-+                                 const char **headers,
-+                                 struct sss_iobuf *body);
-+
-+/**
-  * @brief Run a single asynchronous HTTP request.
-  *
-- * Currently only UNIX sockets at socket_path are supported.
-+ * Use this if you do not need control over additional request options.
-  *
-  * If the request runs into completion, but reports a failure with HTTP return
-  * code, the request will be marked as done. Only if the request cannot run at
-  * all (if e.g. the socket is unreachable), the request will fail completely.
-  *
-- * Headers are a NULL-terminated
-- * array of strings such as:
-+ * Headers are a NULL-terminated array of strings such as:
-  *   static const char *headers[] = {
-  *       "Content-type: application/octet-stream",
-  *       NULL,
-@@ -63,15 +144,15 @@ struct tcurl_ctx *tcurl_init(TALLOC_CTX *mem_ctx,
-  *
-  * @param[in]  mem_ctx      The talloc context that owns the iobuf
-  * @param[in]  ev           Event loop context
-- * @param[in]  tctx         Use tcurl_init to get this context
-- * @param[in]  req_type     The request type
-+ * @param[in]  tcurl_ctx    Use tcurl_init to get this context
-+ * @param[in]  method       HTTP method
-  * @param[in]  socket_path  The path to the UNIX socket to forward the
-- *                          request to
-- * @param[in]  url          The request URL
-+ *                          request to, may be NULL.
-+ * @param[in]  url          The request URL, cannot be NULL.
-  * @param[in]  headers      A NULL-terminated array of strings to use
-  *                          as additional HTTP headers. Pass NULL if you
-  *                          don't need any additional headers.
-- * @param[in]  req_data     The HTTP request input data. For some request
-+ * @param[in]  body         The HTTP request input data. For some request
-  *                          types like DELETE, this is OK to leave as NULL.
-  * @param[in]  timeout      The request timeout in seconds. Use 0 if you want
-  *                          to use the default libcurl timeout.
-@@ -85,12 +166,12 @@ struct tcurl_ctx *tcurl_init(TALLOC_CTX *mem_ctx,
-  */
- struct tevent_req *tcurl_http_send(TALLOC_CTX *mem_ctx,
-                                    struct tevent_context *ev,
--                                   struct tcurl_ctx *tctx,
--                                   enum tcurl_http_request req_type,
-+                                   struct tcurl_ctx *tcurl_ctx,
-+                                   enum tcurl_http_method method,
-                                    const char *socket_path,
-                                    const char *url,
--                                   const char *headers[],
--                                   struct sss_iobuf *req_data,
-+                                   const char **headers,
-+                                   struct sss_iobuf *body,
-                                    int timeout);
- 
- /**
-@@ -104,9 +185,62 @@ struct tevent_req *tcurl_http_send(TALLOC_CTX *mem_ctx,
-  *
-  * @returns The error code of the curl request (not the HTTP code!)
-  */
--int tcurl_http_recv(TALLOC_CTX *mem_ctx,
--                    struct tevent_req *req,
--                    int *_http_code,
--                    struct sss_iobuf **_outbuf);
-+errno_t tcurl_http_recv(TALLOC_CTX *mem_ctx,
-+                        struct tevent_req *req,
-+                        int *_http_code,
-+                        struct sss_iobuf **_response);
-+
-+/**
-+ * @brief We are usually interested only in the reply body without protocol
-+ * headers. Call this function on tcurl_request, if you want to include
-+ * complete protocol response in the output buffer.
-+ *
-+ * @param[in]  tcurl_request
-+ *
-+ * @returns errno code
-+ *
-+ * @see tcurl_http
-+ */
-+errno_t tcurl_req_enable_rawoutput(struct tcurl_request *tcurl_req);
-+
-+/**
-+ * @brief TLS is enabled automatically by providing an URL that points to
-+ * TLS-enabled protocol such as https. If you want to provide different
-+ * path to CA directory or disable peer/hostname check explicitly, use
-+ * this function on tcurl_request.
-+ *
-+ * @param[in]  tcurl_request
-+ * @param[in]  capath        Path to directory containing installed CA certificates.
-+ *                           If not set, libcurl default is used.
-+ * @param[ing  cacert        CA certificate. If NULL it is found in @capath.
-+ * @param[in]  verify_peer   If false, the peer certificate is not verified.
-+ * @param[in]  verify_host   If false, the host name provided in remote
-+ *                           certificate may differ from the actual host name.
-+ *
-+ * @returns errno code
-+ *
-+ * @see tcurl_http
-+ */
-+errno_t tcurl_req_verify_peer(struct tcurl_request *tcurl_req,
-+                              const char *capath,
-+                              const char *cacert,
-+                              bool verify_peer,
-+                              bool verify_host);
-+/**
-+ * @brief Some server require client verification during TLS setup. You can
-+ * provide path to client's certificate file. If this file does not contain
-+ * private key, you can specify a different file the holds the private key.
-+ *
-+ * @param[in]  tcurl_request
-+ * @param[in]  cert          Path to client's certificate.
-+ * @param[in]  key           Path to client's private key.
-+ *
-+ * @returns errno code
-+ *
-+ * @see tcurl_http
-+ */
-+errno_t tcurl_req_set_client_cert(struct tcurl_request *tcurl_req,
-+                                  const char *cert,
-+                                  const char *key);
- 
- #endif /* __TEV_CURL_H */
-diff --git a/src/util/util_errors.c b/src/util/util_errors.c
-index 60c2f439b3e39b1dbff353e429114cb5a3070052..466a3b4062f39b29d831a5d8a62dc8d576eb2e97 100644
---- a/src/util/util_errors.c
-+++ b/src/util/util_errors.c
-@@ -111,6 +111,10 @@ struct err_string error_to_str[] = {
-     { "Credential cache name not allowed" }, /* ERR_KCM_WRONG_CCNAME_FORMAT */
-     { "Cannot encode a JSON object to string" }, /* ERR_JSON_ENCODING */
-     { "Cannot decode a JSON object from string" }, /* ERR_JSON_DECODING */
-+    { "Invalid certificate provided" }, /* ERR_INVALID_CERT */
-+    { "Unable to initialize SSL" }, /* ERR_SSL_FAILURE */
-+    { "Unable to verify peer" }, /* ERR_UNABLE_TO_VERIFY_PEER */
-+    { "Unable to resolve host" }, /* ERR_UNABLE_TO_RESOLVE_HOST */
-     { "ERR_LAST" } /* ERR_LAST */
- };
- 
-diff --git a/src/util/util_errors.h b/src/util/util_errors.h
-index 4e9da814702e2cd46edc52fd5c2ae5f640602609..2f90c0a5d65325a431a8e4d9a480170808c9198e 100644
---- a/src/util/util_errors.h
-+++ b/src/util/util_errors.h
-@@ -133,6 +133,10 @@ enum sssd_errors {
-     ERR_KCM_WRONG_CCNAME_FORMAT,
-     ERR_JSON_ENCODING,
-     ERR_JSON_DECODING,
-+    ERR_INVALID_CERT,
-+    ERR_SSL_FAILURE,
-+    ERR_UNABLE_TO_VERIFY_PEER,
-+    ERR_UNABLE_TO_RESOLVE_HOST,
-     ERR_LAST            /* ALWAYS LAST */
- };
- 
--- 
-2.9.3
-
diff --git a/SOURCES/0075-RESP-Expose-DP-method-getAccountDomain-to-responders.patch b/SOURCES/0075-RESP-Expose-DP-method-getAccountDomain-to-responders.patch
new file mode 100644
index 0000000..208b5dd
--- /dev/null
+++ b/SOURCES/0075-RESP-Expose-DP-method-getAccountDomain-to-responders.patch
@@ -0,0 +1,239 @@
+From cac78825ba2fcb2efcd7ff2e58b562b370bbb28c Mon Sep 17 00:00:00 2001
+From: Jakub Hrozek <jhrozek@redhat.com>
+Date: Mon, 30 Oct 2017 20:51:40 +0100
+Subject: [PATCH 75/83] RESP: Expose DP method getAccountDomain() to responders
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Adds a tevent request that calls the getAccountDomain DP method.
+This request will be used by responders to locate an object's domain.
+
+At the moment, only looking up UIDs and GIDs is supported.
+
+Internally, until we switch to the rdp_ interface everywhere, this
+interface hooks into the sss_dp_issue_request(). When we switch to
+the rdp_ interface, we'll be able to provide a nicer method parameters
+as well.
+
+Reviewed-by: Pavel Březina <pbrezina@redhat.com>
+Reviewed-by: Sumit Bose <sbose@redhat.com>
+(cherry picked from commit 95fd82a4d7b50e64fed6906bc5345f271e8247d9)
+---
+ src/responder/common/responder.h             |  36 +++++++
+ src/responder/common/responder_get_domains.c | 155 +++++++++++++++++++++++++++
+ 2 files changed, 191 insertions(+)
+
+diff --git a/src/responder/common/responder.h b/src/responder/common/responder.h
+index 9a57df558994c418d440eabf4a29f69c4a47faa5..9400e4b60d9fc77c23710174e4c00a83f6395985 100644
+--- a/src/responder/common/responder.h
++++ b/src/responder/common/responder.h
+@@ -375,6 +375,42 @@ struct tevent_req *sss_dp_get_domains_send(TALLOC_CTX *mem_ctx,
+ 
+ errno_t sss_dp_get_domains_recv(struct tevent_req *req);
+ 
++/*
++ * Call a getAccountDomain request
++ *
++ * Only requests by ID are supported.
++ *
++ * @param   mem_ctx     Parent memory context
++ * @param   rctx        Responder context
++ * @param   domain      The SSSD domain we're querying. The response can
++ *                      be either NULL or come from any of domain's subdomains
++ *                      or domain itself
++ * @param   type        Either SSS_DP_USER or SSS_DP_GROUP, other types
++ *                      are not supported at the moment
++ * @param   opt_id      The ID number we're trying to locate
++ *
++ * @return  A tevent request or NULL if allocating the request fails.
++ */
++struct tevent_req *sss_dp_get_account_domain_send(TALLOC_CTX *mem_ctx,
++                                                  struct resp_ctx *rctx,
++                                                  struct sss_domain_info *domain,
++                                                  enum sss_dp_acct_type type,
++                                                  uint32_t opt_id);
++
++/* Receive a getAccountDomain request result
++ *
++ * @param   mem_ctx     The memory context that will own the contents of _domain
++ * @param   req         The request that had finished
++ * @para    _domain     Either NULL (the request did not match any domain) or
++ *                      a string that corresponds to either the input domain
++ *                      or any of its subdomains
++ *
++ * @return EOK on success, errno otherwise
++ */
++errno_t sss_dp_get_account_domain_recv(TALLOC_CTX *mem_ctx,
++                                       struct tevent_req *req,
++                                       char **_domain);
++
+ errno_t schedule_get_domains_task(TALLOC_CTX *mem_ctx,
+                                   struct tevent_context *ev,
+                                   struct resp_ctx *rctx,
+diff --git a/src/responder/common/responder_get_domains.c b/src/responder/common/responder_get_domains.c
+index 4955af064040e03372e9a47fb264499d9a23b828..d69bce2300580beb42d3af8e66ff467db890f284 100644
+--- a/src/responder/common/responder_get_domains.c
++++ b/src/responder/common/responder_get_domains.c
+@@ -642,3 +642,158 @@ errno_t sss_parse_inp_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
+ 
+     return state->error;
+ }
++
++/* ========== Get domain of an ccount ================= */
++struct sss_dp_get_account_domain_info {
++    struct sss_domain_info *dom;
++    enum sss_dp_acct_type type;
++    uint32_t opt_id;
++};
++
++static DBusMessage *sss_dp_get_account_domain_msg(void *pvt);
++
++struct tevent_req *sss_dp_get_account_domain_send(TALLOC_CTX *mem_ctx,
++                                                  struct resp_ctx *rctx,
++                                                  struct sss_domain_info *dom,
++                                                  enum sss_dp_acct_type type,
++                                                  uint32_t opt_id)
++{
++    struct tevent_req *req;
++    struct sss_dp_get_account_domain_info *info;
++    struct sss_dp_req_state *state;
++    char *key;
++    errno_t ret;
++
++    req = tevent_req_create(mem_ctx, &state, struct sss_dp_req_state);
++    if (!req) {
++        return NULL;
++    }
++
++    info = talloc_zero(state, struct sss_dp_get_account_domain_info);
++    if (info == NULL) {
++        ret = ENOMEM;
++        goto immediately;
++    }
++    info->type = type;
++    info->opt_id = opt_id;
++    info->dom = dom;
++
++    key = talloc_asprintf(state, "%d: %"SPRIuid"@%s", type, opt_id, dom->name);
++    if (key == NULL) {
++        ret = ENOMEM;
++        goto immediately;
++    }
++
++    ret = sss_dp_issue_request(state, rctx, key, dom,
++                               sss_dp_get_account_domain_msg,
++                               info, req);
++    talloc_free(key);
++    if (ret != EOK) {
++        DEBUG(SSSDBG_OP_FAILURE,
++              "Could not issue DP request [%d]: %s\n",
++              ret, sss_strerror(ret));
++        goto immediately;
++    }
++
++    return req;
++
++immediately:
++    if (ret == EOK) {
++        tevent_req_done(req);
++    } else {
++        tevent_req_error(req, ret);
++    }
++    tevent_req_post(req, rctx->ev);
++    return req;
++}
++
++static DBusMessage *
++sss_dp_get_account_domain_msg(void *pvt)
++{
++    DBusMessage *msg;
++    dbus_bool_t dbret;
++    struct sss_dp_get_account_domain_info *info;
++    uint32_t entry_type;
++    char *filter;
++
++    info = talloc_get_type(pvt, struct sss_dp_get_account_domain_info);
++
++    switch (info->type) {
++    case SSS_DP_USER:
++        entry_type = BE_REQ_USER;
++        break;
++    case SSS_DP_GROUP:
++        entry_type = BE_REQ_GROUP;
++        break;
++    case SSS_DP_USER_AND_GROUP:
++        entry_type = BE_REQ_USER_AND_GROUP;
++        break;
++    default:
++        DEBUG(SSSDBG_OP_FAILURE,
++              "Unsupported lookup type %X for this request\n", info->type);
++        return NULL;
++    }
++
++    filter = talloc_asprintf(info, "idnumber=%u", info->opt_id);
++    if (!filter) {
++        DEBUG(SSSDBG_CRIT_FAILURE, "Out of memory?!\n");
++        return NULL;
++    }
++
++    msg = dbus_message_new_method_call(NULL,
++                                       DP_PATH,
++                                       IFACE_DP,
++                                       IFACE_DP_GETACCOUNTDOMAIN);
++    if (msg == NULL) {
++        talloc_free(filter);
++        DEBUG(SSSDBG_CRIT_FAILURE, "Out of memory?!\n");
++        return NULL;
++    }
++
++    /* create the message */
++    DEBUG(SSSDBG_TRACE_FUNC,
++          "Creating request for [%s][%#x][%s][%s:-]\n",
++          info->dom->name, entry_type, be_req2str(entry_type), filter);
++
++    dbret = dbus_message_append_args(msg,
++                                     DBUS_TYPE_UINT32, &entry_type,
++                                     DBUS_TYPE_STRING, &filter,
++                                     DBUS_TYPE_INVALID);
++    talloc_free(filter);
++    if (!dbret) {
++        DEBUG(SSSDBG_CRIT_FAILURE, "Failed to build message\n");
++        dbus_message_unref(msg);
++        return NULL;
++    }
++
++    return msg;
++}
++
++errno_t sss_dp_get_account_domain_recv(TALLOC_CTX *mem_ctx,
++                                       struct tevent_req *req,
++                                       char **_domain)
++{
++    errno_t ret;
++    dbus_uint16_t err_maj;
++    dbus_uint32_t err_min;
++    char *msg;
++
++    ret = sss_dp_req_recv(mem_ctx, req, &err_maj, &err_min, &msg);
++    if (ret != EOK) {
++        DEBUG(SSSDBG_OP_FAILURE,
++              "Could not get account info [%d]: %s\n",
++              ret, sss_strerror(ret));
++        return ret;
++    }
++
++    if (err_maj != DP_ERR_OK) {
++        DEBUG(SSSDBG_OP_FAILURE,
++              "Data Provider Error: %u, %u\n",
++              (unsigned int)err_maj, (unsigned int)err_min);
++        talloc_free(msg);
++        return err_min ? err_min : EIO;
++    }
++
++    *_domain = msg;
++    return EOK;
++}
+-- 
+2.14.3
+
diff --git a/SOURCES/0075-tcurl-test-refactor-so-new-options-can-be-added-more.patch b/SOURCES/0075-tcurl-test-refactor-so-new-options-can-be-added-more.patch
deleted file mode 100644
index bcea2c6..0000000
--- a/SOURCES/0075-tcurl-test-refactor-so-new-options-can-be-added-more.patch
+++ /dev/null
@@ -1,438 +0,0 @@
-From a886247bcdb1c551486c34a8d4eccd046a11382f Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Pavel=20B=C5=99ezina?= <pbrezina@redhat.com>
-Date: Fri, 24 Feb 2017 12:23:01 +0100
-Subject: [PATCH 75/90] tcurl test: refactor so new options can be added more
- easily
-
-Just to make the tool a little bit nicer and more flexible.
-
-Reviewed-by: Simo Sorce <simo@redhat.com>
-Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
-(cherry picked from commit b800a6d09244359959404aca81c6796a58cafbcb)
----
- src/tests/tcurl_test_tool.c | 334 +++++++++++++++++++++++++++-----------------
- 1 file changed, 209 insertions(+), 125 deletions(-)
-
-diff --git a/src/tests/tcurl_test_tool.c b/src/tests/tcurl_test_tool.c
-index 9a6266f89131ffd3a561e857af85df9854c44949..e5fc9705db415650d849b89c3d18e41574b7e28b 100644
---- a/src/tests/tcurl_test_tool.c
-+++ b/src/tests/tcurl_test_tool.c
-@@ -28,26 +28,39 @@
- 
- struct tool_ctx {
-     bool verbose;
--
--    errno_t error;
-     bool done;
- 
-     size_t nreqs;
- };
- 
-+struct tool_options {
-+    int debug;
-+    int verbose;
-+
-+    enum tcurl_http_method method;
-+    const char *socket_path;
-+};
-+
- static void request_done(struct tevent_req *req)
- {
--    int http_code;
-+    struct tool_ctx *tool_ctx;
-     struct sss_iobuf *outbuf;
--    struct tool_ctx *tool_ctx = tevent_req_callback_data(req,
--                                                         struct tool_ctx);
-+    int http_code;
-+    errno_t ret;
- 
--    tool_ctx->error = tcurl_request_recv(tool_ctx, req, &outbuf, &http_code);
-+    tool_ctx = tevent_req_callback_data(req, struct tool_ctx);
-+
-+    ret = tcurl_request_recv(tool_ctx, req, &outbuf, &http_code);
-     talloc_zfree(req);
- 
--    if (tool_ctx->error != EOK) {
--        DEBUG(SSSDBG_FATAL_FAILURE, "HTTP request failed: %d\n", tool_ctx->error);
-+    tool_ctx->nreqs--;
-+    if (tool_ctx->nreqs == 0) {
-         tool_ctx->done = true;
-+    }
-+
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_FATAL_FAILURE, "HTTP request failed [%d]: %s\n",
-+              ret, sss_strerror(ret));
-         return;
-     } else if (tool_ctx->verbose) {
-         printf("Request HTTP code: %d\n", http_code);
-@@ -55,167 +68,171 @@ static void request_done(struct tevent_req *req)
-                (const char *) sss_iobuf_get_data(outbuf));
-         talloc_zfree(outbuf);
-     }
--
--    tool_ctx->nreqs--;
--    if (tool_ctx->nreqs == 0) {
--        tool_ctx->done = true;
--    }
- }
- 
--int main(int argc, const char *argv[])
-+static errno_t
-+parse_options(poptContext pc, struct tool_options *opts)
- {
-     int opt;
--    poptContext pc;
--
--    int pc_debug = 0;
--    int pc_verbose = 0;
--    const char *socket_path = NULL;
--    const char *extra_arg_ptr;
--
--    static const char *headers[] = {
--        "Content-type: application/octet-stream",
--        NULL,
--    };
--
--    struct poptOption long_options[] = {
--        POPT_AUTOHELP
--        { "debug", '\0', POPT_ARG_INT, &pc_debug, 0,
--          "The debug level to run with", NULL },
--        { "socket-path", 's', POPT_ARG_STRING, &socket_path, 0,
--          "The path to the HTTP server socket", NULL },
--        { "get", 'g', POPT_ARG_NONE, NULL, 'g', "Perform a HTTP GET (default)", NULL },
--        { "put", 'p', POPT_ARG_NONE, NULL, 'p', "Perform a HTTP PUT", NULL },
--        { "post", 'o', POPT_ARG_NONE, NULL, 'o', "Perform a HTTP POST", NULL },
--        { "del", 'd', POPT_ARG_NONE, NULL, 'd', "Perform a HTTP DELETE", NULL },
--        { "verbose", 'v', POPT_ARG_NONE, NULL, 'v', "Print response code and body", NULL },
--        POPT_TABLEEND
--    };
--
--    struct tevent_req *req;
--    struct tevent_context *ev;
--    enum tcurl_http_method method = TCURL_HTTP_GET;
--    struct tcurl_ctx *ctx;
--    struct tcurl_request *tcurl_req;
--    struct tool_ctx *tool_ctx;
--
--    const char *urls[MAXREQ] = { 0 };
--    struct sss_iobuf **inbufs;
--
--    size_t n_reqs = 0;
--
--    debug_prg_name = argv[0];
--    pc = poptGetContext(NULL, argc, argv, long_options, 0);
--    poptSetOtherOptionHelp(pc, "HTTPDATA");
- 
-     while ((opt = poptGetNextOpt(pc)) > 0) {
-         switch (opt) {
-         case 'g':
--            method = TCURL_HTTP_GET;
-+            opts->method = TCURL_HTTP_GET;
-             break;
-         case 'p':
--            method = TCURL_HTTP_PUT;
-+            opts->method = TCURL_HTTP_PUT;
-             break;
-         case 'o':
--            method = TCURL_HTTP_POST;
-+            opts->method = TCURL_HTTP_POST;
-             break;
-         case 'd':
--            method = TCURL_HTTP_DELETE;
--            break;
--        case 'v':
--            pc_verbose = 1;
-+            opts->method = TCURL_HTTP_DELETE;
-             break;
-         default:
-             DEBUG(SSSDBG_FATAL_FAILURE, "Unexpected option\n");
--            return 1;
-+            return EINVAL;
-         }
-     }
- 
--    DEBUG_CLI_INIT(pc_debug);
--
--    tool_ctx = talloc_zero(NULL, struct tool_ctx);
--    if (tool_ctx == NULL) {
--        DEBUG(SSSDBG_CRIT_FAILURE, "Could not init tool context\n");
--        return 1;
-+    if (opt != -1) {
-+        poptPrintUsage(pc, stderr, 0);
-+        fprintf(stderr, "%s", poptStrerror(opt));
-+        return EINVAL;
-     }
- 
--    inbufs = talloc_zero_array(tool_ctx, struct sss_iobuf *, MAXREQ);
--    if (inbufs == NULL) {
--        talloc_zfree(tool_ctx);
--        return 1;
-+    return EOK;
-+}
-+
-+static errno_t
-+prepare_requests(TALLOC_CTX *mem_ctx,
-+                 poptContext pc,
-+                 struct tool_options *opts,
-+                 struct tcurl_request ***_requests,
-+                 size_t *_num_requests)
-+{
-+    struct tcurl_request **requests;
-+    const char *arg;
-+    const char *url;
-+    struct sss_iobuf *body;
-+    errno_t ret;
-+    size_t i;
-+
-+    static const char *headers[] = {
-+        "Content-type: application/octet-stream",
-+        NULL,
-+    };
-+
-+    requests = talloc_zero_array(mem_ctx, struct tcurl_request *, MAXREQ + 1);
-+    if (requests == NULL) {
-+        return ENOMEM;
-     }
- 
--    while ((extra_arg_ptr = poptGetArg(pc)) != NULL) {
--        switch(method) {
-+    i = 0;
-+    while ((arg = poptGetArg(pc)) != NULL) {
-+        if (i >= MAXREQ) {
-+            fprintf(stderr, _("Too many requests!\n"));
-+            ret = EINVAL;
-+            goto done;
-+        }
-+
-+        switch (opts->method) {
-         case TCURL_HTTP_GET:
-         case TCURL_HTTP_DELETE:
--        case TCURL_HTTP_POST:
--            urls[n_reqs++] = extra_arg_ptr;
-+            url = arg;
-+            body = NULL;
-             break;
-         case TCURL_HTTP_PUT:
--            if (urls[n_reqs] == NULL) {
--                urls[n_reqs] = extra_arg_ptr;
--            } else {
--                inbufs[n_reqs] = sss_iobuf_init_readonly(
--                                              inbufs,
--                                              (uint8_t *) discard_const(extra_arg_ptr),
--                                              strlen(extra_arg_ptr));
--                if (inbufs[n_reqs] == NULL) {
--                    DEBUG(SSSDBG_CRIT_FAILURE, "Could not init input buffer\n");
--                    talloc_zfree(tool_ctx);
--                    return 1;
--                }
--                n_reqs++;
-+        case TCURL_HTTP_POST:
-+            url = arg;
-+
-+            arg = poptGetArg(pc);
-+            if (arg == NULL) {
-+                body = NULL;
-+                break;
-+            }
-+
-+            body = sss_iobuf_init_readonly(requests,
-+                                           discard_const_p(uint8_t, arg),
-+                                           strlen(arg));
-+            if (body == NULL) {
-+                ret = ENOMEM;
-+                goto done;
-             }
-             break;
-+        default:
-+            DEBUG(SSSDBG_CRIT_FAILURE, "Invalid method!\n");
-+            ret = EINVAL;
-+            goto done;
-         }
-+
-+        requests[i] = tcurl_http(requests, opts->method, opts->socket_path,
-+                                 url, headers, body);
-+        if (requests[i] == NULL) {
-+            ret = ENOMEM;
-+            goto done;
-+        }
-+
-+        i++;
-     }
- 
--    if (opt != -1) {
--        poptPrintUsage(pc, stderr, 0);
--        fprintf(stderr, "%s", poptStrerror(opt));
--        talloc_zfree(tool_ctx);
--        return 1;
-+    *_requests = requests;
-+    *_num_requests = i;
-+
-+    ret = EOK;
-+
-+done:
-+    if (ret != EOK) {
-+        talloc_free(requests);
-     }
- 
--    if (!socket_path) {
--        DEBUG(SSSDBG_FATAL_FAILURE, "Please specify the socket path\n");
--        poptPrintUsage(pc, stderr, 0);
--        talloc_zfree(tool_ctx);
--        return 1;
-+    return ret;
-+}
-+
-+static errno_t
-+run_requests(struct tool_ctx *tool_ctx,
-+             struct tcurl_request **requests)
-+{
-+    TALLOC_CTX *tmp_ctx;
-+    struct tcurl_ctx *tcurl_ctx;
-+    struct tevent_context *ev;
-+    struct tevent_req *req;
-+    errno_t ret;
-+    int i;
-+
-+    tmp_ctx = talloc_new(NULL);
-+    if (tmp_ctx == NULL) {
-+        DEBUG(SSSDBG_FATAL_FAILURE, "Out of memory!\n");
-+        return ENOMEM;
-     }
- 
--    tool_ctx->nreqs = n_reqs;
--    tool_ctx->verbose = !!pc_verbose;
-+    if (requests == NULL || requests[0] == NULL) {
-+        ret = EOK;
-+        goto done;
-+    }
- 
--    ev = tevent_context_init(tool_ctx);
-+    ev = tevent_context_init(tmp_ctx);
-     if (ev == NULL) {
-         DEBUG(SSSDBG_CRIT_FAILURE, "Could not init tevent context\n");
--        talloc_zfree(tool_ctx);
--        return 1;
-+        ret = ENOMEM;
-+        goto done;
-     }
- 
--    ctx = tcurl_init(tool_ctx, ev);
--    if (ctx == NULL) {
-+    tcurl_ctx = tcurl_init(tmp_ctx, ev);
-+    if (tcurl_ctx == NULL) {
-         DEBUG(SSSDBG_FATAL_FAILURE, "Could not init tcurl context\n");
--        talloc_zfree(tool_ctx);
--        return 1;
-+        ret = ENOMEM;
-+        goto done;
-     }
- 
--    for (size_t i = 0; i < n_reqs; i++) {
--        tcurl_req = tcurl_http(tool_ctx, method, socket_path,
--                               urls[i], headers, inbufs[i]);
--        if (tcurl_req == NULL) {
--            DEBUG(SSSDBG_FATAL_FAILURE, "Unable to create TCURL request\n");
--            talloc_zfree(tool_ctx);
--            return 1;
-+    for (i = 0; requests[i] != NULL; i++) {
-+        req = tcurl_request_send(tmp_ctx, ev, tcurl_ctx, requests[i], 5);
-+        if (req == NULL) {
-+            DEBUG(SSSDBG_FATAL_FAILURE, "Could not create tevent request\n");
-+            ret = ENOMEM;
-+            goto done;
-         }
- 
--        req = tcurl_request_send(tool_ctx, ev, ctx, tcurl_req, 10);
--        if (ctx == NULL) {
--            DEBUG(SSSDBG_FATAL_FAILURE, "Could not create request\n");
--            talloc_zfree(tool_ctx);
--            return 1;
--        }
-         tevent_req_set_callback(req, request_done, tool_ctx);
-     }
- 
-@@ -226,11 +243,78 @@ int main(int argc, const char *argv[])
-     if (tool_ctx->nreqs > 0) {
-         DEBUG(SSSDBG_FATAL_FAILURE,
-               "The tool finished with some pending requests, fail!\n");
--        talloc_zfree(tool_ctx);
--        return 1;
-+        ret = EEXIST;
-+        goto done;
-     }
- 
-+    ret = EOK;
-+
-+done:
-+    talloc_free(tmp_ctx);
-+    return ret;
-+}
-+
-+int main(int argc, const char *argv[])
-+{
-+    struct tool_options opts = { 0 };
-+    struct tool_ctx *tool_ctx;
-+    struct tcurl_request **requests;
-+    poptContext pc;
-+    errno_t ret;
-+
-+    struct poptOption long_options[] = {
-+        POPT_AUTOHELP
-+        { "debug", '\0', POPT_ARG_INT, &opts.debug, 0, "The debug level to run with", NULL },
-+        { "socket-path", 's', POPT_ARG_STRING, &opts.socket_path, 0, "The path to the HTTP server socket", NULL },
-+        { "get", 'g', POPT_ARG_NONE, NULL, 'g', "Perform a HTTP GET (default)", NULL },
-+        { "put", 'p', POPT_ARG_NONE, NULL, 'p', "Perform a HTTP PUT", NULL },
-+        { "post", 'o', POPT_ARG_NONE, NULL, 'o', "Perform a HTTP POST", NULL },
-+        { "del", 'd', POPT_ARG_NONE, NULL, 'd', "Perform a HTTP DELETE", NULL },
-+        { "verbose", 'v', POPT_ARG_NONE, &opts.verbose, '\0', "Print response code and body", NULL },
-+        POPT_TABLEEND
-+    };
-+
-+    pc = poptGetContext(NULL, argc, argv, long_options, 0);
-+    poptSetOtherOptionHelp(pc, "[URL HTTPDATA]*");
-+
-+    tool_ctx = talloc_zero(NULL, struct tool_ctx);
-+    if (tool_ctx == NULL) {
-+        DEBUG(SSSDBG_CRIT_FAILURE, "Could not init tool context\n");
-+        ret = ENOMEM;
-+        goto done;
-+    }
-+
-+    ret = parse_options(pc, &opts);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_FATAL_FAILURE, "Unable to parse options [%d]: %s\n",
-+              ret, sss_strerror(ret));
-+        goto done;
-+    }
-+
-+    DEBUG_CLI_INIT(opts.debug);
-+    tool_ctx->verbose = opts.verbose;
-+
-+    ret = prepare_requests(tool_ctx, pc, &opts, &requests, &tool_ctx->nreqs);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_FATAL_FAILURE, "Unable to prepare requests [%d]: %s\n",
-+              ret, sss_strerror(ret));
-+        goto done;
-+    }
-+
-+    ret = run_requests(tool_ctx, requests);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_FATAL_FAILURE, "Unable to issue requests [%d]: %s\n",
-+              ret, sss_strerror(ret));
-+        goto done;
-+    }
-+
-+done:
-     talloc_free(tool_ctx);
-     poptFreeContext(pc);
--    return 0;
-+
-+    if (ret != EOK) {
-+        return EXIT_FAILURE;
-+    }
-+
-+    return EXIT_SUCCESS;
- }
--- 
-2.9.3
-
diff --git a/SOURCES/0076-NEGCACHE-Add-API-for-setting-and-checking-locate-acc.patch b/SOURCES/0076-NEGCACHE-Add-API-for-setting-and-checking-locate-acc.patch
new file mode 100644
index 0000000..4d554a2
--- /dev/null
+++ b/SOURCES/0076-NEGCACHE-Add-API-for-setting-and-checking-locate-acc.patch
@@ -0,0 +1,371 @@
+From 72fdce0007af1baa0504c2d11be8b19e1a3296f1 Mon Sep 17 00:00:00 2001
+From: Jakub Hrozek <jhrozek@redhat.com>
+Date: Mon, 6 Nov 2017 10:09:16 +0100
+Subject: [PATCH 76/83] NEGCACHE: Add API for setting and checking
+ locate-account-domain requests
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Extends the negative cache API with several request getsetters:
+    - sss_ncache_set/check_domain_locate_type - check if this request
+      type supports locating account domain or set that this request
+      type does not support the locator.
+
+    - sss_ncache_set/check_locate_gid/uid - check if it is time to call
+      the locator again or set that the locator should not be called
+      for IDs again for the duration of the negative cache.
+
+Reviewed-by: Pavel Březina <pbrezina@redhat.com>
+Reviewed-by: Sumit Bose <sbose@redhat.com>
+(cherry picked from commit 07452697a67902dc6876d2f40d364cf1eadf2431)
+---
+ src/responder/common/negcache.c  | 155 +++++++++++++++++++++++++++++++++++++++
+ src/responder/common/negcache.h  |  64 ++++++++++++++++
+ src/tests/cmocka/test_negcache.c |  75 +++++++++++++++++++
+ 3 files changed, 294 insertions(+)
+
+diff --git a/src/responder/common/negcache.c b/src/responder/common/negcache.c
+index b751d89ee9e67eea32ec4ed0935fcd67d3e92f47..bd3c9d36805adc5cca5621c815576ac21cfbec38 100644
+--- a/src/responder/common/negcache.c
++++ b/src/responder/common/negcache.c
+@@ -37,6 +37,8 @@
+ #define NC_GID_PREFIX NC_ENTRY_PREFIX"GID"
+ #define NC_SID_PREFIX NC_ENTRY_PREFIX"SID"
+ #define NC_CERT_PREFIX NC_ENTRY_PREFIX"CERT"
++#define NC_DOMAIN_ACCT_LOCATE_PREFIX NC_ENTRY_PREFIX"DOM_LOCATE"
++#define NC_DOMAIN_ACCT_LOCATE_TYPE_PREFIX NC_ENTRY_PREFIX"DOM_LOCATE_TYPE"
+ 
+ struct sss_nc_ctx {
+     struct tdb_context *tdb;
+@@ -665,6 +667,159 @@ int sss_ncache_set_cert(struct sss_nc_ctx *ctx, bool permanent,
+     return ret;
+ }
+ 
++static char *domain_lookup_type_str(TALLOC_CTX *mem_ctx,
++                                    struct sss_domain_info *dom,
++                                    const char *lookup_type)
++{
++    return talloc_asprintf(mem_ctx,
++                           "%s/%s/%s",
++                           NC_DOMAIN_ACCT_LOCATE_TYPE_PREFIX,
++                           dom->name,
++                           lookup_type);
++}
++
++int sss_ncache_set_domain_locate_type(struct sss_nc_ctx *ctx,
++                                      struct sss_domain_info *dom,
++                                      const char *lookup_type)
++{
++    char *str;
++    int ret;
++
++    str = domain_lookup_type_str(ctx, dom, lookup_type);
++    if (!str) return ENOMEM;
++
++    /* Permanent cache is always used here, because whether the lookup
++     * type (getgrgid, getpwuid, ..) supports locating an entry's domain
++     * doesn't change
++     */
++    ret = sss_ncache_set_str(ctx, str, true, false);
++    talloc_free(str);
++    return ret;
++}
++
++int sss_ncache_check_domain_locate_type(struct sss_nc_ctx *ctx,
++                                        struct sss_domain_info *dom,
++                                        const char *lookup_type)
++{
++    char *str;
++    int ret;
++
++    str = domain_lookup_type_str(ctx, dom, lookup_type);
++    if (!str) return ENOMEM;
++
++    ret = sss_ncache_check_str(ctx, str);
++    talloc_free(str);
++    return ret;
++}
++
++static char *locate_gid_str(TALLOC_CTX *mem_ctx,
++                            struct sss_domain_info *dom,
++                            gid_t gid)
++{
++    return talloc_asprintf(mem_ctx,
++                           "%s/%s/%s/%"SPRIgid,
++                           NC_DOMAIN_ACCT_LOCATE_PREFIX,
++                           NC_GID_PREFIX,
++                           dom->name,
++                           gid);
++}
++
++int sss_ncache_set_locate_gid(struct sss_nc_ctx *ctx,
++                              struct sss_domain_info *dom,
++                              gid_t gid)
++{
++    char *str;
++    int ret;
++
++    if (dom == NULL) {
++        return EINVAL;
++    }
++
++    str = locate_gid_str(ctx, dom, gid);
++    if (str == NULL) {
++        return ENOMEM;
++    }
++
++    ret = sss_ncache_set_str(ctx, str, false, false);
++    talloc_free(str);
++    return ret;
++}
++
++int sss_ncache_check_locate_gid(struct sss_nc_ctx *ctx,
++                                struct sss_domain_info *dom,
++                                gid_t gid)
++{
++    char *str;
++    int ret;
++
++    if (dom == NULL) {
++        return EINVAL;
++    }
++
++    str = locate_gid_str(ctx, dom, gid);
++    if (str == NULL) {
++        return ENOMEM;
++    }
++
++    ret = sss_ncache_check_str(ctx, str);
++    talloc_free(str);
++    return ret;
++}
++
++static char *locate_uid_str(struct sss_nc_ctx *ctx,
++                            struct sss_domain_info *dom,
++                            uid_t uid)
++{
++    return talloc_asprintf(ctx,
++                           "%s/%s/%s/%"SPRIuid,
++                           NC_DOMAIN_ACCT_LOCATE_PREFIX,
++                           NC_UID_PREFIX,
++                           dom->name,
++                           uid);
++}
++
++int sss_ncache_set_locate_uid(struct sss_nc_ctx *ctx,
++                              struct sss_domain_info *dom,
++                              uid_t uid)
++{
++    char *str;
++    int ret;
++
++    if (dom == NULL) {
++        return EINVAL;
++    }
++
++    str = locate_uid_str(ctx, dom, uid);
++    if (str == NULL) {
++        return ENOMEM;
++    }
++
++    ret = sss_ncache_set_str(ctx, str, false, false);
++    talloc_free(str);
++    return ret;
++}
++
++int sss_ncache_check_locate_uid(struct sss_nc_ctx *ctx,
++                                struct sss_domain_info *dom,
++                                uid_t uid)
++{
++    char *str;
++    int ret;
++
++    if (dom == NULL) {
++        return EINVAL;
++    }
++
++    str = locate_uid_str(ctx, dom, uid);
++    if (str == NULL) {
++        return ENOMEM;
++    }
++
++    ret = sss_ncache_check_str(ctx, str);
++    talloc_free(str);
++    return ret;
++}
++
+ static int delete_permanent(struct tdb_context *tdb,
+                             TDB_DATA key, TDB_DATA data, void *state)
+ {
+diff --git a/src/responder/common/negcache.h b/src/responder/common/negcache.h
+index 782ec140fb7dfe3ec82bed8d25290c0f7b8a36ea..2ed38e5b9a64d3393513ea2110a7c6fcb7675623 100644
+--- a/src/responder/common/negcache.h
++++ b/src/responder/common/negcache.h
+@@ -80,6 +80,70 @@ int sss_ncache_set_service_name(struct sss_nc_ctx *ctx, bool permanent,
+ int sss_ncache_set_service_port(struct sss_nc_ctx *ctx, bool permanent,
+                                 struct sss_domain_info *dom,
+                                 uint16_t port, const char *proto);
++/*
++ * Mark the lookup_type as not supporting the negative cache. This
++ * would be used by the corresponding checker to avoid needless
++ * subsequent calls to the locator for configurations that do not
++ * support the locator plugin.
++ *
++ * @param ctx   The negative cache
++ * @param dom   The top-level domain. It is expected that the caller
++ *              would use the top-level domain head here, because
++ *              this negative cache is "per-request-type" which is the
++ *              same for all subdomains of a domain
++ * @param lookup_type   Lookup type, e.g. getpwuid, getgrnam.
++ *
++ * @return EOK on success, errno on failure.
++ */
++int sss_ncache_set_domain_locate_type(struct sss_nc_ctx *ctx,
++                                      struct sss_domain_info *dom,
++                                      const char *lookup_type);
++/*
++ * Check if the lookup_type supports the domain locator request.
++ *
++ * @param ctx   The negative cache
++ * @param dom   The top-level domain. It is expected that the caller
++ *              would use the top-level domain head here, because
++ *              this negative cache is "per-request-type" which is the
++ *              same for all subdomains of a domain
++ * @param lookup_type   Lookup type, e.g. getpwuid, getgrnam.
++ *
++ * @return      ENOENT if the request supports the locator (or we
++ *              haven't checked yet), EEXIST if the request does
++ *              not support the domain locator request.
++ */
++int sss_ncache_check_domain_locate_type(struct sss_nc_ctx *ctx,
++                                        struct sss_domain_info *dom,
++                                        const char *key);
++
++/*
++ * Call these two functions to mark a GID as checked until the negative
++ * cache expires. This function is used to avoid a situation where
++ * GID would be found in a subsequent domain, so any request that
++ * searches for this GID again (even if it was cached) would first
++ * run the locator again.
++ *
++ * While this negative cache entry is valid, it is expected that
++ * the negatively cached entries in the domain's GID negative
++ * cache (if any) are valid
++ *
++ * The sss_ncache_set_locate_gid() is called by the locator request
++ * when it finishes, the sss_ncache_check_locate_gid() is called
++ * by the caller of the locator request to find if the locator
++ * should be called at all.
++ */
++int sss_ncache_set_locate_gid(struct sss_nc_ctx *ctx,
++                              struct sss_domain_info *dom,
++                              gid_t gid);
++int sss_ncache_check_locate_gid(struct sss_nc_ctx *ctx,
++                                struct sss_domain_info *dom,
++                                gid_t gid);
++int sss_ncache_check_locate_uid(struct sss_nc_ctx *ctx,
++                                struct sss_domain_info *dom,
++                                uid_t uid);
++int sss_ncache_set_locate_uid(struct sss_nc_ctx *ctx,
++                              struct sss_domain_info *dom,
++                              uid_t uid);
+ 
+ int sss_ncache_reset_permanent(struct sss_nc_ctx *ctx);
+ int sss_ncache_reset_users(struct sss_nc_ctx *ctx);
+diff --git a/src/tests/cmocka/test_negcache.c b/src/tests/cmocka/test_negcache.c
+index ba39f778d5ddc6a4e1708aef66fc2aa1c809f150..a0210928bd60e364c60717c8b37b2405730f34ab 100644
+--- a/src/tests/cmocka/test_negcache.c
++++ b/src/tests/cmocka/test_negcache.c
+@@ -883,6 +883,77 @@ static void test_sss_ncache_reset(void **state)
+     assert_int_equal(ret, ENOENT);
+ }
+ 
++static void test_sss_ncache_locate_uid_gid(void **state)
++{
++    uid_t uid;
++    gid_t gid;
++    int ret;
++    struct test_state *ts;
++    struct sss_domain_info *dom;
++    struct sss_domain_info *dom2;
++
++    ts = talloc_get_type_abort(*state, struct test_state);
++
++    uid = getuid();
++    gid = getgid();
++
++    dom = talloc(ts, struct sss_domain_info);
++    assert_non_null(dom);
++    dom->name = discard_const_p(char, TEST_DOM_NAME);
++
++    dom2 = talloc(ts, struct sss_domain_info);
++    assert_non_null(dom2);
++    dom2->name = discard_const_p(char, TEST_DOM_NAME"2");
++
++    ret = sss_ncache_check_locate_gid(ts->ctx, dom, gid);
++    assert_int_equal(ret, ENOENT);
++    ret = sss_ncache_check_locate_uid(ts->ctx, dom, uid);
++    assert_int_equal(ret, ENOENT);
++
++    ret = sss_ncache_set_locate_gid(ts->ctx, dom, gid);
++    assert_int_equal(ret, EOK);
++    ret = sss_ncache_set_locate_uid(ts->ctx, dom, uid);
++    assert_int_equal(ret, EOK);
++
++    ret = sss_ncache_check_locate_gid(ts->ctx, dom, gid);
++    assert_int_equal(ret, EEXIST);
++    ret = sss_ncache_check_locate_uid(ts->ctx, dom, uid);
++    assert_int_equal(ret, EEXIST);
++
++    ret = sss_ncache_check_locate_gid(ts->ctx, dom2, gid);
++    assert_int_equal(ret, ENOENT);
++    ret = sss_ncache_check_locate_uid(ts->ctx, dom2, uid);
++    assert_int_equal(ret, ENOENT);
++}
++
++static void test_sss_ncache_domain_locate_type(void **state)
++{
++    int ret;
++    struct test_state *ts;
++    struct sss_domain_info *dom;
++    struct sss_domain_info *dom2;
++
++    ts = talloc_get_type_abort(*state, struct test_state);
++
++    dom = talloc(ts, struct sss_domain_info);
++    assert_non_null(dom);
++    dom->name = discard_const_p(char, TEST_DOM_NAME);
++
++    dom2 = talloc(ts, struct sss_domain_info);
++    assert_non_null(dom2);
++    dom2->name = discard_const_p(char, TEST_DOM_NAME"2");
++
++    ret = sss_ncache_check_domain_locate_type(ts->ctx, dom, "foo");
++    assert_int_equal(ret, ENOENT);
++    ret = sss_ncache_set_domain_locate_type(ts->ctx, dom, "foo");
++    assert_int_equal(ret, EOK);
++    ret = sss_ncache_check_domain_locate_type(ts->ctx, dom, "foo");
++    assert_int_equal(ret, EEXIST);
++
++    ret = sss_ncache_check_domain_locate_type(ts->ctx, dom2, "foo");
++    assert_int_equal(ret, ENOENT);
++}
++
+ int main(void)
+ {
+     int rv;
+@@ -909,6 +980,10 @@ int main(void)
+                                         setup, teardown),
+         cmocka_unit_test_setup_teardown(test_sss_ncache_reset,
+                                         setup, teardown),
++        cmocka_unit_test_setup_teardown(test_sss_ncache_locate_uid_gid,
++                                        setup, teardown),
++        cmocka_unit_test_setup_teardown(test_sss_ncache_domain_locate_type,
++                                        setup, teardown),
+     };
+ 
+     tests_set_cwd();
+-- 
+2.14.3
+
diff --git a/SOURCES/0076-tcurl-test-add-support-for-raw-output.patch b/SOURCES/0076-tcurl-test-add-support-for-raw-output.patch
deleted file mode 100644
index 3004adc..0000000
--- a/SOURCES/0076-tcurl-test-add-support-for-raw-output.patch
+++ /dev/null
@@ -1,49 +0,0 @@
-From 961abf2d35e296fe2b12b2b48c5d3fc67c0bc779 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Pavel=20B=C5=99ezina?= <pbrezina@redhat.com>
-Date: Fri, 24 Feb 2017 12:23:22 +0100
-Subject: [PATCH 76/90] tcurl test: add support for raw output
-
-Reviewed-by: Simo Sorce <simo@redhat.com>
-Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
-(cherry picked from commit 36e49a842e257ac9bde71728ee3bef4299b6e6e2)
----
- src/tests/tcurl_test_tool.c | 9 +++++++++
- 1 file changed, 9 insertions(+)
-
-diff --git a/src/tests/tcurl_test_tool.c b/src/tests/tcurl_test_tool.c
-index e5fc9705db415650d849b89c3d18e41574b7e28b..7d3bc19f0ec7e118e251247536d25c58fe009f54 100644
---- a/src/tests/tcurl_test_tool.c
-+++ b/src/tests/tcurl_test_tool.c
-@@ -36,6 +36,7 @@ struct tool_ctx {
- struct tool_options {
-     int debug;
-     int verbose;
-+    int raw;
- 
-     enum tcurl_http_method method;
-     const char *socket_path;
-@@ -173,6 +174,13 @@ prepare_requests(TALLOC_CTX *mem_ctx,
-             goto done;
-         }
- 
-+        if (opts->raw) {
-+            ret = tcurl_req_enable_rawoutput(requests[i]);
-+            if (ret != EOK) {
-+                goto done;
-+            }
-+        }
-+
-         i++;
-     }
- 
-@@ -270,6 +278,7 @@ int main(int argc, const char *argv[])
-         { "put", 'p', POPT_ARG_NONE, NULL, 'p', "Perform a HTTP PUT", NULL },
-         { "post", 'o', POPT_ARG_NONE, NULL, 'o', "Perform a HTTP POST", NULL },
-         { "del", 'd', POPT_ARG_NONE, NULL, 'd', "Perform a HTTP DELETE", NULL },
-+        { "raw", 'r', POPT_ARG_NONE, &opts.raw, '\0', "Print raw protocol output", NULL },
-         { "verbose", 'v', POPT_ARG_NONE, &opts.verbose, '\0', "Print response code and body", NULL },
-         POPT_TABLEEND
-     };
--- 
-2.9.3
-
diff --git a/SOURCES/0077-TESTS-Add-tests-for-the-object-by-id-cache_req-inter.patch b/SOURCES/0077-TESTS-Add-tests-for-the-object-by-id-cache_req-inter.patch
new file mode 100644
index 0000000..178e22e
--- /dev/null
+++ b/SOURCES/0077-TESTS-Add-tests-for-the-object-by-id-cache_req-inter.patch
@@ -0,0 +1,439 @@
+From 9a4c06ddf5ec8d610f49acf5d3e231d36b37c50b Mon Sep 17 00:00:00 2001
+From: Jakub Hrozek <jhrozek@redhat.com>
+Date: Sun, 19 Nov 2017 17:25:02 +0100
+Subject: [PATCH 77/83] TESTS: Add tests for the object-by-id cache_req
+ interface
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+This interface will be extended in later patches, but had no tests at
+all.
+
+Reviewed-by: Pavel Březina <pbrezina@redhat.com>
+Reviewed-by: Sumit Bose <sbose@redhat.com>
+(cherry picked from commit 6cd367da68ff56eb48b8b4167dbdd5e53992d194)
+---
+ src/tests/cmocka/test_responder_cache_req.c | 385 ++++++++++++++++++++++++++++
+ 1 file changed, 385 insertions(+)
+
+diff --git a/src/tests/cmocka/test_responder_cache_req.c b/src/tests/cmocka/test_responder_cache_req.c
+index 80086232fd437876c2b190fb972c2ee3194d9efd..f075480a019e476407a3081a795c3c289455aca8 100644
+--- a/src/tests/cmocka/test_responder_cache_req.c
++++ b/src/tests/cmocka/test_responder_cache_req.c
+@@ -197,6 +197,18 @@ static void cache_req_object_by_sid_test_done(struct tevent_req *req)
+     ctx->tctx->done = true;
+ }
+ 
++static void cache_req_object_by_id_test_done(struct tevent_req *req)
++{
++    struct cache_req_test_ctx *ctx = NULL;
++
++    ctx = tevent_req_callback_data(req, struct cache_req_test_ctx);
++
++    ctx->tctx->error = cache_req_object_by_id_recv(ctx, req, &ctx->result);
++    talloc_zfree(req);
++
++    ctx->tctx->done = true;
++}
++
+ static void prepare_user(struct sss_domain_info *domain,
+                          struct test_user *user,
+                          uint64_t timeout,
+@@ -417,6 +429,33 @@ static void run_object_by_sid(struct cache_req_test_ctx *test_ctx,
+     talloc_free(req_mem_ctx);
+ }
+ 
++static void run_object_by_id(struct cache_req_test_ctx *test_ctx,
++                             struct sss_domain_info *domain,
++                             id_t id,
++                             const char **attrs,
++                             int cache_refresh_percent,
++                             errno_t exp_ret)
++{
++    TALLOC_CTX *req_mem_ctx;
++    struct tevent_req *req;
++    errno_t ret;
++
++    req_mem_ctx = talloc_new(global_talloc_context);
++    check_leaks_push(req_mem_ctx);
++
++    req = cache_req_object_by_id_send(req_mem_ctx, test_ctx->tctx->ev,
++            test_ctx->rctx, test_ctx->ncache, cache_refresh_percent,
++            (domain == NULL ? NULL : domain->name), id, attrs);
++    assert_non_null(req);
++    tevent_req_set_callback(req, cache_req_object_by_id_test_done, test_ctx);
++
++    ret = test_ev_loop(test_ctx->tctx);
++    assert_int_equal(ret, exp_ret);
++    assert_true(check_leaks_pop(req_mem_ctx));
++
++    talloc_free(req_mem_ctx);
++}
++
+ struct tevent_req *
+ __wrap_sss_dp_get_account_send(TALLOC_CTX *mem_ctx,
+                                struct resp_ctx *rctx,
+@@ -2132,6 +2171,334 @@ void test_object_by_sid_group_multiple_domains_notfound(void **state)
+     assert_true(test_ctx->dp_called);
+ }
+ 
++void test_object_by_id_user_cache_valid(void **state)
++{
++    struct cache_req_test_ctx *test_ctx = NULL;
++    const char *attrs[] = SYSDB_PW_ATTRS;
++
++    test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx);
++
++    /* Setup user. */
++    prepare_user(test_ctx->tctx->dom, &users[0], 1000, time(NULL));
++
++    /* Test. */
++    run_object_by_id(test_ctx, NULL, users[0].uid, attrs, 0, ERR_OK);
++    check_user(test_ctx, &users[0], test_ctx->tctx->dom);
++}
++
++void test_object_by_id_user_cache_expired(void **state)
++{
++    struct cache_req_test_ctx *test_ctx = NULL;
++    const char *attrs[] = SYSDB_PW_ATTRS;
++
++    test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx);
++
++    /* Setup user. */
++    prepare_user(test_ctx->tctx->dom, &users[0], -1000, time(NULL));
++
++    /* Mock values. */
++    /* DP should be contacted */
++    will_return(__wrap_sss_dp_get_account_send, test_ctx);
++    mock_account_recv_simple();
++
++    /* Test. */
++    run_object_by_id(test_ctx, NULL, users[0].uid, attrs, 0, ERR_OK);
++    assert_true(test_ctx->dp_called);
++    check_user(test_ctx, &users[0], test_ctx->tctx->dom);
++}
++
++void test_object_by_id_user_cache_midpoint(void **state)
++{
++    struct cache_req_test_ctx *test_ctx = NULL;
++    const char *attrs[] = SYSDB_PW_ATTRS;
++
++    test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx);
++
++    /* Setup user. */
++    prepare_user(test_ctx->tctx->dom, &users[0], 50, time(NULL) - 26);
++
++    /* Mock values. */
++    /* DP should be contacted without callback */
++    will_return(__wrap_sss_dp_get_account_send, test_ctx);
++
++    /* Test. */
++    run_object_by_id(test_ctx, NULL, users[0].uid, attrs, 50, ERR_OK);
++    assert_true(test_ctx->dp_called);
++    check_user(test_ctx, &users[0], test_ctx->tctx->dom);
++}
++
++void test_object_by_id_user_ncache(void **state)
++{
++    struct cache_req_test_ctx *test_ctx = NULL;
++    const char *attrs[] = SYSDB_PW_ATTRS;
++    errno_t ret;
++
++    test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx);
++
++    /* Setup user. We explicitly add the UID into BOTH UID and GID
++     * namespaces, because otherwise the cache_req plugin would
++     * search the Data Provider anyway, becase it can't be sure
++     * the object can be of the other type or not
++     */
++    ret = sss_ncache_set_uid(test_ctx->ncache,
++                             false,
++                             test_ctx->tctx->dom,
++                             users[0].uid);
++    assert_int_equal(ret, EOK);
++
++    ret = sss_ncache_set_gid(test_ctx->ncache,
++                             false,
++                             test_ctx->tctx->dom,
++                             users[0].uid);
++    assert_int_equal(ret, EOK);
++
++    /* Test. */
++    run_object_by_id(test_ctx, NULL, users[0].uid, attrs, 0, ENOENT);
++    assert_false(test_ctx->dp_called);
++}
++
++void test_object_by_id_user_missing_found(void **state)
++{
++    struct cache_req_test_ctx *test_ctx = NULL;
++    const char *attrs[] = SYSDB_PW_ATTRS;
++
++    test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx);
++
++    /* Mock values. */
++    will_return(__wrap_sss_dp_get_account_send, test_ctx);
++    mock_account_recv_simple();
++
++    test_ctx->create_user1 = true;
++    test_ctx->create_user2 = false;
++
++    /* Test. */
++    run_object_by_id(test_ctx, NULL, users[0].uid, attrs, 0, ERR_OK);
++    assert_true(test_ctx->dp_called);
++    check_user(test_ctx, &users[0], test_ctx->tctx->dom);
++}
++
++void test_object_by_id_user_missing_notfound(void **state)
++{
++    struct cache_req_test_ctx *test_ctx = NULL;
++    const char *attrs[] = SYSDB_PW_ATTRS;
++
++    test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx);
++
++    /* Mock values. */
++    will_return(__wrap_sss_dp_get_account_send, test_ctx);
++    mock_account_recv_simple();
++
++    /* Test. */
++    run_object_by_id(test_ctx, NULL, users[0].uid, attrs, 0, ENOENT);
++    assert_true(test_ctx->dp_called);
++}
++
++void test_object_by_id_user_multiple_domains_found(void **state)
++{
++    struct cache_req_test_ctx *test_ctx = NULL;
++    struct sss_domain_info *domain = NULL;
++    const char *attrs[] = SYSDB_PW_ATTRS;
++
++    test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx);
++
++    /* Setup user. */
++    domain = find_domain_by_name(test_ctx->tctx->dom,
++                                 "responder_cache_req_test_d", true);
++    assert_non_null(domain);
++
++    prepare_user(domain, &users[0], 1000, time(NULL));
++
++    /* Mock values. */
++    will_return_always(__wrap_sss_dp_get_account_send, test_ctx);
++    will_return_always(sss_dp_req_recv, 0);
++
++    /* Test. */
++    run_object_by_id(test_ctx, NULL, users[0].uid, attrs, 0, ERR_OK);
++    assert_true(test_ctx->dp_called);
++    check_user(test_ctx, &users[0], domain);
++}
++
++void test_object_by_id_user_multiple_domains_notfound(void **state)
++{
++    struct cache_req_test_ctx *test_ctx = NULL;
++    const char *attrs[] = SYSDB_PW_ATTRS;
++
++    test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx);
++
++    /* Mock values. */
++    will_return_always(__wrap_sss_dp_get_account_send, test_ctx);
++    will_return_always(sss_dp_req_recv, 0);
++
++    /* Test. */
++    run_object_by_id(test_ctx, NULL, users[0].uid, attrs, 0, ENOENT);
++    assert_true(test_ctx->dp_called);
++}
++
++void test_object_by_id_group_cache_valid(void **state)
++{
++    struct cache_req_test_ctx *test_ctx = NULL;
++    const char *attrs[] = SYSDB_GRSRC_ATTRS;
++
++    test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx);
++
++    /* Setup user. */
++    prepare_group(test_ctx->tctx->dom, &groups[0], 1000, time(NULL));
++
++    /* Test. */
++    run_object_by_id(test_ctx, NULL, groups[0].gid, attrs, 0, ERR_OK);
++    check_group(test_ctx, &groups[0], test_ctx->tctx->dom);
++}
++
++void test_object_by_id_group_cache_expired(void **state)
++{
++    struct cache_req_test_ctx *test_ctx = NULL;
++    const char *attrs[] = SYSDB_GRSRC_ATTRS;
++
++    test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx);
++
++    /* Setup user. */
++    prepare_group(test_ctx->tctx->dom, &groups[0], -1000, time(NULL));
++
++    /* Mock values. */
++    /* DP should be contacted */
++    will_return(__wrap_sss_dp_get_account_send, test_ctx);
++    mock_account_recv_simple();
++
++    /* Test. */
++    run_object_by_id(test_ctx, NULL, groups[0].gid, attrs, 0, ERR_OK);
++    assert_true(test_ctx->dp_called);
++    check_group(test_ctx, &groups[0], test_ctx->tctx->dom);
++}
++
++void test_object_by_id_group_cache_midpoint(void **state)
++{
++    struct cache_req_test_ctx *test_ctx = NULL;
++    const char *attrs[] = SYSDB_GRSRC_ATTRS;
++
++    test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx);
++
++    /* Setup user. */
++    prepare_group(test_ctx->tctx->dom, &groups[0], 50, time(NULL) - 26);
++
++    /* Mock values. */
++    /* DP should be contacted without callback */
++    will_return(__wrap_sss_dp_get_account_send, test_ctx);
++
++    /* Test. */
++    run_object_by_id(test_ctx, NULL, groups[0].gid, attrs, 50, ERR_OK);
++    assert_true(test_ctx->dp_called);
++    check_group(test_ctx, &groups[0], test_ctx->tctx->dom);
++}
++
++void test_object_by_id_group_ncache(void **state)
++{
++    struct cache_req_test_ctx *test_ctx = NULL;
++    const char *attrs[] = SYSDB_GRSRC_ATTRS;
++    errno_t ret;
++
++    test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx);
++
++    /* Setup group. We explicitly add the UID into BOTH UID and GID
++     * namespaces, because otherwise the cache_req plugin would
++     * search the Data Provider anyway, becase it can't be sure
++     * the object can be of the other type or not
++     */
++    ret = sss_ncache_set_uid(test_ctx->ncache,
++                             false,
++                             test_ctx->tctx->dom,
++                             groups[0].gid);
++    assert_int_equal(ret, EOK);
++
++    ret = sss_ncache_set_gid(test_ctx->ncache,
++                             false,
++                             test_ctx->tctx->dom,
++                             groups[0].gid);
++    assert_int_equal(ret, EOK);
++
++    assert_int_equal(ret, EOK);
++
++    /* Test. */
++    run_object_by_id(test_ctx, NULL, groups[0].gid, attrs, 0, ENOENT);
++    assert_false(test_ctx->dp_called);
++}
++
++void test_object_by_id_group_missing_found(void **state)
++{
++    struct cache_req_test_ctx *test_ctx = NULL;
++    const char *attrs[] = SYSDB_GRSRC_ATTRS;
++
++    test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx);
++
++    /* Mock values. */
++    will_return(__wrap_sss_dp_get_account_send, test_ctx);
++    mock_account_recv_simple();
++
++    test_ctx->create_group1 = true;
++    test_ctx->create_group2 = false;
++
++    /* Test. */
++    run_object_by_id(test_ctx, NULL, groups[0].gid, attrs, 0, ERR_OK);
++    assert_true(test_ctx->dp_called);
++    check_group(test_ctx, &groups[0], test_ctx->tctx->dom);
++}
++
++void test_object_by_id_group_missing_notfound(void **state)
++{
++    struct cache_req_test_ctx *test_ctx = NULL;
++    const char *attrs[] = SYSDB_GRSRC_ATTRS;
++
++    test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx);
++
++    /* Mock values. */
++    will_return(__wrap_sss_dp_get_account_send, test_ctx);
++    mock_account_recv_simple();
++
++    /* Test. */
++    run_object_by_id(test_ctx, NULL, groups[0].gid, attrs, 0, ENOENT);
++    assert_true(test_ctx->dp_called);
++}
++
++void test_object_by_id_group_multiple_domains_found(void **state)
++{
++    struct cache_req_test_ctx *test_ctx = NULL;
++    struct sss_domain_info *domain = NULL;
++    const char *attrs[] = SYSDB_GRSRC_ATTRS;
++
++    test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx);
++
++    /* Setup user. */
++    domain = find_domain_by_name(test_ctx->tctx->dom,
++                                 "responder_cache_req_test_d", true);
++    assert_non_null(domain);
++
++    prepare_group(domain, &groups[0], 1000, time(NULL));
++
++    /* Mock values. */
++    will_return_always(__wrap_sss_dp_get_account_send, test_ctx);
++    will_return_always(sss_dp_req_recv, 0);
++
++    /* Test. */
++    run_object_by_id(test_ctx, NULL, groups[0].gid, attrs, 0, ERR_OK);
++    assert_true(test_ctx->dp_called);
++    check_group(test_ctx, &groups[0], domain);
++}
++
++void test_object_by_id_group_multiple_domains_notfound(void **state)
++{
++    struct cache_req_test_ctx *test_ctx = NULL;
++    const char *attrs[] = SYSDB_GRSRC_ATTRS;
++
++    test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx);
++
++    /* Mock values. */
++    will_return_always(__wrap_sss_dp_get_account_send, test_ctx);
++    will_return_always(sss_dp_req_recv, 0);
++
++    /* Test. */
++    run_object_by_id(test_ctx, NULL, groups[0].gid, attrs, 0, ENOENT);
++    assert_true(test_ctx->dp_called);
++}
++
+ int main(int argc, const char *argv[])
+ {
+     poptContext pc;
+@@ -2218,6 +2585,24 @@ int main(int argc, const char *argv[])
+         new_single_domain_test(object_by_sid_group_missing_notfound),
+         new_multi_domain_test(object_by_sid_group_multiple_domains_found),
+         new_multi_domain_test(object_by_sid_group_multiple_domains_notfound),
++
++        new_single_domain_test(object_by_id_user_cache_valid),
++        new_single_domain_test(object_by_id_user_cache_expired),
++        new_single_domain_test(object_by_id_user_cache_midpoint),
++        new_single_domain_test(object_by_id_user_ncache),
++        new_single_domain_test(object_by_id_user_missing_found),
++        new_single_domain_test(object_by_id_user_missing_notfound),
++        new_multi_domain_test(object_by_id_user_multiple_domains_found),
++        new_multi_domain_test(object_by_id_user_multiple_domains_notfound),
++
++        new_single_domain_test(object_by_id_group_cache_valid),
++        new_single_domain_test(object_by_id_group_cache_expired),
++        new_single_domain_test(object_by_id_group_cache_midpoint),
++        new_single_domain_test(object_by_id_group_ncache),
++        new_single_domain_test(object_by_id_group_missing_found),
++        new_single_domain_test(object_by_id_group_missing_notfound),
++        new_multi_domain_test(object_by_id_group_multiple_domains_found),
++        new_multi_domain_test(object_by_id_group_multiple_domains_notfound),
+     };
+ 
+     /* Set debug level to invalid value so we can deside if -d 0 was used. */
+-- 
+2.14.3
+
diff --git a/SOURCES/0077-tcurl-test-add-support-for-tls-settings.patch b/SOURCES/0077-tcurl-test-add-support-for-tls-settings.patch
deleted file mode 100644
index d1e5926..0000000
--- a/SOURCES/0077-tcurl-test-add-support-for-tls-settings.patch
+++ /dev/null
@@ -1,62 +0,0 @@
-From 4a0d05defd8da2fb7e618e485909b9807b83acbf Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Pavel=20B=C5=99ezina?= <pbrezina@redhat.com>
-Date: Mon, 27 Feb 2017 12:58:06 +0100
-Subject: [PATCH 77/90] tcurl test: add support for tls settings
-
-Reviewed-by: Simo Sorce <simo@redhat.com>
-Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
-(cherry picked from commit 886e0f75e6f4c7877a23a3625f8a20c09109b09d)
----
- src/tests/tcurl_test_tool.c | 19 +++++++++++++++++++
- 1 file changed, 19 insertions(+)
-
-diff --git a/src/tests/tcurl_test_tool.c b/src/tests/tcurl_test_tool.c
-index 7d3bc19f0ec7e118e251247536d25c58fe009f54..9cec000fbf2e4eca2fdc5213c8b3b4cb10f1df1b 100644
---- a/src/tests/tcurl_test_tool.c
-+++ b/src/tests/tcurl_test_tool.c
-@@ -37,9 +37,14 @@ struct tool_options {
-     int debug;
-     int verbose;
-     int raw;
-+    int tls;
-+    int verify_peer;
-+    int verify_host;
- 
-     enum tcurl_http_method method;
-     const char *socket_path;
-+    const char *capath;
-+    const char *cacert;
- };
- 
- static void request_done(struct tevent_req *req)
-@@ -181,6 +186,14 @@ prepare_requests(TALLOC_CTX *mem_ctx,
-             }
-         }
- 
-+        if (opts->tls) {
-+            ret = tcurl_req_verify_peer(requests[i], opts->capath, opts->cacert,
-+                                        opts->verify_peer, opts->verify_host);
-+            if (ret != EOK) {
-+                goto done;
-+            }
-+        }
-+
-         i++;
-     }
- 
-@@ -280,6 +293,12 @@ int main(int argc, const char *argv[])
-         { "del", 'd', POPT_ARG_NONE, NULL, 'd', "Perform a HTTP DELETE", NULL },
-         { "raw", 'r', POPT_ARG_NONE, &opts.raw, '\0', "Print raw protocol output", NULL },
-         { "verbose", 'v', POPT_ARG_NONE, &opts.verbose, '\0', "Print response code and body", NULL },
-+        /* TLS */
-+        { "tls", '\0', POPT_ARG_NONE, &opts.tls, '\0', "Enable TLS", NULL },
-+        { "verify-peer", '\0', POPT_ARG_NONE, &opts.verify_peer, '\0', "Verify peer when TLS is enabled", NULL },
-+        { "verify-host", '\0', POPT_ARG_NONE, &opts.verify_host, '\0', "Verify host when TLS is enabled", NULL },
-+        { "capath", '\0', POPT_ARG_STRING, &opts.capath, '\0', "Path to CA directory where peer certificate is stored", NULL },
-+        { "cacert", '\0', POPT_ARG_STRING, &opts.cacert, '\0', "Path to CA certificate", NULL },
-         POPT_TABLEEND
-     };
- 
--- 
-2.9.3
-
diff --git a/SOURCES/0078-CACHE_REQ-Export-cache_req_search_ncache_add-as-cach.patch b/SOURCES/0078-CACHE_REQ-Export-cache_req_search_ncache_add-as-cach.patch
new file mode 100644
index 0000000..cf3ca1f
--- /dev/null
+++ b/SOURCES/0078-CACHE_REQ-Export-cache_req_search_ncache_add-as-cach.patch
@@ -0,0 +1,77 @@
+From 26ba3d0b033e52e63d6ec438d7be0df97cb4ce1b Mon Sep 17 00:00:00 2001
+From: Jakub Hrozek <jhrozek@redhat.com>
+Date: Mon, 30 Oct 2017 20:18:36 +0100
+Subject: [PATCH 78/83] CACHE_REQ: Export cache_req_search_ncache_add() as
+ cache_req private interface
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Previously, it was enough to add an entry to the negative cache of the
+domain being processed in cache_req (cr->domain). But the locator plugin
+can return any domain from the processed domain's subdomain list as
+well.
+
+Therefore, this patch extends the internal API for the possibility of
+setting the negative cache in another domain as well.
+
+Reviewed-by: Pavel Březina <pbrezina@redhat.com>
+Reviewed-by: Sumit Bose <sbose@redhat.com>
+(cherry picked from commit 800b1a27543fa83bc6cd73d8e2789f3cdbaf584a)
+---
+ src/responder/common/cache_req/cache_req_private.h |  3 +++
+ src/responder/common/cache_req/cache_req_search.c  | 10 ++++++++--
+ 2 files changed, 11 insertions(+), 2 deletions(-)
+
+diff --git a/src/responder/common/cache_req/cache_req_private.h b/src/responder/common/cache_req/cache_req_private.h
+index 0f630542d38a277d1819063fa4134bd7d2525c90..a156fc65fed80693cdd0473613aeaaa3f5bb2269 100644
+--- a/src/responder/common/cache_req/cache_req_private.h
++++ b/src/responder/common/cache_req/cache_req_private.h
+@@ -116,6 +116,9 @@ cache_req_steal_data_and_send(TALLOC_CTX *mem_ctx,
+                               const char *domain,
+                               struct cache_req_data *data);
+ 
++void cache_req_search_ncache_add_to_domain(struct cache_req *cr,
++                                           struct sss_domain_info *domain);
++
+ errno_t
+ cache_req_add_result(TALLOC_CTX *mem_ctx,
+                      struct cache_req_result *new_result,
+diff --git a/src/responder/common/cache_req/cache_req_search.c b/src/responder/common/cache_req/cache_req_search.c
+index 56d0345cd8f98de574961d3c9628ae7a4c24f9be..9d5ad8056cda0284b1cc32cd51d7cb0ec12ad667 100644
+--- a/src/responder/common/cache_req/cache_req_search.c
++++ b/src/responder/common/cache_req/cache_req_search.c
+@@ -60,7 +60,8 @@ static errno_t cache_req_search_ncache(struct cache_req *cr)
+     return EOK;
+ }
+ 
+-static void cache_req_search_ncache_add(struct cache_req *cr)
++void cache_req_search_ncache_add_to_domain(struct cache_req *cr,
++                                           struct sss_domain_info *domain)
+ {
+     errno_t ret;
+ 
+@@ -73,7 +74,7 @@ static void cache_req_search_ncache_add(struct cache_req *cr)
+     CACHE_REQ_DEBUG(SSSDBG_TRACE_FUNC, cr, "Adding [%s] to negative cache\n",
+                     cr->debugobj);
+ 
+-    ret = cr->plugin->ncache_add_fn(cr->ncache, cr->domain, cr->data);
++    ret = cr->plugin->ncache_add_fn(cr->ncache, domain, cr->data);
+     if (ret != EOK) {
+         CACHE_REQ_DEBUG(SSSDBG_MINOR_FAILURE, cr,
+                         "Cannot set negative cache for [%s] [%d]: %s\n",
+@@ -84,6 +85,11 @@ static void cache_req_search_ncache_add(struct cache_req *cr)
+     return;
+ }
+ 
++static void cache_req_search_ncache_add(struct cache_req *cr)
++{
++    return cache_req_search_ncache_add_to_domain(cr, cr->domain);
++}
++
+ static errno_t cache_req_search_ncache_filter(TALLOC_CTX *mem_ctx,
+                                               struct cache_req *cr,
+                                               struct ldb_result **_result)
+-- 
+2.14.3
+
diff --git a/SOURCES/0078-tcurl-add-support-for-http-basic-auth.patch b/SOURCES/0078-tcurl-add-support-for-http-basic-auth.patch
deleted file mode 100644
index fd6ce23..0000000
--- a/SOURCES/0078-tcurl-add-support-for-http-basic-auth.patch
+++ /dev/null
@@ -1,112 +0,0 @@
-From 1c543722b2b1c55b06c3cc02ace987fc68bc26d7 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Pavel=20B=C5=99ezina?= <pbrezina@redhat.com>
-Date: Tue, 28 Feb 2017 13:32:31 +0100
-Subject: [PATCH 78/90] tcurl: add support for http basic auth
-
-Reviewed-by: Simo Sorce <simo@redhat.com>
-Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
-(cherry picked from commit c2ea75da72b426d98ba489039e220d417bfb4c2a)
----
- src/tests/tcurl_test_tool.c | 14 ++++++++++++++
- src/util/tev_curl.c         | 24 ++++++++++++++++++++++++
- src/util/tev_curl.h         | 15 +++++++++++++++
- 3 files changed, 53 insertions(+)
-
-diff --git a/src/tests/tcurl_test_tool.c b/src/tests/tcurl_test_tool.c
-index 9cec000fbf2e4eca2fdc5213c8b3b4cb10f1df1b..4ceef8e06040ea0abd4d112a5b7845f436c69488 100644
---- a/src/tests/tcurl_test_tool.c
-+++ b/src/tests/tcurl_test_tool.c
-@@ -45,6 +45,9 @@ struct tool_options {
-     const char *socket_path;
-     const char *capath;
-     const char *cacert;
-+
-+    const char *username;
-+    const char *password;
- };
- 
- static void request_done(struct tevent_req *req)
-@@ -194,6 +197,14 @@ prepare_requests(TALLOC_CTX *mem_ctx,
-             }
-         }
- 
-+        if (opts->username != NULL && opts->password != NULL) {
-+            ret = tcurl_req_http_basic_auth(requests[i], opts->username,
-+                                            opts->password);
-+            if (ret != EOK) {
-+                goto done;
-+            }
-+        }
-+
-         i++;
-     }
- 
-@@ -299,6 +310,9 @@ int main(int argc, const char *argv[])
-         { "verify-host", '\0', POPT_ARG_NONE, &opts.verify_host, '\0', "Verify host when TLS is enabled", NULL },
-         { "capath", '\0', POPT_ARG_STRING, &opts.capath, '\0', "Path to CA directory where peer certificate is stored", NULL },
-         { "cacert", '\0', POPT_ARG_STRING, &opts.cacert, '\0', "Path to CA certificate", NULL },
-+        /* BASIC AUTH */
-+        { "username", '\0', POPT_ARG_STRING, &opts.username, '\0', "Username for basic authentication", NULL },
-+        { "password", '\0', POPT_ARG_STRING, &opts.password, '\0', "Password for basic authentication", NULL },
-         POPT_TABLEEND
-     };
- 
-diff --git a/src/util/tev_curl.c b/src/util/tev_curl.c
-index c155f4c038d4215933ee30d41c694ad4a14ae132..8faf07c714b636a0351be365597de68d2f68a1be 100644
---- a/src/util/tev_curl.c
-+++ b/src/util/tev_curl.c
-@@ -1092,3 +1092,27 @@ errno_t tcurl_req_set_client_cert(struct tcurl_request *tcurl_req,
- 
-     return EOK;
- }
-+
-+errno_t tcurl_req_http_basic_auth(struct tcurl_request *tcurl_req,
-+                                  const char *username,
-+                                  const char *password)
-+{
-+    errno_t ret;
-+
-+    ret = tcurl_set_option(tcurl_req, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
-+    if (ret != EOK) {
-+        return ret;
-+    }
-+
-+    ret = tcurl_set_option(tcurl_req, CURLOPT_USERNAME, username);
-+    if (ret != EOK) {
-+        return ret;
-+    }
-+
-+    ret = tcurl_set_option(tcurl_req, CURLOPT_PASSWORD, password);
-+    if (ret != EOK) {
-+        return ret;
-+    }
-+
-+    return EOK;
-+}
-diff --git a/src/util/tev_curl.h b/src/util/tev_curl.h
-index 933abcb9b531412737e8fcf391644d828b125cf8..c733127b3686b5665f53cf53ea72674e0d7af64e 100644
---- a/src/util/tev_curl.h
-+++ b/src/util/tev_curl.h
-@@ -243,4 +243,19 @@ errno_t tcurl_req_set_client_cert(struct tcurl_request *tcurl_req,
-                                   const char *cert,
-                                   const char *key);
- 
-+/**
-+ * @brief Force HTTP basic authentication with @username and @password.
-+ *
-+ * @param[in]  tcurl_request
-+ * @param[in]  username
-+ * @param[in]  password
-+ *
-+ * @returns errno code
-+ *
-+ * @see tcurl_http
-+ */
-+errno_t tcurl_req_http_basic_auth(struct tcurl_request *tcurl_req,
-+                                  const char *username,
-+                                  const char *password);
-+
- #endif /* __TEV_CURL_H */
--- 
-2.9.3
-
diff --git a/SOURCES/0079-CACHE_REQ-Add-plugin-methods-required-for-the-domain.patch b/SOURCES/0079-CACHE_REQ-Add-plugin-methods-required-for-the-domain.patch
new file mode 100644
index 0000000..50c6e3c
--- /dev/null
+++ b/SOURCES/0079-CACHE_REQ-Add-plugin-methods-required-for-the-domain.patch
@@ -0,0 +1,458 @@
+From 4127348220f6b32886fcc1e3f890a2e9fdedf7ed Mon Sep 17 00:00:00 2001
+From: Jakub Hrozek <jhrozek@redhat.com>
+Date: Mon, 30 Oct 2017 20:52:42 +0100
+Subject: [PATCH 79/83] CACHE_REQ: Add plugin methods required for the
+ domain-locator request
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Adds three new cache_req plugin methods:
+    - cache_req_dp_get_domain_check_fn - check if it is time to run the
+                                         locator request again
+    - cache_req_dp_get_domain_send/recv_fn - run the locator itself
+
+The reason we added also the checker is that when the locator runs,
+we add a temporary entry into the negative cache that would denote that
+the locator ran and the ordinary domain negative cache (UID negcache,
+GID negcache, ..) were set for the domains and can be still used to
+skip domains that we know do not contain the account without calling
+the getAccountDomain handler again.
+
+If we didn't have this checker, requesting an entry from a domain
+further down the domain list would always call the locator, only
+to always receive the same results.
+
+Reviewed-by: Pavel Březina <pbrezina@redhat.com>
+Reviewed-by: Sumit Bose <sbose@redhat.com>
+(cherry picked from commit 2856dac5818265a6b4e42d768b73c65e333d14ff)
+---
+ src/responder/common/cache_req/cache_req_plugin.h  | 69 ++++++++++++++++++++++
+ .../cache_req/plugins/cache_req_enum_groups.c      |  5 +-
+ .../common/cache_req/plugins/cache_req_enum_svc.c  |  5 +-
+ .../cache_req/plugins/cache_req_enum_users.c       |  5 +-
+ .../cache_req/plugins/cache_req_group_by_filter.c  |  5 +-
+ .../cache_req/plugins/cache_req_group_by_id.c      |  5 +-
+ .../cache_req/plugins/cache_req_group_by_name.c    |  5 +-
+ .../cache_req/plugins/cache_req_host_by_name.c     |  5 +-
+ .../plugins/cache_req_initgroups_by_name.c         |  5 +-
+ .../plugins/cache_req_initgroups_by_upn.c          |  5 +-
+ .../cache_req/plugins/cache_req_netgroup_by_name.c |  5 +-
+ .../cache_req/plugins/cache_req_object_by_id.c     |  5 +-
+ .../cache_req/plugins/cache_req_object_by_name.c   |  5 +-
+ .../cache_req/plugins/cache_req_object_by_sid.c    |  5 +-
+ .../cache_req/plugins/cache_req_svc_by_name.c      |  5 +-
+ .../cache_req/plugins/cache_req_svc_by_port.c      |  5 +-
+ .../cache_req/plugins/cache_req_user_by_cert.c     |  5 +-
+ .../cache_req/plugins/cache_req_user_by_filter.c   |  5 +-
+ .../cache_req/plugins/cache_req_user_by_id.c       |  5 +-
+ .../cache_req/plugins/cache_req_user_by_name.c     |  5 +-
+ .../cache_req/plugins/cache_req_user_by_upn.c      |  5 +-
+ 21 files changed, 149 insertions(+), 20 deletions(-)
+
+diff --git a/src/responder/common/cache_req/cache_req_plugin.h b/src/responder/common/cache_req/cache_req_plugin.h
+index 8117325506b2951c3966fa50506ed0d55273ee81..803d0f4fa5a54900a458d170177e89f82b398bd9 100644
+--- a/src/responder/common/cache_req/cache_req_plugin.h
++++ b/src/responder/common/cache_req/cache_req_plugin.h
+@@ -153,6 +153,72 @@ typedef bool
+ (*cache_req_dp_recv_fn)(struct tevent_req *subreq,
+                         struct cache_req *cr);
+ 
++/**
++ * Check whether the results of the domain locator can still
++ * be considered valid or whether it is time to call the request
++ * again.
++ *
++ * @param   resp_ctx        The responder context
++ * @param   domain          The domain to check. This should be the domain-head,
++ *                          because the locator works across a domain and its
++ *                          subdomains.
++ * @param   data            The cache req data that contain primarily the key
++ *                          to look for.
++ *
++ * @return True if the locator plugin should be ran again, false if
++ * @return False false ifthe lookup should just proceed with the
++ * data that is already in the negative cache.
++ */
++typedef bool
++(*cache_req_dp_get_domain_check_fn)(struct resp_ctx *rctx,
++                                    struct sss_domain_info *domain,
++                                    struct cache_req_data *data);
++/**
++ * Send Data Provider request to locate the domain
++ * of an entry
++ *
++ * @param   resp_ctx        The responder context
++ * @param   domain          The domain to check. This should be the domain-head,
++ *                          because the locator works across a domain and its
++ *                          subdomains.
++ * @param   data            The cache req data that contain primarily the key
++ *                          to look for.
++ *
++ *
++ * @return Tevent request on success.
++ * @return NULL on error.
++ */
++typedef struct tevent_req *
++(*cache_req_dp_get_domain_send_fn)(TALLOC_CTX *mem_ctx,
++                                   struct resp_ctx *rctx,
++                                   struct sss_domain_info *domain,
++                                   struct cache_req_data *data);
++
++/**
++ * Process result of Data Provider find-domain request.
++ *
++ * Do not free subreq! It will be freed in the caller.
++ *
++ * @param       mem_ctx         The memory context that owns the _found_domain
++ *                              result parameter
++ * @param       subreq          The request to finish
++ * @param       cr              The cache_req being processed
++ * @param       _found_domain   The domain the request account belongs to. This
++ *                              parameter can be NULL even on success, in that
++ *                              case the account was not found and no lookups are
++ *                              needed, all domains can be skipped in this case.
++ *
++ * @return EOK if the request did not encounter any error. In this
++ * case, the _found_domain parameter can be considered authoritative,
++ * regarless of its value
++ * @return errno on error. _found_domain should be NULL in this case.
++ */
++typedef errno_t
++(*cache_req_dp_get_domain_recv_fn)(TALLOC_CTX *mem_ctx,
++                                   struct tevent_req *subreq,
++                                   struct cache_req *cr,
++                                   char **_found_domain);
++
+ struct cache_req_plugin {
+     /**
+      * Plugin name.
+@@ -223,6 +289,9 @@ struct cache_req_plugin {
+     cache_req_lookup_fn lookup_fn;
+     cache_req_dp_send_fn dp_send_fn;
+     cache_req_dp_recv_fn dp_recv_fn;
++    cache_req_dp_get_domain_check_fn dp_get_domain_check_fn;
++    cache_req_dp_get_domain_send_fn dp_get_domain_send_fn;
++    cache_req_dp_get_domain_recv_fn dp_get_domain_recv_fn;
+ };
+ 
+ extern const struct cache_req_plugin cache_req_user_by_name;
+diff --git a/src/responder/common/cache_req/plugins/cache_req_enum_groups.c b/src/responder/common/cache_req/plugins/cache_req_enum_groups.c
+index 15350ca8279bc77c73bcc4abe51c97a8a37cb8c8..d302994e8903dea1e25b3da3762aa2ed783daebd 100644
+--- a/src/responder/common/cache_req/plugins/cache_req_enum_groups.c
++++ b/src/responder/common/cache_req/plugins/cache_req_enum_groups.c
+@@ -86,7 +86,10 @@ const struct cache_req_plugin cache_req_enum_groups = {
+     .ncache_filter_fn = cache_req_enum_groups_ncache_filter,
+     .lookup_fn = cache_req_enum_groups_lookup,
+     .dp_send_fn = cache_req_enum_groups_dp_send,
+-    .dp_recv_fn = cache_req_common_dp_recv
++    .dp_recv_fn = cache_req_common_dp_recv,
++    .dp_get_domain_check_fn = NULL,
++    .dp_get_domain_send_fn = NULL,
++    .dp_get_domain_recv_fn = NULL,
+ };
+ 
+ struct tevent_req *
+diff --git a/src/responder/common/cache_req/plugins/cache_req_enum_svc.c b/src/responder/common/cache_req/plugins/cache_req_enum_svc.c
+index 72b2f1a7d2d2e02ce1a995098d1f26003444bddb..282dc1cc83b1fda91d4c4937f99598fbdd6ec625 100644
+--- a/src/responder/common/cache_req/plugins/cache_req_enum_svc.c
++++ b/src/responder/common/cache_req/plugins/cache_req_enum_svc.c
+@@ -79,7 +79,10 @@ const struct cache_req_plugin cache_req_enum_svc = {
+     .ncache_filter_fn = NULL,
+     .lookup_fn = cache_req_enum_svc_lookup,
+     .dp_send_fn = cache_req_enum_svc_dp_send,
+-    .dp_recv_fn = cache_req_common_dp_recv
++    .dp_recv_fn = cache_req_common_dp_recv,
++    .dp_get_domain_check_fn = NULL,
++    .dp_get_domain_send_fn = NULL,
++    .dp_get_domain_recv_fn = NULL,
+ };
+ 
+ struct tevent_req *
+diff --git a/src/responder/common/cache_req/plugins/cache_req_enum_users.c b/src/responder/common/cache_req/plugins/cache_req_enum_users.c
+index a3ddcdd45548a2fa7c367f3fb3be103c115dedb4..f83ff30fdbbaacdb3bfb605a65ce70fbd8eb3a89 100644
+--- a/src/responder/common/cache_req/plugins/cache_req_enum_users.c
++++ b/src/responder/common/cache_req/plugins/cache_req_enum_users.c
+@@ -86,7 +86,10 @@ const struct cache_req_plugin cache_req_enum_users = {
+     .ncache_filter_fn = cache_req_enum_users_ncache_filter,
+     .lookup_fn = cache_req_enum_users_lookup,
+     .dp_send_fn = cache_req_enum_users_dp_send,
+-    .dp_recv_fn = cache_req_common_dp_recv
++    .dp_recv_fn = cache_req_common_dp_recv,
++    .dp_get_domain_check_fn = NULL,
++    .dp_get_domain_send_fn = NULL,
++    .dp_get_domain_recv_fn = NULL,
+ };
+ 
+ struct tevent_req *
+diff --git a/src/responder/common/cache_req/plugins/cache_req_group_by_filter.c b/src/responder/common/cache_req/plugins/cache_req_group_by_filter.c
+index aa89953b88313605041cce599999fc5bbc741525..009f0f88523c8c4c02a25f0f5d6a83187e0a17f8 100644
+--- a/src/responder/common/cache_req/plugins/cache_req_group_by_filter.c
++++ b/src/responder/common/cache_req/plugins/cache_req_group_by_filter.c
+@@ -134,7 +134,10 @@ const struct cache_req_plugin cache_req_group_by_filter = {
+     .ncache_filter_fn = NULL,
+     .lookup_fn = cache_req_group_by_filter_lookup,
+     .dp_send_fn = cache_req_group_by_filter_dp_send,
+-    .dp_recv_fn = cache_req_common_dp_recv
++    .dp_recv_fn = cache_req_common_dp_recv,
++    .dp_get_domain_check_fn = NULL,
++    .dp_get_domain_send_fn = NULL,
++    .dp_get_domain_recv_fn = NULL,
+ };
+ 
+ struct tevent_req *
+diff --git a/src/responder/common/cache_req/plugins/cache_req_group_by_id.c b/src/responder/common/cache_req/plugins/cache_req_group_by_id.c
+index 5ca64283a781318bc4e4d6920fff989c3f3919b4..70381266712d2c27c95027b54efab201c5df7690 100644
+--- a/src/responder/common/cache_req/plugins/cache_req_group_by_id.c
++++ b/src/responder/common/cache_req/plugins/cache_req_group_by_id.c
+@@ -155,7 +155,10 @@ const struct cache_req_plugin cache_req_group_by_id = {
+     .ncache_filter_fn = cache_req_group_by_id_ncache_filter,
+     .lookup_fn = cache_req_group_by_id_lookup,
+     .dp_send_fn = cache_req_group_by_id_dp_send,
+-    .dp_recv_fn = cache_req_common_dp_recv
++    .dp_recv_fn = cache_req_common_dp_recv,
++    .dp_get_domain_check_fn = NULL,
++    .dp_get_domain_send_fn = NULL,
++    .dp_get_domain_recv_fn = NULL,
+ };
+ 
+ struct tevent_req *
+diff --git a/src/responder/common/cache_req/plugins/cache_req_group_by_name.c b/src/responder/common/cache_req/plugins/cache_req_group_by_name.c
+index 7706051818590af77da75d3e4c7f671c89170f82..3be0d5ea557bad11529b897be1d7706a8809acb1 100644
+--- a/src/responder/common/cache_req/plugins/cache_req_group_by_name.c
++++ b/src/responder/common/cache_req/plugins/cache_req_group_by_name.c
+@@ -197,7 +197,10 @@ const struct cache_req_plugin cache_req_group_by_name = {
+     .ncache_filter_fn = NULL,
+     .lookup_fn = cache_req_group_by_name_lookup,
+     .dp_send_fn = cache_req_group_by_name_dp_send,
+-    .dp_recv_fn = cache_req_common_dp_recv
++    .dp_recv_fn = cache_req_common_dp_recv,
++    .dp_get_domain_check_fn = NULL,
++    .dp_get_domain_send_fn = NULL,
++    .dp_get_domain_recv_fn = NULL,
+ };
+ 
+ struct tevent_req *
+diff --git a/src/responder/common/cache_req/plugins/cache_req_host_by_name.c b/src/responder/common/cache_req/plugins/cache_req_host_by_name.c
+index 56048c5e4bcadfb341f4b42d978d53484abd65d2..696d9e50d94e824d2664ed5a8fe3150b821d570e 100644
+--- a/src/responder/common/cache_req/plugins/cache_req_host_by_name.c
++++ b/src/responder/common/cache_req/plugins/cache_req_host_by_name.c
+@@ -99,7 +99,10 @@ const struct cache_req_plugin cache_req_host_by_name = {
+     .ncache_filter_fn = NULL,
+     .lookup_fn = cache_req_host_by_name_lookup,
+     .dp_send_fn = cache_req_host_by_name_dp_send,
+-    .dp_recv_fn = cache_req_common_dp_recv
++    .dp_recv_fn = cache_req_common_dp_recv,
++    .dp_get_domain_check_fn = NULL,
++    .dp_get_domain_send_fn = NULL,
++    .dp_get_domain_recv_fn = NULL,
+ };
+ 
+ struct tevent_req *
+diff --git a/src/responder/common/cache_req/plugins/cache_req_initgroups_by_name.c b/src/responder/common/cache_req/plugins/cache_req_initgroups_by_name.c
+index 75ac44e1ad36238f01342eced9188d07daa50720..c5bea9d84921cc567bf794d3ba5a57fadb81695b 100644
+--- a/src/responder/common/cache_req/plugins/cache_req_initgroups_by_name.c
++++ b/src/responder/common/cache_req/plugins/cache_req_initgroups_by_name.c
+@@ -212,7 +212,10 @@ const struct cache_req_plugin cache_req_initgroups_by_name = {
+     .ncache_filter_fn = NULL,
+     .lookup_fn = cache_req_initgroups_by_name_lookup,
+     .dp_send_fn = cache_req_initgroups_by_name_dp_send,
+-    .dp_recv_fn = cache_req_common_dp_recv
++    .dp_recv_fn = cache_req_common_dp_recv,
++    .dp_get_domain_check_fn = NULL,
++    .dp_get_domain_send_fn = NULL,
++    .dp_get_domain_recv_fn = NULL,
+ };
+ 
+ struct tevent_req *
+diff --git a/src/responder/common/cache_req/plugins/cache_req_initgroups_by_upn.c b/src/responder/common/cache_req/plugins/cache_req_initgroups_by_upn.c
+index dfb21ac1a0090a3ef9029b38f5b1e8bdda3440c6..9bd00f357c630bae4a52e356577000bd8de94013 100644
+--- a/src/responder/common/cache_req/plugins/cache_req_initgroups_by_upn.c
++++ b/src/responder/common/cache_req/plugins/cache_req_initgroups_by_upn.c
+@@ -123,5 +123,8 @@ const struct cache_req_plugin cache_req_initgroups_by_upn = {
+     .ncache_filter_fn = NULL,
+     .lookup_fn = cache_req_initgroups_by_upn_lookup,
+     .dp_send_fn = cache_req_initgroups_by_upn_dp_send,
+-    .dp_recv_fn = cache_req_common_dp_recv
++    .dp_recv_fn = cache_req_common_dp_recv,
++    .dp_get_domain_check_fn = NULL,
++    .dp_get_domain_send_fn = NULL,
++    .dp_get_domain_recv_fn = NULL,
+ };
+diff --git a/src/responder/common/cache_req/plugins/cache_req_netgroup_by_name.c b/src/responder/common/cache_req/plugins/cache_req_netgroup_by_name.c
+index ef0775d0b8eac4d679450f436d8427cff9c04582..d370d342ec5b2c0e0e9f1f4ea90b34b59bff60b6 100644
+--- a/src/responder/common/cache_req/plugins/cache_req_netgroup_by_name.c
++++ b/src/responder/common/cache_req/plugins/cache_req_netgroup_by_name.c
+@@ -131,7 +131,10 @@ const struct cache_req_plugin cache_req_netgroup_by_name = {
+     .ncache_filter_fn = NULL,
+     .lookup_fn = cache_req_netgroup_by_name_lookup,
+     .dp_send_fn = cache_req_netgroup_by_name_dp_send,
+-    .dp_recv_fn = cache_req_common_dp_recv
++    .dp_recv_fn = cache_req_common_dp_recv,
++    .dp_get_domain_check_fn = NULL,
++    .dp_get_domain_send_fn = NULL,
++    .dp_get_domain_recv_fn = NULL,
+ };
+ 
+ struct tevent_req *
+diff --git a/src/responder/common/cache_req/plugins/cache_req_object_by_id.c b/src/responder/common/cache_req/plugins/cache_req_object_by_id.c
+index 339bd4f5fef827acc1aa3c123d041e426d9e4782..2af95313cb2df0f46a61519ac962074033f34a12 100644
+--- a/src/responder/common/cache_req/plugins/cache_req_object_by_id.c
++++ b/src/responder/common/cache_req/plugins/cache_req_object_by_id.c
+@@ -129,7 +129,10 @@ const struct cache_req_plugin cache_req_object_by_id = {
+     .ncache_filter_fn = cache_req_object_by_id_ncache_filter,
+     .lookup_fn = cache_req_object_by_id_lookup,
+     .dp_send_fn = cache_req_object_by_id_dp_send,
+-    .dp_recv_fn = cache_req_common_dp_recv
++    .dp_recv_fn = cache_req_common_dp_recv,
++    .dp_get_domain_check_fn = NULL,
++    .dp_get_domain_send_fn = NULL,
++    .dp_get_domain_recv_fn = NULL,
+ };
+ 
+ struct tevent_req *
+diff --git a/src/responder/common/cache_req/plugins/cache_req_object_by_name.c b/src/responder/common/cache_req/plugins/cache_req_object_by_name.c
+index 854d0b83c420ebebcb5e0e079c707081fa313632..a740fbb8d05efb4601e8e40d2a07896ecb251d4e 100644
+--- a/src/responder/common/cache_req/plugins/cache_req_object_by_name.c
++++ b/src/responder/common/cache_req/plugins/cache_req_object_by_name.c
+@@ -207,7 +207,10 @@ const struct cache_req_plugin cache_req_object_by_name = {
+     .ncache_filter_fn = NULL,
+     .lookup_fn = cache_req_object_by_name_lookup,
+     .dp_send_fn = cache_req_object_by_name_dp_send,
+-    .dp_recv_fn = cache_req_common_dp_recv
++    .dp_recv_fn = cache_req_common_dp_recv,
++    .dp_get_domain_check_fn = NULL,
++    .dp_get_domain_send_fn = NULL,
++    .dp_get_domain_recv_fn = NULL,
+ };
+ 
+ struct tevent_req *
+diff --git a/src/responder/common/cache_req/plugins/cache_req_object_by_sid.c b/src/responder/common/cache_req/plugins/cache_req_object_by_sid.c
+index 039a79df7bb1ab213ce4334835e9fc18e6d0faac..1af638ff9d94ffa3bf6e418433d5c4e98acfb2b8 100644
+--- a/src/responder/common/cache_req/plugins/cache_req_object_by_sid.c
++++ b/src/responder/common/cache_req/plugins/cache_req_object_by_sid.c
+@@ -123,7 +123,10 @@ const struct cache_req_plugin cache_req_object_by_sid = {
+     .ncache_filter_fn = NULL,
+     .lookup_fn = cache_req_object_by_sid_lookup,
+     .dp_send_fn = cache_req_object_by_sid_dp_send,
+-    .dp_recv_fn = cache_req_common_dp_recv
++    .dp_recv_fn = cache_req_common_dp_recv,
++    .dp_get_domain_check_fn = NULL,
++    .dp_get_domain_send_fn = NULL,
++    .dp_get_domain_recv_fn = NULL,
+ };
+ 
+ struct tevent_req *
+diff --git a/src/responder/common/cache_req/plugins/cache_req_svc_by_name.c b/src/responder/common/cache_req/plugins/cache_req_svc_by_name.c
+index 4c32d9977cc06e43eed3a90e7dcf107e91efefb5..5b17051031e35c5767d27b19c922325cee4b6eac 100644
+--- a/src/responder/common/cache_req/plugins/cache_req_svc_by_name.c
++++ b/src/responder/common/cache_req/plugins/cache_req_svc_by_name.c
+@@ -155,7 +155,10 @@ const struct cache_req_plugin cache_req_svc_by_name = {
+     .ncache_filter_fn = NULL,
+     .lookup_fn = cache_req_svc_by_name_lookup,
+     .dp_send_fn = cache_req_svc_by_name_dp_send,
+-    .dp_recv_fn = cache_req_common_dp_recv
++    .dp_recv_fn = cache_req_common_dp_recv,
++    .dp_get_domain_check_fn = NULL,
++    .dp_get_domain_send_fn = NULL,
++    .dp_get_domain_recv_fn = NULL,
+ };
+ 
+ struct tevent_req *
+diff --git a/src/responder/common/cache_req/plugins/cache_req_svc_by_port.c b/src/responder/common/cache_req/plugins/cache_req_svc_by_port.c
+index 1e998f642c766d15d3f6fe777aa5c789629508e2..4c005df3972386fef3c5a858a2b691cb2a63fd57 100644
+--- a/src/responder/common/cache_req/plugins/cache_req_svc_by_port.c
++++ b/src/responder/common/cache_req/plugins/cache_req_svc_by_port.c
+@@ -128,7 +128,10 @@ const struct cache_req_plugin cache_req_svc_by_port = {
+     .ncache_filter_fn = NULL,
+     .lookup_fn = cache_req_svc_by_port_lookup,
+     .dp_send_fn = cache_req_svc_by_port_dp_send,
+-    .dp_recv_fn = cache_req_common_dp_recv
++    .dp_recv_fn = cache_req_common_dp_recv,
++    .dp_get_domain_check_fn = NULL,
++    .dp_get_domain_send_fn = NULL,
++    .dp_get_domain_recv_fn = NULL,
+ };
+ 
+ struct tevent_req *
+diff --git a/src/responder/common/cache_req/plugins/cache_req_user_by_cert.c b/src/responder/common/cache_req/plugins/cache_req_user_by_cert.c
+index 7a0c7d8ce1644f1c41b64c6903e4e20eb3c2c081..a2dc1fad28ca09eeba77c563f17518671095ab42 100644
+--- a/src/responder/common/cache_req/plugins/cache_req_user_by_cert.c
++++ b/src/responder/common/cache_req/plugins/cache_req_user_by_cert.c
+@@ -97,7 +97,10 @@ const struct cache_req_plugin cache_req_user_by_cert = {
+     .ncache_filter_fn = NULL,
+     .lookup_fn = cache_req_user_by_cert_lookup,
+     .dp_send_fn = cache_req_user_by_cert_dp_send,
+-    .dp_recv_fn = cache_req_common_dp_recv
++    .dp_recv_fn = cache_req_common_dp_recv,
++    .dp_get_domain_check_fn = NULL,
++    .dp_get_domain_send_fn = NULL,
++    .dp_get_domain_recv_fn = NULL,
+ };
+ 
+ struct tevent_req *
+diff --git a/src/responder/common/cache_req/plugins/cache_req_user_by_filter.c b/src/responder/common/cache_req/plugins/cache_req_user_by_filter.c
+index dd3f42e855389ecc73690e4d18c4977253b108a6..42b6e816372c51623f29e8a7e28859a9dfca640f 100644
+--- a/src/responder/common/cache_req/plugins/cache_req_user_by_filter.c
++++ b/src/responder/common/cache_req/plugins/cache_req_user_by_filter.c
+@@ -134,7 +134,10 @@ const struct cache_req_plugin cache_req_user_by_filter = {
+     .ncache_filter_fn = NULL,
+     .lookup_fn = cache_req_user_by_filter_lookup,
+     .dp_send_fn = cache_req_user_by_filter_dp_send,
+-    .dp_recv_fn = cache_req_common_dp_recv
++    .dp_recv_fn = cache_req_common_dp_recv,
++    .dp_get_domain_check_fn = NULL,
++    .dp_get_domain_send_fn = NULL,
++    .dp_get_domain_recv_fn = NULL,
+ };
+ 
+ struct tevent_req *
+diff --git a/src/responder/common/cache_req/plugins/cache_req_user_by_id.c b/src/responder/common/cache_req/plugins/cache_req_user_by_id.c
+index 913f9be5bcc2dfd074b52cb3b15fb6948826e831..254330e92cc801b84bfb5e308d6d90ac54507d77 100644
+--- a/src/responder/common/cache_req/plugins/cache_req_user_by_id.c
++++ b/src/responder/common/cache_req/plugins/cache_req_user_by_id.c
+@@ -155,7 +155,10 @@ const struct cache_req_plugin cache_req_user_by_id = {
+     .ncache_filter_fn = cache_req_user_by_id_ncache_filter,
+     .lookup_fn = cache_req_user_by_id_lookup,
+     .dp_send_fn = cache_req_user_by_id_dp_send,
+-    .dp_recv_fn = cache_req_common_dp_recv
++    .dp_recv_fn = cache_req_common_dp_recv,
++    .dp_get_domain_check_fn = NULL,
++    .dp_get_domain_send_fn = NULL,
++    .dp_get_domain_recv_fn = NULL,
+ };
+ 
+ struct tevent_req *
+diff --git a/src/responder/common/cache_req/plugins/cache_req_user_by_name.c b/src/responder/common/cache_req/plugins/cache_req_user_by_name.c
+index 2e49de938d0af50089d0cf49860441c2b6ea679c..d24a2221b2a69d24d360c46c41073e19dc79036b 100644
+--- a/src/responder/common/cache_req/plugins/cache_req_user_by_name.c
++++ b/src/responder/common/cache_req/plugins/cache_req_user_by_name.c
+@@ -202,7 +202,10 @@ const struct cache_req_plugin cache_req_user_by_name = {
+     .ncache_filter_fn = NULL,
+     .lookup_fn = cache_req_user_by_name_lookup,
+     .dp_send_fn = cache_req_user_by_name_dp_send,
+-    .dp_recv_fn = cache_req_common_dp_recv
++    .dp_recv_fn = cache_req_common_dp_recv,
++    .dp_get_domain_check_fn = NULL,
++    .dp_get_domain_send_fn = NULL,
++    .dp_get_domain_recv_fn = NULL,
+ };
+ 
+ struct tevent_req *
+diff --git a/src/responder/common/cache_req/plugins/cache_req_user_by_upn.c b/src/responder/common/cache_req/plugins/cache_req_user_by_upn.c
+index b8bcd241ed79c510aca214ad3788215ae2997d20..e08ab70ae081a5d532d7ab436687978416e7c493 100644
+--- a/src/responder/common/cache_req/plugins/cache_req_user_by_upn.c
++++ b/src/responder/common/cache_req/plugins/cache_req_user_by_upn.c
+@@ -128,5 +128,8 @@ const struct cache_req_plugin cache_req_user_by_upn = {
+     .ncache_filter_fn = NULL,
+     .lookup_fn = cache_req_user_by_upn_lookup,
+     .dp_send_fn = cache_req_user_by_upn_dp_send,
+-    .dp_recv_fn = cache_req_common_dp_recv
++    .dp_recv_fn = cache_req_common_dp_recv,
++    .dp_get_domain_check_fn = NULL,
++    .dp_get_domain_send_fn = NULL,
++    .dp_get_domain_recv_fn = NULL,
+ };
+-- 
+2.14.3
+
diff --git a/SOURCES/0079-tcurl-test-allow-to-set-custom-headers.patch b/SOURCES/0079-tcurl-test-allow-to-set-custom-headers.patch
deleted file mode 100644
index ac764da..0000000
--- a/SOURCES/0079-tcurl-test-allow-to-set-custom-headers.patch
+++ /dev/null
@@ -1,63 +0,0 @@
-From 8047207b470ea417b11919e84931a751485d2326 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Pavel=20B=C5=99ezina?= <pbrezina@redhat.com>
-Date: Fri, 10 Mar 2017 12:11:12 +0100
-Subject: [PATCH 79/90] tcurl test: allow to set custom headers
-
-Reviewed-by: Simo Sorce <simo@redhat.com>
-Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
-(cherry picked from commit d1ed11fc50922aab2332758a9300f3fbf814f112)
----
- src/tests/tcurl_test_tool.c | 11 +++++++++--
- 1 file changed, 9 insertions(+), 2 deletions(-)
-
-diff --git a/src/tests/tcurl_test_tool.c b/src/tests/tcurl_test_tool.c
-index 4ceef8e06040ea0abd4d112a5b7845f436c69488..63a3e26b561781795873c2a4d72ac071a4da9939 100644
---- a/src/tests/tcurl_test_tool.c
-+++ b/src/tests/tcurl_test_tool.c
-@@ -40,6 +40,7 @@ struct tool_options {
-     int tls;
-     int verify_peer;
-     int verify_host;
-+    const char **headers;
- 
-     enum tcurl_http_method method;
-     const char *socket_path;
-@@ -121,13 +122,14 @@ prepare_requests(TALLOC_CTX *mem_ctx,
-                  size_t *_num_requests)
- {
-     struct tcurl_request **requests;
-+    struct sss_iobuf *body;
-+    const char **headers;
-     const char *arg;
-     const char *url;
--    struct sss_iobuf *body;
-     errno_t ret;
-     size_t i;
- 
--    static const char *headers[] = {
-+    static const char *default_headers[] = {
-         "Content-type: application/octet-stream",
-         NULL,
-     };
-@@ -137,6 +139,8 @@ prepare_requests(TALLOC_CTX *mem_ctx,
-         return ENOMEM;
-     }
- 
-+    headers = opts->headers == NULL ? default_headers : opts->headers;
-+
-     i = 0;
-     while ((arg = poptGetArg(pc)) != NULL) {
-         if (i >= MAXREQ) {
-@@ -302,6 +306,9 @@ int main(int argc, const char *argv[])
-         { "put", 'p', POPT_ARG_NONE, NULL, 'p', "Perform a HTTP PUT", NULL },
-         { "post", 'o', POPT_ARG_NONE, NULL, 'o', "Perform a HTTP POST", NULL },
-         { "del", 'd', POPT_ARG_NONE, NULL, 'd', "Perform a HTTP DELETE", NULL },
-+#ifdef POPT_ARG_ARGV
-+        { "header", 'h', POPT_ARG_ARGV, &opts.headers, '\0', "Add HTTP header", NULL },
-+#endif
-         { "raw", 'r', POPT_ARG_NONE, &opts.raw, '\0', "Print raw protocol output", NULL },
-         { "verbose", 'v', POPT_ARG_NONE, &opts.verbose, '\0', "Print response code and body", NULL },
-         /* TLS */
--- 
-2.9.3
-
diff --git a/SOURCES/0080-CACHE_REQ-Add-a-private-request-cache_req_locate_dom.patch b/SOURCES/0080-CACHE_REQ-Add-a-private-request-cache_req_locate_dom.patch
new file mode 100644
index 0000000..f00f399
--- /dev/null
+++ b/SOURCES/0080-CACHE_REQ-Add-a-private-request-cache_req_locate_dom.patch
@@ -0,0 +1,177 @@
+From d30dd0f52d452562e47f9a30b1630eff2f817792 Mon Sep 17 00:00:00 2001
+From: Jakub Hrozek <jhrozek@redhat.com>
+Date: Mon, 30 Oct 2017 20:21:05 +0100
+Subject: [PATCH 80/83] CACHE_REQ: Add a private request
+ cache_req_locate_domain()
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Adds a new request cache_req_locate_domain_send/recv. This request, if the
+plugin that is being processed supports the locator, will call the plugin's
+dp_get_domain_send_fn(). On any error, the request returns just the error
+code. On success, the request returns the domain the object was found at.
+
+If the getAccountDomain() method returns that the back end does not support
+the locator method, all further getAccountDomain() calls are disabled for
+that domain.
+
+Related:
+https://pagure.io/SSSD/sssd/issue/3468
+
+Reviewed-by: Pavel Březina <pbrezina@redhat.com>
+Reviewed-by: Sumit Bose <sbose@redhat.com>
+(cherry picked from commit 0a0b34f5fbe8f4a8c533a7d65f0f2961ee264054)
+---
+ src/responder/common/cache_req/cache_req_private.h |  7 ++
+ src/responder/common/cache_req/cache_req_search.c  | 93 ++++++++++++++++++++++
+ src/util/util_errors.c                             |  1 +
+ src/util/util_errors.h                             |  1 +
+ 4 files changed, 102 insertions(+)
+
+diff --git a/src/responder/common/cache_req/cache_req_private.h b/src/responder/common/cache_req/cache_req_private.h
+index a156fc65fed80693cdd0473613aeaaa3f5bb2269..9586e3788045ff44eb2a4b626dc7fcaf11ec8028 100644
+--- a/src/responder/common/cache_req/cache_req_private.h
++++ b/src/responder/common/cache_req/cache_req_private.h
+@@ -106,6 +106,13 @@ errno_t cache_req_search_recv(TALLOC_CTX *mem_ctx,
+                               struct ldb_result **_result,
+                               bool *_dp_success);
+ 
++struct tevent_req *cache_req_locate_domain_send(TALLOC_CTX *mem_ctx,
++                                                struct tevent_context *ev,
++                                                struct cache_req *cr);
++errno_t cache_req_locate_domain_recv(TALLOC_CTX *mem_ctx,
++                                     struct tevent_req *req,
++                                     char **_found_domain);
++
+ struct tevent_req *
+ cache_req_steal_data_and_send(TALLOC_CTX *mem_ctx,
+                               struct tevent_context *ev,
+diff --git a/src/responder/common/cache_req/cache_req_search.c b/src/responder/common/cache_req/cache_req_search.c
+index 9d5ad8056cda0284b1cc32cd51d7cb0ec12ad667..3365962d473b0982945de2541e44ba86b43a0db5 100644
+--- a/src/responder/common/cache_req/cache_req_search.c
++++ b/src/responder/common/cache_req/cache_req_search.c
+@@ -485,3 +485,96 @@ errno_t cache_req_search_recv(TALLOC_CTX *mem_ctx,
+ 
+     return EOK;
+ }
++
++struct cache_req_locate_domain_state {
++    struct cache_req *cr;
++
++    char *found_domain;
++};
++
++static void cache_req_locate_domain_done(struct tevent_req *subreq);
++
++struct tevent_req *cache_req_locate_domain_send(TALLOC_CTX *mem_ctx,
++                                                struct tevent_context *ev,
++                                                struct cache_req *cr)
++{
++    struct cache_req_locate_domain_state *state;
++    struct tevent_req *req;
++    struct tevent_req *subreq;
++    errno_t ret;
++    bool should_run;
++
++    req = tevent_req_create(mem_ctx, &state, struct cache_req_locate_domain_state);
++    if (req == NULL) {
++        DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create() failed\n");
++        return NULL;
++    }
++    state->cr = cr;
++
++    should_run = cr->plugin->dp_get_domain_check_fn(cr->rctx,
++                                                    get_domains_head(cr->domain),
++                                                    cr->data);
++    if (should_run == false) {
++        /* The request was tried too recently, don't issue a new one
++         * as its results are still valid
++         */
++        ret = ERR_GET_ACCT_DOM_CACHED;
++        goto immediate;
++    }
++
++    subreq = cr->plugin->dp_get_domain_send_fn(state,
++                                               cr->rctx,
++                                               get_domains_head(cr->domain),
++                                               cr->data);
++    if (subreq == NULL) {
++        ret = ENOMEM;
++        goto immediate;
++    }
++    tevent_req_set_callback(subreq, cache_req_locate_domain_done, req);
++    return req;
++
++immediate:
++    if (ret == EOK) {
++        tevent_req_done(req);
++    } else {
++        tevent_req_error(req, ret);
++    }
++    tevent_req_post(req, ev);
++    return req;
++}
++
++static void cache_req_locate_domain_done(struct tevent_req *subreq)
++{
++    struct tevent_req *req;
++    struct cache_req_locate_domain_state *state;
++    errno_t ret;
++
++    req = tevent_req_callback_data(subreq, struct tevent_req);
++    state = tevent_req_data(req, struct cache_req_locate_domain_state);
++
++    ret = state->cr->plugin->dp_get_domain_recv_fn(state,
++                                                   subreq,
++                                                   state->cr,
++                                                   &state->found_domain);
++    talloc_zfree(subreq);
++    if (ret != EOK) {
++        tevent_req_error(req, ret);
++        return;
++    }
++
++    tevent_req_done(req);
++}
++
++errno_t cache_req_locate_domain_recv(TALLOC_CTX *mem_ctx,
++                                     struct tevent_req *req,
++                                     char **_found_domain)
++{
++    struct cache_req_locate_domain_state *state = NULL;
++
++    state = tevent_req_data(req, struct cache_req_locate_domain_state);
++
++    TEVENT_REQ_RETURN_ON_ERROR(req);
++
++    *_found_domain = talloc_steal(mem_ctx, state->found_domain);
++    return EOK;
++}
+diff --git a/src/util/util_errors.c b/src/util/util_errors.c
+index 9a9ba3f3063cab4afb538c3a58527a2d2ed3fffd..06c620b40aaa00d6ce58ace3a28449ffbdf8da88 100644
+--- a/src/util/util_errors.c
++++ b/src/util/util_errors.c
+@@ -116,6 +116,7 @@ struct err_string error_to_str[] = {
+     { "Unable to verify peer" }, /* ERR_UNABLE_TO_VERIFY_PEER */
+     { "Unable to resolve host" }, /* ERR_UNABLE_TO_RESOLVE_HOST */
+     { "GetAccountDomain() not supported" }, /* ERR_GET_ACCT_DOM_NOT_SUPPORTED */
++    { "The last GetAccountDomain() result is still valid" }, /* ERR_GET_ACCT_DOM_CACHED */
+     { "ERR_LAST" } /* ERR_LAST */
+ };
+ 
+diff --git a/src/util/util_errors.h b/src/util/util_errors.h
+index 5ee9862c3f2f60c078693b1b85a40f15436e818c..bebd6e198fc0077891a602f80182a993ce3f789b 100644
+--- a/src/util/util_errors.h
++++ b/src/util/util_errors.h
+@@ -138,6 +138,7 @@ enum sssd_errors {
+     ERR_UNABLE_TO_VERIFY_PEER,
+     ERR_UNABLE_TO_RESOLVE_HOST,
+     ERR_GET_ACCT_DOM_NOT_SUPPORTED,
++    ERR_GET_ACCT_DOM_CACHED,
+     ERR_LAST            /* ALWAYS LAST */
+ };
+ 
+-- 
+2.14.3
+
diff --git a/SOURCES/0080-tcurl-test-add-support-for-client-certificate.patch b/SOURCES/0080-tcurl-test-add-support-for-client-certificate.patch
deleted file mode 100644
index 8635100..0000000
--- a/SOURCES/0080-tcurl-test-add-support-for-client-certificate.patch
+++ /dev/null
@@ -1,53 +0,0 @@
-From f63d4b3749fd76796a26f1c1f07bdddcb681a768 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Pavel=20B=C5=99ezina?= <pbrezina@redhat.com>
-Date: Mon, 13 Mar 2017 13:30:48 +0100
-Subject: [PATCH 80/90] tcurl test: add support for client certificate
-
-Reviewed-by: Simo Sorce <simo@redhat.com>
-Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
-(cherry picked from commit ae6b11229d9961e26922918183c7c1de7780b8d6)
----
- src/tests/tcurl_test_tool.c | 13 +++++++++++++
- 1 file changed, 13 insertions(+)
-
-diff --git a/src/tests/tcurl_test_tool.c b/src/tests/tcurl_test_tool.c
-index 63a3e26b561781795873c2a4d72ac071a4da9939..fbc2790357b131ebb21b4be041688e5f699d73e7 100644
---- a/src/tests/tcurl_test_tool.c
-+++ b/src/tests/tcurl_test_tool.c
-@@ -47,6 +47,9 @@ struct tool_options {
-     const char *capath;
-     const char *cacert;
- 
-+    const char *clientcert;
-+    const char *clientkey;
-+
-     const char *username;
-     const char *password;
- };
-@@ -201,6 +204,14 @@ prepare_requests(TALLOC_CTX *mem_ctx,
-             }
-         }
- 
-+        if (opts->clientcert != NULL) {
-+            ret = tcurl_req_set_client_cert(requests[i], opts->clientcert,
-+                                            opts->clientkey);
-+            if (ret != EOK) {
-+                goto done;
-+            }
-+        }
-+
-         if (opts->username != NULL && opts->password != NULL) {
-             ret = tcurl_req_http_basic_auth(requests[i], opts->username,
-                                             opts->password);
-@@ -317,6 +328,8 @@ int main(int argc, const char *argv[])
-         { "verify-host", '\0', POPT_ARG_NONE, &opts.verify_host, '\0', "Verify host when TLS is enabled", NULL },
-         { "capath", '\0', POPT_ARG_STRING, &opts.capath, '\0', "Path to CA directory where peer certificate is stored", NULL },
-         { "cacert", '\0', POPT_ARG_STRING, &opts.cacert, '\0', "Path to CA certificate", NULL },
-+        { "clientcert", '\0', POPT_ARG_STRING, &opts.clientcert, '\0', "Path to client's certificate", NULL },
-+        { "clientkey", '\0', POPT_ARG_STRING, &opts.clientkey, '\0', "Path to client's private key", NULL },
-         /* BASIC AUTH */
-         { "username", '\0', POPT_ARG_STRING, &opts.username, '\0', "Username for basic authentication", NULL },
-         { "password", '\0', POPT_ARG_STRING, &opts.password, '\0', "Password for basic authentication", NULL },
--- 
-2.9.3
-
diff --git a/SOURCES/0081-CACHE_REQ-Implement-the-plugin-methods-that-utilize-.patch b/SOURCES/0081-CACHE_REQ-Implement-the-plugin-methods-that-utilize-.patch
new file mode 100644
index 0000000..6c0f694
--- /dev/null
+++ b/SOURCES/0081-CACHE_REQ-Implement-the-plugin-methods-that-utilize-.patch
@@ -0,0 +1,413 @@
+From 7482c6affd4dfa77a8d465ff0283617792847725 Mon Sep 17 00:00:00 2001
+From: Jakub Hrozek <jhrozek@redhat.com>
+Date: Mon, 6 Nov 2017 15:52:11 +0100
+Subject: [PATCH 81/83] CACHE_REQ: Implement the plugin methods that utilize
+ the domain locator API
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Mainly, this patch adds handlers for the dp_get_domain_check_fn(),
+dp_get_domain_send_fn() and dp_get_domain_recv_fn() functions to
+requests that resolve objects by ID.
+
+This patch also adds domain-local negcache setter for by-id methods
+Previously, the by-ID methods only used global negative cache setters
+because the ID space is global and we always iterated over all domains.
+
+However, with addition of the domain locator plugin, we want also
+to skip only certain domains and the easiest way to to so is to add
+the IDs for domains that do not contain these IDs to the negative cache
+with the get-account-domain request.
+
+Therefore this patch also adds per-domain negative cache setters for
+the three plugins that search by ID.
+
+Reviewed-by: Pavel Březina <pbrezina@redhat.com>
+Reviewed-by: Sumit Bose <sbose@redhat.com>
+(cherry picked from commit a6eb9c4c3ff68d134bc745e8374f182737e9696b)
+---
+ src/responder/common/cache_req/cache_req_private.h |  5 ++
+ .../common/cache_req/plugins/cache_req_common.c    | 17 +++++
+ .../cache_req/plugins/cache_req_group_by_id.c      | 62 +++++++++++++++--
+ .../cache_req/plugins/cache_req_object_by_id.c     | 77 ++++++++++++++++++++--
+ .../cache_req/plugins/cache_req_user_by_id.c       | 63 ++++++++++++++++--
+ src/tests/cmocka/common_mock_resp_dp.c             | 23 +++++++
+ 6 files changed, 235 insertions(+), 12 deletions(-)
+
+diff --git a/src/responder/common/cache_req/cache_req_private.h b/src/responder/common/cache_req/cache_req_private.h
+index 9586e3788045ff44eb2a4b626dc7fcaf11ec8028..95f24c0e5b9ab1150591d308c7288c57fe478c5d 100644
+--- a/src/responder/common/cache_req/cache_req_private.h
++++ b/src/responder/common/cache_req/cache_req_private.h
+@@ -187,4 +187,9 @@ bool
+ cache_req_common_dp_recv(struct tevent_req *subreq,
+                          struct cache_req *cr);
+ 
++errno_t
++cache_reg_common_get_acct_domain_recv(TALLOC_CTX *mem_ctx,
++                                      struct tevent_req *subreq,
++                                      struct cache_req *cr,
++                                      char **_domain);
+ #endif /* _CACHE_REQ_PRIVATE_H_ */
+diff --git a/src/responder/common/cache_req/plugins/cache_req_common.c b/src/responder/common/cache_req/plugins/cache_req_common.c
+index 1f86258bc14c7a382712959f24a4ec4c153572d4..408c91949ceb3ecaf743f270f58f4e3fcfc3ccb1 100644
+--- a/src/responder/common/cache_req/plugins/cache_req_common.c
++++ b/src/responder/common/cache_req/plugins/cache_req_common.c
+@@ -147,3 +147,20 @@ done:
+     talloc_free(err_msg);
+     return bret;
+ }
++
++errno_t
++cache_reg_common_get_acct_domain_recv(TALLOC_CTX *mem_ctx,
++                                      struct tevent_req *subreq,
++                                      struct cache_req *cr,
++                                      char **_domain)
++{
++    errno_t ret;
++
++    ret = sss_dp_get_account_domain_recv(mem_ctx, subreq, _domain);
++    if (ret != EOK) {
++        CACHE_REQ_DEBUG(SSSDBG_MINOR_FAILURE, cr,
++                        "Could not get account domain [%d]: %s\n",
++                        ret, sss_strerror(ret));
++    }
++    return ret;
++}
+diff --git a/src/responder/common/cache_req/plugins/cache_req_group_by_id.c b/src/responder/common/cache_req/plugins/cache_req_group_by_id.c
+index 70381266712d2c27c95027b54efab201c5df7690..ce84b1b4458b447ff6b4b036c6e8fe8f4d7758c8 100644
+--- a/src/responder/common/cache_req/plugins/cache_req_group_by_id.c
++++ b/src/responder/common/cache_req/plugins/cache_req_group_by_id.c
+@@ -39,6 +39,15 @@ cache_req_group_by_id_ncache_check(struct sss_nc_ctx *ncache,
+                                    struct sss_domain_info *domain,
+                                    struct cache_req_data *data)
+ {
++    errno_t ret;
++
++    if (domain != NULL) {
++        ret = sss_ncache_check_gid(ncache, domain, data->id);
++        if (ret == EEXIST) {
++            return ret;
++        }
++    }
++
+     return sss_ncache_check_gid(ncache, NULL, data->id);
+ }
+ 
+@@ -57,6 +66,14 @@ cache_req_group_by_id_global_ncache_add(struct sss_nc_ctx *ncache,
+     return sss_ncache_set_gid(ncache, false, NULL, data->id);
+ }
+ 
++static errno_t
++cache_req_group_by_id_ncache_add(struct sss_nc_ctx *ncache,
++                                 struct sss_domain_info *domain,
++                                 struct cache_req_data *data)
++{
++    return sss_ncache_set_gid(ncache, false, domain, data->id);
++}
++
+ static errno_t
+ cache_req_group_by_id_lookup(TALLOC_CTX *mem_ctx,
+                              struct cache_req *cr,
+@@ -132,6 +149,43 @@ cache_req_group_by_id_dp_send(TALLOC_CTX *mem_ctx,
+                                    SSS_DP_GROUP, string, id, flag);
+ }
+ 
++static bool
++cache_req_group_by_id_get_domain_check(struct resp_ctx *rctx,
++                                       struct sss_domain_info *domain,
++                                       struct cache_req_data *data)
++{
++    int nret;
++
++    nret = sss_ncache_check_locate_gid(rctx->ncache, domain, data->id);
++    if (nret == EEXIST) {
++        return false;
++    }
++
++    return true;
++}
++
++static struct tevent_req *
++cache_req_group_by_id_get_domain_send(TALLOC_CTX *mem_ctx,
++                                      struct resp_ctx *rctx,
++                                      struct sss_domain_info *domain,
++                                      struct cache_req_data *data)
++{
++    int nret;
++
++    nret = sss_ncache_set_locate_gid(rctx->ncache, domain, data->id);
++    if (nret != EOK) {
++        DEBUG(SSSDBG_MINOR_FAILURE,
++              "Cannot set negative cache, this might result in performance degradation\n");
++        /* Not fatal */
++    }
++
++    return sss_dp_get_account_domain_send(mem_ctx,
++                                          rctx,
++                                          domain,
++                                          SSS_DP_GROUP,
++                                          data->id);
++}
++
+ const struct cache_req_plugin cache_req_group_by_id = {
+     .name = "Group by ID",
+     .attr_expiration = SYSDB_CACHE_EXPIRE,
+@@ -151,14 +205,14 @@ const struct cache_req_plugin cache_req_group_by_id = {
+     .create_debug_name_fn = cache_req_group_by_id_create_debug_name,
+     .global_ncache_add_fn = cache_req_group_by_id_global_ncache_add,
+     .ncache_check_fn = cache_req_group_by_id_ncache_check,
+-    .ncache_add_fn = NULL,
++    .ncache_add_fn = cache_req_group_by_id_ncache_add,
+     .ncache_filter_fn = cache_req_group_by_id_ncache_filter,
+     .lookup_fn = cache_req_group_by_id_lookup,
+     .dp_send_fn = cache_req_group_by_id_dp_send,
+     .dp_recv_fn = cache_req_common_dp_recv,
+-    .dp_get_domain_check_fn = NULL,
+-    .dp_get_domain_send_fn = NULL,
+-    .dp_get_domain_recv_fn = NULL,
++    .dp_get_domain_check_fn = cache_req_group_by_id_get_domain_check,
++    .dp_get_domain_send_fn = cache_req_group_by_id_get_domain_send,
++    .dp_get_domain_recv_fn = cache_reg_common_get_acct_domain_recv,
+ };
+ 
+ struct tevent_req *
+diff --git a/src/responder/common/cache_req/plugins/cache_req_object_by_id.c b/src/responder/common/cache_req/plugins/cache_req_object_by_id.c
+index 2af95313cb2df0f46a61519ac962074033f34a12..1327b480c1b1b68f9826fa229c9b001f2d92b79b 100644
+--- a/src/responder/common/cache_req/plugins/cache_req_object_by_id.c
++++ b/src/responder/common/cache_req/plugins/cache_req_object_by_id.c
+@@ -83,6 +83,26 @@ cache_req_object_by_id_global_ncache_add(struct sss_nc_ctx *ncache,
+     return EOK;
+ }
+ 
++static errno_t
++cache_req_object_by_id_ncache_add(struct sss_nc_ctx *ncache,
++                                  struct sss_domain_info *domain,
++                                  struct cache_req_data *data)
++{
++    errno_t ret;
++
++    ret = sss_ncache_set_uid(ncache, false, domain, data->id);
++    if (ret != EOK) {
++        return ret;
++    }
++
++    ret = sss_ncache_set_gid(ncache, false, domain, data->id);
++    if (ret != EOK) {
++        return ret;
++    }
++
++    return EOK;
++}
++
+ static errno_t
+ cache_req_object_by_id_lookup(TALLOC_CTX *mem_ctx,
+                               struct cache_req *cr,
+@@ -106,6 +126,55 @@ cache_req_object_by_id_dp_send(TALLOC_CTX *mem_ctx,
+                                    cr->data->id, NULL);
+ }
+ 
++static bool
++cache_req_object_by_id_get_domain_check(struct resp_ctx *rctx,
++                                        struct sss_domain_info *domain,
++                                        struct cache_req_data *data)
++{
++    int nret;
++
++    nret = sss_ncache_check_locate_uid(rctx->ncache, domain, data->id);
++    if (nret == EEXIST) {
++        nret = sss_ncache_check_locate_gid(rctx->ncache, domain, data->id);
++        if (nret == EEXIST) {
++            return false;
++        }
++    }
++
++    return true;
++}
++
++static struct tevent_req *
++cache_req_object_by_id_get_domain_send(TALLOC_CTX *mem_ctx,
++                                       struct resp_ctx *rctx,
++                                       struct sss_domain_info *domain,
++                                       struct cache_req_data *data)
++{
++    int nret;
++
++    nret = sss_ncache_set_locate_uid(rctx->ncache, domain, data->id);
++    if (nret != EOK) {
++        DEBUG(SSSDBG_MINOR_FAILURE,
++              "Cannot set negative cache, this might result in "
++              "performance degradation\n");
++        /* Not fatal */
++    }
++
++    nret = sss_ncache_set_locate_gid(rctx->ncache, domain, data->id);
++    if (nret != EOK) {
++        DEBUG(SSSDBG_MINOR_FAILURE,
++              "Cannot set negative cache, this might result in "
++              "performance degradation\n");
++        /* Not fatal */
++    }
++
++    return sss_dp_get_account_domain_send(mem_ctx,
++                                          rctx,
++                                          domain,
++                                          SSS_DP_USER_AND_GROUP,
++                                          data->id);
++}
++
+ const struct cache_req_plugin cache_req_object_by_id = {
+     .name = "Object by ID",
+     .attr_expiration = SYSDB_CACHE_EXPIRE,
+@@ -125,14 +194,14 @@ const struct cache_req_plugin cache_req_object_by_id = {
+     .create_debug_name_fn = cache_req_object_by_id_create_debug_name,
+     .global_ncache_add_fn = cache_req_object_by_id_global_ncache_add,
+     .ncache_check_fn = cache_req_object_by_id_ncache_check,
+-    .ncache_add_fn = NULL,
++    .ncache_add_fn = cache_req_object_by_id_ncache_add,
+     .ncache_filter_fn = cache_req_object_by_id_ncache_filter,
+     .lookup_fn = cache_req_object_by_id_lookup,
+     .dp_send_fn = cache_req_object_by_id_dp_send,
+     .dp_recv_fn = cache_req_common_dp_recv,
+-    .dp_get_domain_check_fn = NULL,
+-    .dp_get_domain_send_fn = NULL,
+-    .dp_get_domain_recv_fn = NULL,
++    .dp_get_domain_check_fn = cache_req_object_by_id_get_domain_check,
++    .dp_get_domain_send_fn = cache_req_object_by_id_get_domain_send,
++    .dp_get_domain_recv_fn = cache_reg_common_get_acct_domain_recv,
+ };
+ 
+ struct tevent_req *
+diff --git a/src/responder/common/cache_req/plugins/cache_req_user_by_id.c b/src/responder/common/cache_req/plugins/cache_req_user_by_id.c
+index 254330e92cc801b84bfb5e308d6d90ac54507d77..656fa41af5f39f68c64e241aa97c4eaf3ec57395 100644
+--- a/src/responder/common/cache_req/plugins/cache_req_user_by_id.c
++++ b/src/responder/common/cache_req/plugins/cache_req_user_by_id.c
+@@ -39,6 +39,15 @@ cache_req_user_by_id_ncache_check(struct sss_nc_ctx *ncache,
+                                   struct sss_domain_info *domain,
+                                   struct cache_req_data *data)
+ {
++    errno_t ret;
++
++    if (domain != NULL) {
++        ret = sss_ncache_check_uid(ncache, domain, data->id);
++        if (ret == EEXIST) {
++            return ret;
++        }
++    }
++
+     return sss_ncache_check_uid(ncache, NULL, data->id);
+ }
+ 
+@@ -57,6 +66,14 @@ cache_req_user_by_id_global_ncache_add(struct sss_nc_ctx *ncache,
+     return sss_ncache_set_uid(ncache, false, NULL, data->id);
+ }
+ 
++static errno_t
++cache_req_user_by_id_ncache_add(struct sss_nc_ctx *ncache,
++                                struct sss_domain_info *domain,
++                                struct cache_req_data *data)
++{
++    return sss_ncache_set_uid(ncache, false, domain, data->id);
++}
++
+ static errno_t
+ cache_req_user_by_id_lookup(TALLOC_CTX *mem_ctx,
+                             struct cache_req *cr,
+@@ -132,6 +149,44 @@ cache_req_user_by_id_dp_send(TALLOC_CTX *mem_ctx,
+                                    SSS_DP_USER, string, id, flag);
+ }
+ 
++static bool
++cache_req_user_by_id_get_domain_check(struct resp_ctx *rctx,
++                                      struct sss_domain_info *domain,
++                                      struct cache_req_data *data)
++{
++    int nret;
++
++    nret = sss_ncache_check_locate_uid(rctx->ncache, domain, data->id);
++    if (nret == EEXIST) {
++        return false;
++    }
++
++    return true;
++}
++
++static struct tevent_req *
++cache_req_user_by_id_get_domain_send(TALLOC_CTX *mem_ctx,
++                                     struct resp_ctx *rctx,
++                                     struct sss_domain_info *domain,
++                                     struct cache_req_data *data)
++{
++    int nret;
++
++    nret = sss_ncache_set_locate_uid(rctx->ncache, domain, data->id);
++    if (nret != EOK) {
++        DEBUG(SSSDBG_MINOR_FAILURE,
++              "Cannot set negative cache, this might result in "
++              "performance degradation\n");
++        /* Not fatal */
++    }
++
++    return sss_dp_get_account_domain_send(mem_ctx,
++                                          rctx,
++                                          domain,
++                                          SSS_DP_USER,
++                                          data->id);
++}
++
+ const struct cache_req_plugin cache_req_user_by_id = {
+     .name = "User by ID",
+     .attr_expiration = SYSDB_CACHE_EXPIRE,
+@@ -151,14 +206,14 @@ const struct cache_req_plugin cache_req_user_by_id = {
+     .create_debug_name_fn = cache_req_user_by_id_create_debug_name,
+     .global_ncache_add_fn = cache_req_user_by_id_global_ncache_add,
+     .ncache_check_fn = cache_req_user_by_id_ncache_check,
+-    .ncache_add_fn = NULL,
++    .ncache_add_fn = cache_req_user_by_id_ncache_add,
+     .ncache_filter_fn = cache_req_user_by_id_ncache_filter,
+     .lookup_fn = cache_req_user_by_id_lookup,
+     .dp_send_fn = cache_req_user_by_id_dp_send,
+     .dp_recv_fn = cache_req_common_dp_recv,
+-    .dp_get_domain_check_fn = NULL,
+-    .dp_get_domain_send_fn = NULL,
+-    .dp_get_domain_recv_fn = NULL,
++    .dp_get_domain_check_fn = cache_req_user_by_id_get_domain_check,
++    .dp_get_domain_send_fn = cache_req_user_by_id_get_domain_send,
++    .dp_get_domain_recv_fn = cache_reg_common_get_acct_domain_recv,
+ };
+ 
+ struct tevent_req *
+diff --git a/src/tests/cmocka/common_mock_resp_dp.c b/src/tests/cmocka/common_mock_resp_dp.c
+index 4b38a38e6f53499132f9fe14a0ec0af157cf85ca..f21ca53ad0d6b7f4ed28d0c1d9e491af31355d43 100644
+--- a/src/tests/cmocka/common_mock_resp_dp.c
++++ b/src/tests/cmocka/common_mock_resp_dp.c
+@@ -179,3 +179,26 @@ errno_t sss_dp_get_domains_recv(struct tevent_req *req)
+ {
+     return test_request_recv(req);
+ }
++
++struct tevent_req *
++sss_dp_get_account_domain_send(TALLOC_CTX *mem_ctx,
++                               struct resp_ctx *rctx,
++                               struct sss_domain_info *domain,
++                               enum sss_dp_acct_type type,
++                               uint32_t opt_id)
++{
++    return test_req_succeed_send(mem_ctx, rctx->ev);
++}
++
++errno_t sss_dp_get_account_domain_recv(TALLOC_CTX *mem_ctx,
++                                       struct tevent_req *req,
++                                       char **_domain)
++{
++    errno_t ret;
++
++    ret = sss_mock_type(errno_t);
++    if (ret == EOK) {
++        *_domain = sss_mock_ptr_type(char *);
++    }
++    return ret;
++}
+-- 
+2.14.3
+
diff --git a/SOURCES/0081-ci-do-not-build-secrets-on-rhel6.patch b/SOURCES/0081-ci-do-not-build-secrets-on-rhel6.patch
deleted file mode 100644
index f804036..0000000
--- a/SOURCES/0081-ci-do-not-build-secrets-on-rhel6.patch
+++ /dev/null
@@ -1,115 +0,0 @@
-From c9358747b25b257d82b050967812e54860fe7685 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Pavel=20B=C5=99ezina?= <pbrezina@redhat.com>
-Date: Tue, 28 Mar 2017 15:24:01 +0200
-Subject: [PATCH 81/90] ci: do not build secrets on rhel6
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-We require newer libcurl version than is available on rhel6. We don't
-ship secrets responder in rhel6 so we just disable its build.
-
-Reviewed-by: Lukáš Slebodník <lslebodn@redhat.com>
-(cherry picked from commit 6698d40512e55e7c2d03e14c227c51b1edc77ffa)
----
- contrib/ci/configure.sh        |  1 +
- contrib/sssd.spec.in           | 15 +++++++++++++++
- src/tests/intg/test_secrets.py |  4 ++++
- 3 files changed, 20 insertions(+)
-
-diff --git a/contrib/ci/configure.sh b/contrib/ci/configure.sh
-index 7590743c2aa5fe31bcdf1a3e92a3f482dbec699b..9d18d0c187561a2dc3bc47d3e8913626e7ff3046 100644
---- a/contrib/ci/configure.sh
-+++ b/contrib/ci/configure.sh
-@@ -38,6 +38,7 @@ if [[ "$DISTRO_BRANCH" == -redhat-redhatenterprise*-6.*- ||
-         "--disable-cifs-idmap-plugin"
-         "--with-syslog=syslog"
-         "--without-python3-bindings"
-+        "--without-secrets"
-         "--without-kcm"
-     )
- fi
-diff --git a/contrib/sssd.spec.in b/contrib/sssd.spec.in
-index af14d4e3d6b9ffeb4696f1517113b8daa575cb99..39a974edebba3dbcd7625d1729b4a7330eaa8a27 100644
---- a/contrib/sssd.spec.in
-+++ b/contrib/sssd.spec.in
-@@ -112,6 +112,12 @@
-     %global enable_systemtap_opt --enable-systemtap
- %endif
- 
-+%if (0%{?fedora} || 0%{?epel} >= 7)
-+    %global with_secrets 1
-+%else
-+    %global with_secret_responder --without-secrets
-+%endif
-+
- %if (0%{?fedora} >= 23 || 0%{?rhel} >= 7)
-     %global with_kcm 1
-     %global with_kcm_option --with-kcm
-@@ -220,8 +226,10 @@ BuildRequires: libsmbclient-devel
- %if (0%{?enable_systemtap} == 1)
- BuildRequires: systemtap-sdt-devel
- %endif
-+%if (0%{?with_secrets} == 1)
- BuildRequires: http-parser-devel
- BuildRequires: jansson-devel
-+%endif
- BuildRequires: libuuid-devel
- BuildRequires: libcurl-devel
- 
-@@ -727,6 +735,7 @@ autoreconf -ivf
-     %{?with_python3_option} \
-     %{?enable_polkit_rules_option} \
-     %{?enable_systemtap_opt} \
-+    %{?with_secret_responder} \
-     %{?with_kcm_option} \
-     %{?experimental}
- 
-@@ -865,7 +874,9 @@ done
- %{_libexecdir}/%{servicename}/sssd_nss
- %{_libexecdir}/%{servicename}/sssd_pam
- %{_libexecdir}/%{servicename}/sssd_autofs
-+%if (0%{?with_secrets} == 1)
- %{_libexecdir}/%{servicename}/sssd_secrets
-+%endif
- %{_libexecdir}/%{servicename}/sssd_ssh
- %{_libexecdir}/%{servicename}/sssd_sudo
- %{_libexecdir}/%{servicename}/p11_child
-@@ -900,7 +911,9 @@ done
- %dir %{_localstatedir}/cache/krb5rcache
- %attr(700,sssd,sssd) %dir %{dbpath}
- %attr(755,sssd,sssd) %dir %{mcpath}
-+%if (0%{?with_secrets} == 1)
- %attr(700,root,root) %dir %{secdbpath}
-+%endif
- %ghost %attr(0644,sssd,sssd) %verify(not md5 size mtime) %{mcpath}/passwd
- %ghost %attr(0644,sssd,sssd) %verify(not md5 size mtime) %{mcpath}/group
- %ghost %attr(0644,sssd,sssd) %verify(not md5 size mtime) %{mcpath}/initgroups
-@@ -933,7 +946,9 @@ done
- %{_mandir}/man5/sssd.conf.5*
- %{_mandir}/man5/sssd-simple.5*
- %{_mandir}/man5/sssd-sudo.5*
-+%if (0%{?with_secrets} == 1)
- %{_mandir}/man5/sssd-secrets.5*
-+%endif
- %{_mandir}/man5/sss_rpcidmapd.5*
- %{_mandir}/man8/sssd.8*
- %{_mandir}/man8/sss_cache.8*
-diff --git a/src/tests/intg/test_secrets.py b/src/tests/intg/test_secrets.py
-index d71c1904558cc6f8a6eee36c4049582705bc30ac..202f43e61cb0e4986394ad2b32da5abdcb0be3e9 100644
---- a/src/tests/intg/test_secrets.py
-+++ b/src/tests/intg/test_secrets.py
-@@ -46,6 +46,10 @@ def create_sssd_secrets_fixture(request):
-         raise Exception("failed to regenerate confdb")
- 
-     resp_path = os.path.join(config.LIBEXEC_PATH, "sssd", "sssd_secrets")
-+    if not os.access(resp_path, os.X_OK):
-+        # It would be cleaner to use pytest.mark.skipif on the package level
-+        # but upstream insists on supporting RHEL-6.
-+        pytest.skip("No Secrets responder, skipping")
- 
-     secpid = os.fork()
-     assert secpid >= 0
--- 
-2.9.3
-
diff --git a/SOURCES/0082-CACHE_REQ-Use-the-domain-locator-request-to-only-sea.patch b/SOURCES/0082-CACHE_REQ-Use-the-domain-locator-request-to-only-sea.patch
new file mode 100644
index 0000000..713322a
--- /dev/null
+++ b/SOURCES/0082-CACHE_REQ-Use-the-domain-locator-request-to-only-sea.patch
@@ -0,0 +1,2048 @@
+From 1b4b03720c409b183debe0e0532b1009301e9cb2 Mon Sep 17 00:00:00 2001
+From: Jakub Hrozek <jhrozek@redhat.com>
+Date: Sun, 19 Nov 2017 22:47:00 +0100
+Subject: [PATCH 82/83] CACHE_REQ: Use the domain-locator request to only
+ search domains where the entry was found
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Uses the internal cache_req interface around the getAccountDomain to only
+search the domain returned by the cache_req_locate_domain_recv() request.
+
+If that request returns that no domain matched, all domains (belonging
+to the currently processed main domain) are skipped by setting the
+per-type negative cache.
+
+if a domain is reported as containing an object, all domains except that
+one are marked with the negative cache entries.
+
+Resolves:
+https://pagure.io/SSSD/sssd/issue/3468
+
+Reviewed-by: Pavel Březina <pbrezina@redhat.com>
+Reviewed-by: Sumit Bose <sbose@redhat.com>
+(cherry picked from commit f2a5e29f063f9d623c1336d76f4b2bc500c1a5e2)
+---
+ src/responder/common/cache_req/cache_req.c        |  402 +++++-
+ src/responder/common/cache_req/cache_req_domain.h |    1 +
+ src/tests/cmocka/test_responder_cache_req.c       | 1373 +++++++++++++++++++++
+ 3 files changed, 1758 insertions(+), 18 deletions(-)
+
+diff --git a/src/responder/common/cache_req/cache_req.c b/src/responder/common/cache_req/cache_req.c
+index 110df561101be538e3f0496addfa2e14e42ea918..ad9bc040dd999a205713141e6a1512e47b69c45e 100644
+--- a/src/responder/common/cache_req/cache_req.c
++++ b/src/responder/common/cache_req/cache_req.c
+@@ -363,6 +363,53 @@ static void cache_req_global_ncache_add(struct cache_req *cr)
+     return;
+ }
+ 
++static bool cache_req_check_acct_domain_lookup_type(struct cache_req *cr,
++                                                    struct sss_domain_info *dom)
++{
++    struct sss_domain_info *head;
++    int nret;
++
++    head = get_domains_head(dom);
++    if (head == NULL) {
++        return false;
++    }
++
++    nret = sss_ncache_check_domain_locate_type(cr->rctx->ncache,
++                                               head,
++                                               cr->plugin->name);
++    if (nret == ENOENT) {
++        return true;
++    }
++    return false;
++}
++
++static errno_t cache_req_set_acct_domain_lookup_type(struct cache_req *cr,
++                                                     struct sss_domain_info *dom)
++{
++    struct sss_domain_info *head;
++
++    head = get_domains_head(dom);
++    if (head == NULL) {
++        return EINVAL;
++    }
++
++    return sss_ncache_set_domain_locate_type(cr->rctx->ncache,
++                                             head,
++                                             cr->plugin->name);
++}
++
++static void cache_req_domain_set_locate_flag(struct cache_req_domain *domains,
++                                             struct cache_req *cr)
++{
++    struct cache_req_domain *crd_iter;
++
++    DLIST_FOR_EACH(crd_iter, domains) {
++        if (cache_req_check_acct_domain_lookup_type(cr, crd_iter->domain)) {
++            crd_iter->locate_domain = true;
++        }
++    }
++}
++
+ static bool
+ cache_req_assume_upn(struct cache_req *cr)
+ {
+@@ -391,6 +438,227 @@ cache_req_assume_upn(struct cache_req *cr)
+     return true;
+ }
+ 
++struct cache_req_locate_dom_state {
++    /* input data */
++    struct tevent_context *ev;
++    struct cache_req *cr;
++    struct cache_req_domain *req_domains;
++
++    /* Return values in case the first cache lookup succeeds */
++    struct ldb_result *result;
++    bool dp_success;
++};
++
++static void cache_req_locate_dom_cache_done(struct tevent_req *subreq);
++static void cache_req_locate_dom_done(struct tevent_req *subreq);
++static void cache_req_locate_dom_mark_neg_all(
++                                struct cache_req_locate_dom_state *state);
++static void cache_req_locate_dom_mark_neg_domains(
++                                struct cache_req_locate_dom_state *state,
++                                const char *found_domain_name);
++
++static struct tevent_req *cache_req_locate_dom_send(TALLOC_CTX *mem_ctx,
++                                                    struct tevent_context *ev,
++                                                    struct cache_req *cr,
++                                                    struct cache_req_domain *req_domains)
++{
++    struct tevent_req *req;
++    struct tevent_req *subreq;
++    struct cache_req_locate_dom_state *state = NULL;
++    errno_t ret;
++
++    req = tevent_req_create(mem_ctx, &state,
++                            struct cache_req_locate_dom_state);
++    if (req == NULL) {
++        DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create() failed\n");
++        return NULL;
++    }
++    state->ev = ev;
++    state->cr = cr;
++    state->req_domains = req_domains;
++
++    /* It is wasteful to run the domain locator request if the results are
++     * present in the cache, because the domain locator always contacts
++     * the DP. Therefore, first run a cache-only search and only if the
++     * requested data is not available, run the locator
++     *
++     * FIXME - this could be optimized further if we are running the
++     * second iteration with cache_first, then we don't need to search
++     * again
++     */
++    subreq = cache_req_search_send(state,
++                                   state->ev,
++                                   state->cr,
++                                   false,       /* Don't bypass cache */
++                                   true);       /* Do bypass DP */
++    if (subreq == NULL) {
++        ret = ENOMEM;
++        goto immediately;
++    }
++    tevent_req_set_callback(subreq, cache_req_locate_dom_cache_done, req);
++
++    return req;
++
++immediately:
++    tevent_req_error(req, ret);
++    tevent_req_post(req, ev);
++    return req;
++}
++
++static void cache_req_locate_dom_cache_done(struct tevent_req *subreq)
++{
++    struct cache_req_locate_dom_state *state = NULL;
++    struct tevent_req *req;
++    errno_t ret;
++
++    req = tevent_req_callback_data(subreq, struct tevent_req);
++    state = tevent_req_data(req, struct cache_req_locate_dom_state);
++
++    ret = cache_req_search_recv(state, subreq, &state->result, &state->dp_success);
++    talloc_zfree(subreq);
++
++    switch (ret) {
++    case EOK:
++        /* Just finish the request and let the caller handle the result */
++        DEBUG(SSSDBG_TRACE_INTERNAL, "Result found in the cache\n");
++        tevent_req_done(req);
++        return;
++    case ENOENT:
++        /* Not cached and locator was requested, run the locator
++         * DP request plugin
++         */
++        subreq = cache_req_locate_domain_send(state,
++                                              state->ev,
++                                              state->cr);
++        if (subreq == NULL) {
++            tevent_req_error(req, ENOMEM);
++            return;
++        }
++        tevent_req_set_callback(subreq, cache_req_locate_dom_done, req);
++        return;
++    default:
++        DEBUG(SSSDBG_OP_FAILURE,
++              "cache_req_search_recv returned [%d]: %s\n", ret, sss_strerror(ret));
++        break;
++    }
++
++    tevent_req_error(req, ret);
++    return;
++}
++
++static void cache_req_locate_dom_done(struct tevent_req *subreq)
++{
++    struct cache_req_locate_dom_state *state;
++    struct tevent_req *req;
++    errno_t ret;
++    char *found_domain_name;
++    int nret;
++
++    req = tevent_req_callback_data(subreq, struct tevent_req);
++    state = tevent_req_data(req, struct cache_req_locate_dom_state);
++
++    ret = cache_req_locate_domain_recv(state, subreq, &found_domain_name);
++    talloc_zfree(subreq);
++    switch (ret) {
++    case ERR_GET_ACCT_DOM_NOT_SUPPORTED:
++        nret = cache_req_set_acct_domain_lookup_type(state->cr,
++                                                     state->cr->domain);
++        if (nret != EOK) {
++            DEBUG(SSSDBG_MINOR_FAILURE,
++                  "Failed to disable domain locating functionality for %s\n",
++                  state->cr->plugin->name);
++        }
++        DEBUG(SSSDBG_CONF_SETTINGS,
++              "Disabled domain locating functionality for %s\n",
++              state->cr->plugin->name);
++        break;
++    case ERR_NOT_FOUND:
++        cache_req_locate_dom_mark_neg_all(state);
++        break;
++    case EOK:
++        cache_req_locate_dom_mark_neg_domains(state, found_domain_name);
++        break;
++    default:
++        /* We explicitly ignore errors here */
++        break;
++    }
++
++    tevent_req_done(req);
++    return;
++}
++
++static void cache_req_locate_dom_mark_neg_all(
++                                struct cache_req_locate_dom_state *state)
++{
++    struct cache_req_domain *iter;
++
++    DLIST_FOR_EACH(iter, state->req_domains) {
++        if (get_domains_head(state->cr->domain) != get_domains_head(iter->domain)) {
++            /* Only add to negative cache for domains from the same "main"
++             * domain" */
++            continue;
++        }
++        cache_req_search_ncache_add_to_domain(state->cr, iter->domain);
++    }
++}
++
++static void cache_req_locate_dom_mark_neg_domains(
++                                struct cache_req_locate_dom_state *state,
++                                const char *found_domain_name)
++{
++    struct sss_domain_info *found_domain;
++    struct cache_req_domain *iter;
++
++    found_domain = find_domain_by_name(get_domains_head(state->cr->domain),
++                                       found_domain_name,
++                                       true);
++    if (found_domain == NULL) {
++        DEBUG(SSSDBG_MINOR_FAILURE,
++                "Cannot find domain %s\n", found_domain_name);
++        return;
++    }
++
++    /* Set negcache in all subdomains of the one being examined
++     * except the found one */
++    DLIST_FOR_EACH(iter, state->req_domains) {
++        if (strcasecmp(found_domain_name,
++                       iter->domain->name) == 0) {
++            continue;
++        }
++
++        if (get_domains_head(found_domain) != get_domains_head(iter->domain)) {
++            /* Don't set negative cache for domains outside the main
++             * domain/subdomain tree b/c the locator request is not
++             * authoritative for them
++             */
++            continue;
++        }
++        cache_req_search_ncache_add_to_domain(state->cr, iter->domain);
++    }
++}
++
++static errno_t cache_req_locate_dom_cache_recv(TALLOC_CTX *mem_ctx,
++                                               struct tevent_req *req,
++                                               struct ldb_result **_result,
++                                               bool *_dp_success)
++{
++    struct cache_req_locate_dom_state *state;
++
++    state = tevent_req_data(req, struct cache_req_locate_dom_state);
++
++    if (_dp_success != NULL) {
++        *_dp_success = state->dp_success;
++    }
++
++    TEVENT_REQ_RETURN_ON_ERROR(req);
++
++    if (_result != NULL) {
++        *_result = talloc_steal(mem_ctx, state->result);
++    }
++
++    return EOK;
++}
++
+ struct cache_req_search_domains_state {
+     /* input data */
+     struct tevent_context *ev;
+@@ -398,6 +666,7 @@ struct cache_req_search_domains_state {
+ 
+     /* work data */
+     struct cache_req_domain *cr_domain;
++    struct cache_req_domain *req_domains;
+     struct sss_domain_info *selected_domain;
+     struct cache_req_result **results;
+     size_t num_results;
+@@ -408,6 +677,10 @@ struct cache_req_search_domains_state {
+ };
+ 
+ static errno_t cache_req_search_domains_next(struct tevent_req *req);
++static errno_t cache_req_handle_result(struct tevent_req *req,
++                                       struct ldb_result *result);
++
++static void cache_req_search_domains_locate_done(struct tevent_req *subreq);
+ 
+ static void cache_req_search_domains_done(struct tevent_req *subreq);
+ 
+@@ -417,6 +690,7 @@ cache_req_search_domains_send(TALLOC_CTX *mem_ctx,
+                               struct cache_req *cr,
+                               struct cache_req_domain *cr_domain,
+                               bool check_next,
++                              bool first_iteration,
+                               bool bypass_cache,
+                               bool bypass_dp)
+ {
+@@ -435,11 +709,23 @@ cache_req_search_domains_send(TALLOC_CTX *mem_ctx,
+     state->cr = cr;
+ 
+     state->cr_domain = cr_domain;
++    state->req_domains = cr_domain;
+     state->check_next = check_next;
+     state->dp_success = true;
+     state->bypass_cache = bypass_cache;
+     state->bypass_dp = bypass_dp;
+ 
++    if (cr->plugin->dp_get_domain_send_fn != NULL
++            && ((state->check_next && cr_domain->next != NULL)
++                || (state->bypass_cache && !first_iteration))) {
++        /* If the request is not qualified with a domain name AND
++         * there are multiple domains to search OR if this is the second
++         * pass during the "check-cache-first" schema, it makes sense
++         * to try to run the domain-locator plugin
++         */
++        cache_req_domain_set_locate_flag(cr_domain, cr);
++    }
++
+     ret = cache_req_search_domains_next(req);
+     if (ret == EAGAIN) {
+         return req;
+@@ -510,12 +796,23 @@ static errno_t cache_req_search_domains_next(struct tevent_req *req)
+             return ret;
+         }
+ 
++        if (state->cr_domain->locate_domain) {
++            subreq = cache_req_locate_dom_send(state,
++                                               state->ev,
++                                               cr,
++                                               state->req_domains);
++            if (subreq == NULL) {
++                return ENOMEM;
++            }
++            tevent_req_set_callback(subreq, cache_req_search_domains_locate_done, req);
++            return EAGAIN;
++        }
++
+         subreq = cache_req_search_send(state, state->ev, cr,
+                                        state->bypass_cache, state->bypass_dp);
+         if (subreq == NULL) {
+             return ENOMEM;
+         }
+-
+         tevent_req_set_callback(subreq, cache_req_search_domains_done, req);
+ 
+         /* we will continue with the following domain the next time */
+@@ -549,6 +846,89 @@ static errno_t cache_req_search_domains_next(struct tevent_req *req)
+     return ENOENT;
+ }
+ 
++static void cache_req_search_domains_locate_done(struct tevent_req *subreq)
++{
++    struct cache_req_search_domains_state *state;
++    struct ldb_result *result = NULL;
++    struct tevent_req *req;
++    bool dp_success;
++    errno_t ret;
++
++    req = tevent_req_callback_data(subreq, struct tevent_req);
++    state = tevent_req_data(req, struct cache_req_search_domains_state);
++
++    ret = cache_req_locate_dom_cache_recv(state, subreq, &result, &dp_success);
++    talloc_zfree(subreq);
++
++    /* Remember if any DP request fails, but here it shouldn't matter
++     * as the only DP request that should realistically happen is midpoint
++     * refresh */
++    state->dp_success = !dp_success ? false : state->dp_success;
++
++    /* Don't locate the domain again */
++    state->cr_domain->locate_domain = false;
++
++    switch (ret) {
++    case EOK:
++        if (result != NULL) {
++            /* Handle result as normally */
++            ret = cache_req_handle_result(req, result);
++            if (ret != EAGAIN) {
++                goto done;
++            }
++        }
++        break;
++    default:
++        /* Some serious error has happened. Finish. */
++        goto done;
++    }
++
++    /* This is a domain less search, continue with the next domain. */
++    ret = cache_req_search_domains_next(req);
++
++done:
++    switch (ret) {
++    case EOK:
++        tevent_req_done(req);
++        break;
++    case EAGAIN:
++        break;
++    default:
++        tevent_req_error(req, ret);
++        break;
++    }
++    return;
++}
++
++static errno_t cache_req_handle_result(struct tevent_req *req,
++                                       struct ldb_result *result)
++{
++    struct cache_req_search_domains_state *state;
++    errno_t ret;
++
++    state = tevent_req_data(req, struct cache_req_search_domains_state);
++
++    /* We got some data from this search. Save it. */
++    ret = cache_req_create_and_add_result(state,
++                                          state->cr,
++                                          state->selected_domain,
++                                          result,
++                                          state->cr->data->name.lookup,
++                                          &state->results,
++                                          &state->num_results);
++    if (ret != EOK) {
++        /* We were unable to save data. */
++        return ret;
++    }
++
++    if (!state->check_next || !state->cr->plugin->search_all_domains) {
++        /* We are not interested in more results. */
++        return EOK;
++    }
++
++    return EAGAIN;
++}
++
+ static void cache_req_search_domains_done(struct tevent_req *subreq)
+ {
+     struct cache_req_search_domains_state *state;
+@@ -568,25 +948,10 @@ static void cache_req_search_domains_done(struct tevent_req *subreq)
+ 
+     switch (ret) {
+     case EOK:
+-        /* We got some data from this search. Save it. */
+-        ret = cache_req_create_and_add_result(state,
+-                                              state->cr,
+-                                              state->selected_domain,
+-                                              result,
+-                                              state->cr->data->name.lookup,
+-                                              &state->results,
+-                                              &state->num_results);
+-        if (ret != EOK) {
+-            /* We were unable to save data. */
++        ret = cache_req_handle_result(req, result);
++        if (ret != EAGAIN) {
+             goto done;
+         }
+-
+-        if (!state->check_next || !state->cr->plugin->search_all_domains) {
+-            /* We are not interested in more results. */
+-            ret = EOK;
+-            goto done;
+-        }
+-
+         break;
+     case ENOENT:
+         if (state->check_next == false) {
+@@ -1030,6 +1395,7 @@ cache_req_search_domains(struct tevent_req *req,
+ 
+     subreq = cache_req_search_domains_send(state, state->ev, state->cr,
+                                            cr_domain, check_next,
++                                           state->first_iteration,
+                                            bypass_cache, bypass_dp);
+     if (subreq == NULL) {
+         return ENOMEM;
+diff --git a/src/responder/common/cache_req/cache_req_domain.h b/src/responder/common/cache_req/cache_req_domain.h
+index ebdc71dd635d5d8a5d06e30e96c5d4101b6d98bf..5769b6aee309d9ba3edd5bb73a3cef6dc3193fdc 100644
+--- a/src/responder/common/cache_req/cache_req_domain.h
++++ b/src/responder/common/cache_req/cache_req_domain.h
+@@ -26,6 +26,7 @@
+ struct cache_req_domain {
+     struct sss_domain_info *domain;
+     bool fqnames;
++    bool locate_domain;
+ 
+     struct cache_req_domain *prev;
+     struct cache_req_domain *next;
+diff --git a/src/tests/cmocka/test_responder_cache_req.c b/src/tests/cmocka/test_responder_cache_req.c
+index f075480a019e476407a3081a795c3c289455aca8..0ee0070d0c9fbb89020f522b2f7613f1076a8cbb 100644
+--- a/src/tests/cmocka/test_responder_cache_req.c
++++ b/src/tests/cmocka/test_responder_cache_req.c
+@@ -27,6 +27,7 @@
+ #include "tests/cmocka/common_mock_resp.h"
+ #include "db/sysdb.h"
+ #include "responder/common/cache_req/cache_req.h"
++#include "db/sysdb_private.h"   /* new_subdomain() */
+ 
+ #define TESTS_PATH "tp_" BASE_FILE_STEM
+ #define TEST_CONF_DB "test_responder_cache_req_conf.ldb"
+@@ -63,6 +64,11 @@ struct test_group {
+                                     test_multi_domain_setup, \
+                                     test_multi_domain_teardown)
+ 
++#define new_subdomain_test(test) \
++    cmocka_unit_test_setup_teardown(test_ ## test, \
++                                    test_subdomain_setup, \
++                                    test_subdomain_teardown)
++
+ #define run_cache_req(ctx, send_fn, done_fn, dom, crp, lookup, expret) do { \
+     TALLOC_CTX *req_mem_ctx;                                                \
+     struct tevent_req *req;                                                 \
+@@ -110,6 +116,7 @@ struct cache_req_test_ctx {
+     struct sss_test_ctx *tctx;
+     struct resp_ctx *rctx;
+     struct sss_nc_ctx *ncache;
++    struct sss_domain_info *subdomain;
+ 
+     struct cache_req_result *result;
+     bool dp_called;
+@@ -120,6 +127,8 @@ struct cache_req_test_ctx {
+     bool create_user2;
+     bool create_group1;
+     bool create_group2;
++    bool create_subgroup1;
++    bool create_subuser1;
+ };
+ 
+ const char *domains[] = {"responder_cache_req_test_a",
+@@ -128,6 +137,8 @@ const char *domains[] = {"responder_cache_req_test_a",
+                          "responder_cache_req_test_d",
+                          NULL};
+ 
++const char *subdomain_name = "responder_cache_req_test_a_sub";
++
+ struct cli_protocol_version *register_cli_protocol_version(void)
+ {
+     static struct cli_protocol_version version[] = {
+@@ -487,6 +498,26 @@ __wrap_sss_dp_get_account_send(TALLOC_CTX *mem_ctx,
+         prepare_group(ctx->tctx->dom, &groups[1], 1000, time(NULL));
+     }
+ 
++    if (ctx->create_subgroup1) {
++        struct sss_domain_info *domain = NULL;
++
++        domain = find_domain_by_name(ctx->tctx->dom,
++                                     subdomain_name,
++                                     true);
++        assert_non_null(domain);
++        prepare_group(domain, &groups[0], 1000, time(NULL));
++    }
++
++    if (ctx->create_subuser1) {
++        struct sss_domain_info *domain = NULL;
++
++        domain = find_domain_by_name(ctx->tctx->dom,
++                                     subdomain_name,
++                                     true);
++        assert_non_null(domain);
++        prepare_user(domain, &users[0], 1000, time(NULL));
++    }
++
+     return test_req_succeed_send(mem_ctx, rctx->ev);
+ }
+ 
+@@ -581,6 +612,67 @@ static int test_multi_domain_teardown(void **state)
+     return 0;
+ }
+ 
++static int test_subdomain_setup(void **state)
++{
++    struct cache_req_test_ctx *test_ctx = NULL;
++    int ret;
++    const char *const testdom[4] = { subdomain_name, "TEST_A.SUB", "test_a", "S-3" };
++
++    assert_true(leak_check_setup());
++
++    test_dom_suite_setup(TESTS_PATH);
++
++    test_ctx = talloc_zero(global_talloc_context, struct cache_req_test_ctx);
++    assert_non_null(test_ctx);
++    *state = test_ctx;
++
++    test_ctx->tctx = create_dom_test_ctx(test_ctx, TESTS_PATH, TEST_CONF_DB,
++                                         TEST_DOM_NAME, TEST_ID_PROVIDER, NULL);
++    assert_non_null(test_ctx->tctx);
++
++    test_ctx->rctx = mock_rctx(test_ctx, test_ctx->tctx->ev,
++                               test_ctx->tctx->dom, NULL);
++    assert_non_null(test_ctx->rctx);
++
++    ret = sss_ncache_init(test_ctx, 10, 0, &test_ctx->ncache);
++    assert_int_equal(ret, EOK);
++
++    test_ctx->subdomain = new_subdomain(test_ctx, test_ctx->tctx->dom,
++                              testdom[0], testdom[1], testdom[2], testdom[3],
++                              false, false, NULL, NULL, 0,
++                              test_ctx->tctx->confdb);
++    assert_non_null(test_ctx->subdomain);
++
++    ret = sysdb_subdomain_store(test_ctx->tctx->sysdb,
++                                testdom[0], testdom[1], testdom[2], testdom[3],
++                                false, false, NULL, 0, NULL);
++    assert_int_equal(ret, EOK);
++
++    ret = sysdb_update_subdomains(test_ctx->tctx->dom,
++                                  test_ctx->tctx->confdb);
++    assert_int_equal(ret, EOK);
++
++    *state = test_ctx;
++    check_leaks_push(test_ctx);
++    return 0;
++}
++
++static int test_subdomain_teardown(void **state)
++{
++    struct cache_req_test_ctx *test_ctx;
++
++    test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx);
++
++    talloc_zfree(test_ctx->result);
++    talloc_zfree(test_ctx->rctx->cr_domains);
++
++    assert_true(check_leaks_pop(test_ctx));
++    talloc_zfree(test_ctx);
++    test_dom_suite_cleanup(TESTS_PATH, TEST_CONF_DB, TEST_DOM_NAME);
++    assert_true(leak_check_teardown());
++    return 0;
++}
++
+ void test_user_by_name_multiple_domains_found(void **state)
+ {
+     struct cache_req_test_ctx *test_ctx = NULL;
+@@ -974,6 +1066,7 @@ void test_user_by_id_multiple_domains_found(void **state)
+     /* Mock values. */
+     will_return_always(__wrap_sss_dp_get_account_send, test_ctx);
+     will_return_always(sss_dp_req_recv, 0);
++    will_return_always(sss_dp_get_account_domain_recv, ERR_GET_ACCT_DOM_NOT_SUPPORTED);
+ 
+     /* Test. */
+     run_user_by_id(test_ctx, NULL, 0, ERR_OK);
+@@ -990,12 +1083,317 @@ void test_user_by_id_multiple_domains_notfound(void **state)
+     /* Mock values. */
+     will_return_always(__wrap_sss_dp_get_account_send, test_ctx);
+     will_return_always(sss_dp_req_recv, 0);
++    will_return_always(sss_dp_get_account_domain_recv, ERR_GET_ACCT_DOM_NOT_SUPPORTED);
+ 
+     /* Test. */
+     run_user_by_id(test_ctx, NULL, 0, ENOENT);
+     assert_true(test_ctx->dp_called);
+ }
+ 
++void test_user_by_id_multiple_domains_locator_cache_valid(void **state)
++{
++    struct cache_req_test_ctx *test_ctx = NULL;
++    struct sss_domain_info *domain = NULL;
++    const char *locator_domain;
++    TALLOC_CTX *tmp_ctx;
++
++    test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx);
++
++    tmp_ctx = talloc_new(test_ctx);
++    assert_non_null(tmp_ctx);
++
++    /* Has to be a talloc ptr, not just const, so it's stealable inside cache_req */
++    locator_domain = talloc_strdup(tmp_ctx, "responder_cache_req_test_d");
++    assert_non_null(locator_domain);
++
++    /* Setup user. */
++    domain = find_domain_by_name(test_ctx->tctx->dom,
++                                 "responder_cache_req_test_d", true);
++    assert_non_null(domain);
++    prepare_user(domain, &users[0], 1000, time(NULL));
++
++    will_return(sss_dp_get_account_domain_recv, EOK);
++    will_return(sss_dp_get_account_domain_recv, locator_domain);
++    will_return_always(sss_dp_get_account_domain_recv, ERR_GET_ACCT_DOM_NOT_SUPPORTED);
++
++    will_return_always(__wrap_sss_dp_get_account_send, test_ctx);
++    will_return_always(sss_dp_req_recv, EOK);
++
++    /* Test. */
++    run_user_by_id(test_ctx, NULL, 0, ERR_OK);
++    /* Even though the locator tells us to skip all domains except d, the domains
++     * are standalone and the result of the locator request is only valid within
++     * the subdomains
++     */
++    assert_true(test_ctx->dp_called);
++    check_user(test_ctx, &users[0], domain);
++
++    talloc_free(tmp_ctx);
++}
++
++void test_user_by_id_multiple_domains_locator_cache_expired(void **state)
++{
++    struct cache_req_test_ctx *test_ctx = NULL;
++    struct sss_domain_info *domain = NULL;
++    const char *locator_domain;
++    TALLOC_CTX *tmp_ctx;
++
++    test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx);
++
++    tmp_ctx = talloc_new(test_ctx);
++    assert_non_null(tmp_ctx);
++
++    /* Has to be a talloc ptr, not just const, so it's stealable inside cache_req */
++    locator_domain = talloc_strdup(tmp_ctx, "responder_cache_req_test_d");
++    assert_non_null(locator_domain);
++
++    /* Setup user. */
++    domain = find_domain_by_name(test_ctx->tctx->dom,
++                                 "responder_cache_req_test_d", true);
++    assert_non_null(domain);
++    prepare_user(domain, &users[0], -1000, time(NULL));
++
++    will_return_always(__wrap_sss_dp_get_account_send, test_ctx);
++    will_return_always(sss_dp_req_recv, EOK);
++
++    will_return(sss_dp_get_account_domain_recv, EOK);
++    will_return(sss_dp_get_account_domain_recv, locator_domain);
++    will_return_always(sss_dp_get_account_domain_recv, ERR_GET_ACCT_DOM_NOT_SUPPORTED);
++
++    /* Test. */
++    run_user_by_id(test_ctx, NULL, 0, ERR_OK);
++
++    assert_true(test_ctx->dp_called);
++    check_user(test_ctx, &users[0], domain);
++
++    talloc_free(tmp_ctx);
++}
++
++void test_user_by_id_sub_domains_locator_cache_valid(void **state)
++{
++    struct cache_req_test_ctx *test_ctx = NULL;
++    struct sss_domain_info *domain = NULL;
++    const char *locator_domain;
++    TALLOC_CTX *tmp_ctx;
++
++    test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx);
++
++    tmp_ctx = talloc_new(test_ctx);
++    assert_non_null(tmp_ctx);
++
++    /* Has to be a talloc ptr, not just const, so it's stealable inside cache_req */
++    locator_domain = talloc_strdup(tmp_ctx, subdomain_name);
++    assert_non_null(locator_domain);
++
++    /* Setup user. */
++    domain = find_domain_by_name(test_ctx->tctx->dom,
++                                 subdomain_name,
++                                 true);
++    assert_non_null(domain);
++    prepare_user(domain, &users[0], 1000, time(NULL));
++
++    will_return(sss_dp_get_account_domain_recv, EOK);
++    will_return(sss_dp_get_account_domain_recv, locator_domain);
++
++    /* Test. */
++    run_user_by_id(test_ctx, NULL, 0, ERR_OK);
++
++    /* Even though the ID is present in the last domain,
++     * we're not calling sss_dp_get_account_send,
++     * because the locator will cause cache_req to skip
++     * all domains except _d
++     */
++    assert_false(test_ctx->dp_called);
++    check_user(test_ctx, &users[0], domain);
++
++    talloc_free(tmp_ctx);
++}
++
++void test_user_by_id_sub_domains_locator_cache_expired(void **state)
++{
++    struct cache_req_test_ctx *test_ctx = NULL;
++    struct sss_domain_info *domain = NULL;
++    const char *locator_domain;
++    TALLOC_CTX *tmp_ctx;
++
++    test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx);
++
++    tmp_ctx = talloc_new(test_ctx);
++    assert_non_null(tmp_ctx);
++
++    /* Has to be a talloc ptr, not just const, so it's stealable inside cache_req */
++    locator_domain = talloc_strdup(tmp_ctx, subdomain_name);
++    assert_non_null(locator_domain);
++
++    /* Setup user. */
++    domain = find_domain_by_name(test_ctx->tctx->dom,
++                                 subdomain_name,
++                                 true);
++    assert_non_null(domain);
++    prepare_user(domain, &users[0], -1000, time(NULL));
++
++    /* Note - DP will only be called once (so, we're not using will_return_always)
++     * because the locator will tell us which domain to look into. For the recv
++     * function, we use always b/c internally it mocks several values.
++     */
++    will_return(__wrap_sss_dp_get_account_send, test_ctx);
++    will_return_always(sss_dp_req_recv, 0);
++
++    will_return(sss_dp_get_account_domain_recv, EOK);
++    will_return(sss_dp_get_account_domain_recv, locator_domain);
++
++    /* Test. */
++    run_user_by_id(test_ctx, NULL, 0, ERR_OK);
++
++    assert_true(test_ctx->dp_called);
++    check_user(test_ctx, &users[0], domain);
++
++    talloc_free(tmp_ctx);
++}
++
++void test_user_by_id_sub_domains_locator_cache_midpoint(void **state)
++{
++    struct cache_req_test_ctx *test_ctx = NULL;
++    struct sss_domain_info *domain = NULL;
++    const char *locator_domain;
++    TALLOC_CTX *tmp_ctx;
++
++    test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx);
++
++    tmp_ctx = talloc_new(test_ctx);
++    assert_non_null(tmp_ctx);
++
++    /* Has to be a talloc ptr, not just const, so it's stealable inside cache_req */
++    locator_domain = talloc_strdup(tmp_ctx, subdomain_name);
++    assert_non_null(locator_domain);
++
++    /* Setup user. */
++    domain = find_domain_by_name(test_ctx->tctx->dom,
++                                 subdomain_name,
++                                 true);
++    assert_non_null(domain);
++    prepare_user(domain, &users[0], 50, time(NULL) - 26);
++
++    /* Note - DP will only be called once and we're not waiting
++     * for the results (so, we're not mocking _recv)
++     */
++    will_return(__wrap_sss_dp_get_account_send, test_ctx);
++
++    will_return(sss_dp_get_account_domain_recv, EOK);
++    will_return(sss_dp_get_account_domain_recv, locator_domain);
++
++    /* Test. */
++    run_user_by_id(test_ctx, NULL, 50, ERR_OK);
++
++    assert_true(test_ctx->dp_called);
++    check_user(test_ctx, &users[0], domain);
++
++    talloc_free(tmp_ctx);
++}
++
++void test_user_by_id_sub_domains_locator_missing_found(void **state)
++{
++    struct cache_req_test_ctx *test_ctx = NULL;
++    struct sss_domain_info *domain = NULL;
++    const char *locator_domain;
++    TALLOC_CTX *tmp_ctx;
++
++    test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx);
++
++    tmp_ctx = talloc_new(test_ctx);
++    assert_non_null(tmp_ctx);
++
++    /* Has to be a talloc ptr, not just const, so it's stealable inside cache_req */
++    locator_domain = talloc_strdup(tmp_ctx, subdomain_name);
++    assert_non_null(locator_domain);
++
++    /* Note - DP will only be called once (so, we're not using will_return_always)
++     * because the locator will tell us which domain to look into. For the recv
++     * function, we use always b/c internally it mocks several values.
++     */
++    will_return(__wrap_sss_dp_get_account_send, test_ctx);
++    will_return_always(sss_dp_req_recv, 0);
++
++    will_return(sss_dp_get_account_domain_recv, EOK);
++    will_return(sss_dp_get_account_domain_recv, locator_domain);
++
++    /* Test. */
++    test_ctx->create_subuser1 = true;
++    run_user_by_id(test_ctx, NULL, 0, ERR_OK);
++
++    assert_true(test_ctx->dp_called);
++
++    domain = find_domain_by_name(test_ctx->tctx->dom,
++                                 subdomain_name,
++                                 true);
++    assert_non_null(domain);
++    check_user(test_ctx, &users[0], domain);
++
++    talloc_free(tmp_ctx);
++}
++
++void test_user_by_id_sub_domains_locator_missing_notfound(void **state)
++{
++    struct cache_req_test_ctx *test_ctx = NULL;
++
++    test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx);
++
++    will_return(sss_dp_get_account_domain_recv, ERR_NOT_FOUND);
++
++    /* Test. */
++    run_user_by_id(test_ctx, NULL, 0, ENOENT);
++    assert_false(test_ctx->dp_called);
++}
++
++void test_user_by_id_sub_domains_locator_cache_expired_two_calls(void **state)
++{
++    struct cache_req_test_ctx *test_ctx = NULL;
++    struct sss_domain_info *domain = NULL;
++    const char *locator_domain;
++    TALLOC_CTX *tmp_ctx;
++
++    test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx);
++
++    tmp_ctx = talloc_new(test_ctx);
++    assert_non_null(tmp_ctx);
++
++    /* Has to be a talloc ptr, not just const, so it's stealable inside cache_req */
++    locator_domain = talloc_strdup(tmp_ctx, subdomain_name);
++    assert_non_null(locator_domain);
++
++    /* Setup user. */
++    domain = find_domain_by_name(test_ctx->tctx->dom,
++                                 subdomain_name,
++                                 true);
++    assert_non_null(domain);
++    test_ctx->create_subuser1 = true;
++    prepare_user(domain, &users[0], -1000, time(NULL));
++
++    /* Note - DP will only be called once (so, we're not using will_return_always)
++     * because the locator will tell us which domain to look into. For the recv
++     * function, we use always b/c internally it mocks several values.
++     */
++    will_return(__wrap_sss_dp_get_account_send, test_ctx);
++    will_return_always(sss_dp_req_recv, 0);
++
++    will_return(sss_dp_get_account_domain_recv, EOK);
++    will_return(sss_dp_get_account_domain_recv, locator_domain);
++
++    /* Test. */
++    run_user_by_id(test_ctx, NULL, 0, ERR_OK);
++    assert_true(test_ctx->dp_called);
++    check_user(test_ctx, &users[0], domain);
++
++    /* Request the same user again */
++    test_ctx->tctx->done = false;
++    talloc_zfree(test_ctx->result);
++
++    run_user_by_id(test_ctx, NULL, 0, ERR_OK);
++    check_user(test_ctx, &users[0], domain);
++
++    talloc_free(tmp_ctx);
++}
++
+ void test_user_by_id_cache_valid(void **state)
+ {
+     struct cache_req_test_ctx *test_ctx = NULL;
+@@ -1332,6 +1730,7 @@ void test_group_by_id_multiple_domains_found(void **state)
+     /* Mock values. */
+     will_return_always(__wrap_sss_dp_get_account_send, test_ctx);
+     will_return_always(sss_dp_req_recv, 0);
++    will_return_always(sss_dp_get_account_domain_recv, ERR_GET_ACCT_DOM_NOT_SUPPORTED);
+ 
+     /* Test. */
+     run_group_by_id(test_ctx, NULL, 0, ERR_OK);
+@@ -1348,12 +1747,318 @@ void test_group_by_id_multiple_domains_notfound(void **state)
+     /* Mock values. */
+     will_return_always(__wrap_sss_dp_get_account_send, test_ctx);
+     will_return_always(sss_dp_req_recv, 0);
++    will_return_always(sss_dp_get_account_domain_recv, ERR_GET_ACCT_DOM_NOT_SUPPORTED);
+ 
+     /* Test. */
+     run_group_by_id(test_ctx, NULL, 0, ENOENT);
+     assert_true(test_ctx->dp_called);
+ }
+ 
++void test_group_by_id_multiple_domains_locator_cache_valid(void **state)
++{
++    struct cache_req_test_ctx *test_ctx = NULL;
++    struct sss_domain_info *domain = NULL;
++    const char *locator_domain;
++    TALLOC_CTX *tmp_ctx;
++
++    test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx);
++
++    tmp_ctx = talloc_new(test_ctx);
++    assert_non_null(tmp_ctx);
++
++    /* Has to be a talloc ptr, not just const, so it's stealable inside cache_req */
++    locator_domain = talloc_strdup(tmp_ctx, "responder_cache_req_test_d");
++    assert_non_null(locator_domain);
++
++    /* Setup group. */
++    domain = find_domain_by_name(test_ctx->tctx->dom,
++                                 "responder_cache_req_test_d", true);
++    assert_non_null(domain);
++    prepare_group(domain, &groups[0], 1000, time(NULL));
++
++    will_return(sss_dp_get_account_domain_recv, EOK);
++    will_return(sss_dp_get_account_domain_recv, locator_domain);
++    will_return_always(sss_dp_get_account_domain_recv, ERR_GET_ACCT_DOM_NOT_SUPPORTED);
++
++    will_return_always(__wrap_sss_dp_get_account_send, test_ctx);
++    will_return_always(sss_dp_req_recv, EOK);
++
++    /* Test. */
++    run_group_by_id(test_ctx, NULL, 0, ERR_OK);
++
++    /* Even though the locator tells us to skip all domains except d, the domains
++     * are standalone and the result of the locator request is only valid within
++     * the subdomains
++     */
++    assert_true(test_ctx->dp_called);
++    check_group(test_ctx, &groups[0], domain);
++
++    talloc_free(tmp_ctx);
++}
++
++void test_group_by_id_multiple_domains_locator_cache_expired(void **state)
++{
++    struct cache_req_test_ctx *test_ctx = NULL;
++    struct sss_domain_info *domain = NULL;
++    const char *locator_domain;
++    TALLOC_CTX *tmp_ctx;
++
++    test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx);
++
++    tmp_ctx = talloc_new(test_ctx);
++    assert_non_null(tmp_ctx);
++
++    /* Has to be a talloc ptr, not just const, so it's stealable inside cache_req */
++    locator_domain = talloc_strdup(tmp_ctx, "responder_cache_req_test_d");
++    assert_non_null(locator_domain);
++
++    /* Setup group. */
++    domain = find_domain_by_name(test_ctx->tctx->dom,
++                                 "responder_cache_req_test_d", true);
++    assert_non_null(domain);
++    prepare_group(domain, &groups[0], -1000, time(NULL));
++
++    will_return_always(__wrap_sss_dp_get_account_send, test_ctx);
++    will_return_always(sss_dp_req_recv, EOK);
++
++    will_return(sss_dp_get_account_domain_recv, EOK);
++    will_return(sss_dp_get_account_domain_recv, locator_domain);
++    will_return_always(sss_dp_get_account_domain_recv, ERR_GET_ACCT_DOM_NOT_SUPPORTED);
++
++    /* Test. */
++    run_group_by_id(test_ctx, NULL, 0, ERR_OK);
++
++    assert_true(test_ctx->dp_called);
++    check_group(test_ctx, &groups[0], domain);
++
++    talloc_free(tmp_ctx);
++}
++
++void test_group_by_id_sub_domains_locator_cache_valid(void **state)
++{
++    struct cache_req_test_ctx *test_ctx = NULL;
++    struct sss_domain_info *domain = NULL;
++    const char *locator_domain;
++    TALLOC_CTX *tmp_ctx;
++
++    test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx);
++
++    tmp_ctx = talloc_new(test_ctx);
++    assert_non_null(tmp_ctx);
++
++    /* Has to be a talloc ptr, not just const, so it's stealable inside cache_req */
++    locator_domain = talloc_strdup(tmp_ctx, subdomain_name);
++    assert_non_null(locator_domain);
++
++    /* Setup group. */
++    domain = find_domain_by_name(test_ctx->tctx->dom,
++                                 subdomain_name,
++                                 true);
++    assert_non_null(domain);
++    prepare_group(domain, &groups[0], 1000, time(NULL));
++
++    will_return(sss_dp_get_account_domain_recv, EOK);
++    will_return(sss_dp_get_account_domain_recv, locator_domain);
++
++    /* Test. */
++    run_group_by_id(test_ctx, NULL, 0, ERR_OK);
++
++    /* Even though the ID is present in the last domain,
++     * we're not calling sss_dp_get_account_send,
++     * because the locator will cause cache_req to skip
++     * all domains except _d
++     */
++    assert_false(test_ctx->dp_called);
++    check_group(test_ctx, &groups[0], domain);
++
++    talloc_free(tmp_ctx);
++}
++
++void test_group_by_id_sub_domains_locator_cache_expired(void **state)
++{
++    struct cache_req_test_ctx *test_ctx = NULL;
++    struct sss_domain_info *domain = NULL;
++    const char *locator_domain;
++    TALLOC_CTX *tmp_ctx;
++
++    test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx);
++
++    tmp_ctx = talloc_new(test_ctx);
++    assert_non_null(tmp_ctx);
++
++    /* Has to be a talloc ptr, not just const, so it's stealable inside cache_req */
++    locator_domain = talloc_strdup(tmp_ctx, subdomain_name);
++    assert_non_null(locator_domain);
++
++    /* Setup group. */
++    domain = find_domain_by_name(test_ctx->tctx->dom,
++                                 subdomain_name,
++                                 true);
++    assert_non_null(domain);
++    prepare_group(domain, &groups[0], -1000, time(NULL));
++
++    /* Note - DP will only be called once (so, we're not using will_return_always)
++     * because the locator will tell us which domain to look into. For the recv
++     * function, we use always b/c internally it mocks several values.
++     */
++    will_return(__wrap_sss_dp_get_account_send, test_ctx);
++    will_return_always(sss_dp_req_recv, 0);
++
++    will_return(sss_dp_get_account_domain_recv, EOK);
++    will_return(sss_dp_get_account_domain_recv, locator_domain);
++
++    /* Test. */
++    run_group_by_id(test_ctx, NULL, 0, ERR_OK);
++
++    assert_true(test_ctx->dp_called);
++    check_group(test_ctx, &groups[0], domain);
++
++    talloc_free(tmp_ctx);
++}
++
++void test_group_by_id_sub_domains_locator_cache_midpoint(void **state)
++{
++    struct cache_req_test_ctx *test_ctx = NULL;
++    struct sss_domain_info *domain = NULL;
++    const char *locator_domain;
++    TALLOC_CTX *tmp_ctx;
++
++    test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx);
++
++    tmp_ctx = talloc_new(test_ctx);
++    assert_non_null(tmp_ctx);
++
++    /* Has to be a talloc ptr, not just const, so it's stealable inside cache_req */
++    locator_domain = talloc_strdup(tmp_ctx, subdomain_name);
++    assert_non_null(locator_domain);
++
++    /* Setup group. */
++    domain = find_domain_by_name(test_ctx->tctx->dom,
++                                 subdomain_name,
++                                 true);
++    assert_non_null(domain);
++    prepare_group(domain, &groups[0], 50, time(NULL) - 26);
++
++    /* Note - DP will only be called once and we're not waiting
++     * for the results (so, we're not mocking _recv)
++     */
++    will_return(__wrap_sss_dp_get_account_send, test_ctx);
++
++    will_return(sss_dp_get_account_domain_recv, EOK);
++    will_return(sss_dp_get_account_domain_recv, locator_domain);
++
++    /* Test. */
++    run_group_by_id(test_ctx, NULL, 50, ERR_OK);
++
++    assert_true(test_ctx->dp_called);
++    check_group(test_ctx, &groups[0], domain);
++
++    talloc_free(tmp_ctx);
++}
++
++void test_group_by_id_sub_domains_locator_missing_found(void **state)
++{
++    struct cache_req_test_ctx *test_ctx = NULL;
++    struct sss_domain_info *domain = NULL;
++    const char *locator_domain;
++    TALLOC_CTX *tmp_ctx;
++
++    test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx);
++
++    tmp_ctx = talloc_new(test_ctx);
++    assert_non_null(tmp_ctx);
++
++    /* Has to be a talloc ptr, not just const, so it's stealable inside cache_req */
++    locator_domain = talloc_strdup(tmp_ctx, subdomain_name);
++    assert_non_null(locator_domain);
++
++    /* Note - DP will only be called once (so, we're not using will_return_always)
++     * because the locator will tell us which domain to look into. For the recv
++     * function, we use always b/c internally it mocks several values.
++     */
++    will_return(__wrap_sss_dp_get_account_send, test_ctx);
++    will_return_always(sss_dp_req_recv, 0);
++
++    will_return(sss_dp_get_account_domain_recv, EOK);
++    will_return(sss_dp_get_account_domain_recv, locator_domain);
++
++    /* Test. */
++    test_ctx->create_subgroup1 = true;
++    run_group_by_id(test_ctx, NULL, 0, ERR_OK);
++
++    assert_true(test_ctx->dp_called);
++
++    domain = find_domain_by_name(test_ctx->tctx->dom,
++                                 subdomain_name,
++                                 true);
++    assert_non_null(domain);
++    check_group(test_ctx, &groups[0], domain);
++
++    talloc_free(tmp_ctx);
++}
++
++void test_group_by_id_sub_domains_locator_missing_notfound(void **state)
++{
++    struct cache_req_test_ctx *test_ctx = NULL;
++
++    test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx);
++
++    will_return(sss_dp_get_account_domain_recv, ERR_NOT_FOUND);
++
++    /* Test. */
++    run_group_by_id(test_ctx, NULL, 0, ENOENT);
++    assert_false(test_ctx->dp_called);
++}
++
++void test_group_by_id_sub_domains_locator_cache_expired_two_calls(void **state)
++{
++    struct cache_req_test_ctx *test_ctx = NULL;
++    struct sss_domain_info *domain = NULL;
++    const char *locator_domain;
++    TALLOC_CTX *tmp_ctx;
++
++    test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx);
++
++    tmp_ctx = talloc_new(test_ctx);
++    assert_non_null(tmp_ctx);
++
++    /* Has to be a talloc ptr, not just const, so it's stealable inside cache_req */
++    locator_domain = talloc_strdup(tmp_ctx, subdomain_name);
++    assert_non_null(locator_domain);
++
++    /* Setup group. */
++    domain = find_domain_by_name(test_ctx->tctx->dom,
++                                 subdomain_name,
++                                 true);
++    assert_non_null(domain);
++    test_ctx->create_subgroup1 = true;
++    prepare_group(domain, &groups[0], -1000, time(NULL));
++
++    /* Note - DP will only be called once (so, we're not using will_return_always)
++     * because the locator will tell us which domain to look into. For the recv
++     * function, we use always b/c internally it mocks several values.
++     */
++    will_return(__wrap_sss_dp_get_account_send, test_ctx);
++    will_return_always(sss_dp_req_recv, 0);
++
++    will_return(sss_dp_get_account_domain_recv, EOK);
++    will_return(sss_dp_get_account_domain_recv, locator_domain);
++
++    /* Test. */
++    run_group_by_id(test_ctx, NULL, 0, ERR_OK);
++    assert_true(test_ctx->dp_called);
++    check_group(test_ctx, &groups[0], domain);
++
++    /* Request the same group again */
++    test_ctx->tctx->done = false;
++    talloc_zfree(test_ctx->result);
++
++    run_group_by_id(test_ctx, NULL, 0, ERR_OK);
++    check_group(test_ctx, &groups[0], domain);
++
++    talloc_free(tmp_ctx);
++}
++
+ void test_group_by_id_cache_valid(void **state)
+ {
+     struct cache_req_test_ctx *test_ctx = NULL;
+@@ -2311,6 +3016,7 @@ void test_object_by_id_user_multiple_domains_found(void **state)
+     /* Mock values. */
+     will_return_always(__wrap_sss_dp_get_account_send, test_ctx);
+     will_return_always(sss_dp_req_recv, 0);
++    will_return_always(sss_dp_get_account_domain_recv, ERR_GET_ACCT_DOM_NOT_SUPPORTED);
+ 
+     /* Test. */
+     run_object_by_id(test_ctx, NULL, users[0].uid, attrs, 0, ERR_OK);
+@@ -2328,6 +3034,7 @@ void test_object_by_id_user_multiple_domains_notfound(void **state)
+     /* Mock values. */
+     will_return_always(__wrap_sss_dp_get_account_send, test_ctx);
+     will_return_always(sss_dp_req_recv, 0);
++    will_return_always(sss_dp_get_account_domain_recv, ERR_GET_ACCT_DOM_NOT_SUPPORTED);
+ 
+     /* Test. */
+     run_object_by_id(test_ctx, NULL, users[0].uid, attrs, 0, ENOENT);
+@@ -2476,6 +3183,7 @@ void test_object_by_id_group_multiple_domains_found(void **state)
+     /* Mock values. */
+     will_return_always(__wrap_sss_dp_get_account_send, test_ctx);
+     will_return_always(sss_dp_req_recv, 0);
++    will_return_always(sss_dp_get_account_domain_recv, ERR_GET_ACCT_DOM_NOT_SUPPORTED);
+ 
+     /* Test. */
+     run_object_by_id(test_ctx, NULL, groups[0].gid, attrs, 0, ERR_OK);
+@@ -2493,12 +3201,641 @@ void test_object_by_id_group_multiple_domains_notfound(void **state)
+     /* Mock values. */
+     will_return_always(__wrap_sss_dp_get_account_send, test_ctx);
+     will_return_always(sss_dp_req_recv, 0);
++    will_return_always(sss_dp_get_account_domain_recv, ERR_GET_ACCT_DOM_NOT_SUPPORTED);
+ 
+     /* Test. */
+     run_object_by_id(test_ctx, NULL, groups[0].gid, attrs, 0, ENOENT);
+     assert_true(test_ctx->dp_called);
+ }
+ 
++void test_object_by_id_user_multiple_domains_locator_cache_valid(void **state)
++{
++    struct cache_req_test_ctx *test_ctx = NULL;
++    struct sss_domain_info *domain = NULL;
++    const char *locator_domain;
++    TALLOC_CTX *tmp_ctx;
++    const char *attrs[] = SYSDB_PW_ATTRS;
++
++    test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx);
++
++    tmp_ctx = talloc_new(test_ctx);
++    assert_non_null(tmp_ctx);
++
++    /* Has to be a talloc ptr, not just const, so it's stealable inside cache_req */
++    locator_domain = talloc_strdup(tmp_ctx, "responder_cache_req_test_d");
++    assert_non_null(locator_domain);
++
++    /* Setup user. */
++    domain = find_domain_by_name(test_ctx->tctx->dom,
++                                 "responder_cache_req_test_d", true);
++    assert_non_null(domain);
++    prepare_user(domain, &users[0], 1000, time(NULL));
++
++    will_return(sss_dp_get_account_domain_recv, EOK);
++    will_return(sss_dp_get_account_domain_recv, locator_domain);
++    will_return_always(sss_dp_get_account_domain_recv, ERR_GET_ACCT_DOM_NOT_SUPPORTED);
++
++    will_return_always(__wrap_sss_dp_get_account_send, test_ctx);
++    will_return_always(sss_dp_req_recv, EOK);
++
++    /* Test. */
++    run_object_by_id(test_ctx, NULL, users[0].uid, attrs, 0, ERR_OK);
++    /* Even though the locator tells us to skip all domains except d, the domains
++     * are standalone and the result of the locator request is only valid within
++     * the subdomains
++     */
++    assert_true(test_ctx->dp_called);
++    check_user(test_ctx, &users[0], domain);
++
++    talloc_free(tmp_ctx);
++}
++
++void test_object_by_id_user_multiple_domains_locator_cache_expired(void **state)
++{
++    struct cache_req_test_ctx *test_ctx = NULL;
++    struct sss_domain_info *domain = NULL;
++    const char *locator_domain;
++    TALLOC_CTX *tmp_ctx;
++    const char *attrs[] = SYSDB_PW_ATTRS;
++
++    test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx);
++
++    tmp_ctx = talloc_new(test_ctx);
++    assert_non_null(tmp_ctx);
++
++    /* Has to be a talloc ptr, not just const, so it's stealable inside cache_req */
++    locator_domain = talloc_strdup(tmp_ctx, "responder_cache_req_test_d");
++    assert_non_null(locator_domain);
++
++    /* Setup user. */
++    domain = find_domain_by_name(test_ctx->tctx->dom,
++                                 "responder_cache_req_test_d", true);
++    assert_non_null(domain);
++    prepare_user(domain, &users[0], -1000, time(NULL));
++
++    will_return_always(__wrap_sss_dp_get_account_send, test_ctx);
++    will_return_always(sss_dp_req_recv, EOK);
++
++    will_return(sss_dp_get_account_domain_recv, EOK);
++    will_return(sss_dp_get_account_domain_recv, locator_domain);
++    will_return_always(sss_dp_get_account_domain_recv, ERR_GET_ACCT_DOM_NOT_SUPPORTED);
++
++    /* Test. */
++    run_object_by_id(test_ctx, NULL, users[0].uid, attrs, 0, ERR_OK);
++
++    assert_true(test_ctx->dp_called);
++    check_user(test_ctx, &users[0], domain);
++
++    talloc_free(tmp_ctx);
++}
++
++void test_object_by_id_user_sub_domains_locator_cache_valid(void **state)
++{
++    struct cache_req_test_ctx *test_ctx = NULL;
++    struct sss_domain_info *domain = NULL;
++    const char *locator_domain;
++    TALLOC_CTX *tmp_ctx;
++    const char *attrs[] = SYSDB_PW_ATTRS;
++
++    test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx);
++
++    tmp_ctx = talloc_new(test_ctx);
++    assert_non_null(tmp_ctx);
++
++    /* Has to be a talloc ptr, not just const, so it's stealable inside cache_req */
++    locator_domain = talloc_strdup(tmp_ctx, subdomain_name);
++    assert_non_null(locator_domain);
++
++    /* Setup user. */
++    domain = find_domain_by_name(test_ctx->tctx->dom,
++                                 subdomain_name,
++                                 true);
++    assert_non_null(domain);
++    prepare_user(domain, &users[0], 1000, time(NULL));
++
++    will_return(sss_dp_get_account_domain_recv, EOK);
++    will_return(sss_dp_get_account_domain_recv, locator_domain);
++
++    /* Test. */
++    run_object_by_id(test_ctx, NULL, users[0].uid, attrs, 0, ERR_OK);
++
++    /* Even though the ID is present in the last domain,
++     * we're not calling sss_dp_get_account_send,
++     * because the locator will cause cache_req to skip
++     * all domains except _d
++     */
++    assert_false(test_ctx->dp_called);
++    check_user(test_ctx, &users[0], domain);
++
++    talloc_free(tmp_ctx);
++}
++
++void test_object_by_id_user_sub_domains_locator_cache_expired(void **state)
++{
++    struct cache_req_test_ctx *test_ctx = NULL;
++    struct sss_domain_info *domain = NULL;
++    const char *locator_domain;
++    TALLOC_CTX *tmp_ctx;
++    const char *attrs[] = SYSDB_PW_ATTRS;
++
++    test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx);
++
++    tmp_ctx = talloc_new(test_ctx);
++    assert_non_null(tmp_ctx);
++
++    /* Has to be a talloc ptr, not just const, so it's stealable inside cache_req */
++    locator_domain = talloc_strdup(tmp_ctx, subdomain_name);
++    assert_non_null(locator_domain);
++
++    /* Setup user. */
++    domain = find_domain_by_name(test_ctx->tctx->dom,
++                                 subdomain_name,
++                                 true);
++    assert_non_null(domain);
++    prepare_user(domain, &users[0], -1000, time(NULL));
++
++    /* Note - DP will only be called once (so, we're not using will_return_always)
++     * because the locator will tell us which domain to look into. For the recv
++     * function, we use always b/c internally it mocks several values.
++     */
++    will_return(__wrap_sss_dp_get_account_send, test_ctx);
++    will_return_always(sss_dp_req_recv, 0);
++
++    will_return(sss_dp_get_account_domain_recv, EOK);
++    will_return(sss_dp_get_account_domain_recv, locator_domain);
++
++    /* Test. */
++    run_object_by_id(test_ctx, NULL, users[0].uid, attrs, 0, ERR_OK);
++
++    assert_true(test_ctx->dp_called);
++    check_user(test_ctx, &users[0], domain);
++
++    talloc_free(tmp_ctx);
++}
++
++void test_object_by_id_user_sub_domains_locator_cache_midpoint(void **state)
++{
++    struct cache_req_test_ctx *test_ctx = NULL;
++    struct sss_domain_info *domain = NULL;
++    const char *locator_domain;
++    TALLOC_CTX *tmp_ctx;
++    const char *attrs[] = SYSDB_PW_ATTRS;
++
++    test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx);
++
++    tmp_ctx = talloc_new(test_ctx);
++    assert_non_null(tmp_ctx);
++
++    /* Has to be a talloc ptr, not just const, so it's stealable inside cache_req */
++    locator_domain = talloc_strdup(tmp_ctx, subdomain_name);
++    assert_non_null(locator_domain);
++
++    /* Setup user. */
++    domain = find_domain_by_name(test_ctx->tctx->dom,
++                                 subdomain_name,
++                                 true);
++    assert_non_null(domain);
++    prepare_user(domain, &users[0], 50, time(NULL) - 26);
++
++    /* Note - DP will only be called once and we're not waiting
++     * for the results (so, we're not mocking _recv)
++     */
++    will_return(__wrap_sss_dp_get_account_send, test_ctx);
++
++    will_return(sss_dp_get_account_domain_recv, EOK);
++    will_return(sss_dp_get_account_domain_recv, locator_domain);
++
++    /* Test. */
++    run_object_by_id(test_ctx, NULL, users[0].uid, attrs, 50, ERR_OK);
++
++    assert_true(test_ctx->dp_called);
++    check_user(test_ctx, &users[0], domain);
++
++    talloc_free(tmp_ctx);
++}
++
++void test_object_by_id_user_sub_domains_locator_missing_found(void **state)
++{
++    struct cache_req_test_ctx *test_ctx = NULL;
++    struct sss_domain_info *domain = NULL;
++    const char *locator_domain;
++    TALLOC_CTX *tmp_ctx;
++    const char *attrs[] = SYSDB_PW_ATTRS;
++
++    test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx);
++
++    tmp_ctx = talloc_new(test_ctx);
++    assert_non_null(tmp_ctx);
++
++    /* Has to be a talloc ptr, not just const, so it's stealable inside cache_req */
++    locator_domain = talloc_strdup(tmp_ctx, subdomain_name);
++    assert_non_null(locator_domain);
++
++    /* Note - DP will only be called once (so, we're not using will_return_always)
++     * because the locator will tell us which domain to look into. For the recv
++     * function, we use always b/c internally it mocks several values.
++     */
++    will_return(__wrap_sss_dp_get_account_send, test_ctx);
++    will_return_always(sss_dp_req_recv, 0);
++
++    will_return(sss_dp_get_account_domain_recv, EOK);
++    will_return(sss_dp_get_account_domain_recv, locator_domain);
++
++    /* Test. */
++    test_ctx->create_subuser1 = true;
++    run_object_by_id(test_ctx, NULL, users[0].uid, attrs, 0, ERR_OK);
++
++    assert_true(test_ctx->dp_called);
++
++    domain = find_domain_by_name(test_ctx->tctx->dom,
++                                 subdomain_name,
++                                 true);
++    assert_non_null(domain);
++    check_user(test_ctx, &users[0], domain);
++
++    talloc_free(tmp_ctx);
++}
++
++void test_object_by_id_user_sub_domains_locator_missing_notfound(void **state)
++{
++    struct cache_req_test_ctx *test_ctx = NULL;
++    const char *attrs[] = SYSDB_PW_ATTRS;
++
++    test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx);
++
++    will_return(sss_dp_get_account_domain_recv, ERR_NOT_FOUND);
++
++    /* The test won't even ask the DP for the object, just iterate
++     * over the domains using the negative cache and quit
++     */
++    run_object_by_id(test_ctx, NULL, users[0].uid, attrs, 0, ENOENT);
++    assert_false(test_ctx->dp_called);
++}
++
++void test_object_by_id_user_sub_domains_locator_cache_expired_two_calls(void **state)
++{
++    struct cache_req_test_ctx *test_ctx = NULL;
++    struct sss_domain_info *domain = NULL;
++    const char *locator_domain;
++    TALLOC_CTX *tmp_ctx;
++    const char *attrs[] = SYSDB_PW_ATTRS;
++
++    test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx);
++
++    tmp_ctx = talloc_new(test_ctx);
++    assert_non_null(tmp_ctx);
++
++    /* Has to be a talloc ptr, not just const, so it's stealable inside cache_req */
++    locator_domain = talloc_strdup(tmp_ctx, subdomain_name);
++    assert_non_null(locator_domain);
++
++    /* Setup user. */
++    domain = find_domain_by_name(test_ctx->tctx->dom,
++                                 subdomain_name,
++                                 true);
++    assert_non_null(domain);
++    test_ctx->create_subuser1 = true;
++    prepare_user(domain, &users[0], -1000, time(NULL));
++
++    /* Note - DP will only be called once (so, we're not using will_return_always)
++     * because the locator will tell us which domain to look into. For the recv
++     * function, we use always b/c internally it mocks several values.
++     */
++    will_return(__wrap_sss_dp_get_account_send, test_ctx);
++    will_return_always(sss_dp_req_recv, 0);
++
++    will_return(sss_dp_get_account_domain_recv, EOK);
++    will_return(sss_dp_get_account_domain_recv, locator_domain);
++
++    /* Test. */
++    run_object_by_id(test_ctx, NULL, users[0].uid, attrs, 0, EOK);
++    assert_true(test_ctx->dp_called);
++    check_user(test_ctx, &users[0], domain);
++
++    /* Request the same user again */
++    test_ctx->tctx->done = false;
++    talloc_zfree(test_ctx->result);
++
++    run_object_by_id(test_ctx, NULL, users[0].uid, attrs, 0, EOK);
++    check_user(test_ctx, &users[0], domain);
++
++    talloc_free(tmp_ctx);
++}
++
++void test_object_by_id_group_multiple_domains_locator_cache_valid(void **state)
++{
++    struct cache_req_test_ctx *test_ctx = NULL;
++    struct sss_domain_info *domain = NULL;
++    const char *locator_domain;
++    TALLOC_CTX *tmp_ctx;
++    const char *attrs[] = SYSDB_PW_ATTRS;
++
++    test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx);
++
++    tmp_ctx = talloc_new(test_ctx);
++    assert_non_null(tmp_ctx);
++
++    /* Has to be a talloc ptr, not just const, so it's stealable inside cache_req */
++    locator_domain = talloc_strdup(tmp_ctx, "responder_cache_req_test_d");
++    assert_non_null(locator_domain);
++
++    /* Setup group. */
++    domain = find_domain_by_name(test_ctx->tctx->dom,
++                                 "responder_cache_req_test_d", true);
++    assert_non_null(domain);
++    prepare_group(domain, &groups[0], 1000, time(NULL));
++
++    will_return(sss_dp_get_account_domain_recv, EOK);
++    will_return(sss_dp_get_account_domain_recv, locator_domain);
++    will_return_always(sss_dp_get_account_domain_recv, ERR_GET_ACCT_DOM_NOT_SUPPORTED);
++
++    will_return_always(__wrap_sss_dp_get_account_send, test_ctx);
++    will_return_always(sss_dp_req_recv, EOK);
++
++    /* Test. */
++    run_object_by_id(test_ctx, NULL, groups[0].gid, attrs, 0, ERR_OK);
++    /* Even though the locator tells us to skip all domains except d, the domains
++     * are standalone and the result of the locator request is only valid within
++     * the subdomains
++     */
++    assert_true(test_ctx->dp_called);
++    check_group(test_ctx, &groups[0], domain);
++
++    talloc_free(tmp_ctx);
++}
++
++void test_object_by_id_group_multiple_domains_locator_cache_expired(void **state)
++{
++    struct cache_req_test_ctx *test_ctx = NULL;
++    struct sss_domain_info *domain = NULL;
++    const char *locator_domain;
++    TALLOC_CTX *tmp_ctx;
++    const char *attrs[] = SYSDB_PW_ATTRS;
++
++    test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx);
++
++    tmp_ctx = talloc_new(test_ctx);
++    assert_non_null(tmp_ctx);
++
++    /* Has to be a talloc ptr, not just const, so it's stealable inside cache_req */
++    locator_domain = talloc_strdup(tmp_ctx, "responder_cache_req_test_d");
++    assert_non_null(locator_domain);
++
++    /* Setup group. */
++    domain = find_domain_by_name(test_ctx->tctx->dom,
++                                 "responder_cache_req_test_d", true);
++    assert_non_null(domain);
++    prepare_group(domain, &groups[0], -1000, time(NULL));
++
++    will_return_always(__wrap_sss_dp_get_account_send, test_ctx);
++    will_return_always(sss_dp_req_recv, EOK);
++
++    will_return(sss_dp_get_account_domain_recv, EOK);
++    will_return(sss_dp_get_account_domain_recv, locator_domain);
++    will_return_always(sss_dp_get_account_domain_recv, ERR_GET_ACCT_DOM_NOT_SUPPORTED);
++
++    /* Test. */
++    run_object_by_id(test_ctx, NULL, groups[0].gid, attrs, 0, ERR_OK);
++
++    assert_true(test_ctx->dp_called);
++    check_group(test_ctx, &groups[0], domain);
++
++    talloc_free(tmp_ctx);
++}
++
++void test_object_by_id_group_sub_domains_locator_cache_valid(void **state)
++{
++    struct cache_req_test_ctx *test_ctx = NULL;
++    struct sss_domain_info *domain = NULL;
++    const char *locator_domain;
++    TALLOC_CTX *tmp_ctx;
++    const char *attrs[] = SYSDB_PW_ATTRS;
++
++    test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx);
++
++    tmp_ctx = talloc_new(test_ctx);
++    assert_non_null(tmp_ctx);
++
++    /* Has to be a talloc ptr, not just const, so it's stealable inside cache_req */
++    locator_domain = talloc_strdup(tmp_ctx, subdomain_name);
++    assert_non_null(locator_domain);
++
++    /* Setup group. */
++    domain = find_domain_by_name(test_ctx->tctx->dom,
++                                 subdomain_name,
++                                 true);
++    assert_non_null(domain);
++    prepare_group(domain, &groups[0], 1000, time(NULL));
++
++    will_return(sss_dp_get_account_domain_recv, EOK);
++    will_return(sss_dp_get_account_domain_recv, locator_domain);
++
++    /* Test. */
++    run_object_by_id(test_ctx, NULL, groups[0].gid, attrs, 0, ERR_OK);
++
++    /* Even though the ID is present in the last domain,
++     * we're not calling sss_dp_get_account_send,
++     * because the locator will cause cache_req to skip
++     * all domains except _d
++     */
++    assert_false(test_ctx->dp_called);
++    check_group(test_ctx, &groups[0], domain);
++
++    talloc_free(tmp_ctx);
++}
++
++void test_object_by_id_group_sub_domains_locator_cache_expired(void **state)
++{
++    struct cache_req_test_ctx *test_ctx = NULL;
++    struct sss_domain_info *domain = NULL;
++    const char *locator_domain;
++    TALLOC_CTX *tmp_ctx;
++    const char *attrs[] = SYSDB_PW_ATTRS;
++
++    test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx);
++
++    tmp_ctx = talloc_new(test_ctx);
++    assert_non_null(tmp_ctx);
++
++    /* Has to be a talloc ptr, not just const, so it's stealable inside cache_req */
++    locator_domain = talloc_strdup(tmp_ctx, subdomain_name);
++    assert_non_null(locator_domain);
++
++    /* Setup group. */
++    domain = find_domain_by_name(test_ctx->tctx->dom,
++                                 subdomain_name,
++                                 true);
++    assert_non_null(domain);
++    prepare_group(domain, &groups[0], -1000, time(NULL));
++
++    /* Note - DP will only be called once (so, we're not using will_return_always)
++     * because the locator will tell us which domain to look into. For the recv
++     * function, we use always b/c internally it mocks several values.
++     */
++    will_return(__wrap_sss_dp_get_account_send, test_ctx);
++    will_return_always(sss_dp_req_recv, 0);
++
++    will_return(sss_dp_get_account_domain_recv, EOK);
++    will_return(sss_dp_get_account_domain_recv, locator_domain);
++
++    /* Test. */
++    run_object_by_id(test_ctx, NULL, groups[0].gid, attrs, 0, ERR_OK);
++
++    assert_true(test_ctx->dp_called);
++    check_group(test_ctx, &groups[0], domain);
++
++    talloc_free(tmp_ctx);
++}
++
++void test_object_by_id_group_sub_domains_locator_cache_midpoint(void **state)
++{
++    struct cache_req_test_ctx *test_ctx = NULL;
++    struct sss_domain_info *domain = NULL;
++    const char *locator_domain;
++    TALLOC_CTX *tmp_ctx;
++    const char *attrs[] = SYSDB_PW_ATTRS;
++
++    test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx);
++
++    tmp_ctx = talloc_new(test_ctx);
++    assert_non_null(tmp_ctx);
++
++    /* Has to be a talloc ptr, not just const, so it's stealable inside cache_req */
++    locator_domain = talloc_strdup(tmp_ctx, subdomain_name);
++    assert_non_null(locator_domain);
++
++    /* Setup group. */
++    domain = find_domain_by_name(test_ctx->tctx->dom,
++                                 subdomain_name,
++                                 true);
++    assert_non_null(domain);
++    prepare_group(domain, &groups[0], 50, time(NULL) - 26);
++
++    /* Note - DP will only be called once and we're not waiting
++     * for the results (so, we're not mocking _recv)
++     */
++    will_return(__wrap_sss_dp_get_account_send, test_ctx);
++
++    will_return(sss_dp_get_account_domain_recv, EOK);
++    will_return(sss_dp_get_account_domain_recv, locator_domain);
++
++    /* Test. */
++    run_object_by_id(test_ctx, NULL, groups[0].gid, attrs, 50, ERR_OK);
++
++    assert_true(test_ctx->dp_called);
++    check_group(test_ctx, &groups[0], domain);
++
++    talloc_free(tmp_ctx);
++}
++
++void test_object_by_id_group_sub_domains_locator_missing_found(void **state)
++{
++    struct cache_req_test_ctx *test_ctx = NULL;
++    struct sss_domain_info *domain = NULL;
++    const char *locator_domain;
++    TALLOC_CTX *tmp_ctx;
++    const char *attrs[] = SYSDB_PW_ATTRS;
++
++    test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx);
++
++    tmp_ctx = talloc_new(test_ctx);
++    assert_non_null(tmp_ctx);
++
++    /* Has to be a talloc ptr, not just const, so it's stealable inside cache_req */
++    locator_domain = talloc_strdup(tmp_ctx, subdomain_name);
++    assert_non_null(locator_domain);
++
++    /* Note - DP will only be called once (so, we're not using will_return_always)
++     * because the locator will tell us which domain to look into. For the recv
++     * function, we use always b/c internally it mocks several values.
++     */
++    will_return(__wrap_sss_dp_get_account_send, test_ctx);
++    will_return_always(sss_dp_req_recv, 0);
++
++    will_return(sss_dp_get_account_domain_recv, EOK);
++    will_return(sss_dp_get_account_domain_recv, locator_domain);
++
++    /* Test. */
++    test_ctx->create_subgroup1 = true;
++    run_object_by_id(test_ctx, NULL, groups[0].gid, attrs, 0, ERR_OK);
++
++    assert_true(test_ctx->dp_called);
++
++    domain = find_domain_by_name(test_ctx->tctx->dom,
++                                 subdomain_name,
++                                 true);
++    assert_non_null(domain);
++    check_group(test_ctx, &groups[0], domain);
++
++    talloc_free(tmp_ctx);
++}
++
++void test_object_by_id_group_sub_domains_locator_missing_notfound(void **state)
++{
++    struct cache_req_test_ctx *test_ctx = NULL;
++    const char *attrs[] = SYSDB_PW_ATTRS;
++
++    test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx);
++
++    will_return(sss_dp_get_account_domain_recv, ERR_NOT_FOUND);
++
++    /* The test won't even ask the DP for the object, just iterate
++     * over the domains using the negative cache and quit
++     */
++    run_object_by_id(test_ctx, NULL, groups[0].gid, attrs, 0, ENOENT);
++    assert_false(test_ctx->dp_called);
++}
++
++void test_object_by_id_group_sub_domains_locator_cache_expired_two_calls(void **state)
++{
++    struct cache_req_test_ctx *test_ctx = NULL;
++    struct sss_domain_info *domain = NULL;
++    const char *locator_domain;
++    TALLOC_CTX *tmp_ctx;
++    const char *attrs[] = SYSDB_PW_ATTRS;
++
++    test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx);
++
++    tmp_ctx = talloc_new(test_ctx);
++    assert_non_null(tmp_ctx);
++
++    /* Has to be a talloc ptr, not just const, so it's stealable inside cache_req */
++    locator_domain = talloc_strdup(tmp_ctx, subdomain_name);
++    assert_non_null(locator_domain);
++
++    /* Setup group. */
++    domain = find_domain_by_name(test_ctx->tctx->dom,
++                                 subdomain_name,
++                                 true);
++    assert_non_null(domain);
++    test_ctx->create_subgroup1 = true;
++    prepare_group(domain, &groups[0], -1000, time(NULL));
++
++    /* Note - DP will only be called once (so, we're not using will_return_always)
++     * because the locator will tell us which domain to look into. For the recv
++     * function, we use always b/c internally it mocks several values.
++     */
++    will_return(__wrap_sss_dp_get_account_send, test_ctx);
++    will_return_always(sss_dp_req_recv, 0);
++
++    will_return(sss_dp_get_account_domain_recv, EOK);
++    will_return(sss_dp_get_account_domain_recv, locator_domain);
++
++    /* Test. */
++    run_object_by_id(test_ctx, NULL, groups[0].gid, attrs, 0, EOK);
++    assert_true(test_ctx->dp_called);
++    check_group(test_ctx, &groups[0], domain);
++
++    /* Request the same group again */
++    test_ctx->tctx->done = false;
++    talloc_zfree(test_ctx->result);
++
++    run_object_by_id(test_ctx, NULL, groups[0].gid, attrs, 0, EOK);
++    check_group(test_ctx, &groups[0], domain);
++
++    talloc_free(tmp_ctx);
++}
++
+ int main(int argc, const char *argv[])
+ {
+     poptContext pc;
+@@ -2557,6 +3894,24 @@ int main(int argc, const char *argv[])
+         new_multi_domain_test(group_by_id_multiple_domains_found),
+         new_multi_domain_test(group_by_id_multiple_domains_notfound),
+ 
++        new_multi_domain_test(group_by_id_multiple_domains_locator_cache_valid),
++        new_multi_domain_test(group_by_id_multiple_domains_locator_cache_expired),
++        new_subdomain_test(group_by_id_sub_domains_locator_cache_valid),
++        new_subdomain_test(group_by_id_sub_domains_locator_cache_expired),
++        new_subdomain_test(group_by_id_sub_domains_locator_cache_midpoint),
++        new_subdomain_test(group_by_id_sub_domains_locator_missing_found),
++        new_subdomain_test(group_by_id_sub_domains_locator_missing_notfound),
++        new_subdomain_test(group_by_id_sub_domains_locator_cache_expired_two_calls),
++
++        new_multi_domain_test(user_by_id_multiple_domains_locator_cache_valid),
++        new_multi_domain_test(user_by_id_multiple_domains_locator_cache_expired),
++        new_subdomain_test(user_by_id_sub_domains_locator_cache_valid),
++        new_subdomain_test(user_by_id_sub_domains_locator_cache_expired),
++        new_subdomain_test(user_by_id_sub_domains_locator_cache_midpoint),
++        new_subdomain_test(user_by_id_sub_domains_locator_missing_found),
++        new_subdomain_test(user_by_id_sub_domains_locator_missing_notfound),
++        new_subdomain_test(user_by_id_sub_domains_locator_cache_expired_two_calls),
++
+         new_single_domain_test(user_by_recent_filter_valid),
+         new_single_domain_test(users_by_recent_filter_valid),
+         new_single_domain_test(group_by_recent_filter_valid),
+@@ -2603,6 +3958,24 @@ int main(int argc, const char *argv[])
+         new_single_domain_test(object_by_id_group_missing_notfound),
+         new_multi_domain_test(object_by_id_group_multiple_domains_found),
+         new_multi_domain_test(object_by_id_group_multiple_domains_notfound),
++
++        new_multi_domain_test(object_by_id_user_multiple_domains_locator_cache_valid),
++        new_multi_domain_test(object_by_id_user_multiple_domains_locator_cache_expired),
++        new_subdomain_test(object_by_id_user_sub_domains_locator_cache_valid),
++        new_subdomain_test(object_by_id_user_sub_domains_locator_cache_expired),
++        new_subdomain_test(object_by_id_user_sub_domains_locator_cache_midpoint),
++        new_subdomain_test(object_by_id_user_sub_domains_locator_missing_found),
++        new_subdomain_test(object_by_id_user_sub_domains_locator_missing_notfound),
++        new_subdomain_test(object_by_id_user_sub_domains_locator_cache_expired_two_calls),
++
++        new_multi_domain_test(object_by_id_group_multiple_domains_locator_cache_valid),
++        new_multi_domain_test(object_by_id_group_multiple_domains_locator_cache_expired),
++        new_subdomain_test(object_by_id_group_sub_domains_locator_cache_valid),
++        new_subdomain_test(object_by_id_group_sub_domains_locator_cache_expired),
++        new_subdomain_test(object_by_id_group_sub_domains_locator_cache_midpoint),
++        new_subdomain_test(object_by_id_group_sub_domains_locator_missing_found),
++        new_subdomain_test(object_by_id_group_sub_domains_locator_missing_notfound),
++        new_subdomain_test(object_by_id_group_sub_domains_locator_cache_expired_two_calls),
+     };
+ 
+     /* Set debug level to invalid value so we can deside if -d 0 was used. */
+-- 
+2.14.3
+
diff --git a/SOURCES/0082-build-make-curl-required-by-secrets.patch b/SOURCES/0082-build-make-curl-required-by-secrets.patch
deleted file mode 100644
index 0880b3c..0000000
--- a/SOURCES/0082-build-make-curl-required-by-secrets.patch
+++ /dev/null
@@ -1,65 +0,0 @@
-From 1ea81a335baa08746df7daf2707c070271990937 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Pavel=20B=C5=99ezina?= <pbrezina@redhat.com>
-Date: Wed, 22 Mar 2017 12:32:31 +0100
-Subject: [PATCH 82/90] build: make curl required by secrets
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Also remove --disable-libcurl since it doesn't make sense.
-
-Reviewed-by: Lukáš Slebodník <lslebodn@redhat.com>
-Reviewed-by: Simo Sorce <simo@redhat.com>
-Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
-(cherry picked from commit 793f2573b2beaf8b48eab850429482acf68ec2b1)
----
- configure.ac            |  6 +++++-
- src/external/libcurl.m4 | 16 ++--------------
- 2 files changed, 7 insertions(+), 15 deletions(-)
-
-diff --git a/configure.ac b/configure.ac
-index cf5e2557ef0a1bd6374200aa33abea6c509d03aa..80d8ea9ff5785b0d76edbb04f454d0dd8c8a1e6d 100644
---- a/configure.ac
-+++ b/configure.ac
-@@ -201,9 +201,13 @@ if test x$with_secrets = xyes; then
- fi
- 
- if test x$with_kcm = xyes; then
--    m4_include([src/external/libcurl.m4])
-     m4_include([src/external/libuuid.m4])
- fi
-+
-+if test x$with_kcm = xyes -o x$with_secrets = xyes; then
-+    m4_include([src/external/libcurl.m4])
-+fi
-+
- # This variable is defined by external/libcurl.m4, but conditionals
- # must be always evaluated
- AM_CONDITIONAL([BUILD_WITH_LIBCURL],
-diff --git a/src/external/libcurl.m4 b/src/external/libcurl.m4
-index b420b04ad806bd1251f086b773ffe480d39f8bd3..42be308cd1e4b04e736daf887be9b75ea92db80e 100644
---- a/src/external/libcurl.m4
-+++ b/src/external/libcurl.m4
-@@ -1,17 +1,5 @@
--AC_ARG_ENABLE([curl],
--              [AS_HELP_STRING([--disable-curl-support],
--                              [do not build with libcurl support])],
--              [enable_libcurl=$enableval],
--              [enable_libcurl=yes])
--
--found_libcurl="no"
--AS_IF([test x$enable_libcurl = xyes],
--      [PKG_CHECK_MODULES([CURL],
--                         [libcurl],
--                         [found_libcurl=yes],
--                         [AC_MSG_ERROR([
--The libcurl development library was not found.])
--      ])])
-+PKG_CHECK_MODULES([CURL], [libcurl], [found_libcurl=yes],
-+              [AC_MSG_ERROR([The libcurl development library was not found.])])
- 
- AS_IF([test x"$found_libcurl" = xyes],
-     CFLAGS="$CFLAGS $CURL_CFLAGS"
--- 
-2.9.3
-
diff --git a/SOURCES/0083-MAN-Document-how-the-Global-Catalog-is-used-currentl.patch b/SOURCES/0083-MAN-Document-how-the-Global-Catalog-is-used-currentl.patch
new file mode 100644
index 0000000..4a339e0
--- /dev/null
+++ b/SOURCES/0083-MAN-Document-how-the-Global-Catalog-is-used-currentl.patch
@@ -0,0 +1,48 @@
+From 251e4914e55c6b66ab6eabd3b3e2e2b7b49029e3 Mon Sep 17 00:00:00 2001
+From: Jakub Hrozek <jhrozek@redhat.com>
+Date: Sun, 19 Nov 2017 22:31:44 +0100
+Subject: [PATCH 83/83] MAN: Document how the Global Catalog is used currently
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+The existing documentation was outdated. Remove it and document what the
+current patchset adds.
+
+Related:
+https://pagure.io/SSSD/sssd/issue/3468
+
+Reviewed-by: Pavel Březina <pbrezina@redhat.com>
+Reviewed-by: Sumit Bose <sbose@redhat.com>
+(cherry picked from commit a72919af8347b5bbc65a3b1fb3e5d31447240b24)
+---
+ src/man/sssd-ad.5.xml | 13 ++++++++++---
+ 1 file changed, 10 insertions(+), 3 deletions(-)
+
+diff --git a/src/man/sssd-ad.5.xml b/src/man/sssd-ad.5.xml
+index 649042d587de3d3600fff59866681e302c721af8..c4a3fc2b5780eb0f15935a2c38f48418c5f7bb52 100644
+--- a/src/man/sssd-ad.5.xml
++++ b/src/man/sssd-ad.5.xml
+@@ -84,9 +84,16 @@
+             <programlisting>
+ ldap_id_mapping = False
+             </programlisting>
+-            In order to retrieve users and groups using POSIX attributes from trusted
+-            domains, the AD administrator must make sure that the POSIX attributes
+-            are replicated to the Global Catalog.
++            If POSIX attributes should be used, it is recommended for
++            performance reasons that the attributes are also replicated
++            to the Global Catalog. If POSIX attributes are replicated,
++            SSSD will attempt to locate the domain of a requested
++            numerical ID with the help of the Global Catalog and only
++            search that domain. In contrast, if POSIX attributes are not
++            replicated to the Global Catalog, SSSD must search all the
++            domains in the forest sequentially. Please note that that the
++            <quote>cache_first</quote> option might be also helpful in
++            speeding up domainless searches.
+         </para>
+         <para>
+             Users, groups and other entities served by SSSD are always treated as
+-- 
+2.14.3
+
diff --git a/SOURCES/0083-secrets-use-tcurl-in-proxy-provider.patch b/SOURCES/0083-secrets-use-tcurl-in-proxy-provider.patch
deleted file mode 100644
index 7292536..0000000
--- a/SOURCES/0083-secrets-use-tcurl-in-proxy-provider.patch
+++ /dev/null
@@ -1,459 +0,0 @@
-From a53c4afd13d92572b8c0ebb93d0dbe3f7c7bc680 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Pavel=20B=C5=99ezina?= <pbrezina@redhat.com>
-Date: Wed, 22 Feb 2017 10:38:56 +0100
-Subject: [PATCH 83/90] secrets: use tcurl in proxy provider
-
-We switch from http-parser to libcurl for an http client. This gaves us many
-features for free such as tls and http basic authentication support instead
-of implementing it on our own.
-
-Resolves:
-https://pagure.io/SSSD/sssd/issue/3192
-
-Reviewed-by: Simo Sorce <simo@redhat.com>
-Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
-(cherry picked from commit df99d709c8cbef3c378c111944d83b7345e4c1ea)
----
- Makefile.am                            |   3 +
- src/responder/secrets/providers.c      |  20 +++
- src/responder/secrets/proxy.c          | 246 ++++++++++++++++++++++-----------
- src/responder/secrets/secsrv_private.h |   5 +
- 4 files changed, 191 insertions(+), 83 deletions(-)
-
-diff --git a/Makefile.am b/Makefile.am
-index 573b37c52fdeab1add4ea057e1e1844ea4d348a5..4a414f77df999b8b1d81f663fcc18dbd2d6d2dc4 100644
---- a/Makefile.am
-+++ b/Makefile.am
-@@ -1486,6 +1486,8 @@ sssd_secrets_SOURCES = \
-     src/responder/secrets/local.c \
-     src/responder/secrets/proxy.c \
-     src/util/sss_sockets.c \
-+    src/util/sss_iobuf.c \
-+    src/util/tev_curl.c \
-     $(SSSD_RESPONDER_OBJ) \
-     $(SSSD_RESOLV_OBJ) \
-     $(NULL)
-@@ -1497,6 +1499,7 @@ sssd_secrets_LDADD = \
-     $(SYSTEMD_DAEMON_LIBS) \
-     $(CARES_LIBS) \
-     $(SSSD_INTERNAL_LTLIBS) \
-+    $(CURL_LIBS) \
-     $(NULL)
- endif
- 
-diff --git a/src/responder/secrets/providers.c b/src/responder/secrets/providers.c
-index 94831c73036d269addca45c0117811a2c68873fd..80a443d91135447ec8ce8d424b692a6d7e26a907 100644
---- a/src/responder/secrets/providers.c
-+++ b/src/responder/secrets/providers.c
-@@ -22,6 +22,7 @@
- #include "responder/secrets/secsrv_private.h"
- #include "responder/secrets/secsrv_local.h"
- #include "responder/secrets/secsrv_proxy.h"
-+#include "util/sss_iobuf.h"
- #include <jansson.h>
- 
- typedef int (*url_mapper_fn)(struct sec_req_ctx *secreq,
-@@ -387,6 +388,25 @@ int sec_http_reply_with_headers(TALLOC_CTX *mem_ctx, struct sec_data *reply,
-     return EOK;
- }
- 
-+errno_t sec_http_reply_iobuf(TALLOC_CTX *mem_ctx,
-+                             struct sec_data *reply,
-+                             int response_code,
-+                             struct sss_iobuf *response)
-+{
-+    DEBUG(SSSDBG_TRACE_LIBS, "HTTP reply %d\n", response_code);
-+
-+    reply->data = (char *)sss_iobuf_get_data(response);
-+    reply->length = sss_iobuf_get_len(response);
-+
-+    talloc_steal(mem_ctx, reply->data);
-+
-+    if (reply->data == NULL) {
-+        return EINVAL;
-+    }
-+
-+    return EOK;
-+}
-+
- enum sec_http_status_codes sec_errno_to_http_status(errno_t err)
- {
-     DEBUG(SSSDBG_TRACE_LIBS, "Request errno: %d\n", err);
-diff --git a/src/responder/secrets/proxy.c b/src/responder/secrets/proxy.c
-index 3ed03e6086d0de0f6f80de227ffc65ef4067db4f..fe2f0134e233d9a98f499fe563abe0af69762514 100644
---- a/src/responder/secrets/proxy.c
-+++ b/src/responder/secrets/proxy.c
-@@ -23,10 +23,15 @@
- #include "util/crypto/sss_crypto.h"
- #include "resolv/async_resolv.h"
- #include "util/sss_sockets.h"
-+#include "util/sss_iobuf.h"
-+#include "util/tev_curl.h"
-+
-+#define SEC_PROXY_TIMEOUT 5
- 
- struct proxy_context {
-     struct resolv_ctx *resctx;
-     struct confdb_ctx *cdb;
-+    struct tcurl_ctx *tcurl;
- };
- 
- enum proxy_auth_type {
-@@ -216,103 +221,177 @@ int proxy_sec_map_url(TALLOC_CTX *mem_ctx, struct sec_req_ctx *secreq,
-     return EOK;
- }
- 
--int proxy_sec_map_headers(TALLOC_CTX *mem_ctx, struct sec_req_ctx *secreq,
--                          struct proxy_cfg *pcfg, char **req_headers)
-+static errno_t proxy_http_append_header(TALLOC_CTX *mem_ctx,
-+                                        const char *name,
-+                                        const char *value,
-+                                        const char ***_headers,
-+                                        size_t *_num_headers)
- {
--    int ret;
--
--    for (int i = 0; i < secreq->num_headers; i++) {
--        bool forward = false;
--        for (int j = 0; pcfg->fwd_headers[j]; j++) {
--            if (strcasecmp(secreq->headers[i].name,
--                           pcfg->fwd_headers[j]) == 0) {
--                forward = true;
-+    const char **headers = *_headers;
-+    size_t num_headers = *_num_headers;
-+
-+    num_headers++;
-+    headers = talloc_realloc(mem_ctx, headers, const char *,
-+                             num_headers + 1);
-+    if (headers == NULL) {
-+        return ENOMEM;
-+    }
-+
-+    headers[num_headers - 1] = talloc_asprintf(headers, "%s: %s", name, value);
-+    if (headers[num_headers - 1] == NULL) {
-+        return ENOMEM;
-+    }
-+
-+    headers[num_headers] = NULL;
-+
-+    *_headers = headers;
-+    *_num_headers = num_headers;
-+
-+    return EOK;
-+}
-+
-+static const char **
-+proxy_http_create_headers(TALLOC_CTX *mem_ctx,
-+                          struct sec_req_ctx *secreq,
-+                          struct proxy_cfg *pcfg)
-+{
-+    TALLOC_CTX *tmp_ctx;
-+    const char **headers;
-+    size_t num_headers;
-+    errno_t ret;
-+    int i, j;
-+
-+    tmp_ctx = talloc_new(NULL);
-+    if (tmp_ctx == NULL) {
-+        DEBUG(SSSDBG_FATAL_FAILURE, "Out of memory!\n");
-+        return NULL;
-+    }
-+
-+    headers = talloc_zero_array(tmp_ctx, const char *, 1);
-+    if (headers == NULL) {
-+        ret = ENOMEM;
-+        goto done;
-+    }
-+
-+    num_headers = 0;
-+    for (i = 0; i < secreq->num_headers; i++) {
-+        for (j = 0; pcfg->fwd_headers[j]; j++) {
-+            if (strcasecmp(secreq->headers[i].name, pcfg->fwd_headers[j]) == 0) {
-+                DEBUG(SSSDBG_TRACE_LIBS, "Forwarding header %s: %s\n",
-+                      secreq->headers[i].name, secreq->headers[i].value);
-+
-+                ret = proxy_http_append_header(tmp_ctx, secreq->headers[i].name,
-+                                               secreq->headers[i].value,
-+                                               &headers, &num_headers);
-+                if (ret != EOK) {
-+                    goto done;
-+                }
-+
-                 break;
-             }
-         }
--        if (forward) {
--            DEBUG(SSSDBG_TRACE_LIBS, "Forwarding header %s:%s\n",
--                  secreq->headers[i].name, secreq->headers[i].value);
--
--            ret = sec_http_append_header(mem_ctx, req_headers,
--                                         secreq->headers[i].name,
--                                         secreq->headers[i].value);
--            if (ret) {
--                DEBUG(SSSDBG_CRIT_FAILURE,
--                      "Couldn't append header %s\n", secreq->headers[i].name);
--                return ret;
--            }
--        }
-     }
- 
-     if (pcfg->auth_type == PAT_HEADER) {
--        DEBUG(SSSDBG_TRACE_LIBS,
--              "Forwarding header %s\n", pcfg->auth.header.name);
-+        DEBUG(SSSDBG_TRACE_LIBS, "Forwarding header %s\n",
-+              pcfg->auth.header.name);
- 
--        ret = sec_http_append_header(mem_ctx, req_headers,
--                                     pcfg->auth.header.name,
--                                     pcfg->auth.header.value);
--        if (ret) {
--            DEBUG(SSSDBG_CRIT_FAILURE,
--                  "Couldn't append header %s\n", pcfg->auth.header.name);
--            return ret;
-+        ret = proxy_http_append_header(tmp_ctx, pcfg->auth.header.name,
-+                                       pcfg->auth.header.value,
-+                                       &headers, &num_headers);
-+        if (ret != EOK) {
-+            goto done;
-         }
-     }
- 
--    return EOK;
-+    talloc_steal(mem_ctx, headers);
-+
-+    ret = EOK;
-+
-+done:
-+    talloc_free(tmp_ctx);
-+
-+    if (ret != EOK) {
-+        return NULL;
-+    }
-+
-+    return headers;
- }
- 
--static int proxy_http_create_request(TALLOC_CTX *mem_ctx,
--                                     struct sec_req_ctx *secreq,
--                                     struct proxy_cfg *pcfg,
--                                     const char *http_uri,
--                                     struct sec_data **http_req)
-+static errno_t proxy_http_create_request(TALLOC_CTX *mem_ctx,
-+                                         struct sec_req_ctx *secreq,
-+                                         struct proxy_cfg *pcfg,
-+                                         const char *url,
-+                                         struct tcurl_request **_tcurl_req)
- {
--    struct sec_data *req;
--    int ret;
-+    TALLOC_CTX *tmp_ctx;
-+    struct tcurl_request *tcurl_req;
-+    enum tcurl_http_method method;
-+    struct sss_iobuf *body;
-+    const char **headers;
-+    errno_t ret;
- 
--    req = talloc_zero(mem_ctx, struct sec_data);
--    if (!req) return ENOMEM;
-+    tmp_ctx = talloc_new(NULL);
-+    if (tmp_ctx == NULL) {
-+        DEBUG(SSSDBG_FATAL_FAILURE, "Out of memory!\n");
-+        return ENOMEM;
-+    }
- 
--    /* Request-Line */
--    req->data = talloc_asprintf(req, "%s %s HTTP/1.1\r\n",
--                                http_method_str(secreq->method), http_uri);
--    if (!req->data) {
-+    headers = proxy_http_create_headers(tmp_ctx, secreq, pcfg);
-+    if (headers == NULL) {
-+        DEBUG(SSSDBG_CRIT_FAILURE, "Unable to construct HTTP headers!\n");
-         ret = ENOMEM;
-         goto done;
-     }
- 
--    /* Headers */
--    ret = proxy_sec_map_headers(req, secreq, pcfg, &req->data);
--    if (ret) {
--        DEBUG(SSSDBG_CRIT_FAILURE, "Couldn't map headers\n");
-+    body = sss_iobuf_init_readonly(tmp_ctx, (uint8_t *)secreq->body.data,
-+                                   secreq->body.length);
-+    if (body == NULL) {
-+        DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create HTTP body!\n");
-+        ret = ENOMEM;
-         goto done;
-     }
- 
--    /* CRLF separator before body */
--    req->data = talloc_strdup_append_buffer(req->data, "\r\n");
--
--    req->length = strlen(req->data);
-+    switch (secreq->method) {
-+    case HTTP_GET:
-+        method = TCURL_HTTP_GET;
-+        break;
-+    case HTTP_PUT:
-+        method = TCURL_HTTP_PUT;
-+        break;
-+    case HTTP_POST:
-+        method = TCURL_HTTP_POST;
-+        break;
-+    case HTTP_DELETE:
-+        method = TCURL_HTTP_DELETE;
-+        break;
-+    default:
-+        DEBUG(SSSDBG_CRIT_FAILURE, "Unexpected HTTP method: %d\n",
-+              secreq->method);
-+        ret = EINVAL;
-+        goto done;
-+    }
- 
--    /* Message-Body */
--    if (secreq->body.length > 0) {
--        req->data = talloc_realloc_size(req, req->data,
--                                        req->length + secreq->body.length);
--        if (!req->data) {
--            ret = ENOMEM;
--            goto done;
--        }
-+    tcurl_req = tcurl_http(tmp_ctx, method, NULL, url, headers, body);
-+    if (tcurl_req == NULL) {
-+        DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create TCURL request!\n");
-+        ret = ENOMEM;
-+        goto done;
-+    }
- 
--        memcpy(&req->data[req->length],
--               secreq->body.data, secreq->body.length);
--        req->length += secreq->body.length;
-+    /* TCURL will return response buffer also with headers. */
-+    ret = tcurl_req_enable_rawoutput(tcurl_req);
-+    if (ret != EOK) {
-+        goto done;
-     }
- 
--    *http_req = req;
-+    talloc_steal(tcurl_req, body);
-+    *_tcurl_req = talloc_steal(mem_ctx, tcurl_req);
-+
-     ret = EOK;
- 
- done:
--    if (ret) talloc_free(req);
-+    talloc_free(tmp_ctx);
-     return ret;
- }
- 
-@@ -911,8 +990,8 @@ struct tevent_req *proxy_secret_req(TALLOC_CTX *mem_ctx,
- {
-     struct tevent_req *req, *subreq;
-     struct proxy_secret_state *state;
-+    struct tcurl_request *tcurl_req;
-     struct proxy_context *pctx;
--    struct sec_data *http_req;
-     char *http_uri;
-     int ret;
- 
-@@ -942,9 +1021,8 @@ struct tevent_req *proxy_secret_req(TALLOC_CTX *mem_ctx,
-         goto done;
-     }
- 
--
-     ret = proxy_http_create_request(state, state->secreq, state->pcfg,
--                                    http_uri, &http_req);
-+                                    http_uri, &tcurl_req);
-     if (ret) {
-         DEBUG(SSSDBG_CRIT_FAILURE,
-               "proxy_http_create_request failed [%d]: %s\n",
-@@ -952,10 +1030,9 @@ struct tevent_req *proxy_secret_req(TALLOC_CTX *mem_ctx,
-         goto done;
-     }
- 
--
--    subreq = proxy_http_req_send(pctx, state, ev, state->secreq,
--                                 http_uri, http_req);
--    if (!subreq) {
-+    subreq = tcurl_request_send(mem_ctx, ev, pctx->tcurl, tcurl_req,
-+                                SEC_PROXY_TIMEOUT);
-+    if (subreq == NULL) {
-         ret = ENOMEM;
-         goto done;
-     }
-@@ -981,32 +1058,30 @@ static void proxy_secret_req_done(struct tevent_req *subreq)
- {
-     struct tevent_req *req;
-     struct proxy_secret_state *state;
--    struct proxy_http_reply *reply = NULL;
-+    struct sss_iobuf *response;
-+    int http_code;
-     int ret;
- 
-     req = tevent_req_callback_data(subreq, struct tevent_req);
-     state = tevent_req_data(req, struct proxy_secret_state);
- 
--    ret = proxy_http_req_recv(subreq, state, &reply);
-+    ret = tcurl_request_recv(state, subreq, &response, &http_code);
-     talloc_zfree(subreq);
- 
-     if (ret != EOK) {
--        DEBUG(SSSDBG_OP_FAILURE,
--              "proxy_http request failed [%d]: %s\n",
-+        DEBUG(SSSDBG_OP_FAILURE, "proxy_http request failed [%d]: %s\n",
-               ret, sss_strerror(ret));
-         tevent_req_error(req, ret);
-         return;
-     }
- 
--    ret = sec_http_reply_with_headers(state->secreq, &state->secreq->reply,
--                                      reply->status_code, reply->reason_phrase,
--                                      reply->headers, reply->num_headers,
--                                      &reply->body);
-+    ret = sec_http_reply_iobuf(state->secreq, &state->secreq->reply,
-+                               http_code, response);
-     if (ret == EOK) {
-         tevent_req_done(req);
-     } else {
-         DEBUG(SSSDBG_OP_FAILURE,
--              "sec_http_reply_with_headers request failed [%d]: %s\n",
-+              "sec_http_reply_iobuf request failed [%d]: %s\n",
-               ret, sss_strerror(ret));
-         tevent_req_error(req, ret);
-     }
-@@ -1034,6 +1109,11 @@ int proxy_secrets_provider_handle(struct sec_ctx *sctx,
- 
-     pctx->resctx = sctx->resctx;
-     pctx->cdb = sctx->rctx->cdb;
-+    pctx->tcurl = tcurl_init(pctx, sctx->rctx->ev);
-+    if (pctx->tcurl == NULL) {
-+        talloc_free(pctx);
-+        return ENOMEM;
-+    }
- 
-     handle->context = pctx;
- 
-diff --git a/src/responder/secrets/secsrv_private.h b/src/responder/secrets/secsrv_private.h
-index a8544f656517a17fe4576247779bff4850beaf97..2e68628f61a0a8e79cd48fb5a510221e6fc36c70 100644
---- a/src/responder/secrets/secsrv_private.h
-+++ b/src/responder/secrets/secsrv_private.h
-@@ -25,6 +25,7 @@
- #include "config.h"
- #include "responder/common/responder.h"
- #include "responder/secrets/secsrv.h"
-+#include "util/sss_iobuf.h"
- #include <http_parser.h>
- 
- struct sec_kvp {
-@@ -129,6 +130,10 @@ int sec_http_reply_with_headers(TALLOC_CTX *mem_ctx, struct sec_data *reply,
-                                 int status_code, const char *reason,
-                                 struct sec_kvp *headers, int num_headers,
-                                 struct sec_data *body);
-+errno_t sec_http_reply_iobuf(TALLOC_CTX *mem_ctx,
-+                             struct sec_data *reply,
-+                             int response_code,
-+                             struct sss_iobuf *response);
- enum sec_http_status_codes sec_errno_to_http_status(errno_t err);
- 
- int sec_json_to_simple_secret(TALLOC_CTX *mem_ctx,
--- 
-2.9.3
-
diff --git a/SOURCES/0084-p11_child-make-sure-OCSP-checks-are-done.patch b/SOURCES/0084-p11_child-make-sure-OCSP-checks-are-done.patch
new file mode 100644
index 0000000..e2b497f
--- /dev/null
+++ b/SOURCES/0084-p11_child-make-sure-OCSP-checks-are-done.patch
@@ -0,0 +1,54 @@
+From 62275e72ff0b9849c899f0fecea90731fff9da0a Mon Sep 17 00:00:00 2001
+From: Sumit Bose <sbose@redhat.com>
+Date: Thu, 7 Dec 2017 17:08:33 +0100
+Subject: [PATCH 84/86] p11_child: make sure OCSP checks are done
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+If CERT_VerifyCertificateNow() is used with
+'certificateUsageCheckAllUsages' OCSP checks are skipped even if OCSP
+was enabled.
+
+This patch calls CERT_CheckOCSPStatus() explicitly if OCSP checks are
+enabled.
+
+Related to https://pagure.io/SSSD/sssd/issue/3560
+
+Reviewed-by: Fabiano Fidêncio <fidencio@redhat.com>
+(cherry picked from commit 2297cc7d6cd5c38a7d64027165e4e82ca497f418)
+---
+ src/p11_child/p11_child_nss.c | 17 +++++++++++++++++
+ 1 file changed, 17 insertions(+)
+
+diff --git a/src/p11_child/p11_child_nss.c b/src/p11_child/p11_child_nss.c
+index 21c508eb1b1b68b3606d0a5eed36573b01f27a19..bf533f3efe4d680f4c6dbd10a0d2c5a5da371c67 100644
+--- a/src/p11_child/p11_child_nss.c
++++ b/src/p11_child/p11_child_nss.c
+@@ -338,6 +338,23 @@ int do_work(TALLOC_CTX *mem_ctx, const char *nss_db,
+                       PR_GetError(), PORT_ErrorToString(PR_GetError()));
+                 continue;
+             }
++
++            /* with 'certificateUsageCheckAllUsages' set
++             * CERT_VerifyCertificateNow() does not do OCSP so it must be done
++             * explicitly */
++            if (cert_verify_opts->do_ocsp) {
++                rv = CERT_CheckOCSPStatus(handle, cert_list_node->cert,
++                                          PR_Now(), NULL);
++                if (rv != SECSuccess) {
++                    DEBUG(SSSDBG_OP_FAILURE,
++                          "Certificate [%s][%s] failed OCSP check [%d][%s], "
++                          "skipping.\n",
++                          cert_list_node->cert->nickname,
++                          cert_list_node->cert->subjectName,
++                          PR_GetError(), PORT_ErrorToString(PR_GetError()));
++                    continue;
++                }
++            }
+         }
+ 
+         if (key_id_in != NULL) {
+-- 
+2.14.3
+
diff --git a/SOURCES/0084-secrets-remove-http-parser-code-in-proxy-provider.patch b/SOURCES/0084-secrets-remove-http-parser-code-in-proxy-provider.patch
deleted file mode 100644
index 7245f94..0000000
--- a/SOURCES/0084-secrets-remove-http-parser-code-in-proxy-provider.patch
+++ /dev/null
@@ -1,612 +0,0 @@
-From cfb82199afe237b4e892aaf2816db63279d7cb21 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Pavel=20B=C5=99ezina?= <pbrezina@redhat.com>
-Date: Tue, 28 Feb 2017 14:14:40 +0100
-Subject: [PATCH 84/90] secrets: remove http-parser code in proxy provider
-
-We switche to libcurl in previous patch. This just removes the unused code.
-
-Resolves:
-https://pagure.io/SSSD/sssd/issue/3192
-
-Reviewed-by: Simo Sorce <simo@redhat.com>
-Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
-(cherry picked from commit 06744bf5a47d5971a338281c8243b11cf72dac90)
----
- src/responder/secrets/proxy.c | 581 ------------------------------------------
- 1 file changed, 581 deletions(-)
-
-diff --git a/src/responder/secrets/proxy.c b/src/responder/secrets/proxy.c
-index fe2f0134e233d9a98f499fe563abe0af69762514..3c495716010ac468c9e2f1fb6356529a8dbdc614 100644
---- a/src/responder/secrets/proxy.c
-+++ b/src/responder/secrets/proxy.c
-@@ -395,587 +395,6 @@ done:
-     return ret;
- }
- 
--struct proxy_http_request {
--    struct sec_data *data;
--    size_t written;
--};
--
--struct proxy_http_reply {
--    http_parser parser;
--    bool complete;
--
--    int status_code;
--    char *reason_phrase;
--    struct sec_kvp *headers;
--    int num_headers;
--    struct sec_data body;
--
--    size_t received;
--};
--
--struct proxy_http_req_state {
--    struct tevent_context *ev;
--
--    char *proxyname;
--    int port;
--
--    struct resolv_hostent *hostent;
--    int hostidx;
--
--    int sd;
--    struct tevent_fd *fde;
--
--    struct proxy_http_request request;
--    struct proxy_http_reply *reply;
--};
--
--static int proxy_http_req_state_destroy(void *data);
--static void proxy_http_req_gethostname_done(struct tevent_req *subreq);
--static void proxy_http_req_connect_step(struct tevent_req *req);
--static void proxy_http_req_connect_done(struct tevent_req *subreq);
--static void proxy_fd_handler(struct tevent_context *ev, struct tevent_fd *fde,
--                             uint16_t flags, void *ptr);
--
--struct tevent_req *proxy_http_req_send(struct proxy_context *pctx,
--                                       TALLOC_CTX *mem_ctx,
--                                       struct tevent_context *ev,
--                                       struct sec_req_ctx *secreq,
--                                       const char *http_uri,
--                                       struct sec_data *http_req)
--{
--    struct proxy_http_req_state *state;
--    struct http_parser_url parsed;
--    struct tevent_req *req, *subreq;
--    int ret;
--
--    req = tevent_req_create(mem_ctx, &state, struct proxy_http_req_state);
--    if (!req) return NULL;
--
--    state->ev = ev;
--    state->request.data = http_req;
--    state->sd = -1;
--    talloc_set_destructor((TALLOC_CTX *)state,
--                          proxy_http_req_state_destroy);
--
--    /* STEP1: reparse URL to get hostname and port */
--    ret = http_parser_parse_url(http_uri, strlen(http_uri), 0, &parsed);
--    if (ret) {
--        DEBUG(SSSDBG_CRIT_FAILURE, "Failed to parse URL [%s]: %d: %s\n",
--                                   http_uri, ret, sss_strerror(ret));
--        goto done;
--    }
--
--    if (!(parsed.field_set & (1 << UF_HOST))) {
--        DEBUG(SSSDBG_CRIT_FAILURE, "No UF_HOST flag found\n");
--        ret = EINVAL;
--        goto done;
--    }
--    state->proxyname =
--        talloc_strndup(state,
--                       &http_uri[parsed.field_data[UF_HOST].off],
--                       parsed.field_data[UF_HOST].len);
--    if (!state->proxyname) {
--        ret = ENOMEM;
--        goto done;
--    }
--    DEBUG(SSSDBG_TRACE_LIBS, "proxy name: %s\n", state->proxyname);
--
--    if (parsed.field_set & (1 << UF_PORT)) {
--        state->port = parsed.port;
--    } else if (parsed.field_set & (1 << UF_SCHEMA)) {
--        uint16_t off = parsed.field_data[UF_SCHEMA].off;
--        uint16_t len = parsed.field_data[UF_SCHEMA].len;
--
--        if ((len == 5) &&
--            (strncmp("https", &http_uri[off], len) == 0)) {
--            state->port = 443;
--        } else if ((len == 4) &&
--                   (strncmp("http", &http_uri[off], len) == 0)) {
--            state->port = 80;
--        }
--    }
--    DEBUG(SSSDBG_TRACE_LIBS, "proxy port: %d\n", state->port);
--
--    /* STEP2: resolve hostname first */
--    subreq = resolv_gethostbyname_send(state, ev, pctx->resctx,
--                                       state->proxyname, IPV4_FIRST,
--                                       default_host_dbs);
--    if (subreq == NULL) {
--        ret = ENOMEM;
--        goto done;
--    }
--
--    tevent_req_set_callback(subreq, proxy_http_req_gethostname_done, req);
--
--    return req;
--
--done:
--    if (ret == EOK) {
--        tevent_req_done(req);
--    } else {
--        tevent_req_error(req, ret);
--    }
--    tevent_req_post(req, ev);
--
--    return req;
--}
--
--static void proxy_http_req_gethostname_done(struct tevent_req *subreq)
--{
--    struct tevent_req *req;
--    struct proxy_http_req_state *state;
--    int resolv_status;
--    int ret;
--
--    req = tevent_req_callback_data(subreq, struct tevent_req);
--    state = tevent_req_data(req, struct proxy_http_req_state);
--
--    ret = resolv_gethostbyname_recv(subreq, state, &resolv_status, NULL,
--                                    &state->hostent);
--    talloc_zfree(subreq);
--    if (ret != EOK) {
--        if (ret == ENOENT) {
--            /* Empty result, just quit */
--            DEBUG(SSSDBG_TRACE_INTERNAL, "No hostent found\n");
--        } else {
--            DEBUG(SSSDBG_OP_FAILURE,
--                  "Could not resolve fqdn for this machine, error [%d]: %s, "
--                  "resolver returned: [%d]: %s\n", ret, strerror(ret),
--                  resolv_status, resolv_strerror(resolv_status));
--        }
--        goto done;
--    }
--
--    /* EOK */
--    DEBUG(SSSDBG_TRACE_INTERNAL, "Found fqdn: %s\n", state->hostent->name);
--
--    /* STEP3: connect to one of the servers */
--    proxy_http_req_connect_step(req);
--    return;
--
--done:
--    if (ret == EOK) {
--        tevent_req_done(req);
--    } else {
--        tevent_req_error(req, ret);
--    }
--}
--
--static void proxy_http_req_connect_step(struct tevent_req *req)
--{
--    struct proxy_http_req_state *state;
--    struct sockaddr_storage *sockaddr;
--    char *ipaddr;
--    struct tevent_req *subreq;
--    int ret;
--
--    state = tevent_req_data(req, struct proxy_http_req_state);
--
--    if (!state->hostent->addr_list[state->hostidx]) {
--        DEBUG(SSSDBG_CRIT_FAILURE, "No more addresses to try.\n");
--        ret = ERR_SEC_NO_PROXY;
--        goto done;
--    }
--
--    sockaddr = resolv_get_sockaddr_address_index(state, state->hostent,
--                                                 state->port, state->hostidx);
--    if (sockaddr == NULL) {
--        DEBUG(SSSDBG_OP_FAILURE, "resolv_get_sockaddr_address() failed\n");
--        ret = EIO;
--        goto done;
--    }
--
--    if (DEBUG_IS_SET(SSSDBG_TRACE_FUNC)) {
--        ipaddr = resolv_get_string_address_index(state, state->hostent,
--                                                 state->hostidx);
--        if (!ipaddr) {
--            ret = EFAULT;
--            goto done;
--        }
--        DEBUG(SSSDBG_TRACE_FUNC, "Connecting to %s:%d\n",
--              ipaddr, state->port);
--    }
--
--    /* increase idx for next attempt */
--    state->hostidx++;
--
--    subreq = sssd_async_socket_init_send(state, state->ev, sockaddr,
--                                         sizeof(struct sockaddr_storage),
--                                         SEC_NET_TIMEOUT);
--    if (!subreq) {
--        ret = EIO;
--        goto done;
--    }
--    tevent_req_set_callback(subreq, proxy_http_req_connect_done, req);
--    return;
--
--done:
--    if (ret == EOK) {
--        tevent_req_done(req);
--    } else {
--        tevent_req_error(req, ret);
--    }
--}
--
--static void proxy_http_req_connect_done(struct tevent_req *subreq)
--{
--    struct tevent_req *req;
--    struct proxy_http_req_state *state;
--    int ret;
--
--    req = tevent_req_callback_data(subreq, struct tevent_req);
--    state = tevent_req_data(req, struct proxy_http_req_state);
--
--    ret = sssd_async_socket_init_recv(subreq, &state->sd);
--    talloc_zfree(subreq);
--    if (ret != EOK) {
--        DEBUG(SSSDBG_CRIT_FAILURE,
--              "sssd_async_socket_init request failed: [%d]: %s.\n",
--              ret, sss_strerror(ret));
--
--        /* try next server if any */
--        proxy_http_req_connect_step(req);
--        return;
--    }
--
--    /* EOK */
--    DEBUG(SSSDBG_TRACE_FUNC, "Connected to %s\n", state->hostent->name);
--
--    state->fde = tevent_add_fd(state->ev, state, state->sd,
--                               TEVENT_FD_WRITE, proxy_fd_handler,
--                               req);
--    if (!state->fde) {
--        ret = EIO;
--        goto done;
--    }
--
--    return;
--
--done:
--    if (ret == EOK) {
--        tevent_req_done(req);
--    } else {
--        tevent_req_error(req, ret);
--    }
--}
--
--
--int proxy_http_req_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
--                        struct proxy_http_reply **reply)
--{
--    struct proxy_http_req_state *state =
--                tevent_req_data(req, struct proxy_http_req_state);
--
--    TEVENT_REQ_RETURN_ON_ERROR(req);
--
--    *reply = talloc_move(mem_ctx, &state->reply);
--
--    return EOK;
--}
--
--static int proxy_http_req_state_destroy(void *data)
--{
--    struct proxy_http_req_state *state =
--        talloc_get_type(data, struct proxy_http_req_state);
--
--    if (!state) return 0;
--
--    if (state->sd != -1) {
--        DEBUG(SSSDBG_TRACE_FUNC, "closing socket [%d]\n", state->sd);
--        close(state->sd);
--        state->sd = -1;
--    }
--
--    return 0;
--}
--
--static int proxy_wire_send(int fd, struct proxy_http_request *req)
--{
--    struct sec_data data;
--    int ret;
--
--    data.data = req->data->data + req->written;
--    data.length = req->data->length - req->written;
--
--    ret = sec_send_data(fd, &data);
--    if (ret != EOK && ret != EAGAIN) {
--        DEBUG(SSSDBG_CRIT_FAILURE,
--              "sec_send_data failed [%d]: %s\n", ret, sss_strerror(ret));
--        return ret;
--    }
--
--    req->written = req->data->length - data.length;
--    return ret;
--}
--
--static void proxy_fd_send(void *data)
--{
--    struct proxy_http_req_state *state;
--    struct tevent_req * req;
--    int ret;
--
--    req = talloc_get_type(data, struct tevent_req);
--    state = tevent_req_data(req, struct proxy_http_req_state);
--
--    ret = proxy_wire_send(state->sd, &state->request);
--    if (ret == EAGAIN) {
--        /* not all data was sent, loop again */
--        return;
--    }
--    if (ret != EOK) {
--        DEBUG(SSSDBG_FATAL_FAILURE, "Failed to send data, aborting!\n");
--        tevent_req_error(req, ret);
--        return;
--    }
--
--    /* ok all sent, wait for reply now */
--    TEVENT_FD_NOT_WRITEABLE(state->fde);
--    TEVENT_FD_READABLE(state->fde);
--    return;
--}
--
--static bool ph_received_data(struct proxy_http_reply *reply, size_t length)
--{
--    reply->received += length;
--    if (reply->received > SEC_REQUEST_MAX_SIZE) {
--        DEBUG(SSSDBG_FATAL_FAILURE, "Request too big, aborting!\n");
--        return true;
--    }
--    return false;
--}
--
--static void ph_append_string(TALLOC_CTX *memctx, char **dest,
--                             const char *src, size_t len)
--{
--    if (*dest) {
--        *dest = talloc_strndup_append_buffer(*dest, src, len);
--    } else {
--        *dest = talloc_strndup(memctx, src, len);
--    }
--}
--
--static int ph_on_message_begin(http_parser *parser)
--{
--    DEBUG(SSSDBG_TRACE_INTERNAL, "HTTP Message parsing begins\n");
--    return 0;
--}
--
--#if ((HTTP_PARSER_VERSION_MAJOR >= 2) && (HTTP_PARSER_VERSION_MINOR >= 2))
--static int ph_on_status(http_parser *parser, const char *at, size_t length)
--{
--    struct proxy_http_reply *reply =
--        talloc_get_type(parser->data, struct proxy_http_reply);
--
--    if (ph_received_data(reply, length)) return -1;
--
--    ph_append_string(reply, &reply->reason_phrase, at, length);
--    if (!reply->reason_phrase) {
--        DEBUG(SSSDBG_FATAL_FAILURE,
--              "Failed to store reason phrase, aborting client!\n");
--        return -1;
--    }
--
--    return 0;
--}
--#endif
--
--static int ph_on_header_field(http_parser *parser,
--                              const char *at, size_t length)
--{
--    struct proxy_http_reply *reply =
--        talloc_get_type(parser->data, struct proxy_http_reply);
--    int n = reply->num_headers;
--
--    if (ph_received_data(reply, length)) return -1;
--
--    if (!reply->headers) {
--        reply->headers = talloc_zero_array(reply, struct sec_kvp, 10);
--    } else if ((n % 10 == 0) &&
--               (reply->headers[n - 1].value)) {
--        reply->headers = talloc_realloc(reply, reply->headers,
--                                        struct sec_kvp, n + 10);
--        if (reply->headers) {
--            memset(&reply->headers[n], 0, sizeof(struct sec_kvp) * 10);
--        }
--    }
--    if (!reply->headers) {
--        DEBUG(SSSDBG_FATAL_FAILURE,
--              "Failed to store headers, aborting client!\n");
--        return -1;
--    }
--
--    if (!n || reply->headers[n - 1].value) {
--        /* new field */
--        n++;
--    }
--    ph_append_string(reply->headers, &reply->headers[n - 1].name, at, length);
--    if (!reply->headers[n - 1].name) {
--        DEBUG(SSSDBG_FATAL_FAILURE,
--              "Failed to store header name, aborting client!\n");
--        return -1;
--    }
--
--    return 0;
--}
--
--static int ph_on_header_value(http_parser *parser,
--                              const char *at, size_t length)
--{
--    struct proxy_http_reply *reply =
--        talloc_get_type(parser->data, struct proxy_http_reply);
--    int n = reply->num_headers;
--
--    if (ph_received_data(reply, length)) return -1;
--
--    if (!reply->headers) {
--        DEBUG(SSSDBG_FATAL_FAILURE,
--              "Invalid headers pointer, aborting client!\n");
--        return -1;
--    }
--
--    if (reply->headers[n].name && !reply->headers[n].value) {
--        /* we increment on new value */
--        n = ++reply->num_headers;
--    }
--
--    ph_append_string(reply->headers, &reply->headers[n - 1].value, at, length);
--    if (!reply->headers[n - 1].value) {
--        DEBUG(SSSDBG_FATAL_FAILURE,
--              "Failed to store header value, aborting client!\n");
--        return -1;
--    }
--
--    return 0;
--}
--
--static int ph_on_headers_complete(http_parser *parser)
--{
--    /* TODO: if message has no body we should return 1 */
--    return 0;
--}
--
--static int ph_on_body(http_parser *parser, const char *at, size_t length)
--{
--    struct proxy_http_reply *reply =
--        talloc_get_type(parser->data, struct proxy_http_reply);
--
--    if (ph_received_data(reply, length)) return -1;
--
--    /* FIXME: body may be binary */
--    ph_append_string(reply, &reply->body.data, at, length);
--    if (!reply->body.data) {
--        DEBUG(SSSDBG_FATAL_FAILURE,
--              "Failed to store body, aborting!\n");
--        return -1;
--    }
--    reply->body.length += length;
--
--    return 0;
--}
--
--static int ph_on_message_complete(http_parser *parser)
--{
--    struct proxy_http_reply *reply =
--        talloc_get_type(parser->data, struct proxy_http_reply);
--
--    reply->status_code = parser->status_code;
--    reply->complete = true;
--
--    return 0;
--}
--
--static http_parser_settings ph_callbacks = {
--    .on_message_begin = ph_on_message_begin,
--#if ((HTTP_PARSER_VERSION_MAJOR >= 2) && (HTTP_PARSER_VERSION_MINOR >= 2))
--    .on_status = ph_on_status,
--#endif
--    .on_header_field = ph_on_header_field,
--    .on_header_value = ph_on_header_value,
--    .on_headers_complete = ph_on_headers_complete,
--    .on_body = ph_on_body,
--    .on_message_complete = ph_on_message_complete
--};
--
--static void proxy_fd_recv(void *data)
--{
--    char buffer[SEC_PACKET_MAX_RECV_SIZE];
--    struct sec_data packet = { buffer,
--                               SEC_PACKET_MAX_RECV_SIZE };
--    struct proxy_http_req_state *state;
--    struct tevent_req *req;
--    bool must_complete = false;
--    int ret;
--
--    req = talloc_get_type(data, struct tevent_req);
--    state = tevent_req_data(req, struct proxy_http_req_state);
--
--    if (!state->reply) {
--        /* A new reply */
--        state->reply = talloc_zero(state, struct proxy_http_reply);
--        if (!state->reply) {
--            DEBUG(SSSDBG_FATAL_FAILURE, "Failed to allocate reply, aborting!\n");
--            tevent_req_error(req, ENOMEM);
--            return;
--        }
--        http_parser_init(&state->reply->parser, HTTP_RESPONSE);
--        state->reply->parser.data = state->reply;
--    }
--
--    ret = sec_recv_data(state->sd, &packet);
--    switch (ret) {
--    case ENODATA:
--        DEBUG(SSSDBG_TRACE_ALL, "Server closed connection.\n");
--        /* if we got no content length and the request is not complete,
--         * then 0 length will indicate EOF to the parser, otherwise we
--         * have an error */
--        must_complete = true;
--        break;
--    case EAGAIN:
--        DEBUG(SSSDBG_TRACE_ALL,
--              "Interrupted before any data could be read, retry later\n");
--        return;
--    case EOK:
--        /* all fine */
--        break;
--    default:
--        DEBUG(SSSDBG_FATAL_FAILURE,
--              "Failed to receive data (%d, %s), aborting\n",
--              ret, sss_strerror(ret));
--        tevent_req_error(req, EIO);
--        return;
--    }
--
--    ret = http_parser_execute(&state->reply->parser, &ph_callbacks,
--                              packet.data, packet.length);
--    if (ret != packet.length) {
--        DEBUG(SSSDBG_FATAL_FAILURE,
--              "Failed to parse request, aborting!\n");
--        tevent_req_error(req, EIO);
--        return;
--    }
--
--    if (!state->reply->complete) {
--        if (must_complete) {
--            tevent_req_error(req, EIO);
--        }
--        return;
--    }
--
--    /* do not read anymore, server is done sending */
--    TEVENT_FD_NOT_READABLE(state->fde);
--    tevent_req_done(req);
--}
--
--static void proxy_fd_handler(struct tevent_context *ev, struct tevent_fd *fde,
--                             uint16_t flags, void *data)
--{
--    if (flags & TEVENT_FD_READ) {
--        proxy_fd_recv(data);
--    } else if (flags & TEVENT_FD_WRITE) {
--        proxy_fd_send(data);
--    }
--}
--
- struct proxy_secret_state {
-     struct tevent_context *ev;
-     struct sec_req_ctx *secreq;
--- 
-2.9.3
-
diff --git a/SOURCES/0085-IPA-Include-SYSDB_OBJECTCATEGORY-not-OBJECTCLASS-in-.patch b/SOURCES/0085-IPA-Include-SYSDB_OBJECTCATEGORY-not-OBJECTCLASS-in-.patch
new file mode 100644
index 0000000..49fffa3
--- /dev/null
+++ b/SOURCES/0085-IPA-Include-SYSDB_OBJECTCATEGORY-not-OBJECTCLASS-in-.patch
@@ -0,0 +1,43 @@
+From 0f707b5f99f4cc17b61026e7a0e7787e776fae87 Mon Sep 17 00:00:00 2001
+From: Jakub Hrozek <jhrozek@redhat.com>
+Date: Wed, 6 Dec 2017 15:45:13 +0100
+Subject: [PATCH 85/86] IPA: Include SYSDB_OBJECTCATEGORY, not OBJECTCLASS in
+ cache search results
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+The function get_object_from_cache() returns an ldb_message that is
+passed to apply_subdomain_homedir() which expects SYSDB_OBJECTCATEGORY
+to be present in the message, otherwise it errors out.
+
+However, get_object_from_cache() was reading only SYSDB_OBJECTCLASS.
+
+This patch changes get_object_from_cache() to ready
+SYSDB_OBJECTCATEGORY.
+
+Resolves:
+https://pagure.io/SSSD/sssd/issue/3599
+
+Reviewed-by: Fabiano Fidêncio <fidencio@redhat.com>
+(cherry picked from commit dc49e07a0dbbbf3d69d09a7c6f236d82c86c7def)
+---
+ src/providers/ipa/ipa_subdomains_id.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/src/providers/ipa/ipa_subdomains_id.c b/src/providers/ipa/ipa_subdomains_id.c
+index 2ba9813a44b4d914d9c2ef7a1a7504546f52954c..d40671086854f9c1a3f8bc7fc711009298dc31c8 100644
+--- a/src/providers/ipa/ipa_subdomains_id.c
++++ b/src/providers/ipa/ipa_subdomains_id.c
+@@ -965,7 +965,7 @@ errno_t get_object_from_cache(TALLOC_CTX *mem_ctx,
+     const char *attrs[] = { SYSDB_NAME,
+                             SYSDB_UIDNUM,
+                             SYSDB_SID_STR,
+-                            SYSDB_OBJECTCLASS,
++                            SYSDB_OBJECTCATEGORY,
+                             SYSDB_UUID,
+                             SYSDB_GHOST,
+                             SYSDB_HOMEDIR,
+-- 
+2.14.3
+
diff --git a/SOURCES/0085-secrets-allow-to-configure-certificate-check.patch b/SOURCES/0085-secrets-allow-to-configure-certificate-check.patch
deleted file mode 100644
index dd9bea7..0000000
--- a/SOURCES/0085-secrets-allow-to-configure-certificate-check.patch
+++ /dev/null
@@ -1,254 +0,0 @@
-From d35f47a4e50feeb2b54c1621d0c2f5b15cd275eb Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Pavel=20B=C5=99ezina?= <pbrezina@redhat.com>
-Date: Tue, 28 Feb 2017 11:47:32 +0100
-Subject: [PATCH 85/90] secrets: allow to configure certificate check
-
-Some users may want to use TLS with unverified peer (for example if
-they use self-signed certificate) or if unverified hostname (if
-certificate hostname does not match with the real hostname). On the
-other side it may be useful to point to a directory containing custom
-certificate authorities.
-
-This patch add three new options to secrets responder:
-verify_peer => peer's certificate must be valid
-verify_host => hostnames must match
-capath => path to directory containing CA certs
-cacert => ca certificate
-cert => client certificate
-key => client private key
-
-Resolves:
-https://pagure.io/SSSD/sssd/issue/3192
-
-Reviewed-by: Simo Sorce <simo@redhat.com>
-Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
-(cherry picked from commit 720e1a5b95a953a0f1c8315bbb7c9c1edf9fb417)
----
- src/config/SSSDConfig/__init__.py.in |  6 +++
- src/config/cfg_rules.ini             |  6 +++
- src/config/etc/sssd.api.conf         |  6 +++
- src/man/sssd-secrets.5.xml           | 76 ++++++++++++++++++++++++++++++++++++
- src/responder/secrets/proxy.c        | 55 ++++++++++++++++++++++++++
- 5 files changed, 149 insertions(+)
-
-diff --git a/src/config/SSSDConfig/__init__.py.in b/src/config/SSSDConfig/__init__.py.in
-index 211338778e81c1c60ffb3cdbc67c9619343d7798..75515ab5c68822538728900482296b9159e1547e 100644
---- a/src/config/SSSDConfig/__init__.py.in
-+++ b/src/config/SSSDConfig/__init__.py.in
-@@ -137,6 +137,12 @@ option_strings = {
-     'forward_headers': _('The list of the headers to forward to the Custodia server together with the request'),
-     'username': _('The username to use when authenticating to a Custodia server using basic_auth'),
-     'password': _('The password to use when authenticating to a Custodia server using basic_auth'),
-+    'verify_peer': _('If true peer\'s certificate is verified if proxy_url uses https protocol'),
-+    'verify_host': _('If false peer\'s certificate may contain different hostname then proxy_url when https protocol is used'),
-+    'capath': _('Path to directory where certificate authority certificates are stored'),
-+    'cacert': _('Path to file containing server\'s CA certificate'),
-+    'cert': _('Path to file containing client\'s certificate'),
-+    'key': _('Path to file containing client\'s private key'),
- 
-     # [provider]
-     'id_provider' : _('Identity provider'),
-diff --git a/src/config/cfg_rules.ini b/src/config/cfg_rules.ini
-index 1a749db754cedd87f263f7ae596d6f8238bb4357..e47ff33242d6a9e5979fe0eb8eea14c2af28685a 100644
---- a/src/config/cfg_rules.ini
-+++ b/src/config/cfg_rules.ini
-@@ -265,6 +265,12 @@ option = auth_header_value
- option = forward_headers
- option = username
- option = password
-+option = verify_peer
-+option = verify_host
-+option = capath
-+option = cacert
-+option = cert
-+option = key
- 
- # KCM responder
- [rule/allowed_kcm_options]
-diff --git a/src/config/etc/sssd.api.conf b/src/config/etc/sssd.api.conf
-index a1a0c2992925a4c7df86832117eec2a0cf7894c9..f86589ecefa0b9e046aba781ded107f8e94395d6 100644
---- a/src/config/etc/sssd.api.conf
-+++ b/src/config/etc/sssd.api.conf
-@@ -114,6 +114,12 @@ auth_header_value = str, None, false
- forward_headers = list, None, false
- username = str, None, false
- password = str, None, false
-+verify_peer = bool, None, false
-+verify_host = bool, None, false
-+capath = str, None, false
-+cacert = str, None, false
-+cert = str, None, false
-+key = str, None, false
- 
- [provider]
- #Available provider types
-diff --git a/src/man/sssd-secrets.5.xml b/src/man/sssd-secrets.5.xml
-index 80e9c405921e1fb46a3d172d9873deebfa5ed2ce..44a86c3fb56a8bdebebd01e9f49ad171986282a4 100644
---- a/src/man/sssd-secrets.5.xml
-+++ b/src/man/sssd-secrets.5.xml
-@@ -273,6 +273,82 @@ systemctl enable sssd-secrets.service
-                 </para>
-                 </listitem>
-             </varlistentry>
-+            <varlistentry>
-+                <term>verify_peer (boolean)</term>
-+                <listitem>
-+                <para>
-+                    Whether peer's certificate should be verified and valid
-+                    if HTTPS protocol is used with the proxy provider.
-+                </para>
-+                <para>
-+                    Default: true
-+                </para>
-+                </listitem>
-+            </varlistentry>
-+            <varlistentry>
-+                <term>verify_host (boolean)</term>
-+                <listitem>
-+                <para>
-+                    Whether peer's hostname must match with hostname in
-+                    its certificate if HTTPS protocol is used with the
-+                    proxy provider.
-+                </para>
-+                <para>
-+                    Default: true
-+                </para>
-+                </listitem>
-+            </varlistentry>
-+            <varlistentry>
-+                <term>capath (string)</term>
-+                <listitem>
-+                <para>
-+                    Path to directory containing stored certificate authority
-+                    certificates. System default path is used if this option is
-+                    not set.
-+                </para>
-+                <para>
-+                    Default: not set
-+                </para>
-+                </listitem>
-+            </varlistentry>
-+            <varlistentry>
-+                <term>cacert (string)</term>
-+                <listitem>
-+                <para>
-+                    Path to file containing server's certificate authority
-+                    certificate. If this option is not set then the CA's
-+                    certificate is looked up in <quote>capath</quote>.
-+                </para>
-+                <para>
-+                    Default: not set
-+                </para>
-+                </listitem>
-+            </varlistentry>
-+            <varlistentry>
-+                <term>cert (string)</term>
-+                <listitem>
-+                <para>
-+                    Path to file containing client's certificate if required
-+                    by the server. This file may also contain private key or
-+                    the private key may be in separate file set with
-+                    <quote>key</quote>.
-+                </para>
-+                <para>
-+                    Default: not set
-+                </para>
-+                </listitem>
-+            </varlistentry>
-+            <varlistentry>
-+                <term>key (string)</term>
-+                <listitem>
-+                <para>
-+                    Path to file containing client's private key.
-+                </para>
-+                <para>
-+                    Default: not set
-+                </para>
-+                </listitem>
-+            </varlistentry>
-         </variablelist>
-     </refsect1>
-     <refsect1 id='restapi'>
-diff --git a/src/responder/secrets/proxy.c b/src/responder/secrets/proxy.c
-index 3c495716010ac468c9e2f1fb6356529a8dbdc614..240a1de1e431d511a1eca24d8b463c37ba893e7b 100644
---- a/src/responder/secrets/proxy.c
-+++ b/src/responder/secrets/proxy.c
-@@ -59,6 +59,13 @@ struct proxy_cfg {
-         struct pat_basic_auth basic;
-         struct pat_header header;
-     } auth;
-+
-+    char *key;
-+    char *cert;
-+    char *cacert;
-+    char *capath;
-+    bool verify_peer;
-+    bool verify_host;
- };
- 
- static int proxy_get_config_string(struct proxy_context *pctx,
-@@ -129,6 +136,38 @@ static int proxy_sec_get_cfg(struct proxy_context *pctx,
-         }
-     }
- 
-+    ret = confdb_get_bool(pctx->cdb, secreq->cfg_section, "verify_peer",
-+                          true, &cfg->verify_peer);
-+    if (ret) goto done;
-+    DEBUG(SSSDBG_CONF_SETTINGS, "verify_peer: %s\n",
-+          (&cfg->verify_peer ? "true" : "false"));
-+
-+    ret = confdb_get_bool(pctx->cdb, secreq->cfg_section, "verify_host",
-+                          true, &cfg->verify_host);
-+    if (ret) goto done;
-+    DEBUG(SSSDBG_CONF_SETTINGS, "verify_host: %s\n",
-+          (&cfg->verify_host ? "true" : "false"));
-+
-+    ret = proxy_get_config_string(pctx, cfg, false, secreq,
-+                                  "capath", &cfg->capath);
-+    if (ret) goto done;
-+    DEBUG(SSSDBG_CONF_SETTINGS, "capath: %s\n", cfg->capath);
-+
-+    ret = proxy_get_config_string(pctx, cfg, false, secreq,
-+                                  "cacert", &cfg->cacert);
-+    if (ret) goto done;
-+    DEBUG(SSSDBG_CONF_SETTINGS, "cacert: %s\n", cfg->cacert);
-+
-+    ret = proxy_get_config_string(pctx, cfg, false, secreq,
-+                                  "cert", &cfg->cert);
-+    if (ret) goto done;
-+    DEBUG(SSSDBG_CONF_SETTINGS, "cert: %s\n", cfg->cert);
-+
-+    ret = proxy_get_config_string(pctx, cfg, false, secreq,
-+                                  "key", &cfg->key);
-+    if (ret) goto done;
-+    DEBUG(SSSDBG_CONF_SETTINGS, "key: %s\n", cfg->key);
-+
-     ret = confdb_get_string_as_list(pctx->cdb, cfg, secreq->cfg_section,
-                                     "forward_headers", &cfg->fwd_headers);
-     if ((ret != 0) && (ret != ENOENT)) goto done;
-@@ -385,6 +424,22 @@ static errno_t proxy_http_create_request(TALLOC_CTX *mem_ctx,
-         goto done;
-     }
- 
-+    /* Set TLS settings to verify peer.
-+     * This has no effect for HTTP protocol so we can set it anyway. */
-+    ret = tcurl_req_verify_peer(tcurl_req, pcfg->capath, pcfg->cacert,
-+                                pcfg->verify_peer, pcfg->verify_host);
-+    if (ret != EOK) {
-+        goto done;
-+    }
-+
-+    /* Set client's certificate if required. */
-+    if (pcfg->cert != NULL) {
-+        ret = tcurl_req_set_client_cert(tcurl_req, pcfg->cert, pcfg->key);
-+        if (ret != EOK) {
-+            goto done;
-+        }
-+    }
-+
-     talloc_steal(tcurl_req, body);
-     *_tcurl_req = talloc_steal(mem_ctx, tcurl_req);
- 
--- 
-2.9.3
-
diff --git a/SOURCES/0086-nss-idmap-allow-NULL-result-in-_timeout-calls.patch b/SOURCES/0086-nss-idmap-allow-NULL-result-in-_timeout-calls.patch
new file mode 100644
index 0000000..95bdd75
--- /dev/null
+++ b/SOURCES/0086-nss-idmap-allow-NULL-result-in-_timeout-calls.patch
@@ -0,0 +1,113 @@
+From 2a3accc78bb9658401b33ad5a3a2f1bc4bc3c269 Mon Sep 17 00:00:00 2001
+From: Sumit Bose <sbose@redhat.com>
+Date: Thu, 7 Dec 2017 17:42:45 +0100
+Subject: [PATCH 86/86] nss-idmap: allow NULL result in *_timeout calls
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+To make the *_timeout calls more resilient checks are added if the
+result parameter is NULL. It will not be used in this case.
+
+Related to https://pagure.io/SSSD/sssd/issue/2478
+
+Reviewed-by: Fabiano Fidêncio <fidencio@redhat.com>
+Reviewed-by: Alexander Bokovoy <abokovoy@redhat.com>
+(cherry picked from commit bba068c535d23eebff61f592bddb3a6438446d6f)
+---
+ src/sss_client/idmap/sss_nss_ex.c | 46 +++++++++++++++++++++++++++------------
+ 1 file changed, 32 insertions(+), 14 deletions(-)
+
+diff --git a/src/sss_client/idmap/sss_nss_ex.c b/src/sss_client/idmap/sss_nss_ex.c
+index dcd9619a8b07ced7498f61b7e809fa46ebffe09e..861b1e1e92db4f7e6e8d74a812dc3c9220711773 100644
+--- a/src/sss_client/idmap/sss_nss_ex.c
++++ b/src/sss_client/idmap/sss_nss_ex.c
+@@ -367,13 +367,17 @@ int sss_nss_getpwnam_timeout(const char *name, struct passwd *pwd,
+         return ret;
+     }
+ 
+-    *result = NULL;
+-
+     ret = sss_get_ex(&inp, flags, timeout);
+     free(discard_const(inp.rd.data));
+-    if (ret == 0) {
+-        *result = inp.result.pwrep.result;
++
++    if (result != NULL) {
++        if (ret == 0) {
++            *result = inp.result.pwrep.result;
++        } else {
++            *result = NULL;
++        }
+     }
++
+     return ret;
+ }
+ 
+@@ -395,12 +399,17 @@ int sss_nss_getpwuid_timeout(uid_t uid, struct passwd *pwd,
+ 
+     SAFEALIGN_COPY_UINT32(&req_data[0], &uid, NULL);
+     SAFEALIGN_COPY_UINT32(&req_data[1], &flags, NULL);
+-    *result = NULL;
+ 
+     ret = sss_get_ex(&inp, flags, timeout);
+-    if (ret == 0) {
+-        *result = inp.result.pwrep.result;
++
++    if (result != NULL) {
++        if (ret == 0) {
++            *result = inp.result.pwrep.result;
++        } else {
++            *result = NULL;
++        }
+     }
++
+     return ret;
+ }
+ 
+@@ -421,13 +430,17 @@ int sss_nss_getgrnam_timeout(const char *name, struct group *grp,
+         return ret;
+     }
+ 
+-    *result = NULL;
+-
+     ret = sss_get_ex(&inp, flags, timeout);
+     free(discard_const(inp.rd.data));
+-    if (ret == 0) {
+-        *result = inp.result.grrep.result;
++
++    if (result != NULL) {
++        if (ret == 0) {
++            *result = inp.result.grrep.result;
++        } else {
++            *result = NULL;
++        }
+     }
++
+     return ret;
+ }
+ 
+@@ -448,12 +461,17 @@ int sss_nss_getgrgid_timeout(gid_t gid, struct group *grp,
+ 
+     SAFEALIGN_COPY_UINT32(&req_data[0], &gid, NULL);
+     SAFEALIGN_COPY_UINT32(&req_data[1], &flags, NULL);
+-    *result = NULL;
+ 
+     ret = sss_get_ex(&inp, flags, timeout);
+-    if (ret == 0) {
+-        *result = inp.result.grrep.result;
++
++    if (result != NULL) {
++        if (ret == 0) {
++            *result = inp.result.grrep.result;
++        } else {
++            *result = NULL;
++        }
+     }
++
+     return ret;
+ }
+ 
+-- 
+2.14.3
+
diff --git a/SOURCES/0086-secrets-support-HTTP-basic-authentication-with-proxy.patch b/SOURCES/0086-secrets-support-HTTP-basic-authentication-with-proxy.patch
deleted file mode 100644
index f048f94..0000000
--- a/SOURCES/0086-secrets-support-HTTP-basic-authentication-with-proxy.patch
+++ /dev/null
@@ -1,39 +0,0 @@
-From 28d590900ab20dec3dc447562aefaa5e2771c48e Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Pavel=20B=C5=99ezina?= <pbrezina@redhat.com>
-Date: Tue, 28 Feb 2017 13:58:20 +0100
-Subject: [PATCH 86/90] secrets: support HTTP basic authentication with proxy
- provider
-
-Even though configuration options auth_type = basic, username and password
-are read they were not used anywhere prior this patch.
-
-Reviewed-by: Simo Sorce <simo@redhat.com>
-Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
-(cherry picked from commit af026ea6a6e812b7d6c5c889dda64ba7b7c433ee)
----
- src/responder/secrets/proxy.c | 9 +++++++++
- 1 file changed, 9 insertions(+)
-
-diff --git a/src/responder/secrets/proxy.c b/src/responder/secrets/proxy.c
-index 240a1de1e431d511a1eca24d8b463c37ba893e7b..fd96e985c897e2cb470a9b5d6eecbd34350fb7d2 100644
---- a/src/responder/secrets/proxy.c
-+++ b/src/responder/secrets/proxy.c
-@@ -440,6 +440,15 @@ static errno_t proxy_http_create_request(TALLOC_CTX *mem_ctx,
-         }
-     }
- 
-+    /* Set basic authentication if required. */
-+    if (pcfg->auth_type == PAT_BASIC_AUTH) {
-+        ret = tcurl_req_http_basic_auth(tcurl_req, pcfg->auth.basic.username,
-+                                        pcfg->auth.basic.password);
-+        if (ret != EOK) {
-+            goto done;
-+        }
-+    }
-+
-     talloc_steal(tcurl_req, body);
-     *_tcurl_req = talloc_steal(mem_ctx, tcurl_req);
- 
--- 
-2.9.3
-
diff --git a/SOURCES/0087-cache-Check-for-max_id-min_id-in-cache_req.patch b/SOURCES/0087-cache-Check-for-max_id-min_id-in-cache_req.patch
new file mode 100644
index 0000000..04860c2
--- /dev/null
+++ b/SOURCES/0087-cache-Check-for-max_id-min_id-in-cache_req.patch
@@ -0,0 +1,353 @@
+From 2f712c8fe0ecaa07f7b15ebeae5213978d033278 Mon Sep 17 00:00:00 2001
+From: amitkuma <amitkuma@redhat.com>
+Date: Thu, 30 Nov 2017 22:18:39 +0530
+Subject: [PATCH 87/87] cache: Check for max_id/min_id in cache_req
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+The cache_req code doesn't check the min_id/max_id
+boundaries for requests by ID.
+Extending the .lookup_fn function in each plugin
+that searches by ID for a check that returns non-zero
+if the entry is out of the range and 0 if not.
+
+Resolves: https://pagure.io/SSSD/sssd/issue/3569
+
+Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
+Reviewed-by: Fabiano Fidêncio <fidencio@redhat.com>
+(cherry picked from commit 2af80640f18966d65cf82106059ce3c060df93bf)
+---
+ src/responder/common/cache_req/cache_req.c         |   1 +
+ src/responder/common/cache_req/cache_req_private.h |   3 +
+ src/responder/common/cache_req/cache_req_search.c  |   5 +
+ .../common/cache_req/plugins/cache_req_common.c    |  11 ++
+ .../cache_req/plugins/cache_req_group_by_id.c      |   6 +
+ .../cache_req/plugins/cache_req_object_by_id.c     |   6 +
+ .../cache_req/plugins/cache_req_user_by_id.c       |   5 +
+ src/tests/cmocka/test_responder_cache_req.c        | 127 +++++++++++++++++----
+ src/util/util_errors.c                             |   1 +
+ src/util/util_errors.h                             |   1 +
+ 10 files changed, 141 insertions(+), 25 deletions(-)
+
+diff --git a/src/responder/common/cache_req/cache_req.c b/src/responder/common/cache_req/cache_req.c
+index ad9bc040dd999a205713141e6a1512e47b69c45e..134688b0f62c6546763d91468af3f54b73b6073a 100644
+--- a/src/responder/common/cache_req/cache_req.c
++++ b/src/responder/common/cache_req/cache_req.c
+@@ -953,6 +953,7 @@ static void cache_req_search_domains_done(struct tevent_req *subreq)
+             goto done;
+         }
+         break;
++    case ERR_ID_OUTSIDE_RANGE:
+     case ENOENT:
+         if (state->check_next == false) {
+             /* Not found. */
+diff --git a/src/responder/common/cache_req/cache_req_private.h b/src/responder/common/cache_req/cache_req_private.h
+index 95f24c0e5b9ab1150591d308c7288c57fe478c5d..9538b9568ca7f77e377cfee67235c8a52ebbe454 100644
+--- a/src/responder/common/cache_req/cache_req_private.h
++++ b/src/responder/common/cache_req/cache_req_private.h
+@@ -192,4 +192,7 @@ cache_reg_common_get_acct_domain_recv(TALLOC_CTX *mem_ctx,
+                                       struct tevent_req *subreq,
+                                       struct cache_req *cr,
+                                       char **_domain);
++
++errno_t cache_req_idminmax_check(struct cache_req_data *data,
++                                 struct sss_domain_info *domain);
+ #endif /* _CACHE_REQ_PRIVATE_H_ */
+diff --git a/src/responder/common/cache_req/cache_req_search.c b/src/responder/common/cache_req/cache_req_search.c
+index 3365962d473b0982945de2541e44ba86b43a0db5..7423feb6305df87d368bcc10ba28b9b29d57ecf0 100644
+--- a/src/responder/common/cache_req/cache_req_search.c
++++ b/src/responder/common/cache_req/cache_req_search.c
+@@ -203,6 +203,11 @@ static errno_t cache_req_search_cache(TALLOC_CTX *mem_ctx,
+ 
+         *_result = result;
+         break;
++    case ERR_ID_OUTSIDE_RANGE:
++        CACHE_REQ_DEBUG(SSSDBG_TRACE_FUNC, cr,
++                        "ID [%s] was filtered out\n",
++                        cr->debugobj);
++        break;
+     case ENOENT:
+         CACHE_REQ_DEBUG(SSSDBG_TRACE_FUNC, cr,
+                         "Object [%s] was not found in cache\n",
+diff --git a/src/responder/common/cache_req/plugins/cache_req_common.c b/src/responder/common/cache_req/plugins/cache_req_common.c
+index 408c91949ceb3ecaf743f270f58f4e3fcfc3ccb1..bb11eaa86a8bca3f9d15afe48dab9921319d184e 100644
+--- a/src/responder/common/cache_req/plugins/cache_req_common.c
++++ b/src/responder/common/cache_req/plugins/cache_req_common.c
+@@ -26,6 +26,17 @@
+ #include "providers/data_provider.h"
+ #include "responder/common/cache_req/cache_req_plugin.h"
+ 
++errno_t cache_req_idminmax_check(struct cache_req_data *data,
++	                         struct sss_domain_info *domain)
++{
++   if (((domain->id_min != 0) && (data->id < domain->id_min)) ||
++       ((domain->id_max != 0) && (data->id > domain->id_max))) {
++        DEBUG(SSSDBG_FUNC_DATA, "id exceeds min/max boundaries\n");
++        return ERR_ID_OUTSIDE_RANGE;
++   }
++   return EOK;
++}
++
+ static struct ldb_message *
+ cache_req_well_known_sid_msg(TALLOC_CTX *mem_ctx,
+                              const char *sid,
+diff --git a/src/responder/common/cache_req/plugins/cache_req_group_by_id.c b/src/responder/common/cache_req/plugins/cache_req_group_by_id.c
+index ce84b1b4458b447ff6b4b036c6e8fe8f4d7758c8..d178283c33c84e277b83772d04973aa6069af967 100644
+--- a/src/responder/common/cache_req/plugins/cache_req_group_by_id.c
++++ b/src/responder/common/cache_req/plugins/cache_req_group_by_id.c
+@@ -81,6 +81,12 @@ cache_req_group_by_id_lookup(TALLOC_CTX *mem_ctx,
+                              struct sss_domain_info *domain,
+                              struct ldb_result **_result)
+ {
++    errno_t ret;
++
++    ret = cache_req_idminmax_check(data, domain);
++    if (ret != EOK) {
++	return ret;
++    }
+     return sysdb_getgrgid_with_views(mem_ctx, domain, data->id, _result);
+ }
+ 
+diff --git a/src/responder/common/cache_req/plugins/cache_req_object_by_id.c b/src/responder/common/cache_req/plugins/cache_req_object_by_id.c
+index 1327b480c1b1b68f9826fa229c9b001f2d92b79b..be9488d298885320139ccfcd3c59a83ff088e77d 100644
+--- a/src/responder/common/cache_req/plugins/cache_req_object_by_id.c
++++ b/src/responder/common/cache_req/plugins/cache_req_object_by_id.c
+@@ -110,6 +110,12 @@ cache_req_object_by_id_lookup(TALLOC_CTX *mem_ctx,
+                               struct sss_domain_info *domain,
+                               struct ldb_result **_result)
+ {
++    errno_t ret;
++
++    ret = cache_req_idminmax_check(data, domain);
++    if (ret != EOK) {
++        return ret;
++    }
+     return sysdb_search_object_by_id(mem_ctx, domain, data->id,
+                                      data->attrs, _result);
+ }
+diff --git a/src/responder/common/cache_req/plugins/cache_req_user_by_id.c b/src/responder/common/cache_req/plugins/cache_req_user_by_id.c
+index 656fa41af5f39f68c64e241aa97c4eaf3ec57395..151c3e17acf6ef0d958d5a73a36e1c93b9e7a9a9 100644
+--- a/src/responder/common/cache_req/plugins/cache_req_user_by_id.c
++++ b/src/responder/common/cache_req/plugins/cache_req_user_by_id.c
+@@ -81,6 +81,11 @@ cache_req_user_by_id_lookup(TALLOC_CTX *mem_ctx,
+                             struct sss_domain_info *domain,
+                             struct ldb_result **_result)
+ {
++    errno_t ret;
++    ret = cache_req_idminmax_check(data, domain);
++    if (ret != EOK) {
++        return ret;
++    }
+     return sysdb_getpwuid_with_views(mem_ctx, domain, data->id, _result);
+ }
+ 
+diff --git a/src/tests/cmocka/test_responder_cache_req.c b/src/tests/cmocka/test_responder_cache_req.c
+index 0ee0070d0c9fbb89020f522b2f7613f1076a8cbb..5f50b27a5ee846c9ccf71e1e661359a07c2e02e8 100644
+--- a/src/tests/cmocka/test_responder_cache_req.c
++++ b/src/tests/cmocka/test_responder_cache_req.c
+@@ -59,6 +59,11 @@ struct test_group {
+                                     test_single_domain_setup, \
+                                     test_single_domain_teardown)
+ 
++#define new_single_domain_id_limit_test(test) \
++    cmocka_unit_test_setup_teardown(test_ ## test, \
++                                    test_single_domain_id_limits_setup, \
++                                    test_single_domain_teardown)
++
+ #define new_multi_domain_test(test) \
+     cmocka_unit_test_setup_teardown(test_ ## test, \
+                                     test_multi_domain_setup, \
+@@ -521,33 +526,39 @@ __wrap_sss_dp_get_account_send(TALLOC_CTX *mem_ctx,
+     return test_req_succeed_send(mem_ctx, rctx->ev);
+ }
+ 
++static int test_single_domain_setup_common(void **state,
++                                           struct sss_test_conf_param *params)
++{
++    struct cache_req_test_ctx *test_ctx = NULL;
++    errno_t ret;
++
++    assert_true(leak_check_setup());
++
++    test_dom_suite_setup(TESTS_PATH);
++
++    test_ctx = talloc_zero(global_talloc_context, struct cache_req_test_ctx);
++    assert_non_null(test_ctx);
++    *state = test_ctx;
++
++    test_ctx->tctx = create_dom_test_ctx(test_ctx, TESTS_PATH, TEST_CONF_DB,
++                                         TEST_DOM_NAME, TEST_ID_PROVIDER, params);
++    assert_non_null(test_ctx->tctx);
++
++    test_ctx->rctx = mock_rctx(test_ctx, test_ctx->tctx->ev,
++                               test_ctx->tctx->dom, NULL);
++    assert_non_null(test_ctx->rctx);
++
++    ret = sss_ncache_init(test_ctx, 10, 0, &test_ctx->ncache);
++    assert_int_equal(ret, EOK);
++
++    check_leaks_push(test_ctx);
++
++    return 0;
++}
++
+ static int test_single_domain_setup(void **state)
+ {
+-    struct cache_req_test_ctx *test_ctx = NULL;
+-    errno_t ret;
+-
+-    assert_true(leak_check_setup());
+-
+-    test_dom_suite_setup(TESTS_PATH);
+-
+-    test_ctx = talloc_zero(global_talloc_context, struct cache_req_test_ctx);
+-    assert_non_null(test_ctx);
+-    *state = test_ctx;
+-
+-    test_ctx->tctx = create_dom_test_ctx(test_ctx, TESTS_PATH, TEST_CONF_DB,
+-                                         TEST_DOM_NAME, TEST_ID_PROVIDER, NULL);
+-    assert_non_null(test_ctx->tctx);
+-
+-    test_ctx->rctx = mock_rctx(test_ctx, test_ctx->tctx->ev,
+-                               test_ctx->tctx->dom, NULL);
+-    assert_non_null(test_ctx->rctx);
+-
+-    ret = sss_ncache_init(test_ctx, 10, 0, &test_ctx->ncache);
+-    assert_int_equal(ret, EOK);
+-
+-    check_leaks_push(test_ctx);
+-
+-    return 0;
++    return test_single_domain_setup_common(state, NULL);
+ }
+ 
+ static int test_single_domain_teardown(void **state)
+@@ -565,6 +576,16 @@ static int test_single_domain_teardown(void **state)
+     return 0;
+ }
+ 
++static int test_single_domain_id_limits_setup(void **state)
++{
++    struct sss_test_conf_param params[] = {
++        { "min_id", "100" },
++        { "max_id", "10000" },
++        { NULL, NULL },             /* Sentinel */
++    };
++    return test_single_domain_setup_common(state, params);
++}
++
+ static int test_multi_domain_setup(void **state)
+ {
+     struct cache_req_test_ctx *test_ctx = NULL;
+@@ -596,6 +617,32 @@ static int test_multi_domain_setup(void **state)
+     return 0;
+ }
+ 
++void test_user_by_id_below_id_range(void **state)
++{
++    struct cache_req_test_ctx *test_ctx = NULL;
++
++    test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx);
++
++    /* Test. */
++    run_cache_req(test_ctx, cache_req_user_by_id_send,
++                  cache_req_user_by_id_test_done, test_ctx->tctx->dom,
++                  0, 10, ENOENT);
++    assert_false(test_ctx->dp_called);
++}
++
++void test_user_by_id_above_id_range(void **state)
++{
++    struct cache_req_test_ctx *test_ctx = NULL;
++
++    test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx);
++
++    /* Test. */
++    run_cache_req(test_ctx, cache_req_user_by_id_send,
++                  cache_req_user_by_id_test_done, test_ctx->tctx->dom,
++                  0, 100000, ENOENT);
++    assert_false(test_ctx->dp_called);
++}
++
+ static int test_multi_domain_teardown(void **state)
+ {
+     struct cache_req_test_ctx *test_ctx;
+@@ -1332,6 +1379,32 @@ void test_user_by_id_sub_domains_locator_missing_found(void **state)
+     talloc_free(tmp_ctx);
+ }
+ 
++void test_group_by_id_below_id_range(void **state)
++{
++    struct cache_req_test_ctx *test_ctx = NULL;
++
++    test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx);
++
++    /* Test. */
++    run_cache_req(test_ctx, cache_req_group_by_id_send,
++                  cache_req_group_by_id_test_done, test_ctx->tctx->dom,
++                  0, 10, ENOENT);
++    assert_false(test_ctx->dp_called);
++}
++
++void test_group_by_id_above_id_range(void **state)
++{
++    struct cache_req_test_ctx *test_ctx = NULL;
++
++    test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx);
++
++    /* Test. */
++    run_cache_req(test_ctx, cache_req_group_by_id_send,
++                  cache_req_group_by_id_test_done, test_ctx->tctx->dom,
++                  0, 100000, ENOENT);
++    assert_false(test_ctx->dp_called);
++}
++
+ void test_user_by_id_sub_domains_locator_missing_notfound(void **state)
+ {
+     struct cache_req_test_ctx *test_ctx = NULL;
+@@ -3874,6 +3947,8 @@ int main(int argc, const char *argv[])
+         new_single_domain_test(user_by_id_missing_notfound),
+         new_multi_domain_test(user_by_id_multiple_domains_found),
+         new_multi_domain_test(user_by_id_multiple_domains_notfound),
++        new_single_domain_id_limit_test(user_by_id_below_id_range),
++        new_single_domain_id_limit_test(user_by_id_above_id_range),
+ 
+         new_single_domain_test(group_by_name_cache_valid),
+         new_single_domain_test(group_by_name_cache_expired),
+@@ -3884,6 +3959,8 @@ int main(int argc, const char *argv[])
+         new_multi_domain_test(group_by_name_multiple_domains_found),
+         new_multi_domain_test(group_by_name_multiple_domains_notfound),
+         new_multi_domain_test(group_by_name_multiple_domains_parse),
++        new_single_domain_id_limit_test(group_by_id_below_id_range),
++        new_single_domain_id_limit_test(group_by_id_above_id_range),
+ 
+         new_single_domain_test(group_by_id_cache_valid),
+         new_single_domain_test(group_by_id_cache_expired),
+diff --git a/src/util/util_errors.c b/src/util/util_errors.c
+index 06c620b40aaa00d6ce58ace3a28449ffbdf8da88..39ce3d7dcf4af4c489a0a9b7768668497cb84ba5 100644
+--- a/src/util/util_errors.c
++++ b/src/util/util_errors.c
+@@ -117,6 +117,7 @@ struct err_string error_to_str[] = {
+     { "Unable to resolve host" }, /* ERR_UNABLE_TO_RESOLVE_HOST */
+     { "GetAccountDomain() not supported" }, /* ERR_GET_ACCT_DOM_NOT_SUPPORTED */
+     { "The last GetAccountDomain() result is still valid" }, /* ERR_GET_ACCT_DOM_CACHED */
++    { "ID is outside the allowed range" }, /* ERR_ID_OUTSIDE_RANGE */
+     { "ERR_LAST" } /* ERR_LAST */
+ };
+ 
+diff --git a/src/util/util_errors.h b/src/util/util_errors.h
+index bebd6e198fc0077891a602f80182a993ce3f789b..621a3b116edac45960190684055bcd0692135957 100644
+--- a/src/util/util_errors.h
++++ b/src/util/util_errors.h
+@@ -139,6 +139,7 @@ enum sssd_errors {
+     ERR_UNABLE_TO_RESOLVE_HOST,
+     ERR_GET_ACCT_DOM_NOT_SUPPORTED,
+     ERR_GET_ACCT_DOM_CACHED,
++    ERR_ID_OUTSIDE_RANGE,
+     ERR_LAST            /* ALWAYS LAST */
+ };
+ 
+-- 
+2.14.3
+
diff --git a/SOURCES/0087-secrets-fix-debug-message.patch b/SOURCES/0087-secrets-fix-debug-message.patch
deleted file mode 100644
index 19e563b..0000000
--- a/SOURCES/0087-secrets-fix-debug-message.patch
+++ /dev/null
@@ -1,29 +0,0 @@
-From 265c8ea3b9564a53e38df08b89e0fbfb4e7dbfb9 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Pavel=20B=C5=99ezina?= <pbrezina@redhat.com>
-Date: Wed, 15 Mar 2017 13:27:59 +0100
-Subject: [PATCH 87/90] secrets: fix debug message
-
-Reviewed-by: Simo Sorce <simo@redhat.com>
-Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
-(cherry picked from commit db826f57b4c2ee814823057cc536386889f7aa1d)
----
- src/responder/secrets/secsrv_cmd.c | 3 ++-
- 1 file changed, 2 insertions(+), 1 deletion(-)
-
-diff --git a/src/responder/secrets/secsrv_cmd.c b/src/responder/secrets/secsrv_cmd.c
-index 70679ec0398fca25cfb0525772f539526a0eb3ff..b88680c3d7c3105d160de5c78e6d981b852318b9 100644
---- a/src/responder/secrets/secsrv_cmd.c
-+++ b/src/responder/secrets/secsrv_cmd.c
-@@ -451,7 +451,8 @@ int sec_send_data(int fd, struct sec_data *data)
- 
-     data->length -= len;
-     data->data += len;
--    DEBUG(SSSDBG_TRACE_INTERNAL, "sent %zu bytes\n", data->length);
-+    DEBUG(SSSDBG_TRACE_INTERNAL, "sent %zu bytes, %zu bytes remaining\n",
-+          len, data->length);
-     return EOK;
- }
- 
--- 
-2.9.3
-
diff --git a/SOURCES/0088-Revert-p11_child-make-sure-OCSP-checks-are-done.patch b/SOURCES/0088-Revert-p11_child-make-sure-OCSP-checks-are-done.patch
new file mode 100644
index 0000000..18b947b
--- /dev/null
+++ b/SOURCES/0088-Revert-p11_child-make-sure-OCSP-checks-are-done.patch
@@ -0,0 +1,47 @@
+From 424aa780fbb645214b92cf09f23c905b93bdf267 Mon Sep 17 00:00:00 2001
+From: Sumit Bose <sbose@redhat.com>
+Date: Tue, 12 Dec 2017 15:28:27 +0100
+Subject: [PATCH 88/89] Revert "p11_child: make sure OCSP checks are done"
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+This reverts commit 2297cc7d6cd5c38a7d64027165e4e82ca497f418.
+
+Reviewed-by: Fabiano Fidêncio <fidencio@redhat.com>
+(cherry picked from commit c221b5fb4d3fc511cebcae2f042e43fb1c577bc7)
+---
+ src/p11_child/p11_child_nss.c | 17 -----------------
+ 1 file changed, 17 deletions(-)
+
+diff --git a/src/p11_child/p11_child_nss.c b/src/p11_child/p11_child_nss.c
+index bf533f3efe4d680f4c6dbd10a0d2c5a5da371c67..21c508eb1b1b68b3606d0a5eed36573b01f27a19 100644
+--- a/src/p11_child/p11_child_nss.c
++++ b/src/p11_child/p11_child_nss.c
+@@ -338,23 +338,6 @@ int do_work(TALLOC_CTX *mem_ctx, const char *nss_db,
+                       PR_GetError(), PORT_ErrorToString(PR_GetError()));
+                 continue;
+             }
+-
+-            /* with 'certificateUsageCheckAllUsages' set
+-             * CERT_VerifyCertificateNow() does not do OCSP so it must be done
+-             * explicitly */
+-            if (cert_verify_opts->do_ocsp) {
+-                rv = CERT_CheckOCSPStatus(handle, cert_list_node->cert,
+-                                          PR_Now(), NULL);
+-                if (rv != SECSuccess) {
+-                    DEBUG(SSSDBG_OP_FAILURE,
+-                          "Certificate [%s][%s] failed OCSP check [%d][%s], "
+-                          "skipping.\n",
+-                          cert_list_node->cert->nickname,
+-                          cert_list_node->cert->subjectName,
+-                          PR_GetError(), PORT_ErrorToString(PR_GetError()));
+-                    continue;
+-                }
+-            }
+         }
+ 
+         if (key_id_in != NULL) {
+-- 
+2.14.3
+
diff --git a/SOURCES/0088-secrets-always-add-Content-Length-header.patch b/SOURCES/0088-secrets-always-add-Content-Length-header.patch
deleted file mode 100644
index d941ad3..0000000
--- a/SOURCES/0088-secrets-always-add-Content-Length-header.patch
+++ /dev/null
@@ -1,112 +0,0 @@
-From 07271dbd7c8f28a6aace48787040580973eb5a4e Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Pavel=20B=C5=99ezina?= <pbrezina@redhat.com>
-Date: Wed, 15 Mar 2017 15:15:08 +0100
-Subject: [PATCH 88/90] secrets: always add Content-Length header
-
-If custodia server does not reply with Content-Length header, curl may
-wait for non-existing body of http reply if such body does not exist
-(for example during POST operation when creating a container).
-
-Reviewed-by: Simo Sorce <simo@redhat.com>
-Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
-(cherry picked from commit 13d720de13e490850c1139eea865bcd5195a2630)
----
- src/responder/secrets/providers.c | 72 ++++++++++++++++++++++++++++++++++++---
- 1 file changed, 68 insertions(+), 4 deletions(-)
-
-diff --git a/src/responder/secrets/providers.c b/src/responder/secrets/providers.c
-index 80a443d91135447ec8ce8d424b692a6d7e26a907..a27fb720b394e7c76d1b65f656146bcd00755449 100644
---- a/src/responder/secrets/providers.c
-+++ b/src/responder/secrets/providers.c
-@@ -388,20 +388,84 @@ int sec_http_reply_with_headers(TALLOC_CTX *mem_ctx, struct sec_data *reply,
-     return EOK;
- }
- 
-+static errno_t
-+sec_http_iobuf_split(struct sss_iobuf *response,
-+                     const char **headers,
-+                     const char **body)
-+{
-+    const char *data = (const char *)sss_iobuf_get_data(response);
-+    char *delim;
-+
-+    /* The last header ends with \r\n and then comes \r\n again as a separator
-+     * of body from headers. We can use this to find this point. */
-+    delim = strstr(data, "\r\n\r\n");
-+    if (delim == NULL) {
-+        return EINVAL;
-+    }
-+
-+    /* Skip to the body delimiter. */
-+    delim = delim + sizeof("\r\n") - 1;
-+
-+    /* Replace \r\n with zeros turning data into:
-+     * from HEADER\r\nBODY into HEADER\0\0BODY format. */
-+    delim[0] = '\0';
-+    delim[1] = '\0';
-+
-+    /* Split the buffer. */
-+    *headers = data;
-+    *body = delim + 2;
-+
-+    return 0;
-+}
-+
-+static const char *
-+sec_http_iobuf_add_content_length(TALLOC_CTX *mem_ctx,
-+                                  const char *headers,
-+                                  size_t body_len)
-+{
-+    /* If Content-Length is already present we do nothing. */
-+    if (strstr(headers, "Content-Length:") != NULL) {
-+        return headers;
-+    }
-+
-+    return talloc_asprintf(mem_ctx, "%sContent-Length: %zu\r\n",
-+                           headers, body_len);
-+}
-+
- errno_t sec_http_reply_iobuf(TALLOC_CTX *mem_ctx,
-                              struct sec_data *reply,
-                              int response_code,
-                              struct sss_iobuf *response)
- {
-+    const char *headers;
-+    const char *body;
-+    size_t body_len;
-+    errno_t ret;
-+
-     DEBUG(SSSDBG_TRACE_LIBS, "HTTP reply %d\n", response_code);
- 
--    reply->data = (char *)sss_iobuf_get_data(response);
--    reply->length = sss_iobuf_get_len(response);
-+    ret = sec_http_iobuf_split(response, &headers, &body);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_OP_FAILURE,
-+              "Unexpected HTTP reply, returning what we got from server\n");
-+        reply->data = (char *)sss_iobuf_get_data(response);
-+        reply->length = sss_iobuf_get_len(response);
- 
--    talloc_steal(mem_ctx, reply->data);
-+        return EOK;
-+    }
- 
-+    /* Add Content-Length header if not present so client does not await
-+     * not-existing incoming data. */
-+    body_len = strlen(body);
-+    headers = sec_http_iobuf_add_content_length(mem_ctx, headers, body_len);
-+    if (headers == NULL) {
-+        return ENOMEM;
-+    }
-+
-+    reply->length = strlen(headers) + sizeof("\r\n") - 1 + body_len;
-+    reply->data = talloc_asprintf(mem_ctx, "%s\r\n%s", headers, body);
-     if (reply->data == NULL) {
--        return EINVAL;
-+        return ENOMEM;
-     }
- 
-     return EOK;
--- 
-2.9.3
-
diff --git a/SOURCES/0089-p11_child-properly-check-results-of-CERT_VerifyCerti.patch b/SOURCES/0089-p11_child-properly-check-results-of-CERT_VerifyCerti.patch
new file mode 100644
index 0000000..85e867a
--- /dev/null
+++ b/SOURCES/0089-p11_child-properly-check-results-of-CERT_VerifyCerti.patch
@@ -0,0 +1,64 @@
+From 56402a2b350ebdcfd49685a5a3c0fd42131b2196 Mon Sep 17 00:00:00 2001
+From: Sumit Bose <sbose@redhat.com>
+Date: Tue, 12 Dec 2017 15:24:57 +0100
+Subject: [PATCH 89/89] p11_child: properly check results of
+ CERT_VerifyCertificateNow
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+With certificateUsageCheckAllUsages not only the return code of
+CERT_VerifyCertificateNow() should be checked but also the usages for
+which the certificate was verified. The usages checked here will all
+involve CA signature checks and OCSP checks if OCSP is enabled.
+
+Related to https://pagure.io/SSSD/sssd/issue/3560
+
+Reviewed-by: Fabiano Fidêncio <fidencio@redhat.com>
+(cherry picked from commit 787ba9c882f1d7ff9ea4f2745e779c5fb04dfafc)
+---
+ src/p11_child/p11_child_nss.c | 14 ++++++++++++--
+ 1 file changed, 12 insertions(+), 2 deletions(-)
+
+diff --git a/src/p11_child/p11_child_nss.c b/src/p11_child/p11_child_nss.c
+index 21c508eb1b1b68b3606d0a5eed36573b01f27a19..cb894280c18fcbd59c5499e36d30f3ba305c0ea2 100644
+--- a/src/p11_child/p11_child_nss.c
++++ b/src/p11_child/p11_child_nss.c
+@@ -45,6 +45,15 @@
+ #include "util/crypto/sss_crypto.h"
+ #include "util/cert.h"
+ 
++#define EXP_USAGES (  certificateUsageSSLClient \
++                    | certificateUsageSSLServer \
++                    | certificateUsageSSLServerWithStepUp \
++                    | certificateUsageEmailSigner \
++                    | certificateUsageEmailRecipient \
++                    | certificateUsageObjectSigner \
++                    | certificateUsageStatusResponder \
++                    | certificateUsageSSLCA )
++
+ enum op_mode {
+     OP_NONE,
+     OP_AUTH,
+@@ -136,6 +145,7 @@ int do_work(TALLOC_CTX *mem_ctx, const char *nss_db,
+     char *cert_b64 = NULL;
+     char *multi = NULL;
+     PRCList *node;
++    SECCertificateUsage returned_usage = 0;
+ 
+     nss_ctx = NSS_InitContext(nss_db, "", "", SECMOD_DB, &parameters, flags);
+     if (nss_ctx == NULL) {
+@@ -329,8 +339,8 @@ int do_work(TALLOC_CTX *mem_ctx, const char *nss_db,
+             rv = CERT_VerifyCertificateNow(handle, cert_list_node->cert,
+                                            PR_TRUE,
+                                            certificateUsageCheckAllUsages,
+-                                           NULL, NULL);
+-            if (rv != SECSuccess) {
++                                           NULL, &returned_usage);
++            if (rv != SECSuccess || ((returned_usage & EXP_USAGES) == 0)) {
+                 DEBUG(SSSDBG_OP_FAILURE,
+                       "Certificate [%s][%s] not valid [%d][%s], skipping.\n",
+                       cert_list_node->cert->nickname,
+-- 
+2.14.3
+
diff --git a/SOURCES/0089-sss_iobuf-fix-read-shadows-a-global-declaration.patch b/SOURCES/0089-sss_iobuf-fix-read-shadows-a-global-declaration.patch
deleted file mode 100644
index 2524495..0000000
--- a/SOURCES/0089-sss_iobuf-fix-read-shadows-a-global-declaration.patch
+++ /dev/null
@@ -1,40 +0,0 @@
-From ce191dc1922d894573eee828c88c325f64515d3e Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Pavel=20B=C5=99ezina?= <pbrezina@redhat.com>
-Date: Tue, 28 Mar 2017 15:26:52 +0200
-Subject: [PATCH 89/90] sss_iobuf: fix 'read' shadows a global declaration
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Reviewed-by: Lukáš Slebodník <lslebodn@redhat.com>
-(cherry picked from commit 18e4fe9d836e8f7bee52724374ffc0011172329f)
----
- src/util/sss_iobuf.c | 6 +++---
- 1 file changed, 3 insertions(+), 3 deletions(-)
-
-diff --git a/src/util/sss_iobuf.c b/src/util/sss_iobuf.c
-index fc288d2df2bfaaba393dd490d4da8976de804cb5..518713e4cc3dd99627a3a4450f235cbbc69ed3a2 100644
---- a/src/util/sss_iobuf.c
-+++ b/src/util/sss_iobuf.c
-@@ -188,15 +188,15 @@ errno_t sss_iobuf_read_len(struct sss_iobuf *iobuf,
-                            size_t len,
-                            uint8_t *_buf)
- {
--    size_t read;
-+    size_t read_bytes;
-     errno_t ret;
- 
--    ret = sss_iobuf_read(iobuf, len, _buf, &read);
-+    ret = sss_iobuf_read(iobuf, len, _buf, &read_bytes);
-     if (ret != EOK) {
-         return ret;
-     }
- 
--    if (read != len) {
-+    if (read_bytes != len) {
-         return ENOBUFS;
-     }
- 
--- 
-2.9.3
-
diff --git a/SOURCES/0090-configure-fix-typo.patch b/SOURCES/0090-configure-fix-typo.patch
deleted file mode 100644
index f7a0f28..0000000
--- a/SOURCES/0090-configure-fix-typo.patch
+++ /dev/null
@@ -1,29 +0,0 @@
-From a87cb169e5700bf9a3e74d4a1980e8e5c8e24692 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Pavel=20B=C5=99ezina?= <pbrezina@redhat.com>
-Date: Wed, 29 Mar 2017 13:28:49 +0200
-Subject: [PATCH 90/90] configure: fix typo
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Reviewed-by: Lukáš Slebodník <lslebodn@redhat.com>
-(cherry picked from commit dc186bfe90665c13d589b3b4efd9009293e62c46)
----
- src/external/libhttp_parser.m4 | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
-diff --git a/src/external/libhttp_parser.m4 b/src/external/libhttp_parser.m4
-index 504bdf0f66c95b3d224c677a205a46e6f8b44726..3a5ef0dbbc63423ad8e960d72e97ec4fb4481dd1 100644
---- a/src/external/libhttp_parser.m4
-+++ b/src/external/libhttp_parser.m4
-@@ -17,6 +17,6 @@ AS_IF([test x"$found_http_parser" != xyes],
-                       ],
-                       [-L$sss_extra_libdir -lhttp_parser_strict])],
-         [AC_MSG_ERROR([
--You must have the header file http_parse.h installed to build sssd
-+You must have the header file http_parser.h installed to build sssd
- with secrets responder. If you want to build sssd without secret responder
- then specify --without-secrets when running configure.])])])
--- 
-2.9.3
-
diff --git a/SOURCES/0090-ifp-use-realloc-in-ifp_list_ctx_remaining_capacity.patch b/SOURCES/0090-ifp-use-realloc-in-ifp_list_ctx_remaining_capacity.patch
new file mode 100644
index 0000000..c731709
--- /dev/null
+++ b/SOURCES/0090-ifp-use-realloc-in-ifp_list_ctx_remaining_capacity.patch
@@ -0,0 +1,92 @@
+From 674c5c3ba930a8546371ea8e138ff20a15090431 Mon Sep 17 00:00:00 2001
+From: Sumit Bose <sbose@redhat.com>
+Date: Fri, 15 Dec 2017 12:09:06 +0100
+Subject: [PATCH 90/90] ifp: use realloc in ifp_list_ctx_remaining_capacity()
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+ifp_list_ctx_remaining_capacity() might be called multiple times if
+results from multiple domains are added to the result list.
+
+The current use of talloc_zero_array() which was introduced with commit
+b0b9222 will override results which are already in the list. This causes
+a regression since it worked before.
+
+This patch replaces it with talloc_realloc().
+
+Resolves https://pagure.io/SSSD/sssd/issue/3608
+
+Reviewed-by: Fabiano Fidêncio <fidencio@redhat.com>
+(cherry picked from commit 510ac193900a7bb9dfae10c0ca4607c224b265af)
+---
+ src/responder/ifp/ifp_private.h |  1 +
+ src/responder/ifp/ifpsrv_util.c | 16 ++++++++++++----
+ 2 files changed, 13 insertions(+), 4 deletions(-)
+
+diff --git a/src/responder/ifp/ifp_private.h b/src/responder/ifp/ifp_private.h
+index 13455bbf70860fb6dbfa3bb65fe3bd565d53257d..b406e7f5bab3e0dbc9696a5ab58e46b6ee7839eb 100644
+--- a/src/responder/ifp/ifp_private.h
++++ b/src/responder/ifp/ifp_private.h
+@@ -93,6 +93,7 @@ struct ifp_list_ctx {
+     struct ifp_ctx *ctx;
+ 
+     const char **paths;
++    size_t paths_max;
+     size_t path_count;
+ };
+ 
+diff --git a/src/responder/ifp/ifpsrv_util.c b/src/responder/ifp/ifpsrv_util.c
+index 1df646339526186e862dcd09cddd971b77c20a8b..da4ab06796a99c930b7a4ad21ca408814f8b4c49 100644
+--- a/src/responder/ifp/ifpsrv_util.c
++++ b/src/responder/ifp/ifpsrv_util.c
+@@ -372,7 +372,9 @@ struct ifp_list_ctx *ifp_list_ctx_new(struct sbus_request *sbus_req,
+     list_ctx->ctx = ctx;
+     list_ctx->dom = ctx->rctx->domains;
+     list_ctx->filter = filter;
+-    list_ctx->paths = talloc_zero_array(list_ctx, const char *, 1);
++    list_ctx->paths_max = 1;
++    list_ctx->paths = talloc_zero_array(list_ctx, const char *,
++                                        list_ctx->paths_max);
+     if (list_ctx->paths == NULL) {
+         talloc_free(list_ctx);
+         return NULL;
+@@ -387,6 +389,7 @@ errno_t ifp_list_ctx_remaining_capacity(struct ifp_list_ctx *list_ctx,
+ {
+     size_t capacity = list_ctx->limit - list_ctx->path_count;
+     errno_t ret;
++    size_t c;
+ 
+     if (list_ctx->limit == 0) {
+         capacity = entries;
+@@ -396,19 +399,24 @@ errno_t ifp_list_ctx_remaining_capacity(struct ifp_list_ctx *list_ctx,
+     if (capacity < entries) {
+         DEBUG(SSSDBG_MINOR_FAILURE,
+               "IFP list request has limit of %"PRIu32" entries but back end "
+-              "returned %zu entries\n", list_ctx->limit, entries);
++              "returned %zu entries\n", list_ctx->limit,
++                                        list_ctx->path_count + entries);
+     } else {
+         capacity = entries;
+     }
+ 
+ immediately:
+-    talloc_zfree(list_ctx->paths);
+-    list_ctx->paths = talloc_zero_array(list_ctx, const char *, capacity);
++    list_ctx->paths_max = list_ctx->path_count + capacity;
++    list_ctx->paths = talloc_realloc(list_ctx, list_ctx->paths, const char *,
++                                     list_ctx->paths_max);
+     if (list_ctx->paths == NULL) {
+         DEBUG(SSSDBG_CRIT_FAILURE, "talloc_zero_array() failed\n");
+         ret = ENOMEM;
+         goto done;
+     }
++    for (c = list_ctx->path_count; c < list_ctx->paths_max; c++) {
++        list_ctx->paths[c] = NULL;
++    }
+ 
+     *_capacity = capacity;
+     ret = EOK;
+-- 
+2.14.3
+
diff --git a/SOURCES/0091-IPA-Delay-the-first-periodic-refresh-of-trusted-doma.patch b/SOURCES/0091-IPA-Delay-the-first-periodic-refresh-of-trusted-doma.patch
new file mode 100644
index 0000000..c828f38
--- /dev/null
+++ b/SOURCES/0091-IPA-Delay-the-first-periodic-refresh-of-trusted-doma.patch
@@ -0,0 +1,64 @@
+From a2f7322b9d8e47c0c93463d9fe1f37dc869799df Mon Sep 17 00:00:00 2001
+From: Jakub Hrozek <jhrozek@redhat.com>
+Date: Mon, 8 Jan 2018 18:30:57 +0100
+Subject: [PATCH 91/96] IPA: Delay the first periodic refresh of trusted
+ domains
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+When the IPA subdomains code is initialized, the responders send a request
+to fetch subdomains. This request first stores the list of trusted domains
+to the cache and then runs the ipa-getkeytab helper.
+
+At the same time, the periodical task to update the subdomains is also
+started. The task founds out that all the trusted domains are already known
+and finishes the request, which replies to the Data Provider requests as
+well even while the ipa-getkeytab request is still running.
+
+This unblocks requests from the responders, which try to connect to the AD
+DCs even before the keytab is available, which switches the SSSD status to
+offline.
+
+This patch simply delays the first periodic task in the IPA subdomains code
+by 10 minutes, thus mitigating the startup race.
+
+Resolves:
+https://pagure.io/SSSD/sssd/issue/3601
+
+Reviewed-by: Sumit Bose <sbose@redhat.com>
+Reviewed-by: Michal Židek <mzidek@redhat.com>
+Reviewed-by: Pavel Březina <pbrezina@redhat.com>
+(cherry picked from commit 261a84355d9d033ca03f46727dbc2cf4921f154e)
+---
+ src/providers/ipa/ipa_subdomains.c | 7 ++++++-
+ 1 file changed, 6 insertions(+), 1 deletion(-)
+
+diff --git a/src/providers/ipa/ipa_subdomains.c b/src/providers/ipa/ipa_subdomains.c
+index 3d3341a3eff5e55ae0c6fa5ad40603adc609e692..7d2cf80c8137a0428880c5474d4d94ca3ad1a5d4 100644
+--- a/src/providers/ipa/ipa_subdomains.c
++++ b/src/providers/ipa/ipa_subdomains.c
+@@ -2379,6 +2379,11 @@ errno_t ipa_subdomains_init(TALLOC_CTX *mem_ctx,
+     struct ipa_options *ipa_options;
+     time_t period;
+     errno_t ret;
++    /* Delay the first ptask that refreshes the trusted domains so that a race between
++     * the first responder-induced request and the ptask doesn't cause issues, see
++     * also upstream ticket #3601
++     */
++    const time_t ptask_first_delay = 600;
+ 
+     ipa_options = ipa_id_ctx->ipa_options;
+ 
+@@ -2401,7 +2406,7 @@ errno_t ipa_subdomains_init(TALLOC_CTX *mem_ctx,
+                   struct ipa_subdomains_ctx, struct dp_subdomains_data, struct dp_reply_std);
+ 
+     period = be_ctx->domain->subdomain_refresh_interval;
+-    ret = be_ptask_create(sd_ctx, be_ctx, period, 0, 0, 0, period,
++    ret = be_ptask_create(sd_ctx, be_ctx, period, ptask_first_delay, 0, 0, period,
+                           BE_PTASK_OFFLINE_DISABLE, 0,
+                           ipa_subdomains_ptask_send, ipa_subdomains_ptask_recv, sd_ctx,
+                           "Subdomains Refresh", NULL);
+-- 
+2.14.3
+
diff --git a/SOURCES/0091-pam_test_client-add-service-and-environment-to-PAM-t.patch b/SOURCES/0091-pam_test_client-add-service-and-environment-to-PAM-t.patch
deleted file mode 100644
index e511e0d..0000000
--- a/SOURCES/0091-pam_test_client-add-service-and-environment-to-PAM-t.patch
+++ /dev/null
@@ -1,105 +0,0 @@
-From a5a6f0ab816be0dfd24b97a59c161adbe15ef406 Mon Sep 17 00:00:00 2001
-From: Sumit Bose <sbose@redhat.com>
-Date: Tue, 24 Jan 2017 14:50:20 +0100
-Subject: [PATCH 91/96] pam_test_client: add service and environment to PAM
- test client
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Related to https://pagure.io/SSSD/sssd/issue/3292
-
-Reviewed-by: Pavel Březina <pbrezina@redhat.com>
-(cherry picked from commit 7be6624d9eda369e9a4d70c8ee4939b3622229b3)
----
- src/sss_client/pam_test_client.c | 50 ++++++++++++++++++++++++++++++----------
- 1 file changed, 38 insertions(+), 12 deletions(-)
-
-diff --git a/src/sss_client/pam_test_client.c b/src/sss_client/pam_test_client.c
-index 29d1fcbf01682668d51bf154736aec673bd46501..ea032a75b195a9bf8078ed7d248da154ab0c8430 100644
---- a/src/sss_client/pam_test_client.c
-+++ b/src/sss_client/pam_test_client.c
-@@ -48,34 +48,44 @@ static struct pam_conv conv = {
- # error "Missing text based pam conversation function"
- #endif
- 
-+#define DEFAULT_ACTION "acct"
-+#define DEFAULT_SERVICE "system-auth"
-+
- int main(int argc, char *argv[]) {
- 
-     pam_handle_t *pamh;
-     char *user;
-     char *action;
-+    char *service;
-     int ret;
-+    size_t c;
-+    char **pam_env;
- 
-     if (argc == 1) {
--        fprintf(stderr, "missing action and user name, using default\n");
--        action = strdup("auth");
--        user = strdup("dummy");
-+        fprintf(stderr, "Usage: pam_test_client USERNAME "
-+                        "[auth|acct|setc|chau|open|clos] [pam_service]\n");
-+        return 0;
-     } else if (argc == 2) {
--        fprintf(stdout, "using first argument as action and default user name\n");
--        action = strdup(argv[1]);
--        user = strdup("dummy");
--    } else {
--        action = strdup(argv[1]);
--        user = strdup(argv[2]);
-+        fprintf(stderr, "using first argument as user name and default action "
-+                        "and service\n");
-+    } else if (argc == 3) {
-+        fprintf(stderr, "using first argument as user name, second as action "
-+                        "and default service\n");
-     }
- 
--    if (action == NULL || user == NULL) {
-+    user = strdup(argv[1]);
-+    action = argc > 2 ? strdup(argv[2]) : strdup(DEFAULT_ACTION);
-+    service = argc > 3 ? strdup(argv[3]) : strdup(DEFAULT_SERVICE);
-+
-+    if (action == NULL || user == NULL || service == NULL) {
-         fprintf(stderr, "Out of memory!\n");
-         return 1;
-     }
- 
--    fprintf(stdout, "action: %s\nuser: %s\n", action,user);
-+    fprintf(stdout, "user: %s\naction: %s\nservice: %s\n",
-+                    user, action, service);
- 
--    ret = pam_start("sss_test", user, &conv, &pamh);
-+    ret = pam_start(service, user, &conv, &pamh);
-     if (ret != PAM_SUCCESS) {
-         fprintf(stderr, "pam_start failed: %s\n", pam_strerror(pamh, ret));
-         return 1;
-@@ -109,7 +119,23 @@ int main(int argc, char *argv[]) {
-         fprintf(stderr, "unknown action\n");
-     }
- 
-+    fprintf(stderr, "PAM Environment:\n");
-+    pam_env = pam_getenvlist(pamh);
-+    if (pam_env != NULL && pam_env[0] != NULL) {
-+        for (c = 0; pam_env[c] != NULL; c++) {
-+            fprintf(stderr, " - %s\n", pam_env[c]);
-+            free(pam_env[c]);
-+        }
-+    } else {
-+        fprintf(stderr, " - no env -\n");
-+    }
-+    free(pam_env);
-+
-     pam_end(pamh, ret);
- 
-+    free(user);
-+    free(action);
-+    free(service);
-+
-     return 0;
- }
--- 
-2.9.3
-
diff --git a/SOURCES/0092-pam_test_client-add-SSSD-getpwnam-lookup.patch b/SOURCES/0092-pam_test_client-add-SSSD-getpwnam-lookup.patch
deleted file mode 100644
index fc139dc..0000000
--- a/SOURCES/0092-pam_test_client-add-SSSD-getpwnam-lookup.patch
+++ /dev/null
@@ -1,142 +0,0 @@
-From 109c99463219be59fbf168a4075a74585193aef9 Mon Sep 17 00:00:00 2001
-From: Sumit Bose <sbose@redhat.com>
-Date: Wed, 25 Jan 2017 16:50:00 +0100
-Subject: [PATCH 92/96] pam_test_client: add SSSD getpwnam lookup
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Related to https://pagure.io/SSSD/sssd/issue/3292
-
-Reviewed-by: Pavel Březina <pbrezina@redhat.com>
-(cherry picked from commit 435b3678de25d22eb8a6e892109d26c32f0760a4)
----
- Makefile.am                      | 10 ++++--
- src/sss_client/pam_test_client.c | 76 ++++++++++++++++++++++++++++++++++++++++
- 2 files changed, 84 insertions(+), 2 deletions(-)
-
-diff --git a/Makefile.am b/Makefile.am
-index 4a414f77df999b8b1d81f663fcc18dbd2d6d2dc4..368ebe54b8617cb5bafb079322582d5346b6c4df 100644
---- a/Makefile.am
-+++ b/Makefile.am
-@@ -3460,8 +3460,14 @@ if BUILD_WITH_LIBCURL
- noinst_PROGRAMS += tcurl-test-tool
- endif
- 
--pam_test_client_SOURCES = src/sss_client/pam_test_client.c
--pam_test_client_LDADD = $(PAM_LIBS) $(PAM_MISC_LIBS)
-+pam_test_client_SOURCES = \
-+    src/sss_client/pam_test_client.c \
-+    $(NULL)
-+pam_test_client_LDADD = \
-+    $(PAM_LIBS) \
-+    $(PAM_MISC_LIBS) \
-+    $(LIBADD_DL) \
-+    $(NULL)
- 
- if BUILD_AUTOFS
- autofs_test_client_SOURCES = \
-diff --git a/src/sss_client/pam_test_client.c b/src/sss_client/pam_test_client.c
-index ea032a75b195a9bf8078ed7d248da154ab0c8430..69af612270492968b56d1c11de2bf56ebf57471f 100644
---- a/src/sss_client/pam_test_client.c
-+++ b/src/sss_client/pam_test_client.c
-@@ -25,6 +25,11 @@
- #include <stdio.h>
- #include <unistd.h>
- #include <string.h>
-+#include <dlfcn.h>
-+#include <sys/types.h>
-+#include <pwd.h>
-+#include <nss.h>
-+#include <errno.h>
- 
- #include <security/pam_appl.h>
- 
-@@ -51,6 +56,70 @@ static struct pam_conv conv = {
- #define DEFAULT_ACTION "acct"
- #define DEFAULT_SERVICE "system-auth"
- 
-+#define DEFAULT_BUFSIZE 4096
-+
-+static int sss_getpwnam_check(const char *user)
-+{
-+    void *dl_handle = NULL;
-+    enum nss_status (*sss_getpwnam_r)(const char *name, struct passwd *result,
-+                                      char *buffer, size_t buflen,
-+                                      int *errnop);
-+    struct passwd pwd = { 0 };
-+    enum nss_status status;
-+    char *buffer = NULL;
-+    size_t buflen;
-+    int nss_errno;
-+    int ret;
-+
-+    dl_handle = dlopen("libnss_sss.so.2", RTLD_NOW);
-+    if (dl_handle == NULL) {
-+        fprintf(stderr, "dlopen failed with [%s].\n", dlerror());
-+        ret = EIO;
-+        goto done;
-+    }
-+
-+    sss_getpwnam_r = dlsym(dl_handle, "_nss_sss_getpwnam_r");
-+    if (sss_getpwnam_r == NULL) {
-+        fprintf(stderr, "dlsym failed with [%s].\n", dlerror());
-+        ret = EIO;
-+        goto done;
-+    }
-+
-+    buflen = DEFAULT_BUFSIZE;
-+    buffer = malloc(buflen);
-+    if (buffer == NULL) {
-+        fprintf(stderr, "malloc failed.\n");
-+        ret = ENOMEM;
-+        goto done;
-+    }
-+
-+    status = sss_getpwnam_r(user, &pwd, buffer, buflen, &nss_errno);
-+    if (status != NSS_STATUS_SUCCESS) {
-+        fprintf(stderr, "sss_getpwnam_r failed with [%d].\n", status);
-+        ret = EIO;
-+        goto done;
-+    }
-+
-+    fprintf(stdout, "SSSD nss user lookup result:\n");
-+    fprintf(stdout, " - user name: %s\n", pwd.pw_name);
-+    fprintf(stdout, " - user id: %d\n", pwd.pw_uid);
-+    fprintf(stdout, " - group id: %d\n", pwd.pw_gid);
-+    fprintf(stdout, " - gecos: %s\n", pwd.pw_gecos);
-+    fprintf(stdout, " - home directory: %s\n", pwd.pw_dir);
-+    fprintf(stdout, " - shell: %s\n", pwd.pw_shell);
-+
-+    ret = 0;
-+
-+done:
-+    if (dl_handle != NULL) {
-+        dlclose(dl_handle);
-+    }
-+
-+    free(buffer);
-+
-+    return ret;
-+}
-+
- int main(int argc, char *argv[]) {
- 
-     pam_handle_t *pamh;
-@@ -85,6 +154,13 @@ int main(int argc, char *argv[]) {
-     fprintf(stdout, "user: %s\naction: %s\nservice: %s\n",
-                     user, action, service);
- 
-+    if (*user != '\0') {
-+        ret = sss_getpwnam_check(user);
-+        if (ret != 0) {
-+            fprintf(stderr, "User name lookup with [%s] failed.\n", user);
-+        }
-+    }
-+
-     ret = pam_start(service, user, &conv, &pamh);
-     if (ret != PAM_SUCCESS) {
-         fprintf(stderr, "pam_start failed: %s\n", pam_strerror(pamh, ret));
--- 
-2.9.3
-
diff --git a/SOURCES/0092-sysdb-add-userMappedCertificate-to-the-index.patch b/SOURCES/0092-sysdb-add-userMappedCertificate-to-the-index.patch
new file mode 100644
index 0000000..346c8b5
--- /dev/null
+++ b/SOURCES/0092-sysdb-add-userMappedCertificate-to-the-index.patch
@@ -0,0 +1,55 @@
+From 57a83eb8657a125d203a335b052d965c7a3b15de Mon Sep 17 00:00:00 2001
+From: Sumit Bose <sbose@redhat.com>
+Date: Mon, 8 Jan 2018 18:22:17 +0100
+Subject: [PATCH 92/96] sysdb: add userMappedCertificate to the index
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Related to https://pagure.io/SSSD/sssd/issue/3503
+
+Reviewed-by: Fabiano Fidêncio <fidencio@redhat.com>
+(cherry picked from commit 5b78fff78bb44d1af5420db23b02210f755f5f17)
+---
+ src/db/sysdb_private.h |  1 +
+ src/db/sysdb_upgrade.c | 12 ++++++++++++
+ 2 files changed, 13 insertions(+)
+
+diff --git a/src/db/sysdb_private.h b/src/db/sysdb_private.h
+index cac06ba46da23080d1ab661502d0792bd37b9291..c0a8e29ab9578acb27cf8d1db049c4260904fdda 100644
+--- a/src/db/sysdb_private.h
++++ b/src/db/sysdb_private.h
+@@ -78,6 +78,7 @@
+      "@IDXATTR: canonicalUserPrincipalName\n" \
+      "@IDXATTR: uniqueID\n" \
+      "@IDXATTR: mail\n" \
++     "@IDXATTR: userMappedCertificate\n" \
+      "\n" \
+      "dn: @MODULES\n" \
+      "@LIST: asq,memberof\n" \
+diff --git a/src/db/sysdb_upgrade.c b/src/db/sysdb_upgrade.c
+index bc157a24664239bc1255e49a1825243a07acc90f..46df971e98f73dc28bc6764a478f13d871515124 100644
+--- a/src/db/sysdb_upgrade.c
++++ b/src/db/sysdb_upgrade.c
+@@ -2475,6 +2475,18 @@ int sysdb_upgrade_19(struct sysdb_ctx *sysdb, const char **ver)
+         goto done;
+     }
+ 
++    ret = ldb_msg_add_empty(msg, "@IDXATTR", LDB_FLAG_MOD_ADD, NULL);
++    if (ret != LDB_SUCCESS) {
++        ret = ENOMEM;
++        goto done;
++    }
++
++    ret = ldb_msg_add_string(msg, "@IDXATTR", SYSDB_USER_MAPPED_CERT);
++    if (ret != LDB_SUCCESS) {
++        ret = ENOMEM;
++        goto done;
++    }
++
+     ret = ldb_modify(sysdb->ldb, msg);
+     if (ret != LDB_SUCCESS) {
+         ret = sysdb_error_to_errno(ret);
+-- 
+2.14.3
+
diff --git a/SOURCES/0093-AD-Inherit-the-MPG-setting-from-the-main-domain.patch b/SOURCES/0093-AD-Inherit-the-MPG-setting-from-the-main-domain.patch
new file mode 100644
index 0000000..875f5fb
--- /dev/null
+++ b/SOURCES/0093-AD-Inherit-the-MPG-setting-from-the-main-domain.patch
@@ -0,0 +1,48 @@
+From e67f94d854ef125626294771473a1204726eeba4 Mon Sep 17 00:00:00 2001
+From: Jakub Hrozek <jhrozek@redhat.com>
+Date: Mon, 15 Jan 2018 22:11:24 +0100
+Subject: [PATCH 93/96] AD: Inherit the MPG setting from the main domain
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+If the auto_private_groups option was set in the domain section for
+direct integration, it only had an effect on the joined domain, not any
+of the subdomains, so requesting a user from the child domain would look
+like this:
+    $ id childuser@child.win.trust.test
+    uid=30000(childuser@child.win.trust.test) gid=40000(usergroup@child.win.trust.test) groups=40000(usergroup@child.win.trust.test)
+The expected result, visible after this patch is:
+    $ id childuser@child.win.trust.test
+    uid=30000(childuser@child.win.trust.test) gid=30000(childuser@child.win.trust.test) groups=30000(childuser@child.win.trust.test),40000(usergroup@child.win.trust.test)
+
+Resolves:
+https://pagure.io/SSSD/sssd/issue/3613
+
+Reviewed-by: Fabiano Fidêncio <fidencio@redhat.com>
+(cherry picked from commit 29ebf45f96b13590ae76a19c7c16c53f172e4ae4)
+---
+ src/providers/ad/ad_subdomains.c | 7 +++++++
+ 1 file changed, 7 insertions(+)
+
+diff --git a/src/providers/ad/ad_subdomains.c b/src/providers/ad/ad_subdomains.c
+index 3fb9b950f171d85817cce35ac92ad7c4974ccb68..1b9483a5dce937d6acdd813486a1e8c18210d35f 100644
+--- a/src/providers/ad/ad_subdomains.c
++++ b/src/providers/ad/ad_subdomains.c
+@@ -391,6 +391,13 @@ ad_subdom_store(struct sdap_idmap_ctx *idmap_ctx,
+     }
+ 
+     mpg = sdap_idmap_domain_has_algorithmic_mapping(idmap_ctx, name, sid_str);
++    if (mpg == false) {
++        /* Domains that use the POSIX attributes set by the admin must
++         * inherit the MPG setting from the parent domain so that the
++         * auto_private_groups options works for trusted domains as well
++         */
++        mpg = domain->mpg;
++    }
+ 
+     ret = sysdb_subdomain_store(domain->sysdb, name, realm, flat, sid_str,
+                                 mpg, enumerate, domain->forest, 0, NULL);
+-- 
+2.14.3
+
diff --git a/SOURCES/0093-sss_sifp-update-method-names.patch b/SOURCES/0093-sss_sifp-update-method-names.patch
deleted file mode 100644
index 0dac7de..0000000
--- a/SOURCES/0093-sss_sifp-update-method-names.patch
+++ /dev/null
@@ -1,54 +0,0 @@
-From 52622fbb51d972ba1f02ff0c7dff2e9fa7adf96c Mon Sep 17 00:00:00 2001
-From: Sumit Bose <sbose@redhat.com>
-Date: Thu, 16 Mar 2017 11:37:41 +0100
-Subject: [PATCH 93/96] sss_sifp: update method names
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Related to https://pagure.io/SSSD/sssd/issue/3292
-
-Reviewed-by: Pavel Březina <pbrezina@redhat.com>
-(cherry picked from commit 40ff10d73063949ca699670ca212e96b809d5fcd)
----
- Makefile.am                    | 2 +-
- src/lib/sifp/sss_sifp_common.c | 4 ++--
- 2 files changed, 3 insertions(+), 3 deletions(-)
-
-diff --git a/Makefile.am b/Makefile.am
-index 368ebe54b8617cb5bafb079322582d5346b6c4df..b16a71cc9e07f21d02b4ceb3f41a8e9de0591ec9 100644
---- a/Makefile.am
-+++ b/Makefile.am
-@@ -1221,7 +1221,7 @@ libsss_simpleifp_la_LIBADD = \
-     $(DHASH_LIBS)
- libsss_simpleifp_la_LDFLAGS = \
-     -Wl,--version-script,$(srcdir)/src/lib/sifp/sss_simpleifp.exports \
--    -version-info 1:0:1
-+    -version-info 1:1:1
- 
- dist_noinst_DATA += src/lib/sifp/sss_simpleifp.exports
- 
-diff --git a/src/lib/sifp/sss_sifp_common.c b/src/lib/sifp/sss_sifp_common.c
-index bd1dc6a3108329d2c795dc0a259637e71964be9f..8913d0be3d43bd8707829001a5b476d9ab864fd8 100644
---- a/src/lib/sifp/sss_sifp_common.c
-+++ b/src/lib/sifp/sss_sifp_common.c
-@@ -168,7 +168,7 @@ sss_sifp_fetch_user_by_uid(sss_sifp_ctx *ctx,
-     uint64_t _uid = uid;
- 
-     return sss_sifp_fetch_object_by_attr(ctx, IFP_PATH_USERS, IFACE_IFP_USERS,
--                                         IFACE_IFP_USERS_USER, "UserByID",
-+                                         IFACE_IFP_USERS_USER, "ByID",
-                                          DBUS_TYPE_UINT64, &_uid, _user);
- }
- 
-@@ -178,6 +178,6 @@ sss_sifp_fetch_user_by_name(sss_sifp_ctx *ctx,
-                             sss_sifp_object **_user)
- {
-     return sss_sifp_fetch_object_by_name(ctx, IFP_PATH_USERS, IFACE_IFP_USERS,
--                                         IFACE_IFP_USERS_USER, "UserByName",
-+                                         IFACE_IFP_USERS_USER, "ByName",
-                                          name, _user);
- }
--- 
-2.9.3
-
diff --git a/SOURCES/0094-SDAP-skip-builtin-AD-groups-in-sdap_save_grpmem.patch b/SOURCES/0094-SDAP-skip-builtin-AD-groups-in-sdap_save_grpmem.patch
new file mode 100644
index 0000000..72c1f2c
--- /dev/null
+++ b/SOURCES/0094-SDAP-skip-builtin-AD-groups-in-sdap_save_grpmem.patch
@@ -0,0 +1,53 @@
+From 75da39f57ba0223be9bd9906cd3ed902623aed10 Mon Sep 17 00:00:00 2001
+From: Sumit Bose <sbose@redhat.com>
+Date: Mon, 18 Dec 2017 20:30:04 +0100
+Subject: [PATCH 94/96] SDAP: skip builtin AD groups in sdap_save_grpmem()
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+While processing group memberships SSSD might accidentally save builtin
+or other well known AD groups. With this patch those groups are skipped
+similar as e.g. in sdap_save_group().
+
+Resolves:
+https://pagure.io/SSSD/sssd/issue/3610
+
+Reviewed-by: Fabiano Fidêncio <fidencio@redhat.com>
+(cherry picked from commit c36a66b7fb77cff29400c751b363a342923e122e)
+---
+ src/providers/ldap/sdap_async_groups.c | 11 +++++++++++
+ 1 file changed, 11 insertions(+)
+
+diff --git a/src/providers/ldap/sdap_async_groups.c b/src/providers/ldap/sdap_async_groups.c
+index b1cfb7e4a4c054e5d365da5fca65da27c9ef5461..bbe6f1386eadbe4eb7b47bea9e5a6bb8ff4ee8eb 100644
+--- a/src/providers/ldap/sdap_async_groups.c
++++ b/src/providers/ldap/sdap_async_groups.c
+@@ -880,6 +880,8 @@ static int sdap_save_grpmem(TALLOC_CTX *memctx,
+     int ret;
+     const char *remove_attrs[] = {SYSDB_MEMBER, SYSDB_ORIG_MEMBER, SYSDB_GHOST,
+                                   NULL};
++    const char *check_dom;
++    const char *check_name;
+ 
+     if (dom->ignore_group_members) {
+         DEBUG(SSSDBG_CRIT_FAILURE,
+@@ -905,6 +907,15 @@ static int sdap_save_grpmem(TALLOC_CTX *memctx,
+         group_dom = sss_get_domain_by_sid_ldap_fallback(get_domains_head(dom),
+                                                         group_sid);
+         if (group_dom == NULL) {
++            ret = well_known_sid_to_name(group_sid, &check_dom, &check_name);
++            if (ret == EOK) {
++                DEBUG(SSSDBG_TRACE_FUNC,
++                      "Skipping group with SID [%s][%s\\%s] which is "
++                      "currently not handled by SSSD.\n",
++                      group_sid, check_dom, check_name);
++                return EOK;
++            }
++
+             DEBUG(SSSDBG_TRACE_FUNC, "SID [%s] does not belong to any known "
+                                      "domain, using [%s].\n", group_sid,
+                                                               dom->name);
+-- 
+2.14.3
+
diff --git a/SOURCES/0094-pam_test_client-add-InfoPipe-user-lookup.patch b/SOURCES/0094-pam_test_client-add-InfoPipe-user-lookup.patch
deleted file mode 100644
index 6d20370..0000000
--- a/SOURCES/0094-pam_test_client-add-InfoPipe-user-lookup.patch
+++ /dev/null
@@ -1,131 +0,0 @@
-From acefbdd65a083b5d9577d9f683ac64e358c2f9c0 Mon Sep 17 00:00:00 2001
-From: Sumit Bose <sbose@redhat.com>
-Date: Thu, 16 Mar 2017 11:38:20 +0100
-Subject: [PATCH 94/96] pam_test_client: add InfoPipe user lookup
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Related to https://pagure.io/SSSD/sssd/issue/3292
-
-Reviewed-by: Pavel Březina <pbrezina@redhat.com>
-(cherry picked from commit 9be97c9cc69e5e6e568d7e21f61a46c3ae2dc387)
----
- Makefile.am                      |  1 +
- src/sss_client/pam_test_client.c | 71 ++++++++++++++++++++++++++++++++++++++++
- 2 files changed, 72 insertions(+)
-
-diff --git a/Makefile.am b/Makefile.am
-index b16a71cc9e07f21d02b4ceb3f41a8e9de0591ec9..c4d252357356c2d5452a414fd360fc5370b2c775 100644
---- a/Makefile.am
-+++ b/Makefile.am
-@@ -3467,6 +3467,7 @@ pam_test_client_LDADD = \
-     $(PAM_LIBS) \
-     $(PAM_MISC_LIBS) \
-     $(LIBADD_DL) \
-+    libsss_simpleifp.la \
-     $(NULL)
- 
- if BUILD_AUTOFS
-diff --git a/src/sss_client/pam_test_client.c b/src/sss_client/pam_test_client.c
-index 69af612270492968b56d1c11de2bf56ebf57471f..40ef3f6d480c0108c985fce7e34e983d145f237e 100644
---- a/src/sss_client/pam_test_client.c
-+++ b/src/sss_client/pam_test_client.c
-@@ -30,9 +30,12 @@
- #include <pwd.h>
- #include <nss.h>
- #include <errno.h>
-+#include <inttypes.h>
- 
- #include <security/pam_appl.h>
- 
-+#include "lib/sifp/sss_sifp.h"
-+
- #ifdef HAVE_SECURITY_PAM_MISC_H
- # include <security/pam_misc.h>
- #elif defined(HAVE_SECURITY_OPENPAM_H)
-@@ -58,6 +61,69 @@ static struct pam_conv conv = {
- 
- #define DEFAULT_BUFSIZE 4096
- 
-+static int get_ifp_user(const char *user)
-+{
-+    sss_sifp_ctx *sifp;
-+    sss_sifp_error error;
-+    sss_sifp_object *user_obj;
-+    const char *tmp_str;
-+    uint32_t tmp_uint32;
-+    size_t c;
-+
-+    struct ifp_user_attr {
-+        const char *name;
-+        bool is_string;
-+    } ifp_user_attr[] = {
-+        { "name", true },
-+        { "uidNumber", false },
-+        { "gidNumber", false },
-+        { "gecos", true },
-+        { "homeDirectory", true },
-+        { "loginShell", true },
-+        { NULL, false }
-+    };
-+
-+    error = sss_sifp_init(&sifp);
-+    if (error != SSS_SIFP_OK) {
-+        fprintf(stderr, "Unable to connect to the InfoPipe");
-+        return EFAULT;
-+    }
-+
-+    error = sss_sifp_fetch_user_by_name(sifp, user, &user_obj);
-+    if (error != SSS_SIFP_OK) {
-+        fprintf(stderr, "Unable to get user object");
-+        return EIO;
-+    }
-+
-+    fprintf(stdout, "SSSD InfoPipe user lookup result:\n");
-+    for (c = 0; ifp_user_attr[c].name != NULL; c++) {
-+        if (ifp_user_attr[c].is_string) {
-+            error = sss_sifp_find_attr_as_string(user_obj->attrs,
-+                                                 ifp_user_attr[c].name,
-+                                                 &tmp_str);
-+        } else {
-+            error = sss_sifp_find_attr_as_uint32(user_obj->attrs,
-+                                                 ifp_user_attr[c].name,
-+                                                 &tmp_uint32);
-+        }
-+        if (error != SSS_SIFP_OK) {
-+            fprintf(stderr, "Unable to get user name attr");
-+            return EIO;
-+        }
-+
-+        if (ifp_user_attr[c].is_string) {
-+            fprintf(stdout, " - %s: %s\n", ifp_user_attr[c].name, tmp_str);
-+        } else {
-+            fprintf(stdout, " - %s: %"PRIu32"\n", ifp_user_attr[c].name,
-+                                                  tmp_uint32);
-+        }
-+    }
-+
-+    sss_sifp_free_object(sifp, &user_obj);
-+    sss_sifp_free(&sifp);
-+    return 0;
-+}
-+
- static int sss_getpwnam_check(const char *user)
- {
-     void *dl_handle = NULL;
-@@ -159,6 +225,11 @@ int main(int argc, char *argv[]) {
-         if (ret != 0) {
-             fprintf(stderr, "User name lookup with [%s] failed.\n", user);
-         }
-+
-+        ret = get_ifp_user(user);
-+        if (ret != 0) {
-+            fprintf(stderr, "InforPipe User lookup with [%s] failed.\n", user);
-+        }
-     }
- 
-     ret = pam_start(service, user, &conv, &pamh);
--- 
-2.9.3
-
diff --git a/SOURCES/0095-SYSDB-Read-the-ldb_message-from-loop-s-index-counter.patch b/SOURCES/0095-SYSDB-Read-the-ldb_message-from-loop-s-index-counter.patch
new file mode 100644
index 0000000..b0469ba
--- /dev/null
+++ b/SOURCES/0095-SYSDB-Read-the-ldb_message-from-loop-s-index-counter.patch
@@ -0,0 +1,39 @@
+From 80e4aa68587d5a70bfd41f78f17902bf06b447a1 Mon Sep 17 00:00:00 2001
+From: Jakub Hrozek <jhrozek@redhat.com>
+Date: Tue, 23 Jan 2018 11:11:14 +0100
+Subject: [PATCH 95/96] SYSDB: Read the ldb_message from loop's index counter
+ when reading subdomain UPNs
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+There was a typo in code that read the UPN suffixes from the subdomain
+ldb_message. As a result, the UPN suffixes from the first domain were
+always consulted for all domains.
+
+Related to:
+https://pagure.io/SSSD/sssd/issue/3431
+
+Reviewed-by: Lukáš Slebodník <lslebodn@redhat.com>
+Reviewed-by: Sumit Bose <sbose@redhat.com>
+(cherry picked from commit a8a3fcbf6f75a7c2665e8bf503c186e07dfab333)
+---
+ src/db/sysdb_subdomains.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/src/db/sysdb_subdomains.c b/src/db/sysdb_subdomains.c
+index 0dd05c24c963f12a28ef6f6b64dc40faa7fcc649..b0863b7935b060cb545197005c84f51efbc2d49c 100644
+--- a/src/db/sysdb_subdomains.c
++++ b/src/db/sysdb_subdomains.c
+@@ -386,7 +386,7 @@ errno_t sysdb_update_subdomains(struct sss_domain_info *domain,
+                                              SYSDB_SUBDOMAIN_FOREST, NULL);
+ 
+         upn_suffixes = NULL;
+-        tmp_el = ldb_msg_find_element(res->msgs[0], SYSDB_UPN_SUFFIXES);
++        tmp_el = ldb_msg_find_element(res->msgs[i], SYSDB_UPN_SUFFIXES);
+         if (tmp_el != NULL) {
+             upn_suffixes = sss_ldb_el_to_string_list(tmp_ctx, tmp_el);
+             if (upn_suffixes == NULL) {
+-- 
+2.14.3
+
diff --git a/SOURCES/0095-sssctl-integrate-pam_test_client-into-sssctl.patch b/SOURCES/0095-sssctl-integrate-pam_test_client-into-sssctl.patch
deleted file mode 100644
index d942a65..0000000
--- a/SOURCES/0095-sssctl-integrate-pam_test_client-into-sssctl.patch
+++ /dev/null
@@ -1,359 +0,0 @@
-From 1bc25dba8f4725ef34e394d8e8eee42dbdaed924 Mon Sep 17 00:00:00 2001
-From: Sumit Bose <sbose@redhat.com>
-Date: Thu, 30 Mar 2017 16:21:15 +0200
-Subject: [PATCH 95/96] sssctl: integrate pam_test_client into sssctl
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Reviewed-by: Pavel Březina <pbrezina@redhat.com>
-(cherry picked from commit 4a9160e2b3b9c531e2b4a7884f49bfbb4a07a992)
----
- Makefile.am                                        |  16 +--
- po/POTFILES.in                                     |   1 -
- src/tools/sssctl/sssctl.c                          |   1 +
- src/tools/sssctl/sssctl.h                          |   4 +
- .../sssctl/sssctl_user_checks.c}                   | 122 +++++++++++----------
- 5 files changed, 72 insertions(+), 72 deletions(-)
- rename src/{sss_client/pam_test_client.c => tools/sssctl/sssctl_user_checks.c} (62%)
-
-diff --git a/Makefile.am b/Makefile.am
-index c4d252357356c2d5452a414fd360fc5370b2c775..f5ac363a35e4aae51e8b70bad27c7fc824be10f2 100644
---- a/Makefile.am
-+++ b/Makefile.am
-@@ -1724,11 +1724,15 @@ sssctl_SOURCES = \
-     src/tools/sssctl/sssctl_domains.c \
-     src/tools/sssctl/sssctl_sifp.c \
-     src/tools/sssctl/sssctl_config.c \
-+    src/tools/sssctl/sssctl_user_checks.c \
-     $(SSSD_TOOLS_OBJ) \
-     $(NULL)
- sssctl_LDADD = \
-     $(TOOLS_LIBS) \
-     $(SSSD_INTERNAL_LTLIBS) \
-+    $(PAM_LIBS) \
-+    $(PAM_MISC_LIBS) \
-+    $(LIBADD_DL) \
-     libsss_simpleifp.la \
-     $(NULL)
- sssctl_CFLAGS = \
-@@ -3449,7 +3453,7 @@ endif # BUILD_KCM
- 
- endif # HAVE_CMOCKA
- 
--noinst_PROGRAMS = pam_test_client
-+noinst_PROGRAMS =
- if BUILD_SUDO
- noinst_PROGRAMS += sss_sudo_cli
- endif
-@@ -3460,16 +3464,6 @@ if BUILD_WITH_LIBCURL
- noinst_PROGRAMS += tcurl-test-tool
- endif
- 
--pam_test_client_SOURCES = \
--    src/sss_client/pam_test_client.c \
--    $(NULL)
--pam_test_client_LDADD = \
--    $(PAM_LIBS) \
--    $(PAM_MISC_LIBS) \
--    $(LIBADD_DL) \
--    libsss_simpleifp.la \
--    $(NULL)
--
- if BUILD_AUTOFS
- autofs_test_client_SOURCES = \
-     src/sss_client/autofs/autofs_test_client.c \
-diff --git a/po/POTFILES.in b/po/POTFILES.in
-index ee532def223fdd5db632ad98fd11a57e38d0e125..f4e4e095f9e4025d129b6b13422bdd0bc07c8e1a 100644
---- a/po/POTFILES.in
-+++ b/po/POTFILES.in
-@@ -9,7 +9,6 @@ src/sss_client/common.c
- src/sss_client/nss_group.c
- src/sss_client/nss_passwd.c
- src/sss_client/pam_sss.c
--src/sss_client/pam_test_client.c
- src/sss_client/ssh/sss_ssh_authorizedkeys.c
- src/sss_client/ssh/sss_ssh_knownhostsproxy.c
- src/tools/sss_useradd.c
-diff --git a/src/tools/sssctl/sssctl.c b/src/tools/sssctl/sssctl.c
-index e1cf46382cd1dee54cd372ca500368f149411b78..509d2e1a00d3b57b541590ce7db5f94d2ff43add 100644
---- a/src/tools/sssctl/sssctl.c
-+++ b/src/tools/sssctl/sssctl.c
-@@ -263,6 +263,7 @@ int main(int argc, const char **argv)
-         SSS_TOOL_DELIMITER("SSSD Status:"),
-         SSS_TOOL_COMMAND("domain-list", "List available domains", 0, sssctl_domain_list),
-         SSS_TOOL_COMMAND("domain-status", "Print information about domain", 0, sssctl_domain_status),
-+        SSS_TOOL_COMMAND("user-checks", "Print information about a user and check authentication", 0, sssctl_user_checks),
-         SSS_TOOL_DELIMITER("Information about cached content:"),
-         SSS_TOOL_COMMAND("user-show", "Information about cached user", 0, sssctl_user_show),
-         SSS_TOOL_COMMAND("group-show", "Information about cached group", 0, sssctl_group_show),
-diff --git a/src/tools/sssctl/sssctl.h b/src/tools/sssctl/sssctl.h
-index 5270a9ec62dfb288511af179a99e9a542ea26ec4..22626e2210252e5e3fadeb6c5d01d4620cd60e5b 100644
---- a/src/tools/sssctl/sssctl.h
-+++ b/src/tools/sssctl/sssctl.h
-@@ -121,4 +121,8 @@ errno_t sssctl_netgroup_show(struct sss_cmdline *cmdline,
- errno_t sssctl_config_check(struct sss_cmdline *cmdline,
-                             struct sss_tool_ctx *tool_ctx,
-                             void *pvt);
-+
-+errno_t sssctl_user_checks(struct sss_cmdline *cmdline,
-+                           struct sss_tool_ctx *tool_ctx,
-+                           void *pvt);
- #endif /* _SSSCTL_H_ */
-diff --git a/src/sss_client/pam_test_client.c b/src/tools/sssctl/sssctl_user_checks.c
-similarity index 62%
-rename from src/sss_client/pam_test_client.c
-rename to src/tools/sssctl/sssctl_user_checks.c
-index 40ef3f6d480c0108c985fce7e34e983d145f237e..7c7b564bd29100382c9bbef7a3131c379e9aa97e 100644
---- a/src/sss_client/pam_test_client.c
-+++ b/src/tools/sssctl/sssctl_user_checks.c
-@@ -35,6 +35,9 @@
- #include <security/pam_appl.h>
- 
- #include "lib/sifp/sss_sifp.h"
-+#include "util/util.h"
-+#include "tools/common/sss_tools.h"
-+#include "tools/sssctl/sssctl.h"
- 
- #ifdef HAVE_SECURITY_PAM_MISC_H
- # include <security/pam_misc.h>
-@@ -85,17 +88,17 @@ static int get_ifp_user(const char *user)
- 
-     error = sss_sifp_init(&sifp);
-     if (error != SSS_SIFP_OK) {
--        fprintf(stderr, "Unable to connect to the InfoPipe");
-+        fprintf(stderr, _("Unable to connect to the InfoPipe"));
-         return EFAULT;
-     }
- 
-     error = sss_sifp_fetch_user_by_name(sifp, user, &user_obj);
-     if (error != SSS_SIFP_OK) {
--        fprintf(stderr, "Unable to get user object");
-+        fprintf(stderr, _("Unable to get user object"));
-         return EIO;
-     }
- 
--    fprintf(stdout, "SSSD InfoPipe user lookup result:\n");
-+    fprintf(stdout, _("SSSD InfoPipe user lookup result:\n"));
-     for (c = 0; ifp_user_attr[c].name != NULL; c++) {
-         if (ifp_user_attr[c].is_string) {
-             error = sss_sifp_find_attr_as_string(user_obj->attrs,
-@@ -107,7 +110,7 @@ static int get_ifp_user(const char *user)
-                                                  &tmp_uint32);
-         }
-         if (error != SSS_SIFP_OK) {
--            fprintf(stderr, "Unable to get user name attr");
-+            fprintf(stderr, _("Unable to get user name attr"));
-             return EIO;
-         }
- 
-@@ -118,6 +121,7 @@ static int get_ifp_user(const char *user)
-                                                   tmp_uint32);
-         }
-     }
-+    fprintf(stdout, "\n");
- 
-     sss_sifp_free_object(sifp, &user_obj);
-     sss_sifp_free(&sifp);
-@@ -139,14 +143,14 @@ static int sss_getpwnam_check(const char *user)
- 
-     dl_handle = dlopen("libnss_sss.so.2", RTLD_NOW);
-     if (dl_handle == NULL) {
--        fprintf(stderr, "dlopen failed with [%s].\n", dlerror());
-+        fprintf(stderr, _("dlopen failed with [%s].\n"), dlerror());
-         ret = EIO;
-         goto done;
-     }
- 
-     sss_getpwnam_r = dlsym(dl_handle, "_nss_sss_getpwnam_r");
-     if (sss_getpwnam_r == NULL) {
--        fprintf(stderr, "dlsym failed with [%s].\n", dlerror());
-+        fprintf(stderr, _("dlsym failed with [%s].\n"), dlerror());
-         ret = EIO;
-         goto done;
-     }
-@@ -154,25 +158,25 @@ static int sss_getpwnam_check(const char *user)
-     buflen = DEFAULT_BUFSIZE;
-     buffer = malloc(buflen);
-     if (buffer == NULL) {
--        fprintf(stderr, "malloc failed.\n");
-+        fprintf(stderr, _("malloc failed.\n"));
-         ret = ENOMEM;
-         goto done;
-     }
- 
-     status = sss_getpwnam_r(user, &pwd, buffer, buflen, &nss_errno);
-     if (status != NSS_STATUS_SUCCESS) {
--        fprintf(stderr, "sss_getpwnam_r failed with [%d].\n", status);
-+        fprintf(stderr, _("sss_getpwnam_r failed with [%d].\n"), status);
-         ret = EIO;
-         goto done;
-     }
- 
--    fprintf(stdout, "SSSD nss user lookup result:\n");
--    fprintf(stdout, " - user name: %s\n", pwd.pw_name);
--    fprintf(stdout, " - user id: %d\n", pwd.pw_uid);
--    fprintf(stdout, " - group id: %d\n", pwd.pw_gid);
--    fprintf(stdout, " - gecos: %s\n", pwd.pw_gecos);
--    fprintf(stdout, " - home directory: %s\n", pwd.pw_dir);
--    fprintf(stdout, " - shell: %s\n", pwd.pw_shell);
-+    fprintf(stdout, _("SSSD nss user lookup result:\n"));
-+    fprintf(stdout, _(" - user name: %s\n"), pwd.pw_name);
-+    fprintf(stdout, _(" - user id: %d\n"), pwd.pw_uid);
-+    fprintf(stdout, _(" - group id: %d\n"), pwd.pw_gid);
-+    fprintf(stdout, _(" - gecos: %s\n"), pwd.pw_gecos);
-+    fprintf(stdout, _(" - home directory: %s\n"), pwd.pw_dir);
-+    fprintf(stdout, _(" - shell: %s\n\n"), pwd.pw_shell);
- 
-     ret = 0;
- 
-@@ -186,87 +190,89 @@ done:
-     return ret;
- }
- 
--int main(int argc, char *argv[]) {
-+errno_t sssctl_user_checks(struct sss_cmdline *cmdline,
-+                           struct sss_tool_ctx *tool_ctx,
-+                           void *pvt)
-+{
- 
-     pam_handle_t *pamh;
--    char *user;
--    char *action;
--    char *service;
-+    const char *user = NULL;
-+    const char *action = DEFAULT_ACTION;
-+    const char *service = DEFAULT_SERVICE;
-     int ret;
-     size_t c;
-     char **pam_env;
- 
--    if (argc == 1) {
--        fprintf(stderr, "Usage: pam_test_client USERNAME "
--                        "[auth|acct|setc|chau|open|clos] [pam_service]\n");
--        return 0;
--    } else if (argc == 2) {
--        fprintf(stderr, "using first argument as user name and default action "
--                        "and service\n");
--    } else if (argc == 3) {
--        fprintf(stderr, "using first argument as user name, second as action "
--                        "and default service\n");
--    }
--
--    user = strdup(argv[1]);
--    action = argc > 2 ? strdup(argv[2]) : strdup(DEFAULT_ACTION);
--    service = argc > 3 ? strdup(argv[3]) : strdup(DEFAULT_SERVICE);
-+    /* Parse command line. */
-+    struct poptOption options[] = {
-+        { "action", 'a', POPT_ARG_STRING, &action, 0,
-+          _("PAM action [auth|acct|setc|chau|open|clos], default: "
-+            DEFAULT_ACTION), NULL },
-+        { "service", 's', POPT_ARG_STRING, &service, 0,
-+          _("PAM service, default: " DEFAULT_SERVICE), NULL },
-+        POPT_TABLEEND
-+    };
- 
--    if (action == NULL || user == NULL || service == NULL) {
--        fprintf(stderr, "Out of memory!\n");
--        return 1;
-+    ret = sss_tool_popt_ex(cmdline, options, SSS_TOOL_OPT_OPTIONAL,
-+                           NULL, NULL, "USERNAME", _("Specify user name."),
-+                           &user, NULL);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_CRIT_FAILURE, "Unable to parse command arguments\n");
-+        return ret;
-     }
- 
--    fprintf(stdout, "user: %s\naction: %s\nservice: %s\n",
-+    fprintf(stdout, _("user: %s\naction: %s\nservice: %s\n\n"),
-                     user, action, service);
- 
-     if (*user != '\0') {
-         ret = sss_getpwnam_check(user);
-         if (ret != 0) {
--            fprintf(stderr, "User name lookup with [%s] failed.\n", user);
-+            fprintf(stderr, _("User name lookup with [%s] failed.\n"), user);
-         }
- 
-         ret = get_ifp_user(user);
-         if (ret != 0) {
--            fprintf(stderr, "InforPipe User lookup with [%s] failed.\n", user);
-+            fprintf(stderr, _("InforPipe User lookup with [%s] failed.\n"),
-+                            user);
-         }
-     }
- 
-     ret = pam_start(service, user, &conv, &pamh);
-     if (ret != PAM_SUCCESS) {
--        fprintf(stderr, "pam_start failed: %s\n", pam_strerror(pamh, ret));
-+        fprintf(stderr, _("pam_start failed: %s\n"), pam_strerror(pamh, ret));
-         return 1;
-     }
- 
-     if ( strncmp(action, "auth", 4)== 0 ) {
--        fprintf(stdout, "testing pam_authenticate\n");
-+        fprintf(stdout, _("testing pam_authenticate\n\n"));
-         ret = pam_authenticate(pamh, 0);
--        fprintf(stderr, "pam_authenticate: %s\n", pam_strerror(pamh, ret));
-+        fprintf(stderr, _("pam_authenticate: %s\n\n"), pam_strerror(pamh, ret));
-     } else if ( strncmp(action, "chau", 4)== 0 ) {
--        fprintf(stdout, "testing pam_chauthtok\n");
-+        fprintf(stdout, _("testing pam_chauthtok\n\n"));
-         ret = pam_chauthtok(pamh, 0);
--        fprintf(stderr, "pam_chauthtok: %s\n", pam_strerror(pamh, ret));
-+        fprintf(stderr, _("pam_chauthtok: %s\n\n"), pam_strerror(pamh, ret));
-     } else if ( strncmp(action, "acct", 4)== 0 ) {
--        fprintf(stdout, "testing pam_acct_mgmt\n");
-+        fprintf(stdout, _("testing pam_acct_mgmt\n\n"));
-         ret = pam_acct_mgmt(pamh, 0);
--        fprintf(stderr, "pam_acct_mgmt: %s\n", pam_strerror(pamh, ret));
-+        fprintf(stderr, _("pam_acct_mgmt: %s\n\n"), pam_strerror(pamh, ret));
-     } else if ( strncmp(action, "setc", 4)== 0 ) {
--        fprintf(stdout, "testing pam_setcred\n");
-+        fprintf(stdout, _("testing pam_setcred\n\n"));
-         ret = pam_setcred(pamh, 0);
--        fprintf(stderr, "pam_setcred: %d[%s]\n", ret, pam_strerror(pamh, ret));
-+        fprintf(stderr, _("pam_setcred: [%s]\n\n"), pam_strerror(pamh, ret));
-     } else if ( strncmp(action, "open", 4)== 0 ) {
--        fprintf(stdout, "testing pam_open_session\n");
-+        fprintf(stdout, _("testing pam_open_session\n\n"));
-         ret = pam_open_session(pamh, 0);
--        fprintf(stderr, "pam_open_session: %s\n", pam_strerror(pamh, ret));
-+        fprintf(stderr, _("pam_open_session: %s\n\n"), pam_strerror(pamh, ret));
-     } else if ( strncmp(action, "clos", 4)== 0 ) {
--        fprintf(stdout, "testing pam_close_session\n");
-+        fprintf(stdout, _("testing pam_close_session\n\n"));
-         ret = pam_close_session(pamh, 0);
--        fprintf(stderr, "pam_close_session: %s\n", pam_strerror(pamh, ret));
-+        fprintf(stderr, _("pam_close_session: %s\n\n"),
-+                        pam_strerror(pamh, ret));
-     } else {
--        fprintf(stderr, "unknown action\n");
-+        fprintf(stderr, _("unknown action\n"));
-     }
- 
--    fprintf(stderr, "PAM Environment:\n");
-+    fprintf(stderr, _("PAM Environment:\n"));
-     pam_env = pam_getenvlist(pamh);
-     if (pam_env != NULL && pam_env[0] != NULL) {
-         for (c = 0; pam_env[c] != NULL; c++) {
-@@ -274,15 +280,11 @@ int main(int argc, char *argv[]) {
-             free(pam_env[c]);
-         }
-     } else {
--        fprintf(stderr, " - no env -\n");
-+        fprintf(stderr, _(" - no env -\n"));
-     }
-     free(pam_env);
- 
-     pam_end(pamh, ret);
- 
--    free(user);
--    free(action);
--    free(service);
--
-     return 0;
- }
--- 
-2.9.3
-
diff --git a/SOURCES/0096-i18n-adding-sssctl-files.patch b/SOURCES/0096-i18n-adding-sssctl-files.patch
deleted file mode 100644
index 84984cf..0000000
--- a/SOURCES/0096-i18n-adding-sssctl-files.patch
+++ /dev/null
@@ -1,34 +0,0 @@
-From e66f1df979b47519f9f51b6064154dae7ba5b396 Mon Sep 17 00:00:00 2001
-From: Sumit Bose <sbose@redhat.com>
-Date: Fri, 7 Apr 2017 14:24:10 +0200
-Subject: [PATCH 96/96] i18n: adding sssctl files
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Reviewed-by: Pavel Březina <pbrezina@redhat.com>
-(cherry picked from commit dbeae483464e42238a84c6a5b8c3c4f5312ae643)
----
- po/POTFILES.in | 8 ++++++++
- 1 file changed, 8 insertions(+)
-
-diff --git a/po/POTFILES.in b/po/POTFILES.in
-index f4e4e095f9e4025d129b6b13422bdd0bc07c8e1a..33e7ed7f9e9bc19f33fca8a1f2649b69b79a882f 100644
---- a/po/POTFILES.in
-+++ b/po/POTFILES.in
-@@ -23,4 +23,12 @@ src/tools/sss_cache.c
- src/tools/sss_debuglevel.c
- src/tools/tools_util.c
- src/tools/tools_util.h
-+src/tools/sssctl/sssctl.c
-+src/tools/sssctl/sssctl_cache.c
-+src/tools/sssctl/sssctl_config.c
-+src/tools/sssctl/sssctl_data.c
-+src/tools/sssctl/sssctl_domains.c
-+src/tools/sssctl/sssctl_logs.c
-+src/tools/sssctl/sssctl_sifp.c
-+src/tools/sssctl/sssctl_user_checks.c
- src/util/util.h
--- 
-2.9.3
-
diff --git a/SOURCES/0096-nss-idmap-check-timed-muted-return-code.patch b/SOURCES/0096-nss-idmap-check-timed-muted-return-code.patch
new file mode 100644
index 0000000..974484c
--- /dev/null
+++ b/SOURCES/0096-nss-idmap-check-timed-muted-return-code.patch
@@ -0,0 +1,73 @@
+From 63c09393b926f0d00317a1ca1dc6c7c992c40f4e Mon Sep 17 00:00:00 2001
+From: Sumit Bose <sbose@redhat.com>
+Date: Tue, 16 Jan 2018 18:09:00 +0100
+Subject: [PATCH 96/96] nss-idmap: check timed muted return code
+
+Check return values and make sure the mutex is released in case of
+errors.
+
+Related to https://pagure.io/SSSD/sssd/issue/2478
+
+Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
+(cherry picked from commit 3e32cb2ad36a9dd2654c7f63469dc595f1bb8593)
+---
+ src/sss_client/idmap/common_ex.c     | 2 ++
+ src/sss_client/idmap/sss_nss_ex.c    | 5 ++++-
+ src/sss_client/idmap/sss_nss_idmap.c | 5 ++++-
+ 3 files changed, 10 insertions(+), 2 deletions(-)
+
+diff --git a/src/sss_client/idmap/common_ex.c b/src/sss_client/idmap/common_ex.c
+index 5efe9fabed7896ce674615472dbb256c4eae2144..e655bb864e063665cb56bcd3ff419e46c766478b 100644
+--- a/src/sss_client/idmap/common_ex.c
++++ b/src/sss_client/idmap/common_ex.c
+@@ -83,6 +83,7 @@ int sss_nss_timedlock(unsigned int timeout_ms, int *time_left_ms)
+     if (ret == 0) {
+         ret = clock_gettime(CLOCK_REALTIME, &endtime);
+         if (ret != 0) {
++            sss_nss_unlock();
+             return ret;
+         }
+ 
+@@ -92,6 +93,7 @@ int sss_nss_timedlock(unsigned int timeout_ms, int *time_left_ms)
+             TIMESPECSUB(&endtime, &starttime, &diff);
+             left = timeout_ms - TIMESPEC_TO_MS(&diff);
+             if (left <= 0) {
++                sss_nss_unlock();
+                 return EIO;
+             } else if (left > SSS_CLI_SOCKET_TIMEOUT) {
+                 *time_left_ms = SSS_CLI_SOCKET_TIMEOUT;
+diff --git a/src/sss_client/idmap/sss_nss_ex.c b/src/sss_client/idmap/sss_nss_ex.c
+index 861b1e1e92db4f7e6e8d74a812dc3c9220711773..af6a95180656b598bcb94c209dfa821cb0275f02 100644
+--- a/src/sss_client/idmap/sss_nss_ex.c
++++ b/src/sss_client/idmap/sss_nss_ex.c
+@@ -202,7 +202,10 @@ int sss_get_ex(struct nss_input *inp, uint32_t flags, unsigned int timeout)
+         }
+     }
+ 
+-    sss_nss_timedlock(timeout, &time_left);
++    ret = sss_nss_timedlock(timeout, &time_left);
++    if (ret != 0) {
++        return ret;
++    }
+ 
+     if (!skip_mc && !skip_data) {
+         /* previous thread might already initialize entry in mmap cache */
+diff --git a/src/sss_client/idmap/sss_nss_idmap.c b/src/sss_client/idmap/sss_nss_idmap.c
+index 6e7685d2b1d80956b6a6668e9bbb146abd9e86ed..cbf8c11f2870e3574c75fe109cb19268e8a0b56d 100644
+--- a/src/sss_client/idmap/sss_nss_idmap.c
++++ b/src/sss_client/idmap/sss_nss_idmap.c
+@@ -257,7 +257,10 @@ static int sss_nss_getyyybyxxx(union input inp, enum sss_cli_command cmd,
+     if (timeout == NO_TIMEOUT) {
+         sss_nss_lock();
+     } else {
+-        sss_nss_timedlock(timeout, &time_left);
++        ret = sss_nss_timedlock(timeout, &time_left);
++        if (ret != 0) {
++            return ret;
++        }
+     }
+ 
+     nret = sss_nss_make_request_timeout(cmd, &rd, time_left, &repbuf, &replen,
+-- 
+2.14.3
+
diff --git a/SOURCES/0097-DESKPROFILE-Add-checks-for-user-and-host-category.patch b/SOURCES/0097-DESKPROFILE-Add-checks-for-user-and-host-category.patch
new file mode 100644
index 0000000..1199481
--- /dev/null
+++ b/SOURCES/0097-DESKPROFILE-Add-checks-for-user-and-host-category.patch
@@ -0,0 +1,155 @@
+From 2349423ad813e8a4fe090c283603b4cf18919662 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Fabiano=20Fid=C3=AAncio?= <fidencio@redhat.com>
+Date: Mon, 22 Jan 2018 00:02:43 +0100
+Subject: [PATCH 97/97] DESKPROFILE: Add checks for user and host category
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+freeipa-deskprofile-plugin can have both user and host category set as
+"all" and when it happens, no users and groups or hosts or hostgroups
+are going to be set.
+
+Let's treat this expected (but so far missed) situation on SSSD side.
+
+Resolves:
+https://pagure.io/SSSD/sssd/issue/3449
+
+Signed-off-by: Fabiano Fidêncio <fidencio@redhat.com>
+
+Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
+(cherry picked from commit b72e444bc1cd2fe8d9617f09b446c678d4684fff)
+---
+ src/providers/ipa/ipa_deskprofile_rules_util.c | 100 ++++++++++++++++++++-----
+ 1 file changed, 82 insertions(+), 18 deletions(-)
+
+diff --git a/src/providers/ipa/ipa_deskprofile_rules_util.c b/src/providers/ipa/ipa_deskprofile_rules_util.c
+index 53c433145666af00a994420ccd1a926b11937fc9..01b7d0527c2a15e0f4d2bdce1867ad0482fca7b0 100644
+--- a/src/providers/ipa/ipa_deskprofile_rules_util.c
++++ b/src/providers/ipa/ipa_deskprofile_rules_util.c
+@@ -684,6 +684,8 @@ ipa_deskprofile_rules_save_rule_to_disk(
+     TALLOC_CTX *tmp_ctx;
+     const char *rule_name;
+     const char *data;
++    const char *hostcat;
++    const char *usercat;
+     char *shortname;
+     char *domainname;
+     char *base_dn;
+@@ -722,6 +724,28 @@ ipa_deskprofile_rules_save_rule_to_disk(
+         goto done;
+     }
+ 
++    ret = sysdb_attrs_get_string(rule, IPA_HOST_CATEGORY, &hostcat);
++    if (ret == ENOENT) {
++        hostcat = NULL;
++    } else if (ret != EOK) {
++        DEBUG(SSSDBG_TRACE_FUNC,
++              "Failed to get the Desktop Profile Rule host category for rule "
++              "\"%s\" [%d]: %s\n",
++              rule_name, ret, sss_strerror(ret));
++        goto done;
++    }
++
++    ret = sysdb_attrs_get_string(rule, IPA_USER_CATEGORY, &usercat);
++    if (ret == ENOENT) {
++        usercat = NULL;
++    } else if (ret != EOK) {
++        DEBUG(SSSDBG_TRACE_FUNC,
++              "Failed to get the Desktop Profile Rule user category for rule "
++              "\"%s\" [%d]: %s\n",
++              rule_name, ret, sss_strerror(ret));
++        goto done;
++    }
++
+     rule_prio = talloc_asprintf(tmp_ctx, "%06d", prio);
+     if (rule_prio == NULL) {
+         DEBUG(SSSDBG_CRIT_FAILURE, "Failed to allocate rule priority\n");
+@@ -753,26 +777,66 @@ ipa_deskprofile_rules_save_rule_to_disk(
+         goto done;
+     }
+ 
+-    ret = ipa_deskprofile_rule_check_memberuser(tmp_ctx, domain, rule,
+-                                                rule_name, rule_prio,
+-                                                base_dn, username,
+-                                                &user_prio, &group_prio);
+-    if (ret != EOK) {
+-        DEBUG(SSSDBG_CRIT_FAILURE,
+-              "ipa_deskprofile_rule_check_memberuser() failed [%d]: %s\n",
+-              ret, sss_strerror(ret));
+-        goto done;
++    if (usercat != NULL && strcasecmp(usercat, "all") == 0) {
++        user_prio = talloc_strdup(tmp_ctx, rule_prio);
++        if (user_prio == NULL) {
++            DEBUG(SSSDBG_CRIT_FAILURE,
++                  "Failed to allocate the user priority "
++                  "when user category is \"all\"\n");
++            ret = ENOMEM;
++            goto done;
++        }
++
++        group_prio = talloc_strdup(tmp_ctx, rule_prio);
++        if (group_prio == NULL) {
++            DEBUG(SSSDBG_CRIT_FAILURE,
++                  "Failed to allocate the group priority "
++                  "when user category is \"all\"\n");
++            ret = ENOMEM;
++            goto done;
++        }
++    } else {
++        ret = ipa_deskprofile_rule_check_memberuser(tmp_ctx, domain, rule,
++                                                    rule_name, rule_prio,
++                                                    base_dn, username,
++                                                    &user_prio, &group_prio);
++        if (ret != EOK) {
++            DEBUG(SSSDBG_CRIT_FAILURE,
++                  "ipa_deskprofile_rule_check_memberuser() failed [%d]: %s\n",
++                  ret, sss_strerror(ret));
++            goto done;
++        }
+     }
+ 
+-    ret = ipa_deskprofile_rule_check_memberhost(tmp_ctx, domain, rule,
+-                                                rule_name, rule_prio,
+-                                                base_dn, hostname,
+-                                                &host_prio, &hostgroup_prio);
+-    if (ret != EOK) {
+-        DEBUG(SSSDBG_CRIT_FAILURE,
+-              "ipa_deskprofile_rule_check_memberhost() failed [%d]: %s\n",
+-              ret, sss_strerror(ret));
+-        goto done;
++    if (hostcat != NULL && strcasecmp(hostcat, "all") == 0) {
++        host_prio = talloc_strdup(tmp_ctx, rule_prio);
++        if (host_prio == NULL) {
++            DEBUG(SSSDBG_CRIT_FAILURE,
++                  "Failed to allocate the host priority "
++                  "when host category is \"all\"\n");
++            ret = ENOMEM;
++            goto done;
++        }
++
++        hostgroup_prio = talloc_strdup(tmp_ctx, rule_prio);
++        if (hostgroup_prio == NULL) {
++            DEBUG(SSSDBG_CRIT_FAILURE,
++                  "Failed to allocate the hostgroup priority "
++                  "when host category is \"all\"\n");
++            ret = ENOMEM;
++            goto done;
++        }
++    } else {
++        ret = ipa_deskprofile_rule_check_memberhost(tmp_ctx, domain, rule,
++                                                    rule_name, rule_prio,
++                                                    base_dn, hostname,
++                                                    &host_prio, &hostgroup_prio);
++        if (ret != EOK) {
++            DEBUG(SSSDBG_CRIT_FAILURE,
++                  "ipa_deskprofile_rule_check_memberhost() failed [%d]: %s\n",
++                  ret, sss_strerror(ret));
++            goto done;
++        }
+     }
+ 
+     ret = ipa_deskprofile_get_normalized_rule_name(mem_ctx, rule_name,
+-- 
+2.14.3
+
diff --git a/SOURCES/0097-responders-do-not-leak-selinux-context-on-clients-de.patch b/SOURCES/0097-responders-do-not-leak-selinux-context-on-clients-de.patch
deleted file mode 100644
index a38530b..0000000
--- a/SOURCES/0097-responders-do-not-leak-selinux-context-on-clients-de.patch
+++ /dev/null
@@ -1,69 +0,0 @@
-From 3a07827a3722fd2166b94af1f5790273fbac01eb Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Pavel=20B=C5=99ezina?= <pbrezina@redhat.com>
-Date: Mon, 3 Apr 2017 12:56:01 +0200
-Subject: [PATCH 97/99] responders: do not leak selinux context on clients
- destruction
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-The SELinux context created in get_client_cred is not talloc bound and
-we were leaking it if available with each client's destruction.
-
-Resolves:
-https://pagure.io/SSSD/sssd/issue/3360
-
-Reviewed-by: Lukáš Slebodník <lslebodn@redhat.com>
-(cherry picked from commit 05c2c3047912fca1c1a35ab1c8d3157b05383495)
----
- src/responder/common/responder_common.c | 20 +++++++++++++++++++-
- 1 file changed, 19 insertions(+), 1 deletion(-)
-
-diff --git a/src/responder/common/responder_common.c b/src/responder/common/responder_common.c
-index 154d7dc7718c437d10e152fcba98161e2034fb14..67e1deefdfde19c95a68029b11099579d851513f 100644
---- a/src/responder/common/responder_common.c
-+++ b/src/responder/common/responder_common.c
-@@ -97,7 +97,7 @@ static errno_t get_client_cred(struct cli_ctx *cctx)
-     SEC_CTX secctx;
-     int ret;
- 
--    cctx->creds = talloc(cctx, struct cli_creds);
-+    cctx->creds = talloc_zero(cctx, struct cli_creds);
-     if (!cctx->creds) return ENOMEM;
- 
- #ifdef HAVE_UCRED
-@@ -464,6 +464,22 @@ static void client_fd_handler(struct tevent_context *ev,
- 
- static errno_t setup_client_idle_timer(struct cli_ctx *cctx);
- 
-+static int cli_ctx_destructor(struct cli_ctx *cctx)
-+{
-+    if (cctx->creds == NULL) {
-+        return 0;
-+    }
-+
-+    if (cctx->creds->selinux_ctx == NULL) {
-+        return 0;
-+    }
-+
-+    SELINUX_context_free(cctx->creds->selinux_ctx);
-+    cctx->creds->selinux_ctx = NULL;
-+
-+    return 0;
-+}
-+
- struct accept_fd_ctx {
-     struct resp_ctx *rctx;
-     bool is_private;
-@@ -520,6 +536,8 @@ static void accept_fd_handler(struct tevent_context *ev,
-         return;
-     }
- 
-+    talloc_set_destructor(cctx, cli_ctx_destructor);
-+
-     len = sizeof(cctx->addr);
-     cctx->cfd = accept(fd, (struct sockaddr *)&cctx->addr, &len);
-     if (cctx->cfd == -1) {
--- 
-2.9.3
-
diff --git a/SOURCES/0098-SELINUX-Check-if-SELinux-is-managed-in-selinux_child.patch b/SOURCES/0098-SELINUX-Check-if-SELinux-is-managed-in-selinux_child.patch
new file mode 100644
index 0000000..c0912b0
--- /dev/null
+++ b/SOURCES/0098-SELINUX-Check-if-SELinux-is-managed-in-selinux_child.patch
@@ -0,0 +1,203 @@
+From ae37ee533a791e038aab47683278fced2bc0b687 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Michal=20=C5=BDidek?= <mzidek@redhat.com>
+Date: Thu, 1 Feb 2018 11:34:21 +0100
+Subject: [PATCH 98/99] SELINUX: Check if SELinux is managed in selinux_child
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+If SELinux policy is not managed at all, don't call any SELinux user
+handling functions and instead return that no update is needed.
+
+Pair-Programmed-With: Jakub Hrozek <jhrozek@redhat.com>
+Reviewed-by: Lukáš Slebodník <lslebodn@redhat.com>
+Reviewed-by: Fabiano Fidêncio <fidencio@redhat.com>
+
+Resolves:
+https://pagure.io/SSSD/sssd/issue/3618
+(cherry picked from commit 450b472a68abf442479755c7916c757907b35ea5)
+---
+ src/providers/ipa/selinux_child.c |  3 +-
+ src/util/sss_semanage.c           | 82 +++++++++++++++++++++++++++++++--------
+ src/util/util.h                   |  3 ++
+ 3 files changed, 70 insertions(+), 18 deletions(-)
+
+diff --git a/src/providers/ipa/selinux_child.c b/src/providers/ipa/selinux_child.c
+index a7e20f715626d0f3ecef7cc06f3de5d44b6a15c1..c659976e80cb7317671da52fe4777ee821589e36 100644
+--- a/src/providers/ipa/selinux_child.c
++++ b/src/providers/ipa/selinux_child.c
+@@ -27,7 +27,6 @@
+ #include <unistd.h>
+ #include <sys/stat.h>
+ #include <popt.h>
+-#include <selinux/selinux.h>
+ 
+ #include "util/util.h"
+ #include "util/child_common.h"
+@@ -173,7 +172,7 @@ static bool seuser_needs_update(struct input_buffer *ibuf)
+     char *db_mls_range = NULL;
+     errno_t ret;
+ 
+-    ret = getseuserbyname(ibuf->username, &db_seuser, &db_mls_range);
++    ret = sss_get_seuser(ibuf->username, &db_seuser, &db_mls_range);
+     DEBUG(SSSDBG_TRACE_INTERNAL,
+           "getseuserbyname: ret: %d seuser: %s mls: %s\n",
+           ret, db_seuser ? db_seuser : "unknown",
+diff --git a/src/util/sss_semanage.c b/src/util/sss_semanage.c
+index 37278cc986a1ea49dc2218a635d52b9d54ca089d..25b6bcdad2ad7e7ac710497f13d6a6e22360b0dd 100644
+--- a/src/util/sss_semanage.c
++++ b/src/util/sss_semanage.c
+@@ -22,8 +22,9 @@
+ #include "config.h"
+ 
+ #include <stdio.h>
+-#ifdef HAVE_SEMANAGE
++#if defined(HAVE_SEMANAGE) && defined(HAVE_SELINUX)
+ #include <semanage/semanage.h>
++#include <selinux/selinux.h>
+ #endif
+ 
+ #include "util/util.h"
+@@ -32,7 +33,7 @@
+ #define DEFAULT_SERANGE "s0"
+ #endif
+ 
+-#ifdef HAVE_SEMANAGE
++#if defined(HAVE_SEMANAGE) && defined(HAVE_SELINUX)
+ /* turn libselinux messages into SSSD DEBUG() calls */
+ static void sss_semanage_error_callback(void *varg,
+                                         semanage_handle_t *handle,
+@@ -73,33 +74,47 @@ static void sss_semanage_close(semanage_handle_t *handle)
+     semanage_handle_destroy(handle);
+ }
+ 
+-static int sss_semanage_init(semanage_handle_t **_handle)
++static int sss_is_selinux_managed(semanage_handle_t *handle)
+ {
+     int ret;
+-    semanage_handle_t *handle = NULL;
+ 
+-    handle = semanage_handle_create();
+-    if (!handle) {
+-        DEBUG(SSSDBG_CRIT_FAILURE, "Cannot create SELinux management handle\n");
+-        ret = EIO;
+-        goto done;
++    if (handle == NULL) {
++        return EINVAL;
+     }
+ 
+-    semanage_msg_set_callback(handle,
+-                              sss_semanage_error_callback,
+-                              NULL);
+-
+     ret = semanage_is_managed(handle);
+     if (ret == 0) {
+         DEBUG(SSSDBG_TRACE_FUNC, "SELinux policy not managed via libsemanage\n");
+-        ret = ERR_SELINUX_NOT_MANAGED;
+-        goto done;
++        return ERR_SELINUX_NOT_MANAGED;
+     } else if (ret == -1) {
+         DEBUG(SSSDBG_CRIT_FAILURE, "Call to semanage_is_managed failed\n");
++        return EIO;
++    }
++
++    return EOK;
++}
++
++static int sss_semanage_init(semanage_handle_t **_handle)
++{
++    int ret;
++    semanage_handle_t *handle = NULL;
++
++    handle = semanage_handle_create();
++    if (!handle) {
++        DEBUG(SSSDBG_CRIT_FAILURE, "Cannot create SELinux management handle\n");
+         ret = EIO;
+         goto done;
+     }
+ 
++    semanage_msg_set_callback(handle,
++                              sss_semanage_error_callback,
++                              NULL);
++
++    ret = sss_is_selinux_managed(handle);
++    if (ret != EOK) {
++        goto done;
++    }
++
+     ret = semanage_access_check(handle);
+     if (ret < SEMANAGE_CAN_READ) {
+         DEBUG(SSSDBG_CRIT_FAILURE, "Cannot read SELinux policy store\n");
+@@ -229,6 +244,34 @@ done:
+     return ret;
+ }
+ 
++int sss_get_seuser(const char *linuxuser,
++                   char **selinuxuser,
++                   char **level)
++{
++    int ret;
++    semanage_handle_t *handle;
++
++    handle = semanage_handle_create();
++    if (handle == NULL) {
++        DEBUG(SSSDBG_CRIT_FAILURE, "Cannot create SELinux management handle\n");
++        return EIO;
++    }
++
++    semanage_msg_set_callback(handle,
++                              sss_semanage_error_callback,
++                              NULL);
++
++    /* We only needed the handle for this call. Close the handle right
++     * after it */
++    ret = sss_is_selinux_managed(handle);
++    sss_semanage_close(handle);
++    if (ret != EOK) {
++        return ret;
++    }
++
++    return getseuserbyname(linuxuser, selinuxuser, level);
++}
++
+ int set_seuser(const char *login_name, const char *seuser_name,
+                const char *mls)
+ {
+@@ -382,7 +425,7 @@ done:
+     sss_semanage_close(handle);
+     return ret;
+ }
+-#else /* HAVE_SEMANAGE */
++#else /* HAVE_SEMANAGE && HAVE_SELINUX */
+ int set_seuser(const char *login_name, const char *seuser_name,
+                const char *mls)
+ {
+@@ -393,4 +436,11 @@ int del_seuser(const char *login_name)
+ {
+     return EOK;
+ }
++
++int sss_get_seuser(const char *linuxuser,
++                   char **selinuxuser,
++                   char **level)
++{
++    return EOK;
++}
+ #endif  /* HAVE_SEMANAGE */
+diff --git a/src/util/util.h b/src/util/util.h
+index 2521b1789b0b8701b1fbcce33890eedb7fe18d5e..be818a9531897e4f988cae48bf6ba30aea0e6d56 100644
+--- a/src/util/util.h
++++ b/src/util/util.h
+@@ -654,6 +654,9 @@ errno_t restore_creds(struct sss_creds *saved_creds);
+ int set_seuser(const char *login_name, const char *seuser_name,
+                const char *mlsrange);
+ int del_seuser(const char *login_name);
++int sss_get_seuser(const char *linuxuser,
++                   char **selinuxuser,
++                   char **level);
+ 
+ /* convert time from generalized form to unix time */
+ errno_t sss_utc_to_time_t(const char *str, const char *format, time_t *unix_time);
+-- 
+2.14.3
+
diff --git a/SOURCES/0098-ipa_s2n_get_acct_info_send-provide-correct-req_input.patch b/SOURCES/0098-ipa_s2n_get_acct_info_send-provide-correct-req_input.patch
deleted file mode 100644
index 5a49b15..0000000
--- a/SOURCES/0098-ipa_s2n_get_acct_info_send-provide-correct-req_input.patch
+++ /dev/null
@@ -1,89 +0,0 @@
-From 250777f65dc23917c436d3ecf0fe21abc65db65e Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Pavel=20B=C5=99ezina?= <pbrezina@redhat.com>
-Date: Mon, 3 Apr 2017 12:09:44 +0200
-Subject: [PATCH 98/99] ipa_s2n_get_acct_info_send: provide correct req_input
- name
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-To avoid crash.
-
-Resolves:
-https://pagure.io/SSSD/sssd/issue/3358
-
-Reviewed-by: Sumit Bose <sbose@redhat.com>
-Reviewed-by: Lukáš Slebodník <lslebodn@redhat.com>
-(cherry picked from commit b07bcd8b99590bd404733fa7ff1add37c55126bc)
----
- src/providers/ipa/ipa_s2n_exop.c | 40 ++++++++++++++++++++++++++++++++++++----
- 1 file changed, 36 insertions(+), 4 deletions(-)
-
-diff --git a/src/providers/ipa/ipa_s2n_exop.c b/src/providers/ipa/ipa_s2n_exop.c
-index 8a3391b4093f1547d84fe44a0f24b1d063d1e28c..2173db357700499a6140aa61841e443139981483 100644
---- a/src/providers/ipa/ipa_s2n_exop.c
-+++ b/src/providers/ipa/ipa_s2n_exop.c
-@@ -1054,6 +1054,33 @@ static const char *ipa_s2n_reqtype2str(enum request_types request_type)
-     return "Unknown request type";
- }
- 
-+static const char *ipa_s2n_reqinp2str(TALLOC_CTX *mem_ctx,
-+                                      struct req_input *req_input)
-+{
-+    const char *str = NULL;
-+
-+    switch (req_input->type) {
-+    case REQ_INP_NAME:
-+        str = talloc_strdup(mem_ctx, req_input->inp.name);
-+        break;
-+    case REQ_INP_SECID:
-+        str = talloc_strdup(mem_ctx, req_input->inp.secid);
-+        break;
-+    case REQ_INP_CERT:
-+        str = talloc_strdup(mem_ctx, req_input->inp.cert);
-+        break;
-+    case REQ_INP_ID:
-+        str = talloc_asprintf(mem_ctx, "%u", req_input->inp.id);
-+        break;
-+    }
-+
-+    if (str == NULL) {
-+        DEBUG(SSSDBG_CRIT_FAILURE, "Out of memory!\n");
-+    }
-+
-+    return str;
-+}
-+
- struct ipa_s2n_get_list_state {
-     struct tevent_context *ev;
-     struct ipa_id_ctx *ipa_ctx;
-@@ -1410,6 +1437,7 @@ struct tevent_req *ipa_s2n_get_acct_info_send(TALLOC_CTX *mem_ctx,
-     struct tevent_req *req;
-     struct tevent_req *subreq;
-     struct berval *bv_req = NULL;
-+    const char *input;
-     int ret = EFAULT;
-     bool is_v1 = false;
- 
-@@ -1454,10 +1482,14 @@ struct tevent_req *ipa_s2n_get_acct_info_send(TALLOC_CTX *mem_ctx,
-         goto fail;
-     }
- 
--    DEBUG(SSSDBG_TRACE_FUNC, "Sending request_type: [%s] for trust user [%s] "
--                            "to IPA server\n",
--                            ipa_s2n_reqtype2str(state->request_type),
--                            req_input->inp.name);
-+    if (DEBUG_IS_SET(SSSDBG_TRACE_FUNC)) {
-+        input = ipa_s2n_reqinp2str(state, req_input);
-+        DEBUG(SSSDBG_TRACE_FUNC,
-+              "Sending request_type: [%s] for trust user [%s] to IPA server\n",
-+              ipa_s2n_reqtype2str(state->request_type),
-+              input);
-+        talloc_zfree(input);
-+    }
- 
-     subreq = ipa_s2n_exop_send(state, state->ev, state->sh, is_v1,
-                                state->exop_timeout, bv_req);
--- 
-2.9.3
-
diff --git a/SOURCES/0099-config-check-Message-when-sssd.conf-is-missing.patch b/SOURCES/0099-config-check-Message-when-sssd.conf-is-missing.patch
deleted file mode 100644
index 2862d4d..0000000
--- a/SOURCES/0099-config-check-Message-when-sssd.conf-is-missing.patch
+++ /dev/null
@@ -1,39 +0,0 @@
-From be05d577626835e3c72d71fc60e6abfa564c7cbe Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Michal=20=C5=BDidek?= <mzidek@redhat.com>
-Date: Tue, 14 Mar 2017 15:43:41 +0100
-Subject: [PATCH 99/99] config-check: Message when sssd.conf is missing
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-sssctl config-check should print a message for user
-if no sssd.conf was found.
-
-Resolves:
-https://pagure.io/SSSD/sssd/issue/3330
-
-Reviewed-by: Lukáš Slebodník <lslebodn@redhat.com>
-(cherry picked from commit 955574eeb3a3b937abc3df150e9bbbb79b75c889)
----
- src/tools/sssctl/sssctl_config.c | 5 ++++-
- 1 file changed, 4 insertions(+), 1 deletion(-)
-
-diff --git a/src/tools/sssctl/sssctl_config.c b/src/tools/sssctl/sssctl_config.c
-index 630df3c8ff5368ef253bb9753380e94c8c0a307d..7e3ebf5428ce3fef232eee7334c7fd90e904b2d3 100644
---- a/src/tools/sssctl/sssctl_config.c
-+++ b/src/tools/sssctl/sssctl_config.c
-@@ -63,7 +63,10 @@ errno_t sssctl_config_check(struct sss_cmdline *cmdline,
- 
-     /* Open config file */
-     ret = sss_ini_config_file_open(init_data, SSSD_CONFIG_FILE);
--    if (ret != EOK) {
-+    if (ret == ENOENT) {
-+        ERROR("File %1$s does not exist.\n", SSSD_CONFIG_FILE);
-+        goto done;
-+    } else if (ret != EOK) {
-         DEBUG(SSSDBG_TRACE_FUNC,
-               "sss_ini_config_file_open failed: %s [%d]\n",
-               sss_strerror(ret),
--- 
-2.9.3
-
diff --git a/SOURCES/0099-util-Add-sss_-prefix-to-some-functions.patch b/SOURCES/0099-util-Add-sss_-prefix-to-some-functions.patch
new file mode 100644
index 0000000..829aea5
--- /dev/null
+++ b/SOURCES/0099-util-Add-sss_-prefix-to-some-functions.patch
@@ -0,0 +1,142 @@
+From 9e1df30e737566ca92c93cb09028717415120f47 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Michal=20=C5=BDidek?= <mzidek@redhat.com>
+Date: Tue, 6 Feb 2018 19:17:55 +0100
+Subject: [PATCH 99/99] util: Add sss_ prefix to some functions
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Add sss_ prefix to del_seuser and set_seuser for consistency
+with sss_get_seuser. Also sss_ prefix makes it clear that
+these functions come from SSSD.
+
+Reviewed-by: Lukáš Slebodník <lslebodn@redhat.com>
+
+Resolves:
+https://pagure.io/SSSD/sssd/issue/3618
+(cherry picked from commit 6b9c38df5712b951e31800efea2df0802e333e08)
+---
+ src/providers/ipa/selinux_child.c |  4 ++--
+ src/tools/sss_useradd.c           |  2 +-
+ src/tools/sss_userdel.c           |  2 +-
+ src/tools/sss_usermod.c           |  2 +-
+ src/util/sss_semanage.c           | 12 ++++++------
+ src/util/util.h                   |  6 +++---
+ 6 files changed, 14 insertions(+), 14 deletions(-)
+
+diff --git a/src/providers/ipa/selinux_child.c b/src/providers/ipa/selinux_child.c
+index c659976e80cb7317671da52fe4777ee821589e36..a601b28c4c68afca51758b1967d1bfc1d51fb450 100644
+--- a/src/providers/ipa/selinux_child.c
++++ b/src/providers/ipa/selinux_child.c
+@@ -157,9 +157,9 @@ static int sc_set_seuser(const char *login_name, const char *seuser_name,
+          * default. We need to remove the SELinux user from the DB
+          * in that case
+          */
+-        ret = del_seuser(login_name);
++        ret = sss_del_seuser(login_name);
+     } else {
+-        ret = set_seuser(login_name, seuser_name, mls);
++        ret = sss_set_seuser(login_name, seuser_name, mls);
+     }
+     umask(old_mask);
+     return ret;
+diff --git a/src/tools/sss_useradd.c b/src/tools/sss_useradd.c
+index 8521b83011b42c9e2acca4136f154acb3919440c..ca2cbd6c119e5a1735e5b3b524cddeccb68a2578 100644
+--- a/src/tools/sss_useradd.c
++++ b/src/tools/sss_useradd.c
+@@ -205,7 +205,7 @@ int main(int argc, const char **argv)
+ 
+     /* Set SELinux login context - must be done after transaction is done
+      * b/c libselinux calls getpwnam */
+-    ret = set_seuser(tctx->octx->name, pc_selinux_user, NULL);
++    ret = sss_set_seuser(tctx->octx->name, pc_selinux_user, NULL);
+     if (ret != EOK) {
+         ERROR("Cannot set SELinux login context\n");
+         ret = EXIT_FAILURE;
+diff --git a/src/tools/sss_userdel.c b/src/tools/sss_userdel.c
+index d085dc3cabd31b2ee82b13c6cbc39c7658b071d1..fb0f2c2ab6163738da2dcf4177c06cd5dc524345 100644
+--- a/src/tools/sss_userdel.c
++++ b/src/tools/sss_userdel.c
+@@ -254,7 +254,7 @@ int main(int argc, const char **argv)
+ 
+     /* Set SELinux login context - must be done after transaction is done
+      * b/c libselinux calls getpwnam */
+-    ret = del_seuser(tctx->octx->name);
++    ret = sss_del_seuser(tctx->octx->name);
+     if (ret != EOK) {
+         ERROR("Cannot reset SELinux login context\n");
+         ret = EXIT_FAILURE;
+diff --git a/src/tools/sss_usermod.c b/src/tools/sss_usermod.c
+index 55e94394766f5f46bb3c14c231186f2d79d6b6ab..6a818f13ad2a7e087e23fa2190b83aeb1eabdbac 100644
+--- a/src/tools/sss_usermod.c
++++ b/src/tools/sss_usermod.c
+@@ -300,7 +300,7 @@ int main(int argc, const char **argv)
+ 
+     /* Set SELinux login context - must be done after transaction is done
+      * b/c libselinux calls getpwnam */
+-    ret = set_seuser(tctx->octx->name, pc_selinux_user, NULL);
++    ret = sss_set_seuser(tctx->octx->name, pc_selinux_user, NULL);
+     if (ret != EOK) {
+         ERROR("Cannot set SELinux login context\n");
+         ret = EXIT_FAILURE;
+diff --git a/src/util/sss_semanage.c b/src/util/sss_semanage.c
+index 25b6bcdad2ad7e7ac710497f13d6a6e22360b0dd..1150b6236c2c227fe2fc69f2505b6e254a1e64ec 100644
+--- a/src/util/sss_semanage.c
++++ b/src/util/sss_semanage.c
+@@ -272,8 +272,8 @@ int sss_get_seuser(const char *linuxuser,
+     return getseuserbyname(linuxuser, selinuxuser, level);
+ }
+ 
+-int set_seuser(const char *login_name, const char *seuser_name,
+-               const char *mls)
++int sss_set_seuser(const char *login_name, const char *seuser_name,
++                   const char *mls)
+ {
+     semanage_handle_t *handle = NULL;
+     semanage_seuser_key_t *key = NULL;
+@@ -346,7 +346,7 @@ done:
+     return ret;
+ }
+ 
+-int del_seuser(const char *login_name)
++int sss_del_seuser(const char *login_name)
+ {
+     semanage_handle_t *handle = NULL;
+     semanage_seuser_key_t *key = NULL;
+@@ -426,13 +426,13 @@ done:
+     return ret;
+ }
+ #else /* HAVE_SEMANAGE && HAVE_SELINUX */
+-int set_seuser(const char *login_name, const char *seuser_name,
+-               const char *mls)
++int sss_set_seuser(const char *login_name, const char *seuser_name,
++                   const char *mls)
+ {
+     return EOK;
+ }
+ 
+-int del_seuser(const char *login_name)
++int sss_del_seuser(const char *login_name)
+ {
+     return EOK;
+ }
+diff --git a/src/util/util.h b/src/util/util.h
+index be818a9531897e4f988cae48bf6ba30aea0e6d56..ef8ef7f57d7949aa2735171f11195dbcdc42288a 100644
+--- a/src/util/util.h
++++ b/src/util/util.h
+@@ -651,9 +651,9 @@ errno_t restore_creds(struct sss_creds *saved_creds);
+  * certain permissions. Therefore the caller should make sure the umask is
+  * not too restricted (especially when called from the daemon code).
+  */
+-int set_seuser(const char *login_name, const char *seuser_name,
+-               const char *mlsrange);
+-int del_seuser(const char *login_name);
++int sss_set_seuser(const char *login_name, const char *seuser_name,
++                   const char *mlsrange);
++int sss_del_seuser(const char *login_name);
+ int sss_get_seuser(const char *linuxuser,
+                    char **selinuxuser,
+                    char **level);
+-- 
+2.14.3
+
diff --git a/SOURCES/0100-MAN-Explain-how-does-auto_private_groups-affect-subd.patch b/SOURCES/0100-MAN-Explain-how-does-auto_private_groups-affect-subd.patch
new file mode 100644
index 0000000..ae772b2
--- /dev/null
+++ b/SOURCES/0100-MAN-Explain-how-does-auto_private_groups-affect-subd.patch
@@ -0,0 +1,43 @@
+From a09a7a03f5b330cc45a0007a56d4789116a91e46 Mon Sep 17 00:00:00 2001
+From: amitkuma <amitkuma@redhat.com>
+Date: Tue, 6 Feb 2018 16:27:00 +0530
+Subject: [PATCH 100/100] MAN: Explain how does auto_private_groups affect
+ subdomains
+
+Fix explains how auto_private_groups affects subdomains.
+a. POSIX sudomains, gets inherited to subdomain.
+b. ID-mapping subdomains, already enabled.
+
+Resolves: https://pagure.io/SSSD/sssd/issue/3627
+
+Reviewed-by: Rob Crittenden <rcritten@redhat.com>
+(cherry picked from commit 52ae76737f2df3012d67f6a0b5052c86022bffdd)
+---
+ src/man/sssd.conf.5.xml | 11 ++++++++++-
+ 1 file changed, 10 insertions(+), 1 deletion(-)
+
+diff --git a/src/man/sssd.conf.5.xml b/src/man/sssd.conf.5.xml
+index 47da07c33bdcfbf2fa94ff932492e9ea4bbfe846..8d06f57539e3fc55189234aab2aea950ba14713a 100644
+--- a/src/man/sssd.conf.5.xml
++++ b/src/man/sssd.conf.5.xml
+@@ -2830,7 +2830,16 @@ subdomain_inherit = ldap_purge_cache_timeout
+                             If this option is enabled, SSSD will automatically
+                             create user private groups based on user's
+                             UID number. The GID number is ignored in this case.
+-                        </para>
++		        </para>
++			<para>
++			    For POSIX subdomains, setting the option in the main
++			    domain is inherited in the subdomain.
++			</para>
++			<para>
++			    For ID-mapping subdomains, auto_private_groups is
++			    already enabled for the subdomains and setting it to
++			    false will not have any effect for the subdomain.
++			</para>
+                         <para>
+                             NOTE: Because the GID number and the user private group
+                             are inferred frm the UID number, it is not supported
+-- 
+2.14.3
+
diff --git a/SOURCES/0100-sbus-check-connection-for-NULL-before-unregister-it.patch b/SOURCES/0100-sbus-check-connection-for-NULL-before-unregister-it.patch
deleted file mode 100644
index 66fee16..0000000
--- a/SOURCES/0100-sbus-check-connection-for-NULL-before-unregister-it.patch
+++ /dev/null
@@ -1,44 +0,0 @@
-From 556eb1200a3754935f573ccffee87554bf9e9296 Mon Sep 17 00:00:00 2001
-From: Sumit Bose <sbose@redhat.com>
-Date: Mon, 10 Apr 2017 13:45:27 +0200
-Subject: [PATCH 100/101] sbus: check connection for NULL before unregister it
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-There seem to be code paths where the data is a added to the hash before
-the connection is properly initialized, to avoid core dump during shut
-down we only call dbus_conection_unregister_object_path() if there is a
-connection.
-
-Resolves:
-https://pagure.io/SSSD/sssd/issue/3367
-
-Reviewed-by: Pavel Březina <pbrezina@redhat.com>
-(cherry picked from commit 35186217d44d0138a1aedf7a4db72249b2c40e66)
----
- src/sbus/sssd_dbus_interface.c | 8 +++++++-
- 1 file changed, 7 insertions(+), 1 deletion(-)
-
-diff --git a/src/sbus/sssd_dbus_interface.c b/src/sbus/sssd_dbus_interface.c
-index 1a11c6abcf23053e3b8c77f4d469d7c202a88eb8..c9007a4814e09e26fedaf605ca7313234d5ebf2c 100644
---- a/src/sbus/sssd_dbus_interface.c
-+++ b/src/sbus/sssd_dbus_interface.c
-@@ -490,7 +490,13 @@ sbus_opath_hash_delete_cb(hash_entry_t *item,
-     conn = talloc_get_type(pvt, struct sbus_connection);
-     path = sbus_opath_get_base_path(NULL, item->key.str);
- 
--    dbus_connection_unregister_object_path(conn->dbus.conn, path);
-+    /* There seem to be code paths where the data is added to the hash
-+     * before the connection is properly initialized, to avoid core dump
-+     * during shut down we only call dbus_connection_unregister_object_path()
-+     * if there is a connection. */
-+    if (conn->dbus.conn != NULL) {
-+        dbus_connection_unregister_object_path(conn->dbus.conn, path);
-+    }
- }
- 
- hash_table_t *
--- 
-2.9.3
-
diff --git a/SOURCES/0101-AD-Use-the-right-sdap_domain-for-the-forest-root.patch b/SOURCES/0101-AD-Use-the-right-sdap_domain-for-the-forest-root.patch
new file mode 100644
index 0000000..128a9ea
--- /dev/null
+++ b/SOURCES/0101-AD-Use-the-right-sdap_domain-for-the-forest-root.patch
@@ -0,0 +1,201 @@
+From d56d41e76741f418e88e479b91193db3ee3f1688 Mon Sep 17 00:00:00 2001
+From: Jakub Hrozek <jhrozek@redhat.com>
+Date: Wed, 17 Jan 2018 21:59:24 +0100
+Subject: [PATCH 101/101] AD: Use the right sdap_domain for the forest root
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Each ad_id_ctx structure which represents a trusted AD domain contains a
+list of sdap_domain structures representing all the other domains. This
+is used to e.g. be able to reach another domain's ad_id_ctx and use its
+LDAP connection.
+
+However, the sdap search call that was searching for trusted domains in
+the forest that the root domain knows about, was unconditionally using
+the first sdap_domain structure in the list linked from the root_domain's
+ad_id_ctx structure.
+
+It should be noted that this search only happens in case the machine is
+joined to one of the non-root domains in the forest and searches the root
+domain explicitly.
+
+In case sdap_domain structures linked from the ad_id_ctx representing
+the root domain were ordered so that the first sdap_domain in the list
+was representing a different domain than the one linked from the
+ad_id_ctx, the sdap search would have used a wrong search base derived
+from the unexpected sdap_domain which would result in a referral being
+returned.
+
+This patch explicitly looks up the sdap_domain structure that
+corresponds to the root domain.
+
+Resolves:
+https://pagure.io/SSSD/sssd/issue/3594
+
+Reviewed-by: Sumit Bose <sbose@redhat.com>
+Reviewed-by: Lukáš Slebodník <lslebodn@redhat.com>
+(cherry picked from commit 9ac071272ce0152eb293d3181a5c12b395655521)
+---
+ src/providers/ad/ad_subdomains.c | 110 +++++++++++++++++++++++++++------------
+ 1 file changed, 77 insertions(+), 33 deletions(-)
+
+diff --git a/src/providers/ad/ad_subdomains.c b/src/providers/ad/ad_subdomains.c
+index 1b9483a5dce937d6acdd813486a1e8c18210d35f..bd94ba8ea93679df8d01508b3d4d85217d9c1c87 100644
+--- a/src/providers/ad/ad_subdomains.c
++++ b/src/providers/ad/ad_subdomains.c
+@@ -57,6 +57,71 @@
+ /* do not refresh more often than every 5 seconds for now */
+ #define AD_SUBDOMAIN_REFRESH_LIMIT 5
+ 
++static struct sss_domain_info *
++ads_get_root_domain(struct be_ctx *be_ctx, struct sysdb_attrs *attrs)
++{
++    struct sss_domain_info *dom;
++    const char *name;
++    errno_t ret;
++
++    if (attrs == NULL) {
++        /* Clients joined to the forest root directly don't even discover
++         * the root domain, so the attrs are expected to be NULL in this
++         * case
++         */
++        return be_ctx->domain;
++    }
++
++    ret = sysdb_attrs_get_string(attrs, AD_AT_TRUST_PARTNER, &name);
++    if (ret != EOK) {
++        DEBUG(SSSDBG_OP_FAILURE, "sysdb_attrs_get_string failed.\n");
++        return NULL;
++    }
++
++    /* With a subsequent run, the root should already be known */
++    for (dom = be_ctx->domain; dom != NULL;
++         dom = get_next_domain(dom, SSS_GND_ALL_DOMAINS)) {
++
++        if (strcasecmp(dom->name, name) == 0) {
++            /* The forest root is special, although it might be disabled for
++             * general lookups we still want to try to get the domains in the
++             * forest from a DC of the forest root */
++            if (sss_domain_get_state(dom) == DOM_DISABLED
++                    && !sss_domain_is_forest_root(dom)) {
++                return NULL;
++            }
++            return dom;
++        }
++    }
++
++    return NULL;
++}
++
++static struct sdap_domain *
++ads_get_root_sdap_domain(struct be_ctx *be_ctx,
++                         struct sdap_options *opts,
++                         struct sysdb_attrs *attrs)
++{
++    struct sdap_domain *root_sdom;
++    struct sss_domain_info *root_dom;
++
++    root_dom = ads_get_root_domain(be_ctx, attrs);
++    if (root_dom == NULL) {
++        DEBUG(SSSDBG_OP_FAILURE,
++              "ads_get_root_domain did not find the domain\n");
++        return NULL;
++    }
++
++    root_sdom = sdap_domain_get(opts, root_dom);
++    if (root_sdom == NULL) {
++        DEBUG(SSSDBG_OP_FAILURE,
++              "Failed to find sdap_domain for the root domain\n");
++        return NULL;
++    }
++
++    return root_sdom;
++}
++
+ static errno_t ad_get_enabled_domains(TALLOC_CTX *mem_ctx,
+                                       struct ad_id_ctx *ad_id_ctx,
+                                       const char *ad_domain,
+@@ -755,6 +820,7 @@ struct ad_get_slave_domain_state {
+     struct sdap_options *opts;
+     struct sdap_idmap_ctx *idmap_ctx;
+     struct sysdb_attrs *root_attrs;
++    struct sdap_domain *root_sdom;
+     struct sdap_id_op *sdap_op;
+ };
+ 
+@@ -786,6 +852,13 @@ ad_get_slave_domain_send(TALLOC_CTX *mem_ctx,
+     state->opts = root_id_ctx->sdap_id_ctx->opts;
+     state->idmap_ctx = root_id_ctx->sdap_id_ctx->opts->idmap_ctx;
+     state->root_attrs = root_attrs;
++    state->root_sdom = ads_get_root_sdap_domain(state->be_ctx,
++                                                state->opts,
++                                                state->root_attrs);
++    if (state->root_sdom == NULL) {
++        ret = ERR_DOMAIN_NOT_FOUND;
++        goto immediately;
++    }
+ 
+     state->sdap_op = sdap_id_op_create(state, root_id_ctx->ldap_ctx->conn_cache);
+     if (state->sdap_op == NULL) {
+@@ -861,7 +934,7 @@ static void ad_get_slave_domain_connect_done(struct tevent_req *subreq)
+ 
+     subreq = sdap_search_bases_send(state, state->ev, state->opts,
+                                     sdap_id_op_handle(state->sdap_op),
+-                                    state->opts->sdom->search_bases,
++                                    state->root_sdom->search_bases,
+                                     NULL, false, 0,
+                                     SLAVE_DOMAIN_FILTER, attrs);
+     if (subreq == NULL) {
+@@ -965,38 +1038,6 @@ static errno_t ad_get_slave_domain_recv(struct tevent_req *req)
+     return EOK;
+ }
+ 
+-static struct sss_domain_info *
+-ads_get_root_domain(struct be_ctx *be_ctx, struct sysdb_attrs *attrs)
+-{
+-    struct sss_domain_info *dom;
+-    const char *name;
+-    errno_t ret;
+-
+-    ret = sysdb_attrs_get_string(attrs, AD_AT_TRUST_PARTNER, &name);
+-    if (ret != EOK) {
+-        DEBUG(SSSDBG_OP_FAILURE, "sysdb_attrs_get_string failed.\n");
+-        return NULL;
+-    }
+-
+-    /* With a subsequent run, the root should already be known */
+-    for (dom = be_ctx->domain; dom != NULL;
+-         dom = get_next_domain(dom, SSS_GND_ALL_DOMAINS)) {
+-
+-        if (strcasecmp(dom->name, name) == 0) {
+-            /* The forest root is special, although it might be disabled for
+-             * general lookups we still want to try to get the domains in the
+-             * forest from a DC of the forest root */
+-            if (sss_domain_get_state(dom) == DOM_DISABLED
+-                    && !sss_domain_is_forest_root(dom)) {
+-                return NULL;
+-            }
+-            return dom;
+-        }
+-    }
+-
+-    return NULL;
+-}
+-
+ static struct ad_id_ctx *
+ ads_get_root_id_ctx(struct be_ctx *be_ctx,
+                     struct ad_id_ctx *ad_id_ctx,
+@@ -1416,6 +1457,9 @@ static void ad_subdomains_refresh_root_done(struct tevent_req *subreq)
+     req = tevent_req_callback_data(subreq, struct tevent_req);
+     state = tevent_req_data(req, struct ad_subdomains_refresh_state);
+ 
++    /* Note: For clients joined to the root domain, root_attrs is NULL,
++     * see ad_get_root_domain_send()
++     */
+     ret = ad_get_root_domain_recv(state, subreq, &root_attrs, &root_id_ctx);
+     talloc_zfree(subreq);
+     if (ret != EOK) {
+-- 
+2.14.3
+
diff --git a/SOURCES/0101-selinux-Do-not-fail-if-SELinux-is-not-managed.patch b/SOURCES/0101-selinux-Do-not-fail-if-SELinux-is-not-managed.patch
deleted file mode 100644
index 1ae6353..0000000
--- a/SOURCES/0101-selinux-Do-not-fail-if-SELinux-is-not-managed.patch
+++ /dev/null
@@ -1,211 +0,0 @@
-From 9b7c29b67ec845b2004d6bcac2bcceabfd855f1e Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Michal=20=C5=BDidek?= <mzidek@redhat.com>
-Date: Wed, 8 Feb 2017 12:01:37 +0100
-Subject: [PATCH 101/101] selinux: Do not fail if SELinux is not managed
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Previously we failed if semanage_is_managed returned 0 or -1 (not
-managed or error). With this patch we only fail in case of error and
-continue normally if selinux is not managed by libsemanage at all.
-
-Resolves:
-https://fedorahosted.org/sssd/ticket/3297
-
-Reviewed-by: Lukáš Slebodník <lslebodn@redhat.com>
-(cherry picked from commit 78a08d30b5fbf6e1e3b589e0cf67022e0c1faa33)
----
- Makefile.am                       |  1 +
- src/providers/ipa/selinux_child.c |  9 ++++--
- src/util/sss_semanage.c           | 61 +++++++++++++++++++++++++--------------
- src/util/util_errors.c            |  1 +
- src/util/util_errors.h            |  1 +
- 5 files changed, 49 insertions(+), 24 deletions(-)
-
-diff --git a/Makefile.am b/Makefile.am
-index f5ac363a35e4aae51e8b70bad27c7fc824be10f2..370d6442ec58a14946ad288a23c696f25ca98f47 100644
---- a/Makefile.am
-+++ b/Makefile.am
-@@ -4040,6 +4040,7 @@ selinux_child_SOURCES = \
-     src/util/atomic_io.c \
-     src/util/util.c \
-     src/util/util_ext.c \
-+    src/util/util_errors.c
-     $(NULL)
- selinux_child_CFLAGS = \
-     $(AM_CFLAGS) \
-diff --git a/src/providers/ipa/selinux_child.c b/src/providers/ipa/selinux_child.c
-index 380005c7ad3269fc8113c62ceef30b076455b5dd..f8dd3954a7244df2dcbb910aabf8888f41306c09 100644
---- a/src/providers/ipa/selinux_child.c
-+++ b/src/providers/ipa/selinux_child.c
-@@ -174,14 +174,19 @@ static bool seuser_needs_update(struct input_buffer *ibuf)
- 
-     ret = get_seuser(ibuf, ibuf->username, &db_seuser, &db_mls_range);
-     DEBUG(SSSDBG_TRACE_INTERNAL,
--          "get_seuser: ret: %d seuser: %s mls: %s\n",
--          ret, db_seuser ? db_seuser : "unknown",
-+          "get_seuser: ret: %d msg: [%s] seuser: %s mls: %s\n",
-+          ret, sss_strerror(ret),
-+          db_seuser ? db_seuser : "unknown",
-           db_mls_range ? db_mls_range : "unknown");
-     if (ret == EOK && db_seuser && db_mls_range &&
-             strcmp(db_seuser, ibuf->seuser) == 0 &&
-             strcmp(db_mls_range, ibuf->mls_range) == 0) {
-         needs_update = false;
-     }
-+    /* OR */
-+    if (ret == ERR_SELINUX_NOT_MANAGED) {
-+        needs_update = false;
-+    }
- 
-     talloc_free(db_seuser);
-     talloc_free(db_mls_range);
-diff --git a/src/util/sss_semanage.c b/src/util/sss_semanage.c
-index fe06bee1dfec3abca3aa3cd5e85e55386ac11343..0da97aad4d8eba733b131c2749932e03ca4242c4 100644
---- a/src/util/sss_semanage.c
-+++ b/src/util/sss_semanage.c
-@@ -73,7 +73,7 @@ static void sss_semanage_close(semanage_handle_t *handle)
-     semanage_handle_destroy(handle);
- }
- 
--static semanage_handle_t *sss_semanage_init(void)
-+static int sss_semanage_init(semanage_handle_t **_handle)
- {
-     int ret;
-     semanage_handle_t *handle = NULL;
-@@ -81,7 +81,8 @@ static semanage_handle_t *sss_semanage_init(void)
-     handle = semanage_handle_create();
-     if (!handle) {
-         DEBUG(SSSDBG_CRIT_FAILURE, "Cannot create SELinux management handle\n");
--        return NULL;
-+        ret = EIO;
-+        goto done;
-     }
- 
-     semanage_msg_set_callback(handle,
-@@ -89,28 +90,41 @@ static semanage_handle_t *sss_semanage_init(void)
-                               NULL);
- 
-     ret = semanage_is_managed(handle);
--    if (ret != 1) {
--        DEBUG(SSSDBG_CRIT_FAILURE, "SELinux policy not managed\n");
--        goto fail;
-+    if (ret == 0) {
-+        DEBUG(SSSDBG_TRACE_FUNC, "SELinux policy not managed via libsemanage\n");
-+        ret = ERR_SELINUX_NOT_MANAGED;
-+        goto done;
-+    } else if (ret == -1) {
-+        DEBUG(SSSDBG_CRIT_FAILURE, "Call to semanage_is_managed failed\n");
-+        ret = EIO;
-+        goto done;
-     }
- 
-     ret = semanage_access_check(handle);
-     if (ret < SEMANAGE_CAN_READ) {
-         DEBUG(SSSDBG_CRIT_FAILURE, "Cannot read SELinux policy store\n");
--        goto fail;
-+        ret = EACCES;
-+        goto done;
-     }
- 
-     ret = semanage_connect(handle);
-     if (ret != 0) {
-         DEBUG(SSSDBG_CRIT_FAILURE,
-               "Cannot estabilish SELinux management connection\n");
--        goto fail;
-+        ret = EIO;
-+        goto done;
-     }
- 
--    return handle;
--fail:
--    sss_semanage_close(handle);
--    return NULL;
-+    ret = EOK;
-+
-+done:
-+    if (ret != EOK) {
-+        sss_semanage_close(handle);
-+    } else {
-+        *_handle = handle;
-+    }
-+
-+    return ret;
- }
- 
- static int sss_semanage_user_add(semanage_handle_t *handle,
-@@ -228,10 +242,11 @@ int set_seuser(const char *login_name, const char *seuser_name,
-         return EOK;
-     }
- 
--    handle = sss_semanage_init();
--    if (!handle) {
--        DEBUG(SSSDBG_CRIT_FAILURE, "Cannot init SELinux management\n");
--        ret = EIO;
-+    ret = sss_semanage_init(&handle);
-+    if (ret == ERR_SELINUX_NOT_MANAGED) {
-+        goto done;
-+    } else if (ret != EOK) {
-+        DEBUG(SSSDBG_CRIT_FAILURE, "Cannot create SELinux handle\n");
-         goto done;
-     }
- 
-@@ -295,10 +310,11 @@ int del_seuser(const char *login_name)
-     int ret;
-     int exists = 0;
- 
--    handle = sss_semanage_init();
--    if (!handle) {
--        DEBUG(SSSDBG_CRIT_FAILURE, "Cannot init SELinux management\n");
--        ret = EIO;
-+    ret = sss_semanage_init(&handle);
-+    if (ret == ERR_SELINUX_NOT_MANAGED) {
-+        goto done;
-+    } else if (ret != EOK) {
-+        DEBUG(SSSDBG_CRIT_FAILURE, "Cannot create SELinux handle\n");
-         goto done;
-     }
- 
-@@ -377,10 +393,11 @@ int get_seuser(TALLOC_CTX *mem_ctx, const char *login_name,
-     semanage_seuser_t *sm_user = NULL;
-     semanage_seuser_key_t *sm_key = NULL;
- 
--    sm_handle = sss_semanage_init();
--    if (sm_handle == NULL) {
-+    ret = sss_semanage_init(&sm_handle);
-+    if (ret == ERR_SELINUX_NOT_MANAGED) {
-+        goto done;
-+    } else if (ret != EOK) {
-         DEBUG(SSSDBG_CRIT_FAILURE, "Cannot create SELinux handle\n");
--        ret = EIO;
-         goto done;
-     }
- 
-diff --git a/src/util/util_errors.c b/src/util/util_errors.c
-index 466a3b4062f39b29d831a5d8a62dc8d576eb2e97..97eaf160f20bcc8cfe52254070a2d182e19addd4 100644
---- a/src/util/util_errors.c
-+++ b/src/util/util_errors.c
-@@ -75,6 +75,7 @@ struct err_string error_to_str[] = {
-     { "Cannot connect to system bus" }, /* ERR_NO_SYSBUS */
-     { "LDAP search returned a referral" }, /* ERR_REFERRAL */
-     { "Error setting SELinux user context" }, /* ERR_SELINUX_CONTEXT */
-+    { "SELinux is not managed by libsemanage" }, /* ERR_SELINUX_NOT_MANAGED */
-     { "Username format not allowed by re_expression" }, /* ERR_REGEX_NOMATCH */
-     { "Time specification not supported" }, /* ERR_TIMESPEC_NOT_SUPPORTED */
-     { "Invalid SSSD configuration detected" }, /* ERR_INVALID_CONFIG */
-diff --git a/src/util/util_errors.h b/src/util/util_errors.h
-index 2f90c0a5d65325a431a8e4d9a480170808c9198e..4a250bf0339ba689680c155fa8e6d43f42c2467e 100644
---- a/src/util/util_errors.h
-+++ b/src/util/util_errors.h
-@@ -97,6 +97,7 @@ enum sssd_errors {
-     ERR_NO_SYSBUS,
-     ERR_REFERRAL,
-     ERR_SELINUX_CONTEXT,
-+    ERR_SELINUX_NOT_MANAGED,
-     ERR_REGEX_NOMATCH,
-     ERR_TIMESPEC_NOT_SUPPORTED,
-     ERR_INVALID_CONFIG,
--- 
-2.9.3
-
diff --git a/SOURCES/0102-AD-sdap_get_ad_tokengroups_done-allocate-temporary-d.patch b/SOURCES/0102-AD-sdap_get_ad_tokengroups_done-allocate-temporary-d.patch
new file mode 100644
index 0000000..5f9991f
--- /dev/null
+++ b/SOURCES/0102-AD-sdap_get_ad_tokengroups_done-allocate-temporary-d.patch
@@ -0,0 +1,47 @@
+From f6f8fca59937b746b3c47cf0aeb23ea554a43fab Mon Sep 17 00:00:00 2001
+From: Sumit Bose <sbose@redhat.com>
+Date: Fri, 16 Feb 2018 12:07:28 +0100
+Subject: [PATCH] AD: sdap_get_ad_tokengroups_done() allocate temporary data on
+ state
+
+Related to https://pagure.io/SSSD/sssd/issue/3639
+
+Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
+(cherry picked from commit db52090e33b8f1747b7d77bab64ab8c9f9f5ecc2)
+---
+ src/providers/ldap/sdap_async_initgroups_ad.c | 5 +----
+ 1 file changed, 1 insertion(+), 4 deletions(-)
+
+diff --git a/src/providers/ldap/sdap_async_initgroups_ad.c b/src/providers/ldap/sdap_async_initgroups_ad.c
+index 61aa69a2dfbe22cac37a5b7fddc07473527e5de5..ee0cd6707924b02d239ce4c329d9853268d49a80 100644
+--- a/src/providers/ldap/sdap_async_initgroups_ad.c
++++ b/src/providers/ldap/sdap_async_initgroups_ad.c
+@@ -372,7 +372,6 @@ immediately:
+ 
+ static void sdap_get_ad_tokengroups_done(struct tevent_req *subreq)
+ {
+-    TALLOC_CTX *tmp_ctx = NULL;
+     struct sdap_get_ad_tokengroups_state *state = NULL;
+     struct tevent_req *req = NULL;
+     struct sysdb_attrs **users = NULL;
+@@ -386,7 +385,7 @@ static void sdap_get_ad_tokengroups_done(struct tevent_req *subreq)
+     req = tevent_req_callback_data(subreq, struct tevent_req);
+     state = tevent_req_data(req, struct sdap_get_ad_tokengroups_state);
+ 
+-    ret = sdap_get_generic_recv(subreq, tmp_ctx, &num_users, &users);
++    ret = sdap_get_generic_recv(subreq, state, &num_users, &users);
+     talloc_zfree(subreq);
+     if (ret != EOK) {
+         DEBUG(SSSDBG_MINOR_FAILURE,
+@@ -449,8 +448,6 @@ static void sdap_get_ad_tokengroups_done(struct tevent_req *subreq)
+     ret = EOK;
+ 
+ done:
+-    talloc_free(tmp_ctx);
+-
+     if (ret != EOK) {
+         tevent_req_error(req, ret);
+         return;
+-- 
+2.14.3
+
diff --git a/SOURCES/0102-UTIL-Use-max-15-characters-for-AD-host-UPN.patch b/SOURCES/0102-UTIL-Use-max-15-characters-for-AD-host-UPN.patch
deleted file mode 100644
index c91d390..0000000
--- a/SOURCES/0102-UTIL-Use-max-15-characters-for-AD-host-UPN.patch
+++ /dev/null
@@ -1,49 +0,0 @@
-From b2dcfa00dcb7b315a739d35ff6722a25b0ab5556 Mon Sep 17 00:00:00 2001
-From: Lukas Slebodnik <lslebodn@redhat.com>
-Date: Tue, 14 Mar 2017 10:34:00 +0100
-Subject: [PATCH 102/102] UTIL: Use max 15 characters for AD host UPN
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-We do not want to use host principal with AD
-"host/name.domain.tld@DOMAIN.TLD" because it does not work.
-We need to use correct user principal for AD hosts. And we cannot
-rely all fallback "*$" because of other principals in keytab.
-
-The NetBIOS naming convention allows for 16 characters in a NetBIOS
-name. Microsoft, however, limits NetBIOS names to 15 characters and
-uses the 16th character as a NetBIOS suffix.
-https://support.microsoft.com/en-us/help/163409/netbios-suffixes-16th-character-of-the-netbios-name
-
-Resolves:
-https://pagure.io/SSSD/sssd/issue/3329
-
-Reviewed-by: Michal Židek <mzidek@redhat.com>
-(cherry picked from commit c6f1bc32774a7cf2f8678499dfbced420be3a3a1)
----
- src/util/sss_krb5.c | 8 +++++++-
- 1 file changed, 7 insertions(+), 1 deletion(-)
-
-diff --git a/src/util/sss_krb5.c b/src/util/sss_krb5.c
-index d461cf881566af37f31524c16f6a5f1511a5dc89..a3f066e8add5b7d7575c1e0f537c5729e4a0dad0 100644
---- a/src/util/sss_krb5.c
-+++ b/src/util/sss_krb5.c
-@@ -51,7 +51,13 @@ sss_krb5_get_primary(TALLOC_CTX *mem_ctx,
-             *c = toupper(*c);
-         }
- 
--        primary = talloc_asprintf(mem_ctx, "%s$", shortname);
-+        /* The samAccountName is recommended to be less than 20 characters.
-+         * This is only for users and groups. For machine accounts,
-+         * the real limit is caused by NetBIOS protocol.
-+         * NetBIOS names are limited to 16 (15 + $)
-+         * https://support.microsoft.com/en-us/help/163409/netbios-suffixes-16th-character-of-the-netbios-name
-+         */
-+        primary = talloc_asprintf(mem_ctx, "%.15s$", shortname);
-         talloc_free(shortname);
-         return primary;
-     }
--- 
-2.9.3
-
diff --git a/SOURCES/0103-AD-do-not-allocate-temporary-data-on-long-living-con.patch b/SOURCES/0103-AD-do-not-allocate-temporary-data-on-long-living-con.patch
new file mode 100644
index 0000000..7140380
--- /dev/null
+++ b/SOURCES/0103-AD-do-not-allocate-temporary-data-on-long-living-con.patch
@@ -0,0 +1,89 @@
+From 3dae415229a7a2526a886ea55a12377fdc62361e Mon Sep 17 00:00:00 2001
+From: Sumit Bose <sbose@redhat.com>
+Date: Fri, 16 Feb 2018 12:09:01 +0100
+Subject: [PATCH] AD: do not allocate temporary data on long living context
+
+Related to https://pagure.io/SSSD/sssd/issue/3639
+
+Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
+(cherry picked from commit e6ad16e05f42a1678a8c6cd14eb54ca75b8d775e)
+---
+ src/providers/ad/ad_common.c      | 5 +++--
+ src/providers/ad/ad_common.h      | 3 ++-
+ src/providers/ad/ad_id.c          | 2 +-
+ src/tests/cmocka/test_ad_common.c | 4 ++--
+ 4 files changed, 8 insertions(+), 6 deletions(-)
+
+diff --git a/src/providers/ad/ad_common.c b/src/providers/ad/ad_common.c
+index 84845e285bef336b503b9d27bfc6eb99d6ee43ff..2a1647173b76b410371315eb364e9a3785714a93 100644
+--- a/src/providers/ad/ad_common.c
++++ b/src/providers/ad/ad_common.c
+@@ -1402,13 +1402,14 @@ ad_ldap_conn_list(TALLOC_CTX *mem_ctx,
+ }
+ 
+ struct sdap_id_conn_ctx **
+-ad_user_conn_list(struct ad_id_ctx *ad_ctx,
++ad_user_conn_list(TALLOC_CTX *mem_ctx,
++                  struct ad_id_ctx *ad_ctx,
+                   struct sss_domain_info *dom)
+ {
+     struct sdap_id_conn_ctx **clist;
+     int cindex = 0;
+ 
+-    clist = talloc_zero_array(ad_ctx, struct sdap_id_conn_ctx *, 3);
++    clist = talloc_zero_array(mem_ctx, struct sdap_id_conn_ctx *, 3);
+     if (clist == NULL) {
+         return NULL;
+     }
+diff --git a/src/providers/ad/ad_common.h b/src/providers/ad/ad_common.h
+index ce33b37c75f45ae72adb268858cce34759b8b02f..931aafc6c031e0979460925a1402517b054b202c 100644
+--- a/src/providers/ad/ad_common.h
++++ b/src/providers/ad/ad_common.h
+@@ -175,7 +175,8 @@ ad_ldap_conn_list(TALLOC_CTX *mem_ctx,
+                   struct sss_domain_info *dom);
+ 
+ struct sdap_id_conn_ctx **
+-ad_user_conn_list(struct ad_id_ctx *ad_ctx,
++ad_user_conn_list(TALLOC_CTX *mem_ctx,
++                  struct ad_id_ctx *ad_ctx,
+                   struct sss_domain_info *dom);
+ 
+ struct sdap_id_conn_ctx *
+diff --git a/src/providers/ad/ad_id.c b/src/providers/ad/ad_id.c
+index 0b8f49819405c7dbbfa18b5359f7743441dc65e5..782d9bc402e71d6b20976367f6afbae82bd25750 100644
+--- a/src/providers/ad/ad_id.c
++++ b/src/providers/ad/ad_id.c
+@@ -367,7 +367,7 @@ get_conn_list(TALLOC_CTX *mem_ctx, struct ad_id_ctx *ad_ctx,
+ 
+     switch (ar->entry_type & BE_REQ_TYPE_MASK) {
+     case BE_REQ_USER: /* user */
+-        clist = ad_user_conn_list(ad_ctx, dom);
++        clist = ad_user_conn_list(mem_ctx, ad_ctx, dom);
+         break;
+     case BE_REQ_BY_SECID:   /* by SID */
+     case BE_REQ_USER_AND_GROUP: /* get SID */
+diff --git a/src/tests/cmocka/test_ad_common.c b/src/tests/cmocka/test_ad_common.c
+index 80b3bb5599a95578b7734d5dfcd20a2a7428a084..a8a447e91bd5107bbfc9d8445d0508778a5012f8 100644
+--- a/src/tests/cmocka/test_ad_common.c
++++ b/src/tests/cmocka/test_ad_common.c
+@@ -771,7 +771,7 @@ void test_user_conn_list(void **state)
+                                                      struct ad_common_test_ctx);
+     assert_non_null(test_ctx);
+ 
+-    conn_list = ad_user_conn_list(test_ctx->ad_ctx,
++    conn_list = ad_user_conn_list(test_ctx, test_ctx->ad_ctx,
+                                   test_ctx->dom);
+     assert_non_null(conn_list);
+ 
+@@ -780,7 +780,7 @@ void test_user_conn_list(void **state)
+     assert_null(conn_list[1]);
+     talloc_free(conn_list);
+ 
+-    conn_list = ad_user_conn_list(test_ctx->ad_ctx,
++    conn_list = ad_user_conn_list(test_ctx, test_ctx->ad_ctx,
+                                   test_ctx->subdom);
+     assert_non_null(conn_list);
+ 
+-- 
+2.14.3
+
diff --git a/SOURCES/0103-Move-sized_output_name-and-sized_domain_name-into-re.patch b/SOURCES/0103-Move-sized_output_name-and-sized_domain_name-into-re.patch
deleted file mode 100644
index 89b507c..0000000
--- a/SOURCES/0103-Move-sized_output_name-and-sized_domain_name-into-re.patch
+++ /dev/null
@@ -1,300 +0,0 @@
-From 84be2901aeb36ac60760cc11c424b717df360e87 Mon Sep 17 00:00:00 2001
-From: Jakub Hrozek <jhrozek@redhat.com>
-Date: Wed, 19 Apr 2017 17:44:40 +0200
-Subject: [PATCH 103/104] Move sized_output_name() and sized_domain_name() into
- responder common code
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-These functions are used to format a name into a format that the user
-configured for output, including case sensitiveness, replacing
-whitespace and qualified format. They were used only in the NSS
-responder, which typically returns strings to the NSS client library and
-then the user.
-
-But it makes sense to just reuse the same code in the IFP responder as
-well, since it does essentially the same job.
-
-The patch also renames sized_member_name to sized_domain_name.
-Previously, the function was only used to format a group member, the IFP
-responder would use the same function to format a group the user is a
-member of.
-
-Related to:
-    https://pagure.io/SSSD/sssd/issue/3268
-
-Reviewed-by: Pavel Březina <pbrezina@redhat.com>
-(cherry picked from commit 7c074ba2f923985ab0d4f9d6a5e01ff3f2f0a7a8)
----
- src/responder/common/responder.h        | 21 ++++++++
- src/responder/common/responder_common.c | 90 +++++++++++++++++++++++++++++++++
- src/responder/nss/nss_private.h         | 11 ----
- src/responder/nss/nss_protocol_grent.c  |  2 +-
- src/responder/nss/nss_utils.c           | 87 -------------------------------
- 5 files changed, 112 insertions(+), 99 deletions(-)
-
-diff --git a/src/responder/common/responder.h b/src/responder/common/responder.h
-index 4210307489fe25829a1674f254ecc7d185029698..dfe1ec455e355de263c3550306e53fea3ada85df 100644
---- a/src/responder/common/responder.h
-+++ b/src/responder/common/responder.h
-@@ -393,4 +393,25 @@ char *sss_resp_create_fqname(TALLOC_CTX *mem_ctx,
- 
- errno_t sss_resp_populate_cr_domains(struct resp_ctx *rctx);
- 
-+/**
-+ * Helper functions to format output names
-+ */
-+
-+/* Format orig_name into a sized_string in output format as prescribed
-+ * by the name_dom domain
-+ */
-+int sized_output_name(TALLOC_CTX *mem_ctx,
-+                      struct resp_ctx *rctx,
-+                      const char *orig_name,
-+                      struct sss_domain_info *name_dom,
-+                      struct sized_string **_name);
-+
-+/* Format orig_name into a sized_string in output format as prescribed
-+ * by the domain read from the fully qualified name.
-+ */
-+int sized_domain_name(TALLOC_CTX *mem_ctx,
-+                      struct resp_ctx *rctx,
-+                      const char *member_name,
-+                      struct sized_string **_name);
-+
- #endif /* __SSS_RESPONDER_H__ */
-diff --git a/src/responder/common/responder_common.c b/src/responder/common/responder_common.c
-index 67e1deefdfde19c95a68029b11099579d851513f..ac6320b08de09bc6c7e8dd1af72e0a493a449f7a 100644
---- a/src/responder/common/responder_common.c
-+++ b/src/responder/common/responder_common.c
-@@ -1651,3 +1651,93 @@ done:
- 
-     return ret;
- }
-+
-+/**
-+ * Helper functions to format output names
-+ */
-+int sized_output_name(TALLOC_CTX *mem_ctx,
-+                      struct resp_ctx *rctx,
-+                      const char *orig_name,
-+                      struct sss_domain_info *name_dom,
-+                      struct sized_string **_name)
-+{
-+    TALLOC_CTX *tmp_ctx = NULL;
-+    errno_t ret;
-+    char *username;
-+    struct sized_string *name;
-+
-+    tmp_ctx = talloc_new(NULL);
-+    if (tmp_ctx == NULL) {
-+        return ENOMEM;
-+    }
-+
-+    username = sss_output_name(tmp_ctx, orig_name, name_dom->case_preserve,
-+                               rctx->override_space);
-+    if (username == NULL) {
-+        ret = EIO;
-+        goto done;
-+    }
-+
-+    if (name_dom->fqnames) {
-+        username = sss_tc_fqname(tmp_ctx, name_dom->names, name_dom, username);
-+        if (username == NULL) {
-+            DEBUG(SSSDBG_CRIT_FAILURE, "sss_replace_space failed\n");
-+            ret = EIO;
-+            goto done;
-+        }
-+    }
-+
-+    name = talloc_zero(tmp_ctx, struct sized_string);
-+    if (name == NULL) {
-+        ret = ENOMEM;
-+        goto done;
-+    }
-+
-+    to_sized_string(name, username);
-+    name->str = talloc_steal(name, username);
-+    *_name = talloc_steal(mem_ctx, name);
-+    ret = EOK;
-+done:
-+    talloc_zfree(tmp_ctx);
-+    return ret;
-+}
-+
-+int sized_domain_name(TALLOC_CTX *mem_ctx,
-+                      struct resp_ctx *rctx,
-+                      const char *member_name,
-+                      struct sized_string **_name)
-+{
-+    TALLOC_CTX *tmp_ctx = NULL;
-+    errno_t ret;
-+    char *domname;
-+    struct sss_domain_info *member_dom;
-+
-+    tmp_ctx = talloc_new(NULL);
-+    if (tmp_ctx == NULL) {
-+        return ENOMEM;
-+    }
-+
-+    ret = sss_parse_internal_fqname(tmp_ctx, member_name, NULL, &domname);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_CRIT_FAILURE, "sss_parse_internal_fqname failed\n");
-+        goto done;
-+    }
-+
-+    if (domname == NULL) {
-+        ret = ERR_WRONG_NAME_FORMAT;
-+        goto done;
-+    }
-+
-+    member_dom = find_domain_by_name(get_domains_head(rctx->domains),
-+                                     domname, true);
-+    if (member_dom == NULL) {
-+        ret = ERR_DOMAIN_NOT_FOUND;
-+        goto done;
-+    }
-+
-+    ret = sized_output_name(mem_ctx, rctx, member_name,
-+                            member_dom, _name);
-+done:
-+    talloc_free(tmp_ctx);
-+    return ret;
-+}
-diff --git a/src/responder/nss/nss_private.h b/src/responder/nss/nss_private.h
-index acb3c4aa504e538ca56dca8d43ee04b0f60954a9..13de83226177bbaa8b8237e3e27b7e72da369194 100644
---- a/src/responder/nss/nss_private.h
-+++ b/src/responder/nss/nss_private.h
-@@ -140,17 +140,6 @@ const char *
- nss_get_name_from_msg(struct sss_domain_info *domain,
-                       struct ldb_message *msg);
- 
--int sized_output_name(TALLOC_CTX *mem_ctx,
--                      struct resp_ctx *rctx,
--                      const char *orig_name,
--                      struct sss_domain_info *name_dom,
--                      struct sized_string **_name);
--
--int sized_member_name(TALLOC_CTX *mem_ctx,
--                      struct resp_ctx *rctx,
--                      const char *member_name,
--                      struct sized_string **_name);
--
- const char *
- nss_get_pwfield(struct nss_ctx *nctx,
-                 struct sss_domain_info *dom);
-diff --git a/src/responder/nss/nss_protocol_grent.c b/src/responder/nss/nss_protocol_grent.c
-index 283ab9f6731bc4c8261ca79075ab030005bf70db..fae1d47d7b217beafba75740e2e6d9cb8cdbc1d0 100644
---- a/src/responder/nss/nss_protocol_grent.c
-+++ b/src/responder/nss/nss_protocol_grent.c
-@@ -163,7 +163,7 @@ nss_protocol_fill_members(struct sss_packet *packet,
-                 }
-             }
- 
--            ret = sized_member_name(tmp_ctx, rctx, member_name, &name);
-+            ret = sized_domain_name(tmp_ctx, rctx, member_name, &name);
-             if (ret != EOK) {
-                 DEBUG(SSSDBG_OP_FAILURE, "Unable to get sized name [%d]: %s\n",
-                       ret, sss_strerror(ret));
-diff --git a/src/responder/nss/nss_utils.c b/src/responder/nss/nss_utils.c
-index f839930a275db56e8d729888af870562d7b6f260..2cd9c33b42f7e018ea89d2df206637f35646489e 100644
---- a/src/responder/nss/nss_utils.c
-+++ b/src/responder/nss/nss_utils.c
-@@ -53,93 +53,6 @@ nss_get_name_from_msg(struct sss_domain_info *domain,
-     return ldb_msg_find_attr_as_string(msg, SYSDB_NAME, NULL);
- }
- 
--int sized_output_name(TALLOC_CTX *mem_ctx,
--                      struct resp_ctx *rctx,
--                      const char *orig_name,
--                      struct sss_domain_info *name_dom,
--                      struct sized_string **_name)
--{
--    TALLOC_CTX *tmp_ctx = NULL;
--    errno_t ret;
--    char *username;
--    struct sized_string *name;
--
--    tmp_ctx = talloc_new(NULL);
--    if (tmp_ctx == NULL) {
--        return ENOMEM;
--    }
--
--    username = sss_output_name(tmp_ctx, orig_name, name_dom->case_preserve,
--                               rctx->override_space);
--    if (username == NULL) {
--        ret = EIO;
--        goto done;
--    }
--
--    if (name_dom->fqnames) {
--        username = sss_tc_fqname(tmp_ctx, name_dom->names, name_dom, username);
--        if (username == NULL) {
--            DEBUG(SSSDBG_CRIT_FAILURE, "sss_replace_space failed\n");
--            ret = EIO;
--            goto done;
--        }
--    }
--
--    name = talloc_zero(tmp_ctx, struct sized_string);
--    if (name == NULL) {
--        ret = ENOMEM;
--        goto done;
--    }
--
--    to_sized_string(name, username);
--    name->str = talloc_steal(name, username);
--    *_name = talloc_steal(mem_ctx, name);
--    ret = EOK;
--done:
--    talloc_zfree(tmp_ctx);
--    return ret;
--}
--
--int sized_member_name(TALLOC_CTX *mem_ctx,
--                      struct resp_ctx *rctx,
--                      const char *member_name,
--                      struct sized_string **_name)
--{
--    TALLOC_CTX *tmp_ctx = NULL;
--    errno_t ret;
--    char *domname;
--    struct sss_domain_info *member_dom;
--
--    tmp_ctx = talloc_new(NULL);
--    if (tmp_ctx == NULL) {
--        return ENOMEM;
--    }
--
--    ret = sss_parse_internal_fqname(tmp_ctx, member_name, NULL, &domname);
--    if (ret != EOK) {
--        DEBUG(SSSDBG_CRIT_FAILURE, "sss_parse_internal_fqname failed\n");
--        goto done;
--    }
--
--    if (domname == NULL) {
--        ret = ERR_WRONG_NAME_FORMAT;
--        goto done;
--    }
--
--    member_dom = find_domain_by_name(get_domains_head(rctx->domains),
--                                     domname, true);
--    if (member_dom == NULL) {
--        ret = ERR_DOMAIN_NOT_FOUND;
--        goto done;
--    }
--
--    ret = sized_output_name(mem_ctx, rctx, member_name,
--                            member_dom, _name);
--done:
--    talloc_free(tmp_ctx);
--    return ret;
--}
--
- const char *
- nss_get_pwfield(struct nss_ctx *nctx,
-                struct sss_domain_info *dom)
--- 
-2.9.3
-
diff --git a/SOURCES/0104-IFP-Use-sized_domain_name-to-format-the-groups-the-u.patch b/SOURCES/0104-IFP-Use-sized_domain_name-to-format-the-groups-the-u.patch
deleted file mode 100644
index 8d252f9..0000000
--- a/SOURCES/0104-IFP-Use-sized_domain_name-to-format-the-groups-the-u.patch
+++ /dev/null
@@ -1,95 +0,0 @@
-From 956d7e794d6c07eec3c0009253c8a86320c3e741 Mon Sep 17 00:00:00 2001
-From: Jakub Hrozek <jhrozek@redhat.com>
-Date: Wed, 19 Apr 2017 17:46:03 +0200
-Subject: [PATCH 104/104] IFP: Use sized_domain_name to format the groups the
- user is a member of
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Resolves:
-    https://pagure.io/SSSD/sssd/issue/3268
-
-Uses the common function sized_domain_name() to format a group the user
-is a member of to the appropriate format.
-
-To see the code is working correctly, run:
-        dbus-send --system --print-reply --dest=org.freedesktop.sssd.infopipe
-                  /org/freedesktop/sssd/infopipe
-                  org.freedesktop.sssd.infopipe.GetUserGroups
-                  string:trusted_user
-
-Where trusted_user is a user from a trusted domain that is a member of groups
-from the joined domain and a trusted domain as well. The groups from the
-joined domain should not be qualified, the groups from the trusted
-domain should be qualified.
-
-Reviewed-by: Pavel Březina <pbrezina@redhat.com>
-(cherry picked from commit c9a73bb6ffa010ef206896a0d1c2801bc056fa45)
----
- src/responder/ifp/ifpsrv_cmd.c | 29 +++++++++++++++--------------
- 1 file changed, 15 insertions(+), 14 deletions(-)
-
-diff --git a/src/responder/ifp/ifpsrv_cmd.c b/src/responder/ifp/ifpsrv_cmd.c
-index d10f35e41dbb1623a0b9de37a4c43363cbefc1a3..e4d6c42ef35ef372472803d3d26b17d4181021a8 100644
---- a/src/responder/ifp/ifpsrv_cmd.c
-+++ b/src/responder/ifp/ifpsrv_cmd.c
-@@ -369,10 +369,11 @@ ifp_user_get_groups_reply(struct sss_domain_info *domain,
-                           struct ifp_req *ireq,
-                           struct ldb_result *res)
- {
--    int i, num;
-+    int i, gri, num;
-     const char *name;
-     const char **groupnames;
--    char *out_name;
-+    struct sized_string *group_name;
-+    errno_t ret;
- 
-     /* one less, the first one is the user entry */
-     num = res->count - 1;
-@@ -381,6 +382,7 @@ ifp_user_get_groups_reply(struct sss_domain_info *domain,
-         return sbus_request_finish(ireq->dbus_req, NULL);
-     }
- 
-+    gri = 0;
-     for (i = 0; i < num; i++) {
-         name = sss_view_ldb_msg_find_attr_as_string(domain,
-                                                     res->msgs[i + 1],
-@@ -390,22 +392,21 @@ ifp_user_get_groups_reply(struct sss_domain_info *domain,
-             continue;
-         }
- 
--        out_name = sss_output_name(ireq, name, domain->case_preserve,
--                                   ireq->ifp_ctx->rctx->override_space);
--        if (out_name == NULL) {
-+        ret = sized_domain_name(ireq, ireq->ifp_ctx->rctx, name, &group_name);
-+        if (ret != EOK) {
-+            DEBUG(SSSDBG_MINOR_FAILURE,
-+                  "Unable to get sized name for %s [%d]: %s\n",
-+                  name, ret, sss_strerror(ret));
-             continue;
-         }
- 
--        if (domain->fqnames) {
--            groupnames[i] = sss_tc_fqname(groupnames, domain->names,
--                                          domain, out_name);
--            if (out_name == NULL) {
--                DEBUG(SSSDBG_CRIT_FAILURE, "sss_tc_fqname failed\n");
--                continue;
--            }
--        } else {
--            groupnames[i] = talloc_steal(groupnames, out_name);
-+        groupnames[gri] = talloc_strndup(groupnames,
-+                                         group_name->str, group_name->len);
-+        if (groupnames[gri] == NULL) {
-+            DEBUG(SSSDBG_MINOR_FAILURE, "talloc_strndup failed\n");
-+            continue;
-         }
-+        gri++;
- 
-         DEBUG(SSSDBG_TRACE_FUNC, "Adding group %s\n", groupnames[i]);
-     }
--- 
-2.9.3
-
diff --git a/SOURCES/0105-RESPONDER-Fallback-to-global-domain-resolution-order.patch b/SOURCES/0105-RESPONDER-Fallback-to-global-domain-resolution-order.patch
deleted file mode 100644
index e0094c0..0000000
--- a/SOURCES/0105-RESPONDER-Fallback-to-global-domain-resolution-order.patch
+++ /dev/null
@@ -1,313 +0,0 @@
-From b7d2310e9ddd79bfdea2bc334bd11d4df9be37a2 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Fabiano=20Fid=C3=AAncio?= <fidencio@redhat.com>
-Date: Wed, 12 Apr 2017 10:43:25 +0200
-Subject: [PATCH 105/110] RESPONDER: Fallback to global domain resolution order
- in case the view doesn't have this option set
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-The current code has been ignoring the domain resolution order set
-globally on IPA in case there's a view but this doesn't have any domain
-resolution order set.
-
-It happens because we haven't been checking whether the view attribute
-didn't exist and then we ended up populating the list cache_req domains'
-list assuming that no order has been set instead of falling back to the
-next preferred method.
-
-Related:
-https://pagure.io/SSSD/sssd/issue/3001
-
-Signed-off-by: Fabiano Fidêncio <fidencio@redhat.com>
-
-Reviewed-by: Pavel Březina <pbrezina@redhat.com>
-(cherry picked from commit a3faad0e4dc1ca4473746c3822ecfc5aed876e6d)
----
- src/responder/common/cache_req/cache_req_domain.c |  14 ++-
- src/responder/common/cache_req/cache_req_domain.h |   5 +-
- src/responder/common/responder_common.c           | 108 +++++++++++++---------
- 3 files changed, 74 insertions(+), 53 deletions(-)
-
-diff --git a/src/responder/common/cache_req/cache_req_domain.c b/src/responder/common/cache_req/cache_req_domain.c
-index bbabd695f1c6b6c29b7e61f571382ab9adfb0ea2..86a88efd54ca0f4a0748b44ece1b8515438d4628 100644
---- a/src/responder/common/cache_req/cache_req_domain.c
-+++ b/src/responder/common/cache_req/cache_req_domain.c
-@@ -120,20 +120,21 @@ done:
-     return cr_domains;
- }
- 
--struct cache_req_domain *
-+errno_t
- cache_req_domain_new_list_from_domain_resolution_order(
-                                         TALLOC_CTX *mem_ctx,
-                                         struct sss_domain_info *domains,
--                                        const char *domain_resolution_order)
-+                                        const char *domain_resolution_order,
-+                                        struct cache_req_domain **_cr_domains)
- {
-     TALLOC_CTX *tmp_ctx;
--    struct cache_req_domain *cr_domains = NULL;
-+    struct cache_req_domain *cr_domains;
-     char **list = NULL;
-     errno_t ret;
- 
-     tmp_ctx = talloc_new(NULL);
-     if (tmp_ctx == NULL) {
--        return NULL;
-+        return ENOMEM;
-     }
- 
-     if (domain_resolution_order != NULL) {
-@@ -160,7 +161,10 @@ cache_req_domain_new_list_from_domain_resolution_order(
-         goto done;
-     }
- 
-+    *_cr_domains = cr_domains;
-+    ret = EOK;
-+
- done:
-     talloc_free(tmp_ctx);
--    return cr_domains;
-+    return ret;
- }
-diff --git a/src/responder/common/cache_req/cache_req_domain.h b/src/responder/common/cache_req/cache_req_domain.h
-index 41c50e8c293d7b032cb2f05482c40e93e4f723dc..000087e5ca2074f22169a4af627810f4f287e430 100644
---- a/src/responder/common/cache_req/cache_req_domain.h
-+++ b/src/responder/common/cache_req/cache_req_domain.h
-@@ -34,11 +34,12 @@ struct cache_req_domain *
- cache_req_domain_get_domain_by_name(struct cache_req_domain *domains,
-                                     const char *name);
- 
--struct cache_req_domain *
-+errno_t
- cache_req_domain_new_list_from_domain_resolution_order(
-                                         TALLOC_CTX *mem_ctx,
-                                         struct sss_domain_info *domains,
--                                        const char *domain_resolution_order);
-+                                        const char *domain_resolution_order,
-+                                        struct cache_req_domain **_cr_domains);
- 
- void cache_req_domain_list_zfree(struct cache_req_domain **cr_domains);
- 
-diff --git a/src/responder/common/responder_common.c b/src/responder/common/responder_common.c
-index ac6320b08de09bc6c7e8dd1af72e0a493a449f7a..62b71b5104fdbb585d086d44d2ca2ab9717dd788 100644
---- a/src/responder/common/responder_common.c
-+++ b/src/responder/common/responder_common.c
-@@ -1486,10 +1486,11 @@ fail:
- }
- 
- /* ====== Helper functions for the domain resolution order ======= */
--static struct cache_req_domain *
-+static errno_t
- sss_resp_new_cr_domains_from_ipa_id_view(TALLOC_CTX *mem_ctx,
-                                          struct sss_domain_info *domains,
--                                         struct sysdb_ctx *sysdb)
-+                                         struct sysdb_ctx *sysdb,
-+                                         struct cache_req_domain **_cr_domains)
- {
-     TALLOC_CTX *tmp_ctx;
-     struct cache_req_domain *cr_domains = NULL;
-@@ -1498,7 +1499,7 @@ sss_resp_new_cr_domains_from_ipa_id_view(TALLOC_CTX *mem_ctx,
- 
-     tmp_ctx = talloc_new(NULL);
-     if (tmp_ctx == NULL) {
--        return NULL;
-+        return ENOMEM;
-     }
- 
-     ret = sysdb_get_view_domain_resolution_order(tmp_ctx, sysdb,
-@@ -1510,12 +1511,13 @@ sss_resp_new_cr_domains_from_ipa_id_view(TALLOC_CTX *mem_ctx,
-         goto done;
-     }
- 
--    /* Using mem_ctx (which is rctx) directly here to avoid copying
--     * this memory around. */
--    cr_domains = cache_req_domain_new_list_from_domain_resolution_order(
--                                    mem_ctx, domains, domain_resolution_order);
--    if (cr_domains == NULL) {
--        ret = ENOMEM;
-+    if (ret == ENOENT) {
-+        goto done;
-+    }
-+
-+    ret = cache_req_domain_new_list_from_domain_resolution_order(
-+                        mem_ctx, domains, domain_resolution_order, &cr_domains);
-+    if (ret != EOK) {
-         DEBUG(SSSDBG_DEFAULT,
-               "cache_req_domain_new_list_from_domain_resolution_order() "
-               "failed [%d]: [%s].\n",
-@@ -1523,25 +1525,31 @@ sss_resp_new_cr_domains_from_ipa_id_view(TALLOC_CTX *mem_ctx,
-         goto done;
-     }
- 
-+    *_cr_domains = cr_domains;
-+
-+    ret = EOK;
-+
- done:
-     talloc_free(tmp_ctx);
--    return cr_domains;
-+    return ret;
- }
- 
--static struct cache_req_domain *
-+static errno_t
- sss_resp_new_cr_domains_from_ipa_config(TALLOC_CTX *mem_ctx,
-                                         struct sss_domain_info *domains,
-                                         struct sysdb_ctx *sysdb,
--                                        const char *domain)
-+                                        const char *domain,
-+                                        struct cache_req_domain **_cr_domains)
- {
-     TALLOC_CTX *tmp_ctx;
--    struct cache_req_domain *cr_domains = NULL;
-     const char *domain_resolution_order = NULL;
-     errno_t ret;
- 
-+    *_cr_domains = NULL;
-+
-     tmp_ctx = talloc_new(NULL);
-     if (tmp_ctx == NULL) {
--        return NULL;
-+        return ENOMEM;
-     }
- 
-     ret = sysdb_domain_get_domain_resolution_order(tmp_ctx, sysdb, domain,
-@@ -1554,11 +1562,13 @@ sss_resp_new_cr_domains_from_ipa_config(TALLOC_CTX *mem_ctx,
-         goto done;
-     }
- 
--    /* Using mem_ctx (which is rctx) directly here to avoid copying
--     * this memory around. */
--    cr_domains = cache_req_domain_new_list_from_domain_resolution_order(
--                                    mem_ctx, domains, domain_resolution_order);
--    if (cr_domains == NULL) {
-+    if (ret == ENOENT) {
-+        goto done;
-+    }
-+
-+    ret = cache_req_domain_new_list_from_domain_resolution_order(
-+                        mem_ctx, domains, domain_resolution_order, _cr_domains);
-+    if (ret != EOK) {
-         DEBUG(SSSDBG_DEFAULT,
-               "cache_req_domain_new_list_from_domain_resolution_order() "
-               "failed [%d]: [%s].\n",
-@@ -1566,9 +1576,11 @@ sss_resp_new_cr_domains_from_ipa_config(TALLOC_CTX *mem_ctx,
-         goto done;
-     }
- 
-+    ret = EOK;
-+
- done:
-     talloc_free(tmp_ctx);
--    return cr_domains;
-+    return ret;
- }
- 
- errno_t sss_resp_populate_cr_domains(struct resp_ctx *rctx)
-@@ -1578,16 +1590,16 @@ errno_t sss_resp_populate_cr_domains(struct resp_ctx *rctx)
-     errno_t ret;
- 
-     if (rctx->domain_resolution_order != NULL) {
--        cr_domains = cache_req_domain_new_list_from_domain_resolution_order(
--                            rctx, rctx->domains, rctx->domain_resolution_order);
--
--        if (cr_domains == NULL) {
-+        ret = cache_req_domain_new_list_from_domain_resolution_order(
-+                rctx, rctx->domains,
-+                rctx->domain_resolution_order, &cr_domains);
-+        if (ret == EOK) {
-+            goto done;
-+        } else {
-             DEBUG(SSSDBG_MINOR_FAILURE,
-                   "Failed to use domain_resolution_order set in the config file.\n"
-                   "Trying to fallback to use ipaDomainOrderResolution setup by "
-                   "IPA.\n");
--        } else {
--            goto done;
-         }
-     }
- 
-@@ -1598,9 +1610,9 @@ errno_t sss_resp_populate_cr_domains(struct resp_ctx *rctx)
-     }
- 
-     if (dom == NULL) {
--        cr_domains = cache_req_domain_new_list_from_domain_resolution_order(
--                                                    rctx, rctx->domains, NULL);
--        if (cr_domains == NULL) {
-+        ret = cache_req_domain_new_list_from_domain_resolution_order(
-+                                        rctx, rctx->domains, NULL, &cr_domains);
-+        if (ret != EOK) {
-             DEBUG(SSSDBG_CRIT_FAILURE,
-                   "Failed to flatten the list of domains.\n");
-         }
-@@ -1608,44 +1620,48 @@ errno_t sss_resp_populate_cr_domains(struct resp_ctx *rctx)
-     }
- 
-     if (dom->has_views) {
--        cr_domains = sss_resp_new_cr_domains_from_ipa_id_view(rctx,
--                                                              rctx->domains,
--                                                              dom->sysdb);
--        if (cr_domains == NULL) {
-+        ret = sss_resp_new_cr_domains_from_ipa_id_view(rctx, rctx->domains,
-+                                                       dom->sysdb,
-+                                                       &cr_domains);
-+        if (ret == EOK) {
-+            goto done;
-+        }
-+
-+        if (ret != ENOENT) {
-             DEBUG(SSSDBG_MINOR_FAILURE,
-                   "Failed to use ipaDomainResolutionOrder set for the "
-                   "view \"%s\".\n"
-                   "Trying to fallback to use ipaDomainOrderResolution "
-                   "set in ipaConfig for the domain: %s.\n",
-                   dom->view_name, dom->name);
--        } else {
--            goto done;
-         }
-     }
- 
--    cr_domains = sss_resp_new_cr_domains_from_ipa_config(rctx, rctx->domains,
--                                                         dom->sysdb,
--                                                         dom->name);
--    if (cr_domains == NULL) {
-+    ret = sss_resp_new_cr_domains_from_ipa_config(rctx, rctx->domains,
-+                                                  dom->sysdb, dom->name,
-+                                                  &cr_domains);
-+    if (ret == EOK) {
-+        goto done;
-+    }
-+
-+    if (ret != ENOENT) {
-         DEBUG(SSSDBG_MINOR_FAILURE,
-               "Failed to use ipaDomainResolutionOrder set in ipaConfig "
-               "for the domain: \"%s\".\n"
-               "No ipaDomainResolutionOrder will be followed.\n",
-               dom->name);
--    } else {
--        goto done;
-     }
- 
--    cr_domains = cache_req_domain_new_list_from_domain_resolution_order(
--                                                    rctx, rctx->domains, NULL);
--    if (cr_domains == NULL) {
-+    ret = cache_req_domain_new_list_from_domain_resolution_order(
-+                                        rctx, rctx->domains, NULL, &cr_domains);
-+    if (ret != EOK) {
-         DEBUG(SSSDBG_CRIT_FAILURE, "Failed to flatten the list of domains.\n");
-         goto done;
-     }
- 
--done:
--    ret = cr_domains != NULL ? EOK : ENOMEM;
-+    ret = EOK;
- 
-+done:
-     cache_req_domain_list_zfree(&rctx->cr_domains);
-     rctx->cr_domains = cr_domains;
- 
--- 
-2.9.3
-
diff --git a/SOURCES/0106-NSS-TESTS-Improve-non-fqnames-tests.patch b/SOURCES/0106-NSS-TESTS-Improve-non-fqnames-tests.patch
deleted file mode 100644
index fce885a..0000000
--- a/SOURCES/0106-NSS-TESTS-Improve-non-fqnames-tests.patch
+++ /dev/null
@@ -1,164 +0,0 @@
-From b4b409f2c5bd0f0b26015b0562ae0ee0e831da82 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Fabiano=20Fid=C3=AAncio?= <fidencio@redhat.com>
-Date: Mon, 17 Apr 2017 09:32:39 +0200
-Subject: [PATCH 106/110] NSS/TESTS: Improve non-fqnames tests
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-With the changes that are about to happen we have to have the subdomain's
-fqnames flag set by the time we populate the cr_domains list (as it
-actually occurs with the real code), as this list may set its own fqnames
-flag based on the subdomain's fqnames flag.
-
-Currently the flag is set to false only when running the tests itself so
-the cr_domains list doesn't get populate properly (although it still
-works with the current code).
-
-For the changes that are comming, let's introduce a new setup function
-that ensures that the subdomain's fqnames flag is set up in the right
-time.
-
-Related:
-https://pagure.io/SSSD/sssd/issue/3001
-
-Signed-off-by: Fabiano Fidêncio <fidencio@redhat.com>
-
-Reviewed-by: Pavel Březina <pbrezina@redhat.com>
-(cherry picked from commit ed518f61f1a5d4cf5d87eec492c158725a73d6a1)
----
- src/tests/cmocka/test_nss_srv.c | 45 +++++++++++++++++++++++++++--------------
- 1 file changed, 30 insertions(+), 15 deletions(-)
-
-diff --git a/src/tests/cmocka/test_nss_srv.c b/src/tests/cmocka/test_nss_srv.c
-index 2f526660cbbbf2443dbae4e213c1336feb6c661e..8c72f44f1869558893627e1f2f91b5f3b96c6317 100644
---- a/src/tests/cmocka/test_nss_srv.c
-+++ b/src/tests/cmocka/test_nss_srv.c
-@@ -1709,8 +1709,6 @@ void test_nss_getgrnam_members_subdom_nonfqnames(void **state)
- {
-     errno_t ret;
- 
--    nss_test_ctx->subdom->fqnames = false;
--
-     mock_input_user_or_group("testsubdomgroup");
-     mock_account_recv_simple();
-     will_return(__wrap_sss_packet_get_cmd, SSS_NSS_GETGRNAM);
-@@ -1802,8 +1800,6 @@ void test_nss_getgrnam_mix_dom_nonfqnames(void **state)
- {
-     errno_t ret;
- 
--    nss_test_ctx->subdom->fqnames = false;
--
-     ret = store_group_member(nss_test_ctx,
-                              testgroup_members.gr_name,
-                              nss_test_ctx->tctx->dom,
-@@ -1917,6 +1913,7 @@ void test_nss_getgrnam_mix_dom_fqdn(void **state)
-     assert_int_equal(ret, EOK);
- }
- 
-+
- void test_nss_getgrnam_mix_dom_fqdn_nonfqnames(void **state)
- {
-     errno_t ret;
-@@ -1929,10 +1926,6 @@ void test_nss_getgrnam_mix_dom_fqdn_nonfqnames(void **state)
-                              SYSDB_MEMBER_USER);
-     assert_int_equal(ret, EOK);
- 
--    nss_test_ctx->tctx->dom->fqnames = false;
--    nss_test_ctx->subdom->fqnames = false;
--
--
-     mock_input_user_or_group("testgroup_members");
-     will_return(__wrap_sss_packet_get_cmd, SSS_NSS_GETGRNAM);
-     will_return_always(__wrap_sss_packet_get_body, WRAP_CALL_REAL);
-@@ -2044,8 +2037,6 @@ void test_nss_getgrnam_mix_subdom_nonfqnames(void **state)
- {
-     errno_t ret;
- 
--    nss_test_ctx->subdom->fqnames = false;
--
-     ret = store_group_member(nss_test_ctx,
-                              testsubdomgroup.gr_name,
-                              nss_test_ctx->subdom,
-@@ -3417,9 +3408,11 @@ static int nss_test_setup_extra_attr(void **state)
-     return 0;
- }
- 
--static int nss_subdom_test_setup(void **state)
-+static int nss_subdom_test_setup_common(void **state, bool nonfqnames)
- {
-     const char *const testdom[4] = { TEST_SUBDOM_NAME, "TEST.SUB", "test", "S-3" };
-+    struct sss_domain_info *dom;
-+
-     struct sss_domain_info *subdomain;
-     errno_t ret;
- 
-@@ -3440,6 +3433,17 @@ static int nss_subdom_test_setup(void **state)
-                                   nss_test_ctx->tctx->confdb);
-     assert_int_equal(ret, EOK);
- 
-+    if (nonfqnames) {
-+        for (dom = nss_test_ctx->rctx->domains;
-+             dom != NULL;
-+             dom = get_next_domain(dom, SSS_GND_ALL_DOMAINS)) {
-+            if (strcmp(dom->name, subdomain->name) == 0) {
-+                dom->fqnames = false;
-+                break;
-+            }
-+        }
-+    }
-+
-     ret = sss_resp_populate_cr_domains(nss_test_ctx->rctx);
-     assert_int_equal(ret, EOK);
-     assert_non_null(nss_test_ctx->rctx->cr_domains);
-@@ -3475,6 +3479,17 @@ static int nss_subdom_test_setup(void **state)
-     assert_int_equal(ret, EOK);
- 
-     return 0;
-+
-+}
-+
-+static int nss_subdom_test_setup(void **state)
-+{
-+    return nss_subdom_test_setup_common(state, false);
-+}
-+
-+static int nss_subdom_test_setup_nonfqnames(void **state)
-+{
-+    return nss_subdom_test_setup_common(state, true);
- }
- 
- static int nss_fqdn_fancy_test_setup(void **state)
-@@ -4192,25 +4207,25 @@ int main(int argc, const char *argv[])
-                                         nss_subdom_test_setup,
-                                         nss_subdom_test_teardown),
-         cmocka_unit_test_setup_teardown(test_nss_getgrnam_members_subdom_nonfqnames,
--                                        nss_subdom_test_setup,
-+                                        nss_subdom_test_setup_nonfqnames,
-                                         nss_subdom_test_teardown),
-         cmocka_unit_test_setup_teardown(test_nss_getgrnam_mix_dom,
-                                         nss_subdom_test_setup,
-                                         nss_subdom_test_teardown),
-         cmocka_unit_test_setup_teardown(test_nss_getgrnam_mix_dom_nonfqnames,
--                                        nss_subdom_test_setup,
-+                                        nss_subdom_test_setup_nonfqnames,
-                                         nss_subdom_test_teardown),
-         cmocka_unit_test_setup_teardown(test_nss_getgrnam_mix_dom_fqdn,
-                                         nss_subdom_test_setup,
-                                         nss_subdom_test_teardown),
-         cmocka_unit_test_setup_teardown(test_nss_getgrnam_mix_dom_fqdn_nonfqnames,
--                                        nss_subdom_test_setup,
-+                                        nss_subdom_test_setup_nonfqnames,
-                                         nss_subdom_test_teardown),
-         cmocka_unit_test_setup_teardown(test_nss_getgrnam_mix_subdom,
-                                         nss_subdom_test_setup,
-                                         nss_subdom_test_teardown),
-         cmocka_unit_test_setup_teardown(test_nss_getgrnam_mix_subdom_nonfqnames,
--                                        nss_subdom_test_setup,
-+                                        nss_subdom_test_setup_nonfqnames,
-                                         nss_subdom_test_teardown),
-         cmocka_unit_test_setup_teardown(test_nss_getgrnam_space,
-                                         nss_test_setup, nss_test_teardown),
--- 
-2.9.3
-
diff --git a/SOURCES/0107-CACHE_REQ-Allow-configurationless-shortname-lookups.patch b/SOURCES/0107-CACHE_REQ-Allow-configurationless-shortname-lookups.patch
deleted file mode 100644
index 352e52d..0000000
--- a/SOURCES/0107-CACHE_REQ-Allow-configurationless-shortname-lookups.patch
+++ /dev/null
@@ -1,139 +0,0 @@
-From 7c6fd66fa9ca942bc240b49f903d9d3d85340c4c Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Fabiano=20Fid=C3=AAncio?= <fidencio@redhat.com>
-Date: Tue, 11 Apr 2017 17:19:29 +0200
-Subject: [PATCH 107/110] CACHE_REQ: Allow configurationless shortname lookups
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Configurationless shortnames lookups must be allowed when a domains'
-resolution order is present and the (head) domain is not enforcing the
-usage of fully-qualified-names.
-
-With this patch SSSD does not require any kind of changes from client
-side for taking advantage of shortname lookups.
-
-Related:
-https://pagure.io/SSSD/sssd/issue/3001
-
-Signed-off-by: Fabiano Fidêncio <fidencio@redhat.com>
-
-Reviewed-by: Pavel Březina <pbrezina@redhat.com>
-(cherry picked from commit dae798231fc2c575f213785768bc24ed765ba243)
----
- src/responder/common/cache_req/cache_req.c        |  2 +-
- src/responder/common/cache_req/cache_req_domain.c | 48 +++++++++++++++++++++++
- src/responder/common/cache_req/cache_req_domain.h |  1 +
- 3 files changed, 50 insertions(+), 1 deletion(-)
-
-diff --git a/src/responder/common/cache_req/cache_req.c b/src/responder/common/cache_req/cache_req.c
-index 3a5fecf34427437bbf95317e05c5bd8b07b4537d..797325a30e6c1ed5f1d4b4c147c65391d5204b52 100644
---- a/src/responder/common/cache_req/cache_req.c
-+++ b/src/responder/common/cache_req/cache_req.c
-@@ -480,7 +480,7 @@ static errno_t cache_req_search_domains_next(struct tevent_req *req)
-          * qualified names on domain less search. We do not descend into
-          * subdomains here since those are implicitly qualified.
-          */
--        if (state->check_next && !allow_no_fqn && domain->fqnames) {
-+        if (state->check_next && !allow_no_fqn && state->cr_domain->fqnames) {
-             state->cr_domain = state->cr_domain->next;
-             continue;
-         }
-diff --git a/src/responder/common/cache_req/cache_req_domain.c b/src/responder/common/cache_req/cache_req_domain.c
-index 86a88efd54ca0f4a0748b44ece1b8515438d4628..bfdd2b7f640178f6d0a0d92f2fed329c856b478c 100644
---- a/src/responder/common/cache_req/cache_req_domain.c
-+++ b/src/responder/common/cache_req/cache_req_domain.c
-@@ -60,6 +60,48 @@ void cache_req_domain_list_zfree(struct cache_req_domain **cr_domains)
-     *cr_domains = NULL;
- }
- 
-+static bool
-+cache_req_domain_use_fqnames(struct sss_domain_info *domain,
-+                             bool enforce_non_fqnames)
-+{
-+    struct sss_domain_info *head;
-+
-+    head = get_domains_head(domain);
-+
-+    /*
-+     * In order to decide whether fully_qualified_names must be used on the
-+     * lookups we have to take into consideration:
-+     * - use_fully_qualified_name value of the head of the domains;
-+     *   (head->fqnames)
-+     * - the presence of a domains' resolution order list;
-+     *   (non_fqnames_enforced)
-+     *
-+     * The relationship between those two can be described by:
-+     * - head->fqnames:
-+     *   - true: in this case doesn't matter whether it's enforced or not,
-+     *           fully-qualified-names will _always_ be used
-+     *   - false: in this case (which is also the default case), the usage
-+     *            depends on it being enforced;
-+     *
-+     *     - enforce_non_fqnames:
-+     *       - true: in this case, the usage of fully-qualified-names is not
-+     *               needed;
-+     *       - false: in this case, the usage of fully-qualified-names will be
-+     *                done accordingly to what's set for the domain itself.
-+     */
-+    switch (head->fqnames) {
-+    case true:
-+        return true;
-+    case false:
-+        switch (enforce_non_fqnames) {
-+        case true:
-+            return false;
-+        case false:
-+            return domain->fqnames;
-+        }
-+    }
-+}
-+
- static struct cache_req_domain *
- cache_req_domain_new_list_from_string_list(TALLOC_CTX *mem_ctx,
-                                            struct sss_domain_info *domains,
-@@ -71,9 +113,11 @@ cache_req_domain_new_list_from_string_list(TALLOC_CTX *mem_ctx,
-     char *name;
-     int flag = SSS_GND_ALL_DOMAINS;
-     int i;
-+    bool enforce_non_fqnames = false;
-     errno_t ret;
- 
-     if (resolution_order != NULL) {
-+        enforce_non_fqnames = true;
-         for (i = 0; resolution_order[i] != NULL; i++) {
-             name = resolution_order[i];
-             for (dom = domains; dom; dom = get_next_domain(dom, flag)) {
-@@ -87,6 +131,8 @@ cache_req_domain_new_list_from_string_list(TALLOC_CTX *mem_ctx,
-                     goto done;
-                 }
-                 cr_domain->domain = dom;
-+                cr_domain->fqnames =
-+                    cache_req_domain_use_fqnames(dom, enforce_non_fqnames);
- 
-                 DLIST_ADD_END(cr_domains, cr_domain,
-                               struct cache_req_domain *);
-@@ -106,6 +152,8 @@ cache_req_domain_new_list_from_string_list(TALLOC_CTX *mem_ctx,
-             goto done;
-         }
-         cr_domain->domain = dom;
-+        cr_domain->fqnames =
-+            cache_req_domain_use_fqnames(dom, enforce_non_fqnames);
- 
-         DLIST_ADD_END(cr_domains, cr_domain, struct cache_req_domain *);
-     }
-diff --git a/src/responder/common/cache_req/cache_req_domain.h b/src/responder/common/cache_req/cache_req_domain.h
-index 000087e5ca2074f22169a4af627810f4f287e430..5bcbb9b493caf05bf71aac5cf7633ded91f22e73 100644
---- a/src/responder/common/cache_req/cache_req_domain.h
-+++ b/src/responder/common/cache_req/cache_req_domain.h
-@@ -25,6 +25,7 @@
- 
- struct cache_req_domain {
-     struct sss_domain_info *domain;
-+    bool fqnames;
- 
-     struct cache_req_domain *prev;
-     struct cache_req_domain *next;
--- 
-2.9.3
-
diff --git a/SOURCES/0108-CACHE_REQ_DOMAIN-Add-some-comments-to-cache_req_doma.patch b/SOURCES/0108-CACHE_REQ_DOMAIN-Add-some-comments-to-cache_req_doma.patch
deleted file mode 100644
index 86ba24f..0000000
--- a/SOURCES/0108-CACHE_REQ_DOMAIN-Add-some-comments-to-cache_req_doma.patch
+++ /dev/null
@@ -1,42 +0,0 @@
-From 3d55506f2e6584d412ca07f2d0d77375aae48ba9 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Fabiano=20Fid=C3=AAncio?= <fidencio@redhat.com>
-Date: Mon, 24 Apr 2017 21:04:58 +0200
-Subject: [PATCH 108/110] CACHE_REQ_DOMAIN: Add some comments to
- cache_req_domain_new_list_from_string_list()
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Signed-off-by: Fabiano Fidêncio <fidencio@redhat.com>
-
-Reviewed-by: Pavel Březina <pbrezina@redhat.com>
-(cherry picked from commit f9bac02756aa05cc9c6ac07ae581dba67240c1a4)
----
- src/responder/common/cache_req/cache_req_domain.c | 4 ++++
- 1 file changed, 4 insertions(+)
-
-diff --git a/src/responder/common/cache_req/cache_req_domain.c b/src/responder/common/cache_req/cache_req_domain.c
-index bfdd2b7f640178f6d0a0d92f2fed329c856b478c..6d37db0f109d5343eb6d7f4272bea522d4c34cf7 100644
---- a/src/responder/common/cache_req/cache_req_domain.c
-+++ b/src/responder/common/cache_req/cache_req_domain.c
-@@ -116,6 +116,8 @@ cache_req_domain_new_list_from_string_list(TALLOC_CTX *mem_ctx,
-     bool enforce_non_fqnames = false;
-     errno_t ret;
- 
-+    /* Firstly, in case a domains' resolution order is passed ... iterate over
-+     * the list adding its domains to the flatten cache req domains' list */
-     if (resolution_order != NULL) {
-         enforce_non_fqnames = true;
-         for (i = 0; resolution_order[i] != NULL; i++) {
-@@ -141,6 +143,8 @@ cache_req_domain_new_list_from_string_list(TALLOC_CTX *mem_ctx,
-         }
-     }
- 
-+    /* Then iterate through all the other domains (and subdomains) and add them
-+     * to the flatten cache req domains' list */
-     for (dom = domains; dom; dom = get_next_domain(dom, flag)) {
-         if (string_in_list(dom->name, resolution_order, false)) {
-             continue;
--- 
-2.9.3
-
diff --git a/SOURCES/0109-RESPONDER_COMMON-Improve-domaiN_resolution_order-deb.patch b/SOURCES/0109-RESPONDER_COMMON-Improve-domaiN_resolution_order-deb.patch
deleted file mode 100644
index a1abb10..0000000
--- a/SOURCES/0109-RESPONDER_COMMON-Improve-domaiN_resolution_order-deb.patch
+++ /dev/null
@@ -1,54 +0,0 @@
-From 326442dc734de72b950a47c5fe2b3ac6a1dfc35e Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Fabiano=20Fid=C3=AAncio?= <fidencio@redhat.com>
-Date: Mon, 24 Apr 2017 21:09:02 +0200
-Subject: [PATCH 109/110] RESPONDER_COMMON: Improve domaiN_resolution_order
- debug messages
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Indicate whether a domain_resolution_order has been used and where
-it came from.
-
-Signed-off-by: Fabiano Fidêncio <fidencio@redhat.com>
-
-Reviewed-by: Pavel Březina <pbrezina@redhat.com>
-(cherry picked from commit 213048fd9a5e800deb74cb5b7f0eaf465945c640)
----
- src/responder/common/responder_common.c | 6 ++++++
- 1 file changed, 6 insertions(+)
-
-diff --git a/src/responder/common/responder_common.c b/src/responder/common/responder_common.c
-index 62b71b5104fdbb585d086d44d2ca2ab9717dd788..7496d293fddb3e947d59a4f2aaeb2c83234dfcc7 100644
---- a/src/responder/common/responder_common.c
-+++ b/src/responder/common/responder_common.c
-@@ -1594,6 +1594,8 @@ errno_t sss_resp_populate_cr_domains(struct resp_ctx *rctx)
-                 rctx, rctx->domains,
-                 rctx->domain_resolution_order, &cr_domains);
-         if (ret == EOK) {
-+            DEBUG(SSSDBG_TRACE_FUNC,
-+                  "Using domain_resolution_order from sssd.conf\n");
-             goto done;
-         } else {
-             DEBUG(SSSDBG_MINOR_FAILURE,
-@@ -1624,6 +1626,8 @@ errno_t sss_resp_populate_cr_domains(struct resp_ctx *rctx)
-                                                        dom->sysdb,
-                                                        &cr_domains);
-         if (ret == EOK) {
-+            DEBUG(SSSDBG_TRACE_FUNC,
-+                  "Using domain_resolution_order from IPA ID View\n");
-             goto done;
-         }
- 
-@@ -1641,6 +1645,8 @@ errno_t sss_resp_populate_cr_domains(struct resp_ctx *rctx)
-                                                   dom->sysdb, dom->name,
-                                                   &cr_domains);
-     if (ret == EOK) {
-+        DEBUG(SSSDBG_TRACE_FUNC,
-+              "Using domain_resolution_order from IPA Config\n");
-         goto done;
-     }
- 
--- 
-2.9.3
-
diff --git a/SOURCES/0110-CACHE_REQ_DOMAIN-debug-the-set-domain-resolution-ord.patch b/SOURCES/0110-CACHE_REQ_DOMAIN-debug-the-set-domain-resolution-ord.patch
deleted file mode 100644
index 8507ad0..0000000
--- a/SOURCES/0110-CACHE_REQ_DOMAIN-debug-the-set-domain-resolution-ord.patch
+++ /dev/null
@@ -1,50 +0,0 @@
-From 3671f188ff9e379022d62eaf7171f397f04ac153 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Fabiano=20Fid=C3=AAncio?= <fidencio@redhat.com>
-Date: Tue, 25 Apr 2017 14:25:12 +0200
-Subject: [PATCH 110/110] CACHE_REQ_DOMAIN: debug the set domain resolution
- order
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Signed-off-by: Fabiano Fidêncio <fidencio@redhat.com>
-
-Reviewed-by: Pavel Březina <pbrezina@redhat.com>
-(cherry picked from commit b78febe4c579f86f8007a27599605d1eb9f97a62)
----
- src/responder/common/cache_req/cache_req_domain.c | 11 +++++++++++
- 1 file changed, 11 insertions(+)
-
-diff --git a/src/responder/common/cache_req/cache_req_domain.c b/src/responder/common/cache_req/cache_req_domain.c
-index 6d37db0f109d5343eb6d7f4272bea522d4c34cf7..2c238c9966d322bb542fa2047313ee9e5144edee 100644
---- a/src/responder/common/cache_req/cache_req_domain.c
-+++ b/src/responder/common/cache_req/cache_req_domain.c
-@@ -191,6 +191,10 @@ cache_req_domain_new_list_from_domain_resolution_order(
- 
-     if (domain_resolution_order != NULL) {
-         if (strcmp(domain_resolution_order, ":") != 0) {
-+            DEBUG(SSSDBG_TRACE_FUNC,
-+                  "Domain resolution order list (split by ':'): \"%s\"\n",
-+                  domain_resolution_order);
-+
-             ret = split_on_separator(tmp_ctx, domain_resolution_order, ':',
-                                      true, true, &list, NULL);
-             if (ret != EOK) {
-@@ -199,7 +203,14 @@ cache_req_domain_new_list_from_domain_resolution_order(
-                         ret, sss_strerror(ret));
-                 goto done;
-             }
-+        } else {
-+            DEBUG(SSSDBG_TRACE_FUNC,
-+                  "Domain resolution order list: ':' "
-+                  "(do not use any specific order)\n");
-         }
-+    } else {
-+        DEBUG(SSSDBG_TRACE_FUNC,
-+              "Domain resolution order list: not set\n");
-     }
- 
-     cr_domains = cache_req_domain_new_list_from_string_list(mem_ctx, domains,
--- 
-2.9.3
-
diff --git a/SOURCES/0111-SECRETS-remove-unused-variable.patch b/SOURCES/0111-SECRETS-remove-unused-variable.patch
deleted file mode 100644
index 1e1d87c..0000000
--- a/SOURCES/0111-SECRETS-remove-unused-variable.patch
+++ /dev/null
@@ -1,33 +0,0 @@
-From 429c282e54feb0e1c9ac27d23be6a8c1d4119976 Mon Sep 17 00:00:00 2001
-From: Lukas Slebodnik <lslebodn@redhat.com>
-Date: Wed, 19 Apr 2017 17:56:20 +0200
-Subject: [PATCH 111/118] SECRETS: remove unused variable
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Reviewed-by: Pavel Březina <pbrezina@redhat.com>
-(cherry picked from commit 0e8f0c06cad5805b1a1161f60e3f2cdb7a5a2921)
----
- src/responder/secrets/proxy.c | 5 -----
- 1 file changed, 5 deletions(-)
-
-diff --git a/src/responder/secrets/proxy.c b/src/responder/secrets/proxy.c
-index fd96e985c897e2cb470a9b5d6eecbd34350fb7d2..9c2aa425d414728d10aa830f640632e98def3c1c 100644
---- a/src/responder/secrets/proxy.c
-+++ b/src/responder/secrets/proxy.c
-@@ -570,11 +570,6 @@ static void proxy_secret_req_done(struct tevent_req *subreq)
-     }
- }
- 
--struct provider_handle proxy_secrets_handle = {
--    .fn = proxy_secret_req,
--    .context = NULL,
--};
--
- int proxy_secrets_provider_handle(struct sec_ctx *sctx,
-                                   struct provider_handle **out_handle)
- {
--- 
-2.9.3
-
diff --git a/SOURCES/0112-IPA-Improve-DEBUG-message-if-a-group-has-no-ipaNTSec.patch b/SOURCES/0112-IPA-Improve-DEBUG-message-if-a-group-has-no-ipaNTSec.patch
deleted file mode 100644
index 77c2a5b..0000000
--- a/SOURCES/0112-IPA-Improve-DEBUG-message-if-a-group-has-no-ipaNTSec.patch
+++ /dev/null
@@ -1,42 +0,0 @@
-From 396849b6160594dbb6dedec5d1bd7fbc3af12cdd Mon Sep 17 00:00:00 2001
-From: Jakub Hrozek <jhrozek@redhat.com>
-Date: Fri, 21 Apr 2017 12:39:44 +0200
-Subject: [PATCH 112/118] IPA: Improve DEBUG message if a group has no
- ipaNTSecurityIdentifier
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-There was an issue in a production deployment where the admin selected a
-GID outside the IDM range for a group that contained a user from the
-trusted domain. This resulted in not adding a SID for the IPA group,
-which in turn meant the group couldn't be resolved on the client.
-
-This patch just improves the DEBUG message so that it's clearer for the
-admins where the issue is.
-
-Reviewed-by: Lukáš Slebodník <lslebodn@redhat.com>
-(cherry picked from commit ef019268d2d112ebff3577e551cd19478d73d93b)
----
- src/providers/ipa/ipa_s2n_exop.c | 5 ++++-
- 1 file changed, 4 insertions(+), 1 deletion(-)
-
-diff --git a/src/providers/ipa/ipa_s2n_exop.c b/src/providers/ipa/ipa_s2n_exop.c
-index 2173db357700499a6140aa61841e443139981483..55ec904ca3188c7cf10ac41972e9ecf94ebf44bb 100644
---- a/src/providers/ipa/ipa_s2n_exop.c
-+++ b/src/providers/ipa/ipa_s2n_exop.c
-@@ -1308,7 +1308,10 @@ static void ipa_s2n_get_list_next(struct tevent_req *subreq)
-     ret = sysdb_attrs_get_string(state->attrs->sysdb_attrs, SYSDB_SID_STR,
-                                  &sid_str);
-     if (ret != EOK) {
--        DEBUG(SSSDBG_OP_FAILURE, "sysdb_attrs_get_string failed.\n");
-+        DEBUG(SSSDBG_CRIT_FAILURE,
-+              "Object [%s] has no SID, please check the "
-+              "ipaNTSecurityIdentifier attribute on the server-side",
-+              state->attrs->a.name);
-         goto fail;
-     }
- 
--- 
-2.9.3
-
diff --git a/SOURCES/0113-IPA-Improve-s2n-debug-message-for-missing-ipaNTSecur.patch b/SOURCES/0113-IPA-Improve-s2n-debug-message-for-missing-ipaNTSecur.patch
deleted file mode 100644
index a2ccf0b..0000000
--- a/SOURCES/0113-IPA-Improve-s2n-debug-message-for-missing-ipaNTSecur.patch
+++ /dev/null
@@ -1,45 +0,0 @@
-From 522dffca552146c0af74325b6ceab0ca950bbc1a Mon Sep 17 00:00:00 2001
-From: Justin Stephenson <jstephen@redhat.com>
-Date: Tue, 25 Apr 2017 13:02:10 -0400
-Subject: [PATCH 113/118] IPA: Improve s2n debug message for missing
- ipaNTSecurityIdentifier
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-This patch improves the log message to be more information for
-the SSSD user troubleshooting issues.
-
-If the IDM POSIX group used for AD trust HBAC/SUDO operation is missing
-the ipaNTSecurityIdentifier it can cause client s2n operations failures
-resolving the group which resulted in the inability to login for the AD
-user.
-
-Reviewed-by: Pavel Březina <pbrezina@redhat.com>
-(cherry picked from commit 0c5f463e9629ac08d647c70cffb30bccdd57ae96)
----
- src/providers/ipa/ipa_s2n_exop.c | 8 +++++++-
- 1 file changed, 7 insertions(+), 1 deletion(-)
-
-diff --git a/src/providers/ipa/ipa_s2n_exop.c b/src/providers/ipa/ipa_s2n_exop.c
-index 55ec904ca3188c7cf10ac41972e9ecf94ebf44bb..f5f4401f86615dc7f81f844e1096ad43e965c384 100644
---- a/src/providers/ipa/ipa_s2n_exop.c
-+++ b/src/providers/ipa/ipa_s2n_exop.c
-@@ -2580,7 +2580,13 @@ static errno_t ipa_s2n_save_objects(struct sss_domain_info *dom,
-     ret = sysdb_attrs_get_string(attrs->sysdb_attrs, SYSDB_SID_STR, &sid_str);
-     if (ret != EOK) {
-         DEBUG(SSSDBG_CRIT_FAILURE,
--              "Cannot find SID of object with override.\n");
-+              "Cannot find SID of object.\n");
-+        if (name != NULL) {
-+            DEBUG(SSSDBG_CRIT_FAILURE,
-+                  "Object [%s] has no SID, please check the "
-+                  "ipaNTSecurityIdentifier attribute on the server-side.\n",
-+                  name);
-+        }
-         goto done;
-     }
- 
--- 
-2.9.3
-
diff --git a/SOURCES/0114-CONFDB-Fix-standalone-application-domains.patch b/SOURCES/0114-CONFDB-Fix-standalone-application-domains.patch
deleted file mode 100644
index 9d2968e..0000000
--- a/SOURCES/0114-CONFDB-Fix-standalone-application-domains.patch
+++ /dev/null
@@ -1,128 +0,0 @@
-From 8441030009c22daa835f89dbc36365415524b320 Mon Sep 17 00:00:00 2001
-From: Jakub Hrozek <jhrozek@redhat.com>
-Date: Fri, 31 Mar 2017 17:12:56 +0200
-Subject: [PATCH 114/118] CONFDB: Fix standalone application domains
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-When a standalone application domain was configured, for example:
-
--------------------------------------------------
-[sssd]
-domains = appdomain
-
-[application/appdomain]
-id_provider=ldap
-ldap_uri = ldap://dc.ipa.test
-ldap_search_base = cn=accounts,dc=ipa,dc=test
-ldap_schema = rfc2307bis
-sudo_provider = none
-
-ldap_sasl_mech = gssapi
-krb5_realm = IPA.TEST
-krb5_server = dc.ipa.test
-
-ldap_user_uid_number = telephonenumber
-ldap_user_gid_number = mobile
-ldap_user_extra_attrs = location:l
--------------------------------------------------
-
-We would, when unrolling the application section into a domain section,
-first add a domain stub, equivalent to:
------------------------------
-[domain/appdomain]
-domain_type = application
------------------------------
-
-Which in config.ldb also contains cn. Then, whem we would add the parameters
-from the [application] section, but try to add the cn again.
-
-This didn't happen when inheriting from a POSIX domain, because there we
-would set LDB_FLAG_REPLACE for any attributes that exist in the inherited
-domain.
-
-This patch skips the cn attribute both when replacing an inherited
-domain's attributes and when writing a standalone application domain.
-
-Resolves:
-https://pagure.io/SSSD/sssd/issue/3355
-
-Reviewed-by: Pavel Březina <pbrezina@redhat.com>
-(cherry picked from commit 734e73257fff1c1884b72b8cf988f6d75c3a7567)
----
- src/confdb/confdb.c | 26 ++++++++++++++++++++++----
- 1 file changed, 22 insertions(+), 4 deletions(-)
-
-diff --git a/src/confdb/confdb.c b/src/confdb/confdb.c
-index 88e114457deac3ca50c291a131122624fb6f6fe4..5bb593de03cc2fb26218b883fd1d753e31bedc2d 100644
---- a/src/confdb/confdb.c
-+++ b/src/confdb/confdb.c
-@@ -1909,7 +1909,7 @@ static int confdb_add_app_domain(TALLOC_CTX *mem_ctx,
- 
-     cdb_path = talloc_asprintf(mem_ctx, CONFDB_DOMAIN_PATH_TMPL, name);
-     if (cdb_path == NULL) {
--    return ENOMEM;
-+        return ENOMEM;
-     }
- 
-     val[0] = CONFDB_DOMAIN_TYPE_APP;
-@@ -1933,6 +1933,7 @@ static int confdb_merge_parent_domain(const char *name,
-     struct ldb_message *replace_msg = NULL;
-     struct ldb_message *app_msg = NULL;
-     struct ldb_dn *domain_dn;
-+    struct ldb_message_element *el = NULL;
-     TALLOC_CTX *tmp_ctx = NULL;
- 
-     tmp_ctx = talloc_new(NULL);
-@@ -1974,6 +1975,12 @@ static int confdb_merge_parent_domain(const char *name,
-             replace_msg->elements[i].flags = LDB_FLAG_MOD_ADD;
-         }
- 
-+        el = ldb_msg_find_element(replace_msg, "cn");
-+        if (el != NULL) {
-+            /* Don't add second cn */
-+            ldb_msg_remove_element(replace_msg, el);
-+        }
-+
-         ret = ldb_modify(cdb->ldb, replace_msg);
-         if (ret != LDB_SUCCESS) {
-             ret = sysdb_error_to_errno(ret);
-@@ -1993,7 +2000,14 @@ static int confdb_merge_parent_domain(const char *name,
-     app_msg->dn = domain_dn;
- 
-     for (unsigned i = 0; i < app_section->msgs[0]->num_elements; i++) {
--        struct ldb_message_element *el = NULL;
-+        struct ldb_message_element *app_el = &app_section->msgs[0]->elements[i];
-+
-+        /* These elements will be skipped when replacing attributes in
-+         * a domain to avoid EEXIST errors
-+         */
-+        if (strcasecmp(app_el->name, "cn") == 0) {
-+            continue;
-+        }
- 
-         if (replace_msg != NULL) {
-             el = ldb_msg_find_element(replace_msg,
-@@ -2013,12 +2027,16 @@ static int confdb_merge_parent_domain(const char *name,
-         ret = ldb_msg_add(app_msg,
-                           &app_section->msgs[0]->elements[i],
-                           ldb_flag);
--        if (ret != EOK) {
-+        if (ret != LDB_SUCCESS) {
-             continue;
-         }
-     }
- 
--    ret = ldb_modify(cdb->ldb, app_msg);
-+    /* We use permissive modification here because adding cn or
-+     * distinguishedName from the app_section to the application
-+     * message would throw EEXIST
-+     */
-+    ret = sss_ldb_modify_permissive(cdb->ldb, app_msg);
-     if (ret != LDB_SUCCESS) {
-         ret = sysdb_error_to_errno(ret);
-         DEBUG(SSSDBG_OP_FAILURE,
--- 
-2.9.3
-
diff --git a/SOURCES/0115-utils-add-sss_domain_is_forest_root.patch b/SOURCES/0115-utils-add-sss_domain_is_forest_root.patch
deleted file mode 100644
index f46b9e2..0000000
--- a/SOURCES/0115-utils-add-sss_domain_is_forest_root.patch
+++ /dev/null
@@ -1,48 +0,0 @@
-From dc8a5bc411403b3d216947e317dfce9dbc5f79d3 Mon Sep 17 00:00:00 2001
-From: Sumit Bose <sbose@redhat.com>
-Date: Tue, 4 Apr 2017 14:35:47 +0200
-Subject: [PATCH 115/118] utils: add sss_domain_is_forest_root()
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Related to https://pagure.io/SSSD/sssd/issue/3361
-
-Reviewed-by: Pavel Březina <pbrezina@redhat.com>
-(cherry picked from commit 712e5b2e4465812c00a8667c75813322373bc657)
----
- src/util/domain_info_utils.c | 5 +++++
- src/util/util.h              | 1 +
- 2 files changed, 6 insertions(+)
-
-diff --git a/src/util/domain_info_utils.c b/src/util/domain_info_utils.c
-index 2af7852f03f89b61f5b9fd8a244e98fb27b7e6a2..541058a16d585155b3b51511740f7db45281e2fd 100644
---- a/src/util/domain_info_utils.c
-+++ b/src/util/domain_info_utils.c
-@@ -844,6 +844,11 @@ void sss_domain_set_state(struct sss_domain_info *dom,
-           "Domain %s is %s\n", dom->name, domain_state_str(dom));
- }
- 
-+bool sss_domain_is_forest_root(struct sss_domain_info *dom)
-+{
-+    return (dom->forest_root == dom);
-+}
-+
- bool is_email_from_domain(const char *email, struct sss_domain_info *dom)
- {
-     const char *p;
-diff --git a/src/util/util.h b/src/util/util.h
-index 436550f5078cc173b8ed8cb58836d366f813146b..4ef13ced48addc19403402d7d880176da24ceec6 100644
---- a/src/util/util.h
-+++ b/src/util/util.h
-@@ -539,6 +539,7 @@ enum sss_domain_state sss_domain_get_state(struct sss_domain_info *dom);
- void sss_domain_set_state(struct sss_domain_info *dom,
-                           enum sss_domain_state state);
- bool is_email_from_domain(const char *email, struct sss_domain_info *dom);
-+bool sss_domain_is_forest_root(struct sss_domain_info *dom);
- const char *sss_domain_type_str(struct sss_domain_info *dom);
- 
- struct sss_domain_info*
--- 
-2.9.3
-
diff --git a/SOURCES/0116-ad-handle-forest-root-not-listed-in-ad_enabled_domai.patch b/SOURCES/0116-ad-handle-forest-root-not-listed-in-ad_enabled_domai.patch
deleted file mode 100644
index 28f65d5..0000000
--- a/SOURCES/0116-ad-handle-forest-root-not-listed-in-ad_enabled_domai.patch
+++ /dev/null
@@ -1,104 +0,0 @@
-From 5ca331e80520035d7de2680cd2803fa508d15287 Mon Sep 17 00:00:00 2001
-From: Sumit Bose <sbose@redhat.com>
-Date: Mon, 3 Apr 2017 21:27:32 +0200
-Subject: [PATCH 116/118] ad: handle forest root not listed in
- ad_enabled_domains
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Although users and groups from the forest root should be ignored SSSD
-will still try to get information about the forest topology from a DC
-from the forest root. So even if the forest root domain is disabled we
-should makes sure it is usable for those searches.
-
-Resolves https://pagure.io/SSSD/sssd/issue/3361
-
-Reviewed-by: Pavel Březina <pbrezina@redhat.com>
-(cherry picked from commit feeabf273aa7af580552366ce58655e6a482a0cd)
----
- src/providers/ad/ad_subdomains.c | 39 ++++++++++++++++++++++++++++++++++++---
- 1 file changed, 36 insertions(+), 3 deletions(-)
-
-diff --git a/src/providers/ad/ad_subdomains.c b/src/providers/ad/ad_subdomains.c
-index bc659b2cb0a02723437d24d0021ec3592381e84c..ef166446e837c3f7cd824c1abf4b5cc587aec9da 100644
---- a/src/providers/ad/ad_subdomains.c
-+++ b/src/providers/ad/ad_subdomains.c
-@@ -433,6 +433,14 @@ static errno_t ad_subdomains_refresh(struct be_ctx *be_ctx,
-         if (c >= num_subdomains) {
-             /* ok this subdomain does not exist anymore, let's clean up */
-             sss_domain_set_state(dom, DOM_DISABLED);
-+
-+            /* Just disable the forest root but do not remove sdap data */
-+            if (sss_domain_is_forest_root(dom)) {
-+                DEBUG(SSSDBG_TRACE_ALL,
-+                      "Skipping removal of forest root sdap data.\n");
-+                continue;
-+            }
-+
-             ret = sysdb_subdomain_delete(dom->sysdb, dom->name);
-             if (ret != EOK) {
-                 goto done;
-@@ -633,6 +641,7 @@ static errno_t ad_subdom_reinit(struct ad_subdomains_ctx *subdoms_ctx)
-     const char *path;
-     errno_t ret;
-     bool canonicalize = false;
-+    struct sss_domain_info *dom;
- 
-     path = dp_opt_get_string(subdoms_ctx->ad_id_ctx->ad_options->basic,
-                              AD_KRB5_CONFD_PATH);
-@@ -675,6 +684,17 @@ static errno_t ad_subdom_reinit(struct ad_subdomains_ctx *subdoms_ctx)
-         return ret;
-     }
- 
-+    /* Make sure disabled domains are not re-enabled accidentially */
-+    if (subdoms_ctx->ad_enabled_domains != NULL) {
-+        for (dom = subdoms_ctx->be_ctx->domain->subdomains; dom;
-+                                            dom = get_next_domain(dom, false)) {
-+            if (!is_domain_enabled(dom->name,
-+                                   subdoms_ctx->ad_enabled_domains)) {
-+                sss_domain_set_state(dom, DOM_DISABLED);
-+            }
-+        }
-+    }
-+
-     return EOK;
- }
- 
-@@ -898,7 +918,7 @@ static errno_t ad_get_slave_domain_recv(struct tevent_req *req)
- static struct sss_domain_info *
- ads_get_root_domain(struct be_ctx *be_ctx, struct sysdb_attrs *attrs)
- {
--    struct sss_domain_info *root;
-+    struct sss_domain_info *dom;
-     const char *name;
-     errno_t ret;
- 
-@@ -909,9 +929,22 @@ ads_get_root_domain(struct be_ctx *be_ctx, struct sysdb_attrs *attrs)
-     }
- 
-     /* With a subsequent run, the root should already be known */
--    root = find_domain_by_name(be_ctx->domain, name, false);
-+    for (dom = be_ctx->domain; dom != NULL;
-+         dom = get_next_domain(dom, SSS_GND_ALL_DOMAINS)) {
- 
--    return root;
-+        if (strcasecmp(dom->name, name) == 0) {
-+            /* The forest root is special, although it might be disabled for
-+             * general lookups we still want to try to get the domains in the
-+             * forest from a DC of the forest root */
-+            if (sss_domain_get_state(dom) == DOM_DISABLED
-+                    && !sss_domain_is_forest_root(dom)) {
-+                return NULL;
-+            }
-+            return dom;
-+        }
-+    }
-+
-+    return NULL;
- }
- 
- static struct ad_id_ctx *
--- 
-2.9.3
-
diff --git a/SOURCES/0117-SDAP-Fix-handling-of-search-bases.patch b/SOURCES/0117-SDAP-Fix-handling-of-search-bases.patch
deleted file mode 100644
index 52c114d..0000000
--- a/SOURCES/0117-SDAP-Fix-handling-of-search-bases.patch
+++ /dev/null
@@ -1,168 +0,0 @@
-From ef6d1aaaa416bca3318e2961269620db7720a55b Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Michal=20=C5=BDidek?= <mzidek@redhat.com>
-Date: Tue, 11 Apr 2017 19:56:37 +0200
-Subject: [PATCH 117/118] SDAP: Fix handling of search bases
-
-We were rewriting the sdap_domain's search bases for only the first
-sdap_domain in the list, which does not work for subdomains.
-
-Also when search bases were already initialized in sdap_domain_subdom_add,
-we should only rewrite them when they were explicitly set in sssd.conf.
-
-Resolves:
-https://pagure.io/SSSD/sssd/issue/3351
-
-Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
-(cherry picked from commit 4c49edbd8df651b1737c59459637962c117212c6)
----
- src/providers/ad/ad_common.c              | 39 +++++++++++++++++++++----------
- src/providers/ad/ad_common.h              |  3 ++-
- src/providers/ipa/ipa_subdomains_server.c |  2 +-
- src/providers/ldap/ldap_options.c         |  2 --
- 4 files changed, 30 insertions(+), 16 deletions(-)
-
-diff --git a/src/providers/ad/ad_common.c b/src/providers/ad/ad_common.c
-index f893b748a2ddcff1eab6e8d919d2aa950b825446..1a9d8dc0bfdf18e76e3c97a7ac7e297c4d24fd44 100644
---- a/src/providers/ad/ad_common.c
-+++ b/src/providers/ad/ad_common.c
-@@ -29,7 +29,8 @@ struct ad_server_data {
-     bool gc;
- };
- 
--errno_t ad_set_search_bases(struct sdap_options *id_opts);
-+errno_t ad_set_search_bases(struct sdap_options *id_opts,
-+                            struct sdap_domain *sdap);
- static errno_t ad_set_sdap_options(struct ad_options *ad_opts,
-                                    struct sdap_options *id_opts);
- 
-@@ -1074,7 +1075,7 @@ ad_get_id_options(struct ad_options *ad_opts,
-     }
- 
-     /* Set up search bases if they were assigned explicitly */
--    ret = ad_set_search_bases(id_opts);
-+    ret = ad_set_search_bases(id_opts, NULL);
-     if (ret != EOK) {
-         talloc_free(id_opts);
-         return ret;
-@@ -1116,11 +1117,14 @@ ad_get_autofs_options(struct ad_options *ad_opts,
- }
- 
- errno_t
--ad_set_search_bases(struct sdap_options *id_opts)
-+ad_set_search_bases(struct sdap_options *id_opts,
-+                    struct sdap_domain *sdom)
- {
-     errno_t ret;
--    char *default_search_base;
-+    char *default_search_base = NULL;
-     size_t o;
-+    struct sdap_domain *sdap_dom;
-+    bool has_default;
-     const int search_base_options[] = { SDAP_USER_SEARCH_BASE,
-                                         SDAP_GROUP_SEARCH_BASE,
-                                         SDAP_NETGROUP_SEARCH_BASE,
-@@ -1132,10 +1136,21 @@ ad_set_search_bases(struct sdap_options *id_opts)
-      * been specifically overridden.
-      */
- 
--    default_search_base =
--            dp_opt_get_string(id_opts->basic, SDAP_SEARCH_BASE);
-+    if (sdom != NULL) {
-+        sdap_dom = sdom;
-+    } else {
-+        /* If no specific sdom was given, use the first in the list. */
-+        sdap_dom = id_opts->sdom;
-+    }
- 
--    if (default_search_base) {
-+    has_default = sdap_dom->search_bases != NULL;
-+
-+    if (has_default == false) {
-+        default_search_base =
-+                dp_opt_get_string(id_opts->basic, SDAP_SEARCH_BASE);
-+    }
-+
-+    if (default_search_base && has_default == false) {
-         /* set search bases if they are not */
-         for (o = 0; search_base_options[o] != -1; o++) {
-             if (NULL == dp_opt_get_string(id_opts->basic,
-@@ -1162,31 +1177,31 @@ ad_set_search_bases(struct sdap_options *id_opts)
-     /* Default search */
-     ret = sdap_parse_search_base(id_opts, id_opts->basic,
-                                  SDAP_SEARCH_BASE,
--                                 &id_opts->sdom->search_bases);
-+                                 &sdap_dom->search_bases);
-     if (ret != EOK && ret != ENOENT) goto done;
- 
-     /* User search */
-     ret = sdap_parse_search_base(id_opts, id_opts->basic,
-                                  SDAP_USER_SEARCH_BASE,
--                                 &id_opts->sdom->user_search_bases);
-+                                 &sdap_dom->user_search_bases);
-     if (ret != EOK && ret != ENOENT) goto done;
- 
-     /* Group search base */
-     ret = sdap_parse_search_base(id_opts, id_opts->basic,
-                                  SDAP_GROUP_SEARCH_BASE,
--                                 &id_opts->sdom->group_search_bases);
-+                                 &sdap_dom->group_search_bases);
-     if (ret != EOK && ret != ENOENT) goto done;
- 
-     /* Netgroup search */
-     ret = sdap_parse_search_base(id_opts, id_opts->basic,
-                                  SDAP_NETGROUP_SEARCH_BASE,
--                                 &id_opts->sdom->netgroup_search_bases);
-+                                 &sdap_dom->netgroup_search_bases);
-     if (ret != EOK && ret != ENOENT) goto done;
- 
-     /* Service search */
-     ret = sdap_parse_search_base(id_opts, id_opts->basic,
-                                  SDAP_SERVICE_SEARCH_BASE,
--                                 &id_opts->sdom->service_search_bases);
-+                                 &sdap_dom->service_search_bases);
-     if (ret != EOK && ret != ENOENT) goto done;
- 
-     ret = EOK;
-diff --git a/src/providers/ad/ad_common.h b/src/providers/ad/ad_common.h
-index 2981550f6c390929501ec8942e861b16ea0a5cb0..ce33b37c75f45ae72adb268858cce34759b8b02f 100644
---- a/src/providers/ad/ad_common.h
-+++ b/src/providers/ad/ad_common.h
-@@ -130,7 +130,8 @@ struct ad_options *ad_create_1way_trust_options(TALLOC_CTX *mem_ctx,
-                                                 const char *keytab,
-                                                 const char *sasl_authid);
- 
--errno_t ad_set_search_bases(struct sdap_options *id_opts);
-+errno_t ad_set_search_bases(struct sdap_options *id_opts,
-+                            struct sdap_domain *sdap);
- 
- errno_t
- ad_failover_init(TALLOC_CTX *mem_ctx, struct be_ctx *ctx,
-diff --git a/src/providers/ipa/ipa_subdomains_server.c b/src/providers/ipa/ipa_subdomains_server.c
-index e8ee30392d84f84e30bcdaa3d2110ba130b1ad73..b02ea67af964a03e5466067cdb2b3ba4498120eb 100644
---- a/src/providers/ipa/ipa_subdomains_server.c
-+++ b/src/providers/ipa/ipa_subdomains_server.c
-@@ -332,7 +332,7 @@ ipa_ad_ctx_new(struct be_ctx *be_ctx,
-         return EFAULT;
-     }
- 
--    ret = ad_set_search_bases(ad_options->id);
-+    ret = ad_set_search_bases(ad_options->id, sdom);
-     if (ret != EOK) {
-         DEBUG(SSSDBG_OP_FAILURE, "Cannot initialize AD search bases\n");
-         talloc_free(ad_options);
-diff --git a/src/providers/ldap/ldap_options.c b/src/providers/ldap/ldap_options.c
-index 15a2609f07506b6dd442b180651a7e25461976c0..eb4e177b456253ebdfa06ee52886a5dffe0d3351 100644
---- a/src/providers/ldap/ldap_options.c
-+++ b/src/providers/ldap/ldap_options.c
-@@ -581,8 +581,6 @@ errno_t sdap_parse_search_base(TALLOC_CTX *mem_ctx,
-     char *unparsed_base;
-     const char *old_filter = NULL;
- 
--    *_search_bases = NULL;
--
-     switch (class) {
-     case SDAP_SEARCH_BASE:
-         class_name = "DEFAULT";
--- 
-2.9.3
-
diff --git a/SOURCES/0118-overrides-add-certificates-to-mapped-attribute.patch b/SOURCES/0118-overrides-add-certificates-to-mapped-attribute.patch
deleted file mode 100644
index 2bfa2c1..0000000
--- a/SOURCES/0118-overrides-add-certificates-to-mapped-attribute.patch
+++ /dev/null
@@ -1,102 +0,0 @@
-From 4a3f3c675e360c888da7d23ab6ec4cca10876b08 Mon Sep 17 00:00:00 2001
-From: Sumit Bose <sbose@redhat.com>
-Date: Thu, 27 Apr 2017 09:28:55 +0200
-Subject: [PATCH 118/118] overrides: add certificates to mapped attribute
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Certificates in overrides are explicitly used to map users to
-certificates, so we add them to SYSDB_USER_MAPPED_CERT as well.
-
-Resolves https://pagure.io/SSSD/sssd/issue/3373
-
-Reviewed-by: Pavel Březina <pbrezina@redhat.com>
-(cherry picked from commit 2e5fc89ef25434fab7febe2c52e97ef989b50d5b)
----
- src/db/sysdb_views.c | 41 +++++++++++++++++++++++++++++++++++++++++
- 1 file changed, 41 insertions(+)
-
-diff --git a/src/db/sysdb_views.c b/src/db/sysdb_views.c
-index 20db9b06183d68b33bb19f498513d7f5cf84b1cf..3773dda77e16b35fa217be0aa7974da7e34c09f4 100644
---- a/src/db/sysdb_views.c
-+++ b/src/db/sysdb_views.c
-@@ -777,6 +777,7 @@ errno_t sysdb_apply_default_override(struct sss_domain_info *domain,
-     int ret;
-     TALLOC_CTX *tmp_ctx;
-     struct sysdb_attrs *attrs;
-+    struct sysdb_attrs *mapped_attrs = NULL;
-     size_t c;
-     size_t d;
-     size_t num_values;
-@@ -791,6 +792,7 @@ errno_t sysdb_apply_default_override(struct sss_domain_info *domain,
-                                     SYSDB_USER_CERT,
-                                     NULL };
-     bool override_attrs_found = false;
-+    bool is_cert = false;
- 
-     if (override_attrs == NULL) {
-         /* nothing to do */
-@@ -846,6 +848,24 @@ errno_t sysdb_apply_default_override(struct sss_domain_info *domain,
-                     num_values = 1;
-                 }
- 
-+                is_cert = false;
-+                if (strcmp(allowed_attrs[c], SYSDB_USER_CERT) == 0) {
-+                    /* Certificates in overrides are explicitly used to map
-+                     * users to certificates, so we add them to
-+                     * SYSDB_USER_MAPPED_CERT as well. */
-+                    is_cert = true;
-+
-+                    if (mapped_attrs == NULL) {
-+                        mapped_attrs = sysdb_new_attrs(tmp_ctx);
-+                        if (mapped_attrs == NULL) {
-+                            DEBUG(SSSDBG_OP_FAILURE,
-+                                  "sysdb_new_attrs failed.\n");
-+                            ret = ENOMEM;
-+                            goto done;
-+                        }
-+                    }
-+                }
-+
-                 for (d = 0; d < num_values; d++) {
-                     ret = sysdb_attrs_add_val(attrs,  allowed_attrs[c],
-                                               &el->values[d]);
-@@ -854,6 +874,18 @@ errno_t sysdb_apply_default_override(struct sss_domain_info *domain,
-                               "sysdb_attrs_add_val failed.\n");
-                         goto done;
-                     }
-+
-+                    if (is_cert) {
-+                        ret = sysdb_attrs_add_val(mapped_attrs,
-+                                                  SYSDB_USER_MAPPED_CERT,
-+                                                  &el->values[d]);
-+                        if (ret != EOK) {
-+                            DEBUG(SSSDBG_OP_FAILURE,
-+                                  "sysdb_attrs_add_val failed.\n");
-+                            goto done;
-+                        }
-+                    }
-+
-                     DEBUG(SSSDBG_TRACE_ALL,
-                           "Override [%s] with [%.*s] for [%s].\n",
-                           allowed_attrs[c], (int) el->values[d].length,
-@@ -878,6 +910,15 @@ errno_t sysdb_apply_default_override(struct sss_domain_info *domain,
-             DEBUG(SSSDBG_OP_FAILURE, "sysdb_set_entry_attr failed.\n");
-             goto done;
-         }
-+
-+        if (mapped_attrs != NULL) {
-+            ret = sysdb_set_entry_attr(domain->sysdb, obj_dn, mapped_attrs,
-+                                       SYSDB_MOD_ADD);
-+            if (ret != EOK) {
-+                DEBUG(SSSDBG_OP_FAILURE,
-+                      "sysdb_set_entry_attr failed, ignored.\n");
-+            }
-+        }
-     }
- 
-     ret = EOK;
--- 
-2.9.3
-
diff --git a/SOURCES/0119-AD-Make-ad_account_can_shortcut-reusable-by-SSSD-on-.patch b/SOURCES/0119-AD-Make-ad_account_can_shortcut-reusable-by-SSSD-on-.patch
deleted file mode 100644
index c9874b9..0000000
--- a/SOURCES/0119-AD-Make-ad_account_can_shortcut-reusable-by-SSSD-on-.patch
+++ /dev/null
@@ -1,243 +0,0 @@
-From 54790675d0fd0627f7db8449ef97d59c0632006e Mon Sep 17 00:00:00 2001
-From: Jakub Hrozek <jhrozek@redhat.com>
-Date: Mon, 24 Apr 2017 10:13:44 +0200
-Subject: [PATCH 119/119] AD: Make ad_account_can_shortcut() reusable by SSSD
- on an IPA server
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Resolves:
-    https://pagure.io/SSSD/sssd/issue/3318
-
-The ad_account_can_shortcut() function is helpful to avoid unnecessary
-searches when SSSD is configured with an Active Directory domain that
-uses ID-mapping in the sense that if we find that an ID is outside our
-range, we can just abort the search in this domain and carry on.
-
-This function was only used in the AD provider functions which are used
-when SSSD is enrolled direcly with an AD server. This patch moves the
-function to a codepath that is shared between directly enrolled SSSD and
-SSSD running on an IPA server.
-
-Apart from moving the code, there are some minor changes to the function
-signature, namely the domain is passed as as struct (previously the
-domain name from the DP input was passed).
-
-Reviewed-by: Michal Židek <mzidek@redhat.com>
-(cherry picked from commit dfe05f505dcfea16e7d66ca1a44206aa2570e861)
----
- src/providers/ad/ad_id.c | 162 ++++++++++++++++++++++++-----------------------
- 1 file changed, 84 insertions(+), 78 deletions(-)
-
-diff --git a/src/providers/ad/ad_id.c b/src/providers/ad/ad_id.c
-index 8f26cb8744d2372c6180342c0d1bca025b16f52c..d1f6c444f5ddbcbbac6ff7f41fb6c8bf9ca976cb 100644
---- a/src/providers/ad/ad_id.c
-+++ b/src/providers/ad/ad_id.c
-@@ -50,6 +50,77 @@ disable_gc(struct ad_options *ad_options)
-     }
- }
- 
-+static bool ad_account_can_shortcut(struct sdap_idmap_ctx *idmap_ctx,
-+                                    struct sss_domain_info *domain,
-+                                    int filter_type,
-+                                    const char *filter_value)
-+{
-+    struct sss_domain_info *dom_head = NULL;
-+    struct sss_domain_info *sid_dom = NULL;
-+    enum idmap_error_code err;
-+    char *sid = NULL;
-+    const char *csid = NULL;
-+    uint32_t id;
-+    bool shortcut = false;
-+    errno_t ret;
-+
-+    if (!sdap_idmap_domain_has_algorithmic_mapping(idmap_ctx, domain->name,
-+                                                   domain->domain_id)) {
-+        goto done;
-+    }
-+
-+    switch (filter_type) {
-+    case BE_FILTER_IDNUM:
-+        /* convert value to ID */
-+        errno = 0;
-+        id = strtouint32(filter_value, NULL, 10);
-+        if (errno != 0) {
-+            ret = errno;
-+            DEBUG(SSSDBG_MINOR_FAILURE, "Unable to convert filter value to "
-+                  "number [%d]: %s\n", ret, strerror(ret));
-+            goto done;
-+        }
-+
-+        /* convert the ID to its SID equivalent */
-+        err = sss_idmap_unix_to_sid(idmap_ctx->map, id, &sid);
-+        if (err != IDMAP_SUCCESS) {
-+            DEBUG(SSSDBG_MINOR_FAILURE, "Mapping ID [%s] to SID failed: "
-+                  "[%s]\n", filter_value, idmap_error_string(err));
-+            goto done;
-+        }
-+        /* fall through */
-+        SSS_ATTRIBUTE_FALLTHROUGH;
-+    case BE_FILTER_SECID:
-+        csid = sid == NULL ? filter_value : sid;
-+
-+        dom_head = get_domains_head(domain);
-+        if (dom_head == NULL) {
-+            DEBUG(SSSDBG_CRIT_FAILURE, "Cannot find domain head\n");
-+            goto done;
-+        }
-+
-+        sid_dom = find_domain_by_sid(dom_head, csid);
-+        if (sid_dom == NULL) {
-+            DEBUG(SSSDBG_OP_FAILURE, "Invalid domain for SID:%s\n", csid);
-+            goto done;
-+        }
-+
-+        if (strcasecmp(sid_dom->name, domain->name) != 0) {
-+            shortcut = true;
-+        }
-+        break;
-+    default:
-+        break;
-+    }
-+
-+done:
-+    if (sid != NULL) {
-+        sss_idmap_free_sid(idmap_ctx->map, sid);
-+    }
-+
-+    return shortcut;
-+}
-+
- struct ad_handle_acct_info_state {
-     struct dp_id_data *ar;
-     struct sdap_id_ctx *ctx;
-@@ -78,6 +149,7 @@ ad_handle_acct_info_send(TALLOC_CTX *mem_ctx,
-     struct ad_handle_acct_info_state *state;
-     struct be_ctx *be_ctx = ctx->be;
-     errno_t ret;
-+    bool shortcut;
- 
-     req = tevent_req_create(mem_ctx, &state, struct ad_handle_acct_info_state);
-     if (req == NULL) {
-@@ -90,6 +162,18 @@ ad_handle_acct_info_send(TALLOC_CTX *mem_ctx,
-     state->ad_options = ad_options;
-     state->cindex = 0;
- 
-+    /* Try to shortcut if this is ID or SID search and it belongs to
-+     * other domain range than is in ar->domain. */
-+    shortcut = ad_account_can_shortcut(ctx->opts->idmap_ctx,
-+                                       sdom->dom,
-+                                       ar->filter_type,
-+                                       ar->filter_value);
-+    if (shortcut) {
-+        DEBUG(SSSDBG_TRACE_FUNC, "This ID is from different domain\n");
-+        ret = EOK;
-+        goto immediate;
-+    }
-+
-     if (sss_domain_get_state(sdom->dom) == DOM_INACTIVE) {
-         ret = ERR_SUBDOM_INACTIVE;
-         goto immediate;
-@@ -297,72 +381,6 @@ get_conn_list(TALLOC_CTX *mem_ctx, struct ad_id_ctx *ad_ctx,
-     return clist;
- }
- 
--static bool ad_account_can_shortcut(struct be_ctx *be_ctx,
--                                    struct sdap_idmap_ctx *idmap_ctx,
--                                    int filter_type,
--                                    const char *filter_value,
--                                    const char *filter_domain)
--{
--    struct sss_domain_info *domain = be_ctx->domain;
--    struct sss_domain_info *req_dom = NULL;
--    enum idmap_error_code err;
--    char *sid = NULL;
--    const char *csid = NULL;
--    uint32_t id;
--    bool shortcut = false;
--    errno_t ret;
--
--    if (!sdap_idmap_domain_has_algorithmic_mapping(idmap_ctx, domain->name,
--                                                   domain->domain_id)) {
--        goto done;
--    }
--
--    switch (filter_type) {
--    case BE_FILTER_IDNUM:
--        /* convert value to ID */
--        errno = 0;
--        id = strtouint32(filter_value, NULL, 10);
--        if (errno != 0) {
--            ret = errno;
--            DEBUG(SSSDBG_MINOR_FAILURE, "Unable to convert filter value to "
--                  "number [%d]: %s\n", ret, strerror(ret));
--            goto done;
--        }
--
--        /* convert the ID to its SID equivalent */
--        err = sss_idmap_unix_to_sid(idmap_ctx->map, id, &sid);
--        if (err != IDMAP_SUCCESS) {
--            DEBUG(SSSDBG_MINOR_FAILURE, "Mapping ID [%s] to SID failed: "
--                  "[%s]\n", filter_value, idmap_error_string(err));
--            goto done;
--        }
--        /* fall through */
--        SSS_ATTRIBUTE_FALLTHROUGH;
--    case BE_FILTER_SECID:
--        csid = sid == NULL ? filter_value : sid;
--
--        req_dom = find_domain_by_sid(domain, csid);
--        if (req_dom == NULL) {
--            DEBUG(SSSDBG_OP_FAILURE, "Invalid domain for SID:%s\n", csid);
--            goto done;
--        }
--
--        if (strcasecmp(req_dom->name, filter_domain) != 0) {
--            shortcut = true;
--        }
--        break;
--    default:
--        break;
--    }
--
--done:
--    if (sid != NULL) {
--        sss_idmap_free_sid(idmap_ctx->map, sid);
--    }
--
--    return shortcut;
--}
--
- struct ad_account_info_handler_state {
-     struct sss_domain_info *domain;
-     struct dp_reply_std reply;
-@@ -384,7 +402,6 @@ ad_account_info_handler_send(TALLOC_CTX *mem_ctx,
-     struct tevent_req *subreq;
-     struct tevent_req *req;
-     struct be_ctx *be_ctx;
--    bool shortcut;
-     errno_t ret;
- 
-     sdap_id_ctx = id_ctx->sdap_id_ctx;
-@@ -403,17 +420,6 @@ ad_account_info_handler_send(TALLOC_CTX *mem_ctx,
-         goto immediately;
-     }
- 
--    /* Try to shortcut if this is ID or SID search and it belongs to
--     * other domain range than is in ar->domain. */
--    shortcut = ad_account_can_shortcut(be_ctx, sdap_id_ctx->opts->idmap_ctx,
--                                       data->filter_type, data->filter_value,
--                                       data->domain);
--    if (shortcut) {
--        DEBUG(SSSDBG_TRACE_FUNC, "This ID is from different domain\n");
--        ret = EOK;
--        goto immediately;
--    }
--
-     domain = be_ctx->domain;
-     if (strcasecmp(data->domain, be_ctx->domain->name) != 0) {
-         /* Subdomain request, verify subdomain. */
--- 
-2.9.3
-
diff --git a/SOURCES/0120-LDAP-AD-Do-not-fail-in-case-rfc2307bis_nested_groups.patch b/SOURCES/0120-LDAP-AD-Do-not-fail-in-case-rfc2307bis_nested_groups.patch
deleted file mode 100644
index abe44ec..0000000
--- a/SOURCES/0120-LDAP-AD-Do-not-fail-in-case-rfc2307bis_nested_groups.patch
+++ /dev/null
@@ -1,47 +0,0 @@
-From 428909abd59f1eb8bb02b6627f37f61af3de2691 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Fabiano=20Fid=C3=AAncio?= <fidencio@redhat.com>
-Date: Mon, 1 May 2017 14:49:50 +0200
-Subject: [PATCH 120/120] LDAP/AD: Do not fail in case
- rfc2307bis_nested_groups_recv() returns ENOENT
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Commit 25699846 introduced a regression seen when an initgroup lookup is
-done and there's no nested groups involved.
-
-In this scenario the whole lookup fails due to an ENOENT returned by
-rfc2307bis_nested_groups_recv(), which leads to the user removal from
-sysdb causing some authentication issues.
-
-Resolves:
-https://pagure.io/SSSD/sssd/issue/3331
-
-Signed-off-by: Fabiano Fidêncio <fidencio@redhat.com>
-Reviewed-by: Sumit Bose <sbose@redhat.com>
----
- src/providers/ldap/sdap_async_initgroups_ad.c | 8 +++++++-
- 1 file changed, 7 insertions(+), 1 deletion(-)
-
-diff --git a/src/providers/ldap/sdap_async_initgroups_ad.c b/src/providers/ldap/sdap_async_initgroups_ad.c
-index f75b9211e2a06616dbf9b948e60b023a818c7e19..2831be9776293260aeec0e2ff85160f1938bdb32 100644
---- a/src/providers/ldap/sdap_async_initgroups_ad.c
-+++ b/src/providers/ldap/sdap_async_initgroups_ad.c
-@@ -1746,7 +1746,13 @@ static void sdap_ad_get_domain_local_groups_done(struct tevent_req *subreq)
- 
-     ret = rfc2307bis_nested_groups_recv(subreq);
-     talloc_zfree(subreq);
--    if (ret != EOK) {
-+    if (ret == ENOENT) {
-+        /* In case of ENOENT we can just proceed without making
-+         * sdap_get_initgr_user() fail because there's no nested
-+         * groups for this user/group. */
-+        ret = EOK;
-+        goto done;
-+    } else if (ret != EOK) {
-         tevent_req_error(req, ret);
-         return;
-     }
--- 
-2.9.3
-
diff --git a/SOURCES/0121-PAM-check-matching-certificates-from-all-domains.patch b/SOURCES/0121-PAM-check-matching-certificates-from-all-domains.patch
deleted file mode 100644
index 55c7c61..0000000
--- a/SOURCES/0121-PAM-check-matching-certificates-from-all-domains.patch
+++ /dev/null
@@ -1,116 +0,0 @@
-From 52514960f5b0609cd9f31f3c4455b61fbe4c04c5 Mon Sep 17 00:00:00 2001
-From: Sumit Bose <sbose@redhat.com>
-Date: Wed, 26 Apr 2017 17:16:19 +0200
-Subject: [PATCH 121/121] PAM: check matching certificates from all domains
-
-Although the cache_req lookup found matching in multiple domains only
-the results from the first domain were used. With this patch the results
-from all domains are checked.
-
-Resolves https://pagure.io/SSSD/sssd/issue/3385
-
-Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
-(cherry picked from commit 92d8b072f8c521e1b4effe109b5caedabd36ed6f)
----
- src/responder/pam/pamsrv_cmd.c | 69 ++++++++++++++++++++++++++++++++++++++----
- 1 file changed, 63 insertions(+), 6 deletions(-)
-
-diff --git a/src/responder/pam/pamsrv_cmd.c b/src/responder/pam/pamsrv_cmd.c
-index f2b3c74b483e527932dda42279d14a9ac184b475..10a178f839ec011c09a6da4575efbb026f3f7700 100644
---- a/src/responder/pam/pamsrv_cmd.c
-+++ b/src/responder/pam/pamsrv_cmd.c
-@@ -1352,15 +1352,71 @@ done:
-     pam_check_user_done(preq, ret);
- }
- 
-+static errno_t get_results_from_all_domains(TALLOC_CTX *mem_ctx,
-+                                            struct cache_req_result **results,
-+                                            struct ldb_result **ldb_results)
-+{
-+    int ret;
-+    size_t count = 0;
-+    size_t c;
-+    size_t d;
-+    size_t r = 0;
-+    struct ldb_result *res;
-+
-+    for (d = 0; results != NULL && results[d] != NULL; d++) {
-+        count += results[d]->count;
-+    }
-+
-+    res = talloc_zero(mem_ctx, struct ldb_result);
-+    if (res == NULL) {
-+        DEBUG(SSSDBG_OP_FAILURE, "talloc_zero failed.\n");
-+        return ENOMEM;
-+    }
-+
-+    if (count == 0) {
-+        *ldb_results = res;
-+        return EOK;
-+    }
-+
-+    res->msgs = talloc_zero_array(res, struct ldb_message *, count);
-+    if (res->msgs == NULL) {
-+        DEBUG(SSSDBG_OP_FAILURE, "talloc_zero_array failed.\n");
-+        return ENOMEM;
-+    }
-+    res->count = count;
-+
-+    for (d = 0; results != NULL && results[d] != NULL; d++) {
-+        for (c = 0; c < results[d]->count; c++) {
-+            if (r >= count) {
-+                DEBUG(SSSDBG_CRIT_FAILURE,
-+                      "More results found then counted before.\n");
-+                ret = EINVAL;
-+                goto done;
-+            }
-+            res->msgs[r++] = talloc_steal(res->msgs, results[d]->msgs[c]);
-+        }
-+    }
-+
-+    *ldb_results = res;
-+    ret = EOK;
-+
-+done:
-+    if (ret != EOK) {
-+        talloc_free(res);
-+    }
-+
-+    return ret;
-+}
-+
- static void pam_forwarder_lookup_by_cert_done(struct tevent_req *req)
- {
-     int ret;
--    struct cache_req_result *result;
-+    struct cache_req_result **results;
-     struct pam_auth_req *preq = tevent_req_callback_data(req,
-                                                          struct pam_auth_req);
-     const char *cert_user;
- 
--    ret = cache_req_user_by_cert_recv(preq, req, &result);
-+    ret = cache_req_recv(preq, req, &results);
-     talloc_zfree(req);
-     if (ret != EOK && ret != ENOENT) {
-         DEBUG(SSSDBG_OP_FAILURE, "cache_req_user_by_cert request failed.\n");
-@@ -1368,12 +1424,13 @@ static void pam_forwarder_lookup_by_cert_done(struct tevent_req *req)
-     }
- 
-     if (ret == EOK) {
--        if (preq->domain == NULL) {
--            preq->domain = result->domain;
-+        ret = get_results_from_all_domains(preq, results,
-+                                           &preq->cert_user_objs);
-+        if (ret != EOK) {
-+            DEBUG(SSSDBG_OP_FAILURE, "get_results_from_all_domains failed.\n");
-+            goto done;
-         }
- 
--        preq->cert_user_objs = talloc_steal(preq, result->ldb_result);
--
-         if (preq->pd->logon_name == NULL) {
-             if (preq->pd->cmd != SSS_PAM_PREAUTH) {
-                 DEBUG(SSSDBG_CRIT_FAILURE,
--- 
-2.9.3
-
diff --git a/SOURCES/0122-DP-Reduce-Data-Provider-log-level-noise.patch b/SOURCES/0122-DP-Reduce-Data-Provider-log-level-noise.patch
deleted file mode 100644
index b7dfbd3..0000000
--- a/SOURCES/0122-DP-Reduce-Data-Provider-log-level-noise.patch
+++ /dev/null
@@ -1,80 +0,0 @@
-From b818bb3f27ce672df0a6cadf2fd90716d2a576dc Mon Sep 17 00:00:00 2001
-From: Justin Stephenson <jstephen@redhat.com>
-Date: Wed, 26 Apr 2017 15:45:33 -0400
-Subject: [PATCH 122/127] DP: Reduce Data Provider log level noise
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Certain operations are not supported with certain providers
-causing informational Data Provider log messages to be logged as
-errors or failures. This patch lowers the log level to reduce overall
-log noise and ensure only critical log messages are logged when
-a low debug_level value is used.
-
-Resolves:
-https://pagure.io/SSSD/sssd/issue/3287
-https://pagure.io/SSSD/sssd/issue/3278
-
-Reviewed-by: Fabiano Fidêncio <fidencio@redhat.com>
-Reviewed-by: Pavel Březina <pbrezina@redhat.com>
-(cherry picked from commit e98d085b529e0ae5e07a717ce3b30f3943be0ee0)
----
- src/providers/data_provider/dp_methods.c |  2 +-
- src/providers/data_provider/dp_targets.c |  2 +-
- src/responder/common/responder_dp.c      | 13 +++++++++++--
- 3 files changed, 13 insertions(+), 4 deletions(-)
-
-diff --git a/src/providers/data_provider/dp_methods.c b/src/providers/data_provider/dp_methods.c
-index 498676d1bec2da300ca4b33f7110debcbf0aac00..9e49c5f5d65b869b3699fdc682a535e0111b6fd4 100644
---- a/src/providers/data_provider/dp_methods.c
-+++ b/src/providers/data_provider/dp_methods.c
-@@ -109,7 +109,7 @@ errno_t dp_find_method(struct data_provider *provider,
-     }
- 
-     if (!dp_target_initialized(provider->targets, target)) {
--        DEBUG(SSSDBG_CRIT_FAILURE, "Target [%s] is not initialized\n",
-+        DEBUG(SSSDBG_CONF_SETTINGS, "Target [%s] is not initialized\n",
-               dp_target_to_string(target));
-         return ERR_MISSING_DP_TARGET;
-     }
-diff --git a/src/providers/data_provider/dp_targets.c b/src/providers/data_provider/dp_targets.c
-index 26d20a8ef79b80d56df76d7a73ec8e63d001ecbc..e2a45bbac969ca7b9b13729f26b8cded8ab7eebc 100644
---- a/src/providers/data_provider/dp_targets.c
-+++ b/src/providers/data_provider/dp_targets.c
-@@ -284,7 +284,7 @@ static errno_t dp_target_init(struct be_ctx *be_ctx,
-     if (!target->explicitly_configured && (ret == ELIBBAD || ret == ENOTSUP)) {
-         /* Target not found but it wasn't explicitly
-          * configured so we shall just continue. */
--        DEBUG(SSSDBG_CRIT_FAILURE, "Target [%s] is not supported by "
-+        DEBUG(SSSDBG_CONF_SETTINGS, "Target [%s] is not supported by "
-               "module [%s].\n", target->name, target->module_name);
-         ret = EOK;
-         goto done;
-diff --git a/src/responder/common/responder_dp.c b/src/responder/common/responder_dp.c
-index 080f70fd5945ffd234e0ef226d8139df071c4752..a75a611960801f5f5bdc95f00aea9ab921e8e293 100644
---- a/src/responder/common/responder_dp.c
-+++ b/src/responder/common/responder_dp.c
-@@ -218,8 +218,17 @@ static int sss_dp_get_reply(DBusPendingCall *pending,
-             err = ETIME;
-             goto done;
-         }
--        DEBUG(SSSDBG_FATAL_FAILURE,"The Data Provider returned an error [%s]\n",
--                 dbus_message_get_error_name(reply));
-+
-+        if (strcmp(dbus_message_get_error_name(reply),
-+                   SBUS_ERROR_DP_NOTSUP) == 0) {
-+            DEBUG(SSSDBG_CONF_SETTINGS,
-+                  "Data Provider does not support this operation.\n");
-+        } else {
-+            DEBUG(SSSDBG_FATAL_FAILURE,
-+                  "The Data Provider returned an error [%s]\n",
-+                  dbus_message_get_error_name(reply));
-+        }
-+
-         /* Falling through to default intentionally*/
-         SSS_ATTRIBUTE_FALLTHROUGH;
-     default:
--- 
-2.9.3
-
diff --git a/SOURCES/0123-NSS-Move-output-name-formatting-to-utils.patch b/SOURCES/0123-NSS-Move-output-name-formatting-to-utils.patch
deleted file mode 100644
index abc8b99..0000000
--- a/SOURCES/0123-NSS-Move-output-name-formatting-to-utils.patch
+++ /dev/null
@@ -1,249 +0,0 @@
-From 43b07b3fe8794a6e19db5cd2e9036e3d4d6c43ad Mon Sep 17 00:00:00 2001
-From: Nikolai Kondrashov <Nikolai.Kondrashov@redhat.com>
-Date: Wed, 22 Mar 2017 14:32:35 +0200
-Subject: [PATCH 123/127] NSS: Move output name formatting to utils
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Move NSS nss_get_name_from_msg and the core of sized_output_name to the
-utils to make them available to provider and other responders.
-
-Reviewed-by: Pavel Březina <pbrezina@redhat.com>
-(cherry picked from commit a012a71f21bf1a4687e58085f19c18cc5b2bbadd)
----
- src/responder/common/responder_common.c | 27 ++++---------
- src/responder/nss/nss_protocol_grent.c  |  2 +-
- src/responder/nss/nss_protocol_pwent.c  |  2 +-
- src/responder/nss/nss_protocol_sid.c    |  2 +-
- src/responder/nss/nss_utils.c           | 27 -------------
- src/util/usertools.c                    | 67 +++++++++++++++++++++++++++++++++
- src/util/util.h                         |  9 +++++
- 7 files changed, 87 insertions(+), 49 deletions(-)
-
-diff --git a/src/responder/common/responder_common.c b/src/responder/common/responder_common.c
-index 7496d293fddb3e947d59a4f2aaeb2c83234dfcc7..9d4889be652c6d6fb974b59001a9ac77b496e9ab 100644
---- a/src/responder/common/responder_common.c
-+++ b/src/responder/common/responder_common.c
-@@ -1685,7 +1685,7 @@ int sized_output_name(TALLOC_CTX *mem_ctx,
- {
-     TALLOC_CTX *tmp_ctx = NULL;
-     errno_t ret;
--    char *username;
-+    char *name_str;
-     struct sized_string *name;
- 
-     tmp_ctx = talloc_new(NULL);
-@@ -1693,30 +1693,19 @@ int sized_output_name(TALLOC_CTX *mem_ctx,
-         return ENOMEM;
-     }
- 
--    username = sss_output_name(tmp_ctx, orig_name, name_dom->case_preserve,
--                               rctx->override_space);
--    if (username == NULL) {
--        ret = EIO;
--        goto done;
--    }
--
--    if (name_dom->fqnames) {
--        username = sss_tc_fqname(tmp_ctx, name_dom->names, name_dom, username);
--        if (username == NULL) {
--            DEBUG(SSSDBG_CRIT_FAILURE, "sss_replace_space failed\n");
--            ret = EIO;
--            goto done;
--        }
--    }
--
-     name = talloc_zero(tmp_ctx, struct sized_string);
-     if (name == NULL) {
-         ret = ENOMEM;
-         goto done;
-     }
- 
--    to_sized_string(name, username);
--    name->str = talloc_steal(name, username);
-+    ret = sss_output_fqname(mem_ctx, name_dom, orig_name,
-+                            rctx->override_space, &name_str);
-+    if (ret != EOK) {
-+        goto done;
-+    }
-+
-+    to_sized_string(name, name_str);
-     *_name = talloc_steal(mem_ctx, name);
-     ret = EOK;
- done:
-diff --git a/src/responder/nss/nss_protocol_grent.c b/src/responder/nss/nss_protocol_grent.c
-index fae1d47d7b217beafba75740e2e6d9cb8cdbc1d0..947463df93e188729959737efa4ac4f44a8459c4 100644
---- a/src/responder/nss/nss_protocol_grent.c
-+++ b/src/responder/nss/nss_protocol_grent.c
-@@ -41,7 +41,7 @@ nss_get_grent(TALLOC_CTX *mem_ctx,
-     }
- 
-     /* Get fields. */
--    name = nss_get_name_from_msg(domain, msg);
-+    name = sss_get_name_from_msg(domain, msg);
-     gid = sss_view_ldb_msg_find_attr_as_uint64(domain, msg, SYSDB_GIDNUM, 0);
- 
-     if (name == NULL || gid == 0) {
-diff --git a/src/responder/nss/nss_protocol_pwent.c b/src/responder/nss/nss_protocol_pwent.c
-index edda9d3c87389898435a34fe7927868bc1cd9ac5..cb643f29e2d5f0a0c55c51afd9def73813061aa7 100644
---- a/src/responder/nss/nss_protocol_pwent.c
-+++ b/src/responder/nss/nss_protocol_pwent.c
-@@ -225,7 +225,7 @@ nss_get_pwent(TALLOC_CTX *mem_ctx,
- 
-     /* Get fields. */
-     upn = ldb_msg_find_attr_as_string(msg, SYSDB_UPN, NULL);
--    name = nss_get_name_from_msg(domain, msg);
-+    name = sss_get_name_from_msg(domain, msg);
-     gid = nss_get_gid(domain, msg);
-     uid = sss_view_ldb_msg_find_attr_as_uint64(domain, msg, SYSDB_UIDNUM, 0);
- 
-diff --git a/src/responder/nss/nss_protocol_sid.c b/src/responder/nss/nss_protocol_sid.c
-index a6a4e27d039c67ef98f6d5900d5e3fcadb3ee717..d4b7ee22d7c68a9e6f7c668f7268cdc5f36768b3 100644
---- a/src/responder/nss/nss_protocol_sid.c
-+++ b/src/responder/nss/nss_protocol_sid.c
-@@ -532,7 +532,7 @@ nss_protocol_fill_name_list(struct nss_ctx *nss_ctx,
-             return ret;
-         }
- 
--        tmp_str = nss_get_name_from_msg(result->domain, result->msgs[c]);
-+        tmp_str = sss_get_name_from_msg(result->domain, result->msgs[c]);
-         if (tmp_str == NULL) {
-             return EINVAL;
-         }
-diff --git a/src/responder/nss/nss_utils.c b/src/responder/nss/nss_utils.c
-index 2cd9c33b42f7e018ea89d2df206637f35646489e..b4950e5a6eaec6a4511f7251dcf2e623c0177230 100644
---- a/src/responder/nss/nss_utils.c
-+++ b/src/responder/nss/nss_utils.c
-@@ -27,33 +27,6 @@
- #include "responder/nss/nss_private.h"
- 
- const char *
--nss_get_name_from_msg(struct sss_domain_info *domain,
--                      struct ldb_message *msg)
--{
--    const char *name;
--
--    /* If domain has a view associated we return overridden name
--     * if possible. */
--    if (DOM_HAS_VIEWS(domain)) {
--        name = ldb_msg_find_attr_as_string(msg, OVERRIDE_PREFIX SYSDB_NAME,
--                                           NULL);
--        if (name != NULL) {
--            return name;
--        }
--    }
--
--    /* Otherwise we try to return name override from
--     * Default Truest View for trusted users. */
--    name = ldb_msg_find_attr_as_string(msg, SYSDB_DEFAULT_OVERRIDE_NAME, NULL);
--    if (name != NULL) {
--        return name;
--    }
--
--    /* If no override is found we return the original name. */
--    return ldb_msg_find_attr_as_string(msg, SYSDB_NAME, NULL);
--}
--
--const char *
- nss_get_pwfield(struct nss_ctx *nctx,
-                struct sss_domain_info *dom)
- {
-diff --git a/src/util/usertools.c b/src/util/usertools.c
-index 7b87c567a6c2dc7e9c267407434b2a7a9edeaa00..5dfe6d7765b8032c7447de75e10c6c2a1d4c49ec 100644
---- a/src/util/usertools.c
-+++ b/src/util/usertools.c
-@@ -816,3 +816,70 @@ done:
-     talloc_free(tmp_ctx);
-     return outname;
- }
-+
-+const char *
-+sss_get_name_from_msg(struct sss_domain_info *domain,
-+                      struct ldb_message *msg)
-+{
-+    const char *name;
-+
-+    /* If domain has a view associated we return overridden name
-+     * if possible. */
-+    if (DOM_HAS_VIEWS(domain)) {
-+        name = ldb_msg_find_attr_as_string(msg, OVERRIDE_PREFIX SYSDB_NAME,
-+                                           NULL);
-+        if (name != NULL) {
-+            return name;
-+        }
-+    }
-+
-+    /* Otherwise we try to return name override from
-+     * Default Truest View for trusted users. */
-+    name = ldb_msg_find_attr_as_string(msg, SYSDB_DEFAULT_OVERRIDE_NAME, NULL);
-+    if (name != NULL) {
-+        return name;
-+    }
-+
-+    /* If no override is found we return the original name. */
-+    return ldb_msg_find_attr_as_string(msg, SYSDB_NAME, NULL);
-+}
-+
-+int sss_output_fqname(TALLOC_CTX *mem_ctx,
-+                      struct sss_domain_info *domain,
-+                      const char *name,
-+                      char override_space,
-+                      char **_output_name)
-+{
-+    TALLOC_CTX *tmp_ctx = NULL;
-+    errno_t ret;
-+    char *output_name;
-+
-+    tmp_ctx = talloc_new(NULL);
-+    if (tmp_ctx == NULL) {
-+        ret = ENOMEM;
-+        goto done;
-+    }
-+
-+    output_name = sss_output_name(tmp_ctx, name, domain->case_preserve,
-+                                  override_space);
-+    if (output_name == NULL) {
-+        ret = EIO;
-+        goto done;
-+    }
-+
-+    if (domain->fqnames) {
-+        output_name = sss_tc_fqname(tmp_ctx, domain->names,
-+                                    domain, output_name);
-+        if (output_name == NULL) {
-+            DEBUG(SSSDBG_CRIT_FAILURE, "sss_tc_fqname failed\n");
-+            ret = EIO;
-+            goto done;
-+        }
-+    }
-+
-+    *_output_name = talloc_steal(mem_ctx, output_name);
-+    ret = EOK;
-+done:
-+    talloc_zfree(tmp_ctx);
-+    return ret;
-+}
-diff --git a/src/util/util.h b/src/util/util.h
-index 4ef13ced48addc19403402d7d880176da24ceec6..5ba4c36ca88e325c20a3b1ecc8080a11ca276dcf 100644
---- a/src/util/util.h
-+++ b/src/util/util.h
-@@ -304,6 +304,15 @@ char *sss_output_name(TALLOC_CTX *mem_ctx,
-                       bool case_sensitive,
-                       const char replace_space);
- 
-+int sss_output_fqname(TALLOC_CTX *mem_ctx,
-+                      struct sss_domain_info *domain,
-+                      const char *name,
-+                      char override_space,
-+                      char **_output_name);
-+
-+const char *sss_get_name_from_msg(struct sss_domain_info *domain,
-+                                  struct ldb_message *msg);
-+
- /* from backup-file.c */
- int backup_file(const char *src, int dbglvl);
- 
--- 
-2.9.3
-
diff --git a/SOURCES/0124-CACHE_REQ-Add-a-new-cache_req_ncache_filter_fn-plugi.patch b/SOURCES/0124-CACHE_REQ-Add-a-new-cache_req_ncache_filter_fn-plugi.patch
deleted file mode 100644
index fd0780e..0000000
--- a/SOURCES/0124-CACHE_REQ-Add-a-new-cache_req_ncache_filter_fn-plugi.patch
+++ /dev/null
@@ -1,322 +0,0 @@
-From da437bb72fc6ab072fc9b3e6d6809bac323de1e2 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Fabiano=20Fid=C3=AAncio?= <fidencio@redhat.com>
-Date: Tue, 25 Apr 2017 14:14:05 +0200
-Subject: [PATCH 124/127] CACHE_REQ: Add a new cache_req_ncache_filter_fn()
- plugin function
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-This function will be responsible for filtering out all the results that
-we have that are also present in the negative cache.
-
-This is useful mainly for plugins which don't use name as an input token
-but can still be affected by filter_{users,groups} options.
-
-For now this new function is not being used anywhere.
-
-Related:
-https://pagure.io/SSSD/sssd/issue/3362
-
-Signed-off-by: Fabiano Fidêncio <fidencio@redhat.com>
-
-Reviewed-by: Pavel Březina <pbrezina@redhat.com>
-(cherry picked from commit f24ee5cca4cd43e7edf26fec453fbd99392bbe4b)
----
- src/responder/common/cache_req/cache_req_plugin.h           | 13 +++++++++++++
- .../common/cache_req/plugins/cache_req_enum_groups.c        |  1 +
- src/responder/common/cache_req/plugins/cache_req_enum_svc.c |  1 +
- .../common/cache_req/plugins/cache_req_enum_users.c         |  1 +
- .../common/cache_req/plugins/cache_req_group_by_filter.c    |  1 +
- .../common/cache_req/plugins/cache_req_group_by_id.c        |  1 +
- .../common/cache_req/plugins/cache_req_group_by_name.c      |  1 +
- .../common/cache_req/plugins/cache_req_host_by_name.c       |  1 +
- .../common/cache_req/plugins/cache_req_initgroups_by_name.c |  1 +
- .../common/cache_req/plugins/cache_req_initgroups_by_upn.c  |  1 +
- .../common/cache_req/plugins/cache_req_netgroup_by_name.c   |  1 +
- .../common/cache_req/plugins/cache_req_object_by_id.c       |  1 +
- .../common/cache_req/plugins/cache_req_object_by_name.c     |  1 +
- .../common/cache_req/plugins/cache_req_object_by_sid.c      |  1 +
- .../common/cache_req/plugins/cache_req_svc_by_name.c        |  1 +
- .../common/cache_req/plugins/cache_req_svc_by_port.c        |  1 +
- .../common/cache_req/plugins/cache_req_user_by_cert.c       |  1 +
- .../common/cache_req/plugins/cache_req_user_by_filter.c     |  1 +
- .../common/cache_req/plugins/cache_req_user_by_id.c         |  1 +
- .../common/cache_req/plugins/cache_req_user_by_name.c       |  1 +
- .../common/cache_req/plugins/cache_req_user_by_upn.c        |  1 +
- 21 files changed, 33 insertions(+)
-
-diff --git a/src/responder/common/cache_req/cache_req_plugin.h b/src/responder/common/cache_req/cache_req_plugin.h
-index e0b619528f6aa31a10a5b48c3c5acc96de90caa1..8117325506b2951c3966fa50506ed0d55273ee81 100644
---- a/src/responder/common/cache_req/cache_req_plugin.h
-+++ b/src/responder/common/cache_req/cache_req_plugin.h
-@@ -93,6 +93,18 @@ typedef errno_t
-                            struct cache_req_data *data);
- 
- /**
-+ * Filter the result through the negative cache.
-+ *
-+ * This is useful for plugins which don't use name as an input
-+ * token but can be affected by filter_users and filter_groups
-+ * options.
-+ */
-+typedef errno_t
-+(*cache_req_ncache_filter_fn)(struct sss_nc_ctx *ncache,
-+                              struct sss_domain_info *domain,
-+                              const char *name);
-+
-+/**
-  * Add an object into global negative cache.
-  *
-  * @return EOK If everything went fine.
-@@ -207,6 +219,7 @@ struct cache_req_plugin {
-     cache_req_global_ncache_add_fn global_ncache_add_fn;
-     cache_req_ncache_check_fn ncache_check_fn;
-     cache_req_ncache_add_fn ncache_add_fn;
-+    cache_req_ncache_filter_fn ncache_filter_fn;
-     cache_req_lookup_fn lookup_fn;
-     cache_req_dp_send_fn dp_send_fn;
-     cache_req_dp_recv_fn dp_recv_fn;
-diff --git a/src/responder/common/cache_req/plugins/cache_req_enum_groups.c b/src/responder/common/cache_req/plugins/cache_req_enum_groups.c
-index 49ce3508e678862e4389657187b9659ce90fbd1c..11ce9e90ff28f77078b025a44593a44be8f1f5c5 100644
---- a/src/responder/common/cache_req/plugins/cache_req_enum_groups.c
-+++ b/src/responder/common/cache_req/plugins/cache_req_enum_groups.c
-@@ -75,6 +75,7 @@ const struct cache_req_plugin cache_req_enum_groups = {
-     .global_ncache_add_fn = NULL,
-     .ncache_check_fn = NULL,
-     .ncache_add_fn = NULL,
-+    .ncache_filter_fn = NULL,
-     .lookup_fn = cache_req_enum_groups_lookup,
-     .dp_send_fn = cache_req_enum_groups_dp_send,
-     .dp_recv_fn = cache_req_common_dp_recv
-diff --git a/src/responder/common/cache_req/plugins/cache_req_enum_svc.c b/src/responder/common/cache_req/plugins/cache_req_enum_svc.c
-index 499b994738d62707b4e86d5a8383e3e2b82e8c57..72b2f1a7d2d2e02ce1a995098d1f26003444bddb 100644
---- a/src/responder/common/cache_req/plugins/cache_req_enum_svc.c
-+++ b/src/responder/common/cache_req/plugins/cache_req_enum_svc.c
-@@ -76,6 +76,7 @@ const struct cache_req_plugin cache_req_enum_svc = {
-     .global_ncache_add_fn = NULL,
-     .ncache_check_fn = NULL,
-     .ncache_add_fn = NULL,
-+    .ncache_filter_fn = NULL,
-     .lookup_fn = cache_req_enum_svc_lookup,
-     .dp_send_fn = cache_req_enum_svc_dp_send,
-     .dp_recv_fn = cache_req_common_dp_recv
-diff --git a/src/responder/common/cache_req/plugins/cache_req_enum_users.c b/src/responder/common/cache_req/plugins/cache_req_enum_users.c
-index b635354be6e9d2e2e2af1a6f867ac68e6cf7f085..e0647a0102d9568abdcebfbf0fb99fc2624d5565 100644
---- a/src/responder/common/cache_req/plugins/cache_req_enum_users.c
-+++ b/src/responder/common/cache_req/plugins/cache_req_enum_users.c
-@@ -75,6 +75,7 @@ const struct cache_req_plugin cache_req_enum_users = {
-     .global_ncache_add_fn = NULL,
-     .ncache_check_fn = NULL,
-     .ncache_add_fn = NULL,
-+    .ncache_filter_fn = NULL,
-     .lookup_fn = cache_req_enum_users_lookup,
-     .dp_send_fn = cache_req_enum_users_dp_send,
-     .dp_recv_fn = cache_req_common_dp_recv
-diff --git a/src/responder/common/cache_req/plugins/cache_req_group_by_filter.c b/src/responder/common/cache_req/plugins/cache_req_group_by_filter.c
-index 4377a476c36e5e03c8533bc62335b84fa1cee3ff..aa89953b88313605041cce599999fc5bbc741525 100644
---- a/src/responder/common/cache_req/plugins/cache_req_group_by_filter.c
-+++ b/src/responder/common/cache_req/plugins/cache_req_group_by_filter.c
-@@ -131,6 +131,7 @@ const struct cache_req_plugin cache_req_group_by_filter = {
-     .global_ncache_add_fn = NULL,
-     .ncache_check_fn = NULL,
-     .ncache_add_fn = NULL,
-+    .ncache_filter_fn = NULL,
-     .lookup_fn = cache_req_group_by_filter_lookup,
-     .dp_send_fn = cache_req_group_by_filter_dp_send,
-     .dp_recv_fn = cache_req_common_dp_recv
-diff --git a/src/responder/common/cache_req/plugins/cache_req_group_by_id.c b/src/responder/common/cache_req/plugins/cache_req_group_by_id.c
-index ad5b7d890a42f29b586ab8e0943fef3dfab1162d..5613bf67c6acd1b2ace00cf75221462f45ef6743 100644
---- a/src/responder/common/cache_req/plugins/cache_req_group_by_id.c
-+++ b/src/responder/common/cache_req/plugins/cache_req_group_by_id.c
-@@ -144,6 +144,7 @@ const struct cache_req_plugin cache_req_group_by_id = {
-     .global_ncache_add_fn = cache_req_group_by_id_global_ncache_add,
-     .ncache_check_fn = cache_req_group_by_id_ncache_check,
-     .ncache_add_fn = NULL,
-+    .ncache_filter_fn = NULL,
-     .lookup_fn = cache_req_group_by_id_lookup,
-     .dp_send_fn = cache_req_group_by_id_dp_send,
-     .dp_recv_fn = cache_req_common_dp_recv
-diff --git a/src/responder/common/cache_req/plugins/cache_req_group_by_name.c b/src/responder/common/cache_req/plugins/cache_req_group_by_name.c
-index de1e8f9442273acf386a2278b06f28ee63a7e3c6..7706051818590af77da75d3e4c7f671c89170f82 100644
---- a/src/responder/common/cache_req/plugins/cache_req_group_by_name.c
-+++ b/src/responder/common/cache_req/plugins/cache_req_group_by_name.c
-@@ -194,6 +194,7 @@ const struct cache_req_plugin cache_req_group_by_name = {
-     .global_ncache_add_fn = NULL,
-     .ncache_check_fn = cache_req_group_by_name_ncache_check,
-     .ncache_add_fn = cache_req_group_by_name_ncache_add,
-+    .ncache_filter_fn = NULL,
-     .lookup_fn = cache_req_group_by_name_lookup,
-     .dp_send_fn = cache_req_group_by_name_dp_send,
-     .dp_recv_fn = cache_req_common_dp_recv
-diff --git a/src/responder/common/cache_req/plugins/cache_req_host_by_name.c b/src/responder/common/cache_req/plugins/cache_req_host_by_name.c
-index 1171cd63fac5cc1d36b31bf8a069f059705cae90..9cb32f6b18327873ba4b96fa177e8295be461db0 100644
---- a/src/responder/common/cache_req/plugins/cache_req_host_by_name.c
-+++ b/src/responder/common/cache_req/plugins/cache_req_host_by_name.c
-@@ -92,6 +92,7 @@ const struct cache_req_plugin cache_req_host_by_name = {
-     .global_ncache_add_fn = NULL,
-     .ncache_check_fn = NULL,
-     .ncache_add_fn = NULL,
-+    .ncache_filter_fn = NULL,
-     .lookup_fn = cache_req_host_by_name_lookup,
-     .dp_send_fn = cache_req_host_by_name_dp_send,
-     .dp_recv_fn = cache_req_common_dp_recv
-diff --git a/src/responder/common/cache_req/plugins/cache_req_initgroups_by_name.c b/src/responder/common/cache_req/plugins/cache_req_initgroups_by_name.c
-index f100aefe5c92279cde7e3209c7f48f5e2b35f135..75ac44e1ad36238f01342eced9188d07daa50720 100644
---- a/src/responder/common/cache_req/plugins/cache_req_initgroups_by_name.c
-+++ b/src/responder/common/cache_req/plugins/cache_req_initgroups_by_name.c
-@@ -209,6 +209,7 @@ const struct cache_req_plugin cache_req_initgroups_by_name = {
-     .global_ncache_add_fn = NULL,
-     .ncache_check_fn = cache_req_initgroups_by_name_ncache_check,
-     .ncache_add_fn = cache_req_initgroups_by_name_ncache_add,
-+    .ncache_filter_fn = NULL,
-     .lookup_fn = cache_req_initgroups_by_name_lookup,
-     .dp_send_fn = cache_req_initgroups_by_name_dp_send,
-     .dp_recv_fn = cache_req_common_dp_recv
-diff --git a/src/responder/common/cache_req/plugins/cache_req_initgroups_by_upn.c b/src/responder/common/cache_req/plugins/cache_req_initgroups_by_upn.c
-index 266ec7b8a28d496d9603bd9b6cdfef268ffa8559..b6fb43ee02d2f041fb3d992b375ae65a02db8b03 100644
---- a/src/responder/common/cache_req/plugins/cache_req_initgroups_by_upn.c
-+++ b/src/responder/common/cache_req/plugins/cache_req_initgroups_by_upn.c
-@@ -120,6 +120,7 @@ const struct cache_req_plugin cache_req_initgroups_by_upn = {
-     .global_ncache_add_fn = NULL,
-     .ncache_check_fn = cache_req_initgroups_by_upn_ncache_check,
-     .ncache_add_fn = cache_req_initgroups_by_upn_ncache_add,
-+    .ncache_filter_fn = NULL,
-     .lookup_fn = cache_req_initgroups_by_upn_lookup,
-     .dp_send_fn = cache_req_initgroups_by_upn_dp_send,
-     .dp_recv_fn = cache_req_common_dp_recv
-diff --git a/src/responder/common/cache_req/plugins/cache_req_netgroup_by_name.c b/src/responder/common/cache_req/plugins/cache_req_netgroup_by_name.c
-index ab3e553d3ecb8ae09094dcfc938ed0ac01925327..4d8bb18579a286042b00528190dadd52fdd7c75c 100644
---- a/src/responder/common/cache_req/plugins/cache_req_netgroup_by_name.c
-+++ b/src/responder/common/cache_req/plugins/cache_req_netgroup_by_name.c
-@@ -128,6 +128,7 @@ const struct cache_req_plugin cache_req_netgroup_by_name = {
-     .global_ncache_add_fn = NULL,
-     .ncache_check_fn = cache_req_netgroup_by_name_ncache_check,
-     .ncache_add_fn = cache_req_netgroup_by_name_ncache_add,
-+    .ncache_filter_fn = NULL,
-     .lookup_fn = cache_req_netgroup_by_name_lookup,
-     .dp_send_fn = cache_req_netgroup_by_name_dp_send,
-     .dp_recv_fn = cache_req_common_dp_recv
-diff --git a/src/responder/common/cache_req/plugins/cache_req_object_by_id.c b/src/responder/common/cache_req/plugins/cache_req_object_by_id.c
-index 9557bd15270b2eb1a0671f9ef91033efac29c3ac..ff3d0e67862be365c56ab24396b4982e8addded0 100644
---- a/src/responder/common/cache_req/plugins/cache_req_object_by_id.c
-+++ b/src/responder/common/cache_req/plugins/cache_req_object_by_id.c
-@@ -111,6 +111,7 @@ const struct cache_req_plugin cache_req_object_by_id = {
-     .global_ncache_add_fn = cache_req_object_by_id_global_ncache_add,
-     .ncache_check_fn = cache_req_object_by_id_ncache_check,
-     .ncache_add_fn = NULL,
-+    .ncache_filter_fn = NULL,
-     .lookup_fn = cache_req_object_by_id_lookup,
-     .dp_send_fn = cache_req_object_by_id_dp_send,
-     .dp_recv_fn = cache_req_common_dp_recv
-diff --git a/src/responder/common/cache_req/plugins/cache_req_object_by_name.c b/src/responder/common/cache_req/plugins/cache_req_object_by_name.c
-index e236d1fa4aadcd87b192d34ebaf5f9ad8908b6c2..854d0b83c420ebebcb5e0e079c707081fa313632 100644
---- a/src/responder/common/cache_req/plugins/cache_req_object_by_name.c
-+++ b/src/responder/common/cache_req/plugins/cache_req_object_by_name.c
-@@ -204,6 +204,7 @@ const struct cache_req_plugin cache_req_object_by_name = {
-     .global_ncache_add_fn = NULL,
-     .ncache_check_fn = cache_req_object_by_name_ncache_check,
-     .ncache_add_fn = cache_req_object_by_name_ncache_add,
-+    .ncache_filter_fn = NULL,
-     .lookup_fn = cache_req_object_by_name_lookup,
-     .dp_send_fn = cache_req_object_by_name_dp_send,
-     .dp_recv_fn = cache_req_common_dp_recv
-diff --git a/src/responder/common/cache_req/plugins/cache_req_object_by_sid.c b/src/responder/common/cache_req/plugins/cache_req_object_by_sid.c
-index dfec79da07d669165205a767cab22c2254686134..039a79df7bb1ab213ce4334835e9fc18e6d0faac 100644
---- a/src/responder/common/cache_req/plugins/cache_req_object_by_sid.c
-+++ b/src/responder/common/cache_req/plugins/cache_req_object_by_sid.c
-@@ -120,6 +120,7 @@ const struct cache_req_plugin cache_req_object_by_sid = {
-     .global_ncache_add_fn = cache_req_object_by_sid_global_ncache_add,
-     .ncache_check_fn = cache_req_object_by_sid_ncache_check,
-     .ncache_add_fn = NULL,
-+    .ncache_filter_fn = NULL,
-     .lookup_fn = cache_req_object_by_sid_lookup,
-     .dp_send_fn = cache_req_object_by_sid_dp_send,
-     .dp_recv_fn = cache_req_common_dp_recv
-diff --git a/src/responder/common/cache_req/plugins/cache_req_svc_by_name.c b/src/responder/common/cache_req/plugins/cache_req_svc_by_name.c
-index b2bfb26ffed1a60ed8389fa89b0e728c8c6cf76c..4c32d9977cc06e43eed3a90e7dcf107e91efefb5 100644
---- a/src/responder/common/cache_req/plugins/cache_req_svc_by_name.c
-+++ b/src/responder/common/cache_req/plugins/cache_req_svc_by_name.c
-@@ -152,6 +152,7 @@ const struct cache_req_plugin cache_req_svc_by_name = {
-     .global_ncache_add_fn = NULL,
-     .ncache_check_fn = cache_req_svc_by_name_ncache_check,
-     .ncache_add_fn = cache_req_svc_by_name_ncache_add,
-+    .ncache_filter_fn = NULL,
-     .lookup_fn = cache_req_svc_by_name_lookup,
-     .dp_send_fn = cache_req_svc_by_name_dp_send,
-     .dp_recv_fn = cache_req_common_dp_recv
-diff --git a/src/responder/common/cache_req/plugins/cache_req_svc_by_port.c b/src/responder/common/cache_req/plugins/cache_req_svc_by_port.c
-index 0e48437f4b64d26112be88af1eebc20f012b70fd..1e998f642c766d15d3f6fe777aa5c789629508e2 100644
---- a/src/responder/common/cache_req/plugins/cache_req_svc_by_port.c
-+++ b/src/responder/common/cache_req/plugins/cache_req_svc_by_port.c
-@@ -125,6 +125,7 @@ const struct cache_req_plugin cache_req_svc_by_port = {
-     .global_ncache_add_fn = NULL,
-     .ncache_check_fn = cache_req_svc_by_port_ncache_check,
-     .ncache_add_fn = cache_req_svc_by_port_ncache_add,
-+    .ncache_filter_fn = NULL,
-     .lookup_fn = cache_req_svc_by_port_lookup,
-     .dp_send_fn = cache_req_svc_by_port_dp_send,
-     .dp_recv_fn = cache_req_common_dp_recv
-diff --git a/src/responder/common/cache_req/plugins/cache_req_user_by_cert.c b/src/responder/common/cache_req/plugins/cache_req_user_by_cert.c
-index 286a34db276e0098060982c572e2a68ceceebf60..7a0c7d8ce1644f1c41b64c6903e4e20eb3c2c081 100644
---- a/src/responder/common/cache_req/plugins/cache_req_user_by_cert.c
-+++ b/src/responder/common/cache_req/plugins/cache_req_user_by_cert.c
-@@ -94,6 +94,7 @@ const struct cache_req_plugin cache_req_user_by_cert = {
-     .global_ncache_add_fn = cache_req_user_by_cert_global_ncache_add,
-     .ncache_check_fn = cache_req_user_by_cert_ncache_check,
-     .ncache_add_fn = NULL,
-+    .ncache_filter_fn = NULL,
-     .lookup_fn = cache_req_user_by_cert_lookup,
-     .dp_send_fn = cache_req_user_by_cert_dp_send,
-     .dp_recv_fn = cache_req_common_dp_recv
-diff --git a/src/responder/common/cache_req/plugins/cache_req_user_by_filter.c b/src/responder/common/cache_req/plugins/cache_req_user_by_filter.c
-index c476814373cd784bf8dbbea1da7b010afe5bb4e4..dd3f42e855389ecc73690e4d18c4977253b108a6 100644
---- a/src/responder/common/cache_req/plugins/cache_req_user_by_filter.c
-+++ b/src/responder/common/cache_req/plugins/cache_req_user_by_filter.c
-@@ -131,6 +131,7 @@ const struct cache_req_plugin cache_req_user_by_filter = {
-     .global_ncache_add_fn = NULL,
-     .ncache_check_fn = NULL,
-     .ncache_add_fn = NULL,
-+    .ncache_filter_fn = NULL,
-     .lookup_fn = cache_req_user_by_filter_lookup,
-     .dp_send_fn = cache_req_user_by_filter_dp_send,
-     .dp_recv_fn = cache_req_common_dp_recv
-diff --git a/src/responder/common/cache_req/plugins/cache_req_user_by_id.c b/src/responder/common/cache_req/plugins/cache_req_user_by_id.c
-index 9ba73292e5dc518e86c6e00e7e493d6871f28e70..b14b3738aa7721723f524ebd46301a3a9a1c712f 100644
---- a/src/responder/common/cache_req/plugins/cache_req_user_by_id.c
-+++ b/src/responder/common/cache_req/plugins/cache_req_user_by_id.c
-@@ -144,6 +144,7 @@ const struct cache_req_plugin cache_req_user_by_id = {
-     .global_ncache_add_fn = cache_req_user_by_id_global_ncache_add,
-     .ncache_check_fn = cache_req_user_by_id_ncache_check,
-     .ncache_add_fn = NULL,
-+    .ncache_filter_fn = NULL,
-     .lookup_fn = cache_req_user_by_id_lookup,
-     .dp_send_fn = cache_req_user_by_id_dp_send,
-     .dp_recv_fn = cache_req_common_dp_recv
-diff --git a/src/responder/common/cache_req/plugins/cache_req_user_by_name.c b/src/responder/common/cache_req/plugins/cache_req_user_by_name.c
-index 15da7d0d20b1ac97511a226daecc8ef7e7d2e7e4..2e49de938d0af50089d0cf49860441c2b6ea679c 100644
---- a/src/responder/common/cache_req/plugins/cache_req_user_by_name.c
-+++ b/src/responder/common/cache_req/plugins/cache_req_user_by_name.c
-@@ -199,6 +199,7 @@ const struct cache_req_plugin cache_req_user_by_name = {
-     .global_ncache_add_fn = NULL,
-     .ncache_check_fn = cache_req_user_by_name_ncache_check,
-     .ncache_add_fn = cache_req_user_by_name_ncache_add,
-+    .ncache_filter_fn = NULL,
-     .lookup_fn = cache_req_user_by_name_lookup,
-     .dp_send_fn = cache_req_user_by_name_dp_send,
-     .dp_recv_fn = cache_req_common_dp_recv
-diff --git a/src/responder/common/cache_req/plugins/cache_req_user_by_upn.c b/src/responder/common/cache_req/plugins/cache_req_user_by_upn.c
-index 40a097b1634d2b2d089b7feb377ea2389a58672c..b8bcd241ed79c510aca214ad3788215ae2997d20 100644
---- a/src/responder/common/cache_req/plugins/cache_req_user_by_upn.c
-+++ b/src/responder/common/cache_req/plugins/cache_req_user_by_upn.c
-@@ -125,6 +125,7 @@ const struct cache_req_plugin cache_req_user_by_upn = {
-     .global_ncache_add_fn = NULL,
-     .ncache_check_fn = cache_req_user_by_upn_ncache_check,
-     .ncache_add_fn = cache_req_user_by_upn_ncache_add,
-+    .ncache_filter_fn = NULL,
-     .lookup_fn = cache_req_user_by_upn_lookup,
-     .dp_send_fn = cache_req_user_by_upn_dp_send,
-     .dp_recv_fn = cache_req_common_dp_recv
--- 
-2.9.3
-
diff --git a/SOURCES/0125-CACHE_REQ_RESULT-Introduce-cache_req_create_ldb_resu.patch b/SOURCES/0125-CACHE_REQ_RESULT-Introduce-cache_req_create_ldb_resu.patch
deleted file mode 100644
index 2508964..0000000
--- a/SOURCES/0125-CACHE_REQ_RESULT-Introduce-cache_req_create_ldb_resu.patch
+++ /dev/null
@@ -1,92 +0,0 @@
-From a2bfa4d2074cacc5d30f17a3b3af260ec9eaaa59 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Fabiano=20Fid=C3=AAncio?= <fidencio@redhat.com>
-Date: Thu, 27 Apr 2017 11:24:45 +0200
-Subject: [PATCH 125/127] CACHE_REQ_RESULT: Introduce
- cache_req_create_ldb_result_from_msg_list()
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Similarly to what cache_req_create_ldb_result_from_msg() does this new
-function creates a new ldb_result from a list of ldb_message.
-
-It's going to be used in the follow-up patch where some messages from
-ldb_result may be filtered and then a new ldb_result has to be created.
-
-Related:
-https://pagure.io/SSSD/sssd/issue/3362
-
-Signed-off-by: Fabiano Fidêncio <fidencio@redhat.com>
-
-Reviewed-by: Pavel Březina <pbrezina@redhat.com>
-(cherry picked from commit 180e0b282be6aeb047c4b24b46e0b56afba1fdc8)
----
- src/responder/common/cache_req/cache_req_private.h |  5 ++++
- src/responder/common/cache_req/cache_req_result.c  | 35 ++++++++++++++++++++++
- 2 files changed, 40 insertions(+)
-
-diff --git a/src/responder/common/cache_req/cache_req_private.h b/src/responder/common/cache_req/cache_req_private.h
-index 851005c389f994b1bd2d04cda9b68df8b18492cc..c0ee5f969f2a171b8a6eb396b3d14b593d157b76 100644
---- a/src/responder/common/cache_req/cache_req_private.h
-+++ b/src/responder/common/cache_req/cache_req_private.h
-@@ -137,6 +137,11 @@ cache_req_create_and_add_result(TALLOC_CTX *mem_ctx,
-                                 size_t *_num_results);
- 
- struct ldb_result *
-+cache_req_create_ldb_result_from_msg_list(TALLOC_CTX *mem_ctx,
-+                                          struct ldb_message **ldb_msgs,
-+                                          size_t ldb_msg_count);
-+
-+struct ldb_result *
- cache_req_create_ldb_result_from_msg(TALLOC_CTX *mem_ctx,
-                                      struct ldb_message *ldb_msg);
- 
-diff --git a/src/responder/common/cache_req/cache_req_result.c b/src/responder/common/cache_req/cache_req_result.c
-index e20ae5653acf22a2e0190ef6a88836c7fab9868e..366ba748082336c7c752b576cfd7b8fb8cd82fcf 100644
---- a/src/responder/common/cache_req/cache_req_result.c
-+++ b/src/responder/common/cache_req/cache_req_result.c
-@@ -122,6 +122,41 @@ cache_req_create_and_add_result(TALLOC_CTX *mem_ctx,
- }
- 
- struct ldb_result *
-+cache_req_create_ldb_result_from_msg_list(TALLOC_CTX *mem_ctx,
-+                                          struct ldb_message **ldb_msgs,
-+                                          size_t ldb_msg_count)
-+{
-+    struct ldb_result *ldb_result;
-+
-+    if (ldb_msgs == NULL || ldb_msgs[0] == NULL) {
-+        DEBUG(SSSDBG_CRIT_FAILURE, "No message set!\n");
-+        return NULL;
-+    }
-+
-+    ldb_result = talloc_zero(NULL, struct ldb_result);
-+    if (ldb_result == NULL) {
-+        return NULL;
-+    }
-+
-+    ldb_result->extended = NULL;
-+    ldb_result->controls = NULL;
-+    ldb_result->refs = NULL;
-+    ldb_result->count = ldb_msg_count;
-+    ldb_result->msgs = talloc_zero_array(ldb_result, struct ldb_message *,
-+                                         ldb_msg_count + 1);
-+    if (ldb_result->msgs == NULL) {
-+        talloc_free(ldb_result);
-+        return NULL;
-+    }
-+
-+    for (size_t i = 0; i < ldb_msg_count; i++) {
-+        ldb_result->msgs[i] = talloc_steal(ldb_result->msgs, ldb_msgs[i]);
-+    }
-+
-+    return ldb_result;
-+}
-+
-+struct ldb_result *
- cache_req_create_ldb_result_from_msg(TALLOC_CTX *mem_ctx,
-                                      struct ldb_message *ldb_msg)
- {
--- 
-2.9.3
-
diff --git a/SOURCES/0126-CACHE_REQ-Make-use-of-cache_req_ncache_filter_fn.patch b/SOURCES/0126-CACHE_REQ-Make-use-of-cache_req_ncache_filter_fn.patch
deleted file mode 100644
index d36136a..0000000
--- a/SOURCES/0126-CACHE_REQ-Make-use-of-cache_req_ncache_filter_fn.patch
+++ /dev/null
@@ -1,392 +0,0 @@
-From 4c3780ced1b1507ebd8c3d0b91a3ef50b74e0b52 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Fabiano=20Fid=C3=AAncio?= <fidencio@redhat.com>
-Date: Tue, 25 Apr 2017 16:33:58 +0200
-Subject: [PATCH 126/127] CACHE_REQ: Make use of cache_req_ncache_filter_fn()
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-This patch makes use of cache_req_ncache_filter_fn() in order to process
-the result of a cache_req search and then filter out all the results
-that are present in the negative cache.
-
-The "post cache_req search" result processing is done basically in two
-different cases:
-- plugins which don't use name as an input token (group_by_id, user_by_id
-  and object_by_id), but still can be affected by filter_{users,groups}
-  options;
-- plugins responsible for groups and users enumeration (enum_groups and
-  enum_users);
-
-Resolves:
-https://pagure.io/SSSD/sssd/issue/3362
-
-Signed-off-by: Fabiano Fidêncio <fidencio@redhat.com>
-
-Reviewed-by: Pavel Březina <pbrezina@redhat.com>
-(cherry picked from commit 4ef0b19a5e8a327443d027e57487c8a1e4f654ce)
----
- src/responder/common/cache_req/cache_req_search.c  | 124 +++++++++++++++++++--
- .../cache_req/plugins/cache_req_enum_groups.c      |  10 +-
- .../cache_req/plugins/cache_req_enum_users.c       |  10 +-
- .../cache_req/plugins/cache_req_group_by_id.c      |  10 +-
- .../cache_req/plugins/cache_req_object_by_id.c     |  17 ++-
- .../cache_req/plugins/cache_req_user_by_id.c       |  10 +-
- src/responder/nss/nss_protocol_grent.c             |  12 --
- src/responder/nss/nss_protocol_pwent.c             |  11 --
- 8 files changed, 165 insertions(+), 39 deletions(-)
-
-diff --git a/src/responder/common/cache_req/cache_req_search.c b/src/responder/common/cache_req/cache_req_search.c
-index 8bc1530b341f587cb502fdf0ca3ed8d37cfb7d13..793dbc5042ae329b2cade5d1eb5a6d41102e264f 100644
---- a/src/responder/common/cache_req/cache_req_search.c
-+++ b/src/responder/common/cache_req/cache_req_search.c
-@@ -84,6 +84,87 @@ static void cache_req_search_ncache_add(struct cache_req *cr)
-     return;
- }
- 
-+static errno_t cache_req_search_ncache_filter(TALLOC_CTX *mem_ctx,
-+                                              struct cache_req *cr,
-+                                              struct ldb_result *result,
-+                                              struct ldb_result **_result)
-+{
-+    TALLOC_CTX *tmp_ctx;
-+    struct ldb_result *filtered_result;
-+    struct ldb_message **msgs;
-+    size_t msg_count;
-+    const char *name;
-+    errno_t ret;
-+
-+    tmp_ctx = talloc_new(NULL);
-+    if (tmp_ctx == NULL) {
-+        return ENOMEM;
-+    }
-+
-+    if (cr->plugin->ncache_filter_fn == NULL) {
-+        CACHE_REQ_DEBUG(SSSDBG_TRACE_FUNC, cr,
-+                        "This request type does not support filtering "
-+                        "result by negative cache\n");
-+
-+        *_result = talloc_steal(mem_ctx, result);
-+
-+        ret = EOK;
-+        goto done;
-+    }
-+
-+    CACHE_REQ_DEBUG(SSSDBG_TRACE_FUNC, cr,
-+                    "Filtering out results by negative cache\n");
-+
-+    msgs = talloc_zero_array(tmp_ctx, struct ldb_message *, result->count);
-+    msg_count = 0;
-+
-+    for (size_t i = 0; i < result->count; i++) {
-+        name = sss_get_name_from_msg(cr->domain, result->msgs[i]);
-+        if (name == NULL) {
-+            CACHE_REQ_DEBUG(SSSDBG_CRIT_FAILURE, cr,
-+                  "sss_get_name_from_msg() returned NULL, which should never "
-+                  "happen in this scenario!\n");
-+            ret = ERR_INTERNAL;
-+            goto done;
-+        }
-+
-+        ret = cr->plugin->ncache_filter_fn(cr->ncache, cr->domain, name);
-+        if (ret == EEXIST) {
-+            CACHE_REQ_DEBUG(SSSDBG_TRACE_FUNC, cr,
-+                            "[%s] filtered out! (negative cache)\n",
-+                            name);
-+            continue;
-+        } else if (ret != EOK && ret != ENOENT) {
-+            CACHE_REQ_DEBUG(SSSDBG_CRIT_FAILURE, cr,
-+                            "Unable to check negative cache [%d]: %s\n",
-+                            ret, sss_strerror(ret));
-+            goto done;
-+        }
-+
-+        msgs[msg_count] = talloc_steal(msgs, result->msgs[i]);
-+        msg_count++;
-+    }
-+
-+    if (msg_count == 0) {
-+        ret = ENOENT;
-+        goto done;
-+    }
-+
-+    filtered_result = cache_req_create_ldb_result_from_msg_list(tmp_ctx, msgs,
-+                                                                msg_count);
-+    if (filtered_result == NULL) {
-+        ret = ENOMEM;
-+        goto done;
-+    }
-+
-+    *_result = talloc_steal(mem_ctx, filtered_result);
-+    ret = EOK;
-+
-+done:
-+    talloc_free(tmp_ctx);
-+    return ret;
-+}
-+
- static errno_t cache_req_search_cache(TALLOC_CTX *mem_ctx,
-                                       struct cache_req *cr,
-                                       struct ldb_result **_result)
-@@ -338,10 +419,18 @@ static void cache_req_search_oob_done(struct tevent_req *subreq)
- 
- static void cache_req_search_done(struct tevent_req *subreq)
- {
-+    TALLOC_CTX *tmp_ctx;
-     struct cache_req_search_state *state;
-     struct tevent_req *req;
-+    struct ldb_result *result = NULL;
-     errno_t ret;
- 
-+    tmp_ctx = talloc_new(NULL);
-+    if (tmp_ctx == NULL) {
-+        ret = ENOMEM;
-+        goto done;
-+    }
-+
-     req = tevent_req_callback_data(subreq, struct tevent_req);
-     state = tevent_req_data(req, struct cache_req_search_state);
- 
-@@ -349,23 +438,36 @@ static void cache_req_search_done(struct tevent_req *subreq)
-     talloc_zfree(subreq);
- 
-     /* Get result from cache again. */
--    ret = cache_req_search_cache(state, state->cr, &state->result);
--    if (ret == ENOENT) {
--        /* Only store entry in negative cache if DP request succeeded
--         * because only then we know that the entry does not exist. */
--        if (state->dp_success) {
--            cache_req_search_ncache_add(state->cr);
-+    ret = cache_req_search_cache(tmp_ctx, state->cr, &result);
-+    if (ret != EOK) {
-+        if (ret == ENOENT) {
-+            /* Only store entry in negative cache if DP request succeeded
-+             * because only then we know that the entry does not exist. */
-+            if (state->dp_success) {
-+                cache_req_search_ncache_add(state->cr);
-+            }
-         }
--        tevent_req_error(req, ENOENT);
--        return;
--    } else if (ret != EOK) {
--        tevent_req_error(req, ret);
--        return;
-+        goto done;
-+    }
-+
-+    /* ret == EOK */
-+    ret = cache_req_search_ncache_filter(state, state->cr, result,
-+                                         &state->result);
-+    if (ret != EOK) {
-+        goto done;
-     }
- 
-     CACHE_REQ_DEBUG(SSSDBG_TRACE_FUNC, state->cr,
-                     "Returning updated object [%s]\n", state->cr->debugobj);
- 
-+done:
-+    talloc_free(tmp_ctx);
-+
-+    if (ret != EOK) {
-+        tevent_req_error(req, ret);
-+        return;
-+    }
-+
-     tevent_req_done(req);
-     return;
- }
-diff --git a/src/responder/common/cache_req/plugins/cache_req_enum_groups.c b/src/responder/common/cache_req/plugins/cache_req_enum_groups.c
-index 11ce9e90ff28f77078b025a44593a44be8f1f5c5..15350ca8279bc77c73bcc4abe51c97a8a37cb8c8 100644
---- a/src/responder/common/cache_req/plugins/cache_req_enum_groups.c
-+++ b/src/responder/common/cache_req/plugins/cache_req_enum_groups.c
-@@ -55,6 +55,14 @@ cache_req_enum_groups_dp_send(TALLOC_CTX *mem_ctx,
-                                    SSS_DP_GROUP, NULL, 0, NULL);
- }
- 
-+static errno_t
-+cache_req_enum_groups_ncache_filter(struct sss_nc_ctx *ncache,
-+                                    struct sss_domain_info *domain,
-+                                    const char *name)
-+{
-+    return sss_ncache_check_group(ncache, domain, name);
-+}
-+
- const struct cache_req_plugin cache_req_enum_groups = {
-     .name = "Enumerate groups",
-     .attr_expiration = SYSDB_CACHE_EXPIRE,
-@@ -75,7 +83,7 @@ const struct cache_req_plugin cache_req_enum_groups = {
-     .global_ncache_add_fn = NULL,
-     .ncache_check_fn = NULL,
-     .ncache_add_fn = NULL,
--    .ncache_filter_fn = NULL,
-+    .ncache_filter_fn = cache_req_enum_groups_ncache_filter,
-     .lookup_fn = cache_req_enum_groups_lookup,
-     .dp_send_fn = cache_req_enum_groups_dp_send,
-     .dp_recv_fn = cache_req_common_dp_recv
-diff --git a/src/responder/common/cache_req/plugins/cache_req_enum_users.c b/src/responder/common/cache_req/plugins/cache_req_enum_users.c
-index e0647a0102d9568abdcebfbf0fb99fc2624d5565..a3ddcdd45548a2fa7c367f3fb3be103c115dedb4 100644
---- a/src/responder/common/cache_req/plugins/cache_req_enum_users.c
-+++ b/src/responder/common/cache_req/plugins/cache_req_enum_users.c
-@@ -55,6 +55,14 @@ cache_req_enum_users_dp_send(TALLOC_CTX *mem_ctx,
-                                    SSS_DP_USER, NULL, 0, NULL);
- }
- 
-+static errno_t
-+cache_req_enum_users_ncache_filter(struct sss_nc_ctx *ncache,
-+                                   struct sss_domain_info *domain,
-+                                   const char *name)
-+{
-+    return sss_ncache_check_user(ncache, domain, name);
-+}
-+
- const struct cache_req_plugin cache_req_enum_users = {
-     .name = "Enumerate users",
-     .attr_expiration = SYSDB_CACHE_EXPIRE,
-@@ -75,7 +83,7 @@ const struct cache_req_plugin cache_req_enum_users = {
-     .global_ncache_add_fn = NULL,
-     .ncache_check_fn = NULL,
-     .ncache_add_fn = NULL,
--    .ncache_filter_fn = NULL,
-+    .ncache_filter_fn = cache_req_enum_users_ncache_filter,
-     .lookup_fn = cache_req_enum_users_lookup,
-     .dp_send_fn = cache_req_enum_users_dp_send,
-     .dp_recv_fn = cache_req_common_dp_recv
-diff --git a/src/responder/common/cache_req/plugins/cache_req_group_by_id.c b/src/responder/common/cache_req/plugins/cache_req_group_by_id.c
-index 5613bf67c6acd1b2ace00cf75221462f45ef6743..5ca64283a781318bc4e4d6920fff989c3f3919b4 100644
---- a/src/responder/common/cache_req/plugins/cache_req_group_by_id.c
-+++ b/src/responder/common/cache_req/plugins/cache_req_group_by_id.c
-@@ -43,6 +43,14 @@ cache_req_group_by_id_ncache_check(struct sss_nc_ctx *ncache,
- }
- 
- static errno_t
-+cache_req_group_by_id_ncache_filter(struct sss_nc_ctx *ncache,
-+                                    struct sss_domain_info *domain,
-+                                    const char *name)
-+{
-+    return sss_ncache_check_group(ncache, domain, name);
-+}
-+
-+static errno_t
- cache_req_group_by_id_global_ncache_add(struct sss_nc_ctx *ncache,
-                                         struct cache_req_data *data)
- {
-@@ -144,7 +152,7 @@ const struct cache_req_plugin cache_req_group_by_id = {
-     .global_ncache_add_fn = cache_req_group_by_id_global_ncache_add,
-     .ncache_check_fn = cache_req_group_by_id_ncache_check,
-     .ncache_add_fn = NULL,
--    .ncache_filter_fn = NULL,
-+    .ncache_filter_fn = cache_req_group_by_id_ncache_filter,
-     .lookup_fn = cache_req_group_by_id_lookup,
-     .dp_send_fn = cache_req_group_by_id_dp_send,
-     .dp_recv_fn = cache_req_common_dp_recv
-diff --git a/src/responder/common/cache_req/plugins/cache_req_object_by_id.c b/src/responder/common/cache_req/plugins/cache_req_object_by_id.c
-index ff3d0e67862be365c56ab24396b4982e8addded0..339bd4f5fef827acc1aa3c123d041e426d9e4782 100644
---- a/src/responder/common/cache_req/plugins/cache_req_object_by_id.c
-+++ b/src/responder/common/cache_req/plugins/cache_req_object_by_id.c
-@@ -50,6 +50,21 @@ cache_req_object_by_id_ncache_check(struct sss_nc_ctx *ncache,
- }
- 
- static errno_t
-+cache_req_object_by_id_ncache_filter(struct sss_nc_ctx *ncache,
-+                                     struct sss_domain_info *domain,
-+                                     const char *name)
-+{
-+    errno_t ret;
-+
-+    ret = sss_ncache_check_user(ncache, domain, name);
-+    if (ret == EEXIST) {
-+        ret = sss_ncache_check_group(ncache, domain, name);
-+    }
-+
-+    return ret;
-+}
-+
-+static errno_t
- cache_req_object_by_id_global_ncache_add(struct sss_nc_ctx *ncache,
-                                          struct cache_req_data *data)
- {
-@@ -111,7 +126,7 @@ const struct cache_req_plugin cache_req_object_by_id = {
-     .global_ncache_add_fn = cache_req_object_by_id_global_ncache_add,
-     .ncache_check_fn = cache_req_object_by_id_ncache_check,
-     .ncache_add_fn = NULL,
--    .ncache_filter_fn = NULL,
-+    .ncache_filter_fn = cache_req_object_by_id_ncache_filter,
-     .lookup_fn = cache_req_object_by_id_lookup,
-     .dp_send_fn = cache_req_object_by_id_dp_send,
-     .dp_recv_fn = cache_req_common_dp_recv
-diff --git a/src/responder/common/cache_req/plugins/cache_req_user_by_id.c b/src/responder/common/cache_req/plugins/cache_req_user_by_id.c
-index b14b3738aa7721723f524ebd46301a3a9a1c712f..913f9be5bcc2dfd074b52cb3b15fb6948826e831 100644
---- a/src/responder/common/cache_req/plugins/cache_req_user_by_id.c
-+++ b/src/responder/common/cache_req/plugins/cache_req_user_by_id.c
-@@ -43,6 +43,14 @@ cache_req_user_by_id_ncache_check(struct sss_nc_ctx *ncache,
- }
- 
- static errno_t
-+cache_req_user_by_id_ncache_filter(struct sss_nc_ctx *ncache,
-+                                   struct sss_domain_info *domain,
-+                                   const char *name)
-+{
-+    return sss_ncache_check_user(ncache, domain, name);
-+}
-+
-+static errno_t
- cache_req_user_by_id_global_ncache_add(struct sss_nc_ctx *ncache,
-                                        struct cache_req_data *data)
- {
-@@ -144,7 +152,7 @@ const struct cache_req_plugin cache_req_user_by_id = {
-     .global_ncache_add_fn = cache_req_user_by_id_global_ncache_add,
-     .ncache_check_fn = cache_req_user_by_id_ncache_check,
-     .ncache_add_fn = NULL,
--    .ncache_filter_fn = NULL,
-+    .ncache_filter_fn = cache_req_user_by_id_ncache_filter,
-     .lookup_fn = cache_req_user_by_id_lookup,
-     .dp_send_fn = cache_req_user_by_id_dp_send,
-     .dp_recv_fn = cache_req_common_dp_recv
-diff --git a/src/responder/nss/nss_protocol_grent.c b/src/responder/nss/nss_protocol_grent.c
-index 947463df93e188729959737efa4ac4f44a8459c4..ee228c722a153a1ba7aa8a1b30a1e551108424bb 100644
---- a/src/responder/nss/nss_protocol_grent.c
-+++ b/src/responder/nss/nss_protocol_grent.c
-@@ -241,18 +241,6 @@ nss_protocol_fill_grent(struct nss_ctx *nss_ctx,
-             continue;
-         }
- 
--        /* Check negative cache during enumeration. */
--        if (cmd_ctx->enumeration) {
--            ret = sss_ncache_check_group(nss_ctx->rctx->ncache,
--                                         result->domain, name->str);
--            if (ret == EEXIST) {
--                DEBUG(SSSDBG_TRACE_FUNC,
--                      "User [%s] filtered out! (negative cache)\n",
--                      name->str);
--                continue;
--            }
--        }
--
-         /* Adjust packet size: gid, num_members + string fields. */
- 
-         ret = sss_packet_grow(packet, 2 * sizeof(uint32_t)
-diff --git a/src/responder/nss/nss_protocol_pwent.c b/src/responder/nss/nss_protocol_pwent.c
-index cb643f29e2d5f0a0c55c51afd9def73813061aa7..b355d4fc90397f51e82545e56940be850f144d49 100644
---- a/src/responder/nss/nss_protocol_pwent.c
-+++ b/src/responder/nss/nss_protocol_pwent.c
-@@ -309,17 +309,6 @@ nss_protocol_fill_pwent(struct nss_ctx *nss_ctx,
-             continue;
-         }
- 
--        /* Check negative cache during enumeration. */
--        if (cmd_ctx->enumeration) {
--            ret = sss_ncache_check_user(nss_ctx->rctx->ncache,
--                                        result->domain, name->str);
--            if (ret == EEXIST) {
--                DEBUG(SSSDBG_TRACE_FUNC,
--                      "User [%s] filtered out! (negative cache)\n", name->str);
--                continue;
--            }
--        }
--
-         /* Adjust packet size: uid, gid + string fields. */
- 
-         ret = sss_packet_grow(packet, 2 * sizeof(uint32_t)
--- 
-2.9.3
-
diff --git a/SOURCES/0127-SERVER_MODE-Update-sdap-lists-for-each-ad_ctx.patch b/SOURCES/0127-SERVER_MODE-Update-sdap-lists-for-each-ad_ctx.patch
deleted file mode 100644
index 772f302..0000000
--- a/SOURCES/0127-SERVER_MODE-Update-sdap-lists-for-each-ad_ctx.patch
+++ /dev/null
@@ -1,81 +0,0 @@
-From 9b9d3e2817fdcf16f2949641d4130b39856a4bf6 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Michal=20=C5=BDidek?= <mzidek@redhat.com>
-Date: Fri, 28 Apr 2017 20:49:56 +0200
-Subject: [PATCH 127/127] SERVER_MODE: Update sdap lists for each ad_ctx
-
-We use separate AD context for each subdomain in the server mode.
-Every such context has it's own sdap_domain list witch represents
-sdap options such as filter and search bases for every domain.
-
-However AD context can only fully initialize sdap_domain structure
-for the same domain for which the whole context was created, which
-resulted in the other sdap_domain structures to be have automaticily
-detected settings. This can cause problems if user is member of
-groups from multiple domains.
-
-Resolves:
-https://pagure.io/SSSD/sssd/issue/3381
-
-Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
-(cherry picked from commit 21f3d6124ea28218d02e1e345d38e2b948e4ec23)
----
- src/providers/ipa/ipa_subdomains_server.c | 36 +++++++++++++++++++++++++++++++
- 1 file changed, 36 insertions(+)
-
-diff --git a/src/providers/ipa/ipa_subdomains_server.c b/src/providers/ipa/ipa_subdomains_server.c
-index b02ea67af964a03e5466067cdb2b3ba4498120eb..443d83824f329b9d8c3d8e820113e1029f832240 100644
---- a/src/providers/ipa/ipa_subdomains_server.c
-+++ b/src/providers/ipa/ipa_subdomains_server.c
-@@ -870,6 +870,7 @@ static errno_t ipa_server_create_trusts_step(struct tevent_req *req)
- {
-     struct tevent_req *subreq = NULL;
-     struct ipa_ad_server_ctx *trust_iter;
-+    struct ipa_ad_server_ctx *trust_i;
-     struct ipa_server_create_trusts_state *state = NULL;
- 
-     state = tevent_req_data(req, struct ipa_server_create_trusts_state);
-@@ -900,6 +901,41 @@ static errno_t ipa_server_create_trusts_step(struct tevent_req *req)
-         }
-     }
- 
-+    /* Refresh all sdap_dom lists in all ipa_ad_server_ctx contexts */
-+    DLIST_FOR_EACH(trust_iter, state->id_ctx->server_mode->trusts) {
-+        struct sdap_domain *sdom_a;
-+
-+        sdom_a = sdap_domain_get(trust_iter->ad_id_ctx->sdap_id_ctx->opts,
-+                                 trust_iter->dom);
-+        if (sdom_a == NULL) {
-+            continue;
-+        }
-+
-+        DLIST_FOR_EACH(trust_i, state->id_ctx->server_mode->trusts) {
-+            struct sdap_domain *sdom_b;
-+
-+            if (strcmp(trust_iter->dom->name, trust_i->dom->name) == 0) {
-+                continue;
-+            }
-+
-+            sdom_b = sdap_domain_get(trust_i->ad_id_ctx->sdap_id_ctx->opts,
-+                                     sdom_a->dom);
-+            if (sdom_b == NULL) {
-+                continue;
-+            }
-+
-+            /* Replace basedn and search bases from sdom_b with values
-+             * from sdom_a */
-+            sdom_b->search_bases = sdom_a->search_bases;
-+            sdom_b->user_search_bases = sdom_a->user_search_bases;
-+            sdom_b->group_search_bases = sdom_a->group_search_bases;
-+            sdom_b->netgroup_search_bases = sdom_a->netgroup_search_bases;
-+            sdom_b->sudo_search_bases = sdom_a->sudo_search_bases;
-+            sdom_b->service_search_bases = sdom_a->service_search_bases;
-+            sdom_b->autofs_search_bases = sdom_a->autofs_search_bases;
-+        }
-+    }
-+
-     return EOK;
- }
- 
--- 
-2.9.3
-
diff --git a/SOURCES/0128-sss_nss_getlistbycert-return-results-from-multiple-d.patch b/SOURCES/0128-sss_nss_getlistbycert-return-results-from-multiple-d.patch
deleted file mode 100644
index 26092f6..0000000
--- a/SOURCES/0128-sss_nss_getlistbycert-return-results-from-multiple-d.patch
+++ /dev/null
@@ -1,348 +0,0 @@
-From 71731d26dc4f2c36989779f327b0e9a399486e14 Mon Sep 17 00:00:00 2001
-From: Sumit Bose <sbose@redhat.com>
-Date: Tue, 9 May 2017 16:57:43 +0200
-Subject: [PATCH] sss_nss_getlistbycert: return results from multiple domains
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Currently only the results from one domain were returned although all
-domains were searched and the results were available. Unit tests are
-updated to cover this case as well.
-
-Resolves https://pagure.io/SSSD/sssd/issue/3393
-
-Reviewed-by: Pavel Březina <pbrezina@redhat.com>
----
- src/responder/nss/nss_cmd.c          | 87 +++++++++++++++++++++++++++++++++++-
- src/responder/nss/nss_protocol.h     |  6 +++
- src/responder/nss/nss_protocol_sid.c | 78 ++++++++++++++++++++++++++++++++
- src/tests/cmocka/test_nss_srv.c      | 33 +++++++++-----
- 4 files changed, 192 insertions(+), 12 deletions(-)
-
-diff --git a/src/responder/nss/nss_cmd.c b/src/responder/nss/nss_cmd.c
-index 1931bf62a686c7f30852dac547866609cf54a81b..a4727c18786a86c28b5415ba82295967a47a8ec0 100644
---- a/src/responder/nss/nss_cmd.c
-+++ b/src/responder/nss/nss_cmd.c
-@@ -51,6 +51,7 @@ nss_cmd_ctx_create(TALLOC_CTX *mem_ctx,
- }
- 
- static void nss_getby_done(struct tevent_req *subreq);
-+static void nss_getlistby_done(struct tevent_req *subreq);
- 
- static errno_t nss_getby_name(struct cli_ctx *cli_ctx,
-                               enum cache_req_type type,
-@@ -212,6 +213,89 @@ done:
-     return EOK;
- }
- 
-+static errno_t nss_getlistby_cert(struct cli_ctx *cli_ctx,
-+                                  enum cache_req_type type)
-+{
-+    struct nss_cmd_ctx *cmd_ctx;
-+    struct tevent_req *subreq;
-+    const char *cert;
-+    errno_t ret;
-+
-+    cmd_ctx = nss_cmd_ctx_create(cli_ctx, cli_ctx, type, NULL);
-+    if (cmd_ctx == NULL) {
-+        ret = ENOMEM;
-+        goto done;
-+    }
-+
-+    cmd_ctx->sid_id_type = SSS_ID_TYPE_UID;
-+
-+    ret = nss_protocol_parse_cert(cli_ctx, &cert);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_CRIT_FAILURE, "Invalid request message!\n");
-+        goto done;
-+    }
-+
-+    DEBUG(SSSDBG_TRACE_FUNC, "Input cert: %s\n", get_last_x_chars(cert, 10));
-+
-+    subreq = cache_req_user_by_cert_send(cmd_ctx, cli_ctx->ev, cli_ctx->rctx,
-+                                         cli_ctx->rctx->ncache, 0,
-+                                         CACHE_REQ_ANY_DOM, NULL,
-+                                         cert);
-+    if (subreq == NULL) {
-+        DEBUG(SSSDBG_OP_FAILURE, "cache_req_user_by_cert_send failed.\n");
-+        ret = ENOMEM;
-+        goto done;
-+    }
-+    tevent_req_set_callback(subreq, nss_getlistby_done, cmd_ctx);
-+
-+    ret = EOK;
-+
-+done:
-+    if (ret != EOK) {
-+        talloc_free(cmd_ctx);
-+        return nss_protocol_done(cli_ctx, ret);
-+    }
-+
-+    return EOK;
-+}
-+
-+static void nss_getlistby_done(struct tevent_req *subreq)
-+{
-+    struct cache_req_result **results;
-+    struct nss_cmd_ctx *cmd_ctx;
-+    errno_t ret;
-+    struct cli_protocol *pctx;
-+
-+    cmd_ctx = tevent_req_callback_data(subreq, struct nss_cmd_ctx);
-+
-+    ret = cache_req_recv(cmd_ctx, subreq, &results);
-+    talloc_zfree(subreq);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_OP_FAILURE, "cache_req_user_by_cert request failed.\n");
-+        goto done;
-+    }
-+
-+    pctx = talloc_get_type(cmd_ctx->cli_ctx->protocol_ctx, struct cli_protocol);
-+
-+    ret = sss_packet_new(pctx->creq, 0, sss_packet_get_cmd(pctx->creq->in),
-+                         &pctx->creq->out);
-+    if (ret != EOK) {
-+        goto done;
-+    }
-+
-+    ret = nss_protocol_fill_name_list_all_domains(cmd_ctx->nss_ctx, cmd_ctx,
-+                                                  pctx->creq->out, results);
-+    if (ret != EOK) {
-+        goto done;
-+    }
-+
-+    sss_packet_set_error(pctx->creq->out, EOK);
-+
-+done:
-+    nss_protocol_done(cmd_ctx->cli_ctx, ret);
-+    talloc_free(cmd_ctx);
-+}
-+
- static errno_t nss_getby_cert(struct cli_ctx *cli_ctx,
-                               enum cache_req_type type,
-                               nss_protocol_fill_packet_fn fill_fn)
-@@ -934,8 +1018,7 @@ static errno_t nss_cmd_getnamebycert(struct cli_ctx *cli_ctx)
- 
- static errno_t nss_cmd_getlistbycert(struct cli_ctx *cli_ctx)
- {
--    return nss_getby_cert(cli_ctx, CACHE_REQ_USER_BY_CERT,
--                          nss_protocol_fill_name_list);
-+    return nss_getlistby_cert(cli_ctx, CACHE_REQ_USER_BY_CERT);
- }
- 
- struct sss_cmd_table *get_nss_cmds(void)
-diff --git a/src/responder/nss/nss_protocol.h b/src/responder/nss/nss_protocol.h
-index e4c0e52c0e642e885ef2c8423ea564beff7242cf..417b0891615dcb8771d49f7b2f4d276342ca3150 100644
---- a/src/responder/nss/nss_protocol.h
-+++ b/src/responder/nss/nss_protocol.h
-@@ -181,6 +181,12 @@ nss_protocol_fill_name_list(struct nss_ctx *nss_ctx,
-                             struct cache_req_result *result);
- 
- errno_t
-+nss_protocol_fill_name_list_all_domains(struct nss_ctx *nss_ctx,
-+                                        struct nss_cmd_ctx *cmd_ctx,
-+                                        struct sss_packet *packet,
-+                                        struct cache_req_result **results);
-+
-+errno_t
- nss_protocol_fill_id(struct nss_ctx *nss_ctx,
-                      struct nss_cmd_ctx *cmd_ctx,
-                      struct sss_packet *packet,
-diff --git a/src/responder/nss/nss_protocol_sid.c b/src/responder/nss/nss_protocol_sid.c
-index d4b7ee22d7c68a9e6f7c668f7268cdc5f36768b3..61357c2bf92e2f15d978b64a15ad5bd5aa354445 100644
---- a/src/responder/nss/nss_protocol_sid.c
-+++ b/src/responder/nss/nss_protocol_sid.c
-@@ -561,3 +561,81 @@ nss_protocol_fill_name_list(struct nss_ctx *nss_ctx,
- 
-     return EOK;
- }
-+
-+errno_t
-+nss_protocol_fill_name_list_all_domains(struct nss_ctx *nss_ctx,
-+                                        struct nss_cmd_ctx *cmd_ctx,
-+                                        struct sss_packet *packet,
-+                                        struct cache_req_result **results)
-+{
-+    enum sss_id_type *id_types;
-+    size_t rp = 0;
-+    size_t body_len;
-+    uint8_t *body;
-+    errno_t ret;
-+    struct sized_string *sz_names;
-+    size_t len;
-+    size_t c;
-+    const char *tmp_str;
-+    size_t d;
-+    size_t total = 0;
-+    size_t iter = 0;
-+
-+    if (results == NULL) {
-+        return EINVAL;
-+    }
-+
-+    for (d = 0; results[d] != NULL; d++) {
-+        total += results[d]->count;
-+    }
-+
-+    sz_names = talloc_array(cmd_ctx, struct sized_string, total);
-+    if (sz_names == NULL) {
-+        return ENOMEM;
-+    }
-+
-+    id_types = talloc_array(cmd_ctx, enum sss_id_type, total);
-+    if (id_types == NULL) {
-+        return ENOMEM;
-+    }
-+
-+    len = 0;
-+    for (d = 0; results[d] != NULL; d++) {
-+        for (c = 0; c < results[d]->count; c++) {
-+            ret = nss_get_id_type(cmd_ctx, results[d], &(id_types[iter]));
-+            if (ret != EOK) {
-+                return ret;
-+            }
-+
-+            tmp_str = sss_get_name_from_msg(results[d]->domain,
-+                                            results[d]->msgs[c]);
-+            if (tmp_str == NULL) {
-+                return EINVAL;
-+            }
-+            to_sized_string(&(sz_names[iter]), tmp_str);
-+
-+            len += sz_names[iter].len;
-+            iter++;
-+        }
-+    }
-+
-+    len += (2 + total) * sizeof(uint32_t);
-+
-+    ret = sss_packet_grow(packet, len);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_OP_FAILURE, "sss_packet_grow failed.\n");
-+        return ret;
-+    }
-+
-+    sss_packet_get_body(packet, &body, &body_len);
-+
-+    SAFEALIGN_SET_UINT32(&body[rp], total, &rp); /* Num results. */
-+    SAFEALIGN_SET_UINT32(&body[rp], 0, &rp); /* Reserved. */
-+    for (c = 0; c < total; c++) {
-+        SAFEALIGN_SET_UINT32(&body[rp], id_types[c], &rp);
-+        SAFEALIGN_SET_STRING(&body[rp], sz_names[c].str, sz_names[c].len,
-+                             &rp);
-+    }
-+
-+    return EOK;
-+}
-diff --git a/src/tests/cmocka/test_nss_srv.c b/src/tests/cmocka/test_nss_srv.c
-index 8c72f44f1869558893627e1f2f91b5f3b96c6317..03b5bcc302322551a32f5b8cfe4b7698947abbe7 100644
---- a/src/tests/cmocka/test_nss_srv.c
-+++ b/src/tests/cmocka/test_nss_srv.c
-@@ -3808,7 +3808,8 @@ static int test_nss_getnamebycert_check(uint32_t status, uint8_t *body, size_t b
-     return EOK;
- }
- 
--static int test_nss_getlistbycert_check(uint32_t status, uint8_t *body, size_t blen)
-+static int test_nss_getlistbycert_check_exp(uint32_t status, uint8_t *body,
-+                                            size_t blen, size_t exp)
- {
-     size_t rp = 0;
-     uint32_t id_type;
-@@ -3817,13 +3818,13 @@ static int test_nss_getlistbycert_check(uint32_t status, uint8_t *body, size_t b
-     const char *name;
-     int found = 0;
-     const char *fq_name1 = "testcertuser@"TEST_DOM_NAME ;
--    const char *fq_name2 = "testcertuser2@"TEST_DOM_NAME;
-+    const char *fq_name2 = "testcertuser2@"TEST_SUBDOM_NAME;
- 
-     assert_int_equal(status, EOK);
- 
-     /* num_results and reserved */
-     SAFEALIGN_COPY_UINT32(&num, body + rp, &rp);
--    assert_in_range(num, 1, 2);
-+    assert_int_equal(num, exp);
-     SAFEALIGN_COPY_UINT32(&reserved, body + rp, &rp);
-     assert_int_equal(reserved, 0);
- 
-@@ -3858,6 +3859,17 @@ static int test_nss_getlistbycert_check(uint32_t status, uint8_t *body, size_t b
-     return EOK;
- }
- 
-+static int test_nss_getlistbycert_check_one(uint32_t status, uint8_t *body,
-+                                            size_t blen)
-+{
-+    return test_nss_getlistbycert_check_exp(status, body, blen, 1);
-+}
-+
-+static int test_nss_getlistbycert_check_two(uint32_t status, uint8_t *body,
-+                                            size_t blen)
-+{
-+    return test_nss_getlistbycert_check_exp(status, body, blen, 2);
-+}
- 
- static void test_nss_getnamebycert(void **state)
- {
-@@ -3949,7 +3961,7 @@ static void test_nss_getlistbycert(void **state)
-     der = sss_base64_decode(nss_test_ctx, TEST_TOKEN_CERT, &der_size);
-     assert_non_null(der);
- 
--    ret = sysdb_attrs_add_mem(attrs, SYSDB_USER_CERT, der, der_size);
-+    ret = sysdb_attrs_add_mem(attrs, SYSDB_USER_MAPPED_CERT, der, der_size);
-     talloc_free(der);
-     assert_int_equal(ret, EOK);
- 
-@@ -3967,7 +3979,7 @@ static void test_nss_getlistbycert(void **state)
-     /* Should go straight to back end, without contacting DP. */
-     /* If there is only a single user mapped the result will look like the */
-     /* result of getnamebycert. */
--    set_cmd_cb(test_nss_getlistbycert_check);
-+    set_cmd_cb(test_nss_getlistbycert_check_one);
-     ret = sss_cmd_execute(nss_test_ctx->cctx, SSS_NSS_GETLISTBYCERT,
-                           nss_test_ctx->nss_cmds);
-     assert_int_equal(ret, EOK);
-@@ -3990,7 +4002,7 @@ static void test_nss_getlistbycert_multi(void **state)
-     attrs = sysdb_new_attrs(nss_test_ctx);
-     assert_non_null(attrs);
- 
--    ret = sysdb_attrs_add_mem(attrs, SYSDB_USER_CERT, der, der_size);
-+    ret = sysdb_attrs_add_mem(attrs, SYSDB_USER_MAPPED_CERT, der, der_size);
-     assert_int_equal(ret, EOK);
- 
-     /* Prime the cache with two valid user */
-@@ -4004,11 +4016,11 @@ static void test_nss_getlistbycert_multi(void **state)
-     attrs = sysdb_new_attrs(nss_test_ctx);
-     assert_non_null(attrs);
- 
--    ret = sysdb_attrs_add_mem(attrs, SYSDB_USER_CERT, der, der_size);
-+    ret = sysdb_attrs_add_mem(attrs, SYSDB_USER_MAPPED_CERT, der, der_size);
-     talloc_free(der);
-     assert_int_equal(ret, EOK);
- 
--    ret = store_user(nss_test_ctx, nss_test_ctx->tctx->dom,
-+    ret = store_user(nss_test_ctx, nss_test_ctx->subdom,
-                      &testbycert2, attrs, 0);
-     assert_int_equal(ret, EOK);
-     talloc_free(attrs);
-@@ -4019,7 +4031,7 @@ static void test_nss_getlistbycert_multi(void **state)
- 
-     /* Query for that user, call a callback when command finishes */
-     /* Should go straight to back end, without contacting DP */
--    set_cmd_cb(test_nss_getlistbycert_check);
-+    set_cmd_cb(test_nss_getlistbycert_check_two);
-     ret = sss_cmd_execute(nss_test_ctx->cctx, SSS_NSS_GETLISTBYCERT,
-                           nss_test_ctx->nss_cmds);
-     assert_int_equal(ret, EOK);
-@@ -4290,7 +4302,8 @@ int main(int argc, const char *argv[])
-         cmocka_unit_test_setup_teardown(test_nss_getlistbycert,
-                                         nss_test_setup, nss_test_teardown),
-         cmocka_unit_test_setup_teardown(test_nss_getlistbycert_multi,
--                                        nss_test_setup, nss_test_teardown),
-+                                        nss_subdom_test_setup,
-+                                        nss_subdom_test_teardown),
-         cmocka_unit_test_setup_teardown(test_nss_getsidbyname,
-                                         nss_test_setup, nss_test_teardown),
-         cmocka_unit_test_setup_teardown(test_nss_getsidbyupn,
--- 
-2.9.3
-
diff --git a/SOURCES/0129-CACHE_REQ-Avoid-using-of-uninitialized-value.patch b/SOURCES/0129-CACHE_REQ-Avoid-using-of-uninitialized-value.patch
deleted file mode 100644
index d11d711..0000000
--- a/SOURCES/0129-CACHE_REQ-Avoid-using-of-uninitialized-value.patch
+++ /dev/null
@@ -1,68 +0,0 @@
-From 6a1da829eaa1eee3e854f0cadc0b6effff776ab4 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Fabiano=20Fid=C3=AAncio?= <fidencio@redhat.com>
-Date: Mon, 15 May 2017 11:54:00 +0200
-Subject: [PATCH 1/2] CACHE_REQ: Avoid using of uninitialized value
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Commit 4ef0b19a introduced the following warning, as "req" may be used
-without being initialized:
-src/responder/common/cache_req/cache_req_search.c:
-     In function 'cache_req_search_done':
-src/responder/common/cache_req/cache_req_search.c:467:9:
-     error: 'req' may be used uninitialized in this function
-     [-Werror=maybe-uninitialized]
-         tevent_req_error(req, ret);
-         ^
-src/responder/common/cache_req/cache_req_search.c:424:24:
-     note: 'req' was declared here
-     struct tevent_req *req;
-                        ^
-cc1: all warnings being treated as errors
-
-In order to fix the issue above, let's just allocate tmp_ctx after "req"
-is already set.
-
-Related:
-https://pagure.io/SSSD/sssd/issue/3362
-
-Signed-off-by: Fabiano Fidêncio <fidencio@redhat.com>
-Co-Author: Lukáš Slebodník <lslebodn@redhat.com>
-Reviewed-by: Sumit Bose <sbose@redhat.com>
----
- src/responder/common/cache_req/cache_req_search.c | 12 ++++++------
- 1 file changed, 6 insertions(+), 6 deletions(-)
-
-diff --git a/src/responder/common/cache_req/cache_req_search.c b/src/responder/common/cache_req/cache_req_search.c
-index 793dbc5042ae329b2cade5d1eb5a6d41102e264f..70448a7639bc9f98d380b8edce9d130adfa0ceb2 100644
---- a/src/responder/common/cache_req/cache_req_search.c
-+++ b/src/responder/common/cache_req/cache_req_search.c
-@@ -425,18 +425,18 @@ static void cache_req_search_done(struct tevent_req *subreq)
-     struct ldb_result *result = NULL;
-     errno_t ret;
- 
--    tmp_ctx = talloc_new(NULL);
--    if (tmp_ctx == NULL) {
--        ret = ENOMEM;
--        goto done;
--    }
--
-     req = tevent_req_callback_data(subreq, struct tevent_req);
-     state = tevent_req_data(req, struct cache_req_search_state);
- 
-     state->dp_success = state->cr->plugin->dp_recv_fn(subreq, state->cr);
-     talloc_zfree(subreq);
- 
-+    tmp_ctx = talloc_new(NULL);
-+    if (tmp_ctx == NULL) {
-+        ret = ENOMEM;
-+        goto done;
-+    }
-+
-     /* Get result from cache again. */
-     ret = cache_req_search_cache(tmp_ctx, state->cr, &result);
-     if (ret != EOK) {
--- 
-2.9.3
-
diff --git a/SOURCES/0130-CACHE_REQ-Ensure-the-domains-are-updated-for-filter-.patch b/SOURCES/0130-CACHE_REQ-Ensure-the-domains-are-updated-for-filter-.patch
deleted file mode 100644
index 1063c09..0000000
--- a/SOURCES/0130-CACHE_REQ-Ensure-the-domains-are-updated-for-filter-.patch
+++ /dev/null
@@ -1,163 +0,0 @@
-From 1a89fc33d1b9b1070c7ab83fb0314e538ac46736 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Fabiano=20Fid=C3=AAncio?= <fidencio@redhat.com>
-Date: Wed, 3 May 2017 13:24:40 +0200
-Subject: [PATCH 2/2] CACHE_REQ: Ensure the domains are updated for "filter"
- related calls
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-As contacting the infopipe responder on a "filter" related call may lead
-to the situation where the cr_domains' list is not populated yet (as the
-domains and subdomains lists from the data provider are not processed
-yet), let's explicitly call sss_dp_get_domains() for those cases and
-avoid returning a wrong result to the caller.
-
-This situation may happen only because the schedule_get_domains_task(),
-that's called when the infopipe responder is initialized, may take some
-time to run/finish.
-
-While I'm not exactly sure whether it's the best solution to avoid the
-"race", it seems to be sane enough to avoid the issues.
-
-Resolves:
-https://pagure.io/SSSD/sssd/issue/3387
-
-Signed-off-by: Fabiano Fidêncio <fidencio@redhat.com>
-Reviewed-by: Sumit Bose <sbose@redhat.com>
-Reviewed-by: Pavel Březina <pbrezina@redhat.com>
----
- src/responder/common/cache_req/cache_req.c | 86 ++++++++++++++++++++++++++++--
- 1 file changed, 81 insertions(+), 5 deletions(-)
-
-diff --git a/src/responder/common/cache_req/cache_req.c b/src/responder/common/cache_req/cache_req.c
-index 797325a30e6c1ed5f1d4b4c147c65391d5204b52..7d77eb7dd72a7ccf3d687eee8f746ab84176b487 100644
---- a/src/responder/common/cache_req/cache_req.c
-+++ b/src/responder/common/cache_req/cache_req.c
-@@ -698,6 +698,13 @@ static errno_t cache_req_process_input(TALLOC_CTX *mem_ctx,
-                                        struct cache_req *cr,
-                                        const char *domain);
- 
-+static errno_t cache_req_update_domains(TALLOC_CTX *mem_ctx,
-+                                        struct tevent_req *req,
-+                                        struct cache_req *cr,
-+                                        const char *domain);
-+
-+static void cache_req_domains_updated(struct tevent_req *subreq);
-+
- static void cache_req_input_parsed(struct tevent_req *subreq);
- 
- static errno_t cache_req_select_domains(struct tevent_req *req,
-@@ -753,13 +760,13 @@ struct tevent_req *cache_req_send(TALLOC_CTX *mem_ctx,
-         goto done;
-     }
- 
-+    state->domain_name = domain;
-     ret = cache_req_process_input(state, req, cr, domain);
-     if (ret != EOK) {
-         goto done;
-     }
- 
--    state->domain_name = domain;
--    ret = cache_req_select_domains(req, domain);
-+    ret = cache_req_select_domains(req, state->domain_name);
- 
- done:
-     if (ret == EOK) {
-@@ -780,14 +787,25 @@ static errno_t cache_req_process_input(TALLOC_CTX *mem_ctx,
- {
-     struct tevent_req *subreq;
-     const char *default_domain;
-+    errno_t ret;
- 
-     if (cr->data->name.input == NULL) {
--        /* Input was not name, there is no need to process it further. */
--        return EOK;
-+        /* Call cache_req_update_domains() in order to get a up to date list
-+         * of domains and subdomains, if needed. Otherwise just return EOK as
-+         * the input was not a name, thus there's no need to process it
-+         * further. */
-+        return cache_req_update_domains(mem_ctx, req, cr, domain);
-     }
- 
-     if (cr->plugin->parse_name == false || domain != NULL) {
--        /* We do not want to parse the name. */
-+        /* Call cache_req_update_domains() in order to get a up to date list
-+         * of domains and subdomains, if needed. Otherwise, just use the input
-+         * name as it is. */
-+        ret = cache_req_update_domains(mem_ctx, req, cr, domain);
-+        if (ret != EOK) {
-+            return ret;
-+        }
-+
-         return cache_req_set_name(cr, cr->data->name.input);
-     }
- 
-@@ -812,6 +830,64 @@ static errno_t cache_req_process_input(TALLOC_CTX *mem_ctx,
-     return EAGAIN;
- }
- 
-+static errno_t cache_req_update_domains(TALLOC_CTX *mem_ctx,
-+                                        struct tevent_req *req,
-+                                        struct cache_req *cr,
-+                                        const char *domain)
-+{
-+    struct tevent_req *subreq;
-+
-+    if (cr->rctx->get_domains_last_call.tv_sec != 0) {
-+        return EOK;
-+    }
-+
-+    subreq = sss_dp_get_domains_send(mem_ctx, cr->rctx, false, domain);
-+    if (subreq == NULL) {
-+        return ENOMEM;
-+    }
-+
-+    tevent_req_set_callback(subreq, cache_req_domains_updated, req);
-+    return EAGAIN;
-+}
-+
-+static void cache_req_domains_updated(struct tevent_req *subreq)
-+{
-+    struct tevent_req *req;
-+    struct cache_req_state *state;
-+    errno_t ret;
-+
-+    req = tevent_req_callback_data(subreq, struct tevent_req);
-+    state = tevent_req_data(req, struct cache_req_state);
-+
-+    ret = sss_dp_get_domains_recv(subreq);
-+    talloc_free(subreq);
-+    if (ret != EOK) {
-+        goto done;
-+    }
-+
-+    if (state->cr->data->name.input == NULL) {
-+        /* Input was not name, there is no need to process it further. */
-+        goto immediately;
-+    }
-+
-+    if (state->cr->plugin->parse_name == false || state->domain_name != NULL) {
-+        /* We do not want to parse the name. */
-+        ret = cache_req_set_name(state->cr, state->cr->data->name.input);
-+        if (ret != EOK) {
-+            goto done;
-+        }
-+    }
-+
-+immediately:
-+    ret = cache_req_select_domains(req, state->domain_name);
-+
-+done:
-+    if (ret != EOK && ret != EAGAIN) {
-+        tevent_req_error(req, ret);
-+        return;
-+    }
-+}
-+
- static void cache_req_input_parsed(struct tevent_req *subreq)
- {
-     struct tevent_req *req;
--- 
-2.9.3
-
diff --git a/SOURCES/0131-AD-SUBDOMAINS-Fix-search-bases-for-child-domains.patch b/SOURCES/0131-AD-SUBDOMAINS-Fix-search-bases-for-child-domains.patch
deleted file mode 100644
index 50c99ed..0000000
--- a/SOURCES/0131-AD-SUBDOMAINS-Fix-search-bases-for-child-domains.patch
+++ /dev/null
@@ -1,102 +0,0 @@
-From f994343e9ffc8f8d2917678ae61bcdf68c316a20 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Michal=20=C5=BDidek?= <mzidek@redhat.com>
-Date: Tue, 9 May 2017 11:21:02 +0200
-Subject: [PATCH 131/131] AD SUBDOMAINS: Fix search bases for child domains
-
-When using direct AD integration, child domains did not respect
-the sssd.conf configuration of search bases.
-
-There were few issues all of which are fixed in this small
-patch.
-
-First problem was that the sdap domain list was not properly
-inherited from the parent in the child domains and the children
-always created their own sdap domains lists that were disconnected
-from the parent context and never used.
-
-Second issue was that the child domain did not call the function
-to reinit the search bases after the sdap_domain was added to the
-list of sdap domains. This caused that child domains always used
-automatically detected search bases and never used the configured
-ones even though they were properly read into the ID options
-context attached to the subdomain.
-
-Also there has been an issue that the sdap search bases
-were rewritten by the new child domain initialization
-(this only happened with more than one child domain)
-because the sdap domain list was 'updated' every time
-a new child domain was initialized, which caused that
-only the main domain and the last child domain had proper
-search bases, the others only the auto-discovered ones
-(because they were overwritten with the 'update').
-
-Resolves:
-https://pagure.io/SSSD/sssd/issue/3397
-
-Reviewed-by: Sumit Bose <sbose@redhat.com>
----
- src/providers/ad/ad_subdomains.c | 17 +++++++++++++++++
- src/providers/ldap/sdap_domain.c |  5 +++++
- 2 files changed, 22 insertions(+)
-
-diff --git a/src/providers/ad/ad_subdomains.c b/src/providers/ad/ad_subdomains.c
-index ef166446e837c3f7cd824c1abf4b5cc587aec9da..c9b79dd9d6840802cddc067eef9d5110cf8d0778 100644
---- a/src/providers/ad/ad_subdomains.c
-+++ b/src/providers/ad/ad_subdomains.c
-@@ -221,6 +221,9 @@ ad_subdom_ad_ctx_new(struct be_ctx *be_ctx,
-     ad_id_ctx->sdap_id_ctx->opts = ad_options->id;
-     ad_options->id_ctx = ad_id_ctx;
- 
-+    /* We need to pass the sdap list from parent */
-+    ad_id_ctx->sdap_id_ctx->opts->sdom = id_ctx->sdap_id_ctx->opts->sdom;
-+
-     /* use AD plugin */
-     srv_ctx = ad_srv_plugin_ctx_init(be_ctx, be_ctx->be_res,
-                                      default_host_dbs,
-@@ -257,6 +260,13 @@ ad_subdom_ad_ctx_new(struct be_ctx *be_ctx,
-     ad_id_ctx->sdap_id_ctx->opts->idmap_ctx =
-         id_ctx->sdap_id_ctx->opts->idmap_ctx;
- 
-+    ret = ad_set_search_bases(ad_options->id, sdom);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_MINOR_FAILURE, "Failed to set LDAP search bases for "
-+              "domain '%s'. Will try to use automatically detected search "
-+              "bases.", subdom->name);
-+    }
-+
-     *_subdom_id_ctx = ad_id_ctx;
-     return EOK;
- }
-@@ -621,6 +631,13 @@ ads_store_sdap_subdom(struct ad_subdomains_ctx *ctx,
-         return ret;
-     }
- 
-+    ret = ad_set_search_bases(ctx->ad_id_ctx->ad_options->id, ctx->sdom);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_MINOR_FAILURE, "failed to set ldap search bases for "
-+              "domain '%s'. will try to use automatically detected search "
-+              "bases.", ctx->sdom->dom->name);
-+    }
-+
-     DLIST_FOR_EACH(sditer, ctx->sdom) {
-         if (IS_SUBDOMAIN(sditer->dom) && sditer->pvt == NULL) {
-             ret = ad_subdom_ad_ctx_new(ctx->be_ctx, ctx->ad_id_ctx,
-diff --git a/src/providers/ldap/sdap_domain.c b/src/providers/ldap/sdap_domain.c
-index 5cba9df0fd5fb320a57adc39093283aed865f57f..d384b2e4a0ec3a7c8d0b05e0ce735feb2189085f 100644
---- a/src/providers/ldap/sdap_domain.c
-+++ b/src/providers/ldap/sdap_domain.c
-@@ -154,6 +154,11 @@ sdap_domain_subdom_add(struct sdap_id_ctx *sdap_id_ctx,
-                     parent->name, ret, strerror(ret));
-                 return ret;
-             }
-+        } else if (sditer->search_bases != NULL) {
-+            DEBUG(SSSDBG_TRACE_FUNC,
-+                  "subdomain %s has already initialized search bases\n",
-+                  dom->name);
-+            continue;
-         } else {
-             sdom = sditer;
-         }
--- 
-2.9.3
-
diff --git a/SOURCES/0132-KRB5-Advise-the-user-to-inspect-the-krb5_child.log-i.patch b/SOURCES/0132-KRB5-Advise-the-user-to-inspect-the-krb5_child.log-i.patch
deleted file mode 100644
index 7eecef9..0000000
--- a/SOURCES/0132-KRB5-Advise-the-user-to-inspect-the-krb5_child.log-i.patch
+++ /dev/null
@@ -1,39 +0,0 @@
-From cf1bb5464609f5873685406f9e09e43de8738e42 Mon Sep 17 00:00:00 2001
-From: Jakub Hrozek <jhrozek@redhat.com>
-Date: Mon, 22 May 2017 09:55:12 +0200
-Subject: [PATCH 132/135] KRB5: Advise the user to inspect the krb5_child.log
- if the child doesn't return a valid response
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-If the child returns a runtime error, it is often not clear from the
-domain debug logs what to do next. This patch adds a DEBUG message that
-tells the admin to look into the krb5_child.log
-
-Resolves:
-https://pagure.io/SSSD/sssd/issue/2955
-
-Reviewed-by: Pavel Březina <pbrezina@redhat.com>
-(cherry picked from commit 7410f735b64937e0c2401c09b5cffc9c78b11849)
----
- src/providers/krb5/krb5_auth.c | 3 +++
- 1 file changed, 3 insertions(+)
-
-diff --git a/src/providers/krb5/krb5_auth.c b/src/providers/krb5/krb5_auth.c
-index 2faf18d17a735476c20f9cc27b15be4a39cadc5c..894bd41bde031ac33187bfa3b14202e9429a9198 100644
---- a/src/providers/krb5/krb5_auth.c
-+++ b/src/providers/krb5/krb5_auth.c
-@@ -890,6 +890,9 @@ static void krb5_auth_done(struct tevent_req *subreq)
-                         state->be_ctx->domain->pwd_expiration_warning,
-                         &res);
-     if (ret) {
-+        DEBUG(SSSDBG_IMPORTANT_INFO,
-+              "The krb5_child process returned an error. Please inspect the "
-+              "krb5_child.log file or the journal for more information\n");
-         DEBUG(SSSDBG_OP_FAILURE, "Could not parse child response [%d]: %s\n",
-               ret, strerror(ret));
-         goto done;
--- 
-2.9.3
-
diff --git a/SOURCES/0133-cache_req-use-the-right-negative-cache-for-initgroup.patch b/SOURCES/0133-cache_req-use-the-right-negative-cache-for-initgroup.patch
deleted file mode 100644
index 52aa5dc..0000000
--- a/SOURCES/0133-cache_req-use-the-right-negative-cache-for-initgroup.patch
+++ /dev/null
@@ -1,40 +0,0 @@
-From 04ef28b7cc49a71209551646b3a82518506f40a6 Mon Sep 17 00:00:00 2001
-From: Sumit Bose <sbose@redhat.com>
-Date: Mon, 22 May 2017 14:58:01 +0200
-Subject: [PATCH 133/135] cache_req: use the right negative cache for
- initgroups by upn
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Reviewed-by: Fabiano Fidêncio <fidencio@redhat.com>
-(cherry picked from commit 870b58a6cc6b5cf92a6503c1578e5c21617c8d40)
----
- src/responder/common/cache_req/plugins/cache_req_initgroups_by_upn.c | 4 ++--
- 1 file changed, 2 insertions(+), 2 deletions(-)
-
-diff --git a/src/responder/common/cache_req/plugins/cache_req_initgroups_by_upn.c b/src/responder/common/cache_req/plugins/cache_req_initgroups_by_upn.c
-index b6fb43ee02d2f041fb3d992b375ae65a02db8b03..dfb21ac1a0090a3ef9029b38f5b1e8bdda3440c6 100644
---- a/src/responder/common/cache_req/plugins/cache_req_initgroups_by_upn.c
-+++ b/src/responder/common/cache_req/plugins/cache_req_initgroups_by_upn.c
-@@ -66,7 +66,7 @@ cache_req_initgroups_by_upn_ncache_check(struct sss_nc_ctx *ncache,
-                                          struct sss_domain_info *domain,
-                                          struct cache_req_data *data)
- {
--    return sss_ncache_check_user(ncache, domain, data->name.lookup);
-+    return sss_ncache_check_upn(ncache, domain, data->name.lookup);
- }
- 
- static errno_t
-@@ -74,7 +74,7 @@ cache_req_initgroups_by_upn_ncache_add(struct sss_nc_ctx *ncache,
-                                        struct sss_domain_info *domain,
-                                        struct cache_req_data *data)
- {
--    return sss_ncache_set_user(ncache, false, domain, data->name.lookup);
-+    return sss_ncache_set_upn(ncache, false, domain, data->name.lookup);
- }
- 
- static errno_t
--- 
-2.9.3
-
diff --git a/SOURCES/0134-test-make-sure-p11_child-is-build-for-pam-srv-tests.patch b/SOURCES/0134-test-make-sure-p11_child-is-build-for-pam-srv-tests.patch
deleted file mode 100644
index 5fa1c16..0000000
--- a/SOURCES/0134-test-make-sure-p11_child-is-build-for-pam-srv-tests.patch
+++ /dev/null
@@ -1,31 +0,0 @@
-From 9e7bb71e02af7cf8fe8b593ddc762a09183ff32c Mon Sep 17 00:00:00 2001
-From: Sumit Bose <sbose@redhat.com>
-Date: Mon, 22 May 2017 15:04:17 +0200
-Subject: [PATCH 134/135] test: make sure p11_child is build for pam-srv-tests
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Reviewed-by: Fabiano Fidêncio <fidencio@redhat.com>
-(cherry picked from commit ec9ac22d699a17d590b1d4ba9ba3750eb719f340)
----
- Makefile.am | 3 +++
- 1 file changed, 3 insertions(+)
-
-diff --git a/Makefile.am b/Makefile.am
-index 370d6442ec58a14946ad288a23c696f25ca98f47..a6279133b56dcd5bcbd1306ae8f2ce18d90c2c12 100644
---- a/Makefile.am
-+++ b/Makefile.am
-@@ -2356,6 +2356,9 @@ nss_srv_tests_LDADD = \
- EXTRA_pam_srv_tests_DEPENDENCIES = \
-     $(ldblib_LTLIBRARIES) \
-     $(NULL)
-+if HAVE_NSS
-+EXTRA_pam_srv_tests_DEPENDENCIES += p11_child
-+endif
- pam_srv_tests_SOURCES = \
-     $(TEST_MOCK_RESP_OBJ) \
-     src/tests/cmocka/test_pam_srv.c \
--- 
-2.9.3
-
diff --git a/SOURCES/0135-pam-properly-support-UPN-logon-names.patch b/SOURCES/0135-pam-properly-support-UPN-logon-names.patch
deleted file mode 100644
index 54d6529..0000000
--- a/SOURCES/0135-pam-properly-support-UPN-logon-names.patch
+++ /dev/null
@@ -1,163 +0,0 @@
-From b6ba8bc43a0b5a448e4cd30bc406bd08c389e365 Mon Sep 17 00:00:00 2001
-From: Sumit Bose <sbose@redhat.com>
-Date: Fri, 12 May 2017 10:40:21 +0200
-Subject: [PATCH 135/135] pam: properly support UPN logon names
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Many logon applications like /bin/login or sshd canonicalize the user
-name before they call pam_start() and hence the UPN is not seen by
-SSSD's pam responder. But some like e.g. gdm don't and authentication
-might fail if a UPN is used.
-
-The reason is that currently the already parsed short name of the user
-was used in the cache_req and hence the cache_req was not able to fall
-back to the UPN lookup code. This patch uses the name originally
-provided by the user as input to allow the fallback to the UPN lookup.
-
-Resolves https://pagure.io/SSSD/sssd/issue/3240
-
-Reviewed-by: Fabiano Fidêncio <fidencio@redhat.com>
-(cherry picked from commit 29d063505c07127f7747405b1a61d8f782673645)
----
- src/responder/pam/pamsrv_cmd.c  |  4 +--
- src/tests/cmocka/test_pam_srv.c | 79 ++++++++++++++++++++++++++++++++++++++++-
- 2 files changed, 80 insertions(+), 3 deletions(-)
-
-diff --git a/src/responder/pam/pamsrv_cmd.c b/src/responder/pam/pamsrv_cmd.c
-index 10a178f839ec011c09a6da4575efbb026f3f7700..36dba37964b71153435b4df5d5328de4361926e6 100644
---- a/src/responder/pam/pamsrv_cmd.c
-+++ b/src/responder/pam/pamsrv_cmd.c
-@@ -1560,7 +1560,7 @@ static int pam_check_user_search(struct pam_auth_req *preq)
- 
-     data = cache_req_data_name(preq,
-                                CACHE_REQ_INITGROUPS,
--                               preq->pd->user);
-+                               preq->pd->logon_name);
-     if (data == NULL) {
-         return ENOMEM;
-     }
-@@ -1589,7 +1589,7 @@ static int pam_check_user_search(struct pam_auth_req *preq)
-                            preq->cctx->rctx->ncache,
-                            0,
-                            preq->req_dom_type,
--                           preq->pd->domain,
-+                           NULL,
-                            data);
-     if (!dpreq) {
-         DEBUG(SSSDBG_CRIT_FAILURE,
-diff --git a/src/tests/cmocka/test_pam_srv.c b/src/tests/cmocka/test_pam_srv.c
-index d249b8f1ea48f1c17b461c3add9e8c63774e5f88..4d351a3707d2a49604595b728fff7705560c871a 100644
---- a/src/tests/cmocka/test_pam_srv.c
-+++ b/src/tests/cmocka/test_pam_srv.c
-@@ -518,6 +518,8 @@ static void mock_input_pam_ex(TALLOC_CTX *mem_ctx,
-     int ret;
-     size_t needed_size;
-     uint8_t *authtok;
-+    char *s_name;
-+    char *dom;
- 
-     if (name != NULL) {
-         pi.pam_user = name;
-@@ -574,7 +576,13 @@ static void mock_input_pam_ex(TALLOC_CTX *mem_ctx,
-     will_return(__wrap_sss_packet_get_body, buf);
-     will_return(__wrap_sss_packet_get_body, buf_size);
- 
--    mock_parse_inp(name, NULL, EOK);
-+    if (strrchr(name, '@') == NULL) {
-+        mock_parse_inp(name, NULL, EOK);
-+    } else {
-+        ret = sss_parse_internal_fqname(mem_ctx, name, &s_name, &dom);
-+        mock_parse_inp(s_name, dom, EOK);
-+    }
-+
-     if (contact_dp) {
-         mock_account_recv_simple();
-     }
-@@ -1582,6 +1590,71 @@ void test_pam_preauth_no_logon_name(void **state)
-     assert_int_equal(ret, EOK);
- }
- 
-+void test_pam_auth_no_upn_logon_name(void **state)
-+{
-+    int ret;
-+
-+    ret = sysdb_cache_password(pam_test_ctx->tctx->dom,
-+                               pam_test_ctx->pam_user_fqdn,
-+                               "12345");
-+    assert_int_equal(ret, EOK);
-+
-+    mock_input_pam_ex(pam_test_ctx, "upn@"TEST_DOM_NAME, "12345", NULL, NULL,
-+                      true);
-+    mock_account_recv_simple();
-+
-+    will_return(__wrap_sss_packet_get_cmd, SSS_PAM_AUTHENTICATE);
-+    will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL);
-+
-+    pam_test_ctx->exp_pam_status = PAM_USER_UNKNOWN;
-+    set_cmd_cb(test_pam_simple_check);
-+    ret = sss_cmd_execute(pam_test_ctx->cctx, SSS_PAM_AUTHENTICATE,
-+                          pam_test_ctx->pam_cmds);
-+    assert_int_equal(ret, EOK);
-+
-+    /* Wait until the test finishes with EOK */
-+    ret = test_ev_loop(pam_test_ctx->tctx);
-+    assert_int_equal(ret, EOK);
-+}
-+
-+void test_pam_auth_upn_logon_name(void **state)
-+{
-+    int ret;
-+    struct sysdb_attrs *attrs;
-+
-+    ret = sysdb_cache_password(pam_test_ctx->tctx->dom,
-+                               pam_test_ctx->pam_user_fqdn,
-+                               "12345");
-+    assert_int_equal(ret, EOK);
-+    attrs = sysdb_new_attrs(pam_test_ctx);
-+    assert_non_null(attrs);
-+    ret = sysdb_attrs_add_string(attrs, SYSDB_UPN, "upn@"TEST_DOM_NAME);
-+    assert_int_equal(ret, EOK);
-+
-+    ret = sysdb_set_user_attr(pam_test_ctx->tctx->dom,
-+                              pam_test_ctx->pam_user_fqdn,
-+                              attrs,
-+                              LDB_FLAG_MOD_ADD);
-+    assert_int_equal(ret, EOK);
-+
-+    mock_input_pam_ex(pam_test_ctx, "upn@"TEST_DOM_NAME, "12345", NULL, NULL,
-+                      true);
-+    mock_account_recv_simple();
-+
-+    will_return(__wrap_sss_packet_get_cmd, SSS_PAM_AUTHENTICATE);
-+    will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL);
-+
-+    set_cmd_cb(test_pam_successful_offline_auth_check);
-+    ret = sss_cmd_execute(pam_test_ctx->cctx, SSS_PAM_AUTHENTICATE,
-+                          pam_test_ctx->pam_cmds);
-+    assert_int_equal(ret, EOK);
-+
-+    /* Wait until the test finishes with EOK */
-+    ret = test_ev_loop(pam_test_ctx->tctx);
-+    assert_int_equal(ret, EOK);
-+}
-+
-+
- static void set_cert_auth_param(struct pam_ctx *pctx, const char *dbpath)
- {
-     pam_test_ctx->pctx->cert_auth = true;
-@@ -2312,6 +2385,10 @@ int main(int argc, const char *argv[])
-                                         pam_test_setup, pam_test_teardown),
-         cmocka_unit_test_setup_teardown(test_pam_preauth_no_logon_name,
-                                         pam_test_setup, pam_test_teardown),
-+        cmocka_unit_test_setup_teardown(test_pam_auth_no_upn_logon_name,
-+                                        pam_test_setup, pam_test_teardown),
-+        cmocka_unit_test_setup_teardown(test_pam_auth_upn_logon_name,
-+                                        pam_test_setup, pam_test_teardown),
-         cmocka_unit_test_setup_teardown(test_pam_cached_auth_success,
-                                         pam_cached_test_setup,
-                                         pam_test_teardown),
--- 
-2.9.3
-
diff --git a/SOURCES/0136-KCM-Fix-the-per-client-serialization-queue.patch b/SOURCES/0136-KCM-Fix-the-per-client-serialization-queue.patch
deleted file mode 100644
index 3cf4420..0000000
--- a/SOURCES/0136-KCM-Fix-the-per-client-serialization-queue.patch
+++ /dev/null
@@ -1,334 +0,0 @@
-From b31f75f44a9e1dc0521ec73176f89e05db4973ba Mon Sep 17 00:00:00 2001
-From: Jakub Hrozek <jhrozek@redhat.com>
-Date: Thu, 11 May 2017 16:24:24 +0200
-Subject: [PATCH 136/138] KCM: Fix the per-client serialization queue
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Resolves:
-    https://pagure.io/SSSD/sssd/issue/3372
-
-Fixes a race condition between one client request adding an operation to
-the hash table value, which was previously a linked list of operations,
-while another concurrent operation would remove the last remaining
-linked list element through its callback.
-
-Instead, the hash table value is now a separate 'queue head' structure
-which is only changed in a tevent request to make sure is is not
-processes concurrently with adding to the queue (which is also a tevent
-request).
-
-Reviewed-by: Pavel Březina <pbrezina@redhat.com>
-(cherry picked from commit fb51bb68e62de7bb8542f5d224994eb7143040a6)
----
- src/responder/kcm/kcmsrv_op_queue.c | 182 ++++++++++++++++++++++++------------
- 1 file changed, 122 insertions(+), 60 deletions(-)
-
-diff --git a/src/responder/kcm/kcmsrv_op_queue.c b/src/responder/kcm/kcmsrv_op_queue.c
-index f6c425dd5b64877c8b7401e488dd6565157fc9b5..55c8b65d94f70979fe56fcc4d8747547a9cc9d33 100644
---- a/src/responder/kcm/kcmsrv_op_queue.c
-+++ b/src/responder/kcm/kcmsrv_op_queue.c
-@@ -27,17 +27,23 @@
- 
- struct kcm_ops_queue_entry {
-     struct tevent_req *req;
--    uid_t uid;
- 
--    hash_table_t *wait_queue_hash;
-+    struct kcm_ops_queue *queue;
- 
--    struct kcm_ops_queue_entry *head;
-     struct kcm_ops_queue_entry *next;
-     struct kcm_ops_queue_entry *prev;
- };
- 
-+struct kcm_ops_queue {
-+    uid_t uid;
-+    struct tevent_context *ev;
-+    struct kcm_ops_queue_ctx *qctx;
-+
-+    struct kcm_ops_queue_entry *head;
-+};
-+
- struct kcm_ops_queue_ctx {
--    /* UID: dlist of kcm_ops_queue_entry */
-+    /* UID:kcm_ops_queue */
-     hash_table_t *wait_queue_hash;
- };
- 
-@@ -45,8 +51,9 @@ struct kcm_ops_queue_ctx {
-  * Per-UID wait queue
-  *
-  * They key in the hash table is the UID of the peer. The value of each
-- * hash table entry is a linked list of kcm_ops_queue_entry structures
-- * which primarily hold the tevent request being queued.
-+ * hash table entry is kcm_ops_queue structure which in turn contains a
-+ * linked list of kcm_ops_queue_entry structures * which primarily hold the
-+ * tevent request being queued.
-  */
- struct kcm_ops_queue_ctx *kcm_ops_queue_create(TALLOC_CTX *mem_ctx)
- {
-@@ -71,11 +78,45 @@ struct kcm_ops_queue_ctx *kcm_ops_queue_create(TALLOC_CTX *mem_ctx)
-     return queue_ctx;
- }
- 
--static int kcm_op_queue_entry_destructor(struct kcm_ops_queue_entry *entry)
-+void queue_removal_cb(struct tevent_context *ctx,
-+                      struct tevent_immediate *imm,
-+                      void *private_data)
- {
-+    struct kcm_ops_queue *kq = talloc_get_type(private_data,
-+                                               struct kcm_ops_queue);
-     int ret;
-+    hash_key_t key;
-+
-+    talloc_free(imm);
-+
-+    if (kq->head != NULL) {
-+        DEBUG(SSSDBG_TRACE_LIBS, "The queue is no longer empty\n");
-+        return;
-+    }
-+
-+    key.type = HASH_KEY_ULONG;
-+    key.ul = kq->uid;
-+
-+    /* If this was the last entry, remove the key (the UID) from the
-+     * hash table to signal the queue is empty
-+     */
-+    ret = hash_delete(kq->qctx->wait_queue_hash, &key);
-+    if (ret != HASH_SUCCESS) {
-+        DEBUG(SSSDBG_CRIT_FAILURE,
-+              "Failed to remove wait queue for user %"SPRIuid"\n",
-+              kq->uid);
-+        return;
-+    }
-+
-+    DEBUG(SSSDBG_FUNC_DATA,
-+          "Removed queue for %"SPRIuid" \n", kq->uid);
-+    talloc_free(kq);
-+}
-+
-+static int kcm_op_queue_entry_destructor(struct kcm_ops_queue_entry *entry)
-+{
-     struct kcm_ops_queue_entry *next_entry;
--    hash_key_t key;
-+    struct tevent_immediate *imm;
- 
-     if (entry == NULL) {
-         return 1;
-@@ -85,22 +126,19 @@ static int kcm_op_queue_entry_destructor(struct kcm_ops_queue_entry *entry)
-     next_entry = entry->next;
- 
-     /* Remove the current entry from the queue */
--    DLIST_REMOVE(entry->head, entry);
-+    DLIST_REMOVE(entry->queue->head, entry);
- 
-     if (next_entry == NULL) {
--        key.type = HASH_KEY_ULONG;
--        key.ul = entry->uid;
--
--        /* If this was the last entry, remove the key (the UID) from the
--         * hash table to signal the queue is empty
-+        /* If there was no other entry, schedule removal of the queue. Do it
-+         * in another tevent tick to avoid issues with callbacks invoking
-+         * the descructor while another request is touching the queue
-          */
--        ret = hash_delete(entry->wait_queue_hash, &key);
--        if (ret != HASH_SUCCESS) {
--            DEBUG(SSSDBG_CRIT_FAILURE,
--                  "Failed to remove wait queue for user %"SPRIuid"\n",
--                  entry->uid);
-+        imm = tevent_create_immediate(entry->queue);
-+        if (imm == NULL) {
-             return 1;
-         }
-+
-+        tevent_schedule_immediate(imm, entry->queue->ev, queue_removal_cb, entry->queue);
-         return 0;
-     }
- 
-@@ -109,41 +147,33 @@ static int kcm_op_queue_entry_destructor(struct kcm_ops_queue_entry *entry)
-     return 0;
- }
- 
--static errno_t kcm_op_queue_add(hash_table_t *wait_queue_hash,
--                                struct kcm_ops_queue_entry *entry,
--                                uid_t uid)
-+static struct kcm_ops_queue *kcm_op_queue_get(struct kcm_ops_queue_ctx *qctx,
-+                                              struct tevent_context *ev,
-+                                              uid_t uid)
- {
-     errno_t ret;
-     hash_key_t key;
-     hash_value_t value;
--    struct kcm_ops_queue_entry *head = NULL;
-+    struct kcm_ops_queue *kq;
- 
-     key.type = HASH_KEY_ULONG;
-     key.ul = uid;
- 
--    ret = hash_lookup(wait_queue_hash, &key, &value);
-+    ret = hash_lookup(qctx->wait_queue_hash, &key, &value);
-     switch (ret) {
-     case HASH_SUCCESS:
--        /* The key with this UID already exists. Its value is request queue
--         * for the UID, so let's just add the current request to the end
--         * of the queue and wait for the previous requests to finish
--         */
-         if (value.type != HASH_VALUE_PTR) {
-             DEBUG(SSSDBG_CRIT_FAILURE, "Unexpected hash value type.\n");
--            return EINVAL;
-+            return NULL;
-         }
- 
--        head = talloc_get_type(value.ptr, struct kcm_ops_queue_entry);
--        if (head == NULL) {
-+        kq = talloc_get_type(value.ptr, struct kcm_ops_queue);
-+        if (kq == NULL) {
-             DEBUG(SSSDBG_CRIT_FAILURE, "Invalid queue pointer\n");
--            return EINVAL;
-+            return NULL;
-         }
- 
--        entry->head = head;
--        DLIST_ADD_END(head, entry, struct kcm_ops_queue_entry *);
--
--        DEBUG(SSSDBG_TRACE_LIBS, "Waiting in queue\n");
--        ret = EAGAIN;
-+        DEBUG(SSSDBG_TRACE_LIBS, "Found existing queue for this ID\n");
-         break;
- 
-     case HASH_ERROR_KEY_NOT_FOUND:
-@@ -151,36 +181,41 @@ static errno_t kcm_op_queue_add(hash_table_t *wait_queue_hash,
-          * another one comes in and return EOK to run the current request
-          * immediatelly
-          */
--        entry->head = entry;
-+        DEBUG(SSSDBG_TRACE_LIBS, "No existing queue for this ID\n");
-+
-+        kq = talloc_zero(qctx->wait_queue_hash, struct kcm_ops_queue);
-+        if (kq == NULL) {
-+            return NULL;
-+        }
-+        kq->uid = uid;
-+        kq->qctx = qctx;
-+        kq->ev = ev;
- 
-         value.type = HASH_VALUE_PTR;
--        value.ptr = entry;
-+        value.ptr = kq;
- 
--        ret = hash_enter(wait_queue_hash, &key, &value);
-+        ret = hash_enter(qctx->wait_queue_hash, &key, &value);
-         if (ret != HASH_SUCCESS) {
-             DEBUG(SSSDBG_CRIT_FAILURE, "hash_enter failed.\n");
--            return EIO;
-+            return NULL;
-         }
--
--        DEBUG(SSSDBG_TRACE_LIBS,
--              "Added a first request to the queue, running immediately\n");
--        ret = EOK;
-         break;
- 
-     default:
-         DEBUG(SSSDBG_CRIT_FAILURE, "hash_lookup failed.\n");
--        return EIO;
-+        return NULL;
-     }
- 
--    talloc_steal(wait_queue_hash, entry);
--    talloc_set_destructor(entry, kcm_op_queue_entry_destructor);
--    return ret;
-+    return kq;
- }
- 
- struct kcm_op_queue_state {
-     struct kcm_ops_queue_entry *entry;
- };
- 
-+static errno_t kcm_op_queue_add_req(struct kcm_ops_queue *kq,
-+                                    struct tevent_req *req);
-+
- /*
-  * Enqueue a request.
-  *
-@@ -198,6 +233,7 @@ struct tevent_req *kcm_op_queue_send(TALLOC_CTX *mem_ctx,
- {
-     errno_t ret;
-     struct tevent_req *req;
-+    struct kcm_ops_queue *kq;
-     struct kcm_op_queue_state *state;
-     uid_t uid;
- 
-@@ -208,22 +244,21 @@ struct tevent_req *kcm_op_queue_send(TALLOC_CTX *mem_ctx,
-         return NULL;
-     }
- 
--    state->entry = talloc_zero(state, struct kcm_ops_queue_entry);
--    if (state->entry == NULL) {
--        ret = ENOMEM;
--        goto immediate;
--    }
--    state->entry->req = req;
--    state->entry->uid = uid;
--    state->entry->wait_queue_hash = qctx->wait_queue_hash;
--
-     DEBUG(SSSDBG_FUNC_DATA,
-           "Adding request by %"SPRIuid" to the wait queue\n", uid);
- 
--    ret = kcm_op_queue_add(qctx->wait_queue_hash, state->entry, uid);
-+    kq = kcm_op_queue_get(qctx, ev, uid);
-+    if (kq == NULL) {
-+        ret = EIO;
-+        DEBUG(SSSDBG_OP_FAILURE,
-+              "Cannot get queue [%d]: %s\n", ret, sss_strerror(ret));
-+        goto immediate;
-+    }
-+
-+    ret = kcm_op_queue_add_req(kq, req);
-     if (ret == EOK) {
-         DEBUG(SSSDBG_TRACE_LIBS,
--              "Wait queue was empty, running immediately\n");
-+              "Queue was empty, running the request immediately\n");
-         goto immediate;
-     } else if (ret != EAGAIN) {
-         DEBUG(SSSDBG_OP_FAILURE,
-@@ -244,6 +279,33 @@ immediate:
-     return req;
- }
- 
-+static errno_t kcm_op_queue_add_req(struct kcm_ops_queue *kq,
-+                                    struct tevent_req *req)
-+{
-+    errno_t ret;
-+    struct kcm_op_queue_state *state = tevent_req_data(req,
-+                                                struct kcm_op_queue_state);
-+
-+    state->entry = talloc_zero(kq->qctx->wait_queue_hash, struct kcm_ops_queue_entry);
-+    if (state->entry == NULL) {
-+        return ENOMEM;
-+    }
-+    state->entry->req = req;
-+    state->entry->queue = kq;
-+    talloc_set_destructor(state->entry, kcm_op_queue_entry_destructor);
-+
-+    if (kq->head == NULL) {
-+        /* First entry, will run callback at once */
-+        ret = EOK;
-+    } else {
-+        /* Will wait for the previous callbacks to finish */
-+        ret = EAGAIN;
-+    }
-+
-+    DLIST_ADD_END(kq->head, state->entry, struct kcm_ops_queue_entry *);
-+    return ret;
-+}
-+
- /*
-  * The queue recv function is called when this request is 'activated'. The queue
-  * entry should be allocated on the same memory context as the enqueued request
--- 
-2.9.4
-
diff --git a/SOURCES/0137-TESTS-Add-a-test-for-parallel-execution-of-klist.patch b/SOURCES/0137-TESTS-Add-a-test-for-parallel-execution-of-klist.patch
deleted file mode 100644
index 25a3528..0000000
--- a/SOURCES/0137-TESTS-Add-a-test-for-parallel-execution-of-klist.patch
+++ /dev/null
@@ -1,79 +0,0 @@
-From 7930ee12093eae1e1ab9422c4f4f9f8c5661fcb9 Mon Sep 17 00:00:00 2001
-From: Jakub Hrozek <jhrozek@redhat.com>
-Date: Tue, 23 May 2017 13:55:01 +0200
-Subject: [PATCH 137/138] TESTS: Add a test for parallel execution of klist
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Integration test for:
-    https://pagure.io/SSSD/sssd/issue/3372
-
-With https://pagure.io/SSSD/sssd/issue/3372 still broken, the unit test
-wold fail because one of the concurrent klist commands would trigger a
-race condition in the KCM queue code, crashing the KCM responder.
-
-Reviewed-by: Pavel Březina <pbrezina@redhat.com>
-(cherry picked from commit 274489b092bba5fc81cb0f803843d56b267c5aaf)
----
- src/tests/intg/krb5utils.py |  6 +++++-
- src/tests/intg/test_kcm.py  | 22 ++++++++++++++++++++++
- 2 files changed, 27 insertions(+), 1 deletion(-)
-
-diff --git a/src/tests/intg/krb5utils.py b/src/tests/intg/krb5utils.py
-index 775cffd0bbfa011f2d8ffc1169dccfef96d78fab..0349ff3829533088fb2263f84b19574127d6e809 100644
---- a/src/tests/intg/krb5utils.py
-+++ b/src/tests/intg/krb5utils.py
-@@ -36,7 +36,7 @@ class Krb5Utils(object):
-     def __init__(self, krb5_conf_path):
-         self.krb5_conf_path = krb5_conf_path
- 
--    def _run_in_env(self, args, stdin=None, extra_env=None):
-+    def spawn_in_env(self, args, stdin=None, extra_env=None):
-         my_env = os.environ
-         my_env['KRB5_CONFIG'] = self.krb5_conf_path
- 
-@@ -50,6 +50,10 @@ class Krb5Utils(object):
-                                stdin=subprocess.PIPE,
-                                stdout=subprocess.PIPE,
-                                stderr=subprocess.PIPE)
-+        return cmd
-+
-+    def _run_in_env(self, args, stdin=None, extra_env=None):
-+        cmd = self.spawn_in_env(args, stdin, extra_env)
-         out, err = cmd.communicate(stdin)
-         return cmd.returncode, out.decode('utf-8'), err.decode('utf-8')
- 
-diff --git a/src/tests/intg/test_kcm.py b/src/tests/intg/test_kcm.py
-index 11f80a1803b4ad9b8e8857bf9a8a244d4816f0a2..1ab2a1837687a6c2cf8676124b42538833550c91 100644
---- a/src/tests/intg/test_kcm.py
-+++ b/src/tests/intg/test_kcm.py
-@@ -445,3 +445,25 @@ def test_kcm_sec_kdestroy_nocache(setup_for_kcm_sec,
-                                   setup_secrets):
-     testenv = setup_for_kcm_sec
-     exercise_subsidiaries(testenv)
-+
-+def test_kcm_sec_parallel_klist(setup_for_kcm_sec,
-+                                setup_secrets):
-+    """
-+    Test that parallel operations from a single UID are handled well.
-+    Regression test for https://pagure.io/SSSD/sssd/issue/3372
-+    """
-+    testenv = setup_for_kcm_sec
-+
-+    testenv.k5kdc.add_principal("alice", "alicepw")
-+    out, _, _ = testenv.k5util.kinit("alice", "alicepw")
-+    assert out == 0
-+
-+
-+    processes = []
-+    for i in range(0,10):
-+        p = testenv.k5util.spawn_in_env(['klist', '-A'])
-+        processes.append(p)
-+
-+    for p in processes:
-+        rc = p.wait()
-+        assert rc == 0
--- 
-2.9.4
-
diff --git a/SOURCES/0138-ipa-filter-IPA-users-from-extdom-lookups-by-certific.patch b/SOURCES/0138-ipa-filter-IPA-users-from-extdom-lookups-by-certific.patch
deleted file mode 100644
index f9689ea..0000000
--- a/SOURCES/0138-ipa-filter-IPA-users-from-extdom-lookups-by-certific.patch
+++ /dev/null
@@ -1,115 +0,0 @@
-From e3b29c9f95d5a5ff007000b254143c337ef0b0dc Mon Sep 17 00:00:00 2001
-From: Sumit Bose <sbose@redhat.com>
-Date: Fri, 19 May 2017 12:52:47 +0200
-Subject: [PATCH 138/138] ipa: filter IPA users from extdom lookups by
- certificate
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-The extdom lookup by certificate will return the names of all matching
-users, both from the IPA and trusted domains. The IPA users from the
-list should not be looked up via the extdom plugin because they are
-already lookup up directly. Additionally the lookup might fail and cause
-an error which might prevent that the remaining users from the list are
-looked up.
-
-Resolves https://pagure.io/SSSD/sssd/issue/3407
-
-Reviewed-by: Pavel Březina <pbrezina@redhat.com>
-(cherry picked from commit eb7095099b2dd0afb1d028dbc15d8c5a897d90f8)
----
- src/providers/ipa/ipa_s2n_exop.c | 35 ++++++++++++++++++++++++++++++-----
- 1 file changed, 30 insertions(+), 5 deletions(-)
-
-diff --git a/src/providers/ipa/ipa_s2n_exop.c b/src/providers/ipa/ipa_s2n_exop.c
-index f5f4401f86615dc7f81f844e1096ad43e965c384..15904e0197919c34b1bce58b4bd2c070f99b67a7 100644
---- a/src/providers/ipa/ipa_s2n_exop.c
-+++ b/src/providers/ipa/ipa_s2n_exop.c
-@@ -792,6 +792,7 @@ static errno_t s2n_response_to_attrs(TALLOC_CTX *mem_ctx,
-     char **name_list = NULL;
-     ber_len_t ber_len;
-     char *fq_name = NULL;
-+    struct sss_domain_info *root_domain = NULL;
- 
-     if (retoid == NULL || retdata == NULL) {
-         DEBUG(SSSDBG_OP_FAILURE, "Missing OID or data.\n");
-@@ -965,6 +966,8 @@ static errno_t s2n_response_to_attrs(TALLOC_CTX *mem_ctx,
-                 goto done;
-             }
- 
-+            root_domain = get_domains_head(dom);
-+
-             while (ber_peek_tag(ber, &ber_len) ==  LBER_SEQUENCE) {
-                 tag = ber_scanf(ber, "{aa}", &domain_name, &name);
-                 if (tag == LBER_ERROR) {
-@@ -983,7 +986,12 @@ static errno_t s2n_response_to_attrs(TALLOC_CTX *mem_ctx,
-                 DEBUG(SSSDBG_TRACE_ALL, "[%s][%s][%s].\n", domain_name, name,
-                                                            fq_name);
- 
--                ret = add_string_to_list(attrs, fq_name, &name_list);
-+                if (strcasecmp(root_domain->name, domain_name) != 0) {
-+                    ret = add_string_to_list(attrs, fq_name, &name_list);
-+                } else {
-+                    DEBUG(SSSDBG_TRACE_ALL,
-+                          "[%s] from root domain, skipping.\n", fq_name);
-+                }
-                 ber_memfree(domain_name);
-                 ber_memfree(name);
-                 talloc_free(fq_name);
-@@ -1228,7 +1236,7 @@ static errno_t ipa_s2n_get_list_step(struct tevent_req *req)
- 
-         break;
-     default:
--        DEBUG(SSSDBG_OP_FAILURE, "Unexpected inoput type [%d].\n",
-+        DEBUG(SSSDBG_OP_FAILURE, "Unexpected input type [%d].\n",
-                                  state->req_input.type);
-         return EINVAL;
-     }
-@@ -1247,9 +1255,10 @@ static errno_t ipa_s2n_get_list_step(struct tevent_req *req)
- 
-     if (state->req_input.type == REQ_INP_NAME
-             && state->req_input.inp.name != NULL) {
--        DEBUG(SSSDBG_TRACE_FUNC, "Sending request_type: [%s] for group [%s].\n",
--                                 ipa_s2n_reqtype2str(state->request_type),
--                                 state->list[state->list_idx]);
-+        DEBUG(SSSDBG_TRACE_FUNC,
-+              "Sending request_type: [%s] for object [%s].\n",
-+              ipa_s2n_reqtype2str(state->request_type),
-+              state->list[state->list_idx]);
-     }
- 
-     subreq = ipa_s2n_exop_send(state, state->ev, state->sh, need_v1,
-@@ -1886,6 +1895,13 @@ static void ipa_s2n_get_user_done(struct tevent_req *subreq)
- 
-         if (state->simple_attrs->response_type == RESP_NAME_LIST
-                 && state->req_input->type == REQ_INP_CERT) {
-+
-+            if (state->simple_attrs->name_list == NULL) {
-+                /* No results from sub-domains, nothing to do */
-+                ret = EOK;
-+                goto done;
-+            }
-+
-             state->mapped_attrs = sysdb_new_attrs(state);
-             if (state->mapped_attrs == NULL) {
-                 DEBUG(SSSDBG_OP_FAILURE, "sysdb_new_attrs failed.\n");
-@@ -2640,6 +2656,15 @@ static void ipa_s2n_get_list_done(struct tevent_req  *subreq)
-         return;
-     }
- 
-+    if (state->attrs == NULL) {
-+        /* If this is a request by certificate we are done */
-+        if (state->req_input->type == REQ_INP_CERT) {
-+            tevent_req_done(req);
-+        } else {
-+            tevent_req_error(req, EINVAL);
-+        }
-+    }
-+
-     ret = sysdb_attrs_get_string(state->attrs->sysdb_attrs, SYSDB_SID_STR,
-                                  &sid_str);
-     if (ret == ENOENT) {
--- 
-2.9.4
-
diff --git a/SOURCES/0139-krb5-accept-changed-principal-if-krb5_canonicalize-T.patch b/SOURCES/0139-krb5-accept-changed-principal-if-krb5_canonicalize-T.patch
deleted file mode 100644
index 259a343..0000000
--- a/SOURCES/0139-krb5-accept-changed-principal-if-krb5_canonicalize-T.patch
+++ /dev/null
@@ -1,50 +0,0 @@
-From 04a1802749b6ebf72730357b06bf8cabe09ebb01 Mon Sep 17 00:00:00 2001
-From: Sumit Bose <sbose@redhat.com>
-Date: Wed, 24 May 2017 16:10:26 +0200
-Subject: [PATCH 139/141] krb5: accept changed principal if
- krb5_canonicalize=True
-
-Currently SSSD accepts significant changes in the principal only if
-krb5_use_enterprise_principal=True. But canonicalization can lead to
-similar changes so they should be accepted in this case as well.
-
-Resolves:
-https://pagure.io/SSSD/sssd/issue/3408
-
-Reviewed-by: Robbie Harwood <rharwood@redhat.com>
-(cherry picked from commit ca95807a9060e454ee68f6f30558d6f7ee968c39)
----
- src/providers/krb5/krb5_auth.c | 3 +++
- 1 file changed, 3 insertions(+)
-
-diff --git a/src/providers/krb5/krb5_auth.c b/src/providers/krb5/krb5_auth.c
-index 894bd41bde031ac33187bfa3b14202e9429a9198..03ea9d88cac67919d4b9ba3a1cf2efa208662195 100644
---- a/src/providers/krb5/krb5_auth.c
-+++ b/src/providers/krb5/krb5_auth.c
-@@ -829,6 +829,7 @@ static void krb5_auth_done(struct tevent_req *subreq)
-     char *renew_interval_str;
-     time_t renew_interval_time = 0;
-     bool use_enterprise_principal;
-+    bool canonicalize;
- 
-     ret = handle_child_recv(subreq, pd, &buf, &len);
-     talloc_zfree(subreq);
-@@ -908,6 +909,7 @@ static void krb5_auth_done(struct tevent_req *subreq)
- 
-     use_enterprise_principal = dp_opt_get_bool(kr->krb5_ctx->opts,
-                                                KRB5_USE_ENTERPRISE_PRINCIPAL);
-+    canonicalize = dp_opt_get_bool(kr->krb5_ctx->opts, KRB5_CANONICALIZE);
- 
-     /* Check if the cases of our upn are correct and update it if needed.
-      * Fail if the upn differs by more than just the case for non-enterprise
-@@ -915,6 +917,7 @@ static void krb5_auth_done(struct tevent_req *subreq)
-     if (res->correct_upn != NULL &&
-         strcmp(kr->upn, res->correct_upn) != 0) {
-         if (strcasecmp(kr->upn, res->correct_upn) == 0 ||
-+            canonicalize == true ||
-             use_enterprise_principal == true) {
-             talloc_free(kr->upn);
-             kr->upn = talloc_strdup(kr, res->correct_upn);
--- 
-2.9.4
-
diff --git a/SOURCES/0140-IPA-Avoid-using-uninitialized-ret-value-when-skippin.patch b/SOURCES/0140-IPA-Avoid-using-uninitialized-ret-value-when-skippin.patch
deleted file mode 100644
index e6a08b1..0000000
--- a/SOURCES/0140-IPA-Avoid-using-uninitialized-ret-value-when-skippin.patch
+++ /dev/null
@@ -1,30 +0,0 @@
-From 4986fe8b68a3e14a30e8091353bf0679eb3c5e55 Mon Sep 17 00:00:00 2001
-From: Jakub Hrozek <jhrozek@redhat.com>
-Date: Wed, 24 May 2017 21:24:20 +0200
-Subject: [PATCH 140/141] IPA: Avoid using uninitialized ret value when
- skipping entries from the joined domain
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Reviewed-by: Fabiano Fidêncio <fidencio@redhat.com>
-(cherry picked from commit 3e3034199b44e01899ec7ba8152fef3738a0e093)
----
- src/providers/ipa/ipa_s2n_exop.c | 1 +
- 1 file changed, 1 insertion(+)
-
-diff --git a/src/providers/ipa/ipa_s2n_exop.c b/src/providers/ipa/ipa_s2n_exop.c
-index 15904e0197919c34b1bce58b4bd2c070f99b67a7..3f5f9859554f0b98ecd3fdad31fd66274c5707b0 100644
---- a/src/providers/ipa/ipa_s2n_exop.c
-+++ b/src/providers/ipa/ipa_s2n_exop.c
-@@ -991,6 +991,7 @@ static errno_t s2n_response_to_attrs(TALLOC_CTX *mem_ctx,
-                 } else {
-                     DEBUG(SSSDBG_TRACE_ALL,
-                           "[%s] from root domain, skipping.\n", fq_name);
-+                    ret = EOK; /* Free resources and continue in the loop */
-                 }
-                 ber_memfree(domain_name);
-                 ber_memfree(name);
--- 
-2.9.4
-
diff --git a/SOURCES/0141-IPA-Return-from-function-after-marking-a-request-as-.patch b/SOURCES/0141-IPA-Return-from-function-after-marking-a-request-as-.patch
deleted file mode 100644
index 1da145d..0000000
--- a/SOURCES/0141-IPA-Return-from-function-after-marking-a-request-as-.patch
+++ /dev/null
@@ -1,30 +0,0 @@
-From 2ae1485566cbd2b095935aaf7e851d12d2de4513 Mon Sep 17 00:00:00 2001
-From: Jakub Hrozek <jhrozek@redhat.com>
-Date: Wed, 24 May 2017 21:26:22 +0200
-Subject: [PATCH 141/141] IPA: Return from function after marking a request as
- finished
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Reviewed-by: Fabiano Fidêncio <fidencio@redhat.com>
-(cherry picked from commit eb404bcdbbff7e080a93d816e17b8cec04f79fc4)
----
- src/providers/ipa/ipa_s2n_exop.c | 1 +
- 1 file changed, 1 insertion(+)
-
-diff --git a/src/providers/ipa/ipa_s2n_exop.c b/src/providers/ipa/ipa_s2n_exop.c
-index 3f5f9859554f0b98ecd3fdad31fd66274c5707b0..39ed17cbf0e8c523212084197e9f2963fed88dc8 100644
---- a/src/providers/ipa/ipa_s2n_exop.c
-+++ b/src/providers/ipa/ipa_s2n_exop.c
-@@ -2664,6 +2664,7 @@ static void ipa_s2n_get_list_done(struct tevent_req  *subreq)
-         } else {
-             tevent_req_error(req, EINVAL);
-         }
-+        return;
-     }
- 
-     ret = sysdb_attrs_get_string(state->attrs->sysdb_attrs, SYSDB_SID_STR,
--- 
-2.9.4
-
diff --git a/SOURCES/0142-HBAC-Do-not-rely-on-originalMemberOf-use-the-sysdb-m.patch b/SOURCES/0142-HBAC-Do-not-rely-on-originalMemberOf-use-the-sysdb-m.patch
deleted file mode 100644
index 4669a77..0000000
--- a/SOURCES/0142-HBAC-Do-not-rely-on-originalMemberOf-use-the-sysdb-m.patch
+++ /dev/null
@@ -1,175 +0,0 @@
-From c7c087b5485d50e8689d31fd9d52af935ae398be Mon Sep 17 00:00:00 2001
-From: Jakub Hrozek <jhrozek@redhat.com>
-Date: Sun, 9 Apr 2017 20:50:47 +0200
-Subject: [PATCH 142/142] HBAC: Do not rely on originalMemberOf, use the sysdb
- memberof links instead
-
-The IPA HBAC code used to read the group members from the
-originalMemberOf attribute value for performance reasons. However,
-especially on IPA clients trusting an AD domain, the originalMemberOf
-attribute value is often not synchronized correctly.
-
-Instead of going through the work of maintaining both member/memberOf
-and originalMemberOf, let's just do an ASQ search for the group names of
-the groups the user is a member of in the cache and read their
-SYSBD_NAME attribute.
-
-To avoid clashing between similarly-named groups in IPA and in AD, we
-look at the container of the group.
-
-Resolves:
-https://pagure.io/SSSD/sssd/issue/3382
-
-Reviewed-by: Sumit Bose <sbose@redhat.com>
-(cherry picked from commit c92e49144978ad3b6c9fffa8803ebdad8f6f5b18)
----
- src/providers/ipa/ipa_hbac_common.c | 97 +++++++++++++++++++++++++------------
- 1 file changed, 67 insertions(+), 30 deletions(-)
-
-diff --git a/src/providers/ipa/ipa_hbac_common.c b/src/providers/ipa/ipa_hbac_common.c
-index b99b75d322930f16412f6abd4cdf0d7e0b59c32c..ba677965a3eb68a54baf99b1875bca2acbb76c99 100644
---- a/src/providers/ipa/ipa_hbac_common.c
-+++ b/src/providers/ipa/ipa_hbac_common.c
-@@ -507,15 +507,15 @@ hbac_eval_user_element(TALLOC_CTX *mem_ctx,
-                        struct hbac_request_element **user_element)
- {
-     errno_t ret;
--    unsigned int i;
-     unsigned int num_groups = 0;
-     TALLOC_CTX *tmp_ctx;
--    const char *member_dn;
-     struct hbac_request_element *users;
--    struct ldb_message *msg;
--    struct ldb_message_element *el;
--    const char *attrs[] = { SYSDB_ORIG_MEMBEROF, NULL };
-     char *shortname;
-+    const char *fqgroupname = NULL;
-+    struct sss_domain_info *ipa_domain;
-+    struct ldb_dn *ipa_groups_basedn;
-+    struct ldb_result *res;
-+    int exp_comp;
- 
-     tmp_ctx = talloc_new(mem_ctx);
-     if (tmp_ctx == NULL) return ENOMEM;
-@@ -533,56 +533,93 @@ hbac_eval_user_element(TALLOC_CTX *mem_ctx,
-     }
-     users->name = talloc_steal(users, shortname);
- 
--    /* Read the originalMemberOf attribute
--     * This will give us the list of both POSIX and
--     * non-POSIX groups that this user belongs to.
-+    ipa_domain = get_domains_head(domain);
-+    if (ipa_domain == NULL) {
-+        ret = EINVAL;
-+        goto done;
-+    }
-+
-+    ipa_groups_basedn = ldb_dn_new_fmt(tmp_ctx, sysdb_ctx_get_ldb(domain->sysdb),
-+                                       SYSDB_TMPL_GROUP_BASE, ipa_domain->name);
-+    if (ipa_groups_basedn == NULL) {
-+        ret = ENOMEM;
-+        goto done;
-+    }
-+
-+    /* +1 because there will be a RDN preceding the base DN */
-+    exp_comp = ldb_dn_get_comp_num(ipa_groups_basedn) + 1;
-+
-+    /*
-+     * Get all the groups the user is a member of.
-+     * This includes both POSIX and non-POSIX groups.
-      */
--    ret = sysdb_search_user_by_name(tmp_ctx, domain, username,
--                                    attrs, &msg);
-+    ret = sysdb_initgroups(tmp_ctx, domain, username, &res);
-     if (ret != EOK) {
-         DEBUG(SSSDBG_CRIT_FAILURE,
--              "Could not determine user memberships for [%s]\n",
--                  users->name);
-+              "sysdb_asq_search failed [%d]: %s\n", ret, sss_strerror(ret));
-         goto done;
-     }
- 
--    el = ldb_msg_find_element(msg, SYSDB_ORIG_MEMBEROF);
--    if (el == NULL || el->num_values == 0) {
-+    if (res->count == 0) {
-+        /* This should not happen at this point */
-+        DEBUG(SSSDBG_MINOR_FAILURE,
-+              "User [%s] not found in cache.\n", username);
-+        ret = ENOENT;
-+        goto done;
-+    } else if (res->count == 1) {
-+        /* The first item is the user entry */
-         DEBUG(SSSDBG_TRACE_LIBS, "No groups for [%s]\n", users->name);
-         ret = create_empty_grouplist(users);
-         goto done;
-     }
-     DEBUG(SSSDBG_TRACE_LIBS,
--          "[%d] groups for [%s]\n", el->num_values, users->name);
-+          "[%u] groups for [%s]\n", res->count - 1, username);
- 
--    users->groups = talloc_array(users, const char *, el->num_values + 1);
-+    /* This also includes the sentinel, b/c we'll skip the user entry below */
-+    users->groups = talloc_array(users, const char *, res->count);
-     if (users->groups == NULL) {
-         ret = ENOMEM;
-         goto done;
-     }
- 
--    for (i = 0; i < el->num_values; i++) {
--        member_dn = (const char *)el->values[i].data;
-+    /* Start counting from 1 to exclude the user entry */
-+    for (size_t i = 1; i < res->count; i++) {
-+        /* Only groups from the IPA domain can be referenced from HBAC rules. To
-+         * avoid evaluating groups which might even have the same name, but come
-+         * from a trusted domain, we first copy the DN to a temporary one..
-+         */
-+        if (ldb_dn_get_comp_num(res->msgs[i]->dn) != exp_comp
-+                || ldb_dn_compare_base(ipa_groups_basedn,
-+                                       res->msgs[i]->dn) != 0) {
-+            DEBUG(SSSDBG_FUNC_DATA,
-+                  "Skipping non-IPA group %s\n",
-+                  ldb_dn_get_linearized(res->msgs[i]->dn));
-+            continue;
-+        }
- 
--        ret = get_ipa_groupname(users->groups, domain->sysdb, member_dn,
--                                &users->groups[num_groups]);
--        if (ret != EOK && ret != ERR_UNEXPECTED_ENTRY_TYPE) {
-+        fqgroupname = ldb_msg_find_attr_as_string(res->msgs[i], SYSDB_NAME, NULL);
-+        if (fqgroupname == NULL) {
-             DEBUG(SSSDBG_MINOR_FAILURE,
--                    "Skipping malformed entry [%s]\n", member_dn);
-+                  "Skipping malformed entry [%s]\n",
-+                  ldb_dn_get_linearized(res->msgs[i]->dn));
-             continue;
--        } else if (ret == EOK) {
--            DEBUG(SSSDBG_TRACE_LIBS, "Added group [%s] for user [%s]\n",
--                      users->groups[num_groups], users->name);
--            num_groups++;
-+        }
-+
-+        ret = sss_parse_internal_fqname(tmp_ctx, fqgroupname,
-+                                        &shortname, NULL);
-+        if (ret != EOK) {
-+            DEBUG(SSSDBG_MINOR_FAILURE, "Malformed name %s, skipping!\n", fqgroupname);
-             continue;
-         }
--        /* Skip entries that are not groups */
--        DEBUG(SSSDBG_TRACE_INTERNAL,
--              "Skipping non-group memberOf [%s]\n", member_dn);
-+
-+        users->groups[num_groups] = talloc_steal(users->groups, shortname);
-+        DEBUG(SSSDBG_TRACE_LIBS, "Added group [%s] for user [%s]\n",
-+              users->groups[num_groups], users->name);
-+        num_groups++;
-     }
-     users->groups[num_groups] = NULL;
- 
--    if (num_groups < el->num_values) {
-+    if (num_groups < (res->count - 1)) {
-         /* Shrink the array memory */
-         users->groups = talloc_realloc(users, users->groups, const char *,
-                                        num_groups+1);
--- 
-2.9.4
-
diff --git a/SOURCES/0143-VALIDATORS-Add-subdomain-section.patch b/SOURCES/0143-VALIDATORS-Add-subdomain-section.patch
deleted file mode 100644
index 8b1c3c5..0000000
--- a/SOURCES/0143-VALIDATORS-Add-subdomain-section.patch
+++ /dev/null
@@ -1,53 +0,0 @@
-From 270121098caff2496da73795fe586ff734ae1e56 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Michal=20=C5=BDidek?= <mzidek@redhat.com>
-Date: Tue, 4 Apr 2017 18:01:02 +0200
-Subject: [PATCH 143/152] VALIDATORS: Add subdomain section
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Add separate rule for subdomain sections.
-
-Resolves:
-https://pagure.io/SSSD/sssd/issue/3356
-
-Reviewed-by: Lukáš Slebodník <lslebodn@redhat.com>
----
- src/config/cfg_rules.ini | 17 ++++++++++++++++-
- 1 file changed, 16 insertions(+), 1 deletion(-)
-
-diff --git a/src/config/cfg_rules.ini b/src/config/cfg_rules.ini
-index e47ff33242d6a9e5979fe0eb8eea14c2af28685a..4b30e8fc43b50844023e7fffa607a59530a302f0 100644
---- a/src/config/cfg_rules.ini
-+++ b/src/config/cfg_rules.ini
-@@ -11,7 +11,8 @@ section = ifp
- section = secrets
- section = kcm
- section_re = ^secrets/users/[0-9]\+$
--section_re = ^domain/.*$
-+section_re = ^domain/[^/\@]\+$
-+section_re = ^domain/[^/\@]\+/[^/\@]\+$
- section_re = ^application/.*$
- 
- [rule/allowed_sssd_options]
-@@ -698,3 +699,17 @@ validator = ini_allowed_options
- section_re = ^application/.*$
- 
- option = inherit_from
-+
-+[rule/allowed_subdomain_options]
-+validator = ini_allowed_options
-+section_re = ^domain/[^/\@]\+/[^/\@]\+$
-+
-+option = ldap_search_base
-+option = ldap_user_search_base
-+option = ldap_group_search_base
-+option = ldap_netgroup_search_base
-+option = ldap_service_search_base
-+option = ad_server
-+option = ad_backup_server
-+option = ad_site
-+option = use_fully_qualified_names
--- 
-2.9.4
-
diff --git a/SOURCES/0144-VALIDATORS-Remove-application-section-domain.patch b/SOURCES/0144-VALIDATORS-Remove-application-section-domain.patch
deleted file mode 100644
index d644b35..0000000
--- a/SOURCES/0144-VALIDATORS-Remove-application-section-domain.patch
+++ /dev/null
@@ -1,48 +0,0 @@
-From 39864f9ce41ce892fe3cafb769584173299f49f7 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Michal=20=C5=BDidek?= <mzidek@redhat.com>
-Date: Tue, 4 Apr 2017 19:07:12 +0200
-Subject: [PATCH 144/152] VALIDATORS: Remove application section domain
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Application domains can use the same options as normal domains section
-with one more additional option.
-
-We could either duplicate all options from the domain section also in
-the application domain section + add the one additional option or
-add this one option to the domain section even though it is not meant
-to be used there to avoid duplication of all domain options in the
-rule for application section.
-
-It would be could to enhance the validators in libini to allow
-something like 'include' section in order to avoid this issue
-in the future.
-
-Resolves:
-https://pagure.io/SSSD/sssd/issue/3356
-
-Reviewed-by: Lukáš Slebodník <lslebodn@redhat.com>
----
- src/config/cfg_rules.ini | 5 +----
- 1 file changed, 1 insertion(+), 4 deletions(-)
-
-diff --git a/src/config/cfg_rules.ini b/src/config/cfg_rules.ini
-index 4b30e8fc43b50844023e7fffa607a59530a302f0..a30fe57e262716abeb2d2af9c3add326122ee4ca 100644
---- a/src/config/cfg_rules.ini
-+++ b/src/config/cfg_rules.ini
-@@ -694,10 +694,7 @@ option = ldap_user_uid_number
- option = ldap_user_uuid
- option = ldap_use_tokengroups
- 
--[rule/allowed_application_options]
--validator = ini_allowed_options
--section_re = ^application/.*$
--
-+# For application domains
- option = inherit_from
- 
- [rule/allowed_subdomain_options]
--- 
-2.9.4
-
diff --git a/SOURCES/0145-VALIDATORS-Escape-special-regex-chars.patch b/SOURCES/0145-VALIDATORS-Escape-special-regex-chars.patch
deleted file mode 100644
index d80f3cf..0000000
--- a/SOURCES/0145-VALIDATORS-Escape-special-regex-chars.patch
+++ /dev/null
@@ -1,35 +0,0 @@
-From fc6bffc8523e6decf4656182f8caf72236e45c3d Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Michal=20=C5=BDidek?= <mzidek@redhat.com>
-Date: Tue, 4 Apr 2017 20:06:40 +0200
-Subject: [PATCH 145/152] VALIDATORS: Escape special regex chars
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-The rule allowed_domain_options did not work because
-of bad regex.
-
-Resolves:
-https://pagure.io/SSSD/sssd/issue/3356
-
-Reviewed-by: Lukáš Slebodník <lslebodn@redhat.com>
----
- src/config/cfg_rules.ini | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
-diff --git a/src/config/cfg_rules.ini b/src/config/cfg_rules.ini
-index a30fe57e262716abeb2d2af9c3add326122ee4ca..628f2e0e0a040bad5128d00d9348aa91170ed704 100644
---- a/src/config/cfg_rules.ini
-+++ b/src/config/cfg_rules.ini
-@@ -295,7 +295,7 @@ option = responder_idle_timeout
- 
- [rule/allowed_domain_options]
- validator = ini_allowed_options
--section_re = ^(domain|application)/.*$
-+section_re = ^\(domain\|application\)/.*$
- 
- option = debug
- option = debug_level
--- 
-2.9.4
-
diff --git a/SOURCES/0146-TESTS-Add-unit-tests-for-cfg-validation.patch b/SOURCES/0146-TESTS-Add-unit-tests-for-cfg-validation.patch
deleted file mode 100644
index dae5f6c..0000000
--- a/SOURCES/0146-TESTS-Add-unit-tests-for-cfg-validation.patch
+++ /dev/null
@@ -1,328 +0,0 @@
-From 897216b87352e9f80181be6f1a036163c599ba46 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Michal=20=C5=BDidek?= <mzidek@redhat.com>
-Date: Fri, 26 May 2017 19:58:48 +0200
-Subject: [PATCH 146/152] TESTS: Add unit tests for cfg validation
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Add infrastructure for unit tests for validators.
-
-Reviewed-by: Lukáš Slebodník <lslebodn@redhat.com>
----
- Makefile.am                          |  16 +++
- src/tests/cmocka/test_config_check.c | 268 +++++++++++++++++++++++++++++++++++
- 2 files changed, 284 insertions(+)
- create mode 100644 src/tests/cmocka/test_config_check.c
-
-diff --git a/Makefile.am b/Makefile.am
-index a6279133b56dcd5bcbd1306ae8f2ce18d90c2c12..503c8cfd795b503f566431c08a56a56147180322 100644
---- a/Makefile.am
-+++ b/Makefile.am
-@@ -252,6 +252,7 @@ if HAVE_CMOCKA
-         dp_opt_tests \
-         responder-get-domains-tests \
-         sbus-internal-tests \
-+        config_check-tests \
-         sss_sifp-tests \
-         test_search_bases \
-         test_ldap_auth \
-@@ -2429,6 +2430,21 @@ sbus_internal_tests_LDADD = \
-     libsss_debug.la \
-     libsss_test_common.la
- 
-+config_check_tests_SOURCES = \
-+    src/tests/cmocka/test_config_check.c \
-+    $(NULL)
-+config_check_tests_CFLAGS = \
-+    $(AM_CFLAGS) \
-+    $(NULL)
-+config_check_tests_LDADD = \
-+    $(CMOCKA_LIBS) \
-+    $(POPT_LIBS) \
-+    $(INI_CONFIG_LIBS) \
-+    $(TALLOC_LIBS) \
-+    $(SSSD_INTERNAL_LTLIBS) \
-+    libsss_test_common.la \
-+    $(NULL)
-+
- test_find_uid_SOURCES = \
-     src/tests/cmocka/test_find_uid.c \
-     src/util/find_uid.c \
-diff --git a/src/tests/cmocka/test_config_check.c b/src/tests/cmocka/test_config_check.c
-new file mode 100644
-index 0000000000000000000000000000000000000000..8fc0b01f3ef3fe03152efd979a3e96c21ba567cc
---- /dev/null
-+++ b/src/tests/cmocka/test_config_check.c
-@@ -0,0 +1,268 @@
-+/*
-+    Authors:
-+        Michal Zidek <mzidek@redhat.com>
-+
-+    Copyright (C) 2017 Red Hat
-+
-+    Config file validators test
-+
-+    This program is free software; you can redistribute it and/or modify
-+    it under the terms of the GNU General Public License as published by
-+    the Free Software Foundation; either version 3 of the License, or
-+    (at your option) any later version.
-+
-+    This program is distributed in the hope that it will be useful,
-+    but WITHOUT ANY WARRANTY; without even the implied warranty of
-+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+    GNU General Public License for more details.
-+
-+    You should have received a copy of the GNU General Public License
-+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
-+*/
-+
-+#include <popt.h>
-+#include <talloc.h>
-+#include <ini_configobj.h>
-+
-+#include "util/sss_ini.h"
-+#include "tests/cmocka/common_mock.h"
-+
-+#ifdef HAVE_LIBINI_CONFIG_V1_3
-+
-+#define RULES_PATH ABS_SRC_DIR"/src/config/cfg_rules.ini"
-+
-+struct sss_ini_initdata {
-+    char **error_list;
-+    struct ref_array *ra_success_list;
-+    struct ref_array *ra_error_list;
-+    struct ini_cfgobj *sssd_config;
-+    struct value_obj *obj;
-+    const struct stat *cstat;
-+    struct ini_cfgfile *file;
-+};
-+
-+void config_check_test_common(const char *cfg_string,
-+                              size_t num_errors_expected,
-+                              const char **errors_expected)
-+{
-+    struct sss_ini_initdata *init_data;
-+    size_t num_errors;
-+    char **strs;
-+    int ret;
-+    TALLOC_CTX *tmp_ctx;
-+
-+    tmp_ctx = talloc_new(NULL);
-+    assert_non_null(tmp_ctx);
-+
-+    init_data = sss_ini_initdata_init(tmp_ctx);
-+
-+    ret = ini_config_file_from_mem(discard_const(cfg_string),
-+                                   strlen(cfg_string),
-+                                   &init_data->file);
-+    assert_int_equal(ret, EOK);
-+
-+    ret = ini_config_create(&(init_data->sssd_config));
-+    assert_int_equal(ret, EOK);
-+
-+    ret = ini_config_parse(init_data->file,
-+                           INI_STOP_ON_ANY,
-+                           INI_MV1S_OVERWRITE,
-+                           INI_PARSE_NOWRAP,
-+                           init_data->sssd_config);
-+    assert_int_equal(ret, EOK);
-+
-+    ret = sss_ini_call_validators_strs(tmp_ctx, init_data,
-+                                       RULES_PATH,
-+                                       &strs, &num_errors);
-+    assert_int_equal(ret, EOK);
-+
-+    /* Output from validators */
-+    for (int i = 0; i < num_errors; i++) {
-+        /* Keep this printf loop for faster debugging */
-+        printf("%s\n", strs[i]);
-+    }
-+
-+    for (int i = 0; i < num_errors && i <= num_errors_expected; i++) {
-+        assert_string_equal(strs[i], errors_expected[i]);
-+    }
-+
-+    /* Check if the number of errors is the same */
-+    assert_int_equal(num_errors_expected, num_errors);
-+
-+    sss_ini_close_file(init_data);
-+    sss_ini_config_destroy(init_data);
-+    talloc_free(tmp_ctx);
-+}
-+
-+void config_check_test_bad_section_name(void **state)
-+{
-+    char cfg_str[] = "[sssssssssssssd]";
-+    const char *expected_errors[] = {
-+        "[rule/allowed_sections]: Section [sssssssssssssd] is not allowed. "
-+        "Check for typos.",
-+    };
-+
-+    config_check_test_common(cfg_str, 1, expected_errors);
-+}
-+
-+void config_check_test_bad_sssd_option_name(void **state)
-+{
-+    char cfg_str[] = "[sssd]\n"
-+                     "debug_leTYPOvel = 10\n";
-+    const char *expected_errors[] = {
-+        "[rule/allowed_sssd_options]: Attribute 'debug_leTYPOvel' is not "
-+        "allowed in section 'sssd'. Check for typos.",
-+    };
-+
-+    config_check_test_common(cfg_str, 1, expected_errors);
-+}
-+
-+void config_check_test_bad_pam_option_name(void **state)
-+{
-+    char cfg_str[] = "[pam]\n"
-+                     "debug_leTYPOvel = 10\n";
-+    const char *expected_errors[] = {
-+        "[rule/allowed_pam_options]: Attribute 'debug_leTYPOvel' is not "
-+        "allowed in section 'pam'. Check for typos.",
-+    };
-+
-+    config_check_test_common(cfg_str, 1, expected_errors);
-+}
-+
-+void config_check_test_bad_nss_option_name(void **state)
-+{
-+    char cfg_str[] = "[nss]\n"
-+                     "debug_leTYPOvel = 10\n";
-+    const char *expected_errors[] = {
-+        "[rule/allowed_nss_options]: Attribute 'debug_leTYPOvel' is not "
-+        "allowed in section 'nss'. Check for typos.",
-+    };
-+
-+    config_check_test_common(cfg_str, 1, expected_errors);
-+}
-+
-+void config_check_test_bad_pac_option_name(void **state)
-+{
-+    char cfg_str[] = "[pac]\n"
-+                     "debug_leTYPOvel = 10\n";
-+    const char *expected_errors[] = {
-+        "[rule/allowed_pac_options]: Attribute 'debug_leTYPOvel' is not "
-+        "allowed in section 'pac'. Check for typos.",
-+    };
-+
-+    config_check_test_common(cfg_str, 1, expected_errors);
-+}
-+
-+void config_check_test_bad_ifp_option_name(void **state)
-+{
-+    char cfg_str[] = "[ifp]\n"
-+                     "debug_leTYPOvel = 10\n";
-+    const char *expected_errors[] = {
-+        "[rule/allowed_ifp_options]: Attribute 'debug_leTYPOvel' is not "
-+        "allowed in section 'ifp'. Check for typos.",
-+    };
-+
-+    config_check_test_common(cfg_str, 1, expected_errors);
-+}
-+
-+void config_check_test_bad_domain_option_name(void **state)
-+{
-+    char cfg_str[] = "[domain/A.test\n"
-+                     "debug_leTYPOvel = 10\n";
-+    const char *expected_errors[] = {
-+        "[rule/allowed_subdomain_options]: Attribute 'debug_leTYPOvel' is not "
-+        "allowed in section 'domain/A.test'. Check for typos.",
-+    };
-+
-+    config_check_test_common(cfg_str, 1, expected_errors);
-+}
-+
-+void config_check_test_bad_appdomain_option_name(void **state)
-+{
-+    char cfg_str[] = "[application/myapp\n"
-+                     "debug_leTYPOvel = 10\n";
-+    const char *expected_errors[] = {
-+        "[rule/allowed_subdomain_options]: Attribute 'debug_leTYPOvel' is not "
-+        "allowed in section 'application/myapp'. Check for typos.",
-+    };
-+
-+    config_check_test_common(cfg_str, 1, expected_errors);
-+}
-+
-+void config_check_test_bad_subdom_option_name(void **state)
-+{
-+    char cfg_str[] = "[domain/A.test/B.A.test]\n"
-+                     "debug_leTYPOvel = 10\n";
-+    const char *expected_errors[] = {
-+        "[rule/allowed_sssd_options]: Attribute 'debug_leTYPOvel' is not "
-+        "allowed in section 'domain/A.test/B.A.test'. Check for typos.",
-+    };
-+
-+    config_check_test_common(cfg_str, 1, expected_errors);
-+}
-+
-+void config_check_test_good_sections(void **state)
-+{
-+    char cfg_str[] = "[sssd]\n"
-+                     "[pam]\n"
-+                     "[nss]\n"
-+                     "[domain/testdom.test]\n"
-+                     "[domain/testdom.test/testsubdom.testdom.test]\n"
-+                     "[application/myapp]\n"
-+                     "[secrets]\n"
-+                     "[ifp]\n"
-+                     "[pac]\n";
-+    const char *expected_errors[] = { NULL };
-+
-+    config_check_test_common(cfg_str, 0, expected_errors);
-+}
-+
-+int main(int argc, const char *argv[])
-+{
-+    poptContext pc;
-+    int opt;
-+    struct poptOption long_options[] = {
-+        POPT_AUTOHELP
-+        SSSD_DEBUG_OPTS
-+        POPT_TABLEEND
-+    };
-+
-+    const struct CMUnitTest tests[] = {
-+        cmocka_unit_test(config_check_test_bad_section_name),
-+        cmocka_unit_test(config_check_test_bad_sssd_option_name),
-+        cmocka_unit_test(config_check_test_bad_pam_option_name),
-+        cmocka_unit_test(config_check_test_bad_nss_option_name),
-+        cmocka_unit_test(config_check_test_bad_pac_option_name),
-+        cmocka_unit_test(config_check_test_bad_ifp_option_name),
-+        cmocka_unit_test(config_check_test_good_sections),
-+    };
-+
-+    /* Set debug level to invalid value so we can decide if -d 0 was used. */
-+    debug_level = SSSDBG_INVALID;
-+
-+    pc = poptGetContext(argv[0], argc, argv, long_options, 0);
-+    while ((opt = poptGetNextOpt(pc)) != -1) {
-+        switch (opt) {
-+        default:
-+            fprintf(stderr, "\nInvalid option %s: %s\n\n",
-+                    poptBadOption(pc, 0), poptStrerror(opt));
-+            poptPrintUsage(pc, stderr, 0);
-+            return 1;
-+        }
-+    }
-+    poptFreeContext(pc);
-+
-+    DEBUG_CLI_INIT(debug_level);
-+    tests_set_cwd();
-+    return cmocka_run_group_tests(tests, NULL, NULL);
-+}
-+
-+#else /* !HAVE_LIBINI_CONFIG_V1_3 */
-+
-+int main(int argc, const char *argv[])
-+{
-+    fprintf(stderr, "%s requires newer version of libini\n", argv[0]);
-+    return 0;
-+}
-+
-+#endif /* HAVE_LIBINI_CONFIG_V1_3 */
--- 
-2.9.4
-
diff --git a/SOURCES/0147-MAN-Fix-typo-in-trusted-domain-section.patch b/SOURCES/0147-MAN-Fix-typo-in-trusted-domain-section.patch
deleted file mode 100644
index 3d015fc..0000000
--- a/SOURCES/0147-MAN-Fix-typo-in-trusted-domain-section.patch
+++ /dev/null
@@ -1,29 +0,0 @@
-From 8a6087c3b53bbe26cb212e60af74da981529f57d Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Michal=20=C5=BDidek?= <mzidek@redhat.com>
-Date: Tue, 30 May 2017 12:05:39 +0200
-Subject: [PATCH 147/152] MAN: Fix typo in trusted domain section
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Reviewed-by: Lukáš Slebodník <lslebodn@redhat.com>
----
- src/man/sssd.conf.5.xml | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
-diff --git a/src/man/sssd.conf.5.xml b/src/man/sssd.conf.5.xml
-index c4e30396f16c40db37af2f56ac218b6e37201ef7..a35f2807eac8bb89d6cb1dd0a48f738d71a7578f 100644
---- a/src/man/sssd.conf.5.xml
-+++ b/src/man/sssd.conf.5.xml
-@@ -2912,7 +2912,7 @@ ldap_user_extra_attrs = phone:telephoneNumber
-         <para>
-             Some options used in the domain section can also be used in the
-             trusted domain section, that is, in a section called
--            <quote>[domain/<replaceable>DOMAIN_NAME</replaceable>]/<replaceable>TRUSTED_DOMAIN_NAME</replaceable>]</quote>.
-+            <quote>[domain/<replaceable>DOMAIN_NAME</replaceable>/<replaceable>TRUSTED_DOMAIN_NAME</replaceable>]</quote>.
-             Currently supported options in the trusted domain section are:
-         </para>
-             <para>ldap_search_base,</para>
--- 
-2.9.4
-
diff --git a/SOURCES/0148-VALIDATORS-Change-regex-for-app-domains.patch b/SOURCES/0148-VALIDATORS-Change-regex-for-app-domains.patch
deleted file mode 100644
index 3ca41ad..0000000
--- a/SOURCES/0148-VALIDATORS-Change-regex-for-app-domains.patch
+++ /dev/null
@@ -1,36 +0,0 @@
-From b32bb7226b89777063e4cd49373ce86353abd74c Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Michal=20=C5=BDidek?= <mzidek@redhat.com>
-Date: Tue, 30 May 2017 13:17:45 +0200
-Subject: [PATCH 148/152] VALIDATORS: Change regex for app domains
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Use the same restrictions for application domains that we use for
-normal domain.
-
-Resolves:
-https://pagure.io/SSSD/sssd/issue/3356
-
-Reviewed-by: Lukáš Slebodník <lslebodn@redhat.com>
----
- src/config/cfg_rules.ini | 3 ++-
- 1 file changed, 2 insertions(+), 1 deletion(-)
-
-diff --git a/src/config/cfg_rules.ini b/src/config/cfg_rules.ini
-index 628f2e0e0a040bad5128d00d9348aa91170ed704..2c8c0cb98ed039c374c827775798f61369c1521e 100644
---- a/src/config/cfg_rules.ini
-+++ b/src/config/cfg_rules.ini
-@@ -13,7 +13,8 @@ section = kcm
- section_re = ^secrets/users/[0-9]\+$
- section_re = ^domain/[^/\@]\+$
- section_re = ^domain/[^/\@]\+/[^/\@]\+$
--section_re = ^application/.*$
-+section_re = ^application/[^/\@]\+$
-+
- 
- [rule/allowed_sssd_options]
- validator = ini_allowed_options
--- 
-2.9.4
-
diff --git a/SOURCES/0149-VALIDATORS-Detect-inherit_from-in-normal-domain.patch b/SOURCES/0149-VALIDATORS-Detect-inherit_from-in-normal-domain.patch
deleted file mode 100644
index b99885a..0000000
--- a/SOURCES/0149-VALIDATORS-Detect-inherit_from-in-normal-domain.patch
+++ /dev/null
@@ -1,154 +0,0 @@
-From b94b578fac8f94d42fd6fb691438d2dbe5248309 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Michal=20=C5=BDidek?= <mzidek@redhat.com>
-Date: Wed, 31 May 2017 14:21:02 +0200
-Subject: [PATCH 149/152] VALIDATORS: Detect inherit_from in normal domain
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-This patch adds new sssd specific validator. In the future we
-can add more checks in it, but currently it only checks if
-the option inherit_from is used on normal domain and reports
-error if it is.
-
-Resolves:
-https://pagure.io/SSSD/sssd/issue/3356
-
-Reviewed-by: Lukáš Slebodník <lslebodn@redhat.com>
----
- src/config/cfg_rules.ini             |  3 ++
- src/tests/cmocka/test_config_check.c | 22 +++++++++++++++
- src/util/sss_ini.c                   | 53 +++++++++++++++++++++++++++++++++++-
- 3 files changed, 77 insertions(+), 1 deletion(-)
-
-diff --git a/src/config/cfg_rules.ini b/src/config/cfg_rules.ini
-index 2c8c0cb98ed039c374c827775798f61369c1521e..744446478e5d5489cd86d8e15ce8e178cf5e3a91 100644
---- a/src/config/cfg_rules.ini
-+++ b/src/config/cfg_rules.ini
-@@ -711,3 +711,6 @@ option = ad_server
- option = ad_backup_server
- option = ad_site
- option = use_fully_qualified_names
-+
-+[rule/sssd_checks]
-+validator = sssd_checks
-diff --git a/src/tests/cmocka/test_config_check.c b/src/tests/cmocka/test_config_check.c
-index 8fc0b01f3ef3fe03152efd979a3e96c21ba567cc..bab3226c004fb9495471af7c7d3f6861552d8a86 100644
---- a/src/tests/cmocka/test_config_check.c
-+++ b/src/tests/cmocka/test_config_check.c
-@@ -217,6 +217,27 @@ void config_check_test_good_sections(void **state)
-     config_check_test_common(cfg_str, 0, expected_errors);
- }
- 
-+void config_check_test_inherit_from_in_normal_dom(void **state)
-+{
-+    char cfg_str[] = "[domain/A.test]\n"
-+                     "inherit_from = domain\n";
-+    const char *expected_errors[] = {
-+        "[rule/sssd_checks]: Attribute 'inherit_from' is not allowed in "
-+        "section 'domain/A.test'. Check for typos.",
-+    };
-+
-+    config_check_test_common(cfg_str, 1, expected_errors);
-+}
-+
-+void config_check_test_inherit_from_in_app_dom(void **state)
-+{
-+    char cfg_str[] = "[application/A.test]\n"
-+                     "inherit_from = domain\n";
-+    const char *expected_errors[] = { NULL };
-+
-+    config_check_test_common(cfg_str, 0, expected_errors);
-+}
-+
- int main(int argc, const char *argv[])
- {
-     poptContext pc;
-@@ -235,6 +256,7 @@ int main(int argc, const char *argv[])
-         cmocka_unit_test(config_check_test_bad_pac_option_name),
-         cmocka_unit_test(config_check_test_bad_ifp_option_name),
-         cmocka_unit_test(config_check_test_good_sections),
-+        cmocka_unit_test(config_check_test_inherit_from_in_normal_dom),
-     };
- 
-     /* Set debug level to invalid value so we can decide if -d 0 was used. */
-diff --git a/src/util/sss_ini.c b/src/util/sss_ini.c
-index e56006c05555d6e0c5e726e83771abce5a72b139..175a4cfaba7ea964aee174e928d5e3c1e81de638 100644
---- a/src/util/sss_ini.c
-+++ b/src/util/sss_ini.c
-@@ -561,12 +561,63 @@ error:
- }
- 
- #ifdef HAVE_LIBINI_CONFIG_V1_3
-+/* Here we can put custom SSSD specific checks that can not be implemented
-+ * using libini validators */
-+static int custom_sssd_checks(const char *rule_name,
-+                              struct ini_cfgobj *rules_obj,
-+                              struct ini_cfgobj *config_obj,
-+                              struct ini_errobj *errobj,
-+                              void **data)
-+{
-+    char **cfg_sections = NULL;
-+    int num_cfg_sections;
-+    struct value_obj *vo = NULL;
-+    char dom_prefix[] = "domain/";
-+    int ret;
-+
-+    /* Get all sections in configuration */
-+    cfg_sections = ini_get_section_list(config_obj, &num_cfg_sections, &ret);
-+    if (ret != EOK) {
-+        goto done;
-+    }
-+
-+    /* Check if a normal domain section (not application domains) has option
-+     * inherit_from and report error if it does */
-+    for (int i = 0; i < num_cfg_sections; i++) {
-+        if (strncmp(dom_prefix, cfg_sections[i], strlen(dom_prefix)) == 0) {
-+            ret = ini_get_config_valueobj(cfg_sections[i],
-+                                          "inherit_from",
-+                                          config_obj,
-+                                          INI_GET_NEXT_VALUE,
-+                                          &vo);
-+            if (vo != NULL) {
-+                ret = ini_errobj_add_msg(errobj,
-+                                         "Attribute 'inherit_from' is not "
-+                                         "allowed in section '%s'. Check for "
-+                                         "typos.",
-+                                         cfg_sections[i]);
-+                if (ret != EOK) {
-+                    goto done;
-+                }
-+            }
-+        }
-+    }
-+
-+    ret = EOK;
-+done:
-+    ini_free_section_list(cfg_sections);
-+    return EOK;
-+}
-+
- static int sss_ini_call_validators_errobj(struct sss_ini_initdata *data,
-                                           const char *rules_path,
-                                           struct ini_errobj *errobj)
- {
-     int ret;
-     struct ini_cfgobj *rules_cfgobj = NULL;
-+    struct ini_validator custom_sssd = { "sssd_checks", custom_sssd_checks,
-+                                         NULL };
-+    struct ini_validator *sss_validators[] = { &custom_sssd, NULL };
- 
-     ret = ini_rules_read_from_file(rules_path, &rules_cfgobj);
-     if (ret != EOK) {
-@@ -575,7 +626,7 @@ static int sss_ini_call_validators_errobj(struct sss_ini_initdata *data,
-         goto done;
-     }
- 
--    ret = ini_rules_check(rules_cfgobj, data->sssd_config, NULL, errobj);
-+    ret = ini_rules_check(rules_cfgobj, data->sssd_config, sss_validators, errobj);
-     if (ret != EOK) {
-         DEBUG(SSSDBG_FATAL_FAILURE,
-               "ini_rules_check failed %d [%s]\n", ret, strerror(ret));
--- 
-2.9.4
-
diff --git a/SOURCES/0150-VALIDATOR-prevent-duplicite-report-from-subdomain-se.patch b/SOURCES/0150-VALIDATOR-prevent-duplicite-report-from-subdomain-se.patch
deleted file mode 100644
index 8ce1bb0..0000000
--- a/SOURCES/0150-VALIDATOR-prevent-duplicite-report-from-subdomain-se.patch
+++ /dev/null
@@ -1,36 +0,0 @@
-From 03bfcf1746b163fa3fbce9f2741db77064ac84e7 Mon Sep 17 00:00:00 2001
-From: Lukas Slebodnik <lslebodn@redhat.com>
-Date: Wed, 31 May 2017 17:35:27 +0200
-Subject: [PATCH 150/152] VALIDATOR: prevent duplicite report from subdomain
- sections
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Issues is subdomain sections e.g. "[domain/A.test/B.A.test]" were
-reported twice.
-
-[rule/allowed_domain_options]: Attribute 'debug_leTYPOvel' is not allowed in section 'domain/A.test/B.A.test'. Check for typos.
-[rule/allowed_subdomain_options]: Attribute 'debug_leTYPOvel' is not allowed in section 'domain/A.test/B.A.test'. Check for typos.
-
-Reviewed-by: Michal Židek <mzidek@redhat.com>
----
- src/config/cfg_rules.ini | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
-diff --git a/src/config/cfg_rules.ini b/src/config/cfg_rules.ini
-index 744446478e5d5489cd86d8e15ce8e178cf5e3a91..d6506b7c3cee13f7c5400a546deb787e755abc8b 100644
---- a/src/config/cfg_rules.ini
-+++ b/src/config/cfg_rules.ini
-@@ -296,7 +296,7 @@ option = responder_idle_timeout
- 
- [rule/allowed_domain_options]
- validator = ini_allowed_options
--section_re = ^\(domain\|application\)/.*$
-+section_re = ^\(domain\|application\)/[^/]\+$
- 
- option = debug
- option = debug_level
--- 
-2.9.4
-
diff --git a/SOURCES/0151-test_config_check-Fix-few-issues.patch b/SOURCES/0151-test_config_check-Fix-few-issues.patch
deleted file mode 100644
index 9e48ed1..0000000
--- a/SOURCES/0151-test_config_check-Fix-few-issues.patch
+++ /dev/null
@@ -1,92 +0,0 @@
-From 15f997c22228f4b87a841148bf05c6911107879c Mon Sep 17 00:00:00 2001
-From: Lukas Slebodnik <lslebodn@redhat.com>
-Date: Wed, 31 May 2017 17:16:47 +0200
-Subject: [PATCH 151/152] test_config_check: Fix few issues
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-* enable few tests
-* malformed configuration file due to missing closing ']'
-* fix few expected failures
-* add few sections into whitelist test
-* crash in test if count of expected failures is different then real
-  value
-
-[ RUN      ] config_check_test_bad_subdom_option_name
-[rule/allowed_domain_options]: Attribute 'debug_leTYPOvel' is not allowed in section 'domain/A.test/B.A.test'. Check for typos.
-[rule/allowed_subdomain_options]: Attribute 'debug_leTYPOvel' is not allowed in section 'domain/A.test/B.A.test'. Check for typos.
-[  ERROR   ] --- Test failed with exception: Segmentation fault(11)
-
-Reviewed-by: Michal Židek <mzidek@redhat.com>
----
- src/tests/cmocka/test_config_check.c | 14 ++++++++++----
- 1 file changed, 10 insertions(+), 4 deletions(-)
-
-diff --git a/src/tests/cmocka/test_config_check.c b/src/tests/cmocka/test_config_check.c
-index bab3226c004fb9495471af7c7d3f6861552d8a86..0066ebe77e9f174449461caebdb3359380bc19b5 100644
---- a/src/tests/cmocka/test_config_check.c
-+++ b/src/tests/cmocka/test_config_check.c
-@@ -81,6 +81,7 @@ void config_check_test_common(const char *cfg_string,
-         /* Keep this printf loop for faster debugging */
-         printf("%s\n", strs[i]);
-     }
-+    assert_int_equal(num_errors, num_errors_expected);
- 
-     for (int i = 0; i < num_errors && i <= num_errors_expected; i++) {
-         assert_string_equal(strs[i], errors_expected[i]);
-@@ -167,7 +168,7 @@ void config_check_test_bad_ifp_option_name(void **state)
- 
- void config_check_test_bad_domain_option_name(void **state)
- {
--    char cfg_str[] = "[domain/A.test\n"
-+    char cfg_str[] = "[domain/A.test]\n"
-                      "debug_leTYPOvel = 10\n";
-     const char *expected_errors[] = {
-         "[rule/allowed_subdomain_options]: Attribute 'debug_leTYPOvel' is not "
-@@ -179,10 +180,10 @@ void config_check_test_bad_domain_option_name(void **state)
- 
- void config_check_test_bad_appdomain_option_name(void **state)
- {
--    char cfg_str[] = "[application/myapp\n"
-+    char cfg_str[] = "[application/myapp]\n"
-                      "debug_leTYPOvel = 10\n";
-     const char *expected_errors[] = {
--        "[rule/allowed_subdomain_options]: Attribute 'debug_leTYPOvel' is not "
-+        "[rule/allowed_domain_options]: Attribute 'debug_leTYPOvel' is not "
-         "allowed in section 'application/myapp'. Check for typos.",
-     };
- 
-@@ -194,7 +195,7 @@ void config_check_test_bad_subdom_option_name(void **state)
-     char cfg_str[] = "[domain/A.test/B.A.test]\n"
-                      "debug_leTYPOvel = 10\n";
-     const char *expected_errors[] = {
--        "[rule/allowed_sssd_options]: Attribute 'debug_leTYPOvel' is not "
-+        "[rule/allowed_subdomain_options]: Attribute 'debug_leTYPOvel' is not "
-         "allowed in section 'domain/A.test/B.A.test'. Check for typos.",
-     };
- 
-@@ -210,6 +211,8 @@ void config_check_test_good_sections(void **state)
-                      "[domain/testdom.test/testsubdom.testdom.test]\n"
-                      "[application/myapp]\n"
-                      "[secrets]\n"
-+                     "[secrets/users/1000]\n"
-+                     "[ssh]\n"
-                      "[ifp]\n"
-                      "[pac]\n";
-     const char *expected_errors[] = { NULL };
-@@ -255,8 +258,11 @@ int main(int argc, const char *argv[])
-         cmocka_unit_test(config_check_test_bad_nss_option_name),
-         cmocka_unit_test(config_check_test_bad_pac_option_name),
-         cmocka_unit_test(config_check_test_bad_ifp_option_name),
-+        cmocka_unit_test(config_check_test_bad_appdomain_option_name),
-+        cmocka_unit_test(config_check_test_bad_subdom_option_name),
-         cmocka_unit_test(config_check_test_good_sections),
-         cmocka_unit_test(config_check_test_inherit_from_in_normal_dom),
-+        cmocka_unit_test(config_check_test_inherit_from_in_app_dom),
-     };
- 
-     /* Set debug level to invalid value so we can decide if -d 0 was used. */
--- 
-2.9.4
-
diff --git a/SOURCES/0152-KRB5-Fix-access_provider-krb5.patch b/SOURCES/0152-KRB5-Fix-access_provider-krb5.patch
deleted file mode 100644
index 89845c7..0000000
--- a/SOURCES/0152-KRB5-Fix-access_provider-krb5.patch
+++ /dev/null
@@ -1,81 +0,0 @@
-From 3ee575c2852adb9d5a5c0a4616c082afc6779a8e Mon Sep 17 00:00:00 2001
-From: Jakub Hrozek <jhrozek@redhat.com>
-Date: Thu, 1 Jun 2017 09:51:31 +0200
-Subject: [PATCH 152/152] KRB5: Fix access_provider=krb5
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-The domain type (posix or not) was being sent to the krb5_child always,
-but the buffer only had enough space in case of authentication, not
-authorization. Bug was introduced in the commit
-  861ab44e8148208425b67c4711bc8fade10fd3ed
-
-This patch makes the buffer one uint32_t unit larger.
-
-To reproduce, just set up sssd.conf with:
-    access_provider = krb5
-
-Without the patch, you would see messages like:
-    ==14111== Invalid write of size 2
-    ==14111==    at 0x4C3041B: memcpy@@GLIBC_2.14 (vg_replace_strmem.c:1018)
-    ==14111==    by 0xE0EE275: safealign_memcpy (util_safealign.h:51)
-    ==14111==    by 0xE0EECB3: create_send_buffer (krb5_child_handler.c:239)
-    ==14111==    by 0xE0EFDDE: handle_child_send (krb5_child_handler.c:529)
-    ==14111==    by 0xE0EDEDD: krb5_access_send (krb5_access.c:149)
-    ==14111==    by 0xE0ED32F: krb5_pam_handler_send (krb5_auth.c:1250)
-    ==14111==    by 0x418868: file_dp_request (dp_request.c:254)
-    ==14111==    by 0x418976: dp_req_send (dp_request.c:300)
-    ==14111==    by 0x41C25F: dp_pam_handler (dp_target_auth.c:219)
-    ==14111==    by 0x52B3456: sbus_request_invoke_or_finish (sssd_dbus_request.c:71)
-    ==14111==    by 0x52B0F37: sbus_message_handler_got_caller_id (sssd_dbus_interface.c:1048)
-    ==14111==    by 0x923C923: tevent_common_loop_immediate (tevent_immediate.c:135)
-    ==14111==  Address 0x126ab506 is 150 bytes inside a block of size 151 alloc'd
-    ==14111==    at 0x4C2BBAD: malloc (vg_replace_malloc.c:299)
-    ==14111==    by 0x944D7F4: __talloc_with_prefix (talloc.c:698)
-    ==14111==    by 0x944D7F4: __talloc (talloc.c:739)
-    ==14111==    by 0x944D7F4: _talloc_named_const (talloc.c:896)
-    ==14111==    by 0x944D7F4: talloc_named_const (talloc.c:1675)
-    ==14111==    by 0xE0EE7B6: create_send_buffer (krb5_child_handler.c:185)
-    ==14111==    by 0xE0EFDDE: handle_child_send (krb5_child_handler.c:529)
-    ==14111==    by 0xE0EDEDD: krb5_access_send (krb5_access.c:149)
-    ==14111==    by 0xE0ED32F: krb5_pam_handler_send (krb5_auth.c:1250)
-    ==14111==    by 0x418868: file_dp_request (dp_request.c:254)
-    ==14111==    by 0x418976: dp_req_send (dp_request.c:300)
-    ==14111==    by 0x41C25F: dp_pam_handler (dp_target_auth.c:219)
-    ==14111==    by 0x52B3456: sbus_request_invoke_or_finish (sssd_dbus_request.c:71)
-    ==14111==    by 0x52B0F37: sbus_message_handler_got_caller_id (sssd_dbus_interface.c:1048)
-    ==14111==    by 0x923C923: tevent_common_loop_immediate (tevent_immediate.c:135)
-
-Resolves:
-https://pagure.io/SSSD/sssd/issue/3418
-
-Reviewed-by: Lukáš Slebodník <lslebodn@redhat.com>
----
- src/providers/krb5/krb5_child_handler.c | 4 ++--
- 1 file changed, 2 insertions(+), 2 deletions(-)
-
-diff --git a/src/providers/krb5/krb5_child_handler.c b/src/providers/krb5/krb5_child_handler.c
-index 87e79a06e917aadb622455bccfc2e9c6769f70c2..11ac867e62d2ff96b827cf6d4ff341fc8ff0a286 100644
---- a/src/providers/krb5/krb5_child_handler.c
-+++ b/src/providers/krb5/krb5_child_handler.c
-@@ -156,14 +156,14 @@ static errno_t create_send_buffer(struct krb5child_req *kr,
-         return ENOMEM;
-     }
- 
--    buf->size = 8*sizeof(uint32_t) + strlen(kr->upn);
-+    buf->size = 9*sizeof(uint32_t) + strlen(kr->upn);
- 
-     if (kr->pd->cmd == SSS_PAM_AUTHENTICATE ||
-         kr->pd->cmd == SSS_PAM_PREAUTH ||
-         kr->pd->cmd == SSS_CMD_RENEW ||
-         kr->pd->cmd == SSS_PAM_CHAUTHTOK_PRELIM ||
-         kr->pd->cmd == SSS_PAM_CHAUTHTOK) {
--        buf->size += 5*sizeof(uint32_t) + strlen(kr->ccname) + strlen(keytab) +
-+        buf->size += 4*sizeof(uint32_t) + strlen(kr->ccname) + strlen(keytab) +
-                      sss_authtok_get_size(kr->pd->authtok);
- 
-         buf->size += sizeof(uint32_t);
--- 
-2.9.4
-
diff --git a/SOURCES/0153-BUILD-Improve-error-messages-for-optional-dependenci.patch b/SOURCES/0153-BUILD-Improve-error-messages-for-optional-dependenci.patch
deleted file mode 100644
index e06105d..0000000
--- a/SOURCES/0153-BUILD-Improve-error-messages-for-optional-dependenci.patch
+++ /dev/null
@@ -1,86 +0,0 @@
-From 9581287c1b5e13a38182af12328ace781957a118 Mon Sep 17 00:00:00 2001
-From: Lukas Slebodnik <lslebodn@redhat.com>
-Date: Tue, 30 May 2017 14:40:07 +0200
-Subject: [PATCH 153/160] BUILD: Improve error messages for optional
- dependencies
-
-Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
-(cherry picked from commit 8ccc9b7c317cf5ee8f295b38bfc4c2b7d551f8f1)
----
- configure.ac               | 2 +-
- contrib/sssd.spec.in       | 6 +++++-
- src/external/libcurl.m4    | 6 +++++-
- src/external/libjansson.m4 | 5 +++--
- 4 files changed, 14 insertions(+), 5 deletions(-)
-
-diff --git a/configure.ac b/configure.ac
-index 80d8ea9ff5785b0d76edbb04f454d0dd8c8a1e6d..e8fe1d47e1803cc570295cf6512a3363e63c51c5 100644
---- a/configure.ac
-+++ b/configure.ac
-@@ -197,7 +197,6 @@ m4_include([src/external/service.m4])
- 
- if test x$with_secrets = xyes; then
-     m4_include([src/external/libhttp_parser.m4])
--    m4_include([src/external/libjansson.m4])
- fi
- 
- if test x$with_kcm = xyes; then
-@@ -206,6 +205,7 @@ fi
- 
- if test x$with_kcm = xyes -o x$with_secrets = xyes; then
-     m4_include([src/external/libcurl.m4])
-+    m4_include([src/external/libjansson.m4])
- fi
- 
- # This variable is defined by external/libcurl.m4, but conditionals
-diff --git a/contrib/sssd.spec.in b/contrib/sssd.spec.in
-index 39a974edebba3dbcd7625d1729b4a7330eaa8a27..b19702d091862e25bea352901b85406ccda1db65 100644
---- a/contrib/sssd.spec.in
-+++ b/contrib/sssd.spec.in
-@@ -228,10 +228,14 @@ BuildRequires: systemtap-sdt-devel
- %endif
- %if (0%{?with_secrets} == 1)
- BuildRequires: http-parser-devel
--BuildRequires: jansson-devel
- %endif
-+%if (0%{?with_kcm} == 1)
- BuildRequires: libuuid-devel
-+%endif
-+%if (0%{?with_secrets} == 1 || 0%{?with_kcm} == 1)
-+BuildRequires: jansson-devel
- BuildRequires: libcurl-devel
-+%endif
- 
- %description
- Provides a set of daemons to manage access to remote directories and
-diff --git a/src/external/libcurl.m4 b/src/external/libcurl.m4
-index 42be308cd1e4b04e736daf887be9b75ea92db80e..94cea9ebe40f07c18452b8c2faf82e81e1dc766b 100644
---- a/src/external/libcurl.m4
-+++ b/src/external/libcurl.m4
-@@ -1,5 +1,9 @@
- PKG_CHECK_MODULES([CURL], [libcurl], [found_libcurl=yes],
--              [AC_MSG_ERROR([The libcurl development library was not found.])])
-+              [AC_MSG_ERROR([The libcurl development library was not found.
-+You must have the header file curl/curl.h installed to build sssd
-+with secrets and KCM responder. If you want to build sssd without these
-+responders then specify --without-secrets --without-kcm when running configure.
-+])])
- 
- AS_IF([test x"$found_libcurl" = xyes],
-     CFLAGS="$CFLAGS $CURL_CFLAGS"
-diff --git a/src/external/libjansson.m4 b/src/external/libjansson.m4
-index 48a4a5fd8df4ac41312a596b5ebd5de7474e75f1..d87769848558efdd32325e01d8d222bb517b4c45 100644
---- a/src/external/libjansson.m4
-+++ b/src/external/libjansson.m4
-@@ -13,5 +13,6 @@ AS_IF([test x"$found_jansson" != xyes],
-                       [-L$sss_extra_libdir -ljanson])],
-         [AC_MSG_ERROR([
- You must have the header file jansson.h installed to build sssd
--with secrets responder. If you want to build sssd without secret responder
--then specify --without-secrets when running configure.])])])
-+with secrets and KCM responder. If you want to build sssd without these
-+responders then specify --without-secrets --without-kcm when running configure.
-+])])])
--- 
-2.9.4
-
diff --git a/SOURCES/0154-RESPONDER_COMMON-update-certmaps-in-responders.patch b/SOURCES/0154-RESPONDER_COMMON-update-certmaps-in-responders.patch
deleted file mode 100644
index 0b1ddaa..0000000
--- a/SOURCES/0154-RESPONDER_COMMON-update-certmaps-in-responders.patch
+++ /dev/null
@@ -1,77 +0,0 @@
-From d363bd0f829fa7af5f96c2b07b975b7b2c5fdcfa Mon Sep 17 00:00:00 2001
-From: Sumit Bose <sbose@redhat.com>
-Date: Tue, 2 May 2017 15:25:10 +0200
-Subject: [PATCH 154/160] RESPONDER_COMMON: update certmaps in responders
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Make certificate mapping data available to the responders.
-
-Related to https://pagure.io/SSSD/sssd/issue/3395
-
-Reviewed-by: Fabiano Fidêncio <fidencio@redhat.com>
-(cherry picked from commit 749963195393efa3a4f9b168dd02fbcc68976ba3)
----
- src/confdb/confdb.h                          |  3 +++
- src/responder/common/responder_get_domains.c | 23 +++++++++++++++++++++++
- 2 files changed, 26 insertions(+)
-
-diff --git a/src/confdb/confdb.h b/src/confdb/confdb.h
-index 8719c239362b371fcdb1b78956bcddde871f141b..797353141edcccbf3341d161ca598c99492e54fe 100644
---- a/src/confdb/confdb.h
-+++ b/src/confdb/confdb.h
-@@ -351,6 +351,9 @@ struct sss_domain_info {
-     char *forest;
-     struct sss_domain_info *forest_root;
-     const char **upn_suffixes;
-+
-+    struct certmap_info **certmaps;
-+    bool user_name_hint;
- };
- 
- /**
-diff --git a/src/responder/common/responder_get_domains.c b/src/responder/common/responder_get_domains.c
-index 8c90b7773e248e1dd6d846c5050e1931fc50c786..155631676d9449f69865919e1b74ee9399607c27 100644
---- a/src/responder/common/responder_get_domains.c
-+++ b/src/responder/common/responder_get_domains.c
-@@ -224,6 +224,26 @@ immediately:
-     return req;
- }
- 
-+static void sss_resp_update_certmaps(struct resp_ctx *rctx)
-+{
-+    int ret;
-+    struct certmap_info **certmaps;
-+    bool user_name_hint;
-+    struct sss_domain_info *dom;
-+
-+    for (dom = rctx->domains; dom != NULL; dom = dom->next) {
-+        ret = sysdb_get_certmap(dom, dom->sysdb, &certmaps, &user_name_hint);
-+        if (ret == EOK) {
-+            dom->user_name_hint = user_name_hint;
-+            talloc_free(dom->certmaps);
-+            dom->certmaps = certmaps;
-+        } else {
-+            DEBUG(SSSDBG_OP_FAILURE,
-+                  "sysdb_get_certmap failed for domain [%s].\n", dom->name);
-+        }
-+    }
-+}
-+
- static void
- sss_dp_get_domains_process(struct tevent_req *subreq)
- {
-@@ -267,6 +287,9 @@ sss_dp_get_domains_process(struct tevent_req *subreq)
-                   ret, sss_strerror(ret));
-             goto fail;
-         }
-+
-+        sss_resp_update_certmaps(state->rctx);
-+
-         tevent_req_done(req);
-         return;
-     }
--- 
-2.9.4
-
diff --git a/SOURCES/0155-tests-fix-test_pam_preauth_cert_no_logon_name.patch b/SOURCES/0155-tests-fix-test_pam_preauth_cert_no_logon_name.patch
deleted file mode 100644
index dc6a41c..0000000
--- a/SOURCES/0155-tests-fix-test_pam_preauth_cert_no_logon_name.patch
+++ /dev/null
@@ -1,43 +0,0 @@
-From 7487682e505735f2143ccecfc5e7e0fc2dac37f2 Mon Sep 17 00:00:00 2001
-From: Sumit Bose <sbose@redhat.com>
-Date: Mon, 8 May 2017 15:28:20 +0200
-Subject: [PATCH 155/160] tests: fix test_pam_preauth_cert_no_logon_name()
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Currently a name is provided for test_pam_preauth_cert_no_logon_name()
-so it is not a no-logon-name test. This patch removes the name and adds
-the now missing mocked reply manually.
-
-Reviewed-by: Fabiano Fidêncio <fidencio@redhat.com>
-(cherry picked from commit 89ff140d7ab92fce52d6730a7d27c8d73c7d9e4a)
----
- src/tests/cmocka/test_pam_srv.c | 10 +++++++---
- 1 file changed, 7 insertions(+), 3 deletions(-)
-
-diff --git a/src/tests/cmocka/test_pam_srv.c b/src/tests/cmocka/test_pam_srv.c
-index 4d351a3707d2a49604595b728fff7705560c871a..35afbdd81d004236885ee80914771ccb4b8acff4 100644
---- a/src/tests/cmocka/test_pam_srv.c
-+++ b/src/tests/cmocka/test_pam_srv.c
-@@ -1873,10 +1873,14 @@ void test_pam_preauth_cert_no_logon_name(void **state)
-      * Since there is a matching user the upcoming lookup by name will find
-      * the user entry. But since we force the lookup by name to go to the
-      * backend to make sure the group-membership data is up to date the
--     * backend response has to be mocked twice and the second argument of
--     * mock_input_pam_cert cannot be NULL but must match the user name. */
--    mock_input_pam_cert(pam_test_ctx, "pamuser", NULL, NULL,
-+     * backend response has to be mocked twice.
-+     * Additionally sss_parse_inp_recv() must be mocked because the cache
-+     * request will be done with the username found by the certificate
-+     * lookup. */
-+    mock_input_pam_cert(pam_test_ctx, NULL, NULL, NULL,
-                         test_lookup_by_cert_cb, TEST_TOKEN_CERT, false);
-+    mock_account_recv_simple();
-+    mock_parse_inp("pamuser", NULL, EOK);
- 
-     will_return(__wrap_sss_packet_get_cmd, SSS_PAM_PREAUTH);
-     will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL);
--- 
-2.9.4
-
diff --git a/SOURCES/0156-pam_sss-add-support-for-SSS_PAM_CERT_INFO_WITH_HINT.patch b/SOURCES/0156-pam_sss-add-support-for-SSS_PAM_CERT_INFO_WITH_HINT.patch
deleted file mode 100644
index 94c6f8e..0000000
--- a/SOURCES/0156-pam_sss-add-support-for-SSS_PAM_CERT_INFO_WITH_HINT.patch
+++ /dev/null
@@ -1,258 +0,0 @@
-From 19cb2e2d826dc4e3c938c5a6b51a03338e80fa9e Mon Sep 17 00:00:00 2001
-From: Sumit Bose <sbose@redhat.com>
-Date: Mon, 8 May 2017 16:01:26 +0200
-Subject: [PATCH 156/160] pam_sss: add support for SSS_PAM_CERT_INFO_WITH_HINT
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-The new response type SSS_PAM_CERT_INFO_WITH_HINT is equivalent to
-SSS_PAM_CERT_INFO but tells pam_sss to prompt for an option user name as
-well.
-
-Resolves:
-https://pagure.io/SSSD/sssd/issue/3395
-
-Reviewed-by: Fabiano Fidêncio <fidencio@redhat.com>
-(cherry picked from commit a192a1d72e92dae3e71e062b333e51a5095a0395)
----
- src/sss_client/pam_message.h |   1 +
- src/sss_client/pam_sss.c     | 129 ++++++++++++++++++++++++++++++++++++++-----
- src/sss_client/sss_cli.h     |  11 +++-
- 3 files changed, 127 insertions(+), 14 deletions(-)
-
-diff --git a/src/sss_client/pam_message.h b/src/sss_client/pam_message.h
-index 3f4a770ac08ee416ead2f215ab873e8eb277c9eb..f215392f6879f01a0ca12abc8807bac5fc1f1cbb 100644
---- a/src/sss_client/pam_message.h
-+++ b/src/sss_client/pam_message.h
-@@ -63,6 +63,7 @@ struct pam_items {
-     char *token_name;
-     char *module_name;
-     char *key_id;
-+    bool user_name_hint;
- };
- 
- int pack_message_v3(struct pam_items *pi, size_t *size, uint8_t **buffer);
-diff --git a/src/sss_client/pam_sss.c b/src/sss_client/pam_sss.c
-index db0dcb9de7b893850bcea96a9cdf76dc0b36dcee..1c06079967e3d9076d537c3de8aba93e13f76d09 100644
---- a/src/sss_client/pam_sss.c
-+++ b/src/sss_client/pam_sss.c
-@@ -982,6 +982,7 @@ static int eval_response(pam_handle_t *pamh, size_t buflen, uint8_t *buf,
- 
-                 break;
-             case SSS_PAM_CERT_INFO:
-+            case SSS_PAM_CERT_INFO_WITH_HINT:
-                 if (buf[p + (len - 1)] != '\0') {
-                     D(("cert info does not end with \\0."));
-                     break;
-@@ -994,7 +995,19 @@ static int eval_response(pam_handle_t *pamh, size_t buflen, uint8_t *buf,
-                     break;
-                 }
- 
--                if (pi->pam_user == NULL || *(pi->pam_user) == '\0') {
-+                if (type == SSS_PAM_CERT_INFO && pi->cert_user == '\0') {
-+                    D(("Invalid CERT message"));
-+                    break;
-+                }
-+
-+                if (type == SSS_PAM_CERT_INFO_WITH_HINT) {
-+                    pi->user_name_hint = true;
-+                } else {
-+                    pi->user_name_hint = false;
-+                }
-+
-+                if ((pi->pam_user == NULL || *(pi->pam_user) == '\0')
-+                        && pi->cert_user != '\0') {
-                     ret = pam_set_item(pamh, PAM_USER, pi->cert_user);
-                     if (ret != PAM_SUCCESS) {
-                         D(("Failed to set PAM_USER during "
-@@ -1469,7 +1482,7 @@ done:
-     return ret;
- }
- 
--#define SC_PROMPT_FMT "PIN for %s for user %s"
-+#define SC_PROMPT_FMT "PIN for %s"
- 
- static int prompt_sc_pin(pam_handle_t *pamh, struct pam_items *pi)
- {
-@@ -1478,32 +1491,108 @@ static int prompt_sc_pin(pam_handle_t *pamh, struct pam_items *pi)
-     char *prompt;
-     size_t size;
-     size_t needed_size;
-+    const struct pam_conv *conv;
-+    const struct pam_message *mesg[2] = { NULL, NULL };
-+    struct pam_message m[2] = { { 0 }, { 0 } };
-+    struct pam_response *resp = NULL;
- 
--    if (pi->token_name == NULL || *pi->token_name == '\0'
--            || pi->cert_user == NULL || *pi->cert_user == '\0') {
-+    if (pi->token_name == NULL || *pi->token_name == '\0') {
-         return EINVAL;
-     }
- 
--    size = sizeof(SC_PROMPT_FMT) + strlen(pi->token_name) +
--           strlen(pi->cert_user);
-+    size = sizeof(SC_PROMPT_FMT) + strlen(pi->token_name);
-     prompt = malloc(size);
-     if (prompt == NULL) {
-         D(("malloc failed."));
-         return ENOMEM;
-     }
- 
--    ret = snprintf(prompt, size, SC_PROMPT_FMT, pi->token_name, pi->cert_user);
-+    ret = snprintf(prompt, size, SC_PROMPT_FMT, pi->token_name);
-     if (ret < 0 || ret >= size) {
-         D(("snprintf failed."));
-         free(prompt);
-         return EFAULT;
-     }
- 
--    ret = do_pam_conversation(pamh, PAM_PROMPT_ECHO_OFF, prompt, NULL, &answer);
--    free(prompt);
--    if (ret != PAM_SUCCESS) {
--        D(("do_pam_conversation failed."));
--        return ret;
-+    if (pi->user_name_hint) {
-+        ret = pam_get_item(pamh, PAM_CONV, (const void **)&conv);
-+        if (ret != PAM_SUCCESS) {
-+            return ret;
-+        }
-+        if (conv == NULL || conv->conv == NULL) {
-+            logger(pamh, LOG_ERR, "No conversation function");
-+            return PAM_SYSTEM_ERR;
-+        }
-+
-+        m[0].msg_style = PAM_PROMPT_ECHO_OFF;
-+        m[0].msg = prompt;
-+        m[1].msg_style = PAM_PROMPT_ECHO_ON;
-+        m[1].msg = "User name hint: ";
-+
-+        mesg[0] = (const struct pam_message *)m;
-+        /* The following assignment might look a bit odd but is recommended in the
-+         * pam_conv man page to make sure that the second argument of the PAM
-+         * conversation function can be interpreted in two different ways.
-+         * Basically it is important that both the actual struct pam_message and
-+         * the pointers to the struct pam_message are arrays. Since the assignment
-+         * makes clear that mesg[] and (*mesg)[] are arrays it should be kept this
-+         * way and not be replaced by other equivalent assignments. */
-+        mesg[1] = &((*mesg)[1]);
-+
-+        ret = conv->conv(2, mesg, &resp, conv->appdata_ptr);
-+        if (ret != PAM_SUCCESS) {
-+            D(("Conversation failure: %s.", pam_strerror(pamh, ret)));
-+            return ret;
-+        }
-+
-+        if (resp == NULL) {
-+            D(("response expected, but resp==NULL"));
-+            return PAM_SYSTEM_ERR;
-+        }
-+
-+        if (resp[0].resp == NULL || *(resp[0].resp) == '\0') {
-+            D(("Missing PIN."));
-+            ret = PAM_CRED_INSUFFICIENT;
-+            goto done;
-+        }
-+
-+        answer = strndup(resp[0].resp, MAX_AUTHTOK_SIZE);
-+        _pam_overwrite((void *)resp[0].resp);
-+        free(resp[0].resp);
-+        resp[0].resp = NULL;
-+        if (answer == NULL) {
-+            D(("strndup failed"));
-+            ret = PAM_BUF_ERR;
-+            goto done;
-+        }
-+
-+        if (resp[1].resp != NULL && *(resp[1].resp) != '\0') {
-+            ret = pam_set_item(pamh, PAM_USER, resp[1].resp);
-+            free(resp[1].resp);
-+            resp[1].resp = NULL;
-+            if (ret != PAM_SUCCESS) {
-+                D(("Failed to set PAM_USER with user name hint [%s]",
-+                   pam_strerror(pamh, ret)));
-+                goto done;
-+            }
-+
-+            ret = pam_get_item(pamh, PAM_USER, (const void **)&(pi->pam_user));
-+            if (ret != PAM_SUCCESS) {
-+                D(("Failed to get PAM_USER with user name hint [%s]",
-+                   pam_strerror(pamh, ret)));
-+                goto done;
-+            }
-+
-+            pi->pam_user_size = strlen(pi->pam_user) + 1;
-+        }
-+    } else {
-+        ret = do_pam_conversation(pamh, PAM_PROMPT_ECHO_OFF, prompt, NULL,
-+                                  &answer);
-+        free(prompt);
-+        if (ret != PAM_SUCCESS) {
-+            D(("do_pam_conversation failed."));
-+            return ret;
-+        }
-     }
- 
-     if (answer == NULL) {
-@@ -1552,6 +1641,20 @@ done:
-     free(answer);
-     answer=NULL;
- 
-+    if (resp != NULL) {
-+        if (resp[0].resp != NULL) {
-+            _pam_overwrite((void *)resp[0].resp);
-+            free(resp[0].resp);
-+        }
-+        if (resp[1].resp != NULL) {
-+            _pam_overwrite((void *)resp[1].resp);
-+            free(resp[1].resp);
-+        }
-+
-+        free(resp);
-+        resp = NULL;
-+    }
-+
-     return ret;
- }
- 
-@@ -1680,7 +1783,7 @@ static int get_authtok_for_authentication(pam_handle_t *pamh,
-                 ret = prompt_2fa(pamh, pi, _("First Factor: "),
-                                  _("Second Factor: "));
-             }
--        } else if (pi->cert_user != NULL) {
-+        } else if (pi->token_name != NULL && *(pi->token_name) != '\0') {
-             ret = prompt_sc_pin(pamh, pi);
-         } else {
-             ret = prompt_password(pamh, pi, _("Password: "));
-diff --git a/src/sss_client/sss_cli.h b/src/sss_client/sss_cli.h
-index 59fee7a4eceb2c185e156e812af7f2f4c6b2a0dd..d4198407f2f86c6594aee6a2a43775e429692df0 100644
---- a/src/sss_client/sss_cli.h
-+++ b/src/sss_client/sss_cli.h
-@@ -427,7 +427,13 @@ enum response_type {
-                           * @param Three zero terminated strings, if one of the
-                           * strings is missing the message will contain only
-                           * an empty string (\0) for that component. */
--    SSS_PAM_CERT_INFO,
-+    SSS_PAM_CERT_INFO,   /**< A message indicating that Smartcard/certificate
-+                          * based authentication is available and contains
-+                          * details about the found Smartcard.
-+                          * @param user name, zero terminated
-+                          * @param token name, zero terminated
-+                          * @param PKCS#11 module name, zero terminated
-+                          * @param key id, zero terminated */
-     SSS_OTP,             /**< Indicates that the autotok was a OTP, so don't
-                           * cache it. There is no message.
-                           * @param None. */
-@@ -442,6 +448,9 @@ enum response_type {
-                               * be used together with other prompting options
-                               * to determine the type of prompting.
-                               * @param None. */
-+    SSS_PAM_CERT_INFO_WITH_HINT, /**< Same as SSS_PAM_CERT_INFO but user name
-+                                  * might be missing and should be prompted
-+                                  * for. */
- };
- 
- /**
--- 
-2.9.4
-
diff --git a/SOURCES/0157-add_pam_cert_response-add-support-for-SSS_PAM_CERT_I.patch b/SOURCES/0157-add_pam_cert_response-add-support-for-SSS_PAM_CERT_I.patch
deleted file mode 100644
index a9a73fd..0000000
--- a/SOURCES/0157-add_pam_cert_response-add-support-for-SSS_PAM_CERT_I.patch
+++ /dev/null
@@ -1,104 +0,0 @@
-From c5c6ba2546d350a7a01a9f44bb5df9c6652a1e06 Mon Sep 17 00:00:00 2001
-From: Sumit Bose <sbose@redhat.com>
-Date: Mon, 8 May 2017 16:02:36 +0200
-Subject: [PATCH 157/160] add_pam_cert_response: add support for
- SSS_PAM_CERT_INFO_WITH_HINT
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Related to https://pagure.io/SSSD/sssd/issue/3395
-
-Reviewed-by: Fabiano Fidêncio <fidencio@redhat.com>
-(cherry picked from commit 6073cfc40747cd6d3142f0f98b880fc390dd7aad)
----
- src/responder/pam/pamsrv.h     |  2 +-
- src/responder/pam/pamsrv_cmd.c |  3 ++-
- src/responder/pam/pamsrv_p11.c | 21 +++++++++++++++------
- 3 files changed, 18 insertions(+), 8 deletions(-)
-
-diff --git a/src/responder/pam/pamsrv.h b/src/responder/pam/pamsrv.h
-index b569748fe2a2005cee5df34bef55e803175492a9..57a37b72594f030995f5e22255eb7a8fcd63d10e 100644
---- a/src/responder/pam/pamsrv.h
-+++ b/src/responder/pam/pamsrv.h
-@@ -101,7 +101,7 @@ errno_t pam_check_cert_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
- 
- errno_t add_pam_cert_response(struct pam_data *pd, const char *user,
-                               const char *token_name, const char *module_name,
--                              const char *key_id);
-+                              const char *key_id, enum response_type type);
- 
- bool may_do_cert_auth(struct pam_ctx *pctx, struct pam_data *pd);
- 
-diff --git a/src/responder/pam/pamsrv_cmd.c b/src/responder/pam/pamsrv_cmd.c
-index 36dba37964b71153435b4df5d5328de4361926e6..080cfafa709d63542fbf57d26fab11f0a367dea7 100644
---- a/src/responder/pam/pamsrv_cmd.c
-+++ b/src/responder/pam/pamsrv_cmd.c
-@@ -1846,7 +1846,8 @@ static void pam_dom_forwarder(struct pam_auth_req *preq)
-                     ret = add_pam_cert_response(preq->pd, cert_user,
-                                                 preq->token_name,
-                                                 preq->module_name,
--                                                preq->key_id);
-+                                                preq->key_id,
-+                                                SSS_PAM_CERT_INFO);
-                     if (ret != EOK) {
-                         DEBUG(SSSDBG_OP_FAILURE, "add_pam_cert_response failed.\n");
-                         preq->pd->pam_status = PAM_AUTHINFO_UNAVAIL;
-diff --git a/src/responder/pam/pamsrv_p11.c b/src/responder/pam/pamsrv_p11.c
-index 365300b9075983b603a6f9e91ba6f8f21706388f..4dce43800c3c6b026c545df35c846269cbb49610 100644
---- a/src/responder/pam/pamsrv_p11.c
-+++ b/src/responder/pam/pamsrv_p11.c
-@@ -580,7 +580,7 @@ errno_t pam_check_cert_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
- 
- errno_t add_pam_cert_response(struct pam_data *pd, const char *sysdb_username,
-                               const char *token_name, const char *module_name,
--                              const char *key_id)
-+                              const char *key_id, enum response_type type)
- {
-     uint8_t *msg = NULL;
-     char *env = NULL;
-@@ -590,14 +590,23 @@ errno_t add_pam_cert_response(struct pam_data *pd, const char *sysdb_username,
-     size_t module_len;
-     size_t key_id_len;
-     int ret;
-+    const char *username = "";
- 
--    if (sysdb_username == NULL || token_name == NULL || module_name == NULL
--            || key_id == NULL) {
-+    if (type != SSS_PAM_CERT_INFO && type != SSS_PAM_CERT_INFO_WITH_HINT) {
-+        DEBUG(SSSDBG_CRIT_FAILURE, "Invalid response type [%d].\n", type);
-+        return EINVAL;
-+    }
-+
-+    if ((type == SSS_PAM_CERT_INFO && sysdb_username == NULL)
-+            || token_name == NULL || module_name == NULL || key_id == NULL) {
-         DEBUG(SSSDBG_CRIT_FAILURE, "Missing mandatory user or slot name.\n");
-         return EINVAL;
-     }
- 
--    user_len = strlen(sysdb_username) + 1;
-+    if (sysdb_username != NULL) {
-+        username = sysdb_username;
-+    }
-+    user_len = strlen(username) + 1;
-     slot_len = strlen(token_name) + 1;
-     module_len = strlen(module_name) + 1;
-     key_id_len = strlen(key_id) + 1;
-@@ -616,12 +625,12 @@ errno_t add_pam_cert_response(struct pam_data *pd, const char *sysdb_username,
-      * re_expression config option was set in a way that user@domain cannot be
-      * handled anymore some more logic has to be added here. But for the time
-      * being I think using sysdb_username is fine. */
--    memcpy(msg, sysdb_username, user_len);
-+    memcpy(msg, username, user_len);
-     memcpy(msg + user_len, token_name, slot_len);
-     memcpy(msg + user_len + slot_len, module_name, module_len);
-     memcpy(msg + user_len + slot_len + module_len, key_id, key_id_len);
- 
--    ret = pam_add_response(pd, SSS_PAM_CERT_INFO, msg_len, msg);
-+    ret = pam_add_response(pd, type, msg_len, msg);
-     talloc_free(msg);
-     if (ret != EOK) {
-         DEBUG(SSSDBG_OP_FAILURE,
--- 
-2.9.4
-
diff --git a/SOURCES/0158-PAM-send-user-name-hint-response-when-needed.patch b/SOURCES/0158-PAM-send-user-name-hint-response-when-needed.patch
deleted file mode 100644
index a444dcf..0000000
--- a/SOURCES/0158-PAM-send-user-name-hint-response-when-needed.patch
+++ /dev/null
@@ -1,335 +0,0 @@
-From a531a785f57be7ae228ca04a7af606debd66eeb1 Mon Sep 17 00:00:00 2001
-From: Sumit Bose <sbose@redhat.com>
-Date: Wed, 3 May 2017 16:30:12 +0200
-Subject: [PATCH 158/160] PAM: send user name hint response when needed
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-If the PAM client didn't send a user name and promtusername is enable
-the PAM responder will tell pam_sss to ask for an optional user name as
-well.
-
-Resolves:
-https://pagure.io/SSSD/sssd/issue/3395
-
-Reviewed-by: Fabiano Fidêncio <fidencio@redhat.com>
-(cherry picked from commit 32474fa2f0a6dc09386bab405fc3461cb3dd12ac)
----
- src/responder/pam/pamsrv_cmd.c  |  72 ++++++++++------
- src/tests/cmocka/test_pam_srv.c | 180 +++++++++++++++++++++++++++++-----------
- 2 files changed, 177 insertions(+), 75 deletions(-)
-
-diff --git a/src/responder/pam/pamsrv_cmd.c b/src/responder/pam/pamsrv_cmd.c
-index 080cfafa709d63542fbf57d26fab11f0a367dea7..49a05657e03feef564d6196029da4cacc2ab8eaf 100644
---- a/src/responder/pam/pamsrv_cmd.c
-+++ b/src/responder/pam/pamsrv_cmd.c
-@@ -1414,7 +1414,7 @@ static void pam_forwarder_lookup_by_cert_done(struct tevent_req *req)
-     struct cache_req_result **results;
-     struct pam_auth_req *preq = tevent_req_callback_data(req,
-                                                          struct pam_auth_req);
--    const char *cert_user;
-+    const char *cert_user = NULL;
- 
-     ret = cache_req_recv(preq, req, &results);
-     talloc_zfree(req);
-@@ -1439,35 +1439,55 @@ static void pam_forwarder_lookup_by_cert_done(struct tevent_req *req)
-                 goto done;
-             }
- 
--            if (preq->cert_user_objs->count != 1) {
--                DEBUG(SSSDBG_CRIT_FAILURE,
--                      "More than one user mapped to certificate.\n");
--                /* TODO: send pam response to ask for a user name */
--                ret = ERR_NO_CREDS;
--                goto done;
--            }
--            cert_user = ldb_msg_find_attr_as_string(
-+            if (preq->cert_user_objs->count == 1) {
-+                cert_user = ldb_msg_find_attr_as_string(
-                                                   preq->cert_user_objs->msgs[0],
-                                                   SYSDB_NAME, NULL);
-+                if (cert_user == NULL) {
-+                    DEBUG(SSSDBG_CRIT_FAILURE,
-+                          "Certificate user object has not name.\n");
-+                    ret = ENOENT;
-+                    goto done;
-+                }
-+
-+                DEBUG(SSSDBG_FUNC_DATA,
-+                      "Found certificate user [%s].\n", cert_user);
-+
-+                ret = sss_parse_name_for_domains(preq->pd,
-+                                               preq->cctx->rctx->domains,
-+                                               preq->cctx->rctx->default_domain,
-+                                               cert_user,
-+                                               &preq->pd->domain,
-+                                               &preq->pd->user);
-+                if (ret != EOK) {
-+                    DEBUG(SSSDBG_OP_FAILURE,
-+                          "sss_parse_name_for_domains failed.\n");
-+                    goto done;
-+                }
-+            }
-+
-+            if (preq->cctx->rctx->domains->user_name_hint) {
-+                ret = add_pam_cert_response(preq->pd, cert_user,
-+                                            preq->token_name,
-+                                            preq->module_name,
-+                                            preq->key_id,
-+                                            SSS_PAM_CERT_INFO_WITH_HINT);
-+                if (ret != EOK) {
-+                    DEBUG(SSSDBG_OP_FAILURE, "add_pam_cert_response failed.\n");
-+                    preq->pd->pam_status = PAM_AUTHINFO_UNAVAIL;
-+                }
-+                ret = EOK;
-+                preq->pd->pam_status = PAM_SUCCESS;
-+                pam_reply(preq);
-+                goto done;
-+            }
-+
-+            /* Without user name hints the certificate must map to single user
-+             * if no login name was given */
-             if (cert_user == NULL) {
-                 DEBUG(SSSDBG_CRIT_FAILURE,
--                      "Certificate user object has not name.\n");
--                ret = ENOENT;
--                goto done;
--            }
--
--            DEBUG(SSSDBG_FUNC_DATA, "Found certificate user [%s].\n",
--                                    cert_user);
--
--            ret = sss_parse_name_for_domains(preq->pd,
--                                             preq->cctx->rctx->domains,
--                                             preq->cctx->rctx->default_domain,
--                                             cert_user,
--                                             &preq->pd->domain,
--                                             &preq->pd->user);
--            if (ret != EOK) {
--                DEBUG(SSSDBG_OP_FAILURE,
--                      "sss_parse_name_for_domains failed.\n");
-+                      "More than one user mapped to certificate.\n");
-+                ret = ERR_NO_CREDS;
-                 goto done;
-             }
- 
-diff --git a/src/tests/cmocka/test_pam_srv.c b/src/tests/cmocka/test_pam_srv.c
-index 35afbdd81d004236885ee80914771ccb4b8acff4..0f92f05417025e41a702127099d1d01e269412dc 100644
---- a/src/tests/cmocka/test_pam_srv.c
-+++ b/src/tests/cmocka/test_pam_srv.c
-@@ -747,57 +747,83 @@ static int test_pam_cert_check_gdm_smartcard(uint32_t status, uint8_t *body,
-     return EOK;
- }
- 
-+static int test_pam_cert_check_ex(uint32_t status, uint8_t *body, size_t blen,
-+                                  enum response_type type, const char *name)
-+{
-+    size_t rp = 0;
-+    uint32_t val;
-+
-+    assert_int_equal(status, 0);
-+
-+    SAFEALIGN_COPY_UINT32(&val, body + rp, &rp);
-+    assert_int_equal(val, pam_test_ctx->exp_pam_status);
-+
-+    SAFEALIGN_COPY_UINT32(&val, body + rp, &rp);
-+    if (name == NULL || *name == '\0') {
-+        assert_int_equal(val, 1);
-+    } else {
-+        assert_int_equal(val, 2);
-+
-+        SAFEALIGN_COPY_UINT32(&val, body + rp, &rp);
-+        assert_int_equal(val, SSS_PAM_DOMAIN_NAME);
-+
-+        SAFEALIGN_COPY_UINT32(&val, body + rp, &rp);
-+        assert_int_equal(val, 9);
-+
-+        assert_int_equal(*(body + rp + val - 1), 0);
-+        assert_string_equal(body + rp, TEST_DOM_NAME);
-+        rp += val;
-+    }
-+
-+    SAFEALIGN_COPY_UINT32(&val, body + rp, &rp);
-+    assert_int_equal(val, type);
-+
-+    SAFEALIGN_COPY_UINT32(&val, body + rp, &rp);
-+    assert_int_equal(val, (strlen(name) + 1
-+                                + sizeof(TEST_TOKEN_NAME)
-+                                + sizeof(TEST_MODULE_NAME)
-+                                + sizeof(TEST_KEY_ID)));
-+
-+    assert_int_equal(*(body + rp + strlen(name)), 0);
-+    assert_string_equal(body + rp, name);
-+    rp += strlen(name) + 1;
-+
-+    assert_int_equal(*(body + rp + sizeof(TEST_TOKEN_NAME) - 1), 0);
-+    assert_string_equal(body + rp, TEST_TOKEN_NAME);
-+    rp += sizeof(TEST_TOKEN_NAME);
-+
-+    assert_int_equal(*(body + rp + sizeof(TEST_MODULE_NAME) - 1), 0);
-+    assert_string_equal(body + rp, TEST_MODULE_NAME);
-+    rp += sizeof(TEST_MODULE_NAME);
-+
-+    assert_int_equal(*(body + rp + sizeof(TEST_KEY_ID) - 1), 0);
-+    assert_string_equal(body + rp, TEST_KEY_ID);
-+    rp += sizeof(TEST_KEY_ID);
-+
-+    assert_int_equal(rp, blen);
-+
-+    return EOK;
-+}
-+
- static int test_pam_cert_check(uint32_t status, uint8_t *body, size_t blen)
- {
--    size_t rp = 0;
--    uint32_t val;
--
--    assert_int_equal(status, 0);
--
--    SAFEALIGN_COPY_UINT32(&val, body + rp, &rp);
--    assert_int_equal(val, pam_test_ctx->exp_pam_status);
--
--    SAFEALIGN_COPY_UINT32(&val, body + rp, &rp);
--    assert_int_equal(val, 2);
--
--    SAFEALIGN_COPY_UINT32(&val, body + rp, &rp);
--    assert_int_equal(val, SSS_PAM_DOMAIN_NAME);
--
--    SAFEALIGN_COPY_UINT32(&val, body + rp, &rp);
--    assert_int_equal(val, 9);
--
--    assert_int_equal(*(body + rp + val - 1), 0);
--    assert_string_equal(body + rp, TEST_DOM_NAME);
--    rp += val;
--
--    SAFEALIGN_COPY_UINT32(&val, body + rp, &rp);
--    assert_int_equal(val, SSS_PAM_CERT_INFO);
--
--    SAFEALIGN_COPY_UINT32(&val, body + rp, &rp);
--    assert_int_equal(val, (sizeof("pamuser@"TEST_DOM_NAME)
--                                + sizeof(TEST_TOKEN_NAME)
--                                + sizeof(TEST_MODULE_NAME)
--                                + sizeof(TEST_KEY_ID)));
--
--    assert_int_equal(*(body + rp + sizeof("pamuser@"TEST_DOM_NAME) - 1), 0);
--    assert_string_equal(body + rp, "pamuser@"TEST_DOM_NAME);
--    rp += sizeof("pamuser@"TEST_DOM_NAME);
--
--    assert_int_equal(*(body + rp + sizeof(TEST_TOKEN_NAME) - 1), 0);
--    assert_string_equal(body + rp, TEST_TOKEN_NAME);
--    rp += sizeof(TEST_TOKEN_NAME);
--
--    assert_int_equal(*(body + rp + sizeof(TEST_MODULE_NAME) - 1), 0);
--    assert_string_equal(body + rp, TEST_MODULE_NAME);
--    rp += sizeof(TEST_MODULE_NAME);
--
--    assert_int_equal(*(body + rp + sizeof(TEST_KEY_ID) - 1), 0);
--    assert_string_equal(body + rp, TEST_KEY_ID);
--    rp += sizeof(TEST_KEY_ID);
--
--    assert_int_equal(rp, blen);
--
--    return EOK;
-+    return test_pam_cert_check_ex(status, body, blen,
-+                                  SSS_PAM_CERT_INFO, "pamuser@"TEST_DOM_NAME);
-+}
-+
-+static int test_pam_cert_check_with_hint(uint32_t status, uint8_t *body,
-+                                         size_t blen)
-+{
-+    return test_pam_cert_check_ex(status, body, blen,
-+                                  SSS_PAM_CERT_INFO_WITH_HINT,
-+                                  "pamuser@"TEST_DOM_NAME);
-+}
-+
-+static int test_pam_cert_check_with_hint_no_user(uint32_t status, uint8_t *body,
-+                                                 size_t blen)
-+{
-+    return test_pam_cert_check_ex(status, body, blen,
-+                                  SSS_PAM_CERT_INFO_WITH_HINT, "");
- }
- 
- static int test_pam_offline_chauthtok_check(uint32_t status,
-@@ -1895,6 +1921,33 @@ void test_pam_preauth_cert_no_logon_name(void **state)
-     assert_int_equal(ret, EOK);
- }
- 
-+void test_pam_preauth_cert_no_logon_name_with_hint(void **state)
-+{
-+    int ret;
-+
-+    set_cert_auth_param(pam_test_ctx->pctx, NSS_DB);
-+    pam_test_ctx->rctx->domains->user_name_hint = true;
-+
-+    /* If no logon name is given the user is looked by certificate first.
-+     * Since user name hint is enabled we do not have to search the user
-+     * during pre-auth and there is no need for an extra mocked response as in
-+     * test_pam_preauth_cert_no_logon_name. */
-+    mock_input_pam_cert(pam_test_ctx, NULL, NULL, NULL,
-+                        test_lookup_by_cert_cb, TEST_TOKEN_CERT, false);
-+
-+    will_return(__wrap_sss_packet_get_cmd, SSS_PAM_PREAUTH);
-+    will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL);
-+
-+    set_cmd_cb(test_pam_cert_check_with_hint);
-+    ret = sss_cmd_execute(pam_test_ctx->cctx, SSS_PAM_PREAUTH,
-+                          pam_test_ctx->pam_cmds);
-+    assert_int_equal(ret, EOK);
-+
-+    /* Wait until the test finishes with EOK */
-+    ret = test_ev_loop(pam_test_ctx->tctx);
-+    assert_int_equal(ret, EOK);
-+}
-+
- void test_pam_preauth_cert_no_logon_name_double_cert(void **state)
- {
-     int ret;
-@@ -1917,6 +1970,29 @@ void test_pam_preauth_cert_no_logon_name_double_cert(void **state)
-     assert_int_equal(ret, EOK);
- }
- 
-+void test_pam_preauth_cert_no_logon_name_double_cert_with_hint(void **state)
-+{
-+    int ret;
-+
-+    set_cert_auth_param(pam_test_ctx->pctx, NSS_DB);
-+    pam_test_ctx->rctx->domains->user_name_hint = true;
-+
-+    mock_input_pam_cert(pam_test_ctx, NULL, NULL, NULL,
-+                        test_lookup_by_cert_double_cb, TEST_TOKEN_CERT, false);
-+
-+    will_return(__wrap_sss_packet_get_cmd, SSS_PAM_PREAUTH);
-+    will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL);
-+
-+    set_cmd_cb(test_pam_cert_check_with_hint_no_user);
-+    ret = sss_cmd_execute(pam_test_ctx->cctx, SSS_PAM_PREAUTH,
-+                          pam_test_ctx->pam_cmds);
-+    assert_int_equal(ret, EOK);
-+
-+    /* Wait until the test finishes with EOK */
-+    ret = test_ev_loop(pam_test_ctx->tctx);
-+    assert_int_equal(ret, EOK);
-+}
-+
- void test_pam_preauth_no_cert_no_logon_name(void **state)
- {
-     int ret;
-@@ -2426,8 +2502,14 @@ int main(int argc, const char *argv[])
-         cmocka_unit_test_setup_teardown(test_pam_preauth_cert_no_logon_name,
-                                         pam_test_setup, pam_test_teardown),
-         cmocka_unit_test_setup_teardown(
-+                                  test_pam_preauth_cert_no_logon_name_with_hint,
-+                                  pam_test_setup, pam_test_teardown),
-+        cmocka_unit_test_setup_teardown(
-                                 test_pam_preauth_cert_no_logon_name_double_cert,
-                                 pam_test_setup, pam_test_teardown),
-+        cmocka_unit_test_setup_teardown(
-+                      test_pam_preauth_cert_no_logon_name_double_cert_with_hint,
-+                      pam_test_setup, pam_test_teardown),
-         cmocka_unit_test_setup_teardown(test_pam_preauth_no_cert_no_logon_name,
-                                         pam_test_setup, pam_test_teardown),
-         cmocka_unit_test_setup_teardown(
--- 
-2.9.4
-
diff --git a/SOURCES/0159-sysdb-sysdb_get_certmap-allow-empty-certmap.patch b/SOURCES/0159-sysdb-sysdb_get_certmap-allow-empty-certmap.patch
deleted file mode 100644
index 290bab5..0000000
--- a/SOURCES/0159-sysdb-sysdb_get_certmap-allow-empty-certmap.patch
+++ /dev/null
@@ -1,105 +0,0 @@
-From b435e510fb06af4e8f3cffd3730f43a6aff165fa Mon Sep 17 00:00:00 2001
-From: Sumit Bose <sbose@redhat.com>
-Date: Mon, 8 May 2017 17:30:06 +0200
-Subject: [PATCH 159/160] sysdb: sysdb_get_certmap() allow empty certmap
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Since sysdb_get_certmap() returns the user name hint information as well
-it should return a result even if there are no certmaps.
-
-Related to https://pagure.io/SSSD/sssd/issue/3395
-
-Reviewed-by: Fabiano Fidêncio <fidencio@redhat.com>
-(cherry picked from commit ee7e72a65d323636600ffda271d5b5c4ddbc78b1)
----
- src/db/sysdb_certmap.c                | 13 ++++++++-----
- src/tests/cmocka/test_sysdb_certmap.c |  9 +++++----
- 2 files changed, 13 insertions(+), 9 deletions(-)
-
-diff --git a/src/db/sysdb_certmap.c b/src/db/sysdb_certmap.c
-index 4917796b11c3967b4d147ebee7c7e83f09b872ce..2d89c08a07be6e8eaf853d6c50b206c5c53d5a37 100644
---- a/src/db/sysdb_certmap.c
-+++ b/src/db/sysdb_certmap.c
-@@ -269,7 +269,7 @@ errno_t sysdb_get_certmap(TALLOC_CTX *mem_ctx, struct sysdb_ctx *sysdb,
-     size_t d;
-     struct ldb_dn *container_dn = NULL;
-     int ret;
--    struct certmap_info **maps;
-+    struct certmap_info **maps = NULL;
-     TALLOC_CTX *tmp_ctx = NULL;
-     struct ldb_result *res;
-     const char *tmp_str;
-@@ -320,7 +320,7 @@ errno_t sysdb_get_certmap(TALLOC_CTX *mem_ctx, struct sysdb_ctx *sysdb,
- 
-     if (res->count == 0) {
-         DEBUG(SSSDBG_TRACE_FUNC, "No certificate maps found.\n");
--        ret = ENOENT;
-+        ret = EOK;
-         goto done;
-     }
- 
-@@ -377,7 +377,7 @@ errno_t sysdb_get_certmap(TALLOC_CTX *mem_ctx, struct sysdb_ctx *sysdb,
-                                                SYSDB_CERTMAP_PRIORITY,
-                                                (uint64_t) -1);
-         if (tmp_uint != (uint64_t) -1) {
--            if (tmp_uint >= UINT32_MAX) {
-+            if (tmp_uint > UINT32_MAX) {
-                 DEBUG(SSSDBG_OP_FAILURE, "Priority value [%lu] too large.\n",
-                                          (unsigned long) tmp_uint);
-                 ret = EINVAL;
-@@ -414,11 +414,14 @@ errno_t sysdb_get_certmap(TALLOC_CTX *mem_ctx, struct sysdb_ctx *sysdb,
-         }
-     }
- 
--    *certmaps = talloc_steal(mem_ctx, maps);
--    *user_name_hint = hint;
-     ret = EOK;
- 
- done:
-+    if (ret == EOK) {
-+        *certmaps = talloc_steal(mem_ctx, maps);
-+        *user_name_hint = hint;
-+    }
-+
-     talloc_free(tmp_ctx);
- 
-     return ret;
-diff --git a/src/tests/cmocka/test_sysdb_certmap.c b/src/tests/cmocka/test_sysdb_certmap.c
-index fb07165561779226935f436c308c85abfc305635..72edf5f53fd6d23d7279eaa496b3e798c06cb903 100644
---- a/src/tests/cmocka/test_sysdb_certmap.c
-+++ b/src/tests/cmocka/test_sysdb_certmap.c
-@@ -88,8 +88,8 @@ static void test_sysdb_get_certmap_not_exists(void **state)
- 
-     ret = sysdb_get_certmap(ctctx, ctctx->tctx->sysdb, &certmap,
-                             &user_name_hint);
--    assert_int_equal(ret, ENOENT);
--
-+    assert_int_equal(ret, EOK);
-+    assert_null(certmap);
- }
- 
- static void check_certmap(struct certmap_info *m, struct certmap_info *r,
-@@ -134,7 +134,7 @@ static void test_sysdb_update_certmap(void **state)
-     int ret;
-     const char *domains[] = { "dom1.test", "dom2.test", "dom3.test", NULL };
-     struct certmap_info map_a = { discard_const("map_a"), 11, discard_const("abc"), discard_const("def"), NULL };
--    struct certmap_info map_b = { discard_const("map_b"), 22, discard_const("abc"), NULL, domains };
-+    struct certmap_info map_b = { discard_const("map_b"), UINT_MAX, discard_const("abc"), NULL, domains };
-     struct certmap_info *certmap_empty[] = { NULL };
-     struct certmap_info *certmap_a[] = { &map_a, NULL };
-     struct certmap_info *certmap_b[] = { &map_b, NULL };
-@@ -152,7 +152,8 @@ static void test_sysdb_update_certmap(void **state)
- 
-     ret = sysdb_get_certmap(ctctx, ctctx->tctx->sysdb, &certmap,
-                             &user_name_hint);
--    assert_int_equal(ret, ENOENT);
-+    assert_int_equal(ret, EOK);
-+    assert_null(certmap);
- 
-     ret = sysdb_update_certmap(ctctx->tctx->sysdb, certmap_a, false);
-     assert_int_equal(ret, EOK);
--- 
-2.9.4
-
diff --git a/SOURCES/0160-sssctl-show-user-name-used-for-authentication-in-use.patch b/SOURCES/0160-sssctl-show-user-name-used-for-authentication-in-use.patch
deleted file mode 100644
index 6680a3b..0000000
--- a/SOURCES/0160-sssctl-show-user-name-used-for-authentication-in-use.patch
+++ /dev/null
@@ -1,53 +0,0 @@
-From 6edf41eba3cec8aa40dffaf639cd5c7756db310e Mon Sep 17 00:00:00 2001
-From: Sumit Bose <sbose@redhat.com>
-Date: Wed, 10 May 2017 17:13:48 +0200
-Subject: [PATCH 160/160] sssctl: show user name used for authentication in
- user-checks
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Since there are cases where the user name is not entered directly but
-determined by other means the user-checks should show the name of the
-user used for authentication.
-
-Related to https://pagure.io/SSSD/sssd/issue/3395
-
-Reviewed-by: Fabiano Fidêncio <fidencio@redhat.com>
-(cherry picked from commit b130adaa3934d0531aca0f32961ab8b4cc720820)
----
- src/tools/sssctl/sssctl_user_checks.c | 11 ++++++++++-
- 1 file changed, 10 insertions(+), 1 deletion(-)
-
-diff --git a/src/tools/sssctl/sssctl_user_checks.c b/src/tools/sssctl/sssctl_user_checks.c
-index 7c7b564bd29100382c9bbef7a3131c379e9aa97e..d5cd8a1b42e84aa96df95ed39905c864a38212b7 100644
---- a/src/tools/sssctl/sssctl_user_checks.c
-+++ b/src/tools/sssctl/sssctl_user_checks.c
-@@ -200,6 +200,8 @@ errno_t sssctl_user_checks(struct sss_cmdline *cmdline,
-     const char *action = DEFAULT_ACTION;
-     const char *service = DEFAULT_SERVICE;
-     int ret;
-+    int pret;
-+    const char *pam_user = NULL;
-     size_t c;
-     char **pam_env;
- 
-@@ -246,7 +248,14 @@ errno_t sssctl_user_checks(struct sss_cmdline *cmdline,
-     if ( strncmp(action, "auth", 4)== 0 ) {
-         fprintf(stdout, _("testing pam_authenticate\n\n"));
-         ret = pam_authenticate(pamh, 0);
--        fprintf(stderr, _("pam_authenticate: %s\n\n"), pam_strerror(pamh, ret));
-+        pret = pam_get_item(pamh, PAM_USER, (const void **) &pam_user);
-+        if (pret != PAM_SUCCESS) {
-+            fprintf(stderr, _("pam_get_item failed: %s\n"), pam_strerror(pamh,
-+                                                                         pret));
-+            pam_user = "- not available -";
-+        }
-+        fprintf(stderr, _("pam_authenticate for user [%s]: %s\n\n"), pam_user,
-+                                                       pam_strerror(pamh, ret));
-     } else if ( strncmp(action, "chau", 4)== 0 ) {
-         fprintf(stdout, _("testing pam_chauthtok\n\n"));
-         ret = pam_chauthtok(pamh, 0);
--- 
-2.9.4
-
diff --git a/SOURCES/0161-RESP-Provide-a-reusable-request-to-fully-resolve-inc.patch b/SOURCES/0161-RESP-Provide-a-reusable-request-to-fully-resolve-inc.patch
deleted file mode 100644
index cc83129..0000000
--- a/SOURCES/0161-RESP-Provide-a-reusable-request-to-fully-resolve-inc.patch
+++ /dev/null
@@ -1,267 +0,0 @@
-From be1f9a082eb28b3346135cbe399f7f909c8a50ce Mon Sep 17 00:00:00 2001
-From: Jakub Hrozek <jhrozek@redhat.com>
-Date: Wed, 24 May 2017 17:34:55 +0200
-Subject: [PATCH 161/166] RESP: Provide a reusable request to fully resolve
- incomplete groups
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-After initgroups, the group objects might not be complete, but just
-stubs that contain the SID and the GID. If the caller needs to know the
-group names as well, this request allows them to iterate over the list
-of the groups and resolve them one-by-one.
-
-Reviewed-by: Pavel Březina <pbrezina@redhat.com>
----
- src/responder/common/responder.h       |  14 +++
- src/responder/common/responder_utils.c | 206 +++++++++++++++++++++++++++++++++
- 2 files changed, 220 insertions(+)
-
-diff --git a/src/responder/common/responder.h b/src/responder/common/responder.h
-index dfe1ec455e355de263c3550306e53fea3ada85df..c09ecd4931c9e197fbdfb7835eb72f49cc6f6d3f 100644
---- a/src/responder/common/responder.h
-+++ b/src/responder/common/responder.h
-@@ -414,4 +414,18 @@ int sized_domain_name(TALLOC_CTX *mem_ctx,
-                       const char *member_name,
-                       struct sized_string **_name);
- 
-+/* Given a ldb_result structure that contains a result of sysdb_initgroups
-+ * where some groups might be just 'stubs' that don't have a name, but only
-+ * a SID and a GID, resolve those incomplete groups into full group objects
-+ */
-+struct tevent_req *resp_resolve_group_names_send(TALLOC_CTX *mem_ctx,
-+                                                 struct tevent_context *ev,
-+                                                 struct resp_ctx *rctx,
-+                                                 struct sss_domain_info *dom,
-+                                                 struct ldb_result *initgr_res);
-+
-+int resp_resolve_group_names_recv(TALLOC_CTX *mem_ctx,
-+                                  struct tevent_req *req,
-+                                  struct ldb_result **_initgr_named_res);
-+
- #endif /* __SSS_RESPONDER_H__ */
-diff --git a/src/responder/common/responder_utils.c b/src/responder/common/responder_utils.c
-index b02212dfd87c2b7c2ca6108d46f939447f0eaa25..7f5c0573087e9c6c885ae158d0677994fd538e2a 100644
---- a/src/responder/common/responder_utils.c
-+++ b/src/responder/common/responder_utils.c
-@@ -23,6 +23,7 @@
- #include <talloc.h>
- 
- #include "responder/common/responder.h"
-+#include "responder/common/cache_req/cache_req.h"
- #include "util/util.h"
- 
- static inline bool
-@@ -193,3 +194,208 @@ char *sss_resp_create_fqname(TALLOC_CTX *mem_ctx,
-     talloc_free(tmp_ctx);
-     return name;
- }
-+
-+struct resp_resolve_group_names_state {
-+    struct tevent_context *ev;
-+    struct resp_ctx *rctx;
-+    struct sss_domain_info *dom;
-+    struct ldb_result *initgr_res;
-+
-+    bool needs_refresh;
-+    unsigned int group_iter;
-+
-+    struct ldb_result *initgr_named_res;
-+};
-+
-+static void resp_resolve_group_done(struct tevent_req *subreq);
-+static errno_t resp_resolve_group_next(struct tevent_req *req);
-+static errno_t resp_resolve_group_reread_names(struct resp_resolve_group_names_state *state);
-+
-+struct tevent_req *resp_resolve_group_names_send(TALLOC_CTX *mem_ctx,
-+                                                 struct tevent_context *ev,
-+                                                 struct resp_ctx *rctx,
-+                                                 struct sss_domain_info *dom,
-+                                                 struct ldb_result *initgr_res)
-+{
-+    struct resp_resolve_group_names_state *state;
-+    struct tevent_req *req;
-+    errno_t ret;
-+
-+    req = tevent_req_create(mem_ctx, &state, struct resp_resolve_group_names_state);
-+    if (req == NULL) {
-+        DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create() failed\n");
-+        return NULL;
-+    }
-+    state->ev = ev;
-+    state->rctx = rctx;
-+    state->dom = dom;
-+    state->initgr_res = initgr_res;
-+
-+    ret = resp_resolve_group_next(req);
-+    if (ret == EOK) {
-+        goto immediate;
-+    } else if (ret != EAGAIN) {
-+        goto immediate;
-+    }
-+
-+    return req;
-+
-+immediate:
-+    if (ret == EOK) {
-+        tevent_req_done(req);
-+    } else {
-+        tevent_req_error(req, ret);
-+    }
-+    tevent_req_post(req, ev);
-+    return req;
-+}
-+
-+static bool
-+resp_resolve_group_needs_refresh(struct resp_resolve_group_names_state *state)
-+{
-+    /* Refresh groups that have a non-zero GID,
-+     * but are marked as non-POSIX
-+     */
-+    bool is_posix;
-+    uint64_t gid;
-+    struct ldb_message *group_msg;
-+
-+    group_msg = state->initgr_res->msgs[state->group_iter];
-+
-+    is_posix = ldb_msg_find_attr_as_bool(group_msg, SYSDB_POSIX, false);
-+    gid = ldb_msg_find_attr_as_uint64(group_msg, SYSDB_GIDNUM, 0);
-+
-+    if (is_posix == false && gid != 0) {
-+        return true;
-+    }
-+
-+    return false;
-+}
-+
-+static errno_t resp_resolve_group_next(struct tevent_req *req)
-+{
-+    struct cache_req_data *data;
-+    uint64_t gid;
-+    struct tevent_req *subreq;
-+    struct resp_resolve_group_names_state *state;
-+
-+    state = tevent_req_data(req, struct resp_resolve_group_names_state);
-+
-+    while (state->group_iter < state->initgr_res->count
-+           && !resp_resolve_group_needs_refresh(state)) {
-+        state->group_iter++;
-+    }
-+
-+    if (state->group_iter >= state->initgr_res->count) {
-+        /* All groups were refreshed */
-+        return EOK;
-+    }
-+
-+    /* Fire a request */
-+    gid = ldb_msg_find_attr_as_uint64(state->initgr_res->msgs[state->group_iter],
-+                                      SYSDB_GIDNUM, 0);
-+    if (gid == 0) {
-+        return EINVAL;
-+    }
-+
-+    data = cache_req_data_id_attrs(state, CACHE_REQ_GROUP_BY_ID, gid, NULL);
-+    if (data == NULL) {
-+        DEBUG(SSSDBG_CRIT_FAILURE, "Unable to set cache request data!\n");
-+        return ENOMEM;
-+    }
-+
-+    subreq = cache_req_send(state,
-+                            state->ev,
-+                            state->rctx,
-+                            state->rctx->ncache,
-+                            0,
-+                            CACHE_REQ_ANY_DOM,
-+                            NULL,
-+                            data);
-+    if (subreq == NULL) {
-+        DEBUG(SSSDBG_CRIT_FAILURE, "Unable to send cache request!\n");
-+        return ENOMEM;
-+    }
-+
-+    tevent_req_set_callback(subreq, resp_resolve_group_done, req);
-+    return EAGAIN;
-+}
-+
-+static void resp_resolve_group_done(struct tevent_req *subreq)
-+{
-+    struct resp_resolve_group_names_state *state;
-+    struct tevent_req *req;
-+    errno_t ret;
-+
-+    req = tevent_req_callback_data(subreq, struct tevent_req);
-+    state = tevent_req_data(req, struct resp_resolve_group_names_state);
-+
-+    ret = cache_req_single_domain_recv(state, subreq, NULL);
-+    talloc_zfree(subreq);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_OP_FAILURE, "Failed to refresh group\n");
-+        /* Try to refresh the others on error */
-+    }
-+
-+    state->group_iter++;
-+    state->needs_refresh = true;
-+
-+    ret = resp_resolve_group_next(req);
-+    if (ret == EOK) {
-+        ret = resp_resolve_group_reread_names(state);
-+        if (ret != EOK) {
-+            tevent_req_error(req, ret);
-+            return;
-+        }
-+        DEBUG(SSSDBG_TRACE_FUNC, "All groups are refreshed, done\n");
-+        tevent_req_done(req);
-+        return;
-+    } else if (ret != EAGAIN) {
-+        tevent_req_error(req, ret);
-+        return;
-+    }
-+
-+    /* Continue refreshing.. */
-+}
-+
-+static errno_t
-+resp_resolve_group_reread_names(struct resp_resolve_group_names_state *state)
-+{
-+    errno_t ret;
-+    const char *username;
-+
-+    /* re-read reply in case any groups were renamed */
-+    /* msgs[0] is the user entry */
-+    username = sss_view_ldb_msg_find_attr_as_string(state->dom,
-+                                                    state->initgr_res->msgs[0],
-+                                                    SYSDB_NAME,
-+                                                    NULL);
-+    if (username == NULL) {
-+        DEBUG(SSSDBG_CRIT_FAILURE, "A user with no name?\n");
-+        return EINVAL;
-+    }
-+
-+    ret = sysdb_initgroups_with_views(state,
-+                                      state->dom,
-+                                      username,
-+                                      &state->initgr_named_res);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_OP_FAILURE, "Cannot re-read the group names\n");
-+        return ret;
-+    }
-+
-+    return EOK;
-+}
-+
-+int resp_resolve_group_names_recv(TALLOC_CTX *mem_ctx,
-+                                  struct tevent_req *req,
-+                                  struct ldb_result **_initgr_named_res)
-+{
-+    struct resp_resolve_group_names_state *state = NULL;
-+    state = tevent_req_data(req, struct resp_resolve_group_names_state);
-+
-+    TEVENT_REQ_RETURN_ON_ERROR(req);
-+
-+    *_initgr_named_res = talloc_steal(mem_ctx, state->initgr_named_res);
-+    return EOK;
-+}
--- 
-2.9.4
-
diff --git a/SOURCES/0162-IFP-Only-format-the-output-name-to-the-short-version.patch b/SOURCES/0162-IFP-Only-format-the-output-name-to-the-short-version.patch
deleted file mode 100644
index b6c6eaa..0000000
--- a/SOURCES/0162-IFP-Only-format-the-output-name-to-the-short-version.patch
+++ /dev/null
@@ -1,121 +0,0 @@
-From f5bee70057370c72ed111b50937e3252e36ccefb Mon Sep 17 00:00:00 2001
-From: Jakub Hrozek <jhrozek@redhat.com>
-Date: Tue, 9 May 2017 12:21:32 +0200
-Subject: [PATCH 162/166] IFP: Only format the output name to the short version
- before output
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-The ifp_user_get_attr_done() request handler was reused for both
-GetUserGroups and GetUserAttrs requests. Yet, it performed output
-formatting of name and nameAlias.
-
-This is bad, because the output formatting should really be done only
-during output. Also, it broke any post-processing of the returned
-message which the request might do later.
-
-Reviewed-by: Pavel Březina <pbrezina@redhat.com>
----
- src/responder/ifp/ifpsrv_cmd.c | 64 ++++++++++++------------------------------
- 1 file changed, 18 insertions(+), 46 deletions(-)
-
-diff --git a/src/responder/ifp/ifpsrv_cmd.c b/src/responder/ifp/ifpsrv_cmd.c
-index e4d6c42ef35ef372472803d3d26b17d4181021a8..915f77e38e94c703f6c67e8d5fdcc59d189943be 100644
---- a/src/responder/ifp/ifpsrv_cmd.c
-+++ b/src/responder/ifp/ifpsrv_cmd.c
-@@ -181,26 +181,6 @@ static void ifp_user_get_attr_process(struct tevent_req *req)
- }
- 
- static errno_t
--ifp_user_get_attr_replace_space(TALLOC_CTX *mem_ctx,
--                                struct ldb_message_element *el,
--                                const char sub)
--{
--    int i;
--
--    for (i = 0; i < el->num_values; i++) {
--        el->values[i].data = (uint8_t *) sss_replace_space(mem_ctx,
--                                             (const char *) el->values[i].data,
--                                             sub);
--        if (el->values[i].data == NULL) {
--            DEBUG(SSSDBG_CRIT_FAILURE, "sss_replace_space failed, skipping\n");
--            return ENOMEM;
--        }
--    }
--
--    return EOK;
--}
--
--static errno_t
- ifp_user_get_attr_handle_reply(struct sss_domain_info *domain,
-                                struct ifp_req *ireq,
-                                const char **attrs,
-@@ -234,6 +214,24 @@ ifp_user_get_attr_handle_reply(struct sss_domain_info *domain,
-     }
- 
-     if (res->count > 0) {
-+        ret = ifp_ldb_el_output_name(ireq->ifp_ctx->rctx, res->msgs[0],
-+                                     SYSDB_NAME, domain);
-+        if (ret != EOK) {
-+            DEBUG(SSSDBG_CRIT_FAILURE,
-+                  "Cannot convert SYSDB_NAME to output format [%d]: %s\n",
-+                  ret, sss_strerror(ret));
-+            return sbus_request_finish(ireq->dbus_req, NULL);
-+        }
-+
-+        ret = ifp_ldb_el_output_name(ireq->ifp_ctx->rctx, res->msgs[0],
-+                                     SYSDB_NAME_ALIAS, domain);
-+        if (ret != EOK) {
-+            DEBUG(SSSDBG_CRIT_FAILURE,
-+                  "Cannot convert SYSDB_NAME_ALIAS to output format [%d]: %s\n",
-+                  ret, sss_strerror(ret));
-+            return sbus_request_finish(ireq->dbus_req, NULL);
-+        }
-+
-         for (ai = 0; attrs[ai]; ai++) {
-             el = sss_view_ldb_msg_find_element(domain, res->msgs[0], attrs[ai]);
-             if (el == NULL || el->num_values == 0) {
-@@ -243,18 +241,6 @@ ifp_user_get_attr_handle_reply(struct sss_domain_info *domain,
-                 continue;
-             }
- 
--            /* Normalize white space in user names */
--            if (ireq->ifp_ctx->rctx->override_space != '\0' &&
--                    strcmp(attrs[ai], SYSDB_NAME) == 0) {
--                ret = ifp_user_get_attr_replace_space(ireq, el,
--                                        ireq->ifp_ctx->rctx->override_space);
--                if (ret != EOK) {
--                    DEBUG(SSSDBG_MINOR_FAILURE, "Cannot normalize %s\n",
--                          attrs[ai]);
--                    continue;
--                }
--            }
--
-             ret = ifp_add_ldb_el_to_dict(&iter_dict, el);
-             if (ret != EOK) {
-                 DEBUG(SSSDBG_MINOR_FAILURE,
-@@ -575,20 +561,6 @@ static void ifp_user_get_attr_done(struct tevent_req *subreq)
-         }
-     }
- 
--    ret = ifp_ldb_el_output_name(state->rctx, state->res->msgs[0],
--                                 SYSDB_NAME, state->dom);
--    if (ret != EOK) {
--        tevent_req_error(req, ret);
--        return;
--    }
--
--    ret = ifp_ldb_el_output_name(state->rctx, state->res->msgs[0],
--                                 SYSDB_NAME_ALIAS, state->dom);
--    if (ret != EOK) {
--        tevent_req_error(req, ret);
--        return;
--    }
--
-     tevent_req_done(req);
- }
- 
--- 
-2.9.4
-
diff --git a/SOURCES/0163-IFP-Resolve-group-names-from-GIDs-if-required.patch b/SOURCES/0163-IFP-Resolve-group-names-from-GIDs-if-required.patch
deleted file mode 100644
index b33cbba..0000000
--- a/SOURCES/0163-IFP-Resolve-group-names-from-GIDs-if-required.patch
+++ /dev/null
@@ -1,204 +0,0 @@
-From 3891e94330a5df632a8db1a6f1d642cf2fa96579 Mon Sep 17 00:00:00 2001
-From: Jakub Hrozek <jhrozek@redhat.com>
-Date: Wed, 24 May 2017 21:32:28 +0200
-Subject: [PATCH 163/166] IFP: Resolve group names from GIDs if required
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-The AD provider only converts SIDs to GIDs during initgroups
-to improve performance. But this is not sufficient for the
-org.freedesktop.sssd.infopipe.GetUserGroups method, which needs to return
-names.
-
-We need to resolve the GIDs to names ourselves in that method.
-
-Resolves:
-https://pagure.io/SSSD/sssd/issue/3392
-
-Reviewed-by: Pavel Březina <pbrezina@redhat.com>
----
- src/responder/ifp/ifpsrv_cmd.c | 115 +++++++++++++++++++++++++++++++----------
- 1 file changed, 89 insertions(+), 26 deletions(-)
-
-diff --git a/src/responder/ifp/ifpsrv_cmd.c b/src/responder/ifp/ifpsrv_cmd.c
-index 915f77e38e94c703f6c67e8d5fdcc59d189943be..70728e1bb656fd032b7f1c240683e8aa3b91a726 100644
---- a/src/responder/ifp/ifpsrv_cmd.c
-+++ b/src/responder/ifp/ifpsrv_cmd.c
-@@ -259,7 +259,18 @@ ifp_user_get_attr_handle_reply(struct sss_domain_info *domain,
-     return sbus_request_finish(ireq->dbus_req, reply);
- }
- 
-+struct ifp_user_get_groups_state {
-+    struct resp_ctx *rctx;
-+
-+    struct ifp_attr_req *group_attr_req;
-+
-+    struct ldb_result *res;
-+    struct ldb_result *res_names;
-+    struct sss_domain_info *dom;
-+};
-+
- static void ifp_user_get_groups_process(struct tevent_req *req);
-+static void ifp_user_get_groups_names_resolved(struct tevent_req *req);
- static errno_t ifp_user_get_groups_reply(struct sss_domain_info *domain,
-                                          struct ifp_req *ireq,
-                                          struct ldb_result *res);
-@@ -269,7 +280,7 @@ int ifp_user_get_groups(struct sbus_request *dbus_req,
- {
-     struct ifp_req *ireq;
-     struct ifp_ctx *ifp_ctx;
--    struct ifp_attr_req *group_req;
-+    struct ifp_user_get_groups_state *state;
-     struct tevent_req *req;
-     errno_t ret;
- 
-@@ -284,68 +295,120 @@ int ifp_user_get_groups(struct sbus_request *dbus_req,
-         return ifp_req_create_handle_failure(dbus_req, ret);
-     }
- 
--    group_req = talloc_zero(ireq, struct ifp_attr_req);
--    if (group_req == NULL) {
-+    state = talloc_zero(ireq, struct ifp_user_get_groups_state);
-+    if (state == NULL) {
-         return sbus_request_finish(dbus_req, NULL);
-     }
--    group_req->ireq = ireq;
--    group_req->name = arg_user;
-+    state->rctx = ifp_ctx->rctx;
- 
--    group_req->attrs = talloc_zero_array(group_req, const char *, 2);
--    if (group_req->attrs == NULL) {
-+    state->group_attr_req = talloc_zero(state, struct ifp_attr_req);
-+    if (state->group_attr_req == NULL) {
-         return sbus_request_finish(dbus_req, NULL);
-     }
-+    state->group_attr_req->ireq = ireq;
-+    state->group_attr_req->name = arg_user;
- 
--    group_req->attrs[0] = talloc_strdup(group_req->attrs, SYSDB_MEMBEROF);
--    if (group_req->attrs[0] == NULL) {
-+    state->group_attr_req->attrs = talloc_zero_array(state->group_attr_req,
-+                                                     const char *, 2);
-+    if (state->group_attr_req->attrs == NULL) {
-+        return sbus_request_finish(dbus_req, NULL);
-+    }
-+
-+    state->group_attr_req->attrs[0] = talloc_strdup(state->group_attr_req->attrs,
-+                                                    SYSDB_MEMBEROF);
-+    if (state->group_attr_req->attrs[0] == NULL) {
-         return sbus_request_finish(dbus_req, NULL);
-     }
- 
-     DEBUG(SSSDBG_FUNC_DATA,
-           "Looking up groups of user [%s] on behalf of %"PRIi64"\n",
--          group_req->name, group_req->ireq->dbus_req->client);
-+          state->group_attr_req->name,
-+          state->group_attr_req->ireq->dbus_req->client);
- 
-     req = ifp_user_get_attr_send(ireq, ifp_ctx->rctx,
-                                  ifp_ctx->rctx->ncache, SSS_DP_INITGROUPS,
--                                 group_req->name, group_req->attrs);
-+                                 state->group_attr_req->name,
-+                                 state->group_attr_req->attrs);
-     if (req == NULL) {
-         return sbus_request_finish(dbus_req, NULL);
-     }
--    tevent_req_set_callback(req, ifp_user_get_groups_process, group_req);
-+    tevent_req_set_callback(req,
-+                            ifp_user_get_groups_process,
-+                            state);
-     return EOK;
- }
- 
- static void ifp_user_get_groups_process(struct tevent_req *req)
- {
--    struct ifp_attr_req *group_req;
-+    struct ifp_user_get_groups_state *state;
-+    struct ifp_attr_req *group_attr_req;
-     errno_t ret;
--    struct ldb_result *res;
--    struct sss_domain_info *dom;
- 
--    group_req = tevent_req_callback_data(req, struct ifp_attr_req);
-+    state = tevent_req_callback_data(req, struct ifp_user_get_groups_state);
-+    group_attr_req = state->group_attr_req;
- 
--    ret = ifp_user_get_attr_recv(group_req, req, &res, &dom);
-+    ret = ifp_user_get_attr_recv(group_attr_req, req, &state->res, &state->dom);
-     talloc_zfree(req);
-     if (ret == ENOENT) {
--        sbus_request_fail_and_finish(group_req->ireq->dbus_req,
--                               sbus_error_new(group_req->ireq->dbus_req,
-+        sbus_request_fail_and_finish(group_attr_req->ireq->dbus_req,
-+                               sbus_error_new(group_attr_req->ireq->dbus_req,
-                                               DBUS_ERROR_FAILED,
-                                               "No such user\n"));
-         return;
-     } else if (ret != EOK) {
--        sbus_request_fail_and_finish(group_req->ireq->dbus_req,
--                               sbus_error_new(group_req->ireq->dbus_req,
-+        sbus_request_fail_and_finish(group_attr_req->ireq->dbus_req,
-+                               sbus_error_new(group_attr_req->ireq->dbus_req,
-                                               DBUS_ERROR_FAILED,
-                                               "Failed to read attribute\n"));
-         return;
-     }
- 
--    ret = ifp_user_get_groups_reply(dom, group_req->ireq, res);
-+    req = resp_resolve_group_names_send(state,
-+                                        state->rctx->ev,
-+                                        state->rctx,
-+                                        state->dom,
-+                                        state->res);
-+    if (req == NULL) {
-+        sbus_request_finish(group_attr_req->ireq->dbus_req, NULL);
-+        return;
-+    }
-+    tevent_req_set_callback(req,
-+                            ifp_user_get_groups_names_resolved,
-+                            state);
-+}
-+
-+static void ifp_user_get_groups_names_resolved(struct tevent_req *req)
-+{
-+    struct ifp_user_get_groups_state *state;
-+    struct ifp_attr_req *group_attr_req;
-+    errno_t ret;
-+
-+    state = tevent_req_callback_data(req, struct ifp_user_get_groups_state);
-+    group_attr_req = state->group_attr_req;
-+
-+    ret = resp_resolve_group_names_recv(state, req, &state->res_names);
-+    talloc_zfree(req);
-     if (ret != EOK) {
--        sbus_request_fail_and_finish(group_req->ireq->dbus_req,
--                               sbus_error_new(group_req->ireq->dbus_req,
--                                              DBUS_ERROR_FAILED,
--                                              "Failed to build a reply\n"));
-+        sbus_request_fail_and_finish(group_attr_req->ireq->dbus_req,
-+                            sbus_error_new(group_attr_req->ireq->dbus_req,
-+                                           DBUS_ERROR_FAILED,
-+                                           "Failed to resolve groupnames\n"));
-+        return;
-+    }
-+
-+    if (state->res_names == NULL) {
-+        state->res_names = state->res;
-+    }
-+
-+    ret = ifp_user_get_groups_reply(state->dom,
-+                                    group_attr_req->ireq,
-+                                    state->res_names);
-+    if (ret != EOK) {
-+        sbus_request_fail_and_finish(group_attr_req->ireq->dbus_req,
-+                                     sbus_error_new(
-+                                            group_attr_req->ireq->dbus_req,
-+                                            DBUS_ERROR_FAILED,
-+                                            "Failed to build a reply\n"));
-         return;
-     }
- }
--- 
-2.9.4
-
diff --git a/SOURCES/0164-ldap-handle-certmap-errors-gracefully.patch b/SOURCES/0164-ldap-handle-certmap-errors-gracefully.patch
deleted file mode 100644
index caebbc1..0000000
--- a/SOURCES/0164-ldap-handle-certmap-errors-gracefully.patch
+++ /dev/null
@@ -1,56 +0,0 @@
-From 85b74b966ec1d417ce76b05cbf3351b20c0981b2 Mon Sep 17 00:00:00 2001
-From: Sumit Bose <sbose@redhat.com>
-Date: Wed, 17 May 2017 15:43:25 +0200
-Subject: [PATCH 164/166] ldap: handle certmap errors gracefully
-
-Currently the LDAP user lookup request errors out if e.g. there is no
-matching rule for a certificate. This might cause the related domain to
-go offline.
-
-With this patch the request returns that no user was found for the given
-certificate but overall result is that the request finishes
-successfully.
-
-Resolves:
-https://pagure.io/SSSD/sssd/issue/3405
-
-Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
----
- src/providers/ldap/ldap_id.c | 21 +++++++++++++++++++++
- 1 file changed, 21 insertions(+)
-
-diff --git a/src/providers/ldap/ldap_id.c b/src/providers/ldap/ldap_id.c
-index 7400dc1f57e30cc6ae5f939ffa628a1e9dd47e06..557712e8dc2b2bde664b4054fa2f8eb39df84d73 100644
---- a/src/providers/ldap/ldap_id.c
-+++ b/src/providers/ldap/ldap_id.c
-@@ -258,6 +258,27 @@ struct tevent_req *users_get_send(TALLOC_CTX *memctx,
-         if (ret != EOK) {
-             DEBUG(SSSDBG_OP_FAILURE,
-                   "sss_cert_derb64_to_ldap_filter failed.\n");
-+
-+            /* Typically sss_cert_derb64_to_ldap_filter() will fail if there
-+             * is no mapping rule matching the current certificate. But this
-+             * just means that no matching user can be found so we can finish
-+             * the request with this result. Even if
-+             * sss_cert_derb64_to_ldap_filter() would fail for other reason
-+             * there is no need to return an error which might cause the
-+             * domain go offline. */
-+
-+            if (noexist_delete) {
-+                ret = sysdb_remove_cert(state->domain, filter_value);
-+                if (ret != EOK) {
-+                    DEBUG(SSSDBG_OP_FAILURE,
-+                          "Ignoring error while removing user certificate "
-+                          "[%d]: %s\n", ret, sss_strerror(ret));
-+                }
-+            }
-+
-+            ret = EOK;
-+            state->sdap_ret = ENOENT;
-+            state->dp_error = DP_ERR_OK;
-             goto done;
-         }
- 
--- 
-2.9.4
-
diff --git a/SOURCES/0165-SECRETS-Fix-warning-Wpointer-bool-conversion.patch b/SOURCES/0165-SECRETS-Fix-warning-Wpointer-bool-conversion.patch
deleted file mode 100644
index 0a3ecf7..0000000
--- a/SOURCES/0165-SECRETS-Fix-warning-Wpointer-bool-conversion.patch
+++ /dev/null
@@ -1,53 +0,0 @@
-From 0e1416e65c99aca947e589bfa56d5bc832c023d6 Mon Sep 17 00:00:00 2001
-From: Lukas Slebodnik <lslebodn@redhat.com>
-Date: Sat, 27 May 2017 14:39:45 +0200
-Subject: [PATCH 165/166] SECRETS: Fix warning Wpointer-bool-conversion
-
-Debug messages would always say that verify_peer and verify_host
-are enabled. Even though they would be explicitly disabled.
-
-src/responder/secrets/proxy.c:143:18: error:
-    address of 'cfg->verify_peer' will always evaluate to
-      'true' [-Werror,-Wpointer-bool-conversion]
-          (&cfg->verify_peer ? "true" : "false"));
-            ~~~~~^~~~~~~~~~~ ~
-src/util/debug.h:108:32: note: expanded from macro 'DEBUG'
-                     format, ##__VA_ARGS__); \
-                               ^~~~~~~~~~~
-src/responder/secrets/proxy.c:149:18: error:
-    address of 'cfg->verify_host' will always evaluate to
-      'true' [-Werror,-Wpointer-bool-conversion]
-          (&cfg->verify_host ? "true" : "false"));
-            ~~~~~^~~~~~~~~~~ ~
-src/util/debug.h:108:32: note: expanded from macro 'DEBUG'
-                     format, ##__VA_ARGS__); \
-                               ^~~~~~~~~~~
-
-Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
----
- src/responder/secrets/proxy.c | 4 ++--
- 1 file changed, 2 insertions(+), 2 deletions(-)
-
-diff --git a/src/responder/secrets/proxy.c b/src/responder/secrets/proxy.c
-index 9c2aa425d414728d10aa830f640632e98def3c1c..a4e97f83ef406e71a1e6509a6b719c47afdfd5b8 100644
---- a/src/responder/secrets/proxy.c
-+++ b/src/responder/secrets/proxy.c
-@@ -140,13 +140,13 @@ static int proxy_sec_get_cfg(struct proxy_context *pctx,
-                           true, &cfg->verify_peer);
-     if (ret) goto done;
-     DEBUG(SSSDBG_CONF_SETTINGS, "verify_peer: %s\n",
--          (&cfg->verify_peer ? "true" : "false"));
-+          cfg->verify_peer ? "true" : "false");
- 
-     ret = confdb_get_bool(pctx->cdb, secreq->cfg_section, "verify_host",
-                           true, &cfg->verify_host);
-     if (ret) goto done;
-     DEBUG(SSSDBG_CONF_SETTINGS, "verify_host: %s\n",
--          (&cfg->verify_host ? "true" : "false"));
-+          cfg->verify_host ? "true" : "false");
- 
-     ret = proxy_get_config_string(pctx, cfg, false, secreq,
-                                   "capath", &cfg->capath);
--- 
-2.9.4
-
diff --git a/SOURCES/0166-IPA-Fix-the-PAM-error-code-that-auth-code-expects-to.patch b/SOURCES/0166-IPA-Fix-the-PAM-error-code-that-auth-code-expects-to.patch
deleted file mode 100644
index f9eb273..0000000
--- a/SOURCES/0166-IPA-Fix-the-PAM-error-code-that-auth-code-expects-to.patch
+++ /dev/null
@@ -1,50 +0,0 @@
-From 62cebc27bd0bdb2c12531203fd79f231e96eab7b Mon Sep 17 00:00:00 2001
-From: Sumit Bose <sbose@redhat.com>
-Date: Fri, 2 Jun 2017 11:17:18 +0200
-Subject: [PATCH 166/166] IPA: Fix the PAM error code that auth code expects to
- start migration
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Recent patches which adds support for PKINIT in krb5_child changed a
-return code which is used to indicate to the IPA provider that password
-migration should be tried.
-
-With this patch krb5_child properly returns PAM_CRED_ERR as expected by
-the IPA provider in this case.
-
-Resolves:
-https://pagure.io/SSSD/sssd/issue/3394
-
-Reviewed-by: Simo Sorce <simo@redhat.com>
-Reviewed-by: Lukáš Slebodník <lslebodn@redhat.com>
----
- src/providers/krb5/krb5_child.c | 11 +++++++++++
- 1 file changed, 11 insertions(+)
-
-diff --git a/src/providers/krb5/krb5_child.c b/src/providers/krb5/krb5_child.c
-index cbbc892bee0365892ac66d3654c974d325166b60..3cd8bfba76a35acd2c885ee2aac4765a6c1cc03c 100644
---- a/src/providers/krb5/krb5_child.c
-+++ b/src/providers/krb5/krb5_child.c
-@@ -1540,6 +1540,17 @@ static krb5_error_code get_and_save_tgt(struct krb5_req *kr,
-         if (kerr != 0) {
-             KRB5_CHILD_DEBUG(SSSDBG_CRIT_FAILURE, kerr);
- 
-+            /* Special case for IPA password migration */
-+            if (kr->pd->cmd == SSS_PAM_AUTHENTICATE
-+                    && kerr == KRB5_PREAUTH_FAILED
-+                    && kr->pkinit_prompting == false
-+                    && kr->password_prompting == false
-+                    && kr->otp == false
-+                    && sss_authtok_get_type(kr->pd->authtok)
-+                            == SSS_AUTHTOK_TYPE_PASSWORD) {
-+                return ERR_CREDS_INVALID;
-+            }
-+
-             /* If during authentication either the MIT Kerberos pkinit
-              * pre-auth module is missing or no Smartcard is inserted and only
-              * pkinit is available KRB5_PREAUTH_FAILED is returned.
--- 
-2.9.4
-
diff --git a/SOURCES/0167-pam_sss-Fix-checking-of-empty-string-cert_user.patch b/SOURCES/0167-pam_sss-Fix-checking-of-empty-string-cert_user.patch
deleted file mode 100644
index b8e2d31..0000000
--- a/SOURCES/0167-pam_sss-Fix-checking-of-empty-string-cert_user.patch
+++ /dev/null
@@ -1,52 +0,0 @@
-From 1ac8b82addfa0a4c94321d5cb72b7991755e61f8 Mon Sep 17 00:00:00 2001
-From: Lukas Slebodnik <lslebodn@redhat.com>
-Date: Fri, 2 Jun 2017 11:56:55 +0200
-Subject: [PATCH 167/169] pam_sss: Fix checking of empty string cert_user
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-src/sss_client/pam_sss.c: In function ‘eval_response’:
-src/sss_client/pam_sss.c:998:64: error: comparison between pointer and zero character constant [-Werror=pointer-compare]
-                 if (type == SSS_PAM_CERT_INFO && pi->cert_user == '\0') {
-                                                                ^~
-src/sss_client/pam_sss.c:998:50: note: did you mean to dereference the pointer?
-                 if (type == SSS_PAM_CERT_INFO && pi->cert_user == '\0') {
-                                                  ^
-src/sss_client/pam_sss.c:1010:42: error: comparison between pointer and zero character constant [-Werror=pointer-compare]
-                         && pi->cert_user != '\0') {
-                                          ^~
-src/sss_client/pam_sss.c:1010:28: note: did you mean to dereference the pointer?
-                         && pi->cert_user != '\0') {
-
-Reviewed-by: Fabiano Fidêncio <fidencio@redhat.com>
-(cherry picked from commit c62dc2ac02253e130991db0f6acd60ce1a2753f1)
----
- src/sss_client/pam_sss.c | 4 ++--
- 1 file changed, 2 insertions(+), 2 deletions(-)
-
-diff --git a/src/sss_client/pam_sss.c b/src/sss_client/pam_sss.c
-index 1c06079967e3d9076d537c3de8aba93e13f76d09..9732459e6fb7ce01c9445c423cf0a583ca36e036 100644
---- a/src/sss_client/pam_sss.c
-+++ b/src/sss_client/pam_sss.c
-@@ -995,7 +995,7 @@ static int eval_response(pam_handle_t *pamh, size_t buflen, uint8_t *buf,
-                     break;
-                 }
- 
--                if (type == SSS_PAM_CERT_INFO && pi->cert_user == '\0') {
-+                if (type == SSS_PAM_CERT_INFO && *pi->cert_user == '\0') {
-                     D(("Invalid CERT message"));
-                     break;
-                 }
-@@ -1007,7 +1007,7 @@ static int eval_response(pam_handle_t *pamh, size_t buflen, uint8_t *buf,
-                 }
- 
-                 if ((pi->pam_user == NULL || *(pi->pam_user) == '\0')
--                        && pi->cert_user != '\0') {
-+                        && *pi->cert_user != '\0') {
-                     ret = pam_set_item(pamh, PAM_USER, pi->cert_user);
-                     if (ret != PAM_SUCCESS) {
-                         D(("Failed to set PAM_USER during "
--- 
-2.9.4
-
diff --git a/SOURCES/0168-CACHE_REQ-Simplify-_search_ncache_filter.patch b/SOURCES/0168-CACHE_REQ-Simplify-_search_ncache_filter.patch
deleted file mode 100644
index 67e6f57..0000000
--- a/SOURCES/0168-CACHE_REQ-Simplify-_search_ncache_filter.patch
+++ /dev/null
@@ -1,124 +0,0 @@
-From 992a6410a3100cc64f9f2ea674fda9151fa5d474 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Fabiano=20Fid=C3=AAncio?= <fidencio@redhat.com>
-Date: Mon, 29 May 2017 14:58:33 +0200
-Subject: [PATCH 168/169] CACHE_REQ: Simplify _search_ncache_filter()
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Let's make the result and input/output argument for
-_search_ncache_filter() and free it inside the function whenever it's
-needed instead of leaving this responsibility for the caller.
-
-Related:
-https://pagure.io/SSSD/sssd/issue/3362
-
-Signed-off-by: Fabiano Fidêncio <fidencio@redhat.com>
-Reviewed-by: Pavel Březina <pbrezina@redhat.com>
-(cherry picked from commit c8193b1602cf44740b59f5dfcdc5330508c0c365)
----
- src/responder/common/cache_req/cache_req_search.c | 27 ++++++-----------------
- 1 file changed, 7 insertions(+), 20 deletions(-)
-
-diff --git a/src/responder/common/cache_req/cache_req_search.c b/src/responder/common/cache_req/cache_req_search.c
-index 70448a7639bc9f98d380b8edce9d130adfa0ceb2..d3aaa7542ddfd28716fbf9cdcedfeadb649dbaa0 100644
---- a/src/responder/common/cache_req/cache_req_search.c
-+++ b/src/responder/common/cache_req/cache_req_search.c
-@@ -86,7 +86,6 @@ static void cache_req_search_ncache_add(struct cache_req *cr)
- 
- static errno_t cache_req_search_ncache_filter(TALLOC_CTX *mem_ctx,
-                                               struct cache_req *cr,
--                                              struct ldb_result *result,
-                                               struct ldb_result **_result)
- {
-     TALLOC_CTX *tmp_ctx;
-@@ -106,8 +105,6 @@ static errno_t cache_req_search_ncache_filter(TALLOC_CTX *mem_ctx,
-                         "This request type does not support filtering "
-                         "result by negative cache\n");
- 
--        *_result = talloc_steal(mem_ctx, result);
--
-         ret = EOK;
-         goto done;
-     }
-@@ -115,11 +112,11 @@ static errno_t cache_req_search_ncache_filter(TALLOC_CTX *mem_ctx,
-     CACHE_REQ_DEBUG(SSSDBG_TRACE_FUNC, cr,
-                     "Filtering out results by negative cache\n");
- 
--    msgs = talloc_zero_array(tmp_ctx, struct ldb_message *, result->count);
-+    msgs = talloc_zero_array(tmp_ctx, struct ldb_message *, (*_result)->count);
-     msg_count = 0;
- 
--    for (size_t i = 0; i < result->count; i++) {
--        name = sss_get_name_from_msg(cr->domain, result->msgs[i]);
-+    for (size_t i = 0; i < (*_result)->count; i++) {
-+        name = sss_get_name_from_msg(cr->domain, (*_result)->msgs[i]);
-         if (name == NULL) {
-             CACHE_REQ_DEBUG(SSSDBG_CRIT_FAILURE, cr,
-                   "sss_get_name_from_msg() returned NULL, which should never "
-@@ -141,7 +138,7 @@ static errno_t cache_req_search_ncache_filter(TALLOC_CTX *mem_ctx,
-             goto done;
-         }
- 
--        msgs[msg_count] = talloc_steal(msgs, result->msgs[i]);
-+        msgs[msg_count] = talloc_steal(msgs, (*_result)->msgs[i]);
-         msg_count++;
-     }
- 
-@@ -157,6 +154,7 @@ static errno_t cache_req_search_ncache_filter(TALLOC_CTX *mem_ctx,
-         goto done;
-     }
- 
-+    talloc_zfree(*_result);
-     *_result = talloc_steal(mem_ctx, filtered_result);
-     ret = EOK;
- 
-@@ -419,10 +417,8 @@ static void cache_req_search_oob_done(struct tevent_req *subreq)
- 
- static void cache_req_search_done(struct tevent_req *subreq)
- {
--    TALLOC_CTX *tmp_ctx;
-     struct cache_req_search_state *state;
-     struct tevent_req *req;
--    struct ldb_result *result = NULL;
-     errno_t ret;
- 
-     req = tevent_req_callback_data(subreq, struct tevent_req);
-@@ -431,14 +427,8 @@ static void cache_req_search_done(struct tevent_req *subreq)
-     state->dp_success = state->cr->plugin->dp_recv_fn(subreq, state->cr);
-     talloc_zfree(subreq);
- 
--    tmp_ctx = talloc_new(NULL);
--    if (tmp_ctx == NULL) {
--        ret = ENOMEM;
--        goto done;
--    }
--
-     /* Get result from cache again. */
--    ret = cache_req_search_cache(tmp_ctx, state->cr, &result);
-+    ret = cache_req_search_cache(state, state->cr, &state->result);
-     if (ret != EOK) {
-         if (ret == ENOENT) {
-             /* Only store entry in negative cache if DP request succeeded
-@@ -451,8 +441,7 @@ static void cache_req_search_done(struct tevent_req *subreq)
-     }
- 
-     /* ret == EOK */
--    ret = cache_req_search_ncache_filter(state, state->cr, result,
--                                         &state->result);
-+    ret = cache_req_search_ncache_filter(state, state->cr, &state->result);
-     if (ret != EOK) {
-         goto done;
-     }
-@@ -461,8 +450,6 @@ static void cache_req_search_done(struct tevent_req *subreq)
-                     "Returning updated object [%s]\n", state->cr->debugobj);
- 
- done:
--    talloc_free(tmp_ctx);
--
-     if (ret != EOK) {
-         tevent_req_error(req, ret);
-         return;
--- 
-2.9.4
-
diff --git a/SOURCES/0169-CACHE_REQ_SEARCH-Check-for-filtered-users-groups-als.patch b/SOURCES/0169-CACHE_REQ_SEARCH-Check-for-filtered-users-groups-als.patch
deleted file mode 100644
index 6d53d21..0000000
--- a/SOURCES/0169-CACHE_REQ_SEARCH-Check-for-filtered-users-groups-als.patch
+++ /dev/null
@@ -1,61 +0,0 @@
-From 79f389eb400eddc133824b079f8bd49ced24643b Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Fabiano=20Fid=C3=AAncio?= <fidencio@redhat.com>
-Date: Wed, 17 May 2017 14:43:39 +0200
-Subject: [PATCH 169/169] CACHE_REQ_SEARCH: Check for filtered users/groups
- also on cache_req_send()
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-cache_req_send() may take some shortcuts in case the object is found in
-the cache and it's still valid.
-
-This behaviour may lead to exposing filtered users and groups when
-they're searched by their uid/gid.
-
-A solution for this issue was proposed on 4ef0b19a but, unfortunately,
-didn't take into consideration that this shortcut could be taken.
-
-There are basically two really easy ways to test this issue:
- 1) Using enumeration:
-    - Set "enumerate = True" in the domain section
-    - restart SSSD cleaning up the cache;
-    - getent passwd <uid of a user who is part of the filter_users>
-    - Wait a little bit till the entry_negative_timeout is expired
-    - getent passwd <same uid used above>
-
- 2) Not using enumeration:
-   - getent passwd <uid of a user who is part of the filter_users>
-   - Wait a little bit till the entry_negative_timeout is expired
-   - getent passwd <same uid used above>
-
-A test covering this code path will be added in the follow-up commit.
-
-Resolves:
-https://pagure.io/SSSD/sssd/issue/3362
-
-Signed-off-by: Fabiano Fidêncio <fidencio@redhat.com>
-Reviewed-by: Pavel Březina <pbrezina@redhat.com>
-(cherry picked from commit 4c09cd008967c5c0ec358dc658ffc6fc1cef2697)
----
- src/responder/common/cache_req/cache_req_search.c | 4 ++++
- 1 file changed, 4 insertions(+)
-
-diff --git a/src/responder/common/cache_req/cache_req_search.c b/src/responder/common/cache_req/cache_req_search.c
-index d3aaa7542ddfd28716fbf9cdcedfeadb649dbaa0..56d0345cd8f98de574961d3c9628ae7a4c24f9be 100644
---- a/src/responder/common/cache_req/cache_req_search.c
-+++ b/src/responder/common/cache_req/cache_req_search.c
-@@ -334,6 +334,10 @@ cache_req_search_send(TALLOC_CTX *mem_ctx,
- 
- done:
-     if (ret == EOK) {
-+        ret = cache_req_search_ncache_filter(state, cr, &state->result);
-+    }
-+
-+    if (ret == EOK) {
-         tevent_req_done(req);
-     } else {
-         tevent_req_error(req, ret);
--- 
-2.9.4
-
diff --git a/SOURCES/0170-cache_req-Do-not-use-default_domain_suffix-with-netg.patch b/SOURCES/0170-cache_req-Do-not-use-default_domain_suffix-with-netg.patch
deleted file mode 100644
index 78326bf..0000000
--- a/SOURCES/0170-cache_req-Do-not-use-default_domain_suffix-with-netg.patch
+++ /dev/null
@@ -1,34 +0,0 @@
-From eb3f60eacc6279a6bd97eff7d7be0cc081a7bf9a Mon Sep 17 00:00:00 2001
-From: Lukas Slebodnik <lslebodn@redhat.com>
-Date: Thu, 8 Jun 2017 12:32:44 +0200
-Subject: [PATCH 170/171] cache_req: Do not use default_domain_suffix with
- netgroups
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Resolves:
-https://pagure.io/SSSD/sssd/issue/3428
-
-Reviewed-by: Pavel Březina <pbrezina@redhat.com>
-(cherry picked from commit c83e265bbb5b2f2aa4f0067263753c8403c383f9)
----
- src/responder/common/cache_req/plugins/cache_req_netgroup_by_name.c | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
-diff --git a/src/responder/common/cache_req/plugins/cache_req_netgroup_by_name.c b/src/responder/common/cache_req/plugins/cache_req_netgroup_by_name.c
-index 4d8bb18579a286042b00528190dadd52fdd7c75c..ef0775d0b8eac4d679450f436d8427cff9c04582 100644
---- a/src/responder/common/cache_req/plugins/cache_req_netgroup_by_name.c
-+++ b/src/responder/common/cache_req/plugins/cache_req_netgroup_by_name.c
-@@ -112,7 +112,7 @@ const struct cache_req_plugin cache_req_netgroup_by_name = {
-     .name = "Netgroup by name",
-     .attr_expiration = SYSDB_CACHE_EXPIRE,
-     .parse_name = true,
--    .ignore_default_domain = false,
-+    .ignore_default_domain = true,
-     .bypass_cache = false,
-     .only_one_result = true,
-     .search_all_domains = false,
--- 
-2.9.4
-
diff --git a/SOURCES/0171-krb5-disable-enterprise-principals-during-password-c.patch b/SOURCES/0171-krb5-disable-enterprise-principals-during-password-c.patch
deleted file mode 100644
index 22655c1..0000000
--- a/SOURCES/0171-krb5-disable-enterprise-principals-during-password-c.patch
+++ /dev/null
@@ -1,47 +0,0 @@
-From 0956acb31884e87ef48c3be8c59960acfc03a547 Mon Sep 17 00:00:00 2001
-From: Sumit Bose <sbose@redhat.com>
-Date: Thu, 8 Jun 2017 11:06:02 +0200
-Subject: [PATCH 171/171] krb5: disable enterprise principals during password
- changes
-
-Currently using enterprise principals during password changes does not
-work reliable.
-
-First there is a special behavior if canonicalization, which in general
-should be used together with enterprise principals, is enabled with AD,
-see https://pagure.io/SSSD/sssd/issue/1405 and
-https://pagure.io/SSSD/sssd/issue/1615 for details. As a result of this
-SSSD currently disables canonicalization during password changes.
-
-Additionally it looks like MIT Kerberos does not handle canonicalized
-principals well, even if canonicalization is enabled, if not the default
-krbtgt/REALM@REALM but kadmin/changepw@REALM is requested. Since it is
-currently not clear what is the expected behavior here it make sense to
-completely disable enterprise principals during password changes for the
-time being.
-
-Resolves https://pagure.io/SSSD/sssd/issue/3426
-
-Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
-(cherry picked from commit 614057ea85c05d3a6d4b62217a41b8b5db8d5d38)
----
- src/providers/krb5/krb5_child_handler.c | 3 ++-
- 1 file changed, 2 insertions(+), 1 deletion(-)
-
-diff --git a/src/providers/krb5/krb5_child_handler.c b/src/providers/krb5/krb5_child_handler.c
-index 11ac867e62d2ff96b827cf6d4ff341fc8ff0a286..0007f92a61ba711aed6be5ee28664e5f7de0f226 100644
---- a/src/providers/krb5/krb5_child_handler.c
-+++ b/src/providers/krb5/krb5_child_handler.c
-@@ -143,7 +143,8 @@ static errno_t create_send_buffer(struct krb5child_req *kr,
-         return EINVAL;
-     }
- 
--    if (kr->pd->cmd == SSS_CMD_RENEW || kr->is_offline) {
-+    if (kr->pd->cmd == SSS_CMD_RENEW || kr->pd->cmd == SSS_PAM_CHAUTHTOK_PRELIM
-+            || kr->pd->cmd == SSS_PAM_CHAUTHTOK || kr->is_offline) {
-         use_enterprise_principal = false;
-     } else {
-         use_enterprise_principal = dp_opt_get_bool(kr->krb5_ctx->opts,
--- 
-2.9.4
-
diff --git a/SOURCES/0172-pam_sss-Fix-leaking-of-memory-in-case-of-failures.patch b/SOURCES/0172-pam_sss-Fix-leaking-of-memory-in-case-of-failures.patch
deleted file mode 100644
index 7c9d403..0000000
--- a/SOURCES/0172-pam_sss-Fix-leaking-of-memory-in-case-of-failures.patch
+++ /dev/null
@@ -1,44 +0,0 @@
-From c58aac42664dd1a04edb37b0874109a6a88d0da1 Mon Sep 17 00:00:00 2001
-From: Lukas Slebodnik <lslebodn@redhat.com>
-Date: Mon, 5 Jun 2017 09:43:46 +0200
-Subject: [PATCH 172/181] pam_sss: Fix leaking of memory in case of failures
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Found by coverity.
-
-Reviewed-by: Fabiano Fidêncio <fidencio@redhat.com>
-(cherry picked from commit 818d01b4a0d332fff06db33c0c985b8c0f1417c7)
----
- src/sss_client/pam_sss.c | 3 +++
- 1 file changed, 3 insertions(+)
-
-diff --git a/src/sss_client/pam_sss.c b/src/sss_client/pam_sss.c
-index 9732459e6fb7ce01c9445c423cf0a583ca36e036..303809b9ea05b5a8709c05ae230d5f289b57de31 100644
---- a/src/sss_client/pam_sss.c
-+++ b/src/sss_client/pam_sss.c
-@@ -1517,10 +1517,12 @@ static int prompt_sc_pin(pam_handle_t *pamh, struct pam_items *pi)
-     if (pi->user_name_hint) {
-         ret = pam_get_item(pamh, PAM_CONV, (const void **)&conv);
-         if (ret != PAM_SUCCESS) {
-+            free(prompt);
-             return ret;
-         }
-         if (conv == NULL || conv->conv == NULL) {
-             logger(pamh, LOG_ERR, "No conversation function");
-+            free(prompt);
-             return PAM_SYSTEM_ERR;
-         }
- 
-@@ -1540,6 +1542,7 @@ static int prompt_sc_pin(pam_handle_t *pamh, struct pam_items *pi)
-         mesg[1] = &((*mesg)[1]);
- 
-         ret = conv->conv(2, mesg, &resp, conv->appdata_ptr);
-+        free(prompt);
-         if (ret != PAM_SUCCESS) {
-             D(("Conversation failure: %s.", pam_strerror(pamh, ret)));
-             return ret;
--- 
-2.9.4
-
diff --git a/SOURCES/0173-IFP-Add-domain-and-domainname-attributes-to-the-user.patch b/SOURCES/0173-IFP-Add-domain-and-domainname-attributes-to-the-user.patch
deleted file mode 100644
index be61e37..0000000
--- a/SOURCES/0173-IFP-Add-domain-and-domainname-attributes-to-the-user.patch
+++ /dev/null
@@ -1,372 +0,0 @@
-From a35b5c33a76857ad9223363e15558facec5c269d Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Pavel=20B=C5=99ezina?= <pbrezina@redhat.com>
-Date: Thu, 8 Jun 2017 11:46:25 +0200
-Subject: [PATCH 173/181] IFP: Add domain and domainname attributes to the user
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-org.freedekstop.sssd.infopipe.Users.User gets two new attributes:
-- domain: object path of user's domain
-- domainname: user's domain name
-
-org.freedekstop.sssd.infopipe.GetUserAttr can now request new attribute:
-- domainname: user's domain name
-
-Resolves:
-https://pagure.io/SSSD/sssd/issue/2714
-
-Reviewed-by: Fabiano Fidêncio <fidencio@redhat.com>
-(cherry picked from commit 37d2194cc9ea4d0254c88a3419e2376572562bab)
----
- src/responder/ifp/ifp_iface.c           |  2 +
- src/responder/ifp/ifp_iface.xml         |  2 +
- src/responder/ifp/ifp_iface_generated.c | 18 ++++++++
- src/responder/ifp/ifp_iface_generated.h |  4 ++
- src/responder/ifp/ifp_private.h         |  4 ++
- src/responder/ifp/ifp_users.c           | 46 ++++++++++++++++++++
- src/responder/ifp/ifp_users.h           |  8 ++++
- src/responder/ifp/ifpsrv_cmd.c          |  8 ++++
- src/responder/ifp/ifpsrv_util.c         | 74 ++++++++++++++++++++++++++++++++-
- src/tests/cmocka/test_ifp.c             | 12 ++++--
- 10 files changed, 173 insertions(+), 5 deletions(-)
-
-diff --git a/src/responder/ifp/ifp_iface.c b/src/responder/ifp/ifp_iface.c
-index e413e74f955c067a0efbe385a08b4b2cc6f2bba1..3293b92d750d33b2ecf77a03098c5169d052c924 100644
---- a/src/responder/ifp/ifp_iface.c
-+++ b/src/responder/ifp/ifp_iface.c
-@@ -104,6 +104,8 @@ struct iface_ifp_users_user iface_ifp_users_user = {
-     .get_loginShell = ifp_users_user_get_login_shell,
-     .get_uniqueID = ifp_users_user_get_unique_id,
-     .get_groups = ifp_users_user_get_groups,
-+    .get_domain = ifp_users_user_get_domain,
-+    .get_domainname = ifp_users_user_get_domainname,
-     .get_extraAttributes = ifp_users_user_get_extra_attributes
- };
- 
-diff --git a/src/responder/ifp/ifp_iface.xml b/src/responder/ifp/ifp_iface.xml
-index 0a23f56907f64c4c24db3ec3c0a312adbdb3edc8..ce071bb999bd207b8cc81f054da80de52a13d3df 100644
---- a/src/responder/ifp/ifp_iface.xml
-+++ b/src/responder/ifp/ifp_iface.xml
-@@ -188,6 +188,8 @@
-         <property name="loginShell" type="s" access="read" />
-         <property name="uniqueID" type="s" access="read" />
-         <property name="groups" type="ao" access="read" />
-+        <property name="domain" type="o" access="read" />
-+        <property name="domainname" type="s" access="read" />
-         <property name="extraAttributes" type="a{sas}" access="read" />
-     </interface>
- 
-diff --git a/src/responder/ifp/ifp_iface_generated.c b/src/responder/ifp/ifp_iface_generated.c
-index 211646b6760d15e0df55ac20b9611b800b11d16c..51db4a9e5c7d72663f8845bd0da22d3f21526be8 100644
---- a/src/responder/ifp/ifp_iface_generated.c
-+++ b/src/responder/ifp/ifp_iface_generated.c
-@@ -982,6 +982,24 @@ const struct sbus_property_meta iface_ifp_users_user__properties[] = {
-         NULL, /* no invoker */
-     },
-     {
-+        "domain", /* name */
-+        "o", /* type */
-+        SBUS_PROPERTY_READABLE,
-+        offsetof(struct iface_ifp_users_user, get_domain),
-+        sbus_invoke_get_o,
-+        0, /* not writable */
-+        NULL, /* no invoker */
-+    },
-+    {
-+        "domainname", /* name */
-+        "s", /* type */
-+        SBUS_PROPERTY_READABLE,
-+        offsetof(struct iface_ifp_users_user, get_domainname),
-+        sbus_invoke_get_s,
-+        0, /* not writable */
-+        NULL, /* no invoker */
-+    },
-+    {
-         "extraAttributes", /* name */
-         "a{sas}", /* type */
-         SBUS_PROPERTY_READABLE,
-diff --git a/src/responder/ifp/ifp_iface_generated.h b/src/responder/ifp/ifp_iface_generated.h
-index e69fc3a3efc6bdcef5d6539790908795818cd857..76f729fcb268e9c07668b3a5ee5bbd7d0b44ca16 100644
---- a/src/responder/ifp/ifp_iface_generated.h
-+++ b/src/responder/ifp/ifp_iface_generated.h
-@@ -88,6 +88,8 @@
- #define IFACE_IFP_USERS_USER_LOGINSHELL "loginShell"
- #define IFACE_IFP_USERS_USER_UNIQUEID "uniqueID"
- #define IFACE_IFP_USERS_USER_GROUPS "groups"
-+#define IFACE_IFP_USERS_USER_DOMAIN "domain"
-+#define IFACE_IFP_USERS_USER_DOMAINNAME "domainname"
- #define IFACE_IFP_USERS_USER_EXTRAATTRIBUTES "extraAttributes"
- 
- /* constants for org.freedesktop.sssd.infopipe.Groups */
-@@ -288,6 +290,8 @@ struct iface_ifp_users_user {
-     void (*get_loginShell)(struct sbus_request *, void *data, const char **);
-     void (*get_uniqueID)(struct sbus_request *, void *data, const char **);
-     void (*get_groups)(struct sbus_request *, void *data, const char ***, int *);
-+    void (*get_domain)(struct sbus_request *, void *data, const char **);
-+    void (*get_domainname)(struct sbus_request *, void *data, const char **);
-     void (*get_extraAttributes)(struct sbus_request *, void *data, hash_table_t **);
- };
- 
-diff --git a/src/responder/ifp/ifp_private.h b/src/responder/ifp/ifp_private.h
-index e800070a59f77f8ce58a2fc402e616bb773e996b..a6e5701b8d1ebb27af0c35fa3ebe0c6c00d16bd6 100644
---- a/src/responder/ifp/ifp_private.h
-+++ b/src/responder/ifp/ifp_private.h
-@@ -70,6 +70,10 @@ errno_t ifp_req_create(struct sbus_request *dbus_req,
- /* Returns an appropriate DBus error for specific ifp_req_create failures */
- int ifp_req_create_handle_failure(struct sbus_request *dbus_req, errno_t err);
- 
-+errno_t ifp_add_value_to_dict(DBusMessageIter *iter_dict,
-+                              const char *key,
-+                              const char *value);
-+
- errno_t ifp_add_ldb_el_to_dict(DBusMessageIter *iter_dict,
-                                struct ldb_message_element *el);
- const char **
-diff --git a/src/responder/ifp/ifp_users.c b/src/responder/ifp/ifp_users.c
-index 188194f2ab356d0e67b0f26b003f3a9ce48e6acd..90b947ed9ca345fbeba6772c90f898451a0868aa 100644
---- a/src/responder/ifp/ifp_users.c
-+++ b/src/responder/ifp/ifp_users.c
-@@ -1328,6 +1328,52 @@ void ifp_users_user_get_groups(struct sbus_request *sbus_req,
-     *_size = num_groups;
- }
- 
-+void ifp_users_user_get_domain(struct sbus_request *sbus_req,
-+                               void *data,
-+                               const char **_out)
-+{
-+    const char *domainname;
-+
-+    *_out = NULL;
-+    ifp_users_user_get_domainname(sbus_req, data, &domainname);
-+
-+    if (domainname == NULL) {
-+        return;
-+    }
-+
-+    *_out = sbus_opath_compose(sbus_req, IFP_PATH_DOMAINS,
-+                               domainname);
-+}
-+
-+void ifp_users_user_get_domainname(struct sbus_request *sbus_req,
-+                                   void *data,
-+                                   const char **_out)
-+{
-+    struct ifp_ctx *ifp_ctx;
-+    struct sss_domain_info *domain;
-+    errno_t ret;
-+
-+    *_out = NULL;
-+
-+    ifp_ctx = talloc_get_type(data, struct ifp_ctx);
-+    if (ifp_ctx == NULL) {
-+        DEBUG(SSSDBG_CRIT_FAILURE, "Invalid pointer!\n");
-+        return;
-+    }
-+
-+    if (!ifp_is_user_attr_allowed(ifp_ctx, "domainname")) {
-+        DEBUG(SSSDBG_TRACE_ALL, "Attribute domainname is not allowed\n");
-+        return;
-+    }
-+
-+    ret = ifp_users_user_get(sbus_req, ifp_ctx, &domain, NULL);
-+    if (ret != EOK) {
-+        return;
-+    }
-+
-+    *_out = domain->name;
-+}
-+
- void ifp_users_user_get_extra_attributes(struct sbus_request *sbus_req,
-                                          void *data,
-                                          hash_table_t **_out)
-diff --git a/src/responder/ifp/ifp_users.h b/src/responder/ifp/ifp_users.h
-index f8fefeb7f658b6e0a5f72371da1b025d69e6f412..715a8bc31996bfd93c21dbe263f2567bd0b50b03 100644
---- a/src/responder/ifp/ifp_users.h
-+++ b/src/responder/ifp/ifp_users.h
-@@ -103,6 +103,14 @@ void ifp_users_user_get_groups(struct sbus_request *sbus_req,
-                                const char ***_out,
-                                int *_size);
- 
-+void ifp_users_user_get_domain(struct sbus_request *sbus_req,
-+                               void *data,
-+                               const char **_out);
-+
-+void ifp_users_user_get_domainname(struct sbus_request *sbus_req,
-+                                   void *data,
-+                                   const char **_out);
-+
- void ifp_users_user_get_extra_attributes(struct sbus_request *sbus_req,
-                                          void *data,
-                                          hash_table_t **_out);
-diff --git a/src/responder/ifp/ifpsrv_cmd.c b/src/responder/ifp/ifpsrv_cmd.c
-index 70728e1bb656fd032b7f1c240683e8aa3b91a726..d86aed57206ba8f0a6facbd64051fa7c901513f3 100644
---- a/src/responder/ifp/ifpsrv_cmd.c
-+++ b/src/responder/ifp/ifpsrv_cmd.c
-@@ -233,6 +233,14 @@ ifp_user_get_attr_handle_reply(struct sss_domain_info *domain,
-         }
- 
-         for (ai = 0; attrs[ai]; ai++) {
-+            if (strcmp(attrs[ai], "domainname") == 0) {
-+                ret = ifp_add_value_to_dict(&iter_dict, "domainname",
-+                                            domain->name);
-+                DEBUG(SSSDBG_MINOR_FAILURE,
-+                      "Cannot add attribute %s to message\n", attrs[ai]);
-+                continue;
-+            }
-+
-             el = sss_view_ldb_msg_find_element(domain, res->msgs[0], attrs[ai]);
-             if (el == NULL || el->num_values == 0) {
-                 DEBUG(SSSDBG_MINOR_FAILURE,
-diff --git a/src/responder/ifp/ifpsrv_util.c b/src/responder/ifp/ifpsrv_util.c
-index 5866d30d8a5845c21f5b05fc5de150162eba747e..643881515fb4805ae93ba56c3bca9d1da7796319 100644
---- a/src/responder/ifp/ifpsrv_util.c
-+++ b/src/responder/ifp/ifpsrv_util.c
-@@ -29,7 +29,7 @@
- #define IFP_USER_DEFAULT_ATTRS {SYSDB_NAME, SYSDB_UIDNUM,   \
-                                 SYSDB_GIDNUM, SYSDB_GECOS,  \
-                                 SYSDB_HOMEDIR, SYSDB_SHELL, \
--                                "groups", \
-+                                "groups", "domain", "domainname", \
-                                 NULL}
- 
- errno_t ifp_req_create(struct sbus_request *dbus_req,
-@@ -100,6 +100,78 @@ int ifp_req_create_handle_failure(struct sbus_request *dbus_req, errno_t err)
-                                             "Cannot create IFP request\n"));
- }
- 
-+errno_t ifp_add_value_to_dict(DBusMessageIter *iter_dict,
-+                              const char *key,
-+                              const char *value)
-+{
-+    DBusMessageIter iter_dict_entry;
-+    DBusMessageIter iter_dict_val;
-+    DBusMessageIter iter_array;
-+    dbus_bool_t dbret;
-+
-+    if (value == NULL || key == NULL) {
-+        return EINVAL;
-+    }
-+
-+    dbret = dbus_message_iter_open_container(iter_dict,
-+                                             DBUS_TYPE_DICT_ENTRY, NULL,
-+                                             &iter_dict_entry);
-+    if (!dbret) {
-+        return ENOMEM;
-+    }
-+
-+    /* Start by appending the key */
-+    dbret = dbus_message_iter_append_basic(&iter_dict_entry,
-+                                           DBUS_TYPE_STRING, &key);
-+    if (!dbret) {
-+        return ENOMEM;
-+    }
-+
-+    dbret = dbus_message_iter_open_container(&iter_dict_entry,
-+                                             DBUS_TYPE_VARIANT,
-+                                             DBUS_TYPE_ARRAY_AS_STRING
-+                                             DBUS_TYPE_STRING_AS_STRING,
-+                                             &iter_dict_val);
-+    if (!dbret) {
-+        return ENOMEM;
-+    }
-+
-+    /* Open container for values */
-+    dbret = dbus_message_iter_open_container(&iter_dict_val,
-+                                 DBUS_TYPE_ARRAY, DBUS_TYPE_STRING_AS_STRING,
-+                                 &iter_array);
-+    if (!dbret) {
-+        return ENOMEM;
-+    }
-+
-+    dbret = dbus_message_iter_append_basic(&iter_array,
-+                                           DBUS_TYPE_STRING,
-+                                           &value);
-+    if (!dbret) {
-+        return ENOMEM;
-+    }
-+
-+    dbret = dbus_message_iter_close_container(&iter_dict_val,
-+                                              &iter_array);
-+    if (!dbret) {
-+        return ENOMEM;
-+    }
-+
-+    dbret = dbus_message_iter_close_container(&iter_dict_entry,
-+                                              &iter_dict_val);
-+    if (!dbret) {
-+        return ENOMEM;
-+    }
-+
-+    dbret = dbus_message_iter_close_container(iter_dict,
-+                                              &iter_dict_entry);
-+    if (!dbret) {
-+        return ENOMEM;
-+    }
-+
-+    return EOK;
-+}
-+
- errno_t ifp_add_ldb_el_to_dict(DBusMessageIter *iter_dict,
-                                struct ldb_message_element *el)
- {
-diff --git a/src/tests/cmocka/test_ifp.c b/src/tests/cmocka/test_ifp.c
-index 21c5475d1c74cd8325815653166bef194ea84f7b..45f718341222c6803a65130741590e10e7aded84 100644
---- a/src/tests/cmocka/test_ifp.c
-+++ b/src/tests/cmocka/test_ifp.c
-@@ -269,7 +269,7 @@ void test_attr_acl(void **state)
-     const char *exp_defaults[] = { SYSDB_NAME, SYSDB_UIDNUM,
-                                    SYSDB_GIDNUM, SYSDB_GECOS,
-                                    SYSDB_HOMEDIR, SYSDB_SHELL,
--                                   "groups", NULL };
-+                                   "groups", "domain", "domainname", NULL };
-     attr_parse_test(exp_defaults, NULL);
- 
-     /* Test adding some attributes to the defaults */
-@@ -277,13 +277,14 @@ void test_attr_acl(void **state)
-                               SYSDB_NAME, SYSDB_UIDNUM,
-                               SYSDB_GIDNUM, SYSDB_GECOS,
-                               SYSDB_HOMEDIR, SYSDB_SHELL,
--                              "groups", NULL };
-+                              "groups", "domain", "domainname", NULL };
-     attr_parse_test(exp_add, "+telephoneNumber, +streetAddress");
- 
-     /* Test removing some attributes to the defaults */
-     const char *exp_rm[] = { SYSDB_NAME,
-                              SYSDB_GIDNUM, SYSDB_GECOS,
-                              SYSDB_HOMEDIR, "groups",
-+                             "domain", "domainname",
-                              NULL };
-     attr_parse_test(exp_rm, "-"SYSDB_SHELL ",-"SYSDB_UIDNUM);
- 
-@@ -292,6 +293,7 @@ void test_attr_acl(void **state)
-                                  SYSDB_NAME, SYSDB_UIDNUM,
-                                  SYSDB_GIDNUM, SYSDB_GECOS,
-                                  SYSDB_HOMEDIR, "groups",
-+                                 "domain", "domainname",
-                                  NULL };
-     attr_parse_test(exp_add_rm, "+telephoneNumber, -"SYSDB_SHELL);
- 
-@@ -299,7 +301,8 @@ void test_attr_acl(void **state)
-     const char *exp_add_rm_override[] = { SYSDB_NAME, SYSDB_UIDNUM,
-                                           SYSDB_GIDNUM, SYSDB_GECOS,
-                                           SYSDB_HOMEDIR, SYSDB_SHELL,
--                                          "groups", NULL };
-+                                          "groups", "domain",
-+                                          "domainname", NULL };
-     attr_parse_test(exp_add_rm_override,
-                     "+telephoneNumber, -telephoneNumber, +telephoneNumber");
- 
-@@ -307,7 +310,8 @@ void test_attr_acl(void **state)
-     const char *rm_all[] = { NULL };
-     attr_parse_test(rm_all,  "-"SYSDB_NAME ", -"SYSDB_UIDNUM
-                              ", -"SYSDB_GIDNUM ", -"SYSDB_GECOS
--                             ", -"SYSDB_HOMEDIR ", -"SYSDB_SHELL", -groups");
-+                             ", -"SYSDB_HOMEDIR ", -"SYSDB_SHELL", -groups, "
-+                             "-domain, -domainname");
- 
-     /* Malformed list */
-     attr_parse_test(NULL,  "missing_plus_or_minus");
--- 
-2.9.4
-
diff --git a/SOURCES/0174-IFP-Fix-error-handling-in-ifp_user_get_attr_handle_r.patch b/SOURCES/0174-IFP-Fix-error-handling-in-ifp_user_get_attr_handle_r.patch
deleted file mode 100644
index 4d39c22..0000000
--- a/SOURCES/0174-IFP-Fix-error-handling-in-ifp_user_get_attr_handle_r.patch
+++ /dev/null
@@ -1,39 +0,0 @@
-From 271679e7a7c0c50e39c7a0989dbae77385475c60 Mon Sep 17 00:00:00 2001
-From: Jakub Hrozek <jhrozek@redhat.com>
-Date: Wed, 14 Jun 2017 18:25:21 +0200
-Subject: [PATCH 174/181] IFP: Fix error handling in
- ifp_user_get_attr_handle_reply()
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-This bug was introduced in 37d2194cc9ea4d0254c88a3419e2376572562bab
-
-Reviewed-by: Fabiano Fidêncio <fidencio@redhat.com>
-(cherry picked from commit 15a76bb7bd9791a3ed1ae416f70753d32c6ff599)
----
- src/responder/ifp/ifpsrv_cmd.c | 8 +++++---
- 1 file changed, 5 insertions(+), 3 deletions(-)
-
-diff --git a/src/responder/ifp/ifpsrv_cmd.c b/src/responder/ifp/ifpsrv_cmd.c
-index d86aed57206ba8f0a6facbd64051fa7c901513f3..fc9161e82e906ac7dde2712ffc7c0cbb58c519b7 100644
---- a/src/responder/ifp/ifpsrv_cmd.c
-+++ b/src/responder/ifp/ifpsrv_cmd.c
-@@ -236,9 +236,11 @@ ifp_user_get_attr_handle_reply(struct sss_domain_info *domain,
-             if (strcmp(attrs[ai], "domainname") == 0) {
-                 ret = ifp_add_value_to_dict(&iter_dict, "domainname",
-                                             domain->name);
--                DEBUG(SSSDBG_MINOR_FAILURE,
--                      "Cannot add attribute %s to message\n", attrs[ai]);
--                continue;
-+                if (ret != EOK) {
-+                    DEBUG(SSSDBG_MINOR_FAILURE,
-+                          "Cannot add attribute domainname to message\n");
-+                    continue;
-+                }
-             }
- 
-             el = sss_view_ldb_msg_find_element(domain, res->msgs[0], attrs[ai]);
--- 
-2.9.4
-
diff --git a/SOURCES/0175-SYSDB-Return-ERR_NO_TS-when-there-s-no-timestamp-cac.patch b/SOURCES/0175-SYSDB-Return-ERR_NO_TS-when-there-s-no-timestamp-cac.patch
deleted file mode 100644
index 748f9e8..0000000
--- a/SOURCES/0175-SYSDB-Return-ERR_NO_TS-when-there-s-no-timestamp-cac.patch
+++ /dev/null
@@ -1,76 +0,0 @@
-From 10b75d84300726e5e311b0488352b891f106d631 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Fabiano=20Fid=C3=AAncio?= <fidencio@redhat.com>
-Date: Wed, 24 May 2017 00:35:23 +0200
-Subject: [PATCH 175/181] SYSDB: Return ERR_NO_TS when there's no timestamp
- cache present
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-This change affects sysdb_search_ts_{users,groups} functions and is
-mainly needed in order to avoid breaking our current tests due to the
-changes planned for fixing https://pagure.io/SSSD/sssd/issue/3369.
-
-Related:
-https://pagure.io/SSSD/sssd/issue/3369
-
-Signed-off-by: Fabiano Fidêncio <fidencio@redhat.com>
-
-Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
-(cherry picked from commit 01c6bb9b47401f9f14c4cfe5c5f03fce2e63629b)
----
- src/db/sysdb_ops.c    | 4 ++--
- src/db/sysdb_search.c | 8 ++++++++
- 2 files changed, 10 insertions(+), 2 deletions(-)
-
-diff --git a/src/db/sysdb_ops.c b/src/db/sysdb_ops.c
-index 4d7b2abd8026c90aaf4e7be687102e459cf3690e..12f8095d2edc60ffab09c92d64f968892c577bbf 100644
---- a/src/db/sysdb_ops.c
-+++ b/src/db/sysdb_ops.c
-@@ -3520,7 +3520,7 @@ int sysdb_search_ts_users(TALLOC_CTX *mem_ctx,
-     ZERO_STRUCT(*res);
- 
-     if (domain->sysdb->ldb_ts == NULL) {
--        return ENOENT;
-+        return ERR_NO_TS;
-     }
- 
-     ret = sysdb_cache_search_users(mem_ctx, domain, domain->sysdb->ldb_ts,
-@@ -3737,7 +3737,7 @@ int sysdb_search_ts_groups(TALLOC_CTX *mem_ctx,
-     ZERO_STRUCT(*res);
- 
-     if (domain->sysdb->ldb_ts == NULL) {
--        return ENOENT;
-+        return ERR_NO_TS;
-     }
- 
-     ret = sysdb_cache_search_groups(mem_ctx, domain, domain->sysdb->ldb_ts,
-diff --git a/src/db/sysdb_search.c b/src/db/sysdb_search.c
-index 474bc08f0b2fe3b0289cbea96fbf2619ced271e7..6b4b51383d89788052ab7e4b572e86abba5330db 100644
---- a/src/db/sysdb_search.c
-+++ b/src/db/sysdb_search.c
-@@ -587,6 +587,10 @@ int sysdb_enumpwent_filter(TALLOC_CTX *mem_ctx,
-     ret = sysdb_search_ts_users(tmp_ctx, domain, ts_filter,
-                                 sysdb_ts_cache_attrs,
-                                 &ts_res);
-+    if (ret == ERR_NO_TS) {
-+        ret = ENOENT;
-+    }
-+
-     if (ret != EOK && ret != ENOENT) {
-         goto done;
-     }
-@@ -1088,6 +1092,10 @@ int sysdb_enumgrent_filter(TALLOC_CTX *mem_ctx,
-     ret = sysdb_search_ts_groups(tmp_ctx, domain, ts_filter,
-                                  sysdb_ts_cache_attrs,
-                                  &ts_res);
-+    if (ret == ERR_NO_TS) {
-+        ret = ENOENT;
-+    }
-+
-     if (ret != EOK && ret != ENOENT) {
-         goto done;
-     }
--- 
-2.9.4
-
diff --git a/SOURCES/0176-SYSDB-Internally-expose-sysdb_search_ts_matches.patch b/SOURCES/0176-SYSDB-Internally-expose-sysdb_search_ts_matches.patch
deleted file mode 100644
index 38fdfa6..0000000
--- a/SOURCES/0176-SYSDB-Internally-expose-sysdb_search_ts_matches.patch
+++ /dev/null
@@ -1,90 +0,0 @@
-From 67e592572e655f19326cf821bbbe43411e8c7b06 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Fabiano=20Fid=C3=AAncio?= <fidencio@redhat.com>
-Date: Tue, 23 May 2017 22:44:24 +0200
-Subject: [PATCH 176/181] SYSDB: Internally expose sysdb_search_ts_matches()
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-This function will be used in the follow-up patches. As it's going to be
-"exposed", let's also rename it from search_ts_matches() to
-sysdb_search_ts_matches().
-
-Related:
-https://pagure.io/SSSD/sssd/issue/3369
-
-Signed-off-by: Fabiano Fidêncio <fidencio@redhat.com>
-
-Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
-(cherry picked from commit 347be58e1769ba90b49a7e5ec1678ef66987f6cd)
----
- src/db/sysdb_private.h |  7 +++++++
- src/db/sysdb_search.c  | 20 ++++++++++----------
- 2 files changed, 17 insertions(+), 10 deletions(-)
-
-diff --git a/src/db/sysdb_private.h b/src/db/sysdb_private.h
-index dfddd2dda9e593bd02d52dee7d06f520a11bbdf6..433220dcc0c35366dbbee41525e6c5932eb897f9 100644
---- a/src/db/sysdb_private.h
-+++ b/src/db/sysdb_private.h
-@@ -260,6 +260,13 @@ int sysdb_search_ts_groups(TALLOC_CTX *mem_ctx,
-                            const char **attrs,
-                            struct ldb_result *res);
- 
-+errno_t sysdb_search_ts_matches(TALLOC_CTX *mem_ctx,
-+                                struct sysdb_ctx *sysdb,
-+                                const char *attrs[],
-+                                struct ldb_result *ts_res,
-+                                const char *filter,
-+                                struct ldb_result **_res);
-+
- /* Compares the modifyTimestamp attribute between old_entry and
-  * new_entry. Returns true if they differ (or either entry is missing
-  * the attribute) and false if the attribute is the same
-diff --git a/src/db/sysdb_search.c b/src/db/sysdb_search.c
-index 6b4b51383d89788052ab7e4b572e86abba5330db..0c04b84a584e047a0ba8243c9216547ea2791e60 100644
---- a/src/db/sysdb_search.c
-+++ b/src/db/sysdb_search.c
-@@ -482,12 +482,12 @@ done:
-     return ret;
- }
- 
--static errno_t search_ts_matches(TALLOC_CTX *mem_ctx,
--                                 struct sysdb_ctx *sysdb,
--                                 const char *attrs[],
--                                 struct ldb_result *ts_res,
--                                 const char *filter,
--                                 struct ldb_result **_res)
-+errno_t sysdb_search_ts_matches(TALLOC_CTX *mem_ctx,
-+                                struct sysdb_ctx *sysdb,
-+                                const char *attrs[],
-+                                struct ldb_result *ts_res,
-+                                const char *filter,
-+                                struct ldb_result **_res)
- {
-     char *dn_filter;
-     TALLOC_CTX *tmp_ctx = NULL;
-@@ -595,8 +595,8 @@ int sysdb_enumpwent_filter(TALLOC_CTX *mem_ctx,
-         goto done;
-     }
- 
--    ret = search_ts_matches(tmp_ctx, domain->sysdb, attrs, &ts_res,
--                            name_filter, &ts_cache_res);
-+    ret = sysdb_search_ts_matches(tmp_ctx, domain->sysdb, attrs, &ts_res,
-+                                  name_filter, &ts_cache_res);
-     if (ret != EOK && ret != ENOENT) {
-         goto done;
-     }
-@@ -1100,8 +1100,8 @@ int sysdb_enumgrent_filter(TALLOC_CTX *mem_ctx,
-         goto done;
-     }
- 
--    ret = search_ts_matches(tmp_ctx, domain->sysdb, attrs, &ts_res,
--                            name_filter, &ts_cache_res);
-+    ret = sysdb_search_ts_matches(tmp_ctx, domain->sysdb, attrs, &ts_res,
-+                                  name_filter, &ts_cache_res);
-     if (ret != EOK && ret != ENOENT) {
-         goto done;
-     }
--- 
-2.9.4
-
diff --git a/SOURCES/0177-SYSDB-Make-the-usage-of-the-filter-more-generic-for-.patch b/SOURCES/0177-SYSDB-Make-the-usage-of-the-filter-more-generic-for-.patch
deleted file mode 100644
index 89eb87a..0000000
--- a/SOURCES/0177-SYSDB-Make-the-usage-of-the-filter-more-generic-for-.patch
+++ /dev/null
@@ -1,157 +0,0 @@
-From 7d926fb2e8fe21e3fa51bc341189d33658600daf Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Fabiano=20Fid=C3=AAncio?= <fidencio@redhat.com>
-Date: Wed, 24 May 2017 11:52:23 +0200
-Subject: [PATCH 177/181] SYSDB: Make the usage of the filter more generic for
- search_ts_matches()
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-In order to make this function re-usable in different parts of our code,
-let's start passing an already built filter to it instead of having the
-specific code building the name filter there.
-
-Related:
-https://pagure.io/SSSD/sssd/issue/3369
-
-Signed-off-by: Fabiano Fidêncio <fidencio@redhat.com>
-
-Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
-(cherry picked from commit 8ad57e17779b3ec60246ac58c1691ee15745084c)
----
- src/db/sysdb_search.c | 67 +++++++++++++++++++++++++++++++++++++++++----------
- 1 file changed, 54 insertions(+), 13 deletions(-)
-
-diff --git a/src/db/sysdb_search.c b/src/db/sysdb_search.c
-index 0c04b84a584e047a0ba8243c9216547ea2791e60..f488442afcc6eef114437a7110722759f86fe19e 100644
---- a/src/db/sysdb_search.c
-+++ b/src/db/sysdb_search.c
-@@ -489,7 +489,6 @@ errno_t sysdb_search_ts_matches(TALLOC_CTX *mem_ctx,
-                                 const char *filter,
-                                 struct ldb_result **_res)
- {
--    char *dn_filter;
-     TALLOC_CTX *tmp_ctx = NULL;
-     struct ldb_result *res;
-     errno_t ret;
-@@ -501,7 +500,7 @@ errno_t sysdb_search_ts_matches(TALLOC_CTX *mem_ctx,
-     }
- 
-     tmp_ctx = talloc_new(NULL);
--    if (!tmp_ctx) {
-+    if (tmp_ctx == NULL) {
-         return ENOMEM;
-     }
- 
-@@ -511,7 +510,43 @@ errno_t sysdb_search_ts_matches(TALLOC_CTX *mem_ctx,
-         goto done;
-     }
- 
--    dn_filter = talloc_asprintf(tmp_ctx, "(&(%s=%s)(|", SYSDB_NAME, filter);
-+    ret = ldb_search(sysdb->ldb, tmp_ctx, &res, NULL,
-+                     LDB_SCOPE_SUBTREE, attrs, "%s", filter);
-+    if (ret) {
-+        ret = sysdb_error_to_errno(ret);
-+        goto done;
-+    }
-+
-+    *_res = talloc_steal(mem_ctx, res);
-+    ret = EOK;
-+
-+done:
-+    talloc_zfree(tmp_ctx);
-+    return ret;
-+}
-+
-+static errno_t sysdb_enum_dn_filter(TALLOC_CTX *mem_ctx,
-+                                    struct ldb_result *ts_res,
-+                                    const char *name_filter,
-+                                    char **_dn_filter)
-+{
-+    TALLOC_CTX *tmp_ctx = NULL;
-+    char *dn_filter;
-+    errno_t ret;
-+
-+    if (ts_res->count == 0) {
-+        *_dn_filter = NULL;
-+        ret = EOK;
-+        goto done;
-+    }
-+
-+    tmp_ctx = talloc_new(NULL);
-+    if (tmp_ctx == NULL) {
-+        return ENOMEM;
-+    }
-+
-+    dn_filter = talloc_asprintf(tmp_ctx, "(&(%s=%s)(|", SYSDB_NAME,
-+                                name_filter);
-     if (dn_filter == NULL) {
-         ret = ENOMEM;
-         goto done;
-@@ -535,15 +570,9 @@ errno_t sysdb_search_ts_matches(TALLOC_CTX *mem_ctx,
-         goto done;
-     }
- 
--    ret = ldb_search(sysdb->ldb, tmp_ctx, &res, NULL,
--                     LDB_SCOPE_SUBTREE, attrs, "%s", dn_filter);
--    if (ret) {
--        ret = sysdb_error_to_errno(ret);
--        goto done;
--    }
--
-+    *_dn_filter = talloc_steal(mem_ctx, dn_filter);
-     ret = EOK;
--    *_res = talloc_steal(mem_ctx, res);
-+
- done:
-     talloc_zfree(tmp_ctx);
-     return ret;
-@@ -558,6 +587,7 @@ int sysdb_enumpwent_filter(TALLOC_CTX *mem_ctx,
-     TALLOC_CTX *tmp_ctx;
-     static const char *attrs[] = SYSDB_PW_ATTRS;
-     char *filter = NULL;
-+    char *dn_filter = NULL;
-     const char *ts_filter = NULL;
-     struct ldb_dn *base_dn;
-     struct ldb_result *res;
-@@ -595,8 +625,13 @@ int sysdb_enumpwent_filter(TALLOC_CTX *mem_ctx,
-         goto done;
-     }
- 
-+    ret = sysdb_enum_dn_filter(tmp_ctx, &ts_res, name_filter, &dn_filter);
-+    if (ret != EOK) {
-+        goto done;
-+    }
-+
-     ret = sysdb_search_ts_matches(tmp_ctx, domain->sysdb, attrs, &ts_res,
--                                  name_filter, &ts_cache_res);
-+                                  dn_filter, &ts_cache_res);
-     if (ret != EOK && ret != ENOENT) {
-         goto done;
-     }
-@@ -1052,6 +1087,7 @@ int sysdb_enumgrent_filter(TALLOC_CTX *mem_ctx,
-     const char *filter = NULL;
-     const char *ts_filter = NULL;
-     const char *base_filter;
-+    char *dn_filter = NULL;
-     struct ldb_dn *base_dn;
-     struct ldb_result *res;
-     struct ldb_result ts_res;
-@@ -1100,8 +1136,13 @@ int sysdb_enumgrent_filter(TALLOC_CTX *mem_ctx,
-         goto done;
-     }
- 
-+    ret = sysdb_enum_dn_filter(tmp_ctx, &ts_res, name_filter, &dn_filter);
-+    if (ret != EOK) {
-+        goto done;
-+    }
-+
-     ret = sysdb_search_ts_matches(tmp_ctx, domain->sysdb, attrs, &ts_res,
--                                  name_filter, &ts_cache_res);
-+                                  dn_filter, &ts_cache_res);
-     if (ret != EOK && ret != ENOENT) {
-         goto done;
-     }
--- 
-2.9.4
-
diff --git a/SOURCES/0178-SYSDB_OPS-Mark-an-entry-as-expired-also-in-the-times.patch b/SOURCES/0178-SYSDB_OPS-Mark-an-entry-as-expired-also-in-the-times.patch
deleted file mode 100644
index b4cbbf8..0000000
--- a/SOURCES/0178-SYSDB_OPS-Mark-an-entry-as-expired-also-in-the-times.patch
+++ /dev/null
@@ -1,55 +0,0 @@
-From 891e9c7cb924830334a42864ef2582e545f42723 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Fabiano=20Fid=C3=AAncio?= <fidencio@redhat.com>
-Date: Mon, 29 May 2017 13:32:59 +0200
-Subject: [PATCH 178/181] SYSDB_OPS: Mark an entry as expired also in the
- timestamp cache
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-As the cleanup task will start using new methods for searching the users
-and groups which have to be cleaned up, SSSD starts relying more in a
-more consistent state of the timestamp cache on pretty much everything
-related to the cleanup task.
-
-One of the things that would cause SSSD some problems is not having the
-ghost user expired in the persistent cache but not in the timestamp
-cache.
-
-With this patch, the entry is also expired in the timestamp cache when
-it's present.
-
-Related:
-https://pagure.io/SSSD/sssd/issue/3369
-
-Signed-off-by: Fabiano Fidêncio <fidencio@redhat.com>
-
-Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
-(cherry picked from commit 9883d1e2913ff0c1db479f1ece8148e03155c7f3)
----
- src/db/sysdb_ops.c | 9 +++++++++
- 1 file changed, 9 insertions(+)
-
-diff --git a/src/db/sysdb_ops.c b/src/db/sysdb_ops.c
-index 12f8095d2edc60ffab09c92d64f968892c577bbf..ae26470487f859fe1de1dc364b6a05b9793a0545 100644
---- a/src/db/sysdb_ops.c
-+++ b/src/db/sysdb_ops.c
-@@ -5065,6 +5065,15 @@ errno_t sysdb_mark_entry_as_expired_ldb_dn(struct sss_domain_info *dom,
-         goto done;
-     }
- 
-+    if (dom->sysdb->ldb_ts != NULL) {
-+        ret = ldb_modify(dom->sysdb->ldb_ts, msg);
-+        if (ret != LDB_SUCCESS) {
-+            DEBUG(SSSDBG_MINOR_FAILURE,
-+                  "Could not mark an entry as expired in the timestamp cache\n");
-+            /* non-fatal */
-+        }
-+    }
-+
-     ret = EOK;
- 
- done:
--- 
-2.9.4
-
diff --git a/SOURCES/0179-SYSDB_OPS-Invalidate-a-cache-entry-also-in-the-ts_ca.patch b/SOURCES/0179-SYSDB_OPS-Invalidate-a-cache-entry-also-in-the-ts_ca.patch
deleted file mode 100644
index e6512c2..0000000
--- a/SOURCES/0179-SYSDB_OPS-Invalidate-a-cache-entry-also-in-the-ts_ca.patch
+++ /dev/null
@@ -1,49 +0,0 @@
-From 256e1b4162832570e10a85579d2b14ed7b54b7f2 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Fabiano=20Fid=C3=AAncio?= <fidencio@redhat.com>
-Date: Mon, 29 May 2017 13:29:26 +0200
-Subject: [PATCH 179/181] SYSDB_OPS: Invalidate a cache entry also in the
- ts_cache
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Similarly to what has been in the previous commit (expiring an entry
-also in the timestamp cache), we should do the same when invalidating an
-entry.
-
-Related:
-https://pagure.io/SSSD/sssd/issue/3369
-
-Signed-off-by: Fabiano Fidêncio <fidencio@redhat.com>
-
-Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
-(cherry picked from commit a71f1a655dcc2ca6dc16bb8eb1c4c9e24cfe2c3e)
----
- src/db/sysdb_ops.c | 11 +++++++++++
- 1 file changed, 11 insertions(+)
-
-diff --git a/src/db/sysdb_ops.c b/src/db/sysdb_ops.c
-index ae26470487f859fe1de1dc364b6a05b9793a0545..ed936f0cb1a37155aabef96db1d267eb03ec0ed9 100644
---- a/src/db/sysdb_ops.c
-+++ b/src/db/sysdb_ops.c
-@@ -5160,6 +5160,17 @@ int sysdb_invalidate_cache_entry(struct sss_domain_info *domain,
-         goto done;
-     }
- 
-+    if (sysdb->ldb_ts != NULL) {
-+        ret = sysdb_set_cache_entry_attr(sysdb->ldb_ts, entry_dn,
-+                                         attrs, SYSDB_MOD_REP);
-+        if (ret != EOK) {
-+            DEBUG(SSSDBG_MINOR_FAILURE,
-+                  "Cannot set attrs in the timestamp cache for %s, %d [%s]\n",
-+                  ldb_dn_get_linearized(entry_dn), ret, sss_strerror(ret));
-+            /* non-fatal */
-+        }
-+    }
-+
-     DEBUG(SSSDBG_FUNC_DATA,
-           "Cache entry [%s] has been invalidated.\n",
-           ldb_dn_get_linearized(entry_dn));
--- 
-2.9.4
-
diff --git a/SOURCES/0180-SYSDB-Introduce-_search_-users-groups-_by_timestamp.patch b/SOURCES/0180-SYSDB-Introduce-_search_-users-groups-_by_timestamp.patch
deleted file mode 100644
index 5bfa491..0000000
--- a/SOURCES/0180-SYSDB-Introduce-_search_-users-groups-_by_timestamp.patch
+++ /dev/null
@@ -1,278 +0,0 @@
-From 11c34233ac7385c6f2a65c5cc57dfefb1cae48cd Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Fabiano=20Fid=C3=AAncio?= <fidencio@redhat.com>
-Date: Wed, 7 Jun 2017 15:07:10 +0200
-Subject: [PATCH 180/181] SYSDB: Introduce
- _search_{users,groups}_by_timestamp()
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-These new two sysdb methods are going to be used, at least for now,
-uniquely and exclusively in the cleanup task.
-
-The reason for adding those is that during the cleanup task a timestamp
-search is done in the persistent cache, which doesn't have the updated
-timestamps, returning then a wrong result that ends up in having all the
-users being removed from the cache.
-
-The persistent cache doesn't have its entries' timestamps updated
-because those are kept updated in the timestamp cache, therefore these
-new two methods end up doing:
-- if the timestamp cache is present:
-  - search for the entries solely in the timestamp cache;
-  - get the needed attributes from these entries from the persistent
-    cache;
-- otherwise:
-  - search for the entries in the persistent cache;
-  - merge its results with timestamp cache's results;
-
-Related:
-https://pagure.io/SSSD/sssd/issue/3369
-
-Signed-off-by: Fabiano Fidêncio <fidencio@redhat.com>
-
-Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
-(cherry picked from commit 41708e1e500e7cada3d3e606aa2b8b9869a5c734)
----
- src/db/sysdb.h     |  14 +++++
- src/db/sysdb_ops.c | 178 +++++++++++++++++++++++++++++++++++++++++++++++++++++
- 2 files changed, 192 insertions(+)
-
-diff --git a/src/db/sysdb.h b/src/db/sysdb.h
-index 62c561be9452a284a8ddf8ebb45720265852c8b0..21d6cf4fc90a050e203e1609be5ee267a618dda9 100644
---- a/src/db/sysdb.h
-+++ b/src/db/sysdb.h
-@@ -1142,6 +1142,13 @@ int sysdb_search_users(TALLOC_CTX *mem_ctx,
-                        size_t *msgs_count,
-                        struct ldb_message ***msgs);
- 
-+int sysdb_search_users_by_timestamp(TALLOC_CTX *mem_ctx,
-+                                    struct sss_domain_info *domain,
-+                                    const char *sub_filter,
-+                                    const char **attrs,
-+                                    size_t *_msgs_count,
-+                                    struct ldb_message ***_msgs);
-+
- int sysdb_delete_user(struct sss_domain_info *domain,
-                       const char *name, uid_t uid);
- 
-@@ -1152,6 +1159,13 @@ int sysdb_search_groups(TALLOC_CTX *mem_ctx,
-                         size_t *msgs_count,
-                         struct ldb_message ***msgs);
- 
-+int sysdb_search_groups_by_timestamp(TALLOC_CTX *mem_ctx,
-+                                     struct sss_domain_info *domain,
-+                                     const char *sub_filter,
-+                                     const char **attrs,
-+                                     size_t *_msgs_count,
-+                                     struct ldb_message ***_msgs);
-+
- int sysdb_delete_group(struct sss_domain_info *domain,
-                        const char *name, gid_t gid);
- 
-diff --git a/src/db/sysdb_ops.c b/src/db/sysdb_ops.c
-index ed936f0cb1a37155aabef96db1d267eb03ec0ed9..7ca6575ce75dab7805236c9f48dbf28a2f3946d2 100644
---- a/src/db/sysdb_ops.c
-+++ b/src/db/sysdb_ops.c
-@@ -374,6 +374,58 @@ enum sysdb_obj_type {
-     SYSDB_GROUP
- };
- 
-+static errno_t cleanup_dn_filter(TALLOC_CTX *mem_ctx,
-+                                struct ldb_result *ts_res,
-+                                const char *object_class,
-+                                const char *filter,
-+                                char **_dn_filter)
-+{
-+    TALLOC_CTX *tmp_ctx;
-+    char *dn_filter;
-+    errno_t ret;
-+
-+    if (ts_res->count == 0) {
-+        *_dn_filter = NULL;
-+        return EOK;
-+    }
-+
-+    tmp_ctx = talloc_new(NULL);
-+    if (tmp_ctx == NULL) {
-+        return ENOMEM;
-+    }
-+
-+    dn_filter = talloc_asprintf(tmp_ctx, "(&(%s)%s(|", object_class, filter);
-+    if (dn_filter == NULL) {
-+        ret = ENOMEM;
-+        goto done;
-+    }
-+
-+    for (size_t i = 0; i < ts_res->count; i++) {
-+        dn_filter = talloc_asprintf_append(
-+                                    dn_filter,
-+                                    "(%s=%s)",
-+                                    SYSDB_DN,
-+                                    ldb_dn_get_linearized(ts_res->msgs[i]->dn));
-+        if (dn_filter == NULL) {
-+            ret = ENOMEM;
-+            goto done;
-+        }
-+    }
-+
-+    dn_filter = talloc_asprintf_append(dn_filter, "))");
-+    if (dn_filter == NULL) {
-+        ret = ENOMEM;
-+        goto done;
-+    }
-+
-+    *_dn_filter = talloc_steal(mem_ctx, dn_filter);
-+    ret = EOK;
-+
-+done:
-+    talloc_zfree(tmp_ctx);
-+    return ret;
-+}
-+
- static int sysdb_search_by_name(TALLOC_CTX *mem_ctx,
-                                 struct sss_domain_info *domain,
-                                 const char *name,
-@@ -3503,6 +3555,69 @@ int sysdb_search_users(TALLOC_CTX *mem_ctx,
-                                          attrs);
- }
- 
-+int sysdb_search_users_by_timestamp(TALLOC_CTX *mem_ctx,
-+                                    struct sss_domain_info *domain,
-+                                    const char *sub_filter,
-+                                    const char **attrs,
-+                                    size_t *_msgs_count,
-+                                    struct ldb_message ***_msgs)
-+{
-+    TALLOC_CTX *tmp_ctx;
-+    struct ldb_result *res;
-+    struct ldb_result ts_res;
-+    struct ldb_message **msgs;
-+    size_t msgs_count;
-+    char *dn_filter = NULL;
-+    errno_t ret;
-+
-+    tmp_ctx = talloc_new(NULL);
-+    if (tmp_ctx == NULL) {
-+        return ENOMEM;
-+    }
-+
-+    ret = sysdb_search_ts_users(tmp_ctx, domain, sub_filter, NULL, &ts_res);
-+    if (ret == ERR_NO_TS) {
-+        ret = sysdb_cache_search_users(tmp_ctx, domain, domain->sysdb->ldb,
-+                                       sub_filter, attrs, &msgs_count, &msgs);
-+        if (ret != EOK) {
-+            goto done;
-+        }
-+
-+       ret = sysdb_merge_msg_list_ts_attrs(domain->sysdb, msgs_count, msgs, attrs);
-+       if (ret != EOK) {
-+           goto done;
-+       }
-+
-+       goto immediately;
-+    } else if (ret != EOK) {
-+        goto done;
-+    }
-+
-+    ret = cleanup_dn_filter(tmp_ctx, &ts_res, SYSDB_UC, sub_filter, &dn_filter);
-+    if (ret != EOK) {
-+        goto done;
-+    }
-+
-+    ret = sysdb_search_ts_matches(tmp_ctx, domain->sysdb, attrs,
-+                                  &ts_res, dn_filter, &res);
-+    if (ret != EOK) {
-+        goto done;
-+    }
-+
-+    msgs_count = res->count;
-+    msgs = res->msgs;
-+
-+immediately:
-+    *_msgs_count = msgs_count;
-+    *_msgs = talloc_steal(mem_ctx, msgs);
-+
-+    ret = EOK;
-+
-+done:
-+    talloc_free(tmp_ctx);
-+    return ret;
-+}
-+
- int sysdb_search_ts_users(TALLOC_CTX *mem_ctx,
-                           struct sss_domain_info *domain,
-                           const char *sub_filter,
-@@ -3720,6 +3835,69 @@ int sysdb_search_groups(TALLOC_CTX *mem_ctx,
-                                          attrs);
- }
- 
-+int sysdb_search_groups_by_timestamp(TALLOC_CTX *mem_ctx,
-+                                     struct sss_domain_info *domain,
-+                                     const char *sub_filter,
-+                                     const char **attrs,
-+                                     size_t *_msgs_count,
-+                                     struct ldb_message ***_msgs)
-+{
-+    TALLOC_CTX *tmp_ctx;
-+    struct ldb_result *res;
-+    struct ldb_result ts_res;
-+    struct ldb_message **msgs;
-+    size_t msgs_count;
-+    char *dn_filter = NULL;
-+    errno_t ret;
-+
-+    tmp_ctx = talloc_new(NULL);
-+    if (tmp_ctx == NULL) {
-+        return ENOMEM;
-+    }
-+
-+    ret = sysdb_search_ts_groups(tmp_ctx, domain, sub_filter, NULL, &ts_res);
-+    if (ret == ERR_NO_TS) {
-+        ret = sysdb_cache_search_groups(tmp_ctx, domain, domain->sysdb->ldb,
-+                                        sub_filter, attrs, &msgs_count, &msgs);
-+        if (ret != EOK) {
-+            goto done;
-+        }
-+
-+       ret = sysdb_merge_msg_list_ts_attrs(domain->sysdb, msgs_count, msgs, attrs);
-+       if (ret != EOK) {
-+           goto done;
-+       }
-+
-+       goto immediately;
-+    } else if (ret != EOK) {
-+        goto done;
-+    }
-+
-+    ret = cleanup_dn_filter(tmp_ctx, &ts_res, SYSDB_GC, sub_filter, &dn_filter);
-+    if (ret != EOK) {
-+        goto done;
-+    }
-+
-+    ret = sysdb_search_ts_matches(tmp_ctx, domain->sysdb, attrs,
-+                                  &ts_res, dn_filter, &res);
-+    if (ret != EOK) {
-+        goto done;
-+    }
-+
-+    msgs_count = res->count;
-+    msgs = res->msgs;
-+
-+immediately:
-+    *_msgs_count = msgs_count;
-+    *_msgs = talloc_steal(mem_ctx, msgs);
-+
-+    ret = EOK;
-+
-+done:
-+    talloc_free(tmp_ctx);
-+    return ret;
-+}
-+
- int sysdb_search_ts_groups(TALLOC_CTX *mem_ctx,
-                            struct sss_domain_info *domain,
-                            const char *sub_filter,
--- 
-2.9.4
-
diff --git a/SOURCES/0181-LDAP_ID_CLEANUP-Use-sysdb_search_-_by_timestamp.patch b/SOURCES/0181-LDAP_ID_CLEANUP-Use-sysdb_search_-_by_timestamp.patch
deleted file mode 100644
index 7bba059..0000000
--- a/SOURCES/0181-LDAP_ID_CLEANUP-Use-sysdb_search_-_by_timestamp.patch
+++ /dev/null
@@ -1,48 +0,0 @@
-From b96c69f0ab0ecd55b734c167763c3bfe2357c448 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Fabiano=20Fid=C3=AAncio?= <fidencio@redhat.com>
-Date: Wed, 7 Jun 2017 15:17:15 +0200
-Subject: [PATCH 181/181] LDAP_ID_CLEANUP: Use sysdb_search_*_by_timestamp()
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Use the appropriate methods for searching users and groups bv timestamp.
-
-Resolves:
-https://pagure.io/SSSD/sssd/issue/3369
-
-Signed-off-by: Fabiano Fidêncio <fidencio@redhat.com>
-
-Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
-(cherry picked from commit 05e579691b51ac2f81ab0c828ff6fe57bd86a8b6)
----
- src/providers/ldap/ldap_id_cleanup.c | 6 ++++--
- 1 file changed, 4 insertions(+), 2 deletions(-)
-
-diff --git a/src/providers/ldap/ldap_id_cleanup.c b/src/providers/ldap/ldap_id_cleanup.c
-index cde2ad81873d46edd5e05c4a701ea1742a012bd0..c85ce45918cf938a95ff85c31bfe0541f9ddd052 100644
---- a/src/providers/ldap/ldap_id_cleanup.c
-+++ b/src/providers/ldap/ldap_id_cleanup.c
-@@ -219,7 +219,8 @@ static int cleanup_users(struct sdap_options *opts,
-         goto done;
-     }
- 
--    ret = sysdb_search_users(tmpctx, dom, subfilter, attrs, &count, &msgs);
-+    ret = sysdb_search_users_by_timestamp(tmpctx, dom, subfilter, attrs,
-+                                          &count, &msgs);
-     if (ret == ENOENT) {
-         count = 0;
-     } else if (ret != EOK) {
-@@ -394,7 +395,8 @@ static int cleanup_groups(TALLOC_CTX *memctx,
-         goto done;
-     }
- 
--    ret = sysdb_search_groups(tmpctx, domain, subfilter, attrs, &count, &msgs);
-+    ret = sysdb_search_groups_by_timestamp(tmpctx, domain, subfilter, attrs,
-+                                           &count, &msgs);
-     if (ret == ENOENT) {
-         count = 0;
-     } else if (ret != EOK) {
--- 
-2.9.4
-
diff --git a/SOURCES/0182-krb5-use-plain-principal-if-password-is-expired.patch b/SOURCES/0182-krb5-use-plain-principal-if-password-is-expired.patch
deleted file mode 100644
index 4802ef1..0000000
--- a/SOURCES/0182-krb5-use-plain-principal-if-password-is-expired.patch
+++ /dev/null
@@ -1,66 +0,0 @@
-From b7aa85ea053aa78fa23de98d6c48e155f0cc06bc Mon Sep 17 00:00:00 2001
-From: Sumit Bose <sbose@redhat.com>
-Date: Mon, 12 Jun 2017 14:42:47 +0200
-Subject: [PATCH 182/182] krb5: use plain principal if password is expired
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Similar as in https://pagure.io/SSSD/sssd/issue/3426 enterprise
-principals should be avoided while requesting a kadmin/changepw@REALM
-principal for a password change.
-
-Resolves https://pagure.io/SSSD/sssd/issue/3419
-
-Reviewed-by: Fabiano Fidêncio <fidencio@redhat.com>
-(cherry picked from commit 7e2ec7caa2d1c17e475fff78c5025496b8695509)
----
- src/providers/krb5/krb5_child.c | 11 ++++++++++-
- 1 file changed, 10 insertions(+), 1 deletion(-)
-
-diff --git a/src/providers/krb5/krb5_child.c b/src/providers/krb5/krb5_child.c
-index 3cd8bfba76a35acd2c885ee2aac4765a6c1cc03c..3a76b900444dea50ec0b783496e22d25aad797ab 100644
---- a/src/providers/krb5/krb5_child.c
-+++ b/src/providers/krb5/krb5_child.c
-@@ -64,6 +64,7 @@ struct cli_opts {
- struct krb5_req {
-     krb5_context ctx;
-     krb5_principal princ;
-+    krb5_principal princ_orig;
-     char* name;
-     krb5_creds *creds;
-     bool otp;
-@@ -1975,7 +1976,7 @@ static errno_t tgt_req_child(struct krb5_req *kr)
-     }
- 
-     set_changepw_options(kr->options);
--    kerr = krb5_get_init_creds_password(kr->ctx, kr->creds, kr->princ,
-+    kerr = krb5_get_init_creds_password(kr->ctx, kr->creds, kr->princ_orig,
-                                         password_or_responder(password),
-                                         sss_krb5_prompter, kr, 0,
-                                         SSSD_KRB5_CHANGEPW_PRINCIPAL,
-@@ -2303,6 +2304,8 @@ static int krb5_cleanup(struct krb5_req *kr)
-         sss_krb5_free_unparsed_name(kr->ctx, kr->name);
-     if (kr->princ != NULL)
-         krb5_free_principal(kr->ctx, kr->princ);
-+    if (kr->princ_orig != NULL)
-+        krb5_free_principal(kr->ctx, kr->princ_orig);
-     if (kr->ctx != NULL)
-         krb5_free_context(kr->ctx);
- 
-@@ -2847,6 +2850,12 @@ static int k5c_setup(struct krb5_req *kr, uint32_t offline)
-         return kerr;
-     }
- 
-+    kerr = krb5_parse_name(kr->ctx, kr->upn, &kr->princ_orig);
-+    if (kerr != 0) {
-+        KRB5_CHILD_DEBUG(SSSDBG_CRIT_FAILURE, kerr);
-+        return kerr;
-+    }
-+
-     kerr = krb5_unparse_name(kr->ctx, kr->princ, &kr->name);
-     if (kerr != 0) {
-         KRB5_CHILD_DEBUG(SSSDBG_CRIT_FAILURE, kerr);
--- 
-2.9.4
-
diff --git a/SOURCES/0183-RESPONDER-Use-fqnames-as-output-when-needed.patch b/SOURCES/0183-RESPONDER-Use-fqnames-as-output-when-needed.patch
deleted file mode 100644
index 1af69a1..0000000
--- a/SOURCES/0183-RESPONDER-Use-fqnames-as-output-when-needed.patch
+++ /dev/null
@@ -1,279 +0,0 @@
-From 48b30d5a62e6af3d1f2b28eac3a2d39efa4349f1 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Fabiano=20Fid=C3=AAncio?= <fidencio@redhat.com>
-Date: Mon, 19 Jun 2017 09:05:00 +0200
-Subject: [PATCH 183/186] RESPONDER: Use fqnames as output when needed
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-As some regressions have been caused by not handling properly naming
-conflicts when using shortnames, last explicitly use fully qualified
-names as output in the following situations:
-- domain resolution order is set;
-- a trusted domain has been using `use_fully_qualified_name = false`
-
-In both cases we want to ensure that even handling shortnames as input,
-the output will always be fully qualified.
-
-As part of this patch, our tests ended up being modified to reflect the
-changes done. In other words, the tests related to shortnames now return
-expect as return a fully qualified name for trusted domains.
-
-Resolves:
-https://pagure.io/SSSD/sssd/issue/3403
-
-Signed-off-by: Fabiano Fidêncio <fidencio@redhat.com>
-
-Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
-(cherry picked from commit 86526891366c4bc3e1ee861143b736d2670a6ba8)
----
- src/confdb/confdb.h                               |   1 +
- src/db/sysdb_subdomains.c                         |   7 ++
- src/responder/common/cache_req/cache_req_domain.c |  14 +++
- src/responder/common/cache_req/cache_req_domain.h |   8 ++
- src/tests/cmocka/test_nss_srv.c                   | 104 +++++++++-------------
- src/util/usertools.c                              |   2 +-
- 6 files changed, 72 insertions(+), 64 deletions(-)
-
-diff --git a/src/confdb/confdb.h b/src/confdb/confdb.h
-index 797353141edcccbf3341d161ca598c99492e54fe..32a422155abef428e8a75fc83a5fe14620c7028e 100644
---- a/src/confdb/confdb.h
-+++ b/src/confdb/confdb.h
-@@ -291,6 +291,7 @@ struct sss_domain_info {
-     bool enumerate;
-     char **sd_enumerate;
-     bool fqnames;
-+    bool output_fqnames;
-     bool mpg;
-     bool ignore_group_members;
-     uint32_t id_min;
-diff --git a/src/db/sysdb_subdomains.c b/src/db/sysdb_subdomains.c
-index e2a4f7bb1fcdf20b6b7e04efc7f396d1c3d08f0f..2789cc4949fb7be9ad272d7613ed18a64fa8a20a 100644
---- a/src/db/sysdb_subdomains.c
-+++ b/src/db/sysdb_subdomains.c
-@@ -129,6 +129,13 @@ struct sss_domain_info *new_subdomain(TALLOC_CTX *mem_ctx,
-     dom->mpg = mpg;
-     dom->state = DOM_ACTIVE;
- 
-+    /* use fully qualified names as output in order to avoid causing
-+     * conflicts with users who have the same name and either the
-+     * shortname user resolution is enabled or the trusted domain has
-+     * been explicitly set to use non-fully qualified names as input.
-+     */
-+    dom->output_fqnames = true;
-+
-     /* If the parent domain filters out group members, the subdomain should
-      * as well if configured */
-     inherit_option = string_in_list(CONFDB_DOMAIN_IGNORE_GROUP_MEMBERS,
-diff --git a/src/responder/common/cache_req/cache_req_domain.c b/src/responder/common/cache_req/cache_req_domain.c
-index 2c238c9966d322bb542fa2047313ee9e5144edee..b5f7f6c2ffabdbd92ee46b3020cee6ef7fec32d8 100644
---- a/src/responder/common/cache_req/cache_req_domain.c
-+++ b/src/responder/common/cache_req/cache_req_domain.c
-@@ -136,6 +136,12 @@ cache_req_domain_new_list_from_string_list(TALLOC_CTX *mem_ctx,
-                 cr_domain->fqnames =
-                     cache_req_domain_use_fqnames(dom, enforce_non_fqnames);
- 
-+                /* when using the domain resolution order, using shortnames as
-+                 * input is allowed by default. However, we really want to use
-+                 * the fully qualified name as output in order to avoid
-+                 * conflicts whith users who have the very same name. */
-+                cr_domain->domain->output_fqnames = true;
-+
-                 DLIST_ADD_END(cr_domains, cr_domain,
-                               struct cache_req_domain *);
-                 break;
-@@ -159,6 +165,14 @@ cache_req_domain_new_list_from_string_list(TALLOC_CTX *mem_ctx,
-         cr_domain->fqnames =
-             cache_req_domain_use_fqnames(dom, enforce_non_fqnames);
- 
-+        /* when using the domain resolution order, using shortnames as input
-+         * is allowed by default. However, we really want to use the fully
-+         * qualified name as output in order to avoid conflicts whith users
-+         * who have the very same name. */
-+        if (resolution_order != NULL) {
-+            cr_domain->domain->output_fqnames = true;
-+        }
-+
-         DLIST_ADD_END(cr_domains, cr_domain, struct cache_req_domain *);
-     }
- 
-diff --git a/src/responder/common/cache_req/cache_req_domain.h b/src/responder/common/cache_req/cache_req_domain.h
-index 5bcbb9b493caf05bf71aac5cf7633ded91f22e73..3780a5d8d88d76e100738d28d1dd0e697edf5eae 100644
---- a/src/responder/common/cache_req/cache_req_domain.h
-+++ b/src/responder/common/cache_req/cache_req_domain.h
-@@ -35,6 +35,14 @@ struct cache_req_domain *
- cache_req_domain_get_domain_by_name(struct cache_req_domain *domains,
-                                     const char *name);
- 
-+/*
-+ * This function may have a side effect of setting the output_fqnames' domain
-+ * property when it's called.
-+ *
-+ * It happens as the output_fqnames' domain property must only be set depending
-+ * on whether a domain resolution order is set or not, and the saner place to
-+ * set it to all domains is when flattening those (thus, in this function).
-+ */
- errno_t
- cache_req_domain_new_list_from_domain_resolution_order(
-                                         TALLOC_CTX *mem_ctx,
-diff --git a/src/tests/cmocka/test_nss_srv.c b/src/tests/cmocka/test_nss_srv.c
-index 03b5bcc302322551a32f5b8cfe4b7698947abbe7..ccedf96beaecfaa4232bbe456d5e5a8394098483 100644
---- a/src/tests/cmocka/test_nss_srv.c
-+++ b/src/tests/cmocka/test_nss_srv.c
-@@ -1648,29 +1648,23 @@ static int test_nss_getgrnam_members_check_subdom(uint32_t status,
-     tmp_ctx = talloc_new(nss_test_ctx);
-     assert_non_null(tmp_ctx);
- 
--    if (nss_test_ctx->subdom->fqnames) {
--        exp_members[0] = sss_tc_fqname(tmp_ctx,
--                                       nss_test_ctx->subdom->names,
--                                       nss_test_ctx->subdom,
--                                       submember1.pw_name);
--        assert_non_null(exp_members[0]);
-+    exp_members[0] = sss_tc_fqname(tmp_ctx,
-+                                   nss_test_ctx->subdom->names,
-+                                   nss_test_ctx->subdom,
-+                                   submember1.pw_name);
-+    assert_non_null(exp_members[0]);
- 
--        exp_members[1] = sss_tc_fqname(tmp_ctx,
--                                       nss_test_ctx->subdom->names,
--                                       nss_test_ctx->subdom,
--                                       submember2.pw_name);
--        assert_non_null(exp_members[1]);
-+    exp_members[1] = sss_tc_fqname(tmp_ctx,
-+                                   nss_test_ctx->subdom->names,
-+                                   nss_test_ctx->subdom,
-+                                   submember2.pw_name);
-+    assert_non_null(exp_members[1]);
- 
--        expected.gr_name = sss_tc_fqname(tmp_ctx,
--                                         nss_test_ctx->subdom->names,
--                                         nss_test_ctx->subdom,
--                                         testsubdomgroup.gr_name);
--        assert_non_null(expected.gr_name);
--    } else {
--        exp_members[0] = submember1.pw_name;
--        exp_members[1] = submember2.pw_name;
--        expected.gr_name = testsubdomgroup.gr_name;
--    }
-+    expected.gr_name = sss_tc_fqname(tmp_ctx,
-+                                     nss_test_ctx->subdom->names,
-+                                     nss_test_ctx->subdom,
-+                                     testsubdomgroup.gr_name);
-+    assert_non_null(expected.gr_name);
- 
-     assert_int_equal(status, EOK);
- 
-@@ -1744,15 +1738,11 @@ static int test_nss_getgrnam_check_mix_dom(uint32_t status,
-     tmp_ctx = talloc_new(nss_test_ctx);
-     assert_non_null(tmp_ctx);
- 
--    if (nss_test_ctx->subdom->fqnames) {
--        exp_members[0] = sss_tc_fqname(tmp_ctx,
--                                       nss_test_ctx->subdom->names,
--                                       nss_test_ctx->subdom,
--                                       submember1.pw_name);
--        assert_non_null(exp_members[0]);
--    } else {
--        exp_members[0] = submember1.pw_name;
--    }
-+    exp_members[0] = sss_tc_fqname(tmp_ctx,
-+                                   nss_test_ctx->subdom->names,
-+                                   nss_test_ctx->subdom,
-+                                   submember1.pw_name);
-+    assert_non_null(exp_members[0]);
-     exp_members[1] = testmember1.pw_name;
-     exp_members[2] = testmember2.pw_name;
- 
-@@ -1840,15 +1830,12 @@ static int test_nss_getgrnam_check_mix_dom_fqdn(uint32_t status,
-     tmp_ctx = talloc_new(nss_test_ctx);
-     assert_non_null(tmp_ctx);
- 
--    if (nss_test_ctx->subdom->fqnames) {
--        exp_members[0] = sss_tc_fqname(tmp_ctx,
--                                       nss_test_ctx->subdom->names,
--                                       nss_test_ctx->subdom,
--                                       submember1.pw_name);
--        assert_non_null(exp_members[0]);
--    } else {
--        exp_members[0] = submember1.pw_name;
--    }
-+    exp_members[0] = sss_tc_fqname(tmp_ctx,
-+                                   nss_test_ctx->subdom->names,
-+                                   nss_test_ctx->subdom,
-+                                   submember1.pw_name);
-+    assert_non_null(exp_members[0]);
-+
-     if (nss_test_ctx->tctx->dom->fqnames) {
-         exp_members[1] = sss_tc_fqname(tmp_ctx, nss_test_ctx->tctx->dom->names,
-                                        nss_test_ctx->tctx->dom, testmember1.pw_name);
-@@ -1961,37 +1948,28 @@ static int test_nss_getgrnam_check_mix_subdom(uint32_t status,
-     tmp_ctx = talloc_new(nss_test_ctx);
-     assert_non_null(tmp_ctx);
- 
--    if (nss_test_ctx->subdom->fqnames) {
--        exp_members[0] = sss_tc_fqname(tmp_ctx,
--                                       nss_test_ctx->subdom->names,
--                                       nss_test_ctx->subdom,
--                                       submember1.pw_name);
--        assert_non_null(exp_members[0]);
-+    exp_members[0] = sss_tc_fqname(tmp_ctx,
-+                                   nss_test_ctx->subdom->names,
-+                                   nss_test_ctx->subdom,
-+                                   submember1.pw_name);
-+    assert_non_null(exp_members[0]);
- 
--        exp_members[1] = sss_tc_fqname(tmp_ctx,
--                                       nss_test_ctx->subdom->names,
--                                       nss_test_ctx->subdom,
--                                       submember2.pw_name);
--        assert_non_null(exp_members[1]);
--    } else {
--        exp_members[0] = submember1.pw_name;
--        exp_members[1] = submember2.pw_name;
--    }
-+    exp_members[1] = sss_tc_fqname(tmp_ctx,
-+                                   nss_test_ctx->subdom->names,
-+                                   nss_test_ctx->subdom,
-+                                   submember2.pw_name);
-+    assert_non_null(exp_members[1]);
- 
-     /* Important: this member is from a non-qualified domain, so his name will
-      * not be qualified either
-      */
-     exp_members[2] = testmember1.pw_name;
- 
--    if (nss_test_ctx->subdom->fqnames) {
--        expected.gr_name = sss_tc_fqname(tmp_ctx,
--                                         nss_test_ctx->subdom->names,
--                                         nss_test_ctx->subdom,
--                                         testsubdomgroup.gr_name);
--        assert_non_null(expected.gr_name);
--    } else {
--        expected.gr_name = testsubdomgroup.gr_name;
--    }
-+    expected.gr_name = sss_tc_fqname(tmp_ctx,
-+                                     nss_test_ctx->subdom->names,
-+                                     nss_test_ctx->subdom,
-+                                     testsubdomgroup.gr_name);
-+    assert_non_null(expected.gr_name);
- 
-     assert_int_equal(status, EOK);
- 
-diff --git a/src/util/usertools.c b/src/util/usertools.c
-index 5dfe6d7765b8032c7447de75e10c6c2a1d4c49ec..83131da1cac25e60a5ec3fffa995a545673e53b9 100644
---- a/src/util/usertools.c
-+++ b/src/util/usertools.c
-@@ -867,7 +867,7 @@ int sss_output_fqname(TALLOC_CTX *mem_ctx,
-         goto done;
-     }
- 
--    if (domain->fqnames) {
-+    if (domain->output_fqnames || domain->fqnames) {
-         output_name = sss_tc_fqname(tmp_ctx, domain->names,
-                                     domain, output_name);
-         if (output_name == NULL) {
--- 
-2.9.4
-
diff --git a/SOURCES/0184-DOMAIN-Add-sss_domain_info_-get-set-_output_fqnames.patch b/SOURCES/0184-DOMAIN-Add-sss_domain_info_-get-set-_output_fqnames.patch
deleted file mode 100644
index e6bd53b..0000000
--- a/SOURCES/0184-DOMAIN-Add-sss_domain_info_-get-set-_output_fqnames.patch
+++ /dev/null
@@ -1,128 +0,0 @@
-From 3fc92dcfbd67f82d26d7db46026f1fa1b69e2c70 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Fabiano=20Fid=C3=AAncio?= <fidencio@redhat.com>
-Date: Tue, 20 Jun 2017 14:22:48 +0200
-Subject: [PATCH 184/186] DOMAIN: Add
- sss_domain_info_{get,set}_output_fqnames()
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Let's avoid setting a domain's property directly from cr_domain code.
-
-In order to do so, let's introduce a setter, which may help us in the
-future whenever we decide to make sss_domain_info an opaque structure.
-
-For completeness, a getter has also been introduced and used in the
-usertools code.
-
-Related:
-https://pagure.io/SSSD/sssd/issue/3403
-
-Signed-off-by: Fabiano Fidêncio <fidencio@redhat.com>
-
-Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
-(cherry picked from commit fa2fc8a2908619031292eaf375eb1a510b8b2eba)
----
- src/confdb/confdb.h                               |  5 ++++-
- src/responder/common/cache_req/cache_req_domain.c |  4 ++--
- src/util/domain_info_utils.c                      | 11 +++++++++++
- src/util/usertools.c                              |  2 +-
- src/util/util.h                                   |  5 +++++
- 5 files changed, 23 insertions(+), 4 deletions(-)
-
-diff --git a/src/confdb/confdb.h b/src/confdb/confdb.h
-index 32a422155abef428e8a75fc83a5fe14620c7028e..2ba1bc47ee11f699726cefaf7c3335d2a8afee49 100644
---- a/src/confdb/confdb.h
-+++ b/src/confdb/confdb.h
-@@ -291,7 +291,6 @@ struct sss_domain_info {
-     bool enumerate;
-     char **sd_enumerate;
-     bool fqnames;
--    bool output_fqnames;
-     bool mpg;
-     bool ignore_group_members;
-     uint32_t id_min;
-@@ -355,6 +354,10 @@ struct sss_domain_info {
- 
-     struct certmap_info **certmaps;
-     bool user_name_hint;
-+
-+    /* Do not use the _output_fqnames property directly in new code, but rather
-+     * use sss_domain_info_{get,set}_output_fqnames(). */
-+    bool output_fqnames;
- };
- 
- /**
-diff --git a/src/responder/common/cache_req/cache_req_domain.c b/src/responder/common/cache_req/cache_req_domain.c
-index b5f7f6c2ffabdbd92ee46b3020cee6ef7fec32d8..c2b5abb74f3bd3d5055f29a4523f29b05feb2014 100644
---- a/src/responder/common/cache_req/cache_req_domain.c
-+++ b/src/responder/common/cache_req/cache_req_domain.c
-@@ -140,7 +140,7 @@ cache_req_domain_new_list_from_string_list(TALLOC_CTX *mem_ctx,
-                  * input is allowed by default. However, we really want to use
-                  * the fully qualified name as output in order to avoid
-                  * conflicts whith users who have the very same name. */
--                cr_domain->domain->output_fqnames = true;
-+                sss_domain_info_set_output_fqnames(cr_domain->domain, true);
- 
-                 DLIST_ADD_END(cr_domains, cr_domain,
-                               struct cache_req_domain *);
-@@ -170,7 +170,7 @@ cache_req_domain_new_list_from_string_list(TALLOC_CTX *mem_ctx,
-          * qualified name as output in order to avoid conflicts whith users
-          * who have the very same name. */
-         if (resolution_order != NULL) {
--            cr_domain->domain->output_fqnames = true;
-+            sss_domain_info_set_output_fqnames(cr_domain->domain, true);
-         }
- 
-         DLIST_ADD_END(cr_domains, cr_domain, struct cache_req_domain *);
-diff --git a/src/util/domain_info_utils.c b/src/util/domain_info_utils.c
-index 541058a16d585155b3b51511740f7db45281e2fd..45c74f089d0fdeaf6b5b50d7e5058df1716ff777 100644
---- a/src/util/domain_info_utils.c
-+++ b/src/util/domain_info_utils.c
-@@ -904,3 +904,14 @@ const char *sss_domain_type_str(struct sss_domain_info *dom)
-     }
-     return "Unknown";
- }
-+
-+void sss_domain_info_set_output_fqnames(struct sss_domain_info *domain,
-+                                        bool output_fqnames)
-+{
-+    domain->output_fqnames = output_fqnames;
-+}
-+
-+bool sss_domain_info_get_output_fqnames(struct sss_domain_info *domain)
-+{
-+    return domain->output_fqnames;
-+}
-diff --git a/src/util/usertools.c b/src/util/usertools.c
-index 83131da1cac25e60a5ec3fffa995a545673e53b9..33f4f7811c843704fff32db3a9ac54b3438f9d37 100644
---- a/src/util/usertools.c
-+++ b/src/util/usertools.c
-@@ -867,7 +867,7 @@ int sss_output_fqname(TALLOC_CTX *mem_ctx,
-         goto done;
-     }
- 
--    if (domain->output_fqnames || domain->fqnames) {
-+    if (sss_domain_info_get_output_fqnames(domain) || domain->fqnames) {
-         output_name = sss_tc_fqname(tmp_ctx, domain->names,
-                                     domain, output_name);
-         if (output_name == NULL) {
-diff --git a/src/util/util.h b/src/util/util.h
-index 5ba4c36ca88e325c20a3b1ecc8080a11ca276dcf..72d4116e1206e9cc69715edc45bf5b9b91e37e6b 100644
---- a/src/util/util.h
-+++ b/src/util/util.h
-@@ -571,6 +571,11 @@ errno_t sssd_domain_init(TALLOC_CTX *mem_ctx,
-                          const char *db_path,
-                          struct sss_domain_info **_domain);
- 
-+void sss_domain_info_set_output_fqnames(struct sss_domain_info *domain,
-+                                        bool output_fqname);
-+
-+bool sss_domain_info_get_output_fqnames(struct sss_domain_info *domain);
-+
- #define IS_SUBDOMAIN(dom) ((dom)->parent != NULL)
- 
- #define DOM_HAS_VIEWS(dom) ((dom)->has_views)
--- 
-2.9.4
-
diff --git a/SOURCES/0185-GPO-Fix-typo-in-DEBUG-message.patch b/SOURCES/0185-GPO-Fix-typo-in-DEBUG-message.patch
deleted file mode 100644
index fa94e3d..0000000
--- a/SOURCES/0185-GPO-Fix-typo-in-DEBUG-message.patch
+++ /dev/null
@@ -1,27 +0,0 @@
-From ab8afcc8befcfa436008da41944cf258513631e6 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Michal=20=C5=BDidek?= <mzidek@redhat.com>
-Date: Wed, 7 Jun 2017 14:37:42 +0200
-Subject: [PATCH 185/186] GPO: Fix typo in DEBUG message
-
-Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
-(cherry picked from commit b1d34059533eb50f6e5a4ac7b6fa1bb6fa60a445)
----
- src/providers/ad/ad_gpo.c | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
-diff --git a/src/providers/ad/ad_gpo.c b/src/providers/ad/ad_gpo.c
-index a8196b4d637eff86a01b342821592bffc214f1ab..2ee284bdc71fcec1c73997f785f7c2c7f387f0b3 100644
---- a/src/providers/ad/ad_gpo.c
-+++ b/src/providers/ad/ad_gpo.c
-@@ -2110,7 +2110,7 @@ ad_gpo_process_gpo_done(struct tevent_req *subreq)
-                                      &state->num_dacl_filtered_gpos);
-     if (ret != EOK) {
-         DEBUG(SSSDBG_OP_FAILURE,
--              "Unable to filter GPO list by DACKL: [%d](%s)\n",
-+              "Unable to filter GPO list by DACL: [%d](%s)\n",
-               ret, sss_strerror(ret));
-         goto done;
-     }
--- 
-2.9.4
-
diff --git a/SOURCES/0186-SDAP-Update-parent-sdap_list.patch b/SOURCES/0186-SDAP-Update-parent-sdap_list.patch
deleted file mode 100644
index 431f35a..0000000
--- a/SOURCES/0186-SDAP-Update-parent-sdap_list.patch
+++ /dev/null
@@ -1,97 +0,0 @@
-From 69b69d84ca9fd3453fa83281fc90e34f413a32f9 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Michal=20=C5=BDidek?= <mzidek@redhat.com>
-Date: Wed, 14 Jun 2017 19:02:10 +0200
-Subject: [PATCH 186/186] SDAP: Update parent sdap_list
-
-Update parent sdap_list with newly created subdomain sdap domain.
-
-Preiously, we inherited the parent sdap_list and used it also in the
-subdomain's context (this was introduced recently with commit
-c4ddb9ccab670f9c0d0377680237b62f9f91c496), but it caused problems
-that were difficult to debug (we somewhere rewrite part of the list
-incorrectly).
-
-This patch reverses to the previous bahavior, where every subdomain
-has it's own sdap_list, however this time the parrent domain's
-sdap_list is updated so that it has correct information about
-search bases of the child domains.
-
-We should ideally have just one sdap_list to avoid the updating
-completely, but this would require more refactoring in the sdap
-code.
-
-Resolves:
-https://pagure.io/SSSD/sssd/issue/3421
-
-Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
-(cherry picked from commit 630aea13063c4b242b3433d16ca4346a1a38429b)
----
- src/providers/ad/ad_subdomains.c | 38 +++++++++++++++++++++++++++++++++++---
- 1 file changed, 35 insertions(+), 3 deletions(-)
-
-diff --git a/src/providers/ad/ad_subdomains.c b/src/providers/ad/ad_subdomains.c
-index c9b79dd9d6840802cddc067eef9d5110cf8d0778..e35041c5ad73cb0fcaaaad96333fc17dd3a17638 100644
---- a/src/providers/ad/ad_subdomains.c
-+++ b/src/providers/ad/ad_subdomains.c
-@@ -141,6 +141,35 @@ static bool is_domain_enabled(const char *domain,
- }
- 
- static errno_t
-+update_parent_sdap_list(struct sdap_domain *parent_list,
-+                        struct sdap_domain *child_sdap)
-+{
-+    struct sdap_domain *sditer;
-+
-+    DLIST_FOR_EACH(sditer, parent_list) {
-+        if (sditer->dom == child_sdap->dom) {
-+            break;
-+        }
-+    }
-+
-+    if (sditer == NULL) {
-+        /* Nothing to do */
-+        return EOK;
-+    }
-+
-+    /* Update the search bases */
-+    sditer->search_bases = child_sdap->search_bases;
-+    sditer->user_search_bases = child_sdap->user_search_bases;
-+    sditer->group_search_bases = child_sdap->group_search_bases;
-+    sditer->netgroup_search_bases = child_sdap->netgroup_search_bases;
-+    sditer->sudo_search_bases = child_sdap->sudo_search_bases;
-+    sditer->service_search_bases = child_sdap->service_search_bases;
-+    sditer->autofs_search_bases = child_sdap->autofs_search_bases;
-+
-+    return EOK;
-+}
-+
-+static errno_t
- ad_subdom_ad_ctx_new(struct be_ctx *be_ctx,
-                      struct ad_id_ctx *id_ctx,
-                      struct sss_domain_info *subdom,
-@@ -221,9 +250,6 @@ ad_subdom_ad_ctx_new(struct be_ctx *be_ctx,
-     ad_id_ctx->sdap_id_ctx->opts = ad_options->id;
-     ad_options->id_ctx = ad_id_ctx;
- 
--    /* We need to pass the sdap list from parent */
--    ad_id_ctx->sdap_id_ctx->opts->sdom = id_ctx->sdap_id_ctx->opts->sdom;
--
-     /* use AD plugin */
-     srv_ctx = ad_srv_plugin_ctx_init(be_ctx, be_ctx->be_res,
-                                      default_host_dbs,
-@@ -267,6 +293,12 @@ ad_subdom_ad_ctx_new(struct be_ctx *be_ctx,
-               "bases.", subdom->name);
-     }
- 
-+    ret = update_parent_sdap_list(id_ctx->sdap_id_ctx->opts->sdom,
-+                                  sdom);
-+    if (ret != EOK) {
-+        return ret;
-+    }
-+
-     *_subdom_id_ctx = ad_id_ctx;
-     return EOK;
- }
--- 
-2.9.4
-
diff --git a/SOURCES/0187-RESPONDERS-Fix-terminating-idle-connections.patch b/SOURCES/0187-RESPONDERS-Fix-terminating-idle-connections.patch
deleted file mode 100644
index 724b886..0000000
--- a/SOURCES/0187-RESPONDERS-Fix-terminating-idle-connections.patch
+++ /dev/null
@@ -1,84 +0,0 @@
-From d6c7d35fdb4d416360a855a37b8c2164f053b470 Mon Sep 17 00:00:00 2001
-From: Jakub Hrozek <jhrozek@redhat.com>
-Date: Tue, 11 Jul 2017 18:26:01 +0200
-Subject: [PATCH 187/190] RESPONDERS: Fix terminating idle connections
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-The client_idle_handler() function tried to schedule another tevent
-timer to check for idle client connections in case the current
-connection was still valid, but in doing so, it also stored the current
-time into the last_request_time field of the client context.
-
-This kept the connection always alive, because the last_request_time
-could then never be older than the timeout.
-
-This patch changes the setup_client_idle_timer() function to only do
-what the synopsis says and set the idle timer. The caller (usually the
-function that accepts the connection) is supposed to store the request
-time itself.
-
-Resolves:
-https://pagure.io/SSSD/sssd/issue/3448
-
-Reviewed-by: Lukáš Slebodník <lslebodn@redhat.com>
-Reviewed-by: Fabiano Fidêncio <fidencio@redhat.com>
----
- src/responder/common/responder_common.c | 16 +++++++++++-----
- 1 file changed, 11 insertions(+), 5 deletions(-)
-
-diff --git a/src/responder/common/responder_common.c b/src/responder/common/responder_common.c
-index 9d4889be652c6d6fb974b59001a9ac77b496e9ab..9d706f9799ef1b31122d8380fbf9c53ba0cc9e68 100644
---- a/src/responder/common/responder_common.c
-+++ b/src/responder/common/responder_common.c
-@@ -607,7 +607,15 @@ static void accept_fd_handler(struct tevent_context *ev,
-     cctx->ev = ev;
-     cctx->rctx = rctx;
- 
--    /* Set up the idle timer */
-+    /* Record the new time and set up the idle timer */
-+    ret = reset_client_idle_timer(cctx);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_MINOR_FAILURE,
-+              "Could not create idle timer for client. "
-+              "This connection may not auto-terminate\n");
-+        /* Non-fatal, continue */
-+    }
-+
-     ret = setup_client_idle_timer(cctx);
-     if (ret != EOK) {
-         DEBUG(SSSDBG_CRIT_FAILURE,
-@@ -634,7 +642,7 @@ static void client_idle_handler(struct tevent_context *ev,
-     if (cctx->last_request_time > now) {
-         DEBUG(SSSDBG_IMPORTANT_INFO,
-               "Time shift detected, re-scheduling the client timeout\n");
--        goto end;
-+        goto done;
-     }
- 
-     if ((now - cctx->last_request_time) > cctx->rctx->client_idle_timeout) {
-@@ -648,7 +656,7 @@ static void client_idle_handler(struct tevent_context *ev,
-         return;
-     }
- 
--end:
-+done:
-     setup_client_idle_timer(cctx);
- }
- 
-@@ -661,11 +669,9 @@ errno_t reset_client_idle_timer(struct cli_ctx *cctx)
- 
- static errno_t setup_client_idle_timer(struct cli_ctx *cctx)
- {
--    time_t now = time(NULL);
-     struct timeval tv =
-             tevent_timeval_current_ofs(cctx->rctx->client_idle_timeout/2, 0);
- 
--    cctx->last_request_time = now;
-     talloc_zfree(cctx->idle);
- 
-     cctx->idle = tevent_add_timer(cctx->ev, cctx, tv, client_idle_handler, cctx);
--- 
-2.9.4
-
diff --git a/SOURCES/0188-TESTS-Integration-test-for-idle-timeout.patch b/SOURCES/0188-TESTS-Integration-test-for-idle-timeout.patch
deleted file mode 100644
index c587a0f..0000000
--- a/SOURCES/0188-TESTS-Integration-test-for-idle-timeout.patch
+++ /dev/null
@@ -1,135 +0,0 @@
-From fd008eddbf069014d8f17944d018ad3d85d5679f Mon Sep 17 00:00:00 2001
-From: Jakub Hrozek <jhrozek@redhat.com>
-Date: Wed, 19 Jul 2017 14:22:17 +0200
-Subject: [PATCH 188/190] TESTS: Integration test for idle timeout
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-The secrets responder test was chosen even though the bug was in the generic
-responder code b/c it runs a single responder process, so it's trivial to
-read the PID of the responder under test.
-
-Changes subprocess.call() for os.execv() so that the setup function can
-return the secret responder PID right away.
-
-The client timeout in the test has to be at least 10 seconds because
-internally, the responders don't allow a shorter timeout.
-
-Regression test for https://pagure.io/SSSD/sssd/issue/3448
-
-Reviewed-by: Lukáš Slebodník <lslebodn@redhat.com>
-Reviewed-by: Fabiano Fidêncio <fidencio@redhat.com>
----
- src/tests/intg/test_secrets.py | 75 ++++++++++++++++++++++++++++++++++--------
- 1 file changed, 62 insertions(+), 13 deletions(-)
-
-diff --git a/src/tests/intg/test_secrets.py b/src/tests/intg/test_secrets.py
-index 202f43e61cb0e4986394ad2b32da5abdcb0be3e9..1be31318b194de1550bc84af16260bf1503567dc 100644
---- a/src/tests/intg/test_secrets.py
-+++ b/src/tests/intg/test_secrets.py
-@@ -55,9 +55,9 @@ def create_sssd_secrets_fixture(request):
-     assert secpid >= 0
- 
-     if secpid == 0:
--        if subprocess.call([resp_path, "--uid=0", "--gid=0"]) != 0:
--            print("sssd_secrets failed to start")
--            sys.exit(99)
-+        os.execv(resp_path, ("--uid=0", "--gid=0"))
-+        print("sssd_secrets failed to start")
-+        sys.exit(99)
-     else:
-         sock_path = os.path.join(config.RUNSTATEDIR, "secrets.socket")
-         sck = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
-@@ -83,13 +83,8 @@ def create_sssd_secrets_fixture(request):
-     return secpid
- 
- 
--@pytest.fixture
--def setup_for_secrets(request):
--    """
--    Just set up the local provider for tests and enable the secrets
--    responder
--    """
--    conf = unindent("""\
-+def generate_sec_config():
-+    return unindent("""\
-         [sssd]
-         domains = local
-         services = nss
-@@ -100,11 +95,19 @@ def setup_for_secrets(request):
-         [secrets]
-         max_secrets = 10
-         max_payload_size = 2
--    """).format(**locals())
-+    """)
-+
-+
-+@pytest.fixture
-+def setup_for_secrets(request):
-+    """
-+    Just set up the local provider for tests and enable the secrets
-+    responder
-+    """
-+    conf = generate_sec_config()
- 
-     create_conf_fixture(request, conf)
--    create_sssd_secrets_fixture(request)
--    return None
-+    return create_sssd_secrets_fixture(request)
- 
- 
- def get_secrets_socket():
-@@ -386,3 +389,49 @@ def test_containers(setup_for_secrets, secrets_cli):
-     with pytest.raises(HTTPError) as err406:
-         cli.create_container(container)
-     assert str(err406.value).startswith("406")
-+
-+
-+def get_num_fds(pid):
-+    procpath = os.path.join("/proc/", str(pid), "fd")
-+    return len([fdname for fdname in os.listdir(procpath)])
-+
-+
-+@pytest.fixture
-+def setup_for_cli_timeout_test(request):
-+    """
-+    Same as the generic setup, except a short client_idle_timeout so that
-+    the test_idle_timeout() test closes the fd towards the client.
-+    """
-+    conf = generate_sec_config() + \
-+        unindent("""
-+        client_idle_timeout = 10
-+        """).format()
-+
-+    create_conf_fixture(request, conf)
-+    return create_sssd_secrets_fixture(request)
-+
-+
-+def test_idle_timeout(setup_for_cli_timeout_test):
-+    """
-+    Test that idle file descriptors are reaped after the idle timeout
-+    passes
-+    """
-+    secpid = setup_for_cli_timeout_test
-+    sock_path = get_secrets_socket()
-+
-+    nfds_pre = get_num_fds(secpid)
-+
-+    sock = socket.socket(family=socket.AF_UNIX)
-+    sock.connect(sock_path)
-+    time.sleep(1)
-+    nfds_conn = get_num_fds(secpid)
-+    assert nfds_pre + 1 == nfds_conn
-+    # With the idle timeout set to 10 seconds, we need to sleep at least 15,
-+    # because the internal timer ticks every timeout/2 seconds, so it would
-+    # tick at 5, 10 and 15 seconds and the client timeout check uses a
-+    # greater-than comparison, so the 10-seconds tick wouldn't yet trigger
-+    # disconnect
-+    time.sleep(15)
-+
-+    nfds_post = get_num_fds(secpid)
-+    assert nfds_pre == nfds_post
--- 
-2.9.4
-
diff --git a/SOURCES/0189-MAN-Document-that-client_idle_timeout-can-t-be-short.patch b/SOURCES/0189-MAN-Document-that-client_idle_timeout-can-t-be-short.patch
deleted file mode 100644
index e353c96..0000000
--- a/SOURCES/0189-MAN-Document-that-client_idle_timeout-can-t-be-short.patch
+++ /dev/null
@@ -1,36 +0,0 @@
-From 0442102b2e5c6f1bc331ca2078baff8a7b4a50cb Mon Sep 17 00:00:00 2001
-From: Jakub Hrozek <jhrozek@redhat.com>
-Date: Thu, 20 Jul 2017 10:10:58 +0200
-Subject: [PATCH 189/190] MAN: Document that client_idle_timeout can't be
- shorter than 10 seconds
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-To ensure the client timeout is not too low and clients do not reconnect
-too often, the client_idle_timeout is forced to be 10 seconds minimum.
-
-Reviewed-by: Lukáš Slebodník <lslebodn@redhat.com>
-Reviewed-by: Fabiano Fidêncio <fidencio@redhat.com>
----
- src/man/sssd.conf.5.xml | 4 +++-
- 1 file changed, 3 insertions(+), 1 deletion(-)
-
-diff --git a/src/man/sssd.conf.5.xml b/src/man/sssd.conf.5.xml
-index a35f2807eac8bb89d6cb1dd0a48f738d71a7578f..89729575c724622af817f1c05a94d4ae8f1ece2d 100644
---- a/src/man/sssd.conf.5.xml
-+++ b/src/man/sssd.conf.5.xml
-@@ -621,7 +621,9 @@
-                             a client of an SSSD process can hold onto a file
-                             descriptor without communicating on it. This value
-                             is limited in order to avoid resource exhaustion
--                            on the system.
-+                            on the system. The timeout can't be shorter than
-+                            10 seconds. If a lower value is configured, it
-+                            will be adjusted to 10 seconds.
-                         </para>
-                         <para>
-                             Default: 60
--- 
-2.9.4
-
diff --git a/SOURCES/0190-ad_account_can_shortcut-shortcut-if-ID-is-unknown.patch b/SOURCES/0190-ad_account_can_shortcut-shortcut-if-ID-is-unknown.patch
deleted file mode 100644
index c652a4d..0000000
--- a/SOURCES/0190-ad_account_can_shortcut-shortcut-if-ID-is-unknown.patch
+++ /dev/null
@@ -1,38 +0,0 @@
-From 55e8b436443cfae1c3b2155be7325d53760f7271 Mon Sep 17 00:00:00 2001
-From: Sumit Bose <sbose@redhat.com>
-Date: Thu, 20 Jul 2017 20:01:14 +0200
-Subject: [PATCH 190/190] ad_account_can_shortcut: shortcut if ID is unknown
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-If sss_idmap_unix_to_sid() returns an error we can assume that the given
-POSIX ID is not from the current domain and can be skipped. This is e.g.
-the case in the IPA provider if a POSIX ID used in the IPA domain is
-checked in a trusted id-mapped AD domain before the IPA domain is
-checked.
-
-Resolves https://pagure.io/SSSD/sssd/issue/3452
-
-Reviewed-by: Lukáš Slebodník <lslebodn@redhat.com>
-Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
----
- src/providers/ad/ad_id.c | 2 ++
- 1 file changed, 2 insertions(+)
-
-diff --git a/src/providers/ad/ad_id.c b/src/providers/ad/ad_id.c
-index d1f6c444f5ddbcbbac6ff7f41fb6c8bf9ca976cb..e14ada386f16851a65097952c85e57b7acda14aa 100644
---- a/src/providers/ad/ad_id.c
-+++ b/src/providers/ad/ad_id.c
-@@ -86,6 +86,8 @@ static bool ad_account_can_shortcut(struct sdap_idmap_ctx *idmap_ctx,
-         if (err != IDMAP_SUCCESS) {
-             DEBUG(SSSDBG_MINOR_FAILURE, "Mapping ID [%s] to SID failed: "
-                   "[%s]\n", filter_value, idmap_error_string(err));
-+            /* assume id is from a different domain */
-+            shortcut = true;
-             goto done;
-         }
-         /* fall through */
--- 
-2.9.4
-
diff --git a/SOURCES/0191-sudo-add-a-threshold-option-to-reduce-size-of-rules-.patch b/SOURCES/0191-sudo-add-a-threshold-option-to-reduce-size-of-rules-.patch
deleted file mode 100644
index 54279f3..0000000
--- a/SOURCES/0191-sudo-add-a-threshold-option-to-reduce-size-of-rules-.patch
+++ /dev/null
@@ -1,211 +0,0 @@
-From 5c159808818fcea77822815b5f1131809c0e673c Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Pavel=20B=C5=99ezina?= <pbrezina@redhat.com>
-Date: Tue, 11 Jul 2017 12:41:57 +0200
-Subject: [PATCH 191/191] sudo: add a threshold option to reduce size of rules
- refresh filter
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-If a large number of rules is expired at one time the ldap filter may
-become too large to be processed by server. This commits adds a new
-option "sudo_threshold" to sudo responder. If the threshold is
-exceeded a full refreshed is done instead of rules refresh.
-
-Resolves:
-https://pagure.io/SSSD/sssd/issue/3478
-
-Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
-Reviewed-by: Fabiano Fidêncio <fidencio@redhat.com>
-(cherry picked from commit a5f300adf19ec9c3087c62bd93a5175db799687a)
----
- src/confdb/confdb.h                        |  2 ++
- src/config/SSSDConfig/__init__.py.in       |  1 +
- src/config/cfg_rules.ini                   |  1 +
- src/config/etc/sssd.api.conf               |  1 +
- src/man/sssd.conf.5.xml                    | 19 +++++++++++++++++++
- src/responder/sudo/sudosrv.c               | 11 +++++++++++
- src/responder/sudo/sudosrv_get_sudorules.c | 25 ++++++++++++++++++++-----
- src/responder/sudo/sudosrv_private.h       |  1 +
- 8 files changed, 56 insertions(+), 5 deletions(-)
-
-diff --git a/src/confdb/confdb.h b/src/confdb/confdb.h
-index 2ba1bc47ee11f699726cefaf7c3335d2a8afee49..884b5bd1a493ca9a71654536524125eb8c7c4533 100644
---- a/src/confdb/confdb.h
-+++ b/src/confdb/confdb.h
-@@ -139,6 +139,8 @@
- #define CONFDB_DEFAULT_SUDO_TIMED false
- #define CONFDB_SUDO_INVERSE_ORDER "sudo_inverse_order"
- #define CONFDB_DEFAULT_SUDO_INVERSE_ORDER false
-+#define CONFDB_SUDO_THRESHOLD "sudo_threshold"
-+#define CONFDB_DEFAULT_SUDO_THRESHOLD 50
- 
- /* autofs */
- #define CONFDB_AUTOFS_CONF_ENTRY "config/autofs"
-diff --git a/src/config/SSSDConfig/__init__.py.in b/src/config/SSSDConfig/__init__.py.in
-index 75515ab5c68822538728900482296b9159e1547e..137a8fa4d526cb10f3136c62f3c7104d9ecb7599 100644
---- a/src/config/SSSDConfig/__init__.py.in
-+++ b/src/config/SSSDConfig/__init__.py.in
-@@ -107,6 +107,7 @@ option_strings = {
-     # [sudo]
-     'sudo_timed' : _('Whether to evaluate the time-based attributes in sudo rules'),
-     'sudo_inverse_order' : _('If true, SSSD will switch back to lower-wins ordering logic'),
-+    'sudo_threshold' : _('Maximum number of rules that can be refreshed at once. If this is exceeded, full refresh is performed.'),
- 
-     # [autofs]
-     'autofs_negative_timeout' : _('Negative cache timeout length (seconds)'),
-diff --git a/src/config/cfg_rules.ini b/src/config/cfg_rules.ini
-index d6506b7c3cee13f7c5400a546deb787e755abc8b..0bdcfdfbefd6cb24e0c01cb9746dbb98c63a31d2 100644
---- a/src/config/cfg_rules.ini
-+++ b/src/config/cfg_rules.ini
-@@ -144,6 +144,7 @@ option = cache_first
- # sudo service
- option = sudo_timed
- option = sudo_inverse_order
-+option = sudo_threshold
- 
- [rule/allowed_autofs_options]
- validator = ini_allowed_options
-diff --git a/src/config/etc/sssd.api.conf b/src/config/etc/sssd.api.conf
-index f86589ecefa0b9e046aba781ded107f8e94395d6..9d5eaaaa23c4c5395b155563de1cdf7752aa3dde 100644
---- a/src/config/etc/sssd.api.conf
-+++ b/src/config/etc/sssd.api.conf
-@@ -79,6 +79,7 @@ pam_app_services = str, None, false
- # sudo service
- sudo_timed = bool, None, false
- sudo_inverse_order = bool, None, false
-+sudo_threshold = int, None, false
- 
- [autofs]
- # autofs service
-diff --git a/src/man/sssd.conf.5.xml b/src/man/sssd.conf.5.xml
-index 89729575c724622af817f1c05a94d4ae8f1ece2d..d508df82d1d99af7835079c928839dc3cc7c28cb 100644
---- a/src/man/sssd.conf.5.xml
-+++ b/src/man/sssd.conf.5.xml
-@@ -1376,6 +1376,25 @@ pam_account_locked_message = Account locked, please contact help desk.
-                     </listitem>
-                 </varlistentry>
-             </variablelist>
-+            <variablelist>
-+                <varlistentry>
-+                    <term>sudo_threshold (integer)</term>
-+                    <listitem>
-+                        <para>
-+                            Maximum number of expired rules that can be
-+                            refreshed at once. If number of expired rules
-+                            is below threshold, those rules are refreshed
-+                            with <quote>rules refresh</quote> mechanism. If
-+                            the threshold is exceeded a
-+                            <quote>full refresh</quote> of sudo rules is
-+                            triggered instead.
-+                        </para>
-+                        <para>
-+                            Default: 50
-+                        </para>
-+                    </listitem>
-+                </varlistentry>
-+            </variablelist>
-         </refsect2>
- 
-         <refsect2 id='AUTOFS' condition="with_autofs">
-diff --git a/src/responder/sudo/sudosrv.c b/src/responder/sudo/sudosrv.c
-index b427878d4dbe9090824a01386a7475be88b699c0..dca70ea4afc0e6df6d1b1864338c7b1091a98fee 100644
---- a/src/responder/sudo/sudosrv.c
-+++ b/src/responder/sudo/sudosrv.c
-@@ -148,6 +148,17 @@ int sudo_process_init(TALLOC_CTX *mem_ctx,
-         goto fail;
-     }
- 
-+    /* Get sudo_inverse_order option */
-+    ret = confdb_get_int(sudo_ctx->rctx->cdb,
-+                         CONFDB_SUDO_CONF_ENTRY, CONFDB_SUDO_THRESHOLD,
-+                         CONFDB_DEFAULT_SUDO_THRESHOLD,
-+                         &sudo_ctx->threshold);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_FATAL_FAILURE, "Error reading from confdb (%d) [%s]\n",
-+              ret, strerror(ret));
-+        goto fail;
-+    }
-+
-     ret = schedule_get_domains_task(rctx, rctx->ev, rctx, NULL);
-     if (ret != EOK) {
-         DEBUG(SSSDBG_FATAL_FAILURE, "schedule_get_domains_tasks failed.\n");
-diff --git a/src/responder/sudo/sudosrv_get_sudorules.c b/src/responder/sudo/sudosrv_get_sudorules.c
-index cfdbfc9c9c66d96f774822d6a4d4aaaf1327abe3..3272e634d895acf4854309371779a00cf1525126 100644
---- a/src/responder/sudo/sudosrv_get_sudorules.c
-+++ b/src/responder/sudo/sudosrv_get_sudorules.c
-@@ -479,6 +479,7 @@ sudosrv_refresh_rules_send(TALLOC_CTX *mem_ctx,
-                            struct tevent_context *ev,
-                            struct resp_ctx *rctx,
-                            struct sss_domain_info *domain,
-+                           int threshold,
-                            uid_t uid,
-                            const char *username,
-                            char **groups)
-@@ -520,9 +521,20 @@ sudosrv_refresh_rules_send(TALLOC_CTX *mem_ctx,
-     DEBUG(SSSDBG_TRACE_INTERNAL, "Refreshing %d expired rules of [%s@%s]\n",
-           num_rules, username, domain->name);
- 
--    subreq = sss_dp_get_sudoers_send(state, rctx, domain, false,
--                                     SSS_DP_SUDO_REFRESH_RULES,
--                                     username, num_rules, rules);
-+    if (num_rules > threshold) {
-+        DEBUG(SSSDBG_TRACE_INTERNAL,
-+              "Rules threshold [%d] is reached, performing full refresh "
-+              "instead.\n", threshold);
-+
-+        subreq = sss_dp_get_sudoers_send(state, rctx, domain, false,
-+                                         SSS_DP_SUDO_FULL_REFRESH,
-+                                         username, 0, NULL);
-+    } else {
-+        subreq = sss_dp_get_sudoers_send(state, rctx, domain, false,
-+                                         SSS_DP_SUDO_REFRESH_RULES,
-+                                         username, num_rules, rules);
-+    }
-+
-     if (subreq == NULL) {
-         ret = ENOMEM;
-         goto immediately;
-@@ -609,6 +621,7 @@ struct sudosrv_get_rules_state {
-     struct sss_domain_info *domain;
-     char **groups;
-     bool inverse_order;
-+    int threshold;
- 
-     struct sysdb_attrs **rules;
-     uint32_t num_rules;
-@@ -640,6 +653,7 @@ struct tevent_req *sudosrv_get_rules_send(TALLOC_CTX *mem_ctx,
-     state->type = type;
-     state->uid = uid;
-     state->inverse_order = sudo_ctx->inverse_order;
-+    state->threshold = sudo_ctx->threshold;
- 
-     DEBUG(SSSDBG_TRACE_FUNC, "Running initgroups for [%s]\n", username);
- 
-@@ -696,8 +710,9 @@ static void sudosrv_get_rules_initgr_done(struct tevent_req *subreq)
-     }
- 
-     subreq = sudosrv_refresh_rules_send(state, state->ev, state->rctx,
--                                        state->domain, state->uid,
--                                        state->username, state->groups);
-+                                        state->domain, state->threshold,
-+                                        state->uid, state->username,
-+                                        state->groups);
-     if (subreq == NULL) {
-         ret = ENOMEM;
-         goto done;
-diff --git a/src/responder/sudo/sudosrv_private.h b/src/responder/sudo/sudosrv_private.h
-index 94f3c4458ab20e64db3e0bfce726d5d30a70a202..c76bdd3955bc29b7ba2cda58c503a4c616d7e63a 100644
---- a/src/responder/sudo/sudosrv_private.h
-+++ b/src/responder/sudo/sudosrv_private.h
-@@ -48,6 +48,7 @@ struct sudo_ctx {
-      */
-     bool timed;
-     bool inverse_order;
-+    int threshold;
- };
- 
- struct sudo_cmd_ctx {
--- 
-2.13.5
-
diff --git a/SOURCES/0192-libwbclient-Change-return-code-for-wbcAuthenticateUs.patch b/SOURCES/0192-libwbclient-Change-return-code-for-wbcAuthenticateUs.patch
deleted file mode 100644
index c33350b..0000000
--- a/SOURCES/0192-libwbclient-Change-return-code-for-wbcAuthenticateUs.patch
+++ /dev/null
@@ -1,57 +0,0 @@
-From b9a2edea74ea04a09301f91fffb6835df72d8760 Mon Sep 17 00:00:00 2001
-From: Lukas Slebodnik <lslebodn@redhat.com>
-Date: Tue, 22 Aug 2017 13:09:18 +0200
-Subject: [PATCH 192/192] libwbclient: Change return code for
- wbcAuthenticateUserEx
-
-Samba-4.6 change behaviour of few functions
-New version of code make sure session info for user is stored in cache.
-It is a performance optimisation to prevent contacting KDC for each
-session. More details in samba bug
-https://bugzilla.samba.org/show_bug.cgi?id=11259
-
-Old return code WBC_SSSD_NOT_IMPLEMENTED was translated
-to NT_STATUS_LOGON_FAILURE which caused many failures.
-
-    [2017/08/21 11:34:15.044321,  5, pid=27742, effective(0, 0), real(0, 0)]
-    ../libcli/security/security_token.c:53(security_token_debug)
-      Security token: (NULL)
-    [2017/08/21 11:34:15.044330,  5, pid=27742, effective(0, 0), real(0, 0)]
-    ../source3/auth/token_util.c:640(debug_unix_user_token)
-      UNIX token of user 0
-      Primary group is 0 and contains 0 supplementary groups
-    [2017/08/21 11:34:15.044349,  4, pid=27742, effective(0, 0), real(0, 0)]
-    ../source3/smbd/sec_ctx.c:439(pop_sec_ctx)
-      pop_sec_ctx (0, 0) - sec_ctx_stack_ndx = 0
-    [2017/08/21 11:34:15.044360,  1, pid=27742, effective(0, 0), real(0, 0)]
-    ../source3/smbd/sesssetup.c:290(reply_sesssetup_and_X_spnego)
-      Failed to generate session_info (user and group token) for session
-    setup: NT_STATUS_LOGON_FAILURE
-
-Resolves:
-https://pagure.io/SSSD/sssd/issue/3461
-
-Reviewed-by: Sumit Bose <sbose@redhat.com>
-(cherry picked from commit 725d04cd21016dc6092a9f03cd363bb83d7c054c)
-(cherry picked from commit aede6a1f4412f133e4b3fd76944f764d76fc4868)
-(cherry picked from commit 260062d946e7cc265e2671f88b1662276431c0bb)
----
- src/sss_client/libwbclient/wbc_pam_sssd.c | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
-diff --git a/src/sss_client/libwbclient/wbc_pam_sssd.c b/src/sss_client/libwbclient/wbc_pam_sssd.c
-index 174cf1310fad0243036fe591978cc89700903896..77698f523e6e7aeb37d4db50b469d1604d7ee595 100644
---- a/src/sss_client/libwbclient/wbc_pam_sssd.c
-+++ b/src/sss_client/libwbclient/wbc_pam_sssd.c
-@@ -49,7 +49,7 @@ wbcErr wbcAuthenticateUserEx(const struct wbcAuthUserParams *params,
-         *error = NULL;
-     }
- 
--    WBC_SSSD_NOT_IMPLEMENTED;
-+    return WBC_ERR_WINBIND_NOT_AVAILABLE;
- }
- 
- /* Trigger a verification of the trust credentials of a specific domain */
--- 
-2.9.4
-
diff --git a/SOURCES/0193-IPA-fix-handling-of-certmap_ctx.patch b/SOURCES/0193-IPA-fix-handling-of-certmap_ctx.patch
deleted file mode 100644
index 47b85fb..0000000
--- a/SOURCES/0193-IPA-fix-handling-of-certmap_ctx.patch
+++ /dev/null
@@ -1,666 +0,0 @@
-From 2fca2f1b77c0e9ae82e1a24bbf89fbc3115a5e24 Mon Sep 17 00:00:00 2001
-From: Sumit Bose <sbose@redhat.com>
-Date: Wed, 6 Sep 2017 16:42:20 +0200
-Subject: [PATCH 193/194] IPA: fix handling of certmap_ctx
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-This patch fixes a use-after-free in the AD provider part and
-initializes the certmap_ctx with data from the cache at startup.
-
-Related to https://pagure.io/SSSD/sssd/issue/3508
-
-Reviewed-by: Lukáš Slebodník <lslebodn@redhat.com>
-Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
-(cherry picked from commit f2e70ec742cd7aab82b74d7e4b424ba3258da7aa)
----
- Makefile.am                               |  20 +++
- src/providers/ipa/ipa_init.c              |   7 +
- src/providers/ipa/ipa_subdomains.c        |  53 +------
- src/providers/ipa/ipa_subdomains_server.c |   4 +-
- src/providers/ldap/ldap_common.h          |   5 +
- src/providers/ldap/ldap_id.c              |   5 +-
- src/providers/ldap/sdap.h                 |   4 +-
- src/providers/ldap/sdap_certmap.c         | 152 +++++++++++++++++++
- src/tests/cmocka/test_sdap_certmap.c      | 244 ++++++++++++++++++++++++++++++
- 9 files changed, 441 insertions(+), 53 deletions(-)
- create mode 100644 src/providers/ldap/sdap_certmap.c
- create mode 100644 src/tests/cmocka/test_sdap_certmap.c
-
-diff --git a/Makefile.am b/Makefile.am
-index 503c8cfd795b503f566431c08a56a56147180322..907c3256a154ebe2aae5a1667744e1dfbe8abaae 100644
---- a/Makefile.am
-+++ b/Makefile.am
-@@ -257,6 +257,7 @@ if HAVE_CMOCKA
-         test_search_bases \
-         test_ldap_auth \
-         test_sdap_access \
-+        test_sdap_certmap \
-         sdap-tests \
-         test_sysdb_ts_cache \
-         test_sysdb_views \
-@@ -2662,6 +2663,24 @@ test_sdap_access_LDADD = \
-     libdlopen_test_providers.la \
-     $(NULL)
- 
-+test_sdap_certmap_SOURCES = \
-+    src/tests/cmocka/test_sdap_certmap.c \
-+    src/providers/ldap/sdap_certmap.c \
-+    $(NULL)
-+test_sdap_certmap_CFLAGS = \
-+    $(AM_CFLAGS) \
-+    $(TALLOC_CFLAGS) \
-+    $(POPT_CFLAGS) \
-+    $(NULL)
-+test_sdap_certmap_LDADD = \
-+    $(CMOCKA_LIBS) \
-+    $(TALLOC_LIBS) \
-+    $(POPT_LIBS) \
-+    $(SSSD_INTERNAL_LTLIBS) \
-+    libsss_test_common.la \
-+    libsss_certmap.la \
-+    $(NULL)
-+
- ad_access_filter_tests_SOURCES = \
-     src/tests/cmocka/test_ad_access_filter.c
- ad_access_filter_tests_LDADD = \
-@@ -3706,6 +3725,7 @@ libsss_ldap_common_la_SOURCES = \
-     src/providers/ldap/sdap_child_helpers.c \
-     src/providers/ldap/sdap_fd_events.c \
-     src/providers/ldap/sdap_id_op.c \
-+    src/providers/ldap/sdap_certmap.c \
-     src/providers/ldap/sdap_idmap.c \
-     src/providers/ldap/sdap_idmap.h \
-     src/providers/ldap/sdap_range.c \
-diff --git a/src/providers/ipa/ipa_init.c b/src/providers/ipa/ipa_init.c
-index 7dec4d1fb8541a48470d4e44f10838e5bea67ad5..2b58b1341463f8947d51dee2076dbe92e3093558 100644
---- a/src/providers/ipa/ipa_init.c
-+++ b/src/providers/ipa/ipa_init.c
-@@ -649,6 +649,13 @@ static errno_t ipa_init_misc(struct be_ctx *be_ctx,
-         return ENOMEM;
-     }
- 
-+    ret = sdap_init_certmap(sdap_id_ctx, sdap_id_ctx);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_CRIT_FAILURE,
-+              "Failed to initialized certificate mapping.\n");
-+        return ret;
-+    }
-+
-     return EOK;
- }
- 
-diff --git a/src/providers/ipa/ipa_subdomains.c b/src/providers/ipa/ipa_subdomains.c
-index ef348adf4a36e870f44387bd700f5c2beea3bfd6..6f0ff50bde234f72d62f43635d9a787316c78430 100644
---- a/src/providers/ipa/ipa_subdomains.c
-+++ b/src/providers/ipa/ipa_subdomains.c
-@@ -311,25 +311,6 @@ struct priv_sss_debug {
-     int level;
- };
- 
--void ext_debug(void *private, const char *file, long line, const char *function,
--               const char *format, ...)
--{
--    va_list ap;
--    struct priv_sss_debug *data = private;
--    int level = SSSDBG_OP_FAILURE;
--
--    if (data != NULL) {
--        level = data->level;
--    }
--
--    if (DEBUG_IS_SET(level)) {
--        va_start(ap, format);
--        sss_vdebug_fn(file, line, function, level, APPEND_LINE_FEED,
--                      format, ap);
--        va_end(ap);
--    }
--}
--
- static errno_t ipa_certmap_parse_results(TALLOC_CTX *mem_ctx,
-                                          struct sss_domain_info *domain,
-                                          struct sdap_options *sdap_opts,
-@@ -344,7 +325,6 @@ static errno_t ipa_certmap_parse_results(TALLOC_CTX *mem_ctx,
-     size_t c;
-     size_t lc = 0;
-     int ret;
--    struct sss_certmap_ctx *certmap_ctx = NULL;
-     const char **ocs = NULL;
-     bool user_name_hint = false;
- 
-@@ -444,50 +424,29 @@ static errno_t ipa_certmap_parse_results(TALLOC_CTX *mem_ctx,
- 
-     certmap_list[lc] = NULL;
- 
--    ret = sss_certmap_init(mem_ctx, ext_debug, NULL, &certmap_ctx);
--    if (ret != 0) {
--        DEBUG(SSSDBG_OP_FAILURE, "sss_certmap_init failed.\n");
-+    ret = sdap_setup_certmap(sdap_opts->sdap_certmap_ctx, certmap_list);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_OP_FAILURE, "sdap_setup_certmap failed.\n");
-         goto done;
-     }
- 
--    for (c = 0; certmap_list[c] != NULL; c++) {
--        DEBUG(SSSDBG_TRACE_ALL, "Trying to add rule [%s][%d][%s][%s].\n",
--                                certmap_list[c]->name,
--                                certmap_list[c]->priority,
--                                certmap_list[c]->match_rule,
--                                certmap_list[c]->map_rule);
--
--        ret = sss_certmap_add_rule(certmap_ctx, certmap_list[c]->priority,
--                                   certmap_list[c]->match_rule,
--                                   certmap_list[c]->map_rule,
--                                   certmap_list[c]->domains);
--        if (ret != 0) {
--            DEBUG(SSSDBG_CRIT_FAILURE,
--                  "sss_certmap_add_rule failed for rule [%s], skipping. "
--                  "Please check for typos and if rule syntax is supported.\n",
--                  certmap_list[c]->name);
--            goto done;
--        }
--    }
--
-     ret = sysdb_update_certmap(domain->sysdb, certmap_list, user_name_hint);
-     if (ret != EOK) {
-         DEBUG(SSSDBG_OP_FAILURE, "sysdb_update_certmap failed");
-         goto done;
-     }
- 
--    sss_certmap_free_ctx(sdap_opts->certmap_ctx);
--    sdap_opts->certmap_ctx = talloc_steal(sdap_opts, certmap_ctx);
--
-     if (_certmap_list != NULL) {
-         *_certmap_list = certmap_list;
-+    } else {
-+        talloc_free(certmap_list);
-     }
-+
-     ret = EOK;
- 
- done:
-     talloc_free(ocs);
-     if (ret != EOK) {
--        sss_certmap_free_ctx(certmap_ctx);
-         talloc_free(certmap_list);
-     }
- 
-diff --git a/src/providers/ipa/ipa_subdomains_server.c b/src/providers/ipa/ipa_subdomains_server.c
-index 443d83824f329b9d8c3d8e820113e1029f832240..56470ac824feaa59eecbd9f442682220237c2412 100644
---- a/src/providers/ipa/ipa_subdomains_server.c
-+++ b/src/providers/ipa/ipa_subdomains_server.c
-@@ -361,8 +361,8 @@ ipa_ad_ctx_new(struct be_ctx *be_ctx,
-         id_ctx->sdap_id_ctx->opts->idmap_ctx;
- 
-     /* Set up the certificate mapping context */
--    ad_id_ctx->sdap_id_ctx->opts->certmap_ctx =
--        id_ctx->sdap_id_ctx->opts->certmap_ctx;
-+    ad_id_ctx->sdap_id_ctx->opts->sdap_certmap_ctx =
-+        id_ctx->sdap_id_ctx->opts->sdap_certmap_ctx;
- 
-     *_ad_id_ctx = ad_id_ctx;
-     return EOK;
-diff --git a/src/providers/ldap/ldap_common.h b/src/providers/ldap/ldap_common.h
-index 1acda4147576503b18f61e0bb56f8efd2263fd44..0510b7d5ab5121bd96f699e8e59520a2a18a604f 100644
---- a/src/providers/ldap/ldap_common.h
-+++ b/src/providers/ldap/ldap_common.h
-@@ -362,4 +362,9 @@ sdap_id_ctx_new(TALLOC_CTX *mem_ctx, struct be_ctx *bectx,
- errno_t sdap_refresh_init(struct be_refresh_ctx *refresh_ctx,
-                           struct sdap_id_ctx *id_ctx);
- 
-+errno_t sdap_init_certmap(TALLOC_CTX *mem_ctx, struct sdap_id_ctx *id_ctx);
-+
-+errno_t sdap_setup_certmap(struct sdap_certmap_ctx *sdap_certmap_ctx,
-+                           struct certmap_info **certmap_list);
-+struct sss_certmap_ctx *sdap_get_sss_certmap(struct sdap_certmap_ctx *ctx);
- #endif /* _LDAP_COMMON_H_ */
-diff --git a/src/providers/ldap/ldap_id.c b/src/providers/ldap/ldap_id.c
-index 557712e8dc2b2bde664b4054fa2f8eb39df84d73..93204d35ea3782c9aa5d622a962c295869472631 100644
---- a/src/providers/ldap/ldap_id.c
-+++ b/src/providers/ldap/ldap_id.c
-@@ -252,9 +252,8 @@ struct tevent_req *users_get_send(TALLOC_CTX *memctx,
-         }
- 
-         ret = sss_cert_derb64_to_ldap_filter(state, filter_value, attr_name,
--                                             ctx->opts->certmap_ctx,
--                                             state->domain,
--                                             &user_filter);
-+                              sdap_get_sss_certmap(ctx->opts->sdap_certmap_ctx),
-+                              state->domain, &user_filter);
-         if (ret != EOK) {
-             DEBUG(SSSDBG_OP_FAILURE,
-                   "sss_cert_derb64_to_ldap_filter failed.\n");
-diff --git a/src/providers/ldap/sdap.h b/src/providers/ldap/sdap.h
-index afdc01948eefe9dda943c8c7ad01a42dd76a1da8..c85fbe9e78e5eefa7e33ea8055730118b0871a4c 100644
---- a/src/providers/ldap/sdap.h
-+++ b/src/providers/ldap/sdap.h
-@@ -446,6 +446,8 @@ struct sdap_ext_member_ctx {
-     ext_member_recv_fn_t ext_member_resolve_recv;
- };
- 
-+struct sdap_certmap_ctx;
-+
- struct sdap_options {
-     struct dp_option *basic;
-     struct sdap_attr_map *gen_map;
-@@ -481,7 +483,7 @@ struct sdap_options {
-     enum dc_functional_level dc_functional_level;
- 
-     /* Certificate mapping support */
--    struct sss_certmap_ctx *certmap_ctx;
-+    struct sdap_certmap_ctx *sdap_certmap_ctx;
- };
- 
- struct sdap_server_opts {
-diff --git a/src/providers/ldap/sdap_certmap.c b/src/providers/ldap/sdap_certmap.c
-new file mode 100644
-index 0000000000000000000000000000000000000000..fcf88a9c69482c8668d486cd2ab0ba37c847e46d
---- /dev/null
-+++ b/src/providers/ldap/sdap_certmap.c
-@@ -0,0 +1,152 @@
-+
-+/*
-+    SSSD
-+
-+    Authors:
-+        Sumit Bose <sbose@redhat.com>
-+
-+    Copyright (C) 2017 Red Hat
-+
-+    This program is free software; you can redistribute it and/or modify
-+    it under the terms of the GNU General Public License as published by
-+    the Free Software Foundation; either version 3 of the License, or
-+    (at your option) any later version.
-+
-+    This program is distributed in the hope that it will be useful,
-+    but WITHOUT ANY WARRANTY; without even the implied warranty of
-+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+    GNU General Public License for more details.
-+
-+    You should have received a copy of the GNU General Public License
-+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
-+*/
-+
-+#include "util/util.h"
-+#include "lib/certmap/sss_certmap.h"
-+#include "providers/ldap/ldap_common.h"
-+
-+struct sdap_certmap_ctx {
-+    struct sss_certmap_ctx *certmap_ctx;
-+};
-+
-+struct priv_sss_debug {
-+    int level;
-+};
-+
-+static void ext_debug(void *private, const char *file, long line,
-+                      const char *function, const char *format, ...)
-+{
-+    va_list ap;
-+    struct priv_sss_debug *data = private;
-+    int level = SSSDBG_OP_FAILURE;
-+
-+    if (data != NULL) {
-+        level = data->level;
-+    }
-+
-+    if (DEBUG_IS_SET(level)) {
-+        va_start(ap, format);
-+        sss_vdebug_fn(file, line, function, level, APPEND_LINE_FEED,
-+                      format, ap);
-+        va_end(ap);
-+    }
-+}
-+
-+struct sss_certmap_ctx *sdap_get_sss_certmap(struct sdap_certmap_ctx *ctx)
-+{
-+    return ctx == NULL ? NULL : ctx->certmap_ctx;
-+}
-+
-+errno_t sdap_setup_certmap(struct sdap_certmap_ctx *sdap_certmap_ctx,
-+                           struct certmap_info **certmap_list)
-+{
-+    int ret;
-+    struct sss_certmap_ctx *sss_certmap_ctx = NULL;
-+    size_t c;
-+
-+    if (sdap_certmap_ctx == NULL) {
-+        DEBUG(SSSDBG_CRIT_FAILURE, "Missing sdap_certmap_ctx.\n");
-+        return EINVAL;
-+    }
-+
-+    if (certmap_list == NULL || *certmap_list == NULL) {
-+        DEBUG(SSSDBG_TRACE_ALL, "No certmap data, nothing to do.\n");
-+        ret = EOK;
-+        goto done;
-+    }
-+
-+    ret = sss_certmap_init(sdap_certmap_ctx, ext_debug, NULL, &sss_certmap_ctx);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_OP_FAILURE, "sss_certmap_init failed.\n");
-+        goto done;
-+    }
-+
-+    for (c = 0; certmap_list[c] != NULL; c++) {
-+        DEBUG(SSSDBG_TRACE_ALL, "Trying to add rule [%s][%d][%s][%s].\n",
-+                                certmap_list[c]->name,
-+                                certmap_list[c]->priority,
-+                                certmap_list[c]->match_rule,
-+                                certmap_list[c]->map_rule);
-+
-+        ret = sss_certmap_add_rule(sss_certmap_ctx, certmap_list[c]->priority,
-+                                   certmap_list[c]->match_rule,
-+                                   certmap_list[c]->map_rule,
-+                                   certmap_list[c]->domains);
-+        if (ret != 0) {
-+            DEBUG(SSSDBG_CRIT_FAILURE,
-+                  "sss_certmap_add_rule failed for rule [%s] "
-+                  "with error [%d][%s], skipping. "
-+                  "Please check for typos and if rule syntax is supported.\n",
-+                  certmap_list[c]->name, ret, sss_strerror(ret));
-+            continue;
-+        }
-+    }
-+
-+    ret = EOK;
-+
-+done:
-+    if (ret == EOK) {
-+        sss_certmap_free_ctx(sdap_certmap_ctx->certmap_ctx);
-+        sdap_certmap_ctx->certmap_ctx = sss_certmap_ctx;
-+    } else {
-+        sss_certmap_free_ctx(sss_certmap_ctx);
-+    }
-+
-+    return ret;
-+}
-+
-+errno_t sdap_init_certmap(TALLOC_CTX *mem_ctx, struct sdap_id_ctx *id_ctx)
-+{
-+    int ret;
-+    bool hint;
-+    struct certmap_info **certmap_list = NULL;
-+
-+    if (id_ctx->opts->sdap_certmap_ctx == NULL) {
-+        id_ctx->opts->sdap_certmap_ctx = talloc_zero(mem_ctx,
-+                                                     struct sdap_certmap_ctx);
-+        if (id_ctx->opts->sdap_certmap_ctx == NULL) {
-+            DEBUG(SSSDBG_OP_FAILURE, "talloc_zero failed.\n");
-+            return ENOMEM;
-+        }
-+    }
-+
-+    ret = sysdb_get_certmap(mem_ctx, id_ctx->be->domain->sysdb,
-+                            &certmap_list, &hint);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_OP_FAILURE, "sysdb_get_certmap failed.\n");
-+        goto done;
-+    }
-+
-+    ret = sdap_setup_certmap(id_ctx->opts->sdap_certmap_ctx, certmap_list);
-+    if (ret != EOK) {
-+            DEBUG(SSSDBG_OP_FAILURE, "sdap_setup_certmap failed.\n");
-+            goto done;
-+    }
-+
-+    ret = EOK;
-+
-+done:
-+    talloc_free(certmap_list);
-+
-+    return ret;
-+}
-diff --git a/src/tests/cmocka/test_sdap_certmap.c b/src/tests/cmocka/test_sdap_certmap.c
-new file mode 100644
-index 0000000000000000000000000000000000000000..9df5666844c8582a3fdb5b086720f1f2819f53f3
---- /dev/null
-+++ b/src/tests/cmocka/test_sdap_certmap.c
-@@ -0,0 +1,244 @@
-+/*
-+    Authors:
-+        Sumit Bose <sbose@redhat.com>
-+
-+    Copyright (C) 2017 Red Hat
-+
-+    SSSD tests - sdap certmap
-+
-+    This program is free software; you can redistribute it and/or modify
-+    it under the terms of the GNU General Public License as published by
-+    the Free Software Foundation; either version 3 of the License, or
-+    (at your option) any later version.
-+
-+    This program is distributed in the hope that it will be useful,
-+    but WITHOUT ANY WARRANTY; without even the implied warranty of
-+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+    GNU General Public License for more details.
-+
-+    You should have received a copy of the GNU General Public License
-+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
-+*/
-+
-+#include <stdarg.h>
-+#include <stdbool.h>
-+#include <setjmp.h>
-+#include <unistd.h>
-+#include <cmocka.h>
-+#include <popt.h>
-+
-+#include "providers/ldap/ldap_common.h"
-+#include "tests/common.h"
-+#include "db/sysdb.h"
-+
-+#define TESTS_PATH "certmap_" BASE_FILE_STEM
-+#define TEST_CONF_DB "test_sysdb_certmap.ldb"
-+#define TEST_ID_PROVIDER "ldap"
-+#define TEST_DOM_NAME "certmap_test"
-+
-+struct certmap_info map_a = { discard_const("map_a"), 11,
-+                              NULL, discard_const("(abc=def)"),
-+                              NULL };
-+struct certmap_info map_b = { discard_const("map_b"), UINT_MAX,
-+                              NULL, NULL, NULL };
-+struct certmap_info *certmap[] = { &map_a, &map_b, NULL };
-+
-+struct certmap_test_ctx {
-+    struct sss_test_ctx *tctx;
-+    struct sdap_id_ctx *id_ctx;
-+};
-+
-+static int test_sysdb_setup(void **state)
-+{
-+    int ret;
-+    struct certmap_test_ctx *test_ctx;
-+    struct sss_test_conf_param params[] = {
-+        { NULL, NULL },             /* Sentinel */
-+    };
-+
-+    assert_true(leak_check_setup());
-+
-+    test_ctx = talloc_zero(global_talloc_context,
-+                           struct certmap_test_ctx);
-+    assert_non_null(test_ctx);
-+    check_leaks_push(test_ctx);
-+
-+    test_dom_suite_setup(TESTS_PATH);
-+
-+    test_ctx->tctx = create_dom_test_ctx(test_ctx, TESTS_PATH,
-+                                         TEST_CONF_DB, TEST_DOM_NAME,
-+                                         TEST_ID_PROVIDER, params);
-+    assert_non_null(test_ctx->tctx);
-+
-+    ret = sysdb_update_certmap(test_ctx->tctx->sysdb, certmap, false);
-+    assert_int_equal(ret, EOK);
-+
-+    test_ctx->id_ctx = talloc_zero(test_ctx->tctx, struct sdap_id_ctx);
-+    assert_non_null(test_ctx->id_ctx);
-+
-+    test_ctx->id_ctx->opts = talloc_zero(test_ctx->tctx, struct sdap_options);
-+    assert_non_null(test_ctx->id_ctx->opts);
-+
-+    test_ctx->id_ctx->be = talloc_zero(test_ctx->tctx, struct be_ctx);
-+    assert_non_null(test_ctx->id_ctx->be);
-+    test_ctx->id_ctx->be->domain = test_ctx->tctx->dom;
-+
-+    *state = test_ctx;
-+    return 0;
-+}
-+
-+static int test_sysdb_teardown(void **state)
-+{
-+    struct certmap_test_ctx *test_ctx =
-+        talloc_get_type(*state, struct certmap_test_ctx);
-+
-+    test_dom_suite_cleanup(TESTS_PATH, TEST_CONF_DB, TEST_DOM_NAME);
-+    talloc_free(test_ctx->tctx);
-+    assert_true(check_leaks_pop(test_ctx));
-+    talloc_free(test_ctx);
-+    assert_true(leak_check_teardown());
-+    return 0;
-+}
-+
-+static void test_sdap_certmap_init(void **state)
-+{
-+    int ret;
-+    struct certmap_test_ctx *test_ctx = talloc_get_type(*state,
-+                                                       struct certmap_test_ctx);
-+
-+    ret = sdap_init_certmap(test_ctx, test_ctx->id_ctx);
-+    assert_int_equal(ret, EOK);
-+
-+    talloc_free(test_ctx->id_ctx->opts->sdap_certmap_ctx);
-+}
-+
-+static void test_sdap_get_sss_certmap(void **state)
-+{
-+    int ret;
-+    struct certmap_test_ctx *test_ctx = talloc_get_type(*state,
-+                                                       struct certmap_test_ctx);
-+    struct sss_certmap_ctx *sss_certmap_ctx;
-+
-+    sss_certmap_ctx = sdap_get_sss_certmap(NULL);
-+    assert_null(sss_certmap_ctx);
-+
-+    ret = sdap_init_certmap(test_ctx, test_ctx->id_ctx);
-+    assert_int_equal(ret, EOK);
-+
-+    sss_certmap_ctx = sdap_get_sss_certmap(
-+                                      test_ctx->id_ctx->opts->sdap_certmap_ctx);
-+    assert_non_null(sss_certmap_ctx);
-+
-+    talloc_free(test_ctx->id_ctx->opts->sdap_certmap_ctx);
-+}
-+
-+static void test_sdap_certmap_init_twice(void **state)
-+{
-+    int ret;
-+    struct certmap_test_ctx *test_ctx = talloc_get_type(*state,
-+                                                       struct certmap_test_ctx);
-+    struct sdap_certmap_ctx *sdap_certmap_ref;
-+    struct sss_certmap_ctx *sss_certmap_ref;
-+
-+    ret = sdap_init_certmap(test_ctx, test_ctx->id_ctx);
-+    assert_int_equal(ret, EOK);
-+
-+    sdap_certmap_ref = test_ctx->id_ctx->opts->sdap_certmap_ctx;
-+    sss_certmap_ref = sdap_get_sss_certmap(sdap_certmap_ref);
-+
-+    ret = sdap_init_certmap(test_ctx, test_ctx->id_ctx);
-+    assert_int_equal(ret, EOK);
-+
-+    assert_ptr_equal(sdap_certmap_ref,
-+                     test_ctx->id_ctx->opts->sdap_certmap_ctx);
-+    assert_ptr_not_equal(sss_certmap_ref,
-+                         sdap_get_sss_certmap(sdap_certmap_ref));
-+
-+    talloc_free(test_ctx->id_ctx->opts->sdap_certmap_ctx);
-+}
-+
-+
-+static void test_sdap_setup_certmap(void **state)
-+{
-+    int ret;
-+    struct certmap_test_ctx *test_ctx = talloc_get_type(*state,
-+                                                       struct certmap_test_ctx);
-+    struct sdap_certmap_ctx *sdap_certmap_ref;
-+    struct sss_certmap_ctx *sss_certmap_ref;
-+
-+    ret = sdap_init_certmap(test_ctx, test_ctx->id_ctx);
-+    assert_int_equal(ret, EOK);
-+
-+    sdap_certmap_ref = test_ctx->id_ctx->opts->sdap_certmap_ctx;
-+    sss_certmap_ref = sdap_get_sss_certmap(sdap_certmap_ref);
-+
-+    ret = sdap_setup_certmap(NULL, NULL);
-+    assert_int_equal(ret, EINVAL);
-+    assert_ptr_equal(sdap_certmap_ref,
-+                     test_ctx->id_ctx->opts->sdap_certmap_ctx);
-+    assert_ptr_equal(sss_certmap_ref, sdap_get_sss_certmap(sdap_certmap_ref));
-+
-+    ret = sdap_setup_certmap(NULL, certmap);
-+    assert_int_equal(ret, EINVAL);
-+    assert_ptr_equal(sdap_certmap_ref,
-+                     test_ctx->id_ctx->opts->sdap_certmap_ctx);
-+    assert_ptr_equal(sss_certmap_ref, sdap_get_sss_certmap(sdap_certmap_ref));
-+
-+    ret = sdap_setup_certmap(sdap_certmap_ref, certmap);
-+    assert_int_equal(ret, EOK);
-+    assert_ptr_equal(sdap_certmap_ref,
-+                     test_ctx->id_ctx->opts->sdap_certmap_ctx);
-+    assert_ptr_not_equal(sss_certmap_ref,
-+                         sdap_get_sss_certmap(sdap_certmap_ref));
-+
-+    talloc_free(test_ctx->id_ctx->opts->sdap_certmap_ctx);
-+}
-+
-+int main(int argc, const char *argv[])
-+{
-+    int rv;
-+    poptContext pc;
-+    int opt;
-+    struct poptOption long_options[] = {
-+        POPT_AUTOHELP
-+        SSSD_DEBUG_OPTS
-+        POPT_TABLEEND
-+    };
-+
-+    const struct CMUnitTest tests[] = {
-+        cmocka_unit_test_setup_teardown(test_sdap_certmap_init,
-+                                        test_sysdb_setup,
-+                                        test_sysdb_teardown),
-+        cmocka_unit_test_setup_teardown(test_sdap_get_sss_certmap,
-+                                        test_sysdb_setup,
-+                                        test_sysdb_teardown),
-+        cmocka_unit_test_setup_teardown(test_sdap_certmap_init_twice,
-+                                        test_sysdb_setup,
-+                                        test_sysdb_teardown),
-+        cmocka_unit_test_setup_teardown(test_sdap_setup_certmap,
-+                                        test_sysdb_setup,
-+                                        test_sysdb_teardown),
-+    };
-+
-+    /* Set debug level to invalid value so we can deside if -d 0 was used. */
-+    debug_level = SSSDBG_INVALID;
-+
-+    pc = poptGetContext(argv[0], argc, argv, long_options, 0);
-+    while((opt = poptGetNextOpt(pc)) != -1) {
-+        switch(opt) {
-+        default:
-+            fprintf(stderr, "\nInvalid option %s: %s\n\n",
-+                    poptBadOption(pc, 0), poptStrerror(opt));
-+            poptPrintUsage(pc, stderr, 0);
-+            return 1;
-+        }
-+    }
-+    poptFreeContext(pc);
-+
-+    DEBUG_CLI_INIT(debug_level);
-+
-+    tests_set_cwd();
-+    rv = cmocka_run_group_tests(tests, NULL, NULL);
-+
-+    return rv;
-+}
--- 
-2.13.5
-
diff --git a/SOURCES/0194-certmap-make-sure-eku_oid_list-is-always-allocated.patch b/SOURCES/0194-certmap-make-sure-eku_oid_list-is-always-allocated.patch
deleted file mode 100644
index c2f4d5d..0000000
--- a/SOURCES/0194-certmap-make-sure-eku_oid_list-is-always-allocated.patch
+++ /dev/null
@@ -1,95 +0,0 @@
-From 27ef368b4105f19382360fe62f944b36ca74adb7 Mon Sep 17 00:00:00 2001
-From: Sumit Bose <sbose@redhat.com>
-Date: Wed, 6 Sep 2017 12:20:25 +0200
-Subject: [PATCH 194/194] certmap: make sure eku_oid_list is always allocated
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-If there are only OIDs in a <EKU> part of a matching rule a NULL pointer
-dereference might occur.
-
-Related to https://pagure.io/SSSD/sssd/issue/3508
-
-Reviewed-by: Lukáš Slebodník <lslebodn@redhat.com>
-Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
-(cherry picked from commit f5a8cd60c6f377af1954b58f007d16cf3f6dc846)
----
- src/lib/certmap/sss_certmap_krb5_match.c | 21 ++++++++++++---------
- src/tests/cmocka/test_certmap.c          | 17 +++++++++++++++++
- 2 files changed, 29 insertions(+), 9 deletions(-)
-
-diff --git a/src/lib/certmap/sss_certmap_krb5_match.c b/src/lib/certmap/sss_certmap_krb5_match.c
-index e40f17b8ace46e61087e0a2fa570a362a84cead2..0a77ac225d73f3506e102fdbdc9084faa0f19cf0 100644
---- a/src/lib/certmap/sss_certmap_krb5_match.c
-+++ b/src/lib/certmap/sss_certmap_krb5_match.c
-@@ -179,19 +179,17 @@ static int parse_krb5_get_eku_value(TALLOC_CTX *mem_ctx,
-         goto done;
-     }
- 
-+    comp->eku_oid_list = talloc_zero_array(comp, const char *,
-+                                           eku_list_size + 1);
-+    if (comp->eku_oid_list == NULL) {
-+        ret = ENOMEM;
-+        goto done;
-+    }
-+
-     for (c = 0; eku_list[c] != NULL; c++) {
-         for (k = 0; ext_key_usage[k].name != NULL; k++) {
- CM_DEBUG(ctx, "[%s][%s].", eku_list[c], ext_key_usage[k].name);
-             if (strcasecmp(eku_list[c], ext_key_usage[k].name) == 0) {
--                if (comp->eku_oid_list == NULL) {
--                    comp->eku_oid_list = talloc_zero_array(comp, const char *,
--                                                           eku_list_size + 1);
--                    if (comp->eku_oid_list == NULL) {
--                        ret = ENOMEM;
--                        goto done;
--                    }
--                }
--
-                 comp->eku_oid_list[e] = talloc_strdup(comp->eku_oid_list,
-                                                       ext_key_usage[k].oid);
-                 if (comp->eku_oid_list[e] == NULL) {
-@@ -225,6 +223,11 @@ CM_DEBUG(ctx, "[%s][%s].", eku_list[c], ext_key_usage[k].name);
-         }
-     }
- 
-+    if (e == 0) {
-+        talloc_free(comp->eku_oid_list);
-+        comp->eku_oid_list = NULL;
-+    }
-+
-     ret = 0;
- 
- done:
-diff --git a/src/tests/cmocka/test_certmap.c b/src/tests/cmocka/test_certmap.c
-index c998443d086eaa72cc2a05c38ddfc5ba590a1ce7..e732bb214476943d0f723b318ab64d3b4156cace 100644
---- a/src/tests/cmocka/test_certmap.c
-+++ b/src/tests/cmocka/test_certmap.c
-@@ -445,6 +445,23 @@ static void test_sss_certmap_add_matching_rule(void **state)
-     assert_null(
-             ctx->prio_list->rule_list->parsed_match_rule->eku->eku_oid_list[3]);
- 
-+    ret = sss_certmap_add_rule(ctx, 96,
-+                               "KRB5:<EKU>1.2.3",
-+                               NULL, NULL);
-+    assert_int_equal(ret, 0);
-+    assert_non_null(ctx->prio_list);
-+    assert_non_null(ctx->prio_list->rule_list);
-+    assert_non_null(ctx->prio_list->rule_list->parsed_match_rule);
-+    assert_int_equal(ctx->prio_list->rule_list->parsed_match_rule->r,
-+                     relation_and);
-+    assert_non_null(ctx->prio_list->rule_list->parsed_match_rule->eku);
-+    assert_true(string_in_list("1.2.3",
-+              discard_const(
-+               ctx->prio_list->rule_list->parsed_match_rule->eku->eku_oid_list),
-+              true));
-+    assert_null(
-+            ctx->prio_list->rule_list->parsed_match_rule->eku->eku_oid_list[1]);
-+
-     /* SAN tests */
-     ret = sss_certmap_add_rule(ctx, 89, "KRB5:<SAN>abc", NULL, NULL);
-     assert_int_equal(ret, 0);
--- 
-2.13.5
-
diff --git a/SOURCES/0195-cache_req-Look-for-name-attribute-also-in-nss_cmd_ge.patch b/SOURCES/0195-cache_req-Look-for-name-attribute-also-in-nss_cmd_ge.patch
deleted file mode 100644
index 2841962..0000000
--- a/SOURCES/0195-cache_req-Look-for-name-attribute-also-in-nss_cmd_ge.patch
+++ /dev/null
@@ -1,62 +0,0 @@
-From cf75c30a42059480eca4c352598ceb3760c27e46 Mon Sep 17 00:00:00 2001
-From: Lukas Slebodnik <lslebodn@redhat.com>
-Date: Mon, 21 Aug 2017 11:42:43 +0200
-Subject: [PATCH 195/195] cache_req: Look for name attribute also in
- nss_cmd_getsidbyid
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-We always check negcache after getting data from backend since commit
-4c09cd008967c5c0ec358dc658ffc6fc1cef2697 because we usually do have a name
-in begging of requests "* by ID".
-
-We were not interested in name in request sid by id before. However, function
-cache_req_search_ncache_filter always expect name otherwise it returns
-ERR_INTERNAL.
-
-[sssd[nss]] [cache_req_set_plugin] (0x2000): CR #8: Setting "Object by ID" plugin
-[sssd[nss]] [cache_req_send] (0x0400): CR #8: New request 'Object by ID'
-[sssd[nss]] [cache_req_select_domains] (0x0400): CR #8: Performing a multi-domain search
-[sssd[nss]] [cache_req_search_domains] (0x0400): CR #8: Search will check the cache and check the data provider
-[sssd[nss]] [cache_req_validate_domain_type] (0x2000): Request type POSIX-only for domain sssdad2012r2.com type POSIX is valid
-[sssd[nss]] [cache_req_set_domain] (0x0400): CR #8: Using domain [sssdad2012r2.com]
-[sssd[nss]] [cache_req_search_send] (0x0400): CR #8: Looking up ID:233600513@sssdad2012r2.com
-[sssd[nss]] [cache_req_search_ncache] (0x0400): CR #8: Checking negative cache for [ID:233600513@sssdad2012r2.com]
-[sssd[nss]] [sss_ncache_check_str] (0x2000): Checking negative cache for [NCE/UID/sssdad2012r2.com/233600513]
-[sssd[nss]] [cache_req_search_ncache] (0x0400): CR #8: [ID:233600513@sssdad2012r2.com] is not present in negative cache
-[sssd[nss]] [cache_req_search_cache] (0x0400): CR #8: Looking up [ID:233600513@sssdad2012r2.com] in cache
-[sssd[nss]] [cache_req_search_send] (0x0400): CR #8: Returning [ID:233600513@sssdad2012r2.com] from cache
-[sssd[nss]] [cache_req_search_ncache_filter] (0x0400): CR #8: Filtering out results by negative cache
-[sssd[nss]] [cache_req_search_ncache_filter] (0x0020): CR #8: sss_get_name_from_msg() returned NULL, which should never happen in this scenario!
-[sssd[nss]] [cache_req_process_result] (0x0400): CR #8: Finished: Error 1432158209: Internal Error
-[sssd[nss]] [nss_protocol_done] (0x4000): Sending reply: error [1432158209]: Internal Error
-[sssd[nss]] [client_recv] (0x0200): Client disconnected!
-
-Resolves:
-https://pagure.io/SSSD/sssd/issue/3485
-
-Reviewed-by: Pavel Březina <pbrezina@redhat.com>
-(cherry picked from commit 2e72ababbbadda4c4036f99528460c1d595e0941)
----
- src/responder/common/cache_req/cache_req_data.c | 4 +++-
- 1 file changed, 3 insertions(+), 1 deletion(-)
-
-diff --git a/src/responder/common/cache_req/cache_req_data.c b/src/responder/common/cache_req/cache_req_data.c
-index 5ab1493b81dbcd1529f1124a2bb1f99d3ae82281..3c365e2fe5826fd58c75f07b08193e5566db2563 100644
---- a/src/responder/common/cache_req/cache_req_data.c
-+++ b/src/responder/common/cache_req/cache_req_data.c
-@@ -26,7 +26,9 @@ static const char **
- cache_req_data_create_attrs(TALLOC_CTX *mem_ctx,
-                             const char **requested)
- {
--    static const char *defattrs[] = { SYSDB_DEFAULT_ATTRS };
-+    static const char *defattrs[] = { SYSDB_DEFAULT_ATTRS, SYSDB_NAME,
-+                                      OVERRIDE_PREFIX SYSDB_NAME,
-+                                      SYSDB_DEFAULT_OVERRIDE_NAME };
-     static size_t defnum = sizeof(defattrs) / sizeof(defattrs[0]);
-     const char **attrs;
-     size_t reqnum;
--- 
-2.13.5
-
diff --git a/SOURCES/0196-sssd_client-add-mutex-protected-call-to-the-PAC-resp.patch b/SOURCES/0196-sssd_client-add-mutex-protected-call-to-the-PAC-resp.patch
deleted file mode 100644
index 2fe32d6..0000000
--- a/SOURCES/0196-sssd_client-add-mutex-protected-call-to-the-PAC-resp.patch
+++ /dev/null
@@ -1,461 +0,0 @@
-From daa59b79602cfeff81223a7461e18f513178c9d4 Mon Sep 17 00:00:00 2001
-From: Sumit Bose <sbose@redhat.com>
-Date: Mon, 18 Sep 2017 15:00:53 +0200
-Subject: [PATCH 196/196] sssd_client: add mutex protected call to the PAC
- responder
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-SSSD's plugin for MIT Kerberos to send the PAC to the PAC responder
-currently uses sss_pac_make_request() which does not protect the
-communication with the PAC responder with a mutex as e.g. the NSS and
-PAM clients.
-
-If an application using threads loads this plugin via libkrb5 in
-different threads and is heavily processing Kerberos tickets with PACs
-chances are that two threads try to communicate with SSSD at once. In
-this case one of the threads will miss a reply and will wait for it
-until the default client timeout of 300s is passed.
-
-This patch adds a call which uses a mutex to protect the communication
-which will avoid the 300s delay mentioned above.
-
-Resolves:
-https://pagure.io/SSSD/sssd/issue/3518
-
-Reviewed-by: Lukáš Slebodník <lslebodn@redhat.com>
-Reviewed-by: Fabiano Fidêncio <fidencio@redhat.com>
-(cherry picked from commit 1f331476e7d33bb03cc35a2a9064ee1cc5bed6cf)
----
- Makefile.am                               |  16 ++++
- src/sss_client/common.c                   |  30 +++++++
- src/sss_client/sss_cli.h                  |   7 ++
- src/sss_client/sss_pac_responder_client.c | 137 ++++++++++++++++++++++++++++++
- src/sss_client/sssd_pac.c                 |   4 +-
- src/tests/intg/Makefile.am                |   1 +
- src/tests/intg/test_pac_responder.py      | 120 ++++++++++++++++++++++++++
- 7 files changed, 313 insertions(+), 2 deletions(-)
- create mode 100644 src/sss_client/sss_pac_responder_client.c
- create mode 100644 src/tests/intg/test_pac_responder.py
-
-diff --git a/Makefile.am b/Makefile.am
-index 907c3256a154ebe2aae5a1667744e1dfbe8abaae..cdd517d50679b876814303fb7d6c63d49bcd8d38 100644
---- a/Makefile.am
-+++ b/Makefile.am
-@@ -3501,6 +3501,9 @@ endif
- if BUILD_WITH_LIBCURL
- noinst_PROGRAMS += tcurl-test-tool
- endif
-+if BUILD_PAC_RESPONDER
-+    noinst_PROGRAMS += sssd_pac_test_client
-+endif
- 
- if BUILD_AUTOFS
- autofs_test_client_SOURCES = \
-@@ -4210,6 +4213,19 @@ sssd_pac_plugin_la_LDFLAGS = \
-     -avoid-version \
-     -module
- 
-+sssd_pac_test_client_SOURCES = \
-+    src/sss_client/sss_pac_responder_client.c \
-+    src/sss_client/common.c \
-+    src/util/strtonum.c \
-+    $(NULL)
-+sssd_pac_test_client_CFLAGS = \
-+    $(AM_CFLAGS) \
-+    $(NULL)
-+sssd_pac_test_client_LDADD = \
-+    $(CLIENT_LIBS) \
-+    -lpthread \
-+    $(NULL)
-+
- # python[23] bindings
- pysss_la_SOURCES = \
-     $(SSSD_TOOLS_OBJ) \
-diff --git a/src/sss_client/common.c b/src/sss_client/common.c
-index b7a5ed760ca379acdfd8f1d2bf95cee1aa271fd8..b527c046e2e3369934b4f9ea7efc1b52eb8c57ea 100644
---- a/src/sss_client/common.c
-+++ b/src/sss_client/common.c
-@@ -821,6 +821,22 @@ int sss_pac_make_request(enum sss_cli_command cmd,
-     }
- }
- 
-+int sss_pac_make_request_with_lock(enum sss_cli_command cmd,
-+                                   struct sss_cli_req_data *rd,
-+                                   uint8_t **repbuf, size_t *replen,
-+                                   int *errnop)
-+{
-+    int ret;
-+
-+    sss_pac_lock();
-+
-+    ret = sss_pac_make_request(cmd, rd, repbuf, replen, errnop);
-+
-+    sss_pac_unlock();
-+
-+    return ret;
-+}
-+
- errno_t check_server_cred(int sockfd)
- {
- #ifdef HAVE_UCRED
-@@ -1079,6 +1095,8 @@ static struct sss_mutex sss_pam_mtx = { .mtx  = PTHREAD_MUTEX_INITIALIZER };
- 
- static struct sss_mutex sss_nss_mc_mtx = { .mtx  = PTHREAD_MUTEX_INITIALIZER };
- 
-+static struct sss_mutex sss_pac_mtx = { .mtx  = PTHREAD_MUTEX_INITIALIZER };
-+
- static void sss_mt_lock(struct sss_mutex *m)
- {
-     pthread_mutex_lock(&m->mtx);
-@@ -1121,6 +1139,16 @@ void sss_nss_mc_unlock(void)
-     sss_mt_unlock(&sss_nss_mc_mtx);
- }
- 
-+/* PAC mutex wrappers */
-+void sss_pac_lock(void)
-+{
-+    sss_mt_lock(&sss_pac_mtx);
-+}
-+void sss_pac_unlock(void)
-+{
-+    sss_mt_unlock(&sss_pac_mtx);
-+}
-+
- #else
- 
- /* sorry no mutexes available */
-@@ -1130,6 +1158,8 @@ void sss_pam_lock(void) { return; }
- void sss_pam_unlock(void) { return; }
- void sss_nss_mc_lock(void) { return; }
- void sss_nss_mc_unlock(void) { return; }
-+void sss_pac_lock(void) { return; }
-+void sss_pac_unlock(void) { return; }
- #endif
- 
- 
-diff --git a/src/sss_client/sss_cli.h b/src/sss_client/sss_cli.h
-index d4198407f2f86c6594aee6a2a43775e429692df0..337fe9803d2df3167cd2da77107dbd077f35a51b 100644
---- a/src/sss_client/sss_cli.h
-+++ b/src/sss_client/sss_cli.h
-@@ -585,6 +585,11 @@ int sss_pac_make_request(enum sss_cli_command cmd,
-                          uint8_t **repbuf, size_t *replen,
-                          int *errnop);
- 
-+int sss_pac_make_request_with_lock(enum sss_cli_command cmd,
-+                                   struct sss_cli_req_data *rd,
-+                                   uint8_t **repbuf, size_t *replen,
-+                                   int *errnop);
-+
- int sss_sudo_make_request(enum sss_cli_command cmd,
-                           struct sss_cli_req_data *rd,
-                           uint8_t **repbuf, size_t *replen,
-@@ -634,6 +639,8 @@ void sss_pam_lock(void);
- void sss_pam_unlock(void);
- void sss_nss_mc_lock(void);
- void sss_nss_mc_unlock(void);
-+void sss_pac_lock(void);
-+void sss_pac_unlock(void);
- 
- errno_t sss_readrep_copy_string(const char *in,
-                                 size_t *offset,
-diff --git a/src/sss_client/sss_pac_responder_client.c b/src/sss_client/sss_pac_responder_client.c
-new file mode 100644
-index 0000000000000000000000000000000000000000..9eb0cbea6175ee273b23d9a975529d85c02fc603
---- /dev/null
-+++ b/src/sss_client/sss_pac_responder_client.c
-@@ -0,0 +1,137 @@
-+
-+#include <stdio.h>
-+#include <stdbool.h>
-+#include <pthread.h>
-+#include <pwd.h>
-+#include <unistd.h>
-+#include <sys/types.h>
-+#include <errno.h>
-+
-+#include <unistd.h>
-+#include <sys/syscall.h>
-+
-+#include "sss_client/sss_cli.h"
-+
-+const uint8_t pac[] = {
-+0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10,
-+0x02, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00,
-+0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x68, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
-+0x00, 0x0c, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x78, 0x02, 0x00, 0x00,
-+0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0xb8,
-+0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x10, 0x00,
-+0x00, 0x00, 0xc8, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x10, 0x08,
-+0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-+0x00, 0x00, 0x02, 0x00, 0x30, 0xe3, 0xd6, 0x9e, 0x99, 0x2b, 0xd3, 0x01, 0xff,
-+0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
-+0xff, 0x7f, 0xe2, 0xf7, 0x8a, 0xaf, 0x00, 0x0f, 0xd0, 0x01, 0xe2, 0xb7, 0xf4,
-+0xd9, 0xc9, 0x0f, 0xd0, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f,
-+0x06, 0x00, 0x06, 0x00, 0x04, 0x00, 0x02, 0x00, 0x06, 0x00, 0x06, 0x00, 0x08,
-+0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x02, 0x00, 0x00, 0x00,
-+0x00, 0x00, 0x10, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0x00, 0x02,
-+0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x02, 0x00, 0x45, 0x02, 0x00, 0x00,
-+0x50, 0x04, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x1c,
-+0x00, 0x02, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0x14,
-+0x00, 0x20, 0x00, 0x02, 0x00, 0x04, 0x00, 0x06, 0x00, 0x24, 0x00, 0x02, 0x00,
-+0x28, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10,
-+0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-+0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x02, 0x00,
-+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
-+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x74, 0x00,
-+0x75, 0x00, 0x31, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-+0x00, 0x03, 0x00, 0x00, 0x00, 0x74, 0x00, 0x20, 0x00, 0x75, 0x00, 0x00, 0x00,
-+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,
-+0xfd, 0xa2, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00, 0x07,
-+0x00, 0x00, 0x00, 0x5c, 0x04, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x56, 0x04,
-+0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x89, 0xa6, 0x00, 0x00, 0x07, 0x00, 0x00,
-+0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00,
-+0x41, 0x00, 0x44, 0x00, 0x2d, 0x00, 0x53, 0x00, 0x45, 0x00, 0x52, 0x00, 0x56,
-+0x00, 0x45, 0x00, 0x52, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00,
-+0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x41, 0x00, 0x44, 0x00, 0x04, 0x00, 0x00,
-+0x00, 0x01, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x15, 0x00, 0x00, 0x00,
-+0xf8, 0x12, 0x13, 0xdc, 0x47, 0xf3, 0x1c, 0x76, 0x47, 0x2f, 0x2e, 0xd7, 0x02,
-+0x00, 0x00, 0x00, 0x30, 0x00, 0x02, 0x00, 0x07, 0x00, 0x00, 0x00, 0x34, 0x00,
-+0x02, 0x00, 0x07, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, 0x05, 0x00,
-+0x00, 0x00, 0x00, 0x00, 0x05, 0x15, 0x00, 0x00, 0x00, 0x29, 0xc9, 0x4f, 0xd9,
-+0xc2, 0x3c, 0xc3, 0x78, 0x36, 0x55, 0x87, 0xf8, 0x54, 0x04, 0x00, 0x00, 0x05,
-+0x00, 0x00, 0x00, 0x01, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x15, 0x00,
-+0x00, 0x00, 0x25, 0xe1, 0xff, 0x1c, 0xf7, 0x87, 0x6b, 0x2c, 0x25, 0xd2, 0x0c,
-+0xe3, 0xf2, 0x03, 0x00, 0x00, 0x00, 0x2c, 0x29, 0x89, 0x65, 0x2d, 0xd3, 0x01,
-+0x06, 0x00, 0x74, 0x00, 0x75, 0x00, 0x31, 0x00, 0x20, 0x00, 0x10, 0x00, 0x10,
-+0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x74, 0x00,
-+0x75, 0x00, 0x31, 0x00, 0x74, 0x00, 0x65, 0x00, 0x73, 0x00, 0x74, 0x00, 0x40,
-+0x00, 0x61, 0x00, 0x64, 0x00, 0x2e, 0x00, 0x64, 0x00, 0x65, 0x00, 0x76, 0x00,
-+0x65, 0x00, 0x6c, 0x00, 0x41, 0x00, 0x44, 0x00, 0x2e, 0x00, 0x44, 0x00, 0x45,
-+0x00, 0x56, 0x00, 0x45, 0x00, 0x4c, 0x00, 0x10, 0x00, 0x00, 0x00, 0x76, 0x8e,
-+0x25, 0x32, 0x7c, 0x85, 0x00, 0x32, 0xac, 0x8f, 0x02, 0x2c, 0x10, 0x00, 0x00,
-+0x00, 0x6b, 0xe8, 0x51, 0x03, 0x30, 0xed, 0xca, 0x7d, 0xe2, 0x12, 0xa5, 0xde};
-+
-+enum nss_status _nss_sss_getpwuid_r(uid_t uid, struct passwd *result,
-+                                    char *buffer, size_t buflen, int *errnop);
-+static void *pac_client(void *arg)
-+{
-+    struct sss_cli_req_data sss_data = { sizeof(pac), pac };
-+    int errnop = -1;
-+    int ret;
-+    size_t c;
-+
-+    fprintf(stderr, "[%ld][%d][%ld][%s] started\n", time(NULL), getpid(),
-+                                                    syscall(SYS_gettid),
-+                                                    (char *) arg);
-+    for (c = 0; c < 1000; c++) {
-+        /* sss_pac_make_request() does not protect the client's file
-+         * descriptor to the PAC responder. With this one thread will miss a
-+         * reply for a SSS_GET_VERSION request and will wait until
-+         * SSS_CLI_SOCKET_TIMEOUT is passed.
-+
-+        ret = sss_pac_make_request(SSS_PAC_ADD_PAC_USER, &sss_data,
-+                                   NULL, NULL, &errnop);
-+         */
-+        ret = sss_pac_make_request_with_lock(SSS_PAC_ADD_PAC_USER, &sss_data,
-+                                             NULL, NULL, &errnop);
-+        if (ret != NSS_STATUS_SUCCESS
-+                && !(ret == NSS_STATUS_UNAVAIL && errnop != ECONNREFUSED)) {
-+                /* NSS_STATUS_UNAVAIL is returned if the PAC responder rejects
-+                 * the request which is ok becasue the client is waiting for a
-+                 * response here as well. Only errnop == ECONNREFUSED should
-+                 * be treated as error becasue this means that the PAC
-+                 * responder is not running. */
-+            fprintf(stderr, "pac: [%s][%d][%d]\n", (char *)arg, ret, errnop);
-+            return ((void *)((uintptr_t)("X")));
-+        }
-+    }
-+
-+    fprintf(stderr, "[%ld][%s] done\n", time(NULL),(char *) arg);
-+    return NULL;
-+}
-+
-+int main(void)
-+{
-+    pthread_t thread1;
-+    pthread_t thread2;
-+    int ret;
-+    void *t_ret;
-+
-+    pthread_create(&thread1, NULL, pac_client,
-+                   ((void *)((uintptr_t)("Thread 1"))));
-+    pthread_create(&thread2, NULL, pac_client,
-+                   ((void *)((uintptr_t)("Thread 2"))));
-+
-+    ret = pthread_join(thread1, &t_ret);
-+    if (ret != 0 || t_ret != NULL) {
-+        fprintf(stderr, "Thread 1 failed.\n");
-+        return EIO;
-+    }
-+
-+    ret = pthread_join(thread2, &t_ret);
-+    if (ret != 0 || t_ret != NULL) {
-+        fprintf(stderr, "Thread 1 failed.\n");
-+        return EIO;
-+    }
-+
-+    return 0;
-+}
-diff --git a/src/sss_client/sssd_pac.c b/src/sss_client/sssd_pac.c
-index 1d98e38826b36aed199b32880a7e27de905a4592..8444834a7f148787e847f5e8e21186c8701b2de7 100644
---- a/src/sss_client/sssd_pac.c
-+++ b/src/sss_client/sssd_pac.c
-@@ -169,8 +169,8 @@ static krb5_error_code sssdpac_verify(krb5_context kcontext,
-     sss_data.len = sssdctx->data.length;
-     sss_data.data = sssdctx->data.data;
- 
--    ret = sss_pac_make_request(SSS_PAC_ADD_PAC_USER, &sss_data,
--                               NULL, NULL, &errnop);
-+    ret = sss_pac_make_request_with_lock(SSS_PAC_ADD_PAC_USER, &sss_data,
-+                                         NULL, NULL, &errnop);
-     if (ret != 0) {
-         /* Ignore the error */
-     }
-diff --git a/src/tests/intg/Makefile.am b/src/tests/intg/Makefile.am
-index 8566106e9017a8d3c9e7a3898a3a886e2966e346..0af7c62ca243822d919619f3d0ebc852a317efc4 100644
---- a/src/tests/intg/Makefile.am
-+++ b/src/tests/intg/Makefile.am
-@@ -29,6 +29,7 @@ dist_noinst_DATA = \
-     kdc.py \
-     krb5utils.py \
-     test_kcm.py \
-+    test_pac_responder.py \
-     $(NULL)
- 
- config.py: config.py.m4
-diff --git a/src/tests/intg/test_pac_responder.py b/src/tests/intg/test_pac_responder.py
-new file mode 100644
-index 0000000000000000000000000000000000000000..4354a5d78da6a6627a27d0ca85c8a1d47419cedf
---- /dev/null
-+++ b/src/tests/intg/test_pac_responder.py
-@@ -0,0 +1,120 @@
-+#
-+# SSSD PAC responder tests
-+#
-+# Copyright (c) 2017 Red Hat, Inc.
-+# Author: Sumit Bose <sbose@redhat.com>
-+#
-+# This is free software; you can redistribute it and/or modify it
-+# under the terms of the GNU General Public License as published by
-+# the Free Software Foundation; version 2 only
-+#
-+# This program is distributed in the hope that it will be useful, but
-+# WITHOUT ANY WARRANTY; without even the implied warranty of
-+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-+# General Public License for more details.
-+#
-+# You should have received a copy of the GNU General Public License
-+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
-+#
-+import os
-+import stat
-+import time
-+import config
-+import signal
-+import subprocess
-+import pytest
-+from util import unindent
-+
-+
-+def stop_sssd():
-+    with open(config.PIDFILE_PATH, "r") as pid_file:
-+        pid = int(pid_file.read())
-+    os.kill(pid, signal.SIGTERM)
-+    while True:
-+        try:
-+            os.kill(pid, signal.SIGCONT)
-+        except:
-+            break
-+        time.sleep(1)
-+
-+
-+def create_conf_fixture(request, contents):
-+    """Generate sssd.conf and add teardown for removing it"""
-+    conf = open(config.CONF_PATH, "w")
-+    conf.write(contents)
-+    conf.close()
-+    os.chmod(config.CONF_PATH, stat.S_IRUSR | stat.S_IWUSR)
-+    request.addfinalizer(lambda: os.unlink(config.CONF_PATH))
-+
-+
-+def create_sssd_fixture(request):
-+    """Start sssd and add teardown for stopping it and removing state"""
-+    if subprocess.call(["sssd", "-D", "-f"]) != 0:
-+        raise Exception("sssd start failed")
-+
-+    def teardown():
-+        try:
-+            stop_sssd()
-+        except:
-+            pass
-+        for path in os.listdir(config.DB_PATH):
-+            os.unlink(config.DB_PATH + "/" + path)
-+        for path in os.listdir(config.MCACHE_PATH):
-+            os.unlink(config.MCACHE_PATH + "/" + path)
-+    request.addfinalizer(teardown)
-+
-+
-+@pytest.fixture
-+def local_domain_only(request):
-+    conf = unindent("""\
-+        [sssd]
-+        domains = LOCAL
-+        services = nss, pac
-+
-+        [nss]
-+        memcache_timeout = 0
-+
-+        [domain/LOCAL]
-+        id_provider = local
-+        min_id = 10000
-+        max_id = 20000
-+    """).format(**locals())
-+    create_conf_fixture(request, conf)
-+    create_sssd_fixture(request)
-+    return None
-+
-+
-+@pytest.fixture
-+def sssd_pac_test_client(request):
-+    path = os.path.join(config.ABS_BUILDDIR,
-+                        "..", "..", "..", "sssd_pac_test_client")
-+    if os.access(path, os.X_OK):
-+        return path
-+
-+    return None
-+
-+
-+def timeout_handler(signum, frame):
-+    raise Exception("Timeout")
-+
-+
-+def test_multithreaded_pac_client(local_domain_only, sssd_pac_test_client):
-+    """
-+    Test for ticket
-+    https://pagure.io/SSSD/sssd/issue/3518
-+    """
-+
-+    if not sssd_pac_test_client:
-+        pytest.skip("The sssd_pac_test_client is not available, skipping test")
-+
-+    signal.signal(signal.SIGALRM, timeout_handler)
-+    signal.alarm(10)
-+
-+    try:
-+        subprocess.check_call(sssd_pac_test_client)
-+    except:
-+        # cancel alarm
-+        signal.alarm(0)
-+        raise Exception("sssd_pac_test_client failed")
-+
-+    signal.alarm(0)
--- 
-2.13.5
-
diff --git a/SOURCES/0197-sysdb-sanitize-search-filter-input.patch b/SOURCES/0197-sysdb-sanitize-search-filter-input.patch
deleted file mode 100644
index 0b2134d..0000000
--- a/SOURCES/0197-sysdb-sanitize-search-filter-input.patch
+++ /dev/null
@@ -1,140 +0,0 @@
-From 944295de4cf4eeba75d4f6bd476a4f59743e1813 Mon Sep 17 00:00:00 2001
-From: Sumit Bose <sbose@redhat.com>
-Date: Thu, 5 Oct 2017 11:07:38 +0200
-Subject: [PATCH 197/197] sysdb: sanitize search filter input
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-This patch sanitizes the input for sysdb searches by UPN/email, SID and
-UUID.
-
-This security issue was assigned CVE-2017-12173
-
-Reviewed-by: Lukáš Slebodník <lslebodn@redhat.com>
-Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
-(cherry picked from commit 1f2662c8f97c9c0fa250055d4b6750abfc6d0835)
----
- src/db/sysdb_ops.c      | 43 +++++++++++++++++++++++++++++++++++--------
- src/tests/sysdb-tests.c |  7 +++++++
- 2 files changed, 42 insertions(+), 8 deletions(-)
-
-diff --git a/src/db/sysdb_ops.c b/src/db/sysdb_ops.c
-index 7ca6575ce75dab7805236c9f48dbf28a2f3946d2..408af9f389edbe0aff0fb8b96f49f0c4463a620a 100644
---- a/src/db/sysdb_ops.c
-+++ b/src/db/sysdb_ops.c
-@@ -601,6 +601,7 @@ int sysdb_search_user_by_upn_res(TALLOC_CTX *mem_ctx,
-     int ret;
-     const char *def_attrs[] = { SYSDB_NAME, SYSDB_UPN, SYSDB_CANONICAL_UPN,
-                                 SYSDB_USER_EMAIL, NULL };
-+    char *sanitized;
- 
-     tmp_ctx = talloc_new(NULL);
-     if (tmp_ctx == NULL) {
-@@ -608,6 +609,12 @@ int sysdb_search_user_by_upn_res(TALLOC_CTX *mem_ctx,
-         goto done;
-     }
- 
-+    ret = sss_filter_sanitize(tmp_ctx, upn, &sanitized);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_OP_FAILURE, "sss_filter_sanitize failed.\n");
-+        goto done;
-+    }
-+
-     if (domain_scope == true) {
-         base_dn = sysdb_user_base_dn(tmp_ctx, domain);
-     } else {
-@@ -620,7 +627,7 @@ int sysdb_search_user_by_upn_res(TALLOC_CTX *mem_ctx,
- 
-     ret = ldb_search(domain->sysdb->ldb, tmp_ctx, &res,
-                      base_dn, LDB_SCOPE_SUBTREE, attrs ? attrs : def_attrs,
--                     SYSDB_PWUPN_FILTER, upn, upn, upn);
-+                     SYSDB_PWUPN_FILTER, sanitized, sanitized, sanitized);
-     if (ret != EOK) {
-         ret = sysdb_error_to_errno(ret);
-         goto done;
-@@ -4757,17 +4764,31 @@ static errno_t sysdb_search_object_by_str_attr(TALLOC_CTX *mem_ctx,
-                                                bool expect_only_one_result,
-                                                struct ldb_result **_res)
- {
--    char *filter;
-+    char *filter = NULL;
-     errno_t ret;
-+    char *sanitized = NULL;
- 
--    filter = talloc_asprintf(NULL, filter_tmpl, str);
-+    if (str == NULL) {
-+        return EINVAL;
-+    }
-+
-+    ret = sss_filter_sanitize(NULL, str, &sanitized);
-+    if (ret != EOK || sanitized == NULL) {
-+        DEBUG(SSSDBG_OP_FAILURE, "sss_filter_sanitize failed.\n");
-+        goto done;
-+    }
-+
-+    filter = talloc_asprintf(NULL, filter_tmpl, sanitized);
-     if (filter == NULL) {
--        return ENOMEM;
-+        ret = ENOMEM;
-+        goto done;
-     }
- 
-     ret = sysdb_search_object_attr(mem_ctx, domain, filter, attrs,
-                                    expect_only_one_result, _res);
- 
-+done:
-+    talloc_free(sanitized);
-     talloc_free(filter);
-     return ret;
- }
-@@ -4856,7 +4877,8 @@ errno_t sysdb_search_object_by_cert(TALLOC_CTX *mem_ctx,
-                                     struct ldb_result **res)
- {
-     int ret;
--    char *user_filter;
-+    char *user_filter = NULL;
-+    char *filter = NULL;
- 
-     ret = sss_cert_derb64_to_ldap_filter(mem_ctx, cert, SYSDB_USER_MAPPED_CERT,
-                                          NULL, NULL, &user_filter);
-@@ -4865,10 +4887,15 @@ errno_t sysdb_search_object_by_cert(TALLOC_CTX *mem_ctx,
-         return ret;
-     }
- 
--    ret = sysdb_search_object_by_str_attr(mem_ctx, domain,
--                                          SYSDB_USER_CERT_FILTER,
--                                          user_filter, attrs, false, res);
-+    filter = talloc_asprintf(NULL, SYSDB_USER_CERT_FILTER, user_filter);
-     talloc_free(user_filter);
-+    if (filter == NULL) {
-+        return ENOMEM;
-+    }
-+
-+    ret = sysdb_search_object_attr(mem_ctx, domain, filter, attrs, false, res);
-+
-+    talloc_free(filter);
- 
-     return ret;
- }
-diff --git a/src/tests/sysdb-tests.c b/src/tests/sysdb-tests.c
-index 6ec82ce4ca5c4f918bc9f3144c21f33b270ea47e..588eb9ef02033ec061b4187964fe562da84e86c8 100644
---- a/src/tests/sysdb-tests.c
-+++ b/src/tests/sysdb-tests.c
-@@ -6444,6 +6444,13 @@ START_TEST(test_upn_basic)
-     fail_unless(strcmp(str, UPN_PRINC) == 0,
-                 "Expected [%s], got [%s].", UPN_PRINC, str);
- 
-+    /* check if input is sanitized */
-+    ret = sysdb_search_user_by_upn(test_ctx, test_ctx->domain, false,
-+                                   "abc@def.ghi)(name="UPN_USER_NAME")(abc=xyz",
-+                                   NULL, &msg);
-+    fail_unless(ret == ENOENT,
-+                "sysdb_search_user_by_upn failed with un-sanitized input.");
-+
-     talloc_free(test_ctx);
- }
- END_TEST
--- 
-2.13.5
-
diff --git a/SOURCES/0198-ipa-make-sure-view-name-is-initialized-at-startup.patch b/SOURCES/0198-ipa-make-sure-view-name-is-initialized-at-startup.patch
deleted file mode 100644
index 25422d6..0000000
--- a/SOURCES/0198-ipa-make-sure-view-name-is-initialized-at-startup.patch
+++ /dev/null
@@ -1,60 +0,0 @@
-From 2d39f46386fb36b5a68f41644ce22c15bf6ccb67 Mon Sep 17 00:00:00 2001
-From: Sumit Bose <sbose@redhat.com>
-Date: Thu, 31 Aug 2017 22:30:25 +0200
-Subject: [PATCH 198/199] ipa: make sure view name is initialized at startup
-
-sysdb_master_domain_update() can only set the view name properly if it was not
-set before but it might be called multiple times before the view name is
-available if the cache is empty. Since ipa_apply_view() keeps track if
-the view name was already set at startup or not the name can safely be
-cleaned here before sysdb_master_domain_update() is called.
-
-Resolves:
-https://pagure.io/SSSD/sssd/issue/3501
-
-Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
-(cherry picked from commit f00591a4615720640cf01b1c408315b57dd397dc)
----
- src/providers/ipa/ipa_subdomains.c | 19 ++++++++++++++++++-
- 1 file changed, 18 insertions(+), 1 deletion(-)
-
-diff --git a/src/providers/ipa/ipa_subdomains.c b/src/providers/ipa/ipa_subdomains.c
-index 6f0ff50bde234f72d62f43635d9a787316c78430..0cb4d405e45689e9548ad3652e7260f2265bd1fe 100644
---- a/src/providers/ipa/ipa_subdomains.c
-+++ b/src/providers/ipa/ipa_subdomains.c
-@@ -739,6 +739,18 @@ done:
-     return ret;
- }
- 
-+static void clean_view_name(struct sss_domain_info *domain)
-+{
-+    struct sss_domain_info *dom = domain;
-+
-+    while (dom) {
-+        dom->has_views = false;
-+        talloc_free(discard_const(dom->view_name));
-+        dom->view_name = NULL;
-+        dom = get_next_domain(dom, SSS_GND_DESCEND);
-+    }
-+}
-+
- static errno_t ipa_apply_view(struct sss_domain_info *domain,
-                               struct ipa_id_ctx *ipa_id_ctx,
-                               const char *view_name,
-@@ -831,7 +843,12 @@ static errno_t ipa_apply_view(struct sss_domain_info *domain,
-     }
- 
-     if (!read_at_init) {
--        /* refresh view data of all domains at startup */
-+        /* refresh view data of all domains at startup, since
-+         * sysdb_master_domain_update and sysdb_update_subdomains might have
-+         * been called earlier without the proper view name the name is
-+         * cleaned here before the calls. This is acceptable because this is
-+         * the initial setup (!read_at_init).  */
-+        clean_view_name(domain);
-         ret = sysdb_master_domain_update(domain);
-         if (ret != EOK) {
-             DEBUG(SSSDBG_OP_FAILURE, "sysdb_master_domain_update failed "
--- 
-2.13.5
-
diff --git a/SOURCES/0199-CACHE_REQ-Copy-the-cr_domain-list-for-each-request.patch b/SOURCES/0199-CACHE_REQ-Copy-the-cr_domain-list-for-each-request.patch
deleted file mode 100644
index ba0c504..0000000
--- a/SOURCES/0199-CACHE_REQ-Copy-the-cr_domain-list-for-each-request.patch
+++ /dev/null
@@ -1,142 +0,0 @@
-From a1634a3c4b977364fd7612efa3ee21872a8d578e Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Fabiano=20Fid=C3=AAncio?= <fidencio@redhat.com>
-Date: Fri, 20 Oct 2017 09:26:43 +0200
-Subject: [PATCH 199/199] CACHE_REQ: Copy the cr_domain list for each request
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Let's copy the cr_domain list for each request as this list may be
-free'd due to a refresh domains request.
-
-Resolves: https://pagure.io/SSSD/sssd/issue/3551
-
-Signed-off-by: Fabiano Fidêncio <fidencio@redhat.com>
-
-Reviewed-by: Pavel Březina <pbrezina@redhat.com>
-(cherry picked from commit 0f44eefe2ce75a0814c8688495477f6c57f3d39a)
----
- src/responder/common/cache_req/cache_req.c        | 14 +++++++--
- src/responder/common/cache_req/cache_req_domain.c | 38 +++++++++++++++++++++++
- src/responder/common/cache_req/cache_req_domain.h |  5 +++
- 3 files changed, 55 insertions(+), 2 deletions(-)
-
-diff --git a/src/responder/common/cache_req/cache_req.c b/src/responder/common/cache_req/cache_req.c
-index 7d77eb7dd72a7ccf3d687eee8f746ab84176b487..83eab8ed2de0b5c7d25306d853aadc9b53cb4842 100644
---- a/src/responder/common/cache_req/cache_req.c
-+++ b/src/responder/common/cache_req/cache_req.c
-@@ -688,6 +688,7 @@ struct cache_req_state {
-     const char *domain_name;
- 
-     /* work data */
-+    struct cache_req_domain *cr_domains;
-     struct cache_req_result **results;
-     size_t num_results;
-     bool first_iteration;
-@@ -940,6 +941,7 @@ static errno_t cache_req_select_domains(struct tevent_req *req,
-     bool bypass_cache;
-     bool bypass_dp;
-     bool search;
-+    errno_t ret;
- 
-     state = tevent_req_data(req, struct cache_req_state);
- 
-@@ -951,12 +953,20 @@ static errno_t cache_req_select_domains(struct tevent_req *req,
-         return EOK;
-     }
- 
-+    ret = cache_req_domain_copy_cr_domains(state,
-+                                           state->cr->rctx->cr_domains,
-+                                           &state->cr_domains);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_CRIT_FAILURE, "cache_req_copy_cr_domains() failed\n");
-+        return EINVAL;
-+    }
-+
-     if (domain_name != NULL) {
-         CACHE_REQ_DEBUG(SSSDBG_TRACE_FUNC, state->cr,
-                         "Performing a single domain search\n");
- 
-         cr_domain = cache_req_domain_get_domain_by_name(
--                                    state->cr->rctx->cr_domains, domain_name);
-+                                    state->cr_domains, domain_name);
-         if (cr_domain == NULL) {
-             return ERR_DOMAIN_NOT_FOUND;
-         }
-@@ -965,7 +975,7 @@ static errno_t cache_req_select_domains(struct tevent_req *req,
-         CACHE_REQ_DEBUG(SSSDBG_TRACE_FUNC, state->cr,
-                         "Performing a multi-domain search\n");
- 
--        cr_domain = state->cr->rctx->cr_domains;
-+        cr_domain = state->cr_domains;
-         check_next = true;
-     }
- 
-diff --git a/src/responder/common/cache_req/cache_req_domain.c b/src/responder/common/cache_req/cache_req_domain.c
-index c2b5abb74f3bd3d5055f29a4523f29b05feb2014..8c9f155303b174f16b884eb66ba1c88a0256719d 100644
---- a/src/responder/common/cache_req/cache_req_domain.c
-+++ b/src/responder/common/cache_req/cache_req_domain.c
-@@ -47,6 +47,44 @@ cache_req_domain_get_domain_by_name(struct cache_req_domain *domains,
-     return ret;
- }
- 
-+errno_t
-+cache_req_domain_copy_cr_domains(TALLOC_CTX *mem_ctx,
-+                                 struct cache_req_domain *src,
-+                                 struct cache_req_domain **_dest)
-+{
-+    struct cache_req_domain *cr_domains = NULL;
-+    struct cache_req_domain *cr_domain;
-+    struct cache_req_domain *iter;
-+    errno_t ret;
-+
-+    if (src == NULL) {
-+        return EINVAL;
-+    }
-+
-+    DLIST_FOR_EACH(iter, src) {
-+        cr_domain = talloc_zero(mem_ctx, struct cache_req_domain);
-+        if (cr_domain == NULL) {
-+            ret = ENOMEM;
-+            goto done;
-+        }
-+
-+        cr_domain->domain = iter->domain;
-+        cr_domain->fqnames = iter->fqnames;
-+
-+        DLIST_ADD_END(cr_domains, cr_domain, struct cache_req_domain *);
-+    }
-+
-+    *_dest = cr_domains;
-+    ret = EOK;
-+
-+done:
-+    if (ret != EOK) {
-+        cache_req_domain_list_zfree(&cr_domains);
-+    }
-+
-+    return ret;
-+}
-+
- void cache_req_domain_list_zfree(struct cache_req_domain **cr_domains)
- {
-     struct cache_req_domain *p, *q, *r;
-diff --git a/src/responder/common/cache_req/cache_req_domain.h b/src/responder/common/cache_req/cache_req_domain.h
-index 3780a5d8d88d76e100738d28d1dd0e697edf5eae..ebdc71dd635d5d8a5d06e30e96c5d4101b6d98bf 100644
---- a/src/responder/common/cache_req/cache_req_domain.h
-+++ b/src/responder/common/cache_req/cache_req_domain.h
-@@ -50,6 +50,11 @@ cache_req_domain_new_list_from_domain_resolution_order(
-                                         const char *domain_resolution_order,
-                                         struct cache_req_domain **_cr_domains);
- 
-+errno_t
-+cache_req_domain_copy_cr_domains(TALLOC_CTX *mem_ctx,
-+                                 struct cache_req_domain *src,
-+                                 struct cache_req_domain **_dest);
-+
- void cache_req_domain_list_zfree(struct cache_req_domain **cr_domains);
- 
- 
--- 
-2.13.5
-
diff --git a/SOURCES/0200-cache_req-Correction-of-cache_req-debug-string-ID-fo.patch b/SOURCES/0200-cache_req-Correction-of-cache_req-debug-string-ID-fo.patch
deleted file mode 100644
index fd77137..0000000
--- a/SOURCES/0200-cache_req-Correction-of-cache_req-debug-string-ID-fo.patch
+++ /dev/null
@@ -1,68 +0,0 @@
-From 9c22ba8f68b48aff49bcdc8af67964a8852e998c Mon Sep 17 00:00:00 2001
-From: amitkuma <amitkuma@redhat.com>
-Date: Tue, 14 Nov 2017 13:59:12 +0530
-Subject: [PATCH 200/201] cache_req: Correction of cache_req debug string ID
- format
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-The cache-req debug string representation uses a wrong format
-specifier for by-ID requests.
-data->id (uint32_t) should be replaced with  %"PRIu32"
-in cache_req_group_by_id.c, cache_req_object_by_id.c &
-cache_req_user_by_id.c.
-
-Resolves:
-https://pagure.io/SSSD/sssd/issue/3570
-
-Reviewed-by: Lukáš Slebodník <lslebodn@redhat.com>
-(cherry picked from commit d25646c64a7117a6551468256efa82d01647751e)
----
- src/responder/common/cache_req/plugins/cache_req_group_by_id.c  | 2 +-
- src/responder/common/cache_req/plugins/cache_req_object_by_id.c | 2 +-
- src/responder/common/cache_req/plugins/cache_req_user_by_id.c   | 2 +-
- 3 files changed, 3 insertions(+), 3 deletions(-)
-
-diff --git a/src/responder/common/cache_req/plugins/cache_req_group_by_id.c b/src/responder/common/cache_req/plugins/cache_req_group_by_id.c
-index 5ca64283a781318bc4e4d6920fff989c3f3919b4..121f95abe86d2466aaea69f0fe68dfb33b1fee9e 100644
---- a/src/responder/common/cache_req/plugins/cache_req_group_by_id.c
-+++ b/src/responder/common/cache_req/plugins/cache_req_group_by_id.c
-@@ -31,7 +31,7 @@ cache_req_group_by_id_create_debug_name(TALLOC_CTX *mem_ctx,
-                                         struct cache_req_data *data,
-                                         struct sss_domain_info *domain)
- {
--    return talloc_asprintf(mem_ctx, "GID:%d@%s", data->id, domain->name);
-+    return talloc_asprintf(mem_ctx, "GID:%"PRIu32"@%s", data->id, domain->name);
- }
- 
- static errno_t
-diff --git a/src/responder/common/cache_req/plugins/cache_req_object_by_id.c b/src/responder/common/cache_req/plugins/cache_req_object_by_id.c
-index 339bd4f5fef827acc1aa3c123d041e426d9e4782..4c88e1035b41969703c1c38d740e15516ac0d622 100644
---- a/src/responder/common/cache_req/plugins/cache_req_object_by_id.c
-+++ b/src/responder/common/cache_req/plugins/cache_req_object_by_id.c
-@@ -31,7 +31,7 @@ cache_req_object_by_id_create_debug_name(TALLOC_CTX *mem_ctx,
-                                          struct cache_req_data *data,
-                                          struct sss_domain_info *domain)
- {
--    return talloc_asprintf(mem_ctx, "ID:%d@%s", data->id, domain->name);
-+    return talloc_asprintf(mem_ctx, "ID:%"PRIu32"@%s", data->id, domain->name);
- }
- 
- static errno_t
-diff --git a/src/responder/common/cache_req/plugins/cache_req_user_by_id.c b/src/responder/common/cache_req/plugins/cache_req_user_by_id.c
-index 913f9be5bcc2dfd074b52cb3b15fb6948826e831..3c25c7631b3da4a829ab577629334a7ee97980da 100644
---- a/src/responder/common/cache_req/plugins/cache_req_user_by_id.c
-+++ b/src/responder/common/cache_req/plugins/cache_req_user_by_id.c
-@@ -31,7 +31,7 @@ cache_req_user_by_id_create_debug_name(TALLOC_CTX *mem_ctx,
-                                        struct cache_req_data *data,
-                                        struct sss_domain_info *domain)
- {
--    return talloc_asprintf(mem_ctx, "UID:%d@%s", data->id, domain->name);
-+    return talloc_asprintf(mem_ctx, "UID:%"PRIu32"@%s", data->id, domain->name);
- }
- 
- static errno_t
--- 
-2.14.3
-
diff --git a/SOURCES/0201-cache-Check-for-max_id-min_id-in-cache_req.patch b/SOURCES/0201-cache-Check-for-max_id-min_id-in-cache_req.patch
deleted file mode 100644
index 1186293..0000000
--- a/SOURCES/0201-cache-Check-for-max_id-min_id-in-cache_req.patch
+++ /dev/null
@@ -1,318 +0,0 @@
-From 7618d442a25a16940f0842d2ad4ae34e60079844 Mon Sep 17 00:00:00 2001
-From: amitkuma <amitkuma@redhat.com>
-Date: Thu, 30 Nov 2017 22:18:39 +0530
-Subject: [PATCH 201/201] cache: Check for max_id/min_id in cache_req
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-The cache_req code doesn't check the min_id/max_id
-boundaries for requests by ID.
-Extending the .lookup_fn function in each plugin
-that searches by ID for a check that returns non-zero
-if the entry is out of the range and 0 if not.
-
-Resolves: https://pagure.io/SSSD/sssd/issue/3569
-
-Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
-Reviewed-by: Fabiano Fidêncio <fidencio@redhat.com>
-(cherry picked from commit 2af80640f18966d65cf82106059ce3c060df93bf)
----
- src/responder/common/cache_req/cache_req.c         |  1 +
- src/responder/common/cache_req/cache_req_private.h |  2 +
- src/responder/common/cache_req/cache_req_search.c  |  5 ++
- .../common/cache_req/plugins/cache_req_common.c    | 11 +++
- .../cache_req/plugins/cache_req_group_by_id.c      |  6 ++
- .../cache_req/plugins/cache_req_object_by_id.c     |  6 ++
- .../cache_req/plugins/cache_req_user_by_id.c       |  5 ++
- src/tests/cmocka/test_responder_cache_req.c        | 81 +++++++++++++++++++++-
- src/util/util_errors.c                             |  1 +
- src/util/util_errors.h                             |  1 +
- 10 files changed, 117 insertions(+), 2 deletions(-)
-
-diff --git a/src/responder/common/cache_req/cache_req.c b/src/responder/common/cache_req/cache_req.c
-index 83eab8ed2de0b5c7d25306d853aadc9b53cb4842..3ebf64af24bb9a2a67165dd17d27fd25fc7915c2 100644
---- a/src/responder/common/cache_req/cache_req.c
-+++ b/src/responder/common/cache_req/cache_req.c
-@@ -574,6 +574,7 @@ static void cache_req_search_domains_done(struct tevent_req *subreq)
-         }
- 
-         break;
-+    case ERR_ID_OUTSIDE_RANGE:
-     case ENOENT:
-         if (state->check_next == false) {
-             /* Not found. */
-diff --git a/src/responder/common/cache_req/cache_req_private.h b/src/responder/common/cache_req/cache_req_private.h
-index c0ee5f969f2a171b8a6eb396b3d14b593d157b76..5b79d9429bc29c47c28e0b5a6367a6e7035ca1c3 100644
---- a/src/responder/common/cache_req/cache_req_private.h
-+++ b/src/responder/common/cache_req/cache_req_private.h
-@@ -165,4 +165,6 @@ bool
- cache_req_common_dp_recv(struct tevent_req *subreq,
-                          struct cache_req *cr);
- 
-+errno_t cache_req_idminmax_check(struct cache_req_data *data,
-+                                 struct sss_domain_info *domain);
- #endif /* _CACHE_REQ_PRIVATE_H_ */
-diff --git a/src/responder/common/cache_req/cache_req_search.c b/src/responder/common/cache_req/cache_req_search.c
-index 56d0345cd8f98de574961d3c9628ae7a4c24f9be..ba25dcf578ef80aa8597bccb41da839d7bd146b3 100644
---- a/src/responder/common/cache_req/cache_req_search.c
-+++ b/src/responder/common/cache_req/cache_req_search.c
-@@ -197,6 +197,11 @@ static errno_t cache_req_search_cache(TALLOC_CTX *mem_ctx,
- 
-         *_result = result;
-         break;
-+    case ERR_ID_OUTSIDE_RANGE:
-+        CACHE_REQ_DEBUG(SSSDBG_TRACE_FUNC, cr,
-+                        "ID [%s] was filtered out\n",
-+                        cr->debugobj);
-+        break;
-     case ENOENT:
-         CACHE_REQ_DEBUG(SSSDBG_TRACE_FUNC, cr,
-                         "Object [%s] was not found in cache\n",
-diff --git a/src/responder/common/cache_req/plugins/cache_req_common.c b/src/responder/common/cache_req/plugins/cache_req_common.c
-index b80f310feeebbdbc824db441ff5313632585d3fb..d7c6ab2eac412042f90d491eb8428116156f512b 100644
---- a/src/responder/common/cache_req/plugins/cache_req_common.c
-+++ b/src/responder/common/cache_req/plugins/cache_req_common.c
-@@ -26,6 +26,17 @@
- #include "providers/data_provider.h"
- #include "responder/common/cache_req/cache_req_plugin.h"
- 
-+errno_t cache_req_idminmax_check(struct cache_req_data *data,
-+	                         struct sss_domain_info *domain)
-+{
-+   if (((domain->id_min != 0) && (data->id < domain->id_min)) ||
-+       ((domain->id_max != 0) && (data->id > domain->id_max))) {
-+        DEBUG(SSSDBG_FUNC_DATA, "id exceeds min/max boundaries\n");
-+        return ERR_ID_OUTSIDE_RANGE;
-+   }
-+   return EOK;
-+}
-+
- static struct ldb_message *
- cache_req_well_known_sid_msg(TALLOC_CTX *mem_ctx,
-                              const char *sid,
-diff --git a/src/responder/common/cache_req/plugins/cache_req_group_by_id.c b/src/responder/common/cache_req/plugins/cache_req_group_by_id.c
-index 121f95abe86d2466aaea69f0fe68dfb33b1fee9e..04e085456f38e154a04ce0a803f2ba22830c1827 100644
---- a/src/responder/common/cache_req/plugins/cache_req_group_by_id.c
-+++ b/src/responder/common/cache_req/plugins/cache_req_group_by_id.c
-@@ -64,6 +64,12 @@ cache_req_group_by_id_lookup(TALLOC_CTX *mem_ctx,
-                              struct sss_domain_info *domain,
-                              struct ldb_result **_result)
- {
-+    errno_t ret;
-+
-+    ret = cache_req_idminmax_check(data, domain);
-+    if (ret != EOK) {
-+	return ret;
-+    }
-     return sysdb_getgrgid_with_views(mem_ctx, domain, data->id, _result);
- }
- 
-diff --git a/src/responder/common/cache_req/plugins/cache_req_object_by_id.c b/src/responder/common/cache_req/plugins/cache_req_object_by_id.c
-index 4c88e1035b41969703c1c38d740e15516ac0d622..50c149e8795a61307e445d4c063dc0df1166519c 100644
---- a/src/responder/common/cache_req/plugins/cache_req_object_by_id.c
-+++ b/src/responder/common/cache_req/plugins/cache_req_object_by_id.c
-@@ -90,6 +90,12 @@ cache_req_object_by_id_lookup(TALLOC_CTX *mem_ctx,
-                               struct sss_domain_info *domain,
-                               struct ldb_result **_result)
- {
-+    errno_t ret;
-+
-+    ret = cache_req_idminmax_check(data, domain);
-+    if (ret != EOK) {
-+        return ret;
-+    }
-     return sysdb_search_object_by_id(mem_ctx, domain, data->id,
-                                      data->attrs, _result);
- }
-diff --git a/src/responder/common/cache_req/plugins/cache_req_user_by_id.c b/src/responder/common/cache_req/plugins/cache_req_user_by_id.c
-index 3c25c7631b3da4a829ab577629334a7ee97980da..05add9564f57655bb53520b848971286bba6cab5 100644
---- a/src/responder/common/cache_req/plugins/cache_req_user_by_id.c
-+++ b/src/responder/common/cache_req/plugins/cache_req_user_by_id.c
-@@ -64,6 +64,11 @@ cache_req_user_by_id_lookup(TALLOC_CTX *mem_ctx,
-                             struct sss_domain_info *domain,
-                             struct ldb_result **_result)
- {
-+    errno_t ret;
-+    ret = cache_req_idminmax_check(data, domain);
-+    if (ret != EOK) {
-+        return ret;
-+    }
-     return sysdb_getpwuid_with_views(mem_ctx, domain, data->id, _result);
- }
- 
-diff --git a/src/tests/cmocka/test_responder_cache_req.c b/src/tests/cmocka/test_responder_cache_req.c
-index 80086232fd437876c2b190fb972c2ee3194d9efd..82ba4125265d0bacd44a9f0082d588eef673a6f0 100644
---- a/src/tests/cmocka/test_responder_cache_req.c
-+++ b/src/tests/cmocka/test_responder_cache_req.c
-@@ -58,6 +58,11 @@ struct test_group {
-                                     test_single_domain_setup, \
-                                     test_single_domain_teardown)
- 
-+#define new_single_domain_id_limit_test(test) \
-+    cmocka_unit_test_setup_teardown(test_ ## test, \
-+                                    test_single_domain_id_limits_setup, \
-+                                    test_single_domain_teardown)
-+
- #define new_multi_domain_test(test) \
-     cmocka_unit_test_setup_teardown(test_ ## test, \
-                                     test_multi_domain_setup, \
-@@ -451,7 +456,8 @@ __wrap_sss_dp_get_account_send(TALLOC_CTX *mem_ctx,
-     return test_req_succeed_send(mem_ctx, rctx->ev);
- }
- 
--static int test_single_domain_setup(void **state)
-+static int test_single_domain_setup_common(void **state,
-+                                           struct sss_test_conf_param *params)
- {
-     struct cache_req_test_ctx *test_ctx = NULL;
-     errno_t ret;
-@@ -465,7 +471,7 @@ static int test_single_domain_setup(void **state)
-     *state = test_ctx;
- 
-     test_ctx->tctx = create_dom_test_ctx(test_ctx, TESTS_PATH, TEST_CONF_DB,
--                                         TEST_DOM_NAME, TEST_ID_PROVIDER, NULL);
-+                                         TEST_DOM_NAME, TEST_ID_PROVIDER, params);
-     assert_non_null(test_ctx->tctx);
- 
-     test_ctx->rctx = mock_rctx(test_ctx, test_ctx->tctx->ev,
-@@ -480,6 +486,11 @@ static int test_single_domain_setup(void **state)
-     return 0;
- }
- 
-+static int test_single_domain_setup(void **state)
-+{
-+    return test_single_domain_setup_common(state, NULL);
-+}
-+
- static int test_single_domain_teardown(void **state)
- {
-     struct cache_req_test_ctx *test_ctx;
-@@ -495,6 +506,16 @@ static int test_single_domain_teardown(void **state)
-     return 0;
- }
- 
-+static int test_single_domain_id_limits_setup(void **state)
-+{
-+    struct sss_test_conf_param params[] = {
-+        { "min_id", "100" },
-+        { "max_id", "10000" },
-+        { NULL, NULL },             /* Sentinel */
-+    };
-+    return test_single_domain_setup_common(state, params);
-+}
-+
- static int test_multi_domain_setup(void **state)
- {
-     struct cache_req_test_ctx *test_ctx = NULL;
-@@ -526,6 +547,32 @@ static int test_multi_domain_setup(void **state)
-     return 0;
- }
- 
-+void test_user_by_id_below_id_range(void **state)
-+{
-+    struct cache_req_test_ctx *test_ctx = NULL;
-+
-+    test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx);
-+
-+    /* Test. */
-+    run_cache_req(test_ctx, cache_req_user_by_id_send,
-+                  cache_req_user_by_id_test_done, test_ctx->tctx->dom,
-+                  0, 10, ENOENT);
-+    assert_false(test_ctx->dp_called);
-+}
-+
-+void test_user_by_id_above_id_range(void **state)
-+{
-+    struct cache_req_test_ctx *test_ctx = NULL;
-+
-+    test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx);
-+
-+    /* Test. */
-+    run_cache_req(test_ctx, cache_req_user_by_id_send,
-+                  cache_req_user_by_id_test_done, test_ctx->tctx->dom,
-+                  0, 100000, ENOENT);
-+    assert_false(test_ctx->dp_called);
-+}
-+
- static int test_multi_domain_teardown(void **state)
- {
-     struct cache_req_test_ctx *test_ctx;
-@@ -1084,6 +1131,32 @@ void test_group_by_name_multiple_domains_found(void **state)
-     check_group(test_ctx, &groups[0], domain);
- }
- 
-+void test_group_by_id_below_id_range(void **state)
-+{
-+    struct cache_req_test_ctx *test_ctx = NULL;
-+
-+    test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx);
-+
-+    /* Test. */
-+    run_cache_req(test_ctx, cache_req_group_by_id_send,
-+                  cache_req_group_by_id_test_done, test_ctx->tctx->dom,
-+                  0, 10, ENOENT);
-+    assert_false(test_ctx->dp_called);
-+}
-+
-+void test_group_by_id_above_id_range(void **state)
-+{
-+    struct cache_req_test_ctx *test_ctx = NULL;
-+
-+    test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx);
-+
-+    /* Test. */
-+    run_cache_req(test_ctx, cache_req_group_by_id_send,
-+                  cache_req_group_by_id_test_done, test_ctx->tctx->dom,
-+                  0, 100000, ENOENT);
-+    assert_false(test_ctx->dp_called);
-+}
-+
- void test_group_by_name_multiple_domains_notfound(void **state)
- {
-     struct cache_req_test_ctx *test_ctx = NULL;
-@@ -2170,6 +2243,8 @@ int main(int argc, const char *argv[])
-         new_single_domain_test(user_by_id_missing_notfound),
-         new_multi_domain_test(user_by_id_multiple_domains_found),
-         new_multi_domain_test(user_by_id_multiple_domains_notfound),
-+        new_single_domain_id_limit_test(user_by_id_below_id_range),
-+        new_single_domain_id_limit_test(user_by_id_above_id_range),
- 
-         new_single_domain_test(group_by_name_cache_valid),
-         new_single_domain_test(group_by_name_cache_expired),
-@@ -2180,6 +2255,8 @@ int main(int argc, const char *argv[])
-         new_multi_domain_test(group_by_name_multiple_domains_found),
-         new_multi_domain_test(group_by_name_multiple_domains_notfound),
-         new_multi_domain_test(group_by_name_multiple_domains_parse),
-+        new_single_domain_id_limit_test(group_by_id_below_id_range),
-+        new_single_domain_id_limit_test(group_by_id_above_id_range),
- 
-         new_single_domain_test(group_by_id_cache_valid),
-         new_single_domain_test(group_by_id_cache_expired),
-diff --git a/src/util/util_errors.c b/src/util/util_errors.c
-index 97eaf160f20bcc8cfe52254070a2d182e19addd4..9f6e413f0414f38dee4f0ee6b5f3fbeaac5d6612 100644
---- a/src/util/util_errors.c
-+++ b/src/util/util_errors.c
-@@ -116,6 +116,7 @@ struct err_string error_to_str[] = {
-     { "Unable to initialize SSL" }, /* ERR_SSL_FAILURE */
-     { "Unable to verify peer" }, /* ERR_UNABLE_TO_VERIFY_PEER */
-     { "Unable to resolve host" }, /* ERR_UNABLE_TO_RESOLVE_HOST */
-+    { "ID is outside the allowed range" }, /* ERR_ID_OUTSIDE_RANGE */
-     { "ERR_LAST" } /* ERR_LAST */
- };
- 
-diff --git a/src/util/util_errors.h b/src/util/util_errors.h
-index 4a250bf0339ba689680c155fa8e6d43f42c2467e..051111ea7c249a5d8e4f302022898648ba2ffdd2 100644
---- a/src/util/util_errors.h
-+++ b/src/util/util_errors.h
-@@ -138,6 +138,7 @@ enum sssd_errors {
-     ERR_SSL_FAILURE,
-     ERR_UNABLE_TO_VERIFY_PEER,
-     ERR_UNABLE_TO_RESOLVE_HOST,
-+    ERR_ID_OUTSIDE_RANGE,
-     ERR_LAST            /* ALWAYS LAST */
- };
- 
--- 
-2.14.3
-
diff --git a/SOURCES/0202-sudo-always-use-srv_opts-from-id-context.patch b/SOURCES/0202-sudo-always-use-srv_opts-from-id-context.patch
deleted file mode 100644
index a22ead0..0000000
--- a/SOURCES/0202-sudo-always-use-srv_opts-from-id-context.patch
+++ /dev/null
@@ -1,64 +0,0 @@
-From fbfb378def4ce7b807685b6d5391a9f3a2ef26d5 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Pavel=20B=C5=99ezina?= <pbrezina@redhat.com>
-Date: Thu, 19 Oct 2017 10:39:21 +0200
-Subject: [PATCH 202/202] sudo: always use srv_opts from id context
-
-Prior this patch, we remember id_ctx->srv_opts in sudo request to switch
-the latest usn values. This works fine most of the time but it may cause
-a crash.
-
-If we have two concurrent sudo refresh and one of these fails, it causes
-failover to try the next server and possibly replacing the old srv_opts
-with new one and it causes an access after free in the other refresh.
-
-Resolves:
-https://pagure.io/SSSD/sssd/issue/3562
-
-Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
-(cherry picked from commit 2ee201dcf6bbe52abbbed3c2fc4c35ca2e0c8a43)
----
- src/providers/ldap/sdap_async_sudo.c | 7 +------
- 1 file changed, 1 insertion(+), 6 deletions(-)
-
-diff --git a/src/providers/ldap/sdap_async_sudo.c b/src/providers/ldap/sdap_async_sudo.c
-index 3c69837fda313b2645c3a8497252670312f600ea..88a387422d5c9ae86cea583bb38dadf90cba37f3 100644
---- a/src/providers/ldap/sdap_async_sudo.c
-+++ b/src/providers/ldap/sdap_async_sudo.c
-@@ -279,7 +279,6 @@ done:
- struct sdap_sudo_refresh_state {
-     struct sdap_sudo_ctx *sudo_ctx;
-     struct tevent_context *ev;
--    struct sdap_server_opts *srv_opts;
-     struct sdap_options *opts;
-     struct sdap_id_op *sdap_op;
-     struct sysdb_ctx *sysdb;
-@@ -405,9 +404,6 @@ static void sdap_sudo_refresh_connect_done(struct tevent_req *subreq)
- 
-     DEBUG(SSSDBG_TRACE_FUNC, "SUDO LDAP connection successful\n");
- 
--    /* Obtain srv_opts here in case of first connection. */
--    state->srv_opts = state->sudo_ctx->id_ctx->srv_opts;
--
-     /* Renew host information if needed. */
-     if (state->sudo_ctx->run_hostinfo) {
-         subreq = sdap_sudo_get_hostinfo_send(state, state->opts,
-@@ -586,7 +582,6 @@ static void sdap_sudo_refresh_done(struct tevent_req *subreq)
-         goto done;
-     }
- 
--
-     /* start transaction */
-     ret = sysdb_transaction_start(state->sysdb);
-     if (ret != EOK) {
-@@ -621,7 +616,7 @@ static void sdap_sudo_refresh_done(struct tevent_req *subreq)
-     /* remember new usn */
-     ret = sysdb_get_highest_usn(state, rules, rules_count, &usn);
-     if (ret == EOK) {
--        sdap_sudo_set_usn(state->srv_opts, usn);
-+        sdap_sudo_set_usn(state->sudo_ctx->id_ctx->srv_opts, usn);
-     } else {
-         DEBUG(SSSDBG_MINOR_FAILURE, "Unable to get highest USN [%d]: %s\n",
-               ret, sss_strerror(ret));
--- 
-2.14.3
-
diff --git a/SOURCES/0203-SELINUX-Use-getseuserbyname-to-get-IPA-seuser.patch b/SOURCES/0203-SELINUX-Use-getseuserbyname-to-get-IPA-seuser.patch
deleted file mode 100644
index 927f159..0000000
--- a/SOURCES/0203-SELINUX-Use-getseuserbyname-to-get-IPA-seuser.patch
+++ /dev/null
@@ -1,182 +0,0 @@
-From 0006bd76856787614f961001929fea95d0669fe5 Mon Sep 17 00:00:00 2001
-From: Justin Stephenson <jstephen@redhat.com>
-Date: Thu, 9 Mar 2017 17:21:37 -0500
-Subject: [PATCH 203/205] SELINUX: Use getseuserbyname to get IPA seuser
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-The libselinux function getseuserbyname is more reliable method to retrieve
-SELinux usernames then functions from libsemanage `semanage_user_query`
-and is recommended by libsemanage developers.
-Replace get_seuser function with getseuserbyname.
-
-Resolves:
-https://pagure.io/SSSD/sssd/issue/3308
-
-Reviewed-by: Michal Židek <mzidek@redhat.com>
-Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
-Reviewed-by: Petr Lautrbach <plautrba@redhat.com>
-(cherry picked from commit cfe87ca0c4fded9cbf907697d08fa0e6c8f8ebce)
----
- Makefile.am                       |  1 +
- src/providers/ipa/selinux_child.c | 12 +++----
- src/util/sss_semanage.c           | 73 ---------------------------------------
- src/util/util.h                   |  2 --
- 4 files changed, 7 insertions(+), 81 deletions(-)
-
-diff --git a/Makefile.am b/Makefile.am
-index cdd517d50679b876814303fb7d6c63d49bcd8d38..1eb398830e4817d4da0878a6577b45df101e920d 100644
---- a/Makefile.am
-+++ b/Makefile.am
-@@ -4094,6 +4094,7 @@ selinux_child_LDADD = \
-     $(POPT_LIBS) \
-     $(DHASH_LIBS) \
-     $(SEMANAGE_LIBS) \
-+    $(SELINUX_LIBS) \
-     $(NULL)
- endif
- 
-diff --git a/src/providers/ipa/selinux_child.c b/src/providers/ipa/selinux_child.c
-index f8dd3954a7244df2dcbb910aabf8888f41306c09..073475094ee491bd5453898c6ba65214fa14fe59 100644
---- a/src/providers/ipa/selinux_child.c
-+++ b/src/providers/ipa/selinux_child.c
-@@ -27,6 +27,7 @@
- #include <unistd.h>
- #include <sys/stat.h>
- #include <popt.h>
-+#include <selinux/selinux.h>
- 
- #include "util/util.h"
- #include "util/child_common.h"
-@@ -172,11 +173,10 @@ static bool seuser_needs_update(struct input_buffer *ibuf)
-     char *db_mls_range = NULL;
-     errno_t ret;
- 
--    ret = get_seuser(ibuf, ibuf->username, &db_seuser, &db_mls_range);
-+    ret = getseuserbyname(ibuf->username, &db_seuser, &db_mls_range);
-     DEBUG(SSSDBG_TRACE_INTERNAL,
--          "get_seuser: ret: %d msg: [%s] seuser: %s mls: %s\n",
--          ret, sss_strerror(ret),
--          db_seuser ? db_seuser : "unknown",
-+          "getseuserbyname: ret: %d seuser: %s mls: %s\n",
-+          ret, db_seuser ? db_seuser : "unknown",
-           db_mls_range ? db_mls_range : "unknown");
-     if (ret == EOK && db_seuser && db_mls_range &&
-             strcmp(db_seuser, ibuf->seuser) == 0 &&
-@@ -188,8 +188,8 @@ static bool seuser_needs_update(struct input_buffer *ibuf)
-         needs_update = false;
-     }
- 
--    talloc_free(db_seuser);
--    talloc_free(db_mls_range);
-+    free(db_seuser);
-+    free(db_mls_range);
-     return needs_update;
- }
- 
-diff --git a/src/util/sss_semanage.c b/src/util/sss_semanage.c
-index 0da97aad4d8eba733b131c2749932e03ca4242c4..37278cc986a1ea49dc2218a635d52b9d54ca089d 100644
---- a/src/util/sss_semanage.c
-+++ b/src/util/sss_semanage.c
-@@ -382,73 +382,6 @@ done:
-     sss_semanage_close(handle);
-     return ret;
- }
--
--int get_seuser(TALLOC_CTX *mem_ctx, const char *login_name,
--               char **_seuser, char **_mls_range)
--{
--    errno_t ret;
--    const char *seuser;
--    const char *mls_range;
--    semanage_handle_t *sm_handle = NULL;
--    semanage_seuser_t *sm_user = NULL;
--    semanage_seuser_key_t *sm_key = NULL;
--
--    ret = sss_semanage_init(&sm_handle);
--    if (ret == ERR_SELINUX_NOT_MANAGED) {
--        goto done;
--    } else if (ret != EOK) {
--        DEBUG(SSSDBG_CRIT_FAILURE, "Cannot create SELinux handle\n");
--        goto done;
--    }
--
--    ret = semanage_seuser_key_create(sm_handle, login_name, &sm_key);
--    if (ret != EOK) {
--        DEBUG(SSSDBG_CRIT_FAILURE, "Cannot create key for %s\n", login_name);
--        ret = EIO;
--        goto done;
--    }
--
--    ret = semanage_seuser_query(sm_handle, sm_key, &sm_user);
--    if (ret < 0) {
--        DEBUG(SSSDBG_CRIT_FAILURE, "Cannot query for %s\n", login_name);
--        ret = EIO;
--        goto done;
--    }
--
--    seuser = semanage_seuser_get_sename(sm_user);
--    if (seuser != NULL) {
--        *_seuser = talloc_strdup(mem_ctx, seuser);
--        if (*_seuser == NULL) {
--            ret = ENOMEM;
--            goto done;
--        }
--        DEBUG(SSSDBG_OP_FAILURE,
--              "SELinux user for %s: %s\n", login_name, *_seuser);
--    } else {
--        DEBUG(SSSDBG_CRIT_FAILURE, "Cannot get sename for %s\n", login_name);
--    }
--
--    mls_range = semanage_seuser_get_mlsrange(sm_user);
--    if (mls_range != NULL) {
--        *_mls_range = talloc_strdup(mem_ctx, mls_range);
--        if (*_mls_range == NULL) {
--            ret = ENOMEM;
--            goto done;
--        }
--        DEBUG(SSSDBG_OP_FAILURE,
--              "SELinux range for %s: %s\n", login_name, *_mls_range);
--    } else {
--        DEBUG(SSSDBG_CRIT_FAILURE, "Cannot get mlsrange for %s\n", login_name);
--    }
--
--    ret = EOK;
--done:
--    semanage_seuser_key_free(sm_key);
--    semanage_seuser_free(sm_user);
--    sss_semanage_close(sm_handle);
--    return ret;
--}
--
- #else /* HAVE_SEMANAGE */
- int set_seuser(const char *login_name, const char *seuser_name,
-                const char *mls)
-@@ -460,10 +393,4 @@ int del_seuser(const char *login_name)
- {
-     return EOK;
- }
--
--int get_seuser(TALLOC_CTX *mem_ctx, const char *login_name,
--               char **_seuser, char **_mls_range)
--{
--    return EOK;
--}
- #endif  /* HAVE_SEMANAGE */
-diff --git a/src/util/util.h b/src/util/util.h
-index 72d4116e1206e9cc69715edc45bf5b9b91e37e6b..1719d8eec1b6b05877b9be3382589e442bff85be 100644
---- a/src/util/util.h
-+++ b/src/util/util.h
-@@ -658,8 +658,6 @@ errno_t restore_creds(struct sss_creds *saved_creds);
- int set_seuser(const char *login_name, const char *seuser_name,
-                const char *mlsrange);
- int del_seuser(const char *login_name);
--int get_seuser(TALLOC_CTX *mem_ctx, const char *login_name,
--               char **_seuser, char **_mls_range);
- 
- /* convert time from generalized form to unix time */
- errno_t sss_utc_to_time_t(const char *str, const char *format, time_t *unix_time);
--- 
-2.14.3
-
diff --git a/SOURCES/0204-SELINUX-Check-if-SELinux-is-managed-in-selinux_child.patch b/SOURCES/0204-SELINUX-Check-if-SELinux-is-managed-in-selinux_child.patch
deleted file mode 100644
index 3c6c46d..0000000
--- a/SOURCES/0204-SELINUX-Check-if-SELinux-is-managed-in-selinux_child.patch
+++ /dev/null
@@ -1,203 +0,0 @@
-From 8db75e9ebbdaec1dde836380ca38d8cfec61cf3d Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Michal=20=C5=BDidek?= <mzidek@redhat.com>
-Date: Thu, 1 Feb 2018 11:34:21 +0100
-Subject: [PATCH 204/205] SELINUX: Check if SELinux is managed in selinux_child
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-If SELinux policy is not managed at all, don't call any SELinux user
-handling functions and instead return that no update is needed.
-
-Pair-Programmed-With: Jakub Hrozek <jhrozek@redhat.com>
-Reviewed-by: Lukáš Slebodník <lslebodn@redhat.com>
-Reviewed-by: Fabiano Fidêncio <fidencio@redhat.com>
-
-Resolves:
-https://pagure.io/SSSD/sssd/issue/3618
-(cherry picked from commit 450b472a68abf442479755c7916c757907b35ea5)
----
- src/providers/ipa/selinux_child.c |  3 +-
- src/util/sss_semanage.c           | 82 +++++++++++++++++++++++++++++++--------
- src/util/util.h                   |  3 ++
- 3 files changed, 70 insertions(+), 18 deletions(-)
-
-diff --git a/src/providers/ipa/selinux_child.c b/src/providers/ipa/selinux_child.c
-index 073475094ee491bd5453898c6ba65214fa14fe59..bc9460e122ce991087a744fd5335e6c468c6a64b 100644
---- a/src/providers/ipa/selinux_child.c
-+++ b/src/providers/ipa/selinux_child.c
-@@ -27,7 +27,6 @@
- #include <unistd.h>
- #include <sys/stat.h>
- #include <popt.h>
--#include <selinux/selinux.h>
- 
- #include "util/util.h"
- #include "util/child_common.h"
-@@ -173,7 +172,7 @@ static bool seuser_needs_update(struct input_buffer *ibuf)
-     char *db_mls_range = NULL;
-     errno_t ret;
- 
--    ret = getseuserbyname(ibuf->username, &db_seuser, &db_mls_range);
-+    ret = sss_get_seuser(ibuf->username, &db_seuser, &db_mls_range);
-     DEBUG(SSSDBG_TRACE_INTERNAL,
-           "getseuserbyname: ret: %d seuser: %s mls: %s\n",
-           ret, db_seuser ? db_seuser : "unknown",
-diff --git a/src/util/sss_semanage.c b/src/util/sss_semanage.c
-index 37278cc986a1ea49dc2218a635d52b9d54ca089d..25b6bcdad2ad7e7ac710497f13d6a6e22360b0dd 100644
---- a/src/util/sss_semanage.c
-+++ b/src/util/sss_semanage.c
-@@ -22,8 +22,9 @@
- #include "config.h"
- 
- #include <stdio.h>
--#ifdef HAVE_SEMANAGE
-+#if defined(HAVE_SEMANAGE) && defined(HAVE_SELINUX)
- #include <semanage/semanage.h>
-+#include <selinux/selinux.h>
- #endif
- 
- #include "util/util.h"
-@@ -32,7 +33,7 @@
- #define DEFAULT_SERANGE "s0"
- #endif
- 
--#ifdef HAVE_SEMANAGE
-+#if defined(HAVE_SEMANAGE) && defined(HAVE_SELINUX)
- /* turn libselinux messages into SSSD DEBUG() calls */
- static void sss_semanage_error_callback(void *varg,
-                                         semanage_handle_t *handle,
-@@ -73,33 +74,47 @@ static void sss_semanage_close(semanage_handle_t *handle)
-     semanage_handle_destroy(handle);
- }
- 
--static int sss_semanage_init(semanage_handle_t **_handle)
-+static int sss_is_selinux_managed(semanage_handle_t *handle)
- {
-     int ret;
--    semanage_handle_t *handle = NULL;
- 
--    handle = semanage_handle_create();
--    if (!handle) {
--        DEBUG(SSSDBG_CRIT_FAILURE, "Cannot create SELinux management handle\n");
--        ret = EIO;
--        goto done;
-+    if (handle == NULL) {
-+        return EINVAL;
-     }
- 
--    semanage_msg_set_callback(handle,
--                              sss_semanage_error_callback,
--                              NULL);
--
-     ret = semanage_is_managed(handle);
-     if (ret == 0) {
-         DEBUG(SSSDBG_TRACE_FUNC, "SELinux policy not managed via libsemanage\n");
--        ret = ERR_SELINUX_NOT_MANAGED;
--        goto done;
-+        return ERR_SELINUX_NOT_MANAGED;
-     } else if (ret == -1) {
-         DEBUG(SSSDBG_CRIT_FAILURE, "Call to semanage_is_managed failed\n");
-+        return EIO;
-+    }
-+
-+    return EOK;
-+}
-+
-+static int sss_semanage_init(semanage_handle_t **_handle)
-+{
-+    int ret;
-+    semanage_handle_t *handle = NULL;
-+
-+    handle = semanage_handle_create();
-+    if (!handle) {
-+        DEBUG(SSSDBG_CRIT_FAILURE, "Cannot create SELinux management handle\n");
-         ret = EIO;
-         goto done;
-     }
- 
-+    semanage_msg_set_callback(handle,
-+                              sss_semanage_error_callback,
-+                              NULL);
-+
-+    ret = sss_is_selinux_managed(handle);
-+    if (ret != EOK) {
-+        goto done;
-+    }
-+
-     ret = semanage_access_check(handle);
-     if (ret < SEMANAGE_CAN_READ) {
-         DEBUG(SSSDBG_CRIT_FAILURE, "Cannot read SELinux policy store\n");
-@@ -229,6 +244,34 @@ done:
-     return ret;
- }
- 
-+int sss_get_seuser(const char *linuxuser,
-+                   char **selinuxuser,
-+                   char **level)
-+{
-+    int ret;
-+    semanage_handle_t *handle;
-+
-+    handle = semanage_handle_create();
-+    if (handle == NULL) {
-+        DEBUG(SSSDBG_CRIT_FAILURE, "Cannot create SELinux management handle\n");
-+        return EIO;
-+    }
-+
-+    semanage_msg_set_callback(handle,
-+                              sss_semanage_error_callback,
-+                              NULL);
-+
-+    /* We only needed the handle for this call. Close the handle right
-+     * after it */
-+    ret = sss_is_selinux_managed(handle);
-+    sss_semanage_close(handle);
-+    if (ret != EOK) {
-+        return ret;
-+    }
-+
-+    return getseuserbyname(linuxuser, selinuxuser, level);
-+}
-+
- int set_seuser(const char *login_name, const char *seuser_name,
-                const char *mls)
- {
-@@ -382,7 +425,7 @@ done:
-     sss_semanage_close(handle);
-     return ret;
- }
--#else /* HAVE_SEMANAGE */
-+#else /* HAVE_SEMANAGE && HAVE_SELINUX */
- int set_seuser(const char *login_name, const char *seuser_name,
-                const char *mls)
- {
-@@ -393,4 +436,11 @@ int del_seuser(const char *login_name)
- {
-     return EOK;
- }
-+
-+int sss_get_seuser(const char *linuxuser,
-+                   char **selinuxuser,
-+                   char **level)
-+{
-+    return EOK;
-+}
- #endif  /* HAVE_SEMANAGE */
-diff --git a/src/util/util.h b/src/util/util.h
-index 1719d8eec1b6b05877b9be3382589e442bff85be..74ce2dcecfe17d1cea96cfb5c4b7edb8fd46f09f 100644
---- a/src/util/util.h
-+++ b/src/util/util.h
-@@ -658,6 +658,9 @@ errno_t restore_creds(struct sss_creds *saved_creds);
- int set_seuser(const char *login_name, const char *seuser_name,
-                const char *mlsrange);
- int del_seuser(const char *login_name);
-+int sss_get_seuser(const char *linuxuser,
-+                   char **selinuxuser,
-+                   char **level);
- 
- /* convert time from generalized form to unix time */
- errno_t sss_utc_to_time_t(const char *str, const char *format, time_t *unix_time);
--- 
-2.14.3
-
diff --git a/SOURCES/0205-util-Add-sss_-prefix-to-some-functions.patch b/SOURCES/0205-util-Add-sss_-prefix-to-some-functions.patch
deleted file mode 100644
index 020f747..0000000
--- a/SOURCES/0205-util-Add-sss_-prefix-to-some-functions.patch
+++ /dev/null
@@ -1,142 +0,0 @@
-From da163073db166a8f3acc07391ccdaedc8aa70230 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Michal=20=C5=BDidek?= <mzidek@redhat.com>
-Date: Tue, 6 Feb 2018 19:17:55 +0100
-Subject: [PATCH 205/205] util: Add sss_ prefix to some functions
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Add sss_ prefix to del_seuser and set_seuser for consistency
-with sss_get_seuser. Also sss_ prefix makes it clear that
-these functions come from SSSD.
-
-Reviewed-by: Lukáš Slebodník <lslebodn@redhat.com>
-
-Resolves:
-https://pagure.io/SSSD/sssd/issue/3618
-(cherry picked from commit 6b9c38df5712b951e31800efea2df0802e333e08)
----
- src/providers/ipa/selinux_child.c |  4 ++--
- src/tools/sss_useradd.c           |  2 +-
- src/tools/sss_userdel.c           |  2 +-
- src/tools/sss_usermod.c           |  2 +-
- src/util/sss_semanage.c           | 12 ++++++------
- src/util/util.h                   |  6 +++---
- 6 files changed, 14 insertions(+), 14 deletions(-)
-
-diff --git a/src/providers/ipa/selinux_child.c b/src/providers/ipa/selinux_child.c
-index bc9460e122ce991087a744fd5335e6c468c6a64b..701be4b35b0f9a9afd9c58eae724d3b8615d0b8c 100644
---- a/src/providers/ipa/selinux_child.c
-+++ b/src/providers/ipa/selinux_child.c
-@@ -157,9 +157,9 @@ static int sc_set_seuser(const char *login_name, const char *seuser_name,
-          * default. We need to remove the SELinux user from the DB
-          * in that case
-          */
--        ret = del_seuser(login_name);
-+        ret = sss_del_seuser(login_name);
-     } else {
--        ret = set_seuser(login_name, seuser_name, mls);
-+        ret = sss_set_seuser(login_name, seuser_name, mls);
-     }
-     umask(old_mask);
-     return ret;
-diff --git a/src/tools/sss_useradd.c b/src/tools/sss_useradd.c
-index 8521b83011b42c9e2acca4136f154acb3919440c..ca2cbd6c119e5a1735e5b3b524cddeccb68a2578 100644
---- a/src/tools/sss_useradd.c
-+++ b/src/tools/sss_useradd.c
-@@ -205,7 +205,7 @@ int main(int argc, const char **argv)
- 
-     /* Set SELinux login context - must be done after transaction is done
-      * b/c libselinux calls getpwnam */
--    ret = set_seuser(tctx->octx->name, pc_selinux_user, NULL);
-+    ret = sss_set_seuser(tctx->octx->name, pc_selinux_user, NULL);
-     if (ret != EOK) {
-         ERROR("Cannot set SELinux login context\n");
-         ret = EXIT_FAILURE;
-diff --git a/src/tools/sss_userdel.c b/src/tools/sss_userdel.c
-index d085dc3cabd31b2ee82b13c6cbc39c7658b071d1..fb0f2c2ab6163738da2dcf4177c06cd5dc524345 100644
---- a/src/tools/sss_userdel.c
-+++ b/src/tools/sss_userdel.c
-@@ -254,7 +254,7 @@ int main(int argc, const char **argv)
- 
-     /* Set SELinux login context - must be done after transaction is done
-      * b/c libselinux calls getpwnam */
--    ret = del_seuser(tctx->octx->name);
-+    ret = sss_del_seuser(tctx->octx->name);
-     if (ret != EOK) {
-         ERROR("Cannot reset SELinux login context\n");
-         ret = EXIT_FAILURE;
-diff --git a/src/tools/sss_usermod.c b/src/tools/sss_usermod.c
-index 55e94394766f5f46bb3c14c231186f2d79d6b6ab..6a818f13ad2a7e087e23fa2190b83aeb1eabdbac 100644
---- a/src/tools/sss_usermod.c
-+++ b/src/tools/sss_usermod.c
-@@ -300,7 +300,7 @@ int main(int argc, const char **argv)
- 
-     /* Set SELinux login context - must be done after transaction is done
-      * b/c libselinux calls getpwnam */
--    ret = set_seuser(tctx->octx->name, pc_selinux_user, NULL);
-+    ret = sss_set_seuser(tctx->octx->name, pc_selinux_user, NULL);
-     if (ret != EOK) {
-         ERROR("Cannot set SELinux login context\n");
-         ret = EXIT_FAILURE;
-diff --git a/src/util/sss_semanage.c b/src/util/sss_semanage.c
-index 25b6bcdad2ad7e7ac710497f13d6a6e22360b0dd..1150b6236c2c227fe2fc69f2505b6e254a1e64ec 100644
---- a/src/util/sss_semanage.c
-+++ b/src/util/sss_semanage.c
-@@ -272,8 +272,8 @@ int sss_get_seuser(const char *linuxuser,
-     return getseuserbyname(linuxuser, selinuxuser, level);
- }
- 
--int set_seuser(const char *login_name, const char *seuser_name,
--               const char *mls)
-+int sss_set_seuser(const char *login_name, const char *seuser_name,
-+                   const char *mls)
- {
-     semanage_handle_t *handle = NULL;
-     semanage_seuser_key_t *key = NULL;
-@@ -346,7 +346,7 @@ done:
-     return ret;
- }
- 
--int del_seuser(const char *login_name)
-+int sss_del_seuser(const char *login_name)
- {
-     semanage_handle_t *handle = NULL;
-     semanage_seuser_key_t *key = NULL;
-@@ -426,13 +426,13 @@ done:
-     return ret;
- }
- #else /* HAVE_SEMANAGE && HAVE_SELINUX */
--int set_seuser(const char *login_name, const char *seuser_name,
--               const char *mls)
-+int sss_set_seuser(const char *login_name, const char *seuser_name,
-+                   const char *mls)
- {
-     return EOK;
- }
- 
--int del_seuser(const char *login_name)
-+int sss_del_seuser(const char *login_name)
- {
-     return EOK;
- }
-diff --git a/src/util/util.h b/src/util/util.h
-index 74ce2dcecfe17d1cea96cfb5c4b7edb8fd46f09f..e816ba8462afb907e291aaa1692208ccb822205c 100644
---- a/src/util/util.h
-+++ b/src/util/util.h
-@@ -655,9 +655,9 @@ errno_t restore_creds(struct sss_creds *saved_creds);
-  * certain permissions. Therefore the caller should make sure the umask is
-  * not too restricted (especially when called from the daemon code).
-  */
--int set_seuser(const char *login_name, const char *seuser_name,
--               const char *mlsrange);
--int del_seuser(const char *login_name);
-+int sss_set_seuser(const char *login_name, const char *seuser_name,
-+                   const char *mlsrange);
-+int sss_del_seuser(const char *login_name);
- int sss_get_seuser(const char *linuxuser,
-                    char **selinuxuser,
-                    char **level);
--- 
-2.14.3
-
diff --git a/SPECS/sssd.spec b/SPECS/sssd.spec
index e957397..f4ef270 100644
--- a/SPECS/sssd.spec
+++ b/SPECS/sssd.spec
@@ -8,7 +8,7 @@
 
 # Determine the location of the LDB modules directory
 %global ldb_modulesdir %(pkg-config --variable=modulesdir ldb)
-%global ldb_version 1.1.17
+%global ldb_version 1.2.2
 
 
 %if (0%{?fedora} || 0%{?rhel} >= 7)
@@ -19,7 +19,7 @@
 
     %global with_krb5_localauth_plugin 1
 
-%global libwbc_alternatives_version 0.13
+%global libwbc_alternatives_version 0.14
 %global libwbc_lib_version %{libwbc_alternatives_version}.0
 %global libwbc_alternatives_suffix %nil
 %if 0%{?__isa_bits} == 64
@@ -39,221 +39,121 @@
 %endif
 
 Name: sssd
-Version: 1.15.2
-Release: 50%{?dist}.11
+Version: 1.16.0
+Release: 19%{?dist}
 Group: Applications/System
 Summary: System Security Services Daemon
 License: GPLv3+
 URL: https://pagure.io/SSSD/sssd/
 Source0: https://releases.pagure.org/SSSD/sssd/sssd-%{version}.tar.gz
+Source1: cert9.db
+Source2: key4.db
 BuildRoot: %(mktemp -ud %{_tmppath}/%{name}-%{version}-%{release}-XXXXXX)
 
 ### Patches ###
-Patch0001: 0001-MAN-Mention-sssd-secrets-in-SEE-ALSO-section.patch
-Patch0002: 0002-split_on_separator-move-to-a-separate-file.patch
-Patch0003: 0003-util-move-string_in_list-to-util_ext.patch
-Patch0004: 0004-certmap-add-new-library-libsss_certmap.patch
-Patch0005: 0005-certmap-add-placeholder-for-OpenSSL-implementation.patch
-Patch0006: 0006-sysdb-add-sysdb_attrs_copy.patch
-Patch0007: 0007-sdap_get_users_send-new-argument-mapped_attrs.patch
-Patch0008: 0008-LDAP-always-store-the-certificate-from-the-request.patch
-Patch0009: 0009-sss_cert_derb64_to_ldap_filter-add-sss_certmap-suppo.patch
-Patch0010: 0010-sysdb-add-certmap-related-calls.patch
-Patch0011: 0011-IPA-add-certmap-support.patch
-Patch0012: 0012-nss-idmap-add-sss_nss_getlistbycert.patch
-Patch0013: 0013-nss-allow-larger-buffer-for-certificate-based-reques.patch
-Patch0014: 0014-IPA-Add-s2n-request-to-string-function.patch
-Patch0015: 0015-IPA-Enhance-debug-logging-for-ipa-s2n-operations.patch
-Patch0016: 0016-UTIL-iobuf-Make-input-parameter-for-the-readonly-ope.patch
-Patch0017: 0017-UTIL-Fix-a-typo-in-the-tcurl-test-tool.patch
-Patch0018: 0018-UTIL-Add-SAFEALIGN_COPY_UINT8_CHECK.patch
-Patch0019: 0019-UTIL-Add-utility-macro-cli_creds_get_gid.patch
-Patch0020: 0020-UTIL-Add-type-specific-getsetters-to-sss_iobuf.patch
-Patch0021: 0021-UTIL-krb5-principal-un-marshalling.patch
-Patch0022: 0022-KCM-Initial-responder-build-and-packaging.patch
-Patch0023: 0023-KCM-request-parsing-and-sending-a-reply.patch
-Patch0024: 0024-KCM-Implement-an-internal-ccache-storage-and-retriev.patch
-Patch0025: 0025-KCM-Add-a-in-memory-credential-storage.patch
-Patch0026: 0026-KCM-Implement-KCM-server-operations.patch
-Patch0027: 0027-MAN-Add-a-manual-page-for-sssd-kcm.patch
-Patch0028: 0028-TESTS-Add-integration-tests-for-the-KCM-responder.patch
-Patch0029: 0029-SECRETS-Create-DB-path-before-the-operation-itself.patch
-Patch0030: 0030-SECRETS-Return-a-nicer-error-message-on-request-with.patch
-Patch0031: 0031-SECRETS-Store-ccaches-in-secrets-for-the-KCM-respond.patch
-Patch0032: 0032-TCURL-Support-HTTP-POST-for-creating-containers.patch
-Patch0033: 0033-KCM-Store-ccaches-in-secrets.patch
-Patch0034: 0034-KCM-Make-the-secrets-ccache-back-end-configurable-ma.patch
-Patch0035: 0035-KCM-Queue-requests-by-the-same-UID.patch
-Patch0036: 0036-KCM-Idle-terminate-the-responder-if-the-secrets-back.patch
-Patch0037: 0037-CONFIGURE-Fix-fallback-if-pkg-config-for-uuid-is-mis.patch
-Patch0038: 0038-intg-fix-configure-failure-with-strict-cflags.patch
-Patch0039: 0039-intg-Remove-bashism-from-intgcheck-prepare.patch
-Patch0040: 0040-UTIL-Introduce-subdomain_create_conf_path.patch
-Patch0041: 0041-SUBDOMAINS-Allow-use_fully_qualified_names-for-subdo.patch
-Patch0042: 0042-CACHE_REQ-Descend-into-subdomains-on-lookups.patch
-Patch0043: 0043-NSS-TESTS-Fix-subdomains-attribution.patch
-Patch0044: 0044-NSS-TESTS-Improve-setup-teardown-for-subdomains-test.patch
-Patch0045: 0045-NSS-TESTS-Include-searches-for-non-fqnames-members-o.patch
-Patch0046: 0046-SYSDB-Add-methods-to-deal-with-the-domain-s-resoluti.patch
-Patch0047: 0047-SYSDB-TESTS-Add-tests-for-the-domain-s-resolution-or.patch
-Patch0048: 0048-IPA-Get-ipaDomainsResolutionOrder-from-ipaConfig.patch
-Patch0049: 0049-IPA_SUBDOMAINS-Rename-_refresh_view-to-_refresh_view.patch
-Patch0050: 0050-IPA-Get-ipaDomainsResolutionOrder-from-IPA-ID-View.patch
-Patch0051: 0051-DLINKLIST-Add-DLIST_FOR_EACH_SAFE-macro.patch
-Patch0052: 0052-CACHE_REQ-Make-use-of-domainResolutionOrder.patch
-Patch0053: 0053-UTIL-Expose-replace_char-as-sss_replace_char.patch
-Patch0054: 0054-Add-domain_resolution_order-config-option.patch
-Patch0055: 0055-ssh-handle-binary-keys-correctly.patch
-Patch0056: 0056-ssh-add-support-for-certificates-from-non-default-vi.patch
-Patch0057: 0057-krb5-return-to-responder-that-pkinit-is-not-availabl.patch
-Patch0058: 0058-IPA-add-mapped-attributes-to-user-from-trusted-domai.patch
-Patch0059: 0059-IPA-lookup-AD-users-by-certificates-on-IPA-clients.patch
-Patch0060: 0060-IPA-enable-AD-user-lookup-by-certificate.patch
-Patch0061: 0061-CONFDB-Introduce-SSSD-domain-type-to-distinguish-POS.patch
-Patch0062: 0062-CONFDB-Allow-configuring-application-sections-as-non.patch
-Patch0063: 0063-CACHE_REQ-Domain-type-selection-in-cache_req.patch
-Patch0064: 0064-IFP-Search-both-POSIX-and-non-POSIX-domains.patch
-Patch0065: 0065-IFP-ListByName-Don-t-crash-when-no-results-are-found.patch
-Patch0066: 0066-PAM-Remove-unneeded-memory-context.patch
-Patch0067: 0067-PAM-Add-application-services.patch
-Patch0068: 0068-SYSDB-Allow-storing-non-POSIX-users.patch
-Patch0069: 0069-SYSDB-Only-generate-new-UID-in-local-domain.patch
-Patch0070: 0070-LDAP-save-non-POSIX-users-in-application-domains.patch
-Patch0071: 0071-LDAP-Relax-search-filters-in-application-domains.patch
-Patch0072: 0072-KRB5-Authenticate-users-in-a-non-POSIX-domain-using-.patch
-Patch0073: 0073-KCM-Fix-off-by-one-error-in-secrets-key-parsing.patch
-Patch0074: 0074-tcurl-add-support-for-ssl-and-raw-output.patch
-Patch0075: 0075-tcurl-test-refactor-so-new-options-can-be-added-more.patch
-Patch0076: 0076-tcurl-test-add-support-for-raw-output.patch
-Patch0077: 0077-tcurl-test-add-support-for-tls-settings.patch
-Patch0078: 0078-tcurl-add-support-for-http-basic-auth.patch
-Patch0079: 0079-tcurl-test-allow-to-set-custom-headers.patch
-Patch0080: 0080-tcurl-test-add-support-for-client-certificate.patch
-Patch0081: 0081-ci-do-not-build-secrets-on-rhel6.patch
-Patch0082: 0082-build-make-curl-required-by-secrets.patch
-Patch0083: 0083-secrets-use-tcurl-in-proxy-provider.patch
-Patch0084: 0084-secrets-remove-http-parser-code-in-proxy-provider.patch
-Patch0085: 0085-secrets-allow-to-configure-certificate-check.patch
-Patch0086: 0086-secrets-support-HTTP-basic-authentication-with-proxy.patch
-Patch0087: 0087-secrets-fix-debug-message.patch
-Patch0088: 0088-secrets-always-add-Content-Length-header.patch
-Patch0089: 0089-sss_iobuf-fix-read-shadows-a-global-declaration.patch
-Patch0090: 0090-configure-fix-typo.patch
-Patch0091: 0091-pam_test_client-add-service-and-environment-to-PAM-t.patch
-Patch0092: 0092-pam_test_client-add-SSSD-getpwnam-lookup.patch
-Patch0093: 0093-sss_sifp-update-method-names.patch
-Patch0094: 0094-pam_test_client-add-InfoPipe-user-lookup.patch
-Patch0095: 0095-sssctl-integrate-pam_test_client-into-sssctl.patch
-Patch0096: 0096-i18n-adding-sssctl-files.patch
-Patch0097: 0097-responders-do-not-leak-selinux-context-on-clients-de.patch
-Patch0098: 0098-ipa_s2n_get_acct_info_send-provide-correct-req_input.patch
-Patch0099: 0099-config-check-Message-when-sssd.conf-is-missing.patch
-Patch0100: 0100-sbus-check-connection-for-NULL-before-unregister-it.patch
-Patch0101: 0101-selinux-Do-not-fail-if-SELinux-is-not-managed.patch
-Patch0102: 0102-UTIL-Use-max-15-characters-for-AD-host-UPN.patch
-Patch0103: 0103-Move-sized_output_name-and-sized_domain_name-into-re.patch
-Patch0104: 0104-IFP-Use-sized_domain_name-to-format-the-groups-the-u.patch
-Patch0105: 0105-RESPONDER-Fallback-to-global-domain-resolution-order.patch
-Patch0106: 0106-NSS-TESTS-Improve-non-fqnames-tests.patch
-Patch0107: 0107-CACHE_REQ-Allow-configurationless-shortname-lookups.patch
-Patch0108: 0108-CACHE_REQ_DOMAIN-Add-some-comments-to-cache_req_doma.patch
-Patch0109: 0109-RESPONDER_COMMON-Improve-domaiN_resolution_order-deb.patch
-Patch0110: 0110-CACHE_REQ_DOMAIN-debug-the-set-domain-resolution-ord.patch
-Patch0111: 0111-SECRETS-remove-unused-variable.patch
-Patch0112: 0112-IPA-Improve-DEBUG-message-if-a-group-has-no-ipaNTSec.patch
-Patch0113: 0113-IPA-Improve-s2n-debug-message-for-missing-ipaNTSecur.patch
-Patch0114: 0114-CONFDB-Fix-standalone-application-domains.patch
-Patch0115: 0115-utils-add-sss_domain_is_forest_root.patch
-Patch0116: 0116-ad-handle-forest-root-not-listed-in-ad_enabled_domai.patch
-Patch0117: 0117-SDAP-Fix-handling-of-search-bases.patch
-Patch0118: 0118-overrides-add-certificates-to-mapped-attribute.patch
-Patch0119: 0119-AD-Make-ad_account_can_shortcut-reusable-by-SSSD-on-.patch
-Patch0120: 0120-LDAP-AD-Do-not-fail-in-case-rfc2307bis_nested_groups.patch
-Patch0121: 0121-PAM-check-matching-certificates-from-all-domains.patch
-Patch0122: 0122-DP-Reduce-Data-Provider-log-level-noise.patch
-Patch0123: 0123-NSS-Move-output-name-formatting-to-utils.patch
-Patch0124: 0124-CACHE_REQ-Add-a-new-cache_req_ncache_filter_fn-plugi.patch
-Patch0125: 0125-CACHE_REQ_RESULT-Introduce-cache_req_create_ldb_resu.patch
-Patch0126: 0126-CACHE_REQ-Make-use-of-cache_req_ncache_filter_fn.patch
-Patch0127: 0127-SERVER_MODE-Update-sdap-lists-for-each-ad_ctx.patch
-Patch0128: 0128-sss_nss_getlistbycert-return-results-from-multiple-d.patch
-Patch0129: 0129-CACHE_REQ-Avoid-using-of-uninitialized-value.patch
-Patch0130: 0130-CACHE_REQ-Ensure-the-domains-are-updated-for-filter-.patch
-Patch0131: 0131-AD-SUBDOMAINS-Fix-search-bases-for-child-domains.patch
-Patch0132: 0132-KRB5-Advise-the-user-to-inspect-the-krb5_child.log-i.patch
-Patch0133: 0133-cache_req-use-the-right-negative-cache-for-initgroup.patch
-Patch0134: 0134-test-make-sure-p11_child-is-build-for-pam-srv-tests.patch
-Patch0135: 0135-pam-properly-support-UPN-logon-names.patch
-Patch0136: 0136-KCM-Fix-the-per-client-serialization-queue.patch
-Patch0137: 0137-TESTS-Add-a-test-for-parallel-execution-of-klist.patch
-Patch0138: 0138-ipa-filter-IPA-users-from-extdom-lookups-by-certific.patch
-Patch0139: 0139-krb5-accept-changed-principal-if-krb5_canonicalize-T.patch
-Patch0140: 0140-IPA-Avoid-using-uninitialized-ret-value-when-skippin.patch
-Patch0141: 0141-IPA-Return-from-function-after-marking-a-request-as-.patch
-Patch0142: 0142-HBAC-Do-not-rely-on-originalMemberOf-use-the-sysdb-m.patch
-Patch0143: 0143-VALIDATORS-Add-subdomain-section.patch
-Patch0144: 0144-VALIDATORS-Remove-application-section-domain.patch
-Patch0145: 0145-VALIDATORS-Escape-special-regex-chars.patch
-Patch0146: 0146-TESTS-Add-unit-tests-for-cfg-validation.patch
-Patch0147: 0147-MAN-Fix-typo-in-trusted-domain-section.patch
-Patch0148: 0148-VALIDATORS-Change-regex-for-app-domains.patch
-Patch0149: 0149-VALIDATORS-Detect-inherit_from-in-normal-domain.patch
-Patch0150: 0150-VALIDATOR-prevent-duplicite-report-from-subdomain-se.patch
-Patch0151: 0151-test_config_check-Fix-few-issues.patch
-Patch0152: 0152-KRB5-Fix-access_provider-krb5.patch
-Patch0153: 0153-BUILD-Improve-error-messages-for-optional-dependenci.patch
-Patch0154: 0154-RESPONDER_COMMON-update-certmaps-in-responders.patch
-Patch0155: 0155-tests-fix-test_pam_preauth_cert_no_logon_name.patch
-Patch0156: 0156-pam_sss-add-support-for-SSS_PAM_CERT_INFO_WITH_HINT.patch
-Patch0157: 0157-add_pam_cert_response-add-support-for-SSS_PAM_CERT_I.patch
-Patch0158: 0158-PAM-send-user-name-hint-response-when-needed.patch
-Patch0159: 0159-sysdb-sysdb_get_certmap-allow-empty-certmap.patch
-Patch0160: 0160-sssctl-show-user-name-used-for-authentication-in-use.patch
-Patch0161: 0161-RESP-Provide-a-reusable-request-to-fully-resolve-inc.patch
-Patch0162: 0162-IFP-Only-format-the-output-name-to-the-short-version.patch
-Patch0163: 0163-IFP-Resolve-group-names-from-GIDs-if-required.patch
-Patch0164: 0164-ldap-handle-certmap-errors-gracefully.patch
-Patch0165: 0165-SECRETS-Fix-warning-Wpointer-bool-conversion.patch
-Patch0166: 0166-IPA-Fix-the-PAM-error-code-that-auth-code-expects-to.patch
-Patch0167: 0167-pam_sss-Fix-checking-of-empty-string-cert_user.patch
-Patch0168: 0168-CACHE_REQ-Simplify-_search_ncache_filter.patch
-Patch0169: 0169-CACHE_REQ_SEARCH-Check-for-filtered-users-groups-als.patch
-Patch0170: 0170-cache_req-Do-not-use-default_domain_suffix-with-netg.patch
-Patch0171: 0171-krb5-disable-enterprise-principals-during-password-c.patch
-Patch0172: 0172-pam_sss-Fix-leaking-of-memory-in-case-of-failures.patch
-Patch0173: 0173-IFP-Add-domain-and-domainname-attributes-to-the-user.patch
-Patch0174: 0174-IFP-Fix-error-handling-in-ifp_user_get_attr_handle_r.patch
-Patch0175: 0175-SYSDB-Return-ERR_NO_TS-when-there-s-no-timestamp-cac.patch
-Patch0176: 0176-SYSDB-Internally-expose-sysdb_search_ts_matches.patch
-Patch0177: 0177-SYSDB-Make-the-usage-of-the-filter-more-generic-for-.patch
-Patch0178: 0178-SYSDB_OPS-Mark-an-entry-as-expired-also-in-the-times.patch
-Patch0179: 0179-SYSDB_OPS-Invalidate-a-cache-entry-also-in-the-ts_ca.patch
-Patch0180: 0180-SYSDB-Introduce-_search_-users-groups-_by_timestamp.patch
-Patch0181: 0181-LDAP_ID_CLEANUP-Use-sysdb_search_-_by_timestamp.patch
-Patch0182: 0182-krb5-use-plain-principal-if-password-is-expired.patch
-Patch0183: 0183-RESPONDER-Use-fqnames-as-output-when-needed.patch
-Patch0184: 0184-DOMAIN-Add-sss_domain_info_-get-set-_output_fqnames.patch
-Patch0185: 0185-GPO-Fix-typo-in-DEBUG-message.patch
-Patch0186: 0186-SDAP-Update-parent-sdap_list.patch
-Patch0187: 0187-RESPONDERS-Fix-terminating-idle-connections.patch
-Patch0188: 0188-TESTS-Integration-test-for-idle-timeout.patch
-Patch0189: 0189-MAN-Document-that-client_idle_timeout-can-t-be-short.patch
-Patch0190: 0190-ad_account_can_shortcut-shortcut-if-ID-is-unknown.patch
-Patch0191: 0191-sudo-add-a-threshold-option-to-reduce-size-of-rules-.patch
-Patch0192: 0192-libwbclient-Change-return-code-for-wbcAuthenticateUs.patch
-Patch0193: 0193-IPA-fix-handling-of-certmap_ctx.patch
-Patch0194: 0194-certmap-make-sure-eku_oid_list-is-always-allocated.patch
-Patch0195: 0195-cache_req-Look-for-name-attribute-also-in-nss_cmd_ge.patch
-Patch0196: 0196-sssd_client-add-mutex-protected-call-to-the-PAC-resp.patch
-Patch0197: 0197-sysdb-sanitize-search-filter-input.patch
-Patch0198: 0198-ipa-make-sure-view-name-is-initialized-at-startup.patch
-Patch0199: 0199-CACHE_REQ-Copy-the-cr_domain-list-for-each-request.patch
-Patch0200: 0200-cache_req-Correction-of-cache_req-debug-string-ID-fo.patch
-Patch0201: 0201-cache-Check-for-max_id-min_id-in-cache_req.patch
-Patch0202: 0202-sudo-always-use-srv_opts-from-id-context.patch
-Patch0203: 0203-SELINUX-Use-getseuserbyname-to-get-IPA-seuser.patch
-Patch0204: 0204-SELINUX-Check-if-SELinux-is-managed-in-selinux_child.patch
-Patch0205: 0205-util-Add-sss_-prefix-to-some-functions.patch
+Patch0001: 0001-NSS-Move-memcache-setup-to-separate-function.patch
+Patch0002: 0002-NSS-Specify-memcache_timeout-0-semantics.patch
+Patch0003: 0003-MAN-Document-memcache_timeout-0-meaning.patch
+Patch0004: 0004-CONFIG-Add-a-new-option-auto_private_groups.patch
+Patch0005: 0005-CONFDB-Remove-the-obsolete-option-magic_private_grou.patch
+Patch0006: 0006-SDAP-Allow-the-mpg-flag-for-the-main-domain.patch
+Patch0007: 0007-LDAP-Turn-group-request-into-user-request-for-MPG-do.patch
+Patch0008: 0008-SYSDB-Prevent-users-and-groups-ID-collision-in-MPG-d.patch
+Patch0009: 0009-TESTS-Add-integration-tests-for-the-auto_private_gro.patch
+Patch0010: 0010-CACHE_REQ-Copy-the-cr_domain-list-for-each-request.patch
+Patch0011: 0011-MAN-GPO-Security-Filtering-limitation.patch
+Patch0012: 0012-sudo-always-use-srv_opts-from-id-context.patch
+Patch0013: 0013-AD-Remember-last-site-discovered.patch
+Patch0014: 0014-sysdb-add-functions-to-get-set-client-site.patch
+Patch0015: 0015-AD-Remember-last-site-discovered-in-sysdb.patch
+Patch0016: 0016-UTIL-Add-wrapper-function-to-configure-logger.patch
+Patch0017: 0017-Add-parameter-logger-to-daemons.patch
+Patch0018: 0018-SYSTEMD-Replace-parameter-debug-to-files-with-DEBUG_.patch
+Patch0019: 0019-SYSTEMD-Add-environment-file-to-responder-service-fi.patch
+Patch0020: 0020-UTIL-Hide-and-deprecate-parameter-debug-to-files.patch
+Patch0021: 0021-LDAP-Bind-to-the-LDAP-server-also-in-the-auth.patch
+Patch0022: 0022-sss_client-create-nss_common.h.patch
+Patch0023: 0023-nss-idmap-add-nss-like-calls-with-timeout-and-flags.patch
+Patch0024: 0024-NSS-add-_EX-version-of-some-requests.patch
+Patch0025: 0025-NSS-add-support-for-SSS_NSS_EX_FLAG_NO_CACHE.patch
+Patch0026: 0026-CACHE_REQ-Add-cache_req_data_set_bypass_dp.patch
+Patch0027: 0027-nss-make-memcache_delete_entry-public.patch
+Patch0028: 0028-NSS-add-support-for-SSS_NSS_EX_FLAG_INVALIDATE_CACHE.patch
+Patch0029: 0029-NSS-TESTS-add-unit-tests-for-_EX-requests.patch
+Patch0030: 0030-nss-idmap-add-timeout-version-of-old-sss_nss_-calls.patch
+Patch0031: 0031-nss-idmap-allow-empty-buffer-with-SSS_NSS_EX_FLAG_IN.patch
+Patch0032: 0032-BUILD-Properly-expand-variables-in-sssd-ifp.service.patch
+Patch0033: 0033-SYSTEMD-Clean-pid-file-in-corner-cases.patch
+Patch0034: 0034-CHILD-Pass-information-about-logger-to-children.patch
+Patch0035: 0035-LDAP-Improve-error-treatment-from-sdap_cli_connect-i.patch
+Patch0036: 0036-p11_child-return-multiple-certs.patch
+Patch0037: 0037-PAM-handled-multiple-certs-in-the-responder.patch
+Patch0038: 0038-pam_sss-refactoring-use-struct-cert_auth_info.patch
+Patch0039: 0039-p11_child-use-options-to-select-certificate-for-auth.patch
+Patch0040: 0040-pam-add-prompt-string-for-certificate-authentication.patch
+Patch0041: 0041-PAM-allow-missing-logon_name-during-certificate-auth.patch
+Patch0042: 0042-p11_child-add-descriptions-for-error-codes-to-debug-.patch
+Patch0043: 0043-pam-filter-certificates-in-the-responder-not-in-the-.patch
+Patch0044: 0044-PAM-add-certificate-s-label-to-the-selection-prompt.patch
+Patch0045: 0045-SYSDB-Remove-code-causing-a-covscan-warning.patch
+Patch0046: 0046-SYSDB-Better-debugging-for-email-conflicts.patch
+Patch0047: 0047-NSS-Use-enum_ctx-as-memory_context-in-_setnetgrent_s.patch
+Patch0048: 0048-TOOLS-Add-a-new-sssctl-command-access-report.patch
+Patch0049: 0049-dp-use-void-to-express-empty-output-argument-list.patch
+Patch0050: 0050-dp-add-method-to-refresh-access-control-rules.patch
+Patch0051: 0051-ipa-implement-method-to-refresh-HBAC-rules.patch
+Patch0052: 0052-ifp-add-method-to-refresh-access-control-rules-in-do.patch
+Patch0053: 0053-sssctl-call-dbus-instead-of-pam-to-refresh-HBAC-rule.patch
+Patch0054: 0054-sysdb-be_refresh_get_values_ex-remove-unused-option.patch
+Patch0055: 0055-sysdb-do-not-use-objectClass-for-users-and-groups.patch
+Patch0056: 0056-sysdb-do-not-use-LDB_SCOPE_ONELEVEL.patch
+Patch0057: 0057-sysdb-remove-IDXONE-and-objectClass-from-users-and-g.patch
+Patch0058: 0058-mmap_cache-make-checks-independent-of-input-size.patch
+Patch0059: 0059-NSS-Fix-covscan-warning.patch
+Patch0060: 0060-responder-Fix-talloc-hierarchy-in-sized_output_name.patch
+Patch0061: 0061-test_responder-Check-memory-leak-in-sized_output_nam.patch
+Patch0062: 0062-UTIL-add-find_domain_by_object_name_ex.patch
+Patch0063: 0063-ipa-handle-users-from-different-domains-in-ipa_resol.patch
+Patch0064: 0064-overrides-fixes-for-sysdb_invalidate_overrides.patch
+Patch0065: 0065-ipa-check-for-SYSDB_OVERRIDE_DN-in-process_members-a.patch
+Patch0066: 0066-IPA-use-cache-searches-in-get_groups_dns.patch
+Patch0067: 0067-ipa-compare-DNs-instead-of-group-names-in-ipa_s2n_sa.patch
+Patch0068: 0068-SDAP-Split-out-utility-function-sdap_get_object_doma.patch
+Patch0069: 0069-LDAP-Extract-the-check-whether-to-run-a-POSIX-check-.patch
+Patch0070: 0070-LDAP-Only-run-the-POSIX-check-with-a-GC-connection.patch
+Patch0071: 0071-SDAP-Search-with-a-NULL-search-base-when-looking-up-.patch
+Patch0072: 0072-SDAP-Rename-sdap_posix_check-to-sdap_gc_posix_check.patch
+Patch0073: 0073-DP-Create-a-new-handler-function-getAccountDomain.patch
+Patch0074: 0074-AD-Implement-a-real-getAccountDomain-handler-for-the.patch
+Patch0075: 0075-RESP-Expose-DP-method-getAccountDomain-to-responders.patch
+Patch0076: 0076-NEGCACHE-Add-API-for-setting-and-checking-locate-acc.patch
+Patch0077: 0077-TESTS-Add-tests-for-the-object-by-id-cache_req-inter.patch
+Patch0078: 0078-CACHE_REQ-Export-cache_req_search_ncache_add-as-cach.patch
+Patch0079: 0079-CACHE_REQ-Add-plugin-methods-required-for-the-domain.patch
+Patch0080: 0080-CACHE_REQ-Add-a-private-request-cache_req_locate_dom.patch
+Patch0081: 0081-CACHE_REQ-Implement-the-plugin-methods-that-utilize-.patch
+Patch0082: 0082-CACHE_REQ-Use-the-domain-locator-request-to-only-sea.patch
+Patch0083: 0083-MAN-Document-how-the-Global-Catalog-is-used-currentl.patch
+Patch0084: 0084-p11_child-make-sure-OCSP-checks-are-done.patch
+Patch0085: 0085-IPA-Include-SYSDB_OBJECTCATEGORY-not-OBJECTCLASS-in-.patch
+Patch0086: 0086-nss-idmap-allow-NULL-result-in-_timeout-calls.patch
+Patch0087: 0087-cache-Check-for-max_id-min_id-in-cache_req.patch
+Patch0088: 0088-Revert-p11_child-make-sure-OCSP-checks-are-done.patch
+Patch0089: 0089-p11_child-properly-check-results-of-CERT_VerifyCerti.patch
+Patch0090: 0090-ifp-use-realloc-in-ifp_list_ctx_remaining_capacity.patch
+Patch0091: 0091-IPA-Delay-the-first-periodic-refresh-of-trusted-doma.patch
+Patch0092: 0092-sysdb-add-userMappedCertificate-to-the-index.patch
+Patch0093: 0093-AD-Inherit-the-MPG-setting-from-the-main-domain.patch
+Patch0094: 0094-SDAP-skip-builtin-AD-groups-in-sdap_save_grpmem.patch
+Patch0095: 0095-SYSDB-Read-the-ldb_message-from-loop-s-index-counter.patch
+Patch0096: 0096-nss-idmap-check-timed-muted-return-code.patch
+Patch0097: 0097-DESKPROFILE-Add-checks-for-user-and-host-category.patch
+Patch0098: 0098-SELINUX-Check-if-SELinux-is-managed-in-selinux_child.patch
+Patch0099: 0099-util-Add-sss_-prefix-to-some-functions.patch
+Patch0100: 0100-MAN-Explain-how-does-auto_private_groups-affect-subd.patch
+Patch0101: 0101-AD-Use-the-right-sdap_domain-for-the-forest-root.patch
+Patch0102: 0102-AD-sdap_get_ad_tokengroups_done-allocate-temporary-d.patch
+Patch0103: 0103-AD-do-not-allocate-temporary-data-on-long-living-con.patch
 
 #This patch should not be removed in RHEL-7
 Patch999: 0999-NOUPSTREAM-Default-to-root-if-sssd-user-is-not-spec
@@ -277,6 +177,7 @@ Requires: python-sssdconfig = %{version}-%{release}
 %global pubconfpath %{sssdstatedir}/pubconf
 %global gpocachepath %{sssdstatedir}/gpo_cache
 %global secdbpath %{sssdstatedir}/secrets
+%global deskprofilepath %{sssdstatedir}/deskprofile
 
 ### Build Dependencies ###
 
@@ -332,6 +233,7 @@ BuildRequires: jansson-devel
 BuildRequires: http-parser-devel
 BuildRequires: curl-devel
 BuildRequires: libuuid-devel
+BuildRequires: pkgconfig(gdm-pam-extensions)
 
 %description
 Provides a set of daemons to manage access to remote directories and
@@ -350,9 +252,6 @@ License: GPLv3+
 # Conflicts
 Conflicts: selinux-policy < 3.10.0-46
 Conflicts: sssd < 1.10.0-8%{?dist}.beta2
-# Due to ABI changes in rhel-7,5 (1.1.30/1.2.0)
-# rhel-7.4 <= will never have libldb 1.2.0 due to samba-4.6.x
-Conflicts: libldb >= 1.1.30
 # Requires
 Requires: sssd-client%{?_isa} = %{version}-%{release}
 Requires: libsss_idmap%{?_isa} = %{version}-%{release}
@@ -749,6 +648,9 @@ for p in %patches ; do
     UpdateTimestamps -p1 $p
 done
 
+cp %{SOURCE1} src/tests/cmocka/p11_nssdb_2certs/
+cp %{SOURCE2} src/tests/cmocka/p11_nssdb_2certs/
+
 %build
 autoreconf -ivf
 
@@ -968,6 +870,7 @@ done
 %attr(700,sssd,sssd) %dir %{dbpath}
 %attr(755,sssd,sssd) %dir %{mcpath}
 %attr(700,root,root) %dir %{secdbpath}
+%attr(755,root,root) %dir %{deskprofilepath}
 %ghost %attr(0644,sssd,sssd) %verify(not md5 size mtime) %{mcpath}/passwd
 %ghost %attr(0644,sssd,sssd) %verify(not md5 size mtime) %{mcpath}/group
 %ghost %attr(0644,sssd,sssd) %verify(not md5 size mtime) %{mcpath}/initgroups
@@ -979,8 +882,6 @@ done
 %attr(711,sssd,sssd) %dir %{_sysconfdir}/sssd
 %attr(711,sssd,sssd) %dir %{_sysconfdir}/sssd/conf.d
 %ghost %attr(0600,sssd,sssd) %config(noreplace) %{_sysconfdir}/sssd/sssd.conf
-%attr(755,root,root) %dir %{_sysconfdir}/systemd/system/sssd.service.d
-%config(noreplace) %{_sysconfdir}/systemd/system/sssd.service.d/journal.conf
 %dir %{_sysconfdir}/logrotate.d
 %config(noreplace) %{_sysconfdir}/logrotate.d/sssd
 %dir %{_sysconfdir}/rwtab.d
@@ -998,6 +899,7 @@ done
 %{_mandir}/man5/sssd-files.5*
 %{_mandir}/man5/sssd-simple.5*
 %{_mandir}/man5/sssd-sudo.5*
+%{_mandir}/man5/sssd-session-recording.5*
 %{_mandir}/man5/sssd-secrets.5*
 %{_mandir}/man5/sss_rpcidmapd.5*
 %{_mandir}/man8/sssd.8*
@@ -1006,10 +908,12 @@ done
 %dir %{_datadir}/sssd/systemtap
 %{_datadir}/sssd/systemtap/id_perf.stp
 %{_datadir}/sssd/systemtap/nested_group_perf.stp
+%{_datadir}/sssd/systemtap/dp_request.stp
 %dir %{_datadir}/systemtap
 %dir %{_datadir}/systemtap/tapset
 %{_datadir}/systemtap/tapset/sssd.stp
 %{_datadir}/systemtap/tapset/sssd_functions.stp
+%{_mandir}/man5/sssd-systemtap.5*
 %endif
 
 %if (0%{?install_pcscd_polkit_rule} == 1)
@@ -1242,8 +1146,8 @@ getent passwd sssd >/dev/null || useradd -r -g sssd -d / -s /sbin/nologin -c "Us
 %if (0%{?with_kcm} == 1)
 %files kcm -f sssd_kcm.lang
 %{_libexecdir}/%{servicename}/sssd_kcm
-%dir %{_sysconfdir}/krb5.conf.d
-%config(noreplace) %{_sysconfdir}/krb5.conf.d/kcm_default_ccache
+%dir %{_datadir}/sssd-kcm
+%{_datadir}/sssd-kcm/kcm_default_ccache
 %{_unitdir}/sssd-kcm.socket
 %{_unitdir}/sssd-kcm.service
 %{_mandir}/man8/sssd-kcm.8*
@@ -1393,58 +1297,194 @@ fi
 }
 
 %changelog
-* Tue Feb 13 2018 Fabiano Fidêncio <fidencio@redhat.com> - 1.15.2-50-11
-- Resolves: rhbz#1516700 - SELINUX: Use getseuserbyname to get IPA
-                           seuser [rhel-7.4.z]
-
-* Sun Jan 07 2018 Fabiano Fidêncio <fidencio@redhat.com> - 1.15.2-50-10
-- Resolves: rhbz#1530975 - sssd_be stuck in an infinite loop after
-                           completing full refresh of sudo rules
-                           [rhel-7.4.z]
-
-* Fri Jan 05 2018 Fabiano Fidêncio <fidencio@redhat.com> - 1.15.2-50.9
-- Resolves: rhbz#1525110 - NSS by-id requests are not checked against
+* Wed Feb 21 2018 Fabiano Fidêncio <fidencio@redhat.com> - 1.16.0-19
+- Related: rhbzrhbz#1544943 - sssd goes offline when renewing expired ticket
+
+* Wed Feb 21 2018 Fabiano Fidêncio <fidencio@redhat.com> - 1.16.0-18
+- Resolves: rhbz#1543348 - sssd_be consumes more memory on RHEL 7.4 systems.
+- Resolves: rhbz#1544943 - sssd goes offline when renewing expired ticket
+
+* Mon Feb 19 2018 Fabiano Fidêncio <fidencio@redhat.com> - 1.16.0-17
+- Resolves: rhbz#1523282 - sssd used wrong search base with wrong AD
+                           server
+
+* Tue Feb  6 2018 Fabiano Fidêncio <fidencio@redhat.com> - 1.16.0-16
+- Resolves: rhbz#1538643 - SSSD crashes when retrieving a Desktop Profile
+                           with no specific host/hostgroup set
+- Related: rhbz#1441908 - SELINUX: Use getseuserbyname to get IPA seuser
+- Related: rhbz#1327705 - [RFE] Automatic creation of user private groups
+                          on RHEL clients joined to AD via sssd [RHEL 7]
+
+* Wed Jan 24 2018 Fabiano Fidêncio <fidencio@redhat.com> - 1.16.0-15
+- Resolves: rhbz#1517971 - AD Domain goes offline immediately during
+                           subdomain initialization - IPA AD Trust
+- Related: rhbz#1482555 - sysdb index improvements - missing ghost
+                          attribute indexing, unneeded objectclass index
+                          etc..
+- Related: rhbz#1327705 - [RFE] Automatic creation of user private groups
+                          on RHEL clients joined to AD via sssd [RHEL 7]
+- Resolves: rhbz#1527149 - AD provider - AD BUILTIN groups are cached with
+                           gidNumber = 0
+- Related: rhbz#1461899 - Loading enterprise principals doesn't work with
+                          a primed cache
+- Related: rhbz#1473571 - ipa-extdom-extop plugin can exhaust DS worker
+                          threads
+
+* Fri Dec 15 2017 Fabiano Fidêncio <fidencio@redhat.com> - 1.16.0-14
+- Resolves: rhbz#1525644 - dbus-send unable to find user by CAC cert
+
+* Thu Dec 14 2017 Fabiano Fidêncio <fidencio@redhat.com> - 1.16.0-13
+- Resolves: rhbz#1523010 - IPA user able to authenticate with revoked cert
+                           on smart card
+
+* Mon Dec 11 2017 Fabiano Fidêncio <fidencio@redhat.com> - 1.16.0-12
+- Resolves: rhbz#1512027 - NSS by-id requests are not checked against
                            max_id/min_id ranges before triggering the
-                           backend [rhel-7.4.z]
-
-* Fri Nov 03 2017 Fabiano Fidêncio <fidencio@redhat.com> - 1.15.2-50.8
-- Resolves: rhbz#1508972 - Accessing IdM kerberos ticket fails while id
-                           mapping is applied [rhel-7.4.z]
-- Resolves: rhbz#1509177 - Race condition between refreshing the cr_domain
+                           backend
+
+* Fri Dec 08 2017 Fabiano Fidêncio <fidencio@redhat.com> - 1.16.0-11
+- Related: rhbz#1507614 - Improve Smartcard integration if multiple
+                          certificates or multiple mapped identities are
+                          available
+- Resolves: rhbz#1523010 - IPA user able to authenticate with revoked
+                           cert on smart card
+- Resolves: rhbz#1520984 - getent output is not showing home directory
+                           for IPA AD trusted user
+- Related: rhbz#1473571 - ipa-extdom-extop plugin can exhaust DS worker
+                          threads
+
+* Wed Dec 06 2017 Fabiano Fidêncio <fidencio@redhat.com> - 1.16.0-10
+- Resolves: rhbz#1421194 - SSSD doesn't use AD global catalog for
+                           gidnumber lookup, resulting in unacceptable
+                           delay for large forests
+
+* Fri Dec 01 2017 Fabiano Fidêncio <fidencio@redhat.com> - 1.16.0-9
+- Resolves: rhbz#1482231 - sssd_nss consumes more memory until
+                           restarted or machine swaps
+- Resolves: rhbz#1512508 - SSSD fails to fetch group information after
+                           switching IPA client to a non-default view
+
+* Thu Nov 30 2017 Fabiano Fidêncio <fidencio@redhat.com> - 1.16.0-8
+- Resolves: rhbz#1490120 - SSSD complaining about corrupted mmap cache
+                           and logging error in /var/log/messages and
+                           /var/log/sssd/sssd_nss.log
+
+* Mon Nov 27 2017 Fabiano Fidêncio <fidencio@redhat.com> - 1.16.0-7
+- Resolves: rhbz#1272214 - [RFE] Create a local per system report about
+                           who can access that IDM client (attestation)
+- Resolves: rhbz#1482555 - sysdb index improvements - missing ghost
+                           attribute indexing, unneeded objectclass index
+                           etc..
+- Resolves: rhbz#888739 -  Enumerating large number of users makes sssd_be
+                           hog the cpu for a long time.
+- Resolves: rhbz#1373547 - SSSD performance issue with malloc and brk
+                           calls
+- Resolves: rhbz#1472255 - Improve SSSD performance in the 7.5 release
+
+* Tue Nov 14 2017 Fabiano Fidêncio <fidencio@redhat.com> - 1.16.0-6
+- Related: rhbz#1460724 - SYSLOG_IDENTIFIER is different
+- Related: rhbz#1432010 - SSSD ships a drop-in configuration snippet in
+                          /etc/systemd/system
+- Related: rhbz#1507614 - Improve Smartcard integration if multiple
+                          certificates or multiple mapped identities are
+                          available
+
+* Mon Nov 13 2017 Fabiano Fidêncio <fidencio@redhat.com> - 1.16.0-5
+- Resolves: rhbz#1507614 - Improve Smartcard integration if multiple
+                           certificates or multiple mapped identities are
+                           available
+- Related: rhbz#1499659 - CVE-2017-12173 sssd: unsanitized input when
+                          searching in local cache database [rhel-7.5]
+- Resolves: rhbz#1408294 - SSSD authentication fails when two IPA
+                           accounts share an email address without a
+                           clear way to debug the problem
+- Resolves: rhbz#1502686 - crash - /usr/libexec/sssd/sssd_nss in
+                           nss_setnetgrent_timeout
+
+* Sun Nov 12 2017 Fabiano Fidêncio <fidencio@redhat.com> - 1.16.0-4
+- Related: rhbz#1460724 - SYSLOG_IDENTIFIER is different
+- Related: rhbz#1459609 - When sssd is configured with id_provider proxy
+                          and auth_provider ldap, login fails if the LDAP
+                          server is not allowing anonymous binds.
+
+* Mon Nov 06 2017 Fabiano Fidêncio <fidencio@redhat.com> - 1.16.0-3
+- Resolves: rhbz#1473571 - ipa-extdom-extop plugin can exhaust DS worker
+                           threads
+
+* Fri Nov 03 2017 Fabiano Fidêncio <fidencio@redhat.com> - 1.16.0-2
+- Resolves: rhbz#1484376 - [RFE] Add a configuration option to SSSD to
+                           disable the memory cache
+- Resolves: rhbz#1327705 - Automatic creation of user private groups on
+                           RHEL clients joined to AD via sssd [RHEL 7]
+- Resolves: rhbz#1505277 - Race condition between refreshing the cr_domain
                            list and a request that is using the list can
-                           cause a segfault is sssd_nss [rhel-7.4.z]
-
-* Fri Oct 27 2017 Fabiano Fidêncio <fidencio@redhat.com> - 1.15.2-50.7
-- Resolves: rhbz#1506142 - SSSD can crash due to ABI changes in
-                           libldb >= 1.2.0 (1.1.30) [rhel-7.4.z]
-- Resolves: rhbz#1506682 - sssd_client: add mutex protected call to the
-                           PAC responder [rhel-7.4.z]
-- Resolves: rhbz#1499658 - CVE-2017-12173 sssd: unsanitized input when
-                           searching in local cache database [rhel-7.4.z]
-
-* Wed Sep 27 2017 Fabiano Fidêncio <fidencio@redhat.com> - 1.15.2-50.6
-- Add a patch that was missed in 1.15.2-50.4
-- Related: rhbz#1489290 - samba shares with sssd authentication broken
-                          on 7.4 [rhel-7.4.z]
-
-* Thu Sep 21 2017 Fabiano Fidêncio <fidencio@redhat.com> - 1.15.2-50.5
-- Resolves: rhbz#1493916 - Issues with certificate mapping rules [rhel-7.4.z]
-
-* Mon Sep 11 2017 Jakub Hrozek <jhrozek@redhat.com> - 1.15.2-50.4
-- Resolves: rhbz#1489290 - samba shares with sssd authentication broken
-                           on 7.4 [rhel-7.4.z]
-
-* Fri Aug 18 2017 Fabiano Fidêncio <fidencio@redhat.com> - 1.15.2-50.3
-- Resolves: rhbz#1482927 - sssd_be is utilizing more CPU during sudoi
-                           rules refresh [rhel-7.4.z]
-
-* Sun Aug  6 2017 Jakub Hrozek <jhrozek@redhat.com> - 1.15.2-50.2
-- Resolves: rhbz#1478252 - Querying the AD domain for external domain's
-                           ID can mark the AD domain offline [rhel-7.4.z]
-
-* Sun Aug  6 2017 Jakub Hrozek <jhrozek@redhat.com> - 1.15.2-50.1
-- Resolves: rhbz#1478250 - Idle nss file descriptors should be closed
-                           [rhel-7.4.z]
+                           cause a segfault is sssd_nss
+- Resolves: rhbz#1462343 - document information on why SSSD does not use
+                           host-based security filtering when processing
+                           AD GPOs
+- Resolves: rhbz#1498734 - sssd_be stuck in an infinite loop after
+                           completing full refresh of sudo rules
+- Resolves: rhbz#1400614 - [RFE] sssd should remember DNS sites from
+                           first search
+- Resolves: rhbz#1460724 - SYSLOG_IDENTIFIER is different
+- Resolves: rhbz#1459609 - When sssd is configured with id_provider proxy
+                           and auth_provider ldap, login fails if the LDAP
+                           server is not allowing anonymous binds.
+
+* Fri Oct 20 2017 Fabiano Fidêncio <fidencio@redhat.com> - 1.16.0-1
+- Resolves: rhbz#1469791 - Rebase SSSD to version 1.16+
+- Resolves: rhbz#1132264 - Allow sssd to retrieve sudo rules of local
+                           users whose sudo rules stored in ldap server
+- Resolves: rhbz#1301740 - sssd can be marked offline if a trusted domain
+                           is not reachable
+- Resolves: rhbz#1399262 - Use TCP for kerberos with AD by default
+- Resolves: rhbz#1416150 - RFE: Log to syslog when sssd cannot contact
+                           servers, goes offline
+- Resolves: rhbz#1441908 - SELINUX: Use getseuserbyname to get IPA seuser
+- Resolves: rhbz#1454559 - python-sssdconfig doesn't parse hexadecimal debug
+                           _level, resulting in set_option():
+                           /usr/lib/python2.7/site-packages/SSSDConfig/__init__.py
+                           killed by TypeError
+- Resolves: rhbz#1456968 - MAN: document that attribute 'provider' is not
+                           allowed in section 'secrets'
+- Resolves: rhbz#1460689 - KCM/secrets: Storing many secrets in a rapid
+                           succession segfaults the secrets responder
+- Resolves: rhbz#1464049 - Idle nss file descriptors should be closed
+- Resolves: rhbz#1468610 - sssd_be is utilizing more CPU during sudo rules
+                           refresh
+- Resolves: rhbz#1474711 - Querying the AD domain for external domain's ID can
+                           mark the AD domain offline
+- Resolves: rhbz#1479398 - samba shares with sssd authentication broken on 7.4
+- Resolves: rhbz#1479983 - id root triggers an LDAP lookup
+- Resolves: rhbz#1489895 - Issues with certificate mapping rules
+- Resolves: rhbz#1490501 - sssd incorrectly checks 'try_inotify' thinking it is
+                           the wrong section
+- Resolves: rhbz#1490913 - MAN: Document that full_name_format must be set if
+                           the output of trusted domains user resolution should
+                           be shortnames only
+- Resolves: rhbz#1499659 - CVE-2017-12173 sssd: unsanitized input when
+                           searching in local cache database [rhel-7.5]
+- Resolves: rhbz#1461899 - Loading enterprise principals doesn't work with a
+                           primed cache
+- Resolves: rhbz#1482674 - SUDO doesn't work for IPA users on IPA clients after
+                           applying ID Views for them in IPA server
+- Resolves: rhbz#1486053 - Accessing IdM kerberos ticket fails while id mapping
+                           is applied
+- Resolves: rhbz#1486786 - sssd going in offline mode due to sudo search filter.
+- Resolves: rhbz#1500087 - SSSD creates bad override search filter due to AD
+                           Trust object with parenthesis
+- Resolves: rhbz#1502713 - SSSD can crash due to ABI changes in libldb >= 1.2.0
+                           (1.1.30)
+- Resolves: rhbz#1461462 - sssd_client: add mutex protected call to the PAC
+                           responder
+- Resolves: rhbz#1489666 - Combination sssd-ad and postfix recieve incorrect
+                           mail with asterisks or spaces
+- Resolves: rhbz#1525052 - sssd_krb5_localauth_plugin fails to fallback to otheri
+                           localname rules
+
+* Tue Oct 17 2017 Jakub Hrozek <jhrozek@redhat.com> - 1.15.2-51
+- Require the 7.5 libldb version which broke ABI
+- Related: rhbz#1469791 - Rebase SSSD to version 1.16+
 
 * Wed Jun 21 2017 Jakub Hrozek <jhrozek@redhat.com> - 1.15.2-50
 - Resolves: rhbz#1457926 - Wrong search base used when SSSD is directly