schema_check.c 19.9 KB
Newer Older
1
2
/* schema_check.c - routines to enforce schema definitions */
/* $OpenLDAP$ */
Kurt Zeilenga's avatar
Kurt Zeilenga committed
3
4
/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
 *
Kurt Zeilenga's avatar
Kurt Zeilenga committed
5
 * Copyright 1998-2008 The OpenLDAP Foundation.
Kurt Zeilenga's avatar
Kurt Zeilenga committed
6
7
8
9
10
11
12
13
14
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted only as authorized by the OpenLDAP
 * Public License.
 *
 * A copy of this license is available in the file LICENSE in the
 * top-level directory of the distribution or, alternatively, at
 * <http://www.OpenLDAP.org/license.html>.
15
16
17
18
19
20
21
22
23
24
25
26
 */

#include "portable.h"

#include <stdio.h>

#include <ac/ctype.h>
#include <ac/string.h>
#include <ac/socket.h>

#include "slap.h"

27
28
29
30
31
static char * oc_check_required(
	Entry *e,
	ObjectClass *oc,
	struct berval *ocname );

Pierangelo Masarati's avatar
Pierangelo Masarati committed
32
33
static int entry_naming_check(
	Entry *e,
34
	int manage,
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
35
	int add_naming,
Pierangelo Masarati's avatar
Pierangelo Masarati committed
36
37
	const char** text,
	char *textbuf, size_t textlen );
38
39
40
41
42
43
44
45
/*
 * entry_schema_check - check that entry e conforms to the schema required
 * by its object class(es).
 *
 * returns 0 if so, non-zero otherwise.
 */

int
46
entry_schema_check( 
47
	Operation *op,
48
49
	Entry *e,
	Attribute *oldattrs,
50
	int manage,
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
51
	int add,
52
	const char** text,
53
	char *textbuf, size_t textlen )
