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