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