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