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/>.
25 * Component: objectClass sorting and constraint checking module
28 * - sort the objectClass attribute into the class
29 * hierarchy and perform constraint checks (correct RDN name,
31 * - fix DNs into 'standard' case
32 * - Add objectCategory and some other attribute defaults
34 * Author: Andrew Bartlett
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"
53 struct ldb_module *module;
54 struct ldb_request *req;
55 const struct dsdb_schema *schema;
57 struct ldb_reply *search_res;
58 struct ldb_reply *search_res2;
60 int (*step_fn)(struct oc_context *);
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);
79 ac->schema = dsdb_get_schema(ldb, ac);
84 static int objectclass_do_add(struct oc_context *ac);
87 * This checks if we have unrelated object classes in our entry's "objectClass"
88 * attribute. That means "unsatisfied" abstract classes (no concrete subclass)
89 * or two or more disjunct structural ones.
90 * If one of these conditions are true, blame.
92 static int check_unrelated_objectclasses(struct ldb_module *module,
93 const struct dsdb_schema *schema,
94 const struct dsdb_class *struct_objectclass,
95 struct ldb_message_element *objectclass_element)
97 struct ldb_context *ldb = ldb_module_get_ctx(module);
101 if (schema == NULL) {
105 for (i = 0; i < objectclass_element->num_values; i++) {
106 const struct dsdb_class *tmp_class = dsdb_class_by_lDAPDisplayName_ldb_val(schema,
107 &objectclass_element->values[i]);
108 const struct dsdb_class *tmp_class2 = struct_objectclass;
110 /* Pointer comparison can be used due to the same schema str. */
111 if (tmp_class == NULL ||
112 tmp_class == struct_objectclass ||
113 tmp_class->objectClassCategory > 2 ||
114 ldb_attr_cmp(tmp_class->lDAPDisplayName, "top") == 0) {
120 ldb_attr_cmp(tmp_class2->lDAPDisplayName, "top") != 0) {
121 tmp_class2 = dsdb_class_by_lDAPDisplayName(schema,
122 tmp_class2->subClassOf);
123 if (tmp_class2 == tmp_class) {
131 ldb_asprintf_errstring(ldb,
132 "objectclass: the objectclass '%s' seems to be unrelated to %s!",
133 tmp_class->lDAPDisplayName,
134 struct_objectclass->lDAPDisplayName);
135 return LDB_ERR_OBJECT_CLASS_VIOLATION;
141 static int get_search_callback(struct ldb_request *req, struct ldb_reply *ares)
143 struct ldb_context *ldb;
144 struct oc_context *ac;
147 ac = talloc_get_type(req->context, struct oc_context);
148 ldb = ldb_module_get_ctx(ac->module);
151 return ldb_module_done(ac->req, NULL, NULL,
152 LDB_ERR_OPERATIONS_ERROR);
154 if (ares->error != LDB_SUCCESS &&
155 ares->error != LDB_ERR_NO_SUCH_OBJECT) {
156 return ldb_module_done(ac->req, ares->controls,
157 ares->response, ares->error);
160 ldb_reset_err_string(ldb);
162 switch (ares->type) {
163 case LDB_REPLY_ENTRY:
164 if (ac->search_res != NULL) {
165 ldb_set_errstring(ldb, "Too many results");
167 return ldb_module_done(ac->req, NULL, NULL,
168 LDB_ERR_OPERATIONS_ERROR);
171 ac->search_res = talloc_steal(ac, ares);
174 case LDB_REPLY_REFERRAL:
181 ret = ac->step_fn(ac);
182 if (ret != LDB_SUCCESS) {
183 return ldb_module_done(ac->req, NULL, NULL, ret);
191 /* Fix up the DN to be in the standard form, taking particular care to match the parent DN
193 This should mean that if the parent is:
194 CN=Users,DC=samba,DC=example,DC=com
195 and a proposed child is
196 cn=Admins ,cn=USERS,dc=Samba,dc=example,dc=COM
198 The resulting DN should be:
200 CN=Admins,CN=Users,DC=samba,DC=example,DC=com
203 static int fix_dn(struct ldb_context *ldb,
205 struct ldb_dn *newdn, struct ldb_dn *parent_dn,
206 struct ldb_dn **fixed_dn)
208 char *upper_rdn_attr;
209 const struct ldb_val *rdn_val;
211 /* Fix up the DN to be in the standard form, taking particular care to
212 * match the parent DN */
213 *fixed_dn = ldb_dn_copy(mem_ctx, parent_dn);
214 if (*fixed_dn == NULL) {
218 /* We need the attribute name in upper case */
219 upper_rdn_attr = strupper_talloc(*fixed_dn,
220 ldb_dn_get_rdn_name(newdn));
221 if (upper_rdn_attr == NULL) {
225 /* Create a new child */
226 if (ldb_dn_add_child_fmt(*fixed_dn, "X=X") == false) {
227 return ldb_operr(ldb);
230 rdn_val = ldb_dn_get_rdn_val(newdn);
231 if (rdn_val == NULL) {
232 return ldb_operr(ldb);
236 /* the rules for rDN length constraints are more complex than
237 this. Until we understand them we need to leave this
238 constraint out. Otherwise we break replication, as windows
239 does sometimes send us rDNs longer than 64 */
240 if (!rdn_val || rdn_val->length > 64) {
241 DEBUG(2,(__location__ ": WARNING: rDN longer than 64 limit for '%s'\n", ldb_dn_get_linearized(newdn)));
246 /* And replace it with CN=foo (we need the attribute in upper case) */
247 return ldb_dn_set_component(*fixed_dn, 0, upper_rdn_attr, *rdn_val);
251 static int objectclass_add(struct ldb_module *module, struct ldb_request *req)
253 struct ldb_context *ldb;
254 struct ldb_request *search_req;
255 struct oc_context *ac;
256 struct ldb_dn *parent_dn;
257 const struct ldb_val *val;
259 static const char * const parent_attrs[] = { "objectClass", NULL };
261 ldb = ldb_module_get_ctx(module);
263 ldb_debug(ldb, LDB_DEBUG_TRACE, "objectclass_add\n");
265 /* do not manipulate our control entries */
266 if (ldb_dn_is_special(req->op.add.message->dn)) {
267 return ldb_next_request(module, req);
270 /* An add operation on the basedn without "NC-add" operation isn't
272 if (ldb_dn_compare(ldb_get_default_basedn(ldb), req->op.add.message->dn) == 0) {
273 unsigned int instanceType;
275 instanceType = ldb_msg_find_attr_as_uint(req->op.add.message,
277 if (!(instanceType & INSTANCE_TYPE_IS_NC_HEAD)) {
279 /* When we are trying to readd the root basedn then
280 * this is denied, but with an interesting mechanism:
281 * there is generated a referral with the last
282 * component value as hostname. */
283 val = ldb_dn_get_component_val(req->op.add.message->dn,
284 ldb_dn_get_comp_num(req->op.add.message->dn) - 1);
286 return ldb_operr(ldb);
288 referral_uri = talloc_asprintf(req, "ldap://%s/%s", val->data,
289 ldb_dn_get_linearized(req->op.add.message->dn));
290 if (referral_uri == NULL) {
291 return ldb_module_oom(module);
294 return ldb_module_send_referral(req, referral_uri);
298 ac = oc_init_context(module, req);
300 return ldb_operr(ldb);
303 /* If there isn't a parent, just go on to the add processing */
304 if (ldb_dn_get_comp_num(ac->req->op.add.message->dn) == 1) {
305 return objectclass_do_add(ac);
308 /* get copy of parent DN */
309 parent_dn = ldb_dn_get_parent(ac, ac->req->op.add.message->dn);
310 if (parent_dn == NULL) {
311 return ldb_operr(ldb);
314 ret = ldb_build_search_req(&search_req, ldb,
315 ac, parent_dn, LDB_SCOPE_BASE,
316 "(objectClass=*)", parent_attrs,
318 ac, get_search_callback,
320 LDB_REQ_SET_LOCATION(search_req);
321 if (ret != LDB_SUCCESS) {
325 ret = dsdb_request_add_controls(search_req,
326 DSDB_FLAG_AS_SYSTEM |
327 DSDB_SEARCH_SHOW_RECYCLED);
328 if (ret != LDB_SUCCESS) {
332 ac->step_fn = objectclass_do_add;
334 return ldb_next_request(ac->module, search_req);
339 check if this is a special RODC nTDSDSA add
341 static bool check_rodc_ntdsdsa_add(struct oc_context *ac,
342 const struct dsdb_class *objectclass)
344 struct ldb_control *rodc_control;
346 if (ldb_attr_cmp(objectclass->lDAPDisplayName, "nTDSDSA") != 0) {
349 rodc_control = ldb_request_get_control(ac->req, LDB_CONTROL_RODC_DCPROMO_OID);
354 rodc_control->critical = false;
358 static int objectclass_do_add(struct oc_context *ac)
360 struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
361 struct ldb_request *add_req;
362 struct ldb_message_element *objectclass_element, *el;
363 struct ldb_message *msg;
364 const char *rdn_name = NULL;
366 const struct dsdb_class *objectclass;
367 struct ldb_dn *objectcategory;
368 int32_t systemFlags = 0;
373 msg = ldb_msg_copy_shallow(ac, ac->req->op.add.message);
375 return ldb_module_oom(ac->module);
378 /* Check if we have a valid parent - this check is needed since
379 * we don't get a LDB_ERR_NO_SUCH_OBJECT error. */
380 if (ac->search_res == NULL) {
381 unsigned int instanceType;
383 /* An add operation on partition DNs without "NC-add" operation
385 instanceType = ldb_msg_find_attr_as_uint(msg, "instanceType",
387 if (!(instanceType & INSTANCE_TYPE_IS_NC_HEAD)) {
388 ldb_asprintf_errstring(ldb, "objectclass: Cannot add %s, parent does not exist!",
389 ldb_dn_get_linearized(msg->dn));
390 return LDB_ERR_NO_SUCH_OBJECT;
393 /* Don't keep any error messages - we've to add a partition */
394 ldb_set_errstring(ldb, NULL);
396 /* Fix up the DN to be in the standard form, taking
397 * particular care to match the parent DN */
398 ret = fix_dn(ldb, msg,
399 ac->req->op.add.message->dn,
400 ac->search_res->message->dn,
402 if (ret != LDB_SUCCESS) {
403 ldb_asprintf_errstring(ldb, "objectclass: Could not munge DN %s into normal form",
404 ldb_dn_get_linearized(ac->req->op.add.message->dn));
409 if (ac->schema != NULL) {
410 unsigned int linkID = 0;
412 * Notice: by the normalization function call in "ldb_request()"
413 * case "LDB_ADD" we have always only *one* "objectClass"
414 * attribute at this stage!
417 objectclass_element = ldb_msg_find_element(msg, "objectClass");
418 if (!objectclass_element) {
419 ldb_asprintf_errstring(ldb, "objectclass: Cannot add %s, no objectclass specified!",
420 ldb_dn_get_linearized(msg->dn));
421 return LDB_ERR_OBJECT_CLASS_VIOLATION;
423 if (objectclass_element->num_values == 0) {
424 ldb_asprintf_errstring(ldb, "objectclass: Cannot add %s, at least one (structural) objectclass has to be specified!",
425 ldb_dn_get_linearized(msg->dn));
426 return LDB_ERR_CONSTRAINT_VIOLATION;
429 /* Now do the sorting */
430 ret = dsdb_sort_objectClass_attr(ldb, ac->schema,
431 objectclass_element, msg,
432 objectclass_element);
433 if (ret != LDB_SUCCESS) {
438 * Get the new top-most structural object class and check for
439 * unrelated structural classes
441 objectclass = dsdb_get_last_structural_class(ac->schema,
442 objectclass_element);
443 if (objectclass == NULL) {
444 ldb_asprintf_errstring(ldb,
445 "Failed to find a structural class for %s",
446 ldb_dn_get_linearized(msg->dn));
447 return LDB_ERR_UNWILLING_TO_PERFORM;
450 ret = check_unrelated_objectclasses(ac->module, ac->schema,
452 objectclass_element);
453 if (ret != LDB_SUCCESS) {
457 rdn_name = ldb_dn_get_rdn_name(msg->dn);
458 if (rdn_name == NULL) {
459 return ldb_operr(ldb);
462 for (i = 0; (!found) && (i < objectclass_element->num_values);
464 const struct dsdb_class *tmp_class =
465 dsdb_class_by_lDAPDisplayName_ldb_val(ac->schema,
466 &objectclass_element->values[i]);
468 if (tmp_class == NULL) continue;
470 if (ldb_attr_cmp(rdn_name, tmp_class->rDNAttID) == 0)
474 ldb_asprintf_errstring(ldb,
475 "objectclass: Invalid RDN '%s' for objectclass '%s'!",
476 rdn_name, objectclass->lDAPDisplayName);
477 return LDB_ERR_NAMING_VIOLATION;
480 if (objectclass->systemOnly &&
481 !ldb_request_get_control(ac->req, LDB_CONTROL_RELAX_OID) &&
482 !check_rodc_ntdsdsa_add(ac, objectclass)) {
483 ldb_asprintf_errstring(ldb,
484 "objectclass: object class '%s' is system-only, rejecting creation of '%s'!",
485 objectclass->lDAPDisplayName,
486 ldb_dn_get_linearized(msg->dn));
487 return LDB_ERR_UNWILLING_TO_PERFORM;
490 if (ac->search_res && ac->search_res->message) {
491 struct ldb_message_element *oc_el
492 = ldb_msg_find_element(ac->search_res->message, "objectClass");
494 bool allowed_class = false;
495 for (i=0; allowed_class == false && oc_el && i < oc_el->num_values; i++) {
496 const struct dsdb_class *sclass;
498 sclass = dsdb_class_by_lDAPDisplayName_ldb_val(ac->schema,
501 /* We don't know this class? what is going on? */
504 for (j=0; sclass->systemPossibleInferiors && sclass->systemPossibleInferiors[j]; j++) {
505 if (ldb_attr_cmp(objectclass->lDAPDisplayName, sclass->systemPossibleInferiors[j]) == 0) {
506 allowed_class = true;
512 if (!allowed_class) {
513 ldb_asprintf_errstring(ldb, "structural objectClass %s is not a valid child class for %s",
514 objectclass->lDAPDisplayName, ldb_dn_get_linearized(ac->search_res->message->dn));
515 return LDB_ERR_NAMING_VIOLATION;
519 objectcategory = ldb_msg_find_attr_as_dn(ldb, ac, msg,
521 if (objectcategory == NULL) {
522 struct dsdb_extended_dn_store_format *dn_format =
523 talloc_get_type(ldb_module_get_private(ac->module),
524 struct dsdb_extended_dn_store_format);
525 if (dn_format && dn_format->store_extended_dn_in_ldb == false) {
526 /* Strip off extended components */
527 struct ldb_dn *dn = ldb_dn_new(ac, ldb,
528 objectclass->defaultObjectCategory);
529 value = ldb_dn_alloc_linearized(msg, dn);
532 value = talloc_strdup(msg,
533 objectclass->defaultObjectCategory);
536 return ldb_module_oom(ac->module);
539 ret = ldb_msg_add_string(msg, "objectCategory", value);
540 if (ret != LDB_SUCCESS) {
544 const struct dsdb_class *ocClass =
545 dsdb_class_by_cn_ldb_val(ac->schema,
546 ldb_dn_get_rdn_val(objectcategory));
547 if (ocClass != NULL) {
548 struct ldb_dn *dn = ldb_dn_new(ac, ldb,
549 ocClass->defaultObjectCategory);
550 if (ldb_dn_compare(objectcategory, dn) != 0) {
554 talloc_free(objectcategory);
555 if (ocClass == NULL) {
556 ldb_asprintf_errstring(ldb, "objectclass: Cannot add %s, 'objectCategory' attribute invalid!",
557 ldb_dn_get_linearized(msg->dn));
558 return LDB_ERR_OBJECT_CLASS_VIOLATION;
562 if (!ldb_msg_find_element(msg, "showInAdvancedViewOnly") && (objectclass->defaultHidingValue == true)) {
563 ldb_msg_add_string(msg, "showInAdvancedViewOnly",
567 /* There are very special rules for systemFlags, see MS-ADTS
568 * MS-ADTS 3.1.1.5.2.4 */
570 el = ldb_msg_find_element(msg, "systemFlags");
571 if ((el != NULL) && (el->num_values > 1)) {
572 ldb_asprintf_errstring(ldb, "objectclass: Cannot add %s, 'systemFlags' attribute multivalued!",
573 ldb_dn_get_linearized(msg->dn));
574 return LDB_ERR_CONSTRAINT_VIOLATION;
577 systemFlags = ldb_msg_find_attr_as_int(msg, "systemFlags", 0);
579 ldb_msg_remove_attr(msg, "systemFlags");
581 /* Only the following flags may be set by a client */
582 if (ldb_request_get_control(ac->req,
583 LDB_CONTROL_RELAX_OID) == NULL) {
584 systemFlags &= ( SYSTEM_FLAG_CONFIG_ALLOW_RENAME
585 | SYSTEM_FLAG_CONFIG_ALLOW_MOVE
586 | SYSTEM_FLAG_CONFIG_ALLOW_LIMITED_MOVE
587 | SYSTEM_FLAG_ATTR_IS_RDN );
590 /* But the last one ("ATTR_IS_RDN") is only allowed on
591 * "attributeSchema" objects. So truncate if it does not fit. */
592 if (ldb_attr_cmp(objectclass->lDAPDisplayName, "attributeSchema") != 0) {
593 systemFlags &= ~SYSTEM_FLAG_ATTR_IS_RDN;
596 if (ldb_attr_cmp(objectclass->lDAPDisplayName, "server") == 0) {
597 systemFlags |= (int32_t)(SYSTEM_FLAG_DISALLOW_MOVE_ON_DELETE | SYSTEM_FLAG_CONFIG_ALLOW_RENAME | SYSTEM_FLAG_CONFIG_ALLOW_LIMITED_MOVE);
598 } else if (ldb_attr_cmp(objectclass->lDAPDisplayName, "site") == 0
599 || ldb_attr_cmp(objectclass->lDAPDisplayName, "serversContainer") == 0
600 || ldb_attr_cmp(objectclass->lDAPDisplayName, "nTDSDSA") == 0) {
601 if (ldb_attr_cmp(objectclass->lDAPDisplayName, "site") == 0)
602 systemFlags |= (int32_t)(SYSTEM_FLAG_CONFIG_ALLOW_RENAME);
603 systemFlags |= (int32_t)(SYSTEM_FLAG_DISALLOW_MOVE_ON_DELETE);
604 } else if (ldb_attr_cmp(objectclass->lDAPDisplayName, "siteLink") == 0
605 || ldb_attr_cmp(objectclass->lDAPDisplayName, "subnet") == 0
606 || ldb_attr_cmp(objectclass->lDAPDisplayName, "siteLinkBridge") == 0
607 || ldb_attr_cmp(objectclass->lDAPDisplayName, "nTDSConnection") == 0) {
608 systemFlags |= (int32_t)(SYSTEM_FLAG_CONFIG_ALLOW_RENAME);
610 /* TODO: If parent object is site or subnet, also add (SYSTEM_FLAG_CONFIG_ALLOW_RENAME) */
612 linkID = ldb_msg_find_attr_as_int(msg, "linkID", 0);
613 if (linkID > 0 && linkID % 2 == 1) {
614 systemFlags |= DS_FLAG_ATTR_NOT_REPLICATED;
617 if (el || systemFlags != 0) {
618 ret = samdb_msg_add_int(ldb, msg, msg, "systemFlags",
620 if (ret != LDB_SUCCESS) {
625 /* make sure that "isCriticalSystemObject" is not specified! */
626 el = ldb_msg_find_element(msg, "isCriticalSystemObject");
628 !ldb_request_get_control(ac->req, LDB_CONTROL_RELAX_OID)) {
629 ldb_set_errstring(ldb,
630 "objectclass: 'isCriticalSystemObject' must not be specified!");
631 return LDB_ERR_UNWILLING_TO_PERFORM;
635 ret = ldb_build_add_req(&add_req, ldb, ac,
638 ac->req, dsdb_next_callback,
640 LDB_REQ_SET_LOCATION(add_req);
641 if (ret != LDB_SUCCESS) {
645 /* perform the add */
646 return ldb_next_request(ac->module, add_req);
649 static int oc_modify_callback(struct ldb_request *req,
650 struct ldb_reply *ares);
651 static int objectclass_do_mod(struct oc_context *ac);
653 static int objectclass_modify(struct ldb_module *module, struct ldb_request *req)
655 struct ldb_context *ldb = ldb_module_get_ctx(module);
656 struct ldb_message_element *objectclass_element;
657 struct ldb_message *msg;
658 struct ldb_request *down_req;
659 struct oc_context *ac;
660 bool oc_changes = false;
663 ldb_debug(ldb, LDB_DEBUG_TRACE, "objectclass_modify\n");
665 /* do not manipulate our control entries */
666 if (ldb_dn_is_special(req->op.mod.message->dn)) {
667 return ldb_next_request(module, req);
670 /* As with the "real" AD we don't accept empty messages */
671 if (req->op.mod.message->num_elements == 0) {
672 ldb_set_errstring(ldb, "objectclass: modify message must have "
673 "elements/attributes!");
674 return LDB_ERR_UNWILLING_TO_PERFORM;
677 ac = oc_init_context(module, req);
679 return ldb_operr(ldb);
682 /* Without schema, there isn't much to do here */
683 if (ac->schema == NULL) {
685 return ldb_next_request(module, req);
688 msg = ldb_msg_copy_shallow(ac, req->op.mod.message);
690 return ldb_module_oom(ac->module);
693 /* For now change everything except the objectclasses */
695 objectclass_element = ldb_msg_find_element(msg, "objectClass");
696 if (objectclass_element != NULL) {
697 ldb_msg_remove_attr(msg, "objectClass");
701 /* MS-ADTS 3.1.1.5.3.5 - on a forest level < 2003 we do allow updates
702 * only on application NCs - not on the default ones */
704 (dsdb_forest_functional_level(ldb) < DS_DOMAIN_FUNCTION_2003)) {
705 struct ldb_dn *nc_root;
707 ret = dsdb_find_nc_root(ldb, ac, req->op.mod.message->dn,
709 if (ret != LDB_SUCCESS) {
713 if ((ldb_dn_compare(nc_root, ldb_get_default_basedn(ldb)) == 0) ||
714 (ldb_dn_compare(nc_root, ldb_get_config_basedn(ldb)) == 0) ||
715 (ldb_dn_compare(nc_root, ldb_get_schema_basedn(ldb)) == 0)) {
716 ldb_set_errstring(ldb,
717 "objectclass: object class changes on objects under the standard name contexts not allowed!");
718 return LDB_ERR_UNWILLING_TO_PERFORM;
721 talloc_free(nc_root);
725 ret = ldb_build_mod_req(&down_req, ldb, ac,
731 ret = ldb_build_mod_req(&down_req, ldb, ac,
737 LDB_REQ_SET_LOCATION(down_req);
738 if (ret != LDB_SUCCESS) {
742 return ldb_next_request(module, down_req);
745 static int oc_modify_callback(struct ldb_request *req, struct ldb_reply *ares)
747 static const char * const attrs[] = { "objectClass", NULL };
748 struct ldb_context *ldb;
749 struct ldb_request *search_req;
750 struct oc_context *ac;
753 ac = talloc_get_type(req->context, struct oc_context);
754 ldb = ldb_module_get_ctx(ac->module);
757 return ldb_module_done(ac->req, NULL, NULL,
758 LDB_ERR_OPERATIONS_ERROR);
761 if (ares->type == LDB_REPLY_REFERRAL) {
762 return ldb_module_send_referral(ac->req, ares->referral);
765 if (ares->error != LDB_SUCCESS) {
766 return ldb_module_done(ac->req, ares->controls,
767 ares->response, ares->error);
770 if (ares->type != LDB_REPLY_DONE) {
772 return ldb_module_done(ac->req, NULL, NULL,
773 LDB_ERR_OPERATIONS_ERROR);
778 /* this looks up the real existing object for fetching some important
779 * information (objectclasses) */
780 ret = ldb_build_search_req(&search_req, ldb,
781 ac, ac->req->op.mod.message->dn,
785 ac, get_search_callback,
787 LDB_REQ_SET_LOCATION(search_req);
788 if (ret != LDB_SUCCESS) {
789 return ldb_module_done(ac->req, NULL, NULL, ret);
792 ret = dsdb_request_add_controls(search_req,
793 DSDB_FLAG_AS_SYSTEM |
794 DSDB_SEARCH_SHOW_RECYCLED);
795 if (ret != LDB_SUCCESS) {
796 return ldb_module_done(ac->req, NULL, NULL, ret);
799 ac->step_fn = objectclass_do_mod;
801 ret = ldb_next_request(ac->module, search_req);
802 if (ret != LDB_SUCCESS) {
803 return ldb_module_done(ac->req, NULL, NULL, ret);
809 static int objectclass_do_mod(struct oc_context *ac)
811 struct ldb_context *ldb;
812 struct ldb_request *mod_req;
813 struct ldb_message_element *oc_el_entry, *oc_el_change;
814 struct ldb_val *vals;
815 struct ldb_message *msg;
816 const struct dsdb_class *current_structural_objectclass;
817 const struct dsdb_class *objectclass;
818 unsigned int i, j, k;
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,
831 if (oc_el_entry == NULL) {
832 /* existing entry without a valid object class? */
833 return ldb_operr(ldb);
837 * Get the current new top-most structural object class
839 * We must not allow this to change
842 current_structural_objectclass
843 = dsdb_get_last_structural_class(ac->schema,
845 if (current_structural_objectclass == NULL) {
846 ldb_asprintf_errstring(ldb,
847 "objectclass: cannot find current structural objectclass on %s!",
848 ldb_dn_get_linearized(ac->search_res->message->dn));
849 return LDB_ERR_OBJECT_CLASS_VIOLATION;
852 /* use a new message structure */
853 msg = ldb_msg_new(ac);
855 return ldb_module_oom(ac->module);
858 msg->dn = ac->req->op.mod.message->dn;
860 /* We've to walk over all "objectClass" message elements */
861 for (k = 0; k < ac->req->op.mod.message->num_elements; k++) {
862 if (ldb_attr_cmp(ac->req->op.mod.message->elements[k].name,
863 "objectClass") != 0) {
867 oc_el_change = &ac->req->op.mod.message->elements[k];
869 switch (oc_el_change->flags & LDB_FLAG_MOD_MASK) {
870 case LDB_FLAG_MOD_ADD:
871 /* Merge the two message elements */
872 for (i = 0; i < oc_el_change->num_values; i++) {
873 for (j = 0; j < oc_el_entry->num_values; j++) {
874 if (ldb_attr_cmp((char *)oc_el_change->values[i].data,
875 (char *)oc_el_entry->values[j].data) == 0) {
876 ldb_asprintf_errstring(ldb,
877 "objectclass: cannot re-add an existing objectclass: '%.*s'!",
878 (int)oc_el_change->values[i].length,
879 (const char *)oc_el_change->values[i].data);
880 return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS;
883 /* append the new object class value - code was
884 * copied from "ldb_msg_add_value" */
885 vals = talloc_realloc(oc_el_entry, oc_el_entry->values,
887 oc_el_entry->num_values + 1);
889 return ldb_module_oom(ac->module);
891 oc_el_entry->values = vals;
892 oc_el_entry->values[oc_el_entry->num_values] =
893 oc_el_change->values[i];
894 ++(oc_el_entry->num_values);
899 case LDB_FLAG_MOD_REPLACE:
901 * In this case the new "oc_el_entry" is simply
904 oc_el_entry = oc_el_change;
908 case LDB_FLAG_MOD_DELETE:
909 /* Merge the two message elements */
910 for (i = 0; i < oc_el_change->num_values; i++) {
912 for (j = 0; j < oc_el_entry->num_values; j++) {
913 if (ldb_attr_cmp((char *)oc_el_change->values[i].data,
914 (char *)oc_el_entry->values[j].data) == 0) {
916 /* delete the object class value
917 * - code was copied from
918 * "ldb_msg_remove_element" */
919 if (j != oc_el_entry->num_values - 1) {
920 memmove(&oc_el_entry->values[j],
921 &oc_el_entry->values[j+1],
922 ((oc_el_entry->num_values-1) - j)*sizeof(struct ldb_val));
924 --(oc_el_entry->num_values);
929 /* we cannot delete a not existing
931 ldb_asprintf_errstring(ldb,
932 "objectclass: cannot delete this objectclass: '%.*s'!",
933 (int)oc_el_change->values[i].length,
934 (const char *)oc_el_change->values[i].data);
935 return LDB_ERR_NO_SUCH_ATTRIBUTE;
942 /* Now do the sorting */
943 ret = dsdb_sort_objectClass_attr(ldb, ac->schema, oc_el_entry,
945 if (ret != LDB_SUCCESS) {
950 * Get the new top-most structural object class and check for
951 * unrelated structural classes
953 objectclass = dsdb_get_last_structural_class(ac->schema,
955 if (objectclass == NULL) {
956 ldb_set_errstring(ldb,
957 "objectclass: cannot delete all structural objectclasses!");
958 return LDB_ERR_OBJECT_CLASS_VIOLATION;
962 * Has (so far, we re-check for each and every
963 * "objectclass" in the message) the structural
964 * objectclass changed?
967 if (objectclass != current_structural_objectclass) {
969 = ldb_dn_get_linearized(ac->search_res->message->dn);
970 ldb_asprintf_errstring(ldb,
971 "objectclass: not permitted "
972 "to change the structural "
973 "objectClass on %s [%s] => [%s]!",
975 current_structural_objectclass->lDAPDisplayName,
976 objectclass->lDAPDisplayName);
977 return LDB_ERR_OBJECT_CLASS_VIOLATION;
980 /* Check for unrelated objectclasses */
981 ret = check_unrelated_objectclasses(ac->module, ac->schema,
984 if (ret != LDB_SUCCESS) {
989 /* Now add the new object class attribute to the change message */
990 ret = ldb_msg_add(msg, oc_el_entry, LDB_FLAG_MOD_REPLACE);
991 if (ret != LDB_SUCCESS) {
992 ldb_module_oom(ac->module);
996 /* Now we have the real and definitive change left to do */
998 ret = ldb_build_mod_req(&mod_req, ldb, ac,
1001 ac->req, dsdb_next_callback,
1003 LDB_REQ_SET_LOCATION(mod_req);
1004 if (ret != LDB_SUCCESS) {
1008 return ldb_next_request(ac->module, mod_req);
1011 static int objectclass_do_rename(struct oc_context *ac);
1013 static int objectclass_rename(struct ldb_module *module, struct ldb_request *req)
1015 static const char * const attrs[] = { "objectClass", NULL };
1016 struct ldb_context *ldb;
1017 struct ldb_request *search_req;
1018 struct oc_context *ac;
1019 struct ldb_dn *parent_dn;
1022 ldb = ldb_module_get_ctx(module);
1024 ldb_debug(ldb, LDB_DEBUG_TRACE, "objectclass_rename\n");
1026 /* do not manipulate our control entries */
1027 if (ldb_dn_is_special(req->op.rename.olddn)) {
1028 return ldb_next_request(module, req);
1032 * Bypass the constraint checks when we do have the "DBCHECK" control
1033 * set, so we can force objects under the deleted objects container.
1035 if (ldb_request_get_control(req, DSDB_CONTROL_DBCHECK) != NULL) {
1036 return ldb_next_request(module, req);
1039 ac = oc_init_context(module, req);
1041 return ldb_operr(ldb);
1044 parent_dn = ldb_dn_get_parent(ac, req->op.rename.newdn);
1045 if (parent_dn == NULL) {
1046 ldb_asprintf_errstring(ldb, "objectclass: Cannot rename %s, the parent DN does not exist!",
1047 ldb_dn_get_linearized(req->op.rename.olddn));
1048 return LDB_ERR_NO_SUCH_OBJECT;
1051 /* this looks up the parent object for fetching some important
1052 * information (objectclasses, DN normalisation...) */
1053 ret = ldb_build_search_req(&search_req, ldb,
1054 ac, parent_dn, LDB_SCOPE_BASE,
1057 ac, get_search_callback,
1059 LDB_REQ_SET_LOCATION(search_req);
1060 if (ret != LDB_SUCCESS) {
1064 /* we have to add the show recycled control, as otherwise DRS
1065 deletes will be refused as we will think the target parent
1067 ret = dsdb_request_add_controls(search_req,
1068 DSDB_FLAG_AS_SYSTEM |
1069 DSDB_SEARCH_SHOW_RECYCLED);
1070 if (ret != LDB_SUCCESS) {
1074 ac->step_fn = objectclass_do_rename;
1076 return ldb_next_request(ac->module, search_req);
1079 static int objectclass_do_rename2(struct oc_context *ac);
1081 static int objectclass_do_rename(struct oc_context *ac)
1083 static const char * const attrs[] = { "objectClass", NULL };
1084 struct ldb_context *ldb;
1085 struct ldb_request *search_req;
1088 ldb = ldb_module_get_ctx(ac->module);
1090 /* Check if we have a valid parent - this check is needed since
1091 * we don't get a LDB_ERR_NO_SUCH_OBJECT error. */
1092 if (ac->search_res == NULL) {
1093 ldb_asprintf_errstring(ldb, "objectclass: Cannot rename %s, parent does not exist!",
1094 ldb_dn_get_linearized(ac->req->op.rename.olddn));
1095 return LDB_ERR_OTHER;
1098 /* now assign "search_res2" to the parent entry to have "search_res"
1099 * free for another lookup */
1100 ac->search_res2 = ac->search_res;
1101 ac->search_res = NULL;
1103 /* this looks up the real existing object for fetching some important
1104 * information (objectclasses) */
1105 ret = ldb_build_search_req(&search_req, ldb,
1106 ac, ac->req->op.rename.olddn,
1110 ac, get_search_callback,
1112 LDB_REQ_SET_LOCATION(search_req);
1113 if (ret != LDB_SUCCESS) {
1117 ret = dsdb_request_add_controls(search_req,
1118 DSDB_FLAG_AS_SYSTEM |
1119 DSDB_SEARCH_SHOW_RECYCLED);
1120 if (ret != LDB_SUCCESS) {
1124 ac->step_fn = objectclass_do_rename2;
1126 return ldb_next_request(ac->module, search_req);
1129 static int objectclass_do_rename2(struct oc_context *ac)
1131 struct ldb_context *ldb;
1132 struct ldb_request *rename_req;
1133 struct ldb_dn *fixed_dn;
1136 ldb = ldb_module_get_ctx(ac->module);
1138 /* Check if we have a valid entry - this check is needed since
1139 * we don't get a LDB_ERR_NO_SUCH_OBJECT error. */
1140 if (ac->search_res == NULL) {
1141 ldb_asprintf_errstring(ldb, "objectclass: Cannot rename %s, entry does not exist!",
1142 ldb_dn_get_linearized(ac->req->op.rename.olddn));
1143 return LDB_ERR_NO_SUCH_OBJECT;
1146 if (ac->schema != NULL) {
1147 struct ldb_message_element *oc_el_entry, *oc_el_parent;
1148 const struct dsdb_class *objectclass;
1149 const char *rdn_name;
1150 bool allowed_class = false;
1154 oc_el_entry = ldb_msg_find_element(ac->search_res->message,
1156 if (oc_el_entry == NULL) {
1157 /* existing entry without a valid object class? */
1158 return ldb_operr(ldb);
1160 objectclass = dsdb_get_last_structural_class(ac->schema,
1162 if (objectclass == NULL) {
1163 /* existing entry without a valid object class? */
1164 return ldb_operr(ldb);
1167 rdn_name = ldb_dn_get_rdn_name(ac->req->op.rename.newdn);
1168 if (rdn_name == NULL) {
1169 return ldb_operr(ldb);
1172 for (i = 0; (!found) && (i < oc_el_entry->num_values); i++) {
1173 const struct dsdb_class *tmp_class =
1174 dsdb_class_by_lDAPDisplayName_ldb_val(ac->schema,
1175 &oc_el_entry->values[i]);
1177 if (tmp_class == NULL) continue;
1179 if (ldb_attr_cmp(rdn_name, tmp_class->rDNAttID) == 0)
1183 ldb_asprintf_errstring(ldb,
1184 "objectclass: Invalid RDN '%s' for objectclass '%s'!",
1185 rdn_name, objectclass->lDAPDisplayName);
1186 return LDB_ERR_UNWILLING_TO_PERFORM;
1189 oc_el_parent = ldb_msg_find_element(ac->search_res2->message,
1191 if (oc_el_parent == NULL) {
1192 /* existing entry without a valid object class? */
1193 return ldb_operr(ldb);
1196 for (i=0; allowed_class == false && i < oc_el_parent->num_values; i++) {
1197 const struct dsdb_class *sclass;
1199 sclass = dsdb_class_by_lDAPDisplayName_ldb_val(ac->schema,
1200 &oc_el_parent->values[i]);
1202 /* We don't know this class? what is going on? */
1205 for (j=0; sclass->systemPossibleInferiors && sclass->systemPossibleInferiors[j]; j++) {
1206 if (ldb_attr_cmp(objectclass->lDAPDisplayName, sclass->systemPossibleInferiors[j]) == 0) {
1207 allowed_class = true;
1213 if (!allowed_class) {
1214 ldb_asprintf_errstring(ldb,
1215 "objectclass: structural objectClass %s is not a valid child class for %s",
1216 objectclass->lDAPDisplayName, ldb_dn_get_linearized(ac->search_res2->message->dn));
1217 return LDB_ERR_NAMING_VIOLATION;
1221 /* Ensure we are not trying to rename it to be a child of itself */
1222 if ((ldb_dn_compare_base(ac->req->op.rename.olddn,
1223 ac->req->op.rename.newdn) == 0) &&
1224 (ldb_dn_compare(ac->req->op.rename.olddn,
1225 ac->req->op.rename.newdn) != 0)) {
1226 ldb_asprintf_errstring(ldb, "objectclass: Cannot rename %s to be a child of itself",
1227 ldb_dn_get_linearized(ac->req->op.rename.olddn));
1228 return LDB_ERR_UNWILLING_TO_PERFORM;
1231 /* Fix up the DN to be in the standard form, taking
1232 * particular care to match the parent DN */
1233 ret = fix_dn(ldb, ac,
1234 ac->req->op.rename.newdn,
1235 ac->search_res2->message->dn,
1237 if (ret != LDB_SUCCESS) {
1238 ldb_asprintf_errstring(ldb, "objectclass: Could not munge DN %s into normal form",
1239 ldb_dn_get_linearized(ac->req->op.rename.newdn));
1244 ret = ldb_build_rename_req(&rename_req, ldb, ac,
1245 ac->req->op.rename.olddn, fixed_dn,
1247 ac->req, dsdb_next_callback,
1249 LDB_REQ_SET_LOCATION(rename_req);
1250 if (ret != LDB_SUCCESS) {
1254 /* perform the rename */
1255 return ldb_next_request(ac->module, rename_req);
1258 static int objectclass_do_delete(struct oc_context *ac);
1260 static int objectclass_delete(struct ldb_module *module, struct ldb_request *req)
1262 static const char * const attrs[] = { "nCName", "objectClass",
1265 "isCriticalSystemObject", NULL };
1266 struct ldb_context *ldb;
1267 struct ldb_request *search_req;
1268 struct oc_context *ac;
1271 ldb = ldb_module_get_ctx(module);
1273 ldb_debug(ldb, LDB_DEBUG_TRACE, "objectclass_delete\n");
1275 /* do not manipulate our control entries */
1276 if (ldb_dn_is_special(req->op.del.dn)) {
1277 return ldb_next_request(module, req);
1280 /* Bypass the constraint checks when we do have the "RELAX" control
1282 if (ldb_request_get_control(req, LDB_CONTROL_RELAX_OID) != NULL) {
1283 return ldb_next_request(module, req);
1286 ac = oc_init_context(module, req);
1288 return ldb_operr(ldb);
1291 /* this looks up the entry object for fetching some important
1292 * information (object classes, system flags...) */
1293 ret = ldb_build_search_req(&search_req, ldb,
1294 ac, req->op.del.dn, LDB_SCOPE_BASE,
1297 ac, get_search_callback,
1299 LDB_REQ_SET_LOCATION(search_req);
1300 if (ret != LDB_SUCCESS) {
1304 ret = dsdb_request_add_controls(search_req,
1305 DSDB_FLAG_AS_SYSTEM |
1306 DSDB_SEARCH_SHOW_RECYCLED);
1307 if (ret != LDB_SUCCESS) {
1311 ac->step_fn = objectclass_do_delete;
1313 return ldb_next_request(ac->module, search_req);
1316 static int objectclass_do_delete(struct oc_context *ac)
1318 struct ldb_context *ldb;
1320 int32_t systemFlags;
1321 bool isCriticalSystemObject;
1324 ldb = ldb_module_get_ctx(ac->module);
1326 /* Check if we have a valid entry - this check is needed since
1327 * we don't get a LDB_ERR_NO_SUCH_OBJECT error. */
1328 if (ac->search_res == NULL) {
1329 ldb_asprintf_errstring(ldb, "objectclass: Cannot delete %s, entry does not exist!",
1330 ldb_dn_get_linearized(ac->req->op.del.dn));
1331 return LDB_ERR_NO_SUCH_OBJECT;
1334 /* DC's ntDSDSA object */
1335 if (ldb_dn_compare(ac->req->op.del.dn, samdb_ntds_settings_dn(ldb, ac)) == 0) {
1336 ldb_asprintf_errstring(ldb, "objectclass: Cannot delete %s, it's the DC's ntDSDSA object!",
1337 ldb_dn_get_linearized(ac->req->op.del.dn));
1338 return LDB_ERR_UNWILLING_TO_PERFORM;
1341 /* DC's rIDSet object */
1342 /* Perform this check only when it does exist - this is needed in order
1343 * to don't let existing provisions break, and to delete . */
1344 ret = samdb_rid_set_dn(ldb, ac, &dn);
1345 if ((ret != LDB_SUCCESS) && (ret != LDB_ERR_NO_SUCH_ATTRIBUTE)
1346 && (ret != LDB_ERR_NO_SUCH_OBJECT)) {
1347 ldb_asprintf_errstring(ldb, "objectclass: Unable to determine if %s, is this DC's rIDSet object: %s ",
1348 ldb_dn_get_linearized(ac->req->op.del.dn),
1349 ldb_errstring(ldb));
1352 if (ret == LDB_SUCCESS) {
1353 if (ldb_dn_compare(ac->req->op.del.dn, dn) == 0) {
1355 ldb_asprintf_errstring(ldb, "objectclass: Cannot delete %s, it's the DC's rIDSet object!",
1356 ldb_dn_get_linearized(ac->req->op.del.dn));
1357 return LDB_ERR_UNWILLING_TO_PERFORM;
1362 /* Only trusted request from system account are allowed to delete
1365 if (ldb_msg_check_string_attribute(ac->search_res->message, "isDeleted", "TRUE") &&
1366 (ldb_req_is_untrusted(ac->req) ||
1367 !dsdb_module_am_system(ac->module))) {
1368 ldb_asprintf_errstring(ldb, "Delete of '%s' failed",
1369 ldb_dn_get_linearized(ac->req->op.del.dn));
1370 return LDB_ERR_UNWILLING_TO_PERFORM;
1373 /* crossRef objects regarding config, schema and default domain NCs */
1374 if (samdb_find_attribute(ldb, ac->search_res->message, "objectClass",
1375 "crossRef") != NULL) {
1376 dn = ldb_msg_find_attr_as_dn(ldb, ac, ac->search_res->message,
1378 if ((ldb_dn_compare(dn, ldb_get_default_basedn(ldb)) == 0) ||
1379 (ldb_dn_compare(dn, ldb_get_config_basedn(ldb)) == 0)) {
1382 ldb_asprintf_errstring(ldb, "objectclass: Cannot delete %s, it's a crossRef object to the main or configuration partition!",
1383 ldb_dn_get_linearized(ac->req->op.del.dn));
1384 return LDB_ERR_NOT_ALLOWED_ON_NON_LEAF;
1386 if (ldb_dn_compare(dn, ldb_get_schema_basedn(ldb)) == 0) {
1389 ldb_asprintf_errstring(ldb, "objectclass: Cannot delete %s, it's a crossRef object to the schema partition!",
1390 ldb_dn_get_linearized(ac->req->op.del.dn));
1391 return LDB_ERR_UNWILLING_TO_PERFORM;
1398 systemFlags = ldb_msg_find_attr_as_int(ac->search_res->message,
1400 if ((systemFlags & SYSTEM_FLAG_DISALLOW_DELETE) != 0) {
1401 ldb_asprintf_errstring(ldb, "objectclass: Cannot delete %s, it isn't permitted!",
1402 ldb_dn_get_linearized(ac->req->op.del.dn));
1403 return LDB_ERR_UNWILLING_TO_PERFORM;
1406 /* isCriticalSystemObject - but this only applies on tree delete
1407 * operations - MS-ADTS 3.1.1.5.5.7.2 */
1408 if (ldb_request_get_control(ac->req, LDB_CONTROL_TREE_DELETE_OID) != NULL) {
1409 isCriticalSystemObject = ldb_msg_find_attr_as_bool(ac->search_res->message,
1410 "isCriticalSystemObject", false);
1411 if (isCriticalSystemObject) {
1413 * Following the explanation from Microsoft
1414 * https://lists.samba.org/archive/cifs-protocol/2011-August/002046.html
1415 * "I finished the investigation on this behavior.
1416 * As per MS-ADTS 3.1.5.5.7.2 , when a tree deletion is performed ,
1417 * every object in the tree will be checked to see if it has isCriticalSystemObject
1418 * set to TRUE, including the root node on which the delete operation is performed
1419 * But there is an exception if the root object is a SAM specific objects(3.1.1.5.2.3 MS-ADTS)
1420 * Its deletion is done through SAM manager and isCriticalSystemObject attribute is not checked
1421 * The root node of the tree delete in your case is CN=ARES,OU=Domain Controllers,DC=w2k8r2,DC=home,DC=matws,DC=net
1422 * which is a SAM object with user class. Therefore the tree deletion is performed without any error
1425 if (samdb_find_attribute(ldb, ac->search_res->message, "objectClass", "group") == NULL &&
1426 samdb_find_attribute(ldb, ac->search_res->message, "objectClass", "samDomain") == NULL &&
1427 samdb_find_attribute(ldb, ac->search_res->message, "objectClass", "samServer") == NULL &&
1428 samdb_find_attribute(ldb, ac->search_res->message, "objectClass", "user") == NULL) {
1429 ldb_asprintf_errstring(ldb,
1430 "objectclass: Cannot tree-delete %s, it's a critical system object!",
1431 ldb_dn_get_linearized(ac->req->op.del.dn));
1432 return LDB_ERR_UNWILLING_TO_PERFORM;
1437 return ldb_next_request(ac->module, ac->req);
1440 static int objectclass_init(struct ldb_module *module)
1442 struct ldb_context *ldb = ldb_module_get_ctx(module);
1445 /* Init everything else */
1446 ret = ldb_next_init(module);
1447 if (ret != LDB_SUCCESS) {
1451 /* Look for the opaque to indicate we might have to cut down the DN of defaultObjectCategory */
1452 ldb_module_set_private(module, ldb_get_opaque(ldb, DSDB_EXTENDED_DN_STORE_FORMAT_OPAQUE_NAME));
1454 ret = ldb_mod_register_control(module, LDB_CONTROL_RODC_DCPROMO_OID);
1455 if (ret != LDB_SUCCESS) {
1456 ldb_debug(ldb, LDB_DEBUG_ERROR,
1457 "objectclass_init: Unable to register control DCPROMO with rootdse\n");
1458 return ldb_operr(ldb);
1464 static const struct ldb_module_ops ldb_objectclass_module_ops = {
1465 .name = "objectclass",
1466 .add = objectclass_add,
1467 .modify = objectclass_modify,
1468 .rename = objectclass_rename,
1469 .del = objectclass_delete,
1470 .init_context = objectclass_init
1473 int ldb_objectclass_module_init(const char *version)
1475 LDB_MODULE_CHECK_VERSION(version);
1476 return ldb_register_module(&ldb_objectclass_module_ops);