]> git.sur5r.net Git - openldap/blob - servers/slapd/schema_check.c
BDB_INDEX code does no harm (but no good yet, not used by filters yet).
[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(Entry *e, struct berval *ocname);
20
21 /*
22  * entry_schema_check - check that entry e conforms to the schema required
23  * by its object class(es).
24  *
25  * returns 0 if so, non-zero otherwise.
26  */
27
28 int
29 entry_schema_check( 
30         Entry *e, Attribute *oldattrs, const char** text,
31         char *textbuf, size_t textlen )
32 {
33         Attribute       *a, *aoc;
34         ObjectClass *oc;
35         int             i;
36         AttributeDescription *ad_objectClass = slap_schema.si_ad_objectClass;
37         int extensible = 0;
38
39         *text = textbuf;
40
41         /* check single-valued attrs for multiple values */
42         for ( a = e->e_attrs; a != NULL; a = a->a_next ) {
43                 /* there should be at least one value */
44                 assert( a->a_vals );
45                 assert( a->a_vals[0] != NULL ); 
46
47                 /* if single value type, check for multiple values */
48                 if( is_at_single_value( a->a_desc->ad_type ) &&
49                         a->a_vals[1] != NULL )
50                 {
51                         char *type = a->a_desc->ad_cname->bv_val;
52
53                         snprintf( textbuf, textlen, 
54                                 "attribute '%s' cannot have multiple values",
55                                 type );
56
57 #ifdef NEW_LOGGING
58                         LDAP_LOG(( "schema", LDAP_LEVEL_INFO,
59                                 "entry_schema_check: dn=\"%s\" %s\n",
60                                 e->e_dn, textbuf ));
61 #else
62                         Debug( LDAP_DEBUG_ANY,
63                             "Entry (%s), %s\n",
64                             e->e_dn, textbuf, 0 );
65 #endif
66
67                         return LDAP_CONSTRAINT_VIOLATION;
68                 }
69         }
70
71         if( !global_schemacheck ) return LDAP_SUCCESS;
72
73         /* find the object class attribute - could error out here */
74         if ( (aoc = attr_find( e->e_attrs, ad_objectClass )) == NULL ) {
75 #ifdef NEW_LOGGING
76                 LDAP_LOG(( "schema", LDAP_LEVEL_INFO,
77                            "entry_schema_check: No objectClass for entry (%s).\n", e->e_dn ));
78 #else
79                 Debug( LDAP_DEBUG_ANY, "No objectClass for entry (%s)\n",
80                     e->e_dn, 0, 0 );
81 #endif
82
83                 *text = "no objectClass attribute";
84                 return LDAP_OBJECT_CLASS_VIOLATION;
85         }
86
87         /* check that the entry has required attrs for each oc */
88         for ( i = 0; aoc->a_vals[i] != NULL; i++ ) {
89                 if ( (oc = oc_find( aoc->a_vals[i]->bv_val )) == NULL ) {
90                         snprintf( textbuf, textlen, 
91                                 "unrecognized objectClass '%s'",
92                                 aoc->a_vals[i]->bv_val );
93
94 #ifdef NEW_LOGGING
95                         LDAP_LOG(( "schema", LDAP_LEVEL_INFO,
96                                 "entry_schema_check: dn (%s), %s\n",
97                                 e->e_dn, textbuf ));
98 #else
99                         Debug( LDAP_DEBUG_ANY,
100                                 "entry_check_schema(%s): \"%s\" not recognized\n",
101                                 e->e_dn, textbuf, 0 );
102 #endif
103
104                         return LDAP_OBJECT_CLASS_VIOLATION;
105
106                 } else {
107                         char *s = oc_check_required( e, aoc->a_vals[i] );
108
109                         if (s != NULL) {
110                                 snprintf( textbuf, textlen, 
111                                         "object class '%s' requires attribute '%s'",
112                                         aoc->a_vals[i]->bv_val, s );
113
114 #ifdef NEW_LOGGING
115                                 LDAP_LOG(( "schema", LDAP_LEVEL_INFO,
116                                         "entry_schema_check: dn=\"%s\" %s",
117                                         e->e_dn, textbuf ));
118 #else
119                                 Debug( LDAP_DEBUG_ANY,
120                                         "Entry (%s): %s\n",
121                                         e->e_dn, textbuf, 0 );
122 #endif
123
124                                 return LDAP_OBJECT_CLASS_VIOLATION;
125                         }
126
127                         if( oc == slap_schema.si_oc_extensibleObject ) {
128                                 extensible=1;
129                         }
130
131                 }
132         }
133
134         if( extensible ) {
135                 return LDAP_SUCCESS;
136         }
137
138         /* check that each attr in the entry is allowed by some oc */
139         for ( a = e->e_attrs; a != NULL; a = a->a_next ) {
140                 int ret = oc_check_allowed( a->a_desc->ad_type, aoc->a_vals );
141                 if ( ret != LDAP_SUCCESS ) {
142                         char *type = a->a_desc->ad_cname->bv_val;
143
144                         snprintf( textbuf, textlen, 
145                                 "attribute '%s' not allowed",
146                                 type );
147
148 #ifdef NEW_LOGGING
149                         LDAP_LOG(( "schema", LDAP_LEVEL_INFO,
150                                 "entry_schema_check: dn=\"%s\" %s\n",
151                                 e->e_dn, textbuf ));
152 #else
153                         Debug( LDAP_DEBUG_ANY,
154                             "Entry (%s), %s\n",
155                             e->e_dn, textbuf, 0 );
156 #endif
157
158                         return ret;
159                 }
160         }
161
162         return LDAP_SUCCESS;
163 }
164
165 static char *
166 oc_check_required( Entry *e, struct berval *ocname )
167 {
168         ObjectClass     *oc;
169         AttributeType   *at;
170         int             i;
171         Attribute       *a;
172
173 #ifdef NEW_LOGGING
174         LDAP_LOG(( "schema", LDAP_LEVEL_ENTRY,
175                    "oc_check_required: dn (%s), objectClass \"%s\"\n",
176                    e->e_dn, ocname->bv_val ));
177 #else
178         Debug( LDAP_DEBUG_TRACE,
179                "oc_check_required entry (%s), objectClass \"%s\"\n",
180                e->e_dn, ocname->bv_val, 0 );
181 #endif
182
183
184         /* find global oc defn. it we don't know about it assume it's ok */
185         if ( (oc = oc_find( ocname->bv_val )) == NULL ) {
186                 return NULL;
187         }
188
189         /* check for empty oc_required */
190         if(oc->soc_required == NULL) {
191                 return NULL;
192         }
193
194         /* for each required attribute */
195         for ( i = 0; oc->soc_required[i] != NULL; i++ ) {
196                 at = oc->soc_required[i];
197                 /* see if it's in the entry */
198                 for ( a = e->e_attrs; a != NULL; a = a->a_next ) {
199                         if( a->a_desc->ad_type == at ) {
200                                 break;
201                         }
202                 }
203                 /* not there => schema violation */
204                 if ( a == NULL ) {
205                         return at->sat_cname;
206                 }
207         }
208
209         return( NULL );
210 }
211
212 int oc_check_allowed(
213         AttributeType *at,
214         struct berval **ocl )
215 {
216         ObjectClass     *oc;
217         int             i, j;
218
219 #ifdef NEW_LOGGING
220         LDAP_LOG(( "schema", LDAP_LEVEL_ENTRY,
221                    "oc_check_allowed: type \"%s\"\n", at->sat_cname ));
222 #else
223         Debug( LDAP_DEBUG_TRACE,
224                 "oc_check_allowed type \"%s\"\n",
225                 at->sat_cname, 0, 0 );
226 #endif
227
228         /* always allow objectClass attribute */
229         if ( strcasecmp( at->sat_cname, "objectClass" ) == 0 ) {
230                 return LDAP_SUCCESS;
231         }
232
233
234         /*
235          * All operational attributions are allowed by schema rules.
236          */
237         if( is_at_operational(at) ) {
238                 return LDAP_SUCCESS;
239         }
240
241         /* check that the type appears as req or opt in at least one oc */
242         for ( i = 0; ocl[i] != NULL; i++ ) {
243                 /* if we know about the oc */
244                 if ( (oc = oc_find( ocl[i]->bv_val )) != NULL ) {
245                         /* does it require the type? */
246                         for ( j = 0; oc->soc_required != NULL && 
247                                 oc->soc_required[j] != NULL; j++ )
248                         {
249                                 if( at == oc->soc_required[j] ) {
250                                         return LDAP_SUCCESS;
251                                 }
252                         }
253                         /* does it allow the type? */
254                         for ( j = 0; oc->soc_allowed != NULL && 
255                                 oc->soc_allowed[j] != NULL; j++ )
256                         {
257                                 if( at == oc->soc_allowed[j] ) {
258                                         return LDAP_SUCCESS;
259                                 }
260                         }
261                         /* maybe the next oc allows it */
262                 }
263         }
264
265         /* not allowed by any oc */
266         return LDAP_OBJECT_CLASS_VIOLATION;
267 }