From 4b3d21146bfb1991be5be07b8ad059f262f80389 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Kuzn=C3=ADk?= Date: Thu, 24 May 2018 10:16:19 +0100 Subject: [PATCH] Introduce SASL support for upstream connections --- servers/lloadd/config.c | 4 - servers/lloadd/connection.c | 13 +- servers/lloadd/lload.h | 13 ++ servers/lloadd/upstream.c | 235 ++++++++++++++++++++++++++++++++++-- 4 files changed, 248 insertions(+), 17 deletions(-) diff --git a/servers/lloadd/config.c b/servers/lloadd/config.c index 80847ebf95..aa7738f8b3 100644 --- a/servers/lloadd/config.c +++ b/servers/lloadd/config.c @@ -1194,10 +1194,6 @@ config_bindconf( ConfigArgs *c ) Debug( LDAP_DEBUG_ANY, "config_bindconf: " "no sasl support available\n" ); return -1; -#else /* HAVE_CYRUS_SASL */ - Debug( LDAP_DEBUG_ANY, "config_bindconf: " - "no sasl support yet\n" ); - return -1; #endif } diff --git a/servers/lloadd/connection.c b/servers/lloadd/connection.c index 22d7969ec7..e8e805c467 100644 --- a/servers/lloadd/connection.c +++ b/servers/lloadd/connection.c @@ -36,9 +36,11 @@ #include #include -#include "lutil.h" #include "lload.h" +#include "lutil.h" +#include "lutil_ldap.h" + static ldap_pvt_thread_mutex_t conn_nextid_mutex; static unsigned long conn_nextid = 0; @@ -322,6 +324,15 @@ connection_destroy( LloadConnection *c ) ber_memfree( c->c_sasl_bind_mech.bv_val ); BER_BVZERO( &c->c_sasl_bind_mech ); } +#ifdef HAVE_CYRUS_SASL + if ( c->c_sasl_defaults ) { + lutil_sasl_freedefs( c->c_sasl_defaults ); + c->c_sasl_defaults = NULL; + } + if ( c->c_sasl_authctx ) { + sasl_dispose( &c->c_sasl_authctx ); + } +#endif /* HAVE_CYRUS_SASL */ CONNECTION_UNLOCK(c); diff --git a/servers/lloadd/lload.h b/servers/lloadd/lload.h index ecd6805170..d787a462d7 100644 --- a/servers/lloadd/lload.h +++ b/servers/lloadd/lload.h @@ -61,6 +61,14 @@ #include +#ifdef HAVE_CYRUS_SASL +#ifdef HAVE_SASL_SASL_H +#include +#else +#include +#endif +#endif /* HAVE_CYRUS_SASL */ + LDAP_BEGIN_DECL #ifdef SERVICE_NAME @@ -347,6 +355,11 @@ struct LloadConnection { unsigned long c_pin_id; +#ifdef HAVE_CYRUS_SASL + sasl_conn_t *c_sasl_authctx; + void *c_sasl_defaults; +#endif /* HAVE_CYRUS_SASL */ + #ifdef LDAP_API_FEATURE_VERIFY_CREDENTIALS struct berval c_vc_cookie; #endif /* LDAP_API_FEATURE_VERIFY_CREDENTIALS */ diff --git a/servers/lloadd/upstream.c b/servers/lloadd/upstream.c index d883a5564a..f19fb1f1f9 100644 --- a/servers/lloadd/upstream.c +++ b/servers/lloadd/upstream.c @@ -21,9 +21,23 @@ #include #include -#include "lutil.h" #include "lload.h" +#include "lutil.h" +#include "lutil_ldap.h" + +#ifdef HAVE_CYRUS_SASL +static const sasl_callback_t client_callbacks[] = { +#ifdef SASL_CB_GETREALM + { SASL_CB_GETREALM, NULL, NULL }, +#endif + { SASL_CB_USER, NULL, NULL }, + { SASL_CB_AUTHNAME, NULL, NULL }, + { SASL_CB_PASS, NULL, NULL }, + { SASL_CB_LIST_END, NULL, NULL } +}; +#endif /* HAVE_CYRUS_SASL */ + int forward_response( LloadConnection *client, LloadOperation *op, BerElement *ber ) { @@ -276,6 +290,135 @@ fail: return rc; } +#ifdef HAVE_CYRUS_SASL +static int +sasl_bind_step( LloadConnection *c, BerValue *scred, BerValue *ccred ) +{ + LloadBackend *b = c->c_private; + sasl_conn_t *ctx = c->c_sasl_authctx; + sasl_interact_t *prompts = NULL; + unsigned credlen; + int rc = -1; + + if ( !ctx ) { + const char *mech = NULL; + void *ssl; + + if ( sasl_client_new( "ldap", b->b_host, NULL, NULL, client_callbacks, + 0, &ctx ) != SASL_OK ) { + goto done; + } + c->c_sasl_authctx = ctx; + + assert( c->c_sasl_defaults == NULL ); + c->c_sasl_defaults = + lutil_sasl_defaults( NULL, bindconf.sb_saslmech.bv_val, + bindconf.sb_realm.bv_val, bindconf.sb_authcId.bv_val, + bindconf.sb_cred.bv_val, bindconf.sb_authzId.bv_val ); + +#ifdef HAVE_TLS + /* Check for TLS */ + ssl = ldap_pvt_tls_sb_ctx( c->c_sb ); + if ( ssl ) { + struct berval authid = BER_BVNULL; + ber_len_t ssf; + + ssf = ldap_pvt_tls_get_strength( ssl ); + (void)ldap_pvt_tls_get_my_dn( ssl, &authid, NULL, 0 ); + + sasl_setprop( ctx, SASL_SSF_EXTERNAL, &ssf ); + sasl_setprop( ctx, SASL_AUTH_EXTERNAL, authid.bv_val ); + ch_free( authid.bv_val ); +#ifdef SASL_CHANNEL_BINDING /* 2.1.25+ */ + { + char cbinding[64]; + struct berval cbv = { sizeof(cbinding), cbinding }; + if ( ldap_pvt_tls_get_unique( ssl, &cbv, 0 ) ) { + sasl_channel_binding_t *cb = + ch_malloc( sizeof(*cb) + cbv.bv_len ); + void *cb_data; + cb->name = "ldap"; + cb->critical = 0; + cb->len = cbv.bv_len; + cb->data = cb_data = cb + 1; + memcpy( cb_data, cbv.bv_val, cbv.bv_len ); + sasl_setprop( ctx, SASL_CHANNEL_BINDING, cb ); + } + } +#endif + } +#endif + +#if !defined(_WIN32) + /* Check for local */ + if ( b->b_proto == LDAP_PROTO_IPC ) { + char authid[sizeof( "gidNumber=4294967295+uidNumber=4294967295," + "cn=peercred,cn=external,cn=auth" )]; + int ssf = LDAP_PVT_SASL_LOCAL_SSF; + + sprintf( authid, + "gidNumber=%u+uidNumber=%u," + "cn=peercred,cn=external,cn=auth", + getegid(), geteuid() ); + sasl_setprop( ctx, SASL_SSF_EXTERNAL, &ssf ); + sasl_setprop( ctx, SASL_AUTH_EXTERNAL, authid ); + } +#endif + + do { + rc = sasl_client_start( ctx, bindconf.sb_saslmech.bv_val, + &prompts, + (const char **)&ccred->bv_val, &credlen, + &mech ); + + if ( rc == SASL_INTERACT ) { + if ( lutil_sasl_interact( NULL, LDAP_SASL_QUIET, + c->c_sasl_defaults, prompts ) ) { + break; + } + } + } while ( rc == SASL_INTERACT ); + + ber_str2bv( mech, 0, 0, &c->c_sasl_bind_mech ); + } else { + assert( c->c_sasl_defaults ); + + do { + rc = sasl_client_step( ctx, + (scred == NULL) ? NULL : scred->bv_val, + (scred == NULL) ? 0 : scred->bv_len, + &prompts, + (const char **)&ccred->bv_val, &credlen); + + if ( rc == SASL_INTERACT ) { + if ( lutil_sasl_interact( NULL, LDAP_SASL_QUIET, + c->c_sasl_defaults, prompts ) ) { + break; + } + } + } while ( rc == SASL_INTERACT ); + } + + if ( rc == SASL_OK ) { + sasl_ssf_t *ssf; + rc = sasl_getprop( ctx, SASL_SSF, (const void **)(char *)&ssf ); + if ( rc == SASL_OK && ssf && *ssf ) { + Debug( LDAP_DEBUG_CONNS, "sasl_bind_step: " + "connid=%lu mech=%s setting up a new SASL security layer\n", + c->c_connid, c->c_sasl_bind_mech.bv_val ); + ldap_pvt_sasl_install( c->c_sb, ctx ); + } + } + ccred->bv_len = credlen; + +done: + Debug( LDAP_DEBUG_TRACE, "sasl_bind_step: " + "connid=%lu next step for SASL bind mech=%s rc=%d\n", + c->c_connid, c->c_sasl_bind_mech.bv_val, rc ); + return rc; +} +#endif /* HAVE_CYRUS_SASL */ + int upstream_bind_cb( LloadConnection *c ) { @@ -308,7 +451,63 @@ upstream_bind_cb( LloadConnection *c ) } switch ( result ) { - case LDAP_SUCCESS: { + case LDAP_SUCCESS: +#ifdef HAVE_CYRUS_SASL + case LDAP_SASL_BIND_IN_PROGRESS: + if ( !BER_BVISNULL( &c->c_sasl_bind_mech ) ) { + BerValue scred = BER_BVNULL, ccred; + ber_len_t len; + int rc; + + CONNECTION_UNLOCK_INCREF(c); + if ( ber_peek_tag( ber, &len ) == LDAP_TAG_SASL_RES_CREDS && + ber_scanf( ber, "m", &scred ) == LBER_ERROR ) { + Debug( LDAP_DEBUG_ANY, "upstream_bind_cb: " + "sasl bind response malformed\n" ); + CONNECTION_LOCK_DECREF(c); + goto fail; + } + + rc = sasl_bind_step( c, &scred, &ccred ); + if ( rc != SASL_OK && + ( rc != SASL_CONTINUE || result == LDAP_SUCCESS ) ) { + CONNECTION_LOCK_DECREF(c); + goto fail; + } + + if ( result == LDAP_SASL_BIND_IN_PROGRESS ) { + BerElement *outber; + + ldap_pvt_thread_mutex_lock( &c->c_io_mutex ); + outber = c->c_pendingber; + if ( outber == NULL && (outber = ber_alloc()) == NULL ) { + ldap_pvt_thread_mutex_unlock( &c->c_io_mutex ); + CONNECTION_LOCK_DECREF(c); + goto fail; + } + c->c_pendingber = outber; + + msgid = c->c_next_msgid++; + ber_printf( outber, "{it{iOt{OON}N}}", + msgid, LDAP_REQ_BIND, LDAP_VERSION3, + &bindconf.sb_binddn, LDAP_AUTH_SASL, + &c->c_sasl_bind_mech, BER_BV_OPTIONAL( &ccred ) ); + ldap_pvt_thread_mutex_unlock( &c->c_io_mutex ); + + connection_write_cb( -1, 0, c ); + + CONNECTION_LOCK_DECREF(c); + if ( rc == SASL_OK ) { + BER_BVZERO( &c->c_sasl_bind_mech ); + } + break; + } + CONNECTION_LOCK_DECREF(c); + } + if ( result == LDAP_SASL_BIND_IN_PROGRESS ) { + goto fail; + } +#endif /* HAVE_CYRUS_SASL */ c->c_pdu_cb = handle_one_response; c->c_state = LLOAD_C_READY; c->c_type = LLOAD_C_OPEN; @@ -333,11 +532,7 @@ upstream_bind_cb( LloadConnection *c ) backend_retry( b ); ldap_pvt_thread_mutex_unlock( &b->b_mutex ); CONNECTION_LOCK_DECREF(c); - } break; -#ifdef HAVE_CYRUS_SASL - case LDAP_SASL_BIND_IN_PROGRESS: - /* TODO: fallthrough until we implement SASL */ -#endif /* HAVE_CYRUS_SASL */ + break; default: Debug( LDAP_DEBUG_ANY, "upstream_bind_cb: " "upstream bind failed, rc=%d, message='%s'\n", @@ -368,9 +563,7 @@ upstream_bind( void *ctx, void *arg ) ldap_pvt_thread_mutex_lock( &c->c_io_mutex ); ber = c->c_pendingber; if ( ber == NULL && (ber = ber_alloc()) == NULL ) { - ldap_pvt_thread_mutex_unlock( &c->c_io_mutex ); - CONNECTION_LOCK_DESTROY(c); - return NULL; + goto fail; } c->c_pendingber = ber; msgid = c->c_next_msgid++; @@ -384,11 +577,22 @@ upstream_bind( void *ctx, void *arg ) #ifdef HAVE_CYRUS_SASL } else { - BerValue cred = BER_BVNULL; + BerValue cred; + int rc; + + rc = sasl_bind_step( c, NULL, &cred ); + if ( rc != SASL_OK && rc != SASL_CONTINUE ) { + goto fail; + } + ber_printf( ber, "{it{iOt{OON}N}}", msgid, LDAP_REQ_BIND, LDAP_VERSION3, &bindconf.sb_binddn, LDAP_AUTH_SASL, - &bindconf.sb_saslmech, BER_BV_OPTIONAL( &cred ) ); + &c->c_sasl_bind_mech, BER_BV_OPTIONAL( &cred ) ); + + if ( rc == SASL_OK ) { + BER_BVZERO( &c->c_sasl_bind_mech ); + } #endif /* HAVE_CYRUS_SASL */ } ldap_pvt_thread_mutex_unlock( &c->c_io_mutex ); @@ -401,6 +605,11 @@ upstream_bind( void *ctx, void *arg ) CONNECTION_UNLOCK_OR_DESTROY(c); return NULL; + +fail: + ldap_pvt_thread_mutex_unlock( &c->c_io_mutex ); + CONNECTION_LOCK_DESTROY(c); + return NULL; } /* @@ -872,5 +1081,7 @@ upstream_destroy( LloadConnection *c ) CONNECTION_UNLOCK(c); return; } + + BER_BVZERO( &c->c_sasl_bind_mech ); connection_destroy( c ); }