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