]> git.sur5r.net Git - openldap/commitdiff
ITS#7428 Use non-blocking IO during SSL Handshake
authorRalf Haferkamp <ralf@openldap.org>
Wed, 21 Nov 2012 12:41:40 +0000 (13:41 +0100)
committerRalf Haferkamp <ralf@openldap.org>
Wed, 21 Nov 2012 13:25:18 +0000 (14:25 +0100)
If a timeout is set, perform the SSL Handshake using non-blocking IO.  This way
we can timeout if SSL Handshake gets stuck for whatever reason.

This code is currently hidden behind #ifdefs (LDAP_USE_NON_BLOCKING_TLS) and
disabled by default as there seem to be some problems using NON-blocking
I/O during the TLS Handshake when linking against NSS (either a bug in NSS
itself of in tls_m.c, see discussion on -devel)

This patch adds an additional parameter to ldap_int_poll() in order to indicate
if we're waiting in order to perform a read or write operation.

libraries/libldap/ldap-int.h
libraries/libldap/open.c
libraries/libldap/os-ip.c
libraries/libldap/request.c
libraries/libldap/tls2.c

index 4c4e47b8bb6a22d59f48fcc00d39ef7ab3a33b8e..c01163f7f3e12be76755c980773b993de50171d0 100644 (file)
@@ -612,7 +612,7 @@ LDAP_F (int) ldap_int_timeval_dup( struct timeval **dest,
 LDAP_F (int) ldap_connect_to_host( LDAP *ld, Sockbuf *sb,
        int proto, LDAPURLDesc *srv, int async );
 LDAP_F (int) ldap_int_poll( LDAP *ld, ber_socket_t s,
-       struct timeval *tvp );
+       struct timeval *tvp, int wr );
 
 #if defined(HAVE_TLS) || defined(HAVE_CYRUS_SASL)
 LDAP_V (char *) ldap_int_hostname;
index a92095334e91fab25d884dda576352699d528f9d..ec5011744a567e2537a89217eb12e6ccb6e6dada 100644 (file)
@@ -540,7 +540,7 @@ ldap_int_check_async_open( LDAP *ld, ber_socket_t sd )
        struct timeval tv = { 0 };
        int rc;
 
