]> git.sur5r.net Git - openldap/blob - servers/slapd/back-ldap/chain.c
67927c40d767c0c98eb6d788c79ee3af13abe3bf
[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 static BackendInfo *lback;
33
34 static int
35 ldap_chain_operational( Operation *op, SlapReply *rs )
36 {
37         /* Trap entries generated by back-ldap.
38          * 
39          * FIXME: we need a better way to recognize them; a cleaner
40          * solution would be to be able to intercept the response
41          * of be_operational(), so that we can divert only those
42          * calls that fail because operational attributes were
43          * requested for entries that do not belong to the underlying
44          * database.  This fix is likely to intercept also entries
45          * generated by back-perl and so. */
46         if ( rs->sr_entry->e_private == NULL ) {
47                 return 0;
48         }
49
50         return SLAP_CB_CONTINUE;
51 }
52
53 /*
54  * Search specific response that strips entryDN from entries
55  */
56 static int
57 ldap_chain_cb_search_response( Operation *op, SlapReply *rs )
58 {
59         assert( op->o_tag == LDAP_REQ_SEARCH );
60
61         if ( rs->sr_type == REP_SEARCH ) {
62                 Attribute       **ap = &rs->sr_entry->e_attrs;
63
64                 for ( ; *ap != NULL; ap = &(*ap)->a_next ) {
65                         /* will be generated later by frontend
66                          * (a cleaner solution would be that
67                          * the frontend checks if it already exists */
68                         if ( ad_cmp( (*ap)->a_desc, slap_schema.si_ad_entryDN ) == 0 )
69                         {
70                                 Attribute *a = *ap;
71
72                                 *ap = (*ap)->a_next;
73                                 attr_free( a );
74
75                                 /* there SHOULD be one only! */
76                                 break;
77                         }
78                 }
79                 
80                 return SLAP_CB_CONTINUE;
81
82         } else if ( rs->sr_type == REP_RESULT ) {
83                 /* back-ldap tried to send result */
84                 op->o_callback->sc_private = (void *)(1);
85         }
86
87         return 0;
88 }
89
90 /*
91  * Dummy response that simply traces if back-ldap tried to send 
92  * anything to the client
93  */
94 static int
95 ldap_chain_cb_response( Operation *op, SlapReply *rs )
96 {
97         if ( rs->sr_type == REP_RESULT ) {
98                 op->o_callback->sc_private = (void *)(1);
99
100         } else if ( op->o_tag == LDAP_REQ_SEARCH && rs->sr_type == REP_SEARCH )
101         {
102                 /* strip the entryDN attribute, but keep returning results */
103                 (void)ldap_chain_cb_search_response( op, rs );
104         }
105
106         return SLAP_CB_CONTINUE;
107 }
108
109 static int
110 ldap_chain_op(
111         Operation       *op,
112         SlapReply       *rs,
113         int             ( *op_f )( Operation *op, SlapReply *rs ), 
114         BerVarray       ref )
115 {
116         slap_overinst   *on = (slap_overinst *) op->o_bd->bd_info;
117         struct ldapinfo li, *lip = (struct ldapinfo *)on->on_bi.bi_private;
118         int             rc;
119
120         if ( lip->url != NULL ) {
121                 op->o_bd->be_private = on->on_bi.bi_private;
122                 return ( *op_f )( op, rs );
123         }
124
125         li = *lip;
126         op->o_bd->be_private = &li;
127
128         /* if we parse the URI then by no means 
129          * we can cache stuff or reuse connections, 
130          * because in back-ldap there's no caching
131          * based on the URI value, which is supposed
132          * to be set once for all (correct?) */
133         op->o_do_not_cache = 1;
134
135         for ( ; !BER_BVISNULL( ref ); ref++ ) {
136                 LDAPURLDesc     *srv;
137                 char            *save_dn;
138                         
139                 /* We're setting the URI of the first referral;
140                  * what if there are more?
141
142 Document: draft-ietf-ldapbis-protocol-27.txt
143
144 4.1.10. Referral 
145    ...
146    If the client wishes to progress the operation, it MUST follow the 
147    referral by contacting one of the supported services. If multiple 
148    URIs are present, the client assumes that any supported URI may be 
149    used to progress the operation. 
150
151                  * so we actually need to follow exactly one,
152                  * and we can assume any is fine.
153                  */
154         
155                 /* parse reference and use 
156                  * proto://[host][:port]/ only */
157                 rc = ldap_url_parse_ext( ref->bv_val, &srv );
158                 if ( rc != LDAP_URL_SUCCESS ) {
159                         /* try next */
160                         rc = LDAP_OTHER;
161                         continue;
162                 }
163
164                 /* remove DN essentially because later on 
165                  * ldap_initialize() will parse the URL 
166                  * as a comma-separated URL list */
167                 save_dn = srv->lud_dn;
168                 srv->lud_dn = "";
169                 srv->lud_scope = LDAP_SCOPE_DEFAULT;
170                 li.url = ldap_url_desc2str( srv );
171                 srv->lud_dn = save_dn;
172                 ldap_free_urldesc( srv );
173
174                 if ( li.url == NULL ) {
175                         /* try next */
176                         rc = LDAP_OTHER;
177                         continue;
178                 }
179
180                 rc = ( *op_f )( op, rs );
181
182                 ldap_memfree( li.url );
183                 li.url = NULL;
184                 
185                 if ( rc == LDAP_SUCCESS && rs->sr_err == LDAP_SUCCESS ) {
186                         break;
187                 }
188         }
189
190         return rc;
191 }
192
193 static int
194 ldap_chain_response( Operation *op, SlapReply *rs )
195 {
196         slap_overinst   *on = (slap_overinst *) op->o_bd->bd_info;
197         void            *private = op->o_bd->be_private;
198         slap_callback   *sc = op->o_callback,
199                         sc2 = { 0 };
200         int             rc = 0;
201         int             cache = op->o_do_not_cache;
202         BerVarray       ref;
203         struct berval   ndn = op->o_ndn;
204
205         struct ldapinfo li, *lip = (struct ldapinfo *)on->on_bi.bi_private;
206
207         if ( rs->sr_err != LDAP_REFERRAL && rs->sr_type != REP_SEARCHREF ) {
208                 return SLAP_CB_CONTINUE;
209         }
210
211         /*
212          * TODO: add checks on who/when chain operations; e.g.:
213          *   a) what identities are authorized
214          *   b) what request DN (e.g. only chain requests rooted at <DN>)
215          *   c) what referral URIs
216          *   d) what protocol scheme (e.g. only ldaps://)
217          *   e) what ssf
218          */
219
220         ref = rs->sr_ref;
221         rs->sr_ref = NULL;
222
223         /* we need this to know if back-ldap returned any result */
224         sc2.sc_response = ldap_chain_cb_response;
225         op->o_callback = &sc2;
226
227         /* Chaining can be performed by a privileged user on behalf
228          * of normal users, using the ProxyAuthz control, by exploiting
229          * the identity assertion feature of back-ldap; see idassert-*
230          * directives in slapd-ldap(5).
231          *
232          * FIXME: the idassert-authcDN is one, will it be fine regardless
233          * of the URI we obtain from the referral?
234          */
235
236         switch ( op->o_tag ) {
237         case LDAP_REQ_BIND: {
238                 struct berval   rndn = op->o_req_ndn;
239                 Connection      *conn = op->o_conn;
240
241                 /* FIXME: can we really get a referral for binds? */
242                 op->o_req_ndn = slap_empty_bv;
243                 op->o_conn = NULL;
244                 rc = ldap_chain_op( op, rs, lback->bi_op_bind, ref );
245                 op->o_req_ndn = rndn;
246                 op->o_conn = conn;
247                 }
248                 break;
249         case LDAP_REQ_ADD:
250                 {
251                 int             cleanup_attrs = 0;
252
253                 if ( op->ora_e->e_attrs == NULL ) {
254                         char            textbuf[ SLAP_TEXT_BUFLEN ];
255                         size_t          textlen = sizeof( textbuf );
256
257                         /* global overlay: create entry */
258                         /* NOTE: this is a hack to use the chain overlay
259                          * as global.  I expect to be able to remove this
260                          * soon by using slap_mods2entry() earlier in
261                          * do_add(), adding the operational attrs later
262                          * if required. */
263                         rs->sr_err = slap_mods2entry( op->ora_modlist,
264                                         &op->ora_e, 0, 1,
265                                         &rs->sr_text, textbuf, textlen );
266                         if ( rs->sr_err != LDAP_SUCCESS ) {
267                                 send_ldap_result( op, rs );
268                                 rc = 1;
269                                 break;
270                         }
271                 }
272                 rc = ldap_chain_op( op, rs, lback->bi_op_add, ref );
273                 if ( cleanup_attrs ) {
274                         attrs_free( op->ora_e->e_attrs );
275                         op->ora_e->e_attrs = NULL;
276                 }
277                 break;
278                 }
279         case LDAP_REQ_DELETE:
280                 rc = ldap_chain_op( op, rs, lback->bi_op_delete, ref );
281                 break;
282         case LDAP_REQ_MODRDN:
283                 rc = ldap_chain_op( op, rs, lback->bi_op_modrdn, ref );
284                 break;
285         case LDAP_REQ_MODIFY:
286                 rc = ldap_chain_op( op, rs, lback->bi_op_modify, ref );
287                 break;
288         case LDAP_REQ_COMPARE:
289                 rc = ldap_chain_op( op, rs, lback->bi_op_compare, ref );
290                 break;
291         case LDAP_REQ_SEARCH:
292                 if ( rs->sr_type == REP_SEARCHREF ) {
293                         struct berval   *curr = ref,
294                                         odn = op->o_req_dn,
295                                         ondn = op->o_req_ndn;
296
297                         rs->sr_type = REP_SEARCH;
298
299                         sc2.sc_response = ldap_chain_cb_search_response;
300
301                         li = *lip;
302                         li.url = NULL;
303                         op->o_bd->be_private = &li;
304                         
305                         /* if we parse the URI then by no means 
306                          * we can cache stuff or reuse connections, 
307                          * because in back-ldap there's no caching
308                          * based on the URI value, which is supposed
309                          * to be set once for all (correct?) */
310                         op->o_do_not_cache = 1;
311
312                         /* copy the private info because we need to modify it */
313                         for ( ; !BER_BVISNULL( &curr[0] ); curr++ ) {
314                                 LDAPURLDesc     *srv;
315                                 char            *save_dn;
316
317                                 /* parse reference and use
318                                  * proto://[host][:port]/ only */
319                                 rc = ldap_url_parse_ext( curr[0].bv_val, &srv );
320                                 if ( rc != LDAP_URL_SUCCESS ) {
321                                         /* try next */
322                                         rs->sr_err = LDAP_OTHER;
323                                         continue;
324                                 }
325
326                                 /* remove DN essentially because later on 
327                                  * ldap_initialize() will parse the URL 
328                                  * as a comma-separated URL list */
329                                 save_dn = srv->lud_dn;
330                                 srv->lud_dn = "";
331                                 srv->lud_scope = LDAP_SCOPE_DEFAULT;
332                                 li.url = ldap_url_desc2str( srv );
333                                 if ( li.url != NULL ) {
334                                         ber_str2bv_x( save_dn, 0, 1, &op->o_req_dn,
335                                                         op->o_tmpmemctx );
336                                         ber_dupbv_x( &op->o_req_ndn, &op->o_req_dn,
337                                                         op->o_tmpmemctx );
338                                 }
339
340                                 srv->lud_dn = save_dn;
341                                 ldap_free_urldesc( srv );
342
343                                 if ( li.url == NULL ) {
344                                         /* try next */
345                                         rs->sr_err = LDAP_OTHER;
346                                         continue;
347                                 }
348
349
350                                 /* FIXME: should we also copy filter and scope?
351                                  * according to RFC3296, no */
352                                 rc = lback->bi_op_search( op, rs );
353
354                                 ldap_memfree( li.url );
355                                 li.url = NULL;
356
357                                 op->o_tmpfree( op->o_req_dn.bv_val,
358                                                 op->o_tmpmemctx );
359                                 op->o_tmpfree( op->o_req_ndn.bv_val,
360                                                 op->o_tmpmemctx );
361
362                                 if ( rc == LDAP_SUCCESS && rs->sr_err == LDAP_SUCCESS ) {
363                                         break;
364                                 }
365                         }
366
367                         op->o_req_dn = odn;
368                         op->o_req_ndn = ondn;
369                         rs->sr_type = REP_SEARCHREF;
370                         rs->sr_entry = NULL;
371
372                         if ( rc != LDAP_SUCCESS ) {
373                                 /* couldn't chase any of the referrals */
374                                 rc = SLAP_CB_CONTINUE;
375                         }
376                         
377                 } else {
378                         rc = ldap_chain_op( op, rs, lback->bi_op_search, ref );
379                 }
380                 break;
381         case LDAP_REQ_EXTENDED:
382                 rc = ldap_chain_op( op, rs, lback->bi_extended, ref );
383                 /* FIXME: ldap_back_extended() by design 
384                  * doesn't send result; frontend is expected
385                  * to send it... */
386                 if ( rc != SLAPD_ABANDON ) {
387                         send_ldap_extended( op, rs );
388                         rc = LDAP_SUCCESS;
389                 }
390                 break;
391         default:
392                 rc = SLAP_CB_CONTINUE;
393                 break;
394         }
395
396         if ( sc2.sc_private == NULL ) {
397                 op->o_callback = NULL;
398                 rc = rs->sr_err = slap_map_api2result( rs );
399                 send_ldap_result( op, rs );
400         }
401
402         op->o_do_not_cache = cache;
403         op->o_bd->be_private = private;
404         op->o_callback = sc;
405         op->o_ndn = ndn;
406         rs->sr_ref = ref;
407
408         return rc;
409 }
410
411 static int
412 ldap_chain_db_config(
413         BackendDB       *be,
414         const char      *fname,
415         int             lineno,
416         int             argc,
417         char    **argv
418 )
419 {
420         slap_overinst   *on = (slap_overinst *) be->bd_info;
421         void            *private = be->be_private;
422         char            *argv0 = NULL;
423         int             rc;
424
425         be->be_private = on->on_bi.bi_private;
426         if ( strncasecmp( argv[ 0 ], "chain-", sizeof( "chain-" ) - 1 ) == 0 ) {
427                 argv0 = argv[ 0 ];
428                 argv[ 0 ] = &argv[ 0 ][ sizeof( "chain-" ) - 1 ];
429         }
430         rc = lback->bi_db_config( be, fname, lineno, argc, argv );
431         if ( argv0 ) {
432                 argv[ 0 ] = argv0;
433         }
434         
435         be->be_private = private;
436         return rc;
437 }
438
439 static int
440 ldap_chain_db_init(
441         BackendDB *be
442 )
443 {
444         slap_overinst   *on = (slap_overinst *)be->bd_info;
445         int             rc;
446         BackendDB       bd = *be;
447
448         if ( lback == NULL ) {
449                 lback = backend_info( "ldap" );
450
451                 if ( lback == NULL ) {
452                         return -1;
453                 }
454         }
455
456         bd.be_private = NULL;
457         rc = lback->bi_db_init( &bd );
458         on->on_bi.bi_private = bd.be_private;
459
460         return rc;
461 }
462
463 static int
464 ldap_chain_db_destroy(
465         BackendDB *be
466 )
467 {
468         slap_overinst *on = (slap_overinst *) be->bd_info;
469         void *private = be->be_private;
470         int rc;
471
472         be->be_private = on->on_bi.bi_private;
473         rc = lback->bi_db_destroy( be );
474         on->on_bi.bi_private = be->be_private;
475         be->be_private = private;
476         return rc;
477 }
478
479 static slap_overinst ldapchain;
480
481 int
482 chain_init( void )
483 {
484         ldapchain.on_bi.bi_type = "chain";
485         ldapchain.on_bi.bi_db_init = ldap_chain_db_init;
486         ldapchain.on_bi.bi_db_config = ldap_chain_db_config;
487         ldapchain.on_bi.bi_db_destroy = ldap_chain_db_destroy;
488         
489         /* ... otherwise the underlying backend's function would be called,
490          * likely passing an invalid entry; on the contrary, the requested
491          * operational attributes should have been returned while chasing
492          * the referrals.  This all in all is a bit messy, because part
493          * of the operational attributes are generated by they backend;
494          * part by the frontend; back-ldap should receive all the available
495          * ones from the remote server, but then, on it own, it strips those
496          * it assumes will be (re)generated by the frontend (e.g.
497          * subschemaSubentry.) */
498         ldapchain.on_bi.bi_operational = ldap_chain_operational;
499         
500         ldapchain.on_response = ldap_chain_response;
501
502         return overlay_register( &ldapchain );
503 }
504