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