+ return( ERR_OK( rs->sr_err ) ? LDAP_SUCCESS : rs->sr_err );
+}
+
+/* return true if bound, false if failed */
+int
+ldap_back_retry( ldapconn_t **lcp, Operation *op, SlapReply *rs, ldap_back_send_t sendok )
+{
+ 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 ) {
+ Debug( LDAP_DEBUG_ANY,
+ "%s ldap_back_retry: retrying URI=\"%s\" DN=\"%s\"\n",
+ op->o_log_prefix, li->li_uri,
+ BER_BVISNULL( &(*lcp)->lc_bound_ndn ) ?
+ "" : (*lcp)->lc_bound_ndn.bv_val );
+
+ ldap_unbind_ext( (*lcp)->lc_ld, NULL, NULL );
+ (*lcp)->lc_ld = NULL;
+ LDAP_BACK_CONN_ISBOUND_CLEAR( (*lcp) );
+
+ /* 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 ) {
+ rc = 0;
+ *lcp = NULL;
+
+ } else {
+ rc = ldap_back_dobind_int( *lcp, op, rs, sendok, 0, 0 );
+ if ( rc == 0 ) {
+ *lcp = NULL;
+ }
+ }
+
+ } else {
+ Debug( LDAP_DEBUG_TRACE,
+ "ldap_back_retry: conn %p refcnt=%u unable to retry.\n",
+ (void *)(*lcp), (*lcp)->lc_refcnt, 0 );
+
+ ldap_back_release_conn_lock( op, rs, *lcp, 0 );
+ *lcp = NULL;
+
+ if ( sendok ) {
+ rs->sr_err = LDAP_UNAVAILABLE;
+ rs->sr_text = "unable to retry";
+ send_ldap_result( op, rs );
+ }
+ }
+
+ ldap_pvt_thread_mutex_unlock( &li->li_conninfo.lai_mutex );
+
+ return rc;
+}
+
+static int
+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;
+ * we have to live with the fact that a user can
+ * authorize itself as any ID that is allowed
+ * by the authzTo directive of the "proxyauthzdn".
+ */
+ /*
+ * NOTE: current Proxy Authorization specification
+ * and implementation do not allow proxy authorization
+ * control to be provided with Bind requests
+ */
+ /*
+ * if no bind took place yet, but the connection is bound
+ * and the "proxyauthzdn" is set, then bind as
+ * "proxyauthzdn" and explicitly add the proxyAuthz
+ * control to every operation with the dn bound
+ * to the connection as control value.
+ */
+
+ /* bind as proxyauthzdn only if no idassert mode
+ * is requested, or if the client's identity
+ * is authorized */
+ switch ( li->li_idassert_mode ) {
+ case LDAP_BACK_IDASSERT_LEGACY:
+ 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;
+ bindcred = li->li_idassert_passwd;
+ dobind = 1;
+ }
+ }
+ break;
+
+ default:
+ /* NOTE: rootdn can always idassert */
+ 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( &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 );
+ }
+ LDAP_BACK_CONN_ISBOUND_CLEAR( lc );
+
+ } else {
+ rs->sr_err = LDAP_SUCCESS;
+ binddn = slap_empty_bv;
+ bindcred = slap_empty_bv;
+ break;
+ }
+
+ goto done;
+ }
+ }
+
+ binddn = li->li_idassert_authcDN;
+ bindcred = li->li_idassert_passwd;
+ dobind = 1;
+ break;
+ }
+
+ if ( dobind && li->li_idassert_authmethod == LDAP_AUTH_SASL ) {
+#ifdef HAVE_CYRUS_SASL
+ void *defaults = NULL;
+ struct berval authzID = BER_BVNULL;
+ int freeauthz = 0;
+
+ /* if SASL supports native authz, prepare for it */
+ if ( ( !op->o_do_not_cache || !op->o_is_auth_check ) &&
+ ( li->li_idassert_flags & LDAP_BACK_AUTH_NATIVE_AUTHZ ) )
+ {
+ switch ( li->li_idassert_mode ) {
+ case LDAP_BACK_IDASSERT_OTHERID:
+ case LDAP_BACK_IDASSERT_OTHERDN:
+ authzID = li->li_idassert_authzID;
+ break;
+
+ case LDAP_BACK_IDASSERT_ANONYMOUS:
+ BER_BVSTR( &authzID, "dn:" );
+ break;
+
+ case LDAP_BACK_IDASSERT_SELF:
+ if ( BER_BVISNULL( &ndn ) ) {
+ /* connection is not authc'd, so don't idassert */
+ BER_BVSTR( &authzID, "dn:" );
+ break;
+ }
+ 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:" ),
+ ndn.bv_val, ndn.bv_len + 1 );
+ freeauthz = 1;
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ if ( li->li_idassert_secprops != NULL ) {
+ rs->sr_err = ldap_set_option( lc->lc_ld,
+ LDAP_OPT_X_SASL_SECPROPS,
+ (void *)li->li_idassert_secprops );
+
+ if ( rs->sr_err != LDAP_OPT_SUCCESS ) {
+ rs->sr_err = LDAP_OTHER;
+ if ( sendok & LDAP_BACK_SENDERR ) {
+ send_ldap_result( op, rs );
+ }
+ LDAP_BACK_CONN_ISBOUND_CLEAR( lc );
+ goto done;
+ }
+ }
+
+ defaults = lutil_sasl_defaults( lc->lc_ld,
+ li->li_idassert_sasl_mech.bv_val,
+ li->li_idassert_sasl_realm.bv_val,
+ li->li_idassert_authcID.bv_val,
+ li->li_idassert_passwd.bv_val,
+ authzID.bv_val );
+
+ 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 ) {
+ LDAP_BACK_CONN_ISBOUND_CLEAR( lc );
+ if ( sendok & LDAP_BACK_SENDERR ) {
+ send_ldap_result( op, rs );
+ }
+
+ } else {
+ LDAP_BACK_CONN_ISBOUND_SET( lc );
+ }
+
+ lutil_sasl_freedefs( defaults );
+ if ( freeauthz ) {
+ slap_sl_free( authzID.bv_val, op->o_tmpmemctx );
+ }
+
+ goto done;
+#endif /* HAVE_CYRUS_SASL */
+ }
+
+ switch ( li->li_idassert_authmethod ) {
+ case LDAP_AUTH_NONE:
+ LDAP_BACK_CONN_ISBOUND_SET( lc );
+ goto done;
+
+ case LDAP_AUTH_SIMPLE:
+ rs->sr_err = ldap_sasl_bind( lc->lc_ld,
+ binddn.bv_val, LDAP_SASL_SIMPLE,
+ &bindcred, NULL, NULL, &msgid );
+ break;
+
+ default:
+ /* unsupported! */
+ LDAP_BACK_CONN_ISBOUND_CLEAR( lc );
+ rs->sr_err = LDAP_AUTH_METHOD_NOT_SUPPORTED;
+ if ( sendok & LDAP_BACK_SENDERR ) {
+ send_ldap_result( op, rs );
+ }
+ goto done;
+ }
+
+ rc = ldap_back_op_result( lc, op, rs, msgid, 0, sendok );
+ if ( rc == LDAP_SUCCESS ) {
+ LDAP_BACK_CONN_ISBOUND_SET( lc );
+ }
+done:;
+ return LDAP_BACK_CONN_ISBOUND( lc );