s4:dsdb_sort_objectClass_attr - simplify memory context handling
[Samba.git] / source4 / dsdb / samdb / ldb_modules / objectclass.c
blob074360086f9295dcf34196f18f3e59392114c2d5
1 /*
2 ldb database library
4 Copyright (C) Simo Sorce 2006-2008
5 Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005-2009
6 Copyright (C) Matthias Dieter Wallnöfer 2010-2011
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 3 of the License, or
11 (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program. If not, see <http://www.gnu.org/licenses/>.
23 * Name: ldb
25 * Component: objectClass sorting and constraint checking module
27 * Description:
28 * - sort the objectClass attribute into the class
29 * hierarchy and perform constraint checks (correct RDN name,
30 * valid parent),
31 * - fix DNs into 'standard' case
32 * - Add objectCategory and some other attribute defaults
34 * Author: Andrew Bartlett
38 #include "includes.h"
39 #include "ldb_module.h"
40 #include "dsdb/samdb/samdb.h"
41 #include "librpc/ndr/libndr.h"
42 #include "librpc/gen_ndr/ndr_security.h"
43 #include "libcli/security/security.h"
44 #include "auth/auth.h"
45 #include "param/param.h"
46 #include "../libds/common/flags.h"
47 #include "dsdb/samdb/ldb_modules/util.h"
49 struct oc_context {
51 struct ldb_module *module;
52 struct ldb_request *req;
53 const struct dsdb_schema *schema;
55 struct ldb_reply *search_res;
56 struct ldb_reply *search_res2;
58 int (*step_fn)(struct oc_context *);
61 static struct oc_context *oc_init_context(struct ldb_module *module,
62 struct ldb_request *req)
64 struct ldb_context *ldb;
65 struct oc_context *ac;
67 ldb = ldb_module_get_ctx(module);
69 ac = talloc_zero(req, struct oc_context);
70 if (ac == NULL) {
71 ldb_oom(ldb);
72 return NULL;
75 ac->module = module;
76 ac->req = req;
77 ac->schema = dsdb_get_schema(ldb, ac);
79 return ac;
82 static int objectclass_do_add(struct oc_context *ac);
85 * This checks if we have unrelated object classes in our entry's "objectClass"
86 * attribute. That means "unsatisfied" abstract classes (no concrete subclass)
87 * or two or more disjunct structural ones.
88 * If one of these conditions are true, blame.
90 static int check_unrelated_objectclasses(struct ldb_module *module,
91 const struct dsdb_schema *schema,
92 const struct dsdb_class *struct_objectclass,
93 struct ldb_message_element *objectclass_element)
95 struct ldb_context *ldb = ldb_module_get_ctx(module);
96 unsigned int i;
97 bool found;
99 if (schema == NULL) {
100 return LDB_SUCCESS;
103 for (i = 0; i < objectclass_element->num_values; i++) {
104 const struct dsdb_class *tmp_class = dsdb_class_by_lDAPDisplayName_ldb_val(schema,
105 &objectclass_element->values[i]);
106 const struct dsdb_class *tmp_class2 = struct_objectclass;
108 /* Pointer comparison can be used due to the same schema str. */
109 if (tmp_class == NULL ||
110 tmp_class == struct_objectclass ||
111 tmp_class->objectClassCategory > 2 ||
112 ldb_attr_cmp(tmp_class->lDAPDisplayName, "top") == 0) {
113 continue;
116 found = false;
117 while (!found &&
118 ldb_attr_cmp(tmp_class2->lDAPDisplayName, "top") != 0) {
119 tmp_class2 = dsdb_class_by_lDAPDisplayName(schema,
120 tmp_class2->subClassOf);
121 if (tmp_class2 == tmp_class) {
122 found = true;
125 if (found) {
126 continue;
129 ldb_asprintf_errstring(ldb,
130 "objectclass: the objectclass '%s' seems to be unrelated to the entry!",
131 tmp_class->lDAPDisplayName);
132 return LDB_ERR_OBJECT_CLASS_VIOLATION;
135 return LDB_SUCCESS;
138 static int get_search_callback(struct ldb_request *req, struct ldb_reply *ares)
140 struct ldb_context *ldb;
141 struct oc_context *ac;
142 int ret;
144 ac = talloc_get_type(req->context, struct oc_context);
145 ldb = ldb_module_get_ctx(ac->module);
147 if (!ares) {
148 return ldb_module_done(ac->req, NULL, NULL,
149 LDB_ERR_OPERATIONS_ERROR);
151 if (ares->error != LDB_SUCCESS &&
152 ares->error != LDB_ERR_NO_SUCH_OBJECT) {
153 return ldb_module_done(ac->req, ares->controls,
154 ares->response, ares->error);
157 ldb_reset_err_string(ldb);
159 switch (ares->type) {
160 case LDB_REPLY_ENTRY:
161 if (ac->search_res != NULL) {
162 ldb_set_errstring(ldb, "Too many results");
163 talloc_free(ares);
164 return ldb_module_done(ac->req, NULL, NULL,
165 LDB_ERR_OPERATIONS_ERROR);
168 ac->search_res = talloc_steal(ac, ares);
169 break;
171 case LDB_REPLY_REFERRAL:
172 /* ignore */
173 talloc_free(ares);
174 break;
176 case LDB_REPLY_DONE:
177 talloc_free(ares);
178 ret = ac->step_fn(ac);
179 if (ret != LDB_SUCCESS) {
180 return ldb_module_done(ac->req, NULL, NULL, ret);
182 break;
185 return LDB_SUCCESS;
188 static int oc_op_callback(struct ldb_request *req, struct ldb_reply *ares)
190 struct oc_context *ac;
192 ac = talloc_get_type(req->context, struct oc_context);
194 if (!ares) {
195 return ldb_module_done(ac->req, NULL, NULL,
196 LDB_ERR_OPERATIONS_ERROR);
199 if (ares->type == LDB_REPLY_REFERRAL) {
200 return ldb_module_send_referral(ac->req, ares->referral);
203 if (ares->error != LDB_SUCCESS) {
204 return ldb_module_done(ac->req, ares->controls,
205 ares->response, ares->error);
208 if (ares->type != LDB_REPLY_DONE) {
209 talloc_free(ares);
210 return ldb_module_done(ac->req, NULL, NULL,
211 LDB_ERR_OPERATIONS_ERROR);
214 return ldb_module_done(ac->req, ares->controls,
215 ares->response, ares->error);
218 /* Fix up the DN to be in the standard form, taking particular care to match the parent DN
220 This should mean that if the parent is:
221 CN=Users,DC=samba,DC=example,DC=com
222 and a proposed child is
223 cn=Admins ,cn=USERS,dc=Samba,dc=example,dc=COM
225 The resulting DN should be:
227 CN=Admins,CN=Users,DC=samba,DC=example,DC=com
230 static int fix_dn(struct ldb_context *ldb,
231 TALLOC_CTX *mem_ctx,
232 struct ldb_dn *newdn, struct ldb_dn *parent_dn,
233 struct ldb_dn **fixed_dn)
235 char *upper_rdn_attr;
236 const struct ldb_val *rdn_val;
238 /* Fix up the DN to be in the standard form, taking particular care to
239 * match the parent DN */
240 *fixed_dn = ldb_dn_copy(mem_ctx, parent_dn);
241 if (*fixed_dn == NULL) {
242 return ldb_oom(ldb);
245 /* We need the attribute name in upper case */
246 upper_rdn_attr = strupper_talloc(*fixed_dn,
247 ldb_dn_get_rdn_name(newdn));
248 if (upper_rdn_attr == NULL) {
249 return ldb_oom(ldb);
252 /* Create a new child */
253 if (ldb_dn_add_child_fmt(*fixed_dn, "X=X") == false) {
254 return ldb_operr(ldb);
257 rdn_val = ldb_dn_get_rdn_val(newdn);
258 if (rdn_val == NULL) {
259 return ldb_operr(ldb);
262 #if 0
263 /* the rules for rDN length constraints are more complex than
264 this. Until we understand them we need to leave this
265 constraint out. Otherwise we break replication, as windows
266 does sometimes send us rDNs longer than 64 */
267 if (!rdn_val || rdn_val->length > 64) {
268 DEBUG(2,(__location__ ": WARNING: rDN longer than 64 limit for '%s'\n", ldb_dn_get_linearized(newdn)));
270 #endif
273 /* And replace it with CN=foo (we need the attribute in upper case) */
274 return ldb_dn_set_component(*fixed_dn, 0, upper_rdn_attr, *rdn_val);
278 static int objectclass_do_add(struct oc_context *ac);
280 static int objectclass_add(struct ldb_module *module, struct ldb_request *req)
282 struct ldb_context *ldb;
283 struct ldb_request *search_req;
284 struct oc_context *ac;
285 struct ldb_dn *parent_dn;
286 const struct ldb_val *val;
287 int ret;
288 static const char * const parent_attrs[] = { "objectClass", NULL };
290 ldb = ldb_module_get_ctx(module);
292 ldb_debug(ldb, LDB_DEBUG_TRACE, "objectclass_add\n");
294 /* do not manipulate our control entries */
295 if (ldb_dn_is_special(req->op.add.message->dn)) {
296 return ldb_next_request(module, req);
299 /* An add operation on the basedn without "NC-add" operation isn't
300 * allowed. */
301 if (ldb_dn_compare(ldb_get_default_basedn(ldb), req->op.add.message->dn) == 0) {
302 unsigned int instanceType;
304 instanceType = ldb_msg_find_attr_as_uint(req->op.add.message,
305 "instanceType", 0);
306 if (!(instanceType & INSTANCE_TYPE_IS_NC_HEAD)) {
307 char *referral_uri;
308 /* When we are trying to readd the root basedn then
309 * this is denied, but with an interesting mechanism:
310 * there is generated a referral with the last
311 * component value as hostname. */
312 val = ldb_dn_get_component_val(req->op.add.message->dn,
313 ldb_dn_get_comp_num(req->op.add.message->dn) - 1);
314 if (val == NULL) {
315 return ldb_operr(ldb);
317 referral_uri = talloc_asprintf(req, "ldap://%s/%s", val->data,
318 ldb_dn_get_linearized(req->op.add.message->dn));
319 if (referral_uri == NULL) {
320 return ldb_module_oom(module);
323 return ldb_module_send_referral(req, referral_uri);
327 ac = oc_init_context(module, req);
328 if (ac == NULL) {
329 return ldb_operr(ldb);
332 /* If there isn't a parent, just go on to the add processing */
333 if (ldb_dn_get_comp_num(ac->req->op.add.message->dn) == 1) {
334 return objectclass_do_add(ac);
337 /* get copy of parent DN */
338 parent_dn = ldb_dn_get_parent(ac, ac->req->op.add.message->dn);
339 if (parent_dn == NULL) {
340 return ldb_operr(ldb);
343 ret = ldb_build_search_req(&search_req, ldb,
344 ac, parent_dn, LDB_SCOPE_BASE,
345 "(objectClass=*)", parent_attrs,
346 NULL,
347 ac, get_search_callback,
348 req);
349 LDB_REQ_SET_LOCATION(search_req);
350 if (ret != LDB_SUCCESS) {
351 return ret;
354 ac->step_fn = objectclass_do_add;
356 return ldb_next_request(ac->module, search_req);
361 check if this is a special RODC nTDSDSA add
363 static bool check_rodc_ntdsdsa_add(struct oc_context *ac,
364 const struct dsdb_class *objectclass)
366 struct ldb_control *rodc_control;
368 if (ldb_attr_cmp(objectclass->lDAPDisplayName, "nTDSDSA") != 0) {
369 return false;
371 rodc_control = ldb_request_get_control(ac->req, LDB_CONTROL_RODC_DCPROMO_OID);
372 if (!rodc_control) {
373 return false;
376 rodc_control->critical = false;
377 return true;
380 static int objectclass_do_add(struct oc_context *ac)
382 struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
383 struct ldb_request *add_req;
384 struct ldb_message_element *objectclass_element, *el;
385 struct ldb_message *msg;
386 const char *rdn_name = NULL;
387 char *value;
388 const struct dsdb_class *objectclass;
389 struct ldb_dn *objectcategory;
390 int32_t systemFlags = 0;
391 unsigned int i, j;
392 bool found;
393 int ret;
395 msg = ldb_msg_copy_shallow(ac, ac->req->op.add.message);
396 if (msg == NULL) {
397 return ldb_module_oom(ac->module);
400 /* Check if we have a valid parent - this check is needed since
401 * we don't get a LDB_ERR_NO_SUCH_OBJECT error. */
402 if (ac->search_res == NULL) {
403 unsigned int instanceType;
405 /* An add operation on partition DNs without "NC-add" operation
406 * isn't allowed. */
407 instanceType = ldb_msg_find_attr_as_uint(msg, "instanceType",
409 if (!(instanceType & INSTANCE_TYPE_IS_NC_HEAD)) {
410 ldb_asprintf_errstring(ldb, "objectclass: Cannot add %s, parent does not exist!",
411 ldb_dn_get_linearized(msg->dn));
412 return LDB_ERR_NO_SUCH_OBJECT;
415 /* Don't keep any error messages - we've to add a partition */
416 ldb_set_errstring(ldb, NULL);
417 } else {
418 /* Fix up the DN to be in the standard form, taking
419 * particular care to match the parent DN */
420 ret = fix_dn(ldb, msg,
421 ac->req->op.add.message->dn,
422 ac->search_res->message->dn,
423 &msg->dn);
424 if (ret != LDB_SUCCESS) {
425 ldb_asprintf_errstring(ldb, "objectclass: Could not munge DN %s into normal form",
426 ldb_dn_get_linearized(ac->req->op.add.message->dn));
427 return ret;
431 if (ac->schema != NULL) {
433 * Notice: by the normalization function call in "ldb_request()"
434 * case "LDB_ADD" we have always only *one* "objectClass"
435 * attribute at this stage!
438 objectclass_element = ldb_msg_find_element(msg, "objectClass");
439 if (!objectclass_element) {
440 ldb_asprintf_errstring(ldb, "objectclass: Cannot add %s, no objectclass specified!",
441 ldb_dn_get_linearized(msg->dn));
442 return LDB_ERR_OBJECT_CLASS_VIOLATION;
444 if (objectclass_element->num_values == 0) {
445 ldb_asprintf_errstring(ldb, "objectclass: Cannot add %s, at least one (structural) objectclass has to be specified!",
446 ldb_dn_get_linearized(msg->dn));
447 return LDB_ERR_CONSTRAINT_VIOLATION;
450 /* Now do the sorting */
451 ret = dsdb_sort_objectClass_attr(ldb, ac->schema,
452 objectclass_element, msg,
453 objectclass_element);
454 if (ret != LDB_SUCCESS) {
455 return ret;
459 * Get the new top-most structural object class and check for
460 * unrelated structural classes
462 objectclass = dsdb_get_last_structural_class(ac->schema,
463 objectclass_element);
464 if (objectclass == NULL) {
465 ldb_asprintf_errstring(ldb,
466 "Failed to find a structural class for %s",
467 ldb_dn_get_linearized(msg->dn));
468 return LDB_ERR_UNWILLING_TO_PERFORM;
471 ret = check_unrelated_objectclasses(ac->module, ac->schema,
472 objectclass,
473 objectclass_element);
474 if (ret != LDB_SUCCESS) {
475 return ret;
478 rdn_name = ldb_dn_get_rdn_name(msg->dn);
479 if (rdn_name == NULL) {
480 return ldb_operr(ldb);
482 found = false;
483 for (i = 0; (!found) && (i < objectclass_element->num_values);
484 i++) {
485 const struct dsdb_class *tmp_class =
486 dsdb_class_by_lDAPDisplayName_ldb_val(ac->schema,
487 &objectclass_element->values[i]);
489 if (tmp_class == NULL) continue;
491 if (ldb_attr_cmp(rdn_name, tmp_class->rDNAttID) == 0)
492 found = true;
494 if (!found) {
495 ldb_asprintf_errstring(ldb,
496 "objectclass: Invalid RDN '%s' for objectclass '%s'!",
497 rdn_name, objectclass->lDAPDisplayName);
498 return LDB_ERR_NAMING_VIOLATION;
501 if (objectclass->systemOnly &&
502 !ldb_request_get_control(ac->req, LDB_CONTROL_RELAX_OID) &&
503 !check_rodc_ntdsdsa_add(ac, objectclass)) {
504 ldb_asprintf_errstring(ldb,
505 "objectclass: object class '%s' is system-only, rejecting creation of '%s'!",
506 objectclass->lDAPDisplayName,
507 ldb_dn_get_linearized(msg->dn));
508 return LDB_ERR_UNWILLING_TO_PERFORM;
511 if (ac->search_res && ac->search_res->message) {
512 struct ldb_message_element *oc_el
513 = ldb_msg_find_element(ac->search_res->message, "objectClass");
515 bool allowed_class = false;
516 for (i=0; allowed_class == false && oc_el && i < oc_el->num_values; i++) {
517 const struct dsdb_class *sclass;
519 sclass = dsdb_class_by_lDAPDisplayName_ldb_val(ac->schema,
520 &oc_el->values[i]);
521 if (!sclass) {
522 /* We don't know this class? what is going on? */
523 continue;
525 for (j=0; sclass->systemPossibleInferiors && sclass->systemPossibleInferiors[j]; j++) {
526 if (ldb_attr_cmp(objectclass->lDAPDisplayName, sclass->systemPossibleInferiors[j]) == 0) {
527 allowed_class = true;
528 break;
533 if (!allowed_class) {
534 ldb_asprintf_errstring(ldb, "structural objectClass %s is not a valid child class for %s",
535 objectclass->lDAPDisplayName, ldb_dn_get_linearized(ac->search_res->message->dn));
536 return LDB_ERR_NAMING_VIOLATION;
540 objectcategory = ldb_msg_find_attr_as_dn(ldb, ac, msg,
541 "objectCategory");
542 if (objectcategory == NULL) {
543 struct dsdb_extended_dn_store_format *dn_format =
544 talloc_get_type(ldb_module_get_private(ac->module),
545 struct dsdb_extended_dn_store_format);
546 if (dn_format && dn_format->store_extended_dn_in_ldb == false) {
547 /* Strip off extended components */
548 struct ldb_dn *dn = ldb_dn_new(ac, ldb,
549 objectclass->defaultObjectCategory);
550 value = ldb_dn_alloc_linearized(msg, dn);
551 talloc_free(dn);
552 } else {
553 value = talloc_strdup(msg,
554 objectclass->defaultObjectCategory);
556 if (value == NULL) {
557 return ldb_module_oom(ac->module);
560 ret = ldb_msg_add_string(msg, "objectCategory", value);
561 if (ret != LDB_SUCCESS) {
562 return ret;
564 } else {
565 const struct dsdb_class *ocClass =
566 dsdb_class_by_cn_ldb_val(ac->schema,
567 ldb_dn_get_rdn_val(objectcategory));
568 if (ocClass != NULL) {
569 struct ldb_dn *dn = ldb_dn_new(ac, ldb,
570 ocClass->defaultObjectCategory);
571 if (ldb_dn_compare(objectcategory, dn) != 0) {
572 ocClass = NULL;
575 talloc_free(objectcategory);
576 if (ocClass == NULL) {
577 ldb_asprintf_errstring(ldb, "objectclass: Cannot add %s, 'objectCategory' attribute invalid!",
578 ldb_dn_get_linearized(msg->dn));
579 return LDB_ERR_OBJECT_CLASS_VIOLATION;
583 if (!ldb_msg_find_element(msg, "showInAdvancedViewOnly") && (objectclass->defaultHidingValue == true)) {
584 ldb_msg_add_string(msg, "showInAdvancedViewOnly",
585 "TRUE");
588 /* There are very special rules for systemFlags, see MS-ADTS
589 * MS-ADTS 3.1.1.5.2.4 */
591 el = ldb_msg_find_element(msg, "systemFlags");
592 if ((el != NULL) && (el->num_values > 1)) {
593 ldb_asprintf_errstring(ldb, "objectclass: Cannot add %s, 'systemFlags' attribute multivalued!",
594 ldb_dn_get_linearized(msg->dn));
595 return LDB_ERR_CONSTRAINT_VIOLATION;
598 systemFlags = ldb_msg_find_attr_as_int(msg, "systemFlags", 0);
600 ldb_msg_remove_attr(msg, "systemFlags");
602 /* Only the following flags may be set by a client */
603 if (ldb_request_get_control(ac->req,
604 LDB_CONTROL_RELAX_OID) == NULL) {
605 systemFlags &= ( SYSTEM_FLAG_CONFIG_ALLOW_RENAME
606 | SYSTEM_FLAG_CONFIG_ALLOW_MOVE
607 | SYSTEM_FLAG_CONFIG_ALLOW_LIMITED_MOVE
608 | SYSTEM_FLAG_ATTR_IS_RDN );
611 /* But the last one ("ATTR_IS_RDN") is only allowed on
612 * "attributeSchema" objects. So truncate if it does not fit. */
613 if (ldb_attr_cmp(objectclass->lDAPDisplayName, "attributeSchema") != 0) {
614 systemFlags &= ~SYSTEM_FLAG_ATTR_IS_RDN;
617 if (ldb_attr_cmp(objectclass->lDAPDisplayName, "server") == 0) {
618 systemFlags |= (int32_t)(SYSTEM_FLAG_DISALLOW_MOVE_ON_DELETE | SYSTEM_FLAG_CONFIG_ALLOW_RENAME | SYSTEM_FLAG_CONFIG_ALLOW_LIMITED_MOVE);
619 } else if (ldb_attr_cmp(objectclass->lDAPDisplayName, "site") == 0
620 || ldb_attr_cmp(objectclass->lDAPDisplayName, "serversContainer") == 0
621 || ldb_attr_cmp(objectclass->lDAPDisplayName, "nTDSDSA") == 0) {
622 if (ldb_attr_cmp(objectclass->lDAPDisplayName, "site") == 0)
623 systemFlags |= (int32_t)(SYSTEM_FLAG_CONFIG_ALLOW_RENAME);
624 systemFlags |= (int32_t)(SYSTEM_FLAG_DISALLOW_MOVE_ON_DELETE);
625 } else if (ldb_attr_cmp(objectclass->lDAPDisplayName, "siteLink") == 0
626 || ldb_attr_cmp(objectclass->lDAPDisplayName, "subnet") == 0
627 || ldb_attr_cmp(objectclass->lDAPDisplayName, "siteLinkBridge") == 0
628 || ldb_attr_cmp(objectclass->lDAPDisplayName, "nTDSConnection") == 0) {
629 systemFlags |= (int32_t)(SYSTEM_FLAG_CONFIG_ALLOW_RENAME);
631 /* TODO: If parent object is site or subnet, also add (SYSTEM_FLAG_CONFIG_ALLOW_RENAME) */
633 if (el || systemFlags != 0) {
634 ret = samdb_msg_add_int(ldb, msg, msg, "systemFlags",
635 systemFlags);
636 if (ret != LDB_SUCCESS) {
637 return ret;
641 /* make sure that "isCriticalSystemObject" is not specified! */
642 el = ldb_msg_find_element(msg, "isCriticalSystemObject");
643 if ((el != NULL) &&
644 !ldb_request_get_control(ac->req, LDB_CONTROL_RELAX_OID)) {
645 ldb_set_errstring(ldb,
646 "objectclass: 'isCriticalSystemObject' must not be specified!");
647 return LDB_ERR_UNWILLING_TO_PERFORM;
651 ret = ldb_build_add_req(&add_req, ldb, ac,
652 msg,
653 ac->req->controls,
654 ac, oc_op_callback,
655 ac->req);
656 LDB_REQ_SET_LOCATION(add_req);
657 if (ret != LDB_SUCCESS) {
658 return ret;
661 /* perform the add */
662 return ldb_next_request(ac->module, add_req);
665 static int oc_modify_callback(struct ldb_request *req,
666 struct ldb_reply *ares);
667 static int objectclass_do_mod(struct oc_context *ac);
669 static int objectclass_modify(struct ldb_module *module, struct ldb_request *req)
671 struct ldb_context *ldb = ldb_module_get_ctx(module);
672 struct ldb_message_element *objectclass_element;
673 struct ldb_message *msg;
674 struct ldb_request *down_req;
675 struct oc_context *ac;
676 bool oc_changes = false;
677 int ret;
679 ldb_debug(ldb, LDB_DEBUG_TRACE, "objectclass_modify\n");
681 /* do not manipulate our control entries */
682 if (ldb_dn_is_special(req->op.mod.message->dn)) {
683 return ldb_next_request(module, req);
686 /* As with the "real" AD we don't accept empty messages */
687 if (req->op.mod.message->num_elements == 0) {
688 ldb_set_errstring(ldb, "objectclass: modify message must have "
689 "elements/attributes!");
690 return LDB_ERR_UNWILLING_TO_PERFORM;
693 ac = oc_init_context(module, req);
694 if (ac == NULL) {
695 return ldb_operr(ldb);
698 /* Without schema, there isn't much to do here */
699 if (ac->schema == NULL) {
700 talloc_free(ac);
701 return ldb_next_request(module, req);
704 msg = ldb_msg_copy_shallow(ac, req->op.mod.message);
705 if (msg == NULL) {
706 return ldb_module_oom(ac->module);
709 /* For now change everything except the objectclasses */
711 objectclass_element = ldb_msg_find_element(msg, "objectClass");
712 if (objectclass_element != NULL) {
713 ldb_msg_remove_attr(msg, "objectClass");
714 oc_changes = true;
717 /* MS-ADTS 3.1.1.5.3.5 - on a forest level < 2003 we do allow updates
718 * only on application NCs - not on the default ones */
719 if (oc_changes &&
720 (dsdb_forest_functional_level(ldb) < DS_DOMAIN_FUNCTION_2003)) {
721 struct ldb_dn *nc_root;
723 ret = dsdb_find_nc_root(ldb, ac, req->op.mod.message->dn,
724 &nc_root);
725 if (ret != LDB_SUCCESS) {
726 return ret;
729 if ((ldb_dn_compare(nc_root, ldb_get_default_basedn(ldb)) == 0) ||
730 (ldb_dn_compare(nc_root, ldb_get_config_basedn(ldb)) == 0) ||
731 (ldb_dn_compare(nc_root, ldb_get_schema_basedn(ldb)) == 0)) {
732 ldb_set_errstring(ldb,
733 "objectclass: object class changes on objects under the standard name contexts not allowed!");
734 return LDB_ERR_UNWILLING_TO_PERFORM;
737 talloc_free(nc_root);
740 ret = ldb_build_mod_req(&down_req, ldb, ac,
741 msg,
742 req->controls, ac,
743 oc_changes ? oc_modify_callback : oc_op_callback,
744 req);
745 LDB_REQ_SET_LOCATION(down_req);
746 if (ret != LDB_SUCCESS) {
747 return ret;
750 return ldb_next_request(module, down_req);
753 static int oc_modify_callback(struct ldb_request *req, struct ldb_reply *ares)
755 static const char * const attrs[] = { "objectClass", NULL };
756 struct ldb_context *ldb;
757 struct ldb_request *search_req;
758 struct oc_context *ac;
759 int ret;
761 ac = talloc_get_type(req->context, struct oc_context);
762 ldb = ldb_module_get_ctx(ac->module);
764 if (!ares) {
765 return ldb_module_done(ac->req, NULL, NULL,
766 LDB_ERR_OPERATIONS_ERROR);
769 if (ares->type == LDB_REPLY_REFERRAL) {
770 return ldb_module_send_referral(ac->req, ares->referral);
773 if (ares->error != LDB_SUCCESS) {
774 return ldb_module_done(ac->req, ares->controls,
775 ares->response, ares->error);
778 if (ares->type != LDB_REPLY_DONE) {
779 talloc_free(ares);
780 return ldb_module_done(ac->req, NULL, NULL,
781 LDB_ERR_OPERATIONS_ERROR);
784 talloc_free(ares);
786 /* this looks up the real existing object for fetching some important
787 * information (objectclasses) */
788 ret = ldb_build_search_req(&search_req, ldb,
789 ac, ac->req->op.mod.message->dn,
790 LDB_SCOPE_BASE,
791 "(objectClass=*)",
792 attrs, NULL,
793 ac, get_search_callback,
794 ac->req);
795 LDB_REQ_SET_LOCATION(search_req);
796 if (ret != LDB_SUCCESS) {
797 return ldb_module_done(ac->req, NULL, NULL, ret);
800 ac->step_fn = objectclass_do_mod;
802 ret = ldb_next_request(ac->module, search_req);
803 if (ret != LDB_SUCCESS) {
804 return ldb_module_done(ac->req, NULL, NULL, ret);
807 return LDB_SUCCESS;
810 static int objectclass_do_mod(struct oc_context *ac)
812 struct ldb_context *ldb;
813 struct ldb_request *mod_req;
814 struct ldb_message_element *oc_el_entry, *oc_el_change;
815 struct ldb_val *vals;
816 struct ldb_message *msg;
817 const struct dsdb_class *objectclass;
818 unsigned int i, j, k;
819 bool found;
820 int ret;
822 ldb = ldb_module_get_ctx(ac->module);
824 /* we should always have a valid entry when we enter here */
825 if (ac->search_res == NULL) {
826 return ldb_operr(ldb);
829 oc_el_entry = ldb_msg_find_element(ac->search_res->message,
830 "objectClass");
831 if (oc_el_entry == NULL) {
832 /* existing entry without a valid object class? */
833 return ldb_operr(ldb);
836 /* use a new message structure */
837 msg = ldb_msg_new(ac);
838 if (msg == NULL) {
839 return ldb_module_oom(ac->module);
842 msg->dn = ac->req->op.mod.message->dn;
844 /* We've to walk over all "objectClass" message elements */
845 for (k = 0; k < ac->req->op.mod.message->num_elements; k++) {
846 if (ldb_attr_cmp(ac->req->op.mod.message->elements[k].name,
847 "objectClass") != 0) {
848 continue;
851 oc_el_change = &ac->req->op.mod.message->elements[k];
853 switch (oc_el_change->flags & LDB_FLAG_MOD_MASK) {
854 case LDB_FLAG_MOD_ADD:
855 /* Merge the two message elements */
856 for (i = 0; i < oc_el_change->num_values; i++) {
857 for (j = 0; j < oc_el_entry->num_values; j++) {
858 if (ldb_attr_cmp((char *)oc_el_change->values[i].data,
859 (char *)oc_el_entry->values[j].data) == 0) {
860 ldb_asprintf_errstring(ldb,
861 "objectclass: cannot re-add an existing objectclass: '%.*s'!",
862 (int)oc_el_change->values[i].length,
863 (const char *)oc_el_change->values[i].data);
864 return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS;
867 /* append the new object class value - code was
868 * copied from "ldb_msg_add_value" */
869 vals = talloc_realloc(oc_el_entry, oc_el_entry->values,
870 struct ldb_val,
871 oc_el_entry->num_values + 1);
872 if (vals == NULL) {
873 return ldb_module_oom(ac->module);
875 oc_el_entry->values = vals;
876 oc_el_entry->values[oc_el_entry->num_values] =
877 oc_el_change->values[i];
878 ++(oc_el_entry->num_values);
881 break;
883 case LDB_FLAG_MOD_REPLACE:
885 * In this case the new "oc_el_entry" is simply
886 * "oc_el_change"
888 oc_el_entry = oc_el_change;
890 break;
892 case LDB_FLAG_MOD_DELETE:
893 /* Merge the two message elements */
894 for (i = 0; i < oc_el_change->num_values; i++) {
895 found = false;
896 for (j = 0; j < oc_el_entry->num_values; j++) {
897 if (ldb_attr_cmp((char *)oc_el_change->values[i].data,
898 (char *)oc_el_entry->values[j].data) == 0) {
899 found = true;
900 /* delete the object class value
901 * - code was copied from
902 * "ldb_msg_remove_element" */
903 if (j != oc_el_entry->num_values - 1) {
904 memmove(&oc_el_entry->values[j],
905 &oc_el_entry->values[j+1],
906 ((oc_el_entry->num_values-1) - j)*sizeof(struct ldb_val));
908 --(oc_el_entry->num_values);
909 break;
912 if (!found) {
913 /* we cannot delete a not existing
914 * object class */
915 ldb_asprintf_errstring(ldb,
916 "objectclass: cannot delete this objectclass: '%.*s'!",
917 (int)oc_el_change->values[i].length,
918 (const char *)oc_el_change->values[i].data);
919 return LDB_ERR_NO_SUCH_ATTRIBUTE;
923 break;
926 /* Now do the sorting */
927 ret = dsdb_sort_objectClass_attr(ldb, ac->schema, oc_el_entry,
928 msg, oc_el_entry);
929 if (ret != LDB_SUCCESS) {
930 return ret;
934 * Get the new top-most structural object class and check for
935 * unrelated structural classes
937 objectclass = dsdb_get_last_structural_class(ac->schema,
938 oc_el_entry);
939 if (objectclass == NULL) {
940 ldb_set_errstring(ldb,
941 "objectclass: cannot delete all structural objectclasses!");
942 return LDB_ERR_OBJECT_CLASS_VIOLATION;
945 /* Check for unrelated objectclasses */
946 ret = check_unrelated_objectclasses(ac->module, ac->schema,
947 objectclass,
948 oc_el_entry);
949 if (ret != LDB_SUCCESS) {
950 return ret;
954 /* Now add the new object class attribute to the change message */
955 ret = ldb_msg_add(msg, oc_el_entry, LDB_FLAG_MOD_REPLACE);
956 if (ret != LDB_SUCCESS) {
957 ldb_module_oom(ac->module);
958 return ret;
961 /* Now we have the real and definitive change left to do */
963 ret = ldb_build_mod_req(&mod_req, ldb, ac,
964 msg,
965 ac->req->controls,
966 ac, oc_op_callback,
967 ac->req);
968 LDB_REQ_SET_LOCATION(mod_req);
969 if (ret != LDB_SUCCESS) {
970 return ret;
973 return ldb_next_request(ac->module, mod_req);
976 static int objectclass_do_rename(struct oc_context *ac);
978 static int objectclass_rename(struct ldb_module *module, struct ldb_request *req)
980 static const char * const attrs[] = { "objectClass", NULL };
981 struct ldb_context *ldb;
982 struct ldb_request *search_req;
983 struct oc_context *ac;
984 struct ldb_dn *parent_dn;
985 int ret;
987 ldb = ldb_module_get_ctx(module);
989 ldb_debug(ldb, LDB_DEBUG_TRACE, "objectclass_rename\n");
991 /* do not manipulate our control entries */
992 if (ldb_dn_is_special(req->op.rename.olddn)) {
993 return ldb_next_request(module, req);
996 ac = oc_init_context(module, req);
997 if (ac == NULL) {
998 return ldb_operr(ldb);
1001 parent_dn = ldb_dn_get_parent(ac, req->op.rename.newdn);
1002 if (parent_dn == NULL) {
1003 ldb_asprintf_errstring(ldb, "objectclass: Cannot rename %s, the parent DN does not exist!",
1004 ldb_dn_get_linearized(req->op.rename.olddn));
1005 return LDB_ERR_NO_SUCH_OBJECT;
1008 /* this looks up the parent object for fetching some important
1009 * information (objectclasses, DN normalisation...) */
1010 ret = ldb_build_search_req(&search_req, ldb,
1011 ac, parent_dn, LDB_SCOPE_BASE,
1012 "(objectClass=*)",
1013 attrs, NULL,
1014 ac, get_search_callback,
1015 req);
1016 LDB_REQ_SET_LOCATION(search_req);
1017 if (ret != LDB_SUCCESS) {
1018 return ret;
1021 /* we have to add the show recycled control, as otherwise DRS
1022 deletes will be refused as we will think the target parent
1023 does not exist */
1024 ret = ldb_request_add_control(search_req, LDB_CONTROL_SHOW_RECYCLED_OID,
1025 false, NULL);
1027 if (ret != LDB_SUCCESS) {
1028 return ret;
1031 ac->step_fn = objectclass_do_rename;
1033 return ldb_next_request(ac->module, search_req);
1036 static int objectclass_do_rename2(struct oc_context *ac);
1038 static int objectclass_do_rename(struct oc_context *ac)
1040 static const char * const attrs[] = { "objectClass", NULL };
1041 struct ldb_context *ldb;
1042 struct ldb_request *search_req;
1043 int ret;
1045 ldb = ldb_module_get_ctx(ac->module);
1047 /* Check if we have a valid parent - this check is needed since
1048 * we don't get a LDB_ERR_NO_SUCH_OBJECT error. */
1049 if (ac->search_res == NULL) {
1050 ldb_asprintf_errstring(ldb, "objectclass: Cannot rename %s, parent does not exist!",
1051 ldb_dn_get_linearized(ac->req->op.rename.olddn));
1052 return LDB_ERR_OTHER;
1055 /* now assign "search_res2" to the parent entry to have "search_res"
1056 * free for another lookup */
1057 ac->search_res2 = ac->search_res;
1058 ac->search_res = NULL;
1060 /* this looks up the real existing object for fetching some important
1061 * information (objectclasses) */
1062 ret = ldb_build_search_req(&search_req, ldb,
1063 ac, ac->req->op.rename.olddn,
1064 LDB_SCOPE_BASE,
1065 "(objectClass=*)",
1066 attrs, NULL,
1067 ac, get_search_callback,
1068 ac->req);
1069 LDB_REQ_SET_LOCATION(search_req);
1070 if (ret != LDB_SUCCESS) {
1071 return ret;
1074 ac->step_fn = objectclass_do_rename2;
1076 return ldb_next_request(ac->module, search_req);
1079 static int objectclass_do_rename2(struct oc_context *ac)
1081 struct ldb_context *ldb;
1082 struct ldb_request *rename_req;
1083 struct ldb_dn *fixed_dn;
1084 int ret;
1086 ldb = ldb_module_get_ctx(ac->module);
1088 /* Check if we have a valid entry - this check is needed since
1089 * we don't get a LDB_ERR_NO_SUCH_OBJECT error. */
1090 if (ac->search_res == NULL) {
1091 ldb_asprintf_errstring(ldb, "objectclass: Cannot rename %s, entry does not exist!",
1092 ldb_dn_get_linearized(ac->req->op.rename.olddn));
1093 return LDB_ERR_NO_SUCH_OBJECT;
1096 if (ac->schema != NULL) {
1097 struct ldb_message_element *oc_el_entry, *oc_el_parent;
1098 const struct dsdb_class *objectclass;
1099 const char *rdn_name;
1100 bool allowed_class = false;
1101 unsigned int i, j;
1102 bool found;
1104 oc_el_entry = ldb_msg_find_element(ac->search_res->message,
1105 "objectClass");
1106 if (oc_el_entry == NULL) {
1107 /* existing entry without a valid object class? */
1108 return ldb_operr(ldb);
1110 objectclass = dsdb_get_last_structural_class(ac->schema,
1111 oc_el_entry);
1112 if (objectclass == NULL) {
1113 /* existing entry without a valid object class? */
1114 return ldb_operr(ldb);
1117 rdn_name = ldb_dn_get_rdn_name(ac->req->op.rename.newdn);
1118 if (rdn_name == NULL) {
1119 return ldb_operr(ldb);
1121 found = false;
1122 for (i = 0; (!found) && (i < oc_el_entry->num_values); i++) {
1123 const struct dsdb_class *tmp_class =
1124 dsdb_class_by_lDAPDisplayName_ldb_val(ac->schema,
1125 &oc_el_entry->values[i]);
1127 if (tmp_class == NULL) continue;
1129 if (ldb_attr_cmp(rdn_name, tmp_class->rDNAttID) == 0)
1130 found = true;
1132 if (!found) {
1133 ldb_asprintf_errstring(ldb,
1134 "objectclass: Invalid RDN '%s' for objectclass '%s'!",
1135 rdn_name, objectclass->lDAPDisplayName);
1136 return LDB_ERR_UNWILLING_TO_PERFORM;
1139 oc_el_parent = ldb_msg_find_element(ac->search_res2->message,
1140 "objectClass");
1141 if (oc_el_parent == NULL) {
1142 /* existing entry without a valid object class? */
1143 return ldb_operr(ldb);
1146 for (i=0; allowed_class == false && i < oc_el_parent->num_values; i++) {
1147 const struct dsdb_class *sclass;
1149 sclass = dsdb_class_by_lDAPDisplayName_ldb_val(ac->schema,
1150 &oc_el_parent->values[i]);
1151 if (!sclass) {
1152 /* We don't know this class? what is going on? */
1153 continue;
1155 for (j=0; sclass->systemPossibleInferiors && sclass->systemPossibleInferiors[j]; j++) {
1156 if (ldb_attr_cmp(objectclass->lDAPDisplayName, sclass->systemPossibleInferiors[j]) == 0) {
1157 allowed_class = true;
1158 break;
1163 if (!allowed_class) {
1164 ldb_asprintf_errstring(ldb,
1165 "objectclass: structural objectClass %s is not a valid child class for %s",
1166 objectclass->lDAPDisplayName, ldb_dn_get_linearized(ac->search_res2->message->dn));
1167 return LDB_ERR_NAMING_VIOLATION;
1171 /* Ensure we are not trying to rename it to be a child of itself */
1172 if ((ldb_dn_compare_base(ac->req->op.rename.olddn,
1173 ac->req->op.rename.newdn) == 0) &&
1174 (ldb_dn_compare(ac->req->op.rename.olddn,
1175 ac->req->op.rename.newdn) != 0)) {
1176 ldb_asprintf_errstring(ldb, "objectclass: Cannot rename %s to be a child of itself",
1177 ldb_dn_get_linearized(ac->req->op.rename.olddn));
1178 return LDB_ERR_UNWILLING_TO_PERFORM;
1181 /* Fix up the DN to be in the standard form, taking
1182 * particular care to match the parent DN */
1183 ret = fix_dn(ldb, ac,
1184 ac->req->op.rename.newdn,
1185 ac->search_res2->message->dn,
1186 &fixed_dn);
1187 if (ret != LDB_SUCCESS) {
1188 ldb_asprintf_errstring(ldb, "objectclass: Could not munge DN %s into normal form",
1189 ldb_dn_get_linearized(ac->req->op.rename.newdn));
1190 return ret;
1194 ret = ldb_build_rename_req(&rename_req, ldb, ac,
1195 ac->req->op.rename.olddn, fixed_dn,
1196 ac->req->controls,
1197 ac, oc_op_callback,
1198 ac->req);
1199 LDB_REQ_SET_LOCATION(rename_req);
1200 if (ret != LDB_SUCCESS) {
1201 return ret;
1204 /* perform the rename */
1205 return ldb_next_request(ac->module, rename_req);
1208 static int objectclass_do_delete(struct oc_context *ac);
1210 static int objectclass_delete(struct ldb_module *module, struct ldb_request *req)
1212 static const char * const attrs[] = { "nCName", "objectClass",
1213 "systemFlags",
1214 "isDeleted",
1215 "isCriticalSystemObject", NULL };
1216 struct ldb_context *ldb;
1217 struct ldb_request *search_req;
1218 struct oc_context *ac;
1219 int ret;
1221 ldb = ldb_module_get_ctx(module);
1223 ldb_debug(ldb, LDB_DEBUG_TRACE, "objectclass_delete\n");
1225 /* do not manipulate our control entries */
1226 if (ldb_dn_is_special(req->op.del.dn)) {
1227 return ldb_next_request(module, req);
1230 /* Bypass the constraint checks when we do have the "RELAX" control
1231 * set. */
1232 if (ldb_request_get_control(req, LDB_CONTROL_RELAX_OID) != NULL) {
1233 return ldb_next_request(module, req);
1236 ac = oc_init_context(module, req);
1237 if (ac == NULL) {
1238 return ldb_operr(ldb);
1241 /* this looks up the entry object for fetching some important
1242 * information (object classes, system flags...) */
1243 ret = ldb_build_search_req(&search_req, ldb,
1244 ac, req->op.del.dn, LDB_SCOPE_BASE,
1245 "(objectClass=*)",
1246 attrs, req->controls,
1247 ac, get_search_callback,
1248 req);
1249 LDB_REQ_SET_LOCATION(search_req);
1250 if (ret != LDB_SUCCESS) {
1251 return ret;
1254 ac->step_fn = objectclass_do_delete;
1256 return ldb_next_request(ac->module, search_req);
1259 static int objectclass_do_delete(struct oc_context *ac)
1261 struct ldb_context *ldb;
1262 struct ldb_dn *dn;
1263 int32_t systemFlags;
1264 bool isCriticalSystemObject;
1265 int ret;
1267 ldb = ldb_module_get_ctx(ac->module);
1269 /* Check if we have a valid entry - this check is needed since
1270 * we don't get a LDB_ERR_NO_SUCH_OBJECT error. */
1271 if (ac->search_res == NULL) {
1272 ldb_asprintf_errstring(ldb, "objectclass: Cannot delete %s, entry does not exist!",
1273 ldb_dn_get_linearized(ac->req->op.del.dn));
1274 return LDB_ERR_NO_SUCH_OBJECT;
1277 /* DC's ntDSDSA object */
1278 if (ldb_dn_compare(ac->req->op.del.dn, samdb_ntds_settings_dn(ldb, ac)) == 0) {
1279 ldb_asprintf_errstring(ldb, "objectclass: Cannot delete %s, it's the DC's ntDSDSA object!",
1280 ldb_dn_get_linearized(ac->req->op.del.dn));
1281 return LDB_ERR_UNWILLING_TO_PERFORM;
1284 /* DC's rIDSet object */
1285 /* Perform this check only when it does exist - this is needed in order
1286 * to don't let existing provisions break. */
1287 ret = samdb_rid_set_dn(ldb, ac, &dn);
1288 if ((ret != LDB_SUCCESS) && (ret != LDB_ERR_NO_SUCH_OBJECT)) {
1289 return ret;
1291 if (ret == LDB_SUCCESS) {
1292 if (ldb_dn_compare(ac->req->op.del.dn, dn) == 0) {
1293 talloc_free(dn);
1294 ldb_asprintf_errstring(ldb, "objectclass: Cannot delete %s, it's the DC's rIDSet object!",
1295 ldb_dn_get_linearized(ac->req->op.del.dn));
1296 return LDB_ERR_UNWILLING_TO_PERFORM;
1298 talloc_free(dn);
1301 /* Only trusted request from system account are allowed to delete
1302 * deleted objects.
1304 if (ldb_msg_check_string_attribute(ac->search_res->message, "isDeleted", "TRUE") &&
1305 (ldb_req_is_untrusted(ac->req) ||
1306 !dsdb_module_am_system(ac->module))) {
1307 ldb_asprintf_errstring(ldb, "Delete of '%s' failed",
1308 ldb_dn_get_linearized(ac->req->op.del.dn));
1309 return LDB_ERR_UNWILLING_TO_PERFORM;
1312 /* crossRef objects regarding config, schema and default domain NCs */
1313 if (samdb_find_attribute(ldb, ac->search_res->message, "objectClass",
1314 "crossRef") != NULL) {
1315 dn = ldb_msg_find_attr_as_dn(ldb, ac, ac->search_res->message,
1316 "nCName");
1317 if ((ldb_dn_compare(dn, ldb_get_default_basedn(ldb)) == 0) ||
1318 (ldb_dn_compare(dn, ldb_get_config_basedn(ldb)) == 0)) {
1319 talloc_free(dn);
1321 ldb_asprintf_errstring(ldb, "objectclass: Cannot delete %s, it's a crossRef object to the main or configuration partition!",
1322 ldb_dn_get_linearized(ac->req->op.del.dn));
1323 return LDB_ERR_NOT_ALLOWED_ON_NON_LEAF;
1325 if (ldb_dn_compare(dn, ldb_get_schema_basedn(ldb)) == 0) {
1326 talloc_free(dn);
1328 ldb_asprintf_errstring(ldb, "objectclass: Cannot delete %s, it's a crossRef object to the schema partition!",
1329 ldb_dn_get_linearized(ac->req->op.del.dn));
1330 return LDB_ERR_UNWILLING_TO_PERFORM;
1332 talloc_free(dn);
1335 /* systemFlags */
1337 systemFlags = ldb_msg_find_attr_as_int(ac->search_res->message,
1338 "systemFlags", 0);
1339 if ((systemFlags & SYSTEM_FLAG_DISALLOW_DELETE) != 0) {
1340 ldb_asprintf_errstring(ldb, "objectclass: Cannot delete %s, it isn't permitted!",
1341 ldb_dn_get_linearized(ac->req->op.del.dn));
1342 return LDB_ERR_UNWILLING_TO_PERFORM;
1345 /* isCriticalSystemObject - but this only applies on tree delete
1346 * operations - MS-ADTS 3.1.1.5.5.7.2 */
1347 if (ldb_request_get_control(ac->req, LDB_CONTROL_TREE_DELETE_OID) != NULL) {
1348 isCriticalSystemObject = ldb_msg_find_attr_as_bool(ac->search_res->message,
1349 "isCriticalSystemObject", false);
1350 if (isCriticalSystemObject) {
1352 * Following the explaination from Microsoft
1353 * https://lists.samba.org/archive/cifs-protocol/2011-August/002046.html
1354 * "I finished the investigation on this behavior.
1355 * As per MS-ADTS 3.1.5.5.7.2 , when a tree deletion is performed ,
1356 * every object in the tree will be checked to see if it has isCriticalSystemObject
1357 * set to TRUE, including the root node on which the delete operation is performed
1358 * But there is an exception if the root object is a SAM specific objects(3.1.1.5.2.3 MS-ADTS)
1359 * Its deletion is done through SAM manger and isCriticalSystemObject attribute is not checked
1360 * The root node of the tree delete in your case is CN=ARES,OU=Domain Controllers,DC=w2k8r2,DC=home,DC=matws,DC=net
1361 * which is a SAM object with user class. Therefore the tree deletion is performed without any error
1364 if (samdb_find_attribute(ldb, ac->search_res->message, "objectClass", "group") == NULL &&
1365 samdb_find_attribute(ldb, ac->search_res->message, "objectClass", "samDomain") == NULL &&
1366 samdb_find_attribute(ldb, ac->search_res->message, "objectClass", "samServer") == NULL &&
1367 samdb_find_attribute(ldb, ac->search_res->message, "objectClass", "user") == NULL) {
1368 ldb_asprintf_errstring(ldb,
1369 "objectclass: Cannot tree-delete %s, it's a critical system object!",
1370 ldb_dn_get_linearized(ac->req->op.del.dn));
1371 return LDB_ERR_UNWILLING_TO_PERFORM;
1376 return ldb_next_request(ac->module, ac->req);
1379 static int objectclass_init(struct ldb_module *module)
1381 struct ldb_context *ldb = ldb_module_get_ctx(module);
1382 int ret;
1384 /* Init everything else */
1385 ret = ldb_next_init(module);
1386 if (ret != LDB_SUCCESS) {
1387 return ret;
1390 /* Look for the opaque to indicate we might have to cut down the DN of defaultObjectCategory */
1391 ldb_module_set_private(module, ldb_get_opaque(ldb, DSDB_EXTENDED_DN_STORE_FORMAT_OPAQUE_NAME));
1393 ret = ldb_mod_register_control(module, LDB_CONTROL_RODC_DCPROMO_OID);
1394 if (ret != LDB_SUCCESS) {
1395 ldb_debug(ldb, LDB_DEBUG_ERROR,
1396 "objectclass_init: Unable to register control DCPROMO with rootdse\n");
1397 return ldb_operr(ldb);
1400 return ret;
1403 static const struct ldb_module_ops ldb_objectclass_module_ops = {
1404 .name = "objectclass",
1405 .add = objectclass_add,
1406 .modify = objectclass_modify,
1407 .rename = objectclass_rename,
1408 .del = objectclass_delete,
1409 .init_context = objectclass_init
1412 int ldb_objectclass_module_init(const char *version)
1414 LDB_MODULE_CHECK_VERSION(version);
1415 return ldb_register_module(&ldb_objectclass_module_ops);