2 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
4 * Copyright 1999-2005 The OpenLDAP Foundation.
5 * Portions Copyright 2001-2003 Pierangelo Masarati.
6 * Portions Copyright 1999-2003 Howard Chu.
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted only as authorized by the OpenLDAP
13 * A copy of this license is available in the file LICENSE in the
14 * top-level directory of the distribution or, alternatively, at
15 * <http://www.OpenLDAP.org/license.html>.
18 * This work was initially developed by the Howard Chu for inclusion
19 * in OpenLDAP Software and subsequently enhanced by Pierangelo
28 #include <ac/socket.h>
29 #include <ac/string.h>
34 #include "../back-ldap/back-ldap.h"
35 #include "back-meta.h"
37 static LDAP_REBIND_PROC meta_back_rebind;
40 meta_back_single_bind(
47 meta_back_bind( Operation *op, SlapReply *rs )
49 metainfo_t *mi = ( metainfo_t * )op->o_bd->be_private;
50 metaconn_t *mc = NULL;
53 i, gotit = 0, isroot = 0;
55 SlapReply *candidates = meta_back_candidates_get( op );
57 rs->sr_err = LDAP_SUCCESS;
59 Debug( LDAP_DEBUG_ARGS, "meta_back_bind: dn: %s.\n%s%s",
60 op->o_req_dn.bv_val, "", "" );
62 if ( op->orb_method == LDAP_AUTH_SIMPLE
63 && be_isroot_dn( op->o_bd, &op->o_req_ndn ) )
65 if ( !be_isroot_pw( op ) ) {
66 rs->sr_err = LDAP_INVALID_CREDENTIALS;
68 send_ldap_result( op, rs );
74 /* we need meta_back_getconn() not send result even on error,
75 * because we want to intercept the error and make it
76 * invalidCredentials */
77 mc = meta_back_getconn( op, rs, NULL, LDAP_BACK_DONTSEND );
79 Debug( LDAP_DEBUG_ANY,
80 "meta_back_bind: no target "
81 "for dn \"%s\" (%d: %s).\n",
82 op->o_req_dn.bv_val, rs->sr_err,
83 rs->sr_text ? rs->sr_text : "" );
84 /* FIXME: there might be cases where we don't want
85 * to map the error onto invalidCredentials */
86 switch ( rs->sr_err ) {
87 case LDAP_NO_SUCH_OBJECT:
88 case LDAP_UNWILLING_TO_PERFORM:
89 rs->sr_err = LDAP_INVALID_CREDENTIALS;
93 send_ldap_result( op, rs );
98 * Each target is scanned ...
100 mc->mc_auth_target = META_BOUND_NONE;
101 for ( i = 0; i < mi->mi_ntargets; i++ ) {
106 * Skip non-candidates
108 if ( candidates[ i ].sr_tag != META_CANDIDATE ) {
115 } else if ( isroot == 0 ) {
117 * A bind operation is expected to have
118 * ONE CANDIDATE ONLY!
120 Debug( LDAP_DEBUG_ANY,
121 "==>meta_back_bind: more than one"
122 " candidate is trying to bind...\n",
126 if ( isroot && !BER_BVISNULL( &mi->mi_targets[ i ].mt_pseudorootdn ) )
128 op2.o_req_dn = mi->mi_targets[ i ].mt_pseudorootdn;
129 op2.o_req_ndn = mi->mi_targets[ i ].mt_pseudorootdn;
130 op2.orb_cred = mi->mi_targets[ i ].mt_pseudorootpw;
131 op2.orb_method = LDAP_AUTH_SIMPLE;
134 lerr = meta_back_single_bind( &op2, rs, mc, i );
135 if ( lerr != LDAP_SUCCESS ) {
137 candidates[ i ].sr_tag = META_NOT_CANDIDATE;
139 if ( META_BACK_ONERR_STOP( mi ) ) {
149 if ( isroot && rc == LDAP_SUCCESS ) {
150 mc->mc_auth_target = META_BOUND_ALL;
151 ber_dupbv( &op->orb_edn, be_root_dn( op->o_bd ) );
154 meta_back_release_conn( op, mc );
157 * rc is LDAP_SUCCESS if at least one bind succeeded,
158 * err is the last error that occurred during a bind;
159 * if at least (and at most?) one bind succeedes, fine.
161 if ( rc != LDAP_SUCCESS ) {
164 * deal with bind failure ...
168 * no target was found within the naming context,
169 * so bind must fail with invalid credentials
171 if ( rs->sr_err == LDAP_SUCCESS && gotit == 0 ) {
172 rs->sr_err = LDAP_INVALID_CREDENTIALS;
175 rs->sr_err = slap_map_api2result( rs );
176 send_ldap_result( op, rs );
184 * meta_back_single_bind
186 * attempts to perform a bind with creds
189 meta_back_single_bind(
195 metainfo_t *mi = ( metainfo_t * )op->o_bd->be_private;
196 metatarget_t *mt = &mi->mi_targets[ candidate ];
197 struct berval mdn = BER_BVNULL;
199 metasingleconn_t *msc = &mc->mc_conns[ candidate ];
204 * Rewrite the bind dn if needed
206 dc.target = &mi->mi_targets[ candidate ];
207 dc.conn = op->o_conn;
211 if ( ldap_back_dn_massage( &dc, &op->o_req_dn, &mdn ) ) {
212 send_ldap_result( op, rs );
216 /* FIXME: this fixes the bind problem right now; we need
217 * to use the asynchronous version to get the "matched"
218 * and more in case of failure ... */
219 /* FIXME: should be check if at least some of the op->o_ctrls
220 * can/should be passed? */
222 rs->sr_err = ldap_sasl_bind( msc->msc_ld, mdn.bv_val,
223 LDAP_SASL_SIMPLE, &op->orb_cred,
224 op->o_ctrls, NULL, &msgid );
225 if ( rs->sr_err == LDAP_SUCCESS ) {
227 struct timeval tv = { 0, 100000 };
229 int nretries = mt->mt_nretries;
236 tv.tv_usec = META_BIND_TIMEOUT;
237 switch ( ldap_result( msc->msc_ld, msgid, 0, &tv, &res ) ) {
239 Debug( LDAP_DEBUG_ANY,
240 "%s meta_back_single_bind: "
241 "ldap_result=0 nretries=%d%s\n",
242 op->o_log_prefix, nretries,
243 rebinding ? " rebinding" : "" );
245 if ( nretries != META_RETRY_NEVER ) {
246 ldap_pvt_thread_yield();
247 if ( nretries > 0 ) {
254 rs->sr_err = LDAP_BUSY;
256 ldap_abandon_ext( msc->msc_ld, msgid, NULL, NULL );
260 /* FIXME: some times the request times out
261 * while the other party is not willing to
262 * send a response any more. Give it a second
263 * chance with a freshly bound connection */
265 nretries = mt->mt_nretries;
269 ldap_get_option( msc->msc_ld, LDAP_OPT_ERROR_NUMBER,
273 ldap_abandon_ext( msc->msc_ld, msgid, NULL, NULL );
276 Debug( LDAP_DEBUG_ANY,
277 "### %s meta_back_single_bind: "
278 "err=%d nretries=%d\n",
279 op->o_log_prefix, rs->sr_err, nretries );
281 rc = slap_map_api2result( rs );
282 if ( rs->sr_err == LDAP_UNAVAILABLE && nretries != META_RETRY_NEVER ) {
283 ldap_pvt_thread_mutex_lock( &mi->mi_conn_mutex );
284 if ( mc->mc_refcnt == 1 ) {
285 ldap_unbind_ext_s( msc->msc_ld, NULL, NULL );
289 ( void )rewrite_session_delete( mt->mt_rwmap.rwm_rw, op->o_conn );
291 /* mc here must be the regular mc,
292 * reset and ready for init */
293 rc = meta_back_init_one_conn( op, rs,
294 mt, msc, LDAP_BACK_DONTSEND );
297 /* can't do anything about it */
301 ldap_pvt_thread_mutex_unlock( &mi->mi_conn_mutex );
304 if ( nretries > 0 ) {
307 ldap_pvt_thread_yield();
314 rc = ldap_parse_result( msc->msc_ld, res, &rs->sr_err,
315 NULL, NULL, NULL, NULL, 1 );
316 if ( rc != LDAP_SUCCESS ) {
323 if ( rs->sr_err != LDAP_SUCCESS ) {
324 rs->sr_err = slap_map_api2result( rs );
328 ber_bvreplace( &msc->msc_bound_ndn, &op->o_req_dn );
329 msc->msc_bound = META_BOUND;
330 mc->mc_auth_target = candidate;
332 if ( LDAP_BACK_SAVECRED( mi ) ) {
333 ber_bvreplace( &msc->msc_cred, &op->orb_cred );
334 ldap_set_rebind_proc( msc->msc_ld, meta_back_rebind, msc );
337 if ( mi->mi_cache.ttl != META_DNCACHE_DISABLED
338 && op->o_req_ndn.bv_len != 0 )
340 ( void )meta_dncache_update_entry( &mi->mi_cache,
341 &op->o_req_ndn, candidate );
345 if ( mdn.bv_val != op->o_req_dn.bv_val ) {
353 * meta_back_single_dobind
356 meta_back_single_dobind(
361 ldap_back_send_t sendok,
365 metainfo_t *mi = ( metainfo_t * )op->o_bd->be_private;
366 metatarget_t *mt = &mi->mi_targets[ candidate ];
367 metasingleconn_t *msc = &mc->mc_conns[ candidate ];
369 struct berval cred = BER_BVC( "" );
372 save_nretries = nretries;
375 * Otherwise an anonymous bind is performed
376 * (note: if the target was already bound, the anonymous
377 * bind clears the previous bind).
379 if ( !BER_BVISNULL( &msc->msc_bound_ndn ) ) {
380 ber_memfree( msc->msc_bound_ndn.bv_val );
381 BER_BVZERO( &msc->msc_bound_ndn );
384 if ( !BER_BVISNULL( &msc->msc_cred ) ) {
385 /* destroy sensitive data */
386 memset( msc->msc_cred.bv_val, 0, msc->msc_cred.bv_len );
387 ber_memfree( msc->msc_cred.bv_val );
388 BER_BVZERO( &msc->msc_cred );
391 /* FIXME: should we check if at least some of the op->o_ctrls
392 * can/should be passed? */
394 rc = ldap_sasl_bind( msc->msc_ld, "", LDAP_SASL_SIMPLE, &cred,
395 NULL, NULL, &msgid );
396 if ( rc == LDAP_SUCCESS ) {
398 struct timeval tv = { 0, 100000 };
405 tv.tv_usec = META_BIND_TIMEOUT;
406 switch ( ldap_result( msc->msc_ld, msgid, 0, &tv, &res ) ) {
408 Debug( LDAP_DEBUG_ANY,
409 "%s meta_back_single_dobind: "
410 "ldap_result=0 nretries=%d%s\n",
411 op->o_log_prefix, nretries,
412 rebinding ? " rebinding" : "" );
414 if ( nretries != META_RETRY_NEVER ) {
415 ldap_pvt_thread_yield();
416 if ( nretries > 0 ) {
426 ldap_abandon_ext( msc->msc_ld, msgid, NULL, NULL );
430 /* FIXME: some times the request times out
431 * while the other party is not willing to
432 * send a response any more. Give it a second
433 * chance with a freshly bound connection */
435 nretries = save_nretries;
439 ldap_get_option( msc->msc_ld,
440 LDAP_OPT_ERROR_NUMBER, &rs->sr_err );
443 ldap_abandon_ext( msc->msc_ld, msgid, NULL, NULL );
446 Debug( LDAP_DEBUG_ANY,
447 "### %s meta_back_single_dobind: "
448 "err=%d nretries=%d\n",
449 op->o_log_prefix, rs->sr_err, nretries );
451 rc = slap_map_api2result( rs );
452 if ( rc == LDAP_UNAVAILABLE && nretries != META_RETRY_NEVER ) {
454 ldap_pvt_thread_mutex_lock( &mi->mi_conn_mutex );
457 if ( mc->mc_refcnt == 1 ) {
458 ldap_unbind_ext_s( msc->msc_ld, NULL, NULL );
462 ( void )rewrite_session_delete( mt->mt_rwmap.rwm_rw, op->o_conn );
464 /* mc here must be the regular mc,
465 * reset and ready for init */
466 rc = meta_back_init_one_conn( op, rs,
467 mt, msc, LDAP_BACK_DONTSEND );
471 /* can't do anything about it */
472 rc = LDAP_UNAVAILABLE;
476 ldap_pvt_thread_mutex_unlock( &mi->mi_conn_mutex );
479 if ( rc == LDAP_SUCCESS ) {
480 ldap_pvt_thread_yield();
481 if ( nretries > 0 ) {
490 rc = ldap_parse_result( msc->msc_ld, res, &rs->sr_err,
491 NULL, NULL, NULL, NULL, 1 );
492 if ( rc == LDAP_SUCCESS ) {
493 rc = slap_map_api2result( rs );
500 rc = slap_map_api2result( rs );
504 if ( rc != LDAP_SUCCESS && ( sendok & LDAP_BACK_SENDERR ) ) {
505 send_ldap_result( op, rs );
519 ldap_back_send_t sendok )
521 metainfo_t *mi = ( metainfo_t * )op->o_bd->be_private;
527 SlapReply *candidates = meta_back_candidates_get( op );
529 if ( be_isroot( op ) ) {
533 Debug( LDAP_DEBUG_TRACE,
534 "%s meta_back_dobind: conn=%ld%s\n",
535 op->o_log_prefix, mc->mc_conn->c_connid,
536 isroot ? " (isroot)" : "" );
538 ldap_pvt_thread_mutex_lock( &mc->mc_mutex );
541 * all the targets are bound as pseudoroot
543 if ( mc->mc_auth_target == META_BOUND_ALL ) {
548 for ( i = 0; i < mi->mi_ntargets; i++ ) {
549 metatarget_t *mt = &mi->mi_targets[ i ];
550 metasingleconn_t *msc = &mc->mc_conns[ i ];
556 if ( candidates[ i ].sr_tag != META_CANDIDATE ) {
560 assert( msc->msc_ld != NULL );
563 * If the target is already bound it is skipped
565 if ( msc->msc_bound == META_BOUND && mc->mc_auth_target == i ) {
568 Debug( LDAP_DEBUG_TRACE, "%s meta_back_dobind[%d]: "
570 op->o_log_prefix, i, 0 );
574 if ( isroot && !BER_BVISNULL( &mi->mi_targets[ i ].mt_pseudorootdn ) )
578 op2.o_tag = LDAP_REQ_BIND;
579 op2.o_req_dn = mi->mi_targets[ i ].mt_pseudorootdn;
580 op2.o_req_ndn = mi->mi_targets[ i ].mt_pseudorootdn;
581 op2.orb_cred = mi->mi_targets[ i ].mt_pseudorootpw;
582 op2.orb_method = LDAP_AUTH_SIMPLE;
584 rc = meta_back_single_bind( &op2, rs, mc, i );
587 rc = meta_back_single_dobind( op, rs, mc, i,
588 LDAP_BACK_DONTSEND, mt->mt_nretries, 1 );
591 if ( rc != LDAP_SUCCESS ) {
592 Debug( LDAP_DEBUG_ANY, "%s meta_back_dobind[%d]: "
593 "(anonymous) err=%d\n",
594 op->o_log_prefix, i, rc );
597 * null cred bind should always succeed
598 * as anonymous, so a failure means
599 * the target is no longer candidate possibly
600 * due to technical reasons (remote host down?)
601 * so better clear the handle
603 /* leave the target candidate, but record the error for later use */
604 candidates[ i ].sr_err = rc;
605 if ( META_BACK_ONERR_STOP( mi ) ) {
612 Debug( LDAP_DEBUG_TRACE, "%s meta_back_dobind[%d]: "
614 op->o_log_prefix, i, 0 );
616 msc->msc_bound = META_ANONYMOUS;
621 ldap_pvt_thread_mutex_unlock( &mc->mc_mutex );
623 Debug( LDAP_DEBUG_TRACE,
624 "%s meta_back_dobind: conn=%ld bound=%d\n",
625 op->o_log_prefix, mc->mc_conn->c_connid, bound );
628 meta_back_release_conn( op, mc );
630 if ( sendok & LDAP_BACK_SENDERR ) {
631 if ( rs->sr_err == LDAP_SUCCESS ) {
632 rs->sr_err = LDAP_BUSY;
634 send_ldap_result( op, rs );
644 * This is a callback used for chasing referrals using the same
645 * credentials as the original user on this session.
650 LDAP_CONST char *url,
655 metasingleconn_t *msc = ( metasingleconn_t * )params;
657 return ldap_sasl_bind_s( ld, msc->msc_bound_ndn.bv_val,
658 LDAP_SASL_SIMPLE, &msc->msc_cred,
663 * FIXME: error return must be handled in a cleaner way ...
672 metainfo_t *mi = ( metainfo_t * )op->o_bd->be_private;
678 const char *save_rmsg = NULL,
680 void *rmatch_ctx = NULL;
682 if ( candidate != META_TARGET_NONE ) {
683 metasingleconn_t *msc = &mc->mc_conns[ candidate ];
685 rs->sr_err = LDAP_SUCCESS;
687 ldap_get_option( msc->msc_ld, LDAP_OPT_ERROR_NUMBER, &rs->sr_err );
688 if ( rs->sr_err != LDAP_SUCCESS ) {
690 * better check the type of error. In some cases
691 * (search ?) it might be better to return a
692 * success if at least one of the targets gave
693 * positive result ...
695 ldap_get_option( msc->msc_ld,
696 LDAP_OPT_ERROR_STRING, &rmsg );
697 if ( rmsg != NULL && rmsg[ 0 ] == '\0' ) {
698 ldap_memfree( rmsg );
702 ldap_get_option( msc->msc_ld,
703 LDAP_OPT_MATCHED_DN, &rmatch );
704 if ( rmatch != NULL && rmatch[ 0 ] == '\0' ) {
705 ldap_memfree( rmatch );
709 rerr = rs->sr_err = slap_map_api2result( rs );
711 Debug(LDAP_DEBUG_ANY,
712 "==> meta_back_op_result: target"
713 " <%d> sending msg \"%s\""
714 " (matched \"%s\")\n",
715 candidate, ( rmsg ? rmsg : "" ),
716 ( rmatch ? rmatch : "" ) );
720 for ( i = 0; i < mi->mi_ntargets; i++ ) {
721 metasingleconn_t *msc = &mc->mc_conns[ i ];
725 rs->sr_err = LDAP_SUCCESS;
727 ldap_get_option( msc->msc_ld, LDAP_OPT_ERROR_NUMBER, &rs->sr_err );
728 if ( rs->sr_err != LDAP_SUCCESS ) {
730 * better check the type of error. In some cases
731 * (search ?) it might be better to return a
732 * success if at least one of the targets gave
733 * positive result ...
735 ldap_get_option( msc->msc_ld,
736 LDAP_OPT_ERROR_STRING, &msg );
737 if ( msg != NULL && msg[ 0 ] == '\0' ) {
742 ldap_get_option( msc->msc_ld,
743 LDAP_OPT_MATCHED_DN, &match );
744 if ( match != NULL && match[ 0 ] == '\0' ) {
745 ldap_memfree( match );
749 rs->sr_err = slap_map_api2result( rs );
751 Debug(LDAP_DEBUG_ANY,
752 "==> meta_back_op_result: target"
753 " <%d> sending msg \"%s\""
754 " (matched \"%s\")\n",
755 i, ( msg ? msg : "" ),
756 ( match ? match : "" ) );
759 * FIXME: need to rewrite "match" (need rwinfo)
761 switch ( rs->sr_err ) {
766 ldap_memfree( rmsg );
771 if ( match != NULL ) {
773 ldap_memfree( rmatch );
786 ldap_memfree( match );
793 if ( rmsg != NULL ) {
794 save_rmsg = rs->sr_text;
797 if ( rmatch != NULL ) {
798 struct berval dn, pdn;
800 ber_str2bv( rmatch, 0, 0, &dn );
801 if ( dnPretty( NULL, &dn, &pdn, op->o_tmpmemctx ) == LDAP_SUCCESS ) {
802 ldap_memfree( rmatch );
803 rmatch_ctx = op->o_tmpmemctx;
806 save_rmatch = rs->sr_matched;
807 rs->sr_matched = rmatch;
809 send_ldap_result( op, rs );
810 if ( rmsg != NULL ) {
812 rs->sr_text = save_rmsg;
814 if ( rmatch != NULL ) {
815 ber_memfree_x( rmatch, rmatch_ctx );
816 rs->sr_matched = save_rmatch;
819 return ( ( rerr == LDAP_SUCCESS ) ? 0 : -1 );