]> git.sur5r.net Git - openldap/blobdiff - servers/slapd/back-ldap/bind.c
address ITS#4332; might remove dynamicObject counting
[openldap] / servers / slapd / back-ldap / bind.c
index 2000f829eaaf979cc28d98ced0345388d15ca90d..33d4545f8a5d0b608baecd858a0dfb22a164d766 100644 (file)
@@ -2,7 +2,7 @@
 /* $OpenLDAP$ */
 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
  *
- * Copyright 1999-2005 The OpenLDAP Foundation.
+ * Copyright 1999-2006 The OpenLDAP Foundation.
  * Portions Copyright 2000-2003 Pierangelo Masarati.
  * Portions Copyright 1999-2003 Howard Chu.
  * All rights reserved.
@@ -42,7 +42,7 @@ static LDAP_REBIND_PROC       ldap_back_default_rebind;
 LDAP_REBIND_PROC       *ldap_back_rebind_f = ldap_back_default_rebind;
 
 static int
-ldap_back_proxy_authz_bind( ldapconn_t *lc, Operation *op, SlapReply *rs );
+ldap_back_proxy_authz_bind( ldapconn_t *lc, Operation *op, SlapReply *rs, ldap_back_send_t sendok );
 
 static int
 ldap_back_prepare_conn( ldapconn_t **lcp, Operation *op, SlapReply *rs, ldap_back_send_t sendok );
@@ -56,7 +56,7 @@ ldap_back_bind( Operation *op, SlapReply *rs )
        int rc = 0;
        ber_int_t msgid;
 
-       lc = ldap_back_getconn( op, rs, LDAP_BACK_SENDERR );
+       lc = ldap_back_getconn( op, rs, LDAP_BACK_BIND_SERR );
        if ( !lc ) {
                return rs->sr_err;
        }
