summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSumit Bose <sbose@redhat.com>2011-06-27 10:03:03 +0200
committerStephen Gallagher <sgallagh@redhat.com>2011-07-13 12:35:45 -0400
commitddcbb85ed4402b6e877b22d60610d206b1a19bc8 (patch)
treeac80de4a876a046945e0a17b99d1cde5b5710920
parent90f150f678347f1f73ee4280cd523021e307d861 (diff)
downloadsssd-ddcbb85ed4402b6e877b22d60610d206b1a19bc8.tar.gz
sssd-ddcbb85ed4402b6e877b22d60610d206b1a19bc8.tar.xz
sssd-ddcbb85ed4402b6e877b22d60610d206b1a19bc8.zip
Fix TLS/SSL validation after switch to ldap_init_fd
Add sockaddr_storage to sdap_service Add sdap_call_conn_cb() to call add connection callback directly Use name based URI instead of IP address based URIs Use ldap_init_fd() instead of ldap_initialize() if available Do not access state after tevent_req_done() is called. Call ldap_install_tls() on ldaps connections
-rw-r--r--src/external/ldap.m42
-rw-r--r--src/providers/ipa/ipa_common.c12
-rw-r--r--src/providers/ldap/ldap_auth.c3
-rw-r--r--src/providers/ldap/ldap_common.c42
-rw-r--r--src/providers/ldap/sdap.h1
-rw-r--r--src/providers/ldap/sdap_async.h3
-rw-r--r--src/providers/ldap/sdap_async_connection.c119
-rw-r--r--src/providers/ldap/sdap_async_private.h2
-rw-r--r--src/providers/ldap/sdap_fd_events.c38
-rw-r--r--src/resolv/async_resolv.c36
-rw-r--r--src/resolv/async_resolv.h4
-rw-r--r--src/util/sss_ldap.c357
-rw-r--r--src/util/sss_ldap.h11
13 files changed, 558 insertions, 72 deletions
diff --git a/src/external/ldap.m4 b/src/external/ldap.m4
index 87d95fa8b..c8a46feb1 100644
--- a/src/external/ldap.m4
+++ b/src/external/ldap.m4
@@ -43,7 +43,7 @@ SAVE_CFLAGS=$CFLAGS
SAVE_LIBS=$LIBS
CFLAGS="$CFLAGS $OPENLDAP_CFLAGS"
LIBS="$LIBS $OPENLDAP_LIBS"
-AC_CHECK_FUNCS([ldap_control_create])
+AC_CHECK_FUNCS([ldap_control_create ldap_init_fd])
AC_CHECK_MEMBERS([struct ldap_conncb.lc_arg],
[AC_RUN_IFELSE(
[AC_LANG_PROGRAM(
diff --git a/src/providers/ipa/ipa_common.c b/src/providers/ipa/ipa_common.c
index 647c1c187..c1581305d 100644
--- a/src/providers/ipa/ipa_common.c
+++ b/src/providers/ipa/ipa_common.c
@@ -558,6 +558,7 @@ static void ipa_resolve_callback(void *private_data, struct fo_server *server)
TALLOC_CTX *tmp_ctx = NULL;
struct ipa_service *service;
struct resolv_hostent *srvaddr;
+ struct sockaddr_storage *sockaddr;
char *address;
const char *safe_address;
char *new_uri;
@@ -584,6 +585,13 @@ static void ipa_resolve_callback(void *private_data, struct fo_server *server)
return;
}
+ sockaddr = resolv_get_sockaddr_address(tmp_ctx, srvaddr, LDAP_PORT);
+ if (sockaddr == NULL) {
+ DEBUG(1, ("resolv_get_sockaddr_address failed.\n"));
+ talloc_free(tmp_ctx);
+ return;
+ }
+
address = resolv_get_string_address(tmp_ctx, srvaddr);
if (address == NULL) {
DEBUG(1, ("resolv_get_string_address failed.\n"));
@@ -600,7 +608,7 @@ static void ipa_resolve_callback(void *private_data, struct fo_server *server)
return;
}
- new_uri = talloc_asprintf(service, "ldap://%s", safe_address);
+ new_uri = talloc_asprintf(service, "ldap://%s", fo_get_server_name(server));
if (!new_uri) {
DEBUG(2, ("Failed to copy URI ...\n"));
talloc_free(tmp_ctx);
@@ -611,6 +619,8 @@ static void ipa_resolve_callback(void *private_data, struct fo_server *server)
/* free old one and replace with new one */
talloc_zfree(service->sdap->uri);
service->sdap->uri = new_uri;
+ talloc_zfree(service->sdap->sockaddr);
+ service->sdap->sockaddr = talloc_steal(service, sockaddr);
talloc_zfree(service->krb5_service->address);
service->krb5_service->address = talloc_steal(service, address);
diff --git a/src/providers/ldap/ldap_auth.c b/src/providers/ldap/ldap_auth.c
index a8aa1af9d..6970d7f69 100644
--- a/src/providers/ldap/ldap_auth.c
+++ b/src/providers/ldap/ldap_auth.c
@@ -558,7 +558,8 @@ static void auth_resolve_done(struct tevent_req *subreq)
}
subreq = sdap_connect_send(state, state->ev, state->ctx->opts,
- state->sdap_service->uri, use_tls);
+ state->sdap_service->uri,
+ state->sdap_service->sockaddr, use_tls);
if (!subreq) {
tevent_req_error(req, ENOMEM);
return;
diff --git a/src/providers/ldap/ldap_common.c b/src/providers/ldap/ldap_common.c
index 950c6e5aa..17b85c3f0 100644
--- a/src/providers/ldap/ldap_common.c
+++ b/src/providers/ldap/ldap_common.c
@@ -530,12 +530,9 @@ static void sdap_uri_callback(void *private_data, struct fo_server *server)
TALLOC_CTX *tmp_ctx = NULL;
struct sdap_service *service;
struct resolv_hostent *srvaddr;
- char *address;
- const char *safe_address;
+ struct sockaddr_storage *sockaddr;
const char *tmp;
char *new_uri;
- LDAPURLDesc *lud;
- int ret;
tmp_ctx = talloc_new(NULL);
if (tmp_ctx == NULL) {
@@ -559,19 +556,10 @@ static void sdap_uri_callback(void *private_data, struct fo_server *server)
return;
}
- address = resolv_get_string_address(tmp_ctx, srvaddr);
- if (address == NULL) {
- DEBUG(1, ("resolv_get_string_address failed.\n"));
- talloc_free(tmp_ctx);
- return;
- }
-
- safe_address = sss_ldap_escape_ip_address(tmp_ctx,
- srvaddr->family,
- address);
- talloc_zfree(address);
- if (safe_address == NULL) {
- DEBUG(1, ("sss_ldap_escape_ip_address failed.\n"));
+ sockaddr = resolv_get_sockaddr_address(tmp_ctx, srvaddr,
+ fo_get_server_port(server));
+ if (sockaddr == NULL) {
+ DEBUG(1, ("resolv_get_sockaddr_address failed.\n"));
talloc_free(tmp_ctx);
return;
}
@@ -582,24 +570,10 @@ static void sdap_uri_callback(void *private_data, struct fo_server *server)
tmp = SSS_LDAP_SRV_NAME;
}
new_uri = talloc_asprintf(service, "%s://%s:%d",
- tmp, safe_address,
+ tmp, fo_get_server_name(server),
fo_get_server_port(server));
} else {
- if (tmp && ldap_is_ldap_url(tmp)) {
- ret = ldap_url_parse(tmp, &lud);
- if (ret != LDAP_SUCCESS) {
- DEBUG(0, ("Failed to parse ldap URI (%s)!\n", tmp));
- talloc_free(tmp_ctx);
- return;
- }
- new_uri = talloc_asprintf(service, "%s://%s:%d",
- lud->lud_scheme,
- safe_address,
- fo_get_server_port(server));
- ldap_free_urldesc(lud);
- } else {
- new_uri = talloc_asprintf(service, "ldap://%s", safe_address);
- }
+ new_uri = talloc_strdup(service, tmp);
}
if (!new_uri) {
@@ -613,6 +587,8 @@ static void sdap_uri_callback(void *private_data, struct fo_server *server)
/* free old one and replace with new one */
talloc_zfree(service->uri);
service->uri = new_uri;
+ talloc_zfree(service->sockaddr);
+ service->sockaddr = talloc_steal(service, sockaddr);
talloc_free(tmp_ctx);
}
diff --git a/src/providers/ldap/sdap.h b/src/providers/ldap/sdap.h
index e03e7e624..7fa56331e 100644
--- a/src/providers/ldap/sdap.h
+++ b/src/providers/ldap/sdap.h
@@ -108,6 +108,7 @@ struct sdap_service {
char *name;
char *uri;
char *kinit_service_name;
+ struct sockaddr_storage *sockaddr;
};
struct sdap_ppolicy_data {
diff --git a/src/providers/ldap/sdap_async.h b/src/providers/ldap/sdap_async.h
index f7b7b568d..84a23be9c 100644
--- a/src/providers/ldap/sdap_async.h
+++ b/src/providers/ldap/sdap_async.h
@@ -22,6 +22,8 @@
#ifndef _SDAP_ASYNC_H_
#define _SDAP_ASYNC_H_
+#include <sys/types.h>
+#include <sys/socket.h>
#include <talloc.h>
#include <tevent.h>
#include "providers/dp_backend.h"
@@ -32,6 +34,7 @@ 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);
int sdap_connect_recv(struct tevent_req *req,
TALLOC_CTX *memctx,
diff --git a/src/providers/ldap/sdap_async_connection.c b/src/providers/ldap/sdap_async_connection.c
index aa6725e83..4e6b8099d 100644
--- a/src/providers/ldap/sdap_async_connection.c
+++ b/src/providers/ldap/sdap_async_connection.c
@@ -20,9 +20,12 @@
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"
@@ -61,6 +64,8 @@ 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;
@@ -68,6 +73,7 @@ struct sdap_connect_state {
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);
@@ -76,21 +82,13 @@ 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;
- 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 ret;
req = tevent_req_create(memctx, &state, struct sdap_connect_state);
if (!req) return NULL;
@@ -103,6 +101,8 @@ struct tevent_req *sdap_connect_send(TALLOC_CTX *memctx,
state->ev = ev;
state->opts = opts;
+ state->uri = uri;
+ state->use_start_tls = use_start_tls;
state->sh = sdap_handle_create(state);
if (!state->sh) {
talloc_zfree(req);
@@ -112,13 +112,67 @@ struct tevent_req *sdap_connect_send(TALLOC_CTX *memctx,
state->sh->page_size = dp_opt_get_int(state->opts->basic,
SDAP_PAGE_SIZE);
- /* Initialize LDAP handler */
- lret = ldap_initialize(&state->sh->ldap, uri);
- if (lret != LDAP_SUCCESS) {
- DEBUG(1, ("ldap_initialize failed: %s\n", ldap_err2string(lret)));
+ subreq = sss_ldap_init_send(state, ev, uri, sockaddr,
+ sizeof(struct sockaddr_storage));
+ 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;
+
+ 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);
@@ -135,27 +189,27 @@ struct tevent_req *sdap_connect_send(TALLOC_CTX *memctx,
}
/* Set Network Timeout */
- tv.tv_sec = dp_opt_get_int(opts->basic, SDAP_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(opts->basic, SDAP_NETWORK_TIMEOUT)));
+ dp_opt_get_int(state->opts->basic, SDAP_NETWORK_TIMEOUT)));
goto fail;
}
/* Set Default Timeout */
- tv.tv_sec = dp_opt_get_int(opts->basic, SDAP_OPT_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(opts->basic, SDAP_OPT_TIMEOUT)));
+ dp_opt_get_int(state->opts->basic, SDAP_OPT_TIMEOUT)));
goto fail;
}
/* Set Referral chasing */
- ldap_referrals = dp_opt_get_bool(opts->basic, SDAP_REFERRALS);
+ 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) {
@@ -173,9 +227,9 @@ struct tevent_req *sdap_connect_send(TALLOC_CTX *memctx,
goto fail;
}
- rebind_proc_params->opts = opts;
+ rebind_proc_params->opts = state->opts;
rebind_proc_params->sh = state->sh;
- rebind_proc_params->use_start_tls = use_start_tls;
+ rebind_proc_params->use_start_tls = state->use_start_tls;
lret = ldap_set_rebind_proc(state->sh->ldap, sdap_rebind_proc,
rebind_proc_params);
@@ -186,7 +240,7 @@ struct tevent_req *sdap_connect_send(TALLOC_CTX *memctx,
}
/* Set alias dereferencing */
- ldap_deref = dp_opt_get_string(opts->basic, SDAP_DEREF);
+ 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) {
@@ -202,18 +256,11 @@ struct tevent_req *sdap_connect_send(TALLOC_CTX *memctx,
}
- ret = setup_ldap_connection_callbacks(state->sh, state->ev);
- if (ret != EOK) {
- DEBUG(1, ("setup_ldap_connection_callbacks failed.\n"));
- goto fail;
- }
-
/* 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) {
+ if (!state->use_start_tls) {
tevent_req_done(req);
- tevent_req_post(req, ev);
- return req;
+ return;
}
DEBUG(4, ("Executing START TLS\n"));
@@ -243,14 +290,14 @@ struct tevent_req *sdap_connect_send(TALLOC_CTX *memctx,
if (ret) 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_connect_done, req, 5, &state->op);
if (ret) {
DEBUG(1, ("Failed to set up operation!\n"));
goto fail;
}
- return req;
+ return;
fail:
if (ret) {
@@ -262,8 +309,7 @@ fail:
tevent_req_error(req, EIO);
}
}
- tevent_req_post(req, ev);
- return req;
+ return;
}
static void sdap_connect_done(struct sdap_op *op,
@@ -1149,6 +1195,7 @@ static void sdap_cli_resolve_done(struct tevent_req *subreq)
subreq = sdap_connect_send(state, state->ev, state->opts,
state->service->uri,
+ state->service->sockaddr,
use_tls);
if (!subreq) {
tevent_req_error(req, ENOMEM);
diff --git a/src/providers/ldap/sdap_async_private.h b/src/providers/ldap/sdap_async_private.h
index f049fa6aa..b29b18dfa 100644
--- a/src/providers/ldap/sdap_async_private.h
+++ b/src/providers/ldap/sdap_async_private.h
@@ -40,6 +40,8 @@ int get_fd_from_ldap(LDAP *ldap, int *fd);
errno_t sdap_set_connected(struct sdap_handle *sh, struct tevent_context *ev);
+errno_t sdap_call_conn_cb(const char *uri,int fd, struct sdap_handle *sh);
+
int sdap_op_add(TALLOC_CTX *memctx, struct tevent_context *ev,
struct sdap_handle *sh, int msgid,
sdap_op_callback_t *callback, void *data,
diff --git a/src/providers/ldap/sdap_fd_events.c b/src/providers/ldap/sdap_fd_events.c
index c3f9e118b..43c94ddbb 100644
--- a/src/providers/ldap/sdap_fd_events.c
+++ b/src/providers/ldap/sdap_fd_events.c
@@ -273,3 +273,41 @@ errno_t sdap_set_connected(struct sdap_handle *sh, struct tevent_context *ev)
return ret;
}
+
+errno_t sdap_call_conn_cb(const char *uri,int fd, struct sdap_handle *sh)
+{
+#ifdef HAVE_LDAP_CONNCB
+ int ret;
+ Sockbuf *sb;
+ LDAPURLDesc *lud;
+
+ sb = ber_sockbuf_alloc();
+ if (sb == NULL) {
+ DEBUG(1, ("ber_sockbuf_alloc failed.\n"));
+ return ENOMEM;
+ }
+
+ ret = ber_sockbuf_ctrl(sb, LBER_SB_OPT_SET_FD, &fd);
+ if (ret != 1) {
+ DEBUG(1, ("ber_sockbuf_ctrl failed.\n"));
+ return EFAULT;
+ }
+
+ ret = ldap_url_parse(uri, &lud);
+ if (ret != 0) {
+ ber_sockbuf_free(sb);
+ DEBUG(1, ("ber_sockbuf_ctrl failed.\n"));
+ return EFAULT;
+ }
+
+ ret = sdap_ldap_connect_callback_add(NULL, sb, lud, NULL,
+ sh->sdap_fd_events->conncb);
+
+ ldap_free_urldesc(lud);
+ ber_sockbuf_free(sb);
+ return ret;
+#else
+ DEBUG(9, ("LDAP connection callbacks are not supported.\n"));
+ return EOK;
+#endif
+}
diff --git a/src/resolv/async_resolv.c b/src/resolv/async_resolv.c
index 1f8056722..9b9b0538e 100644
--- a/src/resolv/async_resolv.c
+++ b/src/resolv/async_resolv.c
@@ -1296,6 +1296,42 @@ resolv_get_string_address(TALLOC_CTX *mem_ctx, struct resolv_hostent *hostent)
return address;
}
+struct sockaddr_storage *
+resolv_get_sockaddr_address(TALLOC_CTX *mem_ctx, struct resolv_hostent *hostent,
+ int port)
+{
+ struct sockaddr_storage *sockaddr;
+
+ if (!hostent) return NULL;
+
+ sockaddr = talloc_zero(mem_ctx, struct sockaddr_storage);
+ if (sockaddr == NULL) {
+ DEBUG(1, ("talloc_zero failed.\n"));
+ return NULL;
+ }
+
+ switch(hostent->family) {
+ case AF_INET:
+ sockaddr->ss_family = AF_INET;
+ memcpy(&((struct sockaddr_in *) sockaddr)->sin_addr,
+ hostent->addr_list[0]->ipaddr, sizeof(struct in_addr));
+ ((struct sockaddr_in *) sockaddr)->sin_port = (in_port_t) htons(port);
+
+ break;
+ case AF_INET6:
+ sockaddr->ss_family = AF_INET6;
+ memcpy(&((struct sockaddr_in6 *) sockaddr)->sin6_addr,
+ hostent->addr_list[0]->ipaddr, sizeof(struct in6_addr));
+ ((struct sockaddr_in6 *) sockaddr)->sin6_port = (in_port_t) htons(port);
+ break;
+ default:
+ DEBUG(1, ("Unknown address family %d\n"));
+ return NULL;
+ }
+
+ return sockaddr;
+}
+
/*
* A simple helper function that will take an array of struct ares_srv_reply that
* was allocated by malloc() in c-ares and copies it using talloc. The old one
diff --git a/src/resolv/async_resolv.h b/src/resolv/async_resolv.h
index 907865f7a..b5547e5df 100644
--- a/src/resolv/async_resolv.h
+++ b/src/resolv/async_resolv.h
@@ -114,6 +114,10 @@ int resolv_gethostbyname_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
char *
resolv_get_string_address(TALLOC_CTX *mem_ctx, struct resolv_hostent *hostent);
+struct sockaddr_storage *
+resolv_get_sockaddr_address(TALLOC_CTX *mem_ctx, struct resolv_hostent *hostent,
+ int port);
+
/** Get SRV record **/
struct tevent_req *resolv_getsrv_send(TALLOC_CTX *mem_ctx,
struct tevent_context *ev,
diff --git a/src/util/sss_ldap.c b/src/util/sss_ldap.c
index 94a10386d..785a4482a 100644
--- a/src/util/sss_ldap.c
+++ b/src/util/sss_ldap.c
@@ -19,6 +19,11 @@
*/
#include <stdlib.h>
#include <netdb.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
#include "config.h"
@@ -76,3 +81,355 @@ sss_ldap_escape_ip_address(TALLOC_CTX *mem_ctx, int family, const char *addr)
return family == AF_INET6 ? talloc_asprintf(mem_ctx, "[%s]", addr) :
talloc_strdup(mem_ctx, addr);
}
+
+#ifdef HAVE_LDAP_INIT_FD
+struct sdap_async_sys_connect_state {
+ long old_flags;
+ struct tevent_fd *fde;
+ int fd;
+ socklen_t addr_len;
+ struct sockaddr_storage addr;
+};
+
+static void sdap_async_sys_connect_done(struct tevent_context *ev,
+ struct tevent_fd *fde, uint16_t flags,
+ void *priv);
+
+static struct tevent_req *sdap_async_sys_connect_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ int fd,
+ const struct sockaddr *addr,
+ socklen_t addr_len)
+{
+ struct tevent_req *req;
+ struct sdap_async_sys_connect_state *state;
+ long flags;
+ int ret;
+ int fret;
+
+ flags = fcntl(fd, F_GETFL, 0);
+ if (flags == -1) {
+ DEBUG(1, ("fcntl F_GETFL failed.\n"));
+ return NULL;
+ }
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct sdap_async_sys_connect_state);
+ if (req == NULL) {
+ DEBUG(1, ("tevent_req_create failed.\n"));
+ return NULL;
+ }
+
+ state->old_flags = flags;
+ state->fd = fd;
+ state->addr_len = addr_len;
+ memcpy(&state->addr, addr, addr_len);
+
+ ret = fcntl(fd, F_SETFL, flags | O_NONBLOCK);
+ if (ret != EOK) {
+ DEBUG(1, ("fcntl F_SETFL failed.\n"));
+ goto done;
+ }
+
+ ret = connect(fd, addr, addr_len);
+ if (ret == EOK) {
+ goto done;
+ }
+
+ ret = errno;
+ switch(ret) {
+ case EINPROGRESS:
+ case EINTR:
+ state->fde = tevent_add_fd(ev, state, fd,
+ TEVENT_FD_READ | TEVENT_FD_WRITE,
+ sdap_async_sys_connect_done, req);
+ if (state->fde == NULL) {
+ DEBUG(1, ("tevent_add_fd failed.\n"));
+ ret = ENOMEM;
+ goto done;
+ }
+
+ return req;
+
+ break;
+ default:
+ DEBUG(1, ("connect failed [%d][%s].\n", ret, strerror(ret)));
+ }
+
+done:
+ fret = fcntl(fd, F_SETFL, flags);
+ if (fret != EOK) {
+ DEBUG(1, ("fcntl F_SETFL failed.\n"));
+ }
+
+ if (ret == EOK) {
+ tevent_req_done(req);
+ } else {
+ tevent_req_error(req, ret);
+ }
+
+ tevent_req_post(req, ev);
+ return req;
+}
+
+static void sdap_async_sys_connect_done(struct tevent_context *ev,
+ struct tevent_fd *fde, uint16_t flags,
+ void *priv)
+{
+ struct tevent_req *req = talloc_get_type(priv, struct tevent_req);
+ struct sdap_async_sys_connect_state *state = tevent_req_data(req,
+ struct sdap_async_sys_connect_state);
+ int ret = EOK;
+ int fret;
+
+ /* I found the following comment in samba's lib/async_req/async_sock.c:
+ * Stevens, Network Programming says that if there's a
+ * successful connect, the socket is only writable. Upon an
+ * error, it's both readable and writable.
+ */
+ if ((flags & (TEVENT_FD_READ|TEVENT_FD_WRITE)) ==
+ (TEVENT_FD_READ|TEVENT_FD_WRITE)) {
+
+ ret = connect(state->fd, (struct sockaddr *) &state->addr,
+ state->addr_len);
+ if (ret == EOK) {
+ goto done;
+ }
+
+ ret = errno;
+ if (ret == EINPROGRESS || ret == EINTR) {
+ return;
+ }
+
+ DEBUG(1, ("connect failed [%d][%s].\n", ret, strerror(ret)));
+ }
+
+done:
+ talloc_zfree(fde);
+
+ fret = fcntl(state->fd, F_SETFL, state->old_flags);
+ if (fret != EOK) {
+ DEBUG(1, ("fcntl F_SETFL failed.\n"));
+ }
+
+ if (ret == EOK) {
+ tevent_req_done(req);
+ } else {
+ tevent_req_error(req, ret);
+ }
+
+ return;
+}
+
+static int sdap_async_sys_connect_recv(struct tevent_req *req)
+{
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+
+ return EOK;
+}
+
+static errno_t set_fd_flags_and_opts(int fd)
+{
+ int ret;
+ long flags;
+ int dummy = 1;
+
+ flags = fcntl(fd, F_GETFD, 0);
+ if (flags == -1) {
+ ret = errno;
+ DEBUG(1, ("fcntl F_GETFD failed [%d][%s].\n", ret, strerror(ret)));
+ return ret;
+ }
+
+ flags = fcntl(fd, F_SETFD, flags| FD_CLOEXEC);
+ if (flags == -1) {
+ ret = errno;
+ DEBUG(1, ("fcntl F_SETFD failed [%d][%s].\n", ret, strerror(ret)));
+ return ret;
+ }
+
+ flags = fcntl(fd, F_GETFL, 0);
+ if (flags == -1) {
+ ret = errno;
+ DEBUG(1, ("fcntl F_GETFL failed [%d][%s].\n", ret, strerror(ret)));
+ return ret;
+ }
+
+ flags = fcntl(fd, F_SETFL, flags| O_NONBLOCK);
+ if (flags == -1) {
+ ret = errno;
+ DEBUG(1, ("fcntl F_SETFL failed [%d][%s].\n", ret, strerror(ret)));
+ return ret;
+ }
+
+ /* SO_KEEPALIVE and TCP_NODELAY are set by OpenLDAP client libraries but
+ * failures are ignored.*/
+ ret = setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &dummy, sizeof(dummy));
+ if (ret != 0) {
+ ret = errno;
+ DEBUG(5, ("setsockopt SO_KEEPALIVE failed.[%d][%s].\n", ret,
+ strerror(ret)));
+ }
+
+ ret = setsockopt(fd, SOL_TCP, TCP_NODELAY, &dummy, sizeof(dummy));
+ if (ret != 0) {
+ ret = errno;
+ DEBUG(5, ("setsockopt TCP_NODELAY failed.[%d][%s].\n", ret,
+ strerror(ret)));
+ }
+
+ return EOK;
+}
+
+#define LDAP_PROTO_TCP 1 /* ldap:// */
+#define LDAP_PROTO_UDP 2 /* reserved */
+#define LDAP_PROTO_IPC 3 /* ldapi:// */
+#define LDAP_PROTO_EXT 4 /* user-defined socket/sockbuf */
+
+extern int ldap_init_fd(ber_socket_t fd, int proto, const char *url, LDAP **ld);
+
+static void sss_ldap_init_sys_connect_done(struct tevent_req *subreq);
+#endif
+
+struct sss_ldap_init_state {
+ LDAP *ldap;
+ int sd;
+ const char *uri;
+};
+
+
+struct tevent_req *sss_ldap_init_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ const char *uri,
+ struct sockaddr_storage *addr,
+ int addr_len)
+{
+ int ret = EOK;
+ struct tevent_req *req;
+ struct sss_ldap_init_state *state;
+
+ req = tevent_req_create(mem_ctx, &state, struct sss_ldap_init_state);
+ if (req == NULL) {
+ DEBUG(1, ("tevent_req_create failed.\n"));
+ return NULL;
+ }
+
+ state->ldap = NULL;
+ state->uri = uri;
+
+#ifdef HAVE_LDAP_INIT_FD
+ struct tevent_req *subreq;
+
+ state->sd = socket(addr->ss_family, SOCK_STREAM, 0);
+ if (state->sd == -1) {
+ ret = errno;
+ DEBUG(1, ("socket failed [%d][%s].\n", ret, strerror(ret)));
+ goto fail;
+ }
+
+ ret = set_fd_flags_and_opts(state->sd);
+ if (ret != EOK) {
+ DEBUG(1, ("set_fd_flags_and_opts failed.\n"));
+ goto fail;
+ }
+
+ DEBUG(9, ("Using file descriptor [%d] for LDAP connection.\n", state->sd));
+
+ subreq = sdap_async_sys_connect_send(state, ev, state->sd,
+ (struct sockaddr *) addr, addr_len);
+ if (subreq == NULL) {
+ ret = ENOMEM;
+ DEBUG(1, ("sdap_async_sys_connect_send failed.\n"));
+ goto fail;
+ }
+
+ tevent_req_set_callback(subreq, sss_ldap_init_sys_connect_done, req);
+ return req;
+
+fail:
+ close(state->sd);
+ tevent_req_error(req, ret);
+#else
+ DEBUG(3, ("ldap_init_fd not available, "
+ "will use ldap_initialize with uri [%s].\n", uri));
+ state->sd = -1;
+ ret = ldap_initialize(&state->ldap, uri);
+ if (ret == LDAP_SUCCESS) {
+ tevent_req_done(req);
+ } else {
+ DEBUG(1, ("ldap_initialize failed [%s].\n", ldap_err2string(ret)));
+ if (ret == LDAP_SERVER_DOWN) {
+ tevent_req_error(req, ETIMEDOUT);
+ } else {
+ tevent_req_error(req, EIO);
+ }
+ }
+#endif
+
+ tevent_req_post(req, ev);
+ return req;
+}
+
+#ifdef HAVE_LDAP_INIT_FD
+static void sss_ldap_init_sys_connect_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct sss_ldap_init_state *state = tevent_req_data(req,
+ struct sss_ldap_init_state);
+ int ret;
+ int lret;
+
+ ret = sdap_async_sys_connect_recv(subreq);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ DEBUG(1, ("sdap_async_sys_connect request failed.\n"));
+ close(state->sd);
+ tevent_req_error(req, ret);
+ return;
+ }
+ /* Initialize LDAP handler */
+
+ lret = ldap_init_fd(state->sd, LDAP_PROTO_TCP, state->uri, &state->ldap);
+ if (lret != LDAP_SUCCESS) {
+ DEBUG(1, ("ldap_init_fd failed: %s\n", ldap_err2string(lret)));
+ close(state->sd);
+ if (lret == LDAP_SERVER_DOWN) {
+ tevent_req_error(req, ETIMEDOUT);
+ } else {
+ tevent_req_error(req, EIO);
+ }
+ return;
+ }
+
+ if (ldap_is_ldaps_url(state->uri)) {
+ lret = ldap_install_tls(state->ldap);
+ if (lret != LDAP_SUCCESS) {
+ if (lret == LDAP_LOCAL_ERROR) {
+ DEBUG(5, ("TLS/SSL already in place.\n"));
+ } else {
+ DEBUG(1, ("ldap_install_tls failed: %s\n",
+ ldap_err2string(lret)));
+
+ tevent_req_error(req, EIO);
+ return;
+ }
+ }
+ }
+
+ tevent_req_done(req);
+ return;
+}
+#endif
+
+int sss_ldap_init_recv(struct tevent_req *req, LDAP **ldap, int *sd)
+{
+ struct sss_ldap_init_state *state = tevent_req_data(req,
+ struct sss_ldap_init_state);
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+
+ *ldap = state->ldap;
+ *sd = state->sd;
+
+ return EOK;
+}
diff --git a/src/util/sss_ldap.h b/src/util/sss_ldap.h
index 619bde641..985a903cb 100644
--- a/src/util/sss_ldap.h
+++ b/src/util/sss_ldap.h
@@ -21,8 +21,12 @@
#ifndef __SSS_LDAP_H__
#define __SSS_LDAP_H__
+#include <sys/types.h>
+#include <sys/socket.h>
#include <ldap.h>
#include <talloc.h>
+#include <tevent.h>
+#include "util/util.h"
int sss_ldap_control_create(const char *oid, int iscritical,
struct berval *value, int dupval,
@@ -31,4 +35,11 @@ int sss_ldap_control_create(const char *oid, int iscritical,
inline const char *
sss_ldap_escape_ip_address(TALLOC_CTX *mem_ctx, int family, const char *addr);
+struct tevent_req *sss_ldap_init_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ const char *uri,
+ struct sockaddr_storage *addr,
+ int addr_len);
+
+int sss_ldap_init_recv(struct tevent_req *req, LDAP **ldap, int *sd);
#endif /* __SSS_LDAP_H__ */