dcesrv_core: better fault codes dcesrv_auth_prepare_auth3()
[Samba.git] / source4 / dsdb / samdb / ldb_modules / objectclass_attrs.c
blob40bd63f5afdcdc919c7e19ceea670206517df8ee
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) Stefan Metzmacher 2009
7 Copyright (C) Matthias Dieter Wallnöfer 2010
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 3 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU Lesser General Public
20 License along with this library; if not, see <http://www.gnu.org/licenses/>.
24 * Name: ldb
26 * Component: objectclass attribute checking module
28 * Description: this checks the attributes on a directory entry (if they're
29 * allowed, if the syntax is correct, if mandatory ones are missing,
30 * denies the deletion of mandatory ones...). The module contains portions
31 * of the "objectclass" and the "validate_update" LDB module.
33 * Author: Matthias Dieter Wallnöfer
36 #include "includes.h"
37 #include "ldb_module.h"
38 #include "dsdb/samdb/samdb.h"
39 #include "dsdb/samdb/ldb_modules/util.h"
41 #undef strcasecmp
43 struct oc_context {
45 struct ldb_module *module;
46 struct ldb_request *req;
47 const struct dsdb_schema *schema;
49 struct ldb_message *msg;
51 struct ldb_reply *search_res;
52 struct ldb_reply *mod_ares;
55 static struct oc_context *oc_init_context(struct ldb_module *module,
56 struct ldb_request *req)
58 struct ldb_context *ldb;
59 struct oc_context *ac;
61 ldb = ldb_module_get_ctx(module);
63 ac = talloc_zero(req, struct oc_context);
64 if (ac == NULL) {
65 ldb_oom(ldb);
66 return NULL;
69 ac->module = module;
70 ac->req = req;
71 ac->schema = dsdb_get_schema(ldb, ac);
73 return ac;
76 static int oc_op_callback(struct ldb_request *req, struct ldb_reply *ares);
79 * Checks the correctness of the "dSHeuristics" attribute as described in both
80 * MS-ADTS 7.1.1.2.4.1.2 dSHeuristics and MS-ADTS 3.1.1.5.3.2 Constraints
82 static int oc_validate_dsheuristics(struct ldb_message_element *el)
84 if (el->num_values > 0) {
85 if ((el->values[0].length >= DS_HR_NINETIETH_CHAR) &&
86 (el->values[0].data[DS_HR_NINETIETH_CHAR-1] != '9')) {
87 return LDB_ERR_CONSTRAINT_VIOLATION;
89 if ((el->values[0].length >= DS_HR_EIGHTIETH_CHAR) &&
90 (el->values[0].data[DS_HR_EIGHTIETH_CHAR-1] != '8')) {
91 return LDB_ERR_CONSTRAINT_VIOLATION;
93 if ((el->values[0].length >= DS_HR_SEVENTIETH_CHAR) &&
94 (el->values[0].data[DS_HR_SEVENTIETH_CHAR-1] != '7')) {
95 return LDB_ERR_CONSTRAINT_VIOLATION;
97 if ((el->values[0].length >= DS_HR_SIXTIETH_CHAR) &&
98 (el->values[0].data[DS_HR_SIXTIETH_CHAR-1] != '6')) {
99 return LDB_ERR_CONSTRAINT_VIOLATION;
101 if ((el->values[0].length >= DS_HR_FIFTIETH_CHAR) &&
102 (el->values[0].data[DS_HR_FIFTIETH_CHAR-1] != '5')) {
103 return LDB_ERR_CONSTRAINT_VIOLATION;
105 if ((el->values[0].length >= DS_HR_FOURTIETH_CHAR) &&
106 (el->values[0].data[DS_HR_FOURTIETH_CHAR-1] != '4')) {
107 return LDB_ERR_CONSTRAINT_VIOLATION;
109 if ((el->values[0].length >= DS_HR_THIRTIETH_CHAR) &&
110 (el->values[0].data[DS_HR_THIRTIETH_CHAR-1] != '3')) {
111 return LDB_ERR_CONSTRAINT_VIOLATION;
113 if ((el->values[0].length >= DS_HR_TWENTIETH_CHAR) &&
114 (el->values[0].data[DS_HR_TWENTIETH_CHAR-1] != '2')) {
115 return LDB_ERR_CONSTRAINT_VIOLATION;
117 if ((el->values[0].length >= DS_HR_TENTH_CHAR) &&
118 (el->values[0].data[DS_HR_TENTH_CHAR-1] != '1')) {
119 return LDB_ERR_CONSTRAINT_VIOLATION;
123 return LDB_SUCCESS;
127 auto normalise values on input
129 static int oc_auto_normalise(struct ldb_context *ldb, const struct dsdb_attribute *attr,
130 struct ldb_message *msg, struct ldb_message_element *el)
132 int i;
133 bool values_copied = false;
135 for (i=0; i<el->num_values; i++) {
136 struct ldb_val v;
137 int ret;
139 * We use msg->elements (owned by this module due to
140 * ldb_msg_copy_shallow()) as a memory context and
141 * then steal from there to the right spot if we don't
142 * free it.
144 ret = attr->ldb_schema_attribute->syntax->canonicalise_fn(ldb,
145 msg->elements,
146 &el->values[i],
147 &v);
148 if (ret != LDB_SUCCESS) {
149 return ret;
151 if (data_blob_cmp(&v, &el->values[i]) == 0) {
152 /* no need to replace it */
153 talloc_free(v.data);
154 continue;
157 /* we need to copy the values array on the first change */
158 if (!values_copied) {
159 struct ldb_val *v2;
160 v2 = talloc_array(msg->elements, struct ldb_val, el->num_values);
161 if (v2 == NULL) {
162 return ldb_oom(ldb);
164 memcpy(v2, el->values, sizeof(struct ldb_val) * el->num_values);
165 el->values = v2;
166 values_copied = true;
169 el->values[i] = v;
172 * By now el->values is a talloc pointer under
173 * msg->elements and may now be used
175 talloc_steal(el->values, v.data);
177 return LDB_SUCCESS;
180 static int attr_handler(struct oc_context *ac)
182 struct ldb_context *ldb;
183 struct ldb_message *msg;
184 struct ldb_request *child_req;
185 const struct dsdb_attribute *attr;
186 unsigned int i;
187 int ret;
188 WERROR werr;
189 struct dsdb_syntax_ctx syntax_ctx;
191 ldb = ldb_module_get_ctx(ac->module);
193 if (ac->req->operation == LDB_ADD) {
194 msg = ldb_msg_copy_shallow(ac, ac->req->op.add.message);
195 } else {
196 msg = ldb_msg_copy_shallow(ac, ac->req->op.mod.message);
198 if (msg == NULL) {
199 return ldb_oom(ldb);
201 ac->msg = msg;
203 /* initialize syntax checking context */
204 dsdb_syntax_ctx_init(&syntax_ctx, ldb, ac->schema);
206 /* Check if attributes exist in the schema, if the values match,
207 * if they're not operational and fix the names to the match the schema
208 * case */
209 for (i = 0; i < msg->num_elements; i++) {
210 attr = dsdb_attribute_by_lDAPDisplayName(ac->schema,
211 msg->elements[i].name);
212 if (attr == NULL) {
213 if (ldb_request_get_control(ac->req, DSDB_CONTROL_DBCHECK) &&
214 ac->req->operation != LDB_ADD) {
215 /* we allow this for dbcheck to fix
216 broken attributes */
217 goto no_attribute;
219 ldb_asprintf_errstring(ldb, "objectclass_attrs: attribute '%s' on entry '%s' was not found in the schema!",
220 msg->elements[i].name,
221 ldb_dn_get_linearized(msg->dn));
222 return LDB_ERR_NO_SUCH_ATTRIBUTE;
225 if ((attr->linkID & 1) == 1 &&
226 !ldb_request_get_control(ac->req, LDB_CONTROL_RELAX_OID) &&
227 !ldb_request_get_control(ac->req, DSDB_CONTROL_DBCHECK)) {
228 /* Odd is for the target. Illegal to modify */
229 ldb_asprintf_errstring(ldb,
230 "objectclass_attrs: attribute '%s' on entry '%s' must not be modified directly, it is a linked attribute",
231 msg->elements[i].name,
232 ldb_dn_get_linearized(msg->dn));
233 return LDB_ERR_UNWILLING_TO_PERFORM;
237 * Enforce systemOnly checks from [ADTS] 3.1.1.5.3.2
238 * Constraints in Modify Operation
240 if (ac->req->operation == LDB_MODIFY && attr->systemOnly) {
242 * Allow dbcheck and relax to bypass. objectClass, name
243 * and distinguishedName are generally handled
244 * elsewhere.
246 * The remaining cases, undelete, msDS-AdditionalDnsHostName
247 * and wellKnownObjects are documented in the specification.
249 if (!ldb_request_get_control(ac->req, LDB_CONTROL_RELAX_OID) &&
250 !ldb_request_get_control(ac->req, DSDB_CONTROL_DBCHECK) &&
251 !ldb_request_get_control(ac->req, DSDB_CONTROL_RESTORE_TOMBSTONE_OID) &&
252 ldb_attr_cmp(attr->lDAPDisplayName, "objectClass") != 0 &&
253 ldb_attr_cmp(attr->lDAPDisplayName, "name") != 0 &&
254 ldb_attr_cmp(attr->lDAPDisplayName, "distinguishedName") != 0 &&
255 ldb_attr_cmp(attr->lDAPDisplayName, "msDS-AdditionalDnsHostName") != 0 &&
256 ldb_attr_cmp(attr->lDAPDisplayName, "wellKnownObjects") != 0) {
258 * Comparison against base schema DN is used as a substitute for
259 * fschemaUpgradeInProgress and other specific schema checks.
261 if (ldb_dn_compare_base(ldb_get_schema_basedn(ldb), msg->dn) != 0) {
262 if (!dsdb_have_system_access(
263 ac->module,
264 ac->req,
265 SYSTEM_CONTROL_KEEP_CRITICAL))
267 ldb_asprintf_errstring(ldb,
268 "objectclass_attrs: attribute '%s' on entry '%s' can only be modified as system",
269 msg->elements[i].name,
270 ldb_dn_get_linearized(msg->dn));
271 return LDB_ERR_CONSTRAINT_VIOLATION;
277 if (!(msg->elements[i].flags & LDB_FLAG_INTERNAL_DISABLE_VALIDATION)) {
278 werr = attr->syntax->validate_ldb(&syntax_ctx, attr,
279 &msg->elements[i]);
280 if (!W_ERROR_IS_OK(werr) &&
281 !ldb_request_get_control(ac->req, DSDB_CONTROL_DBCHECK)) {
282 ldb_asprintf_errstring(ldb, "objectclass_attrs: attribute '%s' on entry '%s' contains at least one invalid value!",
283 msg->elements[i].name,
284 ldb_dn_get_linearized(msg->dn));
285 return LDB_ERR_INVALID_ATTRIBUTE_SYNTAX;
289 if ((attr->systemFlags & DS_FLAG_ATTR_IS_CONSTRUCTED) != 0) {
290 ldb_asprintf_errstring(ldb, "objectclass_attrs: attribute '%s' on entry '%s' is constructed!",
291 msg->elements[i].name,
292 ldb_dn_get_linearized(msg->dn));
293 if (ac->req->operation == LDB_ADD) {
294 return LDB_ERR_UNDEFINED_ATTRIBUTE_TYPE;
295 } else {
296 return LDB_ERR_CONSTRAINT_VIOLATION;
300 /* "dSHeuristics" syntax check */
301 if (ldb_attr_cmp(attr->lDAPDisplayName, "dSHeuristics") == 0) {
302 ret = oc_validate_dsheuristics(&(msg->elements[i]));
303 if (ret != LDB_SUCCESS) {
304 return ret;
308 /* auto normalise some attribute values */
309 if (attr->syntax->auto_normalise) {
310 ret = oc_auto_normalise(ldb, attr, msg, &msg->elements[i]);
311 if (ret != LDB_SUCCESS) {
312 return ret;
316 /* Substitute the attribute name to match in case */
317 msg->elements[i].name = attr->lDAPDisplayName;
320 no_attribute:
321 if (ac->req->operation == LDB_ADD) {
322 ret = ldb_build_add_req(&child_req, ldb, ac,
323 msg, ac->req->controls,
324 ac, oc_op_callback, ac->req);
325 LDB_REQ_SET_LOCATION(child_req);
326 } else {
327 ret = ldb_build_mod_req(&child_req, ldb, ac,
328 msg, ac->req->controls,
329 ac, oc_op_callback, ac->req);
330 LDB_REQ_SET_LOCATION(child_req);
332 if (ret != LDB_SUCCESS) {
333 return ret;
336 return ldb_next_request(ac->module, child_req);
340 these are attributes which are left over from old ways of doing
341 things in ldb, and are harmless
343 static const char *harmless_attrs[] = { "parentGUID", NULL };
345 static int attr_handler2(struct oc_context *ac)
347 struct ldb_context *ldb;
348 struct ldb_message_element *oc_element;
349 struct ldb_message *msg;
350 const char **must_contain, **may_contain, **found_must_contain;
351 /* There exists a hardcoded delete-protected attributes list in AD */
352 const char *del_prot_attributes[] = { "nTSecurityDescriptor",
353 "objectSid", "sAMAccountType", "sAMAccountName", "groupType",
354 "primaryGroupID", "userAccountControl", "accountExpires",
355 "badPasswordTime", "badPwdCount", "codePage", "countryCode",
356 "lastLogoff", "lastLogon", "logonCount", "pwdLastSet", NULL },
357 **l;
358 const struct dsdb_attribute *attr;
359 unsigned int i;
360 bool found;
361 bool isSchemaAttr = false;
363 ldb = ldb_module_get_ctx(ac->module);
365 if (ac->search_res == NULL) {
366 return ldb_operr(ldb);
369 /* We rely here on the preceding "objectclass" LDB module which did
370 * already fix up the objectclass list (inheritance, order...). */
371 oc_element = ldb_msg_find_element(ac->search_res->message,
372 "objectClass");
373 if (oc_element == NULL) {
374 return ldb_operr(ldb);
377 /* LSA-specific object classes are not allowed to be created over LDAP,
378 * so we need to tell if this connection is internal (trusted) or not
379 * (untrusted).
381 * Hongwei Sun from Microsoft explains:
382 * The constraint in 3.1.1.5.2.2 MS-ADTS means that LSA objects cannot
383 * be added or modified through the LDAP interface, instead they can
384 * only be handled through LSA Policy API. This is also explained in
385 * 7.1.6.9.7 MS-ADTS as follows:
386 * "Despite being replicated normally between peer DCs in a domain,
387 * the process of creating or manipulating TDOs is specifically
388 * restricted to the LSA Policy APIs, as detailed in [MS-LSAD] section
389 * 3.1.1.5. Unlike other objects in the DS, TDOs may not be created or
390 * manipulated by client machines over the LDAPv3 transport."
392 for (i = 0; i < oc_element->num_values; i++) {
393 char * attname = (char *)oc_element->values[i].data;
394 if (ldb_req_is_untrusted(ac->req)) {
395 if (strcmp(attname, "secret") == 0 ||
396 strcmp(attname, "trustedDomain") == 0) {
397 ldb_asprintf_errstring(ldb, "objectclass_attrs: LSA objectclasses (entry '%s') cannot be created or changed over LDAP!",
398 ldb_dn_get_linearized(ac->search_res->message->dn));
399 return LDB_ERR_UNWILLING_TO_PERFORM;
402 if (strcmp(attname, "attributeSchema") == 0) {
403 isSchemaAttr = true;
407 must_contain = dsdb_full_attribute_list(ac, ac->schema, oc_element,
408 DSDB_SCHEMA_ALL_MUST);
409 may_contain = dsdb_full_attribute_list(ac, ac->schema, oc_element,
410 DSDB_SCHEMA_ALL_MAY);
411 found_must_contain = const_str_list(str_list_copy(ac, must_contain));
412 if ((must_contain == NULL) || (may_contain == NULL)
413 || (found_must_contain == NULL)) {
414 return ldb_operr(ldb);
417 /* Check the delete-protected attributes list */
418 msg = ac->search_res->message;
419 for (l = del_prot_attributes; *l != NULL; l++) {
420 struct ldb_message_element *el;
422 el = ldb_msg_find_element(ac->msg, *l);
423 if (el == NULL) {
425 * It was not specified in the add or modify,
426 * so it doesn't need to be in the stored record
428 continue;
431 found = str_list_check_ci(must_contain, *l);
432 if (!found) {
433 found = str_list_check_ci(may_contain, *l);
435 if (found && (ldb_msg_find_element(msg, *l) == NULL)) {
436 ldb_asprintf_errstring(ldb, "objectclass_attrs: delete protected attribute '%s' on entry '%s' missing!",
438 ldb_dn_get_linearized(msg->dn));
439 return LDB_ERR_UNWILLING_TO_PERFORM;
443 /* Check if all specified attributes are valid in the given
444 * objectclasses and if they meet additional schema restrictions. */
445 for (i = 0; i < msg->num_elements; i++) {
446 attr = dsdb_attribute_by_lDAPDisplayName(ac->schema,
447 msg->elements[i].name);
448 if (attr == NULL) {
449 if (ldb_request_get_control(ac->req, DSDB_CONTROL_DBCHECK)) {
450 /* allow this to make it possible for dbcheck
451 to remove bad attributes */
452 continue;
454 return ldb_operr(ldb);
457 if (attr->linkID & 1) {
459 * We need to allow backlinks on all objects
460 * even if the schema doesn't allow it.
462 continue;
465 /* We can use "str_list_check" with "strcmp" here since the
466 * attribute information from the schema are always equal
467 * up-down-cased. */
468 found = str_list_check(must_contain, attr->lDAPDisplayName);
469 if (found) {
470 str_list_remove(found_must_contain, attr->lDAPDisplayName);
471 } else {
472 found = str_list_check(may_contain, attr->lDAPDisplayName);
474 if (!found) {
475 found = str_list_check(harmless_attrs, attr->lDAPDisplayName);
477 if (!found) {
478 /* we allow this for dbcheck to fix the rest of this broken entry */
479 if (!ldb_request_get_control(ac->req, DSDB_CONTROL_DBCHECK) ||
480 ac->req->operation == LDB_ADD) {
481 ldb_asprintf_errstring(ldb, "objectclass_attrs: attribute '%s' on entry '%s' does not exist in the specified objectclasses!",
482 msg->elements[i].name,
483 ldb_dn_get_linearized(msg->dn));
484 return LDB_ERR_OBJECT_CLASS_VIOLATION;
490 * We skip this check under dbcheck to allow fixing of other
491 * attributes even if an attribute is missing. This matters
492 * for CN=RID Set as the required attribute rIDNextRid is not
493 * replicated.
495 if (found_must_contain[0] != NULL &&
496 ldb_msg_check_string_attribute(msg, "isDeleted", "TRUE") == 0) {
498 for (i = 0; found_must_contain[i] != NULL; i++) {
499 const struct dsdb_attribute *broken_attr = dsdb_attribute_by_lDAPDisplayName(ac->schema,
500 found_must_contain[i]);
502 bool replicated = (broken_attr->systemFlags &
503 (DS_FLAG_ATTR_NOT_REPLICATED | DS_FLAG_ATTR_IS_CONSTRUCTED)) == 0;
505 if (replicated) {
506 ldb_asprintf_errstring(ldb, "objectclass_attrs: at least one mandatory "
507 "attribute ('%s') on entry '%s' wasn't specified!",
508 found_must_contain[i],
509 ldb_dn_get_linearized(msg->dn));
510 return LDB_ERR_OBJECT_CLASS_VIOLATION;
515 if (isSchemaAttr) {
517 * Before really adding an attribute in the database,
518 * let's check that we can translate it into a dsdb_attribute and
519 * that we can find a valid syntax object.
520 * If not it's better to reject this attribute than not be able
521 * to start samba next time due to schema being unloadable.
523 struct dsdb_attribute *att = talloc(ac, struct dsdb_attribute);
524 const struct dsdb_syntax *attrSyntax;
525 WERROR status;
527 status = dsdb_attribute_from_ldb(NULL, msg, att);
528 if (!W_ERROR_IS_OK(status)) {
529 ldb_set_errstring(ldb,
530 "objectclass: failed to translate the schemaAttribute to a dsdb_attribute");
531 return LDB_ERR_UNWILLING_TO_PERFORM;
534 attrSyntax = dsdb_syntax_for_attribute(att);
535 if (!attrSyntax) {
536 ldb_set_errstring(ldb,
537 "objectclass: unknown attribute syntax");
538 return LDB_ERR_UNWILLING_TO_PERFORM;
541 return ldb_module_done(ac->req, ac->mod_ares->controls,
542 ac->mod_ares->response, LDB_SUCCESS);
545 static int get_search_callback(struct ldb_request *req, struct ldb_reply *ares)
547 struct ldb_context *ldb;
548 struct oc_context *ac;
549 int ret;
551 ac = talloc_get_type(req->context, struct oc_context);
552 ldb = ldb_module_get_ctx(ac->module);
554 if (!ares) {
555 return ldb_module_done(ac->req, NULL, NULL,
556 LDB_ERR_OPERATIONS_ERROR);
558 if (ares->error != LDB_SUCCESS) {
559 return ldb_module_done(ac->req, ares->controls,
560 ares->response, ares->error);
563 ldb_reset_err_string(ldb);
565 switch (ares->type) {
566 case LDB_REPLY_ENTRY:
567 if (ac->search_res != NULL) {
568 ldb_set_errstring(ldb, "Too many results");
569 talloc_free(ares);
570 return ldb_module_done(ac->req, NULL, NULL,
571 LDB_ERR_OPERATIONS_ERROR);
574 ac->search_res = talloc_steal(ac, ares);
575 break;
577 case LDB_REPLY_REFERRAL:
578 /* ignore */
579 talloc_free(ares);
580 break;
582 case LDB_REPLY_DONE:
583 talloc_free(ares);
584 ret = attr_handler2(ac);
585 if (ret != LDB_SUCCESS) {
586 return ldb_module_done(ac->req, NULL, NULL, ret);
588 break;
591 return LDB_SUCCESS;
594 static int oc_op_callback(struct ldb_request *req, struct ldb_reply *ares)
596 struct oc_context *ac;
597 struct ldb_context *ldb;
598 struct ldb_request *search_req;
599 struct ldb_dn *base_dn;
600 int ret;
601 static const char *attrs[] = {"nTSecurityDescriptor", "*", NULL};
603 ac = talloc_get_type(req->context, struct oc_context);
604 ldb = ldb_module_get_ctx(ac->module);
606 if (!ares) {
607 return ldb_module_done(ac->req, NULL, NULL,
608 LDB_ERR_OPERATIONS_ERROR);
611 if (ares->type == LDB_REPLY_REFERRAL) {
612 return ldb_module_send_referral(ac->req, ares->referral);
615 if (ares->error != LDB_SUCCESS) {
616 return ldb_module_done(ac->req, ares->controls, ares->response,
617 ares->error);
620 if (ares->type != LDB_REPLY_DONE) {
621 talloc_free(ares);
622 return ldb_module_done(ac->req, NULL, NULL,
623 LDB_ERR_OPERATIONS_ERROR);
626 ac->search_res = NULL;
627 ac->mod_ares = talloc_steal(ac, ares);
629 /* This looks up all attributes of our just added/modified entry */
630 base_dn = ac->req->operation == LDB_ADD ? ac->req->op.add.message->dn
631 : ac->req->op.mod.message->dn;
632 ret = ldb_build_search_req(&search_req, ldb, ac, base_dn,
633 LDB_SCOPE_BASE, "(objectClass=*)",
634 attrs, NULL, ac,
635 get_search_callback, ac->req);
636 LDB_REQ_SET_LOCATION(search_req);
637 if (ret != LDB_SUCCESS) {
638 return ldb_module_done(ac->req, NULL, NULL, ret);
641 ret = ldb_request_add_control(search_req, LDB_CONTROL_SHOW_RECYCLED_OID,
642 true, NULL);
643 if (ret != LDB_SUCCESS) {
644 return ldb_module_done(ac->req, NULL, NULL, ret);
648 * This ensures we see if there was a DN, that pointed at an
649 * object that is now deleted, that we still consider the
650 * schema check to have passed
652 ret = ldb_request_add_control(search_req, LDB_CONTROL_REVEAL_INTERNALS,
653 false, NULL);
654 if (ret != LDB_SUCCESS) {
655 return ldb_module_done(ac->req, NULL, NULL, ret);
658 ret = ldb_next_request(ac->module, search_req);
659 if (ret != LDB_SUCCESS) {
660 return ldb_module_done(ac->req, NULL, NULL, ret);
663 /* "ldb_module_done" isn't called here since we need to do additional
664 * checks. It is called at the end of "attr_handler2". */
665 return LDB_SUCCESS;
668 static int objectclass_attrs_add(struct ldb_module *module,
669 struct ldb_request *req)
671 struct ldb_context *ldb;
672 struct oc_context *ac;
674 ldb = ldb_module_get_ctx(module);
676 ldb_debug(ldb, LDB_DEBUG_TRACE, "objectclass_attrs_add\n");
678 /* do not manipulate our control entries */
679 if (ldb_dn_is_special(req->op.add.message->dn)) {
680 return ldb_next_request(module, req);
683 ac = oc_init_context(module, req);
684 if (ac == NULL) {
685 return ldb_operr(ldb);
688 /* without schema, there isn't much to do here */
689 if (ac->schema == NULL) {
690 talloc_free(ac);
691 return ldb_next_request(module, req);
694 return attr_handler(ac);
697 static int objectclass_attrs_modify(struct ldb_module *module,
698 struct ldb_request *req)
700 struct ldb_context *ldb;
701 struct ldb_control *sd_propagation_control;
702 int ret;
704 struct oc_context *ac;
706 ldb = ldb_module_get_ctx(module);
708 ldb_debug(ldb, LDB_DEBUG_TRACE, "objectclass_attrs_modify\n");
710 /* do not manipulate our control entries */
711 if (ldb_dn_is_special(req->op.mod.message->dn)) {
712 return ldb_next_request(module, req);
715 sd_propagation_control = ldb_request_get_control(req,
716 DSDB_CONTROL_SEC_DESC_PROPAGATION_OID);
717 if (sd_propagation_control != NULL) {
718 if (req->op.mod.message->num_elements != 1) {
719 return ldb_module_operr(module);
721 ret = strcmp(req->op.mod.message->elements[0].name,
722 "nTSecurityDescriptor");
723 if (ret != 0) {
724 return ldb_module_operr(module);
727 return ldb_next_request(module, req);
730 ac = oc_init_context(module, req);
731 if (ac == NULL) {
732 return ldb_operr(ldb);
735 /* without schema, there isn't much to do here */
736 if (ac->schema == NULL) {
737 talloc_free(ac);
738 return ldb_next_request(module, req);
741 return attr_handler(ac);
744 static const struct ldb_module_ops ldb_objectclass_attrs_module_ops = {
745 .name = "objectclass_attrs",
746 .add = objectclass_attrs_add,
747 .modify = objectclass_attrs_modify
750 int ldb_objectclass_attrs_module_init(const char *version)
752 LDB_MODULE_CHECK_VERSION(version);
753 return ldb_register_module(&ldb_objectclass_attrs_module_ops);