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