diff --git a/SOURCES/0001-SYSDB-Skip-malformed-netgroup-attribute.patch b/SOURCES/0001-SYSDB-Skip-malformed-netgroup-attribute.patch
new file mode 100644
index 0000000..a240448
--- /dev/null
+++ b/SOURCES/0001-SYSDB-Skip-malformed-netgroup-attribute.patch
@@ -0,0 +1,36 @@
+From d0eeff900d721a0e147b3513d075dbb64b002dc1 Mon Sep 17 00:00:00 2001
+From: Lukas Slebodnik <lslebodn@redhat.com>
+Date: Tue, 12 Nov 2013 14:39:27 +0100
+Subject: [PATCH 1/6] SYSDB: Skip malformed netgroup attribute.
+
+It was not easy find out why netgroup could not be covert into result entries.
+Problem was that nisNetgroupTriple contained unexpected string "(,user01)"
+This patch will ignore only malformed attribute and processing of netgroup
+will not fail.
+
+Resolves:
+https://fedorahosted.org/sssd/ticket/2137
+---
+ src/db/sysdb_search.c | 6 +++++-
+ 1 file changed, 5 insertions(+), 1 deletion(-)
+
+diff --git a/src/db/sysdb_search.c b/src/db/sysdb_search.c
+index 8dfaf1f2ada22db5e70ebe18bee1ee299e4767dd..d15fc73ce2272bff53650ae9dd0dbdad99a849e6 100644
+--- a/src/db/sysdb_search.c
++++ b/src/db/sysdb_search.c
+@@ -728,7 +728,11 @@ errno_t sysdb_netgr_to_entries(TALLOC_CTX *mem_ctx,
+                                         &tmp_entry[c]->value.triple.username,
+                                         &tmp_entry[c]->value.triple.domainname);
+                     if (ret != EOK) {
+-                        goto done;
++                        DEBUG(SSSDBG_IMPORTANT_INFO,
++                              ("Cannot split netgroup triple [%s], "
++                               "this attribute will be skipped \n",
++                               triple_str));
++                        continue;
+                     }
+ 
+                     c++;
+-- 
+1.8.4.2
+
diff --git a/SOURCES/0002-monitor-Specific-error-message-for-missing-sssd.conf.patch b/SOURCES/0002-monitor-Specific-error-message-for-missing-sssd.conf.patch
new file mode 100644
index 0000000..ea4a22b
--- /dev/null
+++ b/SOURCES/0002-monitor-Specific-error-message-for-missing-sssd.conf.patch
@@ -0,0 +1,90 @@
+From 08134dde5a6c8b23cf40ec8f0020cd553af2667e Mon Sep 17 00:00:00 2001
+From: Pavel Reichl <pavel.reichl@redhat.com>
+Date: Tue, 19 Nov 2013 11:24:31 +0000
+Subject: [PATCH 2/6] monitor: Specific error message for missing sssd.conf
+
+Specific error message is logged for missing sssd.conf file. New sssd specific
+error value is introduced for this case.
+
+Resolves:
+https://fedorahosted.org/sssd/ticket/2156
+---
+ src/confdb/confdb_setup.c | 9 +++++++--
+ src/monitor/monitor.c     | 8 +++++++-
+ src/util/util_errors.c    | 1 +
+ src/util/util_errors.h    | 1 +
+ 4 files changed, 16 insertions(+), 3 deletions(-)
+
+diff --git a/src/confdb/confdb_setup.c b/src/confdb/confdb_setup.c
+index b13553eaa560bb83ecf7a53b32ab116f38f7f480..2a34e4f7a3e5f9aa37760036520f5274e9289b70 100644
+--- a/src/confdb/confdb_setup.c
++++ b/src/confdb/confdb_setup.c
+@@ -155,8 +155,13 @@ int confdb_init_db(const char *config_file, struct confdb_ctx *cdb)
+     /* Open config file */
+     ret = sss_ini_config_file_open(init_data, config_file);
+     if (ret != EOK) {
+-        DEBUG(SSSDBG_CRIT_FAILURE, ("Failed to open configuration file.\n"));
+-        ret = EIO;
++        DEBUG(SSSDBG_TRACE_FUNC,
++              ("sss_ini_config_file_open failed: %s [%d]\n", strerror(ret),
++               ret));
++        if (ret == ENOENT) {
++            /* sss specific error denoting missing configuration file */
++            ret = ERR_MISSING_CONF;
++        }
+         goto done;
+     }
+ 
+diff --git a/src/monitor/monitor.c b/src/monitor/monitor.c
+index 3d8ba26285b6a8fc60ccb695f8b10ae1714ac918..09f530d2643b45fc31fb4dbe3cb69f2abc5510af 100644
+--- a/src/monitor/monitor.c
++++ b/src/monitor/monitor.c
+@@ -1614,7 +1614,7 @@ static errno_t load_configuration(TALLOC_CTX *mem_ctx,
+     ret = confdb_init_db(config_file, ctx->cdb);
+     if (ret != EOK) {
+         DEBUG(0, ("ConfDB initialization has failed [%s]\n",
+-              strerror(ret)));
++              sss_strerror(ret)));
+         goto done;
+     }
+ 
+@@ -2789,6 +2789,12 @@ int main(int argc, const char *argv[])
+     ret = load_configuration(tmp_ctx, config_file, &monitor);
+     if (ret != EOK) {
+         switch (ret) {
++        case ERR_MISSING_CONF:
++            DEBUG(SSSDBG_CRIT_FAILURE,
++                  ("Configuration file: %s does not exist.\n", config_file));
++            sss_log(SSS_LOG_ALERT,
++                    "Configuration file: %s does not exist.\n", config_file);
++            break;
+         case EPERM:
+         case EACCES:
+             DEBUG(SSSDBG_CRIT_FAILURE,
+diff --git a/src/util/util_errors.c b/src/util/util_errors.c
+index b3d10f97b0567ca21ca08a3f2d326ea401c28aff..114c8b04fd354b166d14e526a3bab6a6c0c05951 100644
+--- a/src/util/util_errors.c
++++ b/src/util/util_errors.c
+@@ -50,6 +50,7 @@ struct err_string error_to_str[] = {
+     { "Dynamic DNS update not possible while offline" }, /* ERR_DYNDNS_OFFLINE */
+     { "Entry not found" }, /* ERR_NOT_FOUND */
+     { "Domain not found" }, /* ERR_DOMAIN_NOT_FOUND */
++    { "Missing configuration file" }, /* ERR_MISSING_CONF */
+ };
+ 
+ 
+diff --git a/src/util/util_errors.h b/src/util/util_errors.h
+index 685607c5bb1b4e7c37152c876518a2b1c69c18d6..bca45f392b0357c3f1c848768358cb1d47514715 100644
+--- a/src/util/util_errors.h
++++ b/src/util/util_errors.h
+@@ -72,6 +72,7 @@ enum sssd_errors {
+     ERR_DYNDNS_OFFLINE,
+     ERR_NOT_FOUND,
+     ERR_DOMAIN_NOT_FOUND,
++    ERR_MISSING_CONF,
+     ERR_LAST            /* ALWAYS LAST */
+ };
+ 
+-- 
+1.8.4.2
+
diff --git a/SOURCES/0003-AD-Fix-a-typo-in-the-man-page.patch b/SOURCES/0003-AD-Fix-a-typo-in-the-man-page.patch
new file mode 100644
index 0000000..9baeeb9
--- /dev/null
+++ b/SOURCES/0003-AD-Fix-a-typo-in-the-man-page.patch
@@ -0,0 +1,26 @@
+From ebb9c8acfb2423a2474cbbc52794d41975478dcf Mon Sep 17 00:00:00 2001
+From: Jakub Hrozek <jhrozek@redhat.com>
+Date: Mon, 18 Nov 2013 11:05:04 +0100
+Subject: [PATCH 3/6] AD: Fix a typo in the man page
+
+https://fedorahosted.org/sssd/ticket/2154
+---
+ src/man/sssd-ad.5.xml | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/src/man/sssd-ad.5.xml b/src/man/sssd-ad.5.xml
+index 3dcf2c7390e89cacec202f49c559eccf03800450..e31f87a96a14907c64166e53da443ad735c6e85e 100644
+--- a/src/man/sssd-ad.5.xml
++++ b/src/man/sssd-ad.5.xml
+@@ -175,7 +175,7 @@ ldap_id_mapping = False
+                             This option specifies LDAP access control
+                             filter that the user must match in order
+                             to be allowed access. Please note that the
+-                            <quote>access_filter</quote> option must be
++                            <quote>access_provider</quote> option must be
+                             explicitly set to <quote>ad</quote> in order
+                             for this option to have an effect.
+                         </para>
+-- 
+1.8.4.2
+
diff --git a/SOURCES/0004-LDAP-Initialize-user-count-for-AD-matching-rule.patch b/SOURCES/0004-LDAP-Initialize-user-count-for-AD-matching-rule.patch
new file mode 100644
index 0000000..10ffd1a
--- /dev/null
+++ b/SOURCES/0004-LDAP-Initialize-user-count-for-AD-matching-rule.patch
@@ -0,0 +1,29 @@
+From 44d60762a2ffe45b2dadf05634eefb2af2e3ce14 Mon Sep 17 00:00:00 2001
+From: Jakub Hrozek <jhrozek@redhat.com>
+Date: Mon, 18 Nov 2013 16:38:34 +0100
+Subject: [PATCH 4/6] LDAP: Initialize user count for AD matching rule
+
+https://fedorahosted.org/sssd/ticket/2157
+
+If AD matching rule was selected, but the group was empty, the SSSD
+accessed random data. Initializing count to zero prevents that.
+---
+ src/providers/ldap/sdap_async_groups.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/src/providers/ldap/sdap_async_groups.c b/src/providers/ldap/sdap_async_groups.c
+index 7a8f3e2a5c83c5b320497a76c363a90620315dcf..9f7e3e55d0234e9aa7b9e59456044587bcad88ef 100644
+--- a/src/providers/ldap/sdap_async_groups.c
++++ b/src/providers/ldap/sdap_async_groups.c
+@@ -1828,7 +1828,7 @@ static void sdap_ad_match_rule_members_process(struct tevent_req *subreq)
+     struct sysdb_attrs *group = state->groups[0];
+     struct ldb_message_element *member_el;
+     struct ldb_message_element *orig_dn_el;
+-    size_t count;
++    size_t count = 0;
+     size_t i;
+     hash_table_t *ghosts;
+ 
+-- 
+1.8.4.2
+
diff --git a/SOURCES/0005-SSSD-Improved-domain-detection.patch b/SOURCES/0005-SSSD-Improved-domain-detection.patch
new file mode 100644
index 0000000..7ae4898
--- /dev/null
+++ b/SOURCES/0005-SSSD-Improved-domain-detection.patch
@@ -0,0 +1,165 @@
+From 3cf1217a277d1103a8956e33fc0a8464227e2dd2 Mon Sep 17 00:00:00 2001
+From: Pavel Reichl <pavel.reichl@redhat.com>
+Date: Thu, 14 Nov 2013 21:34:51 +0000
+Subject: [PATCH 5/6] SSSD: Improved domain detection
+
+A bit more elegant way of detection of what domain the group member belongs to
+
+Resolves:
+https://fedorahosted.org/sssd/ticket/2132
+---
+ src/providers/ldap/ldap_common.c | 39 ++++++++++++++++++++++++++++-----------
+ src/util/sss_ldap.c              | 28 +++++++++++++++++++++++-----
+ src/util/sss_ldap.h              |  6 ++++++
+ 3 files changed, 57 insertions(+), 16 deletions(-)
+
+diff --git a/src/providers/ldap/ldap_common.c b/src/providers/ldap/ldap_common.c
+index facf102edc792c75a563a276f3ea9f3acc3052b4..35ea81360b4ec61eca6b952cd86fc93a6eda17dc 100644
+--- a/src/providers/ldap/ldap_common.c
++++ b/src/providers/ldap/ldap_common.c
+@@ -68,23 +68,40 @@ sdap_domain_get_by_dn(struct sdap_options *opts,
+                       const char *dn)
+ {
+     struct sdap_domain *sditer = NULL;
+-    char *dc = NULL;
++    struct sdap_domain *sdmatch = NULL;
++    TALLOC_CTX *tmp_ctx = NULL;
++    int match_len;
++    int best_match_len = 0;
+ 
+-    dc = strstr(dn, "dc=");
+-    if (dc == NULL) {
+-        dc = strstr(dn, "DC=");
+-        if (dc == NULL) {
+-            return NULL;
+-        }
++    tmp_ctx = talloc_new(NULL);
++    if (tmp_ctx == NULL) {
++        return NULL;
+     }
+ 
+     DLIST_FOR_EACH(sditer, opts->sdom) {
+-        if (strcasecmp(sditer->basedn, dc) == 0) {
+-            return sditer;
++        if (sss_ldap_dn_in_search_bases_len(tmp_ctx, dn, sditer->search_bases,
++                                            NULL, &match_len)
++            || sss_ldap_dn_in_search_bases_len(tmp_ctx, dn,
++                   sditer->user_search_bases, NULL, &match_len)
++            || sss_ldap_dn_in_search_bases_len(tmp_ctx, dn,
++                   sditer->group_search_bases, NULL, &match_len)
++            || sss_ldap_dn_in_search_bases_len(tmp_ctx, dn,
++                   sditer->netgroup_search_bases, NULL, &match_len)
++            || sss_ldap_dn_in_search_bases_len(tmp_ctx, dn,
++                   sditer->sudo_search_bases, NULL, &match_len)
++            || sss_ldap_dn_in_search_bases_len(tmp_ctx, dn,
++                   sditer->service_search_bases, NULL, &match_len)
++            || sss_ldap_dn_in_search_bases_len(tmp_ctx, dn,
++                   sditer->autofs_search_bases, NULL, &match_len)) {
++            if (best_match_len < match_len) {
++                /*this is a longer match*/
++                best_match_len = match_len;
++                sdmatch = sditer;
++            }
+         }
+     }
+-
+-    return NULL;
++    talloc_free(tmp_ctx);
++    return sdmatch;
+ }
+ 
+ errno_t
+diff --git a/src/util/sss_ldap.c b/src/util/sss_ldap.c
+index 6d7b0907ca2fa48d9cff5257ab6bbba0ae7dd5c6..e1a05e8f60afb692ac95c99a443febac72a31187 100644
+--- a/src/util/sss_ldap.c
++++ b/src/util/sss_ldap.c
+@@ -470,10 +470,13 @@ int sss_ldap_init_recv(struct tevent_req *req, LDAP **ldap, int *sd)
+  * _filter will contain combined filters from all possible search bases
+  * or NULL if it should be empty
+  */
+-bool sss_ldap_dn_in_search_bases(TALLOC_CTX *mem_ctx,
+-                                 const char *dn,
+-                                 struct sdap_search_base **search_bases,
+-                                 char **_filter)
++
++
++bool sss_ldap_dn_in_search_bases_len(TALLOC_CTX *mem_ctx,
++                                     const char *dn,
++                                     struct sdap_search_base **search_bases,
++                                     char **_filter,
++                                     int *_match_len)
+ {
+     struct sdap_search_base *base;
+     int basedn_len, dn_len;
+@@ -484,6 +487,7 @@ bool sss_ldap_dn_in_search_bases(TALLOC_CTX *mem_ctx,
+     bool backslash_found = false;
+     char *filter = NULL;
+     bool ret = false;
++    int match_len;
+ 
+     if (dn == NULL) {
+         DEBUG(SSSDBG_FUNC_DATA, ("dn is NULL\n"));
+@@ -511,6 +515,7 @@ bool sss_ldap_dn_in_search_bases(TALLOC_CTX *mem_ctx,
+         if (!base_confirmed) {
+             continue;
+         }
++        match_len = basedn_len;
+ 
+         switch (base->scope) {
+         case LDAP_SCOPE_BASE:
+@@ -558,6 +563,9 @@ bool sss_ldap_dn_in_search_bases(TALLOC_CTX *mem_ctx,
+          *  Append filter otherwise.
+          */
+         ret = true;
++        if (_match_len) {
++            *_match_len = match_len;
++        }
+ 
+         if (base->filter == NULL || _filter == NULL) {
+             goto done;
+@@ -575,7 +583,8 @@ bool sss_ldap_dn_in_search_bases(TALLOC_CTX *mem_ctx,
+         if (filter != NULL) {
+             *_filter = talloc_asprintf(mem_ctx, "(|%s)", filter);
+             if (*_filter == NULL) {
+-                DEBUG(SSSDBG_CRIT_FAILURE, ("talloc_asprintf_append() failed\n"));
++                DEBUG(SSSDBG_CRIT_FAILURE,
++                      ("talloc_asprintf_append() failed\n"));
+                 ret = false;
+                 goto done;
+             }
+@@ -589,6 +598,15 @@ done:
+     return ret;
+ }
+ 
++bool sss_ldap_dn_in_search_bases(TALLOC_CTX *mem_ctx,
++                                 const char *dn,
++                                 struct sdap_search_base **search_bases,
++                                 char **_filter)
++{
++    return sss_ldap_dn_in_search_bases_len(mem_ctx, dn, search_bases, _filter,
++                                           NULL);
++}
++
+ char *sss_ldap_encode_ndr_uint32(TALLOC_CTX *mem_ctx, uint32_t flags)
+ {
+     char hex[9]; /* 4 bytes in hex + terminating zero */
+diff --git a/src/util/sss_ldap.h b/src/util/sss_ldap.h
+index e5c30eb2115d422ef5a52cc5cd75c85be8fbe2d7..f298b2fbb30cf1532f8e94504ffb83ef73880b81 100644
+--- a/src/util/sss_ldap.h
++++ b/src/util/sss_ldap.h
+@@ -74,6 +74,12 @@ bool sss_ldap_dn_in_search_bases(TALLOC_CTX *mem_ctx,
+                                  struct sdap_search_base **search_bases,
+                                  char **_filter);
+ 
++bool sss_ldap_dn_in_search_bases_len(TALLOC_CTX *mem_ctx,
++                                     const char *dn,
++                                     struct sdap_search_base **search_bases,
++                                     char **_filter,
++                                     int *_match_len);
++
+ char *sss_ldap_encode_ndr_uint32(TALLOC_CTX *mem_ctx, uint32_t flags);
+ 
+ #endif /* __SSS_LDAP_H__ */
+-- 
+1.8.4.2
+
diff --git a/SOURCES/0006-SSSD-Unit-test-sss_ldap_dn_in_search_bases.patch b/SOURCES/0006-SSSD-Unit-test-sss_ldap_dn_in_search_bases.patch
new file mode 100644
index 0000000..e0555b3
--- /dev/null
+++ b/SOURCES/0006-SSSD-Unit-test-sss_ldap_dn_in_search_bases.patch
@@ -0,0 +1,260 @@
+From 94c85df2d7ded82f2939d8fe29821e4c78ff000d Mon Sep 17 00:00:00 2001
+From: Pavel Reichl <pavel.reichl@redhat.com>
+Date: Thu, 14 Nov 2013 21:52:26 +0000
+Subject: [PATCH 6/6] SSSD: Unit test - sss_ldap_dn_in_search_bases
+
+Unit test testing detection of the right domain when processing group with members from several domains
+
+Resolves:
+https://fedorahosted.org/sssd/ticket/2132
+---
+ Makefile.am                          |  27 ++++-
+ src/tests/cmocka/test_search_bases.c | 191 +++++++++++++++++++++++++++++++++++
+ 2 files changed, 217 insertions(+), 1 deletion(-)
+ create mode 100644 src/tests/cmocka/test_search_bases.c
+
+diff --git a/Makefile.am b/Makefile.am
+index 2ba1ec0fd94e3292f05de0139d607b3626b5c6f7..583ccdb499306268640bfb894f673c42945e19ff 100644
+--- a/Makefile.am
++++ b/Makefile.am
+@@ -151,7 +151,8 @@ if HAVE_CMOCKA
+         fqnames-tests \
+         test_sss_idmap \
+         test_utils \
+-        ad_access_filter_tests
++        ad_access_filter_tests \
++        test_search_bases
+ endif
+ 
+ check_PROGRAMS = \
+@@ -1367,6 +1368,30 @@ test_utils_LDADD = \
+     $(SSSD_INTERNAL_LTLIBS) \
+     libsss_test_common.la
+ 
++test_search_bases_SOURCES = \
++    $(sssd_be_SOURCES) \
++    src/util/sss_ldap.c \
++    src/util/sss_krb5.c \
++    src/util/find_uid.c \
++    src/util/user_info_msg.c \
++    src/tests/cmocka/test_search_bases.c
++test_search_bases_CFLAGS = \
++    $(AM_CFLAGS) \
++    -DUNIT_TESTING
++test_search_bases_LDADD = \
++    $(PAM_LIBS) \
++    $(CMOCKA_LIBS) \
++    $(POPT_LIBS) \
++    $(SSSD_LIBS) \
++    $(CARES_LIBS) \
++    $(KRB5_LIBS) \
++    $(SSSD_INTERNAL_LTLIBS) \
++    $(SYSTEMD_LOGIN_LIBS) \
++    libsss_ldap_common.la \
++    libsss_idmap.la \
++    libsss_krb5_common.la \
++    libsss_test_common.la
++
+ ad_access_filter_tests_SOURCES = \
+     $(sssd_be_SOURCES) \
+     src/util/sss_ldap.c \
+diff --git a/src/tests/cmocka/test_search_bases.c b/src/tests/cmocka/test_search_bases.c
+new file mode 100644
+index 0000000000000000000000000000000000000000..e03ef3662685d92335bce4a7023e1ac7e64432c8
+--- /dev/null
++++ b/src/tests/cmocka/test_search_bases.c
+@@ -0,0 +1,191 @@
++/*
++    Authors:
++        Pavel Reichl <preichl@redhat.com>
++
++    Copyright (C) 2013 Red Hat
++
++    SSSD tests - Search bases
++
++    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 <stdlib.h>
++#include <stddef.h>
++#include <setjmp.h>
++#include <unistd.h>
++#include <sys/types.h>
++#include <cmocka.h>
++#include <ldap.h>
++
++#include "util/find_uid.h"
++#include "util/sss_ldap.h"
++#include "tests/common.h"
++#include "providers/ldap/ldap_common.h"
++#include "providers/ldap/sdap.h"
++#include "dhash.h"
++#include "tests/common_check.h"
++
++enum sss_test_get_by_dn {
++    DN_NOT_IN_DOMS, /* dn is not in any domain           */
++    DN_IN_DOM1,     /* dn is in the domain based on dns  */
++    DN_IN_DOM2,     /* dn is in the domain based on dns2 */
++};
++
++static struct sdap_search_base** generate_bases(TALLOC_CTX *mem_ctx,
++                                                const char** dns, size_t n)
++{
++    struct sdap_search_base **search_bases;
++    errno_t err;
++    int i;
++
++    search_bases = talloc_array(mem_ctx, struct sdap_search_base *, n + 1);
++    assert_non_null(search_bases);
++
++    for (i=0; i < n; ++i) {
++        err = sdap_create_search_base(mem_ctx, dns[i], LDAP_SCOPE_SUBTREE,
++                                      NULL, &search_bases[i]);
++        if (err != EOK) {
++            fprintf(stderr, "Failed to create search base\n");
++        }
++        assert_int_equal(err, EOK);
++    }
++    search_bases[n] = NULL;
++    return search_bases;
++}
++
++static bool do_test_search_bases(const char* dn, const char** dns, size_t n)
++{
++    TALLOC_CTX *tmp_ctx;
++    struct sdap_search_base **search_bases;
++    bool ret;
++
++    tmp_ctx = talloc_new(NULL);
++    assert_non_null(tmp_ctx);
++
++    search_bases = generate_bases(tmp_ctx, dns, n);
++    check_leaks_push(tmp_ctx);
++    ret = sss_ldap_dn_in_search_bases(tmp_ctx, dn, search_bases, NULL);
++    assert_true(check_leaks_pop(tmp_ctx) == true);
++
++    talloc_free(tmp_ctx);
++    return ret;
++}
++
++void test_search_bases_fail(void **state)
++{
++    const char *dn = "cn=user, dc=sub, dc=ad, dc=pb";
++    const char *dns[] = {"dc=example, dc=com", "dc=subdom, dc=ad, dc=pb"};
++    bool ret;
++
++    ret = do_test_search_bases(dn, dns, 2);
++    assert_false(ret);
++}
++
++void test_search_bases_success(void **state)
++{
++    const char *dn = "cn=user, dc=sub, dc=ad, dc=pb";
++    const char *dns[] = {"", "dc=ad, dc=pb", "dc=sub, dc=ad, dc=pb"};
++    bool ret;
++
++    ret = do_test_search_bases(dn, dns, 3);
++    assert_true(ret);
++}
++
++static void do_test_get_by_dn(const char *dn, const char **dns, size_t n,
++                              const char **dns2, size_t n2, int expected_result)
++{
++    TALLOC_CTX *tmp_ctx;
++    struct sdap_options *opts;
++    struct sdap_domain *sdom;
++    struct sdap_domain *sdom2;
++    struct sdap_domain *res_sdom;
++    struct sdap_search_base **search_bases;
++    struct sdap_search_base **search_bases2;
++    tmp_ctx = talloc_new(NULL);
++    assert_non_null(tmp_ctx);
++
++    search_bases = generate_bases(tmp_ctx, dns, n);
++    search_bases2 = generate_bases(tmp_ctx, dns2, n2);
++    sdom = talloc_zero(tmp_ctx, struct sdap_domain);
++    assert_non_null(sdom);
++    sdom2 = talloc_zero(tmp_ctx, struct sdap_domain);
++    assert_non_null(sdom2);
++
++    sdom->search_bases = search_bases;
++    sdom->next = sdom2;
++    sdom->prev = NULL;
++    sdom2->search_bases = search_bases2;
++    sdom2->next = NULL;
++    sdom2->prev = sdom;
++
++    opts = talloc(tmp_ctx, struct sdap_options);
++    assert_non_null(opts);
++    opts->sdom = sdom;
++    res_sdom = sdap_domain_get_by_dn(opts, dn);
++
++    switch (expected_result) {
++    case DN_NOT_IN_DOMS:
++        assert_null(res_sdom);
++        break;
++    case DN_IN_DOM1:
++        assert_true(res_sdom == sdom);
++        break;
++    case DN_IN_DOM2:
++        assert_true(res_sdom == sdom2);
++        break;
++    }
++
++    talloc_free(tmp_ctx);
++}
++
++void test_get_by_dn(void **state)
++{
++    const char *dn = "cn=user, dc=sub, dc=ad, dc=pb";
++    const char *dns[] = {"dc=ad, dc=pb"};
++    const char *dns2[] = {"dc=sub, dc=ad, dc=pb"};
++
++    do_test_get_by_dn(dn, dns, 1, dns2, 1, DN_IN_DOM2);
++}
++
++void test_get_by_dn2(void **state)
++{
++    const char *dn = "cn=user, dc=ad, dc=com";
++    const char *dns[] = {"dc=ad, dc=com"};
++    const char *dns2[] = {"dc=sub, dc=ad, dc=pb"};
++
++    do_test_get_by_dn(dn, dns, 1, dns2, 1, DN_IN_DOM1);
++}
++
++void test_get_by_dn_fail(void **state)
++{
++    const char *dn = "cn=user, dc=sub, dc=example, dc=com";
++    const char *dns[] = {"dc=ad, dc=pb"};
++    const char *dns2[] = {"dc=sub, dc=ad, dc=pb"};
++
++    do_test_get_by_dn(dn, dns, 1, dns2, 1, DN_NOT_IN_DOMS);
++}
++
++int main(void)
++{
++    const UnitTest tests[] = {
++        unit_test(test_search_bases_fail),
++        unit_test(test_search_bases_success),
++        unit_test(test_get_by_dn_fail),
++        unit_test(test_get_by_dn),
++        unit_test(test_get_by_dn2)
++     };
++
++    return run_tests(tests);
++}
+-- 
+1.8.4.2
+
diff --git a/SOURCES/0007-do-not-use-default_domain_suffix-with-autofs.patch b/SOURCES/0007-do-not-use-default_domain_suffix-with-autofs.patch
new file mode 100644
index 0000000..d10d47d
--- /dev/null
+++ b/SOURCES/0007-do-not-use-default_domain_suffix-with-autofs.patch
@@ -0,0 +1,25 @@
+From b7c339c616e88c0e8db5c5a653dacdedf19a147a Mon Sep 17 00:00:00 2001
+From: Aron Parsons <parsonsa@bit-sys.com>
+Date: Wed, 6 Nov 2013 15:18:54 +0000
+Subject: [PATCH 7/7] do not use default_domain_suffix with autofs
+
+---
+ src/responder/autofs/autofssrv_cmd.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/src/responder/autofs/autofssrv_cmd.c b/src/responder/autofs/autofssrv_cmd.c
+index e9168ea8f85cbaf9e934b44838d17360024a2bc1..4c5bda01d4abd967e8cd8260904f34546b898d99 100644
+--- a/src/responder/autofs/autofssrv_cmd.c
++++ b/src/responder/autofs/autofssrv_cmd.c
+@@ -435,7 +435,7 @@ setautomntent_send(TALLOC_CTX *mem_ctx,
+     state->dctx = dctx;
+ 
+     ret = sss_parse_name_for_domains(state, client->rctx->domains,
+-                                     client->rctx->default_domain, rawname,
++                                     NULL, rawname,
+                                      &domname, &state->mapname);
+     if (ret != EOK) {
+         DEBUG(SSSDBG_FATAL_FAILURE,
+-- 
+1.8.4.2
+
diff --git a/SOURCES/0008-SYSDB-Sanitize-filter-before-sysdb_search_groups.patch b/SOURCES/0008-SYSDB-Sanitize-filter-before-sysdb_search_groups.patch
new file mode 100644
index 0000000..c51c590
--- /dev/null
+++ b/SOURCES/0008-SYSDB-Sanitize-filter-before-sysdb_search_groups.patch
@@ -0,0 +1,63 @@
+From bd24c6f485ac1421053167eabd6e5e963829403b Mon Sep 17 00:00:00 2001
+From: Lukas Slebodnik <lslebodn@redhat.com>
+Date: Mon, 25 Nov 2013 13:43:30 +0100
+Subject: [PATCH 8/9] SYSDB: Sanitize filter before sysdb_search_groups
+
+sysdb_delete_user fails with EIO if user does not exist and contains
+backslashes.
+ldb could not parse filter (&(objectclass=group)(ghost=usr\\\\001)),
+because ghost value was not sanitized
+
+Resolves:
+https://fedorahosted.org/sssd/ticket/2163
+---
+ src/db/sysdb_ops.c      | 9 ++++++++-
+ src/tests/sysdb-tests.c | 5 +++++
+ 2 files changed, 13 insertions(+), 1 deletion(-)
+
+diff --git a/src/db/sysdb_ops.c b/src/db/sysdb_ops.c
+index 094c27b7f478e0a53a3b6666c727e86eb36a249e..eb88cd256d0c2e45e1528e8a867e42354215cc7f 100644
+--- a/src/db/sysdb_ops.c
++++ b/src/db/sysdb_ops.c
+@@ -2539,6 +2539,7 @@ int sysdb_delete_user(struct sysdb_ctx *sysdb,
+     struct ldb_message *msg;
+     int ret;
+     int i;
++    char *sanitized_name;
+ 
+     tmp_ctx = talloc_new(NULL);
+     if (!tmp_ctx) {
+@@ -2578,7 +2579,13 @@ int sysdb_delete_user(struct sysdb_ctx *sysdb,
+         }
+     } else if (ret == ENOENT && name != NULL) {
+         /* Perhaps a ghost user? */
+-        filter = talloc_asprintf(tmp_ctx, "(%s=%s)", SYSDB_GHOST, name);
++        ret = sss_filter_sanitize(tmp_ctx, name, &sanitized_name);
++        if (ret != EOK) {
++            goto fail;
++        }
++
++        filter = talloc_asprintf(tmp_ctx, "(%s=%s)",
++                                          SYSDB_GHOST, sanitized_name);
+         if (filter == NULL) {
+             ret = ENOMEM;
+             goto fail;
+diff --git a/src/tests/sysdb-tests.c b/src/tests/sysdb-tests.c
+index 1c28526e06df012b8749e1540e70a27948c17ab2..bf964fd76d33bbceac6c1846db7a5011db1375f5 100644
+--- a/src/tests/sysdb-tests.c
++++ b/src/tests/sysdb-tests.c
+@@ -3998,6 +3998,11 @@ START_TEST(test_odd_characters)
+     fail_unless(ret == EOK, "sysdb_delete_user error [%d][%s]",
+                             ret, strerror(ret));
+ 
++    /* Delete non existing User */
++    ret = sysdb_delete_user(test_ctx->sysdb, test_ctx->domain,
++                            odd_username, 10000);
++    fail_unless(ret == ENOENT, "sysdb_delete_user error [%d][%s]",
++                               ret, strerror(ret));
+ 
+     /* Delete Group */
+     ret = sysdb_delete_group(test_ctx->sysdb, test_ctx->domain,
+-- 
+1.8.4.2
+
diff --git a/SOURCES/0009-SYSDB-Sanitize-filter-before-removing-ghost-attrs.patch b/SOURCES/0009-SYSDB-Sanitize-filter-before-removing-ghost-attrs.patch
new file mode 100644
index 0000000..ac80bac
--- /dev/null
+++ b/SOURCES/0009-SYSDB-Sanitize-filter-before-removing-ghost-attrs.patch
@@ -0,0 +1,85 @@
+From 0a509d518dd5d17e32e3a4c34b319a38210ba17b Mon Sep 17 00:00:00 2001
+From: Lukas Slebodnik <lslebodn@redhat.com>
+Date: Mon, 25 Nov 2013 16:01:59 +0100
+Subject: [PATCH 9/9] SYSDB: Sanitize filter before removing ghost attrs
+
+sysdb_add_user fails with EIO if enumeration is disabled and user contains
+backslashes.
+We try to remove ghost attributes from groups with disabled enumeration,
+but unsanitized filter is used to find ghost attributes
+"(|(ghost=usr\\\\002)" and ldb cannot parse this filter.
+
+Resolves:
+https://fedorahosted.org/sssd/ticket/2163
+---
+ src/db/sysdb_ops.c      |  9 ++++++++-
+ src/tests/sysdb-tests.c | 19 +++++++++++++++++++
+ 2 files changed, 27 insertions(+), 1 deletion(-)
+
+diff --git a/src/db/sysdb_ops.c b/src/db/sysdb_ops.c
+index eb88cd256d0c2e45e1528e8a867e42354215cc7f..890bf1eb3cc5fc0b6eb6f7a145aee6d87945cd8d 100644
+--- a/src/db/sysdb_ops.c
++++ b/src/db/sysdb_ops.c
+@@ -1091,6 +1091,7 @@ sysdb_remove_ghostattr_from_groups(struct sysdb_ctx *sysdb,
+     struct ldb_dn *tmpdn;
+     const char *group_attrs[] = {SYSDB_NAME, SYSDB_GHOST, SYSDB_ORIG_MEMBER, NULL};
+     const char *userdn;
++    char *sanitized_name;
+     char *filter;
+     errno_t ret = EOK;
+     size_t group_count = 0;
+@@ -1101,7 +1102,13 @@ sysdb_remove_ghostattr_from_groups(struct sysdb_ctx *sysdb,
+         return ENOENT;
+     }
+ 
+-    filter = talloc_asprintf(tmp_ctx, "(|(%s=%s)", SYSDB_GHOST, name);
++    ret = sss_filter_sanitize(tmp_ctx, name, &sanitized_name);
++    if (ret != EOK) {
++        goto done;
++    }
++
++    filter = talloc_asprintf(tmp_ctx, "(|(%s=%s)",
++                                      SYSDB_GHOST, sanitized_name);
+     if (!filter) {
+         ret = ENOMEM;
+         goto done;
+diff --git a/src/tests/sysdb-tests.c b/src/tests/sysdb-tests.c
+index bf964fd76d33bbceac6c1846db7a5011db1375f5..ddbf6f28fd5024945fedcb3c6e2122948c4f1459 100644
+--- a/src/tests/sysdb-tests.c
++++ b/src/tests/sysdb-tests.c
+@@ -3900,6 +3900,8 @@ START_TEST(test_odd_characters)
+     struct ldb_message *msg;
+     const struct ldb_val *val;
+     const char odd_username[] = "*(odd)\\user,name";
++    const char odd_username_orig_dn[] =
++        "\\2a\\28odd\\29\\5cuser,name,cn=users,dc=example,dc=com";
+     const char odd_groupname[] = "*(odd\\*)\\group,name";
+     const char odd_netgroupname[] = "*(odd\\*)\\netgroup,name";
+     const char *received_user;
+@@ -4010,6 +4012,23 @@ START_TEST(test_odd_characters)
+     fail_unless(ret == EOK, "sysdb_delete_group error [%d][%s]",
+                             ret, strerror(ret));
+ 
++    /* Add */
++    ret = sysdb_add_user(test_ctx->sysdb,
++                         test_ctx->domain,
++                         odd_username,
++                         10000, 0,
++                         "","","",
++                         odd_username_orig_dn,
++                         NULL, 5400, 0);
++    fail_unless(ret == EOK, "sysdb_add_user error [%d][%s]",
++                            ret, strerror(ret));
++
++    /* Delete User */
++    ret = sysdb_delete_user(test_ctx->sysdb, test_ctx->domain,
++                            odd_username, 10000);
++    fail_unless(ret == EOK, "sysdb_delete_user error [%d][%s]",
++                            ret, strerror(ret));
++
+     /* ===== Netgroups ===== */
+     /* Add */
+     ret = sysdb_add_netgroup(test_ctx->sysdb, test_ctx->domain,
+-- 
+1.8.4.2
+
diff --git a/SOURCES/0010-LDAP-Split-out-a-request-to-search-for-a-user-w-o-sa.patch b/SOURCES/0010-LDAP-Split-out-a-request-to-search-for-a-user-w-o-sa.patch
new file mode 100644
index 0000000..3d4f47b
--- /dev/null
+++ b/SOURCES/0010-LDAP-Split-out-a-request-to-search-for-a-user-w-o-sa.patch
@@ -0,0 +1,301 @@
+From 26f41ed62ab74d628764702a1522cedd22b55599 Mon Sep 17 00:00:00 2001
+From: Jakub Hrozek <jhrozek@redhat.com>
+Date: Tue, 1 Oct 2013 17:44:07 +0200
+Subject: [PATCH 10/11] LDAP: Split out a request to search for a user w/o
+ saving
+
+Related:
+https://fedorahosted.org/sssd/ticket/2077
+
+Certain situations require that a user entry is downloaded for further
+inpection, but not saved to the sysdb right away. This patch splits the
+previously monolithic request into one that just downloads the data and
+one that uses the new one to download and save the user.
+---
+ src/providers/ldap/sdap_async.h       |  16 ++++
+ src/providers/ldap/sdap_async_users.c | 162 +++++++++++++++++++++++++++-------
+ 2 files changed, 146 insertions(+), 32 deletions(-)
+
+diff --git a/src/providers/ldap/sdap_async.h b/src/providers/ldap/sdap_async.h
+index c8031c9a9d527a6d808f1ddce096de23850ebfd6..dbf572cdc82b100ba9c26b4853f05db1ba5fa4ed 100644
+--- a/src/providers/ldap/sdap_async.h
++++ b/src/providers/ldap/sdap_async.h
+@@ -58,6 +58,22 @@ errno_t sdap_connect_host_recv(TALLOC_CTX *mem_ctx,
+                                struct tevent_req *req,
+                                struct sdap_handle **_sh);
+ 
++/* Search users in LDAP, return them as attrs */
++struct tevent_req *sdap_search_user_send(TALLOC_CTX *memctx,
++                                         struct tevent_context *ev,
++                                         struct sss_domain_info *dom,
++                                         struct sdap_options *opts,
++                                         struct sdap_search_base **search_bases,
++                                         struct sdap_handle *sh,
++                                         const char **attrs,
++                                         const char *filter,
++                                         int timeout,
++                                         bool enumeration);
++int sdap_search_user_recv(TALLOC_CTX *memctx, struct tevent_req *req,
++                          char **higher_usn, struct sysdb_attrs ***users,
++                          size_t *count);
++
++/* Search users in LDAP using the request above, save them to cache */
+ struct tevent_req *sdap_get_users_send(TALLOC_CTX *memctx,
+                                        struct tevent_context *ev,
+                                        struct sss_domain_info *dom,
+diff --git a/src/providers/ldap/sdap_async_users.c b/src/providers/ldap/sdap_async_users.c
+index 9cfe217482580d4a11ad4ace2f688f42ca55d7b3..7f0b2eea0b5ee909bcf148236c7fc43863fe8c13 100644
+--- a/src/providers/ldap/sdap_async_users.c
++++ b/src/providers/ldap/sdap_async_users.c
+@@ -579,15 +579,15 @@ done:
+ 
+ /* ==Search-Users-with-filter============================================= */
+ 
+-struct sdap_get_users_state {
++struct sdap_search_user_state {
+     struct tevent_context *ev;
+     struct sdap_options *opts;
+     struct sdap_handle *sh;
+     struct sss_domain_info *dom;
+-    struct sysdb_ctx *sysdb;
++
+     const char **attrs;
+     const char *base_filter;
+-    char *filter;
++    const char *filter;
+     int timeout;
+     bool enumeration;
+ 
+@@ -599,33 +599,31 @@ struct sdap_get_users_state {
+     struct sdap_search_base **search_bases;
+ };
+ 
+-static errno_t sdap_get_users_next_base(struct tevent_req *req);
+-static void sdap_get_users_process(struct tevent_req *subreq);
++static errno_t sdap_search_user_next_base(struct tevent_req *req);
++static void sdap_search_user_process(struct tevent_req *subreq);
+ 
+-struct tevent_req *sdap_get_users_send(TALLOC_CTX *memctx,
+-                                       struct tevent_context *ev,
+-                                       struct sss_domain_info *dom,
+-                                       struct sysdb_ctx *sysdb,
+-                                       struct sdap_options *opts,
+-                                       struct sdap_search_base **search_bases,
+-                                       struct sdap_handle *sh,
+-                                       const char **attrs,
+-                                       const char *filter,
+-                                       int timeout,
+-                                       bool enumeration)
++struct tevent_req *sdap_search_user_send(TALLOC_CTX *memctx,
++                                         struct tevent_context *ev,
++                                         struct sss_domain_info *dom,
++                                         struct sdap_options *opts,
++                                         struct sdap_search_base **search_bases,
++                                         struct sdap_handle *sh,
++                                         const char **attrs,
++                                         const char *filter,
++                                         int timeout,
++                                         bool enumeration)
+ {
+     errno_t ret;
+     struct tevent_req *req;
+-    struct sdap_get_users_state *state;
++    struct sdap_search_user_state *state;
+ 
+-    req = tevent_req_create(memctx, &state, struct sdap_get_users_state);
+-    if (!req) return NULL;
++    req = tevent_req_create(memctx, &state, struct sdap_search_user_state);
++    if (req == NULL) return NULL;
+ 
+     state->ev = ev;
+     state->opts = opts;
+     state->dom = dom;
+     state->sh = sh;
+-    state->sysdb = sysdb;
+     state->attrs = attrs;
+     state->higher_usn = NULL;
+     state->users =  NULL;
+@@ -643,7 +641,7 @@ struct tevent_req *sdap_get_users_send(TALLOC_CTX *memctx,
+         goto done;
+     }
+ 
+-    ret = sdap_get_users_next_base(req);
++    ret = sdap_search_user_next_base(req);
+ 
+ done:
+     if (ret != EOK) {
+@@ -654,18 +652,18 @@ done:
+     return req;
+ }
+ 
+-static errno_t sdap_get_users_next_base(struct tevent_req *req)
++static errno_t sdap_search_user_next_base(struct tevent_req *req)
+ {
+     struct tevent_req *subreq;
+-    struct sdap_get_users_state *state;
++    struct sdap_search_user_state *state;
+ 
+-    state = tevent_req_data(req, struct sdap_get_users_state);
++    state = tevent_req_data(req, struct sdap_search_user_state);
+ 
+     talloc_zfree(state->filter);
+     state->filter = sdap_get_id_specific_filter(state,
+                         state->base_filter,
+                         state->search_bases[state->base_iter]->filter);
+-    if (!state->filter) {
++    if (state->filter == NULL) {
+         return ENOMEM;
+     }
+ 
+@@ -681,20 +679,20 @@ static errno_t sdap_get_users_next_base(struct tevent_req *req)
+             state->opts->user_map, SDAP_OPTS_USER,
+             state->timeout,
+             state->enumeration); /* If we're enumerating, we need paging */
+-    if (!subreq) {
++    if (subreq == NULL) {
+         return ENOMEM;
+     }
+-    tevent_req_set_callback(subreq, sdap_get_users_process, req);
++    tevent_req_set_callback(subreq, sdap_search_user_process, req);
+ 
+     return EOK;
+ }
+ 
+-static void sdap_get_users_process(struct tevent_req *subreq)
++static void sdap_search_user_process(struct tevent_req *subreq)
+ {
+     struct tevent_req *req = tevent_req_callback_data(subreq,
+                                                       struct tevent_req);
+-    struct sdap_get_users_state *state = tevent_req_data(req,
+-                                            struct sdap_get_users_state);
++    struct sdap_search_user_state *state = tevent_req_data(req,
++                                            struct sdap_search_user_state);
+     int ret;
+     size_t count, i;
+     struct sysdb_attrs **users;
+@@ -744,7 +742,7 @@ static void sdap_get_users_process(struct tevent_req *subreq)
+         state->base_iter++;
+         if (state->search_bases[state->base_iter]) {
+             /* There are more search bases to try */
+-            ret = sdap_get_users_next_base(req);
++            ret = sdap_search_user_next_base(req);
+             if (ret != EOK) {
+                 tevent_req_error(req, ret);
+             }
+@@ -760,12 +758,112 @@ static void sdap_get_users_process(struct tevent_req *subreq)
+         return;
+     }
+ 
++    DEBUG(SSSDBG_TRACE_ALL, ("Retrieved total %zu users\n", state->count));
++    tevent_req_done(req);
++}
++
++
++int sdap_search_user_recv(TALLOC_CTX *memctx, struct tevent_req *req,
++                          char **higher_usn, struct sysdb_attrs ***users,
++                          size_t *count)
++{
++    struct sdap_search_user_state *state = tevent_req_data(req,
++                                            struct sdap_search_user_state);
++
++    if (higher_usn) {
++        *higher_usn = talloc_steal(memctx, state->higher_usn);
++    }
++
++    if (users) {
++        *users = talloc_steal(memctx, state->users);
++    }
++
++    if (count) {
++        *count = state->count;
++    }
++
++    TEVENT_REQ_RETURN_ON_ERROR(req);
++
++    return EOK;
++}
++
++/* ==Search-And-Save-Users-with-filter============================================= */
++struct sdap_get_users_state {
++    struct sysdb_ctx *sysdb;
++    struct sdap_options *opts;
++    struct sss_domain_info *dom;
++
++    char *higher_usn;
++    struct sysdb_attrs **users;
++    size_t count;
++};
++
++static void sdap_get_users_done(struct tevent_req *subreq);
++
++struct tevent_req *sdap_get_users_send(TALLOC_CTX *memctx,
++                                       struct tevent_context *ev,
++                                       struct sss_domain_info *dom,
++                                       struct sysdb_ctx *sysdb,
++                                       struct sdap_options *opts,
++                                       struct sdap_search_base **search_bases,
++                                       struct sdap_handle *sh,
++                                       const char **attrs,
++                                       const char *filter,
++                                       int timeout,
++                                       bool enumeration)
++{
++    errno_t ret;
++    struct tevent_req *req;
++    struct tevent_req *subreq;
++    struct sdap_get_users_state *state;
++
++    req = tevent_req_create(memctx, &state, struct sdap_get_users_state);
++    if (!req) return NULL;
++
++    state->sysdb = sysdb;
++    state->opts = opts;
++    state->dom = dom;
++
++    subreq = sdap_search_user_send(state, ev, dom, opts, search_bases,
++                                   sh, attrs, filter, timeout, enumeration);
++    if (subreq == NULL) {
++        ret = ENOMEM;
++        goto done;
++    }
++    tevent_req_set_callback(subreq, sdap_get_users_done, req);
++
++    ret = EOK;
++done:
++    if (ret != EOK) {
++        tevent_req_error(req, ret);
++        tevent_req_post(req, ev);
++    }
++
++    return req;
++}
++
++static void sdap_get_users_done(struct tevent_req *subreq)
++{
++    struct tevent_req *req = tevent_req_callback_data(subreq,
++                                                      struct tevent_req);
++    struct sdap_get_users_state *state = tevent_req_data(req,
++                                            struct sdap_get_users_state);
++    int ret;
++
++    ret = sdap_search_user_recv(state, subreq, &state->higher_usn,
++                                &state->users, &state->count);
++    if (ret) {
++        DEBUG(SSSDBG_OP_FAILURE, ("Failed to retrieve users\n"));
++        tevent_req_error(req, ret);
++        return;
++    }
++
+     ret = sdap_save_users(state, state->sysdb,
+                           state->dom, state->opts,
+                           state->users, state->count,
+                           &state->higher_usn);
+     if (ret) {
+-        DEBUG(2, ("Failed to store users.\n"));
++        DEBUG(SSSDBG_OP_FAILURE, ("Failed to store users.\n"));
+         tevent_req_error(req, ret);
+         return;
+     }
+-- 
+1.8.4.2
+
diff --git a/SOURCES/0011-LDAP-Search-for-original-DN-during-auth-if-it-s-miss.patch b/SOURCES/0011-LDAP-Search-for-original-DN-during-auth-if-it-s-miss.patch
new file mode 100644
index 0000000..11481f6
--- /dev/null
+++ b/SOURCES/0011-LDAP-Search-for-original-DN-during-auth-if-it-s-miss.patch
@@ -0,0 +1,269 @@
+From 8285fdca515e103eed41625a444de6fe72c5daa7 Mon Sep 17 00:00:00 2001
+From: Jakub Hrozek <jhrozek@redhat.com>
+Date: Tue, 1 Oct 2013 17:44:55 +0200
+Subject: [PATCH 11/11] LDAP: Search for original DN during auth if it's
+ missing
+
+Resolves: https://fedorahosted.org/sssd/ticket/2077
+
+If during the LDAP authentication we find out that the originalDN to
+bind as is missing (because the ID module is not LDAP based), we can try
+to look up the user from LDAP without saving him just in order to
+receive the originalDN.
+---
+ src/providers/ldap/ldap_auth.c | 210 +++++++++++++++++++++++++++++++++++++----
+ 1 file changed, 194 insertions(+), 16 deletions(-)
+
+diff --git a/src/providers/ldap/ldap_auth.c b/src/providers/ldap/ldap_auth.c
+index f9bab4b2b72a061c88fbf18d3f8401b673f79619..ddaeb09c8ae06812855a1daec2fc3399eb4be361 100644
+--- a/src/providers/ldap/ldap_auth.c
++++ b/src/providers/ldap/ldap_auth.c
+@@ -343,6 +343,146 @@ shadow_fail:
+ }
+ 
+ /* ==Get-User-DN========================================================== */
++struct get_user_dn_state {
++    const char *username;
++
++    char *orig_dn;
++};
++
++static void get_user_dn_done(struct tevent_req *subreq);
++
++static struct tevent_req *get_user_dn_send(TALLOC_CTX *memctx,
++                                           struct tevent_context *ev,
++                                           struct sss_domain_info *domain,
++                                           struct sdap_handle *sh,
++                                           struct sdap_options *opts,
++                                           const char *username)
++{
++    struct tevent_req *req;
++    struct tevent_req *subreq;
++    struct get_user_dn_state *state;
++    char *clean_name;
++    char *filter;
++    const char **attrs;
++    errno_t ret;
++
++    req = tevent_req_create(memctx, &state, struct get_user_dn_state);
++    if (!req) return NULL;
++
++    state->username = username;
++
++    ret = sss_filter_sanitize(state, username, &clean_name);
++    if (ret != EOK) {
++        goto done;
++    }
++
++    filter = talloc_asprintf(state, "(&(%s=%s)(objectclass=%s))",
++                             opts->user_map[SDAP_AT_USER_NAME].name,
++                             clean_name,
++                             opts->user_map[SDAP_OC_USER].name);
++    talloc_zfree(clean_name);
++    if (filter == NULL) {
++        DEBUG(SSSDBG_OP_FAILURE, ("Failed to build the base filter\n"));
++        ret = ENOMEM;
++        goto done;
++    }
++
++    /* We're mostly interested in the DN anyway */
++    attrs = talloc_array(state, const char *, 3);
++    if (attrs == NULL) {
++        ret = ENOMEM;
++        goto done;
++    }
++    attrs[0] = "objectclass";
++    attrs[1] = opts->user_map[SDAP_AT_USER_NAME].name;
++    attrs[2] = NULL;
++
++    subreq = sdap_search_user_send(state, ev, domain, opts,
++                                   opts->sdom->user_search_bases,
++                                   sh, attrs, filter,
++                                   dp_opt_get_int(opts->basic,
++                                                  SDAP_SEARCH_TIMEOUT),
++                                   false);
++    if (!subreq) {
++        ret = ENOMEM;
++        goto done;
++    }
++    tevent_req_set_callback(subreq, get_user_dn_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 get_user_dn_done(struct tevent_req *subreq)
++{
++    errno_t ret;
++    struct tevent_req *req = tevent_req_callback_data(subreq,
++                                                      struct tevent_req);
++    struct get_user_dn_state *state = tevent_req_data(req,
++                                                    struct get_user_dn_state);
++    struct ldb_message_element *el;
++    struct sysdb_attrs **users;
++    size_t count;
++
++    ret = sdap_search_user_recv(state, subreq, NULL, &users, &count);
++    talloc_zfree(subreq);
++    if (ret && ret != ENOENT) {
++        DEBUG(SSSDBG_OP_FAILURE, ("Failed to retrieve users\n"));
++        tevent_req_error(req, ret);
++        return;
++    }
++
++    if (count == 0) {
++        DEBUG(SSSDBG_OP_FAILURE, ("No such user\n"));
++        tevent_req_error(req, ENOMEM);
++        return;
++    } else if (count > 1) {
++        DEBUG(SSSDBG_OP_FAILURE, ("Multiple users matched\n"));
++        tevent_req_error(req, EIO);
++        return;
++    }
++
++    /* exactly one user. Get the originalDN */
++    ret = sysdb_attrs_get_el_ext(users[0], SYSDB_ORIG_DN, false, &el);
++    if (ret != EOK) {
++        DEBUG(SSSDBG_OP_FAILURE,
++              ("originalDN is not available for [%s].\n", state->username));
++        tevent_req_error(req, ret);
++        return;
++    }
++
++    state->orig_dn = talloc_strdup(state, (const char *) el->values[0].data);
++    if (state->orig_dn == NULL) {
++        tevent_req_error(req, ENOMEM);
++        return;
++    }
++
++    DEBUG(SSSDBG_TRACE_INTERNAL, ("Found originalDN [%s] for [%s]\n",
++          state->orig_dn, state->username));
++    tevent_req_done(req);
++}
++
++static int get_user_dn_recv(TALLOC_CTX *mem_ctx, struct tevent_req *req,
++                            char **orig_dn)
++{
++    struct get_user_dn_state *state = tevent_req_data(req,
++                                                    struct get_user_dn_state);
++
++    if (orig_dn) {
++        *orig_dn = talloc_move(mem_ctx, &state->orig_dn);
++    }
++
++    TEVENT_REQ_RETURN_ON_ERROR(req);
++
++    return EOK;
++}
+ 
+ static int get_user_dn(TALLOC_CTX *memctx,
+                        struct sysdb_ctx *sysdb,
+@@ -391,25 +531,20 @@ static int get_user_dn(TALLOC_CTX *memctx,
+ 
+     switch (res->count) {
+     case 0:
+-        /* FIXME: not in cache, needs a true search */
+-        ret = ENOENT;
++        /* No such user entry? Look it up */
++        ret = EAGAIN;
+         break;
+ 
+     case 1:
+         dn = ldb_msg_find_attr_as_string(res->msgs[0], SYSDB_ORIG_DN, NULL);
+-        if (dn) {
+-            dn = talloc_strdup(tmpctx, dn);
+-        } else {
+-            /* TODO: try to search ldap server ? */
+-
+-            /* FIXME: remove once we store originalDN on every call
+-             * NOTE: this is wrong, works only with some DITs */
+-            dn = talloc_asprintf(tmpctx, "%s=%s,%s",
+-                                 opts->user_map[SDAP_AT_USER_NAME].name,
+-                                 username,
+-                                 dp_opt_get_string(opts->basic,
+-                                                   SDAP_USER_SEARCH_BASE));
++        if (dn == NULL) {
++            /* The user entry has no original DN. This is the case when the ID
++             * provider is not LDAP-based (proxy perhaps) */
++            ret = EAGAIN;
++            break;
+         }
++
++        dn = talloc_strdup(tmpctx, dn);
+         if (!dn) {
+             ret = ENOMEM;
+             break;
+@@ -466,6 +601,8 @@ struct auth_state {
+ };
+ 
+ static struct tevent_req *auth_get_server(struct tevent_req *req);
++static void auth_get_dn_done(struct tevent_req *subreq);
++static void auth_do_bind(struct tevent_req *req);
+ static void auth_resolve_done(struct tevent_req *subreq);
+ static void auth_connect_done(struct tevent_req *subreq);
+ static void auth_bind_user_done(struct tevent_req *subreq);
+@@ -610,11 +747,52 @@ static void auth_connect_done(struct tevent_req *subreq)
+     ret = get_user_dn(state, state->ctx->be->domain->sysdb, state->ctx->be->domain,
+                       state->ctx->opts, state->username, &state->dn,
+                       &state->pw_expire_type, &state->pw_expire_data);
+-    if (ret) {
+-        tevent_req_error(req, ret);
++    if (ret == EOK) {
++        /* All required user data was pre-cached during an identity lookup.
++         * We can proceed with the bind */
++        auth_do_bind(req);
++        return;
++    } else if (ret == EAGAIN) {
++        /* The cached user entry was missing the bind DN. Need to look
++         * it up based on user name in order to perform the bind */
++        subreq = get_user_dn_send(req, state->ev, state->ctx->be->domain,
++                                  state->sh, state->ctx->opts, state->username);
++        if (subreq == NULL) {
++            tevent_req_error(req, ENOMEM);
++            return;
++        }
++        tevent_req_set_callback(subreq, auth_get_dn_done, req);
++        return;
++    }
++
++    tevent_req_error(req, ret);
++    return;
++}
++
++static void auth_get_dn_done(struct tevent_req *subreq)
++{
++    struct tevent_req *req = tevent_req_callback_data(subreq,
++                                                      struct tevent_req);
++    struct auth_state *state = tevent_req_data(req, struct auth_state);
++    errno_t ret;
++
++    ret = get_user_dn_recv(state, subreq, &state->dn);
++    talloc_zfree(subreq);
++    if (ret != EOK) {
++        tevent_req_error(req, ERR_ACCOUNT_UNKNOWN);
+         return;
+     }
+ 
++    /* The DN was found with an LDAP lookup
++     * We can proceed with the bind */
++    return auth_do_bind(req);
++}
++
++static void auth_do_bind(struct tevent_req *req)
++{
++    struct auth_state *state = tevent_req_data(req, struct auth_state);
++    struct tevent_req *subreq;
++
+     subreq = sdap_auth_send(state, state->ev, state->sh,
+                             NULL, NULL, state->dn,
+                             state->authtok);
+-- 
+1.8.4.2
+
diff --git a/SOURCES/0012-LDAP-Prevent-from-using-uninitialized-sdap_options.patch b/SOURCES/0012-LDAP-Prevent-from-using-uninitialized-sdap_options.patch
new file mode 100644
index 0000000..7c99b36
--- /dev/null
+++ b/SOURCES/0012-LDAP-Prevent-from-using-uninitialized-sdap_options.patch
@@ -0,0 +1,30 @@
+From 5eea6f1e7a43bdd63a1530fb9c68ef292f431f4f Mon Sep 17 00:00:00 2001
+From: Lukas Slebodnik <lslebodn@redhat.com>
+Date: Wed, 13 Nov 2013 08:32:23 +0100
+Subject: [PATCH 12/12] LDAP: Prevent from using uninitialized sdap_options
+
+ldap_get_options can fail in time of ldap back end initialisation
+and then sssd try to release uninitialised sdap_options.
+
+Resolves:
+https://fedorahosted.org/sssd/ticket/2147
+---
+ src/providers/ldap/ldap_init.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/src/providers/ldap/ldap_init.c b/src/providers/ldap/ldap_init.c
+index 17874b1325d3e5bf2eded62c6f022364b84a5c2a..15615b2891f2e3104c11e8610c081adcd1d1ee8e 100644
+--- a/src/providers/ldap/ldap_init.c
++++ b/src/providers/ldap/ldap_init.c
+@@ -94,7 +94,7 @@ int sssm_ldap_id_init(struct be_ctx *bectx,
+     const char *dns_service_name;
+     const char *sasl_mech;
+     struct sdap_service *sdap_service;
+-    struct sdap_options *opts;
++    struct sdap_options *opts = NULL;
+     int ret;
+ 
+     /* If we're already set up, just return that */
+-- 
+1.8.4.2
+
diff --git a/SOURCES/0013-SUBDOMAINS-Reuse-cached-results-if-DP-is-offline.patch b/SOURCES/0013-SUBDOMAINS-Reuse-cached-results-if-DP-is-offline.patch
new file mode 100644
index 0000000..bc5a348
--- /dev/null
+++ b/SOURCES/0013-SUBDOMAINS-Reuse-cached-results-if-DP-is-offline.patch
@@ -0,0 +1,63 @@
+From 8751aace6de14f3782765a89555b65e991f340a0 Mon Sep 17 00:00:00 2001
+From: Jakub Hrozek <jhrozek@redhat.com>
+Date: Tue, 3 Dec 2013 15:25:25 +0100
+Subject: [PATCH 13/15] SUBDOMAINS: Reuse cached results if DP is offline
+
+If Data Provider was unable to refresh the subdomain list, the
+sss_domain_info->subdomains list was NULL. Which meant that no DP
+request matched any known domain and hence offline authentication was
+not working correctly.
+
+Resolves:
+https://fedorahosted.org/sssd/ticket/2168
+---
+ src/providers/ad/ad_subdomains.c   | 7 +++++++
+ src/providers/ipa/ipa_subdomains.c | 8 ++++++--
+ 2 files changed, 13 insertions(+), 2 deletions(-)
+
+diff --git a/src/providers/ad/ad_subdomains.c b/src/providers/ad/ad_subdomains.c
+index 9911dfe0f4fda6749fa6dd3f15b1c04a36964ca4..18414523096ba0e53261415551eea57b4b2758b2 100644
+--- a/src/providers/ad/ad_subdomains.c
++++ b/src/providers/ad/ad_subdomains.c
+@@ -650,5 +650,12 @@ int ad_subdom_init(struct be_ctx *be_ctx,
+         return EFAULT;
+     }
+ 
++    ret = sysdb_update_subdomains(be_ctx->domain);
++    if (ret != EOK) {
++        DEBUG(SSSDBG_MINOR_FAILURE, ("Could not load the list of subdomains. "
++              "Users from trusted domains might not be resolved correctly\n"));
++        /* Ignore this error and try to discover the subdomains later */
++    }
++
+     return EOK;
+ }
+diff --git a/src/providers/ipa/ipa_subdomains.c b/src/providers/ipa/ipa_subdomains.c
+index 4f7627eddb9c54d68e45be876157057f3c30b422..416e21913be8e991c9f496ff2b54f238b602f304 100644
+--- a/src/providers/ipa/ipa_subdomains.c
++++ b/src/providers/ipa/ipa_subdomains.c
+@@ -809,8 +809,6 @@ static void ipa_subdomains_get_conn_done(struct tevent_req *req)
+             DEBUG(SSSDBG_MINOR_FAILURE,
+                   ("No IPA server is available, cannot get the "
+                    "subdomain list while offline\n"));
+-
+-/* FIXME: return saved results ?? */
+         } else {
+             DEBUG(SSSDBG_OP_FAILURE,
+                   ("Failed to connect to IPA server: [%d](%s)\n",
+@@ -1291,6 +1289,12 @@ int ipa_subdom_init(struct be_ctx *be_ctx,
+         DEBUG(SSSDBG_MINOR_FAILURE, ("Failed to add subdom offline callback"));
+     }
+ 
++    ret = sysdb_update_subdomains(be_ctx->domain);
++    if (ret != EOK) {
++        DEBUG(SSSDBG_MINOR_FAILURE, ("Could not load the list of subdomains. "
++              "Users from trusted domains might not be resolved correctly\n"));
++    }
++
+     return EOK;
+ }
+ 
+-- 
+1.8.4.2
+
diff --git a/SOURCES/0014-failover-check-dns_domain-if-primary-servers-lookup-.patch b/SOURCES/0014-failover-check-dns_domain-if-primary-servers-lookup-.patch
new file mode 100644
index 0000000..3da0806
--- /dev/null
+++ b/SOURCES/0014-failover-check-dns_domain-if-primary-servers-lookup-.patch
@@ -0,0 +1,42 @@
+From 0926cec5b98218131ac822e1684f9bce7aa0072c Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pavel=20B=C5=99ezina?= <pbrezina@redhat.com>
+Date: Thu, 5 Dec 2013 13:19:16 +0100
+Subject: [PATCH 14/15] failover: check dns_domain if primary servers lookup
+ failed
+
+If primary servers lookup failed, dns_domain is not set.
+
+Resolves:
+https://fedorahosted.org/sssd/ticket/2173
+---
+ src/providers/fail_over_srv.c | 10 +++++++---
+ 1 file changed, 7 insertions(+), 3 deletions(-)
+
+diff --git a/src/providers/fail_over_srv.c b/src/providers/fail_over_srv.c
+index 543516ef19e288556e39e61b400a628c94e2817b..c27416899e30b3cf8b3c0496adb08089f4b2c11d 100644
+--- a/src/providers/fail_over_srv.c
++++ b/src/providers/fail_over_srv.c
+@@ -302,13 +302,17 @@ static void fo_discover_servers_primary_done(struct tevent_req *subreq)
+     }
+ 
+     if (state->backup_domain == NULL) {
++        /* if there is no backup domain, we are done */
+         DEBUG(SSSDBG_TRACE_FUNC, ("No backup domain specified\n"));
+         goto done;
+     }
+ 
+-    if (strcasecmp(state->dns_domain, state->backup_domain) == 0) {
+-        /* primary domain was unreachable, we will use servers from backup
+-         * domain as primary */
++    if (state->dns_domain != NULL
++            && strcasecmp(state->dns_domain, state->backup_domain) == 0) {
++        /* If there was no error and dns_domain is the same as backup domain,
++         * it means that we were unable to resolve SRV in primary domain, but
++         * SRV from backup domain was resolved and those servers are considered
++         * to be primary. We are done. */
+         state->backup_servers = NULL;
+         state->num_backup_servers = 0;
+ 
+-- 
+1.8.4.2
+
diff --git a/SOURCES/0015-NSS-Set-packet-length-for-initgroups.patch b/SOURCES/0015-NSS-Set-packet-length-for-initgroups.patch
new file mode 100644
index 0000000..b122626
--- /dev/null
+++ b/SOURCES/0015-NSS-Set-packet-length-for-initgroups.patch
@@ -0,0 +1,37 @@
+From 644f0b89c61b8a912514df550633f179c654240a Mon Sep 17 00:00:00 2001
+From: Lukas Slebodnik <lslebodn@redhat.com>
+Date: Tue, 5 Nov 2013 17:58:36 +0100
+Subject: [PATCH 15/15] NSS: Set packet length for initgroups
+
+Some groups could be skipped, but packet length was not trimmed.
+This is a reason why valgrind reported access to uninitialised bytes.
+Actually, it isn't a problem, because the first uint32 in body is number of
+sended gids.
+
+Resolves:
+https://fedorahosted.org/sssd/ticket/2138
+---
+ src/responder/nss/nsssrv_cmd.c | 7 +++++++
+ 1 file changed, 7 insertions(+)
+
+diff --git a/src/responder/nss/nsssrv_cmd.c b/src/responder/nss/nsssrv_cmd.c
+index ada2b64df88d6a03a5c36a76076da447faa573c9..07f31188074bf45664969fb33388edb475e53b96 100644
+--- a/src/responder/nss/nsssrv_cmd.c
++++ b/src/responder/nss/nsssrv_cmd.c
+@@ -3558,6 +3558,13 @@ static int fill_initgr(struct sss_packet *packet, struct ldb_result *res)
+ 
+     ((uint32_t *)body)[0] = num-skipped; /* num results */
+     ((uint32_t *)body)[1] = 0; /* reserved */
++    blen = (2 + bindex) * sizeof(uint32_t);
++    ret = sss_packet_set_size(packet, blen);
++    if (ret != EOK) {
++        DEBUG(SSSDBG_OP_FAILURE,
++              ("Could not set packet size to value:%zu\n", blen));
++        return ret;
++    }
+ 
+     return EOK;
+ }
+-- 
+1.8.4.2
+
diff --git a/SOURCES/0016-NSS-Fix-memory-leak-in-sss_setnetgrent.patch b/SOURCES/0016-NSS-Fix-memory-leak-in-sss_setnetgrent.patch
new file mode 100644
index 0000000..698ba3e
--- /dev/null
+++ b/SOURCES/0016-NSS-Fix-memory-leak-in-sss_setnetgrent.patch
@@ -0,0 +1,33 @@
+From 81aa563090c33bafbf22f1cde586b77ed526c25f Mon Sep 17 00:00:00 2001
+From: Lukas Slebodnik <lslebodn@redhat.com>
+Date: Wed, 27 Nov 2013 10:22:59 +0100
+Subject: [PATCH 16/17] NSS: Fix memory leak in sss_setnetgrent
+
+struct nss_cmd_ctx was not released in function nss_cmd_setnetgrent_done
+and it wasn't used in the other function, because getnetgrent creates its own
+nss_cmd_ctx context. struct nss_cmd_ctx was released after closing client
+because it was allocated under client context. Memory leak is apparent with
+long living clients.
+
+Resolves:
+https://fedorahosted.org/sssd/ticket/2170
+---
+ src/responder/nss/nsssrv_netgroup.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/src/responder/nss/nsssrv_netgroup.c b/src/responder/nss/nsssrv_netgroup.c
+index a1c41968db0becbc42a1c501b666d9aec5241b5f..18e8579372fa39a1b3a60076948bc12bc008fb80 100644
+--- a/src/responder/nss/nsssrv_netgroup.c
++++ b/src/responder/nss/nsssrv_netgroup.c
+@@ -687,7 +687,7 @@ static void nss_cmd_setnetgrent_done(struct tevent_req *req)
+             ((uint32_t *)body)[1] = 0; /* reserved */
+         }
+ 
+-        sss_cmd_done(cmdctx->cctx, NULL);
++        sss_cmd_done(cmdctx->cctx, cmdctx);
+         return;
+     }
+ 
+-- 
+1.8.4.2
+
diff --git a/SOURCES/0017-AD-use-LDAP-for-group-lookups.patch b/SOURCES/0017-AD-use-LDAP-for-group-lookups.patch
new file mode 100644
index 0000000..be1d3cf
--- /dev/null
+++ b/SOURCES/0017-AD-use-LDAP-for-group-lookups.patch
@@ -0,0 +1,253 @@
+From 0324d31d0479e5de0d3aac05bf5fb922d84f84c4 Mon Sep 17 00:00:00 2001
+From: Sumit Bose <sbose@redhat.com>
+Date: Mon, 9 Dec 2013 11:45:28 +0100
+Subject: [PATCH 17/17] AD: use LDAP for group lookups
+
+The group memberships cannot be reliable retrieved from the Global
+Catalog. By default the memberOf attribute is not replicated to the GC
+at all and the member attribute is copied from the local LDAP instance
+to the GC running on the same host, but is only replicated to other GC
+instances for groups with universal scope. Additionally the tokenGroups
+attribute contains invalid SIDs when used with the GC for users from a
+different domains than the GC belongs to.
+
+As a result the requests which tries to resolve group-memberships of a
+AD user have to go to a LDAP server from the domain of the user.
+
+Fixes https://fedorahosted.org/sssd/ticket/2161 and
+https://fedorahosted.org/sssd/ticket/2148 as a side-effect.
+---
+ src/providers/ad/ad_id.c         |  20 +++++-
+ src/providers/ad/ad_subdomains.c | 133 ++++++++++++++++++++++++++++++++++++++-
+ src/providers/ldap/sdap.h        |   2 +
+ 3 files changed, 152 insertions(+), 3 deletions(-)
+
+diff --git a/src/providers/ad/ad_id.c b/src/providers/ad/ad_id.c
+index 87b69c66892ebfa48a1067c63006f3e3bd2e7444..dadb50da92cac87d3162bddb44395dad7d2abbc4 100644
+--- a/src/providers/ad/ad_id.c
++++ b/src/providers/ad/ad_id.c
+@@ -188,6 +188,8 @@ get_conn_list(struct be_req *breq, struct ad_id_ctx *ad_ctx,
+               struct sss_domain_info *dom, struct be_acct_req *ar)
+ {
+     struct sdap_id_conn_ctx **clist;
++    struct sdap_domain *sdom;
++    struct ad_id_ctx *subdom_id_ctx;
+ 
+     /* LDAP, GC, sentinel */
+     clist = talloc_zero_array(breq, struct sdap_id_conn_ctx *, 3);
+@@ -197,8 +199,6 @@ get_conn_list(struct be_req *breq, struct ad_id_ctx *ad_ctx,
+     case BE_REQ_USER: /* user */
+     case BE_REQ_BY_SECID:   /* by SID */
+     case BE_REQ_USER_AND_GROUP: /* get SID */
+-    case BE_REQ_GROUP: /* group */
+-    case BE_REQ_INITGROUPS: /* init groups for user */
+         /* Always try GC first */
+         clist[0] = ad_ctx->gc_ctx;
+         if (IS_SUBDOMAIN(dom) == true) {
+@@ -216,6 +216,22 @@ get_conn_list(struct be_req *breq, struct ad_id_ctx *ad_ctx,
+         clist[1] = ad_ctx->ldap_ctx;
+         break;
+ 
++    case BE_REQ_GROUP: /* group */
++    case BE_REQ_INITGROUPS: /* init groups for user */
++        if (IS_SUBDOMAIN(dom)) {
++            sdom = sdap_domain_get(ad_ctx->sdap_id_ctx->opts, dom);
++            if (sdom == NULL || sdom->pvt == NULL) {
++                DEBUG(SSSDBG_CRIT_FAILURE, ("No ID ctx available for [%s].\n",
++                                            dom->name));
++                return NULL;
++            }
++            subdom_id_ctx = talloc_get_type(sdom->pvt, struct ad_id_ctx);
++            clist[0] = subdom_id_ctx->ldap_ctx;
++        } else {
++            clist[0] = ad_ctx->ldap_ctx;
++        }
++        break;
++
+     default:
+         clist[0] = ad_ctx->ldap_ctx;
+         break;
+diff --git a/src/providers/ad/ad_subdomains.c b/src/providers/ad/ad_subdomains.c
+index 18414523096ba0e53261415551eea57b4b2758b2..28c5eafb395b70e8f3630a43b67c61810683fe7c 100644
+--- a/src/providers/ad/ad_subdomains.c
++++ b/src/providers/ad/ad_subdomains.c
+@@ -25,6 +25,7 @@
+ #include "providers/ldap/sdap_async.h"
+ #include "providers/ad/ad_subdomains.h"
+ #include "providers/ad/ad_domain_info.h"
++#include "providers/ad/ad_srv.h"
+ #include "providers/ldap/sdap_idmap.h"
+ #include "util/util_sss_idmap.h"
+ #include <ctype.h>
+@@ -68,6 +69,7 @@ struct ad_subdomains_ctx {
+ 
+     time_t last_refreshed;
+     struct tevent_timer *timer_event;
++    struct ad_id_ctx *ad_id_ctx;
+ };
+ 
+ struct ad_subdomains_req_ctx {
+@@ -86,10 +88,138 @@ struct ad_subdomains_req_ctx {
+ };
+ 
+ static errno_t
++ad_subdom_ad_ctx_new(struct be_ctx *be_ctx,
++                     struct ad_id_ctx *id_ctx,
++                     struct sss_domain_info *subdom,
++                     struct ad_id_ctx **_subdom_id_ctx)
++{
++    struct ad_options *ad_options;
++    struct ad_id_ctx *ad_id_ctx;
++    const char *gc_service_name;
++    struct ad_srv_plugin_ctx *srv_ctx;
++    char *ad_domain;
++    struct sdap_domain *sdom;
++    errno_t ret;
++    const char *realm;
++    const char *hostname;
++
++    realm = dp_opt_get_cstring(id_ctx->ad_options->basic, AD_KRB5_REALM);
++    hostname = dp_opt_get_cstring(id_ctx->ad_options->basic, AD_HOSTNAME);
++    if (realm == NULL || hostname == NULL) {
++        DEBUG(SSSDBG_CONF_SETTINGS, ("Missing realm or hostname.\n"));
++        return EINVAL;
++    }
++
++    ad_options = ad_create_default_options(id_ctx, realm, hostname);
++    if (ad_options == NULL) {
++        DEBUG(SSSDBG_OP_FAILURE, ("Cannot initialize AD options\n"));
++        talloc_free(ad_options);
++        return ENOMEM;
++    }
++
++    ad_domain = subdom->name;
++
++    ret = dp_opt_set_string(ad_options->basic, AD_DOMAIN, ad_domain);
++    if (ret != EOK) {
++        DEBUG(SSSDBG_OP_FAILURE, ("Cannot set AD domain\n"));
++        talloc_free(ad_options);
++        return ret;
++    }
++
++    gc_service_name = talloc_asprintf(ad_options, "%s%s", "gc_", subdom->name);
++    if (gc_service_name == NULL) {
++        talloc_free(ad_options);
++        return ENOMEM;
++    }
++
++    ret = ad_failover_init(ad_options, be_ctx, NULL, NULL, realm,
++                           subdom->name, gc_service_name,
++                           subdom->name, &ad_options->service);
++    if (ret != EOK) {
++        DEBUG(SSSDBG_OP_FAILURE, ("Cannot initialize AD failover\n"));
++        talloc_free(ad_options);
++        return ret;
++    }
++
++    ad_id_ctx = ad_id_ctx_init(ad_options, be_ctx);
++    if (ad_id_ctx == NULL) {
++        talloc_free(ad_options);
++        return ENOMEM;
++    }
++    ad_id_ctx->sdap_id_ctx->opts = ad_options->id;
++    ad_options->id_ctx = ad_id_ctx;
++
++    /* use AD plugin */
++    srv_ctx = ad_srv_plugin_ctx_init(be_ctx, be_ctx->be_res,
++                                     default_host_dbs,
++                                     ad_id_ctx->ad_options->id,
++                                     hostname,
++                                     ad_domain);
++    if (srv_ctx == NULL) {
++        DEBUG(SSSDBG_FATAL_FAILURE, ("Out of memory?\n"));
++        return ENOMEM;
++    }
++    be_fo_set_srv_lookup_plugin(be_ctx, ad_srv_plugin_send,
++                                ad_srv_plugin_recv, srv_ctx, "AD");
++
++    ret = sdap_domain_subdom_add(ad_id_ctx->sdap_id_ctx,
++                                 ad_id_ctx->sdap_id_ctx->opts->sdom,
++                                 subdom->parent);
++    if (ret != EOK) {
++        DEBUG(SSSDBG_OP_FAILURE, ("Cannot initialize sdap domain\n"));
++        talloc_free(ad_options);
++        return ret;
++    }
++
++    sdom = sdap_domain_get(ad_id_ctx->sdap_id_ctx->opts, subdom);
++    if (sdom == NULL) {
++        return EFAULT;
++    }
++
++    ret = sdap_id_setup_tasks(ad_id_ctx->sdap_id_ctx,
++                              ad_id_ctx->ldap_ctx, sdom,
++                              ldap_enumeration_send,
++                              ldap_enumeration_recv);
++    if (ret != EOK) {
++        talloc_free(ad_options);
++        return ret;
++    }
++
++    /* Set up the ID mapping object */
++    ad_id_ctx->sdap_id_ctx->opts->idmap_ctx =
++        id_ctx->sdap_id_ctx->opts->idmap_ctx;
++
++    *_subdom_id_ctx = ad_id_ctx;
++    return EOK;
++}
++
++static errno_t
+ ads_store_sdap_subdom(struct ad_subdomains_ctx *ctx,
+                       struct sss_domain_info *parent)
+ {
+-    return sdap_domain_subdom_add(ctx->sdap_id_ctx, ctx->sdom, parent);
++    int ret;
++    struct sdap_domain *sditer;
++    struct ad_id_ctx *subdom_id_ctx;
++
++    ret = sdap_domain_subdom_add(ctx->sdap_id_ctx, ctx->sdom, parent);
++    if (ret != EOK) {
++        DEBUG(SSSDBG_OP_FAILURE, ("sdap_domain_subdom_add failed.\n"));
++        return ret;
++    }
++
++    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,
++                                       sditer->dom, &subdom_id_ctx);
++            if (ret != EOK) {
++                DEBUG(SSSDBG_OP_FAILURE, ("ad_subdom_ad_ctx_new failed.\n"));
++            } else {
++                sditer->pvt = subdom_id_ctx;
++            }
++        }
++    }
++
++    return EOK;
+ }
+ 
+ static errno_t
+@@ -630,6 +760,7 @@ int ad_subdom_init(struct be_ctx *be_ctx,
+         DEBUG(SSSDBG_OP_FAILURE, ("talloc_strdup failed.\n"));
+         return ENOMEM;
+     }
++    ctx->ad_id_ctx = id_ctx;
+     *ops = &ad_subdomains_ops;
+     *pvt_data = ctx;
+ 
+diff --git a/src/providers/ldap/sdap.h b/src/providers/ldap/sdap.h
+index c53471b9bd6d9b18f37a8bf4089c8700d4a1163d..fa641730bb78b6a96c0b9640af7612b876f56533 100644
+--- a/src/providers/ldap/sdap.h
++++ b/src/providers/ldap/sdap.h
+@@ -394,6 +394,8 @@ struct sdap_domain {
+     struct timeval last_enum;
+     /* cleanup loop timer */
+     struct timeval last_purge;
++
++    void *pvt;
+ };
+ 
+ struct sdap_options {
+-- 
+1.8.4.2
+
diff --git a/SOURCES/0018-idmap-add-API-to-free-allocated-SIDs.patch b/SOURCES/0018-idmap-add-API-to-free-allocated-SIDs.patch
new file mode 100644
index 0000000..02df2e8
--- /dev/null
+++ b/SOURCES/0018-idmap-add-API-to-free-allocated-SIDs.patch
@@ -0,0 +1,119 @@
+From 16c8b0e7a0ac40b078f98c9f8025d39a59dca9bb Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pavel=20B=C5=99ezina?= <pbrezina@redhat.com>
+Date: Fri, 1 Nov 2013 12:23:23 +0100
+Subject: [PATCH 18/31] idmap: add API to free allocated SIDs
+
+---
+ src/lib/idmap/sss_idmap.c | 36 +++++++++++++++++++++++++++++++++++
+ src/lib/idmap/sss_idmap.h | 48 +++++++++++++++++++++++++++++++++++++++++++++++
+ 2 files changed, 84 insertions(+)
+
+diff --git a/src/lib/idmap/sss_idmap.c b/src/lib/idmap/sss_idmap.c
+index 9278e10d2bee37b741a87cd84d666d8a5b7bb671..3f1e7a58f390a3c10999251e2155ef513ba69bd7 100644
+--- a/src/lib/idmap/sss_idmap.c
++++ b/src/lib/idmap/sss_idmap.c
+@@ -246,6 +246,42 @@ enum idmap_error_code sss_idmap_free(struct sss_idmap_ctx *ctx)
+     return IDMAP_SUCCESS;
+ }
+ 
++static enum idmap_error_code sss_idmap_free_ptr(struct sss_idmap_ctx *ctx,
++                                                void *ptr)
++{
++    CHECK_IDMAP_CTX(ctx, IDMAP_CONTEXT_INVALID);
++
++    if (ptr != NULL) {
++        ctx->free_func(ptr, ctx->alloc_pvt);
++    }
++
++    return IDMAP_SUCCESS;
++}
++
++enum idmap_error_code sss_idmap_free_sid(struct sss_idmap_ctx *ctx,
++                                         char *sid)
++{
++    return sss_idmap_free_ptr(ctx, sid);
++}
++
++enum idmap_error_code sss_idmap_free_dom_sid(struct sss_idmap_ctx *ctx,
++                                             struct sss_dom_sid *dom_sid)
++{
++    return sss_idmap_free_ptr(ctx, dom_sid);
++}
++
++enum idmap_error_code sss_idmap_free_smb_sid(struct sss_idmap_ctx *ctx,
++                                             struct dom_sid *smb_sid)
++{
++    return sss_idmap_free_ptr(ctx, smb_sid);
++}
++
++enum idmap_error_code sss_idmap_free_bin_sid(struct sss_idmap_ctx *ctx,
++                                             uint8_t *bin_sid)
++{
++    return sss_idmap_free_ptr(ctx, bin_sid);
++}
++
+ enum idmap_error_code sss_idmap_calculate_range(struct sss_idmap_ctx *ctx,
+                                                 const char *dom_sid,
+                                                 id_t *slice_num,
+diff --git a/src/lib/idmap/sss_idmap.h b/src/lib/idmap/sss_idmap.h
+index 4101fb9a5e0982c6ba0560decd299a0ed9e722b6..1e1c9a5cfe490301d0e633db808589f1bc0ef857 100644
+--- a/src/lib/idmap/sss_idmap.h
++++ b/src/lib/idmap/sss_idmap.h
+@@ -504,6 +504,54 @@ enum idmap_error_code sss_idmap_unix_to_bin_sid(struct sss_idmap_ctx *ctx,
+ enum idmap_error_code sss_idmap_free(struct sss_idmap_ctx *ctx);
+ 
+ /**
++ * @brief Free mapped SID.
++ *
++ * @param[in] ctx         Idmap context
++ * @param[in] sid         SID to be freed.
++ *
++ * @return
++ *  - #IDMAP_CONTEXT_INVALID: Provided context is invalid
++ */
++enum idmap_error_code sss_idmap_free_sid(struct sss_idmap_ctx *ctx,
++                                         char *sid);
++
++/**
++ * @brief Free mapped domain SID.
++ *
++ * @param[in] ctx         Idmap context
++ * @param[in] dom_sid     Domain SID to be freed.
++ *
++ * @return
++ *  - #IDMAP_CONTEXT_INVALID: Provided context is invalid
++ */
++enum idmap_error_code sss_idmap_free_dom_sid(struct sss_idmap_ctx *ctx,
++                                             struct sss_dom_sid *dom_sid);
++
++/**
++ * @brief Free mapped Samba SID.
++ *
++ * @param[in] ctx         Idmap context
++ * @param[in] smb_sid     Samba SID to be freed.
++ *
++ * @return
++ *  - #IDMAP_CONTEXT_INVALID: Provided context is invalid
++ */
++enum idmap_error_code sss_idmap_free_smb_sid(struct sss_idmap_ctx *ctx,
++                                             struct dom_sid *smb_sid);
++
++/**
++ * @brief Free mapped binary SID.
++ *
++ * @param[in] ctx         Idmap context
++ * @param[in] smb_sid     Binary SID to be freed.
++ *
++ * @return
++ *  - #IDMAP_CONTEXT_INVALID: Provided context is invalid
++ */
++enum idmap_error_code sss_idmap_free_bin_sid(struct sss_idmap_ctx *ctx,
++                                             uint8_t *bin_sid);
++
++/**
+  * @brief Translate error code to a string
+  *
+  * @param[in] err  Idmap error code
+-- 
+1.8.4.2
+
diff --git a/SOURCES/0019-free-idmapped-SIDs-correctly.patch b/SOURCES/0019-free-idmapped-SIDs-correctly.patch
new file mode 100644
index 0000000..241ae14
--- /dev/null
+++ b/SOURCES/0019-free-idmapped-SIDs-correctly.patch
@@ -0,0 +1,205 @@
+From 9fad27b40eff82bcdffa61cafcc54e2d7750faee Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pavel=20B=C5=99ezina?= <pbrezina@redhat.com>
+Date: Fri, 1 Nov 2013 12:27:59 +0100
+Subject: [PATCH 19/31] free idmapped SIDs correctly
+
+Resolves:
+https://fedorahosted.org/sssd/ticket/2133
+---
+ src/providers/ad/ad_id.c                      |  3 +--
+ src/providers/ad/ad_subdomains.c              |  2 ++
+ src/providers/ldap/ldap_id.c                  |  4 ++--
+ src/providers/ldap/sdap_async_initgroups_ad.c |  2 ++
+ src/responder/pac/pacsrv_cmd.c                |  2 ++
+ src/responder/pac/pacsrv_utils.c              |  4 ++--
+ src/tests/cmocka/test_sss_idmap.c             |  2 ++
+ src/tests/sss_idmap-tests.c                   | 14 +++++++-------
+ 8 files changed, 20 insertions(+), 13 deletions(-)
+
+diff --git a/src/providers/ad/ad_id.c b/src/providers/ad/ad_id.c
+index dadb50da92cac87d3162bddb44395dad7d2abbc4..19bc65825be21c6419db1e92db642be0a14b97a8 100644
+--- a/src/providers/ad/ad_id.c
++++ b/src/providers/ad/ad_id.c
+@@ -307,8 +307,7 @@ static errno_t ad_account_can_shortcut(struct be_ctx *be_ctx,
+ 
+ done:
+     if (sid != NULL) {
+-        /* FIXME: use library function when #2133 is fixed */
+-        talloc_free(sid);
++        sss_idmap_free_sid(idmap_ctx->map, sid);
+     }
+ 
+     if (ret == EOK) {
+diff --git a/src/providers/ad/ad_subdomains.c b/src/providers/ad/ad_subdomains.c
+index 28c5eafb395b70e8f3630a43b67c61810683fe7c..dd692fb699ddf14bcf8f9926383e82da77c494e0 100644
+--- a/src/providers/ad/ad_subdomains.c
++++ b/src/providers/ad/ad_subdomains.c
+@@ -302,7 +302,9 @@ ad_subdom_store(struct ad_subdomains_ctx *ctx,
+ 
+     ret = EOK;
+ done:
++    sss_idmap_free_sid(ctx->sdap_id_ctx->opts->idmap_ctx->map, sid_str);
+     talloc_free(tmp_ctx);
++
+     return ret;
+ }
+ 
+diff --git a/src/providers/ldap/ldap_id.c b/src/providers/ldap/ldap_id.c
+index fad1585331b7f0240770d2dc5a2e89788d2ad4da..793bc99ebcec883be7db3fc9dd56fa511d8ba3bb 100644
+--- a/src/providers/ldap/ldap_id.c
++++ b/src/providers/ldap/ldap_id.c
+@@ -139,7 +139,7 @@ struct tevent_req *users_get_send(TALLOC_CTX *memctx,
+ 
+             attr_name = ctx->opts->user_map[SDAP_AT_USER_OBJECTSID].name;
+             ret = sss_filter_sanitize(state, sid, &clean_name);
+-            talloc_zfree(sid);
++            sss_idmap_free_sid(ctx->opts->idmap_ctx->map, sid);
+             if (ret != EOK) {
+                 goto fail;
+             }
+@@ -509,7 +509,7 @@ struct tevent_req *groups_get_send(TALLOC_CTX *memctx,
+ 
+             attr_name = ctx->opts->group_map[SDAP_AT_GROUP_OBJECTSID].name;
+             ret = sss_filter_sanitize(state, sid, &clean_name);
+-            talloc_zfree(sid);
++            sss_idmap_free_sid(ctx->opts->idmap_ctx->map, sid);
+             if (ret != EOK) {
+                 goto fail;
+             }
+diff --git a/src/providers/ldap/sdap_async_initgroups_ad.c b/src/providers/ldap/sdap_async_initgroups_ad.c
+index a0841a799bdbb1ad4de856d1715c88588b3b4da9..aa72c8876ba93eefc6230537801c50ab04e591ce 100644
+--- a/src/providers/ldap/sdap_async_initgroups_ad.c
++++ b/src/providers/ldap/sdap_async_initgroups_ad.c
+@@ -594,6 +594,8 @@ sdap_get_ad_tokengroups_initgroups_lookup_done(struct tevent_req *subreq)
+     in_transaction = false;
+ 
+ done:
++    sss_idmap_free_sid(state->opts->idmap_ctx->map, sid_str);
++
+     if (in_transaction) {
+         sret = sysdb_transaction_cancel(state->sysdb);
+         DEBUG(SSSDBG_FATAL_FAILURE,
+diff --git a/src/responder/pac/pacsrv_cmd.c b/src/responder/pac/pacsrv_cmd.c
+index f6e8abaf580a43417f3ea09929feccf19e5b0f29..144f5f5847e7ead490d59bae0e2fe49722eb9b69 100644
+--- a/src/responder/pac/pacsrv_cmd.c
++++ b/src/responder/pac/pacsrv_cmd.c
+@@ -161,6 +161,8 @@ static errno_t pac_add_pac_user(struct cli_ctx *cctx)
+         goto done;
+     }
+ 
++    talloc_steal(pr_ctx, pr_ctx->user_dom_sid_str);
++
+     ret = responder_get_domain_by_id(cctx->rctx, pr_ctx->user_dom_sid_str,
+                                      &pr_ctx->dom);
+     if (ret == EAGAIN || ret == ENOENT) {
+diff --git a/src/responder/pac/pacsrv_utils.c b/src/responder/pac/pacsrv_utils.c
+index 05b53edee2ada79abf8bd04a6032314b68541d8e..30055a1345b7d943e6adf822438263c92e53b51a 100644
+--- a/src/responder/pac/pacsrv_utils.c
++++ b/src/responder/pac/pacsrv_utils.c
+@@ -264,14 +264,14 @@ errno_t get_sids_from_pac(TALLOC_CTX *mem_ctx,
+             goto done;
+         }
+ 
+-        talloc_zfree(sid_str);
++        sss_idmap_free_sid(pac_ctx->idmap_ctx, sid_str);
+     }
+ 
+     ret = EOK;
+ 
+ done:
+     talloc_free(sid_str);
+-    talloc_free(user_dom_sid_str);
++    sss_idmap_free_sid(pac_ctx->idmap_ctx, user_dom_sid_str);
+ 
+     if (ret == EOK) {
+         *_sid_table = sid_table;
+diff --git a/src/tests/cmocka/test_sss_idmap.c b/src/tests/cmocka/test_sss_idmap.c
+index 53ed35a97863f8f52b82bec64d6dfb192891b0fe..019b4618ef0e14e87cb86d64989e8f5ca9dfdfd8 100644
+--- a/src/tests/cmocka/test_sss_idmap.c
++++ b/src/tests/cmocka/test_sss_idmap.c
+@@ -251,6 +251,7 @@ void test_map_id(void **state)
+     err = sss_idmap_unix_to_sid(test_ctx->idmap_ctx, id, &sid);
+     assert_int_equal(err, IDMAP_SUCCESS);
+     assert_string_equal(sid, TEST_DOM_SID"-0");
++    sss_idmap_free_sid(test_ctx->idmap_ctx, sid);
+ 
+     err = sss_idmap_sid_to_unix(test_ctx->idmap_ctx,
+                                 TEST_DOM_SID"-"TEST_OFFSET_STR, &id);
+@@ -260,6 +261,7 @@ void test_map_id(void **state)
+     err = sss_idmap_unix_to_sid(test_ctx->idmap_ctx, id, &sid);
+     assert_int_equal(err, IDMAP_SUCCESS);
+     assert_string_equal(sid, TEST_DOM_SID"-"TEST_OFFSET_STR);
++    sss_idmap_free_sid(test_ctx->idmap_ctx, sid);
+ }
+ 
+ void test_map_id_external(void **state)
+diff --git a/src/tests/sss_idmap-tests.c b/src/tests/sss_idmap-tests.c
+index 65e61351ddcf52deffe9c8abf38497cd9183c448..b2de0e70f794414587080587af1fd4a06d5ae854 100644
+--- a/src/tests/sss_idmap-tests.c
++++ b/src/tests/sss_idmap-tests.c
+@@ -280,7 +280,7 @@ START_TEST(idmap_test_uid2sid)
+                 "sss_idmap_unix_to_sid returned wrong SID, "
+                 "expected [%s], got [%s].", "S-1-5-21-1-2-3-1000", sid);
+ 
+-    talloc_free(sid);
++    sss_idmap_free_sid(idmap_ctx, sid);
+ }
+ END_TEST
+ 
+@@ -304,7 +304,7 @@ START_TEST(idmap_test_uid2dom_sid)
+                 "sss_idmap_unix_to_dom_sid returned wrong SID, "
+                 "expected [%s], got [%s].", "S-1-5-21-1-2-3-1000", sid);
+ 
+-    talloc_free(sid);
++    sss_idmap_free_sid(idmap_ctx, sid);
+     talloc_free(dom_sid);
+ }
+ END_TEST
+@@ -330,7 +330,7 @@ START_TEST(idmap_test_uid2bin_sid)
+                 "sss_idmap_unix_to_bin_sid returned wrong SID, "
+                 "expected [%s], got [%s].", "S-1-5-21-1-2-3-1000", sid);
+ 
+-    talloc_free(sid);
++    sss_idmap_free_sid(idmap_ctx, sid);
+     talloc_free(bin_sid);
+ }
+ END_TEST
+@@ -385,7 +385,7 @@ START_TEST(idmap_test_sid2dom_sid)
+                 "SID strings do not match.");
+ 
+     talloc_free(dom_sid);
+-    talloc_free(new_sid);
++    sss_idmap_free_sid(idmap_ctx, new_sid);
+ }
+ END_TEST
+ 
+@@ -418,7 +418,7 @@ START_TEST(idmap_test_large_and_too_large_sid)
+                 "did not return IDMAP_SID_INVALID");
+ 
+     talloc_free(dom_sid);
+-    talloc_free(new_sid);
++    sss_idmap_free_sid(idmap_ctx, new_sid);
+ }
+ END_TEST
+ 
+@@ -454,7 +454,7 @@ START_TEST(idmap_test_bin_sid2sid)
+                                             "expected [%s], get [%s]",
+                                             test_sid, sid);
+ 
+-    talloc_free(sid);
++    sss_idmap_free_sid(idmap_ctx, sid);
+ }
+ END_TEST
+ 
+@@ -528,7 +528,7 @@ START_TEST(idmap_test_smb_sid2sid)
+                                             "expected [%s], get [%s]",
+                                             test_sid, sid);
+ 
+-    talloc_free(sid);
++    sss_idmap_free_sid(idmap_ctx, sid);
+ }
+ END_TEST
+ 
+-- 
+1.8.4.2
+
diff --git a/SOURCES/0020-free-idmapped-dom-SIDs-correctly.patch b/SOURCES/0020-free-idmapped-dom-SIDs-correctly.patch
new file mode 100644
index 0000000..8632643
--- /dev/null
+++ b/SOURCES/0020-free-idmapped-dom-SIDs-correctly.patch
@@ -0,0 +1,72 @@
+From 8894f5214c20e530c15a7481ed0b84e533cef519 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pavel=20B=C5=99ezina?= <pbrezina@redhat.com>
+Date: Fri, 1 Nov 2013 13:17:17 +0100
+Subject: [PATCH 20/31] free idmapped dom SIDs correctly
+
+Resolves:
+https://fedorahosted.org/sssd/ticket/2133
+---
+ src/tests/sss_idmap-tests.c | 12 ++++++------
+ 1 file changed, 6 insertions(+), 6 deletions(-)
+
+diff --git a/src/tests/sss_idmap-tests.c b/src/tests/sss_idmap-tests.c
+index b2de0e70f794414587080587af1fd4a06d5ae854..c43e326b1f49ac74c387c76e58f2f32efdce40ff 100644
+--- a/src/tests/sss_idmap-tests.c
++++ b/src/tests/sss_idmap-tests.c
+@@ -261,7 +261,7 @@ START_TEST(idmap_test_dom_sid2uid)
+                 "sss_idmap_dom_sid_to_unix returned wrong id, "
+                 "got [%d], expected [%d].", id, 1000 + IDMAP_RANGE_MIN);
+ 
+-    talloc_free(dom_sid);
++    sss_idmap_free_dom_sid(idmap_ctx, dom_sid);
+ }
+ END_TEST
+ 
+@@ -305,7 +305,7 @@ START_TEST(idmap_test_uid2dom_sid)
+                 "expected [%s], got [%s].", "S-1-5-21-1-2-3-1000", sid);
+ 
+     sss_idmap_free_sid(idmap_ctx, sid);
+-    talloc_free(dom_sid);
++    sss_idmap_free_dom_sid(idmap_ctx, dom_sid);
+ }
+ END_TEST
+ 
+@@ -358,7 +358,7 @@ START_TEST(idmap_test_bin_sid2dom_sid)
+     fail_unless(memcmp(test_bin_sid, new_bin_sid, test_bin_sid_length) == 0,
+                 "Binary SIDs do not match.");
+ 
+-    talloc_free(dom_sid);
++    sss_idmap_free_dom_sid(idmap_ctx, dom_sid);
+     talloc_free(new_bin_sid);
+ }
+ END_TEST
+@@ -384,7 +384,7 @@ START_TEST(idmap_test_sid2dom_sid)
+     fail_unless(strcmp("S-1-5-21-1-2-3-1000", new_sid) == 0,
+                 "SID strings do not match.");
+ 
+-    talloc_free(dom_sid);
++    sss_idmap_free_dom_sid(idmap_ctx, dom_sid);
+     sss_idmap_free_sid(idmap_ctx, new_sid);
+ }
+ END_TEST
+@@ -417,7 +417,7 @@ START_TEST(idmap_test_large_and_too_large_sid)
+                 "Trying to convert  a SID with a too large component "
+                 "did not return IDMAP_SID_INVALID");
+ 
+-    talloc_free(dom_sid);
++    sss_idmap_free_dom_sid(idmap_ctx, dom_sid);
+     sss_idmap_free_sid(idmap_ctx, new_sid);
+ }
+ END_TEST
+@@ -475,7 +475,7 @@ START_TEST(idmap_test_smb_sid2dom_sid)
+     fail_unless(memcmp(&test_smb_sid, new_smb_sid, sizeof(struct dom_sid)) == 0,
+                 "Samba dom_sid-s do not match.");
+ 
+-    talloc_free(dom_sid);
++    sss_idmap_free_dom_sid(idmap_ctx, dom_sid);
+     talloc_free(new_smb_sid);
+ }
+ END_TEST
+-- 
+1.8.4.2
+
diff --git a/SOURCES/0021-free-idmapped-smb-SIDs-correctly.patch b/SOURCES/0021-free-idmapped-smb-SIDs-correctly.patch
new file mode 100644
index 0000000..5566418
--- /dev/null
+++ b/SOURCES/0021-free-idmapped-smb-SIDs-correctly.patch
@@ -0,0 +1,45 @@
+From 6b44bc4465b954183f8a52fbb05da6b63b17f0d1 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pavel=20B=C5=99ezina?= <pbrezina@redhat.com>
+Date: Mon, 4 Nov 2013 11:59:28 +0100
+Subject: [PATCH 21/31] free idmapped smb SIDs correctly
+
+Resolves:
+https://fedorahosted.org/sssd/ticket/2133
+---
+ src/tests/sss_idmap-tests.c | 6 +++---
+ 1 file changed, 3 insertions(+), 3 deletions(-)
+
+diff --git a/src/tests/sss_idmap-tests.c b/src/tests/sss_idmap-tests.c
+index c43e326b1f49ac74c387c76e58f2f32efdce40ff..888be82248045058093d6943ef73742d8812eac3 100644
+--- a/src/tests/sss_idmap-tests.c
++++ b/src/tests/sss_idmap-tests.c
+@@ -476,7 +476,7 @@ START_TEST(idmap_test_smb_sid2dom_sid)
+                 "Samba dom_sid-s do not match.");
+ 
+     sss_idmap_free_dom_sid(idmap_ctx, dom_sid);
+-    talloc_free(new_smb_sid);
++    sss_idmap_free_smb_sid(idmap_ctx, new_smb_sid);
+ }
+ END_TEST
+ 
+@@ -512,7 +512,7 @@ START_TEST(idmap_test_bin_sid2smb_sid)
+     fail_unless(memcmp(&test_smb_sid, smb_sid, sizeof(struct dom_sid)) == 0,
+                  "Samba dom_sid structs do not match.");
+ 
+-    talloc_free(smb_sid);
++    sss_idmap_free_smb_sid(idmap_ctx, smb_sid);
+ }
+ END_TEST
+ 
+@@ -543,7 +543,7 @@ START_TEST(idmap_test_sid2smb_sid)
+     fail_unless(memcmp(&test_smb_sid, smb_sid, sizeof(struct dom_sid)) == 0,
+                  "Samba dom_sid structs do not match.");
+ 
+-    talloc_free(smb_sid);
++    sss_idmap_free_smb_sid(idmap_ctx, smb_sid);
+ }
+ END_TEST
+ 
+-- 
+1.8.4.2
+
diff --git a/SOURCES/0022-free-idmapped-binary-SIDs-correctly.patch b/SOURCES/0022-free-idmapped-binary-SIDs-correctly.patch
new file mode 100644
index 0000000..6ad0fd2
--- /dev/null
+++ b/SOURCES/0022-free-idmapped-binary-SIDs-correctly.patch
@@ -0,0 +1,77 @@
+From ccb2fe6ee1397b3c3d413d6c546cb88701958de3 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pavel=20B=C5=99ezina?= <pbrezina@redhat.com>
+Date: Thu, 7 Nov 2013 11:09:48 +0100
+Subject: [PATCH 22/31] free idmapped binary SIDs correctly
+
+Resolves:
+https://fedorahosted.org/sssd/ticket/2133
+---
+ src/responder/nss/nsssrv_cmd.c |  2 +-
+ src/tests/sss_idmap-tests.c    | 10 +++++-----
+ 2 files changed, 6 insertions(+), 6 deletions(-)
+
+diff --git a/src/responder/nss/nsssrv_cmd.c b/src/responder/nss/nsssrv_cmd.c
+index 07f31188074bf45664969fb33388edb475e53b96..550017c0e4385a7147ed5ef83da2c37cb97c8092 100644
+--- a/src/responder/nss/nsssrv_cmd.c
++++ b/src/responder/nss/nsssrv_cmd.c
+@@ -4341,7 +4341,7 @@ static int nss_cmd_getbysid(enum sss_cli_command cmd, struct cli_ctx *cctx)
+     /* If the body isn't a SID, fail */
+     err = sss_idmap_sid_to_bin_sid(nctx->idmap_ctx, sid_str,
+                                    &bin_sid, &bin_sid_length);
+-    talloc_free(bin_sid);
++    sss_idmap_free_bin_sid(nctx->idmap_ctx, bin_sid);
+     if (err != IDMAP_SUCCESS) {
+         DEBUG(SSSDBG_OP_FAILURE, ("sss_idmap_sid_to_bin_sid failed for [%s].\n",
+                                   body));
+diff --git a/src/tests/sss_idmap-tests.c b/src/tests/sss_idmap-tests.c
+index 888be82248045058093d6943ef73742d8812eac3..f5ec68383679bfc685467bd625c86b8d6f474d48 100644
+--- a/src/tests/sss_idmap-tests.c
++++ b/src/tests/sss_idmap-tests.c
+@@ -242,7 +242,7 @@ START_TEST(idmap_test_bin_sid2uid)
+                 "sss_idmap_bin_sid_to_unix returned wrong id, "
+                 "got [%d], expected [%d].", id, 1000 + IDMAP_RANGE_MIN);
+ 
+-    talloc_free(bin_sid);
++    sss_idmap_free_bin_sid(idmap_ctx, bin_sid);
+ }
+ END_TEST
+ 
+@@ -331,7 +331,7 @@ START_TEST(idmap_test_uid2bin_sid)
+                 "expected [%s], got [%s].", "S-1-5-21-1-2-3-1000", sid);
+ 
+     sss_idmap_free_sid(idmap_ctx, sid);
+-    talloc_free(bin_sid);
++    sss_idmap_free_bin_sid(idmap_ctx, bin_sid);
+ }
+ END_TEST
+ 
+@@ -359,7 +359,7 @@ START_TEST(idmap_test_bin_sid2dom_sid)
+                 "Binary SIDs do not match.");
+ 
+     sss_idmap_free_dom_sid(idmap_ctx, dom_sid);
+-    talloc_free(new_bin_sid);
++    sss_idmap_free_bin_sid(idmap_ctx, new_bin_sid);
+ }
+ END_TEST
+ 
+@@ -437,7 +437,7 @@ START_TEST(idmap_test_sid2bin_sid)
+     fail_unless(memcmp(bin_sid, test_bin_sid, test_bin_sid_length) == 0,
+                 "Binary SIDs do not match");
+ 
+-    talloc_free(bin_sid);
++    sss_idmap_free_bin_sid(idmap_ctx, bin_sid);
+ }
+ END_TEST
+ 
+@@ -496,7 +496,7 @@ START_TEST(idmap_test_smb_sid2bin_sid)
+     fail_unless(memcmp(bin_sid, test_bin_sid, test_bin_sid_length) == 0,
+                 "Binary SIDs do not match.");
+ 
+-    talloc_free(bin_sid);
++    sss_idmap_free_bin_sid(idmap_ctx, bin_sid);
+ }
+ END_TEST
+ 
+-- 
+1.8.4.2
+
diff --git a/SOURCES/0023-Initialize-sid_str-to-NULL-to-avoid-freeing-random-d.patch b/SOURCES/0023-Initialize-sid_str-to-NULL-to-avoid-freeing-random-d.patch
new file mode 100644
index 0000000..4839823
--- /dev/null
+++ b/SOURCES/0023-Initialize-sid_str-to-NULL-to-avoid-freeing-random-d.patch
@@ -0,0 +1,41 @@
+From 456d952eecf2f068feafd2fff8bec8df84eba8ca Mon Sep 17 00:00:00 2001
+From: Jakub Hrozek <jhrozek@redhat.com>
+Date: Thu, 7 Nov 2013 12:00:43 +0100
+Subject: [PATCH 23/31] Initialize sid_str to NULL to avoid freeing random data
+
+If any function before failed, sss_idmap_free_sid() might have been
+called with random data.
+---
+ src/providers/ad/ad_subdomains.c              | 2 +-
+ src/providers/ldap/sdap_async_initgroups_ad.c | 2 +-
+ 2 files changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/src/providers/ad/ad_subdomains.c b/src/providers/ad/ad_subdomains.c
+index dd692fb699ddf14bcf8f9926383e82da77c494e0..100fb13e99f7bf4b3946b1f5c5f9c626674bfb46 100644
+--- a/src/providers/ad/ad_subdomains.c
++++ b/src/providers/ad/ad_subdomains.c
+@@ -234,7 +234,7 @@ ad_subdom_store(struct ad_subdomains_ctx *ctx,
+     errno_t ret;
+     enum idmap_error_code err;
+     struct ldb_message_element *el;
+-    char *sid_str;
++    char *sid_str = NULL;
+     uint32_t trust_type;
+     bool mpg;
+ 
+diff --git a/src/providers/ldap/sdap_async_initgroups_ad.c b/src/providers/ldap/sdap_async_initgroups_ad.c
+index aa72c8876ba93eefc6230537801c50ab04e591ce..e58d93fb2da36febd6074381882192ba9e204e86 100644
+--- a/src/providers/ldap/sdap_async_initgroups_ad.c
++++ b/src/providers/ldap/sdap_async_initgroups_ad.c
+@@ -361,7 +361,7 @@ sdap_get_ad_tokengroups_initgroups_lookup_done(struct tevent_req *subreq)
+     size_t user_count, group_count, i;
+     TALLOC_CTX *tmp_ctx;
+     bool in_transaction = false;
+-    char *sid_str;
++    char *sid_str = NULL;
+     gid_t gid;
+     time_t now;
+     struct sss_domain_info *group_domain;
+-- 
+1.8.4.2
+
diff --git a/SOURCES/0024-ad-refactor-tokengroups-initgroups.patch b/SOURCES/0024-ad-refactor-tokengroups-initgroups.patch
new file mode 100644
index 0000000..cd4ad74
--- /dev/null
+++ b/SOURCES/0024-ad-refactor-tokengroups-initgroups.patch
@@ -0,0 +1,749 @@
+From 8b581624e18d6f232d3174ed112d032bb6deffba Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pavel=20B=C5=99ezina?= <pbrezina@redhat.com>
+Date: Tue, 12 Nov 2013 13:52:40 +0100
+Subject: [PATCH 24/31] ad: refactor tokengroups initgroups
+
+sdap_get_ad_tokengroups_initgroups is split into more parts so
+it can be reused later.
+---
+ src/providers/ldap/sdap_async.h               |  20 +-
+ src/providers/ldap/sdap_async_initgroups.c    |  16 +-
+ src/providers/ldap/sdap_async_initgroups_ad.c | 552 ++++++++++++++++----------
+ 3 files changed, 357 insertions(+), 231 deletions(-)
+
+diff --git a/src/providers/ldap/sdap_async.h b/src/providers/ldap/sdap_async.h
+index dbf572cdc82b100ba9c26b4853f05db1ba5fa4ed..67623454e675f648259c089acca59258f386ecdb 100644
+--- a/src/providers/ldap/sdap_async.h
++++ b/src/providers/ldap/sdap_async.h
+@@ -294,17 +294,17 @@ sdap_get_ad_match_rule_initgroups_recv(struct tevent_req *req);
+ 
+ 
+ struct tevent_req *
+-sdap_get_ad_tokengroups_initgroups_send(TALLOC_CTX *mem_ctx,
+-                                        struct tevent_context *ev,
+-                                        struct sdap_options *opts,
+-                                        struct sysdb_ctx *sysdb,
+-                                        struct sss_domain_info *domain,
+-                                        struct sdap_handle *sh,
+-                                        const char *name,
+-                                        const char *orig_dn,
+-                                        int timeout);
++sdap_ad_tokengroups_initgroups_send(TALLOC_CTX *mem_ctx,
++                                    struct tevent_context *ev,
++                                    struct sdap_options *opts,
++                                    struct sysdb_ctx *sysdb,
++                                    struct sss_domain_info *domain,
++                                    struct sdap_handle *sh,
++                                    const char *name,
++                                    const char *orig_dn,
++                                    int timeout);
+ 
+ errno_t
+-sdap_get_ad_tokengroups_initgroups_recv(struct tevent_req *req);
++sdap_ad_tokengroups_initgroups_recv(struct tevent_req *req);
+ 
+ #endif /* _SDAP_ASYNC_H_ */
+diff --git a/src/providers/ldap/sdap_async_initgroups.c b/src/providers/ldap/sdap_async_initgroups.c
+index c16d484950e06c8474cc38db45b978b624473056..7d5cd2e7cbd86e2eb9774dfee1b8e31edec57b88 100644
+--- a/src/providers/ldap/sdap_async_initgroups.c
++++ b/src/providers/ldap/sdap_async_initgroups.c
+@@ -2857,13 +2857,13 @@ static void sdap_get_initgr_user(struct tevent_req *subreq)
+             /* Take advantage of AD's tokenGroups mechanism to look up all
+              * parent groups in a single request.
+              */
+-            subreq = sdap_get_ad_tokengroups_initgroups_send(state, state->ev,
+-                                                             state->opts,
+-                                                             state->sysdb,
+-                                                             state->dom,
+-                                                             state->sh,
+-                                                             cname, orig_dn,
+-                                                             state->timeout);
++            subreq = sdap_ad_tokengroups_initgroups_send(state, state->ev,
++                                                         state->opts,
++                                                         state->sysdb,
++                                                         state->dom,
++                                                         state->sh,
++                                                         cname, orig_dn,
++                                                         state->timeout);
+         } else if (state->opts->support_matching_rule
+                     && dp_opt_get_bool(state->opts->basic,
+                                        SDAP_AD_MATCHING_RULE_INITGROUPS)) {
+@@ -2952,7 +2952,7 @@ static void sdap_get_initgr_done(struct tevent_req *subreq)
+     case SDAP_SCHEMA_AD:
+         if (state->use_id_mapping
+                 && state->opts->dc_functional_level >= DS_BEHAVIOR_WIN2008) {
+-            ret = sdap_get_ad_tokengroups_initgroups_recv(subreq);
++            ret = sdap_ad_tokengroups_initgroups_recv(subreq);
+         }
+         else if (state->opts->support_matching_rule
+                 && dp_opt_get_bool(state->opts->basic,
+diff --git a/src/providers/ldap/sdap_async_initgroups_ad.c b/src/providers/ldap/sdap_async_initgroups_ad.c
+index e58d93fb2da36febd6074381882192ba9e204e86..7ba155338a358681c1bd201bee1c75f67afb4650 100644
+--- a/src/providers/ldap/sdap_async_initgroups_ad.c
++++ b/src/providers/ldap/sdap_async_initgroups_ad.c
+@@ -298,96 +298,87 @@ sdap_get_ad_match_rule_initgroups_recv(struct tevent_req *req)
+     return EOK;
+ }
+ 
+-struct sdap_ad_tokengroups_initgr_state {
++struct sdap_get_ad_tokengroups_state {
+     struct tevent_context *ev;
+-    struct sdap_options *opts;
+-    struct sysdb_ctx *sysdb;
+-    struct sss_domain_info *domain;
+-    struct sdap_handle *sh;
++    struct sss_idmap_ctx *idmap_ctx;
+     const char *username;
++
++    char **sids;
++    size_t num_sids;
+ };
+ 
+-static void
+-sdap_get_ad_tokengroups_initgroups_lookup_done(struct tevent_req *req);
++static void sdap_get_ad_tokengroups_done(struct tevent_req *subreq);
+ 
+-struct tevent_req *
+-sdap_get_ad_tokengroups_initgroups_send(TALLOC_CTX *mem_ctx,
+-                                        struct tevent_context *ev,
+-                                        struct sdap_options *opts,
+-                                        struct sysdb_ctx *sysdb,
+-                                        struct sss_domain_info *domain,
+-                                        struct sdap_handle *sh,
+-                                        const char *name,
+-                                        const char *orig_dn,
+-                                        int timeout)
++static struct tevent_req *
++sdap_get_ad_tokengroups_send(TALLOC_CTX *mem_ctx,
++                             struct tevent_context *ev,
++                             struct sdap_options *opts,
++                             struct sdap_handle *sh,
++                             const char *name,
++                             const char *orig_dn,
++                             int timeout)
+ {
+-    struct tevent_req *req;
+-    struct tevent_req *subreq;
+-    struct sdap_ad_tokengroups_initgr_state *state;
++    struct sdap_get_ad_tokengroups_state *state = NULL;
++    struct tevent_req *req = NULL;
++    struct tevent_req *subreq = NULL;
+     const char *attrs[] = {AD_TOKENGROUPS_ATTR, NULL};
++    errno_t ret;
+ 
+     req = tevent_req_create(mem_ctx, &state,
+-                            struct sdap_ad_tokengroups_initgr_state);
+-    if (!req) return NULL;
++                            struct sdap_get_ad_tokengroups_state);
++    if (req == NULL) {
++        DEBUG(SSSDBG_CRIT_FAILURE, ("tevent_req_create() failed\n"));
++        return NULL;
++    }
+ 
++    state->idmap_ctx = opts->idmap_ctx->map;
+     state->ev = ev;
+-    state->opts = opts;
+-    state->sysdb = sysdb;
+-    state->domain = domain;
+-    state->sh = sh;
+-    state->username = name;
++    state->username = talloc_strdup(state, name);
++    if (state->username == NULL) {
++        ret = ENOMEM;
++        goto immediately;
++    }
++
++    subreq = sdap_get_generic_send(state, state->ev, opts, sh, orig_dn,
++                                   LDAP_SCOPE_BASE, NULL, attrs,
++                                   NULL, 0, timeout, false);
++    if (subreq == NULL) {
++        ret = ENOMEM;
++        goto immediately;
++    }
++
++    tevent_req_set_callback(subreq, sdap_get_ad_tokengroups_done, req);
++
++    return req;
+ 
+-    subreq = sdap_get_generic_send(
+-            state, state->ev, state->opts, state->sh,
+-            orig_dn, LDAP_SCOPE_BASE, NULL, attrs,
+-            NULL, 0, timeout, false);
+-    if (!subreq) {
+-        tevent_req_error(req, ENOMEM);
+-        tevent_req_post(req, ev);
+-        return req;
++immediately:
++    if (ret == EOK) {
++        tevent_req_done(req);
++    } else {
++        tevent_req_error(req, ret);
+     }
++    tevent_req_post(req, ev);
+ 
+-    tevent_req_set_callback(subreq,
+-                            sdap_get_ad_tokengroups_initgroups_lookup_done,
+-                            req);
+     return req;
+ }
+ 
+-static void
+-sdap_get_ad_tokengroups_initgroups_lookup_done(struct tevent_req *subreq)
++static void sdap_get_ad_tokengroups_done(struct tevent_req *subreq)
+ {
+-    errno_t ret, sret;
++    TALLOC_CTX *tmp_ctx = NULL;
++    struct sdap_get_ad_tokengroups_state *state = NULL;
++    struct tevent_req *req = NULL;
++    struct sysdb_attrs **users = NULL;
++    struct ldb_message_element *el = NULL;
+     enum idmap_error_code err;
+-    size_t user_count, group_count, i;
+-    TALLOC_CTX *tmp_ctx;
+-    bool in_transaction = false;
+     char *sid_str = NULL;
+-    gid_t gid;
+-    time_t now;
+-    struct sss_domain_info *group_domain;
+-    struct sysdb_attrs **users;
+-    struct ldb_message_element *el;
+-    struct ldb_message *msg;
+-    struct ldb_dn *group_ldb_dn;
+-    const char *group_str_dn;
+-    char **ldap_grouplist;
+-    char **sysdb_grouplist;
+-    char **add_groups;
+-    char **del_groups;
+-    const char *attrs[] = { SYSDB_NAME, NULL };
+-    const char *group_name;
+-    struct tevent_req *req =
+-            tevent_req_callback_data(subreq, struct tevent_req);
+-    struct sdap_ad_tokengroups_initgr_state *state =
+-            tevent_req_data(req, struct sdap_ad_tokengroups_initgr_state);
++    size_t num_users;
++    size_t i;
++    errno_t ret;
+ 
+-    tmp_ctx = talloc_new(NULL);
+-    if (!tmp_ctx) {
+-        ret = ENOMEM;
+-        goto done;
+-    }
++    req = tevent_req_callback_data(subreq, struct tevent_req);
++    state = tevent_req_data(req, struct sdap_get_ad_tokengroups_state);
+ 
+-    ret = sdap_get_generic_recv(subreq, tmp_ctx, &user_count, &users);
++    ret = sdap_get_generic_recv(subreq, tmp_ctx, &num_users, &users);
+     talloc_zfree(subreq);
+     if (ret != EOK) {
+         DEBUG(SSSDBG_MINOR_FAILURE,
+@@ -395,226 +386,361 @@ sdap_get_ad_tokengroups_initgroups_lookup_done(struct tevent_req *subreq)
+         goto done;
+     }
+ 
+-    if (user_count != 1) {
++    if (num_users != 1) {
+         DEBUG(SSSDBG_MINOR_FAILURE,
+               ("More than one result on a base search!\n"));
+         ret = EINVAL;
+         goto done;
+     }
+ 
+-    /* Get the list of group SIDs */
+-    ret = sysdb_attrs_get_el_ext(users[0], AD_TOKENGROUPS_ATTR,
+-                                 false, &el);
+-    if (ret != EOK) {
+-        if (ret == ENOENT) {
+-            DEBUG(SSSDBG_TRACE_LIBS,
+-                  ("No tokenGroups entries for [%s]\n",
+-                   state->username));
+-            /* No groups in LDAP. We need to ensure that the
+-             * sysdb matches.
+-             */
+-            el = talloc_zero(tmp_ctx, struct ldb_message_element);
+-            if (!el) {
+-                ret = ENOMEM;
+-                goto done;
+-            }
+-            el->num_values = 0;
++    /* get the list of sids from tokengroups */
++    ret = sysdb_attrs_get_el_ext(users[0], AD_TOKENGROUPS_ATTR, false, &el);
++    if (ret == ENOENT) {
++        DEBUG(SSSDBG_TRACE_LIBS, ("No tokenGroups entries for [%s]\n",
++                                  state->username));
+ 
+-            /* This will skip the group-processing loop below
+-             * and proceed to removing any sysdb groups.
+-             */
+-        } else {
+-            DEBUG(SSSDBG_MINOR_FAILURE,
+-                  ("Could not read tokenGroups attribute: [%s]\n",
+-                   strerror(ret)));
+-            goto done;
+-        }
++        state->sids = NULL;
++        state->num_sids = 0;
++        ret = EOK;
++        goto done;
++    } else if (ret != EOK) {
++        DEBUG(SSSDBG_MINOR_FAILURE, ("Could not read tokenGroups attribute: "
++                                     "[%s]\n", strerror(ret)));
++        goto done;
+     }
+ 
+-    /* Process the groups */
+-    now = time(NULL);
+-
+-    ret = sysdb_transaction_start(state->sysdb);
+-    if (ret != EOK) goto done;
+-    in_transaction = true;
+-
+-    ldap_grouplist = talloc_array(tmp_ctx, char *, el->num_values + 1);
+-    if (!ldap_grouplist) {
++    state->num_sids = 0;
++    state->sids = talloc_zero_array(state, char*, el->num_values);
++    if (state->sids == NULL) {
+         ret = ENOMEM;
+         goto done;
+     }
+-    group_count = 0;
+ 
++    /* convert binary sid to string */
+     for (i = 0; i < el->num_values; i++) {
+-        /* Get the SID and convert it to a GID */
+-
+-        err = sss_idmap_bin_sid_to_sid(state->opts->idmap_ctx->map,
+-                                        el->values[i].data,
+-                                        el->values[i].length,
+-                                        &sid_str);
++        err = sss_idmap_bin_sid_to_sid(state->idmap_ctx, el->values[i].data,
++                                       el->values[i].length, &sid_str);
+         if (err != IDMAP_SUCCESS) {
+             DEBUG(SSSDBG_MINOR_FAILURE,
+                   ("Could not convert binary SID to string: [%s]. Skipping\n",
+                    idmap_error_string(err)));
+             continue;
+         }
+-        DEBUG(SSSDBG_TRACE_LIBS,
+-              ("Processing membership SID [%s]\n",
+-               sid_str));
+-        ret = sdap_idmap_sid_to_unix(state->opts->idmap_ctx, sid_str,
+-                                     &gid);
++
++        state->sids[i] = talloc_move(state->sids, &sid_str);
++        state->num_sids++;
++    }
++
++    /* shrink array to final number of elements */
++    state->sids = talloc_realloc(state, state->sids, char*, state->num_sids);
++    if (state->sids == NULL) {
++        ret = ENOMEM;
++        goto done;
++    }
++
++    ret = EOK;
++
++done:
++    talloc_free(tmp_ctx);
++
++    if (ret != EOK) {
++        tevent_req_error(req, ret);
++        return;
++    }
++
++    tevent_req_done(req);
++}
++
++static errno_t sdap_get_ad_tokengroups_recv(TALLOC_CTX *mem_ctx,
++                                            struct tevent_req *req,
++                                            size_t *_num_sids,
++                                            char ***_sids)
++{
++    struct sdap_get_ad_tokengroups_state *state = NULL;
++    state = tevent_req_data(req, struct sdap_get_ad_tokengroups_state);
++
++    TEVENT_REQ_RETURN_ON_ERROR(req);
++
++    if (_num_sids != NULL) {
++        *_num_sids = state->num_sids;
++    }
++
++    if (_sids != NULL) {
++        *_sids = talloc_steal(mem_ctx, state->sids);
++    }
++
++    return EOK;
++}
++
++static errno_t
++sdap_ad_tokengroups_update_members(TALLOC_CTX *mem_ctx,
++                                   const char *username,
++                                   struct sysdb_ctx *sysdb,
++                                   struct sss_domain_info *domain,
++                                   char **ldap_groups)
++{
++    TALLOC_CTX *tmp_ctx = NULL;
++    char **sysdb_groups = NULL;
++    char **add_groups = NULL;
++    char **del_groups = NULL;
++    errno_t ret;
++
++    tmp_ctx = talloc_new(NULL);
++    if (tmp_ctx == NULL) {
++        DEBUG(SSSDBG_CRIT_FAILURE, ("talloc_new() failed\n"));
++        return ENOMEM;
++    }
++
++    /* Get the current sysdb group list for this user so we can update it. */
++    ret = get_sysdb_grouplist_dn(tmp_ctx, sysdb, domain,
++                                 username, &sysdb_groups);
++    if (ret != EOK) {
++        DEBUG(SSSDBG_MINOR_FAILURE, ("Could not get the list of groups for "
++              "[%s] in the sysdb: [%s]\n", username, strerror(ret)));
++        goto done;
++    }
++
++    /* Find the differences between the sysdb and LDAP lists.
++     * Groups in the sysdb only must be removed. */
++    ret = diff_string_lists(tmp_ctx, ldap_groups, sysdb_groups,
++                            &add_groups, &del_groups, NULL);
++    if (ret != EOK) {
++        goto done;
++    }
++
++    DEBUG(SSSDBG_TRACE_LIBS, ("Updating memberships for [%s]\n", username));
++
++    ret = sysdb_update_members_dn(domain->sysdb, domain, username,
++                                  SYSDB_MEMBER_USER,
++                                  (const char *const *) add_groups,
++                                  (const char *const *) del_groups);
++    if (ret != EOK) {
++        DEBUG(SSSDBG_MINOR_FAILURE, ("Membership update failed [%d]: %s\n",
++                                     ret, strerror(ret)));
++        goto done;
++    }
++
++done:
++    talloc_free(tmp_ctx);
++    return ret;
++}
++
++struct sdap_ad_tokengroups_initgroups_state {
++    struct sdap_idmap_ctx *idmap_ctx;
++    struct sysdb_ctx *sysdb;
++    struct sss_domain_info *domain;
++    const char *username;
++};
++
++static void sdap_ad_tokengroups_initgroups_done(struct tevent_req *subreq);
++
++struct tevent_req *
++sdap_ad_tokengroups_initgroups_send(TALLOC_CTX *mem_ctx,
++                                    struct tevent_context *ev,
++                                    struct sdap_options *opts,
++                                    struct sysdb_ctx *sysdb,
++                                    struct sss_domain_info *domain,
++                                    struct sdap_handle *sh,
++                                    const char *name,
++                                    const char *orig_dn,
++                                    int timeout)
++{
++    struct sdap_ad_tokengroups_initgroups_state *state = NULL;
++    struct tevent_req *req = NULL;
++    struct tevent_req *subreq = NULL;
++    errno_t ret;
++
++    req = tevent_req_create(mem_ctx, &state,
++                            struct sdap_ad_tokengroups_initgroups_state);
++    if (req == NULL) {
++        DEBUG(SSSDBG_CRIT_FAILURE, ("tevent_req_create() failed\n"));
++        return NULL;
++    }
++
++    state->idmap_ctx = opts->idmap_ctx;
++    state->sysdb = sysdb;
++    state->domain = domain;
++    state->username = talloc_strdup(state, name);
++    if (state->username == NULL) {
++        ret = ENOMEM;
++        goto immediately;
++    }
++
++    subreq = sdap_get_ad_tokengroups_send(state, ev, opts, sh, name, orig_dn,
++                                          timeout);
++    if (subreq == NULL) {
++        ret = ENOMEM;
++        goto immediately;
++    }
++
++    tevent_req_set_callback(subreq, sdap_ad_tokengroups_initgroups_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 sdap_ad_tokengroups_initgroups_done(struct tevent_req *subreq)
++{
++    TALLOC_CTX *tmp_ctx = NULL;
++    struct sdap_ad_tokengroups_initgroups_state *state = NULL;
++    struct tevent_req *req = NULL;
++    struct sss_domain_info *domain = NULL;
++    struct ldb_message *msg = NULL;
++    const char *attrs[] = {SYSDB_NAME, NULL};
++    const char *name = NULL;
++    const char *sid = NULL;
++    char **sids = NULL;
++    size_t num_sids;
++    size_t i;
++    time_t now;
++    gid_t gid;
++    char **groups = NULL;
++    size_t num_groups;
++    errno_t ret, sret;
++    bool in_transaction;
++
++    tmp_ctx = talloc_new(NULL);
++    if (tmp_ctx == NULL) {
++        DEBUG(SSSDBG_CRIT_FAILURE, ("talloc_new() failed\n"));
++        ret = ENOMEM;
++        goto done;
++    }
++
++    req = tevent_req_callback_data(subreq, struct tevent_req);
++    state = tevent_req_data(req, struct sdap_ad_tokengroups_initgroups_state);
++
++    ret = sdap_get_ad_tokengroups_recv(state, subreq, &num_sids, &sids);
++    talloc_zfree(subreq);
++    if (ret != EOK) {
++        DEBUG(SSSDBG_CRIT_FAILURE, ("Unable to acquire tokengroups [%d]: %s\n",
++                                    ret, strerror(ret)));
++        goto done;
++    }
++
++    num_groups = 0;
++    groups = talloc_zero_array(tmp_ctx, char*, num_sids + 1);
++    if (groups == NULL) {
++        ret = ENOMEM;
++        goto done;
++    }
++
++    now = time(NULL);
++    ret = sysdb_transaction_start(state->sysdb);
++    if (ret != EOK) {
++        goto done;
++    }
++    in_transaction = true;
++
++    for (i = 0; i < num_sids; i++) {
++        sid = sids[i];
++        DEBUG(SSSDBG_TRACE_LIBS, ("Processing membership SID [%s]\n", sid));
++
++        ret = sdap_idmap_sid_to_unix(state->idmap_ctx, sid, &gid);
+         if (ret == ENOTSUP) {
+             DEBUG(SSSDBG_TRACE_FUNC, ("Skipping built-in object.\n"));
+             ret = EOK;
+             continue;
+         } else if (ret != EOK) {
+-            DEBUG(SSSDBG_MINOR_FAILURE,
+-                  ("Could not convert SID to GID: [%s]. Skipping\n",
+-                   strerror(ret)));
++            DEBUG(SSSDBG_MINOR_FAILURE, ("Could not convert SID to GID: [%s]. "
++                                         "Skipping\n", strerror(ret)));
+             continue;
+         }
+ 
+-        group_domain = find_subdomain_by_sid(get_domains_head(state->domain),
+-                                                              sid_str);
+-        if (group_domain == NULL) {
+-            DEBUG(SSSDBG_MINOR_FAILURE, ("Domain not found for SID %s\n",
+-                                         sid_str));
++        domain = find_subdomain_by_sid(get_domains_head(state->domain), sid);
++        if (domain == NULL) {
++            DEBUG(SSSDBG_MINOR_FAILURE, ("Domain not found for SID %s\n", sid));
+             continue;
+         }
+ 
+-        DEBUG(SSSDBG_TRACE_LIBS,
+-              ("Processing membership GID [%"SPRIgid"]\n", gid));
++        DEBUG(SSSDBG_TRACE_LIBS, ("SID [%s] maps to GID [%"SPRIgid"]\n",
++                                  sid, gid));
+ 
+         /* Check whether this GID already exists in the sysdb */
+-        ret = sysdb_search_group_by_gid(tmp_ctx, group_domain->sysdb,
+-                                        group_domain, gid, attrs, &msg);
++        ret = sysdb_search_group_by_gid(tmp_ctx, domain->sysdb, domain,
++                                        gid, attrs, &msg);
+         if (ret == EOK) {
+-            group_name = ldb_msg_find_attr_as_string(msg, SYSDB_NAME, NULL);
+-            if (!group_name) {
++            name = ldb_msg_find_attr_as_string(msg, SYSDB_NAME, NULL);
++            if (name == NULL) {
+                 DEBUG(SSSDBG_MINOR_FAILURE,
+                       ("Could not retrieve group name from sysdb\n"));
+                 ret = EINVAL;
+                 goto done;
+             }
+         } else if (ret == ENOENT) {
+-            /* This is a new group. For now, we will store it
+-             * under the name of its SID. When a direct lookup of
+-             * the group or its GID occurs, it will replace this
+-             * temporary entry.
+-             */
+-
+-            group_name = sid_str;
+-            ret = sysdb_add_incomplete_group(group_domain->sysdb,
+-                                             group_domain,
+-                                             group_name, gid,
+-                                             NULL, sid_str, false, now);
++            /* This is a new group. For now, we will store it under the name
++             * of its SID. When a direct lookup of the group or its GID occurs,
++             * it will replace this temporary entry. */
++            name = sid;
++            ret = sysdb_add_incomplete_group(domain->sysdb, domain, name, gid,
++                                             NULL, sid, false, now);
+             if (ret != EOK) {
+-                DEBUG(SSSDBG_MINOR_FAILURE,
+-                      ("Could not create incomplete group: [%s]\n",
+-                       strerror(ret)));
++                DEBUG(SSSDBG_MINOR_FAILURE, ("Could not create incomplete "
++                                             "group: [%s]\n", strerror(ret)));
+                 goto done;
+             }
+         } else {
+             /* Unexpected error */
+-            DEBUG(SSSDBG_MINOR_FAILURE,
+-                  ("Could not look up group in sysdb: [%s]\n",
+-                   strerror(ret)));
++            DEBUG(SSSDBG_MINOR_FAILURE, ("Could not look up group in sysdb: "
++                                         "[%s]\n", strerror(ret)));
+             goto done;
+         }
+ 
+-        group_ldb_dn = sysdb_group_dn(group_domain->sysdb, tmp_ctx,
+-                                      group_domain, group_name);
+-        if (group_ldb_dn == NULL) {
+-            DEBUG(SSSDBG_CRIT_FAILURE, ("sysdb_group_dn() failed\n"));
++        groups[num_groups] = sysdb_group_strdn(tmp_ctx, domain->name, name);
++        if (groups[num_groups] == NULL) {
+             ret = ENOMEM;
+             goto done;
+         }
+-
+-        group_str_dn = ldb_dn_get_linearized(group_ldb_dn);
+-        if (group_str_dn == NULL) {
+-            DEBUG(SSSDBG_CRIT_FAILURE, ("ldb_dn_get_linearized() failed\n"));
+-            ret = EINVAL;
+-            goto done;
+-        }
+-
+-        ldap_grouplist[group_count] =
+-                talloc_strdup(ldap_grouplist, group_str_dn);
+-        if (!ldap_grouplist[group_count]) {
+-            ret = ENOMEM;
+-            goto done;
+-        }
+-
+-        talloc_zfree(group_ldb_dn); /* also frees group_str_dn */
+-        group_str_dn = NULL;
+-
+-        group_count++;
+-    }
+-    ldap_grouplist[group_count] = NULL;
+-
+-    /* Get the current sysdb group list for this user
+-     * so we can update it.
+-     */
+-    ret = get_sysdb_grouplist_dn(state, state->sysdb, state->domain,
+-                                 state->username, &sysdb_grouplist);
+-    if (ret != EOK) {
+-        DEBUG(SSSDBG_MINOR_FAILURE,
+-              ("Could not get the list of groups for [%s] in the sysdb: "
+-               "[%s]\n",
+-               state->username, strerror(ret)));
+-        goto done;
++        num_groups++;
+     }
+ 
+-    /* Find the differences between the sysdb and LDAP lists
+-     * Groups in the sysdb only must be removed.
+-     */
+-    ret = diff_string_lists(tmp_ctx, ldap_grouplist, sysdb_grouplist,
+-                            &add_groups, &del_groups, NULL);
+-    if (ret != EOK) goto done;
++    groups[num_groups] = NULL;
+ 
+-    DEBUG(SSSDBG_TRACE_LIBS,
+-          ("Updating memberships for [%s]\n", state->username));
+-    ret = sysdb_update_members_dn(state->sysdb, state->domain,
+-                                  state->username, SYSDB_MEMBER_USER,
+-                                  (const char *const *) add_groups,
+-                                  (const char *const *) del_groups);
++    ret = sdap_ad_tokengroups_update_members(state, state->username,
++                                             state->sysdb, state->domain,
++                                             groups);
+     if (ret != EOK) {
+-        DEBUG(SSSDBG_MINOR_FAILURE,
+-              ("Membership update failed [%d]: %s\n",
+-               ret, strerror(ret)));
++        DEBUG(SSSDBG_MINOR_FAILURE, ("Membership update failed [%d]: %s\n",
++                                     ret, strerror(ret)));
+         goto done;
+     }
+ 
+     ret = sysdb_transaction_commit(state->sysdb);
+     if (ret != EOK) {
+-        DEBUG(SSSDBG_CRIT_FAILURE,
+-              ("Could not commit transaction! [%s]\n",
+-               strerror(ret)));
++        DEBUG(SSSDBG_CRIT_FAILURE, ("Could not commit transaction! [%s]\n",
++                                    strerror(ret)));
+         goto done;
+     }
+     in_transaction = false;
+ 
+ done:
+-    sss_idmap_free_sid(state->opts->idmap_ctx->map, sid_str);
++    talloc_free(tmp_ctx);
+ 
+     if (in_transaction) {
+         sret = sysdb_transaction_cancel(state->sysdb);
+-        DEBUG(SSSDBG_FATAL_FAILURE,
+-              ("Could not cancel transaction! [%s]\n",
+-               strerror(sret)));
++        DEBUG(SSSDBG_FATAL_FAILURE, ("Could not cancel transaction! [%s]\n",
++                                     strerror(sret)));
+     }
+ 
+-    if (ret == EOK) {
+-        tevent_req_done(req);
+-    } else {
++    if (ret != EOK) {
+         tevent_req_error(req, ret);
++        return;
+     }
+-    talloc_free(tmp_ctx);
+-    return;
++
++    tevent_req_done(req);
+ }
+ 
+-errno_t
+-sdap_get_ad_tokengroups_initgroups_recv(struct tevent_req *req)
++errno_t sdap_ad_tokengroups_initgroups_recv(struct tevent_req *req)
+ {
+     TEVENT_REQ_RETURN_ON_ERROR(req);
++
+     return EOK;
+ }
+-- 
+1.8.4.2
+
diff --git a/SOURCES/0025-ad-use-tokengroups-even-when-id-mapping-is-disabled.patch b/SOURCES/0025-ad-use-tokengroups-even-when-id-mapping-is-disabled.patch
new file mode 100644
index 0000000..50f54c9
--- /dev/null
+++ b/SOURCES/0025-ad-use-tokengroups-even-when-id-mapping-is-disabled.patch
@@ -0,0 +1,677 @@
+From 6385798f807d370fe6685653e337f65bf59f21bc Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pavel=20B=C5=99ezina?= <pbrezina@redhat.com>
+Date: Tue, 12 Nov 2013 13:51:34 +0100
+Subject: [PATCH 25/31] ad: use tokengroups even when id mapping is disabled
+
+https://fedorahosted.org/sssd/ticket/1568
+---
+ src/providers/ldap/sdap_async.h               |   4 +-
+ src/providers/ldap/sdap_async_initgroups.c    |  10 +-
+ src/providers/ldap/sdap_async_initgroups_ad.c | 537 +++++++++++++++++++++++++-
+ 3 files changed, 525 insertions(+), 26 deletions(-)
+
+diff --git a/src/providers/ldap/sdap_async.h b/src/providers/ldap/sdap_async.h
+index 67623454e675f648259c089acca59258f386ecdb..f47437553a2d35dac90d86209848e840a237c3fb 100644
+--- a/src/providers/ldap/sdap_async.h
++++ b/src/providers/ldap/sdap_async.h
+@@ -296,13 +296,15 @@ sdap_get_ad_match_rule_initgroups_recv(struct tevent_req *req);
+ struct tevent_req *
+ sdap_ad_tokengroups_initgroups_send(TALLOC_CTX *mem_ctx,
+                                     struct tevent_context *ev,
++                                    struct sdap_id_ctx *id_ctx,
+                                     struct sdap_options *opts,
+                                     struct sysdb_ctx *sysdb,
+                                     struct sss_domain_info *domain,
+                                     struct sdap_handle *sh,
+                                     const char *name,
+                                     const char *orig_dn,
+-                                    int timeout);
++                                    int timeout,
++                                    bool use_id_mapping);
+ 
+ errno_t
+ sdap_ad_tokengroups_initgroups_recv(struct tevent_req *req);
+diff --git a/src/providers/ldap/sdap_async_initgroups.c b/src/providers/ldap/sdap_async_initgroups.c
+index 7d5cd2e7cbd86e2eb9774dfee1b8e31edec57b88..1b865af0a113222b3c9c11e9401718abad577fd7 100644
+--- a/src/providers/ldap/sdap_async_initgroups.c
++++ b/src/providers/ldap/sdap_async_initgroups.c
+@@ -2852,18 +2852,19 @@ static void sdap_get_initgr_user(struct tevent_req *subreq)
+             return;
+         }
+ 
+-        if (state->use_id_mapping
+-                && state->opts->dc_functional_level >= DS_BEHAVIOR_WIN2008) {
++        if (state->opts->dc_functional_level >= DS_BEHAVIOR_WIN2008) {
+             /* Take advantage of AD's tokenGroups mechanism to look up all
+              * parent groups in a single request.
+              */
+             subreq = sdap_ad_tokengroups_initgroups_send(state, state->ev,
++                                                         state->id_ctx,
+                                                          state->opts,
+                                                          state->sysdb,
+                                                          state->dom,
+                                                          state->sh,
+                                                          cname, orig_dn,
+-                                                         state->timeout);
++                                                         state->timeout,
++                                                         state->use_id_mapping);
+         } else if (state->opts->support_matching_rule
+                     && dp_opt_get_bool(state->opts->basic,
+                                        SDAP_AD_MATCHING_RULE_INITGROUPS)) {
+@@ -2950,8 +2951,7 @@ static void sdap_get_initgr_done(struct tevent_req *subreq)
+ 
+     case SDAP_SCHEMA_RFC2307BIS:
+     case SDAP_SCHEMA_AD:
+-        if (state->use_id_mapping
+-                && state->opts->dc_functional_level >= DS_BEHAVIOR_WIN2008) {
++        if (state->opts->dc_functional_level >= DS_BEHAVIOR_WIN2008) {
+             ret = sdap_ad_tokengroups_initgroups_recv(subreq);
+         }
+         else if (state->opts->support_matching_rule
+diff --git a/src/providers/ldap/sdap_async_initgroups_ad.c b/src/providers/ldap/sdap_async_initgroups_ad.c
+index 7ba155338a358681c1bd201bee1c75f67afb4650..8e0506831cb189415b62efaa378d3dc7ec350cde 100644
+--- a/src/providers/ldap/sdap_async_initgroups_ad.c
++++ b/src/providers/ldap/sdap_async_initgroups_ad.c
+@@ -525,33 +525,180 @@ done:
+     return ret;
+ }
+ 
+-struct sdap_ad_tokengroups_initgroups_state {
++struct sdap_ad_resolve_sids_state {
++    struct tevent_context *ev;
++    struct sdap_id_ctx *id_ctx;
++    struct sdap_options *opts;
++    struct sss_domain_info *domain;
++    char **sids;
++
++    const char *current_sid;
++    int index;
++};
++
++static errno_t sdap_ad_resolve_sids_step(struct tevent_req *req);
++static void sdap_ad_resolve_sids_done(struct tevent_req *subreq);
++
++static struct tevent_req *
++sdap_ad_resolve_sids_send(TALLOC_CTX *mem_ctx,
++                          struct tevent_context *ev,
++                          struct sdap_id_ctx *id_ctx,
++                          struct sdap_options *opts,
++                          struct sss_domain_info *domain,
++                          char **sids)
++{
++    struct sdap_ad_resolve_sids_state *state = NULL;
++    struct tevent_req *req = NULL;
++    errno_t ret;
++
++    req = tevent_req_create(mem_ctx, &state,
++                            struct sdap_ad_resolve_sids_state);
++    if (req == NULL) {
++        DEBUG(SSSDBG_CRIT_FAILURE, ("tevent_req_create() failed\n"));
++        return NULL;
++    }
++
++    state->ev = ev;
++    state->id_ctx = id_ctx;
++    state->opts = opts;
++    state->domain = get_domains_head(domain);
++    state->sids = sids;
++    state->index = 0;
++
++    if (state->sids == NULL) {
++        ret = EOK;
++        goto immediately;
++    }
++
++    ret = sdap_ad_resolve_sids_step(req);
++    if (ret != EAGAIN) {
++        goto immediately;
++    }
++
++    return req;
++
++immediately:
++    if (ret == EOK) {
++        tevent_req_done(req);
++    } else {
++        tevent_req_error(req, ret);
++    }
++    tevent_req_post(req, ev);
++
++    return req;
++}
++
++static errno_t sdap_ad_resolve_sids_step(struct tevent_req *req)
++{
++    struct sdap_ad_resolve_sids_state *state = NULL;
++    struct tevent_req *subreq = NULL;
++    struct sdap_domain *sdap_domain = NULL;
++    struct sss_domain_info *domain = NULL;
++
++    state = tevent_req_data(req, struct sdap_ad_resolve_sids_state);
++
++    do {
++        state->current_sid = state->sids[state->index];
++        if (state->current_sid == NULL) {
++            return EOK;
++        }
++        state->index++;
++
++        domain = find_subdomain_by_sid(state->domain, state->current_sid);
++        if (domain == NULL) {
++            DEBUG(SSSDBG_MINOR_FAILURE, ("SID %s does not belong to any known "
++                                         "domain\n", state->current_sid));
++        }
++    } while (domain == NULL);
++
++    sdap_domain = sdap_domain_get(state->opts, domain);
++    if (sdap_domain == NULL) {
++        DEBUG(SSSDBG_CRIT_FAILURE, ("SDAP domain does not exist?\n"));
++        return ERR_INTERNAL;
++    }
++
++    subreq = groups_get_send(state, state->ev, state->id_ctx, sdap_domain,
++                             state->id_ctx->conn, state->current_sid,
++                             BE_FILTER_SECID, BE_ATTR_CORE, false);
++    if (subreq == NULL) {
++        return ENOMEM;
++    }
++
++    tevent_req_set_callback(subreq, sdap_ad_resolve_sids_done, req);
++
++    return EAGAIN;
++}
++
++static void sdap_ad_resolve_sids_done(struct tevent_req *subreq)
++{
++    struct sdap_ad_resolve_sids_state *state = NULL;
++    struct tevent_req *req = NULL;
++    int dp_error;
++    int sdap_error;
++    errno_t ret;
++
++    req = tevent_req_callback_data(subreq, struct tevent_req);
++    state = tevent_req_data(req, struct sdap_ad_resolve_sids_state);
++
++    ret = groups_get_recv(subreq, &dp_error, &sdap_error);
++    talloc_zfree(subreq);
++    if (ret != EOK || sdap_error != EOK || dp_error != DP_ERR_OK) {
++        DEBUG(SSSDBG_CRIT_FAILURE, ("Unable to resolve SID %s [dp_error: %d, "
++              "sdap_error: %d, ret: %d]: %s\n", state->current_sid, dp_error,
++              sdap_error, ret, strerror(ret)));
++        goto done;
++    }
++
++    ret = sdap_ad_resolve_sids_step(req);
++    if (ret == EAGAIN) {
++        /* continue with next SID */
++        return;
++    }
++
++done:
++    if (ret != EOK) {
++        tevent_req_error(req, ret);
++        return;
++    }
++
++    tevent_req_done(req);
++}
++
++static errno_t sdap_ad_resolve_sids_recv(struct tevent_req *req)
++{
++    TEVENT_REQ_RETURN_ON_ERROR(req);
++
++    return EOK;
++}
++
++
++struct sdap_ad_tokengroups_initgr_mapping_state {
+     struct sdap_idmap_ctx *idmap_ctx;
+     struct sysdb_ctx *sysdb;
+     struct sss_domain_info *domain;
+     const char *username;
+ };
+ 
+-static void sdap_ad_tokengroups_initgroups_done(struct tevent_req *subreq);
++static void sdap_ad_tokengroups_initgr_mapping_done(struct tevent_req *subreq);
+ 
+-struct tevent_req *
+-sdap_ad_tokengroups_initgroups_send(TALLOC_CTX *mem_ctx,
+-                                    struct tevent_context *ev,
+-                                    struct sdap_options *opts,
+-                                    struct sysdb_ctx *sysdb,
+-                                    struct sss_domain_info *domain,
+-                                    struct sdap_handle *sh,
+-                                    const char *name,
+-                                    const char *orig_dn,
+-                                    int timeout)
++static struct tevent_req *
++sdap_ad_tokengroups_initgr_mapping_send(TALLOC_CTX *mem_ctx,
++                                        struct tevent_context *ev,
++                                        struct sdap_options *opts,
++                                        struct sysdb_ctx *sysdb,
++                                        struct sss_domain_info *domain,
++                                        struct sdap_handle *sh,
++                                        const char *name,
++                                        const char *orig_dn,
++                                        int timeout)
+ {
+-    struct sdap_ad_tokengroups_initgroups_state *state = NULL;
++    struct sdap_ad_tokengroups_initgr_mapping_state *state = NULL;
+     struct tevent_req *req = NULL;
+     struct tevent_req *subreq = NULL;
+     errno_t ret;
+ 
+     req = tevent_req_create(mem_ctx, &state,
+-                            struct sdap_ad_tokengroups_initgroups_state);
++                            struct sdap_ad_tokengroups_initgr_mapping_state);
+     if (req == NULL) {
+         DEBUG(SSSDBG_CRIT_FAILURE, ("tevent_req_create() failed\n"));
+         return NULL;
+@@ -573,7 +720,8 @@ sdap_ad_tokengroups_initgroups_send(TALLOC_CTX *mem_ctx,
+         goto immediately;
+     }
+ 
+-    tevent_req_set_callback(subreq, sdap_ad_tokengroups_initgroups_done, req);
++    tevent_req_set_callback(subreq, sdap_ad_tokengroups_initgr_mapping_done,
++                            req);
+ 
+     return req;
+ 
+@@ -588,10 +736,10 @@ immediately:
+     return req;
+ }
+ 
+-static void sdap_ad_tokengroups_initgroups_done(struct tevent_req *subreq)
++static void sdap_ad_tokengroups_initgr_mapping_done(struct tevent_req *subreq)
+ {
+     TALLOC_CTX *tmp_ctx = NULL;
+-    struct sdap_ad_tokengroups_initgroups_state *state = NULL;
++    struct sdap_ad_tokengroups_initgr_mapping_state *state = NULL;
+     struct tevent_req *req = NULL;
+     struct sss_domain_info *domain = NULL;
+     struct ldb_message *msg = NULL;
+@@ -599,14 +747,14 @@ static void sdap_ad_tokengroups_initgroups_done(struct tevent_req *subreq)
+     const char *name = NULL;
+     const char *sid = NULL;
+     char **sids = NULL;
+-    size_t num_sids;
++    size_t num_sids = 0;
+     size_t i;
+     time_t now;
+     gid_t gid;
+     char **groups = NULL;
+     size_t num_groups;
+     errno_t ret, sret;
+-    bool in_transaction;
++    bool in_transaction = false;
+ 
+     tmp_ctx = talloc_new(NULL);
+     if (tmp_ctx == NULL) {
+@@ -616,7 +764,7 @@ static void sdap_ad_tokengroups_initgroups_done(struct tevent_req *subreq)
+     }
+ 
+     req = tevent_req_callback_data(subreq, struct tevent_req);
+-    state = tevent_req_data(req, struct sdap_ad_tokengroups_initgroups_state);
++    state = tevent_req_data(req, struct sdap_ad_tokengroups_initgr_mapping_state);
+ 
+     ret = sdap_get_ad_tokengroups_recv(state, subreq, &num_sids, &sids);
+     talloc_zfree(subreq);
+@@ -738,6 +886,355 @@ done:
+     tevent_req_done(req);
+ }
+ 
++static int sdap_ad_tokengroups_initgr_mapping_recv(struct tevent_req *req)
++{
++    TEVENT_REQ_RETURN_ON_ERROR(req);
++
++    return EOK;
++}
++
++struct sdap_ad_tokengroups_initgr_posix_state {
++    struct tevent_context *ev;
++    struct sdap_id_ctx *id_ctx;
++    struct sdap_options *opts;
++    struct sysdb_ctx *sysdb;
++    struct sss_domain_info *domain;
++    const char *username;
++};
++
++static void
++sdap_ad_tokengroups_initgr_posix_tg_done(struct tevent_req *subreq);
++
++static void
++sdap_ad_tokengroups_initgr_posix_sids_done(struct tevent_req *subreq);
++
++static struct tevent_req *
++sdap_ad_tokengroups_initgr_posix_send(TALLOC_CTX *mem_ctx,
++                                      struct tevent_context *ev,
++                                      struct sdap_id_ctx *id_ctx,
++                                      struct sdap_options *opts,
++                                      struct sysdb_ctx *sysdb,
++                                      struct sss_domain_info *domain,
++                                      struct sdap_handle *sh,
++                                      const char *name,
++                                      const char *orig_dn,
++                                      int timeout)
++{
++    struct sdap_ad_tokengroups_initgr_posix_state *state = NULL;
++    struct tevent_req *req = NULL;
++    struct tevent_req *subreq = NULL;
++    errno_t ret;
++
++    req = tevent_req_create(mem_ctx, &state,
++                            struct sdap_ad_tokengroups_initgr_posix_state);
++    if (req == NULL) {
++        DEBUG(SSSDBG_CRIT_FAILURE, ("tevent_req_create() failed\n"));
++        return NULL;
++    }
++
++    state->ev = ev;
++    state->id_ctx = id_ctx;
++    state->opts = opts;
++    state->sysdb = sysdb;
++    state->domain = domain;
++    state->username = talloc_strdup(state, name);
++    if (state->username == NULL) {
++        ret = ENOMEM;
++        goto immediately;
++    }
++
++    subreq = sdap_get_ad_tokengroups_send(state, ev, opts, sh, name, orig_dn,
++                                          timeout);
++    if (subreq == NULL) {
++        ret = ENOMEM;
++        goto immediately;
++    }
++
++    tevent_req_set_callback(subreq, sdap_ad_tokengroups_initgr_posix_tg_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
++sdap_ad_tokengroups_initgr_posix_tg_done(struct tevent_req *subreq)
++{
++    TALLOC_CTX *tmp_ctx = NULL;
++    struct sdap_ad_tokengroups_initgr_posix_state *state = NULL;
++    struct tevent_req *req = NULL;
++    struct sss_domain_info *domain = NULL;
++    struct ldb_message *msg = NULL;
++    const char *attrs[] = {SYSDB_NAME, SYSDB_POSIX, NULL};
++    const char *is_posix = NULL;
++    const char *name = NULL;
++    char *sid = NULL;
++    char **sids = NULL;
++    size_t num_sids = 0;
++    char **valid_groups = NULL;
++    size_t num_valid_groups;
++    char **missing_sids = NULL;
++    size_t num_missing_sids;
++    size_t i;
++    errno_t ret;
++
++    tmp_ctx = talloc_new(NULL);
++    if (tmp_ctx == NULL) {
++        DEBUG(SSSDBG_CRIT_FAILURE, ("talloc_new() failed\n"));
++        ret = ENOMEM;
++        goto done;
++    }
++
++    req = tevent_req_callback_data(subreq, struct tevent_req);
++    state = tevent_req_data(req,
++                            struct sdap_ad_tokengroups_initgr_posix_state);
++
++    ret = sdap_get_ad_tokengroups_recv(state, subreq, &num_sids, &sids);
++    talloc_zfree(subreq);
++    if (ret != EOK) {
++        DEBUG(SSSDBG_CRIT_FAILURE, ("Unable to acquire tokengroups [%d]: %s\n",
++                                    ret, strerror(ret)));
++        goto done;
++    }
++
++    num_valid_groups = 0;
++    valid_groups = talloc_zero_array(tmp_ctx, char*, num_sids + 1);
++    if (valid_groups == NULL) {
++        ret = ENOMEM;
++        goto done;
++    }
++
++    num_missing_sids = 0;
++    missing_sids = talloc_zero_array(tmp_ctx, char*, num_sids + 1);
++    if (missing_sids == NULL) {
++        ret = ENOMEM;
++        goto done;
++    }
++
++    /* For each SID check if it is already present in the cache. If yes, we
++     * will get name of the group and update the membership. Otherwise we need
++     * to remember the SID and download missing groups one by one. */
++    for (i = 0; i < num_sids; i++) {
++        sid = sids[i];
++        DEBUG(SSSDBG_TRACE_LIBS, ("Processing membership SID [%s]\n", sid));
++
++        domain = find_subdomain_by_sid(get_domains_head(state->domain), sid);
++        if (domain == NULL) {
++            DEBUG(SSSDBG_MINOR_FAILURE, ("Domain not found for SID %s\n", sid));
++            continue;
++        }
++
++        ret = sysdb_search_group_by_sid_str(tmp_ctx, domain->sysdb, domain,
++                                            sid, attrs, &msg);
++        if (ret == EOK) {
++            is_posix = ldb_msg_find_attr_as_string(msg, SYSDB_POSIX, NULL);
++            if (is_posix != NULL && strcmp(is_posix, "FALSE") == 0) {
++                /* skip non-posix group */
++                continue;
++            }
++
++            /* we will update membership of this group */
++            name = ldb_msg_find_attr_as_string(msg, SYSDB_NAME, NULL);
++            if (name == NULL) {
++                DEBUG(SSSDBG_MINOR_FAILURE,
++                      ("Could not retrieve group name from sysdb\n"));
++                ret = EINVAL;
++                goto done;
++            }
++
++            valid_groups[num_valid_groups] = sysdb_group_strdn(tmp_ctx,
++                                                               domain->name,
++                                                               name);
++            if (valid_groups[num_valid_groups] == NULL) {
++                ret = ENOMEM;
++                goto done;
++            }
++            num_valid_groups++;
++        } else if (ret == ENOENT) {
++            /* we need to download this group */
++            missing_sids[num_missing_sids] = talloc_steal(missing_sids, sid);
++            num_missing_sids++;
++
++            DEBUG(SSSDBG_TRACE_FUNC, ("Missing SID %s will be downloaded\n",
++                                      sid));
++        } else {
++            DEBUG(SSSDBG_MINOR_FAILURE, ("Could not look up group in sysdb: "
++                                         "[%s]\n", strerror(ret)));
++            goto done;
++        }
++    }
++
++    valid_groups[num_valid_groups] = NULL;
++    missing_sids[num_missing_sids] = NULL;
++
++    /* update membership of existing groups */
++    ret = sdap_ad_tokengroups_update_members(state, state->username,
++                                             state->sysdb, state->domain,
++                                             valid_groups);
++    if (ret != EOK) {
++        DEBUG(SSSDBG_MINOR_FAILURE, ("Membership update failed [%d]: %s\n",
++                                     ret, strerror(ret)));
++        goto done;
++    }
++
++    /* download missing SIDs */
++    missing_sids = talloc_steal(state, missing_sids);
++    subreq = sdap_ad_resolve_sids_send(state, state->ev, state->id_ctx,
++                                       state->opts, state->domain,
++                                       missing_sids);
++    if (subreq == NULL) {
++        ret = ENOMEM;
++        goto done;
++    }
++
++    tevent_req_set_callback(subreq, sdap_ad_tokengroups_initgr_posix_sids_done,
++                            req);
++
++    return;
++
++done:
++    talloc_free(tmp_ctx);
++    if (ret != EOK) {
++        tevent_req_error(req, ret);
++        return;
++    }
++
++    tevent_req_done(req);
++}
++
++static void
++sdap_ad_tokengroups_initgr_posix_sids_done(struct tevent_req *subreq)
++{
++    struct tevent_req *req = NULL;
++    errno_t ret;
++
++    req = tevent_req_callback_data(subreq, struct tevent_req);
++
++    ret = sdap_ad_resolve_sids_recv(subreq);
++    talloc_zfree(subreq);
++    if (ret != EOK) {
++        DEBUG(SSSDBG_CRIT_FAILURE, ("Unable to resolve missing SIDs "
++                                    "[%d]: %s\n", ret, strerror(ret)));
++        goto done;
++    }
++
++done:
++    if (ret != EOK) {
++        tevent_req_error(req, ret);
++        return;
++    }
++
++    tevent_req_done(req);
++}
++
++static errno_t sdap_ad_tokengroups_initgr_posix_recv(struct tevent_req *req)
++{
++    TEVENT_REQ_RETURN_ON_ERROR(req);
++
++    return EOK;
++}
++
++struct sdap_ad_tokengroups_initgroups_state {
++    bool use_id_mapping;
++};
++
++static void sdap_ad_tokengroups_initgroups_done(struct tevent_req *subreq);
++
++struct tevent_req *
++sdap_ad_tokengroups_initgroups_send(TALLOC_CTX *mem_ctx,
++                                    struct tevent_context *ev,
++                                    struct sdap_id_ctx *id_ctx,
++                                    struct sdap_options *opts,
++                                    struct sysdb_ctx *sysdb,
++                                    struct sss_domain_info *domain,
++                                    struct sdap_handle *sh,
++                                    const char *name,
++                                    const char *orig_dn,
++                                    int timeout,
++                                    bool use_id_mapping)
++{
++    struct sdap_ad_tokengroups_initgroups_state *state = NULL;
++    struct tevent_req *req = NULL;
++    struct tevent_req *subreq = NULL;
++    errno_t ret;
++
++    req = tevent_req_create(mem_ctx, &state,
++                            struct sdap_ad_tokengroups_initgroups_state);
++    if (req == NULL) {
++        DEBUG(SSSDBG_CRIT_FAILURE, ("tevent_req_create() failed\n"));
++        return NULL;
++    }
++
++    state->use_id_mapping = use_id_mapping;
++
++    if (state->use_id_mapping) {
++        subreq = sdap_ad_tokengroups_initgr_mapping_send(state, ev, opts,
++                                                         sysdb, domain, sh,
++                                                         name, orig_dn,
++                                                         timeout);
++    } else {
++        subreq = sdap_ad_tokengroups_initgr_posix_send(state, ev, id_ctx, opts,
++                                                       sysdb, domain, sh,
++                                                       name, orig_dn,
++                                                       timeout);
++    }
++    if (subreq == NULL) {
++        ret = ENOMEM;
++        goto immediately;
++    }
++
++    tevent_req_set_callback(subreq, sdap_ad_tokengroups_initgroups_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 sdap_ad_tokengroups_initgroups_done(struct tevent_req *subreq)
++{
++    struct sdap_ad_tokengroups_initgroups_state *state = NULL;
++    struct tevent_req *req = NULL;
++    errno_t ret;
++
++    req = tevent_req_callback_data(subreq, struct tevent_req);
++    state = tevent_req_data(req, struct sdap_ad_tokengroups_initgroups_state);
++
++    if (state->use_id_mapping) {
++        ret = sdap_ad_tokengroups_initgr_mapping_recv(subreq);
++    } else {
++        ret = sdap_ad_tokengroups_initgr_posix_recv(subreq);
++    }
++    talloc_zfree(subreq);
++    if (ret != EOK) {
++        goto done;
++    }
++
++done:
++    if (ret != EOK) {
++        tevent_req_error(req, ret);
++        return;
++    }
++
++    tevent_req_done(req);
++}
++
+ errno_t sdap_ad_tokengroups_initgroups_recv(struct tevent_req *req)
+ {
+     TEVENT_REQ_RETURN_ON_ERROR(req);
+-- 
+1.8.4.2
+
diff --git a/SOURCES/0026-AD-filter-domain-local-groups-for-trusted-sub-domain.patch b/SOURCES/0026-AD-filter-domain-local-groups-for-trusted-sub-domain.patch
new file mode 100644
index 0000000..a0be4bd
--- /dev/null
+++ b/SOURCES/0026-AD-filter-domain-local-groups-for-trusted-sub-domain.patch
@@ -0,0 +1,309 @@
+From 8d55e0fffd29184d44cb49eaab2ca3a4226e0123 Mon Sep 17 00:00:00 2001
+From: Sumit Bose <sbose@redhat.com>
+Date: Tue, 10 Dec 2013 10:14:28 +0100
+Subject: [PATCH 26/31] AD: filter domain local groups for trusted/sub domains
+
+In Active Directory groups with a domain local scope should only be used
+inside of the specific domain. Since SSSD read the group memberships
+from LDAP server of the user's domain the domain local groups are
+included in the LDAP result. Those groups should be filtered out if the
+domain is a sub/trusted domain, i.e. is not the domain the client
+running SSSD is joined to.
+
+The groups will still be in the cache but marked as non-POSIX groups and
+no GID will be assigned.
+
+Fixes https://fedorahosted.org/sssd/ticket/2178
+---
+ src/providers/ldap/sdap.h                     |   8 ++
+ src/providers/ldap/sdap_async_groups.c        | 160 ++++++++++++++++----------
+ src/providers/ldap/sdap_async_initgroups_ad.c |   6 +-
+ src/providers/ldap/sdap_async_nested_groups.c |  28 ++++-
+ 4 files changed, 138 insertions(+), 64 deletions(-)
+
+diff --git a/src/providers/ldap/sdap.h b/src/providers/ldap/sdap.h
+index fa641730bb78b6a96c0b9640af7612b876f56533..a7ea94eb810a96b61862bd8cc6fcd800c3e8e0cb 100644
+--- a/src/providers/ldap/sdap.h
++++ b/src/providers/ldap/sdap.h
+@@ -137,6 +137,14 @@ struct sdap_ppolicy_data {
+ #define SDAP_AD_USN "uSNChanged"
+ #define SDAP_AD_LAST_USN "highestCommittedUSN"
+ 
++#define SDAP_AD_GROUP_TYPE_BUILTIN      0x00000001
++#define SDAP_AD_GROUP_TYPE_GLOBAL       0x00000002
++#define SDAP_AD_GROUP_TYPE_DOMAIN_LOCAL 0x00000004
++#define SDAP_AD_GROUP_TYPE_UNIVERSAL    0x00000008
++#define SDAP_AD_GROUP_TYPE_APP_BASIC    0x00000010
++#define SDAP_AD_GROUP_TYPE_APP_QUERY    0x00000020
++#define SDAP_AD_GROUP_TYPE_SECURITY     0x80000000
++
+ enum sdap_basic_opt {
+     SDAP_URI = 0,
+     SDAP_BACKUP_URI,
+diff --git a/src/providers/ldap/sdap_async_groups.c b/src/providers/ldap/sdap_async_groups.c
+index 9f7e3e55d0234e9aa7b9e59456044587bcad88ef..33648c5da367c908d085a71a9a9017cb294bb300 100644
+--- a/src/providers/ldap/sdap_async_groups.c
++++ b/src/providers/ldap/sdap_async_groups.c
+@@ -451,6 +451,7 @@ static int sdap_save_group(TALLOC_CTX *memctx,
+     bool posix_group;
+     bool use_id_mapping;
+     char *sid_str;
++    int32_t ad_group_type;
+ 
+     tmpctx = talloc_new(NULL);
+     if (!tmpctx) {
+@@ -503,74 +504,113 @@ static int sdap_save_group(TALLOC_CTX *memctx,
+     }
+     DEBUG(SSSDBG_TRACE_FUNC, ("Processing group %s\n", group_name));
+ 
+-    use_id_mapping = sdap_idmap_domain_has_algorithmic_mapping(opts->idmap_ctx,
+-                                                               dom->name,
+-                                                               sid_str);
+-    if (use_id_mapping) {
+-        posix_group = true;
+-
+-        if (sid_str == NULL) {
+-            DEBUG(SSSDBG_MINOR_FAILURE, ("SID not available, cannot map a " \
+-                                         "unix ID to group [%s].\n", group_name));
+-            ret = ENOENT;
++    posix_group = true;
++    if (opts->schema_type == SDAP_SCHEMA_AD) {
++        ret = sysdb_attrs_get_int32_t(attrs, SYSDB_GROUP_TYPE, &ad_group_type);
++        if (ret != EOK) {
++            DEBUG(SSSDBG_OP_FAILURE, ("sysdb_attrs_get_int32_t failed.\n"));
+             goto done;
+         }
+ 
+-        DEBUG(SSSDBG_TRACE_LIBS,
+-              ("Mapping group [%s] objectSID [%s] to unix ID\n",
+-               group_name, sid_str));
+-
+-        /* Convert the SID into a UNIX group ID */
+-        ret = sdap_idmap_sid_to_unix(opts->idmap_ctx, sid_str, &gid);
+-        if (ret == ENOTSUP) {
+-            /* ENOTSUP is returned if built-in SID was provided
+-             * => do not store the group, but return EOK */
+-            DEBUG(SSSDBG_TRACE_FUNC, ("Skipping built-in object.\n"));
+-            ret = EOK;
+-            goto done;
+-        } else if (ret != EOK) {
+-            DEBUG(SSSDBG_MINOR_FAILURE,
+-                  ("Could not convert SID string: [%s]\n",
+-                   strerror(ret)));
+-            goto done;
++        DEBUG(SSSDBG_TRACE_ALL, ("AD group [%s] has type flags %#x.",
++                                 group_name, ad_group_type));
++        /* Only security groups from AD are considered for POSIX groups.
++         * Additionally only global and universal group are taken to account
++         * for trusted domains. */
++        if (!(ad_group_type & SDAP_AD_GROUP_TYPE_SECURITY)
++                || (IS_SUBDOMAIN(dom)
++                    && (!((ad_group_type & SDAP_AD_GROUP_TYPE_GLOBAL)
++                        || (ad_group_type & SDAP_AD_GROUP_TYPE_UNIVERSAL))))) {
++            posix_group = false;
++            gid = 0;
++            DEBUG(SSSDBG_TRACE_FUNC, ("Filtering AD group [%s].\n",
++                                      group_name));
++            ret = sysdb_attrs_add_uint32(group_attrs,
++                                         opts->group_map[SDAP_AT_GROUP_GID].sys_name, 0);
++            if (ret != EOK) {
++                DEBUG(SSSDBG_CRIT_FAILURE,
++                      ("Failed to add a GID to non-posix group!\n"));
++                return ret;
++            }
++            ret = sysdb_attrs_add_bool(group_attrs, SYSDB_POSIX, false);
++            if (ret != EOK) {
++                DEBUG(SSSDBG_OP_FAILURE,
++                      ("Error: Failed to mark group as non-posix!\n"));
++                return ret;
++            }
+         }
++    }
+ 
+-        /* Store the GID in the ldap_attrs so it doesn't get
+-         * treated as a missing attribute from LDAP and removed.
+-         */
+-        ret = sdap_replace_id(attrs, SYSDB_GIDNUM, gid);
+-        if (ret) {
+-            DEBUG(SSSDBG_OP_FAILURE, ("Cannot set the id-mapped GID\n"));
+-            goto done;
+-        }
+-    } else {
+-        ret = sysdb_attrs_get_bool(attrs, SYSDB_POSIX, &posix_group);
+-        if (ret == ENOENT) {
++    if (posix_group) {
++        use_id_mapping = sdap_idmap_domain_has_algorithmic_mapping(opts->idmap_ctx,
++                                                                   dom->name,
++                                                                   sid_str);
++        if (use_id_mapping) {
+             posix_group = true;
+-        } else if (ret != EOK) {
+-            DEBUG(SSSDBG_MINOR_FAILURE,
+-                  ("Error reading posix attribute: [%s]\n",
+-                   strerror(ret)));
+-            goto done;
+-        }
+ 
+-        DEBUG(8, ("This is%s a posix group\n", (posix_group)?"":" not"));
+-        ret = sysdb_attrs_add_bool(group_attrs, SYSDB_POSIX, posix_group);
+-        if (ret != EOK) {
+-            DEBUG(SSSDBG_MINOR_FAILURE,
+-                  ("Error setting posix attribute: [%s]\n",
+-                   strerror(ret)));
+-            goto done;
+-        }
++            if (sid_str == NULL) {
++                DEBUG(SSSDBG_MINOR_FAILURE, ("SID not available, cannot map a " \
++                                             "unix ID to group [%s].\n", group_name));
++                ret = ENOENT;
++                goto done;
++            }
+ 
+-        ret = sysdb_attrs_get_uint32_t(attrs,
+-                                       opts->group_map[SDAP_AT_GROUP_GID].sys_name,
+-                                       &gid);
+-        if (ret != EOK) {
+-            DEBUG(1, ("no gid provided for [%s] in domain [%s].\n",
+-                      group_name, dom->name));
+-            ret = EINVAL;
+-            goto done;
++            DEBUG(SSSDBG_TRACE_LIBS,
++                  ("Mapping group [%s] objectSID [%s] to unix ID\n",
++                   group_name, sid_str));
++
++            /* Convert the SID into a UNIX group ID */
++            ret = sdap_idmap_sid_to_unix(opts->idmap_ctx, sid_str, &gid);
++            if (ret == ENOTSUP) {
++                /* ENOTSUP is returned if built-in SID was provided
++                 * => do not store the group, but return EOK */
++                DEBUG(SSSDBG_TRACE_FUNC, ("Skipping built-in object.\n"));
++                ret = EOK;
++                goto done;
++            } else if (ret != EOK) {
++                DEBUG(SSSDBG_MINOR_FAILURE,
++                      ("Could not convert SID string: [%s]\n",
++                       strerror(ret)));
++                goto done;
++            }
++
++            /* Store the GID in the ldap_attrs so it doesn't get
++             * treated as a missing attribute from LDAP and removed.
++             */
++            ret = sdap_replace_id(attrs, SYSDB_GIDNUM, gid);
++            if (ret) {
++                DEBUG(SSSDBG_OP_FAILURE, ("Cannot set the id-mapped GID\n"));
++                goto done;
++            }
++        } else {
++            ret = sysdb_attrs_get_bool(attrs, SYSDB_POSIX, &posix_group);
++            if (ret == ENOENT) {
++                posix_group = true;
++            } else if (ret != EOK) {
++                DEBUG(SSSDBG_MINOR_FAILURE,
++                      ("Error reading posix attribute: [%s]\n",
++                       strerror(ret)));
++                goto done;
++            }
++
++            DEBUG(8, ("This is%s a posix group\n", (posix_group)?"":" not"));
++            ret = sysdb_attrs_add_bool(group_attrs, SYSDB_POSIX, posix_group);
++            if (ret != EOK) {
++                DEBUG(SSSDBG_MINOR_FAILURE,
++                      ("Error setting posix attribute: [%s]\n",
++                       strerror(ret)));
++                goto done;
++            }
++
++            ret = sysdb_attrs_get_uint32_t(attrs,
++                                           opts->group_map[SDAP_AT_GROUP_GID].sys_name,
++                                           &gid);
++            if (ret != EOK) {
++                DEBUG(1, ("no gid provided for [%s] in domain [%s].\n",
++                          group_name, dom->name));
++                ret = EINVAL;
++                goto done;
++            }
+         }
+     }
+ 
+diff --git a/src/providers/ldap/sdap_async_initgroups_ad.c b/src/providers/ldap/sdap_async_initgroups_ad.c
+index 8e0506831cb189415b62efaa378d3dc7ec350cde..f1bf77e8614c30b214118140e380c23c40c1195b 100644
+--- a/src/providers/ldap/sdap_async_initgroups_ad.c
++++ b/src/providers/ldap/sdap_async_initgroups_ad.c
+@@ -1145,6 +1145,7 @@ static errno_t sdap_ad_tokengroups_initgr_posix_recv(struct tevent_req *req)
+ 
+ struct sdap_ad_tokengroups_initgroups_state {
+     bool use_id_mapping;
++    struct sss_domain_info *domain;
+ };
+ 
+ static void sdap_ad_tokengroups_initgroups_done(struct tevent_req *subreq);
+@@ -1175,8 +1176,9 @@ sdap_ad_tokengroups_initgroups_send(TALLOC_CTX *mem_ctx,
+     }
+ 
+     state->use_id_mapping = use_id_mapping;
++    state->domain = domain;
+ 
+-    if (state->use_id_mapping) {
++    if (state->use_id_mapping && !IS_SUBDOMAIN(state->domain)) {
+         subreq = sdap_ad_tokengroups_initgr_mapping_send(state, ev, opts,
+                                                          sysdb, domain, sh,
+                                                          name, orig_dn,
+@@ -1216,7 +1218,7 @@ static void sdap_ad_tokengroups_initgroups_done(struct tevent_req *subreq)
+     req = tevent_req_callback_data(subreq, struct tevent_req);
+     state = tevent_req_data(req, struct sdap_ad_tokengroups_initgroups_state);
+ 
+-    if (state->use_id_mapping) {
++    if (state->use_id_mapping && !IS_SUBDOMAIN(state->domain)) {
+         ret = sdap_ad_tokengroups_initgr_mapping_recv(subreq);
+     } else {
+         ret = sdap_ad_tokengroups_initgr_posix_recv(subreq);
+diff --git a/src/providers/ldap/sdap_async_nested_groups.c b/src/providers/ldap/sdap_async_nested_groups.c
+index c107b700b84b8c178051fd4505f3947be8373de2..f58564aec0628c827672950f767401ee32051b59 100644
+--- a/src/providers/ldap/sdap_async_nested_groups.c
++++ b/src/providers/ldap/sdap_async_nested_groups.c
+@@ -239,15 +239,39 @@ sdap_nested_group_hash_group(struct sdap_nested_group_ctx *group_ctx,
+     struct sdap_attr_map *map = group_ctx->opts->group_map;
+     gid_t gid;
+     errno_t ret;
++    int32_t ad_group_type;
++    bool posix_group = true;
++
++    if (group_ctx->opts->schema_type == SDAP_SCHEMA_AD) {
++        ret = sysdb_attrs_get_int32_t(group, SYSDB_GROUP_TYPE, &ad_group_type);
++        if (ret != EOK) {
++            DEBUG(SSSDBG_OP_FAILURE, ("sysdb_attrs_get_int32_t failed.\n"));
++            return ret;
++        }
++
++        DEBUG(SSSDBG_TRACE_ALL, ("AD group has type flags %#x.\n",
++                                 ad_group_type));
++        /* Only security groups from AD are considered for POSIX groups.
++         * Additionally only global and universal group are taken to account
++         * for trusted domains. */
++        if (!(ad_group_type & SDAP_AD_GROUP_TYPE_SECURITY)
++                || (IS_SUBDOMAIN(group_ctx->domain)
++                    && (!((ad_group_type & SDAP_AD_GROUP_TYPE_GLOBAL)
++                        || (ad_group_type & SDAP_AD_GROUP_TYPE_UNIVERSAL))))) {
++            posix_group = false;
++            gid = 0;
++            DEBUG(SSSDBG_TRACE_FUNC, ("Filtering AD group.\n"));
++        }
++    }
+ 
+     ret = sysdb_attrs_get_uint32_t(group, map[SDAP_AT_GROUP_GID].sys_name,
+                                    &gid);
+-    if (ret == ENOENT || (ret == EOK && gid == 0)) {
++    if (ret == ENOENT || (ret == EOK && gid == 0) || !posix_group) {
+         DEBUG(SSSDBG_TRACE_ALL,
+              ("The group's gid was %s\n", ret == ENOENT ? "missing" : "zero"));
+         DEBUG(SSSDBG_TRACE_INTERNAL,
+              ("Marking group as non-posix and setting GID=0!\n"));
+-        if (ret == ENOENT) {
++        if (ret == ENOENT || !posix_group) {
+             ret = sysdb_attrs_add_uint32(group,
+                                          map[SDAP_AT_GROUP_GID].sys_name, 0);
+             if (ret != EOK) {
+-- 
+1.8.4.2
+
diff --git a/SOURCES/0027-AD-cross-domain-membership-fix.patch b/SOURCES/0027-AD-cross-domain-membership-fix.patch
new file mode 100644
index 0000000..5f273b4
--- /dev/null
+++ b/SOURCES/0027-AD-cross-domain-membership-fix.patch
@@ -0,0 +1,639 @@
+From 402af69c0bb7ea8b84e36f3567de6086042cb152 Mon Sep 17 00:00:00 2001
+From: Sumit Bose <sbose@redhat.com>
+Date: Wed, 18 Dec 2013 13:47:31 +0100
+Subject: [PATCH 27/31] AD: cross-domain membership fix
+
+A recent patch directed all call related to group membership lookups to
+the AD LDAP port to fix an issue related to missing group memberships in
+the Global Catalog. As a side-effect it broke cross-domain
+group-memberships because those cannot be resolved by the connection to
+the LDAP port.
+
+The patch tires to fix this by restoring the original behaviour in the
+top-level lookup calls in the AD provider and switching to the LDAP port
+only for the LDAP request which is expected to return the full group
+membership.
+
+Additionally this patch contains a related fix for the tokenGroups with
+Posix attributes patch. The original connection, typically a Global
+Catalog connection in the AD case is passed down the stack so that the
+group lookup after the tokenGroups request can run over the same
+connection.
+---
+ src/providers/ad/ad_id.c                      |  19 +--
+ src/providers/ad/ad_init.c                    |   2 +
+ src/providers/ldap/sdap_async.h               |   1 +
+ src/providers/ldap/sdap_async_groups.c        |  62 +++++++-
+ src/providers/ldap/sdap_async_initgroups.c    |  50 ++++++-
+ src/providers/ldap/sdap_async_initgroups_ad.c | 197 ++++++++++++++++++++++----
+ 6 files changed, 281 insertions(+), 50 deletions(-)
+
+diff --git a/src/providers/ad/ad_id.c b/src/providers/ad/ad_id.c
+index 19bc65825be21c6419db1e92db642be0a14b97a8..cf71b172dd7c241a9280a7ea72ef2518f66a7435 100644
+--- a/src/providers/ad/ad_id.c
++++ b/src/providers/ad/ad_id.c
+@@ -199,6 +199,8 @@ get_conn_list(struct be_req *breq, struct ad_id_ctx *ad_ctx,
+     case BE_REQ_USER: /* user */
+     case BE_REQ_BY_SECID:   /* by SID */
+     case BE_REQ_USER_AND_GROUP: /* get SID */
++    case BE_REQ_GROUP: /* group */
++    case BE_REQ_INITGROUPS: /* init groups for user */
+         /* Always try GC first */
+         clist[0] = ad_ctx->gc_ctx;
+         if (IS_SUBDOMAIN(dom) == true) {
+@@ -215,23 +217,6 @@ get_conn_list(struct be_req *breq, struct ad_id_ctx *ad_ctx,
+          */
+         clist[1] = ad_ctx->ldap_ctx;
+         break;
+-
+-    case BE_REQ_GROUP: /* group */
+-    case BE_REQ_INITGROUPS: /* init groups for user */
+-        if (IS_SUBDOMAIN(dom)) {
+-            sdom = sdap_domain_get(ad_ctx->sdap_id_ctx->opts, dom);
+-            if (sdom == NULL || sdom->pvt == NULL) {
+-                DEBUG(SSSDBG_CRIT_FAILURE, ("No ID ctx available for [%s].\n",
+-                                            dom->name));
+-                return NULL;
+-            }
+-            subdom_id_ctx = talloc_get_type(sdom->pvt, struct ad_id_ctx);
+-            clist[0] = subdom_id_ctx->ldap_ctx;
+-        } else {
+-            clist[0] = ad_ctx->ldap_ctx;
+-        }
+-        break;
+-
+     default:
+         clist[0] = ad_ctx->ldap_ctx;
+         break;
+diff --git a/src/providers/ad/ad_init.c b/src/providers/ad/ad_init.c
+index d06efbd082bd6bba74fb6616c7dd722c99244988..332bfda3801db3824ce1896d37e65e2c3a6b8b8b 100644
+--- a/src/providers/ad/ad_init.c
++++ b/src/providers/ad/ad_init.c
+@@ -214,6 +214,8 @@ sssm_ad_id_init(struct be_ctx *bectx,
+         goto done;
+     }
+ 
++    ad_ctx->sdap_id_ctx->opts->sdom->pvt = ad_ctx;
++
+     /* Set up the ID mapping object */
+     ret = sdap_idmap_init(ad_ctx->sdap_id_ctx, ad_ctx->sdap_id_ctx,
+                           &ad_ctx->sdap_id_ctx->opts->idmap_ctx);
+diff --git a/src/providers/ldap/sdap_async.h b/src/providers/ldap/sdap_async.h
+index f47437553a2d35dac90d86209848e840a237c3fb..33e8708ab7e80ab4280df300fdc300d4ecd18305 100644
+--- a/src/providers/ldap/sdap_async.h
++++ b/src/providers/ldap/sdap_async.h
+@@ -297,6 +297,7 @@ struct tevent_req *
+ sdap_ad_tokengroups_initgroups_send(TALLOC_CTX *mem_ctx,
+                                     struct tevent_context *ev,
+                                     struct sdap_id_ctx *id_ctx,
++                                    struct sdap_id_conn_ctx *conn,
+                                     struct sdap_options *opts,
+                                     struct sysdb_ctx *sysdb,
+                                     struct sss_domain_info *domain,
+diff --git a/src/providers/ldap/sdap_async_groups.c b/src/providers/ldap/sdap_async_groups.c
+index 33648c5da367c908d085a71a9a9017cb294bb300..9eece9a6e4baaf302a28b57a63dae45a0741136c 100644
+--- a/src/providers/ldap/sdap_async_groups.c
++++ b/src/providers/ldap/sdap_async_groups.c
+@@ -26,6 +26,7 @@
+ #include "providers/ldap/sdap_async_private.h"
+ #include "providers/ldap/ldap_common.h"
+ #include "providers/ldap/sdap_idmap.h"
++#include "providers/ad/ad_common.h"
+ 
+ /* ==Group-Parsing Routines=============================================== */
+ 
+@@ -1540,9 +1541,13 @@ struct sdap_get_groups_state {
+ 
+     size_t base_iter;
+     struct sdap_search_base **search_bases;
++
++    struct sdap_handle *ldap_sh;
++    struct sdap_id_op *op;
+ };
+ 
+ static errno_t sdap_get_groups_next_base(struct tevent_req *req);
++static void sdap_get_groups_ldap_connect_done(struct tevent_req *subreq);
+ static void sdap_get_groups_process(struct tevent_req *subreq);
+ static void sdap_get_groups_done(struct tevent_req *subreq);
+ 
+@@ -1558,7 +1563,9 @@ struct tevent_req *sdap_get_groups_send(TALLOC_CTX *memctx,
+ {
+     errno_t ret;
+     struct tevent_req *req;
++    struct tevent_req *subreq;
+     struct sdap_get_groups_state *state;
++    struct ad_id_ctx *subdom_id_ctx;
+ 
+     req = tevent_req_create(memctx, &state, struct sdap_get_groups_state);
+     if (!req) return NULL;
+@@ -1586,6 +1593,30 @@ struct tevent_req *sdap_get_groups_send(TALLOC_CTX *memctx,
+         goto done;
+     }
+ 
++    /* With AD by default the Global Catalog is used for lookup. But the GC
++     * group object might not have full group membership data. To make sure we
++     * connect to an LDAP server of the group's domain. */
++    if (state->opts->schema_type == SDAP_SCHEMA_AD && sdom->pvt != NULL) {
++        subdom_id_ctx = talloc_get_type(sdom->pvt, struct ad_id_ctx);
++        state->op = sdap_id_op_create(state, subdom_id_ctx->ldap_ctx->conn_cache);
++        if (!state->op) {
++            DEBUG(2, ("sdap_id_op_create failed\n"));
++            ret = ENOMEM;
++            goto done;
++        }
++
++        subreq = sdap_id_op_connect_send(state->op, state, &ret);
++        if (subreq == NULL) {
++            ret = ENOMEM;
++            goto done;
++        }
++
++        tevent_req_set_callback(subreq,
++                                sdap_get_groups_ldap_connect_done,
++                                req);
++        return req;
++    }
++
+     ret = sdap_get_groups_next_base(req);
+ 
+ done:
+@@ -1597,6 +1628,34 @@ done:
+     return req;
+ }
+ 
++static void sdap_get_groups_ldap_connect_done(struct tevent_req *subreq)
++{
++    struct tevent_req *req;
++    struct sdap_get_groups_state *state;
++    int ret;
++    int dp_error;
++
++    req = tevent_req_callback_data(subreq, struct tevent_req);
++    state = tevent_req_data(req, struct sdap_get_groups_state);
++
++    ret = sdap_id_op_connect_recv(subreq, &dp_error);
++    talloc_zfree(subreq);
++
++    if (ret != EOK) {
++        tevent_req_error(req, ret);
++        return;
++    }
++
++    state->ldap_sh = sdap_id_op_handle(state->op);
++
++    ret = sdap_get_groups_next_base(req);
++    if (ret != EOK) {
++        tevent_req_error(req, ret);
++    }
++
++    return;
++}
++
+ static errno_t sdap_get_groups_next_base(struct tevent_req *req)
+ {
+     struct tevent_req *subreq;
+@@ -1617,7 +1676,8 @@ static errno_t sdap_get_groups_next_base(struct tevent_req *req)
+            state->search_bases[state->base_iter]->basedn));
+ 
+     subreq = sdap_get_generic_send(
+-            state, state->ev, state->opts, state->sh,
++            state, state->ev, state->opts,
++            state->ldap_sh != NULL ? state->ldap_sh : state->sh,
+             state->search_bases[state->base_iter]->basedn,
+             state->search_bases[state->base_iter]->scope,
+             state->filter, state->attrs,
+diff --git a/src/providers/ldap/sdap_async_initgroups.c b/src/providers/ldap/sdap_async_initgroups.c
+index 1b865af0a113222b3c9c11e9401718abad577fd7..aba7ba42dade8923ae91f5bc8962e03d038c15a1 100644
+--- a/src/providers/ldap/sdap_async_initgroups.c
++++ b/src/providers/ldap/sdap_async_initgroups.c
+@@ -2749,6 +2749,10 @@ static void sdap_get_initgr_user(struct tevent_req *subreq)
+     const char *orig_dn;
+     const char *cname;
+     bool in_transaction = false;
++    char *expected_basedn;
++    size_t expected_basedn_len;
++    size_t dn_len;
++    size_t c = 0;
+ 
+     DEBUG(9, ("Receiving info for the user\n"));
+ 
+@@ -2788,11 +2792,50 @@ static void sdap_get_initgr_user(struct tevent_req *subreq)
+     } else if (count != 1) {
+         DEBUG(SSSDBG_OP_FAILURE,
+               ("Expected one user entry and got %zu\n", count));
+-        tevent_req_error(req, EINVAL);
+-        return;
++
++        ret = domain_to_basedn(state, state->dom->name, &expected_basedn);
++        if (ret != EOK) {
++            DEBUG(SSSDBG_OP_FAILURE, ("domain_to_basedn failed.\n"));
++            tevent_req_error(req, ret);
++            return;
++        }
++        expected_basedn = talloc_asprintf(state, "%s%s",
++                                                 "cn=users,", expected_basedn);
++        if (expected_basedn == NULL) {
++            DEBUG(SSSDBG_OP_FAILURE, ("talloc_append failed.\n"));
++            tevent_req_error(req, ENOMEM);
++            return;
++        }
++
++        DEBUG(SSSDBG_TRACE_ALL, ("Expected BaseDN is [%s].\n", expected_basedn));
++        expected_basedn_len = strlen(expected_basedn);
++
++        for (c = 0; c < count; c++) {
++            ret = sysdb_attrs_get_string(usr_attrs[c], SYSDB_ORIG_DN, &orig_dn);
++            if (ret != EOK) {
++                DEBUG(SSSDBG_OP_FAILURE, ("sysdb_attrs_get_string failed.\n"));
++                tevent_req_error(req, ret);
++                return;
++            }
++            dn_len = strlen(orig_dn);
++
++            if (dn_len > expected_basedn_len
++                    && strcasecmp(orig_dn + (dn_len - expected_basedn_len),
++                                  expected_basedn) == 0) {
++                DEBUG(SSSDBG_TRACE_ALL,
++                      ("Found matching dn [%s].\n", orig_dn));
++                break;
++            }
++        }
++
++        if (c == count) {
++            DEBUG(SSSDBG_OP_FAILURE, ("No matching DN found.\n"));
++            tevent_req_error(req, EINVAL);
++            return;
++        }
+     }
+ 
+-    state->orig_user = usr_attrs[0];
++    state->orig_user = usr_attrs[c];
+ 
+     ret = sysdb_transaction_start(state->sysdb);
+     if (ret) {
+@@ -2858,6 +2901,7 @@ static void sdap_get_initgr_user(struct tevent_req *subreq)
+              */
+             subreq = sdap_ad_tokengroups_initgroups_send(state, state->ev,
+                                                          state->id_ctx,
++                                                         state->conn,
+                                                          state->opts,
+                                                          state->sysdb,
+                                                          state->dom,
+diff --git a/src/providers/ldap/sdap_async_initgroups_ad.c b/src/providers/ldap/sdap_async_initgroups_ad.c
+index f1bf77e8614c30b214118140e380c23c40c1195b..8f8f0a4cc635818dcc7f75f9da603ce2f55c820f 100644
+--- a/src/providers/ldap/sdap_async_initgroups_ad.c
++++ b/src/providers/ldap/sdap_async_initgroups_ad.c
+@@ -25,6 +25,7 @@
+ #include "providers/ldap/ldap_common.h"
+ #include "providers/ldap/sdap_async_private.h"
+ #include "providers/ldap/sdap_idmap.h"
++#include "providers/ad/ad_common.h"
+ #include "lib/idmap/sss_idmap.h"
+ 
+ struct sdap_ad_match_rule_initgr_state {
+@@ -528,6 +529,7 @@ done:
+ struct sdap_ad_resolve_sids_state {
+     struct tevent_context *ev;
+     struct sdap_id_ctx *id_ctx;
++    struct sdap_id_conn_ctx *conn;
+     struct sdap_options *opts;
+     struct sss_domain_info *domain;
+     char **sids;
+@@ -543,6 +545,7 @@ static struct tevent_req *
+ sdap_ad_resolve_sids_send(TALLOC_CTX *mem_ctx,
+                           struct tevent_context *ev,
+                           struct sdap_id_ctx *id_ctx,
++                          struct sdap_id_conn_ctx *conn,
+                           struct sdap_options *opts,
+                           struct sss_domain_info *domain,
+                           char **sids)
+@@ -560,6 +563,7 @@ sdap_ad_resolve_sids_send(TALLOC_CTX *mem_ctx,
+ 
+     state->ev = ev;
+     state->id_ctx = id_ctx;
++    state->conn = conn;
+     state->opts = opts;
+     state->domain = get_domains_head(domain);
+     state->sids = sids;
+@@ -618,7 +622,7 @@ static errno_t sdap_ad_resolve_sids_step(struct tevent_req *req)
+     }
+ 
+     subreq = groups_get_send(state, state->ev, state->id_ctx, sdap_domain,
+-                             state->id_ctx->conn, state->current_sid,
++                             state->conn, state->current_sid,
+                              BE_FILTER_SECID, BE_ATTR_CORE, false);
+     if (subreq == NULL) {
+         return ENOMEM;
+@@ -673,12 +677,21 @@ static errno_t sdap_ad_resolve_sids_recv(struct tevent_req *req)
+ 
+ 
+ struct sdap_ad_tokengroups_initgr_mapping_state {
++    struct tevent_context *ev;
++    struct sdap_options *opts;
++    struct sdap_handle *sh;
+     struct sdap_idmap_ctx *idmap_ctx;
+     struct sysdb_ctx *sysdb;
+     struct sss_domain_info *domain;
++    const char *orig_dn;
++    int timeout;
+     const char *username;
++
++    struct sdap_id_op *op;
+ };
+ 
++static void
++sdap_ad_tokengroups_initgr_mapping_connect_done(struct tevent_req *subreq);
+ static void sdap_ad_tokengroups_initgr_mapping_done(struct tevent_req *subreq);
+ 
+ static struct tevent_req *
+@@ -695,6 +708,8 @@ sdap_ad_tokengroups_initgr_mapping_send(TALLOC_CTX *mem_ctx,
+     struct sdap_ad_tokengroups_initgr_mapping_state *state = NULL;
+     struct tevent_req *req = NULL;
+     struct tevent_req *subreq = NULL;
++    struct sdap_domain *sdom;
++    struct ad_id_ctx *subdom_id_ctx;
+     errno_t ret;
+ 
+     req = tevent_req_create(mem_ctx, &state,
+@@ -704,36 +719,92 @@ sdap_ad_tokengroups_initgr_mapping_send(TALLOC_CTX *mem_ctx,
+         return NULL;
+     }
+ 
++    state->ev = ev;
++    state->opts = opts;
++    state->sh = sh;
+     state->idmap_ctx = opts->idmap_ctx;
+     state->sysdb = sysdb;
+     state->domain = domain;
++    state->timeout = timeout;
++    state->orig_dn = orig_dn;
+     state->username = talloc_strdup(state, name);
+     if (state->username == NULL) {
+         ret = ENOMEM;
+         goto immediately;
+     }
+ 
+-    subreq = sdap_get_ad_tokengroups_send(state, ev, opts, sh, name, orig_dn,
+-                                          timeout);
++    sdom = sdap_domain_get(opts, domain);
++    if (sdom == NULL || sdom->pvt == NULL) {
++        DEBUG(SSSDBG_CRIT_FAILURE, ("No ID ctx available for [%s].\n",
++                                    domain->name));
++        ret = EINVAL;
++        goto immediately;
++    }
++    subdom_id_ctx = talloc_get_type(sdom->pvt, struct ad_id_ctx);
++    state->op = sdap_id_op_create(state, subdom_id_ctx->ldap_ctx->conn_cache);
++    if (!state->op) {
++        DEBUG(2, ("sdap_id_op_create failed\n"));
++        ret = ENOMEM;
++        goto immediately;
++    }
++
++    subreq = sdap_id_op_connect_send(state->op, state, &ret);
+     if (subreq == NULL) {
+         ret = ENOMEM;
+         goto immediately;
+     }
+ 
++    tevent_req_set_callback(subreq,
++                            sdap_ad_tokengroups_initgr_mapping_connect_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
++sdap_ad_tokengroups_initgr_mapping_connect_done(struct tevent_req *subreq)
++{
++    struct sdap_ad_tokengroups_initgr_mapping_state *state = NULL;
++    struct tevent_req *req = NULL;
++    int ret;
++    int dp_error = DP_ERR_FATAL;
++
++    req = tevent_req_callback_data(subreq, struct tevent_req);
++    state = tevent_req_data(req,
++                            struct sdap_ad_tokengroups_initgr_mapping_state);
++
++
++    ret = sdap_id_op_connect_recv(subreq, &dp_error);
++    talloc_zfree(subreq);
++
++    if (ret != EOK) {
++        tevent_req_error(req, ret);
++        return;
++    }
++
++    subreq = sdap_get_ad_tokengroups_send(state, state->ev, state->opts,
++                                          sdap_id_op_handle(state->op),
++                                          state->username,
++                                          state->orig_dn, state->timeout);
++    if (subreq == NULL) {
++        tevent_req_error(req, ENOMEM);
++        return;
++    }
++
+     tevent_req_set_callback(subreq, sdap_ad_tokengroups_initgr_mapping_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;
++    return;
+ }
+ 
+ static void sdap_ad_tokengroups_initgr_mapping_done(struct tevent_req *subreq)
+@@ -896,22 +967,31 @@ static int sdap_ad_tokengroups_initgr_mapping_recv(struct tevent_req *req)
+ struct sdap_ad_tokengroups_initgr_posix_state {
+     struct tevent_context *ev;
+     struct sdap_id_ctx *id_ctx;
++    struct sdap_id_conn_ctx *conn;
+     struct sdap_options *opts;
++    struct sdap_handle *sh;
+     struct sysdb_ctx *sysdb;
+     struct sss_domain_info *domain;
++    const char *orig_dn;
++    int timeout;
+     const char *username;
++
++    struct sdap_id_op *op;
+ };
+ 
+ static void
+ sdap_ad_tokengroups_initgr_posix_tg_done(struct tevent_req *subreq);
+ 
+ static void
++sdap_ad_tokengroups_initgr_posix_sids_connect_done(struct tevent_req *subreq);
++static void
+ sdap_ad_tokengroups_initgr_posix_sids_done(struct tevent_req *subreq);
+ 
+ static struct tevent_req *
+ sdap_ad_tokengroups_initgr_posix_send(TALLOC_CTX *mem_ctx,
+                                       struct tevent_context *ev,
+                                       struct sdap_id_ctx *id_ctx,
++                                      struct sdap_id_conn_ctx *conn,
+                                       struct sdap_options *opts,
+                                       struct sysdb_ctx *sysdb,
+                                       struct sss_domain_info *domain,
+@@ -923,6 +1003,8 @@ sdap_ad_tokengroups_initgr_posix_send(TALLOC_CTX *mem_ctx,
+     struct sdap_ad_tokengroups_initgr_posix_state *state = NULL;
+     struct tevent_req *req = NULL;
+     struct tevent_req *subreq = NULL;
++    struct sdap_domain *sdom;
++    struct ad_id_ctx *subdom_id_ctx;
+     errno_t ret;
+ 
+     req = tevent_req_create(mem_ctx, &state,
+@@ -934,36 +1016,91 @@ sdap_ad_tokengroups_initgr_posix_send(TALLOC_CTX *mem_ctx,
+ 
+     state->ev = ev;
+     state->id_ctx = id_ctx;
++    state->conn = conn;
+     state->opts = opts;
++    state->sh = sh;
+     state->sysdb = sysdb;
+     state->domain = domain;
++    state->orig_dn = orig_dn;
++    state->timeout = timeout;
+     state->username = talloc_strdup(state, name);
+     if (state->username == NULL) {
+         ret = ENOMEM;
+         goto immediately;
+     }
+ 
+-    subreq = sdap_get_ad_tokengroups_send(state, ev, opts, sh, name, orig_dn,
+-                                          timeout);
++    sdom = sdap_domain_get(opts, domain);
++    if (sdom == NULL || sdom->pvt == NULL) {
++        DEBUG(SSSDBG_CRIT_FAILURE, ("No ID ctx available for [%s].\n",
++                                    domain->name));
++        ret = EINVAL;
++        goto immediately;
++    }
++    subdom_id_ctx = talloc_get_type(sdom->pvt, struct ad_id_ctx);
++    state->op = sdap_id_op_create(state, subdom_id_ctx->ldap_ctx->conn_cache);
++    if (!state->op) {
++        DEBUG(2, ("sdap_id_op_create failed\n"));
++        ret = ENOMEM;
++        goto immediately;
++    }
++
++    subreq = sdap_id_op_connect_send(state->op, state, &ret);
+     if (subreq == NULL) {
+         ret = ENOMEM;
+         goto immediately;
+     }
+ 
++    tevent_req_set_callback(subreq,
++                            sdap_ad_tokengroups_initgr_posix_sids_connect_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
++sdap_ad_tokengroups_initgr_posix_sids_connect_done(struct tevent_req *subreq)
++{
++    struct sdap_ad_tokengroups_initgr_posix_state *state = NULL;
++    struct tevent_req *req = NULL;
++    int ret;
++    int dp_error = DP_ERR_FATAL;
++
++    req = tevent_req_callback_data(subreq, struct tevent_req);
++    state = tevent_req_data(req,
++                            struct sdap_ad_tokengroups_initgr_posix_state);
++
++
++    ret = sdap_id_op_connect_recv(subreq, &dp_error);
++    talloc_zfree(subreq);
++
++    if (ret != EOK) {
++        tevent_req_error(req, ret);
++        return;
++    }
++
++    subreq = sdap_get_ad_tokengroups_send(state, state->ev, state->opts,
++                                          sdap_id_op_handle(state->op),
++                                          state->username, state->orig_dn,
++                                          state->timeout);
++    if (subreq == NULL) {
++        tevent_req_error(req, ENOMEM);
++        return;
++    }
++
+     tevent_req_set_callback(subreq, sdap_ad_tokengroups_initgr_posix_tg_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;
++    return;
+ }
+ 
+ static void
+@@ -1089,6 +1226,7 @@ sdap_ad_tokengroups_initgr_posix_tg_done(struct tevent_req *subreq)
+     /* download missing SIDs */
+     missing_sids = talloc_steal(state, missing_sids);
+     subreq = sdap_ad_resolve_sids_send(state, state->ev, state->id_ctx,
++                                       state->conn,
+                                        state->opts, state->domain,
+                                        missing_sids);
+     if (subreq == NULL) {
+@@ -1154,6 +1292,7 @@ struct tevent_req *
+ sdap_ad_tokengroups_initgroups_send(TALLOC_CTX *mem_ctx,
+                                     struct tevent_context *ev,
+                                     struct sdap_id_ctx *id_ctx,
++                                    struct sdap_id_conn_ctx *conn,
+                                     struct sdap_options *opts,
+                                     struct sysdb_ctx *sysdb,
+                                     struct sss_domain_info *domain,
+@@ -1184,8 +1323,8 @@ sdap_ad_tokengroups_initgroups_send(TALLOC_CTX *mem_ctx,
+                                                          name, orig_dn,
+                                                          timeout);
+     } else {
+-        subreq = sdap_ad_tokengroups_initgr_posix_send(state, ev, id_ctx, opts,
+-                                                       sysdb, domain, sh,
++        subreq = sdap_ad_tokengroups_initgr_posix_send(state, ev, id_ctx, conn,
++                                                       opts, sysdb, domain, sh,
+                                                        name, orig_dn,
+                                                        timeout);
+     }
+-- 
+1.8.4.2
+
diff --git a/SOURCES/0028-AD-Add-a-utility-function-to-create-list-of-connecti.patch b/SOURCES/0028-AD-Add-a-utility-function-to-create-list-of-connecti.patch
new file mode 100644
index 0000000..3083367
--- /dev/null
+++ b/SOURCES/0028-AD-Add-a-utility-function-to-create-list-of-connecti.patch
@@ -0,0 +1,497 @@
+From 1dced7370e55be16154bbb649606f928765819d0 Mon Sep 17 00:00:00 2001
+From: Jakub Hrozek <jhrozek@redhat.com>
+Date: Tue, 3 Dec 2013 20:45:44 +0100
+Subject: [PATCH 28/31] AD: Add a utility function to create list of
+ connections
+
+ad_id.c and ad_access.c used the same block of code. With the upcoming
+option to disable GC lookups, we should unify the code in a function to
+avoid breaking one of the code paths.
+
+The same applies for the LDAP connection to the trusted AD DC.
+
+Includes a unit test.
+---
+ Makefile.am                       |  28 +++++
+ src/providers/ad/ad_access.c      |  16 +--
+ src/providers/ad/ad_access.h      |   4 +-
+ src/providers/ad/ad_common.c      |  52 +++++++++
+ src/providers/ad/ad_common.h      |   7 ++
+ src/providers/ad/ad_id.c          |  29 ++---
+ src/providers/ad/ad_init.c        |   3 +-
+ src/tests/cmocka/test_ad_common.c | 221 ++++++++++++++++++++++++++++++++++++++
+ 8 files changed, 319 insertions(+), 41 deletions(-)
+ create mode 100644 src/tests/cmocka/test_ad_common.c
+
+diff --git a/Makefile.am b/Makefile.am
+index 583ccdb499306268640bfb894f673c42945e19ff..da407038089f3c010dea139735db9e0e2f000943 100644
+--- a/Makefile.am
++++ b/Makefile.am
+@@ -152,6 +152,7 @@ if HAVE_CMOCKA
+         test_sss_idmap \
+         test_utils \
+         ad_access_filter_tests \
++        ad_common_tests \
+         test_search_bases
+ endif
+ 
+@@ -1398,6 +1399,7 @@ ad_access_filter_tests_SOURCES = \
+     src/util/sss_krb5.c \
+     src/util/find_uid.c \
+     src/util/user_info_msg.c \
++    src/providers/ad/ad_common.c \
+     src/tests/cmocka/test_ad_access_filter.c
+ ad_access_filter_tests_CFLAGS = \
+     $(AM_CFLAGS) \
+@@ -1416,6 +1418,32 @@ ad_access_filter_tests_LDADD = \
+     libsss_krb5_common.la \
+     libsss_test_common.la
+ 
++ad_common_tests_SOURCES = \
++    $(sssd_be_SOURCES) \
++    src/util/sss_ldap.c \
++    src/util/sss_krb5.c \
++    src/util/find_uid.c \
++    src/util/user_info_msg.c \
++    src/tests/cmocka/test_ad_common.c
++ad_common_tests_CFLAGS = \
++    $(AM_CFLAGS) \
++    $(SYSTEMD_LOGIN_CFLAGS) \
++    -DUNIT_TESTING
++ad_common_tests_LDFLAGS = \
++    -Wl,-wrap,sdap_set_sasl_options
++ad_common_tests_LDADD = \
++    $(PAM_LIBS) \
++    $(CMOCKA_LIBS) \
++    $(SSSD_LIBS) \
++    $(CARES_LIBS) \
++    $(KRB5_LIBS) \
++    $(SSSD_INTERNAL_LTLIBS) \
++    $(SYSTEMD_LOGIN_LIBS) \
++    libsss_ldap_common.la \
++    libsss_idmap.la \
++    libsss_krb5_common.la \
++    libsss_test_common.la
++
+ endif
+ 
+ noinst_PROGRAMS = pam_test_client
+diff --git a/src/providers/ad/ad_access.c b/src/providers/ad/ad_access.c
+index 6995172db304810899e538b37572e4ba953db3e7..68a292abc88daa2f10f6797db50cc75335e80483 100644
+--- a/src/providers/ad/ad_access.c
++++ b/src/providers/ad/ad_access.c
+@@ -274,26 +274,12 @@ ad_access_send(TALLOC_CTX *mem_ctx,
+         goto done;
+     }
+ 
+-    state->clist = talloc_zero_array(state, struct sdap_id_conn_ctx *, 3);
++    state->clist = ad_gc_conn_list(state, ctx->ad_id_ctx, domain);
+     if (state->clist == NULL) {
+         ret = ENOMEM;
+         goto done;
+     }
+ 
+-    /* Always try GC first */
+-    ctx->gc_ctx->ignore_mark_offline = false;
+-    state->clist[0] = ctx->gc_ctx;
+-    if (IS_SUBDOMAIN(domain) == false) {
+-        /* fall back to ldap if gc is not available */
+-        state->clist[0]->ignore_mark_offline = true;
+-
+-        /* With root domain users we have the option to
+-         * fall back to LDAP in case ie POSIX attributes
+-         * are used but not replicated to GC
+-         */
+-        state->clist[1] = ctx->ldap_ctx;
+-    }
+-
+     ret = ad_access_step(req, state->clist[state->cindex]);
+     if (ret != EOK) {
+         goto done;
+diff --git a/src/providers/ad/ad_access.h b/src/providers/ad/ad_access.h
+index ca5e69729c574be53b7da04df0ff89446da04c58..3bd19ccc508b43f7103c7041dcc8573a00235097 100644
+--- a/src/providers/ad/ad_access.h
++++ b/src/providers/ad/ad_access.h
+@@ -26,9 +26,7 @@
+ struct ad_access_ctx {
+     struct dp_option *ad_options;
+     struct sdap_access_ctx *sdap_access_ctx;
+-
+-    struct sdap_id_conn_ctx *ldap_ctx;
+-    struct sdap_id_conn_ctx *gc_ctx;
++    struct ad_id_ctx *ad_id_ctx;
+ };
+ 
+ void
+diff --git a/src/providers/ad/ad_common.c b/src/providers/ad/ad_common.c
+index f679c11ad18078b454b778ef30e40cca716412cb..af0ec839964233c7642205f4489e5b6462509848 100644
+--- a/src/providers/ad/ad_common.c
++++ b/src/providers/ad/ad_common.c
+@@ -1096,3 +1096,55 @@ ad_id_ctx_init(struct ad_options *ad_opts, struct be_ctx *bectx)
+ 
+     return ad_ctx;
+ }
++
++struct sdap_id_conn_ctx *
++ad_get_dom_ldap_conn(struct ad_id_ctx *ad_ctx, struct sss_domain_info *dom)
++{
++    struct sdap_id_conn_ctx *conn;
++    struct sdap_domain *sdom;
++    struct ad_id_ctx *subdom_id_ctx;
++
++    if (IS_SUBDOMAIN(dom)) {
++        sdom = sdap_domain_get(ad_ctx->sdap_id_ctx->opts, dom);
++        if (sdom == NULL || sdom->pvt == NULL) {
++            DEBUG(SSSDBG_CRIT_FAILURE, ("No ID ctx available for [%s].\n",
++                                        dom->name));
++            return NULL;
++        }
++        subdom_id_ctx = talloc_get_type(sdom->pvt, struct ad_id_ctx);
++        conn = subdom_id_ctx->ldap_ctx;
++    } else {
++        conn = ad_ctx->ldap_ctx;
++    }
++
++    return conn;
++}
++
++struct sdap_id_conn_ctx **
++ad_gc_conn_list(TALLOC_CTX *mem_ctx, struct ad_id_ctx *ad_ctx,
++                struct sss_domain_info *dom)
++{
++    struct sdap_id_conn_ctx **clist;
++
++    clist = talloc_zero_array(mem_ctx, struct sdap_id_conn_ctx *, 3);
++    if (clist == NULL) return NULL;
++
++    /* Always try GC first */
++    clist[0] = ad_ctx->gc_ctx;
++    if (IS_SUBDOMAIN(dom) == true) {
++        clist[0]->ignore_mark_offline = false;
++        /* Subdomain users are only present in GC. */
++        return clist;
++    }
++
++    /* fall back to ldap if gc is not available */
++    clist[0]->ignore_mark_offline = true;
++
++    /* With root domain users we have the option to
++     * fall back to LDAP in case ie POSIX attributes
++     * are used but not replicated to GC
++     */
++    clist[1] = ad_ctx->ldap_ctx;
++
++    return clist;
++}
+diff --git a/src/providers/ad/ad_common.h b/src/providers/ad/ad_common.h
+index b8b73c042b8a5433f720c89c04447c07cd3eac43..ed5b8584dc5327a24e60985486c6155604271fd2 100644
+--- a/src/providers/ad/ad_common.h
++++ b/src/providers/ad/ad_common.h
+@@ -115,6 +115,13 @@ ad_get_dyndns_options(struct be_ctx *be_ctx,
+ struct ad_id_ctx *
+ ad_id_ctx_init(struct ad_options *ad_opts, struct be_ctx *bectx);
+ 
++struct sdap_id_conn_ctx **
++ad_gc_conn_list(TALLOC_CTX *mem_ctx, struct ad_id_ctx *ad_ctx,
++               struct sss_domain_info *dom);
++
++struct sdap_id_conn_ctx *
++ad_get_dom_ldap_conn(struct ad_id_ctx *ad_ctx, struct sss_domain_info *dom);
++
+ /* AD dynamic DNS updates */
+ errno_t ad_dyndns_init(struct be_ctx *be_ctx,
+                        struct ad_options *ctx);
+diff --git a/src/providers/ad/ad_id.c b/src/providers/ad/ad_id.c
+index cf71b172dd7c241a9280a7ea72ef2518f66a7435..e47c41863a14eed695907548d64f4559fbae629d 100644
+--- a/src/providers/ad/ad_id.c
++++ b/src/providers/ad/ad_id.c
+@@ -188,12 +188,6 @@ get_conn_list(struct be_req *breq, struct ad_id_ctx *ad_ctx,
+               struct sss_domain_info *dom, struct be_acct_req *ar)
+ {
+     struct sdap_id_conn_ctx **clist;
+-    struct sdap_domain *sdom;
+-    struct ad_id_ctx *subdom_id_ctx;
+-
+-    /* LDAP, GC, sentinel */
+-    clist = talloc_zero_array(breq, struct sdap_id_conn_ctx *, 3);
+-    if (clist == NULL) return NULL;
+ 
+     switch (ar->entry_type & BE_REQ_TYPE_MASK) {
+     case BE_REQ_USER: /* user */
+@@ -201,24 +195,17 @@ get_conn_list(struct be_req *breq, struct ad_id_ctx *ad_ctx,
+     case BE_REQ_USER_AND_GROUP: /* get SID */
+     case BE_REQ_GROUP: /* group */
+     case BE_REQ_INITGROUPS: /* init groups for user */
+-        /* Always try GC first */
+-        clist[0] = ad_ctx->gc_ctx;
+-        if (IS_SUBDOMAIN(dom) == true) {
+-            clist[0]->ignore_mark_offline = false;
+-            /* Subdomain users are only present in GC. */
+-            break;
+-        }
+-        /* fall back to ldap if gc is not available */
+-        clist[0]->ignore_mark_offline = true;
+-
+-        /* With root domain users we have the option to
+-         * fall back to LDAP in case ie POSIX attributes
+-         * are used but not replicated to GC
+-         */
+-        clist[1] = ad_ctx->ldap_ctx;
++        clist = ad_gc_conn_list(breq, ad_ctx, dom);
++        if (clist == NULL) return NULL;
+         break;
++
+     default:
++        /* Requests for other object should only contact LDAP by default */
++        clist = talloc_zero_array(breq, struct sdap_id_conn_ctx *, 2);
++        if (clist == NULL) return NULL;
++
+         clist[0] = ad_ctx->ldap_ctx;
++        clist[1] = NULL;
+         break;
+     }
+ 
+diff --git a/src/providers/ad/ad_init.c b/src/providers/ad/ad_init.c
+index 332bfda3801db3824ce1896d37e65e2c3a6b8b8b..ed69a7d9889bac1281b5ff7c7b0f290ab09173fb 100644
+--- a/src/providers/ad/ad_init.c
++++ b/src/providers/ad/ad_init.c
+@@ -377,8 +377,7 @@ sssm_ad_access_init(struct be_ctx *bectx,
+     if (ret != EOK) {
+         goto fail;
+     }
+-    access_ctx->ldap_ctx = ad_id_ctx->ldap_ctx;
+-    access_ctx->gc_ctx = ad_id_ctx->gc_ctx;
++    access_ctx->ad_id_ctx = ad_id_ctx;
+ 
+     ret = dp_copy_options(access_ctx, ad_options->basic, AD_OPTS_BASIC,
+                           &access_ctx->ad_options);
+diff --git a/src/tests/cmocka/test_ad_common.c b/src/tests/cmocka/test_ad_common.c
+new file mode 100644
+index 0000000000000000000000000000000000000000..648b68f2dc05947b1fbb4c680ec63d3c2c6275b3
+--- /dev/null
++++ b/src/tests/cmocka/test_ad_common.c
+@@ -0,0 +1,221 @@
++/*
++    Authors:
++        Jakub Hrozek <jhrozek@redhat.com>
++
++    Copyright (C) 2013 Red Hat
++
++    SSSD tests: AD access control filter tests
++
++    This program is free software; you can redistribute it and/or modify
++    it under the terms of the GNU General Public License as published by
++    the Free Software Foundation; either version 3 of the License, or
++    (at your option) any later version.
++
++    This program is distributed in the hope that it will be useful,
++    but WITHOUT ANY WARRANTY; without even the implied warranty of
++    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++    GNU General Public License for more details.
++
++    You should have received a copy of the GNU General Public License
++    along with this program.  If not, see <http://www.gnu.org/licenses/>.
++*/
++
++#include <talloc.h>
++#include <tevent.h>
++#include <errno.h>
++#include <popt.h>
++#include <unistd.h>
++#include <sys/types.h>
++#include <ifaddrs.h>
++#include <arpa/inet.h>
++
++/* In order to access opaque types */
++#include "providers/ad/ad_common.c"
++
++#include "tests/cmocka/common_mock.h"
++
++#define DOMNAME     "domname"
++#define SUBDOMNAME  "sub."DOMNAME
++#define REALMNAME   DOMNAME
++#define HOST_NAME   "ad."REALMNAME
++
++struct ad_common_test_ctx {
++    struct ad_id_ctx *ad_ctx;
++    struct ad_id_ctx *subdom_ad_ctx;
++
++    struct sss_domain_info *dom;
++    struct sss_domain_info *subdom;
++};
++
++static void
++ad_common_test_setup(void **state)
++{
++    struct ad_common_test_ctx *test_ctx;
++    errno_t ret;
++    struct sdap_domain *sdom;
++    struct ad_id_ctx *ad_ctx;
++    struct ad_id_ctx *subdom_ad_ctx;
++    struct sdap_id_conn_ctx *subdom_ldap_ctx;
++
++    assert_true(leak_check_setup());
++    check_leaks_push(global_talloc_context);
++
++    test_ctx = talloc_zero(global_talloc_context, struct ad_common_test_ctx);
++    assert_non_null(test_ctx);
++
++    test_ctx->dom = talloc_zero(test_ctx, struct sss_domain_info);
++    assert_non_null(test_ctx->dom);
++    test_ctx->dom->name = discard_const(DOMNAME);
++
++    test_ctx->subdom = talloc_zero(test_ctx, struct sss_domain_info);
++    assert_non_null(test_ctx->subdom);
++    test_ctx->subdom->name = discard_const(SUBDOMNAME);
++    test_ctx->subdom->parent = test_ctx->dom;
++
++    ad_ctx = talloc_zero(test_ctx, struct ad_id_ctx);
++    assert_non_null(ad_ctx);
++
++    ad_ctx->ad_options = ad_create_default_options(ad_ctx,
++                                                   REALMNAME, HOST_NAME);
++    assert_non_null(ad_ctx->ad_options);
++
++    ad_ctx->gc_ctx = talloc_zero(ad_ctx, struct sdap_id_conn_ctx);
++    assert_non_null(ad_ctx->gc_ctx);
++
++    ad_ctx->ldap_ctx = talloc_zero(ad_ctx, struct sdap_id_conn_ctx);
++    assert_non_null(ad_ctx->ldap_ctx);
++
++    ad_ctx->sdap_id_ctx = talloc_zero(ad_ctx, struct sdap_id_ctx);
++    assert_non_null(ad_ctx->sdap_id_ctx);
++
++    ad_ctx->sdap_id_ctx->opts = talloc_zero(ad_ctx->sdap_id_ctx,
++                                            struct sdap_options);
++    assert_non_null(ad_ctx->sdap_id_ctx->opts);
++
++    ret = sdap_domain_add(ad_ctx->sdap_id_ctx->opts, test_ctx->dom, &sdom);
++    assert_int_equal(ret, EOK);
++
++    subdom_ad_ctx = talloc_zero(test_ctx, struct ad_id_ctx);
++    assert_non_null(subdom_ad_ctx);
++
++    subdom_ldap_ctx = talloc_zero(subdom_ad_ctx, struct sdap_id_conn_ctx);
++    assert_non_null(subdom_ldap_ctx);
++    subdom_ad_ctx->ldap_ctx = subdom_ldap_ctx;
++
++    ret = sdap_domain_add(ad_ctx->sdap_id_ctx->opts, test_ctx->subdom, &sdom);
++    assert_int_equal(ret, EOK);
++    sdom->pvt = subdom_ad_ctx;
++
++    test_ctx->ad_ctx = ad_ctx;
++    test_ctx->subdom_ad_ctx = subdom_ad_ctx;
++
++    check_leaks_push(test_ctx);
++    *state = test_ctx;
++}
++
++static void
++ad_common_test_teardown(void **state)
++{
++    struct ad_common_test_ctx *test_ctx = talloc_get_type(*state,
++                                                  struct ad_common_test_ctx);
++    assert_non_null(test_ctx);
++
++    assert_true(check_leaks_pop(test_ctx) == true);
++    talloc_free(test_ctx);
++    assert_true(check_leaks_pop(global_talloc_context) == true);
++    assert_true(leak_check_teardown());
++}
++
++errno_t
++__wrap_sdap_set_sasl_options(struct sdap_options *id_opts,
++                             char *default_primary,
++                             char *default_realm,
++                             const char *keytab_path)
++{
++    /* Pretend SASL is fine */
++    return EOK;
++}
++
++void test_ldap_conn_list(void **state)
++{
++    struct sdap_id_conn_ctx *conn;
++
++    struct ad_common_test_ctx *test_ctx = talloc_get_type(*state,
++                                                     struct ad_common_test_ctx);
++    assert_non_null(test_ctx);
++
++    conn = ad_get_dom_ldap_conn(test_ctx->ad_ctx, test_ctx->dom);
++    assert_true(conn == test_ctx->ad_ctx->ldap_ctx);
++
++    conn = ad_get_dom_ldap_conn(test_ctx->ad_ctx, test_ctx->subdom);
++    assert_true(conn == test_ctx->subdom_ad_ctx->ldap_ctx);
++}
++
++void test_conn_list(void **state)
++{
++    struct sdap_id_conn_ctx **conn_list;
++
++    struct ad_common_test_ctx *test_ctx = talloc_get_type(*state,
++                                                     struct ad_common_test_ctx);
++    assert_non_null(test_ctx);
++
++    conn_list = ad_gc_conn_list(test_ctx, test_ctx->ad_ctx, test_ctx->dom);
++    assert_non_null(conn_list);
++
++    assert_true(conn_list[0] == test_ctx->ad_ctx->gc_ctx);
++    /* If there is a fallback, we should ignore the offline mode */
++    assert_true(conn_list[0]->ignore_mark_offline);
++    assert_true(conn_list[1] == test_ctx->ad_ctx->ldap_ctx);
++    assert_false(conn_list[1]->ignore_mark_offline);
++    assert_null(conn_list[2]);
++    talloc_free(conn_list);
++
++    conn_list = ad_gc_conn_list(test_ctx, test_ctx->ad_ctx, test_ctx->subdom);
++    assert_non_null(conn_list);
++
++    assert_true(conn_list[0] == test_ctx->ad_ctx->gc_ctx);
++    assert_false(conn_list[0]->ignore_mark_offline);
++    assert_null(conn_list[1]);
++    talloc_free(conn_list);
++}
++
++int main(int argc, const char *argv[])
++{
++    poptContext pc;
++    int opt;
++    struct poptOption long_options[] = {
++        POPT_AUTOHELP
++        SSSD_DEBUG_OPTS
++        POPT_TABLEEND
++    };
++
++    const UnitTest tests[] = {
++        unit_test_setup_teardown(test_ldap_conn_list,
++                                 ad_common_test_setup,
++                                 ad_common_test_teardown),
++        unit_test_setup_teardown(test_conn_list,
++                                 ad_common_test_setup,
++                                 ad_common_test_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_INIT(debug_level);
++
++    tests_set_cwd();
++
++    return run_tests(tests);
++}
+-- 
+1.8.4.2
+
diff --git a/SOURCES/0029-AD-Add-a-new-option-to-turn-off-GC-lookups.patch b/SOURCES/0029-AD-Add-a-new-option-to-turn-off-GC-lookups.patch
new file mode 100644
index 0000000..03ff29d
--- /dev/null
+++ b/SOURCES/0029-AD-Add-a-new-option-to-turn-off-GC-lookups.patch
@@ -0,0 +1,175 @@
+From 168396cd93b3f0e42b4842f520f2bcece91274c6 Mon Sep 17 00:00:00 2001
+From: Jakub Hrozek <jhrozek@redhat.com>
+Date: Fri, 29 Nov 2013 11:39:09 +0100
+Subject: [PATCH 29/31] AD: Add a new option to turn off GC lookups
+
+SSSD now defaults to using GC by default. For some environments, for
+instance those that don't or can't replicate the POSIX attributes to
+Global Catalog, this might not be desirable.
+
+This patch introduces a new option ad_enable_gc, that is enabled by
+default. Setting this option to false makes the SSSD contact only the
+LDAP port of AD DCs.
+---
+ src/config/etc/sssd.api.d/sssd-ad.conf |  1 +
+ src/man/sssd-ad.5.xml                  | 17 +++++++++++++++++
+ src/providers/ad/ad_common.c           | 31 ++++++++++++++++++-------------
+ src/providers/ad/ad_common.h           |  1 +
+ src/providers/ad/ad_opts.h             |  1 +
+ src/tests/cmocka/test_ad_common.c      | 20 ++++++++++++++++++++
+ 6 files changed, 58 insertions(+), 13 deletions(-)
+
+diff --git a/src/config/etc/sssd.api.d/sssd-ad.conf b/src/config/etc/sssd.api.d/sssd-ad.conf
+index 9f606f6c4da65d4bfb20a97ee27801dac9307868..00e8968d2b6dab33a39005f11a497cb3e2185302 100644
+--- a/src/config/etc/sssd.api.d/sssd-ad.conf
++++ b/src/config/etc/sssd.api.d/sssd-ad.conf
+@@ -5,6 +5,7 @@ ad_backup_server = str, None, false
+ ad_hostname = str, None, false
+ ad_enable_dns_sites = bool, None, false
+ ad_access_filter = str, None, false
++ad_enable_gc = bool, None, false
+ ldap_uri = str, None, false
+ ldap_backup_uri = str, None, false
+ ldap_search_base = str, None, false
+diff --git a/src/man/sssd-ad.5.xml b/src/man/sssd-ad.5.xml
+index e31f87a96a14907c64166e53da443ad735c6e85e..38cc31278cf87c98ca9e53cf91fda7b141bff78d 100644
+--- a/src/man/sssd-ad.5.xml
++++ b/src/man/sssd-ad.5.xml
+@@ -228,6 +228,23 @@ FOREST:EXAMPLE.COM:(memberOf=cn=admins,ou=groups,dc=example,dc=com)
+                 </varlistentry>
+ 
+                 <varlistentry>
++                    <term>ad_enable_gc (boolean)</term>
++                    <listitem>
++                        <para>
++                            By default, the SSSD connects to the Global
++                            Catalog first to retrieve users and uses the
++                            LDAP port to retrieve group memberships or
++                            as a fallback. Disabling this option makes
++                            the SSSD only connect to the LDAP port of the
++                            current AD server.
++                        </para>
++                        <para>
++                            Default: true
++                        </para>
++                    </listitem>
++                </varlistentry>
++
++                <varlistentry>
+                     <term>dyndns_update (boolean)</term>
+                     <listitem>
+                         <para>
+diff --git a/src/providers/ad/ad_common.c b/src/providers/ad/ad_common.c
+index af0ec839964233c7642205f4489e5b6462509848..a5ea4f587f30575a5903d8ae1a459f53512c011f 100644
+--- a/src/providers/ad/ad_common.c
++++ b/src/providers/ad/ad_common.c
+@@ -1125,26 +1125,31 @@ ad_gc_conn_list(TALLOC_CTX *mem_ctx, struct ad_id_ctx *ad_ctx,
+                 struct sss_domain_info *dom)
+ {
+     struct sdap_id_conn_ctx **clist;
++    int cindex = 0;
+ 
+     clist = talloc_zero_array(mem_ctx, struct sdap_id_conn_ctx *, 3);
+     if (clist == NULL) return NULL;
+ 
+     /* Always try GC first */
+-    clist[0] = ad_ctx->gc_ctx;
+-    if (IS_SUBDOMAIN(dom) == true) {
+-        clist[0]->ignore_mark_offline = false;
+-        /* Subdomain users are only present in GC. */
+-        return clist;
++    if (dp_opt_get_bool(ad_ctx->ad_options->basic, AD_ENABLE_GC)) {
++        clist[cindex] = ad_ctx->gc_ctx;
++        if (IS_SUBDOMAIN(dom) == true) {
++            clist[cindex]->ignore_mark_offline = false;
++            /* Subdomain users are only present in GC. */
++            return clist;
++        }
++        /* fall back to ldap if gc is not available */
++        clist[cindex]->ignore_mark_offline = true;
++        cindex++;
+     }
+ 
+-    /* fall back to ldap if gc is not available */
+-    clist[0]->ignore_mark_offline = true;
+-
+-    /* With root domain users we have the option to
+-     * fall back to LDAP in case ie POSIX attributes
+-     * are used but not replicated to GC
+-     */
+-    clist[1] = ad_ctx->ldap_ctx;
++    if (IS_SUBDOMAIN(dom) == false) {
++        /* With root domain users we have the option to
++         * fall back to LDAP in case ie POSIX attributes
++         * are used but not replicated to GC
++         */
++        clist[cindex] = ad_ctx->ldap_ctx;
++    }
+ 
+     return clist;
+ }
+diff --git a/src/providers/ad/ad_common.h b/src/providers/ad/ad_common.h
+index ed5b8584dc5327a24e60985486c6155604271fd2..d370cef69124c127f41d7c4cbaa25713363e7752 100644
+--- a/src/providers/ad/ad_common.h
++++ b/src/providers/ad/ad_common.h
+@@ -42,6 +42,7 @@ enum ad_basic_opt {
+     AD_KRB5_REALM,
+     AD_ENABLE_DNS_SITES,
+     AD_ACCESS_FILTER,
++    AD_ENABLE_GC,
+ 
+     AD_OPTS_BASIC /* opts counter */
+ };
+diff --git a/src/providers/ad/ad_opts.h b/src/providers/ad/ad_opts.h
+index 8022a16274a04389b7a64b491ec28a0c3c55aaef..5b7b1c89f5f45d7cc744a955e6378390948a99fd 100644
+--- a/src/providers/ad/ad_opts.h
++++ b/src/providers/ad/ad_opts.h
+@@ -36,6 +36,7 @@ struct dp_option ad_basic_opts[] = {
+     { "krb5_realm", DP_OPT_STRING, NULL_STRING, NULL_STRING},
+     { "ad_enable_dns_sites", DP_OPT_BOOL, BOOL_TRUE, BOOL_TRUE },
+     { "ad_access_filter", DP_OPT_STRING, NULL_STRING, NULL_STRING},
++    { "ad_enable_gc", DP_OPT_BOOL, BOOL_TRUE, BOOL_TRUE },
+     DP_OPTION_TERMINATOR
+ };
+ 
+diff --git a/src/tests/cmocka/test_ad_common.c b/src/tests/cmocka/test_ad_common.c
+index 648b68f2dc05947b1fbb4c680ec63d3c2c6275b3..07502b82d43d730562c60125b639d8e7d1034458 100644
+--- a/src/tests/cmocka/test_ad_common.c
++++ b/src/tests/cmocka/test_ad_common.c
+@@ -159,6 +159,8 @@ void test_conn_list(void **state)
+                                                      struct ad_common_test_ctx);
+     assert_non_null(test_ctx);
+ 
++    assert_true(dp_opt_get_bool(test_ctx->ad_ctx->ad_options->basic,
++                                AD_ENABLE_GC));
+     conn_list = ad_gc_conn_list(test_ctx, test_ctx->ad_ctx, test_ctx->dom);
+     assert_non_null(conn_list);
+ 
+@@ -177,6 +179,24 @@ void test_conn_list(void **state)
+     assert_false(conn_list[0]->ignore_mark_offline);
+     assert_null(conn_list[1]);
+     talloc_free(conn_list);
++
++    dp_opt_set_bool(test_ctx->ad_ctx->ad_options->basic, AD_ENABLE_GC, false);
++    assert_false(dp_opt_get_bool(test_ctx->ad_ctx->ad_options->basic,
++                                 AD_ENABLE_GC));
++
++    conn_list = ad_gc_conn_list(test_ctx, test_ctx->ad_ctx, test_ctx->dom);
++    assert_non_null(conn_list);
++
++    assert_true(conn_list[0] == test_ctx->ad_ctx->ldap_ctx);
++    assert_false(conn_list[0]->ignore_mark_offline);
++    assert_null(conn_list[1]);
++    talloc_free(conn_list);
++
++    conn_list = ad_gc_conn_list(test_ctx, test_ctx->ad_ctx, test_ctx->subdom);
++    assert_non_null(conn_list);
++
++    assert_null(conn_list[0]);
++    talloc_free(conn_list);
+ }
+ 
+ int main(int argc, const char *argv[])
+-- 
+1.8.4.2
+
diff --git a/SOURCES/0030-AD-Enable-fallback-to-LDAP-of-trusted-domain.patch b/SOURCES/0030-AD-Enable-fallback-to-LDAP-of-trusted-domain.patch
new file mode 100644
index 0000000..7190d1f
--- /dev/null
+++ b/SOURCES/0030-AD-Enable-fallback-to-LDAP-of-trusted-domain.patch
@@ -0,0 +1,69 @@
+From dfebe8a952561e51fe1d603886ba4e979b29d889 Mon Sep 17 00:00:00 2001
+From: Jakub Hrozek <jhrozek@redhat.com>
+Date: Fri, 13 Dec 2013 20:11:11 +0100
+Subject: [PATCH 30/31] AD: Enable fallback to LDAP of trusted domain
+
+Since we have the LDAP port of a trusted AD GC always available now, we
+can always perform a fallback.
+---
+ src/providers/ad/ad_common.c      | 14 +-------------
+ src/tests/cmocka/test_ad_common.c |  7 ++++---
+ 2 files changed, 5 insertions(+), 16 deletions(-)
+
+diff --git a/src/providers/ad/ad_common.c b/src/providers/ad/ad_common.c
+index a5ea4f587f30575a5903d8ae1a459f53512c011f..99fa4c07af2a79bb3ca195214ddb0dbd60c61620 100644
+--- a/src/providers/ad/ad_common.c
++++ b/src/providers/ad/ad_common.c
+@@ -1133,23 +1133,11 @@ ad_gc_conn_list(TALLOC_CTX *mem_ctx, struct ad_id_ctx *ad_ctx,
+     /* Always try GC first */
+     if (dp_opt_get_bool(ad_ctx->ad_options->basic, AD_ENABLE_GC)) {
+         clist[cindex] = ad_ctx->gc_ctx;
+-        if (IS_SUBDOMAIN(dom) == true) {
+-            clist[cindex]->ignore_mark_offline = false;
+-            /* Subdomain users are only present in GC. */
+-            return clist;
+-        }
+-        /* fall back to ldap if gc is not available */
+         clist[cindex]->ignore_mark_offline = true;
+         cindex++;
+     }
+ 
+-    if (IS_SUBDOMAIN(dom) == false) {
+-        /* With root domain users we have the option to
+-         * fall back to LDAP in case ie POSIX attributes
+-         * are used but not replicated to GC
+-         */
+-        clist[cindex] = ad_ctx->ldap_ctx;
+-    }
++    clist[cindex] = ad_get_dom_ldap_conn(ad_ctx, dom);
+ 
+     return clist;
+ }
+diff --git a/src/tests/cmocka/test_ad_common.c b/src/tests/cmocka/test_ad_common.c
+index 07502b82d43d730562c60125b639d8e7d1034458..bbd56b1b9b78cb78cb24726522822ad2f7ae9980 100644
+--- a/src/tests/cmocka/test_ad_common.c
++++ b/src/tests/cmocka/test_ad_common.c
+@@ -176,8 +176,9 @@ void test_conn_list(void **state)
+     assert_non_null(conn_list);
+ 
+     assert_true(conn_list[0] == test_ctx->ad_ctx->gc_ctx);
+-    assert_false(conn_list[0]->ignore_mark_offline);
+-    assert_null(conn_list[1]);
++    assert_true(conn_list[0]->ignore_mark_offline);
++    assert_true(conn_list[1] == test_ctx->subdom_ad_ctx->ldap_ctx);
++    assert_false(conn_list[1]->ignore_mark_offline);
+     talloc_free(conn_list);
+ 
+     dp_opt_set_bool(test_ctx->ad_ctx->ad_options->basic, AD_ENABLE_GC, false);
+@@ -195,7 +196,7 @@ void test_conn_list(void **state)
+     conn_list = ad_gc_conn_list(test_ctx, test_ctx->ad_ctx, test_ctx->subdom);
+     assert_non_null(conn_list);
+ 
+-    assert_null(conn_list[0]);
++    assert_true(conn_list[0] == test_ctx->subdom_ad_ctx->ldap_ctx);
+     talloc_free(conn_list);
+ }
+ 
+-- 
+1.8.4.2
+
diff --git a/SOURCES/0031-Bump-sss_idmap-version-to-3-0-3.patch b/SOURCES/0031-Bump-sss_idmap-version-to-3-0-3.patch
new file mode 100644
index 0000000..57a1353
--- /dev/null
+++ b/SOURCES/0031-Bump-sss_idmap-version-to-3-0-3.patch
@@ -0,0 +1,26 @@
+From eb03d9c884e6d69af31d079a3bcb572de1a5838b Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pavel=20B=C5=99ezina?= <pbrezina@redhat.com>
+Date: Mon, 4 Nov 2013 11:59:49 +0100
+Subject: [PATCH 31/31] Bump sss_idmap version to 3:0:3
+
+New functions were added.
+---
+ Makefile.am | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/Makefile.am b/Makefile.am
+index da407038089f3c010dea139735db9e0e2f000943..16648f9aa2275b60ec84a95ff8a26b1225b97918 100644
+--- a/Makefile.am
++++ b/Makefile.am
+@@ -612,7 +612,7 @@ libsss_idmap_la_SOURCES = \
+     src/lib/idmap/sss_idmap_conv.c \
+     src/util/murmurhash3.c
+ libsss_idmap_la_LDFLAGS = \
+-    -version-info 2:0:2
++    -version-info 3:0:3
+ 
+ dist_pkgconfig_DATA += src/sss_client/idmap/sss_nss_idmap.pc
+ libsss_nss_idmap_la_SOURCES = \
+-- 
+1.8.4.2
+
diff --git a/SOURCES/0032-AD-Refresh-subdomain-data-structures-on-startup.patch b/SOURCES/0032-AD-Refresh-subdomain-data-structures-on-startup.patch
new file mode 100644
index 0000000..ff6078a
--- /dev/null
+++ b/SOURCES/0032-AD-Refresh-subdomain-data-structures-on-startup.patch
@@ -0,0 +1,98 @@
+From 1213f1a45e222b3c1b304262c51900d8ab2a886a Mon Sep 17 00:00:00 2001
+From: Jakub Hrozek <jhrozek@redhat.com>
+Date: Fri, 13 Dec 2013 19:11:47 +0100
+Subject: [PATCH 32/34] AD: Refresh subdomain data structures on startup
+
+Previously, if no changes were done to the list of subdomains, the SSSD
+didn't update its list of sdap_domain mappings for the new subdomain.
+This resulted in errors as no id_ctx was present for the subdomain
+during lookup.
+
+This patch moves the block of code performed during update to a function
+of its own and calls it during provider initialization as well.
+---
+ src/providers/ad/ad_subdomains.c | 49 ++++++++++++++++++++++++++--------------
+ 1 file changed, 32 insertions(+), 17 deletions(-)
+
+diff --git a/src/providers/ad/ad_subdomains.c b/src/providers/ad/ad_subdomains.c
+index 100fb13e99f7bf4b3946b1f5c5f9c626674bfb46..e438a688c364084a3f2bbca338a39d61aa86b5d6 100644
+--- a/src/providers/ad/ad_subdomains.c
++++ b/src/providers/ad/ad_subdomains.c
+@@ -414,6 +414,31 @@ done:
+     return ret;
+ }
+ 
++static errno_t ad_subdom_reinit(struct ad_subdomains_ctx *ctx)
++{
++    errno_t ret;
++
++    ret = sysdb_update_subdomains(ctx->be_ctx->domain);
++    if (ret != EOK) {
++        DEBUG(SSSDBG_OP_FAILURE, ("sysdb_update_subdomains failed.\n"));
++        return ret;
++    }
++
++    ret = sss_write_domain_mappings(ctx->be_ctx->domain, false);
++    if (ret != EOK) {
++        DEBUG(SSSDBG_MINOR_FAILURE, ("sss_krb5_write_mappings failed.\n"));
++        /* Just continue */
++    }
++
++    ret = ads_store_sdap_subdom(ctx, ctx->be_ctx->domain);
++    if (ret != EOK) {
++        DEBUG(SSSDBG_OP_FAILURE, ("ads_store_sdap_subdom failed.\n"));
++        return ret;
++    }
++
++    return EOK;
++}
++
+ static void ad_subdomains_get_conn_done(struct tevent_req *req);
+ static void ad_subdomains_master_dom_done(struct tevent_req *req);
+ static errno_t ad_subdomains_get_slave(struct ad_subdomains_req_ctx *ctx);
+@@ -619,25 +644,15 @@ static void ad_subdomains_get_slave_domain_done(struct tevent_req *req)
+         goto done;
+     }
+ 
++    DEBUG(SSSDBG_TRACE_LIBS, ("There are %schanges\n",
++                    refresh_has_changes ? "" : "no "));
++
+     if (refresh_has_changes) {
+-        ret = sysdb_update_subdomains(ctx->sd_ctx->be_ctx->domain);
++        ret = ad_subdom_reinit(ctx->sd_ctx);
+         if (ret != EOK) {
+-            DEBUG(SSSDBG_OP_FAILURE, ("sysdb_update_subdomains failed.\n"));
++            DEBUG(SSSDBG_OP_FAILURE, ("Could not reinitialize subdomains\n"));
+             goto done;
+         }
+-
+-        ret = ads_store_sdap_subdom(ctx->sd_ctx, ctx->sd_ctx->be_ctx->domain);
+-        if (ret != EOK) {
+-            DEBUG(SSSDBG_OP_FAILURE, ("ads_store_sdap_subdom failed.\n"));
+-            goto done;
+-        }
+-
+-        ret = sss_write_domain_mappings(ctx->sd_ctx->be_ctx->domain, false);
+-        if (ret != EOK) {
+-            DEBUG(SSSDBG_MINOR_FAILURE,
+-                  ("sss_krb5_write_mappings failed.\n"));
+-            /* Just continue */
+-        }
+     }
+ 
+     ret = EOK;
+@@ -783,9 +798,9 @@ int ad_subdom_init(struct be_ctx *be_ctx,
+         return EFAULT;
+     }
+ 
+-    ret = sysdb_update_subdomains(be_ctx->domain);
++    ret = ad_subdom_reinit(ctx);
+     if (ret != EOK) {
+-        DEBUG(SSSDBG_MINOR_FAILURE, ("Could not load the list of subdomains. "
++        DEBUG(SSSDBG_MINOR_FAILURE, ("Could not reinitialize subdomains. "
+               "Users from trusted domains might not be resolved correctly\n"));
+         /* Ignore this error and try to discover the subdomains later */
+     }
+-- 
+1.8.4.2
+
diff --git a/SOURCES/0033-IPA-Refresh-subdomain-data-structures-on-startup.patch b/SOURCES/0033-IPA-Refresh-subdomain-data-structures-on-startup.patch
new file mode 100644
index 0000000..2d070bf
--- /dev/null
+++ b/SOURCES/0033-IPA-Refresh-subdomain-data-structures-on-startup.patch
@@ -0,0 +1,93 @@
+From 3a1056929310cf304449baf3feed94bc8fe46383 Mon Sep 17 00:00:00 2001
+From: Jakub Hrozek <jhrozek@redhat.com>
+Date: Tue, 17 Dec 2013 17:22:45 +0100
+Subject: [PATCH 33/34] IPA: Refresh subdomain data structures on startup
+
+Write domain-mappings at startup and initialize internal data structures
+on provider startup, not only during updates.
+---
+ src/providers/ipa/ipa_subdomains.c | 51 ++++++++++++++++++++++++--------------
+ 1 file changed, 32 insertions(+), 19 deletions(-)
+
+diff --git a/src/providers/ipa/ipa_subdomains.c b/src/providers/ipa/ipa_subdomains.c
+index 416e21913be8e991c9f496ff2b54f238b602f304..56fd4f99654aa07f822c49d6d39526765785f0de 100644
+--- a/src/providers/ipa/ipa_subdomains.c
++++ b/src/providers/ipa/ipa_subdomains.c
+@@ -267,6 +267,35 @@ ipa_ad_subdom_refresh(struct be_ctx *be_ctx,
+     return EOK;
+ }
+ 
++static errno_t
++ipa_subdom_reinit(struct ipa_subdomains_ctx *ctx)
++{
++    errno_t ret;
++
++    ret = sysdb_update_subdomains(ctx->be_ctx->domain);
++    if (ret != EOK) {
++        DEBUG(SSSDBG_OP_FAILURE, ("sysdb_update_subdomains failed.\n"));
++        return ret;
++    }
++
++    ret = ipa_ad_subdom_refresh(ctx->be_ctx, ctx->id_ctx, ctx->be_ctx->domain);
++    if (ret != EOK) {
++        DEBUG(SSSDBG_OP_FAILURE, ("ipa_ad_subdom_refresh failed.\n"));
++        return ret;
++    }
++
++    ret = sss_write_domain_mappings(ctx->be_ctx->domain,
++                    dp_opt_get_bool(ctx->id_ctx->ipa_options->basic,
++                    IPA_SERVER_MODE));
++    if (ret != EOK) {
++        DEBUG(SSSDBG_MINOR_FAILURE,
++                ("sss_krb5_write_mappings failed.\n"));
++        /* Just continue */
++    }
++
++    return EOK;
++}
++
+ static void
+ ipa_ad_subdom_remove(struct ipa_subdomains_ctx *ctx,
+                      struct sss_domain_info *subdom)
+@@ -921,27 +950,11 @@ static void ipa_subdomains_handler_done(struct tevent_req *req)
+     }
+ 
+     if (refresh_has_changes) {
+-        ret = sysdb_update_subdomains(domain);
++        ret = ipa_subdom_reinit(ctx->sd_ctx);
+         if (ret != EOK) {
+-            DEBUG(SSSDBG_OP_FAILURE, ("sysdb_update_subdomains failed.\n"));
++            DEBUG(SSSDBG_OP_FAILURE, ("Could not reinitialize subdomains\n"));
+             goto done;
+         }
+-
+-        ret = ipa_ad_subdom_refresh(ctx->sd_ctx->be_ctx, ctx->sd_ctx->id_ctx,
+-                                    domain);
+-        if (ret != EOK) {
+-            DEBUG(SSSDBG_OP_FAILURE, ("ipa_ad_subdom_refresh failed.\n"));
+-            goto done;
+-        }
+-
+-        ret = sss_write_domain_mappings(domain,
+-                        dp_opt_get_bool(ctx->sd_ctx->id_ctx->ipa_options->basic,
+-                        IPA_SERVER_MODE));
+-        if (ret != EOK) {
+-            DEBUG(SSSDBG_MINOR_FAILURE,
+-                  ("sss_krb5_write_mappings failed.\n"));
+-            /* Just continue */
+-        }
+     }
+ 
+     ret = sysdb_master_domain_update(domain);
+@@ -1289,7 +1302,7 @@ int ipa_subdom_init(struct be_ctx *be_ctx,
+         DEBUG(SSSDBG_MINOR_FAILURE, ("Failed to add subdom offline callback"));
+     }
+ 
+-    ret = sysdb_update_subdomains(be_ctx->domain);
++    ret = ipa_subdom_reinit(ctx);
+     if (ret != EOK) {
+         DEBUG(SSSDBG_MINOR_FAILURE, ("Could not load the list of subdomains. "
+               "Users from trusted domains might not be resolved correctly\n"));
+-- 
+1.8.4.2
+
diff --git a/SOURCES/0034-IPA-Call-ipa_ad_subdom_refresh-when-server-mode-is-i.patch b/SOURCES/0034-IPA-Call-ipa_ad_subdom_refresh-when-server-mode-is-i.patch
new file mode 100644
index 0000000..d3f683f
--- /dev/null
+++ b/SOURCES/0034-IPA-Call-ipa_ad_subdom_refresh-when-server-mode-is-i.patch
@@ -0,0 +1,67 @@
+From 414d36bc08bf3ddb8c742f4548711cc0b448bb85 Mon Sep 17 00:00:00 2001
+From: Jakub Hrozek <jhrozek@redhat.com>
+Date: Wed, 18 Dec 2013 18:14:46 +0100
+Subject: [PATCH 34/34] IPA: Call ipa_ad_subdom_refresh when server mode is
+ initialized
+
+ipa_ad_subdom_refresh was called before IPA server context was
+initialized. On IPA server, this caused the code to dereference a NULL
+pointer and crash.
+---
+ src/providers/ipa/ipa_subdomains.c | 20 ++++++++++++++------
+ 1 file changed, 14 insertions(+), 6 deletions(-)
+
+diff --git a/src/providers/ipa/ipa_subdomains.c b/src/providers/ipa/ipa_subdomains.c
+index 56fd4f99654aa07f822c49d6d39526765785f0de..2d28d7cd0538b204eb2818e71e029dec19456a1c 100644
+--- a/src/providers/ipa/ipa_subdomains.c
++++ b/src/providers/ipa/ipa_subdomains.c
+@@ -278,12 +278,6 @@ ipa_subdom_reinit(struct ipa_subdomains_ctx *ctx)
+         return ret;
+     }
+ 
+-    ret = ipa_ad_subdom_refresh(ctx->be_ctx, ctx->id_ctx, ctx->be_ctx->domain);
+-    if (ret != EOK) {
+-        DEBUG(SSSDBG_OP_FAILURE, ("ipa_ad_subdom_refresh failed.\n"));
+-        return ret;
+-    }
+-
+     ret = sss_write_domain_mappings(ctx->be_ctx->domain,
+                     dp_opt_get_bool(ctx->id_ctx->ipa_options->basic,
+                     IPA_SERVER_MODE));
+@@ -955,6 +949,13 @@ static void ipa_subdomains_handler_done(struct tevent_req *req)
+             DEBUG(SSSDBG_OP_FAILURE, ("Could not reinitialize subdomains\n"));
+             goto done;
+         }
++
++        ret = ipa_ad_subdom_refresh(ctx->sd_ctx->be_ctx, ctx->sd_ctx->id_ctx,
++                                    domain);
++        if (ret != EOK) {
++            DEBUG(SSSDBG_OP_FAILURE, ("ipa_ad_subdom_refresh failed.\n"));
++            goto done;
++        }
+     }
+ 
+     ret = sysdb_master_domain_update(domain);
+@@ -1316,6 +1317,7 @@ int ipa_ad_subdom_init(struct be_ctx *be_ctx,
+ {
+     char *realm;
+     char *hostname;
++    errno_t ret;
+ 
+     if (dp_opt_get_bool(id_ctx->ipa_options->basic,
+                         IPA_SERVER_MODE) == false) {
+@@ -1360,5 +1362,11 @@ int ipa_ad_subdom_init(struct be_ctx *be_ctx,
+     id_ctx->server_mode->trusts = NULL;
+     id_ctx->server_mode->ext_groups = NULL;
+ 
++    ret = ipa_ad_subdom_refresh(be_ctx, id_ctx, be_ctx->domain);
++    if (ret != EOK) {
++        DEBUG(SSSDBG_OP_FAILURE, ("ipa_ad_subdom_refresh failed.\n"));
++        return ret;
++    }
++
+     return EOK;
+ }
+-- 
+1.8.4.2
+
diff --git a/SOURCES/0035-sss_cache-initialize-names-member-of-sss_domain_info.patch b/SOURCES/0035-sss_cache-initialize-names-member-of-sss_domain_info.patch
new file mode 100644
index 0000000..825f38f
--- /dev/null
+++ b/SOURCES/0035-sss_cache-initialize-names-member-of-sss_domain_info.patch
@@ -0,0 +1,80 @@
+From ca3dda947538ca1c16386d4c3f86f97eee7d0abc Mon Sep 17 00:00:00 2001
+From: Sumit Bose <sbose@redhat.com>
+Date: Mon, 25 Nov 2013 17:54:06 +0100
+Subject: [PATCH 35/41] sss_cache: initialize names member of sss_domain_info
+
+sss_tc_fqname() called by sss_get_domain_name() requires that the names
+member of the sss_domain_info struct is set to work properly. If the
+names struct is properly initialized in sss_domain_info the separate one
+in the tool context is not needed anymore.
+
+Related to https://fedorahosted.org/sssd/ticket/1741
+---
+ src/tools/sss_cache.c | 23 ++++++++++-------------
+ 1 file changed, 10 insertions(+), 13 deletions(-)
+
+diff --git a/src/tools/sss_cache.c b/src/tools/sss_cache.c
+index fa2e29de8bf8a035dd725aa3190af1c2a2091e05..3b6e62393f6cf0f6ccc94aea8cf19bf3aedc444f 100644
+--- a/src/tools/sss_cache.c
++++ b/src/tools/sss_cache.c
+@@ -63,7 +63,6 @@ static errno_t search_autofsmaps(TALLOC_CTX *mem_ctx, struct sysdb_ctx *sysdb,
+ struct cache_tool_ctx {
+     struct confdb_ctx *confdb;
+     struct sss_domain_info *domains;
+-    struct sss_names_ctx *nctx;
+ 
+     char *user_filter;
+     char *group_filter;
+@@ -209,7 +208,7 @@ static errno_t update_filter(struct cache_tool_ctx *tctx,
+         return ENOMEM;
+     }
+ 
+-    ret = sss_parse_name(tmp_ctx, tctx->nctx, name,
++    ret = sss_parse_name(tmp_ctx, dinfo->names, name,
+                          &parsed_domain, &parsed_name);
+     if (ret != EOK) {
+         DEBUG(SSSDBG_CRIT_FAILURE, ("sss_parse_name failed\n"));
+@@ -280,17 +279,6 @@ static errno_t update_all_filters(struct cache_tool_ctx *tctx,
+ {
+     errno_t ret;
+ 
+-    if (IS_SUBDOMAIN(dinfo)) {
+-        ret = sss_names_init(tctx, tctx->confdb, dinfo->parent->name,
+-                             &tctx->nctx);
+-    } else {
+-        ret = sss_names_init(tctx, tctx->confdb, dinfo->name, &tctx->nctx);
+-    }
+-    if (ret != EOK) {
+-        DEBUG(SSSDBG_CRIT_FAILURE, ("sss_names_init() failed\n"));
+-        return ret;
+-    }
+-
+     /* Update user filter */
+     ret = update_filter(tctx, dinfo, tctx->user_name,
+                         tctx->update_user_filter, "(%s=%s)", false,
+@@ -467,6 +455,7 @@ errno_t init_domains(struct cache_tool_ctx *ctx, const char *domain)
+ {
+     char *confdb_path;
+     int ret;
++    struct sss_domain_info *dinfo;
+ 
+     confdb_path = talloc_asprintf(ctx, "%s/%s", DB_PATH, CONFDB_FILE);
+     if (confdb_path == NULL) {
+@@ -505,6 +494,14 @@ errno_t init_domains(struct cache_tool_ctx *ctx, const char *domain)
+         }
+     }
+ 
++    for (dinfo = ctx->domains; dinfo; dinfo = get_next_domain(dinfo, false)) {
++        ret = sss_names_init(ctx, ctx->confdb, dinfo->name, &dinfo->names);
++        if (ret != EOK) {
++            DEBUG(SSSDBG_CRIT_FAILURE, ("sss_names_init() failed\n"));
++            return ret;
++        }
++    }
++
+     return EOK;
+ }
+ 
+-- 
+1.8.4.2
+
diff --git a/SOURCES/0036-sss_cache-fix-case-sensitivity-issue.patch b/SOURCES/0036-sss_cache-fix-case-sensitivity-issue.patch
new file mode 100644
index 0000000..b9ca7c5
--- /dev/null
+++ b/SOURCES/0036-sss_cache-fix-case-sensitivity-issue.patch
@@ -0,0 +1,112 @@
+From 2db8726f09800d64231f403198742d22a04a8d8b Mon Sep 17 00:00:00 2001
+From: Sumit Bose <sbose@redhat.com>
+Date: Tue, 26 Nov 2013 10:27:50 +0100
+Subject: [PATCH 36/41] sss_cache: fix case-sensitivity issue
+
+For case-insensitive domains the lower-case name for case-insensitive
+searches is stored in SYSDB_NAME_ALIAS.
+
+Related to https://fedorahosted.org/sssd/ticket/1741
+---
+ src/tools/sss_cache.c | 63 +++++++++++++++++++++++++++++----------------------
+ 1 file changed, 36 insertions(+), 27 deletions(-)
+
+diff --git a/src/tools/sss_cache.c b/src/tools/sss_cache.c
+index 3b6e62393f6cf0f6ccc94aea8cf19bf3aedc444f..56dc47afdcb92b71dc1ef71d7f26fdf276a1c45f 100644
+--- a/src/tools/sss_cache.c
++++ b/src/tools/sss_cache.c
+@@ -196,6 +196,8 @@ static errno_t update_filter(struct cache_tool_ctx *tctx,
+     TALLOC_CTX *tmp_ctx = NULL;
+     char *use_name = NULL;
+     char *filter;
++    char *sanitized;
++    char *lc_sanitized;
+ 
+     if (!name || !update) {
+         /* Nothing to do */
+@@ -215,6 +217,14 @@ static errno_t update_filter(struct cache_tool_ctx *tctx,
+         goto done;
+     }
+ 
++    if (parsed_domain != NULL && strcasecmp(dinfo->name, parsed_domain) != 0) {
++        /* We were able to parse the domain from given fqdn, but it
++         * does not match with currently processed domain. */
++        filter = NULL;
++        ret = EOK;
++        goto done;
++    }
++
+     if (!dinfo->case_sensitive && !force_case_sensitivity) {
+         use_name = sss_tc_utf8_str_tolower(tmp_ctx, parsed_name);
+         if (!use_name) {
+@@ -232,41 +242,40 @@ static errno_t update_filter(struct cache_tool_ctx *tctx,
+             ret = ENOMEM;
+             goto done;
+         }
++    }
+ 
+-        if (!strcasecmp(dinfo->name, parsed_domain)) {
+-            if (fmt) {
+-                filter = talloc_asprintf(tmp_ctx, fmt,
+-                                         SYSDB_NAME, use_name);
+-            } else {
+-                filter = talloc_strdup(tmp_ctx, use_name);
+-            }
+-            if (filter == NULL) {
+-                DEBUG(SSSDBG_CRIT_FAILURE, ("Out of memory\n"));
+-                ret = ENOMEM;
+-                goto done;
+-            }
++    ret = sss_filter_sanitize_for_dom(tmp_ctx, use_name, dinfo,
++                                      &sanitized, &lc_sanitized);
++    if (ret != EOK) {
++        DEBUG(SSSDBG_CRIT_FAILURE, ("Failed to sanitize the given name.\n"));
++        goto done;
++    }
++
++    if (fmt) {
++        if (!dinfo->case_sensitive && !force_case_sensitivity) {
++            filter = talloc_asprintf(tmp_ctx, "(|(%s=%s)(%s=%s))",
++                                     SYSDB_NAME_ALIAS, lc_sanitized,
++                                     SYSDB_NAME_ALIAS, sanitized);
+         } else {
+-            /* We were able to parse the domain from given fqdn, but it
+-             * does not match with currently processed domain. */
+-            filter = NULL;
++            filter = talloc_asprintf(tmp_ctx, fmt, SYSDB_NAME, sanitized);
+         }
+     } else {
+-        if (fmt) {
+-            filter = talloc_asprintf(tmp_ctx, fmt, SYSDB_NAME, name);
+-        } else {
+-            filter = talloc_strdup(tmp_ctx, name);
+-        }
+-        if (filter == NULL) {
+-            DEBUG(SSSDBG_CRIT_FAILURE, ("Out of memory\n"));
+-            ret = ENOMEM;
+-            goto done;
+-        }
++        filter = talloc_strdup(tmp_ctx, sanitized);
++    }
++    if (filter == NULL) {
++        DEBUG(SSSDBG_CRIT_FAILURE, ("Out of memory\n"));
++        ret = ENOMEM;
++        goto done;
+     }
+ 
+-    talloc_free(*_filter);
+-    *_filter = talloc_steal(tctx, filter);
+     ret = EOK;
++
+ done:
++    if (ret == EOK) {
++        talloc_free(*_filter);
++        *_filter = talloc_steal(tctx, filter);
++    }
++
+     talloc_free(tmp_ctx);
+     return ret;
+ 
+-- 
+1.8.4.2
+
diff --git a/SOURCES/0037-Add-sysdb_attrs_add_lc_name_alias.patch b/SOURCES/0037-Add-sysdb_attrs_add_lc_name_alias.patch
new file mode 100644
index 0000000..96db4f3
--- /dev/null
+++ b/SOURCES/0037-Add-sysdb_attrs_add_lc_name_alias.patch
@@ -0,0 +1,107 @@
+From b1f74ee745aa84f53fe330d55fafb9810012f875 Mon Sep 17 00:00:00 2001
+From: Sumit Bose <sbose@redhat.com>
+Date: Thu, 28 Nov 2013 11:28:39 +0100
+Subject: [PATCH 37/41] Add sysdb_attrs_add_lc_name_alias
+
+---
+ src/db/sysdb.c          | 22 ++++++++++++++++++++++
+ src/db/sysdb.h          |  2 ++
+ src/tests/sysdb-tests.c | 29 +++++++++++++++++++++++++++++
+ 3 files changed, 53 insertions(+)
+
+diff --git a/src/db/sysdb.c b/src/db/sysdb.c
+index da5dbe84c2415025a188544f4b4c944888f07888..2a4be58008fc1164765db26aaba3886071448d30 100644
+--- a/src/db/sysdb.c
++++ b/src/db/sysdb.c
+@@ -618,6 +618,28 @@ int sysdb_attrs_add_time_t(struct sysdb_attrs *attrs,
+     return ret;
+ }
+ 
++int sysdb_attrs_add_lc_name_alias(struct sysdb_attrs *attrs,
++                                  const char *value)
++{
++    char *lc_str;
++    int ret;
++
++    if (attrs == NULL || value == NULL) {
++        return EINVAL;
++    }
++
++    lc_str = sss_tc_utf8_str_tolower(attrs, value);
++    if (lc_str == NULL) {
++        DEBUG(SSSDBG_OP_FAILURE, ("Cannot convert name to lowercase\n"));
++        return ENOMEM;
++    }
++
++    ret = sysdb_attrs_add_string(attrs, SYSDB_NAME_ALIAS, lc_str);
++    talloc_free(lc_str);
++
++    return ret;
++}
++
+ int sysdb_attrs_copy_values(struct sysdb_attrs *src,
+                             struct sysdb_attrs *dst,
+                             const char *name)
+diff --git a/src/db/sysdb.h b/src/db/sysdb.h
+index 4d5ef0b4794a85aa7fbc8f261d42eddd1043284e..f3358d642efd1c13203061c43e455a5c26c72740 100644
+--- a/src/db/sysdb.h
++++ b/src/db/sysdb.h
+@@ -274,6 +274,8 @@ int sysdb_attrs_add_uint32(struct sysdb_attrs *attrs,
+                            const char *name, uint32_t value);
+ int sysdb_attrs_add_time_t(struct sysdb_attrs *attrs,
+                            const char *name, time_t value);
++int sysdb_attrs_add_lc_name_alias(struct sysdb_attrs *attrs,
++                                  const char *value);
+ int sysdb_attrs_copy_values(struct sysdb_attrs *src,
+                             struct sysdb_attrs *dst,
+                             const char *name);
+diff --git a/src/tests/sysdb-tests.c b/src/tests/sysdb-tests.c
+index ddbf6f28fd5024945fedcb3c6e2122948c4f1459..63ffac82e15849e5f6534462ce7c58b183412acc 100644
+--- a/src/tests/sysdb-tests.c
++++ b/src/tests/sysdb-tests.c
+@@ -4422,6 +4422,33 @@ START_TEST(test_sysdb_svc_remove_alias)
+ }
+ END_TEST
+ 
++#define LC_NAME_ALIAS_TEST_VAL "TeSt VaLuE"
++#define LC_NAME_ALIAS_CHECK_VAL "test value"
++START_TEST(test_sysdb_attrs_add_lc_name_alias)
++{
++    int ret;
++    struct sysdb_attrs *attrs;
++    const char *str;
++
++    ret = sysdb_attrs_add_lc_name_alias(NULL, NULL);
++    fail_unless(ret == EINVAL, "EINVAL not returned for NULL input");
++
++    attrs = sysdb_new_attrs(NULL);
++    fail_unless(attrs != NULL, "sysdb_new_attrs failed");
++
++    ret = sysdb_attrs_add_lc_name_alias(attrs, LC_NAME_ALIAS_TEST_VAL);
++    fail_unless(ret == EOK, "sysdb_attrs_add_lc_name_alias failed");
++
++    ret = sysdb_attrs_get_string(attrs, SYSDB_NAME_ALIAS, &str);
++    fail_unless(ret == EOK, "sysdb_attrs_get_string failed");
++    fail_unless(strcmp(str, LC_NAME_ALIAS_CHECK_VAL) == 0,
++                "Unexpected value, expected [%s], got [%s]",
++                LC_NAME_ALIAS_CHECK_VAL, str);
++
++    talloc_free(attrs);
++}
++END_TEST
++
+ START_TEST(test_sysdb_has_enumerated)
+ {
+     errno_t ret;
+@@ -5188,6 +5215,8 @@ Suite *create_sysdb_suite(void)
+     tcase_add_test(tc_sysdb, test_sysdb_store_services);
+     tcase_add_test(tc_sysdb, test_sysdb_svc_remove_alias);
+ 
++    tcase_add_test(tc_sysdb, test_sysdb_attrs_add_lc_name_alias);
++
+ /* Add all test cases to the test suite */
+     suite_add_tcase(s, tc_sysdb);
+ 
+-- 
+1.8.4.2
+
diff --git a/SOURCES/0038-Use-sysdb_attrs_add_lc_name_alias-to-add-case-insens.patch b/SOURCES/0038-Use-sysdb_attrs_add_lc_name_alias-to-add-case-insens.patch
new file mode 100644
index 0000000..0679bf7
--- /dev/null
+++ b/SOURCES/0038-Use-sysdb_attrs_add_lc_name_alias-to-add-case-insens.patch
@@ -0,0 +1,195 @@
+From e90d014c1ce95a30f4be2383a4b4f47ad21c5601 Mon Sep 17 00:00:00 2001
+From: Sumit Bose <sbose@redhat.com>
+Date: Thu, 28 Nov 2013 12:31:24 +0100
+Subject: [PATCH 38/41] Use sysdb_attrs_add_lc_name_alias to add
+ case-insensitive alias
+
+---
+ src/providers/ipa/ipa_s2n_exop.c     | 27 ++++++---------------------
+ src/providers/ldap/sdap_async.c      | 21 ++++++++++++++++-----
+ src/providers/proxy/proxy_id.c       | 20 ++------------------
+ src/providers/proxy/proxy_netgroup.c | 10 +---------
+ src/responder/pac/pacsrv_utils.c     |  4 ++--
+ 5 files changed, 27 insertions(+), 55 deletions(-)
+
+diff --git a/src/providers/ipa/ipa_s2n_exop.c b/src/providers/ipa/ipa_s2n_exop.c
+index d8506aaae2582f496033212e5abeef753244c21a..8bad16d42facac3a585a4fdf579c3d2bb913dd71 100644
+--- a/src/providers/ipa/ipa_s2n_exop.c
++++ b/src/providers/ipa/ipa_s2n_exop.c
+@@ -651,7 +651,6 @@ static void ipa_s2n_get_user_done(struct tevent_req *subreq)
+     struct sysdb_attrs *user_attrs = NULL;
+     struct sysdb_attrs *group_attrs = NULL;
+     char *name;
+-    char *lc_name;
+     char *realm;
+     char *upn;
+     struct berval *bv_req = NULL;
+@@ -767,16 +766,10 @@ static void ipa_s2n_get_user_done(struct tevent_req *subreq)
+                 goto done;
+             }
+ 
+-            lc_name = sss_tc_utf8_str_tolower(user_attrs, name);
+-            if (lc_name == NULL) {
+-                DEBUG(SSSDBG_CRIT_FAILURE, ("Cannot convert name to lowercase\n"));
+-                ret =  ENOMEM;
+-                goto done;
+-            }
+-
+-            ret = sysdb_attrs_add_string(user_attrs, SYSDB_NAME_ALIAS, lc_name);
++            ret = sysdb_attrs_add_lc_name_alias(user_attrs, name);
+             if (ret != EOK) {
+-                DEBUG(SSSDBG_OP_FAILURE, ("sysdb_attrs_add_string failed.\n"));
++                DEBUG(SSSDBG_OP_FAILURE,
++                      ("sysdb_attrs_add_lc_name_alias failed.\n"));
+                 goto done;
+             }
+ 
+@@ -852,18 +845,10 @@ static void ipa_s2n_get_user_done(struct tevent_req *subreq)
+                 goto done;
+             }
+ 
+-            lc_name = sss_tc_utf8_str_tolower(group_attrs, name);
+-            if (lc_name == NULL) {
+-                DEBUG(SSSDBG_CRIT_FAILURE,
+-                      ("Cannot convert name to lowercase\n"));
+-                ret = ENOMEM;
+-                goto done;
+-            }
+-
+-            ret = sysdb_attrs_add_string(group_attrs, SYSDB_NAME_ALIAS,
+-                                         lc_name);
++            ret = sysdb_attrs_add_lc_name_alias(group_attrs, name);
+             if (ret != EOK) {
+-                DEBUG(SSSDBG_OP_FAILURE, ("sysdb_attrs_add_string failed.\n"));
++                DEBUG(SSSDBG_OP_FAILURE,
++                      ("sysdb_attrs_add_lc_name_alias failed.\n"));
+                 goto done;
+             }
+ 
+diff --git a/src/providers/ldap/sdap_async.c b/src/providers/ldap/sdap_async.c
+index f5cc962b34c8ef5e8c8dc9cf90b77d782b389032..e905d2dd6d539baadcd29aa0869ca04e845947e2 100644
+--- a/src/providers/ldap/sdap_async.c
++++ b/src/providers/ldap/sdap_async.c
+@@ -2318,12 +2318,23 @@ sdap_save_all_names(const char *name,
+             goto done;
+         }
+ 
+-        ret = sysdb_attrs_add_string(attrs, SYSDB_NAME_ALIAS, domname);
+-        if (ret) {
+-            DEBUG(SSSDBG_OP_FAILURE, ("Failed to add alias [%s] into the "
+-                                      "attribute list\n", aliases[i]));
+-            goto done;
++        if (lowercase) {
++            ret = sysdb_attrs_add_lc_name_alias(attrs, domname);
++            if (ret) {
++                DEBUG(SSSDBG_OP_FAILURE, ("Failed to add lower-cased version "
++                                          "of alias [%s] into the "
++                                          "attribute list\n", aliases[i]));
++                goto done;
++            }
++        } else {
++            ret = sysdb_attrs_add_string(attrs, SYSDB_NAME_ALIAS, domname);
++            if (ret) {
++                DEBUG(SSSDBG_OP_FAILURE, ("Failed to add alias [%s] into the "
++                                          "attribute list\n", aliases[i]));
++                goto done;
++            }
+         }
++
+     }
+ 
+     ret = EOK;
+diff --git a/src/providers/proxy/proxy_id.c b/src/providers/proxy/proxy_id.c
+index 963aad2d0676a665d69be2ea2996ad49cb2bacc6..2c01aad5ae86e11d22383d6b618073ef8e9d6dbb 100644
+--- a/src/providers/proxy/proxy_id.c
++++ b/src/providers/proxy/proxy_id.c
+@@ -223,7 +223,6 @@ static int save_user(struct sysdb_ctx *sysdb, struct sss_domain_info *domain,
+ {
+     const char *shell;
+     const char *gecos;
+-    char *lower;
+     struct sysdb_attrs *attrs = NULL;
+     errno_t ret;
+     const char *cased_alias;
+@@ -249,14 +248,7 @@ static int save_user(struct sysdb_ctx *sysdb, struct sss_domain_info *domain,
+     }
+ 
+     if (lowercase) {
+-        lower = sss_tc_utf8_str_tolower(attrs, pwd->pw_name);
+-        if (!lower) {
+-            DEBUG(SSSDBG_CRIT_FAILURE, ("Cannot convert name to lowercase\n"));
+-            talloc_zfree(attrs);
+-            return ENOMEM;
+-        }
+-
+-        ret = sysdb_attrs_add_string(attrs, SYSDB_NAME_ALIAS, lower);
++        ret = sysdb_attrs_add_lc_name_alias(attrs, pwd->pw_name);
+         if (ret) {
+             DEBUG(SSSDBG_OP_FAILURE, ("Could not add name alias\n"));
+             talloc_zfree(attrs);
+@@ -540,7 +532,6 @@ static int save_group(struct sysdb_ctx *sysdb, struct sss_domain_info *dom,
+ {
+     errno_t ret, sret;
+     struct sysdb_attrs *attrs = NULL;
+-    char *lower;
+     const char *cased_alias;
+     TALLOC_CTX *tmp_ctx;
+     time_t now = time(NULL);
+@@ -595,14 +586,7 @@ static int save_group(struct sysdb_ctx *sysdb, struct sss_domain_info *dom,
+         }
+ 
+         if (dom->case_sensitive == false) {
+-            lower = sss_tc_utf8_str_tolower(attrs, grp->gr_name);
+-            if (!lower) {
+-                DEBUG(SSSDBG_CRIT_FAILURE, ("Cannot convert name to lowercase\n"));
+-                ret = ENOMEM;
+-                goto done;
+-            }
+-
+-            ret = sysdb_attrs_add_string(attrs, SYSDB_NAME_ALIAS, lower);
++            ret = sysdb_attrs_add_lc_name_alias(attrs, grp->gr_name);
+             if (ret) {
+                 DEBUG(SSSDBG_OP_FAILURE, ("Could not add name alias\n"));
+                 ret = ENOMEM;
+diff --git a/src/providers/proxy/proxy_netgroup.c b/src/providers/proxy/proxy_netgroup.c
+index 04a0b18d7079aa15d04562ee5c785caed4456fb5..bb0bc171b8c3688a92c14f711669f752653a39f0 100644
+--- a/src/providers/proxy/proxy_netgroup.c
++++ b/src/providers/proxy/proxy_netgroup.c
+@@ -74,17 +74,9 @@ static errno_t save_netgroup(struct sysdb_ctx *sysdb,
+                              uint64_t cache_timeout)
+ {
+     errno_t ret;
+-    char *lower;
+ 
+     if (lowercase) {
+-        lower = sss_tc_utf8_str_tolower(NULL, name);
+-        if (!lower) {
+-            DEBUG(SSSDBG_CRIT_FAILURE, ("Cannot convert name to lowercase\n"));
+-            return ENOMEM;
+-        }
+-
+-        ret = sysdb_attrs_add_string(attrs, SYSDB_NAME_ALIAS, lower);
+-        talloc_free(lower);
++        ret = sysdb_attrs_add_lc_name_alias(attrs, name);
+         if (ret) {
+             DEBUG(SSSDBG_OP_FAILURE, ("Could not add name alias\n"));
+             return ret;
+diff --git a/src/responder/pac/pacsrv_utils.c b/src/responder/pac/pacsrv_utils.c
+index 30055a1345b7d943e6adf822438263c92e53b51a..6a6ea2e357483c68533c501b93c16383ee644048 100644
+--- a/src/responder/pac/pacsrv_utils.c
++++ b/src/responder/pac/pacsrv_utils.c
+@@ -483,9 +483,9 @@ errno_t get_pwd_from_pac(TALLOC_CTX *mem_ctx,
+         goto done;
+     }
+ 
+-    ret = sysdb_attrs_add_string(attrs, SYSDB_NAME_ALIAS, pwd->pw_name);
++    ret = sysdb_attrs_add_lc_name_alias(attrs, pwd->pw_name);
+     if (ret != EOK) {
+-        DEBUG(SSSDBG_OP_FAILURE, ("sysdb_attrs_add_string failed.\n"));
++        DEBUG(SSSDBG_OP_FAILURE, ("sysdb_attrs_add_lc_name_alias failed.\n"));
+         goto done;
+     }
+ 
+-- 
+1.8.4.2
+
diff --git a/SOURCES/0039-Use-lower-case-name-for-case-insensitive-searches.patch b/SOURCES/0039-Use-lower-case-name-for-case-insensitive-searches.patch
new file mode 100644
index 0000000..ac7d540
--- /dev/null
+++ b/SOURCES/0039-Use-lower-case-name-for-case-insensitive-searches.patch
@@ -0,0 +1,356 @@
+From b0af402f8201d28922892b18792474f4ec546f36 Mon Sep 17 00:00:00 2001
+From: Sumit Bose <sbose@redhat.com>
+Date: Fri, 13 Dec 2013 11:44:59 +0100
+Subject: [PATCH 39/41] Use lower-case name for case-insensitive searches
+
+The patch makes sure that a completely lower-cased version of a fully
+qualified name is used for case insensitive searches. Currently there
+are code paths where the domain name was used as configured and was not
+lower-cased.
+
+To make sure this patch does not break with old entries in the cache or
+case sensitive domains a third template was added to the related filters
+templates which is either filled with a completely lower-cased version or
+with the old version. The other two template values are unchanged.
+---
+ src/db/sysdb.h                       | 10 +++++-----
+ src/db/sysdb_ops.c                   |  8 +++++---
+ src/db/sysdb_search.c                | 30 ++++++++++++++++++++--------
+ src/responder/pam/pam_LOCAL_domain.c |  4 ++--
+ src/tests/cmocka/test_utils.c        | 38 ++++++++++++++++++++++++++++++++++++
+ src/util/sss_tc_utf8.c               | 30 ++++++++++++++++++++++++++++
+ src/util/util.h                      |  6 ++++++
+ 7 files changed, 108 insertions(+), 18 deletions(-)
+
+diff --git a/src/db/sysdb.h b/src/db/sysdb.h
+index f3358d642efd1c13203061c43e455a5c26c72740..f1ed8158ccff70f85940d63f247e23451c22c30f 100644
+--- a/src/db/sysdb.h
++++ b/src/db/sysdb.h
+@@ -144,23 +144,23 @@
+ #define SYSDB_NC "objectclass="SYSDB_NETGROUP_CLASS
+ #define SYSDB_MPGC "|("SYSDB_UC")("SYSDB_GC")"
+ 
+-#define SYSDB_PWNAM_FILTER "(&("SYSDB_UC")(|("SYSDB_NAME_ALIAS"=%s)("SYSDB_NAME"=%s)))"
++#define SYSDB_PWNAM_FILTER "(&("SYSDB_UC")(|("SYSDB_NAME_ALIAS"=%s)("SYSDB_NAME_ALIAS"=%s)("SYSDB_NAME"=%s)))"
+ #define SYSDB_PWUID_FILTER "(&("SYSDB_UC")("SYSDB_UIDNUM"=%lu))"
+ #define SYSDB_PWSID_FILTER "(&("SYSDB_UC")("SYSDB_SID_STR"=%s))"
+ #define SYSDB_PWENT_FILTER "("SYSDB_UC")"
+ 
+-#define SYSDB_GRNAM_FILTER "(&("SYSDB_GC")(|("SYSDB_NAME_ALIAS"=%s)("SYSDB_NAME"=%s)))"
++#define SYSDB_GRNAM_FILTER "(&("SYSDB_GC")(|("SYSDB_NAME_ALIAS"=%s)("SYSDB_NAME_ALIAS"=%s)("SYSDB_NAME"=%s)))"
+ #define SYSDB_GRGID_FILTER "(&("SYSDB_GC")("SYSDB_GIDNUM"=%lu))"
+ #define SYSDB_GRSID_FILTER "(&("SYSDB_GC")("SYSDB_SID_STR"=%s))"
+ #define SYSDB_GRENT_FILTER "("SYSDB_GC")"
+-#define SYSDB_GRNAM_MPG_FILTER "(&("SYSDB_MPGC")(|("SYSDB_NAME_ALIAS"=%s)("SYSDB_NAME"=%s)))"
++#define SYSDB_GRNAM_MPG_FILTER "(&("SYSDB_MPGC")(|("SYSDB_NAME_ALIAS"=%s)("SYSDB_NAME_ALIAS"=%s)("SYSDB_NAME"=%s)))"
+ #define SYSDB_GRGID_MPG_FILTER "(&("SYSDB_MPGC")("SYSDB_GIDNUM"=%lu))"
+ #define SYSDB_GRENT_MPG_FILTER "("SYSDB_MPGC")"
+ 
+ #define SYSDB_INITGR_FILTER "(&("SYSDB_GC")("SYSDB_GIDNUM"=*))"
+ 
+-#define SYSDB_NETGR_FILTER "(&("SYSDB_NC")(|("SYSDB_NAME_ALIAS"=%s)("SYSDB_NAME"=%s)))"
+-#define SYSDB_NETGR_TRIPLES_FILTER "(|("SYSDB_NAME"=%s)("SYSDB_NAME_ALIAS"=%s)("SYSDB_MEMBEROF"=%s))"
++#define SYSDB_NETGR_FILTER "(&("SYSDB_NC")(|("SYSDB_NAME_ALIAS"=%s)("SYSDB_NAME_ALIAS"=%s)("SYSDB_NAME"=%s)))"
++#define SYSDB_NETGR_TRIPLES_FILTER "(|("SYSDB_NAME_ALIAS"=%s)("SYSDB_NAME"=%s)("SYSDB_NAME_ALIAS"=%s)("SYSDB_MEMBEROF"=%s))"
+ 
+ #define SYSDB_SID_FILTER "(&(|("SYSDB_UC")("SYSDB_GC"))("SYSDB_SID_STR"=%s))"
+ 
+diff --git a/src/db/sysdb_ops.c b/src/db/sysdb_ops.c
+index 890bf1eb3cc5fc0b6eb6f7a145aee6d87945cd8d..a5dfd443c84b87609881f9042b3e82958c3b0e5f 100644
+--- a/src/db/sysdb_ops.c
++++ b/src/db/sysdb_ops.c
+@@ -305,6 +305,7 @@ int sysdb_search_user_by_name(TALLOC_CTX *mem_ctx,
+     struct ldb_dn *basedn;
+     size_t msgs_count = 0;
+     char *sanitized_name;
++    char *lc_sanitized_name;
+     char *filter;
+     int ret;
+ 
+@@ -320,13 +321,14 @@ int sysdb_search_user_by_name(TALLOC_CTX *mem_ctx,
+         goto done;
+     }
+ 
+-    ret = sss_filter_sanitize(tmp_ctx, name, &sanitized_name);
++    ret = sss_filter_sanitize_for_dom(tmp_ctx, name, domain, &sanitized_name,
++                                      &lc_sanitized_name);
+     if (ret != EOK) {
+         goto done;
+     }
+ 
+-    filter = talloc_asprintf(tmp_ctx, SYSDB_PWNAM_FILTER, sanitized_name,
+-                             sanitized_name);
++    filter = talloc_asprintf(tmp_ctx, SYSDB_PWNAM_FILTER, lc_sanitized_name,
++                             sanitized_name, sanitized_name);
+     if (!filter) {
+         ret = ENOMEM;
+         goto done;
+diff --git a/src/db/sysdb_search.c b/src/db/sysdb_search.c
+index d15fc73ce2272bff53650ae9dd0dbdad99a849e6..308710a2c780b9a709930b7d8a16a0c07471aff7 100644
+--- a/src/db/sysdb_search.c
++++ b/src/db/sysdb_search.c
+@@ -38,6 +38,7 @@ int sysdb_getpwnam(TALLOC_CTX *mem_ctx,
+     struct ldb_dn *base_dn;
+     struct ldb_result *res;
+     char *sanitized_name;
++    char *lc_sanitized_name;
+     const char *src_name;
+     int ret;
+ 
+@@ -61,13 +62,15 @@ int sysdb_getpwnam(TALLOC_CTX *mem_ctx,
+         goto done;
+     }
+ 
+-    ret = sss_filter_sanitize(tmp_ctx, src_name, &sanitized_name);
++    ret = sss_filter_sanitize_for_dom(tmp_ctx, src_name, domain,
++                                      &sanitized_name, &lc_sanitized_name);
+     if (ret != EOK) {
+         goto done;
+     }
+ 
+     ret = ldb_search(sysdb->ldb, tmp_ctx, &res, base_dn,
+                      LDB_SCOPE_SUBTREE, attrs, SYSDB_PWNAM_FILTER,
++                     lc_sanitized_name,
+                      sanitized_name, sanitized_name);
+     if (ret) {
+         ret = sysdb_error_to_errno(ret);
+@@ -214,6 +217,7 @@ int sysdb_getgrnam(TALLOC_CTX *mem_ctx,
+     struct ldb_dn *base_dn;
+     struct ldb_result *res;
+     const char *src_name;
++    char *lc_sanitized_name;
+     int ret;
+ 
+     tmp_ctx = talloc_new(NULL);
+@@ -243,14 +247,15 @@ int sysdb_getgrnam(TALLOC_CTX *mem_ctx,
+         goto done;
+     }
+ 
+-    ret = sss_filter_sanitize(tmp_ctx, src_name, &sanitized_name);
++    ret = sss_filter_sanitize_for_dom(tmp_ctx, src_name, domain,
++                                      &sanitized_name, &lc_sanitized_name);
+     if (ret != EOK) {
+         goto done;
+     }
+ 
+     ret = ldb_search(sysdb->ldb, tmp_ctx, &res, base_dn,
+                      LDB_SCOPE_SUBTREE, attrs, fmt_filter,
+-                     sanitized_name, sanitized_name);
++                     lc_sanitized_name, sanitized_name, sanitized_name);
+     if (ret) {
+         ret = sysdb_error_to_errno(ret);
+         goto done;
+@@ -481,6 +486,7 @@ int sysdb_get_user_attr(TALLOC_CTX *mem_ctx,
+     struct ldb_dn *base_dn;
+     struct ldb_result *res;
+     char *sanitized_name;
++    char *lc_sanitized_name;
+     int ret;
+ 
+     tmp_ctx = talloc_new(NULL);
+@@ -495,14 +501,15 @@ int sysdb_get_user_attr(TALLOC_CTX *mem_ctx,
+         goto done;
+     }
+ 
+-    ret = sss_filter_sanitize(tmp_ctx, name, &sanitized_name);
++    ret = sss_filter_sanitize_for_dom(tmp_ctx, name, domain, &sanitized_name,
++                                      &lc_sanitized_name);
+     if (ret != EOK) {
+         goto done;
+     }
+ 
+     ret = ldb_search(sysdb->ldb, tmp_ctx, &res, base_dn,
+                      LDB_SCOPE_SUBTREE, attributes,
+-                     SYSDB_PWNAM_FILTER, sanitized_name,
++                     SYSDB_PWNAM_FILTER, lc_sanitized_name, sanitized_name,
+                      sanitized_name);
+     if (ret) {
+         ret = sysdb_error_to_errno(ret);
+@@ -785,6 +792,7 @@ errno_t sysdb_getnetgr(TALLOC_CTX *mem_ctx,
+     struct ldb_dn *base_dn;
+     struct ldb_result *result;
+     char *sanitized_netgroup;
++    char *lc_sanitized_netgroup;
+     char *netgroup_dn;
+     int lret;
+     errno_t ret;
+@@ -802,7 +810,9 @@ errno_t sysdb_getnetgr(TALLOC_CTX *mem_ctx,
+         goto done;
+     }
+ 
+-    ret = sss_filter_sanitize(tmp_ctx, netgroup, &sanitized_netgroup);
++    ret = sss_filter_sanitize_for_dom(tmp_ctx, netgroup, domain,
++                                      &sanitized_netgroup,
++                                      &lc_sanitized_netgroup);
+     if (ret != EOK) {
+         goto done;
+     }
+@@ -816,7 +826,7 @@ errno_t sysdb_getnetgr(TALLOC_CTX *mem_ctx,
+ 
+     lret = ldb_search(sysdb->ldb, tmp_ctx, &result, base_dn,
+                      LDB_SCOPE_SUBTREE, attrs,
+-                     SYSDB_NETGR_TRIPLES_FILTER,
++                     SYSDB_NETGR_TRIPLES_FILTER, lc_sanitized_netgroup,
+                      sanitized_netgroup, sanitized_netgroup,
+                      netgroup_dn);
+     ret = sysdb_error_to_errno(lret);
+@@ -843,6 +853,7 @@ int sysdb_get_netgroup_attr(TALLOC_CTX *mem_ctx,
+     struct ldb_dn *base_dn;
+     struct ldb_result *result;
+     char *sanitized_netgroup;
++    char *lc_sanitized_netgroup;
+     int ret;
+ 
+     tmp_ctx = talloc_new(NULL);
+@@ -857,7 +868,9 @@ int sysdb_get_netgroup_attr(TALLOC_CTX *mem_ctx,
+         goto done;
+     }
+ 
+-    ret = sss_filter_sanitize(tmp_ctx, netgrname, &sanitized_netgroup);
++    ret = sss_filter_sanitize_for_dom(tmp_ctx, netgrname, domain,
++                                      &sanitized_netgroup,
++                                      &lc_sanitized_netgroup);
+     if (ret != EOK) {
+         goto done;
+     }
+@@ -865,6 +878,7 @@ int sysdb_get_netgroup_attr(TALLOC_CTX *mem_ctx,
+     ret = ldb_search(sysdb->ldb, tmp_ctx, &result, base_dn,
+                      LDB_SCOPE_SUBTREE, attributes,
+                      SYSDB_NETGR_FILTER,
++                     lc_sanitized_netgroup,
+                      sanitized_netgroup,
+                      sanitized_netgroup);
+     if (ret) {
+diff --git a/src/responder/pam/pam_LOCAL_domain.c b/src/responder/pam/pam_LOCAL_domain.c
+index e7776cba3b74a86ef63dad30f08b4436a59a89be..49ddbcda39468e6a4f2065d97df309ce881b6aa4 100644
+--- a/src/responder/pam/pam_LOCAL_domain.c
++++ b/src/responder/pam/pam_LOCAL_domain.c
+@@ -258,12 +258,12 @@ int LOCAL_pam_handler(struct pam_auth_req *preq)
+ 
+     if (res->count < 1) {
+         DEBUG(4, ("No user found with filter ["SYSDB_PWNAM_FILTER"]\n",
+-                  pd->user, pd->user));
++                  pd->user, pd->user, pd->user));
+         pd->pam_status = PAM_USER_UNKNOWN;
+         goto done;
+     } else if (res->count > 1) {
+         DEBUG(4, ("More than one object found with filter ["SYSDB_PWNAM_FILTER"]\n",
+-                  pd->user, pd->user));
++                  pd->user, pd->user, pd->user));
+         lreq->error = EFAULT;
+         goto done;
+     }
+diff --git a/src/tests/cmocka/test_utils.c b/src/tests/cmocka/test_utils.c
+index 1be59ab69cfa632010c5e1700dc79f59b6f48fde..61e8e9f07ca2f6772b12fe602acb3d2872b39c10 100644
+--- a/src/tests/cmocka/test_utils.c
++++ b/src/tests/cmocka/test_utils.c
+@@ -177,6 +177,41 @@ void test_find_subdomain_by_sid_missing_sid(void **state)
+     }
+ }
+ 
++#define TEST_SANITIZE_INPUT "TestUser@Test.Domain"
++#define TEST_SANITIZE_LC_INPUT "testuser@test.domain"
++
++void test_sss_filter_sanitize_for_dom(void **state)
++{
++    struct dom_list_test_ctx *test_ctx;
++    int ret;
++    char *sanitized;
++    char *lc_sanitized;
++    struct sss_domain_info *dom;
++
++    test_ctx = talloc_get_type(*state, struct dom_list_test_ctx);
++    dom = test_ctx->dom_list;
++
++    dom->case_sensitive = true;
++
++    ret = sss_filter_sanitize_for_dom(test_ctx, TEST_SANITIZE_INPUT, dom,
++                                      &sanitized, &lc_sanitized);
++    assert_int_equal(ret, EOK);
++    assert_string_equal(sanitized, TEST_SANITIZE_INPUT);
++    assert_string_equal(lc_sanitized, TEST_SANITIZE_INPUT);
++    talloc_free(sanitized);
++    talloc_free(lc_sanitized);
++
++    dom->case_sensitive = false;
++
++    ret = sss_filter_sanitize_for_dom(test_ctx, TEST_SANITIZE_INPUT, dom,
++                                      &sanitized, &lc_sanitized);
++    assert_int_equal(ret, EOK);
++    assert_string_equal(sanitized, TEST_SANITIZE_INPUT);
++    assert_string_equal(lc_sanitized, TEST_SANITIZE_LC_INPUT);
++    talloc_free(sanitized);
++    talloc_free(lc_sanitized);
++}
++
+ int main(int argc, const char *argv[])
+ {
+     poptContext pc;
+@@ -194,6 +229,9 @@ int main(int argc, const char *argv[])
+                                  setup_dom_list, teardown_dom_list),
+         unit_test_setup_teardown(test_find_subdomain_by_sid_missing_sid,
+                                  setup_dom_list, teardown_dom_list),
++
++        unit_test_setup_teardown(test_sss_filter_sanitize_for_dom,
++                                 setup_dom_list, teardown_dom_list),
+     };
+ 
+     /* Set debug level to invalid value so we can deside if -d 0 was used. */
+diff --git a/src/util/sss_tc_utf8.c b/src/util/sss_tc_utf8.c
+index 6a976211f7b77d329ba3261577c43466406f3da9..e1426a44f3518783dca4f22cd6016cdde92d0f56 100644
+--- a/src/util/sss_tc_utf8.c
++++ b/src/util/sss_tc_utf8.c
+@@ -55,3 +55,33 @@ sss_tc_utf8_tolower(TALLOC_CTX *mem_ctx, const uint8_t *s, size_t len, size_t *_
+     return ret;
+ }
+ 
++errno_t sss_filter_sanitize_for_dom(TALLOC_CTX *mem_ctx,
++                                    const char *input,
++                                    struct sss_domain_info *dom,
++                                    char **sanitized,
++                                    char **lc_sanitized)
++{
++    int ret;
++
++    ret = sss_filter_sanitize(mem_ctx, input, sanitized);
++    if (ret != EOK) {
++        DEBUG(SSSDBG_OP_FAILURE, ("sss_filter_sanitize failed.\n"));
++        return ret;
++    }
++
++    if (dom->case_sensitive) {
++        *lc_sanitized = talloc_strdup(mem_ctx, *sanitized);
++    } else {
++        *lc_sanitized = sss_tc_utf8_str_tolower(mem_ctx, *sanitized);
++    }
++
++    if (*lc_sanitized == NULL) {
++        DEBUG(SSSDBG_OP_FAILURE, ("%s failed.\n",
++                                              dom->case_sensitive ?
++                                                    "talloc_strdup" :
++                                                    "sss_tc_utf8_str_tolower"));
++        return ENOMEM;
++    }
++
++    return EOK;
++}
+diff --git a/src/util/util.h b/src/util/util.h
+index 058c1c27986f9749a18dd4c92ddb9428349a1dac..3334476ab83a137d957765fe2c9afba4ad0d014c 100644
+--- a/src/util/util.h
++++ b/src/util/util.h
+@@ -484,6 +484,12 @@ errno_t sss_filter_sanitize(TALLOC_CTX *mem_ctx,
+                             const char *input,
+                             char **sanitized);
+ 
++errno_t sss_filter_sanitize_for_dom(TALLOC_CTX *mem_ctx,
++                                    const char *input,
++                                    struct sss_domain_info *dom,
++                                    char **sanitized,
++                                    char **lc_sanitized);
++
+ char *
+ sss_escape_ip_address(TALLOC_CTX *mem_ctx, int family, const char *addr);
+ 
+-- 
+1.8.4.2
+
diff --git a/SOURCES/0040-Add-new-option-ldap_group_type.patch b/SOURCES/0040-Add-new-option-ldap_group_type.patch
new file mode 100644
index 0000000..0f0f27f
--- /dev/null
+++ b/SOURCES/0040-Add-new-option-ldap_group_type.patch
@@ -0,0 +1,177 @@
+From fd56e9302454869c636c2e40322eec52391b4c4f Mon Sep 17 00:00:00 2001
+From: Sumit Bose <sbose@redhat.com>
+Date: Mon, 9 Dec 2013 12:17:43 +0100
+Subject: [PATCH 40/41] Add new option ldap_group_type
+
+---
+ src/config/SSSDConfig/__init__.py.in     |  1 +
+ src/config/etc/sssd.api.d/sssd-ad.conf   |  1 +
+ src/config/etc/sssd.api.d/sssd-ipa.conf  |  1 +
+ src/config/etc/sssd.api.d/sssd-ldap.conf |  1 +
+ src/db/sysdb.h                           |  1 +
+ src/man/sssd-ldap.5.xml                  | 21 +++++++++++++++++++++
+ src/providers/ad/ad_opts.h               |  1 +
+ src/providers/ipa/ipa_opts.h             |  1 +
+ src/providers/ldap/ldap_opts.h           |  3 +++
+ src/providers/ldap/sdap.h                |  1 +
+ 10 files changed, 32 insertions(+)
+
+diff --git a/src/config/SSSDConfig/__init__.py.in b/src/config/SSSDConfig/__init__.py.in
+index af5903c65e05411d5773f1f9b1f742fdb832433c..8563a91e7afe680edfa0b9dd951ac7ab5a0fd3b0 100644
+--- a/src/config/SSSDConfig/__init__.py.in
++++ b/src/config/SSSDConfig/__init__.py.in
+@@ -284,6 +284,7 @@ option_strings = {
+     'ldap_group_uuid' : _('Group UUID attribute'),
+     'ldap_group_objectsid' : _("objectSID attribute"),
+     'ldap_group_modify_timestamp' : _('Modification time attribute for groups'),
++    'ldap_group_type' : _('Type of the group and other flags'),
+     #replaced by ldap_entry_usn# 'ldap_group_entry_usn' : _('entryUSN attribute'),
+     'ldap_group_nesting_level' : _('Maximum nesting level SSSd will follow'),
+ 
+diff --git a/src/config/etc/sssd.api.d/sssd-ad.conf b/src/config/etc/sssd.api.d/sssd-ad.conf
+index 00e8968d2b6dab33a39005f11a497cb3e2185302..6b136f2ec88614092cf1ceb4e2cea79db064d468 100644
+--- a/src/config/etc/sssd.api.d/sssd-ad.conf
++++ b/src/config/etc/sssd.api.d/sssd-ad.conf
+@@ -91,6 +91,7 @@ ldap_group_uuid = str, None, false
+ ldap_group_objectsid = str, None, false
+ ldap_group_modify_timestamp = str, None, false
+ ldap_group_entry_usn = str, None, false
++ldap_group_type = int, None, false
+ ldap_force_upper_case_realm = bool, None, false
+ ldap_group_nesting_level = int, None, false
+ ldap_netgroup_search_base = str, None, false
+diff --git a/src/config/etc/sssd.api.d/sssd-ipa.conf b/src/config/etc/sssd.api.d/sssd-ipa.conf
+index bc14fbe3d4153bd7a7ca4ffe0351edf0b8c02ee4..a94b5f09b073c050bff597d66c8164e4f38a9bfe 100644
+--- a/src/config/etc/sssd.api.d/sssd-ipa.conf
++++ b/src/config/etc/sssd.api.d/sssd-ipa.conf
+@@ -98,6 +98,7 @@ ldap_group_uuid = str, None, false
+ ldap_group_objectsid = str, None, false
+ ldap_group_modify_timestamp = str, None, false
+ ldap_group_entry_usn = str, None, false
++ldap_group_type = int, None, false
+ ldap_force_upper_case_realm = bool, None, false
+ ldap_group_nesting_level = int, None, false
+ ldap_netgroup_search_base = str, None, false
+diff --git a/src/config/etc/sssd.api.d/sssd-ldap.conf b/src/config/etc/sssd.api.d/sssd-ldap.conf
+index eb239664c49e9d516468c184dfeac190ecf8ddd8..4f5a06800d4ba4dacea08285b9db3abdc44df8f3 100644
+--- a/src/config/etc/sssd.api.d/sssd-ldap.conf
++++ b/src/config/etc/sssd.api.d/sssd-ldap.conf
+@@ -93,6 +93,7 @@ ldap_group_uuid = str, None, false
+ ldap_group_objectsid = str, None, false
+ ldap_group_modify_timestamp = str, None, false
+ ldap_group_entry_usn = str, None, false
++ldap_group_type = int, None, false
+ ldap_group_nesting_level = int, None, false
+ ldap_force_upper_case_realm = bool, None, false
+ ldap_netgroup_search_base = str, None, false
+diff --git a/src/db/sysdb.h b/src/db/sysdb.h
+index f1ed8158ccff70f85940d63f247e23451c22c30f..9bcd7be0960fcfa390fb9150594ea84880a14eea 100644
+--- a/src/db/sysdb.h
++++ b/src/db/sysdb.h
+@@ -76,6 +76,7 @@
+ #define SYSDB_POSIX "isPosix"
+ #define SYSDB_USER_CATEGORY "userCategory"
+ #define SYSDB_HOST_CATEGORY "hostCategory"
++#define SYSDB_GROUP_TYPE "groupType"
+ 
+ #define SYSDB_GECOS "gecos"
+ #define SYSDB_LAST_LOGIN "lastLogin"
+diff --git a/src/man/sssd-ldap.5.xml b/src/man/sssd-ldap.5.xml
+index efe22c9d22adccb244fe99603a74eb93dbddea7f..cc58544c38e8ffa779f0a1b22a69caaf3f193ce1 100644
+--- a/src/man/sssd-ldap.5.xml
++++ b/src/man/sssd-ldap.5.xml
+@@ -849,6 +849,27 @@
+                 </varlistentry>
+ 
+                 <varlistentry>
++                    <term>ldap_group_type (integer)</term>
++                    <listitem>
++                        <para>
++                            The LDAP attribute that contains an integer value
++                            indicating the type of the group and maybe other
++                            flags.
++                        </para>
++                        <para>
++                            This attribute is currently only used by the AD
++                            provider to determine if a group is a domain local
++                            groups and has to be filtered out for trusted
++                            domains.
++                        </para>
++                        <para>
++                            Default: groupType in the AD provider, othewise not
++                            set
++                        </para>
++                    </listitem>
++                </varlistentry>
++
++                <varlistentry>
+                     <term>ldap_group_nesting_level (integer)</term>
+                     <listitem>
+                         <para>
+diff --git a/src/providers/ad/ad_opts.h b/src/providers/ad/ad_opts.h
+index 5b7b1c89f5f45d7cc744a955e6378390948a99fd..0deeec99a9c1944301b80d1f25713b5d0504e88c 100644
+--- a/src/providers/ad/ad_opts.h
++++ b/src/providers/ad/ad_opts.h
+@@ -209,6 +209,7 @@ struct sdap_attr_map ad_2008r2_group_map[] = {
+     { "ldap_group_objectsid", "objectSID", SYSDB_SID, NULL },
+     { "ldap_group_modify_timestamp", "whenChanged", SYSDB_ORIG_MODSTAMP, NULL },
+     { "ldap_group_entry_usn", SDAP_AD_USN, SYSDB_USN, NULL },
++    { "ldap_group_type", "groupType", SYSDB_GROUP_TYPE, NULL },
+     SDAP_ATTR_MAP_TERMINATOR
+ };
+ 
+diff --git a/src/providers/ipa/ipa_opts.h b/src/providers/ipa/ipa_opts.h
+index 5ec36c550b166e07a9ed2f2c31474c55d0ecdaee..27dc3e2f977383836c18cb824abceb03c9e9056c 100644
+--- a/src/providers/ipa/ipa_opts.h
++++ b/src/providers/ipa/ipa_opts.h
+@@ -209,6 +209,7 @@ struct sdap_attr_map ipa_group_map[] = {
+     { "ldap_group_objectsid", "ipaNTSecurityIdentifier", SYSDB_SID_STR, NULL },
+     { "ldap_group_modify_timestamp", "modifyTimestamp", SYSDB_ORIG_MODSTAMP, NULL },
+     { "ldap_group_entry_usn", NULL, SYSDB_USN, NULL },
++    { "ldap_group_type", NULL, SYSDB_GROUP_TYPE, NULL },
+     SDAP_ATTR_MAP_TERMINATOR
+ };
+ 
+diff --git a/src/providers/ldap/ldap_opts.h b/src/providers/ldap/ldap_opts.h
+index a6c821f3ac3ad951a3b45168b298b96fefb96b60..9593dfd30a81db60b7358c66975871507340aa4b 100644
+--- a/src/providers/ldap/ldap_opts.h
++++ b/src/providers/ldap/ldap_opts.h
+@@ -187,6 +187,7 @@ struct sdap_attr_map rfc2307_group_map[] = {
+     { "ldap_group_objectsid", NULL, SYSDB_SID, NULL },
+     { "ldap_group_modify_timestamp", "modifyTimestamp", SYSDB_ORIG_MODSTAMP, NULL },
+     { "ldap_group_entry_usn", NULL, SYSDB_USN, NULL },
++    { "ldap_group_type", NULL, SYSDB_GROUP_TYPE, NULL },
+     SDAP_ATTR_MAP_TERMINATOR
+ };
+ 
+@@ -241,6 +242,7 @@ struct sdap_attr_map rfc2307bis_group_map[] = {
+     { "ldap_group_objectsid", NULL, SYSDB_SID, NULL },
+     { "ldap_group_modify_timestamp", "modifyTimestamp", SYSDB_ORIG_MODSTAMP, NULL },
+     { "ldap_group_entry_usn", NULL, SYSDB_USN, NULL },
++    { "ldap_group_type", NULL, SYSDB_GROUP_TYPE, NULL },
+     SDAP_ATTR_MAP_TERMINATOR
+ };
+ 
+@@ -293,6 +295,7 @@ struct sdap_attr_map gen_ad2008r2_group_map[] = {
+     { "ldap_group_objectsid", "objectSID", SYSDB_SID, NULL },
+     { "ldap_group_modify_timestamp", "whenChanged", SYSDB_ORIG_MODSTAMP, NULL },
+     { "ldap_group_entry_usn", SDAP_AD_USN, SYSDB_USN, NULL },
++    { "ldap_group_type", NULL, SYSDB_GROUP_TYPE, NULL },
+     SDAP_ATTR_MAP_TERMINATOR
+ };
+ 
+diff --git a/src/providers/ldap/sdap.h b/src/providers/ldap/sdap.h
+index a7ea94eb810a96b61862bd8cc6fcd800c3e8e0cb..d408be0a65cdd840d8379b7af4c0ab1e67ed3f5c 100644
+--- a/src/providers/ldap/sdap.h
++++ b/src/providers/ldap/sdap.h
+@@ -296,6 +296,7 @@ enum sdap_group_attrs {
+     SDAP_AT_GROUP_OBJECTSID,
+     SDAP_AT_GROUP_MODSTAMP,
+     SDAP_AT_GROUP_USN,
++    SDAP_AT_GROUP_TYPE,
+ 
+     SDAP_OPTS_GROUP /* attrs counter */
+ };
+-- 
+1.8.4.2
+
diff --git a/SOURCES/0041-Add-sysdb_attrs_get_int32_t.patch b/SOURCES/0041-Add-sysdb_attrs_get_int32_t.patch
new file mode 100644
index 0000000..04d3a63
--- /dev/null
+++ b/SOURCES/0041-Add-sysdb_attrs_get_int32_t.patch
@@ -0,0 +1,63 @@
+From 91c41b25f3d2e5a6074d1dd73c3355f9159d2cae Mon Sep 17 00:00:00 2001
+From: Sumit Bose <sbose@redhat.com>
+Date: Tue, 10 Dec 2013 10:14:02 +0100
+Subject: [PATCH 41/41] Add sysdb_attrs_get_int32_t
+
+---
+ src/db/sysdb.c | 26 ++++++++++++++++++++++++++
+ src/db/sysdb.h |  2 ++
+ 2 files changed, 28 insertions(+)
+
+diff --git a/src/db/sysdb.c b/src/db/sysdb.c
+index 2a4be58008fc1164765db26aaba3886071448d30..0e07ed60858298a1ac85d06146ccb98c5899a705 100644
+--- a/src/db/sysdb.c
++++ b/src/db/sysdb.c
+@@ -366,6 +366,32 @@ int sysdb_attrs_get_string(struct sysdb_attrs *attrs, const char *name,
+     return EOK;
+ }
+ 
++int sysdb_attrs_get_int32_t(struct sysdb_attrs *attrs, const char *name,
++                             int32_t *value)
++{
++    struct ldb_message_element *el;
++    int ret;
++    char *endptr;
++    int32_t val;
++
++    ret = sysdb_attrs_get_el_ext(attrs, name, false, &el);
++    if (ret) {
++        return ret;
++    }
++
++    if (el->num_values != 1) {
++        return ERANGE;
++    }
++
++    errno = 0;
++    val = strtoint32((const char *) el->values[0].data, &endptr, 10);
++    if (errno != 0) return errno;
++    if (*endptr) return EINVAL;
++
++    *value = val;
++    return EOK;
++}
++
+ int sysdb_attrs_get_uint32_t(struct sysdb_attrs *attrs, const char *name,
+                              uint32_t *value)
+ {
+diff --git a/src/db/sysdb.h b/src/db/sysdb.h
+index 9bcd7be0960fcfa390fb9150594ea84880a14eea..255a135f0cad788e4c952b86fe24ca10f5e63732 100644
+--- a/src/db/sysdb.h
++++ b/src/db/sysdb.h
+@@ -294,6 +294,8 @@ errno_t sysdb_attrs_get_bool(struct sysdb_attrs *attrs, const char *name,
+                              bool *value);
+ int sysdb_attrs_get_uint16_t(struct sysdb_attrs *attrs, const char *name,
+                              uint16_t *value);
++int sysdb_attrs_get_int32_t(struct sysdb_attrs *attrs, const char *name,
++                             int32_t *value);
+ int sysdb_attrs_get_uint32_t(struct sysdb_attrs *attrs, const char *name,
+                              uint32_t *value);
+ 
+-- 
+1.8.4.2
+
diff --git a/SOURCES/0042-IPA-fix-for-recent-AD-group-membership-changes.patch b/SOURCES/0042-IPA-fix-for-recent-AD-group-membership-changes.patch
new file mode 100644
index 0000000..fa294ce
--- /dev/null
+++ b/SOURCES/0042-IPA-fix-for-recent-AD-group-membership-changes.patch
@@ -0,0 +1,24 @@
+From 32388a70e254021473cca2caf8fcc77fdf7e7635 Mon Sep 17 00:00:00 2001
+From: Sumit Bose <sbose@redhat.com>
+Date: Tue, 7 Jan 2014 13:04:58 +0100
+Subject: [PATCH 42/43] IPA: fix for recent AD group membership changes
+
+---
+ src/providers/ipa/ipa_subdomains.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+diff --git a/src/providers/ipa/ipa_subdomains.c b/src/providers/ipa/ipa_subdomains.c
+index 2d28d7cd0538b204eb2818e71e029dec19456a1c..9efbd725f1102d34af2107801286bca1c6412c19 100644
+--- a/src/providers/ipa/ipa_subdomains.c
++++ b/src/providers/ipa/ipa_subdomains.c
+@@ -192,6 +192,8 @@ ipa_ad_ctx_new(struct be_ctx *be_ctx,
+         return ret;
+     }
+
++    sdom->pvt = ad_id_ctx;
++
+     /* Set up the ID mapping object */
+     ad_id_ctx->sdap_id_ctx->opts->idmap_ctx =
+         id_ctx->sdap_id_ctx->opts->idmap_ctx;
+--
+1.8.4.2
diff --git a/SOURCES/0043-LDAP-Fix-typo-and-use-the-right-attribute-map.patch b/SOURCES/0043-LDAP-Fix-typo-and-use-the-right-attribute-map.patch
new file mode 100644
index 0000000..1f5f205
--- /dev/null
+++ b/SOURCES/0043-LDAP-Fix-typo-and-use-the-right-attribute-map.patch
@@ -0,0 +1,29 @@
+From 147af349a5642f85689cbfe68136f0e01706330e Mon Sep 17 00:00:00 2001
+From: Jakub Hrozek <jhrozek@redhat.com>
+Date: Wed, 8 Jan 2014 08:11:46 +0100
+Subject: [PATCH 43/43] LDAP: Fix typo and use the right attribute map
+
+https://fedorahosted.org/sssd/ticket/2191
+
+There was a copy-n-paste bug in the code that resulted in using a wrong
+attribute map. This could lead to the primary name not being selected
+correctly.
+---
+ src/providers/ldap/sdap.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/src/providers/ldap/sdap.c b/src/providers/ldap/sdap.c
+index 078326ad3614ed2cd41659ea279642a46a0e24e1..ddcf199b61311b69fde54c3ee25f2338ceb05576 100644
+--- a/src/providers/ldap/sdap.c
++++ b/src/providers/ldap/sdap.c
+@@ -1246,7 +1246,7 @@ errno_t sdap_get_user_primary_name(TALLOC_CTX *memctx,
+                                    const char **_user_name)
+ {
+     return sdap_get_primary_name(memctx,
+-                                 opts->group_map[SDAP_AT_USER_NAME].name,
++                                 opts->user_map[SDAP_AT_USER_NAME].name,
+                                  attrs, dom, _user_name);
+ }
+
+--
+1.8.4.2
diff --git a/SOURCES/0044-pac-fix-double-free.patch b/SOURCES/0044-pac-fix-double-free.patch
new file mode 100644
index 0000000..afc6cf0
--- /dev/null
+++ b/SOURCES/0044-pac-fix-double-free.patch
@@ -0,0 +1,69 @@
+From 47bc6c387ab1d3f835167c528bb57f688080af1a Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pavel=20B=C5=99ezina?= <pbrezina@redhat.com>
+Date: Mon, 11 Nov 2013 12:47:53 +0100
+Subject: [PATCH 44/47] pac: fix double free
+
+---
+ src/responder/pac/pacsrv_utils.c | 14 ++++++--------
+ 1 file changed, 6 insertions(+), 8 deletions(-)
+
+diff --git a/src/responder/pac/pacsrv_utils.c b/src/responder/pac/pacsrv_utils.c
+index 6a6ea2e357483c68533c501b93c16383ee644048..a82320fcae3b0494e7b6b322428e3a17ed729b4a 100644
+--- a/src/responder/pac/pacsrv_utils.c
++++ b/src/responder/pac/pacsrv_utils.c
+@@ -74,6 +74,7 @@ errno_t get_sids_from_pac(TALLOC_CTX *mem_ctx,
+     struct sss_domain_info *user_dom;
+     struct sss_domain_info *group_dom;
+     char *sid_str = NULL;
++    char *msid_str = NULL;
+     char *user_dom_sid_str = NULL;
+     size_t user_dom_sid_str_len;
+     enum idmap_error_code err;
+@@ -231,24 +232,22 @@ errno_t get_sids_from_pac(TALLOC_CTX *mem_ctx,
+ 
+     }
+ 
+-    talloc_zfree(sid_str);
+-
+     for(s = 0; s < info3->sidcount; s++) {
+         err = sss_idmap_smb_sid_to_sid(pac_ctx->idmap_ctx, info3->sids[s].sid,
+-                                       &sid_str);
++                                       &msid_str);
+         if (err != IDMAP_SUCCESS) {
+             DEBUG(SSSDBG_OP_FAILURE, ("sss_idmap_smb_sid_to_sid failed.\n"));
+             ret = EFAULT;
+             goto done;
+         }
+ 
+-        key.str = sid_str;
++        key.str = msid_str;
+         value.ul = 0;
+ 
+-        ret = responder_get_domain_by_id(pac_ctx->rctx, sid_str, &group_dom);
++        ret = responder_get_domain_by_id(pac_ctx->rctx, msid_str, &group_dom);
+         if (ret == EOK) {
+             ret = sysdb_search_object_by_sid(mem_ctx, group_dom->sysdb,
+-                                             group_dom, sid_str, NULL, &msg);
++                                             group_dom, msid_str, NULL, &msg);
+             if (ret == EOK && msg->count == 1 ) {
+                 value.ul = ldb_msg_find_attr_as_uint64(msg->msgs[0],
+                                                        SYSDB_GIDNUM, 0);
+@@ -257,14 +256,13 @@ errno_t get_sids_from_pac(TALLOC_CTX *mem_ctx,
+         }
+ 
+         ret = hash_enter(sid_table, &key, &value);
++        sss_idmap_free_sid(pac_ctx->idmap_ctx, msid_str);
+         if (ret != HASH_SUCCESS) {
+             DEBUG(SSSDBG_OP_FAILURE, ("hash_enter failed [%d][%s].\n",
+                                       ret, hash_error_string(ret)));
+             ret = EIO;
+             goto done;
+         }
+-
+-        sss_idmap_free_sid(pac_ctx->idmap_ctx, sid_str);
+     }
+ 
+     ret = EOK;
+-- 
+1.8.4.2
+
diff --git a/SOURCES/0045-pac-fix-potential-memory-leaks.patch b/SOURCES/0045-pac-fix-potential-memory-leaks.patch
new file mode 100644
index 0000000..3658ae5
--- /dev/null
+++ b/SOURCES/0045-pac-fix-potential-memory-leaks.patch
@@ -0,0 +1,65 @@
+From 7e60e5991e97443044ae9c097131c84e9538cc42 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pavel=20B=C5=99ezina?= <pbrezina@redhat.com>
+Date: Tue, 12 Nov 2013 13:41:49 +0100
+Subject: [PATCH 45/47] pac: fix potential memory leaks
+
+---
+ src/responder/pac/pacsrv_utils.c | 10 +++++-----
+ 1 file changed, 5 insertions(+), 5 deletions(-)
+
+diff --git a/src/responder/pac/pacsrv_utils.c b/src/responder/pac/pacsrv_utils.c
+index a82320fcae3b0494e7b6b322428e3a17ed729b4a..53edcc286119aa4a315285827da96e5d157afec9 100644
+--- a/src/responder/pac/pacsrv_utils.c
++++ b/src/responder/pac/pacsrv_utils.c
+@@ -82,7 +82,7 @@ errno_t get_sids_from_pac(TALLOC_CTX *mem_ctx,
+     hash_key_t key;
+     hash_value_t value;
+     char *rid_start;
+-    struct ldb_result *msg;
++    struct ldb_result *msg = NULL;
+     char *user_sid_str = NULL;
+     char *primary_group_sid_str = NULL;
+ 
+@@ -154,8 +154,8 @@ errno_t get_sids_from_pac(TALLOC_CTX *mem_ctx,
+                                      sid_str, NULL, &msg);
+     if (ret == EOK && msg->count == 1) {
+         value.ul = ldb_msg_find_attr_as_uint64(msg->msgs[0], SYSDB_UIDNUM, 0);
+-        talloc_free(msg);
+     }
++    talloc_zfree(msg);
+ 
+     ret = hash_enter(sid_table, &key, &value);
+     if (ret != HASH_SUCCESS) {
+@@ -189,8 +189,8 @@ errno_t get_sids_from_pac(TALLOC_CTX *mem_ctx,
+                                      sid_str, NULL, &msg);
+     if (ret == EOK && msg->count == 1) {
+         value.ul = ldb_msg_find_attr_as_uint64(msg->msgs[0], SYSDB_GIDNUM, 0);
+-        talloc_free(msg);
+     }
++    talloc_zfree(msg);
+ 
+     ret = hash_enter(sid_table, &key, &value);
+     if (ret != HASH_SUCCESS) {
+@@ -219,8 +219,8 @@ errno_t get_sids_from_pac(TALLOC_CTX *mem_ctx,
+         if (ret == EOK && msg->count == 1) {
+             value.ul = ldb_msg_find_attr_as_uint64(msg->msgs[0],
+                                                    SYSDB_GIDNUM, 0);
+-            talloc_free(msg);
+         }
++        talloc_zfree(msg);
+ 
+         ret = hash_enter(sid_table, &key, &value);
+         if (ret != HASH_SUCCESS) {
+@@ -251,8 +251,8 @@ errno_t get_sids_from_pac(TALLOC_CTX *mem_ctx,
+             if (ret == EOK && msg->count == 1 ) {
+                 value.ul = ldb_msg_find_attr_as_uint64(msg->msgs[0],
+                                                        SYSDB_GIDNUM, 0);
+-                talloc_free(msg);
+             }
++            talloc_zfree(msg);
+         }
+ 
+         ret = hash_enter(sid_table, &key, &value);
+-- 
+1.8.4.2
+
diff --git a/SOURCES/0046-responder-Set-forest-attribute-in-AD-domains.patch b/SOURCES/0046-responder-Set-forest-attribute-in-AD-domains.patch
new file mode 100644
index 0000000..54068b1
--- /dev/null
+++ b/SOURCES/0046-responder-Set-forest-attribute-in-AD-domains.patch
@@ -0,0 +1,321 @@
+From 6ac0feca0cdc66fc8d8a612e25d37a49d27c0233 Mon Sep 17 00:00:00 2001
+From: Pavel Reichl <pavel.reichl@redhat.com>
+Date: Tue, 17 Dec 2013 17:32:04 +0000
+Subject: [PATCH 46/47] responder: Set forest attribute in AD domains
+
+Resolves:
+https://fedorahosted.org/sssd/ticket/2160
+---
+ src/db/sysdb.h                     |  3 ++-
+ src/db/sysdb_subdomains.c          | 35 ++++++++++++++++++++++++++++-
+ src/providers/ad/ad_domain_info.c  | 46 +++++++++++++++++++++++++++++++-------
+ src/providers/ad/ad_domain_info.h  |  3 ++-
+ src/providers/ad/ad_id.c           |  5 +++--
+ src/providers/ad/ad_subdomains.c   |  9 +++++---
+ src/providers/ipa/ipa_subdomains.c |  2 +-
+ src/providers/ldap/sdap_access.c   |  2 +-
+ 8 files changed, 87 insertions(+), 18 deletions(-)
+
+diff --git a/src/db/sysdb.h b/src/db/sysdb.h
+index 255a135f0cad788e4c952b86fe24ca10f5e63732..9677294b22e47f5169d7631673beec2dbc6117ad 100644
+--- a/src/db/sysdb.h
++++ b/src/db/sysdb.h
+@@ -388,7 +388,8 @@ errno_t sysdb_update_subdomains(struct sss_domain_info *domain);
+ errno_t sysdb_master_domain_update(struct sss_domain_info *domain);
+ 
+ errno_t sysdb_master_domain_add_info(struct sss_domain_info *domain,
+-                                     const char *flat, const char *id);
++                                     const char *flat, const char *id,
++                                     const char* forest);
+ 
+ errno_t sysdb_subdomain_delete(struct sysdb_ctx *sysdb, const char *name);
+ 
+diff --git a/src/db/sysdb_subdomains.c b/src/db/sysdb_subdomains.c
+index 43c75799cdc2856916b2dc95c3a544ef99b56081..9c2926c00b0cc08cb8e317ae838e26c82506ee37 100644
+--- a/src/db/sysdb_subdomains.c
++++ b/src/db/sysdb_subdomains.c
+@@ -208,6 +208,7 @@ errno_t sysdb_master_domain_update(struct sss_domain_info *domain)
+                            SYSDB_SUBDOMAIN_REALM,
+                            SYSDB_SUBDOMAIN_FLAT,
+                            SYSDB_SUBDOMAIN_ID,
++                           SYSDB_SUBDOMAIN_FOREST,
+                            NULL};
+ 
+     tmp_ctx = talloc_new(NULL);
+@@ -278,13 +279,27 @@ errno_t sysdb_master_domain_update(struct sss_domain_info *domain)
+         }
+     }
+ 
++    tmp_str = ldb_msg_find_attr_as_string(res->msgs[0], SYSDB_SUBDOMAIN_FOREST,
++                                          NULL);
++    if (tmp_str != NULL &&
++        (domain->forest == NULL ||
++         strcasecmp(tmp_str, domain->forest) != 0)) {
++        talloc_free(domain->forest);
++        domain->forest = talloc_strdup(domain, tmp_str);
++        if (domain->forest == NULL) {
++            ret = ENOMEM;
++            goto done;
++        }
++    }
++
+ done:
+     talloc_free(tmp_ctx);
+     return ret;
+ }
+ 
+ errno_t sysdb_master_domain_add_info(struct sss_domain_info *domain,
+-                                     const char *flat, const char *id)
++                                     const char *flat, const char *id,
++                                     const char* forest)
+ {
+     TALLOC_CTX *tmp_ctx;
+     struct ldb_message *msg;
+@@ -345,6 +360,24 @@ errno_t sysdb_master_domain_add_info(struct sss_domain_info *domain,
+         do_update = true;
+     }
+ 
++   if (forest != NULL && (domain->forest == NULL ||
++                       strcmp(domain->forest, forest) != 0)) {
++        ret = ldb_msg_add_empty(msg, SYSDB_SUBDOMAIN_FOREST,
++                                LDB_FLAG_MOD_REPLACE, NULL);
++        if (ret != LDB_SUCCESS) {
++            ret = sysdb_error_to_errno(ret);
++            goto done;
++        }
++
++        ret = ldb_msg_add_string(msg, SYSDB_SUBDOMAIN_FOREST, forest);
++        if (ret != LDB_SUCCESS) {
++            ret = sysdb_error_to_errno(ret);
++            goto done;
++        }
++
++        do_update = true;
++    }
++
+     if (do_update == false) {
+         ret = EOK;
+         goto done;
+diff --git a/src/providers/ad/ad_domain_info.c b/src/providers/ad/ad_domain_info.c
+index eff2034d12261510ed7535dee7098a6e68f1f2c2..5475c5bc7ec74e81080566c6fbd6919c54a60f40 100644
+--- a/src/providers/ad/ad_domain_info.c
++++ b/src/providers/ad/ad_domain_info.c
+@@ -41,9 +41,9 @@
+ #define MASTER_DOMAIN_SID_FILTER "objectclass=domain"
+ 
+ static errno_t
+-netlogon_get_flat_name(TALLOC_CTX *mem_ctx,
+-                       struct sysdb_attrs *reply,
+-                       char **_flat_name)
++netlogon_get_domain_info(TALLOC_CTX *mem_ctx,
++                         struct sysdb_attrs *reply,
++                         char **_flat_name, char **_forest)
+ {
+     errno_t ret;
+     struct ldb_message_element *el;
+@@ -52,6 +52,7 @@ netlogon_get_flat_name(TALLOC_CTX *mem_ctx,
+     enum ndr_err_code ndr_err;
+     struct netlogon_samlogon_response response;
+     const char *flat_name;
++    const char *forest;
+ 
+     ret = sysdb_attrs_get_el(reply, AD_AT_NETLOGON, &el);
+     if (ret != EOK) {
+@@ -92,11 +93,13 @@ netlogon_get_flat_name(TALLOC_CTX *mem_ctx,
+         goto done;
+     }
+ 
++    /* get flat name */
+     if (response.data.nt5_ex.domain_name != NULL &&
+         *response.data.nt5_ex.domain_name != '\0') {
+         flat_name = response.data.nt5_ex.domain_name;
+     } else {
+-        DEBUG(SSSDBG_MINOR_FAILURE, ("No netlogon data available\n"));
++        DEBUG(SSSDBG_MINOR_FAILURE,
++              ("No netlogon domain name data available\n"));
+         ret = ENOENT;
+         goto done;
+     }
+@@ -107,6 +110,24 @@ netlogon_get_flat_name(TALLOC_CTX *mem_ctx,
+         ret = ENOMEM;
+         goto done;
+     }
++
++    /* get forest */
++    if (response.data.nt5_ex.forest != NULL &&
++        *response.data.nt5_ex.forest != '\0') {
++        forest = response.data.nt5_ex.forest;
++    } else {
++        DEBUG(SSSDBG_MINOR_FAILURE, ("No netlogon forest data available\n"));
++        ret = ENOENT;
++        goto done;
++    }
++
++    *_forest = talloc_strdup(mem_ctx, forest);
++    if (*_forest == NULL) {
++        DEBUG(SSSDBG_OP_FAILURE, ("talloc_strdup failed.\n"));
++        ret = ENOMEM;
++        goto done;
++    }
++
+     ret = EOK;
+ done:
+     talloc_free(ndr_pull);
+@@ -124,6 +145,7 @@ struct ad_master_domain_state {
+     int base_iter;
+ 
+     char *flat;
++    char *forest;
+     char *sid;
+ };
+ 
+@@ -338,14 +360,17 @@ ad_master_domain_netlogon_done(struct tevent_req *subreq)
+ 
+     /* Exactly one flat name. Carry on */
+ 
+-    ret = netlogon_get_flat_name(state, reply[0], &state->flat);
++    ret = netlogon_get_domain_info(state, reply[0], &state->flat,
++                                   &state->forest);
+     if (ret != EOK) {
+-        DEBUG(SSSDBG_MINOR_FAILURE, ("Could not get the flat name\n"));
++        DEBUG(SSSDBG_MINOR_FAILURE,
++              ("Could not get the flat name or forest\n"));
+         /* Not fatal. Just quit. */
+         goto done;
+     }
+-
+     DEBUG(SSSDBG_TRACE_FUNC, ("Found flat name [%s].\n", state->flat));
++    DEBUG(SSSDBG_TRACE_FUNC, ("Found forest [%s].\n", state->forest));
++
+ done:
+     tevent_req_done(req);
+     return;
+@@ -355,7 +380,8 @@ errno_t
+ ad_master_domain_recv(struct tevent_req *req,
+                       TALLOC_CTX *mem_ctx,
+                       char **_flat,
+-                      char **_id)
++                      char **_id,
++                      char **_forest)
+ {
+     struct ad_master_domain_state *state = tevent_req_data(req,
+                                               struct ad_master_domain_state);
+@@ -366,6 +392,10 @@ ad_master_domain_recv(struct tevent_req *req,
+         *_flat = talloc_steal(mem_ctx, state->flat);
+     }
+ 
++    if (_forest) {
++        *_forest = talloc_steal(mem_ctx, state->forest);
++    }
++
+     if (_id) {
+         *_id = talloc_steal(mem_ctx, state->sid);
+     }
+diff --git a/src/providers/ad/ad_domain_info.h b/src/providers/ad/ad_domain_info.h
+index d21706396034509a498391e666e03a8e2eda8e08..d3a6416cebd07b524aceedcb63a18c4467e3dc4e 100644
+--- a/src/providers/ad/ad_domain_info.h
++++ b/src/providers/ad/ad_domain_info.h
+@@ -36,6 +36,7 @@ errno_t
+ ad_master_domain_recv(struct tevent_req *req,
+                       TALLOC_CTX *mem_ctx,
+                       char **_flat,
+-                      char **_id);
++                      char **_id,
++                      char **_forest);
+ 
+ #endif /* _AD_MASTER_DOMAIN_H_ */
+diff --git a/src/providers/ad/ad_id.c b/src/providers/ad/ad_id.c
+index e47c41863a14eed695907548d64f4559fbae629d..44bfa00986b6c0ebfa65dd7b83dd45eb64b87946 100644
+--- a/src/providers/ad/ad_id.c
++++ b/src/providers/ad/ad_id.c
+@@ -519,9 +519,10 @@ ad_enumeration_master_done(struct tevent_req *subreq)
+                                                 struct ad_enumeration_state);
+     char *flat_name;
+     char *master_sid;
++    char *forest;
+ 
+     ret = ad_master_domain_recv(subreq, state,
+-                                &flat_name, &master_sid);
++                                &flat_name, &master_sid, &forest);
+     talloc_zfree(subreq);
+     if (ret != EOK) {
+         DEBUG(SSSDBG_OP_FAILURE, ("Cannot retrieve master domain info\n"));
+@@ -530,7 +531,7 @@ ad_enumeration_master_done(struct tevent_req *subreq)
+     }
+ 
+     ret = sysdb_master_domain_add_info(state->sdom->dom,
+-                                       flat_name, master_sid);
++                                       flat_name, master_sid, forest);
+     if (ret != EOK) {
+         DEBUG(SSSDBG_OP_FAILURE, ("Cannot save master domain info\n"));
+         tevent_req_error(req, ret);
+diff --git a/src/providers/ad/ad_subdomains.c b/src/providers/ad/ad_subdomains.c
+index e438a688c364084a3f2bbca338a39d61aa86b5d6..62c3e16d0d3323a32848b4fbf54d2a151c16f64c 100644
+--- a/src/providers/ad/ad_subdomains.c
++++ b/src/providers/ad/ad_subdomains.c
+@@ -85,6 +85,7 @@ struct ad_subdomains_req_ctx {
+ 
+     char *master_sid;
+     char *flat_name;
++    char *forest;
+ };
+ 
+ static errno_t
+@@ -294,7 +295,7 @@ ad_subdom_store(struct ad_subdomains_ctx *ctx,
+ 
+     /* AD subdomains are currently all mpg and do not enumerate */
+     ret = sysdb_subdomain_store(domain->sysdb, name, realm, flat, sid_str,
+-                                mpg, false, NULL);
++                                mpg, false, domain->forest);
+     if (ret != EOK) {
+         DEBUG(SSSDBG_OP_FAILURE, ("sysdb_subdomain_store failed.\n"));
+         goto done;
+@@ -539,7 +540,8 @@ static void ad_subdomains_master_dom_done(struct tevent_req *req)
+     ctx = tevent_req_callback_data(req, struct ad_subdomains_req_ctx);
+ 
+     ret = ad_master_domain_recv(req, ctx,
+-                                &ctx->flat_name, &ctx->master_sid);
++                                &ctx->flat_name, &ctx->master_sid,
++                                &ctx->forest);
+     talloc_zfree(req);
+     if (ret != EOK) {
+         DEBUG(SSSDBG_OP_FAILURE, ("Cannot retrieve master domain info\n"));
+@@ -547,7 +549,8 @@ static void ad_subdomains_master_dom_done(struct tevent_req *req)
+     }
+ 
+     ret = sysdb_master_domain_add_info(ctx->sd_ctx->be_ctx->domain,
+-                                       ctx->flat_name, ctx->master_sid);
++                                       ctx->flat_name, ctx->master_sid,
++                                       ctx->forest);
+     if (ret != EOK) {
+         DEBUG(SSSDBG_OP_FAILURE, ("Cannot save master domain info\n"));
+         goto done;
+diff --git a/src/providers/ipa/ipa_subdomains.c b/src/providers/ipa/ipa_subdomains.c
+index 9efbd725f1102d34af2107801286bca1c6412c19..d9c204451f1b734ee98ce4c48f3f139731e47dec 100644
+--- a/src/providers/ipa/ipa_subdomains.c
++++ b/src/providers/ipa/ipa_subdomains.c
+@@ -1076,7 +1076,7 @@ static void ipa_subdomains_handler_master_done(struct tevent_req *req)
+         }
+ 
+         ret = sysdb_master_domain_add_info(ctx->sd_ctx->be_ctx->domain,
+-                                           flat, id);
++                                           flat, id, NULL);
+     } else {
+         ctx->search_base_iter++;
+         ret = ipa_subdomains_handler_get(ctx, IPA_SUBDOMAINS_MASTER);
+diff --git a/src/providers/ldap/sdap_access.c b/src/providers/ldap/sdap_access.c
+index 6b387271a229668ddfa5d67143a585e667a16ddd..f0df24e7f3a855304b0cfd9d075ac67334f9bb1a 100644
+--- a/src/providers/ldap/sdap_access.c
++++ b/src/providers/ldap/sdap_access.c
+@@ -214,7 +214,7 @@ static void sdap_access_filter_done(struct tevent_req *subreq)
+     ret = sdap_access_filter_recv(subreq);
+     talloc_zfree(subreq);
+     if (ret != EOK) {
+-        DEBUG(1, ("Error retrieving access check result.\n"));
++        DEBUG(SSSDBG_CRIT_FAILURE, ("Error retrieving access check result.\n"));
+         tevent_req_error(req, ret);
+         return;
+     }
+-- 
+1.8.4.2
+
diff --git a/SOURCES/0047-LDAP-Add-a-new-error-code-for-malformed-access-contr.patch b/SOURCES/0047-LDAP-Add-a-new-error-code-for-malformed-access-contr.patch
new file mode 100644
index 0000000..706968d
--- /dev/null
+++ b/SOURCES/0047-LDAP-Add-a-new-error-code-for-malformed-access-contr.patch
@@ -0,0 +1,146 @@
+From 91ab35daf713e146dfae53a67f6b86b424c897d5 Mon Sep 17 00:00:00 2001
+From: Jakub Hrozek <jhrozek@redhat.com>
+Date: Wed, 8 Jan 2014 17:12:17 +0100
+Subject: [PATCH 47/47] LDAP: Add a new error code for malformed access control
+ filter
+
+https://fedorahosted.org/sssd/ticket/2164
+
+The patch adds a new error code and special cases the new code so that
+access is denied and a nicer log message is shown.
+---
+ src/providers/ldap/sdap_access.c              |  8 +++++++-
+ src/providers/ldap/sdap_async.c               | 12 ++++++------
+ src/providers/ldap/sdap_async_groups_ad.c     |  2 +-
+ src/providers/ldap/sdap_async_initgroups_ad.c |  4 ++--
+ src/util/util_errors.c                        |  1 +
+ src/util/util_errors.h                        |  1 +
+ 6 files changed, 18 insertions(+), 10 deletions(-)
+
+diff --git a/src/providers/ldap/sdap_access.c b/src/providers/ldap/sdap_access.c
+index f0df24e7f3a855304b0cfd9d075ac67334f9bb1a..29e83eb43cf78107e2075e1aa95211abac6d2df1 100644
+--- a/src/providers/ldap/sdap_access.c
++++ b/src/providers/ldap/sdap_access.c
+@@ -855,9 +855,15 @@ static void sdap_access_filter_get_access_done(struct tevent_req *subreq)
+             }
+         } else if (dp_error == DP_ERR_OFFLINE) {
+             ret = sdap_access_filter_decide_offline(req);
++        } else if (ret == ERR_INVALID_FILTER) {
++            sss_log(SSS_LOG_ERR,
++                    "Malformed access control filter [%s]\n", state->filter);
++            DEBUG(SSSDBG_CRIT_FAILURE,
++                ("Malformed access control filter [%s]\n", state->filter));
++            ret = ERR_ACCESS_DENIED;
+         } else {
+             DEBUG(1, ("sdap_get_generic_send() returned error [%d][%s]\n",
+-                      ret, strerror(ret)));
++                      ret, sss_strerror(ret)));
+         }
+ 
+         goto done;
+diff --git a/src/providers/ldap/sdap_async.c b/src/providers/ldap/sdap_async.c
+index e905d2dd6d539baadcd29aa0869ca04e845947e2..367007bde0011ed4de283b2a50b22538830a5275 100644
+--- a/src/providers/ldap/sdap_async.c
++++ b/src/providers/ldap/sdap_async.c
+@@ -1306,9 +1306,9 @@ static errno_t sdap_get_generic_ext_step(struct tevent_req *req)
+                 sss_log(SSS_LOG_ERR, "LDAP connection error, %s",
+                                      sss_ldap_err2string(lret));
+             }
+-        }
+-
+-        else {
++        } else if (lret == LDAP_FILTER_ERROR) {
++            ret = ERR_INVALID_FILTER;
++        } else {
+             ret = EIO;
+         }
+         goto done;
+@@ -1570,7 +1570,7 @@ static void sdap_get_generic_done(struct tevent_req *subreq)
+     talloc_zfree(subreq);
+     if (ret) {
+         DEBUG(4, ("sdap_get_generic_ext_recv failed [%d]: %s\n",
+-                  ret, strerror(ret)));
++                  ret, sss_strerror(ret)));
+         tevent_req_error(req, ret);
+         return;
+     }
+@@ -1790,7 +1790,7 @@ static void sdap_x_deref_search_done(struct tevent_req *subreq)
+     talloc_zfree(subreq);
+     if (ret) {
+         DEBUG(4, ("sdap_get_generic_ext_recv failed [%d]: %s\n",
+-                  ret, strerror(ret)));
++                  ret, sss_strerror(ret)));
+         tevent_req_error(req, ret);
+         return;
+     }
+@@ -2049,7 +2049,7 @@ static void sdap_asq_search_done(struct tevent_req *subreq)
+     talloc_zfree(subreq);
+     if (ret) {
+         DEBUG(4, ("sdap_get_generic_ext_recv failed [%d]: %s\n",
+-                  ret, strerror(ret)));
++                  ret, sss_strerror(ret)));
+         tevent_req_error(req, ret);
+         return;
+     }
+diff --git a/src/providers/ldap/sdap_async_groups_ad.c b/src/providers/ldap/sdap_async_groups_ad.c
+index 9b61c697d5789c3ec3467ec52a7171f6a640ce9e..6a8a4fd139657040ff83cad10ba35a0dde4a0122 100644
+--- a/src/providers/ldap/sdap_async_groups_ad.c
++++ b/src/providers/ldap/sdap_async_groups_ad.c
+@@ -183,7 +183,7 @@ sdap_get_ad_match_rule_members_step(struct tevent_req *subreq)
+     talloc_zfree(subreq);
+     if (ret != EOK) {
+         DEBUG(SSSDBG_MINOR_FAILURE,
+-              ("LDAP search failed: [%s]\n", strerror(ret)));
++              ("LDAP search failed: [%s]\n", sss_strerror(ret)));
+         tevent_req_error(req, ret);
+         return;
+     }
+diff --git a/src/providers/ldap/sdap_async_initgroups_ad.c b/src/providers/ldap/sdap_async_initgroups_ad.c
+index 8f8f0a4cc635818dcc7f75f9da603ce2f55c820f..724f308da68daf05e2dc4cc6c64cac347ab8a0ca 100644
+--- a/src/providers/ldap/sdap_async_initgroups_ad.c
++++ b/src/providers/ldap/sdap_async_initgroups_ad.c
+@@ -208,7 +208,7 @@ sdap_get_ad_match_rule_initgroups_step(struct tevent_req *subreq)
+     talloc_zfree(subreq);
+     if (ret != EOK) {
+         DEBUG(SSSDBG_MINOR_FAILURE,
+-              ("LDAP search failed: [%s]\n", strerror(ret)));
++              ("LDAP search failed: [%s]\n", sss_strerror(ret)));
+         goto error;
+     }
+ 
+@@ -383,7 +383,7 @@ static void sdap_get_ad_tokengroups_done(struct tevent_req *subreq)
+     talloc_zfree(subreq);
+     if (ret != EOK) {
+         DEBUG(SSSDBG_MINOR_FAILURE,
+-              ("LDAP search failed: [%s]\n", strerror(ret)));
++              ("LDAP search failed: [%s]\n", sss_strerror(ret)));
+         goto done;
+     }
+ 
+diff --git a/src/util/util_errors.c b/src/util/util_errors.c
+index 114c8b04fd354b166d14e526a3bab6a6c0c05951..633257e8da0ef039e555a07ad8b51125114ca01c 100644
+--- a/src/util/util_errors.c
++++ b/src/util/util_errors.c
+@@ -51,6 +51,7 @@ struct err_string error_to_str[] = {
+     { "Entry not found" }, /* ERR_NOT_FOUND */
+     { "Domain not found" }, /* ERR_DOMAIN_NOT_FOUND */
+     { "Missing configuration file" }, /* ERR_MISSING_CONF */
++    { "Malformed search filter" }, /* ERR_INVALID_FILTER, */
+ };
+ 
+ 
+diff --git a/src/util/util_errors.h b/src/util/util_errors.h
+index bca45f392b0357c3f1c848768358cb1d47514715..1332085031dbe6935cbdc94543fa14b09fe81028 100644
+--- a/src/util/util_errors.h
++++ b/src/util/util_errors.h
+@@ -73,6 +73,7 @@ enum sssd_errors {
+     ERR_NOT_FOUND,
+     ERR_DOMAIN_NOT_FOUND,
+     ERR_MISSING_CONF,
++    ERR_INVALID_FILTER,
+     ERR_LAST            /* ALWAYS LAST */
+ };
+ 
+-- 
+1.8.4.2
+
diff --git a/SOURCES/0048-FAST-when-parsing-krb5_child-response-make-sure-to-n.patch b/SOURCES/0048-FAST-when-parsing-krb5_child-response-make-sure-to-n.patch
new file mode 100644
index 0000000..0b1fb22
--- /dev/null
+++ b/SOURCES/0048-FAST-when-parsing-krb5_child-response-make-sure-to-n.patch
@@ -0,0 +1,46 @@
+From 103f7efda7b84e7c791af2ebc2255e61e826fd75 Mon Sep 17 00:00:00 2001
+From: Alexander Bokovoy <ab@samba.org>
+Date: Tue, 24 Dec 2013 13:01:46 +0200
+Subject: [PATCH 48/48] FAST: when parsing krb5_child response, make sure to
+ not miss OTP message if it was last one
+
+The last message in the stream might be with empty payload which means we get
+only message type and message length (0) returned, i.e. 8 bytes left remaining
+in the stream after processing preceding message. This makes our calculation at
+the end of a message processing loop incorrect -- p+2*sizeof(int32_t) can be
+equal to len, after all.
+
+Fixes FAST processing for FreeIPA native OTP case:
+https://fedorahosted.org/sssd/ticket/2186
+---
+ src/providers/krb5/krb5_child_handler.c | 7 ++++---
+ 1 file changed, 4 insertions(+), 3 deletions(-)
+
+diff --git a/src/providers/krb5/krb5_child_handler.c b/src/providers/krb5/krb5_child_handler.c
+index 92dec0d2afb1627b61c3dd1037e91546a7ee08d6..d6c1dc1f9707444a82e433a375839cadf73f1259 100644
+--- a/src/providers/krb5/krb5_child_handler.c
++++ b/src/providers/krb5/krb5_child_handler.c
+@@ -548,8 +548,9 @@ parse_krb5_child_response(TALLOC_CTX *mem_ctx, uint8_t *buf, ssize_t len,
+          * CCACHE_ENV_NAME"=". pref_len also counts the trailing '=' because
+          * sizeof() counts the trailing '\0' of a string. */
+         pref_len = sizeof(CCACHE_ENV_NAME);
+-        if (msg_len > pref_len &&
+-            strncmp((const char *) &buf[p], CCACHE_ENV_NAME"=", pref_len) == 0) {
++        if ((msg_type == SSS_PAM_ENV_ITEM) &&
++            (msg_len > pref_len) &&
++            (strncmp((const char *) &buf[p], CCACHE_ENV_NAME"=", pref_len) == 0)) {
+             ccname = (char *) &buf[p+pref_len];
+             ccname_len = msg_len-pref_len;
+         }
+@@ -600,7 +601,7 @@ parse_krb5_child_response(TALLOC_CTX *mem_ctx, uint8_t *buf, ssize_t len,
+ 
+         p += msg_len;
+ 
+-        if ((p < len) && (p + 2*sizeof(int32_t) >= len)) {
++        if ((p < len) && (p + 2*sizeof(int32_t) > len)) {
+             DEBUG(SSSDBG_CRIT_FAILURE,
+                   ("The remainder of the message is too short.\n"));
+             return EINVAL;
+-- 
+1.8.4.2
+
diff --git a/SOURCES/0049-UTIL-Inherit-parent-domain-s-default_shell.patch b/SOURCES/0049-UTIL-Inherit-parent-domain-s-default_shell.patch
new file mode 100644
index 0000000..5d3c101
--- /dev/null
+++ b/SOURCES/0049-UTIL-Inherit-parent-domain-s-default_shell.patch
@@ -0,0 +1,33 @@
+From 14bafb02c396396e04412a4981b98ae75534294a Mon Sep 17 00:00:00 2001
+From: Jakub Hrozek <jhrozek@redhat.com>
+Date: Tue, 14 Jan 2014 10:55:39 +0100
+Subject: [PATCH 49/53] UTIL: Inherit parent domain's default_shell
+
+Some override parameters were not inherited when creating subdomains.
+Especially with AD trusts, this gave strange results.
+---
+ src/util/domain_info_utils.c | 5 ++++-
+ 1 file changed, 4 insertions(+), 1 deletion(-)
+
+diff --git a/src/util/domain_info_utils.c b/src/util/domain_info_utils.c
+index 61efc0b40dcd8969b635781549feab3eee79299e..98678f97b95d271a0d2e18daadae7f3f9a79f89e 100644
+--- a/src/util/domain_info_utils.c
++++ b/src/util/domain_info_utils.c
+@@ -271,10 +271,13 @@ struct sss_domain_info *new_subdomain(TALLOC_CTX *mem_ctx,
+     dom->group_timeout = parent->group_timeout;
+     dom->netgroup_timeout = parent->netgroup_timeout;
+     dom->service_timeout = parent->service_timeout;
+-    dom->override_homedir = parent->override_homedir;
+     dom->names = parent->names;
+ 
++    dom->override_homedir = parent->override_homedir;
++    dom->fallback_homedir = parent->fallback_homedir;
+     dom->subdomain_homedir = parent->subdomain_homedir;
++    dom->override_shell = parent->override_shell;
++    dom->default_shell = parent->default_shell;
+ 
+     if (parent->sysdb == NULL) {
+         DEBUG(SSSDBG_OP_FAILURE, ("Missing sysdb context in parent domain.\n"));
+-- 
+1.8.4.2
+
diff --git a/SOURCES/0050-NSS-Use-plain-user-name-when-expanding-homedir.patch b/SOURCES/0050-NSS-Use-plain-user-name-when-expanding-homedir.patch
new file mode 100644
index 0000000..7fddcbd
--- /dev/null
+++ b/SOURCES/0050-NSS-Use-plain-user-name-when-expanding-homedir.patch
@@ -0,0 +1,42 @@
+From 90e871a816440af34f095d3b1003476a0978a348 Mon Sep 17 00:00:00 2001
+From: Jakub Hrozek <jhrozek@redhat.com>
+Date: Tue, 14 Jan 2014 11:10:25 +0100
+Subject: [PATCH 50/53] NSS: Use plain user name when expanding homedir
+
+---
+ src/responder/nss/nsssrv_cmd.c | 13 ++++++++++++-
+ 1 file changed, 12 insertions(+), 1 deletion(-)
+
+diff --git a/src/responder/nss/nsssrv_cmd.c b/src/responder/nss/nsssrv_cmd.c
+index 550017c0e4385a7147ed5ef83da2c37cb97c8092..c59078b545842561a7e5f62e9a99da6057b23660 100644
+--- a/src/responder/nss/nsssrv_cmd.c
++++ b/src/responder/nss/nsssrv_cmd.c
+@@ -172,13 +172,24 @@ static const char *get_homedir_override(TALLOC_CTX *mem_ctx,
+                                         struct ldb_message *msg,
+                                         struct nss_ctx *nctx,
+                                         struct sss_domain_info *dom,
+-                                        const char *name,
++                                        const char *orig_name,
+                                         uint32_t uid)
+ {
+     const char *homedir;
++    char *name;
++    char *domname;
++    errno_t ret;
+ 
+     homedir = ldb_msg_find_attr_as_string(msg, SYSDB_HOMEDIR, NULL);
+ 
++    /* Subdomain users store FQDN in their name attribute */
++    ret = sss_parse_name(mem_ctx, dom->names, orig_name, &domname, &name);
++    if (ret != EOK) {
++        DEBUG(SSSDBG_MINOR_FAILURE, ("Could not parse [%s] into "
++              "name-value components.\n", orig_name));
++        return NULL;
++    }
++
+     /* Check whether we are unconditionally overriding the server
+      * for home directory locations.
+      */
+-- 
+1.8.4.2
+
diff --git a/SOURCES/0051-simple-access-match-objects-using-flat-name.patch b/SOURCES/0051-simple-access-match-objects-using-flat-name.patch
new file mode 100644
index 0000000..cf15b3e
--- /dev/null
+++ b/SOURCES/0051-simple-access-match-objects-using-flat-name.patch
@@ -0,0 +1,31 @@
+From 9df26b2f56d249ce69f7fd7d5c40b55dfe119e93 Mon Sep 17 00:00:00 2001
+From: Pavel Reichl <pavel.reichl@redhat.com>
+Date: Wed, 8 Jan 2014 15:46:57 +0000
+Subject: [PATCH 51/53] simple access: match objects using flat name
+
+Use flat name to recognise users and groups belonging to main sssd domain.
+
+Resolves:
+https://fedorahosted.org/sssd/ticket/2189
+---
+ src/providers/simple/simple_access.c | 4 +++-
+ 1 file changed, 3 insertions(+), 1 deletion(-)
+
+diff --git a/src/providers/simple/simple_access.c b/src/providers/simple/simple_access.c
+index 46b045e531dfc5fcdff4fc4f5370734aca1e377c..f2bfe755039fd7a370749fd3ce94a47c62c216bc 100644
+--- a/src/providers/simple/simple_access.c
++++ b/src/providers/simple/simple_access.c
+@@ -140,7 +140,9 @@ static errno_t simple_access_parse_names(TALLOC_CTX *mem_ctx,
+             goto done;
+         }
+ 
+-        if (domain == NULL || strcasecmp(domain, be_ctx->domain->name) == 0) {
++        if (domain == NULL || strcasecmp(domain, be_ctx->domain->name) == 0 ||
++            (be_ctx->domain->flat_name != NULL &&
++             strcasecmp(domain, be_ctx->domain->flat_name) == 0)) {
+             /* This object belongs to main SSSD domain. Those users and groups
+              * are stored without domain part, so we will strip it off.
+              * */
+-- 
+1.8.4.2
+
diff --git a/SOURCES/0052-simple-access-refresh-master-domain-info.patch b/SOURCES/0052-simple-access-refresh-master-domain-info.patch
new file mode 100644
index 0000000..4069e20
--- /dev/null
+++ b/SOURCES/0052-simple-access-refresh-master-domain-info.patch
@@ -0,0 +1,219 @@
+From b6c53b49cde8188bf2f8b493b275118472c4482e Mon Sep 17 00:00:00 2001
+From: Pavel Reichl <pavel.reichl@redhat.com>
+Date: Wed, 8 Jan 2014 16:03:08 +0000
+Subject: [PATCH 52/53] simple access: refresh master domain info
+
+To correctly decide if an object is a member of the main sssd domain, a flat name
+is needed. However, the information may not be available when the module is
+inited so it may be necessary to refresh this data later while processing a
+request.
+
+Resolves:
+https://fedorahosted.org/sssd/ticket/2189
+---
+ src/providers/simple/simple_access.c | 135 +++++++++++++++++++++++------------
+ src/providers/simple/simple_access.h |   2 +
+ 2 files changed, 92 insertions(+), 45 deletions(-)
+
+diff --git a/src/providers/simple/simple_access.c b/src/providers/simple/simple_access.c
+index f2bfe755039fd7a370749fd3ce94a47c62c216bc..eab62a826b4749aa5e5dab4a8e491fc2263be4fb 100644
+--- a/src/providers/simple/simple_access.c
++++ b/src/providers/simple/simple_access.c
+@@ -32,7 +32,76 @@
+ #define CONFDB_SIMPLE_ALLOW_GROUPS "simple_allow_groups"
+ #define CONFDB_SIMPLE_DENY_GROUPS "simple_deny_groups"
+ 
++#define TIMEOUT_OF_REFRESH_FILTER_LISTS 5
++
+ static void simple_access_check(struct tevent_req *req);
++static errno_t simple_access_parse_names(TALLOC_CTX *mem_ctx,
++                                         struct be_ctx *be_ctx,
++                                         char **list,
++                                         char ***_out);
++
++static int simple_access_obtain_filter_lists(struct simple_ctx *ctx)
++{
++    struct be_ctx *bectx = ctx->be_ctx;
++    int ret;
++    int i;
++    struct {
++        const char *name;
++        const char *option;
++        char **orig_list;
++        char ***ctx_list;
++    } lists[] = {{"Allow users", CONFDB_SIMPLE_ALLOW_USERS, NULL, NULL},
++                 {"Deny users", CONFDB_SIMPLE_DENY_USERS, NULL, NULL},
++                 {"Allow groups", CONFDB_SIMPLE_ALLOW_GROUPS, NULL, NULL},
++                 {"Deny groups", CONFDB_SIMPLE_DENY_GROUPS, NULL, NULL},
++                 {NULL, NULL, NULL, NULL}};
++
++    lists[0].ctx_list = &ctx->allow_users;
++    lists[1].ctx_list = &ctx->deny_users;
++    lists[2].ctx_list = &ctx->allow_groups;
++    lists[3].ctx_list = &ctx->deny_groups;
++
++    ret = sysdb_master_domain_update(bectx->domain);
++    if (ret != EOK) {
++        DEBUG(SSSDBG_FUNC_DATA, ("Update of master domain failed [%d]: %s.\n",
++                                 ret, sss_strerror(ret)));
++        goto failed;
++    }
++
++    for (i = 0; lists[i].name != NULL; i++) {
++        ret = confdb_get_string_as_list(bectx->cdb, ctx, bectx->conf_path,
++                                        lists[i].option, &lists[i].orig_list);
++        if (ret == ENOENT) {
++            DEBUG(SSSDBG_FUNC_DATA, ("%s list is empty.\n", lists[i].name));
++            *lists[i].ctx_list = NULL;
++            continue;
++        } else if (ret != EOK) {
++            DEBUG(SSSDBG_CRIT_FAILURE, ("confdb_get_string_as_list failed.\n"));
++            goto failed;
++        }
++
++        ret = simple_access_parse_names(ctx, bectx, lists[i].orig_list,
++                                        lists[i].ctx_list);
++        if (ret != EOK) {
++            DEBUG(SSSDBG_CRIT_FAILURE, ("Unable to parse %s list [%d]: %s\n",
++                                        lists[i].name, ret, sss_strerror(ret)));
++            goto failed;
++        }
++    }
++
++    if (!ctx->allow_users &&
++            !ctx->allow_groups &&
++            !ctx->deny_users &&
++            !ctx->deny_groups) {
++        DEBUG(SSSDBG_OP_FAILURE,
++              ("No rules supplied for simple access provider. "
++               "Access will be granted for all users.\n"));
++    }
++    return EOK;
++
++failed:
++    return ret;
++}
+ 
+ void simple_access_handler(struct be_req *be_req)
+ {
+@@ -40,13 +109,16 @@ void simple_access_handler(struct be_req *be_req)
+     struct pam_data *pd;
+     struct tevent_req *req;
+     struct simple_ctx *ctx;
++    int ret;
++    time_t now;
+ 
+     pd = talloc_get_type(be_req_get_data(be_req), struct pam_data);
+ 
+     pd->pam_status = PAM_SYSTEM_ERR;
+ 
+     if (pd->cmd != SSS_PAM_ACCT_MGMT) {
+-        DEBUG(4, ("simple access does not handles pam task %d.\n", pd->cmd));
++        DEBUG(SSSDBG_CONF_SETTINGS,
++              ("simple access does not handle pam task %d.\n", pd->cmd));
+         pd->pam_status = PAM_MODULE_UNKNOWN;
+         goto done;
+     }
+@@ -54,6 +126,18 @@ void simple_access_handler(struct be_req *be_req)
+     ctx = talloc_get_type(be_ctx->bet_info[BET_ACCESS].pvt_bet_data,
+                           struct simple_ctx);
+ 
++
++    now = time(NULL);
++    if ((now - ctx->last_refresh_of_filter_lists)
++        > TIMEOUT_OF_REFRESH_FILTER_LISTS) {
++
++        ret = simple_access_obtain_filter_lists(ctx);
++        if (ret != EOK) {
++            DEBUG(SSSDBG_MINOR_FAILURE, ("Failed to refresh filter lists\n"));
++        }
++        ctx->last_refresh_of_filter_lists = now;
++    }
++
+     req = simple_access_check_send(be_req, be_ctx->ev, ctx, pd->user);
+     if (!req) {
+         pd->pam_status = PAM_SYSTEM_ERR;
+@@ -176,18 +260,6 @@ int sssm_simple_access_init(struct be_ctx *bectx, struct bet_ops **ops,
+ {
+     int ret = EINVAL;
+     struct simple_ctx *ctx;
+-    int i;
+-    struct {
+-        const char *name;
+-        const char *option;
+-        char **orig_list;
+-        char ***ctx_list;
+-    } lists[] = {{"Allow users", CONFDB_SIMPLE_ALLOW_USERS, NULL, NULL},
+-                 {"Deny users", CONFDB_SIMPLE_DENY_USERS, NULL, NULL},
+-                 {"Allow groups", CONFDB_SIMPLE_ALLOW_GROUPS, NULL, NULL},
+-                 {"Deny groups", CONFDB_SIMPLE_DENY_GROUPS, NULL, NULL},
+-                 {NULL, NULL, NULL, NULL}};
+-
+     ctx = talloc_zero(bectx, struct simple_ctx);
+     if (ctx == NULL) {
+         DEBUG(SSSDBG_CRIT_FAILURE, ("talloc_zero failed.\n"));
+@@ -196,39 +268,11 @@ int sssm_simple_access_init(struct be_ctx *bectx, struct bet_ops **ops,
+ 
+     ctx->domain = bectx->domain;
+     ctx->be_ctx = bectx;
++    ctx->last_refresh_of_filter_lists = 0;
+ 
+-    lists[0].ctx_list = &ctx->allow_users;
+-    lists[1].ctx_list = &ctx->deny_users;
+-    lists[2].ctx_list = &ctx->allow_groups;
+-    lists[3].ctx_list = &ctx->deny_groups;
+-
+-    for (i = 0; lists[i].name != NULL; i++) {
+-        ret = confdb_get_string_as_list(bectx->cdb, ctx, bectx->conf_path,
+-                                        lists[i].option, &lists[i].orig_list);
+-        if (ret == ENOENT) {
+-            DEBUG(SSSDBG_FUNC_DATA, ("%s list is empty.\n", lists[i].name));
+-            *lists[i].ctx_list = NULL;
+-            continue;
+-        } else if (ret != EOK) {
+-            DEBUG(SSSDBG_CRIT_FAILURE, ("confdb_get_string_as_list failed.\n"));
+-            goto failed;
+-        }
+-
+-        ret = simple_access_parse_names(ctx, bectx, lists[i].orig_list,
+-                                        lists[i].ctx_list);
+-        if (ret != EOK) {
+-            DEBUG(SSSDBG_CRIT_FAILURE, ("Unable to parse %s list [%d]: %s\n",
+-                                        lists[i].name, ret, sss_strerror(ret)));
+-            goto failed;
+-        }
+-    }
+-
+-    if (!ctx->allow_users &&
+-            !ctx->allow_groups &&
+-            !ctx->deny_users &&
+-            !ctx->deny_groups) {
+-        DEBUG(SSSDBG_OP_FAILURE, ("No rules supplied for simple access provider. "
+-                                  "Access will be granted for all users.\n"));
++    ret = simple_access_obtain_filter_lists(ctx);
++    if (ret != EOK) {
++        goto failed;
+     }
+ 
+     *ops = &simple_access_ops;
+@@ -240,3 +284,4 @@ failed:
+     talloc_free(ctx);
+     return ret;
+ }
++
+diff --git a/src/providers/simple/simple_access.h b/src/providers/simple/simple_access.h
+index 15dfaceb2d9a6670d3559e4a945c2c7a633fad44..a618b2e2ec16a2f32bad7ceb1f5adb7523199316 100644
+--- a/src/providers/simple/simple_access.h
++++ b/src/providers/simple/simple_access.h
+@@ -32,6 +32,8 @@ struct simple_ctx {
+     char **deny_users;
+     char **allow_groups;
+     char **deny_groups;
++
++    time_t last_refresh_of_filter_lists;
+ };
+ 
+ struct tevent_req *simple_access_check_send(TALLOC_CTX *mem_ctx,
+-- 
+1.8.4.2
+
diff --git a/SOURCES/0053-NSS-add-support-for-subdomain_homedir.patch b/SOURCES/0053-NSS-add-support-for-subdomain_homedir.patch
new file mode 100644
index 0000000..c5b5068
--- /dev/null
+++ b/SOURCES/0053-NSS-add-support-for-subdomain_homedir.patch
@@ -0,0 +1,33 @@
+From 7bc2cfa445142254276d712a0ff6622eaf670253 Mon Sep 17 00:00:00 2001
+From: Pavel Reichl <preichl@redhat.com>
+Date: Wed, 15 Jan 2014 15:52:35 +0000
+Subject: [PATCH 53/53] NSS: add support for subdomain_homedir
+
+Resolves:
+https://fedorahosted.org/sssd/ticket/2169
+---
+ src/responder/nss/nsssrv_cmd.c | 8 ++++++++
+ 1 file changed, 8 insertions(+)
+
+diff --git a/src/responder/nss/nsssrv_cmd.c b/src/responder/nss/nsssrv_cmd.c
+index c59078b545842561a7e5f62e9a99da6057b23660..9ac3680de4d6ff12fe0c77a3963f84934e385276 100644
+--- a/src/responder/nss/nsssrv_cmd.c
++++ b/src/responder/nss/nsssrv_cmd.c
+@@ -201,6 +201,14 @@ static const char *get_homedir_override(TALLOC_CTX *mem_ctx,
+                                        name, uid, homedir, dom->name, NULL);
+     }
+ 
++    /* Override home directory location for subdomains.
++     * This option can be overriden by override_homedir.
++     */
++    if (IS_SUBDOMAIN(dom) && dom->subdomain_homedir) {
++        return expand_homedir_template(mem_ctx, dom->subdomain_homedir,
++                                       name, uid, homedir, dom->name, NULL);
++    }
++
+     if (!homedir || *homedir == '\0') {
+         /* In the case of a NULL or empty homedir, check to see if
+          * we have a fallback homedir to use.
+-- 
+1.8.4.2
+
diff --git a/SOURCES/0054-AD-Return-right-error-code-from-netlogon_get_flat_na.patch b/SOURCES/0054-AD-Return-right-error-code-from-netlogon_get_flat_na.patch
new file mode 100644
index 0000000..afca73b
--- /dev/null
+++ b/SOURCES/0054-AD-Return-right-error-code-from-netlogon_get_flat_na.patch
@@ -0,0 +1,29 @@
+From e47d40005610c3f6e14d4a656fda2e8cadde4844 Mon Sep 17 00:00:00 2001
+From: Lukas Slebodnik <lslebodn@redhat.com>
+Date: Thu, 16 Jan 2014 16:42:50 +0100
+Subject: [PATCH 54/57] AD: Return right error code from netlogon_get_flat_name
+
+EOK was returned in done section of netlogon_get_flat_name,
+even if error code was set in variable ret.
+
+This patch fixes also warnings from scan-build.
+---
+ src/providers/ad/ad_domain_info.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/src/providers/ad/ad_domain_info.c b/src/providers/ad/ad_domain_info.c
+index 5475c5bc7ec74e81080566c6fbd6919c54a60f40..28d24b1613040ab1e85cd1504c5469aa60bc08c5 100644
+--- a/src/providers/ad/ad_domain_info.c
++++ b/src/providers/ad/ad_domain_info.c
+@@ -131,7 +131,7 @@ netlogon_get_domain_info(TALLOC_CTX *mem_ctx,
+     ret = EOK;
+ done:
+     talloc_free(ndr_pull);
+-    return EOK;
++    return ret;
+ }
+ 
+ struct ad_master_domain_state {
+-- 
+1.8.4.2
+
diff --git a/SOURCES/0055-AD-Don-t-fail-the-request-if-ad_account_can_shortcut.patch b/SOURCES/0055-AD-Don-t-fail-the-request-if-ad_account_can_shortcut.patch
new file mode 100644
index 0000000..31bc56b
--- /dev/null
+++ b/SOURCES/0055-AD-Don-t-fail-the-request-if-ad_account_can_shortcut.patch
@@ -0,0 +1,28 @@
+From 380c18bfccd25961cf56c9f61b1ac8641a2022b1 Mon Sep 17 00:00:00 2001
+From: Jakub Hrozek <jhrozek@redhat.com>
+Date: Thu, 16 Jan 2014 20:49:15 +0100
+Subject: [PATCH 55/57] AD: Don't fail the request if ad_account_can_shortcut
+ fails
+
+---
+ src/providers/ad/ad_id.c | 4 +++-
+ 1 file changed, 3 insertions(+), 1 deletion(-)
+
+diff --git a/src/providers/ad/ad_id.c b/src/providers/ad/ad_id.c
+index 44bfa00986b6c0ebfa65dd7b83dd45eb64b87946..ada47753fb337641df582a5a59affe8124fc2035 100644
+--- a/src/providers/ad/ad_id.c
++++ b/src/providers/ad/ad_id.c
+@@ -320,7 +320,9 @@ ad_account_info_handler(struct be_req *be_req)
+                                   ar->filter_type, ar->filter_value,
+                                   ar->domain, &shortcut);
+     if (ret != EOK) {
+-        goto fail;
++        DEBUG(SSSDBG_TRACE_FUNC,
++              ("Cannot determine the right domain: %s\n", sss_strerror(ret)));
++        shortcut = false;
+     }
+ 
+     if (shortcut) {
+-- 
+1.8.4.2
+
diff --git a/SOURCES/0056-MAN-Fix-a-typo.patch b/SOURCES/0056-MAN-Fix-a-typo.patch
new file mode 100644
index 0000000..4d8b827
--- /dev/null
+++ b/SOURCES/0056-MAN-Fix-a-typo.patch
@@ -0,0 +1,25 @@
+From c5b312ebf55befc4d37f9a279340b55e65475cd3 Mon Sep 17 00:00:00 2001
+From: Jakub Hrozek <jhrozek@redhat.com>
+Date: Mon, 20 Jan 2014 17:04:28 +0100
+Subject: [PATCH 56/57] MAN: Fix a typo
+
+---
+ 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 43c06955d0182afe5a3c7d703f7c08b7cd09f503..b879bdf63c40e80efa21644bd9ba9d2d3477af06 100644
+--- a/src/man/sssd.conf.5.xml
++++ b/src/man/sssd.conf.5.xml
+@@ -489,7 +489,7 @@
+                         <para>
+                             example:
+                             <programlisting>
+-override_homedir = /home/%u
++fallback_homedir = /home/%u
+                             </programlisting>
+                         </para>
+                         <para>
+-- 
+1.8.4.2
+
diff --git a/SOURCES/0057-LDAP-Fix-error-check.patch b/SOURCES/0057-LDAP-Fix-error-check.patch
new file mode 100644
index 0000000..fd1664f
--- /dev/null
+++ b/SOURCES/0057-LDAP-Fix-error-check.patch
@@ -0,0 +1,28 @@
+From fe7f5b1a3a965d1667ba1552a2b1165788f3bd2e Mon Sep 17 00:00:00 2001
+From: Jakub Hrozek <jhrozek@redhat.com>
+Date: Fri, 17 Jan 2014 10:49:27 +0100
+Subject: [PATCH 57/57] LDAP: Fix error check
+
+https://fedorahosted.org/sssd/ticket/2199
+---
+ src/providers/ldap/ldap_common.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/src/providers/ldap/ldap_common.c b/src/providers/ldap/ldap_common.c
+index 35ea81360b4ec61eca6b952cd86fc93a6eda17dc..4c94937aad9e25bd1cd0b6d573da2982b0f1be05 100644
+--- a/src/providers/ldap/ldap_common.c
++++ b/src/providers/ldap/ldap_common.c
+@@ -831,8 +831,8 @@ errno_t common_parse_search_base(TALLOC_CTX *mem_ctx,
+         ret = sdap_create_search_base(search_bases, unparsed_base,
+                                       LDAP_SCOPE_SUBTREE, old_filter,
+                                       &search_bases[0]);
+-        if (!search_bases[0]) {
+-            ret = ENOMEM;
++        if (ret != EOK) {
++            DEBUG(SSSDBG_OP_FAILURE, ("Cannot create new sdap search base\n"));
+             goto done;
+         }
+ 
+-- 
+1.8.4.2
+
diff --git a/SOURCES/0058-LDAP-Don-t-fail-if-subdomain-cannot-be-found-by-sid.patch b/SOURCES/0058-LDAP-Don-t-fail-if-subdomain-cannot-be-found-by-sid.patch
new file mode 100644
index 0000000..c0ab7e0
--- /dev/null
+++ b/SOURCES/0058-LDAP-Don-t-fail-if-subdomain-cannot-be-found-by-sid.patch
@@ -0,0 +1,47 @@
+From 8509e1fe368b62225b7cf39eb1eec6cac7bf38b3 Mon Sep 17 00:00:00 2001
+From: Lukas Slebodnik <lslebodn@redhat.com>
+Date: Fri, 13 Dec 2013 18:20:08 +0100
+Subject: [PATCH 58/60] LDAP: Don't fail if subdomain cannot be found by sid
+
+Domain needn't contain sid if id_provider is ldap.
+With enabled id mapping, user couldn't be stored, because domain
+couldn't be found by sid.
+
+Resolves:
+https://fedorahosted.org/sssd/ticket/2172
+---
+ src/providers/ldap/sdap_async_users.c | 10 ++++++----
+ 1 file changed, 6 insertions(+), 4 deletions(-)
+
+diff --git a/src/providers/ldap/sdap_async_users.c b/src/providers/ldap/sdap_async_users.c
+index 7f0b2eea0b5ee909bcf148236c7fc43863fe8c13..65c456c8fffb57cbf9e977a49388dbe250d1412a 100644
+--- a/src/providers/ldap/sdap_async_users.c
++++ b/src/providers/ldap/sdap_async_users.c
+@@ -124,6 +124,7 @@ int sdap_save_user(TALLOC_CTX *memctx,
+     bool use_id_mapping;
+     char *sid_str;
+     char *dom_sid_str = NULL;
++    struct sss_domain_info *subdomain;
+ 
+     DEBUG(SSSDBG_TRACE_FUNC, ("Save user\n"));
+ 
+@@ -163,11 +164,12 @@ int sdap_save_user(TALLOC_CTX *memctx,
+     /* If this object has a SID available, we will determine the correct
+      * domain by its SID. */
+     if (sid_str != NULL) {
+-        dom = find_subdomain_by_sid(get_domains_head(dom), sid_str);
+-        if (dom == NULL) {
+-            DEBUG(SSSDBG_OP_FAILURE, ("SID %s does not belong to any known "
++        subdomain = find_subdomain_by_sid(get_domains_head(dom), sid_str);
++        if (subdomain) {
++            dom = subdomain;
++        } else {
++            DEBUG(SSSDBG_TRACE_FUNC, ("SID %s does not belong to any known "
+                                       "domain\n", sid_str));
+-            return ERR_DOMAIN_NOT_FOUND;
+         }
+     }
+ 
+-- 
+1.8.4.2
+
diff --git a/SOURCES/0059-LDAP-update-id-mapping-detection-for-ldap-provider.patch b/SOURCES/0059-LDAP-update-id-mapping-detection-for-ldap-provider.patch
new file mode 100644
index 0000000..f1573d8
--- /dev/null
+++ b/SOURCES/0059-LDAP-update-id-mapping-detection-for-ldap-provider.patch
@@ -0,0 +1,33 @@
+From adff1d0ac15ef7fd58cf2bc79af60f38c807126c Mon Sep 17 00:00:00 2001
+From: Lukas Slebodnik <lslebodn@redhat.com>
+Date: Wed, 15 Jan 2014 14:55:10 +0100
+Subject: [PATCH 59/60] LDAP: update id mapping detection for ldap provider
+
+For id_provider ldap, it is only necessary to enable option ldap_id_mapping.
+It is an regression introduced in the commit d3e1d88ce7de3216a862b
+
+Resolves:
+https://fedorahosted.org/sssd/ticket/2172
+---
+ src/providers/ldap/sdap_idmap.c | 5 +++++
+ 1 file changed, 5 insertions(+)
+
+diff --git a/src/providers/ldap/sdap_idmap.c b/src/providers/ldap/sdap_idmap.c
+index 249201def04131e01722479026b851e47e2283b5..b6455b81fbdedffc92bbaf8bdfbc12c1615a22f5 100644
+--- a/src/providers/ldap/sdap_idmap.c
++++ b/src/providers/ldap/sdap_idmap.c
+@@ -522,6 +522,11 @@ bool sdap_idmap_domain_has_algorithmic_mapping(struct sdap_idmap_ctx *ctx,
+     int ret;
+     TALLOC_CTX *tmp_ctx = NULL;
+ 
++    if (dp_opt_get_bool(ctx->id_ctx->opts->basic, SDAP_ID_MAPPING)
++        && 0 == strcmp("ldap", ctx->id_ctx->be->bet_info[BET_ID].mod_name)) {
++        return true;
++    }
++
+     err = sss_idmap_domain_has_algorithmic_mapping(ctx->map, dom_sid,
+                                                    &has_algorithmic_mapping);
+     if (err == IDMAP_SUCCESS) {
+-- 
+1.8.4.2
+
diff --git a/SOURCES/0060-sdap_idamp-Fall-back-to-another-method-if-sid-is-wro.patch b/SOURCES/0060-sdap_idamp-Fall-back-to-another-method-if-sid-is-wro.patch
new file mode 100644
index 0000000..ccfa04f
--- /dev/null
+++ b/SOURCES/0060-sdap_idamp-Fall-back-to-another-method-if-sid-is-wro.patch
@@ -0,0 +1,42 @@
+From 993aaa15cf4b128951fc9bd4a574e7ac5895d942 Mon Sep 17 00:00:00 2001
+From: Lukas Slebodnik <lslebodn@redhat.com>
+Date: Fri, 13 Dec 2013 15:33:23 +0100
+Subject: [PATCH 60/60] sdap_idamp: Fall back to another method if sid is wrong
+
+sss_idmap_domain_has_algorithmic_mapping can return also
+IDMAP_SID_INVALID, but it does not mean that idmaping is
+unavailable. We should fall back to another method of detection
+(sss_idmap_domain_by_name_has_algorithmic_mapping)
+and do not return false immediately.
+
+Resolves:
+https://fedorahosted.org/sssd/ticket/2172
+---
+ src/providers/ldap/sdap_idmap.c | 10 ++++++++--
+ 1 file changed, 8 insertions(+), 2 deletions(-)
+
+diff --git a/src/providers/ldap/sdap_idmap.c b/src/providers/ldap/sdap_idmap.c
+index b6455b81fbdedffc92bbaf8bdfbc12c1615a22f5..57c448fffa1b13699bd8042d33ac729bddb02ca8 100644
+--- a/src/providers/ldap/sdap_idmap.c
++++ b/src/providers/ldap/sdap_idmap.c
+@@ -529,9 +529,15 @@ bool sdap_idmap_domain_has_algorithmic_mapping(struct sdap_idmap_ctx *ctx,
+ 
+     err = sss_idmap_domain_has_algorithmic_mapping(ctx->map, dom_sid,
+                                                    &has_algorithmic_mapping);
+-    if (err == IDMAP_SUCCESS) {
++    switch (err){
++    case IDMAP_SUCCESS:
+         return has_algorithmic_mapping;
+-    } else if (err != IDMAP_SID_UNKNOWN && err != IDMAP_NO_DOMAIN) {
++    case IDMAP_SID_INVALID: /* FALLTHROUGH */
++    case IDMAP_SID_UNKNOWN: /* FALLTHROUGH */
++    case IDMAP_NO_DOMAIN:   /* FALLTHROUGH */
++        /* continue with idmap_domain_by_name */
++        break;
++    default:
+         return false;
+     }
+ 
+-- 
+1.8.4.2
+
diff --git a/SOURCES/0061-krb5-hint-to-increase-krb5_auth_timeout.patch b/SOURCES/0061-krb5-hint-to-increase-krb5_auth_timeout.patch
new file mode 100644
index 0000000..14fbea6
--- /dev/null
+++ b/SOURCES/0061-krb5-hint-to-increase-krb5_auth_timeout.patch
@@ -0,0 +1,30 @@
+From 4777e706ed485ea61ebb0006c00a3d85440ffe70 Mon Sep 17 00:00:00 2001
+From: Pavel Reichl <preichl@redhat.com>
+Date: Tue, 21 Jan 2014 12:14:01 +0000
+Subject: [PATCH 61/62] krb5: hint to increase krb5_auth_timeout
+
+Resolves:
+https://fedorahosted.org/sssd/ticket/2202
+---
+ src/providers/krb5/krb5_child_handler.c | 5 ++++-
+ 1 file changed, 4 insertions(+), 1 deletion(-)
+
+diff --git a/src/providers/krb5/krb5_child_handler.c b/src/providers/krb5/krb5_child_handler.c
+index d6c1dc1f9707444a82e433a375839cadf73f1259..a0e8f610bbb27a25afa6171763a128dc7b8ea04d 100644
+--- a/src/providers/krb5/krb5_child_handler.c
++++ b/src/providers/krb5/krb5_child_handler.c
+@@ -254,7 +254,10 @@ static void krb5_child_timeout(struct tevent_context *ev,
+         return;
+     }
+ 
+-    DEBUG(9, ("timeout for child [%d] reached.\n", state->child_pid));
++    DEBUG(SSSDBG_IMPORTANT_INFO,
++          ("Timeout for child [%d] reached. In case KDC is distant or network "
++           "is slow you may consider increasing value of krb5_auth_timeout.\n",
++           state->child_pid));
+ 
+     ret = kill(state->child_pid, SIGKILL);
+     if (ret == -1) {
+-- 
+1.8.4.2
+
diff --git a/SOURCES/0062-LDAP-Don-t-abort-request-if-no-id-mapping-domain-mat.patch b/SOURCES/0062-LDAP-Don-t-abort-request-if-no-id-mapping-domain-mat.patch
new file mode 100644
index 0000000..1957040
--- /dev/null
+++ b/SOURCES/0062-LDAP-Don-t-abort-request-if-no-id-mapping-domain-mat.patch
@@ -0,0 +1,111 @@
+From 2ea997d55fb7b18bbf153d5fa625b688285dfdb9 Mon Sep 17 00:00:00 2001
+From: Jakub Hrozek <jhrozek@redhat.com>
+Date: Fri, 24 Jan 2014 10:02:23 +0100
+Subject: [PATCH 62/62] LDAP: Don't abort request if no id mapping domain
+ matches
+
+If an ID was requested from the back end, but no ID mapping domain
+matched, the request ended with a scary error message. It's better to
+treat the request as if no such ID was found in the domain
+
+Related:
+https://fedorahosted.org/sssd/ticket/2200
+---
+ src/providers/ad/ad_id.c     |  2 +-
+ src/providers/ldap/ldap_id.c | 44 +++++++++++++++++++++++++++++++++++++++-----
+ 2 files changed, 40 insertions(+), 6 deletions(-)
+
+diff --git a/src/providers/ad/ad_id.c b/src/providers/ad/ad_id.c
+index ada47753fb337641df582a5a59affe8124fc2035..e74653b734010712ff0562ce1bcbad2b03aba27e 100644
+--- a/src/providers/ad/ad_id.c
++++ b/src/providers/ad/ad_id.c
+@@ -386,7 +386,7 @@ ad_account_info_complete(struct tevent_req *req)
+             error_text = NULL;
+         } else {
+             DEBUG(SSSDBG_FATAL_FAILURE,
+-                  ("Bug: dp_error is OK on failed request"));
++                  ("Bug: dp_error is OK on failed request\n"));
+             dp_error = DP_ERR_FATAL;
+             error_text = req_error_text;
+         }
+diff --git a/src/providers/ldap/ldap_id.c b/src/providers/ldap/ldap_id.c
+index 793bc99ebcec883be7db3fc9dd56fa511d8ba3bb..e36c1f697c18e865a47d991dad103fc440456118 100644
+--- a/src/providers/ldap/ldap_id.c
++++ b/src/providers/ldap/ldap_id.c
+@@ -129,7 +129,20 @@ struct tevent_req *users_get_send(TALLOC_CTX *memctx,
+             /* Convert the UID to its objectSID */
+             err = sss_idmap_unix_to_sid(ctx->opts->idmap_ctx->map,
+                                         uid, &sid);
+-            if (err != IDMAP_SUCCESS) {
++            if (err == IDMAP_NO_DOMAIN) {
++                DEBUG(SSSDBG_MINOR_FAILURE,
++                      ("[%s] did not match any configured ID mapping domain\n",
++                       name));
++
++                ret = sysdb_delete_user(state->sysdb,
++                                        state->domain, NULL, uid);
++                if (ret == ENOENT) {
++                    /* Ignore errors to remove users that were not cached previously */
++                    ret = EOK;
++                }
++
++                goto fail;
++            } else if (err != IDMAP_SUCCESS) {
+                 DEBUG(SSSDBG_MINOR_FAILURE,
+                       ("Mapping ID [%s] to SID failed: [%s]\n",
+                        name, idmap_error_string(err)));
+@@ -213,7 +226,11 @@ struct tevent_req *users_get_send(TALLOC_CTX *memctx,
+     return req;
+ 
+ fail:
+-    tevent_req_error(req, ret);
++    if (ret != EOK) {
++        tevent_req_error(req, ret);
++    } else {
++        tevent_req_done(req);
++    }
+     tevent_req_post(req, ev);
+     return req;
+ }
+@@ -496,10 +513,23 @@ struct tevent_req *groups_get_send(TALLOC_CTX *memctx,
+                 goto fail;
+             }
+ 
+-            /* Convert the UID to its objectSID */
++            /* Convert the GID to its objectSID */
+             err = sss_idmap_unix_to_sid(ctx->opts->idmap_ctx->map,
+                                         gid, &sid);
+-            if (err != IDMAP_SUCCESS) {
++            if (err == IDMAP_NO_DOMAIN) {
++                DEBUG(SSSDBG_MINOR_FAILURE,
++                      ("[%s] did not match any configured ID mapping domain\n",
++                       name));
++
++                ret = sysdb_delete_group(state->sysdb,
++                                         state->domain, NULL, gid);
++                if (ret == ENOENT) {
++                    /* Ignore errors to remove users that were not cached previously */
++                    ret = EOK;
++                }
++
++                goto fail;
++            } else if (err != IDMAP_SUCCESS) {
+                 DEBUG(SSSDBG_MINOR_FAILURE,
+                       ("Mapping ID [%s] to SID failed: [%s]\n",
+                        name, idmap_error_string(err)));
+@@ -587,7 +617,11 @@ struct tevent_req *groups_get_send(TALLOC_CTX *memctx,
+     return req;
+ 
+ fail:
+-    tevent_req_error(req, ret);
++    if (ret != EOK) {
++        tevent_req_error(req, ret);
++    } else {
++        tevent_req_done(req);
++    }
+     tevent_req_post(req, ev);
+     return req;
+ }
+-- 
+1.8.4.2
+
diff --git a/SOURCES/0063-sudo-memset-tm-when-converting-time-attributes.patch b/SOURCES/0063-sudo-memset-tm-when-converting-time-attributes.patch
new file mode 100644
index 0000000..f53462a
--- /dev/null
+++ b/SOURCES/0063-sudo-memset-tm-when-converting-time-attributes.patch
@@ -0,0 +1,34 @@
+From 89eca8339610956f8d95d701dc02d3f8256e2770 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pavel=20B=C5=99ezina?= <pbrezina@redhat.com>
+Date: Wed, 29 Jan 2014 12:56:08 +0100
+Subject: [PATCH 63/71] sudo: memset tm when converting time attributes
+
+strptime() which is used to parse LDAP time value does not initialize
+all fields of tm structure (especially tm_isdst). This results in
+random behavior - when the tm is converted into timestamp via mktime(),
+the result depends on current value of tm_isdst.
+
+Resolves:
+https://fedorahosted.org/sssd/ticket/2213
+
+b
+---
+ src/db/sysdb_sudo.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+diff --git a/src/db/sysdb_sudo.c b/src/db/sysdb_sudo.c
+index 4e98b5b35f3968a1db68c32812eac71670578b60..ceaecbd2666cfb84422bd72b3109da9c4aa0f0f3 100644
+--- a/src/db/sysdb_sudo.c
++++ b/src/db/sysdb_sudo.c
+@@ -56,6 +56,8 @@ static errno_t sysdb_sudo_convert_time(const char *str, time_t *unix_time)
+                              NULL};
+ 
+     for (format = formats; *format != NULL; format++) {
++        /* strptime() may leave some fields uninitialized */
++        memset(&tm, 0, sizeof(struct tm));
+         tret = strptime(str, *format, &tm);
+         if (tret != NULL && *tret == '\0') {
+             *unix_time = mktime(&tm);
+-- 
+1.8.4.2
+
diff --git a/SOURCES/0064-AD-Don-t-mark-domain-as-enumerated-twice.patch b/SOURCES/0064-AD-Don-t-mark-domain-as-enumerated-twice.patch
new file mode 100644
index 0000000..0e318c1
--- /dev/null
+++ b/SOURCES/0064-AD-Don-t-mark-domain-as-enumerated-twice.patch
@@ -0,0 +1,38 @@
+From 392058122b5993a195436c2d5d9833e5e1dd0198 Mon Sep 17 00:00:00 2001
+From: Jakub Hrozek <jhrozek@redhat.com>
+Date: Mon, 16 Dec 2013 03:36:19 +0100
+Subject: [PATCH 64/71] AD: Don't mark domain as enumerated twice
+
+The domain was already marked as enumerated using sysdb_set_enumerated
+in the enumeration request itself.
+---
+ src/providers/ad/ad_id.c | 13 -------------
+ 1 file changed, 13 deletions(-)
+
+diff --git a/src/providers/ad/ad_id.c b/src/providers/ad/ad_id.c
+index e74653b734010712ff0562ce1bcbad2b03aba27e..85edcf6d604f705f5645f77689c2b4c7471b5edd 100644
+--- a/src/providers/ad/ad_id.c
++++ b/src/providers/ad/ad_id.c
+@@ -571,19 +571,6 @@ ad_enumeration_done(struct tevent_req *subreq)
+         return;
+     }
+ 
+-    /* Ok, we've completed an enumeration. Save this to the
+-     * sysdb so we can postpone starting up the enumeration
+-     * process on the next SSSD service restart (to avoid
+-     * slowing down system boot-up
+-     */
+-    ret = sysdb_set_enumerated(state->sdom->dom->sysdb,
+-                               state->sdom->dom, true);
+-    if (ret != EOK) {
+-        DEBUG(SSSDBG_MINOR_FAILURE,
+-              ("Could not mark domain as having enumerated.\n"));
+-        /* This error is non-fatal, so continue */
+-    }
+-
+     tevent_req_done(req);
+ }
+ 
+-- 
+1.8.4.2
+
diff --git a/SOURCES/0065-AD-Store-info-on-whether-a-subdomain-is-set-to-enume.patch b/SOURCES/0065-AD-Store-info-on-whether-a-subdomain-is-set-to-enume.patch
new file mode 100644
index 0000000..284d0d1
--- /dev/null
+++ b/SOURCES/0065-AD-Store-info-on-whether-a-subdomain-is-set-to-enume.patch
@@ -0,0 +1,97 @@
+From c6808be838567870a251d79baad1080910f6ec4c Mon Sep 17 00:00:00 2001
+From: Jakub Hrozek <jhrozek@redhat.com>
+Date: Tue, 10 Dec 2013 17:33:35 +0100
+Subject: [PATCH 65/71] AD: Store info on whether a subdomain is set to
+ enumerate
+
+Depending on the state of the subdomain_enumerate variable, the newly
+created subdomain object is created with the right value of "enumerate"
+attribute in the sysdb.
+---
+ src/providers/ad/ad_subdomains.c | 38 +++++++++++++++++++++++++++++++++-----
+ 1 file changed, 33 insertions(+), 5 deletions(-)
+
+diff --git a/src/providers/ad/ad_subdomains.c b/src/providers/ad/ad_subdomains.c
+index 62c3e16d0d3323a32848b4fbf54d2a151c16f64c..348561a85524c203293c713d3f31552a99d74a43 100644
+--- a/src/providers/ad/ad_subdomains.c
++++ b/src/providers/ad/ad_subdomains.c
+@@ -223,10 +223,28 @@ ads_store_sdap_subdom(struct ad_subdomains_ctx *ctx,
+     return EOK;
+ }
+ 
++static errno_t ad_subdom_enumerates(struct sss_domain_info *parent,
++                                    struct sysdb_attrs *attrs,
++                                    bool *_enumerates)
++{
++    errno_t ret;
++    const char *name;
++
++    ret = sysdb_attrs_get_string(attrs, AD_AT_TRUST_PARTNER, &name);
++    if (ret != EOK) {
++        DEBUG(SSSDBG_OP_FAILURE, ("sysdb_attrs_get_string failed.\n"));
++        return ret;
++    }
++
++    *_enumerates = subdomain_enumerates(parent, name);
++    return EOK;
++}
++
+ static errno_t
+ ad_subdom_store(struct ad_subdomains_ctx *ctx,
+                 struct sss_domain_info *domain,
+-                struct sysdb_attrs *subdom_attrs)
++                struct sysdb_attrs *subdom_attrs,
++                bool enumerate)
+ {
+     TALLOC_CTX *tmp_ctx;
+     const char *name;
+@@ -293,9 +311,8 @@ ad_subdom_store(struct ad_subdomains_ctx *ctx,
+                                              name,
+                                              sid_str);
+ 
+-    /* AD subdomains are currently all mpg and do not enumerate */
+     ret = sysdb_subdomain_store(domain->sysdb, name, realm, flat, sid_str,
+-                                mpg, false, domain->forest);
++                                mpg, enumerate, domain->forest);
+     if (ret != EOK) {
+         DEBUG(SSSDBG_OP_FAILURE, ("sysdb_subdomain_store failed.\n"));
+         goto done;
+@@ -319,6 +336,7 @@ static errno_t ad_subdomains_refresh(struct ad_subdomains_ctx *ctx,
+     const char *value;
+     int c, h;
+     int ret;
++    bool enumerate;
+ 
+     domain = ctx->be_ctx->domain;
+     memset(handled, 0, sizeof(bool) * count);
+@@ -367,7 +385,12 @@ static errno_t ad_subdomains_refresh(struct ad_subdomains_ctx *ctx,
+             talloc_zfree(sdom);
+         } else {
+             /* ok let's try to update it */
+-            ret = ad_subdom_store(ctx, domain, reply[c]);
++            ret = ad_subdom_enumerates(domain, reply[c], &enumerate);
++            if (ret != EOK) {
++                goto done;
++            }
++
++            ret = ad_subdom_store(ctx, domain, reply[c], enumerate);
+             if (ret) {
+                 /* Nothing we can do about the error. Let's at least try
+                  * to reuse the existing domains
+@@ -396,7 +419,12 @@ static errno_t ad_subdomains_refresh(struct ad_subdomains_ctx *ctx,
+         /* Nothing we can do about the error. Let's at least try
+          * to reuse the existing domains.
+          */
+-        ret = ad_subdom_store(ctx, domain, reply[c]);
++        ret = ad_subdom_enumerates(domain, reply[c], &enumerate);
++        if (ret != EOK) {
++            goto done;
++        }
++
++        ret = ad_subdom_store(ctx, domain, reply[c], enumerate);
+         if (ret) {
+             DEBUG(SSSDBG_MINOR_FAILURE, ("Failed to parse subdom data, "
+                   "will try to use cached subdomain\n"));
+-- 
+1.8.4.2
+
diff --git a/SOURCES/0066-LDAP-Pass-a-private-context-to-enumeration-ptask-ins.patch b/SOURCES/0066-LDAP-Pass-a-private-context-to-enumeration-ptask-ins.patch
new file mode 100644
index 0000000..bba823a
--- /dev/null
+++ b/SOURCES/0066-LDAP-Pass-a-private-context-to-enumeration-ptask-ins.patch
@@ -0,0 +1,284 @@
+From 1a5d7f670d94cb5c2b4a727e1e4cb3f1debadaa7 Mon Sep 17 00:00:00 2001
+From: Jakub Hrozek <jhrozek@redhat.com>
+Date: Tue, 10 Dec 2013 21:49:45 +0100
+Subject: [PATCH 66/71] LDAP: Pass a private context to enumeration ptask
+ instead of hardcoded connection
+
+Previously, the sdap-domain enumeration request used a single connection context to
+download all the data. Now we'd like to use different connections to
+download different objects, so the ID context is passed in and the
+request itself decides which connection to use for the sdap-domain
+enumeration.
+---
+ src/providers/ad/ad_id.c           | 12 ++++++++----
+ src/providers/ad/ad_init.c         |  7 ++++---
+ src/providers/ad/ad_subdomains.c   |  8 +++++---
+ src/providers/ipa/ipa_subdomains.c |  8 +++++---
+ src/providers/ldap/ldap_common.c   | 15 +++++++++------
+ src/providers/ldap/ldap_common.h   | 17 +++++++++--------
+ src/providers/ldap/ldap_id_enum.c  | 21 ++++++++++++---------
+ 7 files changed, 52 insertions(+), 36 deletions(-)
+
+diff --git a/src/providers/ad/ad_id.c b/src/providers/ad/ad_id.c
+index 85edcf6d604f705f5645f77689c2b4c7471b5edd..99383c13bdadfe9eb2af9f9323ca19a9759d4620 100644
+--- a/src/providers/ad/ad_id.c
++++ b/src/providers/ad/ad_id.c
+@@ -414,6 +414,7 @@ ad_check_online(struct be_req *be_req)
+ }
+ 
+ struct ad_enumeration_state {
++    struct ad_id_ctx *id_ctx;
+     struct ldap_enum_ctx *ectx;
+     struct sdap_id_op *sdap_op;
+     struct tevent_context *ev;
+@@ -443,6 +444,7 @@ ad_enumeration_send(TALLOC_CTX *mem_ctx,
+ 
+     ectx = talloc_get_type(pvt, struct ldap_enum_ctx);
+     if (ectx == NULL) {
++        DEBUG(SSSDBG_CRIT_FAILURE, ("Cannot retrieve ldap_enum_ctx!\n"));
+         ret = EFAULT;
+         goto fail;
+     }
+@@ -450,8 +452,10 @@ ad_enumeration_send(TALLOC_CTX *mem_ctx,
+     state->ectx = ectx;
+     state->ev = ev;
+     state->sdom = ectx->sdom;
++    state->id_ctx = talloc_get_type(ectx->pvt, struct ad_id_ctx);
+ 
+-    state->sdap_op = sdap_id_op_create(state, ectx->conn->conn_cache);
++    state->sdap_op = sdap_id_op_create(state,
++                                       state->id_ctx->ldap_ctx->conn_cache);
+     if (state->sdap_op == NULL) {
+         DEBUG(SSSDBG_OP_FAILURE, ("sdap_id_op_create failed.\n"));
+         ret = ENOMEM;
+@@ -500,7 +504,7 @@ ad_enumeration_conn_done(struct tevent_req *subreq)
+     }
+ 
+     subreq = ad_master_domain_send(state, state->ev,
+-                                   state->ectx->conn,
++                                   state->id_ctx->ldap_ctx,
+                                    state->sdap_op,
+                                    state->sdom->dom->name);
+     if (subreq == NULL) {
+@@ -540,8 +544,8 @@ ad_enumeration_master_done(struct tevent_req *subreq)
+         return;
+     }
+ 
+-    subreq = sdap_dom_enum_send(state, state->ev, state->ectx->ctx,
+-                                state->sdom, state->ectx->conn);
++    subreq = sdap_dom_enum_send(state, state->ev, state->id_ctx->sdap_id_ctx,
++                                state->sdom, state->id_ctx->ldap_ctx);
+     if (subreq == NULL) {
+         /* The ptask API will reschedule the enumeration on its own on
+          * failure */
+diff --git a/src/providers/ad/ad_init.c b/src/providers/ad/ad_init.c
+index ed69a7d9889bac1281b5ff7c7b0f290ab09173fb..eff6d990d131e3aba124d252d001dd39e78b45cf 100644
+--- a/src/providers/ad/ad_init.c
++++ b/src/providers/ad/ad_init.c
+@@ -205,11 +205,12 @@ sssm_ad_id_init(struct be_ctx *bectx,
+         goto done;
+     }
+ 
+-    ret = sdap_id_setup_tasks(ad_ctx->sdap_id_ctx,
+-                              ad_ctx->sdap_id_ctx->conn,
++    ret = sdap_id_setup_tasks(bectx,
++                              ad_ctx->sdap_id_ctx,
+                               ad_ctx->sdap_id_ctx->opts->sdom,
+                               ad_enumeration_send,
+-                              ad_enumeration_recv);
++                              ad_enumeration_recv,
++                              ad_ctx);
+     if (ret != EOK) {
+         goto done;
+     }
+diff --git a/src/providers/ad/ad_subdomains.c b/src/providers/ad/ad_subdomains.c
+index 348561a85524c203293c713d3f31552a99d74a43..e7871cc32407893948fe1b2803258d68c70889c1 100644
+--- a/src/providers/ad/ad_subdomains.c
++++ b/src/providers/ad/ad_subdomains.c
+@@ -177,10 +177,12 @@ ad_subdom_ad_ctx_new(struct be_ctx *be_ctx,
+         return EFAULT;
+     }
+ 
+-    ret = sdap_id_setup_tasks(ad_id_ctx->sdap_id_ctx,
+-                              ad_id_ctx->ldap_ctx, sdom,
++    ret = sdap_id_setup_tasks(be_ctx,
++                              ad_id_ctx->sdap_id_ctx,
++                              sdom,
+                               ldap_enumeration_send,
+-                              ldap_enumeration_recv);
++                              ldap_enumeration_recv,
++                              ad_id_ctx->sdap_id_ctx);
+     if (ret != EOK) {
+         talloc_free(ad_options);
+         return ret;
+diff --git a/src/providers/ipa/ipa_subdomains.c b/src/providers/ipa/ipa_subdomains.c
+index d9c204451f1b734ee98ce4c48f3f139731e47dec..88b6ba52538be83417e98c9a5dd033bea87ebe4b 100644
+--- a/src/providers/ipa/ipa_subdomains.c
++++ b/src/providers/ipa/ipa_subdomains.c
+@@ -183,10 +183,12 @@ ipa_ad_ctx_new(struct be_ctx *be_ctx,
+         return EFAULT;
+     }
+ 
+-    ret = sdap_id_setup_tasks(ad_id_ctx->sdap_id_ctx,
+-                              ad_id_ctx->ldap_ctx, sdom,
++    ret = sdap_id_setup_tasks(be_ctx,
++                              ad_id_ctx->sdap_id_ctx,
++                              sdom,
+                               ldap_enumeration_send,
+-                              ldap_enumeration_recv);
++                              ldap_enumeration_recv,
++                              ad_id_ctx->sdap_id_ctx);
+     if (ret != EOK) {
+         talloc_free(ad_options);
+         return ret;
+diff --git a/src/providers/ldap/ldap_common.c b/src/providers/ldap/ldap_common.c
+index 4c94937aad9e25bd1cd0b6d573da2982b0f1be05..e799c783c118309409faca0294eadc4736d15108 100644
+--- a/src/providers/ldap/ldap_common.c
++++ b/src/providers/ldap/ldap_common.c
+@@ -974,16 +974,18 @@ void sdap_mark_offline(struct sdap_id_ctx *ctx)
+ 
+ int ldap_id_setup_tasks(struct sdap_id_ctx *ctx)
+ {
+-    return sdap_id_setup_tasks(ctx, ctx->conn, ctx->opts->sdom,
++    return sdap_id_setup_tasks(ctx->be, ctx, ctx->opts->sdom,
+                                ldap_enumeration_send,
+-                               ldap_enumeration_recv);
++                               ldap_enumeration_recv,
++                               ctx);
+ }
+ 
+-int sdap_id_setup_tasks(struct sdap_id_ctx *ctx,
+-                        struct sdap_id_conn_ctx *conn,
++int sdap_id_setup_tasks(struct be_ctx *be_ctx,
++                        struct sdap_id_ctx *ctx,
+                         struct sdap_domain *sdom,
+                         be_ptask_send_t send_fn,
+-                        be_ptask_recv_t recv_fn)
++                        be_ptask_recv_t recv_fn,
++                        void *pvt)
+ {
+     int ret;
+ 
+@@ -991,7 +993,8 @@ int sdap_id_setup_tasks(struct sdap_id_ctx *ctx,
+     if (sdom->dom->enumerate) {
+         DEBUG(SSSDBG_TRACE_FUNC, ("Setting up enumeration for %s\n",
+                                   sdom->dom->name));
+-        ret = ldap_setup_enumeration(ctx, conn, sdom, send_fn, recv_fn);
++        ret = ldap_setup_enumeration(be_ctx, ctx->opts, sdom,
++                                     send_fn, recv_fn, pvt);
+     } else {
+         /* the enumeration task, runs the cleanup process by itself,
+          * but if enumeration is not running we need to schedule it */
+diff --git a/src/providers/ldap/ldap_common.h b/src/providers/ldap/ldap_common.h
+index b3bd950e1dca6df0f5668397d5e5a0796e519862..889d5b118861e4ea3f51ab8a8ea5c5947e2560b9 100644
+--- a/src/providers/ldap/ldap_common.h
++++ b/src/providers/ldap/ldap_common.h
+@@ -95,11 +95,12 @@ void sdap_handle_account_info(struct be_req *breq, struct sdap_id_ctx *ctx,
+ 
+ /* Set up enumeration and/or cleanup */
+ int ldap_id_setup_tasks(struct sdap_id_ctx *ctx);
+-int sdap_id_setup_tasks(struct sdap_id_ctx *ctx,
+-                        struct sdap_id_conn_ctx *conn,
++int sdap_id_setup_tasks(struct be_ctx *be_ctx,
++                        struct sdap_id_ctx *ctx,
+                         struct sdap_domain *sdom,
+                         be_ptask_send_t send_fn,
+-                        be_ptask_recv_t recv_fn);
++                        be_ptask_recv_t recv_fn,
++                        void *pvt);
+ 
+ struct tevent_req *
+ sdap_handle_acct_req_send(TALLOC_CTX *mem_ctx,
+@@ -177,16 +178,16 @@ int ldap_get_autofs_options(TALLOC_CTX *memctx,
+  * structure that contains the request data
+  */
+ struct ldap_enum_ctx {
+-    struct sdap_id_ctx *ctx;
+     struct sdap_domain *sdom;
+-    struct sdap_id_conn_ctx *conn;
++    void *pvt;
+ };
+ 
+-errno_t ldap_setup_enumeration(struct sdap_id_ctx *ctx,
+-                               struct sdap_id_conn_ctx *conn,
++errno_t ldap_setup_enumeration(struct be_ctx *be_ctx,
++                               struct sdap_options *opts,
+                                struct sdap_domain *sdom,
+                                be_ptask_send_t send_fn,
+-                               be_ptask_recv_t recv_fn);
++                               be_ptask_recv_t recv_fn,
++                               void *pvt);
+ struct tevent_req *
+ ldap_enumeration_send(TALLOC_CTX *mem_ctx,
+                       struct tevent_context *ev,
+diff --git a/src/providers/ldap/ldap_id_enum.c b/src/providers/ldap/ldap_id_enum.c
+index 8cccaa916a24beb10fe692d9a0e09f5f47ceb6f7..c791496a6143b23118bf17a58f738fb0bfb5f95a 100644
+--- a/src/providers/ldap/ldap_id_enum.c
++++ b/src/providers/ldap/ldap_id_enum.c
+@@ -27,11 +27,12 @@
+ #include "providers/ldap/ldap_common.h"
+ #include "providers/ldap/sdap_async_enum.h"
+ 
+-errno_t ldap_setup_enumeration(struct sdap_id_ctx *ctx,
+-                               struct sdap_id_conn_ctx *conn,
++errno_t ldap_setup_enumeration(struct be_ctx *be_ctx,
++                               struct sdap_options *opts,
+                                struct sdap_domain *sdom,
+                                be_ptask_send_t send_fn,
+-                               be_ptask_recv_t recv_fn)
++                               be_ptask_recv_t recv_fn,
++                               void *pvt)
+ {
+     errno_t ret;
+     time_t first_delay;
+@@ -60,17 +61,16 @@ errno_t ldap_setup_enumeration(struct sdap_id_ctx *ctx,
+         first_delay = 0;
+     }
+ 
+-    period = dp_opt_get_int(ctx->opts->basic, SDAP_ENUM_REFRESH_TIMEOUT);
++    period = dp_opt_get_int(opts->basic, SDAP_ENUM_REFRESH_TIMEOUT);
+ 
+     ectx = talloc(sdom, struct ldap_enum_ctx);
+     if (ectx == NULL) {
+         return ENOMEM;
+     }
+-    ectx->ctx = ctx;
+     ectx->sdom = sdom;
+-    ectx->conn = conn;
++    ectx->pvt = pvt;
+ 
+-    ret = be_ptask_create(sdom, ctx->be,
++    ret = be_ptask_create(sdom, be_ctx,
+                           period,                   /* period */
+                           first_delay,              /* first_delay */
+                           5,                        /* enabled delay */
+@@ -91,6 +91,7 @@ errno_t ldap_setup_enumeration(struct sdap_id_ctx *ctx,
+ 
+ struct ldap_enumeration_state {
+     struct ldap_enum_ctx *ectx;
++    struct sdap_id_ctx *id_ctx;
+     struct sss_domain_info *dom;
+ };
+ 
+@@ -118,14 +119,16 @@ ldap_enumeration_send(TALLOC_CTX *mem_ctx,
+ 
+     ectx = talloc_get_type(pvt, struct ldap_enum_ctx);
+     if (ectx == NULL) {
++        DEBUG(SSSDBG_CRIT_FAILURE, ("Cannot retrieve ldap_enum_ctx!\n"));
+         ret = EFAULT;
+         goto fail;
+     }
+     state->ectx = ectx;
+     state->dom = ectx->sdom->dom;
++    state->id_ctx = talloc_get_type_abort(ectx->pvt, struct sdap_id_ctx);
+ 
+-    subreq = sdap_dom_enum_send(ectx, ev, ectx->ctx, ectx->sdom,
+-                                ectx->conn);
++    subreq = sdap_dom_enum_send(ectx, ev, state->id_ctx, ectx->sdom,
++                                state->id_ctx->conn);
+     if (subreq == NULL) {
+         /* The ptask API will reschedule the enumeration on its own on
+          * failure */
+-- 
+1.8.4.2
+
diff --git a/SOURCES/0067-LDAP-Add-enum-request-with-custom-connection.patch b/SOURCES/0067-LDAP-Add-enum-request-with-custom-connection.patch
new file mode 100644
index 0000000..04b5e24
--- /dev/null
+++ b/SOURCES/0067-LDAP-Add-enum-request-with-custom-connection.patch
@@ -0,0 +1,482 @@
+From 431198674303beea2a6a25af6d3fa4e852995b26 Mon Sep 17 00:00:00 2001
+From: Jakub Hrozek <jhrozek@redhat.com>
+Date: Mon, 16 Dec 2013 02:41:53 +0100
+Subject: [PATCH 67/71] LDAP: Add enum request with custom connection
+
+This commit changes the enumerate-sdap-domain request to accept a
+connection context per object that can be enumerated. Internally in the
+request, an sdap_id_op is also created per enumerated object type.
+
+This change will allow i.e. users to be enumerated using GC connection,
+while keeping the LDAP connection for groups and services.
+---
+ src/providers/ldap/sdap_async_enum.c | 309 +++++++++++++++++++++--------------
+ src/providers/ldap/sdap_async_enum.h |  11 ++
+ 2 files changed, 193 insertions(+), 127 deletions(-)
+
+diff --git a/src/providers/ldap/sdap_async_enum.c b/src/providers/ldap/sdap_async_enum.c
+index 8487f9a13b279bc71f633590bbab163945fc8f7c..cbc56be20526e6c2323f9fd1b49038dd4bf13fe5 100644
+--- a/src/providers/ldap/sdap_async_enum.c
++++ b/src/providers/ldap/sdap_async_enum.c
+@@ -47,49 +47,56 @@ static struct tevent_req *enum_groups_send(TALLOC_CTX *memctx,
+                                           bool purge);
+ static errno_t enum_groups_recv(struct tevent_req *req);
+ 
+-/* ==Enumeration-Request==================================================== */
+-struct sdap_dom_enum_state {
++/* ==Enumeration-Request-with-connections=================================== */
++struct sdap_dom_enum_ex_state {
+     struct tevent_context *ev;
+     struct sdap_id_ctx *ctx;
+     struct sdap_domain *sdom;
+-    struct sdap_id_conn_ctx *conn;
+-    struct sdap_id_op *op;
++
++    struct sdap_id_conn_ctx *user_conn;
++    struct sdap_id_conn_ctx *group_conn;
++    struct sdap_id_conn_ctx *svc_conn;
++    struct sdap_id_op *user_op;
++    struct sdap_id_op *group_op;
++    struct sdap_id_op *svc_op;
+ 
+     bool purge;
+ };
+ 
+-static errno_t sdap_dom_enum_retry(struct tevent_req *req);
+-static void sdap_dom_enum_conn_done(struct tevent_req *subreq);
+-static void sdap_dom_enum_users_done(struct tevent_req *subreq);
+-static void sdap_dom_enum_groups_done(struct tevent_req *subreq);
+-static void sdap_dom_enum_services_done(struct tevent_req *subreq);
++static errno_t sdap_dom_enum_ex_retry(struct tevent_req *req,
++                                      struct sdap_id_op *op,
++                                      tevent_req_fn tcb);
++static bool sdap_dom_enum_ex_connected(struct tevent_req *subreq);
++static void sdap_dom_enum_ex_get_users(struct tevent_req *subreq);
++static void sdap_dom_enum_ex_users_done(struct tevent_req *subreq);
++static void sdap_dom_enum_ex_get_groups(struct tevent_req *subreq);
++static void sdap_dom_enum_ex_groups_done(struct tevent_req *subreq);
++static void sdap_dom_enum_ex_get_svcs(struct tevent_req *subreq);
++static void sdap_dom_enum_ex_svcs_done(struct tevent_req *subreq);
+ 
+ struct tevent_req *
+-sdap_dom_enum_send(TALLOC_CTX *memctx,
+-                   struct tevent_context *ev,
+-                   struct sdap_id_ctx *ctx,
+-                   struct sdap_domain *sdom,
+-                   struct sdap_id_conn_ctx *conn)
++sdap_dom_enum_ex_send(TALLOC_CTX *memctx,
++                      struct tevent_context *ev,
++                      struct sdap_id_ctx *ctx,
++                      struct sdap_domain *sdom,
++                      struct sdap_id_conn_ctx *user_conn,
++                      struct sdap_id_conn_ctx *group_conn,
++                      struct sdap_id_conn_ctx *svc_conn)
+ {
+     struct tevent_req *req;
+-    struct sdap_dom_enum_state *state;
++    struct sdap_dom_enum_ex_state *state;
+     int t;
+     errno_t ret;
+ 
+-    req = tevent_req_create(ctx, &state, struct sdap_dom_enum_state);
+-    if (!req) return NULL;
++    req = tevent_req_create(ctx, &state, struct sdap_dom_enum_ex_state);
++    if (req == NULL) return NULL;
+ 
+     state->ev = ev;
+     state->ctx = ctx;
+     state->sdom = sdom;
+-    state->conn = conn;
+-    state->op = sdap_id_op_create(state, state->ctx->conn->conn_cache);
+-    if (!state->op) {
+-        DEBUG(SSSDBG_CRIT_FAILURE, ("sdap_id_op_create failed\n"));
+-        ret = EIO;
+-        goto fail;
+-    }
+-
++    state->user_conn = user_conn;
++    state->group_conn = group_conn;
++    state->svc_conn = svc_conn;
+     sdom->last_enum = tevent_timeval_current();
+ 
+     t = dp_opt_get_int(ctx->opts->basic, SDAP_CACHE_PURGE_TIMEOUT);
+@@ -97,9 +104,17 @@ sdap_dom_enum_send(TALLOC_CTX *memctx,
+         state->purge = true;
+     }
+ 
+-    ret = sdap_dom_enum_retry(req);
++    state->user_op = sdap_id_op_create(state, user_conn->conn_cache);
++    if (state->user_op == NULL) {
++        DEBUG(SSSDBG_CRIT_FAILURE, ("sdap_id_op_create failed for users\n"));
++        ret = EIO;
++        goto fail;
++    }
++
++    ret = sdap_dom_enum_ex_retry(req, state->user_op,
++                                 sdap_dom_enum_ex_get_users);
+     if (ret != EOK) {
+-        DEBUG(SSSDBG_OP_FAILURE, ("ldap_id_enumerate_retry failed\n"));
++        DEBUG(SSSDBG_OP_FAILURE, ("sdap_dom_enum_ex_retry failed\n"));
+         goto fail;
+     }
+ 
+@@ -111,31 +126,32 @@ fail:
+     return req;
+ }
+ 
+-static errno_t sdap_dom_enum_retry(struct tevent_req *req)
++static errno_t sdap_dom_enum_ex_retry(struct tevent_req *req,
++                                      struct sdap_id_op *op,
++                                      tevent_req_fn tcb)
+ {
+-    struct sdap_dom_enum_state *state = tevent_req_data(req,
+-                                                   struct sdap_dom_enum_state);
++    struct sdap_dom_enum_ex_state *state = tevent_req_data(req,
++                                                struct sdap_dom_enum_ex_state);
+     struct tevent_req *subreq;
+     errno_t ret;
+ 
+-    subreq = sdap_id_op_connect_send(state->op, state, &ret);
++    subreq = sdap_id_op_connect_send(op, state, &ret);
+     if (subreq == NULL) {
+         DEBUG(SSSDBG_OP_FAILURE,
+               ("sdap_id_op_connect_send failed: %d\n", ret));
+         return ret;
+     }
+ 
+-    tevent_req_set_callback(subreq, sdap_dom_enum_conn_done, req);
++    tevent_req_set_callback(subreq, tcb, req);
+     return EOK;
+ }
+ 
+-static void sdap_dom_enum_conn_done(struct tevent_req *subreq)
++static bool sdap_dom_enum_ex_connected(struct tevent_req *subreq)
+ {
++    errno_t ret;
++    int dp_error;
+     struct tevent_req *req = tevent_req_callback_data(subreq,
+                                                       struct tevent_req);
+-    struct sdap_dom_enum_state *state = tevent_req_data(req,
+-                                                   struct sdap_dom_enum_state);
+-    int ret, dp_error;
+ 
+     ret = sdap_id_op_connect_recv(subreq, &dp_error);
+     talloc_zfree(subreq);
+@@ -150,150 +166,173 @@ static void sdap_dom_enum_conn_done(struct tevent_req *subreq)
+                    "LDAP server: (%d)[%s]\n", ret, strerror(ret)));
+             tevent_req_error(req, ret);
+         }
++        return false;
++    }
++
++    return true;
++}
++
++static void sdap_dom_enum_ex_get_users(struct tevent_req *subreq)
++{
++    struct tevent_req *req = tevent_req_callback_data(subreq,
++                                                      struct tevent_req);
++    struct sdap_dom_enum_ex_state *state = tevent_req_data(req,
++                                                struct sdap_dom_enum_ex_state);
++
++    if (sdap_dom_enum_ex_connected(subreq) == false) {
+         return;
+     }
+ 
+     subreq = enum_users_send(state, state->ev,
+                              state->ctx, state->sdom,
+-                             state->op, state->purge);
++                             state->user_op, state->purge);
+     if (subreq == NULL) {
+         tevent_req_error(req, ENOMEM);
+         return;
+     }
+-    tevent_req_set_callback(subreq, sdap_dom_enum_users_done, req);
++    tevent_req_set_callback(subreq, sdap_dom_enum_ex_users_done, req);
+ }
+ 
+-static void sdap_dom_enum_users_done(struct tevent_req *subreq)
++static void sdap_dom_enum_ex_users_done(struct tevent_req *subreq)
+ {
+     struct tevent_req *req = tevent_req_callback_data(subreq,
+                                                       struct tevent_req);
+-    struct sdap_dom_enum_state *state = tevent_req_data(req,
+-                                                   struct sdap_dom_enum_state);
+-    uint64_t err = 0;
+-    int ret, dp_error = DP_ERR_FATAL;
++    struct sdap_dom_enum_ex_state *state = tevent_req_data(req,
++                                                struct sdap_dom_enum_ex_state);
++    errno_t ret;
++    int dp_error;
+ 
+-    err = enum_users_recv(subreq);
++    ret = enum_users_recv(subreq);
+     talloc_zfree(subreq);
+-    if (err != EOK && err != ENOENT) {
+-        /* We call sdap_id_op_done only on error
+-         * as the connection is reused by groups enumeration */
+-        ret = sdap_id_op_done(state->op, (int)err, &dp_error);
+-        if (dp_error == DP_ERR_OK) {
+-            /* retry */
+-            ret = sdap_dom_enum_retry(req);
+-            if (ret == EOK) {
+-                return;
+-            }
+-
+-            dp_error = DP_ERR_FATAL;
+-        }
+-
+-        if (dp_error == DP_ERR_OFFLINE) {
+-            tevent_req_done(req);
+-        } else {
+-            DEBUG(SSSDBG_OP_FAILURE,
+-                  ("User enumeration failed with: (%d)[%s]\n",
+-                   ret, strerror(ret)));
++    ret = sdap_id_op_done(state->user_op, ret, &dp_error);
++    if (dp_error == DP_ERR_OK && ret != EOK) {
++        /* retry */
++        ret = sdap_dom_enum_ex_retry(req, state->user_op,
++                                     sdap_dom_enum_ex_get_users);
++        if (ret != EOK) {
+             tevent_req_error(req, ret);
++            return;
+         }
+         return;
+     }
+ 
++    state->group_op = sdap_id_op_create(state, state->group_conn->conn_cache);
++    if (state->group_op == NULL) {
++        DEBUG(SSSDBG_CRIT_FAILURE, ("sdap_id_op_create failed for groups\n"));
++        tevent_req_error(req, EIO);
++        return;
++    }
++
++    ret = sdap_dom_enum_ex_retry(req, state->group_op,
++                                 sdap_dom_enum_ex_get_groups);
++    if (ret != EOK) {
++        tevent_req_error(req, ret);
++        return;
++    }
++
++    /* Continues to sdap_dom_enum_ex_get_groups */
++}
++
++static void sdap_dom_enum_ex_get_groups(struct tevent_req *subreq)
++{
++    struct tevent_req *req = tevent_req_callback_data(subreq,
++                                                      struct tevent_req);
++    struct sdap_dom_enum_ex_state *state = tevent_req_data(req,
++                                                struct sdap_dom_enum_ex_state);
++
++    if (sdap_dom_enum_ex_connected(subreq) == false) {
++        return;
++    }
++
+     subreq = enum_groups_send(state, state->ev, state->ctx,
+                               state->sdom,
+-                              state->op, state->purge);
++                              state->group_op, state->purge);
+     if (subreq == NULL) {
+         tevent_req_error(req, ENOMEM);
+         return;
+     }
+-    tevent_req_set_callback(subreq, sdap_dom_enum_groups_done, req);
++    tevent_req_set_callback(subreq, sdap_dom_enum_ex_groups_done, req);
+ }
+ 
+-static void sdap_dom_enum_groups_done(struct tevent_req *subreq)
++static void sdap_dom_enum_ex_groups_done(struct tevent_req *subreq)
+ {
+     struct tevent_req *req = tevent_req_callback_data(subreq,
+                                                       struct tevent_req);
+-    struct sdap_dom_enum_state *state = tevent_req_data(req,
+-                                                   struct sdap_dom_enum_state);
+-    uint64_t err = 0;
+-    int ret, dp_error = DP_ERR_FATAL;
++    struct sdap_dom_enum_ex_state *state = tevent_req_data(req,
++                                                struct sdap_dom_enum_ex_state);
++    int ret;
++    int dp_error;
+ 
+-    err = enum_groups_recv(subreq);
++    ret = enum_groups_recv(subreq);
+     talloc_zfree(subreq);
+-    if (err != EOK && err != ENOENT) {
+-        /* We call sdap_id_op_done only on error
+-         * as the connection is reused by services enumeration */
+-        ret = sdap_id_op_done(state->op, (int)err, &dp_error);
+-        if (dp_error == DP_ERR_OK && ret != EOK) {
+-            /* retry */
+-            ret = sdap_dom_enum_retry(req);
+-            if (ret == EOK) {
+-                return;
+-            }
+-
+-            dp_error = DP_ERR_FATAL;
+-        }
+-
++    ret = sdap_id_op_done(state->group_op, ret, &dp_error);
++    if (dp_error == DP_ERR_OK && ret != EOK) {
++        /* retry */
++        ret = sdap_dom_enum_ex_retry(req, state->group_op,
++                                     sdap_dom_enum_ex_get_groups);
+         if (ret != EOK) {
+-            if (dp_error == DP_ERR_OFFLINE) {
+-                tevent_req_done(req);
+-            } else {
+-                DEBUG(SSSDBG_OP_FAILURE,
+-                      ("Group enumeration failed with: (%d)[%s]\n",
+-                       ret, strerror(ret)));
+-                tevent_req_error(req, ret);
+-            }
+-
++            tevent_req_error(req, ret);
+             return;
+         }
++        return;
++    }
++
++
++    state->svc_op = sdap_id_op_create(state, state->svc_conn->conn_cache);
++    if (state->svc_op == NULL) {
++        DEBUG(SSSDBG_CRIT_FAILURE, ("sdap_id_op_create failed for svcs\n"));
++        tevent_req_error(req, EIO);
++        return;
++    }
++
++    ret = sdap_dom_enum_ex_retry(req, state->svc_op,
++                                 sdap_dom_enum_ex_get_svcs);
++    if (ret != EOK) {
++        tevent_req_error(req, ret);
++        return;
++    }
++}
++
++static void sdap_dom_enum_ex_get_svcs(struct tevent_req *subreq)
++{
++    struct tevent_req *req = tevent_req_callback_data(subreq,
++                                                      struct tevent_req);
++    struct sdap_dom_enum_ex_state *state = tevent_req_data(req,
++                                                struct sdap_dom_enum_ex_state);
++
++    if (sdap_dom_enum_ex_connected(subreq) == false) {
++        return;
+     }
+ 
+     subreq = enum_services_send(state, state->ev, state->ctx,
+-                                state->op, state->purge);
++                                state->svc_op, state->purge);
+     if (!subreq) {
+         tevent_req_error(req, ENOMEM);
+         return;
+     }
+-    tevent_req_set_callback(subreq, sdap_dom_enum_services_done, req);
++    tevent_req_set_callback(subreq, sdap_dom_enum_ex_svcs_done, req);
+ }
+ 
+-static void sdap_dom_enum_services_done(struct tevent_req *subreq)
++static void sdap_dom_enum_ex_svcs_done(struct tevent_req *subreq)
+ {
+-    errno_t ret;
+-    int dp_error = DP_ERR_FATAL;
+     struct tevent_req *req = tevent_req_callback_data(subreq,
+                                                       struct tevent_req);
+-    struct sdap_dom_enum_state *state = tevent_req_data(req,
+-                                                   struct sdap_dom_enum_state);
++    struct sdap_dom_enum_ex_state *state = tevent_req_data(req,
++                                                struct sdap_dom_enum_ex_state);
++    int ret;
++    int dp_error;
+ 
+     ret = enum_services_recv(subreq);
+     talloc_zfree(subreq);
+-    if (ret == ENOENT) ret = EOK;
+-
+-    /* All enumerations are complete, so conclude the
+-     * id_op
+-     */
+-    ret = sdap_id_op_done(state->op, ret, &dp_error);
++    ret = sdap_id_op_done(state->svc_op, ret, &dp_error);
+     if (dp_error == DP_ERR_OK && ret != EOK) {
+         /* retry */
+-        ret = sdap_dom_enum_retry(req);
+-        if (ret == EOK) {
+-            return;
+-        }
+-
+-        dp_error = DP_ERR_FATAL;
+-    }
+-
+-    if (ret != EOK) {
+-        if (dp_error == DP_ERR_OFFLINE) {
+-            tevent_req_done(req);
+-        } else {
+-            DEBUG(SSSDBG_MINOR_FAILURE,
+-                  ("Service enumeration failed with: (%d)[%s]\n",
+-                   ret, strerror(ret)));
++        ret = sdap_dom_enum_ex_retry(req, state->user_op,
++                                     sdap_dom_enum_ex_get_svcs);
++        if (ret != EOK) {
+             tevent_req_error(req, ret);
++            return;
+         }
+-
+         return;
+     }
+ 
+@@ -323,11 +362,27 @@ static void sdap_dom_enum_services_done(struct tevent_req *subreq)
+     tevent_req_done(req);
+ }
+ 
++errno_t sdap_dom_enum_ex_recv(struct tevent_req *req)
++{
++    TEVENT_REQ_RETURN_ON_ERROR(req);
++
++    return EOK;
++}
++
++/* ==Enumeration-Request==================================================== */
++struct tevent_req *
++sdap_dom_enum_send(TALLOC_CTX *memctx,
++                   struct tevent_context *ev,
++                   struct sdap_id_ctx *ctx,
++                   struct sdap_domain *sdom,
++                   struct sdap_id_conn_ctx *conn)
++{
++    return sdap_dom_enum_ex_send(memctx, ev, ctx, sdom, conn, conn, conn);
++}
++
+ errno_t sdap_dom_enum_recv(struct tevent_req *req)
+ {
+-    TEVENT_REQ_RETURN_ON_ERROR(req);
+-
+-    return EOK;
++    return sdap_dom_enum_ex_recv(req);
+ }
+ 
+ /* ==User-Enumeration===================================================== */
+diff --git a/src/providers/ldap/sdap_async_enum.h b/src/providers/ldap/sdap_async_enum.h
+index 04ec8c6dcbec4bcce0de67b9e10acc857c9e9416..2da38f988913fa0d6f252697925e50e05eb794a6 100644
+--- a/src/providers/ldap/sdap_async_enum.h
++++ b/src/providers/ldap/sdap_async_enum.h
+@@ -27,6 +27,17 @@
+ #define _SDAP_ASYNC_ENUM_H_
+ 
+ struct tevent_req *
++sdap_dom_enum_ex_send(TALLOC_CTX *memctx,
++                      struct tevent_context *ev,
++                      struct sdap_id_ctx *ctx,
++                      struct sdap_domain *sdom,
++                      struct sdap_id_conn_ctx *user_conn,
++                      struct sdap_id_conn_ctx *group_conn,
++                      struct sdap_id_conn_ctx *svc_conn);
++
++errno_t sdap_dom_enum_ex_recv(struct tevent_req *req);
++
++struct tevent_req *
+ sdap_dom_enum_send(TALLOC_CTX *memctx,
+                    struct tevent_context *ev,
+                    struct sdap_id_ctx *ctx,
+-- 
+1.8.4.2
+
diff --git a/SOURCES/0068-AD-Enumerate-users-from-GC-other-entities-from-LDAP.patch b/SOURCES/0068-AD-Enumerate-users-from-GC-other-entities-from-LDAP.patch
new file mode 100644
index 0000000..b8b61b9
--- /dev/null
+++ b/SOURCES/0068-AD-Enumerate-users-from-GC-other-entities-from-LDAP.patch
@@ -0,0 +1,57 @@
+From 1a077f0af935497b972a763a7027924a65476b01 Mon Sep 17 00:00:00 2001
+From: Jakub Hrozek <jhrozek@redhat.com>
+Date: Tue, 28 Jan 2014 13:59:44 +0100
+Subject: [PATCH 68/71] AD: Enumerate users from GC, other entities from LDAP
+
+---
+ src/providers/ad/ad_id.c | 20 +++++++++++++++++---
+ 1 file changed, 17 insertions(+), 3 deletions(-)
+
+diff --git a/src/providers/ad/ad_id.c b/src/providers/ad/ad_id.c
+index 99383c13bdadfe9eb2af9f9323ca19a9759d4620..a47aa4f75ab348b0f4597fea264d770b5abe3184 100644
+--- a/src/providers/ad/ad_id.c
++++ b/src/providers/ad/ad_id.c
+@@ -526,6 +526,7 @@ ad_enumeration_master_done(struct tevent_req *subreq)
+     char *flat_name;
+     char *master_sid;
+     char *forest;
++    struct sdap_id_conn_ctx *user_conn;
+ 
+     ret = ad_master_domain_recv(subreq, state,
+                                 &flat_name, &master_sid, &forest);
+@@ -544,8 +545,21 @@ ad_enumeration_master_done(struct tevent_req *subreq)
+         return;
+     }
+ 
+-    subreq = sdap_dom_enum_send(state, state->ev, state->id_ctx->sdap_id_ctx,
+-                                state->sdom, state->id_ctx->ldap_ctx);
++    if (dp_opt_get_bool(state->id_ctx->ad_options->basic, AD_ENABLE_GC)) {
++        user_conn = state->id_ctx->gc_ctx;
++    } else {
++        user_conn = state->id_ctx->ldap_ctx;
++    }
++
++    /* Groups are searched for in LDAP, users in GC. Services (if present,
++     * which is unlikely in AD) from LDAP as well
++     */
++    subreq = sdap_dom_enum_ex_send(state, state->ev,
++                                   state->id_ctx->sdap_id_ctx,
++                                   state->sdom,
++                                   user_conn,                /* Users    */
++                                   state->id_ctx->ldap_ctx,  /* Groups   */
++                                   state->id_ctx->ldap_ctx); /* Services */
+     if (subreq == NULL) {
+         /* The ptask API will reschedule the enumeration on its own on
+          * failure */
+@@ -566,7 +580,7 @@ ad_enumeration_done(struct tevent_req *subreq)
+     struct ad_enumeration_state *state = tevent_req_data(req,
+                                                 struct ad_enumeration_state);
+ 
+-    ret = sdap_dom_enum_recv(subreq);
++    ret = sdap_dom_enum_ex_recv(subreq);
+     talloc_zfree(subreq);
+     if (ret != EOK) {
+         DEBUG(SSSDBG_OP_FAILURE,
+-- 
+1.8.4.2
+
diff --git a/SOURCES/0069-LDAP-Don-t-clobber-original_member-during-enumeratio.patch b/SOURCES/0069-LDAP-Don-t-clobber-original_member-during-enumeratio.patch
new file mode 100644
index 0000000..7523a4a
--- /dev/null
+++ b/SOURCES/0069-LDAP-Don-t-clobber-original_member-during-enumeratio.patch
@@ -0,0 +1,79 @@
+From 37722379349e257e2e77583e515ceafa3eee804c Mon Sep 17 00:00:00 2001
+From: Jakub Hrozek <jhrozek@redhat.com>
+Date: Tue, 21 Jan 2014 23:40:17 +0100
+Subject: [PATCH 69/71] LDAP: Don't clobber original_member during enumeration
+
+---
+ src/providers/ldap/sdap_async_groups.c | 17 +++++++++++------
+ 1 file changed, 11 insertions(+), 6 deletions(-)
+
+diff --git a/src/providers/ldap/sdap_async_groups.c b/src/providers/ldap/sdap_async_groups.c
+index 9eece9a6e4baaf302a28b57a63dae45a0741136c..4ed7d4ab9c0c932da49b244f061329a334719159 100644
+--- a/src/providers/ldap/sdap_async_groups.c
++++ b/src/providers/ldap/sdap_async_groups.c
+@@ -807,6 +807,7 @@ static int sdap_save_groups(TALLOC_CTX *memctx,
+                             int num_groups,
+                             bool populate_members,
+                             hash_table_t *ghosts,
++                            bool save_orig_member,
+                             char **_usn_value)
+ {
+     TALLOC_CTX *tmpctx;
+@@ -864,9 +865,9 @@ static int sdap_save_groups(TALLOC_CTX *memctx,
+         usn_value = NULL;
+ 
+         /* if 2 pass savemembers = false */
+-        ret = sdap_save_group(tmpctx, sysdb,
+-                              opts, dom, groups[i],
+-                              populate_members, has_nesting,
++        ret = sdap_save_group(tmpctx, sysdb, opts, dom, groups[i],
++                              populate_members,
++                              has_nesting && save_orig_member,
+                               ghosts, &usn_value, now);
+ 
+         /* Do not fail completely on errors.
+@@ -1835,7 +1836,7 @@ static void sdap_get_groups_process(struct tevent_req *subreq)
+                   "to allow unrolling of nested groups.\n"));
+         ret = sdap_save_groups(state, state->sysdb, state->dom, state->opts,
+                                state->groups, state->count, false,
+-                               NULL, NULL);
++                               NULL, true, NULL);
+         if (ret) {
+             DEBUG(2, ("Failed to store groups.\n"));
+             tevent_req_error(req, ret);
+@@ -1887,10 +1888,14 @@ static void sdap_get_groups_done(struct tevent_req *subreq)
+ 
+         /* If ignore_group_members is set for the domain, don't update
+          * group memberships in the cache.
++         *
++         * If enumeration is on, don't overwrite orig_members as they've been
++         * saved earlier.
+          */
+         ret = sdap_save_groups(state, state->sysdb, state->dom, state->opts,
+                                state->groups, state->count,
+                                !state->dom->ignore_group_members, NULL,
++                               !state->enumeration,
+                                &state->higher_usn);
+         if (ret) {
+             DEBUG(2, ("Failed to store groups.\n"));
+@@ -2014,7 +2019,7 @@ static void sdap_ad_match_rule_members_process(struct tevent_req *subreq)
+     /* Now save the group, users and ghosts to the cache */
+     ret = sdap_save_groups(tmp_ctx, state->sysdb, state->dom,
+                            state->opts, state->groups, 1,
+-                           false, ghosts, NULL);
++                           false, ghosts, true, NULL);
+     if (ret != EOK) {
+         DEBUG(SSSDBG_MINOR_FAILURE,
+               ("Could not save group to the cache: [%s]\n",
+@@ -2090,7 +2095,7 @@ static void sdap_nested_done(struct tevent_req *subreq)
+     }
+ 
+     ret = sdap_save_groups(state, state->sysdb, state->dom, state->opts,
+-                           groups, group_count, false, ghosts,
++                           groups, group_count, false, ghosts, true,
+                            &state->higher_usn);
+     if (ret != EOK) {
+         goto fail;
+-- 
+1.8.4.2
+
diff --git a/SOURCES/0070-DB-Add-sss_ldb_el_to_string_list.patch b/SOURCES/0070-DB-Add-sss_ldb_el_to_string_list.patch
new file mode 100644
index 0000000..8b9b419
--- /dev/null
+++ b/SOURCES/0070-DB-Add-sss_ldb_el_to_string_list.patch
@@ -0,0 +1,160 @@
+From 329165182d0decee35f3837c1d2ad899f99e9950 Mon Sep 17 00:00:00 2001
+From: Jakub Hrozek <jhrozek@redhat.com>
+Date: Tue, 28 Jan 2014 14:51:58 +0100
+Subject: [PATCH 70/71] DB: Add sss_ldb_el_to_string_list
+
+---
+ src/db/sysdb.c          | 41 ++++++++++++++++++++++++++---------------
+ src/db/sysdb.h          |  2 ++
+ src/tests/sysdb-tests.c | 49 +++++++++++++++++++++++++++++++++++++++++++++++++
+ 3 files changed, 77 insertions(+), 15 deletions(-)
+
+diff --git a/src/db/sysdb.c b/src/db/sysdb.c
+index 0e07ed60858298a1ac85d06146ccb98c5899a705..592cadc1a322ee8504c407a508e34e7eb9465a15 100644
+--- a/src/db/sysdb.c
++++ b/src/db/sysdb.c
+@@ -466,35 +466,46 @@ errno_t sysdb_attrs_get_bool(struct sysdb_attrs *attrs, const char *name,
+     return EOK;
+ }
+ 
+-int sysdb_attrs_get_string_array(struct sysdb_attrs *attrs, const char *name,
+-                                 TALLOC_CTX *mem_ctx, const char ***string)
++const char **sss_ldb_el_to_string_list(TALLOC_CTX *mem_ctx,
++                                       struct ldb_message_element *el)
+ {
+-    struct ldb_message_element *el;
+-    int ret;
+     unsigned int u;
+     const char **a;
+ 
+-    ret = sysdb_attrs_get_el_ext(attrs, name, false, &el);
+-    if (ret) {
+-        return ret;
+-    }
+-
+-    a = talloc_array(mem_ctx, const char *, el->num_values + 1);
++    a = talloc_zero_array(mem_ctx, const char *, el->num_values + 1);
+     if (a == NULL) {
+-        return ENOMEM;
++        return NULL;
+     }
+ 
+-    memset(a, 0, sizeof(const char *) * (el->num_values + 1));
+-
+-    for(u = 0; u < el->num_values; u++) {
++    for (u = 0; u < el->num_values; u++) {
+         a[u] = talloc_strndup(a, (const char *)el->values[u].data,
+                               el->values[u].length);
+         if (a[u] == NULL) {
+             talloc_free(a);
+-            return ENOMEM;
++            return NULL;
+         }
+     }
+ 
++    return a;
++}
++
++int sysdb_attrs_get_string_array(struct sysdb_attrs *attrs, const char *name,
++                                 TALLOC_CTX *mem_ctx, const char ***string)
++{
++    struct ldb_message_element *el;
++    int ret;
++    const char **a;
++
++    ret = sysdb_attrs_get_el_ext(attrs, name, false, &el);
++    if (ret) {
++        return ret;
++    }
++
++    a = sss_ldb_el_to_string_list(mem_ctx, el);
++    if (a == NULL) {
++        return ENOMEM;
++    }
++
+     *string = a;
+     return EOK;
+ }
+diff --git a/src/db/sysdb.h b/src/db/sysdb.h
+index 9677294b22e47f5169d7631673beec2dbc6117ad..5bd2c50ebff1ca1f4bb11d12e40512d8f460acbb 100644
+--- a/src/db/sysdb.h
++++ b/src/db/sysdb.h
+@@ -288,6 +288,8 @@ int sysdb_attrs_steal_string(struct sysdb_attrs *attrs,
+                              const char *name, char *str);
+ int sysdb_attrs_get_string(struct sysdb_attrs *attrs, const char *name,
+                            const char **string);
++const char **sss_ldb_el_to_string_list(TALLOC_CTX *mem_ctx,
++                                       struct ldb_message_element *el);
+ int sysdb_attrs_get_string_array(struct sysdb_attrs *attrs, const char *name,
+                                  TALLOC_CTX *mem_ctx, const char ***string);
+ errno_t sysdb_attrs_get_bool(struct sysdb_attrs *attrs, const char *name,
+diff --git a/src/tests/sysdb-tests.c b/src/tests/sysdb-tests.c
+index 63ffac82e15849e5f6534462ce7c58b183412acc..69b09c7d4580595854330062e0f549eab1f8cf22 100644
+--- a/src/tests/sysdb-tests.c
++++ b/src/tests/sysdb-tests.c
+@@ -4449,6 +4449,52 @@ START_TEST(test_sysdb_attrs_add_lc_name_alias)
+ }
+ END_TEST
+ 
++START_TEST(test_sysdb_attrs_get_string_array)
++{
++    int ret;
++    struct sysdb_attrs *attrs;
++    const char **list;
++    const char *attrname = "test_attr";
++    TALLOC_CTX *tmp_ctx;
++    struct ldb_message_element *el = NULL;
++
++    tmp_ctx = talloc_new(NULL);
++    fail_unless(tmp_ctx != NULL, "talloc_new failed");
++
++    attrs = sysdb_new_attrs(NULL);
++    fail_unless(attrs != NULL, "sysdb_new_attrs failed");
++
++    ret = sysdb_attrs_add_string(attrs, attrname, "val1");
++    fail_unless(ret == EOK, "sysdb_attrs_add_string failed");
++    ret = sysdb_attrs_add_string(attrs, attrname, "val2");
++    fail_unless(ret == EOK, "sysdb_attrs_add_string failed");
++
++    ret = sysdb_attrs_get_el_ext(attrs, attrname, false, &el);
++    fail_unless(ret == EOK, "sysdb_attrs_get_el_ext failed");
++
++    list = sss_ldb_el_to_string_list(tmp_ctx, el);
++    fail_if(list == NULL, ("sss_ldb_el_to_string_list failed\n"));
++
++    ck_assert_str_eq(list[0], "val1");
++    ck_assert_str_eq(list[1], "val2");
++    fail_unless(list[2] == NULL, "Expected terminated list");
++
++    talloc_free(list);
++
++    ret = sysdb_attrs_get_string_array(attrs, attrname, tmp_ctx, &list);
++    fail_unless(ret == EOK, "sysdb_attrs_get_string_array failed");
++
++    /* This test relies on values keeping the same order. It is the case
++     * with LDB, but if we ever switch from LDB, we need to amend the test
++     */
++    ck_assert_str_eq(list[0], "val1");
++    ck_assert_str_eq(list[1], "val2");
++    fail_unless(list[2] == NULL, "Expected terminated list");
++
++    talloc_free(tmp_ctx);
++}
++END_TEST
++
+ START_TEST(test_sysdb_has_enumerated)
+ {
+     errno_t ret;
+@@ -5217,6 +5263,9 @@ Suite *create_sysdb_suite(void)
+ 
+     tcase_add_test(tc_sysdb, test_sysdb_attrs_add_lc_name_alias);
+ 
++/* ===== UTIL TESTS ===== */
++    tcase_add_test(tc_sysdb, test_sysdb_attrs_get_string_array);
++
+ /* Add all test cases to the test suite */
+     suite_add_tcase(s, tc_sysdb);
+ 
+-- 
+1.8.4.2
+
diff --git a/SOURCES/0071-AD-Establish-cross-domain-memberships-after-enumerat.patch b/SOURCES/0071-AD-Establish-cross-domain-memberships-after-enumerat.patch
new file mode 100644
index 0000000..fc32870
--- /dev/null
+++ b/SOURCES/0071-AD-Establish-cross-domain-memberships-after-enumerat.patch
@@ -0,0 +1,508 @@
+From 8c714cbf1d0ce2cbddc4222ade51e1f93f36dbe8 Mon Sep 17 00:00:00 2001
+From: Jakub Hrozek <jhrozek@redhat.com>
+Date: Wed, 22 Jan 2014 15:21:24 +0100
+Subject: [PATCH 71/71] AD: Establish cross-domain memberships after
+ enumeration finishes
+
+Because domain enumeration currently works for each domain separately,
+the code has to establish cross-domain memberships after all domains are
+enumerated. The code works as follows:
+
+    1) check if any *sub*domains were enumerated. If not, do nothing
+    2) if any of the groups saved had more original members than
+       sysdb members, check if members of these groups can be linked now
+       that all users and groups are saved using the orig_member
+       attribute of the group matched against originalDN member of the
+       user.
+
+Related:
+https://fedorahosted.org/sssd/ticket/2142
+---
+ src/providers/ad/ad_id.c         | 390 +++++++++++++++++++++++++++++++++++++--
+ src/providers/ad/ad_subdomains.c |  11 --
+ 2 files changed, 379 insertions(+), 22 deletions(-)
+
+diff --git a/src/providers/ad/ad_id.c b/src/providers/ad/ad_id.c
+index a47aa4f75ab348b0f4597fea264d770b5abe3184..e3302c15774ab1c24b16cefc274313e447b31e5c 100644
+--- a/src/providers/ad/ad_id.c
++++ b/src/providers/ad/ad_id.c
+@@ -420,10 +420,13 @@ struct ad_enumeration_state {
+     struct tevent_context *ev;
+ 
+     struct sdap_domain *sdom;
++    struct sdap_domain *sditer;
+ };
+ 
+ static void ad_enumeration_conn_done(struct tevent_req *subreq);
+ static void ad_enumeration_master_done(struct tevent_req *subreq);
++static errno_t ad_enum_sdom(struct tevent_req *req, struct sdap_domain *sd,
++                            struct ad_id_ctx *id_ctx);
+ static void ad_enumeration_done(struct tevent_req *subreq);
+ 
+ struct tevent_req *
+@@ -452,6 +455,7 @@ ad_enumeration_send(TALLOC_CTX *mem_ctx,
+     state->ectx = ectx;
+     state->ev = ev;
+     state->sdom = ectx->sdom;
++    state->sditer = state->sdom;
+     state->id_ctx = talloc_get_type(ectx->pvt, struct ad_id_ctx);
+ 
+     state->sdap_op = sdap_id_op_create(state,
+@@ -526,7 +530,6 @@ ad_enumeration_master_done(struct tevent_req *subreq)
+     char *flat_name;
+     char *master_sid;
+     char *forest;
+-    struct sdap_id_conn_ctx *user_conn;
+ 
+     ret = ad_master_domain_recv(subreq, state,
+                                 &flat_name, &master_sid, &forest);
+@@ -545,32 +548,57 @@ ad_enumeration_master_done(struct tevent_req *subreq)
+         return;
+     }
+ 
+-    if (dp_opt_get_bool(state->id_ctx->ad_options->basic, AD_ENABLE_GC)) {
+-        user_conn = state->id_ctx->gc_ctx;
++    ret = ad_enum_sdom(req, state->sdom, state->id_ctx);
++    if (ret != EOK) {
++        DEBUG(SSSDBG_OP_FAILURE,
++                ("Could not enumerate domain %s\n", state->sdom->dom->name));
++        tevent_req_error(req, ret);
++        return;
++    }
++
++    /* Execution will resume in ad_enumeration_done */
++}
++
++static errno_t
++ad_enum_sdom(struct tevent_req *req,
++             struct sdap_domain *sd,
++             struct ad_id_ctx *id_ctx)
++{
++    struct sdap_id_conn_ctx *user_conn;
++    struct tevent_req *subreq;
++    struct ad_enumeration_state *state = tevent_req_data(req,
++                                                struct ad_enumeration_state);
++
++    if (dp_opt_get_bool(id_ctx->ad_options->basic, AD_ENABLE_GC)) {
++        user_conn = id_ctx->gc_ctx;
+     } else {
+-        user_conn = state->id_ctx->ldap_ctx;
++        user_conn = id_ctx->ldap_ctx;
+     }
+ 
+     /* Groups are searched for in LDAP, users in GC. Services (if present,
+      * which is unlikely in AD) from LDAP as well
+      */
+     subreq = sdap_dom_enum_ex_send(state, state->ev,
+-                                   state->id_ctx->sdap_id_ctx,
+-                                   state->sdom,
+-                                   user_conn,                /* Users    */
+-                                   state->id_ctx->ldap_ctx,  /* Groups   */
+-                                   state->id_ctx->ldap_ctx); /* Services */
++                                   id_ctx->sdap_id_ctx,
++                                   sd,
++                                   user_conn,         /* Users    */
++                                   id_ctx->ldap_ctx,  /* Groups   */
++                                   id_ctx->ldap_ctx); /* Services */
+     if (subreq == NULL) {
+         /* The ptask API will reschedule the enumeration on its own on
+          * failure */
+         DEBUG(SSSDBG_OP_FAILURE,
+               ("Failed to schedule enumeration, retrying later!\n"));
+-        tevent_req_error(req, ENOMEM);
+-        return;
++        return ENOMEM;
+     }
+     tevent_req_set_callback(subreq, ad_enumeration_done, req);
++
++    return EOK;
+ }
+ 
++static errno_t ad_enum_cross_dom_members(struct sdap_options *opts,
++                                         struct sss_domain_info *dom);
++
+ static void
+ ad_enumeration_done(struct tevent_req *subreq)
+ {
+@@ -579,6 +607,7 @@ ad_enumeration_done(struct tevent_req *subreq)
+                                                       struct tevent_req);
+     struct ad_enumeration_state *state = tevent_req_data(req,
+                                                 struct ad_enumeration_state);
++    struct ad_id_ctx *subdom_id_ctx;
+ 
+     ret = sdap_dom_enum_ex_recv(subreq);
+     talloc_zfree(subreq);
+@@ -589,9 +618,348 @@ ad_enumeration_done(struct tevent_req *subreq)
+         return;
+     }
+ 
++    state->sditer = state->sditer->next;
++    if (state->sditer != NULL) {
++        subdom_id_ctx = talloc_get_type(state->sdom->pvt, struct ad_id_ctx);
++        if (subdom_id_ctx == NULL) {
++            DEBUG(SSSDBG_CRIT_FAILURE, ("Cannot retrieve subdomain ad_id_ctx!\n"));
++            tevent_req_error(req, EFAULT);
++            return;
++        }
++
++        ret = ad_enum_sdom(req, state->sditer, state->sditer->pvt);
++        if (ret != EOK) {
++            DEBUG(SSSDBG_OP_FAILURE, ("Could not enumerate domain %s\n",
++                  state->sditer->dom->name));
++            tevent_req_error(req, ret);
++            return;
++        }
++
++        /* Execution will resume in ad_enumeration_done */
++        return;
++    }
++
++    /* No more subdomains to enumerate. Check if we need to fixup
++     * cross-domain membership
++     */
++    if (state->sditer != state->sdom) {
++        /* We did enumerate at least one subdomain. Walk the subdomains
++         * and fixup members for each of them
++         */
++        for (state->sditer = state->sdom;
++             state->sditer;
++             state->sditer = state->sditer->next) {
++            ret = ad_enum_cross_dom_members(state->id_ctx->ad_options->id,
++                                            state->sditer->dom);
++            if (ret != EOK) {
++                DEBUG(SSSDBG_MINOR_FAILURE, ("Could not check cross-domain "
++                      "memberships for %s, group memberships might be "
++                      "incomplete!\n", state->sdom->dom->name));
++                continue;
++            }
++        }
++    }
++
+     tevent_req_done(req);
+ }
+ 
++static errno_t ad_group_extra_members(TALLOC_CTX *mem_ctx,
++                                      const struct ldb_message *group,
++                                      struct sss_domain_info *dom,
++                                      char ***_group_only);
++static errno_t ad_group_add_member(struct sdap_options *opts,
++                                   struct sss_domain_info *group_domain,
++                                   struct ldb_dn *group_dn,
++                                   const char *member);
++
++static errno_t
++ad_enum_cross_dom_members(struct sdap_options *opts,
++                          struct sss_domain_info *dom)
++{
++    errno_t ret;
++    errno_t sret;
++    char *filter;
++    TALLOC_CTX *tmp_ctx;
++    const char *attrs[] = {
++            SYSDB_NAME,
++            SYSDB_MEMBER,
++            SYSDB_ORIG_MEMBER,
++            NULL
++    };
++    size_t count, i, mi;
++    struct ldb_message **msgs;
++    bool in_transaction = false;
++    char **group_only;
++
++    tmp_ctx = talloc_new(NULL);
++    if (tmp_ctx == NULL) return ENOMEM;
++
++    ret = sysdb_transaction_start(dom->sysdb);
++    if (ret != EOK) {
++        DEBUG(SSSDBG_CRIT_FAILURE, ("Failed to start transaction\n"));
++        goto done;
++    }
++    in_transaction = true;
++
++    filter = talloc_asprintf(tmp_ctx, "(%s=*)", SYSDB_NAME);
++    if (filter == NULL) {
++        ret = ENOMEM;
++        goto done;
++    }
++
++    ret = sysdb_search_groups(tmp_ctx, dom->sysdb, dom,
++                              filter, attrs, &count, &msgs);
++    if (ret != EOK) {
++        goto done;
++    }
++
++    for (i = 0; i < count; i++) {
++        ret = ad_group_extra_members(tmp_ctx, msgs[i], dom, &group_only);
++        if (ret != EOK) {
++            DEBUG(SSSDBG_OP_FAILURE, ("Failed to check extra members\n"));
++        } else if (group_only == NULL) {
++            DEBUG(SSSDBG_TRACE_INTERNAL, ("No extra members\n"));
++            continue;
++        }
++
++        /* Group has extra members */
++        for (mi = 0; group_only[mi]; mi++) {
++            ret = ad_group_add_member(opts, dom, msgs[i]->dn, group_only[mi]);
++            if (ret != EOK) {
++                DEBUG(SSSDBG_MINOR_FAILURE, ("Failed to add [%s]: %s\n",
++                      group_only[mi], strerror(ret)));
++                continue;
++            }
++        }
++
++        talloc_zfree(group_only);
++    }
++
++    ret = sysdb_transaction_commit(dom->sysdb);
++    if (ret != EOK) {
++        DEBUG(SSSDBG_CRIT_FAILURE, ("Failed to commit transaction\n"));
++        goto done;
++    }
++    in_transaction = false;
++
++    ret = EOK;
++done:
++    if (in_transaction) {
++        sret = sysdb_transaction_cancel(dom->sysdb);
++        if (sret != EOK) {
++            DEBUG(SSSDBG_CRIT_FAILURE, ("Could not cancel transaction\n"));
++        }
++    }
++    talloc_free(tmp_ctx);
++    return ret;
++}
++
++static errno_t
++ad_group_stored_orig_members(TALLOC_CTX *mem_ctx, struct sss_domain_info *dom,
++                             struct ldb_dn *dn, char ***_odn_list);
++
++static errno_t
++ad_group_extra_members(TALLOC_CTX *mem_ctx, const struct ldb_message *group,
++                       struct sss_domain_info *dom, char ***_group_only)
++{
++    TALLOC_CTX *tmp_ctx;
++    struct ldb_message_element *m, *om;
++    const char *name;
++    errno_t ret;
++    char **sysdb_odn_list;
++    const char **group_odn_list;
++    char **group_only = NULL;
++
++    if (_group_only == NULL) return EINVAL;
++    *_group_only = NULL;
++
++    tmp_ctx = talloc_new(NULL);
++    if (tmp_ctx == NULL) return ENOMEM;
++
++    om = ldb_msg_find_element(group, SYSDB_ORIG_MEMBER);
++    m = ldb_msg_find_element(group, SYSDB_MEMBER);
++    name = ldb_msg_find_attr_as_string(group, SYSDB_NAME, NULL);
++    if (name == NULL) {
++        DEBUG(SSSDBG_OP_FAILURE, ("A group with no name!\n"));
++        ret = EFAULT;
++        goto done;
++    }
++
++    if (om == NULL || om->num_values == 0) {
++        DEBUG(SSSDBG_TRACE_FUNC, ("Group %s has no original members\n", name));
++        ret = EOK;
++        goto done;
++    }
++
++    if (m == NULL || (m->num_values < om->num_values)) {
++        DEBUG(SSSDBG_TRACE_FUNC,
++              ("Group %s has %d members but %d original members\n",
++               name, m ? m->num_values : 0, om->num_values));
++
++        /* Get the list of originalDN attributes that are already
++         * linked to the group
++         */
++        ret = ad_group_stored_orig_members(tmp_ctx, dom, group->dn,
++                                           &sysdb_odn_list);
++        if (ret != EOK) {
++            DEBUG(SSSDBG_OP_FAILURE,
++                  ("Could not retrieve list of original members for %s\n",
++                  name));
++            goto done;
++        }
++
++        /* Get the list of original DN attributes the group had in AD */
++        group_odn_list = sss_ldb_el_to_string_list(tmp_ctx, om);
++        if (group_odn_list == NULL) {
++            ret = EFAULT;
++            goto done;
++        }
++
++        /* Compare the two lists */
++        ret = diff_string_lists(tmp_ctx, discard_const(group_odn_list),
++                                sysdb_odn_list, &group_only, NULL, NULL);
++        if (ret != EOK) {
++            DEBUG(SSSDBG_OP_FAILURE,
++                  ("Could not compare lists of members for %s\n", name));
++            goto done;
++        }
++    }
++
++    ret = EOK;
++    *_group_only = talloc_steal(mem_ctx, group_only);
++done:
++    talloc_free(tmp_ctx);
++    return ret;
++}
++
++static errno_t
++ad_group_stored_orig_members(TALLOC_CTX *mem_ctx, struct sss_domain_info *dom,
++                             struct ldb_dn *dn, char ***_odn_list)
++{
++    errno_t ret;
++    TALLOC_CTX *tmp_ctx;
++    size_t m_count, i;
++    struct ldb_message **members;
++    const char *attrs[] = {
++            SYSDB_NAME,
++            SYSDB_ORIG_DN,
++            NULL
++    };
++    char **odn_list;
++    const char *odn;
++    size_t oi;
++
++    tmp_ctx = talloc_new(NULL);
++    if (tmp_ctx == NULL) return ENOMEM;
++
++    /* Get all entries member element points to */
++    ret = sysdb_asq_search(tmp_ctx, dom->sysdb, dn, NULL, SYSDB_MEMBER,
++                           attrs, &m_count, &members);
++    if (ret != EOK) {
++        goto done;
++    }
++
++    odn_list = talloc_zero_array(tmp_ctx, char *, m_count + 1);
++    if (odn_list == NULL) {
++        ret = ENOMEM;
++        goto done;
++    }
++
++    /* Get a list of their original DNs */
++    oi = 0;
++    for (i = 0; i < m_count; i++) {
++        odn = ldb_msg_find_attr_as_string(members[i], SYSDB_ORIG_DN, NULL);
++        if (odn == NULL) {
++            continue;
++        }
++
++        odn_list[oi] = talloc_strdup(odn_list, odn);
++        if (odn_list[oi] == NULL) {
++            ret = ENOMEM;
++            goto done;
++        }
++        oi++;
++        DEBUG(SSSDBG_TRACE_INTERNAL, ("Member %s already in sysdb\n", odn));
++    }
++
++    ret = EOK;
++    *_odn_list = talloc_steal(mem_ctx, odn_list);
++done:
++    talloc_free(tmp_ctx);
++    return ret;
++}
++
++static errno_t
++ad_group_add_member(struct sdap_options *opts,
++                    struct sss_domain_info *group_domain,
++                    struct ldb_dn *group_dn,
++                    const char *member)
++{
++    struct sdap_domain *sd;
++    struct ldb_dn *base_dn;
++    TALLOC_CTX *tmp_ctx;
++    errno_t ret;
++    const char *mem_filter;
++    size_t msgs_count;
++    struct ldb_message **msgs;
++
++    /* This member would be from a different domain */
++    sd = sdap_domain_get_by_dn(opts, member);
++    if (sd == NULL) {
++        DEBUG(SSSDBG_MINOR_FAILURE, ("No matching domain for %s\n", member));
++        return ENOENT;
++    }
++
++    tmp_ctx = talloc_new(NULL);
++    if (tmp_ctx == NULL) return ENOMEM;
++
++    mem_filter = talloc_asprintf(tmp_ctx, "(%s=%s)",
++                                 SYSDB_ORIG_DN, member);
++    if (mem_filter == NULL) {
++        ret = ENOMEM;
++        goto done;
++    }
++
++    base_dn = sysdb_domain_dn(sd->dom->sysdb, tmp_ctx, sd->dom);
++    if (base_dn == NULL) {
++        ret = ENOMEM;
++        goto done;
++    }
++
++    ret = sysdb_search_entry(tmp_ctx, sd->dom->sysdb, base_dn,
++                             LDB_SCOPE_SUBTREE, mem_filter, NULL,
++                             &msgs_count, &msgs);
++    if (ret == ENOENT) {
++        DEBUG(SSSDBG_TRACE_FUNC, ("No member [%s] in sysdb\n", member));
++        ret = EOK;
++        goto done;
++    } else if (ret != EOK) {
++        goto done;
++    }
++    DEBUG(SSSDBG_TRACE_INTERNAL, ("[%s] found in sysdb\n", member));
++
++    if (msgs_count != 1) {
++        DEBUG(SSSDBG_CRIT_FAILURE,
++               ("Search by orig DN returned %zd results!\n", msgs_count));
++        ret = EFAULT;
++        goto done;
++    }
++
++    ret = sysdb_mod_group_member(group_domain->sysdb, msgs[0]->dn,
++                                 group_dn, SYSDB_MOD_ADD);
++    if (ret != EOK) {
++        DEBUG(SSSDBG_OP_FAILURE, ("Could not add [%s] as a member of [%s]\n",
++              ldb_dn_get_linearized(msgs[0]->dn),
++              ldb_dn_get_linearized(group_dn)));
++        goto done;
++    }
++
++    ret = EOK;
++done:
++    talloc_free(tmp_ctx);
++    return ret;
++}
++
+ errno_t
+ ad_enumeration_recv(struct tevent_req *req)
+ {
+diff --git a/src/providers/ad/ad_subdomains.c b/src/providers/ad/ad_subdomains.c
+index e7871cc32407893948fe1b2803258d68c70889c1..0d9652b5c615add47958cfdc61eba862a332ae4d 100644
+--- a/src/providers/ad/ad_subdomains.c
++++ b/src/providers/ad/ad_subdomains.c
+@@ -177,17 +177,6 @@ ad_subdom_ad_ctx_new(struct be_ctx *be_ctx,
+         return EFAULT;
+     }
+ 
+-    ret = sdap_id_setup_tasks(be_ctx,
+-                              ad_id_ctx->sdap_id_ctx,
+-                              sdom,
+-                              ldap_enumeration_send,
+-                              ldap_enumeration_recv,
+-                              ad_id_ctx->sdap_id_ctx);
+-    if (ret != EOK) {
+-        talloc_free(ad_options);
+-        return ret;
+-    }
+-
+     /* Set up the ID mapping object */
+     ad_id_ctx->sdap_id_ctx->opts->idmap_ctx =
+         id_ctx->sdap_id_ctx->opts->idmap_ctx;
+-- 
+1.8.4.2
+
diff --git a/SOURCES/0072-AD-SRV-use-right-domain-name-for-CLDAP-ping.patch b/SOURCES/0072-AD-SRV-use-right-domain-name-for-CLDAP-ping.patch
new file mode 100644
index 0000000..b3f1d9c
--- /dev/null
+++ b/SOURCES/0072-AD-SRV-use-right-domain-name-for-CLDAP-ping.patch
@@ -0,0 +1,28 @@
+From 9118f124fad5fe15c5beb910db08794d9b3d2685 Mon Sep 17 00:00:00 2001
+From: Sumit Bose <sbose@redhat.com>
+Date: Fri, 24 Jan 2014 16:52:22 +0100
+Subject: [PATCH 72/73] AD SRV: use right domain name for CLDAP ping
+
+Currently always the name of the configured domain was passed to the
+CLDAP request. This will fail if the CLDAP request is send to a DC form
+a different domain.
+---
+ src/providers/ad/ad_srv.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/src/providers/ad/ad_srv.c b/src/providers/ad/ad_srv.c
+index a238c192e495c3ff6330123d8e53d7d480e88e1b..99502e431e7b8b053e6ec58780abfd10859937be 100644
+--- a/src/providers/ad/ad_srv.c
++++ b/src/providers/ad/ad_srv.c
+@@ -723,7 +723,7 @@ static void ad_srv_plugin_dcs_done(struct tevent_req *subreq)
+                                      state->ctx->be_res,
+                                      state->ctx->host_dbs,
+                                      state->ctx->opts,
+-                                     state->ctx->ad_domain,
++                                     state->discovery_domain,
+                                      dcs, num_dcs);
+     if (subreq == NULL) {
+         ret = ENOMEM;
+-- 
+1.8.4.2
+
diff --git a/SOURCES/0073-MAN-clarify-which-shell-option-takes-precedence.patch b/SOURCES/0073-MAN-clarify-which-shell-option-takes-precedence.patch
new file mode 100644
index 0000000..e6fd18e
--- /dev/null
+++ b/SOURCES/0073-MAN-clarify-which-shell-option-takes-precedence.patch
@@ -0,0 +1,43 @@
+From 045b0cbf342d50ce07fc768fbce63b9bd0309215 Mon Sep 17 00:00:00 2001
+From: Jakub Hrozek <jhrozek@redhat.com>
+Date: Wed, 29 Jan 2014 13:24:15 +0100
+Subject: [PATCH 73/73] MAN: clarify which shell option takes precedence
+
+---
+ src/man/sssd.conf.5.xml | 13 +++++++------
+ 1 file changed, 7 insertions(+), 6 deletions(-)
+
+diff --git a/src/man/sssd.conf.5.xml b/src/man/sssd.conf.5.xml
+index b879bdf63c40e80efa21644bd9ba9d2d3477af06..84770f6b28876278a0ddd6d8a8a8f9a8e0d3146f 100644
+--- a/src/man/sssd.conf.5.xml
++++ b/src/man/sssd.conf.5.xml
+@@ -503,8 +503,9 @@ fallback_homedir = /home/%u
+                     <listitem>
+                         <para>
+                             Override the login shell for all users. This
+-                            option can be specified globally in the [nss]
+-                            section or per-domain.
++                            option supersedes any other shell options if
++                            it takes effect and can be set either in the
++                            [nss] section or per-domain.
+                         </para>
+                         <para>
+                             Default: not set (SSSD will use the value
+@@ -568,10 +569,10 @@ fallback_homedir = /home/%u
+                     <term>default_shell</term>
+                     <listitem>
+                         <para>
+-                            The default shell to use if the provider does not
+-                            return one during lookup. This option supersedes
+-                            any other shell options if it takes effect and can
+-                            be set either in the [nss] section or per-domain.
++                            The default shell to use if the provider does
++                            not return one during lookup. This option can
++                            be specified globally in the [nss] section
++                            or per-domain.
+                         </para>
+                         <para>
+                             Default: not set (Return NULL if no shell is
+-- 
+1.8.4.2
+
diff --git a/SOURCES/0074-LDAP-store-group-if-subdomain-cannot-be-found-by-sid.patch b/SOURCES/0074-LDAP-store-group-if-subdomain-cannot-be-found-by-sid.patch
new file mode 100644
index 0000000..1caa79e
--- /dev/null
+++ b/SOURCES/0074-LDAP-store-group-if-subdomain-cannot-be-found-by-sid.patch
@@ -0,0 +1,47 @@
+From 53554c55a75477b0ea719b845ff1660ac1a2ec82 Mon Sep 17 00:00:00 2001
+From: Lukas Slebodnik <lslebodn@redhat.com>
+Date: Fri, 24 Jan 2014 17:03:27 +0100
+Subject: [PATCH 74/75] LDAP: store group if subdomain cannot be found by sid
+
+Domain needn't contain sid if id_provider is ldap.
+With enabled id mapping, group couldn't be stored, because domain
+couldn't be found by sid.
+
+Resolves:
+https://fedorahosted.org/sssd/ticket/2172
+---
+ src/providers/ldap/sdap_async_groups.c | 10 ++++++----
+ 1 file changed, 6 insertions(+), 4 deletions(-)
+
+diff --git a/src/providers/ldap/sdap_async_groups.c b/src/providers/ldap/sdap_async_groups.c
+index 4ed7d4ab9c0c932da49b244f061329a334719159..ab3691f80aaecca988dcbf0877491a37af3ae49b 100644
+--- a/src/providers/ldap/sdap_async_groups.c
++++ b/src/providers/ldap/sdap_async_groups.c
+@@ -452,6 +452,7 @@ static int sdap_save_group(TALLOC_CTX *memctx,
+     bool posix_group;
+     bool use_id_mapping;
+     char *sid_str;
++    struct sss_domain_info *subdomain;
+     int32_t ad_group_type;
+ 
+     tmpctx = talloc_new(NULL);
+@@ -490,11 +491,12 @@ static int sdap_save_group(TALLOC_CTX *memctx,
+     /* If this object has a SID available, we will determine the correct
+      * domain by its SID. */
+     if (sid_str != NULL) {
+-        dom = find_subdomain_by_sid(get_domains_head(dom), sid_str);
+-        if (dom == NULL) {
+-            DEBUG(SSSDBG_OP_FAILURE, ("SID %s does not belong to any known "
++        subdomain = find_subdomain_by_sid(get_domains_head(dom), sid_str);
++        if (subdomain) {
++            dom = subdomain;
++        } else {
++            DEBUG(SSSDBG_TRACE_FUNC, ("SID %s does not belong to any known "
+                                       "domain\n", sid_str));
+-            return ERR_DOMAIN_NOT_FOUND;
+         }
+     }
+ 
+-- 
+1.8.4.2
+
diff --git a/SOURCES/0075-LDAP-require-attribute-groupType-for-AD-groups.patch b/SOURCES/0075-LDAP-require-attribute-groupType-for-AD-groups.patch
new file mode 100644
index 0000000..ea1b78c
--- /dev/null
+++ b/SOURCES/0075-LDAP-require-attribute-groupType-for-AD-groups.patch
@@ -0,0 +1,31 @@
+From 42817081e7afe5b67d4151c5ac87b0a48da31a1b Mon Sep 17 00:00:00 2001
+From: Lukas Slebodnik <lslebodn@redhat.com>
+Date: Wed, 29 Jan 2014 14:58:53 +0100
+Subject: [PATCH 75/75] LDAP: require attribute groupType for AD groups
+
+Commit 8280c5213094 introduced filtering local groups for trusted/sub domains,
+but attribute groupType was not available with configuration id_provide ldap
+and ldap_schema ad.
+
+Resolves:
+https://fedorahosted.org/sssd/ticket/2172
+---
+ src/providers/ldap/ldap_opts.h | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/src/providers/ldap/ldap_opts.h b/src/providers/ldap/ldap_opts.h
+index 9593dfd30a81db60b7358c66975871507340aa4b..d07051c51aef860943ee8bd4953e8f3d0660a656 100644
+--- a/src/providers/ldap/ldap_opts.h
++++ b/src/providers/ldap/ldap_opts.h
+@@ -295,7 +295,7 @@ struct sdap_attr_map gen_ad2008r2_group_map[] = {
+     { "ldap_group_objectsid", "objectSID", SYSDB_SID, NULL },
+     { "ldap_group_modify_timestamp", "whenChanged", SYSDB_ORIG_MODSTAMP, NULL },
+     { "ldap_group_entry_usn", SDAP_AD_USN, SYSDB_USN, NULL },
+-    { "ldap_group_type", NULL, SYSDB_GROUP_TYPE, NULL },
++    { "ldap_group_type", "groupType", SYSDB_GROUP_TYPE, NULL },
+     SDAP_ATTR_MAP_TERMINATOR
+ };
+ 
+-- 
+1.8.4.2
+
diff --git a/SOURCES/0076-MONITOR-Incorrect-permissions-on-sssd.conf.patch b/SOURCES/0076-MONITOR-Incorrect-permissions-on-sssd.conf.patch
new file mode 100644
index 0000000..381c18e
--- /dev/null
+++ b/SOURCES/0076-MONITOR-Incorrect-permissions-on-sssd.conf.patch
@@ -0,0 +1,46 @@
+From 3af4a383964cf391e0277bd8a272156a1805e357 Mon Sep 17 00:00:00 2001
+From: Pavel Reichl <preichl@redhat.com>
+Date: Wed, 5 Feb 2014 17:32:20 +0000
+Subject: [PATCH 76/80] MONITOR: Incorrect permissions on sssd.conf
+
+Print user friendly warning when permissions on sssd.conf are incorrect and
+provide hint.
+
+Resolves:
+https://fedorahosted.org/sssd/ticket/2208
+
+Reviewed-by: Stephen Gallagher <sgallagh@redhat.com>
+(cherry picked from commit b3cc9b98966fa2d90172348c334b3b70c5261ab3)
+---
+ src/monitor/monitor.c | 8 +++++++-
+ 1 file changed, 7 insertions(+), 1 deletion(-)
+
+diff --git a/src/monitor/monitor.c b/src/monitor/monitor.c
+index 09f530d2643b45fc31fb4dbe3cb69f2abc5510af..72a8b7c4a376dd9b68349b959cb869b9da059d38 100644
+--- a/src/monitor/monitor.c
++++ b/src/monitor/monitor.c
+@@ -84,6 +84,11 @@
+  */
+ #define KRB5_RCACHE_DIR_DISABLE "__LIBKRB5_DEFAULTS__"
+ 
++/* Warning messages */
++#define CONF_FILE_PERM_ERROR_MSG "Cannot read config file %s. Please check "\
++                                 "if permissions are 0600 and the file is "\
++                                 "owned by root.root."
++
+ int cmdline_debug_level;
+ int cmdline_debug_timestamps;
+ int cmdline_debug_microseconds;
+@@ -2798,7 +2803,8 @@ int main(int argc, const char *argv[])
+         case EPERM:
+         case EACCES:
+             DEBUG(SSSDBG_CRIT_FAILURE,
+-                 ("Insufficient permissions to read configuration file.\n"));
++                  (CONF_FILE_PERM_ERROR_MSG, config_file));
++            sss_log(SSS_LOG_ALERT, CONF_FILE_PERM_ERROR_MSG, config_file);
+             break;
+         default:
+             DEBUG(SSSDBG_CRIT_FAILURE,
+-- 
+1.8.5.3
+
diff --git a/SOURCES/0077-Revert-NSS-add-support-for-subdomain_homedir.patch b/SOURCES/0077-Revert-NSS-add-support-for-subdomain_homedir.patch
new file mode 100644
index 0000000..3f14be6
--- /dev/null
+++ b/SOURCES/0077-Revert-NSS-add-support-for-subdomain_homedir.patch
@@ -0,0 +1,34 @@
+From e7987aced2a2052d3cbff185f6e53708b552995b Mon Sep 17 00:00:00 2001
+From: Pavel Reichl <preichl@redhat.com>
+Date: Tue, 21 Jan 2014 15:06:37 +0000
+Subject: [PATCH 77/80] Revert "NSS: add support for subdomain_homedir"
+
+This reverts commit 1dc7694a1cbc62b0d7e23cc1369579e5ce0071e8.
+
+Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
+---
+ src/responder/nss/nsssrv_cmd.c | 8 --------
+ 1 file changed, 8 deletions(-)
+
+diff --git a/src/responder/nss/nsssrv_cmd.c b/src/responder/nss/nsssrv_cmd.c
+index 9ac3680de4d6ff12fe0c77a3963f84934e385276..c59078b545842561a7e5f62e9a99da6057b23660 100644
+--- a/src/responder/nss/nsssrv_cmd.c
++++ b/src/responder/nss/nsssrv_cmd.c
+@@ -201,14 +201,6 @@ static const char *get_homedir_override(TALLOC_CTX *mem_ctx,
+                                        name, uid, homedir, dom->name, NULL);
+     }
+ 
+-    /* Override home directory location for subdomains.
+-     * This option can be overriden by override_homedir.
+-     */
+-    if (IS_SUBDOMAIN(dom) && dom->subdomain_homedir) {
+-        return expand_homedir_template(mem_ctx, dom->subdomain_homedir,
+-                                       name, uid, homedir, dom->name, NULL);
+-    }
+-
+     if (!homedir || *homedir == '\0') {
+         /* In the case of a NULL or empty homedir, check to see if
+          * we have a fallback homedir to use.
+-- 
+1.8.5.3
+
diff --git a/SOURCES/0078-AD-support-for-subdomain_homedir.patch b/SOURCES/0078-AD-support-for-subdomain_homedir.patch
new file mode 100644
index 0000000..ac57314
--- /dev/null
+++ b/SOURCES/0078-AD-support-for-subdomain_homedir.patch
@@ -0,0 +1,233 @@
+From 0b813ab764fd0b255e342765b79533e1869cd06e Mon Sep 17 00:00:00 2001
+From: Pavel Reichl <preichl@redhat.com>
+Date: Wed, 22 Jan 2014 16:47:22 +0000
+Subject: [PATCH 78/80] AD: support for subdomain_homedir
+
+Homedir is defaultly set accordingly to subdomain_homedir for users from AD.
+
+Resolves:
+https://fedorahosted.org/sssd/ticket/2169
+
+Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
+---
+ src/providers/ipa/ipa_subdomains_id.c | 190 ++++++++++++++++++++++++++++++++++
+ 1 file changed, 190 insertions(+)
+
+diff --git a/src/providers/ipa/ipa_subdomains_id.c b/src/providers/ipa/ipa_subdomains_id.c
+index c29a2a3047af105966b636422105abd15e8a3992..fb1ad896885866dd9c34f9db960e09d92763f86d 100644
+--- a/src/providers/ipa/ipa_subdomains_id.c
++++ b/src/providers/ipa/ipa_subdomains_id.c
+@@ -25,6 +25,7 @@
+ #include <errno.h>
+ 
+ #include "util/util.h"
++#include "util/sss_nss.h"
+ #include "util/strtonum.h"
+ #include "db/sysdb.h"
+ #include "providers/ldap/ldap_common.h"
+@@ -350,6 +351,185 @@ ipa_get_ad_id_ctx(struct ipa_id_ctx *ipa_ctx,
+     return (iter) ? iter->ad_id_ctx : NULL;
+ }
+ 
++static errno_t
++get_subdomain_homedir_of_user(TALLOC_CTX *mem_ctx, struct sss_domain_info *dom,
++                              const char *fqname, uint32_t uid,
++                              const char **_homedir)
++{
++    errno_t ret;
++    char *name;
++    const char *homedir;
++    TALLOC_CTX *tmp_ctx;
++
++    tmp_ctx = talloc_new(mem_ctx);
++    if (tmp_ctx == NULL) {
++        ret = ENOMEM;
++        goto done;
++    }
++
++    ret = sss_parse_name(tmp_ctx, dom->names, fqname, NULL, &name);
++    if (ret != EOK) {
++        goto done;
++    }
++
++    homedir = expand_homedir_template(tmp_ctx, dom->subdomain_homedir, name,
++                                      uid, NULL, dom->name, dom->flat_name);
++
++    if (homedir == NULL) {
++        DEBUG(SSSDBG_OP_FAILURE, ("expand_homedir_template failed\n"));
++        ret = ENOMEM;
++        goto done;
++    }
++
++    if (_homedir == NULL) {
++        ret = EINVAL;
++        goto done;
++    }
++    *_homedir = talloc_steal(mem_ctx, homedir);
++
++done:
++    talloc_free(tmp_ctx);
++    return ret;
++}
++
++static errno_t
++store_homedir_of_user(struct sss_domain_info *domain,
++                      const char *fqname, const char *homedir)
++{
++    errno_t ret;
++    errno_t sret;
++    TALLOC_CTX *tmp_ctx;
++    bool in_transaction = false;
++    struct sysdb_attrs *attrs;
++    struct sysdb_ctx *sysdb = domain->sysdb;
++
++    tmp_ctx = talloc_new(NULL);
++    if (tmp_ctx == NULL) {
++        ret = ENOMEM;
++        goto done;
++    }
++
++    attrs = sysdb_new_attrs(tmp_ctx);
++    if (attrs == NULL) {
++        ret = ENOMEM;
++        goto done;
++    }
++
++    ret = sysdb_attrs_add_string(attrs, SYSDB_HOMEDIR, homedir);
++    if (ret != EOK) {
++        DEBUG(SSSDBG_MINOR_FAILURE, ("Error setting homedir: [%s]\n",
++                                     strerror(ret)));
++        goto done;
++    }
++
++    ret = sysdb_transaction_start(sysdb);
++    if (ret != EOK) {
++        DEBUG(SSSDBG_CRIT_FAILURE, ("Failed to start transaction\n"));
++        goto done;
++    }
++
++    in_transaction = true;
++
++    ret = sysdb_set_user_attr(sysdb, domain, fqname, attrs, SYSDB_MOD_REP);
++    if (ret != EOK) {
++        DEBUG(SSSDBG_CRIT_FAILURE,
++              ("Failed to update homedir information!\n"));
++        goto done;
++    }
++
++    ret = sysdb_transaction_commit(sysdb);
++    if (ret != EOK) {
++        DEBUG(SSSDBG_CRIT_FAILURE,
++              ("Cannot commit sysdb transaction [%d]: %s.\n",
++               ret, strerror(ret)));
++        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(tmp_ctx);
++    return ret;
++}
++
++static errno_t
++apply_subdomain_homedir(TALLOC_CTX *mem_ctx, struct sss_domain_info *dom,
++                        int filter_type, const char *filter_value)
++{
++    errno_t ret;
++    uint32_t uid;
++    const char *fqname;
++    const char *homedir = NULL;
++    struct ldb_result *res;
++
++    if (filter_type == BE_FILTER_NAME) {
++        ret = sysdb_getpwnam(mem_ctx, dom->sysdb, dom, filter_value, &res);
++    } else if (filter_type == BE_FILTER_IDNUM) {
++        errno = 0;
++        uid = strtouint32(filter_value, NULL, 10);
++        if (errno != 0) {
++            ret = errno;
++            goto done;
++        }
++        ret = sysdb_getpwuid(mem_ctx, dom->sysdb, dom, uid, &res);
++    } else {
++        DEBUG(SSSDBG_OP_FAILURE,
++              ("Unsupported filter type: [%d].\n", filter_type));
++        ret = EINVAL;
++        goto done;
++    }
++
++    if (ret != EOK) {
++        DEBUG(SSSDBG_OP_FAILURE,
++              ("Failed to make request to our cache: [%d]: [%s]\n",
++               ret, sss_strerror(ret)));
++        goto done;
++    }
++
++    if (res->count == 0) {
++        ret = ENOENT;
++        goto done;
++    }
++
++    /*
++     * Homedir is always overriden by subdomain_homedir even if it was
++     * explicitly set by user.
++     */
++    fqname = ldb_msg_find_attr_as_string(res->msgs[0], SYSDB_NAME, NULL);
++    uid = ldb_msg_find_attr_as_uint64(res->msgs[0], SYSDB_UIDNUM, 0);
++    if (uid == 0) {
++        DEBUG(SSSDBG_OP_FAILURE, ("UID for user [%s] is not known.\n",
++                                  filter_value));
++        ret = ENOENT;
++        goto done;
++    }
++
++    ret = get_subdomain_homedir_of_user(mem_ctx, dom, fqname, uid, &homedir);
++    if (ret != EOK) {
++        DEBUG(SSSDBG_OP_FAILURE,
++              ("get_subdomain_homedir_of_user failed: [%d]: [%s]\n",
++               ret, sss_strerror(ret)));
++        goto done;
++    }
++
++    ret = store_homedir_of_user(dom, fqname, homedir);
++    if (ret != EOK) {
++        DEBUG(SSSDBG_OP_FAILURE,
++              ("store_homedir_of_user failed: [%d]: [%s]\n",
++               ret, sss_strerror(ret)));
++        goto done;
++    }
++
++done:
++    return ret;
++}
++
+ static void
+ ipa_get_ad_acct_ad_part_done(struct tevent_req *subreq)
+ {
+@@ -367,6 +547,16 @@ ipa_get_ad_acct_ad_part_done(struct tevent_req *subreq)
+         return;
+     }
+ 
++    ret = apply_subdomain_homedir(state, state->user_dom,
++                                  state->ar->filter_type,
++                                  state->ar->filter_value);
++    if (ret != EOK) {
++        DEBUG(SSSDBG_OP_FAILURE,
++              ("apply_subdomain_homedir failed: [%d]: [%s].\n",
++               ret, sss_strerror(ret)));
++        goto fail;
++    }
++
+     if ((state->ar->entry_type & BE_REQ_TYPE_MASK) != BE_REQ_INITGROUPS) {
+         tevent_req_done(req);
+         return;
+-- 
+1.8.5.3
+
diff --git a/SOURCES/0079-MAN-update-of-subdomain_homedir-usage.patch b/SOURCES/0079-MAN-update-of-subdomain_homedir-usage.patch
new file mode 100644
index 0000000..d14bd16
--- /dev/null
+++ b/SOURCES/0079-MAN-update-of-subdomain_homedir-usage.patch
@@ -0,0 +1,30 @@
+From 75b31af85b662912d9deb0c6a8f14728f20e5d2d Mon Sep 17 00:00:00 2001
+From: Pavel Reichl <preichl@redhat.com>
+Date: Wed, 29 Jan 2014 16:55:30 +0000
+Subject: [PATCH 79/80] MAN: update of subdomain_homedir usage
+
+Resolves:
+https://fedorahosted.org/sssd/ticket/2169
+
+Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
+---
+ src/man/sssd.conf.5.xml | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+diff --git a/src/man/sssd.conf.5.xml b/src/man/sssd.conf.5.xml
+index 84770f6b28876278a0ddd6d8a8a8f9a8e0d3146f..5d861c73cfeb41920619d95e5c1e5c1975dcc45b 100644
+--- a/src/man/sssd.conf.5.xml
++++ b/src/man/sssd.conf.5.xml
+@@ -1771,7 +1771,8 @@ fallback_homedir = /home/%u
+                     <listitem>
+                         <para>
+                             Use this homedir as default value for all subdomains
+-                            within this domain. See <emphasis>override_homedir</emphasis>
++                            within this domain in IPA AD trust.
++                            See <emphasis>override_homedir</emphasis>
+                             for info about possible values. In addition to those, the
+                             expansion below can only be used with
+                             <emphasis>subdomain_homedir</emphasis>.
+-- 
+1.8.5.3
+
diff --git a/SOURCES/0080-utils-handling-NULL-params-in-sss_parse_name.patch b/SOURCES/0080-utils-handling-NULL-params-in-sss_parse_name.patch
new file mode 100644
index 0000000..b8ff0cd
--- /dev/null
+++ b/SOURCES/0080-utils-handling-NULL-params-in-sss_parse_name.patch
@@ -0,0 +1,98 @@
+From 6ab5595ec7360bd4cc4c0494a1e0afedda701961 Mon Sep 17 00:00:00 2001
+From: Pavel Reichl <preichl@redhat.com>
+Date: Sun, 26 Jan 2014 12:39:43 +0000
+Subject: [PATCH 80/80] utils: handling NULL params in sss_parse_name
+
+Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
+---
+ src/util/usertools.c | 50 +++++++++++++++++++++++++++-----------------------
+ src/util/util.h      |  2 +-
+ 2 files changed, 28 insertions(+), 24 deletions(-)
+
+diff --git a/src/util/usertools.c b/src/util/usertools.c
+index 9edae41e0f216f9f0d1660e473f3aa1bf7160b06..fab0a261e82b8c4d8071ced1dac99b8e3b987b00 100644
+--- a/src/util/usertools.c
++++ b/src/util/usertools.c
+@@ -322,7 +322,7 @@ done:
+ 
+ int sss_parse_name(TALLOC_CTX *memctx,
+                    struct sss_names_ctx *snctx,
+-                   const char *orig, char **domain, char **name)
++                   const char *orig, char **_domain, char **_name)
+ {
+     pcre *re = snctx->re;
+     const char *result;
+@@ -346,31 +346,35 @@ int sss_parse_name(TALLOC_CTX *memctx,
+ 
+     strnum = ret;
+ 
+-    result = NULL;
+-    ret = pcre_get_named_substring(re, orig, ovec, strnum, "name", &result);
+-    if (ret < 0  || !result) {
+-        DEBUG(2, ("Name not found!\n"));
+-        return EINVAL;
++    if (_name != NULL) {
++        result = NULL;
++        ret = pcre_get_named_substring(re, orig, ovec, strnum, "name", &result);
++        if (ret < 0  || !result) {
++            DEBUG(2, ("Name not found!\n"));
++            return EINVAL;
++        }
++        *_name = talloc_strdup(memctx, result);
++        pcre_free_substring(result);
++        if (!*_name) return ENOMEM;
+     }
+-    *name = talloc_strdup(memctx, result);
+-    pcre_free_substring(result);
+-    if (!*name) return ENOMEM;
+ 
+-
+-    result = NULL;
+-    ret = pcre_get_named_substring(re, orig, ovec, strnum, "domain", &result);
+-    if (ret < 0  || !result) {
+-        DEBUG(4, ("Domain not provided!\n"));
+-        *domain = NULL;
+-    } else {
+-        /* ignore "" string */
+-        if (*result) {
+-            *domain = talloc_strdup(memctx, result);
+-            pcre_free_substring(result);
+-            if (!*domain) return ENOMEM;
++    if (_domain != NULL) {
++        result = NULL;
++        ret = pcre_get_named_substring(re, orig, ovec, strnum, "domain",
++                                       &result);
++        if (ret < 0  || !result) {
++            DEBUG(4, ("Domain not provided!\n"));
++            *_domain = NULL;
+         } else {
+-            pcre_free_substring(result);
+-            *domain = NULL;
++            /* ignore "" string */
++            if (*result) {
++                *_domain = talloc_strdup(memctx, result);
++                pcre_free_substring(result);
++                if (!*_domain) return ENOMEM;
++            } else {
++                pcre_free_substring(result);
++                *_domain = NULL;
++            }
+         }
+     }
+ 
+diff --git a/src/util/util.h b/src/util/util.h
+index 3334476ab83a137d957765fe2c9afba4ad0d014c..7b185bcb4287a4afc5bf67b40164cf69b9beeb19 100644
+--- a/src/util/util.h
++++ b/src/util/util.h
+@@ -349,7 +349,7 @@ int sss_names_init(TALLOC_CTX *mem_ctx,
+ 
+ int sss_parse_name(TALLOC_CTX *memctx,
+                    struct sss_names_ctx *snctx,
+-                   const char *orig, char **domain, char **name);
++                   const char *orig, char **_domain, char **_name);
+ 
+ char *
+ sss_get_cased_name(TALLOC_CTX *mem_ctx, const char *orig_name,
+-- 
+1.8.5.3
+
diff --git a/SOURCES/0081-LDAP-Detect-the-presence-of-POSIX-attributes.patch b/SOURCES/0081-LDAP-Detect-the-presence-of-POSIX-attributes.patch
new file mode 100644
index 0000000..6c4671b
--- /dev/null
+++ b/SOURCES/0081-LDAP-Detect-the-presence-of-POSIX-attributes.patch
@@ -0,0 +1,834 @@
+From 3b0d429051648a1545de528ec760c4823088a1d9 Mon Sep 17 00:00:00 2001
+From: Jakub Hrozek <jhrozek@redhat.com>
+Date: Mon, 16 Dec 2013 18:36:12 +0100
+Subject: [PATCH 81/84] LDAP: Detect the presence of POSIX attributes
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+When the schema is set to AD and ID mapping is not used, there is a one-time
+check ran when searching for users to detect the presence of POSIX
+attributes in LDAP. If this check fails, the search fails as if no entry
+was found and returns a special error code.
+
+The sdap_server_opts structure is filled every time a client connects to
+a server so the posix check boolean is reset to false again on connecting
+to the server.
+
+It might be better to move the check to where the rootDSE is retrieved,
+but the check depends on several features that are not known to the code
+that retrieves the rootDSE (or the connection code for example) such as what
+the attribute mappings are or the authentication method that should be used.
+
+Reviewed-by: Sumit Bose <sbose@redhat.com>
+Reviewed-by: Pavel Březina <pbrezina@redhat.com>
+(cherry picked from commit e81deec535d11912b87954c81a1edd768c1386c9)
+---
+ src/providers/ad/ad_id.c              |  50 ++++++++-
+ src/providers/ad/ad_id.h              |   1 +
+ src/providers/ipa/ipa_subdomains_id.c |   2 +-
+ src/providers/ldap/ldap_id.c          | 158 +++++++++++++++++++++++++--
+ src/providers/ldap/sdap.h             |   1 +
+ src/providers/ldap/sdap_async.c       | 200 ++++++++++++++++++++++++++++++++++
+ src/providers/ldap/sdap_async.h       |   9 ++
+ src/providers/ldap/sdap_async_enum.c  |  96 +++++++++++++++-
+ src/util/util_errors.c                |   1 +
+ src/util/util_errors.h                |   1 +
+ 10 files changed, 504 insertions(+), 15 deletions(-)
+
+diff --git a/src/providers/ad/ad_id.c b/src/providers/ad/ad_id.c
+index e3302c15774ab1c24b16cefc274313e447b31e5c..bfae86284355b6c13547aac55b7273133bde851d 100644
+--- a/src/providers/ad/ad_id.c
++++ b/src/providers/ad/ad_id.c
+@@ -27,6 +27,28 @@
+ #include "providers/ldap/sdap_async_enum.h"
+ #include "providers/ldap/sdap_idmap.h"
+ 
++static void
++disable_gc(struct ad_options *ad_options)
++{
++    errno_t ret;
++
++    if (dp_opt_get_bool(ad_options->basic, AD_ENABLE_GC) == false) {
++        return;
++    }
++
++    DEBUG(SSSDBG_IMPORTANT_INFO, ("POSIX attributes were requested "
++          "but are not present on the server side. Global Catalog "
++          "lookups will be disabled\n"));
++
++    ret = dp_opt_set_bool(ad_options->basic,
++                          AD_ENABLE_GC, false);
++    if (ret != EOK) {
++        DEBUG(SSSDBG_MINOR_FAILURE,
++                ("Could not turn off GC support\n"));
++        /* Not fatal */
++    }
++}
++
+ struct ad_handle_acct_info_state {
+     struct be_req *breq;
+     struct be_acct_req *ar;
+@@ -34,6 +56,7 @@ struct ad_handle_acct_info_state {
+     struct sdap_id_conn_ctx **conn;
+     struct sdap_domain *sdom;
+     size_t cindex;
++    struct ad_options *ad_options;
+ 
+     int dp_error;
+     const char *err;
+@@ -47,6 +70,7 @@ ad_handle_acct_info_send(TALLOC_CTX *mem_ctx,
+                          struct be_req *breq,
+                          struct be_acct_req *ar,
+                          struct sdap_id_ctx *ctx,
++                         struct ad_options *ad_options,
+                          struct sdap_domain *sdom,
+                          struct sdap_id_conn_ctx **conn)
+ {
+@@ -64,6 +88,7 @@ ad_handle_acct_info_send(TALLOC_CTX *mem_ctx,
+     state->ctx = ctx;
+     state->sdom = sdom;
+     state->conn = conn;
++    state->ad_options = ad_options;
+     state->cindex = 0;
+ 
+     ret = ad_handle_acct_info_step(req);
+@@ -137,12 +162,14 @@ ad_handle_acct_info_done(struct tevent_req *subreq)
+     if (sdap_err == EOK) {
+         tevent_req_done(req);
+         return;
++    } else if (sdap_err == ERR_NO_POSIX) {
++        disable_gc(state->ad_options);
+     } else if (sdap_err != ENOENT) {
+         tevent_req_error(req, EIO);
+         return;
+     }
+ 
+-    /* Ret is only ENOENT now. Try the next connection */
++    /* Ret is only ENOENT or ERR_NO_POSIX now. Try the next connection */
+     state->cindex++;
+     ret = ad_handle_acct_info_step(req);
+     if (ret != EAGAIN) {
+@@ -356,7 +383,7 @@ ad_account_info_handler(struct be_req *be_req)
+     }
+ 
+     req = ad_handle_acct_info_send(be_req, be_req, ar, sdap_id_ctx,
+-                                   sdom, clist);
++                                   ad_ctx->ad_options, sdom, clist);
+     if (req == NULL) {
+         ret = ENOMEM;
+         goto fail;
+@@ -611,9 +638,24 @@ ad_enumeration_done(struct tevent_req *subreq)
+ 
+     ret = sdap_dom_enum_ex_recv(subreq);
+     talloc_zfree(subreq);
+-    if (ret != EOK) {
++    if (ret == ERR_NO_POSIX) {
++        /* Retry enumerating the same domain again, this time w/o
++         * connecting to GC
++         */
++        disable_gc(state->id_ctx->ad_options);
++        ret = ad_enum_sdom(req, state->sditer, state->id_ctx);
++        if (ret != EOK) {
++            DEBUG(SSSDBG_OP_FAILURE,
++                ("Could not retry domain %s\n", state->sditer->dom->name));
++            tevent_req_error(req, ret);
++            return;
++        }
++
++        /* Execution will resume in ad_enumeration_done */
++        return;
++    } else if (ret != EOK) {
+         DEBUG(SSSDBG_OP_FAILURE,
+-              ("Could not enumerate domain %s\n", state->sdom->dom->name));
++              ("Could not enumerate domain %s\n", state->sditer->dom->name));
+         tevent_req_error(req, ret);
+         return;
+     }
+diff --git a/src/providers/ad/ad_id.h b/src/providers/ad/ad_id.h
+index 74b85645c2d6458617a4064fe3fb3f99696c3741..9eb0ac3754be2fd687ed2257b91854f5fceb82a2 100644
+--- a/src/providers/ad/ad_id.h
++++ b/src/providers/ad/ad_id.h
+@@ -31,6 +31,7 @@ ad_handle_acct_info_send(TALLOC_CTX *mem_ctx,
+                          struct be_req *breq,
+                          struct be_acct_req *ar,
+                          struct sdap_id_ctx *ctx,
++                         struct ad_options *ad_options,
+                          struct sdap_domain *sdom,
+                          struct sdap_id_conn_ctx **conn);
+ errno_t
+diff --git a/src/providers/ipa/ipa_subdomains_id.c b/src/providers/ipa/ipa_subdomains_id.c
+index fb1ad896885866dd9c34f9db960e09d92763f86d..b61c6a5f4d7605f0cdfa182bbc933d35c4613a79 100644
+--- a/src/providers/ipa/ipa_subdomains_id.c
++++ b/src/providers/ipa/ipa_subdomains_id.c
+@@ -323,7 +323,7 @@ ipa_get_ad_acct_send(TALLOC_CTX *mem_ctx,
+     }
+ 
+     subreq = ad_handle_acct_info_send(req, be_req, ar, sdap_id_ctx,
+-                                      sdom, clist);
++                                      ad_id_ctx->ad_options, sdom, clist);
+     if (subreq == NULL) {
+         ret = ENOMEM;
+         goto fail;
+diff --git a/src/providers/ldap/ldap_id.c b/src/providers/ldap/ldap_id.c
+index e36c1f697c18e865a47d991dad103fc440456118..b948ba9f358af994bdbbb99a468f7dbe66e65c1d 100644
+--- a/src/providers/ldap/ldap_id.c
++++ b/src/providers/ldap/ldap_id.c
+@@ -50,6 +50,7 @@ struct users_get_state {
+ 
+     char *filter;
+     const char **attrs;
++    bool use_id_mapping;
+ 
+     int dp_error;
+     int sdap_ret;
+@@ -58,6 +59,8 @@ struct users_get_state {
+ 
+ static int users_get_retry(struct tevent_req *req);
+ static void users_get_connect_done(struct tevent_req *subreq);
++static void users_get_posix_check_done(struct tevent_req *subreq);
++static void users_get_search(struct tevent_req *req);
+ static void users_get_done(struct tevent_req *subreq);
+ 
+ struct tevent_req *users_get_send(TALLOC_CTX *memctx,
+@@ -79,7 +82,6 @@ struct tevent_req *users_get_send(TALLOC_CTX *memctx,
+     uid_t uid;
+     enum idmap_error_code err;
+     char *sid;
+-    bool use_id_mapping;
+ 
+     req = tevent_req_create(memctx, &state, struct users_get_state);
+     if (!req) return NULL;
+@@ -103,7 +105,7 @@ struct tevent_req *users_get_send(TALLOC_CTX *memctx,
+     state->name = name;
+     state->filter_type = filter_type;
+ 
+-    use_id_mapping = sdap_idmap_domain_has_algorithmic_mapping(
++    state->use_id_mapping = sdap_idmap_domain_has_algorithmic_mapping(
+                                                           ctx->opts->idmap_ctx,
+                                                           sdom->dom->name,
+                                                           sdom->dom->domain_id);
+@@ -116,7 +118,7 @@ struct tevent_req *users_get_send(TALLOC_CTX *memctx,
+         }
+         break;
+     case BE_FILTER_IDNUM:
+-        if (use_id_mapping) {
++        if (state->use_id_mapping) {
+             /* If we're ID-mapping, we need to use the objectSID
+              * in the search filter.
+              */
+@@ -184,7 +186,7 @@ struct tevent_req *users_get_send(TALLOC_CTX *memctx,
+         goto fail;
+     }
+ 
+-    if (use_id_mapping || filter_type == BE_FILTER_SECID) {
++    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.
+@@ -269,6 +271,75 @@ static void users_get_connect_done(struct tevent_req *subreq)
+         return;
+     }
+ 
++    /* If POSIX attributes have been requested with an AD server and we
++     * have no idea about POSIX attributes support, run a one-time check
++     */
++    if (state->use_id_mapping == false &&
++            state->ctx->opts->schema_type == SDAP_SCHEMA_AD &&
++            state->ctx->srv_opts &&
++            state->ctx->srv_opts->posix_checked == false) {
++        subreq = sdap_posix_check_send(state, state->ev, state->ctx->opts,
++                                       sdap_id_op_handle(state->op),
++                                       state->sdom->user_search_bases,
++                                       dp_opt_get_int(state->ctx->opts->basic,
++                                                      SDAP_SEARCH_TIMEOUT));
++        if (subreq == NULL) {
++            tevent_req_error(req, ENOMEM);
++            return;
++        }
++        tevent_req_set_callback(subreq, users_get_posix_check_done, req);
++        return;
++    }
++
++    users_get_search(req);
++}
++
++static void users_get_posix_check_done(struct tevent_req *subreq)
++{
++    errno_t ret;
++    bool has_posix;
++    int dp_error;
++    struct tevent_req *req = tevent_req_callback_data(subreq,
++                                                      struct tevent_req);
++    struct users_get_state *state = tevent_req_data(req,
++                                                    struct users_get_state);
++
++    ret = sdap_posix_check_recv(subreq, &has_posix);
++    talloc_zfree(subreq);
++    if (ret != EOK) {
++        /* We can only finish the id_op on error as the connection
++         * is re-used by the user search
++         */
++        ret = sdap_id_op_done(state->op, ret, &dp_error);
++        if (dp_error == DP_ERR_OK && ret != EOK) {
++            /* retry */
++            ret = users_get_retry(req);
++            if (ret != EOK) {
++                tevent_req_error(req, ret);
++            }
++            return;
++        }
++    }
++
++    state->ctx->srv_opts->posix_checked = true;
++
++    /* If the check ran to completion, we know for certain about the attributes
++     */
++    if (has_posix == false) {
++        state->sdap_ret = ERR_NO_POSIX;
++        tevent_req_done(req);
++        return;
++    }
++
++    users_get_search(req);
++}
++
++static void users_get_search(struct tevent_req *req)
++{
++    struct users_get_state *state = tevent_req_data(req,
++                                                     struct users_get_state);
++    struct tevent_req *subreq;
++
+     subreq = sdap_get_users_send(state, state->ev,
+                                  state->domain, state->sysdb,
+                                  state->ctx->opts,
+@@ -434,6 +505,7 @@ struct groups_get_state {
+ 
+     char *filter;
+     const char **attrs;
++    bool use_id_mapping;
+ 
+     int dp_error;
+     int sdap_ret;
+@@ -442,6 +514,8 @@ struct groups_get_state {
+ 
+ static int groups_get_retry(struct tevent_req *req);
+ static void groups_get_connect_done(struct tevent_req *subreq);
++static void groups_get_posix_check_done(struct tevent_req *subreq);
++static void groups_get_search(struct tevent_req *req);
+ static void groups_get_done(struct tevent_req *subreq);
+ 
+ struct tevent_req *groups_get_send(TALLOC_CTX *memctx,
+@@ -463,7 +537,6 @@ struct tevent_req *groups_get_send(TALLOC_CTX *memctx,
+     gid_t gid;
+     enum idmap_error_code err;
+     char *sid;
+-    bool use_id_mapping;
+     const char *member_filter[2];
+ 
+     req = tevent_req_create(memctx, &state, struct groups_get_state);
+@@ -488,7 +561,7 @@ struct tevent_req *groups_get_send(TALLOC_CTX *memctx,
+     state->name = name;
+     state->filter_type = filter_type;
+ 
+-    use_id_mapping = sdap_idmap_domain_has_algorithmic_mapping(
++    state->use_id_mapping = sdap_idmap_domain_has_algorithmic_mapping(
+                                                           ctx->opts->idmap_ctx,
+                                                           sdom->dom->name,
+                                                           sdom->dom->domain_id);
+@@ -503,7 +576,7 @@ struct tevent_req *groups_get_send(TALLOC_CTX *memctx,
+         }
+         break;
+     case BE_FILTER_IDNUM:
+-        if (use_id_mapping) {
++        if (state->use_id_mapping) {
+             /* If we're ID-mapping, we need to use the objectSID
+              * in the search filter.
+              */
+@@ -571,7 +644,7 @@ struct tevent_req *groups_get_send(TALLOC_CTX *memctx,
+         goto fail;
+     }
+ 
+-    if (use_id_mapping || filter_type == BE_FILTER_SECID) {
++    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
+          */
+@@ -660,6 +733,75 @@ static void groups_get_connect_done(struct tevent_req *subreq)
+         return;
+     }
+ 
++    /* If POSIX attributes have been requested with an AD server and we
++     * have no idea about POSIX attributes support, run a one-time check
++     */
++    if (state->use_id_mapping == false &&
++            state->ctx->opts->schema_type == SDAP_SCHEMA_AD &&
++            state->ctx->srv_opts &&
++            state->ctx->srv_opts->posix_checked == false) {
++        subreq = sdap_posix_check_send(state, state->ev, state->ctx->opts,
++                                       sdap_id_op_handle(state->op),
++                                       state->sdom->user_search_bases,
++                                       dp_opt_get_int(state->ctx->opts->basic,
++                                                      SDAP_SEARCH_TIMEOUT));
++        if (subreq == NULL) {
++            tevent_req_error(req, ENOMEM);
++            return;
++        }
++        tevent_req_set_callback(subreq, groups_get_posix_check_done, req);
++        return;
++    }
++
++    groups_get_search(req);
++}
++
++static void groups_get_posix_check_done(struct tevent_req *subreq)
++{
++    errno_t ret;
++    bool has_posix;
++    int dp_error;
++    struct tevent_req *req = tevent_req_callback_data(subreq,
++                                                      struct tevent_req);
++    struct groups_get_state *state = tevent_req_data(req,
++                                                     struct groups_get_state);
++
++    ret = sdap_posix_check_recv(subreq, &has_posix);
++    talloc_zfree(subreq);
++    if (ret != EOK) {
++        /* We can only finish the id_op on error as the connection
++         * is re-used by the group search
++         */
++        ret = sdap_id_op_done(state->op, ret, &dp_error);
++        if (dp_error == DP_ERR_OK && ret != EOK) {
++            /* retry */
++            ret = groups_get_retry(req);
++            if (ret != EOK) {
++                tevent_req_error(req, ret);
++            }
++            return;
++        }
++    }
++
++    state->ctx->srv_opts->posix_checked = true;
++
++    /* If the check ran to completion, we know for certain about the attributes
++     */
++    if (has_posix == false) {
++        state->sdap_ret = ERR_NO_POSIX;
++        tevent_req_done(req);
++        return;
++    }
++
++    groups_get_search(req);
++}
++
++static void groups_get_search(struct tevent_req *req)
++{
++    struct groups_get_state *state = tevent_req_data(req,
++                                                     struct groups_get_state);
++    struct tevent_req *subreq;
++
+     subreq = sdap_get_groups_send(state, state->ev,
+                                   state->sdom,
+                                   state->ctx->opts,
+diff --git a/src/providers/ldap/sdap.h b/src/providers/ldap/sdap.h
+index d408be0a65cdd840d8379b7af4c0ab1e67ed3f5c..f3f13e9c72b368af0c14e2d830a89a41e8cf77e4 100644
+--- a/src/providers/ldap/sdap.h
++++ b/src/providers/ldap/sdap.h
+@@ -446,6 +446,7 @@ struct sdap_server_opts {
+     char *max_group_value;
+     char *max_service_value;
+     char *max_sudo_value;
++    bool posix_checked;
+ };
+ 
+ struct sdap_id_ctx;
+diff --git a/src/providers/ldap/sdap_async.c b/src/providers/ldap/sdap_async.c
+index 367007bde0011ed4de283b2a50b22538830a5275..1022a093f06ec7e9a50b13160fc9a4660a255e92 100644
+--- a/src/providers/ldap/sdap_async.c
++++ b/src/providers/ldap/sdap_async.c
+@@ -21,6 +21,7 @@
+ 
+ #include <ctype.h>
+ #include "util/util.h"
++#include "util/strtonum.h"
+ #include "providers/ldap/sdap_async_private.h"
+ 
+ #define REALM_SEPARATOR '@'
+@@ -2083,6 +2084,205 @@ int sdap_asq_search_recv(struct tevent_req *req,
+     return EOK;
+ }
+ 
++/* ==Posix attribute presence test================================= */
++static errno_t sdap_posix_check_next(struct tevent_req *req);
++static void sdap_posix_check_done(struct tevent_req *subreq);
++static errno_t sdap_posix_check_parse(struct sdap_handle *sh,
++                                      struct sdap_msg *msg,
++                                      void *pvt);
++
++struct sdap_posix_check_state {
++    struct tevent_context *ev;
++    struct sdap_options *opts;
++    struct sdap_handle *sh;
++    struct sdap_search_base **search_bases;
++    int timeout;
++
++    const char **attrs;
++    const char *filter;
++    size_t base_iter;
++
++    bool has_posix;
++};
++
++struct tevent_req *
++sdap_posix_check_send(TALLOC_CTX *memctx, struct tevent_context *ev,
++                      struct sdap_options *opts, struct sdap_handle *sh,
++                      struct sdap_search_base **search_bases,
++                      int timeout)
++{
++    struct tevent_req *req = NULL;
++    struct sdap_posix_check_state *state;
++    errno_t ret;
++
++    req = tevent_req_create(memctx, &state, struct sdap_posix_check_state);
++    if (req == NULL) {
++        return NULL;
++    }
++    state->ev = ev;
++    state->sh = sh;
++    state->opts = opts;
++    state->search_bases = search_bases;
++    state->timeout = timeout;
++
++    state->attrs = talloc_array(state, const char *, 4);
++    if (state->attrs == NULL) {
++        ret = ENOMEM;
++        goto fail;
++    }
++    state->attrs[0] = "objectclass";
++    state->attrs[1] = opts->user_map[SDAP_AT_USER_UID].name;
++    state->attrs[2] = opts->group_map[SDAP_AT_GROUP_GID].name;
++    state->attrs[3] = NULL;
++
++    state->filter = talloc_asprintf(state, "(|(%s=*)(%s=*))",
++                                    opts->user_map[SDAP_AT_USER_UID].name,
++                                    opts->group_map[SDAP_AT_GROUP_GID].name);
++    if (state->filter == NULL) {
++        ret = ENOMEM;
++        goto fail;
++    }
++
++    ret = sdap_posix_check_next(req);
++    if (ret != EOK) {
++        goto fail;
++    }
++
++    return req;
++
++fail:
++    tevent_req_error(req, ret);
++    tevent_req_post(req, ev);
++    return req;
++}
++
++static errno_t sdap_posix_check_next(struct tevent_req *req)
++{
++    struct tevent_req *subreq = NULL;
++    struct sdap_posix_check_state *state =
++        tevent_req_data(req, struct sdap_posix_check_state);
++
++    DEBUG(SSSDBG_TRACE_FUNC,
++          ("Searching for POSIX attributes with base [%s]\n",
++           state->search_bases[state->base_iter]->basedn));
++
++    subreq = sdap_get_generic_ext_send(state, state->ev, state->opts,
++                                 state->sh,
++                                 state->search_bases[state->base_iter]->basedn,
++                                 LDAP_SCOPE_SUBTREE, state->filter,
++                                 state->attrs, false,
++                                 NULL, NULL, 1, state->timeout,
++                                 false, sdap_posix_check_parse,
++                                 state);
++    if (subreq == NULL) {
++        return ENOMEM;
++    }
++    tevent_req_set_callback(subreq, sdap_posix_check_done, req);
++
++    return EOK;
++}
++
++static errno_t sdap_posix_check_parse(struct sdap_handle *sh,
++                                      struct sdap_msg *msg,
++                                      void *pvt)
++{
++    struct berval **vals = NULL;
++    struct sdap_posix_check_state *state =
++        talloc_get_type(pvt, struct sdap_posix_check_state);
++    char *dn;
++    char *endptr;
++
++    dn = ldap_get_dn(sh->ldap, msg->msg);
++    if (dn == NULL) {
++        DEBUG(SSSDBG_TRACE_LIBS,
++              ("Search did not find any entry with POSIX attributes\n"));
++        goto done;
++    }
++    DEBUG(SSSDBG_TRACE_LIBS, ("Found [%s] with POSIX attributes\n", dn));
++    ldap_memfree(dn);
++
++    vals = ldap_get_values_len(sh->ldap, msg->msg,
++                               state->opts->user_map[SDAP_AT_USER_UID].name);
++    if (vals == NULL) {
++        vals = ldap_get_values_len(sh->ldap, msg->msg,
++                               state->opts->group_map[SDAP_AT_GROUP_GID].name);
++        if (vals == NULL) {
++            DEBUG(SSSDBG_TRACE_LIBS, ("Entry does not have POSIX attrs?\n"));
++            goto done;
++        }
++    }
++
++    if (vals[0] == NULL) {
++        DEBUG(SSSDBG_TRACE_LIBS, ("No value for POSIX attr\n"));
++        goto done;
++    }
++
++    errno = 0;
++    strtouint32(vals[0]->bv_val, &endptr, 10);
++    if (errno || *endptr || (vals[0]->bv_val == endptr)) {
++        DEBUG(SSSDBG_OP_FAILURE,
++              ("POSIX attribute is not a number: %s\n", vals[0]->bv_val));
++        goto done;
++    }
++
++    state->has_posix = true;
++done:
++    ldap_value_free_len(vals);
++    return EOK;
++}
++
++static void sdap_posix_check_done(struct tevent_req *subreq)
++{
++    struct tevent_req *req = tevent_req_callback_data(subreq,
++                                                      struct tevent_req);
++    struct sdap_posix_check_state *state =
++        tevent_req_data(req, struct sdap_posix_check_state);
++    errno_t ret;
++
++    ret = sdap_get_generic_ext_recv(subreq);
++    talloc_zfree(subreq);
++    if (ret != EOK) {
++        DEBUG(SSSDBG_OP_FAILURE,
++              ("sdap_get_generic_ext_recv failed [%d]: %s\n",
++              ret, strerror(ret)));
++        tevent_req_error(req, ret);
++        return;
++    }
++
++    /* Positive hit is definitve, no need to search other bases */
++    if (state->has_posix == true) {
++        DEBUG(SSSDBG_FUNC_DATA, ("Server has POSIX attributes\n"));
++        tevent_req_done(req);
++        return;
++    }
++
++    state->base_iter++;
++    if (state->search_bases[state->base_iter]) {
++        /* There are more search bases to try */
++        ret = sdap_posix_check_next(req);
++        if (ret != EOK) {
++            tevent_req_error(req, ret);
++        }
++        return;
++    }
++
++    /* All bases done! */
++    DEBUG(SSSDBG_TRACE_LIBS, ("Cycled through all bases\n"));
++    tevent_req_done(req);
++}
++
++int sdap_posix_check_recv(struct tevent_req *req,
++                          bool *_has_posix)
++{
++    struct sdap_posix_check_state *state = tevent_req_data(req,
++                                            struct sdap_posix_check_state);
++
++    TEVENT_REQ_RETURN_ON_ERROR(req);
++
++    *_has_posix = state->has_posix;
++    return EOK;
++}
++
+ /* ==Generic Deref Search============================================ */
+ enum sdap_deref_type {
+     SDAP_DEREF_OPENLDAP,
+diff --git a/src/providers/ldap/sdap_async.h b/src/providers/ldap/sdap_async.h
+index 33e8708ab7e80ab4280df300fdc300d4ecd18305..593404af3c460f8d956b0832b8f7e398b331729b 100644
+--- a/src/providers/ldap/sdap_async.h
++++ b/src/providers/ldap/sdap_async.h
+@@ -210,6 +210,15 @@ int sdap_deref_search_recv(struct tevent_req *req,
+                            size_t *reply_count,
+                            struct sdap_deref_attrs ***reply);
+ 
++struct tevent_req *
++sdap_posix_check_send(TALLOC_CTX *memctx, struct tevent_context *ev,
++                      struct sdap_options *opts, struct sdap_handle *sh,
++                      struct sdap_search_base **search_bases,
++                      int timeout);
++
++int sdap_posix_check_recv(struct tevent_req *req,
++                          bool *_has_posix);
++
+ errno_t
+ sdap_attrs_add_ldap_attr(struct sysdb_attrs *ldap_attrs,
+                          const char *attr_name,
+diff --git a/src/providers/ldap/sdap_async_enum.c b/src/providers/ldap/sdap_async_enum.c
+index cbc56be20526e6c2323f9fd1b49038dd4bf13fe5..0c20afa9d7a1b0198bb71cffdafcb14763c1dafb 100644
+--- a/src/providers/ldap/sdap_async_enum.c
++++ b/src/providers/ldap/sdap_async_enum.c
+@@ -68,6 +68,8 @@ static errno_t sdap_dom_enum_ex_retry(struct tevent_req *req,
+                                       tevent_req_fn tcb);
+ static bool sdap_dom_enum_ex_connected(struct tevent_req *subreq);
+ static void sdap_dom_enum_ex_get_users(struct tevent_req *subreq);
++static void sdap_dom_enum_ex_posix_check_done(struct tevent_req *subreq);
++static errno_t sdap_dom_enum_search_users(struct tevent_req *req);
+ static void sdap_dom_enum_ex_users_done(struct tevent_req *subreq);
+ static void sdap_dom_enum_ex_get_groups(struct tevent_req *subreq);
+ static void sdap_dom_enum_ex_groups_done(struct tevent_req *subreq);
+@@ -178,19 +180,109 @@ static void sdap_dom_enum_ex_get_users(struct tevent_req *subreq)
+                                                       struct tevent_req);
+     struct sdap_dom_enum_ex_state *state = tevent_req_data(req,
+                                                 struct sdap_dom_enum_ex_state);
++    bool use_id_mapping;
++    errno_t ret;
+ 
+     if (sdap_dom_enum_ex_connected(subreq) == false) {
+         return;
+     }
+ 
++    use_id_mapping = sdap_idmap_domain_has_algorithmic_mapping(
++                                            state->ctx->opts->idmap_ctx,
++                                            state->sdom->dom->name,
++                                            state->sdom->dom->domain_id);
++
++    /* If POSIX attributes have been requested with an AD server and we
++     * have no idea about POSIX attributes support, run a one-time check
++     */
++    if (use_id_mapping == false &&
++            state->ctx->opts->schema_type == SDAP_SCHEMA_AD &&
++            state->ctx->srv_opts &&
++            state->ctx->srv_opts->posix_checked == false) {
++        subreq = sdap_posix_check_send(state, state->ev, state->ctx->opts,
++                                       sdap_id_op_handle(state->user_op),
++                                       state->sdom->user_search_bases,
++                                       dp_opt_get_int(state->ctx->opts->basic,
++                                                      SDAP_SEARCH_TIMEOUT));
++        if (subreq == NULL) {
++            tevent_req_error(req, ENOMEM);
++            return;
++        }
++        tevent_req_set_callback(subreq,
++                                sdap_dom_enum_ex_posix_check_done, req);
++        return;
++    }
++
++
++    ret = sdap_dom_enum_search_users(req);
++    if (ret != EOK) {
++        tevent_req_error(req, ret);
++        return;
++    }
++    /* Execution resumes in sdap_dom_enum_ex_users_done */
++}
++
++static void sdap_dom_enum_ex_posix_check_done(struct tevent_req *subreq)
++{
++    errno_t ret;
++    bool has_posix;
++    int dp_error;
++
++    struct tevent_req *req = tevent_req_callback_data(subreq,
++                                                      struct tevent_req);
++    struct sdap_dom_enum_ex_state *state = tevent_req_data(req,
++                                                struct sdap_dom_enum_ex_state);
++
++    ret = sdap_posix_check_recv(subreq, &has_posix);
++    talloc_zfree(subreq);
++    if (ret != EOK) {
++        /* We can only finish the id_op on error as the connection
++         * is re-used by the user search
++         */
++        ret = sdap_id_op_done(state->user_op, ret, &dp_error);
++        if (dp_error == DP_ERR_OK && ret != EOK) {
++            /* retry */
++            ret = sdap_dom_enum_ex_retry(req, state->user_op,
++                                         sdap_dom_enum_ex_get_users);
++            if (ret != EOK) {
++                tevent_req_error(req, ret);
++            }
++            return;
++        }
++    }
++
++    state->ctx->srv_opts->posix_checked = true;
++
++    /* If the check ran to completion, we know for certain about the attributes
++     */
++    if (has_posix == false) {
++        tevent_req_error(req, ERR_NO_POSIX);
++        return;
++    }
++
++
++    ret = sdap_dom_enum_search_users(req);
++    if (ret != EOK) {
++        tevent_req_error(req, ret);
++        return;
++    }
++    /* Execution resumes in sdap_dom_enum_ex_users_done */
++}
++
++static errno_t sdap_dom_enum_search_users(struct tevent_req *req)
++{
++    struct sdap_dom_enum_ex_state *state = tevent_req_data(req,
++                                                struct sdap_dom_enum_ex_state);
++    struct tevent_req *subreq;
++
+     subreq = enum_users_send(state, state->ev,
+                              state->ctx, state->sdom,
+                              state->user_op, state->purge);
+     if (subreq == NULL) {
+-        tevent_req_error(req, ENOMEM);
+-        return;
++        return ENOMEM;
+     }
+     tevent_req_set_callback(subreq, sdap_dom_enum_ex_users_done, req);
++    return EOK;
+ }
+ 
+ static void sdap_dom_enum_ex_users_done(struct tevent_req *subreq)
+diff --git a/src/util/util_errors.c b/src/util/util_errors.c
+index 633257e8da0ef039e555a07ad8b51125114ca01c..c9b507557da07555c719bb0dd18145e6799a53eb 100644
+--- a/src/util/util_errors.c
++++ b/src/util/util_errors.c
+@@ -52,6 +52,7 @@ struct err_string error_to_str[] = {
+     { "Domain not found" }, /* ERR_DOMAIN_NOT_FOUND */
+     { "Missing configuration file" }, /* ERR_MISSING_CONF */
+     { "Malformed search filter" }, /* ERR_INVALID_FILTER, */
++    { "No POSIX attributes detected" }, /* ERR_NO_POSIX */
+ };
+ 
+ 
+diff --git a/src/util/util_errors.h b/src/util/util_errors.h
+index 1332085031dbe6935cbdc94543fa14b09fe81028..3dd94af1f304d65e22515c859c6f69a021fa7e92 100644
+--- a/src/util/util_errors.h
++++ b/src/util/util_errors.h
+@@ -74,6 +74,7 @@ enum sssd_errors {
+     ERR_DOMAIN_NOT_FOUND,
+     ERR_MISSING_CONF,
+     ERR_INVALID_FILTER,
++    ERR_NO_POSIX,
+     ERR_LAST            /* ALWAYS LAST */
+ };
+ 
+-- 
+1.8.5.3
+
diff --git a/SOURCES/0082-AD-Only-download-domains-that-are-set-to-enumerate.patch b/SOURCES/0082-AD-Only-download-domains-that-are-set-to-enumerate.patch
new file mode 100644
index 0000000..b9b8cc0
--- /dev/null
+++ b/SOURCES/0082-AD-Only-download-domains-that-are-set-to-enumerate.patch
@@ -0,0 +1,34 @@
+From 94f1e5966733e1b327a9eba37781379c5d90f8a3 Mon Sep 17 00:00:00 2001
+From: Jakub Hrozek <jhrozek@redhat.com>
+Date: Wed, 5 Feb 2014 16:39:47 +0100
+Subject: [PATCH 82/84] AD: Only download domains that are set to enumerate
+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 957c55df7a7086166fb3c14cead6a0dab8f574c1)
+---
+ src/providers/ad/ad_id.c | 6 +++++-
+ 1 file changed, 5 insertions(+), 1 deletion(-)
+
+diff --git a/src/providers/ad/ad_id.c b/src/providers/ad/ad_id.c
+index bfae86284355b6c13547aac55b7273133bde851d..2d3c11bb838f1418c006f9d79d8552cec1443e66 100644
+--- a/src/providers/ad/ad_id.c
++++ b/src/providers/ad/ad_id.c
+@@ -660,7 +660,11 @@ ad_enumeration_done(struct tevent_req *subreq)
+         return;
+     }
+ 
+-    state->sditer = state->sditer->next;
++    do {
++        state->sditer = state->sditer->next;
++    } while (state->sditer &&
++             state->sditer->dom->enumerate == false);
++
+     if (state->sditer != NULL) {
+         subdom_id_ctx = talloc_get_type(state->sdom->pvt, struct ad_id_ctx);
+         if (subdom_id_ctx == NULL) {
+-- 
+1.8.5.3
+
diff --git a/SOURCES/0083-AD-Remove-dead-code.patch b/SOURCES/0083-AD-Remove-dead-code.patch
new file mode 100644
index 0000000..178d308
--- /dev/null
+++ b/SOURCES/0083-AD-Remove-dead-code.patch
@@ -0,0 +1,43 @@
+From 7f9da1ed660e45e276778187260ddbbf411c761e Mon Sep 17 00:00:00 2001
+From: Jakub Hrozek <jhrozek@redhat.com>
+Date: Wed, 5 Feb 2014 16:40:41 +0100
+Subject: [PATCH 83/84] AD: Remove dead code
+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 d3436880c0ec1a7776698c739d4a3edc9a6ac57c)
+---
+ src/providers/ad/ad_id.c | 8 --------
+ 1 file changed, 8 deletions(-)
+
+diff --git a/src/providers/ad/ad_id.c b/src/providers/ad/ad_id.c
+index 2d3c11bb838f1418c006f9d79d8552cec1443e66..87af656b364344a8ef27a444e5dfcf8848939110 100644
+--- a/src/providers/ad/ad_id.c
++++ b/src/providers/ad/ad_id.c
+@@ -634,7 +634,6 @@ ad_enumeration_done(struct tevent_req *subreq)
+                                                       struct tevent_req);
+     struct ad_enumeration_state *state = tevent_req_data(req,
+                                                 struct ad_enumeration_state);
+-    struct ad_id_ctx *subdom_id_ctx;
+ 
+     ret = sdap_dom_enum_ex_recv(subreq);
+     talloc_zfree(subreq);
+@@ -666,13 +665,6 @@ ad_enumeration_done(struct tevent_req *subreq)
+              state->sditer->dom->enumerate == false);
+ 
+     if (state->sditer != NULL) {
+-        subdom_id_ctx = talloc_get_type(state->sdom->pvt, struct ad_id_ctx);
+-        if (subdom_id_ctx == NULL) {
+-            DEBUG(SSSDBG_CRIT_FAILURE, ("Cannot retrieve subdomain ad_id_ctx!\n"));
+-            tevent_req_error(req, EFAULT);
+-            return;
+-        }
+-
+         ret = ad_enum_sdom(req, state->sditer, state->sditer->pvt);
+         if (ret != EOK) {
+             DEBUG(SSSDBG_OP_FAILURE, ("Could not enumerate domain %s\n",
+-- 
+1.8.5.3
+
diff --git a/SOURCES/0084-LDAP-Handle-errors-from-sdap_id_op-properly-in-enum-.patch b/SOURCES/0084-LDAP-Handle-errors-from-sdap_id_op-properly-in-enum-.patch
new file mode 100644
index 0000000..88049e5
--- /dev/null
+++ b/SOURCES/0084-LDAP-Handle-errors-from-sdap_id_op-properly-in-enum-.patch
@@ -0,0 +1,99 @@
+From 97616c8e1ab41c72c509a31626fafcfb07241a46 Mon Sep 17 00:00:00 2001
+From: Jakub Hrozek <jhrozek@redhat.com>
+Date: Tue, 11 Feb 2014 12:23:51 +0100
+Subject: [PATCH 84/84] LDAP: Handle errors from sdap_id_op properly in enum
+ code
+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 93dabb2fe0a798f22bb802b9c6521ab9e6a4ac36)
+---
+ src/providers/ldap/sdap_async_enum.c | 42 +++++++++++++++++++++++++++++++++++-
+ 1 file changed, 41 insertions(+), 1 deletion(-)
+
+diff --git a/src/providers/ldap/sdap_async_enum.c b/src/providers/ldap/sdap_async_enum.c
+index 0c20afa9d7a1b0198bb71cffdafcb14763c1dafb..0431d03c38098080847035d522ab5f1f494b8bfe 100644
+--- a/src/providers/ldap/sdap_async_enum.c
++++ b/src/providers/ldap/sdap_async_enum.c
+@@ -235,7 +235,7 @@ static void sdap_dom_enum_ex_posix_check_done(struct tevent_req *subreq)
+ 
+     ret = sdap_posix_check_recv(subreq, &has_posix);
+     talloc_zfree(subreq);
+-    if (ret != EOK) {
++    if (ret != EOK && ret != ERR_NO_POSIX) {
+         /* We can only finish the id_op on error as the connection
+          * is re-used by the user search
+          */
+@@ -248,6 +248,16 @@ static void sdap_dom_enum_ex_posix_check_done(struct tevent_req *subreq)
+                 tevent_req_error(req, ret);
+             }
+             return;
++        } else if (dp_error == DP_ERR_OFFLINE) {
++            DEBUG(SSSDBG_TRACE_FUNC, ("Backend is offline, retrying later\n"));
++            tevent_req_done(req);
++            return;
++        } else {
++            /* Non-recoverable error */
++            DEBUG(SSSDBG_OP_FAILURE,
++                ("POSIX check failed: %d: %s\n", ret, sss_strerror(ret)));
++            tevent_req_error(req, ret);
++            return;
+         }
+     }
+ 
+@@ -306,6 +316,16 @@ static void sdap_dom_enum_ex_users_done(struct tevent_req *subreq)
+             return;
+         }
+         return;
++    } else if (dp_error == DP_ERR_OFFLINE) {
++        DEBUG(SSSDBG_TRACE_FUNC, ("Backend is offline, retrying later\n"));
++        tevent_req_done(req);
++        return;
++    } else if (ret != EOK && ret != ENOENT) {
++        /* Non-recoverable error */
++        DEBUG(SSSDBG_OP_FAILURE,
++              ("User enumeration failed: %d: %s\n", ret, sss_strerror(ret)));
++        tevent_req_error(req, ret);
++        return;
+     }
+ 
+     state->group_op = sdap_id_op_create(state, state->group_conn->conn_cache);
+@@ -367,6 +387,16 @@ static void sdap_dom_enum_ex_groups_done(struct tevent_req *subreq)
+             return;
+         }
+         return;
++    } else if (dp_error == DP_ERR_OFFLINE) {
++        DEBUG(SSSDBG_TRACE_FUNC, ("Backend is offline, retrying later\n"));
++        tevent_req_done(req);
++        return;
++    } else if (ret != EOK && ret != ENOENT) {
++        /* Non-recoverable error */
++        DEBUG(SSSDBG_OP_FAILURE,
++              ("Group enumeration failed: %d: %s\n", ret, sss_strerror(ret)));
++        tevent_req_error(req, ret);
++        return;
+     }
+ 
+ 
+@@ -426,6 +456,16 @@ static void sdap_dom_enum_ex_svcs_done(struct tevent_req *subreq)
+             return;
+         }
+         return;
++    } else if (dp_error == DP_ERR_OFFLINE) {
++        DEBUG(SSSDBG_TRACE_FUNC, ("Backend is offline, retrying later\n"));
++        tevent_req_done(req);
++        return;
++    } else if (ret != EOK && ret != ENOENT) {
++        /* Non-recoverable error */
++        DEBUG(SSSDBG_OP_FAILURE,
++              ("Service enumeration failed: %d: %s\n", ret, sss_strerror(ret)));
++        tevent_req_error(req, ret);
++        return;
+     }
+ 
+     /* Ok, we've completed an enumeration. Save this to the
+-- 
+1.8.5.3
+
diff --git a/SOURCES/0085-SSS_CACHE-Reset-the-initgroups-attribute-when-resett.patch b/SOURCES/0085-SSS_CACHE-Reset-the-initgroups-attribute-when-resett.patch
new file mode 100644
index 0000000..684c1e6
--- /dev/null
+++ b/SOURCES/0085-SSS_CACHE-Reset-the-initgroups-attribute-when-resett.patch
@@ -0,0 +1,35 @@
+From a5910c05a1013c9a050f6df3d4c6884e894bf2d9 Mon Sep 17 00:00:00 2001
+From: Jakub Hrozek <jhrozek@redhat.com>
+Date: Tue, 11 Feb 2014 22:51:48 +0100
+Subject: [PATCH 85/88] SSS_CACHE: Reset the initgroups attribute when
+ resetting users
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+(cherry picked from commit 30ee051025753b63ceb19d3b83c44019a19554a1)
+Reviewed-by: Lukáš Slebodník <lslebodn@redhat.com>
+---
+ src/tools/sss_cache.c | 6 ++++++
+ 1 file changed, 6 insertions(+)
+
+diff --git a/src/tools/sss_cache.c b/src/tools/sss_cache.c
+index 56dc47afdcb92b71dc1ef71d7f26fdf276a1c45f..59b36db3e25333f4ca5da241b607edda226b929f 100644
+--- a/src/tools/sss_cache.c
++++ b/src/tools/sss_cache.c
+@@ -424,6 +424,12 @@ static errno_t invalidate_entry(TALLOC_CTX *ctx, struct sysdb_ctx *sysdb,
+         if (ret == EOK) {
+             switch (entry_type) {
+                 case TYPE_USER:
++                    /* For users, we also need to reset the initgroups
++                     * cache expiry */
++                    ret = sysdb_attrs_add_time_t(sys_attrs,
++                            SYSDB_INITGR_EXPIRE, 1);
++                    if (ret != EOK) return ret;
++
+                     ret = sysdb_set_user_attr(sysdb, domain, name, sys_attrs,
+                                               SYSDB_MOD_REP);
+                     break;
+-- 
+1.8.5.3
+
diff --git a/SOURCES/0086-IPA-Default-to-krb5_use_fast-try.patch b/SOURCES/0086-IPA-Default-to-krb5_use_fast-try.patch
new file mode 100644
index 0000000..724e723
--- /dev/null
+++ b/SOURCES/0086-IPA-Default-to-krb5_use_fast-try.patch
@@ -0,0 +1,70 @@
+From 529c7ade2f7f633fdb80e2f5b2055afd5a017d2f Mon Sep 17 00:00:00 2001
+From: Jakub Hrozek <jhrozek@redhat.com>
+Date: Tue, 11 Feb 2014 15:36:04 +0100
+Subject: [PATCH 86/88] IPA: Default to krb5_use_fast=try
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Reviewed-by: Pavel Březina <pbrezina@redhat.com>
+Reviewed-by: Nathaniel McCallum <npmccallum@redhat.com>
+Reviewed-by: Alexander Bokovoy <abokovoy@redhat.com>
+---
+ src/providers/ipa/ipa_common.c | 27 +++++++++++++++++++++++++++
+ src/providers/ipa/ipa_opts.h   |  2 +-
+ 2 files changed, 28 insertions(+), 1 deletion(-)
+
+diff --git a/src/providers/ipa/ipa_common.c b/src/providers/ipa/ipa_common.c
+index 671374098afa1f2e00fc9cf1788ba4383b600a1b..e0abd169302406a555728589185b67e0fbbcfe94 100644
+--- a/src/providers/ipa/ipa_common.c
++++ b/src/providers/ipa/ipa_common.c
+@@ -664,6 +664,33 @@ int ipa_get_auth_options(struct ipa_options *ipa_opts,
+                   dp_opt_get_string(ipa_opts->auth, KRB5_REALM)));
+     }
+ 
++    /* If krb5_fast_principal was not set explicitly, default to
++     * host/$client_hostname
++     */
++    value = dp_opt_get_string(ipa_opts->auth, KRB5_FAST_PRINCIPAL);
++    if (value == NULL) {
++        value = talloc_asprintf(ipa_opts->auth, "host/%s",
++                                    dp_opt_get_string(ipa_opts->basic,
++                                                      IPA_HOSTNAME));
++        if (value == NULL) {
++            DEBUG(SSSDBG_CRIT_FAILURE, ("Cannot set %s!\n",
++                     ipa_opts->auth[KRB5_FAST_PRINCIPAL].opt_name));
++            ret = ENOMEM;
++            goto done;
++        }
++
++        ret = dp_opt_set_string(ipa_opts->auth, KRB5_FAST_PRINCIPAL,
++                                value);
++        if (ret != EOK) {
++            DEBUG(SSSDBG_CRIT_FAILURE, ("Cannot set %s!\n",
++                     ipa_opts->auth[KRB5_FAST_PRINCIPAL].opt_name));
++            goto done;
++        }
++
++        DEBUG(SSSDBG_CONF_SETTINGS, ("Option %s set to %s\n",
++              ipa_opts->auth[KRB5_FAST_PRINCIPAL].opt_name, value));
++    }
++
+     /* Set flag that controls whether we want to write the
+      * kdcinfo files at all
+      */
+diff --git a/src/providers/ipa/ipa_opts.h b/src/providers/ipa/ipa_opts.h
+index 27dc3e2f977383836c18cb824abceb03c9e9056c..c46d421ad0bfb9272cbdadbfeea5ebcf65a7deb1 100644
+--- a/src/providers/ipa/ipa_opts.h
++++ b/src/providers/ipa/ipa_opts.h
+@@ -274,7 +274,7 @@ struct dp_option ipa_def_krb5_opts[] = {
+     { "krb5_renewable_lifetime", DP_OPT_STRING, NULL_STRING, NULL_STRING },
+     { "krb5_lifetime", DP_OPT_STRING, NULL_STRING, NULL_STRING },
+     { "krb5_renew_interval", DP_OPT_STRING, NULL_STRING, NULL_STRING },
+-    { "krb5_use_fast", DP_OPT_STRING, NULL_STRING, NULL_STRING },
++    { "krb5_use_fast", DP_OPT_STRING, { "try" }, NULL_STRING },
+     { "krb5_fast_principal", DP_OPT_STRING, NULL_STRING, NULL_STRING },
+     { "krb5_canonicalize", DP_OPT_BOOL, BOOL_TRUE, BOOL_TRUE },
+     { "krb5_use_enterprise_principal", DP_OPT_BOOL, BOOL_FALSE, BOOL_FALSE },
+-- 
+1.8.5.3
+
diff --git a/SOURCES/0087-IPA-default-krb5_fast_principal-to-host-client-realm.patch b/SOURCES/0087-IPA-default-krb5_fast_principal-to-host-client-realm.patch
new file mode 100644
index 0000000..ca8a886
--- /dev/null
+++ b/SOURCES/0087-IPA-default-krb5_fast_principal-to-host-client-realm.patch
@@ -0,0 +1,41 @@
+From 920e81404bc37e57f2d7613ca9031e2337c1edd0 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pavel=20B=C5=99ezina?= <pbrezina@redhat.com>
+Date: Fri, 14 Feb 2014 11:45:50 +0100
+Subject: [PATCH 87/88] IPA: default krb5_fast_principal to host/$client@$realm
+
+If krb5_fast_principal is not set in sssd.conf it was set to host/$client,
+KRB5 default realm was used which doesn't have to be the same as realm
+used for IPA, thus authentication failed when using FAST.
+
+Reviewed-by: Alexander Bokovoy <abokovoy@redhat.com>
+(cherry picked from commit e325cabe762fad7d696e014a7fdbb47a5cb8174a)
+---
+ src/providers/ipa/ipa_common.c | 8 +++++---
+ 1 file changed, 5 insertions(+), 3 deletions(-)
+
+diff --git a/src/providers/ipa/ipa_common.c b/src/providers/ipa/ipa_common.c
+index e0abd169302406a555728589185b67e0fbbcfe94..d4db1549b3657268381d0e425615c1b389fed23e 100644
+--- a/src/providers/ipa/ipa_common.c
++++ b/src/providers/ipa/ipa_common.c
+@@ -665,13 +665,15 @@ int ipa_get_auth_options(struct ipa_options *ipa_opts,
+     }
+ 
+     /* If krb5_fast_principal was not set explicitly, default to
+-     * host/$client_hostname
++     * host/$client_hostname@REALM
+      */
+     value = dp_opt_get_string(ipa_opts->auth, KRB5_FAST_PRINCIPAL);
+     if (value == NULL) {
+-        value = talloc_asprintf(ipa_opts->auth, "host/%s",
++        value = talloc_asprintf(ipa_opts->auth, "host/%s@%s",
+                                     dp_opt_get_string(ipa_opts->basic,
+-                                                      IPA_HOSTNAME));
++                                                      IPA_HOSTNAME),
++                                    dp_opt_get_string(ipa_opts->auth,
++                                                      KRB5_REALM));
+         if (value == NULL) {
+             DEBUG(SSSDBG_CRIT_FAILURE, ("Cannot set %s!\n",
+                      ipa_opts->auth[KRB5_FAST_PRINCIPAL].opt_name));
+-- 
+1.8.5.3
+
diff --git a/SOURCES/0088-MAN-Clarify-the-new-krb5_use_fast-IPA-default.patch b/SOURCES/0088-MAN-Clarify-the-new-krb5_use_fast-IPA-default.patch
new file mode 100644
index 0000000..a985a8c
--- /dev/null
+++ b/SOURCES/0088-MAN-Clarify-the-new-krb5_use_fast-IPA-default.patch
@@ -0,0 +1,71 @@
+From 4d5cbad45245016747aa34f2271f2fe5214cf34a Mon Sep 17 00:00:00 2001
+From: Jakub Hrozek <jhrozek@redhat.com>
+Date: Mon, 17 Feb 2014 17:30:52 +0100
+Subject: [PATCH 88/88] MAN: Clarify the new krb5_use_fast IPA default
+
+---
+ src/man/sssd-ipa.5.xml  | 34 ++++++++++++++++++++++++++++++++++
+ src/man/sssd-krb5.5.xml |  2 +-
+ 2 files changed, 35 insertions(+), 1 deletion(-)
+
+diff --git a/src/man/sssd-ipa.5.xml b/src/man/sssd-ipa.5.xml
+index 28ac252abbeb508d62ca1a94f2440afc6b5b5c88..7ab59dc20cc43c7ed86c0e1a988a30813b9fe673 100644
+--- a/src/man/sssd-ipa.5.xml
++++ b/src/man/sssd-ipa.5.xml
+@@ -399,6 +399,40 @@
+                 </varlistentry>
+ 
+                 <varlistentry>
++                    <term>krb5_use_fast (string)</term>
++                    <listitem>
++                        <para>
++                            Enables flexible authentication secure tunneling
++                            (FAST) for Kerberos pre-authentication. The
++                            following options are supported:
++                        </para>
++                        <para>
++                            <emphasis>never</emphasis> use FAST.
++                        </para>
++                        <para>
++                            <emphasis>try</emphasis> to use FAST. If the server
++                            does not support FAST, continue the
++                            authentication without it. This is
++                            equivalent to not setting this option at all.
++                        </para>
++                        <para>
++                            <emphasis>demand</emphasis> to use FAST. The
++                            authentication fails if the server does not
++                            require fast.
++                        </para>
++                        <para>
++                            Default: try
++                        </para>
++                        <para>
++                            NOTE: SSSD supports FAST only with
++                            MIT Kerberos version 1.8 and later. If SSSD is used
++                            with an older version of MIT Kerberos, using this
++                            option is a configuration error.
++                        </para>
++                    </listitem>
++                </varlistentry>
++
++                <varlistentry>
+                     <term>ipa_hbac_refresh (integer)</term>
+                     <listitem>
+                         <para>
+diff --git a/src/man/sssd-krb5.5.xml b/src/man/sssd-krb5.5.xml
+index 384d506616408c3f45f5b85621a8101ef4faa3e8..602c07e9c2e2b9c231c596d50be94b7d220c3257 100644
+--- a/src/man/sssd-krb5.5.xml
++++ b/src/man/sssd-krb5.5.xml
+@@ -502,7 +502,7 @@
+                         </para>
+ 
+                         <para>
+-                            Default: false (AD provide: true)
++                            Default: false (AD provider: true)
+                         </para>
+                     </listitem>
+                 </varlistentry>
+-- 
+1.8.5.3
+
diff --git a/SOURCES/0089-IPA-Don-t-call-tevent_req_post-outside-_send.patch b/SOURCES/0089-IPA-Don-t-call-tevent_req_post-outside-_send.patch
new file mode 100644
index 0000000..ac3ca2d
--- /dev/null
+++ b/SOURCES/0089-IPA-Don-t-call-tevent_req_post-outside-_send.patch
@@ -0,0 +1,29 @@
+From 4b1c7fee8fd597660e77436fd205802e353b2e97 Mon Sep 17 00:00:00 2001
+From: Jakub Hrozek <jhrozek@redhat.com>
+Date: Wed, 19 Feb 2014 15:00:15 +0100
+Subject: [PATCH 89/90] IPA: Don't call tevent_req_post outside _send
+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 6d4574a8dd1a9cafbb15631e7d01bdf6e67f821b)
+---
+ src/providers/ipa/ipa_subdomains_id.c | 1 -
+ 1 file changed, 1 deletion(-)
+
+diff --git a/src/providers/ipa/ipa_subdomains_id.c b/src/providers/ipa/ipa_subdomains_id.c
+index b61c6a5f4d7605f0cdfa182bbc933d35c4613a79..c15bdaa703835ab07a9b3b21d1304220a01eac10 100644
+--- a/src/providers/ipa/ipa_subdomains_id.c
++++ b/src/providers/ipa/ipa_subdomains_id.c
+@@ -580,7 +580,6 @@ ipa_get_ad_acct_ad_part_done(struct tevent_req *subreq)
+ fail:
+     state->dp_error = DP_ERR_FATAL;
+     tevent_req_error(req, ret);
+-    tevent_req_post(req, state->ev);
+     return;
+ }
+ 
+-- 
+1.8.5.3
+
diff --git a/SOURCES/0090-IPA-Don-t-fail-if-apply_subdomain_homedir-returns-EN.patch b/SOURCES/0090-IPA-Don-t-fail-if-apply_subdomain_homedir-returns-EN.patch
new file mode 100644
index 0000000..c09b52e
--- /dev/null
+++ b/SOURCES/0090-IPA-Don-t-fail-if-apply_subdomain_homedir-returns-EN.patch
@@ -0,0 +1,28 @@
+From da729f6765e11fec980fbb9ec1291e2a7ef533fe Mon Sep 17 00:00:00 2001
+From: Jakub Hrozek <jhrozek@redhat.com>
+Date: Wed, 19 Feb 2014 15:34:34 +0100
+Subject: [PATCH 90/90] IPA: Don't fail if apply_subdomain_homedir returns
+ ENOENT
+
+Reviewed-by: Pavel Reichl <preichl@redhat.com>
+(cherry picked from commit 26786da26706aeedbda4caea0383c143ed4e59dc)
+---
+ src/providers/ipa/ipa_subdomains_id.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/src/providers/ipa/ipa_subdomains_id.c b/src/providers/ipa/ipa_subdomains_id.c
+index c15bdaa703835ab07a9b3b21d1304220a01eac10..637dd61f9f272eb4ac4ecb8368d2210801bb0373 100644
+--- a/src/providers/ipa/ipa_subdomains_id.c
++++ b/src/providers/ipa/ipa_subdomains_id.c
+@@ -550,7 +550,7 @@ ipa_get_ad_acct_ad_part_done(struct tevent_req *subreq)
+     ret = apply_subdomain_homedir(state, state->user_dom,
+                                   state->ar->filter_type,
+                                   state->ar->filter_value);
+-    if (ret != EOK) {
++    if (ret != EOK && ret != ENOENT) {
+         DEBUG(SSSDBG_OP_FAILURE,
+               ("apply_subdomain_homedir failed: [%d]: [%s].\n",
+                ret, sss_strerror(ret)));
+-- 
+1.8.5.3
+
diff --git a/SOURCES/0091-LDAP-Setup-periodic-task-only-once.patch b/SOURCES/0091-LDAP-Setup-periodic-task-only-once.patch
new file mode 100644
index 0000000..00b4633
--- /dev/null
+++ b/SOURCES/0091-LDAP-Setup-periodic-task-only-once.patch
@@ -0,0 +1,132 @@
+From f7a7a583c475eb22a6d762e74c67ffcfa7ba32d0 Mon Sep 17 00:00:00 2001
+From: Lukas Slebodnik <lslebodn@redhat.com>
+Date: Wed, 12 Feb 2014 14:33:49 +0100
+Subject: [PATCH 91/92] LDAP: Setup periodic task only once.
+
+If id provider is {ipa, ad} periodic task will be stared in sssm_{ipa,ad}_init
+If you enable enumeration and use different providers for id and sudo(autofs)
+then another periodic task will be scheduled.
+This can cause weird behaviour (e.g. missing members of group)
+
+Perodic tasks will be started only by id_provider.
+
+Resolves:
+https://fedorahosted.org/sssd/ticket/2153
+
+Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
+(cherry picked from commit 057cb583f02bf47678c393cb8f1f74861c2b960b)
+---
+ src/providers/ldap/ldap_init.c | 54 ++++++++++++++++++++++++++++++++----------
+ 1 file changed, 41 insertions(+), 13 deletions(-)
+
+diff --git a/src/providers/ldap/ldap_init.c b/src/providers/ldap/ldap_init.c
+index 15615b2891f2e3104c11e8610c081adcd1d1ee8e..cf4ab5598e2b6eb00c188edabb61e22605e7dc82 100644
+--- a/src/providers/ldap/ldap_init.c
++++ b/src/providers/ldap/ldap_init.c
+@@ -84,9 +84,9 @@ errno_t check_order_list_for_duplicates(char **list,
+     return EOK;
+ }
+ 
+-int sssm_ldap_id_init(struct be_ctx *bectx,
+-                      struct bet_ops **ops,
+-                      void **pvt_data)
++static int ldap_id_init_internal(struct be_ctx *bectx,
++                                 struct bet_ops **ops,
++                                 void **pvt_data)
+ {
+     struct sdap_id_ctx *ctx = NULL;
+     const char *urls;
+@@ -160,11 +160,6 @@ int sssm_ldap_id_init(struct be_ctx *bectx,
+     ret = sdap_idmap_init(ctx, ctx, &ctx->opts->idmap_ctx);
+     if (ret != EOK) goto done;
+ 
+-    ret = ldap_id_setup_tasks(ctx);
+-    if (ret != EOK) {
+-        goto done;
+-    }
+-
+     ret = sdap_setup_child();
+     if (ret != EOK) {
+         DEBUG(1, ("setup_child failed [%d][%s].\n",
+@@ -202,6 +197,39 @@ done:
+     return ret;
+ }
+ 
++int sssm_ldap_id_init(struct be_ctx *bectx,
++                      struct bet_ops **ops,
++                      void **pvt_data)
++{
++    int ret;
++    struct sdap_id_ctx *ctx = NULL;
++
++    ret = ldap_id_init_internal(bectx, ops, (void **) &ctx);
++    if (ret != EOK) {
++        DEBUG(SSSDBG_MINOR_FAILURE,
++              ("ldap_id_init_internal failed [%d][%s].\n",
++              ret, strerror(ret)));
++        goto done;
++    }
++
++    ret = ldap_id_setup_tasks(ctx);
++    if (ret != EOK) {
++        DEBUG(SSSDBG_MINOR_FAILURE,
++              ("sdap_id_setup_tasks failed [%d][%s].\n",
++              ret, strerror(ret)));
++        goto done;
++    }
++
++    *pvt_data = ctx;
++    ret = EOK;
++
++done:
++    if (ret != EOK) {
++        talloc_free(ctx);
++    }
++    return ret;
++}
++
+ int sssm_ldap_auth_init(struct be_ctx *bectx,
+                         struct bet_ops **ops,
+                         void **pvt_data)
+@@ -211,7 +239,7 @@ int sssm_ldap_auth_init(struct be_ctx *bectx,
+     struct sdap_auth_ctx *ctx;
+     int ret;
+ 
+-    ret = sssm_ldap_id_init(bectx, ops, &data);
++    ret = ldap_id_init_internal(bectx, ops, &data);
+     if (ret == EOK) {
+         id_ctx = talloc_get_type(data, struct sdap_id_ctx);
+ 
+@@ -302,9 +330,9 @@ int sssm_ldap_access_init(struct be_ctx *bectx,
+         goto done;
+     }
+ 
+-    ret = sssm_ldap_id_init(bectx, ops, (void **)&access_ctx->id_ctx);
++    ret = ldap_id_init_internal(bectx, ops, (void **)&access_ctx->id_ctx);
+     if (ret != EOK) {
+-        DEBUG(1, ("sssm_ldap_id_init failed.\n"));
++        DEBUG(SSSDBG_CRIT_FAILURE, ("ldap_id_init_internal failed.\n"));
+         goto done;
+     }
+ 
+@@ -417,7 +445,7 @@ int sssm_ldap_sudo_init(struct be_ctx *be_ctx,
+     void *data;
+     int ret;
+ 
+-    ret = sssm_ldap_id_init(be_ctx, ops, &data);
++    ret = ldap_id_init_internal(be_ctx, ops, &data);
+     if (ret != EOK) {
+         DEBUG(SSSDBG_CRIT_FAILURE, ("Cannot init LDAP ID provider [%d]: %s\n",
+                                     ret, strerror(ret)));
+@@ -447,7 +475,7 @@ int sssm_ldap_autofs_init(struct be_ctx *be_ctx,
+     void *data;
+     int ret;
+ 
+-    ret = sssm_ldap_id_init(be_ctx, ops, &data);
++    ret = ldap_id_init_internal(be_ctx, ops, &data);
+     if (ret != EOK) {
+         DEBUG(SSSDBG_CRIT_FAILURE, ("Cannot init LDAP ID provider [%d]: %s\n",
+                                     ret, strerror(ret)));
+-- 
+1.8.5.3
+
diff --git a/SOURCES/0092-UTIL-Sanitize-whitespaces.patch b/SOURCES/0092-UTIL-Sanitize-whitespaces.patch
new file mode 100644
index 0000000..09cdfa0
--- /dev/null
+++ b/SOURCES/0092-UTIL-Sanitize-whitespaces.patch
@@ -0,0 +1,43 @@
+From 1c4109bf7f16016cf0b53cd73e7b80e0d87be660 Mon Sep 17 00:00:00 2001
+From: Lukas Slebodnik <lslebodn@redhat.com>
+Date: Mon, 24 Feb 2014 11:37:52 +0100
+Subject: [PATCH 92/92] UTIL: Sanitize whitespaces.
+
+Original patches submitted by: mpesari(Thanks!!)
+
+It can cause problems if user will hit spaces before entering username.
+(e.g in gdm). Spaces are ignored by LDAP; it's better to escape them.
+
+Resolves:
+https://fedorahosted.org/sssd/ticket/1955
+
+Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
+(cherry picked from commit 2b8208b45feb2aab64d560d3e12e01e7b6d00d39)
+---
+ src/util/util.c | 10 ++++++++++
+ 1 file changed, 10 insertions(+)
+
+diff --git a/src/util/util.c b/src/util/util.c
+index fb3bed146e2c634375a1133ef512673dee16718a..1ec5c2a9474b0cd2d19a50b495e218d1da7da6c8 100644
+--- a/src/util/util.c
++++ b/src/util/util.c
+@@ -537,6 +537,16 @@ errno_t sss_filter_sanitize(TALLOC_CTX *mem_ctx,
+ 
+     while (input[i]) {
+         switch(input[i]) {
++        case '\t':
++            output[j++] = '\\';
++            output[j++] = '0';
++            output[j++] = '9';
++            break;
++        case ' ':
++            output[j++] = '\\';
++            output[j++] = '2';
++            output[j++] = '0';
++            break;
+         case '*':
+             output[j++] = '\\';
+             output[j++] = '2';
+-- 
+1.8.5.3
+
diff --git a/SOURCES/0093-IDMAP-add-sss_idmap_check_collision-_ex.patch b/SOURCES/0093-IDMAP-add-sss_idmap_check_collision-_ex.patch
new file mode 100644
index 0000000..fe6caa9
--- /dev/null
+++ b/SOURCES/0093-IDMAP-add-sss_idmap_check_collision-_ex.patch
@@ -0,0 +1,362 @@
+From 4206e5dbe37277a4002010e85438fe376b5b1812 Mon Sep 17 00:00:00 2001
+From: Sumit Bose <sbose@redhat.com>
+Date: Mon, 3 Feb 2014 13:30:35 +0100
+Subject: [PATCH 93/97] IDMAP: add sss_idmap_check_collision(_ex)
+
+---
+ src/lib/idmap/sss_idmap.c         | 129 ++++++++++++++++++++++++++------------
+ src/lib/idmap/sss_idmap.h         |  65 +++++++++++++++++++
+ src/tests/cmocka/test_sss_idmap.c |  93 +++++++++++++++++++++++++++
+ 3 files changed, 247 insertions(+), 40 deletions(-)
+
+diff --git a/src/lib/idmap/sss_idmap.c b/src/lib/idmap/sss_idmap.c
+index 3f1e7a58f390a3c10999251e2155ef513ba69bd7..4c453120539a549807e9b6bb4db2dc396c1b3152 100644
+--- a/src/lib/idmap/sss_idmap.c
++++ b/src/lib/idmap/sss_idmap.c
+@@ -380,55 +380,104 @@ enum idmap_error_code sss_idmap_calculate_range(struct sss_idmap_ctx *ctx,
+     return IDMAP_SUCCESS;
+ }
+ 
++enum idmap_error_code sss_idmap_check_collision_ex(const char *o_name,
++                                                const char *o_sid,
++                                                struct sss_idmap_range *o_range,
++                                                uint32_t o_first_rid,
++                                                const char *o_range_id,
++                                                bool o_external_mapping,
++                                                const char *n_name,
++                                                const char *n_sid,
++                                                struct sss_idmap_range *n_range,
++                                                uint32_t n_first_rid,
++                                                const char *n_range_id,
++                                                bool n_external_mapping)
++{
++    bool names_equal;
++    bool sids_equal;
++
++    /* TODO: if both ranges have the same ID check if an update is
++     * needed. */
++
++    /* Check if ID ranges overlap.
++     * ID ranges with external mapping may overlap. */
++    if ((!n_external_mapping && !o_external_mapping)
++        && ((n_range->min >= o_range->min
++                && n_range->min <= o_range->max)
++            || (n_range->max >= o_range->min
++                && n_range->max <= o_range->max))) {
++        return IDMAP_COLLISION;
++    }
++
++    names_equal = (strcasecmp(n_name, o_name) == 0);
++    sids_equal = ((n_sid == NULL && o_sid == NULL)
++                    || (n_sid != NULL && o_sid != NULL
++                        && strcasecmp(n_sid, o_sid) == 0));
++
++    /* check if domain name and SID are consistent */
++    if ((names_equal && !sids_equal) || (!names_equal && sids_equal)) {
++        return IDMAP_COLLISION;
++    }
++
++    /* check if external_mapping is consistent */
++    if (names_equal && sids_equal
++            && n_external_mapping != o_external_mapping) {
++        return IDMAP_COLLISION;
++    }
++
++    /* check if RID ranges overlap */
++    if (names_equal && sids_equal
++            && n_external_mapping == false
++            && n_first_rid >= o_first_rid
++            && n_first_rid <= o_first_rid + (o_range->max - o_range->min)) {
++        return IDMAP_COLLISION;
++    }
++
++    return IDMAP_SUCCESS;
++}
++
++enum idmap_error_code sss_idmap_check_collision(struct sss_idmap_ctx *ctx,
++                                                char *n_name, char *n_sid,
++                                                struct sss_idmap_range *n_range,
++                                                uint32_t n_first_rid,
++                                                char *n_range_id,
++                                                bool n_external_mapping)
++{
++    struct idmap_domain_info *dom;
++    enum idmap_error_code err;
++
++    for (dom = ctx->idmap_domain_info; dom != NULL; dom = dom->next) {
++        err = sss_idmap_check_collision_ex(dom->name, dom->sid, dom->range,
++                                           dom->first_rid, dom->range_id,
++                                           dom->external_mapping,
++                                           n_name, n_sid, n_range, n_first_rid,
++                                           n_range_id, n_external_mapping);
++        if (err != IDMAP_SUCCESS) {
++            return err;
++        }
++    }
++    return IDMAP_SUCCESS;
++}
++
+ static enum idmap_error_code dom_check_collision(
+                                              struct idmap_domain_info *dom_list,
+                                              struct idmap_domain_info *new_dom)
+ {
+     struct idmap_domain_info *dom;
+-    bool names_equal;
+-    bool sids_equal;
++    enum idmap_error_code err;
+ 
+     for (dom = dom_list; dom != NULL; dom = dom->next) {
+-
+-        /* TODO: if both ranges have the same ID check if an update is
+-         * needed. */
+-
+-        /* Check if ID ranges overlap.
+-         * ID ranges with external mapping may overlap. */
+-        if ((!new_dom->external_mapping && !dom->external_mapping)
+-            && ((new_dom->range->min >= dom->range->min
+-                    && new_dom->range->min <= dom->range->max)
+-                || (new_dom->range->max >= dom->range->min
+-                    && new_dom->range->max <= dom->range->max))) {
+-            return IDMAP_COLLISION;
+-        }
+-
+-        names_equal = (strcasecmp(new_dom->name, dom->name) == 0);
+-        sids_equal = ((new_dom->sid == NULL && dom->sid == NULL)
+-                        || (new_dom->sid != NULL && dom->sid != NULL
+-                            && strcasecmp(new_dom->sid, dom->sid) == 0));
+-
+-        /* check if domain name and SID are consistent */
+-        if ((names_equal && !sids_equal) || (!names_equal && sids_equal)) {
+-            return IDMAP_COLLISION;
+-        }
+-
+-        /* check if external_mapping is consistent */
+-        if (names_equal && sids_equal
+-                && new_dom->external_mapping != dom->external_mapping) {
+-            return IDMAP_COLLISION;
+-        }
+-
+-        /* check if RID ranges overlap */
+-        if (names_equal && sids_equal
+-                && new_dom->external_mapping == false
+-                && new_dom->first_rid >= dom->first_rid
+-                && new_dom->first_rid <=
+-                    dom->first_rid + (dom->range->max - dom->range->min)) {
+-            return IDMAP_COLLISION;
++        err = sss_idmap_check_collision_ex(dom->name, dom->sid, dom->range,
++                                           dom->first_rid, dom->range_id,
++                                           dom->external_mapping,
++                                           new_dom->name, new_dom->sid,
++                                           new_dom->range, new_dom->first_rid,
++                                           new_dom->range_id,
++                                           new_dom->external_mapping);
++        if (err != IDMAP_SUCCESS) {
++            return err;
+         }
+     }
+-
+     return IDMAP_SUCCESS;
+ }
+ 
+diff --git a/src/lib/idmap/sss_idmap.h b/src/lib/idmap/sss_idmap.h
+index 1e1c9a5cfe490301d0e633db808589f1bc0ef857..ccc63f7f760b877cdb17696325731f8e540b2736 100644
+--- a/src/lib/idmap/sss_idmap.h
++++ b/src/lib/idmap/sss_idmap.h
+@@ -289,6 +289,71 @@ enum idmap_error_code sss_idmap_add_domain_ex(struct sss_idmap_ctx *ctx,
+                                               const char *range_id,
+                                               uint32_t rid,
+                                               bool external_mapping);
++
++/**
++ * @brief Check if a new range would collide with any existing one
++ *
++ * @param[in] ctx         Idmap context
++ * @param[in] n_name      Zero-terminated string with the domain name the new
++ *                        range should belong to
++ * @param[in] n_sid       Zero-terminated string representation of the domain
++ *                        SID (S-1-15-.....) the new range sould belong to
++ * @param[in] n_range     The new id range
++ * @param[in] n_range_id  unique identifier of the new range, it is needed
++ *                        to allow updates at runtime, may be NULL
++ * @param[in] n_first_rid The RID that should be mapped to the first ID of the
++ *                        new range.
++ * @param[in] n_external_mapping Mapping type of the new range
++ *
++ * @return
++ *  - #IDMAP_COLLISION:     New range collides with existing one
++ */
++enum idmap_error_code sss_idmap_check_collision(struct sss_idmap_ctx *ctx,
++                                                char *n_name, char *n_sid,
++                                                struct sss_idmap_range *n_range,
++                                                uint32_t n_first_rid,
++                                                char *n_range_id,
++                                                bool n_external_mapping);
++
++/**
++ * @brief Check if two ranges would collide
++ *
++ * @param[in] o_name      Zero-terminated string with the domain name the
++ *                        first range should belong to
++ * @param[in] o_sid       Zero-terminated string representation of the domain
++ *                        SID (S-1-15-.....) the first range sould belong to
++ * @param[in] o_range     The first id range
++ * @param[in] o_range_id  unique identifier of the first range, it is needed
++ *                        to allow updates at runtime, may be NULL
++ * @param[in] o_first_rid The RID that should be mapped to the first ID of the
++ *                        first range.
++ * @param[in] o_external_mapping Mapping type of the first range
++ * @param[in] n_name      Zero-terminated string with the domain name the
++ *                        second range should belong to
++ * @param[in] n_sid       Zero-terminated string representation of the domain
++ *                        SID (S-1-15-.....) the second range sould belong to
++ * @param[in] n_range     The second id range
++ * @param[in] n_range_id  unique identifier of the second range, it is needed
++ *                        to allow updates at runtime, may be NULL
++ * @param[in] n_first_rid The RID that should be mapped to the first ID of the
++ *                        second range.
++ * @param[in] n_external_mapping Mapping type of the second range
++ *
++ * @return
++ *  - #IDMAP_COLLISION:     New range collides with existing one
++ */
++enum idmap_error_code sss_idmap_check_collision_ex(const char *o_name,
++                                                const char *o_sid,
++                                                struct sss_idmap_range *o_range,
++                                                uint32_t o_first_rid,
++                                                const char *o_range_id,
++                                                bool o_external_mapping,
++                                                const char *n_name,
++                                                const char *n_sid,
++                                                struct sss_idmap_range *n_range,
++                                                uint32_t n_first_rid,
++                                                const char *n_range_id,
++                                                bool n_external_mapping);
+ /**
+  * @brief Translate SID to a unix UID or GID
+  *
+diff --git a/src/tests/cmocka/test_sss_idmap.c b/src/tests/cmocka/test_sss_idmap.c
+index 019b4618ef0e14e87cb86d64989e8f5ca9dfdfd8..ff933216416b61618bf764d8c2554b273706c787 100644
+--- a/src/tests/cmocka/test_sss_idmap.c
++++ b/src/tests/cmocka/test_sss_idmap.c
+@@ -30,11 +30,15 @@
+ #define TEST_RANGE_MAX 399999
+ #define TEST_DOM_NAME "test.dom"
+ #define TEST_DOM_SID "S-1-5-21-123-456-789"
++#define TEST_FIRST_RID 0
++#define TEST_EXT_MAPPING true
+ 
+ #define TEST_2_RANGE_MIN 600000
+ #define TEST_2_RANGE_MAX 799999
+ #define TEST_2_DOM_NAME "test2.dom"
+ #define TEST_2_DOM_SID "S-1-5-21-987-654-321"
++#define TEST_2_FIRST_RID 1000000
++#define TEST_2_EXT_MAPPING true
+ 
+ #define TEST_OFFSET 1000000
+ #define TEST_OFFSET_STR "1000000"
+@@ -408,6 +412,94 @@ void test_has_algorithmic_by_name(void **state)
+     assert_false(use_id_mapping);
+ }
+ 
++void test_sss_idmap_check_collision_ex(void **state)
++{
++    enum idmap_error_code err;
++    struct sss_idmap_range r1 = {TEST_RANGE_MIN, TEST_RANGE_MAX};
++    struct sss_idmap_range r2 = {TEST_2_RANGE_MIN, TEST_2_RANGE_MAX};
++
++    err = sss_idmap_check_collision_ex(TEST_DOM_NAME, TEST_DOM_SID, &r1,
++                                       TEST_FIRST_RID, NULL,
++                                       TEST_EXT_MAPPING,
++                                       TEST_2_DOM_NAME, TEST_2_DOM_SID, &r2,
++                                       TEST_2_FIRST_RID, NULL,
++                                       TEST_2_EXT_MAPPING);
++    assert_int_equal(err, IDMAP_SUCCESS);
++
++    /* Same name, different SID */
++    err = sss_idmap_check_collision_ex(TEST_DOM_NAME, TEST_DOM_SID, &r1,
++                                       TEST_FIRST_RID, NULL,
++                                       TEST_EXT_MAPPING,
++                                       TEST_DOM_NAME, TEST_2_DOM_SID, &r2,
++                                       TEST_2_FIRST_RID, NULL,
++                                       TEST_2_EXT_MAPPING);
++    assert_int_equal(err, IDMAP_COLLISION);
++
++    /* Same SID, different name */
++    err = sss_idmap_check_collision_ex(TEST_DOM_NAME, TEST_DOM_SID, &r1,
++                                       TEST_FIRST_RID, NULL,
++                                       TEST_EXT_MAPPING,
++                                       TEST_2_DOM_NAME, TEST_DOM_SID, &r2,
++                                       TEST_2_FIRST_RID, NULL,
++                                       TEST_2_EXT_MAPPING);
++    assert_int_equal(err, IDMAP_COLLISION);
++
++    /* Same SID and name, no overlaps */
++    err = sss_idmap_check_collision_ex(TEST_DOM_NAME, TEST_DOM_SID, &r1,
++                                       TEST_FIRST_RID, NULL,
++                                       TEST_EXT_MAPPING,
++                                       TEST_DOM_NAME, TEST_DOM_SID, &r2,
++                                       TEST_2_FIRST_RID, NULL,
++                                       TEST_2_EXT_MAPPING);
++    assert_int_equal(err, IDMAP_SUCCESS);
++
++    /* Same SID and name, different mappings */
++    err = sss_idmap_check_collision_ex(TEST_DOM_NAME, TEST_DOM_SID, &r1,
++                                       TEST_FIRST_RID, NULL,
++                                       TEST_EXT_MAPPING,
++                                       TEST_DOM_NAME, TEST_DOM_SID, &r2,
++                                       TEST_2_FIRST_RID, NULL,
++                                       !TEST_EXT_MAPPING);
++    assert_int_equal(err, IDMAP_COLLISION);
++
++    /* Same SID and name, Overlapping RID range */
++    err = sss_idmap_check_collision_ex(TEST_DOM_NAME, TEST_DOM_SID, &r1,
++                                       TEST_FIRST_RID, NULL,
++                                       false,
++                                       TEST_DOM_NAME, TEST_DOM_SID, &r2,
++                                       TEST_FIRST_RID, NULL,
++                                       false);
++    assert_int_equal(err, IDMAP_COLLISION);
++
++    /* Different SID and name, Overlapping RID range */
++    err = sss_idmap_check_collision_ex(TEST_DOM_NAME, TEST_DOM_SID, &r1,
++                                       TEST_FIRST_RID, NULL,
++                                       false,
++                                       TEST_2_DOM_NAME, TEST_2_DOM_SID, &r2,
++                                       TEST_FIRST_RID, NULL,
++                                       false);
++    assert_int_equal(err, IDMAP_SUCCESS);
++
++
++    /* Overlapping ranges with no external mapping */
++    err = sss_idmap_check_collision_ex(TEST_DOM_NAME, TEST_DOM_SID, &r1,
++                                       TEST_FIRST_RID, NULL,
++                                       false,
++                                       TEST_2_DOM_NAME, TEST_2_DOM_SID, &r1,
++                                       TEST_2_FIRST_RID, NULL,
++                                       false);
++    assert_int_equal(err, IDMAP_COLLISION);
++
++    /* Overlapping ranges with external mapping */
++    err = sss_idmap_check_collision_ex(TEST_DOM_NAME, TEST_DOM_SID, &r1,
++                                       TEST_FIRST_RID, NULL,
++                                       true,
++                                       TEST_2_DOM_NAME, TEST_2_DOM_SID, &r1,
++                                       TEST_2_FIRST_RID, NULL,
++                                       true);
++    assert_int_equal(err, IDMAP_SUCCESS);
++}
++
+ int main(int argc, const char *argv[])
+ {
+     poptContext pc;
+@@ -439,6 +531,7 @@ int main(int argc, const char *argv[])
+         unit_test_setup_teardown(test_has_algorithmic_by_name,
+                                  test_sss_idmap_setup_with_both,
+                                  test_sss_idmap_teardown),
++        unit_test(test_sss_idmap_check_collision_ex),
+     };
+ 
+     /* Set debug level to invalid value so we can deside if -d 0 was used. */
+-- 
+1.8.5.3
+
diff --git a/SOURCES/0094-IPA-refactor-idmap-code-and-add-test.patch b/SOURCES/0094-IPA-refactor-idmap-code-and-add-test.patch
new file mode 100644
index 0000000..c648a76
--- /dev/null
+++ b/SOURCES/0094-IPA-refactor-idmap-code-and-add-test.patch
@@ -0,0 +1,630 @@
+From 5ec1d31f32583761c05691c951576b6213037393 Mon Sep 17 00:00:00 2001
+From: Sumit Bose <sbose@redhat.com>
+Date: Fri, 7 Feb 2014 15:54:30 +0100
+Subject: [PATCH 94/97] IPA: refactor idmap code and add test
+
+---
+ Makefile.am                       |  15 +++
+ src/providers/ipa/ipa_common.h    |  10 ++
+ src/providers/ipa/ipa_idmap.c     | 248 +++++++++++++++----------------------
+ src/tests/cmocka/test_ipa_idmap.c | 249 ++++++++++++++++++++++++++++++++++++++
+ 4 files changed, 374 insertions(+), 148 deletions(-)
+ create mode 100644 src/tests/cmocka/test_ipa_idmap.c
+
+diff --git a/Makefile.am b/Makefile.am
+index 16648f9aa2275b60ec84a95ff8a26b1225b97918..2e1a1e6bacfb79e4ef7068a22a64c21d23858cb9 100644
+--- a/Makefile.am
++++ b/Makefile.am
+@@ -150,6 +150,7 @@ if HAVE_CMOCKA
+         dyndns-tests \
+         fqnames-tests \
+         test_sss_idmap \
++        test_ipa_idmap \
+         test_utils \
+         ad_access_filter_tests \
+         ad_common_tests \
+@@ -1359,6 +1360,20 @@ test_sss_idmap_LDADD = \
+     $(SSSD_INTERNAL_LTLIBS) \
+     libsss_test_common.la
+ 
++test_ipa_idmap_SOURCES = \
++    src/tests/cmocka/test_ipa_idmap.c \
++    src/providers/ipa/ipa_idmap.c
++test_ipa_idmap_CFLAGS = \
++    $(AM_CFLAGS)
++test_ipa_idmap_LDFLAGS = \
++    -Wl,-wrap,sysdb_get_ranges
++test_ipa_idmap_LDADD = \
++    $(CMOCKA_LIBS) \
++    $(POPT_LIBS) \
++    libsss_idmap.la \
++    $(SSSD_INTERNAL_LTLIBS) \
++    libsss_test_common.la
++
+ test_utils_SOURCES = \
+     src/tests/cmocka/test_utils.c
+ test_utils_CFLAGS = \
+diff --git a/src/providers/ipa/ipa_common.h b/src/providers/ipa/ipa_common.h
+index 02f0baf55f0d226eeb8956076b9bbcce285d4a94..0b8a17c532b7b0081dc749dcef1e6c0e684a7ed2 100644
+--- a/src/providers/ipa/ipa_common.h
++++ b/src/providers/ipa/ipa_common.h
+@@ -195,6 +195,16 @@ int ipa_sudo_init(struct be_ctx *be_ctx,
+                   struct bet_ops **ops,
+                   void **pvt_data);
+ 
++errno_t get_idmap_data_from_range(struct range_info *r, char *domain_name,
++                                  char **_name, char **_sid, uint32_t *_rid,
++                                  struct sss_idmap_range *_range,
++                                  bool *_external_mapping);
++
++errno_t ipa_idmap_get_ranges_from_sysdb(struct sdap_idmap_ctx *idmap_ctx,
++                                        const char *dom_name,
++                                        const char *dom_sid_str,
++                                        bool allow_collisions);
++
+ errno_t ipa_idmap_init(TALLOC_CTX *mem_ctx,
+                        struct sdap_id_ctx *id_ctx,
+                        struct sdap_idmap_ctx **_idmap_ctx);
+diff --git a/src/providers/ipa/ipa_idmap.c b/src/providers/ipa/ipa_idmap.c
+index eaca0ed3c3ce2622fbf80dff13d22e2e521f54fe..a65086af4cb4bec7ab85774f3ca1a3555056cee0 100644
+--- a/src/providers/ipa/ipa_idmap.c
++++ b/src/providers/ipa/ipa_idmap.c
+@@ -156,9 +156,68 @@ done:
+     return ret;
+ }
+ 
+-errno_t ipa_idmap_find_new_domain(struct sdap_idmap_ctx *idmap_ctx,
+-                                  const char *dom_name,
+-                                  const char *dom_sid_str)
++errno_t get_idmap_data_from_range(struct range_info *r, char *domain_name,
++                                  char **_name, char **_sid, uint32_t *_rid,
++                                  struct sss_idmap_range *_range,
++                                  bool *_external_mapping)
++{
++    if (r->range_type == NULL) {
++        /* Older IPA servers might not have the range_type attribute, but
++         * only support local ranges and trusts with algorithmic mapping. */
++
++        if (r->trusted_dom_sid == NULL && r->secondary_base_rid != 0) {
++            /* local IPA domain */
++            *_rid = 0;
++            *_external_mapping = true;
++            *_name = domain_name;
++            *_sid = NULL;
++        } else if (r->trusted_dom_sid != NULL
++                && r->secondary_base_rid == 0) {
++            /* trusted domain */
++            *_rid = r->base_rid;
++            *_external_mapping = false;
++            *_name = r->trusted_dom_sid;
++            *_sid = r->trusted_dom_sid;
++        } else {
++            DEBUG(SSSDBG_MINOR_FAILURE, ("Cannot determine range type, " \
++                                         "for id range [%s].\n",
++                                         r->name));
++            return EINVAL;
++        }
++    } else {
++        if (strcmp(r->range_type, IPA_RANGE_LOCAL) == 0) {
++            *_rid = 0;
++            *_external_mapping = true;
++            *_name = domain_name;
++            *_sid = NULL;
++        } else if (strcmp(r->range_type, IPA_RANGE_AD_TRUST_POSIX) == 0) {
++            *_rid = 0;
++            *_external_mapping = true;
++            *_name = r->trusted_dom_sid;
++            *_sid = r->trusted_dom_sid;
++        } else if (strcmp(r->range_type, IPA_RANGE_AD_TRUST) == 0) {
++            *_rid = r->base_rid;
++            *_external_mapping = false;
++            *_name = r->trusted_dom_sid;
++            *_sid = r->trusted_dom_sid;
++        } else {
++            DEBUG(SSSDBG_MINOR_FAILURE, ("Range type [%s] of id range " \
++                                         "[%s] not supported.\n", \
++                                         r->range_type, r->name));
++            return EINVAL;
++        }
++    }
++
++    _range->min = r->base_id;
++    _range->max = r->base_id + r->id_range_size -1;
++
++    return EOK;
++}
++
++errno_t ipa_idmap_get_ranges_from_sysdb(struct sdap_idmap_ctx *idmap_ctx,
++                                        const char *dom_name,
++                                        const char *dom_sid_str,
++                                        bool allow_collisions)
+ {
+     int ret;
+     size_t range_count;
+@@ -166,7 +225,6 @@ errno_t ipa_idmap_find_new_domain(struct sdap_idmap_ctx *idmap_ctx,
+     TALLOC_CTX *tmp_ctx;
+     size_t c;
+     enum idmap_error_code err;
+-    struct range_info *r;
+     struct sss_idmap_range range;
+     uint32_t rid;
+     bool external_mapping;
+@@ -187,74 +245,39 @@ errno_t ipa_idmap_find_new_domain(struct sdap_idmap_ctx *idmap_ctx,
+     }
+ 
+     for (c = 0; c < range_count; c++) {
+-        r = range_list[c];
+-
+-        if (r->range_type == NULL) {
+-            /* Older IPA servers might not have the range_type attribute, but
+-             * only support local ranges and trusts with algorithmic mapping. */
+-
+-            if (r->trusted_dom_sid == NULL && r->secondary_base_rid != 0) {
+-                /* local IPA domain */
+-                rid = 0;
+-                external_mapping = true;
+-                name = idmap_ctx->id_ctx->be->domain->name;
+-                sid = NULL;
+-            } else if (r->trusted_dom_sid != NULL
+-                    && r->secondary_base_rid == 0) {
+-                /* trusted domain */
+-                rid = r->base_rid;
+-                external_mapping = false;
+-                name = r->trusted_dom_sid;
+-                sid = r->trusted_dom_sid;
+-            } else {
+-                DEBUG(SSSDBG_MINOR_FAILURE, ("Cannot determine range type, " \
+-                                             "skipping id ange [%s].\n",
+-                                             r->name));
+-                continue;
+-            }
+-        } else {
+-            if (strcmp(r->range_type, IPA_RANGE_LOCAL) == 0) {
+-                rid = 0;
+-                external_mapping = true;
+-                name = idmap_ctx->id_ctx->be->domain->name;
+-                sid = NULL;
+-            } else if (strcmp(r->range_type, IPA_RANGE_AD_TRUST_POSIX) == 0) {
+-                rid = 0;
+-                external_mapping = true;
+-                name = r->trusted_dom_sid;
+-                sid = r->trusted_dom_sid;
+-            } else if (strcmp(r->range_type, IPA_RANGE_AD_TRUST) == 0) {
+-                rid = r->base_rid;
+-                external_mapping = false;
+-                name = r->trusted_dom_sid;
+-                sid = r->trusted_dom_sid;
+-            } else {
+-                DEBUG(SSSDBG_MINOR_FAILURE, ("Range type [%s] not supported, " \
+-                                             "skipping id range [%s].\n",
+-                                             r->range_type, r->name));
+-                continue;
+-            }
++        ret = get_idmap_data_from_range(range_list[c],
++                                        idmap_ctx->id_ctx->be->domain->name,
++                                        &name, &sid, &rid, &range,
++                                        &external_mapping);
++        if (ret != EOK) {
++            DEBUG(SSSDBG_OP_FAILURE, ("get_idmap_data_from_range failed for " \
++                                      "id range [%s], skipping.\n",
++                                      range_list[c]->name));
++            continue;
+         }
+ 
+-        range.min = r->base_id;
+-        range.max = r->base_id + r->id_range_size -1;
+         err = sss_idmap_add_domain_ex(idmap_ctx->map, name, sid, &range,
+-                                      r->name, rid, external_mapping);
+-        if (err != IDMAP_SUCCESS && err != IDMAP_COLLISION) {
+-            DEBUG(SSSDBG_CRIT_FAILURE, ("Could not add range [%s] to ID map\n",
+-                                        r->name));
+-            ret = EIO;
++                                      range_list[c]->name, rid,
++                                      external_mapping);
++        if (err != IDMAP_SUCCESS) {
++            if (!allow_collisions || err != IDMAP_COLLISION) {
++                DEBUG(SSSDBG_CRIT_FAILURE, ("Could not add range [%s] to ID map\n",
++                                            range_list[c]->name));
++                ret = EIO;
++                goto done;
++            }
++        }
++    }
++
++    if (dom_name != NULL || dom_sid_str != NULL) {
++        ret = ipa_idmap_check_posix_child(idmap_ctx, dom_name, dom_sid_str,
++                                          range_count, range_list);
++        if (ret != EOK) {
++            DEBUG(SSSDBG_OP_FAILURE, ("ipa_idmap_check_posix_child failed.\n"));
+             goto done;
+         }
+     }
+ 
+-    ret = ipa_idmap_check_posix_child(idmap_ctx, dom_name, dom_sid_str,
+-                                      range_count, range_list);
+-    if (ret != EOK) {
+-        DEBUG(SSSDBG_OP_FAILURE, ("ipa_idmap_check_posix_child failed.\n"));
+-        goto done;
+-    }
+-
+     ret = EOK;
+ 
+ done:
+@@ -263,6 +286,14 @@ done:
+     return ret;
+ }
+ 
++errno_t ipa_idmap_find_new_domain(struct sdap_idmap_ctx *idmap_ctx,
++                                  const char *dom_name,
++                                  const char *dom_sid_str)
++{
++    return ipa_idmap_get_ranges_from_sysdb(idmap_ctx, dom_name, dom_sid_str,
++                                           true);
++}
++
+ errno_t ipa_idmap_init(TALLOC_CTX *mem_ctx,
+                        struct sdap_id_ctx *id_ctx,
+                        struct sdap_idmap_ctx **_idmap_ctx)
+@@ -270,17 +301,7 @@ errno_t ipa_idmap_init(TALLOC_CTX *mem_ctx,
+     errno_t ret;
+     TALLOC_CTX *tmp_ctx;
+     enum idmap_error_code err;
+-    size_t c;
+     struct sdap_idmap_ctx *idmap_ctx = NULL;
+-    struct sysdb_ctx *sysdb = id_ctx->be->domain->sysdb;
+-    size_t range_count;
+-    struct range_info **range_list;
+-    struct range_info *r;
+-    struct sss_idmap_range range;
+-    uint32_t rid;
+-    bool external_mapping;
+-    char *name;
+-    char *sid;
+ 
+     tmp_ctx = talloc_new(NULL);
+     if (!tmp_ctx) return ENOMEM;
+@@ -309,82 +330,13 @@ errno_t ipa_idmap_init(TALLOC_CTX *mem_ctx,
+         goto done;
+     }
+ 
+-
+-    /* Read in any existing mappings from the cache */
+-    ret = sysdb_get_ranges(tmp_ctx, sysdb, &range_count, &range_list);
+-    if (ret != EOK && ret != ENOENT) {
+-        DEBUG(SSSDBG_FATAL_FAILURE,
+-              ("Could not read ranges from the cache: [%s]\n",
+-               strerror(ret)));
++    ret = ipa_idmap_get_ranges_from_sysdb(idmap_ctx, NULL, NULL, false);
++    if (ret != EOK) {
++        DEBUG(SSSDBG_OP_FAILURE,
++              ("ipa_idmap_get_ranges_from_sysdb failed.\n"));
+         goto done;
+     }
+ 
+-    DEBUG(SSSDBG_CONF_SETTINGS,
+-          ("Initializing [%zu] domains for ID-mapping\n", range_count));
+-
+-    for (c = 0; c < range_count; c++) {
+-
+-        r = range_list[c];
+-
+-        if (r->range_type == NULL) {
+-            /* Older IPA servers might not have the range_type attribute, but
+-             * only support local ranges and trusts with algorithmic mapping. */
+-
+-            if (r->trusted_dom_sid == NULL && r->secondary_base_rid != 0) {
+-                /* local IPA domain */
+-                rid = 0;
+-                external_mapping = true;
+-                sid = NULL;
+-                name = id_ctx->be->domain->name;
+-            } else if (r->trusted_dom_sid != NULL
+-                    && r->secondary_base_rid == 0) {
+-                /* trusted domain */
+-                rid = r->base_rid;
+-                external_mapping = false;
+-                sid = r->trusted_dom_sid;
+-                name = sid;
+-            } else {
+-                DEBUG(SSSDBG_MINOR_FAILURE, ("Cannot determine range type, " \
+-                                             "skipping id ange [%s].\n",
+-                                             r->name));
+-                continue;
+-            }
+-        } else {
+-            if (strcmp(r->range_type, IPA_RANGE_LOCAL) == 0) {
+-                rid = 0;
+-                external_mapping = true;
+-                sid = NULL;
+-                name = id_ctx->be->domain->name;
+-            } else if (strcmp(r->range_type, IPA_RANGE_AD_TRUST_POSIX) == 0) {
+-                rid = 0;
+-                external_mapping = true;
+-                sid = r->trusted_dom_sid;
+-                name = sid;
+-            } else if (strcmp(r->range_type, IPA_RANGE_AD_TRUST) == 0) {
+-                rid = r->base_rid;
+-                external_mapping = false;
+-                sid = r->trusted_dom_sid;
+-                name = sid;
+-            } else {
+-                DEBUG(SSSDBG_MINOR_FAILURE, ("Range type [%s] not supported, " \
+-                                             "skipping id range [%s].\n",
+-                                             r->range_type, r->name));
+-                continue;
+-            }
+-        }
+-
+-        range.min = r->base_id;
+-        range.max = r->base_id + r->id_range_size -1;
+-        err = sss_idmap_add_domain_ex(idmap_ctx->map, name, sid, &range,
+-                                      r->name, rid, external_mapping);
+-        if (err != IDMAP_SUCCESS) {
+-            DEBUG(SSSDBG_CRIT_FAILURE, ("Could not add range [%s] to ID map\n",
+-                                        r->name));
+-            ret = EIO;
+-            goto done;
+-        }
+-    }
+-
+     *_idmap_ctx = talloc_steal(mem_ctx, idmap_ctx);
+     ret = EOK;
+ 
+diff --git a/src/tests/cmocka/test_ipa_idmap.c b/src/tests/cmocka/test_ipa_idmap.c
+new file mode 100644
+index 0000000000000000000000000000000000000000..2fb2cde2f9a7f1172fb69b268d19b559ff9d2f32
+--- /dev/null
++++ b/src/tests/cmocka/test_ipa_idmap.c
+@@ -0,0 +1,249 @@
++/*
++    Authors:
++        Sumit Bose <sbose@redhat.com>
++
++    Copyright (C) 2014 Red Hat
++
++    SSSD tests: Unit tests for id-mapping in the IPA provider
++
++    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 "tests/cmocka/common_mock.h"
++#include "lib/idmap/sss_idmap.h"
++#include "providers/ipa/ipa_common.h"
++#include "providers/ldap/sdap_idmap.h"
++
++#define RANGE_NAME discard_const("range1")
++#define DOMAIN_SID discard_const("S-1-5-21-2-3-4")
++#define DOMAIN_NAME discard_const("dom.test")
++#define BASE_RID 111
++#define SECONDARY_BASE_RID 11223344
++#define BASE_ID 123456
++#define RANGE_SIZE 222222
++#define RANGE_MAX (BASE_ID + RANGE_SIZE - 1)
++
++void test_get_idmap_data_from_range(void **state)
++{
++    char *dom_name;
++    char *sid;
++    uint32_t rid;
++    struct sss_idmap_range range;
++    bool external_mapping;
++    size_t c;
++    errno_t ret;
++
++    struct test_data {
++        struct range_info r;
++        errno_t exp_ret;
++        char *exp_dom_name;
++        char *exp_sid;
++        uint32_t exp_rid;
++        struct sss_idmap_range exp_range;
++        bool exp_external_mapping;
++    } d[] = {
++        /* working IPA_RANGE_LOCAL range */
++        {{RANGE_NAME, BASE_ID, RANGE_SIZE, BASE_RID, SECONDARY_BASE_RID,
++          NULL, discard_const(IPA_RANGE_LOCAL)},
++         EOK, DOMAIN_NAME, NULL, 0, {BASE_ID, RANGE_MAX}, true},
++        /* working old-style IPA_RANGE_LOCAL range without range type */
++        {{RANGE_NAME, BASE_ID, RANGE_SIZE, BASE_RID, SECONDARY_BASE_RID,
++          NULL, NULL},
++         EOK, DOMAIN_NAME, NULL, 0, {BASE_ID, RANGE_MAX}, true},
++        /* old-style IPA_RANGE_LOCAL without SID and secondary base rid */
++        {{RANGE_NAME, BASE_ID, RANGE_SIZE, BASE_RID, 0, NULL, NULL},
++         EINVAL, NULL, NULL, 0, {0, 0}, false},
++        /* old-style range with SID and secondary base rid */
++        {{RANGE_NAME, BASE_ID, RANGE_SIZE, BASE_RID, SECONDARY_BASE_RID,
++          DOMAIN_SID, NULL},
++         EINVAL, NULL, NULL, 0, {0, 0}, false},
++        /* working IPA_RANGE_AD_TRUST range */
++        {{RANGE_NAME, BASE_ID, RANGE_SIZE, BASE_RID, 0, DOMAIN_SID,
++          discard_const(IPA_RANGE_AD_TRUST)},
++         EOK, DOMAIN_SID, DOMAIN_SID, BASE_RID, {BASE_ID, RANGE_MAX}, false},
++        /* working old-style IPA_RANGE_AD_TRUST range without range type */
++        {{RANGE_NAME, BASE_ID, RANGE_SIZE, BASE_RID, 0, DOMAIN_SID, NULL},
++         EOK, DOMAIN_SID, DOMAIN_SID, BASE_RID, {BASE_ID, RANGE_MAX}, false},
++        /* working IPA_RANGE_AD_TRUST_POSIX range */
++        {{RANGE_NAME, BASE_ID, RANGE_SIZE, BASE_RID, 0, DOMAIN_SID,
++          discard_const(IPA_RANGE_AD_TRUST_POSIX)},
++         EOK, DOMAIN_SID, DOMAIN_SID, 0, {BASE_ID, RANGE_MAX}, true},
++        {{0}, 0, NULL, NULL, 0, {0, 0}, false}
++    };
++
++    for (c = 0; d[c].exp_dom_name != NULL; c++) {
++        ret = get_idmap_data_from_range(&d[c].r, DOMAIN_NAME, &dom_name, &sid,
++                                        &rid, &range, &external_mapping);
++        assert_int_equal(ret, d[c].exp_ret);
++        assert_string_equal(dom_name, d[c].exp_dom_name);
++        if (d[c].exp_sid == NULL) {
++            assert_null(sid);
++        } else {
++            assert_string_equal(sid, d[c].exp_sid);
++        }
++        assert_int_equal(rid, d[c].exp_rid);
++        assert_int_equal(range.min, d[c].exp_range.min);
++        assert_int_equal(range.max, d[c].exp_range.max);
++        assert_true(external_mapping == d[c].exp_external_mapping);
++    }
++}
++
++errno_t __wrap_sysdb_get_ranges(TALLOC_CTX *mem_ctx, struct sysdb_ctx *sysdb,
++                                size_t *range_count,
++                                struct range_info ***range_list)
++{
++
++    *range_count = sss_mock_type(size_t);
++    *range_list = talloc_steal(mem_ctx,
++                               sss_mock_ptr_type(struct range_info **));
++    return EOK;
++}
++
++struct test_ctx {
++    struct sdap_idmap_ctx *idmap_ctx;
++    struct sdap_id_ctx *sdap_id_ctx;
++};
++
++static struct range_info **get_range_list(TALLOC_CTX *mem_ctx)
++{
++    struct range_info **range_list;
++
++    range_list = talloc_array(mem_ctx, struct range_info *, 2);
++    assert_non_null(range_list);
++
++    range_list[0] = talloc_zero(range_list, struct range_info);
++    assert_non_null(range_list[0]);
++
++    range_list[0]->name = talloc_strdup(range_list[0], RANGE_NAME);
++    assert_non_null( range_list[0]->name);
++    range_list[0]->base_id = BASE_ID;
++    range_list[0]->id_range_size = RANGE_SIZE;
++    range_list[0]->base_rid = BASE_RID;
++    range_list[0]->secondary_base_rid = 0;
++    range_list[0]->trusted_dom_sid = talloc_strdup(range_list[0], DOMAIN_SID);
++    assert_non_null(range_list[0]->trusted_dom_sid);
++    range_list[0]->range_type = talloc_strdup(range_list[0],
++                                              IPA_RANGE_AD_TRUST);
++    assert_non_null(range_list[0]->range_type);
++
++    return range_list;
++}
++
++void setup_idmap_ctx(void **state)
++{
++    int ret;
++    struct test_ctx *test_ctx;
++
++    assert_true(leak_check_setup());
++
++    test_ctx = talloc_zero(global_talloc_context, struct test_ctx);
++    assert_non_null(test_ctx);
++
++    test_ctx->sdap_id_ctx = talloc_zero(test_ctx,
++                                        struct sdap_id_ctx);
++    assert_non_null(test_ctx->sdap_id_ctx);
++
++    test_ctx->sdap_id_ctx->be = talloc_zero(test_ctx->sdap_id_ctx,
++                                            struct be_ctx);
++    assert_non_null(test_ctx->sdap_id_ctx->be);
++
++    test_ctx->sdap_id_ctx->be->domain = talloc_zero(test_ctx->sdap_id_ctx->be,
++                                                    struct sss_domain_info);
++    assert_non_null(test_ctx->sdap_id_ctx->be->domain);
++
++    test_ctx->sdap_id_ctx->be->domain->name =
++                  talloc_strdup(test_ctx->sdap_id_ctx->be->domain, DOMAIN_NAME);
++    assert_non_null(test_ctx->sdap_id_ctx->be->domain->name);
++
++    will_return(__wrap_sysdb_get_ranges, 1);
++    will_return(__wrap_sysdb_get_ranges, get_range_list(global_talloc_context));
++
++    ret = ipa_idmap_init(test_ctx, test_ctx->sdap_id_ctx,
++                         &test_ctx->idmap_ctx);
++    assert_int_equal(ret, EOK);
++
++    check_leaks_push(test_ctx);
++    *state = test_ctx;
++}
++
++void teardown_idmap_ctx(void **state)
++{
++    struct test_ctx *test_ctx = talloc_get_type(*state, struct test_ctx);
++
++    assert_non_null(test_ctx);
++
++    assert_true(check_leaks_pop(test_ctx) == true);
++
++    talloc_free(test_ctx);
++    assert_true(leak_check_teardown());
++}
++
++void test_ipa_idmap_get_ranges_from_sysdb(void **state)
++{
++    int ret;
++    struct test_ctx *test_ctx = talloc_get_type(*state, struct test_ctx);
++    assert_non_null(test_ctx);
++
++    will_return(__wrap_sysdb_get_ranges, 1);
++    will_return(__wrap_sysdb_get_ranges, get_range_list(test_ctx->idmap_ctx));
++    ret = ipa_idmap_get_ranges_from_sysdb(test_ctx->idmap_ctx,
++                                          DOMAIN_NAME, DOMAIN_SID, true);
++    assert_int_equal(ret, EOK);
++
++    will_return(__wrap_sysdb_get_ranges, 1);
++    will_return(__wrap_sysdb_get_ranges, get_range_list(global_talloc_context));
++    ret = ipa_idmap_get_ranges_from_sysdb(test_ctx->idmap_ctx,
++                                          DOMAIN_NAME, DOMAIN_SID, false);
++    assert_int_equal(ret, EIO);
++}
++
++int main(int argc, const char *argv[])
++{
++    poptContext pc;
++    int opt;
++    struct poptOption long_options[] = {
++        POPT_AUTOHELP
++        SSSD_DEBUG_OPTS
++        POPT_TABLEEND
++    };
++
++    const UnitTest tests[] = {
++        unit_test(test_get_idmap_data_from_range),
++        unit_test_setup_teardown(test_ipa_idmap_get_ranges_from_sysdb,
++                                 setup_idmap_ctx, teardown_idmap_ctx),
++    };
++
++    /* 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_INIT(debug_level);
++
++    tests_set_cwd();
++
++    return run_tests(tests);
++}
+-- 
+1.8.5.3
+
diff --git a/SOURCES/0095-IPA-check-ranges-for-collisions-before-saving-them.patch b/SOURCES/0095-IPA-check-ranges-for-collisions-before-saving-them.patch
new file mode 100644
index 0000000..b083bfd
--- /dev/null
+++ b/SOURCES/0095-IPA-check-ranges-for-collisions-before-saving-them.patch
@@ -0,0 +1,193 @@
+From 841fef45aef0a1424d4afbf3ea2bb40566155af9 Mon Sep 17 00:00:00 2001
+From: Sumit Bose <sbose@redhat.com>
+Date: Fri, 7 Feb 2014 18:17:09 +0100
+Subject: [PATCH 95/97] IPA: check ranges for collisions before saving them
+
+Fixes https://fedorahosted.org/sssd/ticket/2253
+---
+ src/providers/ipa/ipa_subdomains.c | 83 +++++++++++++++++++++++++++++---------
+ 1 file changed, 63 insertions(+), 20 deletions(-)
+
+diff --git a/src/providers/ipa/ipa_subdomains.c b/src/providers/ipa/ipa_subdomains.c
+index 88b6ba52538be83417e98c9a5dd033bea87ebe4b..07ae03b6ab0325b011a26f36f4fdc9a5766b8445 100644
+--- a/src/providers/ipa/ipa_subdomains.c
++++ b/src/providers/ipa/ipa_subdomains.c
+@@ -351,14 +351,28 @@ const char *get_flat_name_from_subdomain_name(struct be_ctx *be_ctx,
+ }
+ 
+ static errno_t ipa_ranges_parse_results(TALLOC_CTX *mem_ctx,
++                                        char *domain_name,
+                                         size_t count,
+                                         struct sysdb_attrs **reply,
+                                         struct range_info ***_range_list)
+ {
+     struct range_info **range_list = NULL;
++    struct range_info *r;
+     const char *value;
+     size_t c;
++    size_t d;
+     int ret;
++    enum idmap_error_code err;
++    char *name1;
++    char *name2;
++    char *sid1;
++    char *sid2;
++    uint32_t rid1;
++    uint32_t rid2;
++    struct sss_idmap_range range1;
++    struct sss_idmap_range range2;
++    bool mapping1;
++    bool mapping2;
+ 
+     range_list = talloc_array(mem_ctx, struct range_info *, count + 1);
+     if (range_list == NULL) {
+@@ -367,8 +381,8 @@ static errno_t ipa_ranges_parse_results(TALLOC_CTX *mem_ctx,
+     }
+ 
+     for (c = 0; c < count; c++) {
+-        range_list[c] = talloc_zero(range_list, struct range_info);
+-        if (range_list[c] == NULL) {
++        r = talloc_zero(range_list, struct range_info);
++        if (r == NULL) {
+             DEBUG(SSSDBG_OP_FAILURE, ("talloc_zero failed.\n"));
+             ret = ENOMEM;
+             goto done;
+@@ -379,8 +393,8 @@ static errno_t ipa_ranges_parse_results(TALLOC_CTX *mem_ctx,
+             DEBUG(SSSDBG_OP_FAILURE, ("sysdb_attrs_get_string failed.\n"));
+             goto done;
+         }
+-        range_list[c]->name = talloc_strdup(range_list[c], value);
+-        if (range_list[c]->name == NULL) {
++        r->name = talloc_strdup(r, value);
++        if (r->name == NULL) {
+             DEBUG(SSSDBG_OP_FAILURE, ("talloc_strdup failed.\n"));
+             ret = ENOMEM;
+             goto done;
+@@ -388,9 +402,8 @@ static errno_t ipa_ranges_parse_results(TALLOC_CTX *mem_ctx,
+ 
+         ret = sysdb_attrs_get_string(reply[c], IPA_TRUSTED_DOMAIN_SID, &value);
+         if (ret == EOK) {
+-            range_list[c]->trusted_dom_sid = talloc_strdup(range_list[c],
+-                                                           value);
+-            if (range_list[c]->trusted_dom_sid == NULL) {
++            r->trusted_dom_sid = talloc_strdup(r, value);
++            if (r->trusted_dom_sid == NULL) {
+                 DEBUG(SSSDBG_OP_FAILURE, ("talloc_strdup failed.\n"));
+                 ret = ENOMEM;
+                 goto done;
+@@ -401,28 +414,28 @@ static errno_t ipa_ranges_parse_results(TALLOC_CTX *mem_ctx,
+         }
+ 
+         ret = sysdb_attrs_get_uint32_t(reply[c], IPA_BASE_ID,
+-                                       &range_list[c]->base_id);
++                                       &r->base_id);
+         if (ret != EOK && ret != ENOENT) {
+             DEBUG(SSSDBG_OP_FAILURE, ("sysdb_attrs_get_string failed.\n"));
+             goto done;
+         }
+ 
+         ret = sysdb_attrs_get_uint32_t(reply[c], IPA_ID_RANGE_SIZE,
+-                                       &range_list[c]->id_range_size);
++                                       &r->id_range_size);
+         if (ret != EOK && ret != ENOENT) {
+             DEBUG(SSSDBG_OP_FAILURE, ("sysdb_attrs_get_string failed.\n"));
+             goto done;
+         }
+ 
+         ret = sysdb_attrs_get_uint32_t(reply[c], IPA_BASE_RID,
+-                                       &range_list[c]->base_rid);
++                                       &r->base_rid);
+         if (ret != EOK && ret != ENOENT) {
+             DEBUG(SSSDBG_OP_FAILURE, ("sysdb_attrs_get_string failed.\n"));
+             goto done;
+         }
+ 
+         ret = sysdb_attrs_get_uint32_t(reply[c], IPA_SECONDARY_BASE_RID,
+-                                       &range_list[c]->secondary_base_rid);
++                                       &r->secondary_base_rid);
+         if (ret != EOK && ret != ENOENT) {
+             DEBUG(SSSDBG_OP_FAILURE, ("sysdb_attrs_get_string failed.\n"));
+             goto done;
+@@ -430,8 +443,8 @@ static errno_t ipa_ranges_parse_results(TALLOC_CTX *mem_ctx,
+ 
+         ret = sysdb_attrs_get_string(reply[c], IPA_RANGE_TYPE, &value);
+         if (ret == EOK) {
+-            range_list[c]->range_type = talloc_strdup(range_list[c], value);
+-            if (range_list[c]->range_type == NULL) {
++            r->range_type = talloc_strdup(r, value);
++            if (r->range_type == NULL) {
+                 DEBUG(SSSDBG_OP_FAILURE, ("talloc_strdup failed.\n"));
+                 ret = ENOMEM;
+                 goto done;
+@@ -439,23 +452,52 @@ static errno_t ipa_ranges_parse_results(TALLOC_CTX *mem_ctx,
+         } else if (ret == ENOENT) {
+             /* Older IPA servers might not have the range_type attribute, but
+              * only support local ranges and trusts with algorithmic mapping. */
+-            if (range_list[c]->trusted_dom_sid == NULL) {
+-                range_list[c]->range_type = talloc_strdup(range_list[c],
+-                                                          IPA_RANGE_LOCAL);
++            if (r->trusted_dom_sid == NULL) {
++                r->range_type = talloc_strdup(r, IPA_RANGE_LOCAL);
+             } else {
+-                range_list[c]->range_type = talloc_strdup(range_list[c],
+-                                                          IPA_RANGE_AD_TRUST);
++                r->range_type = talloc_strdup(r, IPA_RANGE_AD_TRUST);
+             }
+         } else {
+             DEBUG(SSSDBG_OP_FAILURE, ("sysdb_attrs_get_string failed.\n"));
+             goto done;
+         }
+-        if (range_list[c]->range_type == NULL) {
++        if (r->range_type == NULL) {
+             DEBUG(SSSDBG_OP_FAILURE, ("talloc_strdup failed.\n"));
+             ret = ENOMEM;
+             goto done;
+         }
++
++        ret = get_idmap_data_from_range(r, domain_name, &name1, &sid1, &rid1,
++                                        &range1, &mapping1);
++        if (ret != EOK) {
++            DEBUG(SSSDBG_OP_FAILURE, ("get_idmap_data_from_range failed.\n"));
++            goto done;
++        }
++        for (d = 0; d < c; d++) {
++            ret = get_idmap_data_from_range(range_list[d], domain_name, &name2,
++                                            &sid2, &rid2, &range2, &mapping2);
++            if (ret != EOK) {
++                DEBUG(SSSDBG_OP_FAILURE,
++                      ("get_idmap_data_from_range failed.\n"));
++                goto done;
++            }
++
++            err = sss_idmap_check_collision_ex(name1, sid1, &range1, rid1,
++                                               r->name, mapping1,
++                                               name2, sid2, &range2, rid2,
++                                               range_list[d]->name, mapping2);
++            if (err != IDMAP_SUCCESS) {
++                DEBUG(SSSDBG_CRIT_FAILURE,
++                      ("Collision of ranges [%s] and [%s] detected.\n",
++                      r->name, range_list[d]->name));
++                ret = EINVAL;
++                goto done;
++            }
++        }
++
++        range_list[c] = r;
+     }
++
+     range_list[c] = NULL;
+ 
+     *_range_list = range_list;
+@@ -1013,7 +1055,8 @@ static void ipa_subdomains_handler_ranges_done(struct tevent_req *req)
+         goto done;
+     }
+ 
+-    ret = ipa_ranges_parse_results(ctx, reply_count, reply, &range_list);
++    ret = ipa_ranges_parse_results(ctx, domain->name,
++                                   reply_count, reply, &range_list);
+     if (ret != EOK) {
+         DEBUG(SSSDBG_OP_FAILURE,
+               ("ipa_ranges_parse_results request failed.\n"));
+-- 
+1.8.5.3
+
diff --git a/SOURCES/0096-OPTS-Allow-using-defaults-for-blobs.patch b/SOURCES/0096-OPTS-Allow-using-defaults-for-blobs.patch
new file mode 100644
index 0000000..ac6b578
--- /dev/null
+++ b/SOURCES/0096-OPTS-Allow-using-defaults-for-blobs.patch
@@ -0,0 +1,31 @@
+From 2f08218f0eb6e069c94401ac439d5d7f5b032564 Mon Sep 17 00:00:00 2001
+From: Jakub Hrozek <jhrozek@redhat.com>
+Date: Mon, 24 Feb 2014 15:42:15 +0100
+Subject: [PATCH 96/97] OPTS: Allow using defaults for blobs
+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 ddd21d5dc3c89712d9286d1f66f4b2af73651cf2)
+---
+ src/providers/data_provider_opts.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+diff --git a/src/providers/data_provider_opts.c b/src/providers/data_provider_opts.c
+index 0edadecc12d2e354c590df9d3ed011cb4e44eee0..5a2e3b74da7d4af4326a56a9cd47b7826e4b78e3 100644
+--- a/src/providers/data_provider_opts.c
++++ b/src/providers/data_provider_opts.c
+@@ -78,6 +78,9 @@ int dp_get_options(TALLOC_CTX *memctx,
+             if (tmp) {
+                 opts[i].val.blob.data = (uint8_t *)tmp;
+                 opts[i].val.blob.length = strlen(tmp);
++            } else if (opts[i].def_val.blob.data != NULL) {
++                opts[i].val.blob.data = opts[i].def_val.blob.data;
++                opts[i].val.blob.length = opts[i].def_val.blob.length;
+             } else {
+                 opts[i].val.blob.data = NULL;
+                 opts[i].val.blob.length = 0;
+-- 
+1.8.5.3
+
diff --git a/SOURCES/0097-DP-Provide-separate-dp_copy_defaults-function.patch b/SOURCES/0097-DP-Provide-separate-dp_copy_defaults-function.patch
new file mode 100644
index 0000000..4ef2599
--- /dev/null
+++ b/SOURCES/0097-DP-Provide-separate-dp_copy_defaults-function.patch
@@ -0,0 +1,637 @@
+From cb5090d6da0e0b378b095b151af70fa21cd62e9e Mon Sep 17 00:00:00 2001
+From: Jakub Hrozek <jhrozek@redhat.com>
+Date: Mon, 24 Feb 2014 15:42:51 +0100
+Subject: [PATCH 97/97] DP: Provide separate dp_copy_defaults function
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+https://fedorahosted.org/sssd/ticket/2257
+
+Reviewed-by: Pavel Březina <pbrezina@redhat.com>
+(cherry picked from commit 90afedb00608547ae1f32aa7aafd552c4b306909)
+---
+ Makefile.am                        |  12 ++
+ src/providers/ad/ad_common.c       |  16 +-
+ src/providers/data_provider.h      |   5 +
+ src/providers/data_provider_opts.c |  42 ++--
+ src/tests/cmocka/test_dp_opts.c    | 421 +++++++++++++++++++++++++++++++++++++
+ src/tests/ipa_ldap_opt-tests.c     |   2 +-
+ 6 files changed, 476 insertions(+), 22 deletions(-)
+ create mode 100644 src/tests/cmocka/test_dp_opts.c
+
+diff --git a/Makefile.am b/Makefile.am
+index 2e1a1e6bacfb79e4ef7068a22a64c21d23858cb9..9025ec6a5bfa16408278506fdd573666b4b6dbe5 100644
+--- a/Makefile.am
++++ b/Makefile.am
+@@ -154,6 +154,7 @@ if HAVE_CMOCKA
+         test_utils \
+         ad_access_filter_tests \
+         ad_common_tests \
++        dp_opt_tests \
+         test_search_bases
+ endif
+ 
+@@ -1459,6 +1460,17 @@ ad_common_tests_LDADD = \
+     libsss_krb5_common.la \
+     libsss_test_common.la
+ 
++dp_opt_tests_SOURCES = \
++    src/providers/data_provider_opts.c \
++    src/tests/cmocka/test_dp_opts.c
++dp_opt_tests_CFLAGS = \
++    $(AM_CFLAGS)
++dp_opt_tests_LDADD = \
++    $(CMOCKA_LIBS) \
++    $(TALLOC_LIBS) \
++    $(SSSD_INTERNAL_LTLIBS) \
++    libsss_test_common.la
++
+ endif
+ 
+ noinst_PROGRAMS = pam_test_client
+diff --git a/src/providers/ad/ad_common.c b/src/providers/ad/ad_common.c
+index 99fa4c07af2a79bb3ca195214ddb0dbd60c61620..605de49f7f7ae910cbc78e38f888600ba78a0c4f 100644
+--- a/src/providers/ad/ad_common.c
++++ b/src/providers/ad/ad_common.c
+@@ -44,10 +44,10 @@ ad_create_default_sdap_options(TALLOC_CTX *mem_ctx)
+         return NULL;
+     }
+ 
+-    ret = dp_copy_options(id_opts,
+-                          ad_def_ldap_opts,
+-                          SDAP_OPTS_BASIC,
+-                          &id_opts->basic);
++    ret = dp_copy_defaults(id_opts,
++                           ad_def_ldap_opts,
++                           SDAP_OPTS_BASIC,
++                           &id_opts->basic);
+     if (ret != EOK) {
+         goto fail;
+     }
+@@ -117,10 +117,10 @@ ad_create_default_options(TALLOC_CTX *mem_ctx,
+     ad_options = talloc_zero(mem_ctx, struct ad_options);
+     if (ad_options == NULL) return NULL;
+ 
+-    ret = dp_copy_options(ad_options,
+-                          ad_basic_opts,
+-                          AD_OPTS_BASIC,
+-                          &ad_options->basic);
++    ret = dp_copy_defaults(ad_options,
++                           ad_basic_opts,
++                           AD_OPTS_BASIC,
++                           &ad_options->basic);
+     if (ret != EOK) {
+         talloc_free(ad_options);
+         return NULL;
+diff --git a/src/providers/data_provider.h b/src/providers/data_provider.h
+index d086d5d2f368578c6a44a2c3b33738c894feba95..d86ff58e65c7ae35f7269fdd10ed78f529ed8d8c 100644
+--- a/src/providers/data_provider.h
++++ b/src/providers/data_provider.h
+@@ -295,6 +295,11 @@ int dp_copy_options(TALLOC_CTX *memctx,
+                     int num_opts,
+                     struct dp_option **_opts);
+ 
++int dp_copy_defaults(TALLOC_CTX *memctx,
++                     struct dp_option *src_opts,
++                     int num_opts,
++                     struct dp_option **_opts);
++
+ const char *_dp_opt_get_cstring(struct dp_option *opts,
+                                     int id, const char *location);
+ char *_dp_opt_get_string(struct dp_option *opts,
+diff --git a/src/providers/data_provider_opts.c b/src/providers/data_provider_opts.c
+index 5a2e3b74da7d4af4326a56a9cd47b7826e4b78e3..0cc48e46e8bbba487aea15b4ad1044e8ddc0482a 100644
+--- a/src/providers/data_provider_opts.c
++++ b/src/providers/data_provider_opts.c
+@@ -131,11 +131,11 @@ done:
+ }
+ 
+ /* =Basic-Option-Helpers================================================== */
+-
+-int dp_copy_options(TALLOC_CTX *memctx,
+-                    struct dp_option *src_opts,
+-                    int num_opts,
+-                    struct dp_option **_opts)
++static int dp_copy_options_ex(TALLOC_CTX *memctx,
++                              bool copy_values,
++                              struct dp_option *src_opts,
++                              int num_opts,
++                              struct dp_option **_opts)
+ {
+     struct dp_option *opts;
+     int i, ret = EOK;
+@@ -151,9 +151,9 @@ int dp_copy_options(TALLOC_CTX *memctx,
+ 
+         switch (src_opts[i].type) {
+         case DP_OPT_STRING:
+-            if (src_opts[i].val.string) {
++            if (copy_values) {
+                 ret = dp_opt_set_string(opts, i, src_opts[i].val.string);
+-            } else if (src_opts[i].def_val.string) {
++            } else {
+                 ret = dp_opt_set_string(opts, i, src_opts[i].def_val.string);
+             }
+             if (ret != EOK) {
+@@ -169,9 +169,9 @@ int dp_copy_options(TALLOC_CTX *memctx,
+             break;
+ 
+         case DP_OPT_BLOB:
+-            if (src_opts[i].val.blob.data) {
++            if (copy_values) {
+                 ret = dp_opt_set_blob(opts, i, src_opts[i].val.blob);
+-            } else if (src_opts[i].def_val.blob.data) {
++            } else {
+                 ret = dp_opt_set_blob(opts, i, src_opts[i].def_val.blob);
+             }
+             if (ret != EOK) {
+@@ -185,9 +185,9 @@ int dp_copy_options(TALLOC_CTX *memctx,
+             break;
+ 
+         case DP_OPT_NUMBER:
+-            if (src_opts[i].val.number) {
++            if (copy_values) {
+                 ret = dp_opt_set_int(opts, i, src_opts[i].val.number);
+-            } else if (src_opts[i].def_val.number) {
++            } else {
+                 ret = dp_opt_set_int(opts, i, src_opts[i].def_val.number);
+             }
+             if (ret != EOK) {
+@@ -201,9 +201,9 @@ int dp_copy_options(TALLOC_CTX *memctx,
+             break;
+ 
+         case DP_OPT_BOOL:
+-            if (src_opts[i].val.boolean) {
++            if (copy_values) {
+                 ret = dp_opt_set_bool(opts, i, src_opts[i].val.boolean);
+-            } else if (src_opts[i].def_val.boolean) {
++            } else {
+                 ret = dp_opt_set_bool(opts, i, src_opts[i].def_val.boolean);
+             }
+             if (ret != EOK) {
+@@ -225,6 +225,22 @@ done:
+     return ret;
+ }
+ 
++int dp_copy_options(TALLOC_CTX *memctx,
++                    struct dp_option *src_opts,
++                    int num_opts,
++                    struct dp_option **_opts)
++{
++    return dp_copy_options_ex(memctx, true, src_opts, num_opts, _opts);
++}
++
++int dp_copy_defaults(TALLOC_CTX *memctx,
++                     struct dp_option *src_opts,
++                     int num_opts,
++                     struct dp_option **_opts)
++{
++    return dp_copy_options_ex(memctx, false, src_opts, num_opts, _opts);
++}
++
+ static const char *dp_opt_type_to_string(enum dp_opt_type type)
+ {
+     switch (type) {
+diff --git a/src/tests/cmocka/test_dp_opts.c b/src/tests/cmocka/test_dp_opts.c
+new file mode 100644
+index 0000000000000000000000000000000000000000..07998b4034fb33195c99340e5544596925ecf145
+--- /dev/null
++++ b/src/tests/cmocka/test_dp_opts.c
+@@ -0,0 +1,421 @@
++/*
++    Authors:
++        Jakub Hrozek <jhrozek@redhat.com>
++
++    Copyright (C) 2014 Red Hat
++
++    SSSD tests: Data Provider Option Tests
++
++    This program is free software; you can redistribute it and/or modify
++    it under the terms of the GNU General Public License as published by
++    the Free Software Foundation; either version 3 of the License, or
++    (at your option) any later version.
++
++    This program is distributed in the hope that it will be useful,
++    but WITHOUT ANY WARRANTY; without even the implied warranty of
++    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++    GNU General Public License for more details.
++
++    You should have received a copy of the GNU General Public License
++    along with this program.  If not, see <http://www.gnu.org/licenses/>.
++*/
++
++#include <popt.h>
++
++#include "providers/data_provider.h"
++
++#include "tests/cmocka/common_mock.h"
++
++#define STRING_DEFAULT  "stringval"
++#define BLOB_DEFAULT    "blobval"
++#define INT_DEFAULT     123
++
++#define TESTS_PATH "tests_opts"
++#define TEST_CONF_DB "test_opt_conf.ldb"
++#define TEST_SYSDB_FILE "cache_opt_test.ldb"
++#define TEST_DOM_NAME "opt_test"
++#define TEST_ID_PROVIDER "ldap"
++
++enum test_opts {
++    OPT_STRING_NODEFAULT,
++    OPT_STRING_DEFAULT,
++    OPT_BLOB_NODEFAULT,
++    OPT_BLOB_DEFAULT,
++    OPT_INT_NODEFAULT,
++    OPT_INT_DEFAULT,
++    OPT_BOOL_TRUE,
++    OPT_BOOL_FALSE,
++
++    OPT_NUM_OPTS
++};
++
++struct dp_option test_def_opts[] = {
++    { "string_nodefault", DP_OPT_STRING, NULL_STRING, NULL_STRING },
++    { "string_default", DP_OPT_STRING, { STRING_DEFAULT }, NULL_STRING},
++    { "blob_nodefault", DP_OPT_BLOB, NULL_BLOB, NULL_BLOB },
++    { "blob_default", DP_OPT_BLOB,
++                      { .blob = { discard_const(BLOB_DEFAULT),
++                                  sizeof(BLOB_DEFAULT) - 1 } },
++                      NULL_BLOB },
++    { "int_nodefault", DP_OPT_NUMBER, NULL_NUMBER, NULL_NUMBER },
++    { "int_default", DP_OPT_NUMBER, { .number = INT_DEFAULT }, NULL_NUMBER },
++    { "bool_true", DP_OPT_BOOL, BOOL_TRUE, BOOL_TRUE },
++    { "bool_false", DP_OPT_BOOL, BOOL_FALSE, BOOL_FALSE },
++    DP_OPTION_TERMINATOR
++};
++
++static void assert_defaults(struct dp_option *opts)
++{
++    char *s;
++    struct dp_opt_blob b;
++    int i;
++    bool bo;
++
++    s = dp_opt_get_string(opts, OPT_STRING_NODEFAULT);
++    assert_null(s);
++
++    s = dp_opt_get_string(opts, OPT_STRING_DEFAULT);
++    assert_non_null(s);
++    assert_string_equal(s, STRING_DEFAULT);
++
++    b = dp_opt_get_blob(opts, OPT_BLOB_NODEFAULT);
++    assert_null(b.data);
++    assert_int_equal(b.length, 0);
++
++    b = dp_opt_get_blob(opts, OPT_BLOB_DEFAULT);
++    assert_non_null(b.data);
++    assert_int_equal(b.length, strlen(BLOB_DEFAULT));
++    assert_memory_equal(b.data, BLOB_DEFAULT, strlen(BLOB_DEFAULT));
++
++    i = dp_opt_get_int(opts, OPT_INT_NODEFAULT);
++    assert_int_equal(i, 0);
++
++    i = dp_opt_get_int(opts, OPT_INT_DEFAULT);
++    assert_int_equal(i, INT_DEFAULT);
++
++    bo = dp_opt_get_bool(opts, OPT_BOOL_TRUE);
++    assert_true(bo == true);
++
++    bo = dp_opt_get_bool(opts, OPT_BOOL_FALSE);
++    assert_true(bo == false);
++}
++
++void opt_test_copy_default(void **state)
++{
++    int ret;
++    TALLOC_CTX *mem_ctx;
++    struct dp_option *opts;
++    struct dp_opt_blob b;
++
++    mem_ctx = talloc_new(global_talloc_context);
++    assert_non_null(mem_ctx);
++
++    ret = dp_copy_defaults(mem_ctx, test_def_opts, OPT_NUM_OPTS, &opts);
++    assert_int_equal(ret, EOK);
++    assert_defaults(opts);
++
++    /* Test that copy_defaults would still copy defaults even if we
++     * change the values
++     */
++    ret = dp_opt_set_string(opts, OPT_STRING_NODEFAULT, "str1");
++    assert_int_equal(ret, EOK);
++    ret = dp_opt_set_string(opts, OPT_STRING_DEFAULT, "str2");
++    assert_int_equal(ret, EOK);
++
++    b.data = discard_const_p(uint8_t, "blob1");
++    b.length = strlen("blob1");
++    ret = dp_opt_set_blob(opts, OPT_BLOB_NODEFAULT, b);
++    assert_int_equal(ret, EOK);
++
++    ret = dp_opt_set_blob(opts, OPT_BLOB_DEFAULT, b);
++    b.data = discard_const_p(uint8_t, "blob2");
++    b.length = strlen("blob2");
++    assert_int_equal(ret, EOK);
++
++    ret = dp_opt_set_int(opts, OPT_INT_NODEFAULT, 456);
++    assert_int_equal(ret, EOK);
++    ret = dp_opt_set_int(opts, OPT_INT_DEFAULT, 789);
++    assert_int_equal(ret, EOK);
++
++    ret = dp_opt_set_bool(opts, OPT_BOOL_TRUE, false);
++    assert_int_equal(ret, EOK);
++    ret = dp_opt_set_bool(opts, OPT_BOOL_FALSE, true);
++    assert_int_equal(ret, EOK);
++
++    talloc_free(opts);
++    ret = dp_copy_defaults(mem_ctx, test_def_opts, OPT_NUM_OPTS, &opts);
++    assert_int_equal(ret, EOK);
++    assert_defaults(opts);
++}
++
++void opt_test_copy_options(void **state)
++{
++    int ret;
++    TALLOC_CTX *mem_ctx;
++    struct dp_option *opts;
++    char *s;
++    struct dp_opt_blob b;
++    int i;
++    bool bo;
++
++    mem_ctx = talloc_new(global_talloc_context);
++    assert_non_null(mem_ctx);
++
++    ret = dp_copy_options(mem_ctx, test_def_opts, OPT_NUM_OPTS, &opts);
++    assert_int_equal(ret, EOK);
++    assert_int_equal(ret, EOK);
++
++    ret = dp_opt_set_string(opts, OPT_STRING_NODEFAULT, "str1");
++    assert_int_equal(ret, EOK);
++
++    b.data = discard_const_p(uint8_t, "blob1");
++    b.length = strlen("blob1");
++    ret = dp_opt_set_blob(opts, OPT_BLOB_NODEFAULT, b);
++    assert_int_equal(ret, EOK);
++
++    ret = dp_opt_set_int(opts, OPT_INT_NODEFAULT, 456);
++    assert_int_equal(ret, EOK);
++
++    ret = dp_opt_set_bool(opts, OPT_BOOL_TRUE, false);
++    assert_int_equal(ret, EOK);
++
++    /* Test that options set to an explicit value retain
++     * the value and even options with default value
++     * do not return the default unless explicitly set
++     */
++    s = dp_opt_get_string(opts, OPT_STRING_NODEFAULT);
++    assert_string_equal(s, "str1");
++    s = dp_opt_get_string(opts, OPT_STRING_DEFAULT);
++    assert_null(s);
++
++    b = dp_opt_get_blob(opts, OPT_BLOB_NODEFAULT);
++    assert_non_null(b.data);
++    assert_int_equal(b.length, strlen("blob1"));
++    assert_memory_equal(b.data, "blob1", strlen("blob1"));
++    b = dp_opt_get_blob(opts, OPT_BLOB_DEFAULT);
++    assert_null(b.data);
++    assert_int_equal(b.length, 0);
++
++    i = dp_opt_get_int(opts, OPT_INT_NODEFAULT);
++    assert_int_equal(i, 456);
++    i = dp_opt_get_int(opts, OPT_INT_DEFAULT);
++    assert_int_equal(i, 0);
++
++    bo = dp_opt_get_bool(opts, OPT_BOOL_TRUE);
++    assert_false(bo == true);
++}
++
++void opt_test_get(void **state)
++{
++    int ret;
++    struct sss_test_ctx *tctx;
++    struct dp_option *opts;
++    char *dompath;
++    struct sss_test_conf_param params[] = {
++        { "string_nodefault", "stringval2" },
++        { "blob_nodefault", "blobval2" },
++        { "int_nodefault", "456" },
++        { "bool_true", "false" },
++        { NULL, NULL },             /* Sentinel */
++    };
++    char *s;
++    struct dp_opt_blob b;
++    int i;
++    bool bo;
++
++    tctx = create_dom_test_ctx(global_talloc_context, TESTS_PATH, TEST_CONF_DB,
++                               TEST_SYSDB_FILE, TEST_DOM_NAME,
++                               TEST_ID_PROVIDER, params);
++    assert_non_null(tctx);
++
++    dompath = talloc_asprintf(tctx, "config/domain/%s", TEST_DOM_NAME);
++    assert_non_null(dompath);
++
++    ret = dp_get_options(global_talloc_context, tctx->confdb, dompath,
++                         test_def_opts, OPT_NUM_OPTS, &opts);
++    assert_int_equal(ret, EOK);
++
++    /* Options that were not specified explicitly should only have the default
++     * value, those that have been specified explicitly should carry that
++     * value
++     */
++    s = dp_opt_get_string(opts, OPT_STRING_NODEFAULT);
++    assert_non_null(s);
++    assert_string_equal(s, "stringval2");
++
++    s = dp_opt_get_string(opts, OPT_STRING_DEFAULT);
++    assert_non_null(s);
++    assert_string_equal(s, STRING_DEFAULT);
++
++    b = dp_opt_get_blob(opts, OPT_BLOB_NODEFAULT);
++    assert_non_null(b.data);
++    assert_int_equal(b.length, strlen("blobval2"));
++    assert_memory_equal(b.data, "blobval2", strlen("blobval2"));
++
++    b = dp_opt_get_blob(opts, OPT_BLOB_DEFAULT);
++    assert_non_null(b.data);
++    assert_int_equal(b.length, strlen(BLOB_DEFAULT));
++    assert_memory_equal(b.data, BLOB_DEFAULT, strlen(BLOB_DEFAULT));
++
++    i = dp_opt_get_int(opts, OPT_INT_NODEFAULT);
++    assert_int_equal(i, 456);
++
++    i = dp_opt_get_int(opts, OPT_INT_DEFAULT);
++    assert_int_equal(i, INT_DEFAULT);
++
++    bo = dp_opt_get_bool(opts, OPT_BOOL_TRUE);
++    assert_true(bo == false);
++
++    bo = dp_opt_get_bool(opts, OPT_BOOL_FALSE);
++    assert_true(bo == false);
++}
++
++void opt_test_getset_setup(void **state)
++{
++    int ret;
++    struct dp_option *opts;
++
++    ret = dp_copy_defaults(global_talloc_context,
++                           test_def_opts, OPT_NUM_OPTS, &opts);
++    assert_int_equal(ret, EOK);
++    assert_defaults(opts);
++
++    *state = opts;
++}
++
++void opt_test_getset_teardown(void **state)
++{
++    struct dp_option *opts = talloc_get_type(*state, struct dp_option);
++    talloc_free(opts);
++}
++
++void opt_test_getset_string(void **state)
++{
++    struct dp_option *opts = talloc_get_type(*state, struct dp_option);
++    int ret;
++    char *s;
++
++    s = dp_opt_get_string(opts, OPT_STRING_NODEFAULT);
++    assert_null(s);
++
++    ret = dp_opt_set_string(opts, OPT_STRING_NODEFAULT, "str1");
++    assert_int_equal(ret, EOK);
++
++    s = dp_opt_get_string(opts, OPT_STRING_NODEFAULT);
++    assert_non_null(s);
++    assert_string_equal(s, "str1");
++}
++
++void opt_test_getset_blob(void **state)
++{
++    struct dp_option *opts = talloc_get_type(*state, struct dp_option);
++    int ret;
++    struct dp_opt_blob b;
++
++    b = dp_opt_get_blob(opts, OPT_BLOB_NODEFAULT);
++    assert_null(b.data);
++    assert_int_equal(b.length, 0);
++
++    b.data = discard_const_p(uint8_t, "blob2");
++    b.length = strlen("blob2");
++    ret = dp_opt_set_blob(opts, OPT_BLOB_NODEFAULT, b);
++    assert_int_equal(ret, EOK);
++
++    b = dp_opt_get_blob(opts, OPT_BLOB_NODEFAULT);
++    assert_non_null(b.data);
++    assert_int_equal(b.length, strlen("blob2"));
++    assert_memory_equal(b.data, "blob2", strlen("blob2"));
++}
++
++void opt_test_getset_int(void **state)
++{
++    struct dp_option *opts = talloc_get_type(*state, struct dp_option);
++    int ret;
++    int i;
++
++    i = dp_opt_get_int(opts, OPT_INT_NODEFAULT);
++    assert_int_equal(i, 0);
++
++    ret = dp_opt_set_int(opts, OPT_INT_NODEFAULT, 456);
++    assert_int_equal(ret, EOK);
++
++    i = dp_opt_get_int(opts, OPT_INT_NODEFAULT);
++    assert_int_equal(i, 456);
++}
++
++void opt_test_getset_bool(void **state)
++{
++    struct dp_option *opts = talloc_get_type(*state, struct dp_option);
++    int ret;
++    bool b;
++
++    b = dp_opt_get_bool(opts, OPT_BOOL_TRUE);
++    assert_true(b == true);
++
++    ret = dp_opt_set_bool(opts, OPT_BOOL_TRUE, false);
++    assert_int_equal(ret, EOK);
++
++    b = dp_opt_get_bool(opts, OPT_BOOL_TRUE);
++    assert_false(b == true);
++}
++
++int main(int argc, const char *argv[])
++{
++    int no_cleanup = 0;
++    poptContext pc;
++    int opt;
++    int ret;
++    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 UnitTest tests[] = {
++        unit_test_setup_teardown(opt_test_getset_string,
++                                 opt_test_getset_setup,
++                                 opt_test_getset_teardown),
++        unit_test_setup_teardown(opt_test_getset_int,
++                                 opt_test_getset_setup,
++                                 opt_test_getset_teardown),
++        unit_test_setup_teardown(opt_test_getset_bool,
++                                 opt_test_getset_setup,
++                                 opt_test_getset_teardown),
++        unit_test_setup_teardown(opt_test_getset_blob,
++                                 opt_test_getset_setup,
++                                 opt_test_getset_teardown),
++        unit_test(opt_test_copy_default),
++        unit_test(opt_test_copy_options),
++        unit_test(opt_test_get)
++    };
++
++    /* 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_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();
++    test_dom_suite_cleanup(TESTS_PATH, TEST_CONF_DB, TEST_SYSDB_FILE);
++    test_dom_suite_setup(TESTS_PATH);
++
++    ret = run_tests(tests);
++    if (ret == 0 && !no_cleanup) {
++        test_dom_suite_cleanup(TESTS_PATH, TEST_CONF_DB, TEST_SYSDB_FILE);
++    }
++    return ret;
++}
+diff --git a/src/tests/ipa_ldap_opt-tests.c b/src/tests/ipa_ldap_opt-tests.c
+index 40afa5cba87d89bc6fa7345302991fe766b43314..25a094082dc369092f10ad823d98909027dd9a7e 100644
+--- a/src/tests/ipa_ldap_opt-tests.c
++++ b/src/tests/ipa_ldap_opt-tests.c
+@@ -170,7 +170,7 @@ START_TEST(test_copy_opts)
+     tmp_ctx = talloc_new(NULL);
+     fail_unless(tmp_ctx != NULL, "talloc_new failed");
+ 
+-    ret = dp_copy_options(tmp_ctx, ad_def_ldap_opts, SDAP_OPTS_BASIC, &opts);
++    ret = dp_copy_defaults(tmp_ctx, ad_def_ldap_opts, SDAP_OPTS_BASIC, &opts);
+     fail_unless(ret == EOK, "[%s]", strerror(ret));
+ 
+     for (int i=0; i < SDAP_OPTS_BASIC; i++) {
+-- 
+1.8.5.3
+
diff --git a/SOURCES/0098-MAN-Clarify-the-ldap_access_filter-option-further.patch b/SOURCES/0098-MAN-Clarify-the-ldap_access_filter-option-further.patch
new file mode 100644
index 0000000..d98e726
--- /dev/null
+++ b/SOURCES/0098-MAN-Clarify-the-ldap_access_filter-option-further.patch
@@ -0,0 +1,49 @@
+From af16267fc9d681fc4230fa82a9fe86de9491c8fd Mon Sep 17 00:00:00 2001
+From: Jakub Hrozek <jhrozek@redhat.com>
+Date: Mon, 24 Feb 2014 19:42:23 +0100
+Subject: [PATCH 98/99] MAN: Clarify the ldap_access_filter option further
+
+https://fedorahosted.org/sssd/ticket/2235
+
+The memberof example was misleading and was making aministrators think
+that the ldap_access_filter can resolve nested group memberships.
+
+Reviewed-by: Sumit Bose <sbose@redhat.com>
+Reviewed-by: Stephen Gallagher <sgallagh@redhat.com>
+(cherry picked from commit 604d46e028ab62f83060fb88bdd3319a31aca2d1)
+---
+ src/man/sssd-ldap.5.xml | 9 +++++----
+ 1 file changed, 5 insertions(+), 4 deletions(-)
+
+diff --git a/src/man/sssd-ldap.5.xml b/src/man/sssd-ldap.5.xml
+index cc58544c38e8ffa779f0a1b22a69caaf3f193ce1..b271a2b7fa8b19ac3e4475bd8ca634b0414f5ea4 100644
+--- a/src/man/sssd-ldap.5.xml
++++ b/src/man/sssd-ldap.5.xml
+@@ -1775,19 +1775,20 @@
+                             and this option is not set, it will result in all
+                             users being denied access.
+                             Use access_provider = permit to change this default
+-                            behavior.
++                            behavior. Please note that this filter is applied on
++                            the LDAP user entry only.
+                         </para>
+                         <para>
+                             Example:
+                         </para>
+                         <programlisting>
+ access_provider = ldap
+-ldap_access_filter = memberOf=cn=allowedusers,ou=Groups,dc=example,dc=com
++ldap_access_filter = (employeeType=admin)
+                         </programlisting>
+                         <para>
+                             This example means that access to this host is
+-                            restricted to members of the "allowedusers" group
+-                            in ldap.
++                            restricted to users whose employeeType
++                            attribute is set to "admin".
+                         </para>
+                         <para>
+                             Offline caching for this feature is limited to
+-- 
+1.8.5.3
+
diff --git a/SOURCES/0099-MAN-Clarify-that-changing-ID-mapping-options-might-r.patch b/SOURCES/0099-MAN-Clarify-that-changing-ID-mapping-options-might-r.patch
new file mode 100644
index 0000000..d560ead
--- /dev/null
+++ b/SOURCES/0099-MAN-Clarify-that-changing-ID-mapping-options-might-r.patch
@@ -0,0 +1,75 @@
+From 59995f35b7dd6ec552be1081b0120f2344e3ded3 Mon Sep 17 00:00:00 2001
+From: Jakub Hrozek <jhrozek@redhat.com>
+Date: Tue, 25 Feb 2014 17:09:00 +0100
+Subject: [PATCH 99/99] MAN: Clarify that changing ID mapping options might
+ require purging the cache
+
+https://fedorahosted.org/sssd/ticket/2252
+
+Currently SSSD chokes when IDs of users change, we don't support ID
+changes yet. Because some users were confused about the failures, this
+patch adds additional clarification.
+
+Reviewed-by: Sumit Bose <sbose@redhat.com>
+Reviewed-by: Stephen Gallagher <sgallagh@redhat.com>
+(cherry picked from commit 3dfa09a826e5f63b4948462c2452937fc329834d)
+---
+ src/man/include/ldap_id_mapping.xml | 42 +++++++++++++++++++++++++++++++++++++
+ 1 file changed, 42 insertions(+)
+
+diff --git a/src/man/include/ldap_id_mapping.xml b/src/man/include/ldap_id_mapping.xml
+index 71ff248f1f24242b911f615fd6afeb0382dfa5a1..7f5dbd30be67745b26dbced341762705d6e09f14 100644
+--- a/src/man/include/ldap_id_mapping.xml
++++ b/src/man/include/ldap_id_mapping.xml
+@@ -12,6 +12,48 @@
+         need to use manually-assigned values, ALL values must be
+         manually-assigned.
+     </para>
++    <para>
++        Please note that changing the ID mapping related configuration
++        options will cause user and group IDs to change. At the moment,
++        SSSD does not support changing IDs, so the SSSD database must
++        be removed. Because cached passwords are also stored in the
++        database, removing the database should only be performed while
++        the authentication servers are reachable, otherwise users might
++        get locked out. In order to cache the password, an authentication
++        must be performed. It is not sufficient to use
++        <citerefentry>
++            <refentrytitle>sss_cache</refentrytitle>
++            <manvolnum>8</manvolnum>
++        </citerefentry>
++        to remove the database, rather the process
++        consists of:
++            <itemizedlist>
++                <listitem>
++                    <para>
++                        Making sure the remote servers are reachable
++                    </para>
++                </listitem>
++                <listitem>
++                    <para>
++                        Stopping the SSSD service
++                    </para>
++                </listitem>
++                <listitem>
++                    <para>
++                        Removing the database
++                    </para>
++                </listitem>
++                <listitem>
++                    <para>
++                        Starting the SSSD service
++                    </para>
++                </listitem>
++            </itemizedlist>
++        Moreover, as the change of IDs might necessitate the adjustment
++        of other system properties such as file and directory ownership,
++        it's advisable to plan ahead and test the ID mapping configuration
++        thoroughly.
++    </para>
+ 
+     <refsect2 id='idmap_algorithm'>
+         <title>Mapping Algorithm</title>
+-- 
+1.8.5.3
+
diff --git a/SOURCES/0100-DOC-Fix-names-of-arguments-in-doxygen-comments.patch b/SOURCES/0100-DOC-Fix-names-of-arguments-in-doxygen-comments.patch
new file mode 100644
index 0000000..5138aea
--- /dev/null
+++ b/SOURCES/0100-DOC-Fix-names-of-arguments-in-doxygen-comments.patch
@@ -0,0 +1,76 @@
+From 02e3c4dad405464c2f0cec97203a98e5fb251273 Mon Sep 17 00:00:00 2001
+From: Lukas Slebodnik <lslebodn@redhat.com>
+Date: Thu, 13 Feb 2014 17:46:29 +0100
+Subject: [PATCH 100/101] DOC: Fix names of arguments in doxygen comments
+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 3b35ff47651e4893ce537a273466766b962362da)
+---
+ src/lib/idmap/sss_idmap.h            | 2 +-
+ src/sss_client/idmap/sss_nss_idmap.h | 6 +++---
+ src/util/util.h                      | 2 +-
+ 3 files changed, 5 insertions(+), 5 deletions(-)
+
+diff --git a/src/lib/idmap/sss_idmap.h b/src/lib/idmap/sss_idmap.h
+index ccc63f7f760b877cdb17696325731f8e540b2736..0797083293f7e010962828ddcd72709b290859b9 100644
+--- a/src/lib/idmap/sss_idmap.h
++++ b/src/lib/idmap/sss_idmap.h
+@@ -608,7 +608,7 @@ enum idmap_error_code sss_idmap_free_smb_sid(struct sss_idmap_ctx *ctx,
+  * @brief Free mapped binary SID.
+  *
+  * @param[in] ctx         Idmap context
+- * @param[in] smb_sid     Binary SID to be freed.
++ * @param[in] bin_sid     Binary SID to be freed.
+  *
+  * @return
+  *  - #IDMAP_CONTEXT_INVALID: Provided context is invalid
+diff --git a/src/sss_client/idmap/sss_nss_idmap.h b/src/sss_client/idmap/sss_nss_idmap.h
+index be5c506e27f0e418022cff78b48ca3a37aacd5af..79dacfbdb1708569507742fbd8579cf7aaa06d9b 100644
+--- a/src/sss_client/idmap/sss_nss_idmap.h
++++ b/src/sss_client/idmap/sss_nss_idmap.h
+@@ -43,7 +43,7 @@ enum sss_id_type {
+  * @param[in] fq_name  Fully qualified name of a user or a group
+  * @param[out] sid     String representation of the SID of the requested user
+  *                     or group, must be freed by the caller
+- * @param[out] id_type Type of the object related to the given name
++ * @param[out] type    Type of the object related to the given name
+  *
+  * @return
+  *  - 0 (EOK): success, sid contains the requested SID
+@@ -63,7 +63,7 @@ int sss_nss_getsidbyname(const char *fq_name, char **sid,
+  * @param[in] id       POSIX UID or GID
+  * @param[out] sid     String representation of the SID of the requested user
+  *                     or group, must be freed by the caller
+- * @param[out] id_type Type of the object related to the given ID
++ * @param[out] type    Type of the object related to the given ID
+  *
+  * @return
+  *  - see #sss_nss_getsidbyname
+@@ -76,7 +76,7 @@ int sss_nss_getsidbyid(uint32_t id, char **sid, enum sss_id_type *type);
+  * @param[in] sid      String representation of the SID
+  * @param[out] fq_name Fully qualified name of a user or a group,
+  *                     must be freed by the caller
+- * @param[out] id_type Type of the object related to the SID
++ * @param[out] type    Type of the object related to the SID
+  *
+  * @return
+  *  - see #sss_nss_getsidbyname
+diff --git a/src/util/util.h b/src/util/util.h
+index 7b185bcb4287a4afc5bf67b40164cf69b9beeb19..89684e84a5be6c26fe08e5d3ab3d20d3a0e199b1 100644
+--- a/src/util/util.h
++++ b/src/util/util.h
+@@ -513,7 +513,7 @@ bool string_in_list(const char *string, char **list, bool case_sensitive);
+  *        prevents the compiler from optimizing out
+  *
+  * @param data   The address of buffer to wipe
+- * @param s      Size of the buffer
++ * @param size   Size of the buffer
+  */
+ void safezero(void *data, size_t size);
+ 
+-- 
+1.8.5.3
+
diff --git a/SOURCES/0101-libsss_idmap-bump-version-info.patch b/SOURCES/0101-libsss_idmap-bump-version-info.patch
new file mode 100644
index 0000000..d40eb17
--- /dev/null
+++ b/SOURCES/0101-libsss_idmap-bump-version-info.patch
@@ -0,0 +1,31 @@
+From e5792a6be9c3c75c7ec47af873309668f1943ae2 Mon Sep 17 00:00:00 2001
+From: Sumit Bose <sbose@redhat.com>
+Date: Wed, 26 Feb 2014 20:50:19 +0100
+Subject: [PATCH 101/101] libsss_idmap: bump version-info
+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>
+(cherry picked from commit 034ffb3c69cd04f03b36b89766c47a7c9bd9b831)
+---
+ Makefile.am | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/Makefile.am b/Makefile.am
+index 9025ec6a5bfa16408278506fdd573666b4b6dbe5..879054c2fb96f937fbd58ca0757d703cdea218d8 100644
+--- a/Makefile.am
++++ b/Makefile.am
+@@ -614,7 +614,7 @@ libsss_idmap_la_SOURCES = \
+     src/lib/idmap/sss_idmap_conv.c \
+     src/util/murmurhash3.c
+ libsss_idmap_la_LDFLAGS = \
+-    -version-info 3:0:3
++    -version-info 4:0:4
+ 
+ dist_pkgconfig_DATA += src/sss_client/idmap/sss_nss_idmap.pc
+ libsss_nss_idmap_la_SOURCES = \
+-- 
+1.8.5.3
+
diff --git a/SOURCES/0102-config-API-add-missing-subdomain-target-to-AD-provid.patch b/SOURCES/0102-config-API-add-missing-subdomain-target-to-AD-provid.patch
new file mode 100644
index 0000000..3c91933
--- /dev/null
+++ b/SOURCES/0102-config-API-add-missing-subdomain-target-to-AD-provid.patch
@@ -0,0 +1,33 @@
+From 565ae8c3bec0dd3f1cb618b3766a907b820625ca Mon Sep 17 00:00:00 2001
+From: Sumit Bose <sbose@redhat.com>
+Date: Fri, 28 Feb 2014 10:04:08 +0100
+Subject: [PATCH 102/104] config API: add missing subdomain target to AD
+ provider test
+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>
+Reviewed-by: Pavel Březina <pbrezina@redhat.com>
+(cherry picked from commit b564424a77c7c3b361c944e0623023d0cfea2c9f)
+---
+ src/config/SSSDConfigTest.py | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/src/config/SSSDConfigTest.py b/src/config/SSSDConfigTest.py
+index b6c1d74aa42917fde1222f90f99cb343c80d921a..e6cf663ec86396a3d50dcbc14d4cf4d1157b0d5d 100755
+--- a/src/config/SSSDConfigTest.py
++++ b/src/config/SSSDConfigTest.py
+@@ -730,7 +730,7 @@ class SSSDConfigTestSSSDDomain(unittest.TestCase):
+         control_provider_dict = {
+             'ipa': ['id', 'auth', 'access', 'chpass', 'sudo', 'autofs',
+                     'session', 'hostid', 'subdomains'],
+-            'ad': ['id', 'auth', 'access', 'chpass'],
++            'ad': ['id', 'auth', 'access', 'chpass', 'subdomains'],
+             'local': ['id', 'auth', 'chpass'],
+             'ldap': ['id', 'auth', 'access', 'chpass', 'sudo', 'autofs'],
+             'krb5': ['auth', 'access', 'chpass'],
+-- 
+1.8.5.3
+
diff --git a/SOURCES/0103-SUDO-AD-provider.patch b/SOURCES/0103-SUDO-AD-provider.patch
new file mode 100644
index 0000000..a456185
--- /dev/null
+++ b/SOURCES/0103-SUDO-AD-provider.patch
@@ -0,0 +1,244 @@
+From a15ab6146ebba795e3b58d5f32cf7a1d8653c082 Mon Sep 17 00:00:00 2001
+From: Sumit Bose <sbose@redhat.com>
+Date: Fri, 28 Feb 2014 10:05:34 +0100
+Subject: [PATCH 103/104] SUDO: AD provider
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+This patch adds the sudo target to the AD provider. The main reason is
+to cover different default settings in the LDAP and AD provider. E.g.
+the default for ldap_id_mapping is True in the AD provider and False
+in the LDAP provider. If ldap_id_mapping was not set explicitly in the
+config file both components worked with different setting.
+
+Fixes https://fedorahosted.org/sssd/ticket/2256
+
+Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
+Reviewed-by: Lukáš Slebodník <lslebodn@redhat.com>
+Reviewed-by: Pavel Březina <pbrezina@redhat.com>
+(cherry picked from commit 61804568ce5ede3b1a699cda17c033dd6c23f0e3)
+---
+ Makefile.am                            |  5 ++++
+ src/config/SSSDConfigTest.py           |  2 +-
+ src/config/etc/sssd.api.d/sssd-ad.conf | 21 ++++++++++++++
+ src/man/sssd-ad.5.xml                  |  6 ++--
+ src/man/sssd.conf.5.xml                | 15 ++++++++--
+ src/providers/ad/ad_common.h           |  4 +++
+ src/providers/ad/ad_init.c             | 25 +++++++++++++++++
+ src/providers/ad/ad_sudo.c             | 51 ++++++++++++++++++++++++++++++++++
+ 8 files changed, 122 insertions(+), 7 deletions(-)
+ create mode 100644 src/providers/ad/ad_sudo.c
+
+diff --git a/Makefile.am b/Makefile.am
+index 879054c2fb96f937fbd58ca0757d703cdea218d8..b37c04067d34569ad357327b7d463cc5b052f065 100644
+--- a/Makefile.am
++++ b/Makefile.am
+@@ -1803,6 +1803,11 @@ libsss_ad_la_SOURCES = \
+     src/util/sss_krb5.c \
+     src/util/sss_ldap.c
+ 
++if BUILD_SUDO
++libsss_ad_la_SOURCES += \
++    src/providers/ad/ad_sudo.c
++endif
++
+ libsss_ad_la_CFLAGS = \
+     $(AM_CFLAGS) \
+     $(LDAP_CFLAGS) \
+diff --git a/src/config/SSSDConfigTest.py b/src/config/SSSDConfigTest.py
+index e6cf663ec86396a3d50dcbc14d4cf4d1157b0d5d..98b2fee63d519201047b0c576295863d59b0a37a 100755
+--- a/src/config/SSSDConfigTest.py
++++ b/src/config/SSSDConfigTest.py
+@@ -730,7 +730,7 @@ class SSSDConfigTestSSSDDomain(unittest.TestCase):
+         control_provider_dict = {
+             'ipa': ['id', 'auth', 'access', 'chpass', 'sudo', 'autofs',
+                     'session', 'hostid', 'subdomains'],
+-            'ad': ['id', 'auth', 'access', 'chpass', 'subdomains'],
++            'ad': ['id', 'auth', 'access', 'chpass', 'sudo', 'subdomains'],
+             'local': ['id', 'auth', 'chpass'],
+             'ldap': ['id', 'auth', 'access', 'chpass', 'sudo', 'autofs'],
+             'krb5': ['auth', 'access', 'chpass'],
+diff --git a/src/config/etc/sssd.api.d/sssd-ad.conf b/src/config/etc/sssd.api.d/sssd-ad.conf
+index 6b136f2ec88614092cf1ceb4e2cea79db064d468..aa20ca0bb5b70818525d61a1480a6b56bd8c4e48 100644
+--- a/src/config/etc/sssd.api.d/sssd-ad.conf
++++ b/src/config/etc/sssd.api.d/sssd-ad.conf
+@@ -132,3 +132,24 @@ krb5_kpasswd = str, None, false
+ krb5_backup_kpasswd = str, None, false
+ 
+ [provider/ad/subdomains]
++
++[provider/ad/sudo]
++ldap_sudo_search_base = str, None, false
++ldap_sudo_full_refresh_interval = int, None, false
++ldap_sudo_smart_refresh_interval = int, None, false
++ldap_sudo_use_host_filter = bool, None, false
++ldap_sudo_hostnames = str, None, false
++ldap_sudo_ip = str, None, false
++ldap_sudo_include_netgroups = bool, None, false
++ldap_sudo_include_regexp = bool, None, false
++ldap_sudorule_object_class = str, None, false
++ldap_sudorule_name = str, None, false
++ldap_sudorule_command = str, None, false
++ldap_sudorule_host = str, None, false
++ldap_sudorule_user = str, None, false
++ldap_sudorule_option = str, None, false
++ldap_sudorule_runasuser = str, None, false
++ldap_sudorule_runasgroup = str, None, false
++ldap_sudorule_notbefore = str, None, false
++ldap_sudorule_notafter = str, None, false
++ldap_sudorule_order = str, None, false
+diff --git a/src/man/sssd-ad.5.xml b/src/man/sssd-ad.5.xml
+index 38cc31278cf87c98ca9e53cf91fda7b141bff78d..8cd94d4aeaf553ecb54e0e4c866be5fb7a44fa8e 100644
+--- a/src/man/sssd-ad.5.xml
++++ b/src/man/sssd-ad.5.xml
+@@ -60,9 +60,9 @@
+         </para>
+         <para>
+             However, it is neither necessary nor recommended to set these
+-            options. The AD provider can also be used as an access and chpass
+-            provider. No configuration of the access provider is required on
+-            the client side.
++            options. The AD provider can also be used as an access, chpass and
++            sudo provider. No configuration of the access provider is required
++            on the client side.
+         </para>
+         <para>
+             By default, the AD provider will map UID and GID values from the
+diff --git a/src/man/sssd.conf.5.xml b/src/man/sssd.conf.5.xml
+index 5d861c73cfeb41920619d95e5c1e5c1975dcc45b..29b08d53d2568f2fce47b37ea0b88c9dc233c12e 100644
+--- a/src/man/sssd.conf.5.xml
++++ b/src/man/sssd.conf.5.xml
+@@ -1450,14 +1450,23 @@ fallback_homedir = /home/%u
+                             <citerefentry>
+                                 <refentrytitle>sssd-ldap</refentrytitle>
+                                 <manvolnum>5</manvolnum>
+-                            </citerefentry> for more information on configuring LDAP.
++                            </citerefentry> for more information on configuring
++                            LDAP.
++                        </para>
++                        <para>
++                            <quote>ipa</quote> the same as <quote>ldap</quote>
++                            but with IPA default settings.
++                        </para>
++                        <para>
++                            <quote>ad</quote> the same as <quote>ldap</quote>
++                            but with AD default settings.
+                         </para>
+                         <para>
+                             <quote>none</quote> disables SUDO explicitly.
+                         </para>
+                         <para>
+-                            Default: The value of <quote>id_provider</quote> is used if it
+-                            is set.
++                            Default: The value of <quote>id_provider</quote> is
++                            used if it is set.
+                         </para>
+                     </listitem>
+                 </varlistentry>
+diff --git a/src/providers/ad/ad_common.h b/src/providers/ad/ad_common.h
+index d370cef69124c127f41d7c4cbaa25713363e7752..bc11e54b0c4903c876f23bfea3ef573f06ba8c69 100644
+--- a/src/providers/ad/ad_common.h
++++ b/src/providers/ad/ad_common.h
+@@ -128,4 +128,8 @@ errno_t ad_dyndns_init(struct be_ctx *be_ctx,
+                        struct ad_options *ctx);
+ void ad_dyndns_timer(void *pvt);
+ 
++int ad_sudo_init(struct be_ctx *be_ctx,
++                 struct ad_id_ctx *id_ctx,
++                 struct bet_ops **ops,
++                 void **pvt_data);
+ #endif /* AD_COMMON_H_ */
+diff --git a/src/providers/ad/ad_init.c b/src/providers/ad/ad_init.c
+index eff6d990d131e3aba124d252d001dd39e78b45cf..500d807e9c44e92089d31c81f3b22c9606c476e5 100644
+--- a/src/providers/ad/ad_init.c
++++ b/src/providers/ad/ad_init.c
+@@ -467,3 +467,28 @@ int sssm_ad_subdomains_init(struct be_ctx *bectx,
+ 
+     return EOK;
+ }
++
++
++int sssm_ad_sudo_init(struct be_ctx *bectx,
++                      struct bet_ops **ops,
++                      void **pvt_data)
++{
++#ifdef BUILD_SUDO
++    struct ad_id_ctx *id_ctx;
++    int ret;
++
++    DEBUG(SSSDBG_TRACE_INTERNAL, ("Initializing AD sudo handler\n"));
++
++    ret = sssm_ad_id_init(bectx, ops, (void **) &id_ctx);
++    if (ret != EOK) {
++        DEBUG(SSSDBG_CRIT_FAILURE, ("sssm_ad_id_init failed.\n"));
++        return ret;
++    }
++
++    return ad_sudo_init(bectx, id_ctx, ops, pvt_data);
++#else
++    DEBUG(SSSDBG_MINOR_FAILURE, ("Sudo init handler called but SSSD is "
++                                 "built without sudo support, ignoring\n"));
++    return EOK;
++#endif
++}
+diff --git a/src/providers/ad/ad_sudo.c b/src/providers/ad/ad_sudo.c
+new file mode 100644
+index 0000000000000000000000000000000000000000..b85c95c5c2f44e116a75bc24e073c067806621dd
+--- /dev/null
++++ b/src/providers/ad/ad_sudo.c
+@@ -0,0 +1,51 @@
++/*
++    SSSD
++
++    AD SUDO Provider Initialization functions
++
++    Authors:
++        Sumit Bose <sbose@redhat.com>
++
++    Copyright (C) 2014 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 "providers/ad/ad_common.h"
++#include "providers/ldap/sdap_sudo.h"
++
++int ad_sudo_init(struct be_ctx *be_ctx,
++                 struct ad_id_ctx *id_ctx,
++                 struct bet_ops **ops,
++                 void **pvt_data)
++{
++    int ret;
++    struct ad_options *ad_options;
++    struct sdap_options *ldap_options;
++
++    DEBUG(SSSDBG_TRACE_INTERNAL, ("Initializing sudo AD back end\n"));
++
++    ret = sdap_sudo_init(be_ctx, id_ctx->sdap_id_ctx, ops, pvt_data);
++    if (ret != EOK) {
++        DEBUG(SSSDBG_OP_FAILURE, ("Cannot initialize LDAP SUDO [%d]: %s\n",
++                                 ret, strerror(ret)));
++        return ret;
++    }
++
++    ad_options = id_ctx->ad_options;
++    ldap_options = id_ctx->sdap_id_ctx->opts;
++
++    ad_options->id->sudorule_map = ldap_options->sudorule_map;
++    return EOK;
++}
+-- 
+1.8.5.3
+
diff --git a/SOURCES/0104-ipa-server-mode-use-lower-case-user-name-for-home-di.patch b/SOURCES/0104-ipa-server-mode-use-lower-case-user-name-for-home-di.patch
new file mode 100644
index 0000000..4071e9b
--- /dev/null
+++ b/SOURCES/0104-ipa-server-mode-use-lower-case-user-name-for-home-di.patch
@@ -0,0 +1,51 @@
+From 3b8640e8ef5cbcf934cdd6f42e9f6d956ca47395 Mon Sep 17 00:00:00 2001
+From: Sumit Bose <sbose@redhat.com>
+Date: Mon, 3 Mar 2014 12:40:43 +0100
+Subject: [PATCH 104/104] ipa-server-mode: use lower-case user name for home
+ dir
+
+In older IPA server versions where the AD users where looked up by
+winbind the user name component of the home directory path was always
+lower case.  This still holds for IPA clients as well. To avoid
+regression this patch makes the user name component lower case as well.
+
+Fixes https://fedorahosted.org/sssd/ticket/2263
+
+Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
+(cherry picked from commit 48b1db73639135dd4a15ee153f958c912836c621)
+---
+ src/providers/ipa/ipa_subdomains_id.c | 11 ++++++++++-
+ 1 file changed, 10 insertions(+), 1 deletion(-)
+
+diff --git a/src/providers/ipa/ipa_subdomains_id.c b/src/providers/ipa/ipa_subdomains_id.c
+index 637dd61f9f272eb4ac4ecb8368d2210801bb0373..00993c496c1d100b37a780828c81492c2fac6157 100644
+--- a/src/providers/ipa/ipa_subdomains_id.c
++++ b/src/providers/ipa/ipa_subdomains_id.c
+@@ -358,6 +358,7 @@ get_subdomain_homedir_of_user(TALLOC_CTX *mem_ctx, struct sss_domain_info *dom,
+ {
+     errno_t ret;
+     char *name;
++    char *lc_name;
+     const char *homedir;
+     TALLOC_CTX *tmp_ctx;
+ 
+@@ -372,7 +373,15 @@ get_subdomain_homedir_of_user(TALLOC_CTX *mem_ctx, struct sss_domain_info *dom,
+         goto done;
+     }
+ 
+-    homedir = expand_homedir_template(tmp_ctx, dom->subdomain_homedir, name,
++    /* To be compatible with the old winbind based user lookups and IPA
++     * clients the user name in the home directory path will be lower-case. */
++    lc_name = sss_tc_utf8_str_tolower(tmp_ctx, name);
++    if (lc_name == NULL) {
++        ret =ENOMEM;
++        goto done;
++    }
++
++    homedir = expand_homedir_template(tmp_ctx, dom->subdomain_homedir, lc_name,
+                                       uid, NULL, dom->name, dom->flat_name);
+ 
+     if (homedir == NULL) {
+-- 
+1.8.5.3
+
diff --git a/SOURCES/0105-IPA-Do-not-save-intermediate-data-to-sysdb.patch b/SOURCES/0105-IPA-Do-not-save-intermediate-data-to-sysdb.patch
new file mode 100644
index 0000000..ba7de59
--- /dev/null
+++ b/SOURCES/0105-IPA-Do-not-save-intermediate-data-to-sysdb.patch
@@ -0,0 +1,101 @@
+From 4fc9c7b11aa64a151d19b908ca07d8b16b6657ff Mon Sep 17 00:00:00 2001
+From: Jakub Hrozek <jhrozek@redhat.com>
+Date: Tue, 4 Mar 2014 13:48:36 +0100
+Subject: [PATCH 105/105] IPA: Do not save intermediate data to sysdb
+
+https://fedorahosted.org/sssd/ticket/2264
+
+Reviewed-by: Sumit Bose <sbose@redhat.com>
+---
+ src/providers/ipa/ipa_selinux.c | 68 ++++++++++++++++++++---------------------
+ 1 file changed, 34 insertions(+), 34 deletions(-)
+
+diff --git a/src/providers/ipa/ipa_selinux.c b/src/providers/ipa/ipa_selinux.c
+index c227db937a84228c0f3945dbe11ba904c7ad9744..2209ca188654d8c79ee402ba71beeadab2904093 100644
+--- a/src/providers/ipa/ipa_selinux.c
++++ b/src/providers/ipa/ipa_selinux.c
+@@ -251,6 +251,40 @@ static void ipa_selinux_handler_done(struct tevent_req *req)
+         goto fail;
+     }
+ 
++    ret = sysdb_transaction_start(sysdb);
++    if (ret != EOK) {
++        DEBUG(SSSDBG_CRIT_FAILURE, ("Failed to start transaction\n"));
++        goto fail;
++    }
++    in_transaction = true;
++
++    ret = sysdb_delete_usermaps(op_ctx->domain->sysdb, op_ctx->domain);
++    if (ret != EOK) {
++        DEBUG(SSSDBG_CRIT_FAILURE,
++              ("Cannot delete existing maps from sysdb\n"));
++        goto fail;
++    }
++
++    ret = sysdb_store_selinux_config(sysdb, op_ctx->domain,
++                                     default_user, map_order);
++    if (ret != EOK) {
++        goto fail;
++    }
++
++    if (map_count > 0 && maps != NULL) {
++        ret = ipa_save_user_maps(sysdb, op_ctx->domain, map_count, maps);
++        if (ret != EOK) {
++            goto fail;
++        }
++    }
++
++    ret = sysdb_transaction_commit(sysdb);
++    if (ret != EOK) {
++        DEBUG(SSSDBG_OP_FAILURE, ("Could not commit transaction\n"));
++        goto fail;
++    }
++    in_transaction = false;
++
+     /* Process the maps and return list of best matches (maps with
+      * highest priority). The input maps are also parent memory
+      * context for the output list of best matches. The best match
+@@ -279,40 +313,6 @@ static void ipa_selinux_handler_done(struct tevent_req *req)
+         goto fail;
+     }
+ 
+-    ret = sysdb_transaction_start(sysdb);
+-    if (ret != EOK) {
+-        DEBUG(SSSDBG_CRIT_FAILURE, ("Failed to start transaction\n"));
+-        goto fail;
+-    }
+-    in_transaction = true;
+-
+-    ret = sysdb_delete_usermaps(op_ctx->domain->sysdb, op_ctx->domain);
+-    if (ret != EOK) {
+-        DEBUG(SSSDBG_CRIT_FAILURE,
+-              ("Cannot delete existing maps from sysdb\n"));
+-        goto fail;
+-    }
+-
+-    ret = sysdb_store_selinux_config(sysdb, op_ctx->domain,
+-                                     default_user, map_order);
+-    if (ret != EOK) {
+-        goto fail;
+-    }
+-
+-    if (map_count > 0 && maps != NULL) {
+-        ret = ipa_save_user_maps(sysdb, op_ctx->domain, map_count, maps);
+-        if (ret != EOK) {
+-            goto fail;
+-        }
+-    }
+-
+-    ret = sysdb_transaction_commit(sysdb);
+-    if (ret != EOK) {
+-        DEBUG(SSSDBG_OP_FAILURE, ("Could not commit transaction\n"));
+-        goto fail;
+-    }
+-    in_transaction = false;
+-
+     /* If we got here in online mode, set last_update to current time */
+     if (!be_is_offline(be_ctx)) {
+         op_ctx->selinux_ctx->last_update = time(NULL);
+-- 
+1.8.5.3
+
diff --git a/SOURCES/0106-Fix-krb5-changepw-when-FAST-only-preauth-methods-are.patch b/SOURCES/0106-Fix-krb5-changepw-when-FAST-only-preauth-methods-are.patch
new file mode 100644
index 0000000..5c7b4a8
--- /dev/null
+++ b/SOURCES/0106-Fix-krb5-changepw-when-FAST-only-preauth-methods-are.patch
@@ -0,0 +1,127 @@
+From bbe47ea8ebe6373d0b05181eb27bb65432a9cc97 Mon Sep 17 00:00:00 2001
+From: Nathaniel McCallum <npmccallum@redhat.com>
+Date: Fri, 7 Mar 2014 12:21:11 -0500
+Subject: [PATCH 106/107] Fix krb5 changepw when FAST-only preauth methods are
+ used (like OTP)
+
+Before this patch, a different set of options was used when calling
+krb5_get_init_creds_password() for the changepw principal. Because
+this set of options did not contain the same FAST settings as the
+options for normal requests, all authentication would fail when the
+password of a FAST-only account would expire.
+
+The two sets approach was cargo-cult from kinit where multiple
+requests could be issued using the same options set. However, in the
+case of krb5_child, only one request (or occasionally a well-defined
+second request) will be issued. Two option sets are therefore not
+required.
+
+To fix this problem we removed the second option set used for changepw
+requests. All requests now use a single option set which is modified,
+if needed, for well-defined subsequent requests.
+
+Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
+Reviewed-by: Sumit Bose <sbose@redhat.com>
+---
+ src/providers/krb5/krb5_child.c | 40 ++++++----------------------------------
+ 1 file changed, 6 insertions(+), 34 deletions(-)
+
+diff --git a/src/providers/krb5/krb5_child.c b/src/providers/krb5/krb5_child.c
+index aa29de0cb4e14ea4804ba660b4b8e9b64e9e340e..461a27464f4fea09d4ca430b53aff072b29de141 100644
+--- a/src/providers/krb5/krb5_child.c
++++ b/src/providers/krb5/krb5_child.c
+@@ -65,27 +65,14 @@ struct krb5_req {
+ static krb5_context krb5_error_ctx;
+ #define KRB5_CHILD_DEBUG(level, error) KRB5_DEBUG(level, krb5_error_ctx, error)
+ 
+-static krb5_error_code get_changepw_options(krb5_context ctx,
+-                                            krb5_get_init_creds_opt **_options)
++static void set_changepw_options(krb5_context ctx,
++                                 krb5_get_init_creds_opt *options)
+ {
+-    krb5_get_init_creds_opt *options;
+-    krb5_error_code kerr;
+-
+-    kerr = sss_krb5_get_init_creds_opt_alloc(ctx, &options);
+-    if (kerr != 0) {
+-        KRB5_CHILD_DEBUG(SSSDBG_CRIT_FAILURE, kerr);
+-        return kerr;
+-    }
+-
+     sss_krb5_get_init_creds_opt_set_canonicalize(options, 0);
+     krb5_get_init_creds_opt_set_forwardable(options, 0);
+     krb5_get_init_creds_opt_set_proxiable(options, 0);
+     krb5_get_init_creds_opt_set_renew_life(options, 0);
+     krb5_get_init_creds_opt_set_tkt_life(options, 5*60);
+-
+-    *_options = options;
+-
+-    return 0;
+ }
+ 
+ static errno_t sss_send_pac(krb5_authdata **pac_authdata)
+@@ -1023,7 +1010,6 @@ static errno_t changepw_child(struct krb5_req *kr, bool prelim)
+     krb5_prompter_fct prompter = NULL;
+     const char *realm_name;
+     int realm_length;
+-    krb5_get_init_creds_opt *chagepw_options;
+     size_t msg_len;
+     uint8_t *msg;
+ 
+@@ -1041,12 +1027,7 @@ static errno_t changepw_child(struct krb5_req *kr, bool prelim)
+         prompter = sss_krb5_prompter;
+     }
+ 
+-    kerr = get_changepw_options(kr->ctx, &chagepw_options);
+-    if (kerr != 0) {
+-        DEBUG(SSSDBG_OP_FAILURE, ("get_changepw_options failed.\n"));
+-        return kerr;
+-    }
+-
++    set_changepw_options(kr->ctx, kr->options);
+     sss_krb5_princ_realm(kr->ctx, kr->princ, &realm_name, &realm_length);
+ 
+     DEBUG(SSSDBG_TRACE_FUNC,
+@@ -1055,8 +1036,7 @@ static errno_t changepw_child(struct krb5_req *kr, bool prelim)
+                                         discard_const(password),
+                                         prompter, kr, 0,
+                                         SSSD_KRB5_CHANGEPW_PRINCIPAL,
+-                                        chagepw_options);
+-    sss_krb5_get_init_creds_opt_free(kr->ctx, chagepw_options);
++                                        kr->options);
+     if (kerr != 0) {
+         ret = pack_user_info_chpass_error(kr->pd, "Old password not accepted.",
+                                           &msg_len, &msg);
+@@ -1164,7 +1144,6 @@ static errno_t changepw_child(struct krb5_req *kr, bool prelim)
+ 
+ static errno_t tgt_req_child(struct krb5_req *kr)
+ {
+-    krb5_get_init_creds_opt *chagepw_options;
+     const char *password = NULL;
+     krb5_error_code kerr;
+     int ret;
+@@ -1210,19 +1189,12 @@ static errno_t tgt_req_child(struct krb5_req *kr)
+         DEBUG(1, ("Failed to unset expire callback, continue ...\n"));
+     }
+ 
+-    kerr = get_changepw_options(kr->ctx, &chagepw_options);
+-    if (kerr != 0) {
+-        DEBUG(SSSDBG_OP_FAILURE, ("get_changepw_options failed.\n"));
+-        return kerr;
+-    }
+-
++    set_changepw_options(kr->ctx, kr->options);
+     kerr = krb5_get_init_creds_password(kr->ctx, kr->creds, kr->princ,
+                                         discard_const(password),
+                                         sss_krb5_prompter, kr, 0,
+                                         SSSD_KRB5_CHANGEPW_PRINCIPAL,
+-                                        chagepw_options);
+-
+-    sss_krb5_get_init_creds_opt_free(kr->ctx, chagepw_options);
++                                        kr->options);
+ 
+     krb5_free_cred_contents(kr->ctx, kr->creds);
+     if (kerr == 0) {
+-- 
+1.8.5.3
+
diff --git a/SOURCES/0107-IPA-Use-GC-for-AD-initgroup-requests.patch b/SOURCES/0107-IPA-Use-GC-for-AD-initgroup-requests.patch
new file mode 100644
index 0000000..f795e50
--- /dev/null
+++ b/SOURCES/0107-IPA-Use-GC-for-AD-initgroup-requests.patch
@@ -0,0 +1,46 @@
+From d34211137c7e70563b073b83d773ae18688efbbc Mon Sep 17 00:00:00 2001
+From: Sumit Bose <sbose@redhat.com>
+Date: Thu, 6 Mar 2014 15:37:57 +0100
+Subject: [PATCH 107/107] IPA: Use GC for AD initgroup requests
+
+Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
+---
+ src/providers/ipa/ipa_subdomains_id.c | 21 +++++++++++++++------
+ 1 file changed, 15 insertions(+), 6 deletions(-)
+
+diff --git a/src/providers/ipa/ipa_subdomains_id.c b/src/providers/ipa/ipa_subdomains_id.c
+index 00993c496c1d100b37a780828c81492c2fac6157..978ccc261d7525662e835b867044b6a5238a29df 100644
+--- a/src/providers/ipa/ipa_subdomains_id.c
++++ b/src/providers/ipa/ipa_subdomains_id.c
+@@ -307,13 +307,22 @@ ipa_get_ad_acct_send(TALLOC_CTX *mem_ctx,
+     /* Currently only LDAP port for AD is used because POSIX
+      * attributes are not replicated to GC by default
+      */
+-    clist = talloc_zero_array(req, struct sdap_id_conn_ctx *, 2);
+-    if (clist == NULL) {
+-        ret = ENOMEM;
+-        goto fail;
++
++    if ((state->ar->entry_type & BE_REQ_TYPE_MASK) == BE_REQ_INITGROUPS) {
++        clist = ad_gc_conn_list(req, ad_id_ctx, state->user_dom);
++        if (clist == NULL) {
++            ret = ENOMEM;
++            goto fail;
++        }
++    } else {
++        clist = talloc_zero_array(req, struct sdap_id_conn_ctx *, 2);
++        if (clist == NULL) {
++            ret = ENOMEM;
++            goto fail;
++        }
++        clist[0] = ad_id_ctx->ldap_ctx;
++        clist[1] = NULL;
+     }
+-    clist[0] = ad_id_ctx->ldap_ctx;
+-    clist[1] = NULL;
+ 
+     /* Now we already need ad_id_ctx in particular sdap_id_conn_ctx */
+     sdom = sdap_domain_get(sdap_id_ctx->opts, state->user_dom);
+-- 
+1.8.5.3
+
diff --git a/SOURCES/0108-AD-Only-connect-to-GC-for-subdomain-users.patch b/SOURCES/0108-AD-Only-connect-to-GC-for-subdomain-users.patch
new file mode 100644
index 0000000..26f9aea
--- /dev/null
+++ b/SOURCES/0108-AD-Only-connect-to-GC-for-subdomain-users.patch
@@ -0,0 +1,57 @@
+From e62c422753537d8e2b98e979553626850b7b7600 Mon Sep 17 00:00:00 2001
+From: Jakub Hrozek <jhrozek@redhat.com>
+Date: Wed, 5 Mar 2014 11:50:54 +0100
+Subject: [PATCH 108/110] AD: Only connect to GC for subdomain users
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+https://fedorahosted.org/sssd/ticket/2251
+
+By connecting to GC for users from both trusted domains and parent
+domain, we lose the ability to download the shell and homedir if these
+are used with ID mapping.
+
+This patch changes the user lookups only. Changing the logic for all
+lookups would break cross-domain group memberships, for example.
+
+Reviewed-by: Pavel Březina <pbrezina@redhat.com>
+(cherry picked from commit bb8a08118db0916bf8252a9481c16271ec20acd3)
+---
+ src/providers/ad/ad_id.c | 17 +++++++++++++++++
+ 1 file changed, 17 insertions(+)
+
+diff --git a/src/providers/ad/ad_id.c b/src/providers/ad/ad_id.c
+index 87af656b364344a8ef27a444e5dfcf8848939110..a35823b4b77d42fc583a61653a175f0ee4d22ac4 100644
+--- a/src/providers/ad/ad_id.c
++++ b/src/providers/ad/ad_id.c
+@@ -215,9 +215,26 @@ get_conn_list(struct be_req *breq, struct ad_id_ctx *ad_ctx,
+               struct sss_domain_info *dom, struct be_acct_req *ar)
+ {
+     struct sdap_id_conn_ctx **clist;
++    int cindex = 0;
+ 
+     switch (ar->entry_type & BE_REQ_TYPE_MASK) {
+     case BE_REQ_USER: /* user */
++        clist = talloc_zero_array(ad_ctx, struct sdap_id_conn_ctx *, 3);
++        if (clist == NULL) return NULL;
++
++        /* Try GC first for users from trusted domains */
++        if (dp_opt_get_bool(ad_ctx->ad_options->basic, AD_ENABLE_GC)
++                && IS_SUBDOMAIN(dom)) {
++            clist[cindex] = ad_ctx->gc_ctx;
++            clist[cindex]->ignore_mark_offline = true;
++            cindex++;
++        }
++
++        /* Users from primary domain can be just downloaded from LDAP.
++         * The domain's LDAP connection also works as a fallback
++         */
++        clist[cindex] = ad_get_dom_ldap_conn(ad_ctx, dom);
++        break;
+     case BE_REQ_BY_SECID:   /* by SID */
+     case BE_REQ_USER_AND_GROUP: /* get SID */
+     case BE_REQ_GROUP: /* group */
+-- 
+1.8.5.3
+
diff --git a/SOURCES/0109-MAN-Clarify-the-GC-support-a-bit.patch b/SOURCES/0109-MAN-Clarify-the-GC-support-a-bit.patch
new file mode 100644
index 0000000..8269e74
--- /dev/null
+++ b/SOURCES/0109-MAN-Clarify-the-GC-support-a-bit.patch
@@ -0,0 +1,51 @@
+From b9336c0c96d409ecd7371a55fbfcf5691814efec Mon Sep 17 00:00:00 2001
+From: Jakub Hrozek <jhrozek@redhat.com>
+Date: Wed, 5 Mar 2014 12:13:48 +0100
+Subject: [PATCH 109/110] MAN: Clarify the GC support a bit
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+It should be noted that disabling GC does *not* disable lookups from
+trusted domains. Disabling GC might be a a good way for admins who wish
+to use POSIX attributes in trusted domains and the man page should hint
+this option.
+
+Reviewed-by: Pavel Březina <pbrezina@redhat.com>
+(cherry picked from commit fdaaf2525e333af04ee9b48429b6766b5fd6cab6)
+---
+ src/man/sssd-ad.5.xml | 18 +++++++++++++-----
+ 1 file changed, 13 insertions(+), 5 deletions(-)
+
+diff --git a/src/man/sssd-ad.5.xml b/src/man/sssd-ad.5.xml
+index 8cd94d4aeaf553ecb54e0e4c866be5fb7a44fa8e..0554317f533f2309d9fad60dfe5543f8546a6bbc 100644
+--- a/src/man/sssd-ad.5.xml
++++ b/src/man/sssd-ad.5.xml
+@@ -232,11 +232,19 @@ FOREST:EXAMPLE.COM:(memberOf=cn=admins,ou=groups,dc=example,dc=com)
+                     <listitem>
+                         <para>
+                             By default, the SSSD connects to the Global
+-                            Catalog first to retrieve users and uses the
+-                            LDAP port to retrieve group memberships or
+-                            as a fallback. Disabling this option makes
+-                            the SSSD only connect to the LDAP port of the
+-                            current AD server.
++                            Catalog first to retrieve users from trusted
++                            domains and uses the LDAP port to retrieve
++                            group memberships or as a fallback. Disabling
++                            this option makes the SSSD only connect to
++                            the LDAP port of the current AD server.
++                        </para>
++                        <para>
++                            Please note that disabling Global Catalog support
++                            does not disable retrieving users from trusted
++                            domains. The SSSD would connect to the LDAP port
++                            of trusted domains instead. However, Global
++                            Catalog must be used in order to resolve
++                            cross-domain group memberships.
+                         </para>
+                         <para>
+                             Default: true
+-- 
+1.8.5.3
+
diff --git a/SOURCES/0110-IPA-Use-the-correct-domain-when-processing-SELinux-r.patch b/SOURCES/0110-IPA-Use-the-correct-domain-when-processing-SELinux-r.patch
new file mode 100644
index 0000000..a5644b8
--- /dev/null
+++ b/SOURCES/0110-IPA-Use-the-correct-domain-when-processing-SELinux-r.patch
@@ -0,0 +1,121 @@
+From 83eedf41e97e3fae59d92c0331cb3d1dc62a9010 Mon Sep 17 00:00:00 2001
+From: Jakub Hrozek <jhrozek@redhat.com>
+Date: Wed, 5 Mar 2014 16:35:00 +0100
+Subject: [PATCH 110/110] IPA: Use the correct domain when processing SELinux
+ rules
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+We blindly used the user's domain for everything. That wrong in case the
+user comes from a subdomain. We should use the IPA domain for accessing
+the SELinux rules and host data and the user domain only for the user.
+
+https://fedorahosted.org/sssd/ticket/2270
+
+Reviewed-by: Pavel Březina <pbrezina@redhat.com>
+(cherry picked from commit 36f606d6743e77721bedeed0907f1be7a19fa4f4)
+---
+ src/providers/ipa/ipa_selinux.c | 26 ++++++++++++++++----------
+ 1 file changed, 16 insertions(+), 10 deletions(-)
+
+diff --git a/src/providers/ipa/ipa_selinux.c b/src/providers/ipa/ipa_selinux.c
+index 2209ca188654d8c79ee402ba71beeadab2904093..4ec5a64159de139f9ba5b30bf1f1a56baf32a52f 100644
+--- a/src/providers/ipa/ipa_selinux.c
++++ b/src/providers/ipa/ipa_selinux.c
+@@ -57,7 +57,8 @@ static errno_t ipa_get_selinux_recv(struct tevent_req *req,
+ 
+ static struct ipa_selinux_op_ctx *
+ ipa_selinux_create_op_ctx(TALLOC_CTX *mem_ctx, struct sysdb_ctx *sysdb,
+-                          struct sss_domain_info *domain,
++                          struct sss_domain_info *ipa_domain,
++                          struct sss_domain_info *user_domain,
+                           struct be_req *be_req, const char *username,
+                           const char *hostname,
+                           struct ipa_selinux_ctx *selinux_ctx);
+@@ -80,7 +81,8 @@ static errno_t ipa_selinux_process_maps(TALLOC_CTX *mem_ctx,
+ 
+ struct ipa_selinux_op_ctx {
+     struct be_req *be_req;
+-    struct sss_domain_info *domain;
++    struct sss_domain_info *user_domain;
++    struct sss_domain_info *ipa_domain;
+     struct ipa_selinux_ctx *selinux_ctx;
+ 
+     struct sysdb_attrs *user;
+@@ -131,6 +133,7 @@ void ipa_selinux_handler(struct be_req *be_req)
+     }
+ 
+     op_ctx = ipa_selinux_create_op_ctx(be_req, user_domain->sysdb,
++                                       be_ctx->domain,
+                                        user_domain,
+                                        be_req, pd->user, hostname,
+                                        selinux_ctx);
+@@ -155,7 +158,8 @@ fail:
+ 
+ static struct ipa_selinux_op_ctx *
+ ipa_selinux_create_op_ctx(TALLOC_CTX *mem_ctx, struct sysdb_ctx *sysdb,
+-                          struct sss_domain_info *domain,
++                          struct sss_domain_info *ipa_domain,
++                          struct sss_domain_info *user_domain,
+                           struct be_req *be_req, const char *username,
+                           const char *hostname,
+                           struct ipa_selinux_ctx *selinux_ctx)
+@@ -175,15 +179,16 @@ ipa_selinux_create_op_ctx(TALLOC_CTX *mem_ctx, struct sysdb_ctx *sysdb,
+         return NULL;
+     }
+     op_ctx->be_req = be_req;
+-    op_ctx->domain = domain;
++    op_ctx->ipa_domain = ipa_domain;
++    op_ctx->user_domain = user_domain;
+     op_ctx->selinux_ctx = selinux_ctx;
+ 
+-    ret = sss_selinux_extract_user(op_ctx, sysdb, domain, username, &op_ctx->user);
++    ret = sss_selinux_extract_user(op_ctx, sysdb, user_domain, username, &op_ctx->user);
+     if (ret != EOK) {
+         goto fail;
+     }
+ 
+-    host_dn = sysdb_custom_dn(sysdb, op_ctx, domain, hostname, HBAC_HOSTS_SUBDIR);
++    host_dn = sysdb_custom_dn(sysdb, op_ctx, ipa_domain, hostname, HBAC_HOSTS_SUBDIR);
+     if (host_dn == NULL) {
+         goto fail;
+     }
+@@ -229,7 +234,7 @@ static void ipa_selinux_handler_done(struct tevent_req *req)
+     struct ipa_selinux_op_ctx *op_ctx = tevent_req_callback_data(req, struct ipa_selinux_op_ctx);
+     struct be_req *breq = op_ctx->be_req;
+     struct be_ctx *be_ctx = be_req_get_be_ctx(breq);
+-    struct sysdb_ctx *sysdb = op_ctx->domain->sysdb;
++    struct sysdb_ctx *sysdb = op_ctx->ipa_domain->sysdb;
+     errno_t ret, sret;
+     size_t map_count = 0;
+     struct sysdb_attrs **maps = NULL;
+@@ -258,21 +263,22 @@ static void ipa_selinux_handler_done(struct tevent_req *req)
+     }
+     in_transaction = true;
+ 
+-    ret = sysdb_delete_usermaps(op_ctx->domain->sysdb, op_ctx->domain);
++    ret = sysdb_delete_usermaps(op_ctx->ipa_domain->sysdb, op_ctx->ipa_domain);
+     if (ret != EOK) {
+         DEBUG(SSSDBG_CRIT_FAILURE,
+               ("Cannot delete existing maps from sysdb\n"));
+         goto fail;
+     }
+ 
+-    ret = sysdb_store_selinux_config(sysdb, op_ctx->domain,
++    ret = sysdb_store_selinux_config(op_ctx->ipa_domain->sysdb,
++                                     op_ctx->ipa_domain,
+                                      default_user, map_order);
+     if (ret != EOK) {
+         goto fail;
+     }
+ 
+     if (map_count > 0 && maps != NULL) {
+-        ret = ipa_save_user_maps(sysdb, op_ctx->domain, map_count, maps);
++        ret = ipa_save_user_maps(sysdb, op_ctx->ipa_domain, map_count, maps);
+         if (ret != EOK) {
+             goto fail;
+         }
+-- 
+1.8.5.3
+
diff --git a/SOURCES/0111-IPA-KRB5-handle-KRB5_PROG_ETYPE_NOSUPP-during-IPA-pa.patch b/SOURCES/0111-IPA-KRB5-handle-KRB5_PROG_ETYPE_NOSUPP-during-IPA-pa.patch
new file mode 100644
index 0000000..edef6ea
--- /dev/null
+++ b/SOURCES/0111-IPA-KRB5-handle-KRB5_PROG_ETYPE_NOSUPP-during-IPA-pa.patch
@@ -0,0 +1,32 @@
+From f8a49b3bff8d3969824fc7ba4e90d229f0c4edea Mon Sep 17 00:00:00 2001
+From: Sumit Bose <sbose@redhat.com>
+Date: Tue, 11 Mar 2014 13:16:14 +0100
+Subject: [PATCH 111/111] IPA/KRB5: handle KRB5_PROG_ETYPE_NOSUPP during IPA
+ password migration
+
+Fixes https://fedorahosted.org/sssd/ticket/2279
+
+Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
+(cherry picked from commit 63bf0b7697d5a51b5338070d0e2652d49a4728ce)
+---
+ src/providers/krb5/krb5_child.c | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+diff --git a/src/providers/krb5/krb5_child.c b/src/providers/krb5/krb5_child.c
+index 461a27464f4fea09d4ca430b53aff072b29de141..af303e6c8c507c7cef108027c49cc4adb74162e7 100644
+--- a/src/providers/krb5/krb5_child.c
++++ b/src/providers/krb5/krb5_child.c
+@@ -986,6 +986,10 @@ static errno_t map_krb5_error(krb5_error_code kerr)
+     case KRB5KRB_AP_ERR_BAD_INTEGRITY:
+         return ERR_AUTH_FAILED;
+ 
++    /* ERR_CREDS_INVALID is used to indicate to the IPA provider that trying
++     * password migration would make sense. All Kerberos error codes which can
++     * be seen while migrating LDAP users to IPA should be added here. */
++    case KRB5_PROG_ETYPE_NOSUPP:
+     case KRB5_PREAUTH_FAILED:
+     case KRB5KDC_ERR_PREAUTH_FAILED:
+         return ERR_CREDS_INVALID;
+-- 
+1.8.5.3
+
diff --git a/SOURCES/0112-AD-Continue-if-sssd-failes-to-check-extra-members.patch b/SOURCES/0112-AD-Continue-if-sssd-failes-to-check-extra-members.patch
new file mode 100644
index 0000000..25f4e5c
--- /dev/null
+++ b/SOURCES/0112-AD-Continue-if-sssd-failes-to-check-extra-members.patch
@@ -0,0 +1,35 @@
+From 2ac5fed1ea4e9f56a18d0cc3b445855cdc6757c2 Mon Sep 17 00:00:00 2001
+From: Lukas Slebodnik <lslebodn@redhat.com>
+Date: Wed, 12 Mar 2014 17:38:22 +0100
+Subject: [PATCH 112/113] AD: Continue if sssd failes to check extra members
+
+Reported by scan-build
+
+    for (mi = 0; group_only[mi]; mi++) {
+                 ^~~~~~~~~~
+warning: Array access (from variable 'group_only') results in a null pointer
+dereference
+
+It can happend if function ad_group_extra_members fails (ret != EOK)
+
+Reviewed-by: Simo Sorce <simo@redhat.com>
+(cherry picked from commit bad65473c4c28ecbf2b6bd374a7ae2d634d57d8d)
+---
+ src/providers/ad/ad_id.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/src/providers/ad/ad_id.c b/src/providers/ad/ad_id.c
+index a35823b4b77d42fc583a61653a175f0ee4d22ac4..01d18d7ae4bee82d8b75c7103c0f07635c6e08cc 100644
+--- a/src/providers/ad/ad_id.c
++++ b/src/providers/ad/ad_id.c
+@@ -772,6 +772,7 @@ ad_enum_cross_dom_members(struct sdap_options *opts,
+         ret = ad_group_extra_members(tmp_ctx, msgs[i], dom, &group_only);
+         if (ret != EOK) {
+             DEBUG(SSSDBG_OP_FAILURE, ("Failed to check extra members\n"));
++            continue;
+         } else if (group_only == NULL) {
+             DEBUG(SSSDBG_TRACE_INTERNAL, ("No extra members\n"));
+             continue;
+-- 
+1.8.5.3
+
diff --git a/SOURCES/0113-IPA-Write-SELinux-usernames-in-the-right-case.patch b/SOURCES/0113-IPA-Write-SELinux-usernames-in-the-right-case.patch
new file mode 100644
index 0000000..c09551c
--- /dev/null
+++ b/SOURCES/0113-IPA-Write-SELinux-usernames-in-the-right-case.patch
@@ -0,0 +1,103 @@
+From 12fa74c860993f154d1eb1585b4a735ca3684565 Mon Sep 17 00:00:00 2001
+From: Jakub Hrozek <jhrozek@redhat.com>
+Date: Wed, 12 Mar 2014 15:19:02 +0100
+Subject: [PATCH 113/113] IPA: Write SELinux usernames in the right case
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+https://fedorahosted.org/sssd/ticket/2282
+
+Reviewed-by: Michal Židek <mzidek@redhat.com>
+---
+ src/providers/ipa/ipa_selinux.c | 26 +++++++++++++++++++++-----
+ 1 file changed, 21 insertions(+), 5 deletions(-)
+
+diff --git a/src/providers/ipa/ipa_selinux.c b/src/providers/ipa/ipa_selinux.c
+index 4ec5a64159de139f9ba5b30bf1f1a56baf32a52f..7f59161918a04ff8c994a0ce0fe55924ff09eda7 100644
+--- a/src/providers/ipa/ipa_selinux.c
++++ b/src/providers/ipa/ipa_selinux.c
+@@ -225,6 +225,7 @@ static errno_t create_order_array(TALLOC_CTX *mem_ctx, const char *map_order,
+                                   char ***_order_array, size_t *_order_count);
+ static errno_t choose_best_seuser(struct sysdb_attrs **usermaps,
+                                   struct pam_data *pd,
++                                  struct sss_domain_info *user_domain,
+                                   char **order_array, int order_count,
+                                   const char *default_user);
+ 
+@@ -311,8 +312,8 @@ static void ipa_selinux_handler_done(struct tevent_req *req)
+         goto fail;
+     }
+ 
+-    ret = choose_best_seuser(best_match_maps, pd, order_array, order_count,
+-                             default_user);
++    ret = choose_best_seuser(best_match_maps, pd, op_ctx->user_domain,
++                             order_array, order_count, default_user);
+     if (ret != EOK) {
+         DEBUG(SSSDBG_CRIT_FAILURE,
+               ("Failed to evaluate ordered SELinux users array.\n"));
+@@ -601,13 +602,16 @@ done:
+     return ret;
+ }
+ 
+-static errno_t write_selinux_login_file(const char *username, char *string);
++static errno_t write_selinux_login_file(const char *orig_name,
++                                        struct sss_domain_info *dom,
++                                        char *string);
+ static errno_t remove_selinux_login_file(const char *username);
+ 
+ /* Choose best selinux user based on given order and write
+  * the user to selinux login file. */
+ static errno_t choose_best_seuser(struct sysdb_attrs **usermaps,
+                                   struct pam_data *pd,
++                                  struct sss_domain_info *user_domain,
+                                   char **order_array, int order_count,
+                                   const char *default_user)
+ {
+@@ -662,7 +666,7 @@ static errno_t choose_best_seuser(struct sysdb_attrs **usermaps,
+         }
+     }
+ 
+-    ret = write_selinux_login_file(pd->user, file_content);
++    ret = write_selinux_login_file(pd->user, user_domain, file_content);
+ done:
+     if (!file_content) {
+         err = remove_selinux_login_file(pd->user);
+@@ -673,7 +677,9 @@ done:
+     return ret;
+ }
+ 
+-static errno_t write_selinux_login_file(const char *username, char *string)
++static errno_t write_selinux_login_file(const char *orig_name,
++                                        struct sss_domain_info *dom,
++                                        char *string)
+ {
+     char *path = NULL;
+     char *tmp_path = NULL;
+@@ -685,6 +691,7 @@ static errno_t write_selinux_login_file(const char *username, char *string)
+     char *full_string = NULL;
+     int enforce;
+     errno_t ret = EOK;
++    const char *username;
+ 
+     len = strlen(string);
+     if (len == 0) {
+@@ -697,6 +704,15 @@ static errno_t write_selinux_login_file(const char *username, char *string)
+         return ENOMEM;
+     }
+ 
++    /* pam_selinux needs the username in the same format getpwnam() would
++     * return it
++     */
++    username = sss_get_cased_name(tmp_ctx, orig_name, dom->case_sensitive);
++    if (username == NULL) {
++        ret = ENOMEM;
++        goto done;
++    }
++
+     path = selogin_path(tmp_ctx, username);
+     if (path == NULL) {
+         ret = ENOMEM;
+-- 
+1.8.5.3
+
diff --git a/SOURCES/0114-krb5_child-remove-unused-option-lifetime_str-from-k5.patch b/SOURCES/0114-krb5_child-remove-unused-option-lifetime_str-from-k5.patch
new file mode 100644
index 0000000..4aeb7a6
--- /dev/null
+++ b/SOURCES/0114-krb5_child-remove-unused-option-lifetime_str-from-k5.patch
@@ -0,0 +1,49 @@
+From 4069c662c32836f8ebba7f091c44f9db2a1ef62e Mon Sep 17 00:00:00 2001
+From: Sumit Bose <sbose@redhat.com>
+Date: Thu, 20 Mar 2014 18:39:48 +0100
+Subject: [PATCH 114/117] krb5_child: remove unused option lifetime_str from
+ k5c_setup_fast()
+
+Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
+---
+ src/providers/krb5/krb5_child.c | 9 +++------
+ 1 file changed, 3 insertions(+), 6 deletions(-)
+
+diff --git a/src/providers/krb5/krb5_child.c b/src/providers/krb5/krb5_child.c
+index af303e6c8c507c7cef108027c49cc4adb74162e7..7f07efc161d0242e64bd67e13dec9a3faa9f2e30 100644
+--- a/src/providers/krb5/krb5_child.c
++++ b/src/providers/krb5/krb5_child.c
+@@ -1666,7 +1666,7 @@ static errno_t k5c_recv_data(struct krb5_req *kr, int fd, uint32_t *offline)
+     return ret;
+ }
+ 
+-static int k5c_setup_fast(struct krb5_req *kr, char *lifetime_str, bool demand)
++static int k5c_setup_fast(struct krb5_req *kr, bool demand)
+ {
+     krb5_principal fast_princ_struct;
+     krb5_data *realm_data;
+@@ -1675,9 +1675,6 @@ static int k5c_setup_fast(struct krb5_req *kr, char *lifetime_str, bool demand)
+     krb5_error_code kerr;
+     char *tmp_str;
+ 
+-    DEBUG(SSSDBG_CONF_SETTINGS, ("%s is set to [%s]\n",
+-                                 SSSD_KRB5_LIFETIME, lifetime_str));
+-
+     tmp_str = getenv(SSSD_KRB5_FAST_PRINCIPAL);
+     if (tmp_str) {
+         DEBUG(SSSDBG_CONF_SETTINGS, ("%s is set to [%s]\n",
+@@ -1869,9 +1866,9 @@ static int k5c_setup(struct krb5_req *kr, uint32_t offline)
+         if (use_fast_str == NULL || strcasecmp(use_fast_str, "never") == 0) {
+             DEBUG(SSSDBG_CONF_SETTINGS, ("Not using FAST.\n"));
+         } else if (strcasecmp(use_fast_str, "try") == 0) {
+-            kerr = k5c_setup_fast(kr, lifetime_str, false);
++            kerr = k5c_setup_fast(kr, false);
+         } else if (strcasecmp(use_fast_str, "demand") == 0) {
+-            kerr = k5c_setup_fast(kr, lifetime_str, true);
++            kerr = k5c_setup_fast(kr, true);
+         } else {
+             DEBUG(SSSDBG_CRIT_FAILURE,
+                   ("Unsupported value [%s] for krb5_use_fast.\n",
+-- 
+1.8.5.3
+
diff --git a/SOURCES/0115-krb5-child-extract-lifetime-settings-into-set_lifeti.patch b/SOURCES/0115-krb5-child-extract-lifetime-settings-into-set_lifeti.patch
new file mode 100644
index 0000000..c9502fa
--- /dev/null
+++ b/SOURCES/0115-krb5-child-extract-lifetime-settings-into-set_lifeti.patch
@@ -0,0 +1,133 @@
+From b86041a3760faa9273f1df879e8bfa38fbbb84aa Mon Sep 17 00:00:00 2001
+From: Sumit Bose <sbose@redhat.com>
+Date: Fri, 21 Mar 2014 12:14:11 +0100
+Subject: [PATCH 115/117] krb5-child: extract lifetime settings into
+ set_lifetime_options()
+
+Additionally the lifetime option flags are unset if there are no
+explicit settings to make sure the defaults from krb5.conf are used even
+if other values were set manually in between.
+
+Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
+---
+ src/providers/krb5/krb5_child.c | 89 +++++++++++++++++++++++++----------------
+ 1 file changed, 55 insertions(+), 34 deletions(-)
+
+diff --git a/src/providers/krb5/krb5_child.c b/src/providers/krb5/krb5_child.c
+index 7f07efc161d0242e64bd67e13dec9a3faa9f2e30..7ea111e108e189c6839feec0f1108175c0291605 100644
+--- a/src/providers/krb5/krb5_child.c
++++ b/src/providers/krb5/krb5_child.c
+@@ -65,6 +65,57 @@ struct krb5_req {
+ static krb5_context krb5_error_ctx;
+ #define KRB5_CHILD_DEBUG(level, error) KRB5_DEBUG(level, krb5_error_ctx, error)
+ 
++static krb5_error_code set_lifetime_options(krb5_get_init_creds_opt *options)
++{
++    char *lifetime_str;
++    krb5_error_code kerr;
++    krb5_deltat lifetime;
++
++    lifetime_str = getenv(SSSD_KRB5_RENEWABLE_LIFETIME);
++    if (lifetime_str == NULL) {
++        DEBUG(SSSDBG_CONF_SETTINGS, ("Cannot read [%s] from environment.\n",
++              SSSD_KRB5_RENEWABLE_LIFETIME));
++
++        /* Unset option flag to make sure defaults from krb5.conf are used. */
++        options->flags &= ~(KRB5_GET_INIT_CREDS_OPT_RENEW_LIFE);
++    } else {
++        kerr = krb5_string_to_deltat(lifetime_str, &lifetime);
++        if (kerr != 0) {
++            DEBUG(SSSDBG_CRIT_FAILURE,
++                  ("krb5_string_to_deltat failed for [%s].\n",
++                      lifetime_str));
++            KRB5_CHILD_DEBUG(SSSDBG_CRIT_FAILURE, kerr);
++            return kerr;
++        }
++        DEBUG(SSSDBG_CONF_SETTINGS, ("%s is set to [%s]\n",
++              SSSD_KRB5_RENEWABLE_LIFETIME, lifetime_str));
++        krb5_get_init_creds_opt_set_renew_life(options, lifetime);
++    }
++
++    lifetime_str = getenv(SSSD_KRB5_LIFETIME);
++    if (lifetime_str == NULL) {
++        DEBUG(SSSDBG_CONF_SETTINGS, ("Cannot read [%s] from environment.\n",
++              SSSD_KRB5_LIFETIME));
++
++        /* Unset option flag to make sure defaults from krb5.conf are used. */
++        options->flags &= ~(KRB5_GET_INIT_CREDS_OPT_TKT_LIFE);
++    } else {
++        kerr = krb5_string_to_deltat(lifetime_str, &lifetime);
++        if (kerr != 0) {
++            DEBUG(SSSDBG_CRIT_FAILURE,
++                  ("krb5_string_to_deltat failed for [%s].\n",
++                      lifetime_str));
++            KRB5_CHILD_DEBUG(SSSDBG_CRIT_FAILURE, kerr);
++            return kerr;
++        }
++        DEBUG(SSSDBG_CONF_SETTINGS,
++              ("%s is set to [%s]\n", SSSD_KRB5_LIFETIME, lifetime_str));
++        krb5_get_init_creds_opt_set_tkt_life(options, lifetime);
++    }
++
++    return 0;
++}
++
+ static void set_changepw_options(krb5_context ctx,
+                                  krb5_get_init_creds_opt *options)
+ {
+@@ -1744,9 +1795,7 @@ static int k5c_setup_fast(struct krb5_req *kr, bool demand)
+ static int k5c_setup(struct krb5_req *kr, uint32_t offline)
+ {
+     krb5_error_code kerr;
+-    char *lifetime_str;
+     char *use_fast_str;
+-    krb5_deltat lifetime;
+     int parse_flags;
+ 
+     kr->realm = getenv(SSSD_KRB5_REALM);
+@@ -1825,38 +1874,10 @@ static int k5c_setup(struct krb5_req *kr, uint32_t offline)
+     krb5_get_init_creds_opt_set_change_password_prompt(kr->options, 0);
+ #endif
+ 
+-    lifetime_str = getenv(SSSD_KRB5_RENEWABLE_LIFETIME);
+-    if (lifetime_str == NULL) {
+-        DEBUG(SSSDBG_CONF_SETTINGS, ("Cannot read [%s] from environment.\n",
+-              SSSD_KRB5_RENEWABLE_LIFETIME));
+-    } else {
+-        kerr = krb5_string_to_deltat(lifetime_str, &lifetime);
+-        if (kerr != 0) {
+-            DEBUG(1, ("krb5_string_to_deltat failed for [%s].\n",
+-                      lifetime_str));
+-            KRB5_CHILD_DEBUG(SSSDBG_CRIT_FAILURE, kerr);
+-            return kerr;
+-        }
+-        DEBUG(SSSDBG_CONF_SETTINGS, ("%s is set to [%s]\n",
+-              SSSD_KRB5_RENEWABLE_LIFETIME, lifetime_str));
+-        krb5_get_init_creds_opt_set_renew_life(kr->options, lifetime);
+-    }
+-
+-    lifetime_str = getenv(SSSD_KRB5_LIFETIME);
+-    if (lifetime_str == NULL) {
+-        DEBUG(SSSDBG_CONF_SETTINGS, ("Cannot read [%s] from environment.\n",
+-              SSSD_KRB5_LIFETIME));
+-    } else {
+-        kerr = krb5_string_to_deltat(lifetime_str, &lifetime);
+-        if (kerr != 0) {
+-            DEBUG(1, ("krb5_string_to_deltat failed for [%s].\n",
+-                      lifetime_str));
+-            KRB5_CHILD_DEBUG(SSSDBG_CRIT_FAILURE, kerr);
+-            return kerr;
+-        }
+-        DEBUG(SSSDBG_CONF_SETTINGS,
+-              ("%s is set to [%s]\n", SSSD_KRB5_LIFETIME, lifetime_str));
+-        krb5_get_init_creds_opt_set_tkt_life(kr->options, lifetime);
++    kerr = set_lifetime_options(kr->options);
++    if (kerr != 0) {
++        DEBUG(SSSDBG_OP_FAILURE, ("set_lifetime_options failed.\n"));
++        return kerr;
+     }
+ 
+     if (!offline) {
+-- 
+1.8.5.3
+
diff --git a/SOURCES/0116-krb5_client-rename-krb5_set_canonicalize-to-set_cano.patch b/SOURCES/0116-krb5_client-rename-krb5_set_canonicalize-to-set_cano.patch
new file mode 100644
index 0000000..96a23b3
--- /dev/null
+++ b/SOURCES/0116-krb5_client-rename-krb5_set_canonicalize-to-set_cano.patch
@@ -0,0 +1,78 @@
+From 7dbdbb87a5660128b0bbac011c3684e13380f162 Mon Sep 17 00:00:00 2001
+From: Sumit Bose <sbose@redhat.com>
+Date: Fri, 21 Mar 2014 16:15:39 +0100
+Subject: [PATCH 116/117] krb5_client: rename krb5_set_canonicalize() to
+ set_canonicalize_option()
+
+Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
+---
+ src/providers/krb5/krb5_child.c | 32 ++++++++++++++++----------------
+ 1 file changed, 16 insertions(+), 16 deletions(-)
+
+diff --git a/src/providers/krb5/krb5_child.c b/src/providers/krb5/krb5_child.c
+index 7ea111e108e189c6839feec0f1108175c0291605..674dad7387c6874209ee5e491cefc18eba4a6da5 100644
+--- a/src/providers/krb5/krb5_child.c
++++ b/src/providers/krb5/krb5_child.c
+@@ -116,6 +116,20 @@ static krb5_error_code set_lifetime_options(krb5_get_init_creds_opt *options)
+     return 0;
+ }
+ 
++static void set_canonicalize_option(krb5_get_init_creds_opt *opts)
++{
++    int canonicalize = 0;
++    char *tmp_str;
++
++    tmp_str = getenv(SSSD_KRB5_CANONICALIZE);
++    if (tmp_str != NULL && strcasecmp(tmp_str, "true") == 0) {
++        canonicalize = 1;
++    }
++    DEBUG(SSSDBG_CONF_SETTINGS, ("%s is set to [%s]\n",
++          SSSD_KRB5_CANONICALIZE, tmp_str ? tmp_str : "not set"));
++    sss_krb5_get_init_creds_opt_set_canonicalize(opts, canonicalize);
++}
++
+ static void set_changepw_options(krb5_context ctx,
+                                  krb5_get_init_creds_opt *options)
+ {
+@@ -875,20 +889,6 @@ done:
+ 
+ }
+ 
+-static void krb5_set_canonicalize(krb5_get_init_creds_opt *opts)
+-{
+-    int canonicalize = 0;
+-    char *tmp_str;
+-
+-    tmp_str = getenv(SSSD_KRB5_CANONICALIZE);
+-    if (tmp_str != NULL && strcasecmp(tmp_str, "true") == 0) {
+-        canonicalize = 1;
+-    }
+-    DEBUG(SSSDBG_CONF_SETTINGS, ("%s is set to [%s]\n",
+-          SSSD_KRB5_CANONICALIZE, tmp_str ? tmp_str : "not set"));
+-    sss_krb5_get_init_creds_opt_set_canonicalize(opts, canonicalize);
+-}
+-
+ static krb5_error_code get_and_save_tgt_with_keytab(krb5_context ctx,
+                                                     krb5_principal princ,
+                                                     krb5_keytab keytab,
+@@ -904,7 +904,7 @@ static krb5_error_code get_and_save_tgt_with_keytab(krb5_context ctx,
+     krb5_get_init_creds_opt_set_address_list(&options, NULL);
+     krb5_get_init_creds_opt_set_forwardable(&options, 0);
+     krb5_get_init_creds_opt_set_proxiable(&options, 0);
+-    krb5_set_canonicalize(&options);
++    set_canonicalize_option(&options);
+ 
+     kerr = krb5_get_init_creds_keytab(ctx, &creds, princ, keytab, 0, NULL,
+                                       &options);
+@@ -1881,7 +1881,7 @@ static int k5c_setup(struct krb5_req *kr, uint32_t offline)
+     }
+ 
+     if (!offline) {
+-        krb5_set_canonicalize(kr->options);
++        set_canonicalize_option(kr->options);
+ 
+         use_fast_str = getenv(SSSD_KRB5_USE_FAST);
+         if (use_fast_str == NULL || strcasecmp(use_fast_str, "never") == 0) {
+-- 
+1.8.5.3
+
diff --git a/SOURCES/0117-krb5-child-add-revert_changepw_options.patch b/SOURCES/0117-krb5-child-add-revert_changepw_options.patch
new file mode 100644
index 0000000..10478f3
--- /dev/null
+++ b/SOURCES/0117-krb5-child-add-revert_changepw_options.patch
@@ -0,0 +1,59 @@
+From 62b3828dae4445e30d948cf858e0ecbe120eaf36 Mon Sep 17 00:00:00 2001
+From: Sumit Bose <sbose@redhat.com>
+Date: Fri, 21 Mar 2014 16:16:23 +0100
+Subject: [PATCH 117/117] krb5-child: add revert_changepw_options()
+
+After changing the Kerberos password krb5-child will try to get a fresh
+TGT with the new password. This patch tries to make sure the right gic
+options are used.
+
+Resolves: https://fedorahosted.org/sssd/ticket/2289
+
+Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
+---
+ src/providers/krb5/krb5_child.c | 22 ++++++++++++++++++++++
+ 1 file changed, 22 insertions(+)
+
+diff --git a/src/providers/krb5/krb5_child.c b/src/providers/krb5/krb5_child.c
+index 674dad7387c6874209ee5e491cefc18eba4a6da5..97c5a590ad78080613325b5a397ac965905932fd 100644
+--- a/src/providers/krb5/krb5_child.c
++++ b/src/providers/krb5/krb5_child.c
+@@ -140,6 +140,24 @@ static void set_changepw_options(krb5_context ctx,
+     krb5_get_init_creds_opt_set_tkt_life(options, 5*60);
+ }
+ 
++static void revert_changepw_options(krb5_get_init_creds_opt *options)
++{
++    krb5_error_code kerr;
++
++    set_canonicalize_option(options);
++
++    /* Currently we do not set forwardable and proxiable explicitly, the flags
++     * must be removed so that libkrb5 can take the defaults from krb5.conf */
++    options->flags &= ~(KRB5_GET_INIT_CREDS_OPT_FORWARDABLE);
++    options->flags &= ~(KRB5_GET_INIT_CREDS_OPT_PROXIABLE);
++
++    kerr = set_lifetime_options(options);
++    if (kerr != 0) {
++        DEBUG(SSSDBG_OP_FAILURE, ("set_lifetime_options failed.\n"));
++    }
++}
++
++
+ static errno_t sss_send_pac(krb5_authdata **pac_authdata)
+ {
+     struct sss_cli_req_data sss_data;
+@@ -1187,6 +1205,10 @@ static errno_t changepw_child(struct krb5_req *kr, bool prelim)
+ 
+     krb5_free_cred_contents(kr->ctx, kr->creds);
+ 
++    /* We changed some of the gic options for the password change, now we have
++     * to change them back to get a fresh TGT. */
++    revert_changepw_options(kr->options);
++
+     kerr = get_and_save_tgt(kr, newpassword);
+ 
+     sss_authtok_set_empty(kr->pd->newauthtok);
+-- 
+1.8.5.3
+
diff --git a/SOURCES/0118-KRB5-Do-not-attempt-to-get-a-TGT-after-a-password-ch.patch b/SOURCES/0118-KRB5-Do-not-attempt-to-get-a-TGT-after-a-password-ch.patch
new file mode 100644
index 0000000..700c581
--- /dev/null
+++ b/SOURCES/0118-KRB5-Do-not-attempt-to-get-a-TGT-after-a-password-ch.patch
@@ -0,0 +1,164 @@
+From a9278ff6f7b387c529c3b57251974403c5990d44 Mon Sep 17 00:00:00 2001
+From: Jakub Hrozek <jhrozek@redhat.com>
+Date: Tue, 18 Mar 2014 16:48:11 +0100
+Subject: [PATCH] KRB5: Do not attempt to get a TGT after a password change
+ using OTP
+
+https://fedorahosted.org/sssd/ticket/2271
+
+The current krb5_child code attempts to get a TGT for the convenience of
+the user using the new password after a password change operation.
+However, an OTP should never be used twice, which means we can't perform
+the kinit operation after chpass is finished. Instead, we only print a
+PAM information instructing the user to log out and back in manually.
+---
+ src/providers/krb5/krb5_auth.c  | 21 ++++++++++++++++++---
+ src/providers/krb5/krb5_child.c | 12 ++++++++++++
+ src/sss_client/pam_sss.c        | 19 +++++++++++++++++++
+ src/sss_client/sss_cli.h        |  3 +++
+ 4 files changed, 52 insertions(+), 3 deletions(-)
+
+diff --git a/src/providers/krb5/krb5_auth.c b/src/providers/krb5/krb5_auth.c
+index ce461f5adefc6e42fdc69726ff71d23526375c0c..48c0746efc3d136a1f13e8982384d503d8d20723 100644
+--- a/src/providers/krb5/krb5_auth.c
++++ b/src/providers/krb5/krb5_auth.c
+@@ -815,6 +815,7 @@ static void krb5_auth_done(struct tevent_req *subreq)
+     char *renew_interval_str;
+     time_t renew_interval_time = 0;
+     bool use_enterprise_principal;
++    uint32_t user_info_type;
+ 
+     ret = handle_child_recv(subreq, pd, &buf, &len);
+     talloc_zfree(subreq);
+@@ -1062,9 +1063,23 @@ static void krb5_auth_done(struct tevent_req *subreq)
+ 
+     ret = sss_krb5_check_ccache_princ(kr->uid, kr->gid, kr->ccname, kr->upn);
+     if (ret) {
+-        DEBUG(SSSDBG_CRIT_FAILURE,
+-                ("No ccache for %s in %s?\n", kr->upn, kr->ccname));
+-        goto done;
++        if (res->otp == true && pd->cmd == SSS_PAM_CHAUTHTOK) {
++            DEBUG(SSSDBG_IMPORTANT_INFO,
++                  ("Password change succeeded but currently "
++                   "post-chpass kinit is not implemented\n"));
++
++            user_info_type = SSS_PAM_USER_INFO_OTP_CHPASS;
++            ret = pam_add_response(pd, SSS_PAM_USER_INFO, sizeof(uint32_t),
++                                   (const uint8_t *) &user_info_type);
++            if (ret != EOK) {
++                DEBUG(SSSDBG_CRIT_FAILURE, ("pam_add_response failed.\n"));
++                /* Not fatal */
++            }
++        } else {
++            DEBUG(SSSDBG_CRIT_FAILURE,
++                  ("No ccache for %s in %s?\n", kr->upn, kr->ccname));
++            goto done;
++        }
+     }
+ 
+     if (kr->old_ccname) {
+diff --git a/src/providers/krb5/krb5_child.c b/src/providers/krb5/krb5_child.c
+index d000d70167936b09bbaa39ec7a665089572d9aaa..3ee49e4678078d15dab98eeddf56d36f87955dcf 100644
+--- a/src/providers/krb5/krb5_child.c
++++ b/src/providers/krb5/krb5_child.c
+@@ -45,6 +45,7 @@ struct krb5_req {
+     krb5_principal princ;
+     char* name;
+     krb5_creds *creds;
++    bool otp;
+     krb5_get_init_creds_opt *options;
+ 
+     struct pam_data *pd;
+@@ -370,6 +371,8 @@ static krb5_error_code answer_otp(krb5_context ctx,
+         goto done;
+     }
+ 
++    kr->otp = true;
++
+     /* Validate our assumptions about the contents of authtok. */
+     ret = sss_authtok_get_password(kr->pd->authtok, &pwd, &len);
+     if (ret != EOK)
+@@ -694,6 +697,8 @@ static errno_t k5c_send_data(struct krb5_req *kr, int fd, errno_t error)
+     size_t len;
+     int ret;
+ 
++    DEBUG(SSSDBG_FUNC_DATA, ("Received error code %d\n", error));
++
+     ret = pack_response_packet(kr, error, kr->pd->resp_list, &buf, &len);
+     if (ret != EOK) {
+         DEBUG(1, ("pack_response_packet failed.\n"));
+@@ -1110,6 +1115,8 @@ static errno_t changepw_child(struct krb5_req *kr, bool prelim)
+                                         prompter, kr, 0,
+                                         SSSD_KRB5_CHANGEPW_PRINCIPAL,
+                                         kr->options);
++    DEBUG(SSSDBG_TRACE_INTERNAL,
++          ("chpass is%s using OTP\n", kr->otp ? "" : " not"));
+     if (kerr != 0) {
+         ret = pack_user_info_chpass_error(kr->pd, "Old password not accepted.",
+                                           &msg_len, &msg);
+@@ -1205,6 +1212,11 @@ static errno_t changepw_child(struct krb5_req *kr, bool prelim)
+ 
+     krb5_free_cred_contents(kr->ctx, kr->creds);
+ 
++    if (kr->otp == true) {
++        sss_authtok_set_empty(kr->pd->newauthtok);
++        return map_krb5_error(kerr);
++    }
++
+     /* We changed some of the gic options for the password change, now we have
+      * to change them back to get a fresh TGT. */
+     revert_changepw_options(kr->options);
+diff --git a/src/sss_client/pam_sss.c b/src/sss_client/pam_sss.c
+index 4ff38f299bec75a49c8aace06eecab5735ca9443..e629fc19bd705ac35c2fa1ea4946f40c5eb49079 100644
+--- a/src/sss_client/pam_sss.c
++++ b/src/sss_client/pam_sss.c
+@@ -771,6 +771,22 @@ static int user_info_offline_chpass(pam_handle_t *pamh)
+     return PAM_SUCCESS;
+ }
+ 
++static int user_info_otp_chpass(pam_handle_t *pamh)
++{
++    int ret;
++
++    ret = do_pam_conversation(pamh, PAM_TEXT_INFO,
++                              _("After changing the OTP password, you need to "
++                                "log out and back in order to acquire a ticket"),
++                              NULL, NULL);
++    if (ret != PAM_SUCCESS) {
++        D(("do_pam_conversation failed."));
++        return PAM_SYSTEM_ERR;
++    }
++
++    return PAM_SUCCESS;
++}
++
+ static int user_info_chpass_error(pam_handle_t *pamh, size_t buflen,
+                                   uint8_t *buf)
+ {
+@@ -856,6 +872,9 @@ static int eval_user_info_response(pam_handle_t *pamh, size_t buflen,
+         case SSS_PAM_USER_INFO_OFFLINE_CHPASS:
+             ret = user_info_offline_chpass(pamh);
+             break;
++        case SSS_PAM_USER_INFO_OTP_CHPASS:
++            ret = user_info_otp_chpass(pamh);
++            break;
+         case SSS_PAM_USER_INFO_CHPASS_ERROR:
+             ret = user_info_chpass_error(pamh, buflen, buf);
+             break;
+diff --git a/src/sss_client/sss_cli.h b/src/sss_client/sss_cli.h
+index 285a2979addb0c0677527b76b3b6755f2f173815..16a08e1869bb81ede2c1db87f1fb6d0a51087847 100644
+--- a/src/sss_client/sss_cli.h
++++ b/src/sss_client/sss_cli.h
+@@ -451,6 +451,9 @@ enum user_info_type {
+                                        * possible to change the password while
+                                        * the system is offline. This message
+                                        * is generated by the PAM responder. */
++    SSS_PAM_USER_INFO_OTP_CHPASS,   /**< Tell the user that he needs to kinit
++                                      * or login and logout to get a TGT after
++                                      * an OTP password change */
+     SSS_PAM_USER_INFO_CHPASS_ERROR, /**< Tell the user that a password change
+                                      * failed and optionally give a reason.
+                                      * @param Size of the message as unsigned
+-- 
+1.8.5.3
+
diff --git a/SOURCES/0119-IPA-Use-function-sysdb_attrs_get_el-in-safe-way.patch b/SOURCES/0119-IPA-Use-function-sysdb_attrs_get_el-in-safe-way.patch
new file mode 100644
index 0000000..2aa64b7
--- /dev/null
+++ b/SOURCES/0119-IPA-Use-function-sysdb_attrs_get_el-in-safe-way.patch
@@ -0,0 +1,76 @@
+From a9385ea99d15976c5e7585059945f6964f85339c Mon Sep 17 00:00:00 2001
+From: Lukas Slebodnik <lslebodn@redhat.com>
+Date: Tue, 25 Mar 2014 17:57:32 +0100
+Subject: [PATCH] IPA: Use function sysdb_attrs_get_el in safe way
+
+Function sysdb_attrs_get_el can enlarge array of ldb_message_element in "struct
+sysdb_attrs" if attribute is not among available attributes. Array will be
+enlarged with function talloc_realloc but realloc can move array to another
+place in memory therefore ldb_message_element should not be used after next
+call of function sysdb_attrs_get_el
+
+    sysdb_attrs_get_el(netgroup, SYSDB_ORIG_MEMBER_USER, &user_found);
+    sysdb_attrs_get_el(netgroup, SYSDB_ORIG_MEMBER_HOST, &host_found);
+With netgroups, it is common to omit user or host from netgroup triple.
+There is very high probability that realloc will be called. it is possible
+pointer user_found can refer to the old area after the second call of function
+sysdb_attrs_get_el.
+
+Resolves:
+https://fedorahosted.org/sssd/ticket/2284
+---
+ src/providers/ipa/ipa_netgroups.c | 17 +++++++----------
+ 1 file changed, 7 insertions(+), 10 deletions(-)
+
+diff --git a/src/providers/ipa/ipa_netgroups.c b/src/providers/ipa/ipa_netgroups.c
+index 49a4ba9ab60a05b31241916cf0a7669c785764d4..9cc374bc17eb53accaf607736a19555e17ebf4e1 100644
+--- a/src/providers/ipa/ipa_netgroups.c
++++ b/src/providers/ipa/ipa_netgroups.c
+@@ -297,9 +297,7 @@ static void ipa_get_netgroups_process(struct tevent_req *subreq)
+     struct ipa_get_netgroups_state *state = tevent_req_data(req,
+                                                struct ipa_get_netgroups_state);
+     int i, ret;
+-    struct ldb_message_element *ng_found;
+-    struct ldb_message_element *host_found;
+-    struct ldb_message_element *user_found;
++    struct ldb_message_element *el;
+     struct sdap_search_base **netgr_bases;
+     struct sysdb_attrs **netgroups;
+     size_t netgroups_count;
+@@ -345,16 +343,19 @@ static void ipa_get_netgroups_process(struct tevent_req *subreq)
+ 
+     for (i = 0; i < netgroups_count; i++) {
+         ret = sysdb_attrs_get_el(netgroups[i], SYSDB_ORIG_NETGROUP_MEMBER,
+-                                 &ng_found);
++                                 &el);
+         if (ret != EOK) goto done;
++        if (el->num_values) state->entities_found |= ENTITY_NG;
+ 
+         ret = sysdb_attrs_get_el(netgroups[i], SYSDB_ORIG_MEMBER_USER,
+-                                 &user_found);
++                                 &el);
+         if (ret != EOK) goto done;
++        if (el->num_values) state->entities_found |= ENTITY_USER;
+ 
+         ret = sysdb_attrs_get_el(netgroups[i], SYSDB_ORIG_MEMBER_HOST,
+-                                 &host_found);
++                                 &el);
+         if (ret != EOK) goto done;
++        if (el->num_values) state->entities_found |= ENTITY_HOST;
+ 
+         ret = sysdb_attrs_get_string(netgroups[i], SYSDB_ORIG_DN, &orig_dn);
+         if (ret != EOK) {
+@@ -371,10 +372,6 @@ static void ipa_get_netgroups_process(struct tevent_req *subreq)
+             goto done;
+         }
+ 
+-        if (ng_found->num_values) state->entities_found |= ENTITY_NG;
+-        if (user_found->num_values) state->entities_found |= ENTITY_USER;
+-        if (host_found->num_values) state->entities_found |= ENTITY_HOST;
+-
+         if (state->entities_found == 0) {
+             continue;
+         }
+-- 
+1.8.5.3
+
diff --git a/SOURCES/0120-AD-connect-to-forest-root-when-downloading-the-list-.patch b/SOURCES/0120-AD-connect-to-forest-root-when-downloading-the-list-.patch
new file mode 100644
index 0000000..de10d29
--- /dev/null
+++ b/SOURCES/0120-AD-connect-to-forest-root-when-downloading-the-list-.patch
@@ -0,0 +1,478 @@
+From d97ff9abd276e9216e5868be37c3762d208b36c0 Mon Sep 17 00:00:00 2001
+From: Jakub Hrozek <jhrozek@redhat.com>
+Date: Tue, 1 Apr 2014 13:51:49 -0400
+Subject: [PATCH 120/120] AD: connect to forest root when downloading the list
+ of subdomains
+
+https://fedorahosted.org/sssd/ticket/2285
+
+Only the forest root has the knowledge about all the domains in the forest,
+the forest leaves only see themselves and the forest root.
+
+This patch switches to connecting to the forest root for downloading the
+trusted domains instead of the server we are connected to.
+---
+ src/providers/ad/ad_subdomains.c | 372 ++++++++++++++++++++++++++++++++++++++-
+ 1 file changed, 363 insertions(+), 9 deletions(-)
+
+diff --git a/src/providers/ad/ad_subdomains.c b/src/providers/ad/ad_subdomains.c
+index 0d9652b5c615add47958cfdc61eba862a332ae4d..3c841788d5d88069d79a9438b72f57c8c2e0ffda 100644
+--- a/src/providers/ad/ad_subdomains.c
++++ b/src/providers/ad/ad_subdomains.c
+@@ -54,7 +54,9 @@
+  * the same forest. See http://msdn.microsoft.com/en-us/library/cc223786.aspx
+  * for more information.
+  */
+-#define SLAVE_DOMAIN_FILTER "(&(objectclass=trustedDomain)(trustType=2)(!(msDS-TrustForestTrustInfo=*)))"
++#define SLAVE_DOMAIN_FILTER_BASE "(objectclass=trustedDomain)(trustType=2)(!(msDS-TrustForestTrustInfo=*))"
++#define SLAVE_DOMAIN_FILTER      "(&"SLAVE_DOMAIN_FILTER_BASE")"
++#define FOREST_ROOT_FILTER_FMT   "(&"SLAVE_DOMAIN_FILTER_BASE"(cn=%s))"
+ 
+ /* do not refresh more often than every 5 seconds for now */
+ #define AD_SUBDOMAIN_REFRESH_LIMIT 5
+@@ -80,6 +82,11 @@ struct ad_subdomains_req_ctx {
+     char *current_filter;
+     size_t base_iter;
+ 
++    struct ad_id_ctx *root_id_ctx;
++    struct sdap_id_op *root_op;
++    size_t root_base_iter;
++    struct sysdb_attrs *root_domain;
++
+     size_t reply_count;
+     struct sysdb_attrs **reply;
+ 
+@@ -461,6 +468,7 @@ static errno_t ad_subdom_reinit(struct ad_subdomains_ctx *ctx)
+ 
+ static void ad_subdomains_get_conn_done(struct tevent_req *req);
+ static void ad_subdomains_master_dom_done(struct tevent_req *req);
++static errno_t ad_subdomains_get_root(struct ad_subdomains_req_ctx *ctx);
+ static errno_t ad_subdomains_get_slave(struct ad_subdomains_req_ctx *ctx);
+ 
+ static void ad_subdomains_retrieve(struct ad_subdomains_ctx *ctx,
+@@ -481,6 +489,10 @@ static void ad_subdomains_retrieve(struct ad_subdomains_ctx *ctx,
+     req_ctx->sd_ctx = ctx;
+     req_ctx->current_filter = NULL;
+     req_ctx->base_iter = 0;
++    req_ctx->root_base_iter = 0;
++    req_ctx->root_id_ctx = NULL;
++    req_ctx->root_op = NULL;
++    req_ctx->root_domain = NULL;
+     req_ctx->reply_count = 0;
+     req_ctx->reply = NULL;
+ 
+@@ -575,7 +587,20 @@ static void ad_subdomains_master_dom_done(struct tevent_req *req)
+         goto done;
+     }
+ 
+-    ret = ad_subdomains_get_slave(ctx);
++    if (strcasecmp(ctx->sd_ctx->be_ctx->domain->name, ctx->forest) != 0) {
++        DEBUG(SSSDBG_TRACE_FUNC,
++              ("SSSD needs to look up the forest root domain\n"));
++        ret = ad_subdomains_get_root(ctx);
++    } else {
++        DEBUG(SSSDBG_TRACE_FUNC,
++              ("Connected to forest root, looking up child domains..\n"));
++
++        ctx->root_op = ctx->sdap_op;
++        ctx->root_id_ctx = ctx->sd_ctx->ad_id_ctx;
++
++        ret = ad_subdomains_get_slave(ctx);
++    }
++
+     if (ret == EAGAIN) {
+         return;
+     } else if (ret != EOK) {
+@@ -586,6 +611,244 @@ done:
+     be_req_terminate(ctx->be_req, DP_ERR_FATAL, ret, NULL);
+ }
+ 
++static void ad_subdomains_get_root_domain_done(struct tevent_req *req);
++
++static errno_t ad_subdomains_get_root(struct ad_subdomains_req_ctx *ctx)
++{
++    struct tevent_req *req;
++    struct sdap_search_base *base;
++    struct sdap_id_ctx *sdap_id_ctx;
++    char *filter;
++    const char *forest_root_attrs[] = { AD_AT_FLATNAME, AD_AT_TRUST_PARTNER,
++                                        AD_AT_SID, AD_AT_TRUST_TYPE,
++                                        AD_AT_TRUST_ATTRS, NULL };
++
++    sdap_id_ctx = ctx->sd_ctx->sdap_id_ctx;
++    base = sdap_id_ctx->opts->sdom->search_bases[ctx->root_base_iter];
++    if (base == NULL) {
++        return EOK;
++    }
++
++    filter = talloc_asprintf(ctx, FOREST_ROOT_FILTER_FMT, ctx->forest);
++    if (filter == NULL) {
++        return ENOMEM;
++    }
++
++    req = sdap_get_generic_send(ctx, ctx->sd_ctx->be_ctx->ev,
++                                sdap_id_ctx->opts,
++                                sdap_id_op_handle(ctx->sdap_op),
++                                base->basedn, LDAP_SCOPE_SUBTREE,
++                                filter, forest_root_attrs,
++                                NULL, 0,
++                                dp_opt_get_int(sdap_id_ctx->opts->basic,
++                                                SDAP_SEARCH_TIMEOUT),
++                                false);
++
++    if (req == NULL) {
++        DEBUG(SSSDBG_OP_FAILURE, ("sdap_get_generic_send failed.\n"));
++        return ENOMEM;
++    }
++
++    tevent_req_set_callback(req, ad_subdomains_get_root_domain_done, ctx);
++    return EAGAIN;
++}
++
++static struct ad_id_ctx *ads_get_root_id_ctx(struct ad_subdomains_req_ctx *ctx);
++static void ad_subdomains_root_conn_done(struct tevent_req *req);
++
++static void ad_subdomains_get_root_domain_done(struct tevent_req *req)
++{
++    int ret;
++    size_t reply_count;
++    struct sysdb_attrs **reply = NULL;
++    struct ad_subdomains_req_ctx *ctx;
++    int dp_error = DP_ERR_FATAL;
++    bool has_changes;
++
++    ctx = tevent_req_callback_data(req, struct ad_subdomains_req_ctx);
++
++    ret = sdap_get_generic_recv(req, ctx, &reply_count, &reply);
++    talloc_zfree(req);
++    if (ret != EOK) {
++        DEBUG(SSSDBG_OP_FAILURE, ("sdap_get_generic_send request failed.\n"));
++        goto fail;
++    }
++
++    if (reply_count == 0) {
++        /* If no root domain was found in the default search base, try the
++         * next one, if available
++         */
++        ctx->root_base_iter++;
++        ret = ad_subdomains_get_root(ctx);
++        if (ret == EAGAIN) {
++            return;
++        }
++
++        goto fail;
++    } else if (reply_count > 1) {
++        DEBUG(SSSDBG_CRIT_FAILURE,
++              ("Multiple results for root domain search, "
++               "domain list might be incomplete!\n"));
++
++        ctx->root_op = ctx->sdap_op;
++        ctx->root_id_ctx = ctx->sd_ctx->ad_id_ctx;
++
++        ret = ad_subdomains_get_slave(ctx);
++        if (ret == EAGAIN) {
++            return;
++        }
++
++        goto fail;
++    }
++    /* Exactly one result, good. */
++
++    /* We won't use the operation to the local LDAP anymore, but
++     * read from the forest root
++     */
++    ret = sdap_id_op_done(ctx->sdap_op, ret, &dp_error);
++    if (ret != EOK) {
++        if (dp_error == DP_ERR_OFFLINE) {
++            DEBUG(SSSDBG_MINOR_FAILURE,
++                  ("No AD server is available, cannot get the "
++                   "subdomain list while offline\n"));
++        } else {
++            DEBUG(SSSDBG_OP_FAILURE,
++                  ("Failed to search the AD server: [%d](%s)\n",
++                  ret, strerror(ret)));
++        }
++        goto fail;
++    }
++
++    ret = ad_subdomains_refresh(ctx->sd_ctx, 1, reply, &has_changes);
++    if (ret != EOK) {
++        DEBUG(SSSDBG_OP_FAILURE, ("ad_subdomains_refresh failed.\n"));
++        goto fail;
++    }
++
++    if (has_changes) {
++        ret = ad_subdom_reinit(ctx->sd_ctx);
++        if (ret != EOK) {
++            DEBUG(SSSDBG_OP_FAILURE, ("Could not reinitialize subdomains\n"));
++            goto fail;
++        }
++    }
++
++    ctx->root_domain = reply[0];
++    ctx->root_id_ctx = ads_get_root_id_ctx(ctx);
++    if (ctx->root_id_ctx == NULL) {
++        DEBUG(SSSDBG_OP_FAILURE,
++              ("Cannot create id ctx for the root domain\n"));
++        ret = EFAULT;
++        goto fail;
++    }
++
++    ctx->root_op = sdap_id_op_create(ctx,
++                                     ctx->root_id_ctx->ldap_ctx->conn_cache);
++    if (ctx->root_op == NULL) {
++        DEBUG(SSSDBG_OP_FAILURE, ("sdap_id_op_create failed.\n"));
++        ret = ENOMEM;
++        goto fail;
++    }
++
++    req = sdap_id_op_connect_send(ctx->root_op, ctx, &ret);
++    if (req == NULL) {
++        DEBUG(SSSDBG_OP_FAILURE, ("sdap_id_op_connect_send failed: %d(%s).\n",
++                                   ret, strerror(ret)));
++        goto fail;
++    }
++
++    tevent_req_set_callback(req, ad_subdomains_root_conn_done, ctx);
++    return;
++
++fail:
++    if (ret == EOK) {
++        ctx->sd_ctx->last_refreshed = time(NULL);
++        dp_error = DP_ERR_OK;
++    }
++    be_req_terminate(ctx->be_req, dp_error, ret, NULL);
++}
++
++static struct ad_id_ctx *ads_get_root_id_ctx(struct ad_subdomains_req_ctx *ctx)
++{
++    errno_t ret;
++    const char *name;
++    struct sss_domain_info *root;
++    struct sdap_domain *sdom;
++    struct ad_id_ctx *root_id_ctx;
++
++    ret = sysdb_attrs_get_string(ctx->root_domain, AD_AT_TRUST_PARTNER, &name);
++    if (ret != EOK) {
++        DEBUG(SSSDBG_OP_FAILURE, ("sysdb_attrs_get_string failed.\n"));
++        return NULL;
++    }
++
++    /* With a subsequent run, the root should already be known */
++    root = find_subdomain_by_name(ctx->sd_ctx->be_ctx->domain,
++                                  name, false);
++    if (root == NULL) {
++        DEBUG(SSSDBG_OP_FAILURE, ("Could not find the root domain\n"));
++        return NULL;
++    }
++
++    sdom = sdap_domain_get(ctx->sd_ctx->ad_id_ctx->sdap_id_ctx->opts, root);
++    if (sdom == NULL) {
++        DEBUG(SSSDBG_OP_FAILURE,
++              ("Cannot get the sdom for %s!\n", root->name));
++        return NULL;
++    }
++
++    if (sdom->pvt == NULL) {
++        ret = ad_subdom_ad_ctx_new(ctx->sd_ctx->be_ctx,
++                                   ctx->sd_ctx->ad_id_ctx,
++                                   root,
++                                   &root_id_ctx);
++        if (ret != EOK) {
++            DEBUG(SSSDBG_OP_FAILURE, ("ad_subdom_ad_ctx_new failed.\n"));
++            return NULL;
++        }
++        sdom->pvt = root_id_ctx;
++    } else {
++        root_id_ctx = sdom->pvt;
++    }
++
++    return root_id_ctx;
++}
++
++static void ad_subdomains_root_conn_done(struct tevent_req *req)
++{
++    int ret;
++    int dp_error = DP_ERR_FATAL;
++    struct ad_subdomains_req_ctx *ctx;
++
++    ctx = tevent_req_callback_data(req, struct ad_subdomains_req_ctx);
++
++    ret = sdap_id_op_connect_recv(req, &dp_error);
++    talloc_zfree(req);
++    if (ret) {
++        if (dp_error == DP_ERR_OFFLINE) {
++            DEBUG(SSSDBG_MINOR_FAILURE,
++                  ("No AD server is available, cannot get the "
++                   "subdomain list while offline\n"));
++        } else {
++            DEBUG(SSSDBG_OP_FAILURE,
++                  ("Failed to connect to AD server: [%d](%s)\n",
++                  ret, strerror(ret)));
++        }
++
++        goto fail;
++    }
++
++    ret = ad_subdomains_get_slave(ctx);
++    if (ret == EAGAIN) {
++        return;
++    } else if (ret != EOK) {
++        goto fail;
++    }
++
++fail:
++    be_req_terminate(ctx->be_req, dp_error, ret, NULL);
++}
++
+ static void ad_subdomains_get_slave_domain_done(struct tevent_req *req);
+ 
+ static errno_t ad_subdomains_get_slave(struct ad_subdomains_req_ctx *ctx)
+@@ -596,18 +859,18 @@ static errno_t ad_subdomains_get_slave(struct ad_subdomains_req_ctx *ctx)
+                                       AD_AT_SID, AD_AT_TRUST_TYPE,
+                                       AD_AT_TRUST_ATTRS, NULL };
+ 
+-    base = ctx->sd_ctx->sdap_id_ctx->opts->sdom->search_bases[ctx->base_iter];
++    base = ctx->root_id_ctx->sdap_id_ctx->opts->sdom->search_bases[ctx->base_iter];
+     if (base == NULL) {
+         return EOK;
+     }
+ 
+     req = sdap_get_generic_send(ctx, ctx->sd_ctx->be_ctx->ev,
+-                           ctx->sd_ctx->sdap_id_ctx->opts,
+-                           sdap_id_op_handle(ctx->sdap_op),
++                           ctx->root_id_ctx->sdap_id_ctx->opts,
++                           sdap_id_op_handle(ctx->root_op),
+                            base->basedn, LDAP_SCOPE_SUBTREE,
+                            SLAVE_DOMAIN_FILTER, slave_dom_attrs,
+                            NULL, 0,
+-                           dp_opt_get_int(ctx->sd_ctx->sdap_id_ctx->opts->basic,
++                           dp_opt_get_int(ctx->root_id_ctx->sdap_id_ctx->opts->basic,
+                                           SDAP_SEARCH_TIMEOUT),
+                            false);
+ 
+@@ -620,6 +883,68 @@ static errno_t ad_subdomains_get_slave(struct ad_subdomains_req_ctx *ctx)
+     return EAGAIN;
+ }
+ 
++static errno_t ad_subdomains_process(TALLOC_CTX *mem_ctx,
++                                     struct sss_domain_info *domain,
++                                     size_t nsd, struct sysdb_attrs **sd,
++                                     struct sysdb_attrs *root,
++                                     size_t *_nsd_out,
++                                     struct sysdb_attrs ***_sd_out)
++{
++    size_t i, sdi;
++    struct sysdb_attrs **sd_out;
++    const char *sd_name;
++    errno_t ret;
++
++    if (root == NULL) {
++        /* We are connected directly to the root domain. The 'sd'
++         * list is complete and we can just use it
++         */
++        *_nsd_out = nsd;
++        *_sd_out = sd;
++        return EOK;
++    }
++
++    /* If we searched for root separately, we must:
++     *  a) treat the root domain as a subdomain
++     *  b) filter the subdomain we are connected to from the subdomain
++     *     list, from our point of view, it's the master domain
++     */
++    sd_out = talloc_zero_array(mem_ctx, struct sysdb_attrs *, nsd+1);
++    if (sd_out == NULL) {
++        return ENOMEM;
++    }
++
++    sdi = 0;
++    for (i = 0; i < nsd; i++) {
++        ret = sysdb_attrs_get_string(sd[i], AD_AT_TRUST_PARTNER, &sd_name);
++        if (ret != EOK) {
++            DEBUG(SSSDBG_OP_FAILURE, ("sysdb_attrs_get_string failed.\n"));
++            goto fail;
++        }
++
++        if (strcasecmp(sd_name, domain->name) == 0) {
++            DEBUG(SSSDBG_TRACE_INTERNAL,
++                  ("Not including primary domain %s in the subdomain list\n",
++                  domain->name));
++            continue;
++        }
++
++        sd_out[sdi] = talloc_steal(sd_out, sd[i]);
++        sdi++;
++    }
++
++    /* Now include the root */
++    sd_out[sdi] = talloc_steal(sd_out, root);
++
++    *_nsd_out = sdi+1;
++    *_sd_out = sd_out;
++    return EOK;
++
++fail:
++    talloc_free(sd_out);
++    return ret;
++}
++
+ static void ad_subdomains_get_slave_domain_done(struct tevent_req *req)
+ {
+     int ret;
+@@ -628,6 +953,8 @@ static void ad_subdomains_get_slave_domain_done(struct tevent_req *req)
+     struct ad_subdomains_req_ctx *ctx;
+     int dp_error = DP_ERR_FATAL;
+     bool refresh_has_changes = false;
++    size_t nsubdoms;
++    struct sysdb_attrs **subdoms;
+ 
+     ctx = tevent_req_callback_data(req, struct ad_subdomains_req_ctx);
+ 
+@@ -653,13 +980,40 @@ static void ad_subdomains_get_slave_domain_done(struct tevent_req *req)
+     ctx->base_iter++;
+     ret = ad_subdomains_get_slave(ctx);
+     if (ret == EAGAIN) {
++        /* Search in progress */
++        return;
++    }
++
++    ret = sdap_id_op_done(ctx->root_op, ret, &dp_error);
++    if (ret != EOK) {
++        if (dp_error == DP_ERR_OFFLINE) {
++            DEBUG(SSSDBG_MINOR_FAILURE,
++                  ("No AD server is available, cannot get the "
++                   "subdomain list while offline\n"));
++        } else {
++            DEBUG(SSSDBG_OP_FAILURE,
++                  ("Failed to search the AD server: [%d](%s)\n",
++                  ret, strerror(ret)));
++        }
++        tevent_req_error(req, ret);
++        return;
++    }
++
++    /* Based on whether we are connected to the forest root or not, we might
++     * need to exclude the subdomain we are connected to from the list of
++     * subdomains
++     */
++    ret = ad_subdomains_process(ctx, ctx->sd_ctx->be_ctx->domain,
++                                ctx->reply_count, ctx->reply,
++                                ctx->root_domain, &nsubdoms, &subdoms);
++    if (ret != EOK) {
++        DEBUG(SSSDBG_OP_FAILURE, ("Cannot process subdomain list\n"));
++        tevent_req_error(req, ret);
+         return;
+-    } else if (ret != EOK) {
+-        goto done;
+     }
+ 
+     /* Got all the subdomains, let's process them */
+-    ret = ad_subdomains_refresh(ctx->sd_ctx, ctx->reply_count, ctx->reply,
++    ret = ad_subdomains_refresh(ctx->sd_ctx, nsubdoms, subdoms,
+                                 &refresh_has_changes);
+     if (ret != EOK) {
+         DEBUG(SSSDBG_OP_FAILURE, ("Failed to refresh subdomains.\n"));
+-- 
+1.9.0
+
diff --git a/SOURCES/0121-IPA-Fix-SELinux-mapping-order-memory-hierarchy.patch b/SOURCES/0121-IPA-Fix-SELinux-mapping-order-memory-hierarchy.patch
new file mode 100644
index 0000000..e48bd0e
--- /dev/null
+++ b/SOURCES/0121-IPA-Fix-SELinux-mapping-order-memory-hierarchy.patch
@@ -0,0 +1,64 @@
+From 1a088724c4d70edfbecab4252c1644100374f0f0 Mon Sep 17 00:00:00 2001
+From: Jakub Hrozek <jhrozek@redhat.com>
+Date: Wed, 2 Apr 2014 22:11:59 +0200
+Subject: [PATCH 121/121] IPA: Fix SELinux mapping order memory hierarchy
+
+https://fedorahosted.org/sssd/ticket/2300
+
+The list of SELinux mapping orders was allocated on tmp_ctx and parsed
+into an array. The array itself was correctly allocated on mem_ctx but
+its contents remained on tmp_ctx, leading to a use-after-free error.
+This patch fixes the memory hierarchy so that both the array and its
+contents are allocated on mem_ctx.
+
+(cherry picked from commit 355b8a655cfcc4e783077d12f76b55da1d23fb87)
+
+Reviewed-by: Sumit Bose <sbose@redhat.com>
+---
+ src/providers/ipa/ipa_selinux.c | 16 ++++++++--------
+ 1 file changed, 8 insertions(+), 8 deletions(-)
+
+diff --git a/src/providers/ipa/ipa_selinux.c b/src/providers/ipa/ipa_selinux.c
+index 7f59161918a04ff8c994a0ce0fe55924ff09eda7..b7cbe445f1ecbfffaa84bb049aaf45ba4ecb1a35 100644
+--- a/src/providers/ipa/ipa_selinux.c
++++ b/src/providers/ipa/ipa_selinux.c
+@@ -557,21 +557,15 @@ static errno_t create_order_array(TALLOC_CTX *mem_ctx, const char *map_order,
+         goto done;
+     }
+ 
+-    order = talloc_strdup(tmp_ctx, map_order);
+-    if (order == NULL) {
+-        ret = ENOMEM;
+-        goto done;
+-    }
+-    len = strlen(order);
+-
+     /* The "order" string contains one or more SELinux user records
+      * separated by $. Now we need to create an array of string from
+      * this one string. First find out how many elements in the array
+      * will be. This way only one alloc will be necessary for the array
+      */
+     order_count = 1;
++    len = strlen(map_order);
+     for (i = 0; i < len; i++) {
+-        if (order[i] == '$') order_count++;
++        if (map_order[i] == '$') order_count++;
+     }
+ 
+     order_array = talloc_array(tmp_ctx, char *, order_count);
+@@ -580,6 +574,12 @@ static errno_t create_order_array(TALLOC_CTX *mem_ctx, const char *map_order,
+         goto done;
+     }
+ 
++    order = talloc_strdup(order_array, map_order);
++    if (order == NULL) {
++        ret = ENOMEM;
++        goto done;
++    }
++
+     /* Now fill the array with pointers to the original string. Also
+      * use binary zeros to make multiple string out of the one.
+      */
+-- 
+1.9.0
+
diff --git a/SPECS/sssd.spec b/SPECS/sssd.spec
index 93d9fdc..c35d49c 100644
--- a/SPECS/sssd.spec
+++ b/SPECS/sssd.spec
@@ -8,7 +8,7 @@
 
 Name: sssd
 Version: 1.11.2
-Release: 1%{?dist}
+Release: 65%{?dist}
 Group: Applications/System
 Summary: System Security Services Daemon
 License: GPLv3+
@@ -17,6 +17,128 @@ Source0: https://fedorahosted.org/released/sssd/%{name}-%{version}.tar.gz
 BuildRoot: %(mktemp -ud %{_tmppath}/%{name}-%{version}-%{release}-XXXXXX)
 
 ### Patches ###
+Patch0001: 0001-SYSDB-Skip-malformed-netgroup-attribute.patch
+Patch0002: 0002-monitor-Specific-error-message-for-missing-sssd.conf.patch
+Patch0003: 0003-AD-Fix-a-typo-in-the-man-page.patch
+Patch0004: 0004-LDAP-Initialize-user-count-for-AD-matching-rule.patch
+Patch0005: 0005-SSSD-Improved-domain-detection.patch
+Patch0006: 0006-SSSD-Unit-test-sss_ldap_dn_in_search_bases.patch
+Patch0007: 0007-do-not-use-default_domain_suffix-with-autofs.patch
+Patch0008: 0008-SYSDB-Sanitize-filter-before-sysdb_search_groups.patch
+Patch0009: 0009-SYSDB-Sanitize-filter-before-removing-ghost-attrs.patch
+Patch0010: 0010-LDAP-Split-out-a-request-to-search-for-a-user-w-o-sa.patch
+Patch0011: 0011-LDAP-Search-for-original-DN-during-auth-if-it-s-miss.patch
+Patch0012: 0012-LDAP-Prevent-from-using-uninitialized-sdap_options.patch
+Patch0013: 0013-SUBDOMAINS-Reuse-cached-results-if-DP-is-offline.patch
+Patch0014: 0014-failover-check-dns_domain-if-primary-servers-lookup-.patch
+Patch0015: 0015-NSS-Set-packet-length-for-initgroups.patch
+Patch0016: 0016-NSS-Fix-memory-leak-in-sss_setnetgrent.patch
+Patch0017: 0017-AD-use-LDAP-for-group-lookups.patch
+Patch0018: 0018-idmap-add-API-to-free-allocated-SIDs.patch
+Patch0019: 0019-free-idmapped-SIDs-correctly.patch
+Patch0020: 0020-free-idmapped-dom-SIDs-correctly.patch
+Patch0021: 0021-free-idmapped-smb-SIDs-correctly.patch
+Patch0022: 0022-free-idmapped-binary-SIDs-correctly.patch
+Patch0023: 0023-Initialize-sid_str-to-NULL-to-avoid-freeing-random-d.patch
+Patch0024: 0024-ad-refactor-tokengroups-initgroups.patch
+Patch0025: 0025-ad-use-tokengroups-even-when-id-mapping-is-disabled.patch
+Patch0026: 0026-AD-filter-domain-local-groups-for-trusted-sub-domain.patch
+Patch0027: 0027-AD-cross-domain-membership-fix.patch
+Patch0028: 0028-AD-Add-a-utility-function-to-create-list-of-connecti.patch
+Patch0029: 0029-AD-Add-a-new-option-to-turn-off-GC-lookups.patch
+Patch0030: 0030-AD-Enable-fallback-to-LDAP-of-trusted-domain.patch
+Patch0031: 0031-Bump-sss_idmap-version-to-3-0-3.patch
+Patch0032: 0032-AD-Refresh-subdomain-data-structures-on-startup.patch
+Patch0033: 0033-IPA-Refresh-subdomain-data-structures-on-startup.patch
+Patch0034: 0034-IPA-Call-ipa_ad_subdom_refresh-when-server-mode-is-i.patch
+Patch0035: 0035-sss_cache-initialize-names-member-of-sss_domain_info.patch
+Patch0036: 0036-sss_cache-fix-case-sensitivity-issue.patch
+Patch0037: 0037-Add-sysdb_attrs_add_lc_name_alias.patch
+Patch0038: 0038-Use-sysdb_attrs_add_lc_name_alias-to-add-case-insens.patch
+Patch0039: 0039-Use-lower-case-name-for-case-insensitive-searches.patch
+Patch0040: 0040-Add-new-option-ldap_group_type.patch
+Patch0041: 0041-Add-sysdb_attrs_get_int32_t.patch
+Patch0042: 0042-IPA-fix-for-recent-AD-group-membership-changes.patch
+Patch0043: 0043-LDAP-Fix-typo-and-use-the-right-attribute-map.patch
+Patch0044: 0044-pac-fix-double-free.patch
+Patch0045: 0045-pac-fix-potential-memory-leaks.patch
+Patch0046: 0046-responder-Set-forest-attribute-in-AD-domains.patch
+Patch0047: 0047-LDAP-Add-a-new-error-code-for-malformed-access-contr.patch
+Patch0048: 0048-FAST-when-parsing-krb5_child-response-make-sure-to-n.patch
+Patch0049: 0049-UTIL-Inherit-parent-domain-s-default_shell.patch
+Patch0050: 0050-NSS-Use-plain-user-name-when-expanding-homedir.patch
+Patch0051: 0051-simple-access-match-objects-using-flat-name.patch
+Patch0052: 0052-simple-access-refresh-master-domain-info.patch
+Patch0053: 0053-NSS-add-support-for-subdomain_homedir.patch
+Patch0054: 0054-AD-Return-right-error-code-from-netlogon_get_flat_na.patch
+Patch0055: 0055-AD-Don-t-fail-the-request-if-ad_account_can_shortcut.patch
+Patch0056: 0056-MAN-Fix-a-typo.patch
+Patch0057: 0057-LDAP-Fix-error-check.patch
+Patch0058: 0058-LDAP-Don-t-fail-if-subdomain-cannot-be-found-by-sid.patch
+Patch0059: 0059-LDAP-update-id-mapping-detection-for-ldap-provider.patch
+Patch0060: 0060-sdap_idamp-Fall-back-to-another-method-if-sid-is-wro.patch
+Patch0061: 0061-krb5-hint-to-increase-krb5_auth_timeout.patch
+Patch0062: 0062-LDAP-Don-t-abort-request-if-no-id-mapping-domain-mat.patch
+Patch0063: 0063-sudo-memset-tm-when-converting-time-attributes.patch
+Patch0064: 0064-AD-Don-t-mark-domain-as-enumerated-twice.patch
+Patch0065: 0065-AD-Store-info-on-whether-a-subdomain-is-set-to-enume.patch
+Patch0066: 0066-LDAP-Pass-a-private-context-to-enumeration-ptask-ins.patch
+Patch0067: 0067-LDAP-Add-enum-request-with-custom-connection.patch
+Patch0068: 0068-AD-Enumerate-users-from-GC-other-entities-from-LDAP.patch
+Patch0069: 0069-LDAP-Don-t-clobber-original_member-during-enumeratio.patch
+Patch0070: 0070-DB-Add-sss_ldb_el_to_string_list.patch
+Patch0071: 0071-AD-Establish-cross-domain-memberships-after-enumerat.patch
+Patch0072: 0072-AD-SRV-use-right-domain-name-for-CLDAP-ping.patch
+Patch0073: 0073-MAN-clarify-which-shell-option-takes-precedence.patch
+Patch0074: 0074-LDAP-store-group-if-subdomain-cannot-be-found-by-sid.patch
+Patch0075: 0075-LDAP-require-attribute-groupType-for-AD-groups.patch
+Patch0076: 0076-MONITOR-Incorrect-permissions-on-sssd.conf.patch
+Patch0077: 0077-Revert-NSS-add-support-for-subdomain_homedir.patch
+Patch0078: 0078-AD-support-for-subdomain_homedir.patch
+Patch0079: 0079-MAN-update-of-subdomain_homedir-usage.patch
+Patch0080: 0080-utils-handling-NULL-params-in-sss_parse_name.patch
+Patch0081: 0081-LDAP-Detect-the-presence-of-POSIX-attributes.patch
+Patch0082: 0082-AD-Only-download-domains-that-are-set-to-enumerate.patch
+Patch0083: 0083-AD-Remove-dead-code.patch
+Patch0084: 0084-LDAP-Handle-errors-from-sdap_id_op-properly-in-enum-.patch
+Patch0085: 0085-SSS_CACHE-Reset-the-initgroups-attribute-when-resett.patch
+Patch0086: 0086-IPA-Default-to-krb5_use_fast-try.patch
+Patch0087: 0087-IPA-default-krb5_fast_principal-to-host-client-realm.patch
+Patch0088: 0088-MAN-Clarify-the-new-krb5_use_fast-IPA-default.patch
+Patch0089: 0089-IPA-Don-t-call-tevent_req_post-outside-_send.patch
+Patch0090: 0090-IPA-Don-t-fail-if-apply_subdomain_homedir-returns-EN.patch
+Patch0091: 0091-LDAP-Setup-periodic-task-only-once.patch
+Patch0092: 0092-UTIL-Sanitize-whitespaces.patch
+Patch0093: 0093-IDMAP-add-sss_idmap_check_collision-_ex.patch
+Patch0094: 0094-IPA-refactor-idmap-code-and-add-test.patch
+Patch0095: 0095-IPA-check-ranges-for-collisions-before-saving-them.patch
+Patch0096: 0096-OPTS-Allow-using-defaults-for-blobs.patch
+Patch0097: 0097-DP-Provide-separate-dp_copy_defaults-function.patch
+Patch0098: 0098-MAN-Clarify-the-ldap_access_filter-option-further.patch
+Patch0099: 0099-MAN-Clarify-that-changing-ID-mapping-options-might-r.patch
+Patch0100: 0100-DOC-Fix-names-of-arguments-in-doxygen-comments.patch
+Patch0101: 0101-libsss_idmap-bump-version-info.patch
+Patch0102: 0102-config-API-add-missing-subdomain-target-to-AD-provid.patch
+Patch0103: 0103-SUDO-AD-provider.patch
+Patch0104: 0104-ipa-server-mode-use-lower-case-user-name-for-home-di.patch
+Patch0105: 0105-IPA-Do-not-save-intermediate-data-to-sysdb.patch
+Patch0106: 0106-Fix-krb5-changepw-when-FAST-only-preauth-methods-are.patch
+Patch0107: 0107-IPA-Use-GC-for-AD-initgroup-requests.patch
+Patch0108: 0108-AD-Only-connect-to-GC-for-subdomain-users.patch
+Patch0109: 0109-MAN-Clarify-the-GC-support-a-bit.patch
+Patch0110: 0110-IPA-Use-the-correct-domain-when-processing-SELinux-r.patch
+Patch0111: 0111-IPA-KRB5-handle-KRB5_PROG_ETYPE_NOSUPP-during-IPA-pa.patch
+Patch0112: 0112-AD-Continue-if-sssd-failes-to-check-extra-members.patch
+Patch0113: 0113-IPA-Write-SELinux-usernames-in-the-right-case.patch
+Patch0114: 0114-krb5_child-remove-unused-option-lifetime_str-from-k5.patch
+Patch0115: 0115-krb5-child-extract-lifetime-settings-into-set_lifeti.patch
+Patch0116: 0116-krb5_client-rename-krb5_set_canonicalize-to-set_cano.patch
+Patch0117: 0117-krb5-child-add-revert_changepw_options.patch
+Patch0118: 0118-KRB5-Do-not-attempt-to-get-a-TGT-after-a-password-ch.patch
+Patch0119: 0119-IPA-Use-function-sysdb_attrs_get_el-in-safe-way.patch
+Patch0120: 0120-AD-connect-to-forest-root-when-downloading-the-list-.patch
+Patch0121: 0121-IPA-Fix-SELinux-mapping-order-memory-hierarchy.patch
+
 
 ### Dependencies ###
 Requires: sssd-common = %{version}-%{release}
@@ -74,7 +196,7 @@ BuildRequires: pkgconfig
 BuildRequires: glib2-devel
 BuildRequires: diffstat
 BuildRequires: findutils
-BuildRequires: samba4-devel >= samba4-4.0.0-59beta2
+BuildRequires: samba4-devel >= 4.0.0-59beta2
 BuildRequires: selinux-policy-targeted
 
 %description
@@ -699,6 +821,269 @@ fi
 %postun -n libsss_idmap -p /sbin/ldconfig
 
 %changelog
+* 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