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