]> git.sur5r.net Git - openldap/blob - servers/slapd/schema_check.c
Remove unused int i, j
[openldap] / servers / slapd / schema_check.c
1 /* schema_check.c - routines to enforce schema definitions */
2 /* $OpenLDAP$ */
3 /*
4  * Copyright 1998-2000 The OpenLDAP Foundation, All Rights Reserved.
5  * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
6  */
7
8 #include "portable.h"
9
10 #include <stdio.h>
11
12 #include <ac/ctype.h>
13 #include <ac/string.h>
14 #include <ac/socket.h>
15
16 #include "slap.h"
17 #include "ldap_pvt.h"
18
19 static char * oc_check_required(
20         Entry *e,
21         ObjectClass *oc,
22         struct berval *ocname );
23
24 /*
25  * entry_schema_check - check that entry e conforms to the schema required
26  * by its object class(es).
27  *
28  * returns 0 if so, non-zero otherwise.
29  */
30
31 int
32 entry_schema_check( 
33         Entry *e, Attribute *oldattrs,
34         const char** text,
35         char *textbuf, size_t textlen )
36 {
37         Attribute       *a, *asc, *aoc;
38         ObjectClass *sc, *oc;
39         int     rc, i;
40         struct berval nsc;
41         AttributeDescription *ad_structuralObjectClass
42                 = slap_schema.si_ad_structuralObjectClass;
43         AttributeDescription *ad_objectClass
44                 = slap_schema.si_ad_objectClass;
45         int extensible = 0;
46
47         *text = textbuf;
48
49         /* check single-valued attrs for multiple values */
50         for ( a = e->e_attrs; a != NULL; a = a->a_next ) {
51                 /* there should be at least one value */
52                 assert( a->a_vals );
53                 assert( a->a_vals[0].bv_val != NULL ); 
54
55                 /* if single value type, check for multiple values */
56                 if( is_at_single_value( a->a_desc->ad_type ) &&
57                         a->a_vals[1].bv_val != NULL )
58                 {
59                         char *type = a->a_desc->ad_cname.bv_val;
60
61                         snprintf( textbuf, textlen, 
62                                 "attribute '%s' cannot have multiple values",
63                                 type );
64
65 #ifdef NEW_LOGGING
66                         LDAP_LOG(( "schema", LDAP_LEVEL_INFO,
67                                 "entry_schema_check: dn=\"%s\" %s\n",
68                                 e->e_dn, textbuf ));
69 #else
70                         Debug( LDAP_DEBUG_ANY,
71                             "Entry (%s), %s\n",
72                             e->e_dn, textbuf, 0 );
73 #endif
74
75                         return LDAP_CONSTRAINT_VIOLATION;
76                 }
77         }
78
79         /* it's a REALLY bad idea to disable schema checks */
80         if( !global_schemacheck ) return LDAP_SUCCESS;
81
82         /* find the object class attribute - could error out here */
83         asc = attr_find( e->e_attrs, ad_structuralObjectClass );
84         if ( asc == NULL ) {
85 #ifdef NEW_LOGGING
86                 LDAP_LOG(( "schema", LDAP_LEVEL_INFO, "entry_schema_check: "
87                         "No structuralObjectClass for entry (%s)\n",
88                         e->e_dn ));
89 #else
90                 Debug( LDAP_DEBUG_ANY,
91                         "No structuralObjectClass for entry (%s)\n",
92                     e->e_dn, 0, 0 );
93 #endif
94
95                 *text = "no structuralObjectClass operational attribute";
96                 return LDAP_OBJECT_CLASS_VIOLATION;
97         }
98
99         assert( asc->a_vals != NULL );
100         assert( asc->a_vals[0].bv_val != NULL );
101         assert( asc->a_vals[1].bv_val == NULL );
102
103         sc = oc_bvfind( &asc->a_vals[0] );
104         if( sc == NULL ) {
105                 snprintf( textbuf, textlen, 
106                         "unrecognized structuralObjectClass '%s'",
107                         aoc->a_vals[0].bv_val );
108
109 #ifdef NEW_LOGGING
110                 LDAP_LOG(( "schema", LDAP_LEVEL_INFO,
111                         "entry_schema_check: dn (%s), %s\n",
112                         e->e_dn, textbuf ));
113 #else
114                 Debug( LDAP_DEBUG_ANY,
115                         "entry_check_schema(%s): %s\n",
116                         e->e_dn, textbuf, 0 );
117 #endif
118
119                 return LDAP_OBJECT_CLASS_VIOLATION;
120         }
121
122         if( sc->soc_kind != LDAP_SCHEMA_STRUCTURAL ) {
123                 snprintf( textbuf, textlen, 
124                         "structuralObjectClass '%s' is not STRUCTURAL",
125                         aoc->a_vals[0].bv_val );
126
127 #ifdef NEW_LOGGING
128                 LDAP_LOG(( "schema", LDAP_LEVEL_INFO,
129                         "entry_schema_check: dn (%s), %s\n",
130                         e->e_dn, textbuf ));
131 #else
132                 Debug( LDAP_DEBUG_ANY,
133                         "entry_check_schema(%s): %s\n",
134                         e->e_dn, textbuf, 0 );
135 #endif
136
137                 return LDAP_OBJECT_CLASS_VIOLATION;
138         }
139
140         /* find the object class attribute */
141         aoc = attr_find( e->e_attrs, ad_objectClass );
142         if ( aoc == NULL ) {
143 #ifdef NEW_LOGGING
144                 LDAP_LOG(( "schema", LDAP_LEVEL_INFO,
145                         "entry_schema_check: No objectClass for entry (%s).\n"
146                         e->e_dn ));
147 #else
148                 Debug( LDAP_DEBUG_ANY, "No objectClass for entry (%s)\n",
149                     e->e_dn, 0, 0 );
150 #endif
151
152                 *text = "no objectClass attribute";
153                 return LDAP_OBJECT_CLASS_VIOLATION;
154         }
155
156         assert( aoc->a_vals != NULL );
157         assert( aoc->a_vals[0].bv_val != NULL );
158
159         rc = structural_class( aoc->a_vals, &nsc, text, textbuf, textlen );
160         if( rc != LDAP_SUCCESS ) {
161                 return rc;
162         } else if ( nsc.bv_len == 0 ) {
163                 return LDAP_OBJECT_CLASS_VIOLATION;
164         }
165
166         *text = textbuf;
167
168         oc = oc_bvfind( &nsc );
169         if ( oc == NULL ) {
170                 snprintf( textbuf, textlen, 
171                         "unrecognized objectClass '%s'",
172                         aoc->a_vals[i].bv_val );
173                 return LDAP_OBJECT_CLASS_VIOLATION;
174
175         } else if ( sc != oc ) {
176                 snprintf( textbuf, textlen, 
177                         "structuralObjectClass modification from '%s' to '%s' not allowed",
178                         asc->a_vals[0].bv_val, nsc.bv_val );
179                 return LDAP_NO_OBJECT_CLASS_MODS;
180         }
181
182         /* check that the entry has required attrs for each oc */
183         for ( i = 0; aoc->a_vals[i].bv_val != NULL; i++ ) {
184                 if ( (oc = oc_bvfind( &aoc->a_vals[i] )) == NULL ) {
185                         snprintf( textbuf, textlen, 
186                                 "unrecognized objectClass '%s'",
187                                 aoc->a_vals[i].bv_val );
188
189 #ifdef NEW_LOGGING
190                         LDAP_LOG(( "schema", LDAP_LEVEL_INFO,
191                                 "entry_schema_check: dn (%s), %s\n",
192                                 e->e_dn, textbuf ));
193 #else
194                         Debug( LDAP_DEBUG_ANY,
195                                 "entry_check_schema(%s): %s\n",
196                                 e->e_dn, textbuf, 0 );
197 #endif
198
199                         return LDAP_OBJECT_CLASS_VIOLATION;
200
201                 } else if ( oc->soc_kind == LDAP_SCHEMA_ABSTRACT ) {
202                         /* object class is abstract */
203                         if ( oc != slap_schema.si_oc_top &&
204                                 !is_object_subclass( oc, sc ))
205                         {
206                                 int j;
207                                 ObjectClass *xc = NULL;
208                                 for( j=0; aoc->a_vals[j].bv_val; j++ ) {
209                                         if( i != j ) {
210                                                 xc = oc_bvfind( &aoc->a_vals[i] );
211                                                 if( xc == NULL ) {
212                                                         snprintf( textbuf, textlen, 
213                                                                 "unrecognized objectClass '%s'",
214                                                                 aoc->a_vals[i].bv_val );
215
216 #ifdef NEW_LOGGING
217                                                         LDAP_LOG(( "schema", LDAP_LEVEL_INFO,
218                                                                 "entry_schema_check: dn (%s), %s\n",
219                                                                 e->e_dn, textbuf ));
220 #else
221                                                         Debug( LDAP_DEBUG_ANY,
222                                                                 "entry_check_schema(%s): %s\n",
223                                                                 e->e_dn, textbuf, 0 );
224 #endif
225
226                                                         return LDAP_OBJECT_CLASS_VIOLATION;
227                                                 }
228
229                                                 /* since we previous check against the
230                                                  * structural object of this entry, the
231                                                  * abstract class must be a (direct or indirect)
232                                                  * superclass of one of the auxiliary classes of
233                                                  * the entry.
234                                                  */
235                                                 if ( xc->soc_kind == LDAP_SCHEMA_AUXILIARY &&
236                                                         is_object_subclass( oc, xc ) )
237                                                 {
238                                                         break;;
239                                                 }
240
241                                                 xc = NULL;
242                                         }
243                                 }
244
245                                 if( xc == NULL ) {
246                                         snprintf( textbuf, textlen, "instanstantiation of "
247                                                 "abstract objectClass '%s' not allowed",
248                                                 aoc->a_vals[i].bv_val );
249
250 #ifdef NEW_LOGGING
251                                         LDAP_LOG(( "schema", LDAP_LEVEL_INFO,
252                                                 "entry_schema_check: dn (%s), %s\n",
253                                                 e->e_dn, textbuf ));
254 #else
255                                         Debug( LDAP_DEBUG_ANY,
256                                                 "entry_check_schema(%s): %s\n",
257                                                 e->e_dn, textbuf, 0 );
258 #endif
259
260                                         return LDAP_OBJECT_CLASS_VIOLATION;
261                                 }
262                         }
263
264                 } else if ( oc->soc_kind != LDAP_SCHEMA_STRUCTURAL || oc == sc ) {
265                         char *s = oc_check_required( e, oc, &aoc->a_vals[i] );
266
267                         if (s != NULL) {
268                                 snprintf( textbuf, textlen, 
269                                         "object class '%s' requires attribute '%s'",
270                                         aoc->a_vals[i].bv_val, s );
271
272 #ifdef NEW_LOGGING
273                                 LDAP_LOG(( "schema", LDAP_LEVEL_INFO,
274                                         "entry_schema_check: dn=\"%s\" %s",
275                                         e->e_dn, textbuf ));
276 #else
277                                 Debug( LDAP_DEBUG_ANY,
278                                         "Entry (%s): %s\n",
279                                         e->e_dn, textbuf, 0 );
280 #endif
281
282                                 return LDAP_OBJECT_CLASS_VIOLATION;
283                         }
284
285                         if( oc == slap_schema.si_oc_extensibleObject ) {
286                                 extensible=1;
287                         }
288                 }
289         }
290
291         if( extensible ) {
292                 return LDAP_SUCCESS;
293         }
294
295         /* check that each attr in the entry is allowed by some oc */
296         for ( a = e->e_attrs; a != NULL; a = a->a_next ) {
297                 int ret = oc_check_allowed( a->a_desc->ad_type, aoc->a_vals, sc );
298                 if ( ret != LDAP_SUCCESS ) {
299                         char *type = a->a_desc->ad_cname.bv_val;
300
301                         snprintf( textbuf, textlen, 
302                                 "attribute '%s' not allowed",
303                                 type );
304
305 #ifdef NEW_LOGGING
306                         LDAP_LOG(( "schema", LDAP_LEVEL_INFO,
307                                 "entry_schema_check: dn=\"%s\" %s\n",
308                                 e->e_dn, textbuf ));
309 #else
310                         Debug( LDAP_DEBUG_ANY,
311                             "Entry (%s), %s\n",
312                             e->e_dn, textbuf, 0 );
313 #endif
314
315                         return ret;
316                 }
317         }
318
319         return LDAP_SUCCESS;
320 }
321
322 static char *
323 oc_check_required(
324         Entry *e,
325         ObjectClass *oc,
326         struct berval *ocname )
327 {
328         AttributeType   *at;
329         int             i;
330         Attribute       *a;
331
332 #ifdef NEW_LOGGING
333         LDAP_LOG(( "schema", LDAP_LEVEL_ENTRY,
334                 "oc_check_required: dn (%s), objectClass \"%s\"\n",
335         e->e_dn, ocname->bv_val ));
336 #else
337         Debug( LDAP_DEBUG_TRACE,
338                 "oc_check_required entry (%s), objectClass \"%s\"\n",
339                 e->e_dn, ocname->bv_val, 0 );
340 #endif
341
342
343         /* check for empty oc_required */
344         if(oc->soc_required == NULL) {
345                 return NULL;
346         }
347
348         /* for each required attribute */
349         for ( i = 0; oc->soc_required[i] != NULL; i++ ) {
350                 at = oc->soc_required[i];
351                 /* see if it's in the entry */
352                 for ( a = e->e_attrs; a != NULL; a = a->a_next ) {
353                         if( a->a_desc->ad_type == at ) {
354                                 break;
355                         }
356                 }
357                 /* not there => schema violation */
358                 if ( a == NULL ) {
359                         return at->sat_cname.bv_val;
360                 }
361         }
362
363         return( NULL );
364 }
365
366 int oc_check_allowed(
367         AttributeType *at,
368         BVarray ocl,
369         ObjectClass *sc )
370 {
371         int             i, j;
372
373 #ifdef NEW_LOGGING
374         LDAP_LOG(( "schema", LDAP_LEVEL_ENTRY,
375                 "oc_check_allowed: type \"%s\"\n", at->sat_cname.bv_val ));
376 #else
377         Debug( LDAP_DEBUG_TRACE,
378                 "oc_check_allowed type \"%s\"\n",
379                 at->sat_cname.bv_val, 0, 0 );
380 #endif
381
382         /* always allow objectClass attribute */
383         if ( strcasecmp( at->sat_cname.bv_val, "objectClass" ) == 0 ) {
384                 return LDAP_SUCCESS;
385         }
386
387         /*
388          * All operational attributions are allowed by schema rules.
389          */
390         if( is_at_operational(at) ) {
391                 return LDAP_SUCCESS;
392         }
393
394         /* check to see if its allowed by the structuralObjectClass */
395         if( sc ) {
396                 /* does it require the type? */
397                 for ( j = 0; sc->soc_required != NULL && 
398                         sc->soc_required[j] != NULL; j++ )
399                 {
400                         if( at == sc->soc_required[j] ) {
401                                 return LDAP_SUCCESS;
402                         }
403                 }
404
405                 /* does it allow the type? */
406                 for ( j = 0; sc->soc_allowed != NULL && 
407                         sc->soc_allowed[j] != NULL; j++ )
408                 {
409                         if( at == sc->soc_allowed[j] ) {
410                                 return LDAP_SUCCESS;
411                         }
412                 }
413         }
414
415         /* check that the type appears as req or opt in at least one oc */
416         for ( i = 0; ocl[i].bv_val != NULL; i++ ) {
417                 /* if we know about the oc */
418                 ObjectClass     *oc = oc_bvfind( &ocl[i] );
419                 if ( oc != NULL && oc->soc_kind != LDAP_SCHEMA_ABSTRACT &&
420                         ( sc == NULL || oc->soc_kind == LDAP_SCHEMA_AUXILIARY ))
421                 {
422                         /* does it require the type? */
423                         for ( j = 0; oc->soc_required != NULL && 
424                                 oc->soc_required[j] != NULL; j++ )
425                         {
426                                 if( at == oc->soc_required[j] ) {
427                                         return LDAP_SUCCESS;
428                                 }
429                         }
430                         /* does it allow the type? */
431                         for ( j = 0; oc->soc_allowed != NULL && 
432                                 oc->soc_allowed[j] != NULL; j++ )
433                         {
434                                 if( at == oc->soc_allowed[j] ) {
435                                         return LDAP_SUCCESS;
436                                 }
437                         }
438                 }
439         }
440
441         /* not allowed by any oc */
442         return LDAP_OBJECT_CLASS_VIOLATION;
443 }
444
445 /*
446  * Determine the structural object class from a set of OIDs
447  */
448 int structural_class(
449         BVarray ocs,
450         struct berval *scbv,
451         const char **text,
452         char *textbuf, size_t textlen )
453 {
454         int i;
455         ObjectClass *oc;
456         ObjectClass *sc = NULL;
457         int scn = -1;
458
459         *text = "structural_class: internal error";
460         scbv->bv_len = 0;
461
462         for( i=0; ocs[i].bv_val; i++ ) {
463                 oc = oc_bvfind( &ocs[i] );
464
465                 if( oc == NULL ) {
466                         snprintf( textbuf, textlen,
467                                 "unrecongized objectClass '%s'",
468                                 ocs[i].bv_val );
469                         *text = textbuf;
470                         return LDAP_OBJECT_CLASS_VIOLATION;
471                 }
472
473                 if( oc->soc_kind == LDAP_SCHEMA_STRUCTURAL ) {
474                         if( sc == NULL || is_object_subclass( sc, oc ) ) {
475                                 sc = oc;
476                                 scn = i;
477
478                         } else if ( !is_object_subclass( oc, sc ) ) {
479                                 int j;
480                                 ObjectClass *xc = NULL;
481
482                                 /* find common superior */
483                                 for( j=i+1; ocs[j].bv_val; j++ ) {
484                                         xc = oc_bvfind( &ocs[j] );
485
486                                         if( xc == NULL ) {
487                                                 snprintf( textbuf, textlen,
488                                                         "unrecongized objectClass '%s'",
489                                                         ocs[i].bv_val );
490                                                 *text = textbuf;
491                                                 return LDAP_OBJECT_CLASS_VIOLATION;
492                                         }
493
494                                         if( xc->soc_kind != LDAP_SCHEMA_STRUCTURAL ) {
495                                                 xc = NULL;
496                                                 continue;
497                                         }
498
499                                         if( is_object_subclass( sc, xc ) &&
500                                                 is_object_subclass( oc, xc ) )
501                                         {
502                                                 /* found common subclass */
503                                                 break;
504                                         }
505
506                                         xc = NULL;
507                                 }
508
509                                 if( xc == NULL ) {
510                                         /* no common subclass */
511                                         snprintf( textbuf, textlen,
512                                                 "invalid structural object class chain (%s/%s)",
513                                                 ocs[scn].bv_val, ocs[i].bv_val );
514                                         *text = textbuf;
515                                         return LDAP_OBJECT_CLASS_VIOLATION;
516                                 }
517                         }
518                 }
519         }
520
521         if( sc == NULL ) {
522                 *text = "no structural object classes provided";
523                 return LDAP_OBJECT_CLASS_VIOLATION;
524         }
525
526         *scbv = ocs[scn];
527         return LDAP_SUCCESS;
528 }
529
530 /*
531  * Return structural object class from list of modifications
532  */
533 int mods_structural_class(
534         Modifications *mods,
535         struct berval *sc,
536         const char **text,
537         char *textbuf, size_t textlen )
538 {
539         Modifications *ocmod = NULL;
540
541         for( ; mods != NULL; mods = mods->sml_next ) {
542                 if( mods->sml_desc == slap_schema.si_ad_objectClass ) {
543                         if( ocmod != NULL ) {
544                                 *text = "entry has multiple objectClass attributes";
545                                 return LDAP_OBJECT_CLASS_VIOLATION;
546                         }
547                         ocmod = mods;
548                 }
549         }
550
551         if( ocmod == NULL ) {
552                 *text = "entry has no objectClass attribute";
553                 return LDAP_OBJECT_CLASS_VIOLATION;
554         }
555
556         if( ocmod->sml_bvalues == NULL || ocmod->sml_bvalues[0].bv_val == NULL ) {
557                 *text = "objectClass attribute has no values";
558                 return LDAP_OBJECT_CLASS_VIOLATION;
559         }
560
561         return structural_class( ocmod->sml_bvalues, sc,
562                 text, textbuf, textlen );
563 }