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