54
{
55
56
	Attribute	*a, *asc = NULL, *aoc = NULL;
	ObjectClass *sc, *oc, **socs = NULL;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
57
58
	AttributeType *at;
	ContentRule *cr;
59
60
61
62
63
	int	rc, i;
	AttributeDescription *ad_structuralObjectClass
		= slap_schema.si_ad_structuralObjectClass;
	AttributeDescription *ad_objectClass
		= slap_schema.si_ad_objectClass;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
64
	int extensible = 0;
65
	int subentry = is_entry_subentry( e );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
66
	int collectiveSubentry = 0;
67

68
69
70
71
72
	if ( SLAP_NO_SCHEMA_CHECK( op->o_bd )) {
		return LDAP_SUCCESS;
	}

	if ( get_no_schema_check( op ) ) {
73
74
75
		return LDAP_SUCCESS;
	}

Kurt Zeilenga's avatar
Kurt Zeilenga committed
76
77
78
	if( subentry ) {
		collectiveSubentry = is_entry_collectiveAttributeSubentry( e );
	}
79

80
81
	*text = textbuf;

82
	/* misc attribute checks */
Kurt Zeilenga's avatar
Kurt Zeilenga committed
83
	for ( a = e->e_attrs; a != NULL; a = a->a_next ) {
84
85
		const char *type = a->a_desc->ad_cname.bv_val;

Kurt Zeilenga's avatar
Kurt Zeilenga committed
86
		/* there should be at least one value */
87
		assert( a->a_vals != NULL );
88
		assert( a->a_vals[0].bv_val != NULL ); 
Kurt Zeilenga's avatar
Kurt Zeilenga committed
89

90
		if( a->a_desc->ad_type->sat_check ) {
91
			rc = (a->a_desc->ad_type->sat_check)(
92
				op->o_bd, e, a, text, textbuf, textlen );
93
94
95
96
97
			if( rc != LDAP_SUCCESS ) {
				return rc;
			}
		}

98
99
100
101
102
		if( a->a_desc == ad_structuralObjectClass )
			asc = a;
		else if ( a->a_desc == ad_objectClass )
			aoc = a;

Kurt Zeilenga's avatar
Kurt Zeilenga committed
103
		if( !collectiveSubentry && is_at_collective( a->a_desc->ad_type ) ) {
104
			snprintf( textbuf, textlen,
105
				"'%s' can only appear in collectiveAttributeSubentry",
106
107
108
109
				type );
			return LDAP_OBJECT_CLASS_VIOLATION;
		}

Kurt Zeilenga's avatar
Kurt Zeilenga committed
110
111
		/* if single value type, check for multiple values */
		if( is_at_single_value( a->a_desc->ad_type ) &&
112
			a->a_vals[1].bv_val != NULL )
Kurt Zeilenga's avatar
Kurt Zeilenga committed
113
114
115
116
117
118
119
120
121
122
123
124
125
		{
			snprintf( textbuf, textlen, 
				"attribute '%s' cannot have multiple values",
				type );

			Debug( LDAP_DEBUG_ANY,
			    "Entry (%s), %s\n",
			    e->e_dn, textbuf, 0 );

			return LDAP_CONSTRAINT_VIOLATION;
		}
	}

126
127
128
129
130
131
132
133
134
135
136
137
138
	/* check the object class attribute */
	if ( aoc == NULL ) {
		Debug( LDAP_DEBUG_ANY, "No objectClass for entry (%s)\n",
		    e->e_dn, 0, 0 );

		*text = "no objectClass attribute";
		return LDAP_OBJECT_CLASS_VIOLATION;
	}

	assert( aoc->a_vals != NULL );
	assert( aoc->a_vals[0].bv_val != NULL );

	/* check the structural object class attribute */
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
139
	if ( asc == NULL && !add ) {
140
141
142
143
144
		Debug( LDAP_DEBUG_ANY,
			"No structuralObjectClass for entry (%s)\n",
		    e->e_dn, 0, 0 );

		*text = "no structuralObjectClass operational attribute";
145
		return LDAP_OTHER;
146
147
	}

148
149
150
151
152
153
	rc = structural_class( aoc->a_vals, &oc, &socs, text, textbuf, textlen,
		op->o_tmpmemctx );
	if( rc != LDAP_SUCCESS ) {
		return rc;
	}

Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
154
	if ( asc == NULL && add ) {
155
156
157
158
159
160
		attr_merge_one( e, ad_structuralObjectClass, &oc->soc_cname, NULL );
		asc = attr_find( e->e_attrs, ad_structuralObjectClass );
		sc = oc;
		goto got_soc;
	}

161
	assert( asc->a_vals != NULL );
162
163
	assert( asc->a_vals[0].bv_val != NULL );
	assert( asc->a_vals[1].bv_val == NULL );
164

165
	sc = oc_bvfind( &asc->a_vals[0] );
166
167
168
	if( sc == NULL ) {
		snprintf( textbuf, textlen, 
			"unrecognized structuralObjectClass '%s'",
Howard Chu's avatar
Howard Chu committed
169
			asc->a_vals[0].bv_val );
170
171
172
173
174

		Debug( LDAP_DEBUG_ANY,
			"entry_check_schema(%s): %s\n",
			e->e_dn, textbuf, 0 );

Howard Chu's avatar
Howard Chu committed
175
		rc = LDAP_OBJECT_CLASS_VIOLATION;
176
		goto done;
177
178
179
180
181
	}

	if( sc->soc_kind != LDAP_SCHEMA_STRUCTURAL ) {
		snprintf( textbuf, textlen, 
			"structuralObjectClass '%s' is not STRUCTURAL",
Howard Chu's avatar
Howard Chu committed
182
			asc->a_vals[0].bv_val );
183
184
185
186
187

		Debug( LDAP_DEBUG_ANY,
			"entry_check_schema(%s): %s\n",
			e->e_dn, textbuf, 0 );

Howard Chu's avatar
Howard Chu committed
188
		rc = LDAP_OTHER;
189
		goto done;
190
191
	}

192
got_soc:
193
	if( !manage && sc->soc_obsolete ) {
194
195
196
197
198
199
200
201
		snprintf( textbuf, textlen, 
			"structuralObjectClass '%s' is OBSOLETE",
			asc->a_vals[0].bv_val );

		Debug( LDAP_DEBUG_ANY,
			"entry_check_schema(%s): %s\n",
			e->e_dn, textbuf, 0 );

Howard Chu's avatar
Howard Chu committed
202
		rc = LDAP_OBJECT_CLASS_VIOLATION;
203
		goto done;
204
205
	}

206
207
	*text = textbuf;

208
209
210
	if ( oc == NULL ) {
		snprintf( textbuf, textlen, 
			"unrecognized objectClass '%s'",
Howard Chu's avatar
Howard Chu committed
211
			aoc->a_vals[0].bv_val );
212
		rc = LDAP_OBJECT_CLASS_VIOLATION;
213
		goto done;
214

215
	} else if ( sc != slap_schema.si_oc_glue && sc != oc ) {
216
		snprintf( textbuf, textlen, 
217
218
			"structural object class modification "
			"from '%s' to '%s' not allowed",
219
220
			asc->a_vals[0].bv_val, oc->soc_cname.bv_val );
		rc = LDAP_NO_OBJECT_CLASS_MODS;
221
		goto done;
222
	} else if ( sc == slap_schema.si_oc_glue ) {
223
224
		sc = oc;
	}
225

Pierangelo Masarati's avatar
Pierangelo Masarati committed
226
	/* naming check */
227
	if ( !is_entry_glue ( e ) ) {
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
228
		rc = entry_naming_check( e, manage, add, text, textbuf, textlen );
Kurt Zeilenga's avatar
cleanup    
Kurt Zeilenga committed
229
		if( rc != LDAP_SUCCESS ) {
230
			goto done;
Kurt Zeilenga's avatar
cleanup    
Kurt Zeilenga committed
231
232
233
234
		}
	} else {
		/* Glue Entry */
	}
235

Kurt Zeilenga's avatar
Kurt Zeilenga committed
236
237
238
239
240
241
242
	/* find the content rule for the structural class */
	cr = cr_find( sc->soc_oid );

	/* the cr must be same as the structural class */
	assert( !cr || !strcmp( cr->scr_oid, sc->soc_oid ) );

	/* check that the entry has required attrs of the content rule */
243
	if( cr ) {
244
		if( !manage && cr->scr_obsolete ) {
245
246
247
248
249
250
251
252
			snprintf( textbuf, textlen, 
				"content rule '%s' is obsolete",
				ldap_contentrule2name( &cr->scr_crule ));

			Debug( LDAP_DEBUG_ANY,
				"Entry (%s): %s\n",
				e->e_dn, textbuf, 0 );

253
			rc = LDAP_OBJECT_CLASS_VIOLATION;
254
			goto done;
255
256
257
		}

		if( cr->scr_required ) for( i=0; cr->scr_required[i]; i++ ) {
Kurt Zeilenga's avatar
Kurt Zeilenga committed
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
			at = cr->scr_required[i];

			for ( a = e->e_attrs; a != NULL; a = a->a_next ) {
				if( a->a_desc->ad_type == at ) {
					break;
				}
			}

			/* not there => schema violation */
			if ( a == NULL ) {
				snprintf( textbuf, textlen, 
					"content rule '%s' requires attribute '%s'",
					ldap_contentrule2name( &cr->scr_crule ),
					at->sat_cname.bv_val );

				Debug( LDAP_DEBUG_ANY,
					"Entry (%s): %s\n",
					e->e_dn, textbuf, 0 );

277
				rc = LDAP_OBJECT_CLASS_VIOLATION;
278
				goto done;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
279
280
281
			}
		}

282
		if( cr->scr_precluded ) for( i=0; cr->scr_precluded[i]; i++ ) {
Kurt Zeilenga's avatar
Kurt Zeilenga committed
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
			at = cr->scr_precluded[i];

			for ( a = e->e_attrs; a != NULL; a = a->a_next ) {
				if( a->a_desc->ad_type == at ) {
					break;
				}
			}

			/* there => schema violation */
			if ( a != NULL ) {
				snprintf( textbuf, textlen, 
					"content rule '%s' precluded attribute '%s'",
					ldap_contentrule2name( &cr->scr_crule ),
					at->sat_cname.bv_val );

				Debug( LDAP_DEBUG_ANY,
					"Entry (%s): %s\n",
					e->e_dn, textbuf, 0 );

302
				rc = LDAP_OBJECT_CLASS_VIOLATION;
303
				goto done;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
304
305
306
307
			}
		}
	}

308
	/* check that the entry has required attrs for each oc */
309
310
	for ( i = 0; socs[i]; i++ ) {
		oc = socs[i];
311
		if ( !manage && oc->soc_obsolete ) {
Kurt Zeilenga's avatar
Kurt Zeilenga committed
312
313
314
315
316
			/* disallow obsolete classes */
			snprintf( textbuf, textlen, 
				"objectClass '%s' is OBSOLETE",
				aoc->a_vals[i].bv_val );

317
			Debug( LDAP_DEBUG_ANY,
318
				"entry_check_schema(%s): %s\n",
319
				e->e_dn, textbuf, 0 );
320

321
			rc = LDAP_OBJECT_CLASS_VIOLATION;
322
			goto done;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
323
		}
324

325
		if ( oc->soc_check ) {
326
			rc = (oc->soc_check)( op->o_bd, e, oc,
Kurt Zeilenga's avatar
Kurt Zeilenga committed
327
328
				text, textbuf, textlen );
			if( rc != LDAP_SUCCESS ) {
329
				goto done;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
330
331
			}
		}
332

Kurt Zeilenga's avatar
Kurt Zeilenga committed
333
		if ( oc->soc_kind == LDAP_SCHEMA_ABSTRACT ) {
334
			/* object class is abstract */
335
336
337
338
339
			if ( oc != slap_schema.si_oc_top &&
				!is_object_subclass( oc, sc ))
			{
				int j;
				ObjectClass *xc = NULL;
340
				for( j=0; socs[j]; j++ ) {
341
					if( i != j ) {
342
						xc = socs[j];
343
344
345
346
347
348
349
350
351
352

						/* since we previous check against the
						 * structural object of this entry, the
						 * abstract class must be a (direct or indirect)
						 * superclass of one of the auxiliary classes of
						 * the entry.
						 */
						if ( xc->soc_kind == LDAP_SCHEMA_AUXILIARY &&
							is_object_subclass( oc, xc ) )
						{
Kurt Zeilenga's avatar
Kurt Zeilenga committed
353
354
							xc = NULL;
							break;
355
356
357
358
						}
					}
				}

Pierangelo Masarati's avatar
Pierangelo Masarati committed
359
360
				if( xc != NULL ) {
					snprintf( textbuf, textlen, "instantiation of "
361
						"abstract objectClass '%s' not allowed",
362
						aoc->a_vals[i].bv_val );
363
364
365
366
367

					Debug( LDAP_DEBUG_ANY,
						"entry_check_schema(%s): %s\n",
						e->e_dn, textbuf, 0 );

368
					rc = LDAP_OBJECT_CLASS_VIOLATION;
369
					goto done;
370
371
372
373
				}
			}

		} else if ( oc->soc_kind != LDAP_SCHEMA_STRUCTURAL || oc == sc ) {
Kurt Zeilenga's avatar
Kurt Zeilenga committed
374
375
376
			char *s;

			if( oc->soc_kind == LDAP_SCHEMA_AUXILIARY ) {
Luke Howard's avatar
Luke Howard committed
377
378
				int k;

Kurt Zeilenga's avatar
Kurt Zeilenga committed
379
				if( cr ) {
380
381
					int j;

Luke Howard's avatar
Luke Howard committed
382
					k = -1;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
383
					if( cr->scr_auxiliaries ) {
384
385
						for( j = 0; cr->scr_auxiliaries[j]; j++ ) {
							if( cr->scr_auxiliaries[j] == oc ) {
Luke Howard's avatar
Luke Howard committed
386
								k = 0;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
387
388
389
390
								break;
							}
						}
					}
Howard Chu's avatar
Howard Chu committed
391
392
393
394
395
396
					if ( k ) {
						snprintf( textbuf, textlen, 
							"class '%s' not allowed by content rule '%s'",
							oc->soc_cname.bv_val,
							ldap_contentrule2name( &cr->scr_crule ) );
					}
397
				} else if ( global_disallows & SLAP_DISALLOW_AUX_WO_CR ) {
Luke Howard's avatar
Luke Howard committed
398
					k = -1;
Howard Chu's avatar
Howard Chu committed
399
400
401
					snprintf( textbuf, textlen, 
						"class '%s' not allowed by any content rule",
						oc->soc_cname.bv_val );
Luke Howard's avatar
Luke Howard committed
402
403
				} else {
					k = 0;	
Kurt Zeilenga's avatar
Kurt Zeilenga committed
404
405
406
407
408
409
410
				}

				if( k == -1 ) {
					Debug( LDAP_DEBUG_ANY,
						"Entry (%s): %s\n",
						e->e_dn, textbuf, 0 );

411
					rc = LDAP_OBJECT_CLASS_VIOLATION;
412
					goto done;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
413
414
				}
			}
415

Kurt Zeilenga's avatar
Kurt Zeilenga committed
416
			s = oc_check_required( e, oc, &aoc->a_vals[i] );
417
			if (s != NULL) {
418
419
				snprintf( textbuf, textlen, 
					"object class '%s' requires attribute '%s'",
420
					aoc->a_vals[i].bv_val, s );
421

422
				Debug( LDAP_DEBUG_ANY,
423
424
					"Entry (%s): %s\n",
					e->e_dn, textbuf, 0 );
425

426
				rc = LDAP_OBJECT_CLASS_VIOLATION;
427
				goto done;
428
			}
Kurt Zeilenga's avatar
Kurt Zeilenga committed
429

430
			if( oc == slap_schema.si_oc_extensibleObject ) {
Kurt Zeilenga's avatar
Kurt Zeilenga committed
431
432
				extensible=1;
			}
433
434
435
		}
	}

Kurt Zeilenga's avatar
Kurt Zeilenga committed
436
	if( extensible ) {
437
		*text = NULL;
438
		rc = LDAP_SUCCESS;
439
		goto done;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
440
441
	}

442
443
	/* check that each attr in the entry is allowed by some oc */
	for ( a = e->e_attrs; a != NULL; a = a->a_next ) {
444
 		rc = LDAP_OBJECT_CLASS_VIOLATION;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
445
446
447
448

		if( cr && cr->scr_required ) {
			for( i=0; cr->scr_required[i]; i++ ) {
				if( cr->scr_required[i] == a->a_desc->ad_type ) {
449
					rc = LDAP_SUCCESS;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
450
451
452
453
454
					break;
				}
			}
		}

455
		if( rc != LDAP_SUCCESS && cr && cr->scr_allowed ) {
Kurt Zeilenga's avatar
Kurt Zeilenga committed
456
457
			for( i=0; cr->scr_allowed[i]; i++ ) {
				if( cr->scr_allowed[i] == a->a_desc->ad_type ) {
458
					rc = LDAP_SUCCESS;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
459
460
461
462
463
					break;
				}
			}
		}

464
		if( rc != LDAP_SUCCESS ) 
Kurt Zeilenga's avatar
Kurt Zeilenga committed
465
		{
466
			rc = oc_check_allowed( a->a_desc->ad_type, socs, sc );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
467
468
		}

469
		if ( rc != LDAP_SUCCESS ) {
470
			char *type = a->a_desc->ad_cname.bv_val;
471
472
473
474
475

			snprintf( textbuf, textlen, 
				"attribute '%s' not allowed",
				type );

476
			Debug( LDAP_DEBUG_ANY,
477
478
			    "Entry (%s), %s\n",
			    e->e_dn, textbuf, 0 );
479

480
			goto done;
481
		}
482
483
	}

484
	*text = NULL;
485
done:
486
487
	slap_sl_free( socs, op->o_tmpmemctx );
	return rc;
488
489
490
}

