]> git.sur5r.net Git - openldap/blob - servers/slapd/schema_check.c
ac1986161be24c37062bf8e0da17fdc7770a7446
[openldap] / servers / slapd / schema_check.c
1 /* schema_check.c - routines to enforce schema definitions */
2 /* $OpenLDAP$ */
3 /*
4  * Copyright 1998-2002 The OpenLDAP Foundation, All Rights Reserved.
5  * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
6  */
7
8 #include "portable.h"
9
10 #include <stdio.h>
11
12 #include <ac/ctype.h>
13 #include <ac/string.h>
14 #include <ac/socket.h>
15
16 #include "slap.h"
17 #include "ldap_pvt.h"
18
19 static char * oc_check_required(
20         Entry *e,
21         ObjectClass *oc,
22         struct berval *ocname );
23
24 /*
25  * entry_schema_check - check that entry e conforms to the schema required
26  * by its object class(es).
27  *
28  * returns 0 if so, non-zero otherwise.
29  */
30
31 int
32 entry_schema_check( 
33         Backend *be,
34         Entry *e,
35         Attribute *oldattrs,
36         const char** text,
37         char *textbuf, size_t textlen )
38 {
39         Attribute       *a, *asc, *aoc;
40         ObjectClass *sc, *oc;
41 #ifdef SLAP_EXTENDED_SCHEMA
42         AttributeType *at;
43         ContentRule *cr;
44 #endif
45         int     rc, i;
46         struct berval nsc;
47         AttributeDescription *ad_structuralObjectClass
48                 = slap_schema.si_ad_structuralObjectClass;
49         AttributeDescription *ad_objectClass
50                 = slap_schema.si_ad_objectClass;
51         int extensible = 0;
52         int subentry = is_entry_subentry( e );
53         int collectiveSubentry = 0;
54
55         if( subentry ) {
56                 collectiveSubentry = is_entry_collectiveAttributeSubentry( e );
57         }
58
59         *text = textbuf;
60
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;
64
65                 /* there should be at least one value */
66                 assert( a->a_vals );
67                 assert( a->a_vals[0].bv_val != NULL ); 
68
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 ) {
73                                 return rc;
74                         }
75                 }
76
77                 if( !collectiveSubentry && is_at_collective( a->a_desc->ad_type ) ) {
78                         snprintf( textbuf, textlen,
79                                 "'%s' can only appear in collectiveAttributeSubentry",
80                                 type );
81                         return LDAP_OBJECT_CLASS_VIOLATION;
82                 }
83
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 )
87                 {
88                         snprintf( textbuf, textlen, 
89                                 "attribute '%s' cannot have multiple values",
90                                 type );
91
92 #ifdef NEW_LOGGING
93                         LDAP_LOG( OPERATION, INFO, 
94                                 "entry_schema_check: dn=\"%s\" %s\n", e->e_dn, textbuf, 0 );
95 #else
96                         Debug( LDAP_DEBUG_ANY,
97                             "Entry (%s), %s\n",
98                             e->e_dn, textbuf, 0 );
99 #endif
100
101                         return LDAP_CONSTRAINT_VIOLATION;
102                 }
103         }
104
105         /* it's a REALLY bad idea to disable schema checks */
106         if( !global_schemacheck ) return LDAP_SUCCESS;
107
108         /* find the structural object class attribute */
109         asc = attr_find( e->e_attrs, ad_structuralObjectClass );
110         if ( asc == NULL ) {
111 #ifdef NEW_LOGGING
112                 LDAP_LOG( OPERATION, INFO, 
113                         "entry_schema_check: No structuralObjectClass for entry (%s)\n", 
114                         e->e_dn, 0, 0 );
115 #else
116                 Debug( LDAP_DEBUG_ANY,
117                         "No structuralObjectClass for entry (%s)\n",
118                     e->e_dn, 0, 0 );
119 #endif
120
121                 *text = "no structuralObjectClass operational attribute";
122                 return LDAP_OTHER;
123         }
124
125         assert( asc->a_vals != NULL );
126         assert( asc->a_vals[0].bv_val != NULL );
127         assert( asc->a_vals[1].bv_val == NULL );
128
129         sc = oc_bvfind( &asc->a_vals[0] );
130         if( sc == NULL ) {
131                 snprintf( textbuf, textlen, 
132                         "unrecognized structuralObjectClass '%s'",
133                         asc->a_vals[0].bv_val );
134
135 #ifdef NEW_LOGGING
136                 LDAP_LOG( OPERATION, INFO, 
137                         "entry_schema_check: dn (%s), %s\n", e->e_dn, textbuf, 0 );
138 #else
139                 Debug( LDAP_DEBUG_ANY,
140                         "entry_check_schema(%s): %s\n",
141                         e->e_dn, textbuf, 0 );
142 #endif
143
144                 return LDAP_OBJECT_CLASS_VIOLATION;
145         }
146
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 );
151
152 #ifdef NEW_LOGGING
153                 LDAP_LOG( OPERATION, INFO, 
154                         "entry_schema_check: dn (%s), %s\n", e->e_dn, textbuf, 0 );
155 #else
156                 Debug( LDAP_DEBUG_ANY,
157                         "entry_check_schema(%s): %s\n",
158                         e->e_dn, textbuf, 0 );
159 #endif
160
161                 return LDAP_OTHER;
162         }
163
164         /* find the object class attribute */
165         aoc = attr_find( e->e_attrs, ad_objectClass );
166         if ( aoc == NULL ) {
167 #ifdef NEW_LOGGING
168                 LDAP_LOG( OPERATION, INFO, 
169                         "entry_schema_check: No objectClass for entry (%s).\n", 
170                         e->e_dn, 0, 0 );
171 #else
172                 Debug( LDAP_DEBUG_ANY, "No objectClass for entry (%s)\n",
173                     e->e_dn, 0, 0 );
174 #endif
175
176                 *text = "no objectClass attribute";
177                 return LDAP_OBJECT_CLASS_VIOLATION;
178         }
179
180         assert( aoc->a_vals != NULL );
181         assert( aoc->a_vals[0].bv_val != NULL );
182
183         rc = structural_class( aoc->a_vals, &nsc, &oc, text, textbuf, textlen );
184         if( rc != LDAP_SUCCESS ) {
185                 return rc;
186         }
187
188         *text = textbuf;
189
190         if ( oc == NULL ) {
191                 snprintf( textbuf, textlen, 
192                         "unrecognized objectClass '%s'",
193                         aoc->a_vals[0].bv_val );
194                 return LDAP_OBJECT_CLASS_VIOLATION;
195
196         } else if ( sc != oc ) {
197                 snprintf( textbuf, textlen, 
198                         "structural object class modification from '%s' to '%s' not allowed",
199                         asc->a_vals[0].bv_val, nsc.bv_val );
200                 return LDAP_NO_OBJECT_CLASS_MODS;
201         }
202
203 #ifdef SLAP_EXTENDED_SCHEMA
204         /* find the content rule for the structural class */
205         cr = cr_find( sc->soc_oid );
206
207         /* the cr must be same as the structural class */
208         assert( !cr || !strcmp( cr->scr_oid, sc->soc_oid ) );
209
210         /* check that the entry has required attrs of the content rule */
211         if( cr && cr->scr_required ) {
212                 for( i=0; cr->scr_required[i]; i++ ) {
213                         at = cr->scr_required[i];
214
215                         for ( a = e->e_attrs; a != NULL; a = a->a_next ) {
216                                 if( a->a_desc->ad_type == at ) {
217                                         break;
218                                 }
219                         }
220
221                         /* not there => schema violation */
222                         if ( a == NULL ) {
223                                 snprintf( textbuf, textlen, 
224                                         "content rule '%s' requires attribute '%s'",
225                                         ldap_contentrule2name( &cr->scr_crule ),
226                                         at->sat_cname.bv_val );
227
228 #ifdef NEW_LOGGING
229                                 LDAP_LOG( OPERATION, INFO, 
230                                         "entry_schema_check: dn=\"%s\" %s", e->e_dn, textbuf, 0 );
231 #else
232                                 Debug( LDAP_DEBUG_ANY,
233                                         "Entry (%s): %s\n",
234                                         e->e_dn, textbuf, 0 );
235 #endif
236
237                                 return LDAP_OBJECT_CLASS_VIOLATION;
238                         }
239                 }
240         }
241
242         if( cr && cr->scr_precluded ) {
243                 for( i=0; cr->scr_precluded[i]; i++ ) {
244                         at = cr->scr_precluded[i];
245
246                         for ( a = e->e_attrs; a != NULL; a = a->a_next ) {
247                                 if( a->a_desc->ad_type == at ) {
248                                         break;
249                                 }
250                         }
251
252                         /* there => schema violation */
253                         if ( a != NULL ) {
254                                 snprintf( textbuf, textlen, 
255                                         "content rule '%s' precluded attribute '%s'",
256                                         ldap_contentrule2name( &cr->scr_crule ),
257                                         at->sat_cname.bv_val );
258
259 #ifdef NEW_LOGGING
260                                 LDAP_LOG( OPERATION, INFO, 
261                                         "entry_schema_check: dn=\"%s\" %s", e->e_dn, textbuf, 0 );
262 #else
263                                 Debug( LDAP_DEBUG_ANY,
264                                         "Entry (%s): %s\n",
265                                         e->e_dn, textbuf, 0 );
266 #endif
267
268                                 return LDAP_OBJECT_CLASS_VIOLATION;
269                         }
270                 }
271         }
272 #endif /* SLAP_EXTENDED_SCHEMA */
273
274         /* check that the entry has required attrs for each oc */
275         for ( i = 0; aoc->a_vals[i].bv_val != NULL; i++ ) {
276                 if ( (oc = oc_bvfind( &aoc->a_vals[i] )) == NULL ) {
277                         snprintf( textbuf, textlen, 
278                                 "unrecognized objectClass '%s'",
279                                 aoc->a_vals[i].bv_val );
280
281 #ifdef NEW_LOGGING
282                         LDAP_LOG( OPERATION, INFO, 
283                                 "entry_schema_check: dn (%s), %s\n", e->e_dn, textbuf, 0 );
284 #else
285                         Debug( LDAP_DEBUG_ANY,
286                                 "entry_check_schema(%s): %s\n",
287                                 e->e_dn, textbuf, 0 );
288 #endif
289
290                         return LDAP_OBJECT_CLASS_VIOLATION;
291                 }
292
293                 if ( oc->soc_check ) {
294                         int rc = (oc->soc_check)( be, e, oc,
295                                 text, textbuf, textlen );
296                         if( rc != LDAP_SUCCESS ) {
297                                 return rc;
298                         }
299                 }
300
301                 if ( oc->soc_kind == LDAP_SCHEMA_ABSTRACT ) {
302                         /* object class is abstract */
303                         if ( oc != slap_schema.si_oc_top &&
304                                 !is_object_subclass( oc, sc ))
305                         {
306                                 int j;
307                                 ObjectClass *xc = NULL;
308                                 for( j=0; aoc->a_vals[j].bv_val; j++ ) {
309                                         if( i != j ) {
310                                                 xc = oc_bvfind( &aoc->a_vals[i] );
311                                                 if( xc == NULL ) {
312                                                         snprintf( textbuf, textlen, 
313                                                                 "unrecognized objectClass '%s'",
314                                                                 aoc->a_vals[i].bv_val );
315
316 #ifdef NEW_LOGGING
317                                                         LDAP_LOG( OPERATION, INFO, 
318                                                                 "entry_schema_check: dn (%s), %s\n",
319                                                                 e->e_dn, textbuf, 0 );
320 #else
321                                                         Debug( LDAP_DEBUG_ANY,
322                                                                 "entry_check_schema(%s): %s\n",
323                                                                 e->e_dn, textbuf, 0 );
324 #endif
325
326                                                         return LDAP_OBJECT_CLASS_VIOLATION;
327                                                 }
328
329                                                 /* since we previous check against the
330                                                  * structural object of this entry, the
331                                                  * abstract class must be a (direct or indirect)
332                                                  * superclass of one of the auxiliary classes of
333                                                  * the entry.
334                                                  */
335                                                 if ( xc->soc_kind == LDAP_SCHEMA_AUXILIARY &&
336                                                         is_object_subclass( oc, xc ) )
337                                                 {
338                                                         xc = NULL;
339                                                         break;
340                                                 }
341                                         }
342                                 }
343
344                                 if( xc == NULL ) {
345                                         snprintf( textbuf, textlen, "instanstantiation of "
346                                                 "abstract objectClass '%s' not allowed",
347                                                 aoc->a_vals[i].bv_val );
348
349 #ifdef NEW_LOGGING
350                                         LDAP_LOG( OPERATION, INFO, 
351                                                 "entry_schema_check: dn (%s), %s\n", 
352                                                 e->e_dn, textbuf, 0 );
353 #else
354                                         Debug( LDAP_DEBUG_ANY,
355                                                 "entry_check_schema(%s): %s\n",
356                                                 e->e_dn, textbuf, 0 );
357 #endif
358
359                                         return LDAP_OBJECT_CLASS_VIOLATION;
360                                 }
361                         }
362
363                 } else if ( oc->soc_kind != LDAP_SCHEMA_STRUCTURAL || oc == sc ) {
364                         char *s;
365
366 #ifdef SLAP_EXTENDED_SCHEMA
367                         if( oc->soc_kind == LDAP_SCHEMA_AUXILIARY ) {
368                                 int k=0;
369                                 if( cr ) {
370                                         if( cr->scr_auxiliaries ) {
371                                                 for( ; cr->scr_auxiliaries[k]; k++ ) {
372                                                         if( cr->scr_auxiliaries[k] == oc ) {
373                                                                 k=-1;
374                                                                 break;
375                                                         }
376                                                 }
377                                         }
378                                 } else if ( global_disallows & SLAP_DISALLOW_AUX_WO_CR ) {
379                                         k=-1;
380                                 }
381
382                                 if( k == -1 ) {
383                                         snprintf( textbuf, textlen, 
384                                                 "content rule '%s' does not allow class '%s'",
385                                                 ldap_contentrule2name( &cr->scr_crule ),
386                                                 oc->soc_cname.bv_val );
387
388 #ifdef NEW_LOGGING
389                                         LDAP_LOG( OPERATION, INFO, 
390                                                 "entry_schema_check: dn=\"%s\" %s",
391                                                 e->e_dn, textbuf, 0 );
392 #else
393                                         Debug( LDAP_DEBUG_ANY,
394                                                 "Entry (%s): %s\n",
395                                                 e->e_dn, textbuf, 0 );
396 #endif
397
398                                         return LDAP_OBJECT_CLASS_VIOLATION;
399                                 }
400                         }
401 #endif /* SLAP_EXTENDED_SCHEMA */
402
403                         s = oc_check_required( e, oc, &aoc->a_vals[i] );
404                         if (s != NULL) {
405                                 snprintf( textbuf, textlen, 
406                                         "object class '%s' requires attribute '%s'",
407                                         aoc->a_vals[i].bv_val, s );
408
409 #ifdef NEW_LOGGING
410                                 LDAP_LOG( OPERATION, INFO, 
411                                         "entry_schema_check: dn=\"%s\" %s", e->e_dn, textbuf, 0 );
412 #else
413                                 Debug( LDAP_DEBUG_ANY,
414                                         "Entry (%s): %s\n",
415                                         e->e_dn, textbuf, 0 );
416 #endif
417
418                                 return LDAP_OBJECT_CLASS_VIOLATION;
419                         }
420
421                         if( oc == slap_schema.si_oc_extensibleObject ) {
422                                 extensible=1;
423                         }
424                 }
425         }
426
427         if( extensible ) {
428                 return LDAP_SUCCESS;
429         }
430
431         /* check that each attr in the entry is allowed by some oc */
432         for ( a = e->e_attrs; a != NULL; a = a->a_next ) {
433                 int ret;
434
435 #ifdef SLAP_EXTENDED_SCHEMA
436                 ret = LDAP_OBJECT_CLASS_VIOLATION;
437
438                 if( cr && cr->scr_required ) {
439                         for( i=0; cr->scr_required[i]; i++ ) {
440                                 if( cr->scr_required[i] == a->a_desc->ad_type ) {
441                                         ret = LDAP_SUCCESS;
442                                         break;
443                                 }
444                         }
445                 }
446
447                 if( ret != LDAP_SUCCESS && cr && cr->scr_allowed ) {
448                         for( i=0; cr->scr_allowed[i]; i++ ) {
449                                 if( cr->scr_allowed[i] == a->a_desc->ad_type ) {
450                                         ret = LDAP_SUCCESS;
451                                         break;
452                                 }
453                         }
454                 }
455
456                 if( ret != LDAP_SUCCESS ) 
457 #endif /* SLAP_EXTENDED_SCHEMA */
458                 {
459                         ret = oc_check_allowed( a->a_desc->ad_type, aoc->a_vals, sc );
460                 }
461
462                 if ( ret != LDAP_SUCCESS ) {
463                         char *type = a->a_desc->ad_cname.bv_val;
464
465                         snprintf( textbuf, textlen, 
466                                 "attribute '%s' not allowed",
467                                 type );
468
469 #ifdef NEW_LOGGING
470                         LDAP_LOG( OPERATION, INFO, 
471                                 "entry_schema_check: dn=\"%s\" %s\n", e->e_dn, textbuf, 0);
472 #else
473                         Debug( LDAP_DEBUG_ANY,
474                             "Entry (%s), %s\n",
475                             e->e_dn, textbuf, 0 );
476 #endif
477
478                         return ret;
479                 }
480         }
481
482         return LDAP_SUCCESS;
483 }
484
485 static char *
486 oc_check_required(
487         Entry *e,
488         ObjectClass *oc,
489         struct berval *ocname )
490 {
491         AttributeType   *at;
492         int             i;
493         Attribute       *a;
494
495 #ifdef NEW_LOGGING
496         LDAP_LOG( OPERATION, ENTRY, 
497                 "oc_check_required: dn (%s), objectClass \"%s\"\n", 
498                 e->e_dn, ocname->bv_val, 0 );
499 #else
500         Debug( LDAP_DEBUG_TRACE,
501                 "oc_check_required entry (%s), objectClass \"%s\"\n",
502                 e->e_dn, ocname->bv_val, 0 );
503 #endif
504
505
506         /* check for empty oc_required */
507         if(oc->soc_required == NULL) {
508                 return NULL;
509         }
510
511         /* for each required attribute */
512         for ( i = 0; oc->soc_required[i] != NULL; i++ ) {
513                 at = oc->soc_required[i];
514                 /* see if it's in the entry */
515                 for ( a = e->e_attrs; a != NULL; a = a->a_next ) {
516                         if( a->a_desc->ad_type == at ) {
517                                 break;
518                         }
519                 }
520                 /* not there => schema violation */
521                 if ( a == NULL ) {
522                         return at->sat_cname.bv_val;
523                 }
524         }
525
526         return( NULL );
527 }
528
529 int oc_check_allowed(
530         AttributeType *at,
531         BerVarray ocl,
532         ObjectClass *sc )
533 {
534         int             i, j;
535
536 #ifdef NEW_LOGGING
537         LDAP_LOG( OPERATION, ENTRY, 
538                 "oc_check_allowed: type \"%s\"\n", at->sat_cname.bv_val, 0, 0 );
539 #else
540         Debug( LDAP_DEBUG_TRACE,
541                 "oc_check_allowed type \"%s\"\n",
542                 at->sat_cname.bv_val, 0, 0 );
543 #endif
544
545         /* always allow objectClass attribute */
546         if ( strcasecmp( at->sat_cname.bv_val, "objectClass" ) == 0 ) {
547                 return LDAP_SUCCESS;
548         }
549
550         /*
551          * All operational attributions are allowed by schema rules.
552          */
553         if( is_at_operational(at) ) {
554                 return LDAP_SUCCESS;
555         }
556
557         /* check to see if its allowed by the structuralObjectClass */
558         if( sc ) {
559                 /* does it require the type? */
560                 for ( j = 0; sc->soc_required != NULL && 
561                         sc->soc_required[j] != NULL; j++ )
562                 {
563                         if( at == sc->soc_required[j] ) {
564                                 return LDAP_SUCCESS;
565                         }
566                 }
567
568                 /* does it allow the type? */
569                 for ( j = 0; sc->soc_allowed != NULL && 
570                         sc->soc_allowed[j] != NULL; j++ )
571                 {
572                         if( at == sc->soc_allowed[j] ) {
573                                 return LDAP_SUCCESS;
574                         }
575                 }
576         }
577
578         /* check that the type appears as req or opt in at least one oc */
579         for ( i = 0; ocl[i].bv_val != NULL; i++ ) {
580                 /* if we know about the oc */
581                 ObjectClass     *oc = oc_bvfind( &ocl[i] );
582                 if ( oc != NULL && oc->soc_kind != LDAP_SCHEMA_ABSTRACT &&
583                         ( sc == NULL || oc->soc_kind == LDAP_SCHEMA_AUXILIARY ))
584                 {
585                         /* does it require the type? */
586                         for ( j = 0; oc->soc_required != NULL && 
587                                 oc->soc_required[j] != NULL; j++ )
588                         {
589                                 if( at == oc->soc_required[j] ) {
590                                         return LDAP_SUCCESS;
591                                 }
592                         }
593                         /* does it allow the type? */
594                         for ( j = 0; oc->soc_allowed != NULL && 
595                                 oc->soc_allowed[j] != NULL; j++ )
596                         {
597                                 if( at == oc->soc_allowed[j] ) {
598                                         return LDAP_SUCCESS;
599                                 }
600                         }
601                 }
602         }
603
604         /* not allowed by any oc */
605         return LDAP_OBJECT_CLASS_VIOLATION;
606 }
607
608 /*
609  * Determine the structural object class from a set of OIDs
610  */
611 int structural_class(
612         BerVarray ocs,
613         struct berval *scbv,
614         ObjectClass **scp,
615         const char **text,
616         char *textbuf, size_t textlen )
617 {
618         int i;
619         ObjectClass *oc;
620         ObjectClass *sc = NULL;
621         int scn = -1;
622
623         *text = "structural_class: internal error";
624         scbv->bv_len = 0;
625
626         for( i=0; ocs[i].bv_val; i++ ) {
627                 oc = oc_bvfind( &ocs[i] );
628
629                 if( oc == NULL ) {
630                         snprintf( textbuf, textlen,
631                                 "unrecognized objectClass '%s'",
632                                 ocs[i].bv_val );
633                         *text = textbuf;
634                         return LDAP_OBJECT_CLASS_VIOLATION;
635                 }
636
637                 if( oc->soc_kind == LDAP_SCHEMA_STRUCTURAL ) {
638                         if( sc == NULL || is_object_subclass( sc, oc ) ) {
639                                 sc = oc;
640                                 scn = i;
641
642                         } else if ( !is_object_subclass( oc, sc ) ) {
643                                 int j;
644                                 ObjectClass *xc = NULL;
645
646                                 /* find common superior */
647                                 for( j=i+1; ocs[j].bv_val; j++ ) {
648                                         xc = oc_bvfind( &ocs[j] );
649
650                                         if( xc == NULL ) {
651                                                 snprintf( textbuf, textlen,
652                                                         "unrecognized objectClass '%s'",
653                                                         ocs[i].bv_val );
654                                                 *text = textbuf;
655                                                 return LDAP_OBJECT_CLASS_VIOLATION;
656                                         }
657
658                                         if( xc->soc_kind != LDAP_SCHEMA_STRUCTURAL ) {
659                                                 xc = NULL;
660                                                 continue;
661                                         }
662
663                                         if( is_object_subclass( sc, xc ) &&
664                                                 is_object_subclass( oc, xc ) )
665                                         {
666                                                 /* found common subclass */
667                                                 break;
668                                         }
669
670                                         xc = NULL;
671                                 }
672
673                                 if( xc == NULL ) {
674                                         /* no common subclass */
675                                         snprintf( textbuf, textlen,
676                                                 "invalid structural object class chain (%s/%s)",
677                                                 ocs[scn].bv_val, ocs[i].bv_val );
678                                         *text = textbuf;
679                                         return LDAP_OBJECT_CLASS_VIOLATION;
680                                 }
681                         }
682                 }
683         }
684
685         if( scp ) {
686                 *scp = sc;
687         }
688
689         if( sc == NULL ) {
690                 *text = "no structural object classes provided";
691                 return LDAP_OBJECT_CLASS_VIOLATION;
692         }
693
694         if( scn < 0 ) {
695                 *text = "invalid structural object class";
696                 return LDAP_OBJECT_CLASS_VIOLATION;
697         }
698
699         *scbv = ocs[scn];
700
701         if( scbv->bv_len == 0 ) {
702                 *text = "invalid structural object class";
703                 return LDAP_OBJECT_CLASS_VIOLATION;
704         }
705
706         return LDAP_SUCCESS;
707 }
708
709 /*
710  * Return structural object class from list of modifications
711  */
712 int mods_structural_class(
713         Modifications *mods,
714         struct berval *sc,
715         const char **text,
716         char *textbuf, size_t textlen )
717 {
718         Modifications *ocmod = NULL;
719
720         for( ; mods != NULL; mods = mods->sml_next ) {
721                 if( mods->sml_desc == slap_schema.si_ad_objectClass ) {
722                         if( ocmod != NULL ) {
723                                 *text = "entry has multiple objectClass attributes";
724                                 return LDAP_OBJECT_CLASS_VIOLATION;
725                         }
726                         ocmod = mods;
727                 }
728         }
729
730         if( ocmod == NULL ) {
731                 *text = "entry has no objectClass attribute";
732                 return LDAP_OBJECT_CLASS_VIOLATION;
733         }
734
735         if( ocmod->sml_bvalues == NULL || ocmod->sml_bvalues[0].bv_val == NULL ) {
736                 *text = "objectClass attribute has no values";
737                 return LDAP_OBJECT_CLASS_VIOLATION;
738         }
739
740         return structural_class( ocmod->sml_bvalues, sc, NULL,
741                 text, textbuf, textlen );
742 }