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