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 );
24 static int entry_naming_check(
27 char *textbuf, size_t textlen );
29 * entry_schema_check - check that entry e conforms to the schema required
30 * by its object class(es).
32 * returns 0 if so, non-zero otherwise.
41 char *textbuf, size_t textlen )
43 Attribute *a, *asc, *aoc;
45 #ifdef SLAP_EXTENDED_SCHEMA
51 AttributeDescription *ad_structuralObjectClass
52 = slap_schema.si_ad_structuralObjectClass;
53 AttributeDescription *ad_objectClass
54 = slap_schema.si_ad_objectClass;
56 int subentry = is_entry_subentry( e );
57 int collectiveSubentry = 0;
59 if ( SLAP_NO_SCHEMA_CHECK( be )) {
64 collectiveSubentry = is_entry_collectiveAttributeSubentry( e );
69 /* misc attribute checks */
70 for ( a = e->e_attrs; a != NULL; a = a->a_next ) {
71 const char *type = a->a_desc->ad_cname.bv_val;
73 /* there should be at least one value */
75 assert( a->a_vals[0].bv_val != NULL );
77 if( a->a_desc->ad_type->sat_check ) {
78 int rc = (a->a_desc->ad_type->sat_check)(
79 be, e, a, text, textbuf, textlen );
80 if( rc != LDAP_SUCCESS ) {
85 if( !collectiveSubentry && is_at_collective( a->a_desc->ad_type ) ) {
86 snprintf( textbuf, textlen,
87 "'%s' can only appear in collectiveAttributeSubentry",
89 return LDAP_OBJECT_CLASS_VIOLATION;
92 /* if single value type, check for multiple values */
93 if( is_at_single_value( a->a_desc->ad_type ) &&
94 a->a_vals[1].bv_val != NULL )
96 snprintf( textbuf, textlen,
97 "attribute '%s' cannot have multiple values",
101 LDAP_LOG( OPERATION, INFO,
102 "entry_schema_check: dn=\"%s\" %s\n", e->e_dn, textbuf, 0 );
104 Debug( LDAP_DEBUG_ANY,
106 e->e_dn, textbuf, 0 );
109 return LDAP_CONSTRAINT_VIOLATION;
113 /* it's a REALLY bad idea to disable schema checks */
114 if( !global_schemacheck ) return LDAP_SUCCESS;
116 /* find the structural object class attribute */
117 asc = attr_find( e->e_attrs, ad_structuralObjectClass );
120 LDAP_LOG( OPERATION, INFO,
121 "entry_schema_check: No structuralObjectClass for entry (%s)\n",
124 Debug( LDAP_DEBUG_ANY,
125 "No structuralObjectClass for entry (%s)\n",
129 *text = "no structuralObjectClass operational attribute";
133 assert( asc->a_vals != NULL );
134 assert( asc->a_vals[0].bv_val != NULL );
135 assert( asc->a_vals[1].bv_val == NULL );
137 sc = oc_bvfind( &asc->a_vals[0] );
139 snprintf( textbuf, textlen,
140 "unrecognized structuralObjectClass '%s'",
141 asc->a_vals[0].bv_val );
144 LDAP_LOG( OPERATION, INFO,
145 "entry_schema_check: dn (%s), %s\n", e->e_dn, textbuf, 0 );
147 Debug( LDAP_DEBUG_ANY,
148 "entry_check_schema(%s): %s\n",
149 e->e_dn, textbuf, 0 );
152 return LDAP_OBJECT_CLASS_VIOLATION;
155 if( sc->soc_kind != LDAP_SCHEMA_STRUCTURAL ) {
156 snprintf( textbuf, textlen,
157 "structuralObjectClass '%s' is not STRUCTURAL",
158 asc->a_vals[0].bv_val );
161 LDAP_LOG( OPERATION, INFO,
162 "entry_schema_check: dn (%s), %s\n", e->e_dn, textbuf, 0 );
164 Debug( LDAP_DEBUG_ANY,
165 "entry_check_schema(%s): %s\n",
166 e->e_dn, textbuf, 0 );
172 if( sc->soc_obsolete ) {
173 snprintf( textbuf, textlen,
174 "structuralObjectClass '%s' is OBSOLETE",
175 asc->a_vals[0].bv_val );
178 LDAP_LOG( OPERATION, INFO,
179 "entry_schema_check: dn (%s), %s\n", e->e_dn, textbuf, 0 );
181 Debug( LDAP_DEBUG_ANY,
182 "entry_check_schema(%s): %s\n",
183 e->e_dn, textbuf, 0 );
186 return LDAP_OBJECT_CLASS_VIOLATION;
189 /* find the object class attribute */
190 aoc = attr_find( e->e_attrs, ad_objectClass );
193 LDAP_LOG( OPERATION, INFO,
194 "entry_schema_check: No objectClass for entry (%s).\n",
197 Debug( LDAP_DEBUG_ANY, "No objectClass for entry (%s)\n",
201 *text = "no objectClass attribute";
202 return LDAP_OBJECT_CLASS_VIOLATION;
205 assert( aoc->a_vals != NULL );
206 assert( aoc->a_vals[0].bv_val != NULL );
208 rc = structural_class( aoc->a_vals, &nsc, &oc, text, textbuf, textlen );
209 if( rc != LDAP_SUCCESS ) {
216 snprintf( textbuf, textlen,
217 "unrecognized objectClass '%s'",
218 aoc->a_vals[0].bv_val );
219 return LDAP_OBJECT_CLASS_VIOLATION;
221 } else if ( sc != slap_schema.si_oc_glue && sc != oc ) {
222 snprintf( textbuf, textlen,
223 "structural object class modification "
224 "from '%s' to '%s' not allowed",
225 asc->a_vals[0].bv_val, nsc.bv_val );
226 return LDAP_NO_OBJECT_CLASS_MODS;
227 } else if ( sc == slap_schema.si_oc_glue ) {
232 if ( !is_entry_objectclass ( e, slap_schema.si_oc_glue, 0 ) ) {
233 rc = entry_naming_check( e, text, textbuf, textlen );
234 if( rc != LDAP_SUCCESS ) {
241 #ifdef SLAP_EXTENDED_SCHEMA
242 /* find the content rule for the structural class */
243 cr = cr_find( sc->soc_oid );
245 /* the cr must be same as the structural class */
246 assert( !cr || !strcmp( cr->scr_oid, sc->soc_oid ) );
248 /* check that the entry has required attrs of the content rule */
250 if( cr->scr_obsolete ) {
251 snprintf( textbuf, textlen,
252 "content rule '%s' is obsolete",
253 ldap_contentrule2name( &cr->scr_crule ));
256 LDAP_LOG( OPERATION, INFO,
257 "entry_schema_check: dn=\"%s\" %s", e->e_dn, textbuf, 0 );
259 Debug( LDAP_DEBUG_ANY,
261 e->e_dn, textbuf, 0 );
264 return LDAP_OBJECT_CLASS_VIOLATION;
267 if( cr->scr_required ) for( i=0; cr->scr_required[i]; i++ ) {
268 at = cr->scr_required[i];
270 for ( a = e->e_attrs; a != NULL; a = a->a_next ) {
271 if( a->a_desc->ad_type == at ) {
276 /* not there => schema violation */
278 snprintf( textbuf, textlen,
279 "content rule '%s' requires attribute '%s'",
280 ldap_contentrule2name( &cr->scr_crule ),
281 at->sat_cname.bv_val );
284 LDAP_LOG( OPERATION, INFO,
285 "entry_schema_check: dn=\"%s\" %s", e->e_dn, textbuf, 0 );
287 Debug( LDAP_DEBUG_ANY,
289 e->e_dn, textbuf, 0 );
292 return LDAP_OBJECT_CLASS_VIOLATION;
296 if( cr->scr_precluded ) for( i=0; cr->scr_precluded[i]; i++ ) {
297 at = cr->scr_precluded[i];
299 for ( a = e->e_attrs; a != NULL; a = a->a_next ) {
300 if( a->a_desc->ad_type == at ) {
305 /* there => schema violation */
307 snprintf( textbuf, textlen,
308 "content rule '%s' precluded attribute '%s'",
309 ldap_contentrule2name( &cr->scr_crule ),
310 at->sat_cname.bv_val );
313 LDAP_LOG( OPERATION, INFO,
314 "entry_schema_check: dn=\"%s\" %s", e->e_dn, textbuf, 0 );
316 Debug( LDAP_DEBUG_ANY,
318 e->e_dn, textbuf, 0 );
321 return LDAP_OBJECT_CLASS_VIOLATION;
325 #endif /* SLAP_EXTENDED_SCHEMA */
327 /* check that the entry has required attrs for each oc */
328 for ( i = 0; aoc->a_vals[i].bv_val != NULL; i++ ) {
329 if ( (oc = oc_bvfind( &aoc->a_vals[i] )) == NULL ) {
330 snprintf( textbuf, textlen,
331 "unrecognized objectClass '%s'",
332 aoc->a_vals[i].bv_val );
335 LDAP_LOG( OPERATION, INFO,
336 "entry_schema_check: dn (%s), %s\n", e->e_dn, textbuf, 0 );
338 Debug( LDAP_DEBUG_ANY,
339 "entry_check_schema(%s): %s\n",
340 e->e_dn, textbuf, 0 );
343 return LDAP_OBJECT_CLASS_VIOLATION;
346 if ( oc->soc_obsolete ) {
347 /* disallow obsolete classes */
348 snprintf( textbuf, textlen,
349 "objectClass '%s' is OBSOLETE",
350 aoc->a_vals[i].bv_val );
353 LDAP_LOG( OPERATION, INFO,
354 "entry_schema_check: dn (%s), %s\n", e->e_dn, textbuf, 0 );
356 Debug( LDAP_DEBUG_ANY,
357 "entry_check_schema(%s): %s\n",
358 e->e_dn, textbuf, 0 );
361 return LDAP_OBJECT_CLASS_VIOLATION;
364 if ( oc->soc_check ) {
365 int rc = (oc->soc_check)( be, e, oc,
366 text, textbuf, textlen );
367 if( rc != LDAP_SUCCESS ) {
372 if ( oc->soc_kind == LDAP_SCHEMA_ABSTRACT ) {
373 /* object class is abstract */
374 if ( oc != slap_schema.si_oc_top &&
375 !is_object_subclass( oc, sc ))
378 ObjectClass *xc = NULL;
379 for( j=0; aoc->a_vals[j].bv_val; j++ ) {
381 xc = oc_bvfind( &aoc->a_vals[i] );
383 snprintf( textbuf, textlen,
384 "unrecognized objectClass '%s'",
385 aoc->a_vals[i].bv_val );
388 LDAP_LOG( OPERATION, INFO,
389 "entry_schema_check: dn (%s), %s\n",
390 e->e_dn, textbuf, 0 );
392 Debug( LDAP_DEBUG_ANY,
393 "entry_check_schema(%s): %s\n",
394 e->e_dn, textbuf, 0 );
397 return LDAP_OBJECT_CLASS_VIOLATION;
400 /* since we previous check against the
401 * structural object of this entry, the
402 * abstract class must be a (direct or indirect)
403 * superclass of one of the auxiliary classes of
406 if ( xc->soc_kind == LDAP_SCHEMA_AUXILIARY &&
407 is_object_subclass( oc, xc ) )
416 snprintf( textbuf, textlen, "instanstantiation of "
417 "abstract objectClass '%s' not allowed",
418 aoc->a_vals[i].bv_val );
421 LDAP_LOG( OPERATION, INFO,
422 "entry_schema_check: dn (%s), %s\n",
423 e->e_dn, textbuf, 0 );
425 Debug( LDAP_DEBUG_ANY,
426 "entry_check_schema(%s): %s\n",
427 e->e_dn, textbuf, 0 );
430 return LDAP_OBJECT_CLASS_VIOLATION;
434 } else if ( oc->soc_kind != LDAP_SCHEMA_STRUCTURAL || oc == sc ) {
437 #ifdef SLAP_EXTENDED_SCHEMA
438 if( oc->soc_kind == LDAP_SCHEMA_AUXILIARY ) {
445 if( cr->scr_auxiliaries ) {
446 for( j = 0; cr->scr_auxiliaries[j]; j++ ) {
447 if( cr->scr_auxiliaries[j] == oc ) {
453 } else if ( global_disallows & SLAP_DISALLOW_AUX_WO_CR ) {
460 snprintf( textbuf, textlen,
461 "content rule '%s' does not allow class '%s'",
462 ldap_contentrule2name( &cr->scr_crule ),
463 oc->soc_cname.bv_val );
466 LDAP_LOG( OPERATION, INFO,
467 "entry_schema_check: dn=\"%s\" %s",
468 e->e_dn, textbuf, 0 );
470 Debug( LDAP_DEBUG_ANY,
472 e->e_dn, textbuf, 0 );
475 return LDAP_OBJECT_CLASS_VIOLATION;
478 #endif /* SLAP_EXTENDED_SCHEMA */
480 s = oc_check_required( e, oc, &aoc->a_vals[i] );
482 snprintf( textbuf, textlen,
483 "object class '%s' requires attribute '%s'",
484 aoc->a_vals[i].bv_val, s );
487 LDAP_LOG( OPERATION, INFO,
488 "entry_schema_check: dn=\"%s\" %s", e->e_dn, textbuf, 0 );
490 Debug( LDAP_DEBUG_ANY,
492 e->e_dn, textbuf, 0 );
495 return LDAP_OBJECT_CLASS_VIOLATION;
498 if( oc == slap_schema.si_oc_extensibleObject ) {
508 /* check that each attr in the entry is allowed by some oc */
509 for ( a = e->e_attrs; a != NULL; a = a->a_next ) {
512 #ifdef SLAP_EXTENDED_SCHEMA
513 ret = LDAP_OBJECT_CLASS_VIOLATION;
515 if( cr && cr->scr_required ) {
516 for( i=0; cr->scr_required[i]; i++ ) {
517 if( cr->scr_required[i] == a->a_desc->ad_type ) {
524 if( ret != LDAP_SUCCESS && cr && cr->scr_allowed ) {
525 for( i=0; cr->scr_allowed[i]; i++ ) {
526 if( cr->scr_allowed[i] == a->a_desc->ad_type ) {
533 if( ret != LDAP_SUCCESS )
534 #endif /* SLAP_EXTENDED_SCHEMA */
536 ret = oc_check_allowed( a->a_desc->ad_type, aoc->a_vals, sc );
539 if ( ret != LDAP_SUCCESS ) {
540 char *type = a->a_desc->ad_cname.bv_val;
542 snprintf( textbuf, textlen,
543 "attribute '%s' not allowed",
547 LDAP_LOG( OPERATION, INFO,
548 "entry_schema_check: dn=\"%s\" %s\n", e->e_dn, textbuf, 0);
550 Debug( LDAP_DEBUG_ANY,
552 e->e_dn, textbuf, 0 );
566 struct berval *ocname )
573 LDAP_LOG( OPERATION, ENTRY,
574 "oc_check_required: dn (%s), objectClass \"%s\"\n",
575 e->e_dn, ocname->bv_val, 0 );
577 Debug( LDAP_DEBUG_TRACE,
578 "oc_check_required entry (%s), objectClass \"%s\"\n",
579 e->e_dn, ocname->bv_val, 0 );
583 /* check for empty oc_required */
584 if(oc->soc_required == NULL) {
588 /* for each required attribute */
589 for ( i = 0; oc->soc_required[i] != NULL; i++ ) {
590 at = oc->soc_required[i];
591 /* see if it's in the entry */
592 for ( a = e->e_attrs; a != NULL; a = a->a_next ) {
593 if( a->a_desc->ad_type == at ) {
597 /* not there => schema violation */
599 return at->sat_cname.bv_val;
606 int oc_check_allowed(
614 LDAP_LOG( OPERATION, ENTRY,
615 "oc_check_allowed: type \"%s\"\n", at->sat_cname.bv_val, 0, 0 );
617 Debug( LDAP_DEBUG_TRACE,
618 "oc_check_allowed type \"%s\"\n",
619 at->sat_cname.bv_val, 0, 0 );
622 /* always allow objectClass attribute */
623 if ( strcasecmp( at->sat_cname.bv_val, "objectClass" ) == 0 ) {
628 * All operational attributions are allowed by schema rules.
630 if( is_at_operational(at) ) {
634 /* check to see if its allowed by the structuralObjectClass */
636 /* does it require the type? */
637 for ( j = 0; sc->soc_required != NULL &&
638 sc->soc_required[j] != NULL; j++ )
640 if( at == sc->soc_required[j] ) {
645 /* does it allow the type? */
646 for ( j = 0; sc->soc_allowed != NULL &&
647 sc->soc_allowed[j] != NULL; j++ )
649 if( at == sc->soc_allowed[j] ) {
655 /* check that the type appears as req or opt in at least one oc */
656 for ( i = 0; ocl[i].bv_val != NULL; i++ ) {
657 /* if we know about the oc */
658 ObjectClass *oc = oc_bvfind( &ocl[i] );
659 if ( oc != NULL && oc->soc_kind != LDAP_SCHEMA_ABSTRACT &&
660 ( sc == NULL || oc->soc_kind == LDAP_SCHEMA_AUXILIARY ))
662 /* does it require the type? */
663 for ( j = 0; oc->soc_required != NULL &&
664 oc->soc_required[j] != NULL; j++ )
666 if( at == oc->soc_required[j] ) {
670 /* does it allow the type? */
671 for ( j = 0; oc->soc_allowed != NULL &&
672 oc->soc_allowed[j] != NULL; j++ )
674 if( at == oc->soc_allowed[j] ) {
681 /* not allowed by any oc */
682 return LDAP_OBJECT_CLASS_VIOLATION;
686 * Determine the structural object class from a set of OIDs
688 int structural_class(
693 char *textbuf, size_t textlen )
697 ObjectClass *sc = NULL;
700 *text = "structural_class: internal error";
703 for( i=0; ocs[i].bv_val; i++ ) {
704 oc = oc_bvfind( &ocs[i] );
707 snprintf( textbuf, textlen,
708 "unrecognized objectClass '%s'",
711 return LDAP_OBJECT_CLASS_VIOLATION;
714 if( oc->soc_kind == LDAP_SCHEMA_STRUCTURAL ) {
715 if( sc == NULL || is_object_subclass( sc, oc ) ) {
719 } else if ( !is_object_subclass( oc, sc ) ) {
721 ObjectClass *xc = NULL;
723 /* find common superior */
724 for( j=i+1; ocs[j].bv_val; j++ ) {
725 xc = oc_bvfind( &ocs[j] );
728 snprintf( textbuf, textlen,
729 "unrecognized objectClass '%s'",
732 return LDAP_OBJECT_CLASS_VIOLATION;
735 if( xc->soc_kind != LDAP_SCHEMA_STRUCTURAL ) {
740 if( is_object_subclass( sc, xc ) &&
741 is_object_subclass( oc, xc ) )
743 /* found common subclass */
751 /* no common subclass */
752 snprintf( textbuf, textlen,
753 "invalid structural object class chain (%s/%s)",
754 ocs[scn].bv_val, ocs[i].bv_val );
756 return LDAP_OBJECT_CLASS_VIOLATION;
767 *text = "no structural object class provided";
768 return LDAP_OBJECT_CLASS_VIOLATION;
772 *text = "invalid structural object class";
773 return LDAP_OBJECT_CLASS_VIOLATION;
778 if( scbv->bv_len == 0 ) {
779 *text = "invalid structural object class";
780 return LDAP_OBJECT_CLASS_VIOLATION;
787 * Return structural object class from list of modifications
789 int mods_structural_class(
793 char *textbuf, size_t textlen )
795 Modifications *ocmod = NULL;
797 for( ; mods != NULL; mods = mods->sml_next ) {
798 if( mods->sml_desc == slap_schema.si_ad_objectClass ) {
799 if( ocmod != NULL ) {
800 *text = "entry has multiple objectClass attributes";
801 return LDAP_OBJECT_CLASS_VIOLATION;
807 if( ocmod == NULL ) {
808 *text = "entry has no objectClass attribute";
809 return LDAP_OBJECT_CLASS_VIOLATION;
812 if( ocmod->sml_bvalues == NULL || ocmod->sml_bvalues[0].bv_val == NULL ) {
813 *text = "objectClass attribute has no values";
814 return LDAP_OBJECT_CLASS_VIOLATION;
817 return structural_class( ocmod->sml_bvalues, sc, NULL,
818 text, textbuf, textlen );
826 char *textbuf, size_t textlen )
830 const char *p = NULL;
832 int rc = LDAP_SUCCESS;
835 * Get attribute type(s) and attribute value(s) of our RDN
837 if ( ldap_bv2rdn( &e->e_name, &rdn, (char **)&p,
838 LDAP_DN_FORMAT_LDAP ) )
840 *text = "unrecongized attribute type(s) in RDN";
841 return LDAP_INVALID_DN_SYNTAX;
844 /* Check that each AVA of the RDN is present in the entry */
845 /* FIXME: Should also check that each AVA lists a distinct type */
846 for ( cnt = 0; rdn[cnt]; cnt++ ) {
847 LDAPAVA *ava = rdn[cnt];
848 AttributeDescription *desc = NULL;
852 rc = slap_bv2ad( &ava->la_attr, &desc, &errtext );
853 if ( rc != LDAP_SUCCESS ) {
854 snprintf( textbuf, textlen, "%s (in RDN)", errtext );
858 /* find the naming attribute */
859 attr = attr_find( e->e_attrs, desc );
860 if ( attr == NULL ) {
861 snprintf( textbuf, textlen,
862 "naming attribute '%s' is not present in entry",
863 ava->la_attr.bv_val );
864 rc = LDAP_NAMING_VIOLATION;
868 if( ava->la_flags & LDAP_AVA_BINARY ) {
869 snprintf( textbuf, textlen,
870 "value of naming attribute '%s' in unsupported BER form",
871 ava->la_attr.bv_val );
872 rc = LDAP_NAMING_VIOLATION;
875 if ( value_find_ex( desc,
876 SLAP_MR_VALUE_OF_ASSERTION_SYNTAX|
877 SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH,
878 attr->a_nvals, &ava->la_value, NULL ) != 0 )
880 snprintf( textbuf, textlen,
881 "value of naming attribute '%s' is not present in entry",
882 ava->la_attr.bv_val );
883 rc = LDAP_NAMING_VIOLATION;