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((
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,

View File

@ -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 : "<null>", 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;
if ( sd == AC_SOCKET_INVALID ) {
/* not connected yet */
rc = 0;
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 ) {
ber_sockbuf_ctrl( ld->ld_defconn->lconn_sb,
LBER_SB_OPT_GET_FD, &sd );
rc = ldap_open_defconn( ld );
if( sd == AC_SOCKET_INVALID ) {
ld->ld_errno = LDAP_LOCAL_ERROR;
rc = ld->ld_errno;
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;
}
}
}
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( oldctx ) {
if ( oldctx != ld->ld_defconn->lconn_sasl_sockctx ) {
sasl_dispose( &oldctx );
/* 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 );
}
ld->ld_defconn->lconn_sasl_authctx = NULL;
}
{
char *saslhost;
int nocanon = (int)LDAP_BOOL_GET( &ld->ld_options,
LDAP_BOOL_SASL_NOCANON );
if ( rc != LDAP_SUCCESS ) return rc;
/* 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;
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;
/* 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 );
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 );
}
(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( saslrc == SASL_INTERACT ) {
int res;
if( !interact ) break;
res = (interact)( ld, flags, defaults, prompts );
if( res != LDAP_SUCCESS ) break;
}
} while ( saslrc == SASL_INTERACT );
if( res != LDAP_SUCCESS ) break;
}
*rmech = mech;
} while ( saslrc == SASL_INTERACT );
ccred.bv_len = credlen;
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;
}
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 );
}

View File

@ -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((

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.
*
* 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