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;
223 const char *p = NULL;
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 rc = LDAP_INVALID_DN_SYNTAX;
237 /* Check that each AVA of the RDN is present in the entry */
238 /* FIXME: Should also check that each AVA lists a distinct type */
239 for ( cnt = 0; rdn[0][cnt]; cnt++ ) {
240 LDAPAVA *ava = rdn[0][cnt];
241 AttributeDescription *desc = NULL;
245 rc = slap_bv2ad( &ava->la_attr, &desc, &errtext );
246 if ( rc != LDAP_SUCCESS ) {
247 snprintf( textbuf, textlen, "%s (in RDN)", errtext );
251 /* find the naming attribute */
252 attr = attr_find( e->e_attrs, desc );
253 if ( attr == NULL ) {
254 snprintf( textbuf, textlen,
255 "naming attribute '%s' is not present in entry",
256 ava->la_attr.bv_val );
257 rc = LDAP_NO_SUCH_ATTRIBUTE;
261 if ( value_find( desc, attr->a_vals, &ava->la_value ) != 0 ) {
262 snprintf( textbuf, textlen,
263 "value of naming attribute '%s' is not present in entry",
264 ava->la_attr.bv_val );
265 rc = LDAP_NO_SUCH_ATTRIBUTE;
272 if ( rc != LDAP_SUCCESS ) {
277 #ifdef SLAP_EXTENDED_SCHEMA
278 /* find the content rule for the structural class */
279 cr = cr_find( sc->soc_oid );
281 /* the cr must be same as the structural class */
282 assert( !cr || !strcmp( cr->scr_oid, sc->soc_oid ) );
284 /* check that the entry has required attrs of the content rule */
286 if( cr->scr_obsolete ) {
287 snprintf( textbuf, textlen,
288 "content rule '%s' is obsolete",
289 ldap_contentrule2name( &cr->scr_crule ));
292 LDAP_LOG( OPERATION, INFO,
293 "entry_schema_check: dn=\"%s\" %s", e->e_dn, textbuf, 0 );
295 Debug( LDAP_DEBUG_ANY,
297 e->e_dn, textbuf, 0 );
300 return LDAP_OBJECT_CLASS_VIOLATION;
303 if( cr->scr_required ) for( i=0; cr->scr_required[i]; i++ ) {
304 at = cr->scr_required[i];
306 for ( a = e->e_attrs; a != NULL; a = a->a_next ) {
307 if( a->a_desc->ad_type == at ) {
312 /* not there => schema violation */
314 snprintf( textbuf, textlen,
315 "content rule '%s' requires attribute '%s'",
316 ldap_contentrule2name( &cr->scr_crule ),
317 at->sat_cname.bv_val );
320 LDAP_LOG( OPERATION, INFO,
321 "entry_schema_check: dn=\"%s\" %s", e->e_dn, textbuf, 0 );
323 Debug( LDAP_DEBUG_ANY,
325 e->e_dn, textbuf, 0 );
328 return LDAP_OBJECT_CLASS_VIOLATION;
332 if( cr->scr_precluded ) for( i=0; cr->scr_precluded[i]; i++ ) {
333 at = cr->scr_precluded[i];
335 for ( a = e->e_attrs; a != NULL; a = a->a_next ) {
336 if( a->a_desc->ad_type == at ) {
341 /* there => schema violation */
343 snprintf( textbuf, textlen,
344 "content rule '%s' precluded attribute '%s'",
345 ldap_contentrule2name( &cr->scr_crule ),
346 at->sat_cname.bv_val );
349 LDAP_LOG( OPERATION, INFO,
350 "entry_schema_check: dn=\"%s\" %s", e->e_dn, textbuf, 0 );
352 Debug( LDAP_DEBUG_ANY,
354 e->e_dn, textbuf, 0 );
357 return LDAP_OBJECT_CLASS_VIOLATION;
361 #endif /* SLAP_EXTENDED_SCHEMA */
363 /* check that the entry has required attrs for each oc */
364 for ( i = 0; aoc->a_vals[i].bv_val != NULL; i++ ) {
365 if ( (oc = oc_bvfind( &aoc->a_vals[i] )) == NULL ) {
366 snprintf( textbuf, textlen,
367 "unrecognized objectClass '%s'",
368 aoc->a_vals[i].bv_val );
371 LDAP_LOG( OPERATION, INFO,
372 "entry_schema_check: dn (%s), %s\n", e->e_dn, textbuf, 0 );
374 Debug( LDAP_DEBUG_ANY,
375 "entry_check_schema(%s): %s\n",
376 e->e_dn, textbuf, 0 );
379 return LDAP_OBJECT_CLASS_VIOLATION;
382 if ( oc->soc_obsolete ) {
383 /* disallow obsolete classes */
384 snprintf( textbuf, textlen,
385 "objectClass '%s' is OBSOLETE",
386 aoc->a_vals[i].bv_val );
389 LDAP_LOG( OPERATION, INFO,
390 "entry_schema_check: dn (%s), %s\n", 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 if ( oc->soc_check ) {
401 int rc = (oc->soc_check)( be, e, oc,
402 text, textbuf, textlen );
403 if( rc != LDAP_SUCCESS ) {
408 if ( oc->soc_kind == LDAP_SCHEMA_ABSTRACT ) {
409 /* object class is abstract */
410 if ( oc != slap_schema.si_oc_top &&
411 !is_object_subclass( oc, sc ))
414 ObjectClass *xc = NULL;
415 for( j=0; aoc->a_vals[j].bv_val; j++ ) {
417 xc = oc_bvfind( &aoc->a_vals[i] );
419 snprintf( textbuf, textlen,
420 "unrecognized objectClass '%s'",
421 aoc->a_vals[i].bv_val );
424 LDAP_LOG( OPERATION, INFO,
425 "entry_schema_check: dn (%s), %s\n",
426 e->e_dn, textbuf, 0 );
428 Debug( LDAP_DEBUG_ANY,
429 "entry_check_schema(%s): %s\n",
430 e->e_dn, textbuf, 0 );
433 return LDAP_OBJECT_CLASS_VIOLATION;
436 /* since we previous check against the
437 * structural object of this entry, the
438 * abstract class must be a (direct or indirect)
439 * superclass of one of the auxiliary classes of
442 if ( xc->soc_kind == LDAP_SCHEMA_AUXILIARY &&
443 is_object_subclass( oc, xc ) )
452 snprintf( textbuf, textlen, "instanstantiation of "
453 "abstract objectClass '%s' not allowed",
454 aoc->a_vals[i].bv_val );
457 LDAP_LOG( OPERATION, INFO,
458 "entry_schema_check: dn (%s), %s\n",
459 e->e_dn, textbuf, 0 );
461 Debug( LDAP_DEBUG_ANY,
462 "entry_check_schema(%s): %s\n",
463 e->e_dn, textbuf, 0 );
466 return LDAP_OBJECT_CLASS_VIOLATION;
470 } else if ( oc->soc_kind != LDAP_SCHEMA_STRUCTURAL || oc == sc ) {
473 #ifdef SLAP_EXTENDED_SCHEMA
474 if( oc->soc_kind == LDAP_SCHEMA_AUXILIARY ) {
477 if( cr->scr_auxiliaries ) {
478 for( ; cr->scr_auxiliaries[k]; k++ ) {
479 if( cr->scr_auxiliaries[k] == oc ) {
485 } else if ( global_disallows & SLAP_DISALLOW_AUX_WO_CR ) {
490 snprintf( textbuf, textlen,
491 "content rule '%s' does not allow class '%s'",
492 ldap_contentrule2name( &cr->scr_crule ),
493 oc->soc_cname.bv_val );
496 LDAP_LOG( OPERATION, INFO,
497 "entry_schema_check: dn=\"%s\" %s",
498 e->e_dn, textbuf, 0 );
500 Debug( LDAP_DEBUG_ANY,
502 e->e_dn, textbuf, 0 );
505 return LDAP_OBJECT_CLASS_VIOLATION;
508 #endif /* SLAP_EXTENDED_SCHEMA */
510 s = oc_check_required( e, oc, &aoc->a_vals[i] );
512 snprintf( textbuf, textlen,
513 "object class '%s' requires attribute '%s'",
514 aoc->a_vals[i].bv_val, s );
517 LDAP_LOG( OPERATION, INFO,
518 "entry_schema_check: dn=\"%s\" %s", e->e_dn, textbuf, 0 );
520 Debug( LDAP_DEBUG_ANY,
522 e->e_dn, textbuf, 0 );
525 return LDAP_OBJECT_CLASS_VIOLATION;
528 if( oc == slap_schema.si_oc_extensibleObject ) {
538 /* check that each attr in the entry is allowed by some oc */
539 for ( a = e->e_attrs; a != NULL; a = a->a_next ) {
542 #ifdef SLAP_EXTENDED_SCHEMA
543 ret = LDAP_OBJECT_CLASS_VIOLATION;
545 if( cr && cr->scr_required ) {
546 for( i=0; cr->scr_required[i]; i++ ) {
547 if( cr->scr_required[i] == a->a_desc->ad_type ) {
554 if( ret != LDAP_SUCCESS && cr && cr->scr_allowed ) {
555 for( i=0; cr->scr_allowed[i]; i++ ) {
556 if( cr->scr_allowed[i] == a->a_desc->ad_type ) {
563 if( ret != LDAP_SUCCESS )
564 #endif /* SLAP_EXTENDED_SCHEMA */
566 ret = oc_check_allowed( a->a_desc->ad_type, aoc->a_vals, sc );
569 if ( ret != LDAP_SUCCESS ) {
570 char *type = a->a_desc->ad_cname.bv_val;
572 snprintf( textbuf, textlen,
573 "attribute '%s' not allowed",
577 LDAP_LOG( OPERATION, INFO,
578 "entry_schema_check: dn=\"%s\" %s\n", e->e_dn, textbuf, 0);
580 Debug( LDAP_DEBUG_ANY,
582 e->e_dn, textbuf, 0 );
596 struct berval *ocname )
603 LDAP_LOG( OPERATION, ENTRY,
604 "oc_check_required: dn (%s), objectClass \"%s\"\n",
605 e->e_dn, ocname->bv_val, 0 );
607 Debug( LDAP_DEBUG_TRACE,
608 "oc_check_required entry (%s), objectClass \"%s\"\n",
609 e->e_dn, ocname->bv_val, 0 );
613 /* check for empty oc_required */
614 if(oc->soc_required == NULL) {
618 /* for each required attribute */
619 for ( i = 0; oc->soc_required[i] != NULL; i++ ) {
620 at = oc->soc_required[i];
621 /* see if it's in the entry */
622 for ( a = e->e_attrs; a != NULL; a = a->a_next ) {
623 if( a->a_desc->ad_type == at ) {
627 /* not there => schema violation */
629 return at->sat_cname.bv_val;
636 int oc_check_allowed(
644 LDAP_LOG( OPERATION, ENTRY,
645 "oc_check_allowed: type \"%s\"\n", at->sat_cname.bv_val, 0, 0 );
647 Debug( LDAP_DEBUG_TRACE,
648 "oc_check_allowed type \"%s\"\n",
649 at->sat_cname.bv_val, 0, 0 );
652 /* always allow objectClass attribute */
653 if ( strcasecmp( at->sat_cname.bv_val, "objectClass" ) == 0 ) {
658 * All operational attributions are allowed by schema rules.
660 if( is_at_operational(at) ) {
664 /* check to see if its allowed by the structuralObjectClass */
666 /* does it require the type? */
667 for ( j = 0; sc->soc_required != NULL &&
668 sc->soc_required[j] != NULL; j++ )
670 if( at == sc->soc_required[j] ) {
675 /* does it allow the type? */
676 for ( j = 0; sc->soc_allowed != NULL &&
677 sc->soc_allowed[j] != NULL; j++ )
679 if( at == sc->soc_allowed[j] ) {
685 /* check that the type appears as req or opt in at least one oc */
686 for ( i = 0; ocl[i].bv_val != NULL; i++ ) {
687 /* if we know about the oc */
688 ObjectClass *oc = oc_bvfind( &ocl[i] );
689 if ( oc != NULL && oc->soc_kind != LDAP_SCHEMA_ABSTRACT &&
690 ( sc == NULL || oc->soc_kind == LDAP_SCHEMA_AUXILIARY ))
692 /* does it require the type? */
693 for ( j = 0; oc->soc_required != NULL &&
694 oc->soc_required[j] != NULL; j++ )
696 if( at == oc->soc_required[j] ) {
700 /* does it allow the type? */
701 for ( j = 0; oc->soc_allowed != NULL &&
702 oc->soc_allowed[j] != NULL; j++ )
704 if( at == oc->soc_allowed[j] ) {
711 /* not allowed by any oc */
712 return LDAP_OBJECT_CLASS_VIOLATION;
716 * Determine the structural object class from a set of OIDs
718 int structural_class(
723 char *textbuf, size_t textlen )
727 ObjectClass *sc = NULL;
730 *text = "structural_class: internal error";
733 for( i=0; ocs[i].bv_val; i++ ) {
734 oc = oc_bvfind( &ocs[i] );
737 snprintf( textbuf, textlen,
738 "unrecognized objectClass '%s'",
741 return LDAP_OBJECT_CLASS_VIOLATION;
744 if( oc->soc_kind == LDAP_SCHEMA_STRUCTURAL ) {
745 if( sc == NULL || is_object_subclass( sc, oc ) ) {
749 } else if ( !is_object_subclass( oc, sc ) ) {
751 ObjectClass *xc = NULL;
753 /* find common superior */
754 for( j=i+1; ocs[j].bv_val; j++ ) {
755 xc = oc_bvfind( &ocs[j] );
758 snprintf( textbuf, textlen,
759 "unrecognized objectClass '%s'",
762 return LDAP_OBJECT_CLASS_VIOLATION;
765 if( xc->soc_kind != LDAP_SCHEMA_STRUCTURAL ) {
770 if( is_object_subclass( sc, xc ) &&
771 is_object_subclass( oc, xc ) )
773 /* found common subclass */
781 /* no common subclass */
782 snprintf( textbuf, textlen,
783 "invalid structural object class chain (%s/%s)",
784 ocs[scn].bv_val, ocs[i].bv_val );
786 return LDAP_OBJECT_CLASS_VIOLATION;
797 *text = "no structural object class provided";
798 return LDAP_OBJECT_CLASS_VIOLATION;
802 *text = "invalid structural object class";
803 return LDAP_OBJECT_CLASS_VIOLATION;
808 if( scbv->bv_len == 0 ) {
809 *text = "invalid structural object class";
810 return LDAP_OBJECT_CLASS_VIOLATION;
817 * Return structural object class from list of modifications
819 int mods_structural_class(
823 char *textbuf, size_t textlen )
825 Modifications *ocmod = NULL;
827 for( ; mods != NULL; mods = mods->sml_next ) {
828 if( mods->sml_desc == slap_schema.si_ad_objectClass ) {
829 if( ocmod != NULL ) {
830 *text = "entry has multiple objectClass attributes";
831 return LDAP_OBJECT_CLASS_VIOLATION;
837 if( ocmod == NULL ) {
838 *text = "entry has no objectClass attribute";
839 return LDAP_OBJECT_CLASS_VIOLATION;
842 if( ocmod->sml_bvalues == NULL || ocmod->sml_bvalues[0].bv_val == NULL ) {
843 *text = "objectClass attribute has no values";
844 return LDAP_OBJECT_CLASS_VIOLATION;
847 return structural_class( ocmod->sml_bvalues, sc, NULL,
848 text, textbuf, textlen );