]> git.sur5r.net Git - openldap/blob - servers/slapd/back-ldap/chain.c
2b98668d3f8eb2e433467a1c141cae2876c222cd
[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-2011 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
1225 static ConfigTable chaincfg[] = {
1226 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1227         { "chain-chaining", "args",
1228                 2, 4, 0, ARG_MAGIC|ARG_BERVAL|CH_CHAINING, chain_cf_gen,
1229                 "( OLcfgOvAt:3.1 NAME 'olcChainingBehavior' "
1230                         "DESC 'Chaining behavior control parameters (draft-sermersheim-ldap-chaining)' "
1231                         "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
1232 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1233         { "chain-cache-uri", "TRUE/FALSE",
1234                 2, 2, 0, ARG_MAGIC|ARG_ON_OFF|CH_CACHE_URI, chain_cf_gen,
1235                 "( OLcfgOvAt:3.2 NAME 'olcChainCacheURI' "
1236                         "DESC 'Enables caching of URIs not present in configuration' "
1237                         "SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
1238         { "chain-max-depth", "args",
1239                 2, 2, 0, ARG_MAGIC|ARG_INT|CH_MAX_DEPTH, chain_cf_gen,
1240                 "( OLcfgOvAt:3.3 NAME 'olcChainMaxReferralDepth' "
1241                         "DESC 'max referral depth' "
1242                         "SYNTAX OMsInteger "
1243                         "EQUALITY integerMatch "
1244                         "SINGLE-VALUE )", NULL, NULL },
1245         { "chain-return-error", "TRUE/FALSE",
1246                 2, 2, 0, ARG_MAGIC|ARG_ON_OFF|CH_RETURN_ERR, chain_cf_gen,
1247                 "( OLcfgOvAt:3.4 NAME 'olcChainReturnError' "
1248                         "DESC 'Errors are returned instead of the original referral' "
1249                         "SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
1250         { NULL, NULL, 0, 0, 0, ARG_IGNORED }
1251 };
1252
1253 static ConfigOCs chainocs[] = {
1254         { "( OLcfgOvOc:3.1 "
1255                 "NAME 'olcChainConfig' "
1256                 "DESC 'Chain configuration' "
1257                 "SUP olcOverlayConfig "
1258                 "MAY ( "
1259 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1260                         "olcChainingBehavior $ "
1261 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1262                         "olcChainCacheURI $ "
1263                         "olcChainMaxReferralDepth $ "
1264                         "olcChainReturnError "
1265                         ") )",
1266                 Cft_Overlay, chaincfg, NULL, chain_cfadd },
1267         { "( OLcfgOvOc:3.2 "
1268                 "NAME 'olcChainDatabase' "
1269                 "DESC 'Chain remote server configuration' "
1270                 "SUP olcLDAPConfig )",
1271                 Cft_Misc, olcDatabaseDummy, chain_ldadd },
1272         { NULL, 0, NULL }
1273 };
1274
1275 static int
1276 chain_ldadd( CfEntryInfo *p, Entry *e, ConfigArgs *ca )
1277 {
1278         slap_overinst           *on;
1279         ldap_chain_t            *lc;
1280
1281         ldapinfo_t              *li;
1282
1283         AttributeDescription    *ad = NULL;
1284         Attribute               *at;
1285         const char              *text;
1286
1287         int                     rc;
1288
1289         if ( p->ce_type != Cft_Overlay
1290                 || !p->ce_bi
1291                 || p->ce_bi->bi_cf_ocs != chainocs )
1292         {
1293                 return LDAP_CONSTRAINT_VIOLATION;
1294         }
1295
1296         on = (slap_overinst *)p->ce_bi;
1297         lc = (ldap_chain_t *)on->on_bi.bi_private;
1298
1299         assert( ca->be == NULL );
1300         ca->be = (BackendDB *)ch_calloc( 1, sizeof( BackendDB ) );
1301
1302         ca->be->bd_info = (BackendInfo *)on;
1303
1304         rc = slap_str2ad( "olcDbURI", &ad, &text );
1305         assert( rc == LDAP_SUCCESS );
1306
1307         at = attr_find( e->e_attrs, ad );
1308 #if 0
1309         if ( lc->lc_common_li == NULL && at != NULL ) {
1310                 /* FIXME: we should generate an empty default entry
1311                  * if none is supplied */
1312                 Debug( LDAP_DEBUG_ANY, "slapd-chain: "
1313                         "first underlying database \"%s\" "
1314                         "cannot contain attribute \"%s\".\n",
1315                         e->e_name.bv_val, ad->ad_cname.bv_val, 0 );
1316                 rc = LDAP_CONSTRAINT_VIOLATION;
1317                 goto done;
1318
1319         } else
1320 #endif
1321         if ( lc->lc_common_li != NULL && at == NULL ) {
1322                 /* FIXME: we should generate an empty default entry
1323                  * if none is supplied */
1324                 Debug( LDAP_DEBUG_ANY, "slapd-chain: "
1325                         "subsequent underlying database \"%s\" "
1326                         "must contain attribute \"%s\".\n",
1327                         e->e_name.bv_val, ad->ad_cname.bv_val, 0 );
1328                 rc = LDAP_CONSTRAINT_VIOLATION;
1329                 goto done;
1330         }
1331
1332         if ( lc->lc_common_li == NULL ) {
1333                 rc = ldap_chain_db_init_common( ca->be );
1334
1335         } else {
1336                 rc = ldap_chain_db_init_one( ca->be );
1337         }
1338
1339         if ( rc != 0 ) {
1340                 Debug( LDAP_DEBUG_ANY, "slapd-chain: "
1341                         "unable to init %sunderlying database \"%s\".\n",
1342                         lc->lc_common_li == NULL ? "common " : "", e->e_name.bv_val, 0 );
1343                 return LDAP_CONSTRAINT_VIOLATION;
1344         }
1345
1346         li = ca->be->be_private;
1347
1348         if ( lc->lc_common_li == NULL ) {
1349                 lc->lc_common_li = li;
1350
1351         } else {
1352                 li->li_uri = ch_strdup( at->a_vals[ 0 ].bv_val );
1353                 value_add_one( &li->li_bvuri, &at->a_vals[ 0 ] );
1354                 if ( avl_insert( &lc->lc_lai.lai_tree, (caddr_t)li,
1355                         ldap_chain_uri_cmp, ldap_chain_uri_dup ) )
1356                 {
1357                         Debug( LDAP_DEBUG_ANY, "slapd-chain: "
1358                                 "database \"%s\" insert failed.\n",
1359                                 e->e_name.bv_val, 0, 0 );
1360                         rc = LDAP_CONSTRAINT_VIOLATION;
1361                         goto done;
1362                 }
1363         }
1364
1365         ca->ca_private = on;
1366
1367 done:;
1368         if ( rc != LDAP_SUCCESS ) {
1369                 (void)ldap_chain_db_destroy_one( ca->be, NULL );
1370                 ch_free( ca->be );
1371                 ca->be = NULL;
1372         }
1373
1374         return rc;
1375 }
1376
1377 typedef struct ldap_chain_cfadd_apply_t {
1378         Operation       *op;
1379         SlapReply       *rs;
1380         Entry           *p;
1381         ConfigArgs      *ca;
1382         int             count;
1383 } ldap_chain_cfadd_apply_t;
1384
1385 static int
1386 ldap_chain_cfadd_apply( void *datum, void *arg )
1387 {
1388         ldapinfo_t                      *li = (ldapinfo_t *)datum;
1389         ldap_chain_cfadd_apply_t        *lca = (ldap_chain_cfadd_apply_t *)arg;
1390
1391         struct berval                   bv;
1392
1393         /* FIXME: should not hardcode "olcDatabase" here */
1394         bv.bv_len = snprintf( lca->ca->cr_msg, sizeof( lca->ca->cr_msg ),
1395                 "olcDatabase={%d}%s", lca->count, lback->bi_type );
1396         bv.bv_val = lca->ca->cr_msg;
1397
1398         lca->ca->be->be_private = (void *)li;
1399         config_build_entry( lca->op, lca->rs, lca->p->e_private, lca->ca,
1400                 &bv, lback->bi_cf_ocs, &chainocs[1] );
1401
1402         lca->count++;
1403
1404         return 0;
1405 }
1406
1407 static int
1408 chain_cfadd( Operation *op, SlapReply *rs, Entry *p, ConfigArgs *ca )
1409 {
1410         CfEntryInfo     *pe = p->e_private;
1411         slap_overinst   *on = (slap_overinst *)pe->ce_bi;
1412         ldap_chain_t    *lc = (ldap_chain_t *)on->on_bi.bi_private;
1413         void            *priv = (void *)ca->be->be_private;
1414
1415         if ( lback->bi_cf_ocs ) {
1416                 ldap_chain_cfadd_apply_t        lca = { 0 };
1417
1418                 lca.op = op;
1419                 lca.rs = rs;
1420                 lca.p = p;
1421                 lca.ca = ca;
1422                 lca.count = 0;
1423
1424                 (void)ldap_chain_cfadd_apply( (void *)lc->lc_common_li, (void *)&lca );
1425
1426                 (void)avl_apply( lc->lc_lai.lai_tree, ldap_chain_cfadd_apply,
1427                         &lca, 1, AVL_INORDER );
1428
1429                 ca->be->be_private = priv;
1430         }
1431
1432         return 0;
1433 }
1434
1435 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1436 static slap_verbmasks chaining_mode[] = {
1437         { BER_BVC("referralsRequired"),         LDAP_REFERRALS_REQUIRED },
1438         { BER_BVC("referralsPreferred"),        LDAP_REFERRALS_PREFERRED },
1439         { BER_BVC("chainingRequired"),          LDAP_CHAINING_REQUIRED },
1440         { BER_BVC("chainingPreferred"),         LDAP_CHAINING_PREFERRED },
1441         { BER_BVNULL,                           0 }
1442 };
1443 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1444
1445 static int
1446 chain_cf_gen( ConfigArgs *c )
1447 {
1448         slap_overinst   *on = (slap_overinst *)c->bi;
1449         ldap_chain_t    *lc = (ldap_chain_t *)on->on_bi.bi_private;
1450
1451         int             rc = 0;
1452
1453         if ( c->op == SLAP_CONFIG_EMIT ) {
1454                 switch( c->type ) {
1455 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1456                 case CH_CHAINING: {
1457                         struct berval   resolve = BER_BVNULL,
1458                                         continuation = BER_BVNULL;
1459
1460                         if ( !LDAP_CHAIN_CHAINING( lc ) ) {
1461                                 return 1;
1462                         }
1463
1464                         enum_to_verb( chaining_mode, ( ( lc->lc_chaining_ctrlflag & SLAP_CH_RESOLVE_MASK ) >> SLAP_CH_RESOLVE_SHIFT ), &resolve );
1465                         enum_to_verb( chaining_mode, ( ( lc->lc_chaining_ctrlflag & SLAP_CH_CONTINUATION_MASK ) >> SLAP_CH_CONTINUATION_SHIFT ), &continuation );
1466
1467                         c->value_bv.bv_len = STRLENOF( "resolve=" ) + resolve.bv_len
1468                                 + STRLENOF( " " )
1469                                 + STRLENOF( "continuation=" ) + continuation.bv_len;
1470                         c->value_bv.bv_val = ch_malloc( c->value_bv.bv_len + 1 );
1471                         snprintf( c->value_bv.bv_val, c->value_bv.bv_len + 1,
1472                                 "resolve=%s continuation=%s",
1473                                 resolve.bv_val, continuation.bv_val );
1474
1475                         if ( lc->lc_chaining_ctrl.ldctl_iscritical ) {
1476                                 c->value_bv.bv_val = ch_realloc( c->value_bv.bv_val,
1477                                         c->value_bv.bv_len + STRLENOF( " critical" ) + 1 );
1478                                 AC_MEMCPY( &c->value_bv.bv_val[ c->value_bv.bv_len ],
1479                                         " critical", STRLENOF( " critical" ) + 1 );
1480                                 c->value_bv.bv_len += STRLENOF( " critical" );
1481                         }
1482
1483                         break;
1484                 }
1485 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1486
1487                 case CH_CACHE_URI:
1488                         c->value_int = LDAP_CHAIN_CACHE_URI( lc );
1489                         break;
1490
1491                 case CH_MAX_DEPTH:
1492                         c->value_int = lc->lc_max_depth;
1493                         break;
1494
1495                 case CH_RETURN_ERR:
1496                         c->value_int = LDAP_CHAIN_RETURN_ERR( lc );
1497                         break;
1498
1499                 default:
1500                         assert( 0 );
1501                         rc = 1;
1502                 }
1503                 return rc;
1504
1505         } else if ( c->op == LDAP_MOD_DELETE ) {
1506                 switch( c->type ) {
1507                 case CH_CHAINING:
1508                         return 1;
1509
1510                 case CH_CACHE_URI:
1511                         lc->lc_flags &= ~LDAP_CHAIN_F_CACHE_URI;
1512                         break;
1513
1514                 case CH_MAX_DEPTH:
1515                         c->value_int = 0;
1516                         break;
1517
1518                 case CH_RETURN_ERR:
1519                         lc->lc_flags &= ~LDAP_CHAIN_F_RETURN_ERR;
1520                         break;
1521
1522                 default:
1523                         return 1;
1524                 }
1525                 return rc;
1526         }
1527
1528         switch( c->type ) {
1529         case CH_CHAINING: {
1530 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1531                 char                    **argv = c->argv;
1532                 int                     argc = c->argc;
1533                 BerElementBuffer        berbuf;
1534                 BerElement              *ber = (BerElement *)&berbuf;
1535                 int                     resolve = -1,
1536                                         continuation = -1,
1537                                         iscritical = 0;
1538                 Operation               op = { 0 };
1539                 SlapReply               rs = { 0 };
1540
1541                 lc->lc_chaining_ctrlflag = 0;
1542
1543                 for ( argc--, argv++; argc > 0; argc--, argv++ ) {
1544                         if ( strncasecmp( argv[ 0 ], "resolve=", STRLENOF( "resolve=" ) ) == 0 ) {
1545                                 resolve = str2chain( argv[ 0 ] + STRLENOF( "resolve=" ) );
1546                                 if ( resolve == -1 ) {
1547                                         Debug( LDAP_DEBUG_ANY, "%s: "
1548                                                 "illegal <resolve> value %s "
1549                                                 "in \"chain-chaining>\".\n",
1550                                                 c->log, argv[ 0 ], 0 );
1551                                         return 1;
1552                                 }
1553
1554                         } else if ( strncasecmp( argv[ 0 ], "continuation=", STRLENOF( "continuation=" ) ) == 0 ) {
1555                                 continuation = str2chain( argv[ 0 ] + STRLENOF( "continuation=" ) );
1556                                 if ( continuation == -1 ) {
1557                                         Debug( LDAP_DEBUG_ANY, "%s: "
1558                                                 "illegal <continuation> value %s "
1559                                                 "in \"chain-chaining\".\n",
1560                                                 c->log, argv[ 0 ], 0 );
1561                                         return 1;
1562                                 }
1563
1564                         } else if ( strcasecmp( argv[ 0 ], "critical" ) == 0 ) {
1565                                 iscritical = 1;
1566
1567                         } else {
1568                                 Debug( LDAP_DEBUG_ANY, "%s: "
1569                                         "unknown option in \"chain-chaining\".\n",
1570                                         c->log, 0, 0 );
1571                                 return 1;
1572                         }
1573                 }
1574
1575                 if ( resolve != -1 || continuation != -1 ) {
1576                         int     err;
1577
1578                         if ( resolve == -1 ) {
1579                                 /* default */
1580                                 resolve = SLAP_CHAINING_DEFAULT;
1581                         }
1582
1583                         ber_init2( ber, NULL, LBER_USE_DER );
1584
1585                         err = ber_printf( ber, "{e" /* } */, resolve );
1586                         if ( err == -1 ) {
1587                                 ber_free( ber, 1 );
1588                                 Debug( LDAP_DEBUG_ANY, "%s: "
1589                                         "chaining behavior control encoding error!\n",
1590                                         c->log, 0, 0 );
1591                                 return 1;
1592                         }
1593
1594                         if ( continuation > -1 ) {
1595                                 err = ber_printf( ber, "e", continuation );
1596                                 if ( err == -1 ) {
1597                                         ber_free( ber, 1 );
1598                                         Debug( LDAP_DEBUG_ANY, "%s: "
1599                                                 "chaining behavior control encoding error!\n",
1600                                                 c->log, 0, 0 );
1601                                         return 1;
1602                                 }
1603                         }
1604
1605                         err = ber_printf( ber, /* { */ "N}" );
1606                         if ( err == -1 ) {
1607                                 ber_free( ber, 1 );
1608                                 Debug( LDAP_DEBUG_ANY, "%s: "
1609                                         "chaining behavior control encoding error!\n",
1610                                         c->log, 0, 0 );
1611                                 return 1;
1612                         }
1613
1614                         if ( ber_flatten2( ber, &lc->lc_chaining_ctrl.ldctl_value, 0 ) == -1 ) {
1615                                 exit( EXIT_FAILURE );
1616                         }
1617
1618                 } else {
1619                         BER_BVZERO( &lc->lc_chaining_ctrl.ldctl_value );
1620                 }
1621
1622                 lc->lc_chaining_ctrl.ldctl_oid = LDAP_CONTROL_X_CHAINING_BEHAVIOR;
1623                 lc->lc_chaining_ctrl.ldctl_iscritical = iscritical;
1624
1625                 if ( ldap_chain_parse_ctrl( &op, &rs, &lc->lc_chaining_ctrl ) != LDAP_SUCCESS )
1626                 {
1627                         Debug( LDAP_DEBUG_ANY, "%s: "
1628                                 "unable to parse chaining control%s%s.\n",
1629                                 c->log, rs.sr_text ? ": " : "",
1630                                 rs.sr_text ? rs.sr_text : "" );
1631                         return 1;
1632                 }
1633
1634                 lc->lc_chaining_ctrlflag = op.o_chaining;
1635
1636                 lc->lc_flags |= LDAP_CHAIN_F_CHAINING;
1637
1638                 rc = 0;
1639 #else /* ! LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1640                 Debug( LDAP_DEBUG_ANY, "%s: "
1641                         "\"chaining\" control unsupported (ignored).\n",
1642                         c->log, 0, 0 );
1643 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1644                 } break;
1645
1646         case CH_CACHE_URI:
1647                 if ( c->value_int ) {
1648                         lc->lc_flags |= LDAP_CHAIN_F_CACHE_URI;
1649                 } else {
1650                         lc->lc_flags &= ~LDAP_CHAIN_F_CACHE_URI;
1651                 }
1652                 break;
1653
1654         case CH_MAX_DEPTH:
1655                 if ( c->value_int < 0 ) {
1656                         snprintf( c->cr_msg, sizeof( c->cr_msg ),
1657                                 "<%s> invalid max referral depth %d",
1658                                 c->argv[0], c->value_int );
1659                         Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1660                                 c->log, c->cr_msg, 0 );
1661                         rc = 1;
1662                         break;
1663                 }
1664                 lc->lc_max_depth = c->value_int;
1665
1666         case CH_RETURN_ERR:
1667                 if ( c->value_int ) {
1668                         lc->lc_flags |= LDAP_CHAIN_F_RETURN_ERR;
1669                 } else {
1670                         lc->lc_flags &= ~LDAP_CHAIN_F_RETURN_ERR;
1671                 }
1672                 break;
1673
1674         default:
1675                 assert( 0 );
1676                 return 1;
1677         }
1678         return rc;
1679 }
1680
1681 static int
1682 ldap_chain_db_init(
1683         BackendDB *be,
1684         ConfigReply *cr )
1685 {
1686         slap_overinst   *on = (slap_overinst *)be->bd_info;
1687         ldap_chain_t    *lc = NULL;
1688
1689         if ( lback == NULL ) {
1690                 lback = backend_info( "ldap" );
1691
1692                 if ( lback == NULL ) {
1693                         return 1;
1694                 }
1695         }
1696
1697         lc = ch_malloc( sizeof( ldap_chain_t ) );
1698         if ( lc == NULL ) {
1699                 return 1;
1700         }
1701         memset( lc, 0, sizeof( ldap_chain_t ) );
1702         lc->lc_max_depth = 1;
1703         ldap_pvt_thread_mutex_init( &lc->lc_lai.lai_mutex );
1704
1705         on->on_bi.bi_private = (void *)lc;
1706
1707         return 0;
1708 }
1709
1710 static int
1711 ldap_chain_db_config(
1712         BackendDB       *be,
1713         const char      *fname,
1714         int             lineno,
1715         int             argc,
1716         char            **argv )
1717 {
1718         slap_overinst   *on = (slap_overinst *)be->bd_info;
1719         ldap_chain_t    *lc = (ldap_chain_t *)on->on_bi.bi_private;
1720
1721         int             rc = SLAP_CONF_UNKNOWN;
1722
1723         if ( lc->lc_common_li == NULL ) {
1724                 BackendDB db = *be;
1725                 ldap_chain_db_init_common( &db );
1726                 lc->lc_common_li = lc->lc_cfg_li = (ldapinfo_t *)db.be_private;
1727         }
1728
1729         /* Something for the chain database? */
1730         if ( strncasecmp( argv[ 0 ], "chain-", STRLENOF( "chain-" ) ) == 0 ) {
1731                 char            *save_argv0 = argv[ 0 ];
1732                 BackendDB       db = *be;
1733                 static char     *allowed_argv[] = {
1734                         /* special: put URI here, so in the meanwhile
1735                          * it detects whether a new URI is being provided */
1736                         "uri",
1737                         "nretries",
1738                         "timeout",
1739                         /* flags */
1740                         "tls",
1741                         /* FIXME: maybe rebind-as-user should be allowed
1742                          * only within known URIs... */
1743                         "rebind-as-user",
1744                         "chase-referrals",
1745                         "t-f-support",
1746                         "proxy-whoami",
1747                         NULL
1748                 };
1749                 int             which_argv = -1;
1750
1751                 argv[ 0 ] += STRLENOF( "chain-" );
1752
1753                 for ( which_argv = 0; allowed_argv[ which_argv ]; which_argv++ ) {
1754                         if ( strcasecmp( argv[ 0 ], allowed_argv[ which_argv ] ) == 0 ) {
1755                                 break;
1756                         }
1757                 }
1758
1759                 if ( allowed_argv[ which_argv ] == NULL ) {
1760                         which_argv = -1;
1761
1762                         if ( lc->lc_cfg_li == lc->lc_common_li ) {
1763                                 Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1764                                         "\"%s\" only allowed within a URI directive.\n.",
1765                                         fname, lineno, argv[ 0 ] );
1766                                 return 1;
1767                         }
1768                 }
1769
1770                 if ( which_argv == 0 ) {
1771                         rc = ldap_chain_db_init_one( &db );
1772                         if ( rc != 0 ) {
1773                                 Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1774                                         "underlying slapd-ldap initialization failed.\n.",
1775                                         fname, lineno, 0 );
1776                                 return 1;
1777                         }
1778                         lc->lc_cfg_li = db.be_private;
1779                 }
1780
1781                 /* TODO: add checks on what other slapd-ldap(5) args
1782                  * should be put in the template; this is not quite
1783                  * harmful, because attributes that shouldn't don't
1784                  * get actually used, but the user should at least
1785                  * be warned.
1786                  */
1787
1788                 db.bd_info = lback;
1789                 db.be_private = (void *)lc->lc_cfg_li;
1790                 db.be_cf_ocs = lback->bi_cf_ocs;
1791
1792                 rc = config_generic_wrapper( &db, fname, lineno, argc, argv );
1793
1794                 argv[ 0 ] = save_argv0;
1795
1796                 if ( which_argv == 0 ) {
1797 private_destroy:;
1798                         if ( rc != 0 ) {
1799                                 db.bd_info = lback;
1800                                 db.be_private = (void *)lc->lc_cfg_li;
1801                                 ldap_chain_db_destroy_one( &db, NULL );
1802                                 lc->lc_cfg_li = NULL;
1803                         } else {
1804                                 if ( lc->lc_cfg_li->li_bvuri == NULL
1805                                         || BER_BVISNULL( &lc->lc_cfg_li->li_bvuri[ 0 ] )
1806                                         || !BER_BVISNULL( &lc->lc_cfg_li->li_bvuri[ 1 ] ) )
1807                                 {
1808                                         Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1809                                                 "no URI list allowed in slapo-chain.\n",
1810                                                 fname, lineno, 0 );
1811                                         rc = 1;
1812                                         goto private_destroy;
1813                                 }
1814
1815                                 if ( avl_insert( &lc->lc_lai.lai_tree,
1816                                         (caddr_t)lc->lc_cfg_li,
1817                                         ldap_chain_uri_cmp, ldap_chain_uri_dup ) )
1818                                 {
1819                                         Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1820                                                 "duplicate URI in slapo-chain.\n",
1821                                                 fname, lineno, 0 );
1822                                         rc = 1;
1823                                         goto private_destroy;
1824                                 }
1825                         }
1826                 }
1827         }
1828
1829         return rc;
1830 }
1831
1832 enum db_which {
1833         db_open = 0,
1834         db_close,
1835         db_destroy,
1836
1837         db_last
1838 };
1839
1840 typedef struct ldap_chain_db_apply_t {
1841         BackendDB       *be;
1842         BI_db_func      *func;
1843 } ldap_chain_db_apply_t;
1844
1845 static int
1846 ldap_chain_db_apply( void *datum, void *arg )
1847 {
1848         ldapinfo_t              *li = (ldapinfo_t *)datum;
1849         ldap_chain_db_apply_t   *lca = (ldap_chain_db_apply_t *)arg;
1850
1851         lca->be->be_private = (void *)li;
1852
1853         return lca->func( lca->be, NULL );
1854 }
1855
1856 static int
1857 ldap_chain_db_func(
1858         BackendDB *be,
1859         enum db_which which
1860 )
1861 {
1862         slap_overinst   *on = (slap_overinst *)be->bd_info;
1863         ldap_chain_t    *lc = (ldap_chain_t *)on->on_bi.bi_private;
1864
1865         int             rc = 0;
1866
1867         if ( lc ) {
1868                 BI_db_func      *func = (&lback->bi_db_open)[ which ];
1869
1870                 if ( func != NULL && lc->lc_common_li != NULL ) {
1871                         BackendDB               db = *be;
1872
1873                         db.bd_info = lback;
1874                         db.be_private = lc->lc_common_li;
1875
1876                         rc = func( &db, NULL );
1877
1878                         if ( rc != 0 ) {
1879                                 return rc;
1880                         }
1881
1882                         if ( lc->lc_lai.lai_tree != NULL ) {
1883                                 ldap_chain_db_apply_t   lca;
1884
1885                                 lca.be = &db;
1886                                 lca.func = func;
1887
1888                                 rc = avl_apply( lc->lc_lai.lai_tree,
1889                                         ldap_chain_db_apply, (void *)&lca,
1890                                         1, AVL_INORDER ) != AVL_NOMORE;
1891                         }
1892                 }
1893         }
1894
1895         return rc;
1896 }
1897
1898 static int
1899 ldap_chain_db_open(
1900         BackendDB       *be,
1901         ConfigReply     *cr )
1902 {
1903         slap_overinst   *on = (slap_overinst *) be->bd_info;
1904         ldap_chain_t    *lc = (ldap_chain_t *)on->on_bi.bi_private;
1905         slap_mask_t     monitoring;
1906         int             rc = 0;
1907
1908 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1909         rc = overlay_register_control( be, LDAP_CONTROL_X_CHAINING_BEHAVIOR );
1910         if ( rc != 0 ) {
1911                 return rc;
1912         }
1913 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1914
1915         if ( lc->lc_common_li == NULL ) {
1916                 void    *be_private = be->be_private;
1917                 ldap_chain_db_init_common( be );
1918                 lc->lc_common_li = lc->lc_cfg_li = (ldapinfo_t *)be->be_private;
1919                 be->be_private = be_private;
1920         }
1921
1922         /* filter out and restore monitoring */
1923         monitoring = ( SLAP_DBFLAGS( be ) & SLAP_DBFLAG_MONITORING );
1924         SLAP_DBFLAGS( be ) &= ~SLAP_DBFLAG_MONITORING;
1925         rc = ldap_chain_db_func( be, db_open );
1926         SLAP_DBFLAGS( be ) |= monitoring;
1927
1928         return rc;
1929 }
1930
1931 static int
1932 ldap_chain_db_close(
1933         BackendDB       *be,
1934         ConfigReply     *cr )
1935 {
1936 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1937 #ifdef SLAP_CONFIG_DELETE
1938         overlay_unregister_control( be, LDAP_CONTROL_X_CHAINING_BEHAVIOR );
1939 #endif /* SLAP_CONFIG_DELETE */
1940 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1941         return ldap_chain_db_func( be, db_close );
1942 }
1943
1944 static int
1945 ldap_chain_db_destroy(
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
1952         int             rc;
1953
1954         rc = ldap_chain_db_func( be, db_destroy );
1955
1956         if ( lc ) {
1957                 avl_free( lc->lc_lai.lai_tree, NULL );
1958                 ldap_pvt_thread_mutex_destroy( &lc->lc_lai.lai_mutex );
1959                 ch_free( lc );
1960         }
1961
1962         return rc;
1963 }
1964
1965 /*
1966  * inits one instance of the slapd-ldap backend, and stores
1967  * the private info in be_private of the arg
1968  */
1969 static int
1970 ldap_chain_db_init_common(
1971         BackendDB       *be )
1972 {
1973         BackendInfo     *bi = be->bd_info;
1974         ldapinfo_t      *li;
1975         int             rc;
1976
1977         be->bd_info = lback;
1978         be->be_private = NULL;
1979         rc = lback->bi_db_init( be, NULL );
1980         if ( rc != 0 ) {
1981                 return rc;
1982         }
1983         li = (ldapinfo_t *)be->be_private;
1984         li->li_urllist_f = NULL;
1985         li->li_urllist_p = NULL;
1986
1987         be->bd_info = bi;
1988
1989         return 0;
1990 }
1991
1992 /*
1993  * inits one instance of the slapd-ldap backend, stores
1994  * the private info in be_private of the arg and fills
1995  * selected fields with data from the template.
1996  *
1997  * NOTE: add checks about the other fields of the template,
1998  * which are ignored and SHOULD NOT be configured by the user.
1999  */
2000 static int
2001 ldap_chain_db_init_one(
2002         BackendDB       *be )
2003 {
2004         slap_overinst   *on = (slap_overinst *)be->bd_info;
2005         ldap_chain_t    *lc = (ldap_chain_t *)on->on_bi.bi_private;
2006
2007         BackendInfo     *bi = be->bd_info;
2008         ldapinfo_t      *li;
2009
2010         slap_op_t       t;
2011
2012         be->bd_info = lback;
2013         be->be_private = NULL;
2014         t = lback->bi_db_init( be, NULL );
2015         if ( t != 0 ) {
2016                 return t;
2017         }
2018         li = (ldapinfo_t *)be->be_private;
2019         li->li_urllist_f = NULL;
2020         li->li_urllist_p = NULL;
2021
2022         /* copy common data */
2023         li->li_nretries = lc->lc_common_li->li_nretries;
2024         li->li_flags = lc->lc_common_li->li_flags;
2025         li->li_version = lc->lc_common_li->li_version;
2026         for ( t = 0; t < SLAP_OP_LAST; t++ ) {
2027                 li->li_timeout[ t ] = lc->lc_common_li->li_timeout[ t ];
2028         }
2029         be->bd_info = bi;
2030
2031         return 0;
2032 }
2033
2034 static int
2035 ldap_chain_db_open_one(
2036         BackendDB       *be )
2037 {
2038         if ( SLAP_DBMONITORING( be ) ) {
2039                 ldapinfo_t      *li = (ldapinfo_t *)be->be_private;
2040
2041                 if ( li->li_uri == NULL ) {
2042                         ber_str2bv( "cn=Common Connections", 0, 1,
2043                                 &li->li_monitor_info.lmi_rdn );
2044
2045                 } else {
2046                         char            *ptr;
2047
2048                         li->li_monitor_info.lmi_rdn.bv_len
2049                                 = STRLENOF( "cn=" ) + strlen( li->li_uri );
2050                         ptr = li->li_monitor_info.lmi_rdn.bv_val
2051                                 = ch_malloc( li->li_monitor_info.lmi_rdn.bv_len + 1 );
2052                         ptr = lutil_strcopy( ptr, "cn=" );
2053                         ptr = lutil_strcopy( ptr, li->li_uri );
2054                         ptr[ 0 ] = '\0';
2055                 }
2056         }
2057
2058         return lback->bi_db_open( be, NULL );
2059 }
2060
2061 typedef struct ldap_chain_conn_apply_t {
2062         BackendDB       *be;
2063         Connection      *conn;
2064 } ldap_chain_conn_apply_t;
2065
2066 static int
2067 ldap_chain_conn_apply( void *datum, void *arg )
2068 {
2069         ldapinfo_t              *li = (ldapinfo_t *)datum;
2070         ldap_chain_conn_apply_t *lca = (ldap_chain_conn_apply_t *)arg;
2071
2072         lca->be->be_private = (void *)li;
2073
2074         return lback->bi_connection_destroy( lca->be, lca->conn );
2075 }
2076
2077 static int
2078 ldap_chain_connection_destroy(
2079         BackendDB *be,
2080         Connection *conn
2081 )
2082 {
2083         slap_overinst           *on = (slap_overinst *) be->bd_info;
2084         ldap_chain_t            *lc = (ldap_chain_t *)on->on_bi.bi_private;
2085         void                    *private = be->be_private;
2086         ldap_chain_conn_apply_t lca;
2087         int                     rc;
2088
2089         be->be_private = NULL;
2090         lca.be = be;
2091         lca.conn = conn;
2092         ldap_pvt_thread_mutex_lock( &lc->lc_lai.lai_mutex );
2093         rc = avl_apply( lc->lc_lai.lai_tree, ldap_chain_conn_apply,
2094                 (void *)&lca, 1, AVL_INORDER ) != AVL_NOMORE;
2095         ldap_pvt_thread_mutex_unlock( &lc->lc_lai.lai_mutex );
2096         be->be_private = private;
2097
2098         return rc;
2099 }
2100
2101 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
2102 static int
2103 ldap_chain_parse_ctrl(
2104         Operation       *op,
2105         SlapReply       *rs,
2106         LDAPControl     *ctrl )
2107 {
2108         ber_tag_t       tag;
2109         BerElement      *ber;
2110         ber_int_t       mode,
2111                         behavior;
2112
2113         if ( get_chaining( op ) != SLAP_CONTROL_NONE ) {
2114                 rs->sr_text = "Chaining behavior control specified multiple times";
2115                 return LDAP_PROTOCOL_ERROR;
2116         }
2117
2118         if ( op->o_pagedresults != SLAP_CONTROL_NONE ) {
2119                 rs->sr_text = "Chaining behavior control specified with pagedResults control";
2120                 return LDAP_PROTOCOL_ERROR;
2121         }
2122
2123         if ( BER_BVISEMPTY( &ctrl->ldctl_value ) ) {
2124                 mode = (SLAP_CH_RESOLVE_DEFAULT|SLAP_CH_CONTINUATION_DEFAULT);
2125
2126         } else {
2127                 ber_len_t       len;
2128
2129                 /* Parse the control value
2130                  *      ChainingBehavior ::= SEQUENCE { 
2131                  *           resolveBehavior         Behavior OPTIONAL, 
2132                  *           continuationBehavior    Behavior OPTIONAL } 
2133                  *                             
2134                  *      Behavior :: = ENUMERATED { 
2135                  *           chainingPreferred       (0), 
2136                  *           chainingRequired        (1), 
2137                  *           referralsPreferred      (2), 
2138                  *           referralsRequired       (3) } 
2139                  */
2140
2141                 ber = ber_init( &ctrl->ldctl_value );
2142                 if( ber == NULL ) {
2143                         rs->sr_text = "internal error";
2144                         return LDAP_OTHER;
2145                 }
2146
2147                 tag = ber_scanf( ber, "{e" /* } */, &behavior );
2148                 /* FIXME: since the whole SEQUENCE is optional,
2149                  * should we accept no enumerations at all? */
2150                 if ( tag != LBER_ENUMERATED ) {
2151                         rs->sr_text = "Chaining behavior control: resolveBehavior decoding error";
2152                         return LDAP_PROTOCOL_ERROR;
2153                 }
2154
2155                 switch ( behavior ) {
2156                 case LDAP_CHAINING_PREFERRED:
2157                         mode = SLAP_CH_RESOLVE_CHAINING_PREFERRED;
2158                         break;
2159
2160                 case LDAP_CHAINING_REQUIRED:
2161                         mode = SLAP_CH_RESOLVE_CHAINING_REQUIRED;
2162                         break;
2163
2164                 case LDAP_REFERRALS_PREFERRED:
2165                         mode = SLAP_CH_RESOLVE_REFERRALS_PREFERRED;
2166                         break;
2167
2168                 case LDAP_REFERRALS_REQUIRED:
2169                         mode = SLAP_CH_RESOLVE_REFERRALS_REQUIRED;
2170                         break;
2171
2172                 default:
2173                         rs->sr_text = "Chaining behavior control: unknown resolveBehavior";
2174                         return LDAP_PROTOCOL_ERROR;
2175                 }
2176
2177                 tag = ber_peek_tag( ber, &len );
2178                 if ( tag == LBER_ENUMERATED ) {
2179                         tag = ber_scanf( ber, "e", &behavior );
2180                         if ( tag == LBER_ERROR ) {
2181                                 rs->sr_text = "Chaining behavior control: continuationBehavior decoding error";
2182                                 return LDAP_PROTOCOL_ERROR;
2183                         }
2184                 }
2185
2186                 if ( tag == LBER_DEFAULT ) {
2187                         mode |= SLAP_CH_CONTINUATION_DEFAULT;
2188
2189                 } else {
2190                         switch ( behavior ) {
2191                         case LDAP_CHAINING_PREFERRED:
2192                                 mode |= SLAP_CH_CONTINUATION_CHAINING_PREFERRED;
2193                                 break;
2194
2195                         case LDAP_CHAINING_REQUIRED:
2196                                 mode |= SLAP_CH_CONTINUATION_CHAINING_REQUIRED;
2197                                 break;
2198
2199                         case LDAP_REFERRALS_PREFERRED:
2200                                 mode |= SLAP_CH_CONTINUATION_REFERRALS_PREFERRED;
2201                                 break;
2202
2203                         case LDAP_REFERRALS_REQUIRED:
2204                                 mode |= SLAP_CH_CONTINUATION_REFERRALS_REQUIRED;
2205                                 break;
2206
2207                         default:
2208                                 rs->sr_text = "Chaining behavior control: unknown continuationBehavior";
2209                                 return LDAP_PROTOCOL_ERROR;
2210                         }
2211                 }
2212
2213                 if ( ( ber_scanf( ber, /* { */ "}") ) == LBER_ERROR ) {
2214                         rs->sr_text = "Chaining behavior control: decoding error";
2215                         return LDAP_PROTOCOL_ERROR;
2216                 }
2217
2218                 (void) ber_free( ber, 1 );
2219         }
2220
2221         op->o_chaining = mode | ( ctrl->ldctl_iscritical
2222                         ? SLAP_CONTROL_CRITICAL
2223                         : SLAP_CONTROL_NONCRITICAL );
2224
2225         return LDAP_SUCCESS;
2226 }
2227 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
2228
2229 int
2230 chain_initialize( void )
2231 {
2232         int rc;
2233
2234         /* Make sure we don't exceed the bits reserved for userland */
2235         config_check_userland( CH_LAST );
2236
2237 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
2238         rc = register_supported_control( LDAP_CONTROL_X_CHAINING_BEHAVIOR,
2239                         /* SLAP_CTRL_GLOBAL| */ SLAP_CTRL_ACCESS|SLAP_CTRL_HIDE, NULL,
2240                         ldap_chain_parse_ctrl, &sc_chainingBehavior );
2241         if ( rc != LDAP_SUCCESS ) {
2242                 Debug( LDAP_DEBUG_ANY, "slapd-chain: "
2243                         "unable to register chaining behavior control: %d.\n",
2244                         rc, 0, 0 );
2245                 return rc;
2246         }
2247 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
2248
2249         ldapchain.on_bi.bi_type = "chain";
2250         ldapchain.on_bi.bi_db_init = ldap_chain_db_init;
2251         ldapchain.on_bi.bi_db_config = ldap_chain_db_config;
2252         ldapchain.on_bi.bi_db_open = ldap_chain_db_open;
2253         ldapchain.on_bi.bi_db_close = ldap_chain_db_close;
2254         ldapchain.on_bi.bi_db_destroy = ldap_chain_db_destroy;
2255
2256         ldapchain.on_bi.bi_connection_destroy = ldap_chain_connection_destroy;
2257
2258         ldapchain.on_response = ldap_chain_response;
2259
2260         ldapchain.on_bi.bi_cf_ocs = chainocs;
2261
2262         rc = config_register_schema( chaincfg, chainocs );
2263         if ( rc ) {
2264                 return rc;
2265         }
2266
2267         return overlay_register( &ldapchain );
2268 }
2269