1 /* schema_check.c - routines to enforce schema definitions */
4 * Copyright 1998-2003 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;
41 #ifdef SLAP_EXTENDED_SCHEMA
47 AttributeDescription *ad_structuralObjectClass
48 = slap_schema.si_ad_structuralObjectClass;
49 AttributeDescription *ad_objectClass
50 = slap_schema.si_ad_objectClass;
52 int subentry = is_entry_subentry( e );
53 int collectiveSubentry = 0;
56 collectiveSubentry = is_entry_collectiveAttributeSubentry( e );
61 /* misc attribute checks */
62 for ( a = e->e_attrs; a != NULL; a = a->a_next ) {
63 const char *type = a->a_desc->ad_cname.bv_val;
65 /* there should be at least one value */
67 assert( a->a_vals[0].bv_val != NULL );
69 if( a->a_desc->ad_type->sat_check ) {
70 int rc = (a->a_desc->ad_type->sat_check)(
71 be, e, a, text, textbuf, textlen );
72 if( rc != LDAP_SUCCESS ) {
77 if( !collectiveSubentry && is_at_collective( a->a_desc->ad_type ) ) {
78 snprintf( textbuf, textlen,
79 "'%s' can only appear in collectiveAttributeSubentry",
81 return LDAP_OBJECT_CLASS_VIOLATION;
84 /* if single value type, check for multiple values */
85 if( is_at_single_value( a->a_desc->ad_type ) &&
86 a->a_vals[1].bv_val != NULL )
88 snprintf( textbuf, textlen,
89 "attribute '%s' cannot have multiple values",
93 LDAP_LOG( OPERATION, INFO,
94 "entry_schema_check: dn=\"%s\" %s\n", e->e_dn, textbuf, 0 );
96 Debug( LDAP_DEBUG_ANY,
98 e->e_dn, textbuf, 0 );
101 return LDAP_CONSTRAINT_VIOLATION;
105 /* it's a REALLY bad idea to disable schema checks */
106 if( !global_schemacheck ) return LDAP_SUCCESS;
108 /* find the structural object class attribute */
109 asc = attr_find( e->e_attrs, ad_structuralObjectClass );
112 LDAP_LOG( OPERATION, INFO,
113 "entry_schema_check: No structuralObjectClass for entry (%s)\n",
116 Debug( LDAP_DEBUG_ANY,
117 "No structuralObjectClass for entry (%s)\n",
121 *text = "no structuralObjectClass operational attribute";
125 assert( asc->a_vals != NULL );
126 assert( asc->a_vals[0].bv_val != NULL );
127 assert( asc->a_vals[1].bv_val == NULL );
129 sc = oc_bvfind( &asc->a_vals[0] );
131 snprintf( textbuf, textlen,
132 "unrecognized structuralObjectClass '%s'",
133 asc->a_vals[0].bv_val );
136 LDAP_LOG( OPERATION, INFO,
137 "entry_schema_check: dn (%s), %s\n", e->e_dn, textbuf, 0 );
139 Debug( LDAP_DEBUG_ANY,
140 "entry_check_schema(%s): %s\n",
141 e->e_dn, textbuf, 0 );
144 return LDAP_OBJECT_CLASS_VIOLATION;
147 if( sc->soc_kind != LDAP_SCHEMA_STRUCTURAL ) {
148 snprintf( textbuf, textlen,
149 "structuralObjectClass '%s' is not STRUCTURAL",
150 asc->a_vals[0].bv_val );
153 LDAP_LOG( OPERATION, INFO,
154 "entry_schema_check: dn (%s), %s\n", e->e_dn, textbuf, 0 );
156 Debug( LDAP_DEBUG_ANY,
157 "entry_check_schema(%s): %s\n",
158 e->e_dn, textbuf, 0 );
164 if( sc->soc_obsolete ) {
165 snprintf( textbuf, textlen,
166 "structuralObjectClass '%s' is OBSOLETE",
167 asc->a_vals[0].bv_val );
170 LDAP_LOG( OPERATION, INFO,
171 "entry_schema_check: dn (%s), %s\n", e->e_dn, textbuf, 0 );
173 Debug( LDAP_DEBUG_ANY,
174 "entry_check_schema(%s): %s\n",
175 e->e_dn, textbuf, 0 );
178 return LDAP_OBJECT_CLASS_VIOLATION;
181 /* find the object class attribute */
182 aoc = attr_find( e->e_attrs, ad_objectClass );
185 LDAP_LOG( OPERATION, INFO,
186 "entry_schema_check: No objectClass for entry (%s).\n",
189 Debug( LDAP_DEBUG_ANY, "No objectClass for entry (%s)\n",
193 *text = "no objectClass attribute";
194 return LDAP_OBJECT_CLASS_VIOLATION;
197 assert( aoc->a_vals != NULL );
198 assert( aoc->a_vals[0].bv_val != NULL );
200 rc = structural_class( aoc->a_vals, &nsc, &oc, text, textbuf, textlen );
201 if( rc != LDAP_SUCCESS ) {
208 snprintf( textbuf, textlen,
209 "unrecognized objectClass '%s'",
210 aoc->a_vals[0].bv_val );
211 return LDAP_OBJECT_CLASS_VIOLATION;
213 } else if ( sc != oc ) {
214 snprintf( textbuf, textlen,
215 "structural object class modification "
216 "from '%s' to '%s' not allowed",
217 asc->a_vals[0].bv_val, nsc.bv_val );
218 return LDAP_NO_OBJECT_CLASS_MODS;
227 * Get attribute type(s) and attribute value(s) of our RDN
229 if ( ldap_bv2rdn( &e->e_name, &rdn, (char **)&p,
230 LDAP_DN_FORMAT_LDAP ) )
232 *text = "unrecongized attribute type(s) in RDN";
233 return LDAP_INVALID_DN_SYNTAX;
236 /* Check that each AVA of the RDN is present in the entry */
237 /* FIXME: Should also check that each AVA lists a distinct type */
238 for ( cnt = 0; rdn[0][cnt]; cnt++ ) {
239 LDAPAVA *ava = rdn[0][cnt];
240 AttributeDescription *desc = NULL;
244 rc = slap_bv2ad( &ava->la_attr, &desc, &errtext );
245 if ( rc != LDAP_SUCCESS ) {
246 snprintf( textbuf, textlen, "%s (in RDN)", errtext );
250 /* find the naming attribute */
251 attr = attr_find( e->e_attrs, desc );
252 if ( attr == NULL ) {
253 snprintf( textbuf, textlen,
254 "naming attribute '%s' is not present in entry",
256 return LDAP_NO_SUCH_ATTRIBUTE;
259 if ( value_find( desc, attr->a_vals, &ava->la_value ) != 0 ) {
260 snprintf( textbuf, textlen,
261 "value of naming attribute '%s' is not present in entry",
263 return LDAP_NO_SUCH_ATTRIBUTE;
268 #ifdef SLAP_EXTENDED_SCHEMA
269 /* find the content rule for the structural class */
270 cr = cr_find( sc->soc_oid );
272 /* the cr must be same as the structural class */
273 assert( !cr || !strcmp( cr->scr_oid, sc->soc_oid ) );
275 /* check that the entry has required attrs of the content rule */
277 if( cr->scr_obsolete ) {
278 snprintf( textbuf, textlen,
279 "content rule '%s' is obsolete",
280 ldap_contentrule2name( &cr->scr_crule ));
283 LDAP_LOG( OPERATION, INFO,
284 "entry_schema_check: dn=\"%s\" %s", e->e_dn, textbuf, 0 );
286 Debug( LDAP_DEBUG_ANY,
288 e->e_dn, textbuf, 0 );
291 return LDAP_OBJECT_CLASS_VIOLATION;
294 if( cr->scr_required ) for( i=0; cr->scr_required[i]; i++ ) {
295 at = cr->scr_required[i];
297 for ( a = e->e_attrs; a != NULL; a = a->a_next ) {
298 if( a->a_desc->ad_type == at ) {
303 /* not there => schema violation */
305 snprintf( textbuf, textlen,
306 "content rule '%s' requires attribute '%s'",
307 ldap_contentrule2name( &cr->scr_crule ),
308 at->sat_cname.bv_val );
311 LDAP_LOG( OPERATION, INFO,
312 "entry_schema_check: dn=\"%s\" %s", e->e_dn, textbuf, 0 );
314 Debug( LDAP_DEBUG_ANY,
316 e->e_dn, textbuf, 0 );
319 return LDAP_OBJECT_CLASS_VIOLATION;
323 if( cr->scr_precluded ) for( i=0; cr->scr_precluded[i]; i++ ) {
324 at = cr->scr_precluded[i];
326 for ( a = e->e_attrs; a != NULL; a = a->a_next ) {
327 if( a->a_desc->ad_type == at ) {
332 /* there => schema violation */
334 snprintf( textbuf, textlen,
335 "content rule '%s' precluded attribute '%s'",
336 ldap_contentrule2name( &cr->scr_crule ),
337 at->sat_cname.bv_val );
340 LDAP_LOG( OPERATION, INFO,
341 "entry_schema_check: dn=\"%s\" %s", e->e_dn, textbuf, 0 );
343 Debug( LDAP_DEBUG_ANY,
345 e->e_dn, textbuf, 0 );
348 return LDAP_OBJECT_CLASS_VIOLATION;
352 #endif /* SLAP_EXTENDED_SCHEMA */
354 /* check that the entry has required attrs for each oc */
355 for ( i = 0; aoc->a_vals[i].bv_val != NULL; i++ ) {
356 if ( (oc = oc_bvfind( &aoc->a_vals[i] )) == NULL ) {
357 snprintf( textbuf, textlen,
358 "unrecognized objectClass '%s'",
359 aoc->a_vals[i].bv_val );
362 LDAP_LOG( OPERATION, INFO,
363 "entry_schema_check: dn (%s), %s\n", e->e_dn, textbuf, 0 );
365 Debug( LDAP_DEBUG_ANY,
366 "entry_check_schema(%s): %s\n",
367 e->e_dn, textbuf, 0 );
370 return LDAP_OBJECT_CLASS_VIOLATION;
373 if ( oc->soc_obsolete ) {
374 /* disallow obsolete classes */
375 snprintf( textbuf, textlen,
376 "objectClass '%s' is OBSOLETE",
377 aoc->a_vals[i].bv_val );
380 LDAP_LOG( OPERATION, INFO,
381 "entry_schema_check: dn (%s), %s\n", e->e_dn, textbuf, 0 );
383 Debug( LDAP_DEBUG_ANY,
384 "entry_check_schema(%s): %s\n",
385 e->e_dn, textbuf, 0 );
388 return LDAP_OBJECT_CLASS_VIOLATION;
391 if ( oc->soc_check ) {
392 int rc = (oc->soc_check)( be, e, oc,
393 text, textbuf, textlen );
394 if( rc != LDAP_SUCCESS ) {
399 if ( oc->soc_kind == LDAP_SCHEMA_ABSTRACT ) {
400 /* object class is abstract */
401 if ( oc != slap_schema.si_oc_top &&
402 !is_object_subclass( oc, sc ))
405 ObjectClass *xc = NULL;
406 for( j=0; aoc->a_vals[j].bv_val; j++ ) {
408 xc = oc_bvfind( &aoc->a_vals[i] );
410 snprintf( textbuf, textlen,
411 "unrecognized objectClass '%s'",
412 aoc->a_vals[i].bv_val );
415 LDAP_LOG( OPERATION, INFO,
416 "entry_schema_check: dn (%s), %s\n",
417 e->e_dn, textbuf, 0 );
419 Debug( LDAP_DEBUG_ANY,
420 "entry_check_schema(%s): %s\n",
421 e->e_dn, textbuf, 0 );
424 return LDAP_OBJECT_CLASS_VIOLATION;
427 /* since we previous check against the
428 * structural object of this entry, the
429 * abstract class must be a (direct or indirect)
430 * superclass of one of the auxiliary classes of
433 if ( xc->soc_kind == LDAP_SCHEMA_AUXILIARY &&
434 is_object_subclass( oc, xc ) )
443 snprintf( textbuf, textlen, "instanstantiation of "
444 "abstract objectClass '%s' not allowed",
445 aoc->a_vals[i].bv_val );
448 LDAP_LOG( OPERATION, INFO,
449 "entry_schema_check: dn (%s), %s\n",
450 e->e_dn, textbuf, 0 );
452 Debug( LDAP_DEBUG_ANY,
453 "entry_check_schema(%s): %s\n",
454 e->e_dn, textbuf, 0 );
457 return LDAP_OBJECT_CLASS_VIOLATION;
461 } else if ( oc->soc_kind != LDAP_SCHEMA_STRUCTURAL || oc == sc ) {
464 #ifdef SLAP_EXTENDED_SCHEMA
465 if( oc->soc_kind == LDAP_SCHEMA_AUXILIARY ) {
468 if( cr->scr_auxiliaries ) {
469 for( ; cr->scr_auxiliaries[k]; k++ ) {
470 if( cr->scr_auxiliaries[k] == oc ) {
476 } else if ( global_disallows & SLAP_DISALLOW_AUX_WO_CR ) {
481 snprintf( textbuf, textlen,
482 "content rule '%s' does not allow class '%s'",
483 ldap_contentrule2name( &cr->scr_crule ),
484 oc->soc_cname.bv_val );
487 LDAP_LOG( OPERATION, INFO,
488 "entry_schema_check: dn=\"%s\" %s",
489 e->e_dn, textbuf, 0 );
491 Debug( LDAP_DEBUG_ANY,
493 e->e_dn, textbuf, 0 );
496 return LDAP_OBJECT_CLASS_VIOLATION;
499 #endif /* SLAP_EXTENDED_SCHEMA */
501 s = oc_check_required( e, oc, &aoc->a_vals[i] );
503 snprintf( textbuf, textlen,
504 "object class '%s' requires attribute '%s'",
505 aoc->a_vals[i].bv_val, s );
508 LDAP_LOG( OPERATION, INFO,
509 "entry_schema_check: dn=\"%s\" %s", e->e_dn, textbuf, 0 );
511 Debug( LDAP_DEBUG_ANY,
513 e->e_dn, textbuf, 0 );
516 return LDAP_OBJECT_CLASS_VIOLATION;
519 if( oc == slap_schema.si_oc_extensibleObject ) {
529 /* check that each attr in the entry is allowed by some oc */
530 for ( a = e->e_attrs; a != NULL; a = a->a_next ) {
533 #ifdef SLAP_EXTENDED_SCHEMA
534 ret = LDAP_OBJECT_CLASS_VIOLATION;
536 if( cr && cr->scr_required ) {
537 for( i=0; cr->scr_required[i]; i++ ) {
538 if( cr->scr_required[i] == a->a_desc->ad_type ) {
545 if( ret != LDAP_SUCCESS && cr && cr->scr_allowed ) {
546 for( i=0; cr->scr_allowed[i]; i++ ) {
547 if( cr->scr_allowed[i] == a->a_desc->ad_type ) {
554 if( ret != LDAP_SUCCESS )
555 #endif /* SLAP_EXTENDED_SCHEMA */
557 ret = oc_check_allowed( a->a_desc->ad_type, aoc->a_vals, sc );
560 if ( ret != LDAP_SUCCESS ) {
561 char *type = a->a_desc->ad_cname.bv_val;
563 snprintf( textbuf, textlen,
564 "attribute '%s' not allowed",
568 LDAP_LOG( OPERATION, INFO,
569 "entry_schema_check: dn=\"%s\" %s\n", e->e_dn, textbuf, 0);
571 Debug( LDAP_DEBUG_ANY,
573 e->e_dn, textbuf, 0 );
587 struct berval *ocname )
594 LDAP_LOG( OPERATION, ENTRY,
595 "oc_check_required: dn (%s), objectClass \"%s\"\n",
596 e->e_dn, ocname->bv_val, 0 );
598 Debug( LDAP_DEBUG_TRACE,
599 "oc_check_required entry (%s), objectClass \"%s\"\n",
600 e->e_dn, ocname->bv_val, 0 );
604 /* check for empty oc_required */
605 if(oc->soc_required == NULL) {
609 /* for each required attribute */
610 for ( i = 0; oc->soc_required[i] != NULL; i++ ) {
611 at = oc->soc_required[i];
612 /* see if it's in the entry */
613 for ( a = e->e_attrs; a != NULL; a = a->a_next ) {
614 if( a->a_desc->ad_type == at ) {
618 /* not there => schema violation */
620 return at->sat_cname.bv_val;
627 int oc_check_allowed(
635 LDAP_LOG( OPERATION, ENTRY,
636 "oc_check_allowed: type \"%s\"\n", at->sat_cname.bv_val, 0, 0 );
638 Debug( LDAP_DEBUG_TRACE,
639 "oc_check_allowed type \"%s\"\n",
640 at->sat_cname.bv_val, 0, 0 );
643 /* always allow objectClass attribute */
644 if ( strcasecmp( at->sat_cname.bv_val, "objectClass" ) == 0 ) {
649 * All operational attributions are allowed by schema rules.
651 if( is_at_operational(at) ) {
655 /* check to see if its allowed by the structuralObjectClass */
657 /* does it require the type? */
658 for ( j = 0; sc->soc_required != NULL &&
659 sc->soc_required[j] != NULL; j++ )
661 if( at == sc->soc_required[j] ) {
666 /* does it allow the type? */
667 for ( j = 0; sc->soc_allowed != NULL &&
668 sc->soc_allowed[j] != NULL; j++ )
670 if( at == sc->soc_allowed[j] ) {
676 /* check that the type appears as req or opt in at least one oc */
677 for ( i = 0; ocl[i].bv_val != NULL; i++ ) {
678 /* if we know about the oc */
679 ObjectClass *oc = oc_bvfind( &ocl[i] );
680 if ( oc != NULL && oc->soc_kind != LDAP_SCHEMA_ABSTRACT &&
681 ( sc == NULL || oc->soc_kind == LDAP_SCHEMA_AUXILIARY ))
683 /* does it require the type? */
684 for ( j = 0; oc->soc_required != NULL &&
685 oc->soc_required[j] != NULL; j++ )
687 if( at == oc->soc_required[j] ) {
691 /* does it allow the type? */
692 for ( j = 0; oc->soc_allowed != NULL &&
693 oc->soc_allowed[j] != NULL; j++ )
695 if( at == oc->soc_allowed[j] ) {
702 /* not allowed by any oc */
703 return LDAP_OBJECT_CLASS_VIOLATION;
707 * Determine the structural object class from a set of OIDs
709 int structural_class(
714 char *textbuf, size_t textlen )
718 ObjectClass *sc = NULL;
721 *text = "structural_class: internal error";
724 for( i=0; ocs[i].bv_val; i++ ) {
725 oc = oc_bvfind( &ocs[i] );
728 snprintf( textbuf, textlen,
729 "unrecognized objectClass '%s'",
732 return LDAP_OBJECT_CLASS_VIOLATION;
735 if( oc->soc_kind == LDAP_SCHEMA_STRUCTURAL ) {
736 if( sc == NULL || is_object_subclass( sc, oc ) ) {
740 } else if ( !is_object_subclass( oc, sc ) ) {
742 ObjectClass *xc = NULL;
744 /* find common superior */
745 for( j=i+1; ocs[j].bv_val; j++ ) {
746 xc = oc_bvfind( &ocs[j] );
749 snprintf( textbuf, textlen,
750 "unrecognized objectClass '%s'",
753 return LDAP_OBJECT_CLASS_VIOLATION;
756 if( xc->soc_kind != LDAP_SCHEMA_STRUCTURAL ) {
761 if( is_object_subclass( sc, xc ) &&
762 is_object_subclass( oc, xc ) )
764 /* found common subclass */
772 /* no common subclass */
773 snprintf( textbuf, textlen,
774 "invalid structural object class chain (%s/%s)",
775 ocs[scn].bv_val, ocs[i].bv_val );
777 return LDAP_OBJECT_CLASS_VIOLATION;
788 *text = "no structural object class provided";
789 return LDAP_OBJECT_CLASS_VIOLATION;
793 *text = "invalid structural object class";
794 return LDAP_OBJECT_CLASS_VIOLATION;
799 if( scbv->bv_len == 0 ) {
800 *text = "invalid structural object class";
801 return LDAP_OBJECT_CLASS_VIOLATION;
808 * Return structural object class from list of modifications
810 int mods_structural_class(
814 char *textbuf, size_t textlen )
816 Modifications *ocmod = NULL;
818 for( ; mods != NULL; mods = mods->sml_next ) {
819 if( mods->sml_desc == slap_schema.si_ad_objectClass ) {
820 if( ocmod != NULL ) {
821 *text = "entry has multiple objectClass attributes";
822 return LDAP_OBJECT_CLASS_VIOLATION;
828 if( ocmod == NULL ) {
829 *text = "entry has no objectClass attribute";
830 return LDAP_OBJECT_CLASS_VIOLATION;
833 if( ocmod->sml_bvalues == NULL || ocmod->sml_bvalues[0].bv_val == NULL ) {
834 *text = "objectClass attribute has no values";
835 return LDAP_OBJECT_CLASS_VIOLATION;
838 return structural_class( ocmod->sml_bvalues, sc, NULL,
839 text, textbuf, textlen );