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 );
51 if( subentry) collective = is_entry_collectiveAttributes( 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( !collective && is_at_collective( a->a_desc->ad_type ) ) {
72 snprintf( textbuf, textlen, "attribute '%s' "
73 "may only appear in collectiveAttributes subentry",
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->sco_check ) {
223 int rc = (oc->sco_check)( be, e, oc,
224 text, textbuf, textlen );
225 if( rc != LDAP_SUCCESS ) {
229 if ( oc->soc_kind == LDAP_SCHEMA_ABSTRACT ) {
230 /* object class is abstract */
231 if ( oc != slap_schema.si_oc_top &&
232 !is_object_subclass( oc, sc ))
235 ObjectClass *xc = NULL;
236 for( j=0; aoc->a_vals[j].bv_val; j++ ) {
238 xc = oc_bvfind( &aoc->a_vals[i] );
240 snprintf( textbuf, textlen,
241 "unrecognized objectClass '%s'",
242 aoc->a_vals[i].bv_val );
245 LDAP_LOG(( "schema", LDAP_LEVEL_INFO,
246 "entry_schema_check: dn (%s), %s\n",
249 Debug( LDAP_DEBUG_ANY,
250 "entry_check_schema(%s): %s\n",
251 e->e_dn, textbuf, 0 );
254 return LDAP_OBJECT_CLASS_VIOLATION;
257 /* since we previous check against the
258 * structural object of this entry, the
259 * abstract class must be a (direct or indirect)
260 * superclass of one of the auxiliary classes of
263 if ( xc->soc_kind == LDAP_SCHEMA_AUXILIARY &&
264 is_object_subclass( oc, xc ) )
274 snprintf( textbuf, textlen, "instanstantiation of "
275 "abstract objectClass '%s' not allowed",
276 aoc->a_vals[i].bv_val );
279 LDAP_LOG(( "schema", LDAP_LEVEL_INFO,
280 "entry_schema_check: dn (%s), %s\n",
283 Debug( LDAP_DEBUG_ANY,
284 "entry_check_schema(%s): %s\n",
285 e->e_dn, textbuf, 0 );
288 return LDAP_OBJECT_CLASS_VIOLATION;
292 } else if ( oc->soc_kind != LDAP_SCHEMA_STRUCTURAL || oc == sc ) {
293 char *s = oc_check_required( e, oc, &aoc->a_vals[i] );
296 snprintf( textbuf, textlen,
297 "object class '%s' requires attribute '%s'",
298 aoc->a_vals[i].bv_val, s );
301 LDAP_LOG(( "schema", LDAP_LEVEL_INFO,
302 "entry_schema_check: dn=\"%s\" %s",
305 Debug( LDAP_DEBUG_ANY,
307 e->e_dn, textbuf, 0 );
310 return LDAP_OBJECT_CLASS_VIOLATION;
313 if( oc == slap_schema.si_oc_extensibleObject ) {
323 /* check that each attr in the entry is allowed by some oc */
324 for ( a = e->e_attrs; a != NULL; a = a->a_next ) {
325 int ret = oc_check_allowed( a->a_desc->ad_type, aoc->a_vals, sc );
326 if ( ret != LDAP_SUCCESS ) {
327 char *type = a->a_desc->ad_cname.bv_val;
329 snprintf( textbuf, textlen,
330 "attribute '%s' not allowed",
334 LDAP_LOG(( "schema", LDAP_LEVEL_INFO,
335 "entry_schema_check: dn=\"%s\" %s\n",
338 Debug( LDAP_DEBUG_ANY,
340 e->e_dn, textbuf, 0 );
354 struct berval *ocname )
361 LDAP_LOG(( "schema", LDAP_LEVEL_ENTRY,
362 "oc_check_required: dn (%s), objectClass \"%s\"\n",
363 e->e_dn, ocname->bv_val ));
365 Debug( LDAP_DEBUG_TRACE,
366 "oc_check_required entry (%s), objectClass \"%s\"\n",
367 e->e_dn, ocname->bv_val, 0 );
371 /* check for empty oc_required */
372 if(oc->soc_required == NULL) {
376 /* for each required attribute */
377 for ( i = 0; oc->soc_required[i] != NULL; i++ ) {
378 at = oc->soc_required[i];
379 /* see if it's in the entry */
380 for ( a = e->e_attrs; a != NULL; a = a->a_next ) {
381 if( a->a_desc->ad_type == at ) {
385 /* not there => schema violation */
387 return at->sat_cname.bv_val;
394 int oc_check_allowed(
402 LDAP_LOG(( "schema", LDAP_LEVEL_ENTRY,
403 "oc_check_allowed: type \"%s\"\n", at->sat_cname.bv_val ));
405 Debug( LDAP_DEBUG_TRACE,
406 "oc_check_allowed type \"%s\"\n",
407 at->sat_cname.bv_val, 0, 0 );
410 /* always allow objectClass attribute */
411 if ( strcasecmp( at->sat_cname.bv_val, "objectClass" ) == 0 ) {
416 * All operational attributions are allowed by schema rules.
418 if( is_at_operational(at) ) {
422 /* check to see if its allowed by the structuralObjectClass */
424 /* does it require the type? */
425 for ( j = 0; sc->soc_required != NULL &&
426 sc->soc_required[j] != NULL; j++ )
428 if( at == sc->soc_required[j] ) {
433 /* does it allow the type? */
434 for ( j = 0; sc->soc_allowed != NULL &&
435 sc->soc_allowed[j] != NULL; j++ )
437 if( at == sc->soc_allowed[j] ) {
443 /* check that the type appears as req or opt in at least one oc */
444 for ( i = 0; ocl[i].bv_val != NULL; i++ ) {
445 /* if we know about the oc */
446 ObjectClass *oc = oc_bvfind( &ocl[i] );
447 if ( oc != NULL && oc->soc_kind != LDAP_SCHEMA_ABSTRACT &&
448 ( sc == NULL || oc->soc_kind == LDAP_SCHEMA_AUXILIARY ))
450 /* does it require the type? */
451 for ( j = 0; oc->soc_required != NULL &&
452 oc->soc_required[j] != NULL; j++ )
454 if( at == oc->soc_required[j] ) {
458 /* does it allow the type? */
459 for ( j = 0; oc->soc_allowed != NULL &&
460 oc->soc_allowed[j] != NULL; j++ )
462 if( at == oc->soc_allowed[j] ) {
469 /* not allowed by any oc */
470 return LDAP_OBJECT_CLASS_VIOLATION;
474 * Determine the structural object class from a set of OIDs
476 int structural_class(
481 char *textbuf, size_t textlen )
485 ObjectClass *sc = NULL;
488 *text = "structural_class: internal error";
491 for( i=0; ocs[i].bv_val; i++ ) {
492 oc = oc_bvfind( &ocs[i] );
495 snprintf( textbuf, textlen,
496 "unrecongized objectClass '%s'",
499 return LDAP_OBJECT_CLASS_VIOLATION;
502 if( oc->soc_kind == LDAP_SCHEMA_STRUCTURAL ) {
503 if( sc == NULL || is_object_subclass( sc, oc ) ) {
507 } else if ( !is_object_subclass( oc, sc ) ) {
509 ObjectClass *xc = NULL;
511 /* find common superior */
512 for( j=i+1; ocs[j].bv_val; j++ ) {
513 xc = oc_bvfind( &ocs[j] );
516 snprintf( textbuf, textlen,
517 "unrecongized objectClass '%s'",
520 return LDAP_OBJECT_CLASS_VIOLATION;
523 if( xc->soc_kind != LDAP_SCHEMA_STRUCTURAL ) {
528 if( is_object_subclass( sc, xc ) &&
529 is_object_subclass( oc, xc ) )
531 /* found common subclass */
539 /* no common subclass */
540 snprintf( textbuf, textlen,
541 "invalid structural object class chain (%s/%s)",
542 ocs[scn].bv_val, ocs[i].bv_val );
544 return LDAP_OBJECT_CLASS_VIOLATION;
554 *text = "no structural object classes provided";
555 return LDAP_OBJECT_CLASS_VIOLATION;
563 * Return structural object class from list of modifications
565 int mods_structural_class(
569 char *textbuf, size_t textlen )
571 Modifications *ocmod = NULL;
573 for( ; mods != NULL; mods = mods->sml_next ) {
574 if( mods->sml_desc == slap_schema.si_ad_objectClass ) {
575 if( ocmod != NULL ) {
576 *text = "entry has multiple objectClass attributes";
577 return LDAP_OBJECT_CLASS_VIOLATION;
583 if( ocmod == NULL ) {
584 *text = "entry has no objectClass attribute";
585 return LDAP_OBJECT_CLASS_VIOLATION;
588 if( ocmod->sml_bvalues == NULL || ocmod->sml_bvalues[0].bv_val == NULL ) {
589 *text = "objectClass attribute has no values";
590 return LDAP_OBJECT_CLASS_VIOLATION;
593 return structural_class( ocmod->sml_bvalues, sc, NULL,
594 text, textbuf, textlen );