1/*	$NetBSD$	*/
2
3/* schema_check.c - routines to enforce schema definitions */
4/* OpenLDAP: pkg/ldap/servers/slapd/schema_check.c,v 1.103.2.12 2010/04/13 20:23:19 kurt Exp */
5/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
6 *
7 * Copyright 1998-2010 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
29static char * oc_check_required(
30	Entry *e,
31	ObjectClass *oc,
32	struct berval *ocname );
33
34static 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
47int
48entry_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
195got_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;
501done:
502	slap_sl_free( socs, op->o_tmpmemctx );
503	return rc;
504}
505
506static char *
507oc_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
544int 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 */
625int 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
732fail:
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 */
740int 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
778static int
779entry_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