xref: /netbsd-src/external/bsd/openldap/dist/servers/slapd/back-ldap/distproc.c (revision a5847cc334d9a7029f6352b847e9e8d71a0f9e0c)
1 /*	$NetBSD: distproc.c,v 1.1.1.3 2010/12/12 15:23:05 adam Exp $	*/
2 
3 /* distproc.c - implement distributed procedures */
4 /* OpenLDAP: pkg/ldap/servers/slapd/back-ldap/distproc.c,v 1.3.2.9 2010/04/13 20:23:28 kurt Exp */
5 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
6  *
7  * Copyright 2005-2010 The OpenLDAP Foundation.
8  * Portions Copyright 2003 Howard Chu.
9  * All rights reserved.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted only as authorized by the OpenLDAP
13  * Public License.
14  *
15  * A copy of this license is available in the file LICENSE in the
16  * top-level directory of the distribution or, alternatively, at
17  * <http://www.OpenLDAP.org/license.html>.
18  */
19 /* ACKNOWLEDGEMENTS:
20  * This work was initially developed by Pierangelo Masarati for inclusion
21  * in OpenLDAP Software.
22  * Based on back-ldap and slapo-chain, developed by Howard Chu
23  */
24 
25 #include "portable.h"
26 
27 #include <stdio.h>
28 
29 #include <ac/string.h>
30 #include <ac/socket.h>
31 
32 #include "slap.h"
33 
34 #ifdef SLAP_DISTPROC
35 
36 #include "back-ldap.h"
37 
38 #include "config.h"
39 
40 /*
41  * From <draft-sermersheim-ldap-distproc>
42  *
43 
44       ContinuationReference ::= SET {
45          referralURI      [0] SET SIZE (1..MAX) OF URI,
46          localReference   [2] LDAPDN,
47          referenceType    [3] ReferenceType,
48          remainingName    [4] RelativeLDAPDN OPTIONAL,
49          searchScope      [5] SearchScope OPTIONAL,
50          searchedSubtrees [6] SearchedSubtrees OPTIONAL,
51          failedName       [7] LDAPDN OPTIONAL,
52          ...  }
53 
54       ReferenceType ::= ENUMERATED {
55          superior               (0),
56          subordinate            (1),
57          cross                  (2),
58          nonSpecificSubordinate (3),
59          supplier               (4),
60          master                 (5),
61          immediateSuperior      (6),
62          self                   (7),
63          ...  }
64 
65       SearchScope ::= ENUMERATED {
66          baseObject         (0),
67          singleLevel        (1),
68          wholeSubtree       (2),
69          subordinateSubtree (3),
70          ...  }
71 
72    SearchedSubtrees ::= SET OF RelativeLDAPDN
73 
74    LDAPDN, RelativeLDAPDN, and LDAPString, are defined in [RFC2251].
75 
76  */
77 
78 typedef enum ReferenceType_t {
79 	LDAP_DP_RT_UNKNOWN			= -1,
80 	LDAP_DP_RT_SUPERIOR			= 0,
81 	LDAP_DP_RT_SUBORDINATE			= 1,
82 	LDAP_DP_RT_CROSS			= 2,
83 	LDAP_DP_RT_NONSPECIFICSUBORDINATE	= 3,
84 	LDAP_DP_RT_SUPPLIER			= 4,
85 	LDAP_DP_RT_MASTER			= 5,
86 	LDAP_DP_RT_IMMEDIATESUPERIOR		= 6,
87 	LDAP_DP_RT_SELF				= 7,
88 	LDAP_DP_RT_LAST
89 } ReferenceType_t;
90 
91 typedef enum SearchScope_t {
92 	LDAP_DP_SS_UNKNOWN			= -1,
93 	LDAP_DP_SS_BASEOBJECT			= 0,
94 	LDAP_DP_SS_SINGLELEVEL			= 1,
95 	LDAP_DP_SS_WHOLESUBTREE			= 2,
96 	LDAP_DP_SS_SUBORDINATESUBTREE		= 3,
97 	LDAP_DP_SS_LAST
98 } SearchScope_t;
99 
100 typedef struct ContinuationReference_t {
101 	BerVarray		cr_referralURI;
102 	/* ?			[1] ? */
103 	struct berval		cr_localReference;
104 	ReferenceType_t		cr_referenceType;
105 	struct berval		cr_remainingName;
106 	SearchScope_t		cr_searchScope;
107 	BerVarray		cr_searchedSubtrees;
108 	struct berval		cr_failedName;
109 } ContinuationReference_t;
110 #define	CR_INIT		{ NULL, BER_BVNULL, LDAP_DP_RT_UNKNOWN, BER_BVNULL, LDAP_DP_SS_UNKNOWN, NULL, BER_BVNULL }
111 
112 static struct berval	bv2rt[] = {
113 	BER_BVC( "superior" ),
114 	BER_BVC( "subordinate" ),
115 	BER_BVC( "cross" ),
116 	BER_BVC( "nonSpecificSubordinate" ),
117 	BER_BVC( "supplier" ),
118 	BER_BVC( "master" ),
119 	BER_BVC( "immediateSuperior" ),
120 	BER_BVC( "self" ),
121 	BER_BVNULL
122 };
123 
124 static struct berval	bv2ss[] = {
125 	BER_BVC( "baseObject" ),
126 	BER_BVC( "singleLevel" ),
127 	BER_BVC( "wholeSubtree" ),
128 	BER_BVC( "subordinateSubtree" ),
129 	BER_BVNULL
130 };
131 
132 static struct berval *
133 ldap_distproc_rt2bv( ReferenceType_t rt )
134 {
135 	return &bv2rt[ rt ];
136 }
137 
138 static const char *
139 ldap_distproc_rt2str( ReferenceType_t rt )
140 {
141 	return bv2rt[ rt ].bv_val;
142 }
143 
144 static ReferenceType_t
145 ldap_distproc_bv2rt( struct berval *bv )
146 {
147 	ReferenceType_t		rt;
148 
149 	for ( rt = 0; !BER_BVISNULL( &bv2rt[ rt ] ); rt++ ) {
150 		if ( ber_bvstrcasecmp( bv, &bv2rt[ rt ] ) == 0 ) {
151 			return rt;
152 		}
153 	}
154 
155 	return LDAP_DP_RT_UNKNOWN;
156 }
157 
158 static ReferenceType_t
159 ldap_distproc_str2rt( const char *s )
160 {
161 	struct berval	bv;
162 
163 	ber_str2bv( s, 0, 0, &bv );
164 	return ldap_distproc_bv2rt( &bv );
165 }
166 
167 static struct berval *
168 ldap_distproc_ss2bv( SearchScope_t ss )
169 {
170 	return &bv2ss[ ss ];
171 }
172 
173 static const char *
174 ldap_distproc_ss2str( SearchScope_t ss )
175 {
176 	return bv2ss[ ss ].bv_val;
177 }
178 
179 static SearchScope_t
180 ldap_distproc_bv2ss( struct berval *bv )
181 {
182 	ReferenceType_t		ss;
183 
184 	for ( ss = 0; !BER_BVISNULL( &bv2ss[ ss ] ); ss++ ) {
185 		if ( ber_bvstrcasecmp( bv, &bv2ss[ ss ] ) == 0 ) {
186 			return ss;
187 		}
188 	}
189 
190 	return LDAP_DP_SS_UNKNOWN;
191 }
192 
193 static SearchScope_t
194 ldap_distproc_str2ss( const char *s )
195 {
196 	struct berval	bv;
197 
198 	ber_str2bv( s, 0, 0, &bv );
199 	return ldap_distproc_bv2ss( &bv );
200 }
201 
202 /*
203  * NOTE: this overlay assumes that the chainingBehavior control
204  * is registered by the chain overlay; it may move here some time.
205  * This overlay provides support for that control as well.
206  */
207 
208 
209 static int		sc_returnContRef;
210 #define o_returnContRef			o_ctrlflag[sc_returnContRef]
211 #define get_returnContRef(op)		((op)->o_returnContRef & SLAP_CONTROL_MASK)
212 
213 static struct berval	slap_EXOP_CHAINEDREQUEST = BER_BVC( LDAP_EXOP_X_CHAINEDREQUEST );
214 static struct berval	slap_FEATURE_CANCHAINOPS = BER_BVC( LDAP_FEATURE_X_CANCHAINOPS );
215 
216 static BackendInfo	*lback;
217 
218 typedef struct ldap_distproc_t {
219 	/* "common" configuration info (anything occurring before an "uri") */
220 	ldapinfo_t		*lc_common_li;
221 
222 	/* current configuration info */
223 	ldapinfo_t		*lc_cfg_li;
224 
225 	/* tree of configured[/generated?] "uri" info */
226 	ldap_avl_info_t		lc_lai;
227 
228 	unsigned		lc_flags;
229 #define LDAP_DISTPROC_F_NONE		(0x00U)
230 #define	LDAP_DISTPROC_F_CHAINING	(0x01U)
231 #define	LDAP_DISTPROC_F_CACHE_URI	(0x10U)
232 
233 #define	LDAP_DISTPROC_CHAINING( lc )	( ( (lc)->lc_flags & LDAP_DISTPROC_F_CHAINING ) == LDAP_DISTPROC_F_CHAINING )
234 #define	LDAP_DISTPROC_CACHE_URI( lc )	( ( (lc)->lc_flags & LDAP_DISTPROC_F_CACHE_URI ) == LDAP_DISTPROC_F_CACHE_URI )
235 
236 } ldap_distproc_t;
237 
238 static int ldap_distproc_db_init_common( BackendDB	*be );
239 static int ldap_distproc_db_init_one( BackendDB *be );
240 #define	ldap_distproc_db_open_one(be)		(lback)->bi_db_open( (be) )
241 #define	ldap_distproc_db_close_one(be)		(0)
242 #define	ldap_distproc_db_destroy_one(be, ca)	(lback)->bi_db_destroy( (be), (ca) )
243 
244 static int
245 ldap_distproc_parse_ctrl(
246 	Operation	*op,
247 	SlapReply	*rs,
248 	LDAPControl	*ctrl );
249 
250 static int
251 ldap_distproc_uri_cmp( const void *c1, const void *c2 )
252 {
253 	const ldapinfo_t	*li1 = (const ldapinfo_t *)c1;
254 	const ldapinfo_t	*li2 = (const ldapinfo_t *)c2;
255 
256 	assert( li1->li_bvuri != NULL );
257 	assert( !BER_BVISNULL( &li1->li_bvuri[ 0 ] ) );
258 	assert( BER_BVISNULL( &li1->li_bvuri[ 1 ] ) );
259 
260 	assert( li2->li_bvuri != NULL );
261 	assert( !BER_BVISNULL( &li2->li_bvuri[ 0 ] ) );
262 	assert( BER_BVISNULL( &li2->li_bvuri[ 1 ] ) );
263 
264 	/* If local DNs don't match, it is definitely not a match */
265 	return ber_bvcmp( &li1->li_bvuri[ 0 ], &li2->li_bvuri[ 0 ] );
266 }
267 
268 static int
269 ldap_distproc_uri_dup( void *c1, void *c2 )
270 {
271 	ldapinfo_t	*li1 = (ldapinfo_t *)c1;
272 	ldapinfo_t	*li2 = (ldapinfo_t *)c2;
273 
274 	assert( li1->li_bvuri != NULL );
275 	assert( !BER_BVISNULL( &li1->li_bvuri[ 0 ] ) );
276 	assert( BER_BVISNULL( &li1->li_bvuri[ 1 ] ) );
277 
278 	assert( li2->li_bvuri != NULL );
279 	assert( !BER_BVISNULL( &li2->li_bvuri[ 0 ] ) );
280 	assert( BER_BVISNULL( &li2->li_bvuri[ 1 ] ) );
281 
282 	/* Cannot have more than one shared session with same DN */
283 	if ( ber_bvcmp( &li1->li_bvuri[ 0 ], &li2->li_bvuri[ 0 ] ) == 0 ) {
284 		return -1;
285 	}
286 
287 	return 0;
288 }
289 
290 static int
291 ldap_distproc_operational( Operation *op, SlapReply *rs )
292 {
293 	/* Trap entries generated by back-ldap.
294 	 *
295 	 * FIXME: we need a better way to recognize them; a cleaner
296 	 * solution would be to be able to intercept the response
297 	 * of be_operational(), so that we can divert only those
298 	 * calls that fail because operational attributes were
299 	 * requested for entries that do not belong to the underlying
300 	 * database.  This fix is likely to intercept also entries
301 	 * generated by back-perl and so. */
302 	if ( rs->sr_entry->e_private == NULL ) {
303 		return LDAP_SUCCESS;
304 	}
305 
306 	return SLAP_CB_CONTINUE;
307 }
308 
309 static int
310 ldap_distproc_response( Operation *op, SlapReply *rs )
311 {
312 	return SLAP_CB_CONTINUE;
313 }
314 
315 /*
316  * configuration...
317  */
318 
319 enum {
320 	/* NOTE: the chaining behavior control is registered
321 	 * by the chain overlay; it may move here some time */
322 	DP_CHAINING = 1,
323 	DP_CACHE_URI,
324 
325 	DP_LAST
326 };
327 
328 static ConfigDriver distproc_cfgen;
329 static ConfigCfAdd distproc_cfadd;
330 static ConfigLDAPadd distproc_ldadd;
331 
332 static ConfigTable distproc_cfg[] = {
333 	{ "distproc-chaining", "args",
334 		2, 4, 0, ARG_MAGIC|ARG_BERVAL|DP_CHAINING, distproc_cfgen,
335 		/* NOTE: using the same attributeTypes defined
336 		 * for the "chain" overlay */
337 		"( OLcfgOvAt:3.1 NAME 'olcChainingBehavior' "
338 			"DESC 'Chaining behavior control parameters (draft-sermersheim-ldap-chaining)' "
339 			"SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
340 	{ "distproc-cache-uri", "TRUE/FALSE",
341 		2, 2, 0, ARG_MAGIC|ARG_ON_OFF|DP_CACHE_URI, distproc_cfgen,
342 		"( OLcfgOvAt:3.2 NAME 'olcChainCacheURI' "
343 			"DESC 'Enables caching of URIs not present in configuration' "
344 			"SYNTAX OMsBoolean "
345 			"SINGLE-VALUE )", NULL, NULL },
346 	{ NULL, NULL, 0, 0, 0, ARG_IGNORED }
347 };
348 
349 static ConfigOCs distproc_ocs[] = {
350 	{ "( OLcfgOvOc:7.1 "
351 		"NAME 'olcDistProcConfig' "
352 		"DESC 'Distributed procedures <draft-sermersheim-ldap-distproc> configuration' "
353 		"SUP olcOverlayConfig "
354 		"MAY ( "
355 			"olcChainingBehavior $ "
356 			"olcChainCacheURI "
357 			") )",
358 		Cft_Overlay, distproc_cfg, NULL, distproc_cfadd },
359 	{ "( OLcfgOvOc:7.2 "
360 		"NAME 'olcDistProcDatabase' "
361 		"DESC 'Distributed procedure remote server configuration' "
362 		"AUXILIARY )",
363 		Cft_Misc, distproc_cfg, distproc_ldadd },
364 	{ NULL, 0, NULL }
365 };
366 
367 static int
368 distproc_ldadd( CfEntryInfo *p, Entry *e, ConfigArgs *ca )
369 {
370 	slap_overinst		*on;
371 	ldap_distproc_t		*lc;
372 
373 	ldapinfo_t		*li;
374 
375 	AttributeDescription	*ad = NULL;
376 	Attribute		*at;
377 	const char		*text;
378 
379 	int			rc;
380 
381 	if ( p->ce_type != Cft_Overlay
382 		|| !p->ce_bi
383 		|| p->ce_bi->bi_cf_ocs != distproc_ocs )
384 	{
385 		return LDAP_CONSTRAINT_VIOLATION;
386 	}
387 
388 	on = (slap_overinst *)p->ce_bi;
389 	lc = (ldap_distproc_t *)on->on_bi.bi_private;
390 
391 	assert( ca->be == NULL );
392 	ca->be = (BackendDB *)ch_calloc( 1, sizeof( BackendDB ) );
393 
394 	ca->be->bd_info = (BackendInfo *)on;
395 
396 	rc = slap_str2ad( "olcDbURI", &ad, &text );
397 	assert( rc == LDAP_SUCCESS );
398 
399 	at = attr_find( e->e_attrs, ad );
400 	if ( lc->lc_common_li == NULL && at != NULL ) {
401 		/* FIXME: we should generate an empty default entry
402 		 * if none is supplied */
403 		Debug( LDAP_DEBUG_ANY, "slapd-distproc: "
404 			"first underlying database \"%s\" "
405 			"cannot contain attribute \"%s\".\n",
406 			e->e_name.bv_val, ad->ad_cname.bv_val, 0 );
407 		rc = LDAP_CONSTRAINT_VIOLATION;
408 		goto done;
409 
410 	} else if ( lc->lc_common_li != NULL && at == NULL ) {
411 		/* FIXME: we should generate an empty default entry
412 		 * if none is supplied */
413 		Debug( LDAP_DEBUG_ANY, "slapd-distproc: "
414 			"subsequent underlying database \"%s\" "
415 			"must contain attribute \"%s\".\n",
416 			e->e_name.bv_val, ad->ad_cname.bv_val, 0 );
417 		rc = LDAP_CONSTRAINT_VIOLATION;
418 		goto done;
419 	}
420 
421 	if ( lc->lc_common_li == NULL ) {
422 		rc = ldap_distproc_db_init_common( ca->be );
423 
424 	} else {
425 		rc = ldap_distproc_db_init_one( ca->be );
426 	}
427 
428 	if ( rc != 0 ) {
429 		Debug( LDAP_DEBUG_ANY, "slapd-distproc: "
430 			"unable to init %sunderlying database \"%s\".\n",
431 			lc->lc_common_li == NULL ? "common " : "", e->e_name.bv_val, 0 );
432 		return LDAP_CONSTRAINT_VIOLATION;
433 	}
434 
435 	li = ca->be->be_private;
436 
437 	if ( lc->lc_common_li == NULL ) {
438 		lc->lc_common_li = li;
439 
440 	} else if ( avl_insert( &lc->lc_lai.lai_tree, (caddr_t)li,
441 		ldap_distproc_uri_cmp, ldap_distproc_uri_dup ) )
442 	{
443 		Debug( LDAP_DEBUG_ANY, "slapd-distproc: "
444 			"database \"%s\" insert failed.\n",
445 			e->e_name.bv_val, 0, 0 );
446 		rc = LDAP_CONSTRAINT_VIOLATION;
447 		goto done;
448 	}
449 
450 done:;
451 	if ( rc != LDAP_SUCCESS ) {
452 		(void)ldap_distproc_db_destroy_one( ca->be, NULL );
453 		ch_free( ca->be );
454 		ca->be = NULL;
455 	}
456 
457 	return rc;
458 }
459 
460 typedef struct ldap_distproc_cfadd_apply_t {
461 	Operation	*op;
462 	SlapReply	*rs;
463 	Entry		*p;
464 	ConfigArgs	*ca;
465 	int		count;
466 } ldap_distproc_cfadd_apply_t;
467 
468 static int
469 ldap_distproc_cfadd_apply( void *datum, void *arg )
470 {
471 	ldapinfo_t			*li = (ldapinfo_t *)datum;
472 	ldap_distproc_cfadd_apply_t	*lca = (ldap_distproc_cfadd_apply_t *)arg;
473 
474 	struct berval			bv;
475 
476 	/* FIXME: should not hardcode "olcDatabase" here */
477 	bv.bv_len = snprintf( lca->ca->cr_msg, sizeof( lca->ca->cr_msg ),
478 		"olcDatabase={%d}%s", lca->count, lback->bi_type );
479 	bv.bv_val = lca->ca->cr_msg;
480 
481 	lca->ca->be->be_private = (void *)li;
482 	config_build_entry( lca->op, lca->rs, lca->p->e_private, lca->ca,
483 		&bv, lback->bi_cf_ocs, &distproc_ocs[ 1 ] );
484 
485 	lca->count++;
486 
487 	return 0;
488 }
489 
490 static int
491 distproc_cfadd( Operation *op, SlapReply *rs, Entry *p, ConfigArgs *ca )
492 {
493 	CfEntryInfo	*pe = p->e_private;
494 	slap_overinst	*on = (slap_overinst *)pe->ce_bi;
495 	ldap_distproc_t	*lc = (ldap_distproc_t *)on->on_bi.bi_private;
496 	void		*priv = (void *)ca->be->be_private;
497 
498 	if ( lback->bi_cf_ocs ) {
499 		ldap_distproc_cfadd_apply_t	lca = { 0 };
500 
501 		lca.op = op;
502 		lca.rs = rs;
503 		lca.p = p;
504 		lca.ca = ca;
505 		lca.count = 0;
506 
507 		(void)ldap_distproc_cfadd_apply( (void *)lc->lc_common_li, (void *)&lca );
508 
509 		(void)avl_apply( lc->lc_lai.lai_tree, ldap_distproc_cfadd_apply,
510 			&lca, 1, AVL_INORDER );
511 
512 		ca->be->be_private = priv;
513 	}
514 
515 	return 0;
516 }
517 
518 static int
519 distproc_cfgen( ConfigArgs *c )
520 {
521 	slap_overinst	*on = (slap_overinst *)c->bi;
522 	ldap_distproc_t	*lc = (ldap_distproc_t *)on->on_bi.bi_private;
523 
524 	int		rc = 0;
525 
526 	if ( c->op == SLAP_CONFIG_EMIT ) {
527 		switch( c->type ) {
528 		case DP_CACHE_URI:
529 			c->value_int = LDAP_DISTPROC_CACHE_URI( lc );
530 			break;
531 
532 		default:
533 			assert( 0 );
534 			rc = 1;
535 		}
536 		return rc;
537 
538 	} else if ( c->op == LDAP_MOD_DELETE ) {
539 		switch( c->type ) {
540 		case DP_CHAINING:
541 			return 1;
542 
543 		case DP_CACHE_URI:
544 			lc->lc_flags &= ~LDAP_DISTPROC_F_CACHE_URI;
545 			break;
546 
547 		default:
548 			return 1;
549 		}
550 		return rc;
551 	}
552 
553 	switch( c->type ) {
554 	case DP_CACHE_URI:
555 		if ( c->value_int ) {
556 			lc->lc_flags |= LDAP_DISTPROC_F_CACHE_URI;
557 		} else {
558 			lc->lc_flags &= ~LDAP_DISTPROC_F_CACHE_URI;
559 		}
560 		break;
561 
562 	default:
563 		assert( 0 );
564 		return 1;
565 	}
566 
567 	return rc;
568 }
569 
570 static int
571 ldap_distproc_db_init(
572 	BackendDB *be,
573 	ConfigReply *cr )
574 {
575 	slap_overinst	*on = (slap_overinst *)be->bd_info;
576 	ldap_distproc_t	*lc = NULL;
577 
578 	if ( lback == NULL ) {
579 		lback = backend_info( "ldap" );
580 
581 		if ( lback == NULL ) {
582 			return 1;
583 		}
584 	}
585 
586 	lc = ch_malloc( sizeof( ldap_distproc_t ) );
587 	if ( lc == NULL ) {
588 		return 1;
589 	}
590 	memset( lc, 0, sizeof( ldap_distproc_t ) );
591 	ldap_pvt_thread_mutex_init( &lc->lc_lai.lai_mutex );
592 
593 	on->on_bi.bi_private = (void *)lc;
594 
595 	return 0;
596 }
597 
598 static int
599 ldap_distproc_db_config(
600 	BackendDB	*be,
601 	const char	*fname,
602 	int		lineno,
603 	int		argc,
604 	char		**argv )
605 {
606 	slap_overinst	*on = (slap_overinst *)be->bd_info;
607 	ldap_distproc_t	*lc = (ldap_distproc_t *)on->on_bi.bi_private;
608 
609 	int		rc = SLAP_CONF_UNKNOWN;
610 
611 	if ( lc->lc_common_li == NULL ) {
612 		void	*be_private = be->be_private;
613 		ldap_distproc_db_init_common( be );
614 		lc->lc_common_li = lc->lc_cfg_li = (ldapinfo_t *)be->be_private;
615 		be->be_private = be_private;
616 	}
617 
618 	/* Something for the distproc database? */
619 	if ( strncasecmp( argv[ 0 ], "distproc-", STRLENOF( "distproc-" ) ) == 0 ) {
620 		char		*save_argv0 = argv[ 0 ];
621 		BackendInfo	*bd_info = be->bd_info;
622 		void		*be_private = be->be_private;
623 		ConfigOCs	*be_cf_ocs = be->be_cf_ocs;
624 		int		is_uri = 0;
625 
626 		argv[ 0 ] += STRLENOF( "distproc-" );
627 
628 		if ( strcasecmp( argv[ 0 ], "uri" ) == 0 ) {
629 			rc = ldap_distproc_db_init_one( be );
630 			if ( rc != 0 ) {
631 				Debug( LDAP_DEBUG_ANY, "%s: line %d: "
632 					"underlying slapd-ldap initialization failed.\n.",
633 					fname, lineno, 0 );
634 				return 1;
635 			}
636 			lc->lc_cfg_li = be->be_private;
637 			is_uri = 1;
638 		}
639 
640 		/* TODO: add checks on what other slapd-ldap(5) args
641 		 * should be put in the template; this is not quite
642 		 * harmful, because attributes that shouldn't don't
643 		 * get actually used, but the user should at least
644 		 * be warned.
645 		 */
646 
647 		be->bd_info = lback;
648 		be->be_private = (void *)lc->lc_cfg_li;
649 		be->be_cf_ocs = lback->bi_cf_ocs;
650 
651 		rc = config_generic_wrapper( be, fname, lineno, argc, argv );
652 
653 		argv[ 0 ] = save_argv0;
654 		be->be_cf_ocs = be_cf_ocs;
655 		be->be_private = be_private;
656 		be->bd_info = bd_info;
657 
658 		if ( is_uri ) {
659 private_destroy:;
660 			if ( rc != 0 ) {
661 				BackendDB		db = *be;
662 
663 				db.bd_info = lback;
664 				db.be_private = (void *)lc->lc_cfg_li;
665 				ldap_distproc_db_destroy_one( &db, NULL );
666 				lc->lc_cfg_li = NULL;
667 
668 			} else {
669 				if ( lc->lc_cfg_li->li_bvuri == NULL
670 					|| BER_BVISNULL( &lc->lc_cfg_li->li_bvuri[ 0 ] )
671 					|| !BER_BVISNULL( &lc->lc_cfg_li->li_bvuri[ 1 ] ) )
672 				{
673 					Debug( LDAP_DEBUG_ANY, "%s: line %d: "
674 						"no URI list allowed in slapo-distproc.\n",
675 						fname, lineno, 0 );
676 					rc = 1;
677 					goto private_destroy;
678 				}
679 
680 				if ( avl_insert( &lc->lc_lai.lai_tree,
681 					(caddr_t)lc->lc_cfg_li,
682 					ldap_distproc_uri_cmp, ldap_distproc_uri_dup ) )
683 				{
684 					Debug( LDAP_DEBUG_ANY, "%s: line %d: "
685 						"duplicate URI in slapo-distproc.\n",
686 						fname, lineno, 0 );
687 					rc = 1;
688 					goto private_destroy;
689 				}
690 			}
691 		}
692 	}
693 
694 	return rc;
695 }
696 
697 enum db_which {
698 	db_open = 0,
699 	db_close,
700 	db_destroy,
701 
702 	db_last
703 };
704 
705 typedef struct ldap_distproc_db_apply_t {
706 	BackendDB	*be;
707 	BI_db_func	*func;
708 } ldap_distproc_db_apply_t;
709 
710 static int
711 ldap_distproc_db_apply( void *datum, void *arg )
712 {
713 	ldapinfo_t		*li = (ldapinfo_t *)datum;
714 	ldap_distproc_db_apply_t	*lca = (ldap_distproc_db_apply_t *)arg;
715 
716 	lca->be->be_private = (void *)li;
717 
718 	return lca->func( lca->be, NULL );
719 }
720 
721 static int
722 ldap_distproc_db_func(
723 	BackendDB *be,
724 	enum db_which which
725 )
726 {
727 	slap_overinst	*on = (slap_overinst *)be->bd_info;
728 	ldap_distproc_t	*lc = (ldap_distproc_t *)on->on_bi.bi_private;
729 
730 	int		rc = 0;
731 
732 	if ( lc ) {
733 		BI_db_func	*func = (&lback->bi_db_open)[ which ];
734 
735 		if ( func != NULL && lc->lc_common_li != NULL ) {
736 			BackendDB		db = *be;
737 
738 			db.bd_info = lback;
739 			db.be_private = lc->lc_common_li;
740 
741 			rc = func( &db, NULL );
742 
743 			if ( rc != 0 ) {
744 				return rc;
745 			}
746 
747 			if ( lc->lc_lai.lai_tree != NULL ) {
748 				ldap_distproc_db_apply_t	lca;
749 
750 				lca.be = &db;
751 				lca.func = func;
752 
753 				rc = avl_apply( lc->lc_lai.lai_tree,
754 					ldap_distproc_db_apply, (void *)&lca,
755 					1, AVL_INORDER ) != AVL_NOMORE;
756 			}
757 		}
758 	}
759 
760 	return rc;
761 }
762 
763 static int
764 ldap_distproc_db_open(
765 	BackendDB	*be,
766 	ConfigReply	*cr )
767 {
768 	return ldap_distproc_db_func( be, db_open );
769 }
770 
771 static int
772 ldap_distproc_db_close(
773 	BackendDB	*be,
774 	ConfigReply	*cr )
775 {
776 	return ldap_distproc_db_func( be, db_close );
777 }
778 
779 static int
780 ldap_distproc_db_destroy(
781 	BackendDB	*be,
782 	ConfigReply	*cr )
783 {
784 	slap_overinst	*on = (slap_overinst *) be->bd_info;
785 	ldap_distproc_t	*lc = (ldap_distproc_t *)on->on_bi.bi_private;
786 
787 	int		rc;
788 
789 	rc = ldap_distproc_db_func( be, db_destroy );
790 
791 	if ( lc ) {
792 		avl_free( lc->lc_lai.lai_tree, NULL );
793 		ldap_pvt_thread_mutex_destroy( &lc->lc_lai.lai_mutex );
794 		ch_free( lc );
795 	}
796 
797 	return rc;
798 }
799 
800 /*
801  * inits one instance of the slapd-ldap backend, and stores
802  * the private info in be_private of the arg
803  */
804 static int
805 ldap_distproc_db_init_common(
806 	BackendDB	*be )
807 {
808 	BackendInfo	*bi = be->bd_info;
809 	int		t;
810 
811 	be->bd_info = lback;
812 	be->be_private = NULL;
813 	t = lback->bi_db_init( be, NULL );
814 	if ( t != 0 ) {
815 		return t;
816 	}
817 	be->bd_info = bi;
818 
819 	return 0;
820 }
821 
822 /*
823  * inits one instance of the slapd-ldap backend, stores
824  * the private info in be_private of the arg and fills
825  * selected fields with data from the template.
826  *
827  * NOTE: add checks about the other fields of the template,
828  * which are ignored and SHOULD NOT be configured by the user.
829  */
830 static int
831 ldap_distproc_db_init_one(
832 	BackendDB	*be )
833 {
834 	slap_overinst	*on = (slap_overinst *)be->bd_info;
835 	ldap_distproc_t	*lc = (ldap_distproc_t *)on->on_bi.bi_private;
836 
837 	BackendInfo	*bi = be->bd_info;
838 	ldapinfo_t	*li;
839 
840 	slap_op_t	t;
841 
842 	be->bd_info = lback;
843 	be->be_private = NULL;
844 	t = lback->bi_db_init( be, NULL );
845 	if ( t != 0 ) {
846 		return t;
847 	}
848 	li = (ldapinfo_t *)be->be_private;
849 
850 	/* copy common data */
851 	li->li_nretries = lc->lc_common_li->li_nretries;
852 	li->li_flags = lc->lc_common_li->li_flags;
853 	li->li_version = lc->lc_common_li->li_version;
854 	for ( t = 0; t < SLAP_OP_LAST; t++ ) {
855 		li->li_timeout[ t ] = lc->lc_common_li->li_timeout[ t ];
856 	}
857 	be->bd_info = bi;
858 
859 	return 0;
860 }
861 
862 typedef struct ldap_distproc_conn_apply_t {
863 	BackendDB	*be;
864 	Connection	*conn;
865 } ldap_distproc_conn_apply_t;
866 
867 static int
868 ldap_distproc_conn_apply( void *datum, void *arg )
869 {
870 	ldapinfo_t		*li = (ldapinfo_t *)datum;
871 	ldap_distproc_conn_apply_t	*lca = (ldap_distproc_conn_apply_t *)arg;
872 
873 	lca->be->be_private = (void *)li;
874 
875 	return lback->bi_connection_destroy( lca->be, lca->conn );
876 }
877 
878 static int
879 ldap_distproc_connection_destroy(
880 	BackendDB *be,
881 	Connection *conn
882 )
883 {
884 	slap_overinst		*on = (slap_overinst *) be->bd_info;
885 	ldap_distproc_t		*lc = (ldap_distproc_t *)on->on_bi.bi_private;
886 	void			*private = be->be_private;
887 	ldap_distproc_conn_apply_t	lca;
888 	int			rc;
889 
890 	be->be_private = NULL;
891 	lca.be = be;
892 	lca.conn = conn;
893 	ldap_pvt_thread_mutex_lock( &lc->lc_lai.lai_mutex );
894 	rc = avl_apply( lc->lc_lai.lai_tree, ldap_distproc_conn_apply,
895 		(void *)&lca, 1, AVL_INORDER ) != AVL_NOMORE;
896 	ldap_pvt_thread_mutex_unlock( &lc->lc_lai.lai_mutex );
897 	be->be_private = private;
898 
899 	return rc;
900 }
901 
902 static int
903 ldap_distproc_parse_returnContRef_ctrl(
904 	Operation	*op,
905 	SlapReply	*rs,
906 	LDAPControl	*ctrl )
907 {
908 	if ( get_returnContRef( op ) != SLAP_CONTROL_NONE ) {
909 		rs->sr_text = "returnContinuationReference control specified multiple times";
910 		return LDAP_PROTOCOL_ERROR;
911 	}
912 
913 	if ( op->o_pagedresults != SLAP_CONTROL_NONE ) {
914 		rs->sr_text = "returnContinuationReference control specified with pagedResults control";
915 		return LDAP_PROTOCOL_ERROR;
916 	}
917 
918 	if ( !BER_BVISEMPTY( &ctrl->ldctl_value ) ) {
919 		rs->sr_text = "returnContinuationReference control: value must be NULL";
920 		return LDAP_PROTOCOL_ERROR;
921 	}
922 
923 	op->o_returnContRef = ctrl->ldctl_iscritical ? SLAP_CONTROL_CRITICAL : SLAP_CONTROL_NONCRITICAL;
924 
925 	return LDAP_SUCCESS;
926 }
927 
928 static int
929 ldap_exop_chained_request(
930 		Operation	*op,
931 		SlapReply	*rs )
932 {
933 	Statslog( LDAP_DEBUG_STATS, "%s CHAINED REQUEST\n",
934 	    op->o_log_prefix, 0, 0, 0, 0 );
935 
936 	rs->sr_err = backend_check_restrictions( op, rs,
937 			(struct berval *)&slap_EXOP_CHAINEDREQUEST );
938 	if ( rs->sr_err != LDAP_SUCCESS ) {
939 		return rs->sr_err;
940 	}
941 
942 	/* by now, just reject requests */
943 	rs->sr_text = "under development";
944 	return LDAP_UNWILLING_TO_PERFORM;
945 }
946 
947 
948 static slap_overinst distproc;
949 
950 int
951 distproc_initialize( void )
952 {
953 	int	rc;
954 
955 	/* Make sure we don't exceed the bits reserved for userland */
956 	config_check_userland( DP_LAST );
957 
958 	rc = load_extop( (struct berval *)&slap_EXOP_CHAINEDREQUEST,
959 		SLAP_EXOP_HIDE, ldap_exop_chained_request );
960 	if ( rc != LDAP_SUCCESS ) {
961 		Debug( LDAP_DEBUG_ANY, "slapd-distproc: "
962 			"unable to register chainedRequest exop: %d.\n",
963 			rc, 0, 0 );
964 		return rc;
965 	}
966 
967 #ifdef LDAP_DEVEL
968 	rc = supported_feature_load( &slap_FEATURE_CANCHAINOPS );
969 	if ( rc != LDAP_SUCCESS ) {
970 		Debug( LDAP_DEBUG_ANY, "slapd-distproc: "
971 			"unable to register canChainOperations supported feature: %d.\n",
972 			rc, 0, 0 );
973 		return rc;
974 	}
975 #endif
976 
977 	rc = register_supported_control( LDAP_CONTROL_X_RETURNCONTREF,
978 			SLAP_CTRL_GLOBAL|SLAP_CTRL_ACCESS|SLAP_CTRL_HIDE, NULL,
979 			ldap_distproc_parse_returnContRef_ctrl, &sc_returnContRef );
980 	if ( rc != LDAP_SUCCESS ) {
981 		Debug( LDAP_DEBUG_ANY, "slapd-distproc: "
982 			"unable to register returnContinuationReference control: %d.\n",
983 			rc, 0, 0 );
984 		return rc;
985 	}
986 
987 	distproc.on_bi.bi_type = "distproc";
988 	distproc.on_bi.bi_db_init = ldap_distproc_db_init;
989 	distproc.on_bi.bi_db_config = ldap_distproc_db_config;
990 	distproc.on_bi.bi_db_open = ldap_distproc_db_open;
991 	distproc.on_bi.bi_db_close = ldap_distproc_db_close;
992 	distproc.on_bi.bi_db_destroy = ldap_distproc_db_destroy;
993 
994 	/* ... otherwise the underlying backend's function would be called,
995 	 * likely passing an invalid entry; on the contrary, the requested
996 	 * operational attributes should have been returned while chasing
997 	 * the referrals.  This all in all is a bit messy, because part
998 	 * of the operational attributes are generated by the backend;
999 	 * part by the frontend; back-ldap should receive all the available
1000 	 * ones from the remote server, but then, on its own, it strips those
1001 	 * it assumes will be (re)generated by the frontend (e.g.
1002 	 * subschemaSubentry, entryDN, ...) */
1003 	distproc.on_bi.bi_operational = ldap_distproc_operational;
1004 
1005 	distproc.on_bi.bi_connection_destroy = ldap_distproc_connection_destroy;
1006 
1007 	distproc.on_response = ldap_distproc_response;
1008 
1009 	distproc.on_bi.bi_cf_ocs = distproc_ocs;
1010 
1011 	rc = config_register_schema( distproc_cfg, distproc_ocs );
1012 	if ( rc ) {
1013 		return rc;
1014 	}
1015 
1016 	return overlay_register( &distproc );
1017 }
1018 
1019 #endif /* SLAP_DISTPROC */
1020