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