From 73a91a7a41f49c2ab0015cacd04430ed521900d0 Mon Sep 17 00:00:00 2001 From: Martin Nagy Date: Tue, 16 Mar 2010 18:24:58 +0100 Subject: [PATCH 1/2] First shot at asynchronous LDAP binding Only simple bind for now. --- src/providers/ldap/sdap.h | 9 ++ src/providers/ldap/sdap_async.c | 133 ++++++++++++++++++++++++++-- src/providers/ldap/sdap_async_connection.c | 60 ++++++++++--- src/providers/ldap/sdap_async_private.h | 5 + 4 files changed, 189 insertions(+), 18 deletions(-) diff --git a/src/providers/ldap/sdap.h b/src/providers/ldap/sdap.h index 007185f..5aa0fbe 100644 --- a/src/providers/ldap/sdap.h +++ b/src/providers/ldap/sdap.h @@ -33,11 +33,19 @@ struct sdap_msg { struct sdap_op; +typedef int (sdap_bind_op_callback_t)(struct tevent_req *, void *); typedef void (sdap_op_callback_t)(struct sdap_op *op, struct sdap_msg *, int, void *); struct sdap_handle; +struct sdap_bind_op { + struct sdap_handle *sh; + sdap_bind_op_callback_t *callback; + struct tevent_req *req; + void *data; +}; + struct sdap_op { struct sdap_op *prev, *next; struct sdap_handle *sh; @@ -77,6 +85,7 @@ struct sdap_handle { struct tevent_fd *fde; #endif + struct sdap_bind_op *bind_op; struct sdap_op *ops; }; diff --git a/src/providers/ldap/sdap_async.c b/src/providers/ldap/sdap_async.c index 959c08a..b35d62b 100644 --- a/src/providers/ldap/sdap_async.c +++ b/src/providers/ldap/sdap_async.c @@ -127,7 +127,10 @@ static void sdap_handle_release(struct sdap_handle *sh) /* ==Parse-Results-And-Handle-Disconnections============================== */ static void sdap_process_message(struct tevent_context *ev, struct sdap_handle *sh, LDAPMessage *msg); -static void sdap_process_result(struct tevent_context *ev, void *pvt); +static void sdap_finish_bind(struct ldap_cb_data *cb_data, + struct tevent_fd *fde); +static void sdap_process_result(struct tevent_context *ev, + struct sdap_handle *sh); static void sdap_process_next_reply(struct tevent_context *ev, struct tevent_timer *te, struct timeval tv, void *pvt); @@ -136,19 +139,77 @@ static void sdap_ldap_result(struct tevent_context *ev, struct tevent_fd *fde, uint16_t flags, void *pvt) { - sdap_process_result(ev, pvt); + struct ldap_cb_data *cb_data; + + cb_data = talloc_get_type(pvt, struct ldap_cb_data); + + DEBUG(4, ("flags=%d\n", flags)); // FIXME: Remove this line + + if (flags & TEVENT_FD_WRITE) { + sdap_finish_bind(cb_data, fde); + } + + if (flags & TEVENT_FD_READ) { + sdap_process_result(ev, cb_data->sh); + } } static void sdap_ldap_next_result(struct tevent_context *ev, struct tevent_timer *te, struct timeval tv, void *pvt) { - sdap_process_result(ev, pvt); + struct sdap_handle *sh = talloc_get_type(pvt, struct sdap_handle); + + sdap_process_result(ev, sh); } -static void sdap_process_result(struct tevent_context *ev, void *pvt) +static void sdap_finish_bind(struct ldap_cb_data *cb_data, + struct tevent_fd *fde) +{ + struct sdap_handle *sh = cb_data->sh; + struct sdap_bind_op *op = sh->bind_op; + struct fd_event_item *fd_event_item; + int ret; + + DLIST_FOR_EACH(fd_event_item, cb_data->fd_list) { + if (fd_event_item->fde == fde) { + break; + } + } + if (!fd_event_item) { + DEBUG(1, ("Bug: Can't find the right fd_event_item\n")); + return; + } + + DEBUG(8, ("Trace: sh[%p], bind_op[%p], ldap[%p], fd[%d]\n", + sh, op, sh->ldap, fd_event_item->fd)); + + if (op) { + ret = op->callback(op->req, op->data); + if (ret == LDAP_X_CONNECTING || ret != LDAP_SUCCESS) { + return; + } + } else { + DEBUG(1, ("Bug: No bind operation to be performed\n")); + } + + /* If we were successful, or the bind operation + * isn't there, watch the fd only for reading. */ + talloc_free(fd_event_item->fde); + fd_event_item->fde = tevent_add_fd(cb_data->ev, fd_event_item, + fd_event_item->fd, TEVENT_FD_READ, + sdap_ldap_result, cb_data); + if (fd_event_item->fde == NULL) { + DEBUG(1, ("tevent_add_fd failed.\n")); + DLIST_REMOVE(fd_event_item, cb_data->fd_list); + talloc_free(fd_event_item); + return; + } +} + +static void sdap_process_result(struct tevent_context *ev, + struct sdap_handle *sh) { - struct sdap_handle *sh = talloc_get_type(pvt, struct sdap_handle); struct timeval no_timeout = {0, 0}; struct tevent_timer *te; LDAPMessage *msg; @@ -361,8 +422,8 @@ int sdap_ldap_connect_callback_add(LDAP *ld, Sockbuf *sb, LDAPURLDesc *srv, } fd_event_item->fde = tevent_add_fd(cb_data->ev, fd_event_item, ber_fd, - TEVENT_FD_READ, sdap_ldap_result, - cb_data->sh); + TEVENT_FD_READ | TEVENT_FD_WRITE, + sdap_ldap_result, cb_data); if (fd_event_item->fde == NULL) { DEBUG(1, ("tevent_add_fd failed.\n")); talloc_free(fd_event_item); @@ -450,6 +511,64 @@ int sdap_install_ldap_callbacks(struct sdap_handle *sh, /* ==LDAP-Operations-Helpers============================================== */ +static int sdap_bind_op_destructor(void *ptr) +{ + struct sdap_bind_op *op = talloc_get_type(ptr, struct sdap_bind_op); + + op->sh->bind_op = NULL; + return 0; +} + +static void sdap_bind_op_timeout(struct tevent_req *req) +{ + struct sdap_bind_op *op; + + op = tevent_req_callback_data(req, struct sdap_bind_op); + + tevent_req_error(op->req, ETIMEDOUT); +} + +int sdap_bind_op_add(TALLOC_CTX *memctx, struct tevent_context *ev, + struct sdap_handle *sh, struct tevent_req *req, + sdap_bind_op_callback_t *callback, void *data, + int timeout) +{ + struct sdap_bind_op *op; + + if (sh->bind_op) { + DEBUG(1, ("Bug: Pending bind operation\n")); + talloc_zfree(sh->bind_op); + } + + op = talloc(memctx, struct sdap_bind_op); + if (!op) return ENOMEM; + + op->sh = sh; + op->callback = callback; + op->req = req; + op->data = data; + + if (timeout) { + struct tevent_req *subreq; + struct timeval tv; + + tv = tevent_timeval_current_ofs(timeout, 0); + + /* Allocate on op so the timeout is removed if the op gets freed. */ + subreq = tevent_wakeup_send(op, ev, tv); + if (!subreq) { + talloc_zfree(op); + return ENOMEM; + } + tevent_req_set_callback(subreq, sdap_bind_op_timeout, op); + } + + sh->bind_op = op; + talloc_set_destructor((TALLOC_CTX *)op, sdap_bind_op_destructor); + + return EOK; +} + static int sdap_op_destructor(void *mem) { struct sdap_op *op = (struct sdap_op *)mem; diff --git a/src/providers/ldap/sdap_async_connection.c b/src/providers/ldap/sdap_async_connection.c index fe8a501..7f44ba1 100644 --- a/src/providers/ldap/sdap_async_connection.c +++ b/src/providers/ldap/sdap_async_connection.c @@ -149,6 +149,13 @@ struct tevent_req *sdap_connect_send(TALLOC_CTX *memctx, DEBUG(1, ("Failed to set connection callback\n")); goto fail; } + + lret = ldap_set_option(state->sh->ldap, LDAP_OPT_CONNECT_ASYNC, + LDAP_OPT_ON); + if (lret != LDAP_OPT_SUCCESS) { + DEBUG(1, ("Failed to set connection as asynchronous\n")); + goto fail; + } #endif /* if we do not use start_tls the connection is not really connected yet @@ -284,6 +291,8 @@ struct simple_bind_state { static void simple_bind_done(struct sdap_op *op, struct sdap_msg *reply, int error, void *pvt); +static int try_simple_bind(struct tevent_req *req, + void *pvt); static struct tevent_req *simple_bind_send(TALLOC_CTX *memctx, struct tevent_context *ev, @@ -294,14 +303,11 @@ static struct tevent_req *simple_bind_send(TALLOC_CTX *memctx, struct tevent_req *req; struct simple_bind_state *state; int ret = EOK; - int msgid; - int ldap_err; - LDAPControl *request_controls[2]; req = tevent_req_create(memctx, &state, struct simple_bind_state); if (!req) return NULL; - state->reply = talloc(state, struct sdap_msg); + state->reply = talloc_zero(state, struct sdap_msg); if (!state->reply) { talloc_zfree(req); return NULL; @@ -312,6 +318,31 @@ static struct tevent_req *simple_bind_send(TALLOC_CTX *memctx, state->user_dn = user_dn; state->pw = pw; + ret = try_simple_bind(req, state); + if (ret == LDAP_X_CONNECTING) { + /* FIXME: get timeouts from configuration, for now 5 secs. */ + ret = sdap_bind_op_add(req, ev, sh, req, try_simple_bind, state, 5); + if (ret != EOK) { + tevent_req_error(req, ret); + tevent_req_post(req, ev); + } + } + + return req; +} + +static int try_simple_bind(struct tevent_req *req, void *pvt) +{ + struct simple_bind_state *state; + int ret; + int ldap_err; + int msgid; + struct sdap_handle *sh; + LDAPControl *request_controls[2]; + + state = talloc_get_type(pvt, struct simple_bind_state); + sh = state->sh; + ret = sss_ldap_control_create(LDAP_CONTROL_PASSWORDPOLICYREQUEST, 0, NULL, 0, &request_controls[0]); if (ret != LDAP_SUCCESS) { @@ -322,11 +353,16 @@ static struct tevent_req *simple_bind_send(TALLOC_CTX *memctx, DEBUG(4, ("Executing simple bind as: %s\n", state->user_dn)); - ret = ldap_sasl_bind(state->sh->ldap, state->user_dn, LDAP_SASL_SIMPLE, + ret = ldap_sasl_bind(sh->ldap, state->user_dn, LDAP_SASL_SIMPLE, state->pw, request_controls, NULL, &msgid); ldap_control_free(request_controls[0]); - if (ret == -1 || msgid == -1) { - ret = ldap_get_option(state->sh->ldap, + if (ret == LDAP_X_CONNECTING) { + /* The ldap socket is not connected yet, + * we have to try again later. */ + DEBUG(4, ("Simple bind is in progress\n")); + return LDAP_X_CONNECTING; + } else if (ret == -1 || msgid == -1) { + ret = ldap_get_option(sh->ldap, LDAP_OPT_RESULT_CODE, &ldap_err); if (ret != LDAP_OPT_SUCCESS) { DEBUG(1, ("ldap_bind failed (couldn't get ldap error)\n")); @@ -349,23 +385,25 @@ static struct tevent_req *simple_bind_send(TALLOC_CTX *memctx, } /* FIXME: get timeouts from configuration, for now 5 secs. */ - ret = sdap_op_add(state, ev, sh, msgid, + ret = sdap_op_add(state, state->ev, state->sh, msgid, simple_bind_done, req, 5, &state->op); if (ret) { DEBUG(1, ("Failed to set up operation!\n")); + ret = LDAP_OTHER; goto fail; } - return req; + return LDAP_SUCCESS; fail: + /* XXX: Maybe move this to simple_bind_send() and sdap_finish_bind()? */ if (ret == LDAP_SERVER_DOWN) { tevent_req_error(req, ETIMEDOUT); } else { tevent_req_error(req, EIO); } - tevent_req_post(req, ev); - return req; + tevent_req_post(req, state->ev); + return ret; } static void simple_bind_done(struct sdap_op *op, diff --git a/src/providers/ldap/sdap_async_private.h b/src/providers/ldap/sdap_async_private.h index 55f76ed..177106d 100644 --- a/src/providers/ldap/sdap_async_private.h +++ b/src/providers/ldap/sdap_async_private.h @@ -38,6 +38,11 @@ int sdap_install_ldap_callbacks(struct sdap_handle *sh, struct tevent_context *ev); #endif +int sdap_bind_op_add(void *memctx, struct tevent_context *ev, + struct sdap_handle *sh, struct tevent_req *req, + sdap_bind_op_callback_t *callback, void *data, + int timeout); + int sdap_op_add(TALLOC_CTX *memctx, struct tevent_context *ev, struct sdap_handle *sh, int msgid, sdap_op_callback_t *callback, void *data, -- 1.6.2.5