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

Reply via email to