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;
struct berval ccred;
int saslrc, rc;
unsigned credlen; unsigned credlen;
struct berval ccred;
ber_socket_t sd;
void *ssl;
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,203 +408,161 @@ ldap_int_sasl_bind(
return ld->ld_errno; return ld->ld_errno;
} }
rc = 0; /* Starting a Bind */
LDAP_MUTEX_LOCK( &ld->ld_req_mutex ); if ( !result ) {
ber_sockbuf_ctrl( ld->ld_sb, LBER_SB_OPT_GET_FD, &sd ); const char *pmech = NULL;
sasl_conn_t *oldctx;
ber_socket_t sd;
void *ssl;
if ( sd == AC_SOCKET_INVALID ) { rc = 0;
/* not connected yet */ LDAP_MUTEX_LOCK( &ld->ld_req_mutex );
ber_sockbuf_ctrl( ld->ld_sb, LBER_SB_OPT_GET_FD, &sd );
rc = ldap_open_defconn( ld ); if ( sd == AC_SOCKET_INVALID ) {
/* not connected yet */
if ( rc == 0 ) { rc = ldap_open_defconn( ld );
ber_sockbuf_ctrl( ld->ld_defconn->lconn_sb,
LBER_SB_OPT_GET_FD, &sd );
if( sd == AC_SOCKET_INVALID ) { if ( rc == 0 ) {
ld->ld_errno = LDAP_LOCAL_ERROR; ber_sockbuf_ctrl( ld->ld_defconn->lconn_sb,
rc = ld->ld_errno; LBER_SB_OPT_GET_FD, &sd );
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;
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 );
}
ld->ld_defconn->lconn_sasl_authctx = NULL;
} }
}
LDAP_MUTEX_UNLOCK( &ld->ld_req_mutex );
if( rc != 0 ) return ld->ld_errno;
oldctx = ld->ld_defconn->lconn_sasl_authctx; {
char *saslhost;
int nocanon = (int)LDAP_BOOL_GET( &ld->ld_options,
LDAP_BOOL_SASL_NOCANON );
/* If we already have an authentication context, clear it out */ /* If we don't need to canonicalize just use the host
if( oldctx ) { * from the LDAP URI.
if ( oldctx != ld->ld_defconn->lconn_sasl_sockctx ) { */
sasl_dispose( &oldctx ); 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 );
} }
ld->ld_defconn->lconn_sasl_authctx = NULL;
}
{ if ( rc != LDAP_SUCCESS ) return rc;
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 ctx = ld->ld_defconn->lconn_sasl_authctx;
* 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;
ctx = ld->ld_defconn->lconn_sasl_authctx;
#ifdef HAVE_TLS #ifdef HAVE_TLS
/* Check for TLS */ /* Check for TLS */
ssl = ldap_pvt_tls_sb_ctx( ld->ld_defconn->lconn_sb ); ssl = ldap_pvt_tls_sb_ctx( ld->ld_defconn->lconn_sb );
if ( ssl ) { if ( ssl ) {
struct berval authid = BER_BVNULL; struct berval authid = BER_BVNULL;
ber_len_t fac; ber_len_t fac;
fac = ldap_pvt_tls_get_strength( ssl ); fac = ldap_pvt_tls_get_strength( ssl );
/* failure is OK, we just can't use SASL EXTERNAL */ /* failure is OK, we just can't use SASL EXTERNAL */
(void) ldap_pvt_tls_get_my_dn( ssl, &authid, NULL, 0 ); (void) ldap_pvt_tls_get_my_dn( ssl, &authid, NULL, 0 );
(void) ldap_int_sasl_external( ld, ld->ld_defconn, authid.bv_val, fac ); (void) ldap_int_sasl_external( ld, ld->ld_defconn, authid.bv_val, fac );
LDAP_FREE( authid.bv_val ); LDAP_FREE( authid.bv_val );
} }
#endif #endif
#if !defined(_WIN32) #if !defined(_WIN32)
/* Check for local */ /* Check for local */
if ( ldap_pvt_url_scheme2proto( if ( ldap_pvt_url_scheme2proto(
ld->ld_defconn->lconn_server->lud_scheme ) == LDAP_PROTO_IPC ) ld->ld_defconn->lconn_server->lud_scheme ) == LDAP_PROTO_IPC )
{ {
char authid[sizeof("gidNumber=4294967295+uidNumber=4294967295," char authid[sizeof("gidNumber=4294967295+uidNumber=4294967295,"
"cn=peercred,cn=external,cn=auth")]; "cn=peercred,cn=external,cn=auth")];
sprintf( authid, "gidNumber=%u+uidNumber=%u," sprintf( authid, "gidNumber=%u+uidNumber=%u,"
"cn=peercred,cn=external,cn=auth", "cn=peercred,cn=external,cn=auth",
getegid(), geteuid() ); getegid(), geteuid() );
(void) ldap_int_sasl_external( ld, ld->ld_defconn, authid, (void) ldap_int_sasl_external( ld, ld->ld_defconn, authid,
LDAP_PVT_SASL_LOCAL_SSF ); LDAP_PVT_SASL_LOCAL_SSF );
} }
#endif #endif
/* (re)set security properties */ /* (re)set security properties */
sasl_setprop( ctx, SASL_SEC_PROPS, sasl_setprop( ctx, SASL_SEC_PROPS,
&ld->ld_options.ldo_sasl_secprops ); &ld->ld_options.ldo_sasl_secprops );
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,
mechs, mechs,
#if SASL_VERSION_MAJOR < 2 #if SASL_VERSION_MAJOR < 2
NULL, NULL,
#endif #endif
&prompts, &prompts,
(SASL_CONST char **)&ccred.bv_val, (SASL_CONST char **)&ccred.bv_val,
&credlen, &credlen,
&mech ); &mech );
if( pmech == NULL && mech != NULL ) { if( pmech == NULL && mech != NULL ) {
pmech = mech; pmech = mech;
if( flags != LDAP_SASL_QUIET ) { if( flags != LDAP_SASL_QUIET ) {
fprintf(stderr, fprintf(stderr,
"SASL/%s authentication started\n", "SASL/%s authentication started\n",
pmech ); pmech );
}
} }
}
if( saslrc == SASL_INTERACT ) { if( saslrc == SASL_INTERACT ) {
int res; int res;
if( !interact ) break; if( !interact ) break;
res = (interact)( ld, flags, defaults, prompts ); res = (interact)( ld, flags, defaults, prompts );
if( res != LDAP_SUCCESS ) break; if( res != LDAP_SUCCESS ) break;
} }
} while ( saslrc == SASL_INTERACT ); *rmech = mech;
} 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 ); 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;
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 SASL_VERSION_MAJOR >= 2
if ( ld->ld_error ) { if ( ld->ld_error ) {
LDAP_FREE( ld->ld_error ); LDAP_FREE( ld->ld_error );
} }
ld->ld_error = LDAP_STRDUP( sasl_errdetail( ctx ) ); ld->ld_error = LDAP_STRDUP( sasl_errdetail( ctx ) );
#endif #endif
rc = ld->ld_errno = sasl_err2ldap( saslrc );
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