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/>.
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
37 #include "ldb_module.h"
38 #include "dsdb/samdb/samdb.h"
39 #include "dsdb/samdb/ldb_modules/util.h"
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
);
71 ac
->schema
= dsdb_get_schema(ldb
, 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
;
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
)
133 bool values_copied
= false;
135 for (i
=0; i
<el
->num_values
; i
++) {
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
144 ret
= attr
->ldb_schema_attribute
->syntax
->canonicalise_fn(ldb
,
148 if (ret
!= LDB_SUCCESS
) {
151 if (data_blob_cmp(&v
, &el
->values
[i
]) == 0) {
152 /* no need to replace it */
157 /* we need to copy the values array on the first change */
158 if (!values_copied
) {
160 v2
= talloc_array(msg
->elements
, struct ldb_val
, el
->num_values
);
164 memcpy(v2
, el
->values
, sizeof(struct ldb_val
) * el
->num_values
);
166 values_copied
= true;
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
);
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
;
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
);
196 msg
= ldb_msg_copy_shallow(ac
, ac
->req
->op
.mod
.message
);
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
209 for (i
= 0; i
< msg
->num_elements
; i
++) {
210 attr
= dsdb_attribute_by_lDAPDisplayName(ac
->schema
,
211 msg
->elements
[i
].name
);
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
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
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(
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
,
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
;
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
) {
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
) {
316 /* Substitute the attribute name to match in case */
317 msg
->elements
[i
].name
= attr
->lDAPDisplayName
;
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
);
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
) {
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
},
358 const struct dsdb_attribute
*attr
;
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
,
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
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) {
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
);
425 * It was not specified in the add or modify,
426 * so it doesn't need to be in the stored record
431 found
= str_list_check_ci(must_contain
, *l
);
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
);
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 */
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.
465 /* We can use "str_list_check" with "strcmp" here since the
466 * attribute information from the schema are always equal
468 found
= str_list_check(must_contain
, attr
->lDAPDisplayName
);
470 str_list_remove(found_must_contain
, attr
->lDAPDisplayName
);
472 found
= str_list_check(may_contain
, attr
->lDAPDisplayName
);
475 found
= str_list_check(harmless_attrs
, attr
->lDAPDisplayName
);
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
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;
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
;
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
;
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
);
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
;
551 ac
= talloc_get_type(req
->context
, struct oc_context
);
552 ldb
= ldb_module_get_ctx(ac
->module
);
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");
570 return ldb_module_done(ac
->req
, NULL
, NULL
,
571 LDB_ERR_OPERATIONS_ERROR
);
574 ac
->search_res
= talloc_steal(ac
, ares
);
577 case LDB_REPLY_REFERRAL
:
584 ret
= attr_handler2(ac
);
585 if (ret
!= LDB_SUCCESS
) {
586 return ldb_module_done(ac
->req
, NULL
, NULL
, ret
);
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
;
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
);
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
,
620 if (ares
->type
!= LDB_REPLY_DONE
) {
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=*)",
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
,
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
,
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". */
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
);
685 return ldb_operr(ldb
);
688 /* without schema, there isn't much to do here */
689 if (ac
->schema
== NULL
) {
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
;
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");
724 return ldb_module_operr(module
);
727 return ldb_next_request(module
, req
);
730 ac
= oc_init_context(module
, req
);
732 return ldb_operr(ldb
);
735 /* without schema, there isn't much to do here */
736 if (ac
->schema
== NULL
) {
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
);