]> git.sur5r.net Git - openldap/blobdiff - servers/slapd/back-ldap/bind.c
Partially revert prev commit, leave rs->sr_err == SLAPD_ABANDON
[openldap] / servers / slapd / back-ldap / bind.c
index ebc51bdbd0da072fa4c774d009ddc35a8d4200ea..9691f11f6df9a1c60e2c1f802f4e47c693e4ff03 100644 (file)
@@ -2,7 +2,7 @@
 /* $OpenLDAP$ */
 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
  *
- * Copyright 1999-2007 The OpenLDAP Foundation.
+ * Copyright 1999-2009 The OpenLDAP Foundation.
  * Portions Copyright 2000-2003 Pierangelo Masarati.
  * Portions Copyright 1999-2003 Howard Chu.
  * All rights reserved.
@@ -173,7 +173,10 @@ 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;
 
@@ -205,11 +208,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,
@@ -222,6 +241,8 @@ retry:;
                }
        }
 
+       ldap_back_controls_free( op, rs, &ctrls );
+
        if ( rc == LDAP_SUCCESS ) {
                /* If defined, proxyAuthz will be used also when
                 * back-ldap is the authorizing backend; for this
@@ -1207,6 +1228,8 @@ ldap_back_dobind_cb(
 {
        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;
 }
 
@@ -1352,6 +1375,14 @@ retry_lock:;
                                li->li_acl_authcID.bv_val,
                                li->li_acl_passwd.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,
@@ -1361,15 +1392,26 @@ retry_lock:;
 
                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 ) ) {
@@ -1433,12 +1475,12 @@ retry:;
                        if ( op->o_callback == &cb )
                                op->o_callback = cb.sc_next;
                        op->o_tag = o_tag;
-                       rs->sr_text = "Internal proxy bind failure";
+                       rs->sr_text = "Proxy can't contact remote server";
                        send_ldap_result( op, rs );
                }
 
                rc = 0;
-               goto leave;
+               goto func_leave;
        }
 
        rc = ldap_back_op_result( lc, op, rs, msgid,
@@ -1457,7 +1499,7 @@ done:;
                ldap_set_rebind_proc( lc->lc_ld, li->li_rebind_f, lc );
        }
 
-leave:;
+func_leave:;
        if ( op->o_callback == &cb )
                op->o_callback = cb.sc_next;
        op->o_tag = o_tag;
@@ -1595,8 +1637,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 +1645,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;
@@ -1758,7 +1798,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 ) 
@@ -1783,8 +1823,8 @@ retry:;
                }
 
        } 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 );
        }
@@ -1817,7 +1857,7 @@ retry:;
                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 */
@@ -2091,21 +2131,40 @@ 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 );
 
-               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;
                }
 
                lutil_sasl_freedefs( defaults );
@@ -2147,7 +2206,9 @@ 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 );
+               if ( !BER_BVISNULL( binddn ) ) {
+                       ber_bvreplace( &lc->lc_bound_ndn, binddn );
+               }
 
                if ( !BER_BVISNULL( &lc->lc_cred ) ) {
                        memset( lc->lc_cred.bv_val, 0,
@@ -2155,8 +2216,10 @@ 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;
@@ -2194,44 +2257,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;
        }
@@ -2298,7 +2343,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
@@ -2375,33 +2420,25 @@ 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;
 
        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;
        }
 
@@ -2410,7 +2447,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;
@@ -2424,32 +2461,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;
                }
@@ -2469,30 +2503,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;
+}
+
+/*
+ * 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 */
 
-               ctrls[ 0 ]->ldctl_oid = LDAP_CONTROL_OBSOLETE_PROXY_AUTHZ;
+       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 <draft-wahl-ldap-session>, 
+        * 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 ) {
@@ -2505,18 +2663,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 );