4 Copyright (C) Simo Sorce 2004-2005
6 ** NOTE! The following LGPL license applies to the ldb
7 ** library. This does NOT imply that all of Samba is released
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.
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.
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
28 * Component: ldb schema module
30 * Description: add schema check functionality
36 #include "ldb/include/ldb.h"
37 #include "ldb/include/ldb_errors.h"
38 #include "ldb/include/ldb_private.h"
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
51 /* TODO: check attributes syntaxes
52 check there's only one structrual class (or a chain of structural classes)
55 struct schema_attribute {
60 struct schema_attribute_list {
61 struct schema_attribute *attr;
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;
72 static struct schema_attribute *schema_find_attribute(struct schema_attribute_list *list, const char *attr_name)
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]);
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)
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) {
97 for (i = 0, anum = ss->entry_attrs.num; i < msg->num_elements; i++) {
99 if (ldb_attr_cmp(msg->elements[i].name, "objectclass") == 0) {
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) {
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;
112 ss->objectclasses.num += msg->elements[i].num_values;
115 /* TODO: Check for proper attribute Syntax ! */
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) {
124 ss->entry_attrs.num += msg->num_elements;
129 static int get_entry_attributes(struct ldb_context *ldb, const struct ldb_dn *dn, struct schema_structures *ss)
131 struct ldb_result *srch;
134 ret = ldb_search(ldb, dn, LDB_SCOPE_BASE, NULL, NULL, &srch);
138 talloc_steal(ss, srch);
140 /* set flags to 0 as flags on search have undefined values */
141 ret = get_msg_attributes(ss, *(srch->msgs), 0);
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)
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) {
160 for (i = 0, j = 0; i < vals; i++) {
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) {
175 list->attr[j + list->num].name = (char *)el->values[i].data;
176 list->attr[j + list->num].flags = flags;
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)
190 struct ldb_result *srch;
194 for (i = 0; i < schema_struct->objectclasses.num; i++) {
197 if ((schema_struct->objectclasses.attr[i].flags & SCHEMA_FLAG_MOD_MASK) == SCHEMA_FLAG_MOD_DELETE) {
200 filter = talloc_asprintf(schema_struct, "lDAPDisplayName=%s", schema_struct->objectclasses.attr[i].name);
201 if (filter == NULL) {
205 ret = ldb_search(module->ldb, NULL, LDB_SCOPE_SUBTREE, filter, NULL, &srch);
209 talloc_steal(schema_struct, srch);
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);
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);
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;
234 if (ldb_attr_cmp(srch->msgs[0]->elements[j].name, "systemAuxiliaryclass") == 0) {
235 is_aux = SCHEMA_FLAG_AUXILIARY;
238 if (ldb_attr_cmp(srch->msgs[0]->elements[j].name, "auxiliaryClass") == 0) {
239 is_aux = SCHEMA_FLAG_AUXILIARY;
242 if (ldb_attr_cmp(srch->msgs[0]->elements[j].name, "subClassOf") == 0) {
247 if (add_attribute_uniq(schema_struct,
248 &schema_struct->objectclasses,
250 &srch->msgs[0]->elements[j]) != 0) {
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,
260 &srch->msgs[0]->elements[j]) != 0) {
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) {
268 if (add_attribute_uniq(schema_struct,
269 &schema_struct->optional_attrs,
271 &srch->msgs[0]->elements[j]) != 0) {
283 static int schema_add(struct ldb_module *module, struct ldb_request *req)
285 const struct ldb_message *msg = req->op.add.message;
286 struct schema_structures *entry_structs;
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
298 /* do not check on our control entries */
299 if (ldb_dn_is_special(msg->dn)) {
300 return ldb_next_request(module, req);
303 /* TODO: check parent exists */
305 entry_structs = talloc_zero(module, struct schema_structures);
306 if (!entry_structs) {
310 ret = get_msg_attributes(entry_structs, msg, SCHEMA_FLAG_MOD_MASK);
312 talloc_free(entry_structs);
316 ret = get_attr_list_recursive(module, entry_structs);
318 talloc_free(entry_structs);
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;
326 attr = schema_find_attribute(&entry_structs->entry_attrs,
327 entry_structs->required_attrs.attr[i].name);
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;
337 /* mark the attribute as checked */
338 attr->flags = SCHEMA_FLAG_CHECKED;
341 /* now check all others atribs are at least optional_attrs */
342 for (i = 0; i < entry_structs->entry_attrs.num; i++) {
344 if (entry_structs->entry_attrs.attr[i].flags != SCHEMA_FLAG_CHECKED) {
345 struct schema_attribute *attr;
347 attr = schema_find_attribute(&entry_structs->optional_attrs,
348 entry_structs->entry_attrs.attr[i].name);
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;
360 talloc_free(entry_structs);
362 return ldb_next_request(module, req);
366 static int schema_modify(struct ldb_module *module, struct ldb_request *req)
368 const struct ldb_message *msg = req->op.mod.message;
369 struct schema_structures *entry_structs;
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.
382 /* do not check on our control entries */
383 if (ldb_dn_is_special(msg->dn)) {
384 return ldb_next_request(module, req);
387 /* allocate object structs */
388 entry_structs = talloc_zero(module, struct schema_structures);
389 if (!entry_structs) {
393 /* now search for the stored entry objectclasses and attributes*/
394 ret = get_entry_attributes(module->ldb, msg->dn, entry_structs);
396 talloc_free(entry_structs);
400 /* get list of values to modify */
401 ret = get_msg_attributes(entry_structs, msg, SCHEMA_FLAG_MOD_MASK);
403 talloc_free(entry_structs);
407 ret = get_attr_list_recursive(module, entry_structs);
409 talloc_free(entry_structs);
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;
417 attr = schema_find_attribute(&entry_structs->entry_attrs,
418 entry_structs->required_attrs.attr[i].name);
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;
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",
434 talloc_free(entry_structs);
435 return LDB_ERR_OBJECT_CLASS_VIOLATION;
438 /* mark the attribute as checked */
439 attr->flags = SCHEMA_FLAG_CHECKED;
442 /* now check all others atribs are at least optional_attrs */
443 for (i = 0; i < entry_structs->entry_attrs.num; i++) {
445 if (entry_structs->entry_attrs.attr[i].flags != SCHEMA_FLAG_CHECKED) {
446 struct schema_attribute *attr;
448 attr = schema_find_attribute(&entry_structs->optional_attrs,
449 entry_structs->entry_attrs.attr[i].name);
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;
461 talloc_free(entry_structs);
463 return ldb_next_request(module, req);
466 static int schema_request(struct ldb_module *module, struct ldb_request *req)
468 switch (req->operation) {
471 return schema_add(module, req);
474 return schema_modify(module, req);
477 return ldb_next_request(module, req);
482 static const struct ldb_module_ops schema_ops = {
484 .request = schema_request
487 struct ldb_module *schema_module_init(struct ldb_context *ldb, const char *options[])
489 struct ldb_module *ctx;
491 ctx = talloc(ldb, struct ldb_module);
496 ctx->private_data = NULL;
498 ctx->prev = ctx->next = NULL;
499 ctx->ops = &schema_ops;