Add ldap_sasl_interactive_bind()

This commit is contained in:
Howard Chu 2010-10-14 01:29:32 +00:00
parent 0b660dc9f6
commit fca72f333b
4 changed files with 230 additions and 191 deletions

View file

@ -1186,6 +1186,26 @@ ldap_sasl_bind LDAP_P((
typedef int (LDAP_SASL_INTERACT_PROC) LDAP_P(( typedef int (LDAP_SASL_INTERACT_PROC) LDAP_P((
LDAP *ld, unsigned flags, void* defaults, void *interact )); 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_F( int )
ldap_sasl_interactive_bind_s LDAP_P(( ldap_sasl_interactive_bind_s LDAP_P((
LDAP *ld, LDAP *ld,

View file

@ -386,19 +386,18 @@ ldap_int_sasl_bind(
LDAPControl **cctrls, LDAPControl **cctrls,
unsigned flags, unsigned flags,
LDAP_SASL_INTERACT_PROC *interact, LDAP_SASL_INTERACT_PROC *interact,
void * defaults ) void *defaults,
LDAPMessage *result,
const char **rmech,
int *msgid )
{ {
char *data; const char *mech;
const char *mech = NULL; sasl_ssf_t *ssf;
const char *pmech = NULL; sasl_conn_t *ctx;
int saslrc, rc;
sasl_ssf_t *ssf = NULL;
sasl_conn_t *ctx, *oldctx = NULL;
sasl_interact_t *prompts = NULL; sasl_interact_t *prompts = NULL;
unsigned credlen;
struct berval ccred; struct berval ccred;
ber_socket_t sd; int saslrc, rc;
void *ssl; unsigned credlen;
Debug( LDAP_DEBUG_TRACE, "ldap_int_sasl_bind: %s\n", Debug( LDAP_DEBUG_TRACE, "ldap_int_sasl_bind: %s\n",
mechs ? mechs : "<null>", 0, 0 ); mechs ? mechs : "<null>", 0, 0 );
@ -409,6 +408,13 @@ ldap_int_sasl_bind(
return ld->ld_errno; return ld->ld_errno;
} }
/* Starting a Bind */
if ( !result ) {
const char *pmech = NULL;
sasl_conn_t *oldctx;
ber_socket_t sd;
void *ssl;
rc = 0; rc = 0;
LDAP_MUTEX_LOCK( &ld->ld_req_mutex ); LDAP_MUTEX_LOCK( &ld->ld_req_mutex );
ber_sockbuf_ctrl( ld->ld_sb, LBER_SB_OPT_GET_FD, &sd ); ber_sockbuf_ctrl( ld->ld_sb, LBER_SB_OPT_GET_FD, &sd );
@ -500,6 +506,7 @@ ldap_int_sasl_bind(
ccred.bv_val = NULL; ccred.bv_val = NULL;
ccred.bv_len = 0; ccred.bv_len = 0;
mech = NULL;
do { do {
saslrc = sasl_client_start( ctx, saslrc = sasl_client_start( ctx,
@ -529,83 +536,33 @@ ldap_int_sasl_bind(
if( res != LDAP_SUCCESS ) break; if( res != LDAP_SUCCESS ) break;
} }
*rmech = mech;
} while ( saslrc == SASL_INTERACT ); } while ( saslrc == SASL_INTERACT );
ccred.bv_len = credlen; } else {
/* continuing an in-progress Bind */
if ( (saslrc != SASL_OK) && (saslrc != SASL_CONTINUE) ) { struct berval *scred = NULL;
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;
}
do {
struct berval *scred;
unsigned credlen;
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, rc = ldap_result2error( ld, result, 0 );
&scred );
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 ) { if ( rc != LDAP_SUCCESS && rc != LDAP_SASL_BIND_IN_PROGRESS ) {
if( scred ) { if( scred ) {
/* and server provided us with data? */ /* and server provided us with data? */
Debug( LDAP_DEBUG_TRACE, Debug( LDAP_DEBUG_TRACE,
"ldap_int_sasl_bind: rc=%d sasl=%d len=%ld\n", "ldap_int_sasl_bind: rc=%d len=%ld\n",
rc, saslrc, scred ? (long) scred->bv_len : -1L ); rc, scred ? (long) scred->bv_len : -1L, 0 );
ber_bvfree( scred ); ber_bvfree( scred );
scred = NULL; scred = NULL;
} }
rc = ld->ld_errno;
goto done; goto done;
} }
if( rc == LDAP_SUCCESS && saslrc == SASL_OK ) { ctx = ld->ld_defconn->lconn_sasl_authctx;
/* we're done, no need to step */ mech = *rmech;
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;
}
do { do {
if( ! scred ) { if( ! scred ) {
/* no data! */ /* no data! */
@ -632,36 +589,42 @@ ldap_int_sasl_bind(
} }
} while ( saslrc == SASL_INTERACT ); } while ( saslrc == SASL_INTERACT );
ccred.bv_len = credlen;
ber_bvfree( scred ); ber_bvfree( scred );
}
if ( (saslrc != SASL_OK) && (saslrc != SASL_CONTINUE) ) { 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 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 ); 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; 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 ) { if( flags != LDAP_SASL_QUIET ) {
char *data;
saslrc = sasl_getprop( ctx, SASL_USERNAME, saslrc = sasl_getprop( ctx, SASL_USERNAME,
(SASL_CONST void **)(char *) &data ); (SASL_CONST void **)(char *) &data );
if( saslrc == SASL_OK && data && *data ) { if( saslrc == SASL_OK && data && *data ) {
@ -677,6 +640,7 @@ ldap_int_sasl_bind(
#endif #endif
} }
ssf = NULL;
saslrc = sasl_getprop( ctx, SASL_SSF, (SASL_CONST void **)(char *) &ssf ); saslrc = sasl_getprop( ctx, SASL_SSF, (SASL_CONST void **)(char *) &ssf );
if( saslrc == SASL_OK ) { if( saslrc == SASL_OK ) {
if( flags != LDAP_SASL_QUIET ) { if( flags != LDAP_SASL_QUIET ) {
@ -686,7 +650,7 @@ ldap_int_sasl_bind(
if( ssf && *ssf ) { if( ssf && *ssf ) {
if ( ld->ld_defconn->lconn_sasl_sockctx ) { 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 ); sasl_dispose( &oldctx );
ldap_pvt_sasl_remove( ld->ld_defconn->lconn_sb ); ldap_pvt_sasl_remove( ld->ld_defconn->lconn_sb );
} }

View file

@ -685,7 +685,10 @@ LDAP_F (int) ldap_int_sasl_bind LDAP_P((
/* should be passed in client controls */ /* should be passed in client controls */
unsigned flags, unsigned flags,
LDAP_SASL_INTERACT_PROC *interact, LDAP_SASL_INTERACT_PROC *interact,
void *defaults )); void *defaults,
LDAPMessage *result,
const char **rmech,
int *msgid ));
/* in schema.c */ /* in schema.c */
LDAP_F (char *) ldap_int_parse_numericoid LDAP_P(( LDAP_F (char *) ldap_int_parse_numericoid LDAP_P((

View file

@ -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. * This routine uses interactive callbacks.
* *
* LDAP_SUCCESS is returned upon success, the ldap error code * 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 int
ldap_sasl_interactive_bind_s( ldap_sasl_interactive_bind(
LDAP *ld, LDAP *ld,
LDAP_CONST char *dn, /* usually NULL */ LDAP_CONST char *dn, /* usually NULL */
LDAP_CONST char *mechs, LDAP_CONST char *mechs,
@ -417,10 +418,13 @@ ldap_sasl_interactive_bind_s(
LDAPControl **clientControls, LDAPControl **clientControls,
unsigned flags, unsigned flags,
LDAP_SASL_INTERACT_PROC *interact, LDAP_SASL_INTERACT_PROC *interact,
void *defaults ) void *defaults,
LDAPMessage *result,
const char **rmech,
int *msgid )
{ {
int rc;
char *smechs = NULL; char *smechs = NULL;
int rc;
#if defined( HAVE_CYRUS_SASL ) #if defined( HAVE_CYRUS_SASL )
LDAP_MUTEX_LOCK( &ldap_int_sasl_mutex ); LDAP_MUTEX_LOCK( &ldap_int_sasl_mutex );
@ -437,6 +441,9 @@ ldap_sasl_interactive_bind_s(
} else } else
#endif #endif
/* First time */
if ( !result ) {
#ifdef HAVE_CYRUS_SASL #ifdef HAVE_CYRUS_SASL
if( mechs == NULL || *mechs == '\0' ) { if( mechs == NULL || *mechs == '\0' ) {
mechs = ld->ld_options.ldo_def_sasl_mech; 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", "ldap_sasl_interactive_bind_s: user selected: %s\n",
mechs, 0, 0 ); mechs, 0, 0 );
} }
}
rc = ldap_int_sasl_bind( ld, dn, mechs, rc = ldap_int_sasl_bind( ld, dn, mechs,
serverControls, clientControls, serverControls, clientControls,
flags, interact, defaults ); flags, interact, defaults, result, rmech, msgid );
done: done:
#if defined( HAVE_CYRUS_SASL ) #if defined( HAVE_CYRUS_SASL )
@ -474,6 +481,51 @@ done:
return rc; 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_CYRUS_SASL
#ifdef HAVE_SASL_SASL_H #ifdef HAVE_SASL_SASL_H