@@ -80,7 +80,7 @@ ldap_back_bind( Operation *op, SlapReply *rs )
                 * bind with the configured identity assertion */
                /* NOTE: use with care */
                if ( li->li_idassert_flags & LDAP_BACK_AUTH_OVERRIDE ) {
-                       ldap_back_proxy_authz_bind( lc, op, rs );
+                       ldap_back_proxy_authz_bind( lc, op, rs, LDAP_BACK_SENDERR );
                        if ( !LDAP_BACK_CONN_ISBOUND( lc ) ) {
                                rc = 1;
                                goto done;
@@ -336,7 +336,7 @@ retry:;
                                                rc = ldap_install_tls( ld );
 
                                        } else if ( rc == LDAP_REFERRAL ) {
-                                               rc = LDAP_OTHER;
+                                               rc = LDAP_UNWILLING_TO_PERFORM;
                                                *text = "unwilling to chase referral returned by Start TLS exop";
                                        }
 
@@ -415,6 +415,10 @@ ldap_back_prepare_conn( ldapconn_t **lcp, Operation *op, SlapReply *rs, ldap_bac
        /* Set LDAP version. This will always succeed: If the client
         * bound with a particular version, then so can we.
         */
+       if ( vers == 0 ) {
+               /* assume it's an internal op; set to LDAPv3 */
+               vers = LDAP_VERSION3;
+       }
        ldap_set_option( ld, LDAP_OPT_PROTOCOL_VERSION, (const void *)&vers );
 
        /* automatically chase referrals ("[dont-]chase-referrals" statement) */
@@ -464,7 +468,7 @@ ldapconn_t *
 ldap_back_getconn( Operation *op, SlapReply *rs, ldap_back_send_t sendok )
 {
        ldapinfo_t      *li = (ldapinfo_t *)op->o_bd->be_private;
-       ldapconn_t      *lc,
+       ldapconn_t      *lc = NULL,
                        lc_curr = { 0 };
        int             refcnt = 1;
 
@@ -485,22 +489,34 @@ ldap_back_getconn( Operation *op, SlapReply *rs, ldap_back_send_t sendok )
                }
        }
 
-       /* Searches for a ldapconn in the avl tree */
-       ldap_pvt_thread_mutex_lock( &li->li_conninfo.lai_mutex );
+       /* Explicit Bind requests always get their own conn */
+       if ( !( sendok & LDAP_BACK_BINDING ) ) {
+               /* Searches for a ldapconn in the avl tree */
+retry_lock:
+               ldap_pvt_thread_mutex_lock( &li->li_conninfo.lai_mutex );
 
-       lc = (ldapconn_t *)avl_find( li->li_conninfo.lai_tree, 
-                       (caddr_t)&lc_curr, ldap_back_conn_cmp );
-       if ( lc != NULL ) {
-               refcnt = ++lc->lc_refcnt;
+               lc = (ldapconn_t *)avl_find( li->li_conninfo.lai_tree, 
+                               (caddr_t)&lc_curr, ldap_back_conn_cmp );
+               if ( lc != NULL ) {
+                       /* Don't reuse connections while they're still binding */
+                       if ( LDAP_BACK_CONN_BINDING( lc ) ) {
+                               ldap_pvt_thread_mutex_unlock( &li->li_conninfo.lai_mutex );
+                               ldap_pvt_thread_yield();
+                               goto retry_lock;
+                       }
+                       refcnt = ++lc->lc_refcnt;
+               }
+               ldap_pvt_thread_mutex_unlock( &li->li_conninfo.lai_mutex );
        }
-       ldap_pvt_thread_mutex_unlock( &li->li_conninfo.lai_mutex );
 
        /* Looks like we didn't get a bind. Open a new session... */
        if ( lc == NULL ) {
                if ( ldap_back_prepare_conn( &lc, op, rs, sendok ) != LDAP_SUCCESS ) {
                        return NULL;
                }
-
+               if ( sendok & LDAP_BACK_BINDING ) {
+                       LDAP_BACK_CONN_BINDING_SET( lc );
+               }
                lc->lc_conn = lc_curr.lc_conn;
                ber_dupbv( &lc->lc_local_ndn, &lc_curr.lc_local_ndn );
 
@@ -581,9 +597,22 @@ ldap_back_getconn( Operation *op, SlapReply *rs, ldap_back_send_t sendok )
                }
 
        } else {
-               Debug( LDAP_DEBUG_TRACE,
-                       "=>ldap_back_getconn: conn %p fetched (refcnt=%u)\n",
-                       (void *)lc, refcnt, 0 );
+               if ( li->li_idle_timeout != 0 && op->o_time > lc->lc_time + li->li_idle_timeout ) {
+                       /* in case of failure, it frees/taints lc and sets it to NULL */
+                       if ( ldap_back_retry( &lc, op, rs, sendok ) ) {
+                               lc = NULL;
+                       }
+               }
+
+               if ( lc ) {
+                       Debug( LDAP_DEBUG_TRACE,
+                               "=>ldap_back_getconn: conn %p fetched (refcnt=%u)\n",
+                               (void *)lc, refcnt, 0 );
+               }
+       }
+
+       if ( li->li_idle_timeout && lc ) {
+               lc->lc_time = op->o_time;
        }
 
 done:;
@@ -591,17 +620,23 @@ done:;
 }
 
 void
