From 1c48b5a62f73234ed26bb20f0ab345ab61cda0ab Mon Sep 17 00:00:00 2001 From: Stephen Gallagher Date: Thu, 18 Feb 2010 07:49:04 -0500 Subject: Rename server/ directory to src/ Also update BUILD.txt --- src/providers/ldap/sdap_async_connection.c | 1141 ++++++++++++++++++++++++++++ 1 file changed, 1141 insertions(+) create mode 100644 src/providers/ldap/sdap_async_connection.c (limited to 'src/providers/ldap/sdap_async_connection.c') diff --git a/src/providers/ldap/sdap_async_connection.c b/src/providers/ldap/sdap_async_connection.c new file mode 100644 index 000000000..18e47d3b7 --- /dev/null +++ b/src/providers/ldap/sdap_async_connection.c @@ -0,0 +1,1141 @@ +/* + SSSD + + Async LDAP Helper routines + + Copyright (C) Simo Sorce - 2009 + + 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 . +*/ + +#include +#include "util/util.h" +#include "util/sss_krb5.h" +#include "providers/ldap/sdap_async_private.h" + +#define LDAP_X_SSSD_PASSWORD_EXPIRED 0x555D + +/* ==Connect-to-LDAP-Server=============================================== */ + +struct sdap_connect_state { + struct tevent_context *ev; + struct sdap_options *opts; + struct sdap_handle *sh; + + struct sdap_op *op; + + struct sdap_msg *reply; + int result; +}; + +static void sdap_connect_done(struct sdap_op *op, + struct sdap_msg *reply, + int error, void *pvt); + +struct tevent_req *sdap_connect_send(TALLOC_CTX *memctx, + struct tevent_context *ev, + struct sdap_options *opts, + const char *uri, + bool use_start_tls) +{ + struct tevent_req *req; + struct sdap_connect_state *state; + struct timeval tv; + int ver; + int lret; + int ret = EOK; + int msgid; + bool ldap_referrals; + + req = tevent_req_create(memctx, &state, struct sdap_connect_state); + if (!req) return NULL; + + state->reply = talloc(state, struct sdap_msg); + if (!state->reply) { + talloc_zfree(req); + return NULL; + } + + state->ev = ev; + state->opts = opts; + state->sh = sdap_handle_create(state); + if (!state->sh) { + talloc_zfree(req); + return NULL; + } + /* Initialize LDAP handler */ + lret = ldap_initialize(&state->sh->ldap, uri); + if (lret != LDAP_SUCCESS) { + DEBUG(1, ("ldap_initialize failed: %s\n", ldap_err2string(lret))); + goto fail; + } + + /* Force ldap version to 3 */ + ver = LDAP_VERSION3; + lret = ldap_set_option(state->sh->ldap, LDAP_OPT_PROTOCOL_VERSION, &ver); + if (lret != LDAP_OPT_SUCCESS) { + DEBUG(1, ("Failed to set ldap version to 3\n")); + goto fail; + } + + /* Set Network Timeout */ + tv.tv_sec = dp_opt_get_int(opts->basic, SDAP_NETWORK_TIMEOUT); + tv.tv_usec = 0; + lret = ldap_set_option(state->sh->ldap, LDAP_OPT_NETWORK_TIMEOUT, &tv); + if (lret != LDAP_OPT_SUCCESS) { + DEBUG(1, ("Failed to set network timeout to %d\n", + dp_opt_get_int(opts->basic, SDAP_NETWORK_TIMEOUT))); + goto fail; + } + + /* Set Default Timeout */ + tv.tv_sec = dp_opt_get_int(opts->basic, SDAP_OPT_TIMEOUT); + tv.tv_usec = 0; + lret = ldap_set_option(state->sh->ldap, LDAP_OPT_TIMEOUT, &tv); + if (lret != LDAP_OPT_SUCCESS) { + DEBUG(1, ("Failed to set default timeout to %d\n", + dp_opt_get_int(opts->basic, SDAP_OPT_TIMEOUT))); + goto fail; + } + + /* Set Referral chasing */ + ldap_referrals = dp_opt_get_bool(opts->basic, SDAP_REFERRALS); + lret = ldap_set_option(state->sh->ldap, LDAP_OPT_REFERRALS, + (ldap_referrals ? LDAP_OPT_ON : LDAP_OPT_OFF)); + if (lret != LDAP_OPT_SUCCESS) { + DEBUG(1, ("Failed to set referral chasing to %s\n", + (ldap_referrals ? "LDAP_OPT_ON" : "LDAP_OPT_OFF"))); + goto fail; + } + +#ifdef HAVE_LDAP_CONNCB + struct ldap_cb_data *cb_data; + + /* add connection callback */ + state->sh->conncb = talloc_zero(state->sh, struct ldap_conncb); + if (state->sh->conncb == NULL) { + DEBUG(1, ("talloc_zero failed.\n")); + ret = ENOMEM; + goto fail; + } + + cb_data = talloc_zero(state->sh->conncb, struct ldap_cb_data); + if (cb_data == NULL) { + DEBUG(1, ("talloc_zero failed.\n")); + ret = ENOMEM; + goto fail; + } + cb_data->sh = state->sh; + cb_data->ev = state->ev; + + state->sh->conncb->lc_add = sdap_ldap_connect_callback_add; + state->sh->conncb->lc_del = sdap_ldap_connect_callback_del; + state->sh->conncb->lc_arg = cb_data; + + lret = ldap_set_option(state->sh->ldap, LDAP_OPT_CONNECT_CB, + state->sh->conncb); + if (lret != LDAP_OPT_SUCCESS) { + DEBUG(1, ("Failed to set connection callback\n")); + goto fail; + } +#endif + + /* if we do not use start_tls the connection is not really connected yet + * just fake an async procedure and leave connection to the bind call */ + if (!use_start_tls) { + tevent_req_post(req, ev); + return req; + } + + DEBUG(4, ("Executing START TLS\n")); + + lret = ldap_start_tls(state->sh->ldap, NULL, NULL, &msgid); + if (lret != LDAP_SUCCESS) { + DEBUG(3, ("ldap_start_tls failed: [%s]", ldap_err2string(ret))); + goto fail; + } + + state->sh->connected = true; +#ifndef HAVE_LDAP_CONNCB + ret = sdap_install_ldap_callbacks(state->sh, state->ev); + if (ret) goto fail; +#endif + + /* 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")); + goto fail; + } + + return req; + +fail: + if (ret) { + tevent_req_error(req, ret); + } else { + if (lret == LDAP_SERVER_DOWN) { + tevent_req_error(req, ETIMEDOUT); + } else { + tevent_req_error(req, EIO); + } + } + tevent_req_post(req, ev); + return req; +} + +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); + struct sdap_connect_state *state = tevent_req_data(req, + struct sdap_connect_state); + char *errmsg; + int ret; + + if (error) { + tevent_req_error(req, error); + return; + } + + state->reply = talloc_steal(state, reply); + + ret = ldap_parse_result(state->sh->ldap, state->reply->msg, + &state->result, NULL, &errmsg, NULL, NULL, 0); + if (ret != LDAP_SUCCESS) { + DEBUG(2, ("ldap_parse_result failed (%d)\n", state->op->msgid)); + tevent_req_error(req, EIO); + return; + } + + DEBUG(3, ("START TLS result: %s(%d), %s\n", + ldap_err2string(state->result), state->result, errmsg)); + + if (ldap_tls_inplace(state->sh->ldap)) { + DEBUG(9, ("SSL/TLS handler already in place.\n")); + tevent_req_done(req); + return; + } + +/* FIXME: take care that ldap_install_tls might block */ + ret = ldap_install_tls(state->sh->ldap); + if (ret != LDAP_SUCCESS) { + DEBUG(1, ("ldap_install_tls failed: [%d][%s]\n", ret, + ldap_err2string(ret))); + state->result = ret; + tevent_req_error(req, EIO); + return; + } + + tevent_req_done(req); +} + +int sdap_connect_recv(struct tevent_req *req, + TALLOC_CTX *memctx, + struct sdap_handle **sh) +{ + struct sdap_connect_state *state = tevent_req_data(req, + struct sdap_connect_state); + enum tevent_req_state tstate; + uint64_t err = EIO; + + if (tevent_req_is_error(req, &tstate, &err)) { + /* if tstate shows in progress, it is because + * we did not ask to perform tls, just pretend all is fine */ + if (tstate != TEVENT_REQ_IN_PROGRESS) { + return err; + } + } + + *sh = talloc_steal(memctx, state->sh); + if (!*sh) { + return ENOMEM; + } + return EOK; +} + +/* ==Simple-Bind========================================================== */ + +struct simple_bind_state { + struct tevent_context *ev; + struct sdap_handle *sh; + const char *user_dn; + struct berval *pw; + + struct sdap_op *op; + + struct sdap_msg *reply; + int result; +}; + +static void simple_bind_done(struct sdap_op *op, + struct sdap_msg *reply, + int error, void *pvt); + +static struct tevent_req *simple_bind_send(TALLOC_CTX *memctx, + struct tevent_context *ev, + struct sdap_handle *sh, + const char *user_dn, + struct berval *pw) +{ + struct tevent_req *req; + struct simple_bind_state *state; + int ret = EOK; + int msgid; + int ldap_err; + LDAPControl *request_controls[2]; + + req = tevent_req_create(memctx, &state, struct simple_bind_state); + if (!req) return NULL; + + state->reply = talloc(state, struct sdap_msg); + if (!state->reply) { + talloc_zfree(req); + return NULL; + } + + state->ev = ev; + state->sh = sh; + state->user_dn = user_dn; + state->pw = pw; + + ret = sss_ldap_control_create(LDAP_CONTROL_PASSWORDPOLICYREQUEST, + 0, NULL, 0, &request_controls[0]); + if (ret != LDAP_SUCCESS) { + DEBUG(1, ("sss_ldap_control_create failed.\n")); + goto fail; + } + request_controls[1] = NULL; + + 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); + 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; + } + goto fail; + } + DEBUG(8, ("ldap simple bind sent, msgid = %d\n", msgid)); + + if (!sh->connected) { + sh->connected = true; +#ifndef HAVE_LDAP_CONNCB + ret = sdap_install_ldap_callbacks(sh, ev); + if (ret) goto fail; +#endif + } + + /* FIXME: get timeouts from configuration, for now 5 secs. */ + ret = sdap_op_add(state, ev, sh, msgid, + simple_bind_done, req, 5, &state->op); + if (ret) { + DEBUG(1, ("Failed to set up operation!\n")); + goto fail; + } + + 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) +{ + struct tevent_req *req = talloc_get_type(pvt, struct tevent_req); + struct simple_bind_state *state = tevent_req_data(req, + struct simple_bind_state); + char *errmsg; + int ret; + LDAPControl **response_controls; + int c; + ber_int_t pp_grace; + ber_int_t pp_expire; + LDAPPasswordPolicyError pp_error; + + if (error) { + tevent_req_error(req, error); + return; + } + + state->reply = talloc_steal(state, reply); + + ret = ldap_parse_result(state->sh->ldap, state->reply->msg, + &state->result, NULL, &errmsg, NULL, + &response_controls, 0); + if (ret != LDAP_SUCCESS) { + DEBUG(2, ("ldap_parse_result failed (%d)\n", state->op->msgid)); + ret = EIO; + goto done; + } + + if (response_controls == NULL) { + DEBUG(5, ("Server returned no controls.\n")); + } else { + for (c = 0; response_controls[c] != NULL; c++) { + DEBUG(9, ("Server returned control [%s].\n", + response_controls[c]->ldctl_oid)); + if (strcmp(response_controls[c]->ldctl_oid, + LDAP_CONTROL_PASSWORDPOLICYRESPONSE) == 0) { + ret = ldap_parse_passwordpolicy_control(state->sh->ldap, + response_controls[c], + &pp_expire, &pp_grace, + &pp_error); + if (ret != LDAP_SUCCESS) { + DEBUG(1, ("ldap_parse_passwordpolicy_control failed.\n")); + ret = EIO; + goto done; + } + + DEBUG(7, ("Password Policy Response: expire [%d] grace [%d] " + "error [%s].\n", pp_expire, pp_grace, + ldap_passwordpolicy_err2txt(pp_error))); + + if (state->result == LDAP_SUCCESS && + (pp_error == PP_changeAfterReset || pp_grace > 0)) { + DEBUG(4, ("User must set a new password.\n")); + state->result = LDAP_X_SSSD_PASSWORD_EXPIRED; + } + } + } + } + + DEBUG(3, ("Bind result: %s(%d), %s\n", + ldap_err2string(state->result), state->result, errmsg)); + + ret = LDAP_SUCCESS; +done: + ldap_controls_free(response_controls); + + if (ret == LDAP_SUCCESS) { + tevent_req_done(req); + } else { + tevent_req_error(req, ret); + } +} + +static int simple_bind_recv(struct tevent_req *req, int *ldaperr) +{ + struct simple_bind_state *state = tevent_req_data(req, + struct simple_bind_state); + + *ldaperr = LDAP_OTHER; + TEVENT_REQ_RETURN_ON_ERROR(req); + + *ldaperr = state->result; + return EOK; +} + +/* ==SASL-Bind============================================================ */ + +struct sasl_bind_state { + struct tevent_context *ev; + struct sdap_handle *sh; + + const char *sasl_mech; + const char *sasl_user; + struct berval *sasl_cred; + + int result; +}; + +static int sdap_sasl_interact(LDAP *ld, unsigned flags, + void *defaults, void *interact); + +static struct tevent_req *sasl_bind_send(TALLOC_CTX *memctx, + struct tevent_context *ev, + struct sdap_handle *sh, + const char *sasl_mech, + const char *sasl_user, + struct berval *sasl_cred) +{ + struct tevent_req *req; + struct sasl_bind_state *state; + int ret = EOK; + + req = tevent_req_create(memctx, &state, struct sasl_bind_state); + if (!req) return NULL; + + state->ev = ev; + state->sh = sh; + state->sasl_mech = sasl_mech; + state->sasl_user = sasl_user; + state->sasl_cred = sasl_cred; + + 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) { + sh->connected = true; +#ifndef HAVE_LDAP_CONNCB + ret = sdap_install_ldap_callbacks(sh, ev); + if (ret) goto fail; +#endif + } + + tevent_req_post(req, ev); + 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 int sdap_sasl_interact(LDAP *ld, unsigned flags, + void *defaults, void *interact) +{ + struct sasl_bind_state *state = talloc_get_type(defaults, + struct sasl_bind_state); + sasl_interact_t *in = (sasl_interact_t *)interact; + + if (!ld) return LDAP_PARAM_ERROR; + + while (in->id != SASL_CB_LIST_END) { + + switch (in->id) { + case SASL_CB_GETREALM: + case SASL_CB_AUTHNAME: + case SASL_CB_PASS: + if (in->defresult) { + in->result = in->defresult; + } else { + in->result = ""; + } + in->len = strlen(in->result); + break; + case SASL_CB_USER: + if (state->sasl_user) { + in->result = state->sasl_user; + } else if (in->defresult) { + in->result = in->defresult; + } else { + in->result = ""; + } + in->len = strlen(in->result); + break; + case SASL_CB_NOECHOPROMPT: + case SASL_CB_ECHOPROMPT: + goto fail; + } + + in++; + } + + return LDAP_SUCCESS; + +fail: + return LDAP_UNAVAILABLE; +} + +static int sasl_bind_recv(struct tevent_req *req, int *ldaperr) +{ + struct sasl_bind_state *state = tevent_req_data(req, + struct sasl_bind_state); + enum tevent_req_state tstate; + uint64_t err = EIO; + + if (tevent_req_is_error(req, &tstate, &err)) { + if (tstate != TEVENT_REQ_IN_PROGRESS) { + *ldaperr = LDAP_OTHER; + return err; + } + } + + *ldaperr = state->result; + return EOK; +} + +/* ==Perform-Kinit-given-keytab-and-principal============================= */ + +struct sdap_kinit_state { + int result; +}; + +static void sdap_kinit_done(struct tevent_req *subreq); + +struct tevent_req *sdap_kinit_send(TALLOC_CTX *memctx, + struct tevent_context *ev, + struct sdap_handle *sh, + int timeout, + const char *keytab, + const char *principal, + const char *realm) +{ + struct tevent_req *req; + struct tevent_req *subreq; + struct sdap_kinit_state *state; + int ret; + + DEBUG(6, ("Attempting kinit (%s, %s, %s)\n", keytab, principal, realm)); + + req = tevent_req_create(memctx, &state, struct sdap_kinit_state); + if (!req) return NULL; + + state->result = SDAP_AUTH_FAILED; + + if (keytab) { + ret = setenv("KRB5_KTNAME", keytab, 1); + if (ret == -1) { + DEBUG(2, ("Failed to set KRB5_KTNAME to %s\n", keytab)); + return NULL; + } + } + + subreq = sdap_get_tgt_send(state, ev, realm, principal, keytab, timeout); + if (!subreq) { + talloc_zfree(req); + return NULL; + } + tevent_req_set_callback(subreq, sdap_kinit_done, req); + + return req; +} + +static void sdap_kinit_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct sdap_kinit_state *state = tevent_req_data(req, + struct sdap_kinit_state); + + int ret; + int result; + char *ccname = NULL; + + ret = sdap_get_tgt_recv(subreq, state, &result, &ccname); + talloc_zfree(subreq); + if (ret != EOK) { + state->result = SDAP_AUTH_FAILED; + DEBUG(1, ("child failed (%d [%s])\n", ret, strerror(ret))); + tevent_req_error(req, ret); + return; + } + + if (result == EOK) { + ret = setenv("KRB5CCNAME", ccname, 1); + if (ret == -1) { + DEBUG(2, ("Unable to set env. variable KRB5CCNAME!\n")); + state->result = SDAP_AUTH_FAILED; + tevent_req_error(req, EFAULT); + } + + state->result = SDAP_AUTH_SUCCESS; + tevent_req_done(req); + return; + } + + DEBUG(4, ("Could not get TGT: %d [%s]\n", result, strerror(result))); + state->result = SDAP_AUTH_FAILED; + tevent_req_error(req, EIO); +} + +int sdap_kinit_recv(struct tevent_req *req, enum sdap_result *result) +{ + struct sdap_kinit_state *state = tevent_req_data(req, + struct sdap_kinit_state); + enum tevent_req_state tstate; + uint64_t err = EIO; + + if (tevent_req_is_error(req, &tstate, &err)) { + if (tstate != TEVENT_REQ_IN_PROGRESS) { + *result = SDAP_ERROR; + return err; + } + } + + *result = state->result; + return EOK; +} + + +/* ==Authenticaticate-User-by-DN========================================== */ + +struct sdap_auth_state { + const char *user_dn; + struct berval pw; + + int result; + bool is_sasl; +}; + +static void sdap_auth_done(struct tevent_req *subreq); + +/* TODO: handle sasl_cred */ +struct tevent_req *sdap_auth_send(TALLOC_CTX *memctx, + struct tevent_context *ev, + struct sdap_handle *sh, + const char *sasl_mech, + const char *sasl_user, + const char *user_dn, + const char *authtok_type, + struct dp_opt_blob authtok) +{ + struct tevent_req *req, *subreq; + struct sdap_auth_state *state; + + if (authtok_type != NULL && strcasecmp(authtok_type,"password") != 0) { + DEBUG(1,("Authentication token type [%s] is not supported")); + return NULL; + } + + req = tevent_req_create(memctx, &state, struct sdap_auth_state); + if (!req) return NULL; + + state->user_dn = user_dn; + state->pw.bv_val = (char *)authtok.data; + state->pw.bv_len = authtok.length; + + if (sasl_mech) { + state->is_sasl = true; + subreq = sasl_bind_send(state, ev, sh, sasl_mech, sasl_user, NULL); + if (!subreq) { + tevent_req_error(req, ENOMEM); + return tevent_req_post(req, ev); + } + } else { + state->is_sasl = false; + subreq = simple_bind_send(state, ev, sh, user_dn, &state->pw); + if (!subreq) { + tevent_req_error(req, ENOMEM); + return tevent_req_post(req, ev); + } + } + + tevent_req_set_callback(subreq, sdap_auth_done, req); + return req; +} + +static void sdap_auth_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct sdap_auth_state *state = tevent_req_data(req, + struct sdap_auth_state); + int ret; + + if (state->is_sasl) { + ret = sasl_bind_recv(subreq, &state->result); + } else { + ret = simple_bind_recv(subreq, &state->result); + } + if (ret != EOK) { + tevent_req_error(req, ret); + return; + } + + tevent_req_done(req); +} + +int sdap_auth_recv(struct tevent_req *req, enum sdap_result *result) +{ + struct sdap_auth_state *state = tevent_req_data(req, + struct sdap_auth_state); + + *result = SDAP_ERROR; + TEVENT_REQ_RETURN_ON_ERROR(req); + + switch (state->result) { + case LDAP_SUCCESS: + *result = SDAP_AUTH_SUCCESS; + break; + case LDAP_INVALID_CREDENTIALS: + *result = SDAP_AUTH_FAILED; + break; + case LDAP_X_SSSD_PASSWORD_EXPIRED: + *result = SDAP_AUTH_PW_EXPIRED; + break; + default: + break; + } + + return EOK; +} + +/* ==Client connect============================================ */ + +struct sdap_cli_connect_state { + struct tevent_context *ev; + struct sdap_options *opts; + struct sdap_service *service; + + bool use_rootdse; + struct sysdb_attrs *rootdse; + + struct sdap_handle *sh; + + struct fo_server *srv; +}; + +static void sdap_cli_resolve_done(struct tevent_req *subreq); +static void sdap_cli_connect_done(struct tevent_req *subreq); +static void sdap_cli_rootdse_step(struct tevent_req *req); +static void sdap_cli_rootdse_done(struct tevent_req *subreq); +static void sdap_cli_kinit_step(struct tevent_req *req); +static void sdap_cli_kinit_done(struct tevent_req *subreq); +static void sdap_cli_auth_step(struct tevent_req *req); +static void sdap_cli_auth_done(struct tevent_req *subreq); + +struct tevent_req *sdap_cli_connect_send(TALLOC_CTX *memctx, + struct tevent_context *ev, + struct sdap_options *opts, + struct be_ctx *be, + struct sdap_service *service, + struct sysdb_attrs **rootdse) +{ + struct tevent_req *req, *subreq; + struct sdap_cli_connect_state *state; + + req = tevent_req_create(memctx, &state, struct sdap_cli_connect_state); + if (!req) return NULL; + + state->ev = ev; + state->opts = opts; + state->service = service; + state->srv = NULL; + + if (rootdse) { + state->use_rootdse = true; + state->rootdse = *rootdse; + } else { + state->use_rootdse = false; + state->rootdse = NULL; + } + + /* NOTE: this call may cause service->uri to be refreshed + * with a new valid server. Do not use service->uri before */ + subreq = be_resolve_server_send(state, ev, be, service->name); + if (!subreq) { + talloc_zfree(req); + return NULL; + } + tevent_req_set_callback(subreq, sdap_cli_resolve_done, req); + + return req; +} + +static void sdap_cli_resolve_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct sdap_cli_connect_state *state = tevent_req_data(req, + struct sdap_cli_connect_state); + int ret; + + ret = be_resolve_server_recv(subreq, &state->srv); + talloc_zfree(subreq); + if (ret) { + /* all servers have been tried and none + * was found good, go offline */ + tevent_req_error(req, EIO); + return; + } + + subreq = sdap_connect_send(state, state->ev, state->opts, + state->service->uri, + dp_opt_get_bool(state->opts->basic, + SDAP_ID_TLS)); + if (!subreq) { + tevent_req_error(req, ENOMEM); + return; + } + tevent_req_set_callback(subreq, sdap_cli_connect_done, req); +} + +static void sdap_cli_connect_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct sdap_cli_connect_state *state = tevent_req_data(req, + struct sdap_cli_connect_state); + const char *sasl_mech; + int ret; + + ret = sdap_connect_recv(subreq, state, &state->sh); + talloc_zfree(subreq); + if (ret) { + tevent_req_error(req, ret); + return; + } + + if (state->use_rootdse && !state->rootdse) { + /* fetch the rootDSE this time */ + sdap_cli_rootdse_step(req); + return; + } + + sasl_mech = dp_opt_get_string(state->opts->basic, SDAP_SASL_MECH); + + if (sasl_mech && state->use_rootdse) { + /* check if server claims to support GSSAPI */ + if (!sdap_rootdse_sasl_mech_is_supported(state->rootdse, + sasl_mech)) { + tevent_req_error(req, ENOTSUP); + return; + } + } + + if (sasl_mech && (strcasecmp(sasl_mech, "GSSAPI") == 0)) { + if (dp_opt_get_bool(state->opts->basic, SDAP_KRB5_KINIT)) { + sdap_cli_kinit_step(req); + return; + } + } + + sdap_cli_auth_step(req); +} + +static void sdap_cli_rootdse_step(struct tevent_req *req) +{ + struct sdap_cli_connect_state *state = tevent_req_data(req, + struct sdap_cli_connect_state); + struct tevent_req *subreq; + + subreq = sdap_get_rootdse_send(state, state->ev, state->opts, state->sh); + if (!subreq) { + tevent_req_error(req, ENOMEM); + return; + } + tevent_req_set_callback(subreq, sdap_cli_rootdse_done, req); + + if (!state->sh->connected) { + /* this rootdse search is performed before we actually do a bind, + * so we need to set up the callbacks or we will never get notified + * of a reply */ + state->sh->connected = true; +#ifndef HAVE_LDAP_CONNCB + int ret; + + ret = sdap_install_ldap_callbacks(state->sh, state->ev); + if (ret) { + tevent_req_error(req, ret); + } +#endif + } +} + +static void sdap_cli_rootdse_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct sdap_cli_connect_state *state = tevent_req_data(req, + struct sdap_cli_connect_state); + const char *sasl_mech; + int ret; + + ret = sdap_get_rootdse_recv(subreq, state, &state->rootdse); + talloc_zfree(subreq); + if (ret) { + tevent_req_error(req, ret); + return; + } + + sasl_mech = dp_opt_get_string(state->opts->basic, SDAP_SASL_MECH); + + if (sasl_mech && state->use_rootdse) { + /* check if server claims to support GSSAPI */ + if (!sdap_rootdse_sasl_mech_is_supported(state->rootdse, + sasl_mech)) { + tevent_req_error(req, ENOTSUP); + return; + } + } + + if (sasl_mech && (strcasecmp(sasl_mech, "GSSAPI") == 0)) { + if (dp_opt_get_bool(state->opts->basic, SDAP_KRB5_KINIT)) { + sdap_cli_kinit_step(req); + return; + } + } + + sdap_cli_auth_step(req); +} + +static void sdap_cli_kinit_step(struct tevent_req *req) +{ + struct sdap_cli_connect_state *state = tevent_req_data(req, + struct sdap_cli_connect_state); + struct tevent_req *subreq; + + subreq = sdap_kinit_send(state, state->ev, + state->sh, + dp_opt_get_int(state->opts->basic, + SDAP_OPT_TIMEOUT), + dp_opt_get_string(state->opts->basic, + SDAP_KRB5_KEYTAB), + dp_opt_get_string(state->opts->basic, + SDAP_SASL_AUTHID), + dp_opt_get_string(state->opts->basic, + SDAP_KRB5_REALM)); + if (!subreq) { + tevent_req_error(req, ENOMEM); + return; + } + tevent_req_set_callback(subreq, sdap_cli_kinit_done, req); +} + +static void sdap_cli_kinit_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + enum sdap_result result; + int ret; + + ret = sdap_kinit_recv(subreq, &result); + talloc_zfree(subreq); + if (ret) { + tevent_req_error(req, ret); + return; + } + if (result != SDAP_AUTH_SUCCESS) { + tevent_req_error(req, EACCES); + return; + } + + sdap_cli_auth_step(req); +} + +static void sdap_cli_auth_step(struct tevent_req *req) +{ + struct sdap_cli_connect_state *state = tevent_req_data(req, + struct sdap_cli_connect_state); + struct tevent_req *subreq; + + subreq = sdap_auth_send(state, + state->ev, + state->sh, + dp_opt_get_string(state->opts->basic, + SDAP_SASL_MECH), + dp_opt_get_string(state->opts->basic, + SDAP_SASL_AUTHID), + dp_opt_get_string(state->opts->basic, + SDAP_DEFAULT_BIND_DN), + dp_opt_get_string(state->opts->basic, + SDAP_DEFAULT_AUTHTOK_TYPE), + dp_opt_get_blob(state->opts->basic, + SDAP_DEFAULT_AUTHTOK)); + if (!subreq) { + tevent_req_error(req, ENOMEM); + return; + } + tevent_req_set_callback(subreq, sdap_cli_auth_done, req); +} + +static void sdap_cli_auth_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + enum sdap_result result; + int ret; + + ret = sdap_auth_recv(subreq, &result); + talloc_zfree(subreq); + if (ret) { + tevent_req_error(req, ret); + return; + } + if (result != SDAP_AUTH_SUCCESS) { + tevent_req_error(req, EACCES); + return; + } + + tevent_req_done(req); +} + +int sdap_cli_connect_recv(struct tevent_req *req, + TALLOC_CTX *memctx, + struct sdap_handle **gsh, + struct sysdb_attrs **rootdse) +{ + struct sdap_cli_connect_state *state = tevent_req_data(req, + struct sdap_cli_connect_state); + enum tevent_req_state tstate; + uint64_t err; + + if (tevent_req_is_error(req, &tstate, &err)) { + /* mark the server as bad if connection failed */ + if (state->srv) { + fo_set_port_status(state->srv, PORT_NOT_WORKING); + } + + if (tstate == TEVENT_REQ_USER_ERROR) { + return err; + } + return EIO; + } else if (state->srv) { + fo_set_port_status(state->srv, PORT_WORKING); + } + + if (gsh) { + *gsh = talloc_steal(memctx, state->sh); + if (!*gsh) { + return ENOMEM; + } + } else { + talloc_zfree(state->sh); + } + + if (rootdse) { + if (state->use_rootdse) { + *rootdse = talloc_steal(memctx, state->rootdse); + if (!*rootdse) { + return ENOMEM; + } + } else { + *rootdse = NULL; + } + } else { + talloc_zfree(rootdse); + } + + return EOK; +} + -- cgit