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 | |
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.
-rw-r--r-- | Makefile.am | 3 | ||||
-rw-r--r-- | src/providers/ldap/ldap_req_wrap.c | 689 | ||||
-rw-r--r-- | src/providers/ldap/ldap_req_wrap.h | 103 | ||||
-rw-r--r-- | src/providers/ldap/sdap.h | 16 | ||||
-rw-r--r-- | src/providers/ldap/sdap_async.c | 271 | ||||
-rw-r--r-- | src/providers/ldap/sdap_async_connection.c | 218 | ||||
-rw-r--r-- | src/providers/ldap/sdap_async_private.h | 8 | ||||
-rw-r--r-- | src/providers/ldap/sdap_fd_events.c | 81 |
8 files changed, 1222 insertions, 167 deletions
diff --git a/Makefile.am b/Makefile.am index 7482e811..c485bc0d 100644 --- a/Makefile.am +++ b/Makefile.am @@ -331,6 +331,7 @@ dist_noinst_HEADERS = \ src/providers/krb5/krb5_common.h \ src/providers/krb5/krb5_utils.h \ src/providers/ldap/ldap_common.h \ + src/providers/ldap/ldap_req_wrap.h \ src/providers/ldap/sdap.h \ src/providers/ldap/sdap_access.h \ src/providers/ldap/sdap_async.h \ @@ -731,6 +732,7 @@ libsss_ldap_la_SOURCES = \ src/providers/ldap/ldap_auth.c \ src/providers/ldap/ldap_init.c \ src/providers/ldap/ldap_common.c \ + src/providers/ldap/ldap_req_wrap.c \ src/providers/ldap/sdap_async.c \ src/providers/ldap/sdap_async_accounts.c \ src/providers/ldap/sdap_async_connection.c \ @@ -811,6 +813,7 @@ libsss_ipa_la_SOURCES = \ src/providers/ldap/ldap_id_cleanup.c \ src/providers/ldap/ldap_auth.c \ src/providers/ldap/ldap_common.c \ + src/providers/ldap/ldap_req_wrap.c \ src/providers/ldap/sdap_async.c \ src/providers/ldap/sdap_async_accounts.c \ src/providers/ldap/sdap_async_connection.c \ 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; +} diff --git a/src/providers/ldap/ldap_req_wrap.h b/src/providers/ldap/ldap_req_wrap.h new file mode 100644 index 00000000..85ed2580 --- /dev/null +++ b/src/providers/ldap/ldap_req_wrap.h @@ -0,0 +1,103 @@ +/* + 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/>. +*/ + +#ifndef _LDAP_REQ_WRAP_H_ +#define _LDAP_REQ_WRAP_H_ + +#include <talloc.h> +#include <tevent.h> + +#include "providers/ldap/sdap.h" + +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); + +int ldap_sasl_bind_recv(struct tevent_req *req, + int *ret, + int *msgidp); + + +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); + +int ldap_search_ext_recv(struct tevent_req *req, + int *retp, + int *msgidp); + + +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); + +int ldap_extended_operation_recv(struct tevent_req *req, + int *retp, + int *msgidp); + + +struct tevent_req *ldap_start_tls_send(void *mem_ctx, + struct tevent_context *ev, + struct sdap_handle *sh, + LDAPControl **sctrls, + LDAPControl **cctrls); + +int ldap_start_tls_recv(struct tevent_req *req, + int *retp, + int *msgidp); + + +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); + +int ldap_sasl_interactive_bind_recv(struct tevent_req *req); + +#endif /* !_LDAP_REQ_WRAP_H_ */ diff --git a/src/providers/ldap/sdap.h b/src/providers/ldap/sdap.h index 91d91b85..61e7fe25 100644 --- a/src/providers/ldap/sdap.h +++ b/src/providers/ldap/sdap.h @@ -45,6 +45,7 @@ struct sdap_op; typedef void (sdap_op_callback_t)(struct sdap_op *op, struct sdap_msg *, int, void *); +typedef int (fd_wakeup_callback_t)(void *); struct sdap_handle; @@ -63,18 +64,33 @@ struct sdap_op { struct sdap_msg *last; }; +struct ldap_cb_data; + +struct request_spy { + struct fd_event_item *ptr; +}; + struct fd_event_item { struct fd_event_item *prev; struct fd_event_item *next; int fd; struct tevent_fd *fde; + + struct ldap_cb_data *cb_data; + fd_wakeup_callback_t *fd_wakeup_cb; + void *fd_wakeup_cb_data; + struct request_spy *spy; + struct tevent_timer *timeout_watcher; }; struct ldap_cb_data { struct sdap_handle *sh; struct tevent_context *ev; struct fd_event_item *fd_list; + + fd_wakeup_callback_t *wakeup_cb; + void *wakeup_cb_data; }; struct sdap_handle { diff --git a/src/providers/ldap/sdap_async.c b/src/providers/ldap/sdap_async.c index cd61a221..104a72e0 100644 --- a/src/providers/ldap/sdap_async.c +++ b/src/providers/ldap/sdap_async.c @@ -22,6 +22,7 @@ #include <ctype.h> #include "util/util.h" #include "providers/ldap/sdap_async_private.h" +#include "providers/ldap/ldap_req_wrap.h" #define REALM_SEPARATOR '@' #define REPLY_REALLOC_INCREMENT 10 @@ -139,6 +140,8 @@ static void sdap_handle_release(struct sdap_handle *sh) } /* ==Parse-Results-And-Handle-Disconnections============================== */ +static void sdap_finish_bind(struct ldap_cb_data *cb_data, + struct tevent_fd *fde); 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); @@ -146,6 +149,22 @@ static void sdap_process_next_reply(struct tevent_context *ev, struct tevent_timer *te, struct timeval tv, void *pvt); +void sdap_async_ldap_result(struct tevent_context *ev, + struct tevent_fd *fde, + uint16_t flags, void *pvt) +{ + struct ldap_cb_data *cb_data = talloc_get_type(pvt, struct ldap_cb_data); + + if (flags & TEVENT_FD_WRITE) { + sdap_finish_bind(cb_data, fde); + } + + if (flags & TEVENT_FD_READ) { + sdap_process_result(ev, cb_data->sh); + } +} + + void sdap_ldap_result(struct tevent_context *ev, struct tevent_fd *fde, uint16_t flags, void *pvt) { @@ -159,6 +178,103 @@ static void sdap_ldap_next_result(struct tevent_context *ev, sdap_process_result(ev, pvt); } +static void sdap_finish_bind(struct ldap_cb_data *cb_data, + struct tevent_fd *fde) +{ + struct fd_event_item *fd_event_item; + struct request_spy *spy; + struct fd_event_item *spy_ptr; + + DEBUG(8, ("Trace: sh[%p], ldap[%p], fde[%p]\n", + cb_data->sh, cb_data->sh->ldap, fde)); + + DLIST_FOR_EACH(fd_event_item, cb_data->fd_list) { + if (fd_event_item->fde == fde) { + break; + } + } + if (fd_event_item != NULL && fd_event_item->fd_wakeup_cb != NULL) { + if (fd_event_item->spy) { + /* We have to clear the spy in case it is triggered so that + * it does not free fd_event_item. But we will back it up so + * we can restore it in case the wakeup callback signals it + * has not yet finished. */ + spy_ptr = fd_event_item->spy->ptr; + spy = fd_event_item->spy; + + fd_event_item->spy->ptr = NULL; + fd_event_item->spy = NULL; + } else { + spy = NULL; + } + if (fd_event_item->fd_wakeup_cb(fd_event_item->fd_wakeup_cb_data)) { + fd_event_item->fd_wakeup_cb = NULL; + fd_event_item->fd_wakeup_cb_data = NULL; + talloc_zfree(fd_event_item->timeout_watcher); + } else { + /* Restore the spy. */ + if (spy) { + fd_event_item->spy = spy; + spy->ptr = spy_ptr; + } + return; + } + } else if (fd_event_item == NULL) { + DEBUG(1, ("Bug: Couldn't find fd_event_item\n")); + } + + TEVENT_FD_NOT_WRITEABLE(fde); +} + +struct conn_timeout { + struct ldap_cb_data *cb_data; + struct fd_event_item *fd_event_item; +}; + +static void sdap_check_connection_timeout(struct tevent_context *ev, + struct tevent_timer *te, + struct timeval current_time, + void *private_data) +{ + struct conn_timeout *ct = talloc_get_type(private_data, struct conn_timeout); + + ct->fd_event_item->timeout_watcher = NULL; + sdap_finish_bind(ct->cb_data, ct->fd_event_item->fde); +} + +void sdap_add_timeout_watcher(struct ldap_cb_data *cb_data, + struct fd_event_item *fd_event_item) +{ + struct conn_timeout *ct; + struct timeval tv; + struct timeval *timeout; + int ret; + + ct = talloc(fd_event_item, struct conn_timeout); + if (!ct) goto fail; + ct->cb_data = cb_data; + ct->fd_event_item = fd_event_item; + + ret = ldap_get_option(cb_data->sh->ldap, + LDAP_OPT_NETWORK_TIMEOUT, &timeout); + if (ret == LDAP_OPT_SUCCESS) { + tv = tevent_timeval_current_ofs(timeout->tv_sec + 1, 0); + ldap_memfree(timeout); + } else { + DEBUG(1, ("Couldn't get network timeout from ldap\n")); + tv = tevent_timeval_current_ofs(1, 0); + } + + fd_event_item->timeout_watcher = tevent_add_timer(cb_data->ev, + fd_event_item, tv, sdap_check_connection_timeout, ct); + if (!fd_event_item->timeout_watcher) goto fail; + + return; + +fail: + DEBUG(1, ("Out of memory\n")); +} + static void sdap_process_result(struct tevent_context *ev, void *pvt) { struct sdap_handle *sh = talloc_get_type(pvt, struct sdap_handle); @@ -426,6 +542,7 @@ int sdap_op_add(TALLOC_CTX *memctx, struct tevent_context *ev, /* ==Modify-Password====================================================== */ struct sdap_exop_modify_passwd_state { + struct tevent_context *ev; struct sdap_handle *sh; struct sdap_op *op; @@ -434,9 +551,7 @@ struct sdap_exop_modify_passwd_state { char *user_error_message; }; -static void sdap_exop_modify_passwd_done(struct sdap_op *op, - struct sdap_msg *reply, - int error, void *pvt); +static void sdap_exop_modify_passwd_step(struct tevent_req *subreq); struct tevent_req *sdap_exop_modify_passwd_send(TALLOC_CTX *memctx, struct tevent_context *ev, @@ -446,17 +561,18 @@ struct tevent_req *sdap_exop_modify_passwd_send(TALLOC_CTX *memctx, char *new_password) { struct tevent_req *req = NULL; + struct tevent_req *subreq; struct sdap_exop_modify_passwd_state *state; int ret; BerElement *ber = NULL; struct berval *bv = NULL; - int msgid; LDAPControl *request_controls[2]; req = tevent_req_create(memctx, &state, struct sdap_exop_modify_passwd_state); if (!req) return NULL; + state->ev = ev; state->sh = sh; state->user_error_message = NULL; @@ -496,30 +612,59 @@ struct tevent_req *sdap_exop_modify_passwd_send(TALLOC_CTX *memctx, DEBUG(4, ("Executing extended operation\n")); - ret = ldap_extended_operation(state->sh->ldap, LDAP_EXOP_MODIFY_PASSWD, - bv, request_controls, NULL, &msgid); + subreq = ldap_extended_operation_send(state, ev, state->sh, + LDAP_EXOP_MODIFY_PASSWD, bv, + request_controls, NULL); ber_bvfree(bv); ldap_control_free(request_controls[0]); - if (ret == -1 || msgid == -1) { - DEBUG(1, ("ldap_extended_operation failed\n")); - goto fail; + + if (!subreq) { + DEBUG(1, ("ldap_extended_operation_send failed.\n")); + talloc_zfree(req); + return NULL; } - DEBUG(8, ("ldap_extended_operation sent, msgid = %d\n", msgid)); + + tevent_req_set_callback(subreq, sdap_exop_modify_passwd_step, req); + + return req; + +fail: + tevent_req_error(req, EIO); + tevent_req_post(req, ev); + return req; +} + +static void sdap_exop_modify_passwd_done(struct sdap_op *op, + struct sdap_msg *reply, + int error, void *pvt); + +static void sdap_exop_modify_passwd_step(struct tevent_req *subreq) +{ + struct tevent_req *req; + struct sdap_exop_modify_passwd_state *state; + int ret; + int msgid; + int ldap_ret; + + req = tevent_req_callback_data(subreq, struct tevent_req); + state = tevent_req_data(req, struct sdap_exop_modify_passwd_state); + + ret = ldap_extended_operation_recv(subreq, &ldap_ret, &msgid); + talloc_zfree(subreq); + if (ret != EOK) goto fail; /* FIXME: get timeouts from configuration, for now 5 secs. */ - ret = sdap_op_add(state, ev, state->sh, msgid, + ret = sdap_op_add(state, state->ev, state->sh, msgid, sdap_exop_modify_passwd_done, req, 5, &state->op); - if (ret) { + if (ret != EOK) { DEBUG(1, ("Failed to set up operation!\n")); goto fail; } - return req; + return; fail: - tevent_req_error(req, EIO); - tevent_req_post(req, ev); - return req; + tevent_req_error(req, ret); } static void sdap_exop_modify_passwd_done(struct sdap_op *op, @@ -736,10 +881,6 @@ struct sdap_get_generic_state { struct tevent_context *ev; struct sdap_options *opts; struct sdap_handle *sh; - const char *search_base; - int scope; - const char *filter; - const char **attrs; struct sdap_attr_map *map; int map_num_attrs; @@ -753,9 +894,7 @@ struct sdap_get_generic_state { static errno_t add_to_reply(struct sdap_get_generic_state *state, struct sysdb_attrs *msg); -static void sdap_get_generic_done(struct sdap_op *op, - struct sdap_msg *reply, - int error, void *pvt); +static void sdap_get_generic_step(struct tevent_req *subreq); struct tevent_req *sdap_get_generic_send(TALLOC_CTX *memctx, struct tevent_context *ev, @@ -768,13 +907,9 @@ struct tevent_req *sdap_get_generic_send(TALLOC_CTX *memctx, struct sdap_attr_map *map, int map_num_attrs) { - struct tevent_req *req = NULL; - struct sdap_get_generic_state *state = NULL; - char *errmsg; - int lret; - int optret; - int ret; - int msgid; + struct tevent_req *req; + struct tevent_req *subreq; + struct sdap_get_generic_state *state; req = tevent_req_create(memctx, &state, struct sdap_get_generic_state); if (!req) return NULL; @@ -782,10 +917,6 @@ struct tevent_req *sdap_get_generic_send(TALLOC_CTX *memctx, state->ev = ev; state->opts = opts; state->sh = sh; - state->search_base = search_base; - state->scope = scope; - state->filter = filter; - state->attrs = attrs; state->map = map; state->map_num_attrs = map_num_attrs; state->op = NULL; @@ -793,46 +924,49 @@ struct tevent_req *sdap_get_generic_send(TALLOC_CTX *memctx, state->reply_count = 0; state->reply = NULL; - DEBUG(6, ("calling ldap_search_ext with [%s][%s].\n", state->filter, - state->search_base)); + DEBUG(6, ("calling ldap_search_ext with [%s][%s].\n", filter, + search_base)); if (debug_level >= 7) { int i; - if (state->attrs) { - for (i = 0; state->attrs[i]; i++) { - DEBUG(7, ("Requesting attrs: [%s]\n", state->attrs[i])); + if (attrs) { + for (i = 0; attrs[i]; i++) { + DEBUG(7, ("Requesting attrs: [%s]\n", attrs[i])); } } } - lret = ldap_search_ext(state->sh->ldap, state->search_base, - state->scope, state->filter, - discard_const(state->attrs), - false, NULL, NULL, NULL, 0, &msgid); - if (lret != LDAP_SUCCESS) { - DEBUG(3, ("ldap_search_ext failed: %s\n", ldap_err2string(lret))); - if (lret == LDAP_SERVER_DOWN) { - ret = ETIMEDOUT; - optret = ldap_get_option(state->sh->ldap, - SDAP_DIAGNOSTIC_MESSAGE, - (void*)&errmsg); - if (optret == LDAP_SUCCESS) { - DEBUG(3, ("Connection error: %s\n", errmsg)); - sss_log(SSS_LOG_ERR, "LDAP connection error: %s", errmsg); - ldap_memfree(errmsg); - } - else { - sss_log(SSS_LOG_ERR, "LDAP connection error, %s", - ldap_err2string(lret)); - } - } + subreq = ldap_search_ext_send(state, ev, sh, search_base, scope, filter, + attrs, false, NULL, NULL, NULL, 0); + if (!subreq) goto fail; + tevent_req_set_callback(subreq, sdap_get_generic_step, req); - else { - ret = EIO; - } - goto fail; - } - DEBUG(8, ("ldap_search_ext called, msgid = %d\n", msgid)); + return req; + +fail: + tevent_req_error(req, ENOMEM); + tevent_req_post(req, ev); + return req; +} + +static void sdap_get_generic_done(struct sdap_op *op, + struct sdap_msg *reply, + int error, void *pvt); + +static void sdap_get_generic_step(struct tevent_req *subreq) +{ + struct tevent_req *req; + struct sdap_get_generic_state *state; + int ret; + int msgid; + int ldap_ret; + + req = tevent_req_callback_data(subreq, struct tevent_req); + state = tevent_req_data(req, struct sdap_get_generic_state); + + ret = ldap_search_ext_recv(subreq, &ldap_ret, &msgid); + talloc_zfree(subreq); + if (ret != EOK) goto fail; ret = sdap_op_add(state, state->ev, state->sh, msgid, sdap_get_generic_done, req, @@ -844,15 +978,12 @@ struct tevent_req *sdap_get_generic_send(TALLOC_CTX *memctx, goto fail; } - return req; + return; fail: tevent_req_error(req, ret); - tevent_req_post(req, ev); - return req; } - static void sdap_get_generic_done(struct sdap_op *op, struct sdap_msg *reply, int error, void *pvt) diff --git a/src/providers/ldap/sdap_async_connection.c b/src/providers/ldap/sdap_async_connection.c index 806bd5fe..d2ca356f 100644 --- a/src/providers/ldap/sdap_async_connection.c +++ b/src/providers/ldap/sdap_async_connection.c @@ -24,6 +24,7 @@ #include "util/util.h" #include "util/sss_krb5.h" #include "providers/ldap/sdap_async_private.h" +#include "providers/ldap/ldap_req_wrap.h" #define LDAP_X_SSSD_PASSWORD_EXPIRED 0x555D @@ -40,9 +41,7 @@ struct sdap_connect_state { int result; }; -static void sdap_connect_done(struct sdap_op *op, - struct sdap_msg *reply, - int error, void *pvt); +static void sdap_connect_tls_done(struct tevent_req *subreq); struct tevent_req *sdap_connect_send(TALLOC_CTX *memctx, struct tevent_context *ev, @@ -51,14 +50,12 @@ struct tevent_req *sdap_connect_send(TALLOC_CTX *memctx, bool use_start_tls) { struct tevent_req *req; + struct tevent_req *subreq; struct sdap_connect_state *state; struct timeval tv; int ver; int lret; - int optret; int ret = EOK; - int msgid; - char *errmsg = NULL; bool ldap_referrals; req = tevent_req_create(memctx, &state, struct sdap_connect_state); @@ -145,37 +142,12 @@ struct tevent_req *sdap_connect_send(TALLOC_CTX *memctx, DEBUG(4, ("Executing START TLS\n")); - lret = ldap_start_tls(state->sh->ldap, NULL, NULL, &msgid); - if (lret != LDAP_SUCCESS) { - optret = ldap_get_option(state->sh->ldap, - SDAP_DIAGNOSTIC_MESSAGE, - (void*)&errmsg); - if (optret == LDAP_SUCCESS) { - DEBUG(3, ("ldap_start_tls failed: [%s] [%s]\n", - ldap_err2string(lret), - errmsg)); - sss_log(SSS_LOG_ERR, "Could not start TLS. %s", errmsg); - ldap_memfree(errmsg); - } - else { - DEBUG(3, ("ldap_start_tls failed: [%s]\n", - ldap_err2string(lret))); - sss_log(SSS_LOG_ERR, "Could not start TLS. " - "Check for certificate issues."); - } - goto fail; - } - - ret = sdap_set_connected(state->sh, state->ev); - if (ret) goto fail; - - /* FIXME: get timeouts from configuration, for now 5 secs. */ - ret = sdap_op_add(state, ev, state->sh, msgid, - sdap_connect_done, req, 5, &state->op); - if (ret) { - DEBUG(1, ("Failed to set up operation!\n")); + subreq = ldap_start_tls_send(state, ev, state->sh, NULL, NULL); + if (!subreq) { + ret = ENOMEM; goto fail; } + tevent_req_set_callback(subreq, sdap_connect_tls_done, req); return req; @@ -195,6 +167,41 @@ fail: static void sdap_connect_done(struct sdap_op *op, struct sdap_msg *reply, + int error, void *pvt); + +static void sdap_connect_tls_done(struct tevent_req *subreq) +{ + struct tevent_req *req; + struct sdap_connect_state *state; + int ret; + int msgid; + + req = tevent_req_callback_data(subreq, struct tevent_req); + state = tevent_req_data(req, struct sdap_connect_state); + + ret = ldap_start_tls_recv(subreq, NULL, &msgid); + talloc_zfree(subreq); + if (ret != EOK) goto fail; + + ret = sdap_set_connected(state->sh, state->ev); + if (ret != EOK) goto fail; + + /* FIXME: get timeouts from configuration, for now 5 secs. */ + ret = sdap_op_add(state, state->ev, state->sh, msgid, + sdap_connect_done, req, 5, &state->op); + if (ret != EOK) { + DEBUG(1, ("Failed to set up operation!\n")); + goto fail; + } + + return; + +fail: + tevent_req_error(req, ret); +} + +static void sdap_connect_done(struct sdap_op *op, + struct sdap_msg *reply, int error, void *pvt) { struct tevent_req *req = talloc_get_type(pvt, struct tevent_req); @@ -289,9 +296,7 @@ struct simple_bind_state { int result; }; -static void simple_bind_done(struct sdap_op *op, - struct sdap_msg *reply, - int error, void *pvt); +static void simple_bind_step(struct tevent_req *subreq); static struct tevent_req *simple_bind_send(TALLOC_CTX *memctx, struct tevent_context *ev, @@ -302,9 +307,8 @@ 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]; + struct tevent_req *subreq; req = tevent_req_create(memctx, &state, struct simple_bind_state); if (!req) return NULL; @@ -330,47 +334,69 @@ 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, - state->pw, request_controls, NULL, &msgid); + subreq = ldap_sasl_bind_send(state, ev, sh, user_dn, LDAP_SASL_SIMPLE, pw, + request_controls, NULL); ldap_control_free(request_controls[0]); - if (ret == -1 || msgid == -1) { - ret = ldap_get_option(state->sh->ldap, - LDAP_OPT_RESULT_CODE, &ldap_err); - if (ret != LDAP_OPT_SUCCESS) { - DEBUG(1, ("ldap_bind failed (couldn't get ldap error)\n")); - ret = LDAP_LOCAL_ERROR; - } else { - DEBUG(1, ("ldap_bind failed (%d)[%s]\n", - ldap_err, ldap_err2string(ldap_err))); - ret = ldap_err; - } + + if (!subreq) goto fail; + tevent_req_set_callback(subreq, simple_bind_step, req); + + return req; + +fail: + if (ret == LDAP_SERVER_DOWN) { + tevent_req_error(req, ETIMEDOUT); + } else { + tevent_req_error(req, EIO); + } + tevent_req_post(req, ev); + return req; +} + +static void simple_bind_done(struct sdap_op *op, + struct sdap_msg *reply, + int error, void *pvt); + +static void simple_bind_step(struct tevent_req *subreq) +{ + struct tevent_req *req; + struct simple_bind_state *state; + int ret; + int msgid; + int ldap_ret; + + req = tevent_req_callback_data(subreq, struct tevent_req); + state = tevent_req_data(req, struct simple_bind_state); + + ret = ldap_sasl_bind_recv(subreq, &ldap_ret, &msgid); + talloc_zfree(subreq); + if (ret == ENOMEM) { + DEBUG(1, ("out of memory\n")); + } else if (ret == EIO) { + DEBUG(1, ("ldap_bind failed (%d)[%s]\n", ldap_ret, + ldap_err2string(ldap_ret))); + ret = (ldap_ret == LDAP_SERVER_DOWN) ? ETIMEDOUT : EIO; goto fail; } - DEBUG(8, ("ldap simple bind sent, msgid = %d\n", msgid)); - if (!sh->connected) { - ret = sdap_set_connected(sh, ev); + if (!state->sh->connected) { + ret = sdap_set_connected(state->sh, state->ev); if (ret) goto fail; } /* 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 = EIO; goto fail; } - return req; + tevent_req_done(req); fail: - 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_error(req, ret); } static void simple_bind_done(struct sdap_op *op, @@ -501,6 +527,7 @@ struct sasl_bind_state { static int sdap_sasl_interact(LDAP *ld, unsigned flags, void *defaults, void *interact); +static void sasl_bind_interactive_done(struct tevent_req *subreq); static struct tevent_req *sasl_bind_send(TALLOC_CTX *memctx, struct tevent_context *ev, @@ -510,8 +537,8 @@ static struct tevent_req *sasl_bind_send(TALLOC_CTX *memctx, struct berval *sasl_cred) { struct tevent_req *req; + struct tevent_req *subreq; struct sasl_bind_state *state; - int ret = EOK; req = tevent_req_create(memctx, &state, struct sasl_bind_state); if (!req) return NULL; @@ -525,36 +552,45 @@ static struct tevent_req *sasl_bind_send(TALLOC_CTX *memctx, DEBUG(4, ("Executing sasl bind mech: %s, user: %s\n", sasl_mech, sasl_user)); - /* FIXME: Warning, this is a sync call! - * No async variant exist in openldap libraries yet */ - - ret = ldap_sasl_interactive_bind_s(state->sh->ldap, NULL, - sasl_mech, NULL, NULL, - LDAP_SASL_QUIET, - (*sdap_sasl_interact), state); - state->result = ret; - if (ret != LDAP_SUCCESS) { - DEBUG(1, ("ldap_sasl_bind failed (%d)[%s]\n", - ret, ldap_err2string(ret))); - goto fail; - } - - if (!sh->connected) { - ret = sdap_set_connected(sh, ev); - if (ret) goto fail; + subreq = ldap_sasl_interactive_bind_send(state, ev, sh, NULL, sasl_mech, + NULL, NULL, LDAP_SASL_QUIET, + (*sdap_sasl_interact), state); + if (!subreq) { + tevent_req_error(req, ENOMEM); + tevent_req_post(req, ev); + return req; } + tevent_req_set_callback(subreq, sasl_bind_interactive_done, req); - tevent_req_post(req, ev); return req; +} -fail: - if (ret == LDAP_SERVER_DOWN) { - tevent_req_error(req, ETIMEDOUT); - } else { - tevent_req_error(req, EIO); +static void sasl_bind_interactive_done(struct tevent_req *subreq) +{ + struct tevent_req *req; + struct sasl_bind_state *state; + int ret; + + req = tevent_req_callback_data(subreq, struct tevent_req); + state = tevent_req_data(req, struct sasl_bind_state); + + ret = ldap_sasl_interactive_bind_recv(subreq); + talloc_zfree(subreq); + + if (ret != EOK) { + tevent_req_error(req, ret); + return; } - tevent_req_post(req, ev); - return req; + + if (!state->sh->connected) { + ret = sdap_set_connected(state->sh, state->ev); + if (ret != EOK) { + tevent_req_error(req, EIO); + return; + } + } + + tevent_req_done(req); } static int sdap_sasl_interact(LDAP *ld, unsigned flags, diff --git a/src/providers/ldap/sdap_async_private.h b/src/providers/ldap/sdap_async_private.h index bc897fd9..0f8acd84 100644 --- a/src/providers/ldap/sdap_async_private.h +++ b/src/providers/ldap/sdap_async_private.h @@ -30,6 +30,14 @@ struct sdap_handle *sdap_handle_create(TALLOC_CTX *memctx); void sdap_ldap_result(struct tevent_context *ev, struct tevent_fd *fde, uint16_t flags, void *pvt); +void set_fd_retry_cb(struct sdap_handle *sh, + fd_wakeup_callback_t *cb, void *cb_data); +void sdap_async_ldap_result(struct tevent_context *ev, + struct tevent_fd *fde, + uint16_t flags, void *pvt); +void sdap_add_timeout_watcher(struct ldap_cb_data *cb_data, + struct fd_event_item *fd_event_item); + int setup_ldap_connection_callbacks(struct sdap_handle *sh, struct tevent_context *ev); diff --git a/src/providers/ldap/sdap_fd_events.c b/src/providers/ldap/sdap_fd_events.c index 347cf8b8..3f1de002 100644 --- a/src/providers/ldap/sdap_fd_events.c +++ b/src/providers/ldap/sdap_fd_events.c @@ -61,6 +61,15 @@ int remove_ldap_connection_callbacks(struct sdap_handle *sh) } #ifdef HAVE_LDAP_CONNCB +void set_fd_retry_cb(struct sdap_handle *sh, + fd_wakeup_callback_t *fd_cb, void *fd_cb_data) +{ + struct ldap_cb_data *cb_data; + + cb_data = talloc_get_type(sh->sdap_fd_events->conncb->lc_arg, struct ldap_cb_data); + cb_data->wakeup_cb = fd_cb; + cb_data->wakeup_cb_data = fd_cb_data; +} static int remove_connection_callback(TALLOC_CTX *mem_ctx) { @@ -79,6 +88,24 @@ static int remove_connection_callback(TALLOC_CTX *mem_ctx) return EOK; } +static int request_spy_destructor(struct request_spy *spy) +{ + if (spy->ptr) { + spy->ptr->spy = NULL; + talloc_free(spy->ptr); + } + return 0; +} + +static int fd_event_item_destructor(struct fd_event_item *fd_event_item) +{ + if (fd_event_item->spy) { + fd_event_item->spy->ptr = NULL; + } + DLIST_REMOVE(fd_event_item->cb_data->fd_list, fd_event_item); + return 0; +} + static int sdap_ldap_connect_callback_add(LDAP *ld, Sockbuf *sb, LDAPURLDesc *srv, struct sockaddr *addr, @@ -87,6 +114,7 @@ static int sdap_ldap_connect_callback_add(LDAP *ld, Sockbuf *sb, int ret; ber_socket_t ber_fd; struct fd_event_item *fd_event_item; + struct request_spy *spy; struct ldap_cb_data *cb_data = talloc_get_type(ctx->lc_arg, struct ldap_cb_data); @@ -101,7 +129,7 @@ static int sdap_ldap_connect_callback_add(LDAP *ld, Sockbuf *sb, DEBUG(1, ("ber_sockbuf_ctrl failed.\n")); return EINVAL; } - DEBUG(9, ("New LDAP connection to [%s] with fd [%d].\n", + DEBUG(5, ("New LDAP connection to [%s] with fd [%d].\n", ldap_url_desc2str(srv), ber_fd)); fd_event_item = talloc_zero(cb_data, struct fd_event_item); @@ -111,16 +139,40 @@ static int sdap_ldap_connect_callback_add(LDAP *ld, Sockbuf *sb, } fd_event_item->fde = tevent_add_fd(cb_data->ev, fd_event_item, ber_fd, +#ifdef LDAP_OPT_CONNECT_ASYNC + TEVENT_FD_READ | TEVENT_FD_WRITE, + sdap_async_ldap_result, cb_data +#else TEVENT_FD_READ, sdap_ldap_result, - cb_data->sh); + cb_data->sh +#endif + ); + if (fd_event_item->fde == NULL) { DEBUG(1, ("tevent_add_fd failed.\n")); talloc_free(fd_event_item); return ENOMEM; } fd_event_item->fd = ber_fd; + fd_event_item->cb_data = cb_data; + + fd_event_item->fd_wakeup_cb = cb_data->wakeup_cb; + fd_event_item->fd_wakeup_cb_data = cb_data->wakeup_cb_data; + if (fd_event_item->fd_wakeup_cb) { + /* Allocate the spy on the tevent request. */ + spy = talloc(fd_event_item->fd_wakeup_cb_data, struct request_spy); + if (spy == NULL) { + talloc_free(fd_event_item); + return ENOMEM; + } + spy->ptr = fd_event_item; + fd_event_item->spy = spy; + talloc_set_destructor(spy, request_spy_destructor); + } DLIST_ADD(cb_data->fd_list, fd_event_item); + talloc_set_destructor(fd_event_item, fd_event_item_destructor); + sdap_add_timeout_watcher(cb_data, fd_event_item); return LDAP_SUCCESS; } @@ -161,7 +213,15 @@ static void sdap_ldap_connect_callback_del(LDAP *ld, Sockbuf *sb, return; } -#else +#else /* !HAVE_LDAP_CONNCB */ + +void set_fd_retry_cb(struct sdap_handle *sh, + fd_wakeup_callback_t *fd_cb, void *fd_cb_data) +{ + (void)sh; + (void)fd_cb; + (void)fd_cb_data; +} static int sdap_install_ldap_callbacks(struct sdap_handle *sh, struct tevent_context *ev) @@ -199,7 +259,7 @@ static int sdap_install_ldap_callbacks(struct sdap_handle *sh, return EOK; } -#endif +#endif /* HAVE_LDAP_CONNCB */ errno_t setup_ldap_connection_callbacks(struct sdap_handle *sh, @@ -245,6 +305,15 @@ errno_t setup_ldap_connection_callbacks(struct sdap_handle *sh, goto fail; } +#ifdef LDAP_OPT_CONNECT_ASYNC + ret = ldap_set_option(sh->ldap, LDAP_OPT_CONNECT_ASYNC, LDAP_OPT_ON); + if (ret != LDAP_OPT_SUCCESS) { + DEBUG(1, ("Failed to set connection as asynchronous\n")); + ret = EFAULT; + goto fail; + } +#endif + talloc_set_destructor((TALLOC_CTX *) sh->sdap_fd_events->conncb, remove_connection_callback); @@ -253,10 +322,10 @@ errno_t setup_ldap_connection_callbacks(struct sdap_handle *sh, fail: talloc_zfree(sh->sdap_fd_events); return ret; -#else +#else /* !HAVE_LDAP_CONNCB */ DEBUG(9, ("LDAP connection callbacks are not supported.\n")); return EOK; -#endif +#endif /* HAVE_LDAP_CONNCB */ } errno_t sdap_set_connected(struct sdap_handle *sh, struct tevent_context *ev) |