]> git.sur5r.net Git - openldap/blob - servers/slapd/back-ldap/chain.c
d02e039c4f3c8a58a66ce74bcc8584bbbde2a959
[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-2005 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  */
21
22 #include "portable.h"
23
24 #include <stdio.h>
25
26 #include <ac/string.h>
27 #include <ac/socket.h>
28
29 #include "slap.h"
30 #include "back-ldap.h"
31
32 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
33 #define SLAP_CH_RESOLVE_SHIFT                           SLAP_CONTROL_SHIFT
34 #define SLAP_CH_RESOLVE_MASK                            (0x3 << SLAP_CH_RESOLVE_SHIFT)
35 #define SLAP_CH_RESOLVE_CHAINING_PREFERRED              (LDAP_CHAINING_PREFERRED << SLAP_CH_RESOLVE_SHIFT)
36 #define SLAP_CH_RESOLVE_CHAINING_REQUIRED               (LDAP_CHAINING_REQUIRED << SLAP_CH_RESOLVE_SHIFT)
37 #define SLAP_CH_RESOLVE_REFERRALS_PREFERRED             (LDAP_REFERRALS_PREFERRED << SLAP_CH_RESOLVE_SHIFT)
38 #define SLAP_CH_RESOLVE_REFERRALS_REQUIRED              (LDAP_REFERRALS_REQUIRED << SLAP_CH_RESOLVE_SHIFT)
39 #define SLAP_CH_RESOLVE_DEFAULT                         SLAP_CH_RESOLVE_CHAINING_PREFERRED
40 #define SLAP_CH_CONTINUATION_SHIFT                      (SLAP_CH_RESOLVE_SHIFT + 2)
41 #define SLAP_CH_CONTINUATION_MASK                       (0x3 << SLAP_CH_CONTINUATION_SHIFT)
42 #define SLAP_CH_CONTINUATION_CHAINING_PREFERRED         (LDAP_CHAINING_PREFERRED << SLAP_CH_CONTINUATION_SHIFT)
43 #define SLAP_CH_CONTINUATION_CHAINING_REQUIRED          (LDAP_CHAINING_REQUIRED << SLAP_CH_CONTINUATION_SHIFT)
44 #define SLAP_CH_CONTINUATION_REFERRALS_PREFERRED        (LDAP_REFERRALS_PREFERRED << SLAP_CH_CONTINUATION_SHIFT)
45 #define SLAP_CH_CONTINUATION_REFERRALS_REQUIRED         (LDAP_REFERRALS_REQUIRED << SLAP_CH_CONTINUATION_SHIFT)
46 #define SLAP_CH_CONTINUATION_DEFAULT                    SLAP_CH_CONTINUATION_CHAINING_PREFERRED
47
48 #define o_chaining                      o_ctrlflag[sc_chainingBehavior]
49 #define get_chaining(op)                ((op)->o_chaining & SLAP_CONTROL_MASK)
50 #define get_chainingBehavior(op)        ((op)->o_chaining & (SLAP_CH_RESOLVE_MASK|SLAP_CH_CONTINUATION_MASK))
51 #define get_resolveBehavior(op)         ((op)->o_chaining & SLAP_CH_RESOLVE_MASK)
52 #define get_continuationBehavior(op)    ((op)->o_chaining & SLAP_CH_CONTINUATION_MASK)
53 #endif /*  LDAP_CONTROL_X_CHAINING_BEHAVIOR */
54
55 #define LDAP_CH_NONE                    ((void *)(0))
56 #define LDAP_CH_RES                     ((void *)(1))
57 #define LDAP_CH_ERR                     ((void *)(2))
58
59 static int              sc_chainingBehavior;
60 static BackendInfo      *lback;
61
62 typedef struct ldap_chain_t {
63         struct ldapinfo         *lc_li;
64         unsigned                lc_flags;
65 #define LDAP_CHAIN_F_NONE               0x00U
66 #define LDAP_CHAIN_F_CHAINING           0x01U
67         LDAPControl             lc_chaining_ctrl;
68
69 } ldap_chain_t;
70
71 static int
72 ldap_chain_operational( Operation *op, SlapReply *rs )
73 {
74         /* Trap entries generated by back-ldap.
75          * 
76          * FIXME: we need a better way to recognize them; a cleaner
77          * solution would be to be able to intercept the response
78          * of be_operational(), so that we can divert only those
79          * calls that fail because operational attributes were
80          * requested for entries that do not belong to the underlying
81          * database.  This fix is likely to intercept also entries
82          * generated by back-perl and so. */
83         if ( rs->sr_entry->e_private == NULL ) {
84                 return 0;
85         }
86
87         return SLAP_CB_CONTINUE;
88 }
89
90 /*
91  * Search specific response that strips entryDN from entries
92  */
93 static int
94 ldap_chain_cb_search_response( Operation *op, SlapReply *rs )
95 {
96         assert( op->o_tag == LDAP_REQ_SEARCH );
97
98         /* if in error, don't proceed any further */
99         if ( op->o_callback->sc_private == LDAP_CH_ERR ) {
100                 return 0;
101         }
102
103         if ( rs->sr_type == REP_SEARCH ) {
104                 Attribute       **ap = &rs->sr_entry->e_attrs;
105
106                 for ( ; *ap != NULL; ap = &(*ap)->a_next ) {
107                         /* will be generated later by frontend
108                          * (a cleaner solution would be that
109                          * the frontend checks if it already exists */
110                         if ( ad_cmp( (*ap)->a_desc, slap_schema.si_ad_entryDN ) == 0 )
111                         {
112                                 Attribute *a = *ap;
113
114                                 *ap = (*ap)->a_next;
115                                 attr_free( a );
116
117                                 /* there SHOULD be one only! */
118                                 break;
119                         }
120                 }
121                 
122                 return SLAP_CB_CONTINUE;
123
124         } else if ( rs->sr_type == REP_SEARCHREF ) {
125                 /* if we get it here, it means the library was unable
126                  * to chase the referral... */
127
128 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
129                 if ( get_chaining( op ) > SLAP_CONTROL_IGNORED ) {
130                         switch ( get_continuationBehavior( op ) ) {
131                         case SLAP_CH_RESOLVE_CHAINING_REQUIRED:
132                                 op->o_callback->sc_private = LDAP_CH_ERR;
133                                 return -1;
134
135                         default:
136                                 break;
137                         }
138                 }
139 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
140                 return SLAP_CB_CONTINUE;
141
142         } else if ( rs->sr_type == REP_RESULT ) {
143                 /* back-ldap tried to send result */
144                 op->o_callback->sc_private = LDAP_CH_RES;
145         }
146
147         return 0;
148 }
149
150 /*
151  * Dummy response that simply traces if back-ldap tried to send 
152  * anything to the client
153  */
154 static int
155 ldap_chain_cb_response( Operation *op, SlapReply *rs )
156 {
157         /* if in error, don't proceed any further */
158         if ( op->o_callback->sc_private == LDAP_CH_ERR ) {
159                 return 0;
160         }
161
162         if ( rs->sr_type == REP_RESULT ) {
163                 op->o_callback->sc_private = LDAP_CH_RES;
164
165         } else if ( op->o_tag == LDAP_REQ_SEARCH && rs->sr_type == REP_SEARCH )
166         {
167                 /* strip the entryDN attribute, but keep returning results */
168                 (void)ldap_chain_cb_search_response( op, rs );
169         }
170
171         return SLAP_CB_CONTINUE;
172 }
173
174 static int
175 ldap_chain_op(
176         Operation       *op,
177         SlapReply       *rs,
178         int             ( *op_f )( Operation *op, SlapReply *rs ), 
179         BerVarray       ref )
180 {
181         slap_overinst   *on = (slap_overinst *) op->o_bd->bd_info;
182         ldap_chain_t    *lc = (ldap_chain_t *)on->on_bi.bi_private;
183         struct ldapinfo li, *lip = lc->lc_li;
184
185         /* NOTE: returned if ref is empty... */
186         int             rc = LDAP_OTHER;
187
188         if ( lip->url != NULL ) {
189                 op->o_bd->be_private = lip;
190                 return ( *op_f )( op, rs );
191         }
192
193         li = *lip;
194         op->o_bd->be_private = &li;
195
196         /* if we parse the URI then by no means 
197          * we can cache stuff or reuse connections, 
198          * because in back-ldap there's no caching
199          * based on the URI value, which is supposed
200          * to be set once for all (correct?) */
201         op->o_do_not_cache = 1;
202
203         for ( ; !BER_BVISNULL( ref ); ref++ ) {
204                 LDAPURLDesc     *srv;
205                 char            *save_dn;
206                         
207                 /* We're setting the URI of the first referral;
208                  * what if there are more?
209
210 Document: draft-ietf-ldapbis-protocol-27.txt
211
212 4.1.10. Referral 
213    ...
214    If the client wishes to progress the operation, it MUST follow the 
215    referral by contacting one of the supported services. If multiple 
216    URIs are present, the client assumes that any supported URI may be 
217    used to progress the operation. 
218
219                  * so we actually need to follow exactly one,
220                  * and we can assume any is fine.
221                  */
222         
223                 /* parse reference and use 
224                  * proto://[host][:port]/ only */
225                 rc = ldap_url_parse_ext( ref->bv_val, &srv );
226                 if ( rc != LDAP_URL_SUCCESS ) {
227                         /* try next */
228                         rc = LDAP_OTHER;
229                         continue;
230                 }
231
232                 /* remove DN essentially because later on 
233                  * ldap_initialize() will parse the URL 
234                  * as a comma-separated URL list */
235                 save_dn = srv->lud_dn;
236                 srv->lud_dn = "";
237                 srv->lud_scope = LDAP_SCOPE_DEFAULT;
238                 li.url = ldap_url_desc2str( srv );
239                 srv->lud_dn = save_dn;
240                 ldap_free_urldesc( srv );
241
242                 if ( li.url == NULL ) {
243                         /* try next */
244                         rc = LDAP_OTHER;
245                         continue;
246                 }
247
248                 rc = ( *op_f )( op, rs );
249
250                 ldap_memfree( li.url );
251                 li.url = NULL;
252                 
253                 if ( rc == LDAP_SUCCESS && rs->sr_err == LDAP_SUCCESS ) {
254                         break;
255                 }
256         }
257
258         return rc;
259 }
260
261 static int
262 ldap_chain_response( Operation *op, SlapReply *rs )
263 {
264         slap_overinst   *on = (slap_overinst *) op->o_bd->bd_info;
265         void            *private = op->o_bd->be_private;
266         slap_callback   *sc = op->o_callback,
267                         sc2 = { 0 };
268         int             rc = 0;
269         int             cache = op->o_do_not_cache;
270         BerVarray       ref;
271         struct berval   ndn = op->o_ndn;
272
273         ldap_chain_t    *lc = (ldap_chain_t *)on->on_bi.bi_private;
274         struct ldapinfo li, *lip = lc->lc_li;
275
276 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
277         int             sr_err = rs->sr_err;
278         slap_reply_t    sr_type = rs->sr_type;
279         slap_mask_t     chain_mask = 0;
280         ber_len_t       chain_shift = 0;
281 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
282
283         if ( rs->sr_err != LDAP_REFERRAL && rs->sr_type != REP_SEARCHREF ) {
284                 return SLAP_CB_CONTINUE;
285         }
286
287 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
288         if ( rs->sr_err == LDAP_REFERRAL && get_chaining( op ) > SLAP_CONTROL_IGNORED ) {
289                 switch ( get_resolveBehavior( op ) ) {
290                 case SLAP_CH_RESOLVE_REFERRALS_PREFERRED:
291                 case SLAP_CH_RESOLVE_REFERRALS_REQUIRED:
292                         return SLAP_CB_CONTINUE;
293
294                 default:
295                         chain_mask = SLAP_CH_RESOLVE_MASK;
296                         chain_shift = SLAP_CH_RESOLVE_SHIFT;
297                         break;
298                 }
299
300         } else if ( rs->sr_type == REP_SEARCHREF && get_chaining( op ) > SLAP_CONTROL_IGNORED ) {
301                 switch ( get_continuationBehavior( op ) ) {
302                 case SLAP_CH_CONTINUATION_REFERRALS_PREFERRED:
303                 case SLAP_CH_CONTINUATION_REFERRALS_REQUIRED:
304                         return SLAP_CB_CONTINUE;
305
306                 default:
307                         chain_mask = SLAP_CH_CONTINUATION_MASK;
308                         chain_shift = SLAP_CH_CONTINUATION_SHIFT;
309                         break;
310                 }
311         }
312 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
313
314         /*
315          * TODO: add checks on who/when chain operations; e.g.:
316          *   a) what identities are authorized
317          *   b) what request DN (e.g. only chain requests rooted at <DN>)
318          *   c) what referral URIs
319          *   d) what protocol scheme (e.g. only ldaps://)
320          *   e) what ssf
321          */
322
323         ref = rs->sr_ref;
324         rs->sr_ref = NULL;
325
326         /* we need this to know if back-ldap returned any result */
327         sc2.sc_response = ldap_chain_cb_response;
328         op->o_callback = &sc2;
329
330         /* Chaining can be performed by a privileged user on behalf
331          * of normal users, using the ProxyAuthz control, by exploiting
332          * the identity assertion feature of back-ldap; see idassert-*
333          * directives in slapd-ldap(5).
334          *
335          * FIXME: the idassert-authcDN is one, will it be fine regardless
336          * of the URI we obtain from the referral?
337          */
338
339         switch ( op->o_tag ) {
340         case LDAP_REQ_BIND: {
341                 struct berval   rndn = op->o_req_ndn;
342                 Connection      *conn = op->o_conn;
343
344                 /* FIXME: can we really get a referral for binds? */
345                 op->o_req_ndn = slap_empty_bv;
346                 op->o_conn = NULL;
347                 rc = ldap_chain_op( op, rs, lback->bi_op_bind, ref );
348                 op->o_req_ndn = rndn;
349                 op->o_conn = conn;
350                 }
351                 break;
352         case LDAP_REQ_ADD:
353                 {
354                 int             cleanup_attrs = 0;
355
356                 if ( op->ora_e->e_attrs == NULL ) {
357                         char            textbuf[ SLAP_TEXT_BUFLEN ];
358                         size_t          textlen = sizeof( textbuf );
359
360 #if 0
361                         /* FIXME: op->o_bd is still set to the BackendDB 
362                          * structure of the database that tried to handle
363                          * the operation and actually returned a referral
364                          * ... */
365                         assert( SLAP_DBFLAGS( op->o_bd ) & SLAP_DBFLAG_GLOBAL_OVERLAY );
366 #endif
367
368                         /* global overlay: create entry */
369                         /* NOTE: this is a hack to use the chain overlay
370                          * as global.  I expect to be able to remove this
371                          * soon by using slap_mods2entry() earlier in
372                          * do_add(), adding the operational attrs later
373                          * if required. */
374                         rs->sr_err = slap_mods2entry( op->ora_modlist,
375                                         &op->ora_e, 0, 1,
376                                         &rs->sr_text, textbuf, textlen );
377                         if ( rs->sr_err != LDAP_SUCCESS ) {
378                                 send_ldap_result( op, rs );
379                                 rc = 1;
380                                 break;
381                         }
382                 }
383                 rc = ldap_chain_op( op, rs, lback->bi_op_add, ref );
384                 if ( cleanup_attrs ) {
385                         attrs_free( op->ora_e->e_attrs );
386                         op->ora_e->e_attrs = NULL;
387                 }
388                 break;
389                 }
390         case LDAP_REQ_DELETE:
391                 rc = ldap_chain_op( op, rs, lback->bi_op_delete, ref );
392                 break;
393         case LDAP_REQ_MODRDN:
394                 rc = ldap_chain_op( op, rs, lback->bi_op_modrdn, ref );
395                 break;
396         case LDAP_REQ_MODIFY:
397                 rc = ldap_chain_op( op, rs, lback->bi_op_modify, ref );
398                 break;
399         case LDAP_REQ_COMPARE:
400                 rc = ldap_chain_op( op, rs, lback->bi_op_compare, ref );
401                 break;
402         case LDAP_REQ_SEARCH:
403                 if ( rs->sr_type == REP_SEARCHREF ) {
404                         struct berval   *curr = ref,
405                                         odn = op->o_req_dn,
406                                         ondn = op->o_req_ndn;
407
408                         rs->sr_type = REP_SEARCH;
409
410                         sc2.sc_response = ldap_chain_cb_search_response;
411
412                         li = *lip;
413                         li.url = NULL;
414                         op->o_bd->be_private = &li;
415                         
416                         /* if we parse the URI then by no means 
417                          * we can cache stuff or reuse connections, 
418                          * because in back-ldap there's no caching
419                          * based on the URI value, which is supposed
420                          * to be set once for all (correct?) */
421                         op->o_do_not_cache = 1;
422
423                         /* copy the private info because we need to modify it */
424                         for ( ; !BER_BVISNULL( &curr[0] ); curr++ ) {
425                                 LDAPURLDesc     *srv;
426                                 char            *save_dn;
427
428                                 /* parse reference and use
429                                  * proto://[host][:port]/ only */
430                                 rc = ldap_url_parse_ext( curr[0].bv_val, &srv );
431                                 if ( rc != LDAP_URL_SUCCESS ) {
432                                         /* try next */
433                                         rs->sr_err = LDAP_OTHER;
434                                         continue;
435                                 }
436
437                                 /* remove DN essentially because later on 
438                                  * ldap_initialize() will parse the URL 
439                                  * as a comma-separated URL list */
440                                 save_dn = srv->lud_dn;
441                                 srv->lud_dn = "";
442                                 srv->lud_scope = LDAP_SCOPE_DEFAULT;
443                                 li.url = ldap_url_desc2str( srv );
444                                 if ( li.url != NULL ) {
445                                         ber_str2bv_x( save_dn, 0, 1, &op->o_req_dn,
446                                                         op->o_tmpmemctx );
447                                         ber_dupbv_x( &op->o_req_ndn, &op->o_req_dn,
448                                                         op->o_tmpmemctx );
449                                 }
450
451                                 srv->lud_dn = save_dn;
452                                 ldap_free_urldesc( srv );
453
454                                 if ( li.url == NULL ) {
455                                         /* try next */
456                                         rs->sr_err = LDAP_OTHER;
457                                         continue;
458                                 }
459
460
461                                 /* FIXME: should we also copy filter and scope?
462                                  * according to RFC3296, no */
463                                 rc = lback->bi_op_search( op, rs );
464
465                                 ldap_memfree( li.url );
466                                 li.url = NULL;
467
468                                 op->o_tmpfree( op->o_req_dn.bv_val,
469                                                 op->o_tmpmemctx );
470                                 op->o_tmpfree( op->o_req_ndn.bv_val,
471                                                 op->o_tmpmemctx );
472
473                                 if ( rc == LDAP_SUCCESS && rs->sr_err == LDAP_SUCCESS ) {
474                                         break;
475                                 }
476
477                                 rc = rs->sr_err;
478                         }
479
480                         op->o_req_dn = odn;
481                         op->o_req_ndn = ondn;
482                         rs->sr_type = REP_SEARCHREF;
483                         rs->sr_entry = NULL;
484
485                         if ( rc != LDAP_SUCCESS ) {
486                                 /* couldn't chase any of the referrals */
487                                 rc = SLAP_CB_CONTINUE;
488                         }
489                         
490                 } else {
491                         rc = ldap_chain_op( op, rs, lback->bi_op_search, ref );
492                 }
493                 break;
494         case LDAP_REQ_EXTENDED:
495                 rc = ldap_chain_op( op, rs, lback->bi_extended, ref );
496                 /* FIXME: ldap_back_extended() by design 
497                  * doesn't send result; frontend is expected
498                  * to send it... */
499                 if ( rc != SLAPD_ABANDON ) {
500                         send_ldap_extended( op, rs );
501                         rc = LDAP_SUCCESS;
502                 }
503                 break;
504         default:
505                 rc = SLAP_CB_CONTINUE;
506                 break;
507         }
508
509 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
510         if ( rc != LDAP_SUCCESS || sc2.sc_private == LDAP_CH_ERR ) {
511                 if ( rs->sr_err == LDAP_CANNOT_CHAIN ) {
512                         goto cannot_chain;
513                 }
514
515                 switch ( ( get_chainingBehavior( op ) & chain_mask ) >> chain_shift ) {
516                 case LDAP_CHAINING_REQUIRED:
517 cannot_chain:;
518                         op->o_callback = NULL;
519                         send_ldap_error( op, rs, LDAP_CANNOT_CHAIN, "operation cannot be completed without chaining" );
520                         break;
521
522                 default:
523                         rc = SLAP_CB_CONTINUE;
524                         rs->sr_err = sr_err;
525                         rs->sr_type = sr_type;
526                         break;
527                 }
528                 goto dont_chain;
529         }
530 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
531
532         if ( sc2.sc_private == LDAP_CH_NONE ) {
533                 op->o_callback = NULL;
534                 rc = rs->sr_err = slap_map_api2result( rs );
535                 send_ldap_result( op, rs );
536         }
537
538 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
539 dont_chain:;
540 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
541         op->o_do_not_cache = cache;
542         op->o_bd->be_private = private;
543         op->o_callback = sc;
544         op->o_ndn = ndn;
545         rs->sr_ref = ref;
546
547         return rc;
548 }
549
550 static int
551 str2chain( const char *s )
552 {
553         if ( strcasecmp( s, "chainingPreferred" ) == 0 ) {
554                 return LDAP_CHAINING_PREFERRED;
555                 
556         } else if ( strcasecmp( s, "chainingRequired" ) == 0 ) {
557                 return LDAP_CHAINING_REQUIRED;
558
559         } else if ( strcasecmp( s, "referralsPreferred" ) == 0 ) {
560                 return LDAP_REFERRALS_PREFERRED;
561                 
562         } else if ( strcasecmp( s, "referralsRequired" ) == 0 ) {
563                 return LDAP_REFERRALS_REQUIRED;
564         }
565
566         return -1;
567 }
568
569 static int
570 ldap_chain_db_config(
571         BackendDB       *be,
572         const char      *fname,
573         int             lineno,
574         int             argc,
575         char    **argv
576 )
577 {
578         slap_overinst   *on = (slap_overinst *) be->bd_info;
579         ldap_chain_t    *lc = (ldap_chain_t *)on->on_bi.bi_private;
580         void            *private = be->be_private;
581         char            *argv0 = NULL;
582         int             rc;
583
584         be->be_private = lc->lc_li;
585         if ( strncasecmp( argv[ 0 ], "chain-", STRLENOF( "chain-" ) ) == 0 ) {
586                 argv0 = argv[ 0 ];
587                 argv[ 0 ] = &argv[ 0 ][ STRLENOF( "chain-" ) ];
588
589                 if ( strcasecmp( argv0, "chaining" ) == 0 ) {
590                         BerElementBuffer        berbuf;
591                         BerElement              *ber = (BerElement *)&berbuf;
592                         int                     resolve = -1,
593                                                 continuation = -1,
594                                                 iscritical = 0;
595
596                         for ( argc--, argv++; argc > 0; argc--, argv++ ) {
597                                 if ( strncasecmp( argv[0], "resolve=", STRLENOF( "resolve=" ) ) == 0 ) {
598                                         resolve = str2chain( argv[ 0 ] + STRLENOF( "resolve=" ) );
599                                         if ( resolve == -1 ) {
600                                                 fprintf( stderr, "%s line %d: "
601                                                         "illegal <resolve> value %s "
602                                                         "in \"chain-chaining>\"\n",
603                                                         fname, lineno, argv[ 0 ] );
604                                                 return 1;
605                                         }
606
607                                 } else if ( strncasecmp( argv[0], "continuation=", STRLENOF( "continuation=" ) ) == 0 ) {
608                                         continuation = str2chain( argv[ 0 ] + STRLENOF( "continuation=" ) );
609                                         if ( continuation == -1 ) {
610                                                 fprintf( stderr, "%s line %d: "
611                                                         "illegal <continuation> value %s "
612                                                         "in \"chain-chaining\"\n",
613                                                         fname, lineno, argv[ 0 ] );
614                                                 return 1;
615                                         }
616
617                                 } else if ( strcasecmp( argv[ 0 ], "critical" ) == 0 ) {
618                                         iscritical = 1;
619
620                                 } else {
621                                         fprintf( stderr, "%s line %d: "
622                                                 "unknown option in \"chain-chaining\"\n",
623                                                 fname, lineno );
624                                         return 1;
625                                 }
626                         }
627
628                         if ( resolve != -1 || continuation != -1 ) {
629                                 int     err;
630
631                                 if ( resolve == -1 ) {
632                                         /* default */
633                                         resolve = LDAP_CHAINING_PREFERRED;
634                                 }
635
636                                 ber_init2( ber, NULL, LBER_USE_DER );
637
638                                 err = ber_printf( ber, "{e" /* } */, resolve );
639                                 if ( err == -1 ) {
640                                         ber_free( ber, 1 );
641                                         fprintf( stderr, "%s line %d: "
642                                                 "chaining behavior control encoding error!\n",
643                                                 fname, lineno );
644                                         return 1;
645                                 }
646
647                                 if ( continuation > -1 ) {
648                                         err = ber_printf( ber, "e", continuation );
649                                         if ( err == -1 ) {
650                                                 ber_free( ber, 1 );
651                                                 fprintf( stderr, "%s line %d: "
652                                                         "chaining behavior control encoding error!\n",
653                                                         fname, lineno );
654                                                 return 1;
655                                         }
656                                 }
657
658                                 err = ber_printf( ber, /* { */ "N}" );
659                                 if ( err == -1 ) {
660                                         ber_free( ber, 1 );
661                                         fprintf( stderr, "%s line %d: "
662                                                 "chaining behavior control encoding error!\n",
663                                                 fname, lineno );
664                                         return 1;
665                                 }
666
667                                 if ( ber_flatten2( ber, &lc->lc_chaining_ctrl.ldctl_value, 0 ) == -1 ) {
668                                         exit( EXIT_FAILURE );
669                                 }
670
671                         } else {
672                                 BER_BVZERO( &lc->lc_chaining_ctrl.ldctl_value );
673                         }
674
675                         lc->lc_chaining_ctrl.ldctl_oid = LDAP_CONTROL_X_CHAINING_BEHAVIOR;
676                         lc->lc_chaining_ctrl.ldctl_iscritical = iscritical;
677
678                         lc->lc_flags |= LDAP_CHAIN_F_CHAINING;
679
680                         return 0;
681                 }
682         }
683
684
685         rc = lback->bi_db_config( be, fname, lineno, argc, argv );
686         be->be_private = private;
687
688         if ( argv0 ) {
689                 argv[ 0 ] = argv0;
690         }
691
692         return rc;
693 }
694
695 static int
696 ldap_chain_db_init(
697         BackendDB *be
698 )
699 {
700         slap_overinst   *on = (slap_overinst *)be->bd_info;
701         ldap_chain_t    *lc = NULL;
702         int             rc;
703         BackendDB       bd = *be;
704
705         if ( lback == NULL ) {
706                 lback = backend_info( "ldap" );
707
708                 if ( lback == NULL ) {
709                         return -1;
710                 }
711         }
712
713         lc = ch_malloc( sizeof( ldap_chain_t ) );
714         memset( lc, 0, sizeof( ldap_chain_t ) );
715
716         bd.be_private = NULL;
717         rc = lback->bi_db_init( &bd );
718         lc->lc_li = (struct ldapinfo *)bd.be_private;
719         on->on_bi.bi_private = (void *)lc;
720
721         return rc;
722 }
723
724 static int
725 ldap_chain_db_open(
726         BackendDB *be
727 )
728 {
729         int     rc = 0;
730
731 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
732         rc = overlay_register_control( be, LDAP_CONTROL_X_CHAINING_BEHAVIOR );
733 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
734
735         return rc;
736 }
737
738 static int
739 ldap_chain_db_destroy(
740         BackendDB *be
741 )
742 {
743         slap_overinst   *on = (slap_overinst *) be->bd_info;
744         ldap_chain_t    *lc = (ldap_chain_t *)on->on_bi.bi_private;
745         void            *private = be->be_private;
746         int             rc;
747
748         be->be_private = (void *)lc->lc_li;
749         rc = lback->bi_db_destroy( be );
750         lc->lc_li = be->be_private;
751         ch_free( lc );
752         on->on_bi.bi_private = NULL;
753         be->be_private = private;
754         return rc;
755 }
756
757 static int
758 ldap_chain_connection_destroy(
759         BackendDB *be,
760         Connection *conn
761 )
762 {
763         slap_overinst   *on = (slap_overinst *) be->bd_info;
764         ldap_chain_t    *lc = (ldap_chain_t *)on->on_bi.bi_private;
765         void            *private = be->be_private;
766         int             rc;
767
768         be->be_private = (void *)lc->lc_li;
769         rc = lback->bi_connection_destroy( be, conn );
770         be->be_private = private;
771
772         return rc;
773 }
774
775 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
776 static int
777 ldap_chain_parse_ctrl(
778         Operation       *op,
779         SlapReply       *rs,
780         LDAPControl     *ctrl )
781 {
782         ber_tag_t       tag;
783         BerElement      *ber;
784         ber_int_t       mode,
785                         behavior;
786
787         if ( get_chaining( op ) != SLAP_CONTROL_NONE ) {
788                 rs->sr_text = "Chaining behavior control specified multiple times";
789                 return LDAP_PROTOCOL_ERROR;
790         }
791
792         if ( op->o_pagedresults != SLAP_CONTROL_NONE ) {
793                 rs->sr_text = "Chaining behavior control specified with pagedResults control";
794                 return LDAP_PROTOCOL_ERROR;
795         }
796
797         if ( BER_BVISEMPTY( &ctrl->ldctl_value ) ) {
798                 mode = (SLAP_CH_RESOLVE_DEFAULT|SLAP_CH_CONTINUATION_DEFAULT);
799
800         } else {
801                 ber_len_t       len;
802
803                 /* Parse the control value
804                  *      ChainingBehavior ::= SEQUENCE { 
805                  *           resolveBehavior         Behavior OPTIONAL, 
806                  *           continuationBehavior    Behavior OPTIONAL } 
807                  *                             
808                  *      Behavior :: = ENUMERATED { 
809                  *           chainingPreferred       (0), 
810                  *           chainingRequired        (1), 
811                  *           referralsPreferred      (2), 
812                  *           referralsRequired       (3) } 
813                  */
814
815                 ber = ber_init( &ctrl->ldctl_value );
816                 if( ber == NULL ) {
817                         rs->sr_text = "internal error";
818                         return LDAP_OTHER;
819                 }
820
821                 tag = ber_scanf( ber, "{e" /* } */, &behavior );
822                 /* FIXME: since the whole SEQUENCE is optional,
823                  * should we accept no enumerations at all? */
824                 if ( tag != LBER_ENUMERATED ) {
825                         rs->sr_text = "Chaining behavior control: resolveBehavior decoding error";
826                         return LDAP_PROTOCOL_ERROR;
827                 }
828
829                 switch ( behavior ) {
830                 case LDAP_CHAINING_PREFERRED:
831                         mode = SLAP_CH_RESOLVE_CHAINING_PREFERRED;
832                         break;
833
834                 case LDAP_CHAINING_REQUIRED:
835                         mode = SLAP_CH_RESOLVE_CHAINING_REQUIRED;
836                         break;
837
838                 case LDAP_REFERRALS_PREFERRED:
839                         mode = SLAP_CH_RESOLVE_REFERRALS_PREFERRED;
840                         break;
841
842                 case LDAP_REFERRALS_REQUIRED:
843                         mode = SLAP_CH_RESOLVE_REFERRALS_REQUIRED;
844                         break;
845
846                 default:
847                         rs->sr_text = "Chaining behavior control: unknown resolveBehavior";
848                         return LDAP_PROTOCOL_ERROR;
849                 }
850
851                 tag = ber_peek_tag( ber, &len );
852                 if ( tag == LBER_ENUMERATED ) {
853                         tag = ber_scanf( ber, "e", &behavior );
854                         if ( tag == LBER_ERROR ) {
855                                 rs->sr_text = "Chaining behavior control: continuationBehavior decoding error";
856                                 return LDAP_PROTOCOL_ERROR;
857                         }
858                 }
859
860                 if ( tag == LBER_DEFAULT ) {
861                         mode |= SLAP_CH_CONTINUATION_DEFAULT;
862
863                 } else {
864                         switch ( behavior ) {
865                         case LDAP_CHAINING_PREFERRED:
866                                 mode |= SLAP_CH_CONTINUATION_CHAINING_PREFERRED;
867                                 break;
868
869                         case LDAP_CHAINING_REQUIRED:
870                                 mode |= SLAP_CH_CONTINUATION_CHAINING_REQUIRED;
871                                 break;
872
873                         case LDAP_REFERRALS_PREFERRED:
874                                 mode |= SLAP_CH_CONTINUATION_REFERRALS_PREFERRED;
875                                 break;
876
877                         case LDAP_REFERRALS_REQUIRED:
878                                 mode |= SLAP_CH_CONTINUATION_REFERRALS_REQUIRED;
879                                 break;
880
881                         default:
882                                 rs->sr_text = "Chaining behavior control: unknown continuationBehavior";
883                                 return LDAP_PROTOCOL_ERROR;
884                         }
885                 }
886
887                 if ( ( ber_scanf( ber, /* { */ "}") ) == LBER_ERROR ) {
888                         rs->sr_text = "Chaining behavior control: decoding error";
889                         return LDAP_PROTOCOL_ERROR;
890                 }
891
892                 (void) ber_free( ber, 1 );
893         }
894
895         op->o_chaining = mode | ( ctrl->ldctl_iscritical
896                         ? SLAP_CONTROL_CRITICAL
897                         : SLAP_CONTROL_NONCRITICAL );
898
899         return LDAP_SUCCESS;
900 }
901 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
902
903 static slap_overinst ldapchain;
904
905 int
906 chain_init( void )
907 {
908 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
909         int     rc;
910
911         rc = register_supported_control( LDAP_CONTROL_X_CHAINING_BEHAVIOR,
912                         /* SLAP_CTRL_GLOBAL| */ SLAP_CTRL_ACCESS|SLAP_CTRL_HIDE, NULL,
913                         ldap_chain_parse_ctrl, &sc_chainingBehavior );
914         if ( rc != LDAP_SUCCESS ) {
915                 fprintf( stderr, "Failed to register chaining behavior control: %d\n", rc );
916                 return rc;
917         }
918 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
919
920         ldapchain.on_bi.bi_type = "chain";
921         ldapchain.on_bi.bi_db_init = ldap_chain_db_init;
922         ldapchain.on_bi.bi_db_open = ldap_chain_db_open;
923         ldapchain.on_bi.bi_db_config = ldap_chain_db_config;
924         ldapchain.on_bi.bi_db_destroy = ldap_chain_db_destroy;
925
926         /* ... otherwise the underlying backend's function would be called,
927          * likely passing an invalid entry; on the contrary, the requested
928          * operational attributes should have been returned while chasing
929          * the referrals.  This all in all is a bit messy, because part
930          * of the operational attributes are generated by the backend;
931          * part by the frontend; back-ldap should receive all the available
932          * ones from the remote server, but then, on its own, it strips those
933          * it assumes will be (re)generated by the frontend (e.g.
934          * subschemaSubentry.) */
935         ldapchain.on_bi.bi_operational = ldap_chain_operational;
936         
937         ldapchain.on_bi.bi_connection_destroy = ldap_chain_connection_destroy;
938
939         ldapchain.on_response = ldap_chain_response;
940
941         return overlay_register( &ldapchain );
942 }
943