Back to index

openldap  2.4.31
bind.c
Go to the documentation of this file.
00001 /* $OpenLDAP$ */
00002 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
00003  *
00004  * Copyright 1999-2012 The OpenLDAP Foundation.
00005  * Portions Copyright 2001-2003 Pierangelo Masarati.
00006  * Portions Copyright 1999-2003 Howard Chu.
00007  * All rights reserved.
00008  *
00009  * Redistribution and use in source and binary forms, with or without
00010  * modification, are permitted only as authorized by the OpenLDAP
00011  * Public License.
00012  *
00013  * A copy of this license is available in the file LICENSE in the
00014  * top-level directory of the distribution or, alternatively, at
00015  * <http://www.OpenLDAP.org/license.html>.
00016  */
00017 /* ACKNOWLEDGEMENTS:
00018  * This work was initially developed by the Howard Chu for inclusion
00019  * in OpenLDAP Software and subsequently enhanced by Pierangelo
00020  * Masarati.
00021  */
00022 
00023 #include "portable.h"
00024 
00025 #include <stdio.h>
00026 
00027 #include <ac/errno.h>
00028 #include <ac/socket.h>
00029 #include <ac/string.h>
00030 
00031 
00032 #define AVL_INTERNAL
00033 #include "slap.h"
00034 #include "../back-ldap/back-ldap.h"
00035 #include "back-meta.h"
00036 
00037 #include "lutil_ldap.h"
00038 
00039 static int
00040 meta_back_proxy_authz_bind(
00041        metaconn_t           *mc,
00042        int                  candidate,
00043        Operation            *op,
00044        SlapReply            *rs,
00045        ldap_back_send_t     sendok,
00046        int                  dolock );
00047 
00048 static int
00049 meta_back_single_bind(
00050        Operation            *op,
00051        SlapReply            *rs,
00052        metaconn_t           *mc,
00053        int                  candidate );
00054 
00055 int
00056 meta_back_bind( Operation *op, SlapReply *rs )
00057 {
00058        metainfo_t    *mi = ( metainfo_t * )op->o_bd->be_private;
00059        metaconn_t    *mc = NULL;
00060 
00061        int           rc = LDAP_OTHER,
00062                      i,
00063                      gotit = 0,
00064                      isroot = 0;
00065 
00066        SlapReply     *candidates;
00067 
00068        rs->sr_err = LDAP_SUCCESS;
00069 
00070        Debug( LDAP_DEBUG_ARGS, "%s meta_back_bind: dn=\"%s\".\n",
00071               op->o_log_prefix, op->o_req_dn.bv_val, 0 );
00072 
00073        /* the test on the bind method should be superfluous */
00074        switch ( be_rootdn_bind( op, rs ) ) {
00075        case LDAP_SUCCESS:
00076               if ( META_BACK_DEFER_ROOTDN_BIND( mi ) ) {
00077                      /* frontend will return success */
00078                      return rs->sr_err;
00079               }
00080 
00081               isroot = 1;
00082               /* fallthru */
00083 
00084        case SLAP_CB_CONTINUE:
00085               break;
00086 
00087        default:
00088               /* be_rootdn_bind() sent result */
00089               return rs->sr_err;
00090        }
00091 
00092        /* we need meta_back_getconn() not send result even on error,
00093         * because we want to intercept the error and make it
00094         * invalidCredentials */
00095        mc = meta_back_getconn( op, rs, NULL, LDAP_BACK_BIND_DONTSEND );
00096        if ( !mc ) {
00097               if ( LogTest( LDAP_DEBUG_ANY ) ) {
00098                      char   buf[ SLAP_TEXT_BUFLEN ];
00099 
00100                      snprintf( buf, sizeof( buf ),
00101                             "meta_back_bind: no target "
00102                             "for dn \"%s\" (%d%s%s).",
00103                             op->o_req_dn.bv_val, rs->sr_err,
00104                             rs->sr_text ? ". " : "",
00105                             rs->sr_text ? rs->sr_text : "" );
00106                      Debug( LDAP_DEBUG_ANY,
00107                             "%s %s\n",
00108                             op->o_log_prefix, buf, 0 );
00109               }
00110 
00111               /* FIXME: there might be cases where we don't want
00112                * to map the error onto invalidCredentials */
00113               switch ( rs->sr_err ) {
00114               case LDAP_NO_SUCH_OBJECT:
00115               case LDAP_UNWILLING_TO_PERFORM:
00116                      rs->sr_err = LDAP_INVALID_CREDENTIALS;
00117                      rs->sr_text = NULL;
00118                      break;
00119               }
00120               send_ldap_result( op, rs );
00121               return rs->sr_err;
00122        }
00123 
00124        candidates = meta_back_candidates_get( op );
00125 
00126        /*
00127         * Each target is scanned ...
00128         */
00129        mc->mc_authz_target = META_BOUND_NONE;
00130        for ( i = 0; i < mi->mi_ntargets; i++ ) {
00131               metatarget_t  *mt = mi->mi_targets[ i ];
00132               int           lerr;
00133 
00134               /*
00135                * Skip non-candidates
00136                */
00137               if ( !META_IS_CANDIDATE( &candidates[ i ] ) ) {
00138                      continue;
00139               }
00140 
00141               if ( gotit == 0 ) {
00142                      /* set rc to LDAP_SUCCESS only if at least
00143                       * one candidate has been tried */
00144                      rc = LDAP_SUCCESS;
00145                      gotit = 1;
00146 
00147               } else if ( !isroot ) {
00148                      /*
00149                       * A bind operation is expected to have
00150                       * ONE CANDIDATE ONLY!
00151                       */
00152                      Debug( LDAP_DEBUG_ANY,
00153                             "### %s meta_back_bind: more than one"
00154                             " candidate selected...\n",
00155                             op->o_log_prefix, 0, 0 );
00156               }
00157 
00158               if ( isroot ) {
00159                      if ( mt->mt_idassert_authmethod == LDAP_AUTH_NONE
00160                             || BER_BVISNULL( &mt->mt_idassert_authcDN ) )
00161                      {
00162                             metasingleconn_t     *msc = &mc->mc_conns[ i ];
00163 
00164                             /* skip the target if no pseudorootdn is provided */
00165                             if ( !BER_BVISNULL( &msc->msc_bound_ndn ) ) {
00166                                    ch_free( msc->msc_bound_ndn.bv_val );
00167                                    BER_BVZERO( &msc->msc_bound_ndn );
00168                             }
00169 
00170                             if ( !BER_BVISNULL( &msc->msc_cred ) ) {
00171                                    /* destroy sensitive data */
00172                                    memset( msc->msc_cred.bv_val, 0,
00173                                           msc->msc_cred.bv_len );
00174                                    ch_free( msc->msc_cred.bv_val );
00175                                    BER_BVZERO( &msc->msc_cred );
00176                             }
00177 
00178                             continue;
00179                      }
00180 
00181                      
00182                      (void)meta_back_proxy_authz_bind( mc, i, op, rs, LDAP_BACK_DONTSEND, 1 );
00183                      lerr = rs->sr_err;
00184 
00185               } else {
00186                      lerr = meta_back_single_bind( op, rs, mc, i );
00187               }
00188 
00189               if ( lerr != LDAP_SUCCESS ) {
00190                      rc = rs->sr_err = lerr;
00191 
00192                      /* FIXME: in some cases (e.g. unavailable)
00193                       * do not assume it's not candidate; rather
00194                       * mark this as an error to be eventually
00195                       * reported to client */
00196                      META_CANDIDATE_CLEAR( &candidates[ i ] );
00197                      break;
00198               }
00199        }
00200 
00201        /* must re-insert if local DN changed as result of bind */
00202        if ( rc == LDAP_SUCCESS ) {
00203               if ( isroot ) {
00204                      mc->mc_authz_target = META_BOUND_ALL;
00205               }
00206 
00207               if ( !LDAP_BACK_PCONN_ISPRIV( mc )
00208                      && !dn_match( &op->o_req_ndn, &mc->mc_local_ndn ) )
00209               {
00210                      int           lerr;
00211 
00212                      /* wait for all other ops to release the connection */
00213                      ldap_pvt_thread_mutex_lock( &mi->mi_conninfo.lai_mutex );
00214                      assert( mc->mc_refcnt == 1 );
00215 #if META_BACK_PRINT_CONNTREE > 0
00216                      meta_back_print_conntree( mi, ">>> meta_back_bind" );
00217 #endif /* META_BACK_PRINT_CONNTREE */
00218 
00219                      /* delete all cached connections with the current connection */
00220                      if ( LDAP_BACK_SINGLECONN( mi ) ) {
00221                             metaconn_t    *tmpmc;
00222 
00223                             while ( ( tmpmc = avl_delete( &mi->mi_conninfo.lai_tree, (caddr_t)mc, meta_back_conn_cmp ) ) != NULL )
00224                             {
00225                                    assert( !LDAP_BACK_PCONN_ISPRIV( mc ) );
00226                                    Debug( LDAP_DEBUG_TRACE,
00227                                           "=>meta_back_bind: destroying conn %lu (refcnt=%u)\n",
00228                                           mc->mc_conn->c_connid, mc->mc_refcnt, 0 );
00229 
00230                                    if ( tmpmc->mc_refcnt != 0 ) {
00231                                           /* taint it */
00232                                           LDAP_BACK_CONN_TAINTED_SET( tmpmc );
00233 
00234                                    } else {
00235                                           /*
00236                                            * Needs a test because the handler may be corrupted,
00237                                            * and calling ldap_unbind on a corrupted header results
00238                                            * in a segmentation fault
00239                                            */
00240                                           meta_back_conn_free( tmpmc );
00241                                    }
00242                             }
00243                      }
00244 
00245                      ber_bvreplace( &mc->mc_local_ndn, &op->o_req_ndn );
00246                      lerr = avl_insert( &mi->mi_conninfo.lai_tree, (caddr_t)mc,
00247                             meta_back_conndn_cmp, meta_back_conndn_dup );
00248 #if META_BACK_PRINT_CONNTREE > 0
00249                      meta_back_print_conntree( mi, "<<< meta_back_bind" );
00250 #endif /* META_BACK_PRINT_CONNTREE */
00251                      if ( lerr == 0 ) {
00252 #if 0
00253                             /* NOTE: a connection cannot be privileged
00254                              * and be in the avl tree at the same time
00255                              */
00256                             if ( isroot ) {
00257                                    LDAP_BACK_CONN_ISPRIV_SET( mc );
00258                                    LDAP_BACK_PCONN_SET( mc, op );
00259                             }
00260 #endif
00261                             LDAP_BACK_CONN_CACHED_SET( mc );
00262 
00263                      } else {
00264                             LDAP_BACK_CONN_CACHED_CLEAR( mc );
00265                      }
00266                      ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex );
00267               }
00268        }
00269 
00270        if ( mc != NULL ) {
00271               meta_back_release_conn( mi, mc );
00272        }
00273 
00274        /*
00275         * rc is LDAP_SUCCESS if at least one bind succeeded,
00276         * err is the last error that occurred during a bind;
00277         * if at least (and at most?) one bind succeeds, fine.
00278         */
00279        if ( rc != LDAP_SUCCESS ) {
00280               
00281               /*
00282                * deal with bind failure ...
00283                */
00284 
00285               /*
00286                * no target was found within the naming context, 
00287                * so bind must fail with invalid credentials
00288                */
00289               if ( rs->sr_err == LDAP_SUCCESS && gotit == 0 ) {
00290                      rs->sr_err = LDAP_INVALID_CREDENTIALS;
00291               } else {
00292                      rs->sr_err = slap_map_api2result( rs );
00293               }
00294               send_ldap_result( op, rs );
00295               return rs->sr_err;
00296 
00297        }
00298 
00299        return LDAP_SUCCESS;
00300 }
00301 
00302 static int
00303 meta_back_bind_op_result(
00304        Operation            *op,
00305        SlapReply            *rs,
00306        metaconn_t           *mc,
00307        int                  candidate,
00308        int                  msgid,
00309        ldap_back_send_t     sendok,
00310        int                  dolock )
00311 {
00312        metainfo_t           *mi = ( metainfo_t * )op->o_bd->be_private;
00313        metatarget_t         *mt = mi->mi_targets[ candidate ];
00314        metasingleconn_t     *msc = &mc->mc_conns[ candidate ];
00315        LDAPMessage          *res;
00316        struct timeval              tv;
00317        int                  rc;
00318        int                  nretries = mt->mt_nretries;
00319        char                 buf[ SLAP_TEXT_BUFLEN ];
00320 
00321        Debug( LDAP_DEBUG_TRACE,
00322               ">>> %s meta_back_bind_op_result[%d]\n",
00323               op->o_log_prefix, candidate, 0 );
00324 
00325        /* make sure this is clean */
00326        assert( rs->sr_ctrls == NULL );
00327 
00328        if ( rs->sr_err == LDAP_SUCCESS ) {
00329               time_t        stoptime = (time_t)(-1),
00330                             timeout;
00331               int           timeout_err = op->o_protocol >= LDAP_VERSION3 ?
00332                             LDAP_ADMINLIMIT_EXCEEDED : LDAP_OTHER;
00333               const char    *timeout_text = "Operation timed out";
00334               slap_op_t     opidx = slap_req2op( op->o_tag );
00335 
00336               /* since timeout is not specified, compute and use
00337                * the one specific to the ongoing operation */
00338               if ( opidx == LDAP_REQ_SEARCH ) {
00339                      if ( op->ors_tlimit <= 0 ) {
00340                             timeout = 0;
00341 
00342                      } else {
00343                             timeout = op->ors_tlimit;
00344                             timeout_err = LDAP_TIMELIMIT_EXCEEDED;
00345                             timeout_text = NULL;
00346                      }
00347 
00348               } else {
00349                      timeout = mt->mt_timeout[ opidx ];
00350               }
00351 
00352               /* better than nothing :) */
00353               if ( timeout == 0 ) {
00354                      if ( mi->mi_idle_timeout ) {
00355                             timeout = mi->mi_idle_timeout;
00356 
00357                      } else if ( mi->mi_conn_ttl ) {
00358                             timeout = mi->mi_conn_ttl;
00359                      }
00360               }
00361 
00362               if ( timeout ) {
00363                      stoptime = op->o_time + timeout;
00364               }
00365 
00366               LDAP_BACK_TV_SET( &tv );
00367 
00368               /*
00369                * handle response!!!
00370                */
00371 retry:;
00372               rc = ldap_result( msc->msc_ld, msgid, LDAP_MSG_ALL, &tv, &res );
00373               switch ( rc ) {
00374               case 0:
00375                      if ( nretries != META_RETRY_NEVER 
00376                             || ( timeout && slap_get_time() <= stoptime ) )
00377                      {
00378                             ldap_pvt_thread_yield();
00379                             if ( nretries > 0 ) {
00380                                    nretries--;
00381                             }
00382                             tv = mt->mt_bind_timeout;
00383                             goto retry;
00384                      }
00385 
00386                      /* don't let anyone else use this handler,
00387                       * because there's a pending bind that will not
00388                       * be acknowledged */
00389                      if ( dolock) {
00390                             ldap_pvt_thread_mutex_lock( &mi->mi_conninfo.lai_mutex );
00391                      }
00392                      assert( LDAP_BACK_CONN_BINDING( msc ) );
00393 
00394 #ifdef DEBUG_205
00395                      Debug( LDAP_DEBUG_ANY, "### %s meta_back_bind_op_result ldap_unbind_ext[%d] ld=%p\n",
00396                             op->o_log_prefix, candidate, (void *)msc->msc_ld );
00397 #endif /* DEBUG_205 */
00398 
00399                      meta_clear_one_candidate( op, mc, candidate );
00400                      if ( dolock ) {
00401                             ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex );
00402                      }
00403 
00404                      rs->sr_err = timeout_err;
00405                      rs->sr_text = timeout_text;
00406                      break;
00407 
00408               case -1:
00409                      ldap_get_option( msc->msc_ld, LDAP_OPT_ERROR_NUMBER,
00410                             &rs->sr_err );
00411 
00412                      snprintf( buf, sizeof( buf ),
00413                             "err=%d (%s) nretries=%d",
00414                             rs->sr_err, ldap_err2string( rs->sr_err ), nretries );
00415                      Debug( LDAP_DEBUG_ANY,
00416                             "### %s meta_back_bind_op_result[%d]: %s.\n",
00417                             op->o_log_prefix, candidate, buf );
00418                      break;
00419 
00420               default:
00421                      /* only touch when activity actually took place... */
00422                      if ( mi->mi_idle_timeout != 0 && msc->msc_time < op->o_time ) {
00423                             msc->msc_time = op->o_time;
00424                      }
00425 
00426                      /* FIXME: matched? referrals? response controls? */
00427                      rc = ldap_parse_result( msc->msc_ld, res, &rs->sr_err,
00428                                    NULL, NULL, NULL, NULL, 1 );
00429                      if ( rc != LDAP_SUCCESS ) {
00430                             rs->sr_err = rc;
00431                      }
00432                      rs->sr_err = slap_map_api2result( rs );
00433                      break;
00434               }
00435        }
00436 
00437        rs->sr_err = slap_map_api2result( rs );
00438 
00439        Debug( LDAP_DEBUG_TRACE,
00440               "<<< %s meta_back_bind_op_result[%d] err=%d\n",
00441               op->o_log_prefix, candidate, rs->sr_err );
00442 
00443        return rs->sr_err;
00444 }
00445 
00446 /*
00447  * meta_back_single_bind
00448  *
00449  * attempts to perform a bind with creds
00450  */
00451 static int
00452 meta_back_single_bind(
00453        Operation            *op,
00454        SlapReply            *rs,
00455        metaconn_t           *mc,
00456        int                  candidate )
00457 {
00458        metainfo_t           *mi = ( metainfo_t * )op->o_bd->be_private;
00459        metatarget_t         *mt = mi->mi_targets[ candidate ];
00460        struct berval        mdn = BER_BVNULL;
00461        metasingleconn_t     *msc = &mc->mc_conns[ candidate ];
00462        int                  msgid;
00463        dncookie             dc;
00464        struct berval        save_o_dn;
00465        int                  save_o_do_not_cache;
00466        LDAPControl          **ctrls = NULL;
00467        
00468        if ( !BER_BVISNULL( &msc->msc_bound_ndn ) ) {
00469               ch_free( msc->msc_bound_ndn.bv_val );
00470               BER_BVZERO( &msc->msc_bound_ndn );
00471        }
00472 
00473        if ( !BER_BVISNULL( &msc->msc_cred ) ) {
00474               /* destroy sensitive data */
00475               memset( msc->msc_cred.bv_val, 0, msc->msc_cred.bv_len );
00476               ch_free( msc->msc_cred.bv_val );
00477               BER_BVZERO( &msc->msc_cred );
00478        }
00479 
00480        /*
00481         * Rewrite the bind dn if needed
00482         */
00483        dc.target = mt;
00484        dc.conn = op->o_conn;
00485        dc.rs = rs;
00486        dc.ctx = "bindDN";
00487 
00488        if ( ldap_back_dn_massage( &dc, &op->o_req_dn, &mdn ) ) {
00489               rs->sr_text = "DN rewrite error";
00490               rs->sr_err = LDAP_OTHER;
00491               return rs->sr_err;
00492        }
00493 
00494        /* don't add proxyAuthz; set the bindDN */
00495        save_o_dn = op->o_dn;
00496        save_o_do_not_cache = op->o_do_not_cache;
00497        op->o_do_not_cache = 1;
00498        op->o_dn = op->o_req_dn;
00499 
00500        ctrls = op->o_ctrls;
00501        rs->sr_err = meta_back_controls_add( op, rs, mc, candidate, &ctrls );
00502        op->o_dn = save_o_dn;
00503        op->o_do_not_cache = save_o_do_not_cache;
00504        if ( rs->sr_err != LDAP_SUCCESS ) {
00505               goto return_results;
00506        }
00507 
00508        /* FIXME: this fixes the bind problem right now; we need
00509         * to use the asynchronous version to get the "matched"
00510         * and more in case of failure ... */
00511        /* FIXME: should we check if at least some of the op->o_ctrls
00512         * can/should be passed? */
00513        for (;;) {
00514               rs->sr_err = ldap_sasl_bind( msc->msc_ld, mdn.bv_val,
00515                      LDAP_SASL_SIMPLE, &op->orb_cred,
00516                      ctrls, NULL, &msgid );
00517               if ( rs->sr_err != LDAP_X_CONNECTING ) {
00518                      break;
00519               }
00520               ldap_pvt_thread_yield();
00521        }
00522 
00523        mi->mi_ldap_extra->controls_free( op, rs, &ctrls );
00524 
00525        meta_back_bind_op_result( op, rs, mc, candidate, msgid, LDAP_BACK_DONTSEND, 1 );
00526        if ( rs->sr_err != LDAP_SUCCESS ) {
00527               goto return_results;
00528        }
00529 
00530        /* If defined, proxyAuthz will be used also when
00531         * back-ldap is the authorizing backend; for this
00532         * purpose, a successful bind is followed by a
00533         * bind with the configured identity assertion */
00534        /* NOTE: use with care */
00535        if ( mt->mt_idassert_flags & LDAP_BACK_AUTH_OVERRIDE ) {
00536               meta_back_proxy_authz_bind( mc, candidate, op, rs, LDAP_BACK_SENDERR, 1 );
00537               if ( !LDAP_BACK_CONN_ISBOUND( msc ) ) {
00538                      goto return_results;
00539               }
00540               goto cache_refresh;
00541        }
00542 
00543        ber_bvreplace( &msc->msc_bound_ndn, &op->o_req_ndn );
00544        LDAP_BACK_CONN_ISBOUND_SET( msc );
00545        mc->mc_authz_target = candidate;
00546 
00547        if ( META_BACK_TGT_SAVECRED( mt ) ) {
00548               if ( !BER_BVISNULL( &msc->msc_cred ) ) {
00549                      memset( msc->msc_cred.bv_val, 0,
00550                             msc->msc_cred.bv_len );
00551               }
00552               ber_bvreplace( &msc->msc_cred, &op->orb_cred );
00553               ldap_set_rebind_proc( msc->msc_ld, mt->mt_rebind_f, msc );
00554        }
00555 
00556 cache_refresh:;
00557        if ( mi->mi_cache.ttl != META_DNCACHE_DISABLED
00558                      && !BER_BVISEMPTY( &op->o_req_ndn ) )
00559        {
00560               ( void )meta_dncache_update_entry( &mi->mi_cache,
00561                             &op->o_req_ndn, candidate );
00562        }
00563 
00564 return_results:;
00565        if ( mdn.bv_val != op->o_req_dn.bv_val ) {
00566               free( mdn.bv_val );
00567        }
00568 
00569        if ( META_BACK_TGT_QUARANTINE( mt ) ) {
00570               meta_back_quarantine( op, rs, candidate );
00571        }
00572 
00573        return rs->sr_err;
00574 }
00575 
00576 /*
00577  * meta_back_single_dobind
00578  */
00579 int
00580 meta_back_single_dobind(
00581        Operation            *op,
00582        SlapReply            *rs,
00583        metaconn_t           **mcp,
00584        int                  candidate,
00585        ldap_back_send_t     sendok,
00586        int                  nretries,
00587        int                  dolock )
00588 {
00589        metainfo_t           *mi = ( metainfo_t * )op->o_bd->be_private;
00590        metatarget_t         *mt = mi->mi_targets[ candidate ];
00591        metaconn_t           *mc = *mcp;
00592        metasingleconn_t     *msc = &mc->mc_conns[ candidate ];
00593        int                  msgid;
00594 
00595        assert( !LDAP_BACK_CONN_ISBOUND( msc ) );
00596 
00597        /* NOTE: this obsoletes pseudorootdn */
00598        if ( op->o_conn != NULL &&
00599               !op->o_do_not_cache &&
00600               ( BER_BVISNULL( &msc->msc_bound_ndn ) ||
00601                      BER_BVISEMPTY( &msc->msc_bound_ndn ) ||
00602                      ( LDAP_BACK_CONN_ISPRIV( mc ) && dn_match( &msc->msc_bound_ndn, &mt->mt_idassert_authcDN ) ) ||
00603                      ( mt->mt_idassert_flags & LDAP_BACK_AUTH_OVERRIDE ) ) )
00604        {
00605               (void)meta_back_proxy_authz_bind( mc, candidate, op, rs, sendok, dolock );
00606 
00607        } else {
00608               char *binddn = "";
00609               struct berval cred = BER_BVC( "" );
00610 
00611               /* use credentials if available */
00612               if ( !BER_BVISNULL( &msc->msc_bound_ndn )
00613                      && !BER_BVISNULL( &msc->msc_cred ) )
00614               {
00615                      binddn = msc->msc_bound_ndn.bv_val;
00616                      cred = msc->msc_cred;
00617               }
00618 
00619               /* FIXME: should we check if at least some of the op->o_ctrls
00620                * can/should be passed? */
00621               for (;;) {
00622                      rs->sr_err = ldap_sasl_bind( msc->msc_ld,
00623                             binddn, LDAP_SASL_SIMPLE, &cred,
00624                             NULL, NULL, &msgid );
00625                      if ( rs->sr_err != LDAP_X_CONNECTING ) {
00626                             break;
00627                      }
00628                      ldap_pvt_thread_yield();
00629               }
00630 
00631               rs->sr_err = meta_back_bind_op_result( op, rs, mc, candidate, msgid, sendok, dolock );
00632 
00633               /* if bind succeeded, but anonymous, clear msc_bound_ndn */
00634               if ( rs->sr_err != LDAP_SUCCESS || binddn[0] == '\0' ) {
00635                      if ( !BER_BVISNULL( &msc->msc_bound_ndn ) ) {
00636                             ber_memfree( msc->msc_bound_ndn.bv_val );
00637                             BER_BVZERO( &msc->msc_bound_ndn );
00638                      }
00639 
00640                      if ( !BER_BVISNULL( &msc->msc_cred ) ) {
00641                             memset( msc->msc_cred.bv_val, 0, msc->msc_cred.bv_len );
00642                             ber_memfree( msc->msc_cred.bv_val );
00643                             BER_BVZERO( &msc->msc_cred );
00644                      }
00645               }
00646        }
00647 
00648        if ( rs->sr_err != LDAP_SUCCESS ) {
00649               if ( dolock ) {
00650                      ldap_pvt_thread_mutex_lock( &mi->mi_conninfo.lai_mutex );
00651               }
00652               LDAP_BACK_CONN_BINDING_CLEAR( msc );
00653               if ( META_BACK_ONERR_STOP( mi ) ) {
00654                      LDAP_BACK_CONN_TAINTED_SET( mc );
00655                      meta_back_release_conn_lock( mi, mc, 0 );
00656                      *mcp = NULL;
00657               }
00658               if ( dolock ) {
00659                      ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex );
00660               }
00661        }
00662 
00663        if ( META_BACK_TGT_QUARANTINE( mt ) ) {
00664               meta_back_quarantine( op, rs, candidate );
00665        }
00666 
00667        return rs->sr_err;
00668 }
00669 
00670 /*
00671  * meta_back_dobind
00672  */
00673 int
00674 meta_back_dobind(
00675        Operation            *op,
00676        SlapReply            *rs,
00677        metaconn_t           *mc,
00678        ldap_back_send_t     sendok )
00679 {
00680        metainfo_t           *mi = ( metainfo_t * )op->o_bd->be_private;
00681 
00682        int                  bound = 0,
00683                             i,
00684                             isroot = 0;
00685 
00686        SlapReply            *candidates;
00687 
00688        if ( be_isroot( op ) ) {
00689               isroot = 1;
00690        }
00691 
00692        if ( LogTest( LDAP_DEBUG_TRACE ) ) {
00693               char buf[STRLENOF("4294967295U") + 1] = { 0 };
00694               mi->mi_ldap_extra->connid2str( &mc->mc_base, buf, sizeof(buf) );
00695 
00696               Debug( LDAP_DEBUG_TRACE,
00697                      "%s meta_back_dobind: conn=%s%s\n",
00698                      op->o_log_prefix, buf,
00699                      isroot ? " (isroot)" : "" );
00700        }
00701 
00702        /*
00703         * all the targets are bound as pseudoroot
00704         */
00705        if ( mc->mc_authz_target == META_BOUND_ALL ) {
00706               bound = 1;
00707               goto done;
00708        }
00709 
00710        candidates = meta_back_candidates_get( op );
00711 
00712        for ( i = 0; i < mi->mi_ntargets; i++ ) {
00713               metatarget_t         *mt = mi->mi_targets[ i ];
00714               metasingleconn_t     *msc = &mc->mc_conns[ i ];
00715               int                  rc;
00716 
00717               /*
00718                * Not a candidate
00719                */
00720               if ( !META_IS_CANDIDATE( &candidates[ i ] ) ) {
00721                      continue;
00722               }
00723 
00724               assert( msc->msc_ld != NULL );
00725 
00726               /*
00727                * If the target is already bound it is skipped
00728                */
00729 
00730 retry_binding:;
00731               ldap_pvt_thread_mutex_lock( &mi->mi_conninfo.lai_mutex );
00732               if ( LDAP_BACK_CONN_ISBOUND( msc )
00733                      || ( LDAP_BACK_CONN_ISANON( msc )
00734                             && mt->mt_idassert_authmethod == LDAP_AUTH_NONE ) )
00735               {
00736                      ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex );
00737                      ++bound;
00738                      continue;
00739 
00740               } else if ( META_BACK_CONN_CREATING( msc ) || LDAP_BACK_CONN_BINDING( msc ) )
00741               {
00742                      ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex );
00743                      ldap_pvt_thread_yield();
00744                      goto retry_binding;
00745 
00746               }
00747 
00748               LDAP_BACK_CONN_BINDING_SET( msc );
00749               ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex );
00750 
00751               rc = meta_back_single_dobind( op, rs, &mc, i,
00752                      LDAP_BACK_DONTSEND, mt->mt_nretries, 1 );
00753               /*
00754                * NOTE: meta_back_single_dobind() already retries;
00755                * in case of failure, it resets mc...
00756                */
00757               if ( rc != LDAP_SUCCESS ) {
00758                      char          buf[ SLAP_TEXT_BUFLEN ];
00759 
00760                      if ( mc == NULL ) {
00761                             /* meta_back_single_dobind() already sent 
00762                              * response and released connection */
00763                             goto send_err;
00764                      }
00765 
00766 
00767                      if ( rc == LDAP_UNAVAILABLE ) {
00768                             /* FIXME: meta_back_retry() already re-calls
00769                              * meta_back_single_dobind() */
00770                             if ( meta_back_retry( op, rs, &mc, i, sendok ) ) {
00771                                    goto retry_ok;
00772                             }
00773 
00774                             if ( mc != NULL ) {
00775                                    ldap_pvt_thread_mutex_lock( &mi->mi_conninfo.lai_mutex );
00776                                    LDAP_BACK_CONN_BINDING_CLEAR( msc );
00777                                    meta_back_release_conn_lock( mi, mc, 0 );
00778                                    ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex );
00779                             }
00780 
00781                             return 0;
00782                      }
00783 
00784                      ldap_pvt_thread_mutex_lock( &mi->mi_conninfo.lai_mutex );
00785                      LDAP_BACK_CONN_BINDING_CLEAR( msc );
00786                      ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex );
00787 
00788                      snprintf( buf, sizeof( buf ),
00789                             "meta_back_dobind[%d]: (%s) err=%d (%s).",
00790                             i, isroot ? op->o_bd->be_rootdn.bv_val : "anonymous",
00791                             rc, ldap_err2string( rc ) );
00792                      Debug( LDAP_DEBUG_ANY,
00793                             "%s %s\n",
00794                             op->o_log_prefix, buf, 0 );
00795 
00796                      /*
00797                       * null cred bind should always succeed
00798                       * as anonymous, so a failure means
00799                       * the target is no longer candidate possibly
00800                       * due to technical reasons (remote host down?)
00801                       * so better clear the handle
00802                       */
00803                      /* leave the target candidate, but record the error for later use */
00804                      candidates[ i ].sr_err = rc;
00805                      if ( META_BACK_ONERR_STOP( mi ) ) {
00806                             bound = 0;
00807                             goto done;
00808                      }
00809 
00810                      continue;
00811               } /* else */
00812 
00813 retry_ok:;
00814               Debug( LDAP_DEBUG_TRACE,
00815                      "%s meta_back_dobind[%d]: "
00816                      "(%s)\n",
00817                      op->o_log_prefix, i,
00818                      isroot ? op->o_bd->be_rootdn.bv_val : "anonymous" );
00819 
00820               ldap_pvt_thread_mutex_lock( &mi->mi_conninfo.lai_mutex );
00821               LDAP_BACK_CONN_BINDING_CLEAR( msc );
00822               if ( isroot ) {
00823                      LDAP_BACK_CONN_ISBOUND_SET( msc );
00824               } else {
00825                      LDAP_BACK_CONN_ISANON_SET( msc );
00826               }
00827               ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex );
00828               ++bound;
00829        }
00830 
00831 done:;
00832        if ( LogTest( LDAP_DEBUG_TRACE ) ) {
00833               char buf[STRLENOF("4294967295U") + 1] = { 0 };
00834               mi->mi_ldap_extra->connid2str( &mc->mc_base, buf, sizeof(buf) );
00835 
00836               Debug( LDAP_DEBUG_TRACE,
00837                      "%s meta_back_dobind: conn=%s bound=%d\n",
00838                      op->o_log_prefix, buf, bound );
00839        }
00840 
00841        if ( bound == 0 ) {
00842               meta_back_release_conn( mi, mc );
00843 
00844 send_err:;
00845               if ( sendok & LDAP_BACK_SENDERR ) {
00846                      if ( rs->sr_err == LDAP_SUCCESS ) {
00847                             rs->sr_err = LDAP_BUSY;
00848                      }
00849                      send_ldap_result( op, rs );
00850               }
00851 
00852               return 0;
00853        }
00854 
00855        return ( bound > 0 );
00856 }
00857 
00858 /*
00859  * meta_back_default_rebind
00860  *
00861  * This is a callback used for chasing referrals using the same
00862  * credentials as the original user on this session.
00863  */
00864 int 
00865 meta_back_default_rebind(
00866        LDAP                 *ld,
00867        LDAP_CONST char             *url,
00868        ber_tag_t            request,
00869        ber_int_t            msgid,
00870        void                 *params )
00871 {
00872        metasingleconn_t     *msc = ( metasingleconn_t * )params;
00873 
00874        return ldap_sasl_bind_s( ld, msc->msc_bound_ndn.bv_val,
00875                      LDAP_SASL_SIMPLE, &msc->msc_cred,
00876                      NULL, NULL, NULL );
00877 }
00878 
00879 /*
00880  * meta_back_default_urllist
00881  *
00882  * This is a callback used for mucking with the urllist
00883  */
00884 int 
00885 meta_back_default_urllist(
00886        LDAP          *ld,
00887        LDAPURLDesc   **urllist,
00888        LDAPURLDesc   **url,
00889        void          *params )
00890 {
00891        metatarget_t  *mt = (metatarget_t *)params;
00892        LDAPURLDesc   **urltail;
00893 
00894        if ( urllist == url ) {
00895               return LDAP_SUCCESS;
00896        }
00897 
00898        for ( urltail = &(*url)->lud_next; *urltail; urltail = &(*urltail)->lud_next )
00899               /* count */ ;
00900 
00901        *urltail = *urllist;
00902        *urllist = *url;
00903        *url = NULL;
00904 
00905        ldap_pvt_thread_mutex_lock( &mt->mt_uri_mutex );
00906        if ( mt->mt_uri ) {
00907               ch_free( mt->mt_uri );
00908        }
00909 
00910        ldap_get_option( ld, LDAP_OPT_URI, (void *)&mt->mt_uri );
00911        ldap_pvt_thread_mutex_unlock( &mt->mt_uri_mutex );
00912 
00913        return LDAP_SUCCESS;
00914 }
00915 
00916 int
00917 meta_back_cancel(
00918        metaconn_t           *mc,
00919        Operation            *op,
00920        SlapReply            *rs,
00921        ber_int_t            msgid,
00922        int                  candidate,
00923        ldap_back_send_t     sendok )
00924 {
00925        metainfo_t           *mi = (metainfo_t *)op->o_bd->be_private;
00926 
00927        metatarget_t         *mt = mi->mi_targets[ candidate ];
00928        metasingleconn_t     *msc = &mc->mc_conns[ candidate ];
00929 
00930        int                  rc = LDAP_OTHER;
00931 
00932        Debug( LDAP_DEBUG_TRACE, ">>> %s meta_back_cancel[%d] msgid=%d\n",
00933               op->o_log_prefix, candidate, msgid );
00934 
00935        /* default behavior */
00936        if ( META_BACK_TGT_ABANDON( mt ) ) {
00937               rc = ldap_abandon_ext( msc->msc_ld, msgid, NULL, NULL );
00938 
00939        } else if ( META_BACK_TGT_IGNORE( mt ) ) {
00940               rc = ldap_pvt_discard( msc->msc_ld, msgid );
00941 
00942        } else if ( META_BACK_TGT_CANCEL( mt ) ) {
00943               rc = ldap_cancel_s( msc->msc_ld, msgid, NULL, NULL );
00944 
00945        } else {
00946               assert( 0 );
00947        }
00948 
00949        Debug( LDAP_DEBUG_TRACE, "<<< %s meta_back_cancel[%d] err=%d\n",
00950               op->o_log_prefix, candidate, rc );
00951 
00952        return rc;
00953 }
00954 
00955 
00956 
00957 /*
00958  * FIXME: error return must be handled in a cleaner way ...
00959  */
00960 int
00961 meta_back_op_result(
00962        metaconn_t           *mc,
00963        Operation            *op,
00964        SlapReply            *rs,
00965        int                  candidate,
00966        ber_int_t            msgid,
00967        time_t               timeout,
00968        ldap_back_send_t     sendok )
00969 {
00970        metainfo_t    *mi = ( metainfo_t * )op->o_bd->be_private;
00971 
00972        const char    *save_text = rs->sr_text,
00973                      *save_matched = rs->sr_matched;
00974        BerVarray     save_ref = rs->sr_ref;
00975        LDAPControl   **save_ctrls = rs->sr_ctrls;
00976        void          *matched_ctx = NULL;
00977 
00978        char          *matched = NULL;
00979        char          *text = NULL;
00980        char          **refs = NULL;
00981        LDAPControl   **ctrls = NULL;
00982 
00983        assert( mc != NULL );
00984 
00985        rs->sr_text = NULL;
00986        rs->sr_matched = NULL;
00987        rs->sr_ref = NULL;
00988        rs->sr_ctrls = NULL;
00989 
00990        if ( candidate != META_TARGET_NONE ) {
00991               metatarget_t         *mt = mi->mi_targets[ candidate ];
00992               metasingleconn_t     *msc = &mc->mc_conns[ candidate ];
00993 
00994               if ( LDAP_ERR_OK( rs->sr_err ) ) {
00995                      int           rc;
00996                      struct timeval       tv;
00997                      LDAPMessage   *res = NULL;
00998                      time_t        stoptime = (time_t)(-1);
00999                      int           timeout_err = op->o_protocol >= LDAP_VERSION3 ?
01000                                           LDAP_ADMINLIMIT_EXCEEDED : LDAP_OTHER;
01001                      const char    *timeout_text = "Operation timed out";
01002 
01003                      /* if timeout is not specified, compute and use
01004                       * the one specific to the ongoing operation */
01005                      if ( timeout == (time_t)(-1) ) {
01006                             slap_op_t     opidx = slap_req2op( op->o_tag );
01007 
01008                             if ( opidx == SLAP_OP_SEARCH ) {
01009                                    if ( op->ors_tlimit <= 0 ) {
01010                                           timeout = 0;
01011 
01012                                    } else {
01013                                           timeout = op->ors_tlimit;
01014                                           timeout_err = LDAP_TIMELIMIT_EXCEEDED;
01015                                           timeout_text = NULL;
01016                                    }
01017 
01018                             } else {
01019                                    timeout = mt->mt_timeout[ opidx ];
01020                             }
01021                      }
01022 
01023                      /* better than nothing :) */
01024                      if ( timeout == 0 ) {
01025                             if ( mi->mi_idle_timeout ) {
01026                                    timeout = mi->mi_idle_timeout;
01027 
01028                             } else if ( mi->mi_conn_ttl ) {
01029                                    timeout = mi->mi_conn_ttl;
01030                             }
01031                      }
01032 
01033                      if ( timeout ) {
01034                             stoptime = op->o_time + timeout;
01035                      }
01036 
01037                      LDAP_BACK_TV_SET( &tv );
01038 
01039 retry:;
01040                      rc = ldap_result( msc->msc_ld, msgid, LDAP_MSG_ALL, &tv, &res );
01041                      switch ( rc ) {
01042                      case 0:
01043                             if ( timeout && slap_get_time() > stoptime ) {
01044                                    (void)meta_back_cancel( mc, op, rs, msgid, candidate, sendok );
01045                                    rs->sr_err = timeout_err;
01046                                    rs->sr_text = timeout_text;
01047                                    break;
01048                             }
01049 
01050                             LDAP_BACK_TV_SET( &tv );
01051                             ldap_pvt_thread_yield();
01052                             goto retry;
01053 
01054                      case -1:
01055                             ldap_get_option( msc->msc_ld, LDAP_OPT_RESULT_CODE,
01056                                           &rs->sr_err );
01057                             break;
01058 
01059 
01060                      /* otherwise get the result; if it is not
01061                       * LDAP_SUCCESS, record it in the reply
01062                       * structure (this includes 
01063                       * LDAP_COMPARE_{TRUE|FALSE}) */
01064                      default:
01065                             /* only touch when activity actually took place... */
01066                             if ( mi->mi_idle_timeout != 0 && msc->msc_time < op->o_time ) {
01067                                    msc->msc_time = op->o_time;
01068                             }
01069 
01070                             rc = ldap_parse_result( msc->msc_ld, res, &rs->sr_err,
01071                                           &matched, &text, &refs, &ctrls, 1 );
01072                             res = NULL;
01073                             if ( rc == LDAP_SUCCESS ) {
01074                                    rs->sr_text = text;
01075                             } else {
01076                                    rs->sr_err = rc;
01077                             }
01078                             rs->sr_err = slap_map_api2result( rs );
01079 
01080                             /* RFC 4511: referrals can only appear
01081                              * if result code is LDAP_REFERRAL */
01082                             if ( refs != NULL
01083                                    && refs[ 0 ] != NULL
01084                                    && refs[ 0 ][ 0 ] != '\0' )
01085                             {
01086                                    if ( rs->sr_err != LDAP_REFERRAL ) {
01087                                           Debug( LDAP_DEBUG_ANY,
01088                                                  "%s meta_back_op_result[%d]: "
01089                                                  "got referrals with err=%d\n",
01090                                                  op->o_log_prefix,
01091                                                  candidate, rs->sr_err );
01092 
01093                                    } else {
01094                                           int    i;
01095        
01096                                           for ( i = 0; refs[ i ] != NULL; i++ )
01097                                                  /* count */ ;
01098                                           rs->sr_ref = op->o_tmpalloc( sizeof( struct berval ) * ( i + 1 ),
01099                                                  op->o_tmpmemctx );
01100                                           for ( i = 0; refs[ i ] != NULL; i++ ) {
01101                                                  ber_str2bv( refs[ i ], 0, 0, &rs->sr_ref[ i ] );
01102                                           }
01103                                           BER_BVZERO( &rs->sr_ref[ i ] );
01104                                    }
01105 
01106                             } else if ( rs->sr_err == LDAP_REFERRAL ) {
01107                                    Debug( LDAP_DEBUG_ANY,
01108                                           "%s meta_back_op_result[%d]: "
01109                                           "got err=%d with null "
01110                                           "or empty referrals\n",
01111                                           op->o_log_prefix,
01112                                           candidate, rs->sr_err );
01113 
01114                                    rs->sr_err = LDAP_NO_SUCH_OBJECT;
01115                             }
01116 
01117                             if ( ctrls != NULL ) {
01118                                    rs->sr_ctrls = ctrls;
01119                             }
01120                      }
01121 
01122                      assert( res == NULL );
01123               }
01124 
01125               /* if the error in the reply structure is not
01126                * LDAP_SUCCESS, try to map it from client 
01127                * to server error */
01128               if ( !LDAP_ERR_OK( rs->sr_err ) ) {
01129                      rs->sr_err = slap_map_api2result( rs );
01130 
01131                      /* internal ops ( op->o_conn == NULL ) 
01132                       * must not reply to client */
01133                      if ( op->o_conn && !op->o_do_not_cache && matched ) {
01134 
01135                             /* record the (massaged) matched
01136                              * DN into the reply structure */
01137                             rs->sr_matched = matched;
01138                      }
01139               }
01140 
01141               if ( META_BACK_TGT_QUARANTINE( mt ) ) {
01142                      meta_back_quarantine( op, rs, candidate );
01143               }
01144 
01145        } else {
01146               int    i,
01147                      err = rs->sr_err;
01148 
01149               for ( i = 0; i < mi->mi_ntargets; i++ ) {
01150                      metasingleconn_t     *msc = &mc->mc_conns[ i ];
01151                      char                 *xtext = NULL;
01152                      char                 *xmatched = NULL;
01153 
01154                      if ( msc->msc_ld == NULL ) {
01155                             continue;
01156                      }
01157 
01158                      rs->sr_err = LDAP_SUCCESS;
01159 
01160                      ldap_get_option( msc->msc_ld, LDAP_OPT_RESULT_CODE, &rs->sr_err );
01161                      if ( rs->sr_err != LDAP_SUCCESS ) {
01162                             /*
01163                              * better check the type of error. In some cases
01164                              * (search ?) it might be better to return a
01165                              * success if at least one of the targets gave
01166                              * positive result ...
01167                              */
01168                             ldap_get_option( msc->msc_ld,
01169                                           LDAP_OPT_DIAGNOSTIC_MESSAGE, &xtext );
01170                             if ( xtext != NULL && xtext [ 0 ] == '\0' ) {
01171                                    ldap_memfree( xtext );
01172                                    xtext = NULL;
01173                             }
01174 
01175                             ldap_get_option( msc->msc_ld,
01176                                           LDAP_OPT_MATCHED_DN, &xmatched );
01177                             if ( xmatched != NULL && xmatched[ 0 ] == '\0' ) {
01178                                    ldap_memfree( xmatched );
01179                                    xmatched = NULL;
01180                             }
01181 
01182                             rs->sr_err = slap_map_api2result( rs );
01183        
01184                             if ( LogTest( LDAP_DEBUG_ANY ) ) {
01185                                    char   buf[ SLAP_TEXT_BUFLEN ];
01186 
01187                                    snprintf( buf, sizeof( buf ),
01188                                           "meta_back_op_result[%d] "
01189                                           "err=%d text=\"%s\" matched=\"%s\"", 
01190                                           i, rs->sr_err,
01191                                           ( xtext ? xtext : "" ),
01192                                           ( xmatched ? xmatched : "" ) );
01193                                    Debug( LDAP_DEBUG_ANY, "%s %s.\n",
01194                                           op->o_log_prefix, buf, 0 );
01195                             }
01196 
01197                             /*
01198                              * FIXME: need to rewrite "match" (need rwinfo)
01199                              */
01200                             switch ( rs->sr_err ) {
01201                             default:
01202                                    err = rs->sr_err;
01203                                    if ( xtext != NULL ) {
01204                                           if ( text ) {
01205                                                  ldap_memfree( text );
01206                                           }
01207                                           text = xtext;
01208                                           xtext = NULL;
01209                                    }
01210                                    if ( xmatched != NULL ) {
01211                                           if ( matched ) {
01212                                                  ldap_memfree( matched );
01213                                           }
01214                                           matched = xmatched;
01215                                           xmatched = NULL;
01216                                    }
01217                                    break;
01218                             }
01219 
01220                             if ( xtext ) {
01221                                    ldap_memfree( xtext );
01222                             }
01223        
01224                             if ( xmatched ) {
01225                                    ldap_memfree( xmatched );
01226                             }
01227                      }
01228 
01229                      if ( META_BACK_TGT_QUARANTINE( mi->mi_targets[ i ] ) ) {
01230                             meta_back_quarantine( op, rs, i );
01231                      }
01232               }
01233 
01234               if ( err != LDAP_SUCCESS ) {
01235                      rs->sr_err = err;
01236               }
01237        }
01238 
01239        if ( matched != NULL ) {
01240               struct berval dn, pdn;
01241 
01242               ber_str2bv( matched, 0, 0, &dn );
01243               if ( dnPretty( NULL, &dn, &pdn, op->o_tmpmemctx ) == LDAP_SUCCESS ) {
01244                      ldap_memfree( matched );
01245                      matched_ctx = op->o_tmpmemctx;
01246                      matched = pdn.bv_val;
01247               }
01248               rs->sr_matched = matched;
01249        }
01250 
01251        if ( rs->sr_err == LDAP_UNAVAILABLE ) {
01252               if ( !( sendok & LDAP_BACK_RETRYING ) ) {
01253                      if ( op->o_conn && ( sendok & LDAP_BACK_SENDERR ) ) {
01254                             if ( rs->sr_text == NULL ) rs->sr_text = "Proxy operation retry failed";
01255                             send_ldap_result( op, rs );
01256                      }
01257               }
01258 
01259        } else if ( op->o_conn &&
01260               ( ( ( sendok & LDAP_BACK_SENDOK ) && LDAP_ERR_OK( rs->sr_err ) )
01261                      || ( ( sendok & LDAP_BACK_SENDERR ) && !LDAP_ERR_OK( rs->sr_err ) ) ) )
01262        {
01263               send_ldap_result( op, rs );
01264        }
01265        if ( matched ) {
01266               op->o_tmpfree( (char *)rs->sr_matched, matched_ctx );
01267        }
01268        if ( text ) {
01269               ldap_memfree( text );
01270        }
01271        if ( rs->sr_ref ) {
01272               op->o_tmpfree( rs->sr_ref, op->o_tmpmemctx );
01273               rs->sr_ref = NULL;
01274        }
01275        if ( refs ) {
01276               ber_memvfree( (void **)refs );
01277        }
01278        if ( ctrls ) {
01279               assert( rs->sr_ctrls != NULL );
01280               ldap_controls_free( ctrls );
01281        }
01282 
01283        rs->sr_text = save_text;
01284        rs->sr_matched = save_matched;
01285        rs->sr_ref = save_ref;
01286        rs->sr_ctrls = save_ctrls;
01287 
01288        return( LDAP_ERR_OK( rs->sr_err ) ? LDAP_SUCCESS : rs->sr_err );
01289 }
01290 
01291 /*
01292  * meta_back_proxy_authz_cred()
01293  *
01294  * prepares credentials & method for meta_back_proxy_authz_bind();
01295  * or, if method is SASL, performs the SASL bind directly.
01296  */
01297 int
01298 meta_back_proxy_authz_cred(
01299        metaconn_t           *mc,
01300        int                  candidate,
01301        Operation            *op,
01302        SlapReply            *rs,
01303        ldap_back_send_t     sendok,
01304        struct berval        *binddn,
01305        struct berval        *bindcred,
01306        int                  *method )
01307 {
01308        metainfo_t           *mi = (metainfo_t *)op->o_bd->be_private;
01309        metatarget_t         *mt = mi->mi_targets[ candidate ];
01310        metasingleconn_t     *msc = &mc->mc_conns[ candidate ];
01311        struct berval        ndn;
01312        int                  dobind = 0;
01313 
01314        /* don't proxyAuthz if protocol is not LDAPv3 */
01315        switch ( mt->mt_version ) {
01316        case LDAP_VERSION3:
01317               break;
01318 
01319        case 0:
01320               if ( op->o_protocol == 0 || op->o_protocol == LDAP_VERSION3 ) {
01321                      break;
01322               }
01323               /* fall thru */
01324 
01325        default:
01326               rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
01327               if ( sendok & LDAP_BACK_SENDERR ) {
01328                      send_ldap_result( op, rs );
01329               }
01330               LDAP_BACK_CONN_ISBOUND_CLEAR( msc );
01331               goto done;
01332        }
01333 
01334        if ( op->o_tag == LDAP_REQ_BIND ) {
01335               ndn = op->o_req_ndn;
01336 
01337        } else if ( !BER_BVISNULL( &op->o_conn->c_ndn ) ) {
01338               ndn = op->o_conn->c_ndn;
01339 
01340        } else {
01341               ndn = op->o_ndn;
01342        }
01343        rs->sr_err = LDAP_SUCCESS;
01344 
01345        /*
01346         * FIXME: we need to let clients use proxyAuthz
01347         * otherwise we cannot do symmetric pools of servers;
01348         * we have to live with the fact that a user can
01349         * authorize itself as any ID that is allowed
01350         * by the authzTo directive of the "proxyauthzdn".
01351         */
01352        /*
01353         * NOTE: current Proxy Authorization specification
01354         * and implementation do not allow proxy authorization
01355         * control to be provided with Bind requests
01356         */
01357        /*
01358         * if no bind took place yet, but the connection is bound
01359         * and the "proxyauthzdn" is set, then bind as 
01360         * "proxyauthzdn" and explicitly add the proxyAuthz 
01361         * control to every operation with the dn bound 
01362         * to the connection as control value.
01363         */
01364 
01365        /* bind as proxyauthzdn only if no idassert mode
01366         * is requested, or if the client's identity
01367         * is authorized */
01368        switch ( mt->mt_idassert_mode ) {
01369        case LDAP_BACK_IDASSERT_LEGACY:
01370               if ( !BER_BVISNULL( &ndn ) && !BER_BVISEMPTY( &ndn ) ) {
01371                      if ( !BER_BVISNULL( &mt->mt_idassert_authcDN ) && !BER_BVISEMPTY( &mt->mt_idassert_authcDN ) )
01372                      {
01373                             *binddn = mt->mt_idassert_authcDN;
01374                             *bindcred = mt->mt_idassert_passwd;
01375                             dobind = 1;
01376                      }
01377               }
01378               break;
01379 
01380        default:
01381               /* NOTE: rootdn can always idassert */
01382               if ( BER_BVISNULL( &ndn )
01383                      && mt->mt_idassert_authz == NULL
01384                      && !( mt->mt_idassert_flags & LDAP_BACK_AUTH_AUTHZ_ALL ) )
01385               {
01386                      if ( mt->mt_idassert_flags & LDAP_BACK_AUTH_PRESCRIPTIVE ) {
01387                             rs->sr_err = LDAP_INAPPROPRIATE_AUTH;
01388                             if ( sendok & LDAP_BACK_SENDERR ) {
01389                                    send_ldap_result( op, rs );
01390                             }
01391                             LDAP_BACK_CONN_ISBOUND_CLEAR( msc );
01392                             goto done;
01393 
01394                      }
01395 
01396                      rs->sr_err = LDAP_SUCCESS;
01397                      *binddn = slap_empty_bv;
01398                      *bindcred = slap_empty_bv;
01399                      break;
01400 
01401               } else if ( mt->mt_idassert_authz && !be_isroot( op ) ) {
01402                      struct berval authcDN;
01403 
01404                      if ( BER_BVISNULL( &ndn ) ) {
01405                             authcDN = slap_empty_bv;
01406 
01407                      } else {
01408                             authcDN = ndn;
01409                      }      
01410                      rs->sr_err = slap_sasl_matches( op, mt->mt_idassert_authz,
01411                                    &authcDN, &authcDN );
01412                      if ( rs->sr_err != LDAP_SUCCESS ) {
01413                             if ( mt->mt_idassert_flags & LDAP_BACK_AUTH_PRESCRIPTIVE ) {
01414                                    if ( sendok & LDAP_BACK_SENDERR ) {
01415                                           send_ldap_result( op, rs );
01416                                    }
01417                                    LDAP_BACK_CONN_ISBOUND_CLEAR( msc );
01418                                    goto done;
01419                             }
01420 
01421                             rs->sr_err = LDAP_SUCCESS;
01422                             *binddn = slap_empty_bv;
01423                             *bindcred = slap_empty_bv;
01424                             break;
01425                      }
01426               }
01427 
01428               *binddn = mt->mt_idassert_authcDN;
01429               *bindcred = mt->mt_idassert_passwd;
01430               dobind = 1;
01431               break;
01432        }
01433 
01434        if ( dobind && mt->mt_idassert_authmethod == LDAP_AUTH_SASL ) {
01435 #ifdef HAVE_CYRUS_SASL
01436               void          *defaults = NULL;
01437               struct berval authzID = BER_BVNULL;
01438               int           freeauthz = 0;
01439 
01440               /* if SASL supports native authz, prepare for it */
01441               if ( ( !op->o_do_not_cache || !op->o_is_auth_check ) &&
01442                             ( mt->mt_idassert_flags & LDAP_BACK_AUTH_NATIVE_AUTHZ ) )
01443               {
01444                      switch ( mt->mt_idassert_mode ) {
01445                      case LDAP_BACK_IDASSERT_OTHERID:
01446                      case LDAP_BACK_IDASSERT_OTHERDN:
01447                             authzID = mt->mt_idassert_authzID;
01448                             break;
01449 
01450                      case LDAP_BACK_IDASSERT_ANONYMOUS:
01451                             BER_BVSTR( &authzID, "dn:" );
01452                             break;
01453 
01454                      case LDAP_BACK_IDASSERT_SELF:
01455                             if ( BER_BVISNULL( &ndn ) ) {
01456                                    /* connection is not authc'd, so don't idassert */
01457                                    BER_BVSTR( &authzID, "dn:" );
01458                                    break;
01459                             }
01460                             authzID.bv_len = STRLENOF( "dn:" ) + ndn.bv_len;
01461                             authzID.bv_val = slap_sl_malloc( authzID.bv_len + 1, op->o_tmpmemctx );
01462                             AC_MEMCPY( authzID.bv_val, "dn:", STRLENOF( "dn:" ) );
01463                             AC_MEMCPY( authzID.bv_val + STRLENOF( "dn:" ),
01464                                           ndn.bv_val, ndn.bv_len + 1 );
01465                             freeauthz = 1;
01466                             break;
01467 
01468                      default:
01469                             break;
01470                      }
01471               }
01472 
01473               if ( mt->mt_idassert_secprops != NULL ) {
01474                      rs->sr_err = ldap_set_option( msc->msc_ld,
01475                             LDAP_OPT_X_SASL_SECPROPS,
01476                             (void *)mt->mt_idassert_secprops );
01477 
01478                      if ( rs->sr_err != LDAP_OPT_SUCCESS ) {
01479                             rs->sr_err = LDAP_OTHER;
01480                             if ( sendok & LDAP_BACK_SENDERR ) {
01481                                    send_ldap_result( op, rs );
01482                             }
01483                             LDAP_BACK_CONN_ISBOUND_CLEAR( msc );
01484                             goto done;
01485                      }
01486               }
01487 
01488               defaults = lutil_sasl_defaults( msc->msc_ld,
01489                             mt->mt_idassert_sasl_mech.bv_val,
01490                             mt->mt_idassert_sasl_realm.bv_val,
01491                             mt->mt_idassert_authcID.bv_val,
01492                             mt->mt_idassert_passwd.bv_val,
01493                             authzID.bv_val );
01494               if ( defaults == NULL ) {
01495                      rs->sr_err = LDAP_OTHER;
01496                      LDAP_BACK_CONN_ISBOUND_CLEAR( msc );
01497                      if ( sendok & LDAP_BACK_SENDERR ) {
01498                             send_ldap_result( op, rs );
01499                      }
01500                      goto done;
01501               }
01502 
01503               rs->sr_err = ldap_sasl_interactive_bind_s( msc->msc_ld, binddn->bv_val,
01504                             mt->mt_idassert_sasl_mech.bv_val, NULL, NULL,
01505                             LDAP_SASL_QUIET, lutil_sasl_interact,
01506                             defaults );
01507 
01508               rs->sr_err = slap_map_api2result( rs );
01509               if ( rs->sr_err != LDAP_SUCCESS ) {
01510                      LDAP_BACK_CONN_ISBOUND_CLEAR( msc );
01511                      if ( sendok & LDAP_BACK_SENDERR ) {
01512                             send_ldap_result( op, rs );
01513                      }
01514 
01515               } else {
01516                      LDAP_BACK_CONN_ISBOUND_SET( msc );
01517               }
01518 
01519               lutil_sasl_freedefs( defaults );
01520               if ( freeauthz ) {
01521                      slap_sl_free( authzID.bv_val, op->o_tmpmemctx );
01522               }
01523 
01524               goto done;
01525 #endif /* HAVE_CYRUS_SASL */
01526        }
01527 
01528        *method = mt->mt_idassert_authmethod;
01529        switch ( mt->mt_idassert_authmethod ) {
01530        case LDAP_AUTH_NONE:
01531               BER_BVSTR( binddn, "" );
01532               BER_BVSTR( bindcred, "" );
01533               /* fallthru */
01534 
01535        case LDAP_AUTH_SIMPLE:
01536               break;
01537 
01538        default:
01539               /* unsupported! */
01540               LDAP_BACK_CONN_ISBOUND_CLEAR( msc );
01541               rs->sr_err = LDAP_AUTH_METHOD_NOT_SUPPORTED;
01542               if ( sendok & LDAP_BACK_SENDERR ) {
01543                      send_ldap_result( op, rs );
01544               }
01545               break;
01546        }
01547 
01548 done:;
01549 
01550        if ( !BER_BVISEMPTY( binddn ) ) {
01551               LDAP_BACK_CONN_ISIDASSERT_SET( msc );
01552        }
01553 
01554        return rs->sr_err;
01555 }
01556 
01557 static int
01558 meta_back_proxy_authz_bind(
01559        metaconn_t *mc,
01560        int candidate,
01561        Operation *op,
01562        SlapReply *rs,
01563        ldap_back_send_t sendok,
01564        int dolock )
01565 {
01566        metainfo_t           *mi = (metainfo_t *)op->o_bd->be_private;
01567        metatarget_t         *mt = mi->mi_targets[ candidate ];
01568        metasingleconn_t     *msc = &mc->mc_conns[ candidate ];
01569        struct berval        binddn = BER_BVC( "" ),
01570                             cred = BER_BVC( "" );
01571        int                  method = LDAP_AUTH_NONE,
01572                             rc;
01573 
01574        rc = meta_back_proxy_authz_cred( mc, candidate, op, rs, sendok, &binddn, &cred, &method );
01575        if ( rc == LDAP_SUCCESS && !LDAP_BACK_CONN_ISBOUND( msc ) ) {
01576               int    msgid;
01577 
01578               switch ( method ) {
01579               case LDAP_AUTH_NONE:
01580               case LDAP_AUTH_SIMPLE:
01581                      for (;;) {
01582                             rs->sr_err = ldap_sasl_bind( msc->msc_ld,
01583                                    binddn.bv_val, LDAP_SASL_SIMPLE,
01584                                    &cred, NULL, NULL, &msgid );
01585                             if ( rs->sr_err != LDAP_X_CONNECTING ) {
01586                                    break;
01587                             }
01588                             ldap_pvt_thread_yield();
01589                      }
01590                      rc = meta_back_bind_op_result( op, rs, mc, candidate, msgid, sendok, dolock );
01591                      if ( rc == LDAP_SUCCESS ) {
01592                             /* set rebind stuff in case of successful proxyAuthz bind,
01593                              * so that referral chasing is attempted using the right
01594                              * identity */
01595                             LDAP_BACK_CONN_ISBOUND_SET( msc );
01596                             ber_bvreplace( &msc->msc_bound_ndn, &binddn );
01597 
01598                             if ( META_BACK_TGT_SAVECRED( mt ) ) {
01599                                    if ( !BER_BVISNULL( &msc->msc_cred ) ) {
01600                                           memset( msc->msc_cred.bv_val, 0,
01601                                                  msc->msc_cred.bv_len );
01602                                    }
01603                                    ber_bvreplace( &msc->msc_cred, &cred );
01604                                    ldap_set_rebind_proc( msc->msc_ld, mt->mt_rebind_f, msc );
01605                             }
01606                      }
01607                      break;
01608 
01609               default:
01610                      assert( 0 );
01611                      break;
01612               }
01613        }
01614 
01615        return LDAP_BACK_CONN_ISBOUND( msc );
01616 }
01617 
01618 /*
01619  * Add controls;
01620  *
01621  * if any needs to be added, it is prepended to existing ones,
01622  * in a newly allocated array.  The companion function
01623  * mi->mi_ldap_extra->controls_free() must be used to restore the original
01624  * status of op->o_ctrls.
01625  */
01626 int
01627 meta_back_controls_add(
01628               Operation     *op,
01629               SlapReply     *rs,
01630               metaconn_t    *mc,
01631               int           candidate,
01632               LDAPControl   ***pctrls )
01633 {
01634        metainfo_t           *mi = (metainfo_t *)op->o_bd->be_private;
01635        metatarget_t         *mt = mi->mi_targets[ candidate ];
01636        metasingleconn_t     *msc = &mc->mc_conns[ candidate ];
01637 
01638        LDAPControl          **ctrls = NULL;
01639        /* set to the maximum number of controls this backend can add */
01640        LDAPControl          c[ 2 ] = {{ 0 }};
01641        int                  n = 0, i, j1 = 0, j2 = 0;
01642 
01643        *pctrls = NULL;
01644 
01645        rs->sr_err = LDAP_SUCCESS;
01646 
01647        /* don't add controls if protocol is not LDAPv3 */
01648        switch ( mt->mt_version ) {
01649        case LDAP_VERSION3:
01650               break;
01651 
01652        case 0:
01653               if ( op->o_protocol == 0 || op->o_protocol == LDAP_VERSION3 ) {
01654                      break;
01655               }
01656               /* fall thru */
01657 
01658        default:
01659               goto done;
01660        }
01661 
01662        /* put controls that go __before__ existing ones here */
01663 
01664        /* proxyAuthz for identity assertion */
01665        switch ( mi->mi_ldap_extra->proxy_authz_ctrl( op, rs, &msc->msc_bound_ndn,
01666               mt->mt_version, &mt->mt_idassert, &c[ j1 ] ) )
01667        {
01668        case SLAP_CB_CONTINUE:
01669               break;
01670 
01671        case LDAP_SUCCESS:
01672               j1++;
01673               break;
01674 
01675        default:
01676               goto done;
01677        }
01678 
01679        /* put controls that go __after__ existing ones here */
01680 
01681 #ifdef SLAP_CONTROL_X_SESSION_TRACKING
01682        /* session tracking */
01683        if ( META_BACK_TGT_ST_REQUEST( mt ) ) {
01684               switch ( slap_ctrl_session_tracking_request_add( op, rs, &c[ j1 + j2 ] ) ) {
01685               case SLAP_CB_CONTINUE:
01686                      break;
01687 
01688               case LDAP_SUCCESS:
01689                      j2++;
01690                      break;
01691 
01692               default:
01693                      goto done;
01694               }
01695        }
01696 #endif /* SLAP_CONTROL_X_SESSION_TRACKING */
01697 
01698        if ( rs->sr_err == SLAP_CB_CONTINUE ) {
01699               rs->sr_err = LDAP_SUCCESS;
01700        }
01701 
01702        /* if nothing to do, just bail out */
01703        if ( j1 == 0 && j2 == 0 ) {
01704               goto done;
01705        }
01706 
01707        assert( j1 + j2 <= (int) (sizeof( c )/sizeof( c[0] )) );
01708 
01709        if ( op->o_ctrls ) {
01710               for ( n = 0; op->o_ctrls[ n ]; n++ )
01711                      /* just count ctrls */ ;
01712        }
01713 
01714        ctrls = op->o_tmpalloc( (n + j1 + j2 + 1) * sizeof( LDAPControl * ) + ( j1 + j2 ) * sizeof( LDAPControl ),
01715                      op->o_tmpmemctx );
01716        if ( j1 ) {
01717               ctrls[ 0 ] = (LDAPControl *)&ctrls[ n + j1 + j2 + 1 ];
01718               *ctrls[ 0 ] = c[ 0 ];
01719               for ( i = 1; i < j1; i++ ) {
01720                      ctrls[ i ] = &ctrls[ 0 ][ i ];
01721                      *ctrls[ i ] = c[ i ];
01722               }
01723        }
01724 
01725        i = 0;
01726        if ( op->o_ctrls ) {
01727               for ( i = 0; op->o_ctrls[ i ]; i++ ) {
01728                      ctrls[ i + j1 ] = op->o_ctrls[ i ];
01729               }
01730        }
01731 
01732        n += j1;
01733        if ( j2 ) {
01734               ctrls[ n ] = (LDAPControl *)&ctrls[ n + j2 + 1 ] + j1;
01735               *ctrls[ n ] = c[ j1 ];
01736               for ( i = 1; i < j2; i++ ) {
01737                      ctrls[ n + i ] = &ctrls[ n ][ i ];
01738                      *ctrls[ n + i ] = c[ i ];
01739               }
01740        }
01741 
01742        ctrls[ n + j2 ] = NULL;
01743 
01744 done:;
01745        if ( ctrls == NULL ) {
01746               ctrls = op->o_ctrls;
01747        }
01748 
01749        *pctrls = ctrls;
01750        
01751        return rs->sr_err;
01752 }
01753