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