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
81typedef 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
94typedef 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
103typedef 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
116static 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
128static struct berval	bv2ss[] = {
129	BER_BVC( "baseObject" ),
130	BER_BVC( "singleLevel" ),
131	BER_BVC( "wholeSubtree" ),
132	BER_BVC( "subordinateSubtree" ),
133	BER_BVNULL
134};
135
136static struct berval *
137ldap_distproc_rt2bv( ReferenceType_t rt )
138{
139	return &bv2rt[ rt ];
140}
141
142static const char *
143ldap_distproc_rt2str( ReferenceType_t rt )
144{
145	return bv2rt[ rt ].bv_val;
146}
147
148static ReferenceType_t
149ldap_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
162static ReferenceType_t
163ldap_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
171static struct berval *
172ldap_distproc_ss2bv( SearchScope_t ss )
173{
174	return &bv2ss[ ss ];
175}
176
177static const char *
178ldap_distproc_ss2str( SearchScope_t ss )
179{
180	return bv2ss[ ss ].bv_val;
181}
182
183static SearchScope_t
184ldap_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
197static SearchScope_t
198ldap_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
214static int		sc_returnContRef;
215#define o_returnContRef			o_ctrlflag[sc_returnContRef]
216#define get_returnContRef(op)		((op)->o_returnContRef & SLAP_CONTROL_MASK)
217
218static struct berval	slap_EXOP_CHAINEDREQUEST = BER_BVC( LDAP_EXOP_X_CHAINEDREQUEST );
219static struct berval	slap_FEATURE_CANCHAINOPS = BER_BVC( LDAP_FEATURE_X_CANCHAINOPS );
220
221static BackendInfo	*lback;
222
223typedef 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
243static int ldap_distproc_db_init_common( BackendDB	*be );
244static 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
249static int
250ldap_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
267static int
268ldap_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
289static int
290ldap_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
308static int
309ldap_distproc_response( Operation *op, SlapReply *rs )
310{
311	return SLAP_CB_CONTINUE;
312}
313
314/*
315 * configuration...
316 */
317
318enum {
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
327static ConfigDriver distproc_cfgen;
328static ConfigCfAdd distproc_cfadd;
329static ConfigLDAPadd distproc_ldadd;
330
331static 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
348static 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
366static int
367distproc_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
450done:;
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
460typedef 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
468static void
469ldap_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
491static int
492distproc_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
526static int
527distproc_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
578static int
579ldap_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
606static int
607ldap_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 ) {
667private_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
705enum db_which {
706	db_open = 0,
707	db_close,
708	db_destroy,
709
710	db_last
711};
712
713static int
714ldap_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
758static int
759ldap_distproc_db_open(
760	BackendDB	*be,
761	ConfigReply	*cr )
762{
763	return ldap_distproc_db_func( be, db_open );
764}
765
766static int
767ldap_distproc_db_close(
768	BackendDB	*be,
769	ConfigReply	*cr )
770{
771	return ldap_distproc_db_func( be, db_close );
772}
773
774static int
775ldap_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 */
799static int
800ldap_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 */
825static int
826ldap_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
857static int
858ldap_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
888static int
889ldap_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
914static int
915ldap_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
934static slap_overinst distproc;
935
936int
937distproc_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