-ldap_back_release_conn(
+ldap_back_release_conn_lock(
        Operation               *op,
        SlapReply               *rs,
-       ldapconn_t              *lc )
+       ldapconn_t              *lc,
+       int                     dolock )
 {
        ldapinfo_t      *li = (ldapinfo_t *)op->o_bd->be_private;
 
-       ldap_pvt_thread_mutex_lock( &li->li_conninfo.lai_mutex );
+       if ( dolock ) {
+               ldap_pvt_thread_mutex_lock( &li->li_conninfo.lai_mutex );
+       }
        assert( lc->lc_refcnt > 0 );
        lc->lc_refcnt--;
-       ldap_pvt_thread_mutex_unlock( &li->li_conninfo.lai_mutex );
+       LDAP_BACK_CONN_BINDING_CLEAR( lc );
+       if ( dolock ) {
+               ldap_pvt_thread_mutex_unlock( &li->li_conninfo.lai_mutex );
+       }
 }
 
 /*
@@ -633,6 +668,22 @@ ldap_back_dobind_int(
                return rc;
        }
 
+       while ( lc->lc_refcnt > 1 ) {
+               ldap_pvt_thread_yield();
+               rc = LDAP_BACK_CONN_ISBOUND( lc );
+               if ( rc ) {
+                       return rc;
+               }
+       }
+
+       if ( dolock ) {
+               ldap_pvt_thread_mutex_lock( &li->li_conninfo.lai_mutex );
+       }
+       LDAP_BACK_CONN_BINDING_SET( lc );
+       if ( dolock ) {
+               ldap_pvt_thread_mutex_unlock( &li->li_conninfo.lai_mutex );
+       }
+
        /*
         * FIXME: we need to let clients use proxyAuthz
         * otherwise we cannot do symmetric pools of servers;
@@ -660,7 +711,7 @@ ldap_back_dobind_int(
                        ( BER_BVISNULL( &lc->lc_bound_ndn ) ||
                          ( li->li_idassert_flags & LDAP_BACK_AUTH_OVERRIDE ) ) )
        {
-               (void)ldap_back_proxy_authz_bind( lc, op, rs );
+               (void)ldap_back_proxy_authz_bind( lc, op, rs, sendok );
                goto done;
        }
 
@@ -749,13 +800,14 @@ retry:;
        rc = ldap_back_op_result( lc, op, rs, msgid, 0, sendok );
        if ( rc == LDAP_SUCCESS ) {
                LDAP_BACK_CONN_ISBOUND_SET( lc );
-
-       } else {
-               ldap_back_release_conn( op, rs, lc );
        }
 
 done:;
+       LDAP_BACK_CONN_BINDING_CLEAR( lc );
        rc = LDAP_BACK_CONN_ISBOUND( lc );
+       if ( !rc ) {
+               ldap_back_release_conn_lock( op, rs, lc, dolock );
+       }
 
        return rc;
 }
@@ -838,7 +890,7 @@ ldap_back_op_result(
 
 retry:;
                /* if result parsing fails, note the failure reason */
