summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/providers/ad/ad_id.c263
1 files changed, 262 insertions, 1 deletions
diff --git a/src/providers/ad/ad_id.c b/src/providers/ad/ad_id.c
index 5d8c78371..9c4abc8c9 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