]> git.sur5r.net Git - openldap/blobdiff - servers/slapd/back-ldap/bind.c
use trylock only where necessary
[openldap] / servers / slapd / back-ldap / bind.c
index 75124c07b7227b2e29e6e36e8833822a79f28f6d..d9e395d3ed25f189a5101ab4dbcfe7e16abcd526 100644 (file)
@@ -25,6 +25,7 @@
 
 #include <stdio.h>
 
+#include <ac/errno.h>
 #include <ac/socket.h>
 #include <ac/string.h>
 
@@ -91,9 +92,8 @@ ldap_back_bind( Operation *op, SlapReply *rs )
                        if ( !BER_BVISNULL( &lc->lc_cred ) ) {
                                memset( lc->lc_cred.bv_val, 0,
                                                lc->lc_cred.bv_len );
-                               ch_free( lc->lc_cred.bv_val );
                        }
-                       ber_dupbv( &lc->lc_cred, &op->orb_cred );
+                       ber_bvreplace( &lc->lc_cred, &op->orb_cred );
                        ldap_set_rebind_proc( lc->lc_ld, ldap_back_rebind, lc );
                }
        }
@@ -103,27 +103,34 @@ done:;
        if ( lc->lc_bound && !dn_match( &op->o_req_ndn, &lc->lc_local_ndn ) ) {
                int             lerr;
 
-               ldap_pvt_thread_mutex_lock( &li->conn_mutex );
                /* wait for all other ops to release the connection */
-               while ( lc->lc_refcnt > 1 ) {
-                       ldap_pvt_thread_mutex_unlock( &li->conn_mutex );
+retry_lock:;
+               switch ( ldap_pvt_thread_mutex_trylock( &li->conn_mutex ) ) {
+               case LDAP_PVT_THREAD_EBUSY:
+               default:
                        ldap_pvt_thread_yield();
-                       ldap_pvt_thread_mutex_lock( &li->conn_mutex );
+                       goto retry_lock;
+
+               case 0:
+                       if ( lc->lc_refcnt > 1 ) {
+                               ldap_pvt_thread_mutex_unlock( &li->conn_mutex );
+                               ldap_pvt_thread_yield();
+                               goto retry_lock;
+                       }
+                       break;
                }
+
                assert( lc->lc_refcnt == 1 );
                lc = avl_delete( &li->conntree, (caddr_t)lc,
                                ldap_back_conn_cmp );
                assert( lc != NULL );
 
-               if ( !BER_BVISNULL( &lc->lc_local_ndn ) ) {
-                       ch_free( lc->lc_local_ndn.bv_val );
-               }
-               ber_dupbv( &lc->lc_local_ndn, &op->o_req_ndn );
+               ber_bvreplace( &lc->lc_local_ndn, &op->o_req_ndn );
                lerr = avl_insert( &li->conntree, (caddr_t)lc,
                        ldap_back_conn_cmp, ldap_back_conn_dup );
                ldap_pvt_thread_mutex_unlock( &li->conn_mutex );
                if ( lerr == -1 ) {
-                       /* handle this! (e.g. wait until refcnt goes to 1...) */
+                       /* we can do this because lc_refcnt == 1 */
                        ldap_back_conn_free( lc );
                        lc = NULL;
                }
@@ -227,9 +234,9 @@ int
 ldap_back_freeconn( Operation *op, struct ldapconn *lc )
 {
        struct ldapinfo *li = (struct ldapinfo *) op->o_bd->be_private;
-       int             rc = 0;
 
        ldap_pvt_thread_mutex_lock( &li->conn_mutex );
+
        assert( lc->lc_refcnt > 0 );
        if ( --lc->lc_refcnt == 0 ) {
                lc = avl_delete( &li->conntree, (caddr_t)lc,
@@ -238,9 +245,10 @@ ldap_back_freeconn( Operation *op, struct ldapconn *lc )
 
                ldap_back_conn_free( (void *)lc );
        }
+
        ldap_pvt_thread_mutex_unlock( &li->conn_mutex );
 
-       return rc;
+       return 0;
 }
 
 static int
@@ -350,7 +358,7 @@ retry:;
                if ( rs->sr_err == LDAP_SERVER_DOWN
                                || ( rs->sr_err != LDAP_SUCCESS && LDAP_BACK_TLS_CRITICAL( li ) ) )
                {
-                       ldap_unbind_ext_s( ld, NULL, NULL );
+                       ldap_unbind_ext( ld, NULL, NULL );
                        goto error_return;
                }
 
@@ -360,8 +368,7 @@ retry:;
 #endif /* HAVE_TLS */
 
        if ( *lcp == NULL ) {
-               *lcp = (struct ldapconn *)ch_malloc( sizeof( struct ldapconn ) );
-               memset( *lcp, 0, sizeof( struct ldapconn ) );
+               *lcp = (struct ldapconn *)ch_calloc( 1, sizeof( struct ldapconn ) );
        }
        (*lcp)->lc_ld = ld;
        (*lcp)->lc_refcnt = 1;
@@ -385,7 +392,9 @@ struct ldapconn *
 ldap_back_getconn( Operation *op, SlapReply *rs, ldap_back_send_t sendok )
 {
        struct ldapinfo *li = (struct ldapinfo *)op->o_bd->be_private;
-       struct ldapconn *lc, lc_curr = { 0 };
+       struct ldapconn *lc,
+                       lc_curr = { 0 };
+       int             refcnt = 1;
 
        /* Searches for a ldapconn in the avl tree */
 
@@ -402,6 +411,7 @@ ldap_back_getconn( Operation *op, SlapReply *rs, ldap_back_send_t sendok )
        }
        
        /* Internal searches are privileged and shared. So is root. */
+       /* FIXME: there seem to be concurrency issues */
        if ( op->o_do_not_cache || be_isroot( op ) ) {
                lc_curr.lc_local_ndn = op->o_bd->be_rootndn;
                lc_curr.lc_conn = NULL;
@@ -412,16 +422,16 @@ ldap_back_getconn( Operation *op, SlapReply *rs, ldap_back_send_t sendok )
        }
 
        ldap_pvt_thread_mutex_lock( &li->conn_mutex );
+
        lc = (struct ldapconn *)avl_find( li->conntree, 
                        (caddr_t)&lc_curr, ldap_back_conn_cmp );
        if ( lc != NULL ) {
-               lc->lc_refcnt++;
+               refcnt = ++lc->lc_refcnt;
        }
        ldap_pvt_thread_mutex_unlock( &li->conn_mutex );
 
        /* Looks like we didn't get a bind. Open a new session... */
        if ( lc == NULL ) {
-               /* lc here must be NULL */
                if ( ldap_back_prepare_conn( &lc, op, rs, sendok ) != LDAP_SUCCESS ) {
                        return NULL;
                }
@@ -429,8 +439,6 @@ ldap_back_getconn( Operation *op, SlapReply *rs, ldap_back_send_t sendok )
                lc->lc_conn = lc_curr.lc_conn;
                ber_dupbv( &lc->lc_local_ndn, &lc_curr.lc_local_ndn );
 
-               ldap_pvt_thread_mutex_init( &lc->lc_mutex );
-
                if ( lc_curr.lc_ispriv ) {
                        ber_dupbv( &lc->lc_cred, &li->acl_passwd );
                        ber_dupbv( &lc->lc_bound_ndn, &li->acl_authcDN );
@@ -440,7 +448,7 @@ ldap_back_getconn( Operation *op, SlapReply *rs, ldap_back_send_t sendok )
                        BER_BVZERO( &lc->lc_cred );
                        BER_BVZERO( &lc->lc_bound_ndn );
                        if ( op->o_conn && !BER_BVISEMPTY( &op->o_ndn )
-                                       && op->o_bd == op->o_conn->c_authz_backend )
+                               && op->o_bd->be_private == op->o_conn->c_authz_backend->be_private )
                        {
                                ber_dupbv( &lc->lc_bound_ndn, &op->o_ndn );
                        }
@@ -450,6 +458,7 @@ ldap_back_getconn( Operation *op, SlapReply *rs, ldap_back_send_t sendok )
 
                /* Inserts the newly created ldapconn in the avl tree */
                ldap_pvt_thread_mutex_lock( &li->conn_mutex );
+
                assert( lc->lc_refcnt == 1 );
                rs->sr_err = avl_insert( &li->conntree, (caddr_t)lc,
                        ldap_back_conn_cmp, ldap_back_conn_dup );
@@ -462,7 +471,7 @@ ldap_back_getconn( Operation *op, SlapReply *rs, ldap_back_send_t sendok )
 
                Debug( LDAP_DEBUG_TRACE,
                        "=>ldap_back_getconn: conn %p inserted (refcnt=%u)\n",
-                       (void *)lc, lc->lc_refcnt, 0 );
+                       (void *)lc, refcnt, 0 );
        
                /* Err could be -1 in case a duplicate ldapconn is inserted */
                if ( rs->sr_err != 0 ) {
@@ -470,14 +479,15 @@ ldap_back_getconn( Operation *op, SlapReply *rs, ldap_back_send_t sendok )
                        rs->sr_err = LDAP_OTHER;
                        if ( op->o_conn && ( sendok & LDAP_BACK_SENDERR ) ) {
                                send_ldap_error( op, rs, LDAP_OTHER,
-                               "internal server error" );
+                                       "internal server error" );
                        }
                        return NULL;
                }
+
        } else {
                Debug( LDAP_DEBUG_TRACE,
                        "=>ldap_back_getconn: conn %p fetched (refcnt=%u)\n",
-                       (void *)lc, lc->lc_refcnt, 0 );
+                       (void *)lc, refcnt, 0 );
        }
        
        return lc;
