Home | History | Annotate | Line # | Download | only in back-ldap
      1 /*	$NetBSD: distproc.c,v 1.4 2025/09/05 21:16:27 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-2024 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.4 2025/09/05 21:16:27 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 *
    137 ldap_distproc_rt2bv( ReferenceType_t rt )
    138 {
    139 	return &bv2rt[ rt ];
    140 }
    141 
    142 static const char *
    143 ldap_distproc_rt2str( ReferenceType_t rt )
    144 {
    145 	return bv2rt[ rt ].bv_val;
    146 }
    147 
    148 static ReferenceType_t
    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
    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 *
    172 ldap_distproc_ss2bv( SearchScope_t ss )
    173 {
    174 	return &bv2ss[ ss ];
    175 }
    176 
    177 static const char *
    178 ldap_distproc_ss2str( SearchScope_t ss )
    179 {
    180 	return bv2ss[ ss ].bv_val;
    181 }
    182 
    183 static SearchScope_t
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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