1 /* schema_check.c - routines to enforce schema definitions */
2 /* $OpenLDAP: pkg/ldap/servers/slapd/schema_check.c,v 1.103.2.6 2008/04/18 22:33:55 ando Exp $ */
3 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
5 * Copyright 1998-2008 The OpenLDAP Foundation.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted only as authorized by the OpenLDAP
12 * A copy of this license is available in the file LICENSE in the
13 * top-level directory of the distribution or, alternatively, at
14 * <http://www.OpenLDAP.org/license.html>.
22 #include <ac/string.h>
23 #include <ac/socket.h>
27 static char * oc_check_required(
30 struct berval
*ocname
);
32 static int entry_naming_check(
37 char *textbuf
, size_t textlen
);
39 * entry_schema_check - check that entry e conforms to the schema required
40 * by its object class(es).
42 * returns 0 if so, non-zero otherwise.
53 char *textbuf
, size_t textlen
)
55 Attribute
*a
, *asc
= NULL
, *aoc
= NULL
;
56 ObjectClass
*sc
, *oc
, **socs
= NULL
;
60 AttributeDescription
*ad_structuralObjectClass
61 = slap_schema
.si_ad_structuralObjectClass
;
62 AttributeDescription
*ad_objectClass
63 = slap_schema
.si_ad_objectClass
;
65 int subentry
= is_entry_subentry( e
);
66 int collectiveSubentry
= 0;
68 if ( SLAP_NO_SCHEMA_CHECK( op
->o_bd
)) {
72 if ( get_no_schema_check( op
) ) {
77 collectiveSubentry
= is_entry_collectiveAttributeSubentry( e
);
82 /* misc attribute checks */
83 for ( a
= e
->e_attrs
; a
!= NULL
; a
= a
->a_next
) {
84 const char *type
= a
->a_desc
->ad_cname
.bv_val
;
86 /* there should be at least one value */
87 assert( a
->a_vals
!= NULL
);
88 assert( a
->a_vals
[0].bv_val
!= NULL
);
90 if( a
->a_desc
->ad_type
->sat_check
) {
91 rc
= (a
->a_desc
->ad_type
->sat_check
)(
92 op
->o_bd
, e
, a
, text
, textbuf
, textlen
);
93 if( rc
!= LDAP_SUCCESS
) {
98 if( a
->a_desc
== ad_structuralObjectClass
)
100 else if ( a
->a_desc
== ad_objectClass
)
103 if( !collectiveSubentry
&& is_at_collective( a
->a_desc
->ad_type
) ) {
104 snprintf( textbuf
, textlen
,
105 "'%s' can only appear in collectiveAttributeSubentry",
107 return LDAP_OBJECT_CLASS_VIOLATION
;
110 /* if single value type, check for multiple values */
111 if( is_at_single_value( a
->a_desc
->ad_type
) &&
112 a
->a_vals
[1].bv_val
!= NULL
)
114 snprintf( textbuf
, textlen
,
115 "attribute '%s' cannot have multiple values",
118 Debug( LDAP_DEBUG_ANY
,
120 e
->e_dn
, textbuf
, 0 );
122 return LDAP_CONSTRAINT_VIOLATION
;
126 /* check the object class attribute */
128 Debug( LDAP_DEBUG_ANY
, "No objectClass for entry (%s)\n",
131 *text
= "no objectClass attribute";
132 return LDAP_OBJECT_CLASS_VIOLATION
;
135 assert( aoc
->a_vals
!= NULL
);
136 assert( aoc
->a_vals
[0].bv_val
!= NULL
);
138 /* check the structural object class attribute */
139 if ( asc
== NULL
&& !add
) {
140 Debug( LDAP_DEBUG_ANY
,
141 "No structuralObjectClass for entry (%s)\n",
144 *text
= "no structuralObjectClass operational attribute";
148 rc
= structural_class( aoc
->a_vals
, &oc
, &socs
, text
, textbuf
, textlen
,
150 if( rc
!= LDAP_SUCCESS
) {
154 if ( asc
== NULL
&& add
) {
155 attr_merge_one( e
, ad_structuralObjectClass
, &oc
->soc_cname
, NULL
);
156 asc
= attr_find( e
->e_attrs
, ad_structuralObjectClass
);
161 assert( asc
->a_vals
!= NULL
);
162 assert( asc
->a_vals
[0].bv_val
!= NULL
);
163 assert( asc
->a_vals
[1].bv_val
== NULL
);
165 sc
= oc_bvfind( &asc
->a_vals
[0] );
167 snprintf( textbuf
, textlen
,
168 "unrecognized structuralObjectClass '%s'",
169 asc
->a_vals
[0].bv_val
);
171 Debug( LDAP_DEBUG_ANY
,
172 "entry_check_schema(%s): %s\n",
173 e
->e_dn
, textbuf
, 0 );
175 rc
= LDAP_OBJECT_CLASS_VIOLATION
;
179 if( sc
->soc_kind
!= LDAP_SCHEMA_STRUCTURAL
) {
180 snprintf( textbuf
, textlen
,
181 "structuralObjectClass '%s' is not STRUCTURAL",
182 asc
->a_vals
[0].bv_val
);
184 Debug( LDAP_DEBUG_ANY
,
185 "entry_check_schema(%s): %s\n",
186 e
->e_dn
, textbuf
, 0 );
193 if( !manage
&& sc
->soc_obsolete
) {
194 snprintf( textbuf
, textlen
,
195 "structuralObjectClass '%s' is OBSOLETE",
196 asc
->a_vals
[0].bv_val
);
198 Debug( LDAP_DEBUG_ANY
,
199 "entry_check_schema(%s): %s\n",
200 e
->e_dn
, textbuf
, 0 );
202 rc
= LDAP_OBJECT_CLASS_VIOLATION
;
209 snprintf( textbuf
, textlen
,
210 "unrecognized objectClass '%s'",
211 aoc
->a_vals
[0].bv_val
);
212 rc
= LDAP_OBJECT_CLASS_VIOLATION
;
215 } else if ( sc
!= slap_schema
.si_oc_glue
&& sc
!= oc
) {
216 snprintf( textbuf
, textlen
,
217 "structural object class modification "
218 "from '%s' to '%s' not allowed",
219 asc
->a_vals
[0].bv_val
, oc
->soc_cname
.bv_val
);
220 rc
= LDAP_NO_OBJECT_CLASS_MODS
;
222 } else if ( sc
== slap_schema
.si_oc_glue
) {
227 if ( !is_entry_glue ( e
) ) {
228 rc
= entry_naming_check( e
, manage
, add
, text
, textbuf
, textlen
);
229 if( rc
!= LDAP_SUCCESS
) {
236 /* find the content rule for the structural class */
237 cr
= cr_find( sc
->soc_oid
);
239 /* the cr must be same as the structural class */
240 assert( !cr
|| !strcmp( cr
->scr_oid
, sc
->soc_oid
) );
242 /* check that the entry has required attrs of the content rule */
244 if( !manage
&& cr
->scr_obsolete
) {
245 snprintf( textbuf
, textlen
,
246 "content rule '%s' is obsolete",
247 ldap_contentrule2name( &cr
->scr_crule
));
249 Debug( LDAP_DEBUG_ANY
,
251 e
->e_dn
, textbuf
, 0 );
253 rc
= LDAP_OBJECT_CLASS_VIOLATION
;
257 if( cr
->scr_required
) for( i
=0; cr
->scr_required
[i
]; i
++ ) {
258 at
= cr
->scr_required
[i
];
260 for ( a
= e
->e_attrs
; a
!= NULL
; a
= a
->a_next
) {
261 if( a
->a_desc
->ad_type
== at
) {
266 /* not there => schema violation */
268 snprintf( textbuf
, textlen
,
269 "content rule '%s' requires attribute '%s'",
270 ldap_contentrule2name( &cr
->scr_crule
),
271 at
->sat_cname
.bv_val
);
273 Debug( LDAP_DEBUG_ANY
,
275 e
->e_dn
, textbuf
, 0 );
277 rc
= LDAP_OBJECT_CLASS_VIOLATION
;
282 if( cr
->scr_precluded
) for( i
=0; cr
->scr_precluded
[i
]; i
++ ) {
283 at
= cr
->scr_precluded
[i
];
285 for ( a
= e
->e_attrs
; a
!= NULL
; a
= a
->a_next
) {
286 if( a
->a_desc
->ad_type
== at
) {
291 /* there => schema violation */
293 snprintf( textbuf
, textlen
,
294 "content rule '%s' precluded attribute '%s'",
295 ldap_contentrule2name( &cr
->scr_crule
),
296 at
->sat_cname
.bv_val
);
298 Debug( LDAP_DEBUG_ANY
,
300 e
->e_dn
, textbuf
, 0 );
302 rc
= LDAP_OBJECT_CLASS_VIOLATION
;
308 /* check that the entry has required attrs for each oc */
309 for ( i
= 0; socs
[i
]; i
++ ) {
311 if ( !manage
&& oc
->soc_obsolete
) {
312 /* disallow obsolete classes */
313 snprintf( textbuf
, textlen
,
314 "objectClass '%s' is OBSOLETE",
315 aoc
->a_vals
[i
].bv_val
);
317 Debug( LDAP_DEBUG_ANY
,
318 "entry_check_schema(%s): %s\n",
319 e
->e_dn
, textbuf
, 0 );
321 rc
= LDAP_OBJECT_CLASS_VIOLATION
;
325 if ( oc
->soc_check
) {
326 rc
= (oc
->soc_check
)( op
->o_bd
, e
, oc
,
327 text
, textbuf
, textlen
);
328 if( rc
!= LDAP_SUCCESS
) {
333 if ( oc
->soc_kind
== LDAP_SCHEMA_ABSTRACT
) {
334 /* object class is abstract */
335 if ( oc
!= slap_schema
.si_oc_top
&&
336 !is_object_subclass( oc
, sc
))
339 ObjectClass
*xc
= NULL
;
340 for( j
=0; socs
[j
]; j
++ ) {
344 /* since we previous check against the
345 * structural object of this entry, the
346 * abstract class must be a (direct or indirect)
347 * superclass of one of the auxiliary classes of
350 if ( xc
->soc_kind
== LDAP_SCHEMA_AUXILIARY
&&
351 is_object_subclass( oc
, xc
) )
360 snprintf( textbuf
, textlen
, "instantiation of "
361 "abstract objectClass '%s' not allowed",
362 aoc
->a_vals
[i
].bv_val
);
364 Debug( LDAP_DEBUG_ANY
,
365 "entry_check_schema(%s): %s\n",
366 e
->e_dn
, textbuf
, 0 );
368 rc
= LDAP_OBJECT_CLASS_VIOLATION
;
373 } else if ( oc
->soc_kind
!= LDAP_SCHEMA_STRUCTURAL
|| oc
== sc
) {
376 if( oc
->soc_kind
== LDAP_SCHEMA_AUXILIARY
) {
383 if( cr
->scr_auxiliaries
) {
384 for( j
= 0; cr
->scr_auxiliaries
[j
]; j
++ ) {
385 if( cr
->scr_auxiliaries
[j
] == oc
) {
392 snprintf( textbuf
, textlen
,
393 "class '%s' not allowed by content rule '%s'",
394 oc
->soc_cname
.bv_val
,
395 ldap_contentrule2name( &cr
->scr_crule
) );
397 } else if ( global_disallows
& SLAP_DISALLOW_AUX_WO_CR
) {
399 snprintf( textbuf
, textlen
,
400 "class '%s' not allowed by any content rule",
401 oc
->soc_cname
.bv_val
);
407 Debug( LDAP_DEBUG_ANY
,
409 e
->e_dn
, textbuf
, 0 );
411 rc
= LDAP_OBJECT_CLASS_VIOLATION
;
416 s
= oc_check_required( e
, oc
, &aoc
->a_vals
[i
] );
418 snprintf( textbuf
, textlen
,
419 "object class '%s' requires attribute '%s'",
420 aoc
->a_vals
[i
].bv_val
, s
);
422 Debug( LDAP_DEBUG_ANY
,
424 e
->e_dn
, textbuf
, 0 );
426 rc
= LDAP_OBJECT_CLASS_VIOLATION
;
430 if( oc
== slap_schema
.si_oc_extensibleObject
) {
442 /* check that each attr in the entry is allowed by some oc */
443 for ( a
= e
->e_attrs
; a
!= NULL
; a
= a
->a_next
) {
444 rc
= LDAP_OBJECT_CLASS_VIOLATION
;
446 if( cr
&& cr
->scr_required
) {
447 for( i
=0; cr
->scr_required
[i
]; i
++ ) {
448 if( cr
->scr_required
[i
] == a
->a_desc
->ad_type
) {
455 if( rc
!= LDAP_SUCCESS
&& cr
&& cr
->scr_allowed
) {
456 for( i
=0; cr
->scr_allowed
[i
]; i
++ ) {
457 if( cr
->scr_allowed
[i
] == a
->a_desc
->ad_type
) {
464 if( rc
!= LDAP_SUCCESS
)
466 rc
= oc_check_allowed( a
->a_desc
->ad_type
, socs
, sc
);
469 if ( rc
!= LDAP_SUCCESS
) {
470 char *type
= a
->a_desc
->ad_cname
.bv_val
;
472 snprintf( textbuf
, textlen
,
473 "attribute '%s' not allowed",
476 Debug( LDAP_DEBUG_ANY
,
478 e
->e_dn
, textbuf
, 0 );
486 slap_sl_free( socs
, op
->o_tmpmemctx
);
494 struct berval
*ocname
)
500 Debug( LDAP_DEBUG_TRACE
,
501 "oc_check_required entry (%s), objectClass \"%s\"\n",
502 e
->e_dn
, ocname
->bv_val
, 0 );
505 /* check for empty oc_required */
506 if(oc
->soc_required
== NULL
) {
510 /* for each required attribute */
511 for ( i
= 0; oc
->soc_required
[i
] != NULL
; i
++ ) {
512 at
= oc
->soc_required
[i
];
513 /* see if it's in the entry */
514 for ( a
= e
->e_attrs
; a
!= NULL
; a
= a
->a_next
) {
515 if( a
->a_desc
->ad_type
== at
) {
519 /* not there => schema violation */
521 return at
->sat_cname
.bv_val
;
528 int oc_check_allowed(
535 Debug( LDAP_DEBUG_TRACE
,
536 "oc_check_allowed type \"%s\"\n",
537 at
->sat_cname
.bv_val
, 0, 0 );
539 /* always allow objectClass attribute */
540 if ( strcasecmp( at
->sat_cname
.bv_val
, "objectClass" ) == 0 ) {
545 * All operational attributions are allowed by schema rules.
547 if( is_at_operational(at
) ) {
551 /* check to see if its allowed by the structuralObjectClass */
553 /* does it require the type? */
554 for ( j
= 0; sc
->soc_required
!= NULL
&&
555 sc
->soc_required
[j
] != NULL
; j
++ )
557 if( at
== sc
->soc_required
[j
] ) {
562 /* does it allow the type? */
563 for ( j
= 0; sc
->soc_allowed
!= NULL
&&
564 sc
->soc_allowed
[j
] != NULL
; j
++ )
566 if( at
== sc
->soc_allowed
[j
] ) {
572 /* check that the type appears as req or opt in at least one oc */
573 for ( i
= 0; socs
[i
]; i
++ ) {
574 /* if we know about the oc */
575 ObjectClass
*oc
= socs
[i
];
576 /* extensibleObject allows all */
577 if ( oc
== slap_schema
.si_oc_extensibleObject
) {
580 if ( oc
!= NULL
&& oc
->soc_kind
!= LDAP_SCHEMA_ABSTRACT
&&
581 ( sc
== NULL
|| oc
->soc_kind
== LDAP_SCHEMA_AUXILIARY
))
583 /* does it require the type? */
584 for ( j
= 0; oc
->soc_required
!= NULL
&&
585 oc
->soc_required
[j
] != NULL
; j
++ )
587 if( at
== oc
->soc_required
[j
] ) {
591 /* does it allow the type? */
592 for ( j
= 0; oc
->soc_allowed
!= NULL
&&
593 oc
->soc_allowed
[j
] != NULL
; j
++ )
595 if( at
== oc
->soc_allowed
[j
] ) {
602 /* not allowed by any oc */
603 return LDAP_OBJECT_CLASS_VIOLATION
;
607 * Determine the structural object class from a set of OIDs
609 int structural_class(
612 ObjectClass
***socsp
,
614 char *textbuf
, size_t textlen
,
618 ObjectClass
*oc
, **socs
;
619 ObjectClass
*sc
= NULL
;
622 *text
= "structural_class: internal error";
625 for( i
=0; ocs
[i
].bv_val
; i
++ ) ;
628 socs
= slap_sl_malloc( (nocs
+1) * sizeof(ObjectClass
*), ctx
);
630 for( i
=0; ocs
[i
].bv_val
; i
++ ) {
631 socs
[i
] = oc_bvfind( &ocs
[i
] );
633 if( socs
[i
] == NULL
) {
634 snprintf( textbuf
, textlen
,
635 "unrecognized objectClass '%s'",
643 for( i
=0; ocs
[i
].bv_val
; i
++ ) {
645 if( oc
->soc_kind
== LDAP_SCHEMA_STRUCTURAL
) {
646 if( sc
== NULL
|| is_object_subclass( sc
, oc
) ) {
650 } else if ( !is_object_subclass( oc
, sc
) ) {
652 ObjectClass
*xc
= NULL
;
654 /* find common superior */
655 for( j
=i
+1; ocs
[j
].bv_val
; j
++ ) {
659 snprintf( textbuf
, textlen
,
660 "unrecognized objectClass '%s'",
666 if( xc
->soc_kind
!= LDAP_SCHEMA_STRUCTURAL
) {
671 if( is_object_subclass( sc
, xc
) &&
672 is_object_subclass( oc
, xc
) )
674 /* found common subclass */
682 /* no common subclass */
683 snprintf( textbuf
, textlen
,
684 "invalid structural object class chain (%s/%s)",
685 ocs
[scn
].bv_val
, ocs
[i
].bv_val
);
698 *text
= "no structural object class provided";
703 *text
= "invalid structural object class";
710 slap_sl_free( socs
, ctx
);
717 slap_sl_free( socs
, ctx
);
718 return LDAP_OBJECT_CLASS_VIOLATION
;
722 * Return structural object class from list of modifications
724 int mods_structural_class(
728 char *textbuf
, size_t textlen
, void *ctx
)
730 Modifications
*ocmod
= NULL
;
734 for( ; mods
!= NULL
; mods
= mods
->sml_next
) {
735 if( mods
->sml_desc
== slap_schema
.si_ad_objectClass
) {
736 if( ocmod
!= NULL
) {
737 *text
= "entry has multiple objectClass attributes";
738 return LDAP_OBJECT_CLASS_VIOLATION
;
744 if( ocmod
== NULL
) {
745 *text
= "entry has no objectClass attribute";
746 return LDAP_OBJECT_CLASS_VIOLATION
;
749 if( ocmod
->sml_values
== NULL
|| ocmod
->sml_values
[0].bv_val
== NULL
) {
750 *text
= "objectClass attribute has no values";
751 return LDAP_OBJECT_CLASS_VIOLATION
;
754 rc
= structural_class( ocmod
->sml_values
, &ssc
, NULL
,
755 text
, textbuf
, textlen
, ctx
);
756 if ( rc
== LDAP_SUCCESS
)
757 *sc
= ssc
->soc_cname
;
768 char *textbuf
, size_t textlen
)
772 const char *p
= NULL
;
774 int rc
= LDAP_SUCCESS
;
776 if ( BER_BVISEMPTY( &e
->e_name
)) {
781 * Get attribute type(s) and attribute value(s) of our RDN
783 if ( ldap_bv2rdn( &e
->e_name
, &rdn
, (char **)&p
,
784 LDAP_DN_FORMAT_LDAP
) )
786 *text
= "unrecongized attribute type(s) in RDN";
787 return LDAP_INVALID_DN_SYNTAX
;
790 /* Check that each AVA of the RDN is present in the entry */
791 /* FIXME: Should also check that each AVA lists a distinct type */
792 for ( cnt
= 0; rdn
[cnt
]; cnt
++ ) {
793 LDAPAVA
*ava
= rdn
[cnt
];
794 AttributeDescription
*desc
= NULL
;
799 if( ava
->la_flags
& LDAP_AVA_BINARY
) {
800 snprintf( textbuf
, textlen
,
801 "value of naming attribute '%s' in unsupported BER form",
802 ava
->la_attr
.bv_val
);
803 rc
= LDAP_NAMING_VIOLATION
;
806 rc
= slap_bv2ad( &ava
->la_attr
, &desc
, &errtext
);
807 if ( rc
!= LDAP_SUCCESS
) {
808 snprintf( textbuf
, textlen
, "%s (in RDN)", errtext
);
812 if( desc
->ad_type
->sat_usage
) {
813 snprintf( textbuf
, textlen
,
814 "naming attribute '%s' is operational",
815 ava
->la_attr
.bv_val
);
816 rc
= LDAP_NAMING_VIOLATION
;
820 if( desc
->ad_type
->sat_collective
) {
821 snprintf( textbuf
, textlen
,
822 "naming attribute '%s' is collective",
823 ava
->la_attr
.bv_val
);
824 rc
= LDAP_NAMING_VIOLATION
;
828 if( !manage
&& desc
->ad_type
->sat_obsolete
) {
829 snprintf( textbuf
, textlen
,
830 "naming attribute '%s' is obsolete",
831 ava
->la_attr
.bv_val
);
832 rc
= LDAP_NAMING_VIOLATION
;
836 if( !desc
->ad_type
->sat_equality
) {
837 snprintf( textbuf
, textlen
,
838 "naming attribute '%s' has no equality matching rule",
839 ava
->la_attr
.bv_val
);
840 rc
= LDAP_NAMING_VIOLATION
;
844 if( !desc
->ad_type
->sat_equality
->smr_match
) {
845 snprintf( textbuf
, textlen
,
846 "naming attribute '%s' has unsupported equality matching rule",
847 ava
->la_attr
.bv_val
);
848 rc
= LDAP_NAMING_VIOLATION
;
852 /* find the naming attribute */
853 attr
= attr_find( e
->e_attrs
, desc
);
854 if ( attr
== NULL
) {
855 snprintf( textbuf
, textlen
,
856 "naming attribute '%s' is not present in entry",
857 ava
->la_attr
.bv_val
);
862 rc
= LDAP_NAMING_VIOLATION
;
866 rc
= attr_valfind( attr
, SLAP_MR_VALUE_OF_ASSERTION_SYNTAX
|
867 SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH
,
868 &ava
->la_value
, NULL
, NULL
);
872 case LDAP_INAPPROPRIATE_MATCHING
:
873 snprintf( textbuf
, textlen
,
874 "inappropriate matching for naming attribute '%s'",
875 ava
->la_attr
.bv_val
);
877 case LDAP_INVALID_SYNTAX
:
878 snprintf( textbuf
, textlen
,
879 "value of naming attribute '%s' is invalid",
880 ava
->la_attr
.bv_val
);
882 case LDAP_NO_SUCH_ATTRIBUTE
:
883 snprintf( textbuf
, textlen
,
884 "value of naming attribute '%s' is not present in entry",
885 ava
->la_attr
.bv_val
);
891 snprintf( textbuf
, textlen
,
892 "naming attribute '%s' is inappropriate",
893 ava
->la_attr
.bv_val
);
895 rc
= LDAP_NAMING_VIOLATION
;
900 attr_merge_normalize_one( e
, desc
, &ava
->la_value
, NULL
);
902 } else if ( rc
!= LDAP_SUCCESS
) {