schema_check.c 20.5 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/>.
 *
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
5
 * Copyright 1998-2019 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,
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
52
	Attribute **socp,
53
	const char** text,
54
	char *textbuf, size_t textlen )
55
{
56
57
	Attribute	*a, *asc = NULL, *aoc = NULL;
	ObjectClass *sc, *oc, **socs = NULL;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
58
59
	AttributeType *at;
	ContentRule *cr;
60
61
62
63
64
	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
65
	int extensible = 0;
66
	int subentry = is_entry_subentry( e );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
67
	int collectiveSubentry = 0;
68

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

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

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

81
82
	*text = textbuf;

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

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

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

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

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

Kurt Zeilenga's avatar
Kurt Zeilenga committed
111
112
		/* if single value type, check for multiple values */
		if( is_at_single_value( a->a_desc->ad_type ) &&
113
			a->a_vals[1].bv_val != NULL )
Kurt Zeilenga's avatar
Kurt Zeilenga committed
114
115
116
117
118
119
120
121
122
123
124
125
126
		{
			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;
		}
	}

127
128
129
130
131
132
133
134
135
136
137
138
139
	/* 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
140
	if ( asc == NULL && !add ) {
141
142
143
144
145
		Debug( LDAP_DEBUG_ANY,
			"No structuralObjectClass for entry (%s)\n",
		    e->e_dn, 0, 0 );

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

149
150
151
152
153
154
	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
155
	if ( asc == NULL && add ) {
156
157
158
159
160
161
		attr_merge_one( e, ad_structuralObjectClass, &oc->soc_cname, NULL );
		asc = attr_find( e->e_attrs, ad_structuralObjectClass );
		sc = oc;
		goto got_soc;
	}

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

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

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

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

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

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

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

193
got_soc:
194
	if( !manage && sc->soc_obsolete ) {
195
196
197
198
199
200
201
202
		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
203
		rc = LDAP_OBJECT_CLASS_VIOLATION;
204
		goto done;
205
206
	}

207
208
	*text = textbuf;

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

Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
	} else if ( sc != oc ) {
		if ( !manage && sc != slap_schema.si_oc_glue ) {
			snprintf( textbuf, textlen, 
				"structural object class modification "
				"from '%s' to '%s' not allowed",
				asc->a_vals[0].bv_val, oc->soc_cname.bv_val );
			rc = LDAP_NO_OBJECT_CLASS_MODS;
			goto done;
		}

		assert( asc->a_vals != NULL );
		assert( !BER_BVISNULL( &asc->a_vals[0] ) );
		assert( BER_BVISNULL( &asc->a_vals[1] ) );
		assert( asc->a_nvals == asc->a_vals );

		/* draft-zeilenga-ldap-relax: automatically modify
		 * structuralObjectClass if changed with relax */
233
		sc = oc;
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
234
235
236
237
		ber_bvreplace( &asc->a_vals[ 0 ], &sc->soc_cname );
		if ( socp ) {
			*socp = asc;
		}
238
	}
239

Pierangelo Masarati's avatar
Pierangelo Masarati committed
240
	/* naming check */
241
	if ( !is_entry_glue ( e ) ) {
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
242
		rc = entry_naming_check( e, manage, add, text, textbuf, textlen );
Kurt Zeilenga's avatar
cleanup    
Kurt Zeilenga committed
243
		if( rc != LDAP_SUCCESS ) {
244
			goto done;
Kurt Zeilenga's avatar
cleanup    
Kurt Zeilenga committed
245
246
247
248
		}
	} else {
		/* Glue Entry */
	}
249

Kurt Zeilenga's avatar
Kurt Zeilenga committed
250
251
252
253
254
255
256
	/* 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 */
257
	if( cr ) {
258
		if( !manage && cr->scr_obsolete ) {
259
260
261
262
263
264
265
266
			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 );

267
			rc = LDAP_OBJECT_CLASS_VIOLATION;
268
			goto done;
269
270
271
		}

		if( cr->scr_required ) for( i=0; cr->scr_required[i]; i++ ) {
Kurt Zeilenga's avatar
Kurt Zeilenga committed
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
			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 );

291
				rc = LDAP_OBJECT_CLASS_VIOLATION;
292
				goto done;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
293
294
295
			}
		}

296
		if( cr->scr_precluded ) for( i=0; cr->scr_precluded[i]; i++ ) {
Kurt Zeilenga's avatar
Kurt Zeilenga committed
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
			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 );

