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