]> git.sur5r.net Git - openldap/blob - servers/slapd/back-relay/op.c
rework back-relay internals along Hallvard's suggestions (ITS#5328)
[openldap] / servers / slapd / back-relay / op.c
1 /* op.c - relay backend operations */
2 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
3  *
4  * Copyright 2004-2008 The OpenLDAP Foundation.
5  * Portions Copyright 2004 Pierangelo Masarati.
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted only as authorized by the OpenLDAP
10  * Public License.
11  *
12  * A copy of this license is available in the file LICENSE in the
13  * top-level directory of the distribution or, alternatively, at
14  * <http://www.OpenLDAP.org/license.html>.
15  */
16 /* ACKNOWLEDGEMENTS:
17  * This work was initially developed by Pierangelo Masarati for inclusion
18  * in OpenLDAP Software.
19  */
20
21 #include "portable.h"
22
23 #include <stdio.h>
24
25 #include "slap.h"
26 #include "back-relay.h"
27
28 #define RB_ERR_MASK             (0x00FFU)
29 #define RB_ERR                  (0x1000U)
30 #define RB_UNWILLING            (0x2000U)
31 #define RB_REFERRAL             (0x4000U)
32 #define RB_SEND                 (0x8000U)
33 #define RB_UNWILLING_SEND       (RB_UNWILLING|RB_SEND)
34 #define RB_REFERRAL_SEND        (RB_REFERRAL|RB_SEND)
35
36 static int
37 relay_back_swap_bd( Operation *op, SlapReply *rs )
38 {
39         slap_callback   *cb = op->o_callback;
40         BackendDB       *be = op->o_bd;
41
42         op->o_bd = cb->sc_private;
43         cb->sc_private = be;
44
45         return SLAP_CB_CONTINUE;
46 }
47
48 #define relay_back_add_cb( cb, op ) \
49         {                                               \
50                 (cb)->sc_next = (op)->o_callback;       \
51                 (cb)->sc_response = relay_back_swap_bd; \
52                 (cb)->sc_cleanup = relay_back_swap_bd;  \
53                 (cb)->sc_private = (op)->o_bd;          \
54                 (op)->o_callback = (cb);                \
55         }
56
57 /*
58  * selects the backend if not enforced at config;
59  * in case of failure, behaves based on err:
60  *      -1                      don't send result
61  *      LDAP_SUCCESS            don't send result; may send referral if dosend
62  *      any valid error         send as error result if dosend
63  */
64 static BackendDB *
65 relay_back_select_backend( Operation *op, SlapReply *rs, slap_mask_t fail_mode )
66 {
67         relay_back_info         *ri = (relay_back_info *)op->o_bd->be_private;
68         BackendDB               *bd = ri->ri_bd;
69         int                     rc = ( fail_mode & RB_ERR_MASK );
70
71         if ( bd == NULL && !BER_BVISNULL( &op->o_req_ndn ) ) {
72                 bd = select_backend( &op->o_req_ndn, 1 );
73                 if ( bd == op->o_bd ) {
74                         Debug( LDAP_DEBUG_ANY,
75                                 "%s: back-relay for DN=\"%s\" would call self.\n",
76                                 op->o_log_prefix, op->o_req_dn.bv_val, 0 );
77                         if ( fail_mode & RB_UNWILLING ) {
78                                 rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
79
80                         } else if ( fail_mode & RB_ERR ) {
81                                 rs->sr_err = rc;
82                         }
83
84                         if ( fail_mode & RB_SEND ) {
85                                 send_ldap_result( op, rs );
86                         }
87
88                         return NULL;
89                 }
90         }
91
92         if ( bd == NULL && fail_mode & RB_REFERRAL ) {
93                 if ( default_referral ) {
94                         rs->sr_err = LDAP_REFERRAL;
95                         if ( fail_mode & RB_SEND ) {
96                                 rs->sr_ref = referral_rewrite(
97                                         default_referral,
98                                         NULL, &op->o_req_dn,
99                                         LDAP_SCOPE_DEFAULT );
100                                 if ( !rs->sr_ref ) {
101                                         rs->sr_ref = default_referral;
102                                 }
103
104                                 send_ldap_result( op, rs );
105
106                                 if ( rs->sr_ref != default_referral ) {
107                                         ber_bvarray_free( rs->sr_ref );
108                                 }
109                         }
110
111                 } else {
112                         /* NOTE: err is LDAP_INVALID_CREDENTIALS for bind,
113                          * LDAP_NO_SUCH_OBJECT for other operations.
114                          * noSuchObject cannot be returned by bind */
115                         rs->sr_err = rc;
116                         if ( fail_mode & RB_SEND ) {
117                                 send_ldap_result( op, rs );
118                         }
119                 }
120         }
121
122         return bd;
123 }
124
125 static int
126 relay_back_op(
127         Operation       *op,
128         SlapReply       *rs,
129         BackendDB       *bd,
130         BI_op_func      *func,
131         slap_mask_t     fail_mode )
132 {
133         int                     rc = ( fail_mode & RB_ERR_MASK );
134
135         if ( func ) {
136                 BackendDB       *be = op->o_bd;
137                 slap_callback   cb;
138
139                 relay_back_add_cb( &cb, op );
140
141                 op->o_bd = bd;
142                 rc = func( op, rs );
143                 op->o_bd = be;
144
145                 if ( op->o_callback == &cb ) {
146                         op->o_callback = op->o_callback->sc_next;
147                 }
148
149         } else {
150                 if ( fail_mode & RB_ERR ) {
151                         rs->sr_err = rc;
152
153                 } else if ( fail_mode & RB_UNWILLING ) {
154                         rc = rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
155                         rs->sr_text = "operation not supported within naming context";
156                 }
157
158                 if ( fail_mode & RB_SEND ) {
159                         send_ldap_result( op, rs );
160                 }
161         }
162
163         return rc;
164 }
165
166 int
167 relay_back_op_bind( Operation *op, SlapReply *rs )
168 {
169         BackendDB       *bd;
170
171         /* allow rootdn as a means to auth without the need to actually
172          * contact the proxied DSA */
173         switch ( be_rootdn_bind( op, rs ) ) {
174         case SLAP_CB_CONTINUE:
175                 break;
176
177         default:
178                 return rs->sr_err;
179         }
180
181         bd = relay_back_select_backend( op, rs,
182                 ( LDAP_INVALID_CREDENTIALS | RB_ERR | RB_SEND ) );
183         if ( bd == NULL ) {
184                 return rs->sr_err;
185         }
186
187         return relay_back_op( op, rs, bd, bd->be_bind,
188                 ( LDAP_INVALID_CREDENTIALS | RB_ERR | RB_SEND ) );
189 }
190
191 int
192 relay_back_op_unbind( Operation *op, SlapReply *rs )
193 {
194         BackendDB               *bd;
195
196         bd = relay_back_select_backend( op, rs, 0 );
197         if ( bd != NULL ) {
198                 (void)relay_back_op( op, rs, bd, bd->be_unbind, 0 );
199         }
200
201         return 0;
202 }
203
204 int
205 relay_back_op_search( Operation *op, SlapReply *rs )
206 {
207         BackendDB               *bd;
208
209         bd = relay_back_select_backend( op, rs,
210                 ( LDAP_NO_SUCH_OBJECT | RB_ERR | RB_SEND ) );
211         if ( bd == NULL ) {
212                 return rs->sr_err;
213         }
214
215         return relay_back_op( op, rs, bd, bd->be_search,
216                 RB_UNWILLING_SEND );
217 }
218
219 int
220 relay_back_op_compare( Operation *op, SlapReply *rs )
221 {
222         BackendDB               *bd;
223
224         bd = relay_back_select_backend( op, rs,
225                 ( LDAP_NO_SUCH_OBJECT | RB_ERR | RB_SEND ) );
226         if ( bd == NULL ) {
227                 return rs->sr_err;
228         }
229
230         return relay_back_op( op, rs, bd, bd->be_compare,
231                 RB_UNWILLING_SEND );
232 }
233
234 int
235 relay_back_op_modify( Operation *op, SlapReply *rs )
236 {
237         BackendDB               *bd;
238
239         bd = relay_back_select_backend( op, rs,
240                 ( LDAP_NO_SUCH_OBJECT | RB_ERR | RB_SEND ) );
241         if ( bd == NULL ) {
242                 return rs->sr_err;
243         }
244
245         return relay_back_op( op, rs, bd, bd->be_modify,
246                 RB_UNWILLING_SEND );
247 }
248
249 int
250 relay_back_op_modrdn( Operation *op, SlapReply *rs )
251 {
252         BackendDB               *bd;
253
254         bd = relay_back_select_backend( op, rs,
255                 ( LDAP_NO_SUCH_OBJECT | RB_ERR | RB_SEND ) );
256         if ( bd == NULL ) {
257                 return rs->sr_err;
258         }
259
260         return relay_back_op( op, rs, bd, bd->be_modrdn,
261                 RB_UNWILLING_SEND );
262 }
263
264 int
265 relay_back_op_add( Operation *op, SlapReply *rs )
266 {
267         BackendDB               *bd;
268
269         bd = relay_back_select_backend( op, rs,
270                 ( LDAP_NO_SUCH_OBJECT | RB_ERR | RB_SEND ) );
271         if ( bd == NULL ) {
272                 return rs->sr_err;
273         }
274
275         return relay_back_op( op, rs, bd, bd->be_add,
276                 RB_UNWILLING_SEND );
277 }
278
279 int
280 relay_back_op_delete( Operation *op, SlapReply *rs )
281 {
282         BackendDB               *bd;
283
284         bd = relay_back_select_backend( op, rs,
285                 ( LDAP_NO_SUCH_OBJECT | RB_ERR | RB_SEND ) );
286         if ( bd == NULL ) {
287                 return rs->sr_err;
288         }
289
290         return relay_back_op( op, rs, bd, bd->be_delete,
291                 RB_UNWILLING_SEND );
292 }
293
294 int
295 relay_back_op_abandon( Operation *op, SlapReply *rs )
296 {
297         BackendDB               *bd;
298
299         bd = relay_back_select_backend( op, rs, 0 );
300         if ( bd == NULL ) {
301                 return rs->sr_err;
302         }
303
304         return relay_back_op( op, rs, bd, bd->be_abandon, 0 );
305 }
306
307 int
308 relay_back_op_cancel( Operation *op, SlapReply *rs )
309 {
310         BackendDB               *bd;
311         int                     rc;
312
313         bd = relay_back_select_backend( op, rs,
314                 ( LDAP_CANNOT_CANCEL | RB_ERR ) );
315         if ( bd == NULL ) {
316                 if ( op->o_cancel == SLAP_CANCEL_REQ ) {
317                         op->o_cancel = LDAP_CANNOT_CANCEL;
318                 }
319                 return rs->sr_err;
320         }
321
322         rc = relay_back_op( op, rs, bd, bd->be_cancel,
323                 ( LDAP_CANNOT_CANCEL | RB_ERR ) );
324         if ( rc == LDAP_CANNOT_CANCEL && op->o_cancel == SLAP_CANCEL_REQ )
325         {
326                 op->o_cancel = LDAP_CANNOT_CANCEL;
327         }
328
329         return rc;
330 }
331
332 int
333 relay_back_op_extended( Operation *op, SlapReply *rs )
334 {
335         BackendDB               *bd;
336
337         bd = relay_back_select_backend( op, rs,
338                 ( LDAP_NO_SUCH_OBJECT | RB_ERR ) );
339         if ( bd == NULL ) {
340                 return rs->sr_err;
341         }
342
343         return relay_back_op( op, rs, bd, bd->be_extended,
344                 RB_UNWILLING );
345 }
346
347 int
348 relay_back_entry_release_rw( Operation *op, Entry *e, int rw )
349 {
350         relay_back_info         *ri = (relay_back_info *)op->o_bd->be_private;
351         BackendDB               *bd;
352         int                     rc = 1;
353
354         bd = ri->ri_bd;
355         if ( bd == NULL) {
356                 bd = select_backend( &op->o_req_ndn, 1 );
357                 if ( bd == NULL ) {
358                         return 1;
359                 }
360         }
361
362         if ( bd->be_release ) {
363                 BackendDB       *be = op->o_bd;
364
365                 op->o_bd = bd;
366                 rc = bd->be_release( op, e, rw );
367                 op->o_bd = be;
368         }
369
370         return rc;
371
372 }
373
374 int
375 relay_back_entry_get_rw( Operation *op, struct berval *ndn,
376         ObjectClass *oc, AttributeDescription *at, int rw, Entry **e )
377 {
378         relay_back_info         *ri = (relay_back_info *)op->o_bd->be_private;
379         BackendDB               *bd;
380         int                     rc = 1;
381
382         bd = ri->ri_bd;
383         if ( bd == NULL) {
384                 bd = select_backend( &op->o_req_ndn, 1 );
385                 if ( bd == NULL ) {
386                         return 1;
387                 }
388         }
389
390         if ( bd->be_fetch ) {
391                 BackendDB       *be = op->o_bd;
392
393                 op->o_bd = bd;
394                 rc = bd->be_fetch( op, ndn, oc, at, rw, e );
395                 op->o_bd = be;
396         }
397
398         return rc;
399
400 }
401
402 /*
403  * NOTE: even the existence of this function is questionable: we cannot
404  * pass the bi_chk_referrals() call thru the rwm overlay because there
405  * is no way to rewrite the req_dn back; but then relay_back_chk_referrals()
406  * is passing the target database a DN that likely does not belong to its
407  * naming context... mmmh.
408  */
409 int
410 relay_back_chk_referrals( Operation *op, SlapReply *rs )
411 {
412         BackendDB               *bd;
413
414         bd = relay_back_select_backend( op, rs,
415                 ( LDAP_SUCCESS | RB_ERR | RB_SEND ) );
416         /* FIXME: this test only works if there are no overlays, so
417          * it is nearly useless; if made stricter, no nested back-relays
418          * can be instantiated... too bad. */
419         if ( bd == NULL || bd == op->o_bd ) {
420                 return 0;
421         }
422
423         /* no nested back-relays... */
424         if ( overlay_is_over( bd ) ) {
425                 slap_overinfo   *oi = (slap_overinfo *)bd->bd_info->bi_private;
426
427                 if ( oi->oi_orig == op->o_bd->bd_info ) {
428                         return 0;
429                 }
430         }
431
432         return relay_back_op( op, rs, bd, bd->be_chk_referrals, 0 );
433 }
434
435 int
436 relay_back_operational( Operation *op, SlapReply *rs )
437 {
438         BackendDB               *bd;
439
440         bd = relay_back_select_backend( op, rs,
441                 ( LDAP_SUCCESS | RB_ERR ) );
442         /* FIXME: this test only works if there are no overlays, so
443          * it is nearly useless; if made stricter, no nested back-relays
444          * can be instantiated... too bad. */
445         if ( bd == NULL || bd == op->o_bd ) {
446                 return 0;
447         }
448
449         return relay_back_op( op, rs, bd, bd->be_operational, 0 );
450 }
451
452 int
453 relay_back_has_subordinates( Operation *op, Entry *e, int *hasSubs )
454 {
455         SlapReply               rs = { 0 };
456         BackendDB               *bd;
457         int                     rc = 1;
458
459         bd = relay_back_select_backend( op, &rs,
460                 ( LDAP_SUCCESS | RB_ERR ) );
461         /* FIXME: this test only works if there are no overlays, so
462          * it is nearly useless; if made stricter, no nested back-relays
463          * can be instantiated... too bad. */
464         if ( bd == NULL || bd == op->o_bd ) {
465                 return 0;
466         }
467
468         if ( bd->be_has_subordinates ) {
469                 BackendDB       *be = op->o_bd;
470
471                 op->o_bd = bd;
472                 rc = bd->be_has_subordinates( op, e, hasSubs );
473                 op->o_bd = be;
474         }
475
476         return rc;
477
478 }
479
480 int
481 relay_back_connection_init( BackendDB *bd, Connection *c )
482 {
483         relay_back_info         *ri = (relay_back_info *)bd->be_private;
484
485         bd = ri->ri_bd;
486         if ( bd == NULL ) {
487                 return 0;
488         }
489
490         if ( bd->be_connection_init ) {
491                 return bd->be_connection_init( bd, c );
492         }
493
494         return 0;
495 }
496
497 int
498 relay_back_connection_destroy( BackendDB *bd, Connection *c )
499 {
500         relay_back_info         *ri = (relay_back_info *)bd->be_private;
501
502         bd = ri->ri_bd;
503         if ( bd == NULL ) {
504                 return 0;
505         }
506
507         if ( bd->be_connection_destroy ) {
508                 return bd->be_connection_destroy( bd, c );
509         }
510
511         return 0;
512
513 }
514
515 /*
516  * FIXME: must implement tools as well
517  */