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