316
				rc = LDAP_OBJECT_CLASS_VIOLATION;
317
				goto done;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
318
319
320
321
			}
		}
	}

322
	/* check that the entry has required attrs for each oc */
323
324
	for ( i = 0; socs[i]; i++ ) {
		oc = socs[i];
325
		if ( !manage && oc->soc_obsolete ) {
Kurt Zeilenga's avatar
Kurt Zeilenga committed
326
327
328
329
330
			/* disallow obsolete classes */
			snprintf( textbuf, textlen, 
				"objectClass '%s' is OBSOLETE",
				aoc->a_vals[i].bv_val );

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

335
			rc = LDAP_OBJECT_CLASS_VIOLATION;
336
			goto done;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
337
		}
338

339
		if ( oc->soc_check ) {
340
			rc = (oc->soc_check)( op->o_bd, e, oc,
Kurt Zeilenga's avatar
Kurt Zeilenga committed
341
342
				text, textbuf, textlen );
			if( rc != LDAP_SUCCESS ) {
343
				goto done;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
344
345
			}
		}
346

Kurt Zeilenga's avatar
Kurt Zeilenga committed
347
		if ( oc->soc_kind == LDAP_SCHEMA_ABSTRACT ) {
348
			/* object class is abstract */
349
350
351
352
353
			if ( oc != slap_schema.si_oc_top &&
				!is_object_subclass( oc, sc ))
			{
				int j;
				ObjectClass *xc = NULL;
354
				for( j=0; socs[j]; j++ ) {
355
					if( i != j ) {
356
						xc = socs[j];
357
358
359
360
361
362
363
364
365
366

						/* 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
367
368
							xc = NULL;
							break;
369
370
371
372
						}
					}
				}

Pierangelo Masarati's avatar
Pierangelo Masarati committed
373
374
				if( xc != NULL ) {
					snprintf( textbuf, textlen, "instantiation of "
375
						"abstract objectClass '%s' not allowed",
376
						aoc->a_vals[i].bv_val );
377
378
379
380
381

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

382
					rc = LDAP_OBJECT_CLASS_VIOLATION;
383
					goto done;
384
385
386
387
				}
			}

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

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

Kurt Zeilenga's avatar
Kurt Zeilenga committed
393
				if( cr ) {
394
395
					int j;

Luke Howard's avatar
Luke Howard committed
396
					k = -1;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
397
					if( cr->scr_auxiliaries ) {
398
399
						for( j = 0; cr->scr_auxiliaries[j]; j++ ) {
							if( cr->scr_auxiliaries[j] == oc ) {
Luke Howard's avatar
Luke Howard committed
400
								k = 0;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
401
402
403
404
								break;
							}
						}
					}
Howard Chu's avatar
Howard Chu committed
405
406
407
408
409
410
					if ( k ) {
						snprintf( textbuf, textlen, 
							"class '%s' not allowed by content rule '%s'",
							oc->soc_cname.bv_val,
							ldap_contentrule2name( &cr->scr_crule ) );
					}
411
				} else if ( global_disallows & SLAP_DISALLOW_AUX_WO_CR ) {
Luke Howard's avatar
Luke Howard committed
412
					k = -1;
Howard Chu's avatar
Howard Chu committed
413
414
415
					snprintf( textbuf, textlen, 
						"class '%s' not allowed by any content rule",
						oc->soc_cname.bv_val );
Luke Howard's avatar
Luke Howard committed
416
417
				} else {
					k = 0;	
Kurt Zeilenga's avatar
Kurt Zeilenga committed
418
419
420
421
422
423
424
				}

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

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

Kurt Zeilenga's avatar
Kurt Zeilenga committed
430
			s = oc_check_required( e, oc, &aoc->a_vals[i] );
431
			if (s != NULL) {
432
433
				snprintf( textbuf, textlen, 
					"object class '%s' requires attribute '%s'",
434
					aoc->a_vals[i].bv_val, s );
435

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

440
				rc = LDAP_OBJECT_CLASS_VIOLATION;
441
				goto done;
442
			}
Kurt Zeilenga's avatar
Kurt Zeilenga committed
443

444
			if( oc == slap_schema.si_oc_extensibleObject ) {
Kurt Zeilenga's avatar
Kurt Zeilenga committed
445
446
				extensible=1;
			}
447
448
449
		}
	}

Kurt Zeilenga's avatar
Kurt Zeilenga committed
450
	if( extensible ) {
451
		*text = NULL;
452
		rc = LDAP_SUCCESS;
453
		goto done;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
454
455
	}

456
457
	/* check that each attr in the entry is allowed by some oc */
	for ( a = e->e_attrs; a != NULL; a = a->a_next ) {
458
 		rc = LDAP_OBJECT_CLASS_VIOLATION;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
459
460
461
462

		if( cr && cr->scr_required ) {
			for( i=0; cr->scr_required[i]; i++ ) {
				if( cr->scr_required[i] == a->a_desc->ad_type ) {
463
					rc = LDAP_SUCCESS;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
464
465
466
467
468
					break;
				}
			}
		}

469
		if( rc != LDAP_SUCCESS && cr && cr->scr_allowed ) {
Kurt Zeilenga's avatar
Kurt Zeilenga committed
470
471
			for( i=0; cr->scr_allowed[i]; i++ ) {
				if( cr->scr_allowed[i] == a->a_desc->ad_type ) {
472
					rc = LDAP_SUCCESS;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
473
474
475
476
477
					break;
				}
			}
		}

478
		if( rc != LDAP_SUCCESS ) 
Kurt Zeilenga's avatar
Kurt Zeilenga committed
479
		{
480
			rc = oc_check_allowed( a->a_desc->ad_type, socs, sc );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
481
482
		}

483
		if ( rc != LDAP_SUCCESS ) {
484
			char *type = a->a_desc->ad_cname.bv_val;
485
486
487
488
489

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

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

494
			goto done;
495
		}
496
497
	}

498
	*text = NULL;
499
done:
500
501
	slap_sl_free( socs, op->o_tmpmemctx );
	return rc;
502
503
504
}

static char *
505
506
507
508
oc_check_required(
	Entry *e,
	ObjectClass *oc,
	struct berval *ocname )
509
510
511
512
513
514
{
	AttributeType	*at;
	int		i;
	Attribute	*a;

	Debug( LDAP_DEBUG_TRACE,
515
516
		"oc_check_required entry (%s), objectClass \"%s\"\n",
		e->e_dn, ocname->bv_val, 0 );
517

518
519
520

	/* check for empty oc_required */
	if(oc->soc_required == NULL) {
521
		return NULL;
522
523
524
525
526
527
528
	}

	/* 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 ) {
529
			if( a->a_desc->ad_type == at ) {
530
531
532
533
534
				break;
			}
		}
		/* not there => schema violation */
		if ( a == NULL ) {
535
			return at->sat_cname.bv_val;
536
537
538
539
540
541
		}
	}

	return( NULL );
}

542
int oc_check_allowed(
543
	AttributeType *at,
544
	ObjectClass **socs,
545
	ObjectClass *sc )
546
547
{
	int		i, j;
548
549
550

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

553
	/* always allow objectClass attribute */
554
	if ( strcasecmp( at->sat_cname.bv_val, "objectClass" ) == 0 ) {
555
		return LDAP_SUCCESS;
556
557
	}

Kurt Zeilenga's avatar
Kurt Zeilenga committed
558
559
560
	/*
	 * All operational attributions are allowed by schema rules.
	 */
561
	if( is_at_operational(at) ) {
562
		return LDAP_SUCCESS;
563
	}
564

565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
	/* 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;
			}
		}
	}

586
	/* check that the type appears as req or opt in at least one oc */
587
	for ( i = 0; socs[i]; i++ ) {
588
		/* if we know about the oc */
589
		ObjectClass	*oc = socs[i];
590
591
592
593
		/* extensibleObject allows all */
		if ( oc == slap_schema.si_oc_extensibleObject ) {
			return LDAP_SUCCESS;
		}
594
595
596
		if ( oc != NULL && oc->soc_kind != LDAP_SCHEMA_ABSTRACT &&
			( sc == NULL || oc->soc_kind == LDAP_SCHEMA_AUXILIARY ))
		{
597
598
			/* does it require the type? */
			for ( j = 0; oc->soc_required != NULL && 
599
600
601
				oc->soc_required[j] != NULL; j++ )
			{
				if( at == oc->soc_required[j] ) {
602
					return LDAP_SUCCESS;
603
				}
604
605
606
			}
			/* does it allow the type? */
			for ( j = 0; oc->soc_allowed != NULL && 
607
608
609
				oc->soc_allowed[j] != NULL; j++ )
			{
				if( at == oc->soc_allowed[j] ) {
610
					return LDAP_SUCCESS;
611
				}
612
613
614
615
616
			}
		}
	}

	/* not allowed by any oc */
617
	return LDAP_OBJECT_CLASS_VIOLATION;
618
}
619
620
621
622
623

/*
 * Determine the structural object class from a set of OIDs
 */
int structural_class(
624
	BerVarray ocs,
Howard Chu's avatar
Howard Chu committed
625
	ObjectClass **scp,
626
	ObjectClass ***socsp,
627
	const char **text,
628
629
	char *textbuf, size_t textlen,
	void *ctx )
630
{
631
632
	int i, nocs;
	ObjectClass *oc, **socs;
633
634
635
636
	ObjectClass *sc = NULL;
	int scn = -1;

	*text = "structural_class: internal error";
637
638
639
640
641
642

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

644
	for( i=0; ocs[i].bv_val; i++ ) {
645
		socs[i] = oc_bvfind( &ocs[i] );
646

647
		if( socs[i] == NULL ) {
648
			snprintf( textbuf, textlen,
Howard Chu's avatar
Howard Chu committed
649
				"unrecognized objectClass '%s'",
650
				ocs[i].bv_val );
651
			*text = textbuf;
652
			goto fail;
653
		}
654
655
	}
	socs[i] = NULL;
656

657
658
	for( i=0; ocs[i].bv_val; i++ ) {
		oc = socs[i];
659
660
661
662
663
664
665
666
667
668
		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 */
669
				for( j=i+1; ocs[j].bv_val; j++ ) {
670
					xc = socs[j];
671
672
673

					if( xc == NULL ) {
						snprintf( textbuf, textlen,
Howard Chu's avatar
Howard Chu committed
674
							"unrecognized objectClass '%s'",
675
							ocs[j].bv_val );
676
						*text = textbuf;
677
						goto fail;
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
					}

					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)",
699
						ocs[scn].bv_val, ocs[i].bv_val );
700
					*text = textbuf;
701
					goto fail;
702
703
704
705
706
				}
			}
		}
	}

