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