Line data Source code
1 : /*
2 : ldb database library
3 :
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
8 :
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.
13 :
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.
18 :
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/>.
21 : */
22 :
23 : /*
24 : * Name: ldb
25 : *
26 : * Component: objectclass attribute checking module
27 : *
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.
32 : *
33 : * Author: Matthias Dieter Wallnöfer
34 : */
35 :
36 : #include "includes.h"
37 : #include "ldb_module.h"
38 : #include "dsdb/samdb/samdb.h"
39 : #include "dsdb/samdb/ldb_modules/util.h"
40 :
41 : #undef strcasecmp
42 :
43 : struct oc_context {
44 :
45 : struct ldb_module *module;
46 : struct ldb_request *req;
47 : const struct dsdb_schema *schema;
48 :
49 : struct ldb_message *msg;
50 :
51 : struct ldb_reply *search_res;
52 : struct ldb_reply *mod_ares;
53 : };
54 :
55 930290 : static struct oc_context *oc_init_context(struct ldb_module *module,
56 : struct ldb_request *req)
57 : {
58 106787 : struct ldb_context *ldb;
59 106787 : struct oc_context *ac;
60 :
61 930290 : ldb = ldb_module_get_ctx(module);
62 :
63 930290 : ac = talloc_zero(req, struct oc_context);
64 930290 : if (ac == NULL) {
65 0 : ldb_oom(ldb);
66 0 : return NULL;
67 : }
68 :
69 930290 : ac->module = module;
70 930290 : ac->req = req;
71 930290 : ac->schema = dsdb_get_schema(ldb, ac);
72 :
73 930290 : return ac;
74 : }
75 :
76 : static int oc_op_callback(struct ldb_request *req, struct ldb_reply *ares);
77 :
78 : /*
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
81 : */
82 13422 : static int oc_validate_dsheuristics(struct ldb_message_element *el)
83 : {
84 13422 : if (el->num_values > 0) {
85 9479 : if ((el->values[0].length >= DS_HR_NINETIETH_CHAR) &&
86 4 : (el->values[0].data[DS_HR_NINETIETH_CHAR-1] != '9')) {
87 1 : return LDB_ERR_CONSTRAINT_VIOLATION;
88 : }
89 9478 : if ((el->values[0].length >= DS_HR_EIGHTIETH_CHAR) &&
90 6 : (el->values[0].data[DS_HR_EIGHTIETH_CHAR-1] != '8')) {
91 1 : return LDB_ERR_CONSTRAINT_VIOLATION;
92 : }
93 9477 : if ((el->values[0].length >= DS_HR_SEVENTIETH_CHAR) &&
94 8 : (el->values[0].data[DS_HR_SEVENTIETH_CHAR-1] != '7')) {
95 1 : return LDB_ERR_CONSTRAINT_VIOLATION;
96 : }
97 9476 : if ((el->values[0].length >= DS_HR_SIXTIETH_CHAR) &&
98 10 : (el->values[0].data[DS_HR_SIXTIETH_CHAR-1] != '6')) {
99 1 : return LDB_ERR_CONSTRAINT_VIOLATION;
100 : }
101 9475 : if ((el->values[0].length >= DS_HR_FIFTIETH_CHAR) &&
102 12 : (el->values[0].data[DS_HR_FIFTIETH_CHAR-1] != '5')) {
103 1 : return LDB_ERR_CONSTRAINT_VIOLATION;
104 : }
105 9474 : if ((el->values[0].length >= DS_HR_FOURTIETH_CHAR) &&
106 14 : (el->values[0].data[DS_HR_FOURTIETH_CHAR-1] != '4')) {
107 1 : return LDB_ERR_CONSTRAINT_VIOLATION;
108 : }
109 9473 : if ((el->values[0].length >= DS_HR_THIRTIETH_CHAR) &&
110 6406 : (el->values[0].data[DS_HR_THIRTIETH_CHAR-1] != '3')) {
111 1 : return LDB_ERR_CONSTRAINT_VIOLATION;
112 : }
113 9472 : if ((el->values[0].length >= DS_HR_TWENTIETH_CHAR) &&
114 6408 : (el->values[0].data[DS_HR_TWENTIETH_CHAR-1] != '2')) {
115 1 : return LDB_ERR_CONSTRAINT_VIOLATION;
116 : }
117 9471 : if ((el->values[0].length >= DS_HR_TENTH_CHAR) &&
118 6411 : (el->values[0].data[DS_HR_TENTH_CHAR-1] != '1')) {
119 1 : return LDB_ERR_CONSTRAINT_VIOLATION;
120 : }
121 : }
122 :
123 13413 : return LDB_SUCCESS;
124 : }
125 :
126 : /*
127 : auto normalise values on input
128 : */
129 2922970 : static int oc_auto_normalise(struct ldb_context *ldb, const struct dsdb_attribute *attr,
130 : struct ldb_message *msg, struct ldb_message_element *el)
131 : {
132 407543 : int i;
133 2922970 : bool values_copied = false;
134 :
135 5845361 : for (i=0; i<el->num_values; i++) {
136 407543 : struct ldb_val v;
137 407543 : int ret;
138 : /*
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.
143 : */
144 3329943 : ret = attr->ldb_schema_attribute->syntax->canonicalise_fn(ldb,
145 2922400 : msg->elements,
146 2922400 : &el->values[i],
147 : &v);
148 2922400 : if (ret != LDB_SUCCESS) {
149 9 : return ret;
150 : }
151 2922391 : if (data_blob_cmp(&v, &el->values[i]) == 0) {
152 : /* no need to replace it */
153 2922207 : talloc_free(v.data);
154 2922207 : continue;
155 : }
156 :
157 : /* we need to copy the values array on the first change */
158 184 : if (!values_copied) {
159 3 : struct ldb_val *v2;
160 184 : v2 = talloc_array(msg->elements, struct ldb_val, el->num_values);
161 184 : if (v2 == NULL) {
162 0 : return ldb_oom(ldb);
163 : }
164 184 : memcpy(v2, el->values, sizeof(struct ldb_val) * el->num_values);
165 184 : el->values = v2;
166 184 : values_copied = true;
167 : }
168 :
169 184 : el->values[i] = v;
170 :
171 : /*
172 : * By now el->values is a talloc pointer under
173 : * msg->elements and may now be used
174 : */
175 184 : talloc_steal(el->values, v.data);
176 : }
177 2515418 : return LDB_SUCCESS;
178 : }
179 :
180 930290 : static int attr_handler(struct oc_context *ac)
181 : {
182 106787 : struct ldb_context *ldb;
183 106787 : struct ldb_message *msg;
184 106787 : struct ldb_request *child_req;
185 106787 : const struct dsdb_attribute *attr;
186 106787 : unsigned int i;
187 106787 : int ret;
188 106787 : WERROR werr;
189 106787 : struct dsdb_syntax_ctx syntax_ctx;
190 :
191 930290 : ldb = ldb_module_get_ctx(ac->module);
192 :
193 930290 : if (ac->req->operation == LDB_ADD) {
194 542088 : msg = ldb_msg_copy_shallow(ac, ac->req->op.add.message);
195 : } else {
196 388202 : msg = ldb_msg_copy_shallow(ac, ac->req->op.mod.message);
197 : }
198 930290 : if (msg == NULL) {
199 0 : return ldb_oom(ldb);
200 : }
201 930290 : ac->msg = msg;
202 :
203 : /* initialize syntax checking context */
204 930290 : dsdb_syntax_ctx_init(&syntax_ctx, ldb, ac->schema);
205 :
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 9123752 : for (i = 0; i < msg->num_elements; i++) {
210 9262673 : attr = dsdb_attribute_by_lDAPDisplayName(ac->schema,
211 8086779 : msg->elements[i].name);
212 8086779 : if (attr == NULL) {
213 2 : if (ldb_request_get_control(ac->req, DSDB_CONTROL_DBCHECK) &&
214 0 : ac->req->operation != LDB_ADD) {
215 : /* we allow this for dbcheck to fix
216 : broken attributes */
217 0 : goto no_attribute;
218 : }
219 2 : ldb_asprintf_errstring(ldb, "objectclass_attrs: attribute '%s' on entry '%s' was not found in the schema!",
220 2 : msg->elements[i].name,
221 : ldb_dn_get_linearized(msg->dn));
222 2 : return LDB_ERR_NO_SUCH_ATTRIBUTE;
223 : }
224 :
225 8086796 : if ((attr->linkID & 1) == 1 &&
226 25 : !ldb_request_get_control(ac->req, LDB_CONTROL_RELAX_OID) &&
227 6 : !ldb_request_get_control(ac->req, DSDB_CONTROL_DBCHECK)) {
228 : /* Odd is for the target. Illegal to modify */
229 4 : ldb_asprintf_errstring(ldb,
230 : "objectclass_attrs: attribute '%s' on entry '%s' must not be modified directly, it is a linked attribute",
231 4 : msg->elements[i].name,
232 : ldb_dn_get_linearized(msg->dn));
233 4 : return LDB_ERR_UNWILLING_TO_PERFORM;
234 : }
235 :
236 : /*
237 : * Enforce systemOnly checks from [ADTS] 3.1.1.5.3.2
238 : * Constraints in Modify Operation
239 : */
240 8086773 : if (ac->req->operation == LDB_MODIFY && attr->systemOnly) {
241 : /*
242 : * Allow dbcheck and relax to bypass. objectClass, name
243 : * and distinguishedName are generally handled
244 : * elsewhere.
245 : *
246 : * The remaining cases, undelete, msDS-AdditionalDnsHostName
247 : * and wellKnownObjects are documented in the specification.
248 : */
249 275089 : if (!ldb_request_get_control(ac->req, LDB_CONTROL_RELAX_OID) &&
250 238117 : !ldb_request_get_control(ac->req, DSDB_CONTROL_DBCHECK) &&
251 101748 : !ldb_request_get_control(ac->req, DSDB_CONTROL_RESTORE_TOMBSTONE_OID) &&
252 101218 : ldb_attr_cmp(attr->lDAPDisplayName, "objectClass") != 0 &&
253 101141 : ldb_attr_cmp(attr->lDAPDisplayName, "name") != 0 &&
254 101140 : ldb_attr_cmp(attr->lDAPDisplayName, "distinguishedName") != 0 &&
255 101137 : ldb_attr_cmp(attr->lDAPDisplayName, "msDS-AdditionalDnsHostName") != 0 &&
256 101133 : ldb_attr_cmp(attr->lDAPDisplayName, "wellKnownObjects") != 0) {
257 : /*
258 : * Comparison against base schema DN is used as a substitute for
259 : * fschemaUpgradeInProgress and other specific schema checks.
260 : */
261 101133 : if (ldb_dn_compare_base(ldb_get_schema_basedn(ldb), msg->dn) != 0) {
262 96374 : struct ldb_control *as_system = ldb_request_get_control(ac->req,
263 : LDB_CONTROL_AS_SYSTEM_OID);
264 96374 : if (!dsdb_module_am_system(ac->module) && !as_system) {
265 5 : ldb_asprintf_errstring(ldb,
266 : "objectclass_attrs: attribute '%s' on entry '%s' can only be modified as system",
267 5 : msg->elements[i].name,
268 : ldb_dn_get_linearized(msg->dn));
269 5 : return LDB_ERR_CONSTRAINT_VIOLATION;
270 : }
271 : }
272 : }
273 : }
274 :
275 8086768 : if (!(msg->elements[i].flags & LDB_FLAG_INTERNAL_DISABLE_VALIDATION)) {
276 8086676 : werr = attr->syntax->validate_ldb(&syntax_ctx, attr,
277 6910782 : &msg->elements[i]);
278 8086750 : if (!W_ERROR_IS_OK(werr) &&
279 74 : !ldb_request_get_control(ac->req, DSDB_CONTROL_DBCHECK)) {
280 72 : ldb_asprintf_errstring(ldb, "objectclass_attrs: attribute '%s' on entry '%s' contains at least one invalid value!",
281 72 : msg->elements[i].name,
282 : ldb_dn_get_linearized(msg->dn));
283 72 : return LDB_ERR_INVALID_ATTRIBUTE_SYNTAX;
284 : }
285 : }
286 :
287 8086696 : if ((attr->systemFlags & DS_FLAG_ATTR_IS_CONSTRUCTED) != 0) {
288 3 : ldb_asprintf_errstring(ldb, "objectclass_attrs: attribute '%s' on entry '%s' is constructed!",
289 3 : msg->elements[i].name,
290 : ldb_dn_get_linearized(msg->dn));
291 3 : if (ac->req->operation == LDB_ADD) {
292 3 : return LDB_ERR_UNDEFINED_ATTRIBUTE_TYPE;
293 : } else {
294 0 : return LDB_ERR_CONSTRAINT_VIOLATION;
295 : }
296 : }
297 :
298 : /* "dSHeuristics" syntax check */
299 8086693 : if (ldb_attr_cmp(attr->lDAPDisplayName, "dSHeuristics") == 0) {
300 13422 : ret = oc_validate_dsheuristics(&(msg->elements[i]));
301 13422 : if (ret != LDB_SUCCESS) {
302 9 : return ret;
303 : }
304 : }
305 :
306 : /* auto normalise some attribute values */
307 8086684 : if (attr->syntax->auto_normalise) {
308 2922970 : ret = oc_auto_normalise(ldb, attr, msg, &msg->elements[i]);
309 2922970 : if (ret != LDB_SUCCESS) {
310 9 : return ret;
311 : }
312 : }
313 :
314 : /* Substitute the attribute name to match in case */
315 8086675 : msg->elements[i].name = attr->lDAPDisplayName;
316 : }
317 :
318 930186 : no_attribute:
319 930186 : if (ac->req->operation == LDB_ADD) {
320 542040 : ret = ldb_build_add_req(&child_req, ldb, ac,
321 458451 : msg, ac->req->controls,
322 : ac, oc_op_callback, ac->req);
323 542040 : LDB_REQ_SET_LOCATION(child_req);
324 : } else {
325 388146 : ret = ldb_build_mod_req(&child_req, ldb, ac,
326 364948 : msg, ac->req->controls,
327 : ac, oc_op_callback, ac->req);
328 388146 : LDB_REQ_SET_LOCATION(child_req);
329 : }
330 930186 : if (ret != LDB_SUCCESS) {
331 0 : return ret;
332 : }
333 :
334 930186 : return ldb_next_request(ac->module, child_req);
335 : }
336 :
337 : /*
338 : these are attributes which are left over from old ways of doing
339 : things in ldb, and are harmless
340 : */
341 : static const char *harmless_attrs[] = { "parentGUID", NULL };
342 :
343 929368 : static int attr_handler2(struct oc_context *ac)
344 : {
345 106786 : struct ldb_context *ldb;
346 106786 : struct ldb_message_element *oc_element;
347 106786 : struct ldb_message *msg;
348 106786 : const char **must_contain, **may_contain, **found_must_contain;
349 : /* There exists a hardcoded delete-protected attributes list in AD */
350 929368 : const char *del_prot_attributes[] = { "nTSecurityDescriptor",
351 : "objectSid", "sAMAccountType", "sAMAccountName", "groupType",
352 : "primaryGroupID", "userAccountControl", "accountExpires",
353 : "badPasswordTime", "badPwdCount", "codePage", "countryCode",
354 : "lastLogoff", "lastLogon", "logonCount", "pwdLastSet", NULL },
355 : **l;
356 106786 : const struct dsdb_attribute *attr;
357 106786 : unsigned int i;
358 106786 : bool found;
359 929368 : bool isSchemaAttr = false;
360 :
361 929368 : ldb = ldb_module_get_ctx(ac->module);
362 :
363 929368 : if (ac->search_res == NULL) {
364 0 : return ldb_operr(ldb);
365 : }
366 :
367 : /* We rely here on the preceding "objectclass" LDB module which did
368 : * already fix up the objectclass list (inheritance, order...). */
369 929368 : oc_element = ldb_msg_find_element(ac->search_res->message,
370 : "objectClass");
371 929368 : if (oc_element == NULL) {
372 0 : return ldb_operr(ldb);
373 : }
374 :
375 : /* LSA-specific object classes are not allowed to be created over LDAP,
376 : * so we need to tell if this connection is internal (trusted) or not
377 : * (untrusted).
378 : *
379 : * Hongwei Sun from Microsoft explains:
380 : * The constraint in 3.1.1.5.2.2 MS-ADTS means that LSA objects cannot
381 : * be added or modified through the LDAP interface, instead they can
382 : * only be handled through LSA Policy API. This is also explained in
383 : * 7.1.6.9.7 MS-ADTS as follows:
384 : * "Despite being replicated normally between peer DCs in a domain,
385 : * the process of creating or manipulating TDOs is specifically
386 : * restricted to the LSA Policy APIs, as detailed in [MS-LSAD] section
387 : * 3.1.1.5. Unlike other objects in the DS, TDOs may not be created or
388 : * manipulated by client machines over the LDAPv3 transport."
389 : */
390 3081586 : for (i = 0; i < oc_element->num_values; i++) {
391 2152220 : char * attname = (char *)oc_element->values[i].data;
392 2152220 : if (ldb_req_is_untrusted(ac->req)) {
393 499157 : if (strcmp(attname, "secret") == 0 ||
394 499155 : strcmp(attname, "trustedDomain") == 0) {
395 2 : ldb_asprintf_errstring(ldb, "objectclass_attrs: LSA objectclasses (entry '%s') cannot be created or changed over LDAP!",
396 2 : ldb_dn_get_linearized(ac->search_res->message->dn));
397 2 : return LDB_ERR_UNWILLING_TO_PERFORM;
398 : }
399 : }
400 2152218 : if (strcmp(attname, "attributeSchema") == 0) {
401 221548 : isSchemaAttr = true;
402 : }
403 : }
404 :
405 929366 : must_contain = dsdb_full_attribute_list(ac, ac->schema, oc_element,
406 : DSDB_SCHEMA_ALL_MUST);
407 929366 : may_contain = dsdb_full_attribute_list(ac, ac->schema, oc_element,
408 : DSDB_SCHEMA_ALL_MAY);
409 929366 : found_must_contain = const_str_list(str_list_copy(ac, must_contain));
410 929366 : if ((must_contain == NULL) || (may_contain == NULL)
411 929366 : || (found_must_contain == NULL)) {
412 0 : return ldb_operr(ldb);
413 : }
414 :
415 : /* Check the delete-protected attributes list */
416 929366 : msg = ac->search_res->message;
417 15798744 : for (l = del_prot_attributes; *l != NULL; l++) {
418 1708576 : struct ldb_message_element *el;
419 :
420 14869442 : el = ldb_msg_find_element(ac->msg, *l);
421 14869442 : if (el == NULL) {
422 : /*
423 : * It was not specified in the add or modify,
424 : * so it doesn't need to be in the stored record
425 : */
426 13744964 : continue;
427 : }
428 :
429 1124478 : found = str_list_check_ci(must_contain, *l);
430 1124478 : if (!found) {
431 463278 : found = str_list_check_ci(may_contain, *l);
432 : }
433 1124478 : if (found && (ldb_msg_find_element(msg, *l) == NULL)) {
434 64 : ldb_asprintf_errstring(ldb, "objectclass_attrs: delete protected attribute '%s' on entry '%s' missing!",
435 : *l,
436 : ldb_dn_get_linearized(msg->dn));
437 64 : return LDB_ERR_UNWILLING_TO_PERFORM;
438 : }
439 : }
440 :
441 : /* Check if all specified attributes are valid in the given
442 : * objectclasses and if they meet additional schema restrictions. */
443 20656600 : for (i = 0; i < msg->num_elements; i++) {
444 21997347 : attr = dsdb_attribute_by_lDAPDisplayName(ac->schema,
445 19727329 : msg->elements[i].name);
446 19727329 : if (attr == NULL) {
447 0 : if (ldb_request_get_control(ac->req, DSDB_CONTROL_DBCHECK)) {
448 : /* allow this to make it possible for dbcheck
449 : to remove bad attributes */
450 0 : continue;
451 : }
452 0 : return ldb_operr(ldb);
453 : }
454 :
455 19727329 : if (attr->linkID & 1) {
456 : /*
457 : * We need to allow backlinks on all objects
458 : * even if the schema doesn't allow it.
459 : */
460 98022 : continue;
461 : }
462 :
463 : /* We can use "str_list_check" with "strcmp" here since the
464 : * attribute information from the schema are always equal
465 : * up-down-cased. */
466 19629307 : found = str_list_check(must_contain, attr->lDAPDisplayName);
467 19629307 : if (found) {
468 6475341 : str_list_remove(found_must_contain, attr->lDAPDisplayName);
469 : } else {
470 13153966 : found = str_list_check(may_contain, attr->lDAPDisplayName);
471 : }
472 19629307 : if (!found) {
473 31 : found = str_list_check(harmless_attrs, attr->lDAPDisplayName);
474 : }
475 19629307 : if (!found) {
476 : /* we allow this for dbcheck to fix the rest of this broken entry */
477 31 : if (!ldb_request_get_control(ac->req, DSDB_CONTROL_DBCHECK) ||
478 0 : ac->req->operation == LDB_ADD) {
479 31 : ldb_asprintf_errstring(ldb, "objectclass_attrs: attribute '%s' on entry '%s' does not exist in the specified objectclasses!",
480 31 : msg->elements[i].name,
481 : ldb_dn_get_linearized(msg->dn));
482 31 : return LDB_ERR_OBJECT_CLASS_VIOLATION;
483 : }
484 : }
485 : }
486 :
487 : /*
488 : * We skip this check under dbcheck to allow fixing of other
489 : * attributes even if an attribute is missing. This matters
490 : * for CN=RID Set as the required attribute rIDNextRid is not
491 : * replicated.
492 : */
493 964563 : if (found_must_contain[0] != NULL &&
494 35292 : ldb_msg_check_string_attribute(msg, "isDeleted", "TRUE") == 0) {
495 :
496 62 : for (i = 0; found_must_contain[i] != NULL; i++) {
497 42 : const struct dsdb_attribute *broken_attr = dsdb_attribute_by_lDAPDisplayName(ac->schema,
498 42 : found_must_contain[i]);
499 :
500 42 : bool replicated = (broken_attr->systemFlags &
501 : (DS_FLAG_ATTR_NOT_REPLICATED | DS_FLAG_ATTR_IS_CONSTRUCTED)) == 0;
502 :
503 42 : if (replicated) {
504 3 : ldb_asprintf_errstring(ldb, "objectclass_attrs: at least one mandatory "
505 : "attribute ('%s') on entry '%s' wasn't specified!",
506 3 : found_must_contain[i],
507 : ldb_dn_get_linearized(msg->dn));
508 3 : return LDB_ERR_OBJECT_CLASS_VIOLATION;
509 : }
510 : }
511 : }
512 :
513 929268 : if (isSchemaAttr) {
514 : /*
515 : * Before really adding an attribute in the database,
516 : * let's check that we can translate it into a dsdb_attribute and
517 : * that we can find a valid syntax object.
518 : * If not it's better to reject this attribute than not be able
519 : * to start samba next time due to schema being unloadable.
520 : */
521 221548 : struct dsdb_attribute *att = talloc(ac, struct dsdb_attribute);
522 36920 : const struct dsdb_syntax *attrSyntax;
523 36920 : WERROR status;
524 :
525 221548 : status = dsdb_attribute_from_ldb(NULL, msg, att);
526 221548 : if (!W_ERROR_IS_OK(status)) {
527 0 : ldb_set_errstring(ldb,
528 : "objectclass: failed to translate the schemaAttribute to a dsdb_attribute");
529 0 : return LDB_ERR_UNWILLING_TO_PERFORM;
530 : }
531 :
532 221548 : attrSyntax = dsdb_syntax_for_attribute(att);
533 221548 : if (!attrSyntax) {
534 0 : ldb_set_errstring(ldb,
535 : "objectclass: unknown attribute syntax");
536 0 : return LDB_ERR_UNWILLING_TO_PERFORM;
537 : }
538 : }
539 929268 : return ldb_module_done(ac->req, ac->mod_ares->controls,
540 929268 : ac->mod_ares->response, LDB_SUCCESS);
541 : }
542 :
543 1858736 : static int get_search_callback(struct ldb_request *req, struct ldb_reply *ares)
544 : {
545 213572 : struct ldb_context *ldb;
546 213572 : struct oc_context *ac;
547 213572 : int ret;
548 :
549 1858736 : ac = talloc_get_type(req->context, struct oc_context);
550 1858736 : ldb = ldb_module_get_ctx(ac->module);
551 :
552 1858736 : if (!ares) {
553 0 : return ldb_module_done(ac->req, NULL, NULL,
554 : LDB_ERR_OPERATIONS_ERROR);
555 : }
556 1858736 : if (ares->error != LDB_SUCCESS) {
557 0 : return ldb_module_done(ac->req, ares->controls,
558 : ares->response, ares->error);
559 : }
560 :
561 1858736 : ldb_reset_err_string(ldb);
562 :
563 1858736 : switch (ares->type) {
564 929368 : case LDB_REPLY_ENTRY:
565 929368 : if (ac->search_res != NULL) {
566 0 : ldb_set_errstring(ldb, "Too many results");
567 0 : talloc_free(ares);
568 0 : return ldb_module_done(ac->req, NULL, NULL,
569 : LDB_ERR_OPERATIONS_ERROR);
570 : }
571 :
572 929368 : ac->search_res = talloc_steal(ac, ares);
573 929368 : break;
574 :
575 0 : case LDB_REPLY_REFERRAL:
576 : /* ignore */
577 0 : talloc_free(ares);
578 0 : break;
579 :
580 929368 : case LDB_REPLY_DONE:
581 929368 : talloc_free(ares);
582 929368 : ret = attr_handler2(ac);
583 929368 : if (ret != LDB_SUCCESS) {
584 100 : return ldb_module_done(ac->req, NULL, NULL, ret);
585 : }
586 822482 : break;
587 : }
588 :
589 1645064 : return LDB_SUCCESS;
590 : }
591 :
592 930194 : static int oc_op_callback(struct ldb_request *req, struct ldb_reply *ares)
593 : {
594 106787 : struct oc_context *ac;
595 106787 : struct ldb_context *ldb;
596 106787 : struct ldb_request *search_req;
597 106787 : struct ldb_dn *base_dn;
598 106787 : int ret;
599 106787 : static const char *attrs[] = {"nTSecurityDescriptor", "*", NULL};
600 :
601 930194 : ac = talloc_get_type(req->context, struct oc_context);
602 930194 : ldb = ldb_module_get_ctx(ac->module);
603 :
604 930194 : if (!ares) {
605 0 : return ldb_module_done(ac->req, NULL, NULL,
606 : LDB_ERR_OPERATIONS_ERROR);
607 : }
608 :
609 930194 : if (ares->type == LDB_REPLY_REFERRAL) {
610 10 : return ldb_module_send_referral(ac->req, ares->referral);
611 : }
612 :
613 930184 : if (ares->error != LDB_SUCCESS) {
614 816 : return ldb_module_done(ac->req, ares->controls, ares->response,
615 : ares->error);
616 : }
617 :
618 929368 : if (ares->type != LDB_REPLY_DONE) {
619 0 : talloc_free(ares);
620 0 : return ldb_module_done(ac->req, NULL, NULL,
621 : LDB_ERR_OPERATIONS_ERROR);
622 : }
623 :
624 929368 : ac->search_res = NULL;
625 929368 : ac->mod_ares = talloc_steal(ac, ares);
626 :
627 : /* This looks up all attributes of our just added/modified entry */
628 2400503 : base_dn = ac->req->operation == LDB_ADD ? ac->req->op.add.message->dn
629 929368 : : ac->req->op.mod.message->dn;
630 929368 : ret = ldb_build_search_req(&search_req, ldb, ac, base_dn,
631 : LDB_SCOPE_BASE, "(objectClass=*)",
632 : attrs, NULL, ac,
633 : get_search_callback, ac->req);
634 929368 : LDB_REQ_SET_LOCATION(search_req);
635 929368 : if (ret != LDB_SUCCESS) {
636 0 : return ldb_module_done(ac->req, NULL, NULL, ret);
637 : }
638 :
639 929368 : ret = ldb_request_add_control(search_req, LDB_CONTROL_SHOW_RECYCLED_OID,
640 : true, NULL);
641 929368 : if (ret != LDB_SUCCESS) {
642 0 : return ldb_module_done(ac->req, NULL, NULL, ret);
643 : }
644 :
645 : /*
646 : * This ensures we see if there was a DN, that pointed at an
647 : * object that is now deleted, that we still consider the
648 : * schema check to have passed
649 : */
650 929368 : ret = ldb_request_add_control(search_req, LDB_CONTROL_REVEAL_INTERNALS,
651 : false, NULL);
652 929368 : if (ret != LDB_SUCCESS) {
653 0 : return ldb_module_done(ac->req, NULL, NULL, ret);
654 : }
655 :
656 929368 : ret = ldb_next_request(ac->module, search_req);
657 929368 : if (ret != LDB_SUCCESS) {
658 0 : return ldb_module_done(ac->req, NULL, NULL, ret);
659 : }
660 :
661 : /* "ldb_module_done" isn't called here since we need to do additional
662 : * checks. It is called at the end of "attr_handler2". */
663 822582 : return LDB_SUCCESS;
664 : }
665 :
666 542626 : static int objectclass_attrs_add(struct ldb_module *module,
667 : struct ldb_request *req)
668 : {
669 83657 : struct ldb_context *ldb;
670 83657 : struct oc_context *ac;
671 :
672 542626 : ldb = ldb_module_get_ctx(module);
673 :
674 542626 : ldb_debug(ldb, LDB_DEBUG_TRACE, "objectclass_attrs_add\n");
675 :
676 : /* do not manipulate our control entries */
677 542626 : if (ldb_dn_is_special(req->op.add.message->dn)) {
678 538 : return ldb_next_request(module, req);
679 : }
680 :
681 542088 : ac = oc_init_context(module, req);
682 542088 : if (ac == NULL) {
683 0 : return ldb_operr(ldb);
684 : }
685 :
686 : /* without schema, there isn't much to do here */
687 542088 : if (ac->schema == NULL) {
688 0 : talloc_free(ac);
689 0 : return ldb_next_request(module, req);
690 : }
691 :
692 542088 : return attr_handler(ac);
693 : }
694 :
695 640222 : static int objectclass_attrs_modify(struct ldb_module *module,
696 : struct ldb_request *req)
697 : {
698 27627 : struct ldb_context *ldb;
699 27627 : struct ldb_control *sd_propagation_control;
700 27627 : int ret;
701 :
702 27627 : struct oc_context *ac;
703 :
704 640222 : ldb = ldb_module_get_ctx(module);
705 :
706 640222 : ldb_debug(ldb, LDB_DEBUG_TRACE, "objectclass_attrs_modify\n");
707 :
708 : /* do not manipulate our control entries */
709 640222 : if (ldb_dn_is_special(req->op.mod.message->dn)) {
710 715 : return ldb_next_request(module, req);
711 : }
712 :
713 639507 : sd_propagation_control = ldb_request_get_control(req,
714 : DSDB_CONTROL_SEC_DESC_PROPAGATION_OID);
715 639507 : if (sd_propagation_control != NULL) {
716 251305 : if (req->op.mod.message->num_elements != 1) {
717 0 : return ldb_module_operr(module);
718 : }
719 251305 : ret = strcmp(req->op.mod.message->elements[0].name,
720 : "nTSecurityDescriptor");
721 251305 : if (ret != 0) {
722 0 : return ldb_module_operr(module);
723 : }
724 :
725 251305 : return ldb_next_request(module, req);
726 : }
727 :
728 388202 : ac = oc_init_context(module, req);
729 388202 : if (ac == NULL) {
730 0 : return ldb_operr(ldb);
731 : }
732 :
733 : /* without schema, there isn't much to do here */
734 388202 : if (ac->schema == NULL) {
735 0 : talloc_free(ac);
736 0 : return ldb_next_request(module, req);
737 : }
738 :
739 388202 : return attr_handler(ac);
740 : }
741 :
742 : static const struct ldb_module_ops ldb_objectclass_attrs_module_ops = {
743 : .name = "objectclass_attrs",
744 : .add = objectclass_attrs_add,
745 : .modify = objectclass_attrs_modify
746 : };
747 :
748 5903 : int ldb_objectclass_attrs_module_init(const char *version)
749 : {
750 5903 : LDB_MODULE_CHECK_VERSION(version);
751 5903 : return ldb_register_module(&ldb_objectclass_attrs_module_ops);
752 : }
|