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