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