On Wed, 2015-05-27 at 11:15 +0200, Jakub Hrozek wrote: > On Tue, May 26, 2015 at 03:56:35PM -0400, Stephen Gallagher wrote: > > 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 20191f9c34336b3920db8d5774593c4562cefdb7 Mon Sep 17 00:00:00 > > 2001 > > From: Stephen Gallagher <sgall...@redhat.com> > > Date: Fri, 1 May 2015 11:42:06 -0400 > > Subject: [PATCH 1/2] LDAP: Support returning referral information > > Thank you, this was really useful. I tested the referral patch with > universal groups and GC support disabled which always triggers > referrals > to the trusted domains. The referrals were ignored as they're > supposed > to, so we're not going to regress there.. > > ACK > > > From 9369111f0775cbc4c10b5857046c756ae510033f Mon Sep 17 00:00:00 > > 2001 > > From: Stephen Gallagher <sgall...@redhat.com> > > 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 > > Some nitpicks are inline, but mostly the patch looks good! > > [...] > > > + > > + subreq = ad_gpo_get_sd_referral_send(state, state->ev, > > + state > > ->access_ctx, > > + state->opts, > > + refs[0], > > + state > > ->host_domain, > > + state->timeout); > > Missing NULL check for subreq >
Good catch. Fixed. > > + /* 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"); > > Copy-n-paste bug; the error handler should read: > tevent_req_error(req, ENOMEM); > return; > > Interestingly enough, gcc emits a warning here, but Coverity didn't > catch this bug.. > I'm curious what warning GCC saw. In any case, fixed. > > + return ENOMEM; > > + } > > + tevent_req_set_callback(subreq, > > ad_gpo_get_sd_referral_search_done, req); > > + > > +} > > [...] > > > index > > 2ffc2a170c2277aa2ea40e84bb697c62542aa266..760fb3df5148f46bf0e7e0fdb9110456685a914c > > > > 100644 > > --- a/src/providers/ldap/sdap_async.c > > +++ b/src/providers/ldap/sdap_async.c > > The changes to the sdap_async module look OK to me, but can you split > them into a separate patch, please? > I considered it, but then decided against splitting it. There really isn't sufficient value in splitting it. The sdap_async.c portion is fairly short and clearly only useful in context. > > 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); > > + > > Would it make sense to allow NULL ref_count and refs? Like this: > if (_ref_count) { > *_ref_count = state->ref_count; > } > if (_refs) { > *_refs = talloc_steal(mem_ctx, state->refs); > } > > > return EOK; > > } > > I don't see a real need for it, but since it's not harmful, I added it anyway. > > f2ea9bf2ecdaeefa897d414df051532c6219cb97..1861b3e7cbf4edb2df7a963b5 > > 28ef18fd21d6f46 > > 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); > > I only see declarations, not the function bodies or uses? Whoops. That was part of an aborted change I made. I forgot to remove the prototypes from the header. New patches attached. I retested the changes as well. (On my new Vagrant setup, no less)
From 31af96ccb5e1d49e5457c64db1793fe4d160a101 Mon Sep 17 00:00:00 2001 From: Stephen Gallagher <sgall...@redhat.com> 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 | 479 +++++++++++++++++++++++++++++++++++----- src/providers/ad/ad_gpo.h | 7 + src/providers/ldap/sdap_async.c | 32 ++- src/providers/ldap/sdap_async.h | 8 +- 4 files changed, 463 insertions(+), 63 deletions(-) diff --git a/src/providers/ad/ad_gpo.c b/src/providers/ad/ad_gpo.c index 4cfd26800da6c8d77fa4b5ee133a7adab346906c..4e7afb12a73313ece00c6ffdd24cfad1b73a831d 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,172 @@ 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); + if (!subreq) { + ret = ENOMEM; + goto done; + } + + 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 +3645,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 +3658,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 +3673,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 +3686,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 +3698,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 +3718,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 +3752,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 +4125,260 @@ 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"); + tevent_req_error(req, ENOMEM); + return; + } + 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..ca70976b184b8b36fe0244a54f976da86a7b00f0 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,29 @@ 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); + if(_ref_count) { + *_ref_count = state->ref_count; + } + + if (_refs) { + *_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..b5db64d7fa3c852fba60e07db19e823e818d29f3 100644 --- a/src/providers/ldap/sdap_async.h +++ b/src/providers/ldap/sdap_async.h @@ -249,13 +249,15 @@ 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); errno_t sdap_attrs_add_ldap_attr(struct sysdb_attrs *ldap_attrs, const char *attr_name, const char *attr_desc, -- 2.4.1
From 21bd33a95d2b9b9fc08196f91df0f08abd3bd480 Mon Sep 17 00:00:00 2001 From: Stephen Gallagher <sgall...@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
signature.asc
Description: This is a digitally signed message part
_______________________________________________ sssd-devel mailing list sssd-devel@lists.fedorahosted.org https://lists.fedorahosted.org/mailman/listinfo/sssd-devel