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