@@ -490,7 +500,7 @@ ldap_back_release_conn(
        struct ldapconn         *lc )
 {
        struct ldapinfo *li = (struct ldapinfo *)op->o_bd->be_private;
-       
+
        ldap_pvt_thread_mutex_lock( &li->conn_mutex );
        assert( lc->lc_refcnt > 0 );
        lc->lc_refcnt--;
@@ -503,6 +513,8 @@ ldap_back_release_conn(
  * Note: as the check for the value of lc->lc_bound was already here, I removed
  * it from all the callers, and I made the function return the flag, so
  * it can be used to simplify the check.
+ *
+ * Note: dolock indicates whether li->conn_mutex must be locked or not
  */
 static int
 ldap_back_dobind_int(
@@ -510,7 +522,8 @@ ldap_back_dobind_int(
        Operation               *op,
        SlapReply               *rs,
        ldap_back_send_t        sendok,
-       int                     retries )
+       int                     retries,
+       int                     dolock )
 {      
        int             rc;
        ber_int_t       msgid;
@@ -544,7 +557,6 @@ ldap_back_dobind_int(
                 */
                if ( op->o_conn != NULL &&
                                !op->o_do_not_cache &&
-                               !be_isroot( op ) &&
                                ( BER_BVISNULL( &lc->lc_bound_ndn ) ||
                                  ( li->idassert_flags & LDAP_BACK_AUTH_OVERRIDE ) ) )
                {
@@ -605,16 +617,21 @@ retry:;
 
                if ( rs->sr_err == LDAP_SERVER_DOWN ) {
                        if ( retries > 0 ) {
-                               ldap_pvt_thread_mutex_lock( &li->conn_mutex );
+                               if ( dolock ) {
+                                       ldap_pvt_thread_mutex_lock( &li->conn_mutex );
+                               }
+
                                assert( lc->lc_refcnt > 0 );
                                if ( lc->lc_refcnt == 1 ) {
-                                       ldap_unbind_ext_s( lc->lc_ld, NULL, NULL );
+                                       ldap_unbind_ext( lc->lc_ld, NULL, NULL );
                                        lc->lc_ld = NULL;
 
                                        /* lc here must be the regular lc, reset and ready for init */
                                        rs->sr_err = ldap_back_prepare_conn( &lc, op, rs, sendok );
                                }
-                               ldap_pvt_thread_mutex_unlock( &li->conn_mutex );
+                               if ( dolock ) {
+                                       ldap_pvt_thread_mutex_unlock( &li->conn_mutex );
+                               }
                                if ( rs->sr_err == LDAP_SUCCESS ) {
                                        retries--;
                                        goto retry;
@@ -641,13 +658,7 @@ done:;
 int
 ldap_back_dobind( struct ldapconn *lc, Operation *op, SlapReply *rs, ldap_back_send_t sendok )
 {
-       int     rc;
-
-       ldap_pvt_thread_mutex_lock( &lc->lc_mutex );
-       rc = ldap_back_dobind_int( lc, op, rs, sendok, 1 );
-       ldap_pvt_thread_mutex_unlock( &lc->lc_mutex );
-
-       return rc;
+       return ldap_back_dobind_int( lc, op, rs, sendok, 1, 1 );
 }
 
 /*
@@ -766,17 +777,15 @@ ldap_back_retry( struct ldapconn *lc, Operation *op, SlapReply *rs, ldap_back_se
        ldap_pvt_thread_mutex_lock( &li->conn_mutex );
 
        if ( lc->lc_refcnt == 1 ) {
-               ldap_pvt_thread_mutex_lock( &lc->lc_mutex );
-               ldap_unbind_ext_s( lc->lc_ld, NULL, NULL );
+               ldap_unbind_ext( lc->lc_ld, NULL, NULL );
                lc->lc_ld = NULL;
                lc->lc_bound = 0;
 
                /* lc here must be the regular lc, reset and ready for init */
                rc = ldap_back_prepare_conn( &lc, op, rs, sendok );
                if ( rc == LDAP_SUCCESS ) {
-                       rc = ldap_back_dobind_int( lc, op, rs, sendok, 0 );
+                       rc = ldap_back_dobind_int( lc, op, rs, sendok, 0, 0 );
                }
-               ldap_pvt_thread_mutex_unlock( &lc->lc_mutex );
        }
 
        ldap_pvt_thread_mutex_unlock( &li->conn_mutex );
@@ -830,19 +839,30 @@ ldap_back_proxy_authz_bind( struct ldapconn *lc, Operation *op, SlapReply *rs )
                break;
 
        default:
-               if ( li->idassert_authz ) {
+               /* NOTE: rootdn can always idassert */
+               if ( li->idassert_authz && !be_isroot( op ) ) {
                        struct berval authcDN;
 
                        if ( BER_BVISNULL( &op->o_conn->c_ndn ) ) {
                                authcDN = slap_empty_bv;
+
                        } else {
                                authcDN = op->o_conn->c_ndn;
                        }       
                        rs->sr_err = slap_sasl_matches( op, li->idassert_authz,
                                        &authcDN, &authcDN );
                        if ( rs->sr_err != LDAP_SUCCESS ) {
-                               send_ldap_result( op, rs );
-                               lc->lc_bound = 0;
+                               if ( li->idassert_flags & LDAP_BACK_AUTH_PRESCRIPTIVE ) {
+                                       send_ldap_result( op, rs );
+                                       lc->lc_bound = 0;
+
+                               } else {
+                                       rs->sr_err = LDAP_SUCCESS;
+                                       binddn = slap_empty_bv;
+                                       bindcred = slap_empty_bv;
+                                       break;
+                               }
+
                                goto done;
                        }
                }
@@ -1004,8 +1024,12 @@ ldap_back_proxy_authz_ctrl(
 
        rs->sr_err = LDAP_SUCCESS;
 
+       /* 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( &li->idassert_authcID ) || BER_BVISEMPTY( &li->idassert_authcID ) )
-                       && ( BER_BVISNULL( &li->idassert_authcDN ) || BER_BVISEMPTY( &li->idassert_authcDN ) ) ) {
+                       && ( BER_BVISNULL( &li->idassert_authcDN ) || BER_BVISEMPTY( &li->idassert_authcDN ) ) )
+       {
                goto done;
        }
 
@@ -1055,7 +1079,7 @@ ldap_back_proxy_authz_ctrl(
                        goto done;
                }
 
-       } else if ( li->idassert_authz ) {
+       } else if ( li->idassert_authz && !be_isroot( op ) ) {
                int             rc;
                struct berval authcDN;
 
@@ -1067,9 +1091,13 @@ ldap_back_proxy_authz_ctrl(
                rc = slap_sasl_matches( op, li->idassert_authz,
                                &authcDN, & authcDN );
                if ( rc != LDAP_SUCCESS ) {
-                       /* op->o_conn->c_ndn is not authorized
-                        * to use idassert */
-                       return rc;
+                       if ( li->idassert_flags & LDAP_BACK_AUTH_PRESCRIPTIVE )
+                       {
+                               /* op->o_conn->c_ndn is not authorized
+                                * to use idassert */
+                               return rc;
+                       }
+                       return rs->sr_err;
                }
        }
 
@@ -1160,7 +1188,7 @@ ldap_back_proxy_authz_ctrl(
                ctrls[ 0 ]->ldctl_value.bv_len = assertedID.bv_len + STRLENOF( "dn:" );
                ctrls[ 0 ]->ldctl_value.bv_val = ch_malloc( ctrls[ 0 ]->ldctl_value.bv_len + 1 );
                AC_MEMCPY( ctrls[ 0 ]->ldctl_value.bv_val, "dn:", STRLENOF( "dn:" ) );
-               AC_MEMCPY( ctrls[ 0 ]->ldctl_value.bv_val + STRLENOF( "dn:" ),
+               AC_MEMCPY( &ctrls[ 0 ]->ldctl_value.bv_val[ STRLENOF( "dn:" ) ],
                                assertedID.bv_val, assertedID.bv_len + 1 );
                break;
        }
@@ -1191,7 +1219,7 @@ ldap_back_proxy_authz_ctrl_free( Operation *op, LDAPControl ***pctrls )
         * added by back-ldap, so it's the only one we explicitly 
         * free */
        if ( ctrls && ctrls != op->o_ctrls ) {
-               assert( ctrls[ 0 ] );
+               assert( ctrls[ 0 ] != NULL );
 
                if ( !BER_BVISNULL( &ctrls[ 0 ]->ldctl_value ) ) {
                        free( ctrls[ 0 ]->ldctl_value.bv_val );