diff options
-rw-r--r-- | src/providers/ad/ad_id.c | 263 |
1 files changed, 262 insertions, 1 deletions
diff --git a/src/providers/ad/ad_id.c b/src/providers/ad/ad_id.c index 5d8c7837..9c4abc8c 100644 --- a/src/providers/ad/ad_id.c +++ b/src/providers/ad/ad_id.c @@ -23,18 +23,279 @@ #include "providers/ad/ad_common.h" #include "providers/ad/ad_id.h" +struct ad_handle_acct_info_state { + struct be_req *breq; + struct be_acct_req *ar; + struct sdap_id_ctx *ctx; + struct sdap_id_conn_ctx **conn; + struct sdap_domain *sdom; + size_t cindex; + + int dp_error; + const char *err; +}; + +static errno_t ad_handle_acct_info_step(struct tevent_req *req); +static void ad_handle_acct_info_done(struct tevent_req *subreq); + +static struct tevent_req * +ad_handle_acct_info_send(TALLOC_CTX *mem_ctx, + struct be_req *breq, + struct be_acct_req *ar, + struct sdap_id_ctx *ctx, + struct sdap_domain *sdom, + struct sdap_id_conn_ctx **conn) +{ + struct tevent_req *req; + struct ad_handle_acct_info_state *state; + struct be_ctx *be_ctx = be_req_get_be_ctx(breq); + errno_t ret; + + req = tevent_req_create(mem_ctx, &state, struct ad_handle_acct_info_state); + if (req == NULL) { + return NULL; + } + state->breq = breq; + state->ar = ar; + state->ctx = ctx; + state->sdom = sdom; + state->conn = conn; + state->cindex = 0; + + ret = ad_handle_acct_info_step(req); + if (ret == EOK) { + tevent_req_done(req); + tevent_req_post(req, be_ctx->ev); + } else if (ret != EAGAIN) { + tevent_req_error(req, ret); + tevent_req_post(req, be_ctx->ev); + } + + /* Lookup in progress */ + return req; +} + +static errno_t +ad_handle_acct_info_step(struct tevent_req *req) +{ + struct tevent_req *subreq; + struct ad_handle_acct_info_state *state = tevent_req_data(req, + struct ad_handle_acct_info_state); + bool noexist_delete = false; + + if (state->conn[state->cindex] == NULL) { + return EOK; + } + + if (state->conn[state->cindex+1] == NULL) { + noexist_delete = true; + } + + subreq = sdap_handle_acct_req_send(state, state->breq, + state->ar, state->ctx, + state->sdom, + state->conn[state->cindex], + noexist_delete); + if (req == NULL) { + return ENOMEM; + } + tevent_req_set_callback(subreq, ad_handle_acct_info_done, req); + return EAGAIN; +} + +static void +ad_handle_acct_info_done(struct tevent_req *subreq) +{ + errno_t ret; + int dp_error; + int sdap_err; + const char *err; + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct ad_handle_acct_info_state *state = tevent_req_data(req, + struct ad_handle_acct_info_state); + + ret = sdap_handle_acct_req_recv(subreq, &dp_error, &err, &sdap_err); + talloc_zfree(subreq); + if (ret != EOK) { + tevent_req_error(req, ret); + return; + } + + if (sdap_err == EOK) { + tevent_req_done(req); + return; + } else if (sdap_err != ENOENT) { + tevent_req_error(req, EIO); + return; + } + + /* Ret is only ENOENT now. Try the next connection */ + state->cindex++; + ret = ad_handle_acct_info_step(req); + if (ret != EAGAIN) { + /* No additional search in progress. Save the last + * error status, we'll be returning it. + */ + state->dp_error = dp_error; + state->err = err; + + if (ret == EOK) { + /* No more connections */ + tevent_req_done(req); + } else { + tevent_req_error(req, ret); + } + return; + } + + /* Another lookup in progress */ +} + +static errno_t +ad_handle_acct_info_recv(struct tevent_req *req, + int *_dp_error, const char **_err) +{ + struct ad_handle_acct_info_state *state = tevent_req_data(req, + struct ad_handle_acct_info_state); + + if (_dp_error) { + *_dp_error = state->dp_error; + } + + if (_err) { + *_err = state->err; + } + + TEVENT_REQ_RETURN_ON_ERROR(req); + return EOK; +} + +struct sdap_id_conn_ctx ** +get_conn_list(struct be_req *breq, struct ad_id_ctx *ad_ctx, + struct sss_domain_info *dom, struct be_acct_req *ar) +{ + struct sdap_id_conn_ctx **clist; + int i=0; + + /* LDAP, GC, sentinel */ + clist = talloc_zero_array(breq, struct sdap_id_conn_ctx *, 3); + if (clist == NULL) return NULL; + + switch (ar->entry_type & BE_REQ_TYPE_MASK) { + case BE_REQ_USER: /* user */ + case BE_REQ_BY_SECID: /* by SID */ + case BE_REQ_USER_AND_GROUP: /* get SID */ + case BE_REQ_GROUP: /* group */ + case BE_REQ_INITGROUPS: /* init groups for user */ + if (ad_ctx->gc_ctx && IS_SUBDOMAIN(dom)) { + clist[i] = ad_ctx->gc_ctx; + i++; + } else { + clist[i] = ad_ctx->ldap_ctx; + } + break; + + default: + clist[0] = ad_ctx->ldap_ctx; + break; + } + + return clist; +} + +static void ad_account_info_complete(struct tevent_req *req); + void ad_account_info_handler(struct be_req *be_req) { struct ad_id_ctx *ad_ctx; + struct be_acct_req *ar; struct sdap_id_ctx *sdap_id_ctx; struct be_ctx *be_ctx = be_req_get_be_ctx(be_req); + struct tevent_req *req; + struct sss_domain_info *dom; + struct sdap_domain *sdom; + struct sdap_id_conn_ctx **clist; + errno_t ret; ad_ctx = talloc_get_type(be_ctx->bet_info[BET_ID].pvt_bet_data, struct ad_id_ctx); + ar = talloc_get_type(be_req_get_data(be_req), struct be_acct_req); sdap_id_ctx = ad_ctx->sdap_id_ctx; - return sdap_handle_account_info(be_req, sdap_id_ctx, sdap_id_ctx->conn); + if (be_is_offline(be_ctx)) { + return be_req_terminate(be_req, DP_ERR_OFFLINE, EAGAIN, "Offline"); + } + + dom = be_ctx->domain; + if (strcasecmp(ar->domain, be_ctx->domain->name) != 0) { + /* Subdomain request, verify subdomain */ + dom = find_subdomain_by_name(be_ctx->domain, ar->domain, true); + } + + if (dom == NULL) { + ret = EINVAL; + goto fail; + } + + /* Determine whether to connect to GC, LDAP or try both */ + clist = get_conn_list(be_req, ad_ctx, dom, ar); + if (clist == NULL) { + ret = EIO; + goto fail; + } + + sdom = sdap_domain_get(sdap_id_ctx->opts, dom); + if (sdom == NULL) { + ret = EIO; + goto fail; + } + + req = ad_handle_acct_info_send(be_req, be_req, ar, sdap_id_ctx, + sdom, clist); + if (req == NULL) { + ret = ENOMEM; + goto fail; + } + tevent_req_set_callback(req, ad_account_info_complete, be_req); + return; + +fail: + be_req_terminate(be_req, DP_ERR_FATAL, ret, NULL); +} + +static void +ad_account_info_complete(struct tevent_req *req) +{ + struct be_req *be_req; + errno_t ret; + int dp_error; + const char *error_text = "Internal error"; + const char *req_error_text; + + be_req = tevent_req_callback_data(req, struct be_req); + + ret = ad_handle_acct_info_recv(req, &dp_error, &req_error_text); + talloc_zfree(req); + if (dp_error == DP_ERR_OK) { + if (ret == EOK) { + error_text = NULL; + } else { + DEBUG(SSSDBG_FATAL_FAILURE, + ("Bug: dp_error is OK on failed request")); + dp_error = DP_ERR_FATAL; + error_text = req_error_text; + } + } else if (dp_error == DP_ERR_OFFLINE) { + error_text = "Offline"; + } else if (dp_error == DP_ERR_FATAL && ret == ENOMEM) { + error_text = "Out of memory"; + } else { + error_text = req_error_text; + } + + return be_req_terminate(be_req, dp_error, ret, error_text); } void |