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
27 #include <ac/socket.h>
28 #include <ac/string.h>
32 #include "../back-ldap/back-ldap.h"
33 #include "back-meta.h"
34 #undef ldap_debug /* silence a warning in ldap-int.h */
36 #include "../../../libraries/libldap/ldap-int.h"
47 meta_back_search_start(
51 metasingleconn_t *msc,
56 metainfo_t *mi = ( metainfo_t * )op->o_bd->be_private;
57 struct berval realbase = op->o_req_dn;
58 int realscope = op->ors_scope;
59 ber_len_t suffixlen = 0;
60 struct berval mbase = BER_BVNULL;
61 struct berval mfilter = BER_BVNULL;
62 char **mapped_attrs = NULL;
65 /* should we check return values? */
66 if ( op->ors_deref != -1 ) {
67 ldap_set_option( msc->msc_ld, LDAP_OPT_DEREF,
68 ( void * )&op->ors_deref);
70 if ( op->ors_tlimit != SLAP_NO_LIMIT ) {
71 ldap_set_option( msc->msc_ld, LDAP_OPT_TIMELIMIT,
72 ( void * )&op->ors_tlimit);
74 if ( op->ors_slimit != SLAP_NO_LIMIT ) {
75 ldap_set_option( msc->msc_ld, LDAP_OPT_SIZELIMIT,
76 ( void * )&op->ors_slimit);
79 dc->target = &mi->mi_targets[ candidate ];
82 * modifies the base according to the scope, if required
84 suffixlen = mi->mi_targets[ candidate ].mt_nsuffix.bv_len;
85 if ( suffixlen > op->o_req_ndn.bv_len ) {
86 switch ( op->ors_scope ) {
87 case LDAP_SCOPE_SUBTREE:
89 * make the target suffix the new base
90 * FIXME: this is very forgiving, because
91 * "illegal" searchBases may be turned
92 * into the suffix of the target; however,
93 * the requested searchBase already passed
94 * thru the candidate analyzer...
96 if ( dnIsSuffix( &mi->mi_targets[ candidate ].mt_nsuffix,
99 realbase = mi->mi_targets[ candidate ].mt_nsuffix;
103 * this target is no longer candidate
109 #ifdef LDAP_SCOPE_SUBORDINATE
110 case LDAP_SCOPE_SUBORDINATE:
111 #endif /* LDAP_SCOPE_SUBORDINATE */
112 case LDAP_SCOPE_ONELEVEL:
114 struct berval rdn = mi->mi_targets[ candidate ].mt_nsuffix;
115 rdn.bv_len -= op->o_req_ndn.bv_len + STRLENOF( "," );
116 if ( dnIsOneLevelRDN( &rdn )
117 && dnIsSuffix( &mi->mi_targets[ candidate ].mt_nsuffix, &op->o_req_ndn ) )
120 * if there is exactly one level,
121 * make the target suffix the new
122 * base, and make scope "base"
124 realbase = mi->mi_targets[ candidate ].mt_nsuffix;
125 #ifdef LDAP_SCOPE_SUBORDINATE
126 if ( op->ors_scope == LDAP_SCOPE_SUBORDINATE ) {
127 realscope = LDAP_SCOPE_SUBTREE;
129 #endif /* LDAP_SCOPE_SUBORDINATE */
131 realscope = LDAP_SCOPE_BASE;
134 } /* else continue with the next case */
137 case LDAP_SCOPE_BASE:
139 * this target is no longer candidate
146 * Rewrite the search base, if required
148 dc->ctx = "searchBase";
149 switch ( ldap_back_dn_massage( dc, &realbase, &mbase ) ) {
153 case REWRITE_REGEXEC_UNWILLING:
154 rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
155 rs->sr_text = "Operation not allowed";
156 send_ldap_result( op, rs );
159 case REWRITE_REGEXEC_ERR:
162 * this target is no longer candidate
170 rc = ldap_back_filter_map_rewrite( dc, op->ors_filter,
171 &mfilter, BACKLDAP_MAP );
176 case LDAP_COMPARE_FALSE:
179 * this target is no longer candidate
186 * Maps required attributes
188 rc = ldap_back_map_attrs( &mi->mi_targets[ candidate ].mt_rwmap.rwm_at,
189 op->ors_attrs, BACKLDAP_MAP, &mapped_attrs );
190 if ( rc != LDAP_SUCCESS ) {
192 * this target is no longer candidate
201 rc = ldap_search_ext( msc->msc_ld,
202 mbase.bv_val, realscope, mfilter.bv_val,
203 mapped_attrs, op->ors_attrsonly,
204 op->o_ctrls, NULL, NULL, op->ors_slimit,
205 &candidates[ candidate ].sr_msgid );
206 if ( rc == LDAP_SUCCESS ) {
210 candidates[ candidate ].sr_msgid = -1;
215 if ( mapped_attrs ) {
216 free( mapped_attrs );
218 if ( mfilter.bv_val != op->ors_filterstr.bv_val ) {
219 free( mfilter.bv_val );
221 if ( mbase.bv_val != realbase.bv_val ) {
222 free( mbase.bv_val );
229 meta_back_search( Operation *op, SlapReply *rs )
231 metainfo_t *mi = ( metainfo_t * )op->o_bd->be_private;
233 struct timeval tv = { 0, 0 };
234 LDAPMessage *res = NULL, *e;
235 int rc = 0, sres = LDAP_SUCCESS;
236 char *matched = NULL;
237 int i, last = 0, ncandidates = 0,
238 initial_candidates = 0, candidate_match = 0;
242 SlapReply *candidates = meta_back_candidates_get( op );
245 * controls are set in ldap_back_dobind()
247 * FIXME: in case of values return filter, we might want
248 * to map attrs and maybe rewrite value
250 mc = meta_back_getconn( op, rs, NULL, LDAP_BACK_SENDERR );
251 if ( !mc || !meta_back_dobind( op, rs, mc, LDAP_BACK_SENDERR ) ) {
255 dc.conn = op->o_conn;
261 for ( i = 0; i < mi->mi_ntargets; i++ ) {
262 metasingleconn_t *msc = &mc->mc_conns[ i ];
264 candidates[ i ].sr_msgid = -1;
266 if ( candidates[ i ].sr_tag != META_CANDIDATE ) {
269 candidates[ i ].sr_err = LDAP_SUCCESS;
270 candidates[ i ].sr_matched = NULL;
271 candidates[ i ].sr_text = NULL;
272 candidates[ i ].sr_ref = NULL;
273 candidates[ i ].sr_ctrls = NULL;
275 switch ( meta_back_search_start( op, rs, &dc, msc, i, candidates ) )
290 initial_candidates = ncandidates;
297 for ( i = 0; i < mi->mi_ntargets; i++ ) {
298 if ( candidates[ i ].sr_tag == META_CANDIDATE ) {
306 Debug( LDAP_DEBUG_ANY, "%s meta_back_search: ncandidates=%d "
307 "cnd=\"%s\"\n", op->o_log_prefix, ncandidates, cnd );
311 if ( initial_candidates == 0 ) {
312 send_ldap_error( op, rs, LDAP_NO_SUCH_OBJECT, NULL );
313 /* FIXME: find a way to look up the best match */
315 rc = LDAP_NO_SUCH_OBJECT;
319 /* We pull apart the ber result, stuff it into a slapd entry, and
320 * let send_search_entry stuff it back into ber format. Slow & ugly,
321 * but this is necessary for version matching, and for ACL processing.
325 * In case there are no candidates, no cycle takes place...
327 * FIXME: we might use a queue, to better balance the load
328 * among the candidates
330 for ( rc = 0; ncandidates > 0; ) {
331 int gotit = 0, doabandon = 0;
333 for ( i = 0; i < mi->mi_ntargets; i++ ) {
334 metasingleconn_t *msc = &mc->mc_conns[ i ];
336 if ( candidates[ i ].sr_msgid == -1 ) {
340 /* check for abandon */
341 if ( op->o_abandon ) {
346 * FIXME: handle time limit as well?
347 * Note that target servers are likely
348 * to handle it, so at some time we'll
349 * get a LDAP_TIMELIMIT_EXCEEDED from
352 rc = ldap_result( msc->msc_ld, candidates[ i ].sr_msgid,
356 /* timeout exceeded */
358 /* FIXME: res should not need to be freed */
359 assert( res == NULL );
363 } else if ( rc == -1 ) {
365 /* something REALLY bad happened! */
366 ( void )meta_clear_unused_candidates( op, -1 );
367 rs->sr_err = LDAP_OTHER;
368 savepriv = op->o_private;
369 op->o_private = (void *)i;
370 send_ldap_result( op, rs );
371 op->o_private = savepriv;
373 /* anything else needs be done? */
375 /* FIXME: res should not need to be freed */
376 assert( res == NULL );
380 } else if ( rc == LDAP_RES_SEARCH_ENTRY ) {
381 if ( --op->ors_slimit == -1 ) {
385 rs->sr_err = LDAP_SIZELIMIT_EXCEEDED;
386 savepriv = op->o_private;
387 op->o_private = (void *)i;
388 send_ldap_result( op, rs );
389 op->o_private = savepriv;
395 e = ldap_first_entry( msc->msc_ld, res );
396 savepriv = op->o_private;
397 op->o_private = (void *)i;
398 meta_send_entry( op, rs, mc, i, e );
399 op->o_private = savepriv;
408 * If scope is BASE, we need to jump out
409 * as soon as one entry is found; if
410 * the target pool is properly crafted,
411 * this should correspond to the sole
412 * entry that has the base DN
414 /* FIXME: this defeats the purpose of
415 * doing a search with scope == base and
416 * sizelimit = 1 to determine if a
417 * candidate is actually unique */
418 if ( op->ors_scope == LDAP_SCOPE_BASE
419 && rs->sr_nentries > 0 )
428 } else if ( rc == LDAP_RES_SEARCH_REFERENCE ) {
429 char **references = NULL;
434 rc = ldap_parse_reference( msc->msc_ld, res,
435 &references, &rs->sr_ctrls, 1 );
438 if ( rc != LDAP_SUCCESS ) {
442 if ( references == NULL ) {
446 #ifdef ENABLE_REWRITE
447 dc.ctx = "referralDN";
448 #else /* ! ENABLE_REWRITE */
451 #endif /* ! ENABLE_REWRITE */
453 /* FIXME: merge all and return at the end */
455 for ( cnt = 0; references[ cnt ]; cnt++ )
458 rs->sr_ref = ch_calloc( sizeof( struct berval ), cnt + 1 );
460 for ( cnt = 0; references[ cnt ]; cnt++ ) {
461 ber_str2bv( references[ cnt ], 0, 1, &rs->sr_ref[ cnt ] );
463 BER_BVZERO( &rs->sr_ref[ cnt ] );
465 ( void )ldap_back_referral_result_rewrite( &dc, rs->sr_ref );
467 if ( rs->sr_ref != NULL && !BER_BVISNULL( &rs->sr_ref[ 0 ] ) ) {
468 /* ignore return value by now */
469 savepriv = op->o_private;
470 op->o_private = (void *)i;
471 ( void )send_search_reference( op, rs );
472 op->o_private = savepriv;
474 ber_bvarray_free( rs->sr_ref );
480 ldap_value_free( references );
483 if ( rs->sr_ctrls ) {
484 ldap_controls_free( rs->sr_ctrls );
488 } else if ( rc == LDAP_RES_SEARCH_RESULT ) {
489 char buf[ SLAP_TEXT_BUFLEN ];
490 char **references = NULL;
492 if ( ldap_parse_result( msc->msc_ld,
494 &candidates[ i ].sr_err,
495 (char **)&candidates[ i ].sr_matched,
496 NULL /* (char **)&candidates[ i ].sr_text */ ,
498 &candidates[ i ].sr_ctrls, 1 ) )
501 ldap_get_option( msc->msc_ld,
502 LDAP_OPT_ERROR_NUMBER,
504 sres = slap_map_api2result( rs );
507 rs->sr_err = candidates[ i ].sr_err;
508 sres = slap_map_api2result( rs );
511 /* massage matchedDN if need be */
512 if ( candidates[ i ].sr_matched != NULL ) {
513 if ( candidates[ i ].sr_matched[ 0 ] == '\0' ) {
514 ldap_memfree( (char *)candidates[ i ].sr_matched );
515 candidates[ i ].sr_matched = NULL;
518 struct berval match, mmatch;
520 ber_str2bv( candidates[ i ].sr_matched,
523 dc.ctx = "matchedDN";
524 dc.target = &mi->mi_targets[ i ];
526 if ( !ldap_back_dn_massage( &dc, &match, &mmatch ) ) {
527 if ( mmatch.bv_val == match.bv_val ) {
528 candidates[ i ].sr_matched = ch_strdup( mmatch.bv_val );
531 candidates[ i ].sr_matched = mmatch.bv_val;
536 ldap_memfree( match.bv_val );
540 /* just get rid of the error message, if any */
541 if ( candidates[ i ].sr_text && candidates[ i ].sr_text[ 0 ] == '\0' )
543 ldap_memfree( (char *)candidates[ i ].sr_text );
544 candidates[ i ].sr_text = NULL;
547 /* add references to array */
552 for ( cnt = 0; references[ cnt ]; cnt++ )
555 sr_ref = ch_calloc( sizeof( struct berval ), cnt + 1 );
557 for ( cnt = 0; references[ cnt ]; cnt++ ) {
558 ber_str2bv( references[ cnt ], 0, 1, &sr_ref[ cnt ] );
560 BER_BVZERO( &sr_ref[ cnt ] );
562 ( void )ldap_back_referral_result_rewrite( &dc, sr_ref );
565 ldap_value_free( references );
567 if ( rs->sr_v2ref == NULL ) {
568 rs->sr_v2ref = sr_ref;
571 for ( cnt = 0; !BER_BVISNULL( &sr_ref[ cnt ] ); cnt++ ) {
572 ber_bvarray_add( &rs->sr_v2ref, &sr_ref[ cnt ] );
574 ber_memfree( sr_ref );
578 rs->sr_err = candidates[ i ].sr_err;
579 sres = slap_map_api2result( rs );
581 case LDAP_NO_SUCH_OBJECT:
582 /* is_ok is touched any time a valid
583 * (even intermediate) result is
584 * returned; as a consequence, if
585 * a candidate returns noSuchObject
586 * it is ignored and the candidate
587 * is simply demoted. */
599 snprintf( buf, sizeof( buf ),
600 "%s meta_back_search[%d] "
601 "match=\"%s\" err=%d\n",
603 candidates[ i ].sr_matched ? candidates[ i ].sr_matched : "",
604 candidates[ i ].sr_err );
605 Debug( LDAP_DEBUG_ANY, "%s", buf, 0, 0 );
611 * When no candidates are left,
612 * the outer cycle finishes
614 candidates[ i ].sr_msgid = -1;
623 /* check for abandon */
624 if ( op->o_abandon || doabandon ) {
625 for ( i = 0; i < mi->mi_ntargets; i++ ) {
626 metasingleconn_t *msc = &mc->mc_conns[ i ];
628 if ( candidates[ i ].sr_msgid != -1 ) {
629 ldap_abandon_ext( msc->msc_ld,
630 candidates[ i ].sr_msgid,
632 candidates[ i ].sr_msgid = -1;
636 if ( op->o_abandon ) {
644 tv.tv_usec = 100000; /* 0.1 s */
645 ldap_pvt_thread_yield();
655 * FIXME: need a better strategy to handle errors
657 rc = meta_back_op_result( mc, op, rs, META_TARGET_NONE );
662 * Rewrite the matched portion of the search base, if required
664 * FIXME: only the last one gets caught!
666 if ( candidate_match > 0 && rs->sr_nentries > 0 ) {
667 /* we use the first one */
668 for ( i = 0; i < mi->mi_ntargets; i++ ) {
669 if ( candidates[ i ].sr_tag == META_CANDIDATE
670 && candidates[ i ].sr_matched )
672 matched = (char *)candidates[ i ].sr_matched;
673 candidates[ i ].sr_matched = NULL;
685 for ( i = 0; i < mi->mi_ntargets; i++ ) {
686 if ( candidates[ i ].sr_tag == META_CANDIDATE ) {
694 snprintf( buf, sizeof( buf ), "%s meta_back_search: is_scope=%d is_ok=%d cnd=\"%s\"\n",
695 op->o_log_prefix, initial_candidates, is_ok, cnd );
697 Debug( LDAP_DEBUG_ANY, "%s", buf, 0, 0 );
702 * In case we returned at least one entry, we return LDAP_SUCCESS
703 * otherwise, the latter error code we got
705 * FIXME: we should handle error codes and return the more
706 * important/reasonable
709 if ( sres == LDAP_SUCCESS && rs->sr_v2ref ) {
710 sres = LDAP_REFERRAL;
713 rs->sr_matched = matched;
714 rs->sr_ref = ( sres == LDAP_REFERRAL ? rs->sr_v2ref : NULL );
715 savepriv = op->o_private;
716 op->o_private = (void *)mi->mi_ntargets;
717 send_ldap_result( op, rs );
718 op->o_private = savepriv;
719 rs->sr_matched = NULL;
727 if ( rs->sr_v2ref ) {
728 ber_bvarray_free( rs->sr_v2ref );
731 for ( i = 0; i < mi->mi_ntargets; i++ ) {
732 if ( candidates[ i ].sr_tag != META_CANDIDATE ) {
736 if ( candidates[ i ].sr_matched ) {
737 free( (char *)candidates[ i ].sr_matched );
738 candidates[ i ].sr_matched = NULL;
741 if ( candidates[ i ].sr_text ) {
742 ldap_memfree( (char *)candidates[ i ].sr_text );
743 candidates[ i ].sr_text = NULL;
746 if ( candidates[ i ].sr_ref ) {
747 ber_bvarray_free( candidates[ i ].sr_ref );
748 candidates[ i ].sr_ref = NULL;
751 if ( candidates[ i ].sr_ctrls ) {
752 ldap_controls_free( candidates[ i ].sr_ctrls );
753 candidates[ i ].sr_ctrls = NULL;
768 metainfo_t *mi = ( metainfo_t * )op->o_bd->be_private;
769 struct berval a, mapped;
771 BerElement ber = *e->lm_ber;
772 Attribute *attr, **attrp;
773 struct berval *bv, bdn;
777 if ( ber_scanf( &ber, "{m{", &bdn ) == LBER_ERROR ) {
778 return LDAP_DECODING_ERROR;
782 * Rewrite the dn of the result, if needed
784 dc.target = &mi->mi_targets[ target ];
785 dc.conn = op->o_conn;
787 dc.ctx = "searchResult";
789 rs->sr_err = ldap_back_dn_massage( &dc, &bdn, &ent.e_name );
790 if ( rs->sr_err != LDAP_SUCCESS) {
795 * Note: this may fail if the target host(s) schema differs
796 * from the one known to the meta, and a DN with unknown
797 * attributes is returned.
799 * FIXME: should we log anything, or delegate to dnNormalize?
801 if ( dnNormalize( 0, NULL, NULL, &ent.e_name, &ent.e_nname,
802 op->o_tmpmemctx ) != LDAP_SUCCESS )
804 return LDAP_INVALID_DN_SYNTAX;
810 if ( mi->mi_cache.ttl != META_DNCACHE_DISABLED ) {
811 ( void )meta_dncache_update_entry( &mi->mi_cache,
812 &ent.e_nname, target );
815 attrp = &ent.e_attrs;
817 dc.ctx = "searchAttrDN";
818 while ( ber_scanf( &ber, "{m", &a ) != LBER_ERROR ) {
821 ldap_back_map( &mi->mi_targets[ target ].mt_rwmap.rwm_at,
822 &a, &mapped, BACKLDAP_REMAP );
823 if ( BER_BVISNULL( &mapped ) || mapped.bv_val[0] == '\0' ) {
826 attr = ( Attribute * )ch_malloc( sizeof( Attribute ) );
827 if ( attr == NULL ) {
833 if ( slap_bv2ad( &mapped, &attr->a_desc, &text )
835 if ( slap_bv2undef_ad( &mapped, &attr->a_desc, &text )
838 char buf[ SLAP_TEXT_BUFLEN ];
840 snprintf( buf, sizeof( buf ),
841 "%s meta_send_entry(\"%s\"): "
842 "slap_bv2undef_ad(%s): %s\n",
843 op->o_log_prefix, ent.e_name.bv_val,
844 mapped.bv_val, text );
846 Debug( LDAP_DEBUG_ANY, "%s", buf, 0, 0 );
852 /* no subschemaSubentry */
853 if ( attr->a_desc == slap_schema.si_ad_subschemaSubentry ) {
856 * We eat target's subschemaSubentry because
857 * a search for this value is likely not
858 * to resolve to the appropriate backend;
859 * later, the local subschemaSubentry is
862 ( void )ber_scanf( &ber, "x" /* [W] */ );
868 if ( ber_scanf( &ber, "[W]", &attr->a_vals ) == LBER_ERROR
869 || attr->a_vals == NULL )
871 attr->a_vals = (struct berval *)&slap_dummy_bv;
873 } else if ( attr->a_desc == slap_schema.si_ad_objectClass
874 || attr->a_desc == slap_schema.si_ad_structuralObjectClass )
876 for ( last = 0; !BER_BVISNULL( &attr->a_vals[ last ] ); ++last );
878 for ( bv = attr->a_vals; !BER_BVISNULL( bv ); bv++ ) {
879 ldap_back_map( &mi->mi_targets[ target ].mt_rwmap.rwm_oc,
880 bv, &mapped, BACKLDAP_REMAP );
881 if ( BER_BVISNULL( &mapped ) || mapped.bv_val[0] == '\0') {
887 *bv = attr->a_vals[ last ];
888 BER_BVZERO( &attr->a_vals[ last ] );
891 } else if ( mapped.bv_val != bv->bv_val ) {
893 ber_dupbv( bv, &mapped );
897 * It is necessary to try to rewrite attributes with
898 * dn syntax because they might be used in ACLs as
899 * members of groups; since ACLs are applied to the
900 * rewritten stuff, no dn-based subecj clause could
901 * be used at the ldap backend side (see
902 * http://www.OpenLDAP.org/faq/data/cache/452.html)
903 * The problem can be overcome by moving the dn-based
904 * ACLs to the target directory server, and letting
905 * everything pass thru the ldap backend.
907 } else if ( attr->a_desc->ad_type->sat_syntax ==
908 slap_schema.si_syn_distinguishedName )
910 ldap_dnattr_result_rewrite( &dc, attr->a_vals );
912 } else if ( attr->a_desc == slap_schema.si_ad_ref ) {
913 ldap_back_referral_result_rewrite( &dc, attr->a_vals );
916 if ( last && attr->a_desc->ad_type->sat_equality &&
917 attr->a_desc->ad_type->sat_equality->smr_normalize ) {
920 attr->a_nvals = ch_malloc( ( last + 1 ) * sizeof( struct berval ) );
921 for ( i = 0; i<last; i++ ) {
922 attr->a_desc->ad_type->sat_equality->smr_normalize(
923 SLAP_MR_VALUE_OF_ATTRIBUTE_SYNTAX,
924 attr->a_desc->ad_type->sat_syntax,
925 attr->a_desc->ad_type->sat_equality,
926 &attr->a_vals[i], &attr->a_nvals[i],
929 BER_BVZERO( &attr->a_nvals[i] );
932 attr->a_nvals = attr->a_vals;
936 attrp = &attr->a_next;
939 rs->sr_attrs = op->ors_attrs;
941 send_search_entry( op, rs );
945 if ( !BER_BVISNULL( &ent.e_name ) && ent.e_name.bv_val != bdn.bv_val ) {
946 free( ent.e_name.bv_val );
947 BER_BVZERO( &ent.e_name );
949 if ( !BER_BVISNULL( &ent.e_nname ) ) {
950 free( ent.e_nname.bv_val );
951 BER_BVZERO( &ent.e_nname );