-       rc = ldap_int_poll( ld, sd, &tv );
+       rc = ldap_int_poll( ld, sd, &tv, 1 );
        switch ( rc ) {
        case 0:
                /* now ready to start tls */
index 286425691650f865f630c5fd0b9e0c63571e7051..25439cb5463b6252d0e04f067f06aee99dc95014 100644 (file)
@@ -276,7 +276,8 @@ int
 ldap_int_poll(
        LDAP *ld,
        ber_socket_t s,
-       struct timeval *tvp )
+       struct timeval *tvp,
+       int wr )
 {
        int             rc;
                
@@ -288,9 +289,10 @@ ldap_int_poll(
        {
                struct pollfd fd;
                int timeout = INFTIM;
+               short event = wr ? POLL_WRITE : POLL_READ;
 
                fd.fd = s;
-               fd.events = POLL_WRITE;
+               fd.events = event;
 
                if ( tvp != NULL ) {
                        timeout = TV2MILLISEC( tvp );
@@ -310,7 +312,7 @@ ldap_int_poll(
                        return -2;
                }
 
-               if ( fd.revents & POLL_WRITE ) {
+               if ( fd.revents & event ) {
                        if ( ldap_pvt_is_socket_ready( ld, s ) == -1 ) {
                                return -1;
                        }
@@ -452,7 +454,7 @@ ldap_pvt_connect(LDAP *ld, ber_socket_t s,
                return ( -2 );
        }
 
-       rc = ldap_int_poll( ld, s, opt_tv );
+       rc = ldap_int_poll( ld, s, opt_tv, 1 );
 
        osip_debug(ld, "ldap_pvt_connect: %d\n", rc, 0, 0);
 
index 071391d9abc1e3acb7f8fcd1a8e8e85bdaecad6c..52c21a0123bdbbbfe711e135aea52ed72b46fc05 100644 (file)
@@ -261,7 +261,7 @@ ldap_send_server_request(
                ber_sockbuf_ctrl( lc->lconn_sb, LBER_SB_OPT_GET_FD, &sd );
 
                /* poll ... */
-               switch ( ldap_int_poll( ld, sd, &tv ) ) {
+               switch ( ldap_int_poll( ld, sd, &tv, 1 ) ) {
                case 0:
                        /* go on! */
                        lc->lconn_status = LDAP_CONNST_CONNECTED;
index f0b5bef92990daddcd3279e02f73dbfa86af1f7e..708dc7b6a00b615227438e2c0c23a90d4a40aa1f 100644 (file)
@@ -43,6 +43,10 @@ static tls_impl *tls_imp = &ldap_int_tls_impl;
 
 #endif /* HAVE_TLS */
 
+#ifdef LDAP_DEVEL
+#define LDAP_USE_NON_BLOCKING_TLS
+#endif /* LDAP_DEVEL */
+
 /* RFC2459 minimum required set of supported attribute types
  * in a certificate DN
  */
@@ -810,6 +814,11 @@ ldap_int_tls_start ( LDAP *ld, LDAPConn *conn, LDAPURLDesc *srv )
        Sockbuf *sb;
        char *host;
        void *ssl;
+       int ret;
+#ifdef LDAP_USE_NON_BLOCKING_TLS
+       struct timeval start_time_tv, tv, tv0;
+       ber_socket_t    sd = AC_SOCKET_ERROR;
+#endif /* LDAP_USE_NON_BLOCKING_TLS */
 
        if ( !conn )
                return LDAP_PARAM_ERROR;
@@ -828,11 +837,101 @@ ldap_int_tls_start ( LDAP *ld, LDAPConn *conn, LDAPURLDesc *srv )
 
        (void) tls_init( tls_imp );
 
+#ifdef LDAP_USE_NON_BLOCKING_TLS
        /*
-        * Fortunately, the lib uses blocking io...
+        * Use non-blocking io during SSL Handshake when a timeout is configured
         */
-       if ( ldap_int_tls_connect( ld, conn ) < 0 ) {
-               ld->ld_errno = LDAP_CONNECT_ERROR;
+       if ( ld->ld_options.ldo_tm_net.tv_sec >= 0 ) {
+               ber_sockbuf_ctrl( ld->ld_sb, LBER_SB_OPT_SET_NONBLOCK, sb );
+               ber_sockbuf_ctrl( sb, LBER_SB_OPT_GET_FD, &sd );
+               tv = ld->ld_options.ldo_tm_net;
+               tv0 = tv;
+#ifdef HAVE_GETTIMEOFDAY
+               gettimeofday( &start_time_tv, NULL );
+#else /* ! HAVE_GETTIMEOFDAY */
+               time( &start_time_tv.tv_sec );
+               start_time_tv.tv_usec = 0;
+#endif /* ! HAVE_GETTIMEOFDAY */
+       }
+
+#endif /* LDAP_USE_NON_BLOCKING_TLS */
+
+       ld->ld_errno = LDAP_SUCCESS;
+       ret = ldap_int_tls_connect( ld, conn );
+
+#ifdef LDAP_USE_NON_BLOCKING_TLS
+       while ( ret > 0 ) { /* this should only happen for non-blocking io */
+               int wr=0;
+
+               if ( sb->sb_trans_needs_read ) {
+                       wr=0;
+               } else if ( sb->sb_trans_needs_write ) {
+                       wr=1;
+               }
+               Debug( LDAP_DEBUG_TRACE, "ldap_int_tls_start: ldap_int_tls_connect needs %s\n",
+                               wr ? "write": "read", 0, 0);
+
+               ret = ldap_int_poll( ld, sd, &tv, wr);
+               if ( ret < 0 ) {
+                       ld->ld_errno = LDAP_TIMEOUT;
+                       break;
+               } else {
+                       /* ldap_int_poll called ldap_pvt_ndelay_off */
+                       ber_sockbuf_ctrl( ld->ld_sb, LBER_SB_OPT_SET_NONBLOCK, sb );
+                       ret = ldap_int_tls_connect( ld, conn );
+                       if ( ret > 0 ) { /* need to call tls_connect once more */
+                               struct timeval curr_time_tv, delta_tv;
+
+                               /* This is mostly copied from result.c:wait4msg(), should
+                                * probably be moved into a separate function */
+#ifdef HAVE_GETTIMEOFDAY
+                               gettimeofday( &curr_time_tv, NULL );
+#else /* ! HAVE_GETTIMEOFDAY */
+                               time( &curr_time_tv.tv_sec );
+                               curr_time_tv.tv_usec = 0;
+#endif /* ! HAVE_GETTIMEOFDAY */
+
+                               /* delta = curr - start */
+                               delta_tv.tv_sec = curr_time_tv.tv_sec - start_time_tv.tv_sec;
+                               delta_tv.tv_usec = curr_time_tv.tv_usec - start_time_tv.tv_usec;
+                               if ( delta_tv.tv_usec < 0 ) {
+                                       delta_tv.tv_sec--;
+                                       delta_tv.tv_usec += 1000000;
+                               }
+
+                               /* tv0 < delta ? */
+                               if ( ( tv0.tv_sec < delta_tv.tv_sec ) ||
+                                        ( ( tv0.tv_sec == delta_tv.tv_sec ) &&
+                                          ( tv0.tv_usec < delta_tv.tv_usec ) ) )
+                               {
+                                       ret = -1;
+                                       ld->ld_errno = LDAP_TIMEOUT;
+                                       break;
+                               } else {
+                                       /* timeout -= delta_time */
+                                       tv0.tv_sec -= delta_tv.tv_sec;
+                                       tv0.tv_usec -= delta_tv.tv_usec;
+                                       if ( tv0.tv_usec < 0 ) {
+                                               tv0.tv_sec--;
+                                               tv0.tv_usec += 1000000;
+                                       }
+                                       start_time_tv.tv_sec = curr_time_tv.tv_sec;
+                                       start_time_tv.tv_usec = curr_time_tv.tv_usec;
+                               }
+                               tv = tv0;
+                               Debug( LDAP_DEBUG_TRACE, "ldap_int_tls_start: ld %p %ld s %ld us to go\n",
+                                       (void *)ld, (long) tv.tv_sec, (long) tv.tv_usec );
+                       }
+               }
+       }
+       if ( ld->ld_options.ldo_tm_net.tv_sec >= 0 ) {
+               ber_sockbuf_ctrl( ld->ld_sb, LBER_SB_OPT_SET_NONBLOCK, NULL );
+       }
+#endif /* LDAP_USE_NON_BLOCKING_TLS */
+
+       if ( ret < 0 ) {
+               if ( ld->ld_errno == LDAP_SUCCESS )
+                       ld->ld_errno = LDAP_CONNECT_ERROR;
                return (ld->ld_errno);
        }