From fca72f333b50ea7ca06a697d3fac517f6e64aced Mon Sep 17 00:00:00 2001 From: Howard Chu Date: Thu, 14 Oct 2010 01:29:32 +0000 Subject: [PATCH] Add ldap_sasl_interactive_bind() --- include/ldap.h | 20 +++ libraries/libldap/cyrus.c | 334 ++++++++++++++++------------------- libraries/libldap/ldap-int.h | 5 +- libraries/libldap/sasl.c | 66 ++++++- 4 files changed, 232 insertions(+), 193 deletions(-) diff --git a/include/ldap.h b/include/ldap.h index 5c05d58b40..fa6e4176ec 100644 --- a/include/ldap.h +++ b/include/ldap.h @@ -1186,6 +1186,26 @@ ldap_sasl_bind LDAP_P(( typedef int (LDAP_SASL_INTERACT_PROC) LDAP_P(( LDAP *ld, unsigned flags, void* defaults, void *interact )); +LDAP_F( int ) +ldap_sasl_interactive_bind LDAP_P(( + LDAP *ld, + LDAP_CONST char *dn, /* usually NULL */ + LDAP_CONST char *saslMechanism, + LDAPControl **serverControls, + LDAPControl **clientControls, + + /* should be client controls */ + unsigned flags, + LDAP_SASL_INTERACT_PROC *proc, + void *defaults, + + /* as obtained from ldap_result() */ + LDAPMessage *result, + + /* returned during bind processing */ + const char **rmech, + int *msgid )); + LDAP_F( int ) ldap_sasl_interactive_bind_s LDAP_P(( LDAP *ld, diff --git a/libraries/libldap/cyrus.c b/libraries/libldap/cyrus.c index 9bd5938dc4..a223d4d419 100644 --- a/libraries/libldap/cyrus.c +++ b/libraries/libldap/cyrus.c @@ -386,19 +386,18 @@ ldap_int_sasl_bind( LDAPControl **cctrls, unsigned flags, LDAP_SASL_INTERACT_PROC *interact, - void * defaults ) + void *defaults, + LDAPMessage *result, + const char **rmech, + int *msgid ) { - char *data; - const char *mech = NULL; - const char *pmech = NULL; - int saslrc, rc; - sasl_ssf_t *ssf = NULL; - sasl_conn_t *ctx, *oldctx = NULL; + const char *mech; + sasl_ssf_t *ssf; + sasl_conn_t *ctx; sasl_interact_t *prompts = NULL; + struct berval ccred; + int saslrc, rc; unsigned credlen; - struct berval ccred; - ber_socket_t sd; - void *ssl; Debug( LDAP_DEBUG_TRACE, "ldap_int_sasl_bind: %s\n", mechs ? mechs : "", 0, 0 ); @@ -409,203 +408,161 @@ ldap_int_sasl_bind( return ld->ld_errno; } - rc = 0; - LDAP_MUTEX_LOCK( &ld->ld_req_mutex ); - ber_sockbuf_ctrl( ld->ld_sb, LBER_SB_OPT_GET_FD, &sd ); + /* Starting a Bind */ + if ( !result ) { + const char *pmech = NULL; + sasl_conn_t *oldctx; + ber_socket_t sd; + void *ssl; + + rc = 0; + LDAP_MUTEX_LOCK( &ld->ld_req_mutex ); + ber_sockbuf_ctrl( ld->ld_sb, LBER_SB_OPT_GET_FD, &sd ); - if ( sd == AC_SOCKET_INVALID ) { - /* not connected yet */ + if ( sd == AC_SOCKET_INVALID ) { + /* not connected yet */ - rc = ldap_open_defconn( ld ); + rc = ldap_open_defconn( ld ); - if ( rc == 0 ) { - ber_sockbuf_ctrl( ld->ld_defconn->lconn_sb, - LBER_SB_OPT_GET_FD, &sd ); + if ( rc == 0 ) { + ber_sockbuf_ctrl( ld->ld_defconn->lconn_sb, + LBER_SB_OPT_GET_FD, &sd ); - if( sd == AC_SOCKET_INVALID ) { - ld->ld_errno = LDAP_LOCAL_ERROR; - rc = ld->ld_errno; + if( sd == AC_SOCKET_INVALID ) { + ld->ld_errno = LDAP_LOCAL_ERROR; + rc = ld->ld_errno; + } } - } - } - LDAP_MUTEX_UNLOCK( &ld->ld_req_mutex ); - if( rc != 0 ) return ld->ld_errno; + } + LDAP_MUTEX_UNLOCK( &ld->ld_req_mutex ); + if( rc != 0 ) return ld->ld_errno; - oldctx = ld->ld_defconn->lconn_sasl_authctx; + oldctx = ld->ld_defconn->lconn_sasl_authctx; - /* If we already have an authentication context, clear it out */ - if( oldctx ) { - if ( oldctx != ld->ld_defconn->lconn_sasl_sockctx ) { - sasl_dispose( &oldctx ); + /* If we already have an authentication context, clear it out */ + if( oldctx ) { + if ( oldctx != ld->ld_defconn->lconn_sasl_sockctx ) { + sasl_dispose( &oldctx ); + } + ld->ld_defconn->lconn_sasl_authctx = NULL; } - ld->ld_defconn->lconn_sasl_authctx = NULL; - } - { - char *saslhost; - int nocanon = (int)LDAP_BOOL_GET( &ld->ld_options, - LDAP_BOOL_SASL_NOCANON ); - - /* If we don't need to canonicalize just use the host - * from the LDAP URI. - */ - if ( nocanon ) - saslhost = ld->ld_defconn->lconn_server->lud_host; - else - saslhost = ldap_host_connected_to( ld->ld_defconn->lconn_sb, - "localhost" ); - rc = ldap_int_sasl_open( ld, ld->ld_defconn, saslhost ); - if ( !nocanon ) - LDAP_FREE( saslhost ); - } + { + char *saslhost; + int nocanon = (int)LDAP_BOOL_GET( &ld->ld_options, + LDAP_BOOL_SASL_NOCANON ); + + /* If we don't need to canonicalize just use the host + * from the LDAP URI. + */ + if ( nocanon ) + saslhost = ld->ld_defconn->lconn_server->lud_host; + else + saslhost = ldap_host_connected_to( ld->ld_defconn->lconn_sb, + "localhost" ); + rc = ldap_int_sasl_open( ld, ld->ld_defconn, saslhost ); + if ( !nocanon ) + LDAP_FREE( saslhost ); + } - if ( rc != LDAP_SUCCESS ) return rc; + if ( rc != LDAP_SUCCESS ) return rc; - ctx = ld->ld_defconn->lconn_sasl_authctx; + ctx = ld->ld_defconn->lconn_sasl_authctx; #ifdef HAVE_TLS - /* Check for TLS */ - ssl = ldap_pvt_tls_sb_ctx( ld->ld_defconn->lconn_sb ); - if ( ssl ) { - struct berval authid = BER_BVNULL; - ber_len_t fac; - - fac = ldap_pvt_tls_get_strength( ssl ); - /* failure is OK, we just can't use SASL EXTERNAL */ - (void) ldap_pvt_tls_get_my_dn( ssl, &authid, NULL, 0 ); - - (void) ldap_int_sasl_external( ld, ld->ld_defconn, authid.bv_val, fac ); - LDAP_FREE( authid.bv_val ); - } + /* Check for TLS */ + ssl = ldap_pvt_tls_sb_ctx( ld->ld_defconn->lconn_sb ); + if ( ssl ) { + struct berval authid = BER_BVNULL; + ber_len_t fac; + + fac = ldap_pvt_tls_get_strength( ssl ); + /* failure is OK, we just can't use SASL EXTERNAL */ + (void) ldap_pvt_tls_get_my_dn( ssl, &authid, NULL, 0 ); + + (void) ldap_int_sasl_external( ld, ld->ld_defconn, authid.bv_val, fac ); + LDAP_FREE( authid.bv_val ); + } #endif #if !defined(_WIN32) - /* Check for local */ - if ( ldap_pvt_url_scheme2proto( - ld->ld_defconn->lconn_server->lud_scheme ) == LDAP_PROTO_IPC ) - { - char authid[sizeof("gidNumber=4294967295+uidNumber=4294967295," - "cn=peercred,cn=external,cn=auth")]; - sprintf( authid, "gidNumber=%u+uidNumber=%u," - "cn=peercred,cn=external,cn=auth", - getegid(), geteuid() ); - (void) ldap_int_sasl_external( ld, ld->ld_defconn, authid, - LDAP_PVT_SASL_LOCAL_SSF ); - } + /* Check for local */ + if ( ldap_pvt_url_scheme2proto( + ld->ld_defconn->lconn_server->lud_scheme ) == LDAP_PROTO_IPC ) + { + char authid[sizeof("gidNumber=4294967295+uidNumber=4294967295," + "cn=peercred,cn=external,cn=auth")]; + sprintf( authid, "gidNumber=%u+uidNumber=%u," + "cn=peercred,cn=external,cn=auth", + getegid(), geteuid() ); + (void) ldap_int_sasl_external( ld, ld->ld_defconn, authid, + LDAP_PVT_SASL_LOCAL_SSF ); + } #endif - /* (re)set security properties */ - sasl_setprop( ctx, SASL_SEC_PROPS, - &ld->ld_options.ldo_sasl_secprops ); + /* (re)set security properties */ + sasl_setprop( ctx, SASL_SEC_PROPS, + &ld->ld_options.ldo_sasl_secprops ); - ccred.bv_val = NULL; - ccred.bv_len = 0; + ccred.bv_val = NULL; + ccred.bv_len = 0; + mech = NULL; - do { - saslrc = sasl_client_start( ctx, - mechs, + do { + saslrc = sasl_client_start( ctx, + mechs, #if SASL_VERSION_MAJOR < 2 - NULL, + NULL, #endif - &prompts, - (SASL_CONST char **)&ccred.bv_val, - &credlen, - &mech ); + &prompts, + (SASL_CONST char **)&ccred.bv_val, + &credlen, + &mech ); - if( pmech == NULL && mech != NULL ) { - pmech = mech; + if( pmech == NULL && mech != NULL ) { + pmech = mech; - if( flags != LDAP_SASL_QUIET ) { - fprintf(stderr, - "SASL/%s authentication started\n", - pmech ); + if( flags != LDAP_SASL_QUIET ) { + fprintf(stderr, + "SASL/%s authentication started\n", + pmech ); + } } - } - if( saslrc == SASL_INTERACT ) { - int res; - if( !interact ) break; - res = (interact)( ld, flags, defaults, prompts ); - - if( res != LDAP_SUCCESS ) break; - } - } while ( saslrc == SASL_INTERACT ); - - ccred.bv_len = credlen; + if( saslrc == SASL_INTERACT ) { + int res; + if( !interact ) break; + res = (interact)( ld, flags, defaults, prompts ); - if ( (saslrc != SASL_OK) && (saslrc != SASL_CONTINUE) ) { - rc = ld->ld_errno = sasl_err2ldap( saslrc ); -#if SASL_VERSION_MAJOR >= 2 - if ( ld->ld_error ) { - LDAP_FREE( ld->ld_error ); - } - ld->ld_error = LDAP_STRDUP( sasl_errdetail( ctx ) ); -#endif - goto done; - } + if( res != LDAP_SUCCESS ) break; + } + *rmech = mech; + } while ( saslrc == SASL_INTERACT ); - do { - struct berval *scred; - unsigned credlen; + } else { + /* continuing an in-progress Bind */ + struct berval *scred = NULL; scred = NULL; + rc = ldap_parse_sasl_bind_result( ld, result, &scred, 0 ); + if ( rc != LDAP_SUCCESS ) + goto done; - rc = ldap_sasl_bind_s( ld, dn, mech, &ccred, sctrls, cctrls, - &scred ); - - if ( ccred.bv_val != NULL ) { -#if SASL_VERSION_MAJOR < 2 - LDAP_FREE( ccred.bv_val ); -#endif - ccred.bv_val = NULL; - } - + rc = ldap_result2error( ld, result, 0 ); if ( rc != LDAP_SUCCESS && rc != LDAP_SASL_BIND_IN_PROGRESS ) { if( scred ) { /* and server provided us with data? */ Debug( LDAP_DEBUG_TRACE, - "ldap_int_sasl_bind: rc=%d sasl=%d len=%ld\n", - rc, saslrc, scred ? (long) scred->bv_len : -1L ); + "ldap_int_sasl_bind: rc=%d len=%ld\n", + rc, scred ? (long) scred->bv_len : -1L, 0 ); ber_bvfree( scred ); scred = NULL; } - rc = ld->ld_errno; goto done; } - if( rc == LDAP_SUCCESS && saslrc == SASL_OK ) { - /* we're done, no need to step */ - if( scred ) { - /* but we got additional data? */ -#define KLUDGE_FOR_MSAD -#ifdef KLUDGE_FOR_MSAD - /* - * MSAD provides empty additional data in violation of LDAP - * technical specifications. As no existing SASL mechanism - * allows empty data with an outcome message, just ignore it - * for now. Hopefully MS will fix their bug before someone - * defines a mechanism with possibly empty additional data. - */ - if( scred->bv_len == 0 ) { - Debug( LDAP_DEBUG_ANY, - "ldap_int_sasl_bind: ignoring " - " bogus empty data provided with SASL outcome message.\n", - rc, saslrc, scred->bv_len ); - ber_bvfree( scred ); - } else -#endif - { - Debug( LDAP_DEBUG_TRACE, - "ldap_int_sasl_bind: rc=%d sasl=%d len=%ld\n", - rc, saslrc, scred->bv_len ); - rc = ld->ld_errno = LDAP_LOCAL_ERROR; - ber_bvfree( scred ); - goto done; - } - } - break; - } - + ctx = ld->ld_defconn->lconn_sasl_authctx; + mech = *rmech; do { if( ! scred ) { /* no data! */ @@ -632,36 +589,42 @@ ldap_int_sasl_bind( } } while ( saslrc == SASL_INTERACT ); - ccred.bv_len = credlen; ber_bvfree( scred ); + } - if ( (saslrc != SASL_OK) && (saslrc != SASL_CONTINUE) ) { - ld->ld_errno = sasl_err2ldap( saslrc ); -#if SASL_VERSION_MAJOR >= 2 - if ( ld->ld_error ) { - LDAP_FREE( ld->ld_error ); - } - ld->ld_error = LDAP_STRDUP( sasl_errdetail( ctx ) ); -#endif - rc = ld->ld_errno; - goto done; - } - } while ( rc == LDAP_SASL_BIND_IN_PROGRESS ); - - if ( rc != LDAP_SUCCESS ) goto done; - - if ( saslrc != SASL_OK ) { + if ( (saslrc != SASL_OK) && (saslrc != SASL_CONTINUE) ) { + rc = ld->ld_errno = sasl_err2ldap( saslrc ); #if SASL_VERSION_MAJOR >= 2 if ( ld->ld_error ) { LDAP_FREE( ld->ld_error ); } ld->ld_error = LDAP_STRDUP( sasl_errdetail( ctx ) ); #endif - rc = ld->ld_errno = sasl_err2ldap( saslrc ); goto done; } + ccred.bv_len = credlen; + + /* Always send a request on first Bind; only send subsequent if + * saslrc == SASL_CONTINUE + */ + if ( !result || saslrc == SASL_CONTINUE ) { + rc = ldap_sasl_bind( ld, dn, mech, &ccred, sctrls, cctrls, msgid ); + + if ( ccred.bv_val != NULL ) { +#if SASL_VERSION_MAJOR < 2 + LDAP_FREE( ccred.bv_val ); +#endif + ccred.bv_val = NULL; + } + if ( rc == LDAP_SUCCESS ) + rc = LDAP_SASL_BIND_IN_PROGRESS; + goto done; + } + + /* Conversation was completed successfully by now */ if( flags != LDAP_SASL_QUIET ) { + char *data; saslrc = sasl_getprop( ctx, SASL_USERNAME, (SASL_CONST void **)(char *) &data ); if( saslrc == SASL_OK && data && *data ) { @@ -677,6 +640,7 @@ ldap_int_sasl_bind( #endif } + ssf = NULL; saslrc = sasl_getprop( ctx, SASL_SSF, (SASL_CONST void **)(char *) &ssf ); if( saslrc == SASL_OK ) { if( flags != LDAP_SASL_QUIET ) { @@ -686,7 +650,7 @@ ldap_int_sasl_bind( if( ssf && *ssf ) { if ( ld->ld_defconn->lconn_sasl_sockctx ) { - oldctx = ld->ld_defconn->lconn_sasl_sockctx; + sasl_conn_t *oldctx = ld->ld_defconn->lconn_sasl_sockctx; sasl_dispose( &oldctx ); ldap_pvt_sasl_remove( ld->ld_defconn->lconn_sb ); } diff --git a/libraries/libldap/ldap-int.h b/libraries/libldap/ldap-int.h index 70ecb63d82..e4c760aea0 100644 --- a/libraries/libldap/ldap-int.h +++ b/libraries/libldap/ldap-int.h @@ -685,7 +685,10 @@ LDAP_F (int) ldap_int_sasl_bind LDAP_P(( /* should be passed in client controls */ unsigned flags, LDAP_SASL_INTERACT_PROC *interact, - void *defaults )); + void *defaults, + LDAPMessage *result, + const char **rmech, + int *msgid )); /* in schema.c */ LDAP_F (char *) ldap_int_parse_numericoid LDAP_P(( diff --git a/libraries/libldap/sasl.c b/libraries/libldap/sasl.c index 8238c4cf20..61ae706b10 100644 --- a/libraries/libldap/sasl.c +++ b/libraries/libldap/sasl.c @@ -401,15 +401,16 @@ ldap_pvt_sasl_getmechs ( LDAP *ld, char **pmechlist ) } /* - * ldap_sasl_interactive_bind_s - interactive SASL authentication + * ldap_sasl_interactive_bind - interactive SASL authentication * * This routine uses interactive callbacks. * * LDAP_SUCCESS is returned upon success, the ldap error code - * otherwise. + * otherwise. LDAP_SASL_BIND_IN_PROGRESS is returned if further + * calls are needed. */ int -ldap_sasl_interactive_bind_s( +ldap_sasl_interactive_bind( LDAP *ld, LDAP_CONST char *dn, /* usually NULL */ LDAP_CONST char *mechs, @@ -417,10 +418,13 @@ ldap_sasl_interactive_bind_s( LDAPControl **clientControls, unsigned flags, LDAP_SASL_INTERACT_PROC *interact, - void *defaults ) + void *defaults, + LDAPMessage *result, + const char **rmech, + int *msgid ) { - int rc; char *smechs = NULL; + int rc; #if defined( HAVE_CYRUS_SASL ) LDAP_MUTEX_LOCK( &ldap_int_sasl_mutex ); @@ -437,6 +441,9 @@ ldap_sasl_interactive_bind_s( } else #endif + /* First time */ + if ( !result ) { + #ifdef HAVE_CYRUS_SASL if( mechs == NULL || *mechs == '\0' ) { mechs = ld->ld_options.ldo_def_sasl_mech; @@ -460,10 +467,10 @@ ldap_sasl_interactive_bind_s( "ldap_sasl_interactive_bind_s: user selected: %s\n", mechs, 0, 0 ); } - + } rc = ldap_int_sasl_bind( ld, dn, mechs, serverControls, clientControls, - flags, interact, defaults ); + flags, interact, defaults, result, rmech, msgid ); done: #if defined( HAVE_CYRUS_SASL ) @@ -474,6 +481,51 @@ done: return rc; } +/* + * ldap_sasl_interactive_bind_s - interactive SASL authentication + * + * This routine uses interactive callbacks. + * + * LDAP_SUCCESS is returned upon success, the ldap error code + * otherwise. + */ +int +ldap_sasl_interactive_bind_s( + LDAP *ld, + LDAP_CONST char *dn, /* usually NULL */ + LDAP_CONST char *mechs, + LDAPControl **serverControls, + LDAPControl **clientControls, + unsigned flags, + LDAP_SASL_INTERACT_PROC *interact, + void *defaults ) +{ + const char *rmech = NULL; + LDAPMessage *result = NULL; + int rc, msgid; + + do { + rc = ldap_sasl_interactive_bind( ld, dn, mechs, + serverControls, clientControls, + flags, interact, defaults, result, &rmech, &msgid ); + + if ( rc != LDAP_SASL_BIND_IN_PROGRESS ) + break; + +#ifdef LDAP_CONNECTIONLESS + if (LDAP_IS_UDP(ld)) { + break; + } +#endif + + if ( ldap_result( ld, msgid, LDAP_MSG_ALL, NULL, &result ) == -1 || !result ) { + return( ld->ld_errno ); /* ldap_result sets ld_errno */ + } + } while ( rc == LDAP_SASL_BIND_IN_PROGRESS ); + + return rc; +} + #ifdef HAVE_CYRUS_SASL #ifdef HAVE_SASL_SASL_H -- 2.39.5