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