707
	if( scp ) {
Howard Chu's avatar
Howard Chu committed
708
		*scp = sc;
709
	}
Howard Chu's avatar
Howard Chu committed
710

711
	if( sc == NULL ) {
Kurt Zeilenga's avatar
Kurt Zeilenga committed
712
		*text = "no structural object class provided";
713
		goto fail;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
714
715
716
717
	}

	if( scn < 0 ) {
		*text = "invalid structural object class";
718
		goto fail;
719
720
	}

721
722
723
724
	if ( socsp ) {
		*socsp = socs;
	} else {
		slap_sl_free( socs, ctx );
725
	}
726
727
	*text = NULL;

728
	return LDAP_SUCCESS;
729
730
731
732

fail:
	slap_sl_free( socs, ctx );
	return LDAP_OBJECT_CLASS_VIOLATION;
733
734
735
736
737
738
739
740
741
}

/*
 * Return structural object class from list of modifications
 */
int mods_structural_class(
	Modifications *mods,
	struct berval *sc,
	const char **text,
742
	char *textbuf, size_t textlen, void *ctx )
743
744
{
	Modifications *ocmod = NULL;
745
746
	ObjectClass *ssc;
	int rc;
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762

	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
763
	if( ocmod->sml_values == NULL || ocmod->sml_values[0].bv_val == NULL ) {
764
765
766
767
		*text = "objectClass attribute has no values";
		return LDAP_OBJECT_CLASS_VIOLATION;
	}

768
769
770
771
772
	rc = structural_class( ocmod->sml_values, &ssc, NULL,
		text, textbuf, textlen, ctx );
	if ( rc == LDAP_SUCCESS )
		*sc = ssc->soc_cname;
	return rc;
773
}
Pierangelo Masarati's avatar
Pierangelo Masarati committed
774
775
776
777
778


