]> git.sur5r.net Git - openldap/blob - servers/slapd/back-ldap/chain.c
691463bf892935cf1a04cfedc8208f06890ba269
[openldap] / servers / slapd / back-ldap / chain.c
1 /* chain.c - chain LDAP operations */
2 /* $OpenLDAP$ */
3 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
4  *
5  * Copyright 2003-2012 The OpenLDAP Foundation.
6  * Portions Copyright 2003 Howard Chu.
7  * All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted only as authorized by the OpenLDAP
11  * Public License.
12  *
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>.
16  */
17 /* ACKNOWLEDGEMENTS:
18  * This work was initially developed by the Howard Chu for inclusion
19  * in OpenLDAP Software.
20  * This work was subsequently modified by Pierangelo Masarati.
21  */
22
23 #include "portable.h"
24
25 #include <stdio.h>
26
27 #include <ac/string.h>
28 #include <ac/socket.h>
29
30 #include "lutil.h"
31 #include "slap.h"
32 #include "back-ldap.h"
33 #include "config.h"
34
35 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
36 #define SLAP_CHAINING_DEFAULT                           LDAP_CHAINING_PREFERRED
37 #define SLAP_CH_RESOLVE_SHIFT                           SLAP_CONTROL_SHIFT
38 #define SLAP_CH_RESOLVE_MASK                            (0x3 << SLAP_CH_RESOLVE_SHIFT)
39 #define SLAP_CH_RESOLVE_CHAINING_PREFERRED              (LDAP_CHAINING_PREFERRED << SLAP_CH_RESOLVE_SHIFT)
40 #define SLAP_CH_RESOLVE_CHAINING_REQUIRED               (LDAP_CHAINING_REQUIRED << SLAP_CH_RESOLVE_SHIFT)
41 #define SLAP_CH_RESOLVE_REFERRALS_PREFERRED             (LDAP_REFERRALS_PREFERRED << SLAP_CH_RESOLVE_SHIFT)
42 #define SLAP_CH_RESOLVE_REFERRALS_REQUIRED              (LDAP_REFERRALS_REQUIRED << SLAP_CH_RESOLVE_SHIFT)
43 #define SLAP_CH_RESOLVE_DEFAULT                         (SLAP_CHAINING_DEFAULT << SLAP_CH_RESOLVE_SHIFT)
44 #define SLAP_CH_CONTINUATION_SHIFT                      (SLAP_CH_RESOLVE_SHIFT + 2)
45 #define SLAP_CH_CONTINUATION_MASK                       (0x3 << SLAP_CH_CONTINUATION_SHIFT)
46 #define SLAP_CH_CONTINUATION_CHAINING_PREFERRED         (LDAP_CHAINING_PREFERRED << SLAP_CH_CONTINUATION_SHIFT)
47 #define SLAP_CH_CONTINUATION_CHAINING_REQUIRED          (LDAP_CHAINING_REQUIRED << SLAP_CH_CONTINUATION_SHIFT)
48 #define SLAP_CH_CONTINUATION_REFERRALS_PREFERRED        (LDAP_REFERRALS_PREFERRED << SLAP_CH_CONTINUATION_SHIFT)
49 #define SLAP_CH_CONTINUATION_REFERRALS_REQUIRED         (LDAP_REFERRALS_REQUIRED << SLAP_CH_CONTINUATION_SHIFT)
50 #define SLAP_CH_CONTINUATION_DEFAULT                    (SLAP_CHAINING_DEFAULT << SLAP_CH_CONTINUATION_SHIFT)
51
52 #define o_chaining                      o_ctrlflag[sc_chainingBehavior]
53 #define get_chaining(op)                ((op)->o_chaining & SLAP_CONTROL_MASK)
54 #define get_chainingBehavior(op)        ((op)->o_chaining & (SLAP_CH_RESOLVE_MASK|SLAP_CH_CONTINUATION_MASK))
55 #define get_resolveBehavior(op)         ((op)->o_chaining & SLAP_CH_RESOLVE_MASK)
56 #define get_continuationBehavior(op)    ((op)->o_chaining & SLAP_CH_CONTINUATION_MASK)
57
58 static int              sc_chainingBehavior;
59 #endif /*  LDAP_CONTROL_X_CHAINING_BEHAVIOR */
60
61 typedef enum {
62         LDAP_CH_NONE = 0,
63         LDAP_CH_RES,
64         LDAP_CH_ERR
65 } ldap_chain_status_t;
66
67 static BackendInfo      *lback;
68
69 typedef struct ldap_chain_t {
70         /*
71          * A "template" ldapinfo_t gets all common configuration items;
72          * then, for each configured URI, an entry is created in the tree;
73          * all the specific configuration items get in the current URI 
74          * structure.
75          *
76          * Then, for each referral, extract the URI and lookup the
77          * related structure.  If configured to do so, allow URIs
78          * not found in the structure to create a temporary one
79          * that chains anonymously; maybe it can also be added to 
80          * the tree?  Should be all configurable.
81          */
82
83         /* "common" configuration info (anything occurring before an "uri") */
84         ldapinfo_t              *lc_common_li;
85
86         /* current configuration info */
87         ldapinfo_t              *lc_cfg_li;
88
89         /* tree of configured[/generated?] "uri" info */
90         ldap_avl_info_t         lc_lai;
91
92         /* max depth in nested referrals chaining */
93         int                     lc_max_depth;
94
95         unsigned                lc_flags;
96 #define LDAP_CHAIN_F_NONE               (0x00U)
97 #define LDAP_CHAIN_F_CHAINING           (0x01U)
98 #define LDAP_CHAIN_F_CACHE_URI          (0x02U)
99 #define LDAP_CHAIN_F_RETURN_ERR         (0x04U)
100
101 #define LDAP_CHAIN_ISSET(lc, f)         ( ( (lc)->lc_flags & (f) ) == (f) )
102 #define LDAP_CHAIN_CHAINING( lc )       LDAP_CHAIN_ISSET( (lc), LDAP_CHAIN_F_CHAINING )
103 #define LDAP_CHAIN_CACHE_URI( lc )      LDAP_CHAIN_ISSET( (lc), LDAP_CHAIN_F_CACHE_URI )
104 #define LDAP_CHAIN_RETURN_ERR( lc )     LDAP_CHAIN_ISSET( (lc), LDAP_CHAIN_F_RETURN_ERR )
105
106 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
107         LDAPControl             lc_chaining_ctrl;
108         char                    lc_chaining_ctrlflag;
109 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
110 } ldap_chain_t;
111
112 static int ldap_chain_db_init_common( BackendDB *be );
113 static int ldap_chain_db_init_one( BackendDB *be );
114 static int ldap_chain_db_open_one( BackendDB *be );
115 #define ldap_chain_db_close_one(be)     (0)
116 #define ldap_chain_db_destroy_one(be, rs)       (lback)->bi_db_destroy( (be), (rs) )
117
118 typedef struct ldap_chain_cb_t {
119         ldap_chain_status_t     lb_status;
120         ldap_chain_t            *lb_lc;
121         BI_op_func              *lb_op_f;
122         int                     lb_depth;
123 } ldap_chain_cb_t;
124
125 static int
126 ldap_chain_op(
127         Operation       *op,
128         SlapReply       *rs,
129         BI_op_func      *op_f,
130         BerVarray       ref,
131         int             depth );
132
133 static int
134 ldap_chain_search(
135         Operation       *op,
136         SlapReply       *rs,
137         BerVarray       ref,
138         int             depth );
139
140 static slap_overinst ldapchain;
141
142 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
143 static int
144 chaining_control_add(
145                 ldap_chain_t    *lc,
146                 Operation       *op, 
147                 LDAPControl     ***oldctrlsp )
148 {
149         LDAPControl     **ctrls = NULL;
150         int             c = 0;
151
152         *oldctrlsp = op->o_ctrls;
153
154         /* default chaining control not defined */
155         if ( !LDAP_CHAIN_CHAINING( lc ) ) {
156                 return 0;
157         }
158
159         /* already present */
160         if ( get_chaining( op ) > SLAP_CONTROL_IGNORED ) {
161                 return 0;
162         }
163
164         /* FIXME: check other incompatibilities */
165
166         /* add to other controls */
167         if ( op->o_ctrls ) {
168                 for ( c = 0; op->o_ctrls[ c ]; c++ )
169                         /* count them */ ;
170         }
171
172         ctrls = ch_calloc( sizeof( LDAPControl *), c + 2 );
173         ctrls[ 0 ] = &lc->lc_chaining_ctrl;
174         if ( op->o_ctrls ) {
175                 for ( c = 0; op->o_ctrls[ c ]; c++ ) {
176                         ctrls[ c + 1 ] = op->o_ctrls[ c ];
177                 }
178         }
179         ctrls[ c + 1 ] = NULL;
180
181         op->o_ctrls = ctrls;
182
183         op->o_chaining = lc->lc_chaining_ctrlflag;
184
185         return 0;
186 }
187
188 static int
189 chaining_control_remove(
190                 Operation       *op, 
191                 LDAPControl     ***oldctrlsp )
192 {
193         LDAPControl     **oldctrls = *oldctrlsp;
194
195         /* we assume that the first control is the chaining control
196          * added by the chain overlay, so it's the only one we explicitly 
197          * free */
198         if ( op->o_ctrls != oldctrls ) {
199                 assert( op->o_ctrls != NULL );
200                 assert( op->o_ctrls[ 0 ] != NULL );
201
202                 free( op->o_ctrls );
203
204                 op->o_chaining = 0;
205                 op->o_ctrls = oldctrls;
206         } 
207
208         *oldctrlsp = NULL;
209
210         return 0;
211 }
212 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
213
214 static int
215 ldap_chain_uri_cmp( const void *c1, const void *c2 )
216 {
217         const ldapinfo_t        *li1 = (const ldapinfo_t *)c1;
218         const ldapinfo_t        *li2 = (const ldapinfo_t *)c2;
219
220         assert( li1->li_bvuri != NULL );
221         assert( !BER_BVISNULL( &li1->li_bvuri[ 0 ] ) );
222         assert( BER_BVISNULL( &li1->li_bvuri[ 1 ] ) );
223
224         assert( li2->li_bvuri != NULL );
225         assert( !BER_BVISNULL( &li2->li_bvuri[ 0 ] ) );
226         assert( BER_BVISNULL( &li2->li_bvuri[ 1 ] ) );
227
228         return ber_bvcmp( &li1->li_bvuri[ 0 ], &li2->li_bvuri[ 0 ] );
229 }
230
231 static int
232 ldap_chain_uri_dup( void *c1, void *c2 )
233 {
234         ldapinfo_t      *li1 = (ldapinfo_t *)c1;
235         ldapinfo_t      *li2 = (ldapinfo_t *)c2;
236
237         assert( li1->li_bvuri != NULL );
238         assert( !BER_BVISNULL( &li1->li_bvuri[ 0 ] ) );
239         assert( BER_BVISNULL( &li1->li_bvuri[ 1 ] ) );
240
241         assert( li2->li_bvuri != NULL );
242         assert( !BER_BVISNULL( &li2->li_bvuri[ 0 ] ) );
243         assert( BER_BVISNULL( &li2->li_bvuri[ 1 ] ) );
244
245         if ( ber_bvcmp( &li1->li_bvuri[ 0 ], &li2->li_bvuri[ 0 ] ) == 0 ) {
246                 return -1;
247         }
248
249         return 0;
250 }
251
252 /*
253  * Search specific response that strips entryDN from entries
254  */
255 static int
256 ldap_chain_cb_search_response( Operation *op, SlapReply *rs )
257 {
258         ldap_chain_cb_t *lb = (ldap_chain_cb_t *)op->o_callback->sc_private;
259
260         assert( op->o_tag == LDAP_REQ_SEARCH );
261
262         /* if in error, don't proceed any further */
263         if ( lb->lb_status == LDAP_CH_ERR ) {
264                 return 0;
265         }
266
267         if ( rs->sr_type == REP_SEARCH ) {
268                 Attribute       **ap = &rs->sr_entry->e_attrs;
269
270                 for ( ; *ap != NULL; ap = &(*ap)->a_next ) {
271                         /* will be generated later by frontend
272                          * (a cleaner solution would be that
273                          * the frontend checks if it already exists */
274                         if ( ad_cmp( (*ap)->a_desc, slap_schema.si_ad_entryDN ) == 0 )
275                         {
276                                 Attribute *a = *ap;
277
278                                 *ap = (*ap)->a_next;
279                                 attr_free( a );
280
281                                 /* there SHOULD be one only! */
282                                 break;
283                         }
284                 }
285
286                 /* tell the frontend not to add generated
287                  * operational attributes */
288                 rs->sr_flags |= REP_NO_OPERATIONALS;
289                 
290                 return SLAP_CB_CONTINUE;
291
292         } else if ( rs->sr_type == REP_SEARCHREF ) {
293                 /* if we get it here, it means the library was unable
294                  * to chase the referral... */
295                 if ( lb->lb_depth < lb->lb_lc->lc_max_depth && rs->sr_ref != NULL ) {
296                         rs->sr_err = ldap_chain_search( op, rs, rs->sr_ref, lb->lb_depth );
297                 }
298
299 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
300                 if ( rs->sr_err == LDAP_REFERRAL && get_chaining( op ) > SLAP_CONTROL_IGNORED ) {
301                         switch ( get_continuationBehavior( op ) ) {
302                         case SLAP_CH_RESOLVE_CHAINING_REQUIRED:
303                                 lb->lb_status = LDAP_CH_ERR;
304                                 return rs->sr_err = LDAP_X_CANNOT_CHAIN;
305
306                         default:
307                                 break;
308                         }
309                 }
310 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
311                 return SLAP_CB_CONTINUE;
312
313         } else if ( rs->sr_type == REP_RESULT ) {
314                 if ( rs->sr_err == LDAP_REFERRAL
315                         && lb->lb_depth < lb->lb_lc->lc_max_depth
316                         && rs->sr_ref != NULL )
317                 {
318                         rs->sr_err = ldap_chain_op( op, rs, lb->lb_op_f, rs->sr_ref, lb->lb_depth );
319                 }
320
321                 /* back-ldap tried to send result */
322                 lb->lb_status = LDAP_CH_RES;
323         }
324
325         return 0;
326 }
327
328 /*
329  * Dummy response that simply traces if back-ldap tried to send 
330  * anything to the client
331  */
332 static int
333 ldap_chain_cb_response( Operation *op, SlapReply *rs )
334 {
335         ldap_chain_cb_t *lb = (ldap_chain_cb_t *)op->o_callback->sc_private;
336
337         /* if in error, don't proceed any further */
338         if ( lb->lb_status == LDAP_CH_ERR ) {
339                 return 0;
340         }
341
342         if ( rs->sr_type == REP_RESULT ) {
343 retry:;
344                 switch ( rs->sr_err ) {
345                 case LDAP_COMPARE_TRUE:
346                 case LDAP_COMPARE_FALSE:
347                         if ( op->o_tag != LDAP_REQ_COMPARE ) {
348                                 return rs->sr_err;
349                         }
350                         /* fallthru */
351
352                 case LDAP_SUCCESS:
353                         lb->lb_status = LDAP_CH_RES;
354                         break;
355
356                 case LDAP_REFERRAL:
357                         if ( lb->lb_depth < lb->lb_lc->lc_max_depth && rs->sr_ref != NULL ) {
358                                 rs->sr_err = ldap_chain_op( op, rs, lb->lb_op_f, rs->sr_ref, lb->lb_depth );
359                                 goto retry;
360                         }
361
362 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
363                         if ( get_chaining( op ) > SLAP_CONTROL_IGNORED ) {
364                                 switch ( get_continuationBehavior( op ) ) {
365                                 case SLAP_CH_RESOLVE_CHAINING_REQUIRED:
366                                         lb->lb_status = LDAP_CH_ERR;
367                                         return rs->sr_err = LDAP_X_CANNOT_CHAIN;
368
369                                 default:
370                                         break;
371                                 }
372                         }
373 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
374                         break;
375
376                 default:
377                         return rs->sr_err;
378                 }
379
380         } else if ( op->o_tag == LDAP_REQ_SEARCH && rs->sr_type == REP_SEARCH )
381         {
382                 /* strip the entryDN attribute, but keep returning results */
383                 (void)ldap_chain_cb_search_response( op, rs );
384         }
385
386         return SLAP_CB_CONTINUE;
387 }
388
389 static int
390 ldap_chain_op(
391         Operation       *op,
392         SlapReply       *rs,
393         BI_op_func      *op_f,
394         BerVarray       ref,
395         int             depth )
396 {
397         slap_overinst   *on = (slap_overinst *) op->o_bd->bd_info;
398         ldap_chain_cb_t *lb = (ldap_chain_cb_t *)op->o_callback->sc_private;
399         ldap_chain_t    *lc = (ldap_chain_t *)on->on_bi.bi_private;
400         struct berval   odn = op->o_req_dn,
401                         ondn = op->o_req_ndn;
402         ldapinfo_t      li = { 0 }, *lip = NULL;
403         struct berval   bvuri[ 2 ] = { { 0 } };
404
405         /* NOTE: returned if ref is empty... */
406         int             rc = LDAP_OTHER,
407                         first_rc;
408
409 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
410         LDAPControl     **ctrls = NULL;
411         
412         (void)chaining_control_add( lc, op, &ctrls );
413 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
414
415         li.li_bvuri = bvuri;
416         first_rc = -1;
417         for ( ; !BER_BVISNULL( ref ); ref++ ) {
418                 SlapReply       rs2 = { 0 };
419                 LDAPURLDesc     *srv = NULL;
420                 req_search_s    save_oq_search = op->oq_search,
421                                 tmp_oq_search = { 0 };
422                 struct berval   dn = BER_BVNULL,
423                                 pdn = odn,
424                                 ndn = ondn;
425                 char            *filter = NULL;
426                 int             temporary = 0;
427                 int             free_dn = 0;
428                         
429                 /* We're setting the URI of the first referral;
430                  * what if there are more?
431
432 Document: RFC 4511
433
434 4.1.10. Referral 
435    ...
436    If the client wishes to progress the operation, it MUST follow the 
437    referral by contacting one of the supported services. If multiple 
438    URIs are present, the client assumes that any supported URI may be 
439    used to progress the operation. 
440
441                  * so we actually need to follow exactly one,
442                  * and we can assume any is fine.
443                  */
444         
445                 /* parse reference and use 
446                  * proto://[host][:port]/ only */
447                 rc = ldap_url_parse_ext( ref->bv_val, &srv, LDAP_PVT_URL_PARSE_NONE );
448                 if ( rc != LDAP_URL_SUCCESS ) {
449                         Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_op: unable to parse ref=\"%s\"\n",
450                                 op->o_log_prefix, ref->bv_val, 0 );
451
452                         /* try next */
453                         rc = LDAP_OTHER;
454                         continue;
455                 }
456
457                 if ( op->o_tag == LDAP_REQ_SEARCH ) {
458                         if ( srv->lud_scope != LDAP_SCOPE_DEFAULT ) {
459                                 /* RFC 4511: if scope is present, use it */
460                                 tmp_oq_search.rs_scope = srv->lud_scope;
461
462                         } else {
463                                 /* RFC 4511: if scope is absent, use original */
464                                 tmp_oq_search.rs_scope = op->ors_scope;
465                         }
466                 }
467
468                 rc = LDAP_SUCCESS;
469                 srv->lud_scope = LDAP_SCOPE_DEFAULT;
470                 dn.bv_val = srv->lud_dn;
471                 filter = srv->lud_filter;
472
473                 /* normalize DN */
474                 if ( srv->lud_dn == NULL || srv->lud_dn[0] == '\0' ) {
475                         if ( srv->lud_dn == NULL ) {
476                                 srv->lud_dn = "";
477                         }
478
479                 } else {
480                         ber_str2bv( srv->lud_dn, 0, 0, &dn );
481                         rc = dnPrettyNormal( NULL, &dn, &pdn, &ndn, op->o_tmpmemctx );
482                         if ( rc == LDAP_SUCCESS ) {
483                                 /* remove DN essentially because later on 
484                                  * ldap_initialize() will parse the URL 
485                                  * as a comma-separated URL list */
486                                 srv->lud_dn = "";
487                                 free_dn = 1;
488                         }
489                 }
490
491                 /* prepare filter */
492                 if ( rc == LDAP_SUCCESS && op->o_tag == LDAP_REQ_SEARCH ) {
493                         /* filter */
494                         if ( srv->lud_filter != NULL
495                                 && srv->lud_filter[0] != '\0'
496                                 && strcasecmp( srv->lud_filter, "(objectClass=*)" ) != 0 )
497                         {
498                                 /* RFC 4511: if filter is present, use it;
499                                  * otherwise, use original */
500                                 tmp_oq_search.rs_filter = str2filter_x( op, srv->lud_filter );
501                                 if ( tmp_oq_search.rs_filter != NULL ) {
502                                         filter2bv_x( op, tmp_oq_search.rs_filter, &tmp_oq_search.rs_filterstr );
503
504                                 } else {
505                                         Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_op: ref=\"%s\": unable to parse filter=\"%s\"\n",
506                                                 op->o_log_prefix, ref->bv_val, srv->lud_filter );
507                                         rc = LDAP_OTHER;
508                                 }
509                         }
510                 }
511                 srv->lud_filter = NULL;
512
513                 if ( rc == LDAP_SUCCESS ) {
514                         li.li_uri = ldap_url_desc2str( srv );
515                 }
516
517                 srv->lud_dn = dn.bv_val;
518                 srv->lud_filter = filter;
519                 ldap_free_urldesc( srv );
520
521                 if ( rc != LDAP_SUCCESS ) {
522                         /* try next */
523                         rc = LDAP_OTHER;
524                         continue;
525                 }
526
527                 if ( li.li_uri == NULL ) {
528                         Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_op: ref=\"%s\" unable to reconstruct URI\n",
529                                 op->o_log_prefix, ref->bv_val, 0 );
530
531                         /* try next */
532                         rc = LDAP_OTHER;
533                         goto further_cleanup;
534                 }
535
536                 Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_op: ref=\"%s\" -> \"%s\"\n",
537                         op->o_log_prefix, ref->bv_val, li.li_uri );
538
539                 op->o_req_dn = pdn;
540                 op->o_req_ndn = ndn;
541
542                 if ( op->o_tag == LDAP_REQ_SEARCH ) {
543                         op->ors_scope = tmp_oq_search.rs_scope;
544                         if ( tmp_oq_search.rs_filter != NULL ) {
545                                 op->ors_filter = tmp_oq_search.rs_filter;
546                                 op->ors_filterstr = tmp_oq_search.rs_filterstr;
547                         }
548                 }
549
550                 ber_str2bv( li.li_uri, 0, 0, &li.li_bvuri[ 0 ] );
551
552                 /* Searches for a ldapinfo in the avl tree */
553                 ldap_pvt_thread_mutex_lock( &lc->lc_lai.lai_mutex );
554                 lip = (ldapinfo_t *)avl_find( lc->lc_lai.lai_tree, 
555                         (caddr_t)&li, ldap_chain_uri_cmp );
556                 ldap_pvt_thread_mutex_unlock( &lc->lc_lai.lai_mutex );
557
558                 if ( lip != NULL ) {
559                         op->o_bd->be_private = (void *)lip;
560
561                         Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_op: ref=\"%s\": URI=\"%s\" found in cache\n",
562                                 op->o_log_prefix, ref->bv_val, li.li_uri );
563
564                 } else {
565                         rc = ldap_chain_db_init_one( op->o_bd );
566                         if ( rc != 0 ) {
567                                 Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_op: ref=\"%s\" unable to init back-ldap for URI=\"%s\"\n",
568                                         op->o_log_prefix, ref->bv_val, li.li_uri );
569                                 goto cleanup;
570                         }
571                         lip = (ldapinfo_t *)op->o_bd->be_private;
572                         lip->li_uri = li.li_uri;
573                         lip->li_bvuri = bvuri;
574                         rc = ldap_chain_db_open_one( op->o_bd );
575                         if ( rc != 0 ) {
576                                 Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_op: ref=\"%s\" unable to open back-ldap for URI=\"%s\"\n",
577                                         op->o_log_prefix, ref->bv_val, li.li_uri );
578                                 lip->li_uri = NULL;
579                                 lip->li_bvuri = NULL;
580                                 (void)ldap_chain_db_destroy_one( op->o_bd, NULL);
581                                 goto cleanup;
582                         }
583
584                         if ( LDAP_CHAIN_CACHE_URI( lc ) ) {
585                                 ldap_pvt_thread_mutex_lock( &lc->lc_lai.lai_mutex );
586                                 if ( avl_insert( &lc->lc_lai.lai_tree,
587                                         (caddr_t)lip, ldap_chain_uri_cmp, ldap_chain_uri_dup ) )
588                                 {
589                                         /* someone just inserted another;
590                                          * don't bother, use this and then
591                                          * just free it */
592                                         temporary = 1;
593                                 }
594                                 ldap_pvt_thread_mutex_unlock( &lc->lc_lai.lai_mutex );
595
596                         } else {
597                                 temporary = 1;
598                         }
599
600                         Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_op: ref=\"%s\" %s\n",
601                                 op->o_log_prefix, ref->bv_val, temporary ? "temporary" : "caching" );
602                 }
603
604                 lb->lb_op_f = op_f;
605                 lb->lb_depth = depth + 1;
606
607                 rc = op_f( op, &rs2 );
608
609                 /* note the first error */
610                 if ( first_rc == -1 ) {
611                         first_rc = rc;
612                 }
613
614 cleanup:;
615                 ldap_memfree( li.li_uri );
616                 li.li_uri = NULL;
617
618                 if ( temporary ) {
619                         lip->li_uri = NULL;
620                         lip->li_bvuri = NULL;
621                         (void)ldap_chain_db_close_one( op->o_bd );
622                         (void)ldap_chain_db_destroy_one( op->o_bd, NULL );
623                 }
624
625 further_cleanup:;
626                 if ( free_dn ) {
627                         op->o_tmpfree( pdn.bv_val, op->o_tmpmemctx );
628                         op->o_tmpfree( ndn.bv_val, op->o_tmpmemctx );
629                 }
630         
631                 if ( op->o_tag == LDAP_REQ_SEARCH ) {   
632                         if ( tmp_oq_search.rs_filter != NULL ) {
633                                 filter_free_x( op, tmp_oq_search.rs_filter, 1 );
634                         }
635
636                         if ( !BER_BVISNULL( &tmp_oq_search.rs_filterstr ) ) {
637                                 slap_sl_free( tmp_oq_search.rs_filterstr.bv_val, op->o_tmpmemctx );
638                         }
639
640                         op->oq_search = save_oq_search;
641                 }
642
643                 if ( rc == LDAP_SUCCESS && rs2.sr_err == LDAP_SUCCESS ) {
644                         *rs = rs2;
645                         break;
646                 }
647
648                 rc = rs2.sr_err;
649         }
650
651         op->o_req_dn = odn;
652         op->o_req_ndn = ondn;
653
654 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
655         (void)chaining_control_remove( op, &ctrls );
656 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
657
658         if ( rc != LDAP_SUCCESS && first_rc > 0 ) {
659                 rc = first_rc;
660         }
661
662         return rc;
663 }
664
665 static int
666 ldap_chain_search(
667         Operation       *op,
668         SlapReply       *rs,
669         BerVarray       ref,
670         int             depth )
671
672 {
673         slap_overinst   *on = (slap_overinst *) op->o_bd->bd_info;
674         ldap_chain_cb_t *lb = (ldap_chain_cb_t *)op->o_callback->sc_private;
675         ldap_chain_t    *lc = (ldap_chain_t *)on->on_bi.bi_private;
676         ldapinfo_t      li = { 0 }, *lip = NULL;
677         struct berval   bvuri[ 2 ] = { { 0 } };
678
679         struct berval   odn = op->o_req_dn,
680                         ondn = op->o_req_ndn;
681         Entry           *save_entry = rs->sr_entry;
682         slap_mask_t     save_flags = rs->sr_flags;
683
684         int             rc = LDAP_OTHER,
685                         first_rc = -1;
686
687 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
688         LDAPControl     **ctrls = NULL;
689         
690         (void)chaining_control_add( lc, op, &ctrls );
691 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
692
693         assert( rs->sr_type == REP_SEARCHREF );
694
695         rs->sr_type = REP_SEARCH;
696
697         /* if we parse the URI then by no means 
698          * we can cache stuff or reuse connections, 
699          * because in back-ldap there's no caching
700          * based on the URI value, which is supposed
701          * to be set once for all (correct?) */
702         li.li_bvuri = bvuri;
703         for ( ; !BER_BVISNULL( &ref[0] ); ref++ ) {
704                 SlapReply       rs2 = { REP_RESULT };
705                 LDAPURLDesc     *srv;
706                 req_search_s    save_oq_search = op->oq_search,
707                                 tmp_oq_search = { 0 };
708                 struct berval   dn,
709                                 pdn = op->o_req_dn,
710                                 ndn = op->o_req_ndn;
711                 char            *filter = NULL;
712                 int             temporary = 0;
713                 int             free_dn = 0;
714
715                 /* parse reference and use
716                  * proto://[host][:port]/ only */
717                 rc = ldap_url_parse_ext( ref[0].bv_val, &srv, LDAP_PVT_URL_PARSE_NONE );
718                 if ( rc != LDAP_URL_SUCCESS ) {
719                         Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_search: unable to parse ref=\"%s\"\n",
720                                 op->o_log_prefix, ref->bv_val, 0 );
721
722                         /* try next */
723                         rs->sr_err = LDAP_OTHER;
724                         continue;
725                 }
726
727                 if ( srv->lud_scope != LDAP_SCOPE_DEFAULT ) {
728                         /* RFC 4511: if scope is present, use it */
729                         tmp_oq_search.rs_scope = srv->lud_scope;
730
731                 } else {
732                         /* RFC 4511: if scope is absent, use original */
733                         /* Section 4.5.3: if scope is onelevel, use base */
734                         if ( op->ors_scope == LDAP_SCOPE_ONELEVEL )
735                                 tmp_oq_search.rs_scope = LDAP_SCOPE_BASE;
736                         else
737                                 tmp_oq_search.rs_scope = op->ors_scope;
738                 }
739
740                 rc = LDAP_SUCCESS;
741                 srv->lud_scope = LDAP_SCOPE_DEFAULT;
742                 dn.bv_val = srv->lud_dn;
743                 filter = srv->lud_filter;
744
745                 /* normalize DN */
746                 if ( srv->lud_dn == NULL || srv->lud_dn[0] == '\0' ) {
747                         if ( srv->lud_dn == NULL ) {
748                                 srv->lud_dn = "";
749                         }
750
751                         if ( save_entry != NULL ) {
752                                 /* use the "right" DN, if available */
753                                 pdn = save_entry->e_name;
754                                 ndn = save_entry->e_nname;
755                         } /* else leave the original req DN in place, if any RFC 4511 */
756                         
757                 } else {
758                         /* RFC 4511: if DN is present, use it */
759                         ber_str2bv( srv->lud_dn, 0, 0, &dn );
760                         rc = dnPrettyNormal( NULL, &dn, &pdn, &ndn, op->o_tmpmemctx );
761                         if ( rc == LDAP_SUCCESS ) {
762                                 /* remove DN essentially because later on 
763                                  * ldap_initialize() will parse the URL 
764                                  * as a comma-separated URL list */
765                                 srv->lud_dn = "";
766                                 free_dn = 1;
767                         }
768                 }
769
770                 /* prepare filter */
771                 if ( rc == LDAP_SUCCESS ) {
772                         /* filter */
773                         if ( srv->lud_filter != NULL
774                                 && srv->lud_filter[0] != '\0'
775                                 && strcasecmp( srv->lud_filter, "(objectClass=*)" ) != 0 )
776                         {
777                                 /* RFC 4511: if filter is present, use it;
778                                  * otherwise, use original */
779                                 tmp_oq_search.rs_filter = str2filter_x( op, srv->lud_filter );
780                                 if ( tmp_oq_search.rs_filter != NULL ) {
781                                         filter2bv_x( op, tmp_oq_search.rs_filter, &tmp_oq_search.rs_filterstr );
782
783                                 } else {
784                                         Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_search: ref=\"%s\": unable to parse filter=\"%s\"\n",
785                                                 op->o_log_prefix, ref->bv_val, srv->lud_filter );
786                                         rc = LDAP_OTHER;
787                                 }
788                         }
789                 }
790                 srv->lud_filter = NULL;
791
792                 if ( rc == LDAP_SUCCESS ) {
793                         li.li_uri = ldap_url_desc2str( srv );
794                 }
795
796                 srv->lud_dn = dn.bv_val;
797                 srv->lud_filter = filter;
798                 ldap_free_urldesc( srv );
799
800                 if ( rc != LDAP_SUCCESS || li.li_uri == NULL ) {
801                         Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_search: ref=\"%s\" unable to reconstruct URI\n",
802                                 op->o_log_prefix, ref->bv_val, 0 );
803
804                         /* try next */
805                         rc = LDAP_OTHER;
806                         goto further_cleanup;
807                 }
808
809                 Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_search: ref=\"%s\" -> \"%s\"\n",
810                         op->o_log_prefix, ref->bv_val, li.li_uri );
811
812                 op->o_req_dn = pdn;
813                 op->o_req_ndn = ndn;
814                 op->ors_scope = tmp_oq_search.rs_scope;
815                 if ( tmp_oq_search.rs_filter != NULL ) {
816                         op->ors_filter = tmp_oq_search.rs_filter;
817                         op->ors_filterstr = tmp_oq_search.rs_filterstr;
818                 }
819
820                 ber_str2bv( li.li_uri, 0, 0, &li.li_bvuri[ 0 ] );
821
822                 /* Searches for a ldapinfo in the avl tree */
823                 ldap_pvt_thread_mutex_lock( &lc->lc_lai.lai_mutex );
824                 lip = (ldapinfo_t *)avl_find( lc->lc_lai.lai_tree, 
825                         (caddr_t)&li, ldap_chain_uri_cmp );
826                 ldap_pvt_thread_mutex_unlock( &lc->lc_lai.lai_mutex );
827
828                 if ( lip != NULL ) {
829                         op->o_bd->be_private = (void *)lip;
830
831                         Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_search: ref=\"%s\": URI=\"%s\" found in cache\n",
832                                 op->o_log_prefix, ref->bv_val, li.li_uri );
833
834                 } else {
835                         /* if none is found, create a temporary... */
836                         rc = ldap_chain_db_init_one( op->o_bd );
837                         if ( rc != 0 ) {
838                                 Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_search: ref=\"%s\" unable to init back-ldap for URI=\"%s\"\n",
839                                         op->o_log_prefix, ref->bv_val, li.li_uri );
840                                 goto cleanup;
841                         }
842                         lip = (ldapinfo_t *)op->o_bd->be_private;
843                         lip->li_uri = li.li_uri;
844                         lip->li_bvuri = bvuri;
845                         rc = ldap_chain_db_open_one( op->o_bd );
846                         if ( rc != 0 ) {
847                                 Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_search: ref=\"%s\" unable to open back-ldap for URI=\"%s\"\n",
848                                         op->o_log_prefix, ref->bv_val, li.li_uri );
849                                 lip->li_uri = NULL;
850                                 lip->li_bvuri = NULL;
851                                 (void)ldap_chain_db_destroy_one( op->o_bd, NULL );
852                                 goto cleanup;
853                         }
854
855                         if ( LDAP_CHAIN_CACHE_URI( lc ) ) {
856                                 ldap_pvt_thread_mutex_lock( &lc->lc_lai.lai_mutex );
857                                 if ( avl_insert( &lc->lc_lai.lai_tree,
858                                         (caddr_t)lip, ldap_chain_uri_cmp, ldap_chain_uri_dup ) )
859                                 {
860                                         /* someone just inserted another;
861                                          * don't bother, use this and then
862                                          * just free it */
863                                         temporary = 1;
864                                 }
865                                 ldap_pvt_thread_mutex_unlock( &lc->lc_lai.lai_mutex );
866
867                         } else {
868                                 temporary = 1;
869                         }
870
871                         Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_search: ref=\"%s\" %s\n",
872                                 op->o_log_prefix, ref->bv_val, temporary ? "temporary" : "caching" );
873                 }
874
875                 lb->lb_op_f = lback->bi_op_search;
876                 lb->lb_depth = depth + 1;
877
878                 /* FIXME: should we also copy filter and scope?
879                  * according to RFC3296, no */
880                 rc = lback->bi_op_search( op, &rs2 );
881                 if ( first_rc == -1 ) {
882                         first_rc = rc;
883                 }
884
885 cleanup:;
886                 ldap_memfree( li.li_uri );
887                 li.li_uri = NULL;
888
889                 if ( temporary ) {
890                         lip->li_uri = NULL;
891                         lip->li_bvuri = NULL;
892                         (void)ldap_chain_db_close_one( op->o_bd );
893                         (void)ldap_chain_db_destroy_one( op->o_bd, NULL );
894                 }
895                 
896 further_cleanup:;
897                 if ( free_dn ) {
898                         op->o_tmpfree( pdn.bv_val, op->o_tmpmemctx );
899                         op->o_tmpfree( ndn.bv_val, op->o_tmpmemctx );
900                 }
901
902                 op->o_req_dn = odn;
903                 op->o_req_ndn = ondn;
904
905                 if ( tmp_oq_search.rs_filter != NULL ) {
906                         filter_free_x( op, tmp_oq_search.rs_filter, 1 );
907                 }
908
909                 if ( !BER_BVISNULL( &tmp_oq_search.rs_filterstr ) ) {
910                         slap_sl_free( tmp_oq_search.rs_filterstr.bv_val, op->o_tmpmemctx );
911                 }
912
913                 op->oq_search = save_oq_search;
914                 
915                 if ( rc == LDAP_SUCCESS && rs2.sr_err == LDAP_SUCCESS ) {
916                         *rs = rs2;
917                         break;
918                 }
919
920                 rc = rs2.sr_err;
921         }
922
923 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
924         (void)chaining_control_remove( op, &ctrls );
925 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
926
927         op->o_req_dn = odn;
928         op->o_req_ndn = ondn;
929         rs->sr_type = REP_SEARCHREF;
930         rs->sr_entry = save_entry;
931         rs->sr_flags = save_flags;
932
933         if ( rc != LDAP_SUCCESS ) {
934                 /* couldn't chase any of the referrals */
935                 if ( first_rc != -1 ) {
936                         rc = first_rc;
937
938                 } else {
939                         rc = SLAP_CB_CONTINUE;
940                 }
941         }
942
943         return rc;
944 }
945
946 static int
947 ldap_chain_response( Operation *op, SlapReply *rs )
948 {
949         slap_overinst   *on = (slap_overinst *)op->o_bd->bd_info;
950         ldap_chain_t    *lc = (ldap_chain_t *)on->on_bi.bi_private;
951         BackendDB       db, *bd = op->o_bd;
952         ldap_chain_cb_t lb = { 0 };
953         slap_callback   *sc = op->o_callback,
954                         sc2 = { 0 };
955         int             rc = 0;
956         const char      *text = NULL;
957         const char      *matched;
958         BerVarray       ref;
959         struct berval   ndn = op->o_ndn;
960
961         int             sr_err = rs->sr_err;
962         slap_reply_t    sr_type = rs->sr_type;
963 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
964         slap_mask_t     chain_mask = 0;
965         ber_len_t       chain_shift = 0;
966 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
967
968         if ( rs->sr_err != LDAP_REFERRAL && rs->sr_type != REP_SEARCHREF ) {
969                 return SLAP_CB_CONTINUE;
970         }
971
972 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
973         if ( rs->sr_err == LDAP_REFERRAL && get_chaining( op ) > SLAP_CONTROL_IGNORED ) {
974                 switch ( get_resolveBehavior( op ) ) {
975                 case SLAP_CH_RESOLVE_REFERRALS_PREFERRED:
976                 case SLAP_CH_RESOLVE_REFERRALS_REQUIRED:
977                         return SLAP_CB_CONTINUE;
978
979                 default:
980                         chain_mask = SLAP_CH_RESOLVE_MASK;
981                         chain_shift = SLAP_CH_RESOLVE_SHIFT;
982                         break;
983                 }
984
985         } else if ( rs->sr_type == REP_SEARCHREF && get_chaining( op ) > SLAP_CONTROL_IGNORED ) {
986                 switch ( get_continuationBehavior( op ) ) {
987                 case SLAP_CH_CONTINUATION_REFERRALS_PREFERRED:
988                 case SLAP_CH_CONTINUATION_REFERRALS_REQUIRED:
989                         return SLAP_CB_CONTINUE;
990
991                 default:
992                         chain_mask = SLAP_CH_CONTINUATION_MASK;
993                         chain_shift = SLAP_CH_CONTINUATION_SHIFT;
994                         break;
995                 }
996         }
997 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
998
999         /*
1000          * TODO: add checks on who/when chain operations; e.g.:
1001          *   a) what identities are authorized
1002          *   b) what request DN (e.g. only chain requests rooted at <DN>)
1003          *   c) what referral URIs
1004          *   d) what protocol scheme (e.g. only ldaps://)
1005          *   e) what ssf
1006          */
1007
1008         db = *op->o_bd;
1009         SLAP_DBFLAGS( &db ) &= ~SLAP_DBFLAG_MONITORING;
1010         op->o_bd = &db;
1011
1012         text = rs->sr_text;
1013         rs->sr_text = NULL;
1014         matched = rs->sr_matched;
1015         rs->sr_matched = NULL;
1016         ref = rs->sr_ref;
1017         rs->sr_ref = NULL;
1018
1019         /* we need this to know if back-ldap returned any result */
1020         lb.lb_lc = lc;
1021         sc2.sc_next = sc->sc_next;
1022         sc2.sc_private = &lb;
1023         sc2.sc_response = ldap_chain_cb_response;
1024         op->o_callback = &sc2;
1025
1026         /* Chaining can be performed by a privileged user on behalf
1027          * of normal users, using the ProxyAuthz control, by exploiting
1028          * the identity assertion feature of back-ldap; see idassert-*
1029          * directives in slapd-ldap(5).
1030          *
1031          * FIXME: the idassert-authcDN is one, will it be fine regardless
1032          * of the URI we obtain from the referral?
1033          */
1034
1035         switch ( op->o_tag ) {
1036         case LDAP_REQ_BIND: {
1037                 struct berval   rndn = op->o_req_ndn;
1038                 Connection      *conn = op->o_conn;
1039
1040                 /* FIXME: can we really get a referral for binds? */
1041                 op->o_req_ndn = slap_empty_bv;
1042                 op->o_conn = NULL;
1043                 rc = ldap_chain_op( op, rs, lback->bi_op_bind, ref, 0 );
1044                 op->o_req_ndn = rndn;
1045                 op->o_conn = conn;
1046                 }
1047                 break;
1048
1049         case LDAP_REQ_ADD:
1050                 rc = ldap_chain_op( op, rs, lback->bi_op_add, ref, 0 );
1051                 break;
1052
1053         case LDAP_REQ_DELETE:
1054                 rc = ldap_chain_op( op, rs, lback->bi_op_delete, ref, 0 );
1055                 break;
1056
1057         case LDAP_REQ_MODRDN:
1058                 rc = ldap_chain_op( op, rs, lback->bi_op_modrdn, ref, 0 );
1059                 break;
1060
1061         case LDAP_REQ_MODIFY:
1062                 rc = ldap_chain_op( op, rs, lback->bi_op_modify, ref, 0 );
1063                 break;
1064
1065         case LDAP_REQ_COMPARE:
1066                 rc = ldap_chain_op( op, rs, lback->bi_op_compare, ref, 0 );
1067                 if ( rs->sr_err == LDAP_COMPARE_TRUE || rs->sr_err == LDAP_COMPARE_FALSE ) {
1068                         rc = LDAP_SUCCESS;
1069                 }
1070                 break;
1071
1072         case LDAP_REQ_SEARCH:
1073                 if ( rs->sr_type == REP_SEARCHREF ) {
1074                         sc2.sc_response = ldap_chain_cb_search_response;
1075                         rc = ldap_chain_search( op, rs, ref, 0 );
1076                         
1077                 } else {
1078                         /* we might get here before any database actually 
1079                          * performed a search; in those cases, we need
1080                          * to check limits, to make sure safe defaults
1081                          * are in place */
1082                         if ( op->ors_limit != NULL || limits_check( op, rs ) == 0 ) {
1083                                 rc = ldap_chain_op( op, rs, lback->bi_op_search, ref, 0 );
1084
1085                         } else {
1086                                 rc = SLAP_CB_CONTINUE;
1087                         }
1088                 }
1089                 break;
1090
1091         case LDAP_REQ_EXTENDED:
1092                 rc = ldap_chain_op( op, rs, lback->bi_extended, ref, 0 );
1093                 /* FIXME: ldap_back_extended() by design 
1094                  * doesn't send result; frontend is expected
1095                  * to send it... */
1096                 /* FIXME: what about chaining? */
1097                 if ( rc != SLAPD_ABANDON ) {
1098                         rs->sr_err = rc;
1099                         send_ldap_extended( op, rs );
1100                         rc = LDAP_SUCCESS;
1101                 }
1102                 lb.lb_status = LDAP_CH_RES;
1103                 break;
1104
1105         default:
1106                 rc = SLAP_CB_CONTINUE;
1107                 break;
1108         }
1109
1110         switch ( rc ) {
1111         case SLAPD_ABANDON:
1112                 goto dont_chain;
1113
1114         case LDAP_SUCCESS:
1115         case LDAP_REFERRAL:
1116                 sr_err = rs->sr_err;
1117                 /* slapd-ldap sent response */
1118                 if ( !op->o_abandon && lb.lb_status != LDAP_CH_RES ) {
1119                         /* FIXME: should we send response? */
1120                         Debug( LDAP_DEBUG_ANY,
1121                                 "%s: ldap_chain_response: "
1122                                 "overlay should have sent result.\n",
1123                                 op->o_log_prefix, 0, 0 );
1124                 }
1125                 break;
1126
1127         default:
1128 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1129                 if ( lb.lb_status == LDAP_CH_ERR && rs->sr_err == LDAP_X_CANNOT_CHAIN ) {
1130                         goto cannot_chain;
1131                 }
1132
1133                 switch ( ( get_chainingBehavior( op ) & chain_mask ) >> chain_shift ) {
1134                 case LDAP_CHAINING_REQUIRED:
1135 cannot_chain:;
1136                         op->o_callback = NULL;
1137                         send_ldap_error( op, rs, LDAP_X_CANNOT_CHAIN,
1138                                 "operation cannot be completed without chaining" );
1139                         goto dont_chain;
1140
1141                 default:
1142 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1143                         if ( LDAP_CHAIN_RETURN_ERR( lc ) ) {
1144                                 sr_err = rs->sr_err = rc;
1145                                 rs->sr_type = sr_type;
1146
1147                         } else {
1148                                 rc = SLAP_CB_CONTINUE;
1149                                 rs->sr_err = sr_err;
1150                                 rs->sr_type = sr_type;
1151                                 rs->sr_text = text;
1152                                 rs->sr_matched = matched;
1153                                 rs->sr_ref = ref;
1154                         }
1155 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1156                         break;
1157                 }
1158 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1159         }
1160
1161         if ( lb.lb_status == LDAP_CH_NONE && rc != SLAPD_ABANDON ) {
1162                 /* give the remaining callbacks a chance */
1163                 op->o_callback = sc->sc_next;
1164                 rc = rs->sr_err = slap_map_api2result( rs );
1165                 send_ldap_result( op, rs );
1166         }
1167
1168 dont_chain:;
1169         rs->sr_err = sr_err;
1170         rs->sr_type = sr_type;
1171         rs->sr_text = text;
1172         rs->sr_matched = matched;
1173         rs->sr_ref = ref;
1174         op->o_bd = bd;
1175         op->o_callback = sc;
1176         op->o_ndn = ndn;
1177
1178         return rc;
1179 }
1180
1181 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1182 static int
1183 ldap_chain_parse_ctrl(
1184         Operation       *op,
1185         SlapReply       *rs,
1186         LDAPControl     *ctrl );
1187
1188 static int
1189 str2chain( const char *s )
1190 {
1191         if ( strcasecmp( s, "chainingPreferred" ) == 0 ) {
1192                 return LDAP_CHAINING_PREFERRED;
1193                 
1194         } else if ( strcasecmp( s, "chainingRequired" ) == 0 ) {
1195                 return LDAP_CHAINING_REQUIRED;
1196
1197         } else if ( strcasecmp( s, "referralsPreferred" ) == 0 ) {
1198                 return LDAP_REFERRALS_PREFERRED;
1199                 
1200         } else if ( strcasecmp( s, "referralsRequired" ) == 0 ) {
1201                 return LDAP_REFERRALS_REQUIRED;
1202         }
1203
1204         return -1;
1205 }
1206 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1207
1208 /*
1209  * configuration...
1210  */
1211
1212 enum {
1213         CH_CHAINING = 1,
1214         CH_CACHE_URI,
1215         CH_MAX_DEPTH,
1216         CH_RETURN_ERR,
1217
1218         CH_LAST
1219 };
1220
1221 static ConfigDriver chain_cf_gen;
1222 static ConfigCfAdd chain_cfadd;
1223 static ConfigLDAPadd chain_ldadd;
1224 #ifdef SLAP_CONFIG_DELETE
1225 static ConfigLDAPdel chain_lddel;
1226 #endif
1227
1228 static ConfigTable chaincfg[] = {
1229 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1230         { "chain-chaining", "args",
1231                 2, 4, 0, ARG_MAGIC|ARG_BERVAL|CH_CHAINING, chain_cf_gen,
1232                 "( OLcfgOvAt:3.1 NAME 'olcChainingBehavior' "
1233                         "DESC 'Chaining behavior control parameters (draft-sermersheim-ldap-chaining)' "
1234                         "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
1235 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1236         { "chain-cache-uri", "TRUE/FALSE",
1237                 2, 2, 0, ARG_MAGIC|ARG_ON_OFF|CH_CACHE_URI, chain_cf_gen,
1238                 "( OLcfgOvAt:3.2 NAME 'olcChainCacheURI' "
1239                         "DESC 'Enables caching of URIs not present in configuration' "
1240                         "SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
1241         { "chain-max-depth", "args",
1242                 2, 2, 0, ARG_MAGIC|ARG_INT|CH_MAX_DEPTH, chain_cf_gen,
1243                 "( OLcfgOvAt:3.3 NAME 'olcChainMaxReferralDepth' "
1244                         "DESC 'max referral depth' "
1245                         "SYNTAX OMsInteger "
1246                         "EQUALITY integerMatch "
1247                         "SINGLE-VALUE )", NULL, NULL },
1248         { "chain-return-error", "TRUE/FALSE",
1249                 2, 2, 0, ARG_MAGIC|ARG_ON_OFF|CH_RETURN_ERR, chain_cf_gen,
1250                 "( OLcfgOvAt:3.4 NAME 'olcChainReturnError' "
1251                         "DESC 'Errors are returned instead of the original referral' "
1252                         "SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
1253         { NULL, NULL, 0, 0, 0, ARG_IGNORED }
1254 };
1255
1256 static ConfigOCs chainocs[] = {
1257         { "( OLcfgOvOc:3.1 "
1258                 "NAME 'olcChainConfig' "
1259                 "DESC 'Chain configuration' "
1260                 "SUP olcOverlayConfig "
1261                 "MAY ( "
1262 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1263                         "olcChainingBehavior $ "
1264 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1265                         "olcChainCacheURI $ "
1266                         "olcChainMaxReferralDepth $ "
1267                         "olcChainReturnError "
1268                         ") )",
1269                 Cft_Overlay, chaincfg, NULL, chain_cfadd },
1270         { "( OLcfgOvOc:3.2 "
1271                 "NAME 'olcChainDatabase' "
1272                 "DESC 'Chain remote server configuration' "
1273                 "AUXILIARY )",
1274                 Cft_Misc, olcDatabaseDummy, chain_ldadd
1275 #ifdef SLAP_CONFIG_DELETE
1276                 , NULL, chain_lddel
1277 #endif
1278         },
1279         { NULL, 0, NULL }
1280 };
1281
1282 static int
1283 chain_ldadd( CfEntryInfo *p, Entry *e, ConfigArgs *ca )
1284 {
1285         slap_overinst           *on;
1286         ldap_chain_t            *lc;
1287
1288         ldapinfo_t              *li;
1289
1290         AttributeDescription    *ad = NULL;
1291         Attribute               *at;
1292         const char              *text;
1293
1294         int                     rc;
1295
1296         if ( p->ce_type != Cft_Overlay
1297                 || !p->ce_bi
1298                 || p->ce_bi->bi_cf_ocs != chainocs )
1299         {
1300                 return LDAP_CONSTRAINT_VIOLATION;
1301         }
1302
1303         on = (slap_overinst *)p->ce_bi;
1304         lc = (ldap_chain_t *)on->on_bi.bi_private;
1305
1306         assert( ca->be == NULL );
1307         ca->be = (BackendDB *)ch_calloc( 1, sizeof( BackendDB ) );
1308
1309         ca->be->bd_info = (BackendInfo *)on;
1310
1311         rc = slap_str2ad( "olcDbURI", &ad, &text );
1312         assert( rc == LDAP_SUCCESS );
1313
1314         at = attr_find( e->e_attrs, ad );
1315 #if 0
1316         if ( lc->lc_common_li == NULL && at != NULL ) {
1317                 /* FIXME: we should generate an empty default entry
1318                  * if none is supplied */
1319                 Debug( LDAP_DEBUG_ANY, "slapd-chain: "
1320                         "first underlying database \"%s\" "
1321                         "cannot contain attribute \"%s\".\n",
1322                         e->e_name.bv_val, ad->ad_cname.bv_val, 0 );
1323                 rc = LDAP_CONSTRAINT_VIOLATION;
1324                 goto done;
1325
1326         } else
1327 #endif
1328         if ( lc->lc_common_li != NULL && at == NULL ) {
1329                 /* FIXME: we should generate an empty default entry
1330                  * if none is supplied */
1331                 Debug( LDAP_DEBUG_ANY, "slapd-chain: "
1332                         "subsequent underlying database \"%s\" "
1333                         "must contain attribute \"%s\".\n",
1334                         e->e_name.bv_val, ad->ad_cname.bv_val, 0 );
1335                 rc = LDAP_CONSTRAINT_VIOLATION;
1336                 goto done;
1337         }
1338
1339         if ( lc->lc_common_li == NULL ) {
1340                 rc = ldap_chain_db_init_common( ca->be );
1341
1342         } else {
1343                 rc = ldap_chain_db_init_one( ca->be );
1344         }
1345
1346         if ( rc != 0 ) {
1347                 Debug( LDAP_DEBUG_ANY, "slapd-chain: "
1348                         "unable to init %sunderlying database \"%s\".\n",
1349                         lc->lc_common_li == NULL ? "common " : "", e->e_name.bv_val, 0 );
1350                 return LDAP_CONSTRAINT_VIOLATION;
1351         }
1352
1353         li = ca->be->be_private;
1354
1355         if ( lc->lc_common_li == NULL ) {
1356                 lc->lc_common_li = li;
1357
1358         } else {
1359                 li->li_uri = ch_strdup( at->a_vals[ 0 ].bv_val );
1360                 value_add_one( &li->li_bvuri, &at->a_vals[ 0 ] );
1361                 if ( avl_insert( &lc->lc_lai.lai_tree, (caddr_t)li,
1362                         ldap_chain_uri_cmp, ldap_chain_uri_dup ) )
1363                 {
1364                         Debug( LDAP_DEBUG_ANY, "slapd-chain: "
1365                                 "database \"%s\" insert failed.\n",
1366                                 e->e_name.bv_val, 0, 0 );
1367                         rc = LDAP_CONSTRAINT_VIOLATION;
1368                         goto done;
1369                 }
1370         }
1371
1372         ca->ca_private = on;
1373
1374 done:;
1375         if ( rc != LDAP_SUCCESS ) {
1376                 (void)ldap_chain_db_destroy_one( ca->be, NULL );
1377                 ch_free( ca->be );
1378                 ca->be = NULL;
1379         }
1380
1381         return rc;
1382 }
1383
1384 typedef struct ldap_chain_cfadd_apply_t {
1385         Operation       *op;
1386         SlapReply       *rs;
1387         Entry           *p;
1388         ConfigArgs      *ca;
1389         int             count;
1390 } ldap_chain_cfadd_apply_t;
1391
1392 static int
1393 ldap_chain_cfadd_apply( void *datum, void *arg )
1394 {
1395         ldapinfo_t                      *li = (ldapinfo_t *)datum;
1396         ldap_chain_cfadd_apply_t        *lca = (ldap_chain_cfadd_apply_t *)arg;
1397
1398         struct berval                   bv;
1399
1400         /* FIXME: should not hardcode "olcDatabase" here */
1401         bv.bv_len = snprintf( lca->ca->cr_msg, sizeof( lca->ca->cr_msg ),
1402                 "olcDatabase={%d}%s", lca->count, lback->bi_type );
1403         bv.bv_val = lca->ca->cr_msg;
1404
1405         lca->ca->be->be_private = (void *)li;
1406         config_build_entry( lca->op, lca->rs, lca->p->e_private, lca->ca,
1407                 &bv, lback->bi_cf_ocs, &chainocs[1] );
1408
1409         lca->count++;
1410
1411         return 0;
1412 }
1413
1414 static int
1415 chain_cfadd( Operation *op, SlapReply *rs, Entry *p, ConfigArgs *ca )
1416 {
1417         CfEntryInfo     *pe = p->e_private;
1418         slap_overinst   *on = (slap_overinst *)pe->ce_bi;
1419         ldap_chain_t    *lc = (ldap_chain_t *)on->on_bi.bi_private;
1420         void            *priv = (void *)ca->be->be_private;
1421
1422         if ( lback->bi_cf_ocs ) {
1423                 ldap_chain_cfadd_apply_t        lca = { 0 };
1424
1425                 lca.op = op;
1426                 lca.rs = rs;
1427                 lca.p = p;
1428                 lca.ca = ca;
1429                 lca.count = 0;
1430
1431                 (void)ldap_chain_cfadd_apply( (void *)lc->lc_common_li, (void *)&lca );
1432
1433                 (void)avl_apply( lc->lc_lai.lai_tree, ldap_chain_cfadd_apply,
1434                         &lca, 1, AVL_INORDER );
1435
1436                 ca->be->be_private = priv;
1437         }
1438
1439         return 0;
1440 }
1441
1442 #ifdef SLAP_CONFIG_DELETE
1443 static int
1444 chain_lddel( CfEntryInfo *ce, Operation *op )
1445 {
1446         CfEntryInfo     *pe = ce->ce_parent;
1447         slap_overinst   *on = (slap_overinst *)pe->ce_bi;
1448         ldap_chain_t    *lc = (ldap_chain_t *)on->on_bi.bi_private;
1449         ldapinfo_t      *li = (ldapinfo_t *) ce->ce_be->be_private;
1450
1451         if ( li != lc->lc_common_li ) {
1452                 if (! avl_delete( &lc->lc_lai.lai_tree, li, ldap_chain_uri_cmp ) ) {
1453                         Debug( LDAP_DEBUG_ANY, "slapd-chain: avl_delete failed. "
1454                                 "\"%s\" not found.\n", li->li_uri, 0, 0 );
1455                         return -1;
1456                 }
1457         } else if ( lc->lc_lai.lai_tree ) {
1458                 Debug( LDAP_DEBUG_ANY, "slapd-chain: cannot delete first underlying "
1459                         "LDAP database when other databases are still present.\n", 0, 0, 0 );
1460                 return -1;
1461         } else {
1462                 lc->lc_common_li = NULL;
1463         }
1464
1465         ce->ce_be->bd_info = lback;
1466
1467         if ( ce->ce_be->bd_info->bi_db_close ) {
1468                 ce->ce_be->bd_info->bi_db_close( ce->ce_be, NULL );
1469         }
1470         if ( ce->ce_be->bd_info->bi_db_destroy ) {
1471                 ce->ce_be->bd_info->bi_db_destroy( ce->ce_be, NULL );
1472         }
1473
1474         ch_free(ce->ce_be);
1475         ce->ce_be = NULL;
1476
1477         return LDAP_SUCCESS;
1478 }
1479 #endif /* SLAP_CONFIG_DELETE */
1480
1481 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1482 static slap_verbmasks chaining_mode[] = {
1483         { BER_BVC("referralsRequired"),         LDAP_REFERRALS_REQUIRED },
1484         { BER_BVC("referralsPreferred"),        LDAP_REFERRALS_PREFERRED },
1485         { BER_BVC("chainingRequired"),          LDAP_CHAINING_REQUIRED },
1486         { BER_BVC("chainingPreferred"),         LDAP_CHAINING_PREFERRED },
1487         { BER_BVNULL,                           0 }
1488 };
1489 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1490
1491 static int
1492 chain_cf_gen( ConfigArgs *c )
1493 {
1494         slap_overinst   *on = (slap_overinst *)c->bi;
1495         ldap_chain_t    *lc = (ldap_chain_t *)on->on_bi.bi_private;
1496
1497         int             rc = 0;
1498
1499         if ( c->op == SLAP_CONFIG_EMIT ) {
1500                 switch( c->type ) {
1501 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1502                 case CH_CHAINING: {
1503                         struct berval   resolve = BER_BVNULL,
1504                                         continuation = BER_BVNULL;
1505
1506                         if ( !LDAP_CHAIN_CHAINING( lc ) ) {
1507                                 return 1;
1508                         }
1509
1510                         enum_to_verb( chaining_mode, ( ( lc->lc_chaining_ctrlflag & SLAP_CH_RESOLVE_MASK ) >> SLAP_CH_RESOLVE_SHIFT ), &resolve );
1511                         enum_to_verb( chaining_mode, ( ( lc->lc_chaining_ctrlflag & SLAP_CH_CONTINUATION_MASK ) >> SLAP_CH_CONTINUATION_SHIFT ), &continuation );
1512
1513                         c->value_bv.bv_len = STRLENOF( "resolve=" ) + resolve.bv_len
1514                                 + STRLENOF( " " )
1515                                 + STRLENOF( "continuation=" ) + continuation.bv_len;
1516                         c->value_bv.bv_val = ch_malloc( c->value_bv.bv_len + 1 );
1517                         snprintf( c->value_bv.bv_val, c->value_bv.bv_len + 1,
1518                                 "resolve=%s continuation=%s",
1519                                 resolve.bv_val, continuation.bv_val );
1520
1521                         if ( lc->lc_chaining_ctrl.ldctl_iscritical ) {
1522                                 c->value_bv.bv_val = ch_realloc( c->value_bv.bv_val,
1523                                         c->value_bv.bv_len + STRLENOF( " critical" ) + 1 );
1524                                 AC_MEMCPY( &c->value_bv.bv_val[ c->value_bv.bv_len ],
1525                                         " critical", STRLENOF( " critical" ) + 1 );
1526                                 c->value_bv.bv_len += STRLENOF( " critical" );
1527                         }
1528
1529                         break;
1530                 }
1531 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1532
1533                 case CH_CACHE_URI:
1534                         c->value_int = LDAP_CHAIN_CACHE_URI( lc );
1535                         break;
1536
1537                 case CH_MAX_DEPTH:
1538                         c->value_int = lc->lc_max_depth;
1539                         break;
1540
1541                 case CH_RETURN_ERR:
1542                         c->value_int = LDAP_CHAIN_RETURN_ERR( lc );
1543                         break;
1544
1545                 default:
1546                         assert( 0 );
1547                         rc = 1;
1548                 }
1549                 return rc;
1550
1551         } else if ( c->op == LDAP_MOD_DELETE ) {
1552                 switch( c->type ) {
1553                 case CH_CHAINING:
1554                         return 1;
1555
1556                 case CH_CACHE_URI:
1557                         lc->lc_flags &= ~LDAP_CHAIN_F_CACHE_URI;
1558                         break;
1559
1560                 case CH_MAX_DEPTH:
1561                         c->value_int = 0;
1562                         break;
1563
1564                 case CH_RETURN_ERR:
1565                         lc->lc_flags &= ~LDAP_CHAIN_F_RETURN_ERR;
1566                         break;
1567
1568                 default:
1569                         return 1;
1570                 }
1571                 return rc;
1572         }
1573
1574         switch( c->type ) {
1575         case CH_CHAINING: {
1576 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1577                 char                    **argv = c->argv;
1578                 int                     argc = c->argc;
1579                 BerElementBuffer        berbuf;
1580                 BerElement              *ber = (BerElement *)&berbuf;
1581                 int                     resolve = -1,
1582                                         continuation = -1,
1583                                         iscritical = 0;
1584                 Operation               op = { 0 };
1585                 SlapReply               rs = { 0 };
1586
1587                 lc->lc_chaining_ctrlflag = 0;
1588
1589                 for ( argc--, argv++; argc > 0; argc--, argv++ ) {
1590                         if ( strncasecmp( argv[ 0 ], "resolve=", STRLENOF( "resolve=" ) ) == 0 ) {
1591                                 resolve = str2chain( argv[ 0 ] + STRLENOF( "resolve=" ) );
1592                                 if ( resolve == -1 ) {
1593                                         Debug( LDAP_DEBUG_ANY, "%s: "
1594                                                 "illegal <resolve> value %s "
1595                                                 "in \"chain-chaining>\".\n",
1596                                                 c->log, argv[ 0 ], 0 );
1597                                         return 1;
1598                                 }
1599
1600                         } else if ( strncasecmp( argv[ 0 ], "continuation=", STRLENOF( "continuation=" ) ) == 0 ) {
1601                                 continuation = str2chain( argv[ 0 ] + STRLENOF( "continuation=" ) );
1602                                 if ( continuation == -1 ) {
1603                                         Debug( LDAP_DEBUG_ANY, "%s: "
1604                                                 "illegal <continuation> value %s "
1605                                                 "in \"chain-chaining\".\n",
1606                                                 c->log, argv[ 0 ], 0 );
1607                                         return 1;
1608                                 }
1609
1610                         } else if ( strcasecmp( argv[ 0 ], "critical" ) == 0 ) {
1611                                 iscritical = 1;
1612
1613                         } else {
1614                                 Debug( LDAP_DEBUG_ANY, "%s: "
1615                                         "unknown option in \"chain-chaining\".\n",
1616                                         c->log, 0, 0 );
1617                                 return 1;
1618                         }
1619                 }
1620
1621                 if ( resolve != -1 || continuation != -1 ) {
1622                         int     err;
1623
1624                         if ( resolve == -1 ) {
1625                                 /* default */
1626                                 resolve = SLAP_CHAINING_DEFAULT;
1627                         }
1628
1629                         ber_init2( ber, NULL, LBER_USE_DER );
1630
1631                         err = ber_printf( ber, "{e" /* } */, resolve );
1632                         if ( err == -1 ) {
1633                                 ber_free( ber, 1 );
1634                                 Debug( LDAP_DEBUG_ANY, "%s: "
1635                                         "chaining behavior control encoding error!\n",
1636                                         c->log, 0, 0 );
1637                                 return 1;
1638                         }
1639
1640                         if ( continuation > -1 ) {
1641                                 err = ber_printf( ber, "e", continuation );
1642                                 if ( err == -1 ) {
1643                                         ber_free( ber, 1 );
1644                                         Debug( LDAP_DEBUG_ANY, "%s: "
1645                                                 "chaining behavior control encoding error!\n",
1646                                                 c->log, 0, 0 );
1647                                         return 1;
1648                                 }
1649                         }
1650
1651                         err = ber_printf( ber, /* { */ "N}" );
1652                         if ( err == -1 ) {
1653                                 ber_free( ber, 1 );
1654                                 Debug( LDAP_DEBUG_ANY, "%s: "
1655                                         "chaining behavior control encoding error!\n",
1656                                         c->log, 0, 0 );
1657                                 return 1;
1658                         }
1659
1660                         if ( ber_flatten2( ber, &lc->lc_chaining_ctrl.ldctl_value, 0 ) == -1 ) {
1661                                 exit( EXIT_FAILURE );
1662                         }
1663
1664                 } else {
1665                         BER_BVZERO( &lc->lc_chaining_ctrl.ldctl_value );
1666                 }
1667
1668                 lc->lc_chaining_ctrl.ldctl_oid = LDAP_CONTROL_X_CHAINING_BEHAVIOR;
1669                 lc->lc_chaining_ctrl.ldctl_iscritical = iscritical;
1670
1671                 if ( ldap_chain_parse_ctrl( &op, &rs, &lc->lc_chaining_ctrl ) != LDAP_SUCCESS )
1672                 {
1673                         Debug( LDAP_DEBUG_ANY, "%s: "
1674                                 "unable to parse chaining control%s%s.\n",
1675                                 c->log, rs.sr_text ? ": " : "",
1676                                 rs.sr_text ? rs.sr_text : "" );
1677                         return 1;
1678                 }
1679
1680                 lc->lc_chaining_ctrlflag = op.o_chaining;
1681
1682                 lc->lc_flags |= LDAP_CHAIN_F_CHAINING;
1683
1684                 rc = 0;
1685 #else /* ! LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1686                 Debug( LDAP_DEBUG_ANY, "%s: "
1687                         "\"chaining\" control unsupported (ignored).\n",
1688                         c->log, 0, 0 );
1689 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1690                 } break;
1691
1692         case CH_CACHE_URI:
1693                 if ( c->value_int ) {
1694                         lc->lc_flags |= LDAP_CHAIN_F_CACHE_URI;
1695                 } else {
1696                         lc->lc_flags &= ~LDAP_CHAIN_F_CACHE_URI;
1697                 }
1698                 break;
1699
1700         case CH_MAX_DEPTH:
1701                 if ( c->value_int < 0 ) {
1702                         snprintf( c->cr_msg, sizeof( c->cr_msg ),
1703                                 "<%s> invalid max referral depth %d",
1704                                 c->argv[0], c->value_int );
1705                         Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1706                                 c->log, c->cr_msg, 0 );
1707                         rc = 1;
1708                         break;
1709                 }
1710                 lc->lc_max_depth = c->value_int;
1711
1712         case CH_RETURN_ERR:
1713                 if ( c->value_int ) {
1714                         lc->lc_flags |= LDAP_CHAIN_F_RETURN_ERR;
1715                 } else {
1716                         lc->lc_flags &= ~LDAP_CHAIN_F_RETURN_ERR;
1717                 }
1718                 break;
1719
1720         default:
1721                 assert( 0 );
1722                 return 1;
1723         }
1724         return rc;
1725 }
1726
1727 static int
1728 ldap_chain_db_init(
1729         BackendDB *be,
1730         ConfigReply *cr )
1731 {
1732         slap_overinst   *on = (slap_overinst *)be->bd_info;
1733         ldap_chain_t    *lc = NULL;
1734
1735         if ( lback == NULL ) {
1736                 lback = backend_info( "ldap" );
1737
1738                 if ( lback == NULL ) {
1739                         return 1;
1740                 }
1741         }
1742
1743         lc = ch_malloc( sizeof( ldap_chain_t ) );
1744         if ( lc == NULL ) {
1745                 return 1;
1746         }
1747         memset( lc, 0, sizeof( ldap_chain_t ) );
1748         lc->lc_max_depth = 1;
1749         ldap_pvt_thread_mutex_init( &lc->lc_lai.lai_mutex );
1750
1751         on->on_bi.bi_private = (void *)lc;
1752
1753         return 0;
1754 }
1755
1756 static int
1757 ldap_chain_db_config(
1758         BackendDB       *be,
1759         const char      *fname,
1760         int             lineno,
1761         int             argc,
1762         char            **argv )
1763 {
1764         slap_overinst   *on = (slap_overinst *)be->bd_info;
1765         ldap_chain_t    *lc = (ldap_chain_t *)on->on_bi.bi_private;
1766
1767         int             rc = SLAP_CONF_UNKNOWN;
1768
1769         if ( lc->lc_common_li == NULL ) {
1770                 BackendDB db = *be;
1771                 ldap_chain_db_init_common( &db );
1772                 lc->lc_common_li = lc->lc_cfg_li = (ldapinfo_t *)db.be_private;
1773         }
1774
1775         /* Something for the chain database? */
1776         if ( strncasecmp( argv[ 0 ], "chain-", STRLENOF( "chain-" ) ) == 0 ) {
1777                 char            *save_argv0 = argv[ 0 ];
1778                 BackendDB       db = *be;
1779                 static char     *allowed_argv[] = {
1780                         /* special: put URI here, so in the meanwhile
1781                          * it detects whether a new URI is being provided */
1782                         "uri",
1783                         "nretries",
1784                         "timeout",
1785                         /* flags */
1786                         "tls",
1787                         /* FIXME: maybe rebind-as-user should be allowed
1788                          * only within known URIs... */
1789                         "rebind-as-user",
1790                         "chase-referrals",
1791                         "t-f-support",
1792                         "proxy-whoami",
1793                         NULL
1794                 };
1795                 int             which_argv = -1;
1796
1797                 argv[ 0 ] += STRLENOF( "chain-" );
1798
1799                 for ( which_argv = 0; allowed_argv[ which_argv ]; which_argv++ ) {
1800                         if ( strcasecmp( argv[ 0 ], allowed_argv[ which_argv ] ) == 0 ) {
1801                                 break;
1802                         }
1803                 }
1804
1805                 if ( allowed_argv[ which_argv ] == NULL ) {
1806                         which_argv = -1;
1807
1808                         if ( lc->lc_cfg_li == lc->lc_common_li ) {
1809                                 Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1810                                         "\"%s\" only allowed within a URI directive.\n.",
1811                                         fname, lineno, argv[ 0 ] );
1812                                 return 1;
1813                         }
1814                 }
1815
1816                 if ( which_argv == 0 ) {
1817                         rc = ldap_chain_db_init_one( &db );
1818                         if ( rc != 0 ) {
1819                                 Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1820                                         "underlying slapd-ldap initialization failed.\n.",
1821                                         fname, lineno, 0 );
1822                                 return 1;
1823                         }
1824                         lc->lc_cfg_li = db.be_private;
1825                 }
1826
1827                 /* TODO: add checks on what other slapd-ldap(5) args
1828                  * should be put in the template; this is not quite
1829                  * harmful, because attributes that shouldn't don't
1830                  * get actually used, but the user should at least
1831                  * be warned.
1832                  */
1833
1834                 db.bd_info = lback;
1835                 db.be_private = (void *)lc->lc_cfg_li;
1836                 db.be_cf_ocs = lback->bi_cf_ocs;
1837
1838                 rc = config_generic_wrapper( &db, fname, lineno, argc, argv );
1839
1840                 argv[ 0 ] = save_argv0;
1841
1842                 if ( which_argv == 0 ) {
1843 private_destroy:;
1844                         if ( rc != 0 ) {
1845                                 db.bd_info = lback;
1846                                 db.be_private = (void *)lc->lc_cfg_li;
1847                                 ldap_chain_db_destroy_one( &db, NULL );
1848                                 lc->lc_cfg_li = NULL;
1849                         } else {
1850                                 if ( lc->lc_cfg_li->li_bvuri == NULL
1851                                         || BER_BVISNULL( &lc->lc_cfg_li->li_bvuri[ 0 ] )
1852                                         || !BER_BVISNULL( &lc->lc_cfg_li->li_bvuri[ 1 ] ) )
1853                                 {
1854                                         Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1855                                                 "no URI list allowed in slapo-chain.\n",
1856                                                 fname, lineno, 0 );
1857                                         rc = 1;
1858                                         goto private_destroy;
1859                                 }
1860
1861                                 if ( avl_insert( &lc->lc_lai.lai_tree,
1862                                         (caddr_t)lc->lc_cfg_li,
1863                                         ldap_chain_uri_cmp, ldap_chain_uri_dup ) )
1864                                 {
1865                                         Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1866                                                 "duplicate URI in slapo-chain.\n",
1867                                                 fname, lineno, 0 );
1868                                         rc = 1;
1869                                         goto private_destroy;
1870                                 }
1871                         }
1872                 }
1873         }
1874
1875         return rc;
1876 }
1877
1878 enum db_which {
1879         db_open = 0,
1880         db_close,
1881         db_destroy,
1882
1883         db_last
1884 };
1885
1886 typedef struct ldap_chain_db_apply_t {
1887         BackendDB       *be;
1888         BI_db_func      *func;
1889 } ldap_chain_db_apply_t;
1890
1891 static int
1892 ldap_chain_db_apply( void *datum, void *arg )
1893 {
1894         ldapinfo_t              *li = (ldapinfo_t *)datum;
1895         ldap_chain_db_apply_t   *lca = (ldap_chain_db_apply_t *)arg;
1896
1897         lca->be->be_private = (void *)li;
1898
1899         return lca->func( lca->be, NULL );
1900 }
1901
1902 static int
1903 ldap_chain_db_func(
1904         BackendDB *be,
1905         enum db_which which
1906 )
1907 {
1908         slap_overinst   *on = (slap_overinst *)be->bd_info;
1909         ldap_chain_t    *lc = (ldap_chain_t *)on->on_bi.bi_private;
1910
1911         int             rc = 0;
1912
1913         if ( lc ) {
1914                 BI_db_func      *func = (&lback->bi_db_open)[ which ];
1915
1916                 if ( func != NULL && lc->lc_common_li != NULL ) {
1917                         BackendDB               db = *be;
1918
1919                         db.bd_info = lback;
1920                         db.be_private = lc->lc_common_li;
1921
1922                         rc = func( &db, NULL );
1923
1924                         if ( rc != 0 ) {
1925                                 return rc;
1926                         }
1927
1928                         if ( lc->lc_lai.lai_tree != NULL ) {
1929                                 ldap_chain_db_apply_t   lca;
1930
1931                                 lca.be = &db;
1932                                 lca.func = func;
1933
1934                                 rc = avl_apply( lc->lc_lai.lai_tree,
1935                                         ldap_chain_db_apply, (void *)&lca,
1936                                         1, AVL_INORDER ) != AVL_NOMORE;
1937                         }
1938                 }
1939         }
1940
1941         return rc;
1942 }
1943
1944 static int
1945 ldap_chain_db_open(
1946         BackendDB       *be,
1947         ConfigReply     *cr )
1948 {
1949         slap_overinst   *on = (slap_overinst *) be->bd_info;
1950         ldap_chain_t    *lc = (ldap_chain_t *)on->on_bi.bi_private;
1951         slap_mask_t     monitoring;
1952         int             rc = 0;
1953
1954 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1955         rc = overlay_register_control( be, LDAP_CONTROL_X_CHAINING_BEHAVIOR );
1956         if ( rc != 0 ) {
1957                 return rc;
1958         }
1959 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1960
1961         if ( lc->lc_common_li == NULL ) {
1962                 void    *be_private = be->be_private;
1963                 ldap_chain_db_init_common( be );
1964                 lc->lc_common_li = lc->lc_cfg_li = (ldapinfo_t *)be->be_private;
1965                 be->be_private = be_private;
1966         }
1967
1968         /* filter out and restore monitoring */
1969         monitoring = ( SLAP_DBFLAGS( be ) & SLAP_DBFLAG_MONITORING );
1970         SLAP_DBFLAGS( be ) &= ~SLAP_DBFLAG_MONITORING;
1971         rc = ldap_chain_db_func( be, db_open );
1972         SLAP_DBFLAGS( be ) |= monitoring;
1973
1974         return rc;
1975 }
1976
1977 static int
1978 ldap_chain_db_close(
1979         BackendDB       *be,
1980         ConfigReply     *cr )
1981 {
1982 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1983 #ifdef SLAP_CONFIG_DELETE
1984         overlay_unregister_control( be, LDAP_CONTROL_X_CHAINING_BEHAVIOR );
1985 #endif /* SLAP_CONFIG_DELETE */
1986 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1987         return ldap_chain_db_func( be, db_close );
1988 }
1989
1990 static int
1991 ldap_chain_db_destroy(
1992         BackendDB       *be,
1993         ConfigReply     *cr )
1994 {
1995         slap_overinst   *on = (slap_overinst *) be->bd_info;
1996         ldap_chain_t    *lc = (ldap_chain_t *)on->on_bi.bi_private;
1997
1998         int             rc;
1999
2000         rc = ldap_chain_db_func( be, db_destroy );
2001
2002         if ( lc ) {
2003                 avl_free( lc->lc_lai.lai_tree, NULL );
2004                 ldap_pvt_thread_mutex_destroy( &lc->lc_lai.lai_mutex );
2005                 ch_free( lc );
2006         }
2007
2008         return rc;
2009 }
2010
2011 /*
2012  * inits one instance of the slapd-ldap backend, and stores
2013  * the private info in be_private of the arg
2014  */
2015 static int
2016 ldap_chain_db_init_common(
2017         BackendDB       *be )
2018 {
2019         BackendInfo     *bi = be->bd_info;
2020         ldapinfo_t      *li;
2021         int             rc;
2022
2023         be->bd_info = lback;
2024         be->be_private = NULL;
2025         rc = lback->bi_db_init( be, NULL );
2026         if ( rc != 0 ) {
2027                 return rc;
2028         }
2029         li = (ldapinfo_t *)be->be_private;
2030         li->li_urllist_f = NULL;
2031         li->li_urllist_p = NULL;
2032
2033         be->bd_info = bi;
2034
2035         return 0;
2036 }
2037
2038 /*
2039  * inits one instance of the slapd-ldap backend, stores
2040  * the private info in be_private of the arg and fills
2041  * selected fields with data from the template.
2042  *
2043  * NOTE: add checks about the other fields of the template,
2044  * which are ignored and SHOULD NOT be configured by the user.
2045  */
2046 static int
2047 ldap_chain_db_init_one(
2048         BackendDB       *be )
2049 {
2050         slap_overinst   *on = (slap_overinst *)be->bd_info;
2051         ldap_chain_t    *lc = (ldap_chain_t *)on->on_bi.bi_private;
2052
2053         BackendInfo     *bi = be->bd_info;
2054         ldapinfo_t      *li;
2055
2056         slap_op_t       t;
2057
2058         be->bd_info = lback;
2059         be->be_private = NULL;
2060         t = lback->bi_db_init( be, NULL );
2061         if ( t != 0 ) {
2062                 return t;
2063         }
2064         li = (ldapinfo_t *)be->be_private;
2065         li->li_urllist_f = NULL;
2066         li->li_urllist_p = NULL;
2067
2068         /* copy common data */
2069         li->li_nretries = lc->lc_common_li->li_nretries;
2070         li->li_flags = lc->lc_common_li->li_flags;
2071         li->li_version = lc->lc_common_li->li_version;
2072         for ( t = 0; t < SLAP_OP_LAST; t++ ) {
2073                 li->li_timeout[ t ] = lc->lc_common_li->li_timeout[ t ];
2074         }
2075         be->bd_info = bi;
2076
2077         return 0;
2078 }
2079
2080 static int
2081 ldap_chain_db_open_one(
2082         BackendDB       *be )
2083 {
2084         if ( SLAP_DBMONITORING( be ) ) {
2085                 ldapinfo_t      *li = (ldapinfo_t *)be->be_private;
2086
2087                 if ( li->li_uri == NULL ) {
2088                         ber_str2bv( "cn=Common Connections", 0, 1,
2089                                 &li->li_monitor_info.lmi_conn_rdn );
2090                         ber_str2bv( "cn=Operations on Common Connections", 0, 1,
2091                                 &li->li_monitor_info.lmi_conn_rdn );
2092
2093                 } else {
2094                         char            *ptr;
2095
2096                         li->li_monitor_info.lmi_conn_rdn.bv_len
2097                                 = STRLENOF( "cn=" ) + strlen( li->li_uri );
2098                         ptr = li->li_monitor_info.lmi_conn_rdn.bv_val
2099                                 = ch_malloc( li->li_monitor_info.lmi_conn_rdn.bv_len + 1 );
2100                         ptr = lutil_strcopy( ptr, "cn=" );
2101                         ptr = lutil_strcopy( ptr, li->li_uri );
2102                         ptr[ 0 ] = '\0';
2103
2104                         li->li_monitor_info.lmi_ops_rdn.bv_len
2105                                 = STRLENOF( "cn=Operations on " ) + strlen( li->li_uri );
2106                         ptr = li->li_monitor_info.lmi_ops_rdn.bv_val
2107                                 = ch_malloc( li->li_monitor_info.lmi_ops_rdn.bv_len + 1 );
2108                         ptr = lutil_strcopy( ptr, "cn=Operations on " );
2109                         ptr = lutil_strcopy( ptr, li->li_uri );
2110                         ptr[ 0 ] = '\0';
2111                 }
2112         }
2113
2114         return lback->bi_db_open( be, NULL );
2115 }
2116
2117 typedef struct ldap_chain_conn_apply_t {
2118         BackendDB       *be;
2119         Connection      *conn;
2120 } ldap_chain_conn_apply_t;
2121
2122 static int
2123 ldap_chain_conn_apply( void *datum, void *arg )
2124 {
2125         ldapinfo_t              *li = (ldapinfo_t *)datum;
2126         ldap_chain_conn_apply_t *lca = (ldap_chain_conn_apply_t *)arg;
2127
2128         lca->be->be_private = (void *)li;
2129
2130         return lback->bi_connection_destroy( lca->be, lca->conn );
2131 }
2132
2133 static int
2134 ldap_chain_connection_destroy(
2135         BackendDB *be,
2136         Connection *conn
2137 )
2138 {
2139         slap_overinst           *on = (slap_overinst *) be->bd_info;
2140         ldap_chain_t            *lc = (ldap_chain_t *)on->on_bi.bi_private;
2141         void                    *private = be->be_private;
2142         ldap_chain_conn_apply_t lca;
2143         int                     rc;
2144
2145         be->be_private = NULL;
2146         lca.be = be;
2147         lca.conn = conn;
2148         ldap_pvt_thread_mutex_lock( &lc->lc_lai.lai_mutex );
2149         rc = avl_apply( lc->lc_lai.lai_tree, ldap_chain_conn_apply,
2150                 (void *)&lca, 1, AVL_INORDER ) != AVL_NOMORE;
2151         ldap_pvt_thread_mutex_unlock( &lc->lc_lai.lai_mutex );
2152         be->be_private = private;
2153
2154         return rc;
2155 }
2156
2157 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
2158 static int
2159 ldap_chain_parse_ctrl(
2160         Operation       *op,
2161         SlapReply       *rs,
2162         LDAPControl     *ctrl )
2163 {
2164         ber_tag_t       tag;
2165         BerElement      *ber;
2166         ber_int_t       mode,
2167                         behavior;
2168
2169         if ( get_chaining( op ) != SLAP_CONTROL_NONE ) {
2170                 rs->sr_text = "Chaining behavior control specified multiple times";
2171                 return LDAP_PROTOCOL_ERROR;
2172         }
2173
2174         if ( op->o_pagedresults != SLAP_CONTROL_NONE ) {
2175                 rs->sr_text = "Chaining behavior control specified with pagedResults control";
2176                 return LDAP_PROTOCOL_ERROR;
2177         }
2178
2179         if ( BER_BVISEMPTY( &ctrl->ldctl_value ) ) {
2180                 mode = (SLAP_CH_RESOLVE_DEFAULT|SLAP_CH_CONTINUATION_DEFAULT);
2181
2182         } else {
2183                 ber_len_t       len;
2184
2185                 /* Parse the control value
2186                  *      ChainingBehavior ::= SEQUENCE { 
2187                  *           resolveBehavior         Behavior OPTIONAL, 
2188                  *           continuationBehavior    Behavior OPTIONAL } 
2189                  *                             
2190                  *      Behavior :: = ENUMERATED { 
2191                  *           chainingPreferred       (0), 
2192                  *           chainingRequired        (1), 
2193                  *           referralsPreferred      (2), 
2194                  *           referralsRequired       (3) } 
2195                  */
2196
2197                 ber = ber_init( &ctrl->ldctl_value );
2198                 if( ber == NULL ) {
2199                         rs->sr_text = "internal error";
2200                         return LDAP_OTHER;
2201                 }
2202
2203                 tag = ber_scanf( ber, "{e" /* } */, &behavior );
2204                 /* FIXME: since the whole SEQUENCE is optional,
2205                  * should we accept no enumerations at all? */
2206                 if ( tag != LBER_ENUMERATED ) {
2207                         rs->sr_text = "Chaining behavior control: resolveBehavior decoding error";
2208                         return LDAP_PROTOCOL_ERROR;
2209                 }
2210
2211                 switch ( behavior ) {
2212                 case LDAP_CHAINING_PREFERRED:
2213                         mode = SLAP_CH_RESOLVE_CHAINING_PREFERRED;
2214                         break;
2215
2216                 case LDAP_CHAINING_REQUIRED:
2217                         mode = SLAP_CH_RESOLVE_CHAINING_REQUIRED;
2218                         break;
2219
2220                 case LDAP_REFERRALS_PREFERRED:
2221                         mode = SLAP_CH_RESOLVE_REFERRALS_PREFERRED;
2222                         break;
2223
2224                 case LDAP_REFERRALS_REQUIRED:
2225                         mode = SLAP_CH_RESOLVE_REFERRALS_REQUIRED;
2226                         break;
2227
2228                 default:
2229                         rs->sr_text = "Chaining behavior control: unknown resolveBehavior";
2230                         return LDAP_PROTOCOL_ERROR;
2231                 }
2232
2233                 tag = ber_peek_tag( ber, &len );
2234                 if ( tag == LBER_ENUMERATED ) {
2235                         tag = ber_scanf( ber, "e", &behavior );
2236                         if ( tag == LBER_ERROR ) {
2237                                 rs->sr_text = "Chaining behavior control: continuationBehavior decoding error";
2238                                 return LDAP_PROTOCOL_ERROR;
2239                         }
2240                 }
2241
2242                 if ( tag == LBER_DEFAULT ) {
2243                         mode |= SLAP_CH_CONTINUATION_DEFAULT;
2244
2245                 } else {
2246                         switch ( behavior ) {
2247                         case LDAP_CHAINING_PREFERRED:
2248                                 mode |= SLAP_CH_CONTINUATION_CHAINING_PREFERRED;
2249                                 break;
2250
2251                         case LDAP_CHAINING_REQUIRED:
2252                                 mode |= SLAP_CH_CONTINUATION_CHAINING_REQUIRED;
2253                                 break;
2254
2255                         case LDAP_REFERRALS_PREFERRED:
2256                                 mode |= SLAP_CH_CONTINUATION_REFERRALS_PREFERRED;
2257                                 break;
2258
2259                         case LDAP_REFERRALS_REQUIRED:
2260                                 mode |= SLAP_CH_CONTINUATION_REFERRALS_REQUIRED;
2261                                 break;
2262
2263                         default:
2264                                 rs->sr_text = "Chaining behavior control: unknown continuationBehavior";
2265                                 return LDAP_PROTOCOL_ERROR;
2266                         }
2267                 }
2268
2269                 if ( ( ber_scanf( ber, /* { */ "}") ) == LBER_ERROR ) {
2270                         rs->sr_text = "Chaining behavior control: decoding error";
2271                         return LDAP_PROTOCOL_ERROR;
2272                 }
2273
2274                 (void) ber_free( ber, 1 );
2275         }
2276
2277         op->o_chaining = mode | ( ctrl->ldctl_iscritical
2278                         ? SLAP_CONTROL_CRITICAL
2279                         : SLAP_CONTROL_NONCRITICAL );
2280
2281         return LDAP_SUCCESS;
2282 }
2283 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
2284
2285 int
2286 chain_initialize( void )
2287 {
2288         int rc;
2289
2290         /* Make sure we don't exceed the bits reserved for userland */
2291         config_check_userland( CH_LAST );
2292
2293 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
2294         rc = register_supported_control( LDAP_CONTROL_X_CHAINING_BEHAVIOR,
2295                         /* SLAP_CTRL_GLOBAL| */ SLAP_CTRL_ACCESS|SLAP_CTRL_HIDE, NULL,
2296                         ldap_chain_parse_ctrl, &sc_chainingBehavior );
2297         if ( rc != LDAP_SUCCESS ) {
2298                 Debug( LDAP_DEBUG_ANY, "slapd-chain: "
2299                         "unable to register chaining behavior control: %d.\n",
2300                         rc, 0, 0 );
2301                 return rc;
2302         }
2303 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
2304
2305         ldapchain.on_bi.bi_type = "chain";
2306         ldapchain.on_bi.bi_db_init = ldap_chain_db_init;
2307         ldapchain.on_bi.bi_db_config = ldap_chain_db_config;
2308         ldapchain.on_bi.bi_db_open = ldap_chain_db_open;
2309         ldapchain.on_bi.bi_db_close = ldap_chain_db_close;
2310         ldapchain.on_bi.bi_db_destroy = ldap_chain_db_destroy;
2311
2312         ldapchain.on_bi.bi_connection_destroy = ldap_chain_connection_destroy;
2313
2314         ldapchain.on_response = ldap_chain_response;
2315
2316         ldapchain.on_bi.bi_cf_ocs = chainocs;
2317
2318         rc = config_register_schema( chaincfg, chainocs );
2319         if ( rc ) {
2320                 return rc;
2321         }
2322
2323         return overlay_register( &ldapchain );
2324 }
2325