r12658: Couple of fixes related to shared module builds.
[abartlet/samba.git/.git] / source4 / lib / ldb / modules / schema.c
1 /* 
2    ldb database library
3
4    Copyright (C) Simo Sorce  2004-2005
5
6      ** NOTE! The following LGPL license applies to the ldb
7      ** library. This does NOT imply that all of Samba is released
8      ** under the LGPL
9    
10    This library is free software; you can redistribute it and/or
11    modify it under the terms of the GNU Lesser General Public
12    License as published by the Free Software Foundation; either
13    version 2 of the License, or (at your option) any later version.
14
15    This library is distributed in the hope that it will be useful,
16    but WITHOUT ANY WARRANTY; without even the implied warranty of
17    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18    Lesser General Public License for more details.
19
20    You should have received a copy of the GNU Lesser General Public
21    License along with this library; if not, write to the Free Software
22    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
23 */
24
25 /*
26  *  Name: ldb
27  *
28  *  Component: ldb schema module
29  *
30  *  Description: add schema check functionality
31  *
32  *  Author: Simo Sorce
33  */
34
35 #include "includes.h"
36 #include "ldb/include/ldb.h"
37 #include "ldb/include/ldb_errors.h"
38 #include "ldb/include/ldb_private.h"
39
40 #define SCHEMA_FLAG_RESET       0
41 #define SCHEMA_FLAG_MOD_MASK    0x003
42 #define SCHEMA_FLAG_MOD_ADD     0x001
43 #define SCHEMA_FLAG_MOD_REPLACE 0x002
44 #define SCHEMA_FLAG_MOD_DELETE  0x003
45 #define SCHEMA_FLAG_AUXILIARY   0x010
46 #define SCHEMA_FLAG_ABSTRACT    0x020
47 #define SCHEMA_FLAG_STRUCTURAL  0x040
48 #define SCHEMA_FLAG_CHECKED     0x100
49
50
51 /* TODO: check attributes syntaxes
52          check there's only one structrual class (or a chain of structural classes)
53 */
54
55 struct schema_attribute {
56         int flags;
57         char *name;
58 };
59
60 struct schema_attribute_list {
61         struct schema_attribute *attr;
62         int num;
63 };
64
65 struct schema_structures {
66         struct schema_attribute_list entry_attrs;
67         struct schema_attribute_list objectclasses;
68         struct schema_attribute_list required_attrs;
69         struct schema_attribute_list optional_attrs;
70 };
71
72 static struct schema_attribute *schema_find_attribute(struct schema_attribute_list *list, const char *attr_name)
73 {
74         unsigned int i;
75         for (i = 0; i < list->num; i++) {
76                 if (ldb_attr_cmp(list->attr[i].name, attr_name) == 0) {
77                         return &(list->attr[i]);
78                 }
79         }
80         return NULL;
81 }
82
83 /* get all the attributes and objectclasses found in msg and put them in schema_structure
84    attributes go in the entry_attrs structure for later checking
85    objectclasses go in the objectclasses structure */
86 static int get_msg_attributes(struct schema_structures *ss, const struct ldb_message *msg, int flag_mask)
87 {
88         int i, j, anum, cnum;
89
90         ss->entry_attrs.attr = talloc_realloc(ss, ss->entry_attrs.attr,
91                                               struct schema_attribute,
92                                               ss->entry_attrs.num + msg->num_elements);
93         if (ss->entry_attrs.attr == NULL) {
94                 return -1;
95         }
96
97         for (i = 0, anum = ss->entry_attrs.num; i < msg->num_elements; i++) {
98
99                 if (ldb_attr_cmp(msg->elements[i].name, "objectclass") == 0) {
100
101                         ss->objectclasses.attr = talloc_realloc(ss, ss->objectclasses.attr,
102                                                                 struct schema_attribute,
103                                                                 ss->objectclasses.num + msg->elements[i].num_values);
104                         if (ss->objectclasses.attr == NULL) {
105                                 return -1;
106                         }
107
108                         for (j = 0, cnum = ss->objectclasses.num; j < msg->elements[i].num_values; j++) {
109                                 ss->objectclasses.attr[cnum+j].name = (char *)msg->elements[i].values[j].data;
110                                 ss->objectclasses.attr[cnum+j].flags = msg->elements[i].flags & flag_mask;
111                         }
112                         ss->objectclasses.num += msg->elements[i].num_values;
113                 }
114
115                 /* TODO: Check for proper attribute Syntax ! */
116
117                 ss->entry_attrs.attr[anum+i].flags = msg->elements[i].flags & flag_mask;
118                 ss->entry_attrs.attr[anum+i].name = talloc_reference(ss->entry_attrs.attr,
119                                                             msg->elements[i].name);
120                 if (ss->entry_attrs.attr[anum+i].name == NULL) {
121                         return -1;
122                 }
123         }
124         ss->entry_attrs.num += msg->num_elements;
125
126         return 0;
127 }
128
129 static int get_entry_attributes(struct ldb_context *ldb, const struct ldb_dn *dn, struct schema_structures *ss)
130 {
131         struct ldb_result *srch;
132         int ret;
133
134         ret = ldb_search(ldb, dn, LDB_SCOPE_BASE, NULL, NULL, &srch);
135         if (ret != 1) {
136                 return ret;
137         }
138         talloc_steal(ss, srch);
139
140         /* set flags to 0 as flags on search have undefined values */
141         ret = get_msg_attributes(ss, *(srch->msgs), 0);
142         if (ret != 0) {
143                 talloc_free(srch);
144                 return ret;
145         }
146
147         return 0;
148 }
149
150 /* add all attributes in el avoiding duplicates in schema_attribute_list */
151 static int add_attribute_uniq(void *mem_ctx, struct schema_attribute_list *list, int flags, struct ldb_message_element *el)
152 {
153         int i, j, vals;
154
155         vals = el->num_values;
156         list->attr = talloc_realloc(mem_ctx, list->attr, struct schema_attribute, list->num + vals);
157         if (list->attr == NULL) {
158                 return -1;
159         }
160         for (i = 0, j = 0; i < vals; i++) {
161                 int c, found, len;
162
163                 found = 0;
164                 for (c = 0; c < list->num; c++) {
165                         len = strlen(list->attr[c].name);
166                         if (len == el->values[i].length) {
167                                 if (ldb_attr_cmp(list->attr[c].name, 
168                                                  (char *)el->values[i].data) == 0) {
169                                         found = 1;
170                                         break;
171                                 }
172                         }
173                 }
174                 if (!found) {
175                         list->attr[j + list->num].name = (char *)el->values[i].data;
176                         list->attr[j + list->num].flags = flags;
177                         j++;
178                 }
179         }
180         list->num += j;
181
182         return 0;
183 }
184
185
186 /* we need to get all attributes referenced by the entry objectclasses,
187    recursively get parent objectlasses attributes */
188 static int get_attr_list_recursive(struct ldb_module *module, struct schema_structures *schema_struct)
189 {
190         struct ldb_result *srch;
191         int i, j;
192         int ret;
193
194         for (i = 0; i < schema_struct->objectclasses.num; i++) {
195                 char *filter;
196
197                 if ((schema_struct->objectclasses.attr[i].flags & SCHEMA_FLAG_MOD_MASK) == SCHEMA_FLAG_MOD_DELETE) {
198                         continue;
199                 }
200                 filter = talloc_asprintf(schema_struct, "lDAPDisplayName=%s", schema_struct->objectclasses.attr[i].name);
201                 if (filter == NULL) {
202                         return -1;
203                 }
204
205                 ret = ldb_search(module->ldb, NULL, LDB_SCOPE_SUBTREE, filter, NULL, &srch);
206                 if (ret != 1) {
207                         return ret;
208                 }
209                 talloc_steal(schema_struct, srch);
210
211                 if (ret <= 0) {
212                         /* Schema DB Error: Error occurred retrieving
213                            Object Class Description */
214                         ldb_debug_set(module->ldb, LDB_DEBUG_ERROR, 
215                                       "Error retrieving Objectclass %s.\n", 
216                                       schema_struct->objectclasses.attr[i].name);
217                         return -1;
218                 }
219                 if (ret > 1) {
220                         /* Schema DB Error: Too Many Records */
221                         ldb_debug_set(module->ldb, LDB_DEBUG_ERROR, 
222                                       "Too many records found retrieving Objectclass %s.\n", 
223                                       schema_struct->objectclasses.attr[i].name);
224                         return -1;
225                 }
226
227                 /* Add inherited classes eliminating duplicates */
228                 /* fill in required_attrs and optional_attrs attribute lists */
229                 for (j = 0; j < srch->msgs[0]->num_elements; j++) {
230                         int is_aux, is_class;
231
232                         is_aux = 0;
233                         is_class = 0;
234                         if (ldb_attr_cmp(srch->msgs[0]->elements[j].name, "systemAuxiliaryclass") == 0) {
235                                 is_aux = SCHEMA_FLAG_AUXILIARY;
236                                 is_class = 1;
237                         }
238                         if (ldb_attr_cmp(srch->msgs[0]->elements[j].name, "auxiliaryClass") == 0) {
239                                 is_aux = SCHEMA_FLAG_AUXILIARY;
240                                 is_class = 1;
241                         }
242                         if (ldb_attr_cmp(srch->msgs[0]->elements[j].name, "subClassOf") == 0) {
243                                 is_class = 1;
244                         }
245
246                         if (is_class) {
247                                 if (add_attribute_uniq(schema_struct,
248                                                         &schema_struct->objectclasses,
249                                                         is_aux,
250                                                         &srch->msgs[0]->elements[j]) != 0) {
251                                         return -1;
252                                 }
253                         } else {
254
255                                 if (ldb_attr_cmp(srch->msgs[0]->elements[j].name, "mustContain") == 0 ||
256                                         ldb_attr_cmp(srch->msgs[0]->elements[j].name, "SystemMustContain") == 0) {
257                                         if (add_attribute_uniq(schema_struct,
258                                                                 &schema_struct->required_attrs,
259                                                                 SCHEMA_FLAG_RESET,
260                                                                 &srch->msgs[0]->elements[j]) != 0) {
261                                                 return -1;
262                                         }
263                                 }
264
265                                 if (ldb_attr_cmp(srch->msgs[0]->elements[j].name, "mayContain") == 0 ||
266                                     ldb_attr_cmp(srch->msgs[0]->elements[j].name, "SystemMayContain") == 0) {
267
268                                         if (add_attribute_uniq(schema_struct,
269                                                                 &schema_struct->optional_attrs,
270                                                                 SCHEMA_FLAG_RESET,
271                                                                 &srch->msgs[0]->elements[j]) != 0) {
272                                                 return -1;
273                                         }
274                                 }
275                         }
276                 }
277         }
278
279         return 0;
280 }
281
282 /* add_record */
283 static int schema_add(struct ldb_module *module, struct ldb_request *req)
284 {
285         const struct ldb_message *msg = req->op.add.message;
286         struct schema_structures *entry_structs;
287         unsigned int i;
288         int ret;
289
290         /* First implementation:
291                 Build up a list of required_attrs and optional_attrs attributes from each objectclass
292                 Check all the required_attrs attributes are present and all the other attributes
293                 are optional_attrs attributes
294                 Throw an error in case a check fail
295                 Free all structures and commit the change
296         */
297
298         /* do not check on our control entries */
299         if (ldb_dn_is_special(msg->dn)) {
300                 return ldb_next_request(module, req);
301         }
302
303         /* TODO: check parent exists */
304
305         entry_structs = talloc_zero(module, struct schema_structures);
306         if (!entry_structs) {
307                 return -1;
308         }
309
310         ret = get_msg_attributes(entry_structs, msg, SCHEMA_FLAG_MOD_MASK);
311         if (ret != 0) {
312                 talloc_free(entry_structs);
313                 return ret;
314         }
315
316         ret = get_attr_list_recursive(module, entry_structs);
317         if (ret != 0) {
318                 talloc_free(entry_structs);
319                 return ret;
320         }
321
322         /* now check all required_attrs attributes are present */
323         for (i = 0; i < entry_structs->required_attrs.num; i++) {
324                 struct schema_attribute *attr;
325
326                 attr = schema_find_attribute(&entry_structs->entry_attrs,
327                                              entry_structs->required_attrs.attr[i].name);
328
329                 if (attr == NULL) { /* not found */
330                         ldb_debug_set(module->ldb, LDB_DEBUG_ERROR, 
331                                       "The required_attrs attribute %s is missing.\n",
332                                       entry_structs->required_attrs.attr[i].name);
333                         talloc_free(entry_structs);
334                         return LDB_ERR_OBJECT_CLASS_VIOLATION;
335                 }
336
337                 /* mark the attribute as checked */
338                 attr->flags = SCHEMA_FLAG_CHECKED;
339         }
340
341         /* now check all others atribs are at least optional_attrs */
342         for (i = 0; i < entry_structs->entry_attrs.num; i++) {
343
344                 if (entry_structs->entry_attrs.attr[i].flags != SCHEMA_FLAG_CHECKED) {
345                         struct schema_attribute *attr;
346
347                         attr = schema_find_attribute(&entry_structs->optional_attrs,
348                                                      entry_structs->entry_attrs.attr[i].name);
349
350                         if (attr == NULL) { /* not found */
351                                 ldb_debug_set(module->ldb, LDB_DEBUG_ERROR, 
352                                               "The attribute %s is not referenced by any objectclass.\n",
353                                               entry_structs->entry_attrs.attr[i].name);
354                                 talloc_free(entry_structs);
355                                 return LDB_ERR_OBJECT_CLASS_VIOLATION;
356                         }
357                 }
358         }
359
360         talloc_free(entry_structs);
361
362         return ldb_next_request(module, req);
363 }
364
365 /* modify_record */
366 static int schema_modify(struct ldb_module *module, struct ldb_request *req)
367 {
368         const struct ldb_message *msg = req->op.mod.message;
369         struct schema_structures *entry_structs;
370         unsigned int i;
371         int ret;
372
373         /* First implementation:
374                 Retrieve the ldap entry and get the objectclasses,
375                 add msg contained objectclasses if any.
376                 Build up a list of required_attrs and optional_attrs attributes from each objectclass
377                 Check all the attributes are optional_attrs or required_attrs.
378                 Throw an error in case a check fail.
379                 Free all structures and commit the change.
380         */
381
382         /* do not check on our control entries */
383         if (ldb_dn_is_special(msg->dn)) {
384                 return ldb_next_request(module, req);
385         }
386
387         /* allocate object structs */
388         entry_structs = talloc_zero(module, struct schema_structures);
389         if (!entry_structs) {
390                 return -1;
391         }
392
393         /* now search for the stored entry objectclasses and attributes*/
394         ret = get_entry_attributes(module->ldb, msg->dn, entry_structs);
395         if (ret != 0) {
396                 talloc_free(entry_structs);
397                 return ret;
398         }
399
400         /* get list of values to modify */
401         ret = get_msg_attributes(entry_structs, msg, SCHEMA_FLAG_MOD_MASK);
402         if (ret != 0) {
403                 talloc_free(entry_structs);
404                 return ret;
405         }
406
407         ret = get_attr_list_recursive(module, entry_structs);
408         if (ret != 0) {
409                 talloc_free(entry_structs);
410                 return ret;
411         }
412
413         /* now check all required_attrs attributes are present */
414         for (i = 0; i < entry_structs->required_attrs.num; i++) {
415                 struct schema_attribute *attr;
416
417                 attr = schema_find_attribute(&entry_structs->entry_attrs,
418                                              entry_structs->required_attrs.attr[i].name);
419
420                 if (attr == NULL) { /* not found */
421                         ldb_debug_set(module->ldb, LDB_DEBUG_ERROR, 
422                                       "The required_attrs attribute %s is missing.\n",
423                                       entry_structs->required_attrs.attr[i].name);
424                         talloc_free(entry_structs);
425                         return LDB_ERR_OBJECT_CLASS_VIOLATION;
426                 }
427
428                 /* check we are not trying to delete a required attribute */
429                 /* TODO: consider multivalued attrs */
430                 if ((attr->flags & SCHEMA_FLAG_MOD_DELETE) != 0) {
431                         ldb_debug_set(module->ldb, LDB_DEBUG_ERROR, 
432                                       "Trying to delete the required attribute %s.\n",
433                                       attr->name);
434                         talloc_free(entry_structs);
435                         return LDB_ERR_OBJECT_CLASS_VIOLATION;
436                 }
437
438                 /* mark the attribute as checked */
439                 attr->flags = SCHEMA_FLAG_CHECKED;
440         }
441
442         /* now check all others atribs are at least optional_attrs */
443         for (i = 0; i < entry_structs->entry_attrs.num; i++) {
444
445                 if (entry_structs->entry_attrs.attr[i].flags != SCHEMA_FLAG_CHECKED) {
446                         struct schema_attribute *attr;
447
448                         attr = schema_find_attribute(&entry_structs->optional_attrs,
449                                                      entry_structs->entry_attrs.attr[i].name);
450
451                         if (attr == NULL) { /* not found */
452                                 ldb_debug_set(module->ldb, LDB_DEBUG_ERROR, 
453                                               "The attribute %s is not referenced by any objectclass.\n",
454                                               entry_structs->entry_attrs.attr[i].name);
455                                 talloc_free(entry_structs);
456                                 return LDB_ERR_OBJECT_CLASS_VIOLATION;
457                         }
458                 }
459         }
460
461         talloc_free(entry_structs);
462
463         return ldb_next_request(module, req);
464 }
465
466 static int schema_request(struct ldb_module *module, struct ldb_request *req)
467 {
468         switch (req->operation) {
469
470         case LDB_REQ_ADD:
471                 return schema_add(module, req);
472
473         case LDB_REQ_MODIFY:
474                 return schema_modify(module, req);
475
476         default:
477                 return ldb_next_request(module, req);
478
479         }
480 }
481
482 static const struct ldb_module_ops schema_ops = {
483         .name              = "schema",
484         .request           = schema_request
485 };
486
487 struct ldb_module *schema_module_init(struct ldb_context *ldb, const char *options[])
488 {
489         struct ldb_module *ctx;
490
491         ctx = talloc(ldb, struct ldb_module);
492         if (!ctx) {
493                 return NULL;
494         }
495
496         ctx->private_data = NULL;
497         ctx->ldb = ldb;
498         ctx->prev = ctx->next = NULL;
499         ctx->ops = &schema_ops;
500
501         return ctx;
502 }