4 Copyright (C) Simo Sorce 2006
5 Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005-2007
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 3 of the License, or
10 (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program. If not, see <http://www.gnu.org/licenses/>.
24 * Component: objectClass sorting module
26 * Description: sort the objectClass attribute into the class hierarchy
28 * Author: Andrew Bartlett
33 #include "ldb/include/ldb.h"
34 #include "ldb/include/ldb_errors.h"
35 #include "ldb/include/ldb_private.h"
36 #include "dsdb/samdb/samdb.h"
37 #include "lib/util/dlinklist.h"
38 #include "librpc/ndr/libndr.h"
39 #include "librpc/gen_ndr/ndr_security.h"
40 #include "libcli/security/security.h"
41 #include "auth/auth.h"
45 enum oc_step {OC_DO_REQ, OC_SEARCH_SELF, OC_DO_MOD} step;
47 struct ldb_module *module;
48 struct ldb_request *orig_req;
50 struct ldb_request *down_req;
52 struct ldb_request *search_req;
53 struct ldb_reply *search_res;
55 struct ldb_request *mod_req;
59 struct class_list *prev, *next;
60 const char *objectclass;
63 static struct ldb_handle *oc_init_handle(struct ldb_request *req, struct ldb_module *module)
65 struct oc_context *ac;
68 h = talloc_zero(req, struct ldb_handle);
70 ldb_set_errstring(module->ldb, "Out of Memory");
76 ac = talloc_zero(h, struct oc_context);
78 ldb_set_errstring(module->ldb, "Out of Memory");
83 h->private_data = (void *)ac;
85 h->state = LDB_ASYNC_INIT;
86 h->status = LDB_SUCCESS;
94 static int objectclass_sort(struct ldb_module *module,
96 struct ldb_message_element *objectclass_element,
97 struct class_list **sorted_out)
101 const struct dsdb_schema *schema = dsdb_get_schema(module->ldb);
102 struct class_list *sorted = NULL, *parent_class = NULL,
103 *subclass = NULL, *unsorted = NULL, *current, *poss_subclass;
106 * We work on 4 different 'bins' (implemented here as linked lists):
108 * * sorted: the eventual list, in the order we wish to push
109 * into the database. This is the only ordered list.
111 * * parent_class: The current parent class 'bin' we are
112 * trying to find subclasses for
114 * * subclass: The subclasses we have found so far
116 * * unsorted: The remaining objectClasses
118 * The process is a matter of filtering objectClasses up from
119 * unsorted into sorted. Order is irrelevent in the later 3 'bins'.
121 * We start with 'top' (found and promoted to parent_class
122 * initially). Then we find (in unsorted) all the direct
123 * subclasses of 'top'. parent_classes is concatenated onto
124 * the end of 'sorted', and subclass becomes the list in
127 * We then repeat, until we find no more subclasses. Any left
128 * over classes are added to the end.
132 /* Firstly, dump all the objectClass elements into the
133 * unsorted bin, except for 'top', which is special */
134 for (i=0; i < objectclass_element->num_values; i++) {
135 current = talloc(mem_ctx, struct class_list);
137 ldb_set_errstring(module->ldb, "objectclass: out of memory allocating objectclass list");
138 talloc_free(mem_ctx);
139 return LDB_ERR_OPERATIONS_ERROR;
141 current->objectclass = (const char *)objectclass_element->values[i].data;
143 /* this is the root of the tree. We will start
144 * looking for subclasses from here */
145 if (ldb_attr_cmp("top", current->objectclass) == 0) {
146 DLIST_ADD_END(parent_class, current, struct class_list *);
148 DLIST_ADD_END(unsorted, current, struct class_list *);
152 /* DEBUGGING aid: how many layers are we down now? */
156 /* Find all the subclasses of classes in the
157 * parent_classes. Push them onto the subclass list */
159 /* Ensure we don't bother if there are no unsorted entries left */
160 for (current = parent_class; schema && unsorted && current; current = current->next) {
161 /* Walk the list of possible subclasses in unsorted */
162 for (poss_subclass = unsorted; poss_subclass; ) {
163 const struct dsdb_class *class = dsdb_class_by_lDAPDisplayName(schema, poss_subclass->objectclass);
164 struct class_list *next;
166 /* Save the next pointer, as the DLIST_ macros will change poss_subclass->next */
167 next = poss_subclass->next;
169 if (class && ldb_attr_cmp(class->subClassOf, current->objectclass) == 0) {
170 DLIST_REMOVE(unsorted, poss_subclass);
171 DLIST_ADD(subclass, poss_subclass);
175 poss_subclass = next;
179 /* Now push the parent_classes as sorted, we are done with
180 these. Add to the END of the list by concatenation */
181 DLIST_CONCATENATE(sorted, parent_class, struct class_list *);
183 /* and now find subclasses of these */
184 parent_class = subclass;
187 /* If we didn't find any subclasses we will fall out
189 } while (parent_class);
191 /* This shouldn't happen, and would break MMC, but we can't
192 * afford to loose objectClasses. Perhaps there was no 'top',
193 * or some other schema error?
195 * Detecting schema errors is the job of the schema module, so
196 * at this layer we just try not to loose data
198 DLIST_CONCATENATE(sorted, unsorted, struct class_list *);
200 *sorted_out = sorted;
204 DATA_BLOB *get_sd(struct ldb_module *module, TALLOC_CTX *mem_ctx,
205 const struct dsdb_class *objectclass)
208 DATA_BLOB *linear_sd;
209 struct auth_session_info *session_info
210 = ldb_get_opaque(module->ldb, "sessionInfo");
211 struct security_descriptor *sd = sddl_decode(mem_ctx,
212 objectclass->defaultSecurityDescriptor,
213 samdb_domain_sid(module->ldb));
214 if (!session_info || !session_info->security_token) {
218 sd->owner_sid = session_info->security_token->user_sid;
219 sd->group_sid = session_info->security_token->group_sid;
221 linear_sd = talloc(mem_ctx, DATA_BLOB);
226 status = ndr_push_struct_blob(linear_sd, mem_ctx, sd,
227 (ndr_push_flags_fn_t)ndr_push_security_descriptor);
229 if (!NT_STATUS_IS_OK(status)) {
237 static int objectclass_add(struct ldb_module *module, struct ldb_request *req)
239 struct ldb_message_element *objectclass_element;
240 const struct dsdb_schema *schema = dsdb_get_schema(module->ldb);
241 struct class_list *sorted, *current;
242 struct ldb_request *down_req;
243 struct ldb_message *msg;
247 ldb_debug(module->ldb, LDB_DEBUG_TRACE, "objectclass_add\n");
249 if (ldb_dn_is_special(req->op.add.message->dn)) { /* do not manipulate our control entries */
250 return ldb_next_request(module, req);
253 objectclass_element = ldb_msg_find_element(req->op.add.message, "objectClass");
255 /* If no part of this add has an objectClass, then we don't
256 * need to make any changes. cn=rootdse doesn't have an objectClass */
257 if (!objectclass_element) {
258 return ldb_next_request(module, req);
261 mem_ctx = talloc_new(req);
262 if (mem_ctx == NULL) {
263 return LDB_ERR_OPERATIONS_ERROR;
266 ret = objectclass_sort(module, mem_ctx, objectclass_element, &sorted);
267 if (ret != LDB_SUCCESS) {
271 /* prepare the first operation */
272 down_req = talloc(req, struct ldb_request);
273 if (down_req == NULL) {
274 ldb_set_errstring(module->ldb, "Out of memory!");
275 talloc_free(mem_ctx);
276 return LDB_ERR_OPERATIONS_ERROR;
279 *down_req = *req; /* copy the request */
281 down_req->op.add.message = msg = ldb_msg_copy_shallow(down_req, req->op.add.message);
283 if (down_req->op.add.message == NULL) {
284 talloc_free(mem_ctx);
285 return LDB_ERR_OPERATIONS_ERROR;
288 ldb_msg_remove_attr(msg, "objectClass");
289 ret = ldb_msg_add_empty(msg, "objectClass", 0, NULL);
291 if (ret != LDB_SUCCESS) {
292 talloc_free(mem_ctx);
296 /* We must completely replace the existing objectClass entry,
297 * because we need it sorted */
299 /* Move from the linked list back into an ldb msg */
300 for (current = sorted; current; current = current->next) {
301 ret = ldb_msg_add_string(msg, "objectClass", current->objectclass);
302 if (ret != LDB_SUCCESS) {
303 ldb_set_errstring(module->ldb, "objectclass: could not re-add sorted objectclass to modify msg");
304 talloc_free(mem_ctx);
307 /* Last one is the critical one */
308 if (schema && !current->next) {
309 const struct dsdb_class *objectclass
310 = dsdb_class_by_lDAPDisplayName(schema, current->objectclass);
312 if (!ldb_msg_find_element(msg, "objectCategory")) {
313 ldb_msg_add_string(msg, "objectCategory", objectclass->defaultObjectCategory);
315 if (!ldb_msg_find_element(msg, "ntSecurityDescriptor")) {
316 DATA_BLOB *sd = get_sd(module, mem_ctx, objectclass);
317 ldb_msg_add_steal_value(msg, "ntSecurityDescriptor", sd);
323 talloc_free(mem_ctx);
324 ret = ldb_msg_sanity_check(module->ldb, msg);
326 if (ret != LDB_SUCCESS) {
330 /* go on with the call chain */
331 ret = ldb_next_request(module, down_req);
333 /* do not free down_req as the call results may be linked to it,
334 * it will be freed when the upper level request get freed */
335 if (ret == LDB_SUCCESS) {
336 req->handle = down_req->handle;
341 static int objectclass_modify(struct ldb_module *module, struct ldb_request *req)
343 struct ldb_message_element *objectclass_element;
344 struct ldb_message *msg;
345 ldb_debug(module->ldb, LDB_DEBUG_TRACE, "objectclass_modify\n");
347 if (ldb_dn_is_special(req->op.mod.message->dn)) { /* do not manipulate our control entries */
348 return ldb_next_request(module, req);
351 objectclass_element = ldb_msg_find_element(req->op.mod.message, "objectClass");
353 /* If no part of this touches the objectClass, then we don't
354 * need to make any changes. */
355 /* If the only operation is the deletion of the objectClass then go on */
356 if (!objectclass_element) {
357 return ldb_next_request(module, req);
360 switch (objectclass_element->flags & LDB_FLAG_MOD_MASK) {
361 case LDB_FLAG_MOD_DELETE:
362 /* Delete everything? Probably totally illigal, but hey! */
363 if (objectclass_element->num_values == 0) {
364 return ldb_next_request(module, req);
367 case LDB_FLAG_MOD_REPLACE:
369 struct ldb_request *down_req;
370 struct class_list *sorted, *current;
373 mem_ctx = talloc_new(req);
374 if (mem_ctx == NULL) {
375 return LDB_ERR_OPERATIONS_ERROR;
378 /* prepare the first operation */
379 down_req = talloc(req, struct ldb_request);
380 if (down_req == NULL) {
381 ldb_set_errstring(module->ldb, "Out of memory!");
382 talloc_free(mem_ctx);
383 return LDB_ERR_OPERATIONS_ERROR;
386 *down_req = *req; /* copy the request */
388 down_req->op.mod.message = msg = ldb_msg_copy_shallow(down_req, req->op.mod.message);
390 if (down_req->op.add.message == NULL) {
391 talloc_free(mem_ctx);
392 return LDB_ERR_OPERATIONS_ERROR;
395 ret = objectclass_sort(module, mem_ctx, objectclass_element, &sorted);
396 if (ret != LDB_SUCCESS) {
400 /* We must completely replace the existing objectClass entry,
401 * because we need it sorted */
403 ldb_msg_remove_attr(msg, "objectClass");
404 ret = ldb_msg_add_empty(msg, "objectClass", LDB_FLAG_MOD_REPLACE, NULL);
406 if (ret != LDB_SUCCESS) {
407 talloc_free(mem_ctx);
411 /* Move from the linked list back into an ldb msg */
412 for (current = sorted; current; current = current->next) {
413 ret = ldb_msg_add_string(msg, "objectClass", current->objectclass);
414 if (ret != LDB_SUCCESS) {
415 ldb_set_errstring(module->ldb, "objectclass: could not re-add sorted objectclass to modify msg");
416 talloc_free(mem_ctx);
421 talloc_free(mem_ctx);
423 ret = ldb_msg_sanity_check(module->ldb, msg);
424 if (ret != LDB_SUCCESS) {
425 talloc_free(mem_ctx);
429 /* go on with the call chain */
430 ret = ldb_next_request(module, down_req);
432 /* do not free down_req as the call results may be linked to it,
433 * it will be freed when the upper level request get freed */
434 if (ret == LDB_SUCCESS) {
435 req->handle = down_req->handle;
442 struct ldb_handle *h;
443 struct oc_context *ac;
445 h = oc_init_handle(req, module);
447 return LDB_ERR_OPERATIONS_ERROR;
449 ac = talloc_get_type(h->private_data, struct oc_context);
451 /* return or own handle to deal with this call */
454 /* prepare the first operation */
455 ac->down_req = talloc(ac, struct ldb_request);
456 if (ac->down_req == NULL) {
457 ldb_set_errstring(module->ldb, "Out of memory!");
458 return LDB_ERR_OPERATIONS_ERROR;
461 *(ac->down_req) = *req; /* copy the request */
463 ac->down_req->context = NULL;
464 ac->down_req->callback = NULL;
465 ldb_set_timeout_from_prev_req(module->ldb, req, ac->down_req);
467 ac->step = OC_DO_REQ;
469 return ldb_next_request(module, ac->down_req);
473 static int get_self_callback(struct ldb_context *ldb, void *context, struct ldb_reply *ares)
475 struct oc_context *ac;
477 if (!context || !ares) {
478 ldb_set_errstring(ldb, "NULL Context or Result in callback");
479 return LDB_ERR_OPERATIONS_ERROR;
482 ac = talloc_get_type(context, struct oc_context);
484 /* we are interested only in the single reply (base search) we receive here */
485 if (ares->type == LDB_REPLY_ENTRY) {
486 if (ac->search_res != NULL) {
487 ldb_set_errstring(ldb, "Too many results");
489 return LDB_ERR_OPERATIONS_ERROR;
492 ac->search_res = talloc_move(ac, &ares);
500 static int objectclass_search_self(struct ldb_handle *h) {
502 struct oc_context *ac;
503 static const char * const attrs[] = { "objectClass", NULL };
505 ac = talloc_get_type(h->private_data, struct oc_context);
507 /* prepare the search operation */
508 ac->search_req = talloc_zero(ac, struct ldb_request);
509 if (ac->search_req == NULL) {
510 ldb_debug(ac->module->ldb, LDB_DEBUG_ERROR, "Out of Memory!\n");
511 return LDB_ERR_OPERATIONS_ERROR;
514 ac->search_req->operation = LDB_SEARCH;
515 ac->search_req->op.search.base = ac->orig_req->op.mod.message->dn;
516 ac->search_req->op.search.scope = LDB_SCOPE_BASE;
517 ac->search_req->op.search.tree = ldb_parse_tree(ac->search_req, NULL);
518 if (ac->search_req->op.search.tree == NULL) {
519 ldb_set_errstring(ac->module->ldb, "objectclass: Internal error producing null search");
520 return LDB_ERR_OPERATIONS_ERROR;
522 ac->search_req->op.search.attrs = attrs;
523 ac->search_req->controls = NULL;
524 ac->search_req->context = ac;
525 ac->search_req->callback = get_self_callback;
526 ldb_set_timeout_from_prev_req(ac->module->ldb, ac->orig_req, ac->search_req);
528 ac->step = OC_SEARCH_SELF;
530 return ldb_next_request(ac->module, ac->search_req);
533 static int objectclass_do_mod(struct ldb_handle *h) {
535 struct oc_context *ac;
536 struct ldb_message_element *objectclass_element;
537 struct ldb_message *msg;
539 struct class_list *sorted, *current;
542 ac = talloc_get_type(h->private_data, struct oc_context);
544 mem_ctx = talloc_new(ac);
545 if (mem_ctx == NULL) {
546 return LDB_ERR_OPERATIONS_ERROR;
549 ac->mod_req = talloc(ac, struct ldb_request);
550 if (ac->mod_req == NULL) {
551 talloc_free(mem_ctx);
552 return LDB_ERR_OPERATIONS_ERROR;
555 ac->mod_req->operation = LDB_MODIFY;
556 ac->mod_req->controls = NULL;
557 ac->mod_req->context = ac;
558 ac->mod_req->callback = NULL;
559 ldb_set_timeout_from_prev_req(ac->module->ldb, ac->orig_req, ac->mod_req);
561 /* use a new message structure */
562 ac->mod_req->op.mod.message = msg = ldb_msg_new(ac->mod_req);
564 ldb_set_errstring(ac->module->ldb, "objectclass: could not create new modify msg");
565 talloc_free(mem_ctx);
566 return LDB_ERR_OPERATIONS_ERROR;
569 /* This is now the objectClass list from the database */
570 objectclass_element = ldb_msg_find_element(ac->search_res->message,
572 if (!objectclass_element) {
573 /* Where did it go? Move along now, nothing to see here */
574 talloc_free(mem_ctx);
579 msg->dn = ac->orig_req->op.mod.message->dn;
581 ret = objectclass_sort(ac->module, mem_ctx, objectclass_element, &sorted);
582 if (ret != LDB_SUCCESS) {
586 /* We must completely replace the existing objectClass entry.
587 * We could do a constrained add/del, but we are meant to be
588 * in a transaction... */
590 ret = ldb_msg_add_empty(msg, "objectClass", LDB_FLAG_MOD_REPLACE, NULL);
591 if (ret != LDB_SUCCESS) {
592 ldb_set_errstring(ac->module->ldb, "objectclass: could not clear objectclass in modify msg");
593 talloc_free(mem_ctx);
597 /* Move from the linked list back into an ldb msg */
598 for (current = sorted; current; current = current->next) {
599 ret = ldb_msg_add_string(msg, "objectClass", current->objectclass);
600 if (ret != LDB_SUCCESS) {
601 ldb_set_errstring(ac->module->ldb, "objectclass: could not re-add sorted objectclass to modify msg");
602 talloc_free(mem_ctx);
607 ret = ldb_msg_sanity_check(ac->module->ldb, msg);
608 if (ret != LDB_SUCCESS) {
609 talloc_free(mem_ctx);
614 h->state = LDB_ASYNC_INIT;
615 h->status = LDB_SUCCESS;
617 ac->step = OC_DO_MOD;
619 talloc_free(mem_ctx);
620 /* perform the search */
621 return ldb_next_request(ac->module, ac->mod_req);
624 static int oc_wait(struct ldb_handle *handle) {
625 struct oc_context *ac;
628 if (!handle || !handle->private_data) {
629 return LDB_ERR_OPERATIONS_ERROR;
632 if (handle->state == LDB_ASYNC_DONE) {
633 return handle->status;
636 handle->state = LDB_ASYNC_PENDING;
637 handle->status = LDB_SUCCESS;
639 ac = talloc_get_type(handle->private_data, struct oc_context);
643 ret = ldb_wait(ac->down_req->handle, LDB_WAIT_NONE);
645 if (ret != LDB_SUCCESS) {
646 handle->status = ret;
649 if (ac->down_req->handle->status != LDB_SUCCESS) {
650 handle->status = ac->down_req->handle->status;
654 if (ac->down_req->handle->state != LDB_ASYNC_DONE) {
658 /* mods done, go on */
659 return objectclass_search_self(handle);
662 ret = ldb_wait(ac->search_req->handle, LDB_WAIT_NONE);
664 if (ret != LDB_SUCCESS) {
665 handle->status = ret;
668 if (ac->search_req->handle->status != LDB_SUCCESS) {
669 handle->status = ac->search_req->handle->status;
673 if (ac->search_req->handle->state != LDB_ASYNC_DONE) {
677 /* self search done, go on */
678 return objectclass_do_mod(handle);
681 ret = ldb_wait(ac->mod_req->handle, LDB_WAIT_NONE);
683 if (ret != LDB_SUCCESS) {
684 handle->status = ret;
687 if (ac->mod_req->handle->status != LDB_SUCCESS) {
688 handle->status = ac->mod_req->handle->status;
692 if (ac->mod_req->handle->state != LDB_ASYNC_DONE) {
699 ret = LDB_ERR_OPERATIONS_ERROR;
706 handle->state = LDB_ASYNC_DONE;
710 static int oc_wait_all(struct ldb_handle *handle) {
714 while (handle->state != LDB_ASYNC_DONE) {
715 ret = oc_wait(handle);
716 if (ret != LDB_SUCCESS) {
721 return handle->status;
724 static int objectclass_wait(struct ldb_handle *handle, enum ldb_wait_type type)
726 if (type == LDB_WAIT_ALL) {
727 return oc_wait_all(handle);
729 return oc_wait(handle);
733 static const struct ldb_module_ops objectclass_ops = {
734 .name = "objectclass",
735 .add = objectclass_add,
736 .modify = objectclass_modify,
737 .wait = objectclass_wait
740 int ldb_objectclass_init(void)
742 return ldb_register_module(&objectclass_ops);