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