summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Makefile.am3
-rw-r--r--src/providers/ldap/ldap_req_wrap.c689
-rw-r--r--src/providers/ldap/ldap_req_wrap.h103
-rw-r--r--src/providers/ldap/sdap.h16
-rw-r--r--src/providers/ldap/sdap_async.c271
-rw-r--r--src/providers/ldap/sdap_async_connection.c218
-rw-r--r--src/providers/ldap/sdap_async_private.h8
-rw-r--r--src/providers/ldap/sdap_fd_events.c81
8 files changed, 1222 insertions, 167 deletions
diff --git a/Makefile.am b/Makefile.am
index 7482e811a..c485bc0dc 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 000000000..bd86947fc
--- /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 000000000..85ed25801
--- /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 91d91b859..61e7fe254 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 cd61a2214..104a72e01 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 806bd5fe2..d2ca356f3 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 bc897fd96..0f8acd841 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 347cf8b81..3f1de002d 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)