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