static char *
491
492
493
494
oc_check_required(
	Entry *e,
	ObjectClass *oc,
	struct berval *ocname )
495
496
497
498
499
500
{
	AttributeType	*at;
	int		i;
	Attribute	*a;

	Debug( LDAP_DEBUG_TRACE,
501
502
		"oc_check_required entry (%s), objectClass \"%s\"\n",
		e->e_dn, ocname->bv_val, 0 );
503

504
505
506

	/* check for empty oc_required */
	if(oc->soc_required == NULL) {
507
		return NULL;
508
509
510
511
512
513
514
	}

	/* for each required attribute */
	for ( i = 0; oc->soc_required[i] != NULL; i++ ) {
		at = oc->soc_required[i];
		/* see if it's in the entry */
		for ( a = e->e_attrs; a != NULL; a = a->a_next ) {
515
			if( a->a_desc->ad_type == at ) {
516
517
518
519
520
				break;
			}
		}
		/* not there => schema violation */
		if ( a == NULL ) {
521
			return at->sat_cname.bv_val;
522
523
524
525
526
527
		}
	}

	return( NULL );
}

528
int oc_check_allowed(
529
	AttributeType *at,
530
	ObjectClass **socs,
531
	ObjectClass *sc )
532
533
{
	int		i, j;
534
535
536

	Debug( LDAP_DEBUG_TRACE,
		"oc_check_allowed type \"%s\"\n",
537
		at->sat_cname.bv_val, 0, 0 );
538

539
	/* always allow objectClass attribute */
540
	if ( strcasecmp( at->sat_cname.bv_val, "objectClass" ) == 0 ) {
541
		return LDAP_SUCCESS;
542
543
	}

Kurt Zeilenga's avatar
Kurt Zeilenga committed
544
545
546
	/*
	 * All operational attributions are allowed by schema rules.
	 */
547
	if( is_at_operational(at) ) {
548
		return LDAP_SUCCESS;
549
	}
550

551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
	/* check to see if its allowed by the structuralObjectClass */
	if( sc ) {
		/* does it require the type? */
		for ( j = 0; sc->soc_required != NULL && 
			sc->soc_required[j] != NULL; j++ )
		{
			if( at == sc->soc_required[j] ) {
				return LDAP_SUCCESS;
			}
		}

		/* does it allow the type? */
		for ( j = 0; sc->soc_allowed != NULL && 
			sc->soc_allowed[j] != NULL; j++ )
		{
			if( at == sc->soc_allowed[j] ) {
				return LDAP_SUCCESS;
			}
		}
	}

572
	/* check that the type appears as req or opt in at least one oc */
573
	for ( i = 0; socs[i]; i++ ) {
574
		/* if we know about the oc */
575
		ObjectClass	*oc = socs[i];
576
577
578
579
		/* extensibleObject allows all */
		if ( oc == slap_schema.si_oc_extensibleObject ) {
			return LDAP_SUCCESS;
		}
580
581
582
		if ( oc != NULL && oc->soc_kind != LDAP_SCHEMA_ABSTRACT &&
			( sc == NULL || oc->soc_kind == LDAP_SCHEMA_AUXILIARY ))
		{
583
584
			/* does it require the type? */
			for ( j = 0; oc->soc_required != NULL && 
585
586
587
				oc->soc_required[j] != NULL; j++ )
			{
				if( at == oc->soc_required[j] ) {
588
					return LDAP_SUCCESS;
589
				}
590
591
592
			}
			/* does it allow the type? */
			for ( j = 0; oc->soc_allowed != NULL && 
593
594
595
				oc->soc_allowed[j] != NULL; j++ )
			{
				if( at == oc->soc_allowed[j] ) {
596
					return LDAP_SUCCESS;
597
				}
598
599
600
601
602
			}
		}
	}

	/* not allowed by any oc */
603
	return LDAP_OBJECT_CLASS_VIOLATION;
604
}
605
606
607
608
609

