/*
SSSD
Async LDAP Helper routines
Copyright (C) Simo Sorce <ssorce@redhat.com> - 2009
Copyright (C) 2010, rhafer@suse.de, Novell Inc.
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 <unistd.h>
#include <fcntl.h>
#include <sasl/sasl.h>
#include "util/util.h"
#include "util/sss_krb5.h"
#include "util/sss_ldap.h"
#include "providers/ldap/sdap_async_private.h"
#include "providers/ldap/ldap_common.h"
errno_t deref_string_to_val(const char *str, int *val)
{
if (strcasecmp(str, "never") == 0) {
*val = LDAP_DEREF_NEVER;
} else if (strcasecmp(str, "searching") == 0) {
*val = LDAP_DEREF_SEARCHING;
} else if (strcasecmp(str, "finding") == 0) {
*val = LDAP_DEREF_FINDING;
} else if (strcasecmp(str, "always") == 0) {
*val = LDAP_DEREF_ALWAYS;
} else {
DEBUG(1, ("Illegal deref option [%s].\n", str));
return EINVAL;
}
return EOK;
}
/* ==Connect-to-LDAP-Server=============================================== */
struct sdap_rebind_proc_params {
struct sdap_options *opts;
struct sdap_handle *sh;
bool use_start_tls;
};
static int sdap_rebind_proc(LDAP *ldap, LDAP_CONST char *url, ber_tag_t request,
ber_int_t msgid, void *params);
struct sdap_connect_state {
struct tevent_context *ev;
struct sdap_options *opts;
struct sdap_handle *sh;
const char *uri;
bool use_start_tls;
struct sdap_op *op;
struct sdap_msg *reply;
int result;
};
static void sdap_sys_connect_done(struct tevent_req *subreq);
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,
struct sockaddr_storage *sockaddr,
bool use_start_tls)
{
struct tevent_req *req;
struct tevent_req *subreq;
struct sdap_connect_state *state;
int ret;
int timeout;
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->use_start_tls = use_start_tls;
state->uri = talloc_asprintf(state, "%s", uri);
if (!state->uri) {
talloc_zfree(req);
return NULL;
}
state->sh = sdap_handle_create(state);
if (!state->sh) {
talloc_zfree(req);
return NULL;
}
state->sh->page_size = dp_opt_get_int(state->opts->basic,
SDAP_PAGE_SIZE);
timeout = dp_opt_get_int(state->opts->basic, SDAP_NETWORK_TIMEOUT);
subreq = sss_ldap_init_send(state, ev, state->uri, sockaddr,
sizeof(struct sockaddr_storage),
timeout);
if (subreq == NULL) {
ret = ENOMEM;
DEBUG(1, ("sss_ldap_init_send failed.\n"));
goto fail;
}
tevent_req_set_callback(subreq, sdap_sys_connect_done, req);
return req;
fail:
tevent_req_error(req, ret);
tevent_req_post(req, ev);
return req;
}
static void sdap_sys_connect_done(struct tevent_req *subreq)
{
struct tevent_req *req = tevent_req_callback_data(subreq,
struct tevent_req);
struct sdap_connect_state *state = tevent_req_data(req,
struct sdap_connect_state);
struct timeval tv;
int ver;
int lret;
int optret;
int ret = EOK;
int msgid;
char *errmsg = NULL;
bool ldap_referrals;
const char *ldap_deref;
int ldap_deref_val;
struct sdap_rebind_proc_params *rebind_proc_params;
int sd;
bool sasl_nocanon;
const char *sasl_mech;
int sasl_minssf;
ret = sss_ldap_init_recv(subreq, &state->sh->ldap, &sd);
talloc_zfree(subreq);
if (ret != EOK) {
DEBUG(1, ("sdap_async_connect_call request failed.\n"));
tevent_req_error(req, ret);
return;
}
ret = setup_ldap_connection_callbacks(state->sh, state->ev);
if (ret != EOK) {
DEBUG(1, ("setup_ldap_connection_callbacks failed.\n"));
goto fail;
}
/* If sss_ldap_init_recv() does not return a valid file descriptor we have
* to assume that the connection callback will be called by internally by
* the OpenLDAP client library. */
if (sd != -1) {
ret = sdap_call_conn_cb(state->uri, sd, state->sh);
if (ret != EOK) {
DEBUG(1, ("sdap_call_conn_cb failed.\n"));
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;
}
/* TODO: maybe this can be remove when we go async, currently we need it
* to handle EINTR during poll(). */
ret = ldap_set_option(state->sh->ldap, LDAP_OPT_RESTART, LDAP_OPT_ON);
if (ret != LDAP_OPT_SUCCESS) {
DEBUG(1, ("Failed to set restart option.\n"));
}
/* Set Network Timeout */
tv.tv_sec = dp_opt_get_int(state->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(state->opts->basic, SDAP_NETWORK_TIMEOUT)));
goto fail;
}
/* Set Default Timeout */
tv.tv_sec = dp_opt_get_int(state->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(state->opts->basic, SDAP_OPT_TIMEOUT)));
goto fail;
}
/* Set Referral chasing */
ldap_referrals = dp_opt_get_bool(state->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;
}
if (ldap_referrals) {
rebind_proc_params = talloc_zero(state->sh,
struct sdap_rebind_proc_params);
if (rebind_proc_params == NULL) {
DEBUG(1, ("talloc_zero failed.\n"));
ret = ENOMEM;
goto fail;
}
rebind_proc_params->opts = state->opts;
rebind_proc_params->sh = state->sh;
rebind_proc_params->use_start_tls = state->use_start_tls;
lret = ldap_set_rebind_proc(state->sh->ldap, sdap_rebind_proc,
rebind_proc_params);
if (lret != LDAP_SUCCESS) {
DEBUG(1, ("ldap_set_rebind_proc failed.\n"));
goto fail;
}
}
/* Set alias dereferencing */
ldap_deref = dp_opt_get_string(state->opts->basic, SDAP_DEREF);
if (ldap_deref != NULL) {
ret = deref_string_to_val(ldap_deref, &ldap_deref_val);
if (ret != EOK) {
DEBUG(1, ("deref_string_to_val failed.\n"));
goto fail;
}
lret = ldap_set_option(state->sh->ldap, LDAP_OPT_DEREF, &ldap_deref_val);
if (lret != LDAP_OPT_SUCCESS) {
DEBUG(1, ("Failed to set deref option to %d\n", ldap_deref_val));
goto fail;
}
}
/* Set host name canonicalization for LDAP SASL bind */
sasl_nocanon = !dp_opt_get_bool(state->opts->basic, SDAP_SASL_CANONICALIZE);
lret = ldap_set_option(state->sh->ldap, LDAP_OPT_X_SASL_NOCANON,
sasl_nocanon ? LDAP_OPT_ON : LDAP_OPT_OFF);
if (lret != LDAP_OPT_SUCCESS) {
/* Do not fail, just warn into both debug logs and syslog */
DEBUG(SSSDBG_MINOR_FAILURE,
("Failed to set LDAP SASL nocanon option to %s. If your system "
"is configured to use SASL, LDAP operations might fail.\n",
sasl_nocanon ? "true" : "false"));
sss_log(SSS_LOG_INFO,
"Failed to set LDAP SASL nocanon option to %s. If your system "
"is configured to use SASL, LDAP operations might fail.\n",
sasl_nocanon ? "true" : "false");
}
sasl_mech = dp_opt_get_string(state->opts->basic, SDAP_SASL_MECH);
if (sasl_mech != NULL) {
sasl_minssf = dp_opt_get_int(state->opts->basic, SDAP_SASL_MINSSF);
if (sasl_minssf >= 0) {
|