int ldap_int_check_async_open( LDAP *ld, ber_socket_t sd ) { struct timeval tv = { 0 }; int rc; rc = ldap_int_poll( ld, sd, &tv, 1 ); switch ( rc ) { case 0: /* now ready to start tls */ ld->ld_defconn->lconn_status = LDAP_CONNST_CONNECTED; break; default: ld->ld_errno = LDAP_CONNECT_ERROR; return -1; case -2: /* connect not completed yet */ ld->ld_errno = LDAP_X_CONNECTING; return rc; } #ifdef HAVE_TLS if ( ld->ld_options.ldo_tls_mode == LDAP_OPT_X_TLS_HARD || !strcmp( ld->ld_defconn->lconn_server->lud_scheme, "ldaps" )) { ++ld->ld_defconn->lconn_refcnt; /* avoid premature free */ rc = ldap_int_tls_start( ld, ld->ld_defconn, ld->ld_defconn->lconn_server ); --ld->ld_defconn->lconn_refcnt; } #endif return rc; }
int ldap_send_server_request( LDAP *ld, BerElement *ber, ber_int_t msgid, LDAPRequest *parentreq, LDAPURLDesc **srvlist, LDAPConn *lc, LDAPreqinfo *bind, int m_noconn, int m_res ) { LDAPRequest *lr; int incparent, rc; LDAP_ASSERT_MUTEX_OWNER( &ld->ld_req_mutex ); Debug( LDAP_DEBUG_TRACE, "ldap_send_server_request\n", 0, 0, 0 ); incparent = 0; ld->ld_errno = LDAP_SUCCESS; /* optimistic */ LDAP_CONN_LOCK_IF(m_noconn); if ( lc == NULL ) { if ( srvlist == NULL ) { lc = ld->ld_defconn; } else { lc = find_connection( ld, *srvlist, 1 ); if ( lc == NULL ) { if ( (bind != NULL) && (parentreq != NULL) ) { /* Remember the bind in the parent */ incparent = 1; ++parentreq->lr_outrefcnt; } lc = ldap_new_connection( ld, srvlist, 0, 1, bind, 1, m_res ); } } } /* async connect... */ if ( lc != NULL && lc->lconn_status == LDAP_CONNST_CONNECTING ) { ber_socket_t sd = AC_SOCKET_ERROR; struct timeval tv = { 0 }; ber_sockbuf_ctrl( lc->lconn_sb, LBER_SB_OPT_GET_FD, &sd ); /* poll ... */ switch ( ldap_int_poll( ld, sd, &tv, 1 ) ) { case 0: /* go on! */ lc->lconn_status = LDAP_CONNST_CONNECTED; break; case -2: /* async only occurs if a network timeout is set */ /* honor network timeout */ LDAP_MUTEX_LOCK( &ld->ld_options.ldo_mutex ); if ( time( NULL ) - lc->lconn_created <= ld->ld_options.ldo_tm_net.tv_sec ) { /* caller will have to call again */ ld->ld_errno = LDAP_X_CONNECTING; } LDAP_MUTEX_UNLOCK( &ld->ld_options.ldo_mutex ); /* fallthru */ default: /* error */ break; } } if ( lc == NULL || lc->lconn_status != LDAP_CONNST_CONNECTED ) { if ( ld->ld_errno == LDAP_SUCCESS ) { ld->ld_errno = LDAP_SERVER_DOWN; } ber_free( ber, 1 ); if ( incparent ) { /* Forget about the bind */ --parentreq->lr_outrefcnt; } LDAP_CONN_UNLOCK_IF(m_noconn); return( -1 ); } use_connection( ld, lc ); #ifdef LDAP_CONNECTIONLESS if ( LDAP_IS_UDP( ld )) { BerElement tmpber = *ber; ber_rewind( &tmpber ); LDAP_MUTEX_LOCK( &ld->ld_options.ldo_mutex ); rc = ber_write( &tmpber, ld->ld_options.ldo_peer, sizeof( struct sockaddr_storage ), 0 ); LDAP_MUTEX_UNLOCK( &ld->ld_options.ldo_mutex ); if ( rc == -1 ) { ld->ld_errno = LDAP_ENCODING_ERROR; LDAP_CONN_UNLOCK_IF(m_noconn); return rc; } } #endif /* If we still have an incomplete write, try to finish it before * dealing with the new request. If we don't finish here, return * LDAP_BUSY and let the caller retry later. We only allow a single * request to be in WRITING state. */ rc = 0; if ( ld->ld_requests && ld->ld_requests->lr_status == LDAP_REQST_WRITING && ldap_int_flush_request( ld, ld->ld_requests ) < 0 ) { rc = -1; } if ( rc ) { LDAP_CONN_UNLOCK_IF(m_noconn); return rc; } lr = (LDAPRequest *)LDAP_CALLOC( 1, sizeof( LDAPRequest ) ); if ( lr == NULL ) { ld->ld_errno = LDAP_NO_MEMORY; ldap_free_connection( ld, lc, 0, 0 ); ber_free( ber, 1 ); if ( incparent ) { /* Forget about the bind */ --parentreq->lr_outrefcnt; } LDAP_CONN_UNLOCK_IF(m_noconn); return( -1 ); } lr->lr_msgid = msgid; lr->lr_status = LDAP_REQST_INPROGRESS; lr->lr_res_errno = LDAP_SUCCESS; /* optimistic */ lr->lr_ber = ber; lr->lr_conn = lc; if ( parentreq != NULL ) { /* sub-request */ if ( !incparent ) { /* Increment if we didn't do it before the bind */ ++parentreq->lr_outrefcnt; } lr->lr_origid = parentreq->lr_origid; lr->lr_parentcnt = ++parentreq->lr_parentcnt; lr->lr_parent = parentreq; lr->lr_refnext = parentreq->lr_child; parentreq->lr_child = lr; } else { /* original request */ lr->lr_origid = lr->lr_msgid; } /* Extract requestDN for future reference */ #ifdef LDAP_CONNECTIONLESS if ( !LDAP_IS_UDP(ld) ) #endif { BerElement tmpber = *ber; ber_int_t bint; ber_tag_t tag, rtag; ber_reset( &tmpber, 1 ); rtag = ber_scanf( &tmpber, "{it", /*}*/ &bint, &tag ); switch ( tag ) { case LDAP_REQ_BIND: rtag = ber_scanf( &tmpber, "{i" /*}*/, &bint ); break; case LDAP_REQ_DELETE: break; default: rtag = ber_scanf( &tmpber, "{" /*}*/ ); case LDAP_REQ_ABANDON: break; } if ( tag != LDAP_REQ_ABANDON ) { ber_skip_tag( &tmpber, &lr->lr_dn.bv_len ); lr->lr_dn.bv_val = tmpber.ber_ptr; } } lr->lr_prev = NULL; lr->lr_next = ld->ld_requests; if ( lr->lr_next != NULL ) { lr->lr_next->lr_prev = lr; } ld->ld_requests = lr; ld->ld_errno = LDAP_SUCCESS; if ( ldap_int_flush_request( ld, lr ) == -1 ) { msgid = -1; } LDAP_CONN_UNLOCK_IF(m_noconn); return( msgid ); }
int 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; sb = conn->lconn_sb; if( srv ) { host = srv->lud_host; } else { host = conn->lconn_server->lud_host; } /* avoid NULL host */ if( host == NULL ) { host = "localhost"; } (void) tls_init( tls_imp ); #ifdef LDAP_USE_NON_BLOCKING_TLS /* * Use non-blocking io during SSL Handshake when a timeout is configured */ 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); } ssl = ldap_pvt_tls_sb_ctx( sb ); assert( ssl != NULL ); /* * compare host with name(s) in certificate */ if (ld->ld_options.ldo_tls_require_cert != LDAP_OPT_X_TLS_NEVER && ld->ld_options.ldo_tls_require_cert != LDAP_OPT_X_TLS_ALLOW) { ld->ld_errno = ldap_pvt_tls_check_hostname( ld, ssl, host ); if (ld->ld_errno != LDAP_SUCCESS) { return ld->ld_errno; } } return LDAP_SUCCESS; }