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