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