>From 74c9224d76d12db776d76da341902c6af6ad61ed Mon Sep 17 00:00:00 2001
From: Jan Cholasta <jcholast@redhat.com>
Date: Thu, 22 Nov 2012 12:21:52 +0100
Subject: [PATCH] LDAP: If deref search fails, try again without deref

https://fedorahosted.org/sssd/ticket/1660
---
 src/providers/ipa/ipa_hosts.c              |  6 ++++++
 src/providers/ldap/sdap.h                  |  1 +
 src/providers/ldap/sdap_async.c            | 21 ++++++++++++++++++++-
 src/providers/ldap/sdap_async_groups.c     | 14 ++++++++++++--
 src/providers/ldap/sdap_async_initgroups.c | 12 +++++++++++-
 5 files changed, 50 insertions(+), 4 deletions(-)

diff --git a/src/providers/ipa/ipa_hosts.c b/src/providers/ipa/ipa_hosts.c
index 792af41..6da0170 100644
--- a/src/providers/ipa/ipa_hosts.c
+++ b/src/providers/ipa/ipa_hosts.c
@@ -256,6 +256,12 @@ ipa_host_info_done(struct tevent_req *subreq)
                 return;
             }
 
+            if (!sdap_has_deref_support(state->sh, state->opts)) {
+                DEBUG(SSSDBG_CRIT_FAILURE, ("Server does not support deref\n"));
+                tevent_req_error(req, EIO);
+                return;
+            }
+
             subreq = sdap_deref_search_send(state, state->ev, state->opts, state->sh,
                                             host_dn,
                                             state->hostgroup_map[IPA_AT_HOSTGROUP_MEMBER_OF].name,
diff --git a/src/providers/ldap/sdap.h b/src/providers/ldap/sdap.h
index d844ad6..dcb26ca 100644
--- a/src/providers/ldap/sdap.h
+++ b/src/providers/ldap/sdap.h
@@ -78,6 +78,7 @@ struct sdap_handle {
     /* Authentication ticket expiration time (if any) */
     time_t expire_time;
     ber_int_t page_size;
+    bool disable_deref;
 
     struct sdap_fd_events *sdap_fd_events;
 
diff --git a/src/providers/ldap/sdap_async.c b/src/providers/ldap/sdap_async.c
index e044062..7b47c0a 100644
--- a/src/providers/ldap/sdap_async.c
+++ b/src/providers/ldap/sdap_async.c
@@ -1388,6 +1388,10 @@ static void sdap_get_generic_ext_done(struct sdap_op *op,
             ldap_memfree(errmsg);
             tevent_req_error(req, EIO);
             return;
+        } else if (result == LDAP_UNAVAILABLE_CRITICAL_EXTENSION) {
+            ldap_memfree(errmsg);
+            tevent_req_error(req, ENOTSUP);
+            return;
         } else if (result != LDAP_SUCCESS && result != LDAP_NO_SUCH_OBJECT) {
             DEBUG(SSSDBG_OP_FAILURE,
                   ("Unexpected result from ldap: %s(%d), %s\n",
@@ -2058,6 +2062,7 @@ enum sdap_deref_type {
 };
 
 struct sdap_deref_search_state {
+    struct sdap_handle *sh;
     size_t reply_count;
     struct sdap_deref_attrs **reply;
     enum sdap_deref_type deref_type;
@@ -2084,6 +2089,7 @@ sdap_deref_search_send(TALLOC_CTX *memctx,
     req = tevent_req_create(memctx, &state, struct sdap_deref_search_state);
     if (!req) return NULL;
 
+    state->sh = sh;
     state->reply_count = 0;
     state->reply = NULL;
 
@@ -2148,7 +2154,16 @@ static void sdap_deref_search_done(struct tevent_req *subreq)
     talloc_zfree(subreq);
     if (ret != EOK) {
         DEBUG(2, ("dereference processing failed [%d]: %s\n", ret, strerror(ret)));
-        sss_log(SSS_LOG_WARNING, "dereference processing failed : %s", strerror(ret));
+        if (ret == ENOTSUP) {
+            sss_log(SSS_LOG_WARNING,
+                "LDAP server claims to support deref, but deref search failed. "
+                "Disabling deref for further requests. You can permanently "
+                "disable deref by setting ldap_deref_threshold to 0 in domain "
+                "configuration.");
+            state->sh->disable_deref = true;
+        } else {
+            sss_log(SSS_LOG_WARNING, "dereference processing failed : %s", strerror(ret));
+        }
         tevent_req_error(req, ret);
         return;
     }
@@ -2180,6 +2195,10 @@ bool sdap_has_deref_support(struct sdap_handle *sh, struct sdap_options *opts)
     int i;
     int deref_threshold;
 
+    if (sh->disable_deref) {
+        return false;
+    }
+
     deref_threshold = dp_opt_get_int(opts->basic, SDAP_DEREF_THRESHOLD);
     if (deref_threshold == 0) {
         return false;
diff --git a/src/providers/ldap/sdap_async_groups.c b/src/providers/ldap/sdap_async_groups.c
index c5d1fa1..fa5e45b 100644
--- a/src/providers/ldap/sdap_async_groups.c
+++ b/src/providers/ldap/sdap_async_groups.c
@@ -2160,7 +2160,7 @@ sdap_nested_get_user_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
             goto immediate;
         } else {
             DEBUG(SSSDBG_MINOR_FAILURE, ("Couldn't parse out user information "
-                  "based on DN %s, falling back to an LDAP lookup\n"));
+                  "based on DN %s, falling back to an LDAP lookup\n", user_dn));
         }
     }
 
@@ -3513,7 +3513,17 @@ static void sdap_nested_group_process_deref(struct tevent_req *subreq)
                                  &state->derefctx->num_results,
                                  &state->derefctx->deref_result);
     talloc_zfree(subreq);
-    if (ret != EOK && ret != ENOENT) {
+    if (ret == ENOTSUP) {
+        ret = sdap_nested_group_process_noderef(req);
+        if (ret != EAGAIN) {
+            if (ret == EOK) {
+                tevent_req_done(req);
+            } else {
+                tevent_req_error(req, ret);
+            }
+        }
+        return;
+    } else if (ret != EOK && ret != ENOENT) {
         tevent_req_error(req, ret);
         return;
     } else if (ret == ENOENT || state->derefctx->deref_result == NULL) {
diff --git a/src/providers/ldap/sdap_async_initgroups.c b/src/providers/ldap/sdap_async_initgroups.c
index 6936c7b..5f3fe1f 100644
--- a/src/providers/ldap/sdap_async_initgroups.c
+++ b/src/providers/ldap/sdap_async_initgroups.c
@@ -870,7 +870,17 @@ static void sdap_initgr_nested_deref_done(struct tevent_req *subreq)
                                  &num_results,
                                  &deref_result);
     talloc_zfree(subreq);
-    if (ret != EOK && ret != ENOENT) {
+    if (ret == ENOTSUP) {
+        ret = sdap_initgr_nested_noderef_search(req);
+        if (ret != EAGAIN) {
+            if (ret == EOK) {
+                tevent_req_done(req);
+            } else {
+                tevent_req_error(req, ret);
+            }
+        }
+        return;
+    } else if (ret != EOK && ret != ENOENT) {
         tevent_req_error(req, ret);
         return;
     } else if (ret == ENOENT || deref_result == NULL) {
-- 
1.8.0

