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