On Tue, Jun 28, 2011 at 09:24:45AM -0400, Stephen Gallagher wrote: > On Tue, 2011-06-28 at 13:42 +0200, Sumit Bose wrote: > > Hi, > > > > currently we have two competing features in the LDAP provider. One the > > one hand we want to avoid further DNS lookups by the OpenLDAP client > > libraries and want to talk to the same LDAP server as long as possible. > > To achieve this we create LDAP URIs with the IP instead of the server > > name and pass them to ldap_initialize(). But this in general leads to > > failures when TLS/SSL certificates are validated, because here the node > > part of the URI is extracted and compared to the common name and the > > subject alternative names in the certificate. If the IP address of the > > server is not among them, which in general is the case, the validation > > fails. > > > > To solve this this series of patches substitutes ldap_initialize() by a > > sequence of socket(), connect() and ldap_init_fd(). Now we can use the > > IP address during connect() and the URI iwth the server name in > > ldap_init_fd(). > > > > The corresponding trac ticket is > > https://fedorahosted.org/sssd/ticket/905 . > > > > bye, > > Sumit > > _______________________________________________ > > sssd-devel mailing list > > sssd-devel@lists.fedorahosted.org > > https://fedorahosted.org/mailman/listinfo/sssd-devel > > > > Nack. > > These patches segfault when used with TLS (I haven't tested LDAPS yet). >
ah, sorry, after I while I realized that I forgot to squeeze in the changes to the IPA provider. New patches attached. bye, Sumit
From 141dc57aa34af73d647f8db7a2921d44b68ef38e Mon Sep 17 00:00:00 2001 From: Sumit Bose <sb...@redhat.com> Date: Mon, 27 Jun 2011 10:03:03 +0200 Subject: [PATCH 1/4] Add sockaddr_storage to sdap_service --- src/providers/ipa/ipa_common.c | 10 ++++++++++ src/providers/ldap/ldap_common.c | 11 +++++++++++ src/providers/ldap/sdap.h | 1 + src/resolv/async_resolv.c | 36 ++++++++++++++++++++++++++++++++++++ src/resolv/async_resolv.h | 4 ++++ 5 files changed, 62 insertions(+), 0 deletions(-) diff --git a/src/providers/ipa/ipa_common.c b/src/providers/ipa/ipa_common.c index f490309..89a8751 100644 --- a/src/providers/ipa/ipa_common.c +++ b/src/providers/ipa/ipa_common.c @@ -593,6 +593,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; @@ -619,6 +620,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")); @@ -646,6 +654,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_common.c b/src/providers/ldap/ldap_common.c index 9796204..997e027 100644 --- a/src/providers/ldap/ldap_common.c +++ b/src/providers/ldap/ldap_common.c @@ -536,6 +536,7 @@ static void sdap_uri_callback(void *private_data, struct fo_server *server) struct resolv_hostent *srvaddr; char *address; const char *safe_address; + struct sockaddr_storage *sockaddr; const char *tmp; char *new_uri; LDAPURLDesc *lud; @@ -570,6 +571,14 @@ static void sdap_uri_callback(void *private_data, struct fo_server *server) return; } + 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; + } + safe_address = sss_ldap_escape_ip_address(tmp_ctx, srvaddr->family, address); @@ -617,6 +626,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 4b662c1..9184090 100644 --- a/src/providers/ldap/sdap.h +++ b/src/providers/ldap/sdap.h @@ -98,6 +98,7 @@ struct sdap_service { char *name; char *uri; char *kinit_service_name; + struct sockaddr_storage *sockaddr; }; struct sdap_ppolicy_data { diff --git a/src/resolv/async_resolv.c b/src/resolv/async_resolv.c index 1f80567..9b9b053 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 907865f..b5547e5 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, -- 1.7.5.4
From fa7f4141d613cf26d05ca1ae547db0b5b4c3ded6 Mon Sep 17 00:00:00 2001 From: Sumit Bose <sb...@redhat.com> Date: Mon, 27 Jun 2011 15:15:34 +0200 Subject: [PATCH 2/4] Add sdap_call_conn_cb() to call add connection callback directly --- src/providers/ldap/sdap_async_private.h | 2 + src/providers/ldap/sdap_fd_events.c | 38 +++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 0 deletions(-) diff --git a/src/providers/ldap/sdap_async_private.h b/src/providers/ldap/sdap_async_private.h index f049fa6..b29b18d 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 05ea0b5..4fb67b8 100644 --- a/src/providers/ldap/sdap_fd_events.c +++ b/src/providers/ldap/sdap_fd_events.c @@ -278,3 +278,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 +} -- 1.7.5.4
From 43d1611066e07bef715b4f6eb5499464aa51c750 Mon Sep 17 00:00:00 2001 From: Sumit Bose <sb...@redhat.com> Date: Mon, 27 Jun 2011 15:44:40 +0200 Subject: [PATCH 3/4] Use name based URI instead of IP address based URIs --- src/providers/ipa/ipa_common.c | 2 +- src/providers/ldap/ldap_common.c | 39 +------------------------------------ 2 files changed, 3 insertions(+), 38 deletions(-) diff --git a/src/providers/ipa/ipa_common.c b/src/providers/ipa/ipa_common.c index 89a8751..9972c34 100644 --- a/src/providers/ipa/ipa_common.c +++ b/src/providers/ipa/ipa_common.c @@ -643,7 +643,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); diff --git a/src/providers/ldap/ldap_common.c b/src/providers/ldap/ldap_common.c index 997e027..2ffa878 100644 --- a/src/providers/ldap/ldap_common.c +++ b/src/providers/ldap/ldap_common.c @@ -534,13 +534,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) { @@ -564,13 +560,6 @@ 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; - } - sockaddr = resolv_get_sockaddr_address(tmp_ctx, srvaddr, fo_get_server_port(server)); if (sockaddr == NULL) { @@ -579,40 +568,16 @@ static void sdap_uri_callback(void *private_data, struct fo_server *server) 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")); - talloc_free(tmp_ctx); - return; - } - if (fo_is_srv_lookup(server)) { if (!tmp) { DEBUG(1, ("Unknown service, using ldap\n")); 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) { -- 1.7.5.4
From 304aa32f0ad52259757f83446659a573c671b8b4 Mon Sep 17 00:00:00 2001 From: Sumit Bose <sb...@redhat.com> Date: Tue, 28 Jun 2011 12:58:26 +0200 Subject: [PATCH 4/4] Use ldap_init_fd() instead of ldap_initialize() if available --- src/external/ldap.m4 | 2 +- src/providers/ldap/ldap_auth.c | 3 +- src/providers/ldap/sdap_async.h | 3 + src/providers/ldap/sdap_async_connection.c | 119 +++++++--- src/util/sss_ldap.c | 336 ++++++++++++++++++++++++++++ src/util/sss_ldap.h | 10 + 6 files changed, 435 insertions(+), 38 deletions(-) diff --git a/src/external/ldap.m4 b/src/external/ldap.m4 index b56eb34..f243509 100644 --- a/src/external/ldap.m4 +++ b/src/external/ldap.m4 @@ -64,7 +64,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/ldap/ldap_auth.c b/src/providers/ldap/ldap_auth.c index 4f60525..5857e53 100644 --- a/src/providers/ldap/ldap_auth.c +++ b/src/providers/ldap/ldap_auth.c @@ -563,7 +563,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/sdap_async.h b/src/providers/ldap/sdap_async.h index aff104c..4115f62 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 40ed585..5d1fb34 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")); @@ -241,14 +288,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) { @@ -260,8 +307,7 @@ fail: tevent_req_error(req, EIO); } } - tevent_req_post(req, ev); - return req; + return; } static void sdap_connect_done(struct sdap_op *op, @@ -1145,6 +1191,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/util/sss_ldap.c b/src/util/sss_ldap.c index 49d989b..9f76870 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" @@ -94,3 +99,334 @@ 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; + + 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) { + tevent_req_done(req); + 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: + if (ret != EOK) { + tevent_req_error(req, ret); + } + + ret = fcntl(fd, F_SETFL, flags); + if (ret != EOK) { + DEBUG(1, ("fcntl F_SETFL failed.\n")); + } + 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; + + /* 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); + if (ret == EOK) { + tevent_req_done(req); + } else { + tevent_req_error(req, ret); + } + + ret = fcntl(state->fd, F_SETFL, state->old_flags); + if (ret != EOK) { + DEBUG(1, ("fcntl F_SETFL failed.\n")); + } + 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; + } + + 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 16c2264..14fff29 100644 --- a/src/util/sss_ldap.h +++ b/src/util/sss_ldap.h @@ -21,8 +21,11 @@ #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> #ifdef LDAP_OPT_DIAGNOSTIC_MESSAGE #define SDAP_DIAGNOSTIC_MESSAGE LDAP_OPT_DIAGNOSTIC_MESSAGE @@ -49,4 +52,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__ */ -- 1.7.5.4
_______________________________________________ sssd-devel mailing list sssd-devel@lists.fedorahosted.org https://fedorahosted.org/mailman/listinfo/sssd-devel