1 /* schema_check.c - routines to enforce schema definitions */
4 * Copyright 1998-2002 The OpenLDAP Foundation, All Rights Reserved.
5 * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
13 #include <ac/string.h>
14 #include <ac/socket.h>
19 static char * oc_check_required(
22 struct berval *ocname );
25 * entry_schema_check - check that entry e conforms to the schema required
26 * by its object class(es).
28 * returns 0 if so, non-zero otherwise.
37 char *textbuf, size_t textlen )
39 Attribute *a, *asc, *aoc;
43 AttributeDescription *ad_structuralObjectClass
44 = slap_schema.si_ad_structuralObjectClass;
45 AttributeDescription *ad_objectClass
46 = slap_schema.si_ad_objectClass;
48 int subentry = is_entry_subentry( e );
49 int collectiveSubentry = 0;
51 if( subentry) collectiveSubentry = is_entry_collectiveAttributeSubentry( e );
55 /* misc attribute checks */
56 for ( a = e->e_attrs; a != NULL; a = a->a_next ) {
57 const char *type = a->a_desc->ad_cname.bv_val;
59 /* there should be at least one value */
61 assert( a->a_vals[0].bv_val != NULL );
63 if( a->a_desc->ad_type->sat_check ) {
64 int rc = (a->a_desc->ad_type->sat_check)(
65 be, e, a, text, textbuf, textlen );
66 if( rc != LDAP_SUCCESS ) {
71 if( !collectiveSubentry && is_at_collective( a->a_desc->ad_type ) ) {
72 snprintf( textbuf, textlen,
73 "'%s' can only appear in collectiveAttributeSubentry",
75 return LDAP_OBJECT_CLASS_VIOLATION;
78 /* if single value type, check for multiple values */
79 if( is_at_single_value( a->a_desc->ad_type ) &&
80 a->a_vals[1].bv_val != NULL )
82 snprintf( textbuf, textlen,
83 "attribute '%s' cannot have multiple values",
87 LDAP_LOG(( "schema", LDAP_LEVEL_INFO,
88 "entry_schema_check: dn=\"%s\" %s\n",
91 Debug( LDAP_DEBUG_ANY,
93 e->e_dn, textbuf, 0 );
96 return LDAP_CONSTRAINT_VIOLATION;
100 /* it's a REALLY bad idea to disable schema checks */
101 if( !global_schemacheck ) return LDAP_SUCCESS;
103 /* find the object class attribute - could error out here */
104 asc = attr_find( e->e_attrs, ad_structuralObjectClass );
107 LDAP_LOG(( "schema", LDAP_LEVEL_INFO, "entry_schema_check: "
108 "No structuralObjectClass for entry (%s)\n",
111 Debug( LDAP_DEBUG_ANY,
112 "No structuralObjectClass for entry (%s)\n",
116 *text = "no structuralObjectClass operational attribute";
117 return LDAP_OBJECT_CLASS_VIOLATION;
120 assert( asc->a_vals != NULL );
121 assert( asc->a_vals[0].bv_val != NULL );
122 assert( asc->a_vals[1].bv_val == NULL );
124 sc = oc_bvfind( &asc->a_vals[0] );
126 snprintf( textbuf, textlen,
127 "unrecognized structuralObjectClass '%s'",
128 asc->a_vals[0].bv_val );
131 LDAP_LOG(( "schema", LDAP_LEVEL_INFO,
132 "entry_schema_check: dn (%s), %s\n",
135 Debug( LDAP_DEBUG_ANY,
136 "entry_check_schema(%s): %s\n",
137 e->e_dn, textbuf, 0 );
140 return LDAP_OBJECT_CLASS_VIOLATION;
143 if( sc->soc_kind != LDAP_SCHEMA_STRUCTURAL ) {
144 snprintf( textbuf, textlen,
145 "structuralObjectClass '%s' is not STRUCTURAL",
146 asc->a_vals[0].bv_val );
149 LDAP_LOG(( "schema", LDAP_LEVEL_INFO,
150 "entry_schema_check: dn (%s), %s\n",
153 Debug( LDAP_DEBUG_ANY,
154 "entry_check_schema(%s): %s\n",
155 e->e_dn, textbuf, 0 );
158 return LDAP_OBJECT_CLASS_VIOLATION;
161 /* find the object class attribute */
162 aoc = attr_find( e->e_attrs, ad_objectClass );
165 LDAP_LOG(( "schema", LDAP_LEVEL_INFO,
166 "entry_schema_check: No objectClass for entry (%s).\n"
169 Debug( LDAP_DEBUG_ANY, "No objectClass for entry (%s)\n",
173 *text = "no objectClass attribute";
174 return LDAP_OBJECT_CLASS_VIOLATION;
177 assert( aoc->a_vals != NULL );
178 assert( aoc->a_vals[0].bv_val != NULL );
180 rc = structural_class( aoc->a_vals, &nsc, &oc, text, textbuf, textlen );
181 if( rc != LDAP_SUCCESS ) {
183 } else if ( nsc.bv_len == 0 ) {
184 return LDAP_OBJECT_CLASS_VIOLATION;
190 snprintf( textbuf, textlen,
191 "unrecognized objectClass '%s'",
192 aoc->a_vals[0].bv_val );
193 return LDAP_OBJECT_CLASS_VIOLATION;
195 } else if ( sc != oc ) {
196 snprintf( textbuf, textlen,
197 "structuralObjectClass modification from '%s' to '%s' not allowed",
198 asc->a_vals[0].bv_val, nsc.bv_val );
199 return LDAP_NO_OBJECT_CLASS_MODS;
202 /* check that the entry has required attrs for each oc */
203 for ( i = 0; aoc->a_vals[i].bv_val != NULL; i++ ) {
204 if ( (oc = oc_bvfind( &aoc->a_vals[i] )) == NULL ) {
205 snprintf( textbuf, textlen,
206 "unrecognized objectClass '%s'",
207 aoc->a_vals[i].bv_val );
210 LDAP_LOG(( "schema", LDAP_LEVEL_INFO,
211 "entry_schema_check: dn (%s), %s\n",
214 Debug( LDAP_DEBUG_ANY,
215 "entry_check_schema(%s): %s\n",
216 e->e_dn, textbuf, 0 );
219 return LDAP_OBJECT_CLASS_VIOLATION;
222 if ( oc->soc_check ) {
223 int rc = (oc->soc_check)( be, e, oc,
224 text, textbuf, textlen );
225 if( rc != LDAP_SUCCESS ) {
230 if ( oc->soc_kind == LDAP_SCHEMA_ABSTRACT ) {
231 /* object class is abstract */
232 if ( oc != slap_schema.si_oc_top &&
233 !is_object_subclass( oc, sc ))
236 ObjectClass *xc = NULL;
237 for( j=0; aoc->a_vals[j].bv_val; j++ ) {
239 xc = oc_bvfind( &aoc->a_vals[i] );
241 snprintf( textbuf, textlen,
242 "unrecognized objectClass '%s'",
243 aoc->a_vals[i].bv_val );
246 LDAP_LOG(( "schema", LDAP_LEVEL_INFO,
247 "entry_schema_check: dn (%s), %s\n",
250 Debug( LDAP_DEBUG_ANY,
251 "entry_check_schema(%s): %s\n",
252 e->e_dn, textbuf, 0 );
255 return LDAP_OBJECT_CLASS_VIOLATION;
258 /* since we previous check against the
259 * structural object of this entry, the
260 * abstract class must be a (direct or indirect)
261 * superclass of one of the auxiliary classes of
264 if ( xc->soc_kind == LDAP_SCHEMA_AUXILIARY &&
265 is_object_subclass( oc, xc ) )
275 snprintf( textbuf, textlen, "instanstantiation of "
276 "abstract objectClass '%s' not allowed",
277 aoc->a_vals[i].bv_val );
280 LDAP_LOG(( "schema", LDAP_LEVEL_INFO,
281 "entry_schema_check: dn (%s), %s\n",
284 Debug( LDAP_DEBUG_ANY,
285 "entry_check_schema(%s): %s\n",
286 e->e_dn, textbuf, 0 );
289 return LDAP_OBJECT_CLASS_VIOLATION;
293 } else if ( oc->soc_kind != LDAP_SCHEMA_STRUCTURAL || oc == sc ) {
294 char *s = oc_check_required( e, oc, &aoc->a_vals[i] );
297 snprintf( textbuf, textlen,
298 "object class '%s' requires attribute '%s'",
299 aoc->a_vals[i].bv_val, s );
302 LDAP_LOG(( "schema", LDAP_LEVEL_INFO,
303 "entry_schema_check: dn=\"%s\" %s",
306 Debug( LDAP_DEBUG_ANY,
308 e->e_dn, textbuf, 0 );
311 return LDAP_OBJECT_CLASS_VIOLATION;
314 if( oc == slap_schema.si_oc_extensibleObject ) {
324 /* check that each attr in the entry is allowed by some oc */
325 for ( a = e->e_attrs; a != NULL; a = a->a_next ) {
326 int ret = oc_check_allowed( a->a_desc->ad_type, aoc->a_vals, sc );
327 if ( ret != LDAP_SUCCESS ) {
328 char *type = a->a_desc->ad_cname.bv_val;
330 snprintf( textbuf, textlen,
331 "attribute '%s' not allowed",
335 LDAP_LOG(( "schema", LDAP_LEVEL_INFO,
336 "entry_schema_check: dn=\"%s\" %s\n",
339 Debug( LDAP_DEBUG_ANY,
341 e->e_dn, textbuf, 0 );
355 struct berval *ocname )
362 LDAP_LOG(( "schema", LDAP_LEVEL_ENTRY,
363 "oc_check_required: dn (%s), objectClass \"%s\"\n",
364 e->e_dn, ocname->bv_val ));
366 Debug( LDAP_DEBUG_TRACE,
367 "oc_check_required entry (%s), objectClass \"%s\"\n",
368 e->e_dn, ocname->bv_val, 0 );
372 /* check for empty oc_required */
373 if(oc->soc_required == NULL) {
377 /* for each required attribute */
378 for ( i = 0; oc->soc_required[i] != NULL; i++ ) {
379 at = oc->soc_required[i];
380 /* see if it's in the entry */
381 for ( a = e->e_attrs; a != NULL; a = a->a_next ) {
382 if( a->a_desc->ad_type == at ) {
386 /* not there => schema violation */
388 return at->sat_cname.bv_val;
395 int oc_check_allowed(
403 LDAP_LOG(( "schema", LDAP_LEVEL_ENTRY,
404 "oc_check_allowed: type \"%s\"\n", at->sat_cname.bv_val ));
406 Debug( LDAP_DEBUG_TRACE,
407 "oc_check_allowed type \"%s\"\n",
408 at->sat_cname.bv_val, 0, 0 );
411 /* always allow objectClass attribute */
412 if ( strcasecmp( at->sat_cname.bv_val, "objectClass" ) == 0 ) {
417 * All operational attributions are allowed by schema rules.
419 if( is_at_operational(at) ) {
423 /* check to see if its allowed by the structuralObjectClass */
425 /* does it require the type? */
426 for ( j = 0; sc->soc_required != NULL &&
427 sc->soc_required[j] != NULL; j++ )
429 if( at == sc->soc_required[j] ) {
434 /* does it allow the type? */
435 for ( j = 0; sc->soc_allowed != NULL &&
436 sc->soc_allowed[j] != NULL; j++ )
438 if( at == sc->soc_allowed[j] ) {
444 /* check that the type appears as req or opt in at least one oc */
445 for ( i = 0; ocl[i].bv_val != NULL; i++ ) {
446 /* if we know about the oc */
447 ObjectClass *oc = oc_bvfind( &ocl[i] );
448 if ( oc != NULL && oc->soc_kind != LDAP_SCHEMA_ABSTRACT &&
449 ( sc == NULL || oc->soc_kind == LDAP_SCHEMA_AUXILIARY ))
451 /* does it require the type? */
452 for ( j = 0; oc->soc_required != NULL &&
453 oc->soc_required[j] != NULL; j++ )
455 if( at == oc->soc_required[j] ) {
459 /* does it allow the type? */
460 for ( j = 0; oc->soc_allowed != NULL &&
461 oc->soc_allowed[j] != NULL; j++ )
463 if( at == oc->soc_allowed[j] ) {
470 /* not allowed by any oc */
471 return LDAP_OBJECT_CLASS_VIOLATION;
475 * Determine the structural object class from a set of OIDs
477 int structural_class(
482 char *textbuf, size_t textlen )
486 ObjectClass *sc = NULL;
489 *text = "structural_class: internal error";
492 for( i=0; ocs[i].bv_val; i++ ) {
493 oc = oc_bvfind( &ocs[i] );
496 snprintf( textbuf, textlen,
497 "unrecongized objectClass '%s'",
500 return LDAP_OBJECT_CLASS_VIOLATION;
503 if( oc->soc_kind == LDAP_SCHEMA_STRUCTURAL ) {
504 if( sc == NULL || is_object_subclass( sc, oc ) ) {
508 } else if ( !is_object_subclass( oc, sc ) ) {
510 ObjectClass *xc = NULL;
512 /* find common superior */
513 for( j=i+1; ocs[j].bv_val; j++ ) {
514 xc = oc_bvfind( &ocs[j] );
517 snprintf( textbuf, textlen,
518 "unrecongized objectClass '%s'",
521 return LDAP_OBJECT_CLASS_VIOLATION;
524 if( xc->soc_kind != LDAP_SCHEMA_STRUCTURAL ) {
529 if( is_object_subclass( sc, xc ) &&
530 is_object_subclass( oc, xc ) )
532 /* found common subclass */
540 /* no common subclass */
541 snprintf( textbuf, textlen,
542 "invalid structural object class chain (%s/%s)",
543 ocs[scn].bv_val, ocs[i].bv_val );
545 return LDAP_OBJECT_CLASS_VIOLATION;
555 *text = "no structural object classes provided";
556 return LDAP_OBJECT_CLASS_VIOLATION;
564 * Return structural object class from list of modifications
566 int mods_structural_class(
570 char *textbuf, size_t textlen )
572 Modifications *ocmod = NULL;
574 for( ; mods != NULL; mods = mods->sml_next ) {
575 if( mods->sml_desc == slap_schema.si_ad_objectClass ) {
576 if( ocmod != NULL ) {
577 *text = "entry has multiple objectClass attributes";
578 return LDAP_OBJECT_CLASS_VIOLATION;
584 if( ocmod == NULL ) {
585 *text = "entry has no objectClass attribute";
586 return LDAP_OBJECT_CLASS_VIOLATION;
589 if( ocmod->sml_bvalues == NULL || ocmod->sml_bvalues[0].bv_val == NULL ) {
590 *text = "objectClass attribute has no values";
591 return LDAP_OBJECT_CLASS_VIOLATION;
594 return structural_class( ocmod->sml_bvalues, sc, NULL,
595 text, textbuf, textlen );