Commit 46e2b977 authored by Howard Chu's avatar Howard Chu
Browse files

ITS#2424 use two SASL contexts per session to conform to RFC 2222

parent 0cafb28e
......@@ -476,7 +476,7 @@ ldap_int_sasl_open(
int rc;
sasl_conn_t *ctx;
assert( lc->lconn_sasl_ctx == NULL );
assert( lc->lconn_sasl_authctx == NULL );
if ( host == NULL ) {
ld->ld_errno = LDAP_LOCAL_ERROR;
......@@ -504,18 +504,23 @@ ldap_int_sasl_open(
host, 0, 0 );
#endif
lc->lconn_sasl_ctx = ctx;
lc->lconn_sasl_authctx = ctx;
return LDAP_SUCCESS;
}
int ldap_int_sasl_close( LDAP *ld, LDAPConn *lc )
{
sasl_conn_t *ctx = lc->lconn_sasl_ctx;
sasl_conn_t *ctx = lc->lconn_sasl_authctx;
if( ctx != NULL ) {
sasl_dispose( &ctx );
lc->lconn_sasl_ctx = NULL;
if ( lc->lconn_sasl_sockctx && ctx != lc->lconn_sasl_sockctx ) {
ctx = lc->lconn_sasl_sockctx;
sasl_dispose( &ctx );
}
lc->lconn_sasl_sockctx = NULL;
lc->lconn_sasl_authctx = NULL;
}
return LDAP_SUCCESS;
......@@ -537,7 +542,7 @@ ldap_int_sasl_bind(
const char *pmech = NULL;
int saslrc, rc;
sasl_ssf_t *ssf = NULL;
sasl_conn_t *ctx;
sasl_conn_t *ctx, *oldctx = NULL;
sasl_interact_t *prompts = NULL;
unsigned credlen;
struct berval ccred;
......@@ -575,33 +580,23 @@ ldap_int_sasl_bind(
}
}
ctx = ld->ld_defconn->lconn_sasl_ctx;
/* If we already have a context, shut it down */
if( ctx ) {
int msgid;
LDAPMessage *result;
/* Do an anonymous bind to kill the server's context */
msgid = ldap_simple_bind( ld, "", NULL );
/* dispose of the old context */
ldap_int_sasl_close( ld, ld->ld_defconn );
ldap_pvt_sasl_remove( ld->ld_sb );
oldctx = ld->ld_defconn->lconn_sasl_authctx;
/* The reply is sent in the clear, we can't read it
* until after the context and sockbuf are torn down
*/
rc = ldap_result( ld, msgid, 1, NULL, &result );
ldap_msgfree( result );
/* 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;
}
rc = ldap_int_sasl_open( ld, ld->ld_defconn,
ld->ld_defconn->lconn_server->lud_host ?
ld->ld_defconn->lconn_server->lud_host : "localhost" );
if ( rc != LDAP_SUCCESS ) return rc;
ctx = ld->ld_defconn->lconn_sasl_ctx;
ctx = ld->ld_defconn->lconn_sasl_authctx;
/* Check for TLS */
ssl = ldap_pvt_tls_sb_ctx( ld->ld_sb );
......@@ -799,9 +794,16 @@ ldap_int_sasl_bind(
if( flags != LDAP_SASL_QUIET ) {
fprintf( stderr, "SASL installing layers\n" );
}
if ( ld->ld_defconn->lconn_sasl_sockctx ) {
oldctx = ld->ld_defconn->lconn_sasl_sockctx;
sasl_dispose( &oldctx );
ldap_pvt_sasl_remove( ld->ld_sb );
}
ldap_pvt_sasl_install( ld->ld_conns->lconn_sb, ctx );
ld->ld_defconn->lconn_sasl_sockctx = ctx;
}
}
ld->ld_defconn->lconn_sasl_authctx = ctx;
done:
return rc;
......@@ -820,7 +822,7 @@ ldap_int_sasl_external(
sasl_external_properties_t extprops;
#endif
ctx = conn->lconn_sasl_ctx;
ctx = conn->lconn_sasl_authctx;
if ( ctx == NULL ) {
return LDAP_LOCAL_ERROR;
......@@ -1000,7 +1002,7 @@ ldap_int_sasl_get_option( LDAP *ld, int option, void *arg )
return -1;
}
ctx = ld->ld_defconn->lconn_sasl_ctx;
ctx = ld->ld_defconn->lconn_sasl_sockctx;
if ( ctx == NULL ) {
return -1;
......@@ -1062,7 +1064,7 @@ ldap_int_sasl_set_option( LDAP *ld, int option, void *arg )
return -1;
}
ctx = ld->ld_defconn->lconn_sasl_ctx;
ctx = ld->ld_defconn->lconn_sasl_authctx;
if ( ctx == NULL ) {
return -1;
......
......@@ -188,7 +188,8 @@ typedef struct ldap_conn {
void *lconn_tls_ctx;
#endif
#ifdef HAVE_CYRUS_SASL
void *lconn_sasl_ctx;
void *lconn_sasl_authctx; /* context for bind */
void *lconn_sasl_sockctx; /* for security layer */
#endif
int lconn_refcnt;
time_t lconn_lastused; /* time */
......
......@@ -693,7 +693,7 @@ be_isroot_pw( Operation *op )
#if defined( SLAPD_CRYPT ) || defined( SLAPD_SPASSWD )
ldap_pvt_thread_mutex_lock( &passwd_mutex );
#ifdef SLAPD_SPASSWD
lutil_passwd_sasl_conn = op->o_conn->c_sasl_context;
lutil_passwd_sasl_conn = op->o_conn->c_sasl_authctx;
#endif
#endif
......
......@@ -433,7 +433,9 @@ long connection_init(
c->c_sasl_bind_mech.bv_val = NULL;
c->c_sasl_bind_mech.bv_len = 0;
c->c_sasl_context = NULL;
c->c_sasl_done = 0;
c->c_sasl_authctx = NULL;
c->c_sasl_sockctx = NULL;
c->c_sasl_extra = NULL;
c->c_sasl_bindop = NULL;
......@@ -467,7 +469,9 @@ long connection_init(
assert( LDAP_STAILQ_EMPTY(&c->c_ops) );
assert( LDAP_STAILQ_EMPTY(&c->c_pending_ops) );
assert( c->c_sasl_bind_mech.bv_val == NULL );
assert( c->c_sasl_context == NULL );
assert( c->c_sasl_done == 0 );
assert( c->c_sasl_authctx == NULL );
assert( c->c_sasl_sockctx == NULL );
assert( c->c_sasl_extra == NULL );
assert( c->c_sasl_bindop == NULL );
assert( c->c_currentber == NULL );
......@@ -556,7 +560,7 @@ long connection_init(
}
#endif
slap_sasl_open( c );
slap_sasl_open( c, 0 );
slap_sasl_external( c, ssf, authid );
ldap_pvt_thread_mutex_unlock( &c->c_mutex );
......@@ -1241,9 +1245,16 @@ int connection_read(ber_socket_t s)
#ifdef HAVE_CYRUS_SASL
if ( c->c_sasl_layers ) {
/* If previous layer is not removed yet, give up for now */
if ( !c->c_sasl_sockctx ) {
connection_return( c );
ldap_pvt_thread_mutex_unlock( &connections_mutex );
return 0;
}
c->c_sasl_layers = 0;
rc = ldap_pvt_sasl_install( c->c_sb, c->c_sasl_context );
rc = ldap_pvt_sasl_install( c->c_sb, c->c_sasl_sockctx );
if( rc != LDAP_SUCCESS ) {
#ifdef NEW_LOGGING
......
......@@ -259,7 +259,7 @@ slap_passwd_check(
#if defined( SLAPD_CRYPT ) || defined( SLAPD_SPASSWD )
ldap_pvt_thread_mutex_lock( &passwd_mutex );
#ifdef SLAPD_SPASSWD
lutil_passwd_sasl_conn = conn->c_sasl_context;
lutil_passwd_sasl_conn = conn->c_sasl_authctx;
#endif
#endif
......
......@@ -834,7 +834,7 @@ LDAP_SLAPD_F (int) slap_sasl_init(void);
LDAP_SLAPD_F (char *) slap_sasl_secprops( const char * );
LDAP_SLAPD_F (int) slap_sasl_destroy(void);
LDAP_SLAPD_F (int) slap_sasl_open( Connection *c );
LDAP_SLAPD_F (int) slap_sasl_open( Connection *c, int reopen );
LDAP_SLAPD_F (char **) slap_sasl_mechs( Connection *c );
LDAP_SLAPD_F (int) slap_sasl_external( Connection *c,
......
......@@ -825,7 +825,7 @@ slap_sasl_authorize(
#endif
/* Figure out how much data we have for the dn */
rc = sasl_getprop( conn->c_sasl_context, SASL_REALM, (void **)&realm );
rc = sasl_getprop( conn->c_sasl_authctx, SASL_REALM, (void **)&realm );
if( rc != SASL_OK && rc != SASL_NOTDONE ) {
#ifdef NEW_LOGGING
LDAP_LOG( TRANSPORT, ERR,
......@@ -1047,7 +1047,7 @@ int slap_sasl_destroy( void )
return 0;
}
int slap_sasl_open( Connection *conn )
int slap_sasl_open( Connection *conn, int reopen )
{
int cb, sc = LDAP_SUCCESS;
#if SASL_VERSION_MAJOR >= 2
......@@ -1058,18 +1058,18 @@ int slap_sasl_open( Connection *conn )
sasl_conn_t *ctx = NULL;
sasl_callback_t *session_callbacks;
assert( conn->c_sasl_context == NULL );
assert( conn->c_sasl_extra == NULL );
assert( conn->c_sasl_authctx == NULL );
conn->c_sasl_layers = 0;
if ( !reopen ) {
assert( conn->c_sasl_extra == NULL );
session_callbacks =
session_callbacks =
#if SASL_VERSION_MAJOR >= 2
SLAP_CALLOC( 5, sizeof(sasl_callback_t));
SLAP_CALLOC( 5, sizeof(sasl_callback_t));
#else
SLAP_CALLOC( 3, sizeof(sasl_callback_t));
SLAP_CALLOC( 3, sizeof(sasl_callback_t));
#endif
if( session_callbacks == NULL ) {
if( session_callbacks == NULL ) {
#ifdef NEW_LOGGING
LDAP_LOG( TRANSPORT, ERR,
"slap_sasl_open: SLAP_MALLOC failed", 0, 0, 0 );
......@@ -1078,31 +1078,36 @@ int slap_sasl_open( Connection *conn )
"slap_sasl_open: SLAP_MALLOC failed", 0, 0, 0 );
#endif
return -1;
}
conn->c_sasl_extra = session_callbacks;
}
conn->c_sasl_extra = session_callbacks;
session_callbacks[cb=0].id = SASL_CB_LOG;
session_callbacks[cb].proc = &slap_sasl_log;
session_callbacks[cb++].context = conn;
session_callbacks[cb=0].id = SASL_CB_LOG;
session_callbacks[cb].proc = &slap_sasl_log;
session_callbacks[cb++].context = conn;
session_callbacks[cb].id = SASL_CB_PROXY_POLICY;
session_callbacks[cb].proc = &slap_sasl_authorize;
session_callbacks[cb++].context = conn;
session_callbacks[cb].id = SASL_CB_PROXY_POLICY;
session_callbacks[cb].proc = &slap_sasl_authorize;
session_callbacks[cb++].context = conn;
#if SASL_VERSION_MAJOR >= 2
session_callbacks[cb].id = SASL_CB_CANON_USER;
session_callbacks[cb].proc = &slap_sasl_canonicalize;
session_callbacks[cb++].context = conn;
/* XXXX: this should be conditional */
session_callbacks[cb].id = SASL_CB_SERVER_USERDB_CHECKPASS;
session_callbacks[cb].proc = &slap_sasl_checkpass;
session_callbacks[cb++].context = conn;
session_callbacks[cb].id = SASL_CB_CANON_USER;
session_callbacks[cb].proc = &slap_sasl_canonicalize;
session_callbacks[cb++].context = conn;
/* XXXX: this should be conditional */
session_callbacks[cb].id = SASL_CB_SERVER_USERDB_CHECKPASS;
session_callbacks[cb].proc = &slap_sasl_checkpass;
session_callbacks[cb++].context = conn;
#endif
session_callbacks[cb].id = SASL_CB_LIST_END;
session_callbacks[cb].proc = NULL;
session_callbacks[cb++].context = NULL;
session_callbacks[cb].id = SASL_CB_LIST_END;
session_callbacks[cb].proc = NULL;
session_callbacks[cb++].context = NULL;
} else {
session_callbacks = conn->c_sasl_extra;
}
conn->c_sasl_layers = 0;
if( global_host == NULL ) {
global_host = ldap_pvt_get_fqdn( NULL );
......@@ -1161,7 +1166,7 @@ int slap_sasl_open( Connection *conn )
return -1;
}
conn->c_sasl_context = ctx;
conn->c_sasl_authctx = ctx;
if( sc == SASL_OK ) {
sc = sasl_setprop( ctx,
......@@ -1193,7 +1198,7 @@ int slap_sasl_external(
{
#if SASL_VERSION_MAJOR >= 2
int sc;
sasl_conn_t *ctx = conn->c_sasl_context;
sasl_conn_t *ctx = conn->c_sasl_authctx;
if ( ctx == NULL ) {
return LDAP_UNAVAILABLE;
......@@ -1213,7 +1218,7 @@ int slap_sasl_external(
#elif defined(HAVE_CYRUS_SASL)
int sc;
sasl_conn_t *ctx = conn->c_sasl_context;
sasl_conn_t *ctx = conn->c_sasl_authctx;
sasl_external_properties_t extprops;
if ( ctx == NULL ) {
......@@ -1237,32 +1242,7 @@ int slap_sasl_external(
int slap_sasl_reset( Connection *conn )
{
int rc = LDAP_SUCCESS;
#ifdef HAVE_CYRUS_SASL
sasl_conn_t *ctx = conn->c_sasl_context;
slap_ssf_t ssf = 0;
const char *authid = NULL;
#if SASL_VERSION_MAJOR >= 2
sasl_getprop( ctx, SASL_SSF_EXTERNAL, &ssf );
sasl_getprop( ctx, SASL_AUTH_EXTERNAL, &authid );
if ( authid ) authid = ch_strdup( authid );
#else
/* we can't retrieve the external properties from SASL 1.5.
* we can get it again from the underlying TLS or IPC connection,
* but it's simpler just to ignore it since 1.5 is obsolete.
*/
#endif
rc = slap_sasl_close( conn );
ldap_pvt_sasl_remove( conn->c_sb );
if ( rc == LDAP_SUCCESS ) {
rc = slap_sasl_open( conn );
}
if ( rc == LDAP_SUCCESS ) {
rc = slap_sasl_external( conn, ssf, authid );
}
if ( authid ) ch_free( authid );
#endif
return rc;
return LDAP_SUCCESS;
}
char ** slap_sasl_mechs( Connection *conn )
......@@ -1270,7 +1250,9 @@ char ** slap_sasl_mechs( Connection *conn )
char **mechs = NULL;
#ifdef HAVE_CYRUS_SASL
sasl_conn_t *ctx = conn->c_sasl_context;
sasl_conn_t *ctx = conn->c_sasl_authctx;
if( ctx == NULL ) ctx = conn->c_sasl_sockctx;
if( ctx != NULL ) {
int sc;
......@@ -1306,13 +1288,19 @@ char ** slap_sasl_mechs( Connection *conn )
int slap_sasl_close( Connection *conn )
{
#ifdef HAVE_CYRUS_SASL
sasl_conn_t *ctx = conn->c_sasl_context;
sasl_conn_t *ctx = conn->c_sasl_authctx;
if( ctx != NULL ) {
sasl_dispose( &ctx );
}
if ( conn->c_sasl_sockctx && conn->c_sasl_authctx != conn->c_sasl_sockctx ) {
ctx = conn->c_sasl_sockctx;
sasl_dispose( &ctx );
}
conn->c_sasl_context = NULL;
conn->c_sasl_authctx = NULL;
conn->c_sasl_sockctx = NULL;
conn->c_sasl_done = 0;
free( conn->c_sasl_extra );
conn->c_sasl_extra = NULL;
......@@ -1324,7 +1312,7 @@ int slap_sasl_close( Connection *conn )
int slap_sasl_bind( Operation *op, SlapReply *rs )
{
#ifdef HAVE_CYRUS_SASL
sasl_conn_t *ctx = op->o_conn->c_sasl_context;
sasl_conn_t *ctx = op->o_conn->c_sasl_authctx;
struct berval response;
unsigned reslen = 0;
int sc;
......@@ -1365,6 +1353,30 @@ int slap_sasl_bind( Operation *op, SlapReply *rs )
#endif
if ( !op->o_conn->c_sasl_bind_in_progress ) {
/* If we already authenticated once, must use a new context */
if ( op->o_conn->c_sasl_done ) {
slap_ssf_t ssf = 0;
const char *authid = NULL;
#if SASL_VERSION_MAJOR >= 2
sasl_getprop( ctx, SASL_SSF_EXTERNAL, (void *)&ssf );
sasl_getprop( ctx, SASL_AUTH_EXTERNAL, (void *)&authid );
if ( authid ) authid = ch_strdup( authid );
#endif
if ( ctx != op->o_conn->c_sasl_sockctx ) {
sasl_dispose( &ctx );
}
op->o_conn->c_sasl_authctx = NULL;
slap_sasl_open( op->o_conn, 1 );
ctx = op->o_conn->c_sasl_authctx;
#if SASL_VERSION_MAJOR >= 2
if ( authid ) {
sasl_setprop( ctx, SASL_SSF_EXTERNAL, &ssf );
sasl_setprop( ctx, SASL_AUTH_EXTERNAL, authid );
ch_free( (char *)authid );
}
#endif
}
sc = START( ctx,
op->o_conn->c_sasl_bind_mech.bv_val,
op->orb_cred.bv_val, op->orb_cred.bv_len,
......@@ -1384,21 +1396,47 @@ int slap_sasl_bind( Operation *op, SlapReply *rs )
op->orb_edn = op->o_conn->c_sasl_dn;
op->o_conn->c_sasl_dn.bv_val = NULL;
op->o_conn->c_sasl_dn.bv_len = 0;
op->o_conn->c_sasl_done = 1;
rs->sr_err = LDAP_SUCCESS;
(void) sasl_getprop( ctx, SASL_SSF, (void *)&ssf );
op->orb_ssf = ssf ? *ssf : 0;
ctx = NULL;
if( op->orb_ssf ) {
ldap_pvt_thread_mutex_lock( &op->o_conn->c_mutex );
op->o_conn->c_sasl_layers++;
/* If there's an old layer, set sockctx to NULL to
* tell connection_read() to wait for us to finish.
* Otherwise there is a race condition: we have to
* send the Bind response using the old security
* context and then remove it before reading any
* new messages.
*/
if ( op->o_conn->c_sasl_sockctx ) {
ctx = op->o_conn->c_sasl_sockctx;
op->o_conn->c_sasl_sockctx = NULL;
} else {
op->o_conn->c_sasl_sockctx = op->o_conn->c_sasl_authctx;
}
ldap_pvt_thread_mutex_unlock( &op->o_conn->c_mutex );
}
/* Must send response using old security layer */
if (response.bv_len) rs->sr_sasldata = &response;
send_ldap_sasl( op, rs );
/* Now dispose of the old security layer.
*/
if ( ctx ) {
ldap_pvt_thread_mutex_lock( &op->o_conn->c_mutex );
ldap_pvt_sasl_remove( op->o_conn->c_sb );
op->o_conn->c_sasl_sockctx = op->o_conn->c_sasl_authctx;
ldap_pvt_thread_mutex_unlock( &op->o_conn->c_mutex );
sasl_dispose( &ctx );
}
} else if ( sc == SASL_CONTINUE ) {
rs->sr_err = LDAP_SASL_BIND_IN_PROGRESS,
rs->sr_sasldata = &response;
......@@ -1454,7 +1492,7 @@ slap_sasl_setpass( Operation *op, SlapReply *rs )
assert( ber_bvcmp( &slap_EXOP_MODIFY_PASSWD, &op->ore_reqoid ) == 0 );
rs->sr_err = sasl_getprop( op->o_conn->c_sasl_context, SASL_USERNAME,
rs->sr_err = sasl_getprop( op->o_conn->c_sasl_authctx, SASL_USERNAME,
(SASL_CONST void **)&id.bv_val );
if( rs->sr_err != SASL_OK ) {
......@@ -1492,13 +1530,13 @@ slap_sasl_setpass( Operation *op, SlapReply *rs )
}
#if SASL_VERSION_MAJOR < 2
rs->sr_err = sasl_setpass( op->o_conn->c_sasl_context,
rs->sr_err = sasl_setpass( op->o_conn->c_sasl_authctx,
id.bv_val, new.bv_val, new.bv_len, 0, &rs->sr_text );
#else
rs->sr_err = sasl_setpass( op->o_conn->c_sasl_context, id.bv_val,
rs->sr_err = sasl_setpass( op->o_conn->c_sasl_authctx, id.bv_val,
new.bv_val, new.bv_len, old.bv_val, old.bv_len, 0 );
if( rs->sr_err != SASL_OK ) {
rs->sr_text = sasl_errdetail( op->o_conn->c_sasl_context );
rs->sr_text = sasl_errdetail( op->o_conn->c_sasl_authctx );
}
#endif
switch(rs->sr_err) {
......
......@@ -2068,7 +2068,9 @@ typedef struct slap_conn {
int c_needs_tls_accept; /* true if SSL_accept should be called */
#endif
int c_sasl_layers; /* true if we need to install SASL i/o handlers */
void *c_sasl_context; /* SASL session context */
int c_sasl_done; /* SASL completed once */
void *c_sasl_authctx; /* SASL authentication context */
void *c_sasl_sockctx; /* SASL security layer context */
void *c_sasl_extra; /* SASL session extra stuff */
struct slap_op *c_sasl_bindop; /* set to current op if it's a bind */
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment