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