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