/*
 * Determine the structural object class from a set of OIDs
 */
int structural_class(
610
	BerVarray ocs,
Howard Chu's avatar
Howard Chu committed
611
	ObjectClass **scp,
612
	ObjectClass ***socsp,
613
	const char **text,
614
615
	char *textbuf, size_t textlen,
	void *ctx )
616
{
617
618
	int i, nocs;
	ObjectClass *oc, **socs;
619
620
621
622
	ObjectClass *sc = NULL;
	int scn = -1;

	*text = "structural_class: internal error";
623
624
625
626
627
628

	/* count them */
	for( i=0; ocs[i].bv_val; i++ ) ;
	nocs = i;
	
	socs = slap_sl_malloc( (nocs+1) * sizeof(ObjectClass *), ctx );
629

630
	for( i=0; ocs[i].bv_val; i++ ) {
631
		socs[i] = oc_bvfind( &ocs[i] );
632

633
		if( socs[i] == NULL ) {
634
			snprintf( textbuf, textlen,
Howard Chu's avatar
Howard Chu committed
635
				"unrecognized objectClass '%s'",
636
				ocs[i].bv_val );
637
			*text = textbuf;
638
			goto fail;
639
		}
640
641
	}
	socs[i] = NULL;
642

643
644
	for( i=0; ocs[i].bv_val; i++ ) {
		oc = socs[i];
645
646
647
648
649
650
651
652
653
654
		if( oc->soc_kind == LDAP_SCHEMA_STRUCTURAL ) {
			if( sc == NULL || is_object_subclass( sc, oc ) ) {
				sc = oc;
				scn = i;

			} else if ( !is_object_subclass( oc, sc ) ) {
				int j;
				ObjectClass *xc = NULL;

				/* find common superior */
655
				for( j=i+1; ocs[j].bv_val; j++ ) {
656
					xc = socs[j];
657
658
659

					if( xc == NULL ) {
						snprintf( textbuf, textlen,
Howard Chu's avatar
Howard Chu committed
660
							"unrecognized objectClass '%s'",
661
							ocs[j].bv_val );
662
						*text = textbuf;
663
						goto fail;
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
					}

					if( xc->soc_kind != LDAP_SCHEMA_STRUCTURAL ) {
						xc = NULL;
						continue;
					}

					if( is_object_subclass( sc, xc ) &&
						is_object_subclass( oc, xc ) )
					{
						/* found common subclass */
						break;
					}

					xc = NULL;
				}

				if( xc == NULL ) {
					/* no common subclass */
					snprintf( textbuf, textlen,
						"invalid structural object class chain (%s/%s)",
685
						ocs[scn].bv_val, ocs[i].bv_val );
686
					*text = textbuf;
687
					goto fail;
688
689
690
691
692
				}
			}
		}
	}

693
	if( scp ) {
Howard Chu's avatar
Howard Chu committed
694
		*scp = sc;
695
	}
Howard Chu's avatar
Howard Chu committed
696

697
	if( sc == NULL ) {
Kurt Zeilenga's avatar
Kurt Zeilenga committed
698
		*text = "no structural object class provided";
699
		goto fail;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
700
701
702
703
	}

	if( scn < 0 ) {
		*text = "invalid structural object class";
704
		goto fail;
705
706
	}

707
708
709
710
	if ( socsp ) {
		*socsp = socs;
	} else {
		slap_sl_free( socs, ctx );
711
	}
712
713
	*text = NULL;

714
	return LDAP_SUCCESS;
715
716
717
718

fail:
	slap_sl_free( socs, ctx );
	return LDAP_OBJECT_CLASS_VIOLATION;
719
720
721
722
723
724
725
726
727
}

