From: Ralf Haferkamp Date: Wed, 21 Nov 2012 12:41:40 +0000 (+0100) Subject: ITS#7428 Use non-blocking IO during SSL Handshake X-Git-Url: https://git.sur5r.net/?a=commitdiff_plain;h=c728ebf586af778dc2927513097dbfc433b267df;p=openldap ITS#7428 Use non-blocking IO during SSL Handshake 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. --- diff --git a/libraries/libldap/ldap-int.h b/libraries/libldap/ldap-int.h index 4c4e47b8bb..c01163f7f3 100644 --- a/libraries/libldap/ldap-int.h +++ b/libraries/libldap/ldap-int.h @@ -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; diff --git a/libraries/libldap/open.c b/libraries/libldap/open.c index a92095334e..ec5011744a 100644 --- a/libraries/libldap/open.c +++ b/libraries/libldap/open.c @@ -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 */ diff --git a/libraries/libldap/os-ip.c b/libraries/libldap/os-ip.c index 2864256916..25439cb546 100644 --- a/libraries/libldap/os-ip.c +++ b/libraries/libldap/os-ip.c @@ -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); diff --git a/libraries/libldap/request.c b/libraries/libldap/request.c index 071391d9ab..52c21a0123 100644 --- a/libraries/libldap/request.c +++ b/libraries/libldap/request.c @@ -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; diff --git a/libraries/libldap/tls2.c b/libraries/libldap/tls2.c index f0b5bef929..708dc7b6a0 100644 --- a/libraries/libldap/tls2.c +++ b/libraries/libldap/tls2.c @@ -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); }