[sssd/el5/master] Prepare SSSD for RHEL5

Stephen Gallagher sgallagh at fedoraproject.org
Tue Aug 24 17:38:45 UTC 2010


commit 57cf7a3c01c842e8c1e5cfa37f9fdaedfb8411b6
Author: Stephen Gallagher <sgallagh at redhat.com>
Date:   Wed Aug 4 15:11:03 2010 -0400

    Prepare SSSD for RHEL5

 ...ixing-types-in-queue-and-stack-interfaces.patch |  222 +++
 0002-Fix-SASL-authentication.patch                 |   34 +
 0003-Make-RootDSE-optional.patch                   |   63 +
 ...it-requests-for-several-operational-attrs.patch |   56 +
 0005-Add-sss_log-function.patch                    |  326 +++++
 0006-Allow-sssd-clients-to-reconnect.patch         |   36 +
 ...lean-up-initgroups-processing-for-RFC2307.patch | 1428 ++++++++++++++++++++
 ...-Fix-chpass-operations-with-LDAP-provider.patch |   28 +
 nobranch                                           |    1 -
 sssd.spec                                          |  147 ++-
 10 files changed, 2314 insertions(+), 27 deletions(-)
---
diff --git a/0001-Fixing-types-in-queue-and-stack-interfaces.patch b/0001-Fixing-types-in-queue-and-stack-interfaces.patch
new file mode 100644
index 0000000..a744fca
--- /dev/null
+++ b/0001-Fixing-types-in-queue-and-stack-interfaces.patch
@@ -0,0 +1,222 @@
+From 9cb35bdb61898024cf6ba35982ef71cbbc257acb Mon Sep 17 00:00:00 2001
+From: Dmitri Pal <dpal at redhat.com>
+Date: Sat, 19 Jun 2010 11:18:42 -0400
+Subject: [PATCH 1/2] Fixing types in queue and stack interfaces
+
+---
+ common/collection/Makefile.am        |    2 +-
+ common/collection/collection_queue.c |    8 ++++----
+ common/collection/collection_queue.h |    8 ++++----
+ common/collection/collection_stack.c |    8 ++++----
+ common/collection/collection_stack.h |    8 ++++----
+ common/collection/configure.ac       |    2 +-
+ contrib/sssd.spec.in                 |    5 ++---
+ 7 files changed, 20 insertions(+), 21 deletions(-)
+
+diff --git a/common/collection/Makefile.am b/common/collection/Makefile.am
+index b9975d1866b7b2dde78906da6616f00cd0c37cbe..4e90aed12a2ff877fdeaa80b58de556ff2ed4706 100644
+--- a/common/collection/Makefile.am
++++ b/common/collection/Makefile.am
+@@ -41,7 +41,7 @@ libcollection_la_SOURCES = \
+     collection_priv.h \
+     ../trace/trace.h
+ libcollection_la_LDFLAGS = \
+-    -version-info 1:0:0
++    -version-info 2:0:0
+ 
+ # Build unit test
+ check_PROGRAMS = collection_ut collection_stack_ut collection_queue_ut
+diff --git a/common/collection/collection_queue.c b/common/collection/collection_queue.c
+index 7e068339027ab47e6fb9c3804537ea243a6425a6..adac8ee672a7a9c8c85eaa6c375040cbbe3c6ade 100644
+--- a/common/collection/collection_queue.c
++++ b/common/collection/collection_queue.c
+@@ -108,7 +108,7 @@ int col_enqueue_binary_property(struct collection_item *queue,
+ /* Put an int property into a queue. */
+ int col_enqueue_int_property(struct collection_item *queue,
+                              const char *property,
+-                             int number)
++                             int32_t number)
+ {
+     int error = EOK;
+ 
+@@ -135,7 +135,7 @@ int col_enqueue_int_property(struct collection_item *queue,
+ /* Put an unsigned int property into a queue. */
+ int col_enqueue_unsigned_property(struct collection_item *queue,
+                                   const char *property,
+-                                  unsigned int number)
++                                  uint32_t number)
+ {
+     int error = EOK;
+ 
+@@ -163,7 +163,7 @@ int col_enqueue_unsigned_property(struct collection_item *queue,
+ /* Put a long property. */
+ int col_enqueue_long_property(struct collection_item *queue,
+                               const char *property,
+-                              long number)
++                              int64_t number)
+ {
+     int error = EOK;
+ 
+@@ -190,7 +190,7 @@ int col_enqueue_long_property(struct collection_item *queue,
+ /* Put an unsigned long property. */
+ int col_enqueue_ulong_property(struct collection_item *queue,
+                                const char *property,
+-                               unsigned long number)
++                               uint64_t number)
+ {
+     int error = EOK;
+ 
+diff --git a/common/collection/collection_queue.h b/common/collection/collection_queue.h
+index 2fe07489fd6f20d89ba804c81993f5db9e6cf4a9..86c392523d0fe36781fdce0c17d6cda88c878468 100644
+--- a/common/collection/collection_queue.h
++++ b/common/collection/collection_queue.h
+@@ -144,7 +144,7 @@ int col_enqueue_binary_property(struct collection_item *queue,
+  */
+ int col_enqueue_int_property(struct collection_item *queue,
+                              const char *property,
+-                             int number);
++                             int32_t number);
+ /**
+  * @brief Add unsigned value to the queue.
+  *
+@@ -167,7 +167,7 @@ int col_enqueue_int_property(struct collection_item *queue,
+  */
+ int col_enqueue_unsigned_property(struct collection_item *queue,
+                                   const char *property,
+-                                  unsigned int number);
++                                  uint32_t number);
+ /**
+  * @brief Add long integer value to the queue.
+  *
+@@ -190,7 +190,7 @@ int col_enqueue_unsigned_property(struct collection_item *queue,
+  */
+ int col_enqueue_long_property(struct collection_item *queue,
+                               const char *property,
+-                              long number);
++                              int64_t number);
+ /**
+  * @brief Add unsigned long value to the queue.
+  *
+@@ -213,7 +213,7 @@ int col_enqueue_long_property(struct collection_item *queue,
+  */
+ int col_enqueue_ulong_property(struct collection_item *queue,
+                                const char *property,
+-                               unsigned long number);
++                               uint64_t number);
+ /**
+  * @brief Add floating point value to the queue.
+  *
+diff --git a/common/collection/collection_stack.c b/common/collection/collection_stack.c
+index 503ada337d441a60ea5f08a972549b42f3b54b5e..509df511350f6cfd5b8ffcc9b949ada5f7961cff 100644
+--- a/common/collection/collection_stack.c
++++ b/common/collection/collection_stack.c
+@@ -106,7 +106,7 @@ int col_push_binary_property(struct collection_item *stack,
+ /* Push an int property to stack. */
+ int col_push_int_property(struct collection_item *stack,
+                           const char *property,
+-                          int number)
++                          int32_t number)
+ {
+     int error = EOK;
+ 
+@@ -133,7 +133,7 @@ int col_push_int_property(struct collection_item *stack,
+ /* Push an unsigned int property to stack. */
+ int col_push_unsigned_property(struct collection_item *stack,
+                                const char *property,
+-                               unsigned int number)
++                               uint32_t number)
+ {
+     int error = EOK;
+ 
+@@ -161,7 +161,7 @@ int col_push_unsigned_property(struct collection_item *stack,
+ /* Push a long property. */
+ int col_push_long_property(struct collection_item *stack,
+                            const char *property,
+-                           long number)
++                           int64_t number)
+ {
+     int error = EOK;
+ 
+@@ -188,7 +188,7 @@ int col_push_long_property(struct collection_item *stack,
+ /* Push an unsigned long property. */
+ int col_push_ulong_property(struct collection_item *stack,
+                             const char *property,
+-                            unsigned long number)
++                            uint64_t number)
+ {
+     int error = EOK;
+ 
+diff --git a/common/collection/collection_stack.h b/common/collection/collection_stack.h
+index e4be156ba4e26ee5f69ab39f3dd395a501d16c35..f9b01309fce8cb19b16083fcabe855521a6ced34 100644
+--- a/common/collection/collection_stack.h
++++ b/common/collection/collection_stack.h
+@@ -145,7 +145,7 @@ int col_push_binary_property(struct collection_item *stack,
+  */
+ int col_push_int_property(struct collection_item *stack,
+                           const char *property,
+-                          int number);
++                          int32_t number);
+ /**
+  * @brief Push unsigned value to the stack.
+  *
+@@ -168,7 +168,7 @@ int col_push_int_property(struct collection_item *stack,
+  */
+ int col_push_unsigned_property(struct collection_item *stack,
+                                const char *property,
+-                               unsigned int number);
++                               uint32_t number);
+ /**
+  * @brief Push long integer value to the stack.
+  *
+@@ -191,7 +191,7 @@ int col_push_unsigned_property(struct collection_item *stack,
+  */
+ int col_push_long_property(struct collection_item *stack,
+                            const char *property,
+-                           long number);
++                           int64_t number);
+ /**
+  * @brief Push unsigned long value to the stack.
+  *
+@@ -214,7 +214,7 @@ int col_push_long_property(struct collection_item *stack,
+  */
+ int col_push_ulong_property(struct collection_item *stack,
+                             const char *property,
+-                            unsigned long number);
++                            uint64_t number);
+ /**
+  * @brief Push floating point value to the stack.
+  *
+diff --git a/common/collection/configure.ac b/common/collection/configure.ac
+index 25aba1628dafe76a2422bc03abbae0c92cf99bbd..02c0ab4c30820d8dbfeae9eb83a88ffc5627bacc 100644
+--- a/common/collection/configure.ac
++++ b/common/collection/configure.ac
+@@ -1,5 +1,5 @@
+ AC_INIT([collection],
+-        [0.4.0],
++        [0.5.0],
+         [sssd-devel at lists.fedorahosted.org])
+ AC_CONFIG_SRCDIR([collection.c])
+ AC_CONFIG_AUX_DIR([build])
+diff --git a/contrib/sssd.spec.in b/contrib/sssd.spec.in
+index 6a69275dacd37725c1dec5b5bdd61ff3f357f710..cd5d440c6ca8599c84dd43a948358ee38e79786f 100644
+--- a/contrib/sssd.spec.in
++++ b/contrib/sssd.spec.in
+@@ -367,8 +367,8 @@ rm -rf $RPM_BUILD_ROOT
+ %defattr(-,root,root,-)
+ %doc common/collection/COPYING
+ %doc common/collection/COPYING.LESSER
+-%{_libdir}/libcollection.so.1
+-%{_libdir}/libcollection.so.1.0.0
++%{_libdir}/libcollection.so.2
++%{_libdir}/libcollection.so.2.0.0
+ 
+ %files -n libcollection-devel
+ %defattr(-,root,root,-)
+@@ -453,4 +453,3 @@ fi
+ %changelog
+ * Mon Mar 15 2010 Stephen Gallagher <sgallagh at redhat.com> - @PACKAGE_VERSION at -0@PRERELEASE_VERSION@
+ - Automated build of the SSSD
+-
+-- 
+1.7.0.1
+
diff --git a/0002-Fix-SASL-authentication.patch b/0002-Fix-SASL-authentication.patch
new file mode 100644
index 0000000..9551016
--- /dev/null
+++ b/0002-Fix-SASL-authentication.patch
@@ -0,0 +1,34 @@
+From d16e56334d01adf519e342dbd53ffcfe64130691 Mon Sep 17 00:00:00 2001
+From: Sumit Bose <sbose at redhat.com>
+Date: Fri, 25 Jun 2010 17:50:56 +0200
+Subject: [PATCH 2/2] Fix SASL authentication
+
+---
+ src/providers/ldap/sdap_async_connection.c |    4 ++--
+ 1 files changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/src/providers/ldap/sdap_async_connection.c b/src/providers/ldap/sdap_async_connection.c
+index 14a1a4b7d2989a1623159cb5ef3caa041d0d5d52..a0224a1c13feeacb738b0c8878817edbed625921 100644
+--- a/src/providers/ldap/sdap_async_connection.c
++++ b/src/providers/ldap/sdap_async_connection.c
+@@ -571,7 +571,7 @@ static int sdap_sasl_interact(LDAP *ld, unsigned flags,
+ 
+         switch (in->id) {
+         case SASL_CB_GETREALM:
+-        case SASL_CB_AUTHNAME:
++        case SASL_CB_USER:
+         case SASL_CB_PASS:
+             if (in->defresult) {
+                 in->result = in->defresult;
+@@ -580,7 +580,7 @@ static int sdap_sasl_interact(LDAP *ld, unsigned flags,
+             }
+             in->len = strlen(in->result);
+             break;
+-        case SASL_CB_USER:
++        case SASL_CB_AUTHNAME:
+             if (state->sasl_user) {
+                 in->result = state->sasl_user;
+             } else if (in->defresult) {
+-- 
+1.7.0.1
+
diff --git a/0003-Make-RootDSE-optional.patch b/0003-Make-RootDSE-optional.patch
new file mode 100644
index 0000000..4f3200f
--- /dev/null
+++ b/0003-Make-RootDSE-optional.patch
@@ -0,0 +1,63 @@
+From 4dea240ad51ba57e93dab34f7580de32981ae000 Mon Sep 17 00:00:00 2001
+From: Stephen Gallagher <sgallagh at redhat.com>
+Date: Thu, 24 Jun 2010 14:06:07 -0400
+Subject: [PATCH 3/3] Make RootDSE optional
+
+In violation of the standard, some LDAP servers control access to
+the RootDSE, thus preventing us from being able to read it before
+performing a bind.
+
+This patch will allow us to continue on if the RootDSE was
+inaccessible. All of the places that we use the return value of
+the RootDSE after this are already checked for NULL and use sane
+defaults if the RootDSE is unavailable
+---
+ src/providers/ldap/sdap_async.c            |    4 +++-
+ src/providers/ldap/sdap_async_connection.c |   16 ++++++++++++++--
+ 2 files changed, 17 insertions(+), 3 deletions(-)
+
+diff --git a/src/providers/ldap/sdap_async.c b/src/providers/ldap/sdap_async.c
+index 20828d2b79e72bc47b32d3eaa96b57e0e1945c2f..5c2e2e3e1a6baa8c3d26f38eb8594d3704a09953 100644
+--- a/src/providers/ldap/sdap_async.c
++++ b/src/providers/ldap/sdap_async.c
+@@ -678,7 +678,9 @@ static void sdap_get_rootdse_done(struct tevent_req *subreq)
+     }
+ 
+     if (num_results == 0 || !results) {
+-        DEBUG(2, ("No RootDSE for server ?!\n"));
++        DEBUG(2, ("RootDSE could not be retrieved. "
++                  "Please check that anonymous access to RootDSE is allowed\n"
++              ));
+         tevent_req_error(req, ENOENT);
+         return;
+     }
+diff --git a/src/providers/ldap/sdap_async_connection.c b/src/providers/ldap/sdap_async_connection.c
+index a0224a1c13feeacb738b0c8878817edbed625921..fd1cc8c72c99f754b27422ac1debccb8f75f2732 100644
+--- a/src/providers/ldap/sdap_async_connection.c
++++ b/src/providers/ldap/sdap_async_connection.c
+@@ -1052,8 +1052,20 @@ static void sdap_cli_rootdse_done(struct tevent_req *subreq)
+             return;
+         }
+ 
+-        tevent_req_error(req, ret);
+-        return;
++        else if (ret == ENOENT) {
++            /* RootDSE was not available on
++             * the server.
++             * Continue, and just assume that the
++             * features requested by the config
++             * work properly.
++             */
++            state->use_rootdse = false;
++        }
++
++        else {
++            tevent_req_error(req, ret);
++            return;
++        }
+     }
+ 
+     sasl_mech = dp_opt_get_string(state->opts->basic, SDAP_SASL_MECH);
+-- 
+1.7.0.1
+
diff --git a/0004-Add-explicit-requests-for-several-operational-attrs.patch b/0004-Add-explicit-requests-for-several-operational-attrs.patch
new file mode 100644
index 0000000..36192cf
--- /dev/null
+++ b/0004-Add-explicit-requests-for-several-operational-attrs.patch
@@ -0,0 +1,56 @@
+From 7d6b7aab01369b1c2c069f5fe45f39191d4e4df1 Mon Sep 17 00:00:00 2001
+From: Alexander Gordeev <lasaine at lvk.cs.msu.su>
+Date: Thu, 24 Jun 2010 16:18:13 +0400
+Subject: [PATCH 4/4] Add explicit requests for several operational attrs
+
+Operational attributes are not returned in searched requests unless
+explicitly requested according to RFC 4512 section 5.1. Therefore to
+get several standard attributes of root DSE we have to request for
+them. The requested attrs are:
+ - altServer
+ - namingContexts
+ - supportedControl
+ - supportedExtension
+ - supportedFeatures
+ - supportedLDAPVersion
+ - supportedSASLMechanisms
+
+Signed-off-by: Alexander Gordeev <lasaine at lvk.cs.msu.su>
+---
+ src/providers/ldap/sdap_async.c |   13 ++++++++++++-
+ 1 files changed, 12 insertions(+), 1 deletions(-)
+
+diff --git a/src/providers/ldap/sdap_async.c b/src/providers/ldap/sdap_async.c
+index 5c2e2e3e1a6baa8c3d26f38eb8594d3704a09953..18f2bc0c54344535c9ea97697142469e26ffe3c3 100644
+--- a/src/providers/ldap/sdap_async.c
++++ b/src/providers/ldap/sdap_async.c
+@@ -637,6 +637,17 @@ struct tevent_req *sdap_get_rootdse_send(TALLOC_CTX *memctx,
+ {
+     struct tevent_req *req, *subreq;
+     struct sdap_get_rootdse_state *state;
++    const char *attrs[] = {
++	    "*",
++	    "altServer",
++	    "namingContexts",
++	    "supportedControl",
++	    "supportedExtension",
++	    "supportedFeatures",
++	    "supportedLDAPVersion",
++	    "supportedSASLMechanisms",
++	    NULL
++    };
+ 
+     DEBUG(9, ("Getting rootdse\n"));
+ 
+@@ -650,7 +661,7 @@ struct tevent_req *sdap_get_rootdse_send(TALLOC_CTX *memctx,
+ 
+     subreq = sdap_get_generic_send(state, ev, opts, sh,
+                                    "", LDAP_SCOPE_BASE,
+-                                   "(objectclass=*)", NULL, NULL, 0);
++                                   "(objectclass=*)", attrs, NULL, 0);
+     if (!subreq) {
+         talloc_zfree(req);
+         return NULL;
+-- 
+1.7.0.1
+
diff --git a/0005-Add-sss_log-function.patch b/0005-Add-sss_log-function.patch
new file mode 100644
index 0000000..2ca3cb3
--- /dev/null
+++ b/0005-Add-sss_log-function.patch
@@ -0,0 +1,326 @@
+From 0e41d65f17d34bf2f7f12e56d595f026fdeab1d6 Mon Sep 17 00:00:00 2001
+From: Stephen Gallagher <sgallagh at redhat.com>
+Date: Thu, 8 Jul 2010 10:05:22 -0400
+Subject: [PATCH 5/5] Add sss_log() function
+
+Right now, this log function writes to the syslog. In the future,
+it could be modified to work with ELAPI or another logging API.
+
+Add log notifications for startup and shutdown.
+
+Add syslog messages for LDAP GSSAPI bind
+
+We will now emit a level 0 debug message on keytab errors, and
+also write to the syslog (LOG_DAEMON)
+
+Log TLS errors to syslog
+
+Also adds support for detecting LDAPS errors by adding a check for
+SDAP_DIAGNOSTIC_MESSAGE after ldap_search_ext()
+---
+ src/Makefile.am                            |    3 +-
+ src/providers/ldap/ldap_child.c            |   60 +++++++++++++++++++++++-
+ src/providers/ldap/sdap_async.c            |   18 +++++++-
+ src/providers/ldap/sdap_async_connection.c |    6 ++
+ src/util/server.c                          |    5 ++-
+ src/util/sss_log.c                         |   69 ++++++++++++++++++++++++++++
+ src/util/util.h                            |   12 +++++
+ 7 files changed, 168 insertions(+), 5 deletions(-)
+ create mode 100644 src/util/sss_log.c
+
+diff --git a/src/Makefile.am b/src/Makefile.am
+index d77c731722d9c74ac81f07baa933af3a807eb543..8eea7ac2dd74ca3db86375c92f1ddfbccfa44a65 100644
+--- a/src/Makefile.am
++++ b/src/Makefile.am
+@@ -218,7 +218,8 @@ AM_CPPFLAGS = -Wall \
+ EXTRA_DIST = build/config.rpath
+ 
+ SSSD_DEBUG_OBJ = \
+-    util/debug.c
++    util/debug.c \
++    util/sss_log.c
+ 
+ SSSD_UTIL_OBJ = \
+     confdb/confdb.c \
+diff --git a/src/providers/ldap/ldap_child.c b/src/providers/ldap/ldap_child.c
+index 3369d70984f696ca281b676d3f0af68ebc28a7ca..a2e658395d3ee4e3b72c2176b7cf480c4bb1d43b 100644
+--- a/src/providers/ldap/ldap_child.c
++++ b/src/providers/ldap/ldap_child.c
+@@ -136,6 +136,10 @@ static int ldap_child_get_tgt_sync(TALLOC_CTX *memctx,
+     krb5_creds my_creds;
+     krb5_get_init_creds_opt options;
+     krb5_error_code krberr;
++    krb5_kt_cursor cursor;
++    krb5_keytab_entry entry;
++    char *principal;
++    bool found;
+     int ret;
+ 
+     krberr = krb5_init_context(&context);
+@@ -200,8 +204,57 @@ static int ldap_child_get_tgt_sync(TALLOC_CTX *memctx,
+         krberr = krb5_kt_default(context, &keytab);
+     }
+     if (krberr) {
+-        DEBUG(2, ("Failed to read keytab file: %s\n",
++        DEBUG(0, ("Failed to read keytab file: %s\n",
+                   sss_krb5_get_error_message(context, krberr)));
++
++        ret = EFAULT;
++        goto done;
++    }
++
++    /* Verify the keytab */
++    krberr = krb5_kt_start_seq_get(context, keytab, &cursor);
++    if (krberr) {
++        DEBUG(0, ("Cannot read keytab [%s].\n", keytab_name));
++
++        sss_log(SSS_LOG_ERR, "Error reading keytab file [%s]: [%d][%s]. "
++                             "Unable to create GSSAPI-encrypted LDAP connection.",
++                             keytab_name, krberr,
++                             sss_krb5_get_error_message(context, krberr));
++
++        ret = EFAULT;
++        goto done;
++    }
++
++    found = false;
++    while((ret = krb5_kt_next_entry(context, keytab, &entry, &cursor)) == 0){
++        krb5_unparse_name(context, entry.principal, &principal);
++        if (strcmp(full_princ, principal) == 0) {
++            found = true;
++        }
++        free(principal);
++        krb5_free_keytab_entry_contents(context, &entry);
++
++        if (found) {
++            break;
++        }
++    }
++    krberr = krb5_kt_end_seq_get(context, keytab, &cursor);
++    if (krberr) {
++        DEBUG(0, ("Could not close keytab.\n"));
++        sss_log(SSS_LOG_ERR, "Could not close keytab file [%s].",
++                             keytab_name);
++        ret = EFAULT;
++        goto done;
++    }
++
++    if (!found) {
++        DEBUG(0, ("Principal [%s] not found in keytab [%s]\n",
++                  full_princ, keytab_name));
++        sss_log(SSS_LOG_ERR, "Error processing keytab file [%s]: "
++                             "Principal [%s] was not found. "
++                             "Unable to create GSSAPI-encrypted LDAP connection.",
++                             keytab_name, full_princ);
++
+         ret = EFAULT;
+         goto done;
+     }
+@@ -232,8 +285,11 @@ static int ldap_child_get_tgt_sync(TALLOC_CTX *memctx,
+                                         keytab, 0, NULL, &options);
+ 
+     if (krberr) {
+-        DEBUG(2, ("Failed to init credentials: %s\n",
++        DEBUG(0, ("Failed to init credentials: %s\n",
+                   sss_krb5_get_error_message(context, krberr)));
++        sss_log(SSS_LOG_ERR, "Failed to initialize credentials using keytab [%s]: %s. "
++                             "Unable to create GSSAPI-encrypted LDAP connection.",
++                             keytab_name, sss_krb5_get_error_message(context, krberr));
+         ret = EFAULT;
+         goto done;
+     }
+diff --git a/src/providers/ldap/sdap_async.c b/src/providers/ldap/sdap_async.c
+index 18f2bc0c54344535c9ea97697142469e26ffe3c3..fee3c11d053216c1eaf97842b437594d197e46c5 100644
+--- a/src/providers/ldap/sdap_async.c
++++ b/src/providers/ldap/sdap_async.c
+@@ -764,7 +764,9 @@ struct tevent_req *sdap_get_generic_send(TALLOC_CTX *memctx,
+ {
+     struct tevent_req *req = NULL;
+     struct sdap_get_generic_state *state = NULL;
++    char *errmsg;
+     int lret;
++    int optret;
+     int ret;
+     int msgid;
+ 
+@@ -805,7 +807,21 @@ struct tevent_req *sdap_get_generic_send(TALLOC_CTX *memctx,
+         DEBUG(3, ("ldap_search_ext failed: %s\n", ldap_err2string(lret)));
+         if (lret == LDAP_SERVER_DOWN) {
+             ret = ETIMEDOUT;
+-        } else {
++            optret = ldap_get_option(state->sh->ldap,
++                                     SDAP_DIAGNOSTIC_MESSAGE,
++                                     (void*)&errmsg);
++            if (optret == LDAP_SUCCESS) {
++                DEBUG(3, ("Connection error: %s\n", errmsg));
++                sss_log(SSS_LOG_ERR, "LDAP connection error: %s", errmsg);
++                ldap_memfree(errmsg);
++            }
++            else {
++                sss_log(SSS_LOG_ERR, "LDAP connection error, %s",
++                                     ldap_err2string(lret));
++            }
++        }
++
++        else {
+             ret = EIO;
+         }
+         goto fail;
+diff --git a/src/providers/ldap/sdap_async_connection.c b/src/providers/ldap/sdap_async_connection.c
+index fd1cc8c72c99f754b27422ac1debccb8f75f2732..69baf1a347edd84e60e327978ea69d5f583fddc1 100644
+--- a/src/providers/ldap/sdap_async_connection.c
++++ b/src/providers/ldap/sdap_async_connection.c
+@@ -153,11 +153,14 @@ struct tevent_req *sdap_connect_send(TALLOC_CTX *memctx,
+             DEBUG(3, ("ldap_start_tls failed: [%s] [%s]\n",
+                       ldap_err2string(lret),
+                       errmsg));
++            sss_log(SSS_LOG_ERR, "Could not start TLS. %s", errmsg);
+             ldap_memfree(errmsg);
+         }
+         else {
+             DEBUG(3, ("ldap_start_tls failed: [%s]\n",
+                       ldap_err2string(lret)));
++            sss_log(SSS_LOG_ERR, "Could not start TLS. "
++                                 "Check for certificate issues.");
+         }
+         goto fail;
+     }
+@@ -236,11 +239,14 @@ static void sdap_connect_done(struct sdap_op *op,
+             DEBUG(3, ("ldap_install_tls failed: [%s] [%s]\n",
+                       ldap_err2string(ret),
+                       tlserr));
++            sss_log(SSS_LOG_ERR, "Could not start TLS encryption. %s", tlserr);
+             ldap_memfree(tlserr);
+         }
+         else {
+             DEBUG(3, ("ldap_install_tls failed: [%s]\n",
+                       ldap_err2string(ret)));
++            sss_log(SSS_LOG_ERR, "Could not start TLS encryption. "
++                                 "Check for certificate issues.");
+         }
+ 
+         state->result = ret;
+diff --git a/src/util/server.c b/src/util/server.c
+index 4b65da102de11aa1e8a5a1334fdcfd6e1c0d05ce..446e2ea654f64251c20e08fa55643f7ada6f11b8 100644
+--- a/src/util/server.c
++++ b/src/util/server.c
+@@ -228,7 +228,8 @@ void sig_term(int sig)
+ 		kill(-getpgrp(), SIGTERM);
+ 	}
+ #endif
+-	exit(0);
++    sss_log(SSS_LOG_INFO, "Shutting down");
++    exit(0);
+ }
+ 
+ #ifndef HAVE_PRCTL
+@@ -460,6 +461,8 @@ int server_setup(const char *name, int flags,
+         }
+     }
+ 
++    sss_log(SSS_LOG_INFO, "Starting up");
++
+     DEBUG(3, ("CONFDB: %s\n", conf_db));
+ 
+     if (flags & FLAGS_INTERACTIVE) {
+diff --git a/src/util/sss_log.c b/src/util/sss_log.c
+new file mode 100644
+index 0000000000000000000000000000000000000000..45e883109a0a45a146b62dcedf43113147e78c28
+--- /dev/null
++++ b/src/util/sss_log.c
+@@ -0,0 +1,69 @@
++/*
++    SSSD
++
++    sss_log.c
++
++    Authors:
++        Stephen Gallagher <sgallagh at redhat.com>
++
++    Copyright (C) 2010 Red Hat
++
++    This program is free software; you can redistribute it and/or modify
++    it under the terms of the GNU General Public License as published by
++    the Free Software Foundation; either version 3 of the License, or
++    (at your option) any later version.
++
++    This program is distributed in the hope that it will be useful,
++    but WITHOUT ANY WARRANTY; without even the implied warranty of
++    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++    GNU General Public License for more details.
++
++    You should have received a copy of the GNU General Public License
++    along with this program.  If not, see <http://www.gnu.org/licenses/>.
++*/
++
++#include "util/util.h"
++#include <syslog.h>
++
++static int sss_to_syslog(int priority)
++{
++    switch(priority) {
++    case SSS_LOG_EMERG:
++        return LOG_EMERG;
++    case SSS_LOG_ALERT:
++        return LOG_ALERT;
++    case SSS_LOG_CRIT:
++        return LOG_CRIT;
++    case SSS_LOG_ERR:
++        return LOG_ERR;
++    case SSS_LOG_WARNING:
++        return LOG_WARNING;
++    case SSS_LOG_NOTICE:
++        return LOG_NOTICE;
++    case SSS_LOG_INFO:
++        return LOG_INFO;
++    case SSS_LOG_DEBUG:
++        return LOG_DEBUG;
++    default:
++        /* If we've been passed an invalid priority, it's
++         * best to assume it's an emergency.
++         */
++        return LOG_EMERG;
++    }
++}
++
++void sss_log(int priority, const char *format, ...)
++{
++    va_list ap;
++    int syslog_priority;
++
++    syslog_priority = sss_to_syslog(priority);
++
++    openlog(debug_prg_name, 0, LOG_DAEMON);
++
++    va_start(ap, format);
++    vsyslog(syslog_priority, format, ap);
++    va_end(ap);
++
++    closelog();
++}
+diff --git a/src/util/util.h b/src/util/util.h
+index 1277305e20694dd9f23392f9ed377e033e1df51f..3c95f7a2005f0fe815628a6985536c6fced24ada 100644
+--- a/src/util/util.h
++++ b/src/util/util.h
+@@ -212,6 +212,18 @@ int open_debug_file_ex(const char *filename, FILE **filep);
+ int open_debug_file(void);
+ int rotate_debug_files(void);
+ 
++/* From sss_log.c */
++#define SSS_LOG_EMERG   0   /* system is unusable */
++#define SSS_LOG_ALERT   1   /* action must be taken immediately */
++#define SSS_LOG_CRIT    2   /* critical conditions */
++#define SSS_LOG_ERR     3   /* error conditions */
++#define SSS_LOG_WARNING 4   /* warning conditions */
++#define SSS_LOG_NOTICE  5   /* normal but significant condition */
++#define SSS_LOG_INFO    6   /* informational */
++#define SSS_LOG_DEBUG   7   /* debug-level messages */
++
++void sss_log(int priority, const char *format, ...);
++
+ /* from server.c */
+ struct main_context {
+     struct tevent_context *event_ctx;
+-- 
+1.7.1.1
+
diff --git a/0006-Allow-sssd-clients-to-reconnect.patch b/0006-Allow-sssd-clients-to-reconnect.patch
new file mode 100644
index 0000000..1de0899
--- /dev/null
+++ b/0006-Allow-sssd-clients-to-reconnect.patch
@@ -0,0 +1,36 @@
+From 02e3bab1c6ec59080c00b3ca220dedb0dca481ae Mon Sep 17 00:00:00 2001
+From: Sumit Bose <sbose at redhat.com>
+Date: Fri, 23 Jul 2010 15:45:46 +0200
+Subject: [PATCH 6/6] Allow sssd clients to reconnect
+
+Currently the PAM and NSS client just return an error if there are
+problems on an open socket. This will lead to problems in long running
+programs like gdm if sssd is restarted, e.g. during an update. With this
+patch the socket is closed and reopened.
+---
+ src/sss_client/common.c |    7 +++----
+ 1 files changed, 3 insertions(+), 4 deletions(-)
+
+diff --git a/src/sss_client/common.c b/src/sss_client/common.c
+index a4856e0884e7151c7aaeaf9f40cae9e767a8a8e9..6b79c783074c2b1e847773e86db2e613d4b3cc63 100644
+--- a/src/sss_client/common.c
++++ b/src/sss_client/common.c
+@@ -607,12 +607,11 @@ static enum sss_status sss_cli_check_socket(int *errnop, const char *socket_name
+             *errnop = EBADF;
+             break;
+         }
+-        if (*errnop) {
+-            sss_cli_close_socket();
+-            return SSS_STATUS_UNAVAIL;
++        if (*errnop == 0) {
++            return SSS_STATUS_SUCCESS;
+         }
+ 
+-        return SSS_STATUS_SUCCESS;
++        sss_cli_close_socket();
+     }
+ 
+     mysd = sss_nss_open_socket(errnop, socket_name);
+-- 
+1.7.1.1
+
diff --git a/0007-Clean-up-initgroups-processing-for-RFC2307.patch b/0007-Clean-up-initgroups-processing-for-RFC2307.patch
new file mode 100644
index 0000000..4e9dc4d
--- /dev/null
+++ b/0007-Clean-up-initgroups-processing-for-RFC2307.patch
@@ -0,0 +1,1428 @@
+From 504899ef4150fc7f5f3ab53abbb8df95dc6713d3 Mon Sep 17 00:00:00 2001
+From: Stephen Gallagher <sgallagh at redhat.com>
+Date: Fri, 23 Jul 2010 13:20:40 -0400
+Subject: [PATCH 7/7] Clean up initgroups processing for RFC2307
+
+Instead of recursively updating all users of each group the user
+being queried belongs to, just add or remove membership for the
+requested user.
+
+Resolves: rhbz#617623
+---
+ src/Makefile.am                          |   14 ++-
+ src/db/sysdb.c                           |   97 +++++++++++++
+ src/db/sysdb.h                           |   18 +++
+ src/db/sysdb_ops.c                       |  191 +++++++++++++++++++++++++
+ src/providers/ldap/sdap_async_accounts.c |  212 ++++++++++++++++++++++++++--
+ src/tests/sysdb-tests.c                  |  199 ++++++++++++++++++++++++++
+ src/tests/util-tests.c                   |  227 ++++++++++++++++++++++++++++++
+ src/util/util.c                          |  227 ++++++++++++++++++++++++++++++
+ src/util/util.h                          |   19 +++
+ 9 files changed, 1187 insertions(+), 17 deletions(-)
+ create mode 100644 src/tests/util-tests.c
+
+diff --git a/src/Makefile.am b/src/Makefile.am
+index 8eea7ac2dd74ca3db86375c92f1ddfbccfa44a65..847e97ad374970e689073ad51c5d94d811e1f4ef 100644
+--- a/src/Makefile.am
++++ b/src/Makefile.am
+@@ -82,7 +82,8 @@ if HAVE_CHECK
+         find_uid-tests \
+         auth-tests \
+         ipa_ldap_opt-tests \
+-        simple_access-tests
++        simple_access-tests \
++        util-tests
+ endif
+ 
+ check_PROGRAMS = \
+@@ -678,6 +679,17 @@ simple_access_tests_LDADD = \
+     $(SSSD_LIBS) \
+     $(CHECK_LIBS)
+ 
++util_tests_SOURCES = \
++    tests/util-tests.c \
++    $(SSSD_UTIL_OBJ)
++util_tests_CFLAGS = \
++    $(AM_CFLAGS) \
++    $(CHECK_CFLAGS)
++util_tests_LDADD = \
++    $(SSSD_LIBS) \
++    $(CHECK_LIBS) \
++    libsss_test_common.la
++
+ endif
+ 
+ stress_tests_SOURCES = \
+diff --git a/src/db/sysdb.c b/src/db/sysdb.c
+index 41e1756c97d346a96003fd679962c2c6937a3e0c..f6996c4310857a0bb4b1eaa78874293e728d9490 100644
+--- a/src/db/sysdb.c
++++ b/src/db/sysdb.c
+@@ -52,6 +52,28 @@ struct ldb_dn *sysdb_group_dn(struct sysdb_ctx *ctx, void *memctx,
+     return ldb_dn_new_fmt(memctx, ctx->ldb, SYSDB_TMPL_GROUP, name, domain);
+ }
+ 
++errno_t sysdb_group_dn_name(struct sysdb_ctx *ctx, void *memctx,
++                            const char *_dn, char **_name)
++{
++    struct ldb_dn *dn;
++    *_name = NULL;
++
++    dn = ldb_dn_new_fmt(memctx, ctx->ldb, "%s", _dn);
++    if (dn == NULL) {
++        return ENOMEM;
++    }
++
++    *_name = talloc_strdup(memctx, ldb_dn_get_rdn_name(dn));
++    if (!_name) {
++        talloc_zfree(dn);
++        return ENOMEM;
++    }
++
++    talloc_zfree(dn);
++
++    return EOK;
++}
++
+ struct ldb_dn *sysdb_domain_dn(struct sysdb_ctx *ctx, void *memctx,
+                               const char *domain)
+ {
+@@ -1924,3 +1946,78 @@ int sysdb_attrs_replace_name(struct sysdb_attrs *attrs, const char *oldname,
+ 
+     return EOK;
+ }
++
++/* Search for all incidences of attr_name in a list of
++ * sysdb_attrs and add their value to a list
++ *
++ * TODO: Currently only works for single-valued
++ * attributes. Multi-valued attributes will return
++ * only the first entry
++ */
++errno_t sysdb_attrs_to_list(TALLOC_CTX *memctx,
++                            struct sysdb_attrs **attrs,
++                            int attr_count,
++                            const char *attr_name,
++                            char ***_list)
++{
++    int attr_idx;
++    int i;
++    char **list;
++    char **tmp_list;
++    int list_idx;
++
++    *_list = NULL;
++
++    /* Assume that every attrs entry contains the attr_name
++     * This may waste a little memory if some entries don't
++     * have the attribute, but it will save us the trouble
++     * of continuously resizing the array.
++     */
++    list = talloc_array(memctx, char *, attr_count+1);
++    if (!list) {
++        return ENOMEM;
++    }
++
++    list_idx = 0;
++    /* Loop through all entries in attrs */
++    for (attr_idx = 0; attr_idx < attr_count; attr_idx++) {
++        /* Examine each attribute within the entry */
++        for (i = 0; i < attrs[attr_idx]->num; i++) {
++            if (strcasecmp(attrs[attr_idx]->a->name, attr_name) == 0) {
++                /* Attribute name matches the requested name
++                 * Copy it to the output list
++                 */
++                list[list_idx] = talloc_strdup(
++                        list,
++                        (const char *)attrs[attr_idx]->a->values[0].data);
++                if (!list[list_idx]) {
++                    talloc_free(list);
++                    return ENOMEM;
++                }
++                list_idx++;
++
++                /* We only support single-valued attributes
++                 * Break here and go on to the next entry
++                 */
++                break;
++            }
++        }
++    }
++
++    list[list_idx] = NULL;
++
++    /* if list_idx < attr_count, do a realloc to
++     * reclaim unused memory
++     */
++    if (list_idx < attr_count) {
++        tmp_list = talloc_realloc(memctx, list, char *, list_idx+1);
++        if (!tmp_list) {
++            talloc_zfree(list);
++            return ENOMEM;
++        }
++        list = tmp_list;
++    }
++
++    *_list = list;
++    return EOK;
++}
+diff --git a/src/db/sysdb.h b/src/db/sysdb.h
+index 50427d613241592badb62f2b093cc5301479522b..04ce49e66c3d056b8b4a81375674816d9a91cc3e 100644
+--- a/src/db/sysdb.h
++++ b/src/db/sysdb.h
+@@ -200,6 +200,8 @@ struct ldb_dn *sysdb_user_dn(struct sysdb_ctx *ctx, void *memctx,
+                              const char *domain, const char *name);
+ struct ldb_dn *sysdb_group_dn(struct sysdb_ctx *ctx, void *memctx,
+                               const char *domain, const char *name);
++errno_t sysdb_group_dn_name(struct sysdb_ctx *ctx, void *memctx,
++                            const char *dn_str, char **name);
+ struct ldb_dn *sysdb_domain_dn(struct sysdb_ctx *ctx, void *memctx,
+                                const char *domain);
+ struct ldb_dn *sysdb_custom_dn(struct sysdb_ctx *ctx, void *memctx,
+@@ -535,6 +537,16 @@ struct tevent_req *sysdb_remove_group_member_send(TALLOC_CTX *mem_ctx,
+                                                   const char *member);
+ int sysdb_remove_group_member_recv(struct tevent_req *req);
+ 
++
++struct tevent_req * sysdb_update_members_send(TALLOC_CTX *mem_ctx,
++                                              struct tevent_context *ev,
++                                              struct sysdb_handle *handle,
++                                              struct sss_domain_info *domain,
++                                              char *user,
++                                              char **add_groups,
++                                              char **del_groups);
++errno_t sysdb_update_members_recv(struct tevent_req *req);
++
+ /* Password caching function.
+  * If you are in a transaction ignore sysdb and pass in the handle.
+  * If you are not in a transaction pass NULL in handle and provide sysdb,
+@@ -652,4 +664,10 @@ struct tevent_req *sysdb_delete_group_send(TALLOC_CTX *mem_ctx,
+                                            const char *name, gid_t gid);
+ int sysdb_delete_group_recv(struct tevent_req *req);
+ 
++errno_t sysdb_attrs_to_list(TALLOC_CTX *memctx,
++                            struct sysdb_attrs **attrs,
++                            int attr_count,
++                            const char *attr_name,
++                            char ***_list);
++
+ #endif /* __SYS_DB_H__ */
+diff --git a/src/db/sysdb_ops.c b/src/db/sysdb_ops.c
+index 6fcc95b19223ae9ee029519f54da524f16514c28..52102706c494e29c6cdda6fb9677d45226cfec04 100644
+--- a/src/db/sysdb_ops.c
++++ b/src/db/sysdb_ops.c
+@@ -5065,3 +5065,194 @@ int sysdb_cache_auth_recv(struct tevent_req *req, time_t *expire_date,
+ 
+     return (state->authentication_successful ? EOK : EINVAL);
+ }
++
++struct sysdb_update_members_ctx {
++    char *user;
++    struct sss_domain_info *domain;
++    struct tevent_context *ev;
++    struct sysdb_handle *handle;
++
++    char **add_groups;
++    int add_group_iter;
++
++    char **del_groups;
++    int del_group_iter;
++};
++
++static char **empty_string_list(TALLOC_CTX *mem_ctx)
++{
++    char **empty;
++    empty = talloc_array(mem_ctx, char *, 1);
++    if (!empty) {
++        return NULL;
++    }
++
++    empty[0] = NULL;
++
++    return empty;
++}
++
++static errno_t
++sysdb_update_members_step(struct tevent_req *req);
++
++struct tevent_req *sysdb_update_members_send(TALLOC_CTX *mem_ctx,
++                                             struct tevent_context *ev,
++                                             struct sysdb_handle *handle,
++                                             struct sss_domain_info *domain,
++                                             char *user,
++                                             char **add_groups,
++                                             char **del_groups)
++{
++    errno_t ret;
++    struct tevent_req *req;
++    struct sysdb_update_members_ctx *state;
++
++    req = tevent_req_create(mem_ctx, &state, struct sysdb_update_members_ctx);
++    if (!req) {
++        return NULL;
++    }
++
++    state->user = talloc_strdup(state, user);
++    if (!state->user) {
++        goto error;
++    }
++
++    state->domain = domain;
++    state->ev = ev;
++    state->handle = handle;
++
++    if (add_groups) {
++        state->add_groups = dup_string_list(state, (const char**)add_groups);
++    }
++    else {
++        state->add_groups = empty_string_list(state);
++    }
++    if (!state->add_groups) {
++        goto error;
++    }
++    state->add_group_iter = 0;
++
++    if (del_groups) {
++        state->del_groups = dup_string_list(state, (const char **)del_groups);
++    }
++    else {
++        state->del_groups = empty_string_list(state);
++    }
++    if (!state->del_groups) {
++        goto error;
++    }
++    state->del_group_iter = 0;
++
++    ret = sysdb_update_members_step(req);
++    if (ret != EOK) {
++        /* Nothing to do. Finish up */
++        tevent_req_error(req, ret);
++        tevent_req_post(req, state->ev);
++    }
++
++    return req;
++
++error:
++    talloc_free(req);
++    return NULL;
++}
++
++static void
++sysdb_update_members_add_done(struct tevent_req *subreq);
++static void
++sysdb_update_members_del_done(struct tevent_req *subreq);
++
++static errno_t
++sysdb_update_members_step(struct tevent_req *req)
++{
++    struct tevent_req *subreq;
++    struct sysdb_update_members_ctx *state;
++
++    state = tevent_req_data(req, struct sysdb_update_members_ctx);
++
++    if (state->add_groups[state->add_group_iter]) {
++        subreq = sysdb_add_group_member_send(
++                state, state->ev, state->handle,
++                state->domain,
++                state->add_groups[state->add_group_iter],
++                state->user);
++        if (!subreq) {
++            return EIO;
++        }
++
++        tevent_req_set_callback(subreq, sysdb_update_members_add_done, req);
++        return EOK;
++    }
++
++    if (state->del_groups[state->del_group_iter]) {
++        subreq = sysdb_remove_group_member_send(
++                state, state->ev,
++                state->handle, state->domain,
++                state->del_groups[state->del_group_iter],
++                state->user);
++        if (!subreq) {
++            return EIO;
++        }
++
++        tevent_req_set_callback(subreq, sysdb_update_members_del_done, req);
++        return EOK;
++    }
++
++    /* No more members to handle */
++    tevent_req_done(req);
++    return EOK;
++}
++
++static void
++sysdb_update_members_add_done(struct tevent_req *subreq)
++{
++    errno_t ret;
++    struct tevent_req *req =
++            tevent_req_callback_data(subreq, struct tevent_req);
++    struct sysdb_update_members_ctx *state =
++            tevent_req_data(req, struct sysdb_update_members_ctx);
++
++    ret = sysdb_add_group_member_recv(subreq);
++    talloc_zfree(subreq);
++    if(ret != EOK) {
++        tevent_req_error(req, ret);
++        return;
++    }
++
++    state->add_group_iter++;
++    ret = sysdb_update_members_step(req);
++    if (ret != EOK) {
++        tevent_req_error(req, ret);
++        return;
++    }
++}
++
++static void
++sysdb_update_members_del_done(struct tevent_req *subreq)
++{
++    errno_t ret;
++    struct tevent_req *req =
++            tevent_req_callback_data(subreq, struct tevent_req);
++    struct sysdb_update_members_ctx *state =
++            tevent_req_data(req, struct sysdb_update_members_ctx);
++
++    ret = sysdb_remove_group_member_recv(subreq);
++    talloc_zfree(subreq);
++    if(ret != EOK) {
++        tevent_req_error(req, ret);
++        return;
++    }
++
++    state->del_group_iter++;
++    ret = sysdb_update_members_step(req);
++    if (ret != EOK) {
++        tevent_req_error(req, ret);
++        return;
++    }
++}
++
++errno_t
++sysdb_update_members_recv(struct tevent_req *req)
++{
++    return sysdb_op_default_recv(req);
++}
+diff --git a/src/providers/ldap/sdap_async_accounts.c b/src/providers/ldap/sdap_async_accounts.c
+index 384936db400c264b792b55cfd775880cbdc4f2e9..beab326a05fe429a56d4eee78590ed78a58279c8 100644
+--- a/src/providers/ldap/sdap_async_accounts.c
++++ b/src/providers/ldap/sdap_async_accounts.c
+@@ -1485,12 +1485,17 @@ struct sdap_initgr_rfc2307_state {
+     struct sdap_options *opts;
+     struct sss_domain_info *dom;
+     struct sdap_handle *sh;
++    char *name;
++
++    struct sysdb_handle *handle;
++    char **ldap_grouplist;
+ 
+     struct sdap_op *op;
+ };
+ 
+ static void sdap_initgr_rfc2307_process(struct tevent_req *subreq);
+-static void sdap_initgr_rfc2307_done(struct tevent_req *subreq);
++
++static
+ struct tevent_req *sdap_initgr_rfc2307_send(TALLOC_CTX *memctx,
+                                             struct tevent_context *ev,
+                                             struct sdap_options *opts,
+@@ -1498,12 +1503,12 @@ struct tevent_req *sdap_initgr_rfc2307_send(TALLOC_CTX *memctx,
+                                             struct sss_domain_info *dom,
+                                             struct sdap_handle *sh,
+                                             const char *base_dn,
+-                                            const char *name,
+-                                            const char **grp_attrs)
++                                            const char *name)
+ {
+     struct tevent_req *req, *subreq;
+     struct sdap_initgr_rfc2307_state *state;
+     const char *filter;
++    const char *attrs[2];
+ 
+     req = tevent_req_create(memctx, &state, struct sdap_initgr_rfc2307_state);
+     if (!req) return NULL;
+@@ -1514,6 +1519,18 @@ struct tevent_req *sdap_initgr_rfc2307_send(TALLOC_CTX *memctx,
+     state->dom = dom;
+     state->sh = sh;
+     state->op = NULL;
++    state->name = talloc_strdup(state, name);
++    if (!state->name) {
++        talloc_zfree(req);
++        return NULL;
++    }
++
++    attrs[0] = talloc_strdup(state, opts->group_map[SDAP_AT_GROUP_NAME].name);
++    if (!attrs[0]) {
++        talloc_zfree(req);
++        return NULL;
++    }
++    attrs[1] = NULL;
+ 
+     filter = talloc_asprintf(state, "(&(%s=%s)(objectclass=%s))",
+                              opts->group_map[SDAP_AT_GROUP_MEMBER].name,
+@@ -1525,7 +1542,7 @@ struct tevent_req *sdap_initgr_rfc2307_send(TALLOC_CTX *memctx,
+ 
+     subreq = sdap_get_generic_send(state, state->ev, state->opts,
+                                    state->sh, base_dn, LDAP_SCOPE_SUBTREE,
+-                                   filter, grp_attrs,
++                                   filter, attrs,
+                                    state->opts->group_map, SDAP_OPTS_GROUP);
+     if (!subreq) {
+         talloc_zfree(req);
+@@ -1536,6 +1553,7 @@ struct tevent_req *sdap_initgr_rfc2307_send(TALLOC_CTX *memctx,
+     return req;
+ }
+ 
++static void sdap_initgr_rfc2307_get_sysdb_groups(struct tevent_req *subreq);
+ static void sdap_initgr_rfc2307_process(struct tevent_req *subreq)
+ {
+     struct tevent_req *req;
+@@ -1555,34 +1573,196 @@ static void sdap_initgr_rfc2307_process(struct tevent_req *subreq)
+     }
+ 
+     if (count == 0) {
++        /* No groups for this user in LDAP
++         * We need to ensure that there are no groups
++         * in the sysdb either.
++         */
++
++        state->ldap_grouplist = NULL;
++    }
++    else {
++        ret = sysdb_attrs_to_list(state, groups, count,
++                                  state->opts->group_map[SDAP_AT_GROUP_NAME].name,
++                                  &state->ldap_grouplist);
++        if (ret != EOK) {
++            tevent_req_error(req, ret);
++            return;
++        }
++    }
++    /* Start a transaction to look up the groups in the sysdb
++     * and update them with LDAP data
++     */
++
++    subreq = sysdb_transaction_send(state, state->ev, state->sysdb);
++    if (!subreq) {
++        tevent_req_error(req, EIO);
++        return;
++    }
++    tevent_req_set_callback(subreq,
++                            sdap_initgr_rfc2307_get_sysdb_groups,
++                            req);
++}
++
++static void sdap_initgr_rfc2307_update_sysdb_groups(struct tevent_req *subreq);
++static void sdap_initgr_rfc2307_get_sysdb_groups(struct tevent_req *subreq)
++{
++    struct tevent_req *req =
++            tevent_req_callback_data(subreq, struct tevent_req);
++    struct sdap_initgr_rfc2307_state *state =
++            tevent_req_data(req, struct sdap_initgr_rfc2307_state);
++    int ret;
++    const char **attrs;
++
++    ret = sysdb_transaction_recv(subreq, state, &state->handle);
++    talloc_zfree(subreq);
++    if (ret) {
++        tevent_req_error(req, ret);
++        return;
++    }
++
++    attrs = talloc_array(state, const char *, 2);
++    if (!attrs) {
++        tevent_req_error(req, ENOMEM);
++        return;
++    }
++    attrs[0] = SYSDB_MEMBEROF;
++    attrs[1] = NULL;
++
++    /* Search for all groups for which this user is a member */
++    subreq = sysdb_search_user_by_name_send(state, state->ev, state->sysdb,
++                                            state->handle, state->dom,
++                                            state->name, attrs);
++    if (!subreq) {
++        tevent_req_error(req, EIO);
++        return;
++    }
++
++    tevent_req_set_callback(subreq,
++                            sdap_initgr_rfc2307_update_sysdb_groups,
++                            req);
++}
++
++static void
++sdap_initgr_rfc2307_update_sysdb_groups_done(struct tevent_req *subreq);
++static void sdap_initgr_rfc2307_update_sysdb_groups(struct tevent_req *subreq)
++{
++    struct tevent_req *req =
++            tevent_req_callback_data(subreq, struct tevent_req);
++    struct sdap_initgr_rfc2307_state *state =
++            tevent_req_data(req, struct sdap_initgr_rfc2307_state);
++    int ret, i;
++    struct ldb_message *reply;
++    struct ldb_message_element *groups;
++    char **sysdb_grouplist;
++    char **add_groups;
++    char **del_groups;
++
++    ret = sysdb_search_user_recv(subreq, state, &reply);
++    talloc_zfree(subreq);
++    if (ret) {
++        tevent_req_error(req, ret);
++        return;
++    }
++
++    groups = ldb_msg_find_element(reply, SYSDB_MEMBEROF);
++    if (!groups || groups->num_values == 0) {
++        DEBUG(6, ("User is not a member of any groups\n"));
++
+         tevent_req_done(req);
+         return;
+     }
+ 
+-    subreq = sdap_save_groups_send(state, state->ev, state->dom,
+-                                   state->sysdb, state->opts,
+-                                   groups, count);
+-    if (!subreq) {
++    sysdb_grouplist = talloc_array(state, char *,
++                                          groups->num_values+1);
++    if (!sysdb_grouplist) {
+         tevent_req_error(req, ENOMEM);
+         return;
+     }
+-    tevent_req_set_callback(subreq, sdap_initgr_rfc2307_done, req);
++
++    /* Get a list of the groups by groupname only */
++    for (i=0; i < groups->num_values; i++) {
++         ret = sysdb_group_dn_name(state->sysdb,
++                                   sysdb_grouplist,
++                                   (const char *)groups->values[i].data,
++                                   &sysdb_grouplist[i]);
++         if (ret != EOK) {
++             tevent_req_error(req, ENOMEM);
++             return;
++         }
++    }
++
++    sysdb_grouplist[groups->num_values] = NULL;
++
++    /* Find the differences between the sysdb and ldap lists
++     * Groups in ldap only must be added to the sysdb;
++     * groups in the sysdb only must be removed.
++     */
++    ret = diff_string_lists(state,
++                            state->ldap_grouplist, sysdb_grouplist,
++                            &add_groups, &del_groups, NULL);
++    if (ret != EOK) {
++        tevent_req_error(req, ret);
++        return;
++    }
++
++    subreq = sysdb_update_members_send(state, state->ev, state->handle,
++                                       state->dom, state->name,
++                                       add_groups, del_groups);
++    if (!subreq) {
++        tevent_req_error(req, EIO);
++        return;
++    }
++
++    tevent_req_set_callback(subreq,
++                            sdap_initgr_rfc2307_update_sysdb_groups_done,
++                            req);
+ }
+ 
+-static void sdap_initgr_rfc2307_done(struct tevent_req *subreq)
++static void
++sdap_initgr_rfc2307_transaction_done(struct tevent_req *subreq);
++static void
++sdap_initgr_rfc2307_update_sysdb_groups_done(struct tevent_req *subreq)
+ {
+-    struct tevent_req *req;
+-    int ret;
++    errno_t ret;
++    struct tevent_req *req =
++            tevent_req_callback_data(subreq, struct tevent_req);
++    struct sdap_initgr_rfc2307_state *state =
++            tevent_req_data(req, struct sdap_initgr_rfc2307_state);
+ 
+-    req = tevent_req_callback_data(subreq, struct tevent_req);
++    ret = sysdb_update_members_recv(subreq);
++    talloc_zfree(subreq);
++    if (ret != EOK) {
++        tevent_req_error(req, ret);
++        return;
++    }
++
++    /* Commit the transaction */
++    subreq = sysdb_transaction_commit_send(state, state->ev, state->handle);
++    if (!subreq) {
++        tevent_req_error(req, EIO);
++        return;
++    }
++
++    tevent_req_set_callback(subreq,
++                            sdap_initgr_rfc2307_transaction_done,
++                            req);
++}
++
++static void
++sdap_initgr_rfc2307_transaction_done(struct tevent_req *subreq)
++{
++    errno_t ret;
++    struct tevent_req *req =
++            tevent_req_callback_data(subreq, struct tevent_req);
+ 
+-    ret = sdap_save_groups_recv(subreq, NULL, NULL);
++    ret = sysdb_transaction_commit_recv(subreq);
+     talloc_zfree(subreq);
+-    if (ret) {
++    if (ret != EOK) {
+         tevent_req_error(req, ret);
+         return;
+     }
+ 
++    /* Processing completed. Return control to sdap_get_initgr_done() */
+     tevent_req_done(req);
+ }
+ 
+@@ -1988,7 +2168,7 @@ static void sdap_get_initgr_process(struct tevent_req *subreq)
+                                     state->sysdb, state->dom, state->sh,
+                                     dp_opt_get_string(state->opts->basic,
+                                                   SDAP_GROUP_SEARCH_BASE),
+-                                    state->name, state->grp_attrs);
++                                    state->name);
+         if (!subreq) {
+             tevent_req_error(req, ENOMEM);
+             return;
+diff --git a/src/tests/sysdb-tests.c b/src/tests/sysdb-tests.c
+index 8557be5ad126adec06b1605b0708c3d6ac6d5e19..5bdc005473041243b254461dc3931ee30ba0afd0 100644
+--- a/src/tests/sysdb-tests.c
++++ b/src/tests/sysdb-tests.c
+@@ -3124,6 +3124,200 @@ START_TEST (test_sysdb_memberof_check_memberuid_loop_without_group_5)
+ }
+ END_TEST
+ 
++START_TEST (test_sysdb_attrs_to_list)
++{
++    struct sysdb_attrs *attrs_list[3];
++    char **list;
++    errno_t ret;
++
++    TALLOC_CTX *test_ctx = talloc_new(NULL);
++
++    attrs_list[0] = sysdb_new_attrs(test_ctx);
++    sysdb_attrs_add_string(attrs_list[0], "test_attr", "attr1");
++    attrs_list[1] = sysdb_new_attrs(test_ctx);
++    sysdb_attrs_add_string(attrs_list[1], "test_attr", "attr2");
++    attrs_list[2] = sysdb_new_attrs(test_ctx);
++    sysdb_attrs_add_string(attrs_list[2], "nottest_attr", "attr3");
++
++    ret = sysdb_attrs_to_list(test_ctx, attrs_list, 3,
++                              "test_attr", &list);
++    fail_unless(ret == EOK, "sysdb_attrs_to_list failed with code %d", ret);
++
++    fail_unless(strcmp(list[0],"attr1") == 0, "Expected [attr1], got [%s]",
++                                              list[0]);
++    fail_unless(strcmp(list[1],"attr2") == 0, "Expected [attr2], got [%s]",
++                                              list[1]);
++    fail_unless(list[2] == NULL, "List should be NULL-terminated");
++
++    talloc_free(test_ctx);
++}
++END_TEST
++
++static void test_sysdb_update_members_add(struct tevent_req *req);
++START_TEST (test_sysdb_update_members)
++{
++    struct sysdb_test_ctx *test_ctx;
++    struct test_data *data;
++    struct tevent_req *req;
++    int ret;
++
++    /* Setup */
++    ret = setup_sysdb_tests(&test_ctx);
++    if (ret != EOK) {
++        fail("Could not set up the test");
++        return;
++    }
++
++    data = talloc_zero(test_ctx, struct test_data);
++    data->ctx = test_ctx;
++    data->ev = test_ctx->ev;
++
++    /* Start the transaction */
++    req = sysdb_transaction_send(data, data->ev, test_ctx->sysdb);
++    if (!req) {
++        ret = ENOMEM;
++    }
++
++    if (ret == EOK) {
++        tevent_req_set_callback(req, test_sysdb_update_members_add, data);
++
++        ret = test_loop(data);
++    }
++
++    fail_if(ret != EOK, "Could not test sysdb_update_members");
++    talloc_free(test_ctx);
++}
++END_TEST
++
++static void test_sysdb_update_members_add_del(struct tevent_req *req);
++static void test_sysdb_update_members_add(struct tevent_req *req)
++{
++    struct test_data *data = tevent_req_callback_data(req, struct test_data);
++    char **add_groups;
++    char *user;
++    errno_t ret;
++
++    ret = sysdb_transaction_recv(req, data, &data->handle);
++    talloc_zfree(req);
++    if (ret != EOK) {
++        DEBUG(0, ("Could not start transaction\n"));
++        test_return(data, ret);
++        return;
++    }
++
++    /* Add a user to two groups */
++    data->username = talloc_strdup(data, "testuser27000");
++    user = talloc_strdup(data, data->username);
++    add_groups = talloc_array(data, char *, 3);
++    add_groups[0] = talloc_strdup(data, "testgroup28001");
++    add_groups[1] = talloc_strdup(data, "testgroup28002");
++    add_groups[2] = NULL;
++
++    req = sysdb_update_members_send(data, data->ev, data->handle,
++                                    data->ctx->domain, user,
++                                    add_groups, NULL);
++    talloc_free(add_groups);
++    talloc_free(user);
++    if (!req) {
++        DEBUG(0, ("Could not add groups\n"));
++        test_return(data, EIO);
++        return;
++    }
++
++    tevent_req_set_callback(req, test_sysdb_update_members_add_del, data);
++}
++
++static void test_sysdb_update_members_del(struct tevent_req *req);
++static void test_sysdb_update_members_add_del(struct tevent_req *req)
++{
++    struct test_data *data = tevent_req_callback_data(req, struct test_data);
++    errno_t ret;
++    char **add_groups = NULL;
++    char **del_groups = NULL;
++    char *user;
++
++    ret = sysdb_update_members_recv(req);
++    talloc_zfree(req);
++    if (ret != EOK) {
++        DEBUG(0, ("Group addition failed [%d](%s)\n", ret, strerror(ret)));
++        test_return(data, ret);
++        return;
++    }
++
++    /* Remove a user from one group and add to another */
++    user = talloc_strdup(data, data->username);
++    del_groups = talloc_array(data, char *, 2);
++    del_groups[0] = talloc_strdup(del_groups, "testgroup28001");
++    del_groups[1] = NULL;
++    add_groups = talloc_array(data, char *, 2);
++    add_groups[0] = talloc_strdup(add_groups, "testgroup28003");
++    add_groups[1] = NULL;
++
++    req = sysdb_update_members_send(data, data->ev, data->handle,
++                                    data->ctx->domain, user,
++                                    add_groups, del_groups);
++    talloc_free(add_groups);
++    talloc_free(del_groups);
++    talloc_free(user);
++    if (!req) {
++        DEBUG(0, ("Could not add/del groups\n"));
++        test_return(data, EIO);
++        return;
++    }
++
++    tevent_req_set_callback(req, test_sysdb_update_members_del, data);
++}
++
++static void test_sysdb_update_members_done(struct tevent_req *req);
++static void test_sysdb_update_members_del(struct tevent_req *req)
++{
++    struct test_data *data = tevent_req_callback_data(req, struct test_data);
++    errno_t ret;
++    char **del_groups = NULL;
++    char *user;
++
++    ret = sysdb_update_members_recv(req);
++    talloc_zfree(req);
++    if (ret != EOK) {
++        DEBUG(0, ("Group replace failed [%d](%s)\n", ret, strerror(ret)));
++        test_return(data, EIO);
++        return;
++    }
++
++    /* Remove a user from one group and add to another */
++    user = talloc_strdup(data, data->username);
++    del_groups = talloc_array(data, char *, 3);
++    del_groups[0] = talloc_strdup(del_groups, "testgroup28002");
++    del_groups[1] = talloc_strdup(del_groups, "testgroup28003");
++    del_groups[2] = NULL;
++
++    req = sysdb_update_members_send(data, data->ev, data->handle,
++                                    data->ctx->domain, user,
++                                    NULL, del_groups);
++    talloc_free(del_groups);
++    talloc_free(user);
++    if (!req) {
++        DEBUG(0, ("Could not del groups\n"));
++        test_return(data, EIO);
++        return;
++    }
++
++    tevent_req_set_callback(req, test_sysdb_update_members_done, data);
++}
++
++static void test_sysdb_update_members_done(struct tevent_req *req)
++{
++    struct test_data *data = tevent_req_callback_data(req, struct test_data);
++    errno_t ret;
++
++    ret = sysdb_update_members_recv(req);
++    talloc_zfree(req);
++    if (ret != EOK) {
++        DEBUG(0, ("Group delete failed [%d](%s)\n", ret, strerror(ret)));
++    }
++    test_return(data, ret);
++}
++
+ Suite *create_sysdb_suite(void)
+ {
+     Suite *s = suite_create("sysdb");
+@@ -3148,6 +3342,9 @@ Suite *create_sysdb_suite(void)
+     /* test the change */
+     tcase_add_loop_test(tc_sysdb, test_sysdb_get_user_attr, 27000, 27010);
+ 
++    /* Add and remove users in a group with sysdb_update_members */
++    tcase_add_test(tc_sysdb, test_sysdb_update_members);
++
+     /* Remove the other half by gid */
+     tcase_add_loop_test(tc_sysdb, test_sysdb_remove_local_group_by_gid, 28000, 28010);
+ 
+@@ -3233,6 +3430,8 @@ Suite *create_sysdb_suite(void)
+ 
+     tcase_add_test(tc_sysdb, test_sysdb_attrs_replace_name);
+ 
++    tcase_add_test(tc_sysdb, test_sysdb_attrs_to_list);
++
+ /* Add all test cases to the test suite */
+     suite_add_tcase(s, tc_sysdb);
+ 
+diff --git a/src/tests/util-tests.c b/src/tests/util-tests.c
+new file mode 100644
+index 0000000000000000000000000000000000000000..d8d3800fd8c6b5b3e5725255e006922bed7ccb34
+--- /dev/null
++++ b/src/tests/util-tests.c
+@@ -0,0 +1,227 @@
++/*
++    SSSD
++
++    util-tests.c
++
++    Authors:
++        Stephen Gallagher <sgallagh at redhat.com>
++
++    Copyright (C) 2010 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 <popt.h>
++#include <talloc.h>
++#include <check.h>
++#include "util/util.h"
++#include "tests/common.h"
++
++START_TEST(test_diff_string_lists)
++{
++    TALLOC_CTX *test_ctx;
++    char **l1;
++    char **l2;
++    char **l3;
++    char **only_l1;
++    char **only_l2;
++    char **both;
++    int ret;
++
++    test_ctx = talloc_new(NULL);
++
++    /* Test with all values returned */
++    l1 = talloc_array(test_ctx, char *, 4);
++    l1[0] = talloc_strdup(l1, "a");
++    l1[1] = talloc_strdup(l1, "b");
++    l1[2] = talloc_strdup(l1, "c");
++    l1[3] = NULL;
++
++    l2 = talloc_array(test_ctx, char *, 4);
++    l2[0] = talloc_strdup(l1, "d");
++    l2[1] = talloc_strdup(l1, "c");
++    l2[2] = talloc_strdup(l1, "b");
++    l2[3] = NULL;
++
++    ret = diff_string_lists(test_ctx,
++                            l1, l2,
++                            &only_l1, &only_l2, &both);
++
++    fail_unless(ret == EOK, "diff_string_lists returned error [%d]", ret);
++    fail_unless(strcmp(only_l1[0], "a") == 0, "Missing \"a\" from only_l1");
++    fail_unless(only_l1[1] == NULL, "only_l1 not NULL-terminated");
++    fail_unless(strcmp(only_l2[0], "d") == 0, "Missing \"d\" from only_l2");
++    fail_unless(only_l2[1] == NULL, "only_l2 not NULL-terminated");
++    fail_unless(strcmp(both[0], "c") == 0, "Missing \"c\" from both");
++    fail_unless(strcmp(both[1], "b") == 0, "Missing \"b\" from both");
++    fail_unless(both[2] == NULL, "both not NULL-terminated");
++
++    talloc_zfree(only_l1);
++    talloc_zfree(only_l2);
++    talloc_zfree(both);
++
++    /* Test with restricted return values */
++    ret = diff_string_lists(test_ctx,
++                            l1, l2,
++                            &only_l1, &only_l2, NULL);
++
++    fail_unless(ret == EOK, "diff_string_lists returned error [%d]", ret);
++    fail_unless(strcmp(only_l1[0], "a") == 0, "Missing \"a\" from only_l1");
++    fail_unless(only_l1[1] == NULL, "only_l1 not NULL-terminated");
++    fail_unless(strcmp(only_l2[0], "d") == 0, "Missing \"d\" from only_l2");
++    fail_unless(only_l2[1] == NULL, "only_l2 not NULL-terminated");
++    fail_unless(both == NULL, "Nothing returned to both");
++
++    talloc_zfree(only_l1);
++    talloc_zfree(only_l2);
++    talloc_zfree(both);
++
++    ret = diff_string_lists(test_ctx,
++                            l1, l2,
++                            &only_l1, NULL, NULL);
++
++    fail_unless(ret == EOK, "diff_string_lists returned error [%d]", ret);
++    fail_unless(strcmp(only_l1[0], "a") == 0, "Missing \"a\" from only_l1");
++    fail_unless(only_l1[1] == NULL, "only_l1 not NULL-terminated");
++    fail_unless(only_l2 == NULL, "Nothing returned to only_l2");
++    fail_unless(both == NULL, "Nothing returned to both");
++
++    talloc_zfree(only_l1);
++    talloc_zfree(only_l2);
++    talloc_zfree(both);
++
++    ret = diff_string_lists(test_ctx,
++                            l1, l2,
++                            NULL, &only_l2, NULL);
++
++    fail_unless(ret == EOK, "diff_string_lists returned error [%d]", ret);
++    fail_unless(strcmp(only_l2[0], "d") == 0, "Missing \"d\" from only_l2");
++    fail_unless(only_l2[1] == NULL, "only_l2 not NULL-terminated");
++    fail_unless(only_l1 == NULL, "Nothing returned to only_l1");
++    fail_unless(both == NULL, "Nothing returned to both");
++
++    talloc_zfree(only_l1);
++    talloc_zfree(only_l2);
++    talloc_zfree(both);
++
++    /* Test with no overlap */
++    l3 = talloc_array(test_ctx, char *, 4);
++    l3[0] = talloc_strdup(l1, "d");
++    l3[1] = talloc_strdup(l1, "e");
++    l3[2] = talloc_strdup(l1, "f");
++    l3[3] = NULL;
++
++    ret = diff_string_lists(test_ctx,
++                            l1, l3,
++                            &only_l1, &only_l2, &both);
++
++    fail_unless(ret == EOK, "diff_string_lists returned error [%d]", ret);
++    fail_unless(strcmp(only_l1[0], "a") == 0, "Missing \"a\" from only_l1");
++    fail_unless(strcmp(only_l1[1], "b") == 0, "Missing \"b\" from only_l1");
++    fail_unless(strcmp(only_l1[2], "c") == 0, "Missing \"c\" from only_l1");
++    fail_unless(only_l1[3] == NULL, "only_l1 not NULL-terminated");
++    fail_unless(strcmp(only_l2[0], "d") == 0, "Missing \"f\" from only_l2");
++    fail_unless(strcmp(only_l2[1], "e") == 0, "Missing \"e\" from only_l2");
++    fail_unless(strcmp(only_l2[2], "f") == 0, "Missing \"d\" from only_l2");
++    fail_unless(only_l2[3] == NULL, "only_l2 not NULL-terminated");
++    fail_unless(both[0] == NULL, "both should have zero entries");
++
++    talloc_zfree(only_l1);
++    talloc_zfree(only_l2);
++    talloc_zfree(both);
++
++    /* Test with 100% overlap */
++    ret = diff_string_lists(test_ctx,
++                            l1, l1,
++                            &only_l1, &only_l2, &both);
++
++    fail_unless(ret == EOK, "diff_string_lists returned error [%d]", ret);
++    fail_unless(only_l1[0] == NULL, "only_l1 should have zero entries");
++    fail_unless(only_l2[0] == NULL, "only_l2 should have zero entries");
++    fail_unless(strcmp(both[0], "a") == 0, "Missing \"a\" from both");
++    fail_unless(strcmp(both[1], "b") == 0, "Missing \"b\" from both");
++    fail_unless(strcmp(both[2], "c") == 0, "Missing \"c\" from both");
++    fail_unless(both[3] == NULL, "both is not NULL-terminated");
++
++    talloc_zfree(only_l1);
++    talloc_zfree(only_l2);
++    talloc_zfree(both);
++
++    /* Test with no second list */
++    ret = diff_string_lists(test_ctx,
++                            l1, NULL,
++                            &only_l1, &only_l2, &both);
++
++    fail_unless(ret == EOK, "diff_string_lists returned error [%d]", ret);
++    fail_unless(strcmp(only_l1[0], "a") == 0, "Missing \"a\" from only_l1");
++    fail_unless(strcmp(only_l1[1], "b") == 0, "Missing \"b\" from only_l1");
++    fail_unless(strcmp(only_l1[2], "c") == 0, "Missing \"c\" from only_l1");
++    fail_unless(only_l1[3] == NULL, "only_l1 not NULL-terminated");
++    fail_unless(only_l2[0] == NULL, "only_l2 should have zero entries");
++    fail_unless(both[0] == NULL, "both should have zero entries");
++
++    talloc_free(test_ctx);
++}
++END_TEST
++
++Suite *util_suite(void)
++{
++    Suite *s = suite_create("util");
++
++    TCase *tc_util = tcase_create("util");
++
++    tcase_add_test (tc_util, test_diff_string_lists);
++    tcase_set_timeout(tc_util, 60);
++
++    suite_add_tcase (s, tc_util);
++
++    return s;
++}
++
++int main(int argc, const char *argv[])
++{
++    int opt;
++    int failure_count;
++    poptContext pc;
++    Suite *s = util_suite();
++    SRunner *sr = srunner_create (s);
++
++    struct poptOption long_options[] = {
++        POPT_AUTOHELP
++        SSSD_MAIN_OPTS
++        { NULL }
++    };
++
++    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);
++
++    tests_set_cwd();
++
++    srunner_run_all(sr, CK_ENV);
++    failure_count = srunner_ntests_failed (sr);
++    srunner_free (sr);
++    if (failure_count == 0) {
++        return EXIT_SUCCESS;
++    }
++    return  EXIT_FAILURE;
++}
+diff --git a/src/util/util.c b/src/util/util.c
+index 9a973122242bb814e370f9bb3c5b890dd4ebbc5b..10bfb647b898c2c3696bcc7dbb6559585c66bd8a 100644
+--- a/src/util/util.c
++++ b/src/util/util.c
+@@ -22,6 +22,7 @@
+ 
+ #include "talloc.h"
+ #include "util/util.h"
++#include "dhash.h"
+ 
+ /* split a string into an allocated array of strings.
+  * the separator is a string, and is case-sensitive.
+@@ -235,3 +236,229 @@ fail:
+     free_args(ret);
+     return NULL;
+ }
++
++char **dup_string_list(TALLOC_CTX *memctx, const char **str_list)
++{
++    int i = 0;
++    int j = 0;
++    char **dup_list;
++
++    if (!str_list) {
++        return NULL;
++    }
++
++    /* Find the size of the list */
++    while (str_list[i]) i++;
++
++    dup_list = talloc_array(memctx, char *, i+1);
++    if (!dup_list) {
++        return NULL;
++    }
++
++    /* Copy the elements */
++    for (j = 0; j < i; j++) {
++        dup_list[j] = talloc_strdup(dup_list, str_list[j]);
++        if (!dup_list[j]) {
++            talloc_free(dup_list);
++            return NULL;
++        }
++    }
++
++    /* NULL-terminate the list */
++    dup_list[i] = NULL;
++
++    return dup_list;
++}
++
++/* Take two string lists (terminated on a NULL char*)
++ * and return up to three arrays of strings based on
++ * shared ownership.
++ *
++ * Pass NULL to any return type you don't care about
++ */
++errno_t diff_string_lists(TALLOC_CTX *memctx,
++                          char **_list1,
++                          char **_list2,
++                          char ***_list1_only,
++                          char ***_list2_only,
++                          char ***_both_lists)
++{
++    int error;
++    errno_t ret;
++    int i;
++    int i2 = 0;
++    int i12 = 0;
++    hash_table_t *table;
++    hash_key_t key;
++    hash_value_t value;
++    char **list1 = NULL;
++    char **list2 = NULL;
++    char **list1_only = NULL;
++    char **list2_only = NULL;
++    char **both_lists = NULL;
++    unsigned long count;
++    hash_key_t *keys;
++
++    TALLOC_CTX *tmp_ctx = talloc_new(memctx);
++    if (!tmp_ctx) {
++        return ENOMEM;
++    }
++
++    if (!_list1) {
++        list1 = talloc_array(tmp_ctx, char *, 1);
++        if (!list1) {
++            talloc_free(tmp_ctx);
++            return ENOMEM;
++        }
++        list1[0] = NULL;
++    }
++    else {
++        list1 = _list1;
++    }
++
++    if (!_list2) {
++        list2 = talloc_array(tmp_ctx, char *, 1);
++        if (!list2) {
++            talloc_free(tmp_ctx);
++            return ENOMEM;
++        }
++        list2[0] = NULL;
++    }
++    else {
++        list2 = _list2;
++    }
++
++    error = hash_create(10, &table, NULL, NULL);
++    if (error != HASH_SUCCESS) {
++        talloc_free(tmp_ctx);
++        return EIO;
++    }
++
++    key.type = HASH_KEY_STRING;
++    value.type = HASH_VALUE_UNDEF;
++
++    /* Add all entries from list 1 into a hash table */
++    i = 0;
++    while (list1[i]) {
++        key.str = talloc_strdup(tmp_ctx, list1[i]);
++        error = hash_enter(table, &key, &value);
++        if (error != HASH_SUCCESS) {
++            ret = EIO;
++            goto done;
++        }
++        i++;
++    }
++
++    /* Iterate through list 2 and remove matching items */
++    i = 0;
++    while (list2[i]) {
++        key.str = talloc_strdup(tmp_ctx, list2[i]);
++        error = hash_delete(table, &key);
++        if (error == HASH_SUCCESS) {
++            if (_both_lists) {
++                /* String was present in both lists */
++                i12++;
++                both_lists = talloc_realloc(tmp_ctx, both_lists, char *, i12+1);
++                if (!both_lists) {
++                    ret = ENOMEM;
++                    goto done;
++                }
++                both_lists[i12-1] = talloc_strdup(both_lists, list2[i]);
++                if (!both_lists[i12-1]) {
++                    ret = ENOMEM;
++                    goto done;
++                }
++
++                both_lists[i12] = NULL;
++            }
++        }
++        else if (error == HASH_ERROR_KEY_NOT_FOUND) {
++            if (_list2_only) {
++                /* String was present only in list2 */
++                i2++;
++                list2_only = talloc_realloc(tmp_ctx, list2_only,
++                                            char *, i2+1);
++                if (!list2_only) {
++                    ret = ENOMEM;
++                    goto done;
++                }
++                list2_only[i2-1] = talloc_strdup(list2_only, list2[i]);
++                if (!list2_only[i2-1]) {
++                    ret = ENOMEM;
++                    goto done;
++                }
++
++                list2_only[i2] = NULL;
++            }
++        }
++        else {
++            /* An error occurred */
++            ret = EIO;
++            goto done;
++        }
++        i++;
++    }
++
++    /* Get the leftover entries in the hash table */
++    if (_list1_only) {
++        error = hash_keys(table, &count, &keys);
++        if (error != HASH_SUCCESS) {
++            ret = EIO;
++            goto done;
++        }
++
++        list1_only = talloc_array(tmp_ctx, char *, count+1);
++        if (!list1_only) {
++            ret = ENOMEM;
++            goto done;
++        }
++
++        for (i = 0; i < count; i++) {
++            list1_only[i] = talloc_strdup(list1_only, keys[i].str);
++            if (!list1_only[i]) {
++                ret = ENOMEM;
++                goto done;
++            }
++        }
++        list1_only[count] = NULL;
++
++        free(keys);
++
++        *_list1_only = talloc_steal(memctx, list1_only);
++    }
++
++    if (_list2_only) {
++        if (list2_only) {
++            *_list2_only = talloc_steal(memctx, list2_only);
++        }
++        else {
++            *_list2_only = talloc_array(memctx, char *, 1);
++            if (!(*_list2_only)) {
++                ret = ENOMEM;
++                goto done;
++            }
++            *_list2_only[0] = NULL;
++        }
++    }
++
++    if (_both_lists) {
++        if (both_lists) {
++            *_both_lists = talloc_steal(memctx, both_lists);
++        }
++        else {
++            *_both_lists = talloc_array(memctx, char *, 1);
++            if (!(*_both_lists)) {
++                ret = ENOMEM;
++                goto done;
++            }
++            *_both_lists[0] = NULL;
++        }
++    }
++
++    ret = EOK;
++
++done:
++    hash_destroy(table);
++    talloc_free(tmp_ctx);
++    return ret;
++}
+diff --git a/src/util/util.h b/src/util/util.h
+index 3c95f7a2005f0fe815628a6985536c6fced24ada..6bcc9984db06592e3c9092842feab5f5fdbcadbf 100644
+--- a/src/util/util.h
++++ b/src/util/util.h
+@@ -331,4 +331,23 @@ int split_on_separator(TALLOC_CTX *mem_ctx, const char *str,
+                        const char sep, bool trim, char ***_list, int *size);
+ 
+ char **parse_args(const char *str);
++
++
++/* Copy a NULL-terminated string list
++ * Returns NULL on out of memory error or invalid input
++ */
++char **dup_string_list(TALLOC_CTX *memctx, const char **str_list);
++
++/* Take two string lists (terminated on a NULL char*)
++ * and return up to three arrays of strings based on
++ * shared ownership.
++ *
++ * Pass NULL to any return type you don't care about
++ */
++errno_t diff_string_lists(TALLOC_CTX *memctx,
++                          char **string1,
++                          char **string2,
++                          char ***string1_only,
++                          char ***string2_only,
++                          char ***both_strings);
+ #endif /* __SSSD_UTIL_H__ */
+-- 
+1.7.2
+
diff --git a/0008-Fix-chpass-operations-with-LDAP-provider.patch b/0008-Fix-chpass-operations-with-LDAP-provider.patch
new file mode 100644
index 0000000..d56650f
--- /dev/null
+++ b/0008-Fix-chpass-operations-with-LDAP-provider.patch
@@ -0,0 +1,28 @@
+From 5618e8d8c614166386cfd872c7fedc76c4bc579a Mon Sep 17 00:00:00 2001
+From: Stephen Gallagher <sgallagh at redhat.com>
+Date: Wed, 4 Aug 2010 13:42:06 -0400
+Subject: [PATCH 8/8] Fix chpass operations with LDAP provider
+
+The initial verification of the old password was returning an
+error because we were not explicitly setting dp_err to
+DP_ERR_SUCCESS and it was initialized earlier in the function to
+DP_ERR_FATAL.
+---
+ src/providers/ldap/ldap_auth.c |    1 +
+ 1 files changed, 1 insertions(+), 0 deletions(-)
+
+diff --git a/src/providers/ldap/ldap_auth.c b/src/providers/ldap/ldap_auth.c
+index 63719c0730707b1718dd36b9f5db76f1f3f8aebe..b05e3075ce117fad17b87ffde257c80fc035b8c4 100644
+--- a/src/providers/ldap/ldap_auth.c
++++ b/src/providers/ldap/ldap_auth.c
+@@ -865,6 +865,7 @@ static void sdap_auth4chpass_done(struct tevent_req *req)
+         DEBUG(9, ("Initial authentication for change password operation "
+                   "successful.\n"));
+         state->pd->pam_status = PAM_SUCCESS;
++        dp_err = DP_ERR_OK;
+         goto done;
+     }
+ 
+-- 
+1.7.2
+
diff --git a/sssd.spec b/sssd.spec
index a205047..b7719d3 100644
--- a/sssd.spec
+++ b/sssd.spec
@@ -7,7 +7,7 @@ Name: sssd
 Version: 1.2.1
 #Never reset the Release, always increment it
 #Otherwise we can have issues if library versions do not change
-Release: 17%{?dist}
+Release: 26%{?dist}
 Group: Applications/System
 Summary: System Security Services Daemon
 License: GPLv3+
@@ -17,20 +17,28 @@ BuildRoot: %(mktemp -ud %{_tmppath}/%{name}-%{version}-%{release}-XXXXXX)
 
 %global dhash_version 0.4.0
 %global path_utils_version 0.2.0
-%global collection_version 0.4.0
-%global ini_config_version 0.5.0
+%global collection_version 0.5.0
+%global ini_config_version 0.5.1
 %global refarray_version 0.1.0
 
 ### Patches ###
+Patch0001: 0001-Fixing-types-in-queue-and-stack-interfaces.patch
+Patch0002: 0002-Fix-SASL-authentication.patch
+Patch0003: 0003-Make-RootDSE-optional.patch
+Patch0004: 0004-Add-explicit-requests-for-several-operational-attrs.patch
+Patch0005: 0005-Add-sss_log-function.patch
+Patch0006: 0006-Allow-sssd-clients-to-reconnect.patch
+Patch0007: 0007-Clean-up-initgroups-processing-for-RFC2307.patch
+Patch0008: 0008-Fix-chpass-operations-with-LDAP-provider.patch
 
 ### Dependencies ###
 
 Requires: libldb >= 0.9.3
 Requires: libtdb >= 1.1.3
 Requires: sssd-client = %{version}-%{release}
-Requires: libdhash = %{dhash_version}-%{release}
-Requires: libcollection = %{collection_version}-%{release}
-Requires: libini_config = %{ini_config_version}-%{release}
+Requires: libdhash = %{dhash_version}
+Requires: libcollection = %{collection_version}
+Requires: libini_config = %{ini_config_version}
 Requires: cyrus-sasl-gssapi
 Requires: keyutils-libs
 Requires(post): python
@@ -48,6 +56,7 @@ Requires(postun): /sbin/service
 BuildRequires: autoconf
 BuildRequires: automake
 BuildRequires: libtool
+BuildRequires: gettext-devel
 BuildRequires: m4
 %{?fedora:BuildRequires: popt-devel}
 %if 0%{?rhel} <= 5
@@ -161,7 +170,7 @@ and serialization
 Summary: INI file parser for C
 Group: Development/Libraries
 Version: %{ini_config_version}
-Requires: libcollection = %{collection_version}-%{release}
+Requires: libcollection = %{collection_version}
 License: LGPLv3+
 
 %description -n libini_config
@@ -200,6 +209,21 @@ A dynamically-growing, reference-counted array
 
 %prep
 %setup -q
+%patch0001 -p1
+%patch0002 -p1
+%patch0003 -p1
+%patch0004 -p1
+%patch0005 -p1
+%patch0006 -p1
+%patch0007 -p1
+%patch0008 -p1
+
+# RHEL 5 uses an old libtool, so we need to reconfigure
+for i in libtool.m4  lt~obsolete.m4  ltoptions.m4  ltsugar.m4  ltversion.m4
+do
+    find . -name $i -exec rm -f {} \;
+done
+autoreconf -ivf
 
 %build
 %configure \
@@ -209,7 +233,7 @@ A dynamically-growing, reference-counted array
     --with-init-dir=%{_initrddir} \
     --enable-nsslibdir=/%{_lib} \
     --disable-static \
-    --disable-rpath
+    --disable-rpath \
 
 make %{?_smp_mflags}
 
@@ -218,7 +242,9 @@ make %{?_smp_mflags} docs
 popd
 
 %check
+export CK_TIMEOUT_MULTIPLIER=10
 make %{?_smp_mflags} check
+unset CK_TIMEOUT_MULTIPLIER
 
 %install
 rm -rf $RPM_BUILD_ROOT
@@ -369,8 +395,8 @@ rm -rf $RPM_BUILD_ROOT
 %defattr(-,root,root,-)
 %doc common/collection/COPYING
 %doc common/collection/COPYING.LESSER
-%{_libdir}/libcollection.so.1
-%{_libdir}/libcollection.so.1.0.0
+%{_libdir}/libcollection.so.2
+%{_libdir}/libcollection.so.2.0.0
 
 %files -n libcollection-devel
 %defattr(-,root,root,-)
@@ -420,6 +446,10 @@ if [ $1 -ge 2 ] ; then
     python %{_libexecdir}/%{servicename}/upgrade_config.py
 fi
 
+if [ $1 -ge 1 ] ; then
+    /sbin/service %{servicename} condrestart 2>&1 > /dev/null
+fi
+
 %preun
 if [ $1 = 0 ]; then
     /sbin/service %{servicename} stop 2>&1 > /dev/null
@@ -453,33 +483,98 @@ fi
 %postun -n libref_array -p /sbin/ldconfig
 
 %changelog
-* Mon Jun 21 2010 Stephen Gallagher <sgallagh at redhat.com> - 1.2.1-17
-- New stable upstream version 1.2.1
-- Resolves: rhbz#595529 - spec file should eschew %%define in favor of
--                         %%global
-- Resolves: rhbz#593644 - Empty list of simple_allow_users causes sssd service
--                         to fail while restart.
-- Resolves: rhbz#599026 - Makefile typo causes SSSD not to use the kernel
--                         keyring
-- Resolves: rhbz#599724 - sssd is broken on Rawhide
-
-* Sun Jun 06 2010 Stephen Gallagher <sgallagh at redhat.com> - 1.2.0-13
-- Retag to account for missing patch file.
-
-* Sun Jun 06 2010 Stephen Gallagher <sgallagh at redhat.com> - 1.2.0-12
+* Wed Aug 04 2010 Stephen Gallagher <sgallagh at redhat.com> - 1.2.1-26
+- Resolves: rhbz#621307 - Password changes are broken on LDAP
+
+* Wed Aug 04 2010 Stephen Gallagher <sgallagh at redhat.com> - 1.2.1-23.1
+- Initial release for Red Hat Enterprise Linux 5
+
+* Fri Jul 30 2010 Stephen Gallagher <sgallagh at redhat.com> - 1.2.1-23
+- Resolves: rhbz#617623 - SSSD suffers from serious performance issues on
+-                         initgroups calls
+
+* Fri Jul 23 2010 Stephen Gallagher <sgallagh at redhat.com> - 1.2.1-21
+- Resolves: rhbz#607233 - SSSD users cannot log in through GDM
+-                       - Real issue was that long-running services
+-                       - do not reconnect if sssd is restarted
+
+* Fri Jul 09 2010 Stephen Gallagher <sgallagh at redhat.com> - 1.2.1-20
+- Resolves: rhbz#591715 - sssd should emit warnings if there are problems with
+-                         /etc/krb5.keytab file
+
+* Mon Jun 28 2010 Stephen Gallagher <sgallagh at redhat.com> - 1.2.1-19
+- Resolves: rhbz#606836 - libcollection needs an soname bump before RHEL 6
+-                         final
+- Resolves: rhbz#608661 - SASL with OpenLDAP server fails
+- Resolves: rhbz#608688 - SSSD doesn't properly request RootDSE attributes
+
+* Fri Jun 18 2010 Stephen Gallagher <sgallagh at redhat.com> - 1.2.1-15
+- New upstream bugfix release 1.2.1
+- Resolves: rhbz#601770 - SSSD in RHEL 6.0 should ship with zero open Coverity
+-                         bugs.
+- Resolves: rhbz#603041 - Remove unnecessary option krb5_changepw_principal
+- Resolves: rhbz#604704 - authconfig should provide error with no trace back
+-                         if disabling sssd when sssd is not enabled
+- Resolves: rhbz#591873 - Connecting to the network after an offline kerberos
+-                         auth logs continuous error messages to sssd_ldap.log
+- Resolves: rhbz#596295 - Authentication fails for user from the second domain
+-                         when the same user name is filtered out from the
+-                         first domain
+- Related:  rhbz#598559 - Update translation files for SSSD before RHEL 6
+-                         final
+
+* Thu Jun 10 2010 Stephen Gallagher <sgallagh at redhat.com> - 1.2.0-14
+- Resolves: rhbz#593696 - Empty list of simple_allow_users causes sssd service
+-                         to fail while restart
+- Resolves: rhbz#600352 - Wrapping the value for "ldap_access_filter" in
+-                         parentheses causes ldap_search_ext to fail
+- Resolves: rhbz#600468 - Segfault in krb5_child
+- Related:  rhbz#601770 - SSSD in RHEL 6.0 should ship with zero open Coverity
+-                         bugs.
+
+* Wed Jun 02 2010 Stephen Gallagher <sgallagh at redhat.com> - 1.2.0-13
+- Resolves: rhbz#598670 - Ccache file of a user is removed too early
+- Resolves: rhbz#599057 - Incomplete comparison of a service name in
+-                         IPA access provider
+- Resolves: rhbz#598496 - Failure with IPA access provider
+- Resolves: rhbz#599027 - Makefile typo causes SSSD not to use the
+-                         kernel keyring
+
+* Mon May 24 2010 Stephen Gallagher <sgallagh at redhat.com> - 1.2.0-12
 - New stable upstream version 1.2.0
 - Support ServiceGroups for FreeIPA v2 HBAC rules
 - Fix long-standing issue with auth_provider = proxy
 - Better logging for TLS issues in LDAP
+- Resolves: rhbz#584001 - Rebase sssd to 1.2
+- Resolves: rhbz#584017 - Unconfiguring sssd leaves KDC locator file
+- Resolves: rhbz#587384 - authconfig fails if krb5_kpasswd in sssd.conf
+- Resolves: rhbz#587743 - Need to replicate pam_ldap's pam_filter in sssd.conf
+- Resolves: rhbz#590134 - sssd: auth_provider = proxy regression
+- Resolves: rhbz#591131 - Kerberos provider needs to rewrite kdcinfo file when
+-                         going online
+- Resolves: rhbz#591136 - Change SSSD ipa BE to handle new structure of the
+-                         HBAC rule
+
+* Wed May 19 2010 Stephen Gallagher <sgallagh at redhat.com> - 1.1.92-11.1
+- Improve DEBUG logs for STARTTLS failures
+
+* Tue May 18 2010 Stephen Gallagher <sgallagh at redhat.com> - 1.1.92-11
 - New LDAP access provider allows for filtering user access by LDAP attribute
 - Reduced default timeout for detecting offline status with LDAP
 - GSSAPI ticket lifetime made configurable
 - Better offline->online transition support in Kerberos
+
+* Fri May 07 2010 Stephen Gallagher <sgallagh at redhat.com> - 1.1.91-10
+- Release new upstream version 1.1.91
 - Enhancements when using SSSD with FreeIPA v2
 - Support for deferred kinit
 - Support for DNS SRV records for failover
 
-* Wed Apr 07 2010 Stephen Gallagher <sgallagh at redhat.com> - 1.1.1-5
+* Fri Apr 02 2010 Simo Sorce <ssorce at redhat.com> - 1.1.1-3
+- Bump up release number to avoid library sub-packages version issues with
+  previous releases.
+
+* Thu Apr 01 2010 Stephen Gallagher <sgallagh at redhat.com> - 1.1.1-1
 - New upstream release 1.1.1
 - Fixed the IPA provider (which was segfaulting at start)
 - Fixed a bug in the SSSDConfig API causing some options to revert to
@@ -487,7 +582,7 @@ fi
 - This impacted the Authconfig UI
 - Ensure that SASL binds to LDAP auto-retry when interrupted by a signal
 
-* Tue Mar 22 2010 Stephen Gallagher <sgallagh at redhat.com> - 1.1.0-4
+* Tue Mar 22 2010 Stephen Gallagher <sgallagh at redhat.com> - 1.1.0-2
 - Release SSSD 1.1.0 final
 - Fix two potential segfaults
 - Fix memory leak in monitor


More information about the scm-commits mailing list