diff options
author | Martin Nagy <mnagy@redhat.com> | 2010-07-12 04:04:34 +0200 |
---|---|---|
committer | Stephen Gallagher <sgallagh@redhat.com> | 2010-09-02 12:23:18 -0400 |
commit | 56d8d19ac9d857580a233d8264e851883b883c67 (patch) | |
tree | 8cb6e8236a24d230ea51f46fe22cd68fd4574e13 /src/providers/ldap/ldap_req_wrap.c | |
parent | 602fa2c3ef0d088b7b834e9d2ebb306d104a79ce (diff) | |
download | sssd-56d8d19ac9d857580a233d8264e851883b883c67.tar.gz sssd-56d8d19ac9d857580a233d8264e851883b883c67.tar.bz2 sssd-56d8d19ac9d857580a233d8264e851883b883c67.zip |
Make ldap bind asynchronous
Every ldap function that could possibly create a new connection is now
wrapped in a tevent_req. If the connection is created, we will call the
function again after the socket is ready for writing.
Diffstat (limited to 'src/providers/ldap/ldap_req_wrap.c')
-rw-r--r-- | src/providers/ldap/ldap_req_wrap.c | 689 |
1 files changed, 689 insertions, 0 deletions
diff --git a/src/providers/ldap/ldap_req_wrap.c b/src/providers/ldap/ldap_req_wrap.c new file mode 100644 index 00000000..bd86947f --- /dev/null +++ b/src/providers/ldap/ldap_req_wrap.c @@ -0,0 +1,689 @@ +/* + SSSD + + LDAP tevent_req wrappers + + Authors: + Martin Nagy <mnagy@redhat.com> + + Copyright (C) 2010 Red Hat + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <sys/time.h> + +#include <talloc.h> +#include <tevent.h> + +#include "providers/ldap/sdap.h" +#include "providers/ldap/sdap_async_private.h" + +#ifdef HAVE_LDAP_CONNCB +# ifdef LDAP_OPT_CONNECT_ASYNC +# define IS_CONNECTING(r) ((r) == LDAP_X_CONNECTING) +# endif +#else +# define IS_CONNECTING(r) 0 +#endif + +/* Older openldap library doesn't have ldap_controls_dup(). */ +static LDAPControl **dup_ldap_controls(void *mem_ctx, + LDAPControl *const *controls) +{ + int i; + LDAPControl **newc; + + if (!controls) return NULL; + + for (i = 0; controls[i]; i++); + + newc = talloc_array(mem_ctx, LDAPControl *, i + 1); + if (!newc) goto fail; + for (i = 0; controls[i]; i++) { + newc[i] = talloc(newc, LDAPControl); + if (!newc[i]) goto fail; + + if (controls[i]->ldctl_oid) { + newc[i]->ldctl_oid = talloc_strdup(newc[i], controls[i]->ldctl_oid); + if (!newc[i]->ldctl_oid) goto fail; + } else { + newc[i]->ldctl_oid = NULL; + } + if (controls[i]->ldctl_value.bv_val) { + newc[i]->ldctl_value.bv_len = controls[i]->ldctl_value.bv_len; + newc[i]->ldctl_value.bv_val = talloc_memdup(newc[i], + controls[i]->ldctl_value.bv_val, + newc[i]->ldctl_value.bv_len); + if (!newc[i]->ldctl_value.bv_val) goto fail; + newc[i]->ldctl_value.bv_val[newc[i]->ldctl_value.bv_len] = '\0'; + } else { + newc[i]->ldctl_value.bv_len = 0; + newc[i]->ldctl_value.bv_val = NULL; + } + newc[i]->ldctl_iscritical = controls[i]->ldctl_iscritical; + } + newc[i] = NULL; + + return newc; + +fail: + DEBUG(1, ("out of memory\n")); + talloc_free(newc); + return NULL; +} + +/* + * ldap_sasl_bind() + */ +struct sasl_bind_state { + struct sdap_handle *sh; + char *dn; + char *mechanism; + struct berval *cred; + LDAPControl **sctrls; + LDAPControl **cctrls; + + int msgid; + int ret; +}; + +static int ldap_sasl_bind_try(void *cb_data); + +struct tevent_req *ldap_sasl_bind_send(void *mem_ctx, struct tevent_context *ev, + struct sdap_handle *sh, const char *dn, + const char *mechanism, + struct berval *cred, + LDAPControl **sctrls, + LDAPControl **cctrls) +{ + struct tevent_req *req; + struct sasl_bind_state *state; + + req = tevent_req_create(mem_ctx, &state, struct sasl_bind_state); + if (!req) return NULL; + + state->sh = sh; + if (dn) { + state->dn = talloc_strdup(state, dn); + if (!state->dn) { + goto fail; + } + } + if (cred) { + state->cred = ber_dupbv(NULL, cred); + if (!state->cred) { + goto fail; + } + } + if (sctrls) { + state->sctrls = dup_ldap_controls(state, sctrls); + if (!state->sctrls) { + goto fail; + } + } + if (cctrls) { + state->cctrls = dup_ldap_controls(state, cctrls); + if (!state->cctrls) { + goto fail; + } + } + + if (ldap_sasl_bind_try(req)) { + tevent_req_post(req, ev); + } + + return req; + +fail: + tevent_req_error(req, ENOMEM); + tevent_req_post(req, ev); + return req; +} + +static int ldap_sasl_bind_try(void *cb_data) +{ + struct tevent_req *req; + struct sasl_bind_state *state; + int ret; + + req = talloc_get_type(cb_data, struct tevent_req); + state = tevent_req_data(req, struct sasl_bind_state); + + DEBUG(4, ("calling ldap_sasl_bind(dn = \"%s\")\n", state->dn)); + set_fd_retry_cb(state->sh, ldap_sasl_bind_try, cb_data); + ret = ldap_sasl_bind(state->sh->ldap, state->dn, state->mechanism, + state->cred, state->sctrls, state->cctrls, + &state->msgid); + set_fd_retry_cb(state->sh, NULL, NULL); + + if (IS_CONNECTING(ret)) { + DEBUG(4, ("connection in progress, will try again later\n")); + return 0; + } + + if (ret != LDAP_SUCCESS || state->msgid == -1) { + ret = ldap_get_option(state->sh->ldap, LDAP_OPT_RESULT_CODE, + &state->ret); + if (ret != LDAP_OPT_SUCCESS) { + state->ret = LDAP_LOCAL_ERROR; + } + DEBUG(1, ("ldap_sasl_bind() failed (%d) [%s]\n", state->ret, + ldap_err2string(state->ret))); + tevent_req_error(req, EIO); + } else { + DEBUG(4, ("ldap_sasl_bind() succeeded, msgid = %d\n", state->msgid)); + state->ret = LDAP_SUCCESS; + tevent_req_done(req); + } + + return 1; +} + +int ldap_sasl_bind_recv(struct tevent_req *req, int *retp, int *msgidp) +{ + struct sasl_bind_state *state; + state = tevent_req_data(req, struct sasl_bind_state); + + /* Free stuff that we allocated. */ + ber_bvfree(state->cred); + + if (retp) *retp = state->ret; + if (msgidp) *msgidp = state->msgid; + + TEVENT_REQ_RETURN_ON_ERROR(req); + + return EOK; +} + +/* + * ldap_search_ext() + */ +struct search_ext_state { + struct sdap_handle *sh; + char *base; + int scope; + char *filter; + char **attrs; + int attrsonly; + LDAPControl **sctrls; + LDAPControl **cctrls; + struct timeval *timeout; + int sizelimit; + + int msgid; + int ret; +}; + +static int ldap_search_ext_try(void *cb_data); + +struct tevent_req *ldap_search_ext_send(void *mem_ctx, + struct tevent_context *ev, + struct sdap_handle *sh, + const char *base, int scope, + const char *filter, const char **attrs, + int attrsonly, LDAPControl **sctrls, + LDAPControl **cctrls, + const struct timeval *timeout, + int sizelimit) +{ + struct tevent_req *req; + struct search_ext_state *state; + int i; + + req = tevent_req_create(mem_ctx, &state, struct search_ext_state); + if (!req) return NULL; + + state->sh = sh; + state->scope = scope; + state->attrsonly = attrsonly; + state->sizelimit = sizelimit; + + if (base) { + state->base = talloc_strdup(state, base); + if (!state->base) goto fail; + } + if (filter) { + state->filter = talloc_strdup(state, filter); + if (!state->filter) goto fail; + } + if (attrs) { + for (i = 0; attrs[i]; i++); + state->attrs = talloc_array(state, char *, i + 1); + if (!state->attrs) goto fail; + for (i = 0; attrs[i]; i++) { + state->attrs[i] = talloc_strdup(state->attrs, attrs[i]); + if (!state->attrs[i]) goto fail; + } + state->attrs[i] = NULL; + } + if (sctrls) { + state->sctrls = dup_ldap_controls(state, sctrls); + if (!state->sctrls) goto fail; + } + if (cctrls) { + state->cctrls = dup_ldap_controls(state, cctrls); + if (!state->cctrls) goto fail; + } + if (timeout) { + state->timeout = talloc(state, struct timeval); + if (!state->timeout) goto fail; + state->timeout->tv_sec = timeout->tv_sec; + state->timeout->tv_usec = timeout->tv_usec; + } + + if (ldap_search_ext_try(req)) { + tevent_req_post(req, ev); + } + + return req; + +fail: + tevent_req_error(req, ENOMEM); + tevent_req_post(req, ev); + return req; +} + +static int ldap_search_ext_try(void *cb_data) +{ + struct tevent_req *req; + struct search_ext_state *state; + + req = talloc_get_type(cb_data, struct tevent_req); + state = tevent_req_data(req, struct search_ext_state); + + DEBUG(4, ("calling ldap_search_ext()\n")); + set_fd_retry_cb(state->sh, ldap_search_ext_try, cb_data); + state->ret = ldap_search_ext(state->sh->ldap, state->base, state->scope, + state->filter, state->attrs, state->attrsonly, + state->sctrls, state->cctrls, state->timeout, + state->sizelimit, &state->msgid); + set_fd_retry_cb(state->sh, NULL, NULL); + + if (IS_CONNECTING(state->ret)) { + DEBUG(4, ("connection in progress, will try again later\n")); + return 0; + } + + if (state->ret != LDAP_SUCCESS) { + DEBUG(1, ("ldap_search_ext() failed (%d) [%s]\n", state->ret, + ldap_err2string(state->ret))); + tevent_req_error(req, EIO); + } else { + DEBUG(4, ("ldap_search_ext() succeeded, msgid = %d\n", state->msgid)); + tevent_req_done(req); + } + + return 1; +} + +int ldap_search_ext_recv(struct tevent_req *req, int *retp, int *msgidp) +{ + struct search_ext_state *state; + state = tevent_req_data(req, struct search_ext_state); + + if (retp) *retp = state->ret; + if (msgidp) *msgidp = state->msgid; + + TEVENT_REQ_RETURN_ON_ERROR(req); + + return EOK; +} + +/* + * ldap_extended_operation() + */ +struct extended_operation_state { + struct sdap_handle *sh; + char *requestoid; + struct berval *requestdata; + LDAPControl **sctrls; + LDAPControl **cctrls; + + int msgid; + int ret; +}; + +static int ldap_extended_operation_try(void *cb_data); + +struct tevent_req *ldap_extended_operation_send(void *mem_ctx, + struct tevent_context *ev, + struct sdap_handle *sh, + const char *requestoid, + struct berval *requestdata, + LDAPControl **sctrls, + LDAPControl **cctrls) +{ + struct tevent_req *req; + struct extended_operation_state *state; + + req = tevent_req_create(mem_ctx, &state, struct extended_operation_state); + if (!req) return NULL; + + state->sh = sh; + + if (requestoid) { + state->requestoid = talloc_strdup(state, requestoid); + if (!state->requestoid) goto fail; + } + if (requestdata) { + state->requestdata = ber_dupbv(NULL, requestdata); + if (!state->requestdata) { + goto fail; + } + } + if (sctrls) { + state->sctrls = dup_ldap_controls(state, sctrls); + if (!state->sctrls) goto fail; + } + if (cctrls) { + state->cctrls = dup_ldap_controls(state, cctrls); + if (!state->cctrls) goto fail; + } + + if (ldap_extended_operation_try(req)) { + tevent_req_post(req, ev); + } + + return req; + +fail: + tevent_req_error(req, ENOMEM); + tevent_req_post(req, ev); + return req; +} + +static int ldap_extended_operation_try(void *cb_data) +{ + struct tevent_req *req; + struct extended_operation_state *state; + + req = talloc_get_type(cb_data, struct tevent_req); + state = tevent_req_data(req, struct extended_operation_state); + + DEBUG(4, ("calling ldap_extended_operation()\n")); + set_fd_retry_cb(state->sh, ldap_extended_operation_try, cb_data); + state->ret = ldap_extended_operation(state->sh->ldap, state->requestoid, + state->requestdata, state->sctrls, + state->cctrls, &state->msgid); + set_fd_retry_cb(state->sh, NULL, NULL); + + if (IS_CONNECTING(state->ret)) { + DEBUG(4, ("connection in progress, will try again later\n")); + return 0; + } + + if (state->ret != LDAP_SUCCESS) { + DEBUG(1, ("ldap_extended_operation() failed (%d) [%s]\n", state->ret, + ldap_err2string(state->ret))); + tevent_req_error(req, EIO); + } else { + DEBUG(4, ("ldap_extended_operation() succeeded, msgid = %d\n", + state->msgid)); + tevent_req_done(req); + } + + return 1; +} + +int ldap_extended_operation_recv(struct tevent_req *req, + int *retp, int *msgidp) +{ + struct extended_operation_state *state; + state = tevent_req_data(req, struct extended_operation_state); + + /* Free stuff that we allocated. */ + ber_bvfree(state->requestdata); + + if (retp) *retp = state->ret; + if (msgidp) *msgidp = state->msgid; + + TEVENT_REQ_RETURN_ON_ERROR(req); + + return EOK; +} + +/* + * ldap_start_tls() + */ +struct start_tls_state { + struct sdap_handle *sh; + LDAPControl **sctrls; + LDAPControl **cctrls; + + int msgid; + int ret; +}; + +static int ldap_start_tls_try(void *cb_data); + +struct tevent_req *ldap_start_tls_send(void *mem_ctx, + struct tevent_context *ev, + struct sdap_handle *sh, + LDAPControl **sctrls, + LDAPControl **cctrls) +{ + struct tevent_req *req; + struct start_tls_state *state; + + req = tevent_req_create(mem_ctx, &state, struct start_tls_state); + if (!req) return NULL; + + state->sh = sh; + + if (sctrls) { + state->sctrls = dup_ldap_controls(state, sctrls); + if (!state->sctrls) goto fail; + } + if (cctrls) { + state->cctrls = dup_ldap_controls(state, cctrls); + if (!state->cctrls) goto fail; + } + + if (ldap_start_tls_try(req)) { + tevent_req_post(req, ev); + } + + return req; + +fail: + tevent_req_error(req, ENOMEM); + tevent_req_post(req, ev); + return req; +} + +static int ldap_start_tls_try(void *cb_data) +{ + struct tevent_req *req; + struct start_tls_state *state; + int optret; + char *errmsg; + + req = talloc_get_type(cb_data, struct tevent_req); + state = tevent_req_data(req, struct start_tls_state); + + DEBUG(4, ("calling ldap_start_tls()\n")); + set_fd_retry_cb(state->sh, ldap_start_tls_try, cb_data); + state->ret = ldap_start_tls(state->sh->ldap, state->sctrls, state->cctrls, + &state->msgid); + set_fd_retry_cb(state->sh, NULL, NULL); + + if (IS_CONNECTING(state->ret)) { + DEBUG(4, ("connection in progress, will try again later\n")); + return 0; + } + + if (state->ret != LDAP_SUCCESS) { + optret = ldap_get_option(state->sh->ldap, + SDAP_DIAGNOSTIC_MESSAGE, (void *)&errmsg); + if (optret == LDAP_SUCCESS) { + DEBUG(1, ("ldap_start_tls failed: (%d) [%s] [%s]\n", + state->ret, + ldap_err2string(state->ret), + errmsg)); + ldap_memfree(errmsg); + } else { + DEBUG(1, ("ldap_start_tls failed: (%d) [%s]\n", state->ret, + ldap_err2string(state->ret))); + } + tevent_req_error(req, EIO); + } else { + DEBUG(4, ("ldap_start_tls() succeeded, msgid = %d\n", state->msgid)); + tevent_req_done(req); + } + + return 1; +} + +int ldap_start_tls_recv(struct tevent_req *req, int *retp, int *msgidp) +{ + struct start_tls_state *state; + state = tevent_req_data(req, struct start_tls_state); + + if (retp) *retp = state->ret; + if (msgidp) *msgidp = state->msgid; + + TEVENT_REQ_RETURN_ON_ERROR(req); + + return EOK; +} + +/* + * ldap_sasl_interactive_bind() + */ +struct sasl_interactive_bind_state { + struct sdap_handle *sh; + char *dn; + char *mechanism; + LDAPControl **sctrls; + LDAPControl **cctrls; + unsigned flags; + LDAP_SASL_INTERACT_PROC *interact; + void *defaults; +}; + +static int ldap_sasl_interactive_bind_try(void *cb_data); + +struct tevent_req * +ldap_sasl_interactive_bind_send(void *mem_ctx, + struct tevent_context *ev, + struct sdap_handle *sh, + const char *dn, + const char *mechanism, + LDAPControl **sctrls, + LDAPControl **cctrls, + unsigned flags, + LDAP_SASL_INTERACT_PROC *interact, + void *defaults) +{ + struct tevent_req *req; + struct sasl_interactive_bind_state *state; + + req = tevent_req_create(mem_ctx, &state, + struct sasl_interactive_bind_state); + if (!req) return NULL; + + state->sh = sh; + state->flags = flags; + state->interact = interact; + state->defaults = defaults; + if (dn) { + state->dn = talloc_strdup(state, dn); + if (!state->dn) { + goto fail; + } + } + if (mechanism) { + state->mechanism = talloc_strdup(state, mechanism); + if (!state->mechanism) { + goto fail; + } + } + if (sctrls) { + state->sctrls = dup_ldap_controls(state, sctrls); + if (!state->sctrls) { + goto fail; + } + } + if (cctrls) { + state->cctrls = dup_ldap_controls(state, cctrls); + if (!state->cctrls) { + goto fail; + } + } + + if (ldap_sasl_interactive_bind_try(req)) { + tevent_req_post(req, ev); + } + + return req; + +fail: + tevent_req_error(req, ENOMEM); + tevent_req_post(req, ev); + return req; +} + +static int ldap_sasl_interactive_bind_try(void *cb_data) +{ + struct tevent_req *req; + struct sasl_interactive_bind_state *state; + int ret; + + req = talloc_get_type(cb_data, struct tevent_req); + state = tevent_req_data(req, struct sasl_interactive_bind_state); + + /* FIXME: Warning, this is a sync call! + * No async variant exist in openldap libraries yet */ + + DEBUG(4, ("calling ldap_sasl_interactive_bind_s(dn = \"%s\")\n", + state->dn)); + set_fd_retry_cb(state->sh, ldap_sasl_interactive_bind_try, cb_data); + ret = ldap_sasl_interactive_bind_s(state->sh->ldap, state->dn, + state->mechanism, state->sctrls, + state->cctrls, state->flags, + state->interact, state->defaults); + set_fd_retry_cb(state->sh, NULL, NULL); + + if (IS_CONNECTING(ret)) { + DEBUG(4, ("connection in progress, will try again later\n")); + return 0; + } + + if (ret != LDAP_SUCCESS) { + DEBUG(1, ("ldap_sasl_bind failed (%d) [%s]\n", + ret, ldap_err2string(ret))); + + if (ret == LDAP_SERVER_DOWN) { + tevent_req_error(req, ETIMEDOUT); + } else { + tevent_req_error(req, EIO); + } + } else { + DEBUG(4, ("ldap_sasl_interactive_bind() succeeded\n")); + tevent_req_done(req); + } + + return 1; +} + +int ldap_sasl_interactive_bind_recv(struct tevent_req *req) +{ + struct sasl_interactive_bind_state *state; + state = tevent_req_data(req, struct sasl_interactive_bind_state); + + TEVENT_REQ_RETURN_ON_ERROR(req); + + return EOK; +} |