diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..bae0ba3
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+SOURCES/sssd-1.15.2.tar.gz
diff --git a/.sssd.metadata b/.sssd.metadata
new file mode 100644
index 0000000..bc1073e
--- /dev/null
+++ b/.sssd.metadata
@@ -0,0 +1 @@
+f94298ac05169cdf5a9082c3aba9f6a18513720a SOURCES/sssd-1.15.2.tar.gz
diff --git a/README.md b/README.md
deleted file mode 100644
index 0e7897f..0000000
--- a/README.md
+++ /dev/null
@@ -1,5 +0,0 @@
-The master branch has no content
- 
-Look at the c7 branch if you are working with CentOS-7, or the c4/c5/c6 branch for CentOS-4, 5 or 6
- 
-If you find this file in a distro specific branch, it means that no content has been checked in yet
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/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/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-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/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-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/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-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/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-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/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/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/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/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/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/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/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/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/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-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-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/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/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/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/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-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/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/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/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-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/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/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/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/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-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/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/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/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/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/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/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/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-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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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-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-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-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-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-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-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-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-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-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-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-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-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-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/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-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/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/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/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/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/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/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-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-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-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-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-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-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-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/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/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/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-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/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/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/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-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/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/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/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/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/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/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/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/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-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/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/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/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/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-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-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/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/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-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/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/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/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/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/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/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-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-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-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-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-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/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/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-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-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-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/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-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/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/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/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-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/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/SOURCES/0187-RESPONDERS-Fix-terminating-idle-connections.patch b/SOURCES/0187-RESPONDERS-Fix-terminating-idle-connections.patch
new file mode 100644
index 0000000..724b886
--- /dev/null
+++ b/SOURCES/0187-RESPONDERS-Fix-terminating-idle-connections.patch
@@ -0,0 +1,84 @@
+From d6c7d35fdb4d416360a855a37b8c2164f053b470 Mon Sep 17 00:00:00 2001
+From: Jakub Hrozek <jhrozek@redhat.com>
+Date: Tue, 11 Jul 2017 18:26:01 +0200
+Subject: [PATCH 187/190] RESPONDERS: Fix terminating idle connections
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+The client_idle_handler() function tried to schedule another tevent
+timer to check for idle client connections in case the current
+connection was still valid, but in doing so, it also stored the current
+time into the last_request_time field of the client context.
+
+This kept the connection always alive, because the last_request_time
+could then never be older than the timeout.
+
+This patch changes the setup_client_idle_timer() function to only do
+what the synopsis says and set the idle timer. The caller (usually the
+function that accepts the connection) is supposed to store the request
+time itself.
+
+Resolves:
+https://pagure.io/SSSD/sssd/issue/3448
+
+Reviewed-by: Lukáš Slebodník <lslebodn@redhat.com>
+Reviewed-by: Fabiano Fidêncio <fidencio@redhat.com>
+---
+ src/responder/common/responder_common.c | 16 +++++++++++-----
+ 1 file changed, 11 insertions(+), 5 deletions(-)
+
+diff --git a/src/responder/common/responder_common.c b/src/responder/common/responder_common.c
+index 9d4889be652c6d6fb974b59001a9ac77b496e9ab..9d706f9799ef1b31122d8380fbf9c53ba0cc9e68 100644
+--- a/src/responder/common/responder_common.c
++++ b/src/responder/common/responder_common.c
+@@ -607,7 +607,15 @@ static void accept_fd_handler(struct tevent_context *ev,
+     cctx->ev = ev;
+     cctx->rctx = rctx;
+ 
+-    /* Set up the idle timer */
++    /* Record the new time and set up the idle timer */
++    ret = reset_client_idle_timer(cctx);
++    if (ret != EOK) {
++        DEBUG(SSSDBG_MINOR_FAILURE,
++              "Could not create idle timer for client. "
++              "This connection may not auto-terminate\n");
++        /* Non-fatal, continue */
++    }
++
+     ret = setup_client_idle_timer(cctx);
+     if (ret != EOK) {
+         DEBUG(SSSDBG_CRIT_FAILURE,
+@@ -634,7 +642,7 @@ static void client_idle_handler(struct tevent_context *ev,
+     if (cctx->last_request_time > now) {
+         DEBUG(SSSDBG_IMPORTANT_INFO,
+               "Time shift detected, re-scheduling the client timeout\n");
+-        goto end;
++        goto done;
+     }
+ 
+     if ((now - cctx->last_request_time) > cctx->rctx->client_idle_timeout) {
+@@ -648,7 +656,7 @@ static void client_idle_handler(struct tevent_context *ev,
+         return;
+     }
+ 
+-end:
++done:
+     setup_client_idle_timer(cctx);
+ }
+ 
+@@ -661,11 +669,9 @@ errno_t reset_client_idle_timer(struct cli_ctx *cctx)
+ 
+ static errno_t setup_client_idle_timer(struct cli_ctx *cctx)
+ {
+-    time_t now = time(NULL);
+     struct timeval tv =
+             tevent_timeval_current_ofs(cctx->rctx->client_idle_timeout/2, 0);
+ 
+-    cctx->last_request_time = now;
+     talloc_zfree(cctx->idle);
+ 
+     cctx->idle = tevent_add_timer(cctx->ev, cctx, tv, client_idle_handler, cctx);
+-- 
+2.9.4
+
diff --git a/SOURCES/0188-TESTS-Integration-test-for-idle-timeout.patch b/SOURCES/0188-TESTS-Integration-test-for-idle-timeout.patch
new file mode 100644
index 0000000..c587a0f
--- /dev/null
+++ b/SOURCES/0188-TESTS-Integration-test-for-idle-timeout.patch
@@ -0,0 +1,135 @@
+From fd008eddbf069014d8f17944d018ad3d85d5679f Mon Sep 17 00:00:00 2001
+From: Jakub Hrozek <jhrozek@redhat.com>
+Date: Wed, 19 Jul 2017 14:22:17 +0200
+Subject: [PATCH 188/190] TESTS: Integration test for idle timeout
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+The secrets responder test was chosen even though the bug was in the generic
+responder code b/c it runs a single responder process, so it's trivial to
+read the PID of the responder under test.
+
+Changes subprocess.call() for os.execv() so that the setup function can
+return the secret responder PID right away.
+
+The client timeout in the test has to be at least 10 seconds because
+internally, the responders don't allow a shorter timeout.
+
+Regression test for https://pagure.io/SSSD/sssd/issue/3448
+
+Reviewed-by: Lukáš Slebodník <lslebodn@redhat.com>
+Reviewed-by: Fabiano Fidêncio <fidencio@redhat.com>
+---
+ src/tests/intg/test_secrets.py | 75 ++++++++++++++++++++++++++++++++++--------
+ 1 file changed, 62 insertions(+), 13 deletions(-)
+
+diff --git a/src/tests/intg/test_secrets.py b/src/tests/intg/test_secrets.py
+index 202f43e61cb0e4986394ad2b32da5abdcb0be3e9..1be31318b194de1550bc84af16260bf1503567dc 100644
+--- a/src/tests/intg/test_secrets.py
++++ b/src/tests/intg/test_secrets.py
+@@ -55,9 +55,9 @@ def create_sssd_secrets_fixture(request):
+     assert secpid >= 0
+ 
+     if secpid == 0:
+-        if subprocess.call([resp_path, "--uid=0", "--gid=0"]) != 0:
+-            print("sssd_secrets failed to start")
+-            sys.exit(99)
++        os.execv(resp_path, ("--uid=0", "--gid=0"))
++        print("sssd_secrets failed to start")
++        sys.exit(99)
+     else:
+         sock_path = os.path.join(config.RUNSTATEDIR, "secrets.socket")
+         sck = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
+@@ -83,13 +83,8 @@ def create_sssd_secrets_fixture(request):
+     return secpid
+ 
+ 
+-@pytest.fixture
+-def setup_for_secrets(request):
+-    """
+-    Just set up the local provider for tests and enable the secrets
+-    responder
+-    """
+-    conf = unindent("""\
++def generate_sec_config():
++    return unindent("""\
+         [sssd]
+         domains = local
+         services = nss
+@@ -100,11 +95,19 @@ def setup_for_secrets(request):
+         [secrets]
+         max_secrets = 10
+         max_payload_size = 2
+-    """).format(**locals())
++    """)
++
++
++@pytest.fixture
++def setup_for_secrets(request):
++    """
++    Just set up the local provider for tests and enable the secrets
++    responder
++    """
++    conf = generate_sec_config()
+ 
+     create_conf_fixture(request, conf)
+-    create_sssd_secrets_fixture(request)
+-    return None
++    return create_sssd_secrets_fixture(request)
+ 
+ 
+ def get_secrets_socket():
+@@ -386,3 +389,49 @@ def test_containers(setup_for_secrets, secrets_cli):
+     with pytest.raises(HTTPError) as err406:
+         cli.create_container(container)
+     assert str(err406.value).startswith("406")
++
++
++def get_num_fds(pid):
++    procpath = os.path.join("/proc/", str(pid), "fd")
++    return len([fdname for fdname in os.listdir(procpath)])
++
++
++@pytest.fixture
++def setup_for_cli_timeout_test(request):
++    """
++    Same as the generic setup, except a short client_idle_timeout so that
++    the test_idle_timeout() test closes the fd towards the client.
++    """
++    conf = generate_sec_config() + \
++        unindent("""
++        client_idle_timeout = 10
++        """).format()
++
++    create_conf_fixture(request, conf)
++    return create_sssd_secrets_fixture(request)
++
++
++def test_idle_timeout(setup_for_cli_timeout_test):
++    """
++    Test that idle file descriptors are reaped after the idle timeout
++    passes
++    """
++    secpid = setup_for_cli_timeout_test
++    sock_path = get_secrets_socket()
++
++    nfds_pre = get_num_fds(secpid)
++
++    sock = socket.socket(family=socket.AF_UNIX)
++    sock.connect(sock_path)
++    time.sleep(1)
++    nfds_conn = get_num_fds(secpid)
++    assert nfds_pre + 1 == nfds_conn
++    # With the idle timeout set to 10 seconds, we need to sleep at least 15,
++    # because the internal timer ticks every timeout/2 seconds, so it would
++    # tick at 5, 10 and 15 seconds and the client timeout check uses a
++    # greater-than comparison, so the 10-seconds tick wouldn't yet trigger
++    # disconnect
++    time.sleep(15)
++
++    nfds_post = get_num_fds(secpid)
++    assert nfds_pre == nfds_post
+-- 
+2.9.4
+
diff --git a/SOURCES/0189-MAN-Document-that-client_idle_timeout-can-t-be-short.patch b/SOURCES/0189-MAN-Document-that-client_idle_timeout-can-t-be-short.patch
new file mode 100644
index 0000000..e353c96
--- /dev/null
+++ b/SOURCES/0189-MAN-Document-that-client_idle_timeout-can-t-be-short.patch
@@ -0,0 +1,36 @@
+From 0442102b2e5c6f1bc331ca2078baff8a7b4a50cb Mon Sep 17 00:00:00 2001
+From: Jakub Hrozek <jhrozek@redhat.com>
+Date: Thu, 20 Jul 2017 10:10:58 +0200
+Subject: [PATCH 189/190] MAN: Document that client_idle_timeout can't be
+ shorter than 10 seconds
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+To ensure the client timeout is not too low and clients do not reconnect
+too often, the client_idle_timeout is forced to be 10 seconds minimum.
+
+Reviewed-by: Lukáš Slebodník <lslebodn@redhat.com>
+Reviewed-by: Fabiano Fidêncio <fidencio@redhat.com>
+---
+ src/man/sssd.conf.5.xml | 4 +++-
+ 1 file changed, 3 insertions(+), 1 deletion(-)
+
+diff --git a/src/man/sssd.conf.5.xml b/src/man/sssd.conf.5.xml
+index a35f2807eac8bb89d6cb1dd0a48f738d71a7578f..89729575c724622af817f1c05a94d4ae8f1ece2d 100644
+--- a/src/man/sssd.conf.5.xml
++++ b/src/man/sssd.conf.5.xml
+@@ -621,7 +621,9 @@
+                             a client of an SSSD process can hold onto a file
+                             descriptor without communicating on it. This value
+                             is limited in order to avoid resource exhaustion
+-                            on the system.
++                            on the system. The timeout can't be shorter than
++                            10 seconds. If a lower value is configured, it
++                            will be adjusted to 10 seconds.
+                         </para>
+                         <para>
+                             Default: 60
+-- 
+2.9.4
+
diff --git a/SOURCES/0190-ad_account_can_shortcut-shortcut-if-ID-is-unknown.patch b/SOURCES/0190-ad_account_can_shortcut-shortcut-if-ID-is-unknown.patch
new file mode 100644
index 0000000..c652a4d
--- /dev/null
+++ b/SOURCES/0190-ad_account_can_shortcut-shortcut-if-ID-is-unknown.patch
@@ -0,0 +1,38 @@
+From 55e8b436443cfae1c3b2155be7325d53760f7271 Mon Sep 17 00:00:00 2001
+From: Sumit Bose <sbose@redhat.com>
+Date: Thu, 20 Jul 2017 20:01:14 +0200
+Subject: [PATCH 190/190] ad_account_can_shortcut: shortcut if ID is unknown
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+If sss_idmap_unix_to_sid() returns an error we can assume that the given
+POSIX ID is not from the current domain and can be skipped. This is e.g.
+the case in the IPA provider if a POSIX ID used in the IPA domain is
+checked in a trusted id-mapped AD domain before the IPA domain is
+checked.
+
+Resolves https://pagure.io/SSSD/sssd/issue/3452
+
+Reviewed-by: Lukáš Slebodník <lslebodn@redhat.com>
+Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
+---
+ src/providers/ad/ad_id.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+diff --git a/src/providers/ad/ad_id.c b/src/providers/ad/ad_id.c
+index d1f6c444f5ddbcbbac6ff7f41fb6c8bf9ca976cb..e14ada386f16851a65097952c85e57b7acda14aa 100644
+--- a/src/providers/ad/ad_id.c
++++ b/src/providers/ad/ad_id.c
+@@ -86,6 +86,8 @@ static bool ad_account_can_shortcut(struct sdap_idmap_ctx *idmap_ctx,
+         if (err != IDMAP_SUCCESS) {
+             DEBUG(SSSDBG_MINOR_FAILURE, "Mapping ID [%s] to SID failed: "
+                   "[%s]\n", filter_value, idmap_error_string(err));
++            /* assume id is from a different domain */
++            shortcut = true;
+             goto done;
+         }
+         /* fall through */
+-- 
+2.9.4
+
diff --git a/SOURCES/0191-sudo-add-a-threshold-option-to-reduce-size-of-rules-.patch b/SOURCES/0191-sudo-add-a-threshold-option-to-reduce-size-of-rules-.patch
new file mode 100644
index 0000000..54279f3
--- /dev/null
+++ b/SOURCES/0191-sudo-add-a-threshold-option-to-reduce-size-of-rules-.patch
@@ -0,0 +1,211 @@
+From 5c159808818fcea77822815b5f1131809c0e673c Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pavel=20B=C5=99ezina?= <pbrezina@redhat.com>
+Date: Tue, 11 Jul 2017 12:41:57 +0200
+Subject: [PATCH 191/191] sudo: add a threshold option to reduce size of rules
+ refresh filter
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+If a large number of rules is expired at one time the ldap filter may
+become too large to be processed by server. This commits adds a new
+option "sudo_threshold" to sudo responder. If the threshold is
+exceeded a full refreshed is done instead of rules refresh.
+
+Resolves:
+https://pagure.io/SSSD/sssd/issue/3478
+
+Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
+Reviewed-by: Fabiano Fidêncio <fidencio@redhat.com>
+(cherry picked from commit a5f300adf19ec9c3087c62bd93a5175db799687a)
+---
+ src/confdb/confdb.h                        |  2 ++
+ src/config/SSSDConfig/__init__.py.in       |  1 +
+ src/config/cfg_rules.ini                   |  1 +
+ src/config/etc/sssd.api.conf               |  1 +
+ src/man/sssd.conf.5.xml                    | 19 +++++++++++++++++++
+ src/responder/sudo/sudosrv.c               | 11 +++++++++++
+ src/responder/sudo/sudosrv_get_sudorules.c | 25 ++++++++++++++++++++-----
+ src/responder/sudo/sudosrv_private.h       |  1 +
+ 8 files changed, 56 insertions(+), 5 deletions(-)
+
+diff --git a/src/confdb/confdb.h b/src/confdb/confdb.h
+index 2ba1bc47ee11f699726cefaf7c3335d2a8afee49..884b5bd1a493ca9a71654536524125eb8c7c4533 100644
+--- a/src/confdb/confdb.h
++++ b/src/confdb/confdb.h
+@@ -139,6 +139,8 @@
+ #define CONFDB_DEFAULT_SUDO_TIMED false
+ #define CONFDB_SUDO_INVERSE_ORDER "sudo_inverse_order"
+ #define CONFDB_DEFAULT_SUDO_INVERSE_ORDER false
++#define CONFDB_SUDO_THRESHOLD "sudo_threshold"
++#define CONFDB_DEFAULT_SUDO_THRESHOLD 50
+ 
+ /* autofs */
+ #define CONFDB_AUTOFS_CONF_ENTRY "config/autofs"
+diff --git a/src/config/SSSDConfig/__init__.py.in b/src/config/SSSDConfig/__init__.py.in
+index 75515ab5c68822538728900482296b9159e1547e..137a8fa4d526cb10f3136c62f3c7104d9ecb7599 100644
+--- a/src/config/SSSDConfig/__init__.py.in
++++ b/src/config/SSSDConfig/__init__.py.in
+@@ -107,6 +107,7 @@ option_strings = {
+     # [sudo]
+     'sudo_timed' : _('Whether to evaluate the time-based attributes in sudo rules'),
+     'sudo_inverse_order' : _('If true, SSSD will switch back to lower-wins ordering logic'),
++    'sudo_threshold' : _('Maximum number of rules that can be refreshed at once. If this is exceeded, full refresh is performed.'),
+ 
+     # [autofs]
+     'autofs_negative_timeout' : _('Negative cache timeout length (seconds)'),
+diff --git a/src/config/cfg_rules.ini b/src/config/cfg_rules.ini
+index d6506b7c3cee13f7c5400a546deb787e755abc8b..0bdcfdfbefd6cb24e0c01cb9746dbb98c63a31d2 100644
+--- a/src/config/cfg_rules.ini
++++ b/src/config/cfg_rules.ini
+@@ -144,6 +144,7 @@ option = cache_first
+ # sudo service
+ option = sudo_timed
+ option = sudo_inverse_order
++option = sudo_threshold
+ 
+ [rule/allowed_autofs_options]
+ validator = ini_allowed_options
+diff --git a/src/config/etc/sssd.api.conf b/src/config/etc/sssd.api.conf
+index f86589ecefa0b9e046aba781ded107f8e94395d6..9d5eaaaa23c4c5395b155563de1cdf7752aa3dde 100644
+--- a/src/config/etc/sssd.api.conf
++++ b/src/config/etc/sssd.api.conf
+@@ -79,6 +79,7 @@ pam_app_services = str, None, false
+ # sudo service
+ sudo_timed = bool, None, false
+ sudo_inverse_order = bool, None, false
++sudo_threshold = int, None, false
+ 
+ [autofs]
+ # autofs service
+diff --git a/src/man/sssd.conf.5.xml b/src/man/sssd.conf.5.xml
+index 89729575c724622af817f1c05a94d4ae8f1ece2d..d508df82d1d99af7835079c928839dc3cc7c28cb 100644
+--- a/src/man/sssd.conf.5.xml
++++ b/src/man/sssd.conf.5.xml
+@@ -1376,6 +1376,25 @@ pam_account_locked_message = Account locked, please contact help desk.
+                     </listitem>
+                 </varlistentry>
+             </variablelist>
++            <variablelist>
++                <varlistentry>
++                    <term>sudo_threshold (integer)</term>
++                    <listitem>
++                        <para>
++                            Maximum number of expired rules that can be
++                            refreshed at once. If number of expired rules
++                            is below threshold, those rules are refreshed
++                            with <quote>rules refresh</quote> mechanism. If
++                            the threshold is exceeded a
++                            <quote>full refresh</quote> of sudo rules is
++                            triggered instead.
++                        </para>
++                        <para>
++                            Default: 50
++                        </para>
++                    </listitem>
++                </varlistentry>
++            </variablelist>
+         </refsect2>
+ 
+         <refsect2 id='AUTOFS' condition="with_autofs">
+diff --git a/src/responder/sudo/sudosrv.c b/src/responder/sudo/sudosrv.c
+index b427878d4dbe9090824a01386a7475be88b699c0..dca70ea4afc0e6df6d1b1864338c7b1091a98fee 100644
+--- a/src/responder/sudo/sudosrv.c
++++ b/src/responder/sudo/sudosrv.c
+@@ -148,6 +148,17 @@ int sudo_process_init(TALLOC_CTX *mem_ctx,
+         goto fail;
+     }
+ 
++    /* Get sudo_inverse_order option */
++    ret = confdb_get_int(sudo_ctx->rctx->cdb,
++                         CONFDB_SUDO_CONF_ENTRY, CONFDB_SUDO_THRESHOLD,
++                         CONFDB_DEFAULT_SUDO_THRESHOLD,
++                         &sudo_ctx->threshold);
++    if (ret != EOK) {
++        DEBUG(SSSDBG_FATAL_FAILURE, "Error reading from confdb (%d) [%s]\n",
++              ret, strerror(ret));
++        goto fail;
++    }
++
+     ret = schedule_get_domains_task(rctx, rctx->ev, rctx, NULL);
+     if (ret != EOK) {
+         DEBUG(SSSDBG_FATAL_FAILURE, "schedule_get_domains_tasks failed.\n");
+diff --git a/src/responder/sudo/sudosrv_get_sudorules.c b/src/responder/sudo/sudosrv_get_sudorules.c
+index cfdbfc9c9c66d96f774822d6a4d4aaaf1327abe3..3272e634d895acf4854309371779a00cf1525126 100644
+--- a/src/responder/sudo/sudosrv_get_sudorules.c
++++ b/src/responder/sudo/sudosrv_get_sudorules.c
+@@ -479,6 +479,7 @@ sudosrv_refresh_rules_send(TALLOC_CTX *mem_ctx,
+                            struct tevent_context *ev,
+                            struct resp_ctx *rctx,
+                            struct sss_domain_info *domain,
++                           int threshold,
+                            uid_t uid,
+                            const char *username,
+                            char **groups)
+@@ -520,9 +521,20 @@ sudosrv_refresh_rules_send(TALLOC_CTX *mem_ctx,
+     DEBUG(SSSDBG_TRACE_INTERNAL, "Refreshing %d expired rules of [%s@%s]\n",
+           num_rules, username, domain->name);
+ 
+-    subreq = sss_dp_get_sudoers_send(state, rctx, domain, false,
+-                                     SSS_DP_SUDO_REFRESH_RULES,
+-                                     username, num_rules, rules);
++    if (num_rules > threshold) {
++        DEBUG(SSSDBG_TRACE_INTERNAL,
++              "Rules threshold [%d] is reached, performing full refresh "
++              "instead.\n", threshold);
++
++        subreq = sss_dp_get_sudoers_send(state, rctx, domain, false,
++                                         SSS_DP_SUDO_FULL_REFRESH,
++                                         username, 0, NULL);
++    } else {
++        subreq = sss_dp_get_sudoers_send(state, rctx, domain, false,
++                                         SSS_DP_SUDO_REFRESH_RULES,
++                                         username, num_rules, rules);
++    }
++
+     if (subreq == NULL) {
+         ret = ENOMEM;
+         goto immediately;
+@@ -609,6 +621,7 @@ struct sudosrv_get_rules_state {
+     struct sss_domain_info *domain;
+     char **groups;
+     bool inverse_order;
++    int threshold;
+ 
+     struct sysdb_attrs **rules;
+     uint32_t num_rules;
+@@ -640,6 +653,7 @@ struct tevent_req *sudosrv_get_rules_send(TALLOC_CTX *mem_ctx,
+     state->type = type;
+     state->uid = uid;
+     state->inverse_order = sudo_ctx->inverse_order;
++    state->threshold = sudo_ctx->threshold;
+ 
+     DEBUG(SSSDBG_TRACE_FUNC, "Running initgroups for [%s]\n", username);
+ 
+@@ -696,8 +710,9 @@ static void sudosrv_get_rules_initgr_done(struct tevent_req *subreq)
+     }
+ 
+     subreq = sudosrv_refresh_rules_send(state, state->ev, state->rctx,
+-                                        state->domain, state->uid,
+-                                        state->username, state->groups);
++                                        state->domain, state->threshold,
++                                        state->uid, state->username,
++                                        state->groups);
+     if (subreq == NULL) {
+         ret = ENOMEM;
+         goto done;
+diff --git a/src/responder/sudo/sudosrv_private.h b/src/responder/sudo/sudosrv_private.h
+index 94f3c4458ab20e64db3e0bfce726d5d30a70a202..c76bdd3955bc29b7ba2cda58c503a4c616d7e63a 100644
+--- a/src/responder/sudo/sudosrv_private.h
++++ b/src/responder/sudo/sudosrv_private.h
+@@ -48,6 +48,7 @@ struct sudo_ctx {
+      */
+     bool timed;
+     bool inverse_order;
++    int threshold;
+ };
+ 
+ struct sudo_cmd_ctx {
+-- 
+2.13.5
+
diff --git a/SOURCES/0192-libwbclient-Change-return-code-for-wbcAuthenticateUs.patch b/SOURCES/0192-libwbclient-Change-return-code-for-wbcAuthenticateUs.patch
new file mode 100644
index 0000000..c33350b
--- /dev/null
+++ b/SOURCES/0192-libwbclient-Change-return-code-for-wbcAuthenticateUs.patch
@@ -0,0 +1,57 @@
+From b9a2edea74ea04a09301f91fffb6835df72d8760 Mon Sep 17 00:00:00 2001
+From: Lukas Slebodnik <lslebodn@redhat.com>
+Date: Tue, 22 Aug 2017 13:09:18 +0200
+Subject: [PATCH 192/192] libwbclient: Change return code for
+ wbcAuthenticateUserEx
+
+Samba-4.6 change behaviour of few functions
+New version of code make sure session info for user is stored in cache.
+It is a performance optimisation to prevent contacting KDC for each
+session. More details in samba bug
+https://bugzilla.samba.org/show_bug.cgi?id=11259
+
+Old return code WBC_SSSD_NOT_IMPLEMENTED was translated
+to NT_STATUS_LOGON_FAILURE which caused many failures.
+
+    [2017/08/21 11:34:15.044321,  5, pid=27742, effective(0, 0), real(0, 0)]
+    ../libcli/security/security_token.c:53(security_token_debug)
+      Security token: (NULL)
+    [2017/08/21 11:34:15.044330,  5, pid=27742, effective(0, 0), real(0, 0)]
+    ../source3/auth/token_util.c:640(debug_unix_user_token)
+      UNIX token of user 0
+      Primary group is 0 and contains 0 supplementary groups
+    [2017/08/21 11:34:15.044349,  4, pid=27742, effective(0, 0), real(0, 0)]
+    ../source3/smbd/sec_ctx.c:439(pop_sec_ctx)
+      pop_sec_ctx (0, 0) - sec_ctx_stack_ndx = 0
+    [2017/08/21 11:34:15.044360,  1, pid=27742, effective(0, 0), real(0, 0)]
+    ../source3/smbd/sesssetup.c:290(reply_sesssetup_and_X_spnego)
+      Failed to generate session_info (user and group token) for session
+    setup: NT_STATUS_LOGON_FAILURE
+
+Resolves:
+https://pagure.io/SSSD/sssd/issue/3461
+
+Reviewed-by: Sumit Bose <sbose@redhat.com>
+(cherry picked from commit 725d04cd21016dc6092a9f03cd363bb83d7c054c)
+(cherry picked from commit aede6a1f4412f133e4b3fd76944f764d76fc4868)
+(cherry picked from commit 260062d946e7cc265e2671f88b1662276431c0bb)
+---
+ src/sss_client/libwbclient/wbc_pam_sssd.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/src/sss_client/libwbclient/wbc_pam_sssd.c b/src/sss_client/libwbclient/wbc_pam_sssd.c
+index 174cf1310fad0243036fe591978cc89700903896..77698f523e6e7aeb37d4db50b469d1604d7ee595 100644
+--- a/src/sss_client/libwbclient/wbc_pam_sssd.c
++++ b/src/sss_client/libwbclient/wbc_pam_sssd.c
+@@ -49,7 +49,7 @@ wbcErr wbcAuthenticateUserEx(const struct wbcAuthUserParams *params,
+         *error = NULL;
+     }
+ 
+-    WBC_SSSD_NOT_IMPLEMENTED;
++    return WBC_ERR_WINBIND_NOT_AVAILABLE;
+ }
+ 
+ /* Trigger a verification of the trust credentials of a specific domain */
+-- 
+2.9.4
+
diff --git a/SOURCES/0193-IPA-fix-handling-of-certmap_ctx.patch b/SOURCES/0193-IPA-fix-handling-of-certmap_ctx.patch
new file mode 100644
index 0000000..47b85fb
--- /dev/null
+++ b/SOURCES/0193-IPA-fix-handling-of-certmap_ctx.patch
@@ -0,0 +1,666 @@
+From 2fca2f1b77c0e9ae82e1a24bbf89fbc3115a5e24 Mon Sep 17 00:00:00 2001
+From: Sumit Bose <sbose@redhat.com>
+Date: Wed, 6 Sep 2017 16:42:20 +0200
+Subject: [PATCH 193/194] IPA: fix handling of certmap_ctx
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+This patch fixes a use-after-free in the AD provider part and
+initializes the certmap_ctx with data from the cache at startup.
+
+Related to https://pagure.io/SSSD/sssd/issue/3508
+
+Reviewed-by: Lukáš Slebodník <lslebodn@redhat.com>
+Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
+(cherry picked from commit f2e70ec742cd7aab82b74d7e4b424ba3258da7aa)
+---
+ Makefile.am                               |  20 +++
+ src/providers/ipa/ipa_init.c              |   7 +
+ src/providers/ipa/ipa_subdomains.c        |  53 +------
+ src/providers/ipa/ipa_subdomains_server.c |   4 +-
+ src/providers/ldap/ldap_common.h          |   5 +
+ src/providers/ldap/ldap_id.c              |   5 +-
+ src/providers/ldap/sdap.h                 |   4 +-
+ src/providers/ldap/sdap_certmap.c         | 152 +++++++++++++++++++
+ src/tests/cmocka/test_sdap_certmap.c      | 244 ++++++++++++++++++++++++++++++
+ 9 files changed, 441 insertions(+), 53 deletions(-)
+ create mode 100644 src/providers/ldap/sdap_certmap.c
+ create mode 100644 src/tests/cmocka/test_sdap_certmap.c
+
+diff --git a/Makefile.am b/Makefile.am
+index 503c8cfd795b503f566431c08a56a56147180322..907c3256a154ebe2aae5a1667744e1dfbe8abaae 100644
+--- a/Makefile.am
++++ b/Makefile.am
+@@ -257,6 +257,7 @@ if HAVE_CMOCKA
+         test_search_bases \
+         test_ldap_auth \
+         test_sdap_access \
++        test_sdap_certmap \
+         sdap-tests \
+         test_sysdb_ts_cache \
+         test_sysdb_views \
+@@ -2662,6 +2663,24 @@ test_sdap_access_LDADD = \
+     libdlopen_test_providers.la \
+     $(NULL)
+ 
++test_sdap_certmap_SOURCES = \
++    src/tests/cmocka/test_sdap_certmap.c \
++    src/providers/ldap/sdap_certmap.c \
++    $(NULL)
++test_sdap_certmap_CFLAGS = \
++    $(AM_CFLAGS) \
++    $(TALLOC_CFLAGS) \
++    $(POPT_CFLAGS) \
++    $(NULL)
++test_sdap_certmap_LDADD = \
++    $(CMOCKA_LIBS) \
++    $(TALLOC_LIBS) \
++    $(POPT_LIBS) \
++    $(SSSD_INTERNAL_LTLIBS) \
++    libsss_test_common.la \
++    libsss_certmap.la \
++    $(NULL)
++
+ ad_access_filter_tests_SOURCES = \
+     src/tests/cmocka/test_ad_access_filter.c
+ ad_access_filter_tests_LDADD = \
+@@ -3706,6 +3725,7 @@ libsss_ldap_common_la_SOURCES = \
+     src/providers/ldap/sdap_child_helpers.c \
+     src/providers/ldap/sdap_fd_events.c \
+     src/providers/ldap/sdap_id_op.c \
++    src/providers/ldap/sdap_certmap.c \
+     src/providers/ldap/sdap_idmap.c \
+     src/providers/ldap/sdap_idmap.h \
+     src/providers/ldap/sdap_range.c \
+diff --git a/src/providers/ipa/ipa_init.c b/src/providers/ipa/ipa_init.c
+index 7dec4d1fb8541a48470d4e44f10838e5bea67ad5..2b58b1341463f8947d51dee2076dbe92e3093558 100644
+--- a/src/providers/ipa/ipa_init.c
++++ b/src/providers/ipa/ipa_init.c
+@@ -649,6 +649,13 @@ static errno_t ipa_init_misc(struct be_ctx *be_ctx,
+         return ENOMEM;
+     }
+ 
++    ret = sdap_init_certmap(sdap_id_ctx, sdap_id_ctx);
++    if (ret != EOK) {
++        DEBUG(SSSDBG_CRIT_FAILURE,
++              "Failed to initialized certificate mapping.\n");
++        return ret;
++    }
++
+     return EOK;
+ }
+ 
+diff --git a/src/providers/ipa/ipa_subdomains.c b/src/providers/ipa/ipa_subdomains.c
+index ef348adf4a36e870f44387bd700f5c2beea3bfd6..6f0ff50bde234f72d62f43635d9a787316c78430 100644
+--- a/src/providers/ipa/ipa_subdomains.c
++++ b/src/providers/ipa/ipa_subdomains.c
+@@ -311,25 +311,6 @@ struct priv_sss_debug {
+     int level;
+ };
+ 
+-void ext_debug(void *private, const char *file, long line, const char *function,
+-               const char *format, ...)
+-{
+-    va_list ap;
+-    struct priv_sss_debug *data = private;
+-    int level = SSSDBG_OP_FAILURE;
+-
+-    if (data != NULL) {
+-        level = data->level;
+-    }
+-
+-    if (DEBUG_IS_SET(level)) {
+-        va_start(ap, format);
+-        sss_vdebug_fn(file, line, function, level, APPEND_LINE_FEED,
+-                      format, ap);
+-        va_end(ap);
+-    }
+-}
+-
+ static errno_t ipa_certmap_parse_results(TALLOC_CTX *mem_ctx,
+                                          struct sss_domain_info *domain,
+                                          struct sdap_options *sdap_opts,
+@@ -344,7 +325,6 @@ static errno_t ipa_certmap_parse_results(TALLOC_CTX *mem_ctx,
+     size_t c;
+     size_t lc = 0;
+     int ret;
+-    struct sss_certmap_ctx *certmap_ctx = NULL;
+     const char **ocs = NULL;
+     bool user_name_hint = false;
+ 
+@@ -444,50 +424,29 @@ static errno_t ipa_certmap_parse_results(TALLOC_CTX *mem_ctx,
+ 
+     certmap_list[lc] = NULL;
+ 
+-    ret = sss_certmap_init(mem_ctx, ext_debug, NULL, &certmap_ctx);
+-    if (ret != 0) {
+-        DEBUG(SSSDBG_OP_FAILURE, "sss_certmap_init failed.\n");
++    ret = sdap_setup_certmap(sdap_opts->sdap_certmap_ctx, certmap_list);
++    if (ret != EOK) {
++        DEBUG(SSSDBG_OP_FAILURE, "sdap_setup_certmap failed.\n");
+         goto done;
+     }
+ 
+-    for (c = 0; certmap_list[c] != NULL; c++) {
+-        DEBUG(SSSDBG_TRACE_ALL, "Trying to add rule [%s][%d][%s][%s].\n",
+-                                certmap_list[c]->name,
+-                                certmap_list[c]->priority,
+-                                certmap_list[c]->match_rule,
+-                                certmap_list[c]->map_rule);
+-
+-        ret = sss_certmap_add_rule(certmap_ctx, certmap_list[c]->priority,
+-                                   certmap_list[c]->match_rule,
+-                                   certmap_list[c]->map_rule,
+-                                   certmap_list[c]->domains);
+-        if (ret != 0) {
+-            DEBUG(SSSDBG_CRIT_FAILURE,
+-                  "sss_certmap_add_rule failed for rule [%s], skipping. "
+-                  "Please check for typos and if rule syntax is supported.\n",
+-                  certmap_list[c]->name);
+-            goto done;
+-        }
+-    }
+-
+     ret = sysdb_update_certmap(domain->sysdb, certmap_list, user_name_hint);
+     if (ret != EOK) {
+         DEBUG(SSSDBG_OP_FAILURE, "sysdb_update_certmap failed");
+         goto done;
+     }
+ 
+-    sss_certmap_free_ctx(sdap_opts->certmap_ctx);
+-    sdap_opts->certmap_ctx = talloc_steal(sdap_opts, certmap_ctx);
+-
+     if (_certmap_list != NULL) {
+         *_certmap_list = certmap_list;
++    } else {
++        talloc_free(certmap_list);
+     }
++
+     ret = EOK;
+ 
+ done:
+     talloc_free(ocs);
+     if (ret != EOK) {
+-        sss_certmap_free_ctx(certmap_ctx);
+         talloc_free(certmap_list);
+     }
+ 
+diff --git a/src/providers/ipa/ipa_subdomains_server.c b/src/providers/ipa/ipa_subdomains_server.c
+index 443d83824f329b9d8c3d8e820113e1029f832240..56470ac824feaa59eecbd9f442682220237c2412 100644
+--- a/src/providers/ipa/ipa_subdomains_server.c
++++ b/src/providers/ipa/ipa_subdomains_server.c
+@@ -361,8 +361,8 @@ ipa_ad_ctx_new(struct be_ctx *be_ctx,
+         id_ctx->sdap_id_ctx->opts->idmap_ctx;
+ 
+     /* Set up the certificate mapping context */
+-    ad_id_ctx->sdap_id_ctx->opts->certmap_ctx =
+-        id_ctx->sdap_id_ctx->opts->certmap_ctx;
++    ad_id_ctx->sdap_id_ctx->opts->sdap_certmap_ctx =
++        id_ctx->sdap_id_ctx->opts->sdap_certmap_ctx;
+ 
+     *_ad_id_ctx = ad_id_ctx;
+     return EOK;
+diff --git a/src/providers/ldap/ldap_common.h b/src/providers/ldap/ldap_common.h
+index 1acda4147576503b18f61e0bb56f8efd2263fd44..0510b7d5ab5121bd96f699e8e59520a2a18a604f 100644
+--- a/src/providers/ldap/ldap_common.h
++++ b/src/providers/ldap/ldap_common.h
+@@ -362,4 +362,9 @@ sdap_id_ctx_new(TALLOC_CTX *mem_ctx, struct be_ctx *bectx,
+ errno_t sdap_refresh_init(struct be_refresh_ctx *refresh_ctx,
+                           struct sdap_id_ctx *id_ctx);
+ 
++errno_t sdap_init_certmap(TALLOC_CTX *mem_ctx, struct sdap_id_ctx *id_ctx);
++
++errno_t sdap_setup_certmap(struct sdap_certmap_ctx *sdap_certmap_ctx,
++                           struct certmap_info **certmap_list);
++struct sss_certmap_ctx *sdap_get_sss_certmap(struct sdap_certmap_ctx *ctx);
+ #endif /* _LDAP_COMMON_H_ */
+diff --git a/src/providers/ldap/ldap_id.c b/src/providers/ldap/ldap_id.c
+index 557712e8dc2b2bde664b4054fa2f8eb39df84d73..93204d35ea3782c9aa5d622a962c295869472631 100644
+--- a/src/providers/ldap/ldap_id.c
++++ b/src/providers/ldap/ldap_id.c
+@@ -252,9 +252,8 @@ struct tevent_req *users_get_send(TALLOC_CTX *memctx,
+         }
+ 
+         ret = sss_cert_derb64_to_ldap_filter(state, filter_value, attr_name,
+-                                             ctx->opts->certmap_ctx,
+-                                             state->domain,
+-                                             &user_filter);
++                              sdap_get_sss_certmap(ctx->opts->sdap_certmap_ctx),
++                              state->domain, &user_filter);
+         if (ret != EOK) {
+             DEBUG(SSSDBG_OP_FAILURE,
+                   "sss_cert_derb64_to_ldap_filter failed.\n");
+diff --git a/src/providers/ldap/sdap.h b/src/providers/ldap/sdap.h
+index afdc01948eefe9dda943c8c7ad01a42dd76a1da8..c85fbe9e78e5eefa7e33ea8055730118b0871a4c 100644
+--- a/src/providers/ldap/sdap.h
++++ b/src/providers/ldap/sdap.h
+@@ -446,6 +446,8 @@ struct sdap_ext_member_ctx {
+     ext_member_recv_fn_t ext_member_resolve_recv;
+ };
+ 
++struct sdap_certmap_ctx;
++
+ struct sdap_options {
+     struct dp_option *basic;
+     struct sdap_attr_map *gen_map;
+@@ -481,7 +483,7 @@ struct sdap_options {
+     enum dc_functional_level dc_functional_level;
+ 
+     /* Certificate mapping support */
+-    struct sss_certmap_ctx *certmap_ctx;
++    struct sdap_certmap_ctx *sdap_certmap_ctx;
+ };
+ 
+ struct sdap_server_opts {
+diff --git a/src/providers/ldap/sdap_certmap.c b/src/providers/ldap/sdap_certmap.c
+new file mode 100644
+index 0000000000000000000000000000000000000000..fcf88a9c69482c8668d486cd2ab0ba37c847e46d
+--- /dev/null
++++ b/src/providers/ldap/sdap_certmap.c
+@@ -0,0 +1,152 @@
++
++/*
++    SSSD
++
++    Authors:
++        Sumit Bose <sbose@redhat.com>
++
++    Copyright (C) 2017 Red Hat
++
++    This program is free software; you can redistribute it and/or modify
++    it under the terms of the GNU General Public License as published by
++    the Free Software Foundation; either version 3 of the License, or
++    (at your option) any later version.
++
++    This program is distributed in the hope that it will be useful,
++    but WITHOUT ANY WARRANTY; without even the implied warranty of
++    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++    GNU General Public License for more details.
++
++    You should have received a copy of the GNU General Public License
++    along with this program.  If not, see <http://www.gnu.org/licenses/>.
++*/
++
++#include "util/util.h"
++#include "lib/certmap/sss_certmap.h"
++#include "providers/ldap/ldap_common.h"
++
++struct sdap_certmap_ctx {
++    struct sss_certmap_ctx *certmap_ctx;
++};
++
++struct priv_sss_debug {
++    int level;
++};
++
++static void ext_debug(void *private, const char *file, long line,
++                      const char *function, const char *format, ...)
++{
++    va_list ap;
++    struct priv_sss_debug *data = private;
++    int level = SSSDBG_OP_FAILURE;
++
++    if (data != NULL) {
++        level = data->level;
++    }
++
++    if (DEBUG_IS_SET(level)) {
++        va_start(ap, format);
++        sss_vdebug_fn(file, line, function, level, APPEND_LINE_FEED,
++                      format, ap);
++        va_end(ap);
++    }
++}
++
++struct sss_certmap_ctx *sdap_get_sss_certmap(struct sdap_certmap_ctx *ctx)
++{
++    return ctx == NULL ? NULL : ctx->certmap_ctx;
++}
++
++errno_t sdap_setup_certmap(struct sdap_certmap_ctx *sdap_certmap_ctx,
++                           struct certmap_info **certmap_list)
++{
++    int ret;
++    struct sss_certmap_ctx *sss_certmap_ctx = NULL;
++    size_t c;
++
++    if (sdap_certmap_ctx == NULL) {
++        DEBUG(SSSDBG_CRIT_FAILURE, "Missing sdap_certmap_ctx.\n");
++        return EINVAL;
++    }
++
++    if (certmap_list == NULL || *certmap_list == NULL) {
++        DEBUG(SSSDBG_TRACE_ALL, "No certmap data, nothing to do.\n");
++        ret = EOK;
++        goto done;
++    }
++
++    ret = sss_certmap_init(sdap_certmap_ctx, ext_debug, NULL, &sss_certmap_ctx);
++    if (ret != EOK) {
++        DEBUG(SSSDBG_OP_FAILURE, "sss_certmap_init failed.\n");
++        goto done;
++    }
++
++    for (c = 0; certmap_list[c] != NULL; c++) {
++        DEBUG(SSSDBG_TRACE_ALL, "Trying to add rule [%s][%d][%s][%s].\n",
++                                certmap_list[c]->name,
++                                certmap_list[c]->priority,
++                                certmap_list[c]->match_rule,
++                                certmap_list[c]->map_rule);
++
++        ret = sss_certmap_add_rule(sss_certmap_ctx, certmap_list[c]->priority,
++                                   certmap_list[c]->match_rule,
++                                   certmap_list[c]->map_rule,
++                                   certmap_list[c]->domains);
++        if (ret != 0) {
++            DEBUG(SSSDBG_CRIT_FAILURE,
++                  "sss_certmap_add_rule failed for rule [%s] "
++                  "with error [%d][%s], skipping. "
++                  "Please check for typos and if rule syntax is supported.\n",
++                  certmap_list[c]->name, ret, sss_strerror(ret));
++            continue;
++        }
++    }
++
++    ret = EOK;
++
++done:
++    if (ret == EOK) {
++        sss_certmap_free_ctx(sdap_certmap_ctx->certmap_ctx);
++        sdap_certmap_ctx->certmap_ctx = sss_certmap_ctx;
++    } else {
++        sss_certmap_free_ctx(sss_certmap_ctx);
++    }
++
++    return ret;
++}
++
++errno_t sdap_init_certmap(TALLOC_CTX *mem_ctx, struct sdap_id_ctx *id_ctx)
++{
++    int ret;
++    bool hint;
++    struct certmap_info **certmap_list = NULL;
++
++    if (id_ctx->opts->sdap_certmap_ctx == NULL) {
++        id_ctx->opts->sdap_certmap_ctx = talloc_zero(mem_ctx,
++                                                     struct sdap_certmap_ctx);
++        if (id_ctx->opts->sdap_certmap_ctx == NULL) {
++            DEBUG(SSSDBG_OP_FAILURE, "talloc_zero failed.\n");
++            return ENOMEM;
++        }
++    }
++
++    ret = sysdb_get_certmap(mem_ctx, id_ctx->be->domain->sysdb,
++                            &certmap_list, &hint);
++    if (ret != EOK) {
++        DEBUG(SSSDBG_OP_FAILURE, "sysdb_get_certmap failed.\n");
++        goto done;
++    }
++
++    ret = sdap_setup_certmap(id_ctx->opts->sdap_certmap_ctx, certmap_list);
++    if (ret != EOK) {
++            DEBUG(SSSDBG_OP_FAILURE, "sdap_setup_certmap failed.\n");
++            goto done;
++    }
++
++    ret = EOK;
++
++done:
++    talloc_free(certmap_list);
++
++    return ret;
++}
+diff --git a/src/tests/cmocka/test_sdap_certmap.c b/src/tests/cmocka/test_sdap_certmap.c
+new file mode 100644
+index 0000000000000000000000000000000000000000..9df5666844c8582a3fdb5b086720f1f2819f53f3
+--- /dev/null
++++ b/src/tests/cmocka/test_sdap_certmap.c
+@@ -0,0 +1,244 @@
++/*
++    Authors:
++        Sumit Bose <sbose@redhat.com>
++
++    Copyright (C) 2017 Red Hat
++
++    SSSD tests - sdap certmap
++
++    This program is free software; you can redistribute it and/or modify
++    it under the terms of the GNU General Public License as published by
++    the Free Software Foundation; either version 3 of the License, or
++    (at your option) any later version.
++
++    This program is distributed in the hope that it will be useful,
++    but WITHOUT ANY WARRANTY; without even the implied warranty of
++    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++    GNU General Public License for more details.
++
++    You should have received a copy of the GNU General Public License
++    along with this program.  If not, see <http://www.gnu.org/licenses/>.
++*/
++
++#include <stdarg.h>
++#include <stdbool.h>
++#include <setjmp.h>
++#include <unistd.h>
++#include <cmocka.h>
++#include <popt.h>
++
++#include "providers/ldap/ldap_common.h"
++#include "tests/common.h"
++#include "db/sysdb.h"
++
++#define TESTS_PATH "certmap_" BASE_FILE_STEM
++#define TEST_CONF_DB "test_sysdb_certmap.ldb"
++#define TEST_ID_PROVIDER "ldap"
++#define TEST_DOM_NAME "certmap_test"
++
++struct certmap_info map_a = { discard_const("map_a"), 11,
++                              NULL, discard_const("(abc=def)"),
++                              NULL };
++struct certmap_info map_b = { discard_const("map_b"), UINT_MAX,
++                              NULL, NULL, NULL };
++struct certmap_info *certmap[] = { &map_a, &map_b, NULL };
++
++struct certmap_test_ctx {
++    struct sss_test_ctx *tctx;
++    struct sdap_id_ctx *id_ctx;
++};
++
++static int test_sysdb_setup(void **state)
++{
++    int ret;
++    struct certmap_test_ctx *test_ctx;
++    struct sss_test_conf_param params[] = {
++        { NULL, NULL },             /* Sentinel */
++    };
++
++    assert_true(leak_check_setup());
++
++    test_ctx = talloc_zero(global_talloc_context,
++                           struct certmap_test_ctx);
++    assert_non_null(test_ctx);
++    check_leaks_push(test_ctx);
++
++    test_dom_suite_setup(TESTS_PATH);
++
++    test_ctx->tctx = create_dom_test_ctx(test_ctx, TESTS_PATH,
++                                         TEST_CONF_DB, TEST_DOM_NAME,
++                                         TEST_ID_PROVIDER, params);
++    assert_non_null(test_ctx->tctx);
++
++    ret = sysdb_update_certmap(test_ctx->tctx->sysdb, certmap, false);
++    assert_int_equal(ret, EOK);
++
++    test_ctx->id_ctx = talloc_zero(test_ctx->tctx, struct sdap_id_ctx);
++    assert_non_null(test_ctx->id_ctx);
++
++    test_ctx->id_ctx->opts = talloc_zero(test_ctx->tctx, struct sdap_options);
++    assert_non_null(test_ctx->id_ctx->opts);
++
++    test_ctx->id_ctx->be = talloc_zero(test_ctx->tctx, struct be_ctx);
++    assert_non_null(test_ctx->id_ctx->be);
++    test_ctx->id_ctx->be->domain = test_ctx->tctx->dom;
++
++    *state = test_ctx;
++    return 0;
++}
++
++static int test_sysdb_teardown(void **state)
++{
++    struct certmap_test_ctx *test_ctx =
++        talloc_get_type(*state, struct certmap_test_ctx);
++
++    test_dom_suite_cleanup(TESTS_PATH, TEST_CONF_DB, TEST_DOM_NAME);
++    talloc_free(test_ctx->tctx);
++    assert_true(check_leaks_pop(test_ctx));
++    talloc_free(test_ctx);
++    assert_true(leak_check_teardown());
++    return 0;
++}
++
++static void test_sdap_certmap_init(void **state)
++{
++    int ret;
++    struct certmap_test_ctx *test_ctx = talloc_get_type(*state,
++                                                       struct certmap_test_ctx);
++
++    ret = sdap_init_certmap(test_ctx, test_ctx->id_ctx);
++    assert_int_equal(ret, EOK);
++
++    talloc_free(test_ctx->id_ctx->opts->sdap_certmap_ctx);
++}
++
++static void test_sdap_get_sss_certmap(void **state)
++{
++    int ret;
++    struct certmap_test_ctx *test_ctx = talloc_get_type(*state,
++                                                       struct certmap_test_ctx);
++    struct sss_certmap_ctx *sss_certmap_ctx;
++
++    sss_certmap_ctx = sdap_get_sss_certmap(NULL);
++    assert_null(sss_certmap_ctx);
++
++    ret = sdap_init_certmap(test_ctx, test_ctx->id_ctx);
++    assert_int_equal(ret, EOK);
++
++    sss_certmap_ctx = sdap_get_sss_certmap(
++                                      test_ctx->id_ctx->opts->sdap_certmap_ctx);
++    assert_non_null(sss_certmap_ctx);
++
++    talloc_free(test_ctx->id_ctx->opts->sdap_certmap_ctx);
++}
++
++static void test_sdap_certmap_init_twice(void **state)
++{
++    int ret;
++    struct certmap_test_ctx *test_ctx = talloc_get_type(*state,
++                                                       struct certmap_test_ctx);
++    struct sdap_certmap_ctx *sdap_certmap_ref;
++    struct sss_certmap_ctx *sss_certmap_ref;
++
++    ret = sdap_init_certmap(test_ctx, test_ctx->id_ctx);
++    assert_int_equal(ret, EOK);
++
++    sdap_certmap_ref = test_ctx->id_ctx->opts->sdap_certmap_ctx;
++    sss_certmap_ref = sdap_get_sss_certmap(sdap_certmap_ref);
++
++    ret = sdap_init_certmap(test_ctx, test_ctx->id_ctx);
++    assert_int_equal(ret, EOK);
++
++    assert_ptr_equal(sdap_certmap_ref,
++                     test_ctx->id_ctx->opts->sdap_certmap_ctx);
++    assert_ptr_not_equal(sss_certmap_ref,
++                         sdap_get_sss_certmap(sdap_certmap_ref));
++
++    talloc_free(test_ctx->id_ctx->opts->sdap_certmap_ctx);
++}
++
++
++static void test_sdap_setup_certmap(void **state)
++{
++    int ret;
++    struct certmap_test_ctx *test_ctx = talloc_get_type(*state,
++                                                       struct certmap_test_ctx);
++    struct sdap_certmap_ctx *sdap_certmap_ref;
++    struct sss_certmap_ctx *sss_certmap_ref;
++
++    ret = sdap_init_certmap(test_ctx, test_ctx->id_ctx);
++    assert_int_equal(ret, EOK);
++
++    sdap_certmap_ref = test_ctx->id_ctx->opts->sdap_certmap_ctx;
++    sss_certmap_ref = sdap_get_sss_certmap(sdap_certmap_ref);
++
++    ret = sdap_setup_certmap(NULL, NULL);
++    assert_int_equal(ret, EINVAL);
++    assert_ptr_equal(sdap_certmap_ref,
++                     test_ctx->id_ctx->opts->sdap_certmap_ctx);
++    assert_ptr_equal(sss_certmap_ref, sdap_get_sss_certmap(sdap_certmap_ref));
++
++    ret = sdap_setup_certmap(NULL, certmap);
++    assert_int_equal(ret, EINVAL);
++    assert_ptr_equal(sdap_certmap_ref,
++                     test_ctx->id_ctx->opts->sdap_certmap_ctx);
++    assert_ptr_equal(sss_certmap_ref, sdap_get_sss_certmap(sdap_certmap_ref));
++
++    ret = sdap_setup_certmap(sdap_certmap_ref, certmap);
++    assert_int_equal(ret, EOK);
++    assert_ptr_equal(sdap_certmap_ref,
++                     test_ctx->id_ctx->opts->sdap_certmap_ctx);
++    assert_ptr_not_equal(sss_certmap_ref,
++                         sdap_get_sss_certmap(sdap_certmap_ref));
++
++    talloc_free(test_ctx->id_ctx->opts->sdap_certmap_ctx);
++}
++
++int main(int argc, const char *argv[])
++{
++    int rv;
++    poptContext pc;
++    int opt;
++    struct poptOption long_options[] = {
++        POPT_AUTOHELP
++        SSSD_DEBUG_OPTS
++        POPT_TABLEEND
++    };
++
++    const struct CMUnitTest tests[] = {
++        cmocka_unit_test_setup_teardown(test_sdap_certmap_init,
++                                        test_sysdb_setup,
++                                        test_sysdb_teardown),
++        cmocka_unit_test_setup_teardown(test_sdap_get_sss_certmap,
++                                        test_sysdb_setup,
++                                        test_sysdb_teardown),
++        cmocka_unit_test_setup_teardown(test_sdap_certmap_init_twice,
++                                        test_sysdb_setup,
++                                        test_sysdb_teardown),
++        cmocka_unit_test_setup_teardown(test_sdap_setup_certmap,
++                                        test_sysdb_setup,
++                                        test_sysdb_teardown),
++    };
++
++    /* Set debug level to invalid value so we can deside if -d 0 was used. */
++    debug_level = SSSDBG_INVALID;
++
++    pc = poptGetContext(argv[0], argc, argv, long_options, 0);
++    while((opt = poptGetNextOpt(pc)) != -1) {
++        switch(opt) {
++        default:
++            fprintf(stderr, "\nInvalid option %s: %s\n\n",
++                    poptBadOption(pc, 0), poptStrerror(opt));
++            poptPrintUsage(pc, stderr, 0);
++            return 1;
++        }
++    }
++    poptFreeContext(pc);
++
++    DEBUG_CLI_INIT(debug_level);
++
++    tests_set_cwd();
++    rv = cmocka_run_group_tests(tests, NULL, NULL);
++
++    return rv;
++}
+-- 
+2.13.5
+
diff --git a/SOURCES/0194-certmap-make-sure-eku_oid_list-is-always-allocated.patch b/SOURCES/0194-certmap-make-sure-eku_oid_list-is-always-allocated.patch
new file mode 100644
index 0000000..c2f4d5d
--- /dev/null
+++ b/SOURCES/0194-certmap-make-sure-eku_oid_list-is-always-allocated.patch
@@ -0,0 +1,95 @@
+From 27ef368b4105f19382360fe62f944b36ca74adb7 Mon Sep 17 00:00:00 2001
+From: Sumit Bose <sbose@redhat.com>
+Date: Wed, 6 Sep 2017 12:20:25 +0200
+Subject: [PATCH 194/194] certmap: make sure eku_oid_list is always allocated
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+If there are only OIDs in a <EKU> part of a matching rule a NULL pointer
+dereference might occur.
+
+Related to https://pagure.io/SSSD/sssd/issue/3508
+
+Reviewed-by: Lukáš Slebodník <lslebodn@redhat.com>
+Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
+(cherry picked from commit f5a8cd60c6f377af1954b58f007d16cf3f6dc846)
+---
+ src/lib/certmap/sss_certmap_krb5_match.c | 21 ++++++++++++---------
+ src/tests/cmocka/test_certmap.c          | 17 +++++++++++++++++
+ 2 files changed, 29 insertions(+), 9 deletions(-)
+
+diff --git a/src/lib/certmap/sss_certmap_krb5_match.c b/src/lib/certmap/sss_certmap_krb5_match.c
+index e40f17b8ace46e61087e0a2fa570a362a84cead2..0a77ac225d73f3506e102fdbdc9084faa0f19cf0 100644
+--- a/src/lib/certmap/sss_certmap_krb5_match.c
++++ b/src/lib/certmap/sss_certmap_krb5_match.c
+@@ -179,19 +179,17 @@ static int parse_krb5_get_eku_value(TALLOC_CTX *mem_ctx,
+         goto done;
+     }
+ 
++    comp->eku_oid_list = talloc_zero_array(comp, const char *,
++                                           eku_list_size + 1);
++    if (comp->eku_oid_list == NULL) {
++        ret = ENOMEM;
++        goto done;
++    }
++
+     for (c = 0; eku_list[c] != NULL; c++) {
+         for (k = 0; ext_key_usage[k].name != NULL; k++) {
+ CM_DEBUG(ctx, "[%s][%s].", eku_list[c], ext_key_usage[k].name);
+             if (strcasecmp(eku_list[c], ext_key_usage[k].name) == 0) {
+-                if (comp->eku_oid_list == NULL) {
+-                    comp->eku_oid_list = talloc_zero_array(comp, const char *,
+-                                                           eku_list_size + 1);
+-                    if (comp->eku_oid_list == NULL) {
+-                        ret = ENOMEM;
+-                        goto done;
+-                    }
+-                }
+-
+                 comp->eku_oid_list[e] = talloc_strdup(comp->eku_oid_list,
+                                                       ext_key_usage[k].oid);
+                 if (comp->eku_oid_list[e] == NULL) {
+@@ -225,6 +223,11 @@ CM_DEBUG(ctx, "[%s][%s].", eku_list[c], ext_key_usage[k].name);
+         }
+     }
+ 
++    if (e == 0) {
++        talloc_free(comp->eku_oid_list);
++        comp->eku_oid_list = NULL;
++    }
++
+     ret = 0;
+ 
+ done:
+diff --git a/src/tests/cmocka/test_certmap.c b/src/tests/cmocka/test_certmap.c
+index c998443d086eaa72cc2a05c38ddfc5ba590a1ce7..e732bb214476943d0f723b318ab64d3b4156cace 100644
+--- a/src/tests/cmocka/test_certmap.c
++++ b/src/tests/cmocka/test_certmap.c
+@@ -445,6 +445,23 @@ static void test_sss_certmap_add_matching_rule(void **state)
+     assert_null(
+             ctx->prio_list->rule_list->parsed_match_rule->eku->eku_oid_list[3]);
+ 
++    ret = sss_certmap_add_rule(ctx, 96,
++                               "KRB5:<EKU>1.2.3",
++                               NULL, NULL);
++    assert_int_equal(ret, 0);
++    assert_non_null(ctx->prio_list);
++    assert_non_null(ctx->prio_list->rule_list);
++    assert_non_null(ctx->prio_list->rule_list->parsed_match_rule);
++    assert_int_equal(ctx->prio_list->rule_list->parsed_match_rule->r,
++                     relation_and);
++    assert_non_null(ctx->prio_list->rule_list->parsed_match_rule->eku);
++    assert_true(string_in_list("1.2.3",
++              discard_const(
++               ctx->prio_list->rule_list->parsed_match_rule->eku->eku_oid_list),
++              true));
++    assert_null(
++            ctx->prio_list->rule_list->parsed_match_rule->eku->eku_oid_list[1]);
++
+     /* SAN tests */
+     ret = sss_certmap_add_rule(ctx, 89, "KRB5:<SAN>abc", NULL, NULL);
+     assert_int_equal(ret, 0);
+-- 
+2.13.5
+
diff --git a/SOURCES/0195-cache_req-Look-for-name-attribute-also-in-nss_cmd_ge.patch b/SOURCES/0195-cache_req-Look-for-name-attribute-also-in-nss_cmd_ge.patch
new file mode 100644
index 0000000..2841962
--- /dev/null
+++ b/SOURCES/0195-cache_req-Look-for-name-attribute-also-in-nss_cmd_ge.patch
@@ -0,0 +1,62 @@
+From cf75c30a42059480eca4c352598ceb3760c27e46 Mon Sep 17 00:00:00 2001
+From: Lukas Slebodnik <lslebodn@redhat.com>
+Date: Mon, 21 Aug 2017 11:42:43 +0200
+Subject: [PATCH 195/195] cache_req: Look for name attribute also in
+ nss_cmd_getsidbyid
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+We always check negcache after getting data from backend since commit
+4c09cd008967c5c0ec358dc658ffc6fc1cef2697 because we usually do have a name
+in begging of requests "* by ID".
+
+We were not interested in name in request sid by id before. However, function
+cache_req_search_ncache_filter always expect name otherwise it returns
+ERR_INTERNAL.
+
+[sssd[nss]] [cache_req_set_plugin] (0x2000): CR #8: Setting "Object by ID" plugin
+[sssd[nss]] [cache_req_send] (0x0400): CR #8: New request 'Object by ID'
+[sssd[nss]] [cache_req_select_domains] (0x0400): CR #8: Performing a multi-domain search
+[sssd[nss]] [cache_req_search_domains] (0x0400): CR #8: Search will check the cache and check the data provider
+[sssd[nss]] [cache_req_validate_domain_type] (0x2000): Request type POSIX-only for domain sssdad2012r2.com type POSIX is valid
+[sssd[nss]] [cache_req_set_domain] (0x0400): CR #8: Using domain [sssdad2012r2.com]
+[sssd[nss]] [cache_req_search_send] (0x0400): CR #8: Looking up ID:233600513@sssdad2012r2.com
+[sssd[nss]] [cache_req_search_ncache] (0x0400): CR #8: Checking negative cache for [ID:233600513@sssdad2012r2.com]
+[sssd[nss]] [sss_ncache_check_str] (0x2000): Checking negative cache for [NCE/UID/sssdad2012r2.com/233600513]
+[sssd[nss]] [cache_req_search_ncache] (0x0400): CR #8: [ID:233600513@sssdad2012r2.com] is not present in negative cache
+[sssd[nss]] [cache_req_search_cache] (0x0400): CR #8: Looking up [ID:233600513@sssdad2012r2.com] in cache
+[sssd[nss]] [cache_req_search_send] (0x0400): CR #8: Returning [ID:233600513@sssdad2012r2.com] from cache
+[sssd[nss]] [cache_req_search_ncache_filter] (0x0400): CR #8: Filtering out results by negative cache
+[sssd[nss]] [cache_req_search_ncache_filter] (0x0020): CR #8: sss_get_name_from_msg() returned NULL, which should never happen in this scenario!
+[sssd[nss]] [cache_req_process_result] (0x0400): CR #8: Finished: Error 1432158209: Internal Error
+[sssd[nss]] [nss_protocol_done] (0x4000): Sending reply: error [1432158209]: Internal Error
+[sssd[nss]] [client_recv] (0x0200): Client disconnected!
+
+Resolves:
+https://pagure.io/SSSD/sssd/issue/3485
+
+Reviewed-by: Pavel Březina <pbrezina@redhat.com>
+(cherry picked from commit 2e72ababbbadda4c4036f99528460c1d595e0941)
+---
+ src/responder/common/cache_req/cache_req_data.c | 4 +++-
+ 1 file changed, 3 insertions(+), 1 deletion(-)
+
+diff --git a/src/responder/common/cache_req/cache_req_data.c b/src/responder/common/cache_req/cache_req_data.c
+index 5ab1493b81dbcd1529f1124a2bb1f99d3ae82281..3c365e2fe5826fd58c75f07b08193e5566db2563 100644
+--- a/src/responder/common/cache_req/cache_req_data.c
++++ b/src/responder/common/cache_req/cache_req_data.c
+@@ -26,7 +26,9 @@ static const char **
+ cache_req_data_create_attrs(TALLOC_CTX *mem_ctx,
+                             const char **requested)
+ {
+-    static const char *defattrs[] = { SYSDB_DEFAULT_ATTRS };
++    static const char *defattrs[] = { SYSDB_DEFAULT_ATTRS, SYSDB_NAME,
++                                      OVERRIDE_PREFIX SYSDB_NAME,
++                                      SYSDB_DEFAULT_OVERRIDE_NAME };
+     static size_t defnum = sizeof(defattrs) / sizeof(defattrs[0]);
+     const char **attrs;
+     size_t reqnum;
+-- 
+2.13.5
+
diff --git a/SOURCES/0196-sssd_client-add-mutex-protected-call-to-the-PAC-resp.patch b/SOURCES/0196-sssd_client-add-mutex-protected-call-to-the-PAC-resp.patch
new file mode 100644
index 0000000..2fe32d6
--- /dev/null
+++ b/SOURCES/0196-sssd_client-add-mutex-protected-call-to-the-PAC-resp.patch
@@ -0,0 +1,461 @@
+From daa59b79602cfeff81223a7461e18f513178c9d4 Mon Sep 17 00:00:00 2001
+From: Sumit Bose <sbose@redhat.com>
+Date: Mon, 18 Sep 2017 15:00:53 +0200
+Subject: [PATCH 196/196] sssd_client: add mutex protected call to the PAC
+ responder
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+SSSD's plugin for MIT Kerberos to send the PAC to the PAC responder
+currently uses sss_pac_make_request() which does not protect the
+communication with the PAC responder with a mutex as e.g. the NSS and
+PAM clients.
+
+If an application using threads loads this plugin via libkrb5 in
+different threads and is heavily processing Kerberos tickets with PACs
+chances are that two threads try to communicate with SSSD at once. In
+this case one of the threads will miss a reply and will wait for it
+until the default client timeout of 300s is passed.
+
+This patch adds a call which uses a mutex to protect the communication
+which will avoid the 300s delay mentioned above.
+
+Resolves:
+https://pagure.io/SSSD/sssd/issue/3518
+
+Reviewed-by: Lukáš Slebodník <lslebodn@redhat.com>
+Reviewed-by: Fabiano Fidêncio <fidencio@redhat.com>
+(cherry picked from commit 1f331476e7d33bb03cc35a2a9064ee1cc5bed6cf)
+---
+ Makefile.am                               |  16 ++++
+ src/sss_client/common.c                   |  30 +++++++
+ src/sss_client/sss_cli.h                  |   7 ++
+ src/sss_client/sss_pac_responder_client.c | 137 ++++++++++++++++++++++++++++++
+ src/sss_client/sssd_pac.c                 |   4 +-
+ src/tests/intg/Makefile.am                |   1 +
+ src/tests/intg/test_pac_responder.py      | 120 ++++++++++++++++++++++++++
+ 7 files changed, 313 insertions(+), 2 deletions(-)
+ create mode 100644 src/sss_client/sss_pac_responder_client.c
+ create mode 100644 src/tests/intg/test_pac_responder.py
+
+diff --git a/Makefile.am b/Makefile.am
+index 907c3256a154ebe2aae5a1667744e1dfbe8abaae..cdd517d50679b876814303fb7d6c63d49bcd8d38 100644
+--- a/Makefile.am
++++ b/Makefile.am
+@@ -3501,6 +3501,9 @@ endif
+ if BUILD_WITH_LIBCURL
+ noinst_PROGRAMS += tcurl-test-tool
+ endif
++if BUILD_PAC_RESPONDER
++    noinst_PROGRAMS += sssd_pac_test_client
++endif
+ 
+ if BUILD_AUTOFS
+ autofs_test_client_SOURCES = \
+@@ -4210,6 +4213,19 @@ sssd_pac_plugin_la_LDFLAGS = \
+     -avoid-version \
+     -module
+ 
++sssd_pac_test_client_SOURCES = \
++    src/sss_client/sss_pac_responder_client.c \
++    src/sss_client/common.c \
++    src/util/strtonum.c \
++    $(NULL)
++sssd_pac_test_client_CFLAGS = \
++    $(AM_CFLAGS) \
++    $(NULL)
++sssd_pac_test_client_LDADD = \
++    $(CLIENT_LIBS) \
++    -lpthread \
++    $(NULL)
++
+ # python[23] bindings
+ pysss_la_SOURCES = \
+     $(SSSD_TOOLS_OBJ) \
+diff --git a/src/sss_client/common.c b/src/sss_client/common.c
+index b7a5ed760ca379acdfd8f1d2bf95cee1aa271fd8..b527c046e2e3369934b4f9ea7efc1b52eb8c57ea 100644
+--- a/src/sss_client/common.c
++++ b/src/sss_client/common.c
+@@ -821,6 +821,22 @@ int sss_pac_make_request(enum sss_cli_command cmd,
+     }
+ }
+ 
++int sss_pac_make_request_with_lock(enum sss_cli_command cmd,
++                                   struct sss_cli_req_data *rd,
++                                   uint8_t **repbuf, size_t *replen,
++                                   int *errnop)
++{
++    int ret;
++
++    sss_pac_lock();
++
++    ret = sss_pac_make_request(cmd, rd, repbuf, replen, errnop);
++
++    sss_pac_unlock();
++
++    return ret;
++}
++
+ errno_t check_server_cred(int sockfd)
+ {
+ #ifdef HAVE_UCRED
+@@ -1079,6 +1095,8 @@ static struct sss_mutex sss_pam_mtx = { .mtx  = PTHREAD_MUTEX_INITIALIZER };
+ 
+ static struct sss_mutex sss_nss_mc_mtx = { .mtx  = PTHREAD_MUTEX_INITIALIZER };
+ 
++static struct sss_mutex sss_pac_mtx = { .mtx  = PTHREAD_MUTEX_INITIALIZER };
++
+ static void sss_mt_lock(struct sss_mutex *m)
+ {
+     pthread_mutex_lock(&m->mtx);
+@@ -1121,6 +1139,16 @@ void sss_nss_mc_unlock(void)
+     sss_mt_unlock(&sss_nss_mc_mtx);
+ }
+ 
++/* PAC mutex wrappers */
++void sss_pac_lock(void)
++{
++    sss_mt_lock(&sss_pac_mtx);
++}
++void sss_pac_unlock(void)
++{
++    sss_mt_unlock(&sss_pac_mtx);
++}
++
+ #else
+ 
+ /* sorry no mutexes available */
+@@ -1130,6 +1158,8 @@ void sss_pam_lock(void) { return; }
+ void sss_pam_unlock(void) { return; }
+ void sss_nss_mc_lock(void) { return; }
+ void sss_nss_mc_unlock(void) { return; }
++void sss_pac_lock(void) { return; }
++void sss_pac_unlock(void) { return; }
+ #endif
+ 
+ 
+diff --git a/src/sss_client/sss_cli.h b/src/sss_client/sss_cli.h
+index d4198407f2f86c6594aee6a2a43775e429692df0..337fe9803d2df3167cd2da77107dbd077f35a51b 100644
+--- a/src/sss_client/sss_cli.h
++++ b/src/sss_client/sss_cli.h
+@@ -585,6 +585,11 @@ int sss_pac_make_request(enum sss_cli_command cmd,
+                          uint8_t **repbuf, size_t *replen,
+                          int *errnop);
+ 
++int sss_pac_make_request_with_lock(enum sss_cli_command cmd,
++                                   struct sss_cli_req_data *rd,
++                                   uint8_t **repbuf, size_t *replen,
++                                   int *errnop);
++
+ int sss_sudo_make_request(enum sss_cli_command cmd,
+                           struct sss_cli_req_data *rd,
+                           uint8_t **repbuf, size_t *replen,
+@@ -634,6 +639,8 @@ void sss_pam_lock(void);
+ void sss_pam_unlock(void);
+ void sss_nss_mc_lock(void);
+ void sss_nss_mc_unlock(void);
++void sss_pac_lock(void);
++void sss_pac_unlock(void);
+ 
+ errno_t sss_readrep_copy_string(const char *in,
+                                 size_t *offset,
+diff --git a/src/sss_client/sss_pac_responder_client.c b/src/sss_client/sss_pac_responder_client.c
+new file mode 100644
+index 0000000000000000000000000000000000000000..9eb0cbea6175ee273b23d9a975529d85c02fc603
+--- /dev/null
++++ b/src/sss_client/sss_pac_responder_client.c
+@@ -0,0 +1,137 @@
++
++#include <stdio.h>
++#include <stdbool.h>
++#include <pthread.h>
++#include <pwd.h>
++#include <unistd.h>
++#include <sys/types.h>
++#include <errno.h>
++
++#include <unistd.h>
++#include <sys/syscall.h>
++
++#include "sss_client/sss_cli.h"
++
++const uint8_t pac[] = {
++0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10,
++0x02, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00,
++0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x68, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
++0x00, 0x0c, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x78, 0x02, 0x00, 0x00,
++0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0xb8,
++0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x10, 0x00,
++0x00, 0x00, 0xc8, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x10, 0x08,
++0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++0x00, 0x00, 0x02, 0x00, 0x30, 0xe3, 0xd6, 0x9e, 0x99, 0x2b, 0xd3, 0x01, 0xff,
++0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
++0xff, 0x7f, 0xe2, 0xf7, 0x8a, 0xaf, 0x00, 0x0f, 0xd0, 0x01, 0xe2, 0xb7, 0xf4,
++0xd9, 0xc9, 0x0f, 0xd0, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f,
++0x06, 0x00, 0x06, 0x00, 0x04, 0x00, 0x02, 0x00, 0x06, 0x00, 0x06, 0x00, 0x08,
++0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x02, 0x00, 0x00, 0x00,
++0x00, 0x00, 0x10, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0x00, 0x02,
++0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x02, 0x00, 0x45, 0x02, 0x00, 0x00,
++0x50, 0x04, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x1c,
++0x00, 0x02, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0x14,
++0x00, 0x20, 0x00, 0x02, 0x00, 0x04, 0x00, 0x06, 0x00, 0x24, 0x00, 0x02, 0x00,
++0x28, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10,
++0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x02, 0x00,
++0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
++0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x74, 0x00,
++0x75, 0x00, 0x31, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++0x00, 0x03, 0x00, 0x00, 0x00, 0x74, 0x00, 0x20, 0x00, 0x75, 0x00, 0x00, 0x00,
++0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,
++0xfd, 0xa2, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00, 0x07,
++0x00, 0x00, 0x00, 0x5c, 0x04, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x56, 0x04,
++0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x89, 0xa6, 0x00, 0x00, 0x07, 0x00, 0x00,
++0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00,
++0x41, 0x00, 0x44, 0x00, 0x2d, 0x00, 0x53, 0x00, 0x45, 0x00, 0x52, 0x00, 0x56,
++0x00, 0x45, 0x00, 0x52, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00,
++0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x41, 0x00, 0x44, 0x00, 0x04, 0x00, 0x00,
++0x00, 0x01, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x15, 0x00, 0x00, 0x00,
++0xf8, 0x12, 0x13, 0xdc, 0x47, 0xf3, 0x1c, 0x76, 0x47, 0x2f, 0x2e, 0xd7, 0x02,
++0x00, 0x00, 0x00, 0x30, 0x00, 0x02, 0x00, 0x07, 0x00, 0x00, 0x00, 0x34, 0x00,
++0x02, 0x00, 0x07, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, 0x05, 0x00,
++0x00, 0x00, 0x00, 0x00, 0x05, 0x15, 0x00, 0x00, 0x00, 0x29, 0xc9, 0x4f, 0xd9,
++0xc2, 0x3c, 0xc3, 0x78, 0x36, 0x55, 0x87, 0xf8, 0x54, 0x04, 0x00, 0x00, 0x05,
++0x00, 0x00, 0x00, 0x01, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x15, 0x00,
++0x00, 0x00, 0x25, 0xe1, 0xff, 0x1c, 0xf7, 0x87, 0x6b, 0x2c, 0x25, 0xd2, 0x0c,
++0xe3, 0xf2, 0x03, 0x00, 0x00, 0x00, 0x2c, 0x29, 0x89, 0x65, 0x2d, 0xd3, 0x01,
++0x06, 0x00, 0x74, 0x00, 0x75, 0x00, 0x31, 0x00, 0x20, 0x00, 0x10, 0x00, 0x10,
++0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x74, 0x00,
++0x75, 0x00, 0x31, 0x00, 0x74, 0x00, 0x65, 0x00, 0x73, 0x00, 0x74, 0x00, 0x40,
++0x00, 0x61, 0x00, 0x64, 0x00, 0x2e, 0x00, 0x64, 0x00, 0x65, 0x00, 0x76, 0x00,
++0x65, 0x00, 0x6c, 0x00, 0x41, 0x00, 0x44, 0x00, 0x2e, 0x00, 0x44, 0x00, 0x45,
++0x00, 0x56, 0x00, 0x45, 0x00, 0x4c, 0x00, 0x10, 0x00, 0x00, 0x00, 0x76, 0x8e,
++0x25, 0x32, 0x7c, 0x85, 0x00, 0x32, 0xac, 0x8f, 0x02, 0x2c, 0x10, 0x00, 0x00,
++0x00, 0x6b, 0xe8, 0x51, 0x03, 0x30, 0xed, 0xca, 0x7d, 0xe2, 0x12, 0xa5, 0xde};
++
++enum nss_status _nss_sss_getpwuid_r(uid_t uid, struct passwd *result,
++                                    char *buffer, size_t buflen, int *errnop);
++static void *pac_client(void *arg)
++{
++    struct sss_cli_req_data sss_data = { sizeof(pac), pac };
++    int errnop = -1;
++    int ret;
++    size_t c;
++
++    fprintf(stderr, "[%ld][%d][%ld][%s] started\n", time(NULL), getpid(),
++                                                    syscall(SYS_gettid),
++                                                    (char *) arg);
++    for (c = 0; c < 1000; c++) {
++        /* sss_pac_make_request() does not protect the client's file
++         * descriptor to the PAC responder. With this one thread will miss a
++         * reply for a SSS_GET_VERSION request and will wait until
++         * SSS_CLI_SOCKET_TIMEOUT is passed.
++
++        ret = sss_pac_make_request(SSS_PAC_ADD_PAC_USER, &sss_data,
++                                   NULL, NULL, &errnop);
++         */
++        ret = sss_pac_make_request_with_lock(SSS_PAC_ADD_PAC_USER, &sss_data,
++                                             NULL, NULL, &errnop);
++        if (ret != NSS_STATUS_SUCCESS
++                && !(ret == NSS_STATUS_UNAVAIL && errnop != ECONNREFUSED)) {
++                /* NSS_STATUS_UNAVAIL is returned if the PAC responder rejects
++                 * the request which is ok becasue the client is waiting for a
++                 * response here as well. Only errnop == ECONNREFUSED should
++                 * be treated as error becasue this means that the PAC
++                 * responder is not running. */
++            fprintf(stderr, "pac: [%s][%d][%d]\n", (char *)arg, ret, errnop);
++            return ((void *)((uintptr_t)("X")));
++        }
++    }
++
++    fprintf(stderr, "[%ld][%s] done\n", time(NULL),(char *) arg);
++    return NULL;
++}
++
++int main(void)
++{
++    pthread_t thread1;
++    pthread_t thread2;
++    int ret;
++    void *t_ret;
++
++    pthread_create(&thread1, NULL, pac_client,
++                   ((void *)((uintptr_t)("Thread 1"))));
++    pthread_create(&thread2, NULL, pac_client,
++                   ((void *)((uintptr_t)("Thread 2"))));
++
++    ret = pthread_join(thread1, &t_ret);
++    if (ret != 0 || t_ret != NULL) {
++        fprintf(stderr, "Thread 1 failed.\n");
++        return EIO;
++    }
++
++    ret = pthread_join(thread2, &t_ret);
++    if (ret != 0 || t_ret != NULL) {
++        fprintf(stderr, "Thread 1 failed.\n");
++        return EIO;
++    }
++
++    return 0;
++}
+diff --git a/src/sss_client/sssd_pac.c b/src/sss_client/sssd_pac.c
+index 1d98e38826b36aed199b32880a7e27de905a4592..8444834a7f148787e847f5e8e21186c8701b2de7 100644
+--- a/src/sss_client/sssd_pac.c
++++ b/src/sss_client/sssd_pac.c
+@@ -169,8 +169,8 @@ static krb5_error_code sssdpac_verify(krb5_context kcontext,
+     sss_data.len = sssdctx->data.length;
+     sss_data.data = sssdctx->data.data;
+ 
+-    ret = sss_pac_make_request(SSS_PAC_ADD_PAC_USER, &sss_data,
+-                               NULL, NULL, &errnop);
++    ret = sss_pac_make_request_with_lock(SSS_PAC_ADD_PAC_USER, &sss_data,
++                                         NULL, NULL, &errnop);
+     if (ret != 0) {
+         /* Ignore the error */
+     }
+diff --git a/src/tests/intg/Makefile.am b/src/tests/intg/Makefile.am
+index 8566106e9017a8d3c9e7a3898a3a886e2966e346..0af7c62ca243822d919619f3d0ebc852a317efc4 100644
+--- a/src/tests/intg/Makefile.am
++++ b/src/tests/intg/Makefile.am
+@@ -29,6 +29,7 @@ dist_noinst_DATA = \
+     kdc.py \
+     krb5utils.py \
+     test_kcm.py \
++    test_pac_responder.py \
+     $(NULL)
+ 
+ config.py: config.py.m4
+diff --git a/src/tests/intg/test_pac_responder.py b/src/tests/intg/test_pac_responder.py
+new file mode 100644
+index 0000000000000000000000000000000000000000..4354a5d78da6a6627a27d0ca85c8a1d47419cedf
+--- /dev/null
++++ b/src/tests/intg/test_pac_responder.py
+@@ -0,0 +1,120 @@
++#
++# SSSD PAC responder tests
++#
++# Copyright (c) 2017 Red Hat, Inc.
++# Author: Sumit Bose <sbose@redhat.com>
++#
++# This is free software; you can redistribute it and/or modify it
++# under the terms of the GNU General Public License as published by
++# the Free Software Foundation; version 2 only
++#
++# This program is distributed in the hope that it will be useful, but
++# WITHOUT ANY WARRANTY; without even the implied warranty of
++# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
++# General Public License for more details.
++#
++# You should have received a copy of the GNU General Public License
++# along with this program.  If not, see <http://www.gnu.org/licenses/>.
++#
++import os
++import stat
++import time
++import config
++import signal
++import subprocess
++import pytest
++from util import unindent
++
++
++def stop_sssd():
++    with open(config.PIDFILE_PATH, "r") as pid_file:
++        pid = int(pid_file.read())
++    os.kill(pid, signal.SIGTERM)
++    while True:
++        try:
++            os.kill(pid, signal.SIGCONT)
++        except:
++            break
++        time.sleep(1)
++
++
++def create_conf_fixture(request, contents):
++    """Generate sssd.conf and add teardown for removing it"""
++    conf = open(config.CONF_PATH, "w")
++    conf.write(contents)
++    conf.close()
++    os.chmod(config.CONF_PATH, stat.S_IRUSR | stat.S_IWUSR)
++    request.addfinalizer(lambda: os.unlink(config.CONF_PATH))
++
++
++def create_sssd_fixture(request):
++    """Start sssd and add teardown for stopping it and removing state"""
++    if subprocess.call(["sssd", "-D", "-f"]) != 0:
++        raise Exception("sssd start failed")
++
++    def teardown():
++        try:
++            stop_sssd()
++        except:
++            pass
++        for path in os.listdir(config.DB_PATH):
++            os.unlink(config.DB_PATH + "/" + path)
++        for path in os.listdir(config.MCACHE_PATH):
++            os.unlink(config.MCACHE_PATH + "/" + path)
++    request.addfinalizer(teardown)
++
++
++@pytest.fixture
++def local_domain_only(request):
++    conf = unindent("""\
++        [sssd]
++        domains = LOCAL
++        services = nss, pac
++
++        [nss]
++        memcache_timeout = 0
++
++        [domain/LOCAL]
++        id_provider = local
++        min_id = 10000
++        max_id = 20000
++    """).format(**locals())
++    create_conf_fixture(request, conf)
++    create_sssd_fixture(request)
++    return None
++
++
++@pytest.fixture
++def sssd_pac_test_client(request):
++    path = os.path.join(config.ABS_BUILDDIR,
++                        "..", "..", "..", "sssd_pac_test_client")
++    if os.access(path, os.X_OK):
++        return path
++
++    return None
++
++
++def timeout_handler(signum, frame):
++    raise Exception("Timeout")
++
++
++def test_multithreaded_pac_client(local_domain_only, sssd_pac_test_client):
++    """
++    Test for ticket
++    https://pagure.io/SSSD/sssd/issue/3518
++    """
++
++    if not sssd_pac_test_client:
++        pytest.skip("The sssd_pac_test_client is not available, skipping test")
++
++    signal.signal(signal.SIGALRM, timeout_handler)
++    signal.alarm(10)
++
++    try:
++        subprocess.check_call(sssd_pac_test_client)
++    except:
++        # cancel alarm
++        signal.alarm(0)
++        raise Exception("sssd_pac_test_client failed")
++
++    signal.alarm(0)
+-- 
+2.13.5
+
diff --git a/SOURCES/0197-sysdb-sanitize-search-filter-input.patch b/SOURCES/0197-sysdb-sanitize-search-filter-input.patch
new file mode 100644
index 0000000..0b2134d
--- /dev/null
+++ b/SOURCES/0197-sysdb-sanitize-search-filter-input.patch
@@ -0,0 +1,140 @@
+From 944295de4cf4eeba75d4f6bd476a4f59743e1813 Mon Sep 17 00:00:00 2001
+From: Sumit Bose <sbose@redhat.com>
+Date: Thu, 5 Oct 2017 11:07:38 +0200
+Subject: [PATCH 197/197] sysdb: sanitize search filter input
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+This patch sanitizes the input for sysdb searches by UPN/email, SID and
+UUID.
+
+This security issue was assigned CVE-2017-12173
+
+Reviewed-by: Lukáš Slebodník <lslebodn@redhat.com>
+Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
+(cherry picked from commit 1f2662c8f97c9c0fa250055d4b6750abfc6d0835)
+---
+ src/db/sysdb_ops.c      | 43 +++++++++++++++++++++++++++++++++++--------
+ src/tests/sysdb-tests.c |  7 +++++++
+ 2 files changed, 42 insertions(+), 8 deletions(-)
+
+diff --git a/src/db/sysdb_ops.c b/src/db/sysdb_ops.c
+index 7ca6575ce75dab7805236c9f48dbf28a2f3946d2..408af9f389edbe0aff0fb8b96f49f0c4463a620a 100644
+--- a/src/db/sysdb_ops.c
++++ b/src/db/sysdb_ops.c
+@@ -601,6 +601,7 @@ int sysdb_search_user_by_upn_res(TALLOC_CTX *mem_ctx,
+     int ret;
+     const char *def_attrs[] = { SYSDB_NAME, SYSDB_UPN, SYSDB_CANONICAL_UPN,
+                                 SYSDB_USER_EMAIL, NULL };
++    char *sanitized;
+ 
+     tmp_ctx = talloc_new(NULL);
+     if (tmp_ctx == NULL) {
+@@ -608,6 +609,12 @@ int sysdb_search_user_by_upn_res(TALLOC_CTX *mem_ctx,
+         goto done;
+     }
+ 
++    ret = sss_filter_sanitize(tmp_ctx, upn, &sanitized);
++    if (ret != EOK) {
++        DEBUG(SSSDBG_OP_FAILURE, "sss_filter_sanitize failed.\n");
++        goto done;
++    }
++
+     if (domain_scope == true) {
+         base_dn = sysdb_user_base_dn(tmp_ctx, domain);
+     } else {
+@@ -620,7 +627,7 @@ int sysdb_search_user_by_upn_res(TALLOC_CTX *mem_ctx,
+ 
+     ret = ldb_search(domain->sysdb->ldb, tmp_ctx, &res,
+                      base_dn, LDB_SCOPE_SUBTREE, attrs ? attrs : def_attrs,
+-                     SYSDB_PWUPN_FILTER, upn, upn, upn);
++                     SYSDB_PWUPN_FILTER, sanitized, sanitized, sanitized);
+     if (ret != EOK) {
+         ret = sysdb_error_to_errno(ret);
+         goto done;
+@@ -4757,17 +4764,31 @@ static errno_t sysdb_search_object_by_str_attr(TALLOC_CTX *mem_ctx,
+                                                bool expect_only_one_result,
+                                                struct ldb_result **_res)
+ {
+-    char *filter;
++    char *filter = NULL;
+     errno_t ret;
++    char *sanitized = NULL;
+ 
+-    filter = talloc_asprintf(NULL, filter_tmpl, str);
++    if (str == NULL) {
++        return EINVAL;
++    }
++
++    ret = sss_filter_sanitize(NULL, str, &sanitized);
++    if (ret != EOK || sanitized == NULL) {
++        DEBUG(SSSDBG_OP_FAILURE, "sss_filter_sanitize failed.\n");
++        goto done;
++    }
++
++    filter = talloc_asprintf(NULL, filter_tmpl, sanitized);
+     if (filter == NULL) {
+-        return ENOMEM;
++        ret = ENOMEM;
++        goto done;
+     }
+ 
+     ret = sysdb_search_object_attr(mem_ctx, domain, filter, attrs,
+                                    expect_only_one_result, _res);
+ 
++done:
++    talloc_free(sanitized);
+     talloc_free(filter);
+     return ret;
+ }
+@@ -4856,7 +4877,8 @@ errno_t sysdb_search_object_by_cert(TALLOC_CTX *mem_ctx,
+                                     struct ldb_result **res)
+ {
+     int ret;
+-    char *user_filter;
++    char *user_filter = NULL;
++    char *filter = NULL;
+ 
+     ret = sss_cert_derb64_to_ldap_filter(mem_ctx, cert, SYSDB_USER_MAPPED_CERT,
+                                          NULL, NULL, &user_filter);
+@@ -4865,10 +4887,15 @@ errno_t sysdb_search_object_by_cert(TALLOC_CTX *mem_ctx,
+         return ret;
+     }
+ 
+-    ret = sysdb_search_object_by_str_attr(mem_ctx, domain,
+-                                          SYSDB_USER_CERT_FILTER,
+-                                          user_filter, attrs, false, res);
++    filter = talloc_asprintf(NULL, SYSDB_USER_CERT_FILTER, user_filter);
+     talloc_free(user_filter);
++    if (filter == NULL) {
++        return ENOMEM;
++    }
++
++    ret = sysdb_search_object_attr(mem_ctx, domain, filter, attrs, false, res);
++
++    talloc_free(filter);
+ 
+     return ret;
+ }
+diff --git a/src/tests/sysdb-tests.c b/src/tests/sysdb-tests.c
+index 6ec82ce4ca5c4f918bc9f3144c21f33b270ea47e..588eb9ef02033ec061b4187964fe562da84e86c8 100644
+--- a/src/tests/sysdb-tests.c
++++ b/src/tests/sysdb-tests.c
+@@ -6444,6 +6444,13 @@ START_TEST(test_upn_basic)
+     fail_unless(strcmp(str, UPN_PRINC) == 0,
+                 "Expected [%s], got [%s].", UPN_PRINC, str);
+ 
++    /* check if input is sanitized */
++    ret = sysdb_search_user_by_upn(test_ctx, test_ctx->domain, false,
++                                   "abc@def.ghi)(name="UPN_USER_NAME")(abc=xyz",
++                                   NULL, &msg);
++    fail_unless(ret == ENOENT,
++                "sysdb_search_user_by_upn failed with un-sanitized input.");
++
+     talloc_free(test_ctx);
+ }
+ END_TEST
+-- 
+2.13.5
+
diff --git a/SOURCES/0198-ipa-make-sure-view-name-is-initialized-at-startup.patch b/SOURCES/0198-ipa-make-sure-view-name-is-initialized-at-startup.patch
new file mode 100644
index 0000000..25422d6
--- /dev/null
+++ b/SOURCES/0198-ipa-make-sure-view-name-is-initialized-at-startup.patch
@@ -0,0 +1,60 @@
+From 2d39f46386fb36b5a68f41644ce22c15bf6ccb67 Mon Sep 17 00:00:00 2001
+From: Sumit Bose <sbose@redhat.com>
+Date: Thu, 31 Aug 2017 22:30:25 +0200
+Subject: [PATCH 198/199] ipa: make sure view name is initialized at startup
+
+sysdb_master_domain_update() can only set the view name properly if it was not
+set before but it might be called multiple times before the view name is
+available if the cache is empty. Since ipa_apply_view() keeps track if
+the view name was already set at startup or not the name can safely be
+cleaned here before sysdb_master_domain_update() is called.
+
+Resolves:
+https://pagure.io/SSSD/sssd/issue/3501
+
+Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
+(cherry picked from commit f00591a4615720640cf01b1c408315b57dd397dc)
+---
+ src/providers/ipa/ipa_subdomains.c | 19 ++++++++++++++++++-
+ 1 file changed, 18 insertions(+), 1 deletion(-)
+
+diff --git a/src/providers/ipa/ipa_subdomains.c b/src/providers/ipa/ipa_subdomains.c
+index 6f0ff50bde234f72d62f43635d9a787316c78430..0cb4d405e45689e9548ad3652e7260f2265bd1fe 100644
+--- a/src/providers/ipa/ipa_subdomains.c
++++ b/src/providers/ipa/ipa_subdomains.c
+@@ -739,6 +739,18 @@ done:
+     return ret;
+ }
+ 
++static void clean_view_name(struct sss_domain_info *domain)
++{
++    struct sss_domain_info *dom = domain;
++
++    while (dom) {
++        dom->has_views = false;
++        talloc_free(discard_const(dom->view_name));
++        dom->view_name = NULL;
++        dom = get_next_domain(dom, SSS_GND_DESCEND);
++    }
++}
++
+ static errno_t ipa_apply_view(struct sss_domain_info *domain,
+                               struct ipa_id_ctx *ipa_id_ctx,
+                               const char *view_name,
+@@ -831,7 +843,12 @@ static errno_t ipa_apply_view(struct sss_domain_info *domain,
+     }
+ 
+     if (!read_at_init) {
+-        /* refresh view data of all domains at startup */
++        /* refresh view data of all domains at startup, since
++         * sysdb_master_domain_update and sysdb_update_subdomains might have
++         * been called earlier without the proper view name the name is
++         * cleaned here before the calls. This is acceptable because this is
++         * the initial setup (!read_at_init).  */
++        clean_view_name(domain);
+         ret = sysdb_master_domain_update(domain);
+         if (ret != EOK) {
+             DEBUG(SSSDBG_OP_FAILURE, "sysdb_master_domain_update failed "
+-- 
+2.13.5
+
diff --git a/SOURCES/0199-CACHE_REQ-Copy-the-cr_domain-list-for-each-request.patch b/SOURCES/0199-CACHE_REQ-Copy-the-cr_domain-list-for-each-request.patch
new file mode 100644
index 0000000..ba0c504
--- /dev/null
+++ b/SOURCES/0199-CACHE_REQ-Copy-the-cr_domain-list-for-each-request.patch
@@ -0,0 +1,142 @@
+From a1634a3c4b977364fd7612efa3ee21872a8d578e Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Fabiano=20Fid=C3=AAncio?= <fidencio@redhat.com>
+Date: Fri, 20 Oct 2017 09:26:43 +0200
+Subject: [PATCH 199/199] CACHE_REQ: Copy the cr_domain list for each request
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Let's copy the cr_domain list for each request as this list may be
+free'd due to a refresh domains request.
+
+Resolves: https://pagure.io/SSSD/sssd/issue/3551
+
+Signed-off-by: Fabiano Fidêncio <fidencio@redhat.com>
+
+Reviewed-by: Pavel Březina <pbrezina@redhat.com>
+(cherry picked from commit 0f44eefe2ce75a0814c8688495477f6c57f3d39a)
+---
+ src/responder/common/cache_req/cache_req.c        | 14 +++++++--
+ src/responder/common/cache_req/cache_req_domain.c | 38 +++++++++++++++++++++++
+ src/responder/common/cache_req/cache_req_domain.h |  5 +++
+ 3 files changed, 55 insertions(+), 2 deletions(-)
+
+diff --git a/src/responder/common/cache_req/cache_req.c b/src/responder/common/cache_req/cache_req.c
+index 7d77eb7dd72a7ccf3d687eee8f746ab84176b487..83eab8ed2de0b5c7d25306d853aadc9b53cb4842 100644
+--- a/src/responder/common/cache_req/cache_req.c
++++ b/src/responder/common/cache_req/cache_req.c
+@@ -688,6 +688,7 @@ struct cache_req_state {
+     const char *domain_name;
+ 
+     /* work data */
++    struct cache_req_domain *cr_domains;
+     struct cache_req_result **results;
+     size_t num_results;
+     bool first_iteration;
+@@ -940,6 +941,7 @@ static errno_t cache_req_select_domains(struct tevent_req *req,
+     bool bypass_cache;
+     bool bypass_dp;
+     bool search;
++    errno_t ret;
+ 
+     state = tevent_req_data(req, struct cache_req_state);
+ 
+@@ -951,12 +953,20 @@ static errno_t cache_req_select_domains(struct tevent_req *req,
+         return EOK;
+     }
+ 
++    ret = cache_req_domain_copy_cr_domains(state,
++                                           state->cr->rctx->cr_domains,
++                                           &state->cr_domains);
++    if (ret != EOK) {
++        DEBUG(SSSDBG_CRIT_FAILURE, "cache_req_copy_cr_domains() failed\n");
++        return EINVAL;
++    }
++
+     if (domain_name != NULL) {
+         CACHE_REQ_DEBUG(SSSDBG_TRACE_FUNC, state->cr,
+                         "Performing a single domain search\n");
+ 
+         cr_domain = cache_req_domain_get_domain_by_name(
+-                                    state->cr->rctx->cr_domains, domain_name);
++                                    state->cr_domains, domain_name);
+         if (cr_domain == NULL) {
+             return ERR_DOMAIN_NOT_FOUND;
+         }
+@@ -965,7 +975,7 @@ static errno_t cache_req_select_domains(struct tevent_req *req,
+         CACHE_REQ_DEBUG(SSSDBG_TRACE_FUNC, state->cr,
+                         "Performing a multi-domain search\n");
+ 
+-        cr_domain = state->cr->rctx->cr_domains;
++        cr_domain = state->cr_domains;
+         check_next = true;
+     }
+ 
+diff --git a/src/responder/common/cache_req/cache_req_domain.c b/src/responder/common/cache_req/cache_req_domain.c
+index c2b5abb74f3bd3d5055f29a4523f29b05feb2014..8c9f155303b174f16b884eb66ba1c88a0256719d 100644
+--- a/src/responder/common/cache_req/cache_req_domain.c
++++ b/src/responder/common/cache_req/cache_req_domain.c
+@@ -47,6 +47,44 @@ cache_req_domain_get_domain_by_name(struct cache_req_domain *domains,
+     return ret;
+ }
+ 
++errno_t
++cache_req_domain_copy_cr_domains(TALLOC_CTX *mem_ctx,
++                                 struct cache_req_domain *src,
++                                 struct cache_req_domain **_dest)
++{
++    struct cache_req_domain *cr_domains = NULL;
++    struct cache_req_domain *cr_domain;
++    struct cache_req_domain *iter;
++    errno_t ret;
++
++    if (src == NULL) {
++        return EINVAL;
++    }
++
++    DLIST_FOR_EACH(iter, src) {
++        cr_domain = talloc_zero(mem_ctx, struct cache_req_domain);
++        if (cr_domain == NULL) {
++            ret = ENOMEM;
++            goto done;
++        }
++
++        cr_domain->domain = iter->domain;
++        cr_domain->fqnames = iter->fqnames;
++
++        DLIST_ADD_END(cr_domains, cr_domain, struct cache_req_domain *);
++    }
++
++    *_dest = cr_domains;
++    ret = EOK;
++
++done:
++    if (ret != EOK) {
++        cache_req_domain_list_zfree(&cr_domains);
++    }
++
++    return ret;
++}
++
+ void cache_req_domain_list_zfree(struct cache_req_domain **cr_domains)
+ {
+     struct cache_req_domain *p, *q, *r;
+diff --git a/src/responder/common/cache_req/cache_req_domain.h b/src/responder/common/cache_req/cache_req_domain.h
+index 3780a5d8d88d76e100738d28d1dd0e697edf5eae..ebdc71dd635d5d8a5d06e30e96c5d4101b6d98bf 100644
+--- a/src/responder/common/cache_req/cache_req_domain.h
++++ b/src/responder/common/cache_req/cache_req_domain.h
+@@ -50,6 +50,11 @@ cache_req_domain_new_list_from_domain_resolution_order(
+                                         const char *domain_resolution_order,
+                                         struct cache_req_domain **_cr_domains);
+ 
++errno_t
++cache_req_domain_copy_cr_domains(TALLOC_CTX *mem_ctx,
++                                 struct cache_req_domain *src,
++                                 struct cache_req_domain **_dest);
++
+ void cache_req_domain_list_zfree(struct cache_req_domain **cr_domains);
+ 
+ 
+-- 
+2.13.5
+
diff --git a/SOURCES/0999-NOUPSTREAM-Default-to-root-if-sssd-user-is-not-spec b/SOURCES/0999-NOUPSTREAM-Default-to-root-if-sssd-user-is-not-spec
new file mode 100644
index 0000000..f24afe3
--- /dev/null
+++ b/SOURCES/0999-NOUPSTREAM-Default-to-root-if-sssd-user-is-not-spec
@@ -0,0 +1,26 @@
+From 8d38a4b28ab7af15406b244910f369ba1aff02db Mon Sep 17 00:00:00 2001
+From: Jakub Hrozek <jhrozek@redhat.com>
+Date: Thu, 30 Oct 2014 15:59:17 +0100
+Subject: [PATCH 93/93] NOUPSTREAM: Default to root if sssd user is not
+ specified
+
+---
+ src/monitor/monitor.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/src/monitor/monitor.c b/src/monitor/monitor.c
+index 0dea327213a1ad04b6f69c0ffb0fb87254420796..20b4aef4ee94fd42de1585d7d7c2e01ea01845ac 100644
+--- a/src/monitor/monitor.c
++++ b/src/monitor/monitor.c
+@@ -925,7 +925,7 @@ static int get_service_user(struct mt_ctx *ctx)
+ 
+     ret = confdb_get_string(ctx->cdb, ctx, CONFDB_MONITOR_CONF_ENTRY,
+                             CONFDB_MONITOR_USER_RUNAS,
+-                            SSSD_USER, &user_str);
++                            "root", &user_str);
+     if (ret != EOK) {
+         DEBUG(SSSDBG_FATAL_FAILURE, "Failed to get the user to run as\n");
+         return ret;
+-- 
+1.9.3
+
diff --git a/SPECS/sssd.spec b/SPECS/sssd.spec
new file mode 100644
index 0000000..b6134a5
--- /dev/null
+++ b/SPECS/sssd.spec
@@ -0,0 +1,3805 @@
+%global rhel7_minor %(%{__grep} -o "7.[0-9]*" /etc/redhat-release |%{__sed} -s 's/7.//')
+
+# we don't want to provide private python extension libs
+%define __provides_exclude_from %{python_sitearch}/.*\.so$|%{_libdir}/%{name}/modules/libwbclient.so.*$
+%define _hardened_build 1
+
+    %global install_pcscd_polkit_rule 1
+
+# Determine the location of the LDB modules directory
+%global ldb_modulesdir %(pkg-config --variable=modulesdir ldb)
+%global ldb_version 1.1.17
+
+
+%if (0%{?fedora} || 0%{?rhel} >= 7)
+    %global with_cifs_utils_plugin 1
+%else
+    %global with_cifs_utils_plugin_option --disable-cifs-idmap-plugin
+%endif
+
+    %global with_krb5_localauth_plugin 1
+
+%global libwbc_alternatives_version 0.13
+%global libwbc_lib_version %{libwbc_alternatives_version}.0
+%global libwbc_alternatives_suffix %nil
+%if 0%{?__isa_bits} == 64
+%global libwbc_alternatives_suffix -64
+%endif
+
+%global enable_systemtap 1
+%if (0%{?enable_systemtap} == 1)
+    %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.15.2
+Release: 50%{?dist}.8
+Group: Applications/System
+Summary: System Security Services Daemon
+License: GPLv3+
+URL: https://pagure.io/SSSD/sssd/
+Source0: https://releases.pagure.org/SSSD/sssd/sssd-%{version}.tar.gz
+BuildRoot: %(mktemp -ud %{_tmppath}/%{name}-%{version}-%{release}-XXXXXX)
+
+### Patches ###
+Patch0001: 0001-MAN-Mention-sssd-secrets-in-SEE-ALSO-section.patch
+Patch0002: 0002-split_on_separator-move-to-a-separate-file.patch
+Patch0003: 0003-util-move-string_in_list-to-util_ext.patch
+Patch0004: 0004-certmap-add-new-library-libsss_certmap.patch
+Patch0005: 0005-certmap-add-placeholder-for-OpenSSL-implementation.patch
+Patch0006: 0006-sysdb-add-sysdb_attrs_copy.patch
+Patch0007: 0007-sdap_get_users_send-new-argument-mapped_attrs.patch
+Patch0008: 0008-LDAP-always-store-the-certificate-from-the-request.patch
+Patch0009: 0009-sss_cert_derb64_to_ldap_filter-add-sss_certmap-suppo.patch
+Patch0010: 0010-sysdb-add-certmap-related-calls.patch
+Patch0011: 0011-IPA-add-certmap-support.patch
+Patch0012: 0012-nss-idmap-add-sss_nss_getlistbycert.patch
+Patch0013: 0013-nss-allow-larger-buffer-for-certificate-based-reques.patch
+Patch0014: 0014-IPA-Add-s2n-request-to-string-function.patch
+Patch0015: 0015-IPA-Enhance-debug-logging-for-ipa-s2n-operations.patch
+Patch0016: 0016-UTIL-iobuf-Make-input-parameter-for-the-readonly-ope.patch
+Patch0017: 0017-UTIL-Fix-a-typo-in-the-tcurl-test-tool.patch
+Patch0018: 0018-UTIL-Add-SAFEALIGN_COPY_UINT8_CHECK.patch
+Patch0019: 0019-UTIL-Add-utility-macro-cli_creds_get_gid.patch
+Patch0020: 0020-UTIL-Add-type-specific-getsetters-to-sss_iobuf.patch
+Patch0021: 0021-UTIL-krb5-principal-un-marshalling.patch
+Patch0022: 0022-KCM-Initial-responder-build-and-packaging.patch
+Patch0023: 0023-KCM-request-parsing-and-sending-a-reply.patch
+Patch0024: 0024-KCM-Implement-an-internal-ccache-storage-and-retriev.patch
+Patch0025: 0025-KCM-Add-a-in-memory-credential-storage.patch
+Patch0026: 0026-KCM-Implement-KCM-server-operations.patch
+Patch0027: 0027-MAN-Add-a-manual-page-for-sssd-kcm.patch
+Patch0028: 0028-TESTS-Add-integration-tests-for-the-KCM-responder.patch
+Patch0029: 0029-SECRETS-Create-DB-path-before-the-operation-itself.patch
+Patch0030: 0030-SECRETS-Return-a-nicer-error-message-on-request-with.patch
+Patch0031: 0031-SECRETS-Store-ccaches-in-secrets-for-the-KCM-respond.patch
+Patch0032: 0032-TCURL-Support-HTTP-POST-for-creating-containers.patch
+Patch0033: 0033-KCM-Store-ccaches-in-secrets.patch
+Patch0034: 0034-KCM-Make-the-secrets-ccache-back-end-configurable-ma.patch
+Patch0035: 0035-KCM-Queue-requests-by-the-same-UID.patch
+Patch0036: 0036-KCM-Idle-terminate-the-responder-if-the-secrets-back.patch
+Patch0037: 0037-CONFIGURE-Fix-fallback-if-pkg-config-for-uuid-is-mis.patch
+Patch0038: 0038-intg-fix-configure-failure-with-strict-cflags.patch
+Patch0039: 0039-intg-Remove-bashism-from-intgcheck-prepare.patch
+Patch0040: 0040-UTIL-Introduce-subdomain_create_conf_path.patch
+Patch0041: 0041-SUBDOMAINS-Allow-use_fully_qualified_names-for-subdo.patch
+Patch0042: 0042-CACHE_REQ-Descend-into-subdomains-on-lookups.patch
+Patch0043: 0043-NSS-TESTS-Fix-subdomains-attribution.patch
+Patch0044: 0044-NSS-TESTS-Improve-setup-teardown-for-subdomains-test.patch
+Patch0045: 0045-NSS-TESTS-Include-searches-for-non-fqnames-members-o.patch
+Patch0046: 0046-SYSDB-Add-methods-to-deal-with-the-domain-s-resoluti.patch
+Patch0047: 0047-SYSDB-TESTS-Add-tests-for-the-domain-s-resolution-or.patch
+Patch0048: 0048-IPA-Get-ipaDomainsResolutionOrder-from-ipaConfig.patch
+Patch0049: 0049-IPA_SUBDOMAINS-Rename-_refresh_view-to-_refresh_view.patch
+Patch0050: 0050-IPA-Get-ipaDomainsResolutionOrder-from-IPA-ID-View.patch
+Patch0051: 0051-DLINKLIST-Add-DLIST_FOR_EACH_SAFE-macro.patch
+Patch0052: 0052-CACHE_REQ-Make-use-of-domainResolutionOrder.patch
+Patch0053: 0053-UTIL-Expose-replace_char-as-sss_replace_char.patch
+Patch0054: 0054-Add-domain_resolution_order-config-option.patch
+Patch0055: 0055-ssh-handle-binary-keys-correctly.patch
+Patch0056: 0056-ssh-add-support-for-certificates-from-non-default-vi.patch
+Patch0057: 0057-krb5-return-to-responder-that-pkinit-is-not-availabl.patch
+Patch0058: 0058-IPA-add-mapped-attributes-to-user-from-trusted-domai.patch
+Patch0059: 0059-IPA-lookup-AD-users-by-certificates-on-IPA-clients.patch
+Patch0060: 0060-IPA-enable-AD-user-lookup-by-certificate.patch
+Patch0061: 0061-CONFDB-Introduce-SSSD-domain-type-to-distinguish-POS.patch
+Patch0062: 0062-CONFDB-Allow-configuring-application-sections-as-non.patch
+Patch0063: 0063-CACHE_REQ-Domain-type-selection-in-cache_req.patch
+Patch0064: 0064-IFP-Search-both-POSIX-and-non-POSIX-domains.patch
+Patch0065: 0065-IFP-ListByName-Don-t-crash-when-no-results-are-found.patch
+Patch0066: 0066-PAM-Remove-unneeded-memory-context.patch
+Patch0067: 0067-PAM-Add-application-services.patch
+Patch0068: 0068-SYSDB-Allow-storing-non-POSIX-users.patch
+Patch0069: 0069-SYSDB-Only-generate-new-UID-in-local-domain.patch
+Patch0070: 0070-LDAP-save-non-POSIX-users-in-application-domains.patch
+Patch0071: 0071-LDAP-Relax-search-filters-in-application-domains.patch
+Patch0072: 0072-KRB5-Authenticate-users-in-a-non-POSIX-domain-using-.patch
+Patch0073: 0073-KCM-Fix-off-by-one-error-in-secrets-key-parsing.patch
+Patch0074: 0074-tcurl-add-support-for-ssl-and-raw-output.patch
+Patch0075: 0075-tcurl-test-refactor-so-new-options-can-be-added-more.patch
+Patch0076: 0076-tcurl-test-add-support-for-raw-output.patch
+Patch0077: 0077-tcurl-test-add-support-for-tls-settings.patch
+Patch0078: 0078-tcurl-add-support-for-http-basic-auth.patch
+Patch0079: 0079-tcurl-test-allow-to-set-custom-headers.patch
+Patch0080: 0080-tcurl-test-add-support-for-client-certificate.patch
+Patch0081: 0081-ci-do-not-build-secrets-on-rhel6.patch
+Patch0082: 0082-build-make-curl-required-by-secrets.patch
+Patch0083: 0083-secrets-use-tcurl-in-proxy-provider.patch
+Patch0084: 0084-secrets-remove-http-parser-code-in-proxy-provider.patch
+Patch0085: 0085-secrets-allow-to-configure-certificate-check.patch
+Patch0086: 0086-secrets-support-HTTP-basic-authentication-with-proxy.patch
+Patch0087: 0087-secrets-fix-debug-message.patch
+Patch0088: 0088-secrets-always-add-Content-Length-header.patch
+Patch0089: 0089-sss_iobuf-fix-read-shadows-a-global-declaration.patch
+Patch0090: 0090-configure-fix-typo.patch
+Patch0091: 0091-pam_test_client-add-service-and-environment-to-PAM-t.patch
+Patch0092: 0092-pam_test_client-add-SSSD-getpwnam-lookup.patch
+Patch0093: 0093-sss_sifp-update-method-names.patch
+Patch0094: 0094-pam_test_client-add-InfoPipe-user-lookup.patch
+Patch0095: 0095-sssctl-integrate-pam_test_client-into-sssctl.patch
+Patch0096: 0096-i18n-adding-sssctl-files.patch
+Patch0097: 0097-responders-do-not-leak-selinux-context-on-clients-de.patch
+Patch0098: 0098-ipa_s2n_get_acct_info_send-provide-correct-req_input.patch
+Patch0099: 0099-config-check-Message-when-sssd.conf-is-missing.patch
+Patch0100: 0100-sbus-check-connection-for-NULL-before-unregister-it.patch
+Patch0101: 0101-selinux-Do-not-fail-if-SELinux-is-not-managed.patch
+Patch0102: 0102-UTIL-Use-max-15-characters-for-AD-host-UPN.patch
+Patch0103: 0103-Move-sized_output_name-and-sized_domain_name-into-re.patch
+Patch0104: 0104-IFP-Use-sized_domain_name-to-format-the-groups-the-u.patch
+Patch0105: 0105-RESPONDER-Fallback-to-global-domain-resolution-order.patch
+Patch0106: 0106-NSS-TESTS-Improve-non-fqnames-tests.patch
+Patch0107: 0107-CACHE_REQ-Allow-configurationless-shortname-lookups.patch
+Patch0108: 0108-CACHE_REQ_DOMAIN-Add-some-comments-to-cache_req_doma.patch
+Patch0109: 0109-RESPONDER_COMMON-Improve-domaiN_resolution_order-deb.patch
+Patch0110: 0110-CACHE_REQ_DOMAIN-debug-the-set-domain-resolution-ord.patch
+Patch0111: 0111-SECRETS-remove-unused-variable.patch
+Patch0112: 0112-IPA-Improve-DEBUG-message-if-a-group-has-no-ipaNTSec.patch
+Patch0113: 0113-IPA-Improve-s2n-debug-message-for-missing-ipaNTSecur.patch
+Patch0114: 0114-CONFDB-Fix-standalone-application-domains.patch
+Patch0115: 0115-utils-add-sss_domain_is_forest_root.patch
+Patch0116: 0116-ad-handle-forest-root-not-listed-in-ad_enabled_domai.patch
+Patch0117: 0117-SDAP-Fix-handling-of-search-bases.patch
+Patch0118: 0118-overrides-add-certificates-to-mapped-attribute.patch
+Patch0119: 0119-AD-Make-ad_account_can_shortcut-reusable-by-SSSD-on-.patch
+Patch0120: 0120-LDAP-AD-Do-not-fail-in-case-rfc2307bis_nested_groups.patch
+Patch0121: 0121-PAM-check-matching-certificates-from-all-domains.patch
+Patch0122: 0122-DP-Reduce-Data-Provider-log-level-noise.patch
+Patch0123: 0123-NSS-Move-output-name-formatting-to-utils.patch
+Patch0124: 0124-CACHE_REQ-Add-a-new-cache_req_ncache_filter_fn-plugi.patch
+Patch0125: 0125-CACHE_REQ_RESULT-Introduce-cache_req_create_ldb_resu.patch
+Patch0126: 0126-CACHE_REQ-Make-use-of-cache_req_ncache_filter_fn.patch
+Patch0127: 0127-SERVER_MODE-Update-sdap-lists-for-each-ad_ctx.patch
+Patch0128: 0128-sss_nss_getlistbycert-return-results-from-multiple-d.patch
+Patch0129: 0129-CACHE_REQ-Avoid-using-of-uninitialized-value.patch
+Patch0130: 0130-CACHE_REQ-Ensure-the-domains-are-updated-for-filter-.patch
+Patch0131: 0131-AD-SUBDOMAINS-Fix-search-bases-for-child-domains.patch
+Patch0132: 0132-KRB5-Advise-the-user-to-inspect-the-krb5_child.log-i.patch
+Patch0133: 0133-cache_req-use-the-right-negative-cache-for-initgroup.patch
+Patch0134: 0134-test-make-sure-p11_child-is-build-for-pam-srv-tests.patch
+Patch0135: 0135-pam-properly-support-UPN-logon-names.patch
+Patch0136: 0136-KCM-Fix-the-per-client-serialization-queue.patch
+Patch0137: 0137-TESTS-Add-a-test-for-parallel-execution-of-klist.patch
+Patch0138: 0138-ipa-filter-IPA-users-from-extdom-lookups-by-certific.patch
+Patch0139: 0139-krb5-accept-changed-principal-if-krb5_canonicalize-T.patch
+Patch0140: 0140-IPA-Avoid-using-uninitialized-ret-value-when-skippin.patch
+Patch0141: 0141-IPA-Return-from-function-after-marking-a-request-as-.patch
+Patch0142: 0142-HBAC-Do-not-rely-on-originalMemberOf-use-the-sysdb-m.patch
+Patch0143: 0143-VALIDATORS-Add-subdomain-section.patch
+Patch0144: 0144-VALIDATORS-Remove-application-section-domain.patch
+Patch0145: 0145-VALIDATORS-Escape-special-regex-chars.patch
+Patch0146: 0146-TESTS-Add-unit-tests-for-cfg-validation.patch
+Patch0147: 0147-MAN-Fix-typo-in-trusted-domain-section.patch
+Patch0148: 0148-VALIDATORS-Change-regex-for-app-domains.patch
+Patch0149: 0149-VALIDATORS-Detect-inherit_from-in-normal-domain.patch
+Patch0150: 0150-VALIDATOR-prevent-duplicite-report-from-subdomain-se.patch
+Patch0151: 0151-test_config_check-Fix-few-issues.patch
+Patch0152: 0152-KRB5-Fix-access_provider-krb5.patch
+Patch0153: 0153-BUILD-Improve-error-messages-for-optional-dependenci.patch
+Patch0154: 0154-RESPONDER_COMMON-update-certmaps-in-responders.patch
+Patch0155: 0155-tests-fix-test_pam_preauth_cert_no_logon_name.patch
+Patch0156: 0156-pam_sss-add-support-for-SSS_PAM_CERT_INFO_WITH_HINT.patch
+Patch0157: 0157-add_pam_cert_response-add-support-for-SSS_PAM_CERT_I.patch
+Patch0158: 0158-PAM-send-user-name-hint-response-when-needed.patch
+Patch0159: 0159-sysdb-sysdb_get_certmap-allow-empty-certmap.patch
+Patch0160: 0160-sssctl-show-user-name-used-for-authentication-in-use.patch
+Patch0161: 0161-RESP-Provide-a-reusable-request-to-fully-resolve-inc.patch
+Patch0162: 0162-IFP-Only-format-the-output-name-to-the-short-version.patch
+Patch0163: 0163-IFP-Resolve-group-names-from-GIDs-if-required.patch
+Patch0164: 0164-ldap-handle-certmap-errors-gracefully.patch
+Patch0165: 0165-SECRETS-Fix-warning-Wpointer-bool-conversion.patch
+Patch0166: 0166-IPA-Fix-the-PAM-error-code-that-auth-code-expects-to.patch
+Patch0167: 0167-pam_sss-Fix-checking-of-empty-string-cert_user.patch
+Patch0168: 0168-CACHE_REQ-Simplify-_search_ncache_filter.patch
+Patch0169: 0169-CACHE_REQ_SEARCH-Check-for-filtered-users-groups-als.patch
+Patch0170: 0170-cache_req-Do-not-use-default_domain_suffix-with-netg.patch
+Patch0171: 0171-krb5-disable-enterprise-principals-during-password-c.patch
+Patch0172: 0172-pam_sss-Fix-leaking-of-memory-in-case-of-failures.patch
+Patch0173: 0173-IFP-Add-domain-and-domainname-attributes-to-the-user.patch
+Patch0174: 0174-IFP-Fix-error-handling-in-ifp_user_get_attr_handle_r.patch
+Patch0175: 0175-SYSDB-Return-ERR_NO_TS-when-there-s-no-timestamp-cac.patch
+Patch0176: 0176-SYSDB-Internally-expose-sysdb_search_ts_matches.patch
+Patch0177: 0177-SYSDB-Make-the-usage-of-the-filter-more-generic-for-.patch
+Patch0178: 0178-SYSDB_OPS-Mark-an-entry-as-expired-also-in-the-times.patch
+Patch0179: 0179-SYSDB_OPS-Invalidate-a-cache-entry-also-in-the-ts_ca.patch
+Patch0180: 0180-SYSDB-Introduce-_search_-users-groups-_by_timestamp.patch
+Patch0181: 0181-LDAP_ID_CLEANUP-Use-sysdb_search_-_by_timestamp.patch
+Patch0182: 0182-krb5-use-plain-principal-if-password-is-expired.patch
+Patch0183: 0183-RESPONDER-Use-fqnames-as-output-when-needed.patch
+Patch0184: 0184-DOMAIN-Add-sss_domain_info_-get-set-_output_fqnames.patch
+Patch0185: 0185-GPO-Fix-typo-in-DEBUG-message.patch
+Patch0186: 0186-SDAP-Update-parent-sdap_list.patch
+Patch0187: 0187-RESPONDERS-Fix-terminating-idle-connections.patch
+Patch0188: 0188-TESTS-Integration-test-for-idle-timeout.patch
+Patch0189: 0189-MAN-Document-that-client_idle_timeout-can-t-be-short.patch
+Patch0190: 0190-ad_account_can_shortcut-shortcut-if-ID-is-unknown.patch
+Patch0191: 0191-sudo-add-a-threshold-option-to-reduce-size-of-rules-.patch
+Patch0192: 0192-libwbclient-Change-return-code-for-wbcAuthenticateUs.patch
+Patch0193: 0193-IPA-fix-handling-of-certmap_ctx.patch
+Patch0194: 0194-certmap-make-sure-eku_oid_list-is-always-allocated.patch
+Patch0195: 0195-cache_req-Look-for-name-attribute-also-in-nss_cmd_ge.patch
+Patch0196: 0196-sssd_client-add-mutex-protected-call-to-the-PAC-resp.patch
+Patch0197: 0197-sysdb-sanitize-search-filter-input.patch
+Patch0198: 0198-ipa-make-sure-view-name-is-initialized-at-startup.patch
+Patch0199: 0199-CACHE_REQ-Copy-the-cr_domain-list-for-each-request.patch
+
+#This patch should not be removed in RHEL-7
+Patch999: 0999-NOUPSTREAM-Default-to-root-if-sssd-user-is-not-spec
+
+### Dependencies ###
+
+Requires: sssd-common = %{version}-%{release}
+Requires: sssd-ldap = %{version}-%{release}
+Requires: sssd-krb5 = %{version}-%{release}
+Requires: sssd-ipa = %{version}-%{release}
+Requires: sssd-ad = %{version}-%{release}
+Requires: sssd-proxy = %{version}-%{release}
+Requires: python-sssdconfig = %{version}-%{release}
+
+%global servicename sssd
+%global sssdstatedir %{_localstatedir}/lib/sss
+%global dbpath %{sssdstatedir}/db
+%global keytabdir %{sssdstatedir}/keytabs
+%global pipepath %{sssdstatedir}/pipes
+%global mcpath %{sssdstatedir}/mc
+%global pubconfpath %{sssdstatedir}/pubconf
+%global gpocachepath %{sssdstatedir}/gpo_cache
+%global secdbpath %{sssdstatedir}/secrets
+
+### Build Dependencies ###
+
+BuildRequires: autoconf
+BuildRequires: automake
+BuildRequires: libtool
+BuildRequires: m4
+BuildRequires: popt-devel
+BuildRequires: libtalloc-devel
+BuildRequires: libtevent-devel
+BuildRequires: libtdb-devel
+
+# LDB needs a strict version match to build
+BuildRequires: libldb-devel >= %{ldb_version}
+BuildRequires: libdhash-devel >= 0.4.2
+BuildRequires: libcollection-devel
+BuildRequires: libini_config-devel >= 1.3.0
+BuildRequires: dbus-devel
+BuildRequires: dbus-libs
+BuildRequires: openldap-devel
+BuildRequires: pam-devel
+BuildRequires: nss-devel
+BuildRequires: nspr-devel
+BuildRequires: pcre-devel
+BuildRequires: libxslt
+BuildRequires: libxml2
+BuildRequires: docbook-style-xsl
+BuildRequires: krb5-devel >= 1.12
+BuildRequires: c-ares-devel
+BuildRequires: python-devel
+BuildRequires: check-devel
+BuildRequires: doxygen
+BuildRequires: libselinux-devel
+BuildRequires: libsemanage-devel
+BuildRequires: bind-utils
+BuildRequires: keyutils-libs-devel
+BuildRequires: gettext-devel
+BuildRequires: pkgconfig
+BuildRequires: diffstat
+BuildRequires: findutils
+BuildRequires: glib2-devel
+BuildRequires: selinux-policy-targeted
+BuildRequires: libnl3-devel
+BuildRequires: systemd-devel
+%if (0%{?with_cifs_utils_plugin} == 1)
+BuildRequires: cifs-utils-devel
+%endif
+BuildRequires: libnfsidmap-devel
+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
+authentication mechanisms. It provides an NSS and PAM interface toward
+the system and a pluggable backend system to connect to multiple different
+account sources. It is also the basis to provide client auditing and policy
+services for projects like FreeIPA.
+
+The sssd subpackage is a meta-package that contains the deamon as well as all
+the existing back ends.
+
+%package common
+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
+# Due to ABI changes in rhel-7,5 (1.1.30/1.2.0)
+# rhel-7.4 <= will never have libldb 1.2.0 due to samba-4.6.x
+Conflicts: libldb >= 1.1.30
+# Requires
+Requires: sssd-client%{?_isa} = %{version}-%{release}
+Requires: libsss_idmap%{?_isa} = %{version}-%{release}
+Requires: libsss_sudo%{?_isa}  = %{version}-%{release}
+Requires: libsss_autofs%{?_isa} = %{version}-%{release}
+Requires(post): systemd-units chkconfig
+Requires(preun): systemd-units chkconfig
+Requires(postun): systemd-units chkconfig
+# 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}
+Obsoletes: libsss_sudo-devel <= 1.10.0-7%{?dist}.beta1
+
+%description common
+Common files for the SSSD. The common package includes all the files needed
+to run a particular back end, however, the back ends are packaged in separate
+subpackages such as sssd-ldap.
+
+%package client
+Summary: SSSD Client libraries for NSS and PAM
+Group: Applications/System
+License: LGPLv3+
+Requires(post): /sbin/ldconfig
+Requires(postun): /sbin/ldconfig
+Requires(post):  /usr/sbin/alternatives
+Requires(preun): /usr/sbin/alternatives
+
+%description client
+Provides the libraries needed by the PAM and NSS stacks to connect to the SSSD
+service.
+
+%package -n libsss_sudo
+Summary: A library to allow communication between SUDO and SSSD
+Group: Development/Libraries
+License: LGPLv3+
+Requires(post): /sbin/ldconfig
+Requires(postun): /sbin/ldconfig
+
+%description -n libsss_sudo
+A utility library to allow communication between SUDO and SSSD
+
+%package -n libsss_autofs
+Summary: A library to allow communication between Autofs and SSSD
+Group: Development/Libraries
+License: LGPLv3+
+
+%description -n libsss_autofs
+A utility library to allow communication between Autofs and SSSD
+
+%package tools
+Summary: Userspace tools for use with the SSSD
+Group: Applications/System
+License: GPLv3+
+Requires: sssd-common = %{version}-%{release}
+Requires: python-sss = %{version}-%{release}
+Requires: python-sssdconfig = %{version}-%{release}
+
+%description tools
+Provides userspace tools for manipulating users, groups, and nested groups in
+SSSD when using id_provider = local in /etc/sssd/sssd.conf.
+
+Also provides several other administrative tools:
+    * sss_debuglevel to change the debug level on the fly
+    * sss_seed which pre-creates a user entry for use in kickstarts
+    * sss_obfuscate for generating an obfuscated LDAP password
+    * sssctl -- an sssd status and control utility
+
+%package -n python-sssdconfig
+Summary: SSSD and IPA configuration file manipulation classes and functions
+Group: Applications/System
+License: GPLv3+
+BuildArch: noarch
+
+%description -n python-sssdconfig
+Provides python2 files for manipulation SSSD and IPA configuration files.
+
+%package -n python-sss
+Summary: Python2 bindings for sssd
+Group: Development/Libraries
+License: LGPLv3+
+Requires: sssd-common = %{version}-%{release}
+
+%description -n python-sss
+Provides python2 module for manipulating users, groups, and nested groups in
+SSSD when using id_provider = local in /etc/sssd/sssd.conf.
+
+Also provides several other useful python2 bindings:
+    * function for retrieving list of groups user belongs to.
+    * class for obfuscation of passwords
+
+%package -n python-sss-murmur
+Summary: Python2 bindings for murmur hash function
+Group: Development/Libraries
+License: LGPLv3+
+
+%description -n python-sss-murmur
+Provides python2 module for calculating the murmur hash version 3
+
+%package ldap
+Summary: The LDAP back end of the SSSD
+Group: Applications/System
+License: GPLv3+
+Conflicts: sssd < 1.10.0-8.beta2
+Requires: sssd-common = %{version}-%{release}
+Requires: sssd-krb5-common = %{version}-%{release}
+
+%description ldap
+Provides the LDAP back end that the SSSD can utilize to fetch identity data
+from and authenticate against an LDAP server.
+
+%package krb5-common
+Summary: SSSD helpers needed for Kerberos and GSSAPI authentication
+Group: Applications/System
+License: GPLv3+
+Conflicts: sssd < 1.10.0-8.beta2
+Requires: cyrus-sasl-gssapi%{?_isa}
+Requires: sssd-common = %{version}-%{release}
+
+%description krb5-common
+Provides helper processes that the LDAP and Kerberos back ends can use for
+Kerberos user or host authentication.
+
+%package krb5
+Summary: The Kerberos authentication back end for the SSSD
+Group: Applications/System
+License: GPLv3+
+Conflicts: sssd < 1.10.0-8.beta2
+Requires: sssd-common = %{version}-%{release}
+Requires: sssd-krb5-common = %{version}-%{release}
+
+%description krb5
+Provides the Kerberos back end that the SSSD can utilize authenticate
+against a Kerberos server.
+
+%package common-pac
+Summary: Common files needed for supporting PAC processing
+Group: Applications/System
+License: GPLv3+
+Requires: sssd-common = %{version}-%{release}
+
+%description common-pac
+Provides common files needed by SSSD providers such as IPA and Active Directory
+for handling Kerberos PACs.
+
+%package ipa
+Summary: The IPA back end of the SSSD
+Group: Applications/System
+License: GPLv3+
+Conflicts: sssd < 1.10.0-8.beta2
+Requires: sssd-common = %{version}-%{release}
+Requires: sssd-krb5-common = %{version}-%{release}
+Requires: libipa_hbac%{?_isa} = %{version}-%{release}
+Requires: bind-utils
+Requires: sssd-common-pac = %{version}-%{release}
+Requires(pre): shadow-utils
+
+%description ipa
+Provides the IPA back end that the SSSD can utilize to fetch identity data
+from and authenticate against an IPA server.
+
+%package ad
+Summary: The AD back end of the SSSD
+Group: Applications/System
+License: GPLv3+
+Conflicts: sssd < 1.10.0-8.beta2
+Requires: sssd-common = %{version}-%{release}
+Requires: sssd-krb5-common = %{version}-%{release}
+Requires: bind-utils
+Requires: sssd-common-pac = %{version}-%{release}
+
+%description ad
+Provides the Active Directory back end that the SSSD can utilize to fetch
+identity data from and authenticate against an Active Directory server.
+
+%package proxy
+Summary: The proxy back end of the SSSD
+Group: Applications/System
+License: GPLv3+
+Conflicts: sssd < 1.10.0-8.beta2
+Requires: sssd-common = %{version}-%{release}
+
+%description proxy
+Provides the proxy back end which can be used to wrap an existing NSS and/or
+PAM modules to leverage SSSD caching.
+
+%package -n libsss_idmap
+Summary: FreeIPA Idmap library
+Group: Development/Libraries
+License: LGPLv3+
+Requires(post): /sbin/ldconfig
+Requires(postun): /sbin/ldconfig
+
+%description -n libsss_idmap
+Utility library to convert SIDs to Unix uids and gids
+
+%package -n libsss_idmap-devel
+Summary: FreeIPA Idmap library
+Group: Development/Libraries
+License: LGPLv3+
+Requires: libsss_idmap = %{version}-%{release}
+
+%description -n libsss_idmap-devel
+Utility library to SIDs to Unix uids and gids
+
+%package -n libipa_hbac
+Summary: FreeIPA HBAC Evaluator library
+Group: Development/Libraries
+License: LGPLv3+
+Requires(post): /sbin/ldconfig
+Requires(postun): /sbin/ldconfig
+
+%description -n libipa_hbac
+Utility library to validate FreeIPA HBAC rules for authorization requests
+
+%package -n libipa_hbac-devel
+Summary: FreeIPA HBAC Evaluator library
+Group: Development/Libraries
+License: LGPLv3+
+Requires: libipa_hbac = %{version}-%{release}
+
+%description -n libipa_hbac-devel
+Utility library to validate FreeIPA HBAC rules for authorization requests
+
+%package -n python-libipa_hbac
+Summary: Python2 bindings for the FreeIPA HBAC Evaluator library
+Group: Development/Libraries
+License: LGPLv3+
+Requires: libipa_hbac = %{version}-%{release}
+Provides: libipa_hbac-python = %{version}-%{release}
+Obsoletes: libipa_hbac-python < 1.12.90
+
+%description -n python-libipa_hbac
+The python-libipa_hbac contains the bindings so that libipa_hbac can be
+used by Python applications.
+
+%package -n libsss_nss_idmap
+Summary: Library for SID and certificate based lookups
+Group: Development/Libraries
+License: LGPLv3+
+Requires(post): /sbin/ldconfig
+Requires(postun): /sbin/ldconfig
+
+%description -n libsss_nss_idmap
+Utility library for SID and certificate based lookups
+
+%package -n libsss_nss_idmap-devel
+Summary: Library for SID and certificate based lookups
+Group: Development/Libraries
+License: LGPLv3+
+Requires: libsss_nss_idmap = %{version}-%{release}
+
+%description -n libsss_nss_idmap-devel
+Utility library for SID and certificate based lookups
+
+%package -n python-libsss_nss_idmap
+Summary: Python2 bindings for libsss_nss_idmap
+Group: Development/Libraries
+License: LGPLv3+
+Requires: libsss_nss_idmap = %{version}-%{release}
+Provides: libsss_nss_idmap-python = %{version}-%{release}
+Obsoletes: libsss_nss_idmap-python < 1.12.90
+
+%description -n python-libsss_nss_idmap
+The python-libsss_nss_idmap contains the bindings so that libsss_nss_idmap can
+be used by Python applications.
+
+%package dbus
+Summary: The D-Bus responder of the SSSD
+Group: Applications/System
+License: GPLv3+
+Requires: sssd-common = %{version}-%{release}
+
+%description dbus
+Provides the D-Bus responder of the SSSD, called the InfoPipe, that allows
+the information from the SSSD to be transmitted over the system bus.
+
+%if (0%{?install_pcscd_polkit_rule} == 1)
+%package polkit-rules
+Summary: Rules for polkit integration for SSSD
+Group: Applications/System
+License: GPLv3+
+Requires: polkit >= 0.106
+Requires: sssd-common = %{version}-%{release}
+
+%description polkit-rules
+Provides rules for polkit integration with SSSD. This is required
+for smartcard support.
+%endif
+
+%package -n libsss_simpleifp
+Summary: The SSSD D-Bus responder helper library
+Group: Development/Libraries
+License: GPLv3+
+Requires: sssd-dbus = %{version}-%{release}
+Requires(post): /sbin/ldconfig
+Requires(postun): /sbin/ldconfig
+
+%description -n libsss_simpleifp
+Provides library that simplifies D-Bus API for the SSSD InfoPipe responder.
+
+%package -n libsss_simpleifp-devel
+Summary: The SSSD D-Bus responder helper library
+Group: Development/Libraries
+License: GPLv3+
+Requires: dbus-devel
+Requires: libsss_simpleifp = %{version}-%{release}
+
+%description -n libsss_simpleifp-devel
+Provides library that simplifies D-Bus API for the SSSD InfoPipe responder.
+
+%package libwbclient
+Summary: The SSSD libwbclient implementation
+Group: Applications/System
+License: GPLv3+ and LGPLv3+
+Conflicts: libwbclient < 4.1.12
+
+%description libwbclient
+The SSSD libwbclient implementation.
+
+%package libwbclient-devel
+Summary: Development libraries for the SSSD libwbclient implementation
+Group:  Development/Libraries
+License: GPLv3+ and LGPLv3+
+Conflicts: libwbclient-devel < 4.1.12
+
+%description libwbclient-devel
+Development libraries for the SSSD libwbclient implementation.
+
+%package winbind-idmap
+Summary: SSSD's idmap_sss Backend for Winbind
+Group:  Applications/System
+License: GPLv3+ and LGPLv3+
+
+%description winbind-idmap
+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"
+# is the patch prefix option (e.g. -p1)
+# Taken from specfile for python-simplejson
+UpdateTimestamps() {
+  Level=$1
+  PatchFile=$2
+
+  # Locate the affected files:
+  for f in $(diffstat $Level -l $PatchFile); do
+    # Set the files to have the same timestamp as that of the patch:
+    touch -r $PatchFile $f
+  done
+}
+
+%setup -q
+
+for p in %patches ; do
+    %__patch -p1 -i $p
+    UpdateTimestamps -p1 $p
+done
+
+%build
+autoreconf -ivf
+
+%configure \
+    --with-test-dir=/dev/shm \
+    --with-db-path=%{dbpath} \
+    --with-mcache-path=%{mcpath} \
+    --with-pipe-path=%{pipepath} \
+    --with-pubconf-path=%{pubconfpath} \
+    --with-gpo-cache-path=%{gpocachepath} \
+    --with-init-dir=%{_initrddir} \
+    --with-krb5-rcache-dir=%{_localstatedir}/cache/krb5rcache \
+    --enable-nsslibdir=%{_libdir} \
+    --enable-pammoddir=%{_libdir}/security \
+    --enable-nfsidmaplibdir=%{_libdir}/libnfsidmap \
+    --disable-static \
+    --disable-rpath \
+    --with-sssd-user=sssd \
+    --with-initscript=systemd \
+    --with-syslog=journald \
+    --enable-sss-default-nss-plugin \
+    %{?with_cifs_utils_plugin_option} \
+    --without-python3-bindings \
+    --with-ad-gpo-default=permissive \
+    %{?enable_polkit_rules_option} \
+    %{?enable_systemtap_opt} \
+    %{?with_kcm_option}
+
+make %{?_smp_mflags} all docs
+
+%check
+export CK_TIMEOUT_MULTIPLIER=10
+make %{?_smp_mflags} check VERBOSE=yes
+unset CK_TIMEOUT_MULTIPLIER
+
+%install
+
+make install DESTDIR=$RPM_BUILD_ROOT
+
+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
+fi
+
+# Prepare language files
+/usr/lib/rpm/find-lang.sh $RPM_BUILD_ROOT sssd
+
+# Copy default logrotate file
+mkdir -p $RPM_BUILD_ROOT/%{_sysconfdir}/logrotate.d
+install -m644 src/examples/logrotate $RPM_BUILD_ROOT%{_sysconfdir}/logrotate.d/sssd
+
+# Make sure SSSD is able to run on read-only root
+mkdir -p $RPM_BUILD_ROOT/%{_sysconfdir}/rwtab.d
+install -m644 src/examples/rwtab $RPM_BUILD_ROOT%{_sysconfdir}/rwtab.d/sssd
+
+%if (0%{?with_cifs_utils_plugin} == 1)
+# Create directory for cifs-idmap alternative
+# Otherwise this directory could not be owned by sssd-client
+mkdir -p $RPM_BUILD_ROOT/%{_sysconfdir}/cifs-utils
+%endif
+
+# Remove .la files created by libtool
+find $RPM_BUILD_ROOT -name "*.la" -exec rm -f {} \;
+
+# Suppress developer-only documentation
+rm -Rf ${RPM_BUILD_ROOT}/%{_docdir}/%{name}
+
+# Older versions of rpmbuild can only handle one -f option
+# So we need to append to the sssd*.lang file
+for file in `ls $RPM_BUILD_ROOT/%{python_sitelib}/*.egg-info 2> /dev/null`
+do
+    echo %{python_sitelib}/`basename $file` >> python_sssdconfig.lang
+done
+
+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 $subpackage.lang
+done
+
+for man in `find $RPM_BUILD_ROOT/%{_mandir}/??/man?/ -type f | sed -e "s#$RPM_BUILD_ROOT/%{_mandir}/##"`
+do
+    lang=`echo $man | cut -c 1-2`
+    case `basename $man` in
+        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
+            ;;
+        pam_sss*)
+            echo \%lang\(${lang}\) \%{_mandir}/${man}\* >> sssd_client.lang
+            ;;
+        sssd-ldap*)
+            echo \%lang\(${lang}\) \%{_mandir}/${man}\* >> sssd_ldap.lang
+            ;;
+        sssd-krb5*)
+            echo \%lang\(${lang}\) \%{_mandir}/${man}\* >> sssd_krb5.lang
+            ;;
+        sssd-ipa*)
+            echo \%lang\(${lang}\) \%{_mandir}/${man}\* >> sssd_ipa.lang
+            ;;
+        sssd-ad*)
+            echo \%lang\(${lang}\) \%{_mandir}/${man}\* >> sssd_ad.lang
+            ;;
+        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
+            ;;
+    esac
+done
+
+# Print these to the rpmbuild log
+echo "sssd.lang:"
+cat sssd.lang
+
+echo "python_sssdconfig.lang:"
+cat python_sssdconfig.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
+    echo "$subpackage.lang:"
+    cat $subpackage.lang
+done
+
+%files
+%defattr(-,root,root,-)
+%license COPYING
+
+%files common -f sssd.lang
+%defattr(-,root,root,-)
+%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
+
+%dir %{_libexecdir}/%{servicename}
+%{_libexecdir}/%{servicename}/sssd_be
+%{_libexecdir}/%{servicename}/sssd_nss
+%{_libexecdir}/%{servicename}/sssd_pam
+%{_libexecdir}/%{servicename}/sssd_autofs
+%{_libexecdir}/%{servicename}/sssd_secrets
+%{_libexecdir}/%{servicename}/sssd_ssh
+%{_libexecdir}/%{servicename}/sssd_sudo
+%{_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
+%{_libdir}/%{name}/libsss_child.so
+%{_libdir}/%{name}/libsss_crypt.so
+%{_libdir}/%{name}/libsss_cert.so
+%{_libdir}/%{name}/libsss_debug.so
+%{_libdir}/%{name}/libsss_krb5_common.so
+%{_libdir}/%{name}/libsss_ldap_common.so
+%{_libdir}/%{name}/libsss_util.so
+%{_libdir}/%{name}/libsss_semanage.so
+
+# 3rd party application libraries
+%{_libdir}/libnfsidmap/sss.so
+
+%{ldb_modulesdir}/memberof.so
+%{_bindir}/sss_ssh_authorizedkeys
+%{_bindir}/sss_ssh_knownhostsproxy
+%{_sbindir}/sss_cache
+%{_libexecdir}/%{servicename}/sss_signal
+
+%dir %{sssdstatedir}
+%dir %{_localstatedir}/cache/krb5rcache
+%attr(700,sssd,sssd) %dir %{dbpath}
+%attr(755,sssd,sssd) %dir %{mcpath}
+%attr(700,root,root) %dir %{secdbpath}
+%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
+%attr(755,sssd,sssd) %dir %{pipepath}
+%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}
+%attr(711,sssd,sssd) %dir %{_sysconfdir}/sssd
+%attr(711,sssd,sssd) %dir %{_sysconfdir}/sssd/conf.d
+%ghost %attr(0600,sssd,sssd) %config(noreplace) %{_sysconfdir}/sssd/sssd.conf
+%attr(755,root,root) %dir %{_sysconfdir}/systemd/system/sssd.service.d
+%config(noreplace) %{_sysconfdir}/systemd/system/sssd.service.d/journal.conf
+%dir %{_sysconfdir}/logrotate.d
+%config(noreplace) %{_sysconfdir}/logrotate.d/sssd
+%dir %{_sysconfdir}/rwtab.d
+%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
+%{_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*
+%if (0%{?enable_systemtap} == 1)
+%dir %{_datadir}/sssd/systemtap
+%{_datadir}/sssd/systemtap/id_perf.stp
+%{_datadir}/sssd/systemtap/nested_group_perf.stp
+%dir %{_datadir}/systemtap
+%dir %{_datadir}/systemtap/tapset
+%{_datadir}/systemtap/tapset/sssd.stp
+%{_datadir}/systemtap/tapset/sssd_functions.stp
+%endif
+
+%if (0%{?install_pcscd_polkit_rule} == 1)
+%files polkit-rules
+%{_datadir}/polkit-1/rules.d/*
+%endif
+
+%files ldap -f sssd_ldap.lang
+%defattr(-,root,root,-)
+%license COPYING
+%{_libdir}/%{name}/libsss_ldap.so
+%{_mandir}/man5/sssd-ldap.5*
+
+%files krb5-common
+%defattr(-,root,root,-)
+%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,-)
+%license COPYING
+%{_libdir}/%{name}/libsss_krb5.so
+%{_mandir}/man5/sssd-krb5.5*
+
+%files common-pac
+%defattr(-,root,root,-)
+%license COPYING
+%{_libexecdir}/%{servicename}/sssd_pac
+
+%files ipa -f sssd_ipa.lang
+%defattr(-,root,root,-)
+%license COPYING
+%attr(700,sssd,sssd) %dir %{keytabdir}
+%{_libdir}/%{name}/libsss_ipa.so
+%attr(4750,root,sssd) %{_libexecdir}/%{servicename}/selinux_child
+%{_mandir}/man5/sssd-ipa.5*
+
+%files ad -f sssd_ad.lang
+%defattr(-,root,root,-)
+%license COPYING
+%{_libdir}/%{name}/libsss_ad.so
+%{_libexecdir}/%{servicename}/gpo_child
+%{_mandir}/man5/sssd-ad.5*
+
+%files proxy
+%defattr(-,root,root,-)
+%license COPYING
+%attr(4750,root,sssd) %{_libexecdir}/%{servicename}/proxy_child
+%{_libdir}/%{name}/libsss_proxy.so
+
+%files dbus -f sssd_dbus.lang
+%defattr(-,root,root,-)
+%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
+
+%files -n libsss_simpleifp
+%defattr(-,root,root,-)
+%{_libdir}/libsss_simpleifp.so.*
+
+%files -n libsss_simpleifp-devel
+%defattr(-,root,root,-)
+%doc sss_simpleifp_doc/html
+%{_includedir}/sss_sifp.h
+%{_includedir}/sss_sifp_dbus.h
+%{_libdir}/libsss_simpleifp.so
+%{_libdir}/pkgconfig/sss_simpleifp.pc
+
+%files client -f sssd_client.lang
+%defattr(-,root,root,-)
+%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
+%{_libdir}/krb5/plugins/authdata/sssd_pac_plugin.so
+%if (0%{?with_cifs_utils_plugin} == 1)
+%dir %{_libdir}/cifs-utils
+%{_libdir}/cifs-utils/cifs_idmap_sss.so
+%dir %{_sysconfdir}/cifs-utils
+%ghost %{_sysconfdir}/cifs-utils/idmap-plugin
+%endif
+%if (0%{?with_krb5_localauth_plugin} == 1)
+%dir %{_libdir}/%{name}
+%dir %{_libdir}/%{name}/modules
+%{_libdir}/%{name}/modules/sssd_krb5_localauth_plugin.so
+%endif
+%{_mandir}/man8/pam_sss.8*
+%{_mandir}/man8/sssd_krb5_locator_plugin.8*
+
+%files -n libsss_sudo
+%defattr(-,root,root,-)
+%license src/sss_client/COPYING
+%{_libdir}/libsss_sudo.so*
+
+%files -n libsss_autofs
+%defattr(-,root,root,-)
+%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,-)
+%license COPYING
+%{_sbindir}/sss_useradd
+%{_sbindir}/sss_userdel
+%{_sbindir}/sss_usermod
+%{_sbindir}/sss_groupadd
+%{_sbindir}/sss_groupdel
+%{_sbindir}/sss_groupmod
+%{_sbindir}/sss_groupshow
+%{_sbindir}/sss_obfuscate
+%{_sbindir}/sss_override
+%{_sbindir}/sss_debuglevel
+%{_sbindir}/sss_seed
+%{_sbindir}/sssctl
+%{_mandir}/man8/sss_groupadd.8*
+%{_mandir}/man8/sss_groupdel.8*
+%{_mandir}/man8/sss_groupmod.8*
+%{_mandir}/man8/sss_groupshow.8*
+%{_mandir}/man8/sss_useradd.8*
+%{_mandir}/man8/sss_userdel.8*
+%{_mandir}/man8/sss_usermod.8*
+%{_mandir}/man8/sss_obfuscate.8*
+%{_mandir}/man8/sss_override.8*
+%{_mandir}/man8/sss_debuglevel.8*
+%{_mandir}/man8/sss_seed.8*
+%{_mandir}/man8/sssctl.8*
+
+%files -n python-sssdconfig -f python_sssdconfig.lang
+%defattr(-,root,root,-)
+%dir %{python_sitelib}/SSSDConfig
+%{python_sitelib}/SSSDConfig/*.py*
+
+%files -n python-sss
+%defattr(-,root,root,-)
+%{python_sitearch}/pysss.so
+
+%files -n python-sss-murmur
+%defattr(-,root,root,-)
+%{python_sitearch}/pysss_murmur.so
+
+%files -n libsss_idmap
+%defattr(-,root,root,-)
+%license src/sss_client/COPYING src/sss_client/COPYING.LESSER
+%{_libdir}/libsss_idmap.so.*
+
+%files -n libsss_idmap-devel
+%defattr(-,root,root,-)
+%doc idmap_doc/html
+%{_includedir}/sss_idmap.h
+%{_libdir}/libsss_idmap.so
+%{_libdir}/pkgconfig/sss_idmap.pc
+
+%files -n libipa_hbac
+%defattr(-,root,root,-)
+%license src/sss_client/COPYING src/sss_client/COPYING.LESSER
+%{_libdir}/libipa_hbac.so.*
+
+%files -n libipa_hbac-devel
+%defattr(-,root,root,-)
+%doc hbac_doc/html
+%{_includedir}/ipa_hbac.h
+%{_libdir}/libipa_hbac.so
+%{_libdir}/pkgconfig/ipa_hbac.pc
+
+%files -n libsss_nss_idmap
+%defattr(-,root,root,-)
+%license src/sss_client/COPYING src/sss_client/COPYING.LESSER
+%{_libdir}/libsss_nss_idmap.so.*
+
+%files -n libsss_nss_idmap-devel
+%defattr(-,root,root,-)
+%doc nss_idmap_doc/html
+%{_includedir}/sss_nss_idmap.h
+%{_libdir}/libsss_nss_idmap.so
+%{_libdir}/pkgconfig/sss_nss_idmap.pc
+
+%files -n python-libsss_nss_idmap
+%defattr(-,root,root,-)
+%{python_sitearch}/pysss_nss_idmap.so
+
+%files -n python-libipa_hbac
+%defattr(-,root,root,-)
+%{python_sitearch}/pyhbac.so
+
+%files libwbclient
+%defattr(-,root,root,-)
+%dir %{_libdir}/%{name}
+%dir %{_libdir}/%{name}/modules
+%{_libdir}/%{name}/modules/libwbclient.so.*
+
+%files libwbclient-devel
+%defattr(-,root,root,-)
+%{_includedir}/wbclient_sssd.h
+%{_libdir}/%{name}/modules/libwbclient.so
+%{_libdir}/pkgconfig/wbclient_sssd.pc
+
+%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
+
+%pre krb5-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
+
+%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 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-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
+/sbin/ldconfig
+/usr/sbin/alternatives --install /etc/cifs-utils/idmap-plugin cifs-idmap-plugin %{_libdir}/cifs-utils/cifs_idmap_sss.so 20
+
+%preun client
+if [ $1 -eq 0 ] ; then
+        /usr/sbin/alternatives --remove cifs-idmap-plugin %{_libdir}/cifs-utils/cifs_idmap_sss.so
+fi
+%else
+%post client -p /sbin/ldconfig
+%endif
+
+%postun client -p /sbin/ldconfig
+
+%post -n libsss_sudo -p /sbin/ldconfig
+
+%postun -n libsss_sudo -p /sbin/ldconfig
+
+%post -n libipa_hbac -p /sbin/ldconfig
+
+%postun -n libipa_hbac -p /sbin/ldconfig
+
+%post -n libsss_idmap -p /sbin/ldconfig
+
+%postun -n libsss_idmap -p /sbin/ldconfig
+
+%post -n libsss_nss_idmap -p /sbin/ldconfig
+
+%postun -n libsss_nss_idmap -p /sbin/ldconfig
+
+%post -n libsss_simpleifp -p /sbin/ldconfig
+
+%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} \
+              libwbclient.so.%{libwbc_alternatives_version}%{libwbc_alternatives_suffix} \
+              %{_libdir}/%{name}/modules/libwbclient.so.%{libwbc_lib_version} 20
+/sbin/ldconfig
+
+%preun libwbclient
+if [ $1 -eq 0 ]; then
+    %{_sbindir}/update-alternatives \
+        --remove libwbclient.so.%{libwbc_alternatives_version}%{libwbc_alternatives_suffix} \
+                 %{_libdir}/%{name}/modules/libwbclient.so.%{libwbc_lib_version}
+fi
+/sbin/ldconfig
+
+%post libwbclient-devel
+%{_sbindir}/update-alternatives --install %{_libdir}/libwbclient.so \
+                                libwbclient.so%{libwbc_alternatives_suffix} \
+                                %{_libdir}/%{name}/modules/libwbclient.so 20
+
+%preun libwbclient-devel
+if [ $1 -eq 0 ]; then
+        %{_sbindir}/update-alternatives --remove \
+                                libwbclient.so%{libwbc_alternatives_suffix} \
+                                %{_libdir}/%{name}/modules/libwbclient.so
+fi
+
+%posttrans common
+%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 Nov 03 2017 Fabiano Fidêncio <fidencio@redhat.com> - 1.15.2-50.8
+- Resolves: rhbz#1508972 - Accessing IdM kerberos ticket fails while id
+                           mapping is applied [rhel-7.4.z]
+- Resolves: rhbz#1509177 - Race condition between refreshing the cr_domain
+                           list and a request that is using the list can
+                           cause a segfault is sssd_nss [rhel-7.4.z]
+
+* Fri Oct 27 2017 Fabiano Fidêncio <fidencio@redhat.com> - 1.15.2-50.7
+- Resolves: rhbz#1506142 - SSSD can crash due to ABI changes in
+                           libldb >= 1.2.0 (1.1.30) [rhel-7.4.z]
+- Resolves: rhbz#1506682 - sssd_client: add mutex protected call to the
+                           PAC responder [rhel-7.4.z]
+- Resolves: rhbz#1499658 - CVE-2017-12173 sssd: unsanitized input when
+                           searching in local cache database [rhel-7.4.z]
+
+* Wed Sep 27 2017 Fabiano Fidêncio <fidencio@redhat.com> - 1.15.2-50.6
+- Add a patch that was missed in 1.15.2-50.4
+- Related: rhbz#1489290 - samba shares with sssd authentication broken
+                          on 7.4 [rhel-7.4.z]
+
+* Thu Sep 21 2017 Fabiano Fidêncio <fidencio@redhat.com> - 1.15.2-50.5
+- Resolves: rhbz#1493916 - Issues with certificate mapping rules [rhel-7.4.z]
+
+* Mon Sep 11 2017 Jakub Hrozek <jhrozek@redhat.com> - 1.15.2-50.4
+- Resolves: rhbz#1489290 - samba shares with sssd authentication broken
+                           on 7.4 [rhel-7.4.z]
+
+* Fri Aug 18 2017 Fabiano Fidêncio <fidencio@redhat.com> - 1.15.2-50.3
+- Resolves: rhbz#1482927 - sssd_be is utilizing more CPU during sudoi
+                           rules refresh [rhel-7.4.z]
+
+* Sun Aug  6 2017 Jakub Hrozek <jhrozek@redhat.com> - 1.15.2-50.2
+- Resolves: rhbz#1478252 - Querying the AD domain for external domain's
+                           ID can mark the AD domain offline [rhel-7.4.z]
+
+* Sun Aug  6 2017 Jakub Hrozek <jhrozek@redhat.com> - 1.15.2-50.1
+- Resolves: rhbz#1478250 - Idle nss file descriptors should be closed
+                           [rhel-7.4.z]
+
+* 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 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
+
+* 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
+
+* 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
+- 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
+- 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
+
+* 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
+
+* Tue Sep 20 2016 Jakub Hrozek <jhrozek@redhat.com> - 1.14.0-43
+- Resolves: rhbz#1376831 - sssd-common is missing dependency on sssd-sudo
+
+* Fri Sep 16 2016 Jakub Hrozek <jhrozek@redhat.com> - 1.14.0-42
+- Resolves: rhbz#1371631 - login using gdm calls for gdm-smartcard when
+                           smartcard authentication is not enabled
+
+* Wed Sep 14 2016 Jakub Hrozek <jhrozek@redhat.com> - 1.14.0-41
+- Resolves: rhbz#1373420 - sss_override fails to export
+
+* Wed Sep 14 2016 Jakub Hrozek <jhrozek@redhat.com> - 1.14.0-40
+- Resolves: rhbz#1375299 - sss_groupshow <user> fails with error "No such
+                           group in local domain. Printing groups only
+                           allowed in local domain"
+
+* Wed Sep 14 2016 Jakub Hrozek <jhrozek@redhat.com> - 1.14.0-39
+- Resolves: rhbz#1375182 - SSSD goes offline when the LDAP server returns
+                           sizelimit exceeded
+
+* Mon Sep 12 2016 Jakub Hrozek <jhrozek@redhat.com> - 1.14.0-38
+- Resolves: rhbz#1372753 - Access denied for user when access_provider =
+                           krb5 is set in sssd.conf
+
+* Mon Sep 12 2016 Jakub Hrozek <jhrozek@redhat.com> - 1.14.0-37
+- Resolves: rhbz#1373444 - unable to create group in sssd cache
+- Resolves: rhbz#1373577 - unable to add local user in sssd to a group in sssd
+
+* Wed Sep  7 2016 Jakub Hrozek <jhrozek@redhat.com> - 1.14.0-36
+- Resolves: rhbz#1369118 - Don't enable the default shadowtils domain in RHEL
+
+* Mon Sep  5 2016 Jakub Hrozek <jhrozek@redhat.com> - 1.14.0-35
+- Fix permissions for the private pipe directory
+- Resolves: rhbz#1362716 - selinux avc denial for vsftp login as ipa user
+
+* Fri Sep  2 2016 Jakub Hrozek <jhrozek@redhat.com> - 1.14.0-34
+- Resolves: rhbz#1371977 - resolving IPA nested user groups is broken in 1.14
+
+* Fri Sep  2 2016 Jakub Hrozek <jhrozek@redhat.com> - 1.14.0-33
+- Resolves: rhbz#1368496 - sssd is not able to authenticate with alias
+
+* Fri Sep  2 2016 Jakub Hrozek <jhrozek@redhat.com> - 1.14.0-32
+- Resolves: rhbz#1371152 - SSSD qualifies principal twice in IPA-AD trust
+                           if the principal attribute doesn't exist on the
+                           AD side
+
+* Fri Aug 26 2016 Jakub Hrozek <jhrozek@redhat.com> - 1.14.0-31
+- Apply forgotten patch
+- Resolves: rhbz#1368496 - sssd is not able to authenticate with alias
+- Resolves: rhbz#1366470 - sssd: throw away the timestamp cache if
+                           re-initializing the persistent cache
+- Fix deleting non-existent secret
+- Related: rhbz#1311056 - Add a Secrets as a Service component
+
+* Fri Aug 26 2016 Jakub Hrozek <jhrozek@redhat.com> - 1.14.0-30
+- Resolves: rhbz#1362716 - selinux avc denial for vsftp login as ipa user
+
+* Fri Aug 26 2016 Jakub Hrozek <jhrozek@redhat.com> - 1.14.0-29
+- Resolves: rhbz#1368496 - sssd is not able to authenticate with alias
+
+* Fri Aug 26 2016 Jakub Hrozek <jhrozek@redhat.com> - 1.14.0-28
+- Resolves: rhbz#1364033 - sssd exits if clock is adjusted backwards
+                           after boot
+
+* Fri Aug 19 2016 Jakub Hrozek <jhrozek@redhat.com> - 1.14.0-27
+- Resolves: rhbz#1362023 - SSSD fails to start when ldap_user_extra_attrs
+                           contains mail
+
+* Fri Aug 19 2016 Jakub Hrozek <jhrozek@redhat.com> - 1.14.0-26
+- Resolves: rhbz#1368324 - libsss_autofs.so is packaged in two packages
+                           sssd-common and libsss_autofs
+
+* Fri Aug 19 2016 Jakub Hrozek <jhrozek@redhat.com> - 1.14.0-25
+- Fix RPM scriptlet plumbing for the sssd-secrets responder
+- Related: rhbz#1311056 - Add a Secrets as a Service component
+
+* Wed Aug 17 2016 Jakub Hrozek <jhrozek@redhat.com> - 1.14.0-24
+- Add socket-activation plumbing for the sssd-secrets responder
+- Related: rhbz#1311056 - Add a Secrets as a Service component
+
+* Wed Aug 17 2016 Jakub Hrozek <jhrozek@redhat.com> - 1.14.0-23
+- Own the secrets directory
+- Related: rhbz#1311056 - Add a Secrets as a Service component
+
+* Wed Aug 17 2016 Jakub Hrozek <jhrozek@redhat.com> - 1.14.0-22
+- Resolves: rhbz#1268874 - Add an option to disable checking for trusted
+                           domains in the subdomains provider
+
+* Tue Aug 16 2016 Jakub Hrozek <jhrozek@redhat.com> - 1.14.0-21
+- Resolves: rhbz#1271280 - sssd stores and returns incorrect information
+                           about empty netgroup (ldap-server: 389-ds)
+
+* Tue Aug 16 2016 Jakub Hrozek <jhrozek@redhat.com> - 1.14.0-20
+- Resolves: rhbz#1290500 - [feat] command to manually list
+                           fo_add_server_to_list information
+
+* Tue Aug 16 2016 Jakub Hrozek <jhrozek@redhat.com> - 1.14.0-19
+- Add several small fixes related to the config API
+- Related: rhbz#1072458 - [RFE] SSSD configuration file test tool (sssd_check)
+
+* Thu Aug 11 2016 Jakub Hrozek <jhrozek@redhat.com> - 1.14.0-18
+- Resolves: rhbz#1349900 - gpo search errors out and gpo_cache file is
+                           never created
+
+* Wed Aug 10 2016 Jakub Hrozek <jhrozek@redhat.com> - 1.14.0-17
+- Fix regressions in the simple access provider
+- Resolves: rhbz#1360806 - sssd does not start if sub-domain user is used
+                           with simple access provider
+- Apply a number of specfile patches to better match the upstream spefile
+- Related: rhbz#1290381 - Rebase SSSD to 1.14.x in RHEL-7.3
+
+* Wed Aug 10 2016 Jakub Hrozek <jhrozek@redhat.com> - 1.14.0-16
+- Cherry-pick patches from upstream that fix several regressions
+- Avoid checking local users in all cases
+- Resolves: rhbz#1353951 - sssd_pam leaks file descriptors
+
+* Mon Aug  8 2016 Jakub Hrozek <jhrozek@redhat.com> - 1.14.0-15
+- Resolves: rhbz#1364118 - [abrt] [faf] sssd: unknown function():
+                           /usr/libexec/sssd/sssd_nss killed by 11
+- Resolves: rhbz#1361563 - Wrong pam error code returned for password
+                           change in offline mode
+
+* Fri Jul 29 2016 Jakub Hrozek <jhrozek@redhat.com> - 1.14.0-14
+- Resolves: rhbz#1309745 - Support multiple principals for IPA users
+
+* Fri Jul 29 2016 Jakub Hrozek <jhrozek@redhat.com> - 1.14.0-13
+- Resolves: rhbz#1304992 - Handle overriden name of members in the
+                           memberUid attribute
+
+* Wed Jul 27 2016 Jakub Hrozek <jhrozek@redhat.com> - 1.14.0-12
+- handle unresolvable sites more gracefully
+- Resolves: rhbz#1346011 - sssd is looking at a server in the GC of a
+                           subdomain, not the root domain.
+- fix compilation warnings in unit tests
+
+* Wed Jul 27 2016 Jakub Hrozek <jhrozek@redhat.com> - 1.14.0-11
+- fix capaths output
+- Resolves: rhbz#1344940 - GSSAPI error causes failures for child domain
+                           user logins across IPA - AD trust
+- also fix Coverity issues in the secrets responder and suppress noisy
+  debug messages when setting the timestamp cache
+
+* Tue Jul 19 2016 Jakub Hrozek <jhrozek@redhat.com> - 1.14.0-10
+- Resolves: rhbz#1356577 - sssctl: Time stamps without time zone information
+
+* Tue Jul 19 2016 Jakub Hrozek <jhrozek@redhat.com> - 1.14.0-9
+- Resolves: rhbz#1354414 - New or modified ID-View User overrides are not
+                           visible unless rm -f /var/lib/sss/db/*cache*
+
+* Mon Jul 18 2016 Jakub Hrozek <jhrozek@redhat.com> - 1.14.0-8
+- Resolves: rhbz#1211631 - [RFE] Support of UPN for IdM trusted domains
+
+* Thu Jul 14 2016 Jakub Hrozek <jhrozek@redhat.com> - 1.14.0-7
+- Resolves: rhbz#1350520 - [abrt] sssd-common: ipa_dyndns_update_send():
+                           sssd_be killed by SIGSEGV
+
+* Wed Jul 13 2016 Jakub Hrozek <jhrozek@redhat.com> - 1.14.0-6
+- Resolves: rhbz#1349882 - sssd does not work under non-root user
+- Also cherry-pick a few patches from upstream to fix config schema
+- Related: rhbz#1072458 - [RFE] SSSD configuration file test tool (sssd_check)
+
+* Wed Jul 13 2016 Jakub Hrozek <jhrozek@redhat.com> - 1.14.0-5
+- Sync a few minor patches from upstream
+- Fix sssctl manpage
+- Fix nss-tests unit test on big-endian machines
+- Fix several issues in the config schema
+- Related: rhbz#1072458 - [RFE] SSSD configuration file test tool (sssd_check)
+
+* Wed Jul 13 2016 Jakub Hrozek <jhrozek@redhat.com> - 1.14.0-4
+- Bundle http-parser
+- Resolves: rhbz#1311056 - Add a Secrets as a Service component
+
+* Tue Jul 12 2016 Jakub Hrozek <jhrozek@redhat.com> - 1.14.0-3
+- Sync a few minor patches from upstream
+- Fix a failover issue
+- Resolves: rhbz#1334749 - sssd fails to mark a connection as bad on
+                           searches that time out
+
+* Mon Jul 11 2016 Jakub Hrozek <jhrozek@redhat.com> - 1.14.0-2
+- Explicitly BuildRequire newer ding-libs
+- Resolves: rhbz#1072458 - [RFE] SSSD configuration file test tool (sssd_check)
+
+* Fri Jul  8 2016 Jakub Hrozek <jhrozek@redhat.com> - 1.14.0-1
+- New upstream release 1.14.0
+- Resolves: rhbz#1290381 - Rebase SSSD to 1.14.x in RHEL-7.3
+- Resolves: rhbz#835492 - [RFE] SSSD admin tool request - force reload
+- Resolves: rhbz#1072458 - [RFE] SSSD configuration file test tool (sssd_check)
+- Resolves: rhbz#1278691 - Please fix rfc2307 autofs schema defaults
+- Resolves: rhbz#1287209 - default_domain_suffix Appended to User Name
+- Resolves: rhbz#1300663 - Improve sudo protocol to support configurations
+                           with default_domain_suffix
+- Resolves: rhbz#1312275 - Support authentication indicators from IPA
+
+* Thu Jun 30 2016 Jakub Hrozek <jhrozek@redhat.com> - 1.14.0beta1-2
+- Resolves: rhbz#1290381 - Rebase SSSD to 1.14.x in RHEL-7.3
+- Resolves: rhbz#790113 - [RFE] "include" directive in sssd.conf
+- Resolves: rhbz#874985 - [RFE] AD provider support for automount lookups
+- Resolves: rhbz#879333 - [RFE] SSSD admin tool request - status overview
+- Resolves: rhbz#1140022 - [RFE]Allow sssd to add a new option that would
+                           specify which server to update DNS with
+- Resolves: rhbz#1290380 - RFE: Improve SSSD performance in large
+                           environments
+- Resolves: rhbz#883886 - sssd: incorrect checks on length values during
+                          packet decoding
+- Resolves: rhbz#988207 - sssd does not detail which line in configuration
+                          is invalid
+- Resolves: rhbz#1007969 - sssd_cache does not remove have an option to
+                           remove the sssd database
+- Resolves: rhbz#1103249 - PAC responder needs much time to process large
+                           group lists
+- Resolves: rhbz#1118257 - Users in ipa groups, added to netgroups are
+                           not resovable
+- Resolves: rhbz#1269018 - Too much logging from sssd_be
+- Resolves: rhbz#1293695 - sssd mixup nested group from AD trusted domains
+- Resolves: rhbz#1308935 - After removing certificate from user in IPA
+                           and even after sss_cache, FindByCertificate
+                           still finds the user
+- Resolves: rhbz#1315766 - SSSD PAM module does not support multiple
+                           password prompts (e.g. Password + Token) with sudo
+- Resolves: rhbz#1316164 - SSSD fails to process GPO from Active Directory
+- Resolves: rhbz#1322458 - sssd_be[11010]: segfault at 0 ip 00007ff889ff61bb
+                           sp 00007ffc7d66a3b0 error 4 in
+                           libsss_ipa.so[7ff889fcf000+5d000]
+
+* Mon Jun 20 2016 Jakub Hrozek <jhrozek@redhat.com> - 1.14.0alpha-1
+- Resolves: rhbz#1290381 - Rebase SSSD to 1.14.x in RHEL-7.3
+- The rebase includes fixes for the following bugzillas:
+- Resolves: rhbz#789477 - [RFE] SUDO: Support the IPA schema
+- Resolves: rhbz#1059972 - RFE: SSSD: Automatically assign new slices for
+                           any AD domain
+- Resolves: rhbz#1233200 - man sssd.conf should clarify details about
+                           subdomain_inherit option.
+- Resolves: rhbz#1238144 - Need better libhbac debuging added to sssd
+- Resolves: rhbz#1265366 - sss_override segfaults when accidentally adding
+                           --help flag to some commands
+- Resolves: rhbz#1269512 - sss_override: memory violation
+- Resolves: rhbz#1278566 - crash in sssd when non-Englsh locale is used
+                           and pam_strerror prints non-ASCII characters
+- Resolves: rhbz#1283686 - groups get deleted from the cache
+- Resolves: rhbz#1290378 - Smart Cards: Certificate in the ID View
+- Resolves: rhbz#1292238 - extreme memory usage in libnfsidmap sss.so
+                           plug-in when resolving groups with many members
+- Resolves: rhbz#1292456 - sssd_be AD segfaults on missing A record
+- Resolves: rhbz#1294670 - Local users with local sudo rules causes
+                           LDAP queries
+- Resolves: rhbz#1296618 - Properly remove OriginalMemberOf attribute in
+                           SSSD cache if user has no secondary groups anymore
+- Resolves: rhbz#1299553 - Cannot retrieve users after upgrade from 1.12
+                           to 1.13
+- Resolves: rhbz#1302821 - Cannot start sssd after switching to non-root
+- Resolves: rhbz#1310877 - [RFE] Support Automatic Renewing of Kerberos
+                           Host Keytabs
+- Resolves: rhbz#1313014 - sssd is not closing sockets properly
+- Resolves: rhbz#1318996 - SSSD does not fail over to next GC
+- Resolves: rhbz#1327270 - local overrides: issues with sub-domain users
+                           and mixed case names
+- Resolves: rhbz#1342547 - sssd-libwbclient: wbcSidsToUnixIds should not
+                           fail on lookup errors
+
+* Tue May 24 2016 Jakub Hrozek <jhrozek@redhat.com> - 1.13.0-50
+- Build the PAC plugin with krb5-1.14
+- Related: rhbz#1336688 - sssd tries to resolve global catalog servers
+                          from AD forest sub-domains in AD-IPA trust setup
+
+* Tue May 24 2016 Jakub Hrozek <jhrozek@redhat.com> - 1.13.0-49
+- Resolves: rhbz#1336688 - sssd tries to resolve global catalog servers
+                           from AD forest sub-domains in AD-IPA trust setup
+
+* Tue May 24 2016 Jakub Hrozek <jhrozek@redhat.com> - 1.13.0-48
+- Resolves: rhbz#1290853 - [sssd] Trusted (AD) user's info stays in sssd
+                           cache for much more than expected.
+
+* Mon May 23 2016 Jakub Hrozek <jhrozek@redhat.com> - 1.13.0-47
+- Resolves: rhbz#1336706 - sssd_nss memory usage keeps growing when trying
+                           to retrieve non-existing netgroups
+
+* Tue May 17 2016 Jakub Hrozek <jhrozek@redhat.com> - 1.13.0-46
+- Resolves: rhbz#1296902 - In IPA-AD trust environment access is granted
+                           to AD user even if the user is disabled on AD.
+
+* Tue May 17 2016 Jakub Hrozek <jhrozek@redhat.com> - 1.13.0-45
+- Resolves: rhbz#1334159 - IPA provider crashes if a netgroup from a
+                           trusted domain is requested
+
+* Mon Apr 18 2016 Jakub Hrozek <jhrozek@redhat.com> - 1.13.0-44
+- Resolves: rhbz#1308913 - sssd be memory leak in sssd's memberof plugin
+- More patches from upstream related to the memory leak
+
+* Fri Apr  1 2016 Jakub Hrozek <jhrozek@redhat.com> - 1.13.0-43
+- Resolves: rhbz#1308913 - sssd be memory leak in sssd's memberof plugin
+
+* Wed Feb 24 2016 Jakub Hrozek <jhrozek@redhat.com> - 1.13.0-42
+- Resolves: rhbz#1300740 - [RFE] IPA: resolve external group memberships
+                           of IPA groups during getgrnam and getgrgid
+
+* Tue Nov 24 2015 Jakub Hrozek <jhrozek@redhat.com> - 1.13.0-41
+- Resolves: rhbz#1284814  - sssd: [sysdb_add_user] (0x0400): Error: 17
+
+* Wed Oct 14 2015 Jakub Hrozek <jhrozek@redhat.com> - 1.13.0-40
+- Resolves: rhbz#1270827 - local overrides: don't contact server with
+                           overridden name/id
+
+* Wed Oct  7 2015 Jakub Hrozek <jhrozek@redhat.com> - 1.13.0-39
+- Resolves: rhbz#1267837 - sssd_be crashed in ipa_srv_ad_acct_lookup_step
+
+* Wed Oct  7 2015 Jakub Hrozek <jhrozek@redhat.com> - 1.13.0-38
+- Resolves: rhbz#1267176 - Memory leak / possible DoS with krb auth.
+
+* Wed Oct  7 2015 Jakub Hrozek <jhrozek@redhat.com> - 1.13.0-37
+- Resolves: rhbz#1267836 - PAM responder crashed if user was not set
+
+* Wed Sep 30 2015 Jakub Hrozek <jhrozek@redhat.com> - 1.13.0-36
+- Resolves: rhbz#1266107 - AD: Conditional jump or move depends on
+                           uninitialised value
+
+* Wed Sep 23 2015 Jakub Hrozek <jhrozek@redhat.com> - 1.13.0-35
+- Resolves: rhbz#1250135 - Detect re-established trusts in the IPA
+                           subdomain code
+
+* Tue Sep 22 2015 Jakub Hrozek <jhrozek@redhat.com> - 1.13.0-34
+- Fix a Coverity warning in dyndns code
+- Resolves: rhbz#1261155 - nsupdate exits on first GSSAPI error instead
+                           of processing other commands
+* Tue Sep 22 2015 Jakub Hrozek <jhrozek@redhat.com> - 1.13.0-33
+- Resolves: rhbz#1261155 - nsupdate exits on first GSSAPI error instead
+                           of processing other commands
+
+* Tue Sep 22 2015 Jakub Hrozek <jhrozek@redhat.com> - 1.13.0-32
+- Resolves: rhbz#1263735 - Could not resolve AD user from root domain
+
+* Tue Sep 22 2015 Jakub Hrozek <jhrozek@redhat.com> - 1.13.0-31
+- Remove -d from sss_override manpage
+- Related: rhbz#1259512 - sss_override : The local override user is not found
+
+* Tue Sep 22 2015 Jakub Hrozek <jhrozek@redhat.com> - 1.13.0-30
+- Patches required for better handling of failover with one-way trusts
+- Related: rhbz#1250135 - Detect re-established trusts in the IPA subdomain
+                          code
+
+* Fri Sep 18 2015 Jakub Hrozek <jhrozek@redhat.com> - 1.13.0-29
+- Resolves: rhbz#1263587 - sss_override --name doesn't work with RFC2307
+                           and ghost users
+
+* Fri Sep 18 2015 Jakub Hrozek <jhrozek@redhat.com> - 1.13.0-28
+- Resolves: rhbz#1259512 - sss_override : The local override user is not found
+
+* Fri Sep 18 2015 Jakub Hrozek <jhrozek@redhat.com> - 1.13.0-27
+- Resolves: rhbz#1260027 - sssd_be memory leak with sssd-ad in GPO code
+
+* Tue Sep  1 2015 Jakub Hrozek <jhrozek@redhat.com> - 1.13.0-26
+- Resolves: rhbz#1256398 - sssd cannot resolve user names containing
+                           backslash with ldap provider
+
+* Tue Aug 25 2015 Martin Kosek <mkosek@redhat.com> - 1.13.0-25
+- Resolves: rhbz#1254189 - sss_override contains an extra parameter --debug
+                           but is not listed in the man page or in
+                           the arguments help
+
+* Thu Aug 20 2015 Jakub Hrozek <jhrozek@redhat.com> - 1.13.0-24
+- Resolves: rhbz#1254518 - Fix crash in nss responder
+
+* Thu Aug 20 2015 Jakub Hrozek <jhrozek@redhat.com> - 1.13.0-23
+- Support import/export for local overrides
+- Support FQDNs for local overrides
+- Resolves: rhbz#1254184 - sss_override does not work correctly when
+                           'use_fully_qualified_names = True'
+
+* Tue Aug 18 2015 Jakub Hrozek <jhrozek@redhat.com> - 1.13.0-22
+- Resolves: rhbz#1244950 - Add index for 'objectSIDString' and maybe to
+                           other cache attributes
+
+* Mon Aug 17 2015 Jakub Hrozek <jhrozek@redhat.com> - 1.13.0-21
+- Resolves: rhbz#1250415 - sssd: p11_child hardening
+
+* Mon Aug 17 2015 Jakub Hrozek <jhrozek@redhat.com> - 1.13.0-20
+- Related: rhbz#1250135 - Detect re-established trusts in the IPA
+                          subdomain code
+
+* Mon Aug 17 2015 Jakub Hrozek <jhrozek@redhat.com> - 1.13.0-19
+- Resolves: rhbz#1202724 - [RFE] Add a way to lookup users based on CAC
+                           identity certificates
+
+* Mon Aug 17 2015 Jakub Hrozek <jhrozek@redhat.com> - 1.13.0-18
+- Resolves: rhbz#1232950 - [IPA/IdM] sudoOrder not honored as expected
+
+* Mon Aug 17 2015 Jakub Hrozek <jhrozek@redhat.com> - 1.13.0-17
+- Fix wildcard_limit=0
+- Resolves: rhbz#1206571 - [RFE] Expose D-BUS interface
+
+* Mon Aug 17 2015 Jakub Hrozek <jhrozek@redhat.com> - 1.13.0-16
+- Fix race condition in invalidating the memory cache
+- Related: rhbz#1206575 - [RFE] The fast memory cache should cache initgroups
+
+* Mon Aug 17 2015 Jakub Hrozek <jhrozek@redhat.com> - 1.13.0-15
+- Resolves: rhbz#1249015 - KDC proxy not working with SSSD krb5_use_kdcinfo
+                           enabled
+
+* Thu Aug  6 2015 Jakub Hrozek <jhrozek@redhat.com> - 1.13.0-14
+- Bump release number
+- Related: rhbz#1246489 - sss_obfuscate fails with "ImportError: No module
+                          named pysss"
+
+* Thu Aug  6 2015 Lukas Slebodnik <lslebodn@redhat.com> - 1.13.0-13
+- Fix missing dependency of sssd-tools
+- Resolves: rhbz#1246489 - sss_obfuscate fails with "ImportError: No module
+                           named pysss"
+
+* Wed Aug  5 2015 Jakub Hrozek <jhrozek@redhat.com> - 1.13.0-12
+- More memory cache related fixes
+- Related: rhbz#1206575 - [RFE] The fast memory cache should cache initgroups
+
+* Tue Aug  4 2015 Jakub Hrozek <jhrozek@redhat.com> - 1.13.0-11
+- Remove binary blob from SC patches as patch(1) can't handle those
+- Related: rhbz#854396 - [RFE] Support for smart cards
+
+* Tue Aug  4 2015 Jakub Hrozek <jhrozek@redhat.com> - 1.13.0-10
+- Resolves: rhbz#1244949 - getgrgid for user's UID on a trust client
+                           prevents getpw*
+
+* Tue Aug  4 2015 Jakub Hrozek <jhrozek@redhat.com> - 1.13.0-9
+- Fix memory cache integration tests
+- Resolves: rhbz#1206575 - [RFE] The fast memory cache should cache initgroups
+- Resolves: rhbz#854396 - [RFE] Support for smart cards
+
+* Tue Jul 28 2015 Jakub Hrozek <jhrozek@redhat.com> - 1.13.0-8
+- Remove OTP from PAM stack correctly
+- Related: rhbz#1200873 - [RFE] Allow smart multi step prompting when
+                          user logs in with password and token code from IPA
+- Handle sssd-owned keytabs when sssd runs as root
+- Related: rhbz#1205144 - RFE: Support one-way trusts for IPA
+
+* Mon Jul 27 2015 Jakub Hrozek <jhrozek@redhat.com> - 1.13.0-7
+- Resolves: rhbz#1183747 - [FEAT] UID and GID mapping on individual clients
+
+* Fri Jul 24 2015 Jakub Hrozek <jhrozek@redhat.com> - 1.13.0-6
+- Resolves: rhbz#1206565 - [RFE] Add dualstack and multihomed support
+- Resolves: rhbz#1187146 - If v4 address exists, will not create nonexistant
+                           v6 in ipa domain
+
+* Fri Jul 17 2015 Jakub Hrozek <jhrozek@redhat.com> - 1.13.0-5
+- Resolves: rhbz#1242942 - well-known SID check is broken for NetBIOS prefixes
+
+* Fri Jul 17 2015 Jakub Hrozek <jhrozek@redhat.com> - 1.13.0-4
+- Resolves: rhbz#1234722 - sssd ad provider fails to start in rhel7.2
+
+* Thu Jul 16 2015 Jakub Hrozek <jhrozek@redhat.com> - 1.13.0-3
+- Add support for InfoPipe wildcard requests
+- Resolves: rhbz#1206571 - [RFE] Expose D-BUS interface
+
+* Mon Jul  6 2015 Jakub Hrozek <jhrozek@redhat.com> - 1.13.0-2
+- Also package the initgr memcache
+- Related: rhbz#1205554 - Rebase SSSD to 1.13.x
+
+* Mon Jul  6 2015 Jakub Hrozek <jhrozek@redhat.com> - 1.13.0-1
+- Rebase to 1.13.0 upstream
+- Related: rhbz#1205554 - Rebase SSSD to 1.13.x
+- Resolves: rhbz#910187 - [RFE] authenticate against cache in SSSD
+- Resolves: rhbz#1206575 - [RFE] The fast memory cache should cache initgroups
+
+* Wed Jul  1 2015 Jakub Hrozek <jhrozek@redhat.com> - 1.13.0.3alpha
+- Don't default to SSSD user
+- Related: rhbz#1205554 - Rebase SSSD to 1.13.x
+
+* Tue Jun 23 2015 Jakub Hrozek <jhrozek@redhat.com> - 1.13.0.2alpha
+- Related: rhbz#1205554 - Rebase SSSD to 1.13.x
+- GPO default should be permissve
+
+* Mon Jun 22 2015 Jakub Hrozek <jhrozek@redhat.com> - 1.13.0.1alpha
+- Resolves: rhbz#1205554 - Rebase SSSD to 1.13.x
+- Relax the libldb requirement
+- Resolves: rhbz#1221992 - sssd_be segfault at 0 ip sp error 6 in
+                           libtevent.so.0.9.21
+- Resolves: rhbz#1221839 - SSSD group enumeration inconsistent due to
+                           binary SIDs
+- Resolves: rhbz#1219285 - Unable to resolve group memberships for AD
+                           users when using sssd-1.12.2-58.el7_1.6.x86_64
+                           client in combination with
+                           ipa-server-3.0.0-42.el6.x86_64 with AD Trust
+- Resolves: rhbz#1217559 - [RFE] Support GPOs from different domain controllers
+- Resolves: rhbz#1217350 - ignore_group_members doesn't work for subdomains
+- Resolves: rhbz#1217127 - Override for IPA users with login does not list
+                           user all groups
+- Resolves: rhbz#1216285 - autofs provider fails when default_domain_suffix
+                           and use_fully_qualified_names set
+- Resolves: rhbz#1214719 - Group resolution is inconsistent with group
+                           overrides
+- Resolves: rhbz#1214718 - Overridde with --login fails trusted adusers
+                           group membership resolution
+- Resolves: rhbz#1214716 - idoverridegroup for ipa group with --group-name
+                           does not work
+- Resolves: rhbz#1214337 - Overrides with --login work in second attempt
+- Resolves: rhbz#1212489 - Disable the cleanup task by default
+- Resolves: rhbz#1211830 - external users do not resolve with
+                           "default_domain_suffix" set in IPA server sssd.conf
+- Resolves: rhbz#1210854 - Only set the selinux context if the context
+                           differs from the local one
+- Resolves: rhbz#1209483 - When using id_provider=proxy with
+                           auth_provider=ldap, it does not work as expected
+- Resolves: rhbz#1209374 - Man sssd-ad(5) lists Group Policy Management
+                           Editor naming for some policies but not for all
+- Resolves: rhbz#1208507 - sysdb sudo search doesn't escape special characters
+- Resolves: rhbz#1206571 - [RFE] Expose D-BUS interface
+- Resolves: rhbz#1206566 - SSSD does not update Dynamic DNS records if
+                           the IPA domain differs from machine hostname's
+                           domain
+- Resolves: rhbz#1206189 - [bug] sssd always appends default_domain_suffix
+                           when checking for host keys
+- Resolves: rhbz#1204203 - sssd crashes intermittently
+- Resolves: rhbz#1203945 - [FJ7.0 Bug]: getgrent returns error because
+                           sss is written in nsswitch.conf as default
+- Resolves: rhbz#1203642 - GPO access control looks for computer object
+                           in user's domain only
+- Resolves: rhbz#1202245 - SSSD's HBAC processing is not permissive enough
+                           with broken replication entries
+- Resolves: rhbz#1201271 - sssd_nss segfaults if initgroups request is by
+                           UPN and doesn't find anything
+- Resolves: rhbz#1200873 - [RFE] Allow smart multi step prompting when
+                           user logs in with password and token code from IPA
+- Resolves: rhbz#1199541 - Read and use the TTL value when resolving a
+                           SRV query
+- Resolves: rhbz#1199533 - [RFE] Implement background refresh for users,
+                           groups or other cache objects
+- Resolves: rhbz#1199445 - Does sssd-ad use the most suitable attribute
+                           for group name?
+- Resolves: rhbz#1198477 - ccname_file_dummy is not unlinked on error
+- Resolves: rhbz#1187103 - [RFE] User's home directories are not taken
+                           from AD when there is an IPA trust with AD
+- Resolves: rhbz#1185536 - In ipa-ad trust, with 'default_domain_suffix' set
+                           to AD domain, IPA user are not able to log unless
+                           use_fully_qualified_names is set
+- Resolves: rhbz#1175760 - [RFE] Have OpenLDAP lock out ssh keys when
+                           account naturally expires
+- Resolves: rhbz#1163806 - [RFE]ad provider dns_discovery_domain option:
+                           kerberos discovery is not using this option
+- Resolves: rhbz#1205160 - Complain loudly if backend doesn't start due
+                           to missing or invalid keytab
+
+* Wed Apr 22 2015 Jakub Hrozek <jhrozek@redhat.com> - 1.12.2-61
+- Resolves: rhbz#1226119 - Properly handle AD's binary objectGUID
+
+* Wed Apr 22 2015 Jakub Hrozek <jhrozek@redhat.com> - 1.12.2-60
+- Filter out domain-local groups during AD initgroups operation
+- Related: rhbz#1201840 - SSSD downloads too much information when fetching
+                          information about groups
+
+* Wed Apr 22 2015 Jakub Hrozek <jhrozek@redhat.com> - 1.12.2-59
+- Resolves: rhbz#1201840 - SSSD downloads too much information when fetching
+                           information about groups
+
+* Thu Mar 19 2015 Jakub Hrozek <jhrozek@redhat.com> - 1.12.2-58.6
+- Initialize variable in the views code in one success and one failure path
+- Resolves: rhbz#1202170 - sssd_be segfault on IPA(when auth with AD
+                           trusted domain) client at
+                           src/providers/ipa/ipa_s2n_exop.c:1605
+
+* Tue Mar 17 2015 Jakub Hrozek <jhrozek@redhat.com> - 1.12.2-58.5
+- Resolves: rhbz#1202170 - sssd_be segfault on IPA(when auth with AD
+                           trusted domain) client at
+                           src/providers/ipa/ipa_s2n_exop.c:1605
+
+* Tue Mar 17 2015 Jakub Hrozek <jhrozek@redhat.com> - 1.12.2-58.4
+- Handle case where there is no default and no rules
+- Resolves: rhbz#1192314 - With empty ipaselinuxusermapdefault security
+                           context on client is staff_u
+
+* Thu Mar  5 2015 Jakub Hrozek <jhrozek@redhat.com> - 1.12.2-58.3
+- Set a pointer in ldap_child to NULL to avoid warnings
+- Related: rhbz#1198759 - ccname_file_dummy is not unlinked on error
+
+* Thu Mar  5 2015 Jakub Hrozek <jhrozek@redhat.com> - 1.12.2-58.2
+- Resolves: rhbz#1199143 - With empty ipaselinuxusermapdefault security
+                           context on client is staff_u
+
+* Thu Mar  5 2015 Jakub Hrozek <jhrozek@redhat.com> - 1.12.2-58.1
+- Resolves: rhbz#1198759 - ccname_file_dummy is not unlinked on error
+
+* Tue Feb  3 2015 Jakub Hrozek <jhrozek@redhat.com> - 1.12.2-57
+- Run the restart in sssd-common posttrans
+- Explicitly require libwbclient
+- Resolves: rhbz#1187113 - sssd deamon was not running after RHEL 7.1 upgrade
+
+* Fri Jan 30 2015 Jakub Hrozek <jhrozek@redhat.com> - 1.12.2-56
+- Resolves: rhbz#1187113 - sssd deamon was not running after RHEL 7.1 upgrade
+
+* Fri Jan 30 2015 Jakub Hrozek <jhrozek@redhat.com> - 1.12.2-55
+- Fix endianess bug in fill_id()
+- Related: rhbz#1109331 - [RFE] Allow SSSD to be used with smbd shares
+
+* Fri Jan 30 2015 Jakub Hrozek <jhrozek@redhat.com> - 1.12.2-54
+- Resolves: rhbz#1168904 - gid is overridden by uid in default trust view
+
+* Fri Jan 30 2015 Jakub Hrozek <jhrozek@redhat.com> - 1.12.2-53
+- Resolves: rhbz#1187192 - IPA initgroups don't work correctly in
+                           non-default view
+
+* Tue Jan 27 2015 Jakub Hrozek <jhrozek@redhat.com> - 1.12.2-52
+- Resolves: rhbz#1184982 - Need to set different umask in selinux_child
+
+* Tue Jan 27 2015 Jakub Hrozek <jhrozek@redhat.com> - 1.12.2-51
+- Bump the release number
+- Related: rhbz#1184140 - Users saved throug extop don't have the
+                          originalMemberOf attribute
+
+* Tue Jan 27 2015 Jakub Hrozek <jhrozek@redhat.com> - 1.12.2-50
+- Add a patch dependency
+- Related: rhbz#1184140 - Users saved throug extop don't have the
+                          originalMemberOf attribute
+
+* Tue Jan 27 2015 Jakub Hrozek <jhrozek@redhat.com> - 1.12.2-49
+- Process ghost members only once
+- Fix processing of universal groups with members from different domains
+- Related: rhbz#1168904 - gid is overridden by uid in default trust view
+
+* Tue Jan 27 2015 Jakub Hrozek <jhrozek@redhat.com> - 1.12.2-48
+- Related: rhbz#1184140 - Users saved throug extop don't have the
+                          originalMemberOf attribute
+
+* Fri Jan 23 2015 Jakub Hrozek <jhrozek@redhat.com> - 1.12.2-47
+- Resolves: rhbz#1185188 - Uncached SIDs cannot be resolved
+
+* Fri Jan 23 2015 Jakub Hrozek <jhrozek@redhat.com> - 1.12.2-46
+- Handle GID override in MPG domains
+- Handle views with mixed-case domains
+- Related: rhbz#1168904 - gid is overridden by uid in default trust view
+
+* Wed Jan 21 2015 Jakub Hrozek <jhrozek@redhat.com> - 1.12.2-45
+- Open socket to the PAC responder in krb5_child before dropping root
+- Related: rhbz#1184140 - Users saved throug extop don't have the
+                          originalMemberOf attribute
+
+* Tue Jan 20 2015 Jakub Hrozek <jhrozek@redhat.com> - 1.12.2-44
+- Resolves: rhbz#1184140 - Users saved throug extop don't have the
+                           originalMemberOf attribute
+
+* Mon Jan 19 2015 Jakub Hrozek <jhrozek@redhat.com> - 1.12.2-43
+- Resolves: rhbz#1182183 - pam_sss(sshd:auth): authentication failure with
+                           user from AD
+
+* Wed Jan 14 2015 Jakub Hrozek <jhrozek@redhat.com> - 1.12.2-42
+- Resolves: rhbz#889206 - On clock skew sssd returns system error
+
+* Wed Jan 14 2015 Jakub Hrozek <jhrozek@redhat.com> - 1.12.2-41
+- Related: rhbz#1168904 - gid is overridden by uid in default trust view
+
+* Tue Jan 13 2015 Jakub Hrozek <jhrozek@redhat.com> - 1.12.2-40
+- Resolves: rhbz#1177140 - gpo_child fails if "log level" is enabled in smb.conf
+- Related: rhbz#1168904 - gid is overridden by uid in default trust view
+
+* Fri Dec 19 2014 Sumit Bose <sbose@redhat.com> - 1.12.2-39
+- Resolves: rhbz#1175408 - SSSD should not fail authentication when only allow
+                           rules are used
+- Resolves: rhbz#1175705 - sssd-libwbclient conflicts with Samba's and causes
+                           crash in wbinfo
+                           - in addition to the patch libwbclient.so is
+                             filtered out of the Provides list of the package
+
+* Wed Dec 17 2014 Sumit Bose <sbose@redhat.com> - 1.12.2-38
+- Resolves: rhbz#1171215 - Crash in function get_object_from_cache
+- Resolves: rhbz#1171383 - getent fails for posix group with AD users after
+                           login
+- Resolves: rhbz#1171382 - getent of AD universal group fails after group users
+                           login
+- Resolves: rhbz#1170300 - Access is not rejected for disabled domain
+- Resolves: rhbz#1162486 - Error processing external groups with
+                           getgrnam/getgrgid in the server mode
+- Resolves: rhbz#1168904 - gid is overridden by uid in default trust view
+
+* Wed Dec 17 2014 Sumit Bose <sbose@redhat.com> - 1.12.2-37
+- Resolves: rhbz#1169459 - sssd-ad: The man page description to enable GPO HBAC
+                           Policies are unclear
+- Related: rhbz#1113783 - sssd should run under unprivileged user
+
+* Mon Dec 15 2014 Jakub Hrozek <jhrozek@redhat.com> - 1.12.2-35
+- Rebuild to add several forgotten Patch entries
+- Resolves: rhbz#1173482 - MAN: Document that only user names are checked
+                           for pam_trusted_users
+- Resolves: rhbz#1167324 - pam_sss domains option: User auth should fail
+                           when domains=<emtpy value>
+
+* Sun Dec 14 2014 Jakub Hrozek <jhrozek@redhat.com> - 1.12.2-35
+- Remove Coverity warnings in krb5_child code
+- Related: rhbz#1113783 - sssd should run under unprivileged user
+
+* Sat Dec 13 2014 Jakub Hrozek <jhrozek@redhat.com> - 1.12.2-34
+- Resolves: rhbz#1173482 - MAN: Document that only user names are checked
+                           for pam_trusted_users
+- Resolves: rhbz#1167324 - pam_sss domains option: User auth should fail
+                           when domains=<emtpy value>
+
+* Sat Dec 13 2014 Jakub Hrozek <jhrozek@redhat.com> - 1.12.2-33
+- Don't error out on chpass with OTPs
+- Related: rhbz#1109756 - Rebase SSSD to 1.12
+
+* Mon Dec  8 2014 Jakub Hrozek <jhrozek@redhat.com> - 1.12.2-32
+- Resolves: rhbz#1124320 - [FJ7.0 Bug]: getgrent returns error because sss
+                           is written in nsswitch.conf as default.
+
+* Mon Dec  8 2014 Jakub Hrozek <jhrozek@redhat.com> - 1.12.2-31
+- Resolves: rhbz#1169739 - selinuxusermap rule does not apply to trusted
+                           AD users
+- Enable running unit tests without cmocka
+- Related: rhbz#1113783 - sssd should run under unprivileged user
+
+* Wed Dec  3 2014 Jakub Hrozek <jhrozek@redhat.com> - 1.12.2-30
+- krb5_child and ldap_child do not call Kerberos calls as root
+- Related: rhbz#1113783 - sssd should run under unprivileged user
+
+* Wed Dec  3 2014 Jakub Hrozek <jhrozek@redhat.com> - 1.12.2-29
+- Resolves: rhbz#1168735 - The Kerberos provider is not properly views-aware
+
+* Wed Nov 26 2014 Jakub Hrozek <jhrozek@redhat.com> - 1.12.2-28
+- Fix typo in libwbclient-devel alternatives invocation
+- Related: rhbz#1109331 - [RFE] Allow SSSD to be used with smbd shares
+
+* Wed Nov 26 2014 Jakub Hrozek <jhrozek@redhat.com> - 1.12.2-27
+- Resolves: rhbz#1166727 - pam_sss domains option: Untrusted users from
+                           the same domain are allowed to auth.
+
+* Tue Nov 25 2014 Jakub Hrozek <jhrozek@redhat.com> - 1.12.2-26
+- Handle migrating clients between views
+- Related: rhbz#891984 - [RFE] ID Views: Support migration from the sync
+                         solution to the trust solution
+
+* Tue Nov 25 2014 Jakub Hrozek <jhrozek@redhat.com> - 1.12.2-25
+- Use alternatives for libwbclient
+- Related: rhbz#1109331 - [RFE] Allow SSSD to be used with smbd shares
+
+* Tue Nov 25 2014 Jakub Hrozek <jhrozek@redhat.com> - 1.12.2-24
+- Resolves: rhbz#1165794 - sssd does not work with custom value of option
+                           re_expression
+
+* Tue Nov 25 2014 Jakub Hrozek <jhrozek@redhat.com> - 1.12.2-23
+- Add an option that describes where to put generated krb5 files to
+- Related: rhbz#1135043 - [RFE] Implement localauth plugin for MIT krb5 1.12
+
+* Tue Nov 25 2014 Jakub Hrozek <jhrozek@redhat.com> - 1.12.2-22
+- Handle IPA group names returned from the extop plugin
+- Related: rhbz#891984 - [RFE] ID Views: Support migration from the sync
+                         solution to the trust solution
+
+* Tue Nov 25 2014 Jakub Hrozek <jhrozek@redhat.com> - 1.12.2-21
+- Resolves: rhbz#1165792 - automount segfaults in sss_nss_check_header
+
+* Thu Nov 20 2014 Jakub Hrozek <jhrozek@redhat.com> - 1.12.2-20
+- Resolves: rhbz#1163742 - "debug_timestamps = false" and "debug_microseconds
+                           = true" do not work after enabling journald
+                           with sssd.
+
+* Thu Nov 20 2014 Jakub Hrozek <jhrozek@redhat.com> - 1.12.2-19
+- Resolves: rhbz#1153593 - Manpage description of case_sensitive=preserving
+                          is incomplete
+
+* Thu Nov 20 2014 Jakub Hrozek <jhrozek@redhat.com> - 1.12.2-18
+- Support views for IPA users
+- Related: rhbz#891984 - [RFE] ID Views: Support migration from the sync
+                         solution to the trust solution
+
+* Thu Nov 20 2014 Jakub Hrozek <jhrozek@redhat.com> - 1.12.2-17
+- Update man page to clarify TGs should be disabled with a custom search base
+- Related: rhbz#1161741 - TokenGroups for LDAP provider breaks in corner cases
+
+* Wed Nov 19 2014 Jakub Hrozek <jhrozek@redhat.com> - 1.12.2-16
+- Use upstreamed patches for the rootless sssd
+- Related: rhbz#1113783 - sssd should run under unprivileged user
+
+* Wed Nov 19 2014 Jakub Hrozek <jhrozek@redhat.com> - 1.12.2-15
+- Resolves: rhbz#1153603 - Proxy Provider: Fails to lookup case sensitive
+                           users and groups with case_sensitive=preserving
+
+* Wed Nov 19 2014 Jakub Hrozek <jhrozek@redhat.com> - 1.12.2-14
+- Resolves: rhbz#1161741 - TokenGroups for LDAP provider breaks in corner cases
+
+* Wed Nov 19 2014 Jakub Hrozek <jhrozek@redhat.com> - 1.12.2-13
+- Resolves: rhbz#1162480 - dereferencing failure against openldap server
+
+* Wed Nov 12 2014 Jakub Hrozek <jhrozek@redhat.com> - 1.12.2-12
+- Move adding the user from pretrans to pre, copy adding the user to
+  sssd-krb5-common and sssd-ipa as well in order to work around yum
+  ordering issue
+- Related: rhbz#1113783 - sssd should run under unprivileged user
+
+* Tue Nov 11 2014 Jakub Hrozek <jhrozek@redhat.com> - 1.12.2-11
+- Resolves: rhbz#1113783 - sssd should run under unprivileged user
+
+* Fri Nov  7 2014 Jakub Hrozek <jhrozek@redhat.com> - 1.12.2-10
+- Fix two regressions in the new selinux_child process
+- Related: rhbz#1113783 - sssd should run under unprivileged user
+- Resolves: rhbz#1132365 - Remove password from the PAM stack if OTP is used
+
+* Wed Nov  5 2014 Jakub Hrozek <jhrozek@redhat.com> - 1.12.2-9
+- Include the ldap_child and selinux_child patches for rootless sssd
+- Related: rhbz#1113783 - sssd should run under unprivileged user
+
+* Wed Nov  5 2014 Jakub Hrozek <jhrozek@redhat.com> - 1.12.2-8
+- Support overriding SSH public keys with views
+- Support extended attributes via the extop plugin
+- Related: rhbz#1109756 - Rebase SSSD to 1.12
+- Resolves: rhbz#1137010 - disable midpoint refresh for netgroups if ptask
+                           refresh is enabled
+
+* Thu Oct 30 2014 Jakub Hrozek <jhrozek@redhat.com> - 1.12.2-7
+- Resolves: rhbz#1153518 - service lookups returned in lowercase with
+                           case_sensitive=preserving
+- Resolves: rhbz#1158809 - Enumeration shows only a single group multiple
+                           times
+
+* Wed Oct 22 2014 Jakub Hrozek <jhrozek@redhat.com> - 1.12.2-6
+- Include the responder and packaging patches for rootless sssd
+- Related: rhbz#1113783 - sssd should run under unprivileged user
+
+* Wed Oct 22 2014 Jakub Hrozek <jhrozek@redhat.com> - 1.12.2-5
+- Amend the sssd-ldap man page with info about lockout setup
+- Related: rhbz#1109756 - Rebase SSSD to 1.12
+- Resolves: rhbz#1137014 - Shell fallback mechanism in SSSD 
+- Resolves: rhbz#790854 - 4 functions with reference leaks within sssd (src/python/pyhbac.c) 
+
+* Wed Oct 22 2014 Jakub Hrozek <jhrozek@redhat.com> - 1.12.2-4
+- Fix regressions caused by views patches when SSSD is connected to a
+  pre-4.0 IPA server
+- Related: rhbz#1109756 - Rebase SSSD to 1.12
+
+* Wed Oct 22 2014 Jakub Hrozek <jhrozek@redhat.com> - 1.12.2-3
+- Add the low-level server changes for running as unprivileged user
+- Package the libsss_semange library needed for SELinux label changes
+- Related: rhbz#1113783 - sssd should run under unprivileged user 
+- Resolves: rhbz#1113784 - sssd should audit selinux user map changes 
+
+* Wed Oct 22 2014 Jakub Hrozek <jhrozek@redhat.com> - 1.12.2-2
+- Use libsemanage for SELinux label changes
+- Resolves: rhbz#1113784 - sssd should audit selinux user map changes 
+
+* Mon Oct 20 2014 Jakub Hrozek <jhrozek@redhat.com> - 1.12.2-1
+- Rebase SSSD to 1.12.2
+- Related: rhbz#1109756 - Rebase SSSD to 1.12
+
+* Thu Oct 09 2014 Jakub Hrozek <jhrozek@redhat.com> - 1.12.1-2
+- Sync with upstream
+- Related: rhbz#1109756 - Rebase SSSD to 1.12
+
+* Thu Sep 11 2014 Jakub Hrozek <jhrozek@redhat.com> - 1.12.1-1
+- Rebuild against ding-libs with fixed SONAME
+- Related: rhbz#1109756 - Rebase SSSD to 1.12
+
+* Tue Sep  9 2014 Jakub Hrozek <jhrozek@redhat.com> - 1.12.1-1
+- Rebase SSSD to 1.12.1
+- Related: rhbz#1109756 - Rebase SSSD to 1.12
+
+* Fri Sep 05 2014 Jakub Hrozek <jhrozek@redhat.com> - 1.12.0-3
+- Require ldb 2.1.17
+- Related: rhbz#1133914 - Rebase libldb to version 1.1.17 or newer
+
+* Fri Aug 08 2014 Jakub Hrozek <jhrozek@redhat.com> - 1.12.0-2
+- Fix fully qualified IFP lookups
+- Related: rhbz#1109756 - Rebase SSSD to 1.12
+
+* Thu Jul 24 2014 Jakub Hrozek <jhrozek@redhat.com> - 1.12.0-1
+- Rebase SSSD to 1.12.0
+- Related: rhbz#1109756 - Rebase SSSD to 1.12
+
+* Wed May 21 2014 Jakub Hrozek <jhrozek@redhat.com> - 1.11.2-70
+- Squash in upstream review comments about the PAC patch
+- Related: rhbz#1097286 - Expanding home directory fails when the request
+                          comes from the PAC responder
+
+* Tue May 13 2014 Jakub Hrozek <jhrozek@redhat.com> - 1.11.2-69
+- Backport a patch to allow krb5-utils-test to run as root
+- Related: rhbz#1097286 - Expanding home directory fails when the request
+                          comes from the PAC responder
+
+* Tue May 13 2014 Jakub Hrozek <jhrozek@redhat.com> - 1.11.2-68
+- Resolves: rhbz#1097286 - Expanding home directory fails when the request
+                           comes from the PAC responder
+
+* Tue May 13 2014 Jakub Hrozek <jhrozek@redhat.com> - 1.11.2-67
+- Fix a DEBUG message, backport two related fixes
+- Related: rhbz#1090653 - segfault in sssd_be when second domain tree
+                           users are queried while joined to child domain
+
+* Tue May 13 2014 Jakub Hrozek <jhrozek@redhat.com> - 1.11.2-66
+- Resolves: rhbz#1090653 - segfault in sssd_be when second domain tree
+                           users are queried while joined to child domain
+
+* Wed Apr 02 2014 Jakub Hrozek <jhrozek@redhat.com> - 1.11.2-65
+- Resolves: rhbz#1082191 - RHEL7 IPA selinuxusermap hbac rule not always
+                           matching
+
+* Wed Apr 02 2014 Jakub Hrozek <jhrozek@redhat.com> - 1.11.2-64
+- Resolves: rhbz#1077328 - other subdomains are unavailable when joined
+                           to a subdomain in the ad forest
+
+* Wed Mar 26 2014 Sumit Bose <sbose@redhat.com> - 1.11.2-63
+- Resolves: rhbz#1078877 - Valgrind: Invalid read of int while processing
+                           netgroup
+
+* Wed Mar 26 2014 Sumit Bose <sbose@redhat.com> - 1.11.2-62
+- Resolves: rhbz#1075092 - Password change w/ OTP generates error on success
+
+* Fri Mar 21 2014 Jakub Hrozek <jhrozek@redhat.com> - 1.11.2-61
+- Resolves: rhbz#1078840 -  Error during password change
+
+* Thu Mar 13 2014 Jakub Hrozek <jhrozek@redhat.com> - 1.11.2-60
+- Resolves: rhbz#1075663 - SSSD should create the SELinux mapping file
+                           with format expected by pam_selinux
+
+* Wed Mar 12 2014 Jakub Hrozek <jhrozek@redhat.com> - 1.11.2-59
+- Related: rhbz#1075621 - Add another Kerberos error code to trigger IPA
+                          password migration
+
+* Tue Mar 11 2014 Jakub Hrozek <jhrozek@redhat.com> - 1.11.2-58
+- Related: rhbz#1073635 - IPA SELinux code looks for the host in the wrong
+                          sysdb subdir when a trusted user logs in
+
+* Tue Mar 11 2014 Jakub Hrozek <jhrozek@redhat.com> - 1.11.2-57
+- Related: rhbz#1066096 - not retrieving homedirs of AD users with
+                          posix attributes
+
+* Mon Mar 10 2014 Jakub Hrozek <jhrozek@redhat.com> - 1.11.2-56
+- Related: rhbz#1072995 -  AD group inconsistency when using AD provider
+                           in sssd-1.11-40
+
+* Mon Mar 10 2014 Jakub Hrozek <jhrozek@redhat.com> - 1.11.2-55
+- Resolves: rhbz#1073631 - sssd fails to handle expired passwords
+                           when OTP is used
+
+* Tue Mar 04 2014 Jakub Hrozek <jhrozek@redhat.com> - 1.11.2-54
+- Resolves: rhbz#1072067 - SSSD Does not cache SELinux map from FreeIPA
+                           correctly
+
+* Tue Mar 04 2014 Jakub Hrozek <jhrozek@redhat.com> - 1.11.2-53
+- Resolves: rhbz#1071903 - ipa-server-mode: Use lower-case user name
+                           component in home dir path
+
+* Tue Mar 04 2014 Jakub Hrozek <jhrozek@redhat.com> - 1.11.2-52
+- Resolves: rhbz#1068725 - Evaluate usage of sudo LDAP provider together
+                           with the AD provider
+
+* Wed Feb 26 2014 Jakub Hrozek <jhrozek@redhat.com> - 1.11.2-51
+- Fix idmap documentation
+- Bump idmap version info
+- Related: rhbz#1067361 - Check IPA idranges before saving them to the cache
+
+* Wed Feb 26 2014 Jakub Hrozek <jhrozek@redhat.com> - 1.11.2-50
+- Pull some follow up man page fixes from upstream
+- Related: rhbz#1060389 - Document that `sssd` cache needs to be cleared
+                          manually, if ID mapping configuration changes
+- Related: rhbz#1064908 - MAN: Remove misleading memberof example from
+                          ldap_access_filter example
+
+* Wed Feb 26 2014 Jakub Hrozek <jhrozek@redhat.com> - 1.11.2-49
+- Resolves: rhbz#1060389 - Document that `sssd` cache needs to be cleared
+                           manually, if ID mapping configuration changes
+
+* Wed Feb 26 2014 Jakub Hrozek <jhrozek@redhat.com> - 1.11.2-48
+- Resolves: rhbz#1064908 - MAN: Remove misleading memberof example from
+                           ldap_access_filter example
+
+* Wed Feb 26 2014 Jakub Hrozek <jhrozek@redhat.com> - 1.11.2-47
+- Resolves: rhbz#1068723 - Setting int option to 0 yields the default value
+
+* Wed Feb 26 2014 Jakub Hrozek <jhrozek@redhat.com> - 1.11.2-46
+- Resolves: rhbz#1067361 - Check IPA idranges before saving them to the cache
+
+* Wed Feb 26 2014 Jakub Hrozek <jhrozek@redhat.com> - 1.11.2-45
+- Resolves: rhbz#1067476 - SSSD pam module accepts usernames with leading
+                           spaces
+
+* Wed Feb 26 2014 Jakub Hrozek <jhrozek@redhat.com> - 1.11.2-44
+- Resolves: rhbz#1033069 - Configuring two different provider types might
+                           start two parallel enumeration tasks
+
+* Mon Feb 17 2014 Jakub Hrozek <jhrozek@redhat.com> - 1.11.2-43
+- Resolves: rhbz#1068640 - 'IPA: Don't call tevent_req_post outside _send'
+                           should be added to RHEL7
+
+* Mon Feb 17 2014 Jakub Hrozek <jhrozek@redhat.com> - 1.11.2-42
+- Resolves: rhbz#1063977 - SSSD needs to enable FAST by default
+
+* Mon Feb 17 2014 Jakub Hrozek <jhrozek@redhat.com> - 1.11.2-41
+- Resolves: rhbz#1064582 - sss_cache does not reset the SYSDB_INITGR_EXPIRE
+                           attribute when expiring users
+
+* Wed Feb 12 2014 Jakub Hrozek <jhrozek@redhat.com> - 1.11.2-40
+- Resolves: rhbz#1033081 - Implement heuristics to detect if POSIX attributes
+                           have been replicated to the Global Catalog or not
+
+* Wed Feb 12 2014 Jakub Hrozek <jhrozek@redhat.com> - 1.11.2-39
+- Resolves: rhbz#872177 - [RFE] subdomain homedir template should be
+                          configurable/use flatname by default
+
+* Wed Feb 12 2014 Jakub Hrozek <jhrozek@redhat.com> - 1.11.2-38
+- Resolves: rhbz#1059753 - Warn with a user-friendly error message when
+                           permissions on sssd.conf are incorrect
+
+* Wed Jan 29 2014 Jakub Hrozek <jhrozek@redhat.com> - 1.11.2-37
+- Resolves: rhbz#1037653 - Enabling ldap_id_mapping doesn't exclude
+                           uidNumber in filter
+
+* Wed Jan 29 2014 Jakub Hrozek <jhrozek@redhat.com> - 1.11.2-36
+- Resolves: rhbz#1059253 - Man page states default_shell option supersedes
+                           other shell options but in fact override_shell does.
+- Use the right domain for AD site resolution
+- Related: rhbz#743503 -  [RFE] sssd should support DNS sites
+
+* Wed Jan 29 2014 Jakub Hrozek <jhrozek@redhat.com> - 1.11.2-35
+- Resolves: rhbz#1028039 - AD Enumeration reads data from LDAP while
+                           regular lookups connect to GC
+
+* Wed Jan 29 2014 Jakub Hrozek <jhrozek@redhat.com> - 1.11.2-34
+- Resolves: rhbz#877438 - sudoNotBefore/sudoNotAfter not supported by sssd
+                          sudoers plugin
+
+* Fri Jan 24 2014 Daniel Mach <dmach@redhat.com> - 1.11.2-33
+- Mass rebuild 2014-01-24
+
+* Fri Jan 24 2014 Jakub Hrozek <jhrozek@redhat.com> - 1.11.2-32
+- Resolves: rhbz#1054639 - sssd_be aborts a request if it doesn't match
+                           any configured idmap domain
+
+* Fri Jan 24 2014 Jakub Hrozek <jhrozek@redhat.com> - 1.11.2-31
+- Resolves: rhbz#1054899 - explicitly suggest krb5_auth_timeout in a loud
+                           DEBUG message in case Kerberos authentication
+                           times out
+
+* Wed Jan 22 2014 Jakub Hrozek <jhrozek@redhat.com> - 1.11.2-30
+- Resolves: rhbz#1037653 - Enabling ldap_id_mapping doesn't exclude
+                           uidNumber in filter
+
+* Mon Jan 20 2014 Jakub Hrozek <jhrozek@redhat.com> - 1.11.2-29
+- Resolves: rhbz#1051360 - [FJ7.0 Bug]: [REG] sssd_be crashes when
+                           ldap_search_base cannot be parsed.
+- Fix a typo in the man page
+- Related: rhbz#1034920 - RHEL7 sssd not setting IPA AD trusted user homedir
+
+* Mon Jan 20 2014 Jakub Hrozek <jhrozek@redhat.com> - 1.11.2-28
+- Resolves: rhbz#1054639 - sssd_be aborts a request if it doesn't match
+                           any configured idmap domain
+- Fix return value when searching for AD domain flat names
+- Resolves: rhbz#1048102 - Access denied for users from gc domain when
+                           using format DOMAIN\user
+
+* Wed Jan 15 2014 Jakub Hrozek <jhrozek@redhat.com> - 1.11.2-27
+- Resolves: rhbz#1034920 - RHEL7 sssd not setting IPA AD trusted user homedir
+
+* Wed Jan 15 2014 Jakub Hrozek <jhrozek@redhat.com> - 1.11.2-26
+- Resolves: rhbz#1048102 - Access denied for users from gc domain when
+                           using format DOMAIN\user
+
+* Wed Jan 15 2014 Jakub Hrozek <jhrozek@redhat.com> - 1.11.2-25
+- Resolves: rhbz#1053106 - sssd ad trusted sub domain do not inherit
+                           fallbacks and overrides settings
+
+* Thu Jan 09 2014 Jakub Hrozek <jhrozek@redhat.com> - 1.11.2-24
+- Resolves: rhbz#1051016 - FAST does not work in SSSD 1.11.2 in Fedora 20
+
+* Thu Jan 09 2014 Jakub Hrozek <jhrozek@redhat.com> - 1.11.2-23
+- Resolves: rhbz#1033133 - "System Error" when invalid ad_access_filter
+                            is used
+
+* Thu Jan 09 2014 Jakub Hrozek <jhrozek@redhat.com> - 1.11.2-22
+- Resolves: rhbz#1032983 - sssd_be crashes when ad_access_filter uses
+                           FOREST keyword.
+- Fix two memory leaks in the PAC responder (Related: rhbz#991065)
+
+* Wed Jan 08 2014 Jakub Hrozek <jhrozek@redhat.com> - 1.11.2-21
+- Resolves: rhbz#1048184 - Group lookup does not return member with multiple
+                           names after user lookup
+
+* Wed Jan 08 2014 Jakub Hrozek <jhrozek@redhat.com> - 1.11.2-20
+- Resolves: rhbz#1049533 - Group membership lookup issue
+
+* Fri Dec 27 2013 Daniel Mach <dmach@redhat.com> - 1.11.2-19
+- Mass rebuild 2013-12-27
+
+* Thu Dec 19 2013 Jakub Hrozek <jhrozek@redhat.com> - 1.11.2-18
+- Resolves: rhbz#894068 - sss_cache doesn't support subdomains
+
+* Thu Dec 19 2013 Jakub Hrozek <jhrozek@redhat.com> - 1.11.2-17
+- Re-initialize subdomains after provider startup
+- Related: rhbz#1038637 - If SSSD starts offline, subdomains list is
+                          never read
+
+* Thu Dec 19 2013 Jakub Hrozek <jhrozek@redhat.com> - 1.11.2-16
+- The AD provider is able to resolve group memberships for groups with
+  Global and Universal scope
+- Related: rhbz#1033096 - tokenGroups do not work reliable with Global
+                          Catalog
+
+* Wed Dec 18 2013 Jakub Hrozek <jhrozek@redhat.com> - 1.11.2-15
+- Resolves: rhbz#1033096 - tokenGroups do not work reliable with Global
+                           Catalog
+- Resolves: rhbz#1030483 - Individual group search returned multiple
+                           results in GC lookups
+
+* Wed Dec 18 2013 Jakub Hrozek <jhrozek@redhat.com> - 1.11.2-14
+- Resolves: rhbz#1040969 - sssd_nss grows memory footprint when netgroups
+                           are requested
+
+* Thu Dec 12 2013 Jakub Hrozek <jhrozek@redhat.com> - 1.11.2-13
+- Resolves: rhbz#1023409 - Valgrind sssd "Syscall param
+                           socketcall.sendto(msg) points to uninitialised
+                           byte(s)"
+
+* Thu Dec 12 2013 Jakub Hrozek <jhrozek@redhat.com> - 1.11.2-12
+- Resolves: rhbz#1037936 - sssd_be crashes occasionally
+
+* Thu Dec 12 2013 Jakub Hrozek <jhrozek@redhat.com> - 1.11.2-11
+- Resolves: rhbz#1038637 - If SSSD starts offline, subdomains list is
+                           never read
+
+* Mon Dec  2 2013 Jakub Hrozek <jhrozek@redhat.com> - 1.11.2-10
+- Resolves: rhbz#1029631 - sssd_be crashes on manually adding a cleartext
+                           password to ldap_default_authtok
+
+* Mon Dec  2 2013 Jakub Hrozek <jhrozek@redhat.com> - 1.11.2-9
+- Resolves: rhbz#1036758 - SSSD: Allow for custom attributes in RDN when
+                           using id_provider = proxy
+
+* Mon Dec  2 2013 Jakub Hrozek <jhrozek@redhat.com> - 1.11.2-8
+- Resolves: rhbz#1034050 - Errors in domain log when saving user to sysdb
+
+* Mon Dec  2 2013 Jakub Hrozek <jhrozek@redhat.com> - 1.11.2-7
+- Resolves: rhbz#1036157 - sssd can't retrieve auto.master when using the
+                           "default_domain_suffix" option in
+
+* Mon Dec  2 2013 Jakub Hrozek <jhrozek@redhat.com> - 1.11.2-6
+- Resolves: rhbz#1028057 - Improve detection of the right domain when
+                           processing group with members from several domains
+
+* Mon Dec  2 2013 Jakub Hrozek <jhrozek@redhat.com> - 1.11.2-5
+- Resolves: rhbz#1033084 - sssd_be segfaults if empty grop is resolved
+                           using ad_matching_rule
+
+* Mon Dec  2 2013 Jakub Hrozek <jhrozek@redhat.com> - 1.11.2-4
+- Resolves: rhbz#1031562 - Incorrect mention of access_filter in sssd-ad
+                           manpage
+
+* Mon Dec  2 2013 Jakub Hrozek <jhrozek@redhat.com> - 1.11.2-3
+- Resolves: rhbz#991549 - sssd fails to retrieve netgroups with multiple
+                          CN attributes
+
+* Mon Dec  2 2013 Jakub Hrozek <jhrozek@redhat.com> - 1.11.2-2
+- Skip netgroups that don't provide well-formed triplets
+- Related: rhbz#991549 -  sssd fails to retrieve netgroups with multiple
+                          CN attributes
+
+* Wed Oct 30 2013 Jakub Hrozek <jhrozek@redhat.com> - 1.11.2-1
+- New upstream release 1.11.2
+- Remove upstreamed patches
+- https://fedorahosted.org/sssd/wiki/Releases/Notes-1.11.2
+- Resolves: rhbz#991065
+
+* Fri Sep 27 2013 Jakub Hrozek <jhrozek@redhat.com> - 1.11.1-2
+- Resolves: rhbz#1019882 - RHEL7 ipa ad trusted user lookups failed with
+                           sssd_be crash
+- Resolves: rhbz#1002597 - ad: unable to resolve membership when user is
+                           from different domain than group
+
+* Fri Sep 27 2013 Jakub Hrozek <jhrozek@redhat.com> - 1.11.1-1
+- New upstream release 1.11.1
+- https://fedorahosted.org/sssd/wiki/Releases/Notes-1.11.1
+- Resolves: rhbz#991065 - Rebase SSSD to 1.11.0
+
+* Thu Aug 29 2013 Jakub Hrozek <jhrozek@redhat.com> - 1.11.0-1
+- New upstream release 1.11.0
+- https://fedorahosted.org/sssd/wiki/Releases/Notes-1.11.0
+- Resolves: rhbz#991065
+
+* Fri Aug 02 2013 Jakub Hrozek <jhrozek@redhat.com> - 1.11.0.1beta2
+- New upstream release 1.11 beta 2
+- https://fedorahosted.org/sssd/wiki/Releases/Notes-1.11.0beta2
+- 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
+                      pam libraries
+
+* Wed Jul 31 2013 Jakub Hrozek <jhrozek@redhat.com> - 1.10.1-4
+- Resolves: #983587 - sss_debuglevel did not increase verbosity in
+                      sssd_pac.log
+
+* Wed Jul 31 2013 Jakub Hrozek <jhrozek@redhat.com> - 1.10.1-3
+- Resolves: #983580 - Netgroups should ignore the 'use_fully_qualified_names'
+                      setting
+
+* Wed Jul 31 2013 Jakub Hrozek <jhrozek@redhat.com> - 1.10.1-2
+- Apply several important fixes from upstream 1.10 branch
+- Related: #966757 - SSSD failover doesn't work if the first DNS server
+                     in resolv.conf is unavailable
+
+* Thu Jul 18 2013 Jakub Hrozek <jhrozek@redhat.com> - 1.10.1-1
+- New upstream release 1.10.1
+- https://fedorahosted.org/sssd/wiki/Releases/Notes-1.10.1
+
+* Wed Jul 10 2013 Jakub Hrozek <jhrozek@redhat.com> - 1.10.0-18
+- Remove libcmocka dependency
+
+* Mon Jul 08 2013 Jakub Hrozek <jhrozek@redhat.com> - 1.10.0-17
+- sssd-tools should require sssd-common, not sssd
+
+* Tue Jul 02 2013 Stephen Gallagher <sgallagh@redhat.com> - 1.10.0-16
+- Move sssd_pac to the sssd-ipa and sssd-ad subpackages
+- Trim out RHEL5-specific macros since we don't build on RHEL 5
+- Trim out macros for Fedora older than F18
+- Update libldb requirement to 1.1.16
+- Trim RPM changelog down to the last year
+
+* Tue Jul 02 2013 Stephen Gallagher <sgallagh@redhat.com> - 1.10.0-15
+- Move sssd_pac to the sssd-krb5 subpackage
+
+* Mon Jul 01 2013 Stephen Gallagher <sgallagh@redhat.com> - 1.10.0-14
+- Fix Obsoletes: to account for dist tag
+- Convert post and pre scripts to run on the sssd-common subpackage
+- Remove old conversion from SYSV
+
+* Thu Jun 27 2013 Jakub Hrozek <jhrozek@redhat.com> - 1.10.0-13
+- New upstream release 1.10
+- https://fedorahosted.org/sssd/wiki/Releases/Notes-1.10.0
+
+* Mon Jun 17 2013 Dan Horák <dan[at]danny.cz> - 1.10.0-12.beta2
+- the cmocka toolkit exists only on selected arches
+
+* Sun Jun 16 2013 Jakub Hrozek <jhrozek@redhat.com> - 1.10.0-11.beta2
+- Apply a number of patches from upstream to fix issues found post-beta,
+  in particular:
+  -- segfault with a high DEBUG level
+  -- Fix IPA password migration (upstream #1873)
+  -- Fix fail over when retrying SRV resolution (upstream #1886)
+
+* Thu Jun 13 2013 Jakub Hrozek <jhrozek@redhat.com> - 1.10.0-10.beta2
+- Only BuildRequire libcmocka on Fedora
+
+* Thu Jun 13 2013 Jakub Hrozek <jhrozek@redhat.com> - 1.10.0-9.beta2
+- Fix typo in Requires that prevented an upgrade (#973916)
+- Use a hardcoded version in Conflicts, not less-than-current
+
+* Wed Jun 12 2013 Jakub Hrozek <jhrozek@redhat.com> - 1.10.0-8.beta2
+- New upstream release 1.10 beta2
+- https://fedorahosted.org/sssd/wiki/Releases/Notes-1.10.0beta2
+- BuildRequire libcmocka-devel in order to run all upstream tests during build
+- BuildRequire libnl3 instead of libnl1
+- No longer BuildRequire initscripts, we no longer use /sbin/service
+- Remove explicit krb5-libs >= 1.10 requires; this platform doensn't carry any
+  older krb5-libs version
+
+* Thu Jun 06 2013 Jakub Hrozek <jhrozek@redhat.com> - 1.10.0-7.beta1
+- Enable hardened build for RHEL7
+
+* Fri May 24 2013 Jakub Hrozek <jhrozek@redhat.com> - 1.10.0-6.beta1
+- Apply a couple of patches from upstream git that resolve crashes when
+  ID mapping object was not initialized properly but needed later
+
+* Tue May 14 2013 Jakub Hrozek <jhrozek@redhat.com> - 1.10.0-5.beta1
+- Resolves: rhbz#961357 - Missing dyndns_update entry in sssd.conf during
+                          realm join
+- Resolves: rhbz#961278 - Login failure: Enterprise Principal enabled by
+                          default for AD Provider
+- Resolves: rhbz#961251 - sssd does not create user's krb5 ccache dir/file
+                          parent directory when logging in
+
+* Tue May  7 2013 Jakub Hrozek <jhrozek@redhat.com> - 1.10.0-4.beta1
+- Explicitly Require libini_config >= 1.0.0.1 to work around a SONAME bug
+  in ding-libs
+- Fix SSH integration with fully-qualified domains
+- Add the ability to dynamically discover the NetBIOS name
+
+* Fri May  3 2013 Jakub Hrozek <jhrozek@redhat.com> - 1.10.0-3.beta1
+- New upstream release 1.10 beta1
+- https://fedorahosted.org/sssd/wiki/Releases/Notes-1.10.0beta1
+
+* Wed Apr 17 2013 Jakub Hrozek <jhrozek@redhat.com> - 1.10.0-2.alpha1
+- Add a patch to fix krb5 ccache creation issue with krb5 1.11
+
+* Tue Apr  2 2013 Jakub Hrozek <jhrozek@redhat.com> - 1.10.0-1.alpha1
+- New upstream release 1.10 alpha1
+- https://fedorahosted.org/sssd/wiki/Releases/Notes-1.10.0alpha1
+
+* Fri Mar 01 2013 Stephen Gallagher <sgallagh@redhat.com> - 1.9.4-9
+- Split internal helper libraries into a shared object
+- Significantly reduce disk-space usage
+
+* Thu Feb 14 2013 Jakub Hrozek <jhrozek@redhat.com> - 1.9.4-8
+- Fix the Kerberos password expiration warning (#912223)
+
+* Thu Feb 14 2013 Jakub Hrozek <jhrozek@redhat.com> - 1.9.4-7
+- Do not write out dots in the domain-realm mapping file (#905650)
+
+* Mon Feb 11 2013 Jakub Hrozek <jhrozek@redhat.com> - 1.9.4-6
+- Include upstream patch to build with krb5-1.11
+
+* Thu Feb 07 2013 Jakub Hrozek <jhrozek@redhat.com> - 1.9.4-5
+- Rebuild against new libldb
+
+* Mon Feb 04 2013 Jakub Hrozek <jhrozek@redhat.com> - 1.9.4-4
+- Fix build with new automake versions
+
+* Wed Jan 30 2013 Jakub Hrozek <jhrozek@redhat.com> - 1.9.4-3
+- Recreate Kerberos ccache directory if it's missing
+- Resolves: rhbz#853558 - [sssd[krb5_child[PID]]]: Credential cache
+                          directory /run/user/UID/ccdir does not exist
+
+* Tue Jan 29 2013 Jakub Hrozek <jhrozek@redhat.com> - 1.9.4-2
+- Fix changelog dates to make F19 rpmbuild happy
+
+* Mon Jan 28 2013 Jakub Hrozek <jhrozek@redhat.com> - 1.9.4-1
+- New upstream release 1.9.4
+
+* Thu Dec 06 2012 Jakub Hrozek <jhrozek@redhat.com> - 1.9.3-1
+- New upstream release 1.9.3
+
+* Tue Oct 30 2012 Jakub Hrozek <jhrozek@redhat.com> - 1.9.2-5
+- Resolve groups from AD correctly
+
+* Tue Oct 30 2012 Jakub Hrozek <jhrozek@redhat.com> - 1.9.2-4
+- Check the validity of naming context
+
+* Thu Oct 18 2012 Jakub Hrozek <jhrozek@redhat.com> - 1.9.2-3
+- Move the sss_cache tool to the main package
+
+* Sun Oct 14 2012 Jakub Hrozek <jhrozek@redhat.com> - 1.9.2-2
+- Include the 1.9.2 tarball
+
+* Sun Oct 14 2012 Jakub Hrozek <jhrozek@redhat.com> - 1.9.2-1
+- New upstream release 1.9.2
+
+* Sun Oct 07 2012 Jakub Hrozek <jhrozek@redhat.com> - 1.9.1-1
+- New upstream release 1.9.1
+
+* Wed Oct 03 2012 Jakub Hrozek <jhrozek@redhat.com> - 1.9.0-24
+- require the latest libldb
+
+* Tue Sep 25 2012 Jakub Hrozek <jhrozek@redhat.com> - 1.9.0-24
+- Use mcpath insted of mcachepath macro to be consistent with
+  upsteam spec file
+
+* Tue Sep 25 2012 Jakub Hrozek <jhrozek@redhat.com> - 1.9.0-23
+- New upstream release 1.9.0
+
+* Fri Sep 14 2012 Jakub Hrozek <jhrozek@redhat.com> - 1.9.0-22.rc1
+- New upstream release 1.9.0 rc1
+
+* Thu Sep 06 2012 Jakub Hrozek <jhrozek@redhat.com> - 1.9.0-21.beta7
+- New upstream release 1.9.0 beta7
+- obsoletes patches #1-#3
+
+* Mon Sep 03 2012 Jakub Hrozek <jhrozek@redhat.com> - 1.9.0-20.beta6
+- Rebuild against libldb 1.12
+
+* Tue Aug 28 2012 Jakub Hrozek <jhrozek@redhat.com> - 1.9.0-19.beta6
+- Rebuild against libldb 1.11
+
+* Fri Aug 24 2012 Jakub Hrozek <jhrozek@redhat.com> - 1.9.0-18.beta6
+- Change the default ccache location to DIR:/run/user/${UID}/krb5cc
+  and patch man page accordingly
+- Resolves: rhbz#851304
+
+* Mon Aug 20 2012 Jakub Hrozek <jhrozek@redhat.com> - 1.9.0-17.beta6
+- Rebuild against libldb 1.10
+
+* Fri Aug 17 2012 Jakub Hrozek <jhrozek@redhat.com> - 1.9.0-16.beta6
+- Only create the SELinux login file if there are SELinux mappings on
+  the IPA server
+
+* Fri Aug 10 2012 Jakub Hrozek <jhrozek@redhat.com> - 1.9.0-14.beta6
+- Don't discard HBAC rule processing result if SELinux is on
+  Resolves: rhbz#846792 (CVE-2012-3462)
+
+* Thu Aug 02 2012 Jakub Hrozek <jhrozek@redhat.com> - 1.9.0-13.beta6
+- New upstream release 1.9.0 beta 6
+- https://fedorahosted.org/sssd/wiki/Releases/Notes-1.9.0beta6
+- A new option, override_shell was added. If this option is set, all users
+  managed by SSSD will have their shell set to its value.
+- Fixes for the support for setting default SELinux user context from FreeIPA.
+- Fixed a regression introduced in beta 5 that broke LDAP SASL binds
+- The SSSD supports the concept of a Primary Server and a Back Up Server in
+  failover
+- A new command-line tool sss_seed is available to help prime the cache with
+  a user record when deploying a new machine
+- SSSD is now able to discover and save the domain-realm mappings
+  between an IPA server and a trusted Active Directory server.
+- Packaging changes to fix ldconfig usage in subpackages (#843995)
+- Rebuild against libldb 1.1.9
+
+* Fri Jul 27 2012 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 1.9.0-13.beta5
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_18_Mass_Rebuild
+
+* Thu Jul 19 2012 Jakub Hrozek <jhrozek@redhat.com> - 1.9.0-12.beta5
+- New upstream release 1.9.0 beta 5
+- Obsoletes the patch for missing DP_OPTION_TERMINATOR in AD provider options
+- https://fedorahosted.org/sssd/wiki/Releases/Notes-1.9.0beta5
+- Many fixes for the support for setting default SELinux user context from
+  FreeIPA, most notably fixed the specificity evaluation
+- Fixed an incorrect default in the krb5_canonicalize option of the AD
+  provider which was preventing password change operation
+- The shadowLastChange attribute value is now correctly updated with the
+  number of days since the Epoch, not seconds
+
+* Mon Jul 16 2012 Stephen Gallagher <sgallagh@redhat.com> - 1.9.0-11.beta4
+- Fix broken ARM build
+- Add missing DP_OPTION_TERMINATOR in AD provider options
+
+* Wed Jul 11 2012 Jakub Hrozek <jhrozek@redhat.com> - 1.9.0-10.beta4
+- Own several directories create during make install (#839782)
+
+* Wed Jul 11 2012 Jakub Hrozek <jhrozek@redhat.com> - 1.9.0-9.beta4
+- New upstream release 1.9.0 beta 4
+- https://fedorahosted.org/sssd/wiki/Releases/Notes-1.9.0beta4
+- Add a new AD provider to improve integration with Active Directory 2008 R2
+  or later servers
+- SUDO integration was completely rewritten. The new implementation works
+  with multiple domains and uses an improved refresh mechanism to download
+  only the necessary rules
+- The IPA authentication provider now supports subdomains
+- Fixed regression for setups that were setting default_tkt_enctypes
+  manually by reverting a previous workaround.
+
+* Mon Jun 25 2012 Stephen Gallagher <sgallagh@redhat.com> - 1.9.0-8.beta3
+- New upstream release 1.9.0 beta 3
+- https://fedorahosted.org/sssd/wiki/Releases/Notes-1.9.0beta3
+- Add a new PAC responder for dealing with cross-realm Kerberos trusts
+- Terminate idle connections to the NSS and PAM responders
+
+* Wed Jun 20 2012 Stephen Gallagher <sgallagh@redhat.com> - 1.9.0-7.beta2
+- Switch unicode library from libunistring to Glib
+- Drop unnecessary explicit Requires on keyutils
+- Guarantee that versioned Requires include the correct architecture
+
+* Mon Jun 18 2012 Stephen Gallagher <sgallagh@redhat.com> - 1.9.0-6.beta2
+- Fix accidental disabling of the DIR cache support
+
+* Fri Jun 15 2012 Stephen Gallagher <sgallagh@redhat.com> - 1.9.0-5.beta2
+- New upstream release 1.9.0 beta 2
+- https://fedorahosted.org/sssd/wiki/Releases/Notes-1.9.0beta2
+- Add support for the Kerberos DIR cache for storing multiple TGTs
+  automatically
+- Major performance enhancement when storing large groups in the cache
+- Major performance enhancement when performing initgroups() against Active
+  Directory
+- SSSDConfig data file default locations can now be set during configure for
+  easier packaging
+
+* Tue May 29 2012 Stephen Gallagher <sgallagh@redhat.com> - 1.9.0-4.beta1
+- Fix regression in endianness patch
+
+* Tue May 29 2012 Stephen Gallagher <sgallagh@redhat.com> - 1.9.0-3.beta1
+- Rebuild SSSD against ding-libs 0.3.0beta1
+- Fix endianness bug in service map protocol
+
+* Thu May 24 2012 Stephen Gallagher <sgallagh@redhat.com> - 1.9.0-2.beta1
+- Fix several regressions since 1.5.x
+- Ensure that the RPM creates the /var/lib/sss/mc directory
+- Add support for Netscape password warning expiration control
+- Rebuild against libldb 1.1.6
+
+* Fri May 11 2012 Stephen Gallagher <sgallagh@redhat.com> - 1.9.0-1.beta1
+- New upstream release 1.9.0 beta 1
+- https://fedorahosted.org/sssd/wiki/Releases/Notes-1.9.0beta1
+- Add native support for autofs to the IPA provider
+- Support for ID-mapping when connecting to Active Directory
+- Support for handling very large (> 1500 users) groups in Active Directory
+- Support for sub-domains (will be used for dealing with trust relationships)
+- Add a new fast in-memory cache to speed up lookups of cached data on
+  repeated requests
+
+* Thu May 03 2012 Stephen Gallagher <sgallagh@redhat.com> - 1.8.3-11
+- New upstream release 1.8.3
+- https://fedorahosted.org/sssd/wiki/Releases/Notes-1.8.3
+- Numerous manpage and translation updates
+- LDAP: Handle situations where the RootDSE isn't available anonymously
+- LDAP: Fix regression for users using non-standard LDAP attributes for user
+  information
+
+* Mon Apr 09 2012 Stephen Gallagher <sgallagh@redhat.com> - 1.8.2-10
+- New upstream release 1.8.2
+- https://fedorahosted.org/sssd/wiki/Releases/Notes-1.8.2
+- Several fixes to case-insensitive domain functions
+- Fix for GSSAPI binds when the keytab contains unrelated principals
+- Fixed several segfaults
+- Workarounds added for LDAP servers with unreadable RootDSE
+- SSH knownhostproxy will no longer enter an infinite loop preventing login
+- The provided SYSV init script now starts SSSD earlier at startup and stops
+  it later during shutdown
+- Assorted minor fixes for issues discovered by static analysis tools
+
+* Mon Mar 26 2012 Stephen Gallagher <sgallagh@redhat.com> - 1.8.1-9
+- Don't duplicate libsss_autofs.so in two packages
+- Set explicit package contents instead of globbing
+
+* Wed Mar 21 2012 Stephen Gallagher <sgallagh@redhat.com> - 1.8.1-8
+- Fix uninitialized value bug causing crashes throughout the code
+- Resolves: rhbz#804783 - [abrt] Segfault during LDAP 'services' lookup
+
+* Mon Mar 12 2012 Stephen Gallagher <sgallagh@redhat.com> - 1.8.1-7
+- New upstream release 1.8.1
+- Resolve issue where we could enter an infinite loop trying to connect to an
+  auth server
+- Fix serious issue with complex (3+ levels) nested groups
+- Fix netgroup support for case-insensitivity and aliases
+- Fix serious issue with lookup bundling resulting in requests never
+  completing
+- IPA provider will now check the value of nsAccountLock during pam_acct_mgmt
+  in addition to pam_authenticate
+- Fix several regressions in the proxy provider
+- Resolves: rhbz#743133 - Performance regression with Kerberos authentication
+                          against AD
+- Resolves: rhbz#799031 - --debug option for sss_debuglevel doesn't work
+
+* Tue Feb 28 2012 Stephen Gallagher <sgallagh@redhat.com> - 1.8.0-6
+- New upstream release 1.8.0
+- Support for the service map in NSS
+- Support for setting default SELinux user context from FreeIPA
+- Support for retrieving SSH user and host keys from LDAP (Experimental)
+- Support for caching autofs LDAP requests (Experimental)
+- Support for caching SUDO rules (Experimental)
+- Include the IPA AutoFS provider
+- Fixed several memory-corruption bugs
+- Fixed a regression in group enumeration since 1.7.0
+- Fixed a regression in the proxy provider
+- Resolves: rhbz#741981 - Separate Cache Timeouts for SSSD
+- Resolves: rhbz#797968 - sssd_be: The requested tar get is not configured is
+                          logged at each login
+- Resolves: rhbz#754114 - [abrt] sssd-1.6.3-1.fc16: ping_check: Process
+                          /usr/sbin/sssd was killed by signal 11 (SIGSEGV)
+- Resolves: rhbz#743133 - Performance regression with Kerberos authentication
+                          against AD
+- Resolves: rhbz#773706 - SSSD fails during autodetection of search bases for
+                          new LDAP features
+- Resolves: rhbz#786957 - sssd and kerberos should change the default location for create the Credential Cashes to /run/usr/USERNAME/krb5cc
+
+* Wed Feb 22 2012 Stephen Gallagher <sgallagh@redhat.com> - 1.8.0-5.beta3
+- Change default kerberos credential cache location to /run/user/<username>
+
+* Wed Feb 15 2012 Stephen Gallagher <sgallagh@redhat.com> - 1.8.0-4.beta3
+- New upstream release 1.8.0 beta 3
+- https://fedorahosted.org/sssd/wiki/Releases/Notes-1.8.0beta3
+- Fixed a regression in group enumeration since 1.7.0
+- Fixed several memory-corruption bugs
+- Finalized the ABI for the autofs support
+- Fixed a regression in the proxy provider
+
+* Fri Feb 10 2012 Petr Pisar <ppisar@redhat.com> - 1.8.0-3.beta2
+- Rebuild against PCRE 8.30
+
+* Mon Feb 06 2012 Stephen Gallagher <sgallagh@redhat.com> - 1.8.0-1.beta2
+- New upstream release
+- https://fedorahosted.org/sssd/wiki/Releases/Notes-1.8.0beta2
+- Fix two minor manpage bugs
+- Include the IPA AutoFS provider
+
+* Mon Feb 06 2012 Stephen Gallagher <sgallagh@redhat.com> - 1.8.0-1.beta1
+- New upstream release
+- https://fedorahosted.org/sssd/wiki/Releases/Notes-1.8.0beta1
+- Support for the service map in NSS
+- Support for setting default SELinux user context from FreeIPA
+- Support for retrieving SSH user and host keys from LDAP (Experimental)
+- Support for caching autofs LDAP requests (Experimental)
+- Support for caching SUDO rules (Experimental)
+
+* Wed Feb 01 2012 Stephen Gallagher <sgallagh@redhat.com> - 1.7.0-5
+- Resolves: rhbz#773706 - SSSD fails during autodetection of search bases for
+                          new LDAP features - fix netgroups and sudo as well
+
+* Wed Feb 01 2012 Stephen Gallagher <sgallagh@redhat.com> - 1.7.0-4
+- Fixes a serious memory hierarchy bug causing unpredictable behavior in the
+  LDAP provider.
+
+* Wed Feb 01 2012 Stephen Gallagher <sgallagh@redhat.com> - 1.7.0-3
+- Resolves: rhbz#773706 - SSSD fails during autodetection of search bases for
+                          new LDAP features
+
+* Sat Jan 14 2012 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 1.7.0-2
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_17_Mass_Rebuild
+
+* Thu Dec 22 2011 Stephen Gallagher <sgallagh@redhat.com> - 1.7.0-1
+- New upstream release 1.7.0
+- https://fedorahosted.org/sssd/wiki/Releases/Notes-1.7.0
+- Support for case-insensitive domains
+- Support for multiple search bases in the LDAP provider
+- Support for the native FreeIPA netgroup implementation
+- Reliability improvements to the process monitor
+- New DEBUG facility with more consistent log levels
+- New tool to change debug log levels without restarting SSSD
+- SSSD will now disconnect from LDAP server when idle
+- FreeIPA HBAC rules can choose to ignore srchost options for significant
+  performance gains
+- Assorted performance improvements in the LDAP provider
+
+* Mon Dec 19 2011 Stephen Gallagher <sgallagh@redhat.com> - 1.6.4-1
+- New upstream release 1.6.4
+- Rolls up previous patches applied to the 1.6.3 tarball
+- Fixes a rare issue causing crashes in the failover logic
+- Fixes an issue where SSSD would return the wrong PAM error code for users
+  that it does not recognize.
+
+* Wed Dec 07 2011 Stephen Gallagher <sgallagh@redhat.com> - 1.6.3-5
+- Rebuild against libldb 1.1.4
+
+* Tue Nov 29 2011 Stephen Gallagher <sgallagh@redhat.com> - 1.6.3-4
+- Resolves: rhbz#753639 - sssd_nss crashes when passed invalid UTF-8 for the
+                          username in getpwnam()
+- Resolves: rhbz#758425 - LDAP failover not working if server refuses
+                          connections
+
+* Thu Nov 24 2011 Jakub Hrozek <jhrozek@redhat.com> - 1.6.3-3
+- Rebuild for libldb 1.1.3
+
+* Thu Nov 10 2011 Stephen Gallagher <sgallagh@redhat.com> - 1.6.3-2
+- Resolves: rhbz#752495 - Crash when apply settings
+
+* Fri Nov 04 2011 Stephen Gallagher <sgallagh@redhat.com> - 1.6.3-1
+- New upstream release 1.6.3
+- https://fedorahosted.org/sssd/wiki/Releases/Notes-1.6.3
+- Fixes a major cache performance issue introduced in 1.6.2
+- Fixes a potential infinite-loop with certain LDAP layouts
+
+* Wed Oct 26 2011 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 1.6.2-5
+- Rebuilt for glibc bug#747377
+
+* Sun Oct 23 2011 Stephen Gallagher <sgallagh@redhat.com> - 1.6.2-4
+- Change selinux policy requirement to Conflicts: with the old version,
+  rather than Requires: the supported version.
+
+* Fri Oct 21 2011 Stephen Gallagher <sgallagh@redhat.com> - 1.6.2-3
+- Add explicit requirement on selinux-policy version to address new SBUS
+  symlinks.
+
+* Wed Oct 19 2011 Stephen Gallagher <sgallagh@redhat.com> - 1.6.2-2
+- Remove %%files reference to sss_debuglevel copied from wrong upstreeam
+  spec file.
+
+* Tue Oct 18 2011 Stephen Gallagher <sgallagh@redhat.com> - 1.6.2-1
+- Improved handling of users and groups with multi-valued name attributes
+  (aliases)
+- Performance enhancements
+    Initgroups on RFC2307bis/FreeIPA
+    HBAC rule processing
+- Improved process-hang detection and restarting
+- Enabled the midpoint cache refresh by default (fewer cache misses on
+  commonly-used entries)
+- Cleaned up the example configuration
+- New tool to change debug level on the fly
+
+* Mon Aug 29 2011 Stephen Gallagher <sgallagh@redhat.com> - 1.6.1-1
+- New upstream release 1.6.1
+- https://fedorahosted.org/sssd/wiki/Releases/Notes-1.6.1
+- Fixes a serious issue with LDAP connections when the communication is
+  dropped (e.g. VPN disconnection, waking from sleep)
+- SSSD is now less strict when dealing with users/groups with multiple names
+  when a definitive primary name cannot be determined
+- The LDAP provider will no longer attempt to canonicalize by default when
+  using SASL. An option to re-enable this has been provided.
+- Fixes for non-standard LDAP attribute names (e.g. those used by Active
+  Directory)
+- Three HBAC regressions have been fixed.
+- Fix for an infinite loop in the deref code
+
+* Wed Aug 03 2011 Stephen Gallagher <sgallagh@redhat.com> - 1.6.0-2
+- Build with _hardened_build macro
+
+* Wed Aug 03 2011 Stephen Gallagher <sgallagh@redhat.com> - 1.6.0-1
+- New upstream release 1.6.0
+- https://fedorahosted.org/sssd/wiki/Releases/Notes-1.6.0
+- Add host access control support for LDAP (similar to pam_host_attr)
+- Finer-grained control on principals used with Kerberos (such as for FAST or
+- validation)
+- Added a new tool sss_cache to allow selective expiring of cached entries
+- Added support for LDAP DEREF and ASQ controls
+- Added access control features for Novell Directory Server
+- FreeIPA dynamic DNS update now checks first to see if an update is needed
+- Complete rewrite of the HBAC library
+- New libraries: libipa_hbac and libipa_hbac-python
+
+* Tue Jul 05 2011 Stephen Gallagher <sgallagh@redhat.com> - 1.5.11-2
+- New upstream release 1.5.11
+- https://fedorahosted.org/sssd/wiki/Releases/Notes-1.5.11
+- Fix a serious regression that prevented SSSD from working with ldaps:// URIs
+- IPA Provider: Fix a bug with dynamic DNS that resulted in the wrong IPv6
+- address being saved to the AAAA record
+
+* Fri Jul 01 2011 Stephen Gallagher <sgallagh@redhat.com> - 1.5.10-1
+- New upstream release 1.5.10
+- https://fedorahosted.org/sssd/wiki/Releases/Notes-1.5.10
+- Fixed a regression introduced in 1.5.9 that could result in blocking calls
+- to LDAP
+
+* Thu Jun 30 2011 Stephen Gallagher <sgallagh@redhat.com> - 1.5.9-1
+- New upstream release 1.5.9
+- https://fedorahosted.org/sssd/wiki/Releases/Notes-1.5.9
+- Support for overriding home directory, shell and primary GID locally
+- Properly honor TTL values from SRV record lookups
+- Support non-POSIX groups in nested group chains (for RFC2307bis LDAP
+- servers)
+- Properly escape IPv6 addresses in the failover code
+- Do not crash if inotify fails (e.g. resource exhaustion)
+- Don't add multiple TGT renewal callbacks (too many log messages)
+
+* Fri May 27 2011 Stephen Gallagher <sgallagh@redhat.com> - 1.5.8-1
+- New upstream release 1.5.8
+- https://fedorahosted.org/sssd/wiki/Releases/Notes-1.5.8
+- Support for the LDAP paging control
+- Support for multiple DNS servers for name resolution
+- Fixes for several group membership bugs
+- Fixes for rare crash bugs
+
+* Mon May 23 2011 Stephen Gallagher <sgallagh@redhat.com> - 1.5.7-3
+- Resolves: rhbz#706740 - Orphaned links on rc0.d-rc6.d
+- Make sure to properly convert to systemd if upgrading from newer
+- updates for Fedora 14
+
+* Mon May 02 2011 Stephen Gallagher <sgallagh@redhat.com> - 1.5.7-2
+- Fix segfault in TGT renewal
+
+* Fri Apr 29 2011 Stephen Gallagher <sgallagh@redhat.com> - 1.5.7-1
+- Resolves: rhbz#700891 - CVE-2011-1758 sssd: automatic TGT renewal overwrites
+-                         cached password with predicatable filename
+
+* Wed Apr 20 2011 Stephen Gallagher <sgallagh@redhat.com> - 1.5.6.1-1
+- Re-add manpage translations
+
+* Wed Apr 20 2011 Stephen Gallagher <sgallagh@redhat.com> - 1.5.6-1
+- New upstream release 1.5.6
+- https://fedorahosted.org/sssd/wiki/Releases/Notes-1.5.6
+- Fixed a serious memory leak in the memberOf plugin
+- Fixed a regression with the negative cache that caused it to be essentially
+- nonfunctional
+- Fixed an issue where the user's full name would sometimes be removed from
+- the cache
+- Fixed an issue with password changes in the kerberos provider not working
+- with kpasswd
+
+* Wed Apr 20 2011 Stephen Gallagher <sgallagh@redhat.com> - 1.5.5-5
+- Resolves: rhbz#697057 - kpasswd fails when using sssd and
+-                         kadmin server != kdc server
+- Upgrades from SysV should now maintain enabled/disabled status
+
+* Mon Apr 18 2011 Stephen Gallagher <sgallagh@redhat.com> - 1.5.5-4
+- Fix %%postun
+
+* Thu Apr 14 2011 Stephen Gallagher <sgallagh@redhat.com> - 1.5.5-3
+- Fix systemd conversion. Upgrades from SysV to systemd weren't properly
+- enabling the systemd service.
+- Fix a serious memory leak in the memberOf plugin
+- Fix an issue where the user's full name would sometimes be removed
+- from the cache
+
+* Tue Apr 12 2011 Stephen Gallagher <sgallagh@redhat.com> - 1.5.5-2
+- Install systemd unit file instead of sysv init script
+
+* Tue Apr 12 2011 Stephen Gallagher <sgallagh@redhat.com> - 1.5.5-1
+- New upstream release 1.5.5
+- https://fedorahosted.org/sssd/wiki/Releases/Notes-1.5.5
+- Fixes for several crash bugs
+- LDAP group lookups will no longer abort if there is a zero-length member
+- attribute
+- Add automatic fallback to 'cn' if the 'gecos' attribute does not exist
+
+* Thu Mar 24 2011 Stephen Gallagher <sgallagh@redhat.com> - 1.5.4-1
+- New upstream release 1.5.4
+- https://fedorahosted.org/sssd/wiki/Releases/Notes-1.5.4
+- Fixes for Active Directory when not all users and groups have POSIX attributes
+- Fixes for handling users and groups that have name aliases (aliases are ignored)
+- Fix group memberships after initgroups in the IPA provider
+
+* Thu Mar 17 2011 Stephen Gallagher <sgallagh@redhat.com> - 1.5.3-2
+- Resolves: rhbz#683267 - sssd 1.5.1-9 breaks AD authentication
+
+* Fri Mar 11 2011 Stephen Gallagher <sgallagh@redhat.com> - 1.5.3-1
+- New upstream release 1.5.3
+- Support for libldb >= 1.0.0
+
+* Thu Mar 10 2011 Stephen Gallagher <sgallagh@redhat.com> - 1.5.2-1
+- New upstream release 1.5.2
+- https://fedorahosted.org/sssd/wiki/Releases/Notes-1.5.2
+- Fixes for support of FreeIPA v2
+- Fixes for failover if DNS entries change
+- Improved sss_obfuscate tool with better interactive mode
+- Fix several crash bugs
+- Don't attempt to use START_TLS over SSL. Some LDAP servers can't handle this
+- Delete users from the local cache if initgroups calls return 'no such user'
+- (previously only worked for getpwnam/getpwuid)
+- Use new Transifex.net translations
+- Better support for automatic TGT renewal (now survives restart)
+- Netgroup fixes
+
+* Sun Feb 27 2011 Simo Sorce <ssorce@redhat.com> - 1.5.1-9
+- Rebuild sssd against libldb 1.0.2 so the memberof module loads again.
+- Related: rhbz#677425
+
+* Mon Feb 21 2011 Stephen Gallagher <sgallagh@redhat.com> - 1.5.1-8
+- Resolves: rhbz#677768 - name service caches names, so id command shows
+-                         recently deleted users
+
+* Fri Feb 11 2011 Stephen Gallagher <sgallagh@redhat.com> - 1.5.1-7
+- Ensure that SSSD builds against libldb-1.0.0 on F15 and later
+- Remove .la for memberOf
+
+* Fri Feb 11 2011 Stephen Gallagher <sgallagh@redhat.com> - 1.5.1-6
+- Fix memberOf install path
+
+* Fri Feb 11 2011 Stephen Gallagher <sgallagh@redhat.com> - 1.5.1-5
+- Add support for libldb 1.0.0
+
+* Wed Feb 09 2011 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 1.5.1-4
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_15_Mass_Rebuild
+
+* Tue Feb 01 2011 Stephen Gallagher <sgallagh@redhat.com> - 1.5.1-3
+- Fix nested group member filter sanitization for RFC2307bis
+- Put translated tool manpages into the sssd-tools subpackage
+
+* Thu Jan 27 2011 Stephen Gallagher <sgallagh@redhat.com> - 1.5.1-2
+- Restore Requires: cyrus-sasl-gssapi as it is not auto-detected during
+- rpmbuild
+
+* Thu Jan 27 2011 Stephen Gallagher <sgallagh@redhat.com> - 1.5.1-1
+- New upstream release 1.5.1
+- Addresses CVE-2010-4341 - DoS in sssd PAM responder can prevent logins
+- Vast performance improvements when enumerate = true
+- All PAM actions will now perform a forced initgroups lookup instead of just
+- a user information lookup
+-   This guarantees that all group information is available to other
+-   providers, such as the simple provider.
+- For backwards-compatibility, DNS lookups will also fall back to trying the
+- SSSD domain name as a DNS discovery domain.
+- Support for more password expiration policies in LDAP
+-    389 Directory Server
+-    FreeIPA
+-    ActiveDirectory
+- Support for ldap_tls_{cert,key,cipher_suite} config options
+-Assorted bugfixes
+
+* Tue Jan 11 2011 Stephen Gallagher <sgallagh@redhat.com> - 1.5.0-2
+- CVE-2010-4341 - DoS in sssd PAM responder can prevent logins
+
+* Wed Dec 22 2010 Stephen Gallagher <sgallagh@redhat.com> - 1.5.0-1
+- New upstream release 1.5.0
+- Fixed issues with LDAP search filters that needed to be escaped
+- Add Kerberos FAST support on platforms that support it
+- Reduced verbosity of PAM_TEXT_INFO messages for cached credentials
+- Added a Kerberos access provider to honor .k5login
+- Addressed several thread-safety issues in the sss_client code
+- Improved support for delayed online Kerberos auth
+- Significantly reduced time between connecting to the network/VPN and
+- acquiring a TGT
+- Added feature for automatic Kerberos ticket renewal
+- Provides the kerberos ticket for long-lived processes or cron jobs
+- even when the user logs out
+- Added several new features to the LDAP access provider
+- Support for 'shadow' access control
+- Support for authorizedService access control
+- Ability to mix-and-match LDAP access control features
+- Added an option for a separate password-change LDAP server for those
+- platforms where LDAP referrals are not supported
+- Added support for manpage translations
+
+
+* Thu Nov 18 2010 Stephen Gallagher <sgallagh@redhat.com> - 1.4.1-3
+- Solve a shutdown race-condition that sometimes left processes running
+- Resolves: rhbz#606887 - SSSD stops on upgrade
+
+* Tue Nov 16 2010 Stephen Gallagher <sgallagh@redhat.com> - 1.4.1-2
+- Log startup errors to the syslog
+- Allow cache cleanup to be disabled in sssd.conf
+
+* Mon Nov 01 2010 Stephen Gallagher <sgallagh@redhat.com> - 1.4.1-1
+- New upstream release 1.4.1
+- Add support for netgroups to the proxy provider
+- Fixes a minor bug with UIDs/GIDs >= 2^31
+- Fixes a segfault in the kerberos provider
+- Fixes a segfault in the NSS responder if a data provider crashes
+- Correctly use sdap_netgroup_search_base
+
+* Mon Oct 18 2010 Stephen Gallagher <sgallagh@redhat.com> - 1.4.0-2
+- Fix incorrect tarball URL
+
+* Mon Oct 18 2010 Stephen Gallagher <sgallagh@redhat.com> - 1.4.0-1
+- New upstream release 1.4.0
+- Added support for netgroups to the LDAP provider
+- Performance improvements made to group processing of RFC2307 LDAP servers
+- Fixed nested group issues with RFC2307bis LDAP servers without a memberOf plugin
+- Build-system improvements to support Gentoo
+- Split out several libraries into the ding-libs tarball
+- Manpage reviewed and updated
+
+* Mon Oct 04 2010 Stephen Gallagher <sgallagh@redhat.com> - 1.3.0-35
+- Fix pre and post script requirements
+
+* Mon Oct 04 2010 Stephen Gallagher <sgallagh@redhat.com> - 1.3.0-34
+- Resolves: rhbz#606887 - sssd stops on upgrade
+
+* Fri Oct 01 2010 Stephen Gallagher <sgallagh@redhat.com> - 1.3.0-33
+- Resolves: rhbz#626205 - Unable to unlock screen
+
+* Tue Sep 28 2010 Stephen Gallagher <sgallagh@redhat.com> - 1.3.0-32
+- Resolves: rhbz#637955 - libini_config-devel needs libcollection-devel but
+-                         doesn't require it
+
+* Thu Sep 16 2010 Stephen Gallagher <sgallagh@redhat.com> - 1.3.0-31
+- Resolves: rhbz#632615 - the krb5 locator plugin isn't packaged for multilib
+
+* Tue Aug 24 2010 Stephen Gallagher <sgallagh@redhat.com> - 1.3.0-30
+- Resolves: CVE-2010-2940 - sssd allows null password entry to authenticate
+-                           against LDAP
+
+* Thu Jul 22 2010 David Malcolm <dmalcolm@redhat.com> - 1.2.91-21
+- Rebuilt for https://fedoraproject.org/wiki/Features/Python_2.7/MassRebuild
+
+* Fri Jul 09 2010 Stephen Gallagher <sgallagh@redhat.com> - 1.2.91-20
+- New upstream version 1.2.91 (1.3.0rc1)
+- Improved LDAP failover
+- Synchronous sysdb API (provides performance enhancements)
+- Better online reconnection detection
+
+* Mon Jun 21 2010 Stephen Gallagher <sgallagh@redhat.com> - 1.2.1-15
+- New stable upstream version 1.2.1
+- Resolves: rhbz#595529 - spec file should eschew %%define in favor of
+-                         %%global
+- Resolves: rhbz#593644 - Empty list of simple_allow_users causes sssd service
+-                         to fail while restart.
+- Resolves: rhbz#599026 - Makefile typo causes SSSD not to use the kernel
+-                         keyring
+- Resolves: rhbz#599724 - sssd is broken on Rawhide
+
+* Mon May 24 2010 Stephen Gallagher <sgallagh@redhat.com> - 1.2.0-12
+- New stable upstream version 1.2.0
+- Support ServiceGroups for FreeIPA v2 HBAC rules
+- Fix long-standing issue with auth_provider = proxy
+- Better logging for TLS issues in LDAP
+
+* Tue May 18 2010 Stephen Gallagher <sgallagh@redhat.com> - 1.1.92-11
+- New LDAP access provider allows for filtering user access by LDAP attribute
+- Reduced default timeout for detecting offline status with LDAP
+- GSSAPI ticket lifetime made configurable
+- Better offline->online transition support in Kerberos
+
+* Fri May 07 2010 Stephen Gallagher <sgallagh@redhat.com> - 1.1.91-10
+- Release new upstream version 1.1.91
+- Enhancements when using SSSD with FreeIPA v2
+- Support for deferred kinit
+- Support for DNS SRV records for failover
+
+* Fri Apr 02 2010 Simo Sorce <ssorce@redhat.com> - 1.1.1-3
+- Bump up release number to avoid library sub-packages version issues with
+  previous releases.
+
+* Thu Apr 01 2010 Stephen Gallagher <sgallagh@redhat.com> - 1.1.1-1
+- New upstream release 1.1.1
+- Fixed the IPA provider (which was segfaulting at start)
+- Fixed a bug in the SSSDConfig API causing some options to revert to
+- their defaults
+- This impacted the Authconfig UI
+- Ensure that SASL binds to LDAP auto-retry when interrupted by a signal
+
+* Tue Mar 23 2010 Stephen Gallagher <sgallagh@redhat.com> - 1.1.0-2
+- Release SSSD 1.1.0 final
+- Fix two potential segfaults
+- Fix memory leak in monitor
+- Better error message for unusable confdb
+
+* Wed Mar 17 2010 Stephen Gallagher <sgallagh@redhat.com> - 1.1.0-1.pre20100317git0ea7f19
+- Release candidate for SSSD 1.1
+- Add simple access provider
+- Create subpackages for libcollection, libini_config, libdhash and librefarray
+- Support IPv6
+- Support LDAP referrals
+- Fix cache issues
+- Better feedback from PAM when offline
+
+* Wed Feb 24 2010 Stephen Gallagehr <sgallagh@redhat.com> - 1.0.5-2
+- Rebuild against new libtevent
+
+* Fri Feb 19 2010 Stephen Gallagher <sgallagh@redhat.com> - 1.0.5-1
+- Fix licenses in sources and on RPMs
+
+* Mon Jan 25 2010 Stephen Gallagher <sgallagh@redhat.com> - 1.0.4-1
+- Fix regression on 64-bit platforms
+
+* Fri Jan 22 2010 Stephen Gallagher <sgallagh@redhat.com> - 1.0.3-1
+- Fixes link error on platforms that do not do implicit linking
+- Fixes double-free segfault in PAM
+- Fixes double-free error in async resolver
+- Fixes support for TCP-based DNS lookups in async resolver
+- Fixes memory alignment issues on ARM processors
+- Manpage fixes
+
+* Thu Jan 14 2010 Stephen Gallagher <sgallagh@redhat.com> - 1.0.2-1
+- Fixes a bug in the failover code that prevented the SSSD from detecting when it went back online
+- Fixes a bug causing long (sometimes multiple-minute) waits for NSS requests
+- Several segfault bugfixes
+
+* Mon Jan 11 2010 Stephen Gallagher <sgallagh@redhat.com> - 1.0.1-1
+- Fix CVE-2010-0014
+
+* Mon Dec 21 2009 Stephen Gallagher <sgallagh@redhat.com> - 1.0.0-2
+- Patch SSSDConfig API to address
+- https://bugzilla.redhat.com/show_bug.cgi?id=549482
+
+* Fri Dec 18 2009 Stephen Gallagher <sgallagh@redhat.com> - 1.0.0-1
+- New upstream stable release 1.0.0
+
+* Fri Dec 11 2009 Stephen Gallagher <sgallagh@redhat.com> - 0.99.1-1
+- New upstream bugfix release 0.99.1
+
+* Mon Nov 30 2009 Stephen Gallagher <sgallagh@redhat.com> - 0.99.0-1
+- New upstream release 0.99.0
+
+* Tue Oct 27 2009 Stephen Gallagher <sgallagh@redhat.com> - 0.7.1-1
+- Fix segfault in sssd_pam when cache_credentials was enabled
+- Update the sample configuration
+- Fix upgrade issues caused by data provider service removal
+
+* Mon Oct 26 2009 Stephen Gallagher <sgallagh@redhat.com> - 0.7.0-2
+- Fix upgrade issues from old (pre-0.5.0) releases of SSSD
+
+* Fri Oct 23 2009 Stephen Gallagher <sgallagh@redhat.com> - 0.7.0-1
+- New upstream release 0.7.0
+
+* Thu Oct 15 2009 Stephen Gallagher <sgallagh@redhat.com> - 0.6.1-2
+- Fix missing file permissions for sssd-clients
+
+* Tue Oct 13 2009 Stephen Gallagher <sgallagh@redhat.com> - 0.6.1-1
+- Add SSSDConfig API
+- Update polish translation for 0.6.0
+- Fix long timeout on ldap operation
+- Make dp requests more robust
+
+* Tue Sep 29 2009 Stephen Gallagher <sgallagh@redhat.com> - 0.6.0-1
+- Ensure that the configuration upgrade script always writes the config
+  file with 0600 permissions
+- Eliminate an infinite loop in group enumerations
+
+* Mon Sep 28 2009 Sumit Bose <sbose@redhat.com> - 0.6.0-0
+- New upstream release 0.6.0
+
+* Mon Aug 24 2009 Simo Sorce <ssorce@redhat.com> - 0.5.0-0
+- New upstream release 0.5.0
+
+* Wed Jul 29 2009 Jakub Hrozek <jhrozek@redhat.com> - 0.4.1-4
+- Fix for CVE-2009-2410 - Native SSSD users with no password set could log in
+  without a password. (Patch by Stephen Gallagher)
+
+* Sun Jul 26 2009 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 0.4.1-3
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_12_Mass_Rebuild
+
+* Mon Jun 22 2009 Simo Sorce <ssorce@redhat.com> - 0.4.1-2
+- Fix a couple of segfaults that may happen on reload
+
+* Thu Jun 11 2009 Simo Sorce <ssorce@redhat.com> - 0.4.1-1
+- add missing configure check that broke stopping the daemon
+- also fix default config to add a missing required option
+
+* Mon Jun  8 2009 Simo Sorce <ssorce@redhat.com> - 0.4.1-0
+- latest upstream release.
+- also add a patch that fixes debugging output (potential segfault)
+
+* Mon Apr 20 2009 Simo Sorce <ssorce@redhat.com> - 0.3.2-2
+- release out of the official 0.3.2 tarball
+
+* Mon Apr 20 2009 Jakub Hrozek <jhrozek@redhat.com> - 0.3.2-1
+- bugfix release 0.3.2
+- includes previous release patches
+- change permissions of the /etc/sssd/sssd.conf to 0600
+
+* Tue Apr 14 2009 Simo Sorce <ssorce@redhat.com> - 0.3.1-2
+- Add last minute bug fixes, found in testing the package
+
+* Mon Apr 13 2009 Simo Sorce <ssorce@redhat.com> - 0.3.1-1
+- Version 0.3.1
+- includes previous release patches
+
+* Mon Apr 13 2009 Simo Sorce <ssorce@redhat.com> - 0.3.0-2
+- Try to fix build adding automake as an explicit BuildRequire
+- Add also a couple of last minute patches from upstream
+
+* Mon Apr 13 2009 Simo Sorce <ssorce@redhat.com> - 0.3.0-1
+- Version 0.3.0
+- Provides file based configuration and lots of improvements
+
+* Tue Mar 10 2009 Simo Sorce <ssorce@redhat.com> - 0.2.1-1
+- Version 0.2.1
+
+* Tue Mar 10 2009 Simo Sorce <ssorce@redhat.com> - 0.2.0-1
+- Version 0.2.0
+
+* Sun Mar 08 2009 Jakub Hrozek <jhrozek@redhat.com> - 0.1.0-5.20090309git691c9b3
+- package git snapshot
+
+* Fri Mar 06 2009 Jakub Hrozek <jhrozek@redhat.com> - 0.1.0-4
+- fixed items found during review
+- added initscript
+
+* Thu Mar 05 2009 Sumit Bose <sbose@redhat.com> - 0.1.0-3
+- added sss_client
+
+* Mon Feb 23 2009 Jakub Hrozek <jhrozek@redhat.com> - 0.1.0-2
+- Small cleanup and fixes in the spec file
+
+* Thu Feb 12 2009 Stephen Gallagher <sgallagh@redhat.com> - 0.1.0-1
+- Initial release (based on version 0.1.0 upstream code)