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