]> git.sur5r.net Git - openldap/blob - servers/slapd/overlays/chain.c
- consistently honor multiple referrals
[openldap] / servers / slapd / overlays / 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-2004 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 #if defined(SLAPD_LDAP) 
25
26 #ifdef SLAPD_OVER_CHAIN
27
28 #include <stdio.h>
29
30 #include <ac/string.h>
31 #include <ac/socket.h>
32
33 #include "slap.h"
34 #include "../back-ldap/back-ldap.h"
35
36 static BackendInfo *lback;
37
38 static int
39 ldap_chain_chk_referrals( Operation *op, SlapReply *rs )
40 {
41         return LDAP_SUCCESS;
42 }
43
44 static int
45 ldap_chain_operational( Operation *op, SlapReply *rs )
46 {
47         /* trap entries generated by back-ldap.
48          * FIXME: we need a better way to recognize them; a cleaner
49          * solution would be to be able to intercept the response
50          * of be_operational(), so that we can divert only those
51          * calls that fail because operational attributes were
52          * requested for entries that do not belong to the underlying
53          * database.  This fix is likely to intercept also entries
54          * generated by back-perl and so. */
55         if ( rs->sr_entry->e_private == NULL ) {
56                 return 0;
57         }
58
59         return SLAP_CB_CONTINUE;
60 }
61
62 static int
63 ldap_chain_cb_response( Operation *op, SlapReply *rs )
64 {
65         assert( op->o_tag == LDAP_REQ_SEARCH );
66
67         if ( rs->sr_type == REP_SEARCH ) {
68                 Attribute       **ap = &rs->sr_entry->e_attrs;
69
70                 for ( ; *ap != NULL; ap = &(*ap)->a_next ) {
71                         /* will be generated later by frontend
72                          * (a cleaner solution would be that
73                          * the frontend checks if it already exists */
74                         if ( ad_cmp( (*ap)->a_desc, slap_schema.si_ad_entryDN ) == 0 )
75                         {
76                                 Attribute *a = *ap;
77
78                                 *ap = (*ap)->a_next;
79                                 attr_free( a );
80
81                                 /* there SHOULD be one only! */
82                                 break;
83                         }
84                 }
85                 
86                 return SLAP_CB_CONTINUE;
87         }
88
89         return 0;
90 }
91
92 static int
93 ldap_chain_response( Operation *op, SlapReply *rs )
94 {
95         slap_overinst   *on = (slap_overinst *) op->o_bd->bd_info;
96         void            *private = op->o_bd->be_private;
97         slap_callback   *sc = op->o_callback;
98         LDAPControl     **prev = op->o_ctrls;
99         LDAPControl     **ctrls = NULL, authz;
100         int             i, nctrls, rc = 0;
101         int             cache = op->o_do_not_cache;
102         char            *authzid = NULL;
103         BerVarray       ref;
104         struct berval   ndn = op->o_ndn;
105
106         struct ldapinfo li, *lip = (struct ldapinfo *)on->on_bi.bi_private;
107
108         if ( rs->sr_err != LDAP_REFERRAL && rs->sr_type != REP_SEARCHREF )
109                 return SLAP_CB_CONTINUE;
110
111         ref = rs->sr_ref;
112         rs->sr_ref = NULL;
113
114         op->o_callback = NULL;
115
116         if ( lip->url == NULL ) {
117                 /* FIXME: we're setting the URI of the first referral;
118                  * what if there are more?  Is this something we should
119                  * worry about? */
120                 li = *lip;
121                 op->o_bd->be_private = &li;
122
123                 if ( rs->sr_type != REP_SEARCHREF ) {
124                         LDAPURLDesc     *srv;
125                         char            *save_dn;
126
127                         /* parse reference and use 
128                          * proto://[host][:port]/ only */
129                         rc = ldap_url_parse_ext( ref[0].bv_val, &srv );
130                         if ( rc != LDAP_URL_SUCCESS) {
131                                 /* error */
132                                 return 1;
133                         }
134
135                         /* remove DN essentially because later on 
136                          * ldap_initialize() will parse the URL 
137                          * as a comma-separated URL list */
138                         save_dn = srv->lud_dn;
139                         srv->lud_dn = "";
140                         srv->lud_scope = LDAP_SCOPE_DEFAULT;
141                         li.url = ldap_url_desc2str( srv );
142                         srv->lud_dn = save_dn;
143                         ldap_free_urldesc( srv );
144
145                         if ( li.url == NULL ) {
146                                 /* error */
147                                 return 1;
148                         }
149                 }
150
151         } else {
152                 op->o_bd->be_private = on->on_bi.bi_private;
153         }
154
155         /* Chaining is performed by a privileged user on behalf
156          * of a normal user, using the ProxyAuthz control. However,
157          * Binds are done separately, on an anonymous session.
158          */
159         if ( op->o_tag != LDAP_REQ_BIND ) {
160                 for ( i = 0; prev && prev[i]; i++ )
161                         /* count and set prev to the last one */ ;
162                 nctrls = i;
163
164                 /* Add an extra NULL slot */
165                 if ( !prev ) {
166                         i++;
167                 }
168
169                 ctrls = op->o_tmpalloc((i + 1)*sizeof(LDAPControl *),
170                         op->o_tmpmemctx);
171                 for ( i = 0; i < nctrls; i++ ) {
172                         ctrls[i] = prev[i];
173                 }
174                 ctrls[nctrls] = &authz;
175                 ctrls[nctrls + 1] = NULL;
176                 authz.ldctl_oid = LDAP_CONTROL_PROXY_AUTHZ;
177                 authz.ldctl_iscritical = 1;
178                 authz.ldctl_value = op->o_dn;
179                 if ( !BER_BVISEMPTY( &op->o_dn ) ) {
180                         authzid = op->o_tmpalloc( op->o_dn.bv_len + STRLENOF("dn:"),
181                                 op->o_tmpmemctx );
182                         strcpy(authzid, "dn:");
183                         strcpy(authzid + STRLENOF("dn:"), op->o_dn.bv_val);
184                         authz.ldctl_value.bv_len = op->o_dn.bv_len + STRLENOF("dn:");
185                         authz.ldctl_value.bv_val = authzid;
186                 }
187                 op->o_ctrls = ctrls;
188                 op->o_ndn = op->o_bd->be_rootndn;
189         }
190
191         switch ( op->o_tag ) {
192         case LDAP_REQ_BIND: {
193                 struct berval   rndn = op->o_req_ndn;
194                 Connection      *conn = op->o_conn;
195
196                 op->o_req_ndn = slap_empty_bv;
197
198                 op->o_conn = NULL;
199                 rc = lback->bi_op_bind( op, rs );
200                 op->o_req_ndn = rndn;
201                 op->o_conn = conn;
202                 }
203                 break;
204         case LDAP_REQ_ADD:
205                 rc = lback->bi_op_add( op, rs );
206                 break;
207         case LDAP_REQ_DELETE:
208                 rc = lback->bi_op_delete( op, rs );
209                 break;
210         case LDAP_REQ_MODRDN:
211                 rc = lback->bi_op_modrdn( op, rs );
212                 break;
213         case LDAP_REQ_MODIFY:
214                 rc = lback->bi_op_modify( op, rs );
215                 break;
216         case LDAP_REQ_COMPARE:
217                 rc = lback->bi_op_compare( op, rs );
218                 break;
219         case LDAP_REQ_SEARCH:
220                 if ( rs->sr_type == REP_SEARCHREF ) {
221                         struct berval   *curr = ref,
222                                         odn = op->o_req_dn,
223                                         ondn = op->o_req_ndn;
224                         slap_callback   sc2 = { 0 };
225                         int             tmprc = 0;
226                         ber_len_t       refcnt = 0;
227                         BerVarray       newref = NULL;
228
229                         sc2.sc_response = ldap_chain_cb_response;
230                         op->o_callback = &sc2;
231
232                         rs->sr_type = REP_SEARCH;
233
234                         /* copy the private info because we need to modify it */
235                         for ( ; !BER_BVISNULL( &curr[0] ); curr++ ) {
236                                 LDAPURLDesc     *srv;
237                                 char            *save_dn;
238
239                                 /* parse reference and use
240                                  * proto://[host][:port]/ only */
241                                 tmprc = ldap_url_parse_ext( curr[0].bv_val, &srv );
242                                 if ( tmprc != LDAP_URL_SUCCESS ) {
243                                         /* error */
244                                         rc = 1;
245                                         goto end_of_searchref;
246                                 }
247
248                                 /* remove DN essentially because later on 
249                                  * ldap_initialize() will parse the URL 
250                                  * as a comma-separated URL list */
251                                 save_dn = srv->lud_dn;
252                                 srv->lud_dn = "";
253                                 srv->lud_scope = LDAP_SCOPE_DEFAULT;
254                                 li.url = ldap_url_desc2str( srv );
255                                 if ( li.url != NULL ) {
256                                         ber_str2bv_x( save_dn, 0, 1, &op->o_req_dn,
257                                                         op->o_tmpmemctx );
258                                         ber_dupbv_x( &op->o_req_ndn, &op->o_req_dn,
259                                                         op->o_tmpmemctx );
260                                 }
261
262                                 srv->lud_dn = save_dn;
263                                 ldap_free_urldesc( srv );
264
265                                 if ( li.url == NULL ) {
266                                         /* error */
267                                         rc = 1;
268                                         goto end_of_searchref;
269                                 }
270
271
272                                 /* FIXME: should we also copy filter and scope?
273                                  * according to RFC3296, no */
274                                 tmprc = lback->bi_op_search( op, rs );
275
276                                 ldap_memfree( li.url );
277                                 li.url = NULL;
278
279                                 op->o_tmpfree( op->o_req_dn.bv_val,
280                                                 op->o_tmpmemctx );
281                                 op->o_tmpfree( op->o_req_ndn.bv_val,
282                                                 op->o_tmpmemctx );
283
284                                 if ( tmprc ) {
285                                         /* error */
286                                         rc = 1;
287                                         goto end_of_searchref;
288                                 }
289
290                                 if ( rs->sr_err != LDAP_SUCCESS ) {
291                                         /* if search was not successful,
292                                          * at least return the referral! */
293                                         /* FIXME: assumes referrals 
294                                          * are always created via
295                                          * referral_rewrite() and freed via
296                                          * ber_bvarray_free( rs->sr_ref ) */
297                                         newref = ch_realloc( newref, sizeof( struct berval ) * (refcnt + 2) );
298                                         ber_dupbv( &newref[ refcnt ], &curr[ 0 ] );
299                                         refcnt++;
300                                         BER_BVZERO( &newref[ refcnt ] );
301                                 }
302                         }
303
304 end_of_searchref:;
305                         op->o_req_dn = odn;
306                         op->o_req_ndn = ondn;
307                         rs->sr_type = REP_SEARCHREF;
308                         rs->sr_entry = NULL;
309
310                         /* if the error was bad, it was already returned
311                          * by back-ldap; destroy the referrals left;
312                          * otherwise, let the frontend return them. */
313                         if ( newref ) {
314                                 if ( rc == 0 ) {
315                                         rc = SLAP_CB_CONTINUE;
316                                         if ( ref != default_referral ) {
317                                                 ber_bvarray_free( ref );
318                                         }
319                                         ref = newref;
320
321                                 } else {
322                                         ber_bvarray_free( newref );
323                                 }
324                         }
325                         
326                 } else {
327                         rc = lback->bi_op_search( op, rs );
328                 }
329                 break;
330         case LDAP_REQ_EXTENDED:
331                 rc = lback->bi_extended( op, rs );
332                 break;
333         default:
334                 rc = SLAP_CB_CONTINUE;
335                 break;
336         }
337         op->o_do_not_cache = cache;
338         op->o_ctrls = prev;
339         op->o_bd->be_private = private;
340         op->o_callback = sc;
341         op->o_ndn = ndn;
342         if ( ctrls ) {
343                 op->o_tmpfree( ctrls, op->o_tmpmemctx );
344         }
345         if ( authzid ) {
346                 op->o_tmpfree( authzid, op->o_tmpmemctx );
347         }
348         rs->sr_ref = ref;
349         if ( lip->url == NULL && li.url != NULL ) {
350                 ldap_memfree( li.url );
351         }
352
353         return rc;
354 }
355
356 static int
357 ldap_chain_config(
358         BackendDB       *be,
359         const char      *fname,
360         int             lineno,
361         int             argc,
362         char    **argv
363 )
364 {
365         slap_overinst   *on = (slap_overinst *) be->bd_info;
366         void            *private = be->be_private;
367         char            *argv0 = NULL;
368         int             rc;
369
370         be->be_private = on->on_bi.bi_private;
371         if ( strncasecmp( argv[ 0 ], "chain-", sizeof( "chain-" ) - 1 ) == 0 ) {
372                 argv0 = argv[ 0 ];
373                 argv[ 0 ] = &argv[ 0 ][ sizeof( "chain-" ) - 1 ];
374         }
375         rc = lback->bi_db_config( be, fname, lineno, argc, argv );
376         if ( argv0 ) {
377                 argv[ 0 ] = argv0;
378         }
379         
380         be->be_private = private;
381         return rc;
382 }
383
384 static int
385 ldap_chain_init(
386         BackendDB *be
387 )
388 {
389         slap_overinst *on = (slap_overinst *) be->bd_info;
390         void *private = be->be_private;
391         int rc;
392
393         be->be_private = NULL;
394         rc = lback->bi_db_init( be );
395         on->on_bi.bi_private = be->be_private;
396         be->be_private = private;
397
398         return rc;
399 }
400
401 static int
402 ldap_chain_destroy(
403         BackendDB *be
404 )
405 {
406         slap_overinst *on = (slap_overinst *) be->bd_info;
407         void *private = be->be_private;
408         int rc;
409
410         be->be_private = on->on_bi.bi_private;
411         rc = lback->bi_db_destroy( be );
412         on->on_bi.bi_private = be->be_private;
413         be->be_private = private;
414         return rc;
415 }
416
417 static slap_overinst ldapchain;
418
419 int
420 chain_init()
421 {
422         lback = backend_info( "ldap" );
423
424         if ( !lback ) {
425                 return -1;
426         }
427
428         ldapchain.on_bi.bi_type = "chain";
429         ldapchain.on_bi.bi_db_init = ldap_chain_init;
430         ldapchain.on_bi.bi_db_config = ldap_chain_config;
431         ldapchain.on_bi.bi_db_destroy = ldap_chain_destroy;
432         
433         /* ... otherwise the underlying backend's function would be called,
434          * likely passing an invalid entry; on the contrary, the requested
435          * operational attributes should have been returned while chasing
436          * the referrals.  This all in all is a bit messy, because part
437          * of the operational attributes are generated by they backend;
438          * part by the frontend; back-ldap should receive all the available
439          * ones from the remote server, but then, on it own, it strips those
440          * it assumes will be (re)generated by the frontend (e.g.
441          * subschemaSubentry.) */
442         ldapchain.on_bi.bi_operational = ldap_chain_operational;
443         
444         ldapchain.on_response = ldap_chain_response;
445
446
447         ldapchain.on_bi.bi_chk_referrals = ldap_chain_chk_referrals;
448
449         return overlay_register( &ldapchain );
450 }
451
452 #if SLAPD_OVER_CHAIN == SLAPD_MOD_DYNAMIC
453 int init_module(int argc, char *argv[]) {
454         return chain_init();
455 }
456 #endif /* SLAPD_OVER_CHAIN == SLAPD_MOD_DYNAMIC */
457
458 #endif /* SLAPD_OVER_CHAIN */
459
460 #endif /* ! defined(SLAPD_LDAP) */