[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