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