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) Matthias Dieter Wallnöfer 2010-2011
7 :
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.
12 :
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.
17 :
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/>.
20 : */
21 :
22 : /*
23 : * Name: ldb
24 : *
25 : * Component: objectClass sorting and constraint checking module
26 : *
27 : * Description:
28 : * - sort the objectClass attribute into the class
29 : * hierarchy and perform constraint checks (correct RDN name,
30 : * valid parent),
31 : * - fix DNs into 'standard' case
32 : * - Add objectCategory and some other attribute defaults
33 : *
34 : * Author: Andrew Bartlett
35 : */
36 :
37 :
38 : #include "includes.h"
39 : #include "ldb_module.h"
40 : #include "dsdb/samdb/samdb.h"
41 : #include "librpc/ndr/libndr.h"
42 : #include "librpc/gen_ndr/ndr_security.h"
43 : #include "libcli/security/security.h"
44 : #include "auth/auth.h"
45 : #include "param/param.h"
46 : #include "../libds/common/flags.h"
47 : #include "dsdb/samdb/ldb_modules/util.h"
48 :
49 : #undef strcasecmp
50 :
51 : struct oc_context {
52 :
53 : struct ldb_module *module;
54 : struct ldb_request *req;
55 : const struct dsdb_schema *schema;
56 :
57 : struct ldb_reply *search_res;
58 : struct ldb_reply *search_res2;
59 :
60 : int (*step_fn)(struct oc_context *);
61 : };
62 :
63 975603 : static struct oc_context *oc_init_context(struct ldb_module *module,
64 : struct ldb_request *req)
65 : {
66 100910 : struct ldb_context *ldb;
67 100910 : struct oc_context *ac;
68 :
69 975603 : ldb = ldb_module_get_ctx(module);
70 :
71 975603 : ac = talloc_zero(req, struct oc_context);
72 975603 : if (ac == NULL) {
73 0 : ldb_oom(ldb);
74 0 : return NULL;
75 : }
76 :
77 975603 : ac->module = module;
78 975603 : ac->req = req;
79 975603 : ac->schema = dsdb_get_schema(ldb, ac);
80 :
81 975603 : return ac;
82 : }
83 :
84 : static int objectclass_do_add(struct oc_context *ac);
85 :
86 : /*
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.
91 : */
92 543324 : 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)
96 : {
97 543324 : struct ldb_context *ldb = ldb_module_get_ctx(module);
98 83617 : unsigned int i;
99 83617 : bool found;
100 :
101 543324 : if (schema == NULL) {
102 0 : return LDB_SUCCESS;
103 : }
104 :
105 1703686 : for (i = 0; i < objectclass_element->num_values; i++) {
106 1329331 : const struct dsdb_class *tmp_class = dsdb_class_by_lDAPDisplayName_ldb_val(schema,
107 1160367 : &objectclass_element->values[i]);
108 1160367 : const struct dsdb_class *tmp_class2 = struct_objectclass;
109 :
110 : /* Pointer comparison can be used due to the same schema str. */
111 1327607 : if (tmp_class == NULL ||
112 700665 : tmp_class == struct_objectclass ||
113 617048 : tmp_class->objectClassCategory > 2 ||
114 616937 : ldb_attr_cmp(tmp_class->lDAPDisplayName, "top") == 0) {
115 1086754 : continue;
116 : }
117 :
118 71889 : found = false;
119 187530 : while (!found &&
120 113922 : ldb_attr_cmp(tmp_class2->lDAPDisplayName, "top") != 0) {
121 116051 : tmp_class2 = dsdb_class_by_lDAPDisplayName(schema,
122 113917 : tmp_class2->subClassOf);
123 113917 : if (tmp_class2 == tmp_class) {
124 73608 : found = true;
125 : }
126 : }
127 73613 : if (found) {
128 73608 : continue;
129 : }
130 :
131 5 : ldb_asprintf_errstring(ldb,
132 : "objectclass: the objectclass '%s' seems to be unrelated to %s!",
133 5 : tmp_class->lDAPDisplayName,
134 5 : struct_objectclass->lDAPDisplayName);
135 5 : return LDB_ERR_OBJECT_CLASS_VIOLATION;
136 : }
137 :
138 459702 : return LDB_SUCCESS;
139 : }
140 :
141 1275491 : static int get_search_callback(struct ldb_request *req, struct ldb_reply *ares)
142 : {
143 167521 : struct ldb_context *ldb;
144 167521 : struct oc_context *ac;
145 167521 : int ret;
146 :
147 1275491 : ac = talloc_get_type(req->context, struct oc_context);
148 1275491 : ldb = ldb_module_get_ctx(ac->module);
149 :
150 1275491 : if (!ares) {
151 0 : return ldb_module_done(ac->req, NULL, NULL,
152 : LDB_ERR_OPERATIONS_ERROR);
153 : }
154 1275491 : if (ares->error != LDB_SUCCESS &&
155 37852 : ares->error != LDB_ERR_NO_SUCH_OBJECT) {
156 2 : return ldb_module_done(ac->req, ares->controls,
157 : ares->response, ares->error);
158 : }
159 :
160 1275489 : ldb_reset_err_string(ldb);
161 :
162 1275489 : switch (ares->type) {
163 618803 : case LDB_REPLY_ENTRY:
164 618803 : if (ac->search_res != NULL) {
165 0 : ldb_set_errstring(ldb, "Too many results");
166 0 : talloc_free(ares);
167 0 : return ldb_module_done(ac->req, NULL, NULL,
168 : LDB_ERR_OPERATIONS_ERROR);
169 : }
170 :
171 618803 : ac->search_res = talloc_steal(ac, ares);
172 618803 : break;
173 :
174 0 : case LDB_REPLY_REFERRAL:
175 : /* ignore */
176 0 : talloc_free(ares);
177 0 : break;
178 :
179 656686 : case LDB_REPLY_DONE:
180 656686 : talloc_free(ares);
181 656686 : ret = ac->step_fn(ac);
182 656686 : if (ret != LDB_SUCCESS) {
183 38831 : return ldb_module_done(ac->req, NULL, NULL, ret);
184 : }
185 534090 : break;
186 : }
187 :
188 1069149 : return LDB_SUCCESS;
189 : }
190 :
191 : /* Fix up the DN to be in the standard form, taking particular care to match the parent DN
192 :
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
197 :
198 : The resulting DN should be:
199 :
200 : CN=Admins,CN=Users,DC=samba,DC=example,DC=com
201 :
202 : */
203 544353 : static int fix_dn(struct ldb_context *ldb,
204 : TALLOC_CTX *mem_ctx,
205 : struct ldb_dn *newdn, struct ldb_dn *parent_dn,
206 : struct ldb_dn **fixed_dn)
207 : {
208 83594 : char *upper_rdn_attr;
209 83594 : const struct ldb_val *rdn_val;
210 :
211 : /* Fix up the DN to be in the standard form, taking particular care to
212 : * match the parent DN */
213 544353 : *fixed_dn = ldb_dn_copy(mem_ctx, parent_dn);
214 544353 : if (*fixed_dn == NULL) {
215 0 : return ldb_oom(ldb);
216 : }
217 :
218 : /* We need the attribute name in upper case */
219 544353 : upper_rdn_attr = strupper_talloc(*fixed_dn,
220 : ldb_dn_get_rdn_name(newdn));
221 544353 : if (upper_rdn_attr == NULL) {
222 0 : return ldb_oom(ldb);
223 : }
224 :
225 : /* Create a new child */
226 544353 : if (ldb_dn_add_child_fmt(*fixed_dn, "X=X") == false) {
227 0 : return ldb_operr(ldb);
228 : }
229 :
230 544353 : rdn_val = ldb_dn_get_rdn_val(newdn);
231 544353 : if (rdn_val == NULL) {
232 0 : return ldb_operr(ldb);
233 : }
234 :
235 : #if 0
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)));
242 : }
243 : #endif
244 :
245 :
246 : /* And replace it with CN=foo (we need the attribute in upper case) */
247 544353 : return ldb_dn_set_component(*fixed_dn, 0, upper_rdn_attr, *rdn_val);
248 : }
249 :
250 :
251 543753 : static int objectclass_add(struct ldb_module *module, struct ldb_request *req)
252 : {
253 83679 : struct ldb_context *ldb;
254 83679 : struct ldb_request *search_req;
255 83679 : struct oc_context *ac;
256 83679 : struct ldb_dn *parent_dn;
257 83679 : const struct ldb_val *val;
258 83679 : int ret;
259 83679 : static const char * const parent_attrs[] = { "objectClass", NULL };
260 :
261 543753 : ldb = ldb_module_get_ctx(module);
262 :
263 543753 : ldb_debug(ldb, LDB_DEBUG_TRACE, "objectclass_add\n");
264 :
265 : /* do not manipulate our control entries */
266 543753 : if (ldb_dn_is_special(req->op.add.message->dn)) {
267 538 : return ldb_next_request(module, req);
268 : }
269 :
270 : /* An add operation on the basedn without "NC-add" operation isn't
271 : * allowed. */
272 543215 : if (ldb_dn_compare(ldb_get_default_basedn(ldb), req->op.add.message->dn) == 0) {
273 22 : unsigned int instanceType;
274 :
275 128 : instanceType = ldb_msg_find_attr_as_uint(req->op.add.message,
276 : "instanceType", 0);
277 128 : if (!(instanceType & INSTANCE_TYPE_IS_NC_HEAD)) {
278 0 : char *referral_uri;
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 1 : val = ldb_dn_get_component_val(req->op.add.message->dn,
284 1 : ldb_dn_get_comp_num(req->op.add.message->dn) - 1);
285 1 : if (val == NULL) {
286 0 : return ldb_operr(ldb);
287 : }
288 1 : referral_uri = talloc_asprintf(req, "ldap://%s/%s", val->data,
289 1 : ldb_dn_get_linearized(req->op.add.message->dn));
290 1 : if (referral_uri == NULL) {
291 0 : return ldb_module_oom(module);
292 : }
293 :
294 1 : return ldb_module_send_referral(req, referral_uri);
295 : }
296 : }
297 :
298 543214 : ac = oc_init_context(module, req);
299 543214 : if (ac == NULL) {
300 0 : return ldb_operr(ldb);
301 : }
302 :
303 : /* If there isn't a parent, just go on to the add processing */
304 543214 : if (ldb_dn_get_comp_num(ac->req->op.add.message->dn) == 1) {
305 9 : return objectclass_do_add(ac);
306 : }
307 :
308 : /* get copy of parent DN */
309 543205 : parent_dn = ldb_dn_get_parent(ac, ac->req->op.add.message->dn);
310 543205 : if (parent_dn == NULL) {
311 0 : return ldb_operr(ldb);
312 : }
313 :
314 543205 : ret = ldb_build_search_req(&search_req, ldb,
315 : ac, parent_dn, LDB_SCOPE_BASE,
316 : "(objectClass=*)", parent_attrs,
317 : NULL,
318 : ac, get_search_callback,
319 : req);
320 543205 : LDB_REQ_SET_LOCATION(search_req);
321 543205 : if (ret != LDB_SUCCESS) {
322 0 : return ret;
323 : }
324 :
325 543205 : ret = dsdb_request_add_controls(search_req,
326 : DSDB_FLAG_AS_SYSTEM |
327 : DSDB_SEARCH_SHOW_RECYCLED);
328 543205 : if (ret != LDB_SUCCESS) {
329 0 : return ret;
330 : }
331 :
332 543205 : ac->step_fn = objectclass_do_add;
333 :
334 543205 : return ldb_next_request(ac->module, search_req);
335 : }
336 :
337 :
338 : /*
339 : check if this is a special RODC nTDSDSA add
340 : */
341 93 : static bool check_rodc_ntdsdsa_add(struct oc_context *ac,
342 : const struct dsdb_class *objectclass)
343 : {
344 0 : struct ldb_control *rodc_control;
345 :
346 93 : if (ldb_attr_cmp(objectclass->lDAPDisplayName, "nTDSDSA") != 0) {
347 1 : return false;
348 : }
349 92 : rodc_control = ldb_request_get_control(ac->req, LDB_CONTROL_RODC_DCPROMO_OID);
350 92 : if (!rodc_control) {
351 0 : return false;
352 : }
353 :
354 92 : rodc_control->critical = false;
355 92 : return true;
356 : }
357 :
358 543214 : static int objectclass_do_add(struct oc_context *ac)
359 : {
360 543214 : struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
361 83611 : struct ldb_request *add_req;
362 83611 : struct ldb_message_element *objectclass_element, *el;
363 83611 : struct ldb_message *msg;
364 543214 : const char *rdn_name = NULL;
365 83611 : char *value;
366 83611 : const struct dsdb_class *objectclass;
367 83611 : struct ldb_dn *objectcategory;
368 543214 : int32_t systemFlags = 0;
369 83611 : unsigned int i, j;
370 83611 : bool found;
371 83611 : int ret;
372 :
373 543214 : msg = ldb_msg_copy_shallow(ac, ac->req->op.add.message);
374 543214 : if (msg == NULL) {
375 0 : return ldb_module_oom(ac->module);
376 : }
377 :
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 543214 : if (ac->search_res == NULL) {
381 22 : unsigned int instanceType;
382 :
383 : /* An add operation on partition DNs without "NC-add" operation
384 : * isn't allowed. */
385 128 : instanceType = ldb_msg_find_attr_as_uint(msg, "instanceType",
386 : 0);
387 128 : if (!(instanceType & INSTANCE_TYPE_IS_NC_HEAD)) {
388 1 : ldb_asprintf_errstring(ldb, "objectclass: Cannot add %s, parent does not exist!",
389 : ldb_dn_get_linearized(msg->dn));
390 1 : return LDB_ERR_NO_SUCH_OBJECT;
391 : }
392 :
393 : /* Don't keep any error messages - we've to add a partition */
394 127 : ldb_set_errstring(ldb, NULL);
395 : } else {
396 : /* Fix up the DN to be in the standard form, taking
397 : * particular care to match the parent DN */
398 626675 : ret = fix_dn(ldb, msg,
399 543086 : ac->req->op.add.message->dn,
400 543086 : ac->search_res->message->dn,
401 : &msg->dn);
402 543086 : if (ret != LDB_SUCCESS) {
403 0 : ldb_asprintf_errstring(ldb, "objectclass: Could not munge DN %s into normal form",
404 0 : ldb_dn_get_linearized(ac->req->op.add.message->dn));
405 0 : return ret;
406 : }
407 : }
408 :
409 543213 : if (ac->schema != NULL) {
410 543213 : unsigned int linkID = 0;
411 : /*
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!
415 : */
416 :
417 543213 : objectclass_element = ldb_msg_find_element(msg, "objectClass");
418 543213 : if (!objectclass_element) {
419 1 : ldb_asprintf_errstring(ldb, "objectclass: Cannot add %s, no objectclass specified!",
420 : ldb_dn_get_linearized(msg->dn));
421 1 : return LDB_ERR_OBJECT_CLASS_VIOLATION;
422 : }
423 543212 : if (objectclass_element->num_values == 0) {
424 1 : 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 1 : return LDB_ERR_CONSTRAINT_VIOLATION;
427 : }
428 :
429 : /* Now do the sorting */
430 543211 : ret = dsdb_sort_objectClass_attr(ldb, ac->schema,
431 : objectclass_element, msg,
432 : objectclass_element);
433 543211 : if (ret != LDB_SUCCESS) {
434 1 : return ret;
435 : }
436 :
437 : /*
438 : * Get the new top-most structural object class and check for
439 : * unrelated structural classes
440 : */
441 543210 : objectclass = dsdb_get_last_structural_class(ac->schema,
442 : objectclass_element);
443 543210 : if (objectclass == NULL) {
444 1 : ldb_asprintf_errstring(ldb,
445 : "Failed to find a structural class for %s",
446 : ldb_dn_get_linearized(msg->dn));
447 1 : return LDB_ERR_UNWILLING_TO_PERFORM;
448 : }
449 :
450 543209 : ret = check_unrelated_objectclasses(ac->module, ac->schema,
451 : objectclass,
452 : objectclass_element);
453 543209 : if (ret != LDB_SUCCESS) {
454 2 : return ret;
455 : }
456 :
457 543207 : rdn_name = ldb_dn_get_rdn_name(msg->dn);
458 543207 : if (rdn_name == NULL) {
459 0 : return ldb_operr(ldb);
460 : }
461 459596 : found = false;
462 1129672 : for (i = 0; (!found) && (i < objectclass_element->num_values);
463 586465 : i++) {
464 84978 : const struct dsdb_class *tmp_class =
465 671443 : dsdb_class_by_lDAPDisplayName_ldb_val(ac->schema,
466 586465 : &objectclass_element->values[i]);
467 :
468 586465 : if (tmp_class == NULL) continue;
469 :
470 586465 : if (ldb_attr_cmp(rdn_name, tmp_class->rDNAttID) == 0)
471 543206 : found = true;
472 : }
473 543207 : if (!found) {
474 1 : ldb_asprintf_errstring(ldb,
475 : "objectclass: Invalid RDN '%s' for objectclass '%s'!",
476 1 : rdn_name, objectclass->lDAPDisplayName);
477 1 : return LDB_ERR_NAMING_VIOLATION;
478 : }
479 :
480 544881 : if (objectclass->systemOnly &&
481 1675 : !ldb_request_get_control(ac->req, LDB_CONTROL_RELAX_OID) &&
482 93 : !check_rodc_ntdsdsa_add(ac, objectclass)) {
483 1 : ldb_asprintf_errstring(ldb,
484 : "objectclass: object class '%s' is system-only, rejecting creation of '%s'!",
485 1 : objectclass->lDAPDisplayName,
486 : ldb_dn_get_linearized(msg->dn));
487 1 : return LDB_ERR_UNWILLING_TO_PERFORM;
488 : }
489 :
490 543205 : if (ac->search_res && ac->search_res->message) {
491 83589 : struct ldb_message_element *oc_el
492 543078 : = ldb_msg_find_element(ac->search_res->message, "objectClass");
493 :
494 543078 : bool allowed_class = false;
495 1721960 : for (i=0; allowed_class == false && oc_el && i < oc_el->num_values; i++) {
496 167698 : const struct dsdb_class *sclass;
497 :
498 1262991 : sclass = dsdb_class_by_lDAPDisplayName_ldb_val(ac->schema,
499 1095293 : &oc_el->values[i]);
500 1095293 : if (!sclass) {
501 : /* We don't know this class? what is going on? */
502 0 : continue;
503 : }
504 11264486 : for (j=0; sclass->systemPossibleInferiors && sclass->systemPossibleInferiors[j]; j++) {
505 10712268 : if (ldb_attr_cmp(objectclass->lDAPDisplayName, sclass->systemPossibleInferiors[j]) == 0) {
506 459486 : allowed_class = true;
507 459486 : break;
508 : }
509 : }
510 : }
511 :
512 543078 : if (!allowed_class) {
513 6 : ldb_asprintf_errstring(ldb, "structural objectClass %s is not a valid child class for %s",
514 3 : objectclass->lDAPDisplayName, ldb_dn_get_linearized(ac->search_res->message->dn));
515 3 : return LDB_ERR_NAMING_VIOLATION;
516 : }
517 : }
518 :
519 543202 : objectcategory = ldb_msg_find_attr_as_dn(ldb, ac, msg,
520 : "objectCategory");
521 543202 : if (objectcategory == NULL) {
522 83611 : struct dsdb_extended_dn_store_format *dn_format =
523 542671 : talloc_get_type(ldb_module_get_private(ac->module),
524 : struct dsdb_extended_dn_store_format);
525 542671 : if (dn_format && dn_format->store_extended_dn_in_ldb == false) {
526 : /* Strip off extended components */
527 0 : struct ldb_dn *dn = ldb_dn_new(ac, ldb,
528 0 : objectclass->defaultObjectCategory);
529 0 : value = ldb_dn_alloc_linearized(msg, dn);
530 0 : talloc_free(dn);
531 : } else {
532 542671 : value = talloc_strdup(msg,
533 542671 : objectclass->defaultObjectCategory);
534 : }
535 542671 : if (value == NULL) {
536 0 : return ldb_module_oom(ac->module);
537 : }
538 :
539 542671 : ret = ldb_msg_add_string(msg, "objectCategory", value);
540 542671 : if (ret != LDB_SUCCESS) {
541 0 : return ret;
542 : }
543 : } else {
544 0 : const struct dsdb_class *ocClass =
545 531 : dsdb_class_by_cn_ldb_val(ac->schema,
546 : ldb_dn_get_rdn_val(objectcategory));
547 531 : if (ocClass != NULL) {
548 530 : struct ldb_dn *dn = ldb_dn_new(ac, ldb,
549 530 : ocClass->defaultObjectCategory);
550 530 : if (ldb_dn_compare(objectcategory, dn) != 0) {
551 0 : ocClass = NULL;
552 : }
553 : }
554 531 : talloc_free(objectcategory);
555 531 : if (ocClass == NULL) {
556 1 : ldb_asprintf_errstring(ldb, "objectclass: Cannot add %s, 'objectCategory' attribute invalid!",
557 : ldb_dn_get_linearized(msg->dn));
558 1 : return LDB_ERR_OBJECT_CLASS_VIOLATION;
559 : }
560 : }
561 :
562 543201 : if (!ldb_msg_find_element(msg, "showInAdvancedViewOnly") && (objectclass->defaultHidingValue == true)) {
563 254205 : ldb_msg_add_string(msg, "showInAdvancedViewOnly",
564 : "TRUE");
565 : }
566 :
567 : /* There are very special rules for systemFlags, see MS-ADTS
568 : * MS-ADTS 3.1.1.5.2.4 */
569 :
570 543201 : el = ldb_msg_find_element(msg, "systemFlags");
571 543201 : if ((el != NULL) && (el->num_values > 1)) {
572 1 : ldb_asprintf_errstring(ldb, "objectclass: Cannot add %s, 'systemFlags' attribute multivalued!",
573 : ldb_dn_get_linearized(msg->dn));
574 1 : return LDB_ERR_CONSTRAINT_VIOLATION;
575 : }
576 :
577 543200 : systemFlags = ldb_msg_find_attr_as_int(msg, "systemFlags", 0);
578 :
579 543200 : ldb_msg_remove_attr(msg, "systemFlags");
580 :
581 : /* Only the following flags may be set by a client */
582 543200 : if (ldb_request_get_control(ac->req,
583 : LDB_CONTROL_RELAX_OID) == NULL) {
584 271331 : 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 );
588 : }
589 :
590 : /* But the last one ("ATTR_IS_RDN") is only allowed on
591 : * "attributeSchema" objects. So truncate if it does not fit. */
592 543200 : if (ldb_attr_cmp(objectclass->lDAPDisplayName, "attributeSchema") != 0) {
593 358800 : systemFlags &= ~SYSTEM_FLAG_ATTR_IS_RDN;
594 : }
595 :
596 543200 : if (ldb_attr_cmp(objectclass->lDAPDisplayName, "server") == 0) {
597 633 : systemFlags |= (int32_t)(SYSTEM_FLAG_DISALLOW_MOVE_ON_DELETE | SYSTEM_FLAG_CONFIG_ALLOW_RENAME | SYSTEM_FLAG_CONFIG_ALLOW_LIMITED_MOVE);
598 542567 : } else if (ldb_attr_cmp(objectclass->lDAPDisplayName, "site") == 0
599 542369 : || ldb_attr_cmp(objectclass->lDAPDisplayName, "serversContainer") == 0
600 542171 : || ldb_attr_cmp(objectclass->lDAPDisplayName, "nTDSDSA") == 0) {
601 747 : if (ldb_attr_cmp(objectclass->lDAPDisplayName, "site") == 0)
602 198 : systemFlags |= (int32_t)(SYSTEM_FLAG_CONFIG_ALLOW_RENAME);
603 747 : systemFlags |= (int32_t)(SYSTEM_FLAG_DISALLOW_MOVE_ON_DELETE);
604 541820 : } else if (ldb_attr_cmp(objectclass->lDAPDisplayName, "siteLink") == 0
605 541675 : || ldb_attr_cmp(objectclass->lDAPDisplayName, "subnet") == 0
606 541466 : || ldb_attr_cmp(objectclass->lDAPDisplayName, "siteLinkBridge") == 0
607 541466 : || ldb_attr_cmp(objectclass->lDAPDisplayName, "nTDSConnection") == 0) {
608 462 : systemFlags |= (int32_t)(SYSTEM_FLAG_CONFIG_ALLOW_RENAME);
609 : }
610 : /* TODO: If parent object is site or subnet, also add (SYSTEM_FLAG_CONFIG_ALLOW_RENAME) */
611 :
612 543200 : linkID = ldb_msg_find_attr_as_int(msg, "linkID", 0);
613 543200 : if (linkID > 0 && linkID % 2 == 1) {
614 6575 : systemFlags |= DS_FLAG_ATTR_NOT_REPLICATED;
615 : }
616 :
617 543200 : if (el || systemFlags != 0) {
618 206716 : ret = samdb_msg_add_int(ldb, msg, msg, "systemFlags",
619 : systemFlags);
620 206716 : if (ret != LDB_SUCCESS) {
621 0 : return ret;
622 : }
623 : }
624 :
625 : /* make sure that "isCriticalSystemObject" is not specified! */
626 543200 : el = ldb_msg_find_element(msg, "isCriticalSystemObject");
627 556192 : if ((el != NULL) &&
628 12992 : !ldb_request_get_control(ac->req, LDB_CONTROL_RELAX_OID)) {
629 1 : ldb_set_errstring(ldb,
630 : "objectclass: 'isCriticalSystemObject' must not be specified!");
631 1 : return LDB_ERR_UNWILLING_TO_PERFORM;
632 : }
633 : }
634 :
635 543199 : ret = ldb_build_add_req(&add_req, ldb, ac,
636 : msg,
637 459588 : ac->req->controls,
638 459588 : ac->req, dsdb_next_callback,
639 : ac->req);
640 543199 : LDB_REQ_SET_LOCATION(add_req);
641 543199 : if (ret != LDB_SUCCESS) {
642 0 : return ret;
643 : }
644 :
645 : /* perform the add */
646 543199 : return ldb_next_request(ac->module, add_req);
647 : }
648 :
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);
652 :
653 321259 : static int objectclass_modify(struct ldb_module *module, struct ldb_request *req)
654 : {
655 321259 : struct ldb_context *ldb = ldb_module_get_ctx(module);
656 17201 : struct ldb_message_element *objectclass_element;
657 17201 : struct ldb_message *msg;
658 17201 : struct ldb_request *down_req;
659 17201 : struct oc_context *ac;
660 321259 : bool oc_changes = false;
661 17201 : int ret;
662 :
663 321259 : ldb_debug(ldb, LDB_DEBUG_TRACE, "objectclass_modify\n");
664 :
665 : /* do not manipulate our control entries */
666 321259 : if (ldb_dn_is_special(req->op.mod.message->dn)) {
667 715 : return ldb_next_request(module, req);
668 : }
669 :
670 : /* As with the "real" AD we don't accept empty messages */
671 320544 : if (req->op.mod.message->num_elements == 0) {
672 6 : ldb_set_errstring(ldb, "objectclass: modify message must have "
673 : "elements/attributes!");
674 6 : return LDB_ERR_UNWILLING_TO_PERFORM;
675 : }
676 :
677 320538 : ac = oc_init_context(module, req);
678 320538 : if (ac == NULL) {
679 0 : return ldb_operr(ldb);
680 : }
681 :
682 : /* Without schema, there isn't much to do here */
683 320538 : if (ac->schema == NULL) {
684 0 : talloc_free(ac);
685 0 : return ldb_next_request(module, req);
686 : }
687 :
688 320538 : msg = ldb_msg_copy_shallow(ac, req->op.mod.message);
689 320538 : if (msg == NULL) {
690 0 : return ldb_module_oom(ac->module);
691 : }
692 :
693 : /* For now change everything except the objectclasses */
694 :
695 320538 : objectclass_element = ldb_msg_find_element(msg, "objectClass");
696 320538 : if (objectclass_element != NULL) {
697 476 : ldb_msg_remove_attr(msg, "objectClass");
698 476 : oc_changes = true;
699 : }
700 :
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 */
703 303871 : if (oc_changes &&
704 476 : (dsdb_forest_functional_level(ldb) < DS_DOMAIN_FUNCTION_2003)) {
705 0 : struct ldb_dn *nc_root;
706 :
707 0 : ret = dsdb_find_nc_root(ldb, ac, req->op.mod.message->dn,
708 : &nc_root);
709 0 : if (ret != LDB_SUCCESS) {
710 0 : return ret;
711 : }
712 :
713 0 : if ((ldb_dn_compare(nc_root, ldb_get_default_basedn(ldb)) == 0) ||
714 0 : (ldb_dn_compare(nc_root, ldb_get_config_basedn(ldb)) == 0) ||
715 0 : (ldb_dn_compare(nc_root, ldb_get_schema_basedn(ldb)) == 0)) {
716 0 : ldb_set_errstring(ldb,
717 : "objectclass: object class changes on objects under the standard name contexts not allowed!");
718 0 : return LDB_ERR_UNWILLING_TO_PERFORM;
719 : }
720 :
721 0 : talloc_free(nc_root);
722 : }
723 :
724 320538 : if (oc_changes) {
725 476 : ret = ldb_build_mod_req(&down_req, ldb, ac,
726 : msg,
727 : req->controls, ac,
728 : oc_modify_callback,
729 : req);
730 : } else {
731 320062 : ret = ldb_build_mod_req(&down_req, ldb, ac,
732 : msg,
733 : req->controls, req,
734 : dsdb_next_callback,
735 : req);
736 : }
737 320538 : LDB_REQ_SET_LOCATION(down_req);
738 320538 : if (ret != LDB_SUCCESS) {
739 0 : return ret;
740 : }
741 :
742 320538 : return ldb_next_request(module, down_req);
743 : }
744 :
745 476 : static int oc_modify_callback(struct ldb_request *req, struct ldb_reply *ares)
746 : {
747 6 : static const char * const attrs[] = { "objectClass", NULL };
748 6 : struct ldb_context *ldb;
749 6 : struct ldb_request *search_req;
750 6 : struct oc_context *ac;
751 6 : int ret;
752 :
753 476 : ac = talloc_get_type(req->context, struct oc_context);
754 476 : ldb = ldb_module_get_ctx(ac->module);
755 :
756 476 : if (!ares) {
757 0 : return ldb_module_done(ac->req, NULL, NULL,
758 : LDB_ERR_OPERATIONS_ERROR);
759 : }
760 :
761 476 : if (ares->type == LDB_REPLY_REFERRAL) {
762 0 : return ldb_module_send_referral(ac->req, ares->referral);
763 : }
764 :
765 476 : if (ares->error != LDB_SUCCESS) {
766 120 : return ldb_module_done(ac->req, ares->controls,
767 : ares->response, ares->error);
768 : }
769 :
770 356 : if (ares->type != LDB_REPLY_DONE) {
771 0 : talloc_free(ares);
772 0 : return ldb_module_done(ac->req, NULL, NULL,
773 : LDB_ERR_OPERATIONS_ERROR);
774 : }
775 :
776 356 : talloc_free(ares);
777 :
778 : /* this looks up the real existing object for fetching some important
779 : * information (objectclasses) */
780 362 : ret = ldb_build_search_req(&search_req, ldb,
781 356 : ac, ac->req->op.mod.message->dn,
782 : LDB_SCOPE_BASE,
783 : "(objectClass=*)",
784 : attrs, NULL,
785 : ac, get_search_callback,
786 : ac->req);
787 356 : LDB_REQ_SET_LOCATION(search_req);
788 356 : if (ret != LDB_SUCCESS) {
789 0 : return ldb_module_done(ac->req, NULL, NULL, ret);
790 : }
791 :
792 356 : ret = dsdb_request_add_controls(search_req,
793 : DSDB_FLAG_AS_SYSTEM |
794 : DSDB_SEARCH_SHOW_RECYCLED);
795 356 : if (ret != LDB_SUCCESS) {
796 0 : return ldb_module_done(ac->req, NULL, NULL, ret);
797 : }
798 :
799 356 : ac->step_fn = objectclass_do_mod;
800 :
801 356 : ret = ldb_next_request(ac->module, search_req);
802 356 : if (ret != LDB_SUCCESS) {
803 0 : return ldb_module_done(ac->req, NULL, NULL, ret);
804 : }
805 :
806 350 : return LDB_SUCCESS;
807 : }
808 :
809 356 : static int objectclass_do_mod(struct oc_context *ac)
810 : {
811 6 : struct ldb_context *ldb;
812 6 : struct ldb_request *mod_req;
813 6 : struct ldb_message_element *oc_el_entry, *oc_el_change;
814 6 : struct ldb_val *vals;
815 6 : struct ldb_message *msg;
816 6 : const struct dsdb_class *current_structural_objectclass;
817 6 : const struct dsdb_class *objectclass;
818 6 : unsigned int i, j, k;
819 6 : bool found;
820 6 : int ret;
821 :
822 356 : ldb = ldb_module_get_ctx(ac->module);
823 :
824 : /* we should always have a valid entry when we enter here */
825 356 : if (ac->search_res == NULL) {
826 0 : return ldb_operr(ldb);
827 : }
828 :
829 356 : oc_el_entry = ldb_msg_find_element(ac->search_res->message,
830 : "objectClass");
831 356 : if (oc_el_entry == NULL) {
832 : /* existing entry without a valid object class? */
833 0 : return ldb_operr(ldb);
834 : }
835 :
836 : /*
837 : * Get the current new top-most structural object class
838 : *
839 : * We must not allow this to change
840 : */
841 :
842 6 : current_structural_objectclass
843 356 : = dsdb_get_last_structural_class(ac->schema,
844 : oc_el_entry);
845 356 : if (current_structural_objectclass == NULL) {
846 0 : ldb_asprintf_errstring(ldb,
847 : "objectclass: cannot find current structural objectclass on %s!",
848 0 : ldb_dn_get_linearized(ac->search_res->message->dn));
849 0 : return LDB_ERR_OBJECT_CLASS_VIOLATION;
850 : }
851 :
852 : /* use a new message structure */
853 356 : msg = ldb_msg_new(ac);
854 356 : if (msg == NULL) {
855 0 : return ldb_module_oom(ac->module);
856 : }
857 :
858 356 : msg->dn = ac->req->op.mod.message->dn;
859 :
860 : /* We've to walk over all "objectClass" message elements */
861 610 : for (k = 0; k < ac->req->op.mod.message->num_elements; k++) {
862 499 : if (ldb_attr_cmp(ac->req->op.mod.message->elements[k].name,
863 : "objectClass") != 0) {
864 142 : continue;
865 : }
866 :
867 357 : oc_el_change = &ac->req->op.mod.message->elements[k];
868 :
869 357 : switch (oc_el_change->flags & LDB_FLAG_MOD_MASK) {
870 107 : case LDB_FLAG_MOD_ADD:
871 : /* Merge the two message elements */
872 177 : for (i = 0; i < oc_el_change->num_values; i++) {
873 497 : for (j = 0; j < oc_el_entry->num_values; j++) {
874 433 : if (ldb_attr_cmp((char *)oc_el_change->values[i].data,
875 : (char *)oc_el_entry->values[j].data) == 0) {
876 49 : ldb_asprintf_errstring(ldb,
877 : "objectclass: cannot re-add an existing objectclass: '%.*s'!",
878 49 : (int)oc_el_change->values[i].length,
879 49 : (const char *)oc_el_change->values[i].data);
880 49 : return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS;
881 : }
882 : }
883 : /* append the new object class value - code was
884 : * copied from "ldb_msg_add_value" */
885 64 : vals = talloc_realloc(oc_el_entry, oc_el_entry->values,
886 : struct ldb_val,
887 : oc_el_entry->num_values + 1);
888 64 : if (vals == NULL) {
889 0 : return ldb_module_oom(ac->module);
890 : }
891 64 : oc_el_entry->values = vals;
892 64 : oc_el_entry->values[oc_el_entry->num_values] =
893 64 : oc_el_change->values[i];
894 64 : ++(oc_el_entry->num_values);
895 : }
896 :
897 58 : break;
898 :
899 179 : case LDB_FLAG_MOD_REPLACE:
900 : /*
901 : * In this case the new "oc_el_entry" is simply
902 : * "oc_el_change"
903 : */
904 179 : oc_el_entry = oc_el_change;
905 :
906 179 : break;
907 :
908 65 : case LDB_FLAG_MOD_DELETE:
909 : /* Merge the two message elements */
910 129 : for (i = 0; i < oc_el_change->num_values; i++) {
911 65 : found = false;
912 140 : for (j = 0; j < oc_el_entry->num_values; j++) {
913 139 : if (ldb_attr_cmp((char *)oc_el_change->values[i].data,
914 : (char *)oc_el_entry->values[j].data) == 0) {
915 64 : found = true;
916 : /* delete the object class value
917 : * - code was copied from
918 : * "ldb_msg_remove_element" */
919 64 : if (j != oc_el_entry->num_values - 1) {
920 58 : memmove(&oc_el_entry->values[j],
921 58 : &oc_el_entry->values[j+1],
922 58 : ((oc_el_entry->num_values-1) - j)*sizeof(struct ldb_val));
923 : }
924 64 : --(oc_el_entry->num_values);
925 64 : break;
926 : }
927 : }
928 65 : if (!found) {
929 : /* we cannot delete a not existing
930 : * object class */
931 1 : ldb_asprintf_errstring(ldb,
932 : "objectclass: cannot delete this objectclass: '%.*s'!",
933 1 : (int)oc_el_change->values[i].length,
934 1 : (const char *)oc_el_change->values[i].data);
935 1 : return LDB_ERR_NO_SUCH_ATTRIBUTE;
936 : }
937 : }
938 :
939 64 : break;
940 : }
941 :
942 : /* Now do the sorting */
943 307 : ret = dsdb_sort_objectClass_attr(ldb, ac->schema, oc_el_entry,
944 : msg, oc_el_entry);
945 307 : if (ret != LDB_SUCCESS) {
946 1 : return ret;
947 : }
948 :
949 : /*
950 : * Get the new top-most structural object class and check for
951 : * unrelated structural classes
952 : */
953 306 : objectclass = dsdb_get_last_structural_class(ac->schema,
954 : oc_el_entry);
955 306 : if (objectclass == NULL) {
956 2 : ldb_set_errstring(ldb,
957 : "objectclass: cannot delete all structural objectclasses!");
958 2 : return LDB_ERR_OBJECT_CLASS_VIOLATION;
959 : }
960 :
961 : /*
962 : * Has (so far, we re-check for each and every
963 : * "objectclass" in the message) the structural
964 : * objectclass changed?
965 : */
966 :
967 304 : if (objectclass != current_structural_objectclass) {
968 0 : const char *dn
969 189 : = ldb_dn_get_linearized(ac->search_res->message->dn);
970 189 : ldb_asprintf_errstring(ldb,
971 : "objectclass: not permitted "
972 : "to change the structural "
973 : "objectClass on %s [%s] => [%s]!",
974 : dn,
975 189 : current_structural_objectclass->lDAPDisplayName,
976 189 : objectclass->lDAPDisplayName);
977 189 : return LDB_ERR_OBJECT_CLASS_VIOLATION;
978 : }
979 :
980 : /* Check for unrelated objectclasses */
981 115 : ret = check_unrelated_objectclasses(ac->module, ac->schema,
982 : objectclass,
983 : oc_el_entry);
984 115 : if (ret != LDB_SUCCESS) {
985 3 : return ret;
986 : }
987 : }
988 :
989 : /* Now add the new object class attribute to the change message */
990 111 : ret = ldb_msg_add(msg, oc_el_entry, LDB_FLAG_MOD_REPLACE);
991 111 : if (ret != LDB_SUCCESS) {
992 0 : ldb_module_oom(ac->module);
993 0 : return ret;
994 : }
995 :
996 : /* Now we have the real and definitive change left to do */
997 :
998 111 : ret = ldb_build_mod_req(&mod_req, ldb, ac,
999 : msg,
1000 105 : ac->req->controls,
1001 105 : ac->req, dsdb_next_callback,
1002 : ac->req);
1003 111 : LDB_REQ_SET_LOCATION(mod_req);
1004 111 : if (ret != LDB_SUCCESS) {
1005 0 : return ret;
1006 : }
1007 :
1008 111 : return ldb_next_request(ac->module, mod_req);
1009 : }
1010 :
1011 : static int objectclass_do_rename(struct oc_context *ac);
1012 :
1013 1280 : static int objectclass_rename(struct ldb_module *module, struct ldb_request *req)
1014 : {
1015 5 : static const char * const attrs[] = { "objectClass", NULL };
1016 5 : struct ldb_context *ldb;
1017 5 : struct ldb_request *search_req;
1018 5 : struct oc_context *ac;
1019 5 : struct ldb_dn *parent_dn;
1020 5 : int ret;
1021 :
1022 1280 : ldb = ldb_module_get_ctx(module);
1023 :
1024 1280 : ldb_debug(ldb, LDB_DEBUG_TRACE, "objectclass_rename\n");
1025 :
1026 : /* do not manipulate our control entries */
1027 1280 : if (ldb_dn_is_special(req->op.rename.olddn)) {
1028 0 : return ldb_next_request(module, req);
1029 : }
1030 :
1031 : /*
1032 : * Bypass the constraint checks when we do have the "DBCHECK" control
1033 : * set, so we can force objects under the deleted objects container.
1034 : */
1035 1280 : if (ldb_request_get_control(req, DSDB_CONTROL_DBCHECK) != NULL) {
1036 1 : return ldb_next_request(module, req);
1037 : }
1038 :
1039 1279 : ac = oc_init_context(module, req);
1040 1279 : if (ac == NULL) {
1041 0 : return ldb_operr(ldb);
1042 : }
1043 :
1044 1279 : parent_dn = ldb_dn_get_parent(ac, req->op.rename.newdn);
1045 1279 : if (parent_dn == NULL) {
1046 0 : ldb_asprintf_errstring(ldb, "objectclass: Cannot rename %s, the parent DN does not exist!",
1047 : ldb_dn_get_linearized(req->op.rename.olddn));
1048 0 : return LDB_ERR_NO_SUCH_OBJECT;
1049 : }
1050 :
1051 : /* this looks up the parent object for fetching some important
1052 : * information (objectclasses, DN normalisation...) */
1053 1279 : ret = ldb_build_search_req(&search_req, ldb,
1054 : ac, parent_dn, LDB_SCOPE_BASE,
1055 : "(objectClass=*)",
1056 : attrs, NULL,
1057 : ac, get_search_callback,
1058 : req);
1059 1279 : LDB_REQ_SET_LOCATION(search_req);
1060 1279 : if (ret != LDB_SUCCESS) {
1061 0 : return ret;
1062 : }
1063 :
1064 : /* we have to add the show recycled control, as otherwise DRS
1065 : deletes will be refused as we will think the target parent
1066 : does not exist */
1067 1279 : ret = dsdb_request_add_controls(search_req,
1068 : DSDB_FLAG_AS_SYSTEM |
1069 : DSDB_SEARCH_SHOW_RECYCLED);
1070 1279 : if (ret != LDB_SUCCESS) {
1071 0 : return ret;
1072 : }
1073 :
1074 1279 : ac->step_fn = objectclass_do_rename;
1075 :
1076 1279 : return ldb_next_request(ac->module, search_req);
1077 : }
1078 :
1079 : static int objectclass_do_rename2(struct oc_context *ac);
1080 :
1081 1279 : static int objectclass_do_rename(struct oc_context *ac)
1082 : {
1083 5 : static const char * const attrs[] = { "objectClass", NULL };
1084 5 : struct ldb_context *ldb;
1085 5 : struct ldb_request *search_req;
1086 5 : int ret;
1087 :
1088 1279 : ldb = ldb_module_get_ctx(ac->module);
1089 :
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 1279 : if (ac->search_res == NULL) {
1093 3 : ldb_asprintf_errstring(ldb, "objectclass: Cannot rename %s, parent does not exist!",
1094 3 : ldb_dn_get_linearized(ac->req->op.rename.olddn));
1095 3 : return LDB_ERR_OTHER;
1096 : }
1097 :
1098 : /* now assign "search_res2" to the parent entry to have "search_res"
1099 : * free for another lookup */
1100 1276 : ac->search_res2 = ac->search_res;
1101 1276 : ac->search_res = NULL;
1102 :
1103 : /* this looks up the real existing object for fetching some important
1104 : * information (objectclasses) */
1105 1276 : ret = ldb_build_search_req(&search_req, ldb,
1106 1271 : ac, ac->req->op.rename.olddn,
1107 : LDB_SCOPE_BASE,
1108 : "(objectClass=*)",
1109 : attrs, NULL,
1110 : ac, get_search_callback,
1111 : ac->req);
1112 1276 : LDB_REQ_SET_LOCATION(search_req);
1113 1276 : if (ret != LDB_SUCCESS) {
1114 0 : return ret;
1115 : }
1116 :
1117 1276 : ret = dsdb_request_add_controls(search_req,
1118 : DSDB_FLAG_AS_SYSTEM |
1119 : DSDB_SEARCH_SHOW_RECYCLED);
1120 1276 : if (ret != LDB_SUCCESS) {
1121 0 : return ret;
1122 : }
1123 :
1124 1276 : ac->step_fn = objectclass_do_rename2;
1125 :
1126 1276 : return ldb_next_request(ac->module, search_req);
1127 : }
1128 :
1129 1275 : static int objectclass_do_rename2(struct oc_context *ac)
1130 : {
1131 5 : struct ldb_context *ldb;
1132 5 : struct ldb_request *rename_req;
1133 5 : struct ldb_dn *fixed_dn;
1134 5 : int ret;
1135 :
1136 1275 : ldb = ldb_module_get_ctx(ac->module);
1137 :
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 1275 : if (ac->search_res == NULL) {
1141 2 : ldb_asprintf_errstring(ldb, "objectclass: Cannot rename %s, entry does not exist!",
1142 2 : ldb_dn_get_linearized(ac->req->op.rename.olddn));
1143 2 : return LDB_ERR_NO_SUCH_OBJECT;
1144 : }
1145 :
1146 1273 : if (ac->schema != NULL) {
1147 5 : struct ldb_message_element *oc_el_entry, *oc_el_parent;
1148 5 : const struct dsdb_class *objectclass;
1149 5 : const char *rdn_name;
1150 1273 : bool allowed_class = false;
1151 5 : unsigned int i, j;
1152 5 : bool found;
1153 :
1154 1273 : oc_el_entry = ldb_msg_find_element(ac->search_res->message,
1155 : "objectClass");
1156 1273 : if (oc_el_entry == NULL) {
1157 : /* existing entry without a valid object class? */
1158 0 : return ldb_operr(ldb);
1159 : }
1160 1273 : objectclass = dsdb_get_last_structural_class(ac->schema,
1161 : oc_el_entry);
1162 1273 : if (objectclass == NULL) {
1163 : /* existing entry without a valid object class? */
1164 0 : return ldb_operr(ldb);
1165 : }
1166 :
1167 1273 : rdn_name = ldb_dn_get_rdn_name(ac->req->op.rename.newdn);
1168 1273 : if (rdn_name == NULL) {
1169 0 : return ldb_operr(ldb);
1170 : }
1171 1268 : found = false;
1172 2656 : for (i = 0; (!found) && (i < oc_el_entry->num_values); i++) {
1173 5 : const struct dsdb_class *tmp_class =
1174 1388 : dsdb_class_by_lDAPDisplayName_ldb_val(ac->schema,
1175 1383 : &oc_el_entry->values[i]);
1176 :
1177 1383 : if (tmp_class == NULL) continue;
1178 :
1179 1383 : if (ldb_attr_cmp(rdn_name, tmp_class->rDNAttID) == 0)
1180 1271 : found = true;
1181 : }
1182 1273 : if (!found) {
1183 2 : ldb_asprintf_errstring(ldb,
1184 : "objectclass: Invalid RDN '%s' for objectclass '%s'!",
1185 2 : rdn_name, objectclass->lDAPDisplayName);
1186 2 : return LDB_ERR_UNWILLING_TO_PERFORM;
1187 : }
1188 :
1189 1271 : oc_el_parent = ldb_msg_find_element(ac->search_res2->message,
1190 : "objectClass");
1191 1271 : if (oc_el_parent == NULL) {
1192 : /* existing entry without a valid object class? */
1193 0 : return ldb_operr(ldb);
1194 : }
1195 :
1196 3858 : for (i=0; allowed_class == false && i < oc_el_parent->num_values; i++) {
1197 10 : const struct dsdb_class *sclass;
1198 :
1199 2597 : sclass = dsdb_class_by_lDAPDisplayName_ldb_val(ac->schema,
1200 2587 : &oc_el_parent->values[i]);
1201 2587 : if (!sclass) {
1202 : /* We don't know this class? what is going on? */
1203 0 : continue;
1204 : }
1205 130416 : for (j=0; sclass->systemPossibleInferiors && sclass->systemPossibleInferiors[j]; j++) {
1206 129098 : if (ldb_attr_cmp(objectclass->lDAPDisplayName, sclass->systemPossibleInferiors[j]) == 0) {
1207 1264 : allowed_class = true;
1208 1264 : break;
1209 : }
1210 : }
1211 : }
1212 :
1213 1271 : if (!allowed_class) {
1214 4 : ldb_asprintf_errstring(ldb,
1215 : "objectclass: structural objectClass %s is not a valid child class for %s",
1216 2 : objectclass->lDAPDisplayName, ldb_dn_get_linearized(ac->search_res2->message->dn));
1217 2 : return LDB_ERR_NAMING_VIOLATION;
1218 : }
1219 : }
1220 :
1221 : /* Ensure we are not trying to rename it to be a child of itself */
1222 1269 : if ((ldb_dn_compare_base(ac->req->op.rename.olddn,
1223 1277 : ac->req->op.rename.newdn) == 0) &&
1224 8 : (ldb_dn_compare(ac->req->op.rename.olddn,
1225 8 : ac->req->op.rename.newdn) != 0)) {
1226 2 : ldb_asprintf_errstring(ldb, "objectclass: Cannot rename %s to be a child of itself",
1227 2 : ldb_dn_get_linearized(ac->req->op.rename.olddn));
1228 2 : return LDB_ERR_UNWILLING_TO_PERFORM;
1229 : }
1230 :
1231 : /* Fix up the DN to be in the standard form, taking
1232 : * particular care to match the parent DN */
1233 1272 : ret = fix_dn(ldb, ac,
1234 1267 : ac->req->op.rename.newdn,
1235 1267 : ac->search_res2->message->dn,
1236 : &fixed_dn);
1237 1267 : if (ret != LDB_SUCCESS) {
1238 0 : ldb_asprintf_errstring(ldb, "objectclass: Could not munge DN %s into normal form",
1239 0 : ldb_dn_get_linearized(ac->req->op.rename.newdn));
1240 0 : return ret;
1241 :
1242 : }
1243 :
1244 1267 : ret = ldb_build_rename_req(&rename_req, ldb, ac,
1245 1262 : ac->req->op.rename.olddn, fixed_dn,
1246 1262 : ac->req->controls,
1247 1262 : ac->req, dsdb_next_callback,
1248 : ac->req);
1249 1267 : LDB_REQ_SET_LOCATION(rename_req);
1250 1267 : if (ret != LDB_SUCCESS) {
1251 0 : return ret;
1252 : }
1253 :
1254 : /* perform the rename */
1255 1267 : return ldb_next_request(ac->module, rename_req);
1256 : }
1257 :
1258 : static int objectclass_do_delete(struct oc_context *ac);
1259 :
1260 110598 : static int objectclass_delete(struct ldb_module *module, struct ldb_request *req)
1261 : {
1262 160 : static const char * const attrs[] = { "nCName", "objectClass",
1263 : "systemFlags",
1264 : "isDeleted",
1265 : "isCriticalSystemObject", NULL };
1266 160 : struct ldb_context *ldb;
1267 160 : struct ldb_request *search_req;
1268 160 : struct oc_context *ac;
1269 160 : int ret;
1270 :
1271 110598 : ldb = ldb_module_get_ctx(module);
1272 :
1273 110598 : ldb_debug(ldb, LDB_DEBUG_TRACE, "objectclass_delete\n");
1274 :
1275 : /* do not manipulate our control entries */
1276 110598 : if (ldb_dn_is_special(req->op.del.dn)) {
1277 1 : return ldb_next_request(module, req);
1278 : }
1279 :
1280 : /* Bypass the constraint checks when we do have the "RELAX" control
1281 : * set. */
1282 110597 : if (ldb_request_get_control(req, LDB_CONTROL_RELAX_OID) != NULL) {
1283 25 : return ldb_next_request(module, req);
1284 : }
1285 :
1286 110572 : ac = oc_init_context(module, req);
1287 110572 : if (ac == NULL) {
1288 0 : return ldb_operr(ldb);
1289 : }
1290 :
1291 : /* this looks up the entry object for fetching some important
1292 : * information (object classes, system flags...) */
1293 110572 : ret = ldb_build_search_req(&search_req, ldb,
1294 : ac, req->op.del.dn, LDB_SCOPE_BASE,
1295 : "(objectClass=*)",
1296 : attrs, NULL,
1297 : ac, get_search_callback,
1298 : req);
1299 110572 : LDB_REQ_SET_LOCATION(search_req);
1300 110572 : if (ret != LDB_SUCCESS) {
1301 0 : return ret;
1302 : }
1303 :
1304 110572 : ret = dsdb_request_add_controls(search_req,
1305 : DSDB_FLAG_AS_SYSTEM |
1306 : DSDB_SEARCH_SHOW_RECYCLED);
1307 110572 : if (ret != LDB_SUCCESS) {
1308 0 : return ret;
1309 : }
1310 :
1311 110572 : ac->step_fn = objectclass_do_delete;
1312 :
1313 110572 : return ldb_next_request(ac->module, search_req);
1314 : }
1315 :
1316 110571 : static int objectclass_do_delete(struct oc_context *ac)
1317 : {
1318 151 : struct ldb_context *ldb;
1319 151 : struct ldb_dn *dn;
1320 151 : int32_t systemFlags;
1321 151 : bool isCriticalSystemObject;
1322 151 : int ret;
1323 :
1324 110571 : ldb = ldb_module_get_ctx(ac->module);
1325 :
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 110571 : if (ac->search_res == NULL) {
1329 37771 : ldb_asprintf_errstring(ldb, "objectclass: Cannot delete %s, entry does not exist!",
1330 37759 : ldb_dn_get_linearized(ac->req->op.del.dn));
1331 37759 : return LDB_ERR_NO_SUCH_OBJECT;
1332 : }
1333 :
1334 : /* DC's ntDSDSA object */
1335 72812 : if (ldb_dn_compare(ac->req->op.del.dn, samdb_ntds_settings_dn(ldb, ac)) == 0) {
1336 2 : ldb_asprintf_errstring(ldb, "objectclass: Cannot delete %s, it's the DC's ntDSDSA object!",
1337 2 : ldb_dn_get_linearized(ac->req->op.del.dn));
1338 2 : return LDB_ERR_UNWILLING_TO_PERFORM;
1339 : }
1340 :
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 72810 : ret = samdb_rid_set_dn(ldb, ac, &dn);
1345 72810 : if ((ret != LDB_SUCCESS) && (ret != LDB_ERR_NO_SUCH_ATTRIBUTE)
1346 0 : && (ret != LDB_ERR_NO_SUCH_OBJECT)) {
1347 0 : ldb_asprintf_errstring(ldb, "objectclass: Unable to determine if %s, is this DC's rIDSet object: %s ",
1348 0 : ldb_dn_get_linearized(ac->req->op.del.dn),
1349 : ldb_errstring(ldb));
1350 0 : return ret;
1351 : }
1352 72810 : if (ret == LDB_SUCCESS) {
1353 72809 : if (ldb_dn_compare(ac->req->op.del.dn, dn) == 0) {
1354 4 : talloc_free(dn);
1355 4 : ldb_asprintf_errstring(ldb, "objectclass: Cannot delete %s, it's the DC's rIDSet object!",
1356 4 : ldb_dn_get_linearized(ac->req->op.del.dn));
1357 4 : return LDB_ERR_UNWILLING_TO_PERFORM;
1358 : }
1359 72805 : talloc_free(dn);
1360 : }
1361 :
1362 : /* Only trusted request from system account are allowed to delete
1363 : * deleted objects.
1364 : */
1365 72806 : if (ldb_msg_check_string_attribute(ac->search_res->message, "isDeleted", "TRUE") &&
1366 0 : (ldb_req_is_untrusted(ac->req) ||
1367 0 : !dsdb_module_am_system(ac->module))) {
1368 0 : ldb_asprintf_errstring(ldb, "Delete of '%s' failed",
1369 0 : ldb_dn_get_linearized(ac->req->op.del.dn));
1370 0 : return LDB_ERR_UNWILLING_TO_PERFORM;
1371 : }
1372 :
1373 : /* crossRef objects regarding config, schema and default domain NCs */
1374 72806 : if (samdb_find_attribute(ldb, ac->search_res->message, "objectClass",
1375 : "crossRef") != NULL) {
1376 12 : dn = ldb_msg_find_attr_as_dn(ldb, ac, ac->search_res->message,
1377 : "nCName");
1378 20 : if ((ldb_dn_compare(dn, ldb_get_default_basedn(ldb)) == 0) ||
1379 8 : (ldb_dn_compare(dn, ldb_get_config_basedn(ldb)) == 0)) {
1380 8 : talloc_free(dn);
1381 :
1382 8 : ldb_asprintf_errstring(ldb, "objectclass: Cannot delete %s, it's a crossRef object to the main or configuration partition!",
1383 8 : ldb_dn_get_linearized(ac->req->op.del.dn));
1384 8 : return LDB_ERR_NOT_ALLOWED_ON_NON_LEAF;
1385 : }
1386 4 : if (ldb_dn_compare(dn, ldb_get_schema_basedn(ldb)) == 0) {
1387 4 : talloc_free(dn);
1388 :
1389 4 : ldb_asprintf_errstring(ldb, "objectclass: Cannot delete %s, it's a crossRef object to the schema partition!",
1390 4 : ldb_dn_get_linearized(ac->req->op.del.dn));
1391 4 : return LDB_ERR_UNWILLING_TO_PERFORM;
1392 : }
1393 0 : talloc_free(dn);
1394 : }
1395 :
1396 : /* systemFlags */
1397 :
1398 72794 : systemFlags = ldb_msg_find_attr_as_int(ac->search_res->message,
1399 : "systemFlags", 0);
1400 72794 : if ((systemFlags & SYSTEM_FLAG_DISALLOW_DELETE) != 0) {
1401 5 : ldb_asprintf_errstring(ldb, "objectclass: Cannot delete %s, it isn't permitted!",
1402 5 : ldb_dn_get_linearized(ac->req->op.del.dn));
1403 5 : return LDB_ERR_UNWILLING_TO_PERFORM;
1404 : }
1405 :
1406 : /* isCriticalSystemObject - but this only applies on tree delete
1407 : * operations - MS-ADTS 3.1.1.5.5.7.2 */
1408 72789 : if (ldb_request_get_control(ac->req, LDB_CONTROL_TREE_DELETE_OID) != NULL) {
1409 32320 : isCriticalSystemObject = ldb_msg_find_attr_as_bool(ac->search_res->message,
1410 : "isCriticalSystemObject", false);
1411 32320 : if (isCriticalSystemObject) {
1412 : /*
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
1423 : */
1424 :
1425 638 : if (samdb_find_attribute(ldb, ac->search_res->message, "objectClass", "group") == NULL &&
1426 638 : samdb_find_attribute(ldb, ac->search_res->message, "objectClass", "samDomain") == NULL &&
1427 638 : samdb_find_attribute(ldb, ac->search_res->message, "objectClass", "samServer") == NULL &&
1428 319 : samdb_find_attribute(ldb, ac->search_res->message, "objectClass", "user") == NULL) {
1429 0 : ldb_asprintf_errstring(ldb,
1430 : "objectclass: Cannot tree-delete %s, it's a critical system object!",
1431 0 : ldb_dn_get_linearized(ac->req->op.del.dn));
1432 0 : return LDB_ERR_UNWILLING_TO_PERFORM;
1433 : }
1434 : }
1435 : }
1436 :
1437 72789 : return ldb_next_request(ac->module, ac->req);
1438 : }
1439 :
1440 182004 : static int objectclass_init(struct ldb_module *module)
1441 : {
1442 182004 : struct ldb_context *ldb = ldb_module_get_ctx(module);
1443 6016 : int ret;
1444 :
1445 : /* Init everything else */
1446 182004 : ret = ldb_next_init(module);
1447 182004 : if (ret != LDB_SUCCESS) {
1448 0 : return ret;
1449 : }
1450 :
1451 : /* Look for the opaque to indicate we might have to cut down the DN of defaultObjectCategory */
1452 182004 : ldb_module_set_private(module, ldb_get_opaque(ldb, DSDB_EXTENDED_DN_STORE_FORMAT_OPAQUE_NAME));
1453 :
1454 182004 : ret = ldb_mod_register_control(module, LDB_CONTROL_RODC_DCPROMO_OID);
1455 182004 : if (ret != LDB_SUCCESS) {
1456 0 : ldb_debug(ldb, LDB_DEBUG_ERROR,
1457 : "objectclass_init: Unable to register control DCPROMO with rootdse\n");
1458 0 : return ldb_operr(ldb);
1459 : }
1460 :
1461 175988 : return ret;
1462 : }
1463 :
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
1471 : };
1472 :
1473 6040 : int ldb_objectclass_module_init(const char *version)
1474 : {
1475 6040 : LDB_MODULE_CHECK_VERSION(version);
1476 6040 : return ldb_register_module(&ldb_objectclass_module_ops);
1477 : }
|