/*
 * Return structural object class from list of modifications
 */
int mods_structural_class(
	Modifications *mods,
	struct berval *sc,
	const char **text,
728
	char *textbuf, size_t textlen, void *ctx )
729
730
{
	Modifications *ocmod = NULL;
731
732
	ObjectClass *ssc;
	int rc;
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748

	for( ; mods != NULL; mods = mods->sml_next ) {
		if( mods->sml_desc == slap_schema.si_ad_objectClass ) {
			if( ocmod != NULL ) {
				*text = "entry has multiple objectClass attributes";
				return LDAP_OBJECT_CLASS_VIOLATION;
			}
			ocmod = mods;
		}
	}

	if( ocmod == NULL ) {
		*text = "entry has no objectClass attribute";
		return LDAP_OBJECT_CLASS_VIOLATION;
	}

Kurt Zeilenga's avatar
Kurt Zeilenga committed
749
	if( ocmod->sml_values == NULL || ocmod->sml_values[0].bv_val == NULL ) {
750
751
752
753
		*text = "objectClass attribute has no values";
		return LDAP_OBJECT_CLASS_VIOLATION;
	}

754
755
756
757
758
	rc = structural_class( ocmod->sml_values, &ssc, NULL,
		text, textbuf, textlen, ctx );
	if ( rc == LDAP_SUCCESS )
		*sc = ssc->soc_cname;
	return rc;
759
}
Pierangelo Masarati's avatar
Pierangelo Masarati committed
760
761
762
763
764


