]> git.sur5r.net Git - openldap/blob - servers/slapd/back-ldap/chain.c
ebaee37604e019a89046836c93f910d667d487ad
[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, rs)       (lback)->bi_db_destroy( (be), (rs) )
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, NULL);
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, NULL );
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, NULL );
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                 if ( temporary ) {
730                         lip->li_uri = NULL;
731                         lip->li_bvuri = NULL;
732                         (void)ldap_chain_db_close_one( op->o_bd );
733                         (void)ldap_chain_db_destroy_one( op->o_bd, NULL );
734                 }
735                 
736 further_cleanup:;
737                 if ( !BER_BVISNULL( &pdn ) ) {
738                         op->o_tmpfree( pdn.bv_val, op->o_tmpmemctx );
739                 }
740                 op->o_req_dn = save_req_dn;
741
742                 if ( !BER_BVISNULL( &ndn ) ) {
743                         op->o_tmpfree( ndn.bv_val, op->o_tmpmemctx );
744                 }
745                 op->o_req_ndn = save_req_ndn;
746                 
747                 if ( rc == LDAP_SUCCESS && rs2.sr_err == LDAP_SUCCESS ) {
748                         *rs = rs2;
749                         break;
750                 }
751
752                 rc = rs2.sr_err;
753         }
754
755 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
756         (void)chaining_control_remove( op, &ctrls );
757 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
758
759         op->o_req_dn = odn;
760         op->o_req_ndn = ondn;
761         op->o_callback->sc_response = save_response;
762         rs->sr_type = REP_SEARCHREF;
763         rs->sr_entry = NULL;
764
765         if ( rc != LDAP_SUCCESS ) {
766                 /* couldn't chase any of the referrals */
767                 if ( first_rc != -1 ) {
768                         rc = first_rc;
769
770                 } else {
771                         rc = SLAP_CB_CONTINUE;
772                 }
773         }
774
775         return rc;
776 }
777
778 static int
779 ldap_chain_response( Operation *op, SlapReply *rs )
780 {
781         slap_overinst   *on = (slap_overinst *)op->o_bd->bd_info;
782         ldap_chain_t    *lc = (ldap_chain_t *)on->on_bi.bi_private;
783         BackendDB       db, *bd = op->o_bd;
784         ldap_chain_cb_t lb = { 0 };
785         slap_callback   *sc = op->o_callback,
786                         sc2 = { 0 };
787         int             rc = 0;
788         const char      *text = NULL;
789         const char      *matched;
790         BerVarray       ref;
791         struct berval   ndn = op->o_ndn;
792
793         int             sr_err = rs->sr_err;
794         slap_reply_t    sr_type = rs->sr_type;
795 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
796         slap_mask_t     chain_mask = 0;
797         ber_len_t       chain_shift = 0;
798 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
799
800         if ( rs->sr_err != LDAP_REFERRAL && rs->sr_type != REP_SEARCHREF ) {
801                 return SLAP_CB_CONTINUE;
802         }
803
804 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
805         if ( rs->sr_err == LDAP_REFERRAL && get_chaining( op ) > SLAP_CONTROL_IGNORED ) {
806                 switch ( get_resolveBehavior( op ) ) {
807                 case SLAP_CH_RESOLVE_REFERRALS_PREFERRED:
808                 case SLAP_CH_RESOLVE_REFERRALS_REQUIRED:
809                         return SLAP_CB_CONTINUE;
810
811                 default:
812                         chain_mask = SLAP_CH_RESOLVE_MASK;
813                         chain_shift = SLAP_CH_RESOLVE_SHIFT;
814                         break;
815                 }
816
817         } else if ( rs->sr_type == REP_SEARCHREF && get_chaining( op ) > SLAP_CONTROL_IGNORED ) {
818                 switch ( get_continuationBehavior( op ) ) {
819                 case SLAP_CH_CONTINUATION_REFERRALS_PREFERRED:
820                 case SLAP_CH_CONTINUATION_REFERRALS_REQUIRED:
821                         return SLAP_CB_CONTINUE;
822
823                 default:
824                         chain_mask = SLAP_CH_CONTINUATION_MASK;
825                         chain_shift = SLAP_CH_CONTINUATION_SHIFT;
826                         break;
827                 }
828         }
829 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
830
831         /*
832          * TODO: add checks on who/when chain operations; e.g.:
833          *   a) what identities are authorized
834          *   b) what request DN (e.g. only chain requests rooted at <DN>)
835          *   c) what referral URIs
836          *   d) what protocol scheme (e.g. only ldaps://)
837          *   e) what ssf
838          */
839
840         db = *op->o_bd;
841         SLAP_DBFLAGS( &db ) &= ~SLAP_DBFLAG_MONITORING;
842         op->o_bd = &db;
843
844         text = rs->sr_text;
845         rs->sr_text = NULL;
846         matched = rs->sr_matched;
847         rs->sr_matched = NULL;
848         ref = rs->sr_ref;
849         rs->sr_ref = NULL;
850
851         /* we need this to know if back-ldap returned any result */
852         lb.lb_lc = lc;
853         sc2.sc_private = &lb;
854         sc2.sc_response = ldap_chain_cb_response;
855         op->o_callback = &sc2;
856
857         /* Chaining can be performed by a privileged user on behalf
858          * of normal users, using the ProxyAuthz control, by exploiting
859          * the identity assertion feature of back-ldap; see idassert-*
860          * directives in slapd-ldap(5).
861          *
862          * FIXME: the idassert-authcDN is one, will it be fine regardless
863          * of the URI we obtain from the referral?
864          */
865
866         switch ( op->o_tag ) {
867         case LDAP_REQ_BIND: {
868                 struct berval   rndn = op->o_req_ndn;
869                 Connection      *conn = op->o_conn;
870
871                 /* FIXME: can we really get a referral for binds? */
872                 op->o_req_ndn = slap_empty_bv;
873                 op->o_conn = NULL;
874                 rc = ldap_chain_op( op, rs, lback->bi_op_bind, ref, 0 );
875                 op->o_req_ndn = rndn;
876                 op->o_conn = conn;
877                 }
878                 break;
879
880         case LDAP_REQ_ADD:
881                 rc = ldap_chain_op( op, rs, lback->bi_op_add, ref, 0 );
882                 break;
883
884         case LDAP_REQ_DELETE:
885                 rc = ldap_chain_op( op, rs, lback->bi_op_delete, ref, 0 );
886                 break;
887
888         case LDAP_REQ_MODRDN:
889                 rc = ldap_chain_op( op, rs, lback->bi_op_modrdn, ref, 0 );
890                 break;
891
892         case LDAP_REQ_MODIFY:
893                 rc = ldap_chain_op( op, rs, lback->bi_op_modify, ref, 0 );
894                 break;
895
896         case LDAP_REQ_COMPARE:
897                 rc = ldap_chain_op( op, rs, lback->bi_op_compare, ref, 0 );
898                 if ( rs->sr_err == LDAP_COMPARE_TRUE || rs->sr_err == LDAP_COMPARE_FALSE ) {
899                         rc = LDAP_SUCCESS;
900                 }
901                 break;
902
903         case LDAP_REQ_SEARCH:
904                 if ( rs->sr_type == REP_SEARCHREF ) {
905                         rc = ldap_chain_search( op, rs, ref, 0 );
906                         
907                 } else {
908                         /* we might get here before any database actually 
909                          * performed a search; in those cases, we need
910                          * to check limits, to make sure safe defaults
911                          * are in place */
912                         if ( op->ors_limit != NULL || limits_check( op, rs ) == 0 ) {
913                                 rc = ldap_chain_op( op, rs, lback->bi_op_search, ref, 0 );
914
915                         } else {
916                                 rc = SLAP_CB_CONTINUE;
917                         }
918                 }
919                 break;
920
921         case LDAP_REQ_EXTENDED:
922                 rc = ldap_chain_op( op, rs, lback->bi_extended, ref, 0 );
923                 /* FIXME: ldap_back_extended() by design 
924                  * doesn't send result; frontend is expected
925                  * to send it... */
926                 /* FIXME: what about chaining? */
927                 if ( rc != SLAPD_ABANDON ) {
928                         rs->sr_err = rc;
929                         send_ldap_extended( op, rs );
930                         rc = LDAP_SUCCESS;
931                 }
932                 lb.lb_status = LDAP_CH_RES;
933                 break;
934
935         default:
936                 rc = SLAP_CB_CONTINUE;
937                 break;
938         }
939
940         switch ( rc ) {
941         case SLAPD_ABANDON:
942                 goto dont_chain;
943
944         case LDAP_SUCCESS:
945         case LDAP_REFERRAL:
946                 /* slapd-ldap sent response */
947                 if ( !op->o_abandon && lb.lb_status != LDAP_CH_RES ) {
948                         /* FIXME: should we send response? */
949                         Debug( LDAP_DEBUG_ANY,
950                                 "%s: ldap_chain_response: "
951                                 "overlay should have sent result.\n",
952                                 op->o_log_prefix, 0, 0 );
953                 }
954                 break;
955
956         default:
957 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
958                 if ( lb.lb_status == LDAP_CH_ERR && rs->sr_err == LDAP_X_CANNOT_CHAIN ) {
959                         goto cannot_chain;
960                 }
961
962                 switch ( ( get_chainingBehavior( op ) & chain_mask ) >> chain_shift ) {
963                 case LDAP_CHAINING_REQUIRED:
964 cannot_chain:;
965                         op->o_callback = NULL;
966                         send_ldap_error( op, rs, LDAP_X_CANNOT_CHAIN,
967                                 "operation cannot be completed without chaining" );
968                         goto dont_chain;
969
970                 default:
971 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
972                         if ( LDAP_CHAIN_RETURN_ERR( lc ) ) {
973                                 rs->sr_err = rc;
974                                 rs->sr_type = sr_type;
975
976                         } else {
977                                 rc = SLAP_CB_CONTINUE;
978                                 rs->sr_err = sr_err;
979                                 rs->sr_type = sr_type;
980                                 rs->sr_text = text;
981                                 rs->sr_matched = matched;
982                                 rs->sr_ref = ref;
983                         }
984 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
985                         break;
986                 }
987 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
988         }
989
990         if ( lb.lb_status == LDAP_CH_NONE && rc != SLAPD_ABANDON ) {
991                 op->o_callback = NULL;
992                 rc = rs->sr_err = slap_map_api2result( rs );
993                 send_ldap_result( op, rs );
994         }
995
996 dont_chain:;
997         rs->sr_err = sr_err;
998         rs->sr_type = sr_type;
999         rs->sr_text = text;
1000         rs->sr_matched = matched;
1001         rs->sr_ref = ref;
1002         op->o_bd = bd;
1003         op->o_callback = sc;
1004         op->o_ndn = ndn;
1005
1006         return rc;
1007 }
1008
1009 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1010 static int
1011 ldap_chain_parse_ctrl(
1012         Operation       *op,
1013         SlapReply       *rs,
1014         LDAPControl     *ctrl );
1015
1016 static int
1017 str2chain( const char *s )
1018 {
1019         if ( strcasecmp( s, "chainingPreferred" ) == 0 ) {
1020                 return LDAP_CHAINING_PREFERRED;
1021                 
1022         } else if ( strcasecmp( s, "chainingRequired" ) == 0 ) {
1023                 return LDAP_CHAINING_REQUIRED;
1024
1025         } else if ( strcasecmp( s, "referralsPreferred" ) == 0 ) {
1026                 return LDAP_REFERRALS_PREFERRED;
1027                 
1028         } else if ( strcasecmp( s, "referralsRequired" ) == 0 ) {
1029                 return LDAP_REFERRALS_REQUIRED;
1030         }
1031
1032         return -1;
1033 }
1034 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1035
1036 /*
1037  * configuration...
1038  */
1039
1040 enum {
1041         CH_CHAINING = 1,
1042         CH_CACHE_URI,
1043         CH_MAX_DEPTH,
1044         CH_RETURN_ERR,
1045
1046         CH_LAST
1047 };
1048
1049 static ConfigDriver chain_cf_gen;
1050 static ConfigCfAdd chain_cfadd;
1051 static ConfigLDAPadd chain_ldadd;
1052
1053 static ConfigTable chaincfg[] = {
1054 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1055         { "chain-chaining", "args",
1056                 2, 4, 0, ARG_MAGIC|ARG_BERVAL|CH_CHAINING, chain_cf_gen,
1057                 "( OLcfgOvAt:3.1 NAME 'olcChainingBehavior' "
1058                         "DESC 'Chaining behavior control parameters (draft-sermersheim-ldap-chaining)' "
1059                         "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
1060 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1061         { "chain-cache-uri", "TRUE/FALSE",
1062                 2, 2, 0, ARG_MAGIC|ARG_ON_OFF|CH_CACHE_URI, chain_cf_gen,
1063                 "( OLcfgOvAt:3.2 NAME 'olcChainCacheURI' "
1064                         "DESC 'Enables caching of URIs not present in configuration' "
1065                         "SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
1066         { "chain-max-depth", "args",
1067                 2, 2, 0, ARG_MAGIC|ARG_INT|CH_MAX_DEPTH, chain_cf_gen,
1068                 "( OLcfgOvAt:3.3 NAME 'olcChainMaxReferralDepth' "
1069                         "DESC 'max referral depth' "
1070                         "SYNTAX OMsInteger "
1071                         "EQUALITY integerMatch "
1072                         "SINGLE-VALUE )", NULL, NULL },
1073         { "chain-return-error", "TRUE/FALSE",
1074                 2, 2, 0, ARG_MAGIC|ARG_ON_OFF|CH_RETURN_ERR, chain_cf_gen,
1075                 "( OLcfgOvAt:3.4 NAME 'olcChainReturnError' "
1076                         "DESC 'Errors are returned instead of the original referral' "
1077                         "SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
1078         { NULL, NULL, 0, 0, 0, ARG_IGNORED }
1079 };
1080
1081 static ConfigOCs chainocs[] = {
1082         { "( OLcfgOvOc:3.1 "
1083                 "NAME 'olcChainConfig' "
1084                 "DESC 'Chain configuration' "
1085                 "SUP olcOverlayConfig "
1086                 "MAY ( "
1087 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1088                         "olcChainingBehavior $ "
1089 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1090                         "olcChainCacheURI $ "
1091                         "olcChainMaxReferralDepth $ "
1092                         "olcChainReturnError "
1093                         ") )",
1094                 Cft_Overlay, chaincfg, NULL, chain_cfadd },
1095         { "( OLcfgOvOc:3.2 "
1096                 "NAME 'olcChainDatabase' "
1097                 "DESC 'Chain remote server configuration' "
1098                 "AUXILIARY )",
1099                 Cft_Misc, chaincfg, chain_ldadd },
1100         { NULL, 0, NULL }
1101 };
1102
1103 static int
1104 chain_ldadd( CfEntryInfo *p, Entry *e, ConfigArgs *ca )
1105 {
1106         slap_overinst           *on;
1107         ldap_chain_t            *lc;
1108
1109         ldapinfo_t              *li;
1110
1111         AttributeDescription    *ad = NULL;
1112         Attribute               *at;
1113         const char              *text;
1114
1115         int                     rc;
1116
1117         if ( p->ce_type != Cft_Overlay
1118                 || !p->ce_bi
1119                 || p->ce_bi->bi_cf_ocs != chainocs )
1120         {
1121                 return LDAP_CONSTRAINT_VIOLATION;
1122         }
1123
1124         on = (slap_overinst *)p->ce_bi;
1125         lc = (ldap_chain_t *)on->on_bi.bi_private;
1126
1127         assert( ca->be == NULL );
1128         ca->be = (BackendDB *)ch_calloc( 1, sizeof( BackendDB ) );
1129
1130         ca->be->bd_info = (BackendInfo *)on;
1131
1132         rc = slap_str2ad( "olcDbURI", &ad, &text );
1133         assert( rc == LDAP_SUCCESS );
1134
1135         at = attr_find( e->e_attrs, ad );
1136         if ( lc->lc_common_li == NULL && at != NULL ) {
1137                 /* FIXME: we should generate an empty default entry
1138                  * if none is supplied */
1139                 Debug( LDAP_DEBUG_ANY, "slapd-chain: "
1140                         "first underlying database \"%s\" "
1141                         "cannot contain attribute \"%s\".\n",
1142                         e->e_name.bv_val, ad->ad_cname.bv_val, 0 );
1143                 rc = LDAP_CONSTRAINT_VIOLATION;
1144                 goto done;
1145
1146         } else if ( lc->lc_common_li != NULL && at == NULL ) {
1147                 /* FIXME: we should generate an empty default entry
1148                  * if none is supplied */
1149                 Debug( LDAP_DEBUG_ANY, "slapd-chain: "
1150                         "subsequent underlying database \"%s\" "
1151                         "must contain attribute \"%s\".\n",
1152                         e->e_name.bv_val, ad->ad_cname.bv_val, 0 );
1153                 rc = LDAP_CONSTRAINT_VIOLATION;
1154                 goto done;
1155         }
1156
1157         if ( lc->lc_common_li == NULL ) {
1158                 rc = ldap_chain_db_init_common( ca->be );
1159
1160         } else {
1161                 rc = ldap_chain_db_init_one( ca->be );
1162         }
1163
1164         if ( rc != 0 ) {
1165                 Debug( LDAP_DEBUG_ANY, "slapd-chain: "
1166                         "unable to init %sunderlying database \"%s\".\n",
1167                         lc->lc_common_li == NULL ? "common " : "", e->e_name.bv_val, 0 );
1168                 return LDAP_CONSTRAINT_VIOLATION;
1169         }
1170
1171         li = ca->be->be_private;
1172
1173         if ( lc->lc_common_li == NULL ) {
1174                 lc->lc_common_li = li;
1175
1176         } else {
1177                 li->li_uri = ch_strdup( at->a_vals[ 0 ].bv_val );
1178                 value_add_one( &li->li_bvuri, &at->a_vals[ 0 ] );
1179                 if ( avl_insert( &lc->lc_lai.lai_tree, (caddr_t)li,
1180                         ldap_chain_uri_cmp, ldap_chain_uri_dup ) )
1181                 {
1182                         Debug( LDAP_DEBUG_ANY, "slapd-chain: "
1183                                 "database \"%s\" insert failed.\n",
1184                                 e->e_name.bv_val, 0, 0 );
1185                         rc = LDAP_CONSTRAINT_VIOLATION;
1186                         goto done;
1187                 }
1188         }
1189
1190 done:;
1191         if ( rc != LDAP_SUCCESS ) {
1192                 (void)ldap_chain_db_destroy_one( ca->be, NULL );
1193                 ch_free( ca->be );
1194                 ca->be = NULL;
1195         }
1196
1197         return rc;
1198 }
1199
1200 typedef struct ldap_chain_cfadd_apply_t {
1201         Operation       *op;
1202         SlapReply       *rs;
1203         Entry           *p;
1204         ConfigArgs      *ca;
1205         int             count;
1206 } ldap_chain_cfadd_apply_t;
1207
1208 static int
1209 ldap_chain_cfadd_apply( void *datum, void *arg )
1210 {
1211         ldapinfo_t                      *li = (ldapinfo_t *)datum;
1212         ldap_chain_cfadd_apply_t        *lca = (ldap_chain_cfadd_apply_t *)arg;
1213
1214         struct berval                   bv;
1215
1216         /* FIXME: should not hardcode "olcDatabase" here */
1217         bv.bv_len = snprintf( lca->ca->cr_msg, sizeof( lca->ca->cr_msg ),
1218                 "olcDatabase={%d}%s", lca->count, lback->bi_type );
1219         bv.bv_val = lca->ca->cr_msg;
1220
1221         lca->ca->be->be_private = (void *)li;
1222         config_build_entry( lca->op, lca->rs, lca->p->e_private, lca->ca,
1223                 &bv, lback->bi_cf_ocs, &chainocs[1] );
1224
1225         lca->count++;
1226
1227         return 0;
1228 }
1229
1230 static int
1231 chain_cfadd( Operation *op, SlapReply *rs, Entry *p, ConfigArgs *ca )
1232 {
1233         CfEntryInfo     *pe = p->e_private;
1234         slap_overinst   *on = (slap_overinst *)pe->ce_bi;
1235         ldap_chain_t    *lc = (ldap_chain_t *)on->on_bi.bi_private;
1236         void            *priv = (void *)ca->be->be_private;
1237
1238         if ( lback->bi_cf_ocs ) {
1239                 ldap_chain_cfadd_apply_t        lca = { 0 };
1240
1241                 lca.op = op;
1242                 lca.rs = rs;
1243                 lca.p = p;
1244                 lca.ca = ca;
1245                 lca.count = 0;
1246
1247                 (void)ldap_chain_cfadd_apply( (void *)lc->lc_common_li, (void *)&lca );
1248
1249                 (void)avl_apply( lc->lc_lai.lai_tree, ldap_chain_cfadd_apply,
1250                         &lca, 1, AVL_INORDER );
1251
1252                 ca->be->be_private = priv;
1253         }
1254
1255         return 0;
1256 }
1257
1258 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1259 static slap_verbmasks chaining_mode[] = {
1260         { BER_BVC("referralsRequired"),         LDAP_REFERRALS_REQUIRED },
1261         { BER_BVC("referralsPreferred"),        LDAP_REFERRALS_PREFERRED },
1262         { BER_BVC("chainingRequired"),          LDAP_CHAINING_REQUIRED },
1263         { BER_BVC("chainingPreferred"),         LDAP_CHAINING_PREFERRED },
1264         { BER_BVNULL,                           0 }
1265 };
1266 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1267
1268 static int
1269 chain_cf_gen( ConfigArgs *c )
1270 {
1271         slap_overinst   *on = (slap_overinst *)c->bi;
1272         ldap_chain_t    *lc = (ldap_chain_t *)on->on_bi.bi_private;
1273
1274         int             rc = 0;
1275
1276         if ( c->op == SLAP_CONFIG_EMIT ) {
1277                 switch( c->type ) {
1278 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1279                 case CH_CHAINING: {
1280                         struct berval   resolve = BER_BVNULL,
1281                                         continuation = BER_BVNULL;
1282
1283                         if ( !LDAP_CHAIN_CHAINING( lc ) ) {
1284                                 return 1;
1285                         }
1286
1287                         enum_to_verb( chaining_mode, ( ( lc->lc_chaining_ctrlflag & SLAP_CH_RESOLVE_MASK ) >> SLAP_CH_RESOLVE_SHIFT ), &resolve );
1288                         enum_to_verb( chaining_mode, ( ( lc->lc_chaining_ctrlflag & SLAP_CH_CONTINUATION_MASK ) >> SLAP_CH_CONTINUATION_SHIFT ), &continuation );
1289
1290                         c->value_bv.bv_len = STRLENOF( "resolve=" ) + resolve.bv_len
1291                                 + STRLENOF( " " )
1292                                 + STRLENOF( "continuation=" ) + continuation.bv_len;
1293                         c->value_bv.bv_val = ch_malloc( c->value_bv.bv_len + 1 );
1294                         snprintf( c->value_bv.bv_val, c->value_bv.bv_len + 1,
1295                                 "resolve=%s continuation=%s",
1296                                 resolve.bv_val, continuation.bv_val );
1297
1298                         if ( lc->lc_chaining_ctrl.ldctl_iscritical ) {
1299                                 c->value_bv.bv_val = ch_realloc( c->value_bv.bv_val,
1300                                         c->value_bv.bv_len + STRLENOF( " critical" ) + 1 );
1301                                 AC_MEMCPY( &c->value_bv.bv_val[ c->value_bv.bv_len ],
1302                                         " critical", STRLENOF( " critical" ) + 1 );
1303                                 c->value_bv.bv_len += STRLENOF( " critical" );
1304                         }
1305
1306                         break;
1307                 }
1308 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1309
1310                 case CH_CACHE_URI:
1311                         c->value_int = LDAP_CHAIN_CACHE_URI( lc );
1312                         break;
1313
1314                 case CH_MAX_DEPTH:
1315                         c->value_int = lc->lc_max_depth;
1316                         break;
1317
1318                 case CH_RETURN_ERR:
1319                         c->value_int = LDAP_CHAIN_RETURN_ERR( lc );
1320                         break;
1321
1322                 default:
1323                         assert( 0 );
1324                         rc = 1;
1325                 }
1326                 return rc;
1327
1328         } else if ( c->op == LDAP_MOD_DELETE ) {
1329                 switch( c->type ) {
1330                 case CH_CHAINING:
1331                         return 1;
1332
1333                 case CH_CACHE_URI:
1334                         lc->lc_flags &= ~LDAP_CHAIN_F_CACHE_URI;
1335                         break;
1336
1337                 case CH_MAX_DEPTH:
1338                         c->value_int = 0;
1339                         break;
1340
1341                 case CH_RETURN_ERR:
1342                         lc->lc_flags &= ~LDAP_CHAIN_F_RETURN_ERR;
1343                         break;
1344
1345                 default:
1346                         return 1;
1347                 }
1348                 return rc;
1349         }
1350
1351         switch( c->type ) {
1352         case CH_CHAINING: {
1353 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1354                 char                    **argv = c->argv;
1355                 int                     argc = c->argc;
1356                 BerElementBuffer        berbuf;
1357                 BerElement              *ber = (BerElement *)&berbuf;
1358                 int                     resolve = -1,
1359                                         continuation = -1,
1360                                         iscritical = 0;
1361                 Operation               op = { 0 };
1362                 SlapReply               rs = { 0 };
1363
1364                 lc->lc_chaining_ctrlflag = 0;
1365
1366                 for ( argc--, argv++; argc > 0; argc--, argv++ ) {
1367                         if ( strncasecmp( argv[ 0 ], "resolve=", STRLENOF( "resolve=" ) ) == 0 ) {
1368                                 resolve = str2chain( argv[ 0 ] + STRLENOF( "resolve=" ) );
1369                                 if ( resolve == -1 ) {
1370                                         Debug( LDAP_DEBUG_ANY, "%s: "
1371                                                 "illegal <resolve> value %s "
1372                                                 "in \"chain-chaining>\".\n",
1373                                                 c->log, argv[ 0 ], 0 );
1374                                         return 1;
1375                                 }
1376
1377                         } else if ( strncasecmp( argv[ 0 ], "continuation=", STRLENOF( "continuation=" ) ) == 0 ) {
1378                                 continuation = str2chain( argv[ 0 ] + STRLENOF( "continuation=" ) );
1379                                 if ( continuation == -1 ) {
1380                                         Debug( LDAP_DEBUG_ANY, "%s: "
1381                                                 "illegal <continuation> value %s "
1382                                                 "in \"chain-chaining\".\n",
1383                                                 c->log, argv[ 0 ], 0 );
1384                                         return 1;
1385                                 }
1386
1387                         } else if ( strcasecmp( argv[ 0 ], "critical" ) == 0 ) {
1388                                 iscritical = 1;
1389
1390                         } else {
1391                                 Debug( LDAP_DEBUG_ANY, "%s: "
1392                                         "unknown option in \"chain-chaining\".\n",
1393                                         c->log, 0, 0 );
1394                                 return 1;
1395                         }
1396                 }
1397
1398                 if ( resolve != -1 || continuation != -1 ) {
1399                         int     err;
1400
1401                         if ( resolve == -1 ) {
1402                                 /* default */
1403                                 resolve = SLAP_CHAINING_DEFAULT;
1404                         }
1405
1406                         ber_init2( ber, NULL, LBER_USE_DER );
1407
1408                         err = ber_printf( ber, "{e" /* } */, resolve );
1409                         if ( err == -1 ) {
1410                                 ber_free( ber, 1 );
1411                                 Debug( LDAP_DEBUG_ANY, "%s: "
1412                                         "chaining behavior control encoding error!\n",
1413                                         c->log, 0, 0 );
1414                                 return 1;
1415                         }
1416
1417                         if ( continuation > -1 ) {
1418                                 err = ber_printf( ber, "e", continuation );
1419                                 if ( err == -1 ) {
1420                                         ber_free( ber, 1 );
1421                                         Debug( LDAP_DEBUG_ANY, "%s: "
1422                                                 "chaining behavior control encoding error!\n",
1423                                                 c->log, 0, 0 );
1424                                         return 1;
1425                                 }
1426                         }
1427
1428                         err = ber_printf( ber, /* { */ "N}" );
1429                         if ( err == -1 ) {
1430                                 ber_free( ber, 1 );
1431                                 Debug( LDAP_DEBUG_ANY, "%s: "
1432                                         "chaining behavior control encoding error!\n",
1433                                         c->log, 0, 0 );
1434                                 return 1;
1435                         }
1436
1437                         if ( ber_flatten2( ber, &lc->lc_chaining_ctrl.ldctl_value, 0 ) == -1 ) {
1438                                 exit( EXIT_FAILURE );
1439                         }
1440
1441                 } else {
1442                         BER_BVZERO( &lc->lc_chaining_ctrl.ldctl_value );
1443                 }
1444
1445                 lc->lc_chaining_ctrl.ldctl_oid = LDAP_CONTROL_X_CHAINING_BEHAVIOR;
1446                 lc->lc_chaining_ctrl.ldctl_iscritical = iscritical;
1447
1448                 if ( ldap_chain_parse_ctrl( &op, &rs, &lc->lc_chaining_ctrl ) != LDAP_SUCCESS )
1449                 {
1450                         Debug( LDAP_DEBUG_ANY, "%s: "
1451                                 "unable to parse chaining control%s%s.\n",
1452                                 c->log, rs.sr_text ? ": " : "",
1453                                 rs.sr_text ? rs.sr_text : "" );
1454                         return 1;
1455                 }
1456
1457                 lc->lc_chaining_ctrlflag = op.o_chaining;
1458
1459                 lc->lc_flags |= LDAP_CHAIN_F_CHAINING;
1460
1461                 rc = 0;
1462 #else /* ! LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1463                 Debug( LDAP_DEBUG_ANY, "%s: "
1464                         "\"chaining\" control unsupported (ignored).\n",
1465                         c->log, 0, 0 );
1466 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1467                 } break;
1468
1469         case CH_CACHE_URI:
1470                 if ( c->value_int ) {
1471                         lc->lc_flags |= LDAP_CHAIN_F_CACHE_URI;
1472                 } else {
1473                         lc->lc_flags &= ~LDAP_CHAIN_F_CACHE_URI;
1474                 }
1475                 break;
1476
1477         case CH_MAX_DEPTH:
1478                 if ( c->value_int < 0 ) {
1479                         snprintf( c->cr_msg, sizeof( c->cr_msg ),
1480                                 "<%s> invalid max referral depth %d",
1481                                 c->argv[0], c->value_int );
1482                         Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1483                                 c->log, c->cr_msg, 0 );
1484                         rc = 1;
1485                         break;
1486                 }
1487                 lc->lc_max_depth = c->value_int;
1488
1489         case CH_RETURN_ERR:
1490                 if ( c->value_int ) {
1491                         lc->lc_flags |= LDAP_CHAIN_F_RETURN_ERR;
1492                 } else {
1493                         lc->lc_flags &= ~LDAP_CHAIN_F_RETURN_ERR;
1494                 }
1495                 break;
1496
1497         default:
1498                 assert( 0 );
1499                 return 1;
1500         }
1501         return rc;
1502 }
1503
1504 static int
1505 ldap_chain_db_init(
1506         BackendDB *be,
1507         ConfigReply *cr )
1508 {
1509         slap_overinst   *on = (slap_overinst *)be->bd_info;
1510         ldap_chain_t    *lc = NULL;
1511
1512         if ( lback == NULL ) {
1513                 static BackendInfo      lback2;
1514
1515                 lback = backend_info( "ldap" );
1516
1517                 if ( lback == NULL ) {
1518                         return 1;
1519                 }
1520
1521                 lback2 = *lback;
1522                 lback2.bi_type = ldapchain.on_bi.bi_type;
1523                 lback = &lback2;
1524         }
1525
1526         lc = ch_malloc( sizeof( ldap_chain_t ) );
1527         if ( lc == NULL ) {
1528                 return 1;
1529         }
1530         memset( lc, 0, sizeof( ldap_chain_t ) );
1531         lc->lc_max_depth = 1;
1532         ldap_pvt_thread_mutex_init( &lc->lc_lai.lai_mutex );
1533
1534         on->on_bi.bi_private = (void *)lc;
1535
1536         return 0;
1537 }
1538
1539 static int
1540 ldap_chain_db_config(
1541         BackendDB       *be,
1542         const char      *fname,
1543         int             lineno,
1544         int             argc,
1545         char            **argv )
1546 {
1547         slap_overinst   *on = (slap_overinst *)be->bd_info;
1548         ldap_chain_t    *lc = (ldap_chain_t *)on->on_bi.bi_private;
1549
1550         int             rc = SLAP_CONF_UNKNOWN;
1551                 
1552         if ( lc->lc_common_li == NULL ) {
1553                 void    *be_private = be->be_private;
1554                 ldap_chain_db_init_common( be );
1555                 lc->lc_common_li = lc->lc_cfg_li = (ldapinfo_t *)be->be_private;
1556                 be->be_private = be_private;
1557         }
1558
1559         /* Something for the chain database? */
1560         if ( strncasecmp( argv[ 0 ], "chain-", STRLENOF( "chain-" ) ) == 0 ) {
1561                 char            *save_argv0 = argv[ 0 ];
1562                 BackendInfo     *bd_info = be->bd_info;
1563                 void            *be_private = be->be_private;
1564                 ConfigOCs       *be_cf_ocs = be->be_cf_ocs;
1565                 static char     *allowed_argv[] = {
1566                         /* special: put URI here, so in the meanwhile
1567                          * it detects whether a new URI is being provided */
1568                         "uri",
1569                         "nretries",
1570                         "timeout",
1571                         /* flags */
1572                         "tls",
1573                         /* FIXME: maybe rebind-as-user should be allowed
1574                          * only within known URIs... */
1575                         "rebind-as-user",
1576                         "chase-referrals",
1577                         "t-f-support",
1578                         "proxy-whoami",
1579                         NULL
1580                 };
1581                 int             which_argv = -1;
1582
1583                 argv[ 0 ] += STRLENOF( "chain-" );
1584
1585                 for ( which_argv = 0; allowed_argv[ which_argv ]; which_argv++ ) {
1586                         if ( strcasecmp( argv[ 0 ], allowed_argv[ which_argv ] ) == 0 ) {
1587                                 break;
1588                         }
1589                 }
1590
1591                 if ( allowed_argv[ which_argv ] == NULL ) {
1592                         which_argv = -1;
1593
1594                         if ( lc->lc_cfg_li == lc->lc_common_li ) {
1595                                 Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1596                                         "\"%s\" only allowed within a URI directive.\n.",
1597                                         fname, lineno, argv[ 0 ] );
1598                                 return 1;
1599                         }
1600                 }
1601
1602                 if ( which_argv == 0 ) {
1603                         rc = ldap_chain_db_init_one( be );
1604                         if ( rc != 0 ) {
1605                                 Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1606                                         "underlying slapd-ldap initialization failed.\n.",
1607                                         fname, lineno, 0 );
1608                                 return 1;
1609                         }
1610                         lc->lc_cfg_li = be->be_private;
1611                 }
1612
1613                 /* TODO: add checks on what other slapd-ldap(5) args
1614                  * should be put in the template; this is not quite
1615                  * harmful, because attributes that shouldn't don't
1616                  * get actually used, but the user should at least
1617                  * be warned.
1618                  */
1619
1620                 be->bd_info = lback;
1621                 be->be_private = (void *)lc->lc_cfg_li;
1622                 be->be_cf_ocs = lback->bi_cf_ocs;
1623
1624                 rc = config_generic_wrapper( be, fname, lineno, argc, argv );
1625
1626                 argv[ 0 ] = save_argv0;
1627                 be->be_cf_ocs = be_cf_ocs;
1628                 be->be_private = be_private;
1629                 be->bd_info = bd_info;
1630
1631                 if ( which_argv == 0 ) {
1632 private_destroy:;
1633                         if ( rc != 0 ) {
1634                                 BackendDB               db = *be;
1635
1636                                 db.bd_info = lback;
1637                                 db.be_private = (void *)lc->lc_cfg_li;
1638                                 ldap_chain_db_destroy_one( &db, NULL );
1639                                 lc->lc_cfg_li = NULL;
1640
1641                         } else {
1642                                 if ( lc->lc_cfg_li->li_bvuri == NULL
1643                                         || BER_BVISNULL( &lc->lc_cfg_li->li_bvuri[ 0 ] )
1644                                         || !BER_BVISNULL( &lc->lc_cfg_li->li_bvuri[ 1 ] ) )
1645                                 {
1646                                         Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1647                                                 "no URI list allowed in slapo-chain.\n",
1648                                                 fname, lineno, 0 );
1649                                         rc = 1;
1650                                         goto private_destroy;
1651                                 }
1652
1653                                 if ( avl_insert( &lc->lc_lai.lai_tree,
1654                                         (caddr_t)lc->lc_cfg_li,
1655                                         ldap_chain_uri_cmp, ldap_chain_uri_dup ) )
1656                                 {
1657                                         Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1658                                                 "duplicate URI in slapo-chain.\n",
1659                                                 fname, lineno, 0 );
1660                                         rc = 1;
1661                                         goto private_destroy;
1662                                 }
1663                         }
1664                 }
1665         }
1666         
1667         return rc;
1668 }
1669
1670 enum db_which {
1671         db_open = 0,
1672         db_close,
1673         db_destroy,
1674
1675         db_last
1676 };
1677
1678 typedef struct ldap_chain_db_apply_t {
1679         BackendDB       *be;
1680         BI_db_func      *func;
1681 } ldap_chain_db_apply_t;
1682
1683 static int
1684 ldap_chain_db_apply( void *datum, void *arg )
1685 {
1686         ldapinfo_t              *li = (ldapinfo_t *)datum;
1687         ldap_chain_db_apply_t   *lca = (ldap_chain_db_apply_t *)arg;
1688
1689         lca->be->be_private = (void *)li;
1690
1691         return lca->func( lca->be, NULL );
1692 }
1693
1694 static int
1695 ldap_chain_db_func(
1696         BackendDB *be,
1697         enum db_which which
1698 )
1699 {
1700         slap_overinst   *on = (slap_overinst *)be->bd_info;
1701         ldap_chain_t    *lc = (ldap_chain_t *)on->on_bi.bi_private;
1702
1703         int             rc = 0;
1704
1705         if ( lc ) {
1706                 BI_db_func      *func = (&lback->bi_db_open)[ which ];
1707
1708                 if ( func != NULL && lc->lc_common_li != NULL ) {
1709                         BackendDB               db = *be;
1710
1711                         db.bd_info = lback;
1712                         db.be_private = lc->lc_common_li;
1713
1714                         rc = func( &db, NULL );
1715
1716                         if ( rc != 0 ) {
1717                                 return rc;
1718                         }
1719
1720                         if ( lc->lc_lai.lai_tree != NULL ) {
1721                                 ldap_chain_db_apply_t   lca;
1722
1723                                 lca.be = &db;
1724                                 lca.func = func;
1725
1726                                 rc = avl_apply( lc->lc_lai.lai_tree,
1727                                         ldap_chain_db_apply, (void *)&lca,
1728                                         1, AVL_INORDER ) != AVL_NOMORE;
1729                         }
1730                 }
1731         }
1732
1733         return rc;
1734 }
1735
1736 static int
1737 ldap_chain_db_open(
1738         BackendDB       *be,
1739         ConfigReply     *cr )
1740 {
1741         slap_overinst   *on = (slap_overinst *) be->bd_info;
1742         ldap_chain_t    *lc = (ldap_chain_t *)on->on_bi.bi_private;
1743         slap_mask_t     monitoring;
1744         int             rc = 0;
1745
1746 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1747         rc = overlay_register_control( be, LDAP_CONTROL_X_CHAINING_BEHAVIOR );
1748         if ( rc != 0 ) {
1749                 return rc;
1750         }
1751 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1752
1753         if ( lc->lc_common_li == NULL ) {
1754                 void    *be_private = be->be_private;
1755                 ldap_chain_db_init_common( be );
1756                 lc->lc_common_li = lc->lc_cfg_li = (ldapinfo_t *)be->be_private;
1757                 be->be_private = be_private;
1758         }
1759
1760         /* filter out and restore monitoring */
1761         monitoring = ( SLAP_DBFLAGS( be ) & SLAP_DBFLAG_MONITORING );
1762         SLAP_DBFLAGS( be ) &= ~SLAP_DBFLAG_MONITORING;
1763         rc = ldap_chain_db_func( be, db_open );
1764         SLAP_DBFLAGS( be ) |= monitoring;
1765
1766         return rc;
1767 }
1768
1769 static int
1770 ldap_chain_db_close(
1771         BackendDB       *be,
1772         ConfigReply     *cr )
1773 {
1774         return ldap_chain_db_func( be, db_close );
1775 }
1776
1777 static int
1778 ldap_chain_db_destroy(
1779         BackendDB       *be,
1780         ConfigReply     *cr )
1781 {
1782         slap_overinst   *on = (slap_overinst *) be->bd_info;
1783         ldap_chain_t    *lc = (ldap_chain_t *)on->on_bi.bi_private;
1784
1785         int             rc;
1786
1787         rc = ldap_chain_db_func( be, db_destroy );
1788
1789         if ( lc ) {
1790                 avl_free( lc->lc_lai.lai_tree, NULL );
1791                 ldap_pvt_thread_mutex_destroy( &lc->lc_lai.lai_mutex );
1792                 ch_free( lc );
1793         }
1794
1795         return rc;
1796 }
1797
1798 /*
1799  * inits one instance of the slapd-ldap backend, and stores
1800  * the private info in be_private of the arg
1801  */
1802 static int
1803 ldap_chain_db_init_common(
1804         BackendDB       *be )
1805 {
1806         BackendInfo     *bi = be->bd_info;
1807         ldapinfo_t      *li;
1808         int             rc;
1809
1810         be->bd_info = lback;
1811         be->be_private = NULL;
1812         rc = lback->bi_db_init( be, NULL );
1813         if ( rc != 0 ) {
1814                 return rc;
1815         }
1816         li = (ldapinfo_t *)be->be_private;
1817         li->li_urllist_f = NULL;
1818         li->li_urllist_p = NULL;
1819
1820         be->bd_info = bi;
1821
1822         return 0;
1823 }
1824
1825 /*
1826  * inits one instance of the slapd-ldap backend, stores
1827  * the private info in be_private of the arg and fills
1828  * selected fields with data from the template.
1829  *
1830  * NOTE: add checks about the other fields of the template,
1831  * which are ignored and SHOULD NOT be configured by the user.
1832  */
1833 static int
1834 ldap_chain_db_init_one(
1835         BackendDB       *be )
1836 {
1837         slap_overinst   *on = (slap_overinst *)be->bd_info;
1838         ldap_chain_t    *lc = (ldap_chain_t *)on->on_bi.bi_private;
1839
1840         BackendInfo     *bi = be->bd_info;
1841         ldapinfo_t      *li;
1842
1843         slap_op_t       t;
1844
1845         be->bd_info = lback;
1846         be->be_private = NULL;
1847         t = lback->bi_db_init( be, NULL );
1848         if ( t != 0 ) {
1849                 return t;
1850         }
1851         li = (ldapinfo_t *)be->be_private;
1852         li->li_urllist_f = NULL;
1853         li->li_urllist_p = NULL;
1854
1855         /* copy common data */
1856         li->li_nretries = lc->lc_common_li->li_nretries;
1857         li->li_flags = lc->lc_common_li->li_flags;
1858         li->li_version = lc->lc_common_li->li_version;
1859         for ( t = 0; t < SLAP_OP_LAST; t++ ) {
1860                 li->li_timeout[ t ] = lc->lc_common_li->li_timeout[ t ];
1861         }
1862         be->bd_info = bi;
1863
1864         return 0;
1865 }
1866
1867 static int
1868 ldap_chain_db_open_one(
1869         BackendDB       *be )
1870 {
1871         if ( SLAP_DBMONITORING( be ) ) {
1872                 ldapinfo_t      *li = (ldapinfo_t *)be->be_private;
1873
1874                 if ( li->li_uri == NULL ) {
1875                         ber_str2bv( "cn=Common Connections", 0, 1,
1876                                 &li->li_monitor_info.lmi_rdn );
1877
1878                 } else {
1879                         char            *ptr;
1880
1881                         li->li_monitor_info.lmi_rdn.bv_len
1882                                 = STRLENOF( "cn=" ) + strlen( li->li_uri );
1883                         ptr = li->li_monitor_info.lmi_rdn.bv_val
1884                                 = ch_malloc( li->li_monitor_info.lmi_rdn.bv_len + 1 );
1885                         ptr = lutil_strcopy( ptr, "cn=" );
1886                         ptr = lutil_strcopy( ptr, li->li_uri );
1887                         ptr[ 0 ] = '\0';
1888                 }
1889         }
1890
1891         return lback->bi_db_open( be, NULL );
1892 }
1893
1894 typedef struct ldap_chain_conn_apply_t {
1895         BackendDB       *be;
1896         Connection      *conn;
1897 } ldap_chain_conn_apply_t;
1898
1899 static int
1900 ldap_chain_conn_apply( void *datum, void *arg )
1901 {
1902         ldapinfo_t              *li = (ldapinfo_t *)datum;
1903         ldap_chain_conn_apply_t *lca = (ldap_chain_conn_apply_t *)arg;
1904
1905         lca->be->be_private = (void *)li;
1906
1907         return lback->bi_connection_destroy( lca->be, lca->conn );
1908 }
1909
1910 static int
1911 ldap_chain_connection_destroy(
1912         BackendDB *be,
1913         Connection *conn
1914 )
1915 {
1916         slap_overinst           *on = (slap_overinst *) be->bd_info;
1917         ldap_chain_t            *lc = (ldap_chain_t *)on->on_bi.bi_private;
1918         void                    *private = be->be_private;
1919         ldap_chain_conn_apply_t lca;
1920         int                     rc;
1921
1922         be->be_private = NULL;
1923         lca.be = be;
1924         lca.conn = conn;
1925         ldap_pvt_thread_mutex_lock( &lc->lc_lai.lai_mutex );
1926         rc = avl_apply( lc->lc_lai.lai_tree, ldap_chain_conn_apply,
1927                 (void *)&lca, 1, AVL_INORDER ) != AVL_NOMORE;
1928         ldap_pvt_thread_mutex_unlock( &lc->lc_lai.lai_mutex );
1929         be->be_private = private;
1930
1931         return rc;
1932 }
1933
1934 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1935 static int
1936 ldap_chain_parse_ctrl(
1937         Operation       *op,
1938         SlapReply       *rs,
1939         LDAPControl     *ctrl )
1940 {
1941         ber_tag_t       tag;
1942         BerElement      *ber;
1943         ber_int_t       mode,
1944                         behavior;
1945
1946         if ( get_chaining( op ) != SLAP_CONTROL_NONE ) {
1947                 rs->sr_text = "Chaining behavior control specified multiple times";
1948                 return LDAP_PROTOCOL_ERROR;
1949         }
1950
1951         if ( op->o_pagedresults != SLAP_CONTROL_NONE ) {
1952                 rs->sr_text = "Chaining behavior control specified with pagedResults control";
1953                 return LDAP_PROTOCOL_ERROR;
1954         }
1955
1956         if ( BER_BVISEMPTY( &ctrl->ldctl_value ) ) {
1957                 mode = (SLAP_CH_RESOLVE_DEFAULT|SLAP_CH_CONTINUATION_DEFAULT);
1958
1959         } else {
1960                 ber_len_t       len;
1961
1962                 /* Parse the control value
1963                  *      ChainingBehavior ::= SEQUENCE { 
1964                  *           resolveBehavior         Behavior OPTIONAL, 
1965                  *           continuationBehavior    Behavior OPTIONAL } 
1966                  *                             
1967                  *      Behavior :: = ENUMERATED { 
1968                  *           chainingPreferred       (0), 
1969                  *           chainingRequired        (1), 
1970                  *           referralsPreferred      (2), 
1971                  *           referralsRequired       (3) } 
1972                  */
1973
1974                 ber = ber_init( &ctrl->ldctl_value );
1975                 if( ber == NULL ) {
1976                         rs->sr_text = "internal error";
1977                         return LDAP_OTHER;
1978                 }
1979
1980                 tag = ber_scanf( ber, "{e" /* } */, &behavior );
1981                 /* FIXME: since the whole SEQUENCE is optional,
1982                  * should we accept no enumerations at all? */
1983                 if ( tag != LBER_ENUMERATED ) {
1984                         rs->sr_text = "Chaining behavior control: resolveBehavior decoding error";
1985                         return LDAP_PROTOCOL_ERROR;
1986                 }
1987
1988                 switch ( behavior ) {
1989                 case LDAP_CHAINING_PREFERRED:
1990                         mode = SLAP_CH_RESOLVE_CHAINING_PREFERRED;
1991                         break;
1992
1993                 case LDAP_CHAINING_REQUIRED:
1994                         mode = SLAP_CH_RESOLVE_CHAINING_REQUIRED;
1995                         break;
1996
1997                 case LDAP_REFERRALS_PREFERRED:
1998                         mode = SLAP_CH_RESOLVE_REFERRALS_PREFERRED;
1999                         break;
2000
2001                 case LDAP_REFERRALS_REQUIRED:
2002                         mode = SLAP_CH_RESOLVE_REFERRALS_REQUIRED;
2003                         break;
2004
2005                 default:
2006                         rs->sr_text = "Chaining behavior control: unknown resolveBehavior";
2007                         return LDAP_PROTOCOL_ERROR;
2008                 }
2009
2010                 tag = ber_peek_tag( ber, &len );
2011                 if ( tag == LBER_ENUMERATED ) {
2012                         tag = ber_scanf( ber, "e", &behavior );
2013                         if ( tag == LBER_ERROR ) {
2014                                 rs->sr_text = "Chaining behavior control: continuationBehavior decoding error";
2015                                 return LDAP_PROTOCOL_ERROR;
2016                         }
2017                 }
2018
2019                 if ( tag == LBER_DEFAULT ) {
2020                         mode |= SLAP_CH_CONTINUATION_DEFAULT;
2021
2022                 } else {
2023                         switch ( behavior ) {
2024                         case LDAP_CHAINING_PREFERRED:
2025                                 mode |= SLAP_CH_CONTINUATION_CHAINING_PREFERRED;
2026                                 break;
2027
2028                         case LDAP_CHAINING_REQUIRED:
2029                                 mode |= SLAP_CH_CONTINUATION_CHAINING_REQUIRED;
2030                                 break;
2031
2032                         case LDAP_REFERRALS_PREFERRED:
2033                                 mode |= SLAP_CH_CONTINUATION_REFERRALS_PREFERRED;
2034                                 break;
2035
2036                         case LDAP_REFERRALS_REQUIRED:
2037                                 mode |= SLAP_CH_CONTINUATION_REFERRALS_REQUIRED;
2038                                 break;
2039
2040                         default:
2041                                 rs->sr_text = "Chaining behavior control: unknown continuationBehavior";
2042                                 return LDAP_PROTOCOL_ERROR;
2043                         }
2044                 }
2045
2046                 if ( ( ber_scanf( ber, /* { */ "}") ) == LBER_ERROR ) {
2047                         rs->sr_text = "Chaining behavior control: decoding error";
2048                         return LDAP_PROTOCOL_ERROR;
2049                 }
2050
2051                 (void) ber_free( ber, 1 );
2052         }
2053
2054         op->o_chaining = mode | ( ctrl->ldctl_iscritical
2055                         ? SLAP_CONTROL_CRITICAL
2056                         : SLAP_CONTROL_NONCRITICAL );
2057
2058         return LDAP_SUCCESS;
2059 }
2060 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
2061
2062 int
2063 chain_initialize( void )
2064 {
2065         int     rc;
2066
2067         /* Make sure we don't exceed the bits reserved for userland */
2068         config_check_userland( CH_LAST );
2069
2070 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
2071         rc = register_supported_control( LDAP_CONTROL_X_CHAINING_BEHAVIOR,
2072                         /* SLAP_CTRL_GLOBAL| */ SLAP_CTRL_ACCESS|SLAP_CTRL_HIDE, NULL,
2073                         ldap_chain_parse_ctrl, &sc_chainingBehavior );
2074         if ( rc != LDAP_SUCCESS ) {
2075                 Debug( LDAP_DEBUG_ANY, "slapd-chain: "
2076                         "unable to register chaining behavior control: %d.\n",
2077                         rc, 0, 0 );
2078                 return rc;
2079         }
2080 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
2081
2082         ldapchain.on_bi.bi_type = "chain";
2083         ldapchain.on_bi.bi_db_init = ldap_chain_db_init;
2084         ldapchain.on_bi.bi_db_config = ldap_chain_db_config;
2085         ldapchain.on_bi.bi_db_open = ldap_chain_db_open;
2086         ldapchain.on_bi.bi_db_close = ldap_chain_db_close;
2087         ldapchain.on_bi.bi_db_destroy = ldap_chain_db_destroy;
2088
2089         ldapchain.on_bi.bi_connection_destroy = ldap_chain_connection_destroy;
2090
2091         ldapchain.on_response = ldap_chain_response;
2092
2093         ldapchain.on_bi.bi_cf_ocs = chainocs;
2094
2095         rc = config_register_schema( chaincfg, chainocs );
2096         if ( rc ) {
2097                 return rc;
2098         }
2099
2100         return overlay_register( &ldapchain );
2101 }
2102