diff --git a/.gitignore b/.gitignore
index f71df95..bae0ba3 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1 @@
-SOURCES/sssd-1.14.0.tar.gz
+SOURCES/sssd-1.15.2.tar.gz
diff --git a/.sssd.metadata b/.sssd.metadata
index 26fd024..bc1073e 100644
--- a/.sssd.metadata
+++ b/.sssd.metadata
@@ -1 +1 @@
-4ddb4403b07125f413181c43d8d2208c3128924d SOURCES/sssd-1.14.0.tar.gz
+f94298ac05169cdf5a9082c3aba9f6a18513720a SOURCES/sssd-1.15.2.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
new file mode 100644
index 0000000..e405016
--- /dev/null
+++ b/SOURCES/0001-MAN-Mention-sssd-secrets-in-SEE-ALSO-section.patch
@@ -0,0 +1,36 @@
+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-SYSDB-Fixing-DB-update.patch b/SOURCES/0001-SYSDB-Fixing-DB-update.patch
deleted file mode 100644
index f4df02a..0000000
--- a/SOURCES/0001-SYSDB-Fixing-DB-update.patch
+++ /dev/null
@@ -1,71 +0,0 @@
-From dc8e4131f73d62f23a4be7148e24315adbc550e4 Mon Sep 17 00:00:00 2001
-From: Petr Cech <pcech@redhat.com>
-Date: Mon, 20 Jun 2016 09:19:03 -0300
-Subject: [PATCH 01/18] SYSDB: Fixing DB update
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Functions sysdb_user_base_dn() and sysdb_group_base_dn() expect
-that struct sss_domain_info contains pointer to struct sysdb_ctx.
-This is not true in case of sysdb_upgrade functions.
-This patch fixes the situation and revert code to the state before
-12a000c8c7c07259e438fb1e992134bdd07d9a30 commit.
-
-Resolves:
-https://fedorahosted.org/sssd/ticket/3023
-
-Reviewed-by: Lukáš Slebodník <lslebodn@redhat.com>
-Reviewed-by: Sumit Bose <sbose@redhat.com>
-(cherry picked from commit 311836214245600566f881ff6253594e0999008e)
----
- src/db/sysdb_upgrade.c | 22 +++++++++++++++++++---
- 1 file changed, 19 insertions(+), 3 deletions(-)
-
-diff --git a/src/db/sysdb_upgrade.c b/src/db/sysdb_upgrade.c
-index 1d2978caee4512856fb64c4798a4b031bf6a3af1..4ca8433f9d5430b038f90563c34cede02393b0b0 100644
---- a/src/db/sysdb_upgrade.c
-+++ b/src/db/sysdb_upgrade.c
-@@ -443,12 +443,23 @@ int sysdb_check_upgrade_02(struct sss_domain_info *domains,
-             goto done;
-         }
- 
--        users_dn = sysdb_user_base_dn(tmp_ctx, dom);
-+        /*
-+         * dom->sysdb->ldb is not initialized,
-+         * so ldb_dn_new_fmt() shouldn't be changed to sysdb_*_base_dn()
-+         */
-+        users_dn = ldb_dn_new_fmt(tmp_ctx, sysdb->ldb,
-+                                  SYSDB_TMPL_USER_BASE, dom->name);
-         if (!users_dn) {
-             ret = ENOMEM;
-             goto done;
-         }
--        groups_dn = sysdb_group_base_dn(tmp_ctx, dom);
-+
-+        /*
-+         * dom->sysdb->ldb is not initialized,
-+         * so ldb_dn_new_fmt() shouldn't be changed to sysdb_*_base_dn()
-+         */
-+        groups_dn = ldb_dn_new_fmt(tmp_ctx, sysdb->ldb,
-+                                   SYSDB_TMPL_GROUP_BASE, dom->name);
-         if (!groups_dn) {
-             ret = ENOMEM;
-             goto done;
-@@ -1045,7 +1056,12 @@ int sysdb_upgrade_10(struct sysdb_ctx *sysdb, struct sss_domain_info *domain,
-         return ret;
-     }
- 
--    basedn = sysdb_user_base_dn(tmp_ctx, domain);
-+    /*
-+     * dom->sysdb->ldb is not initialized,
-+     * so ldb_dn_new_fmt() shouldn't be changed to sysdb_*_base_dn()
-+     */
-+    basedn = ldb_dn_new_fmt(tmp_ctx, sysdb->ldb,
-+                            SYSDB_TMPL_USER_BASE, domain->name);
-     if (basedn == NULL) {
-         ret = EIO;
-         goto done;
--- 
-2.4.11
-
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
new file mode 100644
index 0000000..24961c7
--- /dev/null
+++ b/SOURCES/0002-split_on_separator-move-to-a-separate-file.patch
@@ -0,0 +1,369 @@
+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/0002-sssctl-Fix-error-handling-after-memory-allocation-fa.patch b/SOURCES/0002-sssctl-Fix-error-handling-after-memory-allocation-fa.patch
deleted file mode 100644
index 4590e63..0000000
--- a/SOURCES/0002-sssctl-Fix-error-handling-after-memory-allocation-fa.patch
+++ /dev/null
@@ -1,33 +0,0 @@
-From 36adb8fdc1e0ec14d394a2c51be16c90f4fa1acd Mon Sep 17 00:00:00 2001
-From: Lukas Slebodnik <lslebodn@redhat.com>
-Date: Fri, 8 Jul 2016 12:16:47 +0200
-Subject: [PATCH 02/18] sssctl: Fix error handling after memory allocation
- failure
-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 4b18d0c25471150940c1a552bc2504ff9debb703)
----
- src/tools/sssctl/sssctl_cache.c | 3 ++-
- 1 file changed, 2 insertions(+), 1 deletion(-)
-
-diff --git a/src/tools/sssctl/sssctl_cache.c b/src/tools/sssctl/sssctl_cache.c
-index 9f626d983a4672cf00fba6b3171b822e8f6e02bd..28de6c139d844f98f9b06844492c935696e19643 100644
---- a/src/tools/sssctl/sssctl_cache.c
-+++ b/src/tools/sssctl/sssctl_cache.c
-@@ -364,8 +364,9 @@ static errno_t sssctl_find_object(TALLOC_CTX *mem_ctx,
-         filter = talloc_asprintf(tmp_ctx, "(&(objectClass=%s)(%s=%s))",
-                                  class, attr_name, filter_value);
-         talloc_free(filter_value);
--        if (filter_value == NULL) {
-+        if (filter == NULL) {
-             DEBUG(SSSDBG_CRIT_FAILURE, "talloc_asprintf() failed\n");
-+            ret = ENOMEM;
-             goto done;
-         }
- 
--- 
-2.4.11
-
diff --git a/SOURCES/0003-sssctl-config-check-access-check-report.patch b/SOURCES/0003-sssctl-config-check-access-check-report.patch
deleted file mode 100644
index 3a564f8..0000000
--- a/SOURCES/0003-sssctl-config-check-access-check-report.patch
+++ /dev/null
@@ -1,34 +0,0 @@
-From 0df858e26420bc6fb49819572694fced6791d414 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Michal=20=C5=BDidek?= <mzidek@redhat.com>
-Date: Thu, 7 Jul 2016 15:43:11 +0200
-Subject: [PATCH 03/18] sssctl: config-check access check report
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Improve output when access check error
-is detected by sssctl config-check command.
-
-Reviewed-by: Pavel Březina <pbrezina@redhat.com>
-(cherry picked from commit 9dc66cb6b96a885f7272a3c4aa6a44d60cdce82c)
----
- src/tools/sssctl/sssctl_config.c | 3 ++-
- 1 file changed, 2 insertions(+), 1 deletion(-)
-
-diff --git a/src/tools/sssctl/sssctl_config.c b/src/tools/sssctl/sssctl_config.c
-index fc13582accd63f58c9d8bce59c4d6e898a96b170..4f6dbcdd7d04183c65b6613efbe5ab95df19e2c7 100644
---- a/src/tools/sssctl/sssctl_config.c
-+++ b/src/tools/sssctl/sssctl_config.c
-@@ -68,7 +68,8 @@ errno_t sssctl_config_check(struct sss_cmdline *cmdline,
-     /* Check the file permissions */
-     ret = sss_ini_config_access_check(init_data);
-     if (ret != EOK) {
--        printf(_("Access check on sssd.conf file failed.\n"));
-+        printf(_("File ownership and permissions check failed. "
-+               "Expected root:root and 0600.\n"));
-         ret = EPERM;
-         goto done;
-     }
--- 
-2.4.11
-
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
new file mode 100644
index 0000000..f21cb03
--- /dev/null
+++ b/SOURCES/0003-util-move-string_in_list-to-util_ext.patch
@@ -0,0 +1,91 @@
+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-FO-Set-port-to-NOT_WORKING-when-trying-a-next-server.patch b/SOURCES/0004-FO-Set-port-to-NOT_WORKING-when-trying-a-next-server.patch
deleted file mode 100644
index e7bf9c3..0000000
--- a/SOURCES/0004-FO-Set-port-to-NOT_WORKING-when-trying-a-next-server.patch
+++ /dev/null
@@ -1,32 +0,0 @@
-From 95939e9831fb3d9a8f464ecff3377dd6027e0321 Mon Sep 17 00:00:00 2001
-From: Jakub Hrozek <jhrozek@redhat.com>
-Date: Tue, 10 May 2016 14:11:36 +0200
-Subject: [PATCH 04/18] FO: Set port to NOT_WORKING when trying a next server
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Resolves: https://fedorahosted.org/sssd/ticket/3009
-
-Reviewed-by: Pavel Březina <pbrezina@redhat.com>
-(cherry picked from commit c420ce830ac0b0b288a2a887ec2cfce5c748018c)
----
- src/providers/fail_over.c | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
-diff --git a/src/providers/fail_over.c b/src/providers/fail_over.c
-index e945c9924597c7addeeb11090e1c1aee5596cb71..1d88d2aa54bfdebd4b648e2b13fa8d03e2be3973 100644
---- a/src/providers/fail_over.c
-+++ b/src/providers/fail_over.c
-@@ -1546,7 +1546,7 @@ void fo_try_next_server(struct fo_service *service)
-     service->active_server = 0;
- 
-     if (server->port_status == PORT_WORKING) {
--        server->port_status = PORT_NEUTRAL;
-+        server->port_status = PORT_NOT_WORKING;
-     }
- }
- 
--- 
-2.4.11
-
diff --git a/SOURCES/0004-certmap-add-new-library-libsss_certmap.patch b/SOURCES/0004-certmap-add-new-library-libsss_certmap.patch
new file mode 100644
index 0000000..1a04fe7
--- /dev/null
+++ b/SOURCES/0004-certmap-add-new-library-libsss_certmap.patch
@@ -0,0 +1,5787 @@
+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-certmap-add-placeholder-for-OpenSSL-implementation.patch b/SOURCES/0005-certmap-add-placeholder-for-OpenSSL-implementation.patch
new file mode 100644
index 0000000..99a2bd8
--- /dev/null
+++ b/SOURCES/0005-certmap-add-placeholder-for-OpenSSL-implementation.patch
@@ -0,0 +1,151 @@
+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/0005-sssctl-Fix-format-string-for-size_t.patch b/SOURCES/0005-sssctl-Fix-format-string-for-size_t.patch
deleted file mode 100644
index b2037a5..0000000
--- a/SOURCES/0005-sssctl-Fix-format-string-for-size_t.patch
+++ /dev/null
@@ -1,37 +0,0 @@
-From 8d82518980eae15d4644ce55058ed852f7f657f5 Mon Sep 17 00:00:00 2001
-From: Lukas Slebodnik <lslebodn@redhat.com>
-Date: Fri, 8 Jul 2016 13:04:10 +0200
-Subject: [PATCH 05/18] sssctl: Fix format string for size_t
-
-src/tools/sssctl/sssctl_config.c: In function 'sssctl_config_check':
-src/tools/sssctl/sssctl_config.c:93:14: warning: format '%lu' expects
-  argument of type 'long unsigned int', but argument 2 has type
-  'size_t {aka unsigned int}' [-Wformat=]
-     printf(_("Issues identified by validators: %lu\n"), num_errors);
-              ^
-src/tools/sssctl/sssctl_config.c:93:12: note: in expansion of macro '_'
-     printf(_("Issues identified by validators: %lu\n"), num_errors);
-            ^
-Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
-
-(cherry picked from commit cca5695e6cab64def52c009afc8f055a85f1fde4)
----
- src/tools/sssctl/sssctl_config.c | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
-diff --git a/src/tools/sssctl/sssctl_config.c b/src/tools/sssctl/sssctl_config.c
-index 4f6dbcdd7d04183c65b6613efbe5ab95df19e2c7..a66d7749c4aee9bd00c0ad2d296292658ffdb9b2 100644
---- a/src/tools/sssctl/sssctl_config.c
-+++ b/src/tools/sssctl/sssctl_config.c
-@@ -91,7 +91,7 @@ errno_t sssctl_config_check(struct sss_cmdline *cmdline,
-     }
- 
-     /* Output from validators */
--    printf(_("Issues identified by validators: %lu\n"), num_errors);
-+    printf(_("Issues identified by validators: %zu\n"), num_errors);
-     for (i = 0; i < num_errors; i++) {
-         printf("%s\n", strs[i]);
-     }
--- 
-2.4.11
-
diff --git a/SOURCES/0006-doxygen-Fix-path-to-header-file-ipa_hbac.h.patch b/SOURCES/0006-doxygen-Fix-path-to-header-file-ipa_hbac.h.patch
deleted file mode 100644
index b3e1f55..0000000
--- a/SOURCES/0006-doxygen-Fix-path-to-header-file-ipa_hbac.h.patch
+++ /dev/null
@@ -1,31 +0,0 @@
-From fda4c78ca651ce478fe744f7de87c2064e80a05d Mon Sep 17 00:00:00 2001
-From: Lukas Slebodnik <lslebodn@redhat.com>
-Date: Fri, 8 Jul 2016 13:27:47 +0200
-Subject: [PATCH 06/18] doxygen: Fix path to header file ipa_hbac.h
-
-Warning: tag INPUT: input source `src/providers/ipa/ipa_hbac.h' does not exist
-warning: source src/providers/ipa/ipa_hbac.h is not
-         a readable file or directory... skipping.
-
-Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
-(cherry picked from commit b9b2c0836f64f375babb75d92b924f3780f20521)
----
- src/lib/ipa_hbac/ipa_hbac.doxy.in | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
-diff --git a/src/lib/ipa_hbac/ipa_hbac.doxy.in b/src/lib/ipa_hbac/ipa_hbac.doxy.in
-index 6d4a92659f1974fa96b8fcfbcca0d4107e3a7dae..d1e9f995ddeffd92dad03b5d1fd18077b6aeb645 100644
---- a/src/lib/ipa_hbac/ipa_hbac.doxy.in
-+++ b/src/lib/ipa_hbac/ipa_hbac.doxy.in
-@@ -678,7 +678,7 @@ WARN_LOGFILE           =
- # directories like "/usr/src/myproject". Separate the files or directories
- # with spaces.
- 
--INPUT                  = @abs_top_srcdir@/src/providers/ipa/ipa_hbac.h
-+INPUT                  = @abs_top_srcdir@/src/lib/ipa_hbac/ipa_hbac.h
- 
- # This tag can be used to specify the character encoding of the source files
- # that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is
--- 
-2.4.11
-
diff --git a/SOURCES/0006-sysdb-add-sysdb_attrs_copy.patch b/SOURCES/0006-sysdb-add-sysdb_attrs_copy.patch
new file mode 100644
index 0000000..f41c5e7
--- /dev/null
+++ b/SOURCES/0006-sysdb-add-sysdb_attrs_copy.patch
@@ -0,0 +1,173 @@
+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-ipa_hbac-Fix-documentation-for-hbac_enable_debug.patch b/SOURCES/0007-ipa_hbac-Fix-documentation-for-hbac_enable_debug.patch
deleted file mode 100644
index 0308ac2..0000000
--- a/SOURCES/0007-ipa_hbac-Fix-documentation-for-hbac_enable_debug.patch
+++ /dev/null
@@ -1,31 +0,0 @@
-From 3743b49557f63d0ca541103fe0929e432cde3678 Mon Sep 17 00:00:00 2001
-From: Lukas Slebodnik <lslebodn@redhat.com>
-Date: Fri, 8 Jul 2016 14:39:43 +0200
-Subject: [PATCH 07/18] ipa_hbac: Fix documentation for hbac_enable_debug
-
-src/lib/ipa_hbac/ipa_hbac.h:68: warning: expected whitespace after [ command
-
-Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
-(cherry picked from commit f3db22862d22821be2d566435cb0c59387343fc2)
----
- src/lib/ipa_hbac/ipa_hbac.h | 4 ++--
- 1 file changed, 2 insertions(+), 2 deletions(-)
-
-diff --git a/src/lib/ipa_hbac/ipa_hbac.h b/src/lib/ipa_hbac/ipa_hbac.h
-index 8801c20c492caf53a089c2724c2ed4a96805904c..f9d339c058f109acdea95fa185182f08d1e8af9e 100644
---- a/src/lib/ipa_hbac/ipa_hbac.h
-+++ b/src/lib/ipa_hbac/ipa_hbac.h
-@@ -65,8 +65,8 @@ typedef void (*hbac_debug_fn_t)(const char *file, int line,
-                                 ...) HBAC_ATTRIBUTE_PRINTF(5, 6);
- 
- /**
-- *  HBAC uses external_debug_fn for logging messages.
-- *  @param[in|out] external_debug_void Pointer to external logging function.
-+ * HBAC uses external_debug_fn for logging messages.
-+ * @param[in] external_debug_fn Pointer to external logging function.
-  */
- void hbac_enable_debug(hbac_debug_fn_t external_debug_fn);
- 
--- 
-2.4.11
-
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
new file mode 100644
index 0000000..9292f78
--- /dev/null
+++ b/SOURCES/0007-sdap_get_users_send-new-argument-mapped_attrs.patch
@@ -0,0 +1,316 @@
+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
new file mode 100644
index 0000000..108254d
--- /dev/null
+++ b/SOURCES/0008-LDAP-always-store-the-certificate-from-the-request.patch
@@ -0,0 +1,178 @@
+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-sssctl-Fix-warning-maybe-uninitialized.patch b/SOURCES/0008-sssctl-Fix-warning-maybe-uninitialized.patch
deleted file mode 100644
index 77fde17..0000000
--- a/SOURCES/0008-sssctl-Fix-warning-maybe-uninitialized.patch
+++ /dev/null
@@ -1,63 +0,0 @@
-From e64aac5e33523f8473471b945403f0d9ba74c8cf Mon Sep 17 00:00:00 2001
-From: Lukas Slebodnik <lslebodn@redhat.com>
-Date: Fri, 8 Jul 2016 15:27:23 +0200
-Subject: [PATCH 08/18] sssctl: Fix warning maybe-uninitialized
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-It looks like some special gcc optimalisation and special case
-may cause to have unitialized output argument _dom when return
-code is EOK
-
-src/tools/sssctl/sssctl_cache.c: In function ‘sssctl_print_object’:
-src/tools/sssctl/sssctl_cache.c:491:8: error: ‘dom’ may be used
-  uninitialized in this function [-Werror=maybe-uninitialized]
-     if (dom == NULL) {
-        ^
-src/tools/sssctl/sssctl_cache.c:447:15: error: ‘entry’ may be used
-  uninitialized in this function [-Werror=maybe-uninitialized]
-     *_entry = talloc_steal(mem_ctx, entry);
-               ^~~~~~~~~~~~
-src/tools/sssctl/sssctl_cache.c:412:25: note: ‘entry’ was declared here
-     struct sysdb_attrs *entry;
-                         ^~~~~
-
-Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
-(cherry picked from commit 64d664c201916d8678b5f4bd7e1559c7ece9217d)
----
- src/tools/sssctl/sssctl_cache.c | 14 +++++++++-----
- 1 file changed, 9 insertions(+), 5 deletions(-)
-
-diff --git a/src/tools/sssctl/sssctl_cache.c b/src/tools/sssctl/sssctl_cache.c
-index 28de6c139d844f98f9b06844492c935696e19643..e23bb89db95217e66a441b7e4d6d32e668486cc8 100644
---- a/src/tools/sssctl/sssctl_cache.c
-+++ b/src/tools/sssctl/sssctl_cache.c
-@@ -372,15 +372,19 @@ static errno_t sssctl_find_object(TALLOC_CTX *mem_ctx,
- 
-         ret = sssctl_query_cache(tmp_ctx, dom->sysdb, base_dn, filter,
-                                  attrs, &entry);
--        if (ret == EOK) {
-+        switch(ret) {
-+        case EOK:
-             /* Entry was found. */
-             *_entry = talloc_steal(mem_ctx, entry);
-             *_dom = dom;
-             goto done;
--        } else if (ret == ENOENT && fqn_provided) {
--            /* Not found but a domain was provided in input. We're done. */
--            goto done;
--        } else if (ret != ENOENT) {
-+        case ENOENT:
-+            if (fqn_provided) {
-+                /* Not found but a domain was provided in input. We're done. */
-+                goto done;
-+            }
-+            break;
-+        default:
-             DEBUG(SSSDBG_CRIT_FAILURE, "Unable to query cache [%d]: %s\n",
-                   ret, sss_strerror(ret));
-             goto done;
--- 
-2.4.11
-
diff --git a/SOURCES/0009-NOUPSTREAM-Bundle-http-parser.patch b/SOURCES/0009-NOUPSTREAM-Bundle-http-parser.patch
deleted file mode 100644
index a17b34c..0000000
--- a/SOURCES/0009-NOUPSTREAM-Bundle-http-parser.patch
+++ /dev/null
@@ -1,2922 +0,0 @@
-From bd5daccde22322d216d450f40a50c1a3d7c5a32c Mon Sep 17 00:00:00 2001
-From: Jakub Hrozek <jhrozek@redhat.com>
-Date: Tue, 12 Jul 2016 13:36:32 +0200
-Subject: [PATCH 09/18] NOUPSTREAM: Bundle http-parser
-
----
- Makefile.am                            |   15 +
- src/external/libhttp_parser.m4         |    7 +-
- src/responder/secrets/http_parser.c    | 2469 ++++++++++++++++++++++++++++++++
- src/responder/secrets/http_parser.h    |  362 +++++
- src/responder/secrets/secsrv_private.h |    2 +-
- 5 files changed, 2850 insertions(+), 5 deletions(-)
- create mode 100644 src/responder/secrets/http_parser.c
- create mode 100644 src/responder/secrets/http_parser.h
-
-diff --git a/Makefile.am b/Makefile.am
-index 706b60d6a065e0a983f5a1cfbc26a78331c67d58..d05919705910fa565ff954224ce40feb5d7ff39f 100644
---- a/Makefile.am
-+++ b/Makefile.am
-@@ -1370,6 +1370,9 @@ sssd_secrets_SOURCES = \
-     $(SSSD_RESPONDER_OBJ) \
-     $(SSSD_RESOLV_OBJ) \
-     $(NULL)
-+sssd_secrets_CFLAGS = \
-+    $(AM_CFLAGS) \
-+    $(NULL)
- sssd_secrets_LDADD = \
-     $(HTTP_PARSER_LIBS) \
-     $(JANSSON_LIBS) \
-@@ -1379,6 +1382,18 @@ sssd_secrets_LDADD = \
-     $(CARES_LIBS) \
-     $(SSSD_INTERNAL_LTLIBS) \
-     $(NULL)
-+
-+if BUNDLE_HTTP_PARSER
-+sssd_secrets_CFLAGS += \
-+    -DHTTP_PARSER_STRICT=1 \
-+    $(NULL)
-+
-+sssd_secrets_SOURCES += \
-+    src/responder/secrets/http_parser.c \
-+    src/responder/secrets/http_parser.h \
-+    $(NULL)
-+endif
-+
- endif
- 
- sssd_be_SOURCES = \
-diff --git a/src/external/libhttp_parser.m4 b/src/external/libhttp_parser.m4
-index 504bdf0f66c95b3d224c677a205a46e6f8b44726..c4c5b0dad29da281295529be047d30e502f5de70 100644
---- a/src/external/libhttp_parser.m4
-+++ b/src/external/libhttp_parser.m4
-@@ -16,7 +16,6 @@ AS_IF([test x"$found_http_parser" != xyes],
-                                     [-L$sss_extra_libdir -lhttp_parser])
-                       ],
-                       [-L$sss_extra_libdir -lhttp_parser_strict])],
--        [AC_MSG_ERROR([
--You must have the header file http_parse.h installed to build sssd
--with secrets responder. If you want to build sssd without secret responder
--then specify --without-secrets when running configure.])])])
-+        [AC_MSG_WARN([Will use bundled http-parser])])])
-+
-+AM_CONDITIONAL([BUNDLE_HTTP_PARSER], [test x"$HTTP_PARSER_LIBS" = x])
-diff --git a/src/responder/secrets/http_parser.c b/src/responder/secrets/http_parser.c
-new file mode 100644
-index 0000000000000000000000000000000000000000..3c896ffadcc0dbf13942457d32c77d15bfec33e7
---- /dev/null
-+++ b/src/responder/secrets/http_parser.c
-@@ -0,0 +1,2469 @@
-+/* Based on src/http/ngx_http_parse.c from NGINX copyright Igor Sysoev
-+ *
-+ * Additional changes are licensed under the same terms as NGINX and
-+ * copyright Joyent, Inc. and other Node contributors. All rights reserved.
-+ *
-+ * Permission is hereby granted, free of charge, to any person obtaining a copy
-+ * of this software and associated documentation files (the "Software"), to
-+ * deal in the Software without restriction, including without limitation the
-+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
-+ * sell copies of the Software, and to permit persons to whom the Software is
-+ * furnished to do so, subject to the following conditions:
-+ *
-+ * The above copyright notice and this permission notice shall be included in
-+ * all copies or substantial portions of the Software.
-+ *
-+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
-+ * IN THE SOFTWARE.
-+ */
-+#include "http_parser.h"
-+#include <assert.h>
-+#include <stddef.h>
-+#include <ctype.h>
-+#include <stdlib.h>
-+#include <string.h>
-+#include <limits.h>
-+
-+#ifndef ULLONG_MAX
-+# define ULLONG_MAX ((uint64_t) -1) /* 2^64-1 */
-+#endif
-+
-+#ifndef MIN
-+# define MIN(a,b) ((a) < (b) ? (a) : (b))
-+#endif
-+
-+#ifndef ARRAY_SIZE
-+# define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
-+#endif
-+
-+#ifndef BIT_AT
-+# define BIT_AT(a, i)                                                \
-+  (!!((unsigned int) (a)[(unsigned int) (i) >> 3] &                  \
-+   (1 << ((unsigned int) (i) & 7))))
-+#endif
-+
-+#ifndef ELEM_AT
-+# define ELEM_AT(a, i, v) ((unsigned int) (i) < ARRAY_SIZE(a) ? (a)[(i)] : (v))
-+#endif
-+
-+#define SET_ERRNO(e)                                                 \
-+do {                                                                 \
-+  parser->http_errno = (e);                                          \
-+} while(0)
-+
-+#define CURRENT_STATE() p_state
-+#define UPDATE_STATE(V) p_state = (enum state) (V);
-+#define RETURN(V)                                                    \
-+do {                                                                 \
-+  parser->state = CURRENT_STATE();                                   \
-+  return (V);                                                        \
-+} while (0);
-+#define REEXECUTE()                                                  \
-+  goto reexecute;                                                    \
-+
-+
-+#ifdef __GNUC__
-+# define LIKELY(X) __builtin_expect(!!(X), 1)
-+# define UNLIKELY(X) __builtin_expect(!!(X), 0)
-+#else
-+# define LIKELY(X) (X)
-+# define UNLIKELY(X) (X)
-+#endif
-+
-+
-+/* Run the notify callback FOR, returning ER if it fails */
-+#define CALLBACK_NOTIFY_(FOR, ER)                                    \
-+do {                                                                 \
-+  assert(HTTP_PARSER_ERRNO(parser) == HPE_OK);                       \
-+                                                                     \
-+  if (LIKELY(settings->on_##FOR)) {                                  \
-+    parser->state = CURRENT_STATE();                                 \
-+    if (UNLIKELY(0 != settings->on_##FOR(parser))) {                 \
-+      SET_ERRNO(HPE_CB_##FOR);                                       \
-+    }                                                                \
-+    UPDATE_STATE(parser->state);                                     \
-+                                                                     \
-+    /* We either errored above or got paused; get out */             \
-+    if (UNLIKELY(HTTP_PARSER_ERRNO(parser) != HPE_OK)) {             \
-+      return (ER);                                                   \
-+    }                                                                \
-+  }                                                                  \
-+} while (0)
-+
-+/* Run the notify callback FOR and consume the current byte */
-+#define CALLBACK_NOTIFY(FOR)            CALLBACK_NOTIFY_(FOR, p - data + 1)
-+
-+/* Run the notify callback FOR and don't consume the current byte */
-+#define CALLBACK_NOTIFY_NOADVANCE(FOR)  CALLBACK_NOTIFY_(FOR, p - data)
-+
-+/* Run data callback FOR with LEN bytes, returning ER if it fails */
-+#define CALLBACK_DATA_(FOR, LEN, ER)                                 \
-+do {                                                                 \
-+  assert(HTTP_PARSER_ERRNO(parser) == HPE_OK);                       \
-+                                                                     \
-+  if (FOR##_mark) {                                                  \
-+    if (LIKELY(settings->on_##FOR)) {                                \
-+      parser->state = CURRENT_STATE();                               \
-+      if (UNLIKELY(0 !=                                              \
-+                   settings->on_##FOR(parser, FOR##_mark, (LEN)))) { \
-+        SET_ERRNO(HPE_CB_##FOR);                                     \
-+      }                                                              \
-+      UPDATE_STATE(parser->state);                                   \
-+                                                                     \
-+      /* We either errored above or got paused; get out */           \
-+      if (UNLIKELY(HTTP_PARSER_ERRNO(parser) != HPE_OK)) {           \
-+        return (ER);                                                 \
-+      }                                                              \
-+    }                                                                \
-+    FOR##_mark = NULL;                                               \
-+  }                                                                  \
-+} while (0)
-+
-+/* Run the data callback FOR and consume the current byte */
-+#define CALLBACK_DATA(FOR)                                           \
-+    CALLBACK_DATA_(FOR, p - FOR##_mark, p - data + 1)
-+
-+/* Run the data callback FOR and don't consume the current byte */
-+#define CALLBACK_DATA_NOADVANCE(FOR)                                 \
-+    CALLBACK_DATA_(FOR, p - FOR##_mark, p - data)
-+
-+/* Set the mark FOR; non-destructive if mark is already set */
-+#define MARK(FOR)                                                    \
-+do {                                                                 \
-+  if (!FOR##_mark) {                                                 \
-+    FOR##_mark = p;                                                  \
-+  }                                                                  \
-+} while (0)
-+
-+/* Don't allow the total size of the HTTP headers (including the status
-+ * line) to exceed HTTP_MAX_HEADER_SIZE.  This check is here to protect
-+ * embedders against denial-of-service attacks where the attacker feeds
-+ * us a never-ending header that the embedder keeps buffering.
-+ *
-+ * This check is arguably the responsibility of embedders but we're doing
-+ * it on the embedder's behalf because most won't bother and this way we
-+ * make the web a little safer.  HTTP_MAX_HEADER_SIZE is still far bigger
-+ * than any reasonable request or response so this should never affect
-+ * day-to-day operation.
-+ */
-+#define COUNT_HEADER_SIZE(V)                                         \
-+do {                                                                 \
-+  parser->nread += (V);                                              \
-+  if (UNLIKELY(parser->nread > (HTTP_MAX_HEADER_SIZE))) {            \
-+    SET_ERRNO(HPE_HEADER_OVERFLOW);                                  \
-+    goto error;                                                      \
-+  }                                                                  \
-+} while (0)
-+
-+
-+#define PROXY_CONNECTION "proxy-connection"
-+#define CONNECTION "connection"
-+#define CONTENT_LENGTH "content-length"
-+#define TRANSFER_ENCODING "transfer-encoding"
-+#define UPGRADE "upgrade"
-+#define CHUNKED "chunked"
-+#define KEEP_ALIVE "keep-alive"
-+#define CLOSE "close"
-+
-+
-+static const char *method_strings[] =
-+  {
-+#define XX(num, name, string) #string,
-+  HTTP_METHOD_MAP(XX)
-+#undef XX
-+  };
-+
-+
-+/* Tokens as defined by rfc 2616. Also lowercases them.
-+ *        token       = 1*<any CHAR except CTLs or separators>
-+ *     separators     = "(" | ")" | "<" | ">" | "@"
-+ *                    | "," | ";" | ":" | "\" | <">
-+ *                    | "/" | "[" | "]" | "?" | "="
-+ *                    | "{" | "}" | SP | HT
-+ */
-+static const char tokens[256] = {
-+/*   0 nul    1 soh    2 stx    3 etx    4 eot    5 enq    6 ack    7 bel  */
-+        0,       0,       0,       0,       0,       0,       0,       0,
-+/*   8 bs     9 ht    10 nl    11 vt    12 np    13 cr    14 so    15 si   */
-+        0,       0,       0,       0,       0,       0,       0,       0,
-+/*  16 dle   17 dc1   18 dc2   19 dc3   20 dc4   21 nak   22 syn   23 etb */
-+        0,       0,       0,       0,       0,       0,       0,       0,
-+/*  24 can   25 em    26 sub   27 esc   28 fs    29 gs    30 rs    31 us  */
-+        0,       0,       0,       0,       0,       0,       0,       0,
-+/*  32 sp    33  !    34  "    35  #    36  $    37  %    38  &    39  '  */
-+        0,      '!',      0,      '#',     '$',     '%',     '&',    '\'',
-+/*  40  (    41  )    42  *    43  +    44  ,    45  -    46  .    47  /  */
-+        0,       0,      '*',     '+',      0,      '-',     '.',      0,
-+/*  48  0    49  1    50  2    51  3    52  4    53  5    54  6    55  7  */
-+       '0',     '1',     '2',     '3',     '4',     '5',     '6',     '7',
-+/*  56  8    57  9    58  :    59  ;    60  <    61  =    62  >    63  ?  */
-+       '8',     '9',      0,       0,       0,       0,       0,       0,
-+/*  64  @    65  A    66  B    67  C    68  D    69  E    70  F    71  G  */
-+        0,      'a',     'b',     'c',     'd',     'e',     'f',     'g',
-+/*  72  H    73  I    74  J    75  K    76  L    77  M    78  N    79  O  */
-+       'h',     'i',     'j',     'k',     'l',     'm',     'n',     'o',
-+/*  80  P    81  Q    82  R    83  S    84  T    85  U    86  V    87  W  */
-+       'p',     'q',     'r',     's',     't',     'u',     'v',     'w',
-+/*  88  X    89  Y    90  Z    91  [    92  \    93  ]    94  ^    95  _  */
-+       'x',     'y',     'z',      0,       0,       0,      '^',     '_',
-+/*  96  `    97  a    98  b    99  c   100  d   101  e   102  f   103  g  */
-+       '`',     'a',     'b',     'c',     'd',     'e',     'f',     'g',
-+/* 104  h   105  i   106  j   107  k   108  l   109  m   110  n   111  o  */
-+       'h',     'i',     'j',     'k',     'l',     'm',     'n',     'o',
-+/* 112  p   113  q   114  r   115  s   116  t   117  u   118  v   119  w  */
-+       'p',     'q',     'r',     's',     't',     'u',     'v',     'w',
-+/* 120  x   121  y   122  z   123  {   124  |   125  }   126  ~   127 del */
-+       'x',     'y',     'z',      0,      '|',      0,      '~',       0 };
-+
-+
-+static const int8_t unhex[256] =
-+  {-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
-+  ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
-+  ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
-+  , 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,-1,-1,-1,-1,-1,-1
-+  ,-1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1
-+  ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
-+  ,-1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1
-+  ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
-+  };
-+
-+
-+#if HTTP_PARSER_STRICT
-+# define T(v) 0
-+#else
-+# define T(v) v
-+#endif
-+
-+
-+static const uint8_t normal_url_char[32] = {
-+/*   0 nul    1 soh    2 stx    3 etx    4 eot    5 enq    6 ack    7 bel  */
-+        0    |   0    |   0    |   0    |   0    |   0    |   0    |   0,
-+/*   8 bs     9 ht    10 nl    11 vt    12 np    13 cr    14 so    15 si   */
-+        0    | T(2)   |   0    |   0    | T(16)  |   0    |   0    |   0,
-+/*  16 dle   17 dc1   18 dc2   19 dc3   20 dc4   21 nak   22 syn   23 etb */
-+        0    |   0    |   0    |   0    |   0    |   0    |   0    |   0,
-+/*  24 can   25 em    26 sub   27 esc   28 fs    29 gs    30 rs    31 us  */
-+        0    |   0    |   0    |   0    |   0    |   0    |   0    |   0,
-+/*  32 sp    33  !    34  "    35  #    36  $    37  %    38  &    39  '  */
-+        0    |   2    |   4    |   0    |   16   |   32   |   64   |  128,
-+/*  40  (    41  )    42  *    43  +    44  ,    45  -    46  .    47  /  */
-+        1    |   2    |   4    |   8    |   16   |   32   |   64   |  128,
-+/*  48  0    49  1    50  2    51  3    52  4    53  5    54  6    55  7  */
-+        1    |   2    |   4    |   8    |   16   |   32   |   64   |  128,
-+/*  56  8    57  9    58  :    59  ;    60  <    61  =    62  >    63  ?  */
-+        1    |   2    |   4    |   8    |   16   |   32   |   64   |   0,
-+/*  64  @    65  A    66  B    67  C    68  D    69  E    70  F    71  G  */
-+        1    |   2    |   4    |   8    |   16   |   32   |   64   |  128,
-+/*  72  H    73  I    74  J    75  K    76  L    77  M    78  N    79  O  */
-+        1    |   2    |   4    |   8    |   16   |   32   |   64   |  128,
-+/*  80  P    81  Q    82  R    83  S    84  T    85  U    86  V    87  W  */
-+        1    |   2    |   4    |   8    |   16   |   32   |   64   |  128,
-+/*  88  X    89  Y    90  Z    91  [    92  \    93  ]    94  ^    95  _  */
-+        1    |   2    |   4    |   8    |   16   |   32   |   64   |  128,
-+/*  96  `    97  a    98  b    99  c   100  d   101  e   102  f   103  g  */
-+        1    |   2    |   4    |   8    |   16   |   32   |   64   |  128,
-+/* 104  h   105  i   106  j   107  k   108  l   109  m   110  n   111  o  */
-+        1    |   2    |   4    |   8    |   16   |   32   |   64   |  128,
-+/* 112  p   113  q   114  r   115  s   116  t   117  u   118  v   119  w  */
-+        1    |   2    |   4    |   8    |   16   |   32   |   64   |  128,
-+/* 120  x   121  y   122  z   123  {   124  |   125  }   126  ~   127 del */
-+        1    |   2    |   4    |   8    |   16   |   32   |   64   |   0, };
-+
-+#undef T
-+
-+enum state
-+  { s_dead = 1 /* important that this is > 0 */
-+
-+  , s_start_req_or_res
-+  , s_res_or_resp_H
-+  , s_start_res
-+  , s_res_H
-+  , s_res_HT
-+  , s_res_HTT
-+  , s_res_HTTP
-+  , s_res_first_http_major
-+  , s_res_http_major
-+  , s_res_first_http_minor
-+  , s_res_http_minor
-+  , s_res_first_status_code
-+  , s_res_status_code
-+  , s_res_status_start
-+  , s_res_status
-+  , s_res_line_almost_done
-+
-+  , s_start_req
-+
-+  , s_req_method
-+  , s_req_spaces_before_url
-+  , s_req_schema
-+  , s_req_schema_slash
-+  , s_req_schema_slash_slash
-+  , s_req_server_start
-+  , s_req_server
-+  , s_req_server_with_at
-+  , s_req_path
-+  , s_req_query_string_start
-+  , s_req_query_string
-+  , s_req_fragment_start
-+  , s_req_fragment
-+  , s_req_http_start
-+  , s_req_http_H
-+  , s_req_http_HT
-+  , s_req_http_HTT
-+  , s_req_http_HTTP
-+  , s_req_first_http_major
-+  , s_req_http_major
-+  , s_req_first_http_minor
-+  , s_req_http_minor
-+  , s_req_line_almost_done
-+
-+  , s_header_field_start
-+  , s_header_field
-+  , s_header_value_discard_ws
-+  , s_header_value_discard_ws_almost_done
-+  , s_header_value_discard_lws
-+  , s_header_value_start
-+  , s_header_value
-+  , s_header_value_lws
-+
-+  , s_header_almost_done
-+
-+  , s_chunk_size_start
-+  , s_chunk_size
-+  , s_chunk_parameters
-+  , s_chunk_size_almost_done
-+
-+  , s_headers_almost_done
-+  , s_headers_done
-+
-+  /* Important: 's_headers_done' must be the last 'header' state. All
-+   * states beyond this must be 'body' states. It is used for overflow
-+   * checking. See the PARSING_HEADER() macro.
-+   */
-+
-+  , s_chunk_data
-+  , s_chunk_data_almost_done
-+  , s_chunk_data_done
-+
-+  , s_body_identity
-+  , s_body_identity_eof
-+
-+  , s_message_done
-+  };
-+
-+
-+#define PARSING_HEADER(state) (state <= s_headers_done)
-+
-+
-+enum header_states
-+  { h_general = 0
-+  , h_C
-+  , h_CO
-+  , h_CON
-+
-+  , h_matching_connection
-+  , h_matching_proxy_connection
-+  , h_matching_content_length
-+  , h_matching_transfer_encoding
-+  , h_matching_upgrade
-+
-+  , h_connection
-+  , h_content_length
-+  , h_transfer_encoding
-+  , h_upgrade
-+
-+  , h_matching_transfer_encoding_chunked
-+  , h_matching_connection_token_start
-+  , h_matching_connection_keep_alive
-+  , h_matching_connection_close
-+  , h_matching_connection_upgrade
-+  , h_matching_connection_token
-+
-+  , h_transfer_encoding_chunked
-+  , h_connection_keep_alive
-+  , h_connection_close
-+  , h_connection_upgrade
-+  };
-+
-+enum http_host_state
-+  {
-+    s_http_host_dead = 1
-+  , s_http_userinfo_start
-+  , s_http_userinfo
-+  , s_http_host_start
-+  , s_http_host_v6_start
-+  , s_http_host
-+  , s_http_host_v6
-+  , s_http_host_v6_end
-+  , s_http_host_v6_zone_start
-+  , s_http_host_v6_zone
-+  , s_http_host_port_start
-+  , s_http_host_port
-+};
-+
-+/* Macros for character classes; depends on strict-mode  */
-+#define CR                  '\r'
-+#define LF                  '\n'
-+#define LOWER(c)            (unsigned char)(c | 0x20)
-+#define IS_ALPHA(c)         (LOWER(c) >= 'a' && LOWER(c) <= 'z')
-+#define IS_NUM(c)           ((c) >= '0' && (c) <= '9')
-+#define IS_ALPHANUM(c)      (IS_ALPHA(c) || IS_NUM(c))
-+#define IS_HEX(c)           (IS_NUM(c) || (LOWER(c) >= 'a' && LOWER(c) <= 'f'))
-+#define IS_MARK(c)          ((c) == '-' || (c) == '_' || (c) == '.' || \
-+  (c) == '!' || (c) == '~' || (c) == '*' || (c) == '\'' || (c) == '(' || \
-+  (c) == ')')
-+#define IS_USERINFO_CHAR(c) (IS_ALPHANUM(c) || IS_MARK(c) || (c) == '%' || \
-+  (c) == ';' || (c) == ':' || (c) == '&' || (c) == '=' || (c) == '+' || \
-+  (c) == '$' || (c) == ',')
-+
-+#define STRICT_TOKEN(c)     (tokens[(unsigned char)c])
-+
-+#if HTTP_PARSER_STRICT
-+#define TOKEN(c)            (tokens[(unsigned char)c])
-+#define IS_URL_CHAR(c)      (BIT_AT(normal_url_char, (unsigned char)c))
-+#define IS_HOST_CHAR(c)     (IS_ALPHANUM(c) || (c) == '.' || (c) == '-')
-+#else
-+#define TOKEN(c)            ((c == ' ') ? ' ' : tokens[(unsigned char)c])
-+#define IS_URL_CHAR(c)                                                         \
-+  (BIT_AT(normal_url_char, (unsigned char)c) || ((c) & 0x80))
-+#define IS_HOST_CHAR(c)                                                        \
-+  (IS_ALPHANUM(c) || (c) == '.' || (c) == '-' || (c) == '_')
-+#endif
-+
-+/**
-+ * Verify that a char is a valid visible (printable) US-ASCII
-+ * character or %x80-FF
-+ **/
-+#define IS_HEADER_CHAR(ch)                                                     \
-+  (ch == CR || ch == LF || ch == 9 || ((unsigned char)ch > 31 && ch != 127))
-+
-+#define start_state (parser->type == HTTP_REQUEST ? s_start_req : s_start_res)
-+
-+
-+#if HTTP_PARSER_STRICT
-+# define STRICT_CHECK(cond)                                          \
-+do {                                                                 \
-+  if (cond) {                                                        \
-+    SET_ERRNO(HPE_STRICT);                                           \
-+    goto error;                                                      \
-+  }                                                                  \
-+} while (0)
-+# define NEW_MESSAGE() (http_should_keep_alive(parser) ? start_state : s_dead)
-+#else
-+# define STRICT_CHECK(cond)
-+# define NEW_MESSAGE() start_state
-+#endif
-+
-+
-+/* Map errno values to strings for human-readable output */
-+#define HTTP_STRERROR_GEN(n, s) { "HPE_" #n, s },
-+static struct {
-+  const char *name;
-+  const char *description;
-+} http_strerror_tab[] = {
-+  HTTP_ERRNO_MAP(HTTP_STRERROR_GEN)
-+};
-+#undef HTTP_STRERROR_GEN
-+
-+int http_message_needs_eof(const http_parser *parser);
-+
-+/* Our URL parser.
-+ *
-+ * This is designed to be shared by http_parser_execute() for URL validation,
-+ * hence it has a state transition + byte-for-byte interface. In addition, it
-+ * is meant to be embedded in http_parser_parse_url(), which does the dirty
-+ * work of turning state transitions URL components for its API.
-+ *
-+ * This function should only be invoked with non-space characters. It is
-+ * assumed that the caller cares about (and can detect) the transition between
-+ * URL and non-URL states by looking for these.
-+ */
-+static enum state
-+parse_url_char(enum state s, const char ch)
-+{
-+  if (ch == ' ' || ch == '\r' || ch == '\n') {
-+    return s_dead;
-+  }
-+
-+#if HTTP_PARSER_STRICT
-+  if (ch == '\t' || ch == '\f') {
-+    return s_dead;
-+  }
-+#endif
-+
-+  switch (s) {
-+    case s_req_spaces_before_url:
-+      /* Proxied requests are followed by scheme of an absolute URI (alpha).
-+       * All methods except CONNECT are followed by '/' or '*'.
-+       */
-+
-+      if (ch == '/' || ch == '*') {
-+        return s_req_path;
-+      }
-+
-+      if (IS_ALPHA(ch)) {
-+        return s_req_schema;
-+      }
-+
-+      break;
-+
-+    case s_req_schema:
-+      if (IS_ALPHA(ch)) {
-+        return s;
-+      }
-+
-+      if (ch == ':') {
-+        return s_req_schema_slash;
-+      }
-+
-+      break;
-+
-+    case s_req_schema_slash:
-+      if (ch == '/') {
-+        return s_req_schema_slash_slash;
-+      }
-+
-+      break;
-+
-+    case s_req_schema_slash_slash:
-+      if (ch == '/') {
-+        return s_req_server_start;
-+      }
-+
-+      break;
-+
-+    case s_req_server_with_at:
-+      if (ch == '@') {
-+        return s_dead;
-+      }
-+
-+    /* FALLTHROUGH */
-+    case s_req_server_start:
-+    case s_req_server:
-+      if (ch == '/') {
-+        return s_req_path;
-+      }
-+
-+      if (ch == '?') {
-+        return s_req_query_string_start;
-+      }
-+
-+      if (ch == '@') {
-+        return s_req_server_with_at;
-+      }
-+
-+      if (IS_USERINFO_CHAR(ch) || ch == '[' || ch == ']') {
-+        return s_req_server;
-+      }
-+
-+      break;
-+
-+    case s_req_path:
-+      if (IS_URL_CHAR(ch)) {
-+        return s;
-+      }
-+
-+      switch (ch) {
-+        case '?':
-+          return s_req_query_string_start;
-+
-+        case '#':
-+          return s_req_fragment_start;
-+      }
-+
-+      break;
-+
-+    case s_req_query_string_start:
-+    case s_req_query_string:
-+      if (IS_URL_CHAR(ch)) {
-+        return s_req_query_string;
-+      }
-+
-+      switch (ch) {
-+        case '?':
-+          /* allow extra '?' in query string */
-+          return s_req_query_string;
-+
-+        case '#':
-+          return s_req_fragment_start;
-+      }
-+
-+      break;
-+
-+    case s_req_fragment_start:
-+      if (IS_URL_CHAR(ch)) {
-+        return s_req_fragment;
-+      }
-+
-+      switch (ch) {
-+        case '?':
-+          return s_req_fragment;
-+
-+        case '#':
-+          return s;
-+      }
-+
-+      break;
-+
-+    case s_req_fragment:
-+      if (IS_URL_CHAR(ch)) {
-+        return s;
-+      }
-+
-+      switch (ch) {
-+        case '?':
-+        case '#':
-+          return s;
-+      }
-+
-+      break;
-+
-+    default:
-+      break;
-+  }
-+
-+  /* We should never fall out of the switch above unless there's an error */
-+  return s_dead;
-+}
-+
-+size_t http_parser_execute (http_parser *parser,
-+                            const http_parser_settings *settings,
-+                            const char *data,
-+                            size_t len)
-+{
-+  char c, ch;
-+  int8_t unhex_val;
-+  const char *p = data;
-+  const char *header_field_mark = 0;
-+  const char *header_value_mark = 0;
-+  const char *url_mark = 0;
-+  const char *body_mark = 0;
-+  const char *status_mark = 0;
-+  enum state p_state = (enum state) parser->state;
-+  const unsigned int lenient = parser->lenient_http_headers;
-+
-+  /* We're in an error state. Don't bother doing anything. */
-+  if (HTTP_PARSER_ERRNO(parser) != HPE_OK) {
-+    return 0;
-+  }
-+
-+  if (len == 0) {
-+    switch (CURRENT_STATE()) {
-+      case s_body_identity_eof:
-+        /* Use of CALLBACK_NOTIFY() here would erroneously return 1 byte read if
-+         * we got paused.
-+         */
-+        CALLBACK_NOTIFY_NOADVANCE(message_complete);
-+        return 0;
-+
-+      case s_dead:
-+      case s_start_req_or_res:
-+      case s_start_res:
-+      case s_start_req:
-+        return 0;
-+
-+      default:
-+        SET_ERRNO(HPE_INVALID_EOF_STATE);
-+        return 1;
-+    }
-+  }
-+
-+
-+  if (CURRENT_STATE() == s_header_field)
-+    header_field_mark = data;
-+  if (CURRENT_STATE() == s_header_value)
-+    header_value_mark = data;
-+  switch (CURRENT_STATE()) {
-+  case s_req_path:
-+  case s_req_schema:
-+  case s_req_schema_slash:
-+  case s_req_schema_slash_slash:
-+  case s_req_server_start:
-+  case s_req_server:
-+  case s_req_server_with_at:
-+  case s_req_query_string_start:
-+  case s_req_query_string:
-+  case s_req_fragment_start:
-+  case s_req_fragment:
-+    url_mark = data;
-+    break;
-+  case s_res_status:
-+    status_mark = data;
-+    break;
-+  default:
-+    break;
-+  }
-+
-+  for (p=data; p != data + len; p++) {
-+    ch = *p;
-+
-+    if (PARSING_HEADER(CURRENT_STATE()))
-+      COUNT_HEADER_SIZE(1);
-+
-+reexecute:
-+    switch (CURRENT_STATE()) {
-+
-+      case s_dead:
-+        /* this state is used after a 'Connection: close' message
-+         * the parser will error out if it reads another message
-+         */
-+        if (LIKELY(ch == CR || ch == LF))
-+          break;
-+
-+        SET_ERRNO(HPE_CLOSED_CONNECTION);
-+        goto error;
-+
-+      case s_start_req_or_res:
-+      {
-+        if (ch == CR || ch == LF)
-+          break;
-+        parser->flags = 0;
-+        parser->content_length = ULLONG_MAX;
-+
-+        if (ch == 'H') {
-+          UPDATE_STATE(s_res_or_resp_H);
-+
-+          CALLBACK_NOTIFY(message_begin);
-+        } else {
-+          parser->type = HTTP_REQUEST;
-+          UPDATE_STATE(s_start_req);
-+          REEXECUTE();
-+        }
-+
-+        break;
-+      }
-+
-+      case s_res_or_resp_H:
-+        if (ch == 'T') {
-+          parser->type = HTTP_RESPONSE;
-+          UPDATE_STATE(s_res_HT);
-+        } else {
-+          if (UNLIKELY(ch != 'E')) {
-+            SET_ERRNO(HPE_INVALID_CONSTANT);
-+            goto error;
-+          }
-+
-+          parser->type = HTTP_REQUEST;
-+          parser->method = HTTP_HEAD;
-+          parser->index = 2;
-+          UPDATE_STATE(s_req_method);
-+        }
-+        break;
-+
-+      case s_start_res:
-+      {
-+        parser->flags = 0;
-+        parser->content_length = ULLONG_MAX;
-+
-+        switch (ch) {
-+          case 'H':
-+            UPDATE_STATE(s_res_H);
-+            break;
-+
-+          case CR:
-+          case LF:
-+            break;
-+
-+          default:
-+            SET_ERRNO(HPE_INVALID_CONSTANT);
-+            goto error;
-+        }
-+
-+        CALLBACK_NOTIFY(message_begin);
-+        break;
-+      }
-+
-+      case s_res_H:
-+        STRICT_CHECK(ch != 'T');
-+        UPDATE_STATE(s_res_HT);
-+        break;
-+
-+      case s_res_HT:
-+        STRICT_CHECK(ch != 'T');
-+        UPDATE_STATE(s_res_HTT);
-+        break;
-+
-+      case s_res_HTT:
-+        STRICT_CHECK(ch != 'P');
-+        UPDATE_STATE(s_res_HTTP);
-+        break;
-+
-+      case s_res_HTTP:
-+        STRICT_CHECK(ch != '/');
-+        UPDATE_STATE(s_res_first_http_major);
-+        break;
-+
-+      case s_res_first_http_major:
-+        if (UNLIKELY(ch < '0' || ch > '9')) {
-+          SET_ERRNO(HPE_INVALID_VERSION);
-+          goto error;
-+        }
-+
-+        parser->http_major = ch - '0';
-+        UPDATE_STATE(s_res_http_major);
-+        break;
-+
-+      /* major HTTP version or dot */
-+      case s_res_http_major:
-+      {
-+        if (ch == '.') {
-+          UPDATE_STATE(s_res_first_http_minor);
-+          break;
-+        }
-+
-+        if (!IS_NUM(ch)) {
-+          SET_ERRNO(HPE_INVALID_VERSION);
-+          goto error;
-+        }
-+
-+        parser->http_major *= 10;
-+        parser->http_major += ch - '0';
-+
-+        if (UNLIKELY(parser->http_major > 999)) {
-+          SET_ERRNO(HPE_INVALID_VERSION);
-+          goto error;
-+        }
-+
-+        break;
-+      }
-+
-+      /* first digit of minor HTTP version */
-+      case s_res_first_http_minor:
-+        if (UNLIKELY(!IS_NUM(ch))) {
-+          SET_ERRNO(HPE_INVALID_VERSION);
-+          goto error;
-+        }
-+
-+        parser->http_minor = ch - '0';
-+        UPDATE_STATE(s_res_http_minor);
-+        break;
-+
-+      /* minor HTTP version or end of request line */
-+      case s_res_http_minor:
-+      {
-+        if (ch == ' ') {
-+          UPDATE_STATE(s_res_first_status_code);
-+          break;
-+        }
-+
-+        if (UNLIKELY(!IS_NUM(ch))) {
-+          SET_ERRNO(HPE_INVALID_VERSION);
-+          goto error;
-+        }
-+
-+        parser->http_minor *= 10;
-+        parser->http_minor += ch - '0';
-+
-+        if (UNLIKELY(parser->http_minor > 999)) {
-+          SET_ERRNO(HPE_INVALID_VERSION);
-+          goto error;
-+        }
-+
-+        break;
-+      }
-+
-+      case s_res_first_status_code:
-+      {
-+        if (!IS_NUM(ch)) {
-+          if (ch == ' ') {
-+            break;
-+          }
-+
-+          SET_ERRNO(HPE_INVALID_STATUS);
-+          goto error;
-+        }
-+        parser->status_code = ch - '0';
-+        UPDATE_STATE(s_res_status_code);
-+        break;
-+      }
-+
-+      case s_res_status_code:
-+      {
-+        if (!IS_NUM(ch)) {
-+          switch (ch) {
-+            case ' ':
-+              UPDATE_STATE(s_res_status_start);
-+              break;
-+            case CR:
-+              UPDATE_STATE(s_res_line_almost_done);
-+              break;
-+            case LF:
-+              UPDATE_STATE(s_header_field_start);
-+              break;
-+            default:
-+              SET_ERRNO(HPE_INVALID_STATUS);
-+              goto error;
-+          }
-+          break;
-+        }
-+
-+        parser->status_code *= 10;
-+        parser->status_code += ch - '0';
-+
-+        if (UNLIKELY(parser->status_code > 999)) {
-+          SET_ERRNO(HPE_INVALID_STATUS);
-+          goto error;
-+        }
-+
-+        break;
-+      }
-+
-+      case s_res_status_start:
-+      {
-+        if (ch == CR) {
-+          UPDATE_STATE(s_res_line_almost_done);
-+          break;
-+        }
-+
-+        if (ch == LF) {
-+          UPDATE_STATE(s_header_field_start);
-+          break;
-+        }
-+
-+        MARK(status);
-+        UPDATE_STATE(s_res_status);
-+        parser->index = 0;
-+        break;
-+      }
-+
-+      case s_res_status:
-+        if (ch == CR) {
-+          UPDATE_STATE(s_res_line_almost_done);
-+          CALLBACK_DATA(status);
-+          break;
-+        }
-+
-+        if (ch == LF) {
-+          UPDATE_STATE(s_header_field_start);
-+          CALLBACK_DATA(status);
-+          break;
-+        }
-+
-+        break;
-+
-+      case s_res_line_almost_done:
-+        STRICT_CHECK(ch != LF);
-+        UPDATE_STATE(s_header_field_start);
-+        break;
-+
-+      case s_start_req:
-+      {
-+        if (ch == CR || ch == LF)
-+          break;
-+        parser->flags = 0;
-+        parser->content_length = ULLONG_MAX;
-+
-+        if (UNLIKELY(!IS_ALPHA(ch))) {
-+          SET_ERRNO(HPE_INVALID_METHOD);
-+          goto error;
-+        }
-+
-+        parser->method = (enum http_method) 0;
-+        parser->index = 1;
-+        switch (ch) {
-+          case 'A': parser->method = HTTP_ACL; break;
-+          case 'B': parser->method = HTTP_BIND; break;
-+          case 'C': parser->method = HTTP_CONNECT; /* or COPY, CHECKOUT */ break;
-+          case 'D': parser->method = HTTP_DELETE; break;
-+          case 'G': parser->method = HTTP_GET; break;
-+          case 'H': parser->method = HTTP_HEAD; break;
-+          case 'L': parser->method = HTTP_LOCK; /* or LINK */ break;
-+          case 'M': parser->method = HTTP_MKCOL; /* or MOVE, MKACTIVITY, MERGE, M-SEARCH, MKCALENDAR */ break;
-+          case 'N': parser->method = HTTP_NOTIFY; break;
-+          case 'O': parser->method = HTTP_OPTIONS; break;
-+          case 'P': parser->method = HTTP_POST;
-+            /* or PROPFIND|PROPPATCH|PUT|PATCH|PURGE */
-+            break;
-+          case 'R': parser->method = HTTP_REPORT; /* or REBIND */ break;
-+          case 'S': parser->method = HTTP_SUBSCRIBE; /* or SEARCH */ break;
-+          case 'T': parser->method = HTTP_TRACE; break;
-+          case 'U': parser->method = HTTP_UNLOCK; /* or UNSUBSCRIBE, UNBIND, UNLINK */ break;
-+          default:
-+            SET_ERRNO(HPE_INVALID_METHOD);
-+            goto error;
-+        }
-+        UPDATE_STATE(s_req_method);
-+
-+        CALLBACK_NOTIFY(message_begin);
-+
-+        break;
-+      }
-+
-+      case s_req_method:
-+      {
-+        const char *matcher;
-+        if (UNLIKELY(ch == '\0')) {
-+          SET_ERRNO(HPE_INVALID_METHOD);
-+          goto error;
-+        }
-+
-+        matcher = method_strings[parser->method];
-+        if (ch == ' ' && matcher[parser->index] == '\0') {
-+          UPDATE_STATE(s_req_spaces_before_url);
-+        } else if (ch == matcher[parser->index]) {
-+          ; /* nada */
-+        } else if (IS_ALPHA(ch)) {
-+
-+          switch (parser->method << 16 | parser->index << 8 | ch) {
-+#define XX(meth, pos, ch, new_meth) \
-+            case (HTTP_##meth << 16 | pos << 8 | ch): \
-+              parser->method = HTTP_##new_meth; break;
-+
-+            XX(POST,      1, 'U', PUT)
-+            XX(POST,      1, 'A', PATCH)
-+            XX(CONNECT,   1, 'H', CHECKOUT)
-+            XX(CONNECT,   2, 'P', COPY)
-+            XX(MKCOL,     1, 'O', MOVE)
-+            XX(MKCOL,     1, 'E', MERGE)
-+            XX(MKCOL,     2, 'A', MKACTIVITY)
-+            XX(MKCOL,     3, 'A', MKCALENDAR)
-+            XX(SUBSCRIBE, 1, 'E', SEARCH)
-+            XX(REPORT,    2, 'B', REBIND)
-+            XX(POST,      1, 'R', PROPFIND)
-+            XX(PROPFIND,  4, 'P', PROPPATCH)
-+            XX(PUT,       2, 'R', PURGE)
-+            XX(LOCK,      1, 'I', LINK)
-+            XX(UNLOCK,    2, 'S', UNSUBSCRIBE)
-+            XX(UNLOCK,    2, 'B', UNBIND)
-+            XX(UNLOCK,    3, 'I', UNLINK)
-+#undef XX
-+
-+            default:
-+              SET_ERRNO(HPE_INVALID_METHOD);
-+              goto error;
-+          }
-+        } else if (ch == '-' &&
-+                   parser->index == 1 &&
-+                   parser->method == HTTP_MKCOL) {
-+          parser->method = HTTP_MSEARCH;
-+        } else {
-+          SET_ERRNO(HPE_INVALID_METHOD);
-+          goto error;
-+        }
-+
-+        ++parser->index;
-+        break;
-+      }
-+
-+      case s_req_spaces_before_url:
-+      {
-+        if (ch == ' ') break;
-+
-+        MARK(url);
-+        if (parser->method == HTTP_CONNECT) {
-+          UPDATE_STATE(s_req_server_start);
-+        }
-+
-+        UPDATE_STATE(parse_url_char(CURRENT_STATE(), ch));
-+        if (UNLIKELY(CURRENT_STATE() == s_dead)) {
-+          SET_ERRNO(HPE_INVALID_URL);
-+          goto error;
-+        }
-+
-+        break;
-+      }
-+
-+      case s_req_schema:
-+      case s_req_schema_slash:
-+      case s_req_schema_slash_slash:
-+      case s_req_server_start:
-+      {
-+        switch (ch) {
-+          /* No whitespace allowed here */
-+          case ' ':
-+          case CR:
-+          case LF:
-+            SET_ERRNO(HPE_INVALID_URL);
-+            goto error;
-+          default:
-+            UPDATE_STATE(parse_url_char(CURRENT_STATE(), ch));
-+            if (UNLIKELY(CURRENT_STATE() == s_dead)) {
-+              SET_ERRNO(HPE_INVALID_URL);
-+              goto error;
-+            }
-+        }
-+
-+        break;
-+      }
-+
-+      case s_req_server:
-+      case s_req_server_with_at:
-+      case s_req_path:
-+      case s_req_query_string_start:
-+      case s_req_query_string:
-+      case s_req_fragment_start:
-+      case s_req_fragment:
-+      {
-+        switch (ch) {
-+          case ' ':
-+            UPDATE_STATE(s_req_http_start);
-+            CALLBACK_DATA(url);
-+            break;
-+          case CR:
-+          case LF:
-+            parser->http_major = 0;
-+            parser->http_minor = 9;
-+            UPDATE_STATE((ch == CR) ?
-+              s_req_line_almost_done :
-+              s_header_field_start);
-+            CALLBACK_DATA(url);
-+            break;
-+          default:
-+            UPDATE_STATE(parse_url_char(CURRENT_STATE(), ch));
-+            if (UNLIKELY(CURRENT_STATE() == s_dead)) {
-+              SET_ERRNO(HPE_INVALID_URL);
-+              goto error;
-+            }
-+        }
-+        break;
-+      }
-+
-+      case s_req_http_start:
-+        switch (ch) {
-+          case 'H':
-+            UPDATE_STATE(s_req_http_H);
-+            break;
-+          case ' ':
-+            break;
-+          default:
-+            SET_ERRNO(HPE_INVALID_CONSTANT);
-+            goto error;
-+        }
-+        break;
-+
-+      case s_req_http_H:
-+        STRICT_CHECK(ch != 'T');
-+        UPDATE_STATE(s_req_http_HT);
-+        break;
-+
-+      case s_req_http_HT:
-+        STRICT_CHECK(ch != 'T');
-+        UPDATE_STATE(s_req_http_HTT);
-+        break;
-+
-+      case s_req_http_HTT:
-+        STRICT_CHECK(ch != 'P');
-+        UPDATE_STATE(s_req_http_HTTP);
-+        break;
-+
-+      case s_req_http_HTTP:
-+        STRICT_CHECK(ch != '/');
-+        UPDATE_STATE(s_req_first_http_major);
-+        break;
-+
-+      /* first digit of major HTTP version */
-+      case s_req_first_http_major:
-+        if (UNLIKELY(ch < '1' || ch > '9')) {
-+          SET_ERRNO(HPE_INVALID_VERSION);
-+          goto error;
-+        }
-+
-+        parser->http_major = ch - '0';
-+        UPDATE_STATE(s_req_http_major);
-+        break;
-+
-+      /* major HTTP version or dot */
-+      case s_req_http_major:
-+      {
-+        if (ch == '.') {
-+          UPDATE_STATE(s_req_first_http_minor);
-+          break;
-+        }
-+
-+        if (UNLIKELY(!IS_NUM(ch))) {
-+          SET_ERRNO(HPE_INVALID_VERSION);
-+          goto error;
-+        }
-+
-+        parser->http_major *= 10;
-+        parser->http_major += ch - '0';
-+
-+        if (UNLIKELY(parser->http_major > 999)) {
-+          SET_ERRNO(HPE_INVALID_VERSION);
-+          goto error;
-+        }
-+
-+        break;
-+      }
-+
-+      /* first digit of minor HTTP version */
-+      case s_req_first_http_minor:
-+        if (UNLIKELY(!IS_NUM(ch))) {
-+          SET_ERRNO(HPE_INVALID_VERSION);
-+          goto error;
-+        }
-+
-+        parser->http_minor = ch - '0';
-+        UPDATE_STATE(s_req_http_minor);
-+        break;
-+
-+      /* minor HTTP version or end of request line */
-+      case s_req_http_minor:
-+      {
-+        if (ch == CR) {
-+          UPDATE_STATE(s_req_line_almost_done);
-+          break;
-+        }
-+
-+        if (ch == LF) {
-+          UPDATE_STATE(s_header_field_start);
-+          break;
-+        }
-+
-+        /* XXX allow spaces after digit? */
-+
-+        if (UNLIKELY(!IS_NUM(ch))) {
-+          SET_ERRNO(HPE_INVALID_VERSION);
-+          goto error;
-+        }
-+
-+        parser->http_minor *= 10;
-+        parser->http_minor += ch - '0';
-+
-+        if (UNLIKELY(parser->http_minor > 999)) {
-+          SET_ERRNO(HPE_INVALID_VERSION);
-+          goto error;
-+        }
-+
-+        break;
-+      }
-+
-+      /* end of request line */
-+      case s_req_line_almost_done:
-+      {
-+        if (UNLIKELY(ch != LF)) {
-+          SET_ERRNO(HPE_LF_EXPECTED);
-+          goto error;
-+        }
-+
-+        UPDATE_STATE(s_header_field_start);
-+        break;
-+      }
-+
-+      case s_header_field_start:
-+      {
-+        if (ch == CR) {
-+          UPDATE_STATE(s_headers_almost_done);
-+          break;
-+        }
-+
-+        if (ch == LF) {
-+          /* they might be just sending \n instead of \r\n so this would be
-+           * the second \n to denote the end of headers*/
-+          UPDATE_STATE(s_headers_almost_done);
-+          REEXECUTE();
-+        }
-+
-+        c = TOKEN(ch);
-+
-+        if (UNLIKELY(!c)) {
-+          SET_ERRNO(HPE_INVALID_HEADER_TOKEN);
-+          goto error;
-+        }
-+
-+        MARK(header_field);
-+
-+        parser->index = 0;
-+        UPDATE_STATE(s_header_field);
-+
-+        switch (c) {
-+          case 'c':
-+            parser->header_state = h_C;
-+            break;
-+
-+          case 'p':
-+            parser->header_state = h_matching_proxy_connection;
-+            break;
-+
-+          case 't':
-+            parser->header_state = h_matching_transfer_encoding;
-+            break;
-+
-+          case 'u':
-+            parser->header_state = h_matching_upgrade;
-+            break;
-+
-+          default:
-+            parser->header_state = h_general;
-+            break;
-+        }
-+        break;
-+      }
-+
-+      case s_header_field:
-+      {
-+        const char* start = p;
-+        for (; p != data + len; p++) {
-+          ch = *p;
-+          c = TOKEN(ch);
-+
-+          if (!c)
-+            break;
-+
-+          switch (parser->header_state) {
-+            case h_general:
-+              break;
-+
-+            case h_C:
-+              parser->index++;
-+              parser->header_state = (c == 'o' ? h_CO : h_general);
-+              break;
-+
-+            case h_CO:
-+              parser->index++;
-+              parser->header_state = (c == 'n' ? h_CON : h_general);
-+              break;
-+
-+            case h_CON:
-+              parser->index++;
-+              switch (c) {
-+                case 'n':
-+                  parser->header_state = h_matching_connection;
-+                  break;
-+                case 't':
-+                  parser->header_state = h_matching_content_length;
-+                  break;
-+                default:
-+                  parser->header_state = h_general;
-+                  break;
-+              }
-+              break;
-+
-+            /* connection */
-+
-+            case h_matching_connection:
-+              parser->index++;
-+              if (parser->index > sizeof(CONNECTION)-1
-+                  || c != CONNECTION[parser->index]) {
-+                parser->header_state = h_general;
-+              } else if (parser->index == sizeof(CONNECTION)-2) {
-+                parser->header_state = h_connection;
-+              }
-+              break;
-+
-+            /* proxy-connection */
-+
-+            case h_matching_proxy_connection:
-+              parser->index++;
-+              if (parser->index > sizeof(PROXY_CONNECTION)-1
-+                  || c != PROXY_CONNECTION[parser->index]) {
-+                parser->header_state = h_general;
-+              } else if (parser->index == sizeof(PROXY_CONNECTION)-2) {
-+                parser->header_state = h_connection;
-+              }
-+              break;
-+
-+            /* content-length */
-+
-+            case h_matching_content_length:
-+              parser->index++;
-+              if (parser->index > sizeof(CONTENT_LENGTH)-1
-+                  || c != CONTENT_LENGTH[parser->index]) {
-+                parser->header_state = h_general;
-+              } else if (parser->index == sizeof(CONTENT_LENGTH)-2) {
-+                if (parser->flags & F_CONTENTLENGTH) {
-+                  SET_ERRNO(HPE_UNEXPECTED_CONTENT_LENGTH);
-+                  goto error;
-+                }
-+                parser->header_state = h_content_length;
-+                parser->flags |= F_CONTENTLENGTH;
-+              }
-+              break;
-+
-+            /* transfer-encoding */
-+
-+            case h_matching_transfer_encoding:
-+              parser->index++;
-+              if (parser->index > sizeof(TRANSFER_ENCODING)-1
-+                  || c != TRANSFER_ENCODING[parser->index]) {
-+                parser->header_state = h_general;
-+              } else if (parser->index == sizeof(TRANSFER_ENCODING)-2) {
-+                parser->header_state = h_transfer_encoding;
-+              }
-+              break;
-+
-+            /* upgrade */
-+
-+            case h_matching_upgrade:
-+              parser->index++;
-+              if (parser->index > sizeof(UPGRADE)-1
-+                  || c != UPGRADE[parser->index]) {
-+                parser->header_state = h_general;
-+              } else if (parser->index == sizeof(UPGRADE)-2) {
-+                parser->header_state = h_upgrade;
-+              }
-+              break;
-+
-+            case h_connection:
-+            case h_content_length:
-+            case h_transfer_encoding:
-+            case h_upgrade:
-+              if (ch != ' ') parser->header_state = h_general;
-+              break;
-+
-+            default:
-+              assert(0 && "Unknown header_state");
-+              break;
-+          }
-+        }
-+
-+        COUNT_HEADER_SIZE(p - start);
-+
-+        if (p == data + len) {
-+          --p;
-+          break;
-+        }
-+
-+        if (ch == ':') {
-+          UPDATE_STATE(s_header_value_discard_ws);
-+          CALLBACK_DATA(header_field);
-+          break;
-+        }
-+
-+        SET_ERRNO(HPE_INVALID_HEADER_TOKEN);
-+        goto error;
-+      }
-+
-+      case s_header_value_discard_ws:
-+        if (ch == ' ' || ch == '\t') break;
-+
-+        if (ch == CR) {
-+          UPDATE_STATE(s_header_value_discard_ws_almost_done);
-+          break;
-+        }
-+
-+        if (ch == LF) {
-+          UPDATE_STATE(s_header_value_discard_lws);
-+          break;
-+        }
-+
-+        /* FALLTHROUGH */
-+
-+      case s_header_value_start:
-+      {
-+        MARK(header_value);
-+
-+        UPDATE_STATE(s_header_value);
-+        parser->index = 0;
-+
-+        c = LOWER(ch);
-+
-+        switch (parser->header_state) {
-+          case h_upgrade:
-+            parser->flags |= F_UPGRADE;
-+            parser->header_state = h_general;
-+            break;
-+
-+          case h_transfer_encoding:
-+            /* looking for 'Transfer-Encoding: chunked' */
-+            if ('c' == c) {
-+              parser->header_state = h_matching_transfer_encoding_chunked;
-+            } else {
-+              parser->header_state = h_general;
-+            }
-+            break;
-+
-+          case h_content_length:
-+            if (UNLIKELY(!IS_NUM(ch))) {
-+              SET_ERRNO(HPE_INVALID_CONTENT_LENGTH);
-+              goto error;
-+            }
-+
-+            parser->content_length = ch - '0';
-+            break;
-+
-+          case h_connection:
-+            /* looking for 'Connection: keep-alive' */
-+            if (c == 'k') {
-+              parser->header_state = h_matching_connection_keep_alive;
-+            /* looking for 'Connection: close' */
-+            } else if (c == 'c') {
-+              parser->header_state = h_matching_connection_close;
-+            } else if (c == 'u') {
-+              parser->header_state = h_matching_connection_upgrade;
-+            } else {
-+              parser->header_state = h_matching_connection_token;
-+            }
-+            break;
-+
-+          /* Multi-value `Connection` header */
-+          case h_matching_connection_token_start:
-+            break;
-+
-+          default:
-+            parser->header_state = h_general;
-+            break;
-+        }
-+        break;
-+      }
-+
-+      case s_header_value:
-+      {
-+        const char* start = p;
-+        enum header_states h_state = (enum header_states) parser->header_state;
-+        for (; p != data + len; p++) {
-+          ch = *p;
-+          if (ch == CR) {
-+            UPDATE_STATE(s_header_almost_done);
-+            parser->header_state = h_state;
-+            CALLBACK_DATA(header_value);
-+            break;
-+          }
-+
-+          if (ch == LF) {
-+            UPDATE_STATE(s_header_almost_done);
-+            COUNT_HEADER_SIZE(p - start);
-+            parser->header_state = h_state;
-+            CALLBACK_DATA_NOADVANCE(header_value);
-+            REEXECUTE();
-+          }
-+
-+          if (!lenient && !IS_HEADER_CHAR(ch)) {
-+            SET_ERRNO(HPE_INVALID_HEADER_TOKEN);
-+            goto error;
-+          }
-+
-+          c = LOWER(ch);
-+
-+          switch (h_state) {
-+            case h_general:
-+            {
-+              const char* p_cr;
-+              const char* p_lf;
-+              size_t limit = data + len - p;
-+
-+              limit = MIN(limit, HTTP_MAX_HEADER_SIZE);
-+
-+              p_cr = (const char*) memchr(p, CR, limit);
-+              p_lf = (const char*) memchr(p, LF, limit);
-+              if (p_cr != NULL) {
-+                if (p_lf != NULL && p_cr >= p_lf)
-+                  p = p_lf;
-+                else
-+                  p = p_cr;
-+              } else if (UNLIKELY(p_lf != NULL)) {
-+                p = p_lf;
-+              } else {
-+                p = data + len;
-+              }
-+              --p;
-+
-+              break;
-+            }
-+
-+            case h_connection:
-+            case h_transfer_encoding:
-+              assert(0 && "Shouldn't get here.");
-+              break;
-+
-+            case h_content_length:
-+            {
-+              uint64_t t;
-+
-+              if (ch == ' ') break;
-+
-+              if (UNLIKELY(!IS_NUM(ch))) {
-+                SET_ERRNO(HPE_INVALID_CONTENT_LENGTH);
-+                parser->header_state = h_state;
-+                goto error;
-+              }
-+
-+              t = parser->content_length;
-+              t *= 10;
-+              t += ch - '0';
-+
-+              /* Overflow? Test against a conservative limit for simplicity. */
-+              if (UNLIKELY((ULLONG_MAX - 10) / 10 < parser->content_length)) {
-+                SET_ERRNO(HPE_INVALID_CONTENT_LENGTH);
-+                parser->header_state = h_state;
-+                goto error;
-+              }
-+
-+              parser->content_length = t;
-+              break;
-+            }
-+
-+            /* Transfer-Encoding: chunked */
-+            case h_matching_transfer_encoding_chunked:
-+              parser->index++;
-+              if (parser->index > sizeof(CHUNKED)-1
-+                  || c != CHUNKED[parser->index]) {
-+                h_state = h_general;
-+              } else if (parser->index == sizeof(CHUNKED)-2) {
-+                h_state = h_transfer_encoding_chunked;
-+              }
-+              break;
-+
-+            case h_matching_connection_token_start:
-+              /* looking for 'Connection: keep-alive' */
-+              if (c == 'k') {
-+                h_state = h_matching_connection_keep_alive;
-+              /* looking for 'Connection: close' */
-+              } else if (c == 'c') {
-+                h_state = h_matching_connection_close;
-+              } else if (c == 'u') {
-+                h_state = h_matching_connection_upgrade;
-+              } else if (STRICT_TOKEN(c)) {
-+                h_state = h_matching_connection_token;
-+              } else if (c == ' ' || c == '\t') {
-+                /* Skip lws */
-+              } else {
-+                h_state = h_general;
-+              }
-+              break;
-+
-+            /* looking for 'Connection: keep-alive' */
-+            case h_matching_connection_keep_alive:
-+              parser->index++;
-+              if (parser->index > sizeof(KEEP_ALIVE)-1
-+                  || c != KEEP_ALIVE[parser->index]) {
-+                h_state = h_matching_connection_token;
-+              } else if (parser->index == sizeof(KEEP_ALIVE)-2) {
-+                h_state = h_connection_keep_alive;
-+              }
-+              break;
-+
-+            /* looking for 'Connection: close' */
-+            case h_matching_connection_close:
-+              parser->index++;
-+              if (parser->index > sizeof(CLOSE)-1 || c != CLOSE[parser->index]) {
-+                h_state = h_matching_connection_token;
-+              } else if (parser->index == sizeof(CLOSE)-2) {
-+                h_state = h_connection_close;
-+              }
-+              break;
-+
-+            /* looking for 'Connection: upgrade' */
-+            case h_matching_connection_upgrade:
-+              parser->index++;
-+              if (parser->index > sizeof(UPGRADE) - 1 ||
-+                  c != UPGRADE[parser->index]) {
-+                h_state = h_matching_connection_token;
-+              } else if (parser->index == sizeof(UPGRADE)-2) {
-+                h_state = h_connection_upgrade;
-+              }
-+              break;
-+
-+            case h_matching_connection_token:
-+              if (ch == ',') {
-+                h_state = h_matching_connection_token_start;
-+                parser->index = 0;
-+              }
-+              break;
-+
-+            case h_transfer_encoding_chunked:
-+              if (ch != ' ') h_state = h_general;
-+              break;
-+
-+            case h_connection_keep_alive:
-+            case h_connection_close:
-+            case h_connection_upgrade:
-+              if (ch == ',') {
-+                if (h_state == h_connection_keep_alive) {
-+                  parser->flags |= F_CONNECTION_KEEP_ALIVE;
-+                } else if (h_state == h_connection_close) {
-+                  parser->flags |= F_CONNECTION_CLOSE;
-+                } else if (h_state == h_connection_upgrade) {
-+                  parser->flags |= F_CONNECTION_UPGRADE;
-+                }
-+                h_state = h_matching_connection_token_start;
-+                parser->index = 0;
-+              } else if (ch != ' ') {
-+                h_state = h_matching_connection_token;
-+              }
-+              break;
-+
-+            default:
-+              UPDATE_STATE(s_header_value);
-+              h_state = h_general;
-+              break;
-+          }
-+        }
-+        parser->header_state = h_state;
-+
-+        COUNT_HEADER_SIZE(p - start);
-+
-+        if (p == data + len)
-+          --p;
-+        break;
-+      }
-+
-+      case s_header_almost_done:
-+      {
-+        if (UNLIKELY(ch != LF)) {
-+          SET_ERRNO(HPE_LF_EXPECTED);
-+          goto error;
-+        }
-+
-+        UPDATE_STATE(s_header_value_lws);
-+        break;
-+      }
-+
-+      case s_header_value_lws:
-+      {
-+        if (ch == ' ' || ch == '\t') {
-+          UPDATE_STATE(s_header_value_start);
-+          REEXECUTE();
-+        }
-+
-+        /* finished the header */
-+        switch (parser->header_state) {
-+          case h_connection_keep_alive:
-+            parser->flags |= F_CONNECTION_KEEP_ALIVE;
-+            break;
-+          case h_connection_close:
-+            parser->flags |= F_CONNECTION_CLOSE;
-+            break;
-+          case h_transfer_encoding_chunked:
-+            parser->flags |= F_CHUNKED;
-+            break;
-+          case h_connection_upgrade:
-+            parser->flags |= F_CONNECTION_UPGRADE;
-+            break;
-+          default:
-+            break;
-+        }
-+
-+        UPDATE_STATE(s_header_field_start);
-+        REEXECUTE();
-+      }
-+
-+      case s_header_value_discard_ws_almost_done:
-+      {
-+        STRICT_CHECK(ch != LF);
-+        UPDATE_STATE(s_header_value_discard_lws);
-+        break;
-+      }
-+
-+      case s_header_value_discard_lws:
-+      {
-+        if (ch == ' ' || ch == '\t') {
-+          UPDATE_STATE(s_header_value_discard_ws);
-+          break;
-+        } else {
-+          switch (parser->header_state) {
-+            case h_connection_keep_alive:
-+              parser->flags |= F_CONNECTION_KEEP_ALIVE;
-+              break;
-+            case h_connection_close:
-+              parser->flags |= F_CONNECTION_CLOSE;
-+              break;
-+            case h_connection_upgrade:
-+              parser->flags |= F_CONNECTION_UPGRADE;
-+              break;
-+            case h_transfer_encoding_chunked:
-+              parser->flags |= F_CHUNKED;
-+              break;
-+            default:
-+              break;
-+          }
-+
-+          /* header value was empty */
-+          MARK(header_value);
-+          UPDATE_STATE(s_header_field_start);
-+          CALLBACK_DATA_NOADVANCE(header_value);
-+          REEXECUTE();
-+        }
-+      }
-+
-+      case s_headers_almost_done:
-+      {
-+        STRICT_CHECK(ch != LF);
-+
-+        if (parser->flags & F_TRAILING) {
-+          /* End of a chunked request */
-+          UPDATE_STATE(s_message_done);
-+          CALLBACK_NOTIFY_NOADVANCE(chunk_complete);
-+          REEXECUTE();
-+        }
-+
-+        /* Cannot use chunked encoding and a content-length header together
-+           per the HTTP specification. */
-+        if ((parser->flags & F_CHUNKED) &&
-+            (parser->flags & F_CONTENTLENGTH)) {
-+          SET_ERRNO(HPE_UNEXPECTED_CONTENT_LENGTH);
-+          goto error;
-+        }
-+
-+        UPDATE_STATE(s_headers_done);
-+
-+        /* Set this here so that on_headers_complete() callbacks can see it */
-+        parser->upgrade =
-+          ((parser->flags & (F_UPGRADE | F_CONNECTION_UPGRADE)) ==
-+           (F_UPGRADE | F_CONNECTION_UPGRADE) ||
-+           parser->method == HTTP_CONNECT);
-+
-+        /* Here we call the headers_complete callback. This is somewhat
-+         * different than other callbacks because if the user returns 1, we
-+         * will interpret that as saying that this message has no body. This
-+         * is needed for the annoying case of recieving a response to a HEAD
-+         * request.
-+         *
-+         * We'd like to use CALLBACK_NOTIFY_NOADVANCE() here but we cannot, so
-+         * we have to simulate it by handling a change in errno below.
-+         */
-+        if (settings->on_headers_complete) {
-+          switch (settings->on_headers_complete(parser)) {
-+            case 0:
-+              break;
-+
-+            case 2:
-+              parser->upgrade = 1;
-+
-+            case 1:
-+              parser->flags |= F_SKIPBODY;
-+              break;
-+
-+            default:
-+              SET_ERRNO(HPE_CB_headers_complete);
-+              RETURN(p - data); /* Error */
-+          }
-+        }
-+
-+        if (HTTP_PARSER_ERRNO(parser) != HPE_OK) {
-+          RETURN(p - data);
-+        }
-+
-+        REEXECUTE();
-+      }
-+
-+      case s_headers_done:
-+      {
-+        int hasBody;
-+        STRICT_CHECK(ch != LF);
-+
-+        parser->nread = 0;
-+
-+        hasBody = parser->flags & F_CHUNKED ||
-+          (parser->content_length > 0 && parser->content_length != ULLONG_MAX);
-+        if (parser->upgrade && (parser->method == HTTP_CONNECT ||
-+                                (parser->flags & F_SKIPBODY) || !hasBody)) {
-+          /* Exit, the rest of the message is in a different protocol. */
-+          UPDATE_STATE(NEW_MESSAGE());
-+          CALLBACK_NOTIFY(message_complete);
-+          RETURN((p - data) + 1);
-+        }
-+
-+        if (parser->flags & F_SKIPBODY) {
-+          UPDATE_STATE(NEW_MESSAGE());
-+          CALLBACK_NOTIFY(message_complete);
-+        } else if (parser->flags & F_CHUNKED) {
-+          /* chunked encoding - ignore Content-Length header */
-+          UPDATE_STATE(s_chunk_size_start);
-+        } else {
-+          if (parser->content_length == 0) {
-+            /* Content-Length header given but zero: Content-Length: 0\r\n */
-+            UPDATE_STATE(NEW_MESSAGE());
-+            CALLBACK_NOTIFY(message_complete);
-+          } else if (parser->content_length != ULLONG_MAX) {
-+            /* Content-Length header given and non-zero */
-+            UPDATE_STATE(s_body_identity);
-+          } else {
-+            if (!http_message_needs_eof(parser)) {
-+              /* Assume content-length 0 - read the next */
-+              UPDATE_STATE(NEW_MESSAGE());
-+              CALLBACK_NOTIFY(message_complete);
-+            } else {
-+              /* Read body until EOF */
-+              UPDATE_STATE(s_body_identity_eof);
-+            }
-+          }
-+        }
-+
-+        break;
-+      }
-+
-+      case s_body_identity:
-+      {
-+        uint64_t to_read = MIN(parser->content_length,
-+                               (uint64_t) ((data + len) - p));
-+
-+        assert(parser->content_length != 0
-+            && parser->content_length != ULLONG_MAX);
-+
-+        /* The difference between advancing content_length and p is because
-+         * the latter will automaticaly advance on the next loop iteration.
-+         * Further, if content_length ends up at 0, we want to see the last
-+         * byte again for our message complete callback.
-+         */
-+        MARK(body);
-+        parser->content_length -= to_read;
-+        p += to_read - 1;
-+
-+        if (parser->content_length == 0) {
-+          UPDATE_STATE(s_message_done);
-+
-+          /* Mimic CALLBACK_DATA_NOADVANCE() but with one extra byte.
-+           *
-+           * The alternative to doing this is to wait for the next byte to
-+           * trigger the data callback, just as in every other case. The
-+           * problem with this is that this makes it difficult for the test
-+           * harness to distinguish between complete-on-EOF and
-+           * complete-on-length. It's not clear that this distinction is
-+           * important for applications, but let's keep it for now.
-+           */
-+          CALLBACK_DATA_(body, p - body_mark + 1, p - data);
-+          REEXECUTE();
-+        }
-+
-+        break;
-+      }
-+
-+      /* read until EOF */
-+      case s_body_identity_eof:
-+        MARK(body);
-+        p = data + len - 1;
-+
-+        break;
-+
-+      case s_message_done:
-+        UPDATE_STATE(NEW_MESSAGE());
-+        CALLBACK_NOTIFY(message_complete);
-+        if (parser->upgrade) {
-+          /* Exit, the rest of the message is in a different protocol. */
-+          RETURN((p - data) + 1);
-+        }
-+        break;
-+
-+      case s_chunk_size_start:
-+      {
-+        assert(parser->nread == 1);
-+        assert(parser->flags & F_CHUNKED);
-+
-+        unhex_val = unhex[(unsigned char)ch];
-+        if (UNLIKELY(unhex_val == -1)) {
-+          SET_ERRNO(HPE_INVALID_CHUNK_SIZE);
-+          goto error;
-+        }
-+
-+        parser->content_length = unhex_val;
-+        UPDATE_STATE(s_chunk_size);
-+        break;
-+      }
-+
-+      case s_chunk_size:
-+      {
-+        uint64_t t;
-+
-+        assert(parser->flags & F_CHUNKED);
-+
-+        if (ch == CR) {
-+          UPDATE_STATE(s_chunk_size_almost_done);
-+          break;
-+        }
-+
-+        unhex_val = unhex[(unsigned char)ch];
-+
-+        if (unhex_val == -1) {
-+          if (ch == ';' || ch == ' ') {
-+            UPDATE_STATE(s_chunk_parameters);
-+            break;
-+          }
-+
-+          SET_ERRNO(HPE_INVALID_CHUNK_SIZE);
-+          goto error;
-+        }
-+
-+        t = parser->content_length;
-+        t *= 16;
-+        t += unhex_val;
-+
-+        /* Overflow? Test against a conservative limit for simplicity. */
-+        if (UNLIKELY((ULLONG_MAX - 16) / 16 < parser->content_length)) {
-+          SET_ERRNO(HPE_INVALID_CONTENT_LENGTH);
-+          goto error;
-+        }
-+
-+        parser->content_length = t;
-+        break;
-+      }
-+
-+      case s_chunk_parameters:
-+      {
-+        assert(parser->flags & F_CHUNKED);
-+        /* just ignore this shit. TODO check for overflow */
-+        if (ch == CR) {
-+          UPDATE_STATE(s_chunk_size_almost_done);
-+          break;
-+        }
-+        break;
-+      }
-+
-+      case s_chunk_size_almost_done:
-+      {
-+        assert(parser->flags & F_CHUNKED);
-+        STRICT_CHECK(ch != LF);
-+
-+        parser->nread = 0;
-+
-+        if (parser->content_length == 0) {
-+          parser->flags |= F_TRAILING;
-+          UPDATE_STATE(s_header_field_start);
-+        } else {
-+          UPDATE_STATE(s_chunk_data);
-+        }
-+        CALLBACK_NOTIFY(chunk_header);
-+        break;
-+      }
-+
-+      case s_chunk_data:
-+      {
-+        uint64_t to_read = MIN(parser->content_length,
-+                               (uint64_t) ((data + len) - p));
-+
-+        assert(parser->flags & F_CHUNKED);
-+        assert(parser->content_length != 0
-+            && parser->content_length != ULLONG_MAX);
-+
-+        /* See the explanation in s_body_identity for why the content
-+         * length and data pointers are managed this way.
-+         */
-+        MARK(body);
-+        parser->content_length -= to_read;
-+        p += to_read - 1;
-+
-+        if (parser->content_length == 0) {
-+          UPDATE_STATE(s_chunk_data_almost_done);
-+        }
-+
-+        break;
-+      }
-+
-+      case s_chunk_data_almost_done:
-+        assert(parser->flags & F_CHUNKED);
-+        assert(parser->content_length == 0);
-+        STRICT_CHECK(ch != CR);
-+        UPDATE_STATE(s_chunk_data_done);
-+        CALLBACK_DATA(body);
-+        break;
-+
-+      case s_chunk_data_done:
-+        assert(parser->flags & F_CHUNKED);
-+        STRICT_CHECK(ch != LF);
-+        parser->nread = 0;
-+        UPDATE_STATE(s_chunk_size_start);
-+        CALLBACK_NOTIFY(chunk_complete);
-+        break;
-+
-+      default:
-+        assert(0 && "unhandled state");
-+        SET_ERRNO(HPE_INVALID_INTERNAL_STATE);
-+        goto error;
-+    }
-+  }
-+
-+  /* Run callbacks for any marks that we have leftover after we ran our of
-+   * bytes. There should be at most one of these set, so it's OK to invoke
-+   * them in series (unset marks will not result in callbacks).
-+   *
-+   * We use the NOADVANCE() variety of callbacks here because 'p' has already
-+   * overflowed 'data' and this allows us to correct for the off-by-one that
-+   * we'd otherwise have (since CALLBACK_DATA() is meant to be run with a 'p'
-+   * value that's in-bounds).
-+   */
-+
-+  assert(((header_field_mark ? 1 : 0) +
-+          (header_value_mark ? 1 : 0) +
-+          (url_mark ? 1 : 0)  +
-+          (body_mark ? 1 : 0) +
-+          (status_mark ? 1 : 0)) <= 1);
-+
-+  CALLBACK_DATA_NOADVANCE(header_field);
-+  CALLBACK_DATA_NOADVANCE(header_value);
-+  CALLBACK_DATA_NOADVANCE(url);
-+  CALLBACK_DATA_NOADVANCE(body);
-+  CALLBACK_DATA_NOADVANCE(status);
-+
-+  RETURN(len);
-+
-+error:
-+  if (HTTP_PARSER_ERRNO(parser) == HPE_OK) {
-+    SET_ERRNO(HPE_UNKNOWN);
-+  }
-+
-+  RETURN(p - data);
-+}
-+
-+
-+/* Does the parser need to see an EOF to find the end of the message? */
-+int
-+http_message_needs_eof (const http_parser *parser)
-+{
-+  if (parser->type == HTTP_REQUEST) {
-+    return 0;
-+  }
-+
-+  /* See RFC 2616 section 4.4 */
-+  if (parser->status_code / 100 == 1 || /* 1xx e.g. Continue */
-+      parser->status_code == 204 ||     /* No Content */
-+      parser->status_code == 304 ||     /* Not Modified */
-+      parser->flags & F_SKIPBODY) {     /* response to a HEAD request */
-+    return 0;
-+  }
-+
-+  if ((parser->flags & F_CHUNKED) || parser->content_length != ULLONG_MAX) {
-+    return 0;
-+  }
-+
-+  return 1;
-+}
-+
-+
-+int
-+http_should_keep_alive (const http_parser *parser)
-+{
-+  if (parser->http_major > 0 && parser->http_minor > 0) {
-+    /* HTTP/1.1 */
-+    if (parser->flags & F_CONNECTION_CLOSE) {
-+      return 0;
-+    }
-+  } else {
-+    /* HTTP/1.0 or earlier */
-+    if (!(parser->flags & F_CONNECTION_KEEP_ALIVE)) {
-+      return 0;
-+    }
-+  }
-+
-+  return !http_message_needs_eof(parser);
-+}
-+
-+
-+const char *
-+http_method_str (enum http_method m)
-+{
-+  return ELEM_AT(method_strings, m, "<unknown>");
-+}
-+
-+
-+void
-+http_parser_init (http_parser *parser, enum http_parser_type t)
-+{
-+  void *data = parser->data; /* preserve application data */
-+  memset(parser, 0, sizeof(*parser));
-+  parser->data = data;
-+  parser->type = t;
-+  parser->state = (t == HTTP_REQUEST ? s_start_req : (t == HTTP_RESPONSE ? s_start_res : s_start_req_or_res));
-+  parser->http_errno = HPE_OK;
-+}
-+
-+void
-+http_parser_settings_init(http_parser_settings *settings)
-+{
-+  memset(settings, 0, sizeof(*settings));
-+}
-+
-+const char *
-+http_errno_name(enum http_errno err) {
-+  assert(((size_t) err) < ARRAY_SIZE(http_strerror_tab));
-+  return http_strerror_tab[err].name;
-+}
-+
-+const char *
-+http_errno_description(enum http_errno err) {
-+  assert(((size_t) err) < ARRAY_SIZE(http_strerror_tab));
-+  return http_strerror_tab[err].description;
-+}
-+
-+static enum http_host_state
-+http_parse_host_char(enum http_host_state s, const char ch) {
-+  switch(s) {
-+    case s_http_userinfo:
-+    case s_http_userinfo_start:
-+      if (ch == '@') {
-+        return s_http_host_start;
-+      }
-+
-+      if (IS_USERINFO_CHAR(ch)) {
-+        return s_http_userinfo;
-+      }
-+      break;
-+
-+    case s_http_host_start:
-+      if (ch == '[') {
-+        return s_http_host_v6_start;
-+      }
-+
-+      if (IS_HOST_CHAR(ch)) {
-+        return s_http_host;
-+      }
-+
-+      break;
-+
-+    case s_http_host:
-+      if (IS_HOST_CHAR(ch)) {
-+        return s_http_host;
-+      }
-+
-+    /* FALLTHROUGH */
-+    case s_http_host_v6_end:
-+      if (ch == ':') {
-+        return s_http_host_port_start;
-+      }
-+
-+      break;
-+
-+    case s_http_host_v6:
-+      if (ch == ']') {
-+        return s_http_host_v6_end;
-+      }
-+
-+    /* FALLTHROUGH */
-+    case s_http_host_v6_start:
-+      if (IS_HEX(ch) || ch == ':' || ch == '.') {
-+        return s_http_host_v6;
-+      }
-+
-+      if (s == s_http_host_v6 && ch == '%') {
-+        return s_http_host_v6_zone_start;
-+      }
-+      break;
-+
-+    case s_http_host_v6_zone:
-+      if (ch == ']') {
-+        return s_http_host_v6_end;
-+      }
-+
-+    /* FALLTHROUGH */
-+    case s_http_host_v6_zone_start:
-+      /* RFC 6874 Zone ID consists of 1*( unreserved / pct-encoded) */
-+      if (IS_ALPHANUM(ch) || ch == '%' || ch == '.' || ch == '-' || ch == '_' ||
-+          ch == '~') {
-+        return s_http_host_v6_zone;
-+      }
-+      break;
-+
-+    case s_http_host_port:
-+    case s_http_host_port_start:
-+      if (IS_NUM(ch)) {
-+        return s_http_host_port;
-+      }
-+
-+      break;
-+
-+    default:
-+      break;
-+  }
-+  return s_http_host_dead;
-+}
-+
-+static int
-+http_parse_host(const char * buf, struct http_parser_url *u, int found_at) {
-+  enum http_host_state s;
-+
-+  const char *p;
-+  size_t buflen = u->field_data[UF_HOST].off + u->field_data[UF_HOST].len;
-+
-+  assert(u->field_set & (1 << UF_HOST));
-+
-+  u->field_data[UF_HOST].len = 0;
-+
-+  s = found_at ? s_http_userinfo_start : s_http_host_start;
-+
-+  for (p = buf + u->field_data[UF_HOST].off; p < buf + buflen; p++) {
-+    enum http_host_state new_s = http_parse_host_char(s, *p);
-+
-+    if (new_s == s_http_host_dead) {
-+      return 1;
-+    }
-+
-+    switch(new_s) {
-+      case s_http_host:
-+        if (s != s_http_host) {
-+          u->field_data[UF_HOST].off = p - buf;
-+        }
-+        u->field_data[UF_HOST].len++;
-+        break;
-+
-+      case s_http_host_v6:
-+        if (s != s_http_host_v6) {
-+          u->field_data[UF_HOST].off = p - buf;
-+        }
-+        u->field_data[UF_HOST].len++;
-+        break;
-+
-+      case s_http_host_v6_zone_start:
-+      case s_http_host_v6_zone:
-+        u->field_data[UF_HOST].len++;
-+        break;
-+
-+      case s_http_host_port:
-+        if (s != s_http_host_port) {
-+          u->field_data[UF_PORT].off = p - buf;
-+          u->field_data[UF_PORT].len = 0;
-+          u->field_set |= (1 << UF_PORT);
-+        }
-+        u->field_data[UF_PORT].len++;
-+        break;
-+
-+      case s_http_userinfo:
-+        if (s != s_http_userinfo) {
-+          u->field_data[UF_USERINFO].off = p - buf ;
-+          u->field_data[UF_USERINFO].len = 0;
-+          u->field_set |= (1 << UF_USERINFO);
-+        }
-+        u->field_data[UF_USERINFO].len++;
-+        break;
-+
-+      default:
-+        break;
-+    }
-+    s = new_s;
-+  }
-+
-+  /* Make sure we don't end somewhere unexpected */
-+  switch (s) {
-+    case s_http_host_start:
-+    case s_http_host_v6_start:
-+    case s_http_host_v6:
-+    case s_http_host_v6_zone_start:
-+    case s_http_host_v6_zone:
-+    case s_http_host_port_start:
-+    case s_http_userinfo:
-+    case s_http_userinfo_start:
-+      return 1;
-+    default:
-+      break;
-+  }
-+
-+  return 0;
-+}
-+
-+void
-+http_parser_url_init(struct http_parser_url *u) {
-+  memset(u, 0, sizeof(*u));
-+}
-+
-+int
-+http_parser_parse_url(const char *buf, size_t buflen, int is_connect,
-+                      struct http_parser_url *u)
-+{
-+  enum state s;
-+  const char *p;
-+  enum http_parser_url_fields uf, old_uf;
-+  int found_at = 0;
-+
-+  u->port = u->field_set = 0;
-+  s = is_connect ? s_req_server_start : s_req_spaces_before_url;
-+  old_uf = UF_MAX;
-+
-+  for (p = buf; p < buf + buflen; p++) {
-+    s = parse_url_char(s, *p);
-+
-+    /* Figure out the next field that we're operating on */
-+    switch (s) {
-+      case s_dead:
-+        return 1;
-+
-+      /* Skip delimeters */
-+      case s_req_schema_slash:
-+      case s_req_schema_slash_slash:
-+      case s_req_server_start:
-+      case s_req_query_string_start:
-+      case s_req_fragment_start:
-+        continue;
-+
-+      case s_req_schema:
-+        uf = UF_SCHEMA;
-+        break;
-+
-+      case s_req_server_with_at:
-+        found_at = 1;
-+
-+      /* FALLTROUGH */
-+      case s_req_server:
-+        uf = UF_HOST;
-+        break;
-+
-+      case s_req_path:
-+        uf = UF_PATH;
-+        break;
-+
-+      case s_req_query_string:
-+        uf = UF_QUERY;
-+        break;
-+
-+      case s_req_fragment:
-+        uf = UF_FRAGMENT;
-+        break;
-+
-+      default:
-+        assert(!"Unexpected state");
-+        return 1;
-+    }
-+
-+    /* Nothing's changed; soldier on */
-+    if (uf == old_uf) {
-+      u->field_data[uf].len++;
-+      continue;
-+    }
-+
-+    u->field_data[uf].off = p - buf;
-+    u->field_data[uf].len = 1;
-+
-+    u->field_set |= (1 << uf);
-+    old_uf = uf;
-+  }
-+
-+  /* host must be present if there is a schema */
-+  /* parsing http:///toto will fail */
-+  if ((u->field_set & (1 << UF_SCHEMA)) &&
-+      (u->field_set & (1 << UF_HOST)) == 0) {
-+    return 1;
-+  }
-+
-+  if (u->field_set & (1 << UF_HOST)) {
-+    if (http_parse_host(buf, u, found_at) != 0) {
-+      return 1;
-+    }
-+  }
-+
-+  /* CONNECT requests can only contain "hostname:port" */
-+  if (is_connect && u->field_set != ((1 << UF_HOST)|(1 << UF_PORT))) {
-+    return 1;
-+  }
-+
-+  if (u->field_set & (1 << UF_PORT)) {
-+    /* Don't bother with endp; we've already validated the string */
-+    unsigned long v = strtoul(buf + u->field_data[UF_PORT].off, NULL, 10);
-+
-+    /* Ports have a max value of 2^16 */
-+    if (v > 0xffff) {
-+      return 1;
-+    }
-+
-+    u->port = (uint16_t) v;
-+  }
-+
-+  return 0;
-+}
-+
-+void
-+http_parser_pause(http_parser *parser, int paused) {
-+  /* Users should only be pausing/unpausing a parser that is not in an error
-+   * state. In non-debug builds, there's not much that we can do about this
-+   * other than ignore it.
-+   */
-+  if (HTTP_PARSER_ERRNO(parser) == HPE_OK ||
-+      HTTP_PARSER_ERRNO(parser) == HPE_PAUSED) {
-+    SET_ERRNO((paused) ? HPE_PAUSED : HPE_OK);
-+  } else {
-+    assert(0 && "Attempting to pause parser in error state");
-+  }
-+}
-+
-+int
-+http_body_is_final(const struct http_parser *parser) {
-+    return parser->state == s_message_done;
-+}
-+
-+unsigned long
-+http_parser_version(void) {
-+  return HTTP_PARSER_VERSION_MAJOR * 0x10000 |
-+         HTTP_PARSER_VERSION_MINOR * 0x00100 |
-+         HTTP_PARSER_VERSION_PATCH * 0x00001;
-+}
-diff --git a/src/responder/secrets/http_parser.h b/src/responder/secrets/http_parser.h
-new file mode 100644
-index 0000000000000000000000000000000000000000..105ae510a8ab57509de2c68bbe1504e39ffb165e
---- /dev/null
-+++ b/src/responder/secrets/http_parser.h
-@@ -0,0 +1,362 @@
-+/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
-+ *
-+ * Permission is hereby granted, free of charge, to any person obtaining a copy
-+ * of this software and associated documentation files (the "Software"), to
-+ * deal in the Software without restriction, including without limitation the
-+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
-+ * sell copies of the Software, and to permit persons to whom the Software is
-+ * furnished to do so, subject to the following conditions:
-+ *
-+ * The above copyright notice and this permission notice shall be included in
-+ * all copies or substantial portions of the Software.
-+ *
-+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
-+ * IN THE SOFTWARE.
-+ */
-+#ifndef http_parser_h
-+#define http_parser_h
-+#ifdef __cplusplus
-+extern "C" {
-+#endif
-+
-+/* Also update SONAME in the Makefile whenever you change these. */
-+#define HTTP_PARSER_VERSION_MAJOR 2
-+#define HTTP_PARSER_VERSION_MINOR 7
-+#define HTTP_PARSER_VERSION_PATCH 0
-+
-+#include <sys/types.h>
-+#if defined(_WIN32) && !defined(__MINGW32__) && \
-+  (!defined(_MSC_VER) || _MSC_VER<1600) && !defined(__WINE__)
-+#include <BaseTsd.h>
-+#include <stddef.h>
-+typedef __int8 int8_t;
-+typedef unsigned __int8 uint8_t;
-+typedef __int16 int16_t;
-+typedef unsigned __int16 uint16_t;
-+typedef __int32 int32_t;
-+typedef unsigned __int32 uint32_t;
-+typedef __int64 int64_t;
-+typedef unsigned __int64 uint64_t;
-+#else
-+#include <stdint.h>
-+#endif
-+
-+/* Compile with -DHTTP_PARSER_STRICT=0 to make less checks, but run
-+ * faster
-+ */
-+#ifndef HTTP_PARSER_STRICT
-+# define HTTP_PARSER_STRICT 1
-+#endif
-+
-+/* Maximium header size allowed. If the macro is not defined
-+ * before including this header then the default is used. To
-+ * change the maximum header size, define the macro in the build
-+ * environment (e.g. -DHTTP_MAX_HEADER_SIZE=<value>). To remove
-+ * the effective limit on the size of the header, define the macro
-+ * to a very large number (e.g. -DHTTP_MAX_HEADER_SIZE=0x7fffffff)
-+ */
-+#ifndef HTTP_MAX_HEADER_SIZE
-+# define HTTP_MAX_HEADER_SIZE (80*1024)
-+#endif
-+
-+typedef struct http_parser http_parser;
-+typedef struct http_parser_settings http_parser_settings;
-+
-+
-+/* Callbacks should return non-zero to indicate an error. The parser will
-+ * then halt execution.
-+ *
-+ * The one exception is on_headers_complete. In a HTTP_RESPONSE parser
-+ * returning '1' from on_headers_complete will tell the parser that it
-+ * should not expect a body. This is used when receiving a response to a
-+ * HEAD request which may contain 'Content-Length' or 'Transfer-Encoding:
-+ * chunked' headers that indicate the presence of a body.
-+ *
-+ * Returning `2` from on_headers_complete will tell parser that it should not
-+ * expect neither a body nor any futher responses on this connection. This is
-+ * useful for handling responses to a CONNECT request which may not contain
-+ * `Upgrade` or `Connection: upgrade` headers.
-+ *
-+ * http_data_cb does not return data chunks. It will be called arbitrarily
-+ * many times for each string. E.G. you might get 10 callbacks for "on_url"
-+ * each providing just a few characters more data.
-+ */
-+typedef int (*http_data_cb) (http_parser*, const char *at, size_t length);
-+typedef int (*http_cb) (http_parser*);
-+
-+
-+/* Request Methods */
-+#define HTTP_METHOD_MAP(XX)         \
-+  XX(0,  DELETE,      DELETE)       \
-+  XX(1,  GET,         GET)          \
-+  XX(2,  HEAD,        HEAD)         \
-+  XX(3,  POST,        POST)         \
-+  XX(4,  PUT,         PUT)          \
-+  /* pathological */                \
-+  XX(5,  CONNECT,     CONNECT)      \
-+  XX(6,  OPTIONS,     OPTIONS)      \
-+  XX(7,  TRACE,       TRACE)        \
-+  /* WebDAV */                      \
-+  XX(8,  COPY,        COPY)         \
-+  XX(9,  LOCK,        LOCK)         \
-+  XX(10, MKCOL,       MKCOL)        \
-+  XX(11, MOVE,        MOVE)         \
-+  XX(12, PROPFIND,    PROPFIND)     \
-+  XX(13, PROPPATCH,   PROPPATCH)    \
-+  XX(14, SEARCH,      SEARCH)       \
-+  XX(15, UNLOCK,      UNLOCK)       \
-+  XX(16, BIND,        BIND)         \
-+  XX(17, REBIND,      REBIND)       \
-+  XX(18, UNBIND,      UNBIND)       \
-+  XX(19, ACL,         ACL)          \
-+  /* subversion */                  \
-+  XX(20, REPORT,      REPORT)       \
-+  XX(21, MKACTIVITY,  MKACTIVITY)   \
-+  XX(22, CHECKOUT,    CHECKOUT)     \
-+  XX(23, MERGE,       MERGE)        \
-+  /* upnp */                        \
-+  XX(24, MSEARCH,     M-SEARCH)     \
-+  XX(25, NOTIFY,      NOTIFY)       \
-+  XX(26, SUBSCRIBE,   SUBSCRIBE)    \
-+  XX(27, UNSUBSCRIBE, UNSUBSCRIBE)  \
-+  /* RFC-5789 */                    \
-+  XX(28, PATCH,       PATCH)        \
-+  XX(29, PURGE,       PURGE)        \
-+  /* CalDAV */                      \
-+  XX(30, MKCALENDAR,  MKCALENDAR)   \
-+  /* RFC-2068, section 19.6.1.2 */  \
-+  XX(31, LINK,        LINK)         \
-+  XX(32, UNLINK,      UNLINK)       \
-+
-+enum http_method
-+  {
-+#define XX(num, name, string) HTTP_##name = num,
-+  HTTP_METHOD_MAP(XX)
-+#undef XX
-+  };
-+
-+
-+enum http_parser_type { HTTP_REQUEST, HTTP_RESPONSE, HTTP_BOTH };
-+
-+
-+/* Flag values for http_parser.flags field */
-+enum flags
-+  { F_CHUNKED               = 1 << 0
-+  , F_CONNECTION_KEEP_ALIVE = 1 << 1
-+  , F_CONNECTION_CLOSE      = 1 << 2
-+  , F_CONNECTION_UPGRADE    = 1 << 3
-+  , F_TRAILING              = 1 << 4
-+  , F_UPGRADE               = 1 << 5
-+  , F_SKIPBODY              = 1 << 6
-+  , F_CONTENTLENGTH         = 1 << 7
-+  };
-+
-+
-+/* Map for errno-related constants
-+ *
-+ * The provided argument should be a macro that takes 2 arguments.
-+ */
-+#define HTTP_ERRNO_MAP(XX)                                           \
-+  /* No error */                                                     \
-+  XX(OK, "success")                                                  \
-+                                                                     \
-+  /* Callback-related errors */                                      \
-+  XX(CB_message_begin, "the on_message_begin callback failed")       \
-+  XX(CB_url, "the on_url callback failed")                           \
-+  XX(CB_header_field, "the on_header_field callback failed")         \
-+  XX(CB_header_value, "the on_header_value callback failed")         \
-+  XX(CB_headers_complete, "the on_headers_complete callback failed") \
-+  XX(CB_body, "the on_body callback failed")                         \
-+  XX(CB_message_complete, "the on_message_complete callback failed") \
-+  XX(CB_status, "the on_status callback failed")                     \
-+  XX(CB_chunk_header, "the on_chunk_header callback failed")         \
-+  XX(CB_chunk_complete, "the on_chunk_complete callback failed")     \
-+                                                                     \
-+  /* Parsing-related errors */                                       \
-+  XX(INVALID_EOF_STATE, "stream ended at an unexpected time")        \
-+  XX(HEADER_OVERFLOW,                                                \
-+     "too many header bytes seen; overflow detected")                \
-+  XX(CLOSED_CONNECTION,                                              \
-+     "data received after completed connection: close message")      \
-+  XX(INVALID_VERSION, "invalid HTTP version")                        \
-+  XX(INVALID_STATUS, "invalid HTTP status code")                     \
-+  XX(INVALID_METHOD, "invalid HTTP method")                          \
-+  XX(INVALID_URL, "invalid URL")                                     \
-+  XX(INVALID_HOST, "invalid host")                                   \
-+  XX(INVALID_PORT, "invalid port")                                   \
-+  XX(INVALID_PATH, "invalid path")                                   \
-+  XX(INVALID_QUERY_STRING, "invalid query string")                   \
-+  XX(INVALID_FRAGMENT, "invalid fragment")                           \
-+  XX(LF_EXPECTED, "LF character expected")                           \
-+  XX(INVALID_HEADER_TOKEN, "invalid character in header")            \
-+  XX(INVALID_CONTENT_LENGTH,                                         \
-+     "invalid character in content-length header")                   \
-+  XX(UNEXPECTED_CONTENT_LENGTH,                                      \
-+     "unexpected content-length header")                             \
-+  XX(INVALID_CHUNK_SIZE,                                             \
-+     "invalid character in chunk size header")                       \
-+  XX(INVALID_CONSTANT, "invalid constant string")                    \
-+  XX(INVALID_INTERNAL_STATE, "encountered unexpected internal state")\
-+  XX(STRICT, "strict mode assertion failed")                         \
-+  XX(PAUSED, "parser is paused")                                     \
-+  XX(UNKNOWN, "an unknown error occurred")
-+
-+
-+/* Define HPE_* values for each errno value above */
-+#define HTTP_ERRNO_GEN(n, s) HPE_##n,
-+enum http_errno {
-+  HTTP_ERRNO_MAP(HTTP_ERRNO_GEN)
-+};
-+#undef HTTP_ERRNO_GEN
-+
-+
-+/* Get an http_errno value from an http_parser */
-+#define HTTP_PARSER_ERRNO(p)            ((enum http_errno) (p)->http_errno)
-+
-+
-+struct http_parser {
-+  /** PRIVATE **/
-+  unsigned int type : 2;         /* enum http_parser_type */
-+  unsigned int flags : 8;        /* F_* values from 'flags' enum; semi-public */
-+  unsigned int state : 7;        /* enum state from http_parser.c */
-+  unsigned int header_state : 7; /* enum header_state from http_parser.c */
-+  unsigned int index : 7;        /* index into current matcher */
-+  unsigned int lenient_http_headers : 1;
-+
-+  uint32_t nread;          /* # bytes read in various scenarios */
-+  uint64_t content_length; /* # bytes in body (0 if no Content-Length header) */
-+
-+  /** READ-ONLY **/
-+  unsigned short http_major;
-+  unsigned short http_minor;
-+  unsigned int status_code : 16; /* responses only */
-+  unsigned int method : 8;       /* requests only */
-+  unsigned int http_errno : 7;
-+
-+  /* 1 = Upgrade header was present and the parser has exited because of that.
-+   * 0 = No upgrade header present.
-+   * Should be checked when http_parser_execute() returns in addition to
-+   * error checking.
-+   */
-+  unsigned int upgrade : 1;
-+
-+  /** PUBLIC **/
-+  void *data; /* A pointer to get hook to the "connection" or "socket" object */
-+};
-+
-+
-+struct http_parser_settings {
-+  http_cb      on_message_begin;
-+  http_data_cb on_url;
-+  http_data_cb on_status;
-+  http_data_cb on_header_field;
-+  http_data_cb on_header_value;
-+  http_cb      on_headers_complete;
-+  http_data_cb on_body;
-+  http_cb      on_message_complete;
-+  /* When on_chunk_header is called, the current chunk length is stored
-+   * in parser->content_length.
-+   */
-+  http_cb      on_chunk_header;
-+  http_cb      on_chunk_complete;
-+};
-+
-+
-+enum http_parser_url_fields
-+  { UF_SCHEMA           = 0
-+  , UF_HOST             = 1
-+  , UF_PORT             = 2
-+  , UF_PATH             = 3
-+  , UF_QUERY            = 4
-+  , UF_FRAGMENT         = 5
-+  , UF_USERINFO         = 6
-+  , UF_MAX              = 7
-+  };
-+
-+
-+/* Result structure for http_parser_parse_url().
-+ *
-+ * Callers should index into field_data[] with UF_* values iff field_set
-+ * has the relevant (1 << UF_*) bit set. As a courtesy to clients (and
-+ * because we probably have padding left over), we convert any port to
-+ * a uint16_t.
-+ */
-+struct http_parser_url {
-+  uint16_t field_set;           /* Bitmask of (1 << UF_*) values */
-+  uint16_t port;                /* Converted UF_PORT string */
-+
-+  struct {
-+    uint16_t off;               /* Offset into buffer in which field starts */
-+    uint16_t len;               /* Length of run in buffer */
-+  } field_data[UF_MAX];
-+};
-+
-+
-+/* Returns the library version. Bits 16-23 contain the major version number,
-+ * bits 8-15 the minor version number and bits 0-7 the patch level.
-+ * Usage example:
-+ *
-+ *   unsigned long version = http_parser_version();
-+ *   unsigned major = (version >> 16) & 255;
-+ *   unsigned minor = (version >> 8) & 255;
-+ *   unsigned patch = version & 255;
-+ *   printf("http_parser v%u.%u.%u\n", major, minor, patch);
-+ */
-+unsigned long http_parser_version(void);
-+
-+void http_parser_init(http_parser *parser, enum http_parser_type type);
-+
-+
-+/* Initialize http_parser_settings members to 0
-+ */
-+void http_parser_settings_init(http_parser_settings *settings);
-+
-+
-+/* Executes the parser. Returns number of parsed bytes. Sets
-+ * `parser->http_errno` on error. */
-+size_t http_parser_execute(http_parser *parser,
-+                           const http_parser_settings *settings,
-+                           const char *data,
-+                           size_t len);
-+
-+
-+/* If http_should_keep_alive() in the on_headers_complete or
-+ * on_message_complete callback returns 0, then this should be
-+ * the last message on the connection.
-+ * If you are the server, respond with the "Connection: close" header.
-+ * If you are the client, close the connection.
-+ */
-+int http_should_keep_alive(const http_parser *parser);
-+
-+/* Returns a string version of the HTTP method. */
-+const char *http_method_str(enum http_method m);
-+
-+/* Return a string name of the given error */
-+const char *http_errno_name(enum http_errno err);
-+
-+/* Return a string description of the given error */
-+const char *http_errno_description(enum http_errno err);
-+
-+/* Initialize all http_parser_url members to 0 */
-+void http_parser_url_init(struct http_parser_url *u);
-+
-+/* Parse a URL; return nonzero on failure */
-+int http_parser_parse_url(const char *buf, size_t buflen,
-+                          int is_connect,
-+                          struct http_parser_url *u);
-+
-+/* Pause or un-pause the parser; a nonzero value pauses */
-+void http_parser_pause(http_parser *parser, int paused);
-+
-+/* Checks if this is the final chunk of the body. */
-+int http_body_is_final(const http_parser *parser);
-+
-+#ifdef __cplusplus
-+}
-+#endif
-+#endif
-diff --git a/src/responder/secrets/secsrv_private.h b/src/responder/secrets/secsrv_private.h
-index c3b663996e138bbb14f1ab74db75398ffd0bd5c7..47ed7642cf8da5ecfc8f995199746596a12b3e1d 100644
---- a/src/responder/secrets/secsrv_private.h
-+++ b/src/responder/secrets/secsrv_private.h
-@@ -25,7 +25,7 @@
- #include "config.h"
- #include "responder/common/responder.h"
- #include "responder/secrets/secsrv.h"
--#include <http_parser.h>
-+#include "http_parser.h"
- 
- struct sec_kvp {
-     char *name;
--- 
-2.4.11
-
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
new file mode 100644
index 0000000..d53da7d
--- /dev/null
+++ b/SOURCES/0009-sss_cert_derb64_to_ldap_filter-add-sss_certmap-suppo.patch
@@ -0,0 +1,235 @@
+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-MAN-Update-description-of-sssctl.patch b/SOURCES/0010-MAN-Update-description-of-sssctl.patch
deleted file mode 100644
index 4e5482c..0000000
--- a/SOURCES/0010-MAN-Update-description-of-sssctl.patch
+++ /dev/null
@@ -1,48 +0,0 @@
-From 41a8e0f463188dd6a78d44908a8601c7c7576b59 Mon Sep 17 00:00:00 2001
-From: Dan Lavu <dlavu@redhat.com>
-Date: Mon, 11 Jul 2016 12:27:34 +0200
-Subject: [PATCH 10/14] MAN: Update description of 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 8ad1883c79fc1e356bbd2d3e4badd4af9955b9fc)
----
- src/man/sssctl.8.xml | 13 ++++++-------
- 1 file changed, 6 insertions(+), 7 deletions(-)
-
-diff --git a/src/man/sssctl.8.xml b/src/man/sssctl.8.xml
-index 721d0a2f7558b105c22135388757f92270265c4a..cbb632b68b145680ab94523b90180c5f76d69eb9 100644
---- a/src/man/sssctl.8.xml
-+++ b/src/man/sssctl.8.xml
-@@ -29,12 +29,11 @@
-     <refsect1 id='description'>
-         <title>DESCRIPTION</title>
-         <para>
--            <command>sssctl</command> provides simple and unified way to obtain
--            information about SSSD status such as active server or list of
--            auto-discovered servers and domains or information about cached
--            objects. It also provides tools to manage SSSD data files during
--            troubleshooting such as a safe way to remove cache files or fetching
--            all SSSD log files and more.
-+            <command>sssctl</command> provides a simple and unified way
-+            to obtain information about SSSD status, such as active server,
-+            auto-discovered servers, domains and cached objects. In addition,
-+            it can manage SSSD data files for troubleshooting in such a way
-+            that is safe to manipulate while SSSD is running.
-         </para>
-     </refsect1>
- 
-@@ -42,7 +41,7 @@
-         <title>AVAILABLE COMMANDS</title>
-         <para>
-             To list all available commands run <command>sssctl</command>
--            without any parameter. To print help for selected command
-+            without any parameters. To print help for selected command
-             run <command>sssctl COMMAND --help</command>.
-         </para>
-     </refsect1>
--- 
-2.4.11
-
diff --git a/SOURCES/0010-sysdb-add-certmap-related-calls.patch b/SOURCES/0010-sysdb-add-certmap-related-calls.patch
new file mode 100644
index 0000000..5b33d5f
--- /dev/null
+++ b/SOURCES/0010-sysdb-add-certmap-related-calls.patch
@@ -0,0 +1,846 @@
+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
new file mode 100644
index 0000000..d2b8ff6
--- /dev/null
+++ b/SOURCES/0011-IPA-add-certmap-support.patch
@@ -0,0 +1,484 @@
+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-nss-srv-tests-Fix-prototype-of-wrapped-ncache-functi.patch b/SOURCES/0011-nss-srv-tests-Fix-prototype-of-wrapped-ncache-functi.patch
deleted file mode 100644
index eafe318..0000000
--- a/SOURCES/0011-nss-srv-tests-Fix-prototype-of-wrapped-ncache-functi.patch
+++ /dev/null
@@ -1,101 +0,0 @@
-From 721f83fa5661c861674c17edbb309bb9ade15892 Mon Sep 17 00:00:00 2001
-From: Lukas Slebodnik <lslebodn@redhat.com>
-Date: Mon, 4 Jul 2016 14:08:46 +0200
-Subject: [PATCH 11/14] nss-srv-tests: Fix prototype of wrapped ncache
- functions
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-The argument ttl was recently removed from negative cache functions
-(sss_ncache_check_user, sss_ncache_check_uid, sss_ncache_check_sid,
-sss_ncache_check_cert) but it was not removed from wrapped versions
-in nss-srv-tests. It caused a crash on machine with big endian
-and when configure wih --coverage.
-
-Reviewed-by: Pavel Březina <pbrezina@redhat.com>
-(cherry picked from commit 35567de112cd5d82acb582cbdb44c8652bbdfda1)
----
- src/tests/cmocka/test_nss_srv.c | 28 ++++++++++++----------------
- 1 file changed, 12 insertions(+), 16 deletions(-)
-
-diff --git a/src/tests/cmocka/test_nss_srv.c b/src/tests/cmocka/test_nss_srv.c
-index 5e0398f20b9906df39371826e50a9c78675dfa74..4137e9151be561a57a8f2e674f385ecb37119255 100644
---- a/src/tests/cmocka/test_nss_srv.c
-+++ b/src/tests/cmocka/test_nss_srv.c
-@@ -151,60 +151,56 @@ int __wrap_sss_cmd_send_empty(struct cli_ctx *cctx, TALLOC_CTX *freectx)
- }
- 
- /* Intercept negative cache lookups */
--int __real_sss_ncache_check_user(struct sss_nc_ctx *ctx, int ttl,
-+int __real_sss_ncache_check_user(struct sss_nc_ctx *ctx,
-                                  struct sss_domain_info *dom, const char *name);
- 
--int __wrap_sss_ncache_check_user(struct sss_nc_ctx *ctx, int ttl,
-+int __wrap_sss_ncache_check_user(struct sss_nc_ctx *ctx,
-                                  struct sss_domain_info *dom, const char *name)
- {
-     int ret;
- 
--    ret = __real_sss_ncache_check_user(ctx, ttl, dom, name);
-+    ret = __real_sss_ncache_check_user(ctx, dom, name);
-     if (ret == EEXIST) {
-         nss_test_ctx->ncache_hits++;
-     }
-     return ret;
- }
- 
--int __real_sss_ncache_check_uid(struct sss_nc_ctx *ctx, int ttl,
-+int __real_sss_ncache_check_uid(struct sss_nc_ctx *ctx,
-                                 struct sss_domain_info *dom, uid_t uid);
- 
--int __wrap_sss_ncache_check_uid(struct sss_nc_ctx *ctx, int ttl,
-+int __wrap_sss_ncache_check_uid(struct sss_nc_ctx *ctx,
-                                 struct sss_domain_info *dom, uid_t uid)
- {
-     int ret;
- 
--    ret = __real_sss_ncache_check_uid(ctx, ttl, dom, uid);
-+    ret = __real_sss_ncache_check_uid(ctx, dom, uid);
-     if (ret == EEXIST) {
-         nss_test_ctx->ncache_hits++;
-     }
-     return ret;
- }
- 
--int __real_sss_ncache_check_sid(struct sss_nc_ctx *ctx,
--                                int ttl, const char *sid);
-+int __real_sss_ncache_check_sid(struct sss_nc_ctx *ctx, const char *sid);
- 
--int __wrap_sss_ncache_check_sid(struct sss_nc_ctx *ctx,
--                                int ttl, const char *sid)
-+int __wrap_sss_ncache_check_sid(struct sss_nc_ctx *ctx, const char *sid)
- {
-     int ret;
- 
--    ret = __real_sss_ncache_check_sid(ctx, ttl, sid);
-+    ret = __real_sss_ncache_check_sid(ctx, sid);
-     if (ret == EEXIST) {
-         nss_test_ctx->ncache_hits++;
-     }
-     return ret;
- }
- 
--int __real_sss_ncache_check_cert(struct sss_nc_ctx *ctx,
--                                 int ttl, const char *cert);
-+int __real_sss_ncache_check_cert(struct sss_nc_ctx *ctx, const char *cert);
- 
--int __wrap_sss_ncache_check_cert(struct sss_nc_ctx *ctx,
--                                 int ttl, const char *cert)
-+int __wrap_sss_ncache_check_cert(struct sss_nc_ctx *ctx, const char *cert)
- {
-     int ret;
- 
--    ret = __real_sss_ncache_check_cert(ctx, ttl, cert);
-+    ret = __real_sss_ncache_check_cert(ctx, cert);
-     if (ret == EEXIST) {
-         nss_test_ctx->ncache_hits++;
-     }
--- 
-2.4.11
-
diff --git a/SOURCES/0012-TOOLS-Prevent-dereference-of-null-pointer.patch b/SOURCES/0012-TOOLS-Prevent-dereference-of-null-pointer.patch
deleted file mode 100644
index 8ff9bfb..0000000
--- a/SOURCES/0012-TOOLS-Prevent-dereference-of-null-pointer.patch
+++ /dev/null
@@ -1,139 +0,0 @@
-From be811502403246414b99f3a8834355c53f6f0511 Mon Sep 17 00:00:00 2001
-From: Lukas Slebodnik <lslebodn@redhat.com>
-Date: Mon, 6 Jun 2016 18:15:44 +0200
-Subject: [PATCH 12/14] TOOLS: Prevent dereference of null pointer
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-VAR_CHECK is called with (var, EOK, ...)
-EOK would be returned in case of "var != EOK"
-and output argument _attrs would not be initialized.
-Therefore there could be dereference of null pointer
-after calling function usermod_build_attrs.
-
-Reviewed-by: Pavel Březina <pbrezina@redhat.com>
-(cherry picked from commit f9d3aec54d19a771a6eafe09ba6d445cc094bfae)
----
- src/tools/sss_sync_ops.c | 63 +++++++++++++++++++++---------------------------
- 1 file changed, 28 insertions(+), 35 deletions(-)
-
-diff --git a/src/tools/sss_sync_ops.c b/src/tools/sss_sync_ops.c
-index 7f2e3ea85d5874e3c40f53f327b400e38e430228..a23a0b8c30366d2fb68554bfed184b8fce675e2b 100644
---- a/src/tools/sss_sync_ops.c
-+++ b/src/tools/sss_sync_ops.c
-@@ -37,13 +37,6 @@
- #define ATTR_NAME_SEP      '='
- #define ATTR_VAL_SEP       ','
- 
--#define VAR_CHECK(var, val, attr, msg) do { \
--        if (var != (val)) { \
--            DEBUG(SSSDBG_CRIT_FAILURE, msg" attribute: %s\n", attr); \
--            return val; \
--        } \
--} while(0)
--
- static int attr_name_val_split(TALLOC_CTX *mem_ctx, const char *nameval,
-                                char **_name, char ***_values, int *_nvals)
- {
-@@ -200,8 +193,9 @@ static int usermod_build_attrs(TALLOC_CTX *mem_ctx,
-                                int lock,
-                                struct sysdb_attrs **_attrs)
- {
--    int ret;
-+    int ret = EOK;
-     struct sysdb_attrs *attrs;
-+    const char *attr_name = NULL;
- 
-     attrs = sysdb_new_attrs(mem_ctx);
-     if (attrs == NULL) {
-@@ -209,60 +203,59 @@ static int usermod_build_attrs(TALLOC_CTX *mem_ctx,
-     }
- 
-     if (shell) {
-+        attr_name = SYSDB_SHELL;
-         ret = sysdb_attrs_add_string(attrs,
--                                     SYSDB_SHELL,
-+                                     attr_name,
-                                      shell);
--        VAR_CHECK(ret, EOK, SYSDB_SHELL,
--                  "Could not add attribute to changeset\n");
-     }
- 
--    if (home) {
-+    if (ret == EOK && home) {
-+        attr_name = SYSDB_HOMEDIR;
-         ret = sysdb_attrs_add_string(attrs,
--                                     SYSDB_HOMEDIR,
-+                                     attr_name,
-                                      home);
--        VAR_CHECK(ret, EOK, SYSDB_HOMEDIR,
--                  "Could not add attribute to changeset\n");
-     }
- 
--    if (gecos) {
-+    if (ret == EOK && gecos) {
-+        attr_name = SYSDB_GECOS;
-         ret = sysdb_attrs_add_string(attrs,
--                                     SYSDB_GECOS,
-+                                     attr_name,
-                                      gecos);
--        VAR_CHECK(ret, EOK, SYSDB_GECOS,
--                  "Could not add attribute to changeset\n");
-     }
- 
--    if (uid) {
-+    if (ret == EOK && uid) {
-+        attr_name = SYSDB_UIDNUM;
-         ret = sysdb_attrs_add_long(attrs,
--                                   SYSDB_UIDNUM,
-+                                   attr_name,
-                                    uid);
--        VAR_CHECK(ret, EOK, SYSDB_UIDNUM,
--                  "Could not add attribute to changeset\n");
-     }
- 
--    if (gid) {
-+    if (ret == EOK && gid) {
-+        attr_name = SYSDB_GIDNUM;
-         ret = sysdb_attrs_add_long(attrs,
--                                   SYSDB_GIDNUM,
-+                                   attr_name,
-                                    gid);
--        VAR_CHECK(ret, EOK, SYSDB_GIDNUM,
--                  "Could not add attribute to changeset\n");
-     }
- 
--    if (lock == DO_LOCK) {
-+    if (ret == EOK && lock == DO_LOCK) {
-+        attr_name = SYSDB_DISABLED;
-         ret = sysdb_attrs_add_string(attrs,
--                                     SYSDB_DISABLED,
-+                                     attr_name,
-                                      "true");
--        VAR_CHECK(ret, EOK, SYSDB_DISABLED,
--                  "Could not add attribute to changeset\n");
-     }
- 
--    if (lock == DO_UNLOCK) {
-+    if (ret == EOK && lock == DO_UNLOCK) {
-+        attr_name = SYSDB_DISABLED;
-         /* PAM code checks for 'false' value in SYSDB_DISABLED attribute */
-         ret = sysdb_attrs_add_string(attrs,
--                                     SYSDB_DISABLED,
-+                                     attr_name,
-                                      "false");
--        VAR_CHECK(ret, EOK, SYSDB_DISABLED,
--                  "Could not add attribute to changeset\n");
-+    }
-+
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_CRIT_FAILURE,
-+              "Could not add attribute [%s] to changeset.\n", attr_name);
-+        return ret;
-     }
- 
-     *_attrs = attrs;
--- 
-2.4.11
-
diff --git a/SOURCES/0012-nss-idmap-add-sss_nss_getlistbycert.patch b/SOURCES/0012-nss-idmap-add-sss_nss_getlistbycert.patch
new file mode 100644
index 0000000..f879ce1
--- /dev/null
+++ b/SOURCES/0012-nss-idmap-add-sss_nss_getlistbycert.patch
@@ -0,0 +1,736 @@
+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/0013-config-override_space-is-monitor-s-option.patch b/SOURCES/0013-config-override_space-is-monitor-s-option.patch
deleted file mode 100644
index f9d2ccd..0000000
--- a/SOURCES/0013-config-override_space-is-monitor-s-option.patch
+++ /dev/null
@@ -1,100 +0,0 @@
-From 3904a70f86d793954822abc543783d57fb36c8ea Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Michal=20=C5=BDidek?= <mzidek@redhat.com>
-Date: Mon, 11 Jul 2016 13:11:41 +0200
-Subject: [PATCH 13/14] config: override_space is monitor's option
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-We read override_space from [sssd] not
-[nss] section.
-
-Resolves:
-https://fedorahosted.org/sssd/ticket/3068
-
-Reviewed-by: Lukáš Slebodník <lslebodn@redhat.com>
-(cherry picked from commit fc04d11c2fdde0bfe280c6030df2b1d6bf15ce63)
----
- src/config/SSSDConfig/__init__.py.in | 2 +-
- src/config/SSSDConfigTest.py         | 3 ++-
- src/config/cfg_rules.ini             | 2 +-
- src/config/etc/sssd.api.conf         | 2 +-
- 4 files changed, 5 insertions(+), 4 deletions(-)
-
-diff --git a/src/config/SSSDConfig/__init__.py.in b/src/config/SSSDConfig/__init__.py.in
-index 52af1386c7b6fc858b27d6f1d9197a7906519fc1..ebdd049e4df7ac2349293d6ce3802e7349098273 100644
---- a/src/config/SSSDConfig/__init__.py.in
-+++ b/src/config/SSSDConfig/__init__.py.in
-@@ -63,6 +63,7 @@ option_strings = {
-     'default_domain_suffix' : _('Domain to add to names without a domain component.'),
-     'user' : _('The user to drop privileges to'),
-     'certificate_verification' : _('Tune certificate verification'),
-+    'override_space': _('All spaces in group or user names will be replaced with this character'),
- 
-     # [nss]
-     'enum_cache_timeout' : _('Enumeration cache timeout length (seconds)'),
-@@ -81,7 +82,6 @@ option_strings = {
-     'shell_fallback' : _('If a shell stored in central directory is allowed but not available, use this fallback'),
-     'default_shell': _('Shell to use if the provider does not list one'),
-     'memcache_timeout': _('How long will be in-memory cache records valid'),
--    'override_space': _('All spaces in group or user names will be replaced with this character'),
- 
-     # [pam]
-     'offline_credentials_expiration' : _('How long to allow cached logins between online logins (days)'),
-diff --git a/src/config/SSSDConfigTest.py b/src/config/SSSDConfigTest.py
-index 6ec30234e24b7b48ccab6a98e1f9396990509190..5fa9bce8e6502c0ebf42671058c5e3d5de00ea0d 100755
---- a/src/config/SSSDConfigTest.py
-+++ b/src/config/SSSDConfigTest.py
-@@ -310,7 +310,8 @@ class SSSDConfigTestSSSDService(unittest.TestCase):
-             'client_idle_timeout',
-             'diag_cmd',
-             'description',
--            'certificate_verification']
-+            'certificate_verification',
-+            'override_space']
- 
-         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 d738ddf5ac5c220dbf2c3c99782368c684072e3f..ae4a9af2cdfd622e1234e26ae7285ff4e47889dc 100644
---- a/src/config/cfg_rules.ini
-+++ b/src/config/cfg_rules.ini
-@@ -38,6 +38,7 @@ option = krb5_rcache_dir
- option = user
- option = default_domain_suffix
- option = certificate_verification
-+option = override_space
- 
- [rule/allowed_nss_options]
- validator = ini_allowed_options
-@@ -75,7 +76,6 @@ option = shell_fallback
- option = default_shell
- option = get_domains_timeout
- option = memcache_timeout
--option = override_space
- 
- [rule/allowed_pam_options]
- validator = ini_allowed_options
-diff --git a/src/config/etc/sssd.api.conf b/src/config/etc/sssd.api.conf
-index 91146593d7fcda2930ff85e8ee3e889cc12af962..df6bdeb392b33a1437d790027054ee5e7b33e724 100644
---- a/src/config/etc/sssd.api.conf
-+++ b/src/config/etc/sssd.api.conf
-@@ -28,6 +28,7 @@ krb5_rcache_dir = str, None, false
- user = str, None, false
- default_domain_suffix = str, None, false
- certificate_verification = str, None, false
-+override_space = str, None, false
- 
- [nss]
- # Name service
-@@ -49,7 +50,6 @@ shell_fallback = str, None, false
- default_shell = str, None, false
- get_domains_timeout = int, None, false
- memcache_timeout = int, None, false
--override_space = str, None, false
- 
- [pam]
- # Authentication service
--- 
-2.4.11
-
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
new file mode 100644
index 0000000..d0dea12
--- /dev/null
+++ b/SOURCES/0013-nss-allow-larger-buffer-for-certificate-based-reques.patch
@@ -0,0 +1,70 @@
+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
new file mode 100644
index 0000000..287bfdf
--- /dev/null
+++ b/SOURCES/0014-IPA-Add-s2n-request-to-string-function.patch
@@ -0,0 +1,44 @@
+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-config-Fix-user_attributes.patch b/SOURCES/0014-config-Fix-user_attributes.patch
deleted file mode 100644
index df2c8e3..0000000
--- a/SOURCES/0014-config-Fix-user_attributes.patch
+++ /dev/null
@@ -1,77 +0,0 @@
-From 08f1019768f6fa569eccf4892e3b5b57372b971d Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Michal=20=C5=BDidek?= <mzidek@redhat.com>
-Date: Mon, 11 Jul 2016 13:23:40 +0200
-Subject: [PATCH 14/14] config: Fix user_attributes
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Fixes:
-https://fedorahosted.org/sssd/ticket/3068
-
-Option user_attributes is also available in
-NSS responder, but not in PAC responder.
-
-Reviewed-by: Lukáš Slebodník <lslebodn@redhat.com>
-(cherry picked from commit 0a172552ec16f3b84d127399551cad786da8fd9d)
----
- src/config/SSSDConfig/__init__.py.in | 1 +
- src/config/cfg_rules.ini             | 2 +-
- src/config/etc/sssd.api.conf         | 2 +-
- 3 files changed, 3 insertions(+), 2 deletions(-)
-
-diff --git a/src/config/SSSDConfig/__init__.py.in b/src/config/SSSDConfig/__init__.py.in
-index ebdd049e4df7ac2349293d6ce3802e7349098273..b5e078d0118a15c10b43fbe050176943ec90e0ee 100644
---- a/src/config/SSSDConfig/__init__.py.in
-+++ b/src/config/SSSDConfig/__init__.py.in
-@@ -82,6 +82,7 @@ option_strings = {
-     'shell_fallback' : _('If a shell stored in central directory is allowed but not available, use this fallback'),
-     'default_shell': _('Shell to use if the provider does not list one'),
-     'memcache_timeout': _('How long will be in-memory cache records valid'),
-+    'user_attributes': _('List of user attributes the NSS responder is allowed to publish'),
- 
-     # [pam]
-     'offline_credentials_expiration' : _('How long to allow cached logins between online logins (days)'),
-diff --git a/src/config/cfg_rules.ini b/src/config/cfg_rules.ini
-index ae4a9af2cdfd622e1234e26ae7285ff4e47889dc..85a15be3493cf4b8c5a612b0f66ae4c86d39b1ab 100644
---- a/src/config/cfg_rules.ini
-+++ b/src/config/cfg_rules.ini
-@@ -58,6 +58,7 @@ option = description
- option = diag_cmd
- 
- # Name service
-+option = user_attributes
- option = enum_cache_timeout
- option = entry_cache_nowait_percentage
- option = entry_negative_timeout
-@@ -192,7 +193,6 @@ option = diag_cmd
- 
- # PAC responder
- option = allowed_uids
--option = user_attributes
- option = pac_lifetime
- 
- [rule/allowed_ifp_options]
-diff --git a/src/config/etc/sssd.api.conf b/src/config/etc/sssd.api.conf
-index df6bdeb392b33a1437d790027054ee5e7b33e724..2d7c5049f5e5bf9df6e5445ee6e5c62211bf1c45 100644
---- a/src/config/etc/sssd.api.conf
-+++ b/src/config/etc/sssd.api.conf
-@@ -50,6 +50,7 @@ shell_fallback = str, None, false
- default_shell = str, None, false
- get_domains_timeout = int, None, false
- memcache_timeout = int, None, false
-+user_attributes = str, None, false
- 
- [pam]
- # Authentication service
-@@ -86,7 +87,6 @@ ca_db = str, None, false
- [pac]
- # PAC responder
- allowed_uids = str, None, false
--user_attributes = str, None, false
- pac_lifetime = int, None, false
- 
- [ifp]
--- 
-2.4.11
-
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
new file mode 100644
index 0000000..8031b4c
--- /dev/null
+++ b/SOURCES/0015-IPA-Enhance-debug-logging-for-ipa-s2n-operations.patch
@@ -0,0 +1,82 @@
+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/0015-sysdb-tests-Fix-cast-from-pointer-to-integer.patch b/SOURCES/0015-sysdb-tests-Fix-cast-from-pointer-to-integer.patch
deleted file mode 100644
index 47af5b0..0000000
--- a/SOURCES/0015-sysdb-tests-Fix-cast-from-pointer-to-integer.patch
+++ /dev/null
@@ -1,42 +0,0 @@
-From 7b8c9b01882788d5112b0a529bf74163805889d3 Mon Sep 17 00:00:00 2001
-From: Lukas Slebodnik <lslebodn@redhat.com>
-Date: Fri, 8 Jul 2016 13:37:10 +0200
-Subject: [PATCH 15/18] sysdb-tests: Fix cast from pointer to integer
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-src/tests/sysdb-tests.c: In function 'test_sysdb_memberof_close_loop':
-src/tests/sysdb-tests.c:2740:5: warning: passing argument
-  1 of '_ck_assert_msg' makes integer from pointer without a cast
-  [enabled by default]
-     fail_unless(data->attrlist[0], "talloc_array failed.");
-     ^
-In file included from src/tests/sysdb-tests.c:23:0:
-/usr/include/check.h:237:16: note: expected 'int' but argument
- is of type 'const char *'
-   void CK_EXPORT _ck_assert_msg (int result, const char *file,
-                  ^
-Reviewed-by: Pavel Březina <pbrezina@redhat.com>
-
-(cherry picked from commit 2bb9e88328ef44eddd935c250ae12337442c5900)
----
- src/tests/sysdb-tests.c | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
-diff --git a/src/tests/sysdb-tests.c b/src/tests/sysdb-tests.c
-index 429aa39538901fe387e41eebb27662d7b958142c..bac8a8788b4fde0d6039121efead6fc20fa046f9 100644
---- a/src/tests/sysdb-tests.c
-+++ b/src/tests/sysdb-tests.c
-@@ -2737,7 +2737,7 @@ START_TEST (test_sysdb_memberof_close_loop)
-     fail_unless(data->attrlist != NULL, "talloc_array failed.");
-     data->attrlist[0] = test_asprintf_fqname(data, test_ctx->domain,
-                                              "testgroup%d", data->gid + 9);
--    fail_unless(data->attrlist[0], "talloc_array failed.");
-+    fail_unless(data->attrlist[0] != NULL, "talloc_array failed.");
-     data->attrlist[1] = NULL;
- 
-     ret = test_memberof_store_group(data);
--- 
-2.4.11
-
diff --git a/SOURCES/0016-PROVIDERS-Setting-right-u-g-id-if-unprivileged.patch b/SOURCES/0016-PROVIDERS-Setting-right-u-g-id-if-unprivileged.patch
deleted file mode 100644
index cdc951f..0000000
--- a/SOURCES/0016-PROVIDERS-Setting-right-u-g-id-if-unprivileged.patch
+++ /dev/null
@@ -1,41 +0,0 @@
-From 0686ce29cadb7875638d5f782199ea4bb186dee3 Mon Sep 17 00:00:00 2001
-From: Petr Cech <pcech@redhat.com>
-Date: Tue, 12 Jul 2016 16:14:04 +0200
-Subject: [PATCH 16/18] PROVIDERS: Setting right {u,g}id if unprivileged
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-be_ctx had talloc_zero() initialized uid and gid which was used
-in function dp_init(). Therefore back-end was every time started as root
-and therefore non-root responders could not communicate with back-end
-due to wrong permission of unix sockets.
-
-This patch sets right uid and gid to data-providers if sssd runs
-as non-root user.
-
-Resolves:
-https://fedorahosted.org/sssd/ticket/3077
-
-Reviewed-by: Lukáš Slebodník <lslebodn@redhat.com>
-(cherry picked from commit 75dead699a19dda7d8dfca89e2f97efbf0c264a2)
----
- src/providers/data_provider_be.c | 2 ++
- 1 file changed, 2 insertions(+)
-
-diff --git a/src/providers/data_provider_be.c b/src/providers/data_provider_be.c
-index 78efed851b2bf053ba890caa05e655431996892a..2ae713054429e789c1ba79c1f5e7a3889af3b291 100644
---- a/src/providers/data_provider_be.c
-+++ b/src/providers/data_provider_be.c
-@@ -386,6 +386,8 @@ errno_t be_process_init(TALLOC_CTX *mem_ctx,
- 
-     be_ctx->ev = ev;
-     be_ctx->cdb = cdb;
-+    be_ctx->uid = uid;
-+    be_ctx->gid = gid;
-     be_ctx->identity = talloc_asprintf(be_ctx, "%%BE_%s", be_domain);
-     be_ctx->conf_path = talloc_asprintf(be_ctx, CONFDB_DOMAIN_PATH_TMPL, be_domain);
-     if (be_ctx->identity == NULL || be_ctx->conf_path == NULL) {
--- 
-2.4.11
-
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
new file mode 100644
index 0000000..5ded3ab
--- /dev/null
+++ b/SOURCES/0016-UTIL-iobuf-Make-input-parameter-for-the-readonly-ope.patch
@@ -0,0 +1,45 @@
+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-UTIL-Fix-a-typo-in-the-tcurl-test-tool.patch b/SOURCES/0017-UTIL-Fix-a-typo-in-the-tcurl-test-tool.patch
new file mode 100644
index 0000000..c51e496
--- /dev/null
+++ b/SOURCES/0017-UTIL-Fix-a-typo-in-the-tcurl-test-tool.patch
@@ -0,0 +1,32 @@
+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/0017-config-Allow-timeout-for-all-sevices.patch b/SOURCES/0017-config-Allow-timeout-for-all-sevices.patch
deleted file mode 100644
index fae8cf5..0000000
--- a/SOURCES/0017-config-Allow-timeout-for-all-sevices.patch
+++ /dev/null
@@ -1,117 +0,0 @@
-From 18b2f16e2b086509cafb453943387fff2d1b0d19 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Michal=20=C5=BDidek?= <mzidek@redhat.com>
-Date: Mon, 11 Jul 2016 13:03:28 +0200
-Subject: [PATCH 17/18] config: Allow timeout for all sevices
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Allow option "timeout" for all sevices.
-Also remove unused macro CONFDB_SERVICE_TIMEOUT.
-
-Resolves:
-https://fedorahosted.org/sssd/ticket/3068
-
-Reviewed-by: Lukáš Slebodník <lslebodn@redhat.com>
-(cherry picked from commit 1b9b5477027d86a2afb2e72981253d108c5398da)
----
- src/confdb/confdb.h          | 1 -
- src/config/cfg_rules.ini     | 7 +++++++
- src/config/etc/sssd.api.conf | 2 +-
- 3 files changed, 8 insertions(+), 2 deletions(-)
-
-diff --git a/src/confdb/confdb.h b/src/confdb/confdb.h
-index 54b1cbc82546a76013c35c6cd3b1924663e9bb23..cc8f66f02eb5ac10ced826326f80bbf5eda82ee1 100644
---- a/src/confdb/confdb.h
-+++ b/src/confdb/confdb.h
-@@ -58,7 +58,6 @@
- #define CONFDB_SERVICE_DEBUG_TIMESTAMPS "debug_timestamps"
- #define CONFDB_SERVICE_DEBUG_MICROSECONDS "debug_microseconds"
- #define CONFDB_SERVICE_DEBUG_TO_FILES "debug_to_files"
--#define CONFDB_SERVICE_TIMEOUT "timeout"
- #define CONFDB_SERVICE_FORCE_TIMEOUT "force_timeout"
- #define CONFDB_SERVICE_RECON_RETRIES "reconnection_retries"
- #define CONFDB_SERVICE_FD_LIMIT "fd_limit"
-diff --git a/src/config/cfg_rules.ini b/src/config/cfg_rules.ini
-index 85a15be3493cf4b8c5a612b0f66ae4c86d39b1ab..5c8d05a817331dd23fd7e349719bd4b44a5bdd02 100644
---- a/src/config/cfg_rules.ini
-+++ b/src/config/cfg_rules.ini
-@@ -44,6 +44,7 @@ option = override_space
- validator = ini_allowed_options
- section_re = ^nss$
- 
-+option = timeout
- option = debug
- option = debug_level
- option = debug_timestamps
-@@ -82,6 +83,7 @@ option = memcache_timeout
- validator = ini_allowed_options
- section_re = ^pam$
- 
-+option = timeout
- option = debug
- option = debug_level
- option = debug_timestamps
-@@ -115,6 +117,7 @@ option = p11_child_timeout
- validator = ini_allowed_options
- section_re = ^sudo$
- 
-+option = timeout
- option = debug
- option = debug_level
- option = debug_timestamps
-@@ -136,6 +139,7 @@ option = sudo_inverse_order
- validator = ini_allowed_options
- section_re = ^autofs$
- 
-+option = timeout
- option = debug
- option = debug_level
- option = debug_timestamps
-@@ -156,6 +160,7 @@ option = autofs_negative_timeout
- validator = ini_allowed_options
- section_re = ^ssh$
- 
-+option = timeout
- option = debug
- option = debug_level
- option = debug_timestamps
-@@ -178,6 +183,7 @@ option = ca_db
- validator = ini_allowed_options
- section_re = ^pac$
- 
-+option = timeout
- option = debug
- option = debug_level
- option = debug_timestamps
-@@ -199,6 +205,7 @@ option = pac_lifetime
- validator = ini_allowed_options
- section_re = ^ifp$
- 
-+option = timeout
- option = debug
- option = debug_level
- option = debug_timestamps
-diff --git a/src/config/etc/sssd.api.conf b/src/config/etc/sssd.api.conf
-index 2d7c5049f5e5bf9df6e5445ee6e5c62211bf1c45..e4011a384cdb3fb3bce93494cbb278ec2622ee40 100644
---- a/src/config/etc/sssd.api.conf
-+++ b/src/config/etc/sssd.api.conf
-@@ -3,6 +3,7 @@
- 
- [service]
- # Options available to all services
-+timeout = int, None, false
- debug = int, None, false
- debug_level = int, None, false
- debug_timestamps = bool, None, false
-@@ -20,7 +21,6 @@ diag_cmd = str, None, false
- # Monitor service
- services = list, str, true, nss, pam
- domains = list, str, true
--timeout = int, None, false
- sbus_timeout = int, None, false
- re_expression = str, None, false
- full_name_format = str, None, false
--- 
-2.4.11
-
diff --git a/SOURCES/0018-UTIL-Add-SAFEALIGN_COPY_UINT8_CHECK.patch b/SOURCES/0018-UTIL-Add-SAFEALIGN_COPY_UINT8_CHECK.patch
new file mode 100644
index 0000000..bb11f98
--- /dev/null
+++ b/SOURCES/0018-UTIL-Add-SAFEALIGN_COPY_UINT8_CHECK.patch
@@ -0,0 +1,36 @@
+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/0018-config-Add-config_file_version-to-schema.patch b/SOURCES/0018-config-Add-config_file_version-to-schema.patch
deleted file mode 100644
index 3d47710..0000000
--- a/SOURCES/0018-config-Add-config_file_version-to-schema.patch
+++ /dev/null
@@ -1,58 +0,0 @@
-From 5e8d4f0107c5e6f1836740945b610a54c216bd7f Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Michal=20=C5=BDidek?= <mzidek@redhat.com>
-Date: Mon, 11 Jul 2016 13:34:03 +0200
-Subject: [PATCH 18/18] config: Add config_file_version to schema
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Resolves:
-https://fedorahosted.org/sssd/ticket/3068
-
-Reviewed-by: Lukáš Slebodník <lslebodn@redhat.com>
-(cherry picked from commit aeab20358006d728a284f969f92f3890498cd651)
----
- src/config/SSSDConfigTest.py | 1 +
- src/config/cfg_rules.ini     | 1 +
- src/config/etc/sssd.api.conf | 1 +
- 3 files changed, 3 insertions(+)
-
-diff --git a/src/config/SSSDConfigTest.py b/src/config/SSSDConfigTest.py
-index 5fa9bce8e6502c0ebf42671058c5e3d5de00ea0d..332d8702d983b6ec8bf12ec781a1bbf296b552e0 100755
---- a/src/config/SSSDConfigTest.py
-+++ b/src/config/SSSDConfigTest.py
-@@ -289,6 +289,7 @@ class SSSDConfigTestSSSDService(unittest.TestCase):
- 
-         options = service.list_options()
-         control_list = [
-+            'config_file_version',
-             'services',
-             'domains',
-             'timeout',
-diff --git a/src/config/cfg_rules.ini b/src/config/cfg_rules.ini
-index 5c8d05a817331dd23fd7e349719bd4b44a5bdd02..635c078436e8ca47f60e8d82341cb131469fe4c9 100644
---- a/src/config/cfg_rules.ini
-+++ b/src/config/cfg_rules.ini
-@@ -39,6 +39,7 @@ option = user
- option = default_domain_suffix
- option = certificate_verification
- option = override_space
-+option = config_file_version
- 
- [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 e4011a384cdb3fb3bce93494cbb278ec2622ee40..737f0e149d56bd07b078cb83acbc43ea2ed3a057 100644
---- a/src/config/etc/sssd.api.conf
-+++ b/src/config/etc/sssd.api.conf
-@@ -19,6 +19,7 @@ diag_cmd = str, None, false
- 
- [sssd]
- # Monitor service
-+config_file_version = int, None, false
- services = list, str, true, nss, pam
- domains = list, str, true
- sbus_timeout = int, None, false
--- 
-2.4.11
-
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
new file mode 100644
index 0000000..84752b5
--- /dev/null
+++ b/SOURCES/0019-UTIL-Add-utility-macro-cli_creds_get_gid.patch
@@ -0,0 +1,34 @@
+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/0019-dyndns-Add-checks-for-NULL.patch b/SOURCES/0019-dyndns-Add-checks-for-NULL.patch
deleted file mode 100644
index f038acd..0000000
--- a/SOURCES/0019-dyndns-Add-checks-for-NULL.patch
+++ /dev/null
@@ -1,57 +0,0 @@
-From b0330a760d838b30c88d6f02f0148b84093761c3 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Michal=20=C5=BDidek?= <mzidek@redhat.com>
-Date: Tue, 12 Jul 2016 12:11:18 +0200
-Subject: [PATCH 19/19] dyndns: Add checks for NULL
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Fixes:
-https://fedorahosted.org/sssd/ticket/3076
-
-We segfaulted in this area once. This patch
-makes the code more defensive and adds
-some DEBUG messages.
-
-Normally the structures are filled in online
-and/or resolve callbacks.
-
-Reviewed-by: Pavel Březina <pbrezina@redhat.com>
----
- src/providers/ipa/ipa_dyndns.c | 20 ++++++++++++++++++++
- 1 file changed, 20 insertions(+)
-
-diff --git a/src/providers/ipa/ipa_dyndns.c b/src/providers/ipa/ipa_dyndns.c
-index 7217c61452e7ead2949a9f7d57b2f2fc58953af1..dc910770c771d4b7a7ee62d25be7c48e16c988a7 100644
---- a/src/providers/ipa/ipa_dyndns.c
-+++ b/src/providers/ipa/ipa_dyndns.c
-@@ -162,6 +162,26 @@ ipa_dyndns_update_send(struct ipa_options *ctx)
-     }
-     state->ipa_ctx = ctx;
- 
-+    /* The following three checks are here to prevent SEGFAULT
-+     * from ticket #3076. */
-+    if (ctx->service == NULL) {
-+        DEBUG(SSSDBG_CRIT_FAILURE, "service structure not initialized\n");
-+        ret = EINVAL;
-+        goto done;
-+    }
-+
-+    if (ctx->service->sdap == NULL) {
-+        DEBUG(SSSDBG_CRIT_FAILURE, "sdap structure not initialized\n");
-+        ret = EINVAL;
-+        goto done;
-+    }
-+
-+    if (ctx->service->sdap->uri == NULL) {
-+        DEBUG(SSSDBG_CRIT_FAILURE, "LDAP uri not set\n");
-+        ret = EINVAL;
-+        goto done;
-+    }
-+
-     if (ctx->dyndns_ctx->last_refresh + 60 > time(NULL) ||
-         ctx->dyndns_ctx->timer_in_progress) {
-         DEBUG(SSSDBG_FUNC_DATA, "Last periodic update ran recently or timer "
--- 
-2.4.11
-
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
new file mode 100644
index 0000000..97c4775
--- /dev/null
+++ b/SOURCES/0020-UTIL-Add-type-specific-getsetters-to-sss_iobuf.patch
@@ -0,0 +1,194 @@
+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-sdap-Fix-ldap_rfc_2307_fallback_to_local_users.patch b/SOURCES/0020-sdap-Fix-ldap_rfc_2307_fallback_to_local_users.patch
deleted file mode 100644
index 331cf4d..0000000
--- a/SOURCES/0020-sdap-Fix-ldap_rfc_2307_fallback_to_local_users.patch
+++ /dev/null
@@ -1,42 +0,0 @@
-From dad90587794fdc2112f2099d5f6cdd9e138781be Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Michal=20=C5=BDidek?= <mzidek@redhat.com>
-Date: Wed, 13 Jul 2016 20:02:47 +0200
-Subject: [PATCH 20/27] sdap: Fix ldap_rfc_2307_fallback_to_local_users
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-We wrongly tried to store empty
-user attributes instead of the
-local user attributes with
-ldap_rfc_2307_fallback_to_local_users
-set to true. This gave us bad
-initgroups results and caused
-segfaults.
-
-Resolves:
-https://fedorahosted.org/sssd/ticket/3045
-
-Reviewed-by: Lukáš Slebodník <lslebodn@redhat.com>
-(cherry picked from commit aa8ec3758d885d6ae4088174369d30f8493ec898)
----
- src/providers/ldap/sdap_async_initgroups.c | 3 +++
- 1 file changed, 3 insertions(+)
-
-diff --git a/src/providers/ldap/sdap_async_initgroups.c b/src/providers/ldap/sdap_async_initgroups.c
-index d14563cb045a0ce34b72051744894362fc32d003..17593f0a268813662d6c7fbf658b1eb4599ce3c3 100644
---- a/src/providers/ldap/sdap_async_initgroups.c
-+++ b/src/providers/ldap/sdap_async_initgroups.c
-@@ -2893,6 +2893,9 @@ static void sdap_get_initgr_user(struct tevent_req *subreq)
-             (dp_opt_get_bool(state->opts->basic,
-                              SDAP_RFC2307_FALLBACK_TO_LOCAL_USERS) == true)) {
-             ret = sdap_fallback_local_user(state, state->shortname, -1, &usr_attrs);
-+            if (ret == EOK) {
-+                state->orig_user = usr_attrs[0];
-+            }
-         } else {
-             ret = ENOENT;
-         }
--- 
-2.4.11
-
diff --git a/SOURCES/0021-UTIL-krb5-principal-un-marshalling.patch b/SOURCES/0021-UTIL-krb5-principal-un-marshalling.patch
new file mode 100644
index 0000000..c088f68
--- /dev/null
+++ b/SOURCES/0021-UTIL-krb5-principal-un-marshalling.patch
@@ -0,0 +1,262 @@
+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/0021-test_utils-Clean-files-after-sss_write_krb5_conf_sni.patch b/SOURCES/0021-test_utils-Clean-files-after-sss_write_krb5_conf_sni.patch
deleted file mode 100644
index 7ad0c30..0000000
--- a/SOURCES/0021-test_utils-Clean-files-after-sss_write_krb5_conf_sni.patch
+++ /dev/null
@@ -1,56 +0,0 @@
-From a2e8989fd06af0dcf4dd7b8013bda6bcb49839f0 Mon Sep 17 00:00:00 2001
-From: Lukas Slebodnik <lslebodn@redhat.com>
-Date: Tue, 12 Jul 2016 12:37:16 +0200
-Subject: [PATCH 21/27] test_utils: Clean files after
- sss_write_krb5_conf_snippet
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-The test directory was not removed (tp_test_utils-test_utils)
-because it contain the snippet for krb5_libdefaults.
-
-Reviewed-by: Fabiano Fidêncio <fabiano@fidencio.org>
-(cherry picked from commit 059904af2d20debcb8ffe1c6f45b996c2c57574e)
----
- src/tests/cmocka/test_utils.c | 9 +++++++++
- 1 file changed, 9 insertions(+)
-
-diff --git a/src/tests/cmocka/test_utils.c b/src/tests/cmocka/test_utils.c
-index fd20990ce7ac632b3b62bf84a20cc75a5ec0e968..b08b19708bb59a076a79805fa37a15924152b8e2 100644
---- a/src/tests/cmocka/test_utils.c
-+++ b/src/tests/cmocka/test_utils.c
-@@ -1252,6 +1252,7 @@ void test_sss_write_krb5_conf_snippet(void **state)
-     char *cwd;
-     char *path;
-     char *file;
-+    char *file_krb5_libdefaults;
- 
-     ret = sss_write_krb5_conf_snippet(NULL, false);
-     assert_int_equal(ret, EINVAL);
-@@ -1274,6 +1275,10 @@ void test_sss_write_krb5_conf_snippet(void **state)
-     ret = asprintf(&file, "%s/%s/localauth_plugin", cwd, TESTS_PATH);
-     assert_true(ret > 0);
- 
-+    ret = asprintf(&file_krb5_libdefaults,
-+                   "%s/%s/krb5_libdefaults", cwd, TESTS_PATH);
-+    assert_true(ret > 0);
-+
-     ret = sss_write_krb5_conf_snippet(path, true);
-     assert_int_equal(ret, EOK);
- 
-@@ -1286,7 +1291,11 @@ void test_sss_write_krb5_conf_snippet(void **state)
-     assert_int_equal(ret, EOK);
- #endif
- 
-+    ret = unlink(file_krb5_libdefaults);
-+    assert_int_equal(ret, EOK);
-+
-     free(file);
-+    free(file_krb5_libdefaults);
-     free(path);
- }
- 
--- 
-2.4.11
-
diff --git a/SOURCES/0022-IPA-read-ipaNTAdditionalSuffixes-for-master-and-trus.patch b/SOURCES/0022-IPA-read-ipaNTAdditionalSuffixes-for-master-and-trus.patch
deleted file mode 100644
index 478a9ba..0000000
--- a/SOURCES/0022-IPA-read-ipaNTAdditionalSuffixes-for-master-and-trus.patch
+++ /dev/null
@@ -1,47 +0,0 @@
-From 4ec714f1681355d95420733a40e3c37cd0bfe6ee Mon Sep 17 00:00:00 2001
-From: Sumit Bose <sbose@redhat.com>
-Date: Thu, 23 Jun 2016 11:58:30 +0200
-Subject: [PATCH 22/27] IPA: read ipaNTAdditionalSuffixes for master and
- trusted domains
-
-Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
-(cherry picked from commit 39f21d2b61685362642d42bc2f94f829671cd5ef)
----
- src/providers/ipa/ipa_subdomains.c | 7 +++++--
- 1 file changed, 5 insertions(+), 2 deletions(-)
-
-diff --git a/src/providers/ipa/ipa_subdomains.c b/src/providers/ipa/ipa_subdomains.c
-index a02a65d97dde68f1da900b9fdca05c54035ce005..263d6207960c232d08114bd0163b3fd03a690685 100644
---- a/src/providers/ipa/ipa_subdomains.c
-+++ b/src/providers/ipa/ipa_subdomains.c
-@@ -40,6 +40,7 @@
- #define IPA_SID "ipaNTSecurityIdentifier"
- #define IPA_TRUSTED_DOMAIN_SID "ipaNTTrustedDomainSID"
- #define IPA_RANGE_TYPE "ipaRangeType"
-+#define IPA_ADDITIONAL_SUFFIXES "ipaNTAdditionalSuffixes"
- 
- #define IPA_BASE_ID "ipaBaseID"
- #define IPA_ID_RANGE_SIZE "ipaIDRangeSize"
-@@ -788,7 +789,8 @@ ipa_subdomains_master_send(TALLOC_CTX *mem_ctx,
-     struct tevent_req *subreq;
-     struct tevent_req *req;
-     errno_t ret;
--    const char *attrs[] = { IPA_CN, IPA_FLATNAME, IPA_SID, NULL };
-+    const char *attrs[] = { IPA_CN, IPA_FLATNAME, IPA_SID,
-+                            IPA_ADDITIONAL_SUFFIXES, NULL };
- 
-     req = tevent_req_create(mem_ctx, &state,
-                             struct ipa_subdomains_master_state);
-@@ -939,7 +941,8 @@ ipa_subdomains_slave_send(TALLOC_CTX *mem_ctx,
-     struct tevent_req *req;
-     errno_t ret;
-     const char *attrs[] = { IPA_CN, IPA_FLATNAME, IPA_TRUSTED_DOMAIN_SID,
--                            IPA_TRUST_DIRECTION, NULL };
-+                            IPA_TRUST_DIRECTION, IPA_ADDITIONAL_SUFFIXES,
-+                            NULL };
- 
-     req = tevent_req_create(mem_ctx, &state,
-                             struct ipa_subdomains_slave_state);
--- 
-2.4.11
-
diff --git a/SOURCES/0022-KCM-Initial-responder-build-and-packaging.patch b/SOURCES/0022-KCM-Initial-responder-build-and-packaging.patch
new file mode 100644
index 0000000..c2ba5a6
--- /dev/null
+++ b/SOURCES/0022-KCM-Initial-responder-build-and-packaging.patch
@@ -0,0 +1,1013 @@
+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/0023-KCM-request-parsing-and-sending-a-reply.patch b/SOURCES/0023-KCM-request-parsing-and-sending-a-reply.patch
new file mode 100644
index 0000000..5e59dc2
--- /dev/null
+++ b/SOURCES/0023-KCM-request-parsing-and-sending-a-reply.patch
@@ -0,0 +1,578 @@
+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-sysdb-add-UPN-suffix-support-for-the-master-domain.patch b/SOURCES/0023-sysdb-add-UPN-suffix-support-for-the-master-domain.patch
deleted file mode 100644
index f58a855..0000000
--- a/SOURCES/0023-sysdb-add-UPN-suffix-support-for-the-master-domain.patch
+++ /dev/null
@@ -1,277 +0,0 @@
-From 2ffd083501491a8ac3880dc834d01f7ee00fddfc Mon Sep 17 00:00:00 2001
-From: Sumit Bose <sbose@redhat.com>
-Date: Thu, 30 Jun 2016 13:48:58 +0200
-Subject: [PATCH 23/27] sysdb: add UPN suffix support for the master domain
-
-sysdb_master_domain_update() and sysdb_master_domain_add_info() are now
-aware of the UPN suffix attribute.
-
-Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
-(cherry picked from commit 132b31fd5fb74a7627896cdceaf29c7601ed4795)
----
- src/confdb/confdb.h                      |  1 +
- src/db/sysdb.h                           |  4 ++-
- src/db/sysdb_subdomains.c                | 49 ++++++++++++++++++++++++++++++--
- src/providers/ad/ad_id.c                 |  2 +-
- src/providers/ad/ad_subdomains.c         |  2 +-
- src/providers/ipa/ipa_subdomains.c       | 10 ++++++-
- src/tests/cmocka/test_sysdb_subdomains.c | 18 ++++++++----
- 7 files changed, 74 insertions(+), 12 deletions(-)
-
-diff --git a/src/confdb/confdb.h b/src/confdb/confdb.h
-index cc8f66f02eb5ac10ced826326f80bbf5eda82ee1..0265ccac5ee2e7b8baa05bf6b09df39ea5b4059a 100644
---- a/src/confdb/confdb.h
-+++ b/src/confdb/confdb.h
-@@ -315,6 +315,7 @@ struct sss_domain_info {
-      */
-     char *forest;
-     struct sss_domain_info *forest_root;
-+    char **upn_suffixes;
- };
- 
- /**
-diff --git a/src/db/sysdb.h b/src/db/sysdb.h
-index 609921fbb0f29561d7e52e8d1404a929af3c5b26..a8dcaa4a9ac5715150487f7efc9c35b778fa0163 100644
---- a/src/db/sysdb.h
-+++ b/src/db/sysdb.h
-@@ -147,6 +147,7 @@
- #define SYSDB_SUBDOMAIN_ENUM "enumerate"
- #define SYSDB_SUBDOMAIN_FOREST "memberOfForest"
- #define SYSDB_SUBDOMAIN_TRUST_DIRECTION "trustDirection"
-+#define SYSDB_UPN_SUFFIXES "upnSuffixes"
- 
- #define SYSDB_BASE_ID "baseID"
- #define SYSDB_ID_RANGE_SIZE "idRangeSize"
-@@ -475,7 +476,8 @@ errno_t sysdb_master_domain_add_info(struct sss_domain_info *domain,
-                                      const char *realm,
-                                      const char *flat,
-                                      const char *id,
--                                     const char* forest);
-+                                     const char *forest,
-+                                     struct ldb_message_element *alt_dom_suf);
- 
- errno_t sysdb_subdomain_delete(struct sysdb_ctx *sysdb, const char *name);
- 
-diff --git a/src/db/sysdb_subdomains.c b/src/db/sysdb_subdomains.c
-index 456e6621b3434a9dbf2e611ad880facbc171c174..c0a190f36d886325a5be1e5d1145b6aef6860ffc 100644
---- a/src/db/sysdb_subdomains.c
-+++ b/src/db/sysdb_subdomains.c
-@@ -448,6 +448,7 @@ errno_t sysdb_master_domain_update(struct sss_domain_info *domain)
-     errno_t ret;
-     TALLOC_CTX *tmp_ctx;
-     const char *tmp_str;
-+    struct ldb_message_element **tmp_el;
-     struct ldb_dn *basedn;
-     struct ldb_result *res;
-     const char *attrs[] = {"cn",
-@@ -455,6 +456,7 @@ errno_t sysdb_master_domain_update(struct sss_domain_info *domain)
-                            SYSDB_SUBDOMAIN_FLAT,
-                            SYSDB_SUBDOMAIN_ID,
-                            SYSDB_SUBDOMAIN_FOREST,
-+                           SYSDB_UPN_SUFFIXES,
-                            NULL};
-     char *view_name = NULL;
- 
-@@ -539,6 +541,19 @@ errno_t sysdb_master_domain_update(struct sss_domain_info *domain)
-         }
-     }
- 
-+    tmp_el = ldb_msg_find_element(res->msgs[0], SYSDB_UPN_SUFFIXES);
-+    if (tmp_el != NULL) {
-+        talloc_free(domain->upn_suffixes);
-+        domain->upn_suffixes = sss_ldb_el_to_string_list(domain, tmp_el);
-+        if (domain->upn_suffixes == NULL) {
-+            DEBUG(SSSDBG_OP_FAILURE, "sss_ldb_el_to_string_list failed.\n");
-+            ret = ENOMEM;
-+            goto done;
-+        }
-+    } else {
-+        talloc_zfree(domain->upn_suffixes);
-+    }
-+
-     ret = sysdb_get_view_name(tmp_ctx, domain->sysdb, &view_name);
-     if (ret != EOK && ret != ENOENT) {
-         DEBUG(SSSDBG_OP_FAILURE, "sysdb_get_view_name failed.\n");
-@@ -633,7 +648,8 @@ errno_t sysdb_master_domain_add_info(struct sss_domain_info *domain,
-                                      const char *realm,
-                                      const char *flat,
-                                      const char *id,
--                                     const char* forest)
-+                                     const char *forest,
-+                                     struct ldb_message_element *upn_suffixes)
- {
-     TALLOC_CTX *tmp_ctx;
-     struct ldb_message *msg;
-@@ -720,7 +736,6 @@ errno_t sysdb_master_domain_add_info(struct sss_domain_info *domain,
-             ret = sysdb_error_to_errno(ret);
-             goto done;
-         }
--
-         ret = ldb_msg_add_string(msg, SYSDB_SUBDOMAIN_REALM, realm);
-         if (ret != LDB_SUCCESS) {
-             ret = sysdb_error_to_errno(ret);
-@@ -730,6 +745,36 @@ errno_t sysdb_master_domain_add_info(struct sss_domain_info *domain,
-         do_update = true;
-     }
- 
-+    if (upn_suffixes != NULL) {
-+        talloc_free(discard_const(upn_suffixes->name));
-+        upn_suffixes->name = talloc_strdup(upn_suffixes, SYSDB_UPN_SUFFIXES);
-+        if (upn_suffixes->name == NULL) {
-+            DEBUG(SSSDBG_OP_FAILURE, "talloc_strdup failed.\n");
-+            ret = ENOMEM;
-+            goto done;
-+        }
-+
-+        ret = ldb_msg_add(msg, upn_suffixes, LDB_FLAG_MOD_REPLACE);
-+        if (ret != LDB_SUCCESS) {
-+            ret = sysdb_error_to_errno(ret);
-+            goto done;
-+        }
-+
-+        do_update = true;
-+    } else {
-+        /* Remove alternative_domain_suffixes from the cache */
-+        if (domain->upn_suffixes != NULL) {
-+            ret = ldb_msg_add_empty(msg, SYSDB_UPN_SUFFIXES,
-+                                    LDB_FLAG_MOD_DELETE, NULL);
-+            if (ret != LDB_SUCCESS) {
-+                ret = sysdb_error_to_errno(ret);
-+                goto done;
-+            }
-+        }
-+
-+        do_update = true;
-+    }
-+
-     if (do_update == false) {
-         ret = EOK;
-         goto done;
-diff --git a/src/providers/ad/ad_id.c b/src/providers/ad/ad_id.c
-index 92ac4ab6a13d7094f7a663b4a070feea3be09571..98915b4b966e2665dbd34257e4002d72b95d76b2 100644
---- a/src/providers/ad/ad_id.c
-+++ b/src/providers/ad/ad_id.c
-@@ -631,7 +631,7 @@ ad_enumeration_master_done(struct tevent_req *subreq)
-     }
- 
-     ret = sysdb_master_domain_add_info(state->sdom->dom, state->realm,
--                                       flat_name, master_sid, forest);
-+                                       flat_name, master_sid, forest, NULL);
-     if (ret != EOK) {
-         DEBUG(SSSDBG_OP_FAILURE, "Cannot save master domain info\n");
-         tevent_req_error(req, ret);
-diff --git a/src/providers/ad/ad_subdomains.c b/src/providers/ad/ad_subdomains.c
-index 05dfc3085fb14a87b5703518d784056b71bf5de0..0a8d1f53cb005507abe4ac55d0fa1ccc9e32b173 100644
---- a/src/providers/ad/ad_subdomains.c
-+++ b/src/providers/ad/ad_subdomains.c
-@@ -1131,7 +1131,7 @@ static void ad_subdomains_refresh_master_done(struct tevent_req *subreq)
-     }
- 
-     ret = sysdb_master_domain_add_info(state->be_ctx->domain, realm,
--                                       flat_name, master_sid, forest);
-+                                       flat_name, master_sid, forest, NULL);
-     if (ret != EOK) {
-         DEBUG(SSSDBG_OP_FAILURE, "Cannot save master domain info [%d]: %s\n",
-               ret, sss_strerror(ret));
-diff --git a/src/providers/ipa/ipa_subdomains.c b/src/providers/ipa/ipa_subdomains.c
-index 263d6207960c232d08114bd0163b3fd03a690685..62b8f65e5d29a4850f90ea7c19abd297becc96f5 100644
---- a/src/providers/ipa/ipa_subdomains.c
-+++ b/src/providers/ipa/ipa_subdomains.c
-@@ -855,6 +855,7 @@ static void ipa_subdomains_master_done(struct tevent_req *subreq)
-     const char *flat = NULL;
-     const char *id = NULL;
-     const char *realm = NULL;
-+    struct ldb_message_element *alternative_domain_suffixes = NULL;
-     errno_t ret;
- 
-     req = tevent_req_callback_data(subreq, struct tevent_req);
-@@ -879,6 +880,12 @@ static void ipa_subdomains_master_done(struct tevent_req *subreq)
-         if (ret != EOK) {
-             goto done;
-         }
-+
-+        ret = sysdb_attrs_get_el_ext(reply[0], IPA_ADDITIONAL_SUFFIXES, false,
-+                                     &alternative_domain_suffixes);
-+        if (ret != EOK && ret != ENOENT) {
-+            goto done;
-+        }
-     } else {
-         /* All search paths are searched and no master domain record was
-          * found.
-@@ -896,7 +903,8 @@ static void ipa_subdomains_master_done(struct tevent_req *subreq)
-         goto done;
-     }
- 
--    ret = sysdb_master_domain_add_info(state->domain, realm, flat, id, NULL);
-+    ret = sysdb_master_domain_add_info(state->domain, realm, flat, id, NULL,
-+                                       alternative_domain_suffixes);
-     if (ret != EOK) {
-         DEBUG(SSSDBG_CRIT_FAILURE, "Unable to add master domain info "
-               "[%d]: %s\n", ret, sss_strerror(ret));
-diff --git a/src/tests/cmocka/test_sysdb_subdomains.c b/src/tests/cmocka/test_sysdb_subdomains.c
-index f55c2918015900351483e3471bf946ea60872dae..6d1ec884284487a12bcbfad77c00cd6c30f67707 100644
---- a/src/tests/cmocka/test_sysdb_subdomains.c
-+++ b/src/tests/cmocka/test_sysdb_subdomains.c
-@@ -165,7 +165,8 @@ static void test_sysdb_master_domain_ops(void **state)
-         talloc_get_type(*state, struct subdom_test_ctx);
- 
-     ret = sysdb_master_domain_add_info(test_ctx->tctx->dom,
--                                       "realm1", "flat1", "id1", "forest1");
-+                                       "realm1", "flat1", "id1", "forest1",
-+                                       NULL);
-     assert_int_equal(ret, EOK);
- 
-     ret = sysdb_master_domain_update(test_ctx->tctx->dom);
-@@ -177,7 +178,8 @@ static void test_sysdb_master_domain_ops(void **state)
-     assert_string_equal(test_ctx->tctx->dom->forest, "forest1");
- 
-     ret = sysdb_master_domain_add_info(test_ctx->tctx->dom,
--                                       "realm2", "flat2", "id2", "forest2");
-+                                       "realm2", "flat2", "id2", "forest2",
-+                                       NULL);
-     assert_int_equal(ret, EOK);
- 
-     ret = sysdb_master_domain_update(test_ctx->tctx->dom);
-@@ -298,7 +300,8 @@ static void test_sysdb_link_forest_root_ad(void **state)
-                                        TEST_REALM,
-                                        TEST_FLAT_NAME,
-                                        TEST_SID,
--                                       TEST_FOREST);
-+                                       TEST_FOREST,
-+                                       NULL);
-     assert_int_equal(ret, EOK);
- 
-     ret = sysdb_subdomain_store(test_ctx->tctx->sysdb,
-@@ -374,7 +377,8 @@ static void test_sysdb_link_forest_member_ad(void **state)
-                                        child_dom[1],
-                                        child_dom[2],
-                                        child_dom[3],
--                                       TEST_FOREST);
-+                                       TEST_FOREST,
-+                                       NULL);
-     assert_int_equal(ret, EOK);
- 
-     ret = sysdb_subdomain_store(test_ctx->tctx->sysdb,
-@@ -457,7 +461,8 @@ static void test_sysdb_link_ad_multidom(void **state)
-                                        TEST_REALM,
-                                        TEST_FLAT_NAME,
-                                        TEST_SID,
--                                       TEST_FOREST);
-+                                       TEST_FOREST,
-+                                       NULL);
-     assert_int_equal(ret, EOK);
- 
-     ret = sysdb_subdomain_store(main_dom1->sysdb,
-@@ -477,7 +482,8 @@ static void test_sysdb_link_ad_multidom(void **state)
-                                        TEST_REALM2,
-                                        TEST_FLAT_NAME2,
-                                        TEST_SID2,
--                                       TEST_FOREST2);
-+                                       TEST_FOREST2,
-+                                       NULL);
-     assert_int_equal(ret, EOK);
- 
-     ret = sysdb_subdomain_store(main_dom2->sysdb,
--- 
-2.4.11
-
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
new file mode 100644
index 0000000..a5e0fc2
--- /dev/null
+++ b/SOURCES/0024-KCM-Implement-an-internal-ccache-storage-and-retriev.patch
@@ -0,0 +1,2153 @@
+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-sysdb-make-subdomain-calls-aware-of-upn_suffixes.patch b/SOURCES/0024-sysdb-make-subdomain-calls-aware-of-upn_suffixes.patch
deleted file mode 100644
index 4b900cf..0000000
--- a/SOURCES/0024-sysdb-make-subdomain-calls-aware-of-upn_suffixes.patch
+++ /dev/null
@@ -1,428 +0,0 @@
-From f3be4b46d39c1a0106b60d561bbdeee4c80961aa Mon Sep 17 00:00:00 2001
-From: Sumit Bose <sbose@redhat.com>
-Date: Fri, 1 Jul 2016 12:54:39 +0200
-Subject: [PATCH 24/27] sysdb: make subdomain calls aware of upn_suffixes
-
-sysdb_subdomain_store() and sysdb_update_subdomains() can now update
-upn_suffixes as well.
-
-Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
-(cherry picked from commit 20348a30feb4be619b3b691c24c9be8131507c46)
----
- src/confdb/confdb.h                           |  2 +-
- src/db/sysdb.h                                |  3 +-
- src/db/sysdb_subdomains.c                     | 56 +++++++++++++++++++++++++--
- src/providers/ad/ad_subdomains.c              |  2 +-
- src/providers/ipa/ipa_subdomains.c            |  9 ++++-
- src/tests/cmocka/test_ipa_subdomains_server.c |  4 +-
- src/tests/cmocka/test_nss_srv.c               |  2 +-
- src/tests/cmocka/test_sysdb_subdomains.c      | 28 +++++++-------
- src/tests/sysdb-tests.c                       |  6 +--
- 9 files changed, 85 insertions(+), 27 deletions(-)
-
-diff --git a/src/confdb/confdb.h b/src/confdb/confdb.h
-index 0265ccac5ee2e7b8baa05bf6b09df39ea5b4059a..72adbd80ea534eb0becd3e517c00b0c26d00444c 100644
---- a/src/confdb/confdb.h
-+++ b/src/confdb/confdb.h
-@@ -315,7 +315,7 @@ struct sss_domain_info {
-      */
-     char *forest;
-     struct sss_domain_info *forest_root;
--    char **upn_suffixes;
-+    const char **upn_suffixes;
- };
- 
- /**
-diff --git a/src/db/sysdb.h b/src/db/sysdb.h
-index a8dcaa4a9ac5715150487f7efc9c35b778fa0163..407ce3c18a7077e8fe45c3c9c7576ae626105122 100644
---- a/src/db/sysdb.h
-+++ b/src/db/sysdb.h
-@@ -466,7 +466,8 @@ errno_t sysdb_subdomain_store(struct sysdb_ctx *sysdb,
-                               const char *name, const char *realm,
-                               const char *flat_name, const char *domain_id,
-                               bool mpg, bool enumerate, const char *forest,
--                              uint32_t trust_direction);
-+                              uint32_t trust_direction,
-+                              struct ldb_message_element *upn_suffixes);
- 
- errno_t sysdb_update_subdomains(struct sss_domain_info *domain);
- 
-diff --git a/src/db/sysdb_subdomains.c b/src/db/sysdb_subdomains.c
-index c0a190f36d886325a5be1e5d1145b6aef6860ffc..02206e470e8e035cc05848137df6a1eb04806869 100644
---- a/src/db/sysdb_subdomains.c
-+++ b/src/db/sysdb_subdomains.c
-@@ -237,6 +237,7 @@ errno_t sysdb_update_subdomains(struct sss_domain_info *domain)
-                            SYSDB_SUBDOMAIN_ENUM,
-                            SYSDB_SUBDOMAIN_FOREST,
-                            SYSDB_SUBDOMAIN_TRUST_DIRECTION,
-+                           SYSDB_UPN_SUFFIXES,
-                            NULL};
-     struct sss_domain_info *dom;
-     struct ldb_dn *basedn;
-@@ -248,6 +249,8 @@ errno_t sysdb_update_subdomains(struct sss_domain_info *domain)
-     bool mpg;
-     bool enumerate;
-     uint32_t trust_direction;
-+    struct ldb_message_element *tmp_el;
-+    const char **upn_suffixes;
- 
-     tmp_ctx = talloc_new(NULL);
-     if (tmp_ctx == NULL) {
-@@ -308,6 +311,17 @@ errno_t sysdb_update_subdomains(struct sss_domain_info *domain)
-         forest = ldb_msg_find_attr_as_string(res->msgs[i],
-                                              SYSDB_SUBDOMAIN_FOREST, NULL);
- 
-+        upn_suffixes = NULL;
-+        tmp_el = ldb_msg_find_element(res->msgs[0], SYSDB_UPN_SUFFIXES);
-+        if (tmp_el != NULL) {
-+            upn_suffixes = sss_ldb_el_to_string_list(tmp_ctx, tmp_el);
-+            if (upn_suffixes == NULL) {
-+                DEBUG(SSSDBG_OP_FAILURE, "sss_ldb_el_to_string_list failed.\n");
-+                ret = ENOMEM;
-+                goto done;
-+            }
-+        }
-+
-         trust_direction = ldb_msg_find_attr_as_int(res->msgs[i],
-                                              SYSDB_SUBDOMAIN_TRUST_DIRECTION,
-                                              0);
-@@ -382,6 +396,9 @@ errno_t sysdb_update_subdomains(struct sss_domain_info *domain)
-                     }
-                 }
- 
-+                talloc_zfree(dom->upn_suffixes);
-+                dom->upn_suffixes = talloc_steal(dom, upn_suffixes);
-+
-                 if (!dom->has_views && dom->view_name == NULL) {
-                     /* maybe views are not initialized, copy from parent */
-                     dom->has_views = dom->parent->has_views;
-@@ -448,7 +465,7 @@ errno_t sysdb_master_domain_update(struct sss_domain_info *domain)
-     errno_t ret;
-     TALLOC_CTX *tmp_ctx;
-     const char *tmp_str;
--    struct ldb_message_element **tmp_el;
-+    struct ldb_message_element *tmp_el;
-     struct ldb_dn *basedn;
-     struct ldb_result *res;
-     const char *attrs[] = {"cn",
-@@ -806,7 +823,8 @@ errno_t sysdb_subdomain_store(struct sysdb_ctx *sysdb,
-                               const char *name, const char *realm,
-                               const char *flat_name, const char *domain_id,
-                               bool mpg, bool enumerate, const char *forest,
--                              uint32_t trust_direction)
-+                              uint32_t trust_direction,
-+                              struct ldb_message_element *upn_suffixes)
- {
-     TALLOC_CTX *tmp_ctx;
-     struct ldb_message *msg;
-@@ -820,8 +838,10 @@ errno_t sysdb_subdomain_store(struct sysdb_ctx *sysdb,
-                            SYSDB_SUBDOMAIN_ENUM,
-                            SYSDB_SUBDOMAIN_FOREST,
-                            SYSDB_SUBDOMAIN_TRUST_DIRECTION,
-+                           SYSDB_UPN_SUFFIXES,
-                            NULL};
-     const char *tmp_str;
-+    struct ldb_message_element *tmp_el;
-     bool tmp_bool;
-     bool store = false;
-     int realm_flags = 0;
-@@ -831,6 +851,7 @@ errno_t sysdb_subdomain_store(struct sysdb_ctx *sysdb,
-     int enum_flags = 0;
-     int forest_flags = 0;
-     int td_flags = 0;
-+    int upn_flags = 0;
-     uint32_t tmp_td;
-     int ret;
- 
-@@ -864,6 +885,7 @@ errno_t sysdb_subdomain_store(struct sysdb_ctx *sysdb,
-         enum_flags = LDB_FLAG_MOD_ADD;
-         if (forest) forest_flags = LDB_FLAG_MOD_ADD;
-         if (trust_direction) td_flags = LDB_FLAG_MOD_ADD;
-+        if (upn_suffixes) upn_flags = LDB_FLAG_MOD_ADD;
-     } else if (res->count != 1) {
-         ret = EINVAL;
-         goto done;
-@@ -915,11 +937,21 @@ errno_t sysdb_subdomain_store(struct sysdb_ctx *sysdb,
-         if (tmp_td != trust_direction) {
-             td_flags = LDB_FLAG_MOD_REPLACE;
-         }
-+
-+        if (upn_suffixes) {
-+            tmp_el = ldb_msg_find_element(res->msgs[0], SYSDB_UPN_SUFFIXES);
-+            /* Luckily ldb_msg_element_compare() only compares the values and
-+             * not the name. */
-+            if (tmp_el == NULL
-+                    || ldb_msg_element_compare(upn_suffixes, tmp_el) != 0) {
-+                upn_flags = LDB_FLAG_MOD_REPLACE;
-+            }
-+        }
-     }
- 
-     if (!store && realm_flags == 0 && flat_flags == 0 && id_flags == 0
-             && mpg_flags == 0 && enum_flags == 0 && forest_flags == 0
--            && td_flags == 0) {
-+            && td_flags == 0 && upn_flags == 0) {
-         ret = EOK;
-         goto done;
-     }
-@@ -1048,6 +1080,24 @@ errno_t sysdb_subdomain_store(struct sysdb_ctx *sysdb,
-         }
-     }
- 
-+    if (upn_flags) {
-+        tmp_el = talloc_zero(tmp_ctx, struct ldb_message_element);
-+        if (tmp_el == NULL) {
-+            DEBUG(SSSDBG_OP_FAILURE, "talloc_zero failed.\n");
-+            ret = ENOMEM;
-+            goto done;
-+        }
-+
-+        tmp_el->name = SYSDB_UPN_SUFFIXES;
-+        tmp_el->num_values = upn_suffixes->num_values;
-+        tmp_el->values = upn_suffixes->values;
-+        ret = ldb_msg_add(msg, tmp_el, upn_flags);
-+        if (ret != LDB_SUCCESS) {
-+            ret = sysdb_error_to_errno(ret);
-+            goto done;
-+        }
-+    }
-+
-     ret = ldb_modify(sysdb->ldb, msg);
-     if (ret != LDB_SUCCESS) {
-         DEBUG(SSSDBG_FATAL_FAILURE, "Failed to add subdomain attributes to "
-diff --git a/src/providers/ad/ad_subdomains.c b/src/providers/ad/ad_subdomains.c
-index 0a8d1f53cb005507abe4ac55d0fa1ccc9e32b173..928c4fe93cc6afa5c3f69c14503896db820a4c0a 100644
---- a/src/providers/ad/ad_subdomains.c
-+++ b/src/providers/ad/ad_subdomains.c
-@@ -252,7 +252,7 @@ ad_subdom_store(struct sdap_idmap_ctx *idmap_ctx,
-     mpg = sdap_idmap_domain_has_algorithmic_mapping(idmap_ctx, name, sid_str);
- 
-     ret = sysdb_subdomain_store(domain->sysdb, name, realm, flat, sid_str,
--                                mpg, enumerate, domain->forest, 0);
-+                                mpg, enumerate, domain->forest, 0, NULL);
-     if (ret != EOK) {
-         DEBUG(SSSDBG_OP_FAILURE, "sysdb_subdomain_store failed.\n");
-         goto done;
-diff --git a/src/providers/ipa/ipa_subdomains.c b/src/providers/ipa/ipa_subdomains.c
-index 62b8f65e5d29a4850f90ea7c19abd297becc96f5..925b1d8b133eb56724ee4f9133a2487090982a8b 100644
---- a/src/providers/ipa/ipa_subdomains.c
-+++ b/src/providers/ipa/ipa_subdomains.c
-@@ -375,6 +375,7 @@ static errno_t ipa_subdom_store(struct sss_domain_info *parent,
-     bool mpg;
-     bool enumerate;
-     uint32_t direction;
-+    struct ldb_message_element *alternative_domain_suffixes = NULL;
- 
-     tmp_ctx = talloc_new(parent);
-     if (tmp_ctx == NULL) {
-@@ -405,6 +406,12 @@ static errno_t ipa_subdom_store(struct sss_domain_info *parent,
-         goto done;
-     }
- 
-+    ret = sysdb_attrs_get_el_ext(attrs, IPA_ADDITIONAL_SUFFIXES, false,
-+                                 &alternative_domain_suffixes);
-+    if (ret != EOK && ret != ENOENT) {
-+        goto done;
-+    }
-+
-     mpg = sdap_idmap_domain_has_algorithmic_mapping(sdap_idmap_ctx, name, id);
- 
-     ret = ipa_subdom_get_forest(tmp_ctx, sysdb_ctx_get_ldb(parent->sysdb),
-@@ -431,7 +438,7 @@ static errno_t ipa_subdom_store(struct sss_domain_info *parent,
-           "Trust direction of %s is %s\n", name, ipa_trust_dir2str(direction));
-     ret = sysdb_subdomain_store(parent->sysdb, name, realm, flat,
-                                 id, mpg, enumerate, forest,
--                                direction);
-+                                direction, alternative_domain_suffixes);
-     if (ret) {
-         DEBUG(SSSDBG_OP_FAILURE, "sysdb_subdomain_store failed.\n");
-         goto done;
-diff --git a/src/tests/cmocka/test_ipa_subdomains_server.c b/src/tests/cmocka/test_ipa_subdomains_server.c
-index 0fddc951894dee45658497851473b9bddbba0ef7..123cf11c01ef4687eecad31a9d73120a87c643e1 100644
---- a/src/tests/cmocka/test_ipa_subdomains_server.c
-+++ b/src/tests/cmocka/test_ipa_subdomains_server.c
-@@ -253,14 +253,14 @@ static void add_test_subdomains(struct trust_test_ctx *test_ctx,
-                                 SUBDOM_NAME, SUBDOM_REALM,
-                                 NULL, SUBDOM_SID,
-                                 true, false, SUBDOM_REALM,
--                                direction);
-+                                direction, NULL);
-     assert_int_equal(ret, EOK);
- 
-     ret = sysdb_subdomain_store(test_ctx->tctx->sysdb,
-                                 CHILD_NAME, CHILD_REALM,
-                                 CHILD_FLAT, CHILD_SID,
-                                 true, false, SUBDOM_REALM,
--                                direction);
-+                                direction, NULL);
-     assert_int_equal(ret, EOK);
- 
-     ret = sysdb_update_subdomains(test_ctx->tctx->dom);
-diff --git a/src/tests/cmocka/test_nss_srv.c b/src/tests/cmocka/test_nss_srv.c
-index 4137e9151be561a57a8f2e674f385ecb37119255..82a304feed864b09168d0f3e06a4e1bb120df7e4 100644
---- a/src/tests/cmocka/test_nss_srv.c
-+++ b/src/tests/cmocka/test_nss_srv.c
-@@ -3089,7 +3089,7 @@ static int nss_subdom_test_setup(void **state)
- 
-     ret = sysdb_subdomain_store(nss_test_ctx->tctx->sysdb,
-                                 testdom[0], testdom[1], testdom[2], testdom[3],
--                                false, false, NULL, 0);
-+                                false, false, NULL, 0, NULL);
-     assert_int_equal(ret, EOK);
- 
-     ret = sysdb_update_subdomains(nss_test_ctx->tctx->dom);
-diff --git a/src/tests/cmocka/test_sysdb_subdomains.c b/src/tests/cmocka/test_sysdb_subdomains.c
-index 6d1ec884284487a12bcbfad77c00cd6c30f67707..c9db56841e841472c81d00a79f475dbbd975ccb0 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)
- 
-     ret = sysdb_subdomain_store(test_ctx->tctx->sysdb,
-                                 dom1[0], dom1[1], dom1[2], dom1[3],
--                                false, false, NULL, 0);
-+                                false, false, NULL, 0, NULL);
-     assert_int_equal(ret, EOK);
- 
-     ret = sysdb_update_subdomains(test_ctx->tctx->dom);
-@@ -115,7 +115,7 @@ static void test_sysdb_subdomain_create(void **state)
- 
-     ret = sysdb_subdomain_store(test_ctx->tctx->sysdb,
-                                 dom2[0], dom2[1], dom2[2], dom2[3],
--                                false, false, NULL, 1);
-+                                false, false, NULL, 1, NULL);
-     assert_int_equal(ret, EOK);
- 
-     ret = sysdb_update_subdomains(test_ctx->tctx->dom);
-@@ -128,12 +128,12 @@ static void test_sysdb_subdomain_create(void **state)
-     /* Reverse the trust directions */
-     ret = sysdb_subdomain_store(test_ctx->tctx->sysdb,
-                                 dom1[0], dom1[1], dom1[2], dom1[3],
--                                false, false, NULL, 1);
-+                                false, false, NULL, 1, NULL);
-     assert_int_equal(ret, EOK);
- 
-     ret = sysdb_subdomain_store(test_ctx->tctx->sysdb,
-                                 dom2[0], dom2[1], dom2[2], dom2[3],
--                                false, false, NULL, 0);
-+                                false, false, NULL, 0, NULL);
-     assert_int_equal(ret, EOK);
- 
-     ret = sysdb_update_subdomains(test_ctx->tctx->dom);
-@@ -215,27 +215,27 @@ static void test_sysdb_link_forest_root_ipa(void **state)
- 
-     ret = sysdb_subdomain_store(test_ctx->tctx->sysdb,
-                                 dom1[0], dom1[1], dom1[2], dom1[3],
--                                false, false, dom1[4], 0);
-+                                false, false, dom1[4], 0, NULL);
-     assert_int_equal(ret, EOK);
- 
-     ret = sysdb_subdomain_store(test_ctx->tctx->sysdb,
-                                 child_dom1[0], child_dom1[1],
-                                 child_dom1[2], child_dom1[3],
-                                 false, false, child_dom1[4],
--                                0);
-+                                0, NULL);
-     assert_int_equal(ret, EOK);
- 
-     ret = sysdb_subdomain_store(test_ctx->tctx->sysdb,
-                                 dom2[0], dom2[1], dom2[2], dom2[3],
-                                 false, false, dom2[4],
--                                0);
-+                                0, NULL);
-     assert_int_equal(ret, EOK);
- 
-     ret = sysdb_subdomain_store(test_ctx->tctx->sysdb,
-                                 child_dom2[0], child_dom2[1],
-                                 child_dom2[2], child_dom2[3],
-                                 false, false, child_dom2[4],
--                                0);
-+                                0, NULL);
-     assert_int_equal(ret, EOK);
- 
-     ret = sysdb_update_subdomains(test_ctx->tctx->dom);
-@@ -308,14 +308,14 @@ static void test_sysdb_link_forest_root_ad(void **state)
-                                 child_dom[0], child_dom[1],
-                                 child_dom[2], child_dom[3],
-                                 false, false, child_dom[4],
--                                0);
-+                                0, NULL);
-     assert_int_equal(ret, EOK);
- 
-     ret = sysdb_subdomain_store(test_ctx->tctx->sysdb,
-                                 sub_dom[0], sub_dom[1],
-                                 sub_dom[2], sub_dom[3],
-                                 false, false, sub_dom[4],
--                                0);
-+                                0, NULL);
-     assert_int_equal(ret, EOK);
- 
-     ret = sysdb_update_subdomains(test_ctx->tctx->dom);
-@@ -385,14 +385,14 @@ static void test_sysdb_link_forest_member_ad(void **state)
-                                 sub_dom[0], sub_dom[1],
-                                 sub_dom[2], sub_dom[3],
-                                 false, false, sub_dom[4],
--                                0);
-+                                0, NULL);
-     assert_int_equal(ret, EOK);
- 
-     ret = sysdb_subdomain_store(test_ctx->tctx->sysdb,
-                                 forest_root[0], forest_root[1],
-                                 forest_root[2], forest_root[3],
-                                 false, false, forest_root[4],
--                                0);
-+                                0, NULL);
-     assert_int_equal(ret, EOK);
- 
-     ret = sysdb_master_domain_update(test_ctx->tctx->dom);
-@@ -469,7 +469,7 @@ static void test_sysdb_link_ad_multidom(void **state)
-                                 child_dom[0], child_dom[1],
-                                 child_dom[2], child_dom[3],
-                                 false, false, child_dom[4],
--                                0);
-+                                0, NULL);
-     assert_int_equal(ret, EOK);
- 
-     ret = sysdb_master_domain_update(main_dom1);
-@@ -489,7 +489,7 @@ static void test_sysdb_link_ad_multidom(void **state)
-     ret = sysdb_subdomain_store(main_dom2->sysdb,
-                                 dom2_forest_root[0], dom2_forest_root[1],
-                                 dom2_forest_root[2], dom2_forest_root[3],
--                                false, false, dom2_forest_root[4], 0);
-+                                false, false, dom2_forest_root[4], 0, NULL);
-     assert_int_equal(ret, EOK);
- 
-     ret = sysdb_master_domain_update(main_dom2);
-diff --git a/src/tests/sysdb-tests.c b/src/tests/sysdb-tests.c
-index bac8a8788b4fde0d6039121efead6fc20fa046f9..d1450015cb0f0b073045e7b6031423e3f5494d78 100644
---- a/src/tests/sysdb-tests.c
-+++ b/src/tests/sysdb-tests.c
-@@ -5472,7 +5472,7 @@ START_TEST(test_sysdb_subdomain_store_user)
-     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);
-+                                false, false, NULL, 0, NULL);
-     fail_if(ret != EOK, "Could not set up the test (test subdom)");
- 
-     ret = sysdb_update_subdomains(test_ctx->domain);
-@@ -5551,7 +5551,7 @@ START_TEST(test_sysdb_subdomain_user_ops)
-     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);
-+                                false, false, NULL, 0, NULL);
-     fail_if(ret != EOK, "Could not set up the test (test subdom)");
- 
-     ret = sysdb_update_subdomains(test_ctx->domain);
-@@ -5624,7 +5624,7 @@ START_TEST(test_sysdb_subdomain_group_ops)
-     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);
-+                                false, false, NULL, 0, NULL);
-     fail_if(ret != EOK, "Could not set up the test (test subdom)");
- 
-     ret = sysdb_update_subdomains(test_ctx->domain);
--- 
-2.4.11
-
diff --git a/SOURCES/0025-DP-add-dp_get_module_data.patch b/SOURCES/0025-DP-add-dp_get_module_data.patch
deleted file mode 100644
index bd41853..0000000
--- a/SOURCES/0025-DP-add-dp_get_module_data.patch
+++ /dev/null
@@ -1,44 +0,0 @@
-From 5ce7c218a7bd34672bd19359dcbeed51cc237474 Mon Sep 17 00:00:00 2001
-From: Sumit Bose <sbose@redhat.com>
-Date: Fri, 1 Jul 2016 17:57:31 +0200
-Subject: [PATCH 25/27] DP: add dp_get_module_data()
-
-Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
-(cherry picked from commit 35fa5a83ce8badf6bc868937047f44c3f32b7c28)
----
- src/providers/data_provider/dp.h         | 2 ++
- src/providers/data_provider/dp_targets.c | 5 +++++
- 2 files changed, 7 insertions(+)
-
-diff --git a/src/providers/data_provider/dp.h b/src/providers/data_provider/dp.h
-index 8cdbd7768a0b3f7f234b6bce6abab02419b3b9d1..5b36baf3489be4cce463dfb42c65a0b7f7ece9ef 100644
---- a/src/providers/data_provider/dp.h
-+++ b/src/providers/data_provider/dp.h
-@@ -118,6 +118,8 @@ bool _dp_target_enabled(struct data_provider *provider,
- struct dp_module *dp_target_module(struct data_provider *provider,
-                                    enum dp_targets target);
- 
-+void *dp_get_module_data(struct dp_module *dp_module);
-+
- void _dp_set_method(struct dp_method *methods,
-                     enum dp_methods method,
-                     dp_req_send_fn send_fn,
-diff --git a/src/providers/data_provider/dp_targets.c b/src/providers/data_provider/dp_targets.c
-index e19cf93a3693dede98567d2105021488380b5408..87ecfe55daa805eec0265795ef76751a1568c474 100644
---- a/src/providers/data_provider/dp_targets.c
-+++ b/src/providers/data_provider/dp_targets.c
-@@ -88,6 +88,11 @@ struct dp_module *dp_target_module(struct data_provider *provider,
-     return provider->targets[target]->module;
- }
- 
-+void *dp_get_module_data(struct dp_module *dp_module)
-+{
-+    return dp_module == NULL ? NULL : dp_module->module_data;
-+}
-+
- const char *dp_target_to_string(enum dp_targets target)
- {
-     switch (target) {
--- 
-2.4.11
-
diff --git a/SOURCES/0025-KCM-Add-a-in-memory-credential-storage.patch b/SOURCES/0025-KCM-Add-a-in-memory-credential-storage.patch
new file mode 100644
index 0000000..6bfffe6
--- /dev/null
+++ b/SOURCES/0025-KCM-Add-a-in-memory-credential-storage.patch
@@ -0,0 +1,907 @@
+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/0026-IPA-add-ipa_init_get_krb5_auth_ctx.patch b/SOURCES/0026-IPA-add-ipa_init_get_krb5_auth_ctx.patch
deleted file mode 100644
index 878d78e..0000000
--- a/SOURCES/0026-IPA-add-ipa_init_get_krb5_auth_ctx.patch
+++ /dev/null
@@ -1,60 +0,0 @@
-From ec460578800d850e5a4f9d522920db1d79147dd6 Mon Sep 17 00:00:00 2001
-From: Sumit Bose <sbose@redhat.com>
-Date: Fri, 1 Jul 2016 17:58:02 +0200
-Subject: [PATCH 26/27] IPA: add ipa_init_get_krb5_auth_ctx()
-
-Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
-(cherry picked from commit 17dccc24e4490dfda2820d46b62a029b14ba2359)
----
- src/providers/ipa/ipa_common.h |  5 +++++
- src/providers/ipa/ipa_init.c   | 13 +++++++++++++
- 2 files changed, 18 insertions(+)
-
-diff --git a/src/providers/ipa/ipa_common.h b/src/providers/ipa/ipa_common.h
-index 939c898e1a690806abd37493aabfb7bdec3e87a9..add9df87692c732b3567eee5584e7698991c66ca 100644
---- a/src/providers/ipa/ipa_common.h
-+++ b/src/providers/ipa/ipa_common.h
-@@ -34,6 +34,8 @@ struct ipa_service {
-     struct krb5_service *krb5_service;
- };
- 
-+struct ipa_init_ctx;
-+
- enum ipa_basic_opt {
-     IPA_DOMAIN = 0,
-     IPA_SERVER,
-@@ -287,4 +289,7 @@ errno_t ipa_idmap_get_ranges_from_sysdb(struct sdap_idmap_ctx *idmap_ctx,
- errno_t ipa_idmap_init(TALLOC_CTX *mem_ctx,
-                        struct sdap_id_ctx *id_ctx,
-                        struct sdap_idmap_ctx **_idmap_ctx);
-+
-+
-+struct krb5_ctx *ipa_init_get_krb5_auth_ctx(void *data);
- #endif /* _IPA_COMMON_H_ */
-diff --git a/src/providers/ipa/ipa_init.c b/src/providers/ipa/ipa_init.c
-index d3093b3b5d269e6acd29f35560cf8299017c72b5..959cdb4a7c40c1be03dd1e7c66dee6e65ca76607 100644
---- a/src/providers/ipa/ipa_init.c
-+++ b/src/providers/ipa/ipa_init.c
-@@ -58,6 +58,19 @@ struct ipa_init_ctx {
-     struct ipa_auth_ctx *auth_ctx;
- };
- 
-+
-+struct krb5_ctx *ipa_init_get_krb5_auth_ctx(void *data)
-+{
-+    struct ipa_init_ctx *ipa_init_ctx;
-+
-+    ipa_init_ctx = talloc_get_type(data, struct ipa_init_ctx);
-+    if (ipa_init_ctx == NULL || ipa_init_ctx->auth_ctx == NULL) {
-+        return NULL;
-+    }
-+
-+    return ipa_init_ctx->auth_ctx->krb5_auth_ctx;
-+}
-+
- static bool srv_in_server_list(const char *servers)
- {
-     TALLOC_CTX *tmp_ctx;
--- 
-2.4.11
-
diff --git a/SOURCES/0026-KCM-Implement-KCM-server-operations.patch b/SOURCES/0026-KCM-Implement-KCM-server-operations.patch
new file mode 100644
index 0000000..56f486a
--- /dev/null
+++ b/SOURCES/0026-KCM-Implement-KCM-server-operations.patch
@@ -0,0 +1,2326 @@
+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-IPA-enable-enterprise-principals-if-server-supports-.patch b/SOURCES/0027-IPA-enable-enterprise-principals-if-server-supports-.patch
deleted file mode 100644
index 8589359..0000000
--- a/SOURCES/0027-IPA-enable-enterprise-principals-if-server-supports-.patch
+++ /dev/null
@@ -1,149 +0,0 @@
-From 6b5b0732b7f4fab195a6205e1046a8402f5d3040 Mon Sep 17 00:00:00 2001
-From: Sumit Bose <sbose@redhat.com>
-Date: Fri, 1 Jul 2016 18:18:14 +0200
-Subject: [PATCH 27/27] IPA: enable enterprise principals if server supports
- them
-
-If there are alternative UPN suffixes found on the server we can safely
-assume that the IPA server supports enterprise principals.
-
-Resolves https://fedorahosted.org/sssd/ticket/3018
-
-Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
-(cherry picked from commit 70673115c03c37ddc64c951b53d92df9d3310762)
----
- src/man/sssd-krb5.5.xml            |  6 +++
- src/providers/ipa/ipa_subdomains.c | 86 ++++++++++++++++++++++++++++++++++++++
- 2 files changed, 92 insertions(+)
-
-diff --git a/src/man/sssd-krb5.5.xml b/src/man/sssd-krb5.5.xml
-index e7fdd19e07db99314a9491faff9974d7d5e617e6..60b7dfb508c0d054a421fd46957574f52e0333d7 100644
---- a/src/man/sssd-krb5.5.xml
-+++ b/src/man/sssd-krb5.5.xml
-@@ -513,6 +513,12 @@
-                         <para>
-                             Default: false (AD provider: true)
-                         </para>
-+                        <para>
-+                            The IPA provider will set to option to 'true' if it
-+                            detects that the server is capable of handling
-+                            enterprise principals and the option is not set
-+                            explicitly in the config file.
-+                        </para>
-                     </listitem>
-                 </varlistentry>
- 
-diff --git a/src/providers/ipa/ipa_subdomains.c b/src/providers/ipa/ipa_subdomains.c
-index 925b1d8b133eb56724ee4f9133a2487090982a8b..4e5bceb8c761bf4476928168d620baf2beb62ad5 100644
---- a/src/providers/ipa/ipa_subdomains.c
-+++ b/src/providers/ipa/ipa_subdomains.c
-@@ -28,6 +28,7 @@
- #include "providers/ipa/ipa_subdomains.h"
- #include "providers/ipa/ipa_common.h"
- #include "providers/ipa/ipa_id.h"
-+#include "providers/ipa/ipa_opts.h"
- 
- #include <ctype.h>
- 
-@@ -999,6 +1000,84 @@ immediately:
-     return req;
- }
- 
-+static errno_t ipa_enable_enterprise_principals(struct be_ctx *be_ctx)
-+{
-+    int ret;
-+    struct sss_domain_info *d;
-+    TALLOC_CTX *tmp_ctx;
-+    char **vals = NULL;
-+    struct dp_module *auth;
-+    struct krb5_ctx *krb5_auth_ctx;
-+
-+    d = get_domains_head(be_ctx->domain);
-+
-+    while (d != NULL) {
-+        DEBUG(SSSDBG_TRACE_ALL, "checking [%s].\n", d->name);
-+        if (d->upn_suffixes != NULL) {
-+            break;
-+        }
-+        d = get_next_domain(d, SSS_GND_DESCEND);
-+    }
-+
-+    if (d == NULL) {
-+        DEBUG(SSSDBG_TRACE_ALL,
-+              "No UPN suffixes found, "
-+              "no need to enable enterprise principals.\n");
-+        return EOK;
-+    }
-+
-+    tmp_ctx = talloc_new(NULL);
-+    if (tmp_ctx == NULL) {
-+        DEBUG(SSSDBG_OP_FAILURE, "talloc_new failed.\n");
-+        return ENOMEM;
-+    }
-+
-+    ret = confdb_get_param(be_ctx->cdb, tmp_ctx, be_ctx->conf_path,
-+                     ipa_def_krb5_opts[KRB5_USE_ENTERPRISE_PRINCIPAL].opt_name,
-+                     &vals);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_OP_FAILURE, "confdb_get_param failed.\n");
-+        goto done;
-+    }
-+
-+    if (vals[0]) {
-+        DEBUG(SSSDBG_CONF_SETTINGS,
-+              "Parameter [%s] set in config file and will not be changed.\n",
-+              ipa_def_krb5_opts[KRB5_USE_ENTERPRISE_PRINCIPAL].opt_name);
-+        return EOK;
-+    }
-+
-+    auth = dp_target_module(be_ctx->provider, DPT_AUTH);
-+    if (auth == NULL) {
-+        DEBUG(SSSDBG_OP_FAILURE, "Unable to find auth proivder.\n");
-+        ret = EINVAL;
-+        goto done;
-+    }
-+
-+    krb5_auth_ctx = ipa_init_get_krb5_auth_ctx(dp_get_module_data(auth));
-+    if (krb5_auth_ctx == NULL) {
-+        DEBUG(SSSDBG_OP_FAILURE, "Unable to find auth proivder data.\n");
-+        ret = EINVAL;
-+        goto done;
-+    }
-+
-+    ret = dp_opt_set_bool(krb5_auth_ctx->opts,
-+                          KRB5_USE_ENTERPRISE_PRINCIPAL, true);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_OP_FAILURE, "dp_opt_set_bool failed.\n");
-+        goto done;
-+    }
-+
-+    DEBUG(SSSDBG_CONF_SETTINGS, "Enterprise principals enabled.\n");
-+
-+    ret = EOK;
-+
-+done:
-+    talloc_free(tmp_ctx);
-+
-+    return ret;
-+}
-+
- static void ipa_subdomains_slave_search_done(struct tevent_req *subreq)
- {
-     struct ipa_subdomains_slave_state *state;
-@@ -1037,6 +1116,13 @@ static void ipa_subdomains_slave_search_done(struct tevent_req *subreq)
-         goto done;
-     }
- 
-+    ret = ipa_enable_enterprise_principals(state->sd_ctx->be_ctx);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_OP_FAILURE, "ipa_enable_enterprise_principals failed. "
-+                                 "Enterprise principals might not work as "
-+                                 "expected.\n");
-+    }
-+
-     if (state->sd_ctx->ipa_id_ctx->server_mode == NULL) {
-         ret = EOK;
-         goto done;
--- 
-2.4.11
-
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
new file mode 100644
index 0000000..39a1629
--- /dev/null
+++ b/SOURCES/0027-MAN-Add-a-manual-page-for-sssd-kcm.patch
@@ -0,0 +1,278 @@
+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/0028-TESTS-Add-integration-tests-for-the-KCM-responder.patch b/SOURCES/0028-TESTS-Add-integration-tests-for-the-KCM-responder.patch
new file mode 100644
index 0000000..776bf94
--- /dev/null
+++ b/SOURCES/0028-TESTS-Add-integration-tests-for-the-KCM-responder.patch
@@ -0,0 +1,799 @@
+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/0028-views-allow-override-added-for-non-default-views-at-.patch b/SOURCES/0028-views-allow-override-added-for-non-default-views-at-.patch
deleted file mode 100644
index fb5707c..0000000
--- a/SOURCES/0028-views-allow-override-added-for-non-default-views-at-.patch
+++ /dev/null
@@ -1,74 +0,0 @@
-From 845e3a3acea6f83b15ed3d887fcc4ed42cb3eb5a Mon Sep 17 00:00:00 2001
-From: Sumit Bose <sbose@redhat.com>
-Date: Mon, 11 Jul 2016 15:04:32 +0200
-Subject: [PATCH 28/31] views: allow override added for non-default views at
- runtime
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Currently a new override for a non-default view cannot be displayed at
-run-time. It even does not only require a restart but the view must be
-un-applied and applied again to make the changes visible.
-
-This patch fixes this and makes non-default view behave like the default
-view where the data from a newly added override are displayed after the
-cached entry of the related object is expired.
-
-Resolves https://fedorahosted.org/sssd/ticket/3092
-
-Reviewed-by: Pavel Březina <pbrezina@redhat.com>
-(cherry picked from commit 26a3d4f2ef35a088e4c5fc928290052c89a2ff43)
----
- src/db/sysdb_views.c | 26 ++++++++++++++++++--------
- 1 file changed, 18 insertions(+), 8 deletions(-)
-
-diff --git a/src/db/sysdb_views.c b/src/db/sysdb_views.c
-index 7adc1523050243b8936cb98be3c71ce4364a03db..2b89e5ca41f719e1217ef3b9e0fd683656e05d42 100644
---- a/src/db/sysdb_views.c
-+++ b/src/db/sysdb_views.c
-@@ -454,15 +454,23 @@ errno_t sysdb_store_override(struct sss_domain_info *domain,
-     obj_override_dn = ldb_msg_find_attr_as_string(msgs[0], SYSDB_OVERRIDE_DN,
-                                                   NULL);
-     if (obj_override_dn != NULL) {
-+        /* obj_override_dn can either point to the object itself, i.e there is
-+         * no override, or to a overide object. This means it can change from
-+         * the object DN to a override DN and back but not from one override
-+         * DN to a different override DN. If the new and the old DN are the
-+         * same we do not need to update the original object.  */
-         if (strcmp(obj_override_dn, override_dn_str) != 0) {
--            DEBUG(SSSDBG_CRIT_FAILURE,
--                  "Existing [%s] and new [%s] override DN do not match.\n",
--                   obj_override_dn, override_dn_str);
--            ret = EINVAL;
--            goto done;
-+            if (strcmp(obj_override_dn, obj_dn_str) != 0
-+                    && strcmp(override_dn_str, obj_dn_str) != 0) {
-+                DEBUG(SSSDBG_CRIT_FAILURE,
-+                      "Existing [%s] and new [%s] override DN do not match.\n",
-+                       obj_override_dn, override_dn_str);
-+                ret = EINVAL;
-+                goto done;
-+            }
-+        } else {
-+            add_ref = false;
-         }
--
--        add_ref = false;
-     }
- 
-     ret = ldb_transaction_start(domain->sysdb->ldb);
-@@ -580,7 +588,9 @@ errno_t sysdb_store_override(struct sss_domain_info *domain,
- 
-         msg->dn = obj_dn;
- 
--        ret = ldb_msg_add_empty(msg, SYSDB_OVERRIDE_DN, LDB_FLAG_MOD_ADD,
-+        ret = ldb_msg_add_empty(msg, SYSDB_OVERRIDE_DN,
-+                                obj_override_dn == NULL ? LDB_FLAG_MOD_ADD
-+                                                        : LDB_FLAG_MOD_REPLACE,
-                                 NULL);
-         if (ret != LDB_SUCCESS) {
-             DEBUG(SSSDBG_OP_FAILURE, "ldb_msg_add_empty failed.\n");
--- 
-2.4.11
-
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
new file mode 100644
index 0000000..3b1bee7
--- /dev/null
+++ b/SOURCES/0029-SECRETS-Create-DB-path-before-the-operation-itself.patch
@@ -0,0 +1,405 @@
+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/0029-sssctl-move-filter-creation-to-separate-function.patch b/SOURCES/0029-sssctl-move-filter-creation-to-separate-function.patch
deleted file mode 100644
index 6abaf60..0000000
--- a/SOURCES/0029-sssctl-move-filter-creation-to-separate-function.patch
+++ /dev/null
@@ -1,129 +0,0 @@
-From 634311d9250903599a61b2f5e205e2568ae92497 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Pavel=20B=C5=99ezina?= <pbrezina@redhat.com>
-Date: Tue, 12 Jul 2016 12:59:48 +0200
-Subject: [PATCH 29/31] sssctl: move filter creation to separate function
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Reviewed-by: Fabiano Fidêncio <fabiano@fidencio.org>
-(cherry picked from commit 3c6e15e8aa38d9dfa02a7255fad56149bdfb35a6)
----
- src/tools/sssctl/sssctl_cache.c | 81 +++++++++++++++++++++++------------------
- 1 file changed, 46 insertions(+), 35 deletions(-)
-
-diff --git a/src/tools/sssctl/sssctl_cache.c b/src/tools/sssctl/sssctl_cache.c
-index e23bb89db95217e66a441b7e4d6d32e668486cc8..3e7644e327eb3e5a0e33023e7a3f5f0f15e03cf6 100644
---- a/src/tools/sssctl/sssctl_cache.c
-+++ b/src/tools/sssctl/sssctl_cache.c
-@@ -285,27 +285,16 @@ done:
-     return ret;
- }
- 
--static errno_t sssctl_find_object(TALLOC_CTX *mem_ctx,
--                                  struct sss_domain_info *domains,
--                                  struct sss_domain_info *domain,
--                                  sssctl_basedn_fn basedn_fn,
--                                  enum cache_object obj_type,
--                                  const char *attr_name,
--                                  const char *attr_value,
--                                  const char **attrs,
--                                  struct sysdb_attrs **_entry,
--                                  struct sss_domain_info **_dom)
-+static const char *sssctl_create_filter(TALLOC_CTX *mem_ctx,
-+                                        struct sss_domain_info *dom,
-+                                        enum cache_object obj_type,
-+                                        const char *attr_name,
-+                                        const char *attr_value)
- {
--    TALLOC_CTX *tmp_ctx;
--    struct sss_domain_info *dom;
--    struct sysdb_attrs *entry;
--    struct ldb_dn *base_dn;
--    bool fqn_provided;
--    bool qualify_attr = false;
--    char *filter;
--    errno_t ret;
-     const char *class;
-+    const char *filter;
-     char *filter_value;
-+    bool qualify_attr = false;
- 
-     if (strcmp(attr_name, SYSDB_NAME) == 0 &&
-             (obj_type == CACHED_USER ||
-@@ -326,9 +315,44 @@ static errno_t sssctl_find_object(TALLOC_CTX *mem_ctx,
-     default:
-         DEBUG(SSSDBG_FATAL_FAILURE,
-               "sssctl doesn't handle this object type (type=%d)\n", obj_type);
--        return EINVAL;
-+        return NULL;
-     }
- 
-+    if (qualify_attr) {
-+        filter_value = sss_create_internal_fqname(NULL, attr_value, dom->name);
-+    } else {
-+        filter_value = talloc_strdup(NULL, attr_value);
-+    }
-+    if (filter_value == NULL) {
-+        return NULL;
-+    }
-+
-+    filter = talloc_asprintf(mem_ctx, "(&(objectClass=%s)(%s=%s))",
-+                             class, attr_name, filter_value);
-+    talloc_free(filter_value);
-+
-+    return filter;
-+}
-+
-+static errno_t sssctl_find_object(TALLOC_CTX *mem_ctx,
-+                                  struct sss_domain_info *domains,
-+                                  struct sss_domain_info *domain,
-+                                  sssctl_basedn_fn basedn_fn,
-+                                  enum cache_object obj_type,
-+                                  const char *attr_name,
-+                                  const char *attr_value,
-+                                  const char **attrs,
-+                                  struct sysdb_attrs **_entry,
-+                                  struct sss_domain_info **_dom)
-+{
-+    TALLOC_CTX *tmp_ctx;
-+    struct sss_domain_info *dom;
-+    struct sysdb_attrs *entry;
-+    struct ldb_dn *base_dn;
-+    bool fqn_provided;
-+    const char *filter;
-+    errno_t ret;
-+
-     tmp_ctx = talloc_new(NULL);
-     if (tmp_ctx == NULL) {
-         DEBUG(SSSDBG_CRIT_FAILURE, "talloc_new() failed\n");
-@@ -349,23 +373,10 @@ static errno_t sssctl_find_object(TALLOC_CTX *mem_ctx,
-             goto done;
-         }
- 
--        if (qualify_attr) {
--            filter_value = sss_create_internal_fqname(tmp_ctx,
--                                                      attr_value,
--                                                      dom->name);
--        } else {
--            filter_value = talloc_strdup(tmp_ctx, attr_value);
--        }
--        if (filter_value == NULL) {
--            ret = ENOMEM;
--            goto done;
--        }
--
--        filter = talloc_asprintf(tmp_ctx, "(&(objectClass=%s)(%s=%s))",
--                                 class, attr_name, filter_value);
--        talloc_free(filter_value);
-+        filter = sssctl_create_filter(tmp_ctx, dom, obj_type,
-+                                      attr_name, attr_value);
-         if (filter == NULL) {
--            DEBUG(SSSDBG_CRIT_FAILURE, "talloc_asprintf() failed\n");
-+            DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create filter\n");
-             ret = ENOMEM;
-             goto done;
-         }
--- 
-2.4.11
-
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
new file mode 100644
index 0000000..2c596dc
--- /dev/null
+++ b/SOURCES/0030-SECRETS-Return-a-nicer-error-message-on-request-with.patch
@@ -0,0 +1,40 @@
+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-sssctl-improve-readability-of-a-condition.patch b/SOURCES/0030-sssctl-improve-readability-of-a-condition.patch
deleted file mode 100644
index 88e259d..0000000
--- a/SOURCES/0030-sssctl-improve-readability-of-a-condition.patch
+++ /dev/null
@@ -1,36 +0,0 @@
-From 5ff189fbc8135d5900ac97120c587128a35f56eb Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Pavel=20B=C5=99ezina?= <pbrezina@redhat.com>
-Date: Wed, 13 Jul 2016 10:41:00 +0200
-Subject: [PATCH 30/31] sssctl: improve readability of a condition
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Reviewed-by: Fabiano Fidêncio <fabiano@fidencio.org>
-(cherry picked from commit aa691837a2fa2fe2e38a55d576644074e0f45bd8)
----
- src/tools/sssctl/sssctl_cache.c | 8 ++++----
- 1 file changed, 4 insertions(+), 4 deletions(-)
-
-diff --git a/src/tools/sssctl/sssctl_cache.c b/src/tools/sssctl/sssctl_cache.c
-index 3e7644e327eb3e5a0e33023e7a3f5f0f15e03cf6..22e3fad89e3c1bbfce48439b43acd2346da3e56f 100644
---- a/src/tools/sssctl/sssctl_cache.c
-+++ b/src/tools/sssctl/sssctl_cache.c
-@@ -296,10 +296,10 @@ static const char *sssctl_create_filter(TALLOC_CTX *mem_ctx,
-     char *filter_value;
-     bool qualify_attr = false;
- 
--    if (strcmp(attr_name, SYSDB_NAME) == 0 &&
--            (obj_type == CACHED_USER ||
--             obj_type == CACHED_GROUP)) {
--        qualify_attr = true;
-+    if (strcmp(attr_name, SYSDB_NAME) == 0) {
-+        if (obj_type == CACHED_USER || obj_type == CACHED_GROUP) {
-+            qualify_attr = true;
-+        }
-     }
- 
-     switch (obj_type) {
--- 
-2.4.11
-
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
new file mode 100644
index 0000000..09d388a
--- /dev/null
+++ b/SOURCES/0031-SECRETS-Store-ccaches-in-secrets-for-the-KCM-respond.patch
@@ -0,0 +1,206 @@
+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-sssctl-Use-localtime-for-time-stamps.patch b/SOURCES/0031-sssctl-Use-localtime-for-time-stamps.patch
deleted file mode 100644
index d94a0eb..0000000
--- a/SOURCES/0031-sssctl-Use-localtime-for-time-stamps.patch
+++ /dev/null
@@ -1,34 +0,0 @@
-From e6cfc113e6f3f7cc1c941d12e0ac7032f19affb9 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Fabiano=20Fid=C3=AAncio?= <fidencio@redhat.com>
-Date: Thu, 14 Jul 2016 15:33:19 +0200
-Subject: [PATCH 31/31] sssctl: Use localtime for time stamps
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Resolves:
-https://fedorahosted.org/sssd/ticket/3096
-
-Signed-off-by: Fabiano Fidêncio <fidencio@redhat.com>
-Reviewed-by: Pavel Březina <pbrezina@redhat.com>
-(cherry picked from commit f316e5446313bbf61c96b6763badc21604bff4a3)
----
- src/tools/sssctl/sssctl_cache.c | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
-diff --git a/src/tools/sssctl/sssctl_cache.c b/src/tools/sssctl/sssctl_cache.c
-index 22e3fad89e3c1bbfce48439b43acd2346da3e56f..4a1f3558ed7064ca40ccf9313d99fbab36e6e4c9 100644
---- a/src/tools/sssctl/sssctl_cache.c
-+++ b/src/tools/sssctl/sssctl_cache.c
-@@ -67,7 +67,7 @@ static errno_t time_to_string(TALLOC_CTX *mem_ctx,
-     char str[255];
-     size_t ret;
- 
--    tm = gmtime(&timestamp);
-+    tm = localtime(&timestamp);
-     if (tm == NULL) {
-         return ENOMEM;
-     }
--- 
-2.4.11
-
diff --git a/SOURCES/0032-SECRETS-Log-message-for-failures-with-removing-file.patch b/SOURCES/0032-SECRETS-Log-message-for-failures-with-removing-file.patch
deleted file mode 100644
index 10627ef..0000000
--- a/SOURCES/0032-SECRETS-Log-message-for-failures-with-removing-file.patch
+++ /dev/null
@@ -1,38 +0,0 @@
-From 38b3bd9bde495d44283de2b837ab0239140edb3d Mon Sep 17 00:00:00 2001
-From: Lukas Slebodnik <lslebodn@redhat.com>
-Date: Fri, 22 Jul 2016 09:53:40 +0200
-Subject: [PATCH 32/44] SECRETS: Log message for failures with removing file
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Type: Unchecked return value
-Reported by coverity
-
-Reviewed-by: Petr Čech <pcech@redhat.com>
----
- src/responder/secrets/local.c | 8 +++++++-
- 1 file changed, 7 insertions(+), 1 deletion(-)
-
-diff --git a/src/responder/secrets/local.c b/src/responder/secrets/local.c
-index 620134ea6b45629114ba795d0e232f414f6e5009..2a85ac06945322265fbd1012c9697728c37b77a0 100644
---- a/src/responder/secrets/local.c
-+++ b/src/responder/secrets/local.c
-@@ -624,7 +624,13 @@ int generate_master_key(const char *filename, size_t size)
-     rsize = sss_atomic_io_s(fd, buf, size, false);
-     close(fd);
-     if (rsize != size) {
--        unlink(filename);
-+        ret = unlink(filename);
-+        /* non-fatal failure */
-+        if (ret != EOK) {
-+            DEBUG(SSSDBG_MINOR_FAILURE,
-+                  "Failed to remove file: %s - %d [%s]!\n",
-+                  filename, ret, sss_strerror(ret));
-+        }
-         return EFAULT;
-     }
- 
--- 
-2.4.11
-
diff --git a/SOURCES/0032-TCURL-Support-HTTP-POST-for-creating-containers.patch b/SOURCES/0032-TCURL-Support-HTTP-POST-for-creating-containers.patch
new file mode 100644
index 0000000..c94fe07
--- /dev/null
+++ b/SOURCES/0032-TCURL-Support-HTTP-POST-for-creating-containers.patch
@@ -0,0 +1,129 @@
+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-IPA-fix-capaths-output.patch b/SOURCES/0033-IPA-fix-capaths-output.patch
deleted file mode 100644
index f5b2f92..0000000
--- a/SOURCES/0033-IPA-fix-capaths-output.patch
+++ /dev/null
@@ -1,67 +0,0 @@
-From cf161fe0317fd37e1c5ad826cb783905aaf1f048 Mon Sep 17 00:00:00 2001
-From: Sumit Bose <sbose@redhat.com>
-Date: Mon, 18 Jul 2016 17:19:36 +0200
-Subject: [PATCH 33/44] IPA: fix [capaths] output
-
-the capaths for a single domain should be collected in a single
-sub-section in the MIT Kerberos configuration not spread over multiple
-one. See the capaths section of the krb5.conf man page for details.
-
-Resolves:
-https://fedorahosted.org/sssd/ticket/3103
-
-Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
----
- src/util/domain_info_utils.c | 29 ++++++++++++++++++++++++++---
- 1 file changed, 26 insertions(+), 3 deletions(-)
-
-diff --git a/src/util/domain_info_utils.c b/src/util/domain_info_utils.c
-index 360f70376c472466168d93d45b6c547d51dd18c6..8cdd50d8d521d734e9ffd9b4e81cd6fbd7d158c7 100644
---- a/src/util/domain_info_utils.c
-+++ b/src/util/domain_info_utils.c
-@@ -280,6 +280,7 @@ sss_write_domain_mappings(struct sss_domain_info *domain)
-     bool capaths_started = false;
-     char *uc_forest;
-     char *uc_parent;
-+    char *parent_capaths = NULL;
- 
-     if (domain == NULL || domain->name == NULL) {
-         DEBUG(SSSDBG_CRIT_FAILURE, "No domain name provided\n");
-@@ -399,9 +400,31 @@ sss_write_domain_mappings(struct sss_domain_info *domain)
-             capaths_started = true;
-         }
- 
--        ret = fprintf(fstream, "%s = {\n  %s = %s\n}\n%s = {\n  %s = %s\n}\n",
--                                dom->realm, uc_parent, uc_forest,
--                                uc_parent, dom->realm, uc_forest);
-+        ret = fprintf(fstream, "%s = {\n  %s = %s\n}\n",
-+                                dom->realm, uc_parent, uc_forest);
-+        if (ret < 0) {
-+            DEBUG(SSSDBG_CRIT_FAILURE, "fprintf failed\n");
-+            goto done;
-+        }
-+
-+        if (parent_capaths == NULL) {
-+            parent_capaths = talloc_asprintf(tmp_ctx, "  %s = %s\n", dom->realm,
-+                                                                     uc_forest);
-+        } else {
-+            parent_capaths = talloc_asprintf_append(parent_capaths,
-+                                                    "  %s = %s\n", dom->realm,
-+                                                    uc_forest);
-+        }
-+        if (parent_capaths == NULL) {
-+            DEBUG(SSSDBG_OP_FAILURE,
-+                  "talloc_asprintf/talloc_asprintf_append failed.\n");
-+            ret = ENOMEM;
-+            goto done;
-+        }
-+    }
-+
-+    if (parent_capaths != NULL) {
-+        ret = fprintf(fstream, "%s = {\n%s}\n", uc_parent, parent_capaths);
-         if (ret < 0) {
-             DEBUG(SSSDBG_CRIT_FAILURE, "fprintf failed\n");
-             goto done;
--- 
-2.4.11
-
diff --git a/SOURCES/0033-KCM-Store-ccaches-in-secrets.patch b/SOURCES/0033-KCM-Store-ccaches-in-secrets.patch
new file mode 100644
index 0000000..d08a786
--- /dev/null
+++ b/SOURCES/0033-KCM-Store-ccaches-in-secrets.patch
@@ -0,0 +1,3787 @@
+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/0034-KCM-Make-the-secrets-ccache-back-end-configurable-ma.patch b/SOURCES/0034-KCM-Make-the-secrets-ccache-back-end-configurable-ma.patch
new file mode 100644
index 0000000..2d65a32
--- /dev/null
+++ b/SOURCES/0034-KCM-Make-the-secrets-ccache-back-end-configurable-ma.patch
@@ -0,0 +1,219 @@
+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/0034-UTIL-make-domain-mapping-content-testable.patch b/SOURCES/0034-UTIL-make-domain-mapping-content-testable.patch
deleted file mode 100644
index c3dcc69..0000000
--- a/SOURCES/0034-UTIL-make-domain-mapping-content-testable.patch
+++ /dev/null
@@ -1,286 +0,0 @@
-From 9d02728f8d64742e28f32fdf5bfdf083dc15a5c8 Mon Sep 17 00:00:00 2001
-From: Sumit Bose <sbose@redhat.com>
-Date: Mon, 18 Jul 2016 17:37:49 +0200
-Subject: [PATCH 34/44] UTIL: make domain mapping content testable
-
-Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
----
- src/util/domain_info_utils.c | 216 +++++++++++++++++++++++++++----------------
- src/util/util.h              |   4 +
- 2 files changed, 138 insertions(+), 82 deletions(-)
-
-diff --git a/src/util/domain_info_utils.c b/src/util/domain_info_utils.c
-index 8cdd50d8d521d734e9ffd9b4e81cd6fbd7d158c7..587a6b993d2bd70662df8e0b0d5963fa00c84cf8 100644
---- a/src/util/domain_info_utils.c
-+++ b/src/util/domain_info_utils.c
-@@ -262,11 +262,135 @@ sss_krb5_touch_config(void)
-     return EOK;
- }
- 
-+errno_t sss_get_domain_mappings_content(TALLOC_CTX *mem_ctx,
-+                                        struct sss_domain_info *domain,
-+                                        char **content)
-+{
-+    int ret;
-+    char *o = NULL;
-+    struct sss_domain_info *dom;
-+    struct sss_domain_info *parent_dom;
-+    char *uc_parent = NULL;
-+    char *uc_forest = NULL;
-+    char *parent_capaths = NULL;
-+    bool capaths_started = false;
-+
-+    if (domain == NULL || content == NULL) {
-+        DEBUG(SSSDBG_CRIT_FAILURE, "Missing parameter.\n");
-+        return EINVAL;
-+    }
-+
-+    o = talloc_strdup(mem_ctx, "[domain_realm]\n");
-+    if (o == NULL) {
-+        DEBUG(SSSDBG_OP_FAILURE, "talloc_strdup failed.\n");
-+        ret = ENOMEM;
-+        goto done;
-+    }
-+
-+    /* This loops skips the starting parent and start rigth with the first
-+     * subdomain. Although in all the interesting cases (AD and IPA) the
-+     * default is that realm and DNS domain are the same strings (expect case)
-+     * and no domain_realm mapping is needed we might consider to add this
-+     * domain here as well to cover corner cases? */
-+    for (dom = get_next_domain(domain, SSS_GND_DESCEND);
-+                dom && IS_SUBDOMAIN(dom); /* if we get back to a parent, stop */
-+                dom = get_next_domain(dom, 0)) {
-+        o = talloc_asprintf_append(o, ".%s = %s\n%s = %s\n",
-+                               dom->name, dom->realm, dom->name, dom->realm);
-+        if (o == NULL) {
-+            DEBUG(SSSDBG_OP_FAILURE, "talloc_asprintf_append failed.\n");
-+            ret = ENOMEM;
-+            goto done;
-+        }
-+    }
-+
-+    parent_dom = domain;
-+    uc_parent = get_uppercase_realm(mem_ctx, parent_dom->name);
-+    if (uc_parent == NULL) {
-+        DEBUG(SSSDBG_OP_FAILURE, "get_uppercase_realm failed.\n");
-+        ret = ENOMEM;
-+        goto done;
-+    }
-+
-+    for (dom = get_next_domain(domain, SSS_GND_DESCEND);
-+            dom && IS_SUBDOMAIN(dom); /* if we get back to a parent, stop */
-+            dom = get_next_domain(dom, 0)) {
-+
-+        if (dom->forest == NULL) {
-+            continue;
-+        }
-+
-+        talloc_free(uc_forest);
-+        uc_forest = get_uppercase_realm(mem_ctx, dom->forest);
-+        if (uc_forest == NULL) {
-+            DEBUG(SSSDBG_OP_FAILURE, "get_uppercase_realm failed.\n");
-+            ret = ENOMEM;
-+            goto done;
-+        }
-+
-+        if (!capaths_started) {
-+            o = talloc_asprintf_append(o, "[capaths]\n");
-+            if (o == NULL) {
-+                DEBUG(SSSDBG_OP_FAILURE, "talloc_asprintf_append failed.\n");
-+                ret = ENOMEM;
-+                goto done;
-+            }
-+            capaths_started = true;
-+        }
-+
-+        o = talloc_asprintf_append(o, "%s = {\n  %s = %s\n}\n",
-+                                   dom->realm, uc_parent, uc_forest);
-+        if (o == NULL) {
-+            DEBUG(SSSDBG_OP_FAILURE, "talloc_asprintf_append failed.\n");
-+            ret = ENOMEM;
-+            goto done;
-+        }
-+
-+        if (parent_capaths == NULL) {
-+            parent_capaths = talloc_asprintf(mem_ctx, "  %s = %s\n", dom->realm,
-+                                                                     uc_forest);
-+        } else {
-+            parent_capaths = talloc_asprintf_append(parent_capaths,
-+                                                    "  %s = %s\n", dom->realm,
-+                                                    uc_forest);
-+        }
-+        if (parent_capaths == NULL) {
-+            DEBUG(SSSDBG_OP_FAILURE,
-+                  "talloc_asprintf/talloc_asprintf_append failed.\n");
-+            ret = ENOMEM;
-+            goto done;
-+        }
-+    }
-+
-+    if (parent_capaths != NULL) {
-+        o = talloc_asprintf_append(o, "%s = {\n%s}\n", uc_parent,
-+                                                       parent_capaths);
-+        if (o == NULL) {
-+            DEBUG(SSSDBG_OP_FAILURE, "talloc_asprintf_append failed.\n");
-+            ret = ENOMEM;
-+            goto done;
-+        }
-+    }
-+
-+    ret = EOK;
-+
-+done:
-+    talloc_free(parent_capaths);
-+    talloc_free(uc_parent);
-+    talloc_free(uc_forest);
-+
-+    if (ret == EOK) {
-+        *content = o;
-+    } else {
-+        talloc_free(o);
-+    }
-+
-+    return ret;
-+}
-+
- errno_t
- sss_write_domain_mappings(struct sss_domain_info *domain)
- {
--    struct sss_domain_info *dom;
--    struct sss_domain_info *parent_dom;
-     errno_t ret;
-     errno_t err;
-     TALLOC_CTX *tmp_ctx;
-@@ -277,10 +401,7 @@ sss_write_domain_mappings(struct sss_domain_info *domain)
-     mode_t old_mode;
-     FILE *fstream = NULL;
-     int i;
--    bool capaths_started = false;
--    char *uc_forest;
--    char *uc_parent;
--    char *parent_capaths = NULL;
-+    char *content = NULL;
- 
-     if (domain == NULL || domain->name == NULL) {
-         DEBUG(SSSDBG_CRIT_FAILURE, "No domain name provided\n");
-@@ -290,6 +411,12 @@ sss_write_domain_mappings(struct sss_domain_info *domain)
-     tmp_ctx = talloc_new(NULL);
-     if (!tmp_ctx) return ENOMEM;
- 
-+    ret = sss_get_domain_mappings_content(tmp_ctx, domain, &content);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_OP_FAILURE, "sss_get_domain_mappings_content failed.\n");
-+        goto done;
-+    }
-+
-     sanitized_domain = talloc_strdup(tmp_ctx, domain->name);
-     if (sanitized_domain == NULL) {
-         DEBUG(SSSDBG_CRIT_FAILURE, "talloc_strdup() failed\n");
-@@ -349,88 +476,13 @@ sss_write_domain_mappings(struct sss_domain_info *domain)
-         goto done;
-     }
- 
--    ret = fprintf(fstream, "[domain_realm]\n");
-+    ret = fprintf(fstream, "%s", content);
-     if (ret < 0) {
-         DEBUG(SSSDBG_OP_FAILURE, "fprintf failed\n");
-         ret = EIO;
-         goto done;
-     }
- 
--    for (dom = get_next_domain(domain, SSS_GND_DESCEND);
--         dom && IS_SUBDOMAIN(dom); /* if we get back to a parent, stop */
--         dom = get_next_domain(dom, 0)) {
--        ret = fprintf(fstream, ".%s = %s\n%s = %s\n",
--                               dom->name, dom->realm, dom->name, dom->realm);
--        if (ret < 0) {
--            DEBUG(SSSDBG_CRIT_FAILURE, "fprintf failed\n");
--            goto done;
--        }
--    }
--
--    parent_dom = domain;
--    uc_parent = get_uppercase_realm(tmp_ctx, parent_dom->name);
--    if (uc_parent == NULL) {
--        DEBUG(SSSDBG_OP_FAILURE, "get_uppercase_realm failed.\n");
--        ret = ENOMEM;
--        goto done;
--    }
--
--    for (dom = get_next_domain(domain, SSS_GND_DESCEND);
--            dom && IS_SUBDOMAIN(dom); /* if we get back to a parent, stop */
--            dom = get_next_domain(dom, 0)) {
--
--        if (dom->forest == NULL) {
--            continue;
--        }
--
--        uc_forest = get_uppercase_realm(tmp_ctx, dom->forest);
--        if (uc_forest == NULL) {
--            DEBUG(SSSDBG_OP_FAILURE, "get_uppercase_realm failed.\n");
--            ret = ENOMEM;
--            goto done;
--        }
--
--        if (!capaths_started) {
--            ret = fprintf(fstream, "[capaths]\n");
--            if (ret < 0) {
--                DEBUG(SSSDBG_OP_FAILURE, "fprintf failed\n");
--                ret = EIO;
--                goto done;
--            }
--            capaths_started = true;
--        }
--
--        ret = fprintf(fstream, "%s = {\n  %s = %s\n}\n",
--                                dom->realm, uc_parent, uc_forest);
--        if (ret < 0) {
--            DEBUG(SSSDBG_CRIT_FAILURE, "fprintf failed\n");
--            goto done;
--        }
--
--        if (parent_capaths == NULL) {
--            parent_capaths = talloc_asprintf(tmp_ctx, "  %s = %s\n", dom->realm,
--                                                                     uc_forest);
--        } else {
--            parent_capaths = talloc_asprintf_append(parent_capaths,
--                                                    "  %s = %s\n", dom->realm,
--                                                    uc_forest);
--        }
--        if (parent_capaths == NULL) {
--            DEBUG(SSSDBG_OP_FAILURE,
--                  "talloc_asprintf/talloc_asprintf_append failed.\n");
--            ret = ENOMEM;
--            goto done;
--        }
--    }
--
--    if (parent_capaths != NULL) {
--        ret = fprintf(fstream, "%s = {\n%s}\n", uc_parent, parent_capaths);
--        if (ret < 0) {
--            DEBUG(SSSDBG_CRIT_FAILURE, "fprintf failed\n");
--            goto done;
--        }
--    }
--
-     ret = fclose(fstream);
-     fstream = NULL;
-     if (ret != 0) {
-diff --git a/src/util/util.h b/src/util/util.h
-index 8a5caa52c2dc5243c3ae51c5a38fd65a949f4ac4..122be90b967fb7793adaff95f3754d7a199fcf48 100644
---- a/src/util/util.h
-+++ b/src/util/util.h
-@@ -540,6 +540,10 @@ errno_t sssd_domain_init(TALLOC_CTX *mem_ctx,
-  * written to */
- #define KRB5_MAPPING_DIR PUBCONF_PATH"/krb5.include.d"
- 
-+errno_t sss_get_domain_mappings_content(TALLOC_CTX *mem_ctx,
-+                                        struct sss_domain_info *domain,
-+                                        char **content);
-+
- errno_t sss_write_domain_mappings(struct sss_domain_info *domain);
- 
- errno_t sss_write_krb5_conf_snippet(const char *path, bool canonicalize);
--- 
-2.4.11
-
diff --git a/SOURCES/0035-KCM-Queue-requests-by-the-same-UID.patch b/SOURCES/0035-KCM-Queue-requests-by-the-same-UID.patch
new file mode 100644
index 0000000..338c394
--- /dev/null
+++ b/SOURCES/0035-KCM-Queue-requests-by-the-same-UID.patch
@@ -0,0 +1,909 @@
+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-tests-add-tests-for-sss_get_domain_mappings_content.patch b/SOURCES/0035-tests-add-tests-for-sss_get_domain_mappings_content.patch
deleted file mode 100644
index d2d75a0..0000000
--- a/SOURCES/0035-tests-add-tests-for-sss_get_domain_mappings_content.patch
+++ /dev/null
@@ -1,201 +0,0 @@
-From 16495f6aca6fb1a3f0cdb30b4a50493453e8ca0f Mon Sep 17 00:00:00 2001
-From: Sumit Bose <sbose@redhat.com>
-Date: Wed, 20 Jul 2016 12:03:48 +0200
-Subject: [PATCH 35/44] tests: add tests for sss_get_domain_mappings_content()
-
-Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
----
- src/tests/cmocka/test_utils.c | 163 ++++++++++++++++++++++++++++++++++++++++++
- 1 file changed, 163 insertions(+)
-
-diff --git a/src/tests/cmocka/test_utils.c b/src/tests/cmocka/test_utils.c
-index b08b19708bb59a076a79805fa37a15924152b8e2..4c72a59437105e683cec2d39c36951aeff63767b 100644
---- a/src/tests/cmocka/test_utils.c
-+++ b/src/tests/cmocka/test_utils.c
-@@ -55,6 +55,95 @@ struct dom_list_test_ctx {
-     struct sss_domain_info *dom_list;
- };
- 
-+static int setup_dom_list_with_subdomains(void **state)
-+{
-+    struct dom_list_test_ctx *test_ctx;
-+    struct sss_domain_info *dom = NULL;
-+    struct sss_domain_info *c = NULL;
-+
-+    assert_true(leak_check_setup());
-+
-+    test_ctx = talloc_zero(global_talloc_context, struct dom_list_test_ctx);
-+    assert_non_null(test_ctx);
-+
-+    dom = talloc_zero(test_ctx, struct sss_domain_info);
-+    assert_non_null(dom);
-+
-+    dom->name = talloc_asprintf(dom, "configured.dom");
-+    assert_non_null(dom->name);
-+
-+    dom->realm = talloc_asprintf(dom, "CONFIGURED.DOM");
-+    assert_non_null(dom->realm);
-+
-+    dom->flat_name = talloc_asprintf(dom, "CONFIGURED");
-+    assert_non_null(dom->flat_name);
-+
-+    dom->domain_id = talloc_asprintf(dom, "S-1-5-21-1-2-1");
-+    assert_non_null(dom->domain_id);
-+
-+    DLIST_ADD(test_ctx->dom_list, dom);
-+
-+    c = talloc_zero(test_ctx, struct sss_domain_info);
-+    assert_non_null(c);
-+
-+    c->name = talloc_asprintf(c, "subdom1.dom");
-+    assert_non_null(c->name);
-+
-+    c->realm = talloc_asprintf(c, "SUBDOM1.DOM");
-+    assert_non_null(c->realm);
-+
-+    c->flat_name = talloc_asprintf(c, "subdom1");
-+    assert_non_null(c->flat_name);
-+
-+    c->domain_id = talloc_asprintf(c, "S-1-5-21-1-2-2");
-+    assert_non_null(c->domain_id);
-+
-+    c->parent = dom;
-+
-+    DLIST_ADD_END(test_ctx->dom_list, c, struct sss_domain_info *);
-+
-+    c = talloc_zero(test_ctx, struct sss_domain_info);
-+    assert_non_null(c);
-+
-+    c->name = talloc_asprintf(c, "subdom2.dom");
-+    assert_non_null(c->name);
-+
-+    c->realm = talloc_asprintf(c, "SUBDOM2.DOM");
-+    assert_non_null(c->realm);
-+
-+    c->flat_name = talloc_asprintf(c, "subdom2");
-+    assert_non_null(c->flat_name);
-+
-+    c->domain_id = talloc_asprintf(c, "S-1-5-21-1-2-3");
-+    assert_non_null(c->domain_id);
-+
-+    c->parent = dom;
-+
-+    DLIST_ADD_END(test_ctx->dom_list, c, struct sss_domain_info *);
-+
-+    c = talloc_zero(test_ctx, struct sss_domain_info);
-+    assert_non_null(c);
-+
-+    c->name = talloc_asprintf(c, "subdom3.dom");
-+    assert_non_null(c->name);
-+
-+    c->realm = talloc_asprintf(c, "SUBDOM3.DOM");
-+    assert_non_null(c->realm);
-+
-+    c->flat_name = talloc_asprintf(c, "subdom3");
-+    assert_non_null(c->flat_name);
-+
-+    c->domain_id = talloc_asprintf(c, "S-1-5-21-1-2-4");
-+    assert_non_null(c->domain_id);
-+
-+    c->parent = dom;
-+
-+    DLIST_ADD_END(test_ctx->dom_list, c, struct sss_domain_info *);
-+
-+    check_leaks_push(test_ctx);
-+    *state = test_ctx;
-+    return 0;
-+}
- 
- static int setup_dom_list(void **state)
- {
-@@ -1682,6 +1771,77 @@ static void test_sss_output_name(void **state)
-     assert_true(check_leaks_pop(global_talloc_context) == true);
- }
- 
-+static void test_sss_get_domain_mappings_content(void **state)
-+{
-+    struct dom_list_test_ctx *test_ctx;
-+    int ret;
-+    struct sss_domain_info *dom;
-+    char *content;
-+    struct sss_domain_info *c;
-+
-+    ret = sss_get_domain_mappings_content(NULL, NULL, NULL);
-+    assert_int_equal(ret, EINVAL);
-+
-+    test_ctx = talloc_get_type(*state, struct dom_list_test_ctx);
-+    assert_non_null(test_ctx);
-+
-+    dom = get_domains_head(test_ctx->dom_list);
-+    assert_non_null(dom);
-+
-+    /* no forest */
-+    ret = sss_get_domain_mappings_content(test_ctx, dom, &content);
-+    assert_int_equal(ret, EOK);
-+    assert_string_equal(content,
-+                        "[domain_realm]\n"
-+                        ".subdom1.dom = SUBDOM1.DOM\n"
-+                        "subdom1.dom = SUBDOM1.DOM\n"
-+                        ".subdom2.dom = SUBDOM2.DOM\n"
-+                        "subdom2.dom = SUBDOM2.DOM\n"
-+                        ".subdom3.dom = SUBDOM3.DOM\n"
-+                        "subdom3.dom = SUBDOM3.DOM\n");
-+    talloc_free(content);
-+
-+    /* IPA with forest */
-+    c = find_domain_by_name(dom, "subdom2.dom", true);
-+    assert_non_null(c);
-+    c->forest_root = find_domain_by_name(dom, "subdom1.dom", true);
-+    assert_non_null(c->forest_root);
-+    c->forest = "subdom1.dom";
-+
-+    c = find_domain_by_name(dom, "subdom3.dom", true);
-+    assert_non_null(c);
-+    c->forest_root = find_domain_by_name(dom, "subdom1.dom", true);
-+    assert_non_null(c->forest_root);
-+    c->forest = "subdom1.dom";
-+
-+    ret = sss_get_domain_mappings_content(test_ctx, dom, &content);
-+    assert_int_equal(ret, EOK);
-+    assert_string_equal(content,
-+                        "[domain_realm]\n"
-+                        ".subdom1.dom = SUBDOM1.DOM\n"
-+                        "subdom1.dom = SUBDOM1.DOM\n"
-+                        ".subdom2.dom = SUBDOM2.DOM\n"
-+                        "subdom2.dom = SUBDOM2.DOM\n"
-+                        ".subdom3.dom = SUBDOM3.DOM\n"
-+                        "subdom3.dom = SUBDOM3.DOM\n"
-+                        "[capaths]\n"
-+                        "SUBDOM2.DOM = {\n"
-+                        "  CONFIGURED.DOM = SUBDOM1.DOM\n"
-+                        "}\n"
-+                        "SUBDOM3.DOM = {\n"
-+                        "  CONFIGURED.DOM = SUBDOM1.DOM\n"
-+                        "}\n"
-+                        "CONFIGURED.DOM = {\n"
-+                        "  SUBDOM2.DOM = SUBDOM1.DOM\n"
-+                        "  SUBDOM3.DOM = SUBDOM1.DOM\n"
-+                        "}\n");
-+    talloc_free(content);
-+
-+    /* Next steps, test AD domain setup. If we join a child domain we have a
-+     * similar case as with IPA but if we join the forest root the generate
-+     * capaths might not be as expected. */
-+}
-+
- int main(int argc, const char *argv[])
- {
-     poptContext pc;
-@@ -1773,6 +1933,9 @@ int main(int argc, const char *argv[])
-         cmocka_unit_test_setup_teardown(test_sss_output_name,
-                                         setup_leak_tests,
-                                         teardown_leak_tests),
-+        cmocka_unit_test_setup_teardown(test_sss_get_domain_mappings_content,
-+                                        setup_dom_list_with_subdomains,
-+                                        teardown_dom_list),
-     };
- 
-     /* Set debug level to invalid value so we can deside if -d 0 was used. */
--- 
-2.4.11
-
diff --git a/SOURCES/0036-Amend-debug-messages-after-failure-of-unlink.patch b/SOURCES/0036-Amend-debug-messages-after-failure-of-unlink.patch
deleted file mode 100644
index 5a2a303..0000000
--- a/SOURCES/0036-Amend-debug-messages-after-failure-of-unlink.patch
+++ /dev/null
@@ -1,151 +0,0 @@
-From ce5063550ef45f7abb3ec2ec3cb52a190bb38af5 Mon Sep 17 00:00:00 2001
-From: Lukas Slebodnik <lslebodn@redhat.com>
-Date: Mon, 25 Jul 2016 09:11:08 +0200
-Subject: [PATCH 36/44] Amend debug messages after failure of unlink
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Some messages did not have errno or name of problematic file.
-There was also improper use of negative value.
-The function strerror was called with -1 instead of errno
-
-Reviewed-by: Petr Čech <pcech@redhat.com>
----
- src/providers/ipa/ipa_init.c            | 5 +++--
- src/responder/common/responder_common.c | 3 ++-
- src/responder/secrets/local.c           | 1 +
- src/sbus/sssd_dbus_server.c             | 9 +++++----
- src/tools/files.c                       | 3 ++-
- src/tools/tools_mc_util.c               | 7 ++++---
- src/util/util.c                         | 6 ++++--
- 7 files changed, 21 insertions(+), 13 deletions(-)
-
-diff --git a/src/providers/ipa/ipa_init.c b/src/providers/ipa/ipa_init.c
-index 959cdb4a7c40c1be03dd1e7c66dee6e65ca76607..c31e195f48b2f369de1b78b38a0f3522d73d8dce 100644
---- a/src/providers/ipa/ipa_init.c
-+++ b/src/providers/ipa/ipa_init.c
-@@ -440,9 +440,10 @@ static void cleanup_ipa_preauth_indicator(void)
- 
-     ret = unlink(PAM_PREAUTH_INDICATOR);
-     if (ret != EOK) {
-+        ret = errno;
-         DEBUG(SSSDBG_OP_FAILURE,
--              "Failed to remove preauth indicator file [%s].\n",
--              PAM_PREAUTH_INDICATOR);
-+              "Failed to remove preauth indicator file [%s] %d [%s].\n",
-+              PAM_PREAUTH_INDICATOR, ret, sss_strerror(ret));
-     }
- }
- 
-diff --git a/src/responder/common/responder_common.c b/src/responder/common/responder_common.c
-index 02a64368cad60990436497865aa0c772a39cde5a..7f6264ae70e5073063b5cfcd73098eefad2ce653 100644
---- a/src/responder/common/responder_common.c
-+++ b/src/responder/common/responder_common.c
-@@ -629,7 +629,8 @@ int create_pipe_fd(const char *sock_name, int *_fd, mode_t umaskval)
-     if (ret != 0 && errno != ENOENT) {
-         ret = errno;
-         DEBUG(SSSDBG_MINOR_FAILURE,
--              "Cannot remove old socket (errno=%d), bind might fail!\n", ret);
-+              "Cannot remove old socket (errno=%d [%s]), bind might fail!\n",
-+              ret, sss_strerror(ret));
-     }
- 
-     if (bind(fd, (struct sockaddr *)&addr, sizeof(addr)) == -1) {
-diff --git a/src/responder/secrets/local.c b/src/responder/secrets/local.c
-index 2a85ac06945322265fbd1012c9697728c37b77a0..470aec0e195a54dd2af2b929ff1b7a304331a214 100644
---- a/src/responder/secrets/local.c
-+++ b/src/responder/secrets/local.c
-@@ -627,6 +627,7 @@ int generate_master_key(const char *filename, size_t size)
-         ret = unlink(filename);
-         /* non-fatal failure */
-         if (ret != EOK) {
-+            ret = errno;
-             DEBUG(SSSDBG_MINOR_FAILURE,
-                   "Failed to remove file: %s - %d [%s]!\n",
-                   filename, ret, sss_strerror(ret));
-diff --git a/src/sbus/sssd_dbus_server.c b/src/sbus/sssd_dbus_server.c
-index 0a1cace415b3514a6904e92a2d879538ce51a593..6cc4172a07118ff99bb6f122c3a1de25cee18c8c 100644
---- a/src/sbus/sssd_dbus_server.c
-+++ b/src/sbus/sssd_dbus_server.c
-@@ -103,8 +103,9 @@ create_socket_symlink(const char *filename, const char *symlink_filename)
-         ret = unlink(symlink_filename);
-         if (ret != 0) {
-             ret = errno;
--            DEBUG(SSSDBG_CRIT_FAILURE, "Cannot remove old symlink: [%d][%s].\n",
--                      ret, strerror(ret));
-+            DEBUG(SSSDBG_CRIT_FAILURE,
-+                  "Cannot remove old symlink '%s': [%d][%s].\n",
-+                  symlink_filename, ret, strerror(ret));
-             return EIO;
-         }
-         errno = 0;
-@@ -351,8 +352,8 @@ done:
-         if (tmp_ret != EOK) {
-             tmp_ret = errno;
-             DEBUG(SSSDBG_MINOR_FAILURE,
--                  "Failed to remove symbolic link: %d [%s]!\n",
--                  tmp_ret, sss_strerror(tmp_ret));
-+                  "Failed to remove symbolic link '%s': %d [%s]!\n",
-+                  symlink_filename, tmp_ret, sss_strerror(tmp_ret));
-         }
-     }
-     talloc_free(tmp_ctx);
-diff --git a/src/tools/files.c b/src/tools/files.c
-index 5364f5c0dd53aad71452e18b8d7f1f04532132a4..8f1aa68beeb2676b56733f49550de170b404c789 100644
---- a/src/tools/files.c
-+++ b/src/tools/files.c
-@@ -225,7 +225,8 @@ static int remove_tree_with_ctx(TALLOC_CTX *mem_ctx,
-             if (ret != 0) {
-                 ret = errno;
-                 DEBUG(SSSDBG_CRIT_FAILURE,
--                        "Removing file failed: [%d][%s]\n", ret, strerror(ret));
-+                      "Removing file failed '%s': [%d][%s]\n",
-+                      result->d_name, ret, strerror(ret));
-                 goto fail;
-             }
-         }
-diff --git a/src/tools/tools_mc_util.c b/src/tools/tools_mc_util.c
-index ce899eb3c674afe8271be526dc83aa909cbecf89..2516a1981ddd965d4cae8c469ed79aaef8fa7193 100644
---- a/src/tools/tools_mc_util.c
-+++ b/src/tools/tools_mc_util.c
-@@ -117,10 +117,11 @@ done:
-         if (ret == EOK) {
-             pret = unlink(mc_filename);
-             if (pret == -1) {
-+                pret = errno;
-                 DEBUG(SSSDBG_MINOR_FAILURE,
--                      "Failed to unlink file %s. "
--                       "Will be unlinked later by sssd_nss.\n",
--                       mc_filename);
-+                      "Failed to unlink file %s, %d [%s]. "
-+                      "Will be unlinked later by sssd_nss.\n",
-+                      mc_filename, pret, strerror(pret));
-             }
-         }
-     }
-diff --git a/src/util/util.c b/src/util/util.c
-index 89abfe734873161008dc9a7a42e7a87364f8074a..d4878bfaf4f0e92672756f12137d79ec65ef48f6 100644
---- a/src/util/util.c
-+++ b/src/util/util.c
-@@ -990,13 +990,15 @@ static int unlink_dbg(const char *filename)
- 
-     ret = unlink(filename);
-     if (ret != 0) {
--        if (errno == 2) {
-+        ret = errno;
-+        if (ret == ENOENT) {
-             DEBUG(SSSDBG_TRACE_INTERNAL,
-                   "File already removed: [%s]\n", filename);
-             return 0;
-         } else {
-             DEBUG(SSSDBG_CRIT_FAILURE,
--                  "Cannot remove temporary file [%s]\n", filename);
-+                  "Cannot remove temporary file [%s] %d [%s]\n",
-+                  filename, ret, strerror(ret));
-             return -1;
-         }
-     }
--- 
-2.4.11
-
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
new file mode 100644
index 0000000..a466a17
--- /dev/null
+++ b/SOURCES/0036-KCM-Idle-terminate-the-responder-if-the-secrets-back.patch
@@ -0,0 +1,55 @@
+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/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
new file mode 100644
index 0000000..bdf9ef7
--- /dev/null
+++ b/SOURCES/0037-CONFIGURE-Fix-fallback-if-pkg-config-for-uuid-is-mis.patch
@@ -0,0 +1,30 @@
+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-SYSDB-Do-not-try-to-modify-ts-cache-for-unsupported-.patch b/SOURCES/0037-SYSDB-Do-not-try-to-modify-ts-cache-for-unsupported-.patch
deleted file mode 100644
index ca36fa7..0000000
--- a/SOURCES/0037-SYSDB-Do-not-try-to-modify-ts-cache-for-unsupported-.patch
+++ /dev/null
@@ -1,40 +0,0 @@
-From af7accb9071ca5f73632e2b47c13f2ce7d26995e Mon Sep 17 00:00:00 2001
-From: Lukas Slebodnik <lslebodn@redhat.com>
-Date: Mon, 25 Jul 2016 08:31:17 +0200
-Subject: [PATCH 37/44] SYSDB: Do not try to modify ts cache for unsupported
- DNs
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Only users and groups have timestamp data in separate cache.
-It caused false positive warnings for autofs, netgroup ...
-
-Reviewed-by: Petr Čech <pcech@redhat.com>
----
- src/db/sysdb_ops.c | 7 ++++++-
- 1 file changed, 6 insertions(+), 1 deletion(-)
-
-diff --git a/src/db/sysdb_ops.c b/src/db/sysdb_ops.c
-index 4755ea3427b99a51d73b7b9134e357cf2b987613..19d6be03ede1bcec3bc7a4ed777e326460d80591 100644
---- a/src/db/sysdb_ops.c
-+++ b/src/db/sysdb_ops.c
-@@ -1198,9 +1198,14 @@ int sysdb_set_entry_attr(struct sysdb_ctx *sysdb,
-     sysdb_write = sysdb_entry_attrs_diff(sysdb, entry_dn, attrs, mod_op);
-     if (sysdb_write == true) {
-         ret = sysdb_set_cache_entry_attr(sysdb->ldb, entry_dn, attrs, mod_op);
-+        if (ret != EOK) {
-+            DEBUG(SSSDBG_MINOR_FAILURE,
-+                  "Cannot set attrs for %s, %d [%s]\n",
-+                  ldb_dn_get_linearized(entry_dn), ret, sss_strerror(ret));
-+        }
-     }
- 
--    if (ret == EOK) {
-+    if (ret == EOK && is_ts_ldb_dn(entry_dn)) {
-         tret = sysdb_set_ts_entry_attr(sysdb, entry_dn, attrs, mod_op);
-         if (tret != EOK) {
-             DEBUG(SSSDBG_MINOR_FAILURE,
--- 
-2.4.11
-
diff --git a/SOURCES/0038-AD-avoid-memory-leak-in-netlogon_get_domain_info-and.patch b/SOURCES/0038-AD-avoid-memory-leak-in-netlogon_get_domain_info-and.patch
deleted file mode 100644
index d92dc85..0000000
--- a/SOURCES/0038-AD-avoid-memory-leak-in-netlogon_get_domain_info-and.patch
+++ /dev/null
@@ -1,96 +0,0 @@
-From 83f4fbf2cb3f9318aedfa03e526671e3c444c40b Mon Sep 17 00:00:00 2001
-From: Sumit Bose <sbose@redhat.com>
-Date: Tue, 12 Jul 2016 13:16:43 +0200
-Subject: [PATCH 38/44] AD: avoid memory leak in netlogon_get_domain_info() and
- make it public
-
-Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
----
- src/providers/ad/ad_common.h      |  6 ++++++
- src/providers/ad/ad_domain_info.c | 29 ++++++++++++++++++++---------
- 2 files changed, 26 insertions(+), 9 deletions(-)
-
-diff --git a/src/providers/ad/ad_common.h b/src/providers/ad/ad_common.h
-index ce363c5a4122aa5e48ca83b0b2bdf63ff4372d91..f4a90e4f0a3fe5910071d5fe690d0a356e2a0bd1 100644
---- a/src/providers/ad/ad_common.h
-+++ b/src/providers/ad/ad_common.h
-@@ -185,4 +185,10 @@ errno_t ad_autofs_init(TALLOC_CTX *mem_ctx,
- errno_t ad_machine_account_password_renewal_init(struct be_ctx *be_ctx,
-                                                  struct ad_options *ad_opts);
- 
-+errno_t netlogon_get_domain_info(TALLOC_CTX *mem_ctx,
-+                                 struct sysdb_attrs *reply,
-+                                 char **_flat_name,
-+                                 char **_site,
-+                                 char **_forest);
-+
- #endif /* AD_COMMON_H_ */
-diff --git a/src/providers/ad/ad_domain_info.c b/src/providers/ad/ad_domain_info.c
-index 5f17ae5427b1206af3ad03dccce9452aefc2e6e2..a06379c263878aa95741055636d0a12764f3ad8c 100644
---- a/src/providers/ad/ad_domain_info.c
-+++ b/src/providers/ad/ad_domain_info.c
-@@ -35,12 +35,11 @@
- #include "providers/ad/ad_common.h"
- #include "util/util.h"
- 
--static errno_t
--netlogon_get_domain_info(TALLOC_CTX *mem_ctx,
--                         struct sysdb_attrs *reply,
--                         char **_flat_name,
--                         char **_site,
--                         char **_forest)
-+errno_t netlogon_get_domain_info(TALLOC_CTX *mem_ctx,
-+                                 struct sysdb_attrs *reply,
-+                                 char **_flat_name,
-+                                 char **_site,
-+                                 char **_forest)
- {
-     errno_t ret;
-     struct ldb_message_element *el;
-@@ -51,6 +50,7 @@ netlogon_get_domain_info(TALLOC_CTX *mem_ctx,
-     const char *flat_name;
-     const char *site;
-     const char *forest;
-+    TALLOC_CTX *tmp_ctx;
- 
-     ret = sysdb_attrs_get_el(reply, AD_AT_NETLOGON, &el);
-     if (ret != EOK) {
-@@ -66,13 +66,24 @@ netlogon_get_domain_info(TALLOC_CTX *mem_ctx,
-         return EIO;
-     }
- 
-+    tmp_ctx = talloc_new(NULL);
-+    if (tmp_ctx == NULL) {
-+        DEBUG(SSSDBG_OP_FAILURE, "talloc_new failed.\n");
-+        return ENOMEM;
-+    }
-+
-     blob.data = el->values[0].data;
-     blob.length = el->values[0].length;
- 
--    ndr_pull = ndr_pull_init_blob(&blob, mem_ctx);
-+    /* The ndr_pull_* calls do not use ndr_pull as a talloc context to
-+     * allocate memory but the second argument of ndr_pull_init_blob(). To
-+     * make sure no memory is leaked here a temporary talloc context is
-+     * needed. */
-+    ndr_pull = ndr_pull_init_blob(&blob, tmp_ctx);
-     if (ndr_pull == NULL) {
-         DEBUG(SSSDBG_OP_FAILURE, "ndr_pull_init_blob() failed.\n");
--        return ENOMEM;
-+        ret = ENOMEM;
-+        goto done;
-     }
- 
-     ndr_err = ndr_pull_netlogon_samlogon_response(ndr_pull, NDR_SCALARS,
-@@ -146,7 +157,7 @@ netlogon_get_domain_info(TALLOC_CTX *mem_ctx,
- 
-     ret = EOK;
- done:
--    talloc_free(ndr_pull);
-+    talloc_free(tmp_ctx);
-     return ret;
- }
- 
--- 
-2.4.11
-
diff --git a/SOURCES/0038-intg-fix-configure-failure-with-strict-cflags.patch b/SOURCES/0038-intg-fix-configure-failure-with-strict-cflags.patch
new file mode 100644
index 0000000..5c7c3be
--- /dev/null
+++ b/SOURCES/0038-intg-fix-configure-failure-with-strict-cflags.patch
@@ -0,0 +1,52 @@
+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/0039-AD-netlogon_get_domain_info-allow-missing-arguments-.patch b/SOURCES/0039-AD-netlogon_get_domain_info-allow-missing-arguments-.patch
deleted file mode 100644
index f81ee20..0000000
--- a/SOURCES/0039-AD-netlogon_get_domain_info-allow-missing-arguments-.patch
+++ /dev/null
@@ -1,217 +0,0 @@
-From df00f0d33874381fc36ee59eaf28a4918ae5dc56 Mon Sep 17 00:00:00 2001
-From: Sumit Bose <sbose@redhat.com>
-Date: Tue, 12 Jul 2016 13:29:33 +0200
-Subject: [PATCH 39/44] AD: netlogon_get_domain_info() allow missing arguments
- and empty results
-
-netlogon_get_domain_info() should not fail if not all parameters can be
-retrieved. It should be the responsibility of the caller to see if the
-needed data is available and act accordingly.
-
-Resolves:
-https://fedorahosted.org/sssd/ticket/3104
-
-Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
----
- src/providers/ad/ad_common.h      |   1 +
- src/providers/ad/ad_domain_info.c | 108 +++++++++++++++++++++-----------------
- src/providers/ad/ad_gpo.c         |   2 +-
- src/providers/ad/ad_subdomains.c  |   3 +-
- 4 files changed, 64 insertions(+), 50 deletions(-)
-
-diff --git a/src/providers/ad/ad_common.h b/src/providers/ad/ad_common.h
-index f4a90e4f0a3fe5910071d5fe690d0a356e2a0bd1..7e86faf1142d7be49eef01e1ddd7bfafea2fcedc 100644
---- a/src/providers/ad/ad_common.h
-+++ b/src/providers/ad/ad_common.h
-@@ -187,6 +187,7 @@ errno_t ad_machine_account_password_renewal_init(struct be_ctx *be_ctx,
- 
- errno_t netlogon_get_domain_info(TALLOC_CTX *mem_ctx,
-                                  struct sysdb_attrs *reply,
-+                                 bool check_next_nearest_site_as_well,
-                                  char **_flat_name,
-                                  char **_site,
-                                  char **_forest);
-diff --git a/src/providers/ad/ad_domain_info.c b/src/providers/ad/ad_domain_info.c
-index a06379c263878aa95741055636d0a12764f3ad8c..5302c8083bd832d0772829d9f3a4b8e9537d34b9 100644
---- a/src/providers/ad/ad_domain_info.c
-+++ b/src/providers/ad/ad_domain_info.c
-@@ -37,6 +37,7 @@
- 
- errno_t netlogon_get_domain_info(TALLOC_CTX *mem_ctx,
-                                  struct sysdb_attrs *reply,
-+                                 bool check_next_nearest_site_as_well,
-                                  char **_flat_name,
-                                  char **_site,
-                                  char **_forest)
-@@ -47,9 +48,6 @@ errno_t netlogon_get_domain_info(TALLOC_CTX *mem_ctx,
-     struct ndr_pull *ndr_pull = NULL;
-     enum ndr_err_code ndr_err;
-     struct netlogon_samlogon_response response;
--    const char *flat_name;
--    const char *site;
--    const char *forest;
-     TALLOC_CTX *tmp_ctx;
- 
-     ret = sysdb_attrs_get_el(reply, AD_AT_NETLOGON, &el);
-@@ -102,57 +100,73 @@ errno_t netlogon_get_domain_info(TALLOC_CTX *mem_ctx,
-         goto done;
-     }
- 
--    /* get flat name */
--    if (response.data.nt5_ex.domain_name != NULL &&
--        *response.data.nt5_ex.domain_name != '\0') {
--        flat_name = response.data.nt5_ex.domain_name;
--    } else {
--        DEBUG(SSSDBG_MINOR_FAILURE,
--              "No netlogon domain name data available\n");
--        ret = ENOENT;
--        goto done;
-+    /* get flat domain name */
-+    if (_flat_name != NULL) {
-+        if (response.data.nt5_ex.domain_name != NULL &&
-+            *response.data.nt5_ex.domain_name != '\0') {
-+            *_flat_name = talloc_strdup(mem_ctx,
-+                                        response.data.nt5_ex.domain_name);
-+            if (*_flat_name == NULL) {
-+                DEBUG(SSSDBG_OP_FAILURE, "talloc_strdup failed.\n");
-+                ret = ENOMEM;
-+                goto done;
-+            }
-+        } else {
-+            DEBUG(SSSDBG_MINOR_FAILURE,
-+                  "No netlogon flat domain name data available.\n");
-+            *_flat_name = NULL;
-+        }
-     }
- 
--    *_flat_name = talloc_strdup(mem_ctx, flat_name);
--    if (*_flat_name == NULL) {
--        DEBUG(SSSDBG_OP_FAILURE, "talloc_strdup failed.\n");
--        ret = ENOMEM;
--        goto done;
--    }
- 
-     /* get forest */
--    if (response.data.nt5_ex.forest != NULL &&
--        *response.data.nt5_ex.forest != '\0') {
--        forest = response.data.nt5_ex.forest;
--    } else {
--        DEBUG(SSSDBG_MINOR_FAILURE, "No netlogon forest data available\n");
--        ret = ENOENT;
--        goto done;
--    }
--
--    *_forest = talloc_strdup(mem_ctx, forest);
--    if (*_forest == NULL) {
--        DEBUG(SSSDBG_OP_FAILURE, "talloc_strdup failed.\n");
--        ret = ENOMEM;
--        goto done;
-+    if (_forest != NULL) {
-+        if (response.data.nt5_ex.forest != NULL &&
-+            *response.data.nt5_ex.forest != '\0') {
-+            *_forest = talloc_strdup(mem_ctx, response.data.nt5_ex.forest);
-+            if (*_forest == NULL) {
-+                DEBUG(SSSDBG_OP_FAILURE, "talloc_strdup failed.\n");
-+                ret = ENOMEM;
-+                goto done;
-+            }
-+        } else {
-+            DEBUG(SSSDBG_MINOR_FAILURE, "No netlogon forest data available.\n");
-+            *_forest = NULL;
-+        }
-     }
- 
-     /* get site name */
--    if (response.data.nt5_ex.client_site != NULL
--        && response.data.nt5_ex.client_site[0] != '\0') {
--        site = response.data.nt5_ex.client_site;
--    } else {
--        DEBUG(SSSDBG_MINOR_FAILURE,
--              "No netlogon site name data available\n");
--        ret = ENOENT;
--        goto done;
--    }
-+    if (_site != NULL) {
-+        if (response.data.nt5_ex.client_site != NULL
-+            && response.data.nt5_ex.client_site[0] != '\0') {
-+            *_site = talloc_strdup(mem_ctx, response.data.nt5_ex.client_site);
-+            if (*_site == NULL) {
-+                DEBUG(SSSDBG_OP_FAILURE, "talloc_strdup failed.\n");
-+                ret = ENOMEM;
-+                goto done;
-+            }
-+        } else {
-+            DEBUG(SSSDBG_MINOR_FAILURE,
-+                  "No netlogon site name data available.\n");
-+            *_site = NULL;
- 
--    *_site = talloc_strdup(mem_ctx, site);
--    if (*_site == NULL) {
--        DEBUG(SSSDBG_OP_FAILURE, "talloc_strdup failed.\n");
--        ret = ENOMEM;
--        goto done;
-+            if (check_next_nearest_site_as_well) {
-+                if (response.data.nt5_ex.next_closest_site != NULL
-+                        && response.data.nt5_ex.next_closest_site[0] != '\0') {
-+                    *_site = talloc_strdup(mem_ctx,
-+                                        response.data.nt5_ex.next_closest_site);
-+                    if (*_site == NULL) {
-+                        DEBUG(SSSDBG_OP_FAILURE, "talloc_strdup failed.\n");
-+                        ret = ENOMEM;
-+                        goto done;
-+                    }
-+                } else {
-+                    DEBUG(SSSDBG_MINOR_FAILURE,
-+                          "No netlogon next closest site name data "
-+                          "available.\n");
-+                }
-+            }
-+        }
-     }
- 
-     ret = EOK;
-@@ -388,7 +402,7 @@ ad_master_domain_netlogon_done(struct tevent_req *subreq)
- 
-     /* Exactly one flat name. Carry on */
- 
--    ret = netlogon_get_domain_info(state, reply[0], &state->flat,
-+    ret = netlogon_get_domain_info(state, reply[0], false, &state->flat,
-                                    &state->site, &state->forest);
-     if (ret != EOK) {
-         DEBUG(SSSDBG_MINOR_FAILURE,
-diff --git a/src/providers/ad/ad_gpo.c b/src/providers/ad/ad_gpo.c
-index 3e54fd8b5932a97779e178c7d3c5b9f6d3b3277c..f609d28136918adfe6a8d5e95319b27ffcab79c0 100644
---- a/src/providers/ad/ad_gpo.c
-+++ b/src/providers/ad/ad_gpo.c
-@@ -2801,7 +2801,7 @@ ad_gpo_site_name_retrieval_done(struct tevent_req *subreq)
-     ret = ad_master_domain_recv(subreq, state, NULL, NULL, &site, NULL);
-     talloc_zfree(subreq);
- 
--    if (ret != EOK) {
-+    if (ret != EOK || site == NULL) {
-         DEBUG(SSSDBG_OP_FAILURE, "Cannot retrieve master domain info\n");
-         tevent_req_error(req, ENOENT);
-         return;
-diff --git a/src/providers/ad/ad_subdomains.c b/src/providers/ad/ad_subdomains.c
-index 928c4fe93cc6afa5c3f69c14503896db820a4c0a..e9da04e384e598927f9c8c203a751bcccd29e895 100644
---- a/src/providers/ad/ad_subdomains.c
-+++ b/src/providers/ad/ad_subdomains.c
-@@ -1108,14 +1108,13 @@ static void ad_subdomains_refresh_master_done(struct tevent_req *subreq)
-     char *master_sid;
-     char *flat_name;
-     char *forest;
--    char *site;
-     errno_t ret;
- 
-     req = tevent_req_callback_data(subreq, struct tevent_req);
-     state = tevent_req_data(req, struct ad_subdomains_refresh_state);
- 
-     ret = ad_master_domain_recv(subreq, state, &flat_name, &master_sid,
--                                &site, &forest);
-+                                NULL, &forest);
-     talloc_zfree(subreq);
-     if (ret != EOK) {
-         DEBUG(SSSDBG_CRIT_FAILURE, "Unable to get master domain information "
--- 
-2.4.11
-
diff --git a/SOURCES/0039-intg-Remove-bashism-from-intgcheck-prepare.patch b/SOURCES/0039-intg-Remove-bashism-from-intgcheck-prepare.patch
new file mode 100644
index 0000000..d10c770
--- /dev/null
+++ b/SOURCES/0039-intg-Remove-bashism-from-intgcheck-prepare.patch
@@ -0,0 +1,54 @@
+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/0040-UTIL-Introduce-subdomain_create_conf_path.patch b/SOURCES/0040-UTIL-Introduce-subdomain_create_conf_path.patch
new file mode 100644
index 0000000..0dc6e0c
--- /dev/null
+++ b/SOURCES/0040-UTIL-Introduce-subdomain_create_conf_path.patch
@@ -0,0 +1,127 @@
+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-tests-add-tests-for-netlogon_get_domain_info.patch b/SOURCES/0040-tests-add-tests-for-netlogon_get_domain_info.patch
deleted file mode 100644
index b429a27..0000000
--- a/SOURCES/0040-tests-add-tests-for-netlogon_get_domain_info.patch
+++ /dev/null
@@ -1,125 +0,0 @@
-From 4319dabb39ea91d1c1cd9fe5294e17706045bd48 Mon Sep 17 00:00:00 2001
-From: Sumit Bose <sbose@redhat.com>
-Date: Mon, 11 Jul 2016 17:05:29 +0200
-Subject: [PATCH 40/44] tests: add tests for netlogon_get_domain_info
-
-Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
----
- Makefile.am                       |  1 +
- src/tests/cmocka/test_ad_common.c | 81 +++++++++++++++++++++++++++++++++++++++
- 2 files changed, 82 insertions(+)
-
-diff --git a/Makefile.am b/Makefile.am
-index d05919705910fa565ff954224ce40feb5d7ff39f..cefd9a43442fc19933f1e373d4f2ed4bb3ba3201 100644
---- a/Makefile.am
-+++ b/Makefile.am
-@@ -2470,6 +2470,7 @@ ad_common_tests_SOURCES = \
-     src/providers/ad/ad_opts.c \
-     src/providers/ad/ad_pac.c \
-     src/providers/ad/ad_pac_common.c \
-+    src/providers/ad/ad_domain_info.c \
-     src/providers/ldap/sdap_async_initgroups_ad.c \
-     $(NULL)
- ad_common_tests_CFLAGS = \
-diff --git a/src/tests/cmocka/test_ad_common.c b/src/tests/cmocka/test_ad_common.c
-index b7a5838de04e1a788bb9a61e84df00fc9bcc784b..7987330e2d0f5df93b62e4a68f34bc4ae23bd79b 100644
---- a/src/tests/cmocka/test_ad_common.c
-+++ b/src/tests/cmocka/test_ad_common.c
-@@ -802,6 +802,84 @@ void test_user_conn_list(void **state)
-     talloc_free(conn_list);
- }
- 
-+void test_netlogon_get_domain_info(void **state)
-+{
-+    int ret;
-+    struct sysdb_attrs *attrs;
-+    struct ldb_val val = { 0 };
-+    char *flat_name;
-+    char *site;
-+    char *forest;
-+
-+    struct ad_common_test_ctx *test_ctx = talloc_get_type(*state,
-+                                                     struct ad_common_test_ctx);
-+    assert_non_null(test_ctx);
-+
-+    attrs = sysdb_new_attrs(test_ctx);
-+    assert_non_null(attrs);
-+
-+    ret = netlogon_get_domain_info(test_ctx, attrs, false, NULL, NULL, NULL);
-+    assert_int_equal(ret, ENOENT);
-+
-+    ret = sysdb_attrs_add_val(attrs, AD_AT_NETLOGON, &val);
-+    assert_int_equal(ret, EOK);
-+
-+    ret = netlogon_get_domain_info(test_ctx, attrs, false, NULL, NULL, NULL);
-+    assert_int_equal(ret, EBADMSG);
-+
-+    talloc_free(attrs);
-+    attrs = sysdb_new_attrs(test_ctx);
-+    assert_non_null(attrs);
-+
-+    val.data = sss_base64_decode(test_ctx, "FwAAAP0zAABsGcIYI7j2TL97Rd+TvpATAmFkBWRldmVsAMAYCWFkLXNlcnZlcsAYAkFEAAlBRC1TRVJWRVIAABdEZWZhdWx0LUZpcnN0LVNpdGUtTmFtZQDAQAUAAAD/////", &val.length);
-+    assert_non_null(val.data);
-+
-+    ret = sysdb_attrs_add_val(attrs, AD_AT_NETLOGON, &val);
-+    assert_int_equal(ret, EOK);
-+
-+    ret = netlogon_get_domain_info(test_ctx, attrs, false, &flat_name, &site, &forest);
-+    assert_int_equal(ret, EOK);
-+    assert_string_equal(flat_name, "AD");
-+    assert_string_equal(site, "Default-First-Site-Name");
-+    assert_string_equal(forest, "ad.devel");
-+
-+    /* missing site */
-+    talloc_free(flat_name);
-+    talloc_free(site);
-+    talloc_free(forest);
-+    talloc_free(val.data);
-+    talloc_free(attrs);
-+    attrs = sysdb_new_attrs(test_ctx);
-+    assert_non_null(attrs);
-+
-+    val.data = sss_base64_decode(test_ctx, "FwAAAH0zAABsGcIYI7j2TL97Rd+TvpATAmFkBWRldmVsAMAYCWFkLXNlcnZlcsAYAkFEAAlBRC1TRVJWRVIAABdEZWZhdWx0LUZpcnN0LVNpdGUtTmFtZQAABQAAAP////8=", &val.length);
-+    assert_non_null(val.data);
-+
-+    ret = sysdb_attrs_add_val(attrs, AD_AT_NETLOGON, &val);
-+    assert_int_equal(ret, EOK);
-+
-+    ret = netlogon_get_domain_info(test_ctx, attrs, false, &flat_name, &site, &forest);
-+    assert_int_equal(ret, EOK);
-+    assert_string_equal(flat_name, "AD");
-+    assert_null(site);
-+    assert_string_equal(forest, "ad.devel");
-+
-+    talloc_free(flat_name);
-+    talloc_free(site);
-+    talloc_free(forest);
-+    ret = netlogon_get_domain_info(test_ctx, attrs, true, &flat_name, &site, &forest);
-+    assert_int_equal(ret, EOK);
-+    assert_string_equal(flat_name, "AD");
-+    assert_null(site);
-+    assert_string_equal(forest, "ad.devel");
-+
-+    talloc_free(flat_name);
-+    talloc_free(site);
-+    talloc_free(forest);
-+    talloc_free(val.data);
-+    talloc_free(attrs);
-+}
-+
- int main(int argc, const char *argv[])
- {
-     poptContext pc;
-@@ -845,6 +923,9 @@ int main(int argc, const char *argv[])
-         cmocka_unit_test_setup_teardown(test_ad_get_pac_data_from_user_entry,
-                                         test_ad_common_setup,
-                                         test_ad_common_teardown),
-+        cmocka_unit_test_setup_teardown(test_netlogon_get_domain_info,
-+                                        test_ad_common_setup,
-+                                        test_ad_common_teardown),
-     };
- 
-     /* Set debug level to invalid value so we can deside if -d 0 was used. */
--- 
-2.4.11
-
diff --git a/SOURCES/0041-AD-replace-ad_get_client_site_parse_ndr-with-netlogo.patch b/SOURCES/0041-AD-replace-ad_get_client_site_parse_ndr-with-netlogo.patch
deleted file mode 100644
index 28bfa38..0000000
--- a/SOURCES/0041-AD-replace-ad_get_client_site_parse_ndr-with-netlogo.patch
+++ /dev/null
@@ -1,217 +0,0 @@
-From b0873f4a99f44eabae11e1cc62f6ec49c07466f0 Mon Sep 17 00:00:00 2001
-From: Sumit Bose <sbose@redhat.com>
-Date: Mon, 18 Jul 2016 11:25:47 +0200
-Subject: [PATCH 41/44] AD: replace ad_get_client_site_parse_ndr() with
- netlogon_get_domain_info()
-
-netlogon_get_domain_info() does not fail if only the site is missing in
-the CLDAP ping respond. If the site is not available a Global Catalog
-can still be looked up with the forest name. Only if the forest name is
-missing as well we fall back to the configured domain name.
-
-Resolves:
-https://fedorahosted.org/sssd/ticket/3104
-
-Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
----
- src/providers/ad/ad_srv.c | 153 ++++++++++------------------------------------
- 1 file changed, 33 insertions(+), 120 deletions(-)
-
-diff --git a/src/providers/ad/ad_srv.c b/src/providers/ad/ad_srv.c
-index 9602945e66aa0529938bdd63067f00971dbcbe89..ff01ee95c4d2c6875a989394489f1a0495cc3003 100644
---- a/src/providers/ad/ad_srv.c
-+++ b/src/providers/ad/ad_srv.c
-@@ -405,93 +405,10 @@ done:
-     return;
- }
- 
--static errno_t ad_get_client_site_parse_ndr(TALLOC_CTX *mem_ctx,
--                                            uint8_t *data,
--                                            size_t length,
--                                            char **_site_name,
--                                            char **_forest_name)
--{
--    TALLOC_CTX *tmp_ctx = NULL;
--    struct ndr_pull *ndr_pull = NULL;
--    struct netlogon_samlogon_response response;
--    enum ndr_err_code ndr_err;
--    char *site = NULL;
--    char *forest = NULL;
--    DATA_BLOB blob;
--    errno_t ret;
--
--    tmp_ctx = talloc_new(NULL);
--    if (tmp_ctx == NULL) {
--        DEBUG(SSSDBG_CRIT_FAILURE, "talloc_new() failed\n");
--        return ENOMEM;
--    }
--
--    blob.data = data;
--    blob.length = length;
--
--    ndr_pull = ndr_pull_init_blob(&blob, mem_ctx);
--    if (ndr_pull == NULL) {
--        DEBUG(SSSDBG_OP_FAILURE, "ndr_pull_init_blob() failed.\n");
--        ret = ENOMEM;
--        goto done;
--    }
--
--    ndr_err = ndr_pull_netlogon_samlogon_response(ndr_pull, NDR_SCALARS,
--                                                  &response);
--    if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
--        DEBUG(SSSDBG_OP_FAILURE, "ndr_pull_netlogon_samlogon_response() "
--                                  "failed [%d]\n", ndr_err);
--        ret = EBADMSG;
--        goto done;
--    }
--
--    if (!(response.ntver & NETLOGON_NT_VERSION_5EX)) {
--        DEBUG(SSSDBG_OP_FAILURE, "This NT version does not provide site "
--                                  "information [%x]\n", response.ntver);
--        ret = EBADMSG;
--        goto done;
--    }
--
--    if (response.data.nt5_ex.client_site != NULL
--        && response.data.nt5_ex.client_site[0] != '\0') {
--        site = talloc_strdup(tmp_ctx, response.data.nt5_ex.client_site);
--    } else if (response.data.nt5_ex.next_closest_site != NULL
--               && response.data.nt5_ex.next_closest_site[0] != '\0') {
--        site = talloc_strdup(tmp_ctx, response.data.nt5_ex.next_closest_site);
--    } else {
--        ret = ENOENT;
--        goto done;
--    }
--
--    if (response.data.nt5_ex.forest != NULL
--            && response.data.nt5_ex.forest[0] != '\0') {
--        forest = talloc_strdup(tmp_ctx, response.data.nt5_ex.forest);
--    } else {
--        ret = ENOENT;
--        goto done;
--    }
--
--
--    if (site == NULL || forest == NULL) {
--        ret = ENOMEM;
--        goto done;
--    }
--
--    *_site_name = talloc_steal(mem_ctx, site);
--    *_forest_name = talloc_steal(mem_ctx, forest);
--
--    ret = EOK;
--
--done:
--    talloc_free(tmp_ctx);
--    return ret;
--}
--
- static void ad_get_client_site_done(struct tevent_req *subreq)
- {
-     struct ad_get_client_site_state *state = NULL;
-     struct tevent_req *req = NULL;
--    struct ldb_message_element *el = NULL;
-     struct sysdb_attrs **reply = NULL;
-     size_t reply_count;
-     errno_t ret;
-@@ -520,25 +437,8 @@ static void ad_get_client_site_done(struct tevent_req *subreq)
-         goto done;
-     }
- 
--    ret = sysdb_attrs_get_el(reply[0], AD_AT_NETLOGON, &el);
--    if (ret != EOK) {
--        DEBUG(SSSDBG_OP_FAILURE, "sysdb_attrs_get_el() failed\n");
--        goto done;
--    }
--
--    if (el->num_values == 0) {
--        DEBUG(SSSDBG_OP_FAILURE, "netlogon has no value\n");
--        ret = ENOENT;
--        goto done;
--    } else if (el->num_values > 1) {
--        DEBUG(SSSDBG_OP_FAILURE, "More than one netlogon value?\n");
--        ret = EIO;
--        goto done;
--    }
--
--    ret = ad_get_client_site_parse_ndr(state, el->values[0].data,
--                                       el->values[0].length, &state->site,
--                                       &state->forest);
-+    ret = netlogon_get_domain_info(state, reply[0], true, NULL, &state->site,
-+                                   &state->forest);
-     if (ret != EOK) {
-         DEBUG(SSSDBG_OP_FAILURE, "Unable to retrieve site name [%d]: %s\n",
-                                   ret, strerror(ret));
-@@ -547,6 +447,7 @@ static void ad_get_client_site_done(struct tevent_req *subreq)
-     }
- 
-     DEBUG(SSSDBG_TRACE_FUNC, "Found site: %s\n", state->site);
-+    DEBUG(SSSDBG_TRACE_FUNC, "Found forest: %s\n", state->forest);
- 
- done:
-     if (ret != EOK) {
-@@ -803,30 +704,42 @@ static void ad_srv_plugin_site_done(struct tevent_req *subreq)
- 
-         ret = EOK;
-     }
-+
-+    primary_domain = state->discovery_domain;
-+    backup_domain = NULL;
-+
-     if (ret == EOK) {
-         if (strcmp(state->service, "gc") == 0) {
--            primary_domain = talloc_asprintf(state, AD_SITE_DOMAIN_FMT,
--                                             state->site, state->forest);
--            if (primary_domain == NULL) {
--                ret = ENOMEM;
--                goto done;
--            }
-+            if (state->forest != NULL) {
-+                if (state->site != NULL) {
-+                    primary_domain = talloc_asprintf(state, AD_SITE_DOMAIN_FMT,
-+                                                     state->site,
-+                                                     state->forest);
-+                    if (primary_domain == NULL) {
-+                        ret = ENOMEM;
-+                        goto done;
-+                    }
- 
--            backup_domain = state->forest;
-+                    backup_domain = state->forest;
-+                } else {
-+                    primary_domain = state->forest;
-+                    backup_domain = NULL;
-+                }
-+            }
-         } else {
--            primary_domain = talloc_asprintf(state, AD_SITE_DOMAIN_FMT,
--                                             state->site, state->discovery_domain);
--            if (primary_domain == NULL) {
--                ret = ENOMEM;
--                goto done;
--            }
-+            if (state->site != NULL) {
-+                primary_domain = talloc_asprintf(state, AD_SITE_DOMAIN_FMT,
-+                                                 state->site,
-+                                                 state->discovery_domain);
-+                if (primary_domain == NULL) {
-+                    ret = ENOMEM;
-+                    goto done;
-+                }
- 
--            backup_domain = state->discovery_domain;
-+                backup_domain = state->discovery_domain;
-+            }
-         }
--    } else if (ret == ENOENT) {
--        primary_domain = state->discovery_domain;
--        backup_domain = NULL;
--    } else {
-+    } else if (ret != ENOENT && ret != EOK) {
-         goto done;
-     }
- 
--- 
-2.4.11
-
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
new file mode 100644
index 0000000..f9a0fe3
--- /dev/null
+++ b/SOURCES/0041-SUBDOMAINS-Allow-use_fully_qualified_names-for-subdo.patch
@@ -0,0 +1,531 @@
+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
new file mode 100644
index 0000000..583b606
--- /dev/null
+++ b/SOURCES/0042-CACHE_REQ-Descend-into-subdomains-on-lookups.patch
@@ -0,0 +1,172 @@
+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-sysdb_master_domain_add_info-properly-set-do_update.patch b/SOURCES/0042-sysdb_master_domain_add_info-properly-set-do_update.patch
deleted file mode 100644
index 15f9cbc..0000000
--- a/SOURCES/0042-sysdb_master_domain_add_info-properly-set-do_update.patch
+++ /dev/null
@@ -1,35 +0,0 @@
-From 9899507b2da55a374baef13c4dc3914fe853cf87 Mon Sep 17 00:00:00 2001
-From: Sumit Bose <sbose@redhat.com>
-Date: Mon, 25 Jul 2016 17:37:51 +0200
-Subject: [PATCH 42/44] sysdb_master_domain_add_info: properly set do_update
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-do_update should be only set if there is a change, i.e if something was
-added to the ldb_message.
-
-Reviewed-by: Lukáš Slebodník <lslebodn@redhat.com>
----
- src/db/sysdb_subdomains.c | 4 ++--
- 1 file changed, 2 insertions(+), 2 deletions(-)
-
-diff --git a/src/db/sysdb_subdomains.c b/src/db/sysdb_subdomains.c
-index 02206e470e8e035cc05848137df6a1eb04806869..ff83f914f31d566e050c74a3ef5f5745f8c93add 100644
---- a/src/db/sysdb_subdomains.c
-+++ b/src/db/sysdb_subdomains.c
-@@ -787,9 +787,9 @@ errno_t sysdb_master_domain_add_info(struct sss_domain_info *domain,
-                 ret = sysdb_error_to_errno(ret);
-                 goto done;
-             }
--        }
- 
--        do_update = true;
-+            do_update = true;
-+        }
-     }
- 
-     if (do_update == false) {
--- 
-2.4.11
-
diff --git a/SOURCES/0043-NSS-TESTS-Fix-subdomains-attribution.patch b/SOURCES/0043-NSS-TESTS-Fix-subdomains-attribution.patch
new file mode 100644
index 0000000..e142203
--- /dev/null
+++ b/SOURCES/0043-NSS-TESTS-Fix-subdomains-attribution.patch
@@ -0,0 +1,35 @@
+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-SYSDB-Removing-of-duplication-of-sysdb_ts_cache_attr.patch b/SOURCES/0043-SYSDB-Removing-of-duplication-of-sysdb_ts_cache_attr.patch
deleted file mode 100644
index 20883fd..0000000
--- a/SOURCES/0043-SYSDB-Removing-of-duplication-of-sysdb_ts_cache_attr.patch
+++ /dev/null
@@ -1,30 +0,0 @@
-From bfbafa51da5e407659c924eed5970f17510ae913 Mon Sep 17 00:00:00 2001
-From: Petr Cech <pcech@redhat.com>
-Date: Tue, 19 Jul 2016 14:28:35 +0200
-Subject: [PATCH 43/44] SYSDB: Removing of duplication of sysdb_ts_cache_attrs
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Reviewed-by: Fabiano Fidêncio <fabiano@fidencio.org>
----
- src/db/sysdb.h | 3 ---
- 1 file changed, 3 deletions(-)
-
-diff --git a/src/db/sysdb.h b/src/db/sysdb.h
-index 407ce3c18a7077e8fe45c3c9c7576ae626105122..0cc550a4c389b4a1a2b78aff760f4b5cbf94e17f 100644
---- a/src/db/sysdb.h
-+++ b/src/db/sysdb.h
-@@ -315,9 +315,6 @@ struct range_info {
- /* These attributes are stored in the timestamp cache */
- extern const char *sysdb_ts_cache_attrs[];
- 
--/* These attributes are stored in the timestamp cache */
--extern const char *sysdb_ts_cache_attrs[];
--
- /* values are copied in the structure, allocated on "attrs" */
- int sysdb_attrs_add_val(struct sysdb_attrs *attrs,
-                         const char *name, const struct ldb_val *val);
--- 
-2.4.11
-
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
new file mode 100644
index 0000000..9eb42a4
--- /dev/null
+++ b/SOURCES/0044-NSS-TESTS-Improve-setup-teardown-for-subdomains-test.patch
@@ -0,0 +1,274 @@
+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-test_utils-Fixing-assignment-discards-const-qualifie.patch b/SOURCES/0044-test_utils-Fixing-assignment-discards-const-qualifie.patch
deleted file mode 100644
index 053dc43..0000000
--- a/SOURCES/0044-test_utils-Fixing-assignment-discards-const-qualifie.patch
+++ /dev/null
@@ -1,37 +0,0 @@
-From 2af91b3728470b9831a748d51d081b117648ae91 Mon Sep 17 00:00:00 2001
-From: Petr Cech <pcech@redhat.com>
-Date: Tue, 26 Jul 2016 10:37:19 +0200
-Subject: [PATCH 44/44] test_utils: Fixing assignment discards 'const'
- qualifier
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Reviewed-by: Lukáš Slebodník <lslebodn@redhat.com>
----
- src/tests/cmocka/test_utils.c | 4 ++--
- 1 file changed, 2 insertions(+), 2 deletions(-)
-
-diff --git a/src/tests/cmocka/test_utils.c b/src/tests/cmocka/test_utils.c
-index 4c72a59437105e683cec2d39c36951aeff63767b..5349accc5c6b55f7d725bfdeaf318b90289880dd 100644
---- a/src/tests/cmocka/test_utils.c
-+++ b/src/tests/cmocka/test_utils.c
-@@ -1806,13 +1806,13 @@ static void test_sss_get_domain_mappings_content(void **state)
-     assert_non_null(c);
-     c->forest_root = find_domain_by_name(dom, "subdom1.dom", true);
-     assert_non_null(c->forest_root);
--    c->forest = "subdom1.dom";
-+    c->forest = discard_const_p(char, "subdom1.dom");
- 
-     c = find_domain_by_name(dom, "subdom3.dom", true);
-     assert_non_null(c);
-     c->forest_root = find_domain_by_name(dom, "subdom1.dom", true);
-     assert_non_null(c->forest_root);
--    c->forest = "subdom1.dom";
-+    c->forest = discard_const_p(char, "subdom1.dom");
- 
-     ret = sss_get_domain_mappings_content(test_ctx, dom, &content);
-     assert_int_equal(ret, EOK);
--- 
-2.4.11
-
diff --git a/SOURCES/0045-IPA-make-ipa_resolve_user_list_-send-recv-public-and.patch b/SOURCES/0045-IPA-make-ipa_resolve_user_list_-send-recv-public-and.patch
deleted file mode 100644
index bd9f1d8..0000000
--- a/SOURCES/0045-IPA-make-ipa_resolve_user_list_-send-recv-public-and.patch
+++ /dev/null
@@ -1,85 +0,0 @@
-From 9812095cc83c7258584235528d355c301c492b92 Mon Sep 17 00:00:00 2001
-From: Sumit Bose <sbose@redhat.com>
-Date: Tue, 12 Jul 2016 17:09:00 +0200
-Subject: [PATCH 45/62] IPA: make ipa_resolve_user_list_{send|recv} public and
- allow AD users
-
-Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
-(cherry picked from commit f2e8a7c3230fac11175c0bd17c14c66a8e9b25ad)
----
- src/providers/ipa/ipa_id.c | 20 ++++++++++++++++----
- src/providers/ipa/ipa_id.h |  8 ++++++++
- 2 files changed, 24 insertions(+), 4 deletions(-)
-
-diff --git a/src/providers/ipa/ipa_id.c b/src/providers/ipa/ipa_id.c
-index e092cd2f80d3c102f88a9bc48a352e1a140b14e8..7cc79f92b5dcd481706353d403125c46d587e7f6 100644
---- a/src/providers/ipa/ipa_id.c
-+++ b/src/providers/ipa/ipa_id.c
-@@ -71,7 +71,7 @@ struct ipa_resolve_user_list_state {
- static errno_t ipa_resolve_user_list_get_user_step(struct tevent_req *req);
- static void ipa_resolve_user_list_get_user_done(struct tevent_req *subreq);
- 
--static struct tevent_req *
-+struct tevent_req *
- ipa_resolve_user_list_send(TALLOC_CTX *memctx, struct tevent_context *ev,
-                            struct ipa_id_ctx *ipa_ctx,
-                            const char *domain_name,
-@@ -132,7 +132,14 @@ 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);
- 
--    subreq = ipa_id_get_account_info_send(state, state->ev, state->ipa_ctx, ar);
-+    if (strcasecmp(state->domain_name,
-+                   state->ipa_ctx->sdap_id_ctx->be->domain->name) != 0) {
-+        subreq = ipa_subdomain_account_send(state, state->ev, state->ipa_ctx,
-+                                            ar);
-+    } else {
-+        subreq = ipa_id_get_account_info_send(state, state->ev, state->ipa_ctx,
-+                                              ar);
-+    }
-     if (subreq == NULL) {
-         DEBUG(SSSDBG_OP_FAILURE, "sdap_handle_acct_req_send failed.\n");
-         return ENOMEM;
-@@ -151,7 +158,12 @@ static void ipa_resolve_user_list_get_user_done(struct tevent_req *subreq)
-                                             struct ipa_resolve_user_list_state);
-     int ret;
- 
--    ret = ipa_id_get_account_info_recv(subreq, &state->dp_error);
-+    if (strcasecmp(state->domain_name,
-+                   state->ipa_ctx->sdap_id_ctx->be->domain->name) != 0) {
-+        ret = ipa_subdomain_account_recv(subreq, &state->dp_error);
-+    } else {
-+        ret = ipa_id_get_account_info_recv(subreq, &state->dp_error);
-+    }
-     talloc_zfree(subreq);
-     if (ret != EOK) {
-         DEBUG(SSSDBG_OP_FAILURE, "sdap_handle_acct request failed: %d\n", ret);
-@@ -182,7 +194,7 @@ done:
-     return;
- }
- 
--static int ipa_resolve_user_list_recv(struct tevent_req *req, int *dp_error)
-+int ipa_resolve_user_list_recv(struct tevent_req *req, int *dp_error)
- {
-     struct ipa_resolve_user_list_state *state = tevent_req_data(req,
-                                             struct ipa_resolve_user_list_state);
-diff --git a/src/providers/ipa/ipa_id.h b/src/providers/ipa/ipa_id.h
-index 17db5226a29a54485f7b26eb9853ef2380426bdf..485fa3f8e6af48ddcf1178c3486456753035c948 100644
---- a/src/providers/ipa/ipa_id.h
-+++ b/src/providers/ipa/ipa_id.h
-@@ -135,4 +135,12 @@ struct tevent_req *ipa_get_subdom_acct_process_pac_send(TALLOC_CTX *mem_ctx,
-                                                    struct ldb_message *user_msg);
- 
- errno_t ipa_get_subdom_acct_process_pac_recv(struct tevent_req *req);
-+
-+struct tevent_req *
-+ipa_resolve_user_list_send(TALLOC_CTX *memctx, struct tevent_context *ev,
-+                           struct ipa_id_ctx *ipa_ctx,
-+                           const char *domain_name,
-+                           struct ldb_message_element *users);
-+int ipa_resolve_user_list_recv(struct tevent_req *req, int *dp_error);
-+
- #endif
--- 
-2.4.11
-
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
new file mode 100644
index 0000000..08acdce
--- /dev/null
+++ b/SOURCES/0045-NSS-TESTS-Include-searches-for-non-fqnames-members-o.patch
@@ -0,0 +1,355 @@
+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/0046-IPA-expand-ghost-members-of-AD-groups-in-server-mode.patch b/SOURCES/0046-IPA-expand-ghost-members-of-AD-groups-in-server-mode.patch
deleted file mode 100644
index 9b41801..0000000
--- a/SOURCES/0046-IPA-expand-ghost-members-of-AD-groups-in-server-mode.patch
+++ /dev/null
@@ -1,115 +0,0 @@
-From c17e7bc80adf9741054e53dc6e8d8f6afa273e18 Mon Sep 17 00:00:00 2001
-From: Sumit Bose <sbose@redhat.com>
-Date: Tue, 12 Jul 2016 17:09:40 +0200
-Subject: [PATCH 46/62] IPA: expand ghost members of AD groups in server-mode
-
-Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
-(cherry picked from commit 160ba891ec483c5b7d2a3fcca5bd992fc790efe0)
----
- src/providers/ipa/ipa_subdomains_id.c | 79 ++++++++++++++++++++++++++++++++++-
- 1 file changed, 78 insertions(+), 1 deletion(-)
-
-diff --git a/src/providers/ipa/ipa_subdomains_id.c b/src/providers/ipa/ipa_subdomains_id.c
-index 65bfd33765d5799037075e599049761f18466bb2..542c596a983bcb48f4eac699f78eb956326cefa2 100644
---- a/src/providers/ipa/ipa_subdomains_id.c
-+++ b/src/providers/ipa/ipa_subdomains_id.c
-@@ -1201,6 +1201,67 @@ fail:
-     return;
- }
- 
-+static void ipa_check_ghost_members_done(struct tevent_req *subreq);
-+static errno_t ipa_check_ghost_members(struct tevent_req *req)
-+{
-+    struct ipa_get_ad_acct_state *state = tevent_req_data(req,
-+                                                struct ipa_get_ad_acct_state);
-+    errno_t ret;
-+    struct tevent_req *subreq;
-+    struct ldb_message_element *ghosts = NULL;
-+
-+
-+    if (state->obj_msg == NULL) {
-+        ret = get_object_from_cache(state, state->obj_dom, state->ar,
-+                                    &state->obj_msg);
-+        if (ret == ENOENT) {
-+            DEBUG(SSSDBG_MINOR_FAILURE,
-+                  "Object not found, ending request\n");
-+            return EOK;
-+        } else if (ret != EOK) {
-+            DEBUG(SSSDBG_OP_FAILURE, "get_object_from_cache failed.\n");
-+            return ret;
-+        }
-+    }
-+
-+    ghosts = ldb_msg_find_element(state->obj_msg, SYSDB_GHOST);
-+
-+    if (ghosts != NULL) {
-+        /* Resolve ghost members */
-+        subreq = ipa_resolve_user_list_send(state, state->ev,
-+                                            state->ipa_ctx,
-+                                            state->obj_dom->name,
-+                                            ghosts);
-+        if (subreq == NULL) {
-+            DEBUG(SSSDBG_OP_FAILURE, "ipa_resolve_user_list_send failed.\n");
-+            return ENOMEM;
-+        }
-+        tevent_req_set_callback(subreq, ipa_check_ghost_members_done, req);
-+        return EAGAIN;
-+    }
-+
-+    return EOK;
-+}
-+
-+static void ipa_check_ghost_members_done(struct tevent_req *subreq)
-+{
-+    struct tevent_req *req = tevent_req_callback_data(subreq,
-+                                                struct tevent_req);
-+    int ret;
-+
-+    ret = ipa_resolve_user_list_recv(subreq, NULL);
-+    talloc_zfree(subreq);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_OP_FAILURE, "ipa_resolve_user_list request failed [%d]\n",
-+                                  ret);
-+        tevent_req_error(req, ret);
-+        return;
-+    }
-+
-+    tevent_req_done(req);
-+    return;
-+}
-+
- static errno_t ipa_get_ad_apply_override_step(struct tevent_req *req)
- {
-     struct ipa_get_ad_acct_state *state = tevent_req_data(req,
-@@ -1228,11 +1289,27 @@ static errno_t ipa_get_ad_apply_override_step(struct tevent_req *req)
-     entry_type = (state->ar->entry_type & BE_REQ_TYPE_MASK);
-     if (entry_type != BE_REQ_INITGROUPS
-             && entry_type != BE_REQ_USER
--            && entry_type != BE_REQ_BY_SECID) {
-+            && entry_type != BE_REQ_BY_SECID
-+            && entry_type != BE_REQ_GROUP) {
-         tevent_req_done(req);
-         return EOK;
-     }
- 
-+    /* expand ghost members, if any, to get group members with overrides
-+     * right. */
-+    if (entry_type == BE_REQ_GROUP) {
-+        ret = ipa_check_ghost_members(req);
-+        if (ret == EOK) {
-+            tevent_req_done(req);
-+            return EOK;
-+        } else if (ret == EAGAIN) {
-+            return EOK;
-+        } else {
-+            DEBUG(SSSDBG_OP_FAILURE, "ipa_check_ghost_members failed.\n");
-+            return ret;
-+        }
-+    }
-+
-     /* Replace ID with name in search filter */
-     if ((entry_type == BE_REQ_USER && state->ar->filter_type == BE_FILTER_IDNUM)
-             || (entry_type == BE_REQ_INITGROUPS
--- 
-2.4.11
-
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
new file mode 100644
index 0000000..b2a6305
--- /dev/null
+++ b/SOURCES/0046-SYSDB-Add-methods-to-deal-with-the-domain-s-resoluti.patch
@@ -0,0 +1,289 @@
+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/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
new file mode 100644
index 0000000..459a50f
--- /dev/null
+++ b/SOURCES/0047-SYSDB-TESTS-Add-tests-for-the-domain-s-resolution-or.patch
@@ -0,0 +1,259 @@
+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/0047-sysdb-add-sysdb_get_user_members_recursively.patch b/SOURCES/0047-sysdb-add-sysdb_get_user_members_recursively.patch
deleted file mode 100644
index 037cdd0..0000000
--- a/SOURCES/0047-sysdb-add-sysdb_get_user_members_recursively.patch
+++ /dev/null
@@ -1,103 +0,0 @@
-From a1e606d051c54dd603bf09adb2bd6d0d7db2663f Mon Sep 17 00:00:00 2001
-From: Sumit Bose <sbose@redhat.com>
-Date: Wed, 20 Jul 2016 18:42:27 +0200
-Subject: [PATCH 47/62] sysdb: add sysdb_get_user_members_recursively()
-
-Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
-(cherry picked from commit 17bfd9f69251781140e4b2b55ffeb649d7a79e86)
----
- src/db/sysdb.h     |  5 +++++
- src/db/sysdb_ops.c | 61 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
- 2 files changed, 66 insertions(+)
-
-diff --git a/src/db/sysdb.h b/src/db/sysdb.h
-index 0cc550a4c389b4a1a2b78aff760f4b5cbf94e17f..405f89e2f1ac6fabc06e77c345de8693845f9d92 100644
---- a/src/db/sysdb.h
-+++ b/src/db/sysdb.h
-@@ -1257,6 +1257,11 @@ errno_t sysdb_get_sids_of_members(TALLOC_CTX *mem_ctx,
-                                   const char ***_dns,
-                                   size_t *_n);
- 
-+errno_t sysdb_get_user_members_recursively(TALLOC_CTX *mem_ctx,
-+                                           struct sss_domain_info *dom,
-+                                           struct ldb_dn *group_dn,
-+                                           struct ldb_result **members);
-+
- errno_t sysdb_handle_original_uuid(const char *orig_name,
-                                    struct sysdb_attrs *src_attrs,
-                                    const char *src_name,
-diff --git a/src/db/sysdb_ops.c b/src/db/sysdb_ops.c
-index 19d6be03ede1bcec3bc7a4ed777e326460d80591..9a8a55ed8aa69e1638d0ab6f636e43baa3d0bfea 100644
---- a/src/db/sysdb_ops.c
-+++ b/src/db/sysdb_ops.c
-@@ -4711,6 +4711,67 @@ done:
-     return ret;
- }
- 
-+errno_t sysdb_get_user_members_recursively(TALLOC_CTX *mem_ctx,
-+                                           struct sss_domain_info *dom,
-+                                           struct ldb_dn *group_dn,
-+                                           struct ldb_result **members)
-+{
-+    TALLOC_CTX *tmp_ctx;
-+    int ret;
-+    size_t count;
-+    struct ldb_result *res;
-+    struct ldb_dn *base_dn;
-+    char *filter;
-+    const char *attrs[] = SYSDB_PW_ATTRS;
-+    struct ldb_message **msgs;
-+
-+    tmp_ctx = talloc_new(NULL);
-+    if (tmp_ctx == NULL) {
-+        return ENOMEM;
-+    }
-+
-+    base_dn = sysdb_base_dn(dom->sysdb, tmp_ctx);
-+    if (base_dn == NULL) {
-+        DEBUG(SSSDBG_OP_FAILURE, "sysdb_base_dn failed.\n");
-+        ret = ENOMEM;
-+        goto done;
-+    }
-+
-+    filter = talloc_asprintf(tmp_ctx, "(&("SYSDB_UC")("SYSDB_MEMBEROF"=%s))",
-+                             ldb_dn_get_linearized(group_dn));
-+    if (filter == NULL) {
-+        DEBUG(SSSDBG_OP_FAILURE, "talloc_asprintf failed.\n");
-+        ret = ENOMEM;
-+        goto done;
-+    }
-+
-+    ret = sysdb_search_entry(tmp_ctx, dom->sysdb, base_dn, LDB_SCOPE_SUBTREE,
-+                             filter, attrs, &count, &msgs);
-+
-+    res = talloc_zero(tmp_ctx, struct ldb_result);
-+    if (res == NULL) {
-+        DEBUG(SSSDBG_OP_FAILURE, "talloc_zero failed.\n");
-+        ret = ENOMEM;
-+        goto done;
-+    }
-+
-+    res->count = count;
-+    res->msgs = talloc_steal(res, msgs);
-+
-+    ret = EOK;
-+
-+done:
-+    if (ret == EOK) {
-+        *members = talloc_steal(mem_ctx, res);
-+    } else if (ret == ENOENT) {
-+        DEBUG(SSSDBG_TRACE_FUNC, "No such entry\n");
-+    } else {
-+        DEBUG(SSSDBG_OP_FAILURE, "Error: %d (%s)\n", ret, strerror(ret));
-+    }
-+    talloc_free(tmp_ctx);
-+    return ret;
-+}
-+
- errno_t sysdb_handle_original_uuid(const char *orig_name,
-                                    struct sysdb_attrs *src_attrs,
-                                    const char *src_name,
--- 
-2.4.11
-
diff --git a/SOURCES/0048-IPA-Get-ipaDomainsResolutionOrder-from-ipaConfig.patch b/SOURCES/0048-IPA-Get-ipaDomainsResolutionOrder-from-ipaConfig.patch
new file mode 100644
index 0000000..3dcbc82
--- /dev/null
+++ b/SOURCES/0048-IPA-Get-ipaDomainsResolutionOrder-from-ipaConfig.patch
@@ -0,0 +1,369 @@
+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-views-properly-override-group-member-names.patch b/SOURCES/0048-views-properly-override-group-member-names.patch
deleted file mode 100644
index c86debf..0000000
--- a/SOURCES/0048-views-properly-override-group-member-names.patch
+++ /dev/null
@@ -1,455 +0,0 @@
-From 11f6fcedb0ac04528dd319fcf95d1fbaa4ea8bd1 Mon Sep 17 00:00:00 2001
-From: Sumit Bose <sbose@redhat.com>
-Date: Thu, 7 Jul 2016 18:54:02 +0200
-Subject: [PATCH 48/62] views: properly override group member names
-
-Resolves https://fedorahosted.org/sssd/ticket/2948
-
-Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
-(cherry picked from commit 1594701fbdc341069e11cff9a85e7a795e52db3d)
----
- src/db/sysdb.h                  |   3 +-
- src/db/sysdb_search.c           |  99 ++++++++++++++++-------------
- src/db/sysdb_views.c            | 136 ++++++++++++++++++----------------------
- src/responder/nss/nsssrv_cmd.c  |   7 ++-
- src/tests/cmocka/test_nss_srv.c |  18 +++---
- 5 files changed, 134 insertions(+), 129 deletions(-)
-
-diff --git a/src/db/sysdb.h b/src/db/sysdb.h
-index 405f89e2f1ac6fabc06e77c345de8693845f9d92..a27552224bb40bd07c7dee4dfe35bfb7a0b4f2c3 100644
---- a/src/db/sysdb.h
-+++ b/src/db/sysdb.h
-@@ -572,7 +572,8 @@ errno_t sysdb_add_overrides_to_object(struct sss_domain_info *domain,
-                                       const char **req_attrs);
- 
- errno_t sysdb_add_group_member_overrides(struct sss_domain_info *domain,
--                                         struct ldb_message *obj);
-+                                         struct ldb_message *obj,
-+                                         bool expect_override_dn);
- 
- errno_t sysdb_getpwnam_with_views(TALLOC_CTX *mem_ctx,
-                                   struct sss_domain_info *domain,
-diff --git a/src/db/sysdb_search.c b/src/db/sysdb_search.c
-index e40b36c38e28992e185447497d1bf69cabc09821..cfee5784dbadd692f30d0758e7e5c3c9fb2814cb 100644
---- a/src/db/sysdb_search.c
-+++ b/src/db/sysdb_search.c
-@@ -771,28 +771,33 @@ int sysdb_getgrnam_with_views(TALLOC_CTX *mem_ctx,
- 
-     /* If there are views we have to check if override values must be added to
-      * the original object. */
--    if (DOM_HAS_VIEWS(domain) && orig_obj->count == 1) {
--        if (!is_local_view(domain->view_name)) {
--            el = ldb_msg_find_element(orig_obj->msgs[0], SYSDB_GHOST);
--            if (el != NULL && el->num_values != 0) {
--                DEBUG(SSSDBG_TRACE_ALL, "Group object [%s], contains ghost "
--                      "entries which must be resolved before overrides can be "
--                      "applied.\n",
--                      ldb_dn_get_linearized(orig_obj->msgs[0]->dn));
--                ret = ENOENT;
-+    if (orig_obj->count == 1) {
-+        if (DOM_HAS_VIEWS(domain)) {
-+            if (!is_local_view(domain->view_name)) {
-+                el = ldb_msg_find_element(orig_obj->msgs[0], SYSDB_GHOST);
-+                if (el != NULL && el->num_values != 0) {
-+                    DEBUG(SSSDBG_TRACE_ALL, "Group object [%s], contains ghost "
-+                          "entries which must be resolved before overrides can be "
-+                          "applied.\n",
-+                          ldb_dn_get_linearized(orig_obj->msgs[0]->dn));
-+                    ret = ENOENT;
-+                    goto done;
-+                }
-+            }
-+
-+            ret = sysdb_add_overrides_to_object(domain, orig_obj->msgs[0],
-+                           override_obj == NULL ? NULL : override_obj ->msgs[0],
-+                           NULL);
-+            if (ret != EOK) {
-+                DEBUG(SSSDBG_OP_FAILURE, "sysdb_add_overrides_to_object failed.\n");
-                 goto done;
-             }
-         }
- 
--        ret = sysdb_add_overrides_to_object(domain, orig_obj->msgs[0],
--                          override_obj == NULL ? NULL : override_obj ->msgs[0],
--                          NULL);
--        if (ret != EOK) {
--            DEBUG(SSSDBG_OP_FAILURE, "sysdb_add_overrides_to_object failed.\n");
--            goto done;
--        }
--
--        ret = sysdb_add_group_member_overrides(domain, orig_obj->msgs[0]);
-+        /* Must be called even without views to check to
-+         * SYSDB_DEFAULT_OVERRIDE_NAME */
-+        ret = sysdb_add_group_member_overrides(domain, orig_obj->msgs[0],
-+                                               DOM_HAS_VIEWS(domain));
-         if (ret != EOK) {
-             DEBUG(SSSDBG_OP_FAILURE,
-                   "sysdb_add_group_member_overrides failed.\n");
-@@ -922,28 +927,33 @@ int sysdb_getgrgid_with_views(TALLOC_CTX *mem_ctx,
- 
-     /* If there are views we have to check if override values must be added to
-      * the original object. */
--    if (DOM_HAS_VIEWS(domain) && orig_obj->count == 1) {
--        if (!is_local_view(domain->view_name)) {
--            el = ldb_msg_find_element(orig_obj->msgs[0], SYSDB_GHOST);
--            if (el != NULL && el->num_values != 0) {
--                DEBUG(SSSDBG_TRACE_ALL, "Group object [%s], contains ghost "
--                      "entries which must be resolved before overrides can be "
--                      "applied.\n",
--                      ldb_dn_get_linearized(orig_obj->msgs[0]->dn));
--                ret = ENOENT;
-+    if (orig_obj->count == 1) {
-+        if (DOM_HAS_VIEWS(domain)) {
-+            if (!is_local_view(domain->view_name)) {
-+                el = ldb_msg_find_element(orig_obj->msgs[0], SYSDB_GHOST);
-+                if (el != NULL && el->num_values != 0) {
-+                    DEBUG(SSSDBG_TRACE_ALL, "Group object [%s], contains ghost "
-+                          "entries which must be resolved before overrides can be "
-+                          "applied.\n",
-+                          ldb_dn_get_linearized(orig_obj->msgs[0]->dn));
-+                    ret = ENOENT;
-+                    goto done;
-+                }
-+            }
-+
-+            ret = sysdb_add_overrides_to_object(domain, orig_obj->msgs[0],
-+                              override_obj == NULL ? NULL : override_obj ->msgs[0],
-+                              NULL);
-+            if (ret != EOK) {
-+                DEBUG(SSSDBG_OP_FAILURE, "sysdb_add_overrides_to_object failed.\n");
-                 goto done;
-             }
-         }
- 
--        ret = sysdb_add_overrides_to_object(domain, orig_obj->msgs[0],
--                          override_obj == NULL ? NULL : override_obj ->msgs[0],
--                          NULL);
--        if (ret != EOK) {
--            DEBUG(SSSDBG_OP_FAILURE, "sysdb_add_overrides_to_object failed.\n");
--            goto done;
--        }
--
--        ret = sysdb_add_group_member_overrides(domain, orig_obj->msgs[0]);
-+        /* Must be called even without views to check to
-+         * SYSDB_DEFAULT_OVERRIDE_NAME */
-+        ret = sysdb_add_group_member_overrides(domain, orig_obj->msgs[0],
-+                                               DOM_HAS_VIEWS(domain));
-         if (ret != EOK) {
-             DEBUG(SSSDBG_OP_FAILURE,
-                   "sysdb_add_group_member_overrides failed.\n");
-@@ -1157,8 +1167,8 @@ int sysdb_enumgrent_filter_with_views(TALLOC_CTX *mem_ctx,
-         goto done;
-     }
- 
--    if (DOM_HAS_VIEWS(domain)) {
--        for (c = 0; c < res->count; c++) {
-+    for (c = 0; c < res->count; c++) {
-+        if (DOM_HAS_VIEWS(domain)) {
-             ret = sysdb_add_overrides_to_object(domain, res->msgs[c], NULL,
-                                                 NULL);
-             /* enumeration assumes that the cache is up-to-date, hence we do not
-@@ -1167,13 +1177,14 @@ int sysdb_enumgrent_filter_with_views(TALLOC_CTX *mem_ctx,
-                 DEBUG(SSSDBG_OP_FAILURE, "sysdb_add_overrides_to_object failed.\n");
-                 goto done;
-             }
-+        }
- 
--            ret = sysdb_add_group_member_overrides(domain, res->msgs[c]);
--            if (ret != EOK) {
--                DEBUG(SSSDBG_OP_FAILURE,
--                      "sysdb_add_group_member_overrides failed.\n");
--                goto done;
--            }
-+        ret = sysdb_add_group_member_overrides(domain, res->msgs[c],
-+                                               DOM_HAS_VIEWS(domain));
-+        if (ret != EOK) {
-+            DEBUG(SSSDBG_OP_FAILURE,
-+                  "sysdb_add_group_member_overrides failed.\n");
-+            goto done;
-         }
-     }
- 
-diff --git a/src/db/sysdb_views.c b/src/db/sysdb_views.c
-index 2b89e5ca41f719e1217ef3b9e0fd683656e05d42..79f513d13ba41212a6cd84e1d9e609df6acba29c 100644
---- a/src/db/sysdb_views.c
-+++ b/src/db/sysdb_views.c
-@@ -1348,14 +1348,13 @@ done:
- }
- 
- errno_t sysdb_add_group_member_overrides(struct sss_domain_info *domain,
--                                         struct ldb_message *obj)
-+                                         struct ldb_message *obj,
-+                                         bool expect_override_dn)
- {
-     int ret;
-     size_t c;
--    struct ldb_message_element *members;
-+    struct ldb_result *res_members;
-     TALLOC_CTX *tmp_ctx;
--    struct ldb_dn *member_dn;
--    struct ldb_result *member_obj;
-     struct ldb_result *override_obj;
-     static const char *member_attrs[] = SYSDB_PW_ATTRS;
-     const char *override_dn_str;
-@@ -1366,12 +1365,6 @@ errno_t sysdb_add_group_member_overrides(struct sss_domain_info *domain,
-     char *val;
-     struct sss_domain_info *orig_dom;
- 
--    members = ldb_msg_find_element(obj, SYSDB_MEMBER);
--    if (members == NULL || members->num_values == 0) {
--        DEBUG(SSSDBG_TRACE_ALL, "Group has no members.\n");
--        return EOK;
--    }
--
-     tmp_ctx = talloc_new(NULL);
-     if (tmp_ctx == NULL) {
-         DEBUG(SSSDBG_OP_FAILURE, "talloc_new failed.\n");
-@@ -1379,38 +1372,30 @@ errno_t sysdb_add_group_member_overrides(struct sss_domain_info *domain,
-         goto done;
-     }
- 
--    for (c = 0; c < members->num_values; c++) {
--        member_dn = ldb_dn_from_ldb_val(tmp_ctx, domain->sysdb->ldb,
--                                        &members->values[c]);
--        if (member_dn == NULL) {
--            DEBUG(SSSDBG_OP_FAILURE, "ldb_dn_from_ldb_val failed.\n");
--            ret = ENOMEM;
--            goto done;
--        }
-+    ret = sysdb_get_user_members_recursively(tmp_ctx, domain, obj->dn,
-+                                             &res_members);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_OP_FAILURE,
-+              "sysdb_get_user_members_recursively failed.\n");
-+        goto done;
-+    }
- 
--        ret = ldb_search(domain->sysdb->ldb, member_dn, &member_obj, member_dn,
--                         LDB_SCOPE_BASE, member_attrs, NULL);
--        if (ret != LDB_SUCCESS) {
--            ret = sysdb_error_to_errno(ret);
--            goto done;
--        }
-+    for (c = 0; c < res_members->count; c++) {
- 
--        if (member_obj->count != 1) {
--            DEBUG(SSSDBG_CRIT_FAILURE,
--                  "Base search for member object returned [%d] results.\n",
--                  member_obj->count);
--            ret = EINVAL;
--            goto done;
--        }
--
--        if (ldb_msg_find_attr_as_uint64(member_obj->msgs[0],
-+        if (ldb_msg_find_attr_as_uint64(res_members->msgs[c],
-                                         SYSDB_UIDNUM, 0) == 0) {
-             /* Skip non-POSIX-user members i.e. groups and non-POSIX users */
-             continue;
-         }
- 
--        override_dn_str = ldb_msg_find_attr_as_string(member_obj->msgs[0],
--                                                      SYSDB_OVERRIDE_DN, NULL);
-+        if (expect_override_dn) {
-+            override_dn_str = ldb_msg_find_attr_as_string(res_members->msgs[c],
-+                                                          SYSDB_OVERRIDE_DN,
-+                                                          NULL);
-+        } else {
-+            override_dn_str = ldb_dn_get_linearized(res_members->msgs[c]->dn);
-+        }
-+
-         if (override_dn_str == NULL) {
-             if (is_local_view(domain->view_name)) {
-                 /* LOCAL view doesn't have to have overrideDN specified. */
-@@ -1420,12 +1405,12 @@ errno_t sysdb_add_group_member_overrides(struct sss_domain_info *domain,
- 
-             DEBUG(SSSDBG_CRIT_FAILURE,
-                   "Missing override DN for object [%s].\n",
--                  ldb_dn_get_linearized(member_obj->msgs[0]->dn));
-+                  ldb_dn_get_linearized(res_members->msgs[c]->dn));
-             ret = ENOENT;
-             goto done;
-         }
- 
--        override_dn = ldb_dn_new(member_obj, domain->sysdb->ldb,
-+        override_dn = ldb_dn_new(res_members, domain->sysdb->ldb,
-                                  override_dn_str);
-         if (override_dn == NULL) {
-             DEBUG(SSSDBG_OP_FAILURE, "ldb_dn_new failed.\n");
-@@ -1433,22 +1418,27 @@ errno_t sysdb_add_group_member_overrides(struct sss_domain_info *domain,
-             goto done;
-         }
- 
--        orig_name = ldb_msg_find_attr_as_string(member_obj->msgs[0],
-+        orig_name = ldb_msg_find_attr_as_string(res_members->msgs[c],
-                                                 SYSDB_NAME,
-                                                 NULL);
-         if (orig_name == NULL) {
-             DEBUG(SSSDBG_CRIT_FAILURE, "Object [%s] has no name.\n",
--                  ldb_dn_get_linearized(member_obj->msgs[0]->dn));
-+                  ldb_dn_get_linearized(res_members->msgs[c]->dn));
-             ret = EINVAL;
-             goto done;
-         }
- 
--        memberuid = NULL;
--        if (ldb_dn_compare(member_obj->msgs[0]->dn, override_dn) != 0) {
-+        /* start with default view name, if it exists or use NULL */
-+        memberuid = ldb_msg_find_attr_as_string(res_members->msgs[c],
-+                                                SYSDB_DEFAULT_OVERRIDE_NAME,
-+                                                NULL);
-+
-+        /* If there is an override object, check if the name is overridden */
-+        if (ldb_dn_compare(res_members->msgs[c]->dn, override_dn) != 0) {
-             DEBUG(SSSDBG_TRACE_ALL, "Checking override for object [%s].\n",
--                  ldb_dn_get_linearized(member_obj->msgs[0]->dn));
-+                  ldb_dn_get_linearized(res_members->msgs[c]->dn));
- 
--            ret = ldb_search(domain->sysdb->ldb, member_obj, &override_obj,
-+            ret = ldb_search(domain->sysdb->ldb, res_members, &override_obj,
-                              override_dn, LDB_SCOPE_BASE, member_attrs, NULL);
-             if (ret != LDB_SUCCESS) {
-                 ret = sysdb_error_to_errno(ret);
-@@ -1458,43 +1448,44 @@ errno_t sysdb_add_group_member_overrides(struct sss_domain_info *domain,
-             if (override_obj->count != 1) {
-                 DEBUG(SSSDBG_CRIT_FAILURE,
-                      "Base search for override object returned [%d] results.\n",
--                     member_obj->count);
-+                    override_obj->count);
-                 ret = EINVAL;
-                 goto done;
-             }
- 
-             memberuid = ldb_msg_find_attr_as_string(override_obj->msgs[0],
-                                                     SYSDB_NAME,
--                                                    NULL);
-+                                                    memberuid);
-+        }
- 
--            if (memberuid != NULL) {
--                ret = sss_parse_internal_fqname(tmp_ctx, orig_name,
--                                                NULL, &orig_domain);
--                if (ret != EOK) {
--                    DEBUG(SSSDBG_OP_FAILURE,
--                         "sss_parse_internal_fqname failed to split [%s].\n",
--                         orig_name);
-+        /* add domain name if memberuid is a short name */
-+        if (memberuid != NULL && strchr(memberuid, '@') == NULL) {
-+            ret = sss_parse_internal_fqname(tmp_ctx, orig_name,
-+                                            NULL, &orig_domain);
-+            if (ret != EOK) {
-+                DEBUG(SSSDBG_OP_FAILURE,
-+                     "sss_parse_internal_fqname failed to split [%s].\n",
-+                     orig_name);
-+                goto done;
-+            }
-+
-+            if (orig_domain != NULL) {
-+                orig_dom = find_domain_by_name(get_domains_head(domain),
-+                                               orig_domain, true);
-+                if (orig_dom == NULL) {
-+                    DEBUG(SSSDBG_CRIT_FAILURE,
-+                          "Cannot find domain with name [%s].\n",
-+                          orig_domain);
-+                    ret = ERR_DOMAIN_NOT_FOUND;
-                     goto done;
-                 }
--
--                if (orig_domain != NULL) {
--                    orig_dom = find_domain_by_name(get_domains_head(domain),
--                                                   orig_domain, true);
--                    if (orig_dom == NULL) {
--                        DEBUG(SSSDBG_CRIT_FAILURE,
--                              "Cannot find domain with name [%s].\n",
--                              orig_domain);
--                        ret = ERR_DOMAIN_NOT_FOUND;
--                        goto done;
--                    }
--                    memberuid = sss_create_internal_fqname(tmp_ctx, memberuid,
--                                                           orig_dom->name);
--                    if (memberuid == NULL) {
--                        DEBUG(SSSDBG_OP_FAILURE,
--                              "sss_create_internal_fqname failed.\n");
--                        ret = ENOMEM;
--                        goto done;
--                    }
-+                memberuid = sss_create_internal_fqname(tmp_ctx, memberuid,
-+                                                       orig_dom->name);
-+                if (memberuid == NULL) {
-+                    DEBUG(SSSDBG_OP_FAILURE,
-+                          "sss_create_internal_fqname failed.\n");
-+                    ret = ENOMEM;
-+                    goto done;
-                 }
-             }
-         }
-@@ -1521,9 +1512,6 @@ errno_t sysdb_add_group_member_overrides(struct sss_domain_info *domain,
-         DEBUG(SSSDBG_TRACE_ALL, "Added [%s] to [%s].\n", memberuid,
-                                 OVERRIDE_PREFIX SYSDB_MEMBERUID);
- 
--        /* Free all temporary data of the current member to avoid memory usage
--         * spikes. All temporary data should be allocated below member_dn. */
--        talloc_free(member_dn);
-     }
- 
-     ret = EOK;
-diff --git a/src/responder/nss/nsssrv_cmd.c b/src/responder/nss/nsssrv_cmd.c
-index 1ae17969688fa29734ca14fd2b152decef1fdbca..4e84b3202cbf367e70a47a3c7edb06e357657538 100644
---- a/src/responder/nss/nsssrv_cmd.c
-+++ b/src/responder/nss/nsssrv_cmd.c
-@@ -2976,7 +2976,12 @@ static int fill_grent(struct sss_packet *packet,
- 
-         memnum = 0;
-         if (!dom->ignore_group_members) {
--            el = sss_view_ldb_msg_find_element(dom, msg, SYSDB_MEMBERUID);
-+            /* unconditionally prefer OVERRIDE_PREFIX SYSDB_MEMBERUID, it
-+             * might contain override names from the default view */
-+            el = ldb_msg_find_element(msg, OVERRIDE_PREFIX SYSDB_MEMBERUID);
-+            if (el == NULL) {
-+                el = ldb_msg_find_element(msg, SYSDB_MEMBERUID);
-+            }
-             if (el) {
-                 ret = fill_members(packet, nctx->rctx, dom, nctx, el,
-                                    &rzero, &rsize, &memnum);
-diff --git a/src/tests/cmocka/test_nss_srv.c b/src/tests/cmocka/test_nss_srv.c
-index 82a304feed864b09168d0f3e06a4e1bb120df7e4..41425e76f3b76fafa917f33fcfef0946f2f71c7d 100644
---- a/src/tests/cmocka/test_nss_srv.c
-+++ b/src/tests/cmocka/test_nss_srv.c
-@@ -1619,11 +1619,11 @@ 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] = testmember1.pw_name;
--    exp_members[1] = testmember2.pw_name;
--    exp_members[2] = sss_tc_fqname(tmp_ctx, nss_test_ctx->subdom->names,
-+    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[2]);
-+    assert_non_null(exp_members[0]);
-+    exp_members[1] = testmember1.pw_name;
-+    exp_members[2] = testmember2.pw_name;
- 
-     assert_int_equal(status, EOK);
- 
-@@ -1682,14 +1682,14 @@ 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->tctx->dom->names,
--                                   nss_test_ctx->tctx->dom, testmember1.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] = sss_tc_fqname(tmp_ctx, nss_test_ctx->tctx->dom->names,
--                                   nss_test_ctx->tctx->dom, testmember2.pw_name);
-+                                   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->subdom->names,
--                                   nss_test_ctx->subdom, submember1.pw_name);
-+    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,
--- 
-2.4.11
-
diff --git a/SOURCES/0049-IPA-fix-lookup-by-UPN-for-subdomains.patch b/SOURCES/0049-IPA-fix-lookup-by-UPN-for-subdomains.patch
deleted file mode 100644
index abd98c4..0000000
--- a/SOURCES/0049-IPA-fix-lookup-by-UPN-for-subdomains.patch
+++ /dev/null
@@ -1,61 +0,0 @@
-From 196e4a16c32e2f5d7e63f2dacd4f3cf4128f6814 Mon Sep 17 00:00:00 2001
-From: Sumit Bose <sbose@redhat.com>
-Date: Fri, 22 Jul 2016 17:35:43 +0200
-Subject: [PATCH 49/62] IPA: fix lookup by UPN for subdomains
-
-Currently the user name used in the extdom exop request is
-unconditionally set to the short name. While this is correct for the
-general name based lookups it breaks UPN/email based lookups where the
-name part after the @-sign might not match to domain name. I guess this
-was introduce during the sysdb refactoring.
-
-Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
-(cherry picked from commit 530458a4ef7cd8429d1db2f3dfae92d9c44e38ef)
----
- src/providers/ipa/ipa_subdomains_id.c | 16 ++++++++++++----
- 1 file changed, 12 insertions(+), 4 deletions(-)
-
-diff --git a/src/providers/ipa/ipa_subdomains_id.c b/src/providers/ipa/ipa_subdomains_id.c
-index 542c596a983bcb48f4eac699f78eb956326cefa2..002857699b65c86a6ed0c912a2a7ae06a8f9e507 100644
---- a/src/providers/ipa/ipa_subdomains_id.c
-+++ b/src/providers/ipa/ipa_subdomains_id.c
-@@ -344,6 +344,7 @@ struct ipa_get_subdom_acct {
-     int entry_type;
-     const char *filter;
-     int filter_type;
-+    const char *extra_value;
-     bool use_pac;
-     struct ldb_message *user_msg;
- 
-@@ -393,6 +394,7 @@ struct tevent_req *ipa_get_subdom_acct_send(TALLOC_CTX *memctx,
-     state->entry_type = (ar->entry_type & BE_REQ_TYPE_MASK);
-     state->filter = ar->filter_value;
-     state->filter_type = ar->filter_type;
-+    state->extra_value = ar->extra_value;
- 
-     switch (state->entry_type) {
-         case BE_REQ_USER:
-@@ -499,10 +501,16 @@ static void ipa_get_subdom_acct_connected(struct tevent_req *subreq)
-     switch (state->filter_type) {
-         case BE_FILTER_NAME:
-             req_input->type = REQ_INP_NAME;
--            /* The extdom plugin expects the shortname and domain separately */
--            ret = sss_parse_internal_fqname(req_input, state->filter,
--                                            &shortname, NULL);
--            req_input->inp.name = talloc_steal(req_input, shortname);
-+            /* The extdom plugin expects the shortname and domain separately,
-+             * but for UPN/email lookup we need to send the raw name */
-+            if (state->extra_value != NULL
-+                    && strcmp(state->extra_value, EXTRA_NAME_IS_UPN) == 0) {
-+                req_input->inp.name = talloc_strdup(req_input, state->filter);
-+            } else {
-+                ret = sss_parse_internal_fqname(req_input, state->filter,
-+                                                &shortname, NULL);
-+                req_input->inp.name = talloc_steal(req_input, shortname);
-+            }
-             if (req_input->inp.name == NULL) {
-                 DEBUG(SSSDBG_OP_FAILURE, "talloc_strdup failed.\n");
-                 tevent_req_error(req, ENOMEM);
--- 
-2.4.11
-
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
new file mode 100644
index 0000000..71624c8
--- /dev/null
+++ b/SOURCES/0049-IPA_SUBDOMAINS-Rename-_refresh_view-to-_refresh_view.patch
@@ -0,0 +1,54 @@
+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/0050-IPA-Get-ipaDomainsResolutionOrder-from-IPA-ID-View.patch b/SOURCES/0050-IPA-Get-ipaDomainsResolutionOrder-from-IPA-ID-View.patch
new file mode 100644
index 0000000..a1da431
--- /dev/null
+++ b/SOURCES/0050-IPA-Get-ipaDomainsResolutionOrder-from-IPA-ID-View.patch
@@ -0,0 +1,347 @@
+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-LDAP-allow-multiple-user-principals.patch b/SOURCES/0050-LDAP-allow-multiple-user-principals.patch
deleted file mode 100644
index de62b4a..0000000
--- a/SOURCES/0050-LDAP-allow-multiple-user-principals.patch
+++ /dev/null
@@ -1,70 +0,0 @@
-From f1eb45c3e8a198615c6731dfe9d965ab421723e8 Mon Sep 17 00:00:00 2001
-From: Sumit Bose <sbose@redhat.com>
-Date: Fri, 22 Jul 2016 12:19:26 +0200
-Subject: [PATCH 50/62] LDAP: allow multiple user principals
-
-In general a user can have multiple principals and recent IPA version
-added support to defined multiple principals. With this patch SSSD does
-not only store the first but all principals read by LDAP from a server.
-
-Resolves https://fedorahosted.org/sssd/ticket/2958
-
-Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
-(cherry picked from commit 0d5d490fb5ec685fd8ef7a75e612e6ec7ef6bde3)
----
- src/providers/ldap/sdap_async_users.c | 32 ++++++++++++++++++--------------
- 1 file changed, 18 insertions(+), 14 deletions(-)
-
-diff --git a/src/providers/ldap/sdap_async_users.c b/src/providers/ldap/sdap_async_users.c
-index e44c045b3f8ff6aed33a42cf2919bc01aa41a243..28101a2d8a38f97d09d50a9f7e071a030b4f9719 100644
---- a/src/providers/ldap/sdap_async_users.c
-+++ b/src/providers/ldap/sdap_async_users.c
-@@ -142,6 +142,7 @@ int sdap_save_user(TALLOC_CTX *memctx,
-     char *sid_str;
-     char *dom_sid_str = NULL;
-     struct sss_domain_info *subdomain;
-+    size_t c;
- 
-     DEBUG(SSSDBG_TRACE_FUNC, "Save user\n");
- 
-@@ -440,20 +441,23 @@ int sdap_save_user(TALLOC_CTX *memctx,
-         DEBUG(SSSDBG_TRACE_FUNC,
-               "User principal is not available for [%s].\n", user_name);
-     } else {
--        upn = talloc_strdup(user_attrs, (const char*) el->values[0].data);
--        if (!upn) {
--            ret = ENOMEM;
--            goto done;
--        }
--        if (dp_opt_get_bool(opts->basic, SDAP_FORCE_UPPER_CASE_REALM)) {
--            make_realm_upper_case(upn);
--        }
--        DEBUG(SSSDBG_TRACE_FUNC,
--              "Adding user principal [%s] to attributes of [%s].\n",
--               upn, user_name);
--        ret = sysdb_attrs_add_string(user_attrs, SYSDB_UPN, upn);
--        if (ret) {
--            goto done;
-+        for (c = 0; c < el->num_values; c++) {
-+            upn = talloc_strdup(tmpctx, (const char*) el->values[c].data);
-+            if (!upn) {
-+                ret = ENOMEM;
-+                goto done;
-+            }
-+
-+            if (dp_opt_get_bool(opts->basic, SDAP_FORCE_UPPER_CASE_REALM)) {
-+                make_realm_upper_case(upn);
-+            }
-+            DEBUG(SSSDBG_TRACE_FUNC,
-+                  "Adding user principal [%s] to attributes of [%s].\n",
-+                   upn, user_name);
-+            ret = sysdb_attrs_add_string(user_attrs, SYSDB_UPN, upn);
-+            if (ret) {
-+                goto done;
-+            }
-         }
-     }
- 
--- 
-2.4.11
-
diff --git a/SOURCES/0051-DLINKLIST-Add-DLIST_FOR_EACH_SAFE-macro.patch b/SOURCES/0051-DLINKLIST-Add-DLIST_FOR_EACH_SAFE-macro.patch
new file mode 100644
index 0000000..b75b73d
--- /dev/null
+++ b/SOURCES/0051-DLINKLIST-Add-DLIST_FOR_EACH_SAFE-macro.patch
@@ -0,0 +1,37 @@
+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-LDAP-new-attribute-option-ldap_user_email.patch b/SOURCES/0051-LDAP-new-attribute-option-ldap_user_email.patch
deleted file mode 100644
index 85b5898..0000000
--- a/SOURCES/0051-LDAP-new-attribute-option-ldap_user_email.patch
+++ /dev/null
@@ -1,171 +0,0 @@
-From 03d7bda082c8719bfb4ea63c9126442c98a27be1 Mon Sep 17 00:00:00 2001
-From: Sumit Bose <sbose@redhat.com>
-Date: Sat, 18 Jun 2016 18:24:50 +0200
-Subject: [PATCH 51/62] LDAP: new attribute option ldap_user_email
-
-Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
-(cherry picked from commit 83a796ec8de4bde65b11cc8032675406950641fa)
----
- src/config/SSSDConfig/__init__.py.in     |  1 +
- src/config/etc/sssd.api.d/sssd-ad.conf   |  1 +
- src/config/etc/sssd.api.d/sssd-ipa.conf  |  1 +
- src/config/etc/sssd.api.d/sssd-ldap.conf |  1 +
- src/db/sysdb.h                           |  1 +
- src/man/sssd-ldap.5.xml                  | 13 +++++++++++++
- src/providers/ad/ad_opts.c               |  1 +
- src/providers/ipa/ipa_opts.c             |  1 +
- src/providers/ldap/ldap_opts.c           |  3 +++
- src/providers/ldap/sdap.h                |  1 +
- 10 files changed, 24 insertions(+)
-
-diff --git a/src/config/SSSDConfig/__init__.py.in b/src/config/SSSDConfig/__init__.py.in
-index b5e078d0118a15c10b43fbe050176943ec90e0ee..7856c4c6b2d675b7f7f0f5f2048086044e8fb5ea 100644
---- a/src/config/SSSDConfig/__init__.py.in
-+++ b/src/config/SSSDConfig/__init__.py.in
-@@ -325,6 +325,7 @@ option_strings = {
-     'ldap_user_ssh_public_key' : _('SSH public key attribute'),
-     'ldap_user_auth_type' : _('attribute listing allowed authentication types for a user'),
-     'ldap_user_certificate' : _('attribute containing the X509 certificate of the user'),
-+    'ldap_user_email' : _('attribute containing the email address of the user'),
- 
-     'ldap_user_extra_attrs' : _('A list of extra attributes to download along with the user entry'),
- 
-diff --git a/src/config/etc/sssd.api.d/sssd-ad.conf b/src/config/etc/sssd.api.d/sssd-ad.conf
-index 23006d26ca6fe7ca2b912ef091b4c73d5d23bee1..87a74f4af0770874c71baaea02d2313721db78bf 100644
---- a/src/config/etc/sssd.api.d/sssd-ad.conf
-+++ b/src/config/etc/sssd.api.d/sssd-ad.conf
-@@ -98,6 +98,7 @@ ldap_pwd_attribute = str, None, false
- ldap_user_ssh_public_key = str, None, false
- ldap_user_auth_type = str, None, false
- ldap_user_certificate = str, None, false
-+ldap_user_email = str, None, false
- ldap_group_search_base = str, None, false
- ldap_group_search_scope = str, None, false
- ldap_group_search_filter = str, None, false
-diff --git a/src/config/etc/sssd.api.d/sssd-ipa.conf b/src/config/etc/sssd.api.d/sssd-ipa.conf
-index 67a46102b4e8dfff2b44b21ac18c0ad8822d7f3a..88da36ef4a0a067530dfd44b7a231f4f74c800f2 100644
---- a/src/config/etc/sssd.api.d/sssd-ipa.conf
-+++ b/src/config/etc/sssd.api.d/sssd-ipa.conf
-@@ -92,6 +92,7 @@ ldap_pwd_attribute = str, None, false
- ldap_user_ssh_public_key = str, None, false
- ldap_user_auth_type = str, None, false
- ldap_user_certificate = str, None, false
-+ldap_user_email = str, None, false
- ldap_group_search_base = str, None, false
- ldap_group_search_scope = str, None, false
- ldap_group_search_filter = str, None, false
-diff --git a/src/config/etc/sssd.api.d/sssd-ldap.conf b/src/config/etc/sssd.api.d/sssd-ldap.conf
-index 8b52f268af195bc68d45389cda52a0ad0aba1aa3..c2ad3463d26cd73b8146604c8060224449421fe6 100644
---- a/src/config/etc/sssd.api.d/sssd-ldap.conf
-+++ b/src/config/etc/sssd.api.d/sssd-ldap.conf
-@@ -86,6 +86,7 @@ ldap_user_nds_login_allowed_time_map = str, None, false
- ldap_user_ssh_public_key = str, None, false
- ldap_user_auth_type = str, None, false
- ldap_user_certificate = str, None, false
-+ldap_user_email = str, None, false
- ldap_group_search_base = str, None, false
- ldap_group_search_scope = str, None, false
- ldap_group_search_filter = str, None, false
-diff --git a/src/db/sysdb.h b/src/db/sysdb.h
-index a27552224bb40bd07c7dee4dfe35bfb7a0b4f2c3..f3952f8a56f1c9f26f2167b64abdf3e9794af17e 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_EMAIL "mail"
- 
- #define SYSDB_SUBDOMAIN_REALM "realmName"
- #define SYSDB_SUBDOMAIN_FLAT "flatName"
-diff --git a/src/man/sssd-ldap.5.xml b/src/man/sssd-ldap.5.xml
-index ce2051d9d3c7df51e26e54abf49e8a20bf5ba3d3..6009dd8dfa787874c085c293b2d1f8aac6d95714 100644
---- a/src/man/sssd-ldap.5.xml
-+++ b/src/man/sssd-ldap.5.xml
-@@ -828,6 +828,19 @@
-                 </varlistentry>
- 
-                 <varlistentry>
-+                    <term>ldap_user_email (string)</term>
-+                    <listitem>
-+                        <para>
-+                            Name of the LDAP attribute containing the email
-+                            address of the user.
-+                        </para>
-+                        <para>
-+                            Default: mail
-+                        </para>
-+                    </listitem>
-+                </varlistentry>
-+
-+                <varlistentry>
-                     <term>ldap_group_object_class (string)</term>
-                     <listitem>
-                         <para>
-diff --git a/src/providers/ad/ad_opts.c b/src/providers/ad/ad_opts.c
-index 57dfcca6b998083c7cf9ac0bcb142ff7736cc8b9..829f9d9556bc3fa74a95eb76db0e31b19befe8fe 100644
---- a/src/providers/ad/ad_opts.c
-+++ b/src/providers/ad/ad_opts.c
-@@ -218,6 +218,7 @@ struct sdap_attr_map ad_2008r2_user_map[] = {
-     { "ldap_user_ssh_public_key", NULL, SYSDB_SSH_PUBKEY, NULL },
-     { "ldap_user_auth_type", NULL, SYSDB_AUTH_TYPE, NULL },
-     { "ldap_user_certificate", "userCertificate;binary", SYSDB_USER_CERT, NULL },
-+    { "ldap_user_email", "mail", SYSDB_USER_EMAIL, NULL },
-     SDAP_ATTR_MAP_TERMINATOR
- };
- 
-diff --git a/src/providers/ipa/ipa_opts.c b/src/providers/ipa/ipa_opts.c
-index a0c318a511693d884f03f0372c592d633ebdcbae..cd3fe9ae4302ff4837a500b9a0c834dadb11f87d 100644
---- a/src/providers/ipa/ipa_opts.c
-+++ b/src/providers/ipa/ipa_opts.c
-@@ -204,6 +204,7 @@ struct sdap_attr_map ipa_user_map[] = {
-     { "ldap_user_ssh_public_key", "ipaSshPubKey", SYSDB_SSH_PUBKEY, NULL },
-     { "ldap_user_auth_type", "ipaUserAuthType", SYSDB_AUTH_TYPE, NULL },
-     { "ldap_user_certificate", "userCertificate;binary", SYSDB_USER_CERT, NULL },
-+    { "ldap_user_email", "mail", SYSDB_USER_EMAIL, NULL },
-     SDAP_ATTR_MAP_TERMINATOR
- };
- 
-diff --git a/src/providers/ldap/ldap_opts.c b/src/providers/ldap/ldap_opts.c
-index 524579d4fcd478f20678bebf2c3ce18f61ed0cb9..c6efe332f53c04f3cdc80875d5ca339ad90cb7ee 100644
---- a/src/providers/ldap/ldap_opts.c
-+++ b/src/providers/ldap/ldap_opts.c
-@@ -180,6 +180,7 @@ struct sdap_attr_map rfc2307_user_map[] = {
-     { "ldap_user_ssh_public_key", "sshPublicKey", SYSDB_SSH_PUBKEY, NULL },
-     { "ldap_user_auth_type", NULL, SYSDB_AUTH_TYPE, NULL },
-     { "ldap_user_certificate", NULL, SYSDB_USER_CERT, NULL },
-+    { "ldap_user_email", "mail", SYSDB_USER_EMAIL, NULL },
-     SDAP_ATTR_MAP_TERMINATOR
- };
- 
-@@ -237,6 +238,7 @@ struct sdap_attr_map rfc2307bis_user_map[] = {
-     { "ldap_user_ssh_public_key", "sshPublicKey", SYSDB_SSH_PUBKEY, NULL },
-     { "ldap_user_auth_type", NULL, SYSDB_AUTH_TYPE, NULL },
-     { "ldap_user_certificate", NULL, SYSDB_USER_CERT, NULL },
-+    { "ldap_user_email", "mail", SYSDB_USER_EMAIL, NULL },
-     SDAP_ATTR_MAP_TERMINATOR
- };
- 
-@@ -294,6 +296,7 @@ struct sdap_attr_map gen_ad2008r2_user_map[] = {
-     { "ldap_user_ssh_public_key", NULL, SYSDB_SSH_PUBKEY, NULL },
-     { "ldap_user_auth_type", NULL, SYSDB_AUTH_TYPE, NULL },
-     { "ldap_user_certificate", NULL, SYSDB_USER_CERT, NULL },
-+    { "ldap_user_email", "mail", SYSDB_USER_EMAIL, NULL },
-     SDAP_ATTR_MAP_TERMINATOR
- };
- 
-diff --git a/src/providers/ldap/sdap.h b/src/providers/ldap/sdap.h
-index 81da1144c657cb71ac860bbe82127a18759e0439..e3cb8464ff40538e1e7f1ba853ed71d9a5cc3c98 100644
---- a/src/providers/ldap/sdap.h
-+++ b/src/providers/ldap/sdap.h
-@@ -284,6 +284,7 @@ enum sdap_user_attrs {
-     SDAP_AT_USER_SSH_PUBLIC_KEY,
-     SDAP_AT_USER_AUTH_TYPE,
-     SDAP_AT_USER_CERT,
-+    SDAP_AT_USER_EMAIL,
- 
-     SDAP_OPTS_USER /* attrs counter */
- };
--- 
-2.4.11
-
diff --git a/SOURCES/0052-CACHE_REQ-Make-use-of-domainResolutionOrder.patch b/SOURCES/0052-CACHE_REQ-Make-use-of-domainResolutionOrder.patch
new file mode 100644
index 0000000..4086ace
--- /dev/null
+++ b/SOURCES/0052-CACHE_REQ-Make-use-of-domainResolutionOrder.patch
@@ -0,0 +1,795 @@
+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-sysdb-include-email-in-UPN-searches.patch b/SOURCES/0052-sysdb-include-email-in-UPN-searches.patch
deleted file mode 100644
index 4dd7f04..0000000
--- a/SOURCES/0052-sysdb-include-email-in-UPN-searches.patch
+++ /dev/null
@@ -1,57 +0,0 @@
-From 2e9ea4e5c12c0d50509904415beda6b841b91f65 Mon Sep 17 00:00:00 2001
-From: Sumit Bose <sbose@redhat.com>
-Date: Mon, 20 Jun 2016 12:57:43 +0200
-Subject: [PATCH 52/62] sysdb: include email in UPN searches
-
-Email addresses and Kerberos user principals names (UPNs) do not only
-look similar they also can be used to identify a user uniquely.
-
-In future this approach should be replace by a more generic one where
-the attributes which can uniquely identifies a user can be configured to
-support even a wider range of login names.
-
-Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
-(cherry picked from commit 78677495a7762469002b0976809fa20ac2196f42)
----
- src/db/sysdb.h     | 2 +-
- src/db/sysdb_ops.c | 4 ++--
- 2 files changed, 3 insertions(+), 3 deletions(-)
-
-diff --git a/src/db/sysdb.h b/src/db/sysdb.h
-index f3952f8a56f1c9f26f2167b64abdf3e9794af17e..c2f58ccb97c37d93391e72ee2d77835283a6c12f 100644
---- a/src/db/sysdb.h
-+++ b/src/db/sysdb.h
-@@ -185,7 +185,7 @@
- #define SYSDB_PWNAM_FILTER "(&("SYSDB_UC")(|("SYSDB_NAME_ALIAS"=%s)("SYSDB_NAME_ALIAS"=%s)("SYSDB_NAME"=%s)))"
- #define SYSDB_PWUID_FILTER "(&("SYSDB_UC")("SYSDB_UIDNUM"=%lu))"
- #define SYSDB_PWSID_FILTER "(&("SYSDB_UC")("SYSDB_SID_STR"=%s))"
--#define SYSDB_PWUPN_FILTER "(&("SYSDB_UC")(|("SYSDB_UPN"=%s)("SYSDB_CANONICAL_UPN"=%s)))"
-+#define SYSDB_PWUPN_FILTER "(&("SYSDB_UC")(|("SYSDB_UPN"=%s)("SYSDB_CANONICAL_UPN"=%s)("SYSDB_USER_EMAIL"=%s)))"
- #define SYSDB_PWENT_FILTER "("SYSDB_UC")"
- 
- #define SYSDB_GRNAM_FILTER "(&("SYSDB_GC")(|("SYSDB_NAME_ALIAS"=%s)("SYSDB_NAME_ALIAS"=%s)("SYSDB_NAME"=%s)))"
-diff --git a/src/db/sysdb_ops.c b/src/db/sysdb_ops.c
-index 9a8a55ed8aa69e1638d0ab6f636e43baa3d0bfea..ed177d1730723a61e01167a75a0baca6d81252f8 100644
---- a/src/db/sysdb_ops.c
-+++ b/src/db/sysdb_ops.c
-@@ -537,7 +537,7 @@ int sysdb_search_user_by_upn_res(TALLOC_CTX *mem_ctx,
-     struct ldb_dn *base_dn;
-     int ret;
-     const char *def_attrs[] = { SYSDB_NAME, SYSDB_UPN, SYSDB_CANONICAL_UPN,
--                                NULL };
-+                                SYSDB_USER_EMAIL, NULL };
- 
-     tmp_ctx = talloc_new(NULL);
-     if (tmp_ctx == NULL) {
-@@ -553,7 +553,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);
-+                     SYSDB_PWUPN_FILTER, upn, upn, upn);
-     if (ret != EOK) {
-         ret = sysdb_error_to_errno(ret);
-         goto done;
--- 
-2.4.11
-
diff --git a/SOURCES/0053-LDAP-include-email-in-UPN-searches.patch b/SOURCES/0053-LDAP-include-email-in-UPN-searches.patch
deleted file mode 100644
index 878b42a..0000000
--- a/SOURCES/0053-LDAP-include-email-in-UPN-searches.patch
+++ /dev/null
@@ -1,115 +0,0 @@
-From c333512b85f979ae0694055a36d8dafcf4105248 Mon Sep 17 00:00:00 2001
-From: Sumit Bose <sbose@redhat.com>
-Date: Mon, 20 Jun 2016 12:58:16 +0200
-Subject: [PATCH 53/62] LDAP: include email in UPN searches
-
-Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
-(cherry picked from commit ba9ebfc49ab3bacb96213c8620411128c09f39da)
----
- src/providers/ldap/ldap_id.c               | 18 +++++++++++++----
- src/providers/ldap/sdap_async_initgroups.c | 32 ++++++++++++++++++++++++------
- 2 files changed, 40 insertions(+), 10 deletions(-)
-
-diff --git a/src/providers/ldap/ldap_id.c b/src/providers/ldap/ldap_id.c
-index 0106a7b965b8d7debbefe82f60088df9ef8c608b..5b303ddbd46fd44646cdd50856c784640426ee25 100644
---- a/src/providers/ldap/ldap_id.c
-+++ b/src/providers/ldap/ldap_id.c
-@@ -127,12 +127,22 @@ struct tevent_req *users_get_send(TALLOC_CTX *memctx,
-         break;
-     case BE_FILTER_NAME:
-         if (extra_value && strcmp(extra_value, EXTRA_NAME_IS_UPN) == 0) {
--            attr_name = ctx->opts->user_map[SDAP_AT_USER_PRINC].name;
--
-             ret = sss_filter_sanitize(state, filter_value, &clean_value);
-             if (ret != EOK) {
-                 goto done;
-             }
-+            /* TODO: Do we have to check the attribute names more carefully? */
-+            user_filter = talloc_asprintf(state, "(|(%s=%s)(%s=%s))",
-+                                   ctx->opts->user_map[SDAP_AT_USER_PRINC].name,
-+                                   clean_value,
-+                                   ctx->opts->user_map[SDAP_AT_USER_EMAIL].name,
-+                                   clean_value);
-+            talloc_zfree(clean_value);
-+            if (user_filter == NULL) {
-+                DEBUG(SSSDBG_OP_FAILURE, "talloc_asprintf failed.\n");
-+                ret = ENOMEM;
-+                goto done;
-+            }
-         } else {
-             attr_name = ctx->opts->user_map[SDAP_AT_USER_NAME].name;
- 
-@@ -242,8 +252,8 @@ struct tevent_req *users_get_send(TALLOC_CTX *memctx,
-         goto done;
-     }
- 
--    if (attr_name == NULL) {
--        DEBUG(SSSDBG_OP_FAILURE, "Missing search attribute name.\n");
-+    if (attr_name == NULL && user_filter == NULL) {
-+        DEBUG(SSSDBG_OP_FAILURE, "Missing search attribute name or filter.\n");
-         ret = EINVAL;
-         goto done;
-     }
-diff --git a/src/providers/ldap/sdap_async_initgroups.c b/src/providers/ldap/sdap_async_initgroups.c
-index 17593f0a268813662d6c7fbf658b1eb4599ce3c3..0a42b18662a8fe12cf048aadfef257b5d9cb48a3 100644
---- a/src/providers/ldap/sdap_async_initgroups.c
-+++ b/src/providers/ldap/sdap_async_initgroups.c
-@@ -2736,13 +2736,25 @@ struct tevent_req *sdap_get_initgr_send(TALLOC_CTX *memctx,
-         break;
-     case BE_FILTER_NAME:
-         if (extra_value && strcmp(extra_value, EXTRA_NAME_IS_UPN) == 0) {
--            search_attr =  state->opts->user_map[SDAP_AT_USER_PRINC].name;
- 
-             ret = sss_filter_sanitize(state, state->filter_value, &clean_name);
-             if (ret != EOK) {
-                 talloc_zfree(req);
-                 return NULL;
-             }
-+
-+            state->user_base_filter =
-+                    talloc_asprintf(state,
-+                                 "(&(|(%s=%s)(%s=%s))(objectclass=%s)",
-+                                 state->opts->user_map[SDAP_AT_USER_PRINC].name,
-+                                 clean_name,
-+                                 state->opts->user_map[SDAP_AT_USER_EMAIL].name,
-+                                 clean_name,
-+                                 state->opts->user_map[SDAP_OC_USER].name);
-+            if (state->user_base_filter == NULL) {
-+                talloc_zfree(req);
-+                return NULL;
-+            }
-         } else {
-             search_attr = state->opts->user_map[SDAP_AT_USER_NAME].name;
- 
-@@ -2766,15 +2778,23 @@ struct tevent_req *sdap_get_initgr_send(TALLOC_CTX *memctx,
-         return NULL;
-     }
- 
--    state->user_base_filter =
--            talloc_asprintf(state, "(&(%s=%s)(objectclass=%s)",
--                            search_attr, clean_name,
--                            state->opts->user_map[SDAP_OC_USER].name);
--    if (!state->user_base_filter) {
-+    if (search_attr == NULL && state->user_base_filter == NULL) {
-+        DEBUG(SSSDBG_OP_FAILURE, "Missing search attribute name or filter.\n");
-         talloc_zfree(req);
-         return NULL;
-     }
- 
-+    if (state->user_base_filter == NULL) {
-+        state->user_base_filter =
-+                talloc_asprintf(state, "(&(%s=%s)(objectclass=%s)",
-+                                search_attr, clean_name,
-+                                state->opts->user_map[SDAP_OC_USER].name);
-+        if (!state->user_base_filter) {
-+            talloc_zfree(req);
-+            return NULL;
-+        }
-+    }
-+
-     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
--- 
-2.4.11
-
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
new file mode 100644
index 0000000..0db08c7
--- /dev/null
+++ b/SOURCES/0053-UTIL-Expose-replace_char-as-sss_replace_char.patch
@@ -0,0 +1,81 @@
+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/0054-Add-domain_resolution_order-config-option.patch b/SOURCES/0054-Add-domain_resolution_order-config-option.patch
new file mode 100644
index 0000000..e91960d
--- /dev/null
+++ b/SOURCES/0054-Add-domain_resolution_order-config-option.patch
@@ -0,0 +1,205 @@
+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-NSS-add-user-email-to-fill_orig.patch b/SOURCES/0054-NSS-add-user-email-to-fill_orig.patch
deleted file mode 100644
index b748512..0000000
--- a/SOURCES/0054-NSS-add-user-email-to-fill_orig.patch
+++ /dev/null
@@ -1,50 +0,0 @@
-From ac8bc2cbea5081f1f0a9395fbaf76616fe5b6fb9 Mon Sep 17 00:00:00 2001
-From: Sumit Bose <sbose@redhat.com>
-Date: Mon, 20 Jun 2016 13:37:56 +0200
-Subject: [PATCH 54/62] NSS: add user email to fill_orig()
-
-The IPA server must send the email address of a user to the clients to
-allow login by email.
-
-Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
-(cherry picked from commit 91767924bdf9b5a28e8902206a40348d6c83a139)
----
- src/db/sysdb.h                 | 1 +
- src/responder/nss/nsssrv_cmd.c | 2 ++
- 2 files changed, 3 insertions(+)
-
-diff --git a/src/db/sysdb.h b/src/db/sysdb.h
-index c2f58ccb97c37d93391e72ee2d77835283a6c12f..8713efa6e8fcc6fb620340fe152989a5dae58434 100644
---- a/src/db/sysdb.h
-+++ b/src/db/sysdb.h
-@@ -220,6 +220,7 @@
-                         SYSDB_SID_STR, \
-                         SYSDB_UPN, \
-                         SYSDB_USER_CERT, \
-+                        SYSDB_USER_EMAIL, \
-                         SYSDB_OVERRIDE_DN, \
-                         SYSDB_OVERRIDE_OBJECT_DN, \
-                         SYSDB_DEFAULT_OVERRIDE_NAME, \
-diff --git a/src/responder/nss/nsssrv_cmd.c b/src/responder/nss/nsssrv_cmd.c
-index 4e84b3202cbf367e70a47a3c7edb06e357657538..77e540d8da969614e4ab40c62e6ae1f271962f31 100644
---- a/src/responder/nss/nsssrv_cmd.c
-+++ b/src/responder/nss/nsssrv_cmd.c
-@@ -4421,6 +4421,7 @@ static errno_t nss_cmd_getsidby_search(struct nss_dom_ctx *dctx)
-                                    SYSDB_AD_USER_ACCOUNT_CONTROL,
-                                    SYSDB_SSH_PUBKEY,
-                                    SYSDB_USER_CERT,
-+                                   SYSDB_USER_EMAIL,
-                                    SYSDB_ORIG_DN,
-                                    SYSDB_ORIG_MEMBEROF,
-                                    SYSDB_DEFAULT_ATTRS, NULL};
-@@ -4977,6 +4978,7 @@ static errno_t fill_orig(struct sss_packet *packet,
-                                     SYSDB_AD_USER_ACCOUNT_CONTROL,
-                                     SYSDB_SSH_PUBKEY,
-                                     SYSDB_USER_CERT,
-+                                    SYSDB_USER_EMAIL,
-                                     SYSDB_ORIG_DN,
-                                     SYSDB_ORIG_MEMBEROF,
-                                     NULL};
--- 
-2.4.11
-
diff --git a/SOURCES/0055-ssh-handle-binary-keys-correctly.patch b/SOURCES/0055-ssh-handle-binary-keys-correctly.patch
new file mode 100644
index 0000000..05f87b1
--- /dev/null
+++ b/SOURCES/0055-ssh-handle-binary-keys-correctly.patch
@@ -0,0 +1,48 @@
+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-utils-add-is_email_from_domain.patch b/SOURCES/0055-utils-add-is_email_from_domain.patch
deleted file mode 100644
index e75ddfc..0000000
--- a/SOURCES/0055-utils-add-is_email_from_domain.patch
+++ /dev/null
@@ -1,102 +0,0 @@
-From 3f7b69c3af7b810443a94a1004d41b87e5ca79d8 Mon Sep 17 00:00:00 2001
-From: Sumit Bose <sbose@redhat.com>
-Date: Mon, 20 Jun 2016 16:11:11 +0200
-Subject: [PATCH 55/62] utils: add is_email_from_domain()
-
-Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
-(cherry picked from commit 04d4c4d45f3942a813b7f772737f801f877f4e64)
----
- src/tests/cmocka/test_utils.c | 21 +++++++++++++++++++++
- src/util/domain_info_utils.c  | 27 +++++++++++++++++++++++++++
- src/util/util.h               |  1 +
- 3 files changed, 49 insertions(+)
-
-diff --git a/src/tests/cmocka/test_utils.c b/src/tests/cmocka/test_utils.c
-index 5349accc5c6b55f7d725bfdeaf318b90289880dd..09273692968b544aed88de00c747e85ff0b6695c 100644
---- a/src/tests/cmocka/test_utils.c
-+++ b/src/tests/cmocka/test_utils.c
-@@ -1842,6 +1842,25 @@ static void test_sss_get_domain_mappings_content(void **state)
-      * capaths might not be as expected. */
- }
- 
-+static void test_is_email_from_domain(void **state)
-+{
-+    struct dom_list_test_ctx *test_ctx = talloc_get_type(*state,
-+                                                      struct dom_list_test_ctx);
-+    struct sss_domain_info *d;
-+
-+    d = find_domain_by_name(test_ctx->dom_list, "name_0.dom", false);
-+    assert_non_null(d);
-+
-+    assert_false(is_email_from_domain(NULL, NULL));
-+    assert_false(is_email_from_domain("hello", NULL));
-+    assert_false(is_email_from_domain(NULL, d));
-+    assert_false(is_email_from_domain("hello", d));
-+    assert_false(is_email_from_domain("hello@hello", d));
-+
-+    assert_true(is_email_from_domain("hello@name_0.dom", d));
-+    assert_true(is_email_from_domain("hello@NaMe_0.DoM", d));
-+}
-+
- int main(int argc, const char *argv[])
- {
-     poptContext pc;
-@@ -1870,6 +1889,8 @@ int main(int argc, const char *argv[])
-                                         setup_dom_list, teardown_dom_list),
-         cmocka_unit_test_setup_teardown(test_find_domain_by_name_disabled,
-                                         setup_dom_list, teardown_dom_list),
-+        cmocka_unit_test_setup_teardown(test_is_email_from_domain,
-+                                        setup_dom_list, teardown_dom_list),
- 
-         cmocka_unit_test_setup_teardown(test_sss_names_init,
-                                         confdb_test_setup,
-diff --git a/src/util/domain_info_utils.c b/src/util/domain_info_utils.c
-index 587a6b993d2bd70662df8e0b0d5963fa00c84cf8..0feda148bd44b9cefc43c094ddc5a72820412322 100644
---- a/src/util/domain_info_utils.c
-+++ b/src/util/domain_info_utils.c
-@@ -824,3 +824,30 @@ void sss_domain_set_state(struct sss_domain_info *dom,
- {
-     dom->state = state;
- }
-+
-+bool is_email_from_domain(const char *email, struct sss_domain_info *dom)
-+{
-+    const char *p;
-+
-+    if (email == NULL || dom == NULL) {
-+        return false;
-+    }
-+
-+    p = strchr(email, '@');
-+    if (p == NULL) {
-+        DEBUG(SSSDBG_TRACE_ALL,
-+              "Input [%s] does not look like an email address.\n", email);
-+        return false;
-+    }
-+
-+    if (strcasecmp(p+1, dom->name) == 0) {
-+        DEBUG(SSSDBG_TRACE_ALL, "Email [%s] is from domain [%s].\n", email,
-+                                                                     dom->name);
-+        return true;
-+    }
-+
-+    DEBUG(SSSDBG_TRACE_ALL, "Email [%s] is not from domain [%s].\n", email,
-+                                                                     dom->name);
-+
-+    return false;
-+}
-diff --git a/src/util/util.h b/src/util/util.h
-index 122be90b967fb7793adaff95f3754d7a199fcf48..4449315f8b1a79ec915bc340b46188c440a27fa3 100644
---- a/src/util/util.h
-+++ b/src/util/util.h
-@@ -514,6 +514,7 @@ struct sss_domain_info *find_domain_by_sid(struct sss_domain_info *domain,
- 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);
- 
- struct sss_domain_info*
- sss_get_domain_by_sid_ldap_fallback(struct sss_domain_info *domain,
--- 
-2.4.11
-
diff --git a/SOURCES/0056-LDAP-IPA-add-local-email-address-to-aliases.patch b/SOURCES/0056-LDAP-IPA-add-local-email-address-to-aliases.patch
deleted file mode 100644
index 51ebcba..0000000
--- a/SOURCES/0056-LDAP-IPA-add-local-email-address-to-aliases.patch
+++ /dev/null
@@ -1,126 +0,0 @@
-From 24ed6bff6cf81c7ba732a5515a2194d9e32cf354 Mon Sep 17 00:00:00 2001
-From: Sumit Bose <sbose@redhat.com>
-Date: Mon, 20 Jun 2016 16:30:03 +0200
-Subject: [PATCH 56/62] LDAP/IPA: add local email address to aliases
-
-Adding email-addresses from the local domain to the alias names is
-strictly not needed by might help to speed up lookups in the NSS
-responder.
-
-Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
-(cherry picked from commit 9a310913d696d190db14c625080678db853a33fd)
----
- src/providers/ipa/ipa_s2n_exop.c | 49 ++++++++++++++++++++++++++++++++++++++++
- src/providers/ldap/sdap_utils.c  | 22 ++++++++++++++++++
- 2 files changed, 71 insertions(+)
-
-diff --git a/src/providers/ipa/ipa_s2n_exop.c b/src/providers/ipa/ipa_s2n_exop.c
-index b28cc415b1c6dfcf06e0cb9769a36135da01b991..255dad45037a6cb8f399bf2df500215f6fb25b59 100644
---- a/src/providers/ipa/ipa_s2n_exop.c
-+++ b/src/providers/ipa/ipa_s2n_exop.c
-@@ -1885,6 +1885,49 @@ done:
-     return ret;
- }
- 
-+static errno_t add_emails_to_aliases(struct sysdb_attrs *attrs,
-+                                     struct sss_domain_info *dom)
-+{
-+    int ret;
-+    const char **emails;
-+    size_t c;
-+    TALLOC_CTX *tmp_ctx;
-+
-+    tmp_ctx = talloc_new(NULL);
-+    if (tmp_ctx == NULL) {
-+        DEBUG(SSSDBG_OP_FAILURE, "talloc_new failed.\n");
-+        return ENOMEM;
-+    }
-+
-+    ret = sysdb_attrs_get_string_array(attrs, SYSDB_USER_EMAIL, tmp_ctx,
-+                                       &emails);
-+    if (ret == EOK) {
-+        for (c = 0; emails[c] != NULL; c++) {
-+            if (is_email_from_domain(emails[c], dom)) {
-+                ret = sysdb_attrs_add_lc_name_alias_safe(attrs, emails[c]);
-+                if (ret != EOK) {
-+                    DEBUG(SSSDBG_OP_FAILURE,
-+                          "Failed to add lower-cased version of email [%s] "
-+                          "into the alias list\n", emails[c]);
-+                    goto done;
-+                }
-+            }
-+        }
-+    } else if (ret == ENOENT) {
-+        DEBUG(SSSDBG_TRACE_ALL, "No email addresses available.\n");
-+    } else {
-+        DEBUG(SSSDBG_OP_FAILURE,
-+              "sysdb_attrs_get_string_array failed, skipping ...\n");
-+    }
-+
-+    ret = EOK;
-+
-+done:
-+    talloc_free(tmp_ctx);
-+
-+    return ret;
-+}
-+
- static errno_t ipa_s2n_save_objects(struct sss_domain_info *dom,
-                                     struct req_input *req_input,
-                                     struct resp_attrs *attrs,
-@@ -2030,6 +2073,12 @@ static errno_t ipa_s2n_save_objects(struct sss_domain_info *dom,
-                 goto done;
-             }
- 
-+            ret = add_emails_to_aliases(attrs->sysdb_attrs, dom);
-+            if (ret != EOK) {
-+                DEBUG(SSSDBG_OP_FAILURE,
-+                      "add_emails_to_aliases failed, skipping ...\n");
-+            }
-+
-             if (upn == NULL) {
-                 /* We also have to store a fake UPN here, because otherwise the
-                  * krb5 child later won't be able to properly construct one as
-diff --git a/src/providers/ldap/sdap_utils.c b/src/providers/ldap/sdap_utils.c
-index 696af51d66e279d718e9af142ce5ed871eae7727..a3a9642171ca057be5a59dfae192803b84c501c8 100644
---- a/src/providers/ldap/sdap_utils.c
-+++ b/src/providers/ldap/sdap_utils.c
-@@ -87,6 +87,7 @@ sdap_save_all_names(const char *name,
-     int i;
-     bool lowercase = !dom->case_sensitive;
-     bool store_as_fqdn;
-+    const char **emails;
- 
-     switch (entry_type) {
-     case SYSDB_MEMBER_USER:
-@@ -143,6 +144,27 @@ sdap_save_all_names(const char *name,
- 
-     }
- 
-+    ret = sysdb_attrs_get_string_array(ldap_attrs, SYSDB_USER_EMAIL, tmp_ctx,
-+                                       &emails);
-+    if (ret == EOK) {
-+        for (i = 0; emails[i] != NULL; i++) {
-+            if (is_email_from_domain(emails[i], dom)) {
-+                ret = sysdb_attrs_add_lc_name_alias_safe(attrs, emails[i]);
-+                if (ret) {
-+                    DEBUG(SSSDBG_OP_FAILURE,
-+                          "Failed to add lower-cased version of email [%s] "
-+                          "into the alias list\n", emails[i]);
-+                    goto done;
-+                }
-+            }
-+        }
-+    } else if (ret == ENOENT) {
-+        DEBUG(SSSDBG_TRACE_ALL, "No email addresses available.\n");
-+    } else {
-+        DEBUG(SSSDBG_OP_FAILURE,
-+              "sysdb_attrs_get_string_array failed, skipping ...\n");
-+    }
-+
-     ret = EOK;
- done:
-     talloc_free(tmp_ctx);
--- 
-2.4.11
-
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
new file mode 100644
index 0000000..cbeb244
--- /dev/null
+++ b/SOURCES/0056-ssh-add-support-for-certificates-from-non-default-vi.patch
@@ -0,0 +1,52 @@
+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/0057-NSS-continue-with-UPN-email-search-if-name-was-not-f.patch b/SOURCES/0057-NSS-continue-with-UPN-email-search-if-name-was-not-f.patch
deleted file mode 100644
index 0155ae1..0000000
--- a/SOURCES/0057-NSS-continue-with-UPN-email-search-if-name-was-not-f.patch
+++ /dev/null
@@ -1,98 +0,0 @@
-From 481bd8fd5db7a92954ff351ab64ab32b4249bf19 Mon Sep 17 00:00:00 2001
-From: Sumit Bose <sbose@redhat.com>
-Date: Tue, 21 Jun 2016 11:06:19 +0200
-Subject: [PATCH 57/62] NSS: continue with UPN/email search if name was not
- found
-
-Currently we only search for UPNs if the domain part of the name was not
-know, with Kerberos aliases and email addresses we have to do this even
-if the domain name is a know domain.
-
-Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
-(cherry picked from commit 447b1da857368678990b54cd6b9cfed940357c44)
----
- src/responder/nss/nsssrv_cmd.c | 12 ++++++------
- 1 file changed, 6 insertions(+), 6 deletions(-)
-
-diff --git a/src/responder/nss/nsssrv_cmd.c b/src/responder/nss/nsssrv_cmd.c
-index 77e540d8da969614e4ab40c62e6ae1f271962f31..cd15b41886ad046d1d70dbd8ad54af5a4eccee5d 100644
---- a/src/responder/nss/nsssrv_cmd.c
-+++ b/src/responder/nss/nsssrv_cmd.c
-@@ -980,6 +980,7 @@ done:
- 
- static void nss_cmd_getby_dp_callback(uint16_t err_maj, uint32_t err_min,
-                                       const char *err_msg, void *ptr);
-+static int nss_cmd_assume_upn(struct nss_dom_ctx *dctx);
- 
- /* search for a user.
-  * Returns:
-@@ -1051,6 +1052,7 @@ static int nss_cmd_getpwnam_search(struct nss_dom_ctx *dctx)
-             /* There are no further domains or this was a
-              * fully-qualified user request.
-              */
-+
-             return ENOENT;
-         }
- 
-@@ -1144,8 +1146,6 @@ static int nss_cmd_getpwnam_search(struct nss_dom_ctx *dctx)
-                 if (dom) continue;
-             }
- 
--            DEBUG(SSSDBG_OP_FAILURE, "No results for getpwnam call\n");
--
-             /* User not found in ldb -> delete user from memory cache. */
-             ret = delete_entry_from_memcache(dctx->domain, name, nctx->rctx,
-                                              nctx->pwd_mc_ctx, SSS_MC_PASSWD);
-@@ -1163,6 +1163,8 @@ static int nss_cmd_getpwnam_search(struct nss_dom_ctx *dctx)
-                       "Deleting user from memcache failed.\n");
-             }
- 
-+            DEBUG(SSSDBG_OP_FAILURE, "No results for getpwnam call\n");
-+
-             return ENOENT;
-         }
- 
-@@ -1215,7 +1217,7 @@ static int nss_cmd_assume_upn(struct nss_dom_ctx *dctx)
- {
-     int ret;
- 
--    if (dctx->domain == NULL) {
-+    if (dctx->cmdctx->name_is_upn == false) {
-         dctx->domain = dctx->cmdctx->cctx->rctx->domains;
-         dctx->check_provider = NEED_CHECK_PROVIDER(dctx->domain->provider);
-         dctx->cmdctx->check_next = true;
-@@ -1563,6 +1565,7 @@ static int nss_cmd_getbynam(enum sss_cli_command cmd, struct cli_ctx *cctx)
- 
-     rawname = (const char *)body;
-     dctx->mc_name = rawname;
-+    dctx->rawname = rawname;
- 
-     DEBUG(SSSDBG_TRACE_FUNC, "Running command [%d][%s] with input [%s].\n",
-           cmd, sss_cmd2str(dctx->cmdctx->cmd), rawname);
-@@ -1588,7 +1591,6 @@ static int nss_cmd_getbynam(enum sss_cli_command cmd, struct cli_ctx *cctx)
-         if (req == NULL) {
-             ret = ENOMEM;
-         } else {
--            dctx->rawname = rawname;
-             tevent_req_set_callback(req, nss_cmd_getbynam_done, dctx);
-             ret = EAGAIN;
-         }
-@@ -1604,7 +1606,6 @@ static int nss_cmd_getbynam(enum sss_cli_command cmd, struct cli_ctx *cctx)
-         if (req == NULL) {
-             ret = ENOMEM;
-         } else {
--            dctx->rawname = rawname;
-             tevent_req_set_callback(req, nss_cmd_getbynam_done, dctx);
-             ret = EAGAIN;
-         }
-@@ -1626,7 +1627,6 @@ static int nss_cmd_getbynam(enum sss_cli_command cmd, struct cli_ctx *cctx)
-         }
-     } else {
-         /* this is a multidomain search */
--        dctx->rawname = rawname;
-         dctx->domain = cctx->rctx->domains;
-         cmdctx->check_next = true;
-         if (cctx->rctx->get_domains_last_call.tv_sec == 0) {
--- 
-2.4.11
-
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
new file mode 100644
index 0000000..db56ae9
--- /dev/null
+++ b/SOURCES/0057-krb5-return-to-responder-that-pkinit-is-not-availabl.patch
@@ -0,0 +1,60 @@
+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/0058-IPA-add-mapped-attributes-to-user-from-trusted-domai.patch b/SOURCES/0058-IPA-add-mapped-attributes-to-user-from-trusted-domai.patch
new file mode 100644
index 0000000..2b6616e
--- /dev/null
+++ b/SOURCES/0058-IPA-add-mapped-attributes-to-user-from-trusted-domai.patch
@@ -0,0 +1,153 @@
+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-PAM-continue-with-UPN-email-search-if-name-was-not-f.patch b/SOURCES/0058-PAM-continue-with-UPN-email-search-if-name-was-not-f.patch
deleted file mode 100644
index 3910eb3..0000000
--- a/SOURCES/0058-PAM-continue-with-UPN-email-search-if-name-was-not-f.patch
+++ /dev/null
@@ -1,90 +0,0 @@
-From d8c0b5421934cae887a44be42250d5df5631d3de Mon Sep 17 00:00:00 2001
-From: Sumit Bose <sbose@redhat.com>
-Date: Wed, 22 Jun 2016 18:21:11 +0200
-Subject: [PATCH 58/62] PAM: continue with UPN/email search if name was not
- found
-
-Currently we only search for UPNs if the domain part of the name was not
-know, with Kerberos aliases and email addresses we have to do this even
-if the domain name is a know domain.
-
-Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
-(cherry picked from commit 3381d9736b698d6111d10e219a0b5b898a4c757c)
----
- src/responder/pam/pamsrv_cmd.c | 39 +++++++++++++++++++++++++++++++++++++++
- 1 file changed, 39 insertions(+)
-
-diff --git a/src/responder/pam/pamsrv_cmd.c b/src/responder/pam/pamsrv_cmd.c
-index 3a35c3f08821aa23051989599d45b8b7b0677da4..1c759f009321cbb322fce624b506ed07f93f997b 100644
---- a/src/responder/pam/pamsrv_cmd.c
-+++ b/src/responder/pam/pamsrv_cmd.c
-@@ -924,6 +924,39 @@ static void pam_check_user_dp_callback(uint16_t err_maj, uint32_t err_min,
- static int pam_check_user_search(struct pam_auth_req *preq);
- static int pam_check_user_done(struct pam_auth_req *preq, int ret);
- 
-+static errno_t pam_cmd_assume_upn(struct pam_auth_req *preq)
-+{
-+    int ret;
-+
-+    if (!preq->pd->name_is_upn
-+            && preq->pd->logon_name != NULL
-+            && strchr(preq->pd->logon_name, '@') != NULL) {
-+        DEBUG(SSSDBG_TRACE_ALL,
-+              "No entry found so far, trying UPN/email lookup with [%s].\n",
-+              preq->pd->logon_name);
-+        /* Assuming Kerberos principal */
-+        preq->domain = preq->cctx->rctx->domains;
-+        preq->check_provider =
-+                            NEED_CHECK_PROVIDER(preq->domain->provider);
-+        preq->pd->user = talloc_strdup(preq->pd, preq->pd->logon_name);
-+        if (preq->pd->user == NULL) {
-+            DEBUG(SSSDBG_OP_FAILURE, "talloc_strdup failed.\n");
-+            return ENOMEM;
-+        }
-+        preq->pd->name_is_upn = true;
-+        preq->pd->domain = NULL;
-+
-+        ret = pam_check_user_search(preq);
-+        if (ret == EOK) {
-+            pam_dom_forwarder(preq);
-+        }
-+        return EOK;
-+    }
-+
-+    return ENOENT;
-+}
-+
-+
- /* TODO: we should probably return some sort of cookie that is set in the
-  * PAM_ENVIRONMENT, so that we can save performing some calls and cache
-  * data. */
-@@ -1220,6 +1253,8 @@ static int pam_forwarder(struct cli_ctx *cctx, int pam_cmd)
-     ret = pam_check_user_search(preq);
-     if (ret == EOK) {
-         pam_dom_forwarder(preq);
-+    } else if (ret == ENOENT) {
-+        ret = pam_cmd_assume_upn(preq);
-     }
- 
- done:
-@@ -1417,6 +1452,8 @@ static void pam_forwarder_cb(struct tevent_req *req)
-     ret = pam_check_user_search(preq);
-     if (ret == EOK) {
-         pam_dom_forwarder(preq);
-+    } else if  (ret == ENOENT) {
-+        ret = pam_cmd_assume_upn(preq);
-     }
- 
- done:
-@@ -1694,6 +1731,8 @@ static void pam_check_user_dp_callback(uint16_t err_maj, uint32_t err_min,
-         }
- 
-         pam_dom_forwarder(preq);
-+    } else if (ret == ENOENT) {
-+        ret = pam_cmd_assume_upn(preq);
-     }
- 
-     ret = pam_check_user_done(preq, ret);
--- 
-2.4.11
-
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
new file mode 100644
index 0000000..1440219
--- /dev/null
+++ b/SOURCES/0059-IPA-lookup-AD-users-by-certificates-on-IPA-clients.patch
@@ -0,0 +1,209 @@
+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-use-different-neg-cache-name-for-UPN-searches.patch b/SOURCES/0059-NSS-use-different-neg-cache-name-for-UPN-searches.patch
deleted file mode 100644
index e77eb21..0000000
--- a/SOURCES/0059-NSS-use-different-neg-cache-name-for-UPN-searches.patch
+++ /dev/null
@@ -1,59 +0,0 @@
-From e5b8922062e127d1014609df16f1909da49850bf Mon Sep 17 00:00:00 2001
-From: Sumit Bose <sbose@redhat.com>
-Date: Fri, 22 Jul 2016 16:01:38 +0200
-Subject: [PATCH 59/62] NSS: use different neg cache name for UPN searches
-
-If Kerberos principals or email address have the same domain suffix as
-the domain itself the first user lookup by name might have already added
-the name to the negative cache and the second lookup by UPN/email will
-skip the domain because of the neg cache entry. To avoid this a special
-name with a '@' prefix is used here.
-
-Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
-(cherry picked from commit 62df78512145db94b51c5573d4df1737197e368a)
----
- src/responder/nss/nsssrv_cmd.c | 12 ++++++++++--
- 1 file changed, 10 insertions(+), 2 deletions(-)
-
-diff --git a/src/responder/nss/nsssrv_cmd.c b/src/responder/nss/nsssrv_cmd.c
-index cd15b41886ad046d1d70dbd8ad54af5a4eccee5d..f3b6ac4afb5d1571f283933b48e0256b91c56391 100644
---- a/src/responder/nss/nsssrv_cmd.c
-+++ b/src/responder/nss/nsssrv_cmd.c
-@@ -1002,6 +1002,7 @@ static int nss_cmd_getpwnam_search(struct nss_dom_ctx *dctx)
-     struct ldb_message *msg;
-     const char *extra_flag = NULL;
-     const char *sysdb_name;
-+    char *neg_cache_name;
- 
-     nctx = talloc_get_type(cctx->rctx->pvt_ctx, struct nss_ctx);
- 
-@@ -1031,9 +1032,15 @@ static int nss_cmd_getpwnam_search(struct nss_dom_ctx *dctx)
-             return ENOMEM;
-         }
- 
-+        if (cmdctx->name_is_upn) {
-+            neg_cache_name = talloc_asprintf(name, "@%s", name);
-+        } else {
-+            neg_cache_name = name;
-+        }
-+
-         /* verify this user has not yet been negatively cached,
-         * or has been permanently filtered */
--        ret = sss_ncache_check_user(nctx->rctx->ncache, dom, name);
-+        ret = sss_ncache_check_user(nctx->rctx->ncache, dom, neg_cache_name);
- 
-         /* if neg cached, return we didn't find it */
-         if (ret == EEXIST) {
-@@ -1130,7 +1137,8 @@ static int nss_cmd_getpwnam_search(struct nss_dom_ctx *dctx)
- 
-         if (dctx->res->count == 0 && !dctx->check_provider) {
-             /* set negative cache only if not result of cache check */
--            ret = sss_ncache_set_user(nctx->rctx->ncache, false, dom, name);
-+            ret = sss_ncache_set_user(nctx->rctx->ncache, false, dom,
-+                                      neg_cache_name);
-             if (ret != EOK) {
-                 DEBUG(SSSDBG_MINOR_FAILURE, "Cannot set negcache for %s\n",
-                       name);
--- 
-2.4.11
-
diff --git a/SOURCES/0060-IPA-enable-AD-user-lookup-by-certificate.patch b/SOURCES/0060-IPA-enable-AD-user-lookup-by-certificate.patch
new file mode 100644
index 0000000..e6ed62e
--- /dev/null
+++ b/SOURCES/0060-IPA-enable-AD-user-lookup-by-certificate.patch
@@ -0,0 +1,30 @@
+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-PAM-Fix-domain-for-UPN-based-lookups.patch b/SOURCES/0060-PAM-Fix-domain-for-UPN-based-lookups.patch
deleted file mode 100644
index ca64d2b..0000000
--- a/SOURCES/0060-PAM-Fix-domain-for-UPN-based-lookups.patch
+++ /dev/null
@@ -1,52 +0,0 @@
-From 3467754b1e32e648b3013244dcbac51677a089eb Mon Sep 17 00:00:00 2001
-From: Sumit Bose <sbose@redhat.com>
-Date: Fri, 22 Jul 2016 17:34:20 +0200
-Subject: [PATCH 60/62] PAM: Fix domain for UPN based lookups
-
-Since sysdb_search_user_by_upn() searches the whole cache we have to set
-the domain so that it matches the result.
-
-Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
-(cherry picked from commit 9b8fcf685c5ca70a5067a621385bcdc8d9fd6469)
----
- src/responder/pam/pamsrv_cmd.c | 17 +++++++++++++++++
- 1 file changed, 17 insertions(+)
-
-diff --git a/src/responder/pam/pamsrv_cmd.c b/src/responder/pam/pamsrv_cmd.c
-index 1c759f009321cbb322fce624b506ed07f93f997b..66564f5d301a53dcdb5967f43ef4afdb897e9974 100644
---- a/src/responder/pam/pamsrv_cmd.c
-+++ b/src/responder/pam/pamsrv_cmd.c
-@@ -1474,6 +1474,7 @@ static int pam_check_user_search(struct pam_auth_req *preq)
-     static const char *user_attrs[] = SYSDB_PW_ATTRS;
-     struct ldb_message *msg;
-     struct ldb_result *res;
-+    const char *sysdb_name;
- 
-     while (dom) {
-        /* if it is a domainless search, skip domains that require fully
-@@ -1533,6 +1534,22 @@ static int pam_check_user_search(struct pam_auth_req *preq)
- 
-         if (preq->pd->name_is_upn) {
-             ret = sysdb_search_user_by_upn(preq, dom, name, user_attrs, &msg);
-+
-+            /* Since sysdb_search_user_by_upn() searches the whole cache we
-+             * have to set the domain so that it matches the result. */
-+            sysdb_name = ldb_msg_find_attr_as_string(msg, SYSDB_NAME, NULL);
-+            if (sysdb_name == NULL) {
-+                DEBUG(SSSDBG_CRIT_FAILURE, "Cached entry has no name.\n");
-+                return EINVAL;
-+            }
-+            preq->domain = find_domain_by_object_name(get_domains_head(dom),
-+                                                      sysdb_name);
-+            if (preq->domain == NULL) {
-+                DEBUG(SSSDBG_CRIT_FAILURE,
-+                      "Cannot find matching domain for [%s].\n",
-+                      sysdb_name);
-+                return EINVAL;
-+            }
-         } else {
-             ret = sysdb_getpwnam_with_views(preq, dom, name, &res);
-             if (res->count > 1) {
--- 
-2.4.11
-
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
new file mode 100644
index 0000000..31f541e
--- /dev/null
+++ b/SOURCES/0061-CONFDB-Introduce-SSSD-domain-type-to-distinguish-POS.patch
@@ -0,0 +1,242 @@
+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-SDAP-add-special-handling-for-IPA-Kerberos-enterpris.patch b/SOURCES/0061-SDAP-add-special-handling-for-IPA-Kerberos-enterpris.patch
deleted file mode 100644
index 13145e0..0000000
--- a/SOURCES/0061-SDAP-add-special-handling-for-IPA-Kerberos-enterpris.patch
+++ /dev/null
@@ -1,56 +0,0 @@
-From c2fe77b2277513d01b56dc26391e8e7cfcbe7429 Mon Sep 17 00:00:00 2001
-From: Sumit Bose <sbose@redhat.com>
-Date: Fri, 22 Jul 2016 12:20:33 +0200
-Subject: [PATCH 61/62] SDAP: add special handling for IPA Kerberos enterprise
- principal strings
-
-Unfortunately principal aliases with an alternative realm are stored in
-IPA as the string representation of an enterprise principal, i.e.
-name\@alt.realm@IPA.REALM. To allow searches with the plain alias
-'name@alt.realm' the returned value is converted before it is saved to
-the cache.
-
-Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
-(cherry picked from commit 15694ca762f61a414f0017c57ed97a8d57456b80)
----
- src/providers/ldap/sdap_async_users.c | 17 +++++++++++++++++
- 1 file changed, 17 insertions(+)
-
-diff --git a/src/providers/ldap/sdap_async_users.c b/src/providers/ldap/sdap_async_users.c
-index 28101a2d8a38f97d09d50a9f7e071a030b4f9719..cccd2506b3e1849101a8a06c39fe6cab263777b6 100644
---- a/src/providers/ldap/sdap_async_users.c
-+++ b/src/providers/ldap/sdap_async_users.c
-@@ -143,6 +143,8 @@ int sdap_save_user(TALLOC_CTX *memctx,
-     char *dom_sid_str = NULL;
-     struct sss_domain_info *subdomain;
-     size_t c;
-+    char *p1;
-+    char *p2;
- 
-     DEBUG(SSSDBG_TRACE_FUNC, "Save user\n");
- 
-@@ -448,6 +450,21 @@ int sdap_save_user(TALLOC_CTX *memctx,
-                 goto done;
-             }
- 
-+            /* Check for IPA Kerberos enterprise principal strings
-+             * 'user\@my.realm@IPA.REALM' and use 'user@my.realm' */
-+            if ( (p1 = strchr(upn,'\\')) != NULL
-+                    && *(p1 + 1) == '@'
-+                    && (p2 = strchr(p1 + 2, '@')) != NULL) {
-+                *p1 = '\0';
-+                *p2 = '\0';
-+                upn = talloc_asprintf(tmpctx, "%s%s", upn, p1 + 1);
-+                if (upn == NULL) {
-+                    DEBUG(SSSDBG_OP_FAILURE, "talloc_asprintf failed.\n");
-+                    ret = ENOMEM;
-+                    goto done;
-+                }
-+            }
-+
-             if (dp_opt_get_bool(opts->basic, SDAP_FORCE_UPPER_CASE_REALM)) {
-                 make_realm_upper_case(upn);
-             }
--- 
-2.4.11
-
diff --git a/SOURCES/0062-CONFDB-Allow-configuring-application-sections-as-non.patch b/SOURCES/0062-CONFDB-Allow-configuring-application-sections-as-non.patch
new file mode 100644
index 0000000..24ecd56
--- /dev/null
+++ b/SOURCES/0062-CONFDB-Allow-configuring-application-sections-as-non.patch
@@ -0,0 +1,531 @@
+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-SDAP-add-enterprise-principal-strings-for-user-searc.patch b/SOURCES/0062-SDAP-add-enterprise-principal-strings-for-user-searc.patch
deleted file mode 100644
index 2077c69..0000000
--- a/SOURCES/0062-SDAP-add-enterprise-principal-strings-for-user-searc.patch
+++ /dev/null
@@ -1,201 +0,0 @@
-From 0274cb7aa22e388e46580b288a7dd957ad955e04 Mon Sep 17 00:00:00 2001
-From: Sumit Bose <sbose@redhat.com>
-Date: Fri, 22 Jul 2016 20:10:42 +0200
-Subject: [PATCH 62/62] SDAP: add enterprise principal strings for user
- searches
-
-Unfortunately principal aliases with an alternative realm are stored in
-IPA as the string representation of an enterprise principal, i.e.
-name\@alt.realm@IPA.REALM. To be able to lookup the alternative
-principal in LDAP properly the UPN search filter is extended to search
-for this type of name as well.
-
-Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
-(cherry picked from commit 50a7a92f92e1584702bf25e61a50cb1c09c7e260)
----
- src/providers/ldap/ldap_common.h           |  5 +++++
- src/providers/ldap/ldap_id.c               | 10 +++++++--
- src/providers/ldap/sdap_async_initgroups.c |  9 ++++++--
- src/providers/ldap/sdap_utils.c            | 28 ++++++++++++++++++++++++
- src/tests/cmocka/test_nested_groups.c      | 34 ++++++++++++++++++++++++++++++
- 5 files changed, 82 insertions(+), 4 deletions(-)
-
-diff --git a/src/providers/ldap/ldap_common.h b/src/providers/ldap/ldap_common.h
-index b39f6789275cf49dd69068ae3de0628b582e4cc5..acdcf47cc5992609cdbf73e4ed9655eade55e214 100644
---- a/src/providers/ldap/ldap_common.h
-+++ b/src/providers/ldap/ldap_common.h
-@@ -300,6 +300,11 @@ char *sdap_combine_filters(TALLOC_CTX *mem_ctx,
-                            const char *base_filter,
-                            const char *extra_filter);
- 
-+char *get_enterprise_principal_string_filter(TALLOC_CTX *mem_ctx,
-+                                             const char *attr_name,
-+                                             const char *princ,
-+                                             struct dp_option *sdap_basic_opts);
-+
- 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 5b303ddbd46fd44646cdd50856c784640426ee25..beb31fba16be76ba2ac01f99b87ee6362704f417 100644
---- a/src/providers/ldap/ldap_id.c
-+++ b/src/providers/ldap/ldap_id.c
-@@ -89,6 +89,7 @@ struct tevent_req *users_get_send(TALLOC_CTX *memctx,
-     enum idmap_error_code err;
-     char *sid;
-     char *user_filter = NULL;
-+    char *ep_filter;
- 
-     req = tevent_req_create(memctx, &state, struct users_get_state);
-     if (!req) return NULL;
-@@ -131,12 +132,17 @@ struct tevent_req *users_get_send(TALLOC_CTX *memctx,
-             if (ret != EOK) {
-                 goto done;
-             }
-+
-+            ep_filter = get_enterprise_principal_string_filter(state,
-+                                   ctx->opts->user_map[SDAP_AT_USER_PRINC].name,
-+                                   clean_value, ctx->opts->basic);
-             /* TODO: Do we have to check the attribute names more carefully? */
--            user_filter = talloc_asprintf(state, "(|(%s=%s)(%s=%s))",
-+            user_filter = talloc_asprintf(state, "(|(%s=%s)(%s=%s)%s)",
-                                    ctx->opts->user_map[SDAP_AT_USER_PRINC].name,
-                                    clean_value,
-                                    ctx->opts->user_map[SDAP_AT_USER_EMAIL].name,
--                                   clean_value);
-+                                   clean_value,
-+                                   ep_filter == NULL ? "" : ep_filter);
-             talloc_zfree(clean_value);
-             if (user_filter == NULL) {
-                 DEBUG(SSSDBG_OP_FAILURE, "talloc_asprintf failed.\n");
-diff --git a/src/providers/ldap/sdap_async_initgroups.c b/src/providers/ldap/sdap_async_initgroups.c
-index 0a42b18662a8fe12cf048aadfef257b5d9cb48a3..7029427724cc37a4508e11ef5448b421e94dc787 100644
---- a/src/providers/ldap/sdap_async_initgroups.c
-+++ b/src/providers/ldap/sdap_async_initgroups.c
-@@ -2682,7 +2682,8 @@ struct tevent_req *sdap_get_initgr_send(TALLOC_CTX *memctx,
-     int ret;
-     char *clean_name;
-     bool use_id_mapping;
--    const char *search_attr;
-+    const char *search_attr = NULL;
-+    char *ep_filter;
- 
-     DEBUG(SSSDBG_TRACE_ALL, "Retrieving info for initgroups call\n");
- 
-@@ -2743,13 +2744,17 @@ struct tevent_req *sdap_get_initgr_send(TALLOC_CTX *memctx,
-                 return NULL;
-             }
- 
-+            ep_filter = get_enterprise_principal_string_filter(state,
-+                                 state->opts->user_map[SDAP_AT_USER_PRINC].name,
-+                                 clean_name, state->opts->basic);
-             state->user_base_filter =
-                     talloc_asprintf(state,
--                                 "(&(|(%s=%s)(%s=%s))(objectclass=%s)",
-+                                 "(&(|(%s=%s)(%s=%s)%s)(objectclass=%s)",
-                                  state->opts->user_map[SDAP_AT_USER_PRINC].name,
-                                  clean_name,
-                                  state->opts->user_map[SDAP_AT_USER_EMAIL].name,
-                                  clean_name,
-+                                 ep_filter == NULL ? "" : ep_filter,
-                                  state->opts->user_map[SDAP_OC_USER].name);
-             if (state->user_base_filter == NULL) {
-                 talloc_zfree(req);
-diff --git a/src/providers/ldap/sdap_utils.c b/src/providers/ldap/sdap_utils.c
-index a3a9642171ca057be5a59dfae192803b84c501c8..0ac3ab2e416d887d00480b5123859c611f514274 100644
---- a/src/providers/ldap/sdap_utils.c
-+++ b/src/providers/ldap/sdap_utils.c
-@@ -227,3 +227,31 @@ char *sdap_combine_filters(TALLOC_CTX *mem_ctx,
- {
-     return sdap_combine_filters_ex(mem_ctx, '&', base_filter, extra_filter);
- }
-+
-+char *get_enterprise_principal_string_filter(TALLOC_CTX *mem_ctx,
-+                                             const char *attr_name,
-+                                             const char *princ,
-+                                             struct dp_option *sdap_basic_opts)
-+{
-+    const char *realm;
-+    char *p;
-+
-+    if (attr_name == NULL || princ == NULL || sdap_basic_opts == NULL) {
-+        return NULL;
-+    }
-+
-+    realm = dp_opt_get_cstring(sdap_basic_opts, SDAP_KRB5_REALM);
-+    if (realm == NULL) {
-+        return NULL;
-+    }
-+
-+    p = strchr(princ, '@');
-+    if (p == NULL) {
-+        return NULL;
-+    }
-+
-+    return talloc_asprintf(mem_ctx, "(%s=%.*s\\\\@%s@%s)", attr_name,
-+                                                           (int) (p - princ),
-+                                                           princ,
-+                                                           p + 1, realm);
-+}
-diff --git a/src/tests/cmocka/test_nested_groups.c b/src/tests/cmocka/test_nested_groups.c
-index 6af7e1f4393992e7f16d72b86e40664487896ea1..c8e80f29fb65f8f8935fea32cd4bf3e16de7d06f 100644
---- a/src/tests/cmocka/test_nested_groups.c
-+++ b/src/tests/cmocka/test_nested_groups.c
-@@ -31,6 +31,7 @@
- #include "providers/ldap/sdap.h"
- #include "providers/ldap/sdap_idmap.h"
- #include "providers/ldap/sdap_async_private.h"
-+#include "providers/ldap/ldap_opts.h"
- 
- #define TESTS_PATH "tp_" BASE_FILE_STEM
- #define TEST_CONF_DB "test_ldap_nested_groups_conf.ldb"
-@@ -1242,6 +1243,38 @@ static void nested_group_external_member_test(void **state)
-                      nested_group.gr_name);
- }
- 
-+static void test_get_enterprise_principal_string_filter(void **state)
-+{
-+    int ret;
-+    char *ep_filter;
-+    struct dp_option *no_krb5_realm_opt = default_basic_opts;
-+
-+    struct dp_option *krb5_realm_opt;
-+
-+    ret = dp_copy_defaults(NULL, default_basic_opts, SDAP_OPTS_BASIC,
-+                           &krb5_realm_opt);
-+    assert_int_equal(ret, EOK);
-+
-+    ret = dp_opt_set_string(krb5_realm_opt, SDAP_KRB5_REALM, "TEST.DOM");
-+    assert_int_equal(ret, EOK);
-+
-+    ep_filter = get_enterprise_principal_string_filter(NULL, NULL, NULL, NULL);
-+    assert_null(ep_filter);
-+
-+    ep_filter = get_enterprise_principal_string_filter(NULL, "aBC", "p@d.c",
-+                                                       no_krb5_realm_opt);
-+    assert_null(ep_filter);
-+
-+    ep_filter = get_enterprise_principal_string_filter(NULL, "aBC", "p",
-+                                                       krb5_realm_opt);
-+    assert_null(ep_filter);
-+
-+    ep_filter = get_enterprise_principal_string_filter(NULL, "aBC", "p@d.c",
-+                                                       krb5_realm_opt);
-+    assert_non_null(ep_filter);
-+    assert_string_equal(ep_filter, "(aBC=p\\\\@d.c@TEST.DOM)");
-+    talloc_free(ep_filter);
-+}
- 
- int main(int argc, const char *argv[])
- {
-@@ -1268,6 +1301,7 @@ int main(int argc, const char *argv[])
-         cmocka_unit_test_setup_teardown(nested_group_external_member_test,
-                                         nested_group_external_member_setup,
-                                         nested_group_external_member_teardown),
-+        cmocka_unit_test(test_get_enterprise_principal_string_filter),
-     };
- 
-     /* Set debug level to invalid value so we can deside if -d 0 was used. */
--- 
-2.4.11
-
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
new file mode 100644
index 0000000..86b6c73
--- /dev/null
+++ b/SOURCES/0063-CACHE_REQ-Domain-type-selection-in-cache_req.patch
@@ -0,0 +1,976 @@
+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-LDAP-Fix-storing-initgroups-for-users-with-no-supple.patch b/SOURCES/0063-LDAP-Fix-storing-initgroups-for-users-with-no-supple.patch
deleted file mode 100644
index ea50e9c..0000000
--- a/SOURCES/0063-LDAP-Fix-storing-initgroups-for-users-with-no-supple.patch
+++ /dev/null
@@ -1,70 +0,0 @@
-From e84b8e754f007cd94d9b535d562d8d315f692e8f Mon Sep 17 00:00:00 2001
-From: Jakub Hrozek <jhrozek@redhat.com>
-Date: Fri, 8 Jul 2016 13:19:31 +0200
-Subject: [PATCH 63/74] LDAP: Fix storing initgroups for users with no
- supplementary groups
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-If there are no supplementary groups, we tried to qualify a NULL pointer
-to an array which resulted in an error.
-
-Reviewed-by: Lukáš Slebodník <lslebodn@redhat.com>
----
- src/providers/ldap/sdap_async_initgroups.c | 32 +++++++++++++++++-------------
- 1 file changed, 18 insertions(+), 14 deletions(-)
-
-diff --git a/src/providers/ldap/sdap_async_initgroups.c b/src/providers/ldap/sdap_async_initgroups.c
-index 7029427724cc37a4508e11ef5448b421e94dc787..cc63dff781338e33a9802f97d98174fce2167b4b 100644
---- a/src/providers/ldap/sdap_async_initgroups.c
-+++ b/src/providers/ldap/sdap_async_initgroups.c
-@@ -301,13 +301,15 @@ int sdap_initgr_common_store(struct sysdb_ctx *sysdb,
-     /* Find the differences between the sysdb and LDAP lists
-      * Groups in the sysdb only must be removed.
-      */
--    ldap_fqdnlist = sss_create_internal_fqname_list(
--                                        tmp_ctx,
--                                        (const char * const *) ldap_grouplist,
--                                        domain->name);
--    if (ldap_fqdnlist == NULL) {
--        ret = ENOMEM;
--        goto done;
-+    if (ldap_grouplist != NULL) {
-+        ldap_fqdnlist = sss_create_internal_fqname_list(
-+                                            tmp_ctx,
-+                                            (const char * const *) ldap_grouplist,
-+                                            domain->name);
-+        if (ldap_fqdnlist == NULL) {
-+            ret = ENOMEM;
-+            goto done;
-+        }
-     }
- 
-     ret = diff_string_lists(tmp_ctx, ldap_fqdnlist, sysdb_grouplist,
-@@ -1288,13 +1290,15 @@ sdap_initgr_store_user_memberships(struct sdap_initgr_nested_state *state)
-         }
-     }
- 
--    ldap_fqdnlist = sss_create_internal_fqname_list(
--                                tmp_ctx,
--                                (const char * const *) ldap_parent_name_list,
--                                state->dom->name);
--    if (ldap_fqdnlist == NULL) {
--        ret = ENOMEM;
--        goto done;
-+    if (ldap_parent_name_list) {
-+        ldap_fqdnlist = sss_create_internal_fqname_list(
-+                                  tmp_ctx,
-+                                  (const char * const *) ldap_parent_name_list,
-+                                  state->dom->name);
-+        if (ldap_fqdnlist == NULL) {
-+            ret = ENOMEM;
-+            goto done;
-+        }
-     }
- 
-     ret = sysdb_get_direct_parents(tmp_ctx, state->dom, SYSDB_MEMBER_USER,
--- 
-2.4.11
-
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
new file mode 100644
index 0000000..c956b23
--- /dev/null
+++ b/SOURCES/0064-IFP-Search-both-POSIX-and-non-POSIX-domains.patch
@@ -0,0 +1,705 @@
+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-LDAP-Changing-of-confusing-debug-message.patch b/SOURCES/0064-LDAP-Changing-of-confusing-debug-message.patch
deleted file mode 100644
index 915b84e..0000000
--- a/SOURCES/0064-LDAP-Changing-of-confusing-debug-message.patch
+++ /dev/null
@@ -1,31 +0,0 @@
-From 256f6eafd5ca3cf1ea0a573ec44b3e5e4b74919c Mon Sep 17 00:00:00 2001
-From: Petr Cech <pcech@redhat.com>
-Date: Tue, 12 Jul 2016 09:32:44 +0200
-Subject: [PATCH 64/74] LDAP: Changing of confusing debug message
-
-This debug message used to confuse our customer. So this patch changes it.
-
-Resolves:
-https://fedorahosted.org/sssd/ticket/3091
-
-Reviewed-by: Stephen Gallagher <sgallagh@redhat.com>
----
- src/providers/ldap/sdap_async.c | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
-diff --git a/src/providers/ldap/sdap_async.c b/src/providers/ldap/sdap_async.c
-index 0c67e54c8a981442b7983a3f68db1bde3a2a6280..4195ba95d911f3956f8cca665310b4b92091e6cd 100644
---- a/src/providers/ldap/sdap_async.c
-+++ b/src/providers/ldap/sdap_async.c
-@@ -167,7 +167,7 @@ static void sdap_process_result(struct tevent_context *ev, void *pvt)
-     if (ret == 0) {
-         /* this almost always means we have reached the end of
-          * the list of received messages */
--        DEBUG(SSSDBG_TRACE_INTERNAL, "Trace: ldap_result found nothing!\n");
-+        DEBUG(SSSDBG_TRACE_INTERNAL, "Trace: end of ldap_result list\n");
-         return;
-     }
- 
--- 
-2.4.11
-
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
new file mode 100644
index 0000000..f062ffa
--- /dev/null
+++ b/SOURCES/0065-IFP-ListByName-Don-t-crash-when-no-results-are-found.patch
@@ -0,0 +1,57 @@
+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-LDAP-Use-FQDN-when-linking-parent-LDAP-groups.patch b/SOURCES/0065-LDAP-Use-FQDN-when-linking-parent-LDAP-groups.patch
deleted file mode 100644
index d381162..0000000
--- a/SOURCES/0065-LDAP-Use-FQDN-when-linking-parent-LDAP-groups.patch
+++ /dev/null
@@ -1,35 +0,0 @@
-From 8798dbf0af2850c5775e0d50165d70b17a031050 Mon Sep 17 00:00:00 2001
-From: Jakub Hrozek <jhrozek@redhat.com>
-Date: Wed, 3 Aug 2016 13:18:51 +0200
-Subject: [PATCH 65/74] LDAP: Use FQDN when linking parent LDAP groups
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Resolves:
-    https://fedorahosted.org/sssd/ticket/3093
-
-Because we compare the list of LDAP names with the list of sysdb names,
-we need to qualify the list of LDAP names before running the diff.
-
-Reviewed-by: Lukáš Slebodník <lslebodn@redhat.com>
----
- src/providers/ldap/sdap_async_initgroups.c | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
-diff --git a/src/providers/ldap/sdap_async_initgroups.c b/src/providers/ldap/sdap_async_initgroups.c
-index cc63dff781338e33a9802f97d98174fce2167b4b..82c708c226bf1a645ff5a395947dfdbad71e0f1f 100644
---- a/src/providers/ldap/sdap_async_initgroups.c
-+++ b/src/providers/ldap/sdap_async_initgroups.c
-@@ -2080,7 +2080,7 @@ rfc2307bis_group_memberships_build(hash_entry_t *item, void *user_data)
-     }
- 
-     if (group->parents_count > 0) {
--        ret = sysdb_attrs_primary_name_list(mstate->dom, tmp_ctx,
-+        ret = sysdb_attrs_primary_fqdn_list(mstate->dom, tmp_ctx,
-                             group->ldap_parents, group->parents_count,
-                             mstate->opts->group_map[SDAP_AT_GROUP_NAME].name,
-                             &ldap_parents_names_list);
--- 
-2.4.11
-
diff --git a/SOURCES/0066-PAM-Remove-unneeded-memory-context.patch b/SOURCES/0066-PAM-Remove-unneeded-memory-context.patch
new file mode 100644
index 0000000..64ba6af
--- /dev/null
+++ b/SOURCES/0066-PAM-Remove-unneeded-memory-context.patch
@@ -0,0 +1,58 @@
+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/0066-sssctl-Consistent-commands-naming.patch b/SOURCES/0066-sssctl-Consistent-commands-naming.patch
deleted file mode 100644
index 35bd2e2..0000000
--- a/SOURCES/0066-sssctl-Consistent-commands-naming.patch
+++ /dev/null
@@ -1,330 +0,0 @@
-From 6f36e763802e35ff2c431e67be20664e221de85f Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Michal=20=C5=BDidek?= <mzidek@redhat.com>
-Date: Mon, 25 Jul 2016 13:50:13 +0200
-Subject: [PATCH 66/74] sssctl: Consistent commands naming
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Use TOPIC-ACTION pattern for sssctl command
-names.
-
-Resolves:
-https://fedorahosted.org/sssd/ticket/3087
-
-Reviewed-by: Pavel Březina <pbrezina@redhat.com>
-Reviewed-by: Lukáš Slebodník <lslebodn@redhat.com>
----
- src/tools/common/sss_tools.h      |  2 ++
- src/tools/sss_override.c          | 26 +++++++++++------------
- src/tools/sssctl/sssctl.c         | 22 ++++++++++----------
- src/tools/sssctl/sssctl.h         | 43 +++++++++++++++++++--------------------
- src/tools/sssctl/sssctl_cache.c   | 18 ++++++++--------
- src/tools/sssctl/sssctl_data.c    | 16 +++++++--------
- src/tools/sssctl/sssctl_domains.c |  6 +++---
- src/tools/sssctl/sssctl_logs.c    |  4 ++--
- 8 files changed, 69 insertions(+), 68 deletions(-)
-
-diff --git a/src/tools/common/sss_tools.h b/src/tools/common/sss_tools.h
-index a9ebabe21df14eba52f348046c179a91a6394b97..41c8b10e8c2dd8d6a86297b42a71dd5aaf9e556a 100644
---- a/src/tools/common/sss_tools.h
-+++ b/src/tools/common/sss_tools.h
-@@ -46,7 +46,9 @@ typedef errno_t
-                 void *pvt);
- 
- #define SSS_TOOL_COMMAND(cmd, msg, err, fn) {cmd, _(msg), err, fn}
-+#define SSS_TOOL_COMMAND_NOMSG(cmd, err, fn) {cmd, NULL, err, fn}
- #define SSS_TOOL_DELIMITER(message) {"", (message), 0, NULL}
-+#define SSS_TOOL_LAST {NULL, NULL, 0, NULL}
- 
- struct sss_route_cmd {
-     const char *command;
-diff --git a/src/tools/sss_override.c b/src/tools/sss_override.c
-index 45a28fd7f5366cd85e6ecd13a4846917d18e9613..d41da52e69acdb67b5a6d624254e3b89a8aa27b8 100644
---- a/src/tools/sss_override.c
-+++ b/src/tools/sss_override.c
-@@ -1913,19 +1913,19 @@ static int override_group_export(struct sss_cmdline *cmdline,
- int main(int argc, const char **argv)
- {
-     struct sss_route_cmd commands[] = {
--        {"user-add", NULL, 0, override_user_add},
--        {"user-del", NULL, 0, override_user_del},
--        {"user-find", NULL, 0, override_user_find},
--        {"user-show", NULL, 0, override_user_show},
--        {"user-import", NULL, 0, override_user_import},
--        {"user-export", NULL, 0, override_user_export},
--        {"group-add", NULL, 0, override_group_add},
--        {"group-del", NULL, 0, override_group_del},
--        {"group-find", NULL, 0, override_group_find},
--        {"group-show", NULL, 0, override_group_show},
--        {"group-import", NULL, 0, override_group_import},
--        {"group-export", NULL, 0, override_group_export},
--        {NULL, NULL, 0, NULL}
-+        SSS_TOOL_COMMAND_NOMSG("user-add", 0, override_user_add),
-+        SSS_TOOL_COMMAND_NOMSG("user-del", 0, override_user_del),
-+        SSS_TOOL_COMMAND_NOMSG("user-find", 0, override_user_find),
-+        SSS_TOOL_COMMAND_NOMSG("user-show", 0, override_user_show),
-+        SSS_TOOL_COMMAND_NOMSG("user-import", 0, override_user_import),
-+        SSS_TOOL_COMMAND_NOMSG("user-export", 0, override_user_export),
-+        SSS_TOOL_COMMAND_NOMSG("group-add", 0, override_group_add),
-+        SSS_TOOL_COMMAND_NOMSG("group-del", 0, override_group_del),
-+        SSS_TOOL_COMMAND_NOMSG("group-find", 0, override_group_find),
-+        SSS_TOOL_COMMAND_NOMSG("group-show", 0, override_group_show),
-+        SSS_TOOL_COMMAND_NOMSG("group-import", 0, override_group_import),
-+        SSS_TOOL_COMMAND_NOMSG("group-export", 0, override_group_export),
-+        SSS_TOOL_LAST
-     };
- 
-     return sss_tool_main(argc, argv, commands, NULL);
-diff --git a/src/tools/sssctl/sssctl.c b/src/tools/sssctl/sssctl.c
-index 86656f136c00234a230b8fc779a0d245f0d125d5..20ea26f8f71c26a925f5c37327635e346f497891 100644
---- a/src/tools/sssctl/sssctl.c
-+++ b/src/tools/sssctl/sssctl.c
-@@ -257,25 +257,25 @@ int main(int argc, const char **argv)
- {
-     struct sss_route_cmd commands[] = {
-         SSS_TOOL_DELIMITER("SSSD Status:"),
--        SSS_TOOL_COMMAND("list-domains", "List available domains", 0, sssctl_list_domains),
-+        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_DELIMITER("Information about cached content:"),
--        SSS_TOOL_COMMAND("user", "Information about cached user", 0, sssctl_user),
--        SSS_TOOL_COMMAND("group", "Information about cached group", 0, sssctl_group),
--        SSS_TOOL_COMMAND("netgroup", "Information about cached netgroup", 0, sssctl_netgroup),
-+        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),
-+        SSS_TOOL_COMMAND("netgroup-show", "Information about cached netgroup", 0, sssctl_netgroup_show),
-         SSS_TOOL_DELIMITER("Local data tools:"),
--        SSS_TOOL_COMMAND("backup-local-data", "Backup local data", 0, sssctl_backup_local_data),
--        SSS_TOOL_COMMAND("restore-local-data", "Restore local data from backup", 0, sssctl_restore_local_data),
--        SSS_TOOL_COMMAND("remove-cache", "Backup local data and remove cached content", 0, sssctl_remove_cache),
--        SSS_TOOL_COMMAND("upgrade-cache", "Perform cache upgrade", ERR_SYSDB_VERSION_TOO_OLD, sssctl_upgrade_cache),
-+        SSS_TOOL_COMMAND("client-data-backup", "Backup local data", 0, sssctl_client_data_backup),
-+        SSS_TOOL_COMMAND("client-data-restore", "Restore local data from backup", 0, sssctl_client_data_restore),
-+        SSS_TOOL_COMMAND("cache-remove", "Backup local data and remove cached content", 0, sssctl_cache_remove),
-+        SSS_TOOL_COMMAND("cache-upgrade", "Perform cache upgrade", ERR_SYSDB_VERSION_TOO_OLD, sssctl_cache_upgrade),
-         SSS_TOOL_DELIMITER("Log files tools:"),
--        SSS_TOOL_COMMAND("remove-logs", "Remove existing SSSD log files", 0, sssctl_remove_logs),
--        SSS_TOOL_COMMAND("fetch-logs", "Archive SSSD log files in tarball", 0, sssctl_fetch_logs),
-+        SSS_TOOL_COMMAND("logs-remove", "Remove existing SSSD log files", 0, sssctl_logs_remove),
-+        SSS_TOOL_COMMAND("logs-fetch", "Archive SSSD log files in tarball", 0, sssctl_logs_fetch),
- #ifdef HAVE_LIBINI_CONFIG_V1_3
-         SSS_TOOL_DELIMITER("Configuration files tools:"),
-         SSS_TOOL_COMMAND("config-check", "Perform static analysis of SSSD configuration", 0, sssctl_config_check),
- #endif
--        {NULL, NULL, 0, NULL}
-+        SSS_TOOL_LAST
-     };
- 
-     return sss_tool_main(argc, argv, commands, NULL);
-diff --git a/src/tools/sssctl/sssctl.h b/src/tools/sssctl/sssctl.h
-index be624755de531df9ff5a97f5640266801695ef6e..72930ee5c3a1195e90c6e35768f715cbf6a1c4e1 100644
---- a/src/tools/sssctl/sssctl.h
-+++ b/src/tools/sssctl/sssctl.h
-@@ -56,52 +56,51 @@ void _sssctl_sifp_error(sss_sifp_ctx *sifp,
- #define sssctl_sifp_error(sifp, error, message) \
-     _sssctl_sifp_error(sifp, error, _(message))
- 
--errno_t sssctl_list_domains(struct sss_cmdline *cmdline,
--                            struct sss_tool_ctx *tool_ctx,
--                            void *pvt);
-+errno_t sssctl_domain_list(struct sss_cmdline *cmdline,
-+                           struct sss_tool_ctx *tool_ctx,
-+                           void *pvt);
- 
- errno_t sssctl_domain_status(struct sss_cmdline *cmdline,
-                              struct sss_tool_ctx *tool_ctx,
-                              void *pvt);
- 
--errno_t sssctl_backup_local_data(struct sss_cmdline *cmdline,
--                                 struct sss_tool_ctx *tool_ctx,
--                                 void *pvt);
--
--errno_t sssctl_restore_local_data(struct sss_cmdline *cmdline,
-+errno_t sssctl_client_data_backup(struct sss_cmdline *cmdline,
-                                   struct sss_tool_ctx *tool_ctx,
-                                   void *pvt);
- 
--errno_t sssctl_remove_cache(struct sss_cmdline *cmdline,
-+errno_t sssctl_client_data_restore(struct sss_cmdline *cmdline,
-+                                   struct sss_tool_ctx *tool_ctx,
-+                                   void *pvt);
-+
-+errno_t sssctl_cache_remove(struct sss_cmdline *cmdline,
-                             struct sss_tool_ctx *tool_ctx,
-                             void *pvt);
- 
--errno_t sssctl_upgrade_cache(struct sss_cmdline *cmdline,
-+errno_t sssctl_cache_upgrade(struct sss_cmdline *cmdline,
-                              struct sss_tool_ctx *tool_ctx,
-                              void *pvt);
- 
--errno_t sssctl_remove_logs(struct sss_cmdline *cmdline,
-+errno_t sssctl_logs_remove(struct sss_cmdline *cmdline,
-                            struct sss_tool_ctx *tool_ctx,
-                            void *pvt);
- 
--errno_t sssctl_fetch_logs(struct sss_cmdline *cmdline,
-+errno_t sssctl_logs_fetch(struct sss_cmdline *cmdline,
-                           struct sss_tool_ctx *tool_ctx,
-                           void *pvt);
- 
--errno_t sssctl_user(struct sss_cmdline *cmdline,
--                    struct sss_tool_ctx *tool_ctx,
--                    void *pvt);
-+errno_t sssctl_user_show(struct sss_cmdline *cmdline,
-+                         struct sss_tool_ctx *tool_ctx,
-+                         void *pvt);
- 
--errno_t sssctl_group(struct sss_cmdline *cmdline,
--                     struct sss_tool_ctx *tool_ctx,
--                     void *pvt);
-+errno_t sssctl_group_show(struct sss_cmdline *cmdline,
-+                          struct sss_tool_ctx *tool_ctx,
-+                          void *pvt);
- 
--errno_t sssctl_netgroup(struct sss_cmdline *cmdline,
--                        struct sss_tool_ctx *tool_ctx,
--                        void *pvt);
-+errno_t sssctl_netgroup_show(struct sss_cmdline *cmdline,
-+                             struct sss_tool_ctx *tool_ctx,
-+                             void *pvt);
- 
- errno_t sssctl_config_check(struct sss_cmdline *cmdline,
-                             struct sss_tool_ctx *tool_ctx,
-                             void *pvt);
--
- #endif /* _SSSCTL_H_ */
-diff --git a/src/tools/sssctl/sssctl_cache.c b/src/tools/sssctl/sssctl_cache.c
-index 4a1f3558ed7064ca40ccf9313d99fbab36e6e4c9..b1a7cc933d58d99fe34a05cb6eabe11a5270cee1 100644
---- a/src/tools/sssctl/sssctl_cache.c
-+++ b/src/tools/sssctl/sssctl_cache.c
-@@ -569,9 +569,9 @@ struct sssctl_cache_opts {
-     int id;
- };
- 
--errno_t sssctl_user(struct sss_cmdline *cmdline,
--                    struct sss_tool_ctx *tool_ctx,
--                    void *pvt)
-+errno_t sssctl_user_show(struct sss_cmdline *cmdline,
-+                         struct sss_tool_ctx *tool_ctx,
-+                         void *pvt)
- {
-     struct sssctl_cache_opts opts = {0};
-     const char *attr;
-@@ -616,9 +616,9 @@ errno_t sssctl_user(struct sss_cmdline *cmdline,
-     return EOK;
- }
- 
--errno_t sssctl_group(struct sss_cmdline *cmdline,
--                     struct sss_tool_ctx *tool_ctx,
--                     void *pvt)
-+errno_t sssctl_group_show(struct sss_cmdline *cmdline,
-+                          struct sss_tool_ctx *tool_ctx,
-+                          void *pvt)
- {
-     struct sssctl_cache_opts opts = {0};
-     const char *attr;
-@@ -662,9 +662,9 @@ errno_t sssctl_group(struct sss_cmdline *cmdline,
-     return EOK;
- }
- 
--errno_t sssctl_netgroup(struct sss_cmdline *cmdline,
--                        struct sss_tool_ctx *tool_ctx,
--                        void *pvt)
-+errno_t sssctl_netgroup_show(struct sss_cmdline *cmdline,
-+                             struct sss_tool_ctx *tool_ctx,
-+                             void *pvt)
- {
-     struct sssctl_cache_opts opts = {0};
-     errno_t ret;
-diff --git a/src/tools/sssctl/sssctl_data.c b/src/tools/sssctl/sssctl_data.c
-index 3ab2ddf20006b2d5a26e2f167819677431854eb6..a26ddd8d5200319e75282b738791cf270f0d75a8 100644
---- a/src/tools/sssctl/sssctl_data.c
-+++ b/src/tools/sssctl/sssctl_data.c
-@@ -124,9 +124,9 @@ static errno_t sssctl_backup(bool force)
-     return ret;
- }
- 
--errno_t sssctl_backup_local_data(struct sss_cmdline *cmdline,
--                                 struct sss_tool_ctx *tool_ctx,
--                                 void *pvt)
-+errno_t sssctl_client_data_backup(struct sss_cmdline *cmdline,
-+                                  struct sss_tool_ctx *tool_ctx,
-+                                  void *pvt)
- {
-     struct sssctl_data_opts opts = {0};
-     errno_t ret;
-@@ -184,9 +184,9 @@ static errno_t sssctl_restore(bool force_start, bool force_restart)
-     return ret;
- }
- 
--errno_t sssctl_restore_local_data(struct sss_cmdline *cmdline,
--                                  struct sss_tool_ctx *tool_ctx,
--                                  void *pvt)
-+errno_t sssctl_client_data_restore(struct sss_cmdline *cmdline,
-+                                   struct sss_tool_ctx *tool_ctx,
-+                                   void *pvt)
- {
-     struct sssctl_data_opts opts = {0};
-     errno_t ret;
-@@ -207,7 +207,7 @@ errno_t sssctl_restore_local_data(struct sss_cmdline *cmdline,
-     return sssctl_restore(opts.start, opts.restart);
- }
- 
--errno_t sssctl_remove_cache(struct sss_cmdline *cmdline,
-+errno_t sssctl_cache_remove(struct sss_cmdline *cmdline,
-                             struct sss_tool_ctx *tool_ctx,
-                             void *pvt)
- {
-@@ -259,7 +259,7 @@ errno_t sssctl_remove_cache(struct sss_cmdline *cmdline,
-     return EOK;
- }
- 
--errno_t sssctl_upgrade_cache(struct sss_cmdline *cmdline,
-+errno_t sssctl_cache_upgrade(struct sss_cmdline *cmdline,
-                              struct sss_tool_ctx *tool_ctx,
-                              void *pvt)
- {
-diff --git a/src/tools/sssctl/sssctl_domains.c b/src/tools/sssctl/sssctl_domains.c
-index 5aaaf770981a92b80b9376fd244d2d97e0502dac..cfc4e56133213e27496350033d4d28c3f5b5c63d 100644
---- a/src/tools/sssctl/sssctl_domains.c
-+++ b/src/tools/sssctl/sssctl_domains.c
-@@ -27,9 +27,9 @@
- #include "sbus/sssd_dbus.h"
- #include "responder/ifp/ifp_iface.h"
- 
--errno_t sssctl_list_domains(struct sss_cmdline *cmdline,
--                            struct sss_tool_ctx *tool_ctx,
--                            void *pvt)
-+errno_t sssctl_domain_list(struct sss_cmdline *cmdline,
-+                           struct sss_tool_ctx *tool_ctx,
-+                           void *pvt)
- {
-     sss_sifp_ctx *sifp;
-     sss_sifp_error error;
-diff --git a/src/tools/sssctl/sssctl_logs.c b/src/tools/sssctl/sssctl_logs.c
-index a203474648e3c1719e16146f8f7b484f9d62541c..883f2ac2fa688b672cd8f388e4c571d1a12a32af 100644
---- a/src/tools/sssctl/sssctl_logs.c
-+++ b/src/tools/sssctl/sssctl_logs.c
-@@ -34,7 +34,7 @@ struct sssctl_logs_opts {
-     int archived;
- };
- 
--errno_t sssctl_remove_logs(struct sss_cmdline *cmdline,
-+errno_t sssctl_logs_remove(struct sss_cmdline *cmdline,
-                            struct sss_tool_ctx *tool_ctx,
-                            void *pvt)
- {
-@@ -74,7 +74,7 @@ errno_t sssctl_remove_logs(struct sss_cmdline *cmdline,
-     return EOK;
- }
- 
--errno_t sssctl_fetch_logs(struct sss_cmdline *cmdline,
-+errno_t sssctl_logs_fetch(struct sss_cmdline *cmdline,
-                           struct sss_tool_ctx *tool_ctx,
-                           void *pvt)
- {
--- 
-2.4.11
-
diff --git a/SOURCES/0067-PAM-Add-application-services.patch b/SOURCES/0067-PAM-Add-application-services.patch
new file mode 100644
index 0000000..2c8fddd
--- /dev/null
+++ b/SOURCES/0067-PAM-Add-application-services.patch
@@ -0,0 +1,452 @@
+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-SDAP-sanitize-member-name-before-using-in-filter.patch b/SOURCES/0067-SDAP-sanitize-member-name-before-using-in-filter.patch
deleted file mode 100644
index ed5cdfa..0000000
--- a/SOURCES/0067-SDAP-sanitize-member-name-before-using-in-filter.patch
+++ /dev/null
@@ -1,57 +0,0 @@
-From a711326867dd901d349c648392b55b6e318196db Mon Sep 17 00:00:00 2001
-From: Lukas Slebodnik <lslebodn@redhat.com>
-Date: Tue, 2 Aug 2016 15:20:35 +0200
-Subject: [PATCH 67/74] SDAP: sanitize member name before using in filter
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-It caused an errors.
-
-(Tue Aug  2 06:29:39 2016) [sssd[be[LDAP]]] [sysdb_cache_search_users]
-(0x2000): Search users with filter:
-(&(objectclass=user)(nameAlias=t(u)ser@ldap))
-(Tue Aug  2 06:29:39 2016) [sssd[be[LDAP]]] [sysdb_cache_search_users]
-(0x0080): Error: 5 (Input/output error)
-
-Resolves:
-https://fedorahosted.org/sssd/ticket/3121
-
-Reviewed-by: Pavel Březina <pbrezina@redhat.com>
----
- src/providers/ldap/sdap_async_groups.c | 11 ++++++++++-
- 1 file changed, 10 insertions(+), 1 deletion(-)
-
-diff --git a/src/providers/ldap/sdap_async_groups.c b/src/providers/ldap/sdap_async_groups.c
-index 102c1c0384be6da8732d56b7a318ded5a5132360..f19b68b8c403734f88b51a411ba0d009977d3491 100644
---- a/src/providers/ldap/sdap_async_groups.c
-+++ b/src/providers/ldap/sdap_async_groups.c
-@@ -1501,6 +1501,7 @@ sdap_process_missing_member_2307(struct sdap_process_group_state *state,
-     const char *filter;
-     const char *username;
-     const char *user_dn;
-+    char *sanitized_name;
-     size_t count;
-     struct ldb_message **msgs = NULL;
-     static const char *attrs[] = { SYSDB_NAME, NULL };
-@@ -1508,8 +1509,16 @@ sdap_process_missing_member_2307(struct sdap_process_group_state *state,
-     tmp_ctx = talloc_new(NULL);
-     if (!tmp_ctx) return ENOMEM;
- 
-+    ret = sss_filter_sanitize(tmp_ctx, member_name, &sanitized_name);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_CRIT_FAILURE,
-+              "Failed to sanitize the given name:'%s'.\n", member_name);
-+        goto done;
-+    }
-+
-     /* Check for the alias in the sysdb */
--    filter = talloc_asprintf(tmp_ctx, "(%s=%s)", SYSDB_NAME_ALIAS, member_name);
-+    filter = talloc_asprintf(tmp_ctx, "(%s=%s)", SYSDB_NAME_ALIAS,
-+                             sanitized_name);
-     if (!filter) {
-         ret = ENOMEM;
-         goto done;
--- 
-2.4.11
-
diff --git a/SOURCES/0068-SDAP-sysdb_search_users-does-not-set-users_count-for.patch b/SOURCES/0068-SDAP-sysdb_search_users-does-not-set-users_count-for.patch
deleted file mode 100644
index f82f6d8..0000000
--- a/SOURCES/0068-SDAP-sysdb_search_users-does-not-set-users_count-for.patch
+++ /dev/null
@@ -1,51 +0,0 @@
-From 4e1111d869c8980f81a17b58844c48f1a342d774 Mon Sep 17 00:00:00 2001
-From: Lukas Slebodnik <lslebodn@redhat.com>
-Date: Thu, 4 Aug 2016 08:50:50 +0200
-Subject: [PATCH 68/74] SDAP: sysdb_search_users does not set users_count for
- failures
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-==32577== Conditional jump or move depends on uninitialised value(s)
-==32577==    at 0x140DCE10: sdap_process_missing_member_2307 (sdap_async_groups.c:1556)
-==32577==    by 0x140DCE10: sdap_process_group_members_2307 (sdap_async_groups.c:1625)
-==32577==    by 0x140DCE10: sdap_process_group_send (sdap_async_groups.c:1298)
-==32577==    by 0x140DCE10: sdap_get_groups_process (sdap_async_groups.c:2130)
-==32577==    by 0x140CFDA8: generic_ext_search_handler.isra.3 (sdap_async.c:1688)
-==32577==    by 0x140D2416: sdap_get_generic_op_finished (sdap_async.c:1578)
-==32577==    by 0x140D0DFC: sdap_process_message (sdap_async.c:353)
-==32577==    by 0x140D0DFC: sdap_process_result (sdap_async.c:197)
-==32577==    by 0x8BF1B4E: tevent_common_loop_timer_delay (tevent_timed.c:341)
-==32577==    by 0x8BF2B59: epoll_event_loop_once (tevent_epoll.c:911)
-==32577==    by 0x8BF1256: std_event_loop_once (tevent_standard.c:114)
-==32577==    by 0x8BED40C: _tevent_loop_once (tevent.c:533)
-==32577==    by 0x8BED5AA: tevent_common_loop_wait (tevent.c:637)
-==32577==    by 0x8BF11F6: std_event_loop_wait (tevent_standard.c:140)
-==32577==    by 0x529DD02: server_loop (server.c:702)
-==32577==    by 0x110951: main (data_provider_be.c:587)
-
-Resolves:
-https://fedorahosted.org/sssd/ticket/3121
-
-Reviewed-by: Pavel Březina <pbrezina@redhat.com>
----
- src/providers/ldap/sdap_async_groups.c | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
-diff --git a/src/providers/ldap/sdap_async_groups.c b/src/providers/ldap/sdap_async_groups.c
-index f19b68b8c403734f88b51a411ba0d009977d3491..72760b75acae4cb6ce15c72f16dae8e859d89847 100644
---- a/src/providers/ldap/sdap_async_groups.c
-+++ b/src/providers/ldap/sdap_async_groups.c
-@@ -1553,7 +1553,7 @@ sdap_process_missing_member_2307(struct sdap_process_group_state *state,
-         if (ret != EOK) {
-             DEBUG(SSSDBG_OP_FAILURE, "Could not add group member %s\n", username);
-         }
--    } else if (ret == ENOENT || count == 0) {
-+    } else if (ret == ENOENT) {
-         /* The entry really does not exist, add a ghost */
-         DEBUG(SSSDBG_TRACE_FUNC, "Adding a ghost entry\n");
-         ret = sdap_add_group_member_2307(state->ghost_dns, member_name);
--- 
-2.4.11
-
diff --git a/SOURCES/0068-SYSDB-Allow-storing-non-POSIX-users.patch b/SOURCES/0068-SYSDB-Allow-storing-non-POSIX-users.patch
new file mode 100644
index 0000000..8ed534b
--- /dev/null
+++ b/SOURCES/0068-SYSDB-Allow-storing-non-POSIX-users.patch
@@ -0,0 +1,152 @@
+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-SYSDB-Only-generate-new-UID-in-local-domain.patch b/SOURCES/0069-SYSDB-Only-generate-new-UID-in-local-domain.patch
new file mode 100644
index 0000000..c6a76f3
--- /dev/null
+++ b/SOURCES/0069-SYSDB-Only-generate-new-UID-in-local-domain.patch
@@ -0,0 +1,36 @@
+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/0069-SYSDB-Sanitize-dn-in-sysdb_get_user_members_recursiv.patch b/SOURCES/0069-SYSDB-Sanitize-dn-in-sysdb_get_user_members_recursiv.patch
deleted file mode 100644
index 24ea5be..0000000
--- a/SOURCES/0069-SYSDB-Sanitize-dn-in-sysdb_get_user_members_recursiv.patch
+++ /dev/null
@@ -1,69 +0,0 @@
-From 0bb66d94542920870effa808861c0c20180111ba Mon Sep 17 00:00:00 2001
-From: Lukas Slebodnik <lslebodn@redhat.com>
-Date: Tue, 2 Aug 2016 15:20:19 +0200
-Subject: [PATCH 69/74] SYSDB: Sanitize dn in
- sysdb_get_user_members_recursively
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-There was a crash in nss responder when a group contained
-a user with special charactes which shoudl be sanitized before
-using in filter.
-
-==31651== Conditional jump or move depends on uninitialised value(s)
-==31651==    at 0x8BEA7DE: _talloc_steal_loc (talloc.c:1215)
-==31651==    by 0x5264889: sysdb_get_user_members_recursively (sysdb_ops.c:4759)
-==31651==    by 0x5278F61: sysdb_add_group_member_overrides (sysdb_views.c:1375)
-==31651==    by 0x526677C: sysdb_getgrnam_with_views (sysdb_search.c:799)
-==31651==    by 0x1172F6: nss_cmd_getgrnam_search (nsssrv_cmd.c:3168)
-==31651==    by 0x119C67: nss_cmd_getby_dp_callback (nsssrv_cmd.c:1382)
-==31651==    by 0x10FD14: nsssrv_dp_send_acct_req_done (nsssrv_cmd.c:916)
-==31651==    by 0x12898B: sss_dp_internal_get_done (responder_dp.c:791)
-==31651==    by 0x58FF861: complete_pending_call_and_unlock (dbus-connection.c:2314)
-==31651==    by 0x5902B50: dbus_connection_dispatch (dbus-connection.c:4580)
-==31651==    by 0x527F261: sbus_dispatch (sssd_dbus_connection.c:96)
-==31651==    by 0x89D8B4E: tevent_common_loop_timer_delay (tevent_timed.c:341)
-
-Resolves:
-https://fedorahosted.org/sssd/ticket/3121
-
-Reviewed-by: Pavel Březina <pbrezina@redhat.com>
----
- src/db/sysdb_ops.c | 12 +++++++++++-
- 1 file changed, 11 insertions(+), 1 deletion(-)
-
-diff --git a/src/db/sysdb_ops.c b/src/db/sysdb_ops.c
-index ed177d1730723a61e01167a75a0baca6d81252f8..342e16fb20e2c418745b137162425509ca1fd0cb 100644
---- a/src/db/sysdb_ops.c
-+++ b/src/db/sysdb_ops.c
-@@ -4722,6 +4722,7 @@ errno_t sysdb_get_user_members_recursively(TALLOC_CTX *mem_ctx,
-     struct ldb_result *res;
-     struct ldb_dn *base_dn;
-     char *filter;
-+    char *sanitized_name;
-     const char *attrs[] = SYSDB_PW_ATTRS;
-     struct ldb_message **msgs;
- 
-@@ -4737,8 +4738,17 @@ errno_t sysdb_get_user_members_recursively(TALLOC_CTX *mem_ctx,
-         goto done;
-     }
- 
-+    ret = sss_filter_sanitize(tmp_ctx, ldb_dn_get_linearized(group_dn),
-+                              &sanitized_name);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_CRIT_FAILURE,
-+              "Failed to sanitize the given name:'%s'.\n",
-+              ldb_dn_get_linearized(group_dn));
-+        goto done;
-+    }
-+
-     filter = talloc_asprintf(tmp_ctx, "(&("SYSDB_UC")("SYSDB_MEMBEROF"=%s))",
--                             ldb_dn_get_linearized(group_dn));
-+                             sanitized_name);
-     if (filter == NULL) {
-         DEBUG(SSSDBG_OP_FAILURE, "talloc_asprintf failed.\n");
-         ret = ENOMEM;
--- 
-2.4.11
-
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
new file mode 100644
index 0000000..d70a026
--- /dev/null
+++ b/SOURCES/0070-LDAP-save-non-POSIX-users-in-application-domains.patch
@@ -0,0 +1,141 @@
+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/0070-SYSDB-Fix-setting-dataExpireTimestamp-if-sysdb-is-su.patch b/SOURCES/0070-SYSDB-Fix-setting-dataExpireTimestamp-if-sysdb-is-su.patch
deleted file mode 100644
index bed5cd3..0000000
--- a/SOURCES/0070-SYSDB-Fix-setting-dataExpireTimestamp-if-sysdb-is-su.patch
+++ /dev/null
@@ -1,178 +0,0 @@
-From ba87a54746b417ad32362f3c6e565c7af8d21afa Mon Sep 17 00:00:00 2001
-From: Jakub Hrozek <jhrozek@redhat.com>
-Date: Wed, 3 Aug 2016 14:23:39 +0200
-Subject: [PATCH 70/74] SYSDB: Fix setting dataExpireTimestamp if sysdb is
- supposed to set the current time
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-sysdb is already able to retrieve the current timestamp if the caller
-doesn't specify it. However, for the timestamp cache this came too late
-and the timestamp cache used zero as the 'now' time.
-
-Resolves:
-https://fedorahosted.org/sssd/ticket/3064
-
-Reviewed-by: Pavel Březina <pbrezina@redhat.com>
----
- src/db/sysdb_ops.c                     | 20 ++++----
- src/tests/cmocka/test_sysdb_ts_cache.c | 83 ++++++++++++++++++++++++++++++++++
- 2 files changed, 93 insertions(+), 10 deletions(-)
-
-diff --git a/src/db/sysdb_ops.c b/src/db/sysdb_ops.c
-index 342e16fb20e2c418745b137162425509ca1fd0cb..67006c155098b9fde00a01d424014852c383a325 100644
---- a/src/db/sysdb_ops.c
-+++ b/src/db/sysdb_ops.c
-@@ -2465,6 +2465,11 @@ int sysdb_store_user(struct sss_domain_info *domain,
-     errno_t sret = EOK;
-     bool in_transaction = false;
- 
-+    /* get transaction timestamp */
-+    if (now == 0) {
-+        now = time(NULL);
-+    }
-+
-     ret = sysdb_check_and_update_ts_usr(domain, name, attrs,
-                                         cache_timeout, now);
-     if (ret == EOK) {
-@@ -2508,11 +2513,6 @@ int sysdb_store_user(struct sss_domain_info *domain,
-         DEBUG(SSSDBG_TRACE_LIBS, "User %s does not exist.\n", name);
-     }
- 
--    /* get transaction timestamp */
--    if (!now) {
--        now = time(NULL);
--    }
--
-     if (ret == ENOENT) {
-         /* the user doesn't exist, turn into adding a user */
-         ret = sysdb_store_new_user(domain, name, uid, gid, gecos, homedir,
-@@ -2700,6 +2700,11 @@ int sysdb_store_group(struct sss_domain_info *domain,
-     errno_t sret = EOK;
-     bool in_transaction = false;
- 
-+    /* get transaction timestamp */
-+    if (!now) {
-+        now = time(NULL);
-+    }
-+
-     ret = sysdb_check_and_update_ts_grp(domain, name, attrs,
-                                         cache_timeout, now);
-     if (ret == EOK) {
-@@ -2741,11 +2746,6 @@ int sysdb_store_group(struct sss_domain_info *domain,
-         }
-     }
- 
--    /* get transaction timestamp */
--    if (!now) {
--        now = time(NULL);
--    }
--
-     if (new_group) {
-         ret = sysdb_store_new_group(domain, name, gid, attrs,
-                                     cache_timeout, now);
-diff --git a/src/tests/cmocka/test_sysdb_ts_cache.c b/src/tests/cmocka/test_sysdb_ts_cache.c
-index d5492299647f54e379ea3f305ccc1501c7f6c79f..aa857e7e4823d2d8ba1e1a794b3e2474876e9ab0 100644
---- a/src/tests/cmocka/test_sysdb_ts_cache.c
-+++ b/src/tests/cmocka/test_sysdb_ts_cache.c
-@@ -1348,6 +1348,86 @@ static void test_user_byupn(void **state)
-     talloc_free(res);
- }
- 
-+static void test_sysdb_zero_now(void **state)
-+{
-+    int ret;
-+    struct sysdb_ts_test_ctx *test_ctx = talloc_get_type_abort(*state,
-+                                                     struct sysdb_ts_test_ctx);
-+    struct ldb_result *res = NULL;
-+    uint64_t cache_expire_sysdb;
-+    uint64_t cache_expire_ts;
-+    struct sysdb_attrs *attrs = NULL;
-+
-+    /* Nothing must be stored in either cache at the beginning of the test */
-+    res = sysdb_getpwnam_res(test_ctx, test_ctx->tctx->dom, TEST_USER_NAME);
-+    assert_int_equal(res->count, 0);
-+    talloc_free(res);
-+
-+    res = sysdb_getgrnam_res(test_ctx, test_ctx->tctx->dom, TEST_GROUP_NAME);
-+    assert_int_equal(res->count, 0);
-+    talloc_free(res);
-+
-+    attrs = create_modstamp_attrs(test_ctx, TEST_MODSTAMP_1);
-+    assert_non_null(attrs);
-+
-+    ret = sysdb_store_user(test_ctx->tctx->dom, TEST_USER_NAME, NULL,
-+                           TEST_USER_UID, TEST_USER_GID, TEST_USER_NAME,
-+                           "/home/"TEST_USER_NAME, "/bin/bash", NULL,
-+                           attrs, NULL, TEST_CACHE_TIMEOUT,
-+                           0);
-+    talloc_zfree(attrs);
-+    assert_int_equal(ret, EOK);
-+
-+    attrs = create_modstamp_attrs(test_ctx, TEST_MODSTAMP_1);
-+    assert_non_null(attrs);
-+
-+    ret = sysdb_store_group(test_ctx->tctx->dom,
-+                            TEST_GROUP_NAME,
-+                            TEST_GROUP_GID,
-+                            attrs,
-+                            TEST_CACHE_TIMEOUT,
-+                            0);
-+    talloc_zfree(attrs);
-+    assert_int_equal(ret, EOK);
-+    talloc_zfree(attrs);
-+
-+    attrs = create_modstamp_attrs(test_ctx, TEST_MODSTAMP_1);
-+    assert_non_null(attrs);
-+
-+    ret = sysdb_store_user(test_ctx->tctx->dom, TEST_USER_NAME, NULL,
-+                           TEST_USER_UID, TEST_USER_GID, TEST_USER_NAME,
-+                           "/home/"TEST_USER_NAME, "/bin/bash", NULL,
-+                           attrs, NULL, TEST_CACHE_TIMEOUT,
-+                           0);
-+    talloc_zfree(attrs);
-+    assert_int_equal(ret, EOK);
-+
-+    attrs = create_modstamp_attrs(test_ctx, TEST_MODSTAMP_1);
-+    assert_non_null(attrs);
-+
-+    ret = sysdb_store_group(test_ctx->tctx->dom,
-+                            TEST_GROUP_NAME,
-+                            TEST_GROUP_GID,
-+                            attrs,
-+                            TEST_CACHE_TIMEOUT,
-+                            0);
-+    talloc_zfree(attrs);
-+    assert_int_equal(ret, EOK);
-+
-+    /* Even though we passed zero as the timestamp, the timestamp cache should
-+     * have used the current time instead
-+     */
-+    get_pw_timestamp_attrs(test_ctx, TEST_USER_NAME,
-+                           &cache_expire_sysdb, &cache_expire_ts);
-+    assert_true(cache_expire_sysdb > TEST_CACHE_TIMEOUT);
-+    assert_true(cache_expire_ts > TEST_CACHE_TIMEOUT);
-+
-+    get_gr_timestamp_attrs(test_ctx, TEST_GROUP_NAME,
-+                           &cache_expire_sysdb, &cache_expire_ts);
-+    assert_true(cache_expire_sysdb > TEST_CACHE_TIMEOUT);
-+    assert_true(cache_expire_ts > TEST_CACHE_TIMEOUT);
-+}
-+
- int main(int argc, const char *argv[])
- {
-     int rv;
-@@ -1396,6 +1476,9 @@ int main(int argc, const char *argv[])
-         cmocka_unit_test_setup_teardown(test_user_byupn,
-                                         test_sysdb_ts_setup,
-                                         test_sysdb_ts_teardown),
-+        cmocka_unit_test_setup_teardown(test_sysdb_zero_now,
-+                                        test_sysdb_ts_setup,
-+                                        test_sysdb_ts_teardown),
-     };
- 
-     /* Set debug level to invalid value so we can deside if -d 0 was used. */
--- 
-2.4.11
-
diff --git a/SOURCES/0071-LDAP-Relax-search-filters-in-application-domains.patch b/SOURCES/0071-LDAP-Relax-search-filters-in-application-domains.patch
new file mode 100644
index 0000000..73f2862
--- /dev/null
+++ b/SOURCES/0071-LDAP-Relax-search-filters-in-application-domains.patch
@@ -0,0 +1,254 @@
+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-Revert-LDAP-Lookup-services-by-all-protocols-unless-.patch b/SOURCES/0071-Revert-LDAP-Lookup-services-by-all-protocols-unless-.patch
deleted file mode 100644
index 0caae35..0000000
--- a/SOURCES/0071-Revert-LDAP-Lookup-services-by-all-protocols-unless-.patch
+++ /dev/null
@@ -1,44 +0,0 @@
-From e4fd5c67a8f63062b17e615ce746a19994ee310c Mon Sep 17 00:00:00 2001
-From: Petr Cech <pcech@redhat.com>
-Date: Wed, 27 Jul 2016 11:13:04 +0200
-Subject: [PATCH 71/74] Revert "LDAP: Lookup services by all protocols unless a
- protocol is specified"
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-This reverts commit aa58e216c1f794bd335151f19e79adbb3ddf4c73.
-
-Reviewed-by: Pavel Březina <pbrezina@redhat.com>
----
- src/providers/ldap/ldap_id_services.c | 7 ++-----
- 1 file changed, 2 insertions(+), 5 deletions(-)
-
-diff --git a/src/providers/ldap/ldap_id_services.c b/src/providers/ldap/ldap_id_services.c
-index 401228c20af31ae2df9bb3d35ed25fb6f06b1839..77215127b53297d840eaa4d2f35a75eedb085e43 100644
---- a/src/providers/ldap/ldap_id_services.c
-+++ b/src/providers/ldap/ldap_id_services.c
-@@ -89,9 +89,6 @@ services_get_send(TALLOC_CTX *mem_ctx,
-     state->sysdb = sdom->dom->sysdb;
-     state->name = name;
-     state->protocol = protocol;
--    if (state->protocol != NULL && state->protocol[0] == '\0') {
--        state->protocol = NULL;
--    }
-     state->filter_type = filter_type;
-     state->noexist_delete = noexist_delete;
- 
-@@ -117,8 +114,8 @@ services_get_send(TALLOC_CTX *mem_ctx,
-     ret = sss_filter_sanitize(state, name, &clean_name);
-     if (ret != EOK)  goto error;
- 
--    if (state->protocol != NULL) {
--        ret = sss_filter_sanitize(state, state->protocol, &clean_protocol);
-+    if (protocol) {
-+        ret = sss_filter_sanitize(state, protocol, &clean_protocol);
-         if (ret != EOK)  goto error;
-     }
- 
--- 
-2.4.11
-
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
new file mode 100644
index 0000000..05693f1
--- /dev/null
+++ b/SOURCES/0072-KRB5-Authenticate-users-in-a-non-POSIX-domain-using-.patch
@@ -0,0 +1,353 @@
+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-PROVIDER-Conversion-empty-string-from-D-Bus-to-NULL.patch b/SOURCES/0072-PROVIDER-Conversion-empty-string-from-D-Bus-to-NULL.patch
deleted file mode 100644
index 1e57a7b..0000000
--- a/SOURCES/0072-PROVIDER-Conversion-empty-string-from-D-Bus-to-NULL.patch
+++ /dev/null
@@ -1,59 +0,0 @@
-From 0edb93b54bf79df41aa34a77e9a173cf8529559a Mon Sep 17 00:00:00 2001
-From: Petr Cech <pcech@redhat.com>
-Date: Wed, 27 Jul 2016 11:13:32 +0200
-Subject: [PATCH 72/74] PROVIDER: Conversion empty string from D-Bus to NULL
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-This patch fixes the issue with empty string recieving from D-Bus.
-Data providers obtains NULL. So this is simple conversin.
-
-Resolves:
-https://fedorahosted.org/sssd/ticket/3084
-
-Reviewed-by: Pavel Březina <pbrezina@redhat.com>
----
- src/providers/data_provider/dp_target_id.c | 6 +++---
- src/providers/ldap/ldap_id_services.c      | 2 +-
- 2 files changed, 4 insertions(+), 4 deletions(-)
-
-diff --git a/src/providers/data_provider/dp_target_id.c b/src/providers/data_provider/dp_target_id.c
-index 7c882be2c17650d818cb3907197e381a8f635a77..1b06cbe5b96f56c33dd048cf6211b7c97819db8c 100644
---- a/src/providers/data_provider/dp_target_id.c
-+++ b/src/providers/data_provider/dp_target_id.c
-@@ -62,15 +62,15 @@ static bool check_and_parse_filter(struct be_acct_req *data,
-                  {0, 0, 0}};
-     int i;
- 
--    if (filter == NULL) {
-+    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 = &filter[types[i].lenght];
--            data->extra_value = extra;
-+            data->filter_value = SBUS_SET_STRING(&filter[types[i].lenght]);
-+            data->extra_value = SBUS_SET_STRING(extra);
-             return true;
-         }
-     }
-diff --git a/src/providers/ldap/ldap_id_services.c b/src/providers/ldap/ldap_id_services.c
-index 77215127b53297d840eaa4d2f35a75eedb085e43..e91fc52d731eaa22be7e98365fc75e7057cb8f1f 100644
---- a/src/providers/ldap/ldap_id_services.c
-+++ b/src/providers/ldap/ldap_id_services.c
-@@ -114,7 +114,7 @@ services_get_send(TALLOC_CTX *mem_ctx,
-     ret = sss_filter_sanitize(state, name, &clean_name);
-     if (ret != EOK)  goto error;
- 
--    if (protocol) {
-+    if (protocol == NULL) {
-         ret = sss_filter_sanitize(state, protocol, &clean_protocol);
-         if (ret != EOK)  goto error;
-     }
--- 
-2.4.11
-
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
new file mode 100644
index 0000000..0530278
--- /dev/null
+++ b/SOURCES/0073-KCM-Fix-off-by-one-error-in-secrets-key-parsing.patch
@@ -0,0 +1,211 @@
+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/0073-LDAP-Fix-Dereference-after-NULL-check.patch b/SOURCES/0073-LDAP-Fix-Dereference-after-NULL-check.patch
deleted file mode 100644
index 3d467ba..0000000
--- a/SOURCES/0073-LDAP-Fix-Dereference-after-NULL-check.patch
+++ /dev/null
@@ -1,36 +0,0 @@
-From 33649cb41206f568e2bef582e13edbc721184a29 Mon Sep 17 00:00:00 2001
-From: Lukas Slebodnik <lslebodn@redhat.com>
-Date: Mon, 8 Aug 2016 09:03:47 +0200
-Subject: [PATCH 73/74] LDAP: Fix Dereference after NULL check
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-The commit dc30c60f166ad9adc63a47a1013508a71624ac87
-changed the logic in NULL check
- -    if (protocol) {
- +    if (protocol == NULL) {
-
-Found by Coverity:
-
-Reviewed-by: Petr Čech <pcech@redhat.com>
----
- src/providers/ldap/ldap_id_services.c | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
-diff --git a/src/providers/ldap/ldap_id_services.c b/src/providers/ldap/ldap_id_services.c
-index e91fc52d731eaa22be7e98365fc75e7057cb8f1f..638cb619b39f135307090dcf0f2c6ab2cc4119d0 100644
---- a/src/providers/ldap/ldap_id_services.c
-+++ b/src/providers/ldap/ldap_id_services.c
-@@ -114,7 +114,7 @@ services_get_send(TALLOC_CTX *mem_ctx,
-     ret = sss_filter_sanitize(state, name, &clean_name);
-     if (ret != EOK)  goto error;
- 
--    if (protocol == NULL) {
-+    if (protocol != NULL) {
-         ret = sss_filter_sanitize(state, protocol, &clean_protocol);
-         if (ret != EOK)  goto error;
-     }
--- 
-2.4.11
-
diff --git a/SOURCES/0074-LDAP-Fixing-wrong-pam-error-code-for-passwd.patch b/SOURCES/0074-LDAP-Fixing-wrong-pam-error-code-for-passwd.patch
deleted file mode 100644
index 913b219..0000000
--- a/SOURCES/0074-LDAP-Fixing-wrong-pam-error-code-for-passwd.patch
+++ /dev/null
@@ -1,32 +0,0 @@
-From 89f9598e19000fed30c1069eec01b094eeae0377 Mon Sep 17 00:00:00 2001
-From: Petr Cech <pcech@redhat.com>
-Date: Tue, 2 Aug 2016 10:11:14 +0200
-Subject: [PATCH 74/74] LDAP: Fixing wrong pam error code for passwd
-
-This patch adds right pam error code for sssd offline state.
-
-Resolves:
-https://fedorahosted.org/sssd/ticket/3109
----
- src/providers/ldap/ldap_auth.c | 5 +++++
- 1 file changed, 5 insertions(+)
-
-diff --git a/src/providers/ldap/ldap_auth.c b/src/providers/ldap/ldap_auth.c
-index 107f6ded1a903904e088f0b6b0320fe82a52af52..35f16b0d4a6f8e566b0cf63b65ba46f31e7c1bcd 100644
---- a/src/providers/ldap/ldap_auth.c
-+++ b/src/providers/ldap/ldap_auth.c
-@@ -1101,6 +1101,11 @@ sdap_pam_chpass_handler_send(TALLOC_CTX *mem_ctx,
-     state->auth_ctx = auth_ctx;
-     state->ev = params->ev;
- 
-+    if (be_is_offline(state->be_ctx)) {
-+        pd->pam_status = PAM_AUTHINFO_UNAVAIL;
-+        goto immediately;
-+    }
-+
-     if ((pd->priv == 1) && (pd->cmd == SSS_PAM_CHAUTHTOK_PRELIM) &&
-         (sss_authtok_get_type(pd->authtok) != SSS_AUTHTOK_TYPE_PASSWORD)) {
-         DEBUG(SSSDBG_CONF_SETTINGS,
--- 
-2.4.11
-
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
new file mode 100644
index 0000000..6351c10
--- /dev/null
+++ b/SOURCES/0074-tcurl-add-support-for-ssl-and-raw-output.patch
@@ -0,0 +1,1478 @@
+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-PAM-Do-not-act-on-ldb_message-in-case-of-a-failure.patch b/SOURCES/0075-PAM-Do-not-act-on-ldb_message-in-case-of-a-failure.patch
deleted file mode 100644
index 8adb0ca..0000000
--- a/SOURCES/0075-PAM-Do-not-act-on-ldb_message-in-case-of-a-failure.patch
+++ /dev/null
@@ -1,60 +0,0 @@
-From 86f099e6ca0e09dd5fe44816238a4323c63f9ee7 Mon Sep 17 00:00:00 2001
-From: Jakub Hrozek <jhrozek@redhat.com>
-Date: Wed, 3 Aug 2016 17:43:14 +0200
-Subject: [PATCH 75/82] PAM: Do not act on ldb_message in case of a failure
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Reviewed-by: Lukáš Slebodník <lslebodn@redhat.com>
----
- src/responder/pam/pamsrv_cmd.c | 33 ++++++++++++++++++---------------
- 1 file changed, 18 insertions(+), 15 deletions(-)
-
-diff --git a/src/responder/pam/pamsrv_cmd.c b/src/responder/pam/pamsrv_cmd.c
-index 66564f5d301a53dcdb5967f43ef4afdb897e9974..be54fbf9b627d0ec1c3b0416401885245794cf9f 100644
---- a/src/responder/pam/pamsrv_cmd.c
-+++ b/src/responder/pam/pamsrv_cmd.c
-@@ -1534,21 +1534,24 @@ static int pam_check_user_search(struct pam_auth_req *preq)
- 
-         if (preq->pd->name_is_upn) {
-             ret = sysdb_search_user_by_upn(preq, dom, name, user_attrs, &msg);
--
--            /* Since sysdb_search_user_by_upn() searches the whole cache we
--             * have to set the domain so that it matches the result. */
--            sysdb_name = ldb_msg_find_attr_as_string(msg, SYSDB_NAME, NULL);
--            if (sysdb_name == NULL) {
--                DEBUG(SSSDBG_CRIT_FAILURE, "Cached entry has no name.\n");
--                return EINVAL;
--            }
--            preq->domain = find_domain_by_object_name(get_domains_head(dom),
--                                                      sysdb_name);
--            if (preq->domain == NULL) {
--                DEBUG(SSSDBG_CRIT_FAILURE,
--                      "Cannot find matching domain for [%s].\n",
--                      sysdb_name);
--                return EINVAL;
-+            if (ret == EOK) {
-+                /* Since sysdb_search_user_by_upn() searches the whole cache we
-+                * have to set the domain so that it matches the result. */
-+                sysdb_name = ldb_msg_find_attr_as_string(msg,
-+                                                         SYSDB_NAME, NULL);
-+                if (sysdb_name == NULL) {
-+                    DEBUG(SSSDBG_CRIT_FAILURE, "Cached entry has no name.\n");
-+                    return EINVAL;
-+                }
-+                preq->domain = find_domain_by_object_name(
-+                                                        get_domains_head(dom),
-+                                                        sysdb_name);
-+                if (preq->domain == NULL) {
-+                    DEBUG(SSSDBG_CRIT_FAILURE,
-+                          "Cannot find matching domain for [%s].\n",
-+                          sysdb_name);
-+                    return EINVAL;
-+                }
-             }
-         } else {
-             ret = sysdb_getpwnam_with_views(preq, dom, name, &res);
--- 
-2.4.11
-
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
new file mode 100644
index 0000000..bcea2c6
--- /dev/null
+++ b/SOURCES/0075-tcurl-test-refactor-so-new-options-can-be-added-more.patch
@@ -0,0 +1,438 @@
+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-IPA-Check-the-return-value-of-sss_parse_internal_fqn.patch b/SOURCES/0076-IPA-Check-the-return-value-of-sss_parse_internal_fqn.patch
deleted file mode 100644
index 1dfbf21..0000000
--- a/SOURCES/0076-IPA-Check-the-return-value-of-sss_parse_internal_fqn.patch
+++ /dev/null
@@ -1,38 +0,0 @@
-From 2e027e43f0adb1b7c80e51d5613fca5a497ca331 Mon Sep 17 00:00:00 2001
-From: Jakub Hrozek <jhrozek@redhat.com>
-Date: Wed, 3 Aug 2016 18:03:59 +0200
-Subject: [PATCH 76/82] IPA: Check the return value of
- sss_parse_internal_fqname
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-We should fail the request if sss_parse_internal_fqname() fails.
-
-Reviewed-by: Lukáš Slebodník <lslebodn@redhat.com>
----
- src/providers/ipa/ipa_subdomains_id.c | 8 ++++++++
- 1 file changed, 8 insertions(+)
-
-diff --git a/src/providers/ipa/ipa_subdomains_id.c b/src/providers/ipa/ipa_subdomains_id.c
-index 002857699b65c86a6ed0c912a2a7ae06a8f9e507..2299523d0c52e3277db6d1061c79b320e78c8f72 100644
---- a/src/providers/ipa/ipa_subdomains_id.c
-+++ b/src/providers/ipa/ipa_subdomains_id.c
-@@ -509,6 +509,14 @@ static void ipa_get_subdom_acct_connected(struct tevent_req *subreq)
-             } else {
-                 ret = sss_parse_internal_fqname(req_input, state->filter,
-                                                 &shortname, NULL);
-+                if (ret != EOK) {
-+                    DEBUG(SSSDBG_CRIT_FAILURE,
-+                          "Cannot parse internal name [%s]: %d\n",
-+                          state->filter, ret);
-+                    tevent_req_error(req, ret);
-+                    return;
-+                }
-+
-                 req_input->inp.name = talloc_steal(req_input, shortname);
-             }
-             if (req_input->inp.name == NULL) {
--- 
-2.4.11
-
diff --git a/SOURCES/0076-tcurl-test-add-support-for-raw-output.patch b/SOURCES/0076-tcurl-test-add-support-for-raw-output.patch
new file mode 100644
index 0000000..3004adc
--- /dev/null
+++ b/SOURCES/0076-tcurl-test-add-support-for-raw-output.patch
@@ -0,0 +1,49 @@
+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-DP-Initialize-D-Bus-as-soon-as-possible.patch b/SOURCES/0077-DP-Initialize-D-Bus-as-soon-as-possible.patch
deleted file mode 100644
index 9641722..0000000
--- a/SOURCES/0077-DP-Initialize-D-Bus-as-soon-as-possible.patch
+++ /dev/null
@@ -1,93 +0,0 @@
-From 178e61b6dc5b547bd77998f586f118bfa9b06d30 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Pavel=20B=C5=99ezina?= <pbrezina@redhat.com>
-Date: Fri, 5 Aug 2016 11:32:43 +0200
-Subject: [PATCH 77/82] DP: Initialize D-Bus as soon as possible
-
-Resolves:
-https://fedorahosted.org/sssd/ticket/3111
-
-Reviewed-by: Petr Cech <pcech@redhat.com>
----
- src/providers/data_provider/dp.c         | 34 +++++++++++++++++++-------------
- src/providers/data_provider/dp_methods.c |  6 ++++++
- 2 files changed, 26 insertions(+), 14 deletions(-)
-
-diff --git a/src/providers/data_provider/dp.c b/src/providers/data_provider/dp.c
-index c4590e3152126fe166d0bc9e3be2a14978f90168..b672370aa153c9cd2031399a8d61c95fb16cd641 100644
---- a/src/providers/data_provider/dp.c
-+++ b/src/providers/data_provider/dp.c
-@@ -86,6 +86,7 @@ errno_t dp_init(struct tevent_context *ev,
-     provider->gid = gid;
-     provider->be_ctx = be_ctx;
- 
-+    /* Initialize request table. */
-     ret = dp_req_table_init(provider, &provider->requests.reply_table);
-     if (ret != EOK) {
-         DEBUG(SSSDBG_CRIT_FAILURE, "Unable to initialize request table "
-@@ -93,20 +94,11 @@ errno_t dp_init(struct tevent_context *ev,
-         goto done;
-     }
- 
--    ret = dp_init_modules(provider, &provider->modules);
--    if (ret != EOK) {
--        DEBUG(SSSDBG_CRIT_FAILURE, "Unable to initialize DP modules "
--              "[%d]: %s\n", ret, sss_strerror(ret));
--        goto done;
--    }
--
--    ret = dp_init_targets(provider, be_ctx, provider, provider->modules);
--    if (ret != EOK) {
--        DEBUG(SSSDBG_CRIT_FAILURE, "Unable to initialize DP targets "
--              "[%d]: %s\n", ret, sss_strerror(ret));
--        goto done;
--    }
--
-+    /* Initialize data provider bus. Data provider can receive client
-+     * registration and other D-Bus methods. However no data provider
-+     * request will be executed as long as the modules and targets
-+     * are not initialized.
-+     */
-     talloc_set_destructor(provider, dp_destructor);
- 
-     ret = dp_init_dbus_server(provider);
-@@ -118,6 +110,20 @@ errno_t dp_init(struct tevent_context *ev,
- 
-     be_ctx->provider = provider;
- 
-+    ret = dp_init_modules(provider, &provider->modules);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_CRIT_FAILURE, "Unable to initialize DP modules "
-+              "[%d]: %s\n", ret, sss_strerror(ret));
-+        goto done;
-+    }
-+
-+    ret = dp_init_targets(provider, be_ctx, provider, provider->modules);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_CRIT_FAILURE, "Unable to initialize DP targets "
-+              "[%d]: %s\n", ret, sss_strerror(ret));
-+        goto done;
-+    }
-+
-     ret = EOK;
- 
- done:
-diff --git a/src/providers/data_provider/dp_methods.c b/src/providers/data_provider/dp_methods.c
-index e4290beeeef3c0c4d156271ad88f891d3245af08..498676d1bec2da300ca4b33f7110debcbf0aac00 100644
---- a/src/providers/data_provider/dp_methods.c
-+++ b/src/providers/data_provider/dp_methods.c
-@@ -76,6 +76,12 @@ bool dp_method_enabled(struct data_provider *provider,
-         return false;
-     }
- 
-+    if (provider == NULL || provider->targets == NULL) {
-+        DEBUG(SSSDBG_TRACE_FUNC, "Target %s is not yet initialized\n",
-+              dp_target_to_string(target));
-+        return false;
-+    }
-+
-     dp_target = provider->targets[target];
-     if (dp_target == NULL || dp_target->initialized == false) {
-         DEBUG(SSSDBG_TRACE_FUNC, "Target %s is not configured\n",
--- 
-2.4.11
-
diff --git a/SOURCES/0077-tcurl-test-add-support-for-tls-settings.patch b/SOURCES/0077-tcurl-test-add-support-for-tls-settings.patch
new file mode 100644
index 0000000..d1e5926
--- /dev/null
+++ b/SOURCES/0077-tcurl-test-add-support-for-tls-settings.patch
@@ -0,0 +1,62 @@
+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-sssctl-Generic-help-for-cache-upgrade-and-config-che.patch b/SOURCES/0078-sssctl-Generic-help-for-cache-upgrade-and-config-che.patch
deleted file mode 100644
index bff746f..0000000
--- a/SOURCES/0078-sssctl-Generic-help-for-cache-upgrade-and-config-che.patch
+++ /dev/null
@@ -1,58 +0,0 @@
-From 44b93ae9e80652661da7c1f35aa5aec532e6f04d Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Michal=20=C5=BDidek?= <mzidek@redhat.com>
-Date: Tue, 26 Jul 2016 16:35:55 +0200
-Subject: [PATCH 78/82] sssctl: Generic help for cache-upgrade and config-check
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-sssctl COMMAND --help should print at least
-generic help, even if the command does not
-accept any command specific options.
-
-Resolves:
-https://fedorahosted.org/sssd/ticket/3086
-
-Reviewed-by: Pavel Březina <pbrezina@redhat.com>
----
- src/tools/sssctl/sssctl_config.c | 6 ++++++
- src/tools/sssctl/sssctl_data.c   | 6 ++++++
- 2 files changed, 12 insertions(+)
-
-diff --git a/src/tools/sssctl/sssctl_config.c b/src/tools/sssctl/sssctl_config.c
-index a66d7749c4aee9bd00c0ad2d296292658ffdb9b2..630df3c8ff5368ef253bb9753380e94c8c0a307d 100644
---- a/src/tools/sssctl/sssctl_config.c
-+++ b/src/tools/sssctl/sssctl_config.c
-@@ -47,6 +47,12 @@ errno_t sssctl_config_check(struct sss_cmdline *cmdline,
-     char **strs = NULL;
-     TALLOC_CTX *tmp_ctx = NULL;
- 
-+    ret = sss_tool_popt(cmdline, NULL, SSS_TOOL_OPT_OPTIONAL, NULL, NULL);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_CRIT_FAILURE, "Unable to parse command arguments\n");
-+        return ret;
-+    }
-+
-     tmp_ctx = talloc_new(NULL);
-     init_data = sss_ini_initdata_init(tmp_ctx);
-     if (!init_data) {
-diff --git a/src/tools/sssctl/sssctl_data.c b/src/tools/sssctl/sssctl_data.c
-index a26ddd8d5200319e75282b738791cf270f0d75a8..72823ab254344bba6f7679882a733b6ef2250525 100644
---- a/src/tools/sssctl/sssctl_data.c
-+++ b/src/tools/sssctl/sssctl_data.c
-@@ -266,6 +266,12 @@ errno_t sssctl_cache_upgrade(struct sss_cmdline *cmdline,
-     struct sysdb_upgrade_ctx db_up_ctx;
-     errno_t ret;
- 
-+    ret = sss_tool_popt(cmdline, NULL, SSS_TOOL_OPT_OPTIONAL, NULL, NULL);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_CRIT_FAILURE, "Unable to parse command arguments\n");
-+        return ret;
-+    }
-+
-     if (sss_deamon_running()) {
-         return ERR_SSSD_RUNNING;
-     }
--- 
-2.4.11
-
diff --git a/SOURCES/0078-tcurl-add-support-for-http-basic-auth.patch b/SOURCES/0078-tcurl-add-support-for-http-basic-auth.patch
new file mode 100644
index 0000000..fd6ce23
--- /dev/null
+++ b/SOURCES/0078-tcurl-add-support-for-http-basic-auth.patch
@@ -0,0 +1,112 @@
+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-NSS-Do-not-check-local-users-with-disabled-local_neg.patch b/SOURCES/0079-NSS-Do-not-check-local-users-with-disabled-local_neg.patch
deleted file mode 100644
index b4c2e9e..0000000
--- a/SOURCES/0079-NSS-Do-not-check-local-users-with-disabled-local_neg.patch
+++ /dev/null
@@ -1,166 +0,0 @@
-From acb2de04987b163d602aa02155b34c50bce93584 Mon Sep 17 00:00:00 2001
-From: Lukas Slebodnik <lslebodn@redhat.com>
-Date: Mon, 8 Aug 2016 13:55:52 +0200
-Subject: [PATCH 79/82] NSS: Do not check local users with disabled
- local_negative_timeout
-
-sssd_nss can set different negative timeout for local users
-and groups. However, checking whether user/group is local
-is quite expensive operation. We can avoid such operations
-if local_negative_timeout is not set.
-
-This fix improve performance(40%) of lookup non-existing
-entries in offline mode and with disabled local_negative_timeout.
-
-  sh$ cat pok.sh
-  for i in {1..10000}; do
-    getent passwd -s sss temp$i
-    getent group -s sss temp$i
-  done
-
-  #without patch
-  sh $time /bin/bash pok.sh
-  real    0m41.534s
-  user    0m3.580s
-  sys     0m14.202s
-
-  #with patch
-  sh $time /bin/bash pok.sh
-  real    0m26.686s
-  user    0m3.292s
-  sys     0m13.165s
-
-Resolves:
-https://fedorahosted.org/sssd/ticket/3122
-
-Reviewed-by: Petr Cech <pcech@redhat.com>
----
- src/responder/common/negcache.c | 45 ++++++++++++++++++++++++-----------------
- 1 file changed, 27 insertions(+), 18 deletions(-)
-
-diff --git a/src/responder/common/negcache.c b/src/responder/common/negcache.c
-index dfeb0d483e4db34cb2f25e1f82884611a707aabe..5b7ad69f432518be94b88e92e24265add722c852 100644
---- a/src/responder/common/negcache.c
-+++ b/src/responder/common/negcache.c
-@@ -143,7 +143,7 @@ done:
- }
- 
- static int sss_ncache_set_str(struct sss_nc_ctx *ctx, char *str,
--                              bool permanent, bool is_local)
-+                              bool permanent, bool use_local_negative)
- {
-     TDB_DATA key;
-     TDB_DATA data;
-@@ -157,15 +157,16 @@ static int sss_ncache_set_str(struct sss_nc_ctx *ctx, char *str,
-     if (permanent) {
-         timest = talloc_strdup(ctx, "0");
-     } else {
--        if (is_local == true && ctx->local_timeout > 0) {
--            timell = (unsigned long long int)time(NULL) + ctx->local_timeout;
-+        if (use_local_negative == true && ctx->local_timeout > ctx->timeout) {
-+            timell = ctx->local_timeout;
-         } else {
--            if (ctx->timeout > 0) {
--                timell = (unsigned long long int)time(NULL) + ctx->timeout;
--            } else {
-+            /* EOK is tested in cwrap based unit test */
-+            if (ctx->timeout == 0) {
-                 return EOK;
-             }
-+            timell = ctx->timeout;
-         }
-+        timell += (unsigned long long int)time(NULL);
-         timest = talloc_asprintf(ctx, "%llu", timell);
-     }
-     if (!timest) return ENOMEM;
-@@ -457,7 +458,7 @@ int sss_ncache_check_cert(struct sss_nc_ctx *ctx, const char *cert)
- static int sss_ncache_set_user_int(struct sss_nc_ctx *ctx, bool permanent,
-                                    const char *domain, const char *name)
- {
--    bool is_local;
-+    bool use_local_negative = false;
-     char *str;
-     int ret;
- 
-@@ -466,8 +467,10 @@ static int sss_ncache_set_user_int(struct sss_nc_ctx *ctx, bool permanent,
-     str = talloc_asprintf(ctx, "%s/%s/%s", NC_USER_PREFIX, domain, name);
-     if (!str) return ENOMEM;
- 
--    is_local = is_user_local_by_name(name);
--    ret = sss_ncache_set_str(ctx, str, permanent, is_local);
-+    if (ctx->local_timeout > 0) {
-+        use_local_negative = is_user_local_by_name(name);
-+    }
-+    ret = sss_ncache_set_str(ctx, str, permanent, use_local_negative);
- 
-     talloc_free(str);
-     return ret;
-@@ -476,7 +479,7 @@ static int sss_ncache_set_user_int(struct sss_nc_ctx *ctx, bool permanent,
- static int sss_ncache_set_group_int(struct sss_nc_ctx *ctx, bool permanent,
-                                     const char *domain, const char *name)
- {
--    bool is_local;
-+    bool use_local_negative = false;
-     char *str;
-     int ret;
- 
-@@ -485,8 +488,10 @@ static int sss_ncache_set_group_int(struct sss_nc_ctx *ctx, bool permanent,
-     str = talloc_asprintf(ctx, "%s/%s/%s", NC_GROUP_PREFIX, domain, name);
-     if (!str) return ENOMEM;
- 
--    is_local = is_group_local_by_name(name);
--    ret = sss_ncache_set_str(ctx, str, permanent, is_local);
-+    if (ctx->local_timeout > 0) {
-+        use_local_negative = is_group_local_by_name(name);
-+    }
-+    ret = sss_ncache_set_str(ctx, str, permanent, use_local_negative);
- 
-     talloc_free(str);
-     return ret;
-@@ -550,7 +555,7 @@ int sss_ncache_set_netgr(struct sss_nc_ctx *ctx, bool permanent,
- int sss_ncache_set_uid(struct sss_nc_ctx *ctx, bool permanent,
-                        struct sss_domain_info *dom, uid_t uid)
- {
--    bool is_local;
-+    bool use_local_negative = false;
-     char *str;
-     int ret;
- 
-@@ -562,8 +567,10 @@ int sss_ncache_set_uid(struct sss_nc_ctx *ctx, bool permanent,
-     }
-     if (!str) return ENOMEM;
- 
--    is_local = is_user_local_by_uid(uid);
--    ret = sss_ncache_set_str(ctx, str, permanent, is_local);
-+    if (ctx->local_timeout > 0) {
-+        use_local_negative = is_user_local_by_uid(uid);
-+    }
-+    ret = sss_ncache_set_str(ctx, str, permanent, use_local_negative);
- 
-     talloc_free(str);
-     return ret;
-@@ -572,7 +579,7 @@ int sss_ncache_set_uid(struct sss_nc_ctx *ctx, bool permanent,
- int sss_ncache_set_gid(struct sss_nc_ctx *ctx, bool permanent,
-                        struct sss_domain_info *dom, gid_t gid)
- {
--    bool is_local;
-+    bool use_local_negative = false;
-     char *str;
-     int ret;
- 
-@@ -584,8 +591,10 @@ int sss_ncache_set_gid(struct sss_nc_ctx *ctx, bool permanent,
-     }
-     if (!str) return ENOMEM;
- 
--    is_local = is_group_local_by_gid(gid);
--    ret = sss_ncache_set_str(ctx, str, permanent, is_local);
-+    if (ctx->local_timeout > 0) {
-+        use_local_negative = is_group_local_by_gid(gid);
-+    }
-+    ret = sss_ncache_set_str(ctx, str, permanent, use_local_negative);
- 
-     talloc_free(str);
-     return ret;
--- 
-2.4.11
-
diff --git a/SOURCES/0079-tcurl-test-allow-to-set-custom-headers.patch b/SOURCES/0079-tcurl-test-allow-to-set-custom-headers.patch
new file mode 100644
index 0000000..ac764da
--- /dev/null
+++ b/SOURCES/0079-tcurl-test-allow-to-set-custom-headers.patch
@@ -0,0 +1,63 @@
+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-config_schema-Add-ldap_user_email-to-schema.patch b/SOURCES/0080-config_schema-Add-ldap_user_email-to-schema.patch
deleted file mode 100644
index 416c768..0000000
--- a/SOURCES/0080-config_schema-Add-ldap_user_email-to-schema.patch
+++ /dev/null
@@ -1,31 +0,0 @@
-From fad4c17acbc0170110a98806886e0eeec793c0c6 Mon Sep 17 00:00:00 2001
-From: Lukas Slebodnik <lslebodn@redhat.com>
-Date: Wed, 10 Aug 2016 08:17:02 +0200
-Subject: [PATCH 80/82] config_schema: Add ldap_user_email to schema
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Resolves:
-https://fedorahosted.org/sssd/ticket/3068
-
-Reviewed-by: Fabiano Fidêncio <fidencio@redhat.com>
----
- src/config/cfg_rules.ini | 1 +
- 1 file changed, 1 insertion(+)
-
-diff --git a/src/config/cfg_rules.ini b/src/config/cfg_rules.ini
-index 635c078436e8ca47f60e8d82341cb131469fe4c9..09f53fa41eb2904f11a78af333b6d79619d2759c 100644
---- a/src/config/cfg_rules.ini
-+++ b/src/config/cfg_rules.ini
-@@ -588,6 +588,7 @@ option = ldap_user_authorized_host
- option = ldap_user_authorized_service
- option = ldap_user_auth_type
- option = ldap_user_certificate
-+option = ldap_user_email
- option = ldap_user_entry_usn
- option = ldap_user_extra_attrs
- option = ldap_user_fullname
--- 
-2.4.11
-
diff --git a/SOURCES/0080-tcurl-test-add-support-for-client-certificate.patch b/SOURCES/0080-tcurl-test-add-support-for-client-certificate.patch
new file mode 100644
index 0000000..8635100
--- /dev/null
+++ b/SOURCES/0080-tcurl-test-add-support-for-client-certificate.patch
@@ -0,0 +1,53 @@
+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-NSS-Use-correct-name-for-invalidating-memory-cache.patch b/SOURCES/0081-NSS-Use-correct-name-for-invalidating-memory-cache.patch
deleted file mode 100644
index 3cc0985..0000000
--- a/SOURCES/0081-NSS-Use-correct-name-for-invalidating-memory-cache.patch
+++ /dev/null
@@ -1,113 +0,0 @@
-From 1d0a914578ce72bad86cbe9e0beeda0c3b2d1dee Mon Sep 17 00:00:00 2001
-From: Lukas Slebodnik <lslebodn@redhat.com>
-Date: Mon, 8 Aug 2016 17:30:29 +0200
-Subject: [PATCH 81/82] NSS: Use correct name for invalidating memory cache
-
-After refactoring of sysdb, we get and internal fully qualified
-name from backend in org.freedesktop.sssd.dataprovider_rev.initgrCheck
-Previously we got short name and we created fq name in
-nss_update_initgr_memcache. Memory cache still need to use short names
-if it was specified.
-
-This patch uses right name in different places.
-
-Reviewed-by: Petr Cech <pcech@redhat.com>
-Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
----
- src/responder/nss/nsssrv_cmd.c     | 31 +++++++++++++++++--------------
- src/responder/nss/nsssrv_private.h |  2 +-
- 2 files changed, 18 insertions(+), 15 deletions(-)
-
-diff --git a/src/responder/nss/nsssrv_cmd.c b/src/responder/nss/nsssrv_cmd.c
-index f3b6ac4afb5d1571f283933b48e0256b91c56391..573959ea76fc1277fe84f40b88dcd34093da468d 100644
---- a/src/responder/nss/nsssrv_cmd.c
-+++ b/src/responder/nss/nsssrv_cmd.c
-@@ -3961,13 +3961,13 @@ done:
- }
- 
- void nss_update_initgr_memcache(struct nss_ctx *nctx,
--                                const char *name, const char *domain,
-+                                const char *fq_name, const char *domain,
-                                 int gnum, uint32_t *groups)
- {
-     TALLOC_CTX *tmp_ctx = NULL;
-     struct sss_domain_info *dom;
-     struct ldb_result *res;
--    struct sized_string delete_name;
-+    struct sized_string *delete_name;
-     bool changed = false;
-     uint32_t id;
-     uint32_t gids[gnum];
-@@ -3987,8 +3987,19 @@ void nss_update_initgr_memcache(struct nss_ctx *nctx,
-     }
- 
-     tmp_ctx = talloc_new(NULL);
-+    if (tmp_ctx == NULL) {
-+        return;
-+    }
- 
--    ret = sysdb_initgroups(tmp_ctx, dom, name, &res);
-+    ret = sized_output_name(tmp_ctx, nctx->rctx, fq_name, dom, &delete_name);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_OP_FAILURE,
-+              "sized_output_name failed for '%s': %d [%s]\n",
-+              fq_name, ret, sss_strerror(ret));
-+        goto done;
-+    }
-+
-+    ret = sysdb_initgroups(tmp_ctx, dom, fq_name, &res);
-     if (ret != EOK && ret != ENOENT) {
-         DEBUG(SSSDBG_CRIT_FAILURE,
-               "Failed to make request to our cache! [%d][%s]\n",
-@@ -4002,8 +4013,7 @@ void nss_update_initgr_memcache(struct nss_ctx *nctx,
- 
-     if (ret == ENOENT || res->count == 0) {
-         /* The user is gone. Invalidate the mc record */
--        to_sized_string(&delete_name, name);
--        ret = sss_mmap_cache_pw_invalidate(nctx->pwd_mc_ctx, &delete_name);
-+        ret = sss_mmap_cache_pw_invalidate(nctx->pwd_mc_ctx, delete_name);
-         if (ret != EOK && ret != ENOENT) {
-             DEBUG(SSSDBG_CRIT_FAILURE,
-                   "Internal failure in memory cache code: %d [%s]\n",
-@@ -4047,13 +4057,6 @@ void nss_update_initgr_memcache(struct nss_ctx *nctx,
-     }
- 
-     if (changed) {
--        char *fq_name = sss_tc_fqname(tmp_ctx, dom->names, dom, name);
--        if (!fq_name) {
--            DEBUG(SSSDBG_CRIT_FAILURE,
--                  "Could not create fq name\n");
--            goto done;
--        }
--
-         for (i = 0; i < gnum; i++) {
-             id = groups[i];
- 
-@@ -4065,9 +4068,9 @@ void nss_update_initgr_memcache(struct nss_ctx *nctx,
-             }
-         }
- 
--        to_sized_string(&delete_name, fq_name);
-+        to_sized_string(delete_name, fq_name);
-         ret = sss_mmap_cache_initgr_invalidate(nctx->initgr_mc_ctx,
--                                               &delete_name);
-+                                               delete_name);
-         if (ret != EOK && ret != ENOENT) {
-             DEBUG(SSSDBG_CRIT_FAILURE,
-                   "Internal failure in memory cache code: %d [%s]\n",
-diff --git a/src/responder/nss/nsssrv_private.h b/src/responder/nss/nsssrv_private.h
-index 79c7b7265f66f57e0ea89fe192a1da4f8992f1a3..391eaaf40f84a7436bee63fd699241e4957fdbeb 100644
---- a/src/responder/nss/nsssrv_private.h
-+++ b/src/responder/nss/nsssrv_private.h
-@@ -146,7 +146,7 @@ errno_t check_cache(struct nss_dom_ctx *dctx,
- void nss_update_pw_memcache(struct nss_ctx *nctx);
- void nss_update_gr_memcache(struct nss_ctx *nctx);
- void nss_update_initgr_memcache(struct nss_ctx *nctx,
--                                const char *name, const char *domain,
-+                                const char *fq_name, const char *domain,
-                                 int gnum, uint32_t *groups);
- 
- int nss_connection_setup(struct cli_ctx *cctx);
--- 
-2.4.11
-
diff --git a/SOURCES/0081-ci-do-not-build-secrets-on-rhel6.patch b/SOURCES/0081-ci-do-not-build-secrets-on-rhel6.patch
new file mode 100644
index 0000000..f804036
--- /dev/null
+++ b/SOURCES/0081-ci-do-not-build-secrets-on-rhel6.patch
@@ -0,0 +1,115 @@
+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-SYSDB-Avoid-optimisation-with-modifyTimestamp-for-us.patch b/SOURCES/0082-SYSDB-Avoid-optimisation-with-modifyTimestamp-for-us.patch
deleted file mode 100644
index 9e36f14..0000000
--- a/SOURCES/0082-SYSDB-Avoid-optimisation-with-modifyTimestamp-for-us.patch
+++ /dev/null
@@ -1,89 +0,0 @@
-From d3925525068798e92cee1da95dbee0f838b2f36f Mon Sep 17 00:00:00 2001
-From: Lukas Slebodnik <lslebodn@redhat.com>
-Date: Wed, 3 Aug 2016 18:48:04 +0200
-Subject: [PATCH 82/82] SYSDB: Avoid optimisation with modifyTimestamp for
- users
-
-The usage of modifyTimestamp needn't be a reliable way
-for detecting of changes in user entry in LDAP.
-The authorisation need to rely current data from LDAP
-and therefore we will temporary disable optimisation with
-modifyTimestamp and we will rather rely on deep comparison
-of attributes. In he future, it might be changed and
-responders might control the optimization level.
-
-Resolves:
-https://fedorahosted.org/sssd/ticket/3110
-
-Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
-(cherry picked from commit 00f3c5cd03625357e226552084e499965512bf53)
----
- src/db/sysdb_ops.c                     | 19 -------------------
- src/tests/cmocka/test_sysdb_ts_cache.c | 14 --------------
- 2 files changed, 33 deletions(-)
-
-diff --git a/src/db/sysdb_ops.c b/src/db/sysdb_ops.c
-index 67006c155098b9fde00a01d424014852c383a325..44fb5b70e6d33fffbca5824f831a3229254ecb57 100644
---- a/src/db/sysdb_ops.c
-+++ b/src/db/sysdb_ops.c
-@@ -1101,16 +1101,6 @@ done:
-     return ret;
- }
- 
--static errno_t sysdb_check_and_update_ts_usr(struct sss_domain_info *domain,
--                                             const char *grp_name,
--                                             struct sysdb_attrs *attrs,
--                                             uint64_t cache_timeout,
--                                             time_t now)
--{
--    return sysdb_check_and_update_ts_obj(domain, SYSDB_USER, grp_name,
--                                         attrs, cache_timeout, now);
--}
--
- static errno_t sysdb_check_and_update_ts_grp(struct sss_domain_info *domain,
-                                              const char *grp_name,
-                                              struct sysdb_attrs *attrs,
-@@ -2470,15 +2460,6 @@ int sysdb_store_user(struct sss_domain_info *domain,
-         now = time(NULL);
-     }
- 
--    ret = sysdb_check_and_update_ts_usr(domain, name, attrs,
--                                        cache_timeout, now);
--    if (ret == EOK) {
--        DEBUG(SSSDBG_TRACE_LIBS,
--              "The user record of %s did not change, only updated "
--              "the timestamp cache\n", name);
--        return EOK;
--    }
--
-     tmp_ctx = talloc_new(NULL);
-     if (!tmp_ctx) {
-         return ENOMEM;
-diff --git a/src/tests/cmocka/test_sysdb_ts_cache.c b/src/tests/cmocka/test_sysdb_ts_cache.c
-index aa857e7e4823d2d8ba1e1a794b3e2474876e9ab0..e950f88631e4c78573bbb7290edfe94b5ced57cd 100644
---- a/src/tests/cmocka/test_sysdb_ts_cache.c
-+++ b/src/tests/cmocka/test_sysdb_ts_cache.c
-@@ -980,20 +980,6 @@ static void test_sysdb_user_update(void **state)
-     assert_int_equal(cache_expire_sysdb, TEST_CACHE_TIMEOUT + TEST_NOW_2);
-     assert_int_equal(cache_expire_ts, TEST_CACHE_TIMEOUT + TEST_NOW_2);
- 
--    /* Update the same attrs and the same modifyTimestamp.
--     * Only the timestamp cache must be bumped */
--    ret = sysdb_store_user(test_ctx->tctx->dom, TEST_USER_NAME, NULL,
--                           TEST_USER_UID, TEST_USER_GID, TEST_USER_NAME,
--                           "/home/"TEST_USER_NAME, "/bin/bash", NULL,
--                           user_attrs, NULL, TEST_CACHE_TIMEOUT,
--                           TEST_NOW_3);
--    assert_int_equal(ret, EOK);
--
--    get_pw_timestamp_attrs(test_ctx, TEST_USER_NAME,
--                           &cache_expire_sysdb, &cache_expire_ts);
--    assert_int_equal(cache_expire_sysdb, TEST_CACHE_TIMEOUT + TEST_NOW_2);
--    assert_int_equal(cache_expire_ts, TEST_CACHE_TIMEOUT + TEST_NOW_3);
--
-     /* Update with different modifyTimestamp but same attrs as previously
-      * saved to the timestamp cache. We should detect the 'real' attributes
-      * are the same and only bump the timestamp cache
--- 
-2.4.11
-
diff --git a/SOURCES/0082-build-make-curl-required-by-secrets.patch b/SOURCES/0082-build-make-curl-required-by-secrets.patch
new file mode 100644
index 0000000..0880b3c
--- /dev/null
+++ b/SOURCES/0082-build-make-curl-required-by-secrets.patch
@@ -0,0 +1,65 @@
+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-SIMPLE-Do-not-parse-names-on-startup.patch b/SOURCES/0083-SIMPLE-Do-not-parse-names-on-startup.patch
deleted file mode 100644
index 8185485..0000000
--- a/SOURCES/0083-SIMPLE-Do-not-parse-names-on-startup.patch
+++ /dev/null
@@ -1,48 +0,0 @@
-From 701cabea51526c3bd40607761dc02069c9c2e499 Mon Sep 17 00:00:00 2001
-From: Jakub Hrozek <jhrozek@redhat.com>
-Date: Thu, 21 Jul 2016 12:18:01 +0200
-Subject: [PATCH 83/86] SIMPLE: Do not parse names on startup
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-It's not required to parse names on SSSD startup in the simple access
-provider. We can instead just parse the name when the access request is
-processed.
-
-Resolves:
-https://fedorahosted.org/sssd/ticket/3101
-
-Reviewed-by: Lukáš Slebodník <lslebodn@redhat.com>
----
- src/providers/simple/simple_access.c | 7 -------
- 1 file changed, 7 deletions(-)
-
-diff --git a/src/providers/simple/simple_access.c b/src/providers/simple/simple_access.c
-index cb72ada20727c63452936647876ef297106e17b0..ae90215351fe7db834898067d3b4bad71015ec5f 100644
---- a/src/providers/simple/simple_access.c
-+++ b/src/providers/simple/simple_access.c
-@@ -284,7 +284,6 @@ errno_t sssm_simple_access_init(TALLOC_CTX *mem_ctx,
-                                 struct dp_method *dp_methods)
- {
-     struct simple_ctx *ctx;
--    errno_t ret;
- 
-     ctx = talloc_zero(mem_ctx, struct simple_ctx);
-     if (ctx == NULL) {
-@@ -296,12 +295,6 @@ errno_t sssm_simple_access_init(TALLOC_CTX *mem_ctx,
-     ctx->be_ctx = be_ctx;
-     ctx->last_refresh_of_filter_lists = 0;
- 
--    ret = simple_access_obtain_filter_lists(ctx);
--    if (ret != EOK) {
--        talloc_free(ctx);
--        return ret;
--    }
--
-     dp_set_method(dp_methods, DPM_ACCESS_HANDLER,
-                   simple_access_handler_send, simple_access_handler_recv, ctx,
-                   struct simple_ctx, struct pam_data, struct pam_data *);
--- 
-2.4.11
-
diff --git a/SOURCES/0083-secrets-use-tcurl-in-proxy-provider.patch b/SOURCES/0083-secrets-use-tcurl-in-proxy-provider.patch
new file mode 100644
index 0000000..7292536
--- /dev/null
+++ b/SOURCES/0083-secrets-use-tcurl-in-proxy-provider.patch
@@ -0,0 +1,459 @@
+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-SIMPLE-Fail-on-any-error-parsing-the-access-control-.patch b/SOURCES/0084-SIMPLE-Fail-on-any-error-parsing-the-access-control-.patch
deleted file mode 100644
index 375b95e..0000000
--- a/SOURCES/0084-SIMPLE-Fail-on-any-error-parsing-the-access-control-.patch
+++ /dev/null
@@ -1,40 +0,0 @@
-From b40c53b524816f9308c90d79662f887e6a2ac1eb Mon Sep 17 00:00:00 2001
-From: Jakub Hrozek <jhrozek@redhat.com>
-Date: Thu, 21 Jul 2016 13:33:18 +0200
-Subject: [PATCH 84/86] SIMPLE: Fail on any error parsing the access control
- list
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Luckily this error was hidden by the fact that SSSD didn't start at all
-when an unparseable name was encountered after startup. Otherwise, this
-would have been a security issue.
-
-Nonetheless, we should just fail and deny access if we can't parse a
-name in a simple access list.
-
-Reviewed-by: Lukáš Slebodník <lslebodn@redhat.com>
----
- src/providers/simple/simple_access.c | 5 ++++-
- 1 file changed, 4 insertions(+), 1 deletion(-)
-
-diff --git a/src/providers/simple/simple_access.c b/src/providers/simple/simple_access.c
-index ae90215351fe7db834898067d3b4bad71015ec5f..577e8354e9b574764734248b2bde4ef06c6fb4fc 100644
---- a/src/providers/simple/simple_access.c
-+++ b/src/providers/simple/simple_access.c
-@@ -211,7 +211,10 @@ simple_access_handler_send(TALLOC_CTX *mem_ctx,
- 
-         ret = simple_access_obtain_filter_lists(simple_ctx);
-         if (ret != EOK) {
--            DEBUG(SSSDBG_MINOR_FAILURE, "Failed to refresh filter lists\n");
-+            DEBUG(SSSDBG_CRIT_FAILURE,
-+                  "Failed to refresh filter lists, denying all access\n");
-+            pd->pam_status = PAM_PERM_DENIED;
-+            goto immediately;
-         }
-         simple_ctx->last_refresh_of_filter_lists = now;
-     }
--- 
-2.4.11
-
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
new file mode 100644
index 0000000..7245f94
--- /dev/null
+++ b/SOURCES/0084-secrets-remove-http-parser-code-in-proxy-provider.patch
@@ -0,0 +1,612 @@
+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-SIMPLE-Make-the-DP-handlers-testable.patch b/SOURCES/0085-SIMPLE-Make-the-DP-handlers-testable.patch
deleted file mode 100644
index 62c43fa..0000000
--- a/SOURCES/0085-SIMPLE-Make-the-DP-handlers-testable.patch
+++ /dev/null
@@ -1,113 +0,0 @@
-From ab6ee9104826e286e0c67c9176c08b9a239e0622 Mon Sep 17 00:00:00 2001
-From: Jakub Hrozek <jhrozek@redhat.com>
-Date: Tue, 26 Jul 2016 12:13:43 +0200
-Subject: [PATCH 85/86] SIMPLE: Make the DP handlers testable
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-To make it possible to call the whole DP handler in the unit test, not
-just the evaluator part.
-
-Reviewed-by: Lukáš Slebodník <lslebodn@redhat.com>
----
- Makefile.am                              |  1 +
- src/providers/simple/simple_access.c     |  5 ++--
- src/providers/simple/simple_access_pvt.h | 43 ++++++++++++++++++++++++++++++++
- 3 files changed, 47 insertions(+), 2 deletions(-)
- create mode 100644 src/providers/simple/simple_access_pvt.h
-
-diff --git a/Makefile.am b/Makefile.am
-index cefd9a43442fc19933f1e373d4f2ed4bb3ba3201..ee9b48c666a44781b582ba5d83102b705e898f29 100644
---- a/Makefile.am
-+++ b/Makefile.am
-@@ -665,6 +665,7 @@ dist_noinst_HEADERS = \
-     src/providers/fail_over_srv.h \
-     src/util/child_common.h \
-     src/providers/simple/simple_access.h \
-+    src/providers/simple/simple_access_pvt.h \
-     src/providers/krb5/krb5_auth.h \
-     src/providers/krb5/krb5_common.h \
-     src/providers/krb5/krb5_utils.h \
-diff --git a/src/providers/simple/simple_access.c b/src/providers/simple/simple_access.c
-index 577e8354e9b574764734248b2bde4ef06c6fb4fc..521beee84833b47b547bd1045c24e3384aa4d9a5 100644
---- a/src/providers/simple/simple_access.c
-+++ b/src/providers/simple/simple_access.c
-@@ -22,6 +22,7 @@
- #include <security/pam_modules.h>
- 
- #include "providers/simple/simple_access.h"
-+#include "providers/simple/simple_access_pvt.h"
- #include "util/sss_utf8.h"
- #include "providers/backend.h"
- #include "db/sysdb.h"
-@@ -176,7 +177,7 @@ struct simple_access_handler_state {
- 
- static void simple_access_handler_done(struct tevent_req *subreq);
- 
--static struct tevent_req *
-+struct tevent_req *
- simple_access_handler_send(TALLOC_CTX *mem_ctx,
-                            struct simple_ctx *simple_ctx,
-                            struct pam_data *pd,
-@@ -265,7 +266,7 @@ done:
-     tevent_req_done(req);
- }
- 
--static errno_t
-+errno_t
- simple_access_handler_recv(TALLOC_CTX *mem_ctx,
-                        struct tevent_req *req,
-                        struct pam_data **_data)
-diff --git a/src/providers/simple/simple_access_pvt.h b/src/providers/simple/simple_access_pvt.h
-new file mode 100644
-index 0000000000000000000000000000000000000000..c133e1c5531be35861178e0b23aa7a09db9f7703
---- /dev/null
-+++ b/src/providers/simple/simple_access_pvt.h
-@@ -0,0 +1,43 @@
-+/*
-+   SSSD
-+
-+   Simple access control
-+
-+   Copyright (C) Sumit Bose <sbose@redhat.com> 2010
-+
-+   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 __SIMPLE_ACCESS_PVT_H__
-+#define __SIMPLE_ACCESS_PVT_H__
-+
-+#include "providers/data_provider/dp.h"
-+
-+/* We only 'export' the functions in a private header file to be able to call
-+ * them from unit tests
-+ */
-+struct tevent_req *
-+simple_access_handler_send(TALLOC_CTX *mem_ctx,
-+                           struct simple_ctx *simple_ctx,
-+                           struct pam_data *pd,
-+                           struct dp_req_params *params);
-+
-+errno_t
-+simple_access_handler_recv(TALLOC_CTX *mem_ctx,
-+                           struct tevent_req *req,
-+                           struct pam_data **_data);
-+
-+int simple_access_obtain_filter_lists(struct simple_ctx *ctx);
-+
-+#endif /* __SIMPLE_ACCESS_PVT_H__ */
--- 
-2.4.11
-
diff --git a/SOURCES/0085-secrets-allow-to-configure-certificate-check.patch b/SOURCES/0085-secrets-allow-to-configure-certificate-check.patch
new file mode 100644
index 0000000..dd9bea7
--- /dev/null
+++ b/SOURCES/0085-secrets-allow-to-configure-certificate-check.patch
@@ -0,0 +1,254 @@
+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-TESTS-Use-the-DP-handlers-in-simple-provider-tests-a.patch b/SOURCES/0086-TESTS-Use-the-DP-handlers-in-simple-provider-tests-a.patch
deleted file mode 100644
index 4eed171..0000000
--- a/SOURCES/0086-TESTS-Use-the-DP-handlers-in-simple-provider-tests-a.patch
+++ /dev/null
@@ -1,308 +0,0 @@
-From 157781d8a05975c034858a38d2c00cdd94d374b0 Mon Sep 17 00:00:00 2001
-From: Jakub Hrozek <jhrozek@redhat.com>
-Date: Tue, 26 Jul 2016 12:14:47 +0200
-Subject: [PATCH 86/86] TESTS: Use the DP handlers in simple provider tests,
- add more tests
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Use the full simple access control handlers, just like SSSD does in the
-tests.
-
-Reviewed-by: Lukáš Slebodník <lslebodn@redhat.com>
----
- src/tests/cmocka/test_simple_access.c | 186 ++++++++++++++++++++++++++++++----
- 1 file changed, 165 insertions(+), 21 deletions(-)
-
-diff --git a/src/tests/cmocka/test_simple_access.c b/src/tests/cmocka/test_simple_access.c
-index 1f093cf7fee4d046dcc727aa8815fb9aafb68c52..d41bb295e24ef2104cf17636f37d487fd14d0b93 100644
---- a/src/tests/cmocka/test_simple_access.c
-+++ b/src/tests/cmocka/test_simple_access.c
-@@ -21,12 +21,14 @@
- #include <tevent.h>
- #include <errno.h>
- #include <popt.h>
-+#include <security/pam_appl.h>
- 
- #include "tests/cmocka/common_mock.h"
- #include "tests/cmocka/common_mock_be.h"
- #include "tests/cmocka/common_mock_resp.h"
- #include "db/sysdb_private.h"   /* new_subdomain() */
- #include "providers/simple/simple_access.h"
-+#include "providers/simple/simple_access_pvt.h"
- 
- #define TESTS_PATH "tp_" BASE_FILE_STEM
- #define TEST_CONF_DB "test_simple_conf.ldb"
-@@ -34,8 +36,6 @@
- #define TEST_SUBDOM_NAME "test.subdomain"
- #define TEST_ID_PROVIDER "ldap"
- 
--int simple_access_obtain_filter_lists(struct simple_ctx *ctx);
--
- struct simple_test_ctx {
-     struct sss_test_ctx *tctx;
-     struct be_ctx *be_ctx;
-@@ -43,6 +43,8 @@ struct simple_test_ctx {
- 
-     bool access_granted;
-     struct simple_ctx *ctx;
-+    struct pam_data *pd;
-+    struct dp_req_params *params;
- };
- 
- static int test_simple_setup(struct sss_test_conf_param params[], void **state)
-@@ -75,6 +77,19 @@ static int test_simple_setup(struct sss_test_conf_param params[], void **state)
-         return ENOMEM;
-     }
- 
-+    simple_test_ctx->pd = talloc_zero(simple_test_ctx, struct pam_data);
-+    if (simple_test_ctx->pd == NULL) {
-+        return ENOMEM;
-+    }
-+    simple_test_ctx->pd->cmd = SSS_PAM_ACCT_MGMT;
-+
-+    simple_test_ctx->params = talloc_zero(simple_test_ctx,
-+                                          struct dp_req_params);
-+    if (simple_test_ctx->params == NULL) {
-+        return ENOMEM;
-+    }
-+    simple_test_ctx->params->ev = simple_test_ctx->tctx->ev;
-+
-     *state = simple_test_ctx;
-     return 0;
- }
-@@ -122,7 +137,7 @@ static int setup_with_params(struct simple_test_ctx *test_ctx,
-         return ret;
-     }
- 
--    test_ctx->ctx = talloc(test_ctx, struct simple_ctx);
-+    test_ctx->ctx = talloc_zero(test_ctx, struct simple_ctx);
-     if (test_ctx->ctx == NULL) {
-         return ENOMEM;
-     }
-@@ -130,11 +145,6 @@ static int setup_with_params(struct simple_test_ctx *test_ctx,
-     test_ctx->ctx->be_ctx = test_ctx->be_ctx;
-     test_ctx->ctx->domain = test_ctx->tctx->dom;
- 
--    ret = simple_access_obtain_filter_lists(test_ctx->ctx);
--    if (ret != EOK) {
--        return ret;
--    }
--
-     return EOK;
- }
- 
-@@ -155,13 +165,14 @@ static int simple_test_teardown(void **state)
-     return 0;
- }
- 
--static void simple_access_check_done(struct tevent_req *req)
-+static void simple_access_handler_done(struct tevent_req *req)
- {
-     struct simple_test_ctx *simple_test_ctx =
-                         tevent_req_callback_data(req, struct simple_test_ctx);
- 
--    simple_test_ctx->tctx->error = simple_access_check_recv(req,
--                                              &simple_test_ctx->access_granted);
-+    simple_test_ctx->tctx->error = simple_access_handler_recv(simple_test_ctx,
-+                                                    req, &simple_test_ctx->pd);
-+    simple_test_ctx->access_granted = (simple_test_ctx->pd->pam_status == PAM_SUCCESS);
-     talloc_free(req);
-     simple_test_ctx->tctx->done = true;
- }
-@@ -175,10 +186,13 @@ static void run_simple_access_check(struct simple_test_ctx *simple_test_ctx,
-     struct tevent_req *req;
- 
-     simple_test_ctx->tctx->done = false;
--    req = simple_access_check_send(simple_test_ctx, simple_test_ctx->tctx->ev,
--                                   simple_test_ctx->ctx, username);
-+    simple_test_ctx->pd->user = discard_const(username);
-+    req = simple_access_handler_send(simple_test_ctx,
-+                                     simple_test_ctx->ctx,
-+                                     simple_test_ctx->pd,
-+                                     simple_test_ctx->params);
-     assert_non_null(req);
--    tevent_req_set_callback(req, simple_access_check_done, simple_test_ctx);
-+    tevent_req_set_callback(req, simple_access_handler_done, simple_test_ctx);
- 
-     ret = test_ev_loop(simple_test_ctx->tctx);
-     assert_int_equal(ret, expected_rv);
-@@ -487,23 +501,29 @@ static void test_group_allow_empty(void **state)
-         { NULL, NULL },
-     };
- 
--    ret = setup_with_params(simple_test_ctx, simple_test_ctx->tctx->dom, params);
-+    ret = setup_with_params(simple_test_ctx,
-+                            simple_test_ctx->tctx->dom,
-+                            params);
-     assert_int_equal(ret, EOK);
- 
--    req = simple_access_check_send(simple_test_ctx, simple_test_ctx->tctx->ev,
--                                   simple_test_ctx->ctx, "u1@simple_test");
-+    simple_test_ctx->pd->user = discard_const("u1@simple_test");
-+    req = simple_access_handler_send(simple_test_ctx, simple_test_ctx->ctx,
-+                                     simple_test_ctx->pd,
-+                                     simple_test_ctx->params);
-     assert_non_null(req);
--    tevent_req_set_callback(req, simple_access_check_done, simple_test_ctx);
-+    tevent_req_set_callback(req, simple_access_handler_done, simple_test_ctx);
- 
-     ret = test_ev_loop(simple_test_ctx->tctx);
-     assert_int_equal(ret, EOK);
-     assert_false(simple_test_ctx->access_granted);
- 
-     simple_test_ctx->tctx->done = false;
--    req = simple_access_check_send(simple_test_ctx, simple_test_ctx->tctx->ev,
--                                   simple_test_ctx->ctx, "u3@simple_test");
-+    simple_test_ctx->pd->user = discard_const("u3@simple_test");
-+    req = simple_access_handler_send(simple_test_ctx, simple_test_ctx->ctx,
-+                                     simple_test_ctx->pd,
-+                                     simple_test_ctx->params);
-     assert_non_null(req);
--    tevent_req_set_callback(req, simple_access_check_done, simple_test_ctx);
-+    tevent_req_set_callback(req, simple_access_handler_done, simple_test_ctx);
- 
-     ret = test_ev_loop(simple_test_ctx->tctx);
-     assert_int_equal(ret, EOK);
-@@ -584,6 +604,118 @@ static void test_group_allow_case_insensitive(void **state)
-     run_simple_access_check(simple_test_ctx, "u1@simple_test", EOK, true);
- }
- 
-+static void test_unparseable_allow_user(void **state)
-+{
-+    errno_t ret;
-+    struct simple_test_ctx *simple_test_ctx = \
-+                            talloc_get_type(*state, struct simple_test_ctx);
-+    struct sss_test_conf_param params[] = {
-+        { "simple_allow_users", "u1, user@no.such.domain" },
-+        { NULL, NULL },
-+    };
-+
-+    ret = setup_with_params(simple_test_ctx,
-+                            simple_test_ctx->tctx->dom,
-+                            params);
-+    assert_int_equal(ret, EOK);
-+
-+    /* Case-sensitive domain, wrong case */
-+    simple_test_ctx->tctx->done = false;
-+    simple_test_ctx->tctx->dom->case_sensitive = false;
-+    /* A user that would normally be denied access will be denied because
-+     * the access list can't be parsed
-+     */
-+    run_simple_access_check(simple_test_ctx, "u2@simple_test", EOK, false);
-+    /* A user that would normally be allowed access will be denied because
-+     * the access list can't be parsed
-+     */
-+    run_simple_access_check(simple_test_ctx, "u1@simple_test", EOK, false);
-+}
-+
-+static void test_unparseable_deny_user(void **state)
-+{
-+    errno_t ret;
-+    struct simple_test_ctx *simple_test_ctx = \
-+                            talloc_get_type(*state, struct simple_test_ctx);
-+    struct sss_test_conf_param params[] = {
-+        { "simple_deny_users", "u2, user@no.such.domain" },
-+        { NULL, NULL },
-+    };
-+
-+    ret = setup_with_params(simple_test_ctx,
-+                            simple_test_ctx->tctx->dom,
-+                            params);
-+    assert_int_equal(ret, EOK);
-+
-+    /* Case-sensitive domain, wrong case */
-+    simple_test_ctx->tctx->done = false;
-+    simple_test_ctx->tctx->dom->case_sensitive = false;
-+    /* A user that would normally be denied access will be denied because
-+     * the access list can't be parsed
-+     */
-+    run_simple_access_check(simple_test_ctx, "u2@simple_test", EOK, false);
-+    /* A user that would normally be allowed access will be denied because
-+     * the access list can't be parsed
-+     */
-+    run_simple_access_check(simple_test_ctx, "u1@simple_test", EOK, false);
-+}
-+
-+static void test_unparseable_allow_group(void **state)
-+{
-+    errno_t ret;
-+    struct simple_test_ctx *simple_test_ctx = \
-+                            talloc_get_type(*state, struct simple_test_ctx);
-+    struct sss_test_conf_param params[] = {
-+        { "simple_allow_groups", "g1, group@no.such.domain" },
-+        { NULL, NULL },
-+    };
-+
-+    ret = setup_with_params(simple_test_ctx,
-+                            simple_test_ctx->tctx->dom,
-+                            params);
-+    assert_int_equal(ret, EOK);
-+
-+    /* Case-sensitive domain, wrong case */
-+    simple_test_ctx->tctx->done = false;
-+    simple_test_ctx->tctx->dom->case_sensitive = false;
-+    /* A group that would normally be denied access will be denied because
-+     * the access list can't be parsed
-+     */
-+    run_simple_access_check(simple_test_ctx, "u2@simple_test", EOK, false);
-+    /* A group that would normally be allowed access will be denied because
-+     * the access list can't be parsed
-+     */
-+    run_simple_access_check(simple_test_ctx, "u1@simple_test", EOK, false);
-+}
-+
-+static void test_unparseable_deny_group(void **state)
-+{
-+    errno_t ret;
-+    struct simple_test_ctx *simple_test_ctx = \
-+                            talloc_get_type(*state, struct simple_test_ctx);
-+    struct sss_test_conf_param params[] = {
-+        { "simple_deny_groups", "g2, group@no.such.domain" },
-+        { NULL, NULL },
-+    };
-+
-+    ret = setup_with_params(simple_test_ctx,
-+                            simple_test_ctx->tctx->dom,
-+                            params);
-+    assert_int_equal(ret, EOK);
-+
-+    /* Case-sensitive domain, wrong case */
-+    simple_test_ctx->tctx->done = false;
-+    simple_test_ctx->tctx->dom->case_sensitive = false;
-+    /* A group that would normally be denied access will be denied because
-+     * the access list can't be parsed
-+     */
-+    run_simple_access_check(simple_test_ctx, "u2@simple_test", EOK, false);
-+    /* A group that would normally be allowed access will be denied because
-+     * the access list can't be parsed
-+     */
-+    run_simple_access_check(simple_test_ctx, "u1@simple_test", EOK, false);
-+}
-+
- static void test_group_space(void **state)
- {
-     errno_t ret;
-@@ -659,6 +791,18 @@ int main(int argc, const char *argv[])
-         cmocka_unit_test_setup_teardown(test_group_space,
-                                         simple_group_test_setup,
-                                         simple_group_test_teardown),
-+        cmocka_unit_test_setup_teardown(test_unparseable_allow_user,
-+                                        simple_test_setup,
-+                                        simple_test_teardown),
-+        cmocka_unit_test_setup_teardown(test_unparseable_deny_user,
-+                                        simple_test_setup,
-+                                        simple_test_teardown),
-+        cmocka_unit_test_setup_teardown(test_unparseable_allow_group,
-+                                        simple_test_setup,
-+                                        simple_test_teardown),
-+        cmocka_unit_test_setup_teardown(test_unparseable_deny_group,
-+                                        simple_test_setup,
-+                                        simple_test_teardown),
-     };
- 
-     /* Set debug level to invalid value so we can decide if -d 0 was used. */
--- 
-2.4.11
-
diff --git a/SOURCES/0086-secrets-support-HTTP-basic-authentication-with-proxy.patch b/SOURCES/0086-secrets-support-HTTP-basic-authentication-with-proxy.patch
new file mode 100644
index 0000000..f048f94
--- /dev/null
+++ b/SOURCES/0086-secrets-support-HTTP-basic-authentication-with-proxy.patch
@@ -0,0 +1,39 @@
+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-gpo-gPCMachineExtensionNames-with-just-whitespaces.patch b/SOURCES/0087-gpo-gPCMachineExtensionNames-with-just-whitespaces.patch
deleted file mode 100644
index d36a75a..0000000
--- a/SOURCES/0087-gpo-gPCMachineExtensionNames-with-just-whitespaces.patch
+++ /dev/null
@@ -1,91 +0,0 @@
-From 662af3eaefdb11aff02947c0d34d31ba37c7b09c Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Michal=20=C5=BDidek?= <mzidek@redhat.com>
-Date: Fri, 29 Jul 2016 16:09:16 +0200
-Subject: [PATCH 87/87] gpo: gPCMachineExtensionNames with just whitespaces
-
-Resolves:
-https://fedorahosted.org/sssd/ticket/3114
-
-We failed GPO procesing if the gPCMachineExtensionNames
-attribute contained just whitespaces. This coused
-failures in some server settings.
-
-Comment from Alexander Bokovoy quoting:
-
-You should use MS-GPOL spec. 2.2.4 'GPO Search' section says that when
-processing gPCMachineExtensionNames, "Group Policy processing terminates
-at the first <CSE GUIDn> out of sequence."
-Since ' ' (space only) does not fall into defined syntax for
-gPCMachineExtensionNames, this Group Policy processing is stopped and
-its CSE GUIDs are set to 'empty list'.
-
-Because of the 3.2.5.1.10 'Extension Protocol Sequences' language
-------------------------------------------------------------------------
-The Group Policy client MUST evaluate the subset of the abstract element
-Filtered GPO list separately for each Group Policy extension by
-including in the subset only those GPOs whose gPCUserExtensionNames (for
-user policy mode) or gPCMachineExtensionNames (for computer policy mode)
-attributes contain CSE GUID that correspond to the Group Policy
-extension. If the CSE GUID corresponding to the Group Policy extension
-is present in Extension List, it is invoked using the
-Implementation Identifier field. Applicability is determined as
-specified in section 3.2.1.5. The Group Policy Registry Extension MUST
-always execute first. All other applicable Group Policy extensions in
-the Extension List MUST be loaded and executed in Extension List order.
-A failure in any Group Policy extension sequence MUST NOT affect the
-execution of other Group Policy extensions.
--------------------------------------------------------------------------
-
-I think we can practically treat wrong content of
-gPCMachineExtensionNames (and gPCUserExtensionNames) as inability of the
-GPO to pass through the Filtered GPO list. Thus, the GPO would be
-ignored.
-
-Reviewed-by: Alexander Bokovoy <abokovoy@redhat.com>
-Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
----
- src/providers/ad/ad_gpo.c | 21 ++++++++++++++++++++-
- 1 file changed, 20 insertions(+), 1 deletion(-)
-
-diff --git a/src/providers/ad/ad_gpo.c b/src/providers/ad/ad_gpo.c
-index f609d28136918adfe6a8d5e95319b27ffcab79c0..63c68ce35922ca0407ae6ea32c0a78100e14504b 100644
---- a/src/providers/ad/ad_gpo.c
-+++ b/src/providers/ad/ad_gpo.c
-@@ -3765,6 +3765,24 @@ done:
-    }
- }
- 
-+static bool machine_ext_names_is_blank(char *attr_value)
-+{
-+    char *ptr;
-+
-+    if (attr_value == NULL) {
-+        return true;
-+    }
-+
-+    ptr = attr_value;
-+    for (; *ptr != '\0'; ptr++) {
-+        if (!isspace(*ptr)) {
-+            return false;
-+        }
-+    }
-+
-+    return true;
-+}
-+
- static errno_t
- ad_gpo_sd_process_attrs(struct tevent_req *req,
-                         char *smb_host,
-@@ -3880,7 +3898,8 @@ ad_gpo_sd_process_attrs(struct tevent_req *req,
-         goto done;
-     }
- 
--    if ((ret == ENOENT) || (el->num_values == 0)) {
-+    if ((ret == ENOENT) || (el->num_values == 0)
-+            || machine_ext_names_is_blank((char *) el[0].values[0].data)) {
-         /*
-          * if gpo has no machine_ext_names (which is perfectly valid: it could
-          * have only user_ext_names, for example), we continue to next gpo
--- 
-2.4.11
-
diff --git a/SOURCES/0087-secrets-fix-debug-message.patch b/SOURCES/0087-secrets-fix-debug-message.patch
new file mode 100644
index 0000000..19e563b
--- /dev/null
+++ b/SOURCES/0087-secrets-fix-debug-message.patch
@@ -0,0 +1,29 @@
+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-secrets-always-add-Content-Length-header.patch b/SOURCES/0088-secrets-always-add-Content-Length-header.patch
new file mode 100644
index 0000000..d941ad3
--- /dev/null
+++ b/SOURCES/0088-secrets-always-add-Content-Length-header.patch
@@ -0,0 +1,112 @@
+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/0088-sss_ini-Change-debug-level-of-config-error-msgs.patch b/SOURCES/0088-sss_ini-Change-debug-level-of-config-error-msgs.patch
deleted file mode 100644
index 1ed8612..0000000
--- a/SOURCES/0088-sss_ini-Change-debug-level-of-config-error-msgs.patch
+++ /dev/null
@@ -1,44 +0,0 @@
-From d049533953665fa3494a4932aeae7705ff84e107 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Michal=20=C5=BDidek?= <mzidek@redhat.com>
-Date: Wed, 27 Jul 2016 14:14:33 +0200
-Subject: [PATCH 088/102] sss_ini: Change debug level of config error msgs
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Syntax errors in configuration files
-prevent SSSD or sssctl to start completely.
-It would be good to display these errors
-by default with the highest level.
-
-Reviewed-by: Petr Čech <pcech@redhat.com>
-(cherry picked from commit 9dc081500979616f9af623ebe2d52837c211759f)
----
- src/util/sss_ini.c | 4 ++--
- 1 file changed, 2 insertions(+), 2 deletions(-)
-
-diff --git a/src/util/sss_ini.c b/src/util/sss_ini.c
-index d9bc46ad71fa820fd5f462cf71036213171fa92a..e56006c05555d6e0c5e726e83771abce5a72b139 100644
---- a/src/util/sss_ini.c
-+++ b/src/util/sss_ini.c
-@@ -182,7 +182,7 @@ int sss_ini_get_mtime(struct sss_ini_initdata *init_data,
- 
- /* Print ini_config errors */
- 
--void sss_ini_config_print_errors(char **error_list)
-+static void sss_ini_config_print_errors(char **error_list)
- {
- #ifdef HAVE_LIBINI_CONFIG_V1
-     unsigned count = 0;
-@@ -192,7 +192,7 @@ void sss_ini_config_print_errors(char **error_list)
-     }
- 
-     while (error_list[count]) {
--        DEBUG(SSSDBG_CRIT_FAILURE, "%s\n", error_list[count]);
-+        DEBUG(SSSDBG_FATAL_FAILURE, "%s\n", error_list[count]);
-         count++;
-     }
- #endif
--- 
-2.4.11
-
diff --git a/SOURCES/0089-CONFIG-full_name_format-is-an-allowed-option-for-all.patch b/SOURCES/0089-CONFIG-full_name_format-is-an-allowed-option-for-all.patch
deleted file mode 100644
index 6c52b00..0000000
--- a/SOURCES/0089-CONFIG-full_name_format-is-an-allowed-option-for-all.patch
+++ /dev/null
@@ -1,77 +0,0 @@
-From c54b38573a93d1176a44935ba310e590904edb92 Mon Sep 17 00:00:00 2001
-From: Jakub Hrozek <jhrozek@redhat.com>
-Date: Fri, 12 Aug 2016 13:23:16 +0200
-Subject: [PATCH 089/102] CONFIG: full_name_format is an allowed option for all
- domains
-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 cc4d1af16820b15595b60c3df15220fb852eb897)
----
- 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 +
- 4 files changed, 5 insertions(+)
-
-diff --git a/src/config/SSSDConfig/__init__.py.in b/src/config/SSSDConfig/__init__.py.in
-index 7856c4c6b2d675b7f7f0f5f2048086044e8fb5ea..3114d1da9fe8e833ae4050ac343ba2763dc56e68 100644
---- a/src/config/SSSDConfig/__init__.py.in
-+++ b/src/config/SSSDConfig/__init__.py.in
-@@ -163,6 +163,7 @@ option_strings = {
-     'subdomain_refresh_interval' : _('How often should subdomains list be refreshed'),
-     'subdomain_inherit' : _('List of options that should be inherited into a subdomain'),
-     'cached_auth_timeout' : _('How long can cached credentials be used for cached authentication'),
-+    'full_name_format' : _('Printf-compatible format for displaying fully-qualified names'),
- 
-     # [provider/ipa]
-     'ipa_domain' : _('IPA domain'),
-diff --git a/src/config/SSSDConfigTest.py b/src/config/SSSDConfigTest.py
-index 332d8702d983b6ec8bf12ec781a1bbf296b552e0..bfb247ac45f752397000d43c54a20e57c6fbd9a5 100755
---- a/src/config/SSSDConfigTest.py
-+++ b/src/config/SSSDConfigTest.py
-@@ -560,6 +560,7 @@ class SSSDConfigTestSSSDDomain(unittest.TestCase):
-             'realmd_tags',
-             'subdomain_refresh_interval',
-             'subdomain_inherit',
-+            'full_name_format',
-             'cached_auth_timeout']
- 
-         self.assertTrue(type(options) == dict,
-@@ -927,6 +928,7 @@ class SSSDConfigTestSSSDDomain(unittest.TestCase):
-             'realmd_tags',
-             'subdomain_refresh_interval',
-             'subdomain_inherit',
-+            'full_name_format',
-             'cached_auth_timeout']
- 
-         self.assertTrue(type(options) == dict,
-diff --git a/src/config/cfg_rules.ini b/src/config/cfg_rules.ini
-index 09f53fa41eb2904f11a78af333b6d79619d2759c..febe4289832f3778b7e974ef4e8b3f6d9d8bffd8 100644
---- a/src/config/cfg_rules.ini
-+++ b/src/config/cfg_rules.ini
-@@ -287,6 +287,7 @@ option = subdomain_refresh_interval
- option = subdomain_inherit
- option = cached_auth_timeout
- option = wildcard_limit
-+option = full_name_format
- 
- #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 737f0e149d56bd07b078cb83acbc43ea2ed3a057..2a43b8b68051c5133abc65a78f0d4a5561e760bd 100644
---- a/src/config/etc/sssd.api.conf
-+++ b/src/config/etc/sssd.api.conf
-@@ -147,6 +147,7 @@ realmd_tags = str, None, false
- subdomain_refresh_interval = int, None, false
- subdomain_inherit = str, None, false
- cached_auth_timeout = int, None, false
-+full_name_format = str, None, false
- 
- #Entry cache timeouts
- entry_cache_user_timeout = int, None, false
--- 
-2.4.11
-
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
new file mode 100644
index 0000000..2524495
--- /dev/null
+++ b/SOURCES/0089-sss_iobuf-fix-read-shadows-a-global-declaration.patch
@@ -0,0 +1,40 @@
+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-CONFIG-re_expression-is-an-allowed-option-for-all-do.patch b/SOURCES/0090-CONFIG-re_expression-is-an-allowed-option-for-all-do.patch
deleted file mode 100644
index 61b409a..0000000
--- a/SOURCES/0090-CONFIG-re_expression-is-an-allowed-option-for-all-do.patch
+++ /dev/null
@@ -1,77 +0,0 @@
-From 92302f81c25bc2b5ce9dbc2236c671d591fe69e0 Mon Sep 17 00:00:00 2001
-From: Jakub Hrozek <jhrozek@redhat.com>
-Date: Fri, 12 Aug 2016 16:41:21 +0200
-Subject: [PATCH 090/102] CONFIG: re_expression is an allowed option for all
- domains
-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 6d19051c50c10fc4de056ebb385c63ec0ed221cb)
----
- 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 +
- 4 files changed, 5 insertions(+)
-
-diff --git a/src/config/SSSDConfig/__init__.py.in b/src/config/SSSDConfig/__init__.py.in
-index 3114d1da9fe8e833ae4050ac343ba2763dc56e68..ac538788b9878dc2613cb48b7483d392cca41d47 100644
---- a/src/config/SSSDConfig/__init__.py.in
-+++ b/src/config/SSSDConfig/__init__.py.in
-@@ -164,6 +164,7 @@ option_strings = {
-     'subdomain_inherit' : _('List of options that should be inherited into a subdomain'),
-     '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'),
- 
-     # [provider/ipa]
-     'ipa_domain' : _('IPA domain'),
-diff --git a/src/config/SSSDConfigTest.py b/src/config/SSSDConfigTest.py
-index bfb247ac45f752397000d43c54a20e57c6fbd9a5..00c688f1e57c5f481d3adba2fe0374145216bc33 100755
---- a/src/config/SSSDConfigTest.py
-+++ b/src/config/SSSDConfigTest.py
-@@ -561,6 +561,7 @@ class SSSDConfigTestSSSDDomain(unittest.TestCase):
-             'subdomain_refresh_interval',
-             'subdomain_inherit',
-             'full_name_format',
-+            're_expression',
-             'cached_auth_timeout']
- 
-         self.assertTrue(type(options) == dict,
-@@ -929,6 +930,7 @@ class SSSDConfigTestSSSDDomain(unittest.TestCase):
-             'subdomain_refresh_interval',
-             'subdomain_inherit',
-             'full_name_format',
-+            're_expression',
-             'cached_auth_timeout']
- 
-         self.assertTrue(type(options) == dict,
-diff --git a/src/config/cfg_rules.ini b/src/config/cfg_rules.ini
-index febe4289832f3778b7e974ef4e8b3f6d9d8bffd8..bd0116f334e2605e7671a208225761421511a75a 100644
---- a/src/config/cfg_rules.ini
-+++ b/src/config/cfg_rules.ini
-@@ -288,6 +288,7 @@ option = subdomain_inherit
- option = cached_auth_timeout
- option = wildcard_limit
- option = full_name_format
-+option = re_expression
- 
- #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 2a43b8b68051c5133abc65a78f0d4a5561e760bd..5ac6f79521f5f776fc17319c3afb87d44961afca 100644
---- a/src/config/etc/sssd.api.conf
-+++ b/src/config/etc/sssd.api.conf
-@@ -148,6 +148,7 @@ subdomain_refresh_interval = int, None, false
- subdomain_inherit = str, None, false
- cached_auth_timeout = int, None, false
- full_name_format = str, None, false
-+re_expression = str, None, false
- 
- #Entry cache timeouts
- entry_cache_user_timeout = int, None, false
--- 
-2.4.11
-
diff --git a/SOURCES/0090-configure-fix-typo.patch b/SOURCES/0090-configure-fix-typo.patch
new file mode 100644
index 0000000..f7a0f28
--- /dev/null
+++ b/SOURCES/0090-configure-fix-typo.patch
@@ -0,0 +1,29 @@
+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/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
new file mode 100644
index 0000000..e511e0d
--- /dev/null
+++ b/SOURCES/0091-pam_test_client-add-service-and-environment-to-PAM-t.patch
@@ -0,0 +1,105 @@
+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/0091-rdp-add-ability-to-forward-reply-to-the-client-reque.patch b/SOURCES/0091-rdp-add-ability-to-forward-reply-to-the-client-reque.patch
deleted file mode 100644
index 1719c18..0000000
--- a/SOURCES/0091-rdp-add-ability-to-forward-reply-to-the-client-reque.patch
+++ /dev/null
@@ -1,518 +0,0 @@
-From dc115d8a6aa1a656522c4f11c89e11d61360fd05 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Pavel=20B=C5=99ezina?= <pbrezina@redhat.com>
-Date: Tue, 28 Jun 2016 11:40:16 +0200
-Subject: [PATCH 091/102] rdp: add ability to forward reply to the client
- request
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-In cases where the InfoPipe servers just as a middle-man between
-the DataProvider and a client we can simply forward the reply
-reducing amount of coded needed in the InfoPipe.
-
-Reviewed-by: Lukáš Slebodník <lslebodn@redhat.com>
-Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
-(cherry picked from commit a40d9cc11d17d9c3c22a0462cd8c419d1e79ffb8)
----
- src/responder/common/data_provider/rdp.h         |  20 ++
- src/responder/common/data_provider/rdp_message.c | 260 +++++++++++++++++------
- src/responder/ifp/ifp_domains.c                  |  78 +------
- 3 files changed, 220 insertions(+), 138 deletions(-)
-
-diff --git a/src/responder/common/data_provider/rdp.h b/src/responder/common/data_provider/rdp.h
-index 8a3ec803d8ea7914e2a54661e31dec084586582a..f0aed179a3d33de0462591e6c0bcd3c518105707 100644
---- a/src/responder/common/data_provider/rdp.h
-+++ b/src/responder/common/data_provider/rdp.h
-@@ -54,6 +54,26 @@ errno_t _rdp_message_recv(struct tevent_req *req,
- #define rdp_message_recv(req, ...)                                         \
-     _rdp_message_recv(req, ##__VA_ARGS__, DBUS_TYPE_INVALID)
- 
-+/**
-+ * Send D-Bus message to Data Provider but instead of returning the reply
-+ * to the caller it forwards the reply to the client request. No further
-+ * processing is required by the caller. In case of a failure the client
-+ * request is freed since there is nothing we can do.
-+ */
-+void _rdp_message_send_and_reply(struct sbus_request *sbus_req,
-+                                 struct resp_ctx *rctx,
-+                                 struct sss_domain_info *domain,
-+                                 const char *path,
-+                                 const char *iface,
-+                                 const char *method,
-+                                 int first_arg_type,
-+                                 ...);
-+
-+#define rdp_message_send_and_reply(sbus_req, rctx, domain, path, iface,       \
-+                                   method, ...)                               \
-+    _rdp_message_send_and_reply(sbus_req, rctx, domain, path, iface, method,  \
-+                                ##__VA_ARGS__, DBUS_TYPE_INVALID)
-+
- errno_t rdp_register_client(struct be_conn *be_conn,
-                             const char *client_name);
- 
-diff --git a/src/responder/common/data_provider/rdp_message.c b/src/responder/common/data_provider/rdp_message.c
-index 78af6f8967b378536b6456274fbcac4b609d1033..e226401567e4a1b2b9784a9aba21540ff5f0bc8d 100644
---- a/src/responder/common/data_provider/rdp_message.c
-+++ b/src/responder/common/data_provider/rdp_message.c
-@@ -53,104 +53,72 @@ static errno_t rdp_error_to_errno(DBusError *error)
-     return EIO;
- }
- 
--struct rdp_message_state {
--    struct DBusMessage *reply;
--};
--
--static int rdp_message_state_destructor(struct rdp_message_state *state)
-+static errno_t
-+rdp_message_send_internal(struct resp_ctx *rctx,
-+                          struct sss_domain_info *domain,
-+                          DBusPendingCallNotifyFunction notify_fn,
-+                          void *notify_fn_data,
-+                          const char *path,
-+                          const char *iface,
-+                          const char *method,
-+                          int first_arg_type,
-+                          va_list va)
- {
--    if (state->reply != NULL) {
--        dbus_message_unref(state->reply);
--    }
--
--    return 0;
--}
--
--static void rdp_message_done(DBusPendingCall *pending, void *ptr);
--
--struct tevent_req *_rdp_message_send(TALLOC_CTX *mem_ctx,
--                                     struct resp_ctx *rctx,
--                                     struct sss_domain_info *domain,
--                                     const char *path,
--                                     const char *iface,
--                                     const char *method,
--                                     int first_arg_type,
--                                     ...)
--{
--    struct rdp_message_state *state;
-     struct be_conn *be_conn;
--    struct tevent_req *req;
--    DBusMessage *msg;
-+    DBusMessage *msg = NULL;
-     dbus_bool_t bret;
-     errno_t ret;
--    va_list va;
--
--    req = tevent_req_create(mem_ctx, &state, struct rdp_message_state);
--    if (req == NULL) {
--        DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create() failed\n");
--        return NULL;
--    }
--
--    talloc_set_destructor(state, rdp_message_state_destructor);
- 
-     ret = sss_dp_get_domain_conn(rctx, domain->conn_name, &be_conn);
-     if (ret != EOK) {
-         DEBUG(SSSDBG_CRIT_FAILURE, "BUG: The Data Provider connection for "
-               "%s is not available!\n", domain->name);
--        ret = ERR_INTERNAL;
--        goto immediately;
-+        goto done;
-     }
- 
-     msg = dbus_message_new_method_call(NULL, path, iface, method);
-     if (msg == NULL) {
-         DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create message\n");
-         ret = ENOMEM;
--        goto immediately;
-+        goto done;
-     }
- 
--    va_start(va, first_arg_type);
-     bret = dbus_message_append_args_valist(msg, first_arg_type, va);
--    va_end(va);
-     if (!bret) {
-         DEBUG(SSSDBG_CRIT_FAILURE, "Failed to build message\n");
-         ret = EIO;
--        goto immediately;
-+        goto done;
-     }
- 
-     DEBUG(SSSDBG_TRACE_FUNC, "DP Request: %s %s.%s\n", path, iface, method);
- 
--    ret = sbus_conn_send(be_conn->conn, msg, 30000,
--                         rdp_message_done, req, NULL);
-+    ret = sbus_conn_send(be_conn->conn, msg, 3000,
-+                         notify_fn, notify_fn_data, NULL);
-     if (ret != EOK) {
-         DEBUG(SSSDBG_CRIT_FAILURE, "Unable to contact Data Provider "
-               "[%d]: %s\n", ret, sss_strerror(ret));
--        goto immediately;
-+        goto done;
-     }
- 
--    return req;
-+    ret = EOK;
- 
--immediately:
--    if (ret == EOK) {
--        tevent_req_done(req);
--    } else {
--        tevent_req_error(req, ret);
-+done:
-+    if (msg != NULL) {
-+        dbus_message_unref(msg);
-     }
--    tevent_req_post(req, rctx->ev);
- 
--    return req;
-+    return ret;
- }
- 
--static void rdp_message_done(DBusPendingCall *pending, void *ptr)
-+static errno_t rdp_process_pending_call(DBusPendingCall *pending,
-+                                        DBusMessage **_reply)
- {
--    struct rdp_message_state *state;
--    DBusMessage *reply = NULL;
--    struct tevent_req *req;
--    DBusError error;
-+    DBusMessage *reply;
-     dbus_bool_t bret;
-+    DBusError error;
-     errno_t ret;
- 
--    req = talloc_get_type(ptr, struct tevent_req);
--    state = tevent_req_data(req, struct rdp_message_state);
-+    *_reply = NULL;
- 
-     dbus_error_init(&error);
- 
-@@ -165,9 +133,8 @@ static void rdp_message_done(DBusPendingCall *pending, void *ptr)
-     switch (dbus_message_get_type(reply)) {
-     case DBUS_MESSAGE_TYPE_METHOD_RETURN:
-         DEBUG(SSSDBG_TRACE_FUNC, "DP Success\n");
--        state->reply = reply;
-         ret = EOK;
--        goto done;
-+        break;
- 
-     case DBUS_MESSAGE_TYPE_ERROR:
-         bret = dbus_set_error_from_message(&error, reply);
-@@ -180,28 +147,105 @@ static void rdp_message_done(DBusPendingCall *pending, void *ptr)
-         DEBUG(SSSDBG_CRIT_FAILURE, "DP Error [%s]: %s\n",
-               error.name, (error.message == NULL ? "(null)" : error.message));
-         ret = rdp_error_to_errno(&error);
--        goto done;
-+        break;
-     default:
-+        dbus_message_unref(reply);
-         DEBUG(SSSDBG_CRIT_FAILURE, "Unexpected type?\n");
-         ret = ERR_INTERNAL;
-         goto done;
-     }
- 
--    ret = ERR_INTERNAL;
-+    *_reply = reply;
- 
- done:
-     dbus_pending_call_unref(pending);
-     dbus_error_free(&error);
- 
-+    return ret;
-+}
-+
-+struct rdp_message_state {
-+    struct DBusMessage *reply;
-+};
-+
-+static int rdp_message_state_destructor(struct rdp_message_state *state)
-+{
-+    if (state->reply != NULL) {
-+        dbus_message_unref(state->reply);
-+    }
-+
-+    return 0;
-+}
-+
-+static void rdp_message_done(DBusPendingCall *pending, void *ptr);
-+
-+struct tevent_req *_rdp_message_send(TALLOC_CTX *mem_ctx,
-+                                     struct resp_ctx *rctx,
-+                                     struct sss_domain_info *domain,
-+                                     const char *path,
-+                                     const char *iface,
-+                                     const char *method,
-+                                     int first_arg_type,
-+                                     ...)
-+{
-+    struct rdp_message_state *state;
-+    struct tevent_req *req;
-+    errno_t ret;
-+    va_list va;
-+
-+    req = tevent_req_create(mem_ctx, &state, struct rdp_message_state);
-+    if (req == NULL) {
-+        DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create() failed\n");
-+        return NULL;
-+    }
-+
-+    talloc_set_destructor(state, rdp_message_state_destructor);
-+
-+    va_start(va, first_arg_type);
-+    ret = rdp_message_send_internal(rctx, domain, rdp_message_done, req,
-+                                    path, iface, method, first_arg_type, va);
-+    va_end(va);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_CRIT_FAILURE, "Unable to contact Data Provider "
-+              "[%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 void rdp_message_done(DBusPendingCall *pending, void *ptr)
-+{
-+    struct rdp_message_state *state;
-+    struct tevent_req *req;
-+    errno_t ret;
-+
-+    req = talloc_get_type(ptr, struct tevent_req);
-+    state = tevent_req_data(req, struct rdp_message_state);
-+
-+    ret = rdp_process_pending_call(pending, &state->reply);
-+    if (ret != EOK) {
-+        if (state->reply != NULL) {
-+            dbus_message_unref(state->reply);
-+        }
-+
-+        state->reply = NULL;
-+
-+        tevent_req_error(req, ret);
-         return;
-     }
- 
--    if (reply != NULL) {
--        dbus_message_unref(reply);
--    }
--    tevent_req_error(req, ret);
-+    tevent_req_done(req);
- }
- 
- errno_t _rdp_message_recv(struct tevent_req *req,
-@@ -241,3 +285,85 @@ done:
-     return ret;
- }
- 
-+static void rdp_message_send_and_reply_done(DBusPendingCall *pending,
-+                                            void *ptr);
-+
-+void _rdp_message_send_and_reply(struct sbus_request *sbus_req,
-+                                 struct resp_ctx *rctx,
-+                                 struct sss_domain_info *domain,
-+                                 const char *path,
-+                                 const char *iface,
-+                                 const char *method,
-+                                 int first_arg_type,
-+                                 ...)
-+{
-+    errno_t ret;
-+    va_list va;
-+
-+    va_start(va, first_arg_type);
-+    ret = rdp_message_send_internal(rctx, domain,
-+                                    rdp_message_send_and_reply_done, sbus_req,
-+                                    path, iface, method, first_arg_type, va);
-+    va_end(va);
-+
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_CRIT_FAILURE, "Unable to contact Data Provider "
-+              "[%d]: %s\n", ret, sss_strerror(ret));
-+        talloc_free(sbus_req);
-+    }
-+}
-+
-+static void rdp_message_send_and_reply_done(DBusPendingCall *pending,
-+                                            void *ptr)
-+{
-+    struct sbus_request *sbus_req;
-+    DBusMessage *reply = NULL;
-+    dbus_uint32_t serial;
-+    const char *sender;
-+    dbus_bool_t dbret;
-+    errno_t ret;
-+
-+    sbus_req = talloc_get_type(ptr, struct sbus_request);
-+
-+    ret = rdp_process_pending_call(pending, &reply);
-+    if (reply == NULL) {
-+        /* Something bad happened. Just kill the request. */
-+        ret = EIO;
-+        goto done;
-+    }
-+
-+    /* Otherwise we have a valid reply and we do not care about returned
-+     * value. We set destination and serial in reply to point to the original
-+     * client request. */
-+
-+    sender = dbus_message_get_sender(sbus_req->message);
-+    serial = dbus_message_get_serial(sbus_req->message);
-+
-+    dbret = dbus_message_set_destination(reply, sender);
-+    if (dbret == false) {
-+        DEBUG(SSSDBG_CRIT_FAILURE, "Unable to set reply sender!\n");
-+        ret = EIO;
-+        goto done;
-+    }
-+
-+    dbret = dbus_message_set_reply_serial(reply, serial);
-+    if (dbret == false) {
-+        DEBUG(SSSDBG_CRIT_FAILURE, "Unable to set reply serial!\n");
-+        ret = EIO;
-+        goto done;
-+    }
-+
-+    sbus_request_finish(sbus_req, reply);
-+
-+    ret = EOK;
-+
-+done:
-+    if (reply != NULL) {
-+        dbus_message_unref(reply);
-+    }
-+
-+    if (ret != EOK) {
-+        /* Something bad happend, just kill the request. */
-+        talloc_free(sbus_req);
-+    }
-+}
-diff --git a/src/responder/ifp/ifp_domains.c b/src/responder/ifp/ifp_domains.c
-index 5333b25275e0847015f9cdd294eab5dbdda32f6c..8bfd39feb39822921ea703d8a89ac372e0ad5410 100644
---- a/src/responder/ifp/ifp_domains.c
-+++ b/src/responder/ifp/ifp_domains.c
-@@ -538,14 +538,11 @@ void ifp_dom_get_parent_domain(struct sbus_request *dbus_req,
-                                dom->parent->name);
- }
- 
--static void ifp_domains_domain_is_online_done(struct tevent_req *req);
--
- int ifp_domains_domain_is_online(struct sbus_request *sbus_req,
-                                  void *data)
- {
-     struct ifp_ctx *ifp_ctx;
-     struct sss_domain_info *dom;
--    struct tevent_req *req;
-     DBusError *error;
- 
-     ifp_ctx = talloc_get_type(data, struct ifp_ctx);
-@@ -558,49 +555,18 @@ int ifp_domains_domain_is_online(struct sbus_request *sbus_req,
-         return EOK;
-     }
- 
--    req = rdp_message_send(sbus_req, ifp_ctx->rctx, dom, DP_PATH,
--                           IFACE_DP_BACKEND, IFACE_DP_BACKEND_ISONLINE,
--                           DBUS_TYPE_STRING, &dom->name);
--    if (req == NULL) {
--        return ENOMEM;
--    }
--
--    tevent_req_set_callback(req, ifp_domains_domain_is_online_done, sbus_req);
-+    rdp_message_send_and_reply(sbus_req, ifp_ctx->rctx, dom, DP_PATH,
-+                               IFACE_DP_BACKEND, IFACE_DP_BACKEND_ISONLINE,
-+                               DBUS_TYPE_STRING, &dom->name);
- 
-     return EOK;
- }
- 
--static void ifp_domains_domain_is_online_done(struct tevent_req *req)
--{
--    struct sbus_request *sbus_req;
--    DBusError *error;
--    bool is_online;
--    errno_t ret;
--
--    sbus_req = tevent_req_callback_data(req, struct sbus_request);
--
--    ret = rdp_message_recv(req, DBUS_TYPE_BOOLEAN, &is_online);
--    if (ret != EOK) {
--        DEBUG(SSSDBG_OP_FAILURE, "Unable to get online status [%d]: %s\n",
--              ret, sss_strerror(ret));
--        error = sbus_error_new(sbus_req, DBUS_ERROR_FAILED,
--                               "Unable to get online status [%d]: %s",
--                               ret, sss_strerror(ret));
--        sbus_request_fail_and_finish(sbus_req, error);
--        return;
--    }
--
--    iface_ifp_domains_domain_IsOnline_finish(sbus_req, is_online);
--}
--
--static void ifp_domains_domain_list_services_done(struct tevent_req *req);
--
- int ifp_domains_domain_list_services(struct sbus_request *sbus_req,
-                                      void *data)
- {
-     struct ifp_ctx *ifp_ctx;
-     struct sss_domain_info *dom;
--    struct tevent_req *req;
-     DBusError *error;
- 
-     ifp_ctx = talloc_get_type(data, struct ifp_ctx);
-@@ -613,40 +579,10 @@ int ifp_domains_domain_list_services(struct sbus_request *sbus_req,
-         return EOK;
-     }
- 
--    req = rdp_message_send(sbus_req, ifp_ctx->rctx, dom, DP_PATH,
--                           IFACE_DP_FAILOVER, IFACE_DP_FAILOVER_LISTSERVICES,
--                           DBUS_TYPE_STRING, &dom->name);
--    if (req == NULL) {
--        return ENOMEM;
--    }
--
--    tevent_req_set_callback(req, ifp_domains_domain_list_services_done, sbus_req);
-+    rdp_message_send_and_reply(sbus_req, ifp_ctx->rctx, dom, DP_PATH,
-+                               IFACE_DP_FAILOVER,
-+                               IFACE_DP_FAILOVER_LISTSERVICES,
-+                               DBUS_TYPE_STRING, &dom->name);
- 
-     return EOK;
- }
--
--static void ifp_domains_domain_list_services_done(struct tevent_req *req)
--{
--    struct sbus_request *sbus_req;
--    DBusError *error;
--    int num_services;
--    const char **services;
--    errno_t ret;
--
--    sbus_req = tevent_req_callback_data(req, struct sbus_request);
--
--    ret = rdp_message_recv(req, DBUS_TYPE_ARRAY, DBUS_TYPE_STRING,
--                           &services, &num_services);
--    if (ret != EOK) {
--        DEBUG(SSSDBG_OP_FAILURE, "Unable to get failover services [%d]: %s\n",
--              ret, sss_strerror(ret));
--        error = sbus_error_new(sbus_req, DBUS_ERROR_FAILED,
--                               "Unable to get failover services [%d]: %s",
--                               ret, sss_strerror(ret));
--        sbus_request_fail_and_finish(sbus_req, error);
--        return;
--    }
--
--    iface_ifp_domains_domain_ListServices_finish(sbus_req, services,
--                                                 num_services);
--}
--- 
-2.4.11
-
diff --git a/SOURCES/0092-pam_test_client-add-SSSD-getpwnam-lookup.patch b/SOURCES/0092-pam_test_client-add-SSSD-getpwnam-lookup.patch
new file mode 100644
index 0000000..fc139dc
--- /dev/null
+++ b/SOURCES/0092-pam_test_client-add-SSSD-getpwnam-lookup.patch
@@ -0,0 +1,142 @@
+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-sbus-add-sbus_request_reply_error.patch b/SOURCES/0092-sbus-add-sbus_request_reply_error.patch
deleted file mode 100644
index 623cc9c..0000000
--- a/SOURCES/0092-sbus-add-sbus_request_reply_error.patch
+++ /dev/null
@@ -1,218 +0,0 @@
-From 9b0cda6876a5407b152bdeb51bb312aa52916172 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Pavel=20B=C5=99ezina?= <pbrezina@redhat.com>
-Date: Tue, 28 Jun 2016 15:29:39 +0200
-Subject: [PATCH 092/102] sbus: add sbus_request_reply_error()
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-This simplifies error handling in sbus requests since we avoid
-creating DBusError and checking for NULL manually. It removes
-few lines of code.
-
-This patch does not replace all calls to sbus_request_fail_and_finish
-since sometimes it is desirable to create the error manualy. But
-it replaces it in most recent places.
-
-Reviewed-by: Lukáš Slebodník <lslebodn@redhat.com>
-Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
-(cherry picked from commit a06e23c0bcf0c8669a29b801876aca8aac422931)
----
- src/providers/data_provider/dp_iface_backend.c | 11 ++--
- src/responder/ifp/ifp_domains.c                | 12 ++---
- src/sbus/sssd_dbus.h                           |  9 +++-
- src/sbus/sssd_dbus_request.c                   | 73 ++++++++++++++++++++------
- 4 files changed, 70 insertions(+), 35 deletions(-)
-
-diff --git a/src/providers/data_provider/dp_iface_backend.c b/src/providers/data_provider/dp_iface_backend.c
-index f4af35ed6ec3858b7fff80cf2933926a653ba6f5..d9a84bfee4c5c11e46e0e8f7021f829825ad95c1 100644
---- a/src/providers/data_provider/dp_iface_backend.c
-+++ b/src/providers/data_provider/dp_iface_backend.c
-@@ -34,7 +34,6 @@ errno_t dp_backend_is_online(struct sbus_request *sbus_req,
- {
-     struct be_ctx *be_ctx;
-     struct sss_domain_info *domain;
--    DBusError *error;
-     bool online;
- 
-     be_ctx = dp_client_be(dp_cli);
-@@ -44,13 +43,9 @@ errno_t dp_backend_is_online(struct sbus_request *sbus_req,
-     } else {
-         domain = find_domain_by_name(be_ctx->domain, domname, false);
-         if (domain == NULL) {
--            error = sbus_error_new(sbus_req, SBUS_ERROR_UNKNOWN_DOMAIN,
--                                   "Unknown domain %s", domname);
--            if (error == NULL) {
--                return ENOMEM;
--            }
--
--            return sbus_request_fail_and_finish(sbus_req, error);
-+            sbus_request_reply_error(sbus_req, SBUS_ERROR_UNKNOWN_DOMAIN,
-+                                     "Unknown domain %s", domname);
-+            return EOK;
-         }
-     }
- 
-diff --git a/src/responder/ifp/ifp_domains.c b/src/responder/ifp/ifp_domains.c
-index 8bfd39feb39822921ea703d8a89ac372e0ad5410..ff690ed6a7d5519979d242a4d5dadd08aff50347 100644
---- a/src/responder/ifp/ifp_domains.c
-+++ b/src/responder/ifp/ifp_domains.c
-@@ -543,15 +543,13 @@ int ifp_domains_domain_is_online(struct sbus_request *sbus_req,
- {
-     struct ifp_ctx *ifp_ctx;
-     struct sss_domain_info *dom;
--    DBusError *error;
- 
-     ifp_ctx = talloc_get_type(data, struct ifp_ctx);
- 
-     dom = get_domain_info_from_req(sbus_req, data);
-     if (dom == NULL) {
--        error = sbus_error_new(sbus_req, SBUS_ERROR_UNKNOWN_DOMAIN,
--                               "Unknown domain");
--        sbus_request_fail_and_finish(sbus_req, error);
-+        sbus_request_reply_error(sbus_req, SBUS_ERROR_UNKNOWN_DOMAIN,
-+                                 "Unknown domain");
-         return EOK;
-     }
- 
-@@ -567,15 +565,13 @@ int ifp_domains_domain_list_services(struct sbus_request *sbus_req,
- {
-     struct ifp_ctx *ifp_ctx;
-     struct sss_domain_info *dom;
--    DBusError *error;
- 
-     ifp_ctx = talloc_get_type(data, struct ifp_ctx);
- 
-     dom = get_domain_info_from_req(sbus_req, data);
-     if (dom == NULL) {
--        error = sbus_error_new(sbus_req, SBUS_ERROR_UNKNOWN_DOMAIN,
--                               "Unknown domain");
--        sbus_request_fail_and_finish(sbus_req, error);
-+        sbus_request_reply_error(sbus_req, SBUS_ERROR_UNKNOWN_DOMAIN,
-+                                 "Unknown domain");
-         return EOK;
-     }
- 
-diff --git a/src/sbus/sssd_dbus.h b/src/sbus/sssd_dbus.h
-index fe1c4a7e1730e088647744d9b49a68c3c71db57f..c0aedf36b496bfda05dcde921ea7060efb4cc91f 100644
---- a/src/sbus/sssd_dbus.h
-+++ b/src/sbus/sssd_dbus.h
-@@ -357,6 +357,11 @@ int sbus_request_return_and_finish(struct sbus_request *dbus_req,
- int sbus_request_fail_and_finish(struct sbus_request *dbus_req,
-                                  const DBusError *error);
- 
-+void sbus_request_reply_error(struct sbus_request *sbus_req,
-+                              const char *error_name,
-+                              const char *fmt,
-+                              ...) SSS_ATTRIBUTE_PRINTF(3, 4);
-+
- /*
-  * Construct a new DBusError instance which can be consumed by functions such
-  * as @sbus_request_fail_and_finish().
-@@ -368,9 +373,9 @@ int sbus_request_fail_and_finish(struct sbus_request *dbus_req,
-  * is duplicated using the returned DBusError instance as a talloc parent.
-  */
- DBusError *sbus_error_new(TALLOC_CTX *mem_ctx,
--                          const char *dbus_err_name,
-+                          const char *dbus_error_name,
-                           const char *fmt,
--                          ...) SSS_ATTRIBUTE_PRINTF(3,4);
-+                          ...) SSS_ATTRIBUTE_PRINTF(3, 4);
- 
- /*
-  * Parse a DBus method call request.
-diff --git a/src/sbus/sssd_dbus_request.c b/src/sbus/sssd_dbus_request.c
-index f8647b5ecfb4a49d45a15733b22c6014f4bd084c..c5b08539ff85b5427e41f6e03991b40a0a43a7e3 100644
---- a/src/sbus/sssd_dbus_request.c
-+++ b/src/sbus/sssd_dbus_request.c
-@@ -199,31 +199,70 @@ int sbus_request_fail_and_finish(struct sbus_request *dbus_req,
-     return ret;
- }
- 
-+static DBusError *sbus_error_new_va(TALLOC_CTX *mem_ctx,
-+                                    const char *error_name,
-+                                    const char *fmt,
-+                                    va_list ap)
-+{
-+    DBusError *error;
-+    const char *error_msg;
-+
-+    error = talloc_zero(mem_ctx, DBusError);
-+    if (error == NULL) {
-+        return NULL;
-+    }
-+
-+    if (fmt != NULL) {
-+        error_msg = talloc_vasprintf(error, fmt, ap);
-+        if (error_msg == NULL) {
-+            talloc_free(error);
-+            return NULL;
-+        }
-+    } else {
-+        error_msg = NULL;
-+    }
-+
-+    dbus_error_init(error);
-+    dbus_set_error_const(error, error_name, error_msg);
-+
-+    return error;
-+}
-+
- DBusError *sbus_error_new(TALLOC_CTX *mem_ctx,
--                          const char *dbus_err_name,
-+                          const char *dbus_error_name,
-                           const char *fmt,
-                           ...)
- {
--    DBusError *dberr;
--    const char *err_msg_dup = NULL;
-+    DBusError *error;
-     va_list ap;
- 
--    dberr = talloc(mem_ctx, DBusError);
--    if (dberr == NULL) return NULL;
--
--    if (fmt) {
--        va_start(ap, fmt);
--        err_msg_dup = talloc_vasprintf(dberr, fmt, ap);
--        va_end(ap);
--        if (err_msg_dup == NULL) {
--            talloc_free(dberr);
--            return NULL;
--        }
-+    va_start(ap, fmt);
-+    error = sbus_error_new_va(mem_ctx, dbus_error_name, fmt, ap);
-+    va_end(ap);
-+
-+    return error;
-+}
-+
-+void sbus_request_reply_error(struct sbus_request *sbus_req,
-+                              const char *error_name,
-+                              const char *fmt,
-+                              ...)
-+{
-+    DBusError *error;
-+    va_list ap;
-+
-+    va_start(ap, fmt);
-+    error = sbus_error_new_va(sbus_req, error_name, fmt, ap);
-+    va_end(ap);
-+
-+    if (error == NULL) {
-+        DEBUG(SSSDBG_CRIT_FAILURE,
-+              "Unable to create D-Bus error, killing request!\n");
-+        talloc_free(sbus_req);
-+        return;
-     }
- 
--    dbus_error_init(dberr);
--    dbus_set_error_const(dberr, dbus_err_name, err_msg_dup);
--    return dberr;
-+    sbus_request_fail_and_finish(sbus_req, error);
- }
- 
- struct array_arg {
--- 
-2.4.11
-
diff --git a/SOURCES/0093-sbus-add-utility-function-to-simplify-message-and-re.patch b/SOURCES/0093-sbus-add-utility-function-to-simplify-message-and-re.patch
deleted file mode 100644
index 561fd47..0000000
--- a/SOURCES/0093-sbus-add-utility-function-to-simplify-message-and-re.patch
+++ /dev/null
@@ -1,624 +0,0 @@
-From 420e47f6a0e173e774faa426d172c6e2160b8302 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Pavel=20B=C5=99ezina?= <pbrezina@redhat.com>
-Date: Wed, 29 Jun 2016 12:35:59 +0200
-Subject: [PATCH 093/102] sbus: add utility function to simplify message and
- reply handling
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-This patch adds the ability to hook DBusMessage to a talloc context
-to remove the need of calling dbus_message_unref(). It also provides
-an automatical way to detect error in a reply so the caller does
-not need to parse it manually and the whole code around DBusError
-can be avoided.
-
-Reviewed-by: Lukáš Slebodník <lslebodn@redhat.com>
-Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
-(cherry picked from commit 439e08cdc5c83b3e5835cb0435983f1da2ffbaf1)
----
- Makefile.am                                      |   2 +
- src/responder/common/data_provider/rdp_message.c |  85 ++-------
- src/sbus/sssd_dbus.h                             |   2 +
- src/sbus/sssd_dbus_utils.c                       | 226 +++++++++++++++++++++++
- src/sbus/sssd_dbus_utils.h                       |  64 +++++++
- src/tools/sssctl/sssctl_domains.c                |  32 +---
- 6 files changed, 313 insertions(+), 98 deletions(-)
- create mode 100644 src/sbus/sssd_dbus_utils.c
- create mode 100644 src/sbus/sssd_dbus_utils.h
-
-diff --git a/Makefile.am b/Makefile.am
-index ee9b48c666a44781b582ba5d83102b705e898f29..1837e36da7302cb51c0b90e51b762ce0a87cd65f 100644
---- a/Makefile.am
-+++ b/Makefile.am
-@@ -634,6 +634,7 @@ dist_noinst_HEADERS = \
-     src/sbus/sssd_dbus_private.h \
-     src/sbus/sssd_dbus_invokers.h \
-     src/sbus/sssd_dbus_errors.h \
-+    src/sbus/sssd_dbus_utils.h \
-     src/db/sysdb.h \
-     src/db/sysdb_sudo.h \
-     src/db/sysdb_autofs.h \
-@@ -915,6 +916,7 @@ libsss_util_la_SOURCES = \
-     src/sbus/sssd_dbus_server.c \
-     src/sbus/sssd_dbus_signals.c \
-     src/sbus/sssd_dbus_common_signals.c \
-+    src/sbus/sssd_dbus_utils.c \
-     src/util/util.c \
-     src/util/memory.c \
-     src/util/safe-format-string.c \
-diff --git a/src/responder/common/data_provider/rdp_message.c b/src/responder/common/data_provider/rdp_message.c
-index e226401567e4a1b2b9784a9aba21540ff5f0bc8d..6ad2ba056e992cd89b87b478d422d1a4259a12d9 100644
---- a/src/responder/common/data_provider/rdp_message.c
-+++ b/src/responder/common/data_provider/rdp_message.c
-@@ -26,33 +26,6 @@
- #include "sbus/sssd_dbus_errors.h"
- #include "util/util.h"
- 
--static errno_t rdp_error_to_errno(DBusError *error)
--{
--    static struct {
--        const char *name;
--        errno_t ret;
--    } list[] = {{SBUS_ERROR_INTERNAL, ERR_INTERNAL},
--                {SBUS_ERROR_NOT_FOUND, ENOENT},
--                {SBUS_ERROR_DP_FATAL, ERR_TERMINATED},
--                {SBUS_ERROR_DP_OFFLINE, ERR_OFFLINE},
--                {SBUS_ERROR_DP_NOTSUP, ENOTSUP},
--                {NULL, ERR_INTERNAL}
--    };
--    int i;
--
--    if (!dbus_error_is_set(error)) {
--        return EOK;
--    }
--
--    for (i = 0; list[i].name != NULL; i ++) {
--        if (dbus_error_has_name(error, list[i].name)) {
--            return list[i].ret;
--        }
--    }
--
--    return EIO;
--}
--
- static errno_t
- rdp_message_send_internal(struct resp_ctx *rctx,
-                           struct sss_domain_info *domain,
-@@ -110,7 +83,8 @@ done:
-     return ret;
- }
- 
--static errno_t rdp_process_pending_call(DBusPendingCall *pending,
-+static errno_t rdp_process_pending_call(TALLOC_CTX *mem_ctx,
-+                                        DBusPendingCall *pending,
-                                         DBusMessage **_reply)
- {
-     DBusMessage *reply;
-@@ -130,6 +104,11 @@ static errno_t rdp_process_pending_call(DBusPendingCall *pending,
-         goto done;
-     }
- 
-+    ret = sbus_talloc_bound_message(mem_ctx, reply);
-+    if (ret != EOK) {
-+        return ret;
-+    }
-+
-     switch (dbus_message_get_type(reply)) {
-     case DBUS_MESSAGE_TYPE_METHOD_RETURN:
-         DEBUG(SSSDBG_TRACE_FUNC, "DP Success\n");
-@@ -146,10 +125,9 @@ static errno_t rdp_process_pending_call(DBusPendingCall *pending,
- 
-         DEBUG(SSSDBG_CRIT_FAILURE, "DP Error [%s]: %s\n",
-               error.name, (error.message == NULL ? "(null)" : error.message));
--        ret = rdp_error_to_errno(&error);
-+        ret = sbus_error_to_errno(&error);
-         break;
-     default:
--        dbus_message_unref(reply);
-         DEBUG(SSSDBG_CRIT_FAILURE, "Unexpected type?\n");
-         ret = ERR_INTERNAL;
-         goto done;
-@@ -168,15 +146,6 @@ struct rdp_message_state {
-     struct DBusMessage *reply;
- };
- 
--static int rdp_message_state_destructor(struct rdp_message_state *state)
--{
--    if (state->reply != NULL) {
--        dbus_message_unref(state->reply);
--    }
--
--    return 0;
--}
--
- static void rdp_message_done(DBusPendingCall *pending, void *ptr);
- 
- struct tevent_req *_rdp_message_send(TALLOC_CTX *mem_ctx,
-@@ -199,8 +168,6 @@ struct tevent_req *_rdp_message_send(TALLOC_CTX *mem_ctx,
-         return NULL;
-     }
- 
--    talloc_set_destructor(state, rdp_message_state_destructor);
--
-     va_start(va, first_arg_type);
-     ret = rdp_message_send_internal(rctx, domain, rdp_message_done, req,
-                                     path, iface, method, first_arg_type, va);
-@@ -233,14 +200,8 @@ static void rdp_message_done(DBusPendingCall *pending, void *ptr)
-     req = talloc_get_type(ptr, struct tevent_req);
-     state = tevent_req_data(req, struct rdp_message_state);
- 
--    ret = rdp_process_pending_call(pending, &state->reply);
-+    ret = rdp_process_pending_call(state, pending, &state->reply);
-     if (ret != EOK) {
--        if (state->reply != NULL) {
--            dbus_message_unref(state->reply);
--        }
--
--        state->reply = NULL;
--
-         tevent_req_error(req, ret);
-         return;
-     }
-@@ -253,35 +214,17 @@ errno_t _rdp_message_recv(struct tevent_req *req,
-                           ...)
- {
-     struct rdp_message_state *state;
--    DBusError error;
--    dbus_bool_t bret;
-     errno_t ret;
-     va_list va;
- 
-     TEVENT_REQ_RETURN_ON_ERROR(req);
- 
-     state = tevent_req_data(req, struct rdp_message_state);
--    dbus_error_init(&error);
- 
-     va_start(va, first_arg_type);
--    bret = dbus_message_get_args_valist(state->reply, &error, first_arg_type, va);
-+    ret = sbus_parse_message_valist(state->reply, false, first_arg_type, va);
-     va_end(va);
- 
--    if (bret == false) {
--        DEBUG(SSSDBG_CRIT_FAILURE, "Unable to parse reply\n");
--        ret = EIO;
--        goto done;
--    }
--
--    ret = rdp_error_to_errno(&error);
--    if (ret != EOK) {
--        DEBUG(SSSDBG_CRIT_FAILURE, "Unable to parse message [%s]: %s\n",
--              error.name, error.message);
--        goto done;
--    }
--
--done:
--    dbus_error_free(&error);
-     return ret;
- }
- 
-@@ -317,7 +260,7 @@ static void rdp_message_send_and_reply_done(DBusPendingCall *pending,
-                                             void *ptr)
- {
-     struct sbus_request *sbus_req;
--    DBusMessage *reply = NULL;
-+    DBusMessage *reply;
-     dbus_uint32_t serial;
-     const char *sender;
-     dbus_bool_t dbret;
-@@ -325,7 +268,7 @@ static void rdp_message_send_and_reply_done(DBusPendingCall *pending,
- 
-     sbus_req = talloc_get_type(ptr, struct sbus_request);
- 
--    ret = rdp_process_pending_call(pending, &reply);
-+    ret = rdp_process_pending_call(sbus_req, pending, &reply);
-     if (reply == NULL) {
-         /* Something bad happened. Just kill the request. */
-         ret = EIO;
-@@ -358,10 +301,6 @@ static void rdp_message_send_and_reply_done(DBusPendingCall *pending,
-     ret = EOK;
- 
- done:
--    if (reply != NULL) {
--        dbus_message_unref(reply);
--    }
--
-     if (ret != EOK) {
-         /* Something bad happend, just kill the request. */
-         talloc_free(sbus_req);
-diff --git a/src/sbus/sssd_dbus.h b/src/sbus/sssd_dbus.h
-index c0aedf36b496bfda05dcde921ea7060efb4cc91f..15e3b117e1a467f4e250cdf4ba8fd0326e4d380e 100644
---- a/src/sbus/sssd_dbus.h
-+++ b/src/sbus/sssd_dbus.h
-@@ -29,6 +29,8 @@ struct sbus_request;
- #include <dbus/dbus.h>
- #include <sys/types.h>
- #include "util/util.h"
-+#include "sbus/sssd_dbus_errors.h"
-+#include "sbus/sssd_dbus_utils.h"
- 
- /* Older platforms (such as RHEL-6) might not have these error constants
-  * defined */
-diff --git a/src/sbus/sssd_dbus_utils.c b/src/sbus/sssd_dbus_utils.c
-new file mode 100644
-index 0000000000000000000000000000000000000000..4c33f9fd75cac2d4a56a5638982f8ecb73da8e2e
---- /dev/null
-+++ b/src/sbus/sssd_dbus_utils.c
-@@ -0,0 +1,226 @@
-+/*
-+    Authors:
-+        Pavel Březina <pbrezina@redhat.com>
-+
-+    Copyright (C) 2016 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 "sbus/sssd_dbus.h"
-+#include "util/util.h"
-+
-+struct sbus_talloc_msg {
-+    DBusMessage *msg;
-+};
-+
-+static int sbus_talloc_msg_destructor(struct sbus_talloc_msg *talloc_msg)
-+{
-+    if (talloc_msg->msg == NULL) {
-+        return 0;
-+    }
-+
-+    dbus_message_unref(talloc_msg->msg);
-+    return 0;
-+}
-+
-+errno_t sbus_talloc_bound_message(TALLOC_CTX *mem_ctx, DBusMessage *msg)
-+{
-+    struct sbus_talloc_msg *talloc_msg;
-+
-+    talloc_msg = talloc(mem_ctx, struct sbus_talloc_msg);
-+    if (talloc_msg == NULL) {
-+        DEBUG(SSSDBG_CRIT_FAILURE,
-+              "Unable to bound D-Bus message with talloc context!\n");
-+        return ENOMEM;
-+    }
-+
-+    talloc_msg->msg = msg;
-+
-+    talloc_set_destructor(talloc_msg, sbus_talloc_msg_destructor);
-+
-+    return EOK;
-+}
-+
-+errno_t sbus_error_to_errno(DBusError *error)
-+{
-+    static struct {
-+        const char *name;
-+        errno_t ret;
-+    } list[] = { { SBUS_ERROR_INTERNAL, ERR_INTERNAL },
-+                 { SBUS_ERROR_NOT_FOUND, ENOENT },
-+                 { SBUS_ERROR_UNKNOWN_DOMAIN, ERR_DOMAIN_NOT_FOUND },
-+                 { SBUS_ERROR_DP_FATAL, ERR_TERMINATED },
-+                 { SBUS_ERROR_DP_OFFLINE, ERR_OFFLINE },
-+                 { SBUS_ERROR_DP_NOTSUP, ENOTSUP },
-+                 { NULL, ERR_INTERNAL } };
-+    int i;
-+
-+    if (!dbus_error_is_set(error)) {
-+        return EOK;
-+    }
-+
-+    for (i = 0; list[i].name != NULL; i++) {
-+        if (dbus_error_has_name(error, list[i].name)) {
-+            return list[i].ret;
-+        }
-+    }
-+
-+    return EIO;
-+}
-+
-+errno_t sbus_check_reply(DBusMessage *reply)
-+{
-+    dbus_bool_t bret;
-+    DBusError error;
-+    errno_t ret;
-+
-+    dbus_error_init(&error);
-+
-+    switch (dbus_message_get_type(reply)) {
-+    case DBUS_MESSAGE_TYPE_METHOD_RETURN:
-+        ret = EOK;
-+        goto done;
-+
-+    case DBUS_MESSAGE_TYPE_ERROR:
-+        bret = dbus_set_error_from_message(&error, reply);
-+        if (bret == false) {
-+            DEBUG(SSSDBG_CRIT_FAILURE, "Unable to read error from message\n");
-+            ret = EIO;
-+            goto done;
-+        }
-+
-+        DEBUG(SSSDBG_CRIT_FAILURE, "D-Bus error [%s]: %s\n",
-+              error.name, (error.message == NULL ? "(null)" : error.message));
-+        ret = sbus_error_to_errno(&error);
-+        goto done;
-+    default:
-+        DEBUG(SSSDBG_CRIT_FAILURE, "Unexpected D-Bus message type?\n");
-+        ret = ERR_INTERNAL;
-+        goto done;
-+    }
-+
-+done:
-+    dbus_error_free(&error);
-+
-+    return ret;
-+}
-+
-+DBusMessage *sbus_create_message_valist(TALLOC_CTX *mem_ctx,
-+                                        const char *bus,
-+                                        const char *path,
-+                                        const char *iface,
-+                                        const char *method,
-+                                        int first_arg_type,
-+                                        va_list va)
-+{
-+    DBusMessage *msg;
-+    dbus_bool_t bret;
-+    errno_t ret;
-+
-+    msg = dbus_message_new_method_call(bus, path, iface, method);
-+    if (msg == NULL) {
-+        DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create message\n");
-+        return NULL;
-+    }
-+
-+    bret = dbus_message_append_args_valist(msg, first_arg_type, va);
-+    if (!bret) {
-+        DEBUG(SSSDBG_CRIT_FAILURE, "Failed to build message\n");
-+        ret = EIO;
-+        goto done;
-+    }
-+
-+    ret = sbus_talloc_bound_message(mem_ctx, msg);
-+
-+done:
-+    if (ret != EOK) {
-+        dbus_message_unref(msg);
-+    }
-+
-+    return msg;
-+}
-+
-+DBusMessage *_sbus_create_message(TALLOC_CTX *mem_ctx,
-+                                  const char *bus,
-+                                  const char *path,
-+                                  const char *iface,
-+                                  const char *method,
-+                                  int first_arg_type,
-+                                  ...)
-+{
-+    DBusMessage *msg;
-+    va_list va;
-+
-+    va_start(va, first_arg_type);
-+    msg = sbus_create_message_valist(mem_ctx, bus, path, iface, method,
-+                                     first_arg_type, va);
-+    va_end(va);
-+
-+    return msg;
-+}
-+
-+errno_t sbus_parse_message_valist(DBusMessage *msg,
-+                                  bool check_reply,
-+                                  int first_arg_type,
-+                                  va_list va)
-+{
-+    DBusError error;
-+    dbus_bool_t bret;
-+    errno_t ret;
-+
-+    if (check_reply) {
-+        ret = sbus_check_reply(msg);
-+        if (ret != EOK) {
-+            return ret;
-+        }
-+    }
-+
-+    dbus_error_init(&error);
-+
-+    bret = dbus_message_get_args_valist(msg, &error, first_arg_type, va);
-+    if (bret == false) {
-+        DEBUG(SSSDBG_CRIT_FAILURE, "Unable to parse D-Bus message\n");
-+        ret = EIO;
-+        goto done;
-+    }
-+
-+    ret = sbus_error_to_errno(&error);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_CRIT_FAILURE, "Unable to parse D-Bus message [%s]: %s\n",
-+              error.name, error.message);
-+        goto done;
-+    }
-+
-+done:
-+    dbus_error_free(&error);
-+    return ret;
-+}
-+
-+errno_t _sbus_parse_message(DBusMessage *msg,
-+                            bool check_reply,
-+                            int first_arg_type,
-+                            ...)
-+{
-+    errno_t ret;
-+    va_list va;
-+
-+    va_start(va, first_arg_type);
-+    ret = sbus_parse_message_valist(msg, check_reply, first_arg_type, va);
-+    va_end(va);
-+
-+    return ret;
-+}
-diff --git a/src/sbus/sssd_dbus_utils.h b/src/sbus/sssd_dbus_utils.h
-new file mode 100644
-index 0000000000000000000000000000000000000000..74c21fb7930c7f5f5417b6a2587cf691b1bc0b19
---- /dev/null
-+++ b/src/sbus/sssd_dbus_utils.h
-@@ -0,0 +1,64 @@
-+/*
-+    Authors:
-+        Pavel Březina <pbrezina@redhat.com>
-+
-+    Copyright (C) 2016 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 SSSD_DBUS_UTILS_H_
-+#define SSSD_DBUS_UTILS_H_
-+
-+errno_t sbus_talloc_bound_message(TALLOC_CTX *mem_ctx, DBusMessage *msg);
-+errno_t sbus_error_to_errno(DBusError *error);
-+errno_t sbus_check_reply(DBusMessage *reply);
-+
-+DBusMessage *sbus_create_message_valist(TALLOC_CTX *mem_ctx,
-+                                        const char *bus,
-+                                        const char *path,
-+                                        const char *iface,
-+                                        const char *method,
-+                                        int first_arg_type,
-+                                        va_list va);
-+
-+DBusMessage *_sbus_create_message(TALLOC_CTX *mem_ctx,
-+                                  const char *bus,
-+                                  const char *path,
-+                                  const char *iface,
-+                                  const char *method,
-+                                  int first_arg_type,
-+                                  ...);
-+
-+#define sbus_create_message(mem_ctx, bus, path, iface, method, ...) \
-+    _sbus_create_message(mem_ctx, bus, path, iface, method,         \
-+                         ##__VA_ARGS__, DBUS_TYPE_INVALID)
-+
-+errno_t sbus_parse_message_valist(DBusMessage *msg,
-+                                  bool check_reply,
-+                                  int first_arg_type,
-+                                  va_list va);
-+
-+errno_t _sbus_parse_message(DBusMessage *msg,
-+                            bool check_reply,
-+                            int first_arg_type,
-+                            ...);
-+
-+#define sbus_parse_message(msg, ...) \
-+    _sbus_parse_message(msg, false, ##__VA_ARGS__, DBUS_TYPE_INVALID)
-+
-+#define sbus_parse_reply(msg, ...) \
-+    _sbus_parse_message(msg, true, ##__VA_ARGS__, DBUS_TYPE_INVALID)
-+
-+#endif /* SSSD_DBUS_UTILS_H_ */
-diff --git a/src/tools/sssctl/sssctl_domains.c b/src/tools/sssctl/sssctl_domains.c
-index cfc4e56133213e27496350033d4d28c3f5b5c63d..17ad670f39dfc045ba090210ffcfa77df713c306 100644
---- a/src/tools/sssctl/sssctl_domains.c
-+++ b/src/tools/sssctl/sssctl_domains.c
-@@ -79,15 +79,11 @@ static errno_t sssctl_domain_status_online(struct sss_tool_ctx *tool_ctx,
- {
-     sss_sifp_ctx *sifp;
-     sss_sifp_error sifp_error;
--    DBusError dbus_error;
-     DBusMessage *reply = NULL;
--    DBusMessage *msg = NULL;
-+    DBusMessage *msg;
-     bool is_online;
--    dbus_bool_t dbret;
-     errno_t ret;
- 
--    dbus_error_init(&dbus_error);
--
-     if (!sssctl_start_sssd(force_start)) {
-         ret = ERR_SSSD_NOT_RUNNING;
-         goto done;
-@@ -100,16 +96,15 @@ static errno_t sssctl_domain_status_online(struct sss_tool_ctx *tool_ctx,
-         goto done;
-     }
- 
--
--    msg = sss_sifp_create_message(domain_path, IFACE_IFP_DOMAINS_DOMAIN,
--                                  IFACE_IFP_DOMAINS_DOMAIN_ISONLINE);
-+    msg = sbus_create_message(tool_ctx, SSS_SIFP_ADDRESS, domain_path,
-+                              IFACE_IFP_DOMAINS_DOMAIN,
-+                              IFACE_IFP_DOMAINS_DOMAIN_ISONLINE);
-     if (msg == NULL) {
-         DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create D-Bus message\n");
-         ret = ENOMEM;
-         goto done;
-     }
- 
--
-     sifp_error = sss_sifp_send_message(sifp, msg, &reply);
-     if (sifp_error != SSS_SIFP_OK) {
-         sssctl_sifp_error(sifp, sifp_error, "Unable to get online status");
-@@ -117,16 +112,9 @@ static errno_t sssctl_domain_status_online(struct sss_tool_ctx *tool_ctx,
-         goto done;
-     }
- 
--    dbret = dbus_message_get_args(reply, &dbus_error,
--                                  DBUS_TYPE_BOOLEAN, &is_online,
--                                  DBUS_TYPE_INVALID);
--    if (!dbret) {
--        DEBUG(SSSDBG_CRIT_FAILURE, "Unable to parse D-Bus reply\n");
--        if (dbus_error_is_set(&dbus_error)) {
--            DEBUG(SSSDBG_CRIT_FAILURE, "%s: %s\n",
--                  dbus_error.name, dbus_error.message);
--        }
--        ret = EIO;
-+    ret = sbus_parse_reply(reply, DBUS_TYPE_BOOLEAN, &is_online);
-+    if (ret != EOK) {
-+        fprintf(stderr, _("Unable to get information from SSSD\n"));
-         goto done;
-     }
- 
-@@ -135,16 +123,10 @@ static errno_t sssctl_domain_status_online(struct sss_tool_ctx *tool_ctx,
-     ret = EOK;
- 
- done:
--    if (msg != NULL) {
--        dbus_message_unref(msg);
--    }
--
-     if (reply != NULL) {
-         dbus_message_unref(reply);
-     }
- 
--    dbus_error_free(&dbus_error);
--
-     return ret;
- }
- 
--- 
-2.4.11
-
diff --git a/SOURCES/0093-sss_sifp-update-method-names.patch b/SOURCES/0093-sss_sifp-update-method-names.patch
new file mode 100644
index 0000000..0dac7de
--- /dev/null
+++ b/SOURCES/0093-sss_sifp-update-method-names.patch
@@ -0,0 +1,54 @@
+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-pam_test_client-add-InfoPipe-user-lookup.patch b/SOURCES/0094-pam_test_client-add-InfoPipe-user-lookup.patch
new file mode 100644
index 0000000..6d20370
--- /dev/null
+++ b/SOURCES/0094-pam_test_client-add-InfoPipe-user-lookup.patch
@@ -0,0 +1,131 @@
+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/0094-sssctl-use-talloc-with-sifp.patch b/SOURCES/0094-sssctl-use-talloc-with-sifp.patch
deleted file mode 100644
index 4417086..0000000
--- a/SOURCES/0094-sssctl-use-talloc-with-sifp.patch
+++ /dev/null
@@ -1,215 +0,0 @@
-From bb8653ba6ba4eddb7faeddd9ceb3349107f77fd9 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Pavel=20B=C5=99ezina?= <pbrezina@redhat.com>
-Date: Wed, 29 Jun 2016 14:03:38 +0200
-Subject: [PATCH 094/102] sssctl: use talloc with sifp
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-This way we completely move D-Bus memory management to talloc and
-we reduce number of code lines needed to send and receive reply.
-
-Reviewed-by: Lukáš Slebodník <lslebodn@redhat.com>
-Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
-(cherry picked from commit 9b74009c1260e6f3b1031a6ae110bf1d957cba81)
----
- src/tools/sssctl/sssctl.h         | 14 +++++++++
- src/tools/sssctl/sssctl_domains.c | 62 ++++++++++++++++++---------------------
- src/tools/sssctl/sssctl_sifp.c    | 46 +++++++++++++++++++++++++++++
- 3 files changed, 88 insertions(+), 34 deletions(-)
-
-diff --git a/src/tools/sssctl/sssctl.h b/src/tools/sssctl/sssctl.h
-index 72930ee5c3a1195e90c6e35768f715cbf6a1c4e1..d4e3359b0b160d12a0c2699f754989a24b2b336a 100644
---- a/src/tools/sssctl/sssctl.h
-+++ b/src/tools/sssctl/sssctl.h
-@@ -24,6 +24,7 @@
- #include "lib/sifp/sss_sifp.h"
- #include "lib/sifp/sss_sifp_dbus.h"
- #include "tools/common/sss_tools.h"
-+#include "sbus/sssd_dbus.h"
- 
- enum sssctl_prompt_result {
-     SSSCTL_PROMPT_YES,
-@@ -56,6 +57,19 @@ void _sssctl_sifp_error(sss_sifp_ctx *sifp,
- #define sssctl_sifp_error(sifp, error, message) \
-     _sssctl_sifp_error(sifp, error, _(message))
- 
-+sss_sifp_error _sssctl_sifp_send(TALLOC_CTX *mem_ctx,
-+                                 sss_sifp_ctx *sifp,
-+                                 DBusMessage **_reply,
-+                                 const char *path,
-+                                 const char *iface,
-+                                 const char *method,
-+                                 int first_arg_type,
-+                                 ...);
-+
-+#define sssctl_sifp_send(mem_ctx, sifp, reply, path, iface, method, ...) \
-+    _sssctl_sifp_send(mem_ctx, sifp, reply, path, iface, method,         \
-+                      ##__VA_ARGS__, DBUS_TYPE_INVALID);
-+
- errno_t sssctl_domain_list(struct sss_cmdline *cmdline,
-                            struct sss_tool_ctx *tool_ctx,
-                            void *pvt);
-diff --git a/src/tools/sssctl/sssctl_domains.c b/src/tools/sssctl/sssctl_domains.c
-index 17ad670f39dfc045ba090210ffcfa77df713c306..40962792b84eabeb2c142f158184b17180a01669 100644
---- a/src/tools/sssctl/sssctl_domains.c
-+++ b/src/tools/sssctl/sssctl_domains.c
-@@ -74,47 +74,32 @@ errno_t sssctl_domain_list(struct sss_cmdline *cmdline,
- }
- 
- static errno_t sssctl_domain_status_online(struct sss_tool_ctx *tool_ctx,
--                                           const char *domain_path,
--                                           bool force_start)
-+                                           sss_sifp_ctx *sifp,
-+                                           const char *domain_path)
- {
--    sss_sifp_ctx *sifp;
--    sss_sifp_error sifp_error;
--    DBusMessage *reply = NULL;
--    DBusMessage *msg;
-+    TALLOC_CTX *tmp_ctx;
-+    sss_sifp_error error;
-+    DBusMessage *reply;
-     bool is_online;
-     errno_t ret;
- 
--    if (!sssctl_start_sssd(force_start)) {
--        ret = ERR_SSSD_NOT_RUNNING;
--        goto done;
-+    tmp_ctx = talloc_new(NULL);
-+    if (tmp_ctx == NULL) {
-+        DEBUG(SSSDBG_CRIT_FAILURE, "talloc_new() failed\n");
-+        return ENOMEM;
-     }
- 
--    sifp_error = sssctl_sifp_init(tool_ctx, &sifp);
--    if (sifp_error != SSS_SIFP_OK) {
--        sssctl_sifp_error(sifp, sifp_error, "Unable to connect to the InfoPipe");
--        ret = EFAULT;
--        goto done;
--    }
--
--    msg = sbus_create_message(tool_ctx, SSS_SIFP_ADDRESS, domain_path,
--                              IFACE_IFP_DOMAINS_DOMAIN,
--                              IFACE_IFP_DOMAINS_DOMAIN_ISONLINE);
--    if (msg == NULL) {
--        DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create D-Bus message\n");
--        ret = ENOMEM;
--        goto done;
--    }
--
--    sifp_error = sss_sifp_send_message(sifp, msg, &reply);
--    if (sifp_error != SSS_SIFP_OK) {
--        sssctl_sifp_error(sifp, sifp_error, "Unable to get online status");
-+    error = sssctl_sifp_send(tmp_ctx, sifp, &reply, domain_path,
-+                             IFACE_IFP_DOMAINS_DOMAIN,
-+                             IFACE_IFP_DOMAINS_DOMAIN_ISONLINE);
-+    if (error != SSS_SIFP_OK) {
-+        sssctl_sifp_error(sifp, error, "Unable to get online status");
-         ret = EIO;
-         goto done;
-     }
- 
-     ret = sbus_parse_reply(reply, DBUS_TYPE_BOOLEAN, &is_online);
-     if (ret != EOK) {
--        fprintf(stderr, _("Unable to get information from SSSD\n"));
-         goto done;
-     }
- 
-@@ -123,10 +108,7 @@ static errno_t sssctl_domain_status_online(struct sss_tool_ctx *tool_ctx,
-     ret = EOK;
- 
- done:
--    if (reply != NULL) {
--        dbus_message_unref(reply);
--    }
--
-+    talloc_free(tmp_ctx);
-     return ret;
- }
- 
-@@ -144,6 +126,8 @@ errno_t sssctl_domain_status(struct sss_cmdline *cmdline,
-                              void *pvt)
- {
-     struct sssctl_domain_status_opts opts = {0};
-+    sss_sifp_ctx *sifp;
-+    sss_sifp_error error;
-     const char *path;
-     bool opt_set;
-     errno_t ret;
-@@ -181,7 +165,17 @@ errno_t sssctl_domain_status(struct sss_cmdline *cmdline,
-         return ENOMEM;
-     }
- 
--    ret = sssctl_domain_status_online(tool_ctx, path, opts.force_start);
-+    if (!sssctl_start_sssd(opts.force_start)) {
-+        return ERR_SSSD_NOT_RUNNING;
-+    }
-+
-+    error = sssctl_sifp_init(tool_ctx, &sifp);
-+    if (error != SSS_SIFP_OK) {
-+        sssctl_sifp_error(sifp, error, "Unable to connect to the InfoPipe");
-+        return EFAULT;
-+    }
-+
-+    ret = sssctl_domain_status_online(tool_ctx, sifp, path);
-     if (ret != EOK) {
-         fprintf(stderr, _("Unable to get online status\n"));
-         return ret;
-diff --git a/src/tools/sssctl/sssctl_sifp.c b/src/tools/sssctl/sssctl_sifp.c
-index e541c4b27ba38e50b209b0957c8b38f03afc891a..782a72d7ce8bbf1080c6d6ac988ffac2f432955f 100644
---- a/src/tools/sssctl/sssctl_sifp.c
-+++ b/src/tools/sssctl/sssctl_sifp.c
-@@ -116,3 +116,49 @@ void _sssctl_sifp_error(sss_sifp_ctx *sifp,
-         break;
-     }
- }
-+
-+sss_sifp_error _sssctl_sifp_send(TALLOC_CTX *mem_ctx,
-+                                 sss_sifp_ctx *sifp,
-+                                 DBusMessage **_reply,
-+                                 const char *path,
-+                                 const char *iface,
-+                                 const char *method,
-+                                 int first_arg_type,
-+                                 ...)
-+{
-+    sss_sifp_error error;
-+    DBusMessage *msg;
-+    dbus_bool_t bret;
-+    errno_t ret;
-+    va_list va;
-+
-+    msg = sss_sifp_create_message(path, iface, method);
-+    if (msg == NULL) {
-+        DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create D-Bus message\n");
-+        return SSS_SIFP_OUT_OF_MEMORY;
-+    }
-+
-+    va_start(va, first_arg_type);
-+    bret = dbus_message_append_args_valist(msg, first_arg_type, va);
-+    va_end(va);
-+    if (!bret) {
-+        DEBUG(SSSDBG_CRIT_FAILURE, "Failed to build message\n");
-+        error = SSS_SIFP_OUT_OF_MEMORY;
-+        goto done;
-+    }
-+
-+    error = sss_sifp_send_message(sifp, msg, _reply);
-+    if (error != SSS_SIFP_OK) {
-+        goto done;
-+    }
-+
-+    ret = sbus_talloc_bound_message(mem_ctx, *_reply);
-+    if (ret != EOK) {
-+        error = SSS_SIFP_OUT_OF_MEMORY;
-+        goto done;
-+    }
-+
-+done:
-+    dbus_message_unref(msg);
-+    return error;
-+}
--- 
-2.4.11
-
diff --git a/SOURCES/0095-failover-mark-subdomain-service-with-sd_-prefix.patch b/SOURCES/0095-failover-mark-subdomain-service-with-sd_-prefix.patch
deleted file mode 100644
index 4d9a1bf..0000000
--- a/SOURCES/0095-failover-mark-subdomain-service-with-sd_-prefix.patch
+++ /dev/null
@@ -1,92 +0,0 @@
-From a3b502fa85d493795c963e4298aeec3dc5806fcf Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Pavel=20B=C5=99ezina?= <pbrezina@redhat.com>
-Date: Wed, 29 Jun 2016 14:58:37 +0200
-Subject: [PATCH 095/102] failover: mark subdomain service with sd_ prefix
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Reviewed-by: Lukáš Slebodník <lslebodn@redhat.com>
-Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
-(cherry picked from commit 778f241e78241b0d6b8734148175f8dee804f494)
----
- src/providers/ad/ad_subdomains.c          | 11 +++++++++--
- src/providers/ipa/ipa_subdomains_server.c | 11 +++++++++--
- 2 files changed, 18 insertions(+), 4 deletions(-)
-
-diff --git a/src/providers/ad/ad_subdomains.c b/src/providers/ad/ad_subdomains.c
-index e9da04e384e598927f9c8c203a751bcccd29e895..a0d5c2e544fc62fda64771dce59b3b7ab8ecd8b6 100644
---- a/src/providers/ad/ad_subdomains.c
-+++ b/src/providers/ad/ad_subdomains.c
-@@ -66,6 +66,7 @@ ad_subdom_ad_ctx_new(struct be_ctx *be_ctx,
-     struct ad_options *ad_options;
-     struct ad_id_ctx *ad_id_ctx;
-     const char *gc_service_name;
-+    const char *service_name;
-     struct ad_srv_plugin_ctx *srv_ctx;
-     char *ad_domain;
-     char *ad_site_override;
-@@ -94,14 +95,20 @@ ad_subdom_ad_ctx_new(struct be_ctx *be_ctx,
- 
-     ad_site_override = dp_opt_get_string(ad_options->basic, AD_SITE);
- 
--    gc_service_name = talloc_asprintf(ad_options, "%s%s", "gc_", subdom->name);
-+    gc_service_name = talloc_asprintf(ad_options, "sd_gc_%s", subdom->name);
-     if (gc_service_name == NULL) {
-         talloc_free(ad_options);
-         return ENOMEM;
-     }
- 
-+    service_name = talloc_asprintf(ad_options, "sd_%s", subdom->name);
-+    if (service_name == NULL) {
-+        talloc_free(ad_options);
-+        return ENOMEM;
-+    }
-+
-     ret = ad_failover_init(ad_options, be_ctx, NULL, NULL, realm,
--                           subdom->name, gc_service_name,
-+                           service_name, gc_service_name,
-                            subdom->name, &ad_options->service);
-     if (ret != EOK) {
-         DEBUG(SSSDBG_OP_FAILURE, "Cannot initialize AD failover\n");
-diff --git a/src/providers/ipa/ipa_subdomains_server.c b/src/providers/ipa/ipa_subdomains_server.c
-index 43636098f6928006db0e0a9c05f2e39f8427d705..13640b04d66a1cdbbf0c8eab7648f7ebe921884b 100644
---- a/src/providers/ipa/ipa_subdomains_server.c
-+++ b/src/providers/ipa/ipa_subdomains_server.c
-@@ -203,6 +203,7 @@ ipa_ad_ctx_new(struct be_ctx *be_ctx,
-     struct ad_options *ad_options;
-     struct ad_id_ctx *ad_id_ctx;
-     const char *gc_service_name;
-+    const char *service_name;
-     struct ad_srv_plugin_ctx *srv_ctx;
-     const char *ad_domain;
-     const char *ad_site_override;
-@@ -250,17 +251,23 @@ ipa_ad_ctx_new(struct be_ctx *be_ctx,
-         DEBUG(SSSDBG_TRACE_ALL, "No extra attrs set.\n");
-     }
- 
--    gc_service_name = talloc_asprintf(ad_options, "%s%s", "gc_", subdom->forest);
-+    gc_service_name = talloc_asprintf(ad_options, "sd_gc_%s", subdom->forest);
-     if (gc_service_name == NULL) {
-         talloc_free(ad_options);
-         return ENOMEM;
-     }
- 
-+    service_name = talloc_asprintf(ad_options, "sd_%s", subdom->name);
-+    if (service_name == NULL) {
-+        talloc_free(ad_options);
-+        return ENOMEM;
-+    }
-+
-     /* Set KRB5 realm to same as the one of IPA when IPA
-      * is able to attach PAC. For testing, use hardcoded. */
-     ret = ad_failover_init(ad_options, be_ctx, NULL, NULL,
-                            id_ctx->server_mode->realm,
--                           subdom->name, gc_service_name,
-+                           service_name, gc_service_name,
-                            subdom->name, &ad_options->service);
-     if (ret != EOK) {
-         DEBUG(SSSDBG_OP_FAILURE, "Cannot initialize AD failover\n");
--- 
-2.4.11
-
diff --git a/SOURCES/0095-sssctl-integrate-pam_test_client-into-sssctl.patch b/SOURCES/0095-sssctl-integrate-pam_test_client-into-sssctl.patch
new file mode 100644
index 0000000..d942a65
--- /dev/null
+++ b/SOURCES/0095-sssctl-integrate-pam_test_client-into-sssctl.patch
@@ -0,0 +1,359 @@
+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
new file mode 100644
index 0000000..84984cf
--- /dev/null
+++ b/SOURCES/0096-i18n-adding-sssctl-files.patch
@@ -0,0 +1,34 @@
+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-sssctl-print-active-server-and-server-list.patch b/SOURCES/0096-sssctl-print-active-server-and-server-list.patch
deleted file mode 100644
index 8bb1918..0000000
--- a/SOURCES/0096-sssctl-print-active-server-and-server-list.patch
+++ /dev/null
@@ -1,1013 +0,0 @@
-From 7f4199c2d4dc9147be436005d75e03fc468f5349 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Pavel=20B=C5=99ezina?= <pbrezina@redhat.com>
-Date: Mon, 27 Jun 2016 13:56:13 +0200
-Subject: [PATCH 096/102] sssctl: print active server and server list
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Resolves:
-https://fedorahosted.org/sssd/ticket/3069
-
-Reviewed-by: Lukáš Slebodník <lslebodn@redhat.com>
-Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
-(cherry picked from commit bd4c2ed5aec7f57ea04500f0e43f151eedfdde45)
----
- src/providers/data_provider/dp_iface.c           |   6 +-
- src/providers/data_provider/dp_iface.h           |   8 +
- src/providers/data_provider/dp_iface.xml         |   8 +
- src/providers/data_provider/dp_iface_failover.c  | 297 ++++++++++++++++++++++-
- src/providers/data_provider/dp_iface_generated.c |  52 ++++
- src/providers/data_provider/dp_iface_generated.h |  10 +
- src/providers/fail_over.c                        |  42 ++++
- src/providers/fail_over.h                        |   4 +
- src/responder/ifp/ifp_domains.c                  |  48 ++++
- src/responder/ifp/ifp_domains.h                  |   8 +
- src/responder/ifp/ifp_iface.c                    |   4 +-
- src/responder/ifp/ifp_iface.xml                  |  10 +
- src/responder/ifp/ifp_iface_generated.c          |  52 ++++
- src/responder/ifp/ifp_iface_generated.h          |  10 +
- src/tools/sssctl/sssctl_domains.c                | 182 +++++++++++++-
- 15 files changed, 722 insertions(+), 19 deletions(-)
-
-diff --git a/src/providers/data_provider/dp_iface.c b/src/providers/data_provider/dp_iface.c
-index 8ed7274f0dd7b59598e2cf21e0dd59d16666df0b..4b2b0ddca68be8899f7285b4d881a91444b99362 100644
---- a/src/providers/data_provider/dp_iface.c
-+++ b/src/providers/data_provider/dp_iface.c
-@@ -42,8 +42,10 @@ struct iface_dp_backend iface_dp_backend = {
- };
- 
- struct iface_dp_failover iface_dp_failover = {
--    {&iface_dp_failover_meta, 0},
--    .ListServices = dp_failover_list_services
-+    { &iface_dp_failover_meta, 0 },
-+    .ListServices = dp_failover_list_services,
-+    .ActiveServer = dp_failover_active_server,
-+    .ListServers = dp_failover_list_servers
- };
- 
- static struct sbus_iface_map dp_map[] = {
-diff --git a/src/providers/data_provider/dp_iface.h b/src/providers/data_provider/dp_iface.h
-index 76e623d21c413fd68f8f3c9a91ea32fd707dc54d..5c6f0eb2f5dd68b63bda389e6fdd2446ca9efb21 100644
---- a/src/providers/data_provider/dp_iface.h
-+++ b/src/providers/data_provider/dp_iface.h
-@@ -69,4 +69,12 @@ errno_t dp_failover_list_services(struct sbus_request *sbus_req,
-                                   void *dp_cli,
-                                   const char *domname);
- 
-+errno_t dp_failover_active_server(struct sbus_request *sbus_req,
-+                                  void *dp_cli,
-+                                  const char *service_name);
-+
-+errno_t dp_failover_list_servers(struct sbus_request *sbus_req,
-+                                 void *dp_cli,
-+                                 const char *service_name);
-+
- #endif /* DP_IFACE_H_ */
-diff --git a/src/providers/data_provider/dp_iface.xml b/src/providers/data_provider/dp_iface.xml
-index eab7fc0f1500bf8890030352421da62c134115b9..992848a048ef9fe813d6ae05bbcabd0913ecb277 100644
---- a/src/providers/data_provider/dp_iface.xml
-+++ b/src/providers/data_provider/dp_iface.xml
-@@ -22,6 +22,14 @@
-             <arg name="domain_name" type="s" direction="in" />
-             <arg name="services" type="as" direction="out" />
-         </method>
-+        <method name="ActiveServer">
-+            <arg name="service_name" type="s" direction="in" />
-+            <arg name="server" type="s" direction="out" />
-+        </method>
-+        <method name="ListServers">
-+            <arg name="service_name" type="s" direction="in" />
-+            <arg name="servers" type="as" direction="out" />
-+        </method>
-     </interface>
- 
-     <interface name="org.freedesktop.sssd.dataprovider">
-diff --git a/src/providers/data_provider/dp_iface_failover.c b/src/providers/data_provider/dp_iface_failover.c
-index 038791088eeab7e9c5923996db77d2a107ff067d..7d95ffdd627604eb8c7e1b2882bf1665f792b660 100644
---- a/src/providers/data_provider/dp_iface_failover.c
-+++ b/src/providers/data_provider/dp_iface_failover.c
-@@ -28,20 +28,208 @@
- #include "providers/backend.h"
- #include "util/util.h"
- 
-+static errno_t
-+dp_failover_list_services_ldap(struct be_ctx *be_ctx,
-+                               const char **services,
-+                               int *_count)
-+{
-+    struct be_svc_data *svc;
-+    int count;
-+
-+    count = 0;
-+    DLIST_FOR_EACH(svc, be_ctx->be_fo->svcs) {
-+        services[count] = talloc_strdup(services, svc->name);
-+        if (services[count] == NULL) {
-+            DEBUG(SSSDBG_CRIT_FAILURE, "talloc_strdup() failed\n");
-+            return ENOMEM;
-+        }
-+        count++;
-+    }
-+
-+    *_count = count;
-+
-+    return EOK;
-+}
-+
-+static errno_t
-+dp_failover_list_services_ad(struct be_ctx *be_ctx,
-+                             struct sss_domain_info *domain,
-+                             const char **services,
-+                             int *_count)
-+{
-+    char *fo_svc_name = NULL;
-+    struct be_svc_data *svc;
-+    errno_t ret;
-+    int count;
-+
-+    fo_svc_name = talloc_asprintf(services, "sd_%s", domain->name);
-+    if (fo_svc_name == NULL) {
-+        ret = ENOMEM;
-+        goto done;
-+    }
-+
-+    count = 0;
-+    DLIST_FOR_EACH(svc, be_ctx->be_fo->svcs) {
-+        /* Drop each sd_gc_* since this service is not used with AD at all,
-+         * we only connect to AD_GC for global catalog. */
-+        if (strncasecmp(svc->name, "sd_gc_", strlen("sd_gc_")) == 0) {
-+            continue;
-+        }
-+
-+        /* Drop all subdomain services for different domain. */
-+        if (strncasecmp(svc->name, "sd_", strlen("sd_")) == 0) {
-+            if (!IS_SUBDOMAIN(domain)) {
-+                continue;
-+            }
-+
-+            if (strcasecmp(svc->name, fo_svc_name) != 0) {
-+                continue;
-+            }
-+        }
-+
-+        if (IS_SUBDOMAIN(domain)) {
-+            /* Drop AD since we connect to subdomain.com for LDAP. */
-+            if (strcasecmp(svc->name, "AD") == 0) {
-+                continue;
-+            }
-+        }
-+
-+        services[count] = talloc_strdup(services, svc->name);
-+        if (services[count] == NULL) {
-+            DEBUG(SSSDBG_CRIT_FAILURE, "talloc_strdup() failed\n");
-+            ret = ENOMEM;
-+            goto done;
-+        }
-+        count++;
-+    }
-+
-+    *_count = count;
-+
-+    ret = EOK;
-+
-+done:
-+    talloc_free(fo_svc_name);
-+    return ret;
-+}
-+
-+static errno_t
-+dp_failover_list_services_ipa(struct be_ctx *be_ctx,
-+                              struct sss_domain_info *domain,
-+                              const char **services,
-+                              int *_count)
-+{
-+    struct be_svc_data *svc;
-+    char *fo_svc_name = NULL;
-+    char *fo_gc_name = NULL;
-+    errno_t ret;
-+    int count;
-+
-+    fo_svc_name = talloc_asprintf(services, "sd_%s", domain->name);
-+    if (fo_svc_name == NULL) {
-+        ret = ENOMEM;
-+        goto done;
-+    }
-+
-+    fo_gc_name = talloc_asprintf(services, "sd_gc_%s", domain->name);
-+    if (fo_gc_name == NULL) {
-+        ret = ENOMEM;
-+        goto done;
-+    }
-+
-+    count = 0;
-+    DLIST_FOR_EACH(svc, be_ctx->be_fo->svcs) {
-+        /* Drop all subdomain services for different domain. */
-+        if (strncasecmp(svc->name, "sd_", strlen("sd_")) == 0) {
-+            if (!IS_SUBDOMAIN(domain)) {
-+                continue;
-+            }
-+
-+            if (strcasecmp(svc->name, fo_svc_name) != 0
-+                    && strcasecmp(svc->name, fo_gc_name) != 0) {
-+                continue;
-+            }
-+        }
-+
-+        services[count] = talloc_strdup(services, svc->name);
-+        if (services[count] == NULL) {
-+            DEBUG(SSSDBG_CRIT_FAILURE, "talloc_strdup() failed\n");
-+            return ENOMEM;
-+        }
-+        count++;
-+    }
-+
-+    *_count = count;
-+
-+    ret = EOK;
-+
-+done:
-+    talloc_free(fo_svc_name);
-+    talloc_free(fo_gc_name);
-+
-+    return ret;
-+}
-+
-+enum dp_fo_svc_type {
-+    DP_FO_SVC_LDAP = 0,
-+    DP_FO_SVC_AD = 1,
-+    DP_FO_SVC_IPA = 1 << 1,
-+    DP_FO_SVC_MIXED = DP_FO_SVC_AD | DP_FO_SVC_IPA
-+};
-+
- errno_t dp_failover_list_services(struct sbus_request *sbus_req,
-                                   void *dp_cli,
-                                   const char *domname)
- {
-+    enum dp_fo_svc_type svc_type = DP_FO_SVC_LDAP;
-+    struct sss_domain_info *domain;
-     struct be_ctx *be_ctx;
-     struct be_svc_data *svc;
-     const char **services;
-     int num_services;
-+    errno_t ret;
- 
-     be_ctx = dp_client_be(dp_cli);
- 
-+    if (SBUS_IS_STRING_EMPTY(domname)) {
-+        domain = be_ctx->domain;
-+    } else {
-+        domain = find_domain_by_name(be_ctx->domain, domname, false);
-+        if (domain == NULL) {
-+            sbus_request_reply_error(sbus_req, SBUS_ERROR_UNKNOWN_DOMAIN,
-+                                     "Unknown domain %s", domname);
-+            return EOK;
-+        }
-+    }
-+
-+    /**
-+     * Returning list of failover services is currently rather difficult
-+     * since there is only one failover context for the whole backend.
-+     *
-+     * The list of services for the given domain depends on whether it is
-+     * a master domain or a subdomain and whether we are using IPA, AD or
-+     * LDAP backend.
-+     *
-+     * For LDAP we just return everything we have.
-+     * For AD master domain we return AD, AD_GC.
-+     * For AD subdomain we return subdomain.com, AD_GC.
-+     * For IPA in client mode we return IPA.
-+     * For IPA in server mode we return IPA for master domain and
-+     * subdomain.com, gc_subdomain.com for subdomain.
-+     *
-+     * We also return everything else for all cases if any other service
-+     * such as kerberos is configured separately.
-+     */
-+
-+    /* Allocate enough space. */
-     num_services = 0;
-     DLIST_FOR_EACH(svc, be_ctx->be_fo->svcs) {
-         num_services++;
-+
-+        if (strcasecmp(svc->name, "AD") == 0) {
-+            svc_type |= DP_FO_SVC_AD;
-+        } else if (strcasecmp(svc->name, "IPA") == 0) {
-+            svc_type |= DP_FO_SVC_IPA;
-+        }
-     }
- 
-     services = talloc_zero_array(sbus_req, const char *, num_services);
-@@ -50,17 +238,108 @@ errno_t dp_failover_list_services(struct sbus_request *sbus_req,
-         return ENOMEM;
-     }
- 
--    num_services = 0;
--    DLIST_FOR_EACH(svc, be_ctx->be_fo->svcs) {
--        services[num_services] = talloc_strdup(services, svc->name);
--        if (services[num_services] == NULL) {
--            DEBUG(SSSDBG_CRIT_FAILURE, "talloc_strdup() failed\n");
--            talloc_free(services);
--            return ENOMEM;
--        }
--        num_services++;
-+    /* Fill the list. */
-+    switch (svc_type) {
-+    case DP_FO_SVC_LDAP:
-+    case DP_FO_SVC_MIXED:
-+        ret = dp_failover_list_services_ldap(be_ctx, services, &num_services);
-+        break;
-+    case DP_FO_SVC_AD:
-+        ret = dp_failover_list_services_ad(be_ctx, domain,
-+                                           services, &num_services);
-+        break;
-+    case DP_FO_SVC_IPA:
-+        ret = dp_failover_list_services_ipa(be_ctx, domain,
-+                                            services, &num_services);
-+        break;
-+    default:
-+        ret = ERR_INTERNAL;
-+        break;
-+    }
-+
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create service list [%d]: %s\n",
-+              ret, sss_strerror(ret));
-+        talloc_free(services);
-+        return ret;
-     }
- 
-     iface_dp_failover_ListServices_finish(sbus_req, services, num_services);
-     return EOK;
- }
-+
-+errno_t dp_failover_active_server(struct sbus_request *sbus_req,
-+                                  void *dp_cli,
-+                                  const char *service_name)
-+{
-+    struct be_ctx *be_ctx;
-+    struct be_svc_data *svc;
-+    const char *server;
-+    bool found = false;
-+
-+    be_ctx = dp_client_be(dp_cli);
-+
-+    DLIST_FOR_EACH(svc, be_ctx->be_fo->svcs) {
-+        if (strcmp(svc->name, service_name) == 0) {
-+            found = true;
-+            break;
-+        }
-+    }
-+
-+    if (!found) {
-+        DEBUG(SSSDBG_CRIT_FAILURE, "Unable to get server name\n");
-+        sbus_request_reply_error(sbus_req, SBUS_ERROR_NOT_FOUND,
-+                                 "Unknown service name");
-+        return EOK;
-+    }
-+
-+    if (svc->last_good_srv == NULL) {
-+        server = "";
-+    } else {
-+        server = fo_get_server_name(svc->last_good_srv);
-+        if (server == NULL) {
-+            DEBUG(SSSDBG_CRIT_FAILURE, "Unable to get server name\n");
-+            sbus_request_reply_error(sbus_req, SBUS_ERROR_INTERNAL,
-+                                     "Unable to get server name");
-+            return EOK;
-+        }
-+    }
-+
-+    iface_dp_failover_ActiveServer_finish(sbus_req, server);
-+    return EOK;
-+}
-+
-+errno_t dp_failover_list_servers(struct sbus_request *sbus_req,
-+                                 void *dp_cli,
-+                                 const char *service_name)
-+{
-+    struct be_ctx *be_ctx;
-+    struct be_svc_data *svc;
-+    const char **servers;
-+    bool found = false;
-+    size_t count;
-+
-+    be_ctx = dp_client_be(dp_cli);
-+
-+    DLIST_FOR_EACH(svc, be_ctx->be_fo->svcs) {
-+        if (strcmp(svc->name, service_name) == 0) {
-+            found = true;
-+            break;
-+        }
-+    }
-+
-+    if (!found) {
-+        DEBUG(SSSDBG_CRIT_FAILURE, "Unable to get server list\n");
-+        sbus_request_reply_error(sbus_req, SBUS_ERROR_NOT_FOUND,
-+                                 "Unknown service name");
-+        return EOK;
-+    }
-+
-+    servers = fo_svc_server_list(sbus_req, svc->fo_service, &count);
-+    if (servers == NULL) {
-+        return ENOMEM;
-+    }
-+
-+    iface_dp_failover_ListServers_finish(sbus_req, servers, (int)count);
-+    return EOK;
-+}
-diff --git a/src/providers/data_provider/dp_iface_generated.c b/src/providers/data_provider/dp_iface_generated.c
-index 7b36fd8aaeebb976a511c5592b1dd0ae28e9bb8a..fd2acb4f4bd8cf1dcbe8842cccc6dc2077fc83a2 100644
---- a/src/providers/data_provider/dp_iface_generated.c
-+++ b/src/providers/data_provider/dp_iface_generated.c
-@@ -111,6 +111,44 @@ int iface_dp_failover_ListServices_finish(struct sbus_request *req, const char *
-                                          DBUS_TYPE_INVALID);
- }
- 
-+/* arguments for org.freedesktop.sssd.DataProvider.Failover.ActiveServer */
-+const struct sbus_arg_meta iface_dp_failover_ActiveServer__in[] = {
-+    { "service_name", "s" },
-+    { NULL, }
-+};
-+
-+/* arguments for org.freedesktop.sssd.DataProvider.Failover.ActiveServer */
-+const struct sbus_arg_meta iface_dp_failover_ActiveServer__out[] = {
-+    { "server", "s" },
-+    { NULL, }
-+};
-+
-+int iface_dp_failover_ActiveServer_finish(struct sbus_request *req, const char *arg_server)
-+{
-+   return sbus_request_return_and_finish(req,
-+                                         DBUS_TYPE_STRING, &arg_server,
-+                                         DBUS_TYPE_INVALID);
-+}
-+
-+/* arguments for org.freedesktop.sssd.DataProvider.Failover.ListServers */
-+const struct sbus_arg_meta iface_dp_failover_ListServers__in[] = {
-+    { "service_name", "s" },
-+    { NULL, }
-+};
-+
-+/* arguments for org.freedesktop.sssd.DataProvider.Failover.ListServers */
-+const struct sbus_arg_meta iface_dp_failover_ListServers__out[] = {
-+    { "servers", "as" },
-+    { NULL, }
-+};
-+
-+int iface_dp_failover_ListServers_finish(struct sbus_request *req, const char *arg_servers[], int len_servers)
-+{
-+   return sbus_request_return_and_finish(req,
-+                                         DBUS_TYPE_ARRAY, DBUS_TYPE_STRING, &arg_servers, len_servers,
-+                                         DBUS_TYPE_INVALID);
-+}
-+
- /* methods for org.freedesktop.sssd.DataProvider.Failover */
- const struct sbus_method_meta iface_dp_failover__methods[] = {
-     {
-@@ -120,6 +158,20 @@ const struct sbus_method_meta iface_dp_failover__methods[] = {
-         offsetof(struct iface_dp_failover, ListServices),
-         invoke_s_method,
-     },
-+    {
-+        "ActiveServer", /* name */
-+        iface_dp_failover_ActiveServer__in,
-+        iface_dp_failover_ActiveServer__out,
-+        offsetof(struct iface_dp_failover, ActiveServer),
-+        invoke_s_method,
-+    },
-+    {
-+        "ListServers", /* name */
-+        iface_dp_failover_ListServers__in,
-+        iface_dp_failover_ListServers__out,
-+        offsetof(struct iface_dp_failover, ListServers),
-+        invoke_s_method,
-+    },
-     { NULL, }
- };
- 
-diff --git a/src/providers/data_provider/dp_iface_generated.h b/src/providers/data_provider/dp_iface_generated.h
-index 977ab3bae803ca002162b02d0c3d9677779983f4..7c2216aa27022769c707d80b59e9b436e72d1739 100644
---- a/src/providers/data_provider/dp_iface_generated.h
-+++ b/src/providers/data_provider/dp_iface_generated.h
-@@ -22,6 +22,8 @@
- /* constants for org.freedesktop.sssd.DataProvider.Failover */
- #define IFACE_DP_FAILOVER "org.freedesktop.sssd.DataProvider.Failover"
- #define IFACE_DP_FAILOVER_LISTSERVICES "ListServices"
-+#define IFACE_DP_FAILOVER_ACTIVESERVER "ActiveServer"
-+#define IFACE_DP_FAILOVER_LISTSERVERS "ListServers"
- 
- /* constants for org.freedesktop.sssd.dataprovider */
- #define IFACE_DP "org.freedesktop.sssd.dataprovider"
-@@ -72,11 +74,19 @@ int iface_dp_backend_IsOnline_finish(struct sbus_request *req, bool arg_status);
- struct iface_dp_failover {
-     struct sbus_vtable vtable; /* derive from sbus_vtable */
-     int (*ListServices)(struct sbus_request *req, void *data, const char *arg_domain_name);
-+    int (*ActiveServer)(struct sbus_request *req, void *data, const char *arg_service_name);
-+    int (*ListServers)(struct sbus_request *req, void *data, const char *arg_service_name);
- };
- 
- /* finish function for ListServices */
- int iface_dp_failover_ListServices_finish(struct sbus_request *req, const char *arg_services[], int len_services);
- 
-+/* finish function for ActiveServer */
-+int iface_dp_failover_ActiveServer_finish(struct sbus_request *req, const char *arg_server);
-+
-+/* 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 */
- struct iface_dp {
-     struct sbus_vtable vtable; /* derive from sbus_vtable */
-diff --git a/src/providers/fail_over.c b/src/providers/fail_over.c
-index 1d88d2aa54bfdebd4b648e2b13fa8d03e2be3973..8ab39f27f77e19e601855632196006a8dbbdf136 100644
---- a/src/providers/fail_over.c
-+++ b/src/providers/fail_over.c
-@@ -1660,6 +1660,48 @@ bool fo_svc_has_server(struct fo_service *service, struct fo_server *server)
-     return false;
- }
- 
-+const char **fo_svc_server_list(TALLOC_CTX *mem_ctx,
-+                                struct fo_service *service,
-+                                size_t *_count)
-+{
-+    const char **list;
-+    const char *server;
-+    struct fo_server *srv;
-+    size_t count;
-+
-+    count = 0;
-+    DLIST_FOR_EACH(srv, service->server_list) {
-+        count++;
-+    }
-+
-+    list = talloc_zero_array(mem_ctx, const char *, count + 1);
-+    if (list == NULL) {
-+        return NULL;
-+    }
-+
-+    count = 0;
-+    DLIST_FOR_EACH(srv, service->server_list) {
-+        server = fo_get_server_name(srv);
-+        if (server == NULL) {
-+            /* _srv_ */
-+            continue;
-+        }
-+
-+        list[count] = talloc_strdup(list, server);
-+        if (list[count] == NULL) {
-+            talloc_free(list);
-+            return NULL;
-+        }
-+        count++;
-+    }
-+
-+    if (_count != NULL) {
-+        *_count = count;
-+    }
-+
-+    return list;
-+}
-+
- bool fo_set_srv_lookup_plugin(struct fo_ctx *ctx,
-                               fo_srv_lookup_plugin_send_t send_fn,
-                               fo_srv_lookup_plugin_recv_t recv_fn,
-diff --git a/src/providers/fail_over.h b/src/providers/fail_over.h
-index f24b5715f13931965400c20562a1578aaf756908..d70212fb7ea569b9c47bba36704aa8ae18754cbb 100644
---- a/src/providers/fail_over.h
-+++ b/src/providers/fail_over.h
-@@ -212,6 +212,10 @@ struct fo_server *fo_get_active_server(struct fo_service *service);
- 
- bool fo_svc_has_server(struct fo_service *service, struct fo_server *server);
- 
-+const char **fo_svc_server_list(TALLOC_CTX *mem_ctx,
-+                                struct fo_service *service,
-+                                size_t *_count);
-+
- /*
-  * pvt will be talloc_stealed to ctx
-  */
-diff --git a/src/responder/ifp/ifp_domains.c b/src/responder/ifp/ifp_domains.c
-index ff690ed6a7d5519979d242a4d5dadd08aff50347..977bbfcbe818f08873ce072d34fdcf900cabf52f 100644
---- a/src/responder/ifp/ifp_domains.c
-+++ b/src/responder/ifp/ifp_domains.c
-@@ -582,3 +582,51 @@ int ifp_domains_domain_list_services(struct sbus_request *sbus_req,
- 
-     return EOK;
- }
-+
-+int ifp_domains_domain_active_server(struct sbus_request *sbus_req,
-+                                     void *data,
-+                                     const char *service)
-+{
-+    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_FAILOVER,
-+                               IFACE_DP_FAILOVER_ACTIVESERVER,
-+                               DBUS_TYPE_STRING, &service);
-+
-+    return EOK;
-+}
-+
-+int ifp_domains_domain_list_servers(struct sbus_request *sbus_req,
-+                                    void *data,
-+                                    const char *service)
-+{
-+    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_FAILOVER,
-+                               IFACE_DP_FAILOVER_LISTSERVERS,
-+                               DBUS_TYPE_STRING, &service);
-+
-+    return EOK;
-+}
-diff --git a/src/responder/ifp/ifp_domains.h b/src/responder/ifp/ifp_domains.h
-index 91645e60701f8f75e89a42e93e2c066def67b018..621ba6158e285911cb8298cef212219dfd3afec8 100644
---- a/src/responder/ifp/ifp_domains.h
-+++ b/src/responder/ifp/ifp_domains.h
-@@ -100,4 +100,12 @@ int ifp_domains_domain_is_online(struct sbus_request *sbus_req,
- int ifp_domains_domain_list_services(struct sbus_request *sbus_req,
-                                      void *data);
- 
-+int ifp_domains_domain_active_server(struct sbus_request *sbus_req,
-+                                     void *data,
-+                                     const char *service);
-+
-+int ifp_domains_domain_list_servers(struct sbus_request *sbus_req,
-+                                    void *data,
-+                                    const char *service);
-+
- #endif /* IFP_DOMAINS_H_ */
-diff --git a/src/responder/ifp/ifp_iface.c b/src/responder/ifp/ifp_iface.c
-index 90bb52b2ccf5207034abbe12bddbfa1eeaf875f7..e6ddc687ba9db878ee39fee5868d1f924d58482d 100644
---- a/src/responder/ifp/ifp_iface.c
-+++ b/src/responder/ifp/ifp_iface.c
-@@ -81,7 +81,9 @@ struct iface_ifp_domains iface_ifp_domains = {
- struct iface_ifp_domains_domain iface_ifp_domains_domain = {
-     { &iface_ifp_domains_domain_meta, 0 },
-     .IsOnline = ifp_domains_domain_is_online,
--    .ListServices = ifp_domains_domain_list_services
-+    .ListServices = ifp_domains_domain_list_services,
-+    .ActiveServer = ifp_domains_domain_active_server,
-+    .ListServers = ifp_domains_domain_list_servers
- };
- 
- struct iface_ifp_users iface_ifp_users = {
-diff --git a/src/responder/ifp/ifp_iface.xml b/src/responder/ifp/ifp_iface.xml
-index 7f6f47299deeba4b1baa23d1e63ee7bb17304a59..25b104ad70c0fd84b6c0fe9dbb0dc6e6439c1376 100644
---- a/src/responder/ifp/ifp_iface.xml
-+++ b/src/responder/ifp/ifp_iface.xml
-@@ -112,6 +112,16 @@
-         <method name="ListServices">
-             <arg name="services" type="as" direction="out" />
-         </method>
-+
-+        <method name="ActiveServer">
-+            <arg name="service" type="s" direction="in" />
-+            <arg name="server" type="s" direction="out" />
-+        </method>
-+
-+        <method name="ListServers">
-+            <arg name="service_name" type="s" direction="in" />
-+            <arg name="servers" type="as" direction="out" />
-+        </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 4d3bb5727b03ae64adad14fcdbb3eb5366edb406..6156ca2947434f301d206232f83cfc0647007707 100644
---- a/src/responder/ifp/ifp_iface_generated.c
-+++ b/src/responder/ifp/ifp_iface_generated.c
-@@ -558,6 +558,44 @@ int iface_ifp_domains_domain_ListServices_finish(struct sbus_request *req, const
-                                          DBUS_TYPE_INVALID);
- }
- 
-+/* arguments for org.freedesktop.sssd.infopipe.Domains.Domain.ActiveServer */
-+const struct sbus_arg_meta iface_ifp_domains_domain_ActiveServer__in[] = {
-+    { "service", "s" },
-+    { NULL, }
-+};
-+
-+/* arguments for org.freedesktop.sssd.infopipe.Domains.Domain.ActiveServer */
-+const struct sbus_arg_meta iface_ifp_domains_domain_ActiveServer__out[] = {
-+    { "server", "s" },
-+    { NULL, }
-+};
-+
-+int iface_ifp_domains_domain_ActiveServer_finish(struct sbus_request *req, const char *arg_server)
-+{
-+   return sbus_request_return_and_finish(req,
-+                                         DBUS_TYPE_STRING, &arg_server,
-+                                         DBUS_TYPE_INVALID);
-+}
-+
-+/* arguments for org.freedesktop.sssd.infopipe.Domains.Domain.ListServers */
-+const struct sbus_arg_meta iface_ifp_domains_domain_ListServers__in[] = {
-+    { "service_name", "s" },
-+    { NULL, }
-+};
-+
-+/* arguments for org.freedesktop.sssd.infopipe.Domains.Domain.ListServers */
-+const struct sbus_arg_meta iface_ifp_domains_domain_ListServers__out[] = {
-+    { "servers", "as" },
-+    { NULL, }
-+};
-+
-+int iface_ifp_domains_domain_ListServers_finish(struct sbus_request *req, const char *arg_servers[], int len_servers)
-+{
-+   return sbus_request_return_and_finish(req,
-+                                         DBUS_TYPE_ARRAY, DBUS_TYPE_STRING, &arg_servers, len_servers,
-+                                         DBUS_TYPE_INVALID);
-+}
-+
- /* methods for org.freedesktop.sssd.infopipe.Domains.Domain */
- const struct sbus_method_meta iface_ifp_domains_domain__methods[] = {
-     {
-@@ -574,6 +612,20 @@ const struct sbus_method_meta iface_ifp_domains_domain__methods[] = {
-         offsetof(struct iface_ifp_domains_domain, ListServices),
-         NULL, /* no invoker */
-     },
-+    {
-+        "ActiveServer", /* name */
-+        iface_ifp_domains_domain_ActiveServer__in,
-+        iface_ifp_domains_domain_ActiveServer__out,
-+        offsetof(struct iface_ifp_domains_domain, ActiveServer),
-+        invoke_s_method,
-+    },
-+    {
-+        "ListServers", /* name */
-+        iface_ifp_domains_domain_ListServers__in,
-+        iface_ifp_domains_domain_ListServers__out,
-+        offsetof(struct iface_ifp_domains_domain, ListServers),
-+        invoke_s_method,
-+    },
-     { NULL, }
- };
- 
-diff --git a/src/responder/ifp/ifp_iface_generated.h b/src/responder/ifp/ifp_iface_generated.h
-index 2eff57410e5292a05818050b96eb85aa3a4f2e16..141348249d2da5447fa04495564a8c6a55d67a1b 100644
---- a/src/responder/ifp/ifp_iface_generated.h
-+++ b/src/responder/ifp/ifp_iface_generated.h
-@@ -58,6 +58,8 @@
- #define IFACE_IFP_DOMAINS_DOMAIN "org.freedesktop.sssd.infopipe.Domains.Domain"
- #define IFACE_IFP_DOMAINS_DOMAIN_ISONLINE "IsOnline"
- #define IFACE_IFP_DOMAINS_DOMAIN_LISTSERVICES "ListServices"
-+#define IFACE_IFP_DOMAINS_DOMAIN_ACTIVESERVER "ActiveServer"
-+#define IFACE_IFP_DOMAINS_DOMAIN_LISTSERVERS "ListServers"
- 
- /* constants for org.freedesktop.sssd.infopipe.Cache */
- #define IFACE_IFP_CACHE "org.freedesktop.sssd.infopipe.Cache"
-@@ -215,6 +217,8 @@ struct iface_ifp_domains_domain {
-     struct sbus_vtable vtable; /* derive from sbus_vtable */
-     int (*IsOnline)(struct sbus_request *req, void *data);
-     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);
- };
- 
- /* finish function for IsOnline */
-@@ -223,6 +227,12 @@ int iface_ifp_domains_domain_IsOnline_finish(struct sbus_request *req, bool arg_
- /* finish function for ListServices */
- int iface_ifp_domains_domain_ListServices_finish(struct sbus_request *req, const char *arg_services[], int len_services);
- 
-+/* finish function for ActiveServer */
-+int iface_ifp_domains_domain_ActiveServer_finish(struct sbus_request *req, const char *arg_server);
-+
-+/* finish function for ListServers */
-+int iface_ifp_domains_domain_ListServers_finish(struct sbus_request *req, const char *arg_servers[], int len_servers);
-+
- /* vtable for org.freedesktop.sssd.infopipe.Cache */
- struct iface_ifp_cache {
-     struct sbus_vtable vtable; /* derive from sbus_vtable */
-diff --git a/src/tools/sssctl/sssctl_domains.c b/src/tools/sssctl/sssctl_domains.c
-index 40962792b84eabeb2c142f158184b17180a01669..545ed95f4415da597b191146409ea6ba028f36f8 100644
---- a/src/tools/sssctl/sssctl_domains.c
-+++ b/src/tools/sssctl/sssctl_domains.c
-@@ -112,6 +112,155 @@ done:
-     return ret;
- }
- 
-+static const char *proper_service_name(const char *service)
-+{
-+    if (strcasecmp(service, "AD_GC") == 0) {
-+        return "AD Global Catalog";
-+    } else if (strcasecmp(service, "AD") == 0) {
-+        return "AD Domain Controller";
-+    } else if (strncasecmp(service, "sd_gc_", strlen("sd_gc_")) == 0) {
-+        return "AD Global Catalog";
-+    } else if (strncasecmp(service, "sd_", strlen("sd_")) == 0) {
-+        return "AD Domain Controller";
-+    }
-+
-+    return service;
-+}
-+
-+static errno_t sssctl_domain_status_active_server(struct sss_tool_ctx *tool_ctx,
-+                                                  sss_sifp_ctx *sifp,
-+                                                  const char *domain_path)
-+{
-+    TALLOC_CTX *tmp_ctx;
-+    sss_sifp_error error;
-+    DBusMessage *reply;
-+    const char *server;
-+    const char **services;
-+    int num_services;
-+    errno_t ret;
-+    int i;
-+
-+    tmp_ctx = talloc_new(NULL);
-+    if (tmp_ctx == NULL) {
-+        DEBUG(SSSDBG_CRIT_FAILURE, "talloc_new() failed\n");
-+        return ENOMEM;
-+    }
-+
-+    error = sssctl_sifp_send(tmp_ctx, sifp, &reply, domain_path,
-+                             IFACE_IFP_DOMAINS_DOMAIN,
-+                             IFACE_IFP_DOMAINS_DOMAIN_LISTSERVICES);
-+    if (error != SSS_SIFP_OK) {
-+        sssctl_sifp_error(sifp, error, "Unable to list services");
-+        ret = EIO;
-+        goto done;
-+    }
-+
-+    ret = sbus_parse_reply(reply, DBUS_TYPE_ARRAY, DBUS_TYPE_STRING,
-+                           &services, &num_services);
-+    if (ret != EOK) {
-+        goto done;
-+    }
-+
-+    printf(_("Active servers:\n"));
-+    for (i = 0; i < num_services; i++) {
-+        error = sssctl_sifp_send(tmp_ctx, sifp, &reply, domain_path,
-+                                 IFACE_IFP_DOMAINS_DOMAIN,
-+                                 IFACE_IFP_DOMAINS_DOMAIN_ACTIVESERVER,
-+                                 DBUS_TYPE_STRING, &services[i]);
-+        if (error != SSS_SIFP_OK) {
-+            sssctl_sifp_error(sifp, error, "Unable to get active server");
-+            ret = EIO;
-+            goto done;
-+        }
-+
-+        ret = sbus_parse_reply(reply, DBUS_TYPE_STRING, &server);
-+        if (ret != EOK) {
-+            goto done;
-+        }
-+
-+        server = SBUS_IS_STRING_EMPTY(server) ? _("not connected") : server;
-+        printf("%s: %s\n", proper_service_name(services[i]), server);
-+    }
-+
-+    ret = EOK;
-+
-+done:
-+    talloc_free(tmp_ctx);
-+    return ret;
-+}
-+
-+static errno_t sssctl_domain_status_server_list(struct sss_tool_ctx *tool_ctx,
-+                                                sss_sifp_ctx *sifp,
-+                                                const char *domain_path)
-+{
-+    TALLOC_CTX *tmp_ctx;
-+    sss_sifp_error error;
-+    DBusMessage *reply;
-+    const char **servers;
-+    int num_servers;
-+    const char **services;
-+    int num_services;
-+    errno_t ret;
-+    int i, j;
-+
-+    tmp_ctx = talloc_new(NULL);
-+    if (tmp_ctx == NULL) {
-+        DEBUG(SSSDBG_CRIT_FAILURE, "talloc_new() failed\n");
-+        return ENOMEM;
-+    }
-+
-+    error = sssctl_sifp_send(tmp_ctx, sifp, &reply, domain_path,
-+                             IFACE_IFP_DOMAINS_DOMAIN,
-+                             IFACE_IFP_DOMAINS_DOMAIN_LISTSERVICES);
-+    if (error != SSS_SIFP_OK) {
-+        sssctl_sifp_error(sifp, error, "Unable to list services");
-+        ret = EIO;
-+        goto done;
-+    }
-+
-+    ret = sbus_parse_reply(reply, DBUS_TYPE_ARRAY, DBUS_TYPE_STRING,
-+                           &services, &num_services);
-+    if (ret != EOK) {
-+        goto done;
-+    }
-+
-+    for (i = 0; i < num_services; i++) {
-+        printf(_("Discovered %s servers:\n"), proper_service_name(services[i]));
-+        error = sssctl_sifp_send(tmp_ctx, sifp, &reply, domain_path,
-+                                 IFACE_IFP_DOMAINS_DOMAIN,
-+                                 IFACE_IFP_DOMAINS_DOMAIN_LISTSERVERS,
-+                                 DBUS_TYPE_STRING, &services[i]);
-+        if (error != SSS_SIFP_OK) {
-+            sssctl_sifp_error(sifp, error, "Unable to get active server");
-+            ret = EIO;
-+            goto done;
-+        }
-+
-+        ret = sbus_parse_reply(reply, DBUS_TYPE_ARRAY, DBUS_TYPE_STRING,
-+                               &servers, &num_servers);
-+        if (ret != EOK) {
-+            goto done;
-+        }
-+
-+        if (num_servers == 0) {
-+            puts(_("None so far.\n"));
-+            continue;
-+        }
-+
-+        for (j = 0; j < num_servers; j++) {
-+            printf("- %s\n", servers[j]);
-+        }
-+
-+        printf("\n");
-+    }
-+
-+    ret = EOK;
-+
-+done:
-+    talloc_free(tmp_ctx);
-+    return ret;
-+}
-+
- struct sssctl_domain_status_opts {
-     const char *domain;
-     int online;
-@@ -135,11 +284,8 @@ errno_t sssctl_domain_status(struct sss_cmdline *cmdline,
-     /* Parse command line. */
-     struct poptOption options[] = {
-         {"online", 'o', POPT_ARG_NONE , &opts.online, 0, _("Show online status"), NULL },
--        /*
--        {"last-requests", 'l', POPT_ARG_NONE, &opts.last, 0, _("Show last requests that went to data provider"), NULL },
-         {"active-server", 'a', POPT_ARG_NONE, &opts.active, 0, _("Show information about active server"), NULL },
-         {"servers", 'r', POPT_ARG_NONE, &opts.servers, 0, _("Show list of discovered servers"), NULL },
--        */
-         {"start", 's', POPT_ARG_NONE, &opts.force_start, 0, _("Start SSSD if it is not running"), NULL },
-         POPT_TABLEEND
-     };
-@@ -175,10 +321,32 @@ errno_t sssctl_domain_status(struct sss_cmdline *cmdline,
-         return EFAULT;
-     }
- 
--    ret = sssctl_domain_status_online(tool_ctx, sifp, path);
--    if (ret != EOK) {
--        fprintf(stderr, _("Unable to get online status\n"));
--        return ret;
-+    if (opts.online) {
-+        ret = sssctl_domain_status_online(tool_ctx, sifp, path);
-+        if (ret != EOK) {
-+            fprintf(stderr, _("Unable to get online status\n"));
-+            return ret;
-+        }
-+
-+        printf("\n");
-+    }
-+
-+    if (opts.active) {
-+        ret = sssctl_domain_status_active_server(tool_ctx, sifp, path);
-+        if (ret != EOK) {
-+            fprintf(stderr, _("Unable to get online status\n"));
-+            return ret;
-+        }
-+
-+        printf("\n");
-+    }
-+
-+    if (opts.servers) {
-+        ret = sssctl_domain_status_server_list(tool_ctx, sifp, path);
-+        if (ret != EOK) {
-+            fprintf(stderr, _("Unable to get server list\n"));
-+            return ret;
-+        }
-     }
- 
-     return EOK;
--- 
-2.4.11
-
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
new file mode 100644
index 0000000..a38530b
--- /dev/null
+++ b/SOURCES/0097-responders-do-not-leak-selinux-context-on-clients-de.patch
@@ -0,0 +1,69 @@
+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/0097-sifp-fix-coverity-warning.patch b/SOURCES/0097-sifp-fix-coverity-warning.patch
deleted file mode 100644
index 6a761a7..0000000
--- a/SOURCES/0097-sifp-fix-coverity-warning.patch
+++ /dev/null
@@ -1,47 +0,0 @@
-From 87f7c2f1f44085963b41eb78e337840ddbc7be76 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Pavel=20B=C5=99ezina?= <pbrezina@redhat.com>
-Date: Thu, 14 Jul 2016 10:49:37 +0200
-Subject: [PATCH 097/102] sifp: fix coverity warning
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-sssd-1.14.1/src/lib/sifp/sss_sifp_dbus.c:51: check_return:
-  Calling "dbus_message_append_args_valist" without checking return value
-  (as is done elsewhere 4 out of 5 times).
-
-Reviewed-by: Lukáš Slebodník <lslebodn@redhat.com>
-Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
-(cherry picked from commit de5160e354c02020f0593c7cabdb811107d5d8e2)
----
- src/lib/sifp/sss_sifp_dbus.c | 7 ++++++-
- 1 file changed, 6 insertions(+), 1 deletion(-)
-
-diff --git a/src/lib/sifp/sss_sifp_dbus.c b/src/lib/sifp/sss_sifp_dbus.c
-index 7c72c52f0d226ccdfaf7b8ffaed7776647a7771c..2906c5ac383c412231127f6ffa8081d47eb2bced 100644
---- a/src/lib/sifp/sss_sifp_dbus.c
-+++ b/src/lib/sifp/sss_sifp_dbus.c
-@@ -36,6 +36,7 @@ static sss_sifp_error sss_sifp_ifp_call(sss_sifp_ctx *ctx,
- {
-    DBusMessage *msg = NULL;
-    sss_sifp_error ret;
-+   dbus_bool_t bret;
- 
-    if (object_path == NULL || interface == NULL || method == NULL) {
-        return SSS_SIFP_INVALID_ARGUMENT;
-@@ -48,7 +49,11 @@ static sss_sifp_error sss_sifp_ifp_call(sss_sifp_ctx *ctx,
-    }
- 
-    if (first_arg_type != DBUS_TYPE_INVALID) {
--       dbus_message_append_args_valist(msg, first_arg_type, ap);
-+       bret = dbus_message_append_args_valist(msg, first_arg_type, ap);
-+       if (!bret) {
-+           ret = SSS_SIFP_IO_ERROR;
-+           goto done;
-+       }
-    }
- 
-    ret = sss_sifp_send_message(ctx, msg, _reply);
--- 
-2.4.11
-
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
new file mode 100644
index 0000000..5a49b15
--- /dev/null
+++ b/SOURCES/0098-ipa_s2n_get_acct_info_send-provide-correct-req_input.patch
@@ -0,0 +1,89 @@
+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/0098-sbus-allow-freeing-msg-through-dbus-api-when-using-t.patch b/SOURCES/0098-sbus-allow-freeing-msg-through-dbus-api-when-using-t.patch
deleted file mode 100644
index 37be3b9..0000000
--- a/SOURCES/0098-sbus-allow-freeing-msg-through-dbus-api-when-using-t.patch
+++ /dev/null
@@ -1,112 +0,0 @@
-From efb18a2688546db9c6fe7ba75b595a2fc54dff41 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Pavel=20B=C5=99ezina?= <pbrezina@redhat.com>
-Date: Fri, 15 Jul 2016 14:50:41 +0200
-Subject: [PATCH 098/102] sbus: allow freeing msg through dbus api when using
- talloc
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-When a talloc-bound message was freed by removing all references
-to it with dbus_message_unref we failed to free the talloc context
-and thus leaking memory or unreferencing invalid message when
-the parent context is freed.
-
-This patch allows to bound dbus message to talloc in the way that
-allows us to free the message by both talloc and dbus api.
-
-Reviewed-by: Lukáš Slebodník <lslebodn@redhat.com>
-(cherry picked from commit 5d556f70f00c43864d8495d7caacfadf962799df)
----
- src/sbus/sssd_dbus_utils.c | 51 ++++++++++++++++++++++++++++++++++++++++++++++
- 1 file changed, 51 insertions(+)
-
-diff --git a/src/sbus/sssd_dbus_utils.c b/src/sbus/sssd_dbus_utils.c
-index 4c33f9fd75cac2d4a56a5638982f8ecb73da8e2e..b0150e2fe7f829013677e0a4a894d1468e5b9128 100644
---- a/src/sbus/sssd_dbus_utils.c
-+++ b/src/sbus/sssd_dbus_utils.c
-@@ -25,22 +25,52 @@
- 
- struct sbus_talloc_msg {
-     DBusMessage *msg;
-+    dbus_int32_t data_slot;
-+    bool in_talloc_destructor;
- };
- 
- static int sbus_talloc_msg_destructor(struct sbus_talloc_msg *talloc_msg)
- {
-+    talloc_msg->in_talloc_destructor = true;
-+
-     if (talloc_msg->msg == NULL) {
-         return 0;
-     }
- 
-+    /* There may exist more references to this message but this talloc
-+     * context is no longer valid. We remove dbus message data to invoke
-+     * dbus destructor now. */
-+    dbus_message_set_data(talloc_msg->msg, talloc_msg->data_slot, NULL, NULL);
-     dbus_message_unref(talloc_msg->msg);
-     return 0;
- }
- 
-+static void sbus_msg_data_destructor(void *ctx)
-+{
-+    struct sbus_talloc_msg *talloc_msg;
-+
-+    talloc_msg = talloc_get_type(ctx, struct sbus_talloc_msg);
-+
-+    dbus_message_free_data_slot(&talloc_msg->data_slot);
-+
-+    if (!talloc_msg->in_talloc_destructor) {
-+        /* References to this message dropped to zero but through
-+         * dbus_message_unref(), not by calling talloc_free(). We need to free
-+         * the talloc context and avoid running talloc desctuctor. */
-+        talloc_set_destructor(talloc_msg, NULL);
-+        talloc_free(talloc_msg);
-+    }
-+}
-+
- errno_t sbus_talloc_bound_message(TALLOC_CTX *mem_ctx, DBusMessage *msg)
- {
-     struct sbus_talloc_msg *talloc_msg;
-+    dbus_int32_t data_slot = -1;
-+    DBusFreeFunction free_fn;
-+    dbus_bool_t bret;
- 
-+    /* Create a talloc context that will unreference this message when
-+     * the parent context is freed. */
-     talloc_msg = talloc(mem_ctx, struct sbus_talloc_msg);
-     if (talloc_msg == NULL) {
-         DEBUG(SSSDBG_CRIT_FAILURE,
-@@ -48,7 +78,28 @@ errno_t sbus_talloc_bound_message(TALLOC_CTX *mem_ctx, DBusMessage *msg)
-         return ENOMEM;
-     }
- 
-+    /* Allocate a dbus message data slot that will contain point to the
-+     * talloc context so we can pick up cases when the dbus message is
-+     * freed through dbus api. */
-+    bret = dbus_message_allocate_data_slot(&data_slot);
-+    if (!bret) {
-+        DEBUG(SSSDBG_CRIT_FAILURE, "Unable to allocate data slot!\n");
-+        talloc_free(talloc_msg);
-+        return ENOMEM;
-+    }
-+
-+    free_fn = sbus_msg_data_destructor;
-+    bret = dbus_message_set_data(msg, data_slot, talloc_msg, free_fn);
-+    if (!bret) {
-+        DEBUG(SSSDBG_CRIT_FAILURE, "Unable to set message data!\n");
-+        talloc_free(talloc_msg);
-+        dbus_message_free_data_slot(&data_slot);
-+        return ENOMEM;
-+    }
-+
-     talloc_msg->msg = msg;
-+    talloc_msg->data_slot = data_slot;
-+    talloc_msg->in_talloc_destructor = false;
- 
-     talloc_set_destructor(talloc_msg, sbus_talloc_msg_destructor);
- 
--- 
-2.4.11
-
diff --git a/SOURCES/0099-PROXY-Do-not-abuse-data-provider-interface.patch b/SOURCES/0099-PROXY-Do-not-abuse-data-provider-interface.patch
deleted file mode 100644
index df4370b..0000000
--- a/SOURCES/0099-PROXY-Do-not-abuse-data-provider-interface.patch
+++ /dev/null
@@ -1,730 +0,0 @@
-From 4b23c3128726fe59e02d28352e37bb0ff7f97640 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Pavel=20B=C5=99ezina?= <pbrezina@redhat.com>
-Date: Fri, 15 Jul 2016 14:20:32 +0200
-Subject: [PATCH 099/102] PROXY: Do not abuse data provider interface
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-We want to use custom interface for proxy provider so we do not
-abuse the data provider one. This way we gain more control over
-it and we can remove the old interface entirely.
-
-Reviewed-by: Lukáš Slebodník <lslebodn@redhat.com>
-(cherry picked from commit e07d700ed9daf0cf96607fa2d72978cb2431b794)
----
- Makefile.am                                 |   6 +-
- src/providers/dp_auth_util.c                |  64 ---------------
- src/providers/proxy/proxy.h                 |   2 +
- src/providers/proxy/proxy_auth.c            |   8 +-
- src/providers/proxy/proxy_child.c           | 119 +++++++++++++++-------------
- src/providers/proxy/proxy_client.c          | 108 +++++++++++--------------
- src/providers/proxy/proxy_iface.xml         |  17 ++++
- src/providers/proxy/proxy_iface_generated.c |  80 +++++++++++++++++++
- src/providers/proxy/proxy_iface_generated.h |  71 +++++++++++++++++
- 9 files changed, 288 insertions(+), 187 deletions(-)
- create mode 100644 src/providers/proxy/proxy_iface.xml
- create mode 100644 src/providers/proxy/proxy_iface_generated.c
- create mode 100644 src/providers/proxy/proxy_iface_generated.h
-
-diff --git a/Makefile.am b/Makefile.am
-index 1837e36da7302cb51c0b90e51b762ce0a87cd65f..5d54838659e44fa446fc921d014e48ac91469b25 100644
---- a/Makefile.am
-+++ b/Makefile.am
-@@ -715,6 +715,7 @@ dist_noinst_HEADERS = \
-     src/providers/ad/ad_domain_info.h \
-     src/providers/ad/ad_subdomains.h \
-     src/providers/proxy/proxy.h \
-+    src/providers/proxy/proxy_iface_generated.h \
-     src/tools/tools_util.h \
-     src/tools/sss_sync_ops.h \
-     src/resolv/async_resolv.h \
-@@ -1197,6 +1198,7 @@ CODEGEN_XML = \
-     $(srcdir)/src/monitor/monitor_iface.xml \
-     $(srcdir)/src/providers/data_provider_iface.xml \
-     $(srcdir)/src/providers/data_provider/dp_iface.xml \
-+    $(srcdir)/src/providers/proxy/proxy_iface.xml \
-     $(srcdir)/src/responder/ifp/ifp_iface.xml
- 
- SBUS_CODEGEN = src/sbus/sbus_codegen
-@@ -3337,7 +3339,7 @@ libsss_proxy_la_SOURCES = \
-     src/providers/proxy/proxy_netgroup.c \
-     src/providers/proxy/proxy_services.c \
-     src/providers/proxy/proxy_auth.c \
--    src/providers/data_provider_iface_generated.c \
-+    src/providers/proxy/proxy_iface_generated.c \
-     $(NULL)
- libsss_proxy_la_CFLAGS = \
-     $(AM_CFLAGS)
-@@ -3606,7 +3608,7 @@ gpo_child_LDADD = \
- 
- proxy_child_SOURCES = \
-     src/providers/proxy/proxy_child.c \
--    src/providers/data_provider_iface_generated.c \
-+    src/providers/proxy/proxy_iface_generated.c \
-     $(NULL)
- proxy_child_CFLAGS = \
-     $(AM_CFLAGS) \
-diff --git a/src/providers/dp_auth_util.c b/src/providers/dp_auth_util.c
-index 8c09299b12c703ed703a025d1e8cfe5df2088eb2..35d22ab5f24ba2300889256f477a9ed856b69cb9 100644
---- a/src/providers/dp_auth_util.c
-+++ b/src/providers/dp_auth_util.c
-@@ -321,67 +321,3 @@ bool dp_unpack_pam_response(DBusMessage *msg, struct pam_data *pd, DBusError *db
- 
-     return true;
- }
--
--void dp_id_callback(DBusPendingCall *pending, void *ptr)
--{
--    DBusMessage *reply;
--    DBusError dbus_error;
--    dbus_bool_t ret;
--    dbus_uint16_t dp_ver;
--    int type;
--
--    dbus_error_init(&dbus_error);
--
--    reply = dbus_pending_call_steal_reply(pending);
--    if (!reply) {
--        /* reply should never be null. This function shouldn't be called
--         * until reply is valid or timeout has occurred. If reply is NULL
--         * here, something is seriously wrong and we should bail out.
--         */
--        DEBUG(SSSDBG_FATAL_FAILURE,
--              "Severe error. A reply callback was called but no"
--                  " reply was received and no timeout occurred\n");
--
--        /* FIXME: Destroy this connection ? */
--        goto done;
--    }
--
--    type = dbus_message_get_type(reply);
--    switch (type) {
--    case DBUS_MESSAGE_TYPE_METHOD_RETURN:
--        ret = dbus_message_get_args(reply, &dbus_error,
--                                    DBUS_TYPE_UINT16, &dp_ver,
--                                    DBUS_TYPE_INVALID);
--        if (!ret) {
--            DEBUG(SSSDBG_CRIT_FAILURE, "Failed to parse message\n");
--            if (dbus_error_is_set(&dbus_error)) dbus_error_free(&dbus_error);
--            /* FIXME: Destroy this connection ? */
--            goto done;
--        }
--
--        DEBUG(SSSDBG_CONF_SETTINGS,
--              "Got id ack and version (%d) from DP\n", dp_ver);
--
--        break;
--
--    case DBUS_MESSAGE_TYPE_ERROR:
--        DEBUG(SSSDBG_FATAL_FAILURE,"The Monitor returned an error [%s]\n",
--                 dbus_message_get_error_name(reply));
--        /* Falling through to default intentionally*/
--    default:
--        /*
--         * Timeout or other error occurred or something
--         * unexpected happened.
--         * It doesn't matter which, because either way we
--         * know that this connection isn't trustworthy.
--         * We'll destroy it now.
--         */
--
--        /* FIXME: Destroy this connection ? */
--        break;
--    }
--
--done:
--    dbus_pending_call_unref(pending);
--    dbus_message_unref(reply);
--}
-diff --git a/src/providers/proxy/proxy.h b/src/providers/proxy/proxy.h
-index 11c85c54ea64db7ad9feb163bd5a86f65ac0ea90..6f91782bb06ea8bbf3ac35052b840dd21300b96e 100644
---- a/src/providers/proxy/proxy.h
-+++ b/src/providers/proxy/proxy.h
-@@ -42,6 +42,8 @@
- #include "sss_client/nss_compat.h"
- #include <dhash.h>
- 
-+#define PROXY_CHILD_PATH "/org/freedesktop/sssd/proxychild"
-+
- struct proxy_nss_ops {
-     enum nss_status (*getpwnam_r)(const char *name, struct passwd *result,
-                                   char *buffer, size_t buflen, int *errnop);
-diff --git a/src/providers/proxy/proxy_auth.c b/src/providers/proxy/proxy_auth.c
-index 6e7139aaa5d45631fa08f265c54b66ab97555a64..2b3510c38b1cb265e3042425c373f39e524a71eb 100644
---- a/src/providers/proxy/proxy_auth.c
-+++ b/src/providers/proxy/proxy_auth.c
-@@ -23,6 +23,7 @@
- */
- 
- #include "providers/proxy/proxy.h"
-+#include "providers/proxy/proxy_iface_generated.h"
- 
- struct pc_init_ctx;
- 
-@@ -531,9 +532,9 @@ static struct tevent_req *proxy_pam_conv_send(TALLOC_CTX *mem_ctx,
-     state->pid = pid;
- 
-     msg = dbus_message_new_method_call(NULL,
--                                       DP_PATH,
--                                       DATA_PROVIDER_IFACE,
--                                       DATA_PROVIDER_IFACE_PAMHANDLER);
-+                                       PROXY_CHILD_PATH,
-+                                       IFACE_PROXY_AUTH,
-+                                       IFACE_PROXY_AUTH_PAM);
-     if (msg == NULL) {
-         DEBUG(SSSDBG_CRIT_FAILURE, "dbus_message_new_method_call failed.\n");
-         talloc_zfree(req);
-@@ -847,4 +848,3 @@ proxy_pam_handler_recv(TALLOC_CTX *mem_ctx,
- 
-     return EOK;
- }
--
-diff --git a/src/providers/proxy/proxy_child.c b/src/providers/proxy/proxy_child.c
-index efd304d5aafd5e53792ef96b75d8aa0c908bbe13..b492adcb3b5efefc08e6eb9e069035aeff8d34df 100644
---- a/src/providers/proxy/proxy_child.c
-+++ b/src/providers/proxy/proxy_child.c
-@@ -44,22 +44,10 @@
- #include "confdb/confdb.h"
- #include "sbus/sssd_dbus.h"
- #include "providers/proxy/proxy.h"
-+#include "providers/proxy/proxy_iface_generated.h"
- 
- #include "providers/backend.h"
- 
--static int pc_pam_handler(struct sbus_request *dbus_req, void *user_data);
--
--struct data_provider_iface pc_methods = {
--    { &data_provider_iface_meta, 0 },
--    .RegisterService = NULL,
--    .pamHandler = pc_pam_handler,
--    .sudoHandler = NULL,
--    .autofsHandler = NULL,
--    .hostHandler = NULL,
--    .getDomains = NULL,
--    .getAccountInfo = NULL,
--};
--
- struct pc_ctx {
-     struct tevent_context *ev;
-     struct confdb_ctx *cdb;
-@@ -382,17 +370,71 @@ done:
-     exit(ret);
- }
- 
--int proxy_child_send_id(struct sbus_connection *conn,
--                        uint16_t version,
--                        uint32_t id);
-+static void proxy_child_id_callback(DBusPendingCall *pending, void *ptr)
-+{
-+    DBusMessage *reply;
-+    errno_t ret;
-+
-+    reply = dbus_pending_call_steal_reply(pending);
-+    if (reply == NULL) {
-+        /* reply should never be null. This function shouldn't be called
-+         * until reply is valid or timeout has occurred. If reply is NULL
-+         * here, something is seriously wrong and we should bail out.
-+         */
-+        DEBUG(SSSDBG_FATAL_FAILURE, "Severe error. A reply callback was "
-+              "called but no reply was received and no timeout occurred\n");
-+        goto done;
-+    }
-+
-+    ret = sbus_parse_reply(reply);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_CRIT_FAILURE, "Unable to get ID ack [%d]: %s\n",
-+              ret, sss_strerror(ret));
-+    }
-+
-+    DEBUG(SSSDBG_TRACE_FUNC, "Got id ack from proxy child\n");
-+
-+done:
-+    dbus_pending_call_unref(pending);
-+    dbus_message_unref(reply);
-+}
-+
-+static errno_t proxy_child_send_id(struct sbus_connection *conn, uint32_t id)
-+{
-+    DBusMessage *msg;
-+    errno_t ret;
-+
-+    msg = sbus_create_message(NULL, NULL, PROXY_CHILD_PATH, IFACE_PROXY_CLIENT,
-+                              IFACE_PROXY_CLIENT_REGISTER,
-+                              DBUS_TYPE_UINT32, &id);
-+    if (msg == NULL) {
-+        DEBUG(SSSDBG_FATAL_FAILURE, "Out of memory?!\n");
-+        return ENOMEM;
-+    }
-+
-+    DEBUG(SSSDBG_TRACE_FUNC, "Sending ID to Proxy Backend: (%"PRIu32")\n", id);
-+
-+    ret = sbus_conn_send(conn, msg, 30000, proxy_child_id_callback, NULL, NULL);
-+
-+    dbus_message_unref(msg);
-+
-+    return ret;
-+}
-+
- static int proxy_cli_init(struct pc_ctx *ctx)
- {
-     char *sbus_address;
-     int ret;
- 
-+    static struct iface_proxy_auth iface_proxy_auth = {
-+        { &iface_proxy_auth_meta, 0 },
-+
-+        .PAM = pc_pam_handler,
-+    };
-+
-     sbus_address = talloc_asprintf(ctx, "unix:path=%s/%s_%s",
--                                      PIPE_PATH, PROXY_CHILD_PIPE,
--                                      ctx->domain->name);
-+                                   PIPE_PATH, PROXY_CHILD_PIPE,
-+                                   ctx->domain->name);
-     if (sbus_address == NULL) {
-         DEBUG(SSSDBG_CRIT_FAILURE, "talloc_asprintf failed.\n");
-         return ENOMEM;
-@@ -404,13 +446,14 @@ static int proxy_cli_init(struct pc_ctx *ctx)
-         return ret;
-     }
- 
--    ret = sbus_conn_register_iface(ctx->conn, &pc_methods.vtable, DP_PATH, ctx);
-+    ret = sbus_conn_register_iface(ctx->conn, &iface_proxy_auth.vtable,
-+                                   PROXY_CHILD_PATH, ctx);
-     if (ret != EOK) {
-         DEBUG(SSSDBG_FATAL_FAILURE, "Failed to export proxy.\n");
-         return ret;
-     }
- 
--    ret = proxy_child_send_id(ctx->conn, DATA_PROVIDER_VERSION, ctx->id);
-+    ret = proxy_child_send_id(ctx->conn, ctx->id);
-     if (ret != EOK) {
-         DEBUG(SSSDBG_FATAL_FAILURE, "dp_common_send_id failed.\n");
-         return ret;
-@@ -419,42 +462,6 @@ static int proxy_cli_init(struct pc_ctx *ctx)
-     return EOK;
- }
- 
--int proxy_child_send_id(struct sbus_connection *conn,
--                        uint16_t version,
--                        uint32_t id)
--{
--    DBusMessage *msg;
--    dbus_bool_t ret;
--    int retval;
--
--    /* create the message */
--    msg = dbus_message_new_method_call(NULL,
--                                       DP_PATH,
--                                       DATA_PROVIDER_IFACE,
--                                       DATA_PROVIDER_IFACE_REGISTERSERVICE);
--    if (msg == NULL) {
--        DEBUG(SSSDBG_FATAL_FAILURE, "Out of memory?!\n");
--        return ENOMEM;
--    }
--
--    DEBUG(SSSDBG_FUNC_DATA, "Sending ID to Proxy Backend: (%d,%"PRIu32")\n",
--                             version, id);
--
--    ret = dbus_message_append_args(msg,
--                                   DBUS_TYPE_UINT16, &version,
--                                   DBUS_TYPE_UINT32, &id,
--                                   DBUS_TYPE_INVALID);
--    if (!ret) {
--        DEBUG(SSSDBG_CRIT_FAILURE, "Failed to build message\n");
--        return EIO;
--    }
--
--    retval = sbus_conn_send(conn, msg, 30000, dp_id_callback, NULL, NULL);
--
--    dbus_message_unref(msg);
--    return retval;
--}
--
- int proxy_child_process_init(TALLOC_CTX *mem_ctx, const char *domain,
-                              struct tevent_context *ev, struct confdb_ctx *cdb,
-                              const char *pam_target, uint32_t id)
-diff --git a/src/providers/proxy/proxy_client.c b/src/providers/proxy/proxy_client.c
-index fc1735f2a101528a1edeaf3cf9c1118e4a21e937..74957caeec5bf50b5cb959d6f5b8ec1ca9ecba37 100644
---- a/src/providers/proxy/proxy_client.c
-+++ b/src/providers/proxy/proxy_client.c
-@@ -22,24 +22,10 @@
-     along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
- 
--#include "config.h"
--
--#include "util/sss_format.h"
-+#include "util/util.h"
-+#include "providers/proxy/proxy_iface_generated.h"
- #include "providers/proxy/proxy.h"
- 
--static int client_registration(struct sbus_request *dbus_req, void *data);
--
--static struct data_provider_iface proxy_methods = {
--    { &data_provider_iface_meta, 0 },
--    .RegisterService = client_registration,
--    .pamHandler = NULL,
--    .sudoHandler = NULL,
--    .autofsHandler = NULL,
--    .hostHandler = NULL,
--    .getDomains = NULL,
--    .getAccountInfo = NULL,
--};
--
- struct proxy_client {
-     struct proxy_auth_ctx *proxy_auth_ctx;
-     struct sbus_connection *conn;
-@@ -47,24 +33,22 @@ struct proxy_client {
-     bool initialized;
- };
- 
--static int client_registration(struct sbus_request *dbus_req, void *data)
-+static int proxy_client_register(struct sbus_request *sbus_req,
-+                                 void *data,
-+                                 uint32_t cli_id)
- {
--    dbus_uint16_t version = DATA_PROVIDER_VERSION;
-     struct sbus_connection *conn;
-     struct proxy_client *proxy_cli;
--    dbus_uint16_t cli_ver;
--    uint32_t cli_id;
-     int hret;
-     hash_key_t key;
-     hash_value_t value;
-     struct tevent_req *req;
-     struct proxy_child_ctx *child_ctx;
-     struct pc_init_ctx *init_ctx;
--    int ret;
- 
--    conn = dbus_req->conn;
-+    conn = sbus_req->conn;
-     proxy_cli = talloc_get_type(data, struct proxy_client);
--    if (!proxy_cli) {
-+    if (proxy_cli == NULL) {
-         DEBUG(SSSDBG_FATAL_FAILURE, "Connection holds no valid init data\n");
-         return EINVAL;
-     }
-@@ -74,14 +58,6 @@ static int client_registration(struct sbus_request *dbus_req, void *data)
-           "Cancel proxy client ID timeout [%p]\n", proxy_cli->timeout);
-     talloc_zfree(proxy_cli->timeout);
- 
--    if (!sbus_request_parse_or_finish(dbus_req,
--                                      DBUS_TYPE_UINT16, &cli_ver,
--                                      DBUS_TYPE_UINT32, &cli_id,
--                                      DBUS_TYPE_INVALID)) {
--        sbus_disconnect(conn);
--        return EOK; /* handled */
--    }
--
-     DEBUG(SSSDBG_FUNC_DATA, "Proxy client [%"PRIu32"] connected\n", cli_id);
- 
-     /* Check the hash table */
-@@ -94,20 +70,14 @@ static int client_registration(struct sbus_request *dbus_req, void *data)
-         return EIO;
-     }
- 
--    /* reply that all is ok */
--    ret = sbus_request_return_and_finish(dbus_req,
--                                         DBUS_TYPE_UINT16, &version,
--                                         DBUS_TYPE_INVALID);
--    if (ret != EOK) {
--        sbus_disconnect(conn);
--        return ret;
--    }
-+    iface_proxy_client_Register_finish(sbus_req);
- 
-     hret = hash_lookup(proxy_cli->proxy_auth_ctx->request_table, &key, &value);
-     if (hret != HASH_SUCCESS) {
-         DEBUG(SSSDBG_CRIT_FAILURE,
--              "Hash error [%d][%s]\n", hret, hash_error_string(hret));
-+              "Hash error [%d]: %s\n", hret, hash_error_string(hret));
-         sbus_disconnect(conn);
-+        return EIO;
-     }
- 
-     /* Signal that the child is up and ready to receive the request */
-@@ -121,7 +91,7 @@ static int client_registration(struct sbus_request *dbus_req, void *data)
-          * break.
-          */
-         DEBUG(SSSDBG_CRIT_FAILURE, "Client connection from a request "
--                  "that's not marked as running\n");
-+              "that's not marked as running\n");
-         return EIO;
-     }
- 
-@@ -133,9 +103,10 @@ static int client_registration(struct sbus_request *dbus_req, void *data)
-     return EOK;
- }
- 
--static void init_timeout(struct tevent_context *ev,
--                         struct tevent_timer *te,
--                         struct timeval t, void *ptr)
-+static void proxy_client_timeout(struct tevent_context *ev,
-+                                 struct tevent_timer *te,
-+                                 struct timeval t,
-+                                 void *ptr)
- {
-     struct proxy_client *proxy_cli;
- 
-@@ -155,38 +126,53 @@ static void init_timeout(struct tevent_context *ev,
- 
- int proxy_client_init(struct sbus_connection *conn, void *data)
- {
--    struct proxy_auth_ctx *proxy_auth_ctx;
-+    struct proxy_auth_ctx *auth_ctx;
-     struct proxy_client *proxy_cli;
-     struct timeval tv;
-+    errno_t ret;
- 
--    proxy_auth_ctx = talloc_get_type(data, struct proxy_auth_ctx);
-+    static struct iface_proxy_client iface_proxy_client = {
-+        { &iface_proxy_client_meta, 0 },
- 
--    /* hang off this memory to the connection so that when the connection
--     * is freed we can potentially call a destructor */
-+        .Register = proxy_client_register,
-+    };
- 
-+    auth_ctx = talloc_get_type(data, struct proxy_auth_ctx);
-+
-+    /* When connection is lost we also free the client. */
-     proxy_cli = talloc_zero(conn, struct proxy_client);
--    if (!proxy_cli) {
--        DEBUG(SSSDBG_FATAL_FAILURE,"Out of memory?!\n");
--        talloc_zfree(conn);
-+    if (proxy_cli == NULL) {
-+        DEBUG(SSSDBG_FATAL_FAILURE, "Out of memory, killing connection.\n");
-+        talloc_free(conn);
-         return ENOMEM;
-     }
--    proxy_cli->proxy_auth_ctx = proxy_auth_ctx;
-+
-+    proxy_cli->proxy_auth_ctx = auth_ctx;
-     proxy_cli->conn = conn;
-     proxy_cli->initialized = false;
- 
--    /* 5 seconds should be plenty */
-+    /* Setup timeout in case client fails to register himself in time. */
-     tv = tevent_timeval_current_ofs(5, 0);
--
--    proxy_cli->timeout = tevent_add_timer(proxy_auth_ctx->be->ev, proxy_cli,
--                                          tv, init_timeout, proxy_cli);
--    if (!proxy_cli->timeout) {
--        DEBUG(SSSDBG_FATAL_FAILURE,"Out of memory?!\n");
--        talloc_zfree(conn);
-+    proxy_cli->timeout = tevent_add_timer(auth_ctx->be->ev, proxy_cli, tv,
-+                                          proxy_client_timeout, proxy_cli);
-+    if (proxy_cli->timeout == NULL) {
-+        /* Connection is closed in the caller. */
-+        DEBUG(SSSDBG_FATAL_FAILURE, "Out of memory, killing connection\n");
-         return ENOMEM;
-     }
-+
-     DEBUG(SSSDBG_CONF_SETTINGS,
-           "Set-up proxy client ID timeout [%p]\n", proxy_cli->timeout);
- 
--    return sbus_conn_register_iface(conn, &proxy_methods.vtable,
--                                    DP_PATH, proxy_cli);
-+    /* Setup D-Bus interfaces and methods. */
-+    ret = sbus_conn_register_iface(conn, &iface_proxy_client.vtable,
-+                                   PROXY_CHILD_PATH, proxy_cli);
-+    if (ret != EOK) {
-+        /* Connection is closed in the caller. */
-+        DEBUG(SSSDBG_FATAL_FAILURE, "Unable to register D-Bus interface, "
-+              "killing connection [%d]: %s\n", ret, sss_strerror(ret));
-+        return ret;
-+    }
-+
-+    return ret;
- }
-diff --git a/src/providers/proxy/proxy_iface.xml b/src/providers/proxy/proxy_iface.xml
-new file mode 100644
-index 0000000000000000000000000000000000000000..39b0b03928661a1851fd739598b0194547441c2c
---- /dev/null
-+++ b/src/providers/proxy/proxy_iface.xml
-@@ -0,0 +1,17 @@
-+<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
-+ "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
-+<node>
-+    <interface name="org.freedesktop.sssd.ProxyChild.Client">
-+        <annotation value="iface_proxy_client" name="org.freedesktop.DBus.GLib.CSymbol"/>
-+        <method name="Register">
-+            <arg name="ID" type="u" direction="in" />
-+        </method>
-+    </interface>
-+
-+    <interface name="org.freedesktop.sssd.ProxyChild.Auth">
-+        <annotation value="iface_proxy_auth" name="org.freedesktop.DBus.GLib.CSymbol"/>
-+        <method name="PAM">
-+            <annotation name="org.freedesktop.sssd.RawHandler" value="true"/>
-+        </method>
-+    </interface>
-+</node>
-diff --git a/src/providers/proxy/proxy_iface_generated.c b/src/providers/proxy/proxy_iface_generated.c
-new file mode 100644
-index 0000000000000000000000000000000000000000..425727d1496b537eb25b002815d14e1f57b8f00d
---- /dev/null
-+++ b/src/providers/proxy/proxy_iface_generated.c
-@@ -0,0 +1,80 @@
-+/* The following definitions are auto-generated from proxy_iface.xml */
-+
-+#include "util/util.h"
-+#include "sbus/sssd_dbus.h"
-+#include "sbus/sssd_dbus_meta.h"
-+#include "sbus/sssd_dbus_invokers.h"
-+#include "proxy_iface_generated.h"
-+
-+/* invokes a handler with a 'u' DBus signature */
-+static int invoke_u_method(struct sbus_request *dbus_req, void *function_ptr);
-+
-+/* arguments for org.freedesktop.sssd.ProxyChild.Client.Register */
-+const struct sbus_arg_meta iface_proxy_client_Register__in[] = {
-+    { "ID", "u" },
-+    { NULL, }
-+};
-+
-+int iface_proxy_client_Register_finish(struct sbus_request *req)
-+{
-+   return sbus_request_return_and_finish(req,
-+                                         DBUS_TYPE_INVALID);
-+}
-+
-+/* methods for org.freedesktop.sssd.ProxyChild.Client */
-+const struct sbus_method_meta iface_proxy_client__methods[] = {
-+    {
-+        "Register", /* name */
-+        iface_proxy_client_Register__in,
-+        NULL, /* no out_args */
-+        offsetof(struct iface_proxy_client, Register),
-+        invoke_u_method,
-+    },
-+    { NULL, }
-+};
-+
-+/* interface info for org.freedesktop.sssd.ProxyChild.Client */
-+const struct sbus_interface_meta iface_proxy_client_meta = {
-+    "org.freedesktop.sssd.ProxyChild.Client", /* name */
-+    iface_proxy_client__methods,
-+    NULL, /* no signals */
-+    NULL, /* no properties */
-+    sbus_invoke_get_all, /* GetAll invoker */
-+};
-+
-+/* methods for org.freedesktop.sssd.ProxyChild.Auth */
-+const struct sbus_method_meta iface_proxy_auth__methods[] = {
-+    {
-+        "PAM", /* name */
-+        NULL, /* no in_args */
-+        NULL, /* no out_args */
-+        offsetof(struct iface_proxy_auth, PAM),
-+        NULL, /* no invoker */
-+    },
-+    { NULL, }
-+};
-+
-+/* interface info for org.freedesktop.sssd.ProxyChild.Auth */
-+const struct sbus_interface_meta iface_proxy_auth_meta = {
-+    "org.freedesktop.sssd.ProxyChild.Auth", /* name */
-+    iface_proxy_auth__methods,
-+    NULL, /* no signals */
-+    NULL, /* no properties */
-+    sbus_invoke_get_all, /* GetAll invoker */
-+};
-+
-+/* invokes a handler with a 'u' DBus signature */
-+static int invoke_u_method(struct sbus_request *dbus_req, void *function_ptr)
-+{
-+    uint32_t arg_0;
-+    int (*handler)(struct sbus_request *, void *, uint32_t) = function_ptr;
-+
-+    if (!sbus_request_parse_or_finish(dbus_req,
-+                               DBUS_TYPE_UINT32, &arg_0,
-+                               DBUS_TYPE_INVALID)) {
-+         return EOK; /* request handled */
-+    }
-+
-+    return (handler)(dbus_req, dbus_req->intf->handler_data,
-+                     arg_0);
-+}
-diff --git a/src/providers/proxy/proxy_iface_generated.h b/src/providers/proxy/proxy_iface_generated.h
-new file mode 100644
-index 0000000000000000000000000000000000000000..7af074fa3d839263318ceac7ea34f62dcde64563
---- /dev/null
-+++ b/src/providers/proxy/proxy_iface_generated.h
-@@ -0,0 +1,71 @@
-+/* The following declarations are auto-generated from proxy_iface.xml */
-+
-+#ifndef __PROXY_IFACE_XML__
-+#define __PROXY_IFACE_XML__
-+
-+#include "sbus/sssd_dbus.h"
-+
-+/* ------------------------------------------------------------------------
-+ * DBus Constants
-+ *
-+ * Various constants of interface and method names mostly for use by clients
-+ */
-+
-+/* constants for org.freedesktop.sssd.ProxyChild.Client */
-+#define IFACE_PROXY_CLIENT "org.freedesktop.sssd.ProxyChild.Client"
-+#define IFACE_PROXY_CLIENT_REGISTER "Register"
-+
-+/* constants for org.freedesktop.sssd.ProxyChild.Auth */
-+#define IFACE_PROXY_AUTH "org.freedesktop.sssd.ProxyChild.Auth"
-+#define IFACE_PROXY_AUTH_PAM "PAM"
-+
-+/* ------------------------------------------------------------------------
-+ * DBus handlers
-+ *
-+ * These structures are filled in by implementors of the different
-+ * dbus interfaces to handle method calls.
-+ *
-+ * Handler functions of type sbus_msg_handler_fn accept raw messages,
-+ * other handlers are typed appropriately. If a handler that is
-+ * set to NULL is invoked it will result in a
-+ * org.freedesktop.DBus.Error.NotSupported error for the caller.
-+ *
-+ * Handlers have a matching xxx_finish() function (unless the method has
-+ * accepts raw messages). These finish functions the
-+ * sbus_request_return_and_finish() with the appropriate arguments to
-+ * construct a valid reply. Once a finish function has been called, the
-+ * @dbus_req it was called with is freed and no longer valid.
-+ */
-+
-+/* vtable for org.freedesktop.sssd.ProxyChild.Client */
-+struct iface_proxy_client {
-+    struct sbus_vtable vtable; /* derive from sbus_vtable */
-+    int (*Register)(struct sbus_request *req, void *data, uint32_t arg_ID);
-+};
-+
-+/* finish function for Register */
-+int iface_proxy_client_Register_finish(struct sbus_request *req);
-+
-+/* vtable for org.freedesktop.sssd.ProxyChild.Auth */
-+struct iface_proxy_auth {
-+    struct sbus_vtable vtable; /* derive from sbus_vtable */
-+    sbus_msg_handler_fn PAM;
-+};
-+
-+/* ------------------------------------------------------------------------
-+ * DBus Interface Metadata
-+ *
-+ * These structure definitions are filled in with the information about
-+ * the interfaces, methods, properties and so on.
-+ *
-+ * The actual definitions are found in the accompanying C file next
-+ * to this header.
-+ */
-+
-+/* interface info for org.freedesktop.sssd.ProxyChild.Client */
-+extern const struct sbus_interface_meta iface_proxy_client_meta;
-+
-+/* interface info for org.freedesktop.sssd.ProxyChild.Auth */
-+extern const struct sbus_interface_meta iface_proxy_auth_meta;
-+
-+#endif /* __PROXY_IFACE_XML__ */
--- 
-2.4.11
-
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
new file mode 100644
index 0000000..2862d4d
--- /dev/null
+++ b/SOURCES/0099-config-check-Message-when-sssd.conf-is-missing.patch
@@ -0,0 +1,39 @@
+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/0100-DP-Remove-old-data-provider-interface.patch b/SOURCES/0100-DP-Remove-old-data-provider-interface.patch
deleted file mode 100644
index db73927..0000000
--- a/SOURCES/0100-DP-Remove-old-data-provider-interface.patch
+++ /dev/null
@@ -1,1034 +0,0 @@
-From c2fe5c54faa92c670161d65fe5a1ff62acd4ac91 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Pavel=20B=C5=99ezina?= <pbrezina@redhat.com>
-Date: Tue, 19 Jul 2016 14:24:16 +0200
-Subject: [PATCH 100/102] DP: Remove old data provider interface
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Reverse data provider interface is moved to a better location in
-NSS responder. All responders now can have an sbus interface
-defined per data provider connection. The unused old data provider
-interface is removed.
-
-Reviewed-by: Lukáš Slebodník <lslebodn@redhat.com>
-(cherry picked from commit 04e870d99e72aa3160bdb6ab05d986fb4005c3ed)
----
- Makefile.am                                   | 11 +--
- src/providers/data_provider.h                 |  1 -
- src/providers/data_provider/dp_target_id.c    |  7 +-
- src/providers/data_provider_iface.xml         | 53 ---------------
- src/providers/data_provider_iface_generated.c | 98 ---------------------------
- src/providers/data_provider_iface_generated.h | 82 ----------------------
- src/responder/autofs/autofssrv.c              | 13 +---
- src/responder/common/responder.h              |  8 +--
- src/responder/common/responder_common.c       | 16 +++--
- src/responder/common/responder_get_domains.c  |  4 +-
- src/responder/ifp/ifpsrv.c                    | 13 +---
- src/responder/nss/nss_iface.c                 | 38 +++++++++++
- src/responder/nss/nss_iface.h                 | 30 ++++++++
- src/responder/nss/nss_iface.xml               | 12 ++++
- src/responder/nss/nss_iface_generated.c       | 69 +++++++++++++++++++
- src/responder/nss/nss_iface_generated.h       | 58 ++++++++++++++++
- src/responder/nss/nsssrv.c                    | 39 ++++-------
- src/responder/nss/nsssrv.h                    |  7 ++
- src/responder/pac/pacsrv.c                    | 13 +---
- src/responder/pam/pamsrv.c                    | 13 +---
- src/responder/pam/pamsrv_dp.c                 |  4 +-
- src/responder/ssh/sshsrv.c                    | 13 +---
- src/responder/sudo/sudosrv.c                  | 13 +---
- src/tests/cwrap/Makefile.am                   |  1 -
- 24 files changed, 259 insertions(+), 357 deletions(-)
- delete mode 100644 src/providers/data_provider_iface.xml
- delete mode 100644 src/providers/data_provider_iface_generated.c
- delete mode 100644 src/providers/data_provider_iface_generated.h
- create mode 100644 src/responder/nss/nss_iface.c
- create mode 100644 src/responder/nss/nss_iface.h
- create mode 100644 src/responder/nss/nss_iface.xml
- create mode 100644 src/responder/nss/nss_iface_generated.c
- create mode 100644 src/responder/nss/nss_iface_generated.h
-
-diff --git a/Makefile.am b/Makefile.am
-index 5d54838659e44fa446fc921d014e48ac91469b25..e2e4c4c08f66ef15684e1b3b1fe17bfae4e4131b 100644
---- a/Makefile.am
-+++ b/Makefile.am
-@@ -492,7 +492,6 @@ SSSD_RESPONDER_OBJ = \
-     src/responder/common/data_provider/rdp_message.c \
-     src/responder/common/data_provider/rdp_client.c \
-     src/monitor/monitor_iface_generated.c \
--    src/providers/data_provider_iface_generated.c \
-     src/providers/data_provider_req.c
- 
- SSSD_TOOLS_OBJ = \
-@@ -610,6 +609,8 @@ dist_noinst_HEADERS = \
-     src/responder/nss/nsssrv_netgroup.h \
-     src/responder/nss/nsssrv_services.h \
-     src/responder/nss/nsssrv_mmap_cache.h \
-+    src/responder/nss/nss_iface_generated.h \
-+    src/responder/nss/nss_iface.h \
-     src/responder/pac/pacsrv.h \
-     src/responder/common/negcache_files.h \
-     src/responder/common/negcache.h \
-@@ -647,7 +648,6 @@ dist_noinst_HEADERS = \
-     src/confdb/confdb_setup.h \
-     src/providers/data_provider.h \
-     src/providers/data_provider_req.h \
--    src/providers/data_provider_iface_generated.h \
-     src/providers/data_provider/dp.h \
-     src/providers/data_provider/dp_flags.h \
-     src/providers/data_provider/dp_responder_iface.h \
-@@ -1196,10 +1196,10 @@ endif
- CODEGEN_XML = \
-     $(srcdir)/src/tests/sbus_codegen_tests.xml \
-     $(srcdir)/src/monitor/monitor_iface.xml \
--    $(srcdir)/src/providers/data_provider_iface.xml \
-     $(srcdir)/src/providers/data_provider/dp_iface.xml \
-     $(srcdir)/src/providers/proxy/proxy_iface.xml \
--    $(srcdir)/src/responder/ifp/ifp_iface.xml
-+    $(srcdir)/src/responder/ifp/ifp_iface.xml \
-+    $(srcdir)/src/responder/nss/nss_iface.xml
- 
- SBUS_CODEGEN = src/sbus/sbus_codegen
- 
-@@ -1248,6 +1248,8 @@ sssd_nss_SOURCES = \
-     src/responder/nss/nsssrv_netgroup.c \
-     src/responder/nss/nsssrv_services.c \
-     src/responder/nss/nsssrv_mmap_cache.c \
-+    src/responder/nss/nss_iface_generated.c \
-+    src/responder/nss/nss_iface.c \
-     $(SSSD_RESPONDER_OBJ)
- sssd_nss_LDADD = \
-     $(TDB_LIBS) \
-@@ -1411,7 +1413,6 @@ sssd_be_SOURCES = \
-     src/providers/be_ptask.c \
-     src/providers/be_refresh.c \
-     src/monitor/monitor_iface_generated.c \
--    src/providers/data_provider_iface_generated.c \
-     src/providers/data_provider/dp.c \
-     src/providers/data_provider/dp_modules.c \
-     src/providers/data_provider/dp_targets.c \
-diff --git a/src/providers/data_provider.h b/src/providers/data_provider.h
-index b0b6876d984d7c6574baaa8d130e374ba2e6f0c4..14a0902c265850d91fa7d29cc2708e70b060ec18 100644
---- a/src/providers/data_provider.h
-+++ b/src/providers/data_provider.h
-@@ -44,7 +44,6 @@
- #include "sss_client/sss_cli.h"
- #include "util/authtok.h"
- #include "providers/data_provider_req.h"
--#include "providers/data_provider_iface_generated.h"
- 
- #define DATA_PROVIDER_VERSION 0x0001
- #define DATA_PROVIDER_PIPE "private/sbus-dp"
-diff --git a/src/providers/data_provider/dp_target_id.c b/src/providers/data_provider/dp_target_id.c
-index 1b06cbe5b96f56c33dd048cf6211b7c97819db8c..938651545ea995091d0aaf29da12bbb8110c9add 100644
---- a/src/providers/data_provider/dp_target_id.c
-+++ b/src/providers/data_provider/dp_target_id.c
-@@ -25,6 +25,7 @@
- #include "providers/data_provider/dp_private.h"
- #include "providers/data_provider/dp_iface.h"
- #include "providers/backend.h"
-+#include "responder/nss/nss_iface.h"
- #include "util/util.h"
- 
- #define FILTER_TYPE(str, type) {str "=", sizeof(str "=") - 1, type}
-@@ -168,9 +169,9 @@ static void dp_req_initgr_pp(const char *req_name,
-     }
- 
-     msg = dbus_message_new_method_call(NULL,
--                                       DP_PATH,
--                                       DATA_PROVIDER_REV_IFACE,
--                                       DATA_PROVIDER_REV_IFACE_INITGRCHECK);
-+                                       NSS_MEMORYCACHE_PATH,
-+                                       IFACE_NSS_MEMORYCACHE,
-+                                       IFACE_NSS_MEMORYCACHE_UPDATEINITGROUPS);
-     if (msg == NULL) {
-         DEBUG(SSSDBG_CRIT_FAILURE, "Out of memory?!\n");
-         return;
-diff --git a/src/providers/data_provider_iface.xml b/src/providers/data_provider_iface.xml
-deleted file mode 100644
-index 143975633081ce2ae5690c4036e7169e41d776fc..0000000000000000000000000000000000000000
---- a/src/providers/data_provider_iface.xml
-+++ /dev/null
-@@ -1,53 +0,0 @@
--<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
-- "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
--<node>
--    <interface name="org.freedesktop.sssd.dataprovider">
--        <annotation value="data_provider_iface" name="org.freedesktop.DBus.GLib.CSymbol"/>
--        <method name="RegisterService">
--            <!-- arguments parsed manually, raw handler -->
--            <annotation name="org.freedesktop.sssd.RawHandler" value="true"/>
--        </method>
--        <method name="pamHandler">
--            <!-- arguments parsed manually, raw handler -->
--            <annotation name="org.freedesktop.sssd.RawHandler" value="true"/>
--        </method>
--        <method name="sudoHandler">
--            <!-- arguments parsed manually, raw handler -->
--            <annotation name="org.freedesktop.sssd.RawHandler" value="true"/>
--        </method>
--        <method name="autofsHandler">
--            <!-- arguments parsed manually, raw handler -->
--            <annotation name="org.freedesktop.sssd.RawHandler" value="true"/>
--        </method>
--        <method name="hostHandler">
--            <!-- arguments parsed manually, raw handler -->
--            <annotation name="org.freedesktop.sssd.RawHandler" value="true"/>
--        </method>
--        <method name="getDomains">
--            <!-- arguments parsed manually, raw handler -->
--            <annotation name="org.freedesktop.sssd.RawHandler" value="true"/>
--        </method>
--        <method name="getAccountInfo">
--            <!-- arguments parsed manually, raw handler -->
--            <annotation name="org.freedesktop.sssd.RawHandler" value="true"/>
--        </method>
--    </interface>
--
--    <!--
--      this is a reverse method sent from providers to
--      the nss responder to tell it to update the mmap
--      cache
--    -->
--
--    <interface name="org.freedesktop.sssd.dataprovider_rev">
--        <annotation value="data_provider_rev_iface" name="org.freedesktop.DBus.GLib.CSymbol"/>
--        <method name="updateCache">
--            <!-- arguments parsed manually, raw handler -->
--            <annotation name="org.freedesktop.sssd.RawHandler" value="true"/>
--        </method>
--        <method name="initgrCheck">
--            <!-- arguments parsed manually, raw handler -->
--            <annotation name="org.freedesktop.sssd.RawHandler" value="true"/>
--        </method>
--    </interface>
--</node>
-diff --git a/src/providers/data_provider_iface_generated.c b/src/providers/data_provider_iface_generated.c
-deleted file mode 100644
-index bdd6a4d76d18bbb44530d816fce14009736b6f6d..0000000000000000000000000000000000000000
---- a/src/providers/data_provider_iface_generated.c
-+++ /dev/null
-@@ -1,98 +0,0 @@
--/* The following definitions are auto-generated from data_provider_iface.xml */
--
--#include "util/util.h"
--#include "sbus/sssd_dbus.h"
--#include "sbus/sssd_dbus_meta.h"
--#include "sbus/sssd_dbus_invokers.h"
--#include "data_provider_iface_generated.h"
--
--/* methods for org.freedesktop.sssd.dataprovider */
--const struct sbus_method_meta data_provider_iface__methods[] = {
--    {
--        "RegisterService", /* name */
--        NULL, /* no in_args */
--        NULL, /* no out_args */
--        offsetof(struct data_provider_iface, RegisterService),
--        NULL, /* no invoker */
--    },
--    {
--        "pamHandler", /* name */
--        NULL, /* no in_args */
--        NULL, /* no out_args */
--        offsetof(struct data_provider_iface, pamHandler),
--        NULL, /* no invoker */
--    },
--    {
--        "sudoHandler", /* name */
--        NULL, /* no in_args */
--        NULL, /* no out_args */
--        offsetof(struct data_provider_iface, sudoHandler),
--        NULL, /* no invoker */
--    },
--    {
--        "autofsHandler", /* name */
--        NULL, /* no in_args */
--        NULL, /* no out_args */
--        offsetof(struct data_provider_iface, autofsHandler),
--        NULL, /* no invoker */
--    },
--    {
--        "hostHandler", /* name */
--        NULL, /* no in_args */
--        NULL, /* no out_args */
--        offsetof(struct data_provider_iface, hostHandler),
--        NULL, /* no invoker */
--    },
--    {
--        "getDomains", /* name */
--        NULL, /* no in_args */
--        NULL, /* no out_args */
--        offsetof(struct data_provider_iface, getDomains),
--        NULL, /* no invoker */
--    },
--    {
--        "getAccountInfo", /* name */
--        NULL, /* no in_args */
--        NULL, /* no out_args */
--        offsetof(struct data_provider_iface, getAccountInfo),
--        NULL, /* no invoker */
--    },
--    { NULL, }
--};
--
--/* interface info for org.freedesktop.sssd.dataprovider */
--const struct sbus_interface_meta data_provider_iface_meta = {
--    "org.freedesktop.sssd.dataprovider", /* name */
--    data_provider_iface__methods,
--    NULL, /* no signals */
--    NULL, /* no properties */
--    sbus_invoke_get_all, /* GetAll invoker */
--};
--
--/* methods for org.freedesktop.sssd.dataprovider_rev */
--const struct sbus_method_meta data_provider_rev_iface__methods[] = {
--    {
--        "updateCache", /* name */
--        NULL, /* no in_args */
--        NULL, /* no out_args */
--        offsetof(struct data_provider_rev_iface, updateCache),
--        NULL, /* no invoker */
--    },
--    {
--        "initgrCheck", /* name */
--        NULL, /* no in_args */
--        NULL, /* no out_args */
--        offsetof(struct data_provider_rev_iface, initgrCheck),
--        NULL, /* no invoker */
--    },
--    { NULL, }
--};
--
--/* interface info for org.freedesktop.sssd.dataprovider_rev */
--const struct sbus_interface_meta data_provider_rev_iface_meta = {
--    "org.freedesktop.sssd.dataprovider_rev", /* name */
--    data_provider_rev_iface__methods,
--    NULL, /* no signals */
--    NULL, /* no properties */
--    sbus_invoke_get_all, /* GetAll invoker */
--};
-diff --git a/src/providers/data_provider_iface_generated.h b/src/providers/data_provider_iface_generated.h
-deleted file mode 100644
-index 976e42b89c6aaf9523b16999b8f5103a1e6f8e66..0000000000000000000000000000000000000000
---- a/src/providers/data_provider_iface_generated.h
-+++ /dev/null
-@@ -1,82 +0,0 @@
--/* The following declarations are auto-generated from data_provider_iface.xml */
--
--#ifndef __DATA_PROVIDER_IFACE_XML__
--#define __DATA_PROVIDER_IFACE_XML__
--
--#include "sbus/sssd_dbus.h"
--
--/* ------------------------------------------------------------------------
-- * DBus Constants
-- *
-- * Various constants of interface and method names mostly for use by clients
-- */
--
--/* constants for org.freedesktop.sssd.dataprovider */
--#define DATA_PROVIDER_IFACE "org.freedesktop.sssd.dataprovider"
--#define DATA_PROVIDER_IFACE_REGISTERSERVICE "RegisterService"
--#define DATA_PROVIDER_IFACE_PAMHANDLER "pamHandler"
--#define DATA_PROVIDER_IFACE_SUDOHANDLER "sudoHandler"
--#define DATA_PROVIDER_IFACE_AUTOFSHANDLER "autofsHandler"
--#define DATA_PROVIDER_IFACE_HOSTHANDLER "hostHandler"
--#define DATA_PROVIDER_IFACE_GETDOMAINS "getDomains"
--#define DATA_PROVIDER_IFACE_GETACCOUNTINFO "getAccountInfo"
--
--/* constants for org.freedesktop.sssd.dataprovider_rev */
--#define DATA_PROVIDER_REV_IFACE "org.freedesktop.sssd.dataprovider_rev"
--#define DATA_PROVIDER_REV_IFACE_UPDATECACHE "updateCache"
--#define DATA_PROVIDER_REV_IFACE_INITGRCHECK "initgrCheck"
--
--/* ------------------------------------------------------------------------
-- * DBus handlers
-- *
-- * These structures are filled in by implementors of the different
-- * dbus interfaces to handle method calls.
-- *
-- * Handler functions of type sbus_msg_handler_fn accept raw messages,
-- * other handlers are typed appropriately. If a handler that is
-- * set to NULL is invoked it will result in a
-- * org.freedesktop.DBus.Error.NotSupported error for the caller.
-- *
-- * Handlers have a matching xxx_finish() function (unless the method has
-- * accepts raw messages). These finish functions the
-- * sbus_request_return_and_finish() with the appropriate arguments to
-- * construct a valid reply. Once a finish function has been called, the
-- * @dbus_req it was called with is freed and no longer valid.
-- */
--
--/* vtable for org.freedesktop.sssd.dataprovider */
--struct data_provider_iface {
--    struct sbus_vtable vtable; /* derive from sbus_vtable */
--    sbus_msg_handler_fn RegisterService;
--    sbus_msg_handler_fn pamHandler;
--    sbus_msg_handler_fn sudoHandler;
--    sbus_msg_handler_fn autofsHandler;
--    sbus_msg_handler_fn hostHandler;
--    sbus_msg_handler_fn getDomains;
--    sbus_msg_handler_fn getAccountInfo;
--};
--
--/* vtable for org.freedesktop.sssd.dataprovider_rev */
--struct data_provider_rev_iface {
--    struct sbus_vtable vtable; /* derive from sbus_vtable */
--    sbus_msg_handler_fn updateCache;
--    sbus_msg_handler_fn initgrCheck;
--};
--
--/* ------------------------------------------------------------------------
-- * DBus Interface Metadata
-- *
-- * These structure definitions are filled in with the information about
-- * the interfaces, methods, properties and so on.
-- *
-- * The actual definitions are found in the accompanying C file next
-- * to this header.
-- */
--
--/* interface info for org.freedesktop.sssd.dataprovider */
--extern const struct sbus_interface_meta data_provider_iface_meta;
--
--/* interface info for org.freedesktop.sssd.dataprovider_rev */
--extern const struct sbus_interface_meta data_provider_rev_iface_meta;
--
--#endif /* __DATA_PROVIDER_IFACE_XML__ */
-diff --git a/src/responder/autofs/autofssrv.c b/src/responder/autofs/autofssrv.c
-index c72f3c1f7aee81a9986076975086cdd88e968edb..826a36e9bc0e2afedfda17104d15b86c5fc1b7e1 100644
---- a/src/responder/autofs/autofssrv.c
-+++ b/src/responder/autofs/autofssrv.c
-@@ -44,17 +44,6 @@ struct mon_cli_iface monitor_autofs_methods = {
-     .sysbusReconnect = NULL,
- };
- 
--static struct data_provider_iface autofs_dp_methods = {
--    { &data_provider_iface_meta, 0 },
--    .RegisterService = NULL,
--    .pamHandler = NULL,
--    .sudoHandler = NULL,
--    .autofsHandler = NULL,
--    .hostHandler = NULL,
--    .getDomains = NULL,
--    .getAccountInfo = NULL,
--};
--
- static errno_t
- autofs_get_config(struct autofs_ctx *actx,
-                   struct confdb_ctx *cdb)
-@@ -130,7 +119,7 @@ autofs_process_init(TALLOC_CTX *mem_ctx,
-                            SSS_AUTOFS_SBUS_SERVICE_VERSION,
-                            &monitor_autofs_methods,
-                            "autofs",
--                           &autofs_dp_methods.vtable,
-+                           NULL,
-                            autofs_connection_setup,
-                            &rctx);
-     if (ret != EOK) {
-diff --git a/src/responder/common/responder.h b/src/responder/common/responder.h
-index 335b313ce1a6bc7c0e0ba332786e2e9f39a04ff1..9e3b2fdbda4e30b859df597374fc7d490b1720e5 100644
---- a/src/responder/common/responder.h
-+++ b/src/responder/common/responder.h
-@@ -163,11 +163,7 @@ struct mon_cli_iface;
- typedef int (*connection_setup_t)(struct cli_ctx *cctx);
- 
- int sss_connection_setup(struct cli_ctx *cctx);
--/*
-- * NOTE: We would like to use more strong typing for the @dp_vtable argument
-- * but can't since it accepts either a struct data_provider_iface
-- * or struct data_provider_rev_iface. So pass the base struct: sbus_vtable
-- */
-+
- int sss_process_init(TALLOC_CTX *mem_ctx,
-                      struct tevent_context *ev,
-                      struct confdb_ctx *cdb,
-@@ -181,7 +177,7 @@ int sss_process_init(TALLOC_CTX *mem_ctx,
-                      uint16_t svc_version,
-                      struct mon_cli_iface *monitor_intf,
-                      const char *cli_name,
--                     struct sbus_vtable *dp_intf,
-+                     struct sbus_iface_map *sbus_iface,
-                      connection_setup_t conn_setup,
-                      struct resp_ctx **responder_ctx);
- 
-diff --git a/src/responder/common/responder_common.c b/src/responder/common/responder_common.c
-index 7f6264ae70e5073063b5cfcd73098eefad2ce653..c604c64a652221521ec7114b8588186f087eb11a 100644
---- a/src/responder/common/responder_common.c
-+++ b/src/responder/common/responder_common.c
-@@ -549,7 +549,7 @@ void idle_handler(struct tevent_context *ev,
- }
- 
- static int sss_dp_init(struct resp_ctx *rctx,
--                       struct sbus_vtable *dp_intf,
-+                       struct sbus_iface_map *sbus_iface,
-                        const char *cli_name,
-                        struct sss_domain_info *domain)
- {
-@@ -577,10 +577,12 @@ static int sss_dp_init(struct resp_ctx *rctx,
-         return ret;
-     }
- 
--    ret = sbus_conn_register_iface(be_conn->conn, dp_intf, DP_PATH, rctx);
--    if (ret != EOK) {
--        DEBUG(SSSDBG_FATAL_FAILURE, "Failed to export data provider.\n");
--        return ret;
-+    if (sbus_iface != NULL) {
-+        ret = sbus_conn_register_iface_map(be_conn->conn, sbus_iface, rctx);
-+        if (ret != EOK) {
-+            DEBUG(SSSDBG_FATAL_FAILURE, "Failed to register D-Bus interface.\n");
-+            return ret;
-+        }
-     }
- 
-     DLIST_ADD_END(rctx->be_conns, be_conn, struct be_conn *);
-@@ -925,7 +927,7 @@ int sss_process_init(TALLOC_CTX *mem_ctx,
-                      uint16_t svc_version,
-                      struct mon_cli_iface *monitor_intf,
-                      const char *cli_name,
--                     struct sbus_vtable *dp_intf,
-+                     struct sbus_iface_map *sbus_iface,
-                      connection_setup_t conn_setup,
-                      struct resp_ctx **responder_ctx)
- {
-@@ -1040,7 +1042,7 @@ int sss_process_init(TALLOC_CTX *mem_ctx,
-             continue;
-         }
- 
--        ret = sss_dp_init(rctx, dp_intf, cli_name, dom);
-+        ret = sss_dp_init(rctx, sbus_iface, cli_name, dom);
-         if (ret != EOK) {
-             DEBUG(SSSDBG_FATAL_FAILURE,
-                   "fatal error setting up backend connector\n");
-diff --git a/src/responder/common/responder_get_domains.c b/src/responder/common/responder_get_domains.c
-index 6b354d8b2251f3a5cf576a58ae191fd99f307dd7..cc7b99f30046569547a08f83e46cbbe9d6c19897 100644
---- a/src/responder/common/responder_get_domains.c
-+++ b/src/responder/common/responder_get_domains.c
-@@ -88,8 +88,8 @@ sss_dp_get_domains_msg(void *pvt)
- 
-     msg = dbus_message_new_method_call(NULL,
-                                        DP_PATH,
--                                       DATA_PROVIDER_IFACE,
--                                       DATA_PROVIDER_IFACE_GETDOMAINS);
-+                                       IFACE_DP,
-+                                       IFACE_DP_GETDOMAINS);
-     if (msg == NULL) {
-         DEBUG(SSSDBG_CRIT_FAILURE, "Out of memory?!\n");
-         return NULL;
-diff --git a/src/responder/ifp/ifpsrv.c b/src/responder/ifp/ifpsrv.c
-index a2137ecb218824909325df6c7052dbbbcb144679..0555c00167045707b7d455d28df368749b9b84f6 100644
---- a/src/responder/ifp/ifpsrv.c
-+++ b/src/responder/ifp/ifpsrv.c
-@@ -58,17 +58,6 @@ struct mon_cli_iface monitor_ifp_methods = {
-     .sysbusReconnect = ifp_sysbus_reconnect,
- };
- 
--static struct data_provider_iface ifp_dp_methods = {
--    { &data_provider_iface_meta, 0 },
--    .RegisterService = NULL,
--    .pamHandler = NULL,
--    .sudoHandler = NULL,
--    .autofsHandler = NULL,
--    .hostHandler = NULL,
--    .getDomains = NULL,
--    .getAccountInfo = NULL,
--};
--
- struct sss_cmd_table *get_ifp_cmds(void)
- {
-     static struct sss_cmd_table ifp_cmds[] = {
-@@ -238,7 +227,7 @@ int ifp_process_init(TALLOC_CTX *mem_ctx,
-                            SSS_IFP_SBUS_SERVICE_VERSION,
-                            &monitor_ifp_methods,
-                            "InfoPipe",
--                           &ifp_dp_methods.vtable,
-+                           NULL,
-                            sss_connection_setup,
-                            &rctx);
-     if (ret != EOK) {
-diff --git a/src/responder/nss/nss_iface.c b/src/responder/nss/nss_iface.c
-new file mode 100644
-index 0000000000000000000000000000000000000000..b01732e086c5fc5c7018ec84c3438e19ed812fef
---- /dev/null
-+++ b/src/responder/nss/nss_iface.c
-@@ -0,0 +1,38 @@
-+/*
-+    Authors:
-+        Pavel Březina <pbrezina@redhat.com>
-+
-+    Copyright (C) 2016 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 "sbus/sssd_dbus.h"
-+#include "responder/nss/nss_iface.h"
-+#include "responder/nss/nsssrv.h"
-+
-+struct iface_nss_memorycache iface_nss_memorycache = {
-+    { &iface_nss_memorycache_meta, 0 },
-+    .UpdateInitgroups = nss_memorycache_update_initgroups
-+};
-+
-+static struct sbus_iface_map iface_map[] = {
-+    { NSS_MEMORYCACHE_PATH, &iface_nss_memorycache.vtable },
-+    { NULL, NULL }
-+};
-+
-+struct sbus_iface_map *nss_get_sbus_interface()
-+{
-+    return iface_map;
-+}
-diff --git a/src/responder/nss/nss_iface.h b/src/responder/nss/nss_iface.h
-new file mode 100644
-index 0000000000000000000000000000000000000000..ab59928c3e2dac62cea6f793ff774d9e0f8da6db
---- /dev/null
-+++ b/src/responder/nss/nss_iface.h
-@@ -0,0 +1,30 @@
-+/*
-+    Authors:
-+        Pavel Březina <pbrezina@redhat.com>
-+
-+    Copyright (C) 2016 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 _NSS_IFACE_H_
-+#define _NSS_IFACE_H_
-+
-+#include "responder/nss/nss_iface_generated.h"
-+
-+#define NSS_MEMORYCACHE_PATH "/org/freedesktop/sssd/nss/memcache"
-+
-+struct sbus_iface_map *nss_get_sbus_interface(void);
-+
-+#endif /* _NSS_IFACE_H_ */
-diff --git a/src/responder/nss/nss_iface.xml b/src/responder/nss/nss_iface.xml
-new file mode 100644
-index 0000000000000000000000000000000000000000..b7cc4deb77135a592bad2ca62570f206231129b7
---- /dev/null
-+++ b/src/responder/nss/nss_iface.xml
-@@ -0,0 +1,12 @@
-+<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
-+ "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
-+<node>
-+    <interface name="org.freedesktop.sssd.nss.MemoryCache">
-+        <annotation value="iface_nss_memorycache" name="org.freedesktop.DBus.GLib.CSymbol"/>
-+        <method name="UpdateInitgroups">
-+            <arg name="user" type="s" direction="in" />
-+            <arg name="domain" type="s" direction="in" />
-+            <arg name="groups" type="au" direction="in" />
-+        </method>
-+    </interface>
-+</node>
-diff --git a/src/responder/nss/nss_iface_generated.c b/src/responder/nss/nss_iface_generated.c
-new file mode 100644
-index 0000000000000000000000000000000000000000..2d0031090e33df9c9e9d9fbf1a18825026509803
---- /dev/null
-+++ b/src/responder/nss/nss_iface_generated.c
-@@ -0,0 +1,69 @@
-+/* The following definitions are auto-generated from nss_iface.xml */
-+
-+#include "util/util.h"
-+#include "sbus/sssd_dbus.h"
-+#include "sbus/sssd_dbus_meta.h"
-+#include "sbus/sssd_dbus_invokers.h"
-+#include "nss_iface_generated.h"
-+
-+/* invokes a handler with a 'ssau' DBus signature */
-+static int invoke_ssau_method(struct sbus_request *dbus_req, void *function_ptr);
-+
-+/* arguments for org.freedesktop.sssd.nss.MemoryCache.UpdateInitgroups */
-+const struct sbus_arg_meta iface_nss_memorycache_UpdateInitgroups__in[] = {
-+    { "user", "s" },
-+    { "domain", "s" },
-+    { "groups", "au" },
-+    { NULL, }
-+};
-+
-+int iface_nss_memorycache_UpdateInitgroups_finish(struct sbus_request *req)
-+{
-+   return sbus_request_return_and_finish(req,
-+                                         DBUS_TYPE_INVALID);
-+}
-+
-+/* methods for org.freedesktop.sssd.nss.MemoryCache */
-+const struct sbus_method_meta iface_nss_memorycache__methods[] = {
-+    {
-+        "UpdateInitgroups", /* name */
-+        iface_nss_memorycache_UpdateInitgroups__in,
-+        NULL, /* no out_args */
-+        offsetof(struct iface_nss_memorycache, UpdateInitgroups),
-+        invoke_ssau_method,
-+    },
-+    { NULL, }
-+};
-+
-+/* interface info for org.freedesktop.sssd.nss.MemoryCache */
-+const struct sbus_interface_meta iface_nss_memorycache_meta = {
-+    "org.freedesktop.sssd.nss.MemoryCache", /* name */
-+    iface_nss_memorycache__methods,
-+    NULL, /* no signals */
-+    NULL, /* no properties */
-+    sbus_invoke_get_all, /* GetAll invoker */
-+};
-+
-+/* invokes a handler with a 'ssau' DBus signature */
-+static int invoke_ssau_method(struct sbus_request *dbus_req, void *function_ptr)
-+{
-+    const char * arg_0;
-+    const char * arg_1;
-+    uint32_t *arg_2;
-+    int len_2;
-+    int (*handler)(struct sbus_request *, void *, const char *, const char *, uint32_t[], int) = function_ptr;
-+
-+    if (!sbus_request_parse_or_finish(dbus_req,
-+                               DBUS_TYPE_STRING, &arg_0,
-+                               DBUS_TYPE_STRING, &arg_1,
-+                               DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, &arg_2, &len_2,
-+                               DBUS_TYPE_INVALID)) {
-+         return EOK; /* request handled */
-+    }
-+
-+    return (handler)(dbus_req, dbus_req->intf->handler_data,
-+                     arg_0,
-+                     arg_1,
-+                     arg_2,
-+                     len_2);
-+}
-diff --git a/src/responder/nss/nss_iface_generated.h b/src/responder/nss/nss_iface_generated.h
-new file mode 100644
-index 0000000000000000000000000000000000000000..ad902482a9be03a60cbf3663b6f771d0a2020b88
---- /dev/null
-+++ b/src/responder/nss/nss_iface_generated.h
-@@ -0,0 +1,58 @@
-+/* The following declarations are auto-generated from nss_iface.xml */
-+
-+#ifndef __NSS_IFACE_XML__
-+#define __NSS_IFACE_XML__
-+
-+#include "sbus/sssd_dbus.h"
-+
-+/* ------------------------------------------------------------------------
-+ * DBus Constants
-+ *
-+ * Various constants of interface and method names mostly for use by clients
-+ */
-+
-+/* constants for org.freedesktop.sssd.nss.MemoryCache */
-+#define IFACE_NSS_MEMORYCACHE "org.freedesktop.sssd.nss.MemoryCache"
-+#define IFACE_NSS_MEMORYCACHE_UPDATEINITGROUPS "UpdateInitgroups"
-+
-+/* ------------------------------------------------------------------------
-+ * DBus handlers
-+ *
-+ * These structures are filled in by implementors of the different
-+ * dbus interfaces to handle method calls.
-+ *
-+ * Handler functions of type sbus_msg_handler_fn accept raw messages,
-+ * other handlers are typed appropriately. If a handler that is
-+ * set to NULL is invoked it will result in a
-+ * org.freedesktop.DBus.Error.NotSupported error for the caller.
-+ *
-+ * Handlers have a matching xxx_finish() function (unless the method has
-+ * accepts raw messages). These finish functions the
-+ * sbus_request_return_and_finish() with the appropriate arguments to
-+ * construct a valid reply. Once a finish function has been called, the
-+ * @dbus_req it was called with is freed and no longer valid.
-+ */
-+
-+/* vtable for org.freedesktop.sssd.nss.MemoryCache */
-+struct iface_nss_memorycache {
-+    struct sbus_vtable vtable; /* derive from sbus_vtable */
-+    int (*UpdateInitgroups)(struct sbus_request *req, void *data, const char *arg_user, const char *arg_domain, uint32_t arg_groups[], int len_groups);
-+};
-+
-+/* finish function for UpdateInitgroups */
-+int iface_nss_memorycache_UpdateInitgroups_finish(struct sbus_request *req);
-+
-+/* ------------------------------------------------------------------------
-+ * DBus Interface Metadata
-+ *
-+ * These structure definitions are filled in with the information about
-+ * the interfaces, methods, properties and so on.
-+ *
-+ * The actual definitions are found in the accompanying C file next
-+ * to this header.
-+ */
-+
-+/* interface info for org.freedesktop.sssd.nss.MemoryCache */
-+extern const struct sbus_interface_meta iface_nss_memorycache_meta;
-+
-+#endif /* __NSS_IFACE_XML__ */
-diff --git a/src/responder/nss/nsssrv.c b/src/responder/nss/nsssrv.c
-index 8be3455e57e07481e7cf7d4d0f525dad5b8601fc..05b51ecdf2e17e20af2ee3ee48377cbe1bf19a24 100644
---- a/src/responder/nss/nsssrv.c
-+++ b/src/responder/nss/nsssrv.c
-@@ -37,6 +37,7 @@
- #include "responder/nss/nsssrv_private.h"
- #include "responder/nss/nsssrv_mmap_cache.h"
- #include "responder/nss/nsssrv_netgroup.h"
-+#include "responder/nss/nss_iface.h"
- #include "responder/common/negcache.h"
- #include "db/sysdb.h"
- #include "confdb/confdb.h"
-@@ -327,7 +328,7 @@ done:
-     return ret;
- }
- 
--static int nss_update_memcache(struct sbus_request *dbus_req, void *data)
-+int nss_update_memcache(struct sbus_request *dbus_req, void *data)
- {
-     struct resp_ctx *rctx = talloc_get_type(data, struct resp_ctx);
-     struct nss_ctx *nctx = talloc_get_type(rctx->pvt_ctx, struct nss_ctx);
-@@ -338,37 +339,24 @@ static int nss_update_memcache(struct sbus_request *dbus_req, void *data)
-     return EOK;
- }
- 
--static int nss_memcache_initgr_check(struct sbus_request *dbus_req, void *data)
-+int nss_memorycache_update_initgroups(struct sbus_request *sbus_req,
-+                                      void *data,
-+                                      const char *user,
-+                                      const char *domain,
-+                                      uint32_t *groups,
-+                                      int num_groups)
- {
-     struct resp_ctx *rctx = talloc_get_type(data, struct resp_ctx);
-     struct nss_ctx *nctx = talloc_get_type(rctx->pvt_ctx, struct nss_ctx);
--    char *user;
--    char *domain;
--    uint32_t *groups;
--    int gnum;
- 
--    if (!sbus_request_parse_or_finish(dbus_req,
--                                      DBUS_TYPE_STRING, &user,
--                                      DBUS_TYPE_STRING, &domain,
--                                      DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, &groups, &gnum,
--                                      DBUS_TYPE_INVALID)) {
--        return EOK; /* handled */
--    }
-+    DEBUG(SSSDBG_TRACE_LIBS, "Updating inigroups memory cache of [%s@%s]\n",
-+          user, domain);
- 
--    DEBUG(SSSDBG_TRACE_LIBS,
--          "Got request for [%s@%s]\n", user, domain);
-+    nss_update_initgr_memcache(nctx, user, domain, num_groups, groups);
- 
--    nss_update_initgr_memcache(nctx, user, domain, gnum, groups);
--
--    return sbus_request_return_and_finish(dbus_req, DBUS_TYPE_INVALID);
-+    return iface_nss_memorycache_UpdateInitgroups_finish(sbus_req);
- }
- 
--static struct data_provider_rev_iface nss_dp_methods = {
--    { &data_provider_rev_iface_meta, 0 },
--    .updateCache = nss_update_memcache,
--    .initgrCheck = nss_memcache_initgr_check
--};
--
- static void nss_dp_reconnect_init(struct sbus_connection *conn,
-                                   int status, void *pvt)
- {
-@@ -419,7 +407,8 @@ int nss_process_init(TALLOC_CTX *mem_ctx,
-                            NSS_SBUS_SERVICE_NAME,
-                            NSS_SBUS_SERVICE_VERSION,
-                            &monitor_nss_methods,
--                           "NSS", &nss_dp_methods.vtable,
-+                           "NSS",
-+                           nss_get_sbus_interface(),
-                            nss_connection_setup,
-                            &rctx);
-     if (ret != EOK) {
-diff --git a/src/responder/nss/nsssrv.h b/src/responder/nss/nsssrv.h
-index 2977479aa52082480f92eab94f7833e2e696a9ac..d4a80f76df236f40d872c701687bf453255d9890 100644
---- a/src/responder/nss/nsssrv.h
-+++ b/src/responder/nss/nsssrv.h
-@@ -81,4 +81,11 @@ struct nss_packet;
- 
- struct sss_cmd_table *get_nss_cmds(void);
- 
-+int nss_memorycache_update_initgroups(struct sbus_request *sbus_req,
-+                                      void *data,
-+                                      const char *user,
-+                                      const char *domain,
-+                                      uint32_t *groups,
-+                                      int num_groups);
-+
- #endif /* __NSSSRV_H__ */
-diff --git a/src/responder/pac/pacsrv.c b/src/responder/pac/pacsrv.c
-index 15d1986f842ac8397cf509ca8ef44728d6ddc5f1..852deb10eff014189d35a2769d895a901d8296e1 100644
---- a/src/responder/pac/pacsrv.c
-+++ b/src/responder/pac/pacsrv.c
-@@ -61,17 +61,6 @@ struct mon_cli_iface monitor_pac_methods = {
-     .sysbusReconnect = NULL,
- };
- 
--static struct data_provider_iface pac_dp_methods = {
--    { &data_provider_iface_meta, 0 },
--    .RegisterService = NULL,
--    .pamHandler = NULL,
--    .sudoHandler = NULL,
--    .autofsHandler = NULL,
--    .hostHandler = NULL,
--    .getDomains = NULL,
--    .getAccountInfo = NULL,
--};
--
- /* TODO: check if this can be made generic for all responders */
- static void pac_dp_reconnect_init(struct sbus_connection *conn,
-                                   int status, void *pvt)
-@@ -122,7 +111,7 @@ int pac_process_init(TALLOC_CTX *mem_ctx,
-                            PAC_SBUS_SERVICE_NAME,
-                            PAC_SBUS_SERVICE_VERSION,
-                            &monitor_pac_methods,
--                           "PAC", &pac_dp_methods.vtable,
-+                           "PAC", NULL,
-                            sss_connection_setup,
-                            &rctx);
-     if (ret != EOK) {
-diff --git a/src/responder/pam/pamsrv.c b/src/responder/pam/pamsrv.c
-index efd1e5c7527decda5de7304b54919846fa2ee0db..9374de4d63b2886262ca1541daff581603d7c838 100644
---- a/src/responder/pam/pamsrv.c
-+++ b/src/responder/pam/pamsrv.c
-@@ -66,17 +66,6 @@ struct mon_cli_iface monitor_pam_methods = {
-     .sysbusReconnect = NULL,
- };
- 
--static struct data_provider_iface pam_dp_methods = {
--    { &data_provider_iface_meta, 0 },
--    .RegisterService = NULL,
--    .pamHandler = NULL,
--    .sudoHandler = NULL,
--    .autofsHandler = NULL,
--    .hostHandler = NULL,
--    .getDomains = NULL,
--    .getAccountInfo = NULL,
--};
--
- static void pam_dp_reconnect_init(struct sbus_connection *conn, int status, void *pvt)
- {
-     struct be_conn *be_conn = talloc_get_type(pvt, struct be_conn);
-@@ -201,7 +190,7 @@ static int pam_process_init(TALLOC_CTX *mem_ctx,
-                            SSS_PAM_SBUS_SERVICE_NAME,
-                            SSS_PAM_SBUS_SERVICE_VERSION,
-                            &monitor_pam_methods,
--                           "PAM", &pam_dp_methods.vtable,
-+                           "PAM", NULL,
-                            sss_connection_setup,
-                            &rctx);
-     if (ret != EOK) {
-diff --git a/src/responder/pam/pamsrv_dp.c b/src/responder/pam/pamsrv_dp.c
-index 826146350670d67f897ee7eec2cf6ca607b96435..aa3fdc3c32d234ed54a9f5202886157601ee3846 100644
---- a/src/responder/pam/pamsrv_dp.c
-+++ b/src/responder/pam/pamsrv_dp.c
-@@ -130,8 +130,8 @@ int pam_dp_send_req(struct pam_auth_req *preq, int timeout)
- 
-     msg = dbus_message_new_method_call(NULL,
-                                        DP_PATH,
--                                       DATA_PROVIDER_IFACE,
--                                       DATA_PROVIDER_IFACE_PAMHANDLER);
-+                                       IFACE_DP,
-+                                       IFACE_DP_PAMHANDLER);
-     if (msg == NULL) {
-         DEBUG(SSSDBG_FATAL_FAILURE,"Out of memory?!\n");
-         return ENOMEM;
-diff --git a/src/responder/ssh/sshsrv.c b/src/responder/ssh/sshsrv.c
-index f763e3b00d20527225046a85609e7ff56861f682..88938215b542b5748721cfecc59ca4141010fb88 100644
---- a/src/responder/ssh/sshsrv.c
-+++ b/src/responder/ssh/sshsrv.c
-@@ -41,17 +41,6 @@ struct mon_cli_iface monitor_ssh_methods = {
-     .sysbusReconnect = NULL,
- };
- 
--static struct data_provider_iface ssh_dp_methods = {
--    { &data_provider_iface_meta, 0 },
--    .RegisterService = NULL,
--    .pamHandler = NULL,
--    .sudoHandler = NULL,
--    .autofsHandler = NULL,
--    .hostHandler = NULL,
--    .getDomains = NULL,
--    .getAccountInfo = NULL,
--};
--
- static void ssh_dp_reconnect_init(struct sbus_connection *conn,
-                                   int status, void *pvt)
- {
-@@ -96,7 +85,7 @@ int ssh_process_init(TALLOC_CTX *mem_ctx,
-                            SSS_SSH_SBUS_SERVICE_VERSION,
-                            &monitor_ssh_methods,
-                            "SSH",
--                           &ssh_dp_methods.vtable,
-+                           NULL,
-                            sss_connection_setup,
-                            &rctx);
-     if (ret != EOK) {
-diff --git a/src/responder/sudo/sudosrv.c b/src/responder/sudo/sudosrv.c
-index e0346033e38f1e39e621e131c3265a583b91a5c3..d832686a8572f3729a0477cdca2f77ebcb19fbc0 100644
---- a/src/responder/sudo/sudosrv.c
-+++ b/src/responder/sudo/sudosrv.c
-@@ -42,17 +42,6 @@ struct mon_cli_iface monitor_sudo_methods = {
-     .sysbusReconnect = NULL,
- };
- 
--static struct data_provider_iface sudo_dp_methods = {
--    { &data_provider_iface_meta, 0 },
--    .RegisterService = NULL,
--    .pamHandler = NULL,
--    .sudoHandler = NULL,
--    .autofsHandler = NULL,
--    .hostHandler = NULL,
--    .getDomains = NULL,
--    .getAccountInfo = NULL,
--};
--
- static void sudo_dp_reconnect_init(struct sbus_connection *conn,
-                                    int status,
-                                    void *pvt)
-@@ -98,7 +87,7 @@ int sudo_process_init(TALLOC_CTX *mem_ctx,
-                            SSS_SUDO_SBUS_SERVICE_VERSION,
-                            &monitor_sudo_methods,
-                            "SUDO",
--                           &sudo_dp_methods.vtable,
-+                           NULL,
-                            sss_connection_setup,
-                            &rctx);
-     if (ret != EOK) {
-diff --git a/src/tests/cwrap/Makefile.am b/src/tests/cwrap/Makefile.am
-index d8a49f1434cefc02bc7fce505d1b4e07fc74ec5f..3e40cba52e927730483b14cc7e56687b250de646 100644
---- a/src/tests/cwrap/Makefile.am
-+++ b/src/tests/cwrap/Makefile.am
-@@ -49,7 +49,6 @@ SSSD_RESPONDER_OBJ = \
-     ../../../src/responder/common/data_provider/rdp_message.c \
-     ../../../src/responder/common/data_provider/rdp_client.c \
-     ../../../src/monitor/monitor_iface_generated.c \
--    ../../../src/providers/data_provider_iface_generated.c \
-     ../../../src/providers/data_provider_req.c
- 
- dist_noinst_DATA = \
--- 
-2.4.11
-
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
new file mode 100644
index 0000000..66fee16
--- /dev/null
+++ b/SOURCES/0100-sbus-check-connection-for-NULL-before-unregister-it.patch
@@ -0,0 +1,44 @@
+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-NSS-Remove-unused-functions.patch b/SOURCES/0101-NSS-Remove-unused-functions.patch
deleted file mode 100644
index f7b683f..0000000
--- a/SOURCES/0101-NSS-Remove-unused-functions.patch
+++ /dev/null
@@ -1,187 +0,0 @@
-From 684ab7416ea1311a98516faddcc975a9731b2a0f Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Pavel=20B=C5=99ezina?= <pbrezina@redhat.com>
-Date: Tue, 19 Jul 2016 14:42:26 +0200
-Subject: [PATCH 101/102] NSS: Remove unused functions
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-When removing the old data provider I noticed that those functions
-are not used at all.
-
-Reviewed-by: Lukáš Slebodník <lslebodn@redhat.com>
-(cherry picked from commit f31610a9ba26b46de9eeab2b0719ff6ad8961104)
----
- src/responder/nss/nsssrv.c         |  11 ----
- src/responder/nss/nsssrv_cmd.c     | 112 -------------------------------------
- src/responder/nss/nsssrv_private.h |   2 -
- 3 files changed, 125 deletions(-)
-
-diff --git a/src/responder/nss/nsssrv.c b/src/responder/nss/nsssrv.c
-index 05b51ecdf2e17e20af2ee3ee48377cbe1bf19a24..06d58f21b00b73c6c7a4b7583e10b4b61a627d75 100644
---- a/src/responder/nss/nsssrv.c
-+++ b/src/responder/nss/nsssrv.c
-@@ -328,17 +328,6 @@ done:
-     return ret;
- }
- 
--int nss_update_memcache(struct sbus_request *dbus_req, void *data)
--{
--    struct resp_ctx *rctx = talloc_get_type(data, struct resp_ctx);
--    struct nss_ctx *nctx = talloc_get_type(rctx->pvt_ctx, struct nss_ctx);
--
--    nss_update_pw_memcache(nctx);
--    nss_update_gr_memcache(nctx);
--
--    return EOK;
--}
--
- int nss_memorycache_update_initgroups(struct sbus_request *sbus_req,
-                                       void *data,
-                                       const char *user,
-diff --git a/src/responder/nss/nsssrv_cmd.c b/src/responder/nss/nsssrv_cmd.c
-index 573959ea76fc1277fe84f40b88dcd34093da468d..b64cea2a53ec6032904237b0afc1377022c2c804 100644
---- a/src/responder/nss/nsssrv_cmd.c
-+++ b/src/responder/nss/nsssrv_cmd.c
-@@ -118,66 +118,6 @@ static int nss_reset_negcache(struct resp_ctx *rctx)
-  * PASSWD db related functions
-  ***************************************************************************/
- 
--void nss_update_pw_memcache(struct nss_ctx *nctx)
--{
--    struct sss_domain_info *dom;
--    struct ldb_result *res;
--    uint64_t exp;
--    struct sized_string key;
--    const char *id;
--    time_t now;
--    int ret;
--    int i;
--
--    now = time(NULL);
--
--    for (dom = nctx->rctx->domains; dom; dom = get_next_domain(dom, 0)) {
--        ret = sysdb_enumpwent_with_views(nctx, dom, &res);
--        if (ret != EOK) {
--            DEBUG(SSSDBG_CRIT_FAILURE,
--                  "Failed to enumerate users for domain [%s]\n", dom->name);
--            continue;
--        }
--
--        for (i = 0; i < res->count; i++) {
--            exp = ldb_msg_find_attr_as_uint64(res->msgs[i],
--                                              SYSDB_CACHE_EXPIRE, 0);
--            if (exp >= now) {
--                continue;
--            }
--
--            /* names require more manipulation (build up fqname conditionally),
--             * but uidNumber is unique and always resolvable too, so we use
--             * that to update the cache, as it points to the same entry */
--            id = sss_view_ldb_msg_find_attr_as_string(dom, res->msgs[i],
--                                                      SYSDB_UIDNUM, NULL);
--            if (!id) {
--                DEBUG(SSSDBG_CRIT_FAILURE,
--                      "Failed to find uidNumber in %s.\n",
--                       ldb_dn_get_linearized(res->msgs[i]->dn));
--                continue;
--            }
--            to_sized_string(&key, id);
--
--            ret = sss_mmap_cache_pw_invalidate(nctx->pwd_mc_ctx, &key);
--            if (ret != EOK && ret != ENOENT) {
--                DEBUG(SSSDBG_CRIT_FAILURE,
--                      "Internal failure in memory cache code: %d [%s]\n",
--                       ret, strerror(ret));
--            }
--
--            ret = sss_mmap_cache_pw_invalidate(nctx->initgr_mc_ctx, &key);
--            if (ret != EOK && ret != ENOENT) {
--                DEBUG(SSSDBG_CRIT_FAILURE,
--                      "Internal failure in memory cache code: %d [%s]\n",
--                      ret, strerror(ret));
--            }
--        }
--
--        talloc_zfree(res);
--    }
--}
--
- static gid_t get_gid_override(struct ldb_message *msg,
-                               struct sss_domain_info *dom)
- {
-@@ -2743,58 +2683,6 @@ done:
-  * GROUP db related functions
-  ***************************************************************************/
- 
--void nss_update_gr_memcache(struct nss_ctx *nctx)
--{
--    struct sss_domain_info *dom;
--    struct ldb_result *res;
--    uint64_t exp;
--    struct sized_string key;
--    const char *id;
--    time_t now;
--    int ret;
--    int i;
--
--    now = time(NULL);
--
--    for (dom = nctx->rctx->domains; dom; dom = get_next_domain(dom, 0)) {
--        ret = sysdb_enumgrent_with_views(nctx, dom, &res);
--        if (ret != EOK) {
--            DEBUG(SSSDBG_CRIT_FAILURE,
--                  "Failed to enumerate users for domain [%s]\n", dom->name);
--            continue;
--        }
--
--        for (i = 0; i < res->count; i++) {
--            exp = ldb_msg_find_attr_as_uint64(res->msgs[i],
--                                              SYSDB_CACHE_EXPIRE, 0);
--            if (exp >= now) {
--                continue;
--            }
--
--            /* names require more manipulation (build up fqname conditionally),
--             * but uidNumber is unique and always resolvable too, so we use
--             * that to update the cache, as it points to the same entry */
--            id = sss_view_ldb_msg_find_attr_as_string(dom, res->msgs[i],
--                                                      SYSDB_GIDNUM, NULL);
--            if (!id) {
--                DEBUG(SSSDBG_CRIT_FAILURE,
--                      "Failed to find gidNumber in %s.\n",
--                       ldb_dn_get_linearized(res->msgs[i]->dn));
--                continue;
--            }
--            to_sized_string(&key, id);
--
--            ret = sss_mmap_cache_gr_invalidate(nctx->grp_mc_ctx, &key);
--            if (ret != EOK && ret != ENOENT) {
--                DEBUG(SSSDBG_CRIT_FAILURE,
--                      "Internal failure in memory cache code: %d [%s]\n",
--                       ret, strerror(ret));
--            }
--        }
--        talloc_zfree(res);
--    }
--}
--
- #define GID_ROFFSET 0
- #define MNUM_ROFFSET sizeof(uint32_t)
- #define STRS_ROFFSET 2*sizeof(uint32_t)
-diff --git a/src/responder/nss/nsssrv_private.h b/src/responder/nss/nsssrv_private.h
-index 391eaaf40f84a7436bee63fd699241e4957fdbeb..472022235adab2c2d2547ee2706ef23fa91b1f8d 100644
---- a/src/responder/nss/nsssrv_private.h
-+++ b/src/responder/nss/nsssrv_private.h
-@@ -143,8 +143,6 @@ errno_t check_cache(struct nss_dom_ctx *dctx,
-                     sss_dp_callback_t callback,
-                     void *pvt);
- 
--void nss_update_pw_memcache(struct nss_ctx *nctx);
--void nss_update_gr_memcache(struct nss_ctx *nctx);
- void nss_update_initgr_memcache(struct nss_ctx *nctx,
-                                 const char *fq_name, const char *domain,
-                                 int gnum, uint32_t *groups);
--- 
-2.4.11
-
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
new file mode 100644
index 0000000..1ae6353
--- /dev/null
+++ b/SOURCES/0101-selinux-Do-not-fail-if-SELinux-is-not-managed.patch
@@ -0,0 +1,211 @@
+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-LDAP-Fixing-of-removing-netgroup-from-cache.patch b/SOURCES/0102-LDAP-Fixing-of-removing-netgroup-from-cache.patch
deleted file mode 100644
index 2932c7e..0000000
--- a/SOURCES/0102-LDAP-Fixing-of-removing-netgroup-from-cache.patch
+++ /dev/null
@@ -1,75 +0,0 @@
-From c0dedeccc42fa7cc14e207182d54595926dfd700 Mon Sep 17 00:00:00 2001
-From: Petr Cech <pcech@redhat.com>
-Date: Fri, 22 Jul 2016 14:28:54 +0200
-Subject: [PATCH 102/102] LDAP: Fixing of removing netgroup from cache
-
-There were problem with local key which wasn't properly removed.
-This patch fixes it.
-
-Resolves:
-https://fedorahosted.org/sssd/ticket/2841
----
- src/providers/ldap/sdap_async_netgroups.c | 40 +++++++++++++++++++++++++++++++
- 1 file changed, 40 insertions(+)
-
-diff --git a/src/providers/ldap/sdap_async_netgroups.c b/src/providers/ldap/sdap_async_netgroups.c
-index df233d956df70cfcb5f68bd2afc9e2a23c50c3bb..cf7d7b12361f8cc578b891961c0c5566442f1b4e 100644
---- a/src/providers/ldap/sdap_async_netgroups.c
-+++ b/src/providers/ldap/sdap_async_netgroups.c
-@@ -38,6 +38,35 @@ bool is_dn(const char *str)
-     return (ret == LDAP_SUCCESS ? true : false);
- }
- 
-+static errno_t add_to_missing_attrs(TALLOC_CTX * mem_ctx,
-+                                    struct sysdb_attrs *attrs,
-+                                    const char *ext_key,
-+                                    char ***_missing)
-+{
-+    bool is_present = false;
-+    size_t size = 0;
-+    size_t ret;
-+
-+    for (int i = 0; i < attrs->num; i++) {
-+        if (strcmp(ext_key, attrs->a[i].name) == 0) {
-+            is_present = true;
-+        }
-+        size++;
-+    }
-+
-+    if (is_present == false) {
-+        ret = add_string_to_list(attrs, ext_key, _missing);
-+        if (ret != EOK) {
-+            goto done;
-+        }
-+    }
-+
-+    ret = EOK;
-+
-+done:
-+    return ret;
-+}
-+
- static errno_t sdap_save_netgroup(TALLOC_CTX *memctx,
-                                   struct sss_domain_info *dom,
-                                   struct sdap_options *opts,
-@@ -138,6 +167,17 @@ static errno_t sdap_save_netgroup(TALLOC_CTX *memctx,
-         goto fail;
-     }
- 
-+    /* Prepare SYSDB_NETGROUP_MEMBER removing
-+     * if not present in netgroup_attrs
-+     */
-+    ret = add_to_missing_attrs(attrs, netgroup_attrs, SYSDB_NETGROUP_MEMBER,
-+                               &missing);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_CRIT_FAILURE, "Failed to add [%s] to missing attributes\n",
-+              SYSDB_NETGROUP_MEMBER);
-+        goto fail;
-+    }
-+
-     ret = sysdb_add_netgroup(dom, name, NULL, netgroup_attrs, missing,
-                              dom->netgroup_timeout, now);
-     if (ret) goto fail;
--- 
-2.4.11
-
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
new file mode 100644
index 0000000..c91d390
--- /dev/null
+++ b/SOURCES/0102-UTIL-Use-max-15-characters-for-AD-host-UPN.patch
@@ -0,0 +1,49 @@
+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_PROVIDER-Add-ad_enabled_domains-option.patch b/SOURCES/0103-AD_PROVIDER-Add-ad_enabled_domains-option.patch
deleted file mode 100644
index d31b96f..0000000
--- a/SOURCES/0103-AD_PROVIDER-Add-ad_enabled_domains-option.patch
+++ /dev/null
@@ -1,122 +0,0 @@
-From 5377817417b800335c5ae21f7e6b301ddbcbe1d1 Mon Sep 17 00:00:00 2001
-From: Petr Cech <pcech@redhat.com>
-Date: Fri, 13 May 2016 05:21:07 -0400
-Subject: [PATCH 103/108] AD_PROVIDER: Add ad_enabled_domains option
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Resolves:
-https://fedorahosted.org/sssd/ticket/2828
-
-Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
-Reviewed-by: Lukáš Slebodník <lslebodn@redhat.com>
----
- src/config/SSSDConfig/__init__.py.in   |  1 +
- src/config/cfg_rules.ini               |  1 +
- src/config/etc/sssd.api.d/sssd-ad.conf |  1 +
- src/man/sssd-ad.5.xml                  | 27 +++++++++++++++++++++++++++
- src/providers/ad/ad_common.h           |  1 +
- src/providers/ad/ad_opts.c             |  1 +
- 6 files changed, 32 insertions(+)
-
-diff --git a/src/config/SSSDConfig/__init__.py.in b/src/config/SSSDConfig/__init__.py.in
-index ac538788b9878dc2613cb48b7483d392cca41d47..1718a9babf390b95710ec356f25f09ea679bdd73 100644
---- a/src/config/SSSDConfig/__init__.py.in
-+++ b/src/config/SSSDConfig/__init__.py.in
-@@ -192,6 +192,7 @@ option_strings = {
- 
-     # [provider/ad]
-     'ad_domain' : _('Active Directory domain'),
-+    'ad_enabled_domains' : _('Enabled Active Directory domains'),
-     'ad_server' : _('Active Directory server address'),
-     'ad_backup_server' : _('Active Directory backup server address'),
-     'ad_hostname' : _('Active Directory client hostname'),
-diff --git a/src/config/cfg_rules.ini b/src/config/cfg_rules.ini
-index bd0116f334e2605e7671a208225761421511a75a..ef6435b08aee416e377fe854e6768f3fa4fd9650 100644
---- a/src/config/cfg_rules.ini
-+++ b/src/config/cfg_rules.ini
-@@ -335,6 +335,7 @@ option = ad_access_filter
- option = ad_backup_server
- option = ad_domain
- option = ad_enable_dns_sites
-+option = ad_enabled_domains
- option = ad_enable_gc
- option = ad_gpo_access_control
- option = ad_gpo_cache_timeout
-diff --git a/src/config/etc/sssd.api.d/sssd-ad.conf b/src/config/etc/sssd.api.d/sssd-ad.conf
-index 87a74f4af0770874c71baaea02d2313721db78bf..8d97a416c8c97bff096042b0b70a3b2c18183710 100644
---- a/src/config/etc/sssd.api.d/sssd-ad.conf
-+++ b/src/config/etc/sssd.api.d/sssd-ad.conf
-@@ -1,5 +1,6 @@
- [provider/ad]
- ad_domain = str, None, false
-+ad_enabled_domains = str, None, false
- ad_server = str, None, false
- ad_backup_server = str, None, false
- ad_hostname = str, None, false
-diff --git a/src/man/sssd-ad.5.xml b/src/man/sssd-ad.5.xml
-index ef27976dd62e164cfb91359efc69bd54e1aa9711..8a2f4ade9387f0d5723b7056bdce9e83363cf035 100644
---- a/src/man/sssd-ad.5.xml
-+++ b/src/man/sssd-ad.5.xml
-@@ -114,6 +114,33 @@ ldap_id_mapping = False
-                 </varlistentry>
- 
-                 <varlistentry>
-+                    <term>ad_enabled_domains (string)</term>
-+                    <listitem>
-+                        <para>
-+                            A comma-separated list of enabled Active Directory domains.
-+                            If provided, SSSD will ignore any domains not listed in this
-+                            option. If left unset, all domains from the AD forest will
-+                            be available.
-+                        </para>
-+                        <para>
-+                            For proper operation, this option must be specified in all
-+                            lower-case and as the fully qualified domain name of the
-+                            Active Directory domain. For example:
-+                            <programlisting>
-+ad_enabled_domains = sales.example.com, eng.example.com
-+                            </programlisting>
-+                        </para>
-+                        <para>
-+                            The short domain name (also known as the NetBIOS or the flat
-+                            name) will be autodetected by SSSD.
-+                        </para>
-+                        <para>
-+                            Default: Not set
-+                        </para>
-+                    </listitem>
-+                </varlistentry>
-+
-+                <varlistentry>
-                     <term>ad_server, ad_backup_server (string)</term>
-                     <listitem>
-                         <para>
-diff --git a/src/providers/ad/ad_common.h b/src/providers/ad/ad_common.h
-index 7e86faf1142d7be49eef01e1ddd7bfafea2fcedc..23351e328968918aa9ca9009c052e670a7d55258 100644
---- a/src/providers/ad/ad_common.h
-+++ b/src/providers/ad/ad_common.h
-@@ -42,6 +42,7 @@ struct ad_options;
- 
- enum ad_basic_opt {
-     AD_DOMAIN = 0,
-+    AD_ENABLED_DOMAINS,
-     AD_SERVER,
-     AD_BACKUP_SERVER,
-     AD_HOSTNAME,
-diff --git a/src/providers/ad/ad_opts.c b/src/providers/ad/ad_opts.c
-index 829f9d9556bc3fa74a95eb76db0e31b19befe8fe..fc1dc67337845754eba8c879c78e08c1777a4abc 100644
---- a/src/providers/ad/ad_opts.c
-+++ b/src/providers/ad/ad_opts.c
-@@ -28,6 +28,7 @@
- 
- struct dp_option ad_basic_opts[] = {
-     { "ad_domain", DP_OPT_STRING, NULL_STRING, NULL_STRING },
-+    { "ad_enabled_domains", DP_OPT_STRING, NULL_STRING, NULL_STRING },
-     { "ad_server", DP_OPT_STRING, NULL_STRING, NULL_STRING },
-     { "ad_backup_server", DP_OPT_STRING, NULL_STRING, NULL_STRING },
-     { "ad_hostname", DP_OPT_STRING, NULL_STRING, NULL_STRING },
--- 
-2.4.11
-
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
new file mode 100644
index 0000000..89b507c
--- /dev/null
+++ b/SOURCES/0103-Move-sized_output_name-and-sized_domain_name-into-re.patch
@@ -0,0 +1,300 @@
+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-AD_PROVIDER-Initializing-of-ad_enabled_domains.patch b/SOURCES/0104-AD_PROVIDER-Initializing-of-ad_enabled_domains.patch
deleted file mode 100644
index 9fa1c6c..0000000
--- a/SOURCES/0104-AD_PROVIDER-Initializing-of-ad_enabled_domains.patch
+++ /dev/null
@@ -1,143 +0,0 @@
-From 7e8b6166086cf04c5b1290c3dffd268438ef9c2c Mon Sep 17 00:00:00 2001
-From: Petr Cech <pcech@redhat.com>
-Date: Tue, 21 Jun 2016 08:34:15 +0200
-Subject: [PATCH 104/108] AD_PROVIDER: Initializing of ad_enabled_domains
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-We add ad_enabled_domains into ad_subdomains_ctx.
-
-Resolves:
-https://fedorahosted.org/sssd/ticket/2828
-
-Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
-Reviewed-by: Lukáš Slebodník <lslebodn@redhat.com>
----
- src/providers/ad/ad_subdomains.c | 82 ++++++++++++++++++++++++++++++++++++++++
- 1 file changed, 82 insertions(+)
-
-diff --git a/src/providers/ad/ad_subdomains.c b/src/providers/ad/ad_subdomains.c
-index a0d5c2e544fc62fda64771dce59b3b7ab8ecd8b6..6e44760330275f7e4262e6863f180747f659edb5 100644
---- a/src/providers/ad/ad_subdomains.c
-+++ b/src/providers/ad/ad_subdomains.c
-@@ -57,6 +57,79 @@
- /* do not refresh more often than every 5 seconds for now */
- #define AD_SUBDOMAIN_REFRESH_LIMIT 5
- 
-+static errno_t ad_get_enabled_domains(TALLOC_CTX *mem_ctx,
-+                                      struct ad_id_ctx *ad_id_ctx,
-+                                      const char *ad_domain,
-+                                      const char ***_ad_enabled_domains)
-+{
-+    int ret;
-+    const char *str;
-+    const char *option_name;
-+    const char **domains = NULL;
-+    int count;
-+    bool is_ad_in_domains;
-+    TALLOC_CTX *tmp_ctx = NULL;
-+
-+    tmp_ctx = talloc_new(NULL);
-+    if (tmp_ctx == NULL) {
-+        return ENOMEM;
-+    }
-+
-+    str = dp_opt_get_cstring(ad_id_ctx->ad_options->basic, AD_ENABLED_DOMAINS);
-+    if (str == NULL) {
-+        *_ad_enabled_domains = NULL;
-+        ret = EOK;
-+        goto done;
-+    }
-+
-+    count = 0;
-+    ret = split_on_separator(tmp_ctx, str, ',', true, true,
-+                             discard_const_p(char **, &domains), &count);
-+    if (ret != EOK) {
-+        option_name = ad_id_ctx->ad_options->basic[AD_ENABLED_DOMAINS].opt_name;
-+        DEBUG(SSSDBG_CRIT_FAILURE, "Failed to parse option [%s], [%i] [%s]!\n",
-+                                   option_name, ret, sss_strerror(ret));
-+        ret = EINVAL;
-+        goto done;
-+    }
-+
-+    is_ad_in_domains = false;
-+    for (int i = 0; i < count; i++) {
-+        is_ad_in_domains += strcmp(ad_domain, domains[i]) == 0 ? true : false;
-+    }
-+
-+    if (is_ad_in_domains == false) {
-+        domains = talloc_realloc(tmp_ctx, domains, const char*, count + 2);
-+        if (domains == NULL) {
-+            ret = ENOMEM;
-+            goto done;
-+        }
-+
-+        domains[count] = talloc_strdup(domains, ad_domain);
-+        if (domains[count] == NULL) {
-+            ret = ENOMEM;
-+            goto done;
-+        }
-+
-+        domains[count + 1] = NULL;
-+    } else {
-+        domains = talloc_realloc(tmp_ctx, domains, const char*, count + 1);
-+        if (domains == NULL) {
-+            ret = ENOMEM;
-+            goto done;
-+        }
-+
-+        domains[count] = NULL;
-+    }
-+
-+    *_ad_enabled_domains = talloc_steal(mem_ctx, domains);
-+    ret = EOK;
-+
-+done:
-+    talloc_free(tmp_ctx);
-+    return ret;
-+}
-+
- static errno_t
- ad_subdom_ad_ctx_new(struct be_ctx *be_ctx,
-                      struct ad_id_ctx *id_ctx,
-@@ -171,6 +244,7 @@ struct ad_subdomains_ctx {
- 
-     struct sdap_domain *sdom;
-     char *domain_name;
-+    const char **ad_enabled_domains;
- 
-     time_t last_refreshed;
- };
-@@ -1357,6 +1431,7 @@ errno_t ad_subdomains_init(TALLOC_CTX *mem_ctx,
- {
-     struct ad_subdomains_ctx *sd_ctx;
-     const char *ad_domain;
-+    const char **ad_enabled_domains = NULL;
-     time_t period;
-     errno_t ret;
- 
-@@ -1368,6 +1443,12 @@ errno_t ad_subdomains_init(TALLOC_CTX *mem_ctx,
-         return ENOMEM;
-     }
- 
-+    ret = ad_get_enabled_domains(sd_ctx, ad_id_ctx, ad_domain,
-+                                 &ad_enabled_domains);
-+    if (ret != EOK) {
-+        return EINVAL;
-+    }
-+
-     sd_ctx->be_ctx = be_ctx;
-     sd_ctx->sdom = ad_id_ctx->sdap_id_ctx->opts->sdom;
-     sd_ctx->sdap_id_ctx = ad_id_ctx->sdap_id_ctx;
-@@ -1376,6 +1457,7 @@ errno_t ad_subdomains_init(TALLOC_CTX *mem_ctx,
-         DEBUG(SSSDBG_OP_FAILURE, "talloc_strdup failed.\n");
-         return ENOMEM;
-     }
-+    sd_ctx->ad_enabled_domains = ad_enabled_domains;
-     sd_ctx->ad_id_ctx = ad_id_ctx;
- 
-     dp_set_method(dp_methods, DPM_DOMAINS_HANDLER,
--- 
-2.4.11
-
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
new file mode 100644
index 0000000..8d252f9
--- /dev/null
+++ b/SOURCES/0104-IFP-Use-sized_domain_name-to-format-the-groups-the-u.patch
@@ -0,0 +1,95 @@
+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-AD_PROVIDER-ad_enabled_domains-only-master.patch b/SOURCES/0105-AD_PROVIDER-ad_enabled_domains-only-master.patch
deleted file mode 100644
index 81520da..0000000
--- a/SOURCES/0105-AD_PROVIDER-ad_enabled_domains-only-master.patch
+++ /dev/null
@@ -1,57 +0,0 @@
-From a0009ecf7cfaaa14eb5b041b9e1d8247d18c61cb Mon Sep 17 00:00:00 2001
-From: Petr Cech <pcech@redhat.com>
-Date: Tue, 21 Jun 2016 09:48:52 +0200
-Subject: [PATCH 105/108] AD_PROVIDER: ad_enabled_domains - only master
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-We can skip looking up other domains if option ad_enabled_domains
-contains only master domain.
-
-Resolves:
-https://fedorahosted.org/sssd/ticket/2828
-
-Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
-Reviewed-by: Lukáš Slebodník <lslebodn@redhat.com>
----
- src/providers/ad/ad_subdomains.c | 16 ++++++++++++++++
- 1 file changed, 16 insertions(+)
-
-diff --git a/src/providers/ad/ad_subdomains.c b/src/providers/ad/ad_subdomains.c
-index 6e44760330275f7e4262e6863f180747f659edb5..5fdfc63886457db02ea4edc430341b31c3e545ce 100644
---- a/src/providers/ad/ad_subdomains.c
-+++ b/src/providers/ad/ad_subdomains.c
-@@ -1170,6 +1170,7 @@ static void ad_subdomains_refresh_connect_done(struct tevent_req *subreq)
-         return;
-     }
- 
-+    /* connect to the DC we are a member of */
-     subreq = ad_master_domain_send(state, state->ev, state->id_ctx->conn,
-                                    state->sdap_op, state->sd_ctx->domain_name);
-     if (subreq == NULL) {
-@@ -1218,6 +1219,21 @@ static void ad_subdomains_refresh_master_done(struct tevent_req *subreq)
-         goto done;
-     }
- 
-+    /*
-+     * If ad_enabled_domains contains only master domain
-+     * we shouldn't lookup other domains.
-+     */
-+    if (state->sd_ctx->ad_enabled_domains != NULL) {
-+        if (talloc_array_length(state->sd_ctx->ad_enabled_domains) == 2) {
-+            if (strcasecmp(state->sd_ctx->ad_enabled_domains[0],
-+                           state->be_ctx->domain->name) == 0) {
-+                DEBUG(SSSDBG_TRACE_FUNC,
-+                      "No other enabled domain than master.\n");
-+                goto done;
-+            }
-+        }
-+    }
-+
-     subreq = ad_get_root_domain_send(state, state->ev, forest,
-                                      sdap_id_op_handle(state->sdap_op),
-                                      state->sd_ctx);
--- 
-2.4.11
-
diff --git a/SOURCES/0105-RESPONDER-Fallback-to-global-domain-resolution-order.patch b/SOURCES/0105-RESPONDER-Fallback-to-global-domain-resolution-order.patch
new file mode 100644
index 0000000..e0094c0
--- /dev/null
+++ b/SOURCES/0105-RESPONDER-Fallback-to-global-domain-resolution-order.patch
@@ -0,0 +1,313 @@
+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-AD_PROVIDER-ad_enabled_domains-other-then-master.patch b/SOURCES/0106-AD_PROVIDER-ad_enabled_domains-other-then-master.patch
deleted file mode 100644
index 6946373..0000000
--- a/SOURCES/0106-AD_PROVIDER-ad_enabled_domains-other-then-master.patch
+++ /dev/null
@@ -1,112 +0,0 @@
-From 9438cd7b8c8cca1e919afec6c5aa3a3233a31f8c Mon Sep 17 00:00:00 2001
-From: Petr Cech <pcech@redhat.com>
-Date: Mon, 27 Jun 2016 11:51:30 +0200
-Subject: [PATCH 106/108] AD_PROVIDER: ad_enabled_domains - other then master
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-We can skip looking up other domains if
-option ad_enabled_domains doesn't contain them.
-
-Resolves:
-https://fedorahosted.org/sssd/ticket/2828
-
-Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
-Reviewed-by: Lukáš Slebodník <lslebodn@redhat.com>
----
- src/providers/ad/ad_subdomains.c | 40 +++++++++++++++++++++++++++++++++++++---
- 1 file changed, 37 insertions(+), 3 deletions(-)
-
-diff --git a/src/providers/ad/ad_subdomains.c b/src/providers/ad/ad_subdomains.c
-index 5fdfc63886457db02ea4edc430341b31c3e545ce..52bf5361fa8de02c7165cbc3513a923ec018fc15 100644
---- a/src/providers/ad/ad_subdomains.c
-+++ b/src/providers/ad/ad_subdomains.c
-@@ -130,6 +130,16 @@ done:
-     return ret;
- }
- 
-+static bool is_domain_enabled(const char *domain,
-+                              const char **enabled_doms)
-+{
-+    if (enabled_doms == NULL) {
-+        return true;
-+    }
-+
-+    return string_in_list(domain, discard_const_p(char *, enabled_doms), false);
-+}
-+
- static errno_t
- ad_subdom_ad_ctx_new(struct be_ctx *be_ctx,
-                      struct ad_id_ctx *id_ctx,
-@@ -492,6 +502,7 @@ done:
- 
- static errno_t ad_subdomains_process(TALLOC_CTX *mem_ctx,
-                                      struct sss_domain_info *domain,
-+                                     const char **enabled_domains_list,
-                                      size_t nsd, struct sysdb_attrs **sd,
-                                      struct sysdb_attrs *root,
-                                      size_t *_nsd_out,
-@@ -500,9 +511,10 @@ static errno_t ad_subdomains_process(TALLOC_CTX *mem_ctx,
-     size_t i, sdi;
-     struct sysdb_attrs **sd_out;
-     const char *sd_name;
-+    const char *root_name;
-     errno_t ret;
- 
--    if (root == NULL) {
-+    if (root == NULL && enabled_domains_list == NULL) {
-         /* We are connected directly to the root domain. The 'sd'
-          * list is complete and we can just use it
-          */
-@@ -529,6 +541,13 @@ static errno_t ad_subdomains_process(TALLOC_CTX *mem_ctx,
-             goto fail;
-         }
- 
-+        if (is_domain_enabled(sd_name, enabled_domains_list) == false) {
-+            DEBUG(SSSDBG_TRACE_FUNC, "Disabling subdomain %s\n", sd_name);
-+            continue;
-+        } else {
-+            DEBUG(SSSDBG_TRACE_FUNC, "Enabling subdomain %s\n", sd_name);
-+        }
-+
-         if (strcasecmp(sd_name, domain->name) == 0) {
-             DEBUG(SSSDBG_TRACE_INTERNAL,
-                   "Not including primary domain %s in the subdomain list\n",
-@@ -541,9 +560,23 @@ static errno_t ad_subdomains_process(TALLOC_CTX *mem_ctx,
-     }
- 
-     /* Now include the root */
--    sd_out[sdi] = talloc_steal(sd_out, root);
-+    if (root != NULL) {
-+        ret = sysdb_attrs_get_string(root, AD_AT_TRUST_PARTNER, &root_name);
-+        if (ret != EOK) {
-+            DEBUG(SSSDBG_OP_FAILURE, "sysdb_attrs_get_string failed.\n");
-+            goto fail;
-+        }
- 
--    *_nsd_out = sdi+1;
-+        if (is_domain_enabled(root_name, enabled_domains_list) == true) {
-+            sd_out[sdi] = talloc_steal(sd_out, root);
-+            sdi++;
-+        } else {
-+            DEBUG(SSSDBG_TRACE_FUNC, "Disabling forest root domain %s\n",
-+                                     root_name);
-+        }
-+    }
-+
-+    *_nsd_out = sdi;
-     *_sd_out = sd_out;
-     return EOK;
- 
-@@ -789,6 +822,7 @@ static void ad_get_slave_domain_done(struct tevent_req *subreq)
-      * subdomains.
-      */
-     ret = ad_subdomains_process(state, state->be_ctx->domain,
-+                                state->sd_ctx->ad_enabled_domains,
-                                 reply_count, reply, state->root_attrs,
-                                 &nsubdoms, &subdoms);
-     if (ret != EOK) {
--- 
-2.4.11
-
diff --git a/SOURCES/0106-NSS-TESTS-Improve-non-fqnames-tests.patch b/SOURCES/0106-NSS-TESTS-Improve-non-fqnames-tests.patch
new file mode 100644
index 0000000..fce885a
--- /dev/null
+++ b/SOURCES/0106-NSS-TESTS-Improve-non-fqnames-tests.patch
@@ -0,0 +1,164 @@
+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
new file mode 100644
index 0000000..352e52d
--- /dev/null
+++ b/SOURCES/0107-CACHE_REQ-Allow-configurationless-shortname-lookups.patch
@@ -0,0 +1,139 @@
+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/0107-TESTS-Adding-tests-for-ad_enabled_domains-option.patch b/SOURCES/0107-TESTS-Adding-tests-for-ad_enabled_domains-option.patch
deleted file mode 100644
index 96cddc6..0000000
--- a/SOURCES/0107-TESTS-Adding-tests-for-ad_enabled_domains-option.patch
+++ /dev/null
@@ -1,398 +0,0 @@
-From 70c1809c7ad309d87fba7570cff5c5e9f10d17f1 Mon Sep 17 00:00:00 2001
-From: Petr Cech <pcech@redhat.com>
-Date: Mon, 27 Jun 2016 11:53:19 +0200
-Subject: [PATCH 107/108] TESTS: Adding tests for ad_enabled_domains option
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-There is special logic around ad_enabled_domains option:
- * option is disabled by default
- * master domain is always added to enabled domains
-
-Resolves:
-https://fedorahosted.org/sssd/ticket/2828
-
-Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
-Reviewed-by: Lukáš Slebodník <lslebodn@redhat.com>
----
- Makefile.am                           |  20 +++
- src/tests/cmocka/test_ad_subdomains.c | 328 ++++++++++++++++++++++++++++++++++
- 2 files changed, 348 insertions(+)
- create mode 100644 src/tests/cmocka/test_ad_subdomains.c
-
-diff --git a/Makefile.am b/Makefile.am
-index e2e4c4c08f66ef15684e1b3b1fe17bfae4e4131b..4d90c7a46e2ee0fe652aa392cf647d056e06c7fc 100644
---- a/Makefile.am
-+++ b/Makefile.am
-@@ -257,6 +257,7 @@ if HAVE_CMOCKA
-         test_sbus_opath \
-         test_fo_srv \
-         pam-srv-tests \
-+        test_ad_subdom \
-         test_ipa_subdom_util \
-         test_tools_colondb \
-         test_krb5_wait_queue \
-@@ -2817,6 +2818,25 @@ test_fo_srv_LDADD = \
-     libsss_test_common.la \
-     $(NULL)
- 
-+test_ad_subdom_SOURCES = \
-+    src/tests/cmocka/test_ad_subdomains.c \
-+    $(NULL)
-+test_ad_subdom_CFLAGS = \
-+    $(AM_CFLAGS) \
-+    $(NDR_NBT_CFLAGS) \
-+    $(NULL)
-+test_ad_subdom_LDADD = \
-+    $(CMOCKA_LIBS) \
-+    $(POPT_LIBS) \
-+    $(TALLOC_LIBS) \
-+    $(SSSD_INTERNAL_LTLIBS) \
-+    libsss_ldap_common.la \
-+    libsss_ad_tests.la \
-+    libsss_idmap.la \
-+    libsss_test_common.la \
-+    libdlopen_test_providers.la \
-+    $(NULL)
-+
- test_ipa_subdom_util_SOURCES = \
-     src/tests/cmocka/test_ipa_subdomains_utils.c \
-     src/providers/ipa/ipa_subdomains_utils.c \
-diff --git a/src/tests/cmocka/test_ad_subdomains.c b/src/tests/cmocka/test_ad_subdomains.c
-new file mode 100644
-index 0000000000000000000000000000000000000000..99908b5f8f0b89c2cc391b5496e24a247bfd7448
---- /dev/null
-+++ b/src/tests/cmocka/test_ad_subdomains.c
-@@ -0,0 +1,328 @@
-+/*
-+    Authors:
-+        Petr Čech <pcech@redhat.com>
-+
-+    Copyright (C) 2016 Red Hat
-+
-+    SSSD tests: AD subdomain tests
-+
-+    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 <tevent.h>
-+#include <errno.h>
-+#include <popt.h>
-+#include <arpa/inet.h>
-+#include <netinet/in.h>
-+#include <sys/types.h>
-+#include <stdarg.h>
-+#include <stdlib.h>
-+
-+#include "tests/cmocka/common_mock.h"
-+#include "tests/cmocka/common_mock_resp.h"
-+#include "providers/ad/ad_common.h"
-+
-+#include "providers/ad/ad_subdomains.c"
-+#include "providers/ad/ad_opts.c"
-+
-+#define AD_DOMAIN "ad_domain.domain.test"
-+#define DOMAIN_1 "one.domain.test"
-+#define DOMAIN_2 "two.domain.test"
-+
-+struct test_ad_subdom_ctx {
-+    struct ad_id_ctx *ad_id_ctx;
-+};
-+
-+static struct ad_id_ctx *
-+test_ad_subdom_init_ad_id_ctx(TALLOC_CTX *mem_ctx)
-+{
-+    struct ad_id_ctx *ad_id_ctx;
-+    struct ad_options *ad_options;
-+    errno_t ret;
-+
-+    ad_id_ctx = talloc_zero(mem_ctx, struct ad_id_ctx);
-+    assert_non_null(ad_id_ctx);
-+
-+    ad_options = talloc_zero(ad_id_ctx, struct ad_options);
-+    assert_non_null(ad_options);
-+
-+    ret = dp_copy_defaults(ad_options,
-+                           ad_basic_opts,
-+                           AD_OPTS_BASIC,
-+                           &ad_options->basic);
-+    assert_int_equal(ret, EOK);
-+
-+    ad_id_ctx->ad_options = ad_options;
-+
-+    return ad_id_ctx;
-+}
-+
-+static int test_ad_subdom_setup(void **state)
-+{
-+    struct test_ad_subdom_ctx *test_ctx;
-+
-+    assert_true(leak_check_setup());
-+
-+    test_ctx = talloc_zero(global_talloc_context, struct test_ad_subdom_ctx);
-+    assert_non_null(test_ctx);
-+
-+    test_ctx->ad_id_ctx = NULL;
-+
-+    check_leaks_push(test_ctx);
-+    *state = test_ctx;
-+    return 0;
-+}
-+
-+static int test_ad_subdom_teardown(void **state)
-+{
-+    struct test_ad_subdom_ctx *test_ctx;
-+
-+    test_ctx = talloc_get_type(*state, struct test_ad_subdom_ctx);
-+    assert_non_null(test_ctx);
-+
-+    assert_true(check_leaks_pop(test_ctx) == true);
-+    talloc_free(test_ctx);
-+    assert_true(leak_check_teardown());
-+    return 0;
-+}
-+
-+static void test_ad_subdom_default(void **state)
-+{
-+    struct test_ad_subdom_ctx *test_ctx;
-+    const char **ad_enabled_domains = NULL;
-+    errno_t ret;
-+
-+    test_ctx = talloc_get_type(*state, struct test_ad_subdom_ctx);
-+    test_ctx->ad_id_ctx = test_ad_subdom_init_ad_id_ctx(test_ctx);
-+    assert_non_null(test_ctx->ad_id_ctx);
-+
-+    ret = ad_get_enabled_domains(test_ctx, test_ctx->ad_id_ctx,
-+                                 AD_DOMAIN,
-+                                 &ad_enabled_domains);
-+    assert_int_equal(ret, EOK);
-+    assert_null(ad_enabled_domains);
-+
-+    talloc_zfree(test_ctx->ad_id_ctx);
-+}
-+
-+static void test_ad_subdom_add_one(void **state)
-+{
-+    struct test_ad_subdom_ctx *test_ctx;
-+    const char **ad_enabled_domains = NULL;
-+    int enabled_domains_count;
-+    int domain_count = 2;
-+    const char *domains[domain_count];
-+    errno_t ret;
-+
-+    test_ctx = talloc_get_type(*state, struct test_ad_subdom_ctx);
-+    test_ctx->ad_id_ctx = test_ad_subdom_init_ad_id_ctx(test_ctx);
-+    assert_non_null(test_ctx->ad_id_ctx);
-+
-+    ret = dp_opt_set_string(test_ctx->ad_id_ctx->ad_options->basic,
-+                            AD_ENABLED_DOMAINS, DOMAIN_1);
-+    assert_int_equal(ret, EOK);
-+
-+    ret = ad_get_enabled_domains(test_ctx, test_ctx->ad_id_ctx,
-+                                 AD_DOMAIN,
-+                                 &ad_enabled_domains);
-+    assert_int_equal(ret, EOK);
-+    assert_non_null(ad_enabled_domains);
-+
-+    for (enabled_domains_count = 0;
-+         ad_enabled_domains[enabled_domains_count] != NULL;
-+         enabled_domains_count++) {
-+    }
-+    assert_int_equal(domain_count, enabled_domains_count);
-+
-+    domains[0] = AD_DOMAIN;
-+    domains[1] = DOMAIN_1;
-+    assert_true(are_values_in_array(domains, domain_count,
-+                                    ad_enabled_domains, enabled_domains_count));
-+
-+    talloc_zfree(test_ctx->ad_id_ctx);
-+    talloc_zfree(ad_enabled_domains);
-+}
-+
-+static void test_ad_subdom_add_two(void **state)
-+{
-+    struct test_ad_subdom_ctx *test_ctx;
-+    const char **ad_enabled_domains = NULL;
-+    int enabled_domains_count;
-+    int domain_count = 3;
-+    const char *domains[domain_count];
-+    errno_t ret;
-+
-+    test_ctx = talloc_get_type(*state, struct test_ad_subdom_ctx);
-+    test_ctx->ad_id_ctx = test_ad_subdom_init_ad_id_ctx(test_ctx);
-+    assert_non_null(test_ctx->ad_id_ctx);
-+
-+    ret = dp_opt_set_string(test_ctx->ad_id_ctx->ad_options->basic,
-+                            AD_ENABLED_DOMAINS, DOMAIN_1","DOMAIN_2);
-+    assert_int_equal(ret, EOK);
-+
-+    ret = ad_get_enabled_domains(test_ctx, test_ctx->ad_id_ctx,
-+                                 AD_DOMAIN,
-+                                 &ad_enabled_domains);
-+    assert_int_equal(ret, EOK);
-+    assert_non_null(ad_enabled_domains);
-+
-+    for (enabled_domains_count = 0;
-+         ad_enabled_domains[enabled_domains_count] != NULL;
-+         enabled_domains_count++) {
-+    }
-+    assert_int_equal(domain_count, enabled_domains_count);
-+
-+    domains[0] = AD_DOMAIN;
-+    domains[1] = DOMAIN_1;
-+    domains[2] = DOMAIN_2;
-+    assert_true(are_values_in_array(domains, domain_count,
-+                                    ad_enabled_domains, enabled_domains_count));
-+
-+    talloc_zfree(test_ctx->ad_id_ctx);
-+    talloc_zfree(ad_enabled_domains);
-+}
-+
-+static void test_ad_subdom_add_master(void **state)
-+{
-+    struct test_ad_subdom_ctx *test_ctx;
-+    const char **ad_enabled_domains = NULL;
-+    int enabled_domains_count;
-+    int domain_count = 1;
-+    const char *domains[domain_count];
-+    errno_t ret;
-+
-+    test_ctx = talloc_get_type(*state, struct test_ad_subdom_ctx);
-+    test_ctx->ad_id_ctx = test_ad_subdom_init_ad_id_ctx(test_ctx);
-+    assert_non_null(test_ctx->ad_id_ctx);
-+
-+    ret = dp_opt_set_string(test_ctx->ad_id_ctx->ad_options->basic,
-+                            AD_ENABLED_DOMAINS, AD_DOMAIN);
-+    assert_int_equal(ret, EOK);
-+
-+    ret = ad_get_enabled_domains(test_ctx, test_ctx->ad_id_ctx,
-+                                 AD_DOMAIN,
-+                                 &ad_enabled_domains);
-+    assert_int_equal(ret, EOK);
-+    assert_non_null(ad_enabled_domains);
-+
-+    for (enabled_domains_count = 0;
-+         ad_enabled_domains[enabled_domains_count] != NULL;
-+         enabled_domains_count++) {
-+    }
-+    assert_int_equal(domain_count, enabled_domains_count);
-+
-+    domains[0] = AD_DOMAIN;
-+    assert_true(are_values_in_array(domains, domain_count,
-+                                    ad_enabled_domains, enabled_domains_count));
-+
-+    talloc_zfree(test_ctx->ad_id_ctx);
-+    talloc_zfree(ad_enabled_domains);
-+}
-+
-+static void test_ad_subdom_add_two_with_master(void **state)
-+{
-+    struct test_ad_subdom_ctx *test_ctx;
-+    const char **ad_enabled_domains = NULL;
-+    int enabled_domains_count;
-+    int domain_count = 3;
-+    const char *domains[domain_count];
-+    errno_t ret;
-+
-+    test_ctx = talloc_get_type(*state, struct test_ad_subdom_ctx);
-+    test_ctx->ad_id_ctx = test_ad_subdom_init_ad_id_ctx(test_ctx);
-+    assert_non_null(test_ctx->ad_id_ctx);
-+
-+    ret = dp_opt_set_string(test_ctx->ad_id_ctx->ad_options->basic,
-+                            AD_ENABLED_DOMAINS,
-+                            DOMAIN_1","AD_DOMAIN","DOMAIN_2);
-+    assert_int_equal(ret, EOK);
-+
-+    ret = ad_get_enabled_domains(test_ctx, test_ctx->ad_id_ctx,
-+                                 AD_DOMAIN,
-+                                 &ad_enabled_domains);
-+    assert_int_equal(ret, EOK);
-+    assert_non_null(ad_enabled_domains);
-+
-+    for (enabled_domains_count = 0;
-+         ad_enabled_domains[enabled_domains_count] != NULL;
-+         enabled_domains_count++) {
-+    }
-+    assert_int_equal(domain_count, enabled_domains_count);
-+
-+    domains[0] = AD_DOMAIN;
-+    domains[1] = DOMAIN_1;
-+    domains[2] = DOMAIN_2;
-+    assert_true(are_values_in_array(domains, domain_count,
-+                                    ad_enabled_domains, enabled_domains_count));
-+
-+    talloc_zfree(test_ctx->ad_id_ctx);
-+    talloc_zfree(ad_enabled_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_setup_teardown(test_ad_subdom_default,
-+                                        test_ad_subdom_setup,
-+                                        test_ad_subdom_teardown),
-+        cmocka_unit_test_setup_teardown(test_ad_subdom_add_one,
-+                                        test_ad_subdom_setup,
-+                                        test_ad_subdom_teardown),
-+        cmocka_unit_test_setup_teardown(test_ad_subdom_add_two,
-+                                        test_ad_subdom_setup,
-+                                        test_ad_subdom_teardown),
-+        cmocka_unit_test_setup_teardown(test_ad_subdom_add_master,
-+                                        test_ad_subdom_setup,
-+                                        test_ad_subdom_teardown),
-+        cmocka_unit_test_setup_teardown(test_ad_subdom_add_two_with_master,
-+                                        test_ad_subdom_setup,
-+                                        test_ad_subdom_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);
-+
-+    /* 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.4.11
-
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
new file mode 100644
index 0000000..86ba24f
--- /dev/null
+++ b/SOURCES/0108-CACHE_REQ_DOMAIN-Add-some-comments-to-cache_req_doma.patch
@@ -0,0 +1,42 @@
+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
new file mode 100644
index 0000000..a1abb10
--- /dev/null
+++ b/SOURCES/0109-RESPONDER_COMMON-Improve-domaiN_resolution_order-deb.patch
@@ -0,0 +1,54 @@
+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/0109-UTIL-Use-sss_atomic_read_s-in-generate_csprng_buffer.patch b/SOURCES/0109-UTIL-Use-sss_atomic_read_s-in-generate_csprng_buffer.patch
deleted file mode 100644
index 0734ff1..0000000
--- a/SOURCES/0109-UTIL-Use-sss_atomic_read_s-in-generate_csprng_buffer.patch
+++ /dev/null
@@ -1,94 +0,0 @@
-From 60596973b503637c742b597aeb862eecae9f9c91 Mon Sep 17 00:00:00 2001
-From: Jakub Hrozek <jhrozek@redhat.com>
-Date: Mon, 8 Aug 2016 14:07:04 +0200
-Subject: [PATCH 109/111] UTIL: Use sss_atomic_read_s in generate_csprng_buffer
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-There was a bug in generate_csprng_buffer() where if we read the exact
-amount of bytes from /dev/urandom, we would always return EIO. Instead,
-let's reuse the existing code from sss_atomic_read_s() which fixes this
-bug and reduces code duplication.
-
-Reviewed-by: Lukáš Slebodník <lslebodn@redhat.com>
-Reviewed-by: Fabiano Fidêncio <fabiano@fidencio.org>
----
- Makefile.am                  |  2 ++
- src/util/crypto/sss_crypto.c | 29 +++++------------------------
- 2 files changed, 7 insertions(+), 24 deletions(-)
-
-diff --git a/Makefile.am b/Makefile.am
-index 4d90c7a46e2ee0fe652aa392cf647d056e06c7fc..a32a1e37c85e2370fa006ee73b730145f03c3fc1 100644
---- a/Makefile.am
-+++ b/Makefile.am
-@@ -815,6 +815,7 @@ if HAVE_NSS
-                         src/util/crypto/nss/nss_nite.c \
-                         src/util/crypto/nss/nss_util.c \
- 			src/util/crypto/sss_crypto.c \
-+			src/util/atomic_io.c \
- 			$(NULL)
-     SSS_CRYPT_CFLAGS = $(NSS_CFLAGS)
-     SSS_CRYPT_LIBS = $(NSS_LIBS)
-@@ -836,6 +837,7 @@ else
-                         src/util/crypto/libcrypto/crypto_obfuscate.c \
-                         src/util/crypto/libcrypto/crypto_nite.c \
- 			src/util/crypto/sss_crypto.c \
-+			src/util/atomic_io.c \
- 			$(NULL)
-     SSS_CRYPT_CFLAGS = $(CRYPTO_CFLAGS)
-     SSS_CRYPT_LIBS = $(CRYPTO_LIBS)
-diff --git a/src/util/crypto/sss_crypto.c b/src/util/crypto/sss_crypto.c
-index 4c775f3d926ae32f3cb72b1329c0a025a0550ed5..ac90bac07c7006a2950331b86bcc412207a3e401 100644
---- a/src/util/crypto/sss_crypto.c
-+++ b/src/util/crypto/sss_crypto.c
-@@ -25,41 +25,22 @@
- int generate_csprng_buffer(uint8_t *buf, size_t size)
- {
-     ssize_t rsize;
--    ssize_t pos;
-     int ret;
-     int fd;
- 
-     fd = open("/dev/urandom", O_RDONLY);
-     if (fd == -1) return errno;
- 
--    rsize = 0;
--    pos = 0;
--    while (rsize < size) {
--        rsize = read(fd, buf + pos, size - pos);
--        switch (rsize) {
--        case -1:
--            if (errno == EINTR) continue;
--            ret = EIO;
--            goto done;
--        case 0:
--            ret = EIO;
--            goto done;
--        default:
--            if (rsize + pos < size - pos) {
--                pos += rsize;
--                continue;
--            }
--            ret = EIO;
--            goto done;
--        }
--    }
--    if (rsize != size) {
-+    rsize = sss_atomic_read_s(fd, buf, size);
-+    if (rsize == -1) {
-+        ret = errno;
-+        goto done;
-+    } else if (rsize != size) {
-         ret = EFAULT;
-         goto done;
-     }
- 
-     ret = EOK;
--
- done:
-     close(fd);
-     return ret;
--- 
-2.4.11
-
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
new file mode 100644
index 0000000..8507ad0
--- /dev/null
+++ b/SOURCES/0110-CACHE_REQ_DOMAIN-debug-the-set-domain-resolution-ord.patch
@@ -0,0 +1,50 @@
+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/0110-SECRETS-Use-sss_atomic_read-write-for-better-readabi.patch b/SOURCES/0110-SECRETS-Use-sss_atomic_read-write-for-better-readabi.patch
deleted file mode 100644
index 1c66e09..0000000
--- a/SOURCES/0110-SECRETS-Use-sss_atomic_read-write-for-better-readabi.patch
+++ /dev/null
@@ -1,46 +0,0 @@
-From eb6a90621a53424e4d0a5534eca303b432509433 Mon Sep 17 00:00:00 2001
-From: Jakub Hrozek <jhrozek@redhat.com>
-Date: Mon, 8 Aug 2016 13:50:54 +0200
-Subject: [PATCH 110/111] SECRETS: Use sss_atomic_read/write for better
- readability
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-sss_atomic_read_s and sss_atomic_write_s are macro-wrappers around
-sss_atomic_io_s but it's easier to follow the code with the read/write
-vairants used directly.
-
-Reviewed-by: Lukáš Slebodník <lslebodn@redhat.com>
-Reviewed-by: Fabiano Fidêncio <fabiano@fidencio.org>
----
- src/responder/secrets/local.c | 6 +++---
- 1 file changed, 3 insertions(+), 3 deletions(-)
-
-diff --git a/src/responder/secrets/local.c b/src/responder/secrets/local.c
-index 470aec0e195a54dd2af2b929ff1b7a304331a214..17469249b357cbdc5e50ddff6b563fdf2f377577 100644
---- a/src/responder/secrets/local.c
-+++ b/src/responder/secrets/local.c
-@@ -621,7 +621,7 @@ int generate_master_key(const char *filename, size_t size)
-     fd = open(filename, O_CREAT|O_EXCL|O_WRONLY, 0600);
-     if (fd == -1) return errno;
- 
--    rsize = sss_atomic_io_s(fd, buf, size, false);
-+    rsize = sss_atomic_write_s(fd, buf, size);
-     close(fd);
-     if (rsize != size) {
-         ret = unlink(filename);
-@@ -681,8 +681,8 @@ int local_secrets_provider_handle(struct sec_ctx *sctx,
-     }
-     if (ret) return EFAULT;
- 
--    size = sss_atomic_io_s(mfd, lctx->master_key.data,
--                           lctx->master_key.length, true);
-+    size = sss_atomic_read_s(mfd, lctx->master_key.data,
-+                             lctx->master_key.length);
-     close(mfd);
-     if (size < 0 || size != lctx->master_key.length) return EIO;
- 
--- 
-2.4.11
-
diff --git a/SOURCES/0111-BUILD-Ship-systemd-service-file-for-sssd-secrets.patch b/SOURCES/0111-BUILD-Ship-systemd-service-file-for-sssd-secrets.patch
deleted file mode 100644
index 3b6ae74..0000000
--- a/SOURCES/0111-BUILD-Ship-systemd-service-file-for-sssd-secrets.patch
+++ /dev/null
@@ -1,154 +0,0 @@
-From d0b2cd8d161e7fc6e6c96f51342c88e6572eb1da Mon Sep 17 00:00:00 2001
-From: Jakub Hrozek <jhrozek@redhat.com>
-Date: Mon, 15 Aug 2016 14:10:23 +0200
-Subject: [PATCH 111/111] BUILD: Ship systemd service file for sssd-secrets
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Adds two new files: sssd-secrets.socket and sssd-secrets.service. These
-can be used to socket-acticate the secrets responder even without
-explicitly starting it in the sssd config file.
-
-The specfile activates the socket after installation which means that
-the admin would just be able to use the secrets socket and the
-sssd_secrets responder would be started automatically by systemd.
-
-The sssd-secrets responder is started as root, mostly because I didn't
-think of an easy way to pass the uid/gid to the responders without
-asking about the sssd user identity in the first place. But nonetheless,
-the sssd-secrets responder wasn't tested as non-root and at least the
-initialization should be performed as root for the time being.
-
-Reviewed-by: Fabiano Fidêncio <fabiano@fidencio.org>
-Reviewed-by: Lukáš Slebodník <lslebodn@redhat.com>
----
- Makefile.am                              | 21 +++++++++++++++++++--
- contrib/sssd.spec.in                     |  6 ++++++
- src/sysv/systemd/sssd-secrets.service.in |  8 ++++++++
- src/sysv/systemd/sssd-secrets.socket.in  |  8 ++++++++
- 4 files changed, 41 insertions(+), 2 deletions(-)
- create mode 100644 src/sysv/systemd/sssd-secrets.service.in
- create mode 100644 src/sysv/systemd/sssd-secrets.socket.in
-
-diff --git a/Makefile.am b/Makefile.am
-index a32a1e37c85e2370fa006ee73b730145f03c3fc1..6ab4399d5b68644668198bc9b0e3056562a4e51a 100644
---- a/Makefile.am
-+++ b/Makefile.am
-@@ -3888,7 +3888,10 @@ systemdunit_DATA =
- systemdconf_DATA =
- if HAVE_SYSTEMD_UNIT
-     systemdunit_DATA += \
--        src/sysv/systemd/sssd.service
-+        src/sysv/systemd/sssd.service \
-+        src/sysv/systemd/sssd-secrets.socket \
-+        src/sysv/systemd/sssd-secrets.service \
-+        $(NULL)
- if WITH_JOURNALD
-     systemdconf_DATA += \
-         src/sysv/systemd/journal.conf
-@@ -3926,6 +3929,7 @@ edit_cmd = $(SED) \
-         -e 's|@sbindir[@]|$(sbindir)|g' \
-         -e 's|@environment_file[@]|$(environment_file)|g' \
-         -e 's|@localstatedir[@]|$(localstatedir)|g' \
-+        -e 's|@libexecdir[@]|$(libexecdir)|g' \
-         -e 's|@prefix[@]|$(prefix)|g'
- 
- replace_script = \
-@@ -3937,7 +3941,10 @@ replace_script = \
- 
- EXTRA_DIST += \
-     src/sysv/systemd/sssd.service.in \
--    src/sysv/systemd/journal.conf.in
-+    src/sysv/systemd/journal.conf.in \
-+    src/sysv/systemd/sssd-secrets.socket.in \
-+    src/sysv/systemd/sssd-secrets.service.in \
-+    $(NULL)
- 
- src/sysv/systemd/sssd.service: src/sysv/systemd/sssd.service.in Makefile
- 	@$(MKDIR_P) src/sysv/systemd/
-@@ -3947,6 +3954,14 @@ src/sysv/systemd/journal.conf: src/sysv/systemd/journal.conf.in Makefile
- 	@$(MKDIR_P) src/sysv/systemd/
- 	$(replace_script)
- 
-+src/sysv/systemd/sssd-secrets.socket: src/sysv/systemd/sssd-secrets.socket.in Makefile
-+	@$(MKDIR_P) src/sysv/systemd/
-+	$(replace_script)
-+
-+src/sysv/systemd/sssd-secrets.service: src/sysv/systemd/sssd-secrets.service.in Makefile
-+	@$(MKDIR_P) src/sysv/systemd/
-+	$(replace_script)
-+
- SSSD_USER_DIRS = \
-     $(DESTDIR)$(dbpath) \
-     $(DESTDIR)$(keytabdir) \
-@@ -4162,6 +4177,8 @@ endif
- 	done;
- 	rm -Rf ldb_mod_test_dir
- 	rm -f $(builddir)/src/sysv/systemd/sssd.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/journal.conf
- 
- CLEANFILES += *.X */*.X */*/*.X
-diff --git a/contrib/sssd.spec.in b/contrib/sssd.spec.in
-index 14f0cb27ac8f1acc3aa0786da576be33b727e024..f1ff16176cb8ca974b98948958cfa1e9290b0bca 100644
---- a/contrib/sssd.spec.in
-+++ b/contrib/sssd.spec.in
-@@ -737,6 +737,8 @@ done
- %{_sbindir}/sssd
- %if (0%{?use_systemd} == 1)
- %{_unitdir}/sssd.service
-+%{_unitdir}/sssd-secrets.socket
-+%{_unitdir}/sssd-secrets.service
- %else
- %{_initrddir}/%{name}
- %endif
-@@ -1069,12 +1071,16 @@ getent passwd sssd >/dev/null || useradd -r -g sssd -d / -s /sbin/nologin -c "Us
- # systemd
- %post common
- %systemd_post sssd.service
-+%systemd_post sssd-secrets.socket
- 
- %preun common
- %systemd_preun sssd.service
-+%systemd_preun sssd-secrets.socket
- 
- %postun common
- %systemd_postun_with_restart sssd.service
-+%systemd_postun_with_restart sssd-secrets.socket
-+%systemd_postun_with_restart sssd-secrets.service
- 
- %else
- # sysv
-diff --git a/src/sysv/systemd/sssd-secrets.service.in b/src/sysv/systemd/sssd-secrets.service.in
-new file mode 100644
-index 0000000000000000000000000000000000000000..119c9bb4b37b672159db707aa11a6d11215f29bf
---- /dev/null
-+++ b/src/sysv/systemd/sssd-secrets.service.in
-@@ -0,0 +1,8 @@
-+[Unit]
-+Description=SSSD Secrets Service responder
-+
-+[Install]
-+Also=sssd-secrets.socket
-+
-+[Service]
-+ExecStart=@libexecdir@/sssd/sssd_secrets --uid 0 --gid 0 --debug-to-files
-diff --git a/src/sysv/systemd/sssd-secrets.socket.in b/src/sysv/systemd/sssd-secrets.socket.in
-new file mode 100644
-index 0000000000000000000000000000000000000000..682e8f6e0fa58092a90259523f9f2f59e0131435
---- /dev/null
-+++ b/src/sysv/systemd/sssd-secrets.socket.in
-@@ -0,0 +1,8 @@
-+[Unit]
-+Description=SSSD Secrets Service responder socket
-+
-+[Socket]
-+ListenStream=@localstatedir@/run/secrets.socket
-+
-+[Install]
-+WantedBy=sockets.target
--- 
-2.4.11
-
diff --git a/SOURCES/0111-SECRETS-remove-unused-variable.patch b/SOURCES/0111-SECRETS-remove-unused-variable.patch
new file mode 100644
index 0000000..1e1d87c
--- /dev/null
+++ b/SOURCES/0111-SECRETS-remove-unused-variable.patch
@@ -0,0 +1,33 @@
+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-DP-Add-log-message-for-get-account-info.patch b/SOURCES/0112-DP-Add-log-message-for-get-account-info.patch
deleted file mode 100644
index a2fa19c..0000000
--- a/SOURCES/0112-DP-Add-log-message-for-get-account-info.patch
+++ /dev/null
@@ -1,33 +0,0 @@
-From b05afb6811e42d3297e884b0664884aa47af923f Mon Sep 17 00:00:00 2001
-From: Lukas Slebodnik <lslebodn@redhat.com>
-Date: Tue, 16 Aug 2016 08:49:57 +0200
-Subject: [PATCH 112/115] DP: Add log message for get account info
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Reviewed-by: Petr Čech <pcech@redhat.com>
-(cherry picked from commit 806f65f3c90dc0f7921932494228ad93f3ed3027)
----
- src/providers/data_provider/dp_target_id.c | 5 +++++
- 1 file changed, 5 insertions(+)
-
-diff --git a/src/providers/data_provider/dp_target_id.c b/src/providers/data_provider/dp_target_id.c
-index 938651545ea995091d0aaf29da12bbb8110c9add..8b126d70e59cb33b6de8b678f5b4c1a15b391329 100644
---- a/src/providers/data_provider/dp_target_id.c
-+++ b/src/providers/data_provider/dp_target_id.c
-@@ -281,6 +281,11 @@ errno_t dp_get_account_info_handler(struct sbus_request *sbus_req,
-         goto done;
-     }
- 
-+    DEBUG(SSSDBG_FUNC_DATA,
-+          "Got request for [%#"PRIx32"][%s][%"PRId32"][%s]\n",
-+          data->entry_type, be_req2str(data->entry_type),
-+          attr_type, filter);
-+
-     key = talloc_asprintf(data, "%u:%u:%s:%s:%s", data->entry_type,
-                           data->attr_type, extra, domain, filter);
-     if (key == NULL) {
--- 
-2.4.11
-
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
new file mode 100644
index 0000000..77c2a5b
--- /dev/null
+++ b/SOURCES/0112-IPA-Improve-DEBUG-message-if-a-group-has-no-ipaNTSec.patch
@@ -0,0 +1,42 @@
+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
new file mode 100644
index 0000000..a2ccf0b
--- /dev/null
+++ b/SOURCES/0113-IPA-Improve-s2n-debug-message-for-missing-ipaNTSecur.patch
@@ -0,0 +1,45 @@
+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/0113-LDAP-Log-autofs-rfc2307-config-changes-only-with-ena.patch b/SOURCES/0113-LDAP-Log-autofs-rfc2307-config-changes-only-with-ena.patch
deleted file mode 100644
index 678ec4e..0000000
--- a/SOURCES/0113-LDAP-Log-autofs-rfc2307-config-changes-only-with-ena.patch
+++ /dev/null
@@ -1,65 +0,0 @@
-From b415b9d2b6d016928a2bbcaa710cdc876e4ecc9c Mon Sep 17 00:00:00 2001
-From: Lukas Slebodnik <lslebodn@redhat.com>
-Date: Tue, 16 Aug 2016 13:32:06 +0200
-Subject: [PATCH 113/115] LDAP: Log autofs rfc2307 config changes only with
- enabled responder
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-grep -nE "0x0040" /var/log/sssd/sssd_example.com.log
-361:(Tue Aug 16 13:04:04 2016) [sssd[be[example.com]]]
-  [ldap_get_autofs_options] (0x0040): Your configuration uses the autofs
-  provider with schema set to rfc2307 and default attribute mappings.
-  The default map has changed in this release, please make sure
-  the configuration matches the server attributes.
-
-Reviewed-by: Petr Čech <pcech@redhat.com>
----
- src/providers/ldap/ldap_options.c | 23 ++++++++++++++++++++++-
- 1 file changed, 22 insertions(+), 1 deletion(-)
-
-diff --git a/src/providers/ldap/ldap_options.c b/src/providers/ldap/ldap_options.c
-index 018f6c31fb6360952308e44979581790b8477dc3..15a2609f07506b6dd442b180651a7e25461976c0 100644
---- a/src/providers/ldap/ldap_options.c
-+++ b/src/providers/ldap/ldap_options.c
-@@ -444,6 +444,10 @@ static bool has_defaults(struct confdb_ctx *cdb,
- static bool ldap_rfc2307_autofs_defaults(struct confdb_ctx *cdb,
-                                          const char *conf_path)
- {
-+    char **services = NULL;
-+    errno_t ret;
-+    bool has_autofs_defaults = false;
-+
-     const char *attrs[] = {
-         rfc2307_autofs_entry_map[SDAP_OC_AUTOFS_ENTRY].opt_name,
-         /* SDAP_AT_AUTOFS_ENTRY_KEY missing on purpose, its value was
-@@ -455,7 +459,24 @@ static bool ldap_rfc2307_autofs_defaults(struct confdb_ctx *cdb,
-         NULL,
-     };
- 
--    return has_defaults(cdb, conf_path, attrs);
-+    ret = confdb_get_string_as_list(cdb, cdb,
-+                                    CONFDB_MONITOR_CONF_ENTRY,
-+                                    CONFDB_MONITOR_ACTIVE_SERVICES, &services);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_FATAL_FAILURE, "Unable to read from confdb [%d]: %s\n",
-+              ret, sss_strerror(ret));
-+        goto done;
-+    }
-+
-+    if (string_in_list("autofs", services, true) == false) {
-+        goto done;
-+    }
-+
-+    has_autofs_defaults = has_defaults(cdb, conf_path, attrs);
-+done:
-+    talloc_free(services);
-+
-+    return has_autofs_defaults;
- }
- 
- int ldap_get_autofs_options(TALLOC_CTX *memctx,
--- 
-2.4.11
-
diff --git a/SOURCES/0114-CONFDB-Fix-standalone-application-domains.patch b/SOURCES/0114-CONFDB-Fix-standalone-application-domains.patch
new file mode 100644
index 0000000..9d2968e
--- /dev/null
+++ b/SOURCES/0114-CONFDB-Fix-standalone-application-domains.patch
@@ -0,0 +1,128 @@
+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/0114-SSSCTL-More-helpful-error-message-when-InfoPipe-is-d.patch b/SOURCES/0114-SSSCTL-More-helpful-error-message-when-InfoPipe-is-d.patch
deleted file mode 100644
index f59feff..0000000
--- a/SOURCES/0114-SSSCTL-More-helpful-error-message-when-InfoPipe-is-d.patch
+++ /dev/null
@@ -1,36 +0,0 @@
-From 2b8b5e03319ab72d2c74d61ec24038c5ca412661 Mon Sep 17 00:00:00 2001
-From: Justin Stephenson <jstephen@redhat.com>
-Date: Fri, 12 Aug 2016 12:12:57 -0400
-Subject: [PATCH 114/115] SSSCTL: More helpful error message when InfoPipe is
- disabled
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Resolves:
-https://fedorahosted.org/sssd/ticket/3130
-
-Reviewed-by: Petr Čech <pcech@redhat.com>
-Reviewed-by: Pavel Březina <pbrezina@redhat.com>
----
- src/tools/sssctl/sssctl_sifp.c | 4 +++-
- 1 file changed, 3 insertions(+), 1 deletion(-)
-
-diff --git a/src/tools/sssctl/sssctl_sifp.c b/src/tools/sssctl/sssctl_sifp.c
-index 782a72d7ce8bbf1080c6d6ac988ffac2f432955f..c53119b4e199852d1d233c79a0ad3c0bc4de7470 100644
---- a/src/tools/sssctl/sssctl_sifp.c
-+++ b/src/tools/sssctl/sssctl_sifp.c
-@@ -26,7 +26,9 @@
- #include "tools/sssctl/sssctl.h"
- 
- #define ERR_SSSD _("Check that SSSD is running and " \
--                   "the InfoPipe responder is enabled.\n")
-+                   "the InfoPipe responder is enabled. " \
-+                   "Make sure 'ifp' is listed in the " \
-+                   "'services' option in sssd.conf.\n")
- 
- struct sssctl_sifp_data {
-     sss_sifp_ctx *sifp;
--- 
-2.4.11
-
diff --git a/SOURCES/0115-sdap-Skip-exact-duplicates-when-extending-maps.patch b/SOURCES/0115-sdap-Skip-exact-duplicates-when-extending-maps.patch
deleted file mode 100644
index cfd3300..0000000
--- a/SOURCES/0115-sdap-Skip-exact-duplicates-when-extending-maps.patch
+++ /dev/null
@@ -1,111 +0,0 @@
-From c4379aa97754b4c4cfc02663315b7c6319e3fa61 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Michal=20=C5=BDidek?= <mzidek@redhat.com>
-Date: Wed, 10 Aug 2016 15:41:34 +0200
-Subject: [PATCH 115/115] sdap: Skip exact duplicates when extending maps
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-When extending map with entry that already
-exists in the map in the exacty same form,
-then there is no need to fail.
-
-We should only fail if we try to
-change purpose of already used sysdb
-attribute.
-
-Resolves:
-https://fedorahosted.org/sssd/ticket/3120
-
-Signed-off-by: Lukas Slebodnik <lslebodn@redhat.com>
-
-Reviewed-by: Lukáš Slebodník <lslebodn@redhat.com>
-Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
----
- src/providers/ldap/sdap.c | 42 ++++++++++++++++++++++++++++++++----------
- 1 file changed, 32 insertions(+), 10 deletions(-)
-
-diff --git a/src/providers/ldap/sdap.c b/src/providers/ldap/sdap.c
-index 97b8f126d4ed6bc59c510d5763789a458bd4863a..dc7d5e0caf223c3ee3c43054aa44e796f1b37766 100644
---- a/src/providers/ldap/sdap.c
-+++ b/src/providers/ldap/sdap.c
-@@ -122,19 +122,30 @@ static errno_t split_extra_attr(TALLOC_CTX *mem_ctx,
-     return EOK;
- }
- 
--static bool is_sysdb_duplicate(struct sdap_attr_map *map,
--                               int num_entries,
--                               const char *sysdb_attr)
-+enum duplicate_t {
-+    NOT_FOUND = 0,
-+    ALREADY_IN_MAP, /* nothing to add */
-+    CONFLICT_WITH_MAP /* attempt to redefine attribute */
-+};
-+
-+static enum duplicate_t check_duplicate(struct sdap_attr_map *map,
-+                                        int num_entries,
-+                                        const char *sysdb_attr,
-+                                        const char *ldap_attr)
- {
-     int i;
- 
-     for (i = 0; i < num_entries; i++) {
-         if (strcmp(map[i].sys_name, sysdb_attr) == 0) {
--            return true;
-+            if (strcmp(map[i].name, ldap_attr) == 0) {
-+                return ALREADY_IN_MAP;
-+            } else {
-+                return CONFLICT_WITH_MAP;
-+            }
-         }
-     }
- 
--    return false;
-+    return NOT_FOUND;
- }
- 
- int sdap_extend_map(TALLOC_CTX *memctx,
-@@ -167,14 +178,20 @@ int sdap_extend_map(TALLOC_CTX *memctx,
-         return ENOMEM;
-     }
- 
--    for (i = 0; extra_attrs[i]; i++) {
--        ret = split_extra_attr(map, extra_attrs[i], &sysdb_attr, &ldap_attr);
-+    for (i = 0; *extra_attrs != NULL; extra_attrs++) {
-+        ret = split_extra_attr(map, *extra_attrs, &sysdb_attr, &ldap_attr);
-         if (ret != EOK) {
--            DEBUG(SSSDBG_MINOR_FAILURE, "Cannot split %s\n", extra_attrs[i]);
-+            DEBUG(SSSDBG_MINOR_FAILURE, "Cannot split %s\n", *extra_attrs);
-             continue;
-         }
- 
--        if (is_sysdb_duplicate(map, num_entries, sysdb_attr)) {
-+        ret = check_duplicate(map, num_entries, sysdb_attr, ldap_attr);
-+        if (ret == ALREADY_IN_MAP) {
-+            DEBUG(SSSDBG_TRACE_FUNC,
-+                  "Attribute %s (%s in LDAP) is already in map.\n",
-+                  sysdb_attr, ldap_attr);
-+            continue;
-+        } else if (ret == CONFLICT_WITH_MAP) {
-             DEBUG(SSSDBG_FATAL_FAILURE,
-                   "Attribute %s (%s in LDAP) is already used by SSSD, please "
-                   "choose a different cache name\n", sysdb_attr, ldap_attr);
-@@ -193,9 +210,14 @@ int sdap_extend_map(TALLOC_CTX *memctx,
-             map[num_entries+i].def_name == NULL) {
-             return ENOMEM;
-         }
--        DEBUG(SSSDBG_TRACE_FUNC, "Extending map with %s\n", extra_attrs[i]);
-+        DEBUG(SSSDBG_TRACE_FUNC, "Extending map with %s\n", *extra_attrs);
-+
-+        /* index must be incremented only for appended entry. */
-+        i++;
-     }
- 
-+    nextra = i;
-+
-     /* Sentinel */
-     memset(&map[num_entries+nextra], 0, sizeof(struct sdap_attr_map));
- 
--- 
-2.4.11
-
diff --git a/SOURCES/0115-utils-add-sss_domain_is_forest_root.patch b/SOURCES/0115-utils-add-sss_domain_is_forest_root.patch
new file mode 100644
index 0000000..f46b9e2
--- /dev/null
+++ b/SOURCES/0115-utils-add-sss_domain_is_forest_root.patch
@@ -0,0 +1,48 @@
+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
new file mode 100644
index 0000000..28f65d5
--- /dev/null
+++ b/SOURCES/0116-ad-handle-forest-root-not-listed-in-ad_enabled_domai.patch
@@ -0,0 +1,104 @@
+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/0116-watchdog-cope-with-time-shift.patch b/SOURCES/0116-watchdog-cope-with-time-shift.patch
deleted file mode 100644
index dbea5c2..0000000
--- a/SOURCES/0116-watchdog-cope-with-time-shift.patch
+++ /dev/null
@@ -1,96 +0,0 @@
-From c99c5bb88c8b02f24a2b0b15ea8bc9fe2a8dc6c4 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Pavel=20B=C5=99ezina?= <pbrezina@redhat.com>
-Date: Mon, 22 Aug 2016 13:15:04 +0200
-Subject: [PATCH 116/117] watchdog: cope with time shift
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-When a time is changed into the past during sssd runtime
-(e.g. on boot during time correction), it is possible that
-we never hit watchdog tevent timer since it is based on
-system time.
-
-This patch adds a past-time shift detection mechanism. If a time
-shift is detected we restart watchdog.
-
-Resolves:
-https://fedorahosted.org/sssd/ticket/3154
-
-Reviewed-by: Lukáš Slebodník <lslebodn@redhat.com>
-Reviewed-by: Fabiano Fidêncio <fidencio@redhat.com>
----
- src/util/util_watchdog.c | 41 +++++++++++++++++++++++++++++++++++++++++
- 1 file changed, 41 insertions(+)
-
-diff --git a/src/util/util_watchdog.c b/src/util/util_watchdog.c
-index 5032fddba1b94b3fc7e560162c392dfa57d699cf..1c27d73f13b3042ecb549a2184e1368e8339d199 100644
---- a/src/util/util_watchdog.c
-+++ b/src/util/util_watchdog.c
-@@ -29,8 +29,39 @@ struct watchdog_ctx {
-     struct timeval interval;
-     struct tevent_timer *te;
-     volatile int ticks;
-+
-+    /* To detect time shift. */
-+    struct tevent_context *ev;
-+    int input_interval;
-+    time_t timestamp;
- } watchdog_ctx;
- 
-+static bool watchdog_detect_timeshift(void)
-+{
-+    time_t prev_time;
-+    time_t cur_time;
-+    errno_t ret;
-+
-+    prev_time = watchdog_ctx.timestamp;
-+    cur_time = watchdog_ctx.timestamp = time(NULL);
-+    if (cur_time < prev_time) {
-+        /* Time shift detected. We need to restart watchdog. */
-+        DEBUG(SSSDBG_IMPORTANT_INFO, "Time shift detected, "
-+              "restarting watchdog!\n");
-+        teardown_watchdog();
-+        ret = setup_watchdog(watchdog_ctx.ev, watchdog_ctx.input_interval);
-+        if (ret != EOK) {
-+            DEBUG(SSSDBG_FATAL_FAILURE, "Unable to restart watchdog "
-+                  "[%d]: %s\n", ret, sss_strerror(ret));
-+            orderly_shutdown(1);
-+        }
-+
-+        return true;
-+    }
-+
-+    return false;
-+}
-+
- /* the watchdog is purposefully *not* handled by the tevent
-  * signal handler as it is meant to check if the daemon is
-  * still processing the event queue itself. A stuck process
-@@ -38,6 +69,12 @@ struct watchdog_ctx {
-  * signals either */
- static void watchdog_handler(int sig)
- {
-+    /* Do not count ticks if time shift was detected
-+     * since watchdog was restarted. */
-+    if (watchdog_detect_timeshift()) {
-+        return;
-+    }
-+
-     /* if 3 ticks passed by kills itself */
- 
-     if (__sync_add_and_fetch(&watchdog_ctx.ticks, 1) > 3) {
-@@ -101,6 +138,10 @@ int setup_watchdog(struct tevent_context *ev, int interval)
-     watchdog_ctx.interval.tv_sec = interval;
-     watchdog_ctx.interval.tv_usec = 0;
- 
-+    watchdog_ctx.ev = ev;
-+    watchdog_ctx.input_interval = interval;
-+    watchdog_ctx.timestamp = time(NULL);
-+
-     /* Start the timer */
-     /* we give 1 second head start to the watchdog event */
-     its.it_value.tv_sec = interval + 1;
--- 
-2.4.11
-
diff --git a/SOURCES/0117-PROXY-Use-the-fqname-when-converting-to-lowercase.patch b/SOURCES/0117-PROXY-Use-the-fqname-when-converting-to-lowercase.patch
deleted file mode 100644
index a41eedb..0000000
--- a/SOURCES/0117-PROXY-Use-the-fqname-when-converting-to-lowercase.patch
+++ /dev/null
@@ -1,39 +0,0 @@
-From fe4117b3203c0464b1366066bf09d83978c632d8 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Fabiano=20Fid=C3=AAncio?= <fidencio@redhat.com>
-Date: Tue, 23 Aug 2016 23:46:59 +0200
-Subject: [PATCH 117/117] PROXY: Use the fqname when converting to lowercase
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-When saving the user there is a comparison between the "cased alias"
-and the "lowercase password name". However, the first doesn't use fully
-qualified name while the second does, resulting in a not expected
-override of the "nameAlias" attribute of a stored user when trying to
-authenticate more than once using an alias.
-
-Resolves:
-https://fedorahosted.org/sssd/ticket/3134
-
-Signed-off-by: Fabiano Fidêncio <fidencio@redhat.com>
-Reviewed-by: Lukáš Slebodník <lslebodn@redhat.com>
----
- src/providers/proxy/proxy_id.c | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
-diff --git a/src/providers/proxy/proxy_id.c b/src/providers/proxy/proxy_id.c
-index f633a5e1c71dfc7f8d124e62169cfda48773bbb1..df8a329d4a5a4bd7ed7723d0219089e7b4ef639d 100644
---- a/src/providers/proxy/proxy_id.c
-+++ b/src/providers/proxy/proxy_id.c
-@@ -256,7 +256,7 @@ static int save_user(struct sss_domain_info *domain,
-     }
- 
-     if (lowercase) {
--        lc_pw_name = sss_tc_utf8_str_tolower(attrs, pwd->pw_name);
-+        lc_pw_name = sss_tc_utf8_str_tolower(attrs, real_name);
-         if (lc_pw_name == NULL) {
-             DEBUG(SSSDBG_OP_FAILURE, "Cannot convert name to lowercase.\n");
-             ret = ENOMEM;
--- 
-2.4.11
-
diff --git a/SOURCES/0117-SDAP-Fix-handling-of-search-bases.patch b/SOURCES/0117-SDAP-Fix-handling-of-search-bases.patch
new file mode 100644
index 0000000..52c114d
--- /dev/null
+++ b/SOURCES/0117-SDAP-Fix-handling-of-search-bases.patch
@@ -0,0 +1,168 @@
+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-BUILD-Allow-to-read-private-pipes-for-root.patch b/SOURCES/0118-BUILD-Allow-to-read-private-pipes-for-root.patch
deleted file mode 100644
index 8d87aad..0000000
--- a/SOURCES/0118-BUILD-Allow-to-read-private-pipes-for-root.patch
+++ /dev/null
@@ -1,81 +0,0 @@
-From 711a29023252013a8451ee1b90f045782fee1a38 Mon Sep 17 00:00:00 2001
-From: Lukas Slebodnik <lslebodn@redhat.com>
-Date: Fri, 19 Aug 2016 10:46:12 +0200
-Subject: [PATCH 118/121] BUILD: Allow to read private pipes for root
-
-Root can read anything from any directory even with permissions 000.
-
-However SELinux checks discretionary access control (DAC)
-and deny access if access is not allowed for root by DAC.
-The pam_sss use different unix socket /var/lib/sss/pipes/private/pam
-for user with uid 0. Therefore root need to be able read content
-of directory with private pipes.
-
-type=AVC msg=audit(08/19/2016 10:58:34.081:3369) : avc:  denied
-  { dac_read_search } for  pid=20257 comm=vsftpd capability=dac_read_search
-  scontext=system_u:system_r:ftpd_t:s0-s0:c0.c1023
-  tcontext=system_u:system_r:ftpd_t:s0-s0:c0.c1023 tclass=capability
-
-type=AVC msg=audit(08/19/2016 10:58:34.081:3369) : avc:  denied
-  { dac_override } for  pid=20257 comm=vsftpd capability=dac_override
-  scontext=system_u:system_r:ftpd_t:s0-s0:c0.c1023
-  tcontext=system_u:system_r:ftpd_t:s0-s0:c0.c1023 tclass=capability
-
-Resolves:
-https://fedorahosted.org/sssd/ticket/3143
-
-Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
----
- Makefile.am          | 8 ++++----
- contrib/sssd.spec.in | 2 +-
- 2 files changed, 5 insertions(+), 5 deletions(-)
-
-diff --git a/Makefile.am b/Makefile.am
-index 6ab4399d5b68644668198bc9b0e3056562a4e51a..b8cd8b64ca8a130a5dd3107e1fb1445310192059 100644
---- a/Makefile.am
-+++ b/Makefile.am
-@@ -3967,7 +3967,6 @@ SSSD_USER_DIRS = \
-     $(DESTDIR)$(keytabdir) \
-     $(DESTDIR)$(mcpath) \
-     $(DESTDIR)$(pipepath) \
--    $(DESTDIR)$(pipepath)/private \
-     $(DESTDIR)$(pubconfpath) \
-     $(DESTDIR)$(pubconfpath)/krb5.include.d \
-     $(DESTDIR)$(gpocachepath) \
-@@ -3994,16 +3993,17 @@ installsssddirs::
-     $(DESTDIR)$(sssddatadir) \
-     $(DESTDIR)$(sudolibdir) \
-     $(DESTDIR)$(autofslibdir) \
-+    $(DESTDIR)$(pipepath)/private \
-     $(SSSD_USER_DIRS) \
-     $(NULL);
- if SSSD_USER
--	-chown $(SSSD_USER):$(SSSD_USER) \
--	$(SSSD_USER_DIRS)
-+	-chown $(SSSD_USER):$(SSSD_USER) $(SSSD_USER_DIRS)
-+	-chown $(SSSD_USER) $(DESTDIR)$(pipepath)/private
- endif
- 	$(INSTALL) -d -m 0700 $(DESTDIR)$(dbpath) $(DESTDIR)$(logpath) \
--            $(DESTDIR)$(pipepath)/private \
- 	    $(DESTDIR)$(keytabdir) \
- 	    $(NULL)
-+	$(INSTALL) -d -m 0750 $(DESTDIR)$(pipepath)/private
- 	$(INSTALL) -d -m 0755 $(DESTDIR)$(mcpath) $(DESTDIR)$(pipepath) \
-             $(DESTDIR)$(pubconfpath) \
-             $(DESTDIR)$(pubconfpath)/krb5.include.d $(DESTDIR)$(gpocachepath)
-diff --git a/contrib/sssd.spec.in b/contrib/sssd.spec.in
-index f1ff16176cb8ca974b98948958cfa1e9290b0bca..cb68a73e85122b016de7df37bcf4fc232a10a2ac 100644
---- a/contrib/sssd.spec.in
-+++ b/contrib/sssd.spec.in
-@@ -784,7 +784,7 @@ done
- %ghost %attr(0644,sssd,sssd) %verify(not md5 size mtime) %{mcpath}/group
- %ghost %attr(0644,sssd,sssd) %verify(not md5 size mtime) %{mcpath}/initgroups
- %attr(755,sssd,sssd) %dir %{pipepath}
--%attr(700,sssd,sssd) %dir %{pipepath}/private
-+%attr(750,sssd,root) %dir %{pipepath}/private
- %attr(755,sssd,sssd) %dir %{pubconfpath}
- %attr(755,sssd,sssd) %dir %{gpocachepath}
- %attr(750,sssd,sssd) %dir %{_var}/log/%{name}
--- 
-2.4.11
-
diff --git a/SOURCES/0118-overrides-add-certificates-to-mapped-attribute.patch b/SOURCES/0118-overrides-add-certificates-to-mapped-attribute.patch
new file mode 100644
index 0000000..2bfa2c1
--- /dev/null
+++ b/SOURCES/0118-overrides-add-certificates-to-mapped-attribute.patch
@@ -0,0 +1,102 @@
+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
new file mode 100644
index 0000000..c9874b9
--- /dev/null
+++ b/SOURCES/0119-AD-Make-ad_account_can_shortcut-reusable-by-SSSD-on-.patch
@@ -0,0 +1,243 @@
+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/0119-SYSDB-Rework-sysdb_cache_connect.patch b/SOURCES/0119-SYSDB-Rework-sysdb_cache_connect.patch
deleted file mode 100644
index bc2bacb..0000000
--- a/SOURCES/0119-SYSDB-Rework-sysdb_cache_connect.patch
+++ /dev/null
@@ -1,123 +0,0 @@
-From 960eca66245f23cf8ae0f32c3e44581a0e1117f9 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Fabiano=20Fid=C3=AAncio?= <fidencio@redhat.com>
-Date: Tue, 16 Aug 2016 11:20:49 +0200
-Subject: [PATCH 119/121] SYSDB: Rework sysdb_cache_connect()
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-As sysdb_cache_connect() has two very specific use cases (connect to the
-cache and connect to the timestamp cache) and each of those calls have a
-predetermined/fixed sets of values for a few parameters, let's try to
-make the code a bit simpler to follow by having explicit functions for
-connecting to the cache and connecting to the timestamp cache.
-
-Macros could be used as well, but I have a slightly preference for
-having two new functions instead of macros accessing internal parameters
-of the macro's parameter.
-
-Related:
-https://fedorahosted.org/sssd/ticket/3128
-
-Signed-off-by: Fabiano Fidêncio <fidencio@redhat.com>
-
-Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
----
- src/db/sysdb_init.c | 53 ++++++++++++++++++++++++++++++++++-------------------
- 1 file changed, 34 insertions(+), 19 deletions(-)
-
-diff --git a/src/db/sysdb_init.c b/src/db/sysdb_init.c
-index 9e3646bfeb9a494ebff2d348ab1c53336f8a5c03..59934701c4d2b9d770385a202af058404a6d3eb9 100644
---- a/src/db/sysdb_init.c
-+++ b/src/db/sysdb_init.c
-@@ -511,14 +511,14 @@ done:
-     return ret;
- }
- 
--static errno_t sysdb_cache_connect(TALLOC_CTX *mem_ctx,
--                                   struct sss_domain_info *domain,
--                                   const char *ldb_file,
--                                   int flags,
--                                   const char *exp_version,
--                                   const char *base_ldif,
--                                   struct ldb_context **_ldb,
--                                   const char **_version)
-+static errno_t sysdb_cache_connect_helper(TALLOC_CTX *mem_ctx,
-+                                          struct sss_domain_info *domain,
-+                                          const char *ldb_file,
-+                                          int flags,
-+                                          const char *exp_version,
-+                                          const char *base_ldif,
-+                                          struct ldb_context **_ldb,
-+                                          const char **_version)
- {
-     TALLOC_CTX *tmp_ctx = NULL;
-     struct ldb_message_element *el;
-@@ -619,6 +619,29 @@ done:
-     return ret;
- }
- 
-+static errno_t sysdb_cache_connect(TALLOC_CTX *mem_ctx,
-+                                   struct sysdb_ctx *sysdb,
-+                                   struct sss_domain_info *domain,
-+                                   struct ldb_context **ldb,
-+                                   const char **version)
-+{
-+    return sysdb_cache_connect_helper(mem_ctx, domain, sysdb->ldb_file,
-+                                      0, SYSDB_VERSION, SYSDB_BASE_LDIF,
-+                                      ldb, version);
-+}
-+
-+static errno_t sysdb_ts_cache_connect(TALLOC_CTX *mem_ctx,
-+                                      struct sysdb_ctx *sysdb,
-+                                      struct sss_domain_info *domain,
-+                                      struct ldb_context **ldb,
-+                                      const char **version)
-+{
-+    return sysdb_cache_connect_helper(mem_ctx, domain, sysdb->ldb_ts_file,
-+                                      LDB_FLG_NOSYNC, SYSDB_TS_VERSION,
-+                                      SYSDB_TS_BASE_LDIF,
-+                                      ldb, version);
-+}
-+
- static errno_t remove_ts_cache(struct sysdb_ctx *sysdb)
- {
-     errno_t ret;
-@@ -649,9 +672,7 @@ static int sysdb_domain_cache_connect(struct sysdb_ctx *sysdb,
-         return ENOMEM;
-     }
- 
--    ret = sysdb_cache_connect(tmp_ctx, domain, sysdb->ldb_file, 0,
--                              SYSDB_VERSION, SYSDB_BASE_LDIF,
--                              &ldb, &version);
-+    ret = sysdb_cache_connect(tmp_ctx, sysdb, domain, &ldb, &version);
-     switch (ret) {
-     case ERR_SYSDB_VERSION_TOO_OLD:
-         if (upgrade_ctx == NULL) {
-@@ -731,10 +752,7 @@ static int sysdb_timestamp_cache_connect(struct sysdb_ctx *sysdb,
-         return ENOMEM;
-     }
- 
--    ret = sysdb_cache_connect(tmp_ctx, domain,
--                              sysdb->ldb_ts_file, LDB_FLG_NOSYNC,
--                              SYSDB_TS_VERSION, SYSDB_TS_BASE_LDIF,
--                              &ldb, &version);
-+    ret = sysdb_ts_cache_connect(tmp_ctx, sysdb, domain, &ldb, &version);
-     switch (ret) {
-     case ERR_SYSDB_VERSION_TOO_OLD:
-         if (upgrade_ctx == NULL) {
-@@ -801,10 +819,7 @@ static int sysdb_timestamp_cache_connect(struct sysdb_ctx *sysdb,
-         /* Now the connect must succeed because the previous cache doesn't
-          * exist anymore.
-          */
--        ret = sysdb_cache_connect(tmp_ctx, domain,
--                                  sysdb->ldb_ts_file, LDB_FLG_NOSYNC,
--                                  SYSDB_TS_VERSION, SYSDB_TS_BASE_LDIF,
--                                  &ldb, &version);
-+        ret = sysdb_ts_cache_connect(tmp_ctx, sysdb, domain, &ldb, &version);
-         if (ret != EOK) {
-             DEBUG(SSSDBG_MINOR_FAILURE,
-                   "Could not delete the timestamp ldb file (%d) (%s)\n",
--- 
-2.4.11
-
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
new file mode 100644
index 0000000..abe44ec
--- /dev/null
+++ b/SOURCES/0120-LDAP-AD-Do-not-fail-in-case-rfc2307bis_nested_groups.patch
@@ -0,0 +1,47 @@
+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/0120-SYSDB-Remove-the-timestamp-cache-for-a-newly-created.patch b/SOURCES/0120-SYSDB-Remove-the-timestamp-cache-for-a-newly-created.patch
deleted file mode 100644
index 9145773..0000000
--- a/SOURCES/0120-SYSDB-Remove-the-timestamp-cache-for-a-newly-created.patch
+++ /dev/null
@@ -1,151 +0,0 @@
-From 9e5fc47e6636f6a149dba9a0a708892be85a6f22 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Fabiano=20Fid=C3=AAncio?= <fidencio@redhat.com>
-Date: Tue, 16 Aug 2016 11:46:41 +0200
-Subject: [PATCH 120/121] SYSDB: Remove the timestamp cache for a newly created
- cache
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-As many users are used to remove the persistent cache without removing
-the timestamp cache, let's throw away the timestamp cache in this case.
-
-Resolves:
-https://fedorahosted.org/sssd/ticket/3128
-
-Signed-off-by: Fabiano Fidêncio <fidencio@redhat.com>
-
-Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
----
- src/db/sysdb_init.c | 69 ++++++++++++++++++++++++++++++++++++-----------------
- 1 file changed, 47 insertions(+), 22 deletions(-)
-
-diff --git a/src/db/sysdb_init.c b/src/db/sysdb_init.c
-index 59934701c4d2b9d770385a202af058404a6d3eb9..c387c1b12c116f38d5a13f1adeac5ef64d593af8 100644
---- a/src/db/sysdb_init.c
-+++ b/src/db/sysdb_init.c
-@@ -511,12 +511,30 @@ done:
-     return ret;
- }
- 
-+static errno_t remove_ts_cache(struct sysdb_ctx *sysdb)
-+{
-+    errno_t ret;
-+
-+    if (sysdb->ldb_ts_file == NULL) {
-+        return EOK;
-+    }
-+
-+    ret = unlink(sysdb->ldb_ts_file);
-+    if (ret != EOK && errno != ENOENT) {
-+        return errno;
-+    }
-+
-+    return EOK;
-+}
-+
- static errno_t sysdb_cache_connect_helper(TALLOC_CTX *mem_ctx,
-+                                          struct sysdb_ctx *sysdb,
-                                           struct sss_domain_info *domain,
-                                           const char *ldb_file,
-                                           int flags,
-                                           const char *exp_version,
-                                           const char *base_ldif,
-+                                          bool *_newly_created,
-                                           struct ldb_context **_ldb,
-                                           const char **_version)
- {
-@@ -527,6 +545,7 @@ static errno_t sysdb_cache_connect_helper(TALLOC_CTX *mem_ctx,
-     const char *version = NULL;
-     int ret;
-     struct ldb_context *ldb;
-+    bool newly_created;
- 
-     tmp_ctx = talloc_new(NULL);
-     if (!tmp_ctx) {
-@@ -592,8 +611,9 @@ static errno_t sysdb_cache_connect_helper(TALLOC_CTX *mem_ctx,
-         goto done;
-     }
- 
--    /* The cache has been newly created.
--     * We need to reopen the LDB to ensure that
-+    newly_created = true;
-+
-+    /* We need to reopen the LDB to ensure that
-      * all of the special values take effect
-      * (such as enabling the memberOf plugin and
-      * the various indexes).
-@@ -613,6 +633,9 @@ static errno_t sysdb_cache_connect_helper(TALLOC_CTX *mem_ctx,
-     }
- done:
-     if (ret == EOK) {
-+        if (_newly_created != NULL) {
-+            *_newly_created = newly_created;
-+        }
-         *_ldb = talloc_steal(mem_ctx, ldb);
-     }
-     talloc_free(tmp_ctx);
-@@ -625,9 +648,27 @@ static errno_t sysdb_cache_connect(TALLOC_CTX *mem_ctx,
-                                    struct ldb_context **ldb,
-                                    const char **version)
- {
--    return sysdb_cache_connect_helper(mem_ctx, domain, sysdb->ldb_file,
-+    bool newly_created;
-+    bool ldb_file_exists;
-+    errno_t ret;
-+
-+    ldb_file_exists = !(access(sysdb->ldb_file, F_OK) == -1 && errno == ENOENT);
-+
-+    ret = sysdb_cache_connect_helper(mem_ctx, sysdb, domain, sysdb->ldb_file,
-                                       0, SYSDB_VERSION, SYSDB_BASE_LDIF,
--                                      ldb, version);
-+                                      &newly_created, ldb, version);
-+
-+    /* The cache has been newly created. */
-+    if (ret == EOK && newly_created && !ldb_file_exists) {
-+        ret = remove_ts_cache(sysdb);
-+        if (ret != EOK) {
-+            DEBUG(SSSDBG_MINOR_FAILURE,
-+                  "Could not delete the timestamp ldb file (%d) (%s)\n",
-+                  ret, sss_strerror(ret));
-+        }
-+    }
-+
-+    return ret;
- }
- 
- static errno_t sysdb_ts_cache_connect(TALLOC_CTX *mem_ctx,
-@@ -636,28 +677,12 @@ static errno_t sysdb_ts_cache_connect(TALLOC_CTX *mem_ctx,
-                                       struct ldb_context **ldb,
-                                       const char **version)
- {
--    return sysdb_cache_connect_helper(mem_ctx, domain, sysdb->ldb_ts_file,
-+    return sysdb_cache_connect_helper(mem_ctx, sysdb, domain, sysdb->ldb_ts_file,
-                                       LDB_FLG_NOSYNC, SYSDB_TS_VERSION,
--                                      SYSDB_TS_BASE_LDIF,
-+                                      SYSDB_TS_BASE_LDIF, NULL,
-                                       ldb, version);
- }
- 
--static errno_t remove_ts_cache(struct sysdb_ctx *sysdb)
--{
--    errno_t ret;
--
--    if (sysdb->ldb_ts_file == NULL) {
--        return EOK;
--    }
--
--    ret = unlink(sysdb->ldb_ts_file);
--    if (ret != EOK && errno != ENOENT) {
--        return errno;
--    }
--
--    return EOK;
--}
--
- static int sysdb_domain_cache_connect(struct sysdb_ctx *sysdb,
-                                       struct sss_domain_info *domain,
-                                       struct sysdb_dom_upgrade_ctx *upgrade_ctx)
--- 
-2.4.11
-
diff --git a/SOURCES/0121-PAM-check-matching-certificates-from-all-domains.patch b/SOURCES/0121-PAM-check-matching-certificates-from-all-domains.patch
new file mode 100644
index 0000000..55c7c61
--- /dev/null
+++ b/SOURCES/0121-PAM-check-matching-certificates-from-all-domains.patch
@@ -0,0 +1,116 @@
+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/0121-SECRETS-Return-ENOENT-when_deleting-a-non-existent-s.patch b/SOURCES/0121-SECRETS-Return-ENOENT-when_deleting-a-non-existent-s.patch
deleted file mode 100644
index 80ff5bb..0000000
--- a/SOURCES/0121-SECRETS-Return-ENOENT-when_deleting-a-non-existent-s.patch
+++ /dev/null
@@ -1,47 +0,0 @@
-From 0f3b872aa881d5480fc98b0cfc7421ffc7f802a3 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Fabiano=20Fid=C3=AAncio?= <fidencio@redhat.com>
-Date: Wed, 17 Aug 2016 13:12:21 +0200
-Subject: [PATCH 121/121] SECRETS: Return ENOENT when_deleting a non-existent
- secret
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-For this, just make use of the sysdb_error_to_errno() function.
-
-Resolves:
-https://fedorahosted.org/sssd/ticket/3125
-
-Signed-off-by: Fabiano Fidêncio <fidencio@redhat.com>
-
-Reviewed-by: Lukáš Slebodník <lslebodn@redhat.com>
-Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
----
- src/responder/secrets/local.c | 9 ++-------
- 1 file changed, 2 insertions(+), 7 deletions(-)
-
-diff --git a/src/responder/secrets/local.c b/src/responder/secrets/local.c
-index 17469249b357cbdc5e50ddff6b563fdf2f377577..ac3049b62fa77f69d44ec5792139fe3378afb3f4 100644
---- a/src/responder/secrets/local.c
-+++ b/src/responder/secrets/local.c
-@@ -375,15 +375,10 @@ int local_db_delete(TALLOC_CTX *mem_ctx,
-     int ret;
- 
-     ret = local_db_dn(mem_ctx, lctx->ldb, req_path, &dn);
--    if (ret != EOK) goto done;
-+    if (ret != EOK) return ret;
- 
-     ret = ldb_delete(lctx->ldb, dn);
--    if (ret != EOK) {
--        ret = EIO;
--    }
--
--done:
--    return ret;
-+    return sysdb_error_to_errno(ret);
- }
- 
- int local_db_create(TALLOC_CTX *mem_ctx,
--- 
-2.4.11
-
diff --git a/SOURCES/0122-DP-Reduce-Data-Provider-log-level-noise.patch b/SOURCES/0122-DP-Reduce-Data-Provider-log-level-noise.patch
new file mode 100644
index 0000000..b7dfbd3
--- /dev/null
+++ b/SOURCES/0122-DP-Reduce-Data-Provider-log-level-noise.patch
@@ -0,0 +1,80 @@
+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/0122-IPA-Parse-qualified-names-when-guessing-AD-user-prin.patch b/SOURCES/0122-IPA-Parse-qualified-names-when-guessing-AD-user-prin.patch
deleted file mode 100644
index 8bc504d..0000000
--- a/SOURCES/0122-IPA-Parse-qualified-names-when-guessing-AD-user-prin.patch
+++ /dev/null
@@ -1,61 +0,0 @@
-From b93f618189d9906802c79d3090fcc477f762e6e6 Mon Sep 17 00:00:00 2001
-From: Jakub Hrozek <jhrozek@redhat.com>
-Date: Tue, 9 Aug 2016 22:08:27 +0200
-Subject: [PATCH 122/126] IPA: Parse qualified names when guessing AD user
- principal
-
-Most AD users store their UPN in an attribute. If they don't, or the sssd
-was configured (typically in earlier versions to work around a bug) to not
-look at the principal attribute, then sssd is supposed to guess
-the attribute.
-
-That currently doesn't work in 1.14, because the username is already
-qualified and then we also append the realm name to it. We need to parse
-the simple username from the qualified name first.
-
-The issue can be reproduced simply by authenticating as the Administrator
-account in IPA-AD trust setups.
-
-Resolves:
-https://fedorahosted.org/sssd/ticket/3127
-
-Reviewed-by: Sumit Bose <sbose@redhat.com>
----
- src/providers/ipa/ipa_s2n_exop.c | 14 ++++++++++++--
- 1 file changed, 12 insertions(+), 2 deletions(-)
-
-diff --git a/src/providers/ipa/ipa_s2n_exop.c b/src/providers/ipa/ipa_s2n_exop.c
-index 255dad45037a6cb8f399bf2df500215f6fb25b59..bfa6757046282d656627aa57cb9054b09facd2b8 100644
---- a/src/providers/ipa/ipa_s2n_exop.c
-+++ b/src/providers/ipa/ipa_s2n_exop.c
-@@ -1941,6 +1941,7 @@ static errno_t ipa_s2n_save_objects(struct sss_domain_info *dom,
-     struct sss_nss_homedir_ctx homedir_ctx;
-     char *name = NULL;
-     char *realm;
-+    char *short_name = NULL;
-     char *upn = NULL;
-     gid_t gid;
-     gid_t orig_gid = 0;
-@@ -2092,8 +2093,17 @@ static errno_t ipa_s2n_save_objects(struct sss_domain_info *dom,
-                     ret = ENOMEM;
-                     goto done;
-                 }
--                upn = talloc_asprintf(tmp_ctx, "%s@%s",
--                                      attrs->a.user.pw_name, realm);
-+
-+                ret = sss_parse_internal_fqname(tmp_ctx, attrs->a.user.pw_name,
-+                                                &short_name, NULL);
-+                if (ret != EOK) {
-+                    DEBUG(SSSDBG_CRIT_FAILURE,
-+                          "Cannot parse internal name %s\n",
-+                          attrs->a.user.pw_name);
-+                    goto done;
-+                }
-+
-+                upn = talloc_asprintf(tmp_ctx, "%s@%s", short_name, realm);
-                 if (!upn) {
-                     DEBUG(SSSDBG_OP_FAILURE, "failed to format UPN.\n");
-                     ret = ENOMEM;
--- 
-2.4.11
-
diff --git a/SOURCES/0123-NSS-Move-output-name-formatting-to-utils.patch b/SOURCES/0123-NSS-Move-output-name-formatting-to-utils.patch
new file mode 100644
index 0000000..abc8b99
--- /dev/null
+++ b/SOURCES/0123-NSS-Move-output-name-formatting-to-utils.patch
@@ -0,0 +1,249 @@
+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/0123-SYSDB-Fix-uninitialized-scalar-variable.patch b/SOURCES/0123-SYSDB-Fix-uninitialized-scalar-variable.patch
deleted file mode 100644
index d9897ba..0000000
--- a/SOURCES/0123-SYSDB-Fix-uninitialized-scalar-variable.patch
+++ /dev/null
@@ -1,35 +0,0 @@
-From d6ecd69f63496446d23252426de41523ebe2bbf6 Mon Sep 17 00:00:00 2001
-From: Lukas Slebodnik <lslebodn@redhat.com>
-Date: Mon, 29 Aug 2016 09:13:49 +0200
-Subject: [PATCH 123/126] SYSDB: Fix uninitialized scalar variable
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-The boolean variable newly_created could be used uninitialized
-in done section in case of failure. The variable was firstly initialized
-to true after succesfull execution of function sysdb_cache_create_empty.
-
-Uninitialized variable usually means true for boolean variable.
-
-Reviewed-by: Fabiano Fidêncio <fidencio@redhat.com>
----
- src/db/sysdb_init.c | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
-diff --git a/src/db/sysdb_init.c b/src/db/sysdb_init.c
-index c387c1b12c116f38d5a13f1adeac5ef64d593af8..d110aa7a2878e47650db177cfd342d0ac32248ab 100644
---- a/src/db/sysdb_init.c
-+++ b/src/db/sysdb_init.c
-@@ -545,7 +545,7 @@ static errno_t sysdb_cache_connect_helper(TALLOC_CTX *mem_ctx,
-     const char *version = NULL;
-     int ret;
-     struct ldb_context *ldb;
--    bool newly_created;
-+    bool newly_created = false;
- 
-     tmp_ctx = talloc_new(NULL);
-     if (!tmp_ctx) {
--- 
-2.4.11
-
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
new file mode 100644
index 0000000..fd0780e
--- /dev/null
+++ b/SOURCES/0124-CACHE_REQ-Add-a-new-cache_req_ncache_filter_fn-plugi.patch
@@ -0,0 +1,322 @@
+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/0124-PROXY-Use-right-name-in-ldap-filter.patch b/SOURCES/0124-PROXY-Use-right-name-in-ldap-filter.patch
deleted file mode 100644
index 77703a5..0000000
--- a/SOURCES/0124-PROXY-Use-right-name-in-ldap-filter.patch
+++ /dev/null
@@ -1,51 +0,0 @@
-From d2636b0f34d6e4ff7cf99a44db8317fd485a3783 Mon Sep 17 00:00:00 2001
-From: Lukas Slebodnik <lslebodn@redhat.com>
-Date: Fri, 26 Aug 2016 14:57:22 +0200
-Subject: [PATCH 124/126] PROXY: Use right name in ldap filter
-
-We used internal fq name in ldap filter
-with id_provider proxy to files and auth provider
-ldap
-
-[sssd[be[LDAP]]] [sdap_get_generic_ext_step]
-    (0x0400): calling ldap_search_ext with
-    [(&(uid=testuser1@ldap)(objectclass=posixAccount))][dc=example,dc=com].
-
-Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
----
- src/providers/ldap/ldap_auth.c | 11 ++++++++---
- 1 file changed, 8 insertions(+), 3 deletions(-)
-
-diff --git a/src/providers/ldap/ldap_auth.c b/src/providers/ldap/ldap_auth.c
-index 35f16b0d4a6f8e566b0cf63b65ba46f31e7c1bcd..00d38284e428eea42254820fd08ee4fb125235a6 100644
---- a/src/providers/ldap/ldap_auth.c
-+++ b/src/providers/ldap/ldap_auth.c
-@@ -361,7 +361,7 @@ shadow_fail:
- 
- /* ==Get-User-DN========================================================== */
- struct get_user_dn_state {
--    const char *username;
-+    char *username;
- 
-     char *orig_dn;
- };
-@@ -386,9 +386,14 @@ static struct tevent_req *get_user_dn_send(TALLOC_CTX *memctx,
-     req = tevent_req_create(memctx, &state, struct get_user_dn_state);
-     if (!req) return NULL;
- 
--    state->username = username;
-+    ret = sss_parse_internal_fqname(state, username,
-+                                    &state->username, NULL);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_OP_FAILURE, "Cannot parse %s\n", username);
-+        goto done;
-+    }
- 
--    ret = sss_filter_sanitize(state, username, &clean_name);
-+    ret = sss_filter_sanitize(state, state->username, &clean_name);
-     if (ret != EOK) {
-         goto done;
-     }
--- 
-2.4.11
-
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
new file mode 100644
index 0000000..2508964
--- /dev/null
+++ b/SOURCES/0125-CACHE_REQ_RESULT-Introduce-cache_req_create_ldb_resu.patch
@@ -0,0 +1,92 @@
+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/0125-SYSDB-Fix-error-handling-in-sysdb_get_user_members_r.patch b/SOURCES/0125-SYSDB-Fix-error-handling-in-sysdb_get_user_members_r.patch
deleted file mode 100644
index f2581b1..0000000
--- a/SOURCES/0125-SYSDB-Fix-error-handling-in-sysdb_get_user_members_r.patch
+++ /dev/null
@@ -1,50 +0,0 @@
-From fde5249cee996276492886697be570b9e698e454 Mon Sep 17 00:00:00 2001
-From: Lukas Slebodnik <lslebodn@redhat.com>
-Date: Tue, 30 Aug 2016 15:37:43 +0200
-Subject: [PATCH 125/126] SYSDB: Fix error handling in
- sysdb_get_user_members_recursively
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-We ignored failures from sysdb_search_entry
-
-Reviewed-by: Petr Čech <pcech@redhat.com>
----
- src/db/sysdb_ops.c   | 3 +++
- src/db/sysdb_views.c | 5 ++++-
- 2 files changed, 7 insertions(+), 1 deletion(-)
-
-diff --git a/src/db/sysdb_ops.c b/src/db/sysdb_ops.c
-index 44fb5b70e6d33fffbca5824f831a3229254ecb57..e4c8e1e285e3bc49710f71c896ba9a30c742d4fa 100644
---- a/src/db/sysdb_ops.c
-+++ b/src/db/sysdb_ops.c
-@@ -4738,6 +4738,9 @@ errno_t sysdb_get_user_members_recursively(TALLOC_CTX *mem_ctx,
- 
-     ret = sysdb_search_entry(tmp_ctx, dom->sysdb, base_dn, LDB_SCOPE_SUBTREE,
-                              filter, attrs, &count, &msgs);
-+    if (ret != EOK) {
-+        goto done;
-+    }
- 
-     res = talloc_zero(tmp_ctx, struct ldb_result);
-     if (res == NULL) {
-diff --git a/src/db/sysdb_views.c b/src/db/sysdb_views.c
-index 79f513d13ba41212a6cd84e1d9e609df6acba29c..9dc48f5b6c414bbc7c64bcd1fe73553f388588bd 100644
---- a/src/db/sysdb_views.c
-+++ b/src/db/sysdb_views.c
-@@ -1374,7 +1374,10 @@ errno_t sysdb_add_group_member_overrides(struct sss_domain_info *domain,
- 
-     ret = sysdb_get_user_members_recursively(tmp_ctx, domain, obj->dn,
-                                              &res_members);
--    if (ret != EOK) {
-+    if (ret == ENOENT) {
-+        ret = EOK;
-+        goto done;
-+    } else if (ret != EOK) {
-         DEBUG(SSSDBG_OP_FAILURE,
-               "sysdb_get_user_members_recursively failed.\n");
-         goto done;
--- 
-2.4.11
-
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
new file mode 100644
index 0000000..d36136a
--- /dev/null
+++ b/SOURCES/0126-CACHE_REQ-Make-use-of-cache_req_ncache_filter_fn.patch
@@ -0,0 +1,392 @@
+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/0126-sdap_initgr_nested_get_membership_diff-use-fully-qua.patch b/SOURCES/0126-sdap_initgr_nested_get_membership_diff-use-fully-qua.patch
deleted file mode 100644
index 0cb5cc4..0000000
--- a/SOURCES/0126-sdap_initgr_nested_get_membership_diff-use-fully-qua.patch
+++ /dev/null
@@ -1,35 +0,0 @@
-From 16196f3248a32a7bd9e395b0fdc85249ca4201d7 Mon Sep 17 00:00:00 2001
-From: Sumit Bose <sbose@redhat.com>
-Date: Tue, 30 Aug 2016 17:30:10 +0200
-Subject: [PATCH 126/126] sdap_initgr_nested_get_membership_diff: use
- fully-qualified names
-
-I think this is a leftover from the change to use fully-qualified names
-in sysdb. To verify this you can create a nested group in IPA. Without
-this patch the id command will only show the groups the user is a direct
-member of. With the patch the indirect groups memberships should be
-shown as well.
-
-https://fedorahosted.org/sssd/ticket/3163
-
-Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
----
- src/providers/ldap/sdap_async_initgroups.c | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
-diff --git a/src/providers/ldap/sdap_async_initgroups.c b/src/providers/ldap/sdap_async_initgroups.c
-index 82c708c226bf1a645ff5a395947dfdbad71e0f1f..f9593f0dfaa2dc6e33fd6c9d1f0c9b78cad3a1d9 100644
---- a/src/providers/ldap/sdap_async_initgroups.c
-+++ b/src/providers/ldap/sdap_async_initgroups.c
-@@ -1414,7 +1414,7 @@ sdap_initgr_nested_get_membership_diff(TALLOC_CTX *mem_ctx,
-                group_name, parents_count);
- 
-     if (parents_count > 0) {
--        ret = sysdb_attrs_primary_name_list(dom, tmp_ctx,
-+        ret = sysdb_attrs_primary_fqdn_list(dom, tmp_ctx,
-                                             ldap_parentlist,
-                                             parents_count,
-                                             opts->group_map[SDAP_AT_GROUP_NAME].name,
--- 
-2.4.11
-
diff --git a/SOURCES/0127-Revert-CONFIG-Use-default-config-when-none-provided.patch b/SOURCES/0127-Revert-CONFIG-Use-default-config-when-none-provided.patch
deleted file mode 100644
index 623336d..0000000
--- a/SOURCES/0127-Revert-CONFIG-Use-default-config-when-none-provided.patch
+++ /dev/null
@@ -1,196 +0,0 @@
-From 17ba1565f6dc3874c554f37ca949ad284647141d Mon Sep 17 00:00:00 2001
-From: Jakub Hrozek <jhrozek@redhat.com>
-Date: Wed, 7 Sep 2016 17:21:19 +0200
-Subject: [PATCH 127/127] Revert "CONFIG: Use default config when none
- provided"
-
-This reverts commit 59744cff6edb106ae799b2321cb8731edadf409a.
----
- Makefile.am                   | 10 ----------
- contrib/sssd.spec.in          |  3 ---
- src/confdb/confdb.h           |  1 -
- src/confdb/confdb_setup.c     | 40 ++++------------------------------------
- src/examples/sssd-shadowutils |  6 ------
- src/examples/sssd.conf        | 17 -----------------
- 6 files changed, 4 insertions(+), 73 deletions(-)
- delete mode 100644 src/examples/sssd-shadowutils
- delete mode 100644 src/examples/sssd.conf
-
-diff --git a/Makefile.am b/Makefile.am
-index b8cd8b64ca8a130a5dd3107e1fb1445310192059..056c73bb265523705a0de16d4d5e078f516f566f 100644
---- a/Makefile.am
-+++ b/Makefile.am
-@@ -35,7 +35,6 @@ endif
- 
- sssdlibexecdir = $(libexecdir)/sssd
- sssdlibdir = $(libdir)/sssd
--sssddefaultconfdir = $(sssdlibdir)/conf
- ldblibdir = @ldblibdir@
- if BUILD_KRB5_LOCATOR_PLUGIN
- krb5plugindir = @krb5pluginpath@
-@@ -85,7 +84,6 @@ pkgconfigdir = $(libdir)/pkgconfig
- krb5rcachedir = @krb5rcachedir@
- sudolibdir = @sudolibpath@
- polkitdir = @polkitdir@
--pamconfdir = $(sysconfdir)/pam.d
- systemtap_tapdir = @tapset_dir@
- 
- secdbpath = @secdbpath@
-@@ -464,7 +462,6 @@ AM_CPPFLAGS = \
-     -DSSSDDATADIR=\"$(sssddatadir)\" \
-     -DSSSD_LIBEXEC_PATH=\"$(sssdlibexecdir)\" \
-     -DSSSD_CONF_DIR=\"$(sssdconfdir)\" \
--    -DSSSD_DEFAULT_CONF_DIR=\"$(sssddefaultconfdir)\" \
-     -DSSS_NSS_MCACHE_DIR=\"$(mcpath)\" \
-     -DSSS_NSS_SOCKET_NAME=\"$(pipepath)/nss\" \
-     -DSSS_PAM_SOCKET_NAME=\"$(pipepath)/pam\" \
-@@ -1465,12 +1462,6 @@ dist_noinst_DATA += \
-     src/sss_client/COPYING.LESSER \
-     src/m4
- 
--dist_sssddefaultconf_DATA = \
--    src/examples/sssd.conf
--
--dist_pamconf_DATA = \
--    src/examples/sssd-shadowutils
--
- ######################
- # Command-line Tools #
- ######################
-@@ -3972,7 +3963,6 @@ SSSD_USER_DIRS = \
-     $(DESTDIR)$(gpocachepath) \
-     $(DESTDIR)$(sssdconfdir) \
-     $(DESTDIR)$(sssdconfdir)/conf.d \
--    $(DESTDIR)$(sssddefaultconfdir) \
-     $(DESTDIR)$(logpath) \
-     $(NULL)
- 
-diff --git a/contrib/sssd.spec.in b/contrib/sssd.spec.in
-index cb68a73e85122b016de7df37bcf4fc232a10a2ac..4e24aa39c65ad698607615d93de8624e2e1832ff 100644
---- a/contrib/sssd.spec.in
-+++ b/contrib/sssd.spec.in
-@@ -800,9 +800,6 @@ done
- %dir %{_sysconfdir}/rwtab.d
- %config(noreplace) %{_sysconfdir}/rwtab.d/sssd
- %dir %{_datadir}/sssd
--%{_sysconfdir}/pam.d/sssd-shadowutils
--%{_libdir}/%{name}/conf/sssd.conf
--
- %{_datadir}/sssd/cfg_rules.ini
- %{_datadir}/sssd/sssd.api.conf
- %{_datadir}/sssd/sssd.api.d
-diff --git a/src/confdb/confdb.h b/src/confdb/confdb.h
-index 72adbd80ea534eb0becd3e517c00b0c26d00444c..e8df280562d7014e0dc5d4fe5c3336eaba204537 100644
---- a/src/confdb/confdb.h
-+++ b/src/confdb/confdb.h
-@@ -40,7 +40,6 @@
- 
- #define CONFDB_DEFAULT_CFG_FILE_VER 2
- #define CONFDB_FILE "config.ldb"
--#define SSSD_DEFAULT_CONFIG_FILE SSSD_DEFAULT_CONF_DIR"/sssd.conf"
- #define SSSD_CONFIG_FILE SSSD_CONF_DIR"/sssd.conf"
- #define CONFDB_DEFAULT_CONFIG_DIR SSSD_CONF_DIR"/conf.d"
- #define SSSD_MIN_ID 1
-diff --git a/src/confdb/confdb_setup.c b/src/confdb/confdb_setup.c
-index d6feab9000d54d2c3761de6d8e990053ade7e85f..a71d9dd1202824b3c9a7e69f1d8fa905ac1b8c02 100644
---- a/src/confdb/confdb_setup.c
-+++ b/src/confdb/confdb_setup.c
-@@ -21,14 +21,12 @@
- 
- #include "config.h"
- #include <sys/stat.h>
--#include <unistd.h>
- #include "util/util.h"
- #include "db/sysdb.h"
- #include "confdb.h"
- #include "confdb_private.h"
- #include "confdb_setup.h"
- #include "util/sss_ini.h"
--#include "tools/tools_util.h"
- 
- 
- static int confdb_test(struct confdb_ctx *cdb)
-@@ -161,41 +159,11 @@ static int confdb_init_db(const char *config_file, const char *config_dir,
-         DEBUG(SSSDBG_TRACE_FUNC,
-               "sss_ini_config_file_open failed: %s [%d]\n", strerror(ret),
-                ret);
--        if (ret != ENOENT) {
--            /* Anything other than ENOENT is unrecoverable */
--            goto done;
--        } else {
--            /* Copy the default configuration file to the standard location
--             * and then retry
--             */
--             ret = copy_file_secure(SSSD_DEFAULT_CONFIG_FILE,
--                                    SSSD_CONFIG_FILE,
--                                    0600,
--                                    getuid(),
--                                    getgid(),
--                                    false);
--             if (ret != EOK) {
--                 DEBUG(SSSDBG_FATAL_FAILURE,
--                       "Could not copy default configuration: %s",
--                       sss_strerror(ret));
--                 /* sss specific error denoting missing configuration file */
--                 ret = ERR_MISSING_CONF;
--                 goto done;
--             }
--
--             /* Try again */
--             ret = sss_ini_config_file_open(init_data, config_file);
--            if (ret != EOK) {
--                DEBUG(SSSDBG_TRACE_FUNC,
--                      "sss_ini_config_file_open(default) failed: %s [%d]\n",
--                      strerror(ret), ret);
--                if (ret == ENOENT) {
--                    /* sss specific error denoting missing configuration file */
--                    ret = ERR_MISSING_CONF;
--                }
--                goto done;
--            }
-+        if (ret == ENOENT) {
-+            /* sss specific error denoting missing configuration file */
-+            ret = ERR_MISSING_CONF;
-         }
-+        goto done;
-     }
- 
-     ret = sss_ini_config_access_check(init_data);
-diff --git a/src/examples/sssd-shadowutils b/src/examples/sssd-shadowutils
-deleted file mode 100644
-index 626c7d075dfbf97dd91e259f94c6061689c83e9e..0000000000000000000000000000000000000000
---- a/src/examples/sssd-shadowutils
-+++ /dev/null
-@@ -1,6 +0,0 @@
--#%PAM-1.0
--auth        [success=done ignore=ignore default=die] pam_unix.so nullok try_first_pass
--auth        required      pam_deny.so
--
--account     required      pam_unix.so
--account     required      pam_permit.so
-diff --git a/src/examples/sssd.conf b/src/examples/sssd.conf
-deleted file mode 100644
-index a851dbb7ecd5c3220fbd6a946a6c7be2822dbd27..0000000000000000000000000000000000000000
---- a/src/examples/sssd.conf
-+++ /dev/null
-@@ -1,17 +0,0 @@
--[sssd]
--config_file_version = 2
--services = nss, pam
--domains = shadowutils
--
--[nss]
--
--[pam]
--
--[domain/shadowutils]
--id_provider = proxy
--proxy_lib_name = files
--
--auth_provider = proxy
--proxy_pam_target = sssd-shadowutils
--
--proxy_fast_alias = True
--- 
-2.7.4
-
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
new file mode 100644
index 0000000..772f302
--- /dev/null
+++ b/SOURCES/0127-SERVER_MODE-Update-sdap-lists-for-each-ad_ctx.patch
@@ -0,0 +1,81 @@
+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-TOOLS-Fix-a-typo-in-groupadd.patch b/SOURCES/0128-TOOLS-Fix-a-typo-in-groupadd.patch
deleted file mode 100644
index 4476896..0000000
--- a/SOURCES/0128-TOOLS-Fix-a-typo-in-groupadd.patch
+++ /dev/null
@@ -1,33 +0,0 @@
-From bac0f5a1d74d2dbedc5873592edbbdd791db2ede Mon Sep 17 00:00:00 2001
-From: Jakub Hrozek <jhrozek@redhat.com>
-Date: Tue, 6 Sep 2016 12:13:08 +0200
-Subject: [PATCH 128/135] TOOLS: Fix a typo in groupadd()
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Resolves:
-https://fedorahosted.org/sssd/ticket/3173
-
-Reviewed-by: Lukáš Slebodník <lslebodn@redhat.com>
-(cherry picked from commit 6be723a089a1e07a1cd19b4fa53fd142c13f0c69)
----
- src/tools/sss_sync_ops.c | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
-diff --git a/src/tools/sss_sync_ops.c b/src/tools/sss_sync_ops.c
-index a23a0b8c30366d2fb68554bfed184b8fce675e2b..39ef5bec96bd3942da8a8adfd21c99b03a77e551 100644
---- a/src/tools/sss_sync_ops.c
-+++ b/src/tools/sss_sync_ops.c
-@@ -657,7 +657,7 @@ int groupadd(struct ops_ctx *data)
-     int ret;
- 
-     data->sysdb_fqname = sss_create_internal_fqname(data,
--                                                    data->sysdb_fqname,
-+                                                    data->name,
-                                                     data->domain->name);
-     if (data->sysdb_fqname == NULL) {
-         return ENOMEM;
--- 
-2.7.4
-
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
new file mode 100644
index 0000000..26092f6
--- /dev/null
+++ b/SOURCES/0128-sss_nss_getlistbycert-return-results-from-multiple-d.patch
@@ -0,0 +1,348 @@
+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
new file mode 100644
index 0000000..d11d711
--- /dev/null
+++ b/SOURCES/0129-CACHE_REQ-Avoid-using-of-uninitialized-value.patch
@@ -0,0 +1,68 @@
+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/0129-TOOLS-sss_groupshow-did-not-work.patch b/SOURCES/0129-TOOLS-sss_groupshow-did-not-work.patch
deleted file mode 100644
index 7179654..0000000
--- a/SOURCES/0129-TOOLS-sss_groupshow-did-not-work.patch
+++ /dev/null
@@ -1,60 +0,0 @@
-From 7d8269e8a885cc0344dc78951d2880edac32a02c Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Michal=20=C5=BDidek?= <mzidek@redhat.com>
-Date: Tue, 6 Sep 2016 13:46:53 +0200
-Subject: [PATCH 129/135] TOOLS: sss_groupshow did not work
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-sss_groupshow used shortname to search
-in sysdb database. We have to u e sysdb_fqname
-(aka internal_fqname) format for all sysdb
-oprations.
-
-Resolves:
-https://fedorahosted.org/sssd/ticket/3175
-
-Reviewed-by: Lukáš Slebodník <lslebodn@redhat.com>
-(cherry picked from commit 5210c5d3a5a83b5d08396ee23d88f6ba0994097d)
----
- src/tools/sss_groupshow.c | 13 +++++++++++--
- 1 file changed, 11 insertions(+), 2 deletions(-)
-
-diff --git a/src/tools/sss_groupshow.c b/src/tools/sss_groupshow.c
-index 41d7475cef1093a4cb214ec4b017db59e6c26fe2..5870cc802c70366c47a0d30cb0d9795cf6035bc5 100644
---- a/src/tools/sss_groupshow.c
-+++ b/src/tools/sss_groupshow.c
-@@ -318,7 +318,7 @@ int group_show(TALLOC_CTX *mem_ctx,
-                struct sysdb_ctx *sysdb,
-                struct sss_domain_info *domain,
-                bool   recursive,
--               const char *name,
-+               const char *shortname,
-                struct group_info **res)
- {
-     struct group_info *root;
-@@ -326,11 +326,20 @@ int group_show(TALLOC_CTX *mem_ctx,
-     struct ldb_message *msg = NULL;
-     const char **group_members = NULL;
-     int nmembers = 0;
-+    char *sysdb_fqname = NULL;
-     int ret;
-     int i;
- 
-+    sysdb_fqname = sss_create_internal_fqname(mem_ctx,
-+                                              shortname,
-+                                              domain->name);
-+    if (sysdb_fqname == NULL) {
-+        return ENOMEM;
-+    }
-+
-     /* First, search for the root group */
--    ret = sysdb_search_group_by_name(mem_ctx, domain, name, attrs, &msg);
-+    ret = sysdb_search_group_by_name(mem_ctx, domain, sysdb_fqname, attrs,
-+                                     &msg);
-     if (ret) {
-         DEBUG(SSSDBG_OP_FAILURE,
-               "Search failed: %s (%d)\n", strerror(ret), ret);
--- 
-2.7.4
-
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
new file mode 100644
index 0000000..1063c09
--- /dev/null
+++ b/SOURCES/0130-CACHE_REQ-Ensure-the-domains-are-updated-for-filter-.patch
@@ -0,0 +1,163 @@
+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/0130-TESTS-sss_groupadd-groupshow-regressions.patch b/SOURCES/0130-TESTS-sss_groupadd-groupshow-regressions.patch
deleted file mode 100644
index 11e71cf..0000000
--- a/SOURCES/0130-TESTS-sss_groupadd-groupshow-regressions.patch
+++ /dev/null
@@ -1,78 +0,0 @@
-From cb355831d3f128688b33b9099ec047f8ef6a3234 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Michal=20=C5=BDidek?= <mzidek@redhat.com>
-Date: Tue, 6 Sep 2016 17:37:14 +0200
-Subject: [PATCH 130/135] TESTS: sss_groupadd/groupshow regressions
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Adds regression CI test for ticket #3173 and #3175.
-
-Resolves:
-https://fedorahosted.org/sssd/ticket/3173
-https://fedorahosted.org/sssd/ticket/3175
-
-Reviewed-by: Lukáš Slebodník <lslebodn@redhat.com>
-(cherry picked from commit 20c2d76d9430a1fc069531ff537df046a74c8f61)
----
- src/tests/intg/test_local_domain.py | 28 +++++++++++++++++++++++++++-
- 1 file changed, 27 insertions(+), 1 deletion(-)
-
-diff --git a/src/tests/intg/test_local_domain.py b/src/tests/intg/test_local_domain.py
-index 31834b5b9ab93329ac5eb76e3676d6f89f5b996e..56e3812b113b36301d1ec6049e5a1210d3070442 100644
---- a/src/tests/intg/test_local_domain.py
-+++ b/src/tests/intg/test_local_domain.py
-@@ -19,11 +19,13 @@
- import os
- import stat
- import pwd
-+import grp
- import time
- import config
- import signal
- import subprocess
- import pytest
-+import ent
- from util import unindent
- 
- 
-@@ -90,6 +92,11 @@ def assert_nonexistent_user(name):
-         pwd.getpwnam(name)
- 
- 
-+def assert_nonexistent_group(name):
-+    with pytest.raises(KeyError):
-+        grp.getgrnam(name)
-+
-+
- def test_wrong_LC_ALL(local_domain_only):
-     """
-     Regression test for ticket
-@@ -106,4 +113,23 @@ def test_wrong_LC_ALL(local_domain_only):
-     # sss_userdel must remove the user despite wrong LC_ALL
-     subprocess.check_call(["sss_userdel", "foo", "-R"])
-     assert_nonexistent_user("foo")
--    os.environ["LC_LOCAL"] = oldvalue
-+    os.environ["LC_ALL"] = oldvalue
-+
-+
-+def test_sss_group_add_show_del(local_domain_only):
-+    """
-+    Regression test for tickets
-+    https://fedorahosted.org/sssd/ticket/3173
-+    https://fedorahosted.org/sssd/ticket/3175
-+    """
-+
-+    subprocess.check_call(["sss_groupadd", "foo", "-g", "10001"])
-+
-+    "This should not raise KeyError"
-+    ent.assert_group_by_name("foo", dict(name="foo", gid=10001))
-+
-+    "sss_grupshow should return 0 with existing group name"
-+    subprocess.check_call(["sss_groupshow", "foo"])
-+
-+    subprocess.check_call(["sss_groupdel", "foo"])
-+    assert_nonexistent_group("foo")
--- 
-2.7.4
-
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
new file mode 100644
index 0000000..50c99ed
--- /dev/null
+++ b/SOURCES/0131-AD-SUBDOMAINS-Fix-search-bases-for-child-domains.patch
@@ -0,0 +1,102 @@
+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/0131-TOOLS-use-internal-fqdn-for-DN.patch b/SOURCES/0131-TOOLS-use-internal-fqdn-for-DN.patch
deleted file mode 100644
index 9b77d88..0000000
--- a/SOURCES/0131-TOOLS-use-internal-fqdn-for-DN.patch
+++ /dev/null
@@ -1,57 +0,0 @@
-From bfbad5b20c8a34b545cea8df6c937125edf0e42c Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Michal=20=C5=BDidek?= <mzidek@redhat.com>
-Date: Wed, 7 Sep 2016 10:58:25 +0200
-Subject: [PATCH 131/135] TOOLS: use internal fqdn for DN
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Use internal fqdn when creating sysdb group dn.
-
-Resolves:
-https://fedorahosted.org/sssd/ticket/3178
-
-Reviewed-by: Lukáš Slebodník <lslebodn@redhat.com>
-(cherry picked from commit 5e2142b66589e5e50cb404fc972ed5418bbaa772)
----
- src/tools/sss_sync_ops.c | 11 ++++++++++-
- 1 file changed, 10 insertions(+), 1 deletion(-)
-
-diff --git a/src/tools/sss_sync_ops.c b/src/tools/sss_sync_ops.c
-index 39ef5bec96bd3942da8a8adfd21c99b03a77e551..a0291baeada49b9f21e040a54e303214d5a46332 100644
---- a/src/tools/sss_sync_ops.c
-+++ b/src/tools/sss_sync_ops.c
-@@ -137,6 +137,7 @@ static int mod_groups_member(struct sss_domain_info *dom,
-     struct ldb_dn *parent_dn;
-     int ret;
-     int i;
-+    char *grp_sysdb_fqname = NULL;
- 
-     tmpctx = talloc_new(NULL);
-     if (!tmpctx) {
-@@ -145,13 +146,21 @@ static int mod_groups_member(struct sss_domain_info *dom,
- 
- /* FIXME: add transaction around loop */
-     for (i = 0; grouplist[i]; i++) {
-+        grp_sysdb_fqname = sss_create_internal_fqname(tmpctx, grouplist[i],
-+                                                      dom->name);
-+        if (grp_sysdb_fqname == NULL) {
-+            ret = ENOMEM;
-+            goto done;
-+        }
- 
--        parent_dn = sysdb_group_dn(tmpctx, dom, grouplist[i]);
-+        parent_dn = sysdb_group_dn(tmpctx, dom, grp_sysdb_fqname);
-         if (!parent_dn) {
-             ret = ENOMEM;
-             goto done;
-         }
- 
-+        talloc_free(grp_sysdb_fqname);
-+
-         ret = sysdb_mod_group_member(dom, member_dn, parent_dn, optype);
-         if (ret) {
-             goto done;
--- 
-2.7.4
-
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
new file mode 100644
index 0000000..7eecef9
--- /dev/null
+++ b/SOURCES/0132-KRB5-Advise-the-user-to-inspect-the-krb5_child.log-i.patch
@@ -0,0 +1,39 @@
+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/0132-TESTS-Test-for-sss_user-groupmod-a.patch b/SOURCES/0132-TESTS-Test-for-sss_user-groupmod-a.patch
deleted file mode 100644
index af34532..0000000
--- a/SOURCES/0132-TESTS-Test-for-sss_user-groupmod-a.patch
+++ /dev/null
@@ -1,66 +0,0 @@
-From 2eb23ddb5f03a71a329bf4874194455ff87e1806 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Michal=20=C5=BDidek?= <mzidek@redhat.com>
-Date: Wed, 7 Sep 2016 13:08:59 +0200
-Subject: [PATCH 132/135] TESTS: Test for sss_user/groupmod -a
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Regression tests for ticket #3178.
-
-Resolves:
-https://fedorahosted.org/sssd/ticket/3178
-
-Reviewed-by: Lukáš Slebodník <lslebodn@redhat.com>
-(cherry picked from commit 7fa4964d84f41bd80a6d971ffaeef87a7c2f19be)
----
- src/tests/intg/test_local_domain.py | 36 ++++++++++++++++++++++++++++++++++++
- 1 file changed, 36 insertions(+)
-
-diff --git a/src/tests/intg/test_local_domain.py b/src/tests/intg/test_local_domain.py
-index 56e3812b113b36301d1ec6049e5a1210d3070442..5e3e3d4d1cdc6db5d68a6e5b9d96d94c2c694b14 100644
---- a/src/tests/intg/test_local_domain.py
-+++ b/src/tests/intg/test_local_domain.py
-@@ -133,3 +133,39 @@ def test_sss_group_add_show_del(local_domain_only):
- 
-     subprocess.check_call(["sss_groupdel", "foo"])
-     assert_nonexistent_group("foo")
-+
-+
-+def test_add_local_user_to_local_group(local_domain_only):
-+    """
-+    Regression test for ticket
-+    https://fedorahosted.org/sssd/ticket/3178
-+    """
-+    subprocess.check_call(["sss_groupadd", "-g", "10009", "group10009"])
-+    subprocess.check_call(["sss_useradd", "-u", "10009", "-M", "user10009"])
-+    subprocess.check_call(["sss_usermod", "-a", "group10009", "user10009"])
-+
-+    ent.assert_group_by_name(
-+        "group10009",
-+        dict(name="group10009", passwd="*", gid=10009,
-+             mem=ent.contains_only("user10009")))
-+
-+
-+def test_add_local_group_to_local_group(local_domain_only):
-+    """
-+    Regression test for tickets
-+    https://fedorahosted.org/sssd/ticket/3178
-+    """
-+    subprocess.check_call(["sss_groupadd", "-g", "10009", "group_child"])
-+    subprocess.check_call(["sss_useradd", "-u", "10009", "-M", "user_child"])
-+    subprocess.check_call(["sss_usermod", "-a", "group_child", "user_child"])
-+
-+    subprocess.check_call(["sss_groupadd", "-g", "10008", "group_parent"])
-+    subprocess.check_call(
-+        ["sss_groupmod", "-a", "group_parent", "group_child"])
-+
-+    # User from child_group is member of parent_group, so child_group's
-+    # member must be also parent_group's member
-+    ent.assert_group_by_name(
-+        "group_parent",
-+        dict(name="group_parent", passwd="*", gid=10008,
-+             mem=ent.contains_only("user_child")))
--- 
-2.7.4
-
diff --git a/SOURCES/0133-TOOLS-sss_mc_refresh_nested_group-short-fqname-usage.patch b/SOURCES/0133-TOOLS-sss_mc_refresh_nested_group-short-fqname-usage.patch
deleted file mode 100644
index 6f8b54e..0000000
--- a/SOURCES/0133-TOOLS-sss_mc_refresh_nested_group-short-fqname-usage.patch
+++ /dev/null
@@ -1,137 +0,0 @@
-From 1a1aaf46d1ee3ae4c9346c5c492520257c7c1b42 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Michal=20=C5=BDidek?= <mzidek@redhat.com>
-Date: Wed, 7 Sep 2016 14:43:13 +0200
-Subject: [PATCH 133/135] TOOLS: sss_mc_refresh_nested_group short/fqname usage
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-We use shortname to refresh memory cache, but in case of nested groups,
-we used internal_fqname to refresh parent groups.
-
-We also wrongly used the shortname for sysdb_search operation.
-Which caused error message to be printed when sss_usermod -a or
-sss_groupmod -a where called.
-
-Reviewed-by: Lukáš Slebodník <lslebodn@redhat.com>
----
- src/tools/tools_mc_util.c | 66 +++++++++++++++++++++++++++++++++--------------
- 1 file changed, 47 insertions(+), 19 deletions(-)
-
-diff --git a/src/tools/tools_mc_util.c b/src/tools/tools_mc_util.c
-index 2516a1981ddd965d4cae8c469ed79aaef8fa7193..716e3760f67d958f2139adbb49998d9e352d23f4 100644
---- a/src/tools/tools_mc_util.c
-+++ b/src/tools/tools_mc_util.c
-@@ -293,62 +293,90 @@ errno_t sss_mc_refresh_group(const char *groupname)
-     return sss_mc_refresh_ent(groupname, SSS_TOOLS_GROUP);
- }
- 
--errno_t sss_mc_refresh_nested_group(struct tools_ctx *tctx,
--                                    const char *name)
-+static errno_t sss_mc_refresh_nested_group(struct tools_ctx *tctx,
-+                                           const char *shortname)
- {
-     errno_t ret;
--    struct ldb_message *msg;
-+    struct ldb_message *msg = NULL;
-     struct ldb_message_element *el;
-     const char *attrs[] = { SYSDB_MEMBEROF,
-                             SYSDB_NAME,
-                             NULL };
-     size_t i;
--    char *parent_name;
-+    char *parent_internal_name;
-+    char *parent_outname;
-+    char *internal_name;
-+    TALLOC_CTX *tmpctx;
- 
--    ret = sss_mc_refresh_group(name);
-+    tmpctx = talloc_new(tctx);
-+    if (tmpctx == NULL) {
-+        return ENOMEM;
-+    }
-+
-+    internal_name = sss_create_internal_fqname(tmpctx, shortname,
-+                                               tctx->local->name);
-+    if (internal_name == NULL) {
-+        ret = ENOMEM;
-+        goto done;
-+    }
-+
-+    ret = sss_mc_refresh_group(shortname);
-     if (ret != EOK) {
-         DEBUG(SSSDBG_MINOR_FAILURE,
--              "Cannot refresh group %s from memory cache\n", name);
-+              "Cannot refresh group %s from memory cache\n", shortname);
-         /* try to carry on */
-     }
- 
--    ret = sysdb_search_group_by_name(tctx, tctx->local, name, attrs, &msg);
-+    ret = sysdb_search_group_by_name(tmpctx, tctx->local, internal_name, attrs,
-+                                     &msg);
-     if (ret) {
-         DEBUG(SSSDBG_OP_FAILURE,
-                "Search failed: %s (%d)\n", strerror(ret), ret);
--        return ret;
-+        goto done;
-     }
- 
-     el = ldb_msg_find_element(msg, SYSDB_MEMBEROF);
-     if (!el || el->num_values == 0) {
--        DEBUG(SSSDBG_TRACE_INTERNAL, "Group %s has no parents\n", name);
--        talloc_free(msg);
--        return EOK;
-+        DEBUG(SSSDBG_TRACE_INTERNAL, "Group %s has no parents\n",
-+              internal_name);
-+        ret = EOK;
-+        goto done;
-     }
- 
-     /* This group is nested. We need to invalidate all its parents, too */
-     for (i=0; i < el->num_values; i++) {
--        ret = sysdb_group_dn_name(tctx->sysdb, tctx,
-+        ret = sysdb_group_dn_name(tctx->sysdb, tmpctx,
-                                   (const char *) el->values[i].data,
--                                  &parent_name);
-+                                  &parent_internal_name);
-         if (ret != EOK) {
-             DEBUG(SSSDBG_MINOR_FAILURE, "Malformed DN [%s]? Skipping\n",
-                   (const char *) el->values[i].data);
--            talloc_free(parent_name);
-+            talloc_free(parent_internal_name);
-             continue;
-         }
- 
--        ret = sss_mc_refresh_group(parent_name);
--        talloc_free(parent_name);
-+        parent_outname = sss_output_name(tmpctx, parent_internal_name,
-+                                         tctx->local->case_preserve, 0);
-+        if (parent_outname == NULL) {
-+            ret = ENOMEM;
-+            goto done;
-+        }
-+
-+        ret = sss_mc_refresh_group(parent_outname);
-+        talloc_free(parent_internal_name);
-+        talloc_free(parent_outname);
-         if (ret != EOK) {
-             DEBUG(SSSDBG_MINOR_FAILURE,
--                  "Cannot refresh group %s from memory cache\n", name);
-+                  "Cannot refresh group %s from memory cache\n", parent_outname);
-             /* try to carry on */
-         }
-     }
- 
--    talloc_free(msg);
--    return EOK;
-+    ret = EOK;
-+
-+done:
-+    talloc_free(tmpctx);
-+    return ret;
- }
- 
- errno_t sss_mc_refresh_grouplist(struct tools_ctx *tctx,
--- 
-2.7.4
-
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
new file mode 100644
index 0000000..52aa5dc
--- /dev/null
+++ b/SOURCES/0133-cache_req-use-the-right-negative-cache-for-initgroup.patch
@@ -0,0 +1,40 @@
+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-TESTS-Add-FQDN-variants-for-some-tests.patch b/SOURCES/0134-TESTS-Add-FQDN-variants-for-some-tests.patch
deleted file mode 100644
index 109d2ef..0000000
--- a/SOURCES/0134-TESTS-Add-FQDN-variants-for-some-tests.patch
+++ /dev/null
@@ -1,116 +0,0 @@
-From 28db1765c40137e9452b4aa27281a1deb25d1d36 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Michal=20=C5=BDidek?= <mzidek@redhat.com>
-Date: Wed, 7 Sep 2016 15:00:12 +0200
-Subject: [PATCH 134/135] TESTS: Add FQDN variants for some tests
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Adds FQDN variants of some already existing tests.
-
-Reviewed-by: Lukáš Slebodník <lslebodn@redhat.com>
----
- src/tests/intg/test_local_domain.py | 83 +++++++++++++++++++++++++++++++++++++
- 1 file changed, 83 insertions(+)
-
-diff --git a/src/tests/intg/test_local_domain.py b/src/tests/intg/test_local_domain.py
-index 5e3e3d4d1cdc6db5d68a6e5b9d96d94c2c694b14..b34e4a3d31cdbc1dc257d8fffcf0f5a07803b20c 100644
---- a/src/tests/intg/test_local_domain.py
-+++ b/src/tests/intg/test_local_domain.py
-@@ -87,6 +87,27 @@ def local_domain_only(request):
-     return None
- 
- 
-+@pytest.fixture
-+def local_domain_only_fqdn(request):
-+    conf = unindent("""\
-+        [sssd]
-+        domains = LOCAL
-+        services = nss
-+
-+        [nss]
-+        memcache_timeout = 0
-+
-+        [domain/LOCAL]
-+        id_provider = local
-+        min_id = 10000
-+        max_id = 20000
-+        use_fully_qualified_names = True
-+    """).format(**locals())
-+    create_conf_fixture(request, conf)
-+    create_sssd_fixture(request)
-+    return None
-+
-+
- def assert_nonexistent_user(name):
-     with pytest.raises(KeyError):
-         pwd.getpwnam(name)
-@@ -169,3 +190,65 @@ def test_add_local_group_to_local_group(local_domain_only):
-         "group_parent",
-         dict(name="group_parent", passwd="*", gid=10008,
-              mem=ent.contains_only("user_child")))
-+
-+
-+def test_sss_group_add_show_del_fqdn(local_domain_only_fqdn):
-+    """
-+    Regression test for tickets
-+    https://fedorahosted.org/sssd/ticket/3173
-+    https://fedorahosted.org/sssd/ticket/3175
-+    """
-+
-+    subprocess.check_call(["sss_groupadd", "foo@LOCAL", "-g", "10001"])
-+
-+    "This should not raise KeyError"
-+    ent.assert_group_by_name("foo@LOCAL", dict(name="foo@LOCAL", gid=10001))
-+
-+    "sss_grupshow should return 0 with existing group name"
-+    subprocess.check_call(["sss_groupshow", "foo@LOCAL"])
-+
-+    subprocess.check_call(["sss_groupdel", "foo@LOCAL"])
-+    assert_nonexistent_group("foo@LOCAL")
-+
-+
-+def test_add_local_user_to_local_group_fqdn(local_domain_only_fqdn):
-+    """
-+    Regression test for ticket
-+    https://fedorahosted.org/sssd/ticket/3178
-+    """
-+    subprocess.check_call(
-+        ["sss_groupadd", "-g", "10009", "group10009@LOCAL"])
-+    subprocess.check_call(
-+        ["sss_useradd", "-u", "10009", "-M", "user10009@LOCAL"])
-+    subprocess.check_call(
-+        ["sss_usermod", "-a", "group10009@LOCAL", "user10009@LOCAL"])
-+
-+    ent.assert_group_by_name(
-+        "group10009@LOCAL",
-+        dict(name="group10009@LOCAL", passwd="*", gid=10009,
-+             mem=ent.contains_only("user10009@LOCAL")))
-+
-+
-+def test_add_local_group_to_local_group_fqdn(local_domain_only_fqdn):
-+    """
-+    Regression test for tickets
-+    https://fedorahosted.org/sssd/ticket/3178
-+    """
-+    subprocess.check_call(
-+        ["sss_groupadd", "-g", "10009", "group_child@LOCAL"])
-+    subprocess.check_call(
-+        ["sss_useradd", "-u", "10009", "-M", "user_child@LOCAL"])
-+    subprocess.check_call(
-+        ["sss_usermod", "-a", "group_child@LOCAL", "user_child@LOCAL"])
-+
-+    subprocess.check_call(
-+        ["sss_groupadd", "-g", "10008", "group_parent@LOCAL"])
-+    subprocess.check_call(
-+        ["sss_groupmod", "-a", "group_parent@LOCAL", "group_child@LOCAL"])
-+
-+    # User from child_group is member of parent_group, so child_group's
-+    # member must be also parent_group's member
-+    ent.assert_group_by_name(
-+        "group_parent@LOCAL",
-+        dict(name="group_parent@LOCAL", passwd="*", gid=10008,
-+             mem=ent.contains_only("user_child@LOCAL")))
--- 
-2.7.4
-
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
new file mode 100644
index 0000000..5fa1c16
--- /dev/null
+++ b/SOURCES/0134-test-make-sure-p11_child-is-build-for-pam-srv-tests.patch
@@ -0,0 +1,31 @@
+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-KRB5-Send-the-output-username-not-internal-fqname-to.patch b/SOURCES/0135-KRB5-Send-the-output-username-not-internal-fqname-to.patch
deleted file mode 100644
index a3368d4..0000000
--- a/SOURCES/0135-KRB5-Send-the-output-username-not-internal-fqname-to.patch
+++ /dev/null
@@ -1,155 +0,0 @@
-From 0f0480dd1c227a841542d621a778e23cf637a644 Mon Sep 17 00:00:00 2001
-From: Jakub Hrozek <jhrozek@redhat.com>
-Date: Wed, 7 Sep 2016 12:07:36 +0200
-Subject: [PATCH 135/135] KRB5: Send the output username, not internal fqname
- to krb5_child
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-krb5_child calls krb5_kuserok() during the access phase which checks if
-a particular user is allowed to authenticate as a particular principal.
-We used to pass the internal fqname to krb5_kuserok() which broke the
-functionality and all users were denied access.
-
-This patch changes that to send the 'output' username to krb5_child,
-because that's the username the system receives through getpwnam() or
-getpwuid() anyway. The patch also adds a new structure member fo the
-krb5child_req structure to avoid reusing the pd->user variable but have
-an explicit one that serves as the input for the child process.
-
-Resolves:
-https://fedorahosted.org/sssd/ticket/3172
-
-Reviewed-by: Lukáš Slebodník <lslebodn@redhat.com>
----
- src/providers/krb5/krb5_access.c        | 10 ++++++++--
- src/providers/krb5/krb5_auth.c          | 18 ++++++++++++++----
- src/providers/krb5/krb5_auth.h          |  9 ++++++---
- src/providers/krb5/krb5_child_handler.c |  4 ++--
- 4 files changed, 30 insertions(+), 11 deletions(-)
-
-diff --git a/src/providers/krb5/krb5_access.c b/src/providers/krb5/krb5_access.c
-index 3afb90150d77ef4ab2c1b5b79abb95d68eb131f6..be9068c0f9180f8de0de259aae368534effaf7fb 100644
---- a/src/providers/krb5/krb5_access.c
-+++ b/src/providers/krb5/krb5_access.c
-@@ -51,6 +51,7 @@ struct tevent_req *krb5_access_send(TALLOC_CTX *mem_ctx,
-     int ret;
-     const char **attrs;
-     struct ldb_result *res;
-+    struct sss_domain_info *dom;
- 
-     req = tevent_req_create(mem_ctx, &state, struct krb5_access_state);
-     if (req == NULL) {
-@@ -64,8 +65,13 @@ struct tevent_req *krb5_access_send(TALLOC_CTX *mem_ctx,
-     state->krb5_ctx = krb5_ctx;
-     state->access_allowed = false;
- 
--    ret = krb5_setup(state, pd, krb5_ctx, be_ctx->domain->case_sensitive,
--                     &state->kr);
-+    ret = get_domain_or_subdomain(be_ctx, pd->domain, &dom);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_OP_FAILURE, "get_domain_or_subdomain failed.\n");
-+        goto done;
-+    }
-+
-+    ret = krb5_setup(state, pd, dom, krb5_ctx, &state->kr);
-     if (ret != EOK) {
-         DEBUG(SSSDBG_CRIT_FAILURE, "krb5_setup failed.\n");
-         goto done;
-diff --git a/src/providers/krb5/krb5_auth.c b/src/providers/krb5/krb5_auth.c
-index dabf55cf24a8afda16fee6697120c7c6f088b796..f0f2280022a3ee951ccfa0040b616c48c3b25706 100644
---- a/src/providers/krb5/krb5_auth.c
-+++ b/src/providers/krb5/krb5_auth.c
-@@ -174,8 +174,10 @@ done:
-     return ret;
- }
- 
--errno_t krb5_setup(TALLOC_CTX *mem_ctx, struct pam_data *pd,
--                   struct krb5_ctx *krb5_ctx, bool cs,
-+errno_t krb5_setup(TALLOC_CTX *mem_ctx,
-+                   struct pam_data *pd,
-+                   struct sss_domain_info *dom,
-+                   struct krb5_ctx *krb5_ctx,
-                    struct krb5child_req **_krb5_req)
- {
-     struct krb5child_req *kr;
-@@ -201,13 +203,21 @@ errno_t krb5_setup(TALLOC_CTX *mem_ctx, struct pam_data *pd,
-     kr->krb5_ctx = krb5_ctx;
- 
-     ret = get_krb_primary(krb5_ctx->name_to_primary,
--                          pd->user, cs, &mapped_name);
-+                          pd->user, dom->case_sensitive, &mapped_name);
-     if (ret == EOK) {
-         DEBUG(SSSDBG_TRACE_FUNC, "Setting mapped name to: %s\n", mapped_name);
-         kr->user = mapped_name;
-+        kr->kuserok_user = mapped_name;
-     } else if (ret == ENOENT) {
-         DEBUG(SSSDBG_TRACE_ALL, "No mapping for: %s\n", pd->user);
-         kr->user = pd->user;
-+
-+        kr->kuserok_user = sss_output_name(kr, kr->user,
-+                                           dom->case_sensitive, 0);
-+        if (kr->kuserok_user == NULL) {
-+            ret = ENOMEM;
-+            goto done;
-+        }
-     } else {
-         DEBUG(SSSDBG_CRIT_FAILURE, "get_krb_primary failed - %s:[%d]\n",
-               sss_strerror(ret), ret);
-@@ -534,7 +544,7 @@ struct tevent_req *krb5_auth_send(TALLOC_CTX *mem_ctx,
-     attrs[6] = SYSDB_AUTH_TYPE;
-     attrs[7] = NULL;
- 
--    ret = krb5_setup(state, pd, krb5_ctx, state->domain->case_sensitive,
-+    ret = krb5_setup(state, pd, state->domain, krb5_ctx,
-                      &state->kr);
-     if (ret != EOK) {
-         DEBUG(SSSDBG_CRIT_FAILURE, "krb5_setup failed.\n");
-diff --git a/src/providers/krb5/krb5_auth.h b/src/providers/krb5/krb5_auth.h
-index dbad061f0203b6383daeeab506bf9950d892ea4b..11bb595833269177b7e2c5fc6372d6a6fb6d93d2 100644
---- a/src/providers/krb5/krb5_auth.h
-+++ b/src/providers/krb5/krb5_auth.h
-@@ -57,11 +57,14 @@ struct krb5child_req {
-     bool send_pac;
- 
-     const char *user;
-+    const char *kuserok_user;
- };
- 
--errno_t krb5_setup(TALLOC_CTX *mem_ctx, struct pam_data *pd,
--                   struct krb5_ctx *krb5_ctx, bool case_sensitive,
--                   struct krb5child_req **krb5_req);
-+errno_t krb5_setup(TALLOC_CTX *mem_ctx,
-+                   struct pam_data *pd,
-+                   struct sss_domain_info *dom,
-+                   struct krb5_ctx *krb5_ctx,
-+                   struct krb5child_req **_krb5_req);
- 
- struct tevent_req *
- krb5_pam_handler_send(TALLOC_CTX *mem_ctx,
-diff --git a/src/providers/krb5/krb5_child_handler.c b/src/providers/krb5/krb5_child_handler.c
-index 09a1e5f59494a5c07d5c9eefb94919ca9389cb27..1eec7261f00976b3725fee9323755edecd5409a5 100644
---- a/src/providers/krb5/krb5_child_handler.c
-+++ b/src/providers/krb5/krb5_child_handler.c
-@@ -161,7 +161,7 @@ static errno_t create_send_buffer(struct krb5child_req *kr,
-     }
- 
-     if (kr->pd->cmd == SSS_PAM_ACCT_MGMT) {
--        username_len = strlen(kr->pd->user);
-+        username_len = strlen(kr->kuserok_user);
-         buf->size += sizeof(uint32_t) + username_len;
-     }
- 
-@@ -217,7 +217,7 @@ static errno_t create_send_buffer(struct krb5child_req *kr,
- 
-     if (kr->pd->cmd == SSS_PAM_ACCT_MGMT) {
-         SAFEALIGN_SET_UINT32(&buf->data[rp], username_len, &rp);
--        safealign_memcpy(&buf->data[rp], kr->pd->user, username_len, &rp);
-+        safealign_memcpy(&buf->data[rp], kr->kuserok_user, username_len, &rp);
-     }
- 
-     *io_buf = buf;
--- 
-2.7.4
-
diff --git a/SOURCES/0135-pam-properly-support-UPN-logon-names.patch b/SOURCES/0135-pam-properly-support-UPN-logon-names.patch
new file mode 100644
index 0000000..54d6529
--- /dev/null
+++ b/SOURCES/0135-pam-properly-support-UPN-logon-names.patch
@@ -0,0 +1,163 @@
+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
new file mode 100644
index 0000000..3cf4420
--- /dev/null
+++ b/SOURCES/0136-KCM-Fix-the-per-client-serialization-queue.patch
@@ -0,0 +1,334 @@
+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/0136-LDAP-Return-partial-results-from-adminlimit-exceeded.patch b/SOURCES/0136-LDAP-Return-partial-results-from-adminlimit-exceeded.patch
deleted file mode 100644
index 806432b..0000000
--- a/SOURCES/0136-LDAP-Return-partial-results-from-adminlimit-exceeded.patch
+++ /dev/null
@@ -1,42 +0,0 @@
-From abbbcb79ff0c5b82d7a3acb324c3d44f87479837 Mon Sep 17 00:00:00 2001
-From: Jakub Hrozek <jhrozek@redhat.com>
-Date: Mon, 12 Sep 2016 17:36:09 +0200
-Subject: [PATCH 136/140] LDAP: Return partial results from adminlimit exceeded
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Resolves:
-    https://fedorahosted.org/sssd/ticket/3185
-
-Since commit c420ce830ac0b0b288a2a887ec2cfce5c748018c we try to move to
-the next server on any error on the connection, which in case there is
-only one server sends SSSD offline.
-
-It's more graceful to try to process the results, same as we already do
-with sizelimit exceeded.
-
-Reviewed-by: Michal Židek <mzidek@redhat.com>
-Reviewed-by: Lukáš Slebodník <lslebodn@redhat.com>
-(cherry picked from commit 3319d964721396c07daba383ded6aaaf33ed6e3b)
----
- src/providers/ldap/sdap_async.c | 3 ++-
- 1 file changed, 2 insertions(+), 1 deletion(-)
-
-diff --git a/src/providers/ldap/sdap_async.c b/src/providers/ldap/sdap_async.c
-index 4195ba95d911f3956f8cca665310b4b92091e6cd..b8e04b9d8464d7c2c681080cba456c34b93b923a 100644
---- a/src/providers/ldap/sdap_async.c
-+++ b/src/providers/ldap/sdap_async.c
-@@ -1526,7 +1526,8 @@ static void sdap_get_generic_op_finished(struct sdap_op *op,
-                   sss_ldap_err2string(result), result,
-                   errmsg ? errmsg : "no errmsg set");
- 
--        if (result == LDAP_SIZELIMIT_EXCEEDED) {
-+        if (result == LDAP_SIZELIMIT_EXCEEDED
-+                || result == LDAP_ADMINLIMIT_EXCEEDED) {
-             /* Try to return what we've got */
- 
-             if ( ! (state->flags & SDAP_SRCH_FLG_SIZELIMIT_SILENT)) {
--- 
-2.7.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
new file mode 100644
index 0000000..25a3528
--- /dev/null
+++ b/SOURCES/0137-TESTS-Add-a-test-for-parallel-execution-of-klist.patch
@@ -0,0 +1,79 @@
+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/0137-TOOLS-sss_groupshow-fails-to-show-MPG.patch b/SOURCES/0137-TOOLS-sss_groupshow-fails-to-show-MPG.patch
deleted file mode 100644
index 285ef6e..0000000
--- a/SOURCES/0137-TOOLS-sss_groupshow-fails-to-show-MPG.patch
+++ /dev/null
@@ -1,60 +0,0 @@
-From 8125ddd951a9e66fb3b11e16489e003d32ce4890 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Michal=20=C5=BDidek?= <mzidek@redhat.com>
-Date: Mon, 12 Sep 2016 19:22:56 +0200
-Subject: [PATCH 137/140] TOOLS: sss_groupshow fails to show MPG
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-The MPG search uses it's own search function
-that used sysdb operation with shortname,
-but it expects internal fqname.
-
-Resolves:
-https://fedorahosted.org/sssd/ticket/3184
-
-Reviewed-by: Lukáš Slebodník <lslebodn@redhat.com>
-(cherry picked from commit 812bed08943df8bf3fd1ff9eabcaf5bedc635c92)
----
- src/tools/sss_groupshow.c | 12 ++++++++++--
- 1 file changed, 10 insertions(+), 2 deletions(-)
-
-diff --git a/src/tools/sss_groupshow.c b/src/tools/sss_groupshow.c
-index 5870cc802c70366c47a0d30cb0d9795cf6035bc5..00f6f12939b6bef2dd10085f8cf99304e87f1211 100644
---- a/src/tools/sss_groupshow.c
-+++ b/src/tools/sss_groupshow.c
-@@ -553,13 +553,14 @@ int group_show_recurse(TALLOC_CTX *mem_ctx,
- 
- static int group_show_mpg(TALLOC_CTX *mem_ctx,
-                           struct sss_domain_info *domain,
--                          const char *name,
-+                          const char *shortname,
-                           struct group_info **res)
- {
-     const char *attrs[] = GROUP_SHOW_MPG_ATTRS;
-     struct ldb_message *msg;
-     struct group_info *info;
-     int ret;
-+    char *sysdb_fqname;
- 
-     info = talloc_zero(mem_ctx, struct group_info);
-     if (!info) {
-@@ -567,7 +568,14 @@ static int group_show_mpg(TALLOC_CTX *mem_ctx,
-         goto fail;
-     }
- 
--    ret = sysdb_search_user_by_name(info, domain, name, attrs, &msg);
-+    sysdb_fqname = sss_create_internal_fqname(mem_ctx,
-+                                              shortname,
-+                                              domain->name);
-+    if (sysdb_fqname == NULL) {
-+        return ENOMEM;
-+    }
-+
-+    ret = sysdb_search_user_by_name(info, domain, sysdb_fqname, attrs, &msg);
-     if (ret) {
-         DEBUG(SSSDBG_OP_FAILURE,
-               "Search failed: %s (%d)\n", strerror(ret), ret);
--- 
-2.7.4
-
diff --git a/SOURCES/0138-TESTS-sss_groupshow-with-MPG.patch b/SOURCES/0138-TESTS-sss_groupshow-with-MPG.patch
deleted file mode 100644
index e273877..0000000
--- a/SOURCES/0138-TESTS-sss_groupshow-with-MPG.patch
+++ /dev/null
@@ -1,55 +0,0 @@
-From 6eb330b5370db40a55d18de2d932601d6b3128b2 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Michal=20=C5=BDidek?= <mzidek@redhat.com>
-Date: Mon, 12 Sep 2016 19:25:13 +0200
-Subject: [PATCH 138/140] TESTS: sss_groupshow with MPG
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Regression test for ticket #3184
-
-Resolves:
-https://fedorahosted.org/sssd/ticket/3184
-
-Reviewed-by: Lukáš Slebodník <lslebodn@redhat.com>
-(cherry picked from commit bb14556c1df503314644fc424fbbf95759791db9)
----
- src/tests/intg/test_local_domain.py | 22 ++++++++++++++++++++++
- 1 file changed, 22 insertions(+)
-
-diff --git a/src/tests/intg/test_local_domain.py b/src/tests/intg/test_local_domain.py
-index b34e4a3d31cdbc1dc257d8fffcf0f5a07803b20c..8e1d6fb2b69f5e6e033ae06d4bd52cc88e54872b 100644
---- a/src/tests/intg/test_local_domain.py
-+++ b/src/tests/intg/test_local_domain.py
-@@ -118,6 +118,28 @@ def assert_nonexistent_group(name):
-         grp.getgrnam(name)
- 
- 
-+def test_groupshow_mpg(local_domain_only):
-+    """
-+    Regression test for ticket
-+    https://fedorahosted.org/sssd/ticket/3184
-+    """
-+    subprocess.check_call(["sss_useradd", "foo", "-M"])
-+
-+    # The user's mpg has to be found (should return 0)
-+    subprocess.check_call(["sss_groupshow", "foo"])
-+
-+
-+def test_groupshow_mpg_fqdn(local_domain_only_fqdn):
-+    """
-+    Regression test for ticket (fq variant)
-+    https://fedorahosted.org/sssd/ticket/3184
-+    """
-+    subprocess.check_call(["sss_useradd", "foo@LOCAL", "-M"])
-+
-+    # The user's mpg has to be found (should return 0)
-+    subprocess.check_call(["sss_groupshow", "foo@LOCAL"])
-+
-+
- def test_wrong_LC_ALL(local_domain_only):
-     """
-     Regression test for ticket
--- 
-2.7.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
new file mode 100644
index 0000000..f9689ea
--- /dev/null
+++ b/SOURCES/0138-ipa-filter-IPA-users-from-extdom-lookups-by-certific.patch
@@ -0,0 +1,115 @@
+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-TOOLS-sss_override-without-name-override.patch b/SOURCES/0139-TOOLS-sss_override-without-name-override.patch
deleted file mode 100644
index b9114ef..0000000
--- a/SOURCES/0139-TOOLS-sss_override-without-name-override.patch
+++ /dev/null
@@ -1,67 +0,0 @@
-From e56e1396bab69d9498f4ec6a36e9e228ded90116 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Michal=20=C5=BDidek?= <mzidek@redhat.com>
-Date: Wed, 7 Sep 2016 17:09:53 +0200
-Subject: [PATCH 139/140] TOOLS: sss_override without name override
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-sss_override failed to export user/group overrides
-if user had no overrides for name.
-
-Resolves:
-https://fedorahosted.org/sssd/ticket/3179
-
-Reviewed-by: Pavel Březina <pbrezina@redhat.com>
-(cherry picked from commit 07e7683f5a86991feaa764e2055116554ada1b93)
----
- src/tools/sss_override.c | 24 ++++++++++++++----------
- 1 file changed, 14 insertions(+), 10 deletions(-)
-
-diff --git a/src/tools/sss_override.c b/src/tools/sss_override.c
-index d41da52e69acdb67b5a6d624254e3b89a8aa27b8..212bf9ab84b20d4777fc2601359fad58596bb7c4 100644
---- a/src/tools/sss_override.c
-+++ b/src/tools/sss_override.c
-@@ -1159,12 +1159,14 @@ list_user_overrides(TALLOC_CTX *mem_ctx,
-         }
- 
-         fqname = ldb_msg_find_attr_as_string(msgs[i], SYSDB_NAME, NULL);
--        ret = sss_parse_internal_fqname(tmp_ctx, fqname, &name, NULL);
--        if (ret != EOK) {
--            ret = ERR_WRONG_NAME_FORMAT;
--            goto done;
-+        if (fqname != NULL) {
-+            ret = sss_parse_internal_fqname(tmp_ctx, fqname, &name, NULL);
-+            if (ret != EOK) {
-+                ret = ERR_WRONG_NAME_FORMAT;
-+                goto done;
-+            }
-+            objs[i].name = talloc_steal(objs, name);
-         }
--        objs[i].name = talloc_steal(objs, name);
- 
-         objs[i].uid = ldb_msg_find_attr_as_uint(msgs[i], SYSDB_UIDNUM, 0);
-         objs[i].gid = ldb_msg_find_attr_as_uint(msgs[i], SYSDB_GIDNUM, 0);
-@@ -1248,12 +1250,14 @@ list_group_overrides(TALLOC_CTX *mem_ctx,
-         talloc_steal(objs, objs[i].orig_name);
- 
-         fqname = ldb_msg_find_attr_as_string(msgs[i], SYSDB_NAME, NULL);
--        ret = sss_parse_internal_fqname(tmp_ctx, fqname, &name, NULL);
--        if (ret != EOK) {
--            ret = ERR_WRONG_NAME_FORMAT;
--            goto done;
-+        if (fqname != NULL) {
-+            ret = sss_parse_internal_fqname(tmp_ctx, fqname, &name, NULL);
-+            if (ret != EOK) {
-+                ret = ERR_WRONG_NAME_FORMAT;
-+                goto done;
-+            }
-+            objs[i].name = talloc_steal(objs, name);
-         }
--        objs[i].name = talloc_steal(objs, name);
- 
-         objs[i].gid = ldb_msg_find_attr_as_uint(msgs[i], SYSDB_GIDNUM, 0);
-     }
--- 
-2.7.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
new file mode 100644
index 0000000..259a343
--- /dev/null
+++ b/SOURCES/0139-krb5-accept-changed-principal-if-krb5_canonicalize-T.patch
@@ -0,0 +1,50 @@
+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
new file mode 100644
index 0000000..e6a08b1
--- /dev/null
+++ b/SOURCES/0140-IPA-Avoid-using-uninitialized-ret-value-when-skippin.patch
@@ -0,0 +1,30 @@
+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/0140-TEST-Add-regression-test-for-ticket-3179.patch b/SOURCES/0140-TEST-Add-regression-test-for-ticket-3179.patch
deleted file mode 100644
index 18f80b9..0000000
--- a/SOURCES/0140-TEST-Add-regression-test-for-ticket-3179.patch
+++ /dev/null
@@ -1,203 +0,0 @@
-From d3e93eafcf3e484ee8ec092c59914018b42ea4ad Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Michal=20=C5=BDidek?= <mzidek@redhat.com>
-Date: Wed, 7 Sep 2016 18:23:16 +0200
-Subject: [PATCH 140/140] TEST: Add regression test for ticket #3179
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Resolves:
-https://fedorahosted.org/sssd/ticket/3179
-
-Reviewed-by: Lukáš Slebodník <lslebodn@redhat.com>
-Reviewed-by: Pavel Březina <pbrezina@redhat.com>
-(cherry picked from commit 1c72723cde8bea0d390b928c7cd29e48e7a7deab)
----
- src/tests/intg/ldap_local_override_test.py | 126 ++++++++++++++++++++++++++---
- 1 file changed, 114 insertions(+), 12 deletions(-)
-
-diff --git a/src/tests/intg/ldap_local_override_test.py b/src/tests/intg/ldap_local_override_test.py
-index 046535c7727d0f2271f4b974f68ba0722222982b..832849713e1185e836f3dd6decf2deac79ab98dd 100644
---- a/src/tests/intg/ldap_local_override_test.py
-+++ b/src/tests/intg/ldap_local_override_test.py
-@@ -205,27 +205,38 @@ def assert_user_default():
-     ent.assert_passwd_by_name('user2@LDAP', user2)
- 
- 
--def assert_user_overriden():
-+def assert_user_overriden(override_name=True):
- 
--    user1 = dict(name='ov_user1', passwd='*', uid=10010, gid=20010,
-+    if override_name:
-+        name1 = "ov_user1"
-+        name2 = "ov_user2"
-+    else:
-+        name1 = "user1"
-+        name2 = "user2"
-+
-+    user1 = dict(name=name1, passwd='*', uid=10010, gid=20010,
-                  gecos='Overriden User 1',
-                  dir='/home/ov/user1',
-                  shell='/bin/ov_user1_shell')
- 
--    user2 = dict(name='ov_user2', passwd='*', uid=10020, gid=20020,
-+    user2 = dict(name=name2, passwd='*', uid=10020, gid=20020,
-                  gecos='Overriden User 2',
-                  dir='/home/ov/user2',
-                  shell='/bin/ov_user2_shell')
- 
-     ent.assert_passwd_by_name('user1', user1)
-     ent.assert_passwd_by_name('user1@LDAP', user1)
--    ent.assert_passwd_by_name('ov_user1', user1)
--    ent.assert_passwd_by_name('ov_user1@LDAP', user1)
-+
-+    if override_name:
-+        ent.assert_passwd_by_name('ov_user1', user1)
-+        ent.assert_passwd_by_name('ov_user1@LDAP', user1)
- 
-     ent.assert_passwd_by_name('user2', user2)
-     ent.assert_passwd_by_name('user2@LDAP', user2)
--    ent.assert_passwd_by_name('ov_user2', user2)
--    ent.assert_passwd_by_name('ov_user2@LDAP', user2)
-+
-+    if override_name:
-+        ent.assert_passwd_by_name('ov_user2', user2)
-+        ent.assert_passwd_by_name('ov_user2@LDAP', user2)
- 
- 
- #
-@@ -514,6 +525,54 @@ def test_imp_exp_user_override(ldap_conn, env_imp_exp_user_override):
-     assert_user_overriden()
- 
- 
-+# Regression test for bug 3179
-+
-+
-+def test_imp_exp_user_overrride_noname(ldap_conn,
-+                                       env_two_users_and_group):
-+
-+    # Override
-+    subprocess.check_call(["sss_override", "user-add", "user1",
-+                           "-u", "10010",
-+                           "-g", "20010",
-+                           "-c", "Overriden User 1",
-+                           "-h", "/home/ov/user1",
-+                           "-s", "/bin/ov_user1_shell"])
-+
-+    subprocess.check_call(["sss_override", "user-add", "user2@LDAP",
-+                           "-u", "10020",
-+                           "-g", "20020",
-+                           "-c", "Overriden User 2",
-+                           "-h", "/home/ov/user2",
-+                           "-s", "/bin/ov_user2_shell"])
-+
-+    # Restart SSSD so the override might take effect
-+    restart_sssd()
-+
-+    # Assert entries are overriden
-+    assert_user_overriden(override_name=False)
-+
-+    # Export overrides
-+    subprocess.check_call(["sss_override", "user-export", OVERRIDE_FILENAME])
-+
-+    # Drop all overrides
-+    subprocess.check_call(["sss_override", "user-del", "user1"])
-+    subprocess.check_call(["sss_override", "user-del", "user2@LDAP"])
-+
-+    # Avoid hitting memory cache
-+    time.sleep(2)
-+
-+    # Assert entries are not overridden
-+    assert_user_default()
-+
-+    # Import overrides
-+    subprocess.check_call(["sss_override", "user-import",
-+                           OVERRIDE_FILENAME])
-+    restart_sssd()
-+
-+    assert_user_overriden(override_name=False)
-+
-+
- #
- # Override user-show
- #
-@@ -581,7 +640,7 @@ def test_find_user_override(ldap_conn, env_find_user_override):
- # Common group asserts
- #
- 
--def assert_group_overriden():
-+def assert_group_overriden(override_name=True):
- 
-     # Assert entries are overridden
-     empty_group = dict(gid=3002, mem=ent.contains_only())
-@@ -589,13 +648,17 @@ def assert_group_overriden():
- 
-     ent.assert_group_by_name("group", group)
-     ent.assert_group_by_name("group@LDAP", group)
--    ent.assert_group_by_name("ov_group", group)
--    ent.assert_group_by_name("ov_group@LDAP", group)
-+
-+    if override_name:
-+        ent.assert_group_by_name("ov_group", group)
-+        ent.assert_group_by_name("ov_group@LDAP", group)
- 
-     ent.assert_group_by_name("empty_group", empty_group)
-     ent.assert_group_by_name("empty_group@LDAP", empty_group)
--    ent.assert_group_by_name("ov_empty_group", empty_group)
--    ent.assert_group_by_name("ov_empty_group@LDAP", empty_group)
-+
-+    if override_name:
-+        ent.assert_group_by_name("ov_empty_group", empty_group)
-+        ent.assert_group_by_name("ov_empty_group@LDAP", empty_group)
- 
- 
- def assert_group_default():
-@@ -841,6 +904,45 @@ def test_imp_exp_group_override(ldap_conn, env_imp_exp_group_override):
-     assert_group_overriden()
- 
- 
-+# Regression test for bug 3179
-+
-+
-+def test_imp_exp_group_override_noname(ldap_conn, env_group_basic):
-+
-+    # Override - do not use -n here)
-+    subprocess.check_call(["sss_override", "group-add", "group",
-+                           "-g", "3001"])
-+
-+    subprocess.check_call(["sss_override", "group-add", "empty_group@LDAP",
-+                           "--gid", "3002"])
-+
-+    # Restart SSSD so the override might take effect
-+    restart_sssd()
-+
-+    # Assert entries are overridden
-+    assert_group_overriden(override_name=False)
-+
-+    # Export overrides
-+    subprocess.check_call(["sss_override", "group-export",
-+                           OVERRIDE_FILENAME])
-+
-+    # Drop all overrides
-+    subprocess.check_call(["sss_override", "group-del", "group"])
-+    subprocess.check_call(["sss_override", "group-del", "empty_group@LDAP"])
-+
-+    # Avoid hitting memory cache
-+    time.sleep(2)
-+
-+    assert_group_default()
-+
-+    # Import overrides
-+    subprocess.check_call(["sss_override", "group-import",
-+                           OVERRIDE_FILENAME])
-+    restart_sssd()
-+
-+    assert_group_overriden(override_name=False)
-+
-+
- # Regression test for bug #2802
- # sss_override segfaults when accidentally adding --help flag to some commands
- 
--- 
-2.7.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
new file mode 100644
index 0000000..1da145d
--- /dev/null
+++ b/SOURCES/0141-IPA-Return-from-function-after-marking-a-request-as-.patch
@@ -0,0 +1,30 @@
+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/0141-p11-only-set-PKCS11_LOGIN_TOKEN_NAME-if-gdm-smartcar.patch b/SOURCES/0141-p11-only-set-PKCS11_LOGIN_TOKEN_NAME-if-gdm-smartcar.patch
deleted file mode 100644
index 2df0058..0000000
--- a/SOURCES/0141-p11-only-set-PKCS11_LOGIN_TOKEN_NAME-if-gdm-smartcar.patch
+++ /dev/null
@@ -1,267 +0,0 @@
-From 5000ad4c45d3cfb4ffa0d76d291c8b1b6c49b02b Mon Sep 17 00:00:00 2001
-From: Sumit Bose <sbose@redhat.com>
-Date: Wed, 31 Aug 2016 14:32:31 +0200
-Subject: [PATCH 141/143] p11: only set PKCS11_LOGIN_TOKEN_NAME if
- gdm-smartcard is used
-
-Resolves https://fedorahosted.org/sssd/ticket/3165
-
-Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
-(cherry picked from commit 71cd9f98150577224559bdc12c53c01ce6f2c3d9)
----
- src/responder/pam/pamsrv_p11.c  | 33 +++++++++------
- src/tests/cmocka/test_pam_srv.c | 89 +++++++++++++++++++++++++++++++++++------
- 2 files changed, 97 insertions(+), 25 deletions(-)
-
-diff --git a/src/responder/pam/pamsrv_p11.c b/src/responder/pam/pamsrv_p11.c
-index a2514f6a1d699de3a245063f49db1b7e51a2b10b..22da33067d5c479153376927855dcd6b43322d8b 100644
---- a/src/responder/pam/pamsrv_p11.c
-+++ b/src/responder/pam/pamsrv_p11.c
-@@ -505,7 +505,11 @@ errno_t pam_check_cert_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
- }
- 
- /* The PKCS11_LOGIN_TOKEN_NAME environment variable is e.g. used by the Gnome
-- * Settings Daemon to determine the name of the token used for login */
-+ * Settings Daemon to determine the name of the token used for login but it
-+ * should be only set if SSSD is called by gdm-smartcard. Otherwise desktop
-+ * components might assume that gdm-smartcard PAM stack is configured
-+ * correctly which might not be the case e.g. if Smartcard authentication was
-+ * used when running gdm-password. */
- #define PKCS11_LOGIN_TOKEN_ENV_NAME "PKCS11_LOGIN_TOKEN_NAME"
- 
- errno_t add_pam_cert_response(struct pam_data *pd, const char *sysdb_username,
-@@ -553,19 +557,22 @@ errno_t add_pam_cert_response(struct pam_data *pd, const char *sysdb_username,
-         return ret;
-     }
- 
--    env = talloc_asprintf(pd, "%s=%s", PKCS11_LOGIN_TOKEN_ENV_NAME, token_name);
--    if (env == NULL) {
--        DEBUG(SSSDBG_OP_FAILURE, "talloc_asprintf failed.\n");
--        return ENOMEM;
--    }
-+    if (strcmp(pd->service, "gdm-smartcard") == 0) {
-+        env = talloc_asprintf(pd, "%s=%s", PKCS11_LOGIN_TOKEN_ENV_NAME,
-+                              token_name);
-+        if (env == NULL) {
-+            DEBUG(SSSDBG_OP_FAILURE, "talloc_asprintf failed.\n");
-+            return ENOMEM;
-+        }
- 
--    ret = pam_add_response(pd, SSS_PAM_ENV_ITEM, strlen(env) + 1,
--                           (uint8_t *)env);
--    talloc_free(env);
--    if (ret != EOK) {
--        DEBUG(SSSDBG_OP_FAILURE,
--              "pam_add_response failed to add environment variable.\n");
--        return ret;
-+        ret = pam_add_response(pd, SSS_PAM_ENV_ITEM, strlen(env) + 1,
-+                               (uint8_t *)env);
-+        talloc_free(env);
-+        if (ret != EOK) {
-+            DEBUG(SSSDBG_OP_FAILURE,
-+                  "pam_add_response failed to add environment variable.\n");
-+            return ret;
-+        }
-     }
- 
-     return ret;
-diff --git a/src/tests/cmocka/test_pam_srv.c b/src/tests/cmocka/test_pam_srv.c
-index 5de092d0f19318d1d6c773355dbb38e345600133..02199e6f121cab0784389256cdaac38baf9d73e3 100644
---- a/src/tests/cmocka/test_pam_srv.c
-+++ b/src/tests/cmocka/test_pam_srv.c
-@@ -554,7 +554,7 @@ static void mock_input_pam(TALLOC_CTX *mem_ctx, const char *name,
- }
- 
- static void mock_input_pam_cert(TALLOC_CTX *mem_ctx, const char *name,
--                                const char *pin)
-+                                const char *pin, const char *service)
- {
-     size_t buf_size;
-     uint8_t *m_buf;
-@@ -576,7 +576,7 @@ static void mock_input_pam_cert(TALLOC_CTX *mem_ctx, const char *name,
-         pi.pam_authtok_type = SSS_AUTHTOK_TYPE_SC_PIN;
-     }
- 
--    pi.pam_service = "login";
-+    pi.pam_service = service == NULL ? "login" : service;
-     pi.pam_service_size = strlen(pi.pam_service) + 1;
-     pi.pam_tty = "/dev/tty";
-     pi.pam_tty_size = strlen(pi.pam_tty) + 1;
-@@ -626,7 +626,8 @@ static int test_pam_simple_check(uint32_t status, uint8_t *body, size_t blen)
- 
- #define PKCS11_LOGIN_TOKEN_ENV_NAME "PKCS11_LOGIN_TOKEN_NAME"
- 
--static int test_pam_cert_check(uint32_t status, uint8_t *body, size_t blen)
-+static int test_pam_cert_check_gdm_smartcard(uint32_t status, uint8_t *body,
-+                                             size_t blen)
- {
-     size_t rp = 0;
-     uint32_t val;
-@@ -675,6 +676,44 @@ static int test_pam_cert_check(uint32_t status, uint8_t *body, size_t 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") + sizeof(TEST_TOKEN_NAME)));
-+
-+    assert_int_equal(*(body + rp + sizeof("pamuser") - 1), 0);
-+    assert_string_equal(body + rp, "pamuser");
-+    rp += sizeof("pamuser");
-+
-+    assert_int_equal(*(body + rp + sizeof(TEST_TOKEN_NAME) - 1), 0);
-+    assert_string_equal(body + rp, TEST_TOKEN_NAME);
-+
-+    return EOK;
-+}
- 
- static int test_pam_offline_chauthtok_check(uint32_t status,
-                                             uint8_t *body, size_t blen)
-@@ -1438,7 +1477,7 @@ void test_pam_preauth_no_logon_name(void **state)
- {
-     int ret;
- 
--    mock_input_pam_cert(pam_test_ctx, NULL, NULL);
-+    mock_input_pam_cert(pam_test_ctx, NULL, NULL, NULL);
- 
-     will_return(__wrap_sss_packet_get_cmd, SSS_PAM_PREAUTH);
-     will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL);
-@@ -1465,7 +1504,7 @@ 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);
-+    mock_input_pam_cert(pam_test_ctx, "pamuser", NULL, NULL);
- 
-     will_return(__wrap_sss_packet_get_cmd, SSS_PAM_PREAUTH);
-     will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL);
-@@ -1544,7 +1583,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);
-+    mock_input_pam_cert(pam_test_ctx, "pamuser", NULL, NULL);
- 
-     will_return(__wrap_sss_packet_get_cmd, SSS_PAM_PREAUTH);
-     will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL);
-@@ -1566,7 +1605,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);
-+    mock_input_pam_cert(pam_test_ctx, "pamuser", NULL, NULL);
- 
-     will_return(__wrap_sss_packet_get_cmd, SSS_PAM_PREAUTH);
-     will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL);
-@@ -1583,13 +1622,37 @@ void test_pam_preauth_cert_match(void **state)
-     assert_int_equal(ret, EOK);
- }
- 
-+/* Test if PKCS11_LOGIN_TOKEN_NAME is added for the gdm-smartcard service */
-+void test_pam_preauth_cert_match_gdm_smartcard(void **state)
-+{
-+    int ret;
-+
-+    set_cert_auth_param(pam_test_ctx->pctx, NSS_DB);
-+
-+    mock_input_pam_cert(pam_test_ctx, "pamuser", NULL, "gdm-smartcard");
-+
-+    will_return(__wrap_sss_packet_get_cmd, SSS_PAM_PREAUTH);
-+    will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL);
-+    mock_account_recv(0, 0, NULL, test_lookup_by_cert_cb,
-+                      discard_const(TEST_TOKEN_CERT));
-+
-+    set_cmd_cb(test_pam_cert_check_gdm_smartcard);
-+    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_match_wrong_user(void **state)
- {
-     int ret;
- 
-     set_cert_auth_param(pam_test_ctx->pctx, NSS_DB);
- 
--    mock_input_pam_cert(pam_test_ctx, "pamuser", NULL);
-+    mock_input_pam_cert(pam_test_ctx, "pamuser", NULL, NULL);
- 
-     will_return(__wrap_sss_packet_get_cmd, SSS_PAM_PREAUTH);
-     will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL);
-@@ -1613,7 +1676,7 @@ void test_pam_preauth_cert_no_logon_name(void **state)
- 
-     set_cert_auth_param(pam_test_ctx->pctx, NSS_DB);
- 
--    mock_input_pam_cert(pam_test_ctx, NULL, NULL);
-+    mock_input_pam_cert(pam_test_ctx, NULL, NULL, NULL);
- 
-     will_return(__wrap_sss_packet_get_cmd, SSS_PAM_PREAUTH);
-     will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL);
-@@ -1636,7 +1699,7 @@ 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);
-+    mock_input_pam_cert(pam_test_ctx, NULL, NULL, NULL);
- 
-     will_return(__wrap_sss_packet_get_cmd, SSS_PAM_PREAUTH);
-     will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL);
-@@ -1657,7 +1720,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);
-+    mock_input_pam_cert(pam_test_ctx, NULL, NULL, NULL);
- 
-     will_return(__wrap_sss_packet_get_cmd, SSS_PAM_PREAUTH);
-     will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL);
-@@ -1679,7 +1742,7 @@ void test_pam_cert_auth(void **state)
- 
-     set_cert_auth_param(pam_test_ctx->pctx, NSS_DB);
- 
--    mock_input_pam_cert(pam_test_ctx, "pamuser", "123456");
-+    mock_input_pam_cert(pam_test_ctx, "pamuser", "123456", NULL);
- 
-     will_return(__wrap_sss_packet_get_cmd, SSS_PAM_AUTHENTICATE);
-     will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL);
-@@ -1790,6 +1853,8 @@ int main(int argc, const char *argv[])
-                                         pam_test_setup, pam_test_teardown),
-         cmocka_unit_test_setup_teardown(test_pam_preauth_cert_match,
-                                         pam_test_setup, pam_test_teardown),
-+        cmocka_unit_test_setup_teardown(test_pam_preauth_cert_match_gdm_smartcard,
-+                                        pam_test_setup, pam_test_teardown),
-         cmocka_unit_test_setup_teardown(test_pam_preauth_cert_match_wrong_user,
-                                         pam_test_setup, pam_test_teardown),
-         cmocka_unit_test_setup_teardown(test_pam_preauth_cert_no_logon_name,
--- 
-2.7.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
new file mode 100644
index 0000000..4669a77
--- /dev/null
+++ b/SOURCES/0142-HBAC-Do-not-rely-on-originalMemberOf-use-the-sysdb-m.patch
@@ -0,0 +1,175 @@
+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/0142-p11-return-a-fully-qualified-name.patch b/SOURCES/0142-p11-return-a-fully-qualified-name.patch
deleted file mode 100644
index 195b9cc..0000000
--- a/SOURCES/0142-p11-return-a-fully-qualified-name.patch
+++ /dev/null
@@ -1,100 +0,0 @@
-From e87e0059520de24047e8448a5b417393adc6c5b4 Mon Sep 17 00:00:00 2001
-From: Sumit Bose <sbose@redhat.com>
-Date: Fri, 16 Sep 2016 11:47:40 +0200
-Subject: [PATCH 142/143] p11: return a fully-qualified name
-
-Related to https://fedorahosted.org/sssd/ticket/3165
-
-Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
-(cherry picked from commit 3649b959709f1ab187092f054d4aace0798c98fa)
----
- src/responder/pam/pamsrv_p11.c  | 20 +++++++++-----------
- src/tests/cmocka/test_pam_srv.c | 16 ++++++++--------
- 2 files changed, 17 insertions(+), 19 deletions(-)
-
-diff --git a/src/responder/pam/pamsrv_p11.c b/src/responder/pam/pamsrv_p11.c
-index 22da33067d5c479153376927855dcd6b43322d8b..570bfe09d4385a038e7e03fcb64c72dd794774a6 100644
---- a/src/responder/pam/pamsrv_p11.c
-+++ b/src/responder/pam/pamsrv_p11.c
-@@ -521,33 +521,31 @@ errno_t add_pam_cert_response(struct pam_data *pd, const char *sysdb_username,
-     size_t msg_len;
-     size_t slot_len;
-     int ret;
--    char *username;
- 
-     if (sysdb_username == NULL || token_name == NULL) {
-         DEBUG(SSSDBG_CRIT_FAILURE, "Missing mandatory user or slot name.\n");
-         return EINVAL;
-     }
- 
--    ret = sss_parse_internal_fqname(pd, sysdb_username, &username, NULL);
--    if (ret != EOK) {
--        DEBUG(SSSDBG_CRIT_FAILURE, "Cannot parse [%s]\n", sysdb_username);
--        return ret;
--    }
--
--    user_len = strlen(username) + 1;
-+    user_len = strlen(sysdb_username) + 1;
-     slot_len = strlen(token_name) + 1;
-     msg_len = user_len + slot_len;
- 
-     msg = talloc_zero_size(pd, msg_len);
-     if (msg == NULL) {
-         DEBUG(SSSDBG_OP_FAILURE, "talloc_zero_size failed.\n");
--        talloc_free(username);
-         return ENOMEM;
-     }
- 
--    memcpy(msg, username, user_len);
-+    /* 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
-+     * Smartcard. If this type of name is irritating at the PIN prompt or the
-+     * 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 + user_len, token_name, slot_len);
--    talloc_free(username);
- 
-     ret = pam_add_response(pd, SSS_PAM_CERT_INFO, msg_len, msg);
-     talloc_free(msg);
-diff --git a/src/tests/cmocka/test_pam_srv.c b/src/tests/cmocka/test_pam_srv.c
-index 02199e6f121cab0784389256cdaac38baf9d73e3..4b2dea4be6a819b23afd243ba99cd9bd57c16c20 100644
---- a/src/tests/cmocka/test_pam_srv.c
-+++ b/src/tests/cmocka/test_pam_srv.c
-@@ -664,11 +664,11 @@ static int test_pam_cert_check_gdm_smartcard(uint32_t status, uint8_t *body,
-     assert_int_equal(val, SSS_PAM_CERT_INFO);
- 
-     SAFEALIGN_COPY_UINT32(&val, body + rp, &rp);
--    assert_int_equal(val, (sizeof("pamuser") + sizeof(TEST_TOKEN_NAME)));
-+    assert_int_equal(val, (sizeof("pamuser@"TEST_DOM_NAME) + sizeof(TEST_TOKEN_NAME)));
- 
--    assert_int_equal(*(body + rp + sizeof("pamuser") - 1), 0);
--    assert_string_equal(body + rp, "pamuser");
--    rp += sizeof("pamuser");
-+    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);
-@@ -703,11 +703,11 @@ static int test_pam_cert_check(uint32_t status, uint8_t *body, size_t blen)
-     assert_int_equal(val, SSS_PAM_CERT_INFO);
- 
-     SAFEALIGN_COPY_UINT32(&val, body + rp, &rp);
--    assert_int_equal(val, (sizeof("pamuser") + sizeof(TEST_TOKEN_NAME)));
-+    assert_int_equal(val, (sizeof("pamuser@"TEST_DOM_NAME) + sizeof(TEST_TOKEN_NAME)));
- 
--    assert_int_equal(*(body + rp + sizeof("pamuser") - 1), 0);
--    assert_string_equal(body + rp, "pamuser");
--    rp += sizeof("pamuser");
-+    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);
--- 
-2.7.4
-
diff --git a/SOURCES/0143-VALIDATORS-Add-subdomain-section.patch b/SOURCES/0143-VALIDATORS-Add-subdomain-section.patch
new file mode 100644
index 0000000..8b1c3c5
--- /dev/null
+++ b/SOURCES/0143-VALIDATORS-Add-subdomain-section.patch
@@ -0,0 +1,53 @@
+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/0143-pam_sss-check-PKCS11_LOGIN_TOKEN_NAME.patch b/SOURCES/0143-pam_sss-check-PKCS11_LOGIN_TOKEN_NAME.patch
deleted file mode 100644
index 1cd1c77..0000000
--- a/SOURCES/0143-pam_sss-check-PKCS11_LOGIN_TOKEN_NAME.patch
+++ /dev/null
@@ -1,109 +0,0 @@
-From b5a092b4b0e4f072f0f402146a83addb97cf2977 Mon Sep 17 00:00:00 2001
-From: Sumit Bose <sbose@redhat.com>
-Date: Fri, 16 Sep 2016 11:48:18 +0200
-Subject: [PATCH 143/143] pam_sss: check PKCS11_LOGIN_TOKEN_NAME
-
-Check if PKCS11_LOGIN_TOKEN_NAME is set and prompt the user if the
-matching Smartcard is not inserted.
-
-Related to https://fedorahosted.org/sssd/ticket/3165
-
-Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
-(cherry picked from commit 35ba922bc51416f02877b53a6f25c04104ae5f03)
----
- src/sss_client/pam_sss.c | 65 ++++++++++++++++++++++++++++++++++++++++++++++++
- 1 file changed, 65 insertions(+)
-
-diff --git a/src/sss_client/pam_sss.c b/src/sss_client/pam_sss.c
-index fdb9c907644f1317b6f8e58619f01ad2753deafc..2049d5fb0c6092aaaa914385c79d02d8f44b447e 100644
---- a/src/sss_client/pam_sss.c
-+++ b/src/sss_client/pam_sss.c
-@@ -1410,6 +1410,7 @@ done:
- }
- 
- #define SC_PROMPT_FMT "PIN for %s for user %s"
-+
- static int prompt_sc_pin(pam_handle_t *pamh, struct pam_items *pi)
- {
-     int ret;
-@@ -1691,6 +1692,62 @@ static int get_authtok_for_password_change(pam_handle_t *pamh,
-     return PAM_SUCCESS;
- }
- 
-+#define SC_ENTER_FMT "Please enter smart card labeled\n %s\nand press enter"
-+
-+static int check_login_token_name(pam_handle_t *pamh, struct pam_items *pi,
-+                                  bool quiet_mode)
-+{
-+    int ret;
-+    int pam_status;
-+    char *login_token_name;
-+    char *prompt = NULL;
-+    size_t size;
-+    char *answer = NULL;
-+
-+    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) {
-+        size = sizeof(SC_ENTER_FMT) + strlen(login_token_name);
-+        prompt = malloc(size);
-+        if (prompt == NULL) {
-+            D(("malloc failed."));
-+            return ENOMEM;
-+        }
-+
-+        ret = snprintf(prompt, size, SC_ENTER_FMT,
-+                       login_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);
-+        free(answer);
-+        if (ret != PAM_SUCCESS) {
-+            D(("do_pam_conversation failed."));
-+            return ret;
-+        }
-+
-+        pam_status = send_and_receive(pamh, pi, SSS_PAM_PREAUTH, quiet_mode);
-+        if (pam_status != PAM_SUCCESS) {
-+            D(("send_and_receive returned [%d] during pre-auth", pam_status));
-+        /*
-+         * Since we are waiting for the right Smartcard to be inserted errors
-+         * can be ignored here.
-+         */
-+        }
-+    }
-+
-+    return PAM_SUCCESS;
-+}
-+
- static int pam_sss(enum sss_cli_command task, pam_handle_t *pamh,
-                    int pam_flags, int argc, const char **argv)
- {
-@@ -1758,6 +1815,14 @@ static int pam_sss(enum sss_cli_command task, pam_handle_t *pamh,
-                     }
-                 }
- 
-+                if (strcmp(pi.pam_service, "gdm-smartcard") == 0) {
-+                    ret = check_login_token_name(pamh, &pi, quiet_mode);
-+                    if (ret != PAM_SUCCESS) {
-+                        D(("check_login_token_name failed.\n"));
-+                        return ret;
-+                    }
-+                }
-+
-                 ret = get_authtok_for_authentication(pamh, &pi, flags);
-                 if (ret != PAM_SUCCESS) {
-                     D(("failed to get authentication token: %s",
--- 
-2.7.4
-
diff --git a/SOURCES/0144-VALIDATORS-Remove-application-section-domain.patch b/SOURCES/0144-VALIDATORS-Remove-application-section-domain.patch
new file mode 100644
index 0000000..d644b35
--- /dev/null
+++ b/SOURCES/0144-VALIDATORS-Remove-application-section-domain.patch
@@ -0,0 +1,48 @@
+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/0144-sysdb-add-parent_dom-to-sysdb_get_direct_parents.patch b/SOURCES/0144-sysdb-add-parent_dom-to-sysdb_get_direct_parents.patch
deleted file mode 100644
index 7cf09f2..0000000
--- a/SOURCES/0144-sysdb-add-parent_dom-to-sysdb_get_direct_parents.patch
+++ /dev/null
@@ -1,124 +0,0 @@
-From b368dca11e715400da64348a17049abf5b072f57 Mon Sep 17 00:00:00 2001
-From: Sumit Bose <sbose@redhat.com>
-Date: Tue, 18 Oct 2016 14:59:19 +0200
-Subject: [PATCH 144/151] sysdb: add parent_dom to sysdb_get_direct_parents()
-
-Currently sysdb_get_direct_parents() only return direct parents from the
-same domain as the child object. In setups with sub-domains this might
-not be sufficient. A new option parent_dom is added which allows to
-specify a domain the direct parents should be lookup up in. If it is
-NULL the whole cache is searched.
-
-Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
-(cherry picked from commit 3dd4c3eca80e9223a65f3318821bd0fb5b45aedd)
----
- src/db/sysdb.h                             | 21 +++++++++++++++++++++
- src/db/sysdb_search.c                      |  7 ++++++-
- src/providers/ldap/sdap_async_initgroups.c | 11 +++++++----
- 3 files changed, 34 insertions(+), 5 deletions(-)
-
-diff --git a/src/db/sysdb.h b/src/db/sysdb.h
-index 8713efa6e8fcc6fb620340fe152989a5dae58434..4164657c2b329a240d46fe3ecdfb4b2eefffc5b3 100644
---- a/src/db/sysdb.h
-+++ b/src/db/sysdb.h
-@@ -1135,8 +1135,29 @@ errno_t sysdb_remove_attrs(struct sss_domain_info *domain,
-                            enum sysdb_member_type type,
-                            char **remove_attrs);
- 
-+/**
-+ * @brief Return direct parents of an object in the cache
-+ *
-+ * @param[in]  mem_ctx         Memory context the result should be allocated
-+ *                             on
-+ * @param[in]  dom             domain the object is in
-+ * @param[in]  parent_dom      domain which should be searched for direct
-+ *                             parents if NULL all domains in the given cache
-+ *                             are searched
-+ * @param[in]  mtype           Type of the object, SYSDB_MEMBER_USER or
-+ *                             SYSDB_MEMBER_GROUP
-+ * @param[in]  name            Name of the object
-+ * @param[out] _direct_parents List of names of the direct parent groups
-+ *
-+ *
-+ * @return
-+ *  - EOK:    success
-+ *  - EINVAL: wrong mtype
-+ *  - ENOMEM: Memory allocation failed
-+ */
- errno_t sysdb_get_direct_parents(TALLOC_CTX *mem_ctx,
-                                  struct sss_domain_info *dom,
-+                                 struct sss_domain_info *parent_dom,
-                                  enum sysdb_member_type mtype,
-                                  const char *name,
-                                  char ***_direct_parents);
-diff --git a/src/db/sysdb_search.c b/src/db/sysdb_search.c
-index cfee5784dbadd692f30d0758e7e5c3c9fb2814cb..4d63c3838a49392bbf2a57aeb6f7740f4d4fbdcd 100644
---- a/src/db/sysdb_search.c
-+++ b/src/db/sysdb_search.c
-@@ -1981,6 +1981,7 @@ done:
- 
- errno_t sysdb_get_direct_parents(TALLOC_CTX *mem_ctx,
-                                  struct sss_domain_info *dom,
-+                                 struct sss_domain_info *parent_dom,
-                                  enum sysdb_member_type mtype,
-                                  const char *name,
-                                  char ***_direct_parents)
-@@ -2029,7 +2030,11 @@ errno_t sysdb_get_direct_parents(TALLOC_CTX *mem_ctx,
-         goto done;
-     }
- 
--    basedn = sysdb_group_base_dn(tmp_ctx, dom);
-+    if (parent_dom == NULL) {
-+        basedn = sysdb_base_dn(dom->sysdb, tmp_ctx);
-+    } else {
-+        basedn = sysdb_group_base_dn(tmp_ctx, parent_dom);
-+    }
-     if (!basedn) {
-         ret = ENOMEM;
-         goto done;
-diff --git a/src/providers/ldap/sdap_async_initgroups.c b/src/providers/ldap/sdap_async_initgroups.c
-index f9593f0dfaa2dc6e33fd6c9d1f0c9b78cad3a1d9..77324d0ee9eb2ad2fc35c2098d6c9c23a62747c9 100644
---- a/src/providers/ldap/sdap_async_initgroups.c
-+++ b/src/providers/ldap/sdap_async_initgroups.c
-@@ -1301,7 +1301,8 @@ sdap_initgr_store_user_memberships(struct sdap_initgr_nested_state *state)
-         }
-     }
- 
--    ret = sysdb_get_direct_parents(tmp_ctx, state->dom, SYSDB_MEMBER_USER,
-+    ret = sysdb_get_direct_parents(tmp_ctx, state->dom, state->dom,
-+                                   SYSDB_MEMBER_USER,
-                                    state->username, &sysdb_parent_name_list);
-     if (ret) {
-         DEBUG(SSSDBG_CRIT_FAILURE,
-@@ -1388,7 +1389,7 @@ sdap_initgr_nested_get_membership_diff(TALLOC_CTX *mem_ctx,
-         goto done;
-     }
- 
--    ret = sysdb_get_direct_parents(tmp_ctx, dom, SYSDB_MEMBER_GROUP,
-+    ret = sysdb_get_direct_parents(tmp_ctx, dom, dom, SYSDB_MEMBER_GROUP,
-                                    group_name, &sysdb_parents_names_list);
-     if (ret) {
-         DEBUG(SSSDBG_CRIT_FAILURE,
-@@ -2070,7 +2071,8 @@ rfc2307bis_group_memberships_build(hash_entry_t *item, void *user_data)
-         goto done;
-     }
- 
--    ret = sysdb_get_direct_parents(tmp_ctx, mstate->dom, SYSDB_MEMBER_GROUP,
-+    ret = sysdb_get_direct_parents(tmp_ctx, mstate->dom, mstate->dom,
-+                                   SYSDB_MEMBER_GROUP,
-                                    group_name, &sysdb_parents_names_list);
-     if (ret) {
-         DEBUG(SSSDBG_CRIT_FAILURE,
-@@ -2130,7 +2132,8 @@ errno_t save_rfc2307bis_user_memberships(
-     }
-     in_transaction = true;
- 
--    ret = sysdb_get_direct_parents(tmp_ctx, state->dom, SYSDB_MEMBER_USER,
-+    ret = sysdb_get_direct_parents(tmp_ctx, state->dom, state->dom,
-+                                   SYSDB_MEMBER_USER,
-                                    state->name, &sysdb_parent_name_list);
-     if (ret) {
-         DEBUG(SSSDBG_CRIT_FAILURE,
--- 
-2.7.4
-
diff --git a/SOURCES/0145-VALIDATORS-Escape-special-regex-chars.patch b/SOURCES/0145-VALIDATORS-Escape-special-regex-chars.patch
new file mode 100644
index 0000000..d80f3cf
--- /dev/null
+++ b/SOURCES/0145-VALIDATORS-Escape-special-regex-chars.patch
@@ -0,0 +1,35 @@
+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/0145-sdap-make-some-nested-group-related-calls-public.patch b/SOURCES/0145-sdap-make-some-nested-group-related-calls-public.patch
deleted file mode 100644
index abba383..0000000
--- a/SOURCES/0145-sdap-make-some-nested-group-related-calls-public.patch
+++ /dev/null
@@ -1,80 +0,0 @@
-From f0e6ed1942f1a4263a88a0c02d866d616448668f Mon Sep 17 00:00:00 2001
-From: Sumit Bose <sbose@redhat.com>
-Date: Tue, 18 Oct 2016 18:16:30 +0200
-Subject: [PATCH 145/151] sdap: make some nested group related calls public
-
-sdap_nested_groups_store() and rfc2307bis_nested_groups_send/recv() will
-be reused for domain local group lookups.
-
-Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
-(cherry picked from commit 49d3f0a487d55571b2bdc9d3f8280b304b964b9d)
----
- src/providers/ldap/sdap_async_initgroups.c | 12 ++----------
- src/providers/ldap/sdap_async_private.h    | 16 ++++++++++++++++
- 2 files changed, 18 insertions(+), 10 deletions(-)
-
-diff --git a/src/providers/ldap/sdap_async_initgroups.c b/src/providers/ldap/sdap_async_initgroups.c
-index 77324d0ee9eb2ad2fc35c2098d6c9c23a62747c9..f1ba65a98b07a593b0e3a722d0b2c56c8e4b821e 100644
---- a/src/providers/ldap/sdap_async_initgroups.c
-+++ b/src/providers/ldap/sdap_async_initgroups.c
-@@ -622,7 +622,7 @@ static int sdap_initgr_rfc2307_recv(struct tevent_req *req)
- }
- 
- /* ==Common code for pure RFC2307bis and IPA/AD========================= */
--static errno_t
-+errno_t
- sdap_nested_groups_store(struct sysdb_ctx *sysdb,
-                          struct sss_domain_info *domain,
-                          struct sdap_options *opts,
-@@ -1558,14 +1558,6 @@ static void sdap_initgr_rfc2307bis_process(struct tevent_req *subreq);
- static void sdap_initgr_rfc2307bis_done(struct tevent_req *subreq);
- errno_t save_rfc2307bis_user_memberships(
-         struct sdap_initgr_rfc2307bis_state *state);
--struct tevent_req *rfc2307bis_nested_groups_send(
--        TALLOC_CTX *mem_ctx, struct tevent_context *ev,
--        struct sdap_options *opts, struct sysdb_ctx *sysdb,
--        struct sss_domain_info *dom, struct sdap_handle *sh,
--        struct sdap_search_base **search_bases,
--        struct sysdb_attrs **groups, size_t num_groups,
--        hash_table_t *group_hash, size_t nesting);
--static errno_t rfc2307bis_nested_groups_recv(struct tevent_req *req);
- 
- static struct tevent_req *sdap_initgr_rfc2307bis_send(
-         TALLOC_CTX *memctx,
-@@ -2616,7 +2608,7 @@ static void rfc2307bis_nested_groups_process(struct tevent_req *subreq)
-     tevent_req_set_callback(subreq, rfc2307bis_nested_groups_done, req);
- }
- 
--static errno_t rfc2307bis_nested_groups_recv(struct tevent_req *req)
-+errno_t rfc2307bis_nested_groups_recv(struct tevent_req *req)
- {
-     TEVENT_REQ_RETURN_ON_ERROR(req);
-     return EOK;
-diff --git a/src/providers/ldap/sdap_async_private.h b/src/providers/ldap/sdap_async_private.h
-index f09ddb71f48080251e61b8a850ccb8c9b5058331..4af4f7144d8855e4ed705f6a64e0a7818bc0b9a9 100644
---- a/src/providers/ldap/sdap_async_private.h
-+++ b/src/providers/ldap/sdap_async_private.h
-@@ -157,4 +157,20 @@ errno_t sdap_check_ad_group_type(struct sss_domain_info *dom,
-                                  struct sysdb_attrs *group_attrs,
-                                  const char *group_name,
-                                  bool *_need_filter);
-+
-+struct tevent_req *rfc2307bis_nested_groups_send(
-+        TALLOC_CTX *mem_ctx, struct tevent_context *ev,
-+        struct sdap_options *opts, struct sysdb_ctx *sysdb,
-+        struct sss_domain_info *dom, struct sdap_handle *sh,
-+        struct sdap_search_base **search_bases,
-+        struct sysdb_attrs **groups, size_t num_groups,
-+        hash_table_t *group_hash, size_t nesting);
-+errno_t rfc2307bis_nested_groups_recv(struct tevent_req *req);
-+
-+errno_t sdap_nested_groups_store(struct sysdb_ctx *sysdb,
-+                                 struct sss_domain_info *domain,
-+                                 struct sdap_options *opts,
-+                                 struct sysdb_attrs **groups,
-+                                 unsigned long count);
-+
- #endif /* _SDAP_ASYNC_PRIVATE_H_ */
--- 
-2.7.4
-
diff --git a/SOURCES/0146-LDAP-AD-resolve-domain-local-groups-for-remote-users.patch b/SOURCES/0146-LDAP-AD-resolve-domain-local-groups-for-remote-users.patch
deleted file mode 100644
index 3e2d38d..0000000
--- a/SOURCES/0146-LDAP-AD-resolve-domain-local-groups-for-remote-users.patch
+++ /dev/null
@@ -1,683 +0,0 @@
-From 2341059ba000d4fa87691f84bf3c39822b38aecb Mon Sep 17 00:00:00 2001
-From: Sumit Bose <sbose@redhat.com>
-Date: Tue, 18 Oct 2016 18:18:44 +0200
-Subject: [PATCH 146/151] LDAP/AD: resolve domain local groups for remote users
-
-If a user from a trusted domain in the same forest is a direct or
-indirect member of domain local groups from the local domain those
-memberships must be resolved as well. Since those domain local groups
-are not valid in the trusted domain a DC from the trusted domain which
-is used to lookup the user data is not aware of them. As a consequence
-those memberships must be resolved against a local DC in a second step.
-
-Resolves https://fedorahosted.org/sssd/ticket/3206
-
-Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
-(cherry picked from commit 25699846bd1c9f8bb513b6271eb4366ab682fbd2)
----
- src/db/sysdb.h                                |   2 +
- src/providers/ldap/sdap_async_initgroups.c    | 158 +++++++++-
- src/providers/ldap/sdap_async_initgroups_ad.c | 407 ++++++++++++++++++++++++++
- src/providers/ldap/sdap_async_private.h       |  10 +
- 4 files changed, 570 insertions(+), 7 deletions(-)
-
-diff --git a/src/db/sysdb.h b/src/db/sysdb.h
-index 4164657c2b329a240d46fe3ecdfb4b2eefffc5b3..a0279fb249e1258c9cb73a4fcab55e4b242c61f3 100644
---- a/src/db/sysdb.h
-+++ b/src/db/sysdb.h
-@@ -224,6 +224,8 @@
-                         SYSDB_OVERRIDE_DN, \
-                         SYSDB_OVERRIDE_OBJECT_DN, \
-                         SYSDB_DEFAULT_OVERRIDE_NAME, \
-+                        SYSDB_UUID, \
-+                        SYSDB_ORIG_DN, \
-                         NULL}
- 
- #define SYSDB_GRSRC_ATTRS {SYSDB_NAME, SYSDB_GIDNUM, \
-diff --git a/src/providers/ldap/sdap_async_initgroups.c b/src/providers/ldap/sdap_async_initgroups.c
-index f1ba65a98b07a593b0e3a722d0b2c56c8e4b821e..8eaba261c49082d086df9f19464ac0f40fae71fb 100644
---- a/src/providers/ldap/sdap_async_initgroups.c
-+++ b/src/providers/ldap/sdap_async_initgroups.c
-@@ -2317,6 +2317,7 @@ static errno_t rfc2307bis_nested_groups_step(struct tevent_req *req)
-     struct sdap_rfc2307bis_nested_ctx *state =
-             tevent_req_data(req, struct sdap_rfc2307bis_nested_ctx);
-     char *oc_list;
-+    const char *class;
- 
-     tmp_ctx = talloc_new(state);
-     if (!tmp_ctx) {
-@@ -2324,9 +2325,21 @@ static errno_t rfc2307bis_nested_groups_step(struct tevent_req *req)
-         goto done;
-     }
- 
--    ret = sdap_get_group_primary_name(state, state->opts,
--                                      state->groups[state->group_iter],
--                                      state->dom, &state->primary_name);
-+    ret = sysdb_attrs_get_string(state->groups[state->group_iter],
-+                                 SYSDB_OBJECTCLASS, &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
-+         * name.
-+         * If the objectClass attribute is missing the object is coming from
-+         * LDAP and we have to find the primary name first. */
-+        ret = sysdb_attrs_get_string(state->groups[state->group_iter],
-+                                     SYSDB_NAME, &state->primary_name);
-+    } else {
-+        ret = sdap_get_group_primary_name(state, state->opts,
-+                                          state->groups[state->group_iter],
-+                                          state->dom, &state->primary_name);
-+    }
-     if (ret != EOK) {
-         goto done;
-     }
-@@ -3069,6 +3082,103 @@ fail:
-     tevent_req_error(req, ret);
- }
- 
-+static void sdap_ad_check_domain_local_groups_done(struct tevent_req *subreq);
-+
-+errno_t sdap_ad_check_domain_local_groups(struct tevent_req *req)
-+{
-+    struct sdap_get_initgr_state *state = tevent_req_data(req,
-+                                               struct sdap_get_initgr_state);
-+    int ret;
-+    struct sdap_domain *local_sdom;
-+    const char *orig_name;
-+    const char *sysdb_name;
-+    struct ldb_result *res;
-+    struct tevent_req *subreq;
-+    struct sysdb_attrs **groups;
-+
-+    /* We only need to check for domain local groups in the AD case and if the
-+     * user is not from our domain, i.e. if the user comes from a sub-domain.
-+     */
-+    if (state->opts->schema_type != SDAP_SCHEMA_AD
-+            || !IS_SUBDOMAIN(state->dom)
-+            || !dp_target_enabled(state->id_ctx->be->provider, "ad", DPT_ID)) {
-+        return EOK;
-+    }
-+
-+    local_sdom = sdap_domain_get(state->id_ctx->opts, state->dom->parent);
-+    if (local_sdom == NULL || local_sdom->pvt == NULL) {
-+        DEBUG(SSSDBG_CRIT_FAILURE, "No ID ctx available for [%s].\n",
-+                                    state->dom->parent->name);
-+        return EINVAL;
-+    }
-+
-+    ret = sysdb_attrs_get_string(state->orig_user, SYSDB_NAME, &orig_name);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_CRIT_FAILURE, "Missing name in user object.\n");
-+        return ret;
-+    }
-+
-+    sysdb_name = sss_create_internal_fqname(state, orig_name, state->dom->name);
-+    if (sysdb_name == NULL) {
-+        DEBUG(SSSDBG_OP_FAILURE, "sss_create_internal_fqname failed.\n");
-+        return ENOMEM;
-+    }
-+
-+    ret = sysdb_initgroups(state, state->dom, sysdb_name, &res);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_CRIT_FAILURE, "sysdb_initgroups failed for user [%s].\n",
-+                                   sysdb_name);
-+        return ret;
-+    }
-+
-+    if (res->count == 0) {
-+        DEBUG(SSSDBG_CRIT_FAILURE,
-+              "sysdb_initgroups returned no results for user [%s].\n",
-+              sysdb_name);
-+        return EINVAL;
-+    }
-+
-+    /* The user object, the first entry in the res->msgs, is included as well
-+     * to cover the case where the remote user is directly added to
-+     * a domain local group. */
-+    ret = sysdb_msg2attrs(state, res->count, res->msgs, &groups);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_OP_FAILURE, "sysdb_msg2attrs failed.\n");
-+        return ret;
-+    }
-+
-+    subreq = sdap_ad_get_domain_local_groups_send(state, state->ev, local_sdom,
-+                             state->opts, state->sysdb, state->dom->parent,
-+                             groups, res->count);
-+    if (subreq == NULL) {
-+        DEBUG(SSSDBG_OP_FAILURE, "sdap_ad_get_domain_local_groups_send failed.\n");
-+        return ENOMEM;
-+    }
-+
-+    tevent_req_set_callback(subreq, sdap_ad_check_domain_local_groups_done,
-+                            req);
-+
-+    return EAGAIN;
-+}
-+
-+static void sdap_ad_check_domain_local_groups_done(struct tevent_req *subreq)
-+{
-+    struct tevent_req *req = tevent_req_callback_data(subreq,
-+                                                      struct tevent_req);
-+    int ret;
-+
-+    ret = sdap_ad_get_domain_local_groups_recv(subreq);
-+    talloc_zfree(subreq);
-+    if (ret != EOK) {
-+        tevent_req_error(req, ret);
-+        return;
-+    }
-+
-+    tevent_req_done(req);
-+
-+    return;
-+}
-+
- static void sdap_get_initgr_pgid(struct tevent_req *req);
- static void sdap_get_initgr_done(struct tevent_req *subreq)
- {
-@@ -3201,8 +3311,6 @@ static void sdap_get_initgr_done(struct tevent_req *subreq)
-     if (ret == EOK) {
-         DEBUG(SSSDBG_TRACE_FUNC,
-               "Primary group already cached, nothing to do.\n");
--        ret = EOK;
--        goto done;
-     } else {
-         gid = talloc_asprintf(state, "%lu", (unsigned long)primary_gid);
-         if (gid == NULL) {
-@@ -3219,10 +3327,28 @@ static void sdap_get_initgr_done(struct tevent_req *subreq)
-             goto done;
-         }
-         tevent_req_set_callback(subreq, sdap_get_initgr_pgid, req);
-+
-+        talloc_free(tmp_ctx);
-+        return;
-     }
- 
--    talloc_free(tmp_ctx);
--    return;
-+    ret = sdap_ad_check_domain_local_groups(req);
-+    if (ret == EAGAIN) {
-+        DEBUG(SSSDBG_TRACE_ALL,
-+              "Checking for domain local group memberships.\n");
-+        talloc_free(tmp_ctx);
-+        return;
-+    } else if (ret == EOK) {
-+        DEBUG(SSSDBG_TRACE_ALL,
-+              "No need to check for domain local group memberships.\n");
-+    } else {
-+        DEBUG(SSSDBG_OP_FAILURE,
-+              "sdap_ad_check_domain_local_groups failed, "
-+              "meberships to domain local groups might be missing.\n");
-+        /* do not let the request fail completely because we already have at
-+         * least "some" groups */
-+        ret = EOK;
-+    }
- 
- done:
-     talloc_free(tmp_ctx);
-@@ -3247,7 +3373,25 @@ static void sdap_get_initgr_pgid(struct tevent_req *subreq)
-         return;
-     }
- 
-+    ret = sdap_ad_check_domain_local_groups(req);
-+    if (ret == EAGAIN) {
-+        DEBUG(SSSDBG_TRACE_ALL,
-+              "Checking for domain local group memberships.\n");
-+        return;
-+    } else if (ret == EOK) {
-+        DEBUG(SSSDBG_TRACE_ALL,
-+              "No need to check for domain local group memberships.\n");
-+    } else {
-+        DEBUG(SSSDBG_OP_FAILURE, "sdap_ad_check_domain_local_groups failed.\n");
-+        DEBUG(SSSDBG_OP_FAILURE,
-+              "sdap_ad_check_domain_local_groups failed, "
-+              "meberships to domain local groups might be missing.\n");
-+        /* do not let the request fail completely because we already have at
-+         * least "some" groups */
-+    }
-+
-     tevent_req_done(req);
-+    return;
- }
- 
- int sdap_get_initgr_recv(struct tevent_req *req)
-diff --git a/src/providers/ldap/sdap_async_initgroups_ad.c b/src/providers/ldap/sdap_async_initgroups_ad.c
-index ad54c1fb837eb4b8e123a1c230feb697eb37bb41..1fee4ab43a6c13803a088ffa4695dde7f39b3d2b 100644
---- a/src/providers/ldap/sdap_async_initgroups_ad.c
-+++ b/src/providers/ldap/sdap_async_initgroups_ad.c
-@@ -1412,6 +1412,413 @@ static errno_t sdap_ad_tokengroups_initgr_posix_recv(struct tevent_req *req)
-     return EOK;
- }
- 
-+struct sdap_ad_get_domain_local_groups_state {
-+    struct tevent_context *ev;
-+    struct sdap_id_conn_ctx *conn;
-+    struct sdap_options *opts;
-+    struct sdap_id_op *op;
-+    struct sysdb_ctx *sysdb;
-+    struct sss_domain_info *dom;
-+    int dp_error;
-+
-+    struct sdap_search_base **search_bases;
-+    struct sysdb_attrs **groups;
-+    size_t num_groups;
-+    hash_table_t *group_hash;
-+};
-+
-+static void
-+sdap_ad_get_domain_local_groups_connect_done(struct tevent_req *subreq);
-+static void sdap_ad_get_domain_local_groups_done(struct tevent_req *subreq);
-+
-+struct tevent_req *
-+sdap_ad_get_domain_local_groups_send(TALLOC_CTX *mem_ctx,
-+                                     struct tevent_context *ev,
-+                                     struct sdap_domain *local_sdom,
-+                                     struct sdap_options *opts,
-+                                     struct sysdb_ctx *sysdb,
-+                                     struct sss_domain_info *dom,
-+                                     struct sysdb_attrs **groups,
-+                                     size_t num_groups)
-+{
-+    struct sdap_ad_get_domain_local_groups_state *state;
-+    struct tevent_req *req;
-+    struct tevent_req *subreq;
-+    struct ad_id_ctx *ad_id_ctx;
-+    errno_t ret;
-+
-+    req = tevent_req_create(mem_ctx, &state,
-+                            struct sdap_ad_get_domain_local_groups_state);
-+    if (req == NULL) {
-+        DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create() failed\n");
-+        return NULL;
-+    }
-+
-+    state->ev = ev;
-+    ad_id_ctx = talloc_get_type(local_sdom->pvt, struct ad_id_ctx);
-+    state->conn = ad_id_ctx->ldap_ctx;
-+    state->opts = opts;
-+    state->sysdb = sysdb;
-+    state->dom = dom;
-+    state->search_bases = state->conn->id_ctx->opts->sdom->group_search_bases;
-+    state->groups = groups;
-+    state->num_groups = num_groups;
-+
-+    ret = sss_hash_create(state, 32, &state->group_hash);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_OP_FAILURE, "sss_hash_create failed.\n");
-+        goto fail;
-+    }
-+
-+    state->op = sdap_id_op_create(state, state->conn->conn_cache);
-+    if (state->op == NULL) {
-+        DEBUG(SSSDBG_OP_FAILURE, "sdap_id_op_create failed\n");
-+        ret = ENOMEM;
-+        goto fail;
-+    }
-+
-+    subreq = sdap_id_op_connect_send(state->op, state, &ret);
-+    if (subreq == NULL) {
-+        DEBUG(SSSDBG_OP_FAILURE, "sdap_id_op_connect_send failed.\n");
-+        goto fail;
-+    }
-+
-+    tevent_req_set_callback(subreq,
-+                            sdap_ad_get_domain_local_groups_connect_done, req);
-+
-+    return req;
-+
-+fail:
-+    tevent_req_error(req, ret);
-+    tevent_req_post(req, ev);
-+    return req;
-+}
-+
-+static void
-+sdap_ad_get_domain_local_groups_connect_done(struct tevent_req *subreq)
-+{
-+
-+    struct tevent_req *req = tevent_req_callback_data(subreq,
-+                                                      struct tevent_req);
-+    struct sdap_ad_get_domain_local_groups_state *state = tevent_req_data(req,
-+                                  struct sdap_ad_get_domain_local_groups_state);
-+    int dp_error = DP_ERR_FATAL;
-+    int 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;
-+    }
-+    subreq = rfc2307bis_nested_groups_send(state, state->ev, state->opts,
-+                                           state->sysdb, state->dom,
-+                                           sdap_id_op_handle(state->op),
-+                                           state->search_bases,
-+                                           state->groups, state->num_groups,
-+                                           state->group_hash, 0);
-+    if (subreq == NULL) {
-+        DEBUG(SSSDBG_OP_FAILURE, "rfc2307bis_nested_groups_send failed.\n");
-+        state->dp_error = DP_ERR_FATAL;
-+        tevent_req_error(req, ENOMEM);
-+        return;
-+    }
-+
-+    tevent_req_set_callback(subreq,
-+                            sdap_ad_get_domain_local_groups_done, req);
-+
-+    return;
-+}
-+
-+struct sdap_nested_group {
-+    struct sysdb_attrs *group;
-+    struct sysdb_attrs **ldap_parents;
-+    size_t parents_count;
-+};
-+
-+static errno_t
-+sdap_ad_get_domain_local_groups_parse_parents(TALLOC_CTX *mem_ctx,
-+                                              struct sdap_nested_group *gr,
-+                                              struct sss_domain_info *dom,
-+                                              struct sysdb_ctx *sysdb,
-+                                              struct sdap_options *opts,
-+                                              const char **_sysdb_name,
-+                                              enum sysdb_member_type *_type,
-+                                              char ***_add_list,
-+                                              char ***_del_list)
-+{
-+    int ret;
-+    size_t c;
-+    char **groupnamelist = NULL;
-+    struct sysdb_attrs *groups[1];
-+    enum sysdb_member_type type;
-+    const char *sysdb_name;
-+    const char *group_name;
-+    const char *class;
-+    struct sss_domain_info *obj_dom;
-+    char *local_groups_base_dn;
-+    char **cached_local_parents = NULL;
-+    char **add_list = NULL;
-+    char **del_list = NULL;
-+    TALLOC_CTX *tmp_ctx;
-+
-+    tmp_ctx = talloc_new(NULL);
-+    if (tmp_ctx == NULL) {
-+        DEBUG(SSSDBG_OP_FAILURE, "talloc_new failed.\n");
-+        return ENOMEM;
-+    }
-+
-+    local_groups_base_dn = talloc_asprintf(tmp_ctx, SYSDB_TMPL_GROUP_BASE,
-+                                           dom->name);
-+    if (local_groups_base_dn == NULL) {
-+        DEBUG(SSSDBG_OP_FAILURE, "talloc_asprintf failed.\n");
-+        ret = ENOMEM;
-+        goto done;
-+    }
-+
-+    if (gr->parents_count != 0) {
-+        /* Store the parents if needed */
-+        ret = sdap_nested_groups_store(sysdb, dom, opts,
-+                                       gr->ldap_parents, gr->parents_count);
-+        if (ret != EOK) {
-+            DEBUG(SSSDBG_MINOR_FAILURE, "Could not save groups [%d]: %s\n",
-+                      ret, strerror(ret));
-+            goto done;
-+        }
-+
-+        ret = sysdb_attrs_primary_fqdn_list(dom, tmp_ctx,
-+                                    gr->ldap_parents, gr->parents_count,
-+                                    opts->group_map[SDAP_AT_GROUP_NAME].name,
-+                                    &groupnamelist);
-+        if (ret != EOK) {
-+            DEBUG(SSSDBG_OP_FAILURE, "sysdb_attrs_primary_fqdn_list failed.\n");
-+            goto done;
-+        }
-+    }
-+
-+    ret = sysdb_attrs_get_string(gr->group, SYSDB_NAME, &sysdb_name);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_OP_FAILURE,
-+              "sysdb_attrs_get_string failed to get SYSDB_NAME, "
-+              "skipping.\n");
-+        goto done;
-+    }
-+
-+    ret = sysdb_attrs_get_string(gr->group, SYSDB_OBJECTCLASS, &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.
-+         */
-+        DEBUG(SSSDBG_TRACE_LIBS,
-+              "sysdb_attrs_get_string failed to get SYSDB_OBJECTCLASS "
-+              "for [%s], assuming group.\n", sysdb_name);
-+
-+        /* make sure group exists in cache */
-+        groups[0]= gr->group;
-+        ret = sdap_nested_groups_store(sysdb, dom, opts, groups, 1);
-+        if (ret != EOK) {
-+            DEBUG(SSSDBG_MINOR_FAILURE, "Could not save groups [%d]: %s\n",
-+                      ret, strerror(ret));
-+            goto done;
-+        }
-+
-+        /* Since the object is coming from LDAP it cannot have the internal
-+         * fully-qualified name, so we can expand it unconditionally. */
-+        group_name = NULL;
-+        ret = sysdb_attrs_primary_name(dom->sysdb, gr->group,
-+                        opts->group_map[SDAP_AT_GROUP_NAME].name,
-+                        &group_name);
-+        if (ret != EOK || group_name == NULL) {
-+            DEBUG(SSSDBG_OP_FAILURE, "Could not determine primary name\n");
-+            group_name = sysdb_name;
-+        }
-+
-+        group_name = sss_create_internal_fqname(tmp_ctx, group_name,
-+                                                dom->name);
-+        if (group_name != NULL) {
-+            sysdb_name = group_name;
-+        }
-+
-+        type = SYSDB_MEMBER_GROUP;
-+    } else {
-+        if (class != NULL && strcmp(class, SYSDB_USER_CLASS) == 0) {
-+            type = SYSDB_MEMBER_USER;
-+        } else {
-+            type = SYSDB_MEMBER_GROUP;
-+        }
-+    }
-+
-+    /* We need to get the cached list of groups form the local domain the
-+     * object is a member of to compare them with the current list just
-+     * retrieved (groupnamelist). Even if this list is empty we have to
-+     * proceed because the membership might have been removed recently on the
-+     * server. */
-+
-+    obj_dom = find_domain_by_object_name(get_domains_head(dom),
-+                                         sysdb_name);
-+    if (obj_dom == NULL) {
-+        obj_dom = dom;
-+        DEBUG(SSSDBG_OP_FAILURE, "Cannot find domain for [%s], "
-+                                 "trying with local domain [%s].\n",
-+                                 sysdb_name, obj_dom->name);
-+    }
-+
-+    ret = sysdb_get_direct_parents(tmp_ctx, obj_dom, dom, type, sysdb_name,
-+                                   &cached_local_parents);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_OP_FAILURE,"sysdb_get_direct_parents failed.\n");
-+        goto done;
-+    }
-+
-+    if (cached_local_parents != NULL && cached_local_parents[0] == NULL) {
-+        talloc_zfree(cached_local_parents);
-+    }
-+
-+    if (DEBUG_IS_SET(SSSDBG_TRACE_ALL)) {
-+        if (cached_local_parents != NULL) {
-+            for (c = 0; cached_local_parents[c] != NULL; c++) {
-+                DEBUG(SSSDBG_TRACE_ALL, "[%s] cached_local_parents [%s].\n",
-+                                        sysdb_name, cached_local_parents[c]);
-+            }
-+        }
-+
-+        if (groupnamelist != NULL) {
-+            for (c = 0; groupnamelist[c] != NULL; c++) {
-+                DEBUG(SSSDBG_TRACE_ALL, "[%s] groupnamelist [%s].\n",
-+                                        sysdb_name, groupnamelist[c]);
-+            }
-+        }
-+    }
-+
-+    ret = diff_string_lists(tmp_ctx, cached_local_parents, groupnamelist,
-+                            &del_list, &add_list, NULL);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_OP_FAILURE, "diff_string_lists failed.\n");
-+        goto done;
-+    }
-+
-+    if (DEBUG_IS_SET(SSSDBG_TRACE_ALL)) {
-+        if (add_list != NULL) {
-+            for (c = 0; add_list[c] != NULL; c++) {
-+                DEBUG(SSSDBG_TRACE_ALL, "add: [%s] will be member of [%s].\n",
-+                                        sysdb_name, add_list[c]);
-+            }
-+        }
-+        if (del_list != NULL) {
-+            for (c = 0; del_list[c] != NULL; c++) {
-+                DEBUG(SSSDBG_TRACE_ALL, "del: [%s] was member of [%s].\n",
-+                                        sysdb_name, del_list[c]);
-+            }
-+        }
-+    }
-+
-+    *_type = type;
-+    *_sysdb_name = talloc_steal(mem_ctx, sysdb_name);
-+    *_add_list = talloc_steal(mem_ctx, groupnamelist);
-+    *_del_list = talloc_steal(mem_ctx, del_list);
-+    ret = EOK;
-+
-+done:
-+    talloc_free(tmp_ctx);
-+
-+    return ret;
-+}
-+
-+static void sdap_ad_get_domain_local_groups_done(struct tevent_req *subreq)
-+{
-+
-+    struct tevent_req *req = tevent_req_callback_data(subreq,
-+                                                      struct tevent_req);
-+    struct sdap_ad_get_domain_local_groups_state *state = tevent_req_data(req,
-+                                  struct sdap_ad_get_domain_local_groups_state);
-+    int ret;
-+    int hret;
-+    unsigned long count;
-+    hash_value_t *values = NULL;
-+    struct sdap_nested_group *gr;
-+    size_t c;
-+    const char *sysdb_name = NULL;
-+    enum sysdb_member_type type;
-+    char **add_list = NULL;
-+    char **del_list = NULL;
-+
-+    ret = rfc2307bis_nested_groups_recv(subreq);
-+    talloc_zfree(subreq);
-+    if (ret != EOK) {
-+        tevent_req_error(req, ret);
-+        return;
-+    }
-+
-+    hret = hash_values(state->group_hash, &count, &values);
-+    if (hret != HASH_SUCCESS) {
-+        DEBUG(SSSDBG_OP_FAILURE, "hash_values failed.\n");
-+        ret = EIO;
-+        goto done;
-+    }
-+
-+    for (c = 0; c < count; c++) {
-+        gr = talloc_get_type(values[c].ptr,
-+                             struct sdap_nested_group);
-+
-+        /* The values from the hash are either user or group objects returned
-+         * by sysdb_initgroups() which where used to start the request or
-+         * nested parents found during the request. The nested parents contain
-+         * the processed LDAP data and can be identified by a missing
-+         * objectclass attribute. */
-+        ret = sdap_ad_get_domain_local_groups_parse_parents(state, gr,
-+                                                            state->dom,
-+                                                            state->sysdb,
-+                                                            state->opts,
-+                                                            &sysdb_name,
-+                                                            &type,
-+                                                            &add_list,
-+                                                            &del_list);
-+        if (ret != EOK) {
-+            DEBUG(SSSDBG_OP_FAILURE,
-+                  "sdap_ad_get_domain_local_groups_parse_parents failed.\n");
-+            continue;
-+        }
-+
-+        if ((add_list == NULL && del_list == NULL)
-+                || (add_list == NULL && del_list != NULL && del_list[0] == NULL)
-+                || (add_list != NULL && add_list[0] == NULL && del_list == NULL)
-+                || (add_list != NULL && add_list[0] == NULL
-+                        && del_list != NULL && del_list[0] == NULL) ) {
-+            continue;
-+        }
-+
-+        DEBUG(SSSDBG_TRACE_INTERNAL, "Updating domain local memberships for %s\n",
-+                                     sysdb_name);
-+        ret = sysdb_update_members(state->dom, sysdb_name, type,
-+                                   (const char *const *) add_list,
-+                                   (const char *const *) del_list);
-+        if (ret != EOK) {
-+            DEBUG(SSSDBG_OP_FAILURE, "sysdb_update_members failed.\n");
-+            goto done;
-+        }
-+    }
-+
-+    ret = EOK;
-+done:
-+    talloc_zfree(values);
-+
-+    if (ret == EOK) {
-+        tevent_req_done(req);
-+    } else {
-+        tevent_req_error(req, ret);
-+    }
-+
-+    return;
-+}
-+
-+errno_t sdap_ad_get_domain_local_groups_recv(struct tevent_req *req)
-+{
-+    TEVENT_REQ_RETURN_ON_ERROR(req);
-+    return EOK;
-+}
-+
- struct sdap_ad_tokengroups_initgroups_state {
-     bool use_id_mapping;
-     struct sss_domain_info *domain;
-diff --git a/src/providers/ldap/sdap_async_private.h b/src/providers/ldap/sdap_async_private.h
-index 4af4f7144d8855e4ed705f6a64e0a7818bc0b9a9..266bc03115e2bdd6a283f5f7da565fd00d3a77be 100644
---- a/src/providers/ldap/sdap_async_private.h
-+++ b/src/providers/ldap/sdap_async_private.h
-@@ -173,4 +173,14 @@ errno_t sdap_nested_groups_store(struct sysdb_ctx *sysdb,
-                                  struct sysdb_attrs **groups,
-                                  unsigned long count);
- 
-+struct tevent_req *
-+sdap_ad_get_domain_local_groups_send(TALLOC_CTX *mem_ctx,
-+                                     struct tevent_context *ev,
-+                                     struct sdap_domain *local_sdom,
-+                                     struct sdap_options *opts,
-+                                     struct sysdb_ctx *sysdb,
-+                                     struct sss_domain_info *dom,
-+                                     struct sysdb_attrs **groups,
-+                                     size_t num_groups);
-+errno_t sdap_ad_get_domain_local_groups_recv(struct tevent_req *req);
- #endif /* _SDAP_ASYNC_PRIVATE_H_ */
--- 
-2.7.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
new file mode 100644
index 0000000..dae5f6c
--- /dev/null
+++ b/SOURCES/0146-TESTS-Add-unit-tests-for-cfg-validation.patch
@@ -0,0 +1,328 @@
+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-IPA-Initialize-a-boolean-control-value.patch b/SOURCES/0147-IPA-Initialize-a-boolean-control-value.patch
deleted file mode 100644
index 7a6ee0e..0000000
--- a/SOURCES/0147-IPA-Initialize-a-boolean-control-value.patch
+++ /dev/null
@@ -1,60 +0,0 @@
-From ffe5898f9588ba4ec3258807d377a82e52c38c67 Mon Sep 17 00:00:00 2001
-From: Jakub Hrozek <jhrozek@redhat.com>
-Date: Tue, 4 Oct 2016 10:45:43 +0200
-Subject: [PATCH 147/149] IPA: Initialize a boolean control value
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-without this patch, valgrind was reporting:
-==30955== Conditional jump or move depends on uninitialised value(s)
-==30955== at 0xDBBACC3: ipa_subdomains_slave_search_done (ipa_subdomains.c:1111)
-==30955== by 0xE73B34D: sdap_search_bases_ex_done (sdap_ops.c:222)
-==30955== by 0xE6FFA98: sdap_get_generic_done (sdap_async.c:1872)
-==30955== by 0xE6FF4E2: generic_ext_search_handler (sdap_async.c:1689)
-==30955== by 0xE6FF840: sdap_get_and_parse_generic_done (sdap_async.c:1797)
-==30955== by 0xE6FEFB5: sdap_get_generic_op_finished (sdap_async.c:1579)
-==30955== by 0xE6FB1D2: sdap_process_message (sdap_async.c:353)
-==30955== by 0xE6FAD51: sdap_process_result (sdap_async.c:197)
-==30955== by 0xE6FAA14: sdap_ldap_next_result (sdap_async.c:145)
-==30955== by 0x8E157FF: tevent_common_loop_timer_delay (tevent_timed.c:341)
-==30955== by 0x8E16809: epoll_event_loop_once (tevent_epoll.c:911)
-==30955== by 0x8E14F09: std_event_loop_once (tevent_standard.c:114)
-==30955==
-
-Resolves:
-https://fedorahosted.org/sssd/ticket/3213
-
-Reviewed-by: Fabiano Fidêncio <fidencio@redhat.com>
----
- 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 4e5bceb8c761bf4476928168d620baf2beb62ad5..d02d2d5c05904c54c5e1997aece82f940b7334ee 100644
---- a/src/providers/ipa/ipa_subdomains.c
-+++ b/src/providers/ipa/ipa_subdomains.c
-@@ -482,6 +482,11 @@ static errno_t ipa_subdomains_refresh(struct ipa_subdomains_ctx *ctx,
-     memset(handled, 0, sizeof(bool) * count);
-     h = 0;
- 
-+    if (changes == NULL) {
-+        return EINVAL;
-+    }
-+    *changes = false;
-+
-     /* check existing subdomains */
-     for (dom = get_next_domain(parent, SSS_GND_DESCEND);
-          dom && IS_SUBDOMAIN(dom); /* if we get back to a parent, stop */
-@@ -1084,7 +1089,7 @@ static void ipa_subdomains_slave_search_done(struct tevent_req *subreq)
-     struct tevent_req *req;
-     struct sysdb_attrs **reply;
-     size_t reply_count;
--    bool has_changes;
-+    bool has_changes = false;
-     errno_t ret;
- 
-     req = tevent_req_callback_data(subreq, struct tevent_req);
--- 
-2.7.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
new file mode 100644
index 0000000..3d015fc
--- /dev/null
+++ b/SOURCES/0147-MAN-Fix-typo-in-trusted-domain-section.patch
@@ -0,0 +1,29 @@
+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-IPA-AD-check-auth-ctx-before-using-it.patch b/SOURCES/0148-IPA-AD-check-auth-ctx-before-using-it.patch
deleted file mode 100644
index d6ad243..0000000
--- a/SOURCES/0148-IPA-AD-check-auth-ctx-before-using-it.patch
+++ /dev/null
@@ -1,92 +0,0 @@
-From 1083c5f195ecf29435f24e136cf6470992614494 Mon Sep 17 00:00:00 2001
-From: Sumit Bose <sbose@redhat.com>
-Date: Tue, 8 Nov 2016 11:51:57 +0100
-Subject: [PATCH 148/149] IPA/AD: check auth ctx before using it
-
-In e6b6b9fa79c67d7d2698bc7e33d2e2f6bb53d483 a feature was introduced to
-set the 'canonicalize' option in the system-wide Kerberos configuration
-according to the settings in SSSD if the AD or IPA provider were used.
-Unfortunately the patch implied that the auth provider is the same as
-the id provider which might not always be the case. A different auth
-provider caused a crash in the backend which is fixed by this patch.
-
-Resolves https://fedorahosted.org/sssd/ticket/3234
-
-Reviewed-by: Petr Cech <pcech@redhat.com>
-(cherry picked from commit ea11ed3ea6291488dd762033246edc4ce3951aeb)
----
- src/providers/ad/ad_subdomains.c   | 13 +++++++++++--
- src/providers/ipa/ipa_subdomains.c | 20 +++++++++++++++++---
- 2 files changed, 28 insertions(+), 5 deletions(-)
-
-diff --git a/src/providers/ad/ad_subdomains.c b/src/providers/ad/ad_subdomains.c
-index 52bf5361fa8de02c7165cbc3513a923ec018fc15..5e57d218c072a2627f165ae072cb761e1a146048 100644
---- a/src/providers/ad/ad_subdomains.c
-+++ b/src/providers/ad/ad_subdomains.c
-@@ -618,14 +618,23 @@ static errno_t ad_subdom_reinit(struct ad_subdomains_ctx *subdoms_ctx)
- {
-     const char *path;
-     errno_t ret;
--    bool canonicalize;
-+    bool canonicalize = false;
- 
-     path = dp_opt_get_string(subdoms_ctx->ad_id_ctx->ad_options->basic,
-                              AD_KRB5_CONFD_PATH);
- 
--    canonicalize = dp_opt_get_bool(
-+    if (subdoms_ctx->ad_id_ctx->ad_options->auth_ctx != NULL
-+            && subdoms_ctx->ad_id_ctx->ad_options->auth_ctx->opts != NULL) {
-+        canonicalize = dp_opt_get_bool(
-                              subdoms_ctx->ad_id_ctx->ad_options->auth_ctx->opts,
-                              KRB5_CANONICALIZE);
-+    } else {
-+        DEBUG(SSSDBG_CONF_SETTINGS, "Auth provider data is not available, "
-+                                    "most probably because the auth provider "
-+                                    "is not 'ad'. Kerberos configuration "
-+                                    "snippet to set the 'canonicalize' option "
-+                                    "will not be created.\n");
-+    }
- 
-     ret = sss_write_krb5_conf_snippet(path, canonicalize);
-     if (ret != EOK) {
-diff --git a/src/providers/ipa/ipa_subdomains.c b/src/providers/ipa/ipa_subdomains.c
-index d02d2d5c05904c54c5e1997aece82f940b7334ee..eb1bc92691da9e82e07595ed84eea35fff78d1a5 100644
---- a/src/providers/ipa/ipa_subdomains.c
-+++ b/src/providers/ipa/ipa_subdomains.c
-@@ -73,16 +73,30 @@ static errno_t
- ipa_subdom_reinit(struct ipa_subdomains_ctx *ctx)
- {
-     errno_t ret;
-+    bool canonicalize = false;
- 
-     DEBUG(SSSDBG_TRACE_INTERNAL,
-           "Re-initializing domain %s\n", ctx->be_ctx->domain->name);
- 
-+    if (ctx->ipa_id_ctx->ipa_options->auth_ctx != NULL
-+          && ctx->ipa_id_ctx->ipa_options->auth_ctx->krb5_auth_ctx != NULL
-+          && ctx->ipa_id_ctx->ipa_options->auth_ctx->krb5_auth_ctx->opts != NULL
-+       ) {
-+        canonicalize = dp_opt_get_bool(
-+                    ctx->ipa_id_ctx->ipa_options->auth_ctx->krb5_auth_ctx->opts,
-+                    KRB5_CANONICALIZE);
-+    } else {
-+        DEBUG(SSSDBG_CONF_SETTINGS, "Auth provider data is not available, "
-+                                    "most probably because the auth provider "
-+                                    "is not 'ipa'. Kerberos configuration "
-+                                    "snippet to set the 'canonicalize' option "
-+                                    "will not be created.\n");
-+    }
-+
-     ret = sss_write_krb5_conf_snippet(
-                           dp_opt_get_string(ctx->ipa_id_ctx->ipa_options->basic,
-                                             IPA_KRB5_CONFD_PATH),
--                          dp_opt_get_bool(
--                    ctx->ipa_id_ctx->ipa_options->auth_ctx->krb5_auth_ctx->opts,
--                    KRB5_CANONICALIZE));
-+                          canonicalize);
-     if (ret != EOK) {
-         DEBUG(SSSDBG_MINOR_FAILURE, "sss_write_krb5_conf_snippet failed.\n");
-         /* Just continue */
--- 
-2.7.4
-
diff --git a/SOURCES/0148-VALIDATORS-Change-regex-for-app-domains.patch b/SOURCES/0148-VALIDATORS-Change-regex-for-app-domains.patch
new file mode 100644
index 0000000..3ca41ad
--- /dev/null
+++ b/SOURCES/0148-VALIDATORS-Change-regex-for-app-domains.patch
@@ -0,0 +1,36 @@
+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-Qualify-ghost-user-attribute-in-case-ldap_group_nest.patch b/SOURCES/0149-Qualify-ghost-user-attribute-in-case-ldap_group_nest.patch
deleted file mode 100644
index 9809cf2..0000000
--- a/SOURCES/0149-Qualify-ghost-user-attribute-in-case-ldap_group_nest.patch
+++ /dev/null
@@ -1,60 +0,0 @@
-From 362911a85e3aa244cdbdf75b8b4131bb26396d19 Mon Sep 17 00:00:00 2001
-From: Jakub Hrozek <jhrozek@redhat.com>
-Date: Wed, 9 Nov 2016 11:59:10 +0100
-Subject: [PATCH 149/149] Qualify ghost user attribute in case
- ldap_group_nesting_level is set to 0
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-When the sssd is set to not resolve nested groups with RFC2307bis, then
-the LDAP provider takes a different path. We didn't qualify the ghost
-users in this case.
-
-Resolves:
-https://fedorahosted.org/sssd/ticket/3236
-
-Reviewed-by: Lukáš Slebodník <lslebodn@redhat.com>
-(cherry picked from commit 538a7f1dd8339b90e0cfc64e7919a34d1d5c10d3)
----
- src/providers/ldap/sdap_async_groups.c | 15 +++++++++++----
- 1 file changed, 11 insertions(+), 4 deletions(-)
-
-diff --git a/src/providers/ldap/sdap_async_groups.c b/src/providers/ldap/sdap_async_groups.c
-index 72760b75acae4cb6ce15c72f16dae8e859d89847..49a1934b2604e6a6af8ee8e8b3d1f88d2029658d 100644
---- a/src/providers/ldap/sdap_async_groups.c
-+++ b/src/providers/ldap/sdap_async_groups.c
-@@ -1659,7 +1659,7 @@ static void sdap_process_group_members(struct tevent_req *subreq)
-     struct sdap_process_group_state *state =
-                         tevent_req_data(req, struct sdap_process_group_state);
-     struct ldb_message_element *el;
--    uint8_t* name_string;
-+    char *name_string;
- 
-     state->check_count--;
-     DEBUG(SSSDBG_TRACE_ALL, "Members remaining: %zu\n", state->check_count);
-@@ -1685,11 +1685,18 @@ static void sdap_process_group_members(struct tevent_req *subreq)
-         goto next;
-     }
- 
--    name_string = el[0].values[0].data;
-+    name_string = sss_create_internal_fqname(state,
-+                                            (const char *) el[0].values[0].data,
-+                                            state->dom->name);
-+    if (name_string == NULL) {
-+        ret = ENOMEM;
-+        goto next;
-+    }
-+
-     state->ghost_dns->values[state->ghost_dns->num_values].data =
--            talloc_steal(state->ghost_dns->values, name_string);
-+            talloc_steal(state->ghost_dns->values, (uint8_t *) name_string);
-     state->ghost_dns->values[state->ghost_dns->num_values].length =
--            strlen((char *)name_string);
-+            strlen(name_string);
-     state->ghost_dns->num_values++;
- 
- next:
--- 
-2.7.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
new file mode 100644
index 0000000..b99885a
--- /dev/null
+++ b/SOURCES/0149-VALIDATORS-Detect-inherit_from-in-normal-domain.patch
@@ -0,0 +1,154 @@
+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-SYSDB-Split-sysdb_try_to_find_expected_dn-into-small.patch b/SOURCES/0150-SYSDB-Split-sysdb_try_to_find_expected_dn-into-small.patch
deleted file mode 100644
index ee1e343..0000000
--- a/SOURCES/0150-SYSDB-Split-sysdb_try_to_find_expected_dn-into-small.patch
+++ /dev/null
@@ -1,342 +0,0 @@
-From 57a070724f42bb01b8bb3f866e906f40643e0421 Mon Sep 17 00:00:00 2001
-From: Jakub Hrozek <jhrozek@redhat.com>
-Date: Fri, 28 Oct 2016 13:46:02 +0200
-Subject: [PATCH 150/151] SYSDB: Split sysdb_try_to_find_expected_dn() into
- smaller functions
-
-The function sysdb_try_to_find_expected_dn was performing several matching
-algorithms and thus it was getting big and hard to extend. This patch
-doesn't contain any functional changes, only shuffles the code around
-and splits the monolithic sysdb_try_to_find_expected_dn function into
-smaller blocks.
-
-Reviewed-by: Sumit Bose <sbose@redhat.com>
-(cherry picked from commit e5a984093ad7921c83da75272cede2b0e52ba2d6)
----
- src/db/sysdb_subdomains.c | 278 +++++++++++++++++++++++++++++-----------------
- 1 file changed, 179 insertions(+), 99 deletions(-)
-
-diff --git a/src/db/sysdb_subdomains.c b/src/db/sysdb_subdomains.c
-index ff83f914f31d566e050c74a3ef5f5745f8c93add..b011bad6c988db952622e7ddaabf015ec24e54ba 100644
---- a/src/db/sysdb_subdomains.c
-+++ b/src/db/sysdb_subdomains.c
-@@ -1145,74 +1145,29 @@ done:
-     return ret;
- }
- 
--errno_t sysdb_try_to_find_expected_dn(struct sss_domain_info *dom,
--                                      const char *domain_component_name,
--                                      struct sysdb_attrs **usr_attrs,
--                                      size_t count,
--                                      struct sysdb_attrs **exp_usr)
-+static errno_t match_cn_users(TALLOC_CTX *tmp_ctx,
-+                              struct sysdb_attrs **usr_attrs,
-+                              size_t count,
-+                              const char *dom_basedn,
-+                              struct sysdb_attrs **_result)
- {
--    char *dom_basedn;
--    size_t dom_basedn_len;
--    char *expected_basedn;
--    size_t expected_basedn_len;
-+    errno_t ret;
-+    const char *orig_dn;
-     size_t dn_len;
--    const char *orig_dn;
--    size_t c = 0;
--    int ret;
--    TALLOC_CTX *tmp_ctx;
--    struct ldb_context *ldb_ctx;
--    struct ldb_dn *ldb_dom_basedn;
--    int dom_basedn_comp_num;
--    struct ldb_dn *ldb_dn;
--    int dn_comp_num;
--    const char *component_name;
-     struct sysdb_attrs *result = NULL;
-     const char *result_dn_str = NULL;
-+    char *cn_users_basedn;
-+    size_t cn_users_basedn_len;
- 
--    if (dom == NULL || domain_component_name == NULL || usr_attrs == NULL
--            || count == 0) {
--        return EINVAL;
--    }
--
--    tmp_ctx = talloc_new(NULL);
--    if (tmp_ctx == NULL) {
--        DEBUG(SSSDBG_OP_FAILURE, "talloc_new failed.\n");
--        return ENOMEM;
--    }
--
--    ret = domain_to_basedn(tmp_ctx, dom->name, &dom_basedn);
--    if (ret != EOK) {
--        DEBUG(SSSDBG_OP_FAILURE, "domain_to_basedn failed.\n");
--        goto done;
--    }
--    expected_basedn = talloc_asprintf(tmp_ctx, "%s%s", "cn=users,", dom_basedn);
--    if (expected_basedn == NULL) {
-+    cn_users_basedn = talloc_asprintf(tmp_ctx, "%s%s", "cn=users,", dom_basedn);
-+    if (cn_users_basedn == NULL) {
-         ret = ENOMEM;
-         goto done;
-     }
-+    cn_users_basedn_len = strlen(cn_users_basedn);
-+    DEBUG(SSSDBG_TRACE_ALL, "cn=users baseDN is [%s].\n", cn_users_basedn);
- 
--    ldb_ctx = sysdb_ctx_get_ldb(dom->sysdb);
--    if (ldb_ctx == NULL) {
--        DEBUG(SSSDBG_OP_FAILURE, "Missing ldb context.\n");
--        ret = EINVAL;
--        goto done;
--    }
--
--    ldb_dom_basedn = ldb_dn_new(tmp_ctx, ldb_ctx, dom_basedn);
--    if (ldb_dom_basedn == NULL) {
--        DEBUG(SSSDBG_OP_FAILURE, "ldb_dn_new failed.\n");
--        ret = ENOMEM;
--        goto done;
--    }
--
--    dom_basedn_comp_num = ldb_dn_get_comp_num(ldb_dom_basedn);
--    dom_basedn_comp_num++;
--
--    DEBUG(SSSDBG_TRACE_ALL, "Expected BaseDN is [%s].\n", expected_basedn);
--    expected_basedn_len = strlen(expected_basedn);
--    dom_basedn_len = strlen(dom_basedn);
--
--    for (c = 0; c < count; c++) {
-+    for (size_t c = 0; c < count; c++) {
-         ret = sysdb_attrs_get_string(usr_attrs[c], SYSDB_ORIG_DN, &orig_dn);
-         if (ret != EOK) {
-             DEBUG(SSSDBG_OP_FAILURE, "sysdb_attrs_get_string failed.\n");
-@@ -1220,9 +1175,9 @@ errno_t sysdb_try_to_find_expected_dn(struct sss_domain_info *dom,
-         }
-         dn_len = strlen(orig_dn);
- 
--        if (dn_len > expected_basedn_len
--                && strcasecmp(orig_dn + (dn_len - expected_basedn_len),
--                              expected_basedn) == 0) {
-+        if (dn_len > cn_users_basedn_len
-+                && strcasecmp(orig_dn + (dn_len - cn_users_basedn_len),
-+                              cn_users_basedn) == 0) {
-             DEBUG(SSSDBG_TRACE_ALL,
-                   "Found matching dn [%s].\n", orig_dn);
-             if (result != NULL) {
-@@ -1237,52 +1192,177 @@ errno_t sysdb_try_to_find_expected_dn(struct sss_domain_info *dom,
-         }
-     }
- 
--    if (result == NULL) {
--        for (c = 0; c < count; c++) {
--            ret = sysdb_attrs_get_string(usr_attrs[c], SYSDB_ORIG_DN, &orig_dn);
--            if (ret != EOK) {
--                DEBUG(SSSDBG_OP_FAILURE, "sysdb_attrs_get_string failed.\n");
-+    ret = EOK;
-+done:
-+    *_result = result;
-+    return ret;
-+}
-+
-+static errno_t match_non_dc_comp(TALLOC_CTX *tmp_ctx,
-+                                 struct sss_domain_info *dom,
-+                                 struct sysdb_attrs **usr_attrs,
-+                                 size_t count,
-+                                 struct ldb_dn *ldb_basedn,
-+                                 const char *basedn,
-+                                 const char *domain_component_name,
-+                                 struct sysdb_attrs **_result)
-+{
-+    errno_t ret;
-+    const char *orig_dn;
-+    size_t orig_dn_len;
-+    size_t basedn_len;
-+    struct ldb_context *ldb_ctx;
-+    struct ldb_dn *ldb_orig_dn;
-+    int dn_comp_num;
-+    int basedn_comp_num;
-+    const char *component_name;
-+    struct sysdb_attrs *result = NULL;
-+    const char *result_dn_str = NULL;
-+
-+    ldb_ctx = sysdb_ctx_get_ldb(dom->sysdb);
-+    if (ldb_ctx == NULL) {
-+        DEBUG(SSSDBG_OP_FAILURE, "Missing ldb context.\n");
-+        ret = EINVAL;
-+        goto done;
-+    }
-+
-+    basedn_len = strlen(basedn);
-+
-+    basedn_comp_num = ldb_dn_get_comp_num(ldb_basedn);
-+    basedn_comp_num++;
-+
-+    for (size_t c = 0; c < count; c++) {
-+        ret = sysdb_attrs_get_string(usr_attrs[c], SYSDB_ORIG_DN, &orig_dn);
-+        if (ret != EOK) {
-+            DEBUG(SSSDBG_OP_FAILURE, "sysdb_attrs_get_string failed.\n");
-+            goto done;
-+        }
-+        orig_dn_len = strlen(orig_dn);
-+
-+        if (orig_dn_len > basedn_len
-+                /* Does the user's original DN with the non-domain part
-+                 * stripped match the domain base DN?
-+                 */
-+                && strcasecmp(orig_dn + (orig_dn_len - basedn_len),
-+                              basedn) == 0) {
-+            ldb_orig_dn = ldb_dn_new(tmp_ctx, ldb_ctx, orig_dn);
-+            if (ldb_orig_dn == NULL) {
-+                DEBUG(SSSDBG_OP_FAILURE, "ldb_dn_new failed");
-+                ret = ENOMEM;
-                 goto done;
-             }
--            dn_len = strlen(orig_dn);
- 
--            if (dn_len > dom_basedn_len
--                    && strcasecmp(orig_dn + (dn_len - dom_basedn_len),
--                                  dom_basedn) == 0) {
--                ldb_dn = ldb_dn_new(tmp_ctx, ldb_ctx, orig_dn);
--                if (ldb_dn == NULL) {
--                    DEBUG(SSSDBG_OP_FAILURE, "ldb_dn_new failed");
--                    ret = ENOMEM;
--                    goto done;
--                }
--
--                dn_comp_num = ldb_dn_get_comp_num(ldb_dn);
--                if (dn_comp_num > dom_basedn_comp_num) {
--                    component_name = ldb_dn_get_component_name(ldb_dn,
--                                           (dn_comp_num - dom_basedn_comp_num));
--                    DEBUG(SSSDBG_TRACE_ALL, "Comparing [%s] and [%s].\n",
--                                            component_name,
--                                            domain_component_name);
--                    if (component_name != NULL
--                            && strcasecmp(component_name,
--                                          domain_component_name) != 0) {
--                        DEBUG(SSSDBG_TRACE_ALL,
--                              "Found matching dn [%s].\n", orig_dn);
--                        if (result != NULL) {
--                            DEBUG(SSSDBG_OP_FAILURE,
--                                 "Found 2 matching DN [%s] and [%s], "
--                                 "expecting only 1.\n", result_dn_str, orig_dn);
--                            ret = EINVAL;
--                            goto done;
--                        }
--                        result = usr_attrs[c];
--                        result_dn_str = orig_dn;
-+            dn_comp_num = ldb_dn_get_comp_num(ldb_orig_dn);
-+            if (dn_comp_num > basedn_comp_num) {
-+                component_name = ldb_dn_get_component_name(ldb_orig_dn,
-+                        (dn_comp_num - basedn_comp_num));
-+                DEBUG(SSSDBG_TRACE_ALL, "Comparing [%s] and [%s].\n",
-+                      component_name,
-+                      domain_component_name);
-+                /* If the component is NOT a DC component, then the entry
-+                 * must come from our domain, perhaps from a child container.
-+                 * If it matched the DC component, the entry was from a child
-+                 * subdomain different from this one.
-+                 */
-+                if (component_name != NULL
-+                        && strcasecmp(component_name,
-+                                      domain_component_name) != 0) {
-+                    DEBUG(SSSDBG_TRACE_ALL,
-+                            "Found matching dn [%s].\n", orig_dn);
-+                    if (result != NULL) {
-+                        DEBUG(SSSDBG_OP_FAILURE,
-+                                "Found 2 matching DN [%s] and [%s], "
-+                                "expecting only 1.\n", result_dn_str, orig_dn);
-+                        ret = EINVAL;
-+                        goto done;
-                     }
-+                    result = usr_attrs[c];
-+                    result_dn_str = orig_dn;
-                 }
-             }
-         }
-     }
- 
-+    ret = EOK;
-+    *_result = result;
-+done:
-+    return ret;
-+}
-+
-+static errno_t match_basedn(TALLOC_CTX *tmp_ctx,
-+                            struct sss_domain_info *dom,
-+                            struct sysdb_attrs **usr_attrs,
-+                            size_t count,
-+                            const char *dom_basedn,
-+                            const char *domain_component_name,
-+                            struct sysdb_attrs **_result)
-+{
-+    struct ldb_context *ldb_ctx;
-+    struct ldb_dn *ldb_dom_basedn;
-+
-+    ldb_ctx = sysdb_ctx_get_ldb(dom->sysdb);
-+    if (ldb_ctx == NULL) {
-+        DEBUG(SSSDBG_OP_FAILURE, "Missing ldb context.\n");
-+        return EINVAL;
-+    }
-+
-+
-+    ldb_dom_basedn = ldb_dn_new(tmp_ctx, ldb_ctx, dom_basedn);
-+    if (ldb_dom_basedn == NULL) {
-+        DEBUG(SSSDBG_OP_FAILURE, "ldb_dn_new failed.\n");
-+        return ENOMEM;
-+    }
-+
-+    return match_non_dc_comp(tmp_ctx, dom,
-+                             usr_attrs, count,
-+                             ldb_dom_basedn, dom_basedn,
-+                             domain_component_name,
-+                             _result);
-+}
-+
-+errno_t sysdb_try_to_find_expected_dn(struct sss_domain_info *dom,
-+                                      const char *domain_component_name,
-+                                      struct sysdb_attrs **usr_attrs,
-+                                      size_t count,
-+                                      struct sysdb_attrs **exp_usr)
-+{
-+    char *dom_basedn;
-+    int ret;
-+    TALLOC_CTX *tmp_ctx;
-+    struct sysdb_attrs *result = NULL;
-+
-+    if (dom == NULL || domain_component_name == NULL
-+            || usr_attrs == NULL || count == 0) {
-+        return EINVAL;
-+    }
-+
-+    tmp_ctx = talloc_new(NULL);
-+    if (tmp_ctx == NULL) {
-+        DEBUG(SSSDBG_OP_FAILURE, "talloc_new failed.\n");
-+        return ENOMEM;
-+    }
-+
-+    ret = domain_to_basedn(tmp_ctx, dom->name, &dom_basedn);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_OP_FAILURE, "domain_to_basedn failed.\n");
-+        ret = EINVAL;
-+        goto done;
-+    }
-+
-+    ret = match_cn_users(tmp_ctx, usr_attrs, count, dom_basedn, &result);
-+    if (ret != EOK) {
-+        goto done;
-+    }
-+
-+    if (result == NULL) {
-+        ret = match_basedn(tmp_ctx, dom, usr_attrs,
-+                           count, dom_basedn, domain_component_name,
-+                           &result);
-+        if (ret != EOK) {
-+            goto done;
-+        }
-+    }
-+
-     if (result == NULL) {
-         DEBUG(SSSDBG_OP_FAILURE, "No matching DN found.\n");
-         ret = ENOENT;
--- 
-2.7.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
new file mode 100644
index 0000000..8ce1bb0
--- /dev/null
+++ b/SOURCES/0150-VALIDATOR-prevent-duplicite-report-from-subdomain-se.patch
@@ -0,0 +1,36 @@
+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-SYSDB-Augment-sysdb_try_to_find_expected_dn-to-match.patch b/SOURCES/0151-SYSDB-Augment-sysdb_try_to_find_expected_dn-to-match.patch
deleted file mode 100644
index d4e0758..0000000
--- a/SOURCES/0151-SYSDB-Augment-sysdb_try_to_find_expected_dn-to-match.patch
+++ /dev/null
@@ -1,283 +0,0 @@
-From a315e923a930e743de51c05183de383245f1c83e Mon Sep 17 00:00:00 2001
-From: Jakub Hrozek <jhrozek@redhat.com>
-Date: Mon, 31 Oct 2016 21:39:57 +0100
-Subject: [PATCH 151/151] SYSDB: Augment sysdb_try_to_find_expected_dn to match
- search base as well
-
-In cases where the domain name in sssd.conf does not match the AD
-domain, our previous matching process wouldn't match. This patch
-augments the matching as follows:
-    - the search base is known to sysdb_try_to_find_expected_dn and is
-      expected to be non-NULL
-    - the existing matching is ran first
-    - during the search base, matching, all the non-DC components are
-      stripped from the search base to 'canonicalize' the search base
-    - if only a single entry that matches with a non-DC DN component
-      (matching with a DC component would mean the DN comes from a
-      different domain) then this entry is a match and is returned
-
-Resolves:
-https://fedorahosted.org/sssd/ticket/3199
-
-Reviewed-by: Sumit Bose <sbose@redhat.com>
-(cherry picked from commit 24d8c85fae253f988165c112af208198cf48eef6)
----
- src/db/sysdb.h                             |  1 +
- src/db/sysdb_subdomains.c                  | 99 ++++++++++++++++++++++++++++++
- src/providers/ldap/sdap_async_initgroups.c |  8 ++-
- src/tests/cmocka/test_sysdb_subdomains.c   | 43 +++++++++++--
- 4 files changed, 144 insertions(+), 7 deletions(-)
-
-diff --git a/src/db/sysdb.h b/src/db/sysdb.h
-index a0279fb249e1258c9cb73a4fcab55e4b242c61f3..6a1bbf089206970892590e85ae1f5c741a79f969 100644
---- a/src/db/sysdb.h
-+++ b/src/db/sysdb.h
-@@ -1296,6 +1296,7 @@ errno_t sysdb_handle_original_uuid(const char *orig_name,
- 
- errno_t sysdb_try_to_find_expected_dn(struct sss_domain_info *dom,
-                                       const char *domain_component_name,
-+                                      const char *ldap_search_base,
-                                       struct sysdb_attrs **usr_attrs,
-                                       size_t count,
-                                       struct sysdb_attrs **exp_usr);
-diff --git a/src/db/sysdb_subdomains.c b/src/db/sysdb_subdomains.c
-index b011bad6c988db952622e7ddaabf015ec24e54ba..780140484f6f023bc6e8c12266e3b81ff016ec10 100644
---- a/src/db/sysdb_subdomains.c
-+++ b/src/db/sysdb_subdomains.c
-@@ -1320,8 +1320,97 @@ static errno_t match_basedn(TALLOC_CTX *tmp_ctx,
-                              _result);
- }
- 
-+static errno_t match_search_base(TALLOC_CTX *tmp_ctx,
-+                                 struct sss_domain_info *dom,
-+                                 const char *domain_component_name,
-+                                 const char *domain_search_base,
-+                                 struct sysdb_attrs **usr_attrs,
-+                                 size_t count,
-+                                 struct sysdb_attrs **_result)
-+{
-+    errno_t ret;
-+    bool ok;
-+    const char *search_base;
-+    struct ldb_context *ldb_ctx;
-+    struct sysdb_attrs *result = NULL;
-+    struct ldb_dn *ldb_search_base;
-+    int search_base_comp_num;
-+    int non_dc_comp_num;
-+    const char *component_name;
-+
-+    ldb_ctx = sysdb_ctx_get_ldb(dom->sysdb);
-+    if (ldb_ctx == NULL) {
-+        DEBUG(SSSDBG_OP_FAILURE, "Missing ldb context.\n");
-+        ret = EINVAL;
-+        goto done;
-+    }
-+
-+    ldb_search_base = ldb_dn_new(tmp_ctx, ldb_ctx, domain_search_base);
-+    if (ldb_search_base == NULL) {
-+        DEBUG(SSSDBG_OP_FAILURE, "ldb_dn_new failed.\n");
-+        ret = ENOMEM;
-+        goto done;
-+    }
-+
-+    /* strip non-DC components from the search base */
-+    search_base_comp_num = ldb_dn_get_comp_num(ldb_search_base);
-+    for (non_dc_comp_num = 0;
-+         non_dc_comp_num < search_base_comp_num;
-+         non_dc_comp_num++) {
-+
-+        component_name = ldb_dn_get_component_name(ldb_search_base,
-+                                                   non_dc_comp_num);
-+        if (strcasecmp(domain_component_name, component_name) == 0) {
-+            break;
-+        }
-+    }
-+
-+    if (non_dc_comp_num == search_base_comp_num) {
-+        /* The search base does not have any non-DC components, the search wouldn't
-+         * match anyway
-+         */
-+        ret = EOK;
-+        *_result = NULL;
-+        goto done;
-+    }
-+
-+    ok = ldb_dn_remove_child_components(ldb_search_base, non_dc_comp_num);
-+    if (!ok) {
-+        ret = EINVAL;
-+        goto done;
-+    }
-+
-+    search_base = ldb_dn_get_linearized(ldb_search_base);
-+    if (search_base == NULL) {
-+        ret = ENOMEM;
-+        goto done;
-+    }
-+
-+    ret = match_cn_users(tmp_ctx, usr_attrs, count, search_base, &result);
-+    if (ret != EOK) {
-+        goto done;
-+    }
-+
-+    if (result == NULL) {
-+        ret = match_non_dc_comp(tmp_ctx, dom,
-+                                usr_attrs, count,
-+                                ldb_search_base, search_base,
-+                                domain_component_name,
-+                                &result);
-+        if (ret != EOK) {
-+            goto done;
-+        }
-+    }
-+
-+    ret = EOK;
-+    *_result = result;
-+done:
-+    return ret;
-+}
-+
- errno_t sysdb_try_to_find_expected_dn(struct sss_domain_info *dom,
-                                       const char *domain_component_name,
-+                                      const char *domain_search_base,
-                                       struct sysdb_attrs **usr_attrs,
-                                       size_t count,
-                                       struct sysdb_attrs **exp_usr)
-@@ -1332,6 +1421,7 @@ errno_t sysdb_try_to_find_expected_dn(struct sss_domain_info *dom,
-     struct sysdb_attrs *result = NULL;
- 
-     if (dom == NULL || domain_component_name == NULL
-+            || domain_search_base == NULL
-             || usr_attrs == NULL || count == 0) {
-         return EINVAL;
-     }
-@@ -1364,6 +1454,15 @@ errno_t sysdb_try_to_find_expected_dn(struct sss_domain_info *dom,
-     }
- 
-     if (result == NULL) {
-+        ret = match_search_base(tmp_ctx, dom, domain_component_name,
-+                                   domain_search_base, usr_attrs, count,
-+                                   &result);
-+        if (ret != EOK) {
-+            goto done;
-+        }
-+    }
-+
-+    if (result == NULL) {
-         DEBUG(SSSDBG_OP_FAILURE, "No matching DN found.\n");
-         ret = ENOENT;
-         goto done;
-diff --git a/src/providers/ldap/sdap_async_initgroups.c b/src/providers/ldap/sdap_async_initgroups.c
-index 8eaba261c49082d086df9f19464ac0f40fae71fb..1173f4a875a1ea79990ff491ee7f2512f8435ac7 100644
---- a/src/providers/ldap/sdap_async_initgroups.c
-+++ b/src/providers/ldap/sdap_async_initgroups.c
-@@ -2947,7 +2947,13 @@ static void sdap_get_initgr_user(struct tevent_req *subreq)
-         DEBUG(SSSDBG_OP_FAILURE,
-               "Expected one user entry and got %zu\n", count);
- 
--        ret = sysdb_try_to_find_expected_dn(state->dom, "dc", usr_attrs, count,
-+        /* When matching against a search base, it's sufficient to pick only
-+         * the first search base because all bases in a single domain would
-+         * have the same DC= components
-+         */
-+        ret = sysdb_try_to_find_expected_dn(state->dom, "dc",
-+                                            state->sdom->search_bases[0]->basedn,
-+                                            usr_attrs, count,
-                                             &state->orig_user);
-         if (ret != EOK) {
-             DEBUG(SSSDBG_OP_FAILURE,
-diff --git a/src/tests/cmocka/test_sysdb_subdomains.c b/src/tests/cmocka/test_sysdb_subdomains.c
-index c9db56841e841472c81d00a79f475dbbd975ccb0..52056e0435d2793893f1a4e336f38acf7a70b2c0 100644
---- a/src/tests/cmocka/test_sysdb_subdomains.c
-+++ b/src/tests/cmocka/test_sysdb_subdomains.c
-@@ -520,7 +520,9 @@ static void test_try_to_find_expected_dn(void **state)
-     int ret;
-     struct sysdb_attrs *result;
-     struct sysdb_attrs *usr_attrs[10] = { NULL };
-+    struct sysdb_attrs *dom_usr_attrs[10] = { NULL };
-     struct sss_domain_info *dom;
-+    char *dom_basedn;
-     struct subdom_test_ctx *test_ctx =
-         talloc_get_type(*state, struct subdom_test_ctx);
- 
-@@ -528,6 +530,9 @@ static void test_try_to_find_expected_dn(void **state)
-                               "child2.test_sysdb_subdomains_2", true);
-     assert_non_null(dom);
- 
-+    ret = domain_to_basedn(test_ctx, dom->name, &dom_basedn);
-+    assert_int_equal(ret, EOK);
-+
-     usr_attrs[0] = sysdb_new_attrs(test_ctx);
-     assert_non_null(usr_attrs[0]);
- 
-@@ -535,13 +540,13 @@ static void test_try_to_find_expected_dn(void **state)
-                   "uid=user,cn=abc,dc=c2,dc=child2,dc=test_sysdb_subdomains_2");
-     assert_int_equal(ret, EOK);
- 
--    ret = sysdb_try_to_find_expected_dn(NULL, NULL, NULL, 0, NULL);
-+    ret = sysdb_try_to_find_expected_dn(NULL, NULL, NULL, NULL, 0, NULL);
-     assert_int_equal(ret, EINVAL);
- 
--    ret = sysdb_try_to_find_expected_dn(dom, "dc", usr_attrs, 1, &result);
-+    ret = sysdb_try_to_find_expected_dn(dom, "dc", dom_basedn, usr_attrs, 1, &result);
-     assert_int_equal(ret, ENOENT);
- 
--    ret = sysdb_try_to_find_expected_dn(dom, "xy", usr_attrs, 1, &result);
-+    ret = sysdb_try_to_find_expected_dn(dom, "xy", dom_basedn, usr_attrs, 1, &result);
-     assert_int_equal(ret, EOK);
-     assert_ptr_equal(result, usr_attrs[0]);
- 
-@@ -559,11 +564,11 @@ static void test_try_to_find_expected_dn(void **state)
-                  "uid=user2,cn=abc,dc=c2,dc=child2,dc=test_sysdb_subdomains_2");
-     assert_int_equal(ret, EOK);
- 
--    ret = sysdb_try_to_find_expected_dn(dom, "dc", usr_attrs, 3, &result);
-+    ret = sysdb_try_to_find_expected_dn(dom, "dc", dom_basedn, usr_attrs, 3, &result);
-     assert_int_equal(ret, EOK);
-     assert_ptr_equal(result, usr_attrs[1]);
- 
--    ret = sysdb_try_to_find_expected_dn(dom, "xy", usr_attrs, 3, &result);
-+    ret = sysdb_try_to_find_expected_dn(dom, "xy", dom_basedn, usr_attrs, 3, &result);
-     assert_int_equal(ret, EINVAL);
- 
-     /* Make sure cn=users match is preferred */
-@@ -575,10 +580,36 @@ static void test_try_to_find_expected_dn(void **state)
-                  "uid=user2,cn=abc,cn=users,dc=child2,dc=test_sysdb_subdomains_2");
-     assert_int_equal(ret, EOK);
- 
--    ret = sysdb_try_to_find_expected_dn(dom, "dc", usr_attrs, 3, &result);
-+    ret = sysdb_try_to_find_expected_dn(dom, "dc", dom_basedn, usr_attrs, 3, &result);
-     assert_int_equal(ret, EOK);
-     assert_ptr_equal(result, usr_attrs[2]);
- 
-+    /* test a case where the domain name does not match the basedn */
-+    dom->name = discard_const("default");
-+    dom_usr_attrs[0] = usr_attrs[0];
-+
-+    ret = sysdb_try_to_find_expected_dn(dom, "dc", dom_basedn, dom_usr_attrs, 1, &result);
-+    assert_int_equal(ret, ENOENT);
-+
-+    dom_usr_attrs[1] = usr_attrs[1];
-+    dom_usr_attrs[2] = usr_attrs[2];
-+
-+    /* Make sure cn=users match is preferred */
-+    ret = sysdb_try_to_find_expected_dn(dom, "dc", dom_basedn, dom_usr_attrs, 3, &result);
-+    assert_int_equal(ret, EOK);
-+    assert_ptr_equal(result, dom_usr_attrs[2]);
-+
-+    talloc_free(usr_attrs[2]);
-+    usr_attrs[2] = sysdb_new_attrs(test_ctx);
-+    assert_non_null(usr_attrs[2]);
-+    ret = sysdb_attrs_add_string(usr_attrs[2], SYSDB_ORIG_DN,
-+                 "uid=user2,cn=abc,dc=c2,dc=child2,dc=test_sysdb_subdomains_2");
-+    assert_int_equal(ret, EOK);
-+
-+    dom_usr_attrs[2] = usr_attrs[2];
-+    ret = sysdb_try_to_find_expected_dn(dom, "dc", dom_basedn, dom_usr_attrs, 3, &result);
-+    assert_int_equal(ret, EOK);
-+    assert_ptr_equal(result, usr_attrs[1]);
- 
-     talloc_free(usr_attrs[0]);
-     talloc_free(usr_attrs[1]);
--- 
-2.7.4
-
diff --git a/SOURCES/0151-test_config_check-Fix-few-issues.patch b/SOURCES/0151-test_config_check-Fix-few-issues.patch
new file mode 100644
index 0000000..9e48ed1
--- /dev/null
+++ b/SOURCES/0151-test_config_check-Fix-few-issues.patch
@@ -0,0 +1,92 @@
+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
new file mode 100644
index 0000000..89845c7
--- /dev/null
+++ b/SOURCES/0152-KRB5-Fix-access_provider-krb5.patch
@@ -0,0 +1,81 @@
+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/0152-SYSDB-Adding-lowercase-sudoUser-form.patch b/SOURCES/0152-SYSDB-Adding-lowercase-sudoUser-form.patch
deleted file mode 100644
index 504689e..0000000
--- a/SOURCES/0152-SYSDB-Adding-lowercase-sudoUser-form.patch
+++ /dev/null
@@ -1,106 +0,0 @@
-From 81ae14a34ad568b39b0077cc88112941802df27d Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Petr=20=C4=8Cech?= <pcech@redhat.com>
-Date: Wed, 12 Oct 2016 16:48:38 +0200
-Subject: [PATCH 152/153] SYSDB: Adding lowercase sudoUser form
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-If domain is not case sensitive we add lowercase form of usernames
-to sudoUser attributes. So we actually able to apply sudoRule on
-user Administrator@... with login admnistrator@...
-
-Resolves:
-https://fedorahosted.org/sssd/ticket/3203
-
-Reviewed-by: Pavel Březina <pbrezina@redhat.com>
-(cherry picked from commit f4a1046bb88d7a0ab3617e49ae94bfa849d10645)
----
- src/db/sysdb_sudo.c | 64 +++++++++++++++++++++++++++++++++++++++++++++++++++++
- 1 file changed, 64 insertions(+)
-
-diff --git a/src/db/sysdb_sudo.c b/src/db/sysdb_sudo.c
-index 601fb63f236a7ed9eede130fd8cf4c3a1559fc4b..4bd93ffc60caa1ce48b72ee207899da0c4196d61 100644
---- a/src/db/sysdb_sudo.c
-+++ b/src/db/sysdb_sudo.c
-@@ -852,6 +852,65 @@ sysdb_sudo_add_sss_attrs(struct sysdb_attrs *rule,
-     return EOK;
- }
- 
-+static errno_t sysdb_sudo_add_lowered_users(struct sss_domain_info *domain,
-+                                            struct sysdb_attrs *rule)
-+{
-+    TALLOC_CTX *tmp_ctx;
-+    const char **users = NULL;
-+    const char *lowered = NULL;
-+    errno_t ret;
-+
-+    if (domain->case_sensitive == true || rule == NULL) {
-+        return EOK;
-+    }
-+
-+    tmp_ctx = talloc_new(NULL);
-+    if (tmp_ctx == NULL) {
-+        return ENOMEM;
-+    }
-+
-+    ret = sysdb_attrs_get_string_array(rule, SYSDB_SUDO_CACHE_AT_USER, tmp_ctx,
-+                                       &users);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_OP_FAILURE, "Unable to get %s attribute [%d]: %s\n",
-+              SYSDB_SUDO_CACHE_AT_USER, ret, strerror(ret));
-+        goto done;
-+    }
-+
-+    if (users == NULL) {
-+        ret =  EOK;
-+        goto done;
-+    }
-+
-+    for (int i = 0; users[i] != NULL; i++) {
-+        lowered = sss_tc_utf8_str_tolower(tmp_ctx, users[i]);
-+        if (lowered == NULL) {
-+            DEBUG(SSSDBG_OP_FAILURE, "Cannot convert name to lowercase.\n");
-+            ret = ENOMEM;
-+            goto done;
-+        }
-+
-+        if (strcmp(users[i], lowered) == 0) {
-+            /* It protects us from adding duplicate. */
-+            continue;
-+        }
-+
-+        ret = sysdb_attrs_add_string(rule, SYSDB_SUDO_CACHE_AT_USER, lowered);
-+        if (ret != EOK) {
-+            DEBUG(SSSDBG_OP_FAILURE,
-+                  "Unable to add %s attribute [%d]: %s\n",
-+                  SYSDB_SUDO_CACHE_AT_USER, ret, strerror(ret));
-+            goto done;
-+        }
-+    }
-+
-+    ret = EOK;
-+
-+done:
-+    talloc_zfree(tmp_ctx);
-+    return ret;
-+}
-+
- static errno_t
- sysdb_sudo_store_rule(struct sss_domain_info *domain,
-                       struct sysdb_attrs *rule,
-@@ -868,6 +927,11 @@ sysdb_sudo_store_rule(struct sss_domain_info *domain,
- 
-     DEBUG(SSSDBG_TRACE_FUNC, "Adding sudo rule %s\n", name);
- 
-+    ret = sysdb_sudo_add_lowered_users(domain, rule);
-+    if (ret != EOK) {
-+        return ret;
-+    }
-+
-     ret = sysdb_sudo_add_sss_attrs(rule, name, cache_timeout, now);
-     if (ret != EOK) {
-         return ret;
--- 
-2.7.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
new file mode 100644
index 0000000..e06105d
--- /dev/null
+++ b/SOURCES/0153-BUILD-Improve-error-messages-for-optional-dependenci.patch
@@ -0,0 +1,86 @@
+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/0153-SYSDB-Fixing-of-sudorule-without-a-sudoUser.patch b/SOURCES/0153-SYSDB-Fixing-of-sudorule-without-a-sudoUser.patch
deleted file mode 100644
index ea2bc3f..0000000
--- a/SOURCES/0153-SYSDB-Fixing-of-sudorule-without-a-sudoUser.patch
+++ /dev/null
@@ -1,47 +0,0 @@
-From aed255dd1ae6ec40ca15a53c38a13354dd5c88e8 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Petr=20=C4=8Cech?= <pcech@redhat.com>
-Date: Wed, 16 Nov 2016 10:09:18 +0100
-Subject: [PATCH 153/153] SYSDB: Fixing of sudorule without a sudoUser
-
-This patch solved a regression caused by the recent patches
-to lowercase sudoUser -- in case sudoUser is missing completely,
-we abort the processing of this rule and all others.
-
-With this patch, we return ERR_MALFORMED_ENTRY and gracefully
-skip the malformed rule instead.
-
-Resolves:
-https://fedorahosted.org/sssd/ticket/3241
-
-Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
-(cherry picked from commit 7e23edbaa7a6bbd0b461d5792535896b6a77928b)
----
- src/db/sysdb_sudo.c | 5 +++++
- 1 file changed, 5 insertions(+)
-
-diff --git a/src/db/sysdb_sudo.c b/src/db/sysdb_sudo.c
-index 4bd93ffc60caa1ce48b72ee207899da0c4196d61..f5160f19012028f92723b9012fad85d803aa5137 100644
---- a/src/db/sysdb_sudo.c
-+++ b/src/db/sysdb_sudo.c
-@@ -874,6 +874,7 @@ static errno_t sysdb_sudo_add_lowered_users(struct sss_domain_info *domain,
-     if (ret != EOK) {
-         DEBUG(SSSDBG_OP_FAILURE, "Unable to get %s attribute [%d]: %s\n",
-               SYSDB_SUDO_CACHE_AT_USER, ret, strerror(ret));
-+        ret = ERR_MALFORMED_ENTRY;
-         goto done;
-     }
- 
-@@ -977,6 +978,10 @@ sysdb_sudo_store(struct sss_domain_info *domain,
-             /* Multiple CNs are error on server side, we can just ignore this
-              * rule and save the others. Loud debug message is in logs. */
-             continue;
-+        } else if (ret == ERR_MALFORMED_ENTRY) {
-+            /* Attribute SYSDB_SUDO_CACHE_AT_USER is missing but we can
-+             * continue with next sudoRule. */
-+            continue;
-         } else if (ret != EOK) {
-             goto done;
-         }
--- 
-2.7.4
-
diff --git a/SOURCES/0154-MONITOR-Do-not-set-up-watchdog-for-monitor.patch b/SOURCES/0154-MONITOR-Do-not-set-up-watchdog-for-monitor.patch
deleted file mode 100644
index 20ec5e1..0000000
--- a/SOURCES/0154-MONITOR-Do-not-set-up-watchdog-for-monitor.patch
+++ /dev/null
@@ -1,73 +0,0 @@
-From c7edf2dabc1cc3fc5298c872babf60a3001dfc2f Mon Sep 17 00:00:00 2001
-From: Jakub Hrozek <jhrozek@redhat.com>
-Date: Mon, 7 Nov 2016 11:58:20 +0100
-Subject: [PATCH 154/154] MONITOR: Do not set up watchdog for monitor
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-It makes little sense to set up watchdog for monitor because there is no
-entity that would restart the monitor. Therefore we should disable the
-watchdog for monitor process.
-
-Resolves:
-https://fedorahosted.org/sssd/ticket/3232
-
-Reviewed-by: Lukáš Slebodník <lslebodn@redhat.com>
-(cherry picked from commit fbe6644aa28d93f492434950680c5618eb567712)
----
- src/monitor/monitor.c |  2 ++
- src/util/server.c     | 11 +++++++----
- src/util/util.h       |  1 +
- 3 files changed, 10 insertions(+), 4 deletions(-)
-
-diff --git a/src/monitor/monitor.c b/src/monitor/monitor.c
-index 89dd0a91d1fbd41dd853cf8745de732b7059d02b..0f01580e01b6a0a9ab507a54183e5813133be5a9 100644
---- a/src/monitor/monitor.c
-+++ b/src/monitor/monitor.c
-@@ -2880,6 +2880,8 @@ int main(int argc, const char *argv[])
- 
-     /* we want a pid file check */
-     flags |= FLAGS_PID_FILE;
-+    /* the monitor should not run a watchdog on itself */
-+    flags |= FLAGS_NO_WATCHDOG;
- 
-     /* Open before server_setup() does to have logging
-      * during configuration checking */
-diff --git a/src/util/server.c b/src/util/server.c
-index 953cd3d6171bf2fbcefd9b9138c679100d5153c3..013e572e6284b16534910088f7801219251896d8 100644
---- a/src/util/server.c
-+++ b/src/util/server.c
-@@ -666,10 +666,13 @@ int server_setup(const char *name, int flags,
-                                      ret, strerror(ret));
-         return ret;
-     }
--    ret = setup_watchdog(ctx->event_ctx, watchdog_interval);
--    if (ret != EOK) {
--        DEBUG(SSSDBG_CRIT_FAILURE, "Watchdog setup failed.\n");
--        return ret;
-+
-+    if ((flags & FLAGS_NO_WATCHDOG) == 0) {
-+        ret = setup_watchdog(ctx->event_ctx, watchdog_interval);
-+        if (ret != EOK) {
-+            DEBUG(SSSDBG_CRIT_FAILURE, "Watchdog setup failed.\n");
-+            return ret;
-+        }
-     }
- 
-     sss_log(SSS_LOG_INFO, "Starting up");
-diff --git a/src/util/util.h b/src/util/util.h
-index 4449315f8b1a79ec915bc340b46188c440a27fa3..0cbd88bd5bdb7741148dcc40aeb5ee2abaa4ff98 100644
---- a/src/util/util.h
-+++ b/src/util/util.h
-@@ -84,6 +84,7 @@
- #define FLAGS_INTERACTIVE 0x0002
- #define FLAGS_PID_FILE 0x0004
- #define FLAGS_GEN_CONF 0x0008
-+#define FLAGS_NO_WATCHDOG 0x0010
- 
- #define PIPE_INIT { -1, -1 }
- 
--- 
-2.7.4
-
diff --git a/SOURCES/0154-RESPONDER_COMMON-update-certmaps-in-responders.patch b/SOURCES/0154-RESPONDER_COMMON-update-certmaps-in-responders.patch
new file mode 100644
index 0000000..0b1ddaa
--- /dev/null
+++ b/SOURCES/0154-RESPONDER_COMMON-update-certmaps-in-responders.patch
@@ -0,0 +1,77 @@
+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-AUTOFS-Fix-offline-resolution-of-autofs-maps.patch b/SOURCES/0155-AUTOFS-Fix-offline-resolution-of-autofs-maps.patch
deleted file mode 100644
index b8ccb04..0000000
--- a/SOURCES/0155-AUTOFS-Fix-offline-resolution-of-autofs-maps.patch
+++ /dev/null
@@ -1,57 +0,0 @@
-From 3f911325b21c53d3a932c48bc1bac328ec716268 Mon Sep 17 00:00:00 2001
-From: Jakub Hrozek <jhrozek@redhat.com>
-Date: Thu, 4 Aug 2016 17:58:32 +0200
-Subject: [PATCH 155/155] AUTOFS: Fix offline resolution of autofs maps
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-If talking to the Data Provider failed, we never re-tried looking into
-the cache. We should consult the cache on DP failures and return cached
-results, if possible.
-
-Resolves:
-https://fedorahosted.org/sssd/ticket/3080
-
-Reviewed-by: Pavel Březina <pbrezina@redhat.com>
----
- src/responder/autofs/autofssrv_cmd.c | 16 ++++++++++++----
- 1 file changed, 12 insertions(+), 4 deletions(-)
-
-diff --git a/src/responder/autofs/autofssrv_cmd.c b/src/responder/autofs/autofssrv_cmd.c
-index 9666ab2d195a581f18eaa7ff9bbc4c8167a71b15..f5aa25a483c3b3352f40e8cc66dfd3a24a60af0d 100644
---- a/src/responder/autofs/autofssrv_cmd.c
-+++ b/src/responder/autofs/autofssrv_cmd.c
-@@ -871,17 +871,25 @@ static void lookup_automntmap_cache_updated(uint16_t err_maj, uint32_t err_min,
-     if (err_maj) {
-         DEBUG(SSSDBG_CRIT_FAILURE,
-               "Unable to get information from Data Provider\n"
--               "Error: %u, %u, %s\n"
--               "Will try to return what we have in cache\n",
-+              "Error: %u, %u, %s\n"
-+              "Will try to return what we have in cache\n",
-                (unsigned int)err_maj, (unsigned int)err_min, err_msg);
--        /* Loop to the next domain if possible */
-+
-+        /* Try to fall back to cache */
-+        ret = lookup_automntmap_step(lookup_ctx);
-+        if (ret == EOK) {
-+            /* We have cached results to return */
-+            autofs_setent_notify(lookup_ctx->map, ret);
-+            return;
-+        }
-+
-+        /* Otherwise try the next domain */
-         if (dctx->cmd_ctx->check_next
-                 && (dctx->domain = get_next_domain(dctx->domain, 0))) {
-             dctx->check_provider = NEED_CHECK_PROVIDER(dctx->domain->provider);
-         }
-     }
- 
--    /* ok the backend returned, search to see if we have updated results */
-     ret = lookup_automntmap_step(lookup_ctx);
-     if (ret != EOK) {
-         if (ret == EAGAIN) {
--- 
-2.7.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
new file mode 100644
index 0000000..dc6a41c
--- /dev/null
+++ b/SOURCES/0155-tests-fix-test_pam_preauth_cert_no_logon_name.patch
@@ -0,0 +1,43 @@
+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-Prevent-use-after-free-in-fd_input_available.patch b/SOURCES/0156-Prevent-use-after-free-in-fd_input_available.patch
deleted file mode 100644
index ef0555c..0000000
--- a/SOURCES/0156-Prevent-use-after-free-in-fd_input_available.patch
+++ /dev/null
@@ -1,71 +0,0 @@
-From f8ecb57dcb7ce41b572cb67e6d2554296a54e738 Mon Sep 17 00:00:00 2001
-From: Carl Henrik Lunde <chlunde@ifi.uio.no>
-Date: Thu, 1 Dec 2016 00:09:00 +0100
-Subject: [PATCH 156/156] Prevent use after free in fd_input_available
-
-When both TEVENT_FD_WRITE and TEVENT_FD_READ are set, and an error/EOF
-occurs when reading from the socket, we will get a use after free
-in the second call ares_process_fd.  The first call will free the watch
-structure via a callback.
-
-Prevent this by calling ares_process_fd only once.
-
-Invalid read of size 4
-   at fd_input_available (async_resolv.c:147)
-   by epoll_event_loop (tevent_epoll.c:728)
-   by epoll_event_loop_once (tevent_epoll.c:926)
-   by std_event_loop_once (tevent_standard.c:114)
-   by _tevent_loop_once (tevent.c:533)
-   by tevent_common_loop_wait (tevent.c:637)
-   by std_event_loop_wait (tevent_standard.c:140)
-   by server_loop (server.c:702)
-   by main (data_provider_be.c:587)
- Address ... is 112 bytes inside a block of size 136 free'd
-   at free (vg_replace_malloc.c:530)
-   by _talloc_free_internal (talloc.c:1116)
-   by _talloc_free (talloc.c:1647)
-   by ares__close_sockets (ares__close_sockets.c:50)
-   by handle_error (ares_process.c:679)
-   by read_tcp_data (ares_process.c:391)
-   by processfds (ares_process.c:138)
-   by fd_input_available (async_resolv.c:144)
-   by epoll_event_loop (tevent_epoll.c:728)
-   by epoll_event_loop_once (tevent_epoll.c:926)
-   by std_event_loop_once (tevent_standard.c:114)
-   by _tevent_loop_once (tevent.c:533)
-   by tevent_common_loop_wait (tevent.c:637)
-   by std_event_loop_wait (tevent_standard.c:140)
-   by server_loop (server.c:702)
-
-Resolves:
-https://fedorahosted.org/sssd/ticket/3250
-
-Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
-(cherry picked from commit 9676b464dd428557ff5a648e1351a3972440396f)
----
- src/resolv/async_resolv.c | 9 +++------
- 1 file changed, 3 insertions(+), 6 deletions(-)
-
-diff --git a/src/resolv/async_resolv.c b/src/resolv/async_resolv.c
-index 58d5c6e550bb34cbaa50517323133fad4f900980..e29f679423eeccae1c8fb7af5fdafc69f051741a 100644
---- a/src/resolv/async_resolv.c
-+++ b/src/resolv/async_resolv.c
-@@ -140,12 +140,9 @@ fd_input_available(struct tevent_context *ev, struct tevent_fd *fde,
-         return;
-     }
- 
--    if (flags & TEVENT_FD_READ) {
--        ares_process_fd(watch->ctx->channel, watch->fd, ARES_SOCKET_BAD);
--    }
--    if (flags & TEVENT_FD_WRITE) {
--        ares_process_fd(watch->ctx->channel, ARES_SOCKET_BAD, watch->fd);
--    }
-+    ares_process_fd(watch->ctx->channel,
-+                    flags & TEVENT_FD_READ ? watch->fd : ARES_SOCKET_BAD,
-+                    flags & TEVENT_FD_WRITE ? watch->fd : ARES_SOCKET_BAD);
- }
- 
- static void
--- 
-2.9.3
-
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
new file mode 100644
index 0000000..94c6f8e
--- /dev/null
+++ b/SOURCES/0156-pam_sss-add-support-for-SSS_PAM_CERT_INFO_WITH_HINT.patch
@@ -0,0 +1,258 @@
+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-SSH-Use-default_domain_suffix-for-users-authorized-k.patch b/SOURCES/0157-SSH-Use-default_domain_suffix-for-users-authorized-k.patch
deleted file mode 100644
index cbfdcb1..0000000
--- a/SOURCES/0157-SSH-Use-default_domain_suffix-for-users-authorized-k.patch
+++ /dev/null
@@ -1,80 +0,0 @@
-From dfdd2fe5c177d70a0c5db383820f237ebc7e722f Mon Sep 17 00:00:00 2001
-From: Jakub Hrozek <jhrozek@redhat.com>
-Date: Thu, 24 Nov 2016 18:07:56 +0100
-Subject: [PATCH 157/157] SSH: Use default_domain_suffix for users' authorized
- keys
-
-In commit eeecc48d22a28bb69da56f6ffd8824163fc9bf00 we disabled
-default_domain_suffix for the SSH responder, but in a wrong way -- we
-disabled the functionality completely, also for users, not only for
-computers. This might have been correct at the time, since SSH keys in ID
-overrides are a relatively new feature, but it's definitely not correct
-in general.
-
-Instead, this patch restores the use of default_domain_suffix, but only
-for looking up public keys of users, not of computers.
-
-Resolves:
-https://fedorahosted.org/sssd/ticket/3259
-
-Reviewed-by: Petr Cech <pcech@redhat.com>
-(cherry picked from commit ed71fba97dfcf5b3f0f1834c06660c481b9ab3ce)
-(cherry picked from commit 2949fe58ac344c44d756ca309d4b2b7f3590cee3)
----
- src/responder/ssh/sshsrv_cmd.c | 12 ++++++++----
- 1 file changed, 8 insertions(+), 4 deletions(-)
-
-diff --git a/src/responder/ssh/sshsrv_cmd.c b/src/responder/ssh/sshsrv_cmd.c
-index ab721d66e5299ba6810a599a15a10094d5792092..2e64893dfc2018727e6fc5fb80b47bd7eb1fac58 100644
---- a/src/responder/ssh/sshsrv_cmd.c
-+++ b/src/responder/ssh/sshsrv_cmd.c
-@@ -36,7 +36,8 @@
- #include "responder/ssh/sshsrv_private.h"
- 
- static errno_t
--ssh_cmd_parse_request(struct ssh_cmd_ctx *cmd_ctx);
-+ssh_cmd_parse_request(struct ssh_cmd_ctx *cmd_ctx,
-+                      char *default_domain);
- 
- static errno_t
- ssh_user_pubkeys_search(struct ssh_cmd_ctx *cmd_ctx);
-@@ -57,7 +58,7 @@ sss_ssh_cmd_get_user_pubkeys(struct cli_ctx *cctx)
-     cmd_ctx->cctx = cctx;
-     cmd_ctx->is_user = true;
- 
--    ret = ssh_cmd_parse_request(cmd_ctx);
-+    ret = ssh_cmd_parse_request(cmd_ctx, cctx->rctx->default_domain);
-     if (ret != EOK) {
-         goto done;
-     }
-@@ -107,7 +108,7 @@ sss_ssh_cmd_get_host_pubkeys(struct cli_ctx *cctx)
-     cmd_ctx->cctx = cctx;
-     cmd_ctx->is_user = false;
- 
--    ret = ssh_cmd_parse_request(cmd_ctx);
-+    ret = ssh_cmd_parse_request(cmd_ctx, NULL);
-     if (ret != EOK) {
-         goto done;
-     }
-@@ -681,7 +682,8 @@ done:
- }
- 
- static errno_t
--ssh_cmd_parse_request(struct ssh_cmd_ctx *cmd_ctx)
-+ssh_cmd_parse_request(struct ssh_cmd_ctx *cmd_ctx,
-+                      char *default_domain)
- {
-     struct cli_protocol *pctx;
-     struct ssh_ctx *ssh_ctx;
-@@ -754,6 +756,8 @@ ssh_cmd_parse_request(struct ssh_cmd_ctx *cmd_ctx)
-                 return EINVAL;
-             }
-             c += domain_len;
-+        } else {
-+            domain = default_domain;
-         }
- 
-         DEBUG(SSSDBG_TRACE_FUNC,
--- 
-2.9.3
-
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
new file mode 100644
index 0000000..a9a73fd
--- /dev/null
+++ b/SOURCES/0157-add_pam_cert_response-add-support-for-SSS_PAM_CERT_I.patch
@@ -0,0 +1,104 @@
+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
new file mode 100644
index 0000000..a444dcf
--- /dev/null
+++ b/SOURCES/0158-PAM-send-user-name-hint-response-when-needed.patch
@@ -0,0 +1,335 @@
+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/0158-SERVER-Set-the-process-group-during-server_setup.patch b/SOURCES/0158-SERVER-Set-the-process-group-during-server_setup.patch
deleted file mode 100644
index 7fc6190..0000000
--- a/SOURCES/0158-SERVER-Set-the-process-group-during-server_setup.patch
+++ /dev/null
@@ -1,92 +0,0 @@
-From 7e0a61d1c10f30e694f5f536b374c72f774b50a1 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Fabiano=20Fid=C3=AAncio?= <fidencio@redhat.com>
-Date: Sun, 8 Jan 2017 23:27:57 +0100
-Subject: [PATCH 158/160] SERVER: Set the process group during server_setup()
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-By calling setpgid() in server_setup() we are able to kill the process
-in the watchdog by simply doing kill(-getpid(), SIGTERM).
-
-However, in order to have it working properly the SELinux policy for
-SSSD has to be updated and unless SSSD is ran with SELinux on permissive
-mode, each of the responders and the monitor will trigger a similar
-message:
-
-    Jan 09 14:31:50 client1.ipa.example audit[11630]: AVC avc:  denied
-    { setpgid } for  pid=11630 comm="sssd_pac"
-    scontext=system_u:system_r:sssd_t:s0
-    tcontext=system_u:system_r:sssd_t:s0 tclass=process permissive=0
-
-It's important to say that till SELinux policy is fixed, we might end up
-leaking some processes.
-
-Related:
-https://fedorahosted.org/sssd/ticket/3266
-
-Signed-off-by: Fabiano Fidêncio <fidencio@redhat.com>
-Reviewed-by: Lukáš Slebodník <lslebodn@redhat.com>
-(cherry picked from commit 087162b85e191af51637904702813969b35eaadc)
-(cherry picked from commit 442985a7af2262fab57f56c7a8cd40af10081610)
----
- src/monitor/monitor.c |  6 +++---
- src/util/server.c     | 11 +++++++++++
- 2 files changed, 14 insertions(+), 3 deletions(-)
-
-diff --git a/src/monitor/monitor.c b/src/monitor/monitor.c
-index 0f01580e01b6a0a9ab507a54183e5813133be5a9..17e980dbf165634491a05012399945f2d21c2056 100644
---- a/src/monitor/monitor.c
-+++ b/src/monitor/monitor.c
-@@ -1606,7 +1606,7 @@ static void monitor_quit(struct mt_ctx *mt_ctx, int ret)
-               "Terminating [%s][%d]\n", svc->name, svc->pid);
-         do {
-             errno = 0;
--            kret = kill(svc->pid, SIGTERM);
-+            kret = kill(-svc->pid, SIGTERM);
-             if (kret < 0) {
-                 error = errno;
-                 DEBUG(SSSDBG_CRIT_FAILURE, "Couldn't kill [%s][%d]: [%s]\n",
-@@ -1627,7 +1627,7 @@ static void monitor_quit(struct mt_ctx *mt_ctx, int ret)
-                               "[%d][%s] while waiting for [%s]\n",
-                                   error, strerror(error), svc->name);
-                         /* Forcibly kill this child */
--                        kill(svc->pid, SIGKILL);
-+                        kill(-svc->pid, SIGKILL);
-                         break;
-                     }
-                 } else if (pid != 0) {
-@@ -1642,7 +1642,7 @@ static void monitor_quit(struct mt_ctx *mt_ctx, int ret)
-                         DEBUG(SSSDBG_FATAL_FAILURE,
-                               "Child [%s] did not exit cleanly\n", svc->name);
-                         /* Forcibly kill this child */
--                        kill(svc->pid, SIGKILL);
-+                        kill(-svc->pid, SIGKILL);
-                     }
-                     killed = true;
-                 }
-diff --git a/src/util/server.c b/src/util/server.c
-index 013e572e6284b16534910088f7801219251896d8..d333c3c3c771c38005183831fc7a4b004a59a6c3 100644
---- a/src/util/server.c
-+++ b/src/util/server.c
-@@ -460,6 +460,17 @@ int server_setup(const char *name, int flags,
-     struct logrotate_ctx *lctx;
-     char *locale;
-     int watchdog_interval;
-+    pid_t my_pid;
-+
-+    my_pid = getpid();
-+    ret = setpgid(my_pid, my_pid);
-+    if (ret != EOK) {
-+        ret = errno;
-+        DEBUG(SSSDBG_MINOR_FAILURE,
-+              "Failed setting process group: %s[%d]. "
-+              "We might leak processes in case of failure\n",
-+              sss_strerror(ret), ret);
-+    }
- 
-     ret = chown_debug_file(NULL, uid, gid);
-     if (ret != EOK) {
--- 
-2.9.3
-
diff --git a/SOURCES/0159-WATCHDOG-define-and-use-_MAX_TICKS-as-3.patch b/SOURCES/0159-WATCHDOG-define-and-use-_MAX_TICKS-as-3.patch
deleted file mode 100644
index 9e2d757..0000000
--- a/SOURCES/0159-WATCHDOG-define-and-use-_MAX_TICKS-as-3.patch
+++ /dev/null
@@ -1,44 +0,0 @@
-From 57404a6b7ca0d86ba661e358f3544a32ef4e15bc Mon Sep 17 00:00:00 2001
-From: Jakub Hrozek <jhrozek@redhat.com>
-Date: Mon, 15 Aug 2016 12:54:20 +0200
-Subject: [PATCH 159/160] WATCHDOG: define and use _MAX_TICKS as 3
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Instead of using the number 3 directly, let's introduce and use
-WATCHDOG_MAX_TICKS.
-
-Reviewed-by: Petr Čech <pcech@redhat.com>
-(cherry picked from commit d7075a255a1f28e890539072e06d0140ffe0927c)
----
- src/util/util_watchdog.c | 6 +++---
- 1 file changed, 3 insertions(+), 3 deletions(-)
-
-diff --git a/src/util/util_watchdog.c b/src/util/util_watchdog.c
-index 1c27d73f13b3042ecb549a2184e1368e8339d199..c184fbd759bdbca4a9eae379ff0d87e2d1628470 100644
---- a/src/util/util_watchdog.c
-+++ b/src/util/util_watchdog.c
-@@ -22,6 +22,7 @@
- #include "util/util.h"
- 
- #define WATCHDOG_DEF_INTERVAL 10
-+#define WATCHDOG_MAX_TICKS 3
- 
- /* this is intentionally a global variable */
- struct watchdog_ctx {
-@@ -75,9 +76,8 @@ static void watchdog_handler(int sig)
-         return;
-     }
- 
--    /* if 3 ticks passed by kills itself */
--
--    if (__sync_add_and_fetch(&watchdog_ctx.ticks, 1) > 3) {
-+    /* if a pre-defined number of ticks passed by kills itself */
-+    if (__sync_add_and_fetch(&watchdog_ctx.ticks, 1) > WATCHDOG_MAX_TICKS) {
-         DEBUG(SSSDBG_FATAL_FAILURE,
-               "Watchdog timer overflow, killing process!\n");
-         orderly_shutdown(1);
--- 
-2.9.3
-
diff --git a/SOURCES/0159-sysdb-sysdb_get_certmap-allow-empty-certmap.patch b/SOURCES/0159-sysdb-sysdb_get_certmap-allow-empty-certmap.patch
new file mode 100644
index 0000000..290bab5
--- /dev/null
+++ b/SOURCES/0159-sysdb-sysdb_get_certmap-allow-empty-certmap.patch
@@ -0,0 +1,105 @@
+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-WATCHDOG-Avoid-non-async-signal-safe-from-the-signal.patch b/SOURCES/0160-WATCHDOG-Avoid-non-async-signal-safe-from-the-signal.patch
deleted file mode 100644
index 68c466e..0000000
--- a/SOURCES/0160-WATCHDOG-Avoid-non-async-signal-safe-from-the-signal.patch
+++ /dev/null
@@ -1,294 +0,0 @@
-From 6955a5c2a0abf38adace2553f21ea072416b1f61 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Fabiano=20Fid=C3=AAncio?= <fidencio@redhat.com>
-Date: Wed, 7 Dec 2016 21:36:56 +0100
-Subject: [PATCH 160/160] WATCHDOG: Avoid non async-signal-safe from the
- signal_handler
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-While debugging rhbz#1396912 a deadlock on sssd_be was noticed[0] and
-it's been caused by the use of non async-signal-safe functions from the
-signal_handler (please, see man 7 signal for more info about which are
-the async-signal-safe functions that can be used).
-
-In order to work this situation around a pipe has been added to the
-watchdog_ctx structure and, in case of clock screw,  a single byte is
-written to this pipe (which is an async-signal-safe operation) and the
-logic currently done by the timer handler to reset the watchdog will be
-done inside the fd handler in a safe way.
-
-With this patch we ended up losing some debug messages as
-orderly_shutdown() has been replaced by kill(-getpgrp(), SIGTERM) (or
-_exit(1) considering the cases where setting up the process group during
-the server_setup() has failed).
-Personally I don't think is worth the trouble to try to log those messages
-properly in this specific case.
-
-It's really worth to mention that a proper fix the clock screw situation
-should be implemented on samba's side, by having tevent using monotonic
-(or boottime) clock.
-
-[0]:
-  [root@dusan ~]# pstack 17922
-    #0  __lll_lock_wait_private () at ../nptl/sysdeps/unix/sysv/linux/x86_64/lowlevellock.S:95
-    #1  0x00007fe707d04f93 in _L_lock_14932 () from /lib64/libc.so.6
-    #2  0x00007fe707d02013 in __GI___libc_malloc (bytes=140630248638304, bytes@entry=15) at malloc.c:2891
-    #3  0x00007fe707d0888a in __GI___strdup (s=0x7fe707dff4f7 "/etc/localtime") at strdup.c:42
-    #4  0x00007fe707d31b61 in tzset_internal (always=<optimized out>, explicit=explicit@entry=1) at tzset.c:438
-    #5  0x00007fe707d32523 in __tz_convert (timer=timer@entry=0x7ffcd5d2b090, use_localtime=use_localtime@entry=1, tp=tp@entry=0x7fe708041d40 <_tmbuf>) at tzset.c:621
-    #6  0x00007fe707d30521 in __GI_localtime (t=t@entry=0x7ffcd5d2b090) at localtime.c:42
-    #7  0x00007fe70886c7b0 in sss_vdebug_fn (file=<optimized out>, line=<optimized out>, function=0x7fe70bff27f0 <__FUNCTION__.9379> "watchdog_handler", level=16, flags=flags@entry=0, format=format@entry=0x7fe70bff2760 "Watchdog timer overflow, killing process!\n", ap=ap@entry=0x7ffcd5d2b130) at src/util/debug.c:248
-    #8  0x00007fe70886c995 in sss_debug_fn (file=file@entry=0x7fe70bff263b "src/util/util_watchdog.c", line=line@entry=82, function=function@entry=0x7fe70bff27f0 <__FUNCTION__.9379> "watchdog_handler", level=level@entry=16, format=format@entry=0x7fe70bff2760 "Watchdog timer overflow, killing process!\n") at src/util/debug.c:284
-    #9  0x00007fe70bfdb409 in watchdog_handler (sig=<optimized out>) at src/util/util_watchdog.c:81
-    #10 <signal handler called>
-    #11 0x00007fe707cff664 in _int_malloc (av=av@entry=0x7fe70803c760 <main_arena>, bytes=bytes@entry=151) at malloc.c:3494
-    #12 0x00007fe707d01fbc in __GI___libc_malloc (bytes=bytes@entry=151) at malloc.c:2893
-    #13 0x00007fe708450749 in __talloc_with_prefix (prefix_len=0, size=55, context=0x7fe718373210) at ../talloc.c:668
-    #14 __talloc (size=55, context=0x7fe718373210) at ../talloc.c:708
-    #15 _talloc_named_const (name=0x7fe70bb7015d "../common/ldb_pack.c:425", size=55, context=0x7fe718373210) at ../talloc.c:865
-    #16 talloc_named_const (context=<optimized out>, size=size@entry=55, name=name@entry=0x7fe70bb7015d "../common/ldb_pack.c:425") at ../talloc.c:1606
-    #17 0x00007fe70bb61803 in ldb_unpack_data_only_attr_list (ldb=ldb@entry=0x7fe70e4d52c0, data=data@entry=0x7ffcd5d2b990, message=0x7fe7184aa1e0, list=list@entry=0x0, list_size=list_size@entry=0, nb_elements_in_db=nb_elements_in_db@entry=0x0) at ../common/ldb_pack.c:425
-    #18 0x00007fe70bb61a7d in ldb_unpack_data (ldb=ldb@entry=0x7fe70e4d52c0, data=data@entry=0x7ffcd5d2b990, message=<optimized out>) at ../common/ldb_pack.c:470
-    #19 0x00007fe6fdc29b46 in ltdb_parse_data_unpack (key=..., data=..., private_data=0x7ffcd5d2ba70) at ../ldb_tdb/ldb_search.c:249
-    #20 0x00007fe70a5e0a24 in tdb_parse_data (tdb=tdb@entry=0x7fe70e4eaa10, key=..., offset=15619748, len=414772, parser=parser@entry=0x7fe6fdc29b10 <ltdb_parse_data_unpack>, private_data=private_data@entry=0x7ffcd5d2ba70) at ../common/io.c:637
-    #21 0x00007fe70a5dc1fc in tdb_parse_record (tdb=0x7fe70e4eaa10, key=..., parser=parser@entry=0x7fe6fdc29b10 <ltdb_parse_data_unpack>, private_data=private_data@entry=0x7ffcd5d2ba70) at ../common/tdb.c:253
-    #22 0x00007fe6fdc29e7b in ltdb_search_dn1 (module=module@entry=0x7fe70e4eab50, dn=dn@entry=0x7fe7183c4940, msg=msg@entry=0x7fe7184aa1e0) at ../ldb_tdb/ldb_search.c:287
-    #23 0x00007fe6fdc2acbb in ltdb_dn_list_load (module=module@entry=0x7fe70e4eab50, dn=dn@entry=0x7fe7183c4940, list=list@entry=0x7fe7183c3a30) at ../ldb_tdb/ldb_index.c:181
-    #24 0x00007fe6fdc2bbbb in ltdb_index_add1 (module=module@entry=0x7fe70e4eab50, dn=dn@entry=0x7fe7183bf3e0 "name=testuser7045@domain.com,cn=users,cn=DOMAIN.COM,cn=sysdb", v_idx=v_idx@entry=0, el=<optimized out>, el=<optimized out>) at ../ldb_tdb/ldb_index.c:1134
-    #25 0x00007fe6fdc2c62c in ltdb_index_add_el (el=0x7fe7184aa3e0, dn=0x7fe7183bf3e0 "name=testuser7045@domain.com,cn=users,cn=DOMAIN.COM,cn=sysdb", module=0x7fe70e4eab50) at ../ldb_tdb/ldb_index.c:1180
-    #26 ltdb_index_add_element (module=module@entry=0x7fe70e4eab50, dn=<optimized out>, el=el@entry=0x7fe7184aa3e0) at ../ldb_tdb/ldb_index.c:1290
-    #27 0x00007fe6fdc290bb in ltdb_modify_internal (module=module@entry=0x7fe70e4eab50, msg=0x7fe7183bf0c0, req=req@entry=0x7fe7183bdc10) at ../ldb_tdb/ldb_tdb.c:903
-    #28 0x00007fe6fdc2958a in ltdb_modify (ctx=0x7fe7183c2950, ctx=0x7fe7183c2950) at ../ldb_tdb/ldb_tdb.c:998
-    #29 ltdb_callback (ev=<optimized out>, te=<optimized out>, t=..., private_data=<optimized out>) at ../ldb_tdb/ldb_tdb.c:1380
-    #30 0x00007fe708664b4f in tevent_common_loop_timer_delay (ev=ev@entry=0x7fe70e4d2890) at ../tevent_timed.c:341
-    #31 0x00007fe708665b5a in epoll_event_loop_once (ev=0x7fe70e4d2890, location=<optimized out>) at ../tevent_epoll.c:911
-    #32 0x00007fe708664257 in std_event_loop_once (ev=0x7fe70e4d2890, location=0x7fe70bb72ec5 "../common/ldb.c:631") at ../tevent_standard.c:114
-    #33 0x00007fe70866040d in _tevent_loop_once (ev=ev@entry=0x7fe70e4d2890, location=location@entry=0x7fe70bb72ec5 "../common/ldb.c:631") at ../tevent.c:533
-    #34 0x00007fe70bb6bc4f in ldb_wait (handle=0x7fe7183c4530, type=<optimized out>) at ../common/ldb.c:631
-    #35 0x00007fe70bb6c793 in ldb_autotransaction_request (ldb=0x7fe70e4d52c0, req=0x7fe7183bdc10) at ../common/ldb.c:573
-    #36 0x00007fe70bb6d263 in ldb_modify (ldb=ldb@entry=0x7fe70e4d52c0, message=<optimized out>) at ../common/ldb.c:1655
-    #37 0x00007fe70bfa2ab5 in sysdb_set_cache_entry_attr (ldb=0x7fe70e4d52c0, entry_dn=entry_dn@entry=0x7fe7183c4760, attrs=attrs@entry=0x7fe7183bf680, mod_op=mod_op@entry=2) at src/db/sysdb_ops.c:1159
-    #38 0x00007fe70bfa304d in sysdb_rep_ts_entry_attr (sysdb=0x7fe70e4eadd0, attrs=0x7fe7183bf680, entry_dn=0x7fe7183c4760) at src/db/sysdb_ops.c:1218
-    #39 sysdb_set_ts_entry_attr (sysdb=sysdb@entry=0x7fe70e4eadd0, entry_dn=entry_dn@entry=0x7fe7183c4760, attrs=attrs@entry=0x7fe7183bb840, mod_op=mod_op@entry=2) at src/db/sysdb_ops.c:1248
-    #40 0x00007fe70bfa4aa9 in sysdb_set_entry_attr (sysdb=0x7fe70e4eadd0, entry_dn=0x7fe7183c4760, attrs=attrs@entry=0x7fe7183bb840, mod_op=mod_op@entry=2) at src/db/sysdb_ops.c:1199
-    #41 0x00007fe70bfa4b5f in sysdb_set_user_attr (domain=domain@entry=0x7fe70e4d62f0, name=name@entry=0x7fe7183c01f0 "testuser7045@domain.com", attrs=attrs@entry=0x7fe7183bb840, mod_op=mod_op@entry=2) at src/db/sysdb_ops.c:1285
-    #42 0x00007fe70bfa58c3 in sysdb_add_user (domain=domain@entry=0x7fe70e4d62f0, name=name@entry=0x7fe7183c01f0 "testuser7045@domain.com", uid=uid@entry=1415408147, gid=<optimized out>, gid@entry=1415400513, gecos=gecos@entry=0x7fe710465d00 "Test User7045", homedir=homedir@entry=0x0, shell=shell@entry=0x0, orig_dn=orig_dn@entry=0x7fe710465940 "CN=Test User7045,OU=Sales,DC=DOMAIN,DC=COM", attrs=attrs@entry=0x7fe7183bb840, cache_timeout=cache_timeout@entry=5400, now=now@entry=1481105315) at src/db/sysdb_ops.c:1928
-    #43 0x00007fe70bfab271 in sysdb_store_new_user (now=1481105315, cache_timeout=5400, attrs=0x7fe7183bb840, orig_dn=0x7fe710465940 "CN=Test User7045,OU=Sales,DC=DOMAIN,DC=COM", shell=0x0, homedir=0x0, gecos=0x7fe710465d00 "Test User7045", gid=1415400513, uid=1415408147, name=0x7fe7183c01f0 "testuser7045@domain.com", domain=0x7fe70e4d62f0) at src/db/sysdb_ops.c:2549
-    #44 sysdb_store_user (domain=domain@entry=0x7fe70e4d62f0, name=0x7fe7183c01f0 "testuser7045@domain.com", pwd=pwd@entry=0x0, uid=1415408147, gid=1415400513, gecos=gecos@entry=0x7fe710465d00 "Test User7045", homedir=homedir@entry=0x0, shell=shell@entry=0x0, orig_dn=orig_dn@entry=0x7fe710465940 "CN=Test User7045,OU=Sales,DC=DOMAIN,DC=COM", attrs=attrs@entry=0x7fe7183bb840, remove_attrs=0x7fe7183c08a0, cache_timeout=cache_timeout@entry=5400, now=now@entry=1481105315) at src/db/sysdb_ops.c:2499
-    #45 0x00007fe6fba0d9f9 in sdap_save_user (memctx=memctx@entry=0x7fe70e544ee0, opts=opts@entry=0x7fe70e518400, dom=dom@entry=0x7fe70e4d62f0, attrs=<optimized out>, _usn_value=_usn_value@entry=0x7ffcd5d2c260, now=now@entry=1481105315) at src/providers/ldap/sdap_async_users.c:509
-    #46 0x00007fe6fba0df9a in sdap_save_users (memctx=memctx@entry=0x7fe70e544e40, sysdb=0x7fe70e4eadd0, dom=0x7fe70e4d62f0, opts=0x7fe70e518400, users=<optimized out>, num_users=10006, _usn_value=_usn_value@entry=0x7fe70e544e60) at src/providers/ldap/sdap_async_users.c:572
-    #47 0x00007fe6fba0e460 in sdap_get_users_done (subreq=<optimized out>) at src/providers/ldap/sdap_async_users.c:938
-    #48 0x00007fe6fba0c9d5 in sdap_search_user_process (subreq=0x0) at src/providers/ldap/sdap_async_users.c:814
-    #49 0x00007fe6fba07379 in generic_ext_search_handler (subreq=0x0, opts=<optimized out>) at src/providers/ldap/sdap_async.c:1689
-    #50 0x00007fe6fba0991b in sdap_get_generic_op_finished (op=<optimized out>, reply=<optimized out>, error=<optimized out>, pvt=<optimized out>) at src/providers/ldap/sdap_async.c:1621
-    #51 0x00007fe6fba083cd in sdap_process_message (ev=<optimized out>, sh=<optimized out>, msg=0x7fe70e5f9ce0) at src/providers/ldap/sdap_async.c:353
-    #52 sdap_process_result (ev=<optimized out>, pvt=<optimized out>) at src/providers/ldap/sdap_async.c:197
-    #53 0x00007fe708664b4f in tevent_common_loop_timer_delay (ev=ev@entry=0x7fe70e4cbc30) at ../tevent_timed.c:341
-    #54 0x00007fe708665b5a in epoll_event_loop_once (ev=0x7fe70e4cbc30, location=<optimized out>) at ../tevent_epoll.c:911
-    #55 0x00007fe708664257 in std_event_loop_once (ev=0x7fe70e4cbc30, location=0x7fe70bfee8e7 "src/util/server.c:702") at ../tevent_standard.c:114
-    #56 0x00007fe70866040d in _tevent_loop_once (ev=ev@entry=0x7fe70e4cbc30, location=location@entry=0x7fe70bfee8e7 "src/util/server.c:702") at ../tevent.c:533
-    #57 0x00007fe7086605ab in tevent_common_loop_wait (ev=0x7fe70e4cbc30, location=0x7fe70bfee8e7 "src/util/server.c:702") at ../tevent.c:637
-    #58 0x00007fe7086641f7 in std_event_loop_wait (ev=0x7fe70e4cbc30, location=0x7fe70bfee8e7 "src/util/server.c:702") at ../tevent_standard.c:140
-    #59 0x00007fe70bfd1993 in server_loop (main_ctx=0x7fe70e4cd080) at src/util/server.c:702
-    #60 0x00007fe70c84cb82 in main (argc=8, argv=<optimized out>) at src/providers/data_provider_be.c:587
-
-Resolves:
-https://fedorahosted.org/sssd/ticket/3266
-
-Signed-off-by: Fabiano Fidêncio <fidencio@redhat.com>
-Reviewed-by: Pavel Březina <pbrezina@redhat.com>
-Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
-Reviewed-by: Simo Sorce <simo@redhat.com>
-(cherry picked from commit e6a5f8c58539fc31fd81fac89cfc85703b4250ea)
-(cherry picked from commit 0606a71b698c4acf954ba7284e62acbd0aa5e52d)
----
- src/util/util_watchdog.c | 118 ++++++++++++++++++++++++++++++++++++++---------
- 1 file changed, 96 insertions(+), 22 deletions(-)
-
-diff --git a/src/util/util_watchdog.c b/src/util/util_watchdog.c
-index c184fbd759bdbca4a9eae379ff0d87e2d1628470..0df85b71c0b078dc2dfd23a39df82d1a10bad7d6 100644
---- a/src/util/util_watchdog.c
-+++ b/src/util/util_watchdog.c
-@@ -23,6 +23,7 @@
- 
- #define WATCHDOG_DEF_INTERVAL 10
- #define WATCHDOG_MAX_TICKS 3
-+#define DEFAULT_BUFFER_SIZE 4096
- 
- /* this is intentionally a global variable */
- struct watchdog_ctx {
-@@ -35,32 +36,27 @@ struct watchdog_ctx {
-     struct tevent_context *ev;
-     int input_interval;
-     time_t timestamp;
-+    struct tevent_fd *tfd;
-+    int pipefd[2];
- } watchdog_ctx;
- 
--static bool watchdog_detect_timeshift(void)
-+static void watchdog_detect_timeshift(void)
- {
-     time_t prev_time;
-     time_t cur_time;
--    errno_t ret;
- 
-     prev_time = watchdog_ctx.timestamp;
-     cur_time = watchdog_ctx.timestamp = time(NULL);
-     if (cur_time < prev_time) {
-         /* Time shift detected. We need to restart watchdog. */
--        DEBUG(SSSDBG_IMPORTANT_INFO, "Time shift detected, "
--              "restarting watchdog!\n");
--        teardown_watchdog();
--        ret = setup_watchdog(watchdog_ctx.ev, watchdog_ctx.input_interval);
--        if (ret != EOK) {
--            DEBUG(SSSDBG_FATAL_FAILURE, "Unable to restart watchdog "
--                  "[%d]: %s\n", ret, sss_strerror(ret));
--            orderly_shutdown(1);
-+        if (write(watchdog_ctx.pipefd[1], "1", 1) != 1) {
-+            if (getpid() == getpgrp()) {
-+                kill(-getpgrp(), SIGTERM);
-+            } else {
-+                _exit(1);
-+            }
-         }
--
--        return true;
-     }
--
--    return false;
- }
- 
- /* the watchdog is purposefully *not* handled by the tevent
-@@ -70,17 +66,16 @@ static bool watchdog_detect_timeshift(void)
-  * signals either */
- static void watchdog_handler(int sig)
- {
--    /* Do not count ticks if time shift was detected
--     * since watchdog was restarted. */
--    if (watchdog_detect_timeshift()) {
--        return;
--    }
-+
-+    watchdog_detect_timeshift();
- 
-     /* if a pre-defined number of ticks passed by kills itself */
-     if (__sync_add_and_fetch(&watchdog_ctx.ticks, 1) > WATCHDOG_MAX_TICKS) {
--        DEBUG(SSSDBG_FATAL_FAILURE,
--              "Watchdog timer overflow, killing process!\n");
--        orderly_shutdown(1);
-+        if (getpid() == getpgrp()) {
-+            kill(-getpgrp(), SIGTERM);
-+        } else {
-+            _exit(1);
-+        }
-     }
- }
- 
-@@ -109,10 +104,67 @@ static void watchdog_event_handler(struct tevent_context *ev,
-     }
- }
- 
-+static errno_t watchdog_fd_recv_data(int fd)
-+{
-+    ssize_t len;
-+    char buffer[DEFAULT_BUFFER_SIZE];
-+    errno_t ret;
-+
-+    errno = 0;
-+    len = read(fd, buffer, DEFAULT_BUFFER_SIZE);
-+    if (len == -1) {
-+        if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) {
-+            return EAGAIN;
-+        } else {
-+            ret = errno;
-+            DEBUG(SSSDBG_CRIT_FAILURE,
-+                  "write failed [%d]: %s\n", ret, strerror(ret));
-+            return ret;
-+        }
-+    }
-+
-+    return EOK;
-+}
-+
-+static void watchdog_fd_read_handler(struct tevent_context *ev,
-+                                     struct tevent_fd *fde,
-+                                     uint16_t flags,
-+                                     void *data)
-+{
-+    errno_t ret;
-+
-+    ret = watchdog_fd_recv_data(watchdog_ctx.pipefd[0]);
-+    switch(ret) {
-+    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. "
-+              "orderly_shutdown() will be called.\n", ret, strerror(ret));
-+        orderly_shutdown(1);
-+    }
-+
-+    DEBUG(SSSDBG_IMPORTANT_INFO, "Time shift detected, "
-+          "restarting watchdog!\n");
-+    teardown_watchdog();
-+    ret = setup_watchdog(watchdog_ctx.ev, watchdog_ctx.input_interval);
-+    if (ret != EOK) {
-+        DEBUG(SSSDBG_FATAL_FAILURE, "Unable to restart watchdog "
-+              "[%d]: %s\n", ret, sss_strerror(ret));
-+        orderly_shutdown(1);
-+    }
-+}
-+
- int setup_watchdog(struct tevent_context *ev, int interval)
- {
-     struct sigevent sev;
-     struct itimerspec its;
-+    struct tevent_fd *tfd;
-     int signum = SIGRTMIN;
-     int ret;
- 
-@@ -142,6 +194,21 @@ int setup_watchdog(struct tevent_context *ev, int interval)
-     watchdog_ctx.input_interval = interval;
-     watchdog_ctx.timestamp = time(NULL);
- 
-+    ret = pipe(watchdog_ctx.pipefd);
-+    if (ret == -1) {
-+        ret = errno;
-+        DEBUG(SSSDBG_FATAL_FAILURE,
-+              "pipe failed [%d] [%s].\n", ret, strerror(ret));
-+        return ret;
-+    }
-+
-+    sss_fd_nonblocking(watchdog_ctx.pipefd[0]);
-+    sss_fd_nonblocking(watchdog_ctx.pipefd[1]);
-+
-+    tfd = tevent_add_fd(ev, (TALLOC_CTX *)ev, watchdog_ctx.pipefd[0],
-+                        TEVENT_FD_READ, watchdog_fd_read_handler, NULL);
-+    watchdog_ctx.tfd = tfd;
-+
-     /* Start the timer */
-     /* we give 1 second head start to the watchdog event */
-     its.it_value.tv_sec = interval + 1;
-@@ -178,6 +245,13 @@ void teardown_watchdog(void)
-              ret, strerror(ret));
-     }
- 
-+    /* Free the tevent_fd */
-+    talloc_zfree(watchdog_ctx.tfd);
-+
-+    /* Close the pipefds */
-+    PIPE_FD_CLOSE(watchdog_ctx.pipefd[0]);
-+    PIPE_FD_CLOSE(watchdog_ctx.pipefd[1]);
-+
-     /* and kill the watchdog event */
-     talloc_free(watchdog_ctx.te);
- }
--- 
-2.9.3
-
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
new file mode 100644
index 0000000..6680a3b
--- /dev/null
+++ b/SOURCES/0160-sssctl-show-user-name-used-for-authentication-in-use.patch
@@ -0,0 +1,53 @@
+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
new file mode 100644
index 0000000..cc83129
--- /dev/null
+++ b/SOURCES/0161-RESP-Provide-a-reusable-request-to-fully-resolve-inc.patch
@@ -0,0 +1,267 @@
+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/0161-TESTS-Extending-sysdb-sudo-store-tests.patch b/SOURCES/0161-TESTS-Extending-sysdb-sudo-store-tests.patch
deleted file mode 100644
index 23939a1..0000000
--- a/SOURCES/0161-TESTS-Extending-sysdb-sudo-store-tests.patch
+++ /dev/null
@@ -1,225 +0,0 @@
-From e2f39220bc1cbfc87bbe41e84042ab8be9d046ec Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Petr=20=C4=8Cech?= <pcech@redhat.com>
-Date: Thu, 13 Oct 2016 09:31:52 +0200
-Subject: [PATCH 161/162] TESTS: Extending sysdb sudo store tests
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-We covered diference between case sensitive and case insensitive
-domains. If domain is case insensitive we add lowercase form of
-sudoUser to local sysdb cache.
-
-Resolves:
-https://fedorahosted.org/sssd/ticket/3203
-
-Reviewed-by: Pavel Březina <pbrezina@redhat.com>
-(cherry picked from commit 23637e2fd2b1fe42bdd2335893a11ac8016f56bc)
-(cherry picked from commit 143b1dcbbe865a139616a22b139e19bd772e46f0)
----
- src/tests/cmocka/test_sysdb_sudo.c | 168 ++++++++++++++++++++++++++++++++++++-
- 1 file changed, 167 insertions(+), 1 deletion(-)
-
-diff --git a/src/tests/cmocka/test_sysdb_sudo.c b/src/tests/cmocka/test_sysdb_sudo.c
-index 889de72371ac724de7c791d889a670cf25a36968..f21ff3655efbdc5b66a1fdbc24a51ec8174c3c8c 100644
---- a/src/tests/cmocka/test_sysdb_sudo.c
-+++ b/src/tests/cmocka/test_sysdb_sudo.c
-@@ -44,7 +44,7 @@ struct test_user {
-     const char *name;
-     uid_t uid;
-     gid_t gid;
--} users[] = { { "test_user1", 1001, 1001 },
-+} users[] = { { "test_USER1", 1001, 1001 },
-               { "test_user2", 1002, 1002 },
-               { "test_user3", 1003, 1003 } };
- 
-@@ -104,6 +104,29 @@ static void create_rule_attrs(struct sysdb_attrs *rule, int i)
-     assert_int_equal(ret, EOK);
- }
- 
-+static void create_rule_attrs_multiple_sudoUser(struct sysdb_attrs *rule)
-+{
-+    errno_t ret;
-+
-+    ret = sysdb_attrs_add_string_safe(rule, SYSDB_SUDO_CACHE_AT_CN,
-+                                      rules[0].name);
-+    assert_int_equal(ret, EOK);
-+
-+    ret = sysdb_attrs_add_string_safe(rule, SYSDB_SUDO_CACHE_AT_HOST,
-+                                      rules[0].host);
-+    assert_int_equal(ret, EOK);
-+
-+    ret = sysdb_attrs_add_string_safe(rule, SYSDB_SUDO_CACHE_AT_RUNASUSER,
-+                                      rules[0].as_user);
-+    assert_int_equal(ret, EOK);
-+
-+    for (int i = 0; i < 3; i++ ) {
-+        ret = sysdb_attrs_add_string_safe(rule, SYSDB_SUDO_CACHE_AT_USER,
-+                                          users[i].name);
-+        assert_int_equal(ret, EOK);
-+    }
-+}
-+
- static int get_stored_rules_count(struct sysdb_test_ctx *test_ctx)
- {
-     errno_t ret;
-@@ -217,6 +240,143 @@ void test_store_sudo(void **state)
-     talloc_zfree(msgs);
- }
- 
-+void test_store_sudo_case_sensitive(void **state)
-+{
-+    errno_t ret;
-+    char *filter;
-+    const char *attrs[] = { SYSDB_SUDO_CACHE_AT_CN, SYSDB_SUDO_CACHE_AT_HOST,
-+                            SYSDB_SUDO_CACHE_AT_RUNASUSER,
-+                            SYSDB_SUDO_CACHE_AT_USER, NULL };
-+    struct ldb_message **msgs = NULL;
-+    size_t msgs_count;
-+    const char *result;
-+    struct ldb_message_element *element;
-+    struct sysdb_attrs *rule;
-+    struct sysdb_test_ctx *test_ctx = talloc_get_type_abort(*state,
-+                                                         struct sysdb_test_ctx);
-+    const char *lowered_name = sss_tc_utf8_str_tolower(test_ctx, users[0].name);
-+
-+    rule = sysdb_new_attrs(test_ctx);
-+    assert_non_null(rule);
-+    create_rule_attrs_multiple_sudoUser(rule);
-+
-+    test_ctx->tctx->dom->case_sensitive = true;
-+
-+    ret = sysdb_sudo_store(test_ctx->tctx->dom, &rule, 1);
-+    assert_int_equal(ret, EOK);
-+
-+    filter = sysdb_sudo_filter_user(test_ctx, users[0].name, NULL, 0);
-+    assert_non_null(filter);
-+
-+    ret = sysdb_search_sudo_rules(test_ctx, test_ctx->tctx->dom, filter,
-+                                  attrs, &msgs_count, &msgs);
-+    assert_int_equal(ret, EOK);
-+
-+    assert_int_equal(msgs_count, 1);
-+
-+    result = ldb_msg_find_attr_as_string(msgs[0], SYSDB_SUDO_CACHE_AT_CN, NULL);
-+    assert_non_null(result);
-+    assert_string_equal(result, rules[0].name);
-+
-+    result = ldb_msg_find_attr_as_string(msgs[0], SYSDB_SUDO_CACHE_AT_HOST,
-+                                         NULL);
-+    assert_non_null(result);
-+    assert_string_equal(result, rules[0].host);
-+
-+    result = ldb_msg_find_attr_as_string(msgs[0], SYSDB_SUDO_CACHE_AT_RUNASUSER,
-+                                         NULL);
-+    assert_non_null(result);
-+    assert_string_equal(result, rules[0].as_user);
-+
-+    ret = ldb_msg_check_string_attribute(msgs[0], SYSDB_SUDO_CACHE_AT_USER,
-+				                         users[0].name);
-+    assert_int_equal(ret, 1);
-+
-+    ret = ldb_msg_check_string_attribute(msgs[0], SYSDB_SUDO_CACHE_AT_USER,
-+				                         lowered_name);
-+    assert_int_equal(ret, 0);
-+
-+    ret = ldb_msg_check_string_attribute(msgs[0], SYSDB_SUDO_CACHE_AT_USER,
-+				                         users[1].name);
-+    assert_int_equal(ret, 1);
-+
-+    ret = ldb_msg_check_string_attribute(msgs[0], SYSDB_SUDO_CACHE_AT_USER,
-+				                         users[2].name);
-+    assert_int_equal(ret, 1);
-+
-+    element = ldb_msg_find_element(msgs[0], SYSDB_SUDO_CACHE_AT_USER);
-+    assert_int_equal(element->num_values, 3);
-+
-+    talloc_zfree(lowered_name);
-+    talloc_zfree(rule);
-+    talloc_zfree(filter);
-+    talloc_zfree(msgs);
-+}
-+
-+void test_store_sudo_case_insensitive(void **state)
-+{
-+    errno_t ret;
-+    char *filter;
-+    const char *attrs[] = { SYSDB_SUDO_CACHE_AT_CN, SYSDB_SUDO_CACHE_AT_HOST,
-+                            SYSDB_SUDO_CACHE_AT_RUNASUSER,
-+                            SYSDB_SUDO_CACHE_AT_USER, NULL };
-+    struct ldb_message **msgs = NULL;
-+    size_t msgs_count;
-+    const char *result;
-+    struct ldb_message_element *element;
-+    struct sysdb_attrs *rule;
-+    struct sysdb_test_ctx *test_ctx = talloc_get_type_abort(*state,
-+                                                         struct sysdb_test_ctx);
-+    const char *lowered_name = sss_tc_utf8_str_tolower(test_ctx, users[0].name);
-+
-+    rule = sysdb_new_attrs(test_ctx);
-+    assert_non_null(rule);
-+    create_rule_attrs_multiple_sudoUser(rule);
-+
-+    test_ctx->tctx->dom->case_sensitive = false;
-+
-+    ret = sysdb_sudo_store(test_ctx->tctx->dom, &rule, 1);
-+    assert_int_equal(ret, EOK);
-+
-+    filter = sysdb_sudo_filter_user(test_ctx, users[0].name, NULL, 0);
-+    assert_non_null(filter);
-+
-+    ret = sysdb_search_sudo_rules(test_ctx, test_ctx->tctx->dom, filter,
-+                                  attrs, &msgs_count, &msgs);
-+    assert_int_equal(ret, EOK);
-+
-+    assert_int_equal(msgs_count, 1);
-+
-+    result = ldb_msg_find_attr_as_string(msgs[0], SYSDB_SUDO_CACHE_AT_CN, NULL);
-+    assert_non_null(result);
-+    assert_string_equal(result, rules[0].name);
-+
-+    result = ldb_msg_find_attr_as_string(msgs[0], SYSDB_SUDO_CACHE_AT_HOST,
-+                                         NULL);
-+    assert_non_null(result);
-+    assert_string_equal(result, rules[0].host);
-+
-+    result = ldb_msg_find_attr_as_string(msgs[0], SYSDB_SUDO_CACHE_AT_RUNASUSER,
-+                                         NULL);
-+    assert_non_null(result);
-+    assert_string_equal(result, rules[0].as_user);
-+
-+    for (int i = 0; i < 3; i++) {
-+        ret = ldb_msg_check_string_attribute(msgs[0], SYSDB_SUDO_CACHE_AT_USER,
-+                                             users[i].name);
-+        assert_int_equal(ret, 1);
-+    }
-+
-+    /* test there is no duplication of lowercase forms */
-+    element = ldb_msg_find_element(msgs[0], SYSDB_SUDO_CACHE_AT_USER);
-+    assert_int_equal(element->num_values, 4);
-+
-+    talloc_zfree(lowered_name);
-+    talloc_zfree(rule);
-+    talloc_zfree(filter);
-+    talloc_zfree(msgs);
-+}
-+
- void test_sudo_purge_by_filter(void **state)
- {
-     errno_t ret;
-@@ -648,6 +808,12 @@ int main(int argc, const char *argv[])
-         cmocka_unit_test_setup_teardown(test_store_sudo,
-                                         test_sysdb_setup,
-                                         test_sysdb_teardown),
-+        cmocka_unit_test_setup_teardown(test_store_sudo_case_sensitive,
-+                                        test_sysdb_setup,
-+                                        test_sysdb_teardown),
-+        cmocka_unit_test_setup_teardown(test_store_sudo_case_insensitive,
-+                                        test_sysdb_setup,
-+                                        test_sysdb_teardown),
- 
-         /* sysdb_sudo_purge() */
-         cmocka_unit_test_setup_teardown(test_sudo_purge_by_filter,
--- 
-2.9.3
-
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
new file mode 100644
index 0000000..b6c6eaa
--- /dev/null
+++ b/SOURCES/0162-IFP-Only-format-the-output-name-to-the-short-version.patch
@@ -0,0 +1,121 @@
+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/0162-SUDO-Only-store-lowercased-attribute-value-once.patch b/SOURCES/0162-SUDO-Only-store-lowercased-attribute-value-once.patch
deleted file mode 100644
index 7435a9f..0000000
--- a/SOURCES/0162-SUDO-Only-store-lowercased-attribute-value-once.patch
+++ /dev/null
@@ -1,85 +0,0 @@
-From 68cf1c69d2a19caca93d838745389f005ad66f5c Mon Sep 17 00:00:00 2001
-From: Jakub Hrozek <jhrozek@redhat.com>
-Date: Sun, 5 Feb 2017 20:25:23 +0100
-Subject: [PATCH 162/162] SUDO: Only store lowercased attribute value once
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-The current code doesn't handle the situation where lowercasing the
-sudoUser attribute would yield the same value again.
-
-For example:
-    sudoUser:   TUSER
-    sudoUser    tuser
-would break.
-
-This patch switches to using the utility function
-sysdb_attrs_add_lower_case_string() which already checks for duplicates.
-
-Resolves:
-https://fedorahosted.org/sssd/ticket/3301
-
-Reviewed-by: Fabiano Fidêncio <fidencio@redhat.com>
-Reviewed-by: Pavel Březina <pbrezina@redhat.com>
-(cherry picked from commit a5ecc93abb01cece628fdef04ebad43bba267419)
-(cherry picked from commit d5ddca8b44d00b92d4a70ea90d48247635a4e1ca)
----
- src/db/sysdb_sudo.c                | 17 +++--------------
- src/tests/cmocka/test_sysdb_sudo.c |  5 +++++
- 2 files changed, 8 insertions(+), 14 deletions(-)
-
-diff --git a/src/db/sysdb_sudo.c b/src/db/sysdb_sudo.c
-index f5160f19012028f92723b9012fad85d803aa5137..97a1bee99c0255579f42cc7263d3d755429cd417 100644
---- a/src/db/sysdb_sudo.c
-+++ b/src/db/sysdb_sudo.c
-@@ -857,7 +857,6 @@ static errno_t sysdb_sudo_add_lowered_users(struct sss_domain_info *domain,
- {
-     TALLOC_CTX *tmp_ctx;
-     const char **users = NULL;
--    const char *lowered = NULL;
-     errno_t ret;
- 
-     if (domain->case_sensitive == true || rule == NULL) {
-@@ -884,19 +883,9 @@ static errno_t sysdb_sudo_add_lowered_users(struct sss_domain_info *domain,
-     }
- 
-     for (int i = 0; users[i] != NULL; i++) {
--        lowered = sss_tc_utf8_str_tolower(tmp_ctx, users[i]);
--        if (lowered == NULL) {
--            DEBUG(SSSDBG_OP_FAILURE, "Cannot convert name to lowercase.\n");
--            ret = ENOMEM;
--            goto done;
--        }
--
--        if (strcmp(users[i], lowered) == 0) {
--            /* It protects us from adding duplicate. */
--            continue;
--        }
--
--        ret = sysdb_attrs_add_string(rule, SYSDB_SUDO_CACHE_AT_USER, lowered);
-+        ret = sysdb_attrs_add_lower_case_string(rule, true,
-+                                                SYSDB_SUDO_CACHE_AT_USER,
-+                                                users[i]);
-         if (ret != EOK) {
-             DEBUG(SSSDBG_OP_FAILURE,
-                   "Unable to add %s attribute [%d]: %s\n",
-diff --git a/src/tests/cmocka/test_sysdb_sudo.c b/src/tests/cmocka/test_sysdb_sudo.c
-index f21ff3655efbdc5b66a1fdbc24a51ec8174c3c8c..34afe120d97e99e3213a85bf7489a5e0f6309e4b 100644
---- a/src/tests/cmocka/test_sysdb_sudo.c
-+++ b/src/tests/cmocka/test_sysdb_sudo.c
-@@ -335,6 +335,11 @@ void test_store_sudo_case_insensitive(void **state)
- 
-     test_ctx->tctx->dom->case_sensitive = false;
- 
-+    ret = sysdb_attrs_add_lower_case_string(rule, false,
-+                                            SYSDB_SUDO_CACHE_AT_USER,
-+                                            users[0].name);
-+    assert_int_equal(ret, EOK);
-+
-     ret = sysdb_sudo_store(test_ctx->tctx->dom, &rule, 1);
-     assert_int_equal(ret, EOK);
- 
--- 
-2.9.3
-
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
new file mode 100644
index 0000000..b33cbba
--- /dev/null
+++ b/SOURCES/0163-IFP-Resolve-group-names-from-GIDs-if-required.patch
@@ -0,0 +1,204 @@
+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/0163-UTIL-Store-UPN-suffixes-when-creating-a-new-subdomai.patch b/SOURCES/0163-UTIL-Store-UPN-suffixes-when-creating-a-new-subdomai.patch
deleted file mode 100644
index 9418081..0000000
--- a/SOURCES/0163-UTIL-Store-UPN-suffixes-when-creating-a-new-subdomai.patch
+++ /dev/null
@@ -1,138 +0,0 @@
-From c860682bca53bbafe34b6c22ba151faf18ad2ace Mon Sep 17 00:00:00 2001
-From: Jakub Hrozek <jhrozek@redhat.com>
-Date: Thu, 2 Mar 2017 13:52:54 +0100
-Subject: [PATCH 163/163] UTIL: Store UPN suffixes when creating a new
- subdomain
-
-We used to store UPN suffixes pointer into the domain structure only if
-the domain changed, not when a new domain was created. As an effect, the
-enterprise principals flag was not enabled unless a domain changed,
-preventing logins with enterprise principals.
-
-Reviewed-by: Sumit Bose <sbose@redhat.com>
-(cherry picked from commit 8718ff9ccd29f6431bfa8630bfa3576b2692c9ee)
----
- src/db/sysdb_private.h          |  1 +
- src/db/sysdb_subdomains.c       | 11 ++++++++++-
- src/tests/cmocka/test_fqnames.c |  2 +-
- src/tests/cmocka/test_nss_srv.c |  2 +-
- src/tests/sysdb-tests.c         |  8 ++++----
- 5 files changed, 17 insertions(+), 7 deletions(-)
-
-diff --git a/src/db/sysdb_private.h b/src/db/sysdb_private.h
-index b6bf3706e6b9e49d8dd4984f3334b317d17ed9bf..bfd24799950ab3b31d57df11b8f91c0b2572f13a 100644
---- a/src/db/sysdb_private.h
-+++ b/src/db/sysdb_private.h
-@@ -190,6 +190,7 @@ struct sss_domain_info *new_subdomain(TALLOC_CTX *mem_ctx,
-                                       bool mpg,
-                                       bool enumerate,
-                                       const char *forest,
-+                                      const char **upn_suffixes,
-                                       uint32_t trust_direction);
- 
- /* Helper functions to deal with the timestamp cache should not be used
-diff --git a/src/db/sysdb_subdomains.c b/src/db/sysdb_subdomains.c
-index 780140484f6f023bc6e8c12266e3b81ff016ec10..4f326405f955abd462f892e6013a8c24764afd55 100644
---- a/src/db/sysdb_subdomains.c
-+++ b/src/db/sysdb_subdomains.c
-@@ -32,6 +32,7 @@ struct sss_domain_info *new_subdomain(TALLOC_CTX *mem_ctx,
-                                       bool mpg,
-                                       bool enumerate,
-                                       const char *forest,
-+                                      const char **upn_suffixes,
-                                       uint32_t trust_direction)
- {
-     struct sss_domain_info *dom;
-@@ -108,6 +109,14 @@ struct sss_domain_info *new_subdomain(TALLOC_CTX *mem_ctx,
-         }
-     }
- 
-+    if (upn_suffixes != NULL) {
-+        dom->upn_suffixes = dup_string_list(dom, upn_suffixes);
-+        if (dom->upn_suffixes == NULL) {
-+            DEBUG(SSSDBG_OP_FAILURE, "Failed to copy UPN upn_suffixes.\n");
-+            goto fail;
-+        }
-+    }
-+
-     dom->enumerate = enumerate;
-     dom->fqnames = true;
-     dom->mpg = mpg;
-@@ -442,7 +451,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,
--                                trust_direction);
-+                                upn_suffixes, trust_direction);
-             if (dom == NULL) {
-                 ret = ENOMEM;
-                 goto done;
-diff --git a/src/tests/cmocka/test_fqnames.c b/src/tests/cmocka/test_fqnames.c
-index f4cdd80ef94584fe4eb1f0578bf388da3ead824c..19788248a39774bb4509363145ac4ce0815b7d28 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, 0);
-+                                     NULL, false, false, NULL, NULL, 0);
-     assert_non_null(test_ctx->subdom);
- 
-     check_leaks_push(test_ctx);
-diff --git a/src/tests/cmocka/test_nss_srv.c b/src/tests/cmocka/test_nss_srv.c
-index 41425e76f3b76fafa917f33fcfef0946f2f71c7d..5eee82d78f4e4ab4dcdc0dcdfb24c2e7d017acf5 100644
---- a/src/tests/cmocka/test_nss_srv.c
-+++ b/src/tests/cmocka/test_nss_srv.c
-@@ -3084,7 +3084,7 @@ 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, 0);
-+                              false, false, NULL, NULL, 0);
-     assert_non_null(subdomain);
- 
-     ret = sysdb_subdomain_store(nss_test_ctx->tctx->sysdb,
-diff --git a/src/tests/sysdb-tests.c b/src/tests/sysdb-tests.c
-index d1450015cb0f0b073045e7b6031423e3f5494d78..6fd1988668124dc2dc922b41d3f7387c6d00c486 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, 0);
-+                              false, false, NULL, NULL, 0);
-     fail_if(subdomain == NULL, "Failed to create new subdomain.");
- 
-     ret = sss_names_init_from_args(test_ctx,
-@@ -5468,7 +5468,7 @@ 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, 0);
-+                              false, false, NULL, NULL, 0);
-     fail_unless(subdomain != NULL, "Failed to create new subdomin.");
-     ret = sysdb_subdomain_store(test_ctx->sysdb,
-                                 testdom[0], testdom[1], testdom[2], testdom[3],
-@@ -5547,7 +5547,7 @@ 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, 0);
-+                              false, false, NULL, NULL, 0);
-     fail_unless(subdomain != NULL, "Failed to create new subdomin.");
-     ret = sysdb_subdomain_store(test_ctx->sysdb,
-                                 testdom[0], testdom[1], testdom[2], testdom[3],
-@@ -5620,7 +5620,7 @@ 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, 0);
-+                              false, false, NULL, NULL, 0);
-     fail_unless(subdomain != NULL, "Failed to create new subdomin.");
-     ret = sysdb_subdomain_store(test_ctx->sysdb,
-                                 testdom[0], testdom[1], testdom[2], testdom[3],
--- 
-2.9.3
-
diff --git a/SOURCES/0164-Move-sized_output_name-and-sized_domain_name-into-re.patch b/SOURCES/0164-Move-sized_output_name-and-sized_domain_name-into-re.patch
deleted file mode 100644
index dd5e219..0000000
--- a/SOURCES/0164-Move-sized_output_name-and-sized_domain_name-into-re.patch
+++ /dev/null
@@ -1,272 +0,0 @@
-From ed3bdd8998c5e0e18f9b8cfefa9acd00b8531585 Mon Sep 17 00:00:00 2001
-From: Jakub Hrozek <jhrozek@redhat.com>
-Date: Wed, 19 Apr 2017 17:44:40 +0200
-Subject: [PATCH 164/165] 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/nsssrv_cmd.c          | 89 +-------------------------------
- 3 files changed, 112 insertions(+), 88 deletions(-)
-
-diff --git a/src/responder/common/responder.h b/src/responder/common/responder.h
-index 9e3b2fdbda4e30b859df597374fc7d490b1720e5..fd6a67ba72f28f52d6cc1bbad16e1a7245462c93 100644
---- a/src/responder/common/responder.h
-+++ b/src/responder/common/responder.h
-@@ -358,4 +358,25 @@ char *sss_resp_create_fqname(TALLOC_CTX *mem_ctx,
-                              bool name_is_upn,
-                              const char *orig_name);
- 
-+/**
-+ * 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 c604c64a652221521ec7114b8588186f087eb11a..1db8a2283f3e96bccebe9a8443d5d04b9d5f4a54 100644
---- a/src/responder/common/responder_common.c
-+++ b/src/responder/common/responder_common.c
-@@ -1248,3 +1248,93 @@ void responder_set_fd_limit(rlim_t fd_limit)
-                "Proceeding with system values\n");
-     }
- }
-+
-+/**
-+ * 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/nsssrv_cmd.c b/src/responder/nss/nsssrv_cmd.c
-index b64cea2a53ec6032904237b0afc1377022c2c804..0a9dd301266d4db1be1977cc8c337abde7cd79f5 100644
---- a/src/responder/nss/nsssrv_cmd.c
-+++ b/src/responder/nss/nsssrv_cmd.c
-@@ -253,93 +253,6 @@ static const char *get_shell_override(TALLOC_CTX *mem_ctx,
-     return talloc_strdup(mem_ctx, NOLOGIN_SHELL);
- }
- 
--static 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;
--}
--
--static 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;
--}
--
- static int fill_pwent(struct sss_packet *packet,
-                       struct sss_domain_info *dom,
-                       struct nss_ctx *nctx,
-@@ -2727,7 +2640,7 @@ static int fill_members(struct sss_packet *packet,
-             }
-         }
- 
--        ret = sized_member_name(tmp_ctx, rctx, fqname, &name);
-+        ret = sized_domain_name(tmp_ctx, rctx, fqname, &name);
-         if (ret != EOK) {
-             DEBUG(SSSDBG_OP_FAILURE, "sized_member_name failed: %d\n", ret);
-             goto done;
--- 
-2.9.3
-
diff --git a/SOURCES/0164-ldap-handle-certmap-errors-gracefully.patch b/SOURCES/0164-ldap-handle-certmap-errors-gracefully.patch
new file mode 100644
index 0000000..caebbc1
--- /dev/null
+++ b/SOURCES/0164-ldap-handle-certmap-errors-gracefully.patch
@@ -0,0 +1,56 @@
+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-IFP-Use-sized_domain_name-to-format-the-groups-the-u.patch b/SOURCES/0165-IFP-Use-sized_domain_name-to-format-the-groups-the-u.patch
deleted file mode 100644
index 6a83760..0000000
--- a/SOURCES/0165-IFP-Use-sized_domain_name-to-format-the-groups-the-u.patch
+++ /dev/null
@@ -1,95 +0,0 @@
-From e32d3a1074a64a1976047fb9fcd0defac439f97c Mon Sep 17 00:00:00 2001
-From: Jakub Hrozek <jhrozek@redhat.com>
-Date: Wed, 19 Apr 2017 17:46:03 +0200
-Subject: [PATCH 165/165] 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 97fad47e90c2155fc7243e68a7110ba9fd28aa39..f14a41da096edd2ace40c6faf8fb6f6dc3b503fe 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/0165-SECRETS-Fix-warning-Wpointer-bool-conversion.patch b/SOURCES/0165-SECRETS-Fix-warning-Wpointer-bool-conversion.patch
new file mode 100644
index 0000000..0a3ecf7
--- /dev/null
+++ b/SOURCES/0165-SECRETS-Fix-warning-Wpointer-bool-conversion.patch
@@ -0,0 +1,53 @@
+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
new file mode 100644
index 0000000..f9eb273
--- /dev/null
+++ b/SOURCES/0166-IPA-Fix-the-PAM-error-code-that-auth-code-expects-to.patch
@@ -0,0 +1,50 @@
+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/0166-LDAP-AD-Do-not-fail-in-case-rfc2307bis_nested_groups.patch b/SOURCES/0166-LDAP-AD-Do-not-fail-in-case-rfc2307bis_nested_groups.patch
deleted file mode 100644
index d04aedf..0000000
--- a/SOURCES/0166-LDAP-AD-Do-not-fail-in-case-rfc2307bis_nested_groups.patch
+++ /dev/null
@@ -1,49 +0,0 @@
-From b9460652c3ab86b1b0cfe1e8ea868e6e0bb492ad 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 166/166] 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>
-(cherry picked from commit df4b24bed15f45bf286fb0102fd397218fdd4186)
-(cherry picked from commit 4540d9f6817c78eef7b6e2d79245434811b59ad9)
----
- 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 1fee4ab43a6c13803a088ffa4695dde7f39b3d2b..904cffd820fa1f0aeb86929b41b0d7523f36d315 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/0167-HBAC-Do-not-rely-on-originalMemberOf-use-the-sysdb-m.patch b/SOURCES/0167-HBAC-Do-not-rely-on-originalMemberOf-use-the-sysdb-m.patch
deleted file mode 100644
index 343c654..0000000
--- a/SOURCES/0167-HBAC-Do-not-rely-on-originalMemberOf-use-the-sysdb-m.patch
+++ /dev/null
@@ -1,175 +0,0 @@
-From bef2586e998a3fa2a85475d19e8cf257383b79db Mon Sep 17 00:00:00 2001
-From: Jakub Hrozek <jhrozek@redhat.com>
-Date: Sun, 9 Apr 2017 20:50:47 +0200
-Subject: [PATCH 167/167] 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/0167-pam_sss-Fix-checking-of-empty-string-cert_user.patch b/SOURCES/0167-pam_sss-Fix-checking-of-empty-string-cert_user.patch
new file mode 100644
index 0000000..b8e2d31
--- /dev/null
+++ b/SOURCES/0167-pam_sss-Fix-checking-of-empty-string-cert_user.patch
@@ -0,0 +1,52 @@
+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
new file mode 100644
index 0000000..67e6f57
--- /dev/null
+++ b/SOURCES/0168-CACHE_REQ-Simplify-_search_ncache_filter.patch
@@ -0,0 +1,124 @@
+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
new file mode 100644
index 0000000..6d53d21
--- /dev/null
+++ b/SOURCES/0169-CACHE_REQ_SEARCH-Check-for-filtered-users-groups-als.patch
@@ -0,0 +1,61 @@
+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
new file mode 100644
index 0000000..78326bf
--- /dev/null
+++ b/SOURCES/0170-cache_req-Do-not-use-default_domain_suffix-with-netg.patch
@@ -0,0 +1,34 @@
+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
new file mode 100644
index 0000000..22655c1
--- /dev/null
+++ b/SOURCES/0171-krb5-disable-enterprise-principals-during-password-c.patch
@@ -0,0 +1,47 @@
+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
new file mode 100644
index 0000000..7c9d403
--- /dev/null
+++ b/SOURCES/0172-pam_sss-Fix-leaking-of-memory-in-case-of-failures.patch
@@ -0,0 +1,44 @@
+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
new file mode 100644
index 0000000..be61e37
--- /dev/null
+++ b/SOURCES/0173-IFP-Add-domain-and-domainname-attributes-to-the-user.patch
@@ -0,0 +1,372 @@
+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
new file mode 100644
index 0000000..4d39c22
--- /dev/null
+++ b/SOURCES/0174-IFP-Fix-error-handling-in-ifp_user_get_attr_handle_r.patch
@@ -0,0 +1,39 @@
+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
new file mode 100644
index 0000000..748f9e8
--- /dev/null
+++ b/SOURCES/0175-SYSDB-Return-ERR_NO_TS-when-there-s-no-timestamp-cac.patch
@@ -0,0 +1,76 @@
+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
new file mode 100644
index 0000000..38fdfa6
--- /dev/null
+++ b/SOURCES/0176-SYSDB-Internally-expose-sysdb_search_ts_matches.patch
@@ -0,0 +1,90 @@
+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
new file mode 100644
index 0000000..89eb87a
--- /dev/null
+++ b/SOURCES/0177-SYSDB-Make-the-usage-of-the-filter-more-generic-for-.patch
@@ -0,0 +1,157 @@
+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
new file mode 100644
index 0000000..b4cbbf8
--- /dev/null
+++ b/SOURCES/0178-SYSDB_OPS-Mark-an-entry-as-expired-also-in-the-times.patch
@@ -0,0 +1,55 @@
+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
new file mode 100644
index 0000000..e6512c2
--- /dev/null
+++ b/SOURCES/0179-SYSDB_OPS-Invalidate-a-cache-entry-also-in-the-ts_ca.patch
@@ -0,0 +1,49 @@
+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
new file mode 100644
index 0000000..5bfa491
--- /dev/null
+++ b/SOURCES/0180-SYSDB-Introduce-_search_-users-groups-_by_timestamp.patch
@@ -0,0 +1,278 @@
+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
new file mode 100644
index 0000000..7bba059
--- /dev/null
+++ b/SOURCES/0181-LDAP_ID_CLEANUP-Use-sysdb_search_-_by_timestamp.patch
@@ -0,0 +1,48 @@
+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
new file mode 100644
index 0000000..4802ef1
--- /dev/null
+++ b/SOURCES/0182-krb5-use-plain-principal-if-password-is-expired.patch
@@ -0,0 +1,66 @@
+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
new file mode 100644
index 0000000..1af69a1
--- /dev/null
+++ b/SOURCES/0183-RESPONDER-Use-fqnames-as-output-when-needed.patch
@@ -0,0 +1,279 @@
+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
new file mode 100644
index 0000000..e6bd53b
--- /dev/null
+++ b/SOURCES/0184-DOMAIN-Add-sss_domain_info_-get-set-_output_fqnames.patch
@@ -0,0 +1,128 @@
+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
new file mode 100644
index 0000000..fa94e3d
--- /dev/null
+++ b/SOURCES/0185-GPO-Fix-typo-in-DEBUG-message.patch
@@ -0,0 +1,27 @@
+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
new file mode 100644
index 0000000..431f35a
--- /dev/null
+++ b/SOURCES/0186-SDAP-Update-parent-sdap_list.patch
@@ -0,0 +1,97 @@
+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/SPECS/sssd.spec b/SPECS/sssd.spec
index ae74e0a..533bdf8 100644
--- a/SPECS/sssd.spec
+++ b/SPECS/sssd.spec
@@ -19,7 +19,7 @@
 
     %global with_krb5_localauth_plugin 1
 
-%global libwbc_alternatives_version 0.12
+%global libwbc_alternatives_version 0.13
 %global libwbc_lib_version %{libwbc_alternatives_version}.0
 %global libwbc_alternatives_suffix %nil
 %if 0%{?__isa_bits} == 64
@@ -31,183 +31,210 @@
     %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: sssd
-Version: 1.14.0
-Release: 43%{?dist}.18
+Version: 1.15.2
+Release: 50%{?dist}
 Group: Applications/System
 Summary: System Security Services Daemon
 License: GPLv3+
-URL: http://fedorahosted.org/sssd/
-Source0: https://fedorahosted.org/released/sssd/%{name}-%{version}.tar.gz
+URL: https://pagure.io/SSSD/sssd/
+Source0: https://releases.pagure.org/SSSD/sssd/sssd-%{version}.tar.gz
 BuildRoot: %(mktemp -ud %{_tmppath}/%{name}-%{version}-%{release}-XXXXXX)
 
 ### Patches ###
-Patch0001: 0001-SYSDB-Fixing-DB-update.patch
-Patch0002: 0002-sssctl-Fix-error-handling-after-memory-allocation-fa.patch
-Patch0003: 0003-sssctl-config-check-access-check-report.patch
-Patch0004: 0004-FO-Set-port-to-NOT_WORKING-when-trying-a-next-server.patch
-Patch0005: 0005-sssctl-Fix-format-string-for-size_t.patch
-Patch0006: 0006-doxygen-Fix-path-to-header-file-ipa_hbac.h.patch
-Patch0007: 0007-ipa_hbac-Fix-documentation-for-hbac_enable_debug.patch
-Patch0008: 0008-sssctl-Fix-warning-maybe-uninitialized.patch
-Patch0009: 0009-NOUPSTREAM-Bundle-http-parser.patch
-Patch0010: 0010-MAN-Update-description-of-sssctl.patch
-Patch0011: 0011-nss-srv-tests-Fix-prototype-of-wrapped-ncache-functi.patch
-Patch0012: 0012-TOOLS-Prevent-dereference-of-null-pointer.patch
-Patch0013: 0013-config-override_space-is-monitor-s-option.patch
-Patch0014: 0014-config-Fix-user_attributes.patch
-Patch0015: 0015-sysdb-tests-Fix-cast-from-pointer-to-integer.patch
-Patch0016: 0016-PROVIDERS-Setting-right-u-g-id-if-unprivileged.patch
-Patch0017: 0017-config-Allow-timeout-for-all-sevices.patch
-Patch0018: 0018-config-Add-config_file_version-to-schema.patch
-Patch0019: 0019-dyndns-Add-checks-for-NULL.patch
-Patch0020: 0020-sdap-Fix-ldap_rfc_2307_fallback_to_local_users.patch
-Patch0021: 0021-test_utils-Clean-files-after-sss_write_krb5_conf_sni.patch
-Patch0022: 0022-IPA-read-ipaNTAdditionalSuffixes-for-master-and-trus.patch
-Patch0023: 0023-sysdb-add-UPN-suffix-support-for-the-master-domain.patch
-Patch0024: 0024-sysdb-make-subdomain-calls-aware-of-upn_suffixes.patch
-Patch0025: 0025-DP-add-dp_get_module_data.patch
-Patch0026: 0026-IPA-add-ipa_init_get_krb5_auth_ctx.patch
-Patch0027: 0027-IPA-enable-enterprise-principals-if-server-supports-.patch
-Patch0028: 0028-views-allow-override-added-for-non-default-views-at-.patch
-Patch0029: 0029-sssctl-move-filter-creation-to-separate-function.patch
-Patch0030: 0030-sssctl-improve-readability-of-a-condition.patch
-Patch0031: 0031-sssctl-Use-localtime-for-time-stamps.patch
-Patch0032: 0032-SECRETS-Log-message-for-failures-with-removing-file.patch
-Patch0033: 0033-IPA-fix-capaths-output.patch
-Patch0034: 0034-UTIL-make-domain-mapping-content-testable.patch
-Patch0035: 0035-tests-add-tests-for-sss_get_domain_mappings_content.patch
-Patch0036: 0036-Amend-debug-messages-after-failure-of-unlink.patch
-Patch0037: 0037-SYSDB-Do-not-try-to-modify-ts-cache-for-unsupported-.patch
-Patch0038: 0038-AD-avoid-memory-leak-in-netlogon_get_domain_info-and.patch
-Patch0039: 0039-AD-netlogon_get_domain_info-allow-missing-arguments-.patch
-Patch0040: 0040-tests-add-tests-for-netlogon_get_domain_info.patch
-Patch0041: 0041-AD-replace-ad_get_client_site_parse_ndr-with-netlogo.patch
-Patch0042: 0042-sysdb_master_domain_add_info-properly-set-do_update.patch
-Patch0043: 0043-SYSDB-Removing-of-duplication-of-sysdb_ts_cache_attr.patch
-Patch0044: 0044-test_utils-Fixing-assignment-discards-const-qualifie.patch
-Patch0045: 0045-IPA-make-ipa_resolve_user_list_-send-recv-public-and.patch
-Patch0046: 0046-IPA-expand-ghost-members-of-AD-groups-in-server-mode.patch
-Patch0047: 0047-sysdb-add-sysdb_get_user_members_recursively.patch
-Patch0048: 0048-views-properly-override-group-member-names.patch
-Patch0049: 0049-IPA-fix-lookup-by-UPN-for-subdomains.patch
-Patch0050: 0050-LDAP-allow-multiple-user-principals.patch
-Patch0051: 0051-LDAP-new-attribute-option-ldap_user_email.patch
-Patch0052: 0052-sysdb-include-email-in-UPN-searches.patch
-Patch0053: 0053-LDAP-include-email-in-UPN-searches.patch
-Patch0054: 0054-NSS-add-user-email-to-fill_orig.patch
-Patch0055: 0055-utils-add-is_email_from_domain.patch
-Patch0056: 0056-LDAP-IPA-add-local-email-address-to-aliases.patch
-Patch0057: 0057-NSS-continue-with-UPN-email-search-if-name-was-not-f.patch
-Patch0058: 0058-PAM-continue-with-UPN-email-search-if-name-was-not-f.patch
-Patch0059: 0059-NSS-use-different-neg-cache-name-for-UPN-searches.patch
-Patch0060: 0060-PAM-Fix-domain-for-UPN-based-lookups.patch
-Patch0061: 0061-SDAP-add-special-handling-for-IPA-Kerberos-enterpris.patch
-Patch0062: 0062-SDAP-add-enterprise-principal-strings-for-user-searc.patch
-Patch0063: 0063-LDAP-Fix-storing-initgroups-for-users-with-no-supple.patch
-Patch0064: 0064-LDAP-Changing-of-confusing-debug-message.patch
-Patch0065: 0065-LDAP-Use-FQDN-when-linking-parent-LDAP-groups.patch
-Patch0066: 0066-sssctl-Consistent-commands-naming.patch
-Patch0067: 0067-SDAP-sanitize-member-name-before-using-in-filter.patch
-Patch0068: 0068-SDAP-sysdb_search_users-does-not-set-users_count-for.patch
-Patch0069: 0069-SYSDB-Sanitize-dn-in-sysdb_get_user_members_recursiv.patch
-Patch0070: 0070-SYSDB-Fix-setting-dataExpireTimestamp-if-sysdb-is-su.patch
-Patch0071: 0071-Revert-LDAP-Lookup-services-by-all-protocols-unless-.patch
-Patch0072: 0072-PROVIDER-Conversion-empty-string-from-D-Bus-to-NULL.patch
-Patch0073: 0073-LDAP-Fix-Dereference-after-NULL-check.patch
-Patch0074: 0074-LDAP-Fixing-wrong-pam-error-code-for-passwd.patch
-Patch0075: 0075-PAM-Do-not-act-on-ldb_message-in-case-of-a-failure.patch
-Patch0076: 0076-IPA-Check-the-return-value-of-sss_parse_internal_fqn.patch
-Patch0077: 0077-DP-Initialize-D-Bus-as-soon-as-possible.patch
-Patch0078: 0078-sssctl-Generic-help-for-cache-upgrade-and-config-che.patch
-Patch0079: 0079-NSS-Do-not-check-local-users-with-disabled-local_neg.patch
-Patch0080: 0080-config_schema-Add-ldap_user_email-to-schema.patch
-Patch0081: 0081-NSS-Use-correct-name-for-invalidating-memory-cache.patch
-Patch0082: 0082-SYSDB-Avoid-optimisation-with-modifyTimestamp-for-us.patch
-Patch0083: 0083-SIMPLE-Do-not-parse-names-on-startup.patch
-Patch0084: 0084-SIMPLE-Fail-on-any-error-parsing-the-access-control-.patch
-Patch0085: 0085-SIMPLE-Make-the-DP-handlers-testable.patch
-Patch0086: 0086-TESTS-Use-the-DP-handlers-in-simple-provider-tests-a.patch
-Patch0087: 0087-gpo-gPCMachineExtensionNames-with-just-whitespaces.patch
-Patch0088: 0088-sss_ini-Change-debug-level-of-config-error-msgs.patch
-Patch0089: 0089-CONFIG-full_name_format-is-an-allowed-option-for-all.patch
-Patch0090: 0090-CONFIG-re_expression-is-an-allowed-option-for-all-do.patch
-Patch0091: 0091-rdp-add-ability-to-forward-reply-to-the-client-reque.patch
-Patch0092: 0092-sbus-add-sbus_request_reply_error.patch
-Patch0093: 0093-sbus-add-utility-function-to-simplify-message-and-re.patch
-Patch0094: 0094-sssctl-use-talloc-with-sifp.patch
-Patch0095: 0095-failover-mark-subdomain-service-with-sd_-prefix.patch
-Patch0096: 0096-sssctl-print-active-server-and-server-list.patch
-Patch0097: 0097-sifp-fix-coverity-warning.patch
-Patch0098: 0098-sbus-allow-freeing-msg-through-dbus-api-when-using-t.patch
-Patch0099: 0099-PROXY-Do-not-abuse-data-provider-interface.patch
-Patch0100: 0100-DP-Remove-old-data-provider-interface.patch
-Patch0101: 0101-NSS-Remove-unused-functions.patch
-Patch0102: 0102-LDAP-Fixing-of-removing-netgroup-from-cache.patch
-Patch0103: 0103-AD_PROVIDER-Add-ad_enabled_domains-option.patch
-Patch0104: 0104-AD_PROVIDER-Initializing-of-ad_enabled_domains.patch
-Patch0105: 0105-AD_PROVIDER-ad_enabled_domains-only-master.patch
-Patch0106: 0106-AD_PROVIDER-ad_enabled_domains-other-then-master.patch
-Patch0107: 0107-TESTS-Adding-tests-for-ad_enabled_domains-option.patch
-Patch0109: 0109-UTIL-Use-sss_atomic_read_s-in-generate_csprng_buffer.patch
-Patch0110: 0110-SECRETS-Use-sss_atomic_read-write-for-better-readabi.patch
-Patch0111: 0111-BUILD-Ship-systemd-service-file-for-sssd-secrets.patch
-Patch0112: 0112-DP-Add-log-message-for-get-account-info.patch
-Patch0113: 0113-LDAP-Log-autofs-rfc2307-config-changes-only-with-ena.patch
-Patch0114: 0114-SSSCTL-More-helpful-error-message-when-InfoPipe-is-d.patch
-Patch0115: 0115-sdap-Skip-exact-duplicates-when-extending-maps.patch
-Patch0116: 0116-watchdog-cope-with-time-shift.patch
-Patch0117: 0117-PROXY-Use-the-fqname-when-converting-to-lowercase.patch
-Patch0118: 0118-BUILD-Allow-to-read-private-pipes-for-root.patch
-Patch0119: 0119-SYSDB-Rework-sysdb_cache_connect.patch
-Patch0120: 0120-SYSDB-Remove-the-timestamp-cache-for-a-newly-created.patch
-Patch0121: 0121-SECRETS-Return-ENOENT-when_deleting-a-non-existent-s.patch
-Patch0122: 0122-IPA-Parse-qualified-names-when-guessing-AD-user-prin.patch
-Patch0123: 0123-SYSDB-Fix-uninitialized-scalar-variable.patch
-Patch0124: 0124-PROXY-Use-right-name-in-ldap-filter.patch
-Patch0125: 0125-SYSDB-Fix-error-handling-in-sysdb_get_user_members_r.patch
-Patch0126: 0126-sdap_initgr_nested_get_membership_diff-use-fully-qua.patch
-Patch0127: 0127-Revert-CONFIG-Use-default-config-when-none-provided.patch
-Patch0128: 0128-TOOLS-Fix-a-typo-in-groupadd.patch
-Patch0129: 0129-TOOLS-sss_groupshow-did-not-work.patch
-Patch0130: 0130-TESTS-sss_groupadd-groupshow-regressions.patch
-Patch0131: 0131-TOOLS-use-internal-fqdn-for-DN.patch
-Patch0132: 0132-TESTS-Test-for-sss_user-groupmod-a.patch
-Patch0133: 0133-TOOLS-sss_mc_refresh_nested_group-short-fqname-usage.patch
-Patch0134: 0134-TESTS-Add-FQDN-variants-for-some-tests.patch
-Patch0135: 0135-KRB5-Send-the-output-username-not-internal-fqname-to.patch
-Patch0136: 0136-LDAP-Return-partial-results-from-adminlimit-exceeded.patch
-Patch0137: 0137-TOOLS-sss_groupshow-fails-to-show-MPG.patch
-Patch0138: 0138-TESTS-sss_groupshow-with-MPG.patch
-Patch0139: 0139-TOOLS-sss_override-without-name-override.patch
-Patch0140: 0140-TEST-Add-regression-test-for-ticket-3179.patch
-Patch0141: 0141-p11-only-set-PKCS11_LOGIN_TOKEN_NAME-if-gdm-smartcar.patch
-Patch0142: 0142-p11-return-a-fully-qualified-name.patch
-Patch0143: 0143-pam_sss-check-PKCS11_LOGIN_TOKEN_NAME.patch
-Patch0144: 0144-sysdb-add-parent_dom-to-sysdb_get_direct_parents.patch
-Patch0145: 0145-sdap-make-some-nested-group-related-calls-public.patch
-Patch0146: 0146-LDAP-AD-resolve-domain-local-groups-for-remote-users.patch
-Patch0147: 0147-IPA-Initialize-a-boolean-control-value.patch
-Patch0148: 0148-IPA-AD-check-auth-ctx-before-using-it.patch
-Patch0149: 0149-Qualify-ghost-user-attribute-in-case-ldap_group_nest.patch
-Patch0150: 0150-SYSDB-Split-sysdb_try_to_find_expected_dn-into-small.patch
-Patch0151: 0151-SYSDB-Augment-sysdb_try_to_find_expected_dn-to-match.patch
-Patch0152: 0152-SYSDB-Adding-lowercase-sudoUser-form.patch
-Patch0153: 0153-SYSDB-Fixing-of-sudorule-without-a-sudoUser.patch
-Patch0154: 0154-MONITOR-Do-not-set-up-watchdog-for-monitor.patch
-Patch0155: 0155-AUTOFS-Fix-offline-resolution-of-autofs-maps.patch
-Patch0156: 0156-Prevent-use-after-free-in-fd_input_available.patch
-Patch0157: 0157-SSH-Use-default_domain_suffix-for-users-authorized-k.patch
-Patch0158: 0158-SERVER-Set-the-process-group-during-server_setup.patch
-Patch0159: 0159-WATCHDOG-define-and-use-_MAX_TICKS-as-3.patch
-Patch0160: 0160-WATCHDOG-Avoid-non-async-signal-safe-from-the-signal.patch
-Patch0161: 0161-TESTS-Extending-sysdb-sudo-store-tests.patch
-Patch0162: 0162-SUDO-Only-store-lowercased-attribute-value-once.patch
-Patch0163: 0163-UTIL-Store-UPN-suffixes-when-creating-a-new-subdomai.patch
-Patch0164: 0164-Move-sized_output_name-and-sized_domain_name-into-re.patch
-Patch0165: 0165-IFP-Use-sized_domain_name-to-format-the-groups-the-u.patch
-Patch0166: 0166-LDAP-AD-Do-not-fail-in-case-rfc2307bis_nested_groups.patch
-Patch0167: 0167-HBAC-Do-not-rely-on-originalMemberOf-use-the-sysdb-m.patch
+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
 
 #This patch should not be removed in RHEL-7
 Patch999: 0999-NOUPSTREAM-Default-to-root-if-sssd-user-is-not-spec
@@ -283,6 +310,9 @@ BuildRequires: samba4-devel >= 4.0.0-59beta2
 BuildRequires: libsmbclient-devel
 BuildRequires: systemtap-sdt-devel
 BuildRequires: jansson-devel
+BuildRequires: http-parser-devel
+BuildRequires: curl-devel
+BuildRequires: libuuid-devel
 
 %description
 Provides a set of daemons to manage access to remote directories and
@@ -299,10 +329,9 @@ Summary: Common files for the SSSD
 Group: Applications/System
 License: GPLv3+
 # Conflicts
+Conflicts: selinux-policy < 3.10.0-46
 Conflicts: sssd < 1.10.0-8%{?dist}.beta2
-
 # Requires
-Requires: selinux-policy >= 3.13.1-102.el7_3.15
 Requires: sssd-client%{?_isa} = %{version}-%{release}
 Requires: libsss_idmap%{?_isa} = %{version}-%{release}
 Requires: libsss_sudo%{?_isa}  = %{version}-%{release}
@@ -310,8 +339,11 @@ Requires: libsss_autofs%{?_isa} = %{version}-%{release}
 Requires(post): systemd-units chkconfig
 Requires(preun): systemd-units chkconfig
 Requires(postun): systemd-units chkconfig
-Requires(pre): shadow-utils
-
+# sssd-common owns sssd.service file and is restarted in posttrans
+# libwbclient alternative might break restarting sssd
+# gpo_child -> libsmbclient -> samba-client-libs -> libwbclient
+OrderWithRequires: libwbclient
+OrderWithRequires: sssd-libwbclient
 
 ### Provides ###
 Provides: libsss_sudo-devel = %{version}-%{release}
@@ -421,7 +453,6 @@ License: GPLv3+
 Conflicts: sssd < 1.10.0-8.beta2
 Requires: cyrus-sasl-gssapi%{?_isa}
 Requires: sssd-common = %{version}-%{release}
-Requires(pre): shadow-utils
 
 %description krb5-common
 Provides helper processes that the LDAP and Kerberos back ends can use for
@@ -474,9 +505,6 @@ Requires: sssd-common = %{version}-%{release}
 Requires: sssd-krb5-common = %{version}-%{release}
 Requires: bind-utils
 Requires: sssd-common-pac = %{version}-%{release}
-# In order for libwbclient to be upgraded before sssd-ad and sets up the
-# alternatives symlink
-Requires: libwbclient >= 4.2.3-1
 
 %description ad
 Provides the Active Directory back end that the SSSD can utilize to fetch
@@ -578,7 +606,6 @@ be used by Python applications.
 Summary: The D-Bus responder of the SSSD
 Group: Applications/System
 License: GPLv3+
-BuildRequires: augeas-devel
 Requires: sssd-common = %{version}-%{release}
 
 %description dbus
@@ -638,7 +665,7 @@ Conflicts: libwbclient-devel < 4.1.12
 Development libraries for the SSSD libwbclient implementation.
 
 %package winbind-idmap
-Summary: SSSSD's idmap_sss Backend for Winbind
+Summary: SSSD's idmap_sss Backend for Winbind
 Group:  Applications/System
 License: GPLv3+ and LGPLv3+
 
@@ -646,6 +673,37 @@ License: GPLv3+ and LGPLv3+
 The idmap_sss module provides a way for Winbind to call SSSD to map UIDs/GIDs
 and SIDs.
 
+%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
+
+%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
 # Update timestamps on the files touched by a patch, to avoid non-equal
 # .pyc/.pyo files across the multilib peers within a build, where "Level"
@@ -694,7 +752,8 @@ autoreconf -ivf
     --without-python3-bindings \
     --with-ad-gpo-default=permissive \
     %{?enable_polkit_rules_option} \
-    %{?enable_systemtap_opt}
+    %{?enable_systemtap_opt} \
+    %{?with_kcm_option}
 
 make %{?_smp_mflags} all docs
 
@@ -707,7 +766,7 @@ unset CK_TIMEOUT_MULTIPLIER
 
 make install DESTDIR=$RPM_BUILD_ROOT
 
-if [ ! -f %{buildroot}/%{_libdir}/%{name}/modules/libwbclient.so.%{libwbc_lib_version}]
+if [ ! -f $RPM_BUILD_ROOT/%{_libdir}/%{name}/modules/libwbclient.so.%{libwbc_lib_version} ]
 then
     echo "Expected libwbclient version not found, please check if version has changed."
     exit -1
@@ -730,11 +789,6 @@ install -m644 src/examples/rwtab $RPM_BUILD_ROOT%{_sysconfdir}/rwtab.d/sssd
 mkdir -p $RPM_BUILD_ROOT/%{_sysconfdir}/cifs-utils
 %endif
 
-# Replace sysv init script with systemd unit file
-rm -f $RPM_BUILD_ROOT/%{_initrddir}/%{name}
-mkdir -p $RPM_BUILD_ROOT/%{_unitdir}/
-cp src/sysv/systemd/sssd.service $RPM_BUILD_ROOT/%{_unitdir}/
-
 # Remove .la files created by libtool
 find $RPM_BUILD_ROOT -name "*.la" -exec rm -f {} \;
 
@@ -748,11 +802,12 @@ do
     echo %{python_sitelib}/`basename $file` >> python_sssdconfig.lang
 done
 
-touch sssd_tools.lang
-touch sssd_client.lang
-for provider in ldap krb5 ipa ad proxy
+touch sssd.lang
+for subpackage in sssd_ldap sssd_krb5 sssd_ipa sssd_ad sssd_proxy sssd_tools \
+                  sssd_client sssd_dbus sssd_winbind_idmap \
+                  libsss_certmap sssd_kcm
 do
-    touch sssd_$provider.lang
+    touch $subpackage.lang
 done
 
 for man in `find $RPM_BUILD_ROOT/%{_mandir}/??/man?/ -type f | sed -e "s#$RPM_BUILD_ROOT/%{_mandir}/##"`
@@ -762,9 +817,15 @@ do
         sss_cache*)
             echo \%lang\(${lang}\) \%{_mandir}/${man}\* >> sssd.lang
             ;;
+        sss_ssh*)
+            echo \%lang\(${lang}\) \%{_mandir}/${man}\* >> sssd.lang
+            ;;
         sss_*)
             echo \%lang\(${lang}\) \%{_mandir}/${man}\* >> sssd_tools.lang
             ;;
+        sssctl*)
+            echo \%lang\(${lang}\) \%{_mandir}/${man}\* >> sssd_tools.lang
+            ;;
         sssd_krb5_*)
             echo \%lang\(${lang}\) \%{_mandir}/${man}\* >> sssd_client.lang
             ;;
@@ -786,6 +847,18 @@ do
         sssd-proxy*)
             echo \%lang\(${lang}\) \%{_mandir}/${man}\* >> sssd_proxy.lang
             ;;
+        sssd-ifp*)
+            echo \%lang\(${lang}\) \%{_mandir}/${man}\* >> sssd_dbus.lang
+            ;;
+        sssd-kcm*)
+            echo \%lang\(${lang}\) \%{_mandir}/${man}\* >> sssd_kcm.lang
+            ;;
+        idmap_sss*)
+            echo \%lang\(${lang}\) \%{_mandir}/${man}\* >> sssd_winbind_idmap.lang
+            ;;
+        sss-certmap*)
+            echo \%lang\(${lang}\) \%{_mandir}/${man}\* >> libsss_certmap.lang
+            ;;
         *)
             echo \%lang\(${lang}\) \%{_mandir}/${man}\* >> sssd.lang
             ;;
@@ -796,28 +869,40 @@ done
 echo "sssd.lang:"
 cat sssd.lang
 
-echo "sssd_client.lang:"
-cat sssd_client.lang
-
-echo "sssd_tools.lang:"
-cat sssd_tools.lang
+echo "python_sssdconfig.lang:"
+cat python_sssdconfig.lang
 
-for provider in ldap krb5 ipa ad proxy
+for subpackage in sssd_ldap sssd_krb5 sssd_ipa sssd_ad sssd_proxy sssd_tools \
+                  sssd_client sssd_dbus sssd_winbind_idmap \
+                  libsss_certmap sssd_kcm
 do
-    echo "sssd_$provider.lang:"
-    cat sssd_$provider.lang
+    echo "$subpackage.lang:"
+    cat $subpackage.lang
 done
 
 %files
 %defattr(-,root,root,-)
-%doc COPYING
+%license COPYING
 
 %files common -f sssd.lang
 %defattr(-,root,root,-)
-%doc COPYING
+%license COPYING
 %doc src/examples/sssd-example.conf
 %{_sbindir}/sssd
 %{_unitdir}/sssd.service
+%{_unitdir}/sssd-autofs.socket
+%{_unitdir}/sssd-autofs.service
+%{_unitdir}/sssd-nss.socket
+%{_unitdir}/sssd-nss.service
+%{_unitdir}/sssd-pac.socket
+%{_unitdir}/sssd-pac.service
+%{_unitdir}/sssd-pam.socket
+%{_unitdir}/sssd-pam-priv.socket
+%{_unitdir}/sssd-pam.service
+%{_unitdir}/sssd-ssh.socket
+%{_unitdir}/sssd-ssh.service
+%{_unitdir}/sssd-sudo.socket
+%{_unitdir}/sssd-sudo.service
 %{_unitdir}/sssd-secrets.socket
 %{_unitdir}/sssd-secrets.service
 
@@ -829,9 +914,12 @@ done
 %{_libexecdir}/%{servicename}/sssd_secrets
 %{_libexecdir}/%{servicename}/sssd_ssh
 %{_libexecdir}/%{servicename}/sssd_sudo
-%attr(4750,root,sssd) %{_libexecdir}/%{servicename}/p11_child
+%{_libexecdir}/%{servicename}/p11_child
+%{_libexecdir}/%{servicename}/sssd_check_socket_activated_responders
 
 %dir %{_libdir}/%{name}
+# The files provider is intentionally packaged in -common
+%{_libdir}/%{name}/libsss_files.so
 %{_libdir}/%{name}/libsss_simple.so
 
 #Internal shared libraries
@@ -876,6 +964,8 @@ done
 %dir %{_sysconfdir}/rwtab.d
 %config(noreplace) %{_sysconfdir}/rwtab.d/sssd
 %dir %{_datadir}/sssd
+%{_sysconfdir}/pam.d/sssd-shadowutils
+%{_libdir}/%{name}/conf/sssd.conf
 
 %{_datadir}/sssd/cfg_rules.ini
 %{_datadir}/sssd/sssd.api.conf
@@ -883,8 +973,10 @@ done
 %{_mandir}/man1/sss_ssh_authorizedkeys.1*
 %{_mandir}/man1/sss_ssh_knownhostsproxy.1*
 %{_mandir}/man5/sssd.conf.5*
+%{_mandir}/man5/sssd-files.5*
 %{_mandir}/man5/sssd-simple.5*
 %{_mandir}/man5/sssd-sudo.5*
+%{_mandir}/man5/sssd-secrets.5*
 %{_mandir}/man5/sss_rpcidmapd.5*
 %{_mandir}/man8/sssd.8*
 %{_mandir}/man8/sss_cache.8*
@@ -905,31 +997,31 @@ done
 
 %files ldap -f sssd_ldap.lang
 %defattr(-,root,root,-)
-%doc COPYING
+%license COPYING
 %{_libdir}/%{name}/libsss_ldap.so
 %{_mandir}/man5/sssd-ldap.5*
 
 %files krb5-common
 %defattr(-,root,root,-)
-%doc COPYING
+%license COPYING
 %attr(755,sssd,sssd) %dir %{pubconfpath}/krb5.include.d
 %attr(4750,root,sssd) %{_libexecdir}/%{servicename}/ldap_child
 %attr(4750,root,sssd) %{_libexecdir}/%{servicename}/krb5_child
 
 %files krb5 -f sssd_krb5.lang
 %defattr(-,root,root,-)
-%doc COPYING
+%license COPYING
 %{_libdir}/%{name}/libsss_krb5.so
 %{_mandir}/man5/sssd-krb5.5*
 
 %files common-pac
 %defattr(-,root,root,-)
-%doc COPYING
+%license COPYING
 %{_libexecdir}/%{servicename}/sssd_pac
 
 %files ipa -f sssd_ipa.lang
 %defattr(-,root,root,-)
-%doc COPYING
+%license COPYING
 %attr(700,sssd,sssd) %dir %{keytabdir}
 %{_libdir}/%{name}/libsss_ipa.so
 %attr(4750,root,sssd) %{_libexecdir}/%{servicename}/selinux_child
@@ -937,26 +1029,26 @@ done
 
 %files ad -f sssd_ad.lang
 %defattr(-,root,root,-)
-%doc COPYING
+%license COPYING
 %{_libdir}/%{name}/libsss_ad.so
 %{_libexecdir}/%{servicename}/gpo_child
 %{_mandir}/man5/sssd-ad.5*
 
 %files proxy
 %defattr(-,root,root,-)
-%doc COPYING
+%license COPYING
 %attr(4750,root,sssd) %{_libexecdir}/%{servicename}/proxy_child
 %{_libdir}/%{name}/libsss_proxy.so
 
-%files dbus
+%files dbus -f sssd_dbus.lang
 %defattr(-,root,root,-)
-%doc COPYING
+%license COPYING
 %{_libexecdir}/%{servicename}/sssd_ifp
 %{_mandir}/man5/sssd-ifp.5*
+%{_unitdir}/sssd-ifp.service
 # InfoPipe DBus plumbing
 %{_sysconfdir}/dbus-1/system.d/org.freedesktop.sssd.infopipe.conf
 %{_datadir}/dbus-1/system-services/org.freedesktop.sssd.infopipe.service
-%{_libdir}/%{name}/libsss_config.so
 
 %files -n libsss_simpleifp
 %defattr(-,root,root,-)
@@ -972,7 +1064,7 @@ done
 
 %files client -f sssd_client.lang
 %defattr(-,root,root,-)
-%doc src/sss_client/COPYING src/sss_client/COPYING.LESSER
+%license src/sss_client/COPYING src/sss_client/COPYING.LESSER
 %{_libdir}/libnss_sss.so.2
 %{_libdir}/security/pam_sss.so
 %{_libdir}/krb5/plugins/libkrb5/sssd_krb5_locator_plugin.so
@@ -993,18 +1085,18 @@ done
 
 %files -n libsss_sudo
 %defattr(-,root,root,-)
-%doc src/sss_client/COPYING
+%license src/sss_client/COPYING
 %{_libdir}/libsss_sudo.so*
 
 %files -n libsss_autofs
 %defattr(-,root,root,-)
-%doc src/sss_client/COPYING src/sss_client/COPYING.LESSER
+%license src/sss_client/COPYING src/sss_client/COPYING.LESSER
 %dir %{_libdir}/%{name}/modules
 %{_libdir}/%{name}/modules/libsss_autofs.so
 
 %files tools -f sssd_tools.lang
 %defattr(-,root,root,-)
-%doc COPYING
+%license COPYING
 %{_sbindir}/sss_useradd
 %{_sbindir}/sss_userdel
 %{_sbindir}/sss_usermod
@@ -1045,7 +1137,7 @@ done
 
 %files -n libsss_idmap
 %defattr(-,root,root,-)
-%doc src/sss_client/COPYING src/sss_client/COPYING.LESSER
+%license src/sss_client/COPYING src/sss_client/COPYING.LESSER
 %{_libdir}/libsss_idmap.so.*
 
 %files -n libsss_idmap-devel
@@ -1057,7 +1149,7 @@ done
 
 %files -n libipa_hbac
 %defattr(-,root,root,-)
-%doc src/sss_client/COPYING src/sss_client/COPYING.LESSER
+%license src/sss_client/COPYING src/sss_client/COPYING.LESSER
 %{_libdir}/libipa_hbac.so.*
 
 %files -n libipa_hbac-devel
@@ -1069,7 +1161,7 @@ done
 
 %files -n libsss_nss_idmap
 %defattr(-,root,root,-)
-%doc src/sss_client/COPYING src/sss_client/COPYING.LESSER
+%license src/sss_client/COPYING src/sss_client/COPYING.LESSER
 %{_libdir}/libsss_nss_idmap.so.*
 
 %files -n libsss_nss_idmap-devel
@@ -1099,11 +1191,24 @@ done
 %{_libdir}/%{name}/modules/libwbclient.so
 %{_libdir}/pkgconfig/wbclient_sssd.pc
 
-%files winbind-idmap
+%files winbind-idmap -f sssd_winbind_idmap.lang
 %dir %{_libdir}/samba/idmap
 %{_libdir}/samba/idmap/sss.so
 %{_mandir}/man8/idmap_sss.8*
 
+%files -n libsss_certmap -f libsss_certmap.lang
+%defattr(-,root,root,-)
+%license src/sss_client/COPYING src/sss_client/COPYING.LESSER
+%{_libdir}/libsss_certmap.so.*
+%{_mandir}/man5/sss-certmap.5*
+
+%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 ipa
 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
@@ -1112,23 +1217,80 @@ getent passwd sssd >/dev/null || useradd -r -g sssd -d / -s /sbin/nologin -c "Us
 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
 
+%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
+%{_unitdir}/sssd-kcm.socket
+%{_unitdir}/sssd-kcm.service
+%{_mandir}/man8/sssd-kcm.8*
+%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
-/bin/systemctl status sssd.service >/dev/null 2>&1 && touch /var/tmp/sssd.upgrade || :
+/bin/systemctl is-active --quiet sssd.service && touch /var/tmp/sssd_is_running || :
 
 %post common
 %systemd_post sssd.service
+%systemd_post sssd-autofs.socket
+%systemd_post sssd-nss.socket
+%systemd_post sssd-pac.socket
+%systemd_post sssd-pam.socket
+%systemd_post sssd-pam-priv.socket
 %systemd_post sssd-secrets.socket
+%systemd_post sssd-ssh.socket
+%systemd_post sssd-sudo.socket
 
 %preun common
 %systemd_preun sssd.service
+%systemd_preun sssd-autofs.socket
+%systemd_preun sssd-nss.socket
+%systemd_preun sssd-pac.socket
+%systemd_preun sssd-pam.socket
+%systemd_preun sssd-pam-priv.socket
 %systemd_preun sssd-secrets.socket
+%systemd_preun sssd-ssh.socket
+%systemd_preun sssd-sudo.socket
 
 %postun common
-%systemd_postun_with_restart sssd.service
+%systemd_postun_with_restart sssd-autofs.socket
+%systemd_postun_with_restart sssd-autofs.service
+%systemd_postun_with_restart sssd-nss.socket
+%systemd_postun_with_restart sssd-nss.service
+%systemd_postun_with_restart sssd-pac.socket
+%systemd_postun_with_restart sssd-pac.service
+%systemd_postun_with_restart sssd-pam.socket
+%systemd_postun_with_restart sssd-pam-priv.socket
+%systemd_postun_with_restart sssd-pam.service
 %systemd_postun_with_restart sssd-secrets.socket
 %systemd_postun_with_restart sssd-secrets.service
+%systemd_postun_with_restart sssd-ssh.socket
+%systemd_postun_with_restart sssd-ssh.service
+%systemd_postun_with_restart sssd-sudo.socket
+%systemd_postun_with_restart sssd-sudo.service
+
+%post dbus
+%systemd_post sssd-ifp.service
+
+%preun dbus
+%systemd_preun sssd-ifp.service
+
+%postun dbus
+%systemd_postun_with_restart sssd-ifp.service
+
+%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
 
 %if (0%{?with_cifs_utils_plugin} == 1)
 %post client
@@ -1165,6 +1327,10 @@ fi
 
 %postun -n libsss_simpleifp -p /sbin/ldconfig
 
+%post -n libsss_certmap -p /sbin/ldconfig
+
+%postun -n libsss_certmap -p /sbin/ldconfig
+
 %post libwbclient
 %{_sbindir}/update-alternatives \
     --install %{_libdir}/libwbclient.so.%{libwbc_alternatives_version} \
@@ -1193,85 +1359,296 @@ if [ $1 -eq 0 ]; then
 fi
 
 %posttrans common
-/bin/systemctl daemon-reload >/dev/null 2>&1 || :
-if [ -f /var/tmp/sssd.upgrade ]; then
-    /bin/systemctl restart sssd.service >/dev/null 2>&1 || :
-else
-    /bin/systemctl try-restart sssd.service >/dev/null 2>&1 || :
-fi
-/usr/bin/rm -f /var/tmp/sssd.upgrade || :
+%systemd_postun_with_restart sssd.service
+# After changing order of sssd-common and *libwbclient,
+# older version of sssd will restart sssd.service in postun scriptlet
+# It failed due to missing alternative to libwbclient. Start it again.
+/bin/systemctl is-active --quiet sssd.service || {
+    if [ -f /var/tmp/sssd_is_running ]; then
+        systemctl start sssd.service >/dev/null 2>&1;
+        rm -f /var/tmp/sssd_is_running;
+    fi
+}
 
 %changelog
-* Fri May 26 2017 Jakub Hrozek <jhrozek@redhat.com> - 1.14.0-43.18
-- Resolves: rhbz#1456013 - sssd intermittently failing to resolve groups
+* Wed Jun 21 2017 Jakub Hrozek <jhrozek@redhat.com> - 1.15.2-50
+- Resolves: rhbz#1457926 - Wrong search base used when SSSD is directly
+                           connected to AD child domain
+
+* Wed Jun 21 2017 Jakub Hrozek <jhrozek@redhat.com> - 1.15.2-49
+- Resolves: rhbz#1450107 - SSSD doesn't handle conflicts between users
+                           from trusted domains with the same name when
+                           shortname user resolution is enabled
+
+* Fri Jun 16 2017 Jakub Hrozek <jhrozek@redhat.com> - 1.15.2-48
+- Resolves: rhbz#1459846 - krb5: properly handle 'password expired'
+                           information retured by the KDC during
+                           PKINIT/Smartcard authentication
+
+* Thu Jun 15 2017 Jakub Hrozek <jhrozek@redhat.com> - 1.15.2-47
+- Resolves: rhbz#1430415 - ldap_purge_cache_timeout in RHEL7.3 invalidate
+                           most of the entries once the cleanup task kicks in
+
+* Thu Jun 15 2017 Jakub Hrozek <jhrozek@redhat.com> - 1.15.2-46
+- Resolves: rhbz#1455254 - Make domain available as user attribute
+
+* Thu Jun  8 2017 Jakub Hrozek <jhrozek@redhat.com> - 1.15.2-45
+- Resolves: rhbz#1449731 - IPA client cannot change AD Trusted User password
+
+* Thu Jun  8 2017 Jakub Hrozek <jhrozek@redhat.com> - 1.15.2-44
+- Resolves: rhbz#1457927 - getent failed to fetch netgroup information
+                           after changing default_domain_suffix to
+                           ADdomin in /etc/sssd/sssd.conf
+
+* Mon Jun  5 2017 Jakub Hrozek <jhrozek@redhat.com> - 1.15.2-43
+- Resolves: rhbz#1440132 - fiter_users and filter_groups stop working
+                           properly in v 1.15
+
+* Mon Jun  5 2017 Jakub Hrozek <jhrozek@redhat.com> - 1.15.2-42
+- Resolves: rhbz#1449728 - LDAP to IPA migration doesn't work in master
+
+* Mon Jun  5 2017 Jakub Hrozek <jhrozek@redhat.com> - 1.15.2-41
+- Resolves: rhbz#1445445 - Smart card login fails if same cert mapped to
+                           IdM user and AD user
+
+* Mon Jun  5 2017 Jakub Hrozek <jhrozek@redhat.com> - 1.15.2-40
+- Resolves: rhbz#1449729 - org.freedesktop.sssd.infopipe.GetUserGroups
+                           does not resolve groups into names with AD
+
+* Thu Jun  1 2017 Jakub Hrozek <jhrozek@redhat.com> - 1.15.2-39
+- Resolves: rhbz#1450094 - Properly support IPA's promptusername config
+                           option
+
+* Thu Jun  1 2017 Jakub Hrozek <jhrozek@redhat.com> - 1.15.2-38
+- Resolves: rhbz#1457644 - Segfault in access_provider = krb5 is set in
+                           sssd.conf due to an off-by-one error when
+                           constructing the child send buffer
+- Resolves: rhbz#1456531 - Option name typos are not detected with validator
+                           function of sssctl config-check command in domain
+                           sections
+
+* Fri May 26 2017 Jakub Hrozek <jhrozek@redhat.com> - 1.15.2-37
+- Resolves: rhbz#1428906 - sssd intermittently failing to resolve groups
                            for an AD user in IPA-AD trust environment.
 
-* Fri May 12 2017 Jakub Hrozek <jhrozek@redhat.com> - 1.14.0-43.17
-- Resolves: rhbz#1450125 - Wrong pam return code for user from subdomain
+* Fri May 26 2017 Jakub Hrozek <jhrozek@redhat.com> - 1.15.2-36
+- Resolves: rhbz#1389796 - Smartcard authentication with UPN as logon name
+                           might fail
+- Fix Coverity issues in patches for rhbz#1445445
+
+* Wed May 24 2017 Jakub Hrozek <jhrozek@redhat.com> - 1.15.2-35
+- Resolves: rhbz#1445445 - Smart card login fails if same cert mapped to
+                           IdM user and AD user
+
+* Wed May 24 2017 Jakub Hrozek <jhrozek@redhat.com> - 1.15.2-34
+- Resolves: rhbz#1446302 - crash in sssd-kcm due to a race-condition
+                           between two concurrent requests
+
+* Tue May 23 2017 Jakub Hrozek <jhrozek@redhat.com> - 1.15.2-33
+- Resolves: rhbz#1389796 - Smartcard authentication with UPN as logon name might fail
+
+* Tue May 23 2017 Jakub Hrozek <jhrozek@redhat.com> - 1.15.2-32
+- Resolves: rhbz#1306707 - Need better debug message when krb5_child
+                           returns an unhandled error, leading to a
+                           System Error PAM code
+
+* Mon May 22 2017 Jakub Hrozek <jhrozek@redhat.com> - 1.15.2-31
+- Resolves: rhbz#1446535 - Group resolution does not work in subdomain
+                           without ad_server option
+
+* Wed May  17 2017 Sumit Bose <sbose@redhat.com> - 1.15.2-30
+- Resolves: rhbz#1449726 - sss_nss_getlistbycert() does not return results from
+                           multiple domains
+- Resolves: rhbz#1447098 - sssd unable to search dbus for ipa user by
+                           certificate
+- Additional patch for rhbz#1440132
+
+* Thu May  11 2017 Jakub Hrozek <jhrozek@redhat.com> - 1.15.2-29
+- Reapply patch by Lukas Slebodnik to fix upgrade issues with libwbclient
+- Resolves: rhbz#1439457 - SSSD does not start after upgrade from 7.3 to 7.4 
+- Resolves: rhbz#1449107 - error: %pre(sssd-common-1.15.2-26.el7.x86_64)
+                           scriptlet failed, exit status 3
+
+* Thu May 11 2017 Jakub Hrozek <jhrozek@redhat.com> - 1.15.2-28
+- Resolves: rhbz#1440132 - fiter_users and filter_groups stop working
+                           properly in v 1.15
+- Also apply an additional patch for rhbz#1441545
+
+* Thu May  4 2017 Jakub Hrozek <jhrozek@redhat.com> - 1.15.2-25
+- Resolves: rhbz#1445445 - Smart card login fails if same cert mapped to
+                           IdM user and AD user
+
+* Wed May  3 2017 Jakub Hrozek <jhrozek@redhat.com> - 1.15.2-24
+- Resolves: rhbz#1434992 - Wrong pam return code for user from subdomain
                            with ad_access_filter
 
-* Tue May  2 2017 Jakub Hrozek <jhrozek@redhat.com> - 1.14.0-43.16
-- Resolves: rhbz#1446085 - D-Bus interface of sssd is giving inappropriate
+* Wed May  3 2017 Lukas Slebodnik <lslebodn@redhat.com> - 1.15.2-23
+- Resolves: rhbz#1430494 - expect sss_ssh_authorizedkeys and
+                           sss_ssh_knownhostsproxy manuals to be packaged
+                           into sssd-common package
+
+* Tue May  2 2017 Jakub Hrozek <jhrozek@redhat.com> - 1.15.2-22
+- Resolves: rhbz#1427749 - SSSD in server mode iterates over all domains
+                           for group-by-GID requests, causing unnecessary
+                           searches
+
+* Tue May  2 2017 Jakub Hrozek <jhrozek@redhat.com> - 1.15.2-21
+- Resolves: rhbz#1446139 - Infopipe method ListByCertificate does not
+                           return the users with overrides
+
+* Tue May  2 2017 Jakub Hrozek <jhrozek@redhat.com> - 1.15.2-20
+- Resolves: rhbz#1441545 - With multiple subdomain sections id command
+                           output for user is not displayed for both domains
+
+* Tue May  2 2017 Jakub Hrozek <jhrozek@redhat.com> - 1.15.2-19
+- Resolves: rhbz#1428866 - Using ad_enabled_domains configuration option
+                           in sssd.conf causes nameservice lookups to fail.
+
+* Tue May  2 2017 Jakub Hrozek <jhrozek@redhat.com> - 1.15.2-18
+- Remove an unused variable from the sssd-secrets responder
+- Related: rhbz#1398701 - [sssd-secrets] https proxy talks plain http
+- Improve two DEBUG messages in the client trust code to aid troubleshooting
+- Fix standalone application domains
+- Related: rhbz#1425891 - Support delivering non-POSIX users and groups
+                          through the IFP and PAM interfaces
+
+* Wed Apr 26 2017 Jakub Hrozek <jhrozek@redhat.com> - 1.15.2-17
+- Allow completely server-side unqualified name resolution if the domain order is set,
+  do not require any client-side changes
+- Related: rhbz#1330196 - [RFE] Short name input format with SSSD for users from
+                          all domains when domain autodiscovery is used or when
+                          IPA client resolves trusted AD domain users
+
+* Mon Apr 24 2017 Jakub Hrozek <jhrozek@redhat.com> - 1.15.2-16
+- Resolves: rhbz#1402532 - D-Bus interface of sssd is giving inappropriate
                            group information for trusted AD users
 
-* Wed Apr 26 2017 Jakub Hrozek <jhrozek@redhat.com> - 1.14.0-43.15
-- Resolves: rhbz#1445821 - sssd does not evaluate AD UPN suffixes which
-                           results in failed user logins
-
-* Wed Feb 15 2017 Jakub Hrozek <jhrozek@redhat.com> - 1.14.0-43.14
-- Resolves: rhbz#1422183 - Fails to accept any sudo rules if there are
+* Thu Apr 13 2017 Jakub Hrozek <jhrozek@redhat.com> - 1.15.2-15
+- Resolves: rhbz#1431858 - Wrong principal found with ad provider and long
+                           host name
+
+* Wed Apr 12 2017 Jakub Hrozek <jhrozek@redhat.com> - 1.15.2-14
+- Resolves: rhbz#1415167 - pam_acct_mgmt with pam_sss.so fails in
+                           unprivileged container unless
+                           selinux_provider = none is used
+
+* Wed Apr 12 2017 Jakub Hrozek <jhrozek@redhat.com> - 1.15.2-13
+- Resolves: rhbz#1438388 - [abrt] [faf] sssd: unknown function():
+                           /usr/libexec/sssd/sssd_pam killed by 6
+
+* Tue Apr 11 2017 Jakub Hrozek <jhrozek@redhat.com> - 1.15.2-12
+- Resolves: rhbz#1432112 - sssctl config-check does not give any error
+                           when default configuration file is not present
+
+* Tue Apr 11 2017 Jakub Hrozek <jhrozek@redhat.com> - 1.15.2-11
+- Resolves: rhbz#1438374 - [abrt] [faf] sssd: vfprintf():
+                           /usr/libexec/sssd/sssd_be killed by 11
+
+* Tue Apr 11 2017 Jakub Hrozek <jhrozek@redhat.com> - 1.15.2-10
+- Resolves: rhbz#1427195 - sssd_nss consumes more memory until restarted
+                           or machine swaps
+
+* Mon Apr 10 2017 Jakub Hrozek <jhrozek@redhat.com> - 1.15.2-9
+- Resolves: rhbz#1414023 - Create troubleshooting tool to determine if a
+                           failure is in SSSD or not when using layered
+                           products like RH-SSO/CFME etc
+
+* Thu Mar 30 2017 Jakub Hrozek <jhrozek@redhat.com> - 1.15.2-8
+- Resolves: rhbz#1398701 - [sssd-secrets] https proxy talks plain http
+
+* Thu Mar 30 2017 Jakub Hrozek <jhrozek@redhat.com> - 1.15.2-7
+- Fix off-by-one error in the KCM responder
+- Related: rhbz#1396012 - [RFE] KCM ccache daemon in SSSD
+
+* Thu Mar 30 2017 Jakub Hrozek <jhrozek@redhat.com> - 1.15.2-6
+- Resolves: rhbz#1425891 - Support delivering non-POSIX users and groups
+                           through the IFP and PAM interfaces
+
+* Wed Mar 29 2017 Jakub Hrozek <jhrozek@redhat.com> - 1.15.2-5
+- Resolves: rhbz#1434991 - Issue processing ssh keys from certificates in
+                           ssh respoder
+
+* Wed Mar 29 2017 Jakub Hrozek <jhrozek@redhat.com> - 1.15.2-4
+- Resolves: rhbz#1330196 - [RFE] Short name input format with SSSD for
+                           users from all domains when domain autodiscovery
+                           is used or when IPA client resolves trusted AD
+                           domain users
+- Also backport some buildtime fixes for the KCM responder
+- Related: rhbz#1396012 - [RFE] KCM ccache daemon in SSSD
+
+* Mon Mar 27 2017 Jakub Hrozek <jhrozek@redhat.com> - 1.15.2-3
+- Resolves: rhbz#1396012 - [RFE] KCM ccache daemon in SSSD
+
+* Thu Mar 23 2017 Jakub Hrozek <jhrozek@redhat.com> - 1.15.2-2
+- Resolves: rhbz#1340711 - [RFE] Use one smartcard and certificate for
+                           authentication to distinct logon accounts
+
+* Wed Mar 15 2017 Jakub Hrozek <jhrozek@redhat.com> - 1.15.2-1
+- Update to upstream 1.15.2
+- https://docs.pagure.org/SSSD.sssd/users/relnotes/notes_1_15_2.html
+- Resolves: rhbz#1418728 - IPA - sudo does not handle associated conflict
+                           entries
+- Resolves: rhbz#1386748 - sssd doesn't update PTR records if A/PTR zones
+                           are configured as non-secure and secure
+- Resolves: rhbz#1214491 - [RFE] Make it possible to configure AD subdomain
+                           in the SSSD server mode
+
+* Thu Mar  9 2017 Fabiano Fidêncio <fidencio@redhat.com> - 1.15.1-2
+- Drop "NOUPSTREAM: Bundle http-parser" patch
+  Related: rhbz#1393819 - New package: http-parser
+
+* Sat Mar  4 2017 Jakub Hrozek <jhrozek@redhat.com> - 1.15.1-1
+- Update to upstream 1.15.1
+- https://docs.pagure.org/SSSD.sssd/users/relnotes/notes_1_15_1.html
+- Resolves: rhbz#1327085 - Don't prompt for password if there is already
+                           one on the stack
+- Resolves: rhbz#1378722 - [RFE] Make GETSIDBYNAME and GETORIGBYNAME
+                           request aware of UPNs and aliases
+- Resolves: rhbz#1405075 - [RFE] Add PKINIT support to SSSD Kerberos provider
+- Resolves: rhbz#1416526 - Need correction in sssd-krb5 man page
+- Resolves: rhbz#1418752 - pam_sss crashes in do_pam_conversation if no
+                           conversation function is provided by the
+                           client app
+- Resolves: rhbz#1419356 - Fails to accept any sudo rules if there are
                            two user entries in an ldap role with the same
-                           sudo user.
-
-* Fri Feb  3 2017 Jakub Hrozek <jhrozek@redhat.com> - 1.14.0-43.13
-- Resolves: rhbz#1418943 - If a long-running task (e.g. enumeration) blocks the
-                           sssd_be process, sssd_be can deadlock
-- Also Require a new-enough version of selinux-policy so that setpgid() by sssd
-  is allowed
-
-* Wed Jan 11 2017 Jakub Hrozek <jhrozek@redhat.com> - 1.14.0-43.12
-- Resolves: rhbz#1405584 - SSH: default_domain_suffix is not being used
-                           for users' authorized keys
-
-* Tue Dec 13 2016 Jakub Hrozek <jhrozek@redhat.com> - 1.14.0-43.11
-- Resolves: rhbz#1404340 - Use-after free in resolver in case the fd is
+                           sudo user
+- Resolves: rhbz#1421622 - SSSD - Users/Groups are cached as mixed-case
+                           resulting in users unable to sign in
+
+* Wed Feb  1 2017 Jakub Hrozek <jhrozek@redhat.com> - 1.15.0-2
+- Fix several packaging issues, notably the p11_child is no longer setuid
+  and the libwbclient used a wrong version number in the symlink
+
+* Mon Jan 30 2017 Jakub Hrozek <jhrozek@redhat.com> - 1.15.0-1
+- Update to upstream 1.15.0
+- Resolves: rhbz#1393824 - Rebase SSSD to version 1.15
+- Resolves: rhbz#1407960 - wbcLookupSid() fails in pdomain is NULL
+- Resolves: rhbz#1406437 - sssctl netgroup-show Cannot allocate memory
+- Resolves: rhbz#1400422 - Use-after free in resolver in case the fd is
                            writeable and readable at the same time
-
-* Fri Nov 25 2016 Jakub Hrozek <jhrozek@redhat.com> - 1.14.0-43.10
-- Resolves: rhbz#1398673 - autofs map resolution doesn't work offline
-
-* Thu Nov 24 2016 Jakub Hrozek <jhrozek@redhat.com> - 1.14.0-43.9
-- Resolves: rhbz#1398169 - sssd fails to start after upgrading to RHEL 7.3
-
-* Wed Nov 23 2016 Jakub Hrozek <jhrozek@redhat.com> - 1.14.0-43.8
-- Resolves: rhbz#1392946 - sudo: ignore case on case insensitive domains
-
-* Wed Nov 23 2016 Jakub Hrozek <jhrozek@redhat.com> - 1.14.0-43.7
-- Resolves: rhbz#1393730 - No supplementary groups are resolved for users
+- Resolves: rhbz#1393085 - bz - ldap group names don't resolve after
+                           upgrading sssd to 1.14.0 if ldap_nesting_level is set to 0
+- Resolves: rhbz#1392444 - sssd_be keeps crashing
+- Resolves: rhbz#1392441 - sssd fails to start after upgrading to RHEL 7.3
+- Resolves: rhbz#1382602 - autofs map resolution doesn't work offline
+- Resolves: rhbz#1380436 - sudo: ignore case on case insensitive domains
+- Resolves: rhbz#1378251 - Typo In SSSD-AD Man Page
+- Resolves: rhbz#1373427 - Clock skew makes SSSD return System Error
+- Resolves: rhbz#1306707 - Need better handling of "Server not found in
+                           Kerberos database"
+- Resolves: rhbz#1297462 - Don't include 'enable_only=sssd' in the localauth
+                           plugin config
+
+* Mon Nov  7 2016 Jakub Hrozek <jhrozek@redhat.com> - 1.14.0-46
+- Resolves: rhbz#1382598 - IPA: Uninitialized variable during subdomain check
+
+* Mon Nov  7 2016 Jakub Hrozek <jhrozek@redhat.com> - 1.14.0-45
+- Resolves: rhbz#1378911 - No supplementary groups are resolved for users
                            in nested OUs when domain stanza differs from AD
                            domain
 
-* Fri Nov 18 2016 Jakub Hrozek <jhrozek@redhat.com> - 1.14.0-43.6
-- Related: rhbz#1396486 - bz - ldap group names don't resolve after
-                          upgrading sssd to 1.14.0 if ldap_nesting_level
-                          is set to 0
-
-* Fri Nov 18 2016 Jakub Hrozek <jhrozek@redhat.com> - 1.14.0-43.5
-- Related: rhbz#1396485 - sssd_be keeps crashing
-
-* Mon Nov 14 2016 Jakub Hrozek <jhrozek@redhat.com> - 1.14.0-43.4
-- Revert the fix for ignoring sudoUser case as it breaks processing
-  of rules that completely lack a sudoUser attribute
-- Related: rhbz#1392946 - sudo: ignore case on case insensitive domains
-
-* Wed Nov  9 2016 Jakub Hrozek <jhrozek@redhat.com> - 1.14.0-43.3
-- Resolves: rhbz#1392946 - sudo: ignore case on case insensitive domains
-
-* Tue Nov  8 2016 Jakub Hrozek <jhrozek@redhat.com> - 1.14.0-43.2
-- Resolves: rhbz#1392893 - IPA: Uninitialized variable during subdomain check
-
-* Mon Nov  7 2016 Jakub Hrozek <jhrozek@redhat.com> - 1.14.0-43.1
-- Resolves: rhbz#1392896 - AD provider: SSSD does not retrieve a domain-local
+* Mon Nov  7 2016 Jakub Hrozek <jhrozek@redhat.com> - 1.14.0-44
+- Resolves: rhbz#1372075 - AD provider: SSSD does not retrieve a domain-local
                            group with the AD provider when following AGGUDLP
                            group structure across domains
 
@@ -2447,7 +2824,7 @@ fi
 - Related: rhbz#991065
 
 * Wed Jul 31 2013 Jakub Hrozek <jhrozek@redhat.com> - 1.10.1-5
-- Resolves: #906427 - Do not use %{_lib} in specfile for the nss and
+- Resolves: #906427 - Do not use %%{_lib} in specfile for the nss and
                       pam libraries
 
 * Wed Jul 31 2013 Jakub Hrozek <jhrozek@redhat.com> - 1.10.1-4