4 Copyright (C) Simo Sorce 2006-2008
5 Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005-2009
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
27 * - sort the objectClass attribute into the class
29 * - fix DNs and attributes into 'standard' case
30 * - Add objectCategory and ntSecurityDescriptor defaults
32 * Author: Andrew Bartlett
37 #include "ldb_module.h"
38 #include "dlinklist.h"
39 #include "dsdb/samdb/samdb.h"
40 #include "librpc/ndr/libndr.h"
41 #include "librpc/gen_ndr/ndr_security.h"
42 #include "libcli/security/security.h"
43 #include "auth/auth.h"
44 #include "param/param.h"
45 #include "../libds/common/flags.h"
50 struct ldb_module *module;
51 struct ldb_request *req;
53 struct ldb_reply *search_res;
55 int (*step_fn)(struct oc_context *);
59 struct class_list *prev, *next;
60 const struct dsdb_class *objectclass;
63 static struct oc_context *oc_init_context(struct ldb_module *module,
64 struct ldb_request *req)
66 struct ldb_context *ldb;
67 struct oc_context *ac;
69 ldb = ldb_module_get_ctx(module);
71 ac = talloc_zero(req, struct oc_context);
73 ldb_set_errstring(ldb, "Out of Memory");
83 static int objectclass_do_add(struct oc_context *ac);
85 /* Sort objectClasses into correct order, and validate that all
86 * objectClasses specified actually exist in the schema
89 static int objectclass_sort(struct ldb_module *module,
90 const struct dsdb_schema *schema,
92 struct ldb_message_element *objectclass_element,
93 struct class_list **sorted_out)
95 struct ldb_context *ldb;
96 unsigned int i, lowest;
97 struct class_list *unsorted = NULL, *sorted = NULL, *current = NULL, *poss_parent = NULL, *new_parent = NULL, *current_lowest = NULL;
99 ldb = ldb_module_get_ctx(module);
103 * We work on 4 different 'bins' (implemented here as linked lists):
105 * * sorted: the eventual list, in the order we wish to push
106 * into the database. This is the only ordered list.
108 * * parent_class: The current parent class 'bin' we are
109 * trying to find subclasses for
111 * * subclass: The subclasses we have found so far
113 * * unsorted: The remaining objectClasses
115 * The process is a matter of filtering objectClasses up from
116 * unsorted into sorted. Order is irrelevent in the later 3 'bins'.
118 * We start with 'top' (found and promoted to parent_class
119 * initially). Then we find (in unsorted) all the direct
120 * subclasses of 'top'. parent_classes is concatenated onto
121 * the end of 'sorted', and subclass becomes the list in
124 * We then repeat, until we find no more subclasses. Any left
125 * over classes are added to the end.
129 /* Firstly, dump all the objectClass elements into the
130 * unsorted bin, except for 'top', which is special */
131 for (i=0; i < objectclass_element->num_values; i++) {
132 current = talloc(mem_ctx, struct class_list);
135 return LDB_ERR_OPERATIONS_ERROR;
137 current->objectclass = dsdb_class_by_lDAPDisplayName_ldb_val(schema, &objectclass_element->values[i]);
138 if (!current->objectclass) {
139 ldb_asprintf_errstring(ldb, "objectclass %.*s is not a valid objectClass in schema",
140 (int)objectclass_element->values[i].length, (const char *)objectclass_element->values[i].data);
141 /* This looks weird, but windows apparently returns this for invalid objectClass values */
142 return LDB_ERR_NO_SUCH_ATTRIBUTE;
143 } else if (current->objectclass->isDefunct) {
144 ldb_asprintf_errstring(ldb, "objectclass %.*s marked as isDefunct objectClass in schema - not valid for new objects",
145 (int)objectclass_element->values[i].length, (const char *)objectclass_element->values[i].data);
146 /* This looks weird, but windows apparently returns this for invalid objectClass values */
147 return LDB_ERR_NO_SUCH_ATTRIBUTE;
150 /* Don't add top to list, we will do that later */
151 if (ldb_attr_cmp("top", current->objectclass->lDAPDisplayName) != 0) {
152 DLIST_ADD_END(unsorted, current, struct class_list *);
156 /* Add top here, to prevent duplicates */
157 current = talloc(mem_ctx, struct class_list);
158 current->objectclass = dsdb_class_by_lDAPDisplayName(schema, "top");
159 DLIST_ADD_END(sorted, current, struct class_list *);
162 /* For each object: find parent chain */
163 for (current = unsorted; schema && current; current = current->next) {
164 for (poss_parent = unsorted; poss_parent; poss_parent = poss_parent->next) {
165 if (ldb_attr_cmp(poss_parent->objectclass->lDAPDisplayName, current->objectclass->subClassOf) == 0) {
169 /* If we didn't get to the end of the list, we need to add this parent */
170 if (poss_parent || (ldb_attr_cmp("top", current->objectclass->subClassOf) == 0)) {
174 new_parent = talloc(mem_ctx, struct class_list);
175 new_parent->objectclass = dsdb_class_by_lDAPDisplayName(schema, current->objectclass->subClassOf);
176 DLIST_ADD_END(unsorted, new_parent, struct class_list *);
182 current_lowest = NULL;
183 for (current = unsorted; schema && current; current = current->next) {
184 if(current->objectclass->subClass_order < lowest) {
185 current_lowest = current;
186 lowest = current->objectclass->subClass_order;
190 if(current_lowest != NULL) {
191 DLIST_REMOVE(unsorted,current_lowest);
192 DLIST_ADD_END(sorted,current_lowest, struct class_list *);
198 *sorted_out = sorted;
203 /* If we don't have schema yet, then just merge the lists again */
204 DLIST_CONCATENATE(sorted, unsorted, struct class_list *);
205 *sorted_out = sorted;
209 /* This shouldn't happen, and would break MMC, perhaps there
210 * was no 'top', a conflict in the objectClasses or some other
213 ldb_asprintf_errstring(ldb, "objectclass %s is not a valid objectClass in objectClass chain", unsorted->objectclass->lDAPDisplayName);
214 return LDB_ERR_OBJECT_CLASS_VIOLATION;
217 static int get_search_callback(struct ldb_request *req, struct ldb_reply *ares)
219 struct ldb_context *ldb;
220 struct oc_context *ac;
223 ac = talloc_get_type(req->context, struct oc_context);
224 ldb = ldb_module_get_ctx(ac->module);
227 return ldb_module_done(ac->req, NULL, NULL,
228 LDB_ERR_OPERATIONS_ERROR);
230 if (ares->error != LDB_SUCCESS &&
231 ares->error != LDB_ERR_NO_SUCH_OBJECT) {
232 return ldb_module_done(ac->req, ares->controls,
233 ares->response, ares->error);
236 ldb_reset_err_string(ldb);
238 switch (ares->type) {
239 case LDB_REPLY_ENTRY:
240 if (ac->search_res != NULL) {
241 ldb_set_errstring(ldb, "Too many results");
243 return ldb_module_done(ac->req, NULL, NULL,
244 LDB_ERR_OPERATIONS_ERROR);
247 ac->search_res = talloc_steal(ac, ares);
250 case LDB_REPLY_REFERRAL:
257 ret = ac->step_fn(ac);
258 if (ret != LDB_SUCCESS) {
259 return ldb_module_done(ac->req, NULL, NULL, ret);
267 static int oc_op_callback(struct ldb_request *req, struct ldb_reply *ares)
269 struct oc_context *ac;
271 ac = talloc_get_type(req->context, struct oc_context);
274 return ldb_module_done(ac->req, NULL, NULL,
275 LDB_ERR_OPERATIONS_ERROR);
277 if (ares->error != LDB_SUCCESS) {
278 return ldb_module_done(ac->req, ares->controls,
279 ares->response, ares->error);
282 if (ares->type != LDB_REPLY_DONE) {
284 return ldb_module_done(ac->req, NULL, NULL,
285 LDB_ERR_OPERATIONS_ERROR);
288 return ldb_module_done(ac->req, ares->controls,
289 ares->response, ares->error);
292 /* Fix up the DN to be in the standard form, taking particular care to match the parent DN
294 This should mean that if the parent is:
295 CN=Users,DC=samba,DC=example,DC=com
296 and a proposed child is
297 cn=Admins ,cn=USERS,dc=Samba,dc=example,dc=COM
299 The resulting DN should be:
301 CN=Admins,CN=Users,DC=samba,DC=example,DC=com
304 static int fix_dn(TALLOC_CTX *mem_ctx,
305 struct ldb_dn *newdn, struct ldb_dn *parent_dn,
306 struct ldb_dn **fixed_dn)
308 char *upper_rdn_attr;
309 const struct ldb_val *rdn_val;
311 /* Fix up the DN to be in the standard form, taking particular care to match the parent DN */
312 *fixed_dn = ldb_dn_copy(mem_ctx, parent_dn);
314 /* We need the attribute name in upper case */
315 upper_rdn_attr = strupper_talloc(*fixed_dn,
316 ldb_dn_get_rdn_name(newdn));
317 if (!upper_rdn_attr) {
318 return LDB_ERR_OPERATIONS_ERROR;
321 /* Create a new child */
322 if (ldb_dn_add_child_fmt(*fixed_dn, "X=X") == false) {
323 return LDB_ERR_OPERATIONS_ERROR;
327 rdn_val = ldb_dn_get_rdn_val(newdn);
330 /* the rules for rDN length constraints are more complex than
331 this. Until we understand them we need to leave this
332 constraint out. Otherwise we break replication, as windows
333 does sometimes send us rDNs longer than 64 */
334 if (!rdn_val || rdn_val->length > 64) {
335 DEBUG(2,(__location__ ": WARNING: rDN longer than 64 limit for '%s'\n", ldb_dn_get_linearized(newdn)));
340 /* And replace it with CN=foo (we need the attribute in upper case */
341 return ldb_dn_set_component(*fixed_dn, 0, upper_rdn_attr, *rdn_val);
344 /* Fix all attribute names to be in the correct case, and check they are all valid per the schema */
345 static int fix_check_attributes(struct ldb_context *ldb,
346 const struct dsdb_schema *schema,
347 struct ldb_message *msg,
348 enum ldb_request_type op)
351 for (i=0; i < msg->num_elements; i++) {
352 const struct dsdb_attribute *attribute = dsdb_attribute_by_lDAPDisplayName(schema, msg->elements[i].name);
353 /* Add in a very special case for 'clearTextPassword',
354 * which is used for internal processing only, and is
355 * not presented in the schema */
357 if (strcasecmp(msg->elements[i].name, "clearTextPassword") != 0) {
358 ldb_asprintf_errstring(ldb, "attribute %s is not a valid attribute in schema", msg->elements[i].name);
359 /* Apparently Windows sends exactly this behaviour */
360 return LDB_ERR_NO_SUCH_ATTRIBUTE;
363 msg->elements[i].name = attribute->lDAPDisplayName;
365 /* We have to deny write operations on constructed attributes */
366 if ((attribute->systemFlags & DS_FLAG_ATTR_IS_CONSTRUCTED) != 0) {
367 ldb_asprintf_errstring(ldb, "attribute %s is constructed", msg->elements[i].name);
369 return LDB_ERR_UNDEFINED_ATTRIBUTE_TYPE;
371 return LDB_ERR_CONSTRAINT_VIOLATION;
381 static int objectclass_do_add(struct oc_context *ac);
383 static int objectclass_add(struct ldb_module *module, struct ldb_request *req)
385 struct ldb_context *ldb;
386 struct ldb_request *search_req;
387 struct oc_context *ac;
388 struct ldb_dn *parent_dn;
390 static const char * const parent_attrs[] = { "objectGUID", "objectClass", NULL };
392 ldb = ldb_module_get_ctx(module);
394 ldb_debug(ldb, LDB_DEBUG_TRACE, "objectclass_add\n");
396 /* do not manipulate our control entries */
397 if (ldb_dn_is_special(req->op.add.message->dn)) {
398 return ldb_next_request(module, req);
401 /* the objectClass must be specified on add */
402 if (ldb_msg_find_element(req->op.add.message,
403 "objectClass") == NULL) {
404 return LDB_ERR_OBJECT_CLASS_VIOLATION;
407 ac = oc_init_context(module, req);
409 return LDB_ERR_OPERATIONS_ERROR;
412 /* If there isn't a parent, just go on to the add processing */
413 if (ldb_dn_get_comp_num(ac->req->op.add.message->dn) == 1) {
414 return objectclass_do_add(ac);
417 /* get copy of parent DN */
418 parent_dn = ldb_dn_get_parent(ac, ac->req->op.add.message->dn);
419 if (parent_dn == NULL) {
421 return LDB_ERR_OPERATIONS_ERROR;
424 ret = ldb_build_search_req(&search_req, ldb,
425 ac, parent_dn, LDB_SCOPE_BASE,
426 "(objectClass=*)", parent_attrs,
428 ac, get_search_callback,
430 if (ret != LDB_SUCCESS) {
433 talloc_steal(search_req, parent_dn);
435 ac->step_fn = objectclass_do_add;
437 return ldb_next_request(ac->module, search_req);
440 static int objectclass_do_add(struct oc_context *ac)
442 struct ldb_context *ldb;
443 const struct dsdb_schema *schema;
444 struct ldb_request *add_req;
446 struct ldb_message_element *objectclass_element, *el;
447 struct ldb_message *msg;
449 struct class_list *sorted, *current;
451 const struct dsdb_class *objectclass;
452 int32_t systemFlags = 0;
453 const char *rdn_name = NULL;
455 ldb = ldb_module_get_ctx(ac->module);
456 schema = dsdb_get_schema(ldb, ac);
458 mem_ctx = talloc_new(ac);
459 if (mem_ctx == NULL) {
461 return LDB_ERR_OPERATIONS_ERROR;
464 msg = ldb_msg_copy_shallow(ac, ac->req->op.add.message);
466 /* Check we have a valid parent */
467 if (ac->search_res == NULL) {
468 if (ldb_dn_compare(ldb_get_root_basedn(ldb), msg->dn) == 0) {
469 /* Allow the tree to be started */
471 /* but don't keep any error string, it's meaningless */
472 ldb_set_errstring(ldb, NULL);
474 ldb_asprintf_errstring(ldb, "objectclass: Cannot add %s, parent does not exist!",
475 ldb_dn_get_linearized(msg->dn));
476 talloc_free(mem_ctx);
477 return LDB_ERR_NO_SUCH_OBJECT;
481 /* Fix up the DN to be in the standard form, taking particular care to match the parent DN */
483 ac->req->op.add.message->dn,
484 ac->search_res->message->dn,
487 if (ret != LDB_SUCCESS) {
488 ldb_asprintf_errstring(ldb, "Could not munge DN %s into normal form",
489 ldb_dn_get_linearized(ac->req->op.add.message->dn));
490 talloc_free(mem_ctx);
496 ret = fix_check_attributes(ldb, schema, msg, ac->req->operation);
497 if (ret != LDB_SUCCESS) {
498 talloc_free(mem_ctx);
502 /* This is now the objectClass list from the database */
503 objectclass_element = ldb_msg_find_element(msg, "objectClass");
505 if (!objectclass_element) {
506 /* Where did it go? bail now... */
507 talloc_free(mem_ctx);
508 return LDB_ERR_OPERATIONS_ERROR;
510 ret = objectclass_sort(ac->module, schema, mem_ctx, objectclass_element, &sorted);
511 if (ret != LDB_SUCCESS) {
512 talloc_free(mem_ctx);
516 ldb_msg_remove_attr(msg, "objectClass");
517 ret = ldb_msg_add_empty(msg, "objectClass", 0, NULL);
519 if (ret != LDB_SUCCESS) {
520 talloc_free(mem_ctx);
524 /* We must completely replace the existing objectClass entry,
525 * because we need it sorted */
527 /* Move from the linked list back into an ldb msg */
528 for (current = sorted; current; current = current->next) {
529 value = talloc_strdup(msg, current->objectclass->lDAPDisplayName);
532 talloc_free(mem_ctx);
533 return LDB_ERR_OPERATIONS_ERROR;
535 ret = ldb_msg_add_string(msg, "objectClass", value);
536 if (ret != LDB_SUCCESS) {
537 ldb_set_errstring(ldb,
538 "objectclass: could not re-add sorted "
539 "objectclass to modify msg");
540 talloc_free(mem_ctx);
545 /* Retrive the message again so get_last_structural_class works */
546 objectclass_element = ldb_msg_find_element(msg, "objectClass");
548 /* Make sure its valid to add an object of this type */
549 objectclass = get_last_structural_class(schema,objectclass_element);
550 if(objectclass == NULL) {
551 ldb_asprintf_errstring(ldb,
552 "Failed to find a structural class for %s",
553 ldb_dn_get_linearized(msg->dn));
554 return LDB_ERR_NAMING_VIOLATION;
557 rdn_name = ldb_dn_get_rdn_name(msg->dn);
558 if (objectclass->rDNAttID
559 && ldb_attr_cmp(rdn_name, objectclass->rDNAttID) != 0) {
560 ldb_asprintf_errstring(ldb,
561 "RDN %s is not correct for most specific structural objectclass %s, should be %s",
562 rdn_name, objectclass->lDAPDisplayName, objectclass->rDNAttID);
563 return LDB_ERR_NAMING_VIOLATION;
566 if (ac->search_res && ac->search_res->message) {
567 struct ldb_message_element *oc_el
568 = ldb_msg_find_element(ac->search_res->message, "objectClass");
570 bool allowed_class = false;
572 for (i=0; allowed_class == false && oc_el && i < oc_el->num_values; i++) {
573 const struct dsdb_class *sclass;
575 sclass = dsdb_class_by_lDAPDisplayName_ldb_val(schema, &oc_el->values[i]);
577 /* We don't know this class? what is going on? */
580 if (ldb_request_get_control(ac->req, LDB_CONTROL_RELAX_OID)) {
581 for (j=0; sclass->systemPossibleInferiors && sclass->systemPossibleInferiors[j]; j++) {
582 if (ldb_attr_cmp(objectclass->lDAPDisplayName, sclass->systemPossibleInferiors[j]) == 0) {
583 allowed_class = true;
588 for (j=0; sclass->systemPossibleInferiors && sclass->systemPossibleInferiors[j]; j++) {
589 if (ldb_attr_cmp(objectclass->lDAPDisplayName, sclass->systemPossibleInferiors[j]) == 0) {
590 allowed_class = true;
597 if (!allowed_class) {
598 ldb_asprintf_errstring(ldb, "structural objectClass %s is not a valid child class for %s",
599 objectclass->lDAPDisplayName, ldb_dn_get_linearized(ac->search_res->message->dn));
600 return LDB_ERR_NAMING_VIOLATION;
604 if (objectclass->systemOnly && !ldb_request_get_control(ac->req, LDB_CONTROL_RELAX_OID)) {
605 ldb_asprintf_errstring(ldb, "objectClass %s is systemOnly, rejecting creation of %s",
606 objectclass->lDAPDisplayName, ldb_dn_get_linearized(msg->dn));
607 return LDB_ERR_UNWILLING_TO_PERFORM;
610 if (!ldb_msg_find_element(msg, "objectCategory")) {
611 struct dsdb_extended_dn_store_format *dn_format = talloc_get_type(ldb_module_get_private(ac->module), struct dsdb_extended_dn_store_format);
612 if (dn_format && dn_format->store_extended_dn_in_ldb == false) {
613 /* Strip off extended components */
614 struct ldb_dn *dn = ldb_dn_new(msg, ldb, objectclass->defaultObjectCategory);
615 value = ldb_dn_alloc_linearized(msg, dn);
618 value = talloc_strdup(msg, objectclass->defaultObjectCategory);
622 talloc_free(mem_ctx);
623 return LDB_ERR_OPERATIONS_ERROR;
625 ldb_msg_add_string(msg, "objectCategory", value);
627 if (!ldb_msg_find_element(msg, "showInAdvancedViewOnly") && (objectclass->defaultHidingValue == true)) {
628 ldb_msg_add_string(msg, "showInAdvancedViewOnly",
632 /* There are very special rules for systemFlags, see MS-ADTS 3.1.1.5.2.4 */
633 el = ldb_msg_find_element(msg, "systemFlags");
635 systemFlags = ldb_msg_find_attr_as_int(msg, "systemFlags", 0);
638 /* Only these flags may be set by a client, but we can't tell between a client and our provision at this point */
639 /* systemFlags &= ( SYSTEM_FLAG_CONFIG_ALLOW_RENAME | SYSTEM_FLAG_CONFIG_ALLOW_MOVE | SYSTEM_FLAG_CONFIG_LIMITED_MOVE); */
640 ldb_msg_remove_element(msg, el);
643 /* This flag is only allowed on attributeSchema objects */
644 if (ldb_attr_cmp(objectclass->lDAPDisplayName, "attributeSchema") == 0) {
645 systemFlags &= ~SYSTEM_FLAG_ATTR_IS_RDN;
648 if (ldb_attr_cmp(objectclass->lDAPDisplayName, "server") == 0) {
649 systemFlags |= (int32_t)(SYSTEM_FLAG_DISALLOW_MOVE_ON_DELETE | SYSTEM_FLAG_CONFIG_ALLOW_RENAME | SYSTEM_FLAG_CONFIG_ALLOW_LIMITED_MOVE);
650 } else if (ldb_attr_cmp(objectclass->lDAPDisplayName, "site") == 0
651 || ldb_attr_cmp(objectclass->lDAPDisplayName, "serverContainer") == 0
652 || ldb_attr_cmp(objectclass->lDAPDisplayName, "ntDSDSA") == 0) {
653 systemFlags |= (int32_t)(SYSTEM_FLAG_DISALLOW_MOVE_ON_DELETE);
655 } else if (ldb_attr_cmp(objectclass->lDAPDisplayName, "siteLink") == 0
656 || ldb_attr_cmp(objectclass->lDAPDisplayName, "siteLinkBridge") == 0
657 || ldb_attr_cmp(objectclass->lDAPDisplayName, "nTDSConnection") == 0) {
658 systemFlags |= (int32_t)(SYSTEM_FLAG_CONFIG_ALLOW_RENAME);
661 /* TODO: If parent object is site or subnet, also add (SYSTEM_FLAG_CONFIG_ALLOW_RENAME) */
663 if (el || systemFlags != 0) {
664 samdb_msg_add_int(ldb, msg, msg, "systemFlags", systemFlags);
668 talloc_free(mem_ctx);
669 ret = ldb_msg_sanity_check(ldb, msg);
672 if (ret != LDB_SUCCESS) {
676 ret = ldb_build_add_req(&add_req, ldb, ac,
681 if (ret != LDB_SUCCESS) {
685 /* perform the add */
686 return ldb_next_request(ac->module, add_req);
689 static int oc_modify_callback(struct ldb_request *req,
690 struct ldb_reply *ares);
691 static int objectclass_do_mod(struct oc_context *ac);
693 static int objectclass_modify(struct ldb_module *module, struct ldb_request *req)
695 struct ldb_context *ldb = ldb_module_get_ctx(module);
696 struct ldb_message_element *objectclass_element;
697 struct ldb_message *msg;
698 const struct dsdb_schema *schema = dsdb_get_schema(ldb, NULL);
699 struct class_list *sorted, *current;
700 struct ldb_request *down_req;
701 struct oc_context *ac;
706 ldb_debug(ldb, LDB_DEBUG_TRACE, "objectclass_modify\n");
708 /* do not manipulate our control entries */
709 if (ldb_dn_is_special(req->op.mod.message->dn)) {
710 return ldb_next_request(module, req);
713 /* Without schema, there isn't much to do here */
715 return ldb_next_request(module, req);
718 /* As with the "real" AD we don't accept empty messages */
719 if (req->op.mod.message->num_elements == 0) {
720 ldb_set_errstring(ldb, "objectclass: modify message must have "
721 "elements/attributes!");
722 return LDB_ERR_UNWILLING_TO_PERFORM;
725 ac = oc_init_context(module, req);
728 return LDB_ERR_OPERATIONS_ERROR;
731 if (!talloc_reference(ac, schema)) {
733 return LDB_ERR_OPERATIONS_ERROR;
736 /* If no part of this touches the objectClass, then we don't
737 * need to make any changes. */
738 objectclass_element = ldb_msg_find_element(req->op.mod.message, "objectClass");
740 /* If the only operation is the deletion of the objectClass
741 * then go on with just fixing the attribute case */
742 if (!objectclass_element) {
743 msg = ldb_msg_copy_shallow(ac, req->op.mod.message);
745 return LDB_ERR_OPERATIONS_ERROR;
748 ret = fix_check_attributes(ldb, schema, msg, req->operation);
749 if (ret != LDB_SUCCESS) {
753 ret = ldb_build_mod_req(&down_req, ldb, ac,
758 if (ret != LDB_SUCCESS) {
762 /* go on with the call chain */
763 return ldb_next_request(module, down_req);
766 switch (objectclass_element->flags & LDB_FLAG_MOD_MASK) {
767 case LDB_FLAG_MOD_DELETE:
768 if (objectclass_element->num_values == 0) {
769 return LDB_ERR_OBJECT_CLASS_MODS_PROHIBITED;
773 case LDB_FLAG_MOD_REPLACE:
774 mem_ctx = talloc_new(ac);
775 if (mem_ctx == NULL) {
776 return LDB_ERR_OPERATIONS_ERROR;
779 msg = ldb_msg_copy_shallow(ac, req->op.mod.message);
781 talloc_free(mem_ctx);
782 return LDB_ERR_OPERATIONS_ERROR;
785 ret = fix_check_attributes(ldb, schema, msg, req->operation);
786 if (ret != LDB_SUCCESS) {
787 talloc_free(mem_ctx);
791 ret = objectclass_sort(module, schema, mem_ctx, objectclass_element, &sorted);
792 if (ret != LDB_SUCCESS) {
793 talloc_free(mem_ctx);
797 /* We must completely replace the existing objectClass entry,
798 * because we need it sorted */
800 ldb_msg_remove_attr(msg, "objectClass");
801 ret = ldb_msg_add_empty(msg, "objectClass", LDB_FLAG_MOD_REPLACE, NULL);
803 if (ret != LDB_SUCCESS) {
804 talloc_free(mem_ctx);
808 /* Move from the linked list back into an ldb msg */
809 for (current = sorted; current; current = current->next) {
810 /* copy the value as this string is on the schema
811 * context and we can't rely on it not changing
812 * before the operation is over */
813 value = talloc_strdup(msg,
814 current->objectclass->lDAPDisplayName);
817 talloc_free(mem_ctx);
818 return LDB_ERR_OPERATIONS_ERROR;
820 ret = ldb_msg_add_string(msg, "objectClass", value);
821 if (ret != LDB_SUCCESS) {
822 ldb_set_errstring(ldb,
823 "objectclass: could not re-add sorted "
824 "objectclass to modify msg");
825 talloc_free(mem_ctx);
830 talloc_free(mem_ctx);
832 ret = ldb_msg_sanity_check(ldb, msg);
833 if (ret != LDB_SUCCESS) {
837 ret = ldb_build_mod_req(&down_req, ldb, ac,
842 if (ret != LDB_SUCCESS) {
846 /* go on with the call chain */
847 return ldb_next_request(module, down_req);
850 /* This isn't the default branch of the switch, but a 'in any
851 * other case'. When a delete isn't for all objectClasses for
855 msg = ldb_msg_copy_shallow(ac, req->op.mod.message);
858 return LDB_ERR_OPERATIONS_ERROR;
861 ret = fix_check_attributes(ldb, schema, msg, req->operation);
862 if (ret != LDB_SUCCESS) {
867 ret = ldb_build_mod_req(&down_req, ldb, ac,
870 ac, oc_modify_callback,
872 if (ret != LDB_SUCCESS) {
876 return ldb_next_request(module, down_req);
879 static int oc_modify_callback(struct ldb_request *req, struct ldb_reply *ares)
881 struct ldb_context *ldb;
882 static const char * const attrs[] = { "objectClass", NULL };
883 struct ldb_request *search_req;
884 struct oc_context *ac;
887 ac = talloc_get_type(req->context, struct oc_context);
888 ldb = ldb_module_get_ctx(ac->module);
891 return ldb_module_done(ac->req, NULL, NULL,
892 LDB_ERR_OPERATIONS_ERROR);
894 if (ares->error != LDB_SUCCESS) {
895 return ldb_module_done(ac->req, ares->controls,
896 ares->response, ares->error);
899 if (ares->type != LDB_REPLY_DONE) {
901 return ldb_module_done(ac->req, NULL, NULL,
902 LDB_ERR_OPERATIONS_ERROR);
907 ret = ldb_build_search_req(&search_req, ldb, ac,
908 ac->req->op.mod.message->dn, LDB_SCOPE_BASE,
911 ac, get_search_callback,
913 if (ret != LDB_SUCCESS) {
914 return ldb_module_done(ac->req, NULL, NULL, ret);
917 ac->step_fn = objectclass_do_mod;
919 ret = ldb_next_request(ac->module, search_req);
920 if (ret != LDB_SUCCESS) {
921 return ldb_module_done(ac->req, NULL, NULL, ret);
926 static int objectclass_do_mod(struct oc_context *ac)
928 struct ldb_context *ldb;
929 const struct dsdb_schema *schema;
930 struct ldb_request *mod_req;
932 struct ldb_message_element *objectclass_element;
933 struct ldb_message *msg;
935 struct class_list *sorted, *current;
938 ldb = ldb_module_get_ctx(ac->module);
940 if (ac->search_res == NULL) {
941 return LDB_ERR_OPERATIONS_ERROR;
943 schema = dsdb_get_schema(ldb, ac);
945 mem_ctx = talloc_new(ac);
946 if (mem_ctx == NULL) {
947 return LDB_ERR_OPERATIONS_ERROR;
950 /* use a new message structure */
951 msg = ldb_msg_new(ac);
953 ldb_set_errstring(ldb,
954 "objectclass: could not create new modify msg");
955 talloc_free(mem_ctx);
956 return LDB_ERR_OPERATIONS_ERROR;
959 /* This is now the objectClass list from the database */
960 objectclass_element = ldb_msg_find_element(ac->search_res->message,
962 if (!objectclass_element) {
963 /* Where did it go? bail now... */
964 talloc_free(mem_ctx);
965 return LDB_ERR_OPERATIONS_ERROR;
969 msg->dn = ac->req->op.mod.message->dn;
971 ret = objectclass_sort(ac->module, schema, mem_ctx, objectclass_element, &sorted);
972 if (ret != LDB_SUCCESS) {
976 /* We must completely replace the existing objectClass entry.
977 * We could do a constrained add/del, but we are meant to be
978 * in a transaction... */
980 ret = ldb_msg_add_empty(msg, "objectClass", LDB_FLAG_MOD_REPLACE, NULL);
981 if (ret != LDB_SUCCESS) {
982 ldb_set_errstring(ldb, "objectclass: could not clear objectclass in modify msg");
983 talloc_free(mem_ctx);
987 /* Move from the linked list back into an ldb msg */
988 for (current = sorted; current; current = current->next) {
989 value = talloc_strdup(msg, current->objectclass->lDAPDisplayName);
992 return LDB_ERR_OPERATIONS_ERROR;
994 ret = ldb_msg_add_string(msg, "objectClass", value);
995 if (ret != LDB_SUCCESS) {
996 ldb_set_errstring(ldb, "objectclass: could not re-add sorted objectclass to modify msg");
997 talloc_free(mem_ctx);
1002 ret = ldb_msg_sanity_check(ldb, msg);
1003 if (ret != LDB_SUCCESS) {
1004 talloc_free(mem_ctx);
1008 ret = ldb_build_mod_req(&mod_req, ldb, ac,
1013 if (ret != LDB_SUCCESS) {
1014 talloc_free(mem_ctx);
1018 talloc_free(mem_ctx);
1019 /* perform the modify */
1020 return ldb_next_request(ac->module, mod_req);
1023 static int objectclass_do_rename(struct oc_context *ac);
1025 static int objectclass_rename(struct ldb_module *module, struct ldb_request *req)
1027 static const char * const attrs[] = { NULL };
1028 struct ldb_context *ldb;
1029 struct ldb_request *search_req;
1030 struct oc_context *ac;
1031 struct ldb_dn *parent_dn;
1034 ldb = ldb_module_get_ctx(module);
1036 ldb_debug(ldb, LDB_DEBUG_TRACE, "objectclass_rename\n");
1038 if (ldb_dn_is_special(req->op.rename.newdn)) { /* do not manipulate our control entries */
1039 return ldb_next_request(module, req);
1042 /* Firstly ensure we are not trying to rename it to be a child of itself */
1043 if ((ldb_dn_compare_base(req->op.rename.olddn, req->op.rename.newdn) == 0)
1044 && (ldb_dn_compare(req->op.rename.olddn, req->op.rename.newdn) != 0)) {
1045 ldb_asprintf_errstring(ldb, "Cannot rename %s to be a child of itself",
1046 ldb_dn_get_linearized(req->op.rename.olddn));
1047 return LDB_ERR_UNWILLING_TO_PERFORM;
1050 ac = oc_init_context(module, req);
1052 return LDB_ERR_OPERATIONS_ERROR;
1055 parent_dn = ldb_dn_get_parent(ac, req->op.rename.newdn);
1056 if (parent_dn == NULL) {
1058 return LDB_ERR_OPERATIONS_ERROR;
1062 it makes a search request, looking for the parent DN to fix up the new DN
1063 to a standard one, at objectclass_do_rename()
1065 ret = ldb_build_search_req(&search_req, ldb,
1066 ac, parent_dn, LDB_SCOPE_BASE,
1069 ac, get_search_callback,
1071 if (ret != LDB_SUCCESS) {
1075 /* we have to add the show deleted control, as otherwise DRS
1076 deletes will be refused as we will think the target parent
1078 ret = ldb_request_add_control(search_req, LDB_CONTROL_SHOW_DELETED_OID, false, NULL);
1080 if (ret != LDB_SUCCESS) {
1084 ac->step_fn = objectclass_do_rename;
1086 return ldb_next_request(ac->module, search_req);
1091 static int objectclass_do_rename(struct oc_context *ac)
1093 struct ldb_context *ldb;
1094 struct ldb_request *rename_req;
1095 struct ldb_dn *fixed_dn;
1098 ldb = ldb_module_get_ctx(ac->module);
1100 /* Check we have a valid parent */
1101 if (ac->search_res == NULL) {
1102 ldb_asprintf_errstring(ldb, "objectclass: Cannot rename %s, parent does not exist!",
1103 ldb_dn_get_linearized(ac->req->op.rename.newdn));
1104 return LDB_ERR_UNWILLING_TO_PERFORM;
1107 /* Fix up the DN to be in the standard form,
1108 * taking particular care to match the parent DN */
1110 ac->req->op.rename.newdn,
1111 ac->search_res->message->dn,
1113 if (ret != LDB_SUCCESS) {
1117 /* TODO: Check this is a valid child to this parent,
1118 * by reading the allowedChildClasses and
1119 * allowedChildClasssesEffective attributes */
1121 ret = ldb_build_rename_req(&rename_req, ldb, ac,
1122 ac->req->op.rename.olddn, fixed_dn,
1126 if (ret != LDB_SUCCESS) {
1130 /* perform the rename */
1131 return ldb_next_request(ac->module, rename_req);
1134 static int objectclass_init(struct ldb_module *module)
1136 struct ldb_context *ldb = ldb_module_get_ctx(module);
1138 /* Init everything else */
1139 ret = ldb_next_init(module);
1140 if (ret != LDB_SUCCESS) {
1144 /* Look for the opaque to indicate we might have to cut down the DN of defaultObjectCategory */
1145 ldb_module_set_private(module, ldb_get_opaque(ldb, DSDB_EXTENDED_DN_STORE_FORMAT_OPAQUE_NAME));
1150 _PUBLIC_ const struct ldb_module_ops ldb_objectclass_module_ops = {
1151 .name = "objectclass",
1152 .add = objectclass_add,
1153 .modify = objectclass_modify,
1154 .rename = objectclass_rename,
1155 .init_context = objectclass_init