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