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