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