1 /* schema_check.c - routines to enforce schema definitions */
4 * Copyright 1998-2000 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.
33 Entry *e, Attribute *oldattrs,
35 char *textbuf, size_t textlen )
37 Attribute *a, *asc, *aoc;
41 AttributeDescription *ad_structuralObjectClass
42 = slap_schema.si_ad_structuralObjectClass;
43 AttributeDescription *ad_objectClass
44 = slap_schema.si_ad_objectClass;
49 /* check single-valued attrs for multiple values */
50 for ( a = e->e_attrs; a != NULL; a = a->a_next ) {
51 /* there should be at least one value */
53 assert( a->a_vals[0] != NULL );
55 /* if single value type, check for multiple values */
56 if( is_at_single_value( a->a_desc->ad_type ) &&
57 a->a_vals[1] != NULL )
59 char *type = a->a_desc->ad_cname.bv_val;
61 snprintf( textbuf, textlen,
62 "attribute '%s' cannot have multiple values",
66 LDAP_LOG(( "schema", LDAP_LEVEL_INFO,
67 "entry_schema_check: dn=\"%s\" %s\n",
70 Debug( LDAP_DEBUG_ANY,
72 e->e_dn, textbuf, 0 );
75 return LDAP_CONSTRAINT_VIOLATION;
79 /* it's a REALLY bad idea to disable schema checks */
80 if( !global_schemacheck ) return LDAP_SUCCESS;
82 /* find the object class attribute - could error out here */
83 asc = attr_find( e->e_attrs, ad_structuralObjectClass );
86 LDAP_LOG(( "schema", LDAP_LEVEL_INFO, "entry_schema_check: "
87 "No structuralObjectClass for entry (%s)\n",
90 Debug( LDAP_DEBUG_ANY,
91 "No structuralObjectClass for entry (%s)\n",
95 *text = "no structuralObjectClass operational attribute";
96 return LDAP_OBJECT_CLASS_VIOLATION;
99 assert( asc->a_vals != NULL );
100 assert( asc->a_vals[0] != NULL );
101 assert( asc->a_vals[1] == NULL );
103 sc = oc_bvfind( asc->a_vals[0] );
105 snprintf( textbuf, textlen,
106 "unrecognized structuralObjectClass '%s'",
107 aoc->a_vals[0]->bv_val );
110 LDAP_LOG(( "schema", LDAP_LEVEL_INFO,
111 "entry_schema_check: dn (%s), %s\n",
114 Debug( LDAP_DEBUG_ANY,
115 "entry_check_schema(%s): %s\n",
116 e->e_dn, textbuf, 0 );
119 return LDAP_OBJECT_CLASS_VIOLATION;
122 if( sc->soc_kind != LDAP_SCHEMA_STRUCTURAL ) {
123 snprintf( textbuf, textlen,
124 "structuralObjectClass '%s' is not STRUCTURAL",
125 aoc->a_vals[0]->bv_val );
128 LDAP_LOG(( "schema", LDAP_LEVEL_INFO,
129 "entry_schema_check: dn (%s), %s\n",
132 Debug( LDAP_DEBUG_ANY,
133 "entry_check_schema(%s): %s\n",
134 e->e_dn, textbuf, 0 );
137 return LDAP_OBJECT_CLASS_VIOLATION;
140 /* find the object class attribute */
141 aoc = attr_find( e->e_attrs, ad_objectClass );
144 LDAP_LOG(( "schema", LDAP_LEVEL_INFO,
145 "entry_schema_check: No objectClass for entry (%s).\n"
148 Debug( LDAP_DEBUG_ANY, "No objectClass for entry (%s)\n",
152 *text = "no objectClass attribute";
153 return LDAP_OBJECT_CLASS_VIOLATION;
156 assert( aoc->a_vals != NULL );
157 assert( aoc->a_vals[0] != NULL );
159 rc = structural_class( aoc->a_vals, &nsc, text, textbuf, textlen );
160 if( rc != LDAP_SUCCESS ) {
162 } else if ( nsc.bv_len == 0 ) {
163 return LDAP_OBJECT_CLASS_VIOLATION;
168 oc = oc_bvfind( &nsc );
170 snprintf( textbuf, textlen,
171 "unrecognized objectClass '%s'",
172 aoc->a_vals[i]->bv_val );
173 return LDAP_OBJECT_CLASS_VIOLATION;
175 } else if ( sc != oc ) {
176 snprintf( textbuf, textlen,
177 "structuralObjectClass modification from '%s' to '%s' not allowed",
178 asc->a_vals[0]->bv_val, nsc.bv_val );
179 return LDAP_NO_OBJECT_CLASS_MODS;
182 /* check that the entry has required attrs for each oc */
183 for ( i = 0; aoc->a_vals[i] != NULL; i++ ) {
184 if ( (oc = oc_bvfind( aoc->a_vals[i] )) == NULL ) {
185 snprintf( textbuf, textlen,
186 "unrecognized objectClass '%s'",
187 aoc->a_vals[i]->bv_val );
190 LDAP_LOG(( "schema", LDAP_LEVEL_INFO,
191 "entry_schema_check: dn (%s), %s\n",
194 Debug( LDAP_DEBUG_ANY,
195 "entry_check_schema(%s): %s\n",
196 e->e_dn, textbuf, 0 );
199 return LDAP_OBJECT_CLASS_VIOLATION;
201 } else if ( oc->soc_kind == LDAP_SCHEMA_ABSTRACT ) {
202 /* object class is abstract */
203 if ( oc != slap_schema.si_oc_top &&
204 !is_object_subclass( oc, sc ))
207 ObjectClass *xc = NULL;
208 for( j=0; aoc->a_vals[j]; j++ ) {
210 xc = oc_bvfind( aoc->a_vals[i] );
212 snprintf( textbuf, textlen,
213 "unrecognized objectClass '%s'",
214 aoc->a_vals[i]->bv_val );
217 LDAP_LOG(( "schema", LDAP_LEVEL_INFO,
218 "entry_schema_check: dn (%s), %s\n",
221 Debug( LDAP_DEBUG_ANY,
222 "entry_check_schema(%s): %s\n",
223 e->e_dn, textbuf, 0 );
226 return LDAP_OBJECT_CLASS_VIOLATION;
229 /* since we previous check against the
230 * structural object of this entry, the
231 * abstract class must be a (direct or indirect)
232 * superclass of one of the auxiliary classes of
235 if ( xc->soc_kind == LDAP_SCHEMA_AUXILIARY &&
236 is_object_subclass( oc, xc ) )
246 snprintf( textbuf, textlen, "instanstantiation of "
247 "abstract objectClass '%s' not allowed",
248 aoc->a_vals[i]->bv_val );
251 LDAP_LOG(( "schema", LDAP_LEVEL_INFO,
252 "entry_schema_check: dn (%s), %s\n",
255 Debug( LDAP_DEBUG_ANY,
256 "entry_check_schema(%s): %s\n",
257 e->e_dn, textbuf, 0 );
260 return LDAP_OBJECT_CLASS_VIOLATION;
264 } else if ( oc->soc_kind != LDAP_SCHEMA_STRUCTURAL || oc == sc ) {
265 char *s = oc_check_required( e, oc, aoc->a_vals[i] );
268 snprintf( textbuf, textlen,
269 "object class '%s' requires attribute '%s'",
270 aoc->a_vals[i]->bv_val, s );
273 LDAP_LOG(( "schema", LDAP_LEVEL_INFO,
274 "entry_schema_check: dn=\"%s\" %s",
277 Debug( LDAP_DEBUG_ANY,
279 e->e_dn, textbuf, 0 );
282 return LDAP_OBJECT_CLASS_VIOLATION;
285 if( oc == slap_schema.si_oc_extensibleObject ) {
295 /* check that each attr in the entry is allowed by some oc */
296 for ( a = e->e_attrs; a != NULL; a = a->a_next ) {
297 int ret = oc_check_allowed( a->a_desc->ad_type, aoc->a_vals, sc );
298 if ( ret != LDAP_SUCCESS ) {
299 char *type = a->a_desc->ad_cname.bv_val;
301 snprintf( textbuf, textlen,
302 "attribute '%s' not allowed",
306 LDAP_LOG(( "schema", LDAP_LEVEL_INFO,
307 "entry_schema_check: dn=\"%s\" %s\n",
310 Debug( LDAP_DEBUG_ANY,
312 e->e_dn, textbuf, 0 );
326 struct berval *ocname )
333 LDAP_LOG(( "schema", LDAP_LEVEL_ENTRY,
334 "oc_check_required: dn (%s), objectClass \"%s\"\n",
335 e->e_dn, ocname->bv_val ));
337 Debug( LDAP_DEBUG_TRACE,
338 "oc_check_required entry (%s), objectClass \"%s\"\n",
339 e->e_dn, ocname->bv_val, 0 );
343 /* check for empty oc_required */
344 if(oc->soc_required == NULL) {
348 /* for each required attribute */
349 for ( i = 0; oc->soc_required[i] != NULL; i++ ) {
350 at = oc->soc_required[i];
351 /* see if it's in the entry */
352 for ( a = e->e_attrs; a != NULL; a = a->a_next ) {
353 if( a->a_desc->ad_type == at ) {
357 /* not there => schema violation */
359 return at->sat_cname.bv_val;
366 int oc_check_allowed(
374 LDAP_LOG(( "schema", LDAP_LEVEL_ENTRY,
375 "oc_check_allowed: type \"%s\"\n", at->sat_cname.bv_val ));
377 Debug( LDAP_DEBUG_TRACE,
378 "oc_check_allowed type \"%s\"\n",
379 at->sat_cname.bv_val, 0, 0 );
382 /* always allow objectClass attribute */
383 if ( strcasecmp( at->sat_cname.bv_val, "objectClass" ) == 0 ) {
388 * All operational attributions are allowed by schema rules.
390 if( is_at_operational(at) ) {
394 /* check to see if its allowed by the structuralObjectClass */
396 /* does it require the type? */
397 for ( j = 0; sc->soc_required != NULL &&
398 sc->soc_required[j] != NULL; j++ )
400 if( at == sc->soc_required[j] ) {
405 /* does it allow the type? */
406 for ( j = 0; sc->soc_allowed != NULL &&
407 sc->soc_allowed[j] != NULL; j++ )
409 if( at == sc->soc_allowed[j] ) {
415 /* check that the type appears as req or opt in at least one oc */
416 for ( i = 0; ocl[i] != NULL; i++ ) {
417 /* if we know about the oc */
418 ObjectClass *oc = oc_bvfind( ocl[i] );
419 if ( oc != NULL && oc->soc_kind != LDAP_SCHEMA_ABSTRACT &&
420 ( sc == NULL || oc->soc_kind == LDAP_SCHEMA_AUXILIARY ))
422 /* does it require the type? */
423 for ( j = 0; oc->soc_required != NULL &&
424 oc->soc_required[j] != NULL; j++ )
426 if( at == oc->soc_required[j] ) {
430 /* does it allow the type? */
431 for ( j = 0; oc->soc_allowed != NULL &&
432 oc->soc_allowed[j] != NULL; j++ )
434 if( at == oc->soc_allowed[j] ) {
441 /* not allowed by any oc */
442 return LDAP_OBJECT_CLASS_VIOLATION;
446 * Determine the structural object class from a set of OIDs
448 int structural_class(
452 char *textbuf, size_t textlen )
456 ObjectClass *sc = NULL;
459 *text = "structural_class: internal error";
462 for( i=0; ocs[i]; i++ ) {
463 oc = oc_bvfind( ocs[i] );
466 snprintf( textbuf, textlen,
467 "unrecongized objectClass '%s'",
470 return LDAP_OBJECT_CLASS_VIOLATION;
473 if( oc->soc_kind == LDAP_SCHEMA_STRUCTURAL ) {
474 if( sc == NULL || is_object_subclass( sc, oc ) ) {
478 } else if ( !is_object_subclass( oc, sc ) ) {
480 ObjectClass *xc = NULL;
482 /* find common superior */
483 for( j=i+1; ocs[j]; j++ ) {
484 xc = oc_bvfind( ocs[j] );
487 snprintf( textbuf, textlen,
488 "unrecongized objectClass '%s'",
491 return LDAP_OBJECT_CLASS_VIOLATION;
494 if( xc->soc_kind != LDAP_SCHEMA_STRUCTURAL ) {
499 if( is_object_subclass( sc, xc ) &&
500 is_object_subclass( oc, xc ) )
502 /* found common subclass */
510 /* no common subclass */
511 snprintf( textbuf, textlen,
512 "invalid structural object class chain (%s/%s)",
513 ocs[scn]->bv_val, ocs[i]->bv_val );
515 return LDAP_OBJECT_CLASS_VIOLATION;
522 *text = "no structural object classes provided";
523 return LDAP_OBJECT_CLASS_VIOLATION;
531 * Return structural object class from list of modifications
533 int mods_structural_class(
537 char *textbuf, size_t textlen )
539 Modifications *ocmod = NULL;
541 for( ; mods != NULL; mods = mods->sml_next ) {
542 if( mods->sml_desc == slap_schema.si_ad_objectClass ) {
543 if( ocmod != NULL ) {
544 *text = "entry has multiple objectClass attributes";
545 return LDAP_OBJECT_CLASS_VIOLATION;
551 if( ocmod == NULL ) {
552 *text = "entry has no objectClass attribute";
553 return LDAP_OBJECT_CLASS_VIOLATION;
556 if( ocmod->sml_bvalues == NULL || ocmod->sml_bvalues[0] == NULL ) {
557 *text = "objectClass attribute has no values";
558 return LDAP_OBJECT_CLASS_VIOLATION;
561 return structural_class( ocmod->sml_bvalues, sc,
562 text, textbuf, textlen );