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