On Fri, 2015-05-22 at 13:04 +0200, Jakub Hrozek wrote: > On Thu, May 14, 2015 at 05:58:49PM +0200, Jakub Hrozek wrote: > > On Thu, May 14, 2015 at 11:49:17AM -0400, Stephen Gallagher wrote: > > > On Thu, 2015-05-14 at 17:42 +0200, Jakub Hrozek wrote: > > > > On Wed, May 06, 2015 at 02:26:30PM -0400, Stephen Gallagher > > > > wrote: > > > > > 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 > > > > > > > > Thanks a lot for the patches! I have one questions about the > > > > first > > > > patch > > > > and two nitpicks about the second. I'm also still waiting for > > > > the > > > > Coverity results, but the queue seems to be quite long.. > > > > > > > > > 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. > > > > > > > > [...] > > > > > > > > > } 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; > > > > > > > > This is a change in behaviour. Previously, we would always > > > > return > > > > ERR_REFERRAL and let the caller handle the error code based on > > > > whether > > > > the caller ignores referrals (like the AD Provider does by > > > > default) > > > > or > > > > not. > > > > > > > > Do you think it's OK to always treat referrals as a success > > > > now, even > > > > for the plain LDAP provider? If so, then I guess we can remove > > > > the > > > > ERR_REFERRAL special-case and the error code as well. > > > > > > > > > > Hmm, that's a difficult question. I'd *prefer* to always return > > > the > > > reference list (as I'm doing here) and leave the decision on > > > whether or > > > how to handle it up to the callers. But you're right, it's a > > > change in > > > behavior right now. > > > > > > That being said, it shouldn't functionally be any different, > > > since we > > > pretty much ignore ERR_REFERRAL anyway (we only return the > > > records we > > > actually received, if any). Or am I mistaken in that? > > > > The current sdap_get_generic_ext_recv() handler looks like this: > > > > ret = sdap_get_generic_ext_recv(subreq); > > talloc_zfree(subreq); > > if (ret == ERR_REFERRAL) { > > if (dp_opt_get_bool(opts->basic, SDAP_REFERRALS)) { > > tevent_req_error(req, ret); > > return; > > } > > } > > > > So unless referrals are supposed to be ignored, we fail the request > > on > > receiving ERR_REFERRAL. We could easily turn that into: > > > > ret = sdap_get_generic_ext_recv(subreq, mem_ctx, &ref_count, > > &refs); > > talloc_zfree(subreq); > > if (ref_count > 0) { > > if (dp_opt_get_bool(opts->basic, SDAP_REFERRALS)) { > > tevent_req_error(req, ret); > > return; > > } > > } > > > > I guess ignoring the referrals makes sense, if SDAP_REFERRALS is > > enabled, > > the referral chasing would follow legitimate referrals. The > > referrals we > > receive in LDAP results processing are just noise more or less... > > Bump..
Sorry for the delay; two new patches attached. This patch fixes the two missing error checks in the AD GPO code as well as making several changes to the general LDAP support of referrals. While I was looking through this, I discovered a bug that resulted in referrals for more information not being returned to the caller (this is different from referral-as-final-result). I tweaked the code so that this would come back as well. I also added some extra debugging at level 9 so we can see when these occur, what they were and that they were ignored. As discussed above, I just changed the referral check around sdap_get_generic_ext_recv() to check for ref_count > 0 instead of ERR_REFERRALS. I didn't remove that completely from the system just in case we decide to use the error for something else involving referrals in the future. I retested these patches against the problematic cross-realm GPO case, which worked successfully.
From 9369111f0775cbc4c10b5857046c756ae510033f 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 | 473 +++++++++++++++++++++++++++++++++++----- src/providers/ad/ad_gpo.h | 7 + src/providers/ldap/sdap_async.c | 27 ++- src/providers/ldap/sdap_async.h | 18 +- 4 files changed, 462 insertions(+), 63 deletions(-) diff --git a/src/providers/ad/ad_gpo.c b/src/providers/ad/ad_gpo.c index 4cfd26800da6c8d77fa4b5ee133a7adab346906c..e2bbaf671a900a29ff4064cb06c2111043571c4b 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); @@ -3455,71 +3466,167 @@ ad_gpo_get_gpo_attrs_step(struct tevent_req *req) subreq = sdap_sd_search_send(state, state->ev, state->opts, sdap_id_op_handle(state->sdap_op), gpo_dn, SECINFO_DACL, attrs, state->timeout); if (subreq == NULL) { - DEBUG(SSSDBG_OP_FAILURE, "sdap_get_generic_send failed.\n"); + DEBUG(SSSDBG_OP_FAILURE, "sdap_sd_search_send failed.\n"); return ENOMEM; } 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,259 @@ 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); + if (subreq == NULL) { + DEBUG(SSSDBG_OP_FAILURE, "sdap_sd_search_send failed.\n"); + return ENOMEM; + } + 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); + if (ret != EOK) { + ret = sdap_id_op_done(state->ref_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)) { + /* 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 2ffc2a170c2277aa2ea40e84bb697c62542aa266..760fb3df5148f46bf0e7e0fdb9110456685a914c 100644 --- a/src/providers/ldap/sdap_async.c +++ b/src/providers/ldap/sdap_async.c @@ -2004,10 +2004,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); @@ -2135,16 +2139,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 *);; @@ -2156,19 +2174,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 f2ea9bf2ecdaeefa897d414df051532c6219cb97..1861b3e7cbf4edb2df7a963b528ef18fd21d6f46 100644 --- a/src/providers/ldap/sdap_async.h +++ b/src/providers/ldap/sdap_async.h @@ -249,13 +249,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.4.1
From 20191f9c34336b3920db8d5774593c4562cefdb7 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 | 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
signature.asc
Description: This is a digitally signed message part
_______________________________________________ sssd-devel mailing list [email protected] https://lists.fedorahosted.org/mailman/listinfo/sssd-devel