-               rc = ldap_result( lc->lc_ld, msgid, 1, &tv, &res );
+               rc = ldap_result( lc->lc_ld, msgid, LDAP_MSG_ALL, &tv, &res );
                switch ( rc ) {
                case 0:
                        if ( timeout ) {
@@ -925,6 +977,9 @@ ldap_back_retry( ldapconn_t **lcp, Operation *op, SlapReply *rs, ldap_back_send_
        int             rc = 0;
        ldapinfo_t      *li = (ldapinfo_t *)op->o_bd->be_private;
 
+       assert( lcp != NULL );
+       assert( *lcp != NULL );
+
        ldap_pvt_thread_mutex_lock( &li->li_conninfo.lai_mutex );
 
        if ( (*lcp)->lc_refcnt == 1 ) {
@@ -940,7 +995,11 @@ ldap_back_retry( ldapconn_t **lcp, Operation *op, SlapReply *rs, ldap_back_send_
 
                /* lc here must be the regular lc, reset and ready for init */
                rc = ldap_back_prepare_conn( lcp, op, rs, sendok );
-               if ( rc == LDAP_SUCCESS ) {
+               if ( rc != LDAP_SUCCESS ) {
+                       rc = 0;
+                       *lcp = NULL;
+
+               } else {
                        rc = ldap_back_dobind_int( *lcp, op, rs, sendok, 0, 0 );
                        if ( rc == 0 ) {
                                *lcp = NULL;
@@ -954,15 +1013,23 @@ ldap_back_retry( ldapconn_t **lcp, Operation *op, SlapReply *rs, ldap_back_send_
 }
 
 static int
-ldap_back_proxy_authz_bind( ldapconn_t *lc, Operation *op, SlapReply *rs )
+ldap_back_proxy_authz_bind( ldapconn_t *lc, Operation *op, SlapReply *rs, ldap_back_send_t sendok )
 {
        ldapinfo_t      *li = (ldapinfo_t *)op->o_bd->be_private;
        struct berval   binddn = slap_empty_bv;
        struct berval   bindcred = slap_empty_bv;
+       struct berval   ndn;
        int             dobind = 0;
        int             msgid;
        int             rc;
 
+       if ( !BER_BVISNULL( &op->o_conn->c_ndn ) ) {
+               ndn = op->o_conn->c_ndn;
+
+       } else {
+               ndn = op->o_ndn;
+       }
+
        /*
         * FIXME: we need to let clients use proxyAuthz
         * otherwise we cannot do symmetric pools of servers;
@@ -988,7 +1055,7 @@ ldap_back_proxy_authz_bind( ldapconn_t *lc, Operation *op, SlapReply *rs )
         * is authorized */
        switch ( li->li_idassert_mode ) {
        case LDAP_BACK_IDASSERT_LEGACY:
-               if ( !BER_BVISNULL( &op->o_conn->c_ndn ) && !BER_BVISEMPTY( &op->o_conn->c_ndn ) ) {
+               if ( !BER_BVISNULL( &ndn ) && !BER_BVISEMPTY( &ndn ) ) {
                        if ( !BER_BVISNULL( &li->li_idassert_authcDN ) && !BER_BVISEMPTY( &li->li_idassert_authcDN ) )
                        {
                                binddn = li->li_idassert_authcDN;
@@ -1000,20 +1067,39 @@ ldap_back_proxy_authz_bind( ldapconn_t *lc, Operation *op, SlapReply *rs )
 
        default:
                /* NOTE: rootdn can always idassert */
-               if ( li->li_idassert_authz && !be_isroot( op ) ) {
+               if ( BER_BVISNULL( &ndn ) && li->li_idassert_authz == NULL ) {
+                       if ( li->li_idassert_flags & LDAP_BACK_AUTH_PRESCRIPTIVE ) {
+                               rs->sr_err = LDAP_INAPPROPRIATE_AUTH;
+                               if ( sendok & LDAP_BACK_SENDERR ) {
+                                       send_ldap_result( op, rs );
+                               }
+                               LDAP_BACK_CONN_ISBOUND_CLEAR( lc );
+
+                       } else {
+                               rs->sr_err = LDAP_SUCCESS;
+                               binddn = slap_empty_bv;
+                               bindcred = slap_empty_bv;
+                               break;
+                       }
+
+                       goto done;
+
+               } else if ( li->li_idassert_authz && !be_isroot( op ) ) {
                        struct berval authcDN;
 
-                       if ( BER_BVISNULL( &op->o_conn->c_ndn ) ) {
+                       if ( BER_BVISNULL( &ndn ) ) {
                                authcDN = slap_empty_bv;
 
                        } else {
-                               authcDN = op->o_conn->c_ndn;
+                               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 ) {
-                                       send_ldap_result( op, rs );
+                                       if ( sendok & LDAP_BACK_SENDERR ) {
+                                               send_ldap_result( op, rs );
+                                       }
                                        LDAP_BACK_CONN_ISBOUND_CLEAR( lc );
 
                                } else {
@@ -1054,16 +1140,16 @@ ldap_back_proxy_authz_bind( ldapconn_t *lc, Operation *op, SlapReply *rs )
                                break;
 
                        case LDAP_BACK_IDASSERT_SELF:
-                               if ( BER_BVISNULL( &op->o_conn->c_ndn ) ) {
+                               if ( BER_BVISNULL( &ndn ) ) {
                                        /* connection is not authc'd, so don't idassert */
                                        BER_BVSTR( &authzID, "dn:" );
                                        break;
                                }
-                               authzID.bv_len = STRLENOF( "dn:" ) + op->o_conn->c_ndn.bv_len;
+                               authzID.bv_len = STRLENOF( "dn:" ) + ndn.bv_len;
                                authzID.bv_val = slap_sl_malloc( authzID.bv_len + 1, op->o_tmpmemctx );
                                AC_MEMCPY( authzID.bv_val, "dn:", STRLENOF( "dn:" ) );
                                AC_MEMCPY( authzID.bv_val + STRLENOF( "dn:" ),
-                                               op->o_conn->c_ndn.bv_val, op->o_conn->c_ndn.bv_len + 1 );
+                                               ndn.bv_val, ndn.bv_len + 1 );
                                freeauthz = 1;
                                break;
 
@@ -1078,7 +1164,10 @@ ldap_back_proxy_authz_bind( ldapconn_t *lc, Operation *op, SlapReply *rs )
                                (void *)li->li_idassert_secprops );
 
                        if ( rs->sr_err != LDAP_OPT_SUCCESS ) {
-                               send_ldap_result( op, rs );
+                               rs->sr_err = LDAP_OTHER;
+                               if ( sendok & LDAP_BACK_SENDERR ) {
+                                       send_ldap_result( op, rs );
+                               }
                                LDAP_BACK_CONN_ISBOUND_CLEAR( lc );
                                goto done;
                        }
@@ -1099,7 +1188,9 @@ ldap_back_proxy_authz_bind( ldapconn_t *lc, Operation *op, SlapReply *rs )
                rs->sr_err = slap_map_api2result( rs );
                if ( rs->sr_err != LDAP_SUCCESS ) {
                        LDAP_BACK_CONN_ISBOUND_CLEAR( lc );
-                       send_ldap_result( op, rs );
+                       if ( sendok & LDAP_BACK_SENDERR ) {
+                               send_ldap_result( op, rs );
+                       }
 
                } else {
                        LDAP_BACK_CONN_ISBOUND_SET( lc );
@@ -1129,11 +1220,13 @@ ldap_back_proxy_authz_bind( ldapconn_t *lc, Operation *op, SlapReply *rs )
                /* unsupported! */
                LDAP_BACK_CONN_ISBOUND_CLEAR( lc );
                rs->sr_err = LDAP_AUTH_METHOD_NOT_SUPPORTED;
-               send_ldap_result( op, rs );
+               if ( sendok & LDAP_BACK_SENDERR ) {
+                       send_ldap_result( op, rs );
+               }
                goto done;
        }
 
-       rc = ldap_back_op_result( lc, op, rs, msgid, 0, LDAP_BACK_SENDERR );
+       rc = ldap_back_op_result( lc, op, rs, msgid, 0, sendok );
        if ( rc == LDAP_SUCCESS ) {
                LDAP_BACK_CONN_ISBOUND_SET( lc );
        }
@@ -1178,7 +1271,8 @@ ldap_back_proxy_authz_ctrl(
        LDAPControl     **ctrls = NULL;
        int             i = 0,
                        mode;
-       struct berval   assertedID;
+       struct berval   assertedID,
+                       ndn;
 
        *pctrls = NULL;
 
@@ -1197,6 +1291,13 @@ ldap_back_proxy_authz_ctrl(
                goto done;
        }
 
+       if ( !BER_BVISNULL( &op->o_conn->c_ndn ) ) {
+               ndn = op->o_conn->c_ndn;
+
+       } else {
+               ndn = op->o_ndn;
+       }
+
        if ( li->li_idassert_mode == LDAP_BACK_IDASSERT_LEGACY ) {
                if ( op->o_proxy_authz ) {
                        /*
@@ -1220,7 +1321,7 @@ ldap_back_proxy_authz_ctrl(
                        goto done;
                }
 
-               if ( BER_BVISNULL( &op->o_conn->c_ndn ) ) {
+               if ( BER_BVISNULL( &ndn ) ) {
                        goto done;
                }
 
@@ -1230,13 +1331,13 @@ ldap_back_proxy_authz_ctrl(
 
        } else if ( li->li_idassert_authmethod == LDAP_AUTH_SASL ) {
                if ( ( li->li_idassert_flags & LDAP_BACK_AUTH_NATIVE_AUTHZ )
-                               /* && ( !BER_BVISNULL( &op->o_conn->c_ndn )
+                               /* && ( !BER_BVISNULL( &ndn )
                                        || LDAP_BACK_CONN_ISBOUND( lc ) ) */ )
                {
                        /* already asserted in SASL via native authz */
                        /* NOTE: the test on lc->lc_bound is used to trap
                         * native authorization of anonymous users,
-                        * since in that case op->o_conn->c_ndn is NULL */
+                        * since in that case ndn is NULL */
                        goto done;
                }
 
@@ -1244,17 +1345,17 @@ ldap_back_proxy_authz_ctrl(
                int             rc;
                struct berval authcDN;
 
-               if ( BER_BVISNULL( &op->o_conn->c_ndn ) ) {
+               if ( BER_BVISNULL( &ndn ) ) {
                        authcDN = slap_empty_bv;
                } else {
-                       authcDN = op->o_conn->c_ndn;
+                       authcDN = ndn;
                }
                rc = slap_sasl_matches( op, li->li_idassert_authz,
                                &authcDN, & authcDN );
                if ( rc != LDAP_SUCCESS ) {
                        if ( li->li_idassert_flags & LDAP_BACK_AUTH_PRESCRIPTIVE )
                        {
-                               /* op->o_conn->c_ndn is not authorized
+                               /* ndn is not authorized
                                 * to use idassert */
                                return rc;
                        }
@@ -1292,14 +1393,20 @@ ldap_back_proxy_authz_ctrl(
        }
 
        switch ( mode ) {
-       case LDAP_BACK_IDASSERT_LEGACY:
        case LDAP_BACK_IDASSERT_SELF:
+               if ( BER_BVISNULL( &ndn ) ) {
+                       goto done;
+               }
+               assertedID = ndn;
+               break;
+
+       case LDAP_BACK_IDASSERT_LEGACY:
                /* original behavior:
                 * assert the client's identity */
-               if ( BER_BVISNULL( &op->o_conn->c_ndn ) ) {
+               if ( BER_BVISNULL( &ndn ) ) {
                        assertedID = slap_empty_bv;
                } else {
-                       assertedID = op->o_conn->c_ndn;
+                       assertedID = ndn;
                }
                break;
 
@@ -1331,8 +1438,9 @@ ldap_back_proxy_authz_ctrl(
                        /* just count ctrls */ ;
        }
 
-       ctrls = ch_malloc( sizeof( LDAPControl * ) * (i + 2) );
-       ctrls[ 0 ] = ch_malloc( sizeof( LDAPControl ) );
+       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;
@@ -1341,13 +1449,14 @@ ldap_back_proxy_authz_ctrl(
        /* already in u:ID or dn:DN form */
        case LDAP_BACK_IDASSERT_OTHERID:
        case LDAP_BACK_IDASSERT_OTHERDN:
-               ber_dupbv( &ctrls[ 0 ]->ldctl_value, &assertedID );
+               ber_dupbv_x( &ctrls[ 0 ]->ldctl_value, &assertedID, op->o_tmpmemctx );
                break;
 
        /* needs the dn: prefix */
        default:
                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 );
+               ctrls[ 0 ]->ldctl_value.bv_val = op->o_tmpalloc( ctrls[ 0 ]->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:" ) ],
                                assertedID.bv_val, assertedID.bv_len + 1 );
@@ -1383,11 +1492,10 @@ ldap_back_proxy_authz_ctrl_free( Operation *op, LDAPControl ***pctrls )
                assert( ctrls[ 0 ] != NULL );
 
                if ( !BER_BVISNULL( &ctrls[ 0 ]->ldctl_value ) ) {
-                       free( ctrls[ 0 ]->ldctl_value.bv_val );
+                       op->o_tmpfree( ctrls[ 0 ]->ldctl_value.bv_val, op->o_tmpmemctx );
                }
 
-               free( ctrls[ 0 ] );
-               free( ctrls );
+               op->o_tmpfree( ctrls, op->o_tmpmemctx );
        } 
 
        *pctrls = NULL;