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