Home | History | Annotate | Line # | Download | only in slapd
      1 /*	$NetBSD: cr.c,v 1.4 2025/09/05 21:16:25 christos Exp $	*/
      2 
      3 /* cr.c - content rule routines */
      4 /* $OpenLDAP$ */
      5 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
      6  *
      7  * Copyright 1998-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: cr.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/ctype.h>
     27 #include <ac/string.h>
     28 #include <ac/socket.h>
     29 
     30 #include "slap.h"
     31 
     32 struct cindexrec {
     33 	struct berval	cir_name;
     34 	ContentRule	*cir_cr;
     35 };
     36 
     37 static Avlnode	*cr_index = NULL;
     38 static LDAP_STAILQ_HEAD(CRList, ContentRule) cr_list
     39 	= LDAP_STAILQ_HEAD_INITIALIZER(cr_list);
     40 
     41 static int
     42 cr_index_cmp(
     43     const void	*v_cir1,
     44     const void	*v_cir2 )
     45 {
     46 	const struct cindexrec	*cir1 = v_cir1;
     47 	const struct cindexrec	*cir2 = v_cir2;
     48 	int i = cir1->cir_name.bv_len - cir2->cir_name.bv_len;
     49 	if (i) return i;
     50 	return strcasecmp( cir1->cir_name.bv_val, cir2->cir_name.bv_val );
     51 }
     52 
     53 static int
     54 cr_index_name_cmp(
     55     const void	*v_name,
     56     const void	*v_cir )
     57 {
     58 	const struct berval    *name = v_name;
     59 	const struct cindexrec *cir  = v_cir;
     60 	int i = name->bv_len - cir->cir_name.bv_len;
     61 	if (i) return i;
     62 	return strncasecmp( name->bv_val, cir->cir_name.bv_val, name->bv_len );
     63 }
     64 
     65 ContentRule *
     66 cr_find( const char *crname )
     67 {
     68 	struct berval bv;
     69 
     70 	bv.bv_val = (char *)crname;
     71 	bv.bv_len = strlen( crname );
     72 
     73 	return( cr_bvfind( &bv ) );
     74 }
     75 
     76 ContentRule *
     77 cr_bvfind( struct berval *crname )
     78 {
     79 	struct cindexrec	*cir;
     80 
     81 	cir = ldap_avl_find( cr_index, crname, cr_index_name_cmp );
     82 
     83 	if ( cir != NULL ) {
     84 		return( cir->cir_cr );
     85 	}
     86 
     87 	return( NULL );
     88 }
     89 
     90 static int
     91 cr_destroy_one( ContentRule *c )
     92 {
     93 	assert( c != NULL );
     94 
     95 	if (c->scr_auxiliaries) ldap_memfree(c->scr_auxiliaries);
     96 	if (c->scr_required) ldap_memfree(c->scr_required);
     97 	if (c->scr_allowed) ldap_memfree(c->scr_allowed);
     98 	if (c->scr_precluded) ldap_memfree(c->scr_precluded);
     99 	ldap_contentrule_free((LDAPContentRule *)c);
    100 
    101 	return 0;
    102 }
    103 
    104 void
    105 cr_destroy( void )
    106 {
    107 	ContentRule *c;
    108 
    109 	ldap_avl_free(cr_index, ldap_memfree);
    110 
    111 	while( !LDAP_STAILQ_EMPTY(&cr_list) ) {
    112 		c = LDAP_STAILQ_FIRST(&cr_list);
    113 		LDAP_STAILQ_REMOVE_HEAD(&cr_list, scr_next);
    114 
    115 		cr_destroy_one( c );
    116 	}
    117 }
    118 
    119 static int
    120 cr_insert(
    121     ContentRule		*scr,
    122     const char		**err
    123 )
    124 {
    125 	struct cindexrec	*cir;
    126 	char			**names;
    127 
    128 	assert( scr != NULL );
    129 
    130 	if ( scr->scr_oid ) {
    131 		cir = (struct cindexrec *)
    132 			ch_calloc( 1, sizeof(struct cindexrec) );
    133 		cir->cir_name.bv_val = scr->scr_oid;
    134 		cir->cir_name.bv_len = strlen( scr->scr_oid );
    135 		cir->cir_cr = scr;
    136 
    137 		if ( ldap_avl_insert( &cr_index, (caddr_t) cir,
    138 		                 cr_index_cmp, ldap_avl_dup_error ) )
    139 		{
    140 			*err = scr->scr_oid;
    141 			ldap_memfree(cir);
    142 			return SLAP_SCHERR_CR_DUP;
    143 		}
    144 
    145 		/* FIX: temporal consistency check */
    146 		assert( cr_bvfind(&cir->cir_name) != NULL );
    147 	}
    148 
    149 	if ( (names = scr->scr_names) ) {
    150 		while ( *names ) {
    151 			cir = (struct cindexrec *)
    152 				ch_calloc( 1, sizeof(struct cindexrec) );
    153 			cir->cir_name.bv_val = *names;
    154 			cir->cir_name.bv_len = strlen( *names );
    155 			cir->cir_cr = scr;
    156 
    157 			if ( ldap_avl_insert( &cr_index, (caddr_t) cir,
    158 			                 cr_index_cmp, ldap_avl_dup_error ) )
    159 			{
    160 				*err = *names;
    161 				ldap_memfree(cir);
    162 				return SLAP_SCHERR_CR_DUP;
    163 			}
    164 
    165 			/* FIX: temporal consistency check */
    166 			assert( cr_bvfind(&cir->cir_name) != NULL );
    167 
    168 			names++;
    169 		}
    170 	}
    171 
    172 	LDAP_STAILQ_INSERT_TAIL(&cr_list, scr, scr_next);
    173 
    174 	return 0;
    175 }
    176 
    177 static int
    178 cr_add_auxiliaries(
    179     ContentRule		*scr,
    180 	int			*op,
    181     const char		**err )
    182 {
    183 	int naux;
    184 
    185 	if( scr->scr_oc_oids_aux == NULL ) return 0;
    186 
    187 	for( naux=0; scr->scr_oc_oids_aux[naux]; naux++ ) {
    188 		/* count them */ ;
    189 	}
    190 
    191 	scr->scr_auxiliaries = ch_calloc( naux+1, sizeof(ObjectClass *));
    192 
    193 	for( naux=0; scr->scr_oc_oids_aux[naux]; naux++ ) {
    194 		ObjectClass *soc = scr->scr_auxiliaries[naux]
    195 			= oc_find(scr->scr_oc_oids_aux[naux]);
    196 		if ( !soc ) {
    197 			*err = scr->scr_oc_oids_aux[naux];
    198 			return SLAP_SCHERR_CLASS_NOT_FOUND;
    199 		}
    200 
    201 		if( soc->soc_flags & SLAP_OC_OPERATIONAL &&
    202 			soc != slap_schema.si_oc_extensibleObject )
    203 		{
    204 			(*op)++;
    205 		}
    206 
    207 		if( soc->soc_kind != LDAP_SCHEMA_AUXILIARY ) {
    208 			*err = scr->scr_oc_oids_aux[naux];
    209 			return SLAP_SCHERR_CR_BAD_AUX;
    210 		}
    211 	}
    212 
    213 	scr->scr_auxiliaries[naux] = NULL;
    214 	return 0;
    215 }
    216 
    217 static int
    218 cr_create_required(
    219     ContentRule		*scr,
    220 	int			*op,
    221     const char		**err )
    222 {
    223     char		**attrs = scr->scr_at_oids_must;
    224 	char		**attrs1;
    225 	AttributeType	*sat;
    226 
    227 	if ( attrs ) {
    228 		attrs1 = attrs;
    229 		while ( *attrs1 ) {
    230 			sat = at_find(*attrs1);
    231 			if ( !sat ) {
    232 				*err = *attrs1;
    233 				return SLAP_SCHERR_ATTR_NOT_FOUND;
    234 			}
    235 
    236 			if( is_at_operational( sat )) (*op)++;
    237 
    238 			if ( at_find_in_list(sat, scr->scr_required) < 0) {
    239 				if ( at_append_to_list(sat, &scr->scr_required) ) {
    240 					*err = *attrs1;
    241 					return SLAP_SCHERR_OUTOFMEM;
    242 				}
    243 			} else {
    244 				*err = *attrs1;
    245 				return SLAP_SCHERR_CR_BAD_AT;
    246 			}
    247 			attrs1++;
    248 		}
    249 	}
    250 	return 0;
    251 }
    252 
    253 static int
    254 cr_create_allowed(
    255     ContentRule		*scr,
    256 	int			*op,
    257     const char		**err )
    258 {
    259     char		**attrs = scr->scr_at_oids_may;
    260 	char		**attrs1;
    261 	AttributeType	*sat;
    262 
    263 	if ( attrs ) {
    264 		attrs1 = attrs;
    265 		while ( *attrs1 ) {
    266 			sat = at_find(*attrs1);
    267 			if ( !sat ) {
    268 				*err = *attrs1;
    269 				return SLAP_SCHERR_ATTR_NOT_FOUND;
    270 			}
    271 
    272 			if( is_at_operational( sat )) (*op)++;
    273 
    274 			if ( at_find_in_list(sat, scr->scr_required) < 0 &&
    275 				at_find_in_list(sat, scr->scr_allowed) < 0 )
    276 			{
    277 				if ( at_append_to_list(sat, &scr->scr_allowed) ) {
    278 					*err = *attrs1;
    279 					return SLAP_SCHERR_OUTOFMEM;
    280 				}
    281 			} else {
    282 				*err = *attrs1;
    283 				return SLAP_SCHERR_CR_BAD_AT;
    284 			}
    285 			attrs1++;
    286 		}
    287 	}
    288 	return 0;
    289 }
    290 
    291 static int
    292 cr_create_precluded(
    293     ContentRule		*scr,
    294 	int			*op,
    295     const char		**err )
    296 {
    297     char		**attrs = scr->scr_at_oids_not;
    298 	char		**attrs1;
    299 	AttributeType	*sat;
    300 
    301 	if ( attrs ) {
    302 		attrs1 = attrs;
    303 		while ( *attrs1 ) {
    304 			sat = at_find(*attrs1);
    305 			if ( !sat ) {
    306 				*err = *attrs1;
    307 				return SLAP_SCHERR_ATTR_NOT_FOUND;
    308 			}
    309 
    310 			if( is_at_operational( sat )) (*op)++;
    311 
    312 			/* FIXME: should also make sure attribute type is not
    313 				a required attribute of the structural class or
    314 				any auxiliary class */
    315 			if ( at_find_in_list(sat, scr->scr_required) < 0 &&
    316 				at_find_in_list(sat, scr->scr_allowed) < 0 &&
    317 				at_find_in_list(sat, scr->scr_precluded) < 0 )
    318 			{
    319 				if ( at_append_to_list(sat, &scr->scr_precluded) ) {
    320 					*err = *attrs1;
    321 					return SLAP_SCHERR_OUTOFMEM;
    322 				}
    323 			} else {
    324 				*err = *attrs1;
    325 				return SLAP_SCHERR_CR_BAD_AT;
    326 			}
    327 			attrs1++;
    328 		}
    329 	}
    330 	return 0;
    331 }
    332 
    333 int
    334 cr_add(
    335     LDAPContentRule	*cr,
    336 	int user,
    337 	ContentRule **rscr,
    338     const char		**err
    339 )
    340 {
    341 	ContentRule	*scr;
    342 	int		code;
    343 	int		op = 0;
    344 	char	*oidm = NULL;
    345 
    346 	if ( cr->cr_names != NULL ) {
    347 		int i;
    348 
    349 		for( i=0; cr->cr_names[i]; i++ ) {
    350 			if( !slap_valid_descr( cr->cr_names[i] ) ) {
    351 				return SLAP_SCHERR_BAD_DESCR;
    352 			}
    353 		}
    354 	}
    355 
    356 	if ( !OID_LEADCHAR( cr->cr_oid[0] )) {
    357 		/* Expand OID macros */
    358 		char *oid = oidm_find( cr->cr_oid );
    359 		if ( !oid ) {
    360 			*err = cr->cr_oid;
    361 			return SLAP_SCHERR_OIDM;
    362 		}
    363 		if ( oid != cr->cr_oid ) {
    364 			oidm = cr->cr_oid;
    365 			cr->cr_oid = oid;
    366 		}
    367 	}
    368 
    369 	scr = (ContentRule *) ch_calloc( 1, sizeof(ContentRule) );
    370 	AC_MEMCPY( &scr->scr_crule, cr, sizeof(LDAPContentRule) );
    371 
    372 	scr->scr_oidmacro = oidm;
    373 	scr->scr_sclass = oc_find(cr->cr_oid);
    374 	if ( !scr->scr_sclass ) {
    375 		*err = cr->cr_oid;
    376 		code = SLAP_SCHERR_CLASS_NOT_FOUND;
    377 		goto fail;
    378 	}
    379 
    380 	/* check object class usage */
    381 	if( scr->scr_sclass->soc_kind != LDAP_SCHEMA_STRUCTURAL )
    382 	{
    383 		*err = cr->cr_oid;
    384 		code = SLAP_SCHERR_CR_BAD_STRUCT;
    385 		goto fail;
    386 	}
    387 
    388 	if( scr->scr_sclass->soc_flags & SLAP_OC_OPERATIONAL ) op++;
    389 
    390 	code = cr_add_auxiliaries( scr, &op, err );
    391 	if ( code != 0 ) goto fail;
    392 
    393 	code = cr_create_required( scr, &op, err );
    394 	if ( code != 0 ) goto fail;
    395 
    396 	code = cr_create_allowed( scr, &op, err );
    397 	if ( code != 0 ) goto fail;
    398 
    399 	code = cr_create_precluded( scr, &op, err );
    400 	if ( code != 0 ) goto fail;
    401 
    402 	if( user && op ) {
    403 		code = SLAP_SCHERR_CR_BAD_AUX;
    404 		goto fail;
    405 	}
    406 
    407 	code = cr_insert(scr,err);
    408 	if ( code == 0 && rscr )
    409 		*rscr = scr;
    410 	return code;
    411 fail:
    412 	ch_free( scr );
    413 	return code;
    414 }
    415 
    416 void
    417 cr_unparse( BerVarray *res, ContentRule *start, ContentRule *end, int sys )
    418 {
    419 	ContentRule *cr;
    420 	int i, num;
    421 	struct berval bv, *bva = NULL, idx;
    422 	char ibuf[32];
    423 
    424 	if ( !start )
    425 		start = LDAP_STAILQ_FIRST( &cr_list );
    426 
    427 	/* count the result size */
    428 	i = 0;
    429 	for ( cr=start; cr; cr=LDAP_STAILQ_NEXT(cr, scr_next)) {
    430 		if ( sys && !(cr->scr_flags & SLAP_CR_HARDCODE)) continue;
    431 		i++;
    432 		if ( cr == end ) break;
    433 	}
    434 	if (!i) return;
    435 
    436 	num = i;
    437 	bva = ch_malloc( (num+1) * sizeof(struct berval) );
    438 	BER_BVZERO( bva );
    439 	idx.bv_val = ibuf;
    440 	if ( sys ) {
    441 		idx.bv_len = 0;
    442 		ibuf[0] = '\0';
    443 	}
    444 	i = 0;
    445 	for ( cr=start; cr; cr=LDAP_STAILQ_NEXT(cr, scr_next)) {
    446 		LDAPContentRule lcr, *lcrp;
    447 		if ( sys && !(cr->scr_flags & SLAP_CR_HARDCODE)) continue;
    448 		if ( cr->scr_oidmacro ) {
    449 			lcr = cr->scr_crule;
    450 			lcr.cr_oid = cr->scr_oidmacro;
    451 			lcrp = &lcr;
    452 		} else {
    453 			lcrp = &cr->scr_crule;
    454 		}
    455 		if ( ldap_contentrule2bv( lcrp, &bv ) == NULL ) {
    456 			ber_bvarray_free( bva );
    457 		}
    458 		if ( !sys ) {
    459 			idx.bv_len = sprintf(idx.bv_val, "{%d}", i);
    460 		}
    461 		bva[i].bv_len = idx.bv_len + bv.bv_len;
    462 		bva[i].bv_val = ch_malloc( bva[i].bv_len + 1 );
    463 		strcpy( bva[i].bv_val, ibuf );
    464 		strcpy( bva[i].bv_val + idx.bv_len, bv.bv_val );
    465 		i++;
    466 		bva[i].bv_val = NULL;
    467 		ldap_memfree( bv.bv_val );
    468 		if ( cr == end ) break;
    469 	}
    470 	*res = bva;
    471 }
    472 
    473 int
    474 cr_schema_info( Entry *e )
    475 {
    476 	AttributeDescription *ad_ditContentRules
    477 		= slap_schema.si_ad_ditContentRules;
    478 	ContentRule	*cr;
    479 
    480 	struct berval	val;
    481 	struct berval	nval;
    482 
    483 	LDAP_STAILQ_FOREACH(cr, &cr_list, scr_next) {
    484 		if ( ldap_contentrule2bv( &cr->scr_crule, &val ) == NULL ) {
    485 			return -1;
    486 		}
    487 
    488 #if 0
    489 		if( cr->scr_flags & SLAP_CR_HIDE ) continue;
    490 #endif
    491 #if 0
    492 		Debug( LDAP_DEBUG_TRACE, "Merging cr [%ld] %s\n",
    493 	       (long) val.bv_len, val.bv_val );
    494 #endif
    495 
    496 		nval.bv_val = cr->scr_oid;
    497 		nval.bv_len = strlen(cr->scr_oid);
    498 
    499 		if( attr_merge_one( e, ad_ditContentRules, &val, &nval ) )
    500 		{
    501 			return -1;
    502 		}
    503 		ldap_memfree( val.bv_val );
    504 	}
    505 	return 0;
    506 }
    507