r4863: schema_find_attribute() should be static
[abartlet/samba.git/.git] / source4 / lib / ldb / modules / schema.c
1 /* 
2    ldb database library
3
4    Copyright (C) Simo Sorce  2004
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_private.h"
38
39 #define SCHEMA_FLAG_RESET       0
40 #define SCHEMA_FLAG_MOD_MASK    0x003
41 #define SCHEMA_FLAG_MOD_ADD     0x001
42 #define SCHEMA_FLAG_MOD_REPLACE 0x002
43 #define SCHEMA_FLAG_MOD_DELETE  0x003
44 #define SCHEMA_FLAG_AUXILIARY   0x010
45 #define SCHEMA_FLAG_ABSTRACT    0x020
46 #define SCHEMA_FLAG_STRUCTURAL  0x040
47 #define SCHEMA_FLAG_CHECKED     0x100
48
49
50 /* TODO: check attributes syntaxes
51          check there's only one structrual class (or a chain of structural classes)
52 */
53
54 struct private_data {
55         const char *error_string;
56 };
57
58 struct schema_attribute {
59         int flags;
60         char *name;
61 };
62
63 struct schema_attribute_list {
64         struct schema_attribute *attr;
65         int num;
66 };
67
68 struct schema_structures {
69         struct schema_attribute_list entry_attrs;
70         struct schema_attribute_list objectclasses;
71         struct schema_attribute_list required_attrs;
72         struct schema_attribute_list optional_attrs;
73 };
74
75 /* This function embedds the knowledge of aliased names.
76    Currently it handles only dn vs distinguishedNAme as a special case as AD
77    only have this special alias case, in future we should read the schema
78    to find out which names have an alias and check for them */
79 static int schema_attr_cmp(const char *attr1, const char *attr2)
80 {
81         int ret;
82
83         ret = ldb_attr_cmp(attr1, attr2);
84         if (ret != 0) {
85                 if ((ldb_attr_cmp("dn", attr1) == 0) &&
86                     (ldb_attr_cmp("distinguishedName", attr2) == 0)) {
87                         return 0;
88                 }
89                 if ((ldb_attr_cmp("dn", attr2) == 0) &&
90                     (ldb_attr_cmp("distinguishedName", attr1) == 0)) {
91                         return 0;
92                 }
93         }
94         return ret;
95 }
96
97 static struct schema_attribute *schema_find_attribute(struct schema_attribute_list *list, const char *attr_name)
98 {
99         unsigned int i;
100         for (i = 0; i < list->num; i++) {
101                 if (ldb_attr_cmp(list->attr[i].name, attr_name) == 0) {
102                         return &(list->attr[i]);
103                 }
104         }
105         return NULL;
106 }
107
108 /* get all the attributes and objectclasses found in msg and put them in schema_structure
109    attributes go in the entry_attrs structure for later checking
110    objectclasses go in the objectclasses structure */
111 static int get_msg_attributes(struct schema_structures *ss, const struct ldb_message *msg)
112 {
113         int i, j, k, l;
114
115         ss->entry_attrs.attr = talloc_realloc(ss, ss->entry_attrs.attr,
116                                               struct schema_attribute,
117                                               ss->entry_attrs.num + msg->num_elements);
118         if (ss->entry_attrs.attr == NULL) {
119                 return -1;
120         }
121
122         for (i = 0, j = ss->entry_attrs.num; i < msg->num_elements; i++) {
123
124                 if (schema_attr_cmp(msg->elements[i].name, "objectclass") == 0) {
125
126                         ss->objectclasses.attr = talloc_realloc(ss, ss->objectclasses.attr,
127                                                                 struct schema_attribute,
128                                                                 ss->objectclasses.num + msg->elements[i].num_values);
129                         if (ss->objectclasses.attr == NULL) {
130                                 return -1;
131                         }
132
133                         for (k = 0, l = ss->objectclasses.num; k < msg->elements[i].num_values; k++) {
134                                 ss->objectclasses.attr[l].name = msg->elements[i].values[k].data;
135                                 ss->objectclasses.attr[l].flags = msg->elements[i].flags;
136                                 l++;
137                         }
138                         ss->objectclasses.num += msg->elements[i].num_values;
139                 }
140
141                 ss->entry_attrs.attr[j].flags = msg->elements[i].flags;
142                 ss->entry_attrs.attr[j].name = talloc_reference(ss->entry_attrs.attr,
143                                                             msg->elements[i].name);
144                 if (ss->entry_attrs.attr[j].name == NULL) {
145                         return -1;
146                 }
147                 j++;
148         }
149         ss->entry_attrs.num += msg->num_elements;
150
151         return 0;
152 }
153
154 static int get_entry_attributes(struct ldb_context *ldb, const char *dn, struct schema_structures *ss)
155 {
156         char *filter = talloc_asprintf(ss, "dn=%s", dn);
157         struct ldb_message **srch;
158         int ret;
159
160         ret = ldb_search(ldb, NULL, LDB_SCOPE_SUBTREE, filter, NULL, &srch);
161         if (ret != 1) {
162                 return ret;
163         }
164         talloc_steal(ss, srch);
165
166         ret = get_msg_attributes(ss, *srch);
167         if (ret != 0) {
168                 ldb_search_free(ldb, srch);
169                 return ret;
170         }
171
172         return 0;
173 }
174
175 /* add all attributes in el avoiding duplicates in schema_attribute_list */
176 static int add_attribute_uniq(void *mem_ctx, struct schema_attribute_list *list, int flags, struct ldb_message_element *el)
177 {
178         int i, j, vals;
179
180         vals = el->num_values;
181         list->attr = talloc_realloc(mem_ctx, list->attr, struct schema_attribute, list->num + vals);
182         if (list->attr == NULL) {
183                 return -1;
184         }
185         for (i = 0, j = 0; i < vals; i++) {
186                 int c, found, len;
187
188                 found = 0;
189                 for (c = 0; c < list->num; c++) {
190                         len = strlen(list->attr[c].name);
191                         if (len == el->values[i].length) {
192                                 if (schema_attr_cmp(list->attr[c].name, el->values[i].data) == 0) {
193                                         found = 1;
194                                         break;
195                                 }
196                         }
197                 }
198                 if (!found) {
199                         list->attr[j + list->num].name = el->values[i].data;
200                         list->attr[j + list->num].flags = flags;
201                         j++;
202                 }
203         }
204         list->num += j;
205
206         return 0;
207 }
208
209
210 /* we need to get all attributes referenced by the entry objectclasses,
211    recursively get parent objectlasses attributes */
212 static int get_attr_list_recursive(struct ldb_module *module, struct schema_structures *schema_struct)
213 {
214         struct private_data *data = (struct private_data *)module->private_data;
215         struct ldb_message **srch;
216         int i, j;
217         int ret;
218
219         for (i = 0; i < schema_struct->objectclasses.num; i++) {
220                 char *filter;
221
222                 if ((schema_struct->objectclasses.attr[i].flags & SCHEMA_FLAG_MOD_MASK) == SCHEMA_FLAG_MOD_DELETE) {
223                         continue;
224                 }
225                 filter = talloc_asprintf(schema_struct, "lDAPDisplayName=%s", schema_struct->objectclasses.attr[i].name);
226                 if (filter == NULL) {
227                         return -1;
228                 }
229
230                 ret = ldb_search(module->ldb, NULL, LDB_SCOPE_SUBTREE, filter, NULL, &srch);
231                 if (ret != 1) {
232                         return ret;
233                 }
234                 talloc_steal(schema_struct, srch);
235
236                 if (ret <= 0) {
237                         /* Schema DB Error: Error occurred retrieving Object Class Description */
238                         ldb_debug(module->ldb, LDB_DEBUG_ERROR, "Error retrieving Objectclass %s.\n", schema_struct->objectclasses.attr[i].name);
239                         data->error_string = "Internal error. Error retrieving schema objectclass";
240                         return -1;
241                 }
242                 if (ret > 1) {
243                         /* Schema DB Error: Too Many Records */
244                         ldb_debug(module->ldb, LDB_DEBUG_ERROR, "Too many records found retrieving Objectclass %s.\n", schema_struct->objectclasses.attr[i].name);
245                         data->error_string = "Internal error. Too many records searching for schema objectclass";
246                         return -1;
247                 }
248
249                 /* Add inherited classes eliminating duplicates */
250                 /* fill in required_attrs and optional_attrs attribute lists */
251                 for (j = 0; j < (*srch)->num_elements; j++) {
252                         int is_aux, is_class;
253
254                         is_aux = 0;
255                         is_class = 0;
256                         if (schema_attr_cmp((*srch)->elements[j].name, "systemAuxiliaryclass") == 0) {
257                                 is_aux = SCHEMA_FLAG_AUXILIARY;
258                                 is_class = 1;
259                         }
260                         if (schema_attr_cmp((*srch)->elements[j].name, "subClassOf") == 0) {
261                                 is_class = 1;
262                         }
263
264                         if (is_class) {
265                                 if (add_attribute_uniq(schema_struct,
266                                                         &schema_struct->objectclasses,
267                                                         is_aux,
268                                                         &(*srch)->elements[j]) != 0) {
269                                         return -1;
270                                 }
271                         } else {
272
273                                 if (schema_attr_cmp((*srch)->elements[j].name, "mustContain") == 0 ||
274                                         schema_attr_cmp((*srch)->elements[j].name, "SystemMustContain") == 0) {
275                                         if (add_attribute_uniq(schema_struct,
276                                                                 &schema_struct->required_attrs,
277                                                                 SCHEMA_FLAG_RESET,
278                                                                 &(*srch)->elements[j]) != 0) {
279                                                 return -1;
280                                         }
281                                 }
282
283                                 if (schema_attr_cmp((*srch)->elements[j].name, "mayContain") == 0 ||
284                                     schema_attr_cmp((*srch)->elements[j].name, "SystemMayContain") == 0) {
285
286                                         if (add_attribute_uniq(schema_struct,
287                                                                 &schema_struct->optional_attrs,
288                                                                 SCHEMA_FLAG_RESET,
289                                                                 &(*srch)->elements[j]) != 0) {
290                                                 return -1;
291                                         }
292                                 }
293                         }
294                 }
295         }
296
297         return 0;
298 }
299
300 /* close */
301 static int schema_close(struct ldb_module *module)
302 {
303         return ldb_next_close(module);
304 }
305
306 /* search */
307 static int schema_search(struct ldb_module *module, const char *base,
308                        enum ldb_scope scope, const char *expression,
309                        const char * const *attrs, struct ldb_message ***res)
310 {
311         return ldb_next_search(module, base, scope, expression, attrs, res); 
312 }
313
314 /* search_free */
315 static int schema_search_free(struct ldb_module *module, struct ldb_message **res)
316 {
317         return ldb_next_search_free(module, res);
318 }
319
320 /* add_record */
321 static int schema_add_record(struct ldb_module *module, const struct ldb_message *msg)
322 {
323         struct private_data *data = (struct private_data *)module->private_data;
324         struct schema_structures *entry_structs;
325         unsigned int i;
326         int ret;
327
328         /* First implementation:
329                 Build up a list of required_attrs and optional_attrs attributes from each objectclass
330                 Check all the required_attrs attributes are present and all the other attributes
331                 are optional_attrs attributes
332                 Throw an error in case a check fail
333                 Free all structures and commit the change
334         */
335
336         if (msg->dn[0] == '@') { /* do not check on our control entries */
337                 return ldb_next_add_record(module, msg);
338         }
339
340         entry_structs = talloc_zero(module, struct schema_structures);
341         if (!entry_structs) {
342                 return -1;
343         }
344
345         ret = get_msg_attributes(entry_structs, msg);
346         if (ret != 0) {
347                 talloc_free(entry_structs);
348                 return ret;
349         }
350
351         ret = get_attr_list_recursive(module, entry_structs);
352         if (ret != 0) {
353                 talloc_free(entry_structs);
354                 return ret;
355         }
356
357         /* now check all required_attrs attributes are present */
358         for (i = 0; i < entry_structs->required_attrs.num; i++) {
359                 struct schema_attribute *attr;
360
361                 attr = schema_find_attribute(&entry_structs->entry_attrs,
362                                              entry_structs->required_attrs.attr[i].name);
363
364                 if (attr == NULL) { /* not found */
365                         ldb_debug(module->ldb, LDB_DEBUG_ERROR,
366                                   "The required_attrs attribute %s is missing.\n",
367                                   entry_structs->required_attrs.attr[i].name);
368
369                         data->error_string = "Objectclass violation, a required attribute is missing";
370                         talloc_free(entry_structs);
371                         return -1;
372                 }
373
374                 /* check we are not trying to delete a required attribute */
375                 /* TODO: consider multivalued attrs */
376                 if ((attr->flags & SCHEMA_FLAG_MOD_DELETE) != 0) {
377                         ldb_debug(module->ldb, LDB_DEBUG_ERROR,
378                                   "Trying to delete the required attribute %s.\n",
379                                   attr->name);
380
381                         data->error_string = "Objectclass violation, a required attribute cannot be removed";
382                         talloc_free(entry_structs);
383                         return -1;
384                 }
385
386                 /* mark the attribute as checked */
387                 attr->flags = SCHEMA_FLAG_CHECKED;
388         }
389
390         /* now check all others atribs are at least optional_attrs */
391         for (i = 0; i < entry_structs->entry_attrs.num; i++) {
392
393                 if (entry_structs->entry_attrs.attr[i].flags != SCHEMA_FLAG_CHECKED) {
394                         struct schema_attribute *attr;
395
396                         attr = schema_find_attribute(&entry_structs->optional_attrs,
397                                                      entry_structs->entry_attrs.attr[i].name);
398
399                         if (attr == NULL) { /* not found */
400                                 ldb_debug(module->ldb, LDB_DEBUG_ERROR,
401                                           "The attribute %s is not referenced by any objectclass.\n",
402                                           entry_structs->entry_attrs.attr[i].name);
403
404                                 data->error_string = "Objectclass violation, an invalid attribute name was found";
405                                 talloc_free(entry_structs);
406                                 return -1;
407                         }
408                 }
409         }
410
411         talloc_free(entry_structs);
412
413         return ldb_next_add_record(module, msg);
414 }
415
416 /* modify_record */
417 static int schema_modify_record(struct ldb_module *module, const struct ldb_message *msg)
418 {
419         struct private_data *data = (struct private_data *)module->private_data;
420         struct schema_structures *entry_structs;
421         unsigned int i;
422         int ret;
423
424         /* First implementation:
425                 Retrieve the ldap entry and get the objectclasses,
426                 add msg contained objectclasses if any.
427                 Build up a list of required_attrs and optional_attrs attributes from each objectclass
428                 Check all the attributes are optional_attrs or required_attrs.
429                 Throw an error in case a check fail.
430                 Free all structures and commit the change.
431         */
432
433         if (msg->dn[0] == '@') { /* do not check on our control entries */
434                 return ldb_next_modify_record(module, msg);
435         }
436
437         /* allocate object structs */
438         entry_structs = talloc_zero(module, struct schema_structures);
439         if (!entry_structs) {
440                 return -1;
441         }
442
443         /* now search for the stored entry objectclasses and attributes*/
444         ret = get_entry_attributes(module->ldb, msg->dn, entry_structs);
445         if (ret != 0) {
446                 talloc_free(entry_structs);
447                 return ret;
448         }
449
450         /* get list of values to modify */
451         ret = get_msg_attributes(entry_structs, msg);
452         if (ret != 0) {
453                 talloc_free(entry_structs);
454                 return ret;
455         }
456
457         ret = get_attr_list_recursive(module, entry_structs);
458         if (ret != 0) {
459                 talloc_free(entry_structs);
460                 return ret;
461         }
462
463         /* now check all required_attrs attributes are present */
464         for (i = 0; i < entry_structs->required_attrs.num; i++) {
465                 struct schema_attribute *attr;
466
467                 attr = schema_find_attribute(&entry_structs->entry_attrs,
468                                              entry_structs->required_attrs.attr[i].name);
469
470                 if (attr == NULL) { /* not found */
471                         ldb_debug(module->ldb, LDB_DEBUG_ERROR,
472                                   "The required_attrs attribute %s is missing.\n",
473                                   entry_structs->required_attrs.attr[i].name);
474
475                         data->error_string = "Objectclass violation, a required attribute is missing";
476                         talloc_free(entry_structs);
477                         return -1;
478                 }
479
480                 /* mark the attribute as checked */
481                 attr->flags = SCHEMA_FLAG_CHECKED;
482         }
483
484         /* now check all others atribs are at least optional_attrs */
485         for (i = 0; i < entry_structs->entry_attrs.num; i++) {
486
487                 if (entry_structs->entry_attrs.attr[i].flags != SCHEMA_FLAG_CHECKED) {
488                         struct schema_attribute *attr;
489
490                         attr = schema_find_attribute(&entry_structs->optional_attrs,
491                                                      entry_structs->entry_attrs.attr[i].name);
492
493                         if (attr == NULL) { /* not found */
494                                 ldb_debug(module->ldb, LDB_DEBUG_ERROR,
495                                           "The attribute %s is not referenced by any objectclass.\n",
496                                           entry_structs->entry_attrs.attr[i].name);
497
498                                 data->error_string = "Objectclass violation, an invalid attribute name was found";
499                                 talloc_free(entry_structs);
500                                 return -1;
501                         }
502                 }
503         }
504
505         talloc_free(entry_structs);
506
507         return ldb_next_modify_record(module, msg);
508 }
509
510 /* delete_record */
511 static int schema_delete_record(struct ldb_module *module, const char *dn)
512 {
513 /*      struct private_data *data = (struct private_data *)module->private_data; */
514         return ldb_next_delete_record(module, dn);
515 }
516
517 /* rename_record */
518 static int schema_rename_record(struct ldb_module *module, const char *olddn, const char *newdn)
519 {
520         return ldb_next_rename_record(module, olddn, newdn);
521 }
522
523 static int schema_named_lock(struct ldb_module *module, const char *name) {
524         return ldb_next_named_lock(module, name);
525 }
526
527 static int schema_named_unlock(struct ldb_module *module, const char *name) {
528         return ldb_next_named_unlock(module, name);
529 }
530
531 /* return extended error information */
532 static const char *schema_errstring(struct ldb_module *module)
533 {
534         struct private_data *data = (struct private_data *)module->private_data;
535
536         if (data->error_string) {
537                 const char *error;
538
539                 error = data->error_string;
540                 data->error_string = NULL;
541                 return error;
542         }
543
544         return ldb_next_errstring(module);
545 }
546
547 static const struct ldb_module_ops schema_ops = {
548         "schema",
549         schema_close, 
550         schema_search,
551         schema_search_free,
552         schema_add_record,
553         schema_modify_record,
554         schema_delete_record,
555         schema_rename_record,
556         schema_named_lock,
557         schema_named_unlock,
558         schema_errstring,
559 };
560
561 #ifdef HAVE_DLOPEN_DISABLED
562 struct ldb_module *init_module(struct ldb_context *ldb, const char *options[])
563 #else
564 struct ldb_module *schema_module_init(struct ldb_context *ldb, const char *options[])
565 #endif
566 {
567         struct ldb_module *ctx;
568         struct private_data *data;
569
570         ctx = talloc(ldb, struct ldb_module);
571         if (!ctx) {
572                 return NULL;
573         }
574
575         data = talloc(ctx, struct private_data);
576         if (data == NULL) {
577                 talloc_free(ctx);
578                 return NULL;
579         }
580
581         data->error_string = NULL;
582         ctx->private_data = data;
583         ctx->ldb = ldb;
584         ctx->prev = ctx->next = NULL;
585         ctx->ops = &schema_ops;
586
587         return ctx;
588 }