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