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