Patch 0001: 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.



Patch 0002: AD GPO: Support processing referrals

For GPOs assigned to a site, it's possible that their definition
actually exists in another domain. To retrieve this information,
we need to follow the referral and perform a base search on
another domain controller.

Resolves:
https://fedorahosted.org/sssd/ticket/2645
From 3f8826061d34639ddaaf245947085ea577e77fbe Mon Sep 17 00:00:00 2001
From: Stephen Gallagher <[email protected]>
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 | 106 ++++++++++++++++++++++++++++++++++------
 1 file changed, 92 insertions(+), 14 deletions(-)

diff --git a/src/providers/ldap/sdap_async.c b/src/providers/ldap/sdap_async.c
index 0d1f0195c0b96d44151487642d30b631063d617a..7ad24902911a5baf6686da8cfcb2a0c151085c29 100644
--- a/src/providers/ldap/sdap_async.c
+++ b/src/providers/ldap/sdap_async.c
@@ -1154,10 +1154,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 +1378,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 +1443,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 +1516,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,24 +1593,40 @@ 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;
 
-    ret = sdap_get_generic_ext_recv(subreq);
+    ret = sdap_get_generic_ext_recv(subreq, NULL, NULL, NULL);
     talloc_zfree(subreq);
     if (ret == ERR_REFERRAL) {
         if (dp_opt_get_bool(opts->basic, SDAP_REFERRALS)) {
             tevent_req_error(req, ret);
             return;
@@ -1563,10 +1640,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 +2561,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.3.6

From 9a45e8523540c9eff8a8b9d84da81c9ee77a91de Mon Sep 17 00:00:00 2001
From: Stephen Gallagher <[email protected]>
Date: Fri, 1 May 2015 16:26:36 -0400
Subject: [PATCH 2/2] AD GPO: Support processing referrals

For GPOs assigned to a site, it's possible that their definition
actually exists in another domain. To retrieve this information,
we need to follow the referral and perform a base search on
another domain controller.

Resolves:
https://fedorahosted.org/sssd/ticket/2645
---
 src/providers/ad/ad_gpo.c       | 466 +++++++++++++++++++++++++++++++++++-----
 src/providers/ad/ad_gpo.h       |   7 +
 src/providers/ldap/sdap_async.c |  27 ++-
 src/providers/ldap/sdap_async.h |  18 +-
 4 files changed, 456 insertions(+), 62 deletions(-)

diff --git a/src/providers/ad/ad_gpo.c b/src/providers/ad/ad_gpo.c
index 4cfd26800da6c8d77fa4b5ee133a7adab346906c..eb8aa8c941a2a978de3939fbfec6675b56a7b70f 100644
--- a/src/providers/ad/ad_gpo.c
+++ b/src/providers/ad/ad_gpo.c
@@ -144,17 +144,20 @@ struct tevent_req *ad_gpo_process_som_send(TALLOC_CTX *mem_ctx,
                                            const char *domain_name);
 int ad_gpo_process_som_recv(struct tevent_req *req,
                             TALLOC_CTX *mem_ctx,
                             struct gp_som ***som_list);
 
-struct tevent_req *ad_gpo_process_gpo_send(TALLOC_CTX *mem_ctx,
-                                           struct tevent_context *ev,
-                                           struct sdap_id_op *sdap_op,
-                                           struct sdap_options *opts,
-                                           char *server_hostname,
-                                           int timeout,
-                                           struct gp_som **som_list);
+struct tevent_req *
+ad_gpo_process_gpo_send(TALLOC_CTX *mem_ctx,
+                        struct tevent_context *ev,
+                        struct sdap_id_op *sdap_op,
+                        struct sdap_options *opts,
+                        char *server_hostname,
+                        struct sss_domain_info *host_domain,
+                        struct ad_access_ctx *access_ctx,
+                        int timeout,
+                        struct gp_som **som_list);
 int ad_gpo_process_gpo_recv(struct tevent_req *req,
                             TALLOC_CTX *mem_ctx,
                             struct gp_gpo ***candidate_gpos,
                             int *num_candidate_gpos);
 
@@ -1456,10 +1459,11 @@ ad_gpo_perform_hbac_processing(TALLOC_CTX *mem_ctx,
 /* == ad_gpo_access_send/recv implementation ================================*/
 
 struct ad_gpo_access_state {
     struct tevent_context *ev;
     struct ldb_context *ldb_ctx;
+    struct ad_access_ctx *access_ctx;
     enum gpo_access_control_mode gpo_mode;
     enum gpo_map_type gpo_map_type;
     struct sdap_id_conn_ctx *conn;
     struct sdap_id_op *sdap_op;
     char *server_hostname;
@@ -1575,10 +1579,11 @@ ad_gpo_access_send(TALLOC_CTX *mem_ctx,
     state->user = user;
     state->ldb_ctx = sysdb_ctx_get_ldb(state->host_domain->sysdb);
     state->gpo_mode = ctx->gpo_access_control_mode;
     state->gpo_timeout_option = ctx->gpo_cache_timeout;
     state->ad_hostname = dp_opt_get_string(ctx->ad_options, AD_HOSTNAME);
+    state->access_ctx = ctx;
     state->opts = ctx->sdap_access_ctx->id_ctx->opts;
     state->timeout = dp_opt_get_int(state->opts->basic, SDAP_SEARCH_TIMEOUT);
     state->conn = ad_get_dom_ldap_conn(ctx->ad_id_ctx, state->host_domain);
     state->sdap_op = sdap_id_op_create(state, state->conn->conn_cache);
     if (state->sdap_op == NULL) {
@@ -1889,10 +1894,12 @@ ad_gpo_process_som_done(struct tevent_req *subreq)
     subreq = ad_gpo_process_gpo_send(state,
                                      state->ev,
                                      state->sdap_op,
                                      state->opts,
                                      state->server_hostname,
+                                     state->host_domain,
+                                     state->access_ctx,
                                      state->timeout,
                                      som_list);
     if (subreq == NULL) {
         ret = ENOMEM;
         goto done;
@@ -3348,14 +3355,16 @@ static errno_t ad_gpo_parse_sd(TALLOC_CTX *mem_ctx,
 }
 
 /* == ad_gpo_process_gpo_send/recv implementation ========================== */
 
 struct ad_gpo_process_gpo_state {
+    struct ad_access_ctx *access_ctx;
     struct tevent_context *ev;
     struct sdap_id_op *sdap_op;
     struct sdap_options *opts;
     char *server_hostname;
+    struct sss_domain_info *host_domain;
     int timeout;
     struct gp_gpo **candidate_gpos;
     int num_candidate_gpos;
     int gpo_index;
 };
@@ -3376,10 +3385,12 @@ struct tevent_req *
 ad_gpo_process_gpo_send(TALLOC_CTX *mem_ctx,
                         struct tevent_context *ev,
                         struct sdap_id_op *sdap_op,
                         struct sdap_options *opts,
                         char *server_hostname,
+                        struct sss_domain_info *host_domain,
+                        struct ad_access_ctx *access_ctx,
                         int timeout,
                         struct gp_som **som_list)
 {
     struct tevent_req *req;
     struct ad_gpo_process_gpo_state *state;
@@ -3393,10 +3404,12 @@ ad_gpo_process_gpo_send(TALLOC_CTX *mem_ctx,
 
     state->ev = ev;
     state->sdap_op = sdap_op;
     state->opts = opts;
     state->server_hostname = server_hostname;
+    state->host_domain = host_domain;
+    state->access_ctx = access_ctx;
     state->timeout = timeout;
     state->gpo_index = 0;
     state->candidate_gpos = NULL;
     state->num_candidate_gpos = 0;
 
@@ -3435,13 +3448,11 @@ immediately:
 }
 
 static errno_t
 ad_gpo_get_gpo_attrs_step(struct tevent_req *req)
 {
-    const char *attrs[] = {AD_AT_NT_SEC_DESC, AD_AT_CN, AD_AT_FILE_SYS_PATH,
-                           AD_AT_MACHINE_EXT_NAMES, AD_AT_FUNC_VERSION,
-                           AD_AT_FLAGS, NULL};
+    const char *attrs[] = AD_GPO_ATTRS;
     struct tevent_req *subreq;
     struct ad_gpo_process_gpo_state *state;
 
     state = tevent_req_data(req, struct ad_gpo_process_gpo_state);
 
@@ -3463,63 +3474,159 @@ ad_gpo_get_gpo_attrs_step(struct tevent_req *req)
 
     tevent_req_set_callback(subreq, ad_gpo_get_gpo_attrs_done, req);
     return EAGAIN;
 }
 
+static errno_t
+ad_gpo_sd_process_attrs(struct tevent_req *req,
+                        char *smb_host,
+                        struct sysdb_attrs *result);
+void
+ad_gpo_get_sd_referral_done(struct tevent_req *subreq);
+
+static struct tevent_req *
+ad_gpo_get_sd_referral_send(TALLOC_CTX *mem_ctx,
+                            struct tevent_context *ev,
+                            struct ad_access_ctx *access_ctx,
+                            struct sdap_options *opts,
+                            const char *referral,
+                            struct sss_domain_info *host_domain,
+                            int timeout);
+errno_t
+ad_gpo_get_sd_referral_recv(struct tevent_req *req,
+                            TALLOC_CTX *mem_ctx,
+                            char **_smb_host,
+                            struct sysdb_attrs **_reply);
+
 static void
 ad_gpo_get_gpo_attrs_done(struct tevent_req *subreq)
 {
     struct tevent_req *req;
     struct ad_gpo_process_gpo_state *state;
     int ret;
     int dp_error;
-    size_t num_results;
+    size_t num_results, refcount;
     struct sysdb_attrs **results;
+    char **refs;
+
+    req = tevent_req_callback_data(subreq, struct tevent_req);
+    state = tevent_req_data(req, struct ad_gpo_process_gpo_state);
+
+    ret = sdap_sd_search_recv(subreq, state,
+                              &num_results, &results,
+                              &refcount, &refs);
+    talloc_zfree(subreq);
+
+    if (ret != EOK) {
+        ret = sdap_id_op_done(state->sdap_op, ret, &dp_error);
+
+        DEBUG(SSSDBG_OP_FAILURE,
+              "Unable to get GPO attributes: [%d](%s)\n",
+              ret, sss_strerror(ret));
+        ret = ENOENT;
+        goto done;
+    }
+
+    if ((num_results < 1) || (results == NULL)) {
+        if (refcount == 1) {
+            /* If we were redirected to a referral, process it.
+             * There must be a single referral result here; if we get
+             * more than one (or zero) it's a bug.
+             */
+
+            subreq = ad_gpo_get_sd_referral_send(state, state->ev,
+                                                 state->access_ctx,
+                                                 state->opts,
+                                                 refs[0],
+                                                 state->host_domain,
+                                                 state->timeout);
+            tevent_req_set_callback(subreq, ad_gpo_get_sd_referral_done, req);
+            ret = EAGAIN;
+            goto done;
+
+        } else {
+            const char *gpo_dn = state->candidate_gpos[state->gpo_index]->gpo_dn;
+
+            DEBUG(SSSDBG_OP_FAILURE,
+                  "No attrs found for GPO [%s].", gpo_dn);
+            ret = ENOENT;
+            goto done;
+        }
+    } else if (num_results > 1) {
+        DEBUG(SSSDBG_OP_FAILURE, "Received multiple replies\n");
+        ret = ERR_INTERNAL;
+        goto done;
+    }
+
+    ret = ad_gpo_sd_process_attrs(req, state->server_hostname, results[0]);
+
+done:
+
+   if (ret == EOK) {
+       tevent_req_done(req);
+   } else if (ret != EAGAIN) {
+       tevent_req_error(req, ret);
+   }
+}
+
+void
+ad_gpo_get_sd_referral_done(struct tevent_req *subreq)
+{
+    errno_t ret;
+    int dp_error;
+    struct sysdb_attrs *reply;
+    char *smb_host;
+
+    struct tevent_req *req =
+            tevent_req_callback_data(subreq, struct tevent_req);
+    struct ad_gpo_process_gpo_state *state =
+            tevent_req_data(req, struct ad_gpo_process_gpo_state);
+
+    ret = ad_gpo_get_sd_referral_recv(subreq, state, &smb_host, &reply);
+    talloc_zfree(subreq);
+    if (ret != EOK) {
+        /* Terminate the sdap_id_op */
+        ret = sdap_id_op_done(state->sdap_op, ret, &dp_error);
+
+        DEBUG(SSSDBG_OP_FAILURE,
+              "Unable to get referred GPO attributes: [%d](%s)\n",
+              ret, sss_strerror(ret));
+
+        goto done;
+    }
+
+    /* Lookup succeeded. Process it */
+    ret = ad_gpo_sd_process_attrs(req, smb_host, reply);
+
+done:
+
+   if (ret == EOK) {
+       tevent_req_done(req);
+   } else if (ret != EAGAIN) {
+       tevent_req_error(req, ret);
+   }
+}
+
+static errno_t
+ad_gpo_sd_process_attrs(struct tevent_req *req,
+                        char *smb_host,
+                        struct sysdb_attrs *result)
+{
+    struct ad_gpo_process_gpo_state *state;
+    struct gp_gpo *gp_gpo;
+    int ret;
     struct ldb_message_element *el = NULL;
     const char *gpo_guid = NULL;
     const char *raw_file_sys_path = NULL;
     char *file_sys_path = NULL;
     uint8_t *raw_machine_ext_names = NULL;
 
-    req = tevent_req_callback_data(subreq, struct tevent_req);
     state = tevent_req_data(req, struct ad_gpo_process_gpo_state);
-
-    ret = sdap_sd_search_recv(subreq, state, &num_results, &results);
-    talloc_zfree(subreq);
-
-    if (ret != EOK) {
-        ret = sdap_id_op_done(state->sdap_op, ret, &dp_error);
-
-        DEBUG(SSSDBG_OP_FAILURE,
-              "Unable to get GPO attributes: [%d](%s)\n",
-              ret, sss_strerror(ret));
-        ret = ENOENT;
-        goto done;
-    }
-
-    if ((num_results < 1) || (results == NULL)) {
-        const char *gpo_dn = state->candidate_gpos[state->gpo_index]->gpo_dn;
-
-        DEBUG(SSSDBG_OP_FAILURE,
-              "BUG: No attrs found for GPO [%s]. This was likely caused by "
-              "the GPO entry being a referred to another domain controller."
-              " SSSD does not yet support this configuration. See upstream "
-              "ticket #2645 for more information.\n",
-              gpo_dn);
-        ret = ERR_INTERNAL;
-        goto done;
-    }
-    else if (num_results > 1) {
-        DEBUG(SSSDBG_OP_FAILURE, "Received multiple replies\n");
-        ret = ERR_INTERNAL;
-        goto done;
-    }
-
-    struct gp_gpo *gp_gpo = state->candidate_gpos[state->gpo_index];
+    gp_gpo = state->candidate_gpos[state->gpo_index];
 
     /* retrieve AD_AT_CN */
-    ret = sysdb_attrs_get_string(results[0], AD_AT_CN, &gpo_guid);
+    ret = sysdb_attrs_get_string(result, AD_AT_CN, &gpo_guid);
     if (ret != EOK) {
         DEBUG(SSSDBG_OP_FAILURE,
               "sysdb_attrs_get_string failed: [%d](%s)\n",
               ret, sss_strerror(ret));
         goto done;
@@ -3533,11 +3640,11 @@ ad_gpo_get_gpo_attrs_done(struct tevent_req *subreq)
 
     DEBUG(SSSDBG_TRACE_ALL, "populating attrs for gpo_guid: %s\n",
           gp_gpo->gpo_guid);
 
     /* retrieve AD_AT_FILE_SYS_PATH */
-    ret = sysdb_attrs_get_string(results[0],
+    ret = sysdb_attrs_get_string(result,
                                  AD_AT_FILE_SYS_PATH,
                                  &raw_file_sys_path);
 
     if (ret != EOK) {
         DEBUG(SSSDBG_OP_FAILURE,
@@ -3546,11 +3653,11 @@ ad_gpo_get_gpo_attrs_done(struct tevent_req *subreq)
         goto done;
     }
 
     file_sys_path = talloc_strdup(gp_gpo, raw_file_sys_path);
 
-    ret = ad_gpo_extract_smb_components(gp_gpo, state->server_hostname,
+    ret = ad_gpo_extract_smb_components(gp_gpo, smb_host,
                                         file_sys_path, &gp_gpo->smb_server,
                                         &gp_gpo->smb_share, &gp_gpo->smb_path);
     if (ret != EOK) {
         DEBUG(SSSDBG_OP_FAILURE,
               "unable to extract smb components from file_sys_path: [%d](%s)\n",
@@ -3561,11 +3668,11 @@ ad_gpo_get_gpo_attrs_done(struct tevent_req *subreq)
     DEBUG(SSSDBG_TRACE_ALL, "smb_server: %s\n", gp_gpo->smb_server);
     DEBUG(SSSDBG_TRACE_ALL, "smb_share: %s\n", gp_gpo->smb_share);
     DEBUG(SSSDBG_TRACE_ALL, "smb_path: %s\n", gp_gpo->smb_path);
 
     /* retrieve AD_AT_FUNC_VERSION */
-    ret = sysdb_attrs_get_int32_t(results[0], AD_AT_FUNC_VERSION,
+    ret = sysdb_attrs_get_int32_t(result, AD_AT_FUNC_VERSION,
                                   &gp_gpo->gpo_func_version);
     if (ret != EOK) {
         DEBUG(SSSDBG_OP_FAILURE,
               "sysdb_attrs_get_int32_t failed: [%d](%s)\n",
               ret, sss_strerror(ret));
@@ -3574,11 +3681,11 @@ ad_gpo_get_gpo_attrs_done(struct tevent_req *subreq)
 
     DEBUG(SSSDBG_TRACE_ALL, "gpo_func_version: %d\n",
                             gp_gpo->gpo_func_version);
 
     /* retrieve AD_AT_FLAGS */
-    ret = sysdb_attrs_get_int32_t(results[0], AD_AT_FLAGS,
+    ret = sysdb_attrs_get_int32_t(result, AD_AT_FLAGS,
                                   &gp_gpo->gpo_flags);
     if (ret != EOK) {
         DEBUG(SSSDBG_OP_FAILURE,
               "sysdb_attrs_get_int32_t failed: [%d](%s)\n",
               ret, sss_strerror(ret));
@@ -3586,11 +3693,11 @@ ad_gpo_get_gpo_attrs_done(struct tevent_req *subreq)
     }
 
     DEBUG(SSSDBG_TRACE_ALL, "gpo_flags: %d\n", gp_gpo->gpo_flags);
 
     /* retrieve AD_AT_NT_SEC_DESC */
-    ret = sysdb_attrs_get_el(results[0], AD_AT_NT_SEC_DESC, &el);
+    ret = sysdb_attrs_get_el(result, AD_AT_NT_SEC_DESC, &el);
     if (ret != EOK && ret != ENOENT) {
         DEBUG(SSSDBG_OP_FAILURE, "sysdb_attrs_get_el() failed\n");
         goto done;
     }
     if ((ret == ENOENT) || (el->num_values == 0)) {
@@ -3606,11 +3713,11 @@ ad_gpo_get_gpo_attrs_done(struct tevent_req *subreq)
         DEBUG(SSSDBG_OP_FAILURE, "ad_gpo_parse_sd() failed\n");
         goto done;
     }
 
     /* retrieve AD_AT_MACHINE_EXT_NAMES */
-    ret = sysdb_attrs_get_el(results[0], AD_AT_MACHINE_EXT_NAMES, &el);
+    ret = sysdb_attrs_get_el(result, AD_AT_MACHINE_EXT_NAMES, &el);
     if (ret != EOK && ret != ENOENT) {
         DEBUG(SSSDBG_OP_FAILURE, "sysdb_attrs_get_el() failed\n");
         goto done;
     }
 
@@ -3640,15 +3747,11 @@ ad_gpo_get_gpo_attrs_done(struct tevent_req *subreq)
 
     ret = ad_gpo_get_gpo_attrs_step(req);
 
  done:
 
-    if (ret == EOK) {
-        tevent_req_done(req);
-    } else if (ret != EAGAIN) {
-        tevent_req_error(req, ret);
-    }
+    return ret;
 }
 
 int
 ad_gpo_process_gpo_recv(struct tevent_req *req,
                         TALLOC_CTX *mem_ctx,
@@ -4017,5 +4120,254 @@ gpo_fork_child(struct tevent_req *req)
         return err;
     }
 
     return EOK;
 }
+
+struct ad_gpo_get_sd_referral_state {
+    struct tevent_context *ev;
+    struct ad_access_ctx *access_ctx;
+    struct sdap_options *opts;
+    struct sss_domain_info *host_domain;
+    struct sss_domain_info *ref_domain;
+    struct sdap_id_conn_ctx *conn;
+    struct sdap_id_op *ref_op;
+    int timeout;
+    char *gpo_dn;
+    char *smb_host;
+
+
+    struct sysdb_attrs *reply;
+};
+
+static void
+ad_gpo_get_sd_referral_conn_done(struct tevent_req *subreq);
+
+static struct tevent_req *
+ad_gpo_get_sd_referral_send(TALLOC_CTX *mem_ctx,
+                            struct tevent_context *ev,
+                            struct ad_access_ctx *access_ctx,
+                            struct sdap_options *opts,
+                            const char *referral,
+                            struct sss_domain_info *host_domain,
+                            int timeout)
+{
+    errno_t ret;
+    struct tevent_req *req;
+    struct ad_gpo_get_sd_referral_state *state;
+    struct tevent_req *subreq;
+    LDAPURLDesc *lud;
+
+    req = tevent_req_create(mem_ctx, &state,
+                            struct ad_gpo_get_sd_referral_state);
+    if (!req) return NULL;
+
+    state->ev = ev;
+    state->access_ctx = access_ctx;
+    state->opts = opts;
+    state->host_domain = host_domain;
+    state->timeout = timeout;
+
+    /* Parse the URL for the domain */
+    ret = ldap_url_parse(referral, &lud);
+    if (ret != LDAP_SUCCESS) {
+        DEBUG(SSSDBG_CRIT_FAILURE,
+              "Failed to parse referral URI (%s)!\n", referral);
+        ret = EINVAL;
+        goto done;
+    }
+
+    state->gpo_dn = talloc_strdup(state, lud->lud_dn);
+    if (!state->gpo_dn) {
+        DEBUG(SSSDBG_OP_FAILURE,
+              "Could not copy referral DN (%s)!\n", lud->lud_dn);
+        ldap_free_urldesc(lud);
+        ret = ENOMEM;
+        goto done;
+    }
+
+    /* Active Directory returns the domain name as the hostname
+     * in these referrals, so we can use that to look up the
+     * necessary connection.
+     */
+    state->ref_domain = find_domain_by_name(state->host_domain,
+                                            lud->lud_host, true);
+    ldap_free_urldesc(lud);
+    if (!state->ref_domain) {
+        DEBUG(SSSDBG_CRIT_FAILURE,
+              "Could not find domain matching [%s]\n",
+              lud->lud_host);
+        ret = EIO;
+        goto done;
+    }
+
+    state->conn = ad_get_dom_ldap_conn(state->access_ctx->ad_id_ctx,
+                                       state->ref_domain);
+    if (!state->conn) {
+        DEBUG(SSSDBG_OP_FAILURE,
+              "No connection for %s\n", state->ref_domain->name);
+        ret = EINVAL;
+        goto done;
+    }
+
+    /* Get the hostname we're going to connect to.
+     * We'll need this later for performing the samba
+     * connection.
+     */
+    ret = ldap_url_parse(state->conn->service->uri, &lud);
+    if (ret != LDAP_SUCCESS) {
+        DEBUG(SSSDBG_CRIT_FAILURE,
+              "Failed to parse service URI (%s)!\n", referral);
+        ret = EINVAL;
+        goto done;
+    }
+
+    state->smb_host = talloc_strdup(state, lud->lud_host);
+    ldap_free_urldesc(lud);
+    if (!state->smb_host) {
+        ret = ENOMEM;
+        goto done;
+    }
+
+    /* Start an ID operation for the referral */
+    state->ref_op = sdap_id_op_create(state, state->conn->conn_cache);
+    if (!state->ref_op) {
+        DEBUG(SSSDBG_OP_FAILURE, "sdap_id_op_create failed.\n");
+        ret = ENOMEM;
+        goto done;
+    }
+
+    /* Establish the sdap_id_op connection */
+    subreq = sdap_id_op_connect_send(state->ref_op, state, &ret);
+    if (subreq == NULL) {
+        DEBUG(SSSDBG_OP_FAILURE, "sdap_id_op_connect_send failed: %d(%s).\n",
+                                  ret, sss_strerror(ret));
+        goto done;
+    }
+    tevent_req_set_callback(subreq, ad_gpo_get_sd_referral_conn_done, req);
+
+done:
+
+    if (ret != EOK) {
+        tevent_req_error(req, ret);
+        tevent_req_post(req, ev);
+    }
+    return req;
+}
+
+static void
+ad_gpo_get_sd_referral_search_done(struct tevent_req *subreq);
+
+static void
+ad_gpo_get_sd_referral_conn_done(struct tevent_req *subreq)
+{
+    errno_t ret;
+    int dp_error;
+    const char *attrs[] = AD_GPO_ATTRS;
+
+    struct tevent_req *req =
+            tevent_req_callback_data(subreq, struct tevent_req);
+    struct ad_gpo_get_sd_referral_state *state =
+            tevent_req_data(req, struct ad_gpo_get_sd_referral_state);
+
+    ret = sdap_id_op_connect_recv(subreq, &dp_error);
+    talloc_zfree(subreq);
+    if (ret != EOK) {
+        if (dp_error == DP_ERR_OFFLINE) {
+            DEBUG(SSSDBG_TRACE_FUNC,
+                  "Backend is marked offline, retry later!\n");
+            tevent_req_done(req);
+        } else {
+            DEBUG(SSSDBG_MINOR_FAILURE,
+                  "Cross-realm GPO processing failed to connect to " \
+                   "referred LDAP server: (%d)[%s]\n",
+                   ret, sss_strerror(ret));
+            tevent_req_error(req, ret);
+        }
+        return;
+    }
+
+    /* Request the referred GPO data */
+    subreq = sdap_sd_search_send(state, state->ev, state->opts,
+                                 sdap_id_op_handle(state->ref_op),
+                                 state->gpo_dn,
+                                 SECINFO_DACL,
+                                 attrs,
+                                 state->timeout);
+    tevent_req_set_callback(subreq, ad_gpo_get_sd_referral_search_done, req);
+
+}
+
+static void
+ad_gpo_get_sd_referral_search_done(struct tevent_req *subreq)
+{
+    errno_t ret;
+    int dp_error;
+    size_t num_results, num_refs;
+    struct sysdb_attrs **results = NULL;
+    char **refs;
+    struct tevent_req *req =
+            tevent_req_callback_data(subreq, struct tevent_req);
+    struct ad_gpo_get_sd_referral_state *state =
+            tevent_req_data(req, struct ad_gpo_get_sd_referral_state);
+
+    ret = sdap_sd_search_recv(subreq, NULL,
+                              &num_results, &results,
+                              &num_refs, &refs);
+    talloc_zfree(subreq);
+
+    ret = sdap_id_op_done(state->ref_op, ret, &dp_error);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_OP_FAILURE,
+              "Unable to get GPO attributes: [%d](%s)\n",
+              ret, sss_strerror(ret));
+        ret = ENOENT;
+        goto done;
+    }
+
+    if ((num_results < 1) || (results == NULL)) {
+        /* TODO:
+         * It's strictly possible for the referral search to return
+         * another referral value here, but it shouldn't actually
+         * happen with Active Directory. Properly handling (and
+         * limiting) the referral chain would be fairly complex, so
+         * we will do it later if it ever becomes necessary.
+         */
+        DEBUG(SSSDBG_OP_FAILURE,
+              "No attrs found for referred GPO [%s].", state->gpo_dn);
+        ret = ENOENT;
+        goto done;
+
+    } else if (num_results > 1) {
+        DEBUG(SSSDBG_OP_FAILURE, "Received multiple replies\n");
+        ret = ERR_INTERNAL;
+        goto done;
+    }
+
+    state->reply = talloc_steal(state, results[0]);
+
+done:
+   talloc_free(results);
+
+   if (ret == EOK) {
+       tevent_req_done(req);
+   } else if (ret != EAGAIN) {
+       tevent_req_error(req, ret);
+   }
+}
+
+errno_t
+ad_gpo_get_sd_referral_recv(struct tevent_req *req,
+                            TALLOC_CTX *mem_ctx,
+                            char **_smb_host,
+                            struct sysdb_attrs **_reply)
+{
+    struct ad_gpo_get_sd_referral_state *state =
+                tevent_req_data(req, struct ad_gpo_get_sd_referral_state);
+
+    TEVENT_REQ_RETURN_ON_ERROR(req);
+
+    *_smb_host = talloc_steal(mem_ctx, state->smb_host);
+    *_reply = talloc_steal(mem_ctx, state->reply);
+
+    return EOK;
+}
diff --git a/src/providers/ad/ad_gpo.h b/src/providers/ad/ad_gpo.h
index 9fd590a2b262b66c1efd493d8736774bdfa61b40..f57889fca1c7fa77f25c345c1c969d6b499217e5 100644
--- a/src/providers/ad/ad_gpo.h
+++ b/src/providers/ad/ad_gpo.h
@@ -25,10 +25,17 @@
 
 #include "providers/ad/ad_access.h"
 
 #define AD_GPO_CHILD_OUT_FILENO 3
 
+#define AD_GPO_ATTRS {AD_AT_NT_SEC_DESC, \
+                      AD_AT_CN, AD_AT_FILE_SYS_PATH, \
+                      AD_AT_MACHINE_EXT_NAMES, \
+                      AD_AT_FUNC_VERSION, \
+                      AD_AT_FLAGS, \
+                      NULL}
+
 /*
  * This pair of functions provides client-side GPO processing.
  *
  * While a GPO can target both user and computer objects, this
  * implementation only supports targetting of computer objects.
diff --git a/src/providers/ldap/sdap_async.c b/src/providers/ldap/sdap_async.c
index 7ad24902911a5baf6686da8cfcb2a0c151085c29..ae418446efd604271209d3896c4b988a54b3467a 100644
--- a/src/providers/ldap/sdap_async.c
+++ b/src/providers/ldap/sdap_async.c
@@ -1992,10 +1992,14 @@ struct sdap_sd_search_state {
   LDAPControl **ctrls;
   struct sdap_options *opts;
   size_t reply_count;
   struct sysdb_attrs **reply;
   struct sdap_reply sreply;
+
+  /* Referrals returned by the search */
+  size_t ref_count;
+  char **refs;
 };
 
 static int sdap_sd_search_create_control(struct sdap_handle *sh,
 					 int val,
 					 LDAPControl **ctrl);
@@ -2123,16 +2127,30 @@ static errno_t sdap_sd_search_parse_entry(struct sdap_handle *sh,
     return EOK;
 }
 
 static void sdap_sd_search_done(struct tevent_req *subreq)
 {
+    int ret;
+
     struct tevent_req *req = tevent_req_callback_data(subreq,
                                                       struct tevent_req);
     struct sdap_sd_search_state *state =
                 tevent_req_data(req, struct sdap_sd_search_state);
 
-    return generic_ext_search_handler(subreq, state->opts);
+    ret = sdap_get_generic_ext_recv(subreq, state,
+                                    &state->ref_count,
+                                    &state->refs);
+    talloc_zfree(subreq);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_OP_FAILURE,
+              "sdap_get_generic_ext_recv failed [%d]: %s\n",
+              ret, sss_strerror(ret));
+        tevent_req_error(req, ret);
+        return;
+    }
+
+    tevent_req_done(req);
 }
 
 static int sdap_sd_search_ctrls_destructor(void *ptr)
 {
     LDAPControl **ctrls = talloc_get_type(ptr, LDAPControl *);;
@@ -2144,19 +2162,24 @@ static int sdap_sd_search_ctrls_destructor(void *ptr)
 }
 
 int sdap_sd_search_recv(struct tevent_req *req,
                         TALLOC_CTX *mem_ctx,
                         size_t *_reply_count,
-                        struct sysdb_attrs ***_reply)
+                        struct sysdb_attrs ***_reply,
+                        size_t *_ref_count,
+                        char ***_refs)
 {
     struct sdap_sd_search_state *state = tevent_req_data(req,
                                             struct sdap_sd_search_state);
     TEVENT_REQ_RETURN_ON_ERROR(req);
 
     *_reply_count = state->sreply.reply_count;
     *_reply = talloc_steal(mem_ctx, state->sreply.reply);
 
+    *_ref_count = state->ref_count;
+    *_refs = talloc_steal(mem_ctx, state->refs);
+
     return EOK;
 }
 
 /* ==Attribute scoped search============================================ */
 struct sdap_asq_search_state {
diff --git a/src/providers/ldap/sdap_async.h b/src/providers/ldap/sdap_async.h
index 29afd8e1ad8cceae66fbe3cd5c6b1ffb69e93e07..f695e607680a908c1c955deca5963b832bf50e43 100644
--- a/src/providers/ldap/sdap_async.h
+++ b/src/providers/ldap/sdap_async.h
@@ -248,13 +248,25 @@ sdap_sd_search_send(TALLOC_CTX *memctx,
 		    const char *base_dn,
 		    int sd_flags,
 		    const char **attrs,
 		    int timeout);
 int sdap_sd_search_recv(struct tevent_req *req,
-			TALLOC_CTX *mem_ctx,
-			size_t *reply_count,
-			struct sysdb_attrs ***reply);
+                        TALLOC_CTX *mem_ctx,
+                        size_t *_reply_count,
+                        struct sysdb_attrs ***_reply,
+                        size_t *_ref_count,
+                        char ***_refs);
+
+struct tevent_req *
+sdap_sd_follow_referral_send(TALLOC_CTX *mem_ctx,
+                             const char *ref);
+
+errno_t
+sdap_sd_follow_referral_recv(struct tevent_req *req,
+                             TALLOC_CTX *mem_ctx,
+                             size_t *_reply_count,
+                             struct sysdb_attrs ***_reply);
 
 errno_t
 sdap_attrs_add_ldap_attr(struct sysdb_attrs *ldap_attrs,
                          const char *attr_name,
                          const char *attr_desc,
-- 
2.3.6

_______________________________________________
sssd-devel mailing list
[email protected]
https://lists.fedorahosted.org/mailman/listinfo/sssd-devel

Reply via email to