Home | History | Annotate | Line # | Download | only in slapd
      1 /*	$NetBSD: root_dse.c,v 1.4 2025/09/05 21:16:25 christos Exp $	*/
      2 
      3 /* root_dse.c - Provides the Root DSA-Specific Entry */
      4 /* $OpenLDAP$ */
      5 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
      6  *
      7  * Copyright 1999-2024 The OpenLDAP Foundation.
      8  * All rights reserved.
      9  *
     10  * Redistribution and use in source and binary forms, with or without
     11  * modification, are permitted only as authorized by the OpenLDAP
     12  * Public License.
     13  *
     14  * A copy of this license is available in the file LICENSE in the
     15  * top-level directory of the distribution or, alternatively, at
     16  * <http://www.OpenLDAP.org/license.html>.
     17  */
     18 
     19 #include <sys/cdefs.h>
     20 __RCSID("$NetBSD: root_dse.c,v 1.4 2025/09/05 21:16:25 christos Exp $");
     21 
     22 #include "portable.h"
     23 
     24 #include <stdio.h>
     25 
     26 #include <ac/string.h>
     27 
     28 #include "slap.h"
     29 #include <ldif.h>
     30 #include "lber_pvt.h"
     31 
     32 #ifdef LDAP_SLAPI
     33 #include "slapi/slapi.h"
     34 #endif
     35 
     36 static struct berval	builtin_supportedFeatures[] = {
     37 	BER_BVC(LDAP_FEATURE_MODIFY_INCREMENT),		/* Modify/increment */
     38 	BER_BVC(LDAP_FEATURE_ALL_OP_ATTRS),		/* All Op Attrs (+) */
     39 	BER_BVC(LDAP_FEATURE_OBJECTCLASS_ATTRS),	/* OCs in Attrs List (@class) */
     40 	BER_BVC(LDAP_FEATURE_ABSOLUTE_FILTERS),		/* (&) and (|) search filters */
     41 	BER_BVC(LDAP_FEATURE_LANGUAGE_TAG_OPTIONS),	/* Language Tag Options */
     42 	BER_BVC(LDAP_FEATURE_LANGUAGE_RANGE_OPTIONS),	/* Language Range Options */
     43 #ifdef LDAP_DEVEL
     44 	BER_BVC(LDAP_FEATURE_SUBORDINATE_SCOPE),	/* "children" search scope */
     45 #endif
     46 	BER_BVNULL
     47 };
     48 static struct berval	*supportedFeatures;
     49 
     50 static Entry	*usr_attr = NULL;
     51 
     52 /*
     53  * allow modules to register functions that muck with the root DSE entry
     54  */
     55 
     56 typedef struct entry_info_t {
     57 	SLAP_ENTRY_INFO_FN	func;
     58 	void			*arg;
     59 	struct entry_info_t	*next;
     60 } entry_info_t;
     61 
     62 static entry_info_t *extra_info;
     63 
     64 int
     65 entry_info_register( SLAP_ENTRY_INFO_FN func, void *arg )
     66 {
     67 	entry_info_t	*ei = ch_calloc( 1, sizeof( entry_info_t ) );
     68 
     69 	ei->func = func;
     70 	ei->arg = arg;
     71 
     72 	ei->next = extra_info;
     73 	extra_info = ei;
     74 
     75 	return 0;
     76 }
     77 
     78 int
     79 entry_info_unregister( SLAP_ENTRY_INFO_FN func, void *arg )
     80 {
     81 	entry_info_t	**eip;
     82 
     83 	for ( eip = &extra_info; *eip != NULL; eip = &(*eip)->next ) {
     84 		if ( (*eip)->func == func && (*eip)->arg == arg ) {
     85 			entry_info_t	*ei = *eip;
     86 
     87 			*eip = ei->next;
     88 
     89 			ch_free( ei );
     90 
     91 			return 0;
     92 		}
     93 	}
     94 
     95 	return -1;
     96 }
     97 
     98 void
     99 entry_info_destroy( void )
    100 {
    101 	entry_info_t	**eip;
    102 
    103 	for ( eip = &extra_info; *eip != NULL;  ) {
    104 		entry_info_t	*ei = *eip;
    105 
    106 		eip = &(*eip)->next;
    107 
    108 		ch_free( ei );
    109 	}
    110 }
    111 
    112 /*
    113  * Allow modules to register supported features
    114  */
    115 
    116 static int
    117 supported_feature_init( void )
    118 {
    119 	int		i;
    120 
    121 	if ( supportedFeatures != NULL ) {
    122 		return 0;
    123 	}
    124 
    125 	for ( i = 0; !BER_BVISNULL( &builtin_supportedFeatures[ i ] ); i++ )
    126 		;
    127 
    128 	supportedFeatures = ch_calloc( sizeof( struct berval ), i + 1 );
    129 	if ( supportedFeatures == NULL ) {
    130 		return -1;
    131 	}
    132 
    133 	for ( i = 0; !BER_BVISNULL( &builtin_supportedFeatures[ i ] ); i++ ) {
    134 		ber_dupbv( &supportedFeatures[ i ], &builtin_supportedFeatures[ i ] );
    135 	}
    136 	BER_BVZERO( &supportedFeatures[ i ] );
    137 
    138 	return 0;
    139 }
    140 
    141 int
    142 supported_feature_destroy( void )
    143 {
    144 	int		i;
    145 
    146 	if ( supportedFeatures == NULL ) {
    147 		return 0;
    148 	}
    149 
    150 	for ( i = 0; !BER_BVISNULL( &supportedFeatures[ i ] ); i++ ) {
    151 		ch_free( supportedFeatures[ i ].bv_val );
    152 	}
    153 
    154 	ch_free( supportedFeatures );
    155 	supportedFeatures = NULL;
    156 
    157 	return 0;
    158 }
    159 
    160 int
    161 supported_feature_load( struct berval *f )
    162 {
    163 	struct berval	*tmp;
    164 	int		i;
    165 
    166 	supported_feature_init();
    167 
    168 	for ( i = 0; !BER_BVISNULL( &supportedFeatures[ i ] ); i++ )
    169 		;
    170 
    171 	tmp = ch_realloc( supportedFeatures, sizeof( struct berval ) * ( i + 2 ) );
    172 	if ( tmp == NULL ) {
    173 		return -1;
    174 	}
    175 	supportedFeatures = tmp;
    176 
    177 	ber_dupbv( &supportedFeatures[ i ], f );
    178 	BER_BVZERO( &supportedFeatures[ i + 1 ] );
    179 
    180 	return 0;
    181 }
    182 
    183 int
    184 root_dse_info(
    185 	Connection *conn,
    186 	Entry **entry,
    187 	const char **text )
    188 {
    189 	Entry		*e;
    190 	struct berval val;
    191 #ifdef LDAP_SLAPI
    192 	struct berval *bv;
    193 #endif
    194 	int		i, j;
    195 	char ** supportedSASLMechanisms;
    196 	BackendDB *be;
    197 
    198 	AttributeDescription *ad_structuralObjectClass
    199 		= slap_schema.si_ad_structuralObjectClass;
    200 	AttributeDescription *ad_objectClass
    201 		= slap_schema.si_ad_objectClass;
    202 	AttributeDescription *ad_namingContexts
    203 		= slap_schema.si_ad_namingContexts;
    204 #ifdef LDAP_SLAPI
    205 	AttributeDescription *ad_supportedExtension
    206 		= slap_schema.si_ad_supportedExtension;
    207 #endif
    208 	AttributeDescription *ad_supportedLDAPVersion
    209 		= slap_schema.si_ad_supportedLDAPVersion;
    210 	AttributeDescription *ad_supportedSASLMechanisms
    211 		= slap_schema.si_ad_supportedSASLMechanisms;
    212 	AttributeDescription *ad_supportedFeatures
    213 		= slap_schema.si_ad_supportedFeatures;
    214 	AttributeDescription *ad_monitorContext
    215 		= slap_schema.si_ad_monitorContext;
    216 	AttributeDescription *ad_configContext
    217 		= slap_schema.si_ad_configContext;
    218 	AttributeDescription *ad_ref
    219 		= slap_schema.si_ad_ref;
    220 
    221 	e = entry_alloc();
    222 	if( e == NULL ) {
    223 		Debug( LDAP_DEBUG_ANY,
    224 			"root_dse_info: entry_alloc failed" );
    225 		return LDAP_OTHER;
    226 	}
    227 
    228 	e->e_attrs = NULL;
    229 	e->e_name.bv_val = ch_strdup( LDAP_ROOT_DSE );
    230 	e->e_name.bv_len = sizeof( LDAP_ROOT_DSE )-1;
    231 	e->e_nname.bv_val = ch_strdup( LDAP_ROOT_DSE );
    232 	e->e_nname.bv_len = sizeof( LDAP_ROOT_DSE )-1;
    233 
    234 	/* the DN is an empty string so no pretty/normalization is needed */
    235 	assert( !e->e_name.bv_len );
    236 	assert( !e->e_nname.bv_len );
    237 
    238 	e->e_private = NULL;
    239 
    240 	/* FIXME: is this really needed? */
    241 	BER_BVSTR( &val, "top" );
    242 	if( attr_merge_one( e, ad_objectClass, &val, NULL ) ) {
    243 fail:
    244 		entry_free( e );
    245 		return LDAP_OTHER;
    246 	}
    247 
    248 	BER_BVSTR( &val, "OpenLDAProotDSE" );
    249 	if( attr_merge_one( e, ad_objectClass, &val, NULL ) ) {
    250 		goto fail;
    251 	}
    252 	if( attr_merge_one( e, ad_structuralObjectClass, &val, NULL ) ) {
    253 		goto fail;
    254 	}
    255 
    256 	LDAP_STAILQ_FOREACH( be, &backendDB, be_next ) {
    257 		if ( be->be_suffix == NULL
    258 				|| be->be_nsuffix == NULL ) {
    259 			/* no suffix! */
    260 			continue;
    261 		}
    262 		if ( SLAP_DBHIDDEN( be )) {
    263 			continue;
    264 		}
    265 		if ( SLAP_MONITOR( be )) {
    266 			if( attr_merge_one( e, ad_monitorContext,
    267 					&be->be_suffix[0],
    268 					&be->be_nsuffix[0] ) )
    269 			{
    270 				goto fail;
    271 			}
    272 			continue;
    273 		}
    274 		if ( SLAP_CONFIG( be )) {
    275 			if( attr_merge_one( e, ad_configContext,
    276 					&be->be_suffix[0],
    277 					& be->be_nsuffix[0] ) )
    278 			{
    279 				goto fail;
    280 			}
    281 			continue;
    282 		}
    283 		if ( SLAP_GLUE_SUBORDINATE( be ) && !SLAP_GLUE_ADVERTISE( be ) ) {
    284 			continue;
    285 		}
    286 		if ( attr_merge( e, ad_namingContexts,
    287 				be->be_suffix, be->be_nsuffix ) ) {
    288 			goto fail;
    289 		}
    290 	}
    291 
    292 	/* altServer unsupported */
    293 
    294 	/* supportedControl */
    295 	if ( controls_root_dse_info( e ) != 0 ) {
    296 		goto fail;
    297 	}
    298 
    299 	/* supportedExtension */
    300 	if ( exop_root_dse_info( e ) != 0 ) {
    301 		goto fail;
    302 	}
    303 
    304 #ifdef LDAP_SLAPI
    305 	/* netscape supportedExtension */
    306 	for ( i = 0; (bv = slapi_int_get_supported_extop(i)) != NULL; i++ ) {
    307 		if( attr_merge_one( e, ad_supportedExtension, bv, NULL ) ) {
    308 			goto fail;
    309 		}
    310 	}
    311 #endif /* LDAP_SLAPI */
    312 
    313 	/* supportedFeatures */
    314 	if ( supportedFeatures == NULL ) {
    315 		supported_feature_init();
    316 	}
    317 
    318 	if( attr_merge( e, ad_supportedFeatures, supportedFeatures, NULL ) ) {
    319 		goto fail;
    320 	}
    321 
    322 	/* supportedLDAPVersion */
    323 		/* don't publish version 2 as we don't really support it
    324 		 * (even when configured to accept version 2 Bind requests)
    325 		 * and the value would never be used by true LDAPv2 (or LDAPv3)
    326 		 * clients.
    327 		 */
    328 	for ( i=LDAP_VERSION3; i<=LDAP_VERSION_MAX; i++ ) {
    329 		char buf[sizeof("255")];
    330 		snprintf(buf, sizeof buf, "%d", i);
    331 		val.bv_val = buf;
    332 		val.bv_len = strlen( val.bv_val );
    333 		if( attr_merge_one( e, ad_supportedLDAPVersion, &val, NULL ) ) {
    334 			goto fail;
    335 		}
    336 	}
    337 
    338 	/* supportedSASLMechanism */
    339 	supportedSASLMechanisms = slap_sasl_mechs( conn );
    340 
    341 	if( supportedSASLMechanisms != NULL ) {
    342 		for ( i=0; supportedSASLMechanisms[i] != NULL; i++ ) {
    343 			val.bv_val = supportedSASLMechanisms[i];
    344 			val.bv_len = strlen( val.bv_val );
    345 			if( attr_merge_one( e, ad_supportedSASLMechanisms, &val, NULL ) ) {
    346 				ldap_charray_free( supportedSASLMechanisms );
    347 				goto fail;
    348 			}
    349 		}
    350 		ldap_charray_free( supportedSASLMechanisms );
    351 	}
    352 
    353 	if ( default_referral != NULL ) {
    354 		if( attr_merge( e, ad_ref, default_referral, NULL /* FIXME */ ) ) {
    355 			goto fail;
    356 		}
    357 	}
    358 
    359 	if( usr_attr != NULL) {
    360 		Attribute *a;
    361 		for( a = usr_attr->e_attrs; a != NULL; a = a->a_next ) {
    362 			if( attr_merge( e, a->a_desc, a->a_vals,
    363 				(a->a_nvals == a->a_vals) ? NULL : a->a_nvals ) )
    364 			{
    365 				goto fail;
    366 			}
    367 		}
    368 	}
    369 
    370 	if ( extra_info ) {
    371 		entry_info_t	*ei = extra_info;
    372 
    373 		for ( ; ei; ei = ei->next ) {
    374 			ei->func( ei->arg, e );
    375 		}
    376 	}
    377 
    378 	*entry = e;
    379 	return LDAP_SUCCESS;
    380 }
    381 
    382 int
    383 root_dse_init( void )
    384 {
    385 	return 0;
    386 }
    387 
    388 int
    389 root_dse_destroy( void )
    390 {
    391 	if ( usr_attr ) {
    392 		entry_free( usr_attr );
    393 		usr_attr = NULL;
    394 	}
    395 
    396 	return 0;
    397 }
    398 
    399 /*
    400  * Read the entries specified in fname and merge the attributes
    401  * to the user defined rootDSE. Note thaat if we find any errors
    402  * what so ever, we will discard the entire entries, print an
    403  * error message and return.
    404  */
    405 int
    406 root_dse_read_file( const char *fname )
    407 {
    408 	struct LDIFFP	*fp;
    409 	int rc = 0, lmax = 0, ldifrc;
    410 	unsigned long lineno = 0;
    411 	char	*buf = NULL;
    412 
    413 	if ( (fp = ldif_open( fname, "r" )) == NULL ) {
    414 		Debug( LDAP_DEBUG_ANY,
    415 			"root_dse_read_file: could not open rootdse attr file \"%s\" - absolute path?\n",
    416 			fname );
    417 		perror( fname );
    418 		return EXIT_FAILURE;
    419 	}
    420 
    421 	usr_attr = entry_alloc();
    422 	if( usr_attr == NULL ) {
    423 		Debug( LDAP_DEBUG_ANY,
    424 			"root_dse_read_file: entry_alloc failed" );
    425 		ldif_close( fp );
    426 		return LDAP_OTHER;
    427 	}
    428 	usr_attr->e_attrs = NULL;
    429 
    430 	while(( ldifrc = ldif_read_record( fp, &lineno, &buf, &lmax )) > 0 ) {
    431 		Entry *e = str2entry( buf );
    432 		Attribute *a;
    433 
    434 		if( e == NULL ) {
    435 			Debug( LDAP_DEBUG_ANY, "root_dse_read_file: "
    436 				"could not parse entry (file=\"%s\" line=%lu)\n",
    437 				fname, lineno );
    438 			rc = LDAP_OTHER;
    439 			break;
    440 		}
    441 
    442 		/* make sure the DN is the empty DN */
    443 		if( e->e_nname.bv_len ) {
    444 			Debug( LDAP_DEBUG_ANY,
    445 				"root_dse_read_file: invalid rootDSE "
    446 				"- dn=\"%s\" (file=\"%s\" line=%lu)\n",
    447 				e->e_dn, fname, lineno );
    448 			entry_free( e );
    449 			rc = LDAP_OTHER;
    450 			break;
    451 		}
    452 
    453 		/*
    454 		 * we found a valid entry, so walk thru all the attributes in the
    455 		 * entry, and add each attribute type and description to the
    456 		 * usr_attr entry
    457 		 */
    458 
    459 		for(a = e->e_attrs; a != NULL; a = a->a_next) {
    460 			if( attr_merge( usr_attr, a->a_desc, a->a_vals,
    461 				(a->a_nvals == a->a_vals) ? NULL : a->a_nvals ) )
    462 			{
    463 				rc = LDAP_OTHER;
    464 				break;
    465 			}
    466 		}
    467 
    468 		entry_free( e );
    469 		if (rc) break;
    470 	}
    471 
    472 	if ( ldifrc < 0 )
    473 		rc = LDAP_OTHER;
    474 
    475 	if (rc) {
    476 		entry_free( usr_attr );
    477 		usr_attr = NULL;
    478 	}
    479 
    480 	ch_free( buf );
    481 
    482 	ldif_close( fp );
    483 
    484 	Debug(LDAP_DEBUG_CONFIG, "rootDSE file=\"%s\" read.\n", fname );
    485 	return rc;
    486 }
    487 
    488 int
    489 slap_discover_feature(
    490 	slap_bindconf	*sb,
    491 	const char	*attr,
    492 	const char	*val )
    493 {
    494 	LDAP		*ld = NULL;
    495 	LDAPMessage	*res = NULL, *entry;
    496 	int		rc, i;
    497 	struct berval	bv_val,
    498 			**values = NULL;
    499 	char		*attrs[ 2 ] = { NULL, NULL };
    500 
    501 	rc = slap_client_connect( &ld, sb );
    502 	if ( rc != LDAP_SUCCESS ) {
    503 		goto done;
    504 	}
    505 
    506 	attrs[ 0 ] = (char *) attr;
    507 	rc = ldap_search_ext_s( ld, "", LDAP_SCOPE_BASE, "(objectClass=*)",
    508 			attrs, 0, NULL, NULL, NULL, 0, &res );
    509 	if ( rc != LDAP_SUCCESS ) {
    510 		goto done;
    511 	}
    512 
    513 	entry = ldap_first_entry( ld, res );
    514 	if ( entry == NULL ) {
    515 		goto done;
    516 	}
    517 
    518 	values = ldap_get_values_len( ld, entry, attrs[ 0 ] );
    519 	if ( values == NULL ) {
    520 		rc = LDAP_NO_SUCH_ATTRIBUTE;
    521 		goto done;
    522 	}
    523 
    524 	ber_str2bv( val, 0, 0, &bv_val );
    525 	for ( i = 0; values[ i ] != NULL; i++ ) {
    526 		if ( bvmatch( &bv_val, values[ i ] ) ) {
    527 			rc = LDAP_COMPARE_TRUE;
    528 			goto done;
    529 		}
    530 	}
    531 
    532 	rc = LDAP_COMPARE_FALSE;
    533 
    534 done:;
    535 	if ( values != NULL ) {
    536 		ldap_value_free_len( values );
    537 	}
    538 
    539 	if ( res != NULL ) {
    540 		ldap_msgfree( res );
    541 	}
    542 
    543 	ldap_unbind_ext( ld, NULL, NULL );
    544 
    545 	return rc;
    546 }
    547 
    548