]> git.sur5r.net Git - openldap/blob - servers/slapd/back-ldap/chain.c
a2051d912ca9e3e011d1a8c52df165492ccd59c6
[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         BI_op_func      *op_f,
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 about 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 && sc2.sc_private != LDAP_CH_RES ) {
775                         /* FIXME: should we send response? */
776                         Debug( LDAP_DEBUG_ANY,
777                                 "%s: ldap_chain_response: "
778                                 "overlay should have sent result.\n",
779                                 op->o_log_prefix, 0, 0 );
780                 }
781                 break;
782
783         default:
784 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
785                 if ( sc2.sc_private == LDAP_CH_ERR && rs->sr_err == LDAP_X_CANNOT_CHAIN ) {
786                         goto cannot_chain;
787                 }
788
789                 switch ( ( get_chainingBehavior( op ) & chain_mask ) >> chain_shift ) {
790                 case LDAP_CHAINING_REQUIRED:
791 cannot_chain:;
792                         op->o_callback = NULL;
793                         send_ldap_error( op, rs, LDAP_X_CANNOT_CHAIN,
794                                 "operation cannot be completed without chaining" );
795                         goto dont_chain;
796
797                 default:
798 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
799                         rc = SLAP_CB_CONTINUE;
800                         rs->sr_err = sr_err;
801                         rs->sr_type = sr_type;
802                         rs->sr_matched = matched;
803                         rs->sr_ref = ref;
804 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
805                         break;
806                 }
807 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
808         }
809
810         if ( sc2.sc_private == LDAP_CH_NONE && rc != SLAPD_ABANDON ) {
811                 op->o_callback = NULL;
812                 rc = rs->sr_err = slap_map_api2result( rs );
813                 send_ldap_result( op, rs );
814         }
815
816 dont_chain:;
817         rs->sr_err = sr_err;
818         rs->sr_type = sr_type;
819         rs->sr_matched = matched;
820         rs->sr_ref = ref;
821         op->o_bd->be_private = private;
822         op->o_callback = sc;
823         op->o_ndn = ndn;
824
825         return rc;
826 }
827
828 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
829 static int
830 ldap_chain_parse_ctrl(
831         Operation       *op,
832         SlapReply       *rs,
833         LDAPControl     *ctrl );
834
835 static int
836 str2chain( const char *s )
837 {
838         if ( strcasecmp( s, "chainingPreferred" ) == 0 ) {
839                 return LDAP_CHAINING_PREFERRED;
840                 
841         } else if ( strcasecmp( s, "chainingRequired" ) == 0 ) {
842                 return LDAP_CHAINING_REQUIRED;
843
844         } else if ( strcasecmp( s, "referralsPreferred" ) == 0 ) {
845                 return LDAP_REFERRALS_PREFERRED;
846                 
847         } else if ( strcasecmp( s, "referralsRequired" ) == 0 ) {
848                 return LDAP_REFERRALS_REQUIRED;
849         }
850
851         return -1;
852 }
853 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
854
855 /*
856  * configuration...
857  */
858
859 enum {
860         CH_CHAINING = 1,
861         CH_CACHE_URI = 2,
862
863         CH_LAST
864 };
865
866 static ConfigDriver chain_cf_gen;
867 static ConfigCfAdd chain_cfadd;
868 static ConfigLDAPadd chain_ldadd;
869
870 static ConfigTable chaincfg[] = {
871 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
872         { "chain-chaining", "args",
873                 2, 4, 0, ARG_MAGIC|ARG_BERVAL|CH_CHAINING, chain_cf_gen,
874                 "( OLcfgOvAt:3.1 NAME 'olcChainingBehavior' "
875                         "DESC 'Chaining behavior control parameters (draft-sermersheim-ldap-chaining)' "
876                         "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
877 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
878         { "chain-cache-uri", "TRUE/FALSE",
879                 2, 2, 0, ARG_MAGIC|ARG_ON_OFF|CH_CACHE_URI, chain_cf_gen,
880                 "( OLcfgOvAt:3.2 NAME 'olcCacheURI' "
881                         "DESC 'Enables caching of URIs not present in configuration' "
882                         "SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
883         { NULL, NULL, 0, 0, 0, ARG_IGNORED }
884 };
885
886 static ConfigOCs chainocs[] = {
887         { "( OLcfgOvOc:3.1 "
888                 "NAME 'olcChainConfig' "
889                 "DESC 'Chain configuration' "
890                 "SUP olcOverlayConfig "
891                 "MAY ( "
892 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
893                         "olcChainingBehavior $ "
894 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
895                         "olcCacheURI "
896                         ") )",
897                 Cft_Overlay, chaincfg, NULL, chain_cfadd },
898         { "( OLcfgOvOc:3.2 "
899                 "NAME 'olcChainDatabase' "
900                 "DESC 'Chain remote server configuration' "
901                 "AUXILIARY )",
902                 Cft_Misc, chaincfg, chain_ldadd },
903         { NULL, 0, NULL }
904 };
905
906 static int
907 chain_ldadd( CfEntryInfo *p, Entry *e, ConfigArgs *ca )
908 {
909         slap_overinst           *on;
910         ldap_chain_t            *lc;
911
912         ldapinfo_t              *li;
913
914         AttributeDescription    *ad = NULL;
915         Attribute               *at;
916         const char              *text;
917
918         int                     rc;
919
920         if ( p->ce_type != Cft_Overlay
921                 || !p->ce_bi
922                 || p->ce_bi->bi_cf_ocs != chainocs )
923         {
924                 return LDAP_CONSTRAINT_VIOLATION;
925         }
926
927         on = (slap_overinst *)p->ce_bi;
928         lc = (ldap_chain_t *)on->on_bi.bi_private;
929
930         assert( ca->be == NULL );
931         ca->be = (BackendDB *)ch_calloc( 1, sizeof( BackendDB ) );
932
933         ca->be->bd_info = (BackendInfo *)on;
934
935         rc = slap_str2ad( "olcDbURI", &ad, &text );
936         assert( rc == LDAP_SUCCESS );
937
938         at = attr_find( e->e_attrs, ad );
939         if ( lc->lc_common_li == NULL && at != NULL ) {
940                 /* FIXME: we should generate an empty default entry
941                  * if none is supplied */
942                 Debug( LDAP_DEBUG_ANY, "slapd-chain: "
943                         "first underlying database \"%s\" "
944                         "cannot contain attribute \"%s\".\n",
945                         e->e_name.bv_val, ad->ad_cname.bv_val, 0 );
946                 rc = LDAP_CONSTRAINT_VIOLATION;
947                 goto done;
948
949         } else if ( lc->lc_common_li != NULL && at == NULL ) {
950                 /* FIXME: we should generate an empty default entry
951                  * if none is supplied */
952                 Debug( LDAP_DEBUG_ANY, "slapd-chain: "
953                         "subsequent underlying database \"%s\" "
954                         "must contain attribute \"%s\".\n",
955                         e->e_name.bv_val, ad->ad_cname.bv_val, 0 );
956                 rc = LDAP_CONSTRAINT_VIOLATION;
957                 goto done;
958         }
959
960         if ( lc->lc_common_li == NULL ) {
961                 rc = ldap_chain_db_init_common( ca->be );
962
963         } else {
964                 rc = ldap_chain_db_init_one( ca->be );
965         }
966
967         if ( rc != 0 ) {
968                 Debug( LDAP_DEBUG_ANY, "slapd-chain: "
969                         "unable to init %sunderlying database \"%s\".\n",
970                         lc->lc_common_li == NULL ? "common " : "", e->e_name.bv_val, 0 );
971                 return LDAP_CONSTRAINT_VIOLATION;
972         }
973
974         li = ca->be->be_private;
975
976         if ( lc->lc_common_li == NULL ) {
977                 lc->lc_common_li = li;
978
979         } else if ( avl_insert( &lc->lc_lai.lai_tree, (caddr_t)li,
980                 ldap_chain_uri_cmp, ldap_chain_uri_dup ) )
981         {
982                 Debug( LDAP_DEBUG_ANY, "slapd-chain: "
983                         "database \"%s\" insert failed.\n",
984                         e->e_name.bv_val, 0, 0 );
985                 rc = LDAP_CONSTRAINT_VIOLATION;
986                 goto done;
987         }
988
989 done:;
990         if ( rc != LDAP_SUCCESS ) {
991                 (void)ldap_chain_db_destroy_one( ca->be );
992                 ch_free( ca->be );
993                 ca->be = NULL;
994         }
995
996         return rc;
997 }
998
999 typedef struct ldap_chain_cfadd_apply_t {
1000         Operation       *op;
1001         SlapReply       *rs;
1002         Entry           *p;
1003         ConfigArgs      *ca;
1004         int             count;
1005 } ldap_chain_cfadd_apply_t;
1006
1007 static int
1008 ldap_chain_cfadd_apply( void *datum, void *arg )
1009 {
1010         ldapinfo_t                      *li = (ldapinfo_t *)datum;
1011         ldap_chain_cfadd_apply_t        *lca = (ldap_chain_cfadd_apply_t *)arg;
1012
1013         struct berval                   bv;
1014
1015         /* FIXME: should not hardcode "olcDatabase" here */
1016         bv.bv_len = snprintf( lca->ca->msg, sizeof( lca->ca->msg ),
1017                 "olcDatabase={%d}%s", lca->count, lback->bi_type );
1018         bv.bv_val = lca->ca->msg;
1019
1020         lca->ca->be->be_private = (void *)li;
1021         config_build_entry( lca->op, lca->rs, lca->p->e_private, lca->ca,
1022                 &bv, lback->bi_cf_ocs, &chainocs[1] );
1023
1024         lca->count++;
1025
1026         return 0;
1027 }
1028
1029 static int
1030 chain_cfadd( Operation *op, SlapReply *rs, Entry *p, ConfigArgs *ca )
1031 {
1032         CfEntryInfo     *pe = p->e_private;
1033         slap_overinst   *on = (slap_overinst *)pe->ce_bi;
1034         ldap_chain_t    *lc = (ldap_chain_t *)on->on_bi.bi_private;
1035         void            *priv = (void *)ca->be->be_private;
1036
1037         if ( lback->bi_cf_ocs ) {
1038                 ldap_chain_cfadd_apply_t        lca = { 0 };
1039
1040                 lca.op = op;
1041                 lca.rs = rs;
1042                 lca.p = p;
1043                 lca.ca = ca;
1044                 lca.count = 0;
1045
1046                 (void)ldap_chain_cfadd_apply( (void *)lc->lc_common_li, (void *)&lca );
1047
1048                 (void)avl_apply( lc->lc_lai.lai_tree, ldap_chain_cfadd_apply,
1049                         &lca, 1, AVL_INORDER );
1050
1051                 ca->be->be_private = priv;
1052         }
1053
1054         return 0;
1055 }
1056
1057 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1058 static slap_verbmasks chaining_mode[] = {
1059         { BER_BVC("referralsRequired"),         LDAP_REFERRALS_REQUIRED },
1060         { BER_BVC("referralsPreferred"),        LDAP_REFERRALS_PREFERRED },
1061         { BER_BVC("chainingRequired"),          LDAP_CHAINING_REQUIRED },
1062         { BER_BVC("chainingPreferred"),         LDAP_CHAINING_PREFERRED },
1063         { BER_BVNULL,                           0 }
1064 };
1065 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1066
1067 static int
1068 chain_cf_gen( ConfigArgs *c )
1069 {
1070         slap_overinst   *on = (slap_overinst *)c->bi;
1071         ldap_chain_t    *lc = (ldap_chain_t *)on->on_bi.bi_private;
1072
1073         int             rc = 0;
1074
1075         if ( c->op == SLAP_CONFIG_EMIT ) {
1076                 switch( c->type ) {
1077 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1078                 case CH_CHAINING: {
1079                         struct berval   resolve = BER_BVNULL,
1080                                         continuation = BER_BVNULL;
1081
1082                         if ( !LDAP_CHAIN_CHAINING( lc ) ) {
1083                                 return 1;
1084                         }
1085
1086                         enum_to_verb( chaining_mode, ( ( lc->lc_chaining_ctrlflag & SLAP_CH_RESOLVE_MASK ) >> SLAP_CH_RESOLVE_SHIFT ), &resolve );
1087                         enum_to_verb( chaining_mode, ( ( lc->lc_chaining_ctrlflag & SLAP_CH_CONTINUATION_MASK ) >> SLAP_CH_CONTINUATION_SHIFT ), &continuation );
1088
1089                         c->value_bv.bv_len = STRLENOF( "resolve=" ) + resolve.bv_len
1090                                 + STRLENOF( " " )
1091                                 + STRLENOF( "continuation=" ) + continuation.bv_len;
1092                         c->value_bv.bv_val = ch_malloc( c->value_bv.bv_len + 1 );
1093                         snprintf( c->value_bv.bv_val, c->value_bv.bv_len + 1,
1094                                 "resolve=%s continuation=%s",
1095                                 resolve.bv_val, continuation.bv_val );
1096
1097                         if ( lc->lc_chaining_ctrl.ldctl_iscritical ) {
1098                                 c->value_bv.bv_val = ch_realloc( c->value_bv.bv_val,
1099                                         c->value_bv.bv_len + STRLENOF( " critical" ) + 1 );
1100                                 AC_MEMCPY( &c->value_bv.bv_val[ c->value_bv.bv_len ],
1101                                         " critical", STRLENOF( " critical" ) + 1 );
1102                                 c->value_bv.bv_len += STRLENOF( " critical" );
1103                         }
1104
1105                         break;
1106                 }
1107 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1108
1109                 case CH_CACHE_URI:
1110                         c->value_int = LDAP_CHAIN_CACHE_URI( lc );
1111                         break;
1112
1113                 default:
1114                         assert( 0 );
1115                         rc = 1;
1116                 }
1117                 return rc;
1118
1119         } else if ( c->op == LDAP_MOD_DELETE ) {
1120                 switch( c->type ) {
1121                 case CH_CHAINING:
1122                         return 1;
1123
1124                 case CH_CACHE_URI:
1125                         lc->lc_flags &= ~LDAP_CHAIN_F_CACHE_URI;
1126                         break;
1127
1128                 default:
1129                         return 1;
1130                 }
1131                 return rc;
1132         }
1133
1134         switch( c->type ) {
1135         case CH_CHAINING: {
1136 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1137                 char                    **argv = c->argv;
1138                 int                     argc = c->argc;
1139                 BerElementBuffer        berbuf;
1140                 BerElement              *ber = (BerElement *)&berbuf;
1141                 int                     resolve = -1,
1142                                         continuation = -1,
1143                                         iscritical = 0;
1144                 Operation               op = { 0 };
1145                 SlapReply               rs = { 0 };
1146
1147                 lc->lc_chaining_ctrlflag = 0;
1148
1149                 for ( argc--, argv++; argc > 0; argc--, argv++ ) {
1150                         if ( strncasecmp( argv[ 0 ], "resolve=", STRLENOF( "resolve=" ) ) == 0 ) {
1151                                 resolve = str2chain( argv[ 0 ] + STRLENOF( "resolve=" ) );
1152                                 if ( resolve == -1 ) {
1153                                         Debug( LDAP_DEBUG_ANY, "%s: "
1154                                                 "illegal <resolve> value %s "
1155                                                 "in \"chain-chaining>\".\n",
1156                                                 c->log, argv[ 0 ], 0 );
1157                                         return 1;
1158                                 }
1159
1160                         } else if ( strncasecmp( argv[ 0 ], "continuation=", STRLENOF( "continuation=" ) ) == 0 ) {
1161                                 continuation = str2chain( argv[ 0 ] + STRLENOF( "continuation=" ) );
1162                                 if ( continuation == -1 ) {
1163                                         Debug( LDAP_DEBUG_ANY, "%s: "
1164                                                 "illegal <continuation> value %s "
1165                                                 "in \"chain-chaining\".\n",
1166                                                 c->log, argv[ 0 ], 0 );
1167                                         return 1;
1168                                 }
1169
1170                         } else if ( strcasecmp( argv[ 0 ], "critical" ) == 0 ) {
1171                                 iscritical = 1;
1172
1173                         } else {
1174                                 Debug( LDAP_DEBUG_ANY, "%s: "
1175                                         "unknown option in \"chain-chaining\".\n",
1176                                         c->log, 0, 0 );
1177                                 return 1;
1178                         }
1179                 }
1180
1181                 if ( resolve != -1 || continuation != -1 ) {
1182                         int     err;
1183
1184                         if ( resolve == -1 ) {
1185                                 /* default */
1186                                 resolve = SLAP_CHAINING_DEFAULT;
1187                         }
1188
1189                         ber_init2( ber, NULL, LBER_USE_DER );
1190
1191                         err = ber_printf( ber, "{e" /* } */, resolve );
1192                         if ( err == -1 ) {
1193                                 ber_free( ber, 1 );
1194                                 Debug( LDAP_DEBUG_ANY, "%s: "
1195                                         "chaining behavior control encoding error!\n",
1196                                         c->log, 0, 0 );
1197                                 return 1;
1198                         }
1199
1200                         if ( continuation > -1 ) {
1201                                 err = ber_printf( ber, "e", continuation );
1202                                 if ( err == -1 ) {
1203                                         ber_free( ber, 1 );
1204                                         Debug( LDAP_DEBUG_ANY, "%s: "
1205                                                 "chaining behavior control encoding error!\n",
1206                                                 c->log, 0, 0 );
1207                                         return 1;
1208                                 }
1209                         }
1210
1211                         err = ber_printf( ber, /* { */ "N}" );
1212                         if ( err == -1 ) {
1213                                 ber_free( ber, 1 );
1214                                 Debug( LDAP_DEBUG_ANY, "%s: "
1215                                         "chaining behavior control encoding error!\n",
1216                                         c->log, 0, 0 );
1217                                 return 1;
1218                         }
1219
1220                         if ( ber_flatten2( ber, &lc->lc_chaining_ctrl.ldctl_value, 0 ) == -1 ) {
1221                                 exit( EXIT_FAILURE );
1222                         }
1223
1224                 } else {
1225                         BER_BVZERO( &lc->lc_chaining_ctrl.ldctl_value );
1226                 }
1227
1228                 lc->lc_chaining_ctrl.ldctl_oid = LDAP_CONTROL_X_CHAINING_BEHAVIOR;
1229                 lc->lc_chaining_ctrl.ldctl_iscritical = iscritical;
1230
1231                 if ( ldap_chain_parse_ctrl( &op, &rs, &lc->lc_chaining_ctrl ) != LDAP_SUCCESS )
1232                 {
1233                         Debug( LDAP_DEBUG_ANY, "%s: "
1234                                 "unable to parse chaining control%s%s.\n",
1235                                 c->log, rs.sr_text ? ": " : "",
1236                                 rs.sr_text ? rs.sr_text : "" );
1237                         return 1;
1238                 }
1239
1240                 lc->lc_chaining_ctrlflag = op.o_chaining;
1241
1242                 lc->lc_flags |= LDAP_CHAIN_F_CHAINING;
1243
1244                 rc = 0;
1245 #else /* ! LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1246                 Debug( LDAP_DEBUG_ANY, "%s: "
1247                         "\"chaining\" control unsupported (ignored).\n",
1248                         c->log, 0, 0 );
1249 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1250                 } break;
1251
1252         case CH_CACHE_URI:
1253                 if ( c->value_int ) {
1254                         lc->lc_flags |= LDAP_CHAIN_F_CACHE_URI;
1255                 } else {
1256                         lc->lc_flags &= ~LDAP_CHAIN_F_CACHE_URI;
1257                 }
1258                 break;
1259
1260         default:
1261                 assert( 0 );
1262                 return 1;
1263         }
1264         return rc;
1265 }
1266
1267 static int
1268 ldap_chain_db_init(
1269         BackendDB *be )
1270 {
1271         slap_overinst   *on = (slap_overinst *)be->bd_info;
1272         ldap_chain_t    *lc = NULL;
1273
1274         if ( lback == NULL ) {
1275                 lback = backend_info( "ldap" );
1276
1277                 if ( lback == NULL ) {
1278                         return 1;
1279                 }
1280         }
1281
1282         lc = ch_malloc( sizeof( ldap_chain_t ) );
1283         if ( lc == NULL ) {
1284                 return 1;
1285         }
1286         memset( lc, 0, sizeof( ldap_chain_t ) );
1287
1288         on->on_bi.bi_private = (void *)lc;
1289
1290         return 0;
1291 }
1292
1293 static int
1294 ldap_chain_db_config(
1295         BackendDB       *be,
1296         const char      *fname,
1297         int             lineno,
1298         int             argc,
1299         char            **argv )
1300 {
1301         slap_overinst   *on = (slap_overinst *)be->bd_info;
1302         ldap_chain_t    *lc = (ldap_chain_t *)on->on_bi.bi_private;
1303
1304         int             rc = SLAP_CONF_UNKNOWN;
1305                 
1306         if ( lc->lc_common_li == NULL ) {
1307                 void    *be_private = be->be_private;
1308                 ldap_chain_db_init_common( be );
1309                 lc->lc_common_li = lc->lc_cfg_li = (ldapinfo_t *)be->be_private;
1310                 be->be_private = be_private;
1311         }
1312
1313         /* Something for the chain database? */
1314         if ( strncasecmp( argv[ 0 ], "chain-", STRLENOF( "chain-" ) ) == 0 ) {
1315                 char            *save_argv0 = argv[ 0 ];
1316                 BackendInfo     *bd_info = be->bd_info;
1317                 void            *be_private = be->be_private;
1318                 ConfigOCs       *be_cf_ocs = be->be_cf_ocs;
1319                 static char     *allowed_argv[] = {
1320                         /* special: put URI here, so in the meanwhile
1321                          * it detects whether a new URI is being provided */
1322                         "uri",
1323                         "nretries",
1324                         "timeout",
1325                         /* flags */
1326                         "tls",
1327                         /* FIXME: maybe rebind-as-user should be allowed
1328                          * only within known URIs... */
1329                         "rebind-as-user",
1330                         "chase-referrals",
1331                         "t-f-support",
1332                         "proxy-whoami",
1333                         NULL
1334                 };
1335                 int             which_argv = -1;
1336
1337                 argv[ 0 ] += STRLENOF( "chain-" );
1338
1339                 for ( which_argv = 0; allowed_argv[ which_argv ]; which_argv++ ) {
1340                         if ( strcasecmp( argv[ 0 ], allowed_argv[ which_argv ] ) == 0 ) {
1341                                 break;
1342                         }
1343                 }
1344
1345                 if ( allowed_argv[ which_argv ] == NULL ) {
1346                         which_argv = -1;
1347
1348                         if ( lc->lc_cfg_li == lc->lc_common_li ) {
1349                                 Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1350                                         "\"%s\" only allowed within a URI directive.\n.",
1351                                         fname, lineno, argv[ 0 ] );
1352                                 return 1;
1353                         }
1354                 }
1355
1356                 if ( which_argv == 0 ) {
1357                         rc = ldap_chain_db_init_one( be );
1358                         if ( rc != 0 ) {
1359                                 Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1360                                         "underlying slapd-ldap initialization failed.\n.",
1361                                         fname, lineno, 0 );
1362                                 return 1;
1363                         }
1364                         lc->lc_cfg_li = be->be_private;
1365                 }
1366
1367                 /* TODO: add checks on what other slapd-ldap(5) args
1368                  * should be put in the template; this is not quite
1369                  * harmful, because attributes that shouldn't don't
1370                  * get actually used, but the user should at least
1371                  * be warned.
1372                  */
1373
1374                 be->bd_info = lback;
1375                 be->be_private = (void *)lc->lc_cfg_li;
1376                 be->be_cf_ocs = lback->bi_cf_ocs;
1377
1378                 rc = config_generic_wrapper( be, fname, lineno, argc, argv );
1379
1380                 argv[ 0 ] = save_argv0;
1381                 be->be_cf_ocs = be_cf_ocs;
1382                 be->be_private = be_private;
1383                 be->bd_info = bd_info;
1384
1385                 if ( which_argv == 0 ) {
1386 private_destroy:;
1387                         if ( rc != 0 ) {
1388                                 BackendDB               db = *be;
1389
1390                                 db.bd_info = lback;
1391                                 db.be_private = (void *)lc->lc_cfg_li;
1392                                 ldap_chain_db_destroy_one( &db );
1393                                 lc->lc_cfg_li = NULL;
1394
1395                         } else {
1396                                 if ( lc->lc_cfg_li->li_bvuri == NULL
1397                                         || BER_BVISNULL( &lc->lc_cfg_li->li_bvuri[ 0 ] )
1398                                         || !BER_BVISNULL( &lc->lc_cfg_li->li_bvuri[ 1 ] ) )
1399                                 {
1400                                         Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1401                                                 "no URI list allowed in slapo-chain.\n",
1402                                                 fname, lineno, 0 );
1403                                         rc = 1;
1404                                         goto private_destroy;
1405                                 }
1406
1407                                 if ( avl_insert( &lc->lc_lai.lai_tree,
1408                                         (caddr_t)lc->lc_cfg_li,
1409                                         ldap_chain_uri_cmp, ldap_chain_uri_dup ) )
1410                                 {
1411                                         Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1412                                                 "duplicate URI in slapo-chain.\n",
1413                                                 fname, lineno, 0 );
1414                                         rc = 1;
1415                                         goto private_destroy;
1416                                 }
1417                         }
1418                 }
1419         }
1420         
1421         return rc;
1422 }
1423
1424 enum db_which {
1425         db_open = 0,
1426         db_close,
1427         db_destroy,
1428
1429         db_last
1430 };
1431
1432 typedef struct ldap_chain_db_apply_t {
1433         BackendDB       *be;
1434         BI_db_func      *func;
1435 } ldap_chain_db_apply_t;
1436
1437 static int
1438 ldap_chain_db_apply( void *datum, void *arg )
1439 {
1440         ldapinfo_t              *li = (ldapinfo_t *)datum;
1441         ldap_chain_db_apply_t   *lca = (ldap_chain_db_apply_t *)arg;
1442
1443         lca->be->be_private = (void *)li;
1444
1445         return lca->func( lca->be );
1446 }
1447
1448 static int
1449 ldap_chain_db_func(
1450         BackendDB *be,
1451         enum db_which which
1452 )
1453 {
1454         slap_overinst   *on = (slap_overinst *)be->bd_info;
1455         ldap_chain_t    *lc = (ldap_chain_t *)on->on_bi.bi_private;
1456
1457         int             rc = 0;
1458
1459         if ( lc ) {
1460                 BI_db_func      *func = (&lback->bi_db_open)[ which ];
1461
1462                 if ( func != NULL && lc->lc_common_li != NULL ) {
1463                         BackendDB               db = *be;
1464
1465                         db.bd_info = lback;
1466                         db.be_private = lc->lc_common_li;
1467
1468                         rc = func( &db );
1469
1470                         if ( rc != 0 ) {
1471                                 return rc;
1472                         }
1473
1474                         if ( lc->lc_lai.lai_tree != NULL ) {
1475                                 ldap_chain_db_apply_t   lca;
1476
1477                                 lca.be = &db;
1478                                 lca.func = func;
1479
1480                                 rc = avl_apply( lc->lc_lai.lai_tree,
1481                                         ldap_chain_db_apply, (void *)&lca,
1482                                         1, AVL_INORDER ) != AVL_NOMORE;
1483                         }
1484                 }
1485         }
1486
1487         return rc;
1488 }
1489
1490 static int
1491 ldap_chain_db_open(
1492         BackendDB       *be )
1493 {
1494         slap_overinst   *on = (slap_overinst *) be->bd_info;
1495         ldap_chain_t    *lc = (ldap_chain_t *)on->on_bi.bi_private;
1496
1497 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1498         int     rc = 0;
1499
1500         rc = overlay_register_control( be, LDAP_CONTROL_X_CHAINING_BEHAVIOR );
1501         if ( rc != 0 ) {
1502                 return rc;
1503         }
1504 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1505
1506         if ( lc->lc_common_li == NULL ) {
1507                 void    *be_private = be->be_private;
1508                 ldap_chain_db_init_common( be );
1509                 lc->lc_common_li = lc->lc_cfg_li = (ldapinfo_t *)be->be_private;
1510                 be->be_private = be_private;
1511         }
1512
1513         return ldap_chain_db_func( be, db_open );
1514 }
1515
1516 static int
1517 ldap_chain_db_close(
1518         BackendDB       *be )
1519 {
1520         return ldap_chain_db_func( be, db_close );
1521 }
1522
1523 static int
1524 ldap_chain_db_destroy(
1525         BackendDB       *be )
1526 {
1527         slap_overinst   *on = (slap_overinst *) be->bd_info;
1528         ldap_chain_t    *lc = (ldap_chain_t *)on->on_bi.bi_private;
1529
1530         int             rc;
1531
1532         rc = ldap_chain_db_func( be, db_destroy );
1533
1534         if ( lc ) {
1535                 avl_free( lc->lc_lai.lai_tree, NULL );
1536                 ch_free( lc );
1537         }
1538
1539         return rc;
1540 }
1541
1542 /*
1543  * inits one instance of the slapd-ldap backend, and stores
1544  * the private info in be_private of the arg
1545  */
1546 static int
1547 ldap_chain_db_init_common(
1548         BackendDB       *be )
1549 {
1550         BackendInfo     *bi = be->bd_info;
1551         int             t;
1552
1553         be->bd_info = lback;
1554         be->be_private = NULL;
1555         t = lback->bi_db_init( be );
1556         if ( t != 0 ) {
1557                 return t;
1558         }
1559         be->bd_info = bi;
1560
1561         return 0;
1562 }
1563
1564 /*
1565  * inits one instance of the slapd-ldap backend, stores
1566  * the private info in be_private of the arg and fills
1567  * selected fields with data from the template.
1568  *
1569  * NOTE: add checks about the other fields of the template,
1570  * which are ignored and SHOULD NOT be configured by the user.
1571  */
1572 static int
1573 ldap_chain_db_init_one(
1574         BackendDB       *be )
1575 {
1576         slap_overinst   *on = (slap_overinst *)be->bd_info;
1577         ldap_chain_t    *lc = (ldap_chain_t *)on->on_bi.bi_private;
1578
1579         BackendInfo     *bi = be->bd_info;
1580         ldapinfo_t      *li;
1581
1582         int             t;
1583
1584         be->bd_info = lback;
1585         be->be_private = NULL;
1586         t = lback->bi_db_init( be );
1587         if ( t != 0 ) {
1588                 return t;
1589         }
1590         li = (ldapinfo_t *)be->be_private;
1591
1592         /* copy common data */
1593         li->li_nretries = lc->lc_common_li->li_nretries;
1594         li->li_flags = lc->lc_common_li->li_flags;
1595         li->li_version = lc->lc_common_li->li_version;
1596         for ( t = 0; t < LDAP_BACK_OP_LAST; t++ ) {
1597                 li->li_timeout[ t ] = lc->lc_common_li->li_timeout[ t ];
1598         }
1599         be->bd_info = bi;
1600
1601         return 0;
1602 }
1603
1604 typedef struct ldap_chain_conn_apply_t {
1605         BackendDB       *be;
1606         Connection      *conn;
1607 } ldap_chain_conn_apply_t;
1608
1609 static int
1610 ldap_chain_conn_apply( void *datum, void *arg )
1611 {
1612         ldapinfo_t              *li = (ldapinfo_t *)datum;
1613         ldap_chain_conn_apply_t *lca = (ldap_chain_conn_apply_t *)arg;
1614
1615         lca->be->be_private = (void *)li;
1616
1617         return lback->bi_connection_destroy( lca->be, lca->conn );
1618 }
1619
1620 static int
1621 ldap_chain_connection_destroy(
1622         BackendDB *be,
1623         Connection *conn
1624 )
1625 {
1626         slap_overinst           *on = (slap_overinst *) be->bd_info;
1627         ldap_chain_t            *lc = (ldap_chain_t *)on->on_bi.bi_private;
1628         void                    *private = be->be_private;
1629         ldap_chain_conn_apply_t lca;
1630         int                     rc;
1631
1632         be->be_private = NULL;
1633         lca.be = be;
1634         lca.conn = conn;
1635         ldap_pvt_thread_mutex_lock( &lc->lc_lai.lai_mutex );
1636         rc = avl_apply( lc->lc_lai.lai_tree, ldap_chain_conn_apply,
1637                 (void *)&lca, 1, AVL_INORDER ) != AVL_NOMORE;
1638         ldap_pvt_thread_mutex_unlock( &lc->lc_lai.lai_mutex );
1639         be->be_private = private;
1640
1641         return rc;
1642 }
1643
1644 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1645 static int
1646 ldap_chain_parse_ctrl(
1647         Operation       *op,
1648         SlapReply       *rs,
1649         LDAPControl     *ctrl )
1650 {
1651         ber_tag_t       tag;
1652         BerElement      *ber;
1653         ber_int_t       mode,
1654                         behavior;
1655
1656         if ( get_chaining( op ) != SLAP_CONTROL_NONE ) {
1657                 rs->sr_text = "Chaining behavior control specified multiple times";
1658                 return LDAP_PROTOCOL_ERROR;
1659         }
1660
1661         if ( op->o_pagedresults != SLAP_CONTROL_NONE ) {
1662                 rs->sr_text = "Chaining behavior control specified with pagedResults control";
1663                 return LDAP_PROTOCOL_ERROR;
1664         }
1665
1666         if ( BER_BVISEMPTY( &ctrl->ldctl_value ) ) {
1667                 mode = (SLAP_CH_RESOLVE_DEFAULT|SLAP_CH_CONTINUATION_DEFAULT);
1668
1669         } else {
1670                 ber_len_t       len;
1671
1672                 /* Parse the control value
1673                  *      ChainingBehavior ::= SEQUENCE { 
1674                  *           resolveBehavior         Behavior OPTIONAL, 
1675                  *           continuationBehavior    Behavior OPTIONAL } 
1676                  *                             
1677                  *      Behavior :: = ENUMERATED { 
1678                  *           chainingPreferred       (0), 
1679                  *           chainingRequired        (1), 
1680                  *           referralsPreferred      (2), 
1681                  *           referralsRequired       (3) } 
1682                  */
1683
1684                 ber = ber_init( &ctrl->ldctl_value );
1685                 if( ber == NULL ) {
1686                         rs->sr_text = "internal error";
1687                         return LDAP_OTHER;
1688                 }
1689
1690                 tag = ber_scanf( ber, "{e" /* } */, &behavior );
1691                 /* FIXME: since the whole SEQUENCE is optional,
1692                  * should we accept no enumerations at all? */
1693                 if ( tag != LBER_ENUMERATED ) {
1694                         rs->sr_text = "Chaining behavior control: resolveBehavior decoding error";
1695                         return LDAP_PROTOCOL_ERROR;
1696                 }
1697
1698                 switch ( behavior ) {
1699                 case LDAP_CHAINING_PREFERRED:
1700                         mode = SLAP_CH_RESOLVE_CHAINING_PREFERRED;
1701                         break;
1702
1703                 case LDAP_CHAINING_REQUIRED:
1704                         mode = SLAP_CH_RESOLVE_CHAINING_REQUIRED;
1705                         break;
1706
1707                 case LDAP_REFERRALS_PREFERRED:
1708                         mode = SLAP_CH_RESOLVE_REFERRALS_PREFERRED;
1709                         break;
1710
1711                 case LDAP_REFERRALS_REQUIRED:
1712                         mode = SLAP_CH_RESOLVE_REFERRALS_REQUIRED;
1713                         break;
1714
1715                 default:
1716                         rs->sr_text = "Chaining behavior control: unknown resolveBehavior";
1717                         return LDAP_PROTOCOL_ERROR;
1718                 }
1719
1720                 tag = ber_peek_tag( ber, &len );
1721                 if ( tag == LBER_ENUMERATED ) {
1722                         tag = ber_scanf( ber, "e", &behavior );
1723                         if ( tag == LBER_ERROR ) {
1724                                 rs->sr_text = "Chaining behavior control: continuationBehavior decoding error";
1725                                 return LDAP_PROTOCOL_ERROR;
1726                         }
1727                 }
1728
1729                 if ( tag == LBER_DEFAULT ) {
1730                         mode |= SLAP_CH_CONTINUATION_DEFAULT;
1731
1732                 } else {
1733                         switch ( behavior ) {
1734                         case LDAP_CHAINING_PREFERRED:
1735                                 mode |= SLAP_CH_CONTINUATION_CHAINING_PREFERRED;
1736                                 break;
1737
1738                         case LDAP_CHAINING_REQUIRED:
1739                                 mode |= SLAP_CH_CONTINUATION_CHAINING_REQUIRED;
1740                                 break;
1741
1742                         case LDAP_REFERRALS_PREFERRED:
1743                                 mode |= SLAP_CH_CONTINUATION_REFERRALS_PREFERRED;
1744                                 break;
1745
1746                         case LDAP_REFERRALS_REQUIRED:
1747                                 mode |= SLAP_CH_CONTINUATION_REFERRALS_REQUIRED;
1748                                 break;
1749
1750                         default:
1751                                 rs->sr_text = "Chaining behavior control: unknown continuationBehavior";
1752                                 return LDAP_PROTOCOL_ERROR;
1753                         }
1754                 }
1755
1756                 if ( ( ber_scanf( ber, /* { */ "}") ) == LBER_ERROR ) {
1757                         rs->sr_text = "Chaining behavior control: decoding error";
1758                         return LDAP_PROTOCOL_ERROR;
1759                 }
1760
1761                 (void) ber_free( ber, 1 );
1762         }
1763
1764         op->o_chaining = mode | ( ctrl->ldctl_iscritical
1765                         ? SLAP_CONTROL_CRITICAL
1766                         : SLAP_CONTROL_NONCRITICAL );
1767
1768         return LDAP_SUCCESS;
1769 }
1770 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1771
1772 static slap_overinst ldapchain;
1773
1774 int
1775 chain_initialize( void )
1776 {
1777         int     rc;
1778
1779         /* Make sure we don't exceed the bits reserved for userland */
1780         config_check_userland( CH_LAST );
1781
1782 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1783         rc = register_supported_control( LDAP_CONTROL_X_CHAINING_BEHAVIOR,
1784                         /* SLAP_CTRL_GLOBAL| */ SLAP_CTRL_ACCESS|SLAP_CTRL_HIDE, NULL,
1785                         ldap_chain_parse_ctrl, &sc_chainingBehavior );
1786         if ( rc != LDAP_SUCCESS ) {
1787                 Debug( LDAP_DEBUG_ANY, "slapd-chain: "
1788                         "unable to register chaining behavior control: %d.\n",
1789                         rc, 0, 0 );
1790                 return rc;
1791         }
1792 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1793
1794         ldapchain.on_bi.bi_type = "chain";
1795         ldapchain.on_bi.bi_db_init = ldap_chain_db_init;
1796         ldapchain.on_bi.bi_db_config = ldap_chain_db_config;
1797         ldapchain.on_bi.bi_db_open = ldap_chain_db_open;
1798         ldapchain.on_bi.bi_db_close = ldap_chain_db_close;
1799         ldapchain.on_bi.bi_db_destroy = ldap_chain_db_destroy;
1800
1801         ldapchain.on_bi.bi_connection_destroy = ldap_chain_connection_destroy;
1802
1803         ldapchain.on_response = ldap_chain_response;
1804
1805         ldapchain.on_bi.bi_cf_ocs = chainocs;
1806
1807         rc = config_register_schema( chaincfg, chainocs );
1808         if ( rc ) {
1809                 return rc;
1810         }
1811
1812         return overlay_register( &ldapchain );
1813 }
1814