Home | History | Annotate | Line # | Download | only in slapd
      1 /*	$NetBSD: oc.c,v 1.4 2025/09/05 21:16:25 christos Exp $	*/
      2 
      3 /* oc.c - object class 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: oc.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 int is_object_subclass(
     33 	ObjectClass *sup,
     34 	ObjectClass *sub )
     35 {
     36 	int i;
     37 
     38 	if( sub == NULL || sup == NULL ) return 0;
     39 
     40 #if 0
     41 	Debug( LDAP_DEBUG_TRACE, "is_object_subclass(%s,%s) %d\n",
     42 		sup->soc_oid, sub->soc_oid, sup == sub );
     43 #endif
     44 
     45 	if ( sup == sub ) {
     46 		return 1;
     47 	}
     48 
     49 	if ( sub->soc_sups == NULL ) {
     50 		return 0;
     51 	}
     52 
     53 	for ( i = 0; sub->soc_sups[i] != NULL; i++ ) {
     54 		if ( is_object_subclass( sup, sub->soc_sups[i] ) ) {
     55 			return 1;
     56 		}
     57 	}
     58 
     59 	return 0;
     60 }
     61 
     62 int is_entry_objectclass(
     63 	Entry*	e,
     64 	ObjectClass *oc,
     65 	unsigned flags )
     66 {
     67 	/*
     68 	 * set_flags should only be true if oc is one of operational
     69 	 * object classes which we support objectClass flags for
     70 	 * (e.g., referral, alias, ...).  See <slap.h>.
     71 	 */
     72 
     73 	Attribute *attr;
     74 	struct berval *bv;
     75 
     76 	assert( !( e == NULL || oc == NULL ) );
     77 	assert( ( flags & SLAP_OCF_MASK ) != SLAP_OCF_MASK );
     78 
     79 	if ( e == NULL || oc == NULL ) {
     80 		return 0;
     81 	}
     82 
     83 	if ( flags == SLAP_OCF_SET_FLAGS && ( e->e_ocflags & SLAP_OC__END ) )
     84 	{
     85 		/* flags are set, use them */
     86 		return (e->e_ocflags & oc->soc_flags & SLAP_OC__MASK) != 0;
     87 	}
     88 
     89 	/*
     90 	 * find objectClass attribute
     91 	 */
     92 	attr = attr_find( e->e_attrs, slap_schema.si_ad_objectClass );
     93 	if ( attr == NULL ) {
     94 		/* no objectClass attribute */
     95 		Debug( LDAP_DEBUG_ANY, "is_entry_objectclass(\"%s\", \"%s\") "
     96 			"no objectClass attribute\n",
     97 			e->e_dn == NULL ? "" : e->e_dn,
     98 			oc->soc_oclass.oc_oid );
     99 
    100 		/* mark flags as set */
    101 		e->e_ocflags |= SLAP_OC__END;
    102 
    103 		return 0;
    104 	}
    105 
    106 	for ( bv = attr->a_vals; bv->bv_val; bv++ ) {
    107 		ObjectClass *objectClass = oc_bvfind( bv );
    108 
    109 		if ( objectClass == NULL ) {
    110 			/* FIXME: is this acceptable? */
    111 			continue;
    112 		}
    113 
    114 		if ( !( flags & SLAP_OCF_SET_FLAGS ) ) {
    115 			if ( objectClass == oc ) {
    116 				return 1;
    117 			}
    118 
    119 			if ( ( flags & SLAP_OCF_CHECK_SUP )
    120 				&& is_object_subclass( oc, objectClass ) )
    121 			{
    122 				return 1;
    123 			}
    124 		}
    125 
    126 		e->e_ocflags |= objectClass->soc_flags;
    127 	}
    128 
    129 	/* mark flags as set */
    130 	e->e_ocflags |= SLAP_OC__END;
    131 
    132 	return ( e->e_ocflags & oc->soc_flags & SLAP_OC__MASK ) != 0;
    133 }
    134 
    135 
    136 struct oindexrec {
    137 	struct berval oir_name;
    138 	ObjectClass	*oir_oc;
    139 };
    140 
    141 static Avlnode	*oc_index = NULL;
    142 static Avlnode	*oc_cache = NULL;
    143 static LDAP_STAILQ_HEAD(OCList, ObjectClass) oc_list
    144 	= LDAP_STAILQ_HEAD_INITIALIZER(oc_list);
    145 
    146 ObjectClass *oc_sys_tail;
    147 
    148 static int
    149 oc_index_cmp(
    150 	const void *v_oir1,
    151 	const void *v_oir2 )
    152 {
    153 	const struct oindexrec *oir1 = v_oir1, *oir2 = v_oir2;
    154 	int i = oir1->oir_name.bv_len - oir2->oir_name.bv_len;
    155 	if (i) return i;
    156 	return strcasecmp( oir1->oir_name.bv_val, oir2->oir_name.bv_val );
    157 }
    158 
    159 static int
    160 oc_index_name_cmp(
    161 	const void *v_name,
    162 	const void *v_oir )
    163 {
    164 	const struct berval    *name = v_name;
    165 	const struct oindexrec *oir  = v_oir;
    166 	int i = name->bv_len - oir->oir_name.bv_len;
    167 	if (i) return i;
    168 	return strncasecmp( name->bv_val, oir->oir_name.bv_val, name->bv_len );
    169 }
    170 
    171 ObjectClass *
    172 oc_find( const char *ocname )
    173 {
    174 	struct berval bv;
    175 
    176 	bv.bv_val = (char *)ocname;
    177 	bv.bv_len = strlen( ocname );
    178 
    179 	return( oc_bvfind( &bv ) );
    180 }
    181 
    182 ObjectClass *
    183 oc_bvfind( struct berval *ocname )
    184 {
    185 	struct oindexrec	*oir;
    186 
    187 	if ( oc_cache ) {
    188 		oir = ldap_avl_find( oc_cache, ocname, oc_index_name_cmp );
    189 		if ( oir ) return oir->oir_oc;
    190 	}
    191 	oir = ldap_avl_find( oc_index, ocname, oc_index_name_cmp );
    192 
    193 	if ( oir != NULL ) {
    194 		if ( at_oc_cache ) {
    195 			ldap_avl_insert( &oc_cache, (caddr_t) oir,
    196 				oc_index_cmp, ldap_avl_dup_error );
    197 		}
    198 		return( oir->oir_oc );
    199 	}
    200 
    201 	return( NULL );
    202 }
    203 
    204 static LDAP_STAILQ_HEAD(OCUList, ObjectClass) oc_undef_list
    205 	= LDAP_STAILQ_HEAD_INITIALIZER(oc_undef_list);
    206 
    207 ObjectClass *
    208 oc_bvfind_undef( struct berval *ocname )
    209 {
    210 	ObjectClass	*oc = oc_bvfind( ocname );
    211 
    212 	if ( oc ) {
    213 		return oc;
    214 	}
    215 
    216 	LDAP_STAILQ_FOREACH( oc, &oc_undef_list, soc_next ) {
    217 		int	d = oc->soc_cname.bv_len - ocname->bv_len;
    218 
    219 		if ( d ) {
    220 			continue;
    221 		}
    222 
    223 		if ( strcasecmp( oc->soc_cname.bv_val, ocname->bv_val ) == 0 ) {
    224 			break;
    225 		}
    226 	}
    227 
    228 	if ( oc ) {
    229 		return oc;
    230 	}
    231 
    232 	oc = ch_malloc( sizeof( ObjectClass ) + ocname->bv_len + 1 );
    233 	memset( oc, 0, sizeof( ObjectClass ) );
    234 
    235 	oc->soc_cname.bv_len = ocname->bv_len;
    236 	oc->soc_cname.bv_val = (char *)&oc[ 1 ];
    237 	AC_MEMCPY( oc->soc_cname.bv_val, ocname->bv_val, ocname->bv_len );
    238 	oc->soc_cname.bv_val[ oc->soc_cname.bv_len ] = '\0';
    239 
    240 	/* canonical to upper case */
    241 	ldap_pvt_str2upper( oc->soc_cname.bv_val );
    242 
    243 	LDAP_STAILQ_NEXT( oc, soc_next ) = NULL;
    244 	ldap_pvt_thread_mutex_lock( &oc_undef_mutex );
    245 	LDAP_STAILQ_INSERT_HEAD( &oc_undef_list, oc, soc_next );
    246 	ldap_pvt_thread_mutex_unlock( &oc_undef_mutex );
    247 
    248 	return oc;
    249 }
    250 
    251 static int
    252 oc_create_required(
    253 	ObjectClass		*soc,
    254 	char			**attrs,
    255 	int			*op,
    256 	const char		**err )
    257 {
    258 	char		**attrs1;
    259 	AttributeType	*sat;
    260 	AttributeType	**satp;
    261 	int		i;
    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, soc->soc_required) < 0) {
    275 				if ( at_append_to_list(sat, &soc->soc_required) ) {
    276 					*err = *attrs1;
    277 					return SLAP_SCHERR_OUTOFMEM;
    278 				}
    279 			}
    280 			attrs1++;
    281 		}
    282 		/* Now delete duplicates from the allowed list */
    283 		for ( satp = soc->soc_required; *satp; satp++ ) {
    284 			i = at_find_in_list(*satp, soc->soc_allowed);
    285 			if ( i >= 0 ) {
    286 				at_delete_from_list(i, &soc->soc_allowed);
    287 			}
    288 		}
    289 	}
    290 	return 0;
    291 }
    292 
    293 static int
    294 oc_create_allowed(
    295     ObjectClass		*soc,
    296     char		**attrs,
    297 	int			*op,
    298     const char		**err )
    299 {
    300 	char		**attrs1;
    301 	AttributeType	*sat;
    302 
    303 	if ( attrs ) {
    304 		attrs1 = attrs;
    305 		while ( *attrs1 ) {
    306 			sat = at_find(*attrs1);
    307 			if ( !sat ) {
    308 				*err = *attrs1;
    309 				return SLAP_SCHERR_ATTR_NOT_FOUND;
    310 			}
    311 
    312 			if( is_at_operational( sat )) (*op)++;
    313 
    314 			if ( at_find_in_list(sat, soc->soc_required) < 0 &&
    315 			     at_find_in_list(sat, soc->soc_allowed) < 0 ) {
    316 				if ( at_append_to_list(sat, &soc->soc_allowed) ) {
    317 					*err = *attrs1;
    318 					return SLAP_SCHERR_OUTOFMEM;
    319 				}
    320 			}
    321 			attrs1++;
    322 		}
    323 	}
    324 	return 0;
    325 }
    326 
    327 static int
    328 oc_add_sups(
    329 	ObjectClass		*soc,
    330 	char			**sups,
    331 	int			*op,
    332 	const char		**err )
    333 {
    334 	int		code;
    335 	ObjectClass	*soc1;
    336 	int		nsups;
    337 	char	**sups1;
    338 	int		add_sups = 0;
    339 
    340 	if ( sups ) {
    341 		if ( !soc->soc_sups ) {
    342 			/* We are at the first recursive level */
    343 			add_sups = 1;
    344 			nsups = 1;
    345 			sups1 = sups;
    346 			while ( *sups1 ) {
    347 				nsups++;
    348 				sups1++;
    349 			}
    350 			soc->soc_sups = (ObjectClass **)ch_calloc(nsups,
    351 					  sizeof(ObjectClass *));
    352 		}
    353 
    354 		nsups = 0;
    355 		sups1 = sups;
    356 		while ( *sups1 ) {
    357 			soc1 = oc_find(*sups1);
    358 			if ( !soc1 ) {
    359 				*err = *sups1;
    360 				return SLAP_SCHERR_CLASS_NOT_FOUND;
    361 			}
    362 
    363 			/* check object class usage
    364 			 * abstract classes can only sup abstract classes
    365 			 * structural classes can not sup auxiliary classes
    366 			 * auxiliary classes can not sup structural classes
    367 			 */
    368 			if( soc->soc_kind != soc1->soc_kind
    369 				&& soc1->soc_kind != LDAP_SCHEMA_ABSTRACT )
    370 			{
    371 				*err = *sups1;
    372 				return SLAP_SCHERR_CLASS_BAD_SUP;
    373 			}
    374 
    375 			if( soc1->soc_obsolete && !soc->soc_obsolete ) {
    376 				*err = *sups1;
    377 				return SLAP_SCHERR_CLASS_BAD_SUP;
    378 			}
    379 
    380 			if( soc->soc_flags & SLAP_OC_OPERATIONAL ) (*op)++;
    381 
    382 			if ( add_sups ) {
    383 				soc->soc_sups[nsups] = soc1;
    384 			}
    385 
    386 			code = oc_add_sups( soc, soc1->soc_sup_oids, op, err );
    387 			if ( code ) return code;
    388 
    389 			code = oc_create_required( soc, soc1->soc_at_oids_must, op, err );
    390 			if ( code ) return code;
    391 
    392 			code = oc_create_allowed( soc, soc1->soc_at_oids_may, op, err );
    393 			if ( code ) return code;
    394 
    395 			nsups++;
    396 			sups1++;
    397 		}
    398 	}
    399 
    400 	return 0;
    401 }
    402 
    403 static void
    404 oc_delete_names( ObjectClass *oc )
    405 {
    406 	char			**names = oc->soc_names;
    407 
    408 	if (!names) return;
    409 
    410 	while (*names) {
    411 		struct oindexrec	tmpoir, *oir;
    412 
    413 		ber_str2bv( *names, 0, 0, &tmpoir.oir_name );
    414 		tmpoir.oir_oc = oc;
    415 		oir = (struct oindexrec *)ldap_avl_delete( &oc_index,
    416 			(caddr_t)&tmpoir, oc_index_cmp );
    417 		assert( oir != NULL );
    418 		ldap_memfree( oir );
    419 		names++;
    420 	}
    421 }
    422 
    423 /* Mark the ObjectClass as deleted, remove from list, and remove all its
    424  * names from the AVL tree. Leave the OID in the tree.
    425  */
    426 void
    427 oc_delete( ObjectClass *oc )
    428 {
    429 	oc->soc_flags |= SLAP_OC_DELETED;
    430 
    431 	LDAP_STAILQ_REMOVE(&oc_list, oc, ObjectClass, soc_next);
    432 
    433 	oc_delete_names( oc );
    434 }
    435 
    436 static void
    437 oc_clean( ObjectClass *o )
    438 {
    439 	if (o->soc_sups) {
    440 		ldap_memfree(o->soc_sups);
    441 		o->soc_sups = NULL;
    442 	}
    443 	if (o->soc_required) {
    444 		ldap_memfree(o->soc_required);
    445 		o->soc_required = NULL;
    446 	}
    447 	if (o->soc_allowed) {
    448 		ldap_memfree(o->soc_allowed);
    449 		o->soc_allowed = NULL;
    450 	}
    451 	if (o->soc_oidmacro) {
    452 		ldap_memfree(o->soc_oidmacro);
    453 		o->soc_oidmacro = NULL;
    454 	}
    455 }
    456 
    457 static void
    458 oc_destroy_one( void *v )
    459 {
    460 	struct oindexrec *oir = v;
    461 	ObjectClass *o = oir->oir_oc;
    462 
    463 	oc_clean( o );
    464 	ldap_objectclass_free((LDAPObjectClass *)o);
    465 	ldap_memfree(oir);
    466 }
    467 
    468 void
    469 oc_destroy( void )
    470 {
    471 	ObjectClass *o;
    472 
    473 	while( !LDAP_STAILQ_EMPTY(&oc_list) ) {
    474 		o = LDAP_STAILQ_FIRST(&oc_list);
    475 		LDAP_STAILQ_REMOVE_HEAD(&oc_list, soc_next);
    476 
    477 		oc_delete_names( o );
    478 	}
    479 
    480 	ldap_avl_free( oc_index, oc_destroy_one );
    481 
    482 	while( !LDAP_STAILQ_EMPTY(&oc_undef_list) ) {
    483 		o = LDAP_STAILQ_FIRST(&oc_undef_list);
    484 		LDAP_STAILQ_REMOVE_HEAD(&oc_undef_list, soc_next);
    485 
    486 		ch_free( (ObjectClass *)o );
    487 	}
    488 }
    489 
    490 int
    491 oc_start( ObjectClass **oc )
    492 {
    493 	assert( oc != NULL );
    494 
    495 	*oc = LDAP_STAILQ_FIRST(&oc_list);
    496 
    497 	return (*oc != NULL);
    498 }
    499 
    500 int
    501 oc_next( ObjectClass **oc )
    502 {
    503 	assert( oc != NULL );
    504 
    505 #if 0	/* pedantic check: breaks when deleting an oc, don't use it. */
    506 	{
    507 		ObjectClass *tmp = NULL;
    508 
    509 		LDAP_STAILQ_FOREACH(tmp,&oc_list,soc_next) {
    510 			if ( tmp == *oc ) {
    511 				break;
    512 			}
    513 		}
    514 
    515 		assert( tmp != NULL );
    516 	}
    517 #endif
    518 
    519 	if ( *oc == NULL ) {
    520 		return 0;
    521 	}
    522 
    523 	*oc = LDAP_STAILQ_NEXT(*oc,soc_next);
    524 
    525 	return (*oc != NULL);
    526 }
    527 
    528 /*
    529  * check whether the two ObjectClasses actually __are__ identical,
    530  * or rather inconsistent
    531  */
    532 static int
    533 oc_check_dup(
    534 	ObjectClass	*soc,
    535 	ObjectClass	*new_soc )
    536 {
    537 	if ( new_soc->soc_oid != NULL ) {
    538 		if ( soc->soc_oid == NULL ) {
    539 			return SLAP_SCHERR_CLASS_INCONSISTENT;
    540 		}
    541 
    542 		if ( strcmp( soc->soc_oid, new_soc->soc_oid ) != 0 ) {
    543 			return SLAP_SCHERR_CLASS_INCONSISTENT;
    544 		}
    545 
    546 	} else {
    547 		if ( soc->soc_oid != NULL ) {
    548 			return SLAP_SCHERR_CLASS_INCONSISTENT;
    549 		}
    550 	}
    551 
    552 	if ( new_soc->soc_names ) {
    553 		int	i;
    554 
    555 		if ( soc->soc_names == NULL ) {
    556 			return SLAP_SCHERR_CLASS_INCONSISTENT;
    557 		}
    558 
    559 		for ( i = 0; new_soc->soc_names[ i ]; i++ ) {
    560 			if ( soc->soc_names[ i ] == NULL ) {
    561 				return SLAP_SCHERR_CLASS_INCONSISTENT;
    562 			}
    563 
    564 			if ( strcasecmp( soc->soc_names[ i ],
    565 					new_soc->soc_names[ i ] ) != 0 )
    566 			{
    567 				return SLAP_SCHERR_CLASS_INCONSISTENT;
    568 			}
    569 		}
    570 	} else {
    571 		if ( soc->soc_names != NULL ) {
    572 			return SLAP_SCHERR_CLASS_INCONSISTENT;
    573 		}
    574 	}
    575 
    576 	return SLAP_SCHERR_CLASS_DUP;
    577 }
    578 
    579 static struct oindexrec *oir_old;
    580 
    581 static int
    582 oc_dup_error( void *left, void *right )
    583 {
    584 	oir_old = left;
    585 	return -1;
    586 }
    587 
    588 static int
    589 oc_insert(
    590     ObjectClass		**roc,
    591 	ObjectClass		*prev,
    592     const char		**err )
    593 {
    594 	struct oindexrec	*oir;
    595 	char			**names;
    596 	ObjectClass		*soc = *roc;
    597 
    598 	if ( soc->soc_oid ) {
    599 		oir = (struct oindexrec *)
    600 			ch_calloc( 1, sizeof(struct oindexrec) );
    601 		ber_str2bv( soc->soc_oid, 0, 0, &oir->oir_name );
    602 		oir->oir_oc = soc;
    603 		oir_old = NULL;
    604 
    605 		if ( ldap_avl_insert( &oc_index, (caddr_t) oir,
    606 			oc_index_cmp, oc_dup_error ) )
    607 		{
    608 			ObjectClass	*old_soc;
    609 			int		rc;
    610 
    611 			*err = soc->soc_oid;
    612 
    613 			assert( oir_old != NULL );
    614 			old_soc = oir_old->oir_oc;
    615 
    616 			/* replacing a deleted definition? */
    617 			if ( old_soc->soc_flags & SLAP_OC_DELETED ) {
    618 				ObjectClass tmp;
    619 
    620 				/* Keep old oid, free new oid;
    621 				 * Keep new everything else, free old
    622 				 */
    623 				tmp = *old_soc;
    624 				*old_soc = *soc;
    625 				old_soc->soc_oid = tmp.soc_oid;
    626 				tmp.soc_oid = soc->soc_oid;
    627 				*soc = tmp;
    628 
    629 				oc_clean( soc );
    630 				oc_destroy_one( oir );
    631 
    632 				oir = oir_old;
    633 				soc = old_soc;
    634 				*roc = soc;
    635 			} else {
    636 				rc = oc_check_dup( old_soc, soc );
    637 
    638 				ldap_memfree( oir );
    639 				return rc;
    640 			}
    641 		}
    642 
    643 		/* FIX: temporal consistency check */
    644 		assert( oc_bvfind( &oir->oir_name ) != NULL );
    645 	}
    646 
    647 	assert( soc != NULL );
    648 
    649 	if ( (names = soc->soc_names) ) {
    650 		while ( *names ) {
    651 			oir = (struct oindexrec *)
    652 				ch_calloc( 1, sizeof(struct oindexrec) );
    653 			oir->oir_name.bv_val = *names;
    654 			oir->oir_name.bv_len = strlen( *names );
    655 			oir->oir_oc = soc;
    656 
    657 			if ( ldap_avl_insert( &oc_index, (caddr_t) oir,
    658 				oc_index_cmp, ldap_avl_dup_error ) )
    659 			{
    660 				ObjectClass	*old_soc;
    661 				int		rc;
    662 
    663 				*err = *names;
    664 
    665 				old_soc = oc_bvfind( &oir->oir_name );
    666 				assert( old_soc != NULL );
    667 				rc = oc_check_dup( old_soc, soc );
    668 
    669 				ldap_memfree( oir );
    670 
    671 				while ( names > soc->soc_names ) {
    672 					struct oindexrec	tmpoir;
    673 
    674 					names--;
    675 					ber_str2bv( *names, 0, 0, &tmpoir.oir_name );
    676 					tmpoir.oir_oc = soc;
    677 					oir = (struct oindexrec *)ldap_avl_delete( &oc_index,
    678 						(caddr_t)&tmpoir, oc_index_cmp );
    679 					assert( oir != NULL );
    680 					ldap_memfree( oir );
    681 				}
    682 
    683 				if ( soc->soc_oid ) {
    684 					struct oindexrec	tmpoir;
    685 
    686 					ber_str2bv( soc->soc_oid, 0, 0, &tmpoir.oir_name );
    687 					tmpoir.oir_oc = soc;
    688 					oir = (struct oindexrec *)ldap_avl_delete( &oc_index,
    689 						(caddr_t)&tmpoir, oc_index_cmp );
    690 					assert( oir != NULL );
    691 					ldap_memfree( oir );
    692 				}
    693 
    694 				return rc;
    695 			}
    696 
    697 			/* FIX: temporal consistency check */
    698 			assert( oc_bvfind(&oir->oir_name) != NULL );
    699 
    700 			names++;
    701 		}
    702 	}
    703 	if ( soc->soc_flags & SLAP_OC_HARDCODE ) {
    704 		prev = oc_sys_tail;
    705 		oc_sys_tail = soc;
    706 	}
    707 	if ( prev ) {
    708 		LDAP_STAILQ_INSERT_AFTER( &oc_list, prev, soc, soc_next );
    709 	} else {
    710 		LDAP_STAILQ_INSERT_TAIL( &oc_list, soc, soc_next );
    711 	}
    712 
    713 	return 0;
    714 }
    715 
    716 int
    717 oc_add(
    718     LDAPObjectClass	*oc,
    719 	int user,
    720 	ObjectClass		**rsoc,
    721 	ObjectClass		*prev,
    722     const char		**err )
    723 {
    724 	ObjectClass	*soc;
    725 	int		code;
    726 	int		op = 0;
    727 	char	*oidm = NULL;
    728 
    729 	if ( oc->oc_names != NULL ) {
    730 		int i;
    731 
    732 		for( i=0; oc->oc_names[i]; i++ ) {
    733 			if( !slap_valid_descr( oc->oc_names[i] ) ) {
    734 				return SLAP_SCHERR_BAD_DESCR;
    735 			}
    736 		}
    737 	}
    738 
    739 	if ( !OID_LEADCHAR( oc->oc_oid[0] )) {
    740 		/* Expand OID macros */
    741 		char *oid = oidm_find( oc->oc_oid );
    742 		if ( !oid ) {
    743 			*err = oc->oc_oid;
    744 			return SLAP_SCHERR_OIDM;
    745 		}
    746 		if ( oid != oc->oc_oid ) {
    747 			oidm = oc->oc_oid;
    748 			oc->oc_oid = oid;
    749 		}
    750 	}
    751 
    752 	soc = (ObjectClass *) ch_calloc( 1, sizeof(ObjectClass) );
    753 	AC_MEMCPY( &soc->soc_oclass, oc, sizeof(LDAPObjectClass) );
    754 
    755 	soc->soc_oidmacro = oidm;
    756 	if( oc->oc_names != NULL ) {
    757 		soc->soc_cname.bv_val = soc->soc_names[0];
    758 	} else {
    759 		soc->soc_cname.bv_val = soc->soc_oid;
    760 	}
    761 	soc->soc_cname.bv_len = strlen( soc->soc_cname.bv_val );
    762 
    763 	if( soc->soc_sup_oids == NULL &&
    764 		soc->soc_kind == LDAP_SCHEMA_STRUCTURAL )
    765 	{
    766 		/* structural object classes implicitly inherit from 'top' */
    767 		static char *top_oids[] = { SLAPD_TOP_OID, NULL };
    768 		code = oc_add_sups( soc, top_oids, &op, err );
    769 	} else {
    770 		code = oc_add_sups( soc, soc->soc_sup_oids, &op, err );
    771 	}
    772 
    773 	if ( code != 0 ) {
    774 		goto done;
    775 	}
    776 
    777 	if ( user && op ) {
    778 		code = SLAP_SCHERR_CLASS_BAD_SUP;
    779 		goto done;
    780 	}
    781 
    782 	code = oc_create_required( soc, soc->soc_at_oids_must, &op, err );
    783 	if ( code != 0 ) {
    784 		goto done;
    785 	}
    786 
    787 	code = oc_create_allowed( soc, soc->soc_at_oids_may, &op, err );
    788 	if ( code != 0 ) {
    789 		goto done;
    790 	}
    791 
    792 	if ( user && op ) {
    793 		code = SLAP_SCHERR_CLASS_BAD_USAGE;
    794 		goto done;
    795 	}
    796 
    797 	if ( !user ) {
    798 		soc->soc_flags |= SLAP_OC_HARDCODE;
    799 	}
    800 
    801 	code = oc_insert(&soc,prev,err);
    802 done:;
    803 	if ( code != 0 ) {
    804 		if ( soc->soc_sups ) {
    805 			ch_free( soc->soc_sups );
    806 		}
    807 
    808 		if ( soc->soc_required ) {
    809 			ch_free( soc->soc_required );
    810 		}
    811 
    812 		if ( soc->soc_allowed ) {
    813 			ch_free( soc->soc_allowed );
    814 		}
    815 
    816 		if ( soc->soc_oidmacro ) {
    817 			ch_free( soc->soc_oidmacro );
    818 		}
    819 
    820 		ch_free( soc );
    821 
    822 	} else if ( rsoc ) {
    823 		*rsoc = soc;
    824 	}
    825 	return code;
    826 }
    827 
    828 void
    829 oc_unparse( BerVarray *res, ObjectClass *start, ObjectClass *end, int sys )
    830 {
    831 	ObjectClass *oc;
    832 	int i, num;
    833 	struct berval bv, *bva = NULL, idx;
    834 	char ibuf[32];
    835 
    836 	if ( !start )
    837 		start = LDAP_STAILQ_FIRST( &oc_list );
    838 
    839 	/* count the result size */
    840 	i = 0;
    841 	for ( oc=start; oc; oc=LDAP_STAILQ_NEXT(oc, soc_next)) {
    842 		if ( sys && !(oc->soc_flags & SLAP_OC_HARDCODE)) break;
    843 		i++;
    844 		if ( oc == end ) break;
    845 	}
    846 	if (!i) return;
    847 
    848 	num = i;
    849 	bva = ch_malloc( (num+1) * sizeof(struct berval) );
    850 	BER_BVZERO( bva );
    851 	idx.bv_val = ibuf;
    852 	if ( sys ) {
    853 		idx.bv_len = 0;
    854 		ibuf[0] = '\0';
    855 	}
    856 	i = 0;
    857 	for ( oc=start; oc; oc=LDAP_STAILQ_NEXT(oc, soc_next)) {
    858 		LDAPObjectClass loc, *locp;
    859 		if ( sys && !(oc->soc_flags & SLAP_OC_HARDCODE)) break;
    860 		if ( oc->soc_oidmacro ) {
    861 			loc = oc->soc_oclass;
    862 			loc.oc_oid = oc->soc_oidmacro;
    863 			locp = &loc;
    864 		} else {
    865 			locp = &oc->soc_oclass;
    866 		}
    867 		if ( ldap_objectclass2bv( locp, &bv ) == NULL ) {
    868 			ber_bvarray_free( bva );
    869 		}
    870 		if ( !sys ) {
    871 			idx.bv_len = sprintf(idx.bv_val, "{%d}", i);
    872 		}
    873 		bva[i].bv_len = idx.bv_len + bv.bv_len;
    874 		bva[i].bv_val = ch_malloc( bva[i].bv_len + 1 );
    875 		strcpy( bva[i].bv_val, ibuf );
    876 		strcpy( bva[i].bv_val + idx.bv_len, bv.bv_val );
    877 		i++;
    878 		bva[i].bv_val = NULL;
    879 		ldap_memfree( bv.bv_val );
    880 		if ( oc == end ) break;
    881 	}
    882 	*res = bva;
    883 }
    884 
    885 int
    886 oc_schema_info( Entry *e )
    887 {
    888 	AttributeDescription *ad_objectClasses = slap_schema.si_ad_objectClasses;
    889 	ObjectClass	*oc;
    890 	struct berval	val;
    891 	struct berval	nval;
    892 
    893 	LDAP_STAILQ_FOREACH( oc, &oc_list, soc_next ) {
    894 		if( oc->soc_flags & SLAP_OC_HIDE ) continue;
    895 
    896 		if ( ldap_objectclass2bv( &oc->soc_oclass, &val ) == NULL ) {
    897 			return -1;
    898 		}
    899 
    900 		nval = oc->soc_cname;
    901 
    902 #if 0
    903 		Debug( LDAP_DEBUG_TRACE, "Merging oc [%ld] %s (%s)\n",
    904 	       (long) val.bv_len, val.bv_val, nval.bv_val );
    905 #endif
    906 
    907 		if( attr_merge_one( e, ad_objectClasses, &val, &nval ) ) {
    908 			return -1;
    909 		}
    910 		ldap_memfree( val.bv_val );
    911 	}
    912 	return 0;
    913 }
    914 
    915 int
    916 register_oc( const char *def, ObjectClass **soc, int dupok )
    917 {
    918 	LDAPObjectClass *oc;
    919 	int code;
    920 	const char *err;
    921 
    922 	oc = ldap_str2objectclass( def, &code, &err, LDAP_SCHEMA_ALLOW_ALL );
    923 	if ( !oc ) {
    924 		Debug( LDAP_DEBUG_ANY,
    925 			"register_oc: objectclass \"%s\": %s, %s\n",
    926 			def, ldap_scherr2str(code), err );
    927 		return code;
    928 	}
    929 	code = oc_add(oc,0,NULL,NULL,&err);
    930 	if ( code && ( code != SLAP_SCHERR_CLASS_DUP || !dupok )) {
    931 		Debug( LDAP_DEBUG_ANY,
    932 			"register_oc: objectclass \"%s\": %s, %s\n",
    933 			def, scherr2str(code), err );
    934 		ldap_objectclass_free(oc);
    935 		return code;
    936 	}
    937 	if ( soc )
    938 		*soc = oc_find(oc->oc_names[0]);
    939 	if ( code ) {
    940 		ldap_objectclass_free(oc);
    941 	} else {
    942 		ldap_memfree(oc);
    943 	}
    944 	return 0;
    945 }
    946