xref: /netbsd-src/external/bsd/openldap/dist/servers/slapd/schema_check.c (revision 549b59ed3ccf0d36d3097190a0db27b770f3a839)
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