X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;f=servers%2Fslapd%2Fback-ldap%2Fbind.c;h=9ef163c879d61cca0c537fed119f413947bfc518;hb=e76299c196bb93fd2e3ca7972260aa55f9c1a796;hp=ad268f8ba0e6235f9e3aa2ea9b2920d5c0afbe6c;hpb=e2d65fb69b732be4e4569baf00564247d60d0d81;p=openldap diff --git a/servers/slapd/back-ldap/bind.c b/servers/slapd/back-ldap/bind.c index ad268f8ba0..9ef163c879 100644 --- a/servers/slapd/back-ldap/bind.c +++ b/servers/slapd/back-ldap/bind.c @@ -2,7 +2,7 @@ /* $OpenLDAP$ */ /* This work is part of OpenLDAP Software . * - * Copyright 1999-2007 The OpenLDAP Foundation. + * Copyright 1999-2011 The OpenLDAP Foundation. * Portions Copyright 2000-2003 Pierangelo Masarati. * Portions Copyright 1999-2003 Howard Chu. * All rights reserved. @@ -32,14 +32,55 @@ #define AVL_INTERNAL #include "slap.h" #include "back-ldap.h" -#undef ldap_debug /* silence a warning in ldap-int.h */ -#include "../../../libraries/libldap/ldap-int.h" - +#include "lutil.h" #include "lutil_ldap.h" #define LDAP_CONTROL_OBSOLETE_PROXY_AUTHZ "2.16.840.1.113730.3.4.12" +#ifdef LDAP_DEVEL +#define SLAP_AUTH_DN 1 +#endif + #if LDAP_BACK_PRINT_CONNTREE > 0 + +static const struct { + slap_mask_t f; + char c; +} flagsmap[] = { + { LDAP_BACK_FCONN_ISBOUND, 'B' }, + { LDAP_BACK_FCONN_ISANON, 'A' }, + { LDAP_BACK_FCONN_ISPRIV, 'P' }, + { LDAP_BACK_FCONN_ISTLS, 'T' }, + { LDAP_BACK_FCONN_BINDING, 'X' }, + { LDAP_BACK_FCONN_TAINTED, 'E' }, + { LDAP_BACK_FCONN_ABANDON, 'N' }, + { LDAP_BACK_FCONN_ISIDASR, 'S' }, + { LDAP_BACK_FCONN_CACHED, 'C' }, + { 0, '\0' } +}; + +static void +ldap_back_conn_print( ldapconn_t *lc, const char *avlstr ) +{ + char buf[ SLAP_TEXT_BUFLEN ]; + char fbuf[ sizeof("BAPTIENSC") ]; + int i; + + ldap_back_conn2str( &lc->lc_base, buf, sizeof( buf ) ); + for ( i = 0; flagsmap[ i ].c != '\0'; i++ ) { + if ( lc->lc_lcflags & flagsmap[i].f ) { + fbuf[i] = flagsmap[i].c; + + } else { + fbuf[i] = '.'; + } + } + fbuf[i] = '\0'; + + fprintf( stderr, "lc=%p %s %s flags=0x%08x (%s)\n", + (void *)lc, buf, avlstr, lc->lc_lcflags, fbuf ); +} + static void ldap_back_ravl_print( Avlnode *root, int depth ) { @@ -57,13 +98,9 @@ ldap_back_ravl_print( Avlnode *root, int depth ) } lc = root->avl_data; - fprintf( stderr, "lc=%p local=\"%s\" conn=%p %s refcnt=%d flags=0x%08x\n", - (void *)lc, - lc->lc_local_ndn.bv_val ? lc->lc_local_ndn.bv_val : "", - (void *)lc->lc_conn, - avl_bf2str( root->avl_bf ), lc->lc_refcnt, lc->lc_lcflags ); - - ldap_back_ravl_print( root->avl_left, depth+1 ); + ldap_back_conn_print( lc, avl_bf2str( root->avl_bf ) ); + + ldap_back_ravl_print( root->avl_left, depth + 1 ); } static char* priv2str[] = { @@ -91,11 +128,8 @@ ldap_back_print_conntree( ldapinfo_t *li, char *msg ) LDAP_TAILQ_FOREACH( lc, &li->li_conn_priv[ c ].lic_priv, lc_q ) { - fprintf( stderr, " [%d] lc=%p local=\"%s\" conn=%p refcnt=%d flags=0x%08x\n", - i, - (void *)lc, - lc->lc_local_ndn.bv_val ? lc->lc_local_ndn.bv_val : "", - (void *)lc->lc_conn, lc->lc_refcnt, lc->lc_lcflags ); + fprintf( stderr, " [%d] ", i ); + ldap_back_conn_print( lc, "" ); i++; } } @@ -112,7 +146,7 @@ ldap_back_print_conntree( ldapinfo_t *li, char *msg ) #endif /* LDAP_BACK_PRINT_CONNTREE */ static int -ldap_back_freeconn( Operation *op, ldapconn_t *lc, int dolock ); +ldap_back_freeconn( ldapinfo_t *li, ldapconn_t *lc, int dolock ); static ldapconn_t * ldap_back_getconn( Operation *op, SlapReply *rs, ldap_back_send_t sendok, @@ -133,16 +167,63 @@ ldap_back_prepare_conn( ldapconn_t *lc, Operation *op, SlapReply *rs, static int ldap_back_conndnlc_cmp( const void *c1, const void *c2 ); +ldapconn_t * +ldap_back_conn_delete( ldapinfo_t *li, ldapconn_t *lc ) +{ + if ( LDAP_BACK_PCONN_ISPRIV( lc ) ) { + if ( LDAP_BACK_CONN_CACHED( lc ) ) { + assert( lc->lc_q.tqe_prev != NULL ); + assert( li->li_conn_priv[ LDAP_BACK_CONN2PRIV( lc ) ].lic_num > 0 ); + li->li_conn_priv[ LDAP_BACK_CONN2PRIV( lc ) ].lic_num--; + LDAP_TAILQ_REMOVE( &li->li_conn_priv[ LDAP_BACK_CONN2PRIV( lc ) ].lic_priv, lc, lc_q ); + LDAP_TAILQ_ENTRY_INIT( lc, lc_q ); + LDAP_BACK_CONN_CACHED_CLEAR( lc ); + + } else { + assert( LDAP_BACK_CONN_TAINTED( lc ) ); + assert( lc->lc_q.tqe_prev == NULL ); + } + + } else { + ldapconn_t *tmplc = NULL; + + if ( LDAP_BACK_CONN_CACHED( lc ) ) { + assert( !LDAP_BACK_CONN_TAINTED( lc ) ); + tmplc = avl_delete( &li->li_conninfo.lai_tree, (caddr_t)lc, + ldap_back_conndnlc_cmp ); + assert( tmplc == lc ); + LDAP_BACK_CONN_CACHED_CLEAR( lc ); + } + + assert( LDAP_BACK_CONN_TAINTED( lc ) || tmplc == lc ); + } + + return lc; +} + int ldap_back_bind( Operation *op, SlapReply *rs ) { ldapinfo_t *li = (ldapinfo_t *) op->o_bd->be_private; ldapconn_t *lc; - int rc = 0; + LDAPControl **ctrls = NULL; + struct berval save_o_dn; + int save_o_do_not_cache, + rc = 0; ber_int_t msgid; ldap_back_send_t retrying = LDAP_BACK_RETRYING; + /* allow rootdn as a means to auth without the need to actually + * contact the proxied DSA */ + switch ( be_rootdn_bind( op, rs ) ) { + case SLAP_CB_CONTINUE: + break; + + default: + return rs->sr_err; + } + lc = ldap_back_getconn( op, rs, LDAP_BACK_BIND_SERR, NULL, NULL ); if ( !lc ) { return rs->sr_err; @@ -161,11 +242,27 @@ ldap_back_bind( Operation *op, SlapReply *rs ) } LDAP_BACK_CONN_ISBOUND_CLEAR( lc ); + /* don't add proxyAuthz; set the bindDN */ + save_o_dn = op->o_dn; + save_o_do_not_cache = op->o_do_not_cache; + op->o_dn = op->o_req_dn; + op->o_do_not_cache = 1; + + ctrls = op->o_ctrls; + rc = ldap_back_controls_add( op, rs, lc, &ctrls ); + op->o_dn = save_o_dn; + op->o_do_not_cache = save_o_do_not_cache; + if ( rc != LDAP_SUCCESS ) { + send_ldap_result( op, rs ); + ldap_back_release_conn( li, lc ); + return( rc ); + } + retry:; /* method is always LDAP_AUTH_SIMPLE if we got here */ rs->sr_err = ldap_sasl_bind( lc->lc_ld, op->o_req_dn.bv_val, LDAP_SASL_SIMPLE, - &op->orb_cred, op->o_ctrls, NULL, &msgid ); + &op->orb_cred, ctrls, NULL, &msgid ); /* FIXME: should we always retry, or only when piping the bind * in the "override" connection pool? */ rc = ldap_back_op_result( lc, op, rs, msgid, @@ -178,7 +275,11 @@ retry:; } } + ldap_back_controls_free( op, rs, &ctrls ); + if ( rc == LDAP_SUCCESS ) { + op->o_conn->c_authz_cookie = op->o_bd->be_private; + /* If defined, proxyAuthz will be used also when * back-ldap is the authorizing backend; for this * purpose, after a successful bind the connection @@ -187,7 +288,7 @@ retry:; * connection with identity assertion */ /* NOTE: use with care */ if ( li->li_idassert_flags & LDAP_BACK_AUTH_OVERRIDE ) { - ldap_back_release_conn( op, rs, lc ); + ldap_back_release_conn( li, lc ); return( rc ); } @@ -232,35 +333,16 @@ retry_lock:; #endif /* LDAP_BACK_PRINT_CONNTREE */ assert( lc->lc_refcnt == 1 ); - if ( LDAP_BACK_PCONN_ISPRIV( lc ) ) { - /* this can happen, for example, if the bind fails - * for some reason... */ - if ( lc->lc_q.tqe_prev != NULL ) { - assert( LDAP_BACK_CONN_CACHED( lc ) ); - assert( li->li_conn_priv[ LDAP_BACK_CONN2PRIV( lc ) ].lic_num > 0 ); - LDAP_TAILQ_REMOVE( &li->li_conn_priv[ LDAP_BACK_CONN2PRIV( lc ) ].lic_priv, lc, lc_q ); - li->li_conn_priv[ LDAP_BACK_CONN2PRIV( lc ) ].lic_num--; - lc->lc_q.tqe_prev = NULL; - lc->lc_q.tqe_next = NULL; - - } else { - assert( !LDAP_BACK_CONN_CACHED( lc ) ); - } - - } else { - tmplc = avl_delete( &li->li_conninfo.lai_tree, (caddr_t)lc, - ldap_back_conndnlc_cmp ); - assert( ( LDAP_BACK_CONN_TAINTED( lc ) && tmplc == NULL ) || lc == tmplc ); - } - LDAP_BACK_CONN_CACHED_CLEAR( lc ); + ldap_back_conn_delete( li, lc ); /* delete all cached connections with the current connection */ if ( LDAP_BACK_SINGLECONN( li ) ) { while ( ( tmplc = avl_delete( &li->li_conninfo.lai_tree, (caddr_t)lc, ldap_back_conn_cmp ) ) != NULL ) { + assert( !LDAP_BACK_PCONN_ISPRIV( lc ) ); Debug( LDAP_DEBUG_TRACE, - "=>ldap_back_bind: destroying conn %ld (refcnt=%u)\n", - LDAP_BACK_PCONN_ID( lc ), lc->lc_refcnt, 0 ); + "=>ldap_back_bind: destroying conn %lu (refcnt=%u)\n", + lc->lc_conn->c_connid, lc->lc_refcnt, 0 ); if ( tmplc->lc_refcnt != 0 ) { /* taint it */ @@ -307,7 +389,7 @@ retry_lock:; } if ( lc != NULL ) { - ldap_back_release_conn( op, rs, lc ); + ldap_back_release_conn( li, lc ); } return( rc ); @@ -409,10 +491,8 @@ ldap_back_conndn_dup( void *c1, void *c2 ) } static int -ldap_back_freeconn( Operation *op, ldapconn_t *lc, int dolock ) +ldap_back_freeconn( ldapinfo_t *li, ldapconn_t *lc, int dolock ) { - ldapinfo_t *li = (ldapinfo_t *) op->o_bd->be_private; - if ( dolock ) { ldap_pvt_thread_mutex_lock( &li->li_conninfo.lai_mutex ); } @@ -421,31 +501,7 @@ ldap_back_freeconn( Operation *op, ldapconn_t *lc, int dolock ) ldap_back_print_conntree( li, ">>> ldap_back_freeconn" ); #endif /* LDAP_BACK_PRINT_CONNTREE */ - if ( LDAP_BACK_PCONN_ISPRIV( lc ) ) { - if ( lc->lc_q.tqe_prev != NULL ) { - assert( LDAP_BACK_CONN_CACHED( lc ) ); - assert( li->li_conn_priv[ LDAP_BACK_CONN2PRIV( lc ) ].lic_num > 0 ); - li->li_conn_priv[ LDAP_BACK_CONN2PRIV( lc ) ].lic_num--; - LDAP_TAILQ_REMOVE( &li->li_conn_priv[ LDAP_BACK_CONN2PRIV( lc ) ].lic_priv, lc, lc_q ); - LDAP_BACK_CONN_CACHED_CLEAR( lc ); - - } else { - assert( !LDAP_BACK_CONN_CACHED( lc ) ); - } - lc->lc_q.tqe_prev = NULL; - lc->lc_q.tqe_next = NULL; - - } else { - ldapconn_t *tmplc = NULL; - - if ( LDAP_BACK_CONN_CACHED( lc ) ) { - tmplc = avl_delete( &li->li_conninfo.lai_tree, (caddr_t)lc, - ldap_back_conndnlc_cmp ); - assert( tmplc == lc ); - LDAP_BACK_CONN_CACHED_CLEAR( lc ); - } - assert( LDAP_BACK_CONN_TAINTED( lc ) || tmplc == lc ); - } + (void)ldap_back_conn_delete( li, lc ); if ( lc->lc_refcnt == 0 ) { ldap_back_conn_free( (void *)lc ); @@ -529,12 +585,13 @@ retry:; rc = ldap_parse_extended_result( ld, res, NULL, &data, 0 ); if ( rc == LDAP_SUCCESS ) { - int err; - rc = ldap_parse_result( ld, res, &err, + SlapReply rs; + rc = ldap_parse_result( ld, res, &rs.sr_err, NULL, NULL, NULL, NULL, 1 ); - if ( rc == LDAP_SUCCESS ) { - rc = err; + if ( rc != LDAP_SUCCESS ) { + rs.sr_err = rc; } + rc = slap_map_api2result( &rs ); res = NULL; /* FIXME: in case a referral @@ -612,7 +669,8 @@ ldap_back_prepare_conn( ldapconn_t *lc, Operation *op, SlapReply *rs, ldap_back_ LDAP *ld = NULL; #ifdef HAVE_TLS int is_tls = op->o_conn->c_is_tls; - time_t lc_time = (time_t)(-1); + int flags = li->li_flags; + time_t lctime = (time_t)(-1); slap_bindconf *sb; #endif /* HAVE_TLS */ @@ -656,7 +714,12 @@ ldap_back_prepare_conn( ldapconn_t *lc, Operation *op, SlapReply *rs, ldap_back_ #ifdef HAVE_TLS if ( LDAP_BACK_CONN_ISPRIV( lc ) ) { - sb = &li->li_acl; + /* See "rationale" comment in ldap_back_getconn() */ + if ( li->li_acl_authmethod == LDAP_AUTH_NONE && + li->li_idassert_authmethod != LDAP_AUTH_NONE ) + sb = &li->li_idassert.si_bc; + else + sb = &li->li_acl; } else if ( LDAP_BACK_CONN_ISIDASSERT( lc ) ) { sb = &li->li_idassert.si_bc; @@ -671,17 +734,28 @@ ldap_back_prepare_conn( ldapconn_t *lc, Operation *op, SlapReply *rs, ldap_back_ ldap_set_option( ld, LDAP_OPT_X_TLS_CTX, sb->sb_tls_ctx ); } + /* if required by the bindconf configuration, force TLS */ + if ( ( sb == &li->li_acl || sb == &li->li_idassert.si_bc ) && + sb->sb_tls_ctx ) + { + flags |= LDAP_BACK_F_USE_TLS; + } + ldap_pvt_thread_mutex_lock( &li->li_uri_mutex ); + assert( li->li_uri_mutex_do_not_lock == 0 ); + li->li_uri_mutex_do_not_lock = 1; rs->sr_err = ldap_back_start_tls( ld, op->o_protocol, &is_tls, - li->li_uri, li->li_flags, li->li_nretries, &rs->sr_text ); + li->li_uri, flags, li->li_nretries, &rs->sr_text ); + li->li_uri_mutex_do_not_lock = 0; ldap_pvt_thread_mutex_unlock( &li->li_uri_mutex ); if ( rs->sr_err != LDAP_SUCCESS ) { ldap_unbind_ext( ld, NULL, NULL ); + rs->sr_text = "Start TLS failed"; goto error_return; } else if ( li->li_idle_timeout ) { /* only touch when activity actually took place... */ - lc_time = op->o_time; + lctime = op->o_time; } #endif /* HAVE_TLS */ @@ -693,8 +767,8 @@ ldap_back_prepare_conn( ldapconn_t *lc, Operation *op, SlapReply *rs, ldap_back_ } else { LDAP_BACK_CONN_ISTLS_CLEAR( lc ); } - if ( lc_time != (time_t)(-1) ) { - lc->lc_time = lc_time; + if ( lctime != (time_t)(-1) ) { + lc->lc_time = lctime; } #endif /* HAVE_TLS */ @@ -703,10 +777,9 @@ error_return:; rs->sr_err = slap_map_api2result( rs ); if ( sendok & LDAP_BACK_SENDERR ) { if ( rs->sr_text == NULL ) { - rs->sr_text = "ldap_initialize() failed"; + rs->sr_text = "Proxy connection initialization failed"; } send_ldap_result( op, rs ); - rs->sr_text = NULL; } } else { @@ -728,7 +801,7 @@ ldap_back_getconn( { ldapinfo_t *li = (ldapinfo_t *)op->o_bd->be_private; ldapconn_t *lc = NULL, - lc_curr = { 0 }; + lc_curr = {{ 0 }}; int refcnt = 1, lookupconn = !( sendok & LDAP_BACK_BINDING ); @@ -759,6 +832,7 @@ ldap_back_getconn( if ( dont_retry ) { rs->sr_err = LDAP_UNAVAILABLE; if ( op->o_conn && ( sendok & LDAP_BACK_SENDERR ) ) { + rs->sr_text = "Target is quarantined"; send_ldap_result( op, rs ); } return NULL; @@ -792,13 +866,13 @@ ldap_back_getconn( op->o_ndn = op->o_req_ndn; } isproxyauthz = ldap_back_is_proxy_authz( op, rs, sendok, binddn, bindcred ); - if ( isproxyauthz == -1 ) { - return NULL; - } if ( op->o_tag == LDAP_REQ_BIND ) { op->o_dn = save_o_dn; op->o_ndn = save_o_ndn; } + if ( isproxyauthz == -1 ) { + return NULL; + } lc_curr.lc_local_ndn = op->o_ndn; /* Explicit binds must not be shared; @@ -849,8 +923,7 @@ retry_lock: { LDAP_TAILQ_REMOVE( &li->li_conn_priv[ LDAP_BACK_CONN2PRIV( lc ) ].lic_priv, lc, lc_q ); - lc->lc_q.tqe_prev = NULL; - lc->lc_q.tqe_next = NULL; + LDAP_TAILQ_ENTRY_INIT( lc, lc_q ); LDAP_TAILQ_INSERT_TAIL( &li->li_conn_priv[ LDAP_BACK_CONN2PRIV( lc ) ].lic_priv, lc, lc_q ); } @@ -912,10 +985,10 @@ retry_lock: /* * the rationale is: connections as the rootdn are privileged, - * so acl_authcDN is to be used; however, in some cases + * so li_acl is to be used; however, in some cases * one already configured identity assertion with a highly - * privileged idassert_authcDN, so if acl_authcDN is NULL - * and idassert_authcDN is not, use the second instead. + * privileged idassert_authcDN, so if li_acl is not configured + * and idassert is, use idassert instead. * * might change in the future, because it's preferable * to make clear what identity is being used, since @@ -923,7 +996,8 @@ retry_lock: * the same identity twice... */ if ( LDAP_BACK_CONN_ISPRIV( &lc_curr ) ) { - if ( BER_BVISNULL( &li->li_acl_authcDN ) && !BER_BVISNULL( &li->li_idassert_authcDN ) ) { + if ( li->li_acl_authmethod == LDAP_AUTH_NONE && + li->li_idassert_authmethod != LDAP_AUTH_NONE ) { ber_dupbv( &lc->lc_bound_ndn, &li->li_idassert_authcDN ); ber_dupbv( &lc->lc_cred, &li->li_idassert_passwd ); @@ -1053,10 +1127,9 @@ retry_lock: LDAP_BACK_CONN_CACHED_CLEAR( lc ); ldap_back_conn_free( lc ); rs->sr_err = LDAP_OTHER; - rs->sr_text = "proxy bind collision"; + rs->sr_text = "Proxy bind collision"; if ( op->o_conn && ( sendok & LDAP_BACK_SENDERR ) ) { send_ldap_result( op, rs ); - rs->sr_text = NULL; } return NULL; } @@ -1078,26 +1151,8 @@ retry_lock: ldap_back_print_conntree( li, ">>> ldap_back_getconn(timeout)" ); #endif /* LDAP_BACK_PRINT_CONNTREE */ - if ( LDAP_BACK_PCONN_ISPRIV( lc ) ) { - if ( lc->lc_q.tqe_prev != NULL ) { - assert( LDAP_BACK_CONN_CACHED( lc ) ); - assert( li->li_conn_priv[ LDAP_BACK_CONN2PRIV( lc ) ].lic_num > 0 ); - LDAP_TAILQ_REMOVE( &li->li_conn_priv[ LDAP_BACK_CONN2PRIV( lc ) ].lic_priv, - lc, lc_q ); - li->li_conn_priv[ LDAP_BACK_CONN2PRIV( lc ) ].lic_num--; - lc->lc_q.tqe_prev = NULL; - lc->lc_q.tqe_next = NULL; - - } else { - assert( !LDAP_BACK_CONN_CACHED( lc ) ); - } - - } else { - (void)avl_delete( &li->li_conninfo.lai_tree, (caddr_t)lc, - ldap_back_conndnlc_cmp ); - } + (void)ldap_back_conn_delete( li, lc ); LDAP_BACK_CONN_TAINTED_SET( lc ); - LDAP_BACK_CONN_CACHED_CLEAR( lc ); #if LDAP_BACK_PRINT_CONNTREE > 0 ldap_back_print_conntree( li, "<<< ldap_back_getconn(timeout)" ); @@ -1110,8 +1165,9 @@ retry_lock: char buf[ SLAP_TEXT_BUFLEN ]; snprintf( buf, sizeof( buf ), - "conn %p fetched refcnt=%u %s", - (void *)lc, refcnt, expiring ? " expiring" : "" ); + "conn %p fetched refcnt=%u%s", + (void *)lc, refcnt, + expiring ? " expiring" : "" ); Debug( LDAP_DEBUG_TRACE, "=>ldap_back_getconn: %s.\n", buf, 0, 0 ); } @@ -1126,12 +1182,10 @@ done:; void ldap_back_release_conn_lock( - Operation *op, - SlapReply *rs, + ldapinfo_t *li, ldapconn_t **lcp, int dolock ) { - ldapinfo_t *li = (ldapinfo_t *)op->o_bd->be_private; ldapconn_t *lc = *lcp; @@ -1142,7 +1196,7 @@ ldap_back_release_conn_lock( LDAP_BACK_CONN_BINDING_CLEAR( lc ); lc->lc_refcnt--; if ( LDAP_BACK_CONN_TAINTED( lc ) ) { - ldap_back_freeconn( op, lc, 0 ); + ldap_back_freeconn( li, lc, 0 ); *lcp = NULL; } if ( dolock ) { @@ -1221,6 +1275,19 @@ done:; ldap_pvt_thread_mutex_unlock( &li->li_quarantine_mutex ); } +static int +ldap_back_dobind_cb( + Operation *op, + SlapReply *rs +) +{ + ber_tag_t *tptr = op->o_callback->sc_private; + op->o_tag = *tptr; + rs->sr_tag = slap_req2res( op->o_tag ); + + return SLAP_CB_CONTINUE; +} + /* * ldap_back_dobind_int * @@ -1245,6 +1312,9 @@ ldap_back_dobind_int( isbound, binding = 0; ber_int_t msgid; + ber_tag_t o_tag = op->o_tag; + slap_callback cb = {0}; + char *tmp_dn; assert( lcp != NULL ); assert( retries >= 0 ); @@ -1316,10 +1386,16 @@ retry_lock:; * then bind as the asserting identity and explicitly * add the proxyAuthz control to every operation with the * dn bound to the connection as control value. - * This is done also if this is the authrizing backend, + * This is done also if this is the authorizing backend, * but the "override" flag is given to idassert. * It allows to use SASL bind and yet proxyAuthz users */ + op->o_tag = LDAP_REQ_BIND; + cb.sc_next = op->o_callback; + cb.sc_private = &o_tag; + cb.sc_response = ldap_back_dobind_cb; + op->o_callback = &cb; + if ( LDAP_BACK_CONN_ISIDASSERT( lc ) ) { if ( BER_BVISEMPTY( &binddn ) && BER_BVISEMPTY( &bindcred ) ) { /* if we got here, it shouldn't return result */ @@ -1332,47 +1408,71 @@ retry_lock:; } #ifdef HAVE_CYRUS_SASL - if ( LDAP_BACK_CONN_ISPRIV( lc ) - && li->li_acl_authmethod == LDAP_AUTH_SASL ) - { + if ( LDAP_BACK_CONN_ISPRIV( lc )) { + slap_bindconf *sb; + if ( li->li_acl_authmethod != LDAP_AUTH_NONE ) + sb = &li->li_acl; + else + sb = &li->li_idassert.si_bc; + + if ( sb->sb_method == LDAP_AUTH_SASL ) { void *defaults = NULL; - if ( li->li_acl_secprops != NULL ) { + if ( sb->sb_secprops != NULL ) { rc = ldap_set_option( lc->lc_ld, - LDAP_OPT_X_SASL_SECPROPS, li->li_acl_secprops ); + LDAP_OPT_X_SASL_SECPROPS, sb->sb_secprops ); if ( rc != LDAP_OPT_SUCCESS ) { Debug( LDAP_DEBUG_ANY, "Error: ldap_set_option " "(SECPROPS,\"%s\") failed!\n", - li->li_acl_secprops, 0, 0 ); + sb->sb_secprops, 0, 0 ); goto done; } } defaults = lutil_sasl_defaults( lc->lc_ld, - li->li_acl_sasl_mech.bv_val, - li->li_acl_sasl_realm.bv_val, - li->li_acl_authcID.bv_val, - li->li_acl_passwd.bv_val, + sb->sb_saslmech.bv_val, + sb->sb_realm.bv_val, + sb->sb_authcId.bv_val, + sb->sb_cred.bv_val, NULL ); + if ( defaults == NULL ) { + rs->sr_err = LDAP_OTHER; + LDAP_BACK_CONN_ISBOUND_CLEAR( lc ); + if ( sendok & LDAP_BACK_SENDERR ) { + send_ldap_result( op, rs ); + } + goto done; + } rs->sr_err = ldap_sasl_interactive_bind_s( lc->lc_ld, - li->li_acl_authcDN.bv_val, - li->li_acl_sasl_mech.bv_val, NULL, NULL, + sb->sb_binddn.bv_val, + sb->sb_saslmech.bv_val, NULL, NULL, LDAP_SASL_QUIET, lutil_sasl_interact, defaults ); lutil_sasl_freedefs( defaults ); - rs->sr_err = slap_map_api2result( rs ); - if ( rs->sr_err != LDAP_SUCCESS ) { + switch ( rs->sr_err ) { + case LDAP_SUCCESS: + LDAP_BACK_CONN_ISBOUND_SET( lc ); + break; + + case LDAP_LOCAL_ERROR: + /* list client API error codes that require + * to taint the connection */ + /* FIXME: should actually retry? */ + LDAP_BACK_CONN_TAINTED_SET( lc ); + + /* fallthru */ + + default: LDAP_BACK_CONN_ISBOUND_CLEAR( lc ); + rs->sr_err = slap_map_api2result( rs ); if ( sendok & LDAP_BACK_SENDERR ) { send_ldap_result( op, rs ); } - - } else { - LDAP_BACK_CONN_ISBOUND_SET( lc ); + break; } if ( LDAP_BACK_QUARANTINE( li ) ) { @@ -1381,11 +1481,22 @@ retry_lock:; goto done; } + } #endif /* HAVE_CYRUS_SASL */ retry:; + if ( BER_BVISNULL( &lc->lc_cred ) ) { + tmp_dn = ""; + if ( !BER_BVISNULL( &lc->lc_bound_ndn ) && !BER_BVISEMPTY( &lc->lc_bound_ndn ) ) { + Debug( LDAP_DEBUG_ANY, "%s ldap_back_dobind_int: DN=\"%s\" without creds, binding anonymously", + op->o_log_prefix, lc->lc_bound_ndn.bv_val, 0 ); + } + + } else { + tmp_dn = lc->lc_bound_ndn.bv_val; + } rs->sr_err = ldap_sasl_bind( lc->lc_ld, - BER_BVISNULL( &lc->lc_cred ) ? "" : lc->lc_bound_ndn.bv_val, + tmp_dn, LDAP_SASL_SIMPLE, &lc->lc_cred, NULL, NULL, &msgid ); @@ -1403,6 +1514,7 @@ retry:; /* lc here must be the regular lc, reset and ready for init */ rs->sr_err = ldap_back_prepare_conn( lc, op, rs, sendok ); if ( rs->sr_err != LDAP_SUCCESS ) { + sendok &= ~LDAP_BACK_SENDERR; lc->lc_refcnt = 0; } } @@ -1417,19 +1529,11 @@ retry:; } goto retry; } - - } else { - if ( dolock ) { - ldap_pvt_thread_mutex_lock( &li->li_conninfo.lai_mutex ); - } - if ( dolock ) { - ldap_pvt_thread_mutex_unlock( &li->li_conninfo.lai_mutex ); - } } assert( lc->lc_refcnt == 1 ); lc->lc_refcnt = 0; - ldap_back_freeconn( op, lc, dolock ); + ldap_back_freeconn( li, lc, dolock ); *lcp = NULL; rs->sr_err = slap_map_api2result( rs ); @@ -1440,15 +1544,21 @@ retry:; if ( rs->sr_err != LDAP_SUCCESS && ( sendok & LDAP_BACK_SENDERR ) ) { + if ( op->o_callback == &cb ) + op->o_callback = cb.sc_next; + op->o_tag = o_tag; + rs->sr_text = "Proxy can't contact remote server"; send_ldap_result( op, rs ); } - return 0; + rc = 0; + goto func_leave; } rc = ldap_back_op_result( lc, op, rs, msgid, - -1, (sendok|LDAP_BACK_BINDING) ); + -1, ( sendok | LDAP_BACK_BINDING ) ); if ( rc == LDAP_SUCCESS ) { + op->o_conn->c_authz_cookie = op->o_bd->be_private; LDAP_BACK_CONN_ISBOUND_SET( lc ); } @@ -1456,12 +1566,17 @@ done:; LDAP_BACK_CONN_BINDING_CLEAR( lc ); rc = LDAP_BACK_CONN_ISBOUND( lc ); if ( !rc ) { - ldap_back_release_conn_lock( op, rs, lcp, dolock ); + ldap_back_release_conn_lock( li, lcp, dolock ); } else if ( LDAP_BACK_SAVECRED( li ) ) { ldap_set_rebind_proc( lc->lc_ld, li->li_rebind_f, lc ); } +func_leave:; + if ( op->o_callback == &cb ) + op->o_callback = cb.sc_next; + op->o_tag = o_tag; + return rc; } @@ -1539,13 +1654,19 @@ ldap_back_default_urllist( *urllist = *url; *url = NULL; - ldap_pvt_thread_mutex_lock( &li->li_uri_mutex ); + if ( !li->li_uri_mutex_do_not_lock ) { + ldap_pvt_thread_mutex_lock( &li->li_uri_mutex ); + } + if ( li->li_uri ) { ch_free( li->li_uri ); } ldap_get_option( ld, LDAP_OPT_URI, (void *)&li->li_uri ); - ldap_pvt_thread_mutex_unlock( &li->li_uri_mutex ); + + if ( !li->li_uri_mutex_do_not_lock ) { + ldap_pvt_thread_mutex_unlock( &li->li_uri_mutex ); + } return LDAP_SUCCESS; } @@ -1595,8 +1716,6 @@ ldap_back_op_result( char **refs = NULL; LDAPControl **ctrls = NULL; -#define ERR_OK(err) ((err) == LDAP_SUCCESS || (err) == LDAP_COMPARE_FALSE || (err) == LDAP_COMPARE_TRUE) - rs->sr_text = NULL; rs->sr_matched = NULL; rs->sr_ref = NULL; @@ -1605,7 +1724,7 @@ ldap_back_op_result( /* if the error recorded in the reply corresponds * to a successful state, get the error from the * remote server response */ - if ( ERR_OK( rs->sr_err ) ) { + if ( LDAP_ERR_OK( rs->sr_err ) ) { int rc; struct timeval tv; LDAPMessage *res = NULL; @@ -1659,8 +1778,23 @@ retry:; if ( sendok & LDAP_BACK_BINDING ) { ldap_unbind_ext( lc->lc_ld, NULL, NULL ); lc->lc_ld = NULL; + + /* let it be used, but taint/delete it so that + * no-one else can look it up any further */ + ldap_pvt_thread_mutex_lock( &li->li_conninfo.lai_mutex ); + +#if LDAP_BACK_PRINT_CONNTREE > 0 + ldap_back_print_conntree( li, ">>> ldap_back_getconn(timeout)" ); +#endif /* LDAP_BACK_PRINT_CONNTREE */ + + (void)ldap_back_conn_delete( li, lc ); LDAP_BACK_CONN_TAINTED_SET( lc ); +#if LDAP_BACK_PRINT_CONNTREE > 0 + ldap_back_print_conntree( li, "<<< ldap_back_getconn(timeout)" ); +#endif /* LDAP_BACK_PRINT_CONNTREE */ + ldap_pvt_thread_mutex_unlock( &li->li_conninfo.lai_mutex ); + } else { (void)ldap_back_cancel( lc, op, rs, msgid, sendok ); } @@ -1692,22 +1826,50 @@ retry:; rc = ldap_parse_result( lc->lc_ld, res, &rs->sr_err, &match, &text, &refs, &ctrls, 1 ); - rs->sr_text = text; - if ( rc != LDAP_SUCCESS ) { + if ( rc == LDAP_SUCCESS ) { + rs->sr_text = text; + } else { rs->sr_err = rc; } - if ( refs != NULL ) { - int i; - - for ( i = 0; refs[ i ] != NULL; i++ ) - /* count */ ; - rs->sr_ref = op->o_tmpalloc( sizeof( struct berval ) * ( i + 1 ), - op->o_tmpmemctx ); - for ( i = 0; refs[ i ] != NULL; i++ ) { - ber_str2bv( refs[ i ], 0, 0, &rs->sr_ref[ i ] ); + rs->sr_err = slap_map_api2result( rs ); + + /* RFC 4511: referrals can only appear + * if result code is LDAP_REFERRAL */ + if ( refs != NULL + && refs[ 0 ] != NULL + && refs[ 0 ][ 0 ] != '\0' ) + { + if ( rs->sr_err != LDAP_REFERRAL ) { + Debug( LDAP_DEBUG_ANY, + "%s ldap_back_op_result: " + "got referrals with err=%d\n", + op->o_log_prefix, + rs->sr_err, 0 ); + + } else { + int i; + + for ( i = 0; refs[ i ] != NULL; i++ ) + /* count */ ; + rs->sr_ref = op->o_tmpalloc( sizeof( struct berval ) * ( i + 1 ), + op->o_tmpmemctx ); + for ( i = 0; refs[ i ] != NULL; i++ ) { + ber_str2bv( refs[ i ], 0, 0, &rs->sr_ref[ i ] ); + } + BER_BVZERO( &rs->sr_ref[ i ] ); } - BER_BVZERO( &rs->sr_ref[ i ] ); + + } else if ( rs->sr_err == LDAP_REFERRAL ) { + Debug( LDAP_DEBUG_ANY, + "%s ldap_back_op_result: " + "got err=%d with null " + "or empty referrals\n", + op->o_log_prefix, + rs->sr_err, 0 ); + + rs->sr_err = LDAP_NO_SUCH_OBJECT; } + if ( ctrls != NULL ) { rs->sr_ctrls = ctrls; } @@ -1717,7 +1879,7 @@ retry:; /* if the error in the reply structure is not * LDAP_SUCCESS, try to map it from client * to server error */ - if ( !ERR_OK( rs->sr_err ) ) { + if ( !LDAP_ERR_OK( rs->sr_err ) ) { rs->sr_err = slap_map_api2result( rs ); /* internal ops ( op->o_conn == NULL ) @@ -1736,44 +1898,80 @@ retry:; ldap_back_quarantine( op, rs ); } if ( op->o_conn && ( sendok & LDAP_BACK_SENDERR ) ) { + if ( rs->sr_text == NULL ) rs->sr_text = "Proxy operation retry failed"; send_ldap_result( op, rs ); } } } else if ( op->o_conn && - ( ( ( sendok & LDAP_BACK_SENDOK ) && ERR_OK( rs->sr_err ) ) - || ( ( sendok & LDAP_BACK_SENDERR ) && rs->sr_err != LDAP_SUCCESS ) ) ) + ( ( ( sendok & LDAP_BACK_SENDOK ) && LDAP_ERR_OK( rs->sr_err ) ) + || ( ( sendok & LDAP_BACK_SENDERR ) && !LDAP_ERR_OK( rs->sr_err ) ) ) ) { send_ldap_result( op, rs ); } - if ( match ) { - if ( rs->sr_matched != match ) { - free( (char *)rs->sr_matched ); - } - rs->sr_matched = NULL; - ldap_memfree( match ); - } - if ( text ) { ldap_memfree( text ); } rs->sr_text = NULL; + /* there can't be refs with a (successful) bind */ if ( rs->sr_ref ) { - assert( refs != NULL ); - ber_memvfree( (void **)refs ); op->o_tmpfree( rs->sr_ref, op->o_tmpmemctx ); rs->sr_ref = NULL; } - if ( ctrls ) { - assert( rs->sr_ctrls != NULL ); + if ( refs ) { + ber_memvfree( (void **)refs ); + } + + /* match should not be possible with a successful bind */ + if ( match ) { + if ( rs->sr_matched != match ) { + free( (char *)rs->sr_matched ); + } + rs->sr_matched = NULL; + ldap_memfree( match ); + } + + if ( ctrls != NULL ) { + if ( op->o_tag == LDAP_REQ_BIND && rs->sr_err == LDAP_SUCCESS ) { + int i; + + for ( i = 0; ctrls[i] != NULL; i++ ); + + rs->sr_ctrls = op->o_tmpalloc( sizeof( LDAPControl * )*( i + 1 ), + op->o_tmpmemctx ); + for ( i = 0; ctrls[ i ] != NULL; i++ ) { + char *ptr; + ber_len_t oidlen = strlen( ctrls[i]->ldctl_oid ); + ber_len_t size = sizeof( LDAPControl ) + + oidlen + 1 + + ctrls[i]->ldctl_value.bv_len + 1; + + rs->sr_ctrls[ i ] = op->o_tmpalloc( size, op->o_tmpmemctx ); + rs->sr_ctrls[ i ]->ldctl_oid = (char *)&rs->sr_ctrls[ i ][ 1 ]; + lutil_strcopy( rs->sr_ctrls[ i ]->ldctl_oid, ctrls[i]->ldctl_oid ); + rs->sr_ctrls[ i ]->ldctl_value.bv_val + = (char *)&rs->sr_ctrls[ i ]->ldctl_oid[oidlen + 1]; + rs->sr_ctrls[ i ]->ldctl_value.bv_len + = ctrls[i]->ldctl_value.bv_len; + ptr = lutil_memcopy( rs->sr_ctrls[ i ]->ldctl_value.bv_val, + ctrls[i]->ldctl_value.bv_val, ctrls[i]->ldctl_value.bv_len ); + *ptr = '\0'; + } + rs->sr_ctrls[ i ] = NULL; + rs->sr_flags |= REP_CTRLS_MUSTBEFREED; + + } else { + assert( rs->sr_ctrls != NULL ); + rs->sr_ctrls = NULL; + } + ldap_controls_free( ctrls ); - rs->sr_ctrls = NULL; } - return( ERR_OK( rs->sr_err ) ? LDAP_SUCCESS : rs->sr_err ); + return( LDAP_ERR_OK( rs->sr_err ) ? LDAP_SUCCESS : rs->sr_err ); } /* return true if bound, false if failed */ @@ -1781,8 +1979,7 @@ int ldap_back_retry( ldapconn_t **lcp, Operation *op, SlapReply *rs, ldap_back_send_t sendok ) { ldapinfo_t *li = (ldapinfo_t *)op->o_bd->be_private; - int rc = 0, - binding; + int rc = 0; assert( lcp != NULL ); assert( *lcp != NULL ); @@ -1790,7 +1987,7 @@ ldap_back_retry( ldapconn_t **lcp, Operation *op, SlapReply *rs, ldap_back_send_ ldap_pvt_thread_mutex_lock( &li->li_conninfo.lai_mutex ); if ( (*lcp)->lc_refcnt == 1 ) { - binding = LDAP_BACK_CONN_BINDING( *lcp ); + int binding = LDAP_BACK_CONN_BINDING( *lcp ); ldap_pvt_thread_mutex_lock( &li->li_uri_mutex ); Debug( LDAP_DEBUG_ANY, @@ -1809,7 +2006,7 @@ ldap_back_retry( ldapconn_t **lcp, Operation *op, SlapReply *rs, ldap_back_send_ if ( rc != LDAP_SUCCESS ) { /* freeit, because lc_refcnt == 1 */ (*lcp)->lc_refcnt = 0; - (void)ldap_back_freeconn( op, *lcp, 0 ); + (void)ldap_back_freeconn( li, *lcp, 0 ); *lcp = NULL; rc = 0; @@ -1825,7 +2022,7 @@ ldap_back_retry( ldapconn_t **lcp, Operation *op, SlapReply *rs, ldap_back_send_ /* freeit, because lc_refcnt == 1 */ (*lcp)->lc_refcnt = 0; LDAP_BACK_CONN_TAINTED_SET( *lcp ); - (void)ldap_back_freeconn( op, *lcp, 0 ); + (void)ldap_back_freeconn( li, *lcp, 0 ); *lcp = NULL; } } @@ -1836,12 +2033,12 @@ ldap_back_retry( ldapconn_t **lcp, Operation *op, SlapReply *rs, ldap_back_send_ (void *)(*lcp), (*lcp)->lc_refcnt, 0 ); LDAP_BACK_CONN_TAINTED_SET( *lcp ); - ldap_back_release_conn_lock( op, rs, lcp, 0 ); + ldap_back_release_conn_lock( li, lcp, 0 ); assert( *lcp == NULL ); - if ( sendok ) { + if ( sendok & LDAP_BACK_SENDERR ) { rs->sr_err = LDAP_UNAVAILABLE; - rs->sr_text = "unable to retry"; + rs->sr_text = "Unable to retry"; send_ldap_result( op, rs ); } } @@ -1928,32 +2125,51 @@ ldap_back_is_proxy_authz( Operation *op, SlapReply *rs, ldap_back_send_t sendok, goto done; - } else if ( li->li_idassert_authz && !be_isroot( op ) ) { - struct berval authcDN; + } else if ( !be_isroot( op ) ) { + if ( li->li_idassert_passthru ) { + struct berval authcDN; - if ( BER_BVISNULL( &ndn ) ) { - authcDN = slap_empty_bv; - - } else { - authcDN = ndn; - } - rs->sr_err = slap_sasl_matches( op, li->li_idassert_authz, - &authcDN, &authcDN ); - if ( rs->sr_err != LDAP_SUCCESS ) { - if ( li->li_idassert_flags & LDAP_BACK_AUTH_PRESCRIPTIVE ) { - if ( sendok & LDAP_BACK_SENDERR ) { - send_ldap_result( op, rs ); - dobind = -1; - } + if ( BER_BVISNULL( &ndn ) ) { + authcDN = slap_empty_bv; } else { - rs->sr_err = LDAP_SUCCESS; - *binddn = slap_empty_bv; - *bindcred = slap_empty_bv; + authcDN = ndn; + } + rs->sr_err = slap_sasl_matches( op, li->li_idassert_passthru, + &authcDN, &authcDN ); + if ( rs->sr_err == LDAP_SUCCESS ) { + dobind = 0; break; } + } - goto done; + if ( li->li_idassert_authz ) { + struct berval authcDN; + + if ( BER_BVISNULL( &ndn ) ) { + authcDN = slap_empty_bv; + + } else { + authcDN = ndn; + } + rs->sr_err = slap_sasl_matches( op, li->li_idassert_authz, + &authcDN, &authcDN ); + if ( rs->sr_err != LDAP_SUCCESS ) { + if ( li->li_idassert_flags & LDAP_BACK_AUTH_PRESCRIPTIVE ) { + if ( sendok & LDAP_BACK_SENDERR ) { + send_ldap_result( op, rs ); + dobind = -1; + } + + } else { + rs->sr_err = LDAP_SUCCESS; + *binddn = slap_empty_bv; + *bindcred = slap_empty_bv; + break; + } + + goto done; + } } } @@ -1993,6 +2209,15 @@ ldap_back_proxy_authz_bind( void *defaults = NULL; struct berval authzID = BER_BVNULL; int freeauthz = 0; + LDAPControl **ctrlsp = NULL; + LDAPMessage *result = NULL; + const char *rmech = NULL; + const char *save_text = rs->sr_text; + +#ifdef SLAP_AUTH_DN + LDAPControl ctrl, *ctrls[2]; + int msgid; +#endif /* SLAP_AUTH_DN */ /* if SASL supports native authz, prepare for it */ if ( ( !op->o_do_not_cache || !op->o_is_auth_check ) && @@ -2048,23 +2273,131 @@ ldap_back_proxy_authz_bind( li->li_idassert_authcID.bv_val, li->li_idassert_passwd.bv_val, authzID.bv_val ); + if ( defaults == NULL ) { + rs->sr_err = LDAP_OTHER; + LDAP_BACK_CONN_ISBOUND_CLEAR( lc ); + if ( sendok & LDAP_BACK_SENDERR ) { + send_ldap_result( op, rs ); + } + goto done; + } - rs->sr_err = ldap_sasl_interactive_bind_s( lc->lc_ld, binddn->bv_val, - li->li_idassert_sasl_mech.bv_val, NULL, NULL, - LDAP_SASL_QUIET, lutil_sasl_interact, - defaults ); +#ifdef SLAP_AUTH_DN + if ( li->li_idassert_flags & LDAP_BACK_AUTH_DN_AUTHZID ) { + assert( BER_BVISNULL( binddn ) ); - rs->sr_err = slap_map_api2result( rs ); - if ( rs->sr_err != LDAP_SUCCESS ) { + ctrl.ldctl_oid = LDAP_CONTROL_AUTHZID_REQUEST; + ctrl.ldctl_iscritical = 0; + BER_BVZERO( &ctrl.ldctl_value ); + ctrls[0] = &ctrl; + ctrls[1] = NULL; + ctrlsp = ctrls; + } +#endif /* SLAP_AUTH_DN */ + + do { + rs->sr_err = ldap_sasl_interactive_bind( lc->lc_ld, binddn->bv_val, + li->li_idassert_sasl_mech.bv_val, + ctrlsp, NULL, LDAP_SASL_QUIET, lutil_sasl_interact, defaults, + result, &rmech, &msgid ); + + if ( rs->sr_err != LDAP_SASL_BIND_IN_PROGRESS ) + break; + + ldap_msgfree( result ); + + if ( ldap_result( lc->lc_ld, msgid, LDAP_MSG_ALL, NULL, &result ) == -1 || !result ) { + ldap_get_option( lc->lc_ld, LDAP_OPT_RESULT_CODE, (void*)&rs->sr_err ); + ldap_get_option( lc->lc_ld, LDAP_OPT_DIAGNOSTIC_MESSAGE, (void*)&rs->sr_text ); + break; + } + } while ( rs->sr_err == LDAP_SASL_BIND_IN_PROGRESS ); + + switch ( rs->sr_err ) { + case LDAP_SUCCESS: +#ifdef SLAP_AUTH_DN + /* FIXME: right now, the only reason to check + * response controls is RFC 3829 authzid */ + if ( li->li_idassert_flags & LDAP_BACK_AUTH_DN_AUTHZID ) { + ctrlsp = NULL; + rc = ldap_parse_result( lc->lc_ld, result, NULL, NULL, NULL, NULL, + &ctrlsp, 0 ); + if ( rc == LDAP_SUCCESS && ctrlsp ) { + LDAPControl *ctrl; + + ctrl = ldap_control_find( LDAP_CONTROL_AUTHZID_RESPONSE, + ctrlsp, NULL ); + if ( ctrl ) { + Debug( LDAP_DEBUG_TRACE, "%s: ldap_back_proxy_authz_bind: authzID=\"%s\" (authzid)\n", + op->o_log_prefix, ctrl->ldctl_value.bv_val, 0 ); + if ( ctrl->ldctl_value.bv_len > STRLENOF("dn:") && + strncasecmp( ctrl->ldctl_value.bv_val, "dn:", STRLENOF("dn:") ) == 0 ) + { + struct berval bv; + bv.bv_val = &ctrl->ldctl_value.bv_val[STRLENOF("dn:")]; + bv.bv_len = ctrl->ldctl_value.bv_len - STRLENOF("dn:"); + ber_bvreplace( &lc->lc_bound_ndn, &bv ); + } + } + } + + ldap_controls_free( ctrlsp ); + + } else if ( li->li_idassert_flags & LDAP_BACK_AUTH_DN_WHOAMI ) { + struct berval *val = NULL; + rc = ldap_whoami_s( lc->lc_ld, &val, NULL, NULL ); + if ( rc == LDAP_SUCCESS && val != NULL ) { + Debug( LDAP_DEBUG_TRACE, "%s: ldap_back_proxy_authz_bind: authzID=\"%s\" (whoami)\n", + op->o_log_prefix, val->bv_val, 0 ); + if ( val->bv_len > STRLENOF("dn:") && + strncasecmp( val->bv_val, "dn:", STRLENOF("dn:") ) == 0 ) + { + struct berval bv; + bv.bv_val = &val->bv_val[STRLENOF("dn:")]; + bv.bv_len = val->bv_len - STRLENOF("dn:"); + ber_bvreplace( &lc->lc_bound_ndn, &bv ); + } + ber_bvfree( val ); + } + } + + if ( ( li->li_idassert_flags & LDAP_BACK_AUTH_DN_MASK ) && + BER_BVISNULL( &lc->lc_bound_ndn ) ) + { + /* all in all, we only need it to be non-null */ + /* FIXME: should this be configurable? */ + static struct berval bv = BER_BVC("cn=authzdn"); + ber_bvreplace( &lc->lc_bound_ndn, &bv ); + } +#endif /* SLAP_AUTH_DN */ + op->o_conn->c_authz_cookie = op->o_bd->be_private; + LDAP_BACK_CONN_ISBOUND_SET( lc ); + break; + + case LDAP_LOCAL_ERROR: + /* list client API error codes that require + * to taint the connection */ + /* FIXME: should actually retry? */ + LDAP_BACK_CONN_TAINTED_SET( lc ); + + /* fallthru */ + + default: LDAP_BACK_CONN_ISBOUND_CLEAR( lc ); + rs->sr_err = slap_map_api2result( rs ); if ( sendok & LDAP_BACK_SENDERR ) { send_ldap_result( op, rs ); } + break; + } - } else { - LDAP_BACK_CONN_ISBOUND_SET( lc ); + if ( save_text != rs->sr_text ) { + ldap_memfree( (char *)rs->sr_text ); + rs->sr_text = save_text; } + ldap_msgfree( result ); + lutil_sasl_freedefs( defaults ); if ( freeauthz ) { slap_sl_free( authzID.bv_val, op->o_tmpmemctx ); @@ -2086,7 +2419,7 @@ ldap_back_proxy_authz_bind( binddn->bv_val, LDAP_SASL_SIMPLE, bindcred, NULL, NULL, &msgid ); rc = ldap_back_op_result( lc, op, rs, msgid, - -1, (sendok|LDAP_BACK_BINDING) ); + -1, ( sendok | LDAP_BACK_BINDING ) ); break; default: @@ -2104,7 +2437,10 @@ ldap_back_proxy_authz_bind( * so that referral chasing is attempted using the right * identity */ LDAP_BACK_CONN_ISBOUND_SET( lc ); - ber_bvreplace( &lc->lc_bound_ndn, binddn ); + op->o_conn->c_authz_cookie = op->o_bd->be_private; + if ( !BER_BVISNULL( binddn ) ) { + ber_bvreplace( &lc->lc_bound_ndn, binddn ); + } if ( !BER_BVISNULL( &lc->lc_cred ) ) { memset( lc->lc_cred.bv_val, 0, @@ -2112,13 +2448,16 @@ ldap_back_proxy_authz_bind( } if ( LDAP_BACK_SAVECRED( li ) ) { - ber_bvreplace( &lc->lc_cred, bindcred ); - ldap_set_rebind_proc( lc->lc_ld, li->li_rebind_f, lc ); + if ( !BER_BVISNULL( bindcred ) ) { + ber_bvreplace( &lc->lc_cred, bindcred ); + ldap_set_rebind_proc( lc->lc_ld, li->li_rebind_f, lc ); + } } else { lc->lc_cred.bv_len = 0; } } + done:; return LDAP_BACK_CONN_ISBOUND( lc ); } @@ -2151,44 +2490,26 @@ done:; */ int ldap_back_proxy_authz_ctrl( + Operation *op, + SlapReply *rs, struct berval *bound_ndn, int version, slap_idassert_t *si, - Operation *op, - SlapReply *rs, - LDAPControl ***pctrls ) + LDAPControl *ctrl ) { - LDAPControl **ctrls = NULL; - int i = 0; slap_idassert_mode_t mode; struct berval assertedID, ndn; int isroot = 0; - *pctrls = NULL; - - rs->sr_err = LDAP_SUCCESS; - - /* don't proxyAuthz if protocol is not LDAPv3 */ - switch ( version ) { - case LDAP_VERSION3: - break; - - case 0: - if ( op->o_protocol == 0 || op->o_protocol == LDAP_VERSION3 ) { - break; - } - /* fall thru */ - - default: - goto done; - } + rs->sr_err = SLAP_CB_CONTINUE; /* FIXME: SASL/EXTERNAL over ldapi:// doesn't honor the authcID, * but if it is not set this test fails. We need a different * means to detect if idassert is enabled */ if ( ( BER_BVISNULL( &si->si_bc.sb_authcId ) || BER_BVISEMPTY( &si->si_bc.sb_authcId ) ) - && ( BER_BVISNULL( &si->si_bc.sb_binddn ) || BER_BVISEMPTY( &si->si_bc.sb_binddn ) ) ) + && ( BER_BVISNULL( &si->si_bc.sb_binddn ) || BER_BVISEMPTY( &si->si_bc.sb_binddn ) ) + && BER_BVISNULL( &si->si_bc.sb_saslmech ) ) { goto done; } @@ -2255,7 +2576,7 @@ ldap_back_proxy_authz_ctrl( authcDN = ndn; } rc = slap_sasl_matches( op, si->si_authz, - &authcDN, & authcDN ); + &authcDN, &authcDN ); if ( rc != LDAP_SUCCESS ) { if ( si->si_flags & LDAP_BACK_AUTH_PRESCRIPTIVE ) { /* ndn is not authorized @@ -2332,33 +2653,26 @@ ldap_back_proxy_authz_ctrl( goto done; } - if ( op->o_ctrls ) { - for ( i = 0; op->o_ctrls[ i ]; i++ ) - /* just count ctrls */ ; - } - - ctrls = op->o_tmpalloc( sizeof( LDAPControl * ) * (i + 2) + sizeof( LDAPControl ), - op->o_tmpmemctx ); - ctrls[ 0 ] = (LDAPControl *)&ctrls[ i + 2 ]; - - ctrls[ 0 ]->ldctl_oid = LDAP_CONTROL_PROXY_AUTHZ; - ctrls[ 0 ]->ldctl_iscritical = 1; + ctrl->ldctl_oid = LDAP_CONTROL_PROXY_AUTHZ; + ctrl->ldctl_iscritical = ( ( si->si_flags & LDAP_BACK_AUTH_PROXYAUTHZ_CRITICAL ) == LDAP_BACK_AUTH_PROXYAUTHZ_CRITICAL ); switch ( si->si_mode ) { /* already in u:ID or dn:DN form */ case LDAP_BACK_IDASSERT_OTHERID: case LDAP_BACK_IDASSERT_OTHERDN: - ber_dupbv_x( &ctrls[ 0 ]->ldctl_value, &assertedID, op->o_tmpmemctx ); + ber_dupbv_x( &ctrl->ldctl_value, &assertedID, op->o_tmpmemctx ); + rs->sr_err = LDAP_SUCCESS; break; /* needs the dn: prefix */ default: - ctrls[ 0 ]->ldctl_value.bv_len = assertedID.bv_len + STRLENOF( "dn:" ); - ctrls[ 0 ]->ldctl_value.bv_val = op->o_tmpalloc( ctrls[ 0 ]->ldctl_value.bv_len + 1, + ctrl->ldctl_value.bv_len = assertedID.bv_len + STRLENOF( "dn:" ); + ctrl->ldctl_value.bv_val = op->o_tmpalloc( ctrl->ldctl_value.bv_len + 1, op->o_tmpmemctx ); - AC_MEMCPY( ctrls[ 0 ]->ldctl_value.bv_val, "dn:", STRLENOF( "dn:" ) ); - AC_MEMCPY( &ctrls[ 0 ]->ldctl_value.bv_val[ STRLENOF( "dn:" ) ], + AC_MEMCPY( ctrl->ldctl_value.bv_val, "dn:", STRLENOF( "dn:" ) ); + AC_MEMCPY( &ctrl->ldctl_value.bv_val[ STRLENOF( "dn:" ) ], assertedID.bv_val, assertedID.bv_len + 1 ); + rs->sr_err = LDAP_SUCCESS; break; } @@ -2367,7 +2681,7 @@ ldap_back_proxy_authz_ctrl( * this hack provides compatibility with those DSAs that * implement it this way */ if ( si->si_flags & LDAP_BACK_AUTH_OBSOLETE_ENCODING_WORKAROUND ) { - struct berval authzID = ctrls[ 0 ]->ldctl_value; + struct berval authzID = ctrl->ldctl_value; BerElementBuffer berbuf; BerElement *ber = (BerElement *)&berbuf; ber_tag_t tag; @@ -2381,32 +2695,29 @@ ldap_back_proxy_authz_ctrl( goto free_ber; } - if ( ber_flatten2( ber, &ctrls[ 0 ]->ldctl_value, 1 ) == -1 ) { + if ( ber_flatten2( ber, &ctrl->ldctl_value, 1 ) == -1 ) { rs->sr_err = LDAP_OTHER; goto free_ber; } + rs->sr_err = LDAP_SUCCESS; + free_ber:; op->o_tmpfree( authzID.bv_val, op->o_tmpmemctx ); ber_free_buf( ber ); if ( rs->sr_err != LDAP_SUCCESS ) { - op->o_tmpfree( ctrls, op->o_tmpmemctx ); - ctrls = NULL; goto done; } } else if ( si->si_flags & LDAP_BACK_AUTH_OBSOLETE_PROXY_AUTHZ ) { - struct berval authzID = ctrls[ 0 ]->ldctl_value, + struct berval authzID = ctrl->ldctl_value, tmp; BerElementBuffer berbuf; BerElement *ber = (BerElement *)&berbuf; ber_tag_t tag; if ( strncasecmp( authzID.bv_val, "dn:", STRLENOF( "dn:" ) ) != 0 ) { - op->o_tmpfree( ctrls[ 0 ]->ldctl_value.bv_val, op->o_tmpmemctx ); - op->o_tmpfree( ctrls, op->o_tmpmemctx ); - ctrls = NULL; rs->sr_err = LDAP_PROTOCOL_ERROR; goto done; } @@ -2426,30 +2737,154 @@ free_ber:; goto free_ber2; } - if ( ber_flatten2( ber, &ctrls[ 0 ]->ldctl_value, 1 ) == -1 ) { + if ( ber_flatten2( ber, &ctrl->ldctl_value, 1 ) == -1 ) { rs->sr_err = LDAP_OTHER; goto free_ber2; } + ctrl->ldctl_oid = LDAP_CONTROL_OBSOLETE_PROXY_AUTHZ; + rs->sr_err = LDAP_SUCCESS; + free_ber2:; op->o_tmpfree( authzID.bv_val, op->o_tmpmemctx ); ber_free_buf( ber ); if ( rs->sr_err != LDAP_SUCCESS ) { - op->o_tmpfree( ctrls, op->o_tmpmemctx ); - ctrls = NULL; goto done; } + } + +done:; + + return rs->sr_err; +} - ctrls[ 0 ]->ldctl_oid = LDAP_CONTROL_OBSOLETE_PROXY_AUTHZ; +/* + * Add controls; + * + * if any needs to be added, it is prepended to existing ones, + * in a newly allocated array. The companion function + * ldap_back_controls_free() must be used to restore the original + * status of op->o_ctrls. + */ +int +ldap_back_controls_add( + Operation *op, + SlapReply *rs, + ldapconn_t *lc, + LDAPControl ***pctrls ) +{ + ldapinfo_t *li = (ldapinfo_t *)op->o_bd->be_private; + + LDAPControl **ctrls = NULL; + /* set to the maximum number of controls this backend can add */ + LDAPControl c[ 2 ] = { { 0 } }; + int n = 0, i, j1 = 0, j2 = 0; + + *pctrls = NULL; + + rs->sr_err = LDAP_SUCCESS; + + /* don't add controls if protocol is not LDAPv3 */ + switch ( li->li_version ) { + case LDAP_VERSION3: + break; + + case 0: + if ( op->o_protocol == 0 || op->o_protocol == LDAP_VERSION3 ) { + break; + } + /* fall thru */ + + default: + goto done; + } + + /* put controls that go __before__ existing ones here */ + + /* proxyAuthz for identity assertion */ + switch ( ldap_back_proxy_authz_ctrl( op, rs, &lc->lc_bound_ndn, + li->li_version, &li->li_idassert, &c[ j1 ] ) ) + { + case SLAP_CB_CONTINUE: + break; + + case LDAP_SUCCESS: + j1++; + break; + + default: + goto done; + } + + /* put controls that go __after__ existing ones here */ + +#ifdef SLAP_CONTROL_X_SESSION_TRACKING + /* FIXME: according to , + * the server should check if the control can be added + * based on the identity of the client and so */ + + /* session tracking */ + if ( LDAP_BACK_ST_REQUEST( li ) ) { + switch ( slap_ctrl_session_tracking_request_add( op, rs, &c[ j1 + j2 ] ) ) { + case SLAP_CB_CONTINUE: + break; + + case LDAP_SUCCESS: + j2++; + break; + + default: + goto done; + } } +#endif /* SLAP_CONTROL_X_SESSION_TRACKING */ + + if ( rs->sr_err == SLAP_CB_CONTINUE ) { + rs->sr_err = LDAP_SUCCESS; + } + + /* if nothing to do, just bail out */ + if ( j1 == 0 && j2 == 0 ) { + goto done; + } + + assert( j1 + j2 <= (int) (sizeof( c )/sizeof( c[0] )) ); + if ( op->o_ctrls ) { + for ( n = 0; op->o_ctrls[ n ]; n++ ) + /* just count ctrls */ ; + } + + ctrls = op->o_tmpalloc( (n + j1 + j2 + 1) * sizeof( LDAPControl * ) + ( j1 + j2 ) * sizeof( LDAPControl ), + op->o_tmpmemctx ); + if ( j1 ) { + ctrls[ 0 ] = (LDAPControl *)&ctrls[ n + j1 + j2 + 1 ]; + *ctrls[ 0 ] = c[ 0 ]; + for ( i = 1; i < j1; i++ ) { + ctrls[ i ] = &ctrls[ 0 ][ i ]; + *ctrls[ i ] = c[ i ]; + } + } + + i = 0; if ( op->o_ctrls ) { for ( i = 0; op->o_ctrls[ i ]; i++ ) { - ctrls[ i + 1 ] = op->o_ctrls[ i ]; + ctrls[ i + j1 ] = op->o_ctrls[ i ]; + } + } + + n += j1; + if ( j2 ) { + ctrls[ n ] = (LDAPControl *)&ctrls[ n + j2 + 1 ] + j1; + *ctrls[ n ] = c[ j1 ]; + for ( i = 1; i < j2; i++ ) { + ctrls[ n + i ] = &ctrls[ n ][ i ]; + *ctrls[ n + i ] = c[ i ]; } } - ctrls[ i + 1 ] = NULL; + + ctrls[ n + j2 ] = NULL; done:; if ( ctrls == NULL ) { @@ -2462,18 +2897,39 @@ done:; } int -ldap_back_proxy_authz_ctrl_free( Operation *op, LDAPControl ***pctrls ) +ldap_back_controls_free( Operation *op, SlapReply *rs, LDAPControl ***pctrls ) { LDAPControl **ctrls = *pctrls; - /* we assume that the first control is the proxyAuthz - * added by back-ldap, so it's the only one we explicitly - * free */ + /* we assume that the controls added by the proxy come first, + * so as soon as we find op->o_ctrls[ 0 ] we can stop */ if ( ctrls && ctrls != op->o_ctrls ) { + int i = 0, n = 0, n_added; + LDAPControl *lower, *upper; + assert( ctrls[ 0 ] != NULL ); - if ( !BER_BVISNULL( &ctrls[ 0 ]->ldctl_value ) ) { - op->o_tmpfree( ctrls[ 0 ]->ldctl_value.bv_val, op->o_tmpmemctx ); + for ( n = 0; ctrls[ n ] != NULL; n++ ) + /* count 'em */ ; + + if ( op->o_ctrls ) { + for ( i = 0; op->o_ctrls[ i ] != NULL; i++ ) + /* count 'em */ ; + } + + n_added = n - i; + lower = (LDAPControl *)&ctrls[ n ]; + upper = &lower[ n_added ]; + + for ( i = 0; ctrls[ i ] != NULL; i++ ) { + if ( ctrls[ i ] < lower || ctrls[ i ] >= upper ) { + /* original; don't touch */ + continue; + } + + if ( !BER_BVISNULL( &ctrls[ i ]->ldctl_value ) ) { + op->o_tmpfree( ctrls[ i ]->ldctl_value.bv_val, op->o_tmpmemctx ); + } } op->o_tmpfree( ctrls, op->o_tmpmemctx ); @@ -2483,3 +2939,84 @@ ldap_back_proxy_authz_ctrl_free( Operation *op, LDAPControl ***pctrls ) return 0; } + +int +ldap_back_conn2str( const ldapconn_base_t *lc, char *buf, ber_len_t buflen ) +{ + char tbuf[ SLAP_TEXT_BUFLEN ]; + char *ptr = buf, *end = buf + buflen; + int len; + + if ( ptr + sizeof("conn=") > end ) return -1; + ptr = lutil_strcopy( ptr, "conn=" ); + + len = ldap_back_connid2str( lc, ptr, (ber_len_t)(end - ptr) ); + ptr += len; + if ( ptr >= end ) return -1; + + if ( !BER_BVISNULL( &lc->lcb_local_ndn ) ) { + if ( ptr + sizeof(" DN=\"\"") + lc->lcb_local_ndn.bv_len > end ) return -1; + ptr = lutil_strcopy( ptr, " DN=\"" ); + ptr = lutil_strncopy( ptr, lc->lcb_local_ndn.bv_val, lc->lcb_local_ndn.bv_len ); + *ptr++ = '"'; + } + + if ( lc->lcb_create_time != 0 ) { + len = snprintf( tbuf, sizeof(tbuf), "%ld", lc->lcb_create_time ); + if ( ptr + sizeof(" created=") + len >= end ) return -1; + ptr = lutil_strcopy( ptr, " created=" ); + ptr = lutil_strcopy( ptr, tbuf ); + } + + if ( lc->lcb_time != 0 ) { + len = snprintf( tbuf, sizeof(tbuf), "%ld", lc->lcb_time ); + if ( ptr + sizeof(" modified=") + len >= end ) return -1; + ptr = lutil_strcopy( ptr, " modified=" ); + ptr = lutil_strcopy( ptr, tbuf ); + } + + len = snprintf( tbuf, sizeof(tbuf), "%u", lc->lcb_refcnt ); + if ( ptr + sizeof(" refcnt=") + len >= end ) return -1; + ptr = lutil_strcopy( ptr, " refcnt=" ); + ptr = lutil_strcopy( ptr, tbuf ); + + return ptr - buf; +} + +int +ldap_back_connid2str( const ldapconn_base_t *lc, char *buf, ber_len_t buflen ) +{ + static struct berval conns[] = { + BER_BVC("ROOTDN"), + BER_BVC("ROOTDN-TLS"), + BER_BVC("ANON"), + BER_BVC("ANON-TLS"), + BER_BVC("BIND"), + BER_BVC("BIND-TLS"), + BER_BVNULL + }; + + int len = 0; + + if ( LDAP_BACK_PCONN_ISPRIV( (const ldapconn_t *)lc ) ) { + long cid; + struct berval *bv; + + cid = (long)lc->lcb_conn; + assert( cid >= LDAP_BACK_PCONN_FIRST && cid < LDAP_BACK_PCONN_LAST ); + + bv = &conns[ cid ]; + + if ( bv->bv_len >= buflen ) { + return bv->bv_len + 1; + } + + len = bv->bv_len; + lutil_strncopy( buf, bv->bv_val, bv->bv_len + 1 ); + + } else { + len = snprintf( buf, buflen, "%lu", lc->lcb_conn->c_connid ); + } + + return len; +}