]> git.sur5r.net Git - openldap/blob - servers/slapd/schema_check.c
d8ae713294edae9bfab29359f1b02eabc1cafe28
[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  * Determine the structural object class from a set of OIDs
26  */
27 static int structural_class(
28         struct berval **ocs,
29         struct berval *scbv,
30         const char **text )
31 {
32         int i;
33         ObjectClass *oc;
34         ObjectClass *sc = NULL;
35         int scn = 0;
36
37         *text = "structural object error";
38         scbv->bv_len = 0;
39
40         for( i=0; ocs[i]; i++ ) {
41                 oc = oc_find( ocs[i]->bv_val );
42
43                 if( oc == NULL ) {
44                         *text = "unrecongized objectClass attribute";
45                         return LDAP_OBJECT_CLASS_VIOLATION;
46                 }
47
48                 if( oc->soc_kind == LDAP_SCHEMA_STRUCTURAL ) {
49                         if( sc == NULL || is_object_subclass( sc, oc ) ) {
50                                 sc = oc;
51                                 scn = i;
52
53                         } else if ( !is_object_subclass( oc, sc ) ) {
54                                 /* FIXME: multiple inheritance possible! */
55                                 *text = "invalid strucutural object class chain";
56                                 return LDAP_OBJECT_CLASS_VIOLATION;
57                         }
58                 }
59         }
60
61         if( sc == NULL ) {
62                 *text = "no strucutural object classes";
63                 return LDAP_OBJECT_CLASS_VIOLATION;
64         }
65
66         *scbv = *ocs[scn];
67         return LDAP_SUCCESS;
68 }
69
70 /*
71  * Return structural object class from list of modifications
72  */
73 int mods_structural_class(
74         Modifications *mods,
75         struct berval *sc,
76         const char **text )
77 {
78         Modifications *ocmod = NULL;
79
80         for( ; mods != NULL; mods = mods->sml_next ) {
81                 if( mods->sml_desc == slap_schema.si_ad_objectClass ) {
82                         if( ocmod != NULL ) {
83                                 *text = "entry has multiple objectClass attributes";
84                                 return LDAP_OBJECT_CLASS_VIOLATION;
85                         }
86                         ocmod = mods;
87                 }
88         }
89
90         if( ocmod == NULL ) {
91                 *text = "entry has no objectClass attribute";
92                 return LDAP_OBJECT_CLASS_VIOLATION;
93         }
94
95         if( ocmod->sml_bvalues == NULL || ocmod->sml_bvalues[0] == NULL ) {
96                 *text = "objectClass attribute has no values";
97                 return LDAP_OBJECT_CLASS_VIOLATION;
98         }
99
100         return structural_class( ocmod->sml_bvalues, sc, text );
101 }
102
103 /*
104  * entry_schema_check - check that entry e conforms to the schema required
105  * by its object class(es).
106  *
107  * returns 0 if so, non-zero otherwise.
108  */
109
110 int
111 entry_schema_check( 
112         Entry *e, Attribute *oldattrs,
113         const char** text,
114         char *textbuf, size_t textlen )
115 {
116         Attribute       *a, *asc, *aoc;
117         ObjectClass *sc, *oc;
118         int     rc, i;
119         struct berval nsc;
120         AttributeDescription *ad_structuralObjectClass
121                 = slap_schema.si_ad_structuralObjectClass;
122         AttributeDescription *ad_objectClass
123                 = slap_schema.si_ad_objectClass;
124         int extensible = 0;
125
126         *text = textbuf;
127
128         /* check single-valued attrs for multiple values */
129         for ( a = e->e_attrs; a != NULL; a = a->a_next ) {
130                 /* there should be at least one value */
131                 assert( a->a_vals );
132                 assert( a->a_vals[0] != NULL ); 
133
134                 /* if single value type, check for multiple values */
135                 if( is_at_single_value( a->a_desc->ad_type ) &&
136                         a->a_vals[1] != NULL )
137                 {
138                         char *type = a->a_desc->ad_cname.bv_val;
139
140                         snprintf( textbuf, textlen, 
141                                 "attribute '%s' cannot have multiple values",
142                                 type );
143
144 #ifdef NEW_LOGGING
145                         LDAP_LOG(( "schema", LDAP_LEVEL_INFO,
146                                 "entry_schema_check: dn=\"%s\" %s\n",
147                                 e->e_dn, textbuf ));
148 #else
149                         Debug( LDAP_DEBUG_ANY,
150                             "Entry (%s), %s\n",
151                             e->e_dn, textbuf, 0 );
152 #endif
153
154                         return LDAP_CONSTRAINT_VIOLATION;
155                 }
156         }
157
158         if( !global_schemacheck ) return LDAP_SUCCESS;
159
160 #if 1
161         /* find the object class attribute - could error out here */
162         asc = attr_find( e->e_attrs, ad_structuralObjectClass );
163         if ( asc == NULL ) {
164 #ifdef NEW_LOGGING
165                 LDAP_LOG(( "schema", LDAP_LEVEL_INFO, "entry_schema_check: "
166                         "No structuralObjectClass for entry (%s)\n",
167                         e->e_dn ));
168 #else
169                 Debug( LDAP_DEBUG_ANY,
170                         "No structuralObjectClass for entry (%s)\n",
171                     e->e_dn, 0, 0 );
172 #endif
173
174                 *text = "no structuralObjectClass operational attribute";
175                 return LDAP_OBJECT_CLASS_VIOLATION;
176         }
177
178         assert( asc->a_vals != NULL );
179         assert( asc->a_vals[0] != NULL );
180         assert( asc->a_vals[1] == NULL );
181
182         sc = oc_find( asc->a_vals[0]->bv_val );
183         if( sc == NULL ) {
184                 snprintf( textbuf, textlen, 
185                         "unrecognized structuralObjectClass '%s'",
186                         aoc->a_vals[0]->bv_val );
187
188 #ifdef NEW_LOGGING
189                 LDAP_LOG(( "schema", LDAP_LEVEL_INFO,
190                         "entry_schema_check: dn (%s), %s\n",
191                         e->e_dn, textbuf ));
192 #else
193                 Debug( LDAP_DEBUG_ANY,
194                         "entry_check_schema(%s): %s\n",
195                         e->e_dn, textbuf, 0 );
196 #endif
197
198                 return LDAP_OBJECT_CLASS_VIOLATION;
199         }
200
201         if( sc->soc_kind != LDAP_SCHEMA_STRUCTURAL ) {
202                 snprintf( textbuf, textlen, 
203                         "structuralObjectClass '%s' is not STRUCTURAL",
204                         aoc->a_vals[0]->bv_val );
205
206 #ifdef NEW_LOGGING
207                 LDAP_LOG(( "schema", LDAP_LEVEL_INFO,
208                         "entry_schema_check: dn (%s), %s\n",
209                         e->e_dn, textbuf ));
210 #else
211                 Debug( LDAP_DEBUG_ANY,
212                         "entry_check_schema(%s): %s\n",
213                         e->e_dn, textbuf, 0 );
214 #endif
215
216                 return LDAP_OBJECT_CLASS_VIOLATION;
217         }
218 #endif
219
220         /* find the object class attribute */
221         aoc = attr_find( e->e_attrs, ad_objectClass );
222         if ( aoc == NULL ) {
223 #ifdef NEW_LOGGING
224                 LDAP_LOG(( "schema", LDAP_LEVEL_INFO,
225                         "entry_schema_check: No objectClass for entry (%s).\n", e->e_dn ));
226 #else
227                 Debug( LDAP_DEBUG_ANY, "No objectClass for entry (%s)\n",
228                     e->e_dn, 0, 0 );
229 #endif
230
231                 *text = "no objectClass attribute";
232                 return LDAP_OBJECT_CLASS_VIOLATION;
233         }
234
235         assert( aoc->a_vals != NULL );
236         assert( aoc->a_vals[0] != NULL );
237
238         rc = structural_class( aoc->a_vals, &nsc, text );
239         if( rc != LDAP_SUCCESS ) {
240                 return rc;
241         } else if ( nsc.bv_len == 0 ) {
242                 return LDAP_OBJECT_CLASS_VIOLATION;
243         }
244
245         oc = oc_find( nsc.bv_val );
246         if ( oc == NULL ) {
247                 snprintf( textbuf, textlen, 
248                         "unrecognized objectClass '%s'",
249                         aoc->a_vals[i]->bv_val );
250                 return LDAP_OBJECT_CLASS_VIOLATION;
251
252         } else if ( sc != oc ) {
253                 snprintf( textbuf, textlen, 
254                         "structuralObjectClass modification from '%s' to '%s' not allowed",
255                         asc->a_vals[0]->bv_val, nsc.bv_val );
256                 return LDAP_NO_OBJECT_CLASS_MODS;
257         }
258
259         /* check that the entry has required attrs for each oc */
260         for ( i = 0; aoc->a_vals[i] != NULL; i++ ) {
261                 if ( (oc = oc_find( aoc->a_vals[i]->bv_val )) == NULL ) {
262                         snprintf( textbuf, textlen, 
263                                 "unrecognized objectClass '%s'",
264                                 aoc->a_vals[i]->bv_val );
265
266 #ifdef NEW_LOGGING
267                         LDAP_LOG(( "schema", LDAP_LEVEL_INFO,
268                                 "entry_schema_check: dn (%s), %s\n",
269                                 e->e_dn, textbuf ));
270 #else
271                         Debug( LDAP_DEBUG_ANY,
272                                 "entry_check_schema(%s): \"%s\" not recognized\n",
273                                 e->e_dn, textbuf, 0 );
274 #endif
275
276                         return LDAP_OBJECT_CLASS_VIOLATION;
277
278                 } else if ( oc->soc_kind == LDAP_SCHEMA_ABSTRACT ) {
279                         /* object class is abstract */
280                         /* FIXME: need to check that is is a superclass of something */
281
282                 } else if ( oc->soc_kind == LDAP_SCHEMA_STRUCTURAL && oc != sc ) {
283                         /* object class is a superclass of the structural class */
284                         /* nothing in particular to check */
285
286                 } else {
287                         char *s = oc_check_required( e, oc, aoc->a_vals[i] );
288
289                         if (s != NULL) {
290                                 snprintf( textbuf, textlen, 
291                                         "object class '%s' requires attribute '%s'",
292                                         aoc->a_vals[i]->bv_val, s );
293
294 #ifdef NEW_LOGGING
295                                 LDAP_LOG(( "schema", LDAP_LEVEL_INFO,
296                                         "entry_schema_check: dn=\"%s\" %s",
297                                         e->e_dn, textbuf ));
298 #else
299                                 Debug( LDAP_DEBUG_ANY,
300                                         "Entry (%s): %s\n",
301                                         e->e_dn, textbuf, 0 );
302 #endif
303
304                                 return LDAP_OBJECT_CLASS_VIOLATION;
305                         }
306
307                         if( oc == slap_schema.si_oc_extensibleObject ) {
308                                 extensible=1;
309                         }
310                 }
311         }
312
313         if( extensible ) {
314                 return LDAP_SUCCESS;
315         }
316
317         /* check that each attr in the entry is allowed by some oc */
318         for ( a = e->e_attrs; a != NULL; a = a->a_next ) {
319                 int ret = oc_check_allowed( a->a_desc->ad_type, aoc->a_vals );
320                 if ( ret != LDAP_SUCCESS ) {
321                         char *type = a->a_desc->ad_cname.bv_val;
322
323                         snprintf( textbuf, textlen, 
324                                 "attribute '%s' not allowed",
325                                 type );
326
327 #ifdef NEW_LOGGING
328                         LDAP_LOG(( "schema", LDAP_LEVEL_INFO,
329                                 "entry_schema_check: dn=\"%s\" %s\n",
330                                 e->e_dn, textbuf ));
331 #else
332                         Debug( LDAP_DEBUG_ANY,
333                             "Entry (%s), %s\n",
334                             e->e_dn, textbuf, 0 );
335 #endif
336
337                         return ret;
338                 }
339         }
340
341         return LDAP_SUCCESS;
342 }
343
344 static char *
345 oc_check_required(
346         Entry *e,
347         ObjectClass *oc,
348         struct berval *ocname )
349 {
350         AttributeType   *at;
351         int             i;
352         Attribute       *a;
353
354 #ifdef NEW_LOGGING
355         LDAP_LOG(( "schema", LDAP_LEVEL_ENTRY,
356                 "oc_check_required: dn (%s), objectClass \"%s\"\n",
357         e->e_dn, ocname->bv_val ));
358 #else
359         Debug( LDAP_DEBUG_TRACE,
360                 "oc_check_required entry (%s), objectClass \"%s\"\n",
361                 e->e_dn, ocname->bv_val, 0 );
362 #endif
363
364
365         /* check for empty oc_required */
366         if(oc->soc_required == NULL) {
367                 return NULL;
368         }
369
370         /* for each required attribute */
371         for ( i = 0; oc->soc_required[i] != NULL; i++ ) {
372                 at = oc->soc_required[i];
373                 /* see if it's in the entry */
374                 for ( a = e->e_attrs; a != NULL; a = a->a_next ) {
375                         if( a->a_desc->ad_type == at ) {
376                                 break;
377                         }
378                 }
379                 /* not there => schema violation */
380                 if ( a == NULL ) {
381                         return at->sat_cname.bv_val;
382                 }
383         }
384
385         return( NULL );
386 }
387
388 int oc_check_allowed(
389         AttributeType *at,
390         struct berval **ocl )
391 {
392         ObjectClass     *oc;
393         int             i, j;
394
395 #ifdef NEW_LOGGING
396         LDAP_LOG(( "schema", LDAP_LEVEL_ENTRY,
397                 "oc_check_allowed: type \"%s\"\n", at->sat_cname.bv_val ));
398 #else
399         Debug( LDAP_DEBUG_TRACE,
400                 "oc_check_allowed type \"%s\"\n",
401                 at->sat_cname.bv_val, 0, 0 );
402 #endif
403
404         /* always allow objectClass attribute */
405         if ( strcasecmp( at->sat_cname.bv_val, "objectClass" ) == 0 ) {
406                 return LDAP_SUCCESS;
407         }
408
409         /*
410          * All operational attributions are allowed by schema rules.
411          */
412         if( is_at_operational(at) ) {
413                 return LDAP_SUCCESS;
414         }
415
416         /* check that the type appears as req or opt in at least one oc */
417         for ( i = 0; ocl[i] != NULL; i++ ) {
418                 /* if we know about the oc */
419                 if ( (oc = oc_find( ocl[i]->bv_val )) != NULL ) {
420                         /* does it require the type? */
421                         for ( j = 0; oc->soc_required != NULL && 
422                                 oc->soc_required[j] != NULL; j++ )
423                         {
424                                 if( at == oc->soc_required[j] ) {
425                                         return LDAP_SUCCESS;
426                                 }
427                         }
428                         /* does it allow the type? */
429                         for ( j = 0; oc->soc_allowed != NULL && 
430                                 oc->soc_allowed[j] != NULL; j++ )
431                         {
432                                 if( at == oc->soc_allowed[j] ) {
433                                         return LDAP_SUCCESS;
434                                 }
435                         }
436                         /* maybe the next oc allows it */
437                 }
438         }
439
440         /* not allowed by any oc */
441         return LDAP_OBJECT_CLASS_VIOLATION;
442 }