From 21bd33a95d2b9b9fc08196f91df0f08abd3bd480 Mon Sep 17 00:00:00 2001
From: Stephen Gallagher <sgallagh@redhat.com>
Date: Fri, 1 May 2015 11:42:06 -0400
Subject: [PATCH 1/2] LDAP: Support returning referral information

Some callers may be interested in the raw referral values returned from
a lookup. This patch allows interested consumers to get these referrals
back and process them if they wish. It does not implement a generic
automatic following of referrals.
---
 src/providers/ldap/sdap_async.c | 134 +++++++++++++++++++++++++++++++++-------
 1 file changed, 112 insertions(+), 22 deletions(-)

diff --git a/src/providers/ldap/sdap_async.c b/src/providers/ldap/sdap_async.c
index 0d1f0195c0b96d44151487642d30b631063d617a..2ffc2a170c2277aa2ea40e84bb697c62542aa266 100644
--- a/src/providers/ldap/sdap_async.c
+++ b/src/providers/ldap/sdap_async.c
@@ -297,19 +297,14 @@ static void sdap_process_message(struct tevent_context *ev,
     DEBUG(SSSDBG_TRACE_ALL,
           "Message type: [%s]\n", sdap_ldap_result_str(msgtype));
 
     switch (msgtype) {
     case LDAP_RES_SEARCH_ENTRY:
+    case LDAP_RES_SEARCH_REFERENCE:
         /* go and process entry */
         break;
 
-    case LDAP_RES_SEARCH_REFERENCE:
-        /* more ops to come with this msgid */
-        /* just ignore */
-        ldap_msgfree(msg);
-        return;
-
     case LDAP_RES_BIND:
     case LDAP_RES_SEARCH_RESULT:
     case LDAP_RES_MODIFY:
     case LDAP_RES_ADD:
     case LDAP_RES_DELETE:
@@ -1154,10 +1149,13 @@ struct sdap_get_generic_ext_state {
 
     LDAPControl **serverctrls;
     int nserverctrls;
     LDAPControl **clientctrls;
 
+    size_t ref_count;
+    char **refs;
+
     sdap_parse_cb parse_cb;
     void *cb_data;
 
     bool allow_paging;
 };
@@ -1375,19 +1373,59 @@ static errno_t sdap_get_generic_ext_step(struct tevent_req *req)
 
 done:
     return ret;
 }
 
+static errno_t
+sdap_get_generic_ext_add_references(struct sdap_get_generic_ext_state *state,
+                                    char **refs)
+{
+    int i;
+
+    if (refs == NULL) {
+        /* Rare, but it's possible that we might get a reference result with
+         * no references attached.
+         */
+        return EOK;
+    }
+
+    for (i = 0; refs[i]; i++) {
+        DEBUG(SSSDBG_TRACE_LIBS, "Additional References: %s\n", refs[i]);
+    }
+
+    /* Extend the size of the ref array */
+    state->refs = talloc_realloc(state, state->refs, char *,
+                                 state->ref_count + i);
+    if (state->refs == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE,
+              "talloc_realloc failed extending ref_array.\n");
+        return ENOMEM;
+    }
+
+    /* Copy in all the references */
+    for (i = 0; refs[i]; i++) {
+        state->refs[state->ref_count + i] =
+                talloc_strdup(state->refs, refs[i]);
+
+        if (state->refs[state->ref_count + i] == NULL) {
+            return ENOMEM;
+        }
+    }
+
+    state->ref_count += i;
+
+    return EOK;
+}
+
 static void sdap_get_generic_op_finished(struct sdap_op *op,
                                          struct sdap_msg *reply,
                                          int error, void *pvt)
 {
     struct tevent_req *req = talloc_get_type(pvt, struct tevent_req);
     struct sdap_get_generic_ext_state *state = tevent_req_data(req,
                                             struct sdap_get_generic_ext_state);
     char *errmsg = NULL;
-    int i;
     char **refs = NULL;
     int result;
     int ret;
     int lret;
     ber_int_t total_count;
@@ -1400,12 +1438,31 @@ static void sdap_get_generic_op_finished(struct sdap_op *op,
         return;
     }
 
     switch (ldap_msgtype(reply->msg)) {
     case LDAP_RES_SEARCH_REFERENCE:
-        /* ignore references for now */
-        talloc_free(reply);
+        ret = ldap_parse_reference(state->sh->ldap, reply->msg,
+                                   &refs, NULL, 0);
+        if (ret != LDAP_SUCCESS) {
+            DEBUG(SSSDBG_OP_FAILURE,
+                  "ldap_parse_reference failed (%d)\n", state->op->msgid);
+            tevent_req_error(req, EIO);
+            return;
+        }
+
+        ret = sdap_get_generic_ext_add_references(state, refs);
+        if (ret != EOK) {
+            DEBUG(SSSDBG_OP_FAILURE,
+                  "sdap_get_generic_ext_add_references failed: %s(%d)",
+                  sss_strerror(ret), ret);
+            ldap_memvfree((void **)refs);
+            tevent_req_error(req, ret);
+            return;
+        }
+
+        /* Remove the original strings */
+        ldap_memvfree((void **)refs);
 
         /* unlock the operation so that we can proceed with the next result */
         sdap_unlock_next_reply(state->op);
         break;
 
@@ -1454,19 +1511,18 @@ static void sdap_get_generic_op_finished(struct sdap_op *op,
         } else if (result == LDAP_UNAVAILABLE_CRITICAL_EXTENSION) {
             ldap_memfree(errmsg);
             tevent_req_error(req, ENOTSUP);
             return;
         } else if (result == LDAP_REFERRAL) {
-            if (refs != NULL) {
-                for (i = 0; refs[i]; i++) {
-                    DEBUG(SSSDBG_TRACE_LIBS, "Ref: %s\n", refs[i]);
-                }
-                ldap_memvfree((void **) refs);
+            ret = sdap_get_generic_ext_add_references(state, refs);
+            if (ret != EOK) {
+                DEBUG(SSSDBG_OP_FAILURE,
+                      "sdap_get_generic_ext_add_references failed: %s(%d)",
+                      sss_strerror(ret), ret);
+                tevent_req_error(req, ret);
             }
-            ldap_memfree(errmsg);
-            tevent_req_error(req, ERR_REFERRAL);
-            return;
+            /* For referrals, we need to fall through as if it was LDAP_SUCCESS */
         } else if (result != LDAP_SUCCESS && result != LDAP_NO_SUCH_OBJECT) {
             DEBUG(SSSDBG_OP_FAILURE,
                   "Unexpected result from ldap: %s(%d), %s\n",
                    sss_ldap_err2string(result), result,
                    errmsg ? errmsg : "no errmsg set");
@@ -1532,30 +1588,63 @@ static void sdap_get_generic_op_finished(struct sdap_op *op,
         return;
     }
 }
 
 static int
-sdap_get_generic_ext_recv(struct tevent_req *req)
+sdap_get_generic_ext_recv(struct tevent_req *req,
+                          TALLOC_CTX *mem_ctx,
+                          size_t *ref_count,
+                          char ***refs)
 {
+    struct sdap_get_generic_ext_state *state =
+            tevent_req_data(req, struct sdap_get_generic_ext_state);
+
     TEVENT_REQ_RETURN_ON_ERROR(req);
+
+    if (ref_count) {
+        *ref_count = state->ref_count;
+    }
+
+    if (refs) {
+        *refs = talloc_steal(mem_ctx, state->refs);
+    }
+
     return EOK;
 }
 
+/* This search handler can be used by most calls */
 static void generic_ext_search_handler(struct tevent_req *subreq,
                                        struct sdap_options *opts)
 {
     struct tevent_req *req = tevent_req_callback_data(subreq,
                                                       struct tevent_req);
     int ret;
+    size_t ref_count, i;
+    char **refs;
 
-    ret = sdap_get_generic_ext_recv(subreq);
+    ret = sdap_get_generic_ext_recv(subreq, req, &ref_count, &refs);
     talloc_zfree(subreq);
-    if (ret == ERR_REFERRAL) {
+    if (ref_count > 0) {
         if (dp_opt_get_bool(opts->basic, SDAP_REFERRALS)) {
-            tevent_req_error(req, ret);
+            /* We got back referrals here, but they should have
+             * been processed internally by openldap libs.
+             * This should never happen.
+             */
+            talloc_free(refs);
+            tevent_req_error(req, EINVAL);
             return;
         }
+
+        /* We will ignore referrals in the generic handler */
+        DEBUG(SSSDBG_TRACE_ALL,
+              "Request included referrals which were ignored.\n");
+        if (debug_level & SSSDBG_TRACE_ALL) {
+            for(i = 0; i < ref_count; i++) {
+                DEBUG(SSSDBG_TRACE_ALL,
+                      "    Ref: %s\n", refs[i]);
+            }
+        }
     } else if (ret) {
         DEBUG(SSSDBG_OP_FAILURE,
               "sdap_get_generic_ext_recv failed [%d]: %s\n",
               ret, sss_strerror(ret));
         tevent_req_error(req, ret);
@@ -1563,10 +1652,11 @@ static void generic_ext_search_handler(struct tevent_req *subreq,
     }
 
     tevent_req_done(req);
 }
 
+
 /* ==Generic Search============================================ */
 struct sdap_get_generic_state {
     struct sdap_attr_map *map;
     int map_num_attrs;
 
@@ -2483,11 +2573,11 @@ static void sdap_posix_check_done(struct tevent_req *subreq)
                                                       struct tevent_req);
     struct sdap_posix_check_state *state =
         tevent_req_data(req, struct sdap_posix_check_state);
     errno_t ret;
 
-    ret = sdap_get_generic_ext_recv(subreq);
+    ret = sdap_get_generic_ext_recv(subreq, NULL, NULL, NULL);
     talloc_zfree(subreq);
     if (ret != EOK) {
         DEBUG(SSSDBG_OP_FAILURE,
               "sdap_get_generic_ext_recv failed [%d]: %s\n",
               ret, strerror(ret));
-- 
2.4.1