static int
entry_naming_check(
	Entry *e,
765
	int manage,
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
766
	int add_naming,
Pierangelo Masarati's avatar
Pierangelo Masarati committed
767
768
769
770
	const char** text,
	char *textbuf, size_t textlen )
{
	/* naming check */
Howard Chu's avatar
Howard Chu committed
771
	LDAPRDN		rdn = NULL;
Pierangelo Masarati's avatar
Pierangelo Masarati committed
772
773
774
775
	const char	*p = NULL;
	ber_len_t	cnt;
	int		rc = LDAP_SUCCESS;

776
777
778
779
	if ( BER_BVISEMPTY( &e->e_name )) {
		return LDAP_SUCCESS;
	}

Pierangelo Masarati's avatar
Pierangelo Masarati committed
780
781
782
783
784
785
786
787
788
789
790
791
	/*
	 * Get attribute type(s) and attribute value(s) of our RDN
	 */
	if ( ldap_bv2rdn( &e->e_name, &rdn, (char **)&p,
		LDAP_DN_FORMAT_LDAP ) )
	{
		*text = "unrecongized attribute type(s) in RDN";
		return LDAP_INVALID_DN_SYNTAX;
	}

	/* Check that each AVA of the RDN is present in the entry */
	/* FIXME: Should also check that each AVA lists a distinct type */
Howard Chu's avatar
Howard Chu committed
792
793
	for ( cnt = 0; rdn[cnt]; cnt++ ) {
		LDAPAVA *ava = rdn[cnt];
Pierangelo Masarati's avatar
Pierangelo Masarati committed
794
795
796
		AttributeDescription *desc = NULL;
		Attribute *attr;
		const char *errtext;
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
797
		int add = 0;
Pierangelo Masarati's avatar
Pierangelo Masarati committed
798

799
800
801
802
803
804
805
		if( ava->la_flags & LDAP_AVA_BINARY ) {
			snprintf( textbuf, textlen, 
				"value of naming attribute '%s' in unsupported BER form",
				ava->la_attr.bv_val );
			rc = LDAP_NAMING_VIOLATION;
		}

Pierangelo Masarati's avatar
Pierangelo Masarati committed
806
807
808
809
810
811
		rc = slap_bv2ad( &ava->la_attr, &desc, &errtext );
		if ( rc != LDAP_SUCCESS ) {
			snprintf( textbuf, textlen, "%s (in RDN)", errtext );
			break;
		}

Kurt Zeilenga's avatar
Kurt Zeilenga committed
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
		if( desc->ad_type->sat_usage ) {
			snprintf( textbuf, textlen, 
				"naming attribute '%s' is operational",
				ava->la_attr.bv_val );
			rc = LDAP_NAMING_VIOLATION;
			break;
		}
 
		if( desc->ad_type->sat_collective ) {
			snprintf( textbuf, textlen, 
				"naming attribute '%s' is collective",
				ava->la_attr.bv_val );
			rc = LDAP_NAMING_VIOLATION;
			break;
		}

828
		if( !manage && desc->ad_type->sat_obsolete ) {
Kurt Zeilenga's avatar
Kurt Zeilenga committed
829
			snprintf( textbuf, textlen, 
Pierangelo Masarati's avatar
Pierangelo Masarati committed
830
				"naming attribute '%s' is obsolete",
Kurt Zeilenga's avatar
Kurt Zeilenga committed
831
832
833
834
835
836
				ava->la_attr.bv_val );
			rc = LDAP_NAMING_VIOLATION;
			break;
		}

		if( !desc->ad_type->sat_equality ) {
Pierangelo Masarati's avatar
Pierangelo Masarati committed
837
			snprintf( textbuf, textlen, 
838
				"naming attribute '%s' has no equality matching rule",
Pierangelo Masarati's avatar
Pierangelo Masarati committed
839
				ava->la_attr.bv_val );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
840
			rc = LDAP_NAMING_VIOLATION;
Pierangelo Masarati's avatar
Pierangelo Masarati committed
841
842
843
			break;
		}

Kurt Zeilenga's avatar
Kurt Zeilenga committed
844
		if( !desc->ad_type->sat_equality->smr_match ) {
Kurt Zeilenga's avatar
Kurt Zeilenga committed
845
			snprintf( textbuf, textlen, 
846
				"naming attribute '%s' has unsupported equality matching rule",
Kurt Zeilenga's avatar
Kurt Zeilenga committed
847
848
				ava->la_attr.bv_val );
			rc = LDAP_NAMING_VIOLATION;
849
			break;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
850
851
		}

852
853
854
		/* find the naming attribute */
		attr = attr_find( e->e_attrs, desc );
		if ( attr == NULL ) {
Pierangelo Masarati's avatar
Pierangelo Masarati committed
855
			snprintf( textbuf, textlen, 
856
				"naming attribute '%s' is not present in entry",
Pierangelo Masarati's avatar
Pierangelo Masarati committed
857
				ava->la_attr.bv_val );
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
858
859
			if ( add_naming ) {
				add = 1;
860

Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
861
862
863
			} else {
				rc = LDAP_NAMING_VIOLATION;
			}
864

Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
		} else {
			rc = attr_valfind( attr, SLAP_MR_VALUE_OF_ASSERTION_SYNTAX|
				SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH,
				&ava->la_value, NULL, NULL );

			if( rc != 0 ) {
				switch( rc ) {
				case LDAP_INAPPROPRIATE_MATCHING:
					snprintf( textbuf, textlen, 
						"inappropriate matching for naming attribute '%s'",
						ava->la_attr.bv_val );
					break;
				case LDAP_INVALID_SYNTAX:
					snprintf( textbuf, textlen, 
						"value of naming attribute '%s' is invalid",
						ava->la_attr.bv_val );
					break;
				case LDAP_NO_SUCH_ATTRIBUTE:
					if ( add_naming ) {
						add = 1;
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
885
886
887
888
889
						rc = LDAP_SUCCESS;
					} else {
						snprintf( textbuf, textlen, 
							"value of naming attribute '%s' is not present in entry",
							ava->la_attr.bv_val );
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
890
891
892
893
894
895
896
					}
					break;
				default:
					snprintf( textbuf, textlen, 
						"naming attribute '%s' is inappropriate",
						ava->la_attr.bv_val );
				}
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
897
898
899
900

				if ( !add ) {
					rc = LDAP_NAMING_VIOLATION;
				}
901
			}
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
902
903
904
905
906
907
		}

		if ( add ) {
			attr_merge_normalize_one( e, desc, &ava->la_value, NULL );

		} else if ( rc != LDAP_SUCCESS ) {
908
909
			break;
		}
Pierangelo Masarati's avatar
Pierangelo Masarati committed
910
911
912
913
914
915
	}

	ldap_rdnfree( rdn );
	return rc;
}