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