r17860: Let's commit the work down up to now on the new schema module.
[Samba.git] / source4 / dsdb / samdb / ldb_modules / schema.c
blob21a6527e10a015558af0803f5a9a0e4ddf4eda56
1 /*
2 ldb database library
4 Copyright (C) Simo Sorce 2004-2006
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22 * Name: ldb
24 * Component: ldb schema module
26 * Description: add schema check functionality
28 * Author: Simo Sorce
30 * License: GNU GPL v2 or Later
33 #include "includes.h"
34 #include "libcli/ldap/ldap.h"
35 #include "ldb/include/ldb_errors.h"
36 #include "ldb/include/ldb_private.h"
37 #include "include/dlinklist.h"
39 /* Syntax-Table
41 see ldap_server/devdocs/AD-syntaxes.txt
44 enum schema_int_attr_id {
45 SCHEMA_AS_BOOLEAN,
46 SCHEMA_AS_INTEGER,
47 SCHEMA_AS_OCTET_STRING,
48 SCHEMA_AS_SID,
49 SCHEMA_AS_OID,
50 SCHEMA_AS_ENUMERATION,
51 SCHEMA_AS_NUMERIC_STRING,
52 SCHEMA_AS_PRINTABLE_STRING,
53 SCHEMA_AS_CASE_IGNORE_STRING,
54 SCHEMA_AS_IA5_STRING,
55 SCHEMA_AS_UTC_TIME,
56 SCHEMA_AS_GENERALIZED_TIME,
57 SCHEMA_AS_CASE_SENSITIVE_STRING,
58 SCHEMA_AS_DIRECTORY_STRING,
59 SCHEMA_AS_LARGE_INTEGER,
60 SCHEMA_AS_OBJECT_SECURITY_DESCRIPTOR,
61 SCHEMA_AS_DN,
62 SCHEMA_AS_DN_BINARY,
63 SCHEMA_AS_OR_NAME,
64 SCHEMA_AS_REPLICA_LINK,
65 SCHEMA_AS_PRESENTATION_ADDRESS,
66 SCHEMA_AS_ACCESS_POINT,
67 SCHEMA_AS_DN_STRING
70 enum schema_class_type {
71 SCHEMA_CT_88 = 0,
72 SCHEMA_CT_STRUCTURAL = 1,
73 SCHEMA_CT_ABSTRACT = 2,
74 SCHEMA_CT_AUXILIARY = 3
77 struct schema_attribute {
78 char *OID; /* attributeID */
79 char *name; /* lDAPDisplayName */
80 enum schema_int_attr_id syntax; /* generated from attributeSyntax, oMSyntax, oMObjectClass */
81 bool single; /* isSingleValued */
82 int min; /* rangeLower */
83 int max; /* rangeUpper */
86 struct schema_class {
87 char *OID; /* governsID */
88 char *name; /* lDAPDisplayName */
89 enum schema_class_type type; /* objectClassCategory */
90 bool systemOnly; /* systemOnly */
91 struct schema_class *parent; /* subClassOf */
92 struct schema_class **sysaux; /* systemAuxiliaryClass */
93 struct schema_class **aux; /* auxiliaryClass */
94 struct schema_class **sysposssup; /* systemPossSuperiors */
95 struct schema_class **posssup; /* possSuperiors */
96 struct schema_class **possinf; /* possibleInferiors */
97 struct schema_attribute **sysmust; /* systemMustContain */
98 struct schema_attribute **must; /* MustContain */
99 struct schema_attribute **sysmay; /* systemMayContain */
100 struct schema_attribute **may; /* MayContain */
103 /* TODO: ditcontentrules */
105 struct schema_private_data {
106 struct ldb_dn *schema_dn;
107 struct schema_attribute **attrs;
108 struct schema_store *attrs_store;
109 int num_attributes;
110 struct schema_class **class;
111 struct schema_store *class_store;
112 int num_classes;
115 struct schema_class_dlist {
116 struct schema_class *class;
117 struct schema_class_dlist *prev;
118 struct schema_class_dlist *next;
119 enum schema_class_type role;
122 struct schema_context {
124 enum sc_op { SC_ADD, SC_MOD, SC_DEL, SC_RENAME } op;
125 enum sc_step { SC_INIT, SC_ADD_CHECK_PARENT, SC_ADD_TEMP, SC_DEL_CHECK_CHILDREN } step;
127 struct schema_private_data *data;
129 struct ldb_module *module;
130 struct ldb_request *orig_req;
131 struct ldb_request *down_req;
133 struct ldb_request *parent_req;
134 struct ldb_reply *parent_res;
136 struct schema_class_dlist *class_list;
137 struct schema_class **sup_list;
138 struct schema_class **aux_list;
141 /* FIXME: I'd really like to use an hash table here */
142 struct schema_link {
143 const char *name;
144 void *object;
147 struct schema_store {
148 struct schema_link *store;
149 int num_links;
152 static struct schema_store *schema_store_new(TALLOC_CTX *mem_ctx)
154 struct schema_store *ht;
156 ht = talloc(mem_ctx, struct schema_store);
157 if (!ht) return NULL;
159 ht->store = NULL;
160 ht->num_links = 0;
162 return ht;
165 static int schema_store_add(struct schema_store *ht, const char *key, void *object)
167 ht->store = talloc_realloc(ht, ht->store, struct schema_link, ht->num_links + 1);
168 if (!ht->store) return LDB_ERR_OPERATIONS_ERROR;
170 ht->store[ht->num_links].name = key;
171 ht->store[ht->num_links].object = object;
173 ht->num_links++;
175 return LDB_SUCCESS;
178 static void *schema_store_find(struct schema_store *ht, const char *key)
180 int i;
182 for (i = 0; i < ht->num_links; i++) {
183 if (strcasecmp(ht->store[i].name, key) == 0) {
184 return ht->store[i].object;
188 return NULL;
191 #define SCHEMA_CHECK_VALUE(mem, val, mod) \
192 do { if (mem == val) { \
193 ret = LDB_ERR_OPERATIONS_ERROR; \
194 ldb_asprintf_errstring(mod->ldb, \
195 "schema module: Memory allocation or attribute error on %s", #mem); \
196 goto done; } } while(0)
198 struct schema_class **schema_get_class_list(struct ldb_module *module,
199 struct schema_private_data *data,
200 struct ldb_message_element *el)
202 struct schema_class **list;
203 int i;
205 list = talloc_array(data, struct schema_class *, el->num_values + 1);
206 if (!list) {
207 ldb_debug(module->ldb, LDB_DEBUG_ERROR, "Out of Memory");
208 return NULL;
211 for (i = 0; i < el->num_values; i++) {
212 list[i] = (struct schema_class *)schema_store_find(data->class_store,
213 (char *)el->values[i].data);
214 if (!list[i]) {
215 ldb_debug_set(module->ldb,
216 LDB_DEBUG_ERROR,
217 "Class %s referenced but not found in schema\n",
218 (char *)el->values[i].data);
219 return NULL;
222 list[i] = NULL;
224 return list;
227 struct schema_attribute **schema_get_attrs_list(struct ldb_module *module,
228 struct schema_private_data *data,
229 struct ldb_message_element *el)
231 struct schema_attribute **list;
232 int i;
234 list = talloc_array(data, struct schema_attribute *, el->num_values + 1);
235 if (!list) {
236 ldb_debug(module->ldb, LDB_DEBUG_ERROR, "Out of Memory");
237 return NULL;
240 for (i = 0; i < el->num_values; i++) {
241 list[i] = (struct schema_attribute *)schema_store_find(data->attrs_store,
242 (char *)el->values[i].data);
243 if (!list[i]) {
244 ldb_debug_set(module->ldb,
245 LDB_DEBUG_ERROR,
246 "Attriobute %s referenced but not found in schema\n",
247 (char *)el->values[i].data);
248 return NULL;
251 list[i] = NULL;
253 return list;
256 static int map_schema_syntax(uint32_t om_syntax, const char *attr_syntax, const struct ldb_val *om_class, enum schema_int_attr_id *syntax)
258 int ret;
260 ret = LDB_SUCCESS;
262 switch(om_syntax) {
263 case 1:
264 *syntax = SCHEMA_AS_BOOLEAN;
265 break;
266 case 2:
267 *syntax = SCHEMA_AS_INTEGER;
268 break;
269 case 4:
270 if (strcmp(attr_syntax, "2.5.5.10") == 0) {
271 *syntax = SCHEMA_AS_OCTET_STRING;
272 break;
274 if (strcmp(attr_syntax, "2.5.5.17") == 0) {
275 *syntax = SCHEMA_AS_SID;
276 break;
278 ret = LDB_ERR_OPERATIONS_ERROR;
279 break;
280 case 6:
281 *syntax = SCHEMA_AS_OID;
282 break;
283 case 10:
284 *syntax = SCHEMA_AS_ENUMERATION;
285 break;
286 case 18:
287 *syntax = SCHEMA_AS_NUMERIC_STRING;
288 break;
289 case 19:
290 *syntax = SCHEMA_AS_PRINTABLE_STRING;
291 break;
292 case 20:
293 *syntax = SCHEMA_AS_CASE_IGNORE_STRING;
294 break;
295 case 22:
296 *syntax = SCHEMA_AS_IA5_STRING;
297 break;
298 case 23:
299 *syntax = SCHEMA_AS_UTC_TIME;
300 break;
301 case 24:
302 *syntax = SCHEMA_AS_GENERALIZED_TIME;
303 break;
304 case 27:
305 *syntax = SCHEMA_AS_CASE_SENSITIVE_STRING;
306 break;
307 case 64:
308 *syntax = SCHEMA_AS_DIRECTORY_STRING;
309 break;
310 case 65:
311 *syntax = SCHEMA_AS_LARGE_INTEGER;
312 break;
313 case 66:
314 *syntax = SCHEMA_AS_OBJECT_SECURITY_DESCRIPTOR;
315 break;
316 case 127:
317 if (!om_class) {
318 ret = LDB_ERR_OPERATIONS_ERROR;
319 break;
322 if (memcmp(om_class->data, "\x2b\x0c\x02\x87\x73\x1c\x00\x85\x4a\x00", MIN(om_class->length, 10)) == 0) {
323 *syntax = SCHEMA_AS_DN;
324 break;
326 if (memcmp(om_class->data, "\x2a\x86\x48\x86\xf7\x14\x01\x01\x01\x0b", MIN(om_class->length, 10)) == 0) {
327 *syntax = SCHEMA_AS_DN_BINARY;
328 break;
330 if (memcmp(om_class->data, "\x56\x06\x01\x02\x05\x0b\x1d\x00\x00\x00", MIN(om_class->length, 10)) == 0) {
331 *syntax = SCHEMA_AS_OR_NAME;
332 break;
334 if (memcmp(om_class->data, "\x2a\x86\x48\x86\xf7\x14\x01\x01\x01\x06", MIN(om_class->length, 10)) == 0) {
335 *syntax = SCHEMA_AS_REPLICA_LINK;
336 break;
338 if (memcmp(om_class->data, "\x2b\x0c\x02\x87\x73\x1c\x00\x85\x5c\x00", MIN(om_class->length, 10)) == 0) {
339 *syntax = SCHEMA_AS_PRESENTATION_ADDRESS;
340 break;
342 if (memcmp(om_class->data, "\x2b\x0c\x02\x87\x73\x1c\x00\x85\x3e\x00", MIN(om_class->length, 10)) == 0) {
343 *syntax = SCHEMA_AS_ACCESS_POINT;
344 break;
346 if (memcmp(om_class->data, "\x2a\x86\x48\x86\xf7\x14\x01\x01\x01\x0c", MIN(om_class->length, 10)) == 0) {
347 *syntax = SCHEMA_AS_DN_STRING;
348 break;
350 /* not found will error in default: */
351 default:
352 ret = LDB_ERR_OPERATIONS_ERROR;
355 return ret;
358 static int schema_init_attrs(struct ldb_module *module, struct schema_private_data *data)
360 static const char *schema_attrs[] = { "attributeID",
361 "lDAPDisplayName",
362 "attributeSyntax",
363 "oMSyntax",
364 "oMObjectClass",
365 "isSingleValued",
366 "rangeLower",
367 "rangeUpper",
368 NULL };
369 struct ldb_result *res;
370 int ret, i;
372 ret = ldb_search(module->ldb,
373 data->schema_dn,
374 LDB_SCOPE_SUBTREE,
375 "(objectClass=attributeSchema)",
376 schema_attrs,
377 &res);
379 if (ret != LDB_SUCCESS) {
380 goto done;
383 data->num_attributes = res->count;
384 data->attrs = talloc_array(data, struct schema_attribute *, res->count);
385 SCHEMA_CHECK_VALUE(data->attrs, NULL, module);
387 data->attrs_store = schema_store_new(data);
388 SCHEMA_CHECK_VALUE(data->attrs_store, NULL, module);
390 for (i = 0; i < res->count; i++) {
391 const char *tmp_single;
392 const char *attr_syntax;
393 uint32_t om_syntax;
394 const struct ldb_val *om_class;
396 data->attrs[i] = talloc(data->attrs, struct schema_attribute);
397 SCHEMA_CHECK_VALUE(data->attrs[i], NULL, module);
399 data->attrs[i]->OID = talloc_strdup(data->attrs[i],
400 ldb_msg_find_attr_as_string(res->msgs[i], "attributeID", NULL));
401 SCHEMA_CHECK_VALUE(data->attrs[i]->OID, NULL, module);
403 data->attrs[i]->name = talloc_strdup(data->attrs[i],
404 ldb_msg_find_attr_as_string(res->msgs[i], "lDAPDisplayName", NULL));
405 SCHEMA_CHECK_VALUE(data->attrs[i]->name, NULL, module);
407 /* once we have both the OID and the attribute name, add the pointer to the store */
408 schema_store_add(data->attrs_store, data->attrs[i]->OID, data->attrs[i]);
409 schema_store_add(data->attrs_store, data->attrs[i]->name, data->attrs[i]);
411 attr_syntax = ldb_msg_find_attr_as_string(res->msgs[i], "attributeSyntax", NULL);
412 SCHEMA_CHECK_VALUE(attr_syntax, NULL, module);
414 om_syntax = ldb_msg_find_attr_as_uint(res->msgs[i], "oMSyntax", 0);
415 /* 0 is not a valid oMSyntax */
416 SCHEMA_CHECK_VALUE(om_syntax, 0, module);
418 om_class = ldb_msg_find_ldb_val(res->msgs[i], "oMObjectClass");
420 ret = map_schema_syntax(om_syntax, attr_syntax, om_class, &data->attrs[i]->syntax);
421 if (ret != LDB_SUCCESS) {
422 ldb_asprintf_errstring(module->ldb,
423 "schema module: invalid om syntax value on %s",
424 data->attrs[i]->name);
425 goto done;
428 tmp_single = ldb_msg_find_attr_as_string(res->msgs[i], "isSingleValued", NULL);
429 SCHEMA_CHECK_VALUE(tmp_single, NULL, module);
430 if (strcmp(tmp_single, "TRUE") == 0) {
431 data->attrs[i]->single = 1;
432 } else {
433 data->attrs[i]->single = 0;
436 /* rangeLower and rangeUpper are optional */
437 data->attrs[i]->min = ldb_msg_find_attr_as_int(res->msgs[i], "rangeLower", -1);
438 data->attrs[i]->max = ldb_msg_find_attr_as_int(res->msgs[i], "rangeUpper", -1);
441 done:
442 talloc_free(res);
443 return ret;
446 static int schema_init_classes(struct ldb_module *module, struct schema_private_data *data)
448 static const char *schema_attrs[] = { "governsID",
449 "lDAPDisplayName",
450 "objectClassCategory",
451 "systemOnly",
452 "subClassOf",
453 "systemAuxiliaryClass",
454 "auxiliaryClass",
455 "systemPossSuperiors",
456 "possSuperiors",
457 "possibleInferiors",
458 "systemMustContain",
459 "MustContain",
460 "systemMayContain",
461 "MayContain",
462 NULL };
463 struct ldb_result *res;
464 int ret, i;
466 ret = ldb_search(module->ldb,
467 data->schema_dn,
468 LDB_SCOPE_SUBTREE,
469 "(objectClass=classSchema)",
470 schema_attrs,
471 &res);
473 if (ret != LDB_SUCCESS) {
474 goto done;
477 data->num_classes = res->count;
478 data->class = talloc_array(data, struct schema_class *, res->count);
479 SCHEMA_CHECK_VALUE(data->class, NULL, module);
481 data->class_store = schema_store_new(data);
482 SCHEMA_CHECK_VALUE(data->class_store, NULL, module);
484 for (i = 0; i < res->count; i++) {
485 struct ldb_message_element *el;
487 data->class[i] = talloc(data->class, struct schema_class);
488 SCHEMA_CHECK_VALUE(data->class[i], NULL, module);
490 data->class[i]->OID = talloc_strdup(data->class[i],
491 ldb_msg_find_attr_as_string(res->msgs[i], "governsID", NULL));
492 SCHEMA_CHECK_VALUE(data->class[i]->OID, NULL, module);
494 data->class[i]->name = talloc_strdup(data->class[i],
495 ldb_msg_find_attr_as_string(res->msgs[i], "lDAPDisplayName", NULL));
496 SCHEMA_CHECK_VALUE(data->class[i]->name, NULL, module);
498 /* once we have both the OID and the class name, add the pointer to the store */
499 schema_store_add(data->class_store, data->class[i]->OID, data->class[i]);
500 schema_store_add(data->class_store, data->class[i]->name, data->class[i]);
502 data->class[i]->type = ldb_msg_find_attr_as_int(res->msgs[i], "objectClassCategory", -1);
503 /* 0 should not be a valid value, but turn out it is so test with -1 */
504 SCHEMA_CHECK_VALUE(data->class[i]->type, -1, module);
506 /* the following attributes are all optional */
508 data->class[i]->systemOnly = ldb_msg_find_attr_as_bool(res->msgs[i], "systemOnly", False);
510 /* attributes are loaded first, so we can just go an query the attributes repo */
512 el = ldb_msg_find_element(res->msgs[i], "systemMustContain");
513 if (el) {
514 data->class[i]->sysmust = schema_get_attrs_list(module, data, el);
515 SCHEMA_CHECK_VALUE(data->class[i]->sysmust, NULL, module);
518 el = ldb_msg_find_element(res->msgs[i], "MustContain");
519 if (el) {
520 data->class[i]->must = schema_get_attrs_list(module, data, el);
521 SCHEMA_CHECK_VALUE(data->class[i]->must, NULL, module);
524 el = ldb_msg_find_element(res->msgs[i], "systemMayContain");
525 if (el) {
526 data->class[i]->sysmay = schema_get_attrs_list(module, data, el);
527 SCHEMA_CHECK_VALUE(data->class[i]->sysmay, NULL, module);
530 el = ldb_msg_find_element(res->msgs[i], "MayContain");
531 if (el) {
532 data->class[i]->may = schema_get_attrs_list(module, data, el);
533 SCHEMA_CHECK_VALUE(data->class[i]->may, NULL, module);
538 /* subClassOf, systemAuxiliaryClass, auxiliaryClass, systemPossSuperiors
539 * must be filled in a second loop, when all class objects are allocated
540 * or we may not find a class that has not yet been parsed */
541 for (i = 0; i < res->count; i++) {
542 struct ldb_message_element *el;
543 const char *attr;
545 /* this is single valued anyway */
546 attr = ldb_msg_find_attr_as_string(res->msgs[i], "subClassOf", NULL);
547 SCHEMA_CHECK_VALUE(attr, NULL, module);
548 data->class[i]->parent = schema_store_find(data->class_store, attr);
549 SCHEMA_CHECK_VALUE(data->class[i]->parent, NULL, module);
551 /* the following attributes are all optional */
553 data->class[i]->sysaux = NULL;
554 el = ldb_msg_find_element(res->msgs[i], "systemAuxiliaryClass");
555 if (el) {
556 data->class[i]->sysaux = schema_get_class_list(module, data, el);
557 SCHEMA_CHECK_VALUE(data->class[i]->sysaux, NULL, module);
560 data->class[i]->aux = NULL;
561 el = ldb_msg_find_element(res->msgs[i], "auxiliaryClass");
562 if (el) {
563 data->class[i]->aux = schema_get_class_list(module, data, el);
564 SCHEMA_CHECK_VALUE(data->class[i]->aux, NULL, module);
567 data->class[i]->sysposssup = NULL;
568 el = ldb_msg_find_element(res->msgs[i], "systemPossSuperiors");
569 if (el) {
570 data->class[i]->sysposssup = schema_get_class_list(module, data, el);
571 SCHEMA_CHECK_VALUE(data->class[i]->sysposssup, NULL, module);
574 data->class[i]->posssup = NULL;
575 el = ldb_msg_find_element(res->msgs[i], "possSuperiors");
576 if (el) {
577 data->class[i]->posssup = schema_get_class_list(module, data, el);
578 SCHEMA_CHECK_VALUE(data->class[i]->posssup, NULL, module);
581 data->class[i]->possinf = NULL;
582 el = ldb_msg_find_element(res->msgs[i], "possibleInferiors");
583 if (el) {
584 data->class[i]->possinf = schema_get_class_list(module, data, el);
585 SCHEMA_CHECK_VALUE(data->class[i]->possinf, NULL, module);
589 done:
590 talloc_free(res);
591 return ret;
594 static struct ldb_handle *schema_init_handle(struct ldb_request *req, struct ldb_module *module, enum sc_op op)
596 struct schema_context *sctx;
597 struct ldb_handle *h;
599 h = talloc_zero(req, struct ldb_handle);
600 if (h == NULL) {
601 ldb_set_errstring(module->ldb, "Out of Memory");
602 return NULL;
605 h->module = module;
607 sctx = talloc_zero(h, struct schema_context);
608 if (sctx == NULL) {
609 ldb_set_errstring(module->ldb, "Out of Memory");
610 talloc_free(h);
611 return NULL;
614 h->private_data = (void *)sctx;
616 h->state = LDB_ASYNC_INIT;
617 h->status = LDB_SUCCESS;
619 sctx->op = op;
620 sctx->step = SC_INIT;
621 sctx->data = module->private_data;
622 sctx->module = module;
623 sctx->orig_req = req;
625 return h;
628 static int schema_add_check_parent(struct ldb_context *ldb, void *context, struct ldb_reply *ares)
630 struct schema_context *sctx;
632 if (!context || !ares) {
633 ldb_set_errstring(ldb, "NULL Context or Result in callback");
634 return LDB_ERR_OPERATIONS_ERROR;
637 sctx = talloc_get_type(context, struct schema_context);
639 /* we are interested only in the single reply (base search) we receive here */
640 if (ares->type == LDB_REPLY_ENTRY) {
641 if (sctx->parent_res != NULL) {
642 ldb_set_errstring(ldb, "Too many results");
643 talloc_free(ares);
644 return LDB_ERR_OPERATIONS_ERROR;
646 sctx->parent_res = talloc_steal(sctx, ares);
647 } else {
648 talloc_free(ares);
651 return LDB_SUCCESS;
654 static int schema_add_build_parent_req(struct schema_context *sctx)
656 static const char * const parent_attrs[] = { "objectClass", NULL };
657 int ret;
659 sctx->parent_req = talloc_zero(sctx, struct ldb_request);
660 if (sctx->parent_req == NULL) {
661 ldb_debug(sctx->module->ldb, LDB_DEBUG_ERROR, "Out of Memory!\n");
662 return LDB_ERR_OPERATIONS_ERROR;
665 sctx->parent_req->operation = LDB_SEARCH;
666 sctx->parent_req->op.search.scope = LDB_SCOPE_BASE;
667 sctx->parent_req->op.search.base = ldb_dn_get_parent(sctx->parent_req, sctx->orig_req->op.add.message->dn);
668 sctx->parent_req->op.search.tree = ldb_parse_tree(sctx->module->ldb, "(objectClass=*)");
669 sctx->parent_req->op.search.attrs = parent_attrs;
670 sctx->parent_req->controls = NULL;
671 sctx->parent_req->context = sctx;
672 sctx->parent_req->callback = schema_add_check_parent;
673 ret = ldb_set_timeout_from_prev_req(sctx->module->ldb, sctx->orig_req, sctx->parent_req);
675 return ret;
678 static struct schema_class_dlist *schema_add_get_dlist_entry_with_class(struct schema_class_dlist *list, struct schema_class *class)
680 struct schema_class_dlist *temp;
682 for (temp = list; temp && (temp->class != class); temp = temp->next) /* noop */ ;
683 return temp;
686 static int schema_add_class_to_dlist(struct schema_class_dlist *list, struct schema_class *class, enum schema_class_type role)
688 struct schema_class_dlist *entry;
689 struct schema_class_dlist *temp;
690 int ret;
692 /* see if this class already exist in the class list */
693 if (schema_add_get_dlist_entry_with_class(list, class)) {
694 return LDB_SUCCESS;
697 /* this is a new class go on and add to the list */
698 entry = talloc_zero(list, struct schema_class_dlist);
699 if (!entry) return LDB_ERR_OPERATIONS_ERROR;
700 entry->class = class;
701 entry->role = class->type;
703 /* If parent is top (list is guaranteed to start always with top) */
704 if (class->parent == list->class) {
705 /* if the hierarchy role is structural try to add it just after top */
706 if (role == SCHEMA_CT_STRUCTURAL) {
707 /* but check no other class at after top has a structural role */
708 if (list->next && (list->next->role == SCHEMA_CT_STRUCTURAL)) {
709 return LDB_ERR_OBJECT_CLASS_VIOLATION;
711 DLIST_ADD_AFTER(list, entry, list);
712 } else {
713 DLIST_ADD_END(list, entry, struct schema_class_dlist *);
715 return LDB_SUCCESS;
718 /* search if parent has already been added */
719 temp = schema_add_get_dlist_entry_with_class(list->next, class->parent);
720 if (temp == NULL) {
721 ret = schema_add_class_to_dlist(list, class->parent, role);
722 if (ret != LDB_SUCCESS) {
723 return ret;
725 temp = schema_add_get_dlist_entry_with_class(list->next, class->parent);
727 if (!temp) { /* parent not found !? */
728 return LDB_ERR_OPERATIONS_ERROR;
731 DLIST_ADD_AFTER(list, entry, temp);
732 if (role == SCHEMA_CT_STRUCTURAL || role == SCHEMA_CT_AUXILIARY) {
733 temp = entry;
734 do {
735 temp->role = role;
736 temp = temp->prev;
737 /* stop when hierarchy base is met or when base class parent is top */
738 } while (temp->class == temp->next->class->parent &&
739 temp->next->class->parent != list->class);
741 /* if we have not reached the head of the list
742 * and role is structural */
743 if (temp != list && role == SCHEMA_CT_STRUCTURAL) {
744 struct schema_class_dlist *hfirst, *hlast;
746 /* check if the list second entry is structural */
747 if (list->next->role == SCHEMA_CT_STRUCTURAL) {
748 /* we have a confilict here */
749 return LDB_ERR_OBJECT_CLASS_VIOLATION;
751 /* we have to move this hierarchy of classes
752 * so that the base of the structural hierarchy is right after top */
754 hfirst = temp->next;
755 hlast = entry;
756 /* now hfirst - hlast are the boundaries of the structural hierarchy */
758 /* extract the structural hierachy from the list */
759 hfirst->prev->next = hlast->next;
760 if (hlast->next) hlast->next->prev = hfirst->prev;
762 /* insert the structural hierarchy just after top */
763 list->next->prev = hlast;
764 hlast->next = list->next;
765 list->next = hfirst;
766 hfirst->prev = list;
770 return LDB_SUCCESS;
773 /* merge source list into dest list and remove duplicates */
774 static int schema_merge_class_list(TALLOC_CTX *mem_ctx, struct schema_class ***dest, struct schema_class **source)
776 struct schema_class **list = *dest;
777 int i, j, n, f;
779 n = 0;
780 if (list) for (n = 0; list[n]; n++) /* noop */ ;
781 f = n;
783 for (i = 0; source[i]; i++) {
784 for (j = 0; j < f; j++) {
785 if (list[j] == source[i]) {
786 break;
789 if (j < f) { /* duplicate found */
790 continue;
793 list = talloc_realloc(mem_ctx, list, struct schema_class *, n + 2);
794 if (!list) {
795 return LDB_ERR_OPERATIONS_ERROR;
797 list[n] = source[i];
798 n++;
799 list[n] = NULL;
802 *dest = list;
804 return LDB_SUCCESS;
807 /* validate and modify the objectclass attribute to sort and add parents */
808 static int schema_add_build_objectclass_list(struct schema_context *sctx)
810 struct schema_class_dlist *temp;
811 struct ldb_message_element * el;
812 struct schema_class *class;
813 int ret, i, an;
815 /* First of all initialize list, it must start with class top */
816 sctx->class_list = talloc_zero(sctx, struct schema_class_dlist);
817 if (!sctx->class_list) return LDB_ERR_OPERATIONS_ERROR;
819 sctx->class_list->class = schema_store_find(sctx->data->class_store, "top");
820 if (!sctx->class_list->class) return LDB_ERR_OPERATIONS_ERROR;
822 el = ldb_msg_find_element(sctx->orig_req->op.add.message, "objectClass");
823 if (!el) {
824 return LDB_ERR_OBJECT_CLASS_VIOLATION;
827 for (i = 0; i < el->num_values; i++) {
829 class = schema_store_find(sctx->data->class_store, (char *)el->values[i].data);
830 if (!class) {
831 return LDB_ERR_NO_SUCH_OBJECT;
834 ret = schema_add_class_to_dlist(sctx->class_list, class, class->type);
835 if (ret != LDB_SUCCESS) {
836 return ret;
840 /* now check if there is any class role that is still not STRUCTURAL or AUXILIARY */
841 /* build also the auxiliary class list and the possible superiors list */
842 temp = sctx->class_list->next; /* top is special, skip it */
843 an = 0;
845 while (temp) {
846 if (temp->role == SCHEMA_CT_ABSTRACT || temp->role == SCHEMA_CT_88) {
847 return LDB_ERR_OBJECT_CLASS_VIOLATION;
849 if (temp->class->sysaux) {
850 ret = schema_merge_class_list(sctx, &sctx->aux_list, temp->class->sysaux);
851 if (ret != LDB_SUCCESS) {
852 return LDB_ERR_OPERATIONS_ERROR;
855 if (temp->class->aux) {
856 ret = schema_merge_class_list(sctx, &sctx->aux_list, temp->class->aux);
857 if (ret != LDB_SUCCESS) {
858 return LDB_ERR_OPERATIONS_ERROR;
861 if (temp->class->sysposssup) {
862 ret = schema_merge_class_list(sctx, &sctx->sup_list, temp->class->sysposssup);
863 if (ret != LDB_SUCCESS) {
864 return LDB_ERR_OPERATIONS_ERROR;
867 if (temp->class->posssup) {
868 ret = schema_merge_class_list(sctx, &sctx->sup_list, temp->class->posssup);
869 if (ret != LDB_SUCCESS) {
870 return LDB_ERR_OPERATIONS_ERROR;
873 temp = temp->next;
876 /* complete sup_list with material from the aux classes */
877 for (i = 0; sctx->aux_list && sctx->aux_list[i]; i++) {
878 if (sctx->aux_list[i]->sysposssup) {
879 ret = schema_merge_class_list(sctx, &sctx->sup_list, sctx->aux_list[i]->sysposssup);
880 if (ret != LDB_SUCCESS) {
881 return LDB_ERR_OPERATIONS_ERROR;
884 if (sctx->aux_list[i]->posssup) {
885 ret = schema_merge_class_list(sctx, &sctx->sup_list, sctx->aux_list[i]->posssup);
886 if (ret != LDB_SUCCESS) {
887 return LDB_ERR_OPERATIONS_ERROR;
892 if (!sctx->sup_list) return LDB_ERR_NAMING_VIOLATION;
894 return LDB_SUCCESS;
897 static int schema_add_check_container_constraints(struct schema_context *sctx)
899 struct schema_class **parent_possinf = NULL;
900 struct schema_class **parent_classes;
901 struct schema_class_dlist *temp;
902 struct ldb_message_element *el;
903 int i, j, ret;
905 el = ldb_msg_find_element(sctx->parent_res->message, "objectClass");
906 if (!el) {
907 /* what the .. */
908 return LDB_ERR_OPERATIONS_ERROR;
911 parent_classes = talloc_array(sctx, struct schema_class *, el->num_values + 1);
913 for (i = 0; i < el->num_values; i++) {
915 parent_classes[i] = schema_store_find(sctx->data->class_store, (const char *)el->values[i].data);
916 if (!parent_classes[i]) { /* should not be possible */
917 return LDB_ERR_OPERATIONS_ERROR;
920 if (parent_classes[i]->possinf) {
921 ret = schema_merge_class_list(sctx, &parent_possinf, parent_classes[i]->possinf);
922 if (ret != LDB_SUCCESS) {
923 return LDB_ERR_OPERATIONS_ERROR;
927 /* check also embedded auxiliary classes possinf */
928 for (j = 0; parent_classes[i]->sysaux && parent_classes[i]->sysaux[j]; j++) {
929 if (parent_classes[i]->sysaux[j]->possinf) {
930 ret = schema_merge_class_list(sctx, &parent_possinf, parent_classes[i]->sysaux[j]->possinf);
931 if (ret != LDB_SUCCESS) {
932 return LDB_ERR_OPERATIONS_ERROR;
936 for (j = 0; parent_classes[i]->aux && parent_classes[i]->aux[j]; j++) {
937 if (parent_classes[i]->aux[j]->possinf) {
938 ret = schema_merge_class_list(sctx, &parent_possinf, parent_classes[i]->aux[j]->possinf);
939 if (ret != LDB_SUCCESS) {
940 return LDB_ERR_OPERATIONS_ERROR;
946 /* foreach parent objectclass,
947 * check parent possible inferiors match all of the child objectclasses
948 * and that
949 * poss Superiors of the child objectclasses mathes one of the parent classes
952 temp = sctx->class_list->next; /* skip top it is special */
953 while (temp) {
955 for (i = 0; parent_possinf[i]; i++) {
956 if (temp->class == parent_possinf[i]) {
957 break;
960 if (parent_possinf[i] == NULL) {
961 /* class not found in possible inferiors */
962 return LDB_ERR_NAMING_VIOLATION;
965 temp = temp->next;
968 for (i = 0; parent_classes[i]; i++) {
969 for (j = 0; sctx->sup_list[j]; j++) {
970 if (sctx->sup_list[j] == parent_classes[i]) {
971 break;
974 if (sctx->sup_list[j]) { /* possible Superiors match one of the parent classes */
975 return LDB_SUCCESS;
979 /* no parent classes matched superiors */
980 return LDB_ERR_NAMING_VIOLATION;
983 static int schema_add_build_down_req(struct schema_context *sctx)
985 struct schema_class_dlist *temp;
986 struct ldb_message *msg;
987 int ret;
989 sctx->down_req = talloc(sctx, struct ldb_request);
990 if (!sctx->down_req) {
991 ldb_set_errstring(sctx->module->ldb, "Out of memory!");
992 return LDB_ERR_OPERATIONS_ERROR;
995 *(sctx->down_req) = *(sctx->orig_req); /* copy the request */
996 msg = ldb_msg_copy_shallow(sctx->down_req, sctx->orig_req->op.add.message);
997 if (!msg) {
998 ldb_set_errstring(sctx->module->ldb, "Out of memory!");
999 return LDB_ERR_OPERATIONS_ERROR;
1002 ldb_msg_remove_attr(msg, "objectClass");
1003 ret = ldb_msg_add_empty(msg, "objectClass", 0);
1004 if (ret != LDB_SUCCESS) {
1005 return ret;
1008 /* Add the complete list of classes back to the message */
1009 for (temp = sctx->class_list; temp; temp = temp->next) {
1010 ret = ldb_msg_add_string(msg, "objectClass", temp->class->name);
1011 if (ret != LDB_SUCCESS) {
1012 return ret;
1016 sctx->down_req->op.add.message = msg;
1018 return LDB_SUCCESS;
1021 static int schema_add_continue(struct ldb_handle *h)
1023 struct schema_context *sctx;
1024 int ret;
1026 sctx = talloc_get_type(h->private_data, struct schema_context);
1028 switch (sctx->step) {
1029 case SC_INIT:
1031 /* First of all check that a parent exists for this entry */
1032 ret = schema_add_build_parent_req(sctx);
1033 if (ret != LDB_SUCCESS) {
1034 break;
1037 sctx->step = SC_ADD_CHECK_PARENT;
1038 return ldb_next_request(sctx->module, sctx->parent_req);
1040 case SC_ADD_CHECK_PARENT:
1042 /* parent search done, check result and go on */
1043 if (sctx->parent_res == NULL) {
1044 /* we must have a parent */
1045 ret = LDB_ERR_NO_SUCH_OBJECT;
1046 break;
1049 /* Check objectclasses are ok */
1050 ret = schema_add_build_objectclass_list(sctx);
1051 if (ret != LDB_SUCCESS) {
1052 break;
1055 /* check the parent is of the right type for this object */
1056 ret = schema_add_check_container_constraints(sctx);
1057 if (ret != LDB_SUCCESS) {
1058 break;
1061 /* check attributes syntax */
1063 ret = schema_check_attributes_syntax(sctx);
1064 if (ret != LDB_SUCCESS) {
1065 break;
1069 ret = schema_add_build_down_req(sctx);
1070 if (ret != LDB_SUCCESS) {
1071 break;
1073 sctx->step = SC_ADD_TEMP;
1075 return ldb_next_request(sctx->module, sctx->down_req);
1077 default:
1078 ret = LDB_ERR_OPERATIONS_ERROR;
1079 break;
1082 /* this is reached only in case of error */
1083 /* FIXME: fire an async reply ? */
1084 h->status = ret;
1085 h->state = LDB_ASYNC_DONE;
1086 return ret;
1089 static int schema_add(struct ldb_module *module, struct ldb_request *req)
1091 struct schema_context *sctx;
1092 struct ldb_handle *h;
1094 if (ldb_dn_is_special(req->op.add.message->dn)) { /* do not manipulate our control entries */
1095 return ldb_next_request(module, req);
1098 h = schema_init_handle(req, module, SC_ADD);
1099 if (!h) {
1100 return LDB_ERR_OPERATIONS_ERROR;
1103 sctx = talloc_get_type(h->private_data, struct schema_context);
1104 sctx->orig_req->handle = h;
1105 return schema_add_continue(h);
1109 static int schema_modify(struct ldb_module *module, struct ldb_request *req)
1111 struct ldb_handle *h;
1112 struct schema_context *sctx;
1114 if (ldb_dn_is_special(req->op.mod.message->dn)) { /* do not manipulate our control entries */
1115 return ldb_next_request(module, req);
1118 return ldb_next_request(module, req);
1121 static int schema_delete(struct ldb_module *module, struct ldb_request *req)
1123 struct ldb_handle *h;
1124 struct schema_context *sctx;
1126 if (ldb_dn_is_special(req->op.del.dn)) { /* do not manipulate our control entries */
1127 return ldb_next_request(module, req);
1130 /* First of all check no children exists for this entry */
1132 return ldb_next_request(module, req);
1135 static int schema_rename(struct ldb_module *module, struct ldb_request *req)
1137 struct ldb_handle *h;
1138 struct schema_context *sctx;
1140 if (ldb_dn_is_special(req->op.rename.olddn) &&
1141 ldb_dn_is_special(req->op.rename.newdn)) { /* do not manipulate our control entries */
1142 return ldb_next_request(module, req);
1145 return ldb_next_request(module, req);
1148 static int schema_wait_loop(struct ldb_handle *handle) {
1149 struct schema_context *sctx;
1150 int ret;
1152 if (!handle || !handle->private_data) {
1153 return LDB_ERR_OPERATIONS_ERROR;
1156 if (handle->state == LDB_ASYNC_DONE) {
1157 return handle->status;
1160 handle->state = LDB_ASYNC_PENDING;
1161 handle->status = LDB_SUCCESS;
1163 sctx = talloc_get_type(handle->private_data, struct schema_context);
1165 switch (sctx->step) {
1166 case SC_ADD_CHECK_PARENT:
1167 ret = ldb_wait(sctx->parent_req->handle, LDB_WAIT_NONE);
1169 if (ret != LDB_SUCCESS) {
1170 handle->status = ret;
1171 goto done;
1173 if (sctx->parent_req->handle->status != LDB_SUCCESS) {
1174 handle->status = sctx->parent_req->handle->status;
1175 goto done;
1178 if (sctx->parent_req->handle->state != LDB_ASYNC_DONE) {
1179 return LDB_SUCCESS;
1182 return schema_add_continue(handle);
1184 case SC_ADD_TEMP:
1185 ret = ldb_wait(sctx->down_req->handle, LDB_WAIT_NONE);
1187 if (ret != LDB_SUCCESS) {
1188 handle->status = ret;
1189 goto done;
1191 if (sctx->down_req->handle->status != LDB_SUCCESS) {
1192 handle->status = sctx->down_req->handle->status;
1193 goto done;
1196 if (sctx->down_req->handle->state != LDB_ASYNC_DONE) {
1197 return LDB_SUCCESS;
1200 break;
1202 default:
1203 ret = LDB_ERR_OPERATIONS_ERROR;
1204 goto done;
1207 ret = LDB_SUCCESS;
1209 done:
1210 handle->state = LDB_ASYNC_DONE;
1211 return ret;
1214 static int schema_wait_all(struct ldb_handle *handle) {
1216 int ret;
1218 while (handle->state != LDB_ASYNC_DONE) {
1219 ret = schema_wait_loop(handle);
1220 if (ret != LDB_SUCCESS) {
1221 return ret;
1225 return handle->status;
1228 static int schema_wait(struct ldb_handle *handle, enum ldb_wait_type type)
1230 if (type == LDB_WAIT_ALL) {
1231 return schema_wait_all(handle);
1232 } else {
1233 return schema_wait_loop(handle);
1237 static int schema_init(struct ldb_module *module)
1239 static const char *schema_attrs[] = { "schemaNamingContext", NULL };
1240 struct schema_private_data *data;
1241 struct ldb_result *res;
1242 int ret;
1244 /* need to let the partiorion module to register first */
1245 ret = ldb_next_init(module);
1246 if (ret != LDB_SUCCESS) {
1247 return ret;
1250 data = talloc_zero(module, struct schema_private_data);
1251 if (data == NULL) {
1252 return LDB_ERR_OPERATIONS_ERROR;
1255 /* find the schema partition */
1256 ret = ldb_search(module->ldb,
1257 ldb_dn_new(module),
1258 LDB_SCOPE_BASE,
1259 "(objectClass=*)",
1260 schema_attrs,
1261 &res);
1263 if (res->count != 1) {
1264 /* FIXME: return a clear error string */
1265 talloc_free(data);
1266 talloc_free(res);
1267 return LDB_ERR_OPERATIONS_ERROR;
1270 data->schema_dn = ldb_msg_find_attr_as_dn(data, res->msgs[0], "schemaNamingContext");
1271 if (data->schema_dn == NULL) {
1272 /* FIXME: return a clear error string */
1273 talloc_free(data);
1274 talloc_free(res);
1275 return LDB_ERR_OPERATIONS_ERROR;
1278 talloc_free(res);
1280 ret = schema_init_attrs(module, data);
1281 if (ret != LDB_SUCCESS) {
1282 talloc_free(data);
1283 return ret;
1286 ret = schema_init_classes(module, data);
1287 if (ret != LDB_SUCCESS) {
1288 talloc_free(data);
1289 return ret;
1292 module->private_data = data;
1293 return LDB_SUCCESS;
1296 static const struct ldb_module_ops schema_ops = {
1297 .name = "schema",
1298 .init_context = schema_init,
1299 .add = schema_add,
1300 .modify = schema_modify,
1301 .del = schema_delete,
1302 .rename = schema_rename,
1303 .wait = schema_wait
1306 int ldb_schema_init(void)
1308 return ldb_register_module(&schema_ops);