static int
entry_naming_check(
	Entry *e,
779
	int manage,
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
780
	int add_naming,
Pierangelo Masarati's avatar
Pierangelo Masarati committed
781
782
783
784
	const char** text,
	char *textbuf, size_t textlen )
{
	/* naming check */
Howard Chu's avatar
Howard Chu committed
785
	LDAPRDN		rdn = NULL;
Pierangelo Masarati's avatar
Pierangelo Masarati committed
786
787
788
789
	const char	*p = NULL;
	ber_len_t	cnt;
	int		rc = LDAP_SUCCESS;

790
791
792
793
	if ( BER_BVISEMPTY( &e->e_name )) {
		return LDAP_SUCCESS;
	}

Pierangelo Masarati's avatar
Pierangelo Masarati committed
794
795
796
797
798
799
	/*
	 * Get attribute type(s) and attribute value(s) of our RDN
	 */
	if ( ldap_bv2rdn( &e->e_name, &rdn, (char **)&p,
		LDAP_DN_FORMAT_LDAP ) )
	{
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
800
		*text = "unrecognized attribute type(s) in RDN";
Pierangelo Masarati's avatar
Pierangelo Masarati committed
801
802
803
804
805
		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
806
807
	for ( cnt = 0; rdn[cnt]; cnt++ ) {
		LDAPAVA *ava = rdn[cnt];
Pierangelo Masarati's avatar
Pierangelo Masarati committed
808
809
810
		AttributeDescription *desc = NULL;
		Attribute *attr;
		const char *errtext;
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
811
		int add = 0;
Pierangelo Masarati's avatar
Pierangelo Masarati committed
812

813
814
815
816
817
		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;
Howard Chu's avatar
Howard Chu committed
818
			break;
819
820
		}

Pierangelo Masarati's avatar
Pierangelo Masarati committed
821
822
823
824
825
826
		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
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
		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;
		}

843
		if( !manage && desc->ad_type->sat_obsolete ) {
Kurt Zeilenga's avatar
Kurt Zeilenga committed
844
			snprintf( textbuf, textlen, 
Pierangelo Masarati's avatar
Pierangelo Masarati committed
845
				"naming attribute '%s' is obsolete",
Kurt Zeilenga's avatar
Kurt Zeilenga committed
846
847
848
849
850
851
				ava->la_attr.bv_val );
			rc = LDAP_NAMING_VIOLATION;
			break;
		}

		if( !desc->ad_type->sat_equality ) {
Pierangelo Masarati's avatar
Pierangelo Masarati committed
852
			snprintf( textbuf, textlen, 
853
				"naming attribute '%s' has no equality matching rule",
Pierangelo Masarati's avatar
Pierangelo Masarati committed
854
				ava->la_attr.bv_val );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
855
			rc = LDAP_NAMING_VIOLATION;
Pierangelo Masarati's avatar
Pierangelo Masarati committed
856
857
858
			break;
		}

Kurt Zeilenga's avatar
Kurt Zeilenga committed
859
		if( !desc->ad_type->sat_equality->smr_match ) {
Kurt Zeilenga's avatar
Kurt Zeilenga committed
860
			snprintf( textbuf, textlen, 
861
				"naming attribute '%s' has unsupported equality matching rule",
Kurt Zeilenga's avatar
Kurt Zeilenga committed
862
863
				ava->la_attr.bv_val );
			rc = LDAP_NAMING_VIOLATION;
864
			break;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
865
866
		}

867
868
869
		/* find the naming attribute */
		attr = attr_find( e->e_attrs, desc );
		if ( attr == NULL ) {
Pierangelo Masarati's avatar
Pierangelo Masarati committed
870
			snprintf( textbuf, textlen, 
871
				"naming attribute '%s' is not present in entry",
Pierangelo Masarati's avatar
Pierangelo Masarati committed
872
				ava->la_attr.bv_val );
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
873
874
			if ( add_naming ) {
				add = 1;
875

Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
876
877
878
			} else {
				rc = LDAP_NAMING_VIOLATION;
			}
879

Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
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 );

Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
885
			if ( rc != 0 ) {
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
886
887
888
889
890
891
892
893
894
895
896
897
898
				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 ) {
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
899
900
901
902
903
904
905
906
907
908
						if ( is_at_single_value( desc->ad_type ) ) {
							snprintf( textbuf, textlen, 
								"value of single-valued naming attribute '%s' conflicts with value present in entry",
								ava->la_attr.bv_val );

						} else {
							add = 1;
							rc = LDAP_SUCCESS;
						}

Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
909
910
911
912
					} 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
913
914
915
916
917
918
919
					}
					break;
				default:
					snprintf( textbuf, textlen, 
						"naming attribute '%s' is inappropriate",
						ava->la_attr.bv_val );
				}
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
920
921
922
923

				if ( !add ) {
					rc = LDAP_NAMING_VIOLATION;
				}
924
			}
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
925
926
927
928
929
930
		}

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

		} else if ( rc != LDAP_SUCCESS ) {
931
932
			break;
		}
Pierangelo Masarati's avatar
Pierangelo Masarati committed
933
934
935
936
937
938
	}

	ldap_rdnfree( rdn );
	return rc;
}