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 624277 : static struct oc_context *oc_init_context(struct ldb_module *module,
56 : struct ldb_request *req)
57 : {
58 : struct ldb_context *ldb;
59 : struct oc_context *ac;
60 :
61 624277 : ldb = ldb_module_get_ctx(module);
62 :
63 624277 : ac = talloc_zero(req, struct oc_context);
64 624277 : if (ac == NULL) {
65 0 : ldb_oom(ldb);
66 0 : return NULL;
67 : }
68 :
69 624277 : ac->module = module;
70 624277 : ac->req = req;
71 624277 : ac->schema = dsdb_get_schema(ldb, ac);
72 :
73 624277 : 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 13166 : static int oc_validate_dsheuristics(struct ldb_message_element *el)
83 : {
84 13166 : if (el->num_values > 0) {
85 9345 : 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 9344 : 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 9343 : 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 9342 : 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 9341 : 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 9340 : 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 9339 : if ((el->values[0].length >= DS_HR_THIRTIETH_CHAR) &&
110 6442 : (el->values[0].data[DS_HR_THIRTIETH_CHAR-1] != '3')) {
111 1 : return LDB_ERR_CONSTRAINT_VIOLATION;
112 : }
113 9338 : if ((el->values[0].length >= DS_HR_TWENTIETH_CHAR) &&
114 6444 : (el->values[0].data[DS_HR_TWENTIETH_CHAR-1] != '2')) {
115 1 : return LDB_ERR_CONSTRAINT_VIOLATION;
116 : }
117 9337 : if ((el->values[0].length >= DS_HR_TENTH_CHAR) &&
118 6447 : (el->values[0].data[DS_HR_TENTH_CHAR-1] != '1')) {
119 1 : return LDB_ERR_CONSTRAINT_VIOLATION;
120 : }
121 : }
122 :
123 13157 : return LDB_SUCCESS;
124 : }
125 :
126 : /*
127 : auto normalise values on input
128 : */
129 1804901 : 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 : int i;
133 1804901 : bool values_copied = false;
134 :
135 3609215 : for (i=0; i<el->num_values; i++) {
136 : struct ldb_val v;
137 : 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 1804317 : ret = attr->ldb_schema_attribute->syntax->canonicalise_fn(ldb,
145 1804317 : msg->elements,
146 1804317 : &el->values[i],
147 : &v);
148 1804317 : if (ret != LDB_SUCCESS) {
149 3 : return ret;
150 : }
151 1804314 : if (data_blob_cmp(&v, &el->values[i]) == 0) {
152 : /* no need to replace it */
153 1804211 : talloc_free(v.data);
154 1804211 : continue;
155 : }
156 :
157 : /* we need to copy the values array on the first change */
158 103 : if (!values_copied) {
159 : struct ldb_val *v2;
160 103 : v2 = talloc_array(msg->elements, struct ldb_val, el->num_values);
161 103 : if (v2 == NULL) {
162 0 : return ldb_oom(ldb);
163 : }
164 103 : memcpy(v2, el->values, sizeof(struct ldb_val) * el->num_values);
165 103 : el->values = v2;
166 103 : values_copied = true;
167 : }
168 :
169 103 : 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 103 : talloc_steal(el->values, v.data);
176 : }
177 1804898 : return LDB_SUCCESS;
178 : }
179 :
180 624277 : static int attr_handler(struct oc_context *ac)
181 : {
182 : struct ldb_context *ldb;
183 : struct ldb_message *msg;
184 : struct ldb_request *child_req;
185 : const struct dsdb_attribute *attr;
186 : unsigned int i;
187 : int ret;
188 : WERROR werr;
189 : struct dsdb_syntax_ctx syntax_ctx;
190 :
191 624277 : ldb = ldb_module_get_ctx(ac->module);
192 :
193 624277 : if (ac->req->operation == LDB_ADD) {
194 322645 : msg = ldb_msg_copy_shallow(ac, ac->req->op.add.message);
195 : } else {
196 301632 : msg = ldb_msg_copy_shallow(ac, ac->req->op.mod.message);
197 : }
198 624277 : if (msg == NULL) {
199 0 : return ldb_oom(ldb);
200 : }
201 624277 : ac->msg = msg;
202 :
203 : /* initialize syntax checking context */
204 624277 : 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 5606040 : for (i = 0; i < msg->num_elements; i++) {
210 4981856 : attr = dsdb_attribute_by_lDAPDisplayName(ac->schema,
211 4981856 : msg->elements[i].name);
212 4981856 : 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 4981873 : 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 4981850 : 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 188419 : if (!ldb_request_get_control(ac->req, LDB_CONTROL_RELAX_OID) &&
250 162489 : !ldb_request_get_control(ac->req, DSDB_CONTROL_DBCHECK) &&
251 68999 : !ldb_request_get_control(ac->req, DSDB_CONTROL_RESTORE_TOMBSTONE_OID) &&
252 68469 : ldb_attr_cmp(attr->lDAPDisplayName, "objectClass") != 0 &&
253 68398 : ldb_attr_cmp(attr->lDAPDisplayName, "name") != 0 &&
254 68397 : ldb_attr_cmp(attr->lDAPDisplayName, "distinguishedName") != 0 &&
255 68394 : ldb_attr_cmp(attr->lDAPDisplayName, "msDS-AdditionalDnsHostName") != 0 &&
256 68394 : 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 68394 : if (ldb_dn_compare_base(ldb_get_schema_basedn(ldb), msg->dn) != 0) {
262 64695 : struct ldb_control *as_system = ldb_request_get_control(ac->req,
263 : LDB_CONTROL_AS_SYSTEM_OID);
264 64695 : 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 4981845 : if (!(msg->elements[i].flags & LDB_FLAG_INTERNAL_DISABLE_VALIDATION)) {
276 4981776 : werr = attr->syntax->validate_ldb(&syntax_ctx, attr,
277 4981776 : &msg->elements[i]);
278 4981845 : if (!W_ERROR_IS_OK(werr) &&
279 69 : !ldb_request_get_control(ac->req, DSDB_CONTROL_DBCHECK)) {
280 67 : ldb_asprintf_errstring(ldb, "objectclass_attrs: attribute '%s' on entry '%s' contains at least one invalid value!",
281 67 : msg->elements[i].name,
282 : ldb_dn_get_linearized(msg->dn));
283 67 : return LDB_ERR_INVALID_ATTRIBUTE_SYNTAX;
284 : }
285 : }
286 :
287 4981778 : 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 4981775 : if (ldb_attr_cmp(attr->lDAPDisplayName, "dSHeuristics") == 0) {
300 13166 : ret = oc_validate_dsheuristics(&(msg->elements[i]));
301 13166 : if (ret != LDB_SUCCESS) {
302 9 : return ret;
303 : }
304 : }
305 :
306 : /* auto normalise some attribute values */
307 4981766 : if (attr->syntax->auto_normalise) {
308 1804901 : ret = oc_auto_normalise(ldb, attr, msg, &msg->elements[i]);
309 1804901 : if (ret != LDB_SUCCESS) {
310 3 : return ret;
311 : }
312 : }
313 :
314 : /* Substitute the attribute name to match in case */
315 4981763 : msg->elements[i].name = attr->lDAPDisplayName;
316 : }
317 :
318 624184 : no_attribute:
319 624184 : if (ac->req->operation == LDB_ADD) {
320 322603 : ret = ldb_build_add_req(&child_req, ldb, ac,
321 322603 : msg, ac->req->controls,
322 : ac, oc_op_callback, ac->req);
323 322603 : LDB_REQ_SET_LOCATION(child_req);
324 : } else {
325 301581 : ret = ldb_build_mod_req(&child_req, ldb, ac,
326 301581 : msg, ac->req->controls,
327 : ac, oc_op_callback, ac->req);
328 301581 : LDB_REQ_SET_LOCATION(child_req);
329 : }
330 624184 : if (ret != LDB_SUCCESS) {
331 0 : return ret;
332 : }
333 :
334 624184 : 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 623739 : static int attr_handler2(struct oc_context *ac)
344 : {
345 : struct ldb_context *ldb;
346 : struct ldb_message_element *oc_element;
347 : struct ldb_message *msg;
348 : const char **must_contain, **may_contain, **found_must_contain;
349 : /* There exists a hardcoded delete-protected attributes list in AD */
350 623739 : 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 : const struct dsdb_attribute *attr;
357 : unsigned int i;
358 : bool found;
359 623739 : bool isSchemaAttr = false;
360 :
361 623739 : ldb = ldb_module_get_ctx(ac->module);
362 :
363 623739 : 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 623739 : oc_element = ldb_msg_find_element(ac->search_res->message,
370 : "objectClass");
371 623739 : 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 2083730 : for (i = 0; i < oc_element->num_values; i++) {
391 1459993 : char * attname = (char *)oc_element->values[i].data;
392 1459993 : if (ldb_req_is_untrusted(ac->req)) {
393 395302 : if (strcmp(attname, "secret") == 0 ||
394 395300 : 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 1459991 : if (strcmp(attname, "attributeSchema") == 0) {
401 140963 : isSchemaAttr = true;
402 : }
403 : }
404 :
405 623737 : must_contain = dsdb_full_attribute_list(ac, ac->schema, oc_element,
406 : DSDB_SCHEMA_ALL_MUST);
407 623737 : may_contain = dsdb_full_attribute_list(ac, ac->schema, oc_element,
408 : DSDB_SCHEMA_ALL_MAY);
409 623737 : found_must_contain = const_str_list(str_list_copy(ac, must_contain));
410 623737 : if ((must_contain == NULL) || (may_contain == NULL)
411 623737 : || (found_must_contain == NULL)) {
412 0 : return ldb_operr(ldb);
413 : }
414 :
415 : /* Check the delete-protected attributes list */
416 623737 : msg = ac->search_res->message;
417 10603051 : for (l = del_prot_attributes; *l != NULL; l++) {
418 : struct ldb_message_element *el;
419 :
420 9979378 : el = ldb_msg_find_element(ac->msg, *l);
421 9979378 : 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 9227034 : continue;
427 : }
428 :
429 752344 : found = str_list_check_ci(must_contain, *l);
430 752344 : if (!found) {
431 337524 : found = str_list_check_ci(may_contain, *l);
432 : }
433 752344 : 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 13845113 : for (i = 0; i < msg->num_elements; i++) {
444 13221471 : attr = dsdb_attribute_by_lDAPDisplayName(ac->schema,
445 13221471 : msg->elements[i].name);
446 13221471 : 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 : /* We can use "str_list_check" with "strcmp" here since the
456 : * attribute information from the schema are always equal
457 : * up-down-cased. */
458 13221471 : found = str_list_check(must_contain, attr->lDAPDisplayName);
459 13221471 : if (found) {
460 4314763 : str_list_remove(found_must_contain, attr->lDAPDisplayName);
461 : } else {
462 8906708 : found = str_list_check(may_contain, attr->lDAPDisplayName);
463 : }
464 13221471 : if (!found) {
465 31 : found = str_list_check(harmless_attrs, attr->lDAPDisplayName);
466 : }
467 13221471 : if (!found) {
468 : /* we allow this for dbcheck to fix the rest of this broken entry */
469 31 : if (!ldb_request_get_control(ac->req, DSDB_CONTROL_DBCHECK) ||
470 0 : ac->req->operation == LDB_ADD) {
471 31 : ldb_asprintf_errstring(ldb, "objectclass_attrs: attribute '%s' on entry '%s' does not exist in the specified objectclasses!",
472 31 : msg->elements[i].name,
473 : ldb_dn_get_linearized(msg->dn));
474 31 : return LDB_ERR_OBJECT_CLASS_VIOLATION;
475 : }
476 : }
477 : }
478 :
479 : /*
480 : * We skip this check under dbcheck to allow fixing of other
481 : * attributes even if an attribute is missing. This matters
482 : * for CN=RID Set as the required attribute rIDNextRid is not
483 : * replicated.
484 : */
485 648323 : if (found_must_contain[0] != NULL &&
486 24681 : ldb_msg_check_string_attribute(msg, "isDeleted", "TRUE") == 0) {
487 :
488 62 : for (i = 0; found_must_contain[i] != NULL; i++) {
489 42 : const struct dsdb_attribute *broken_attr = dsdb_attribute_by_lDAPDisplayName(ac->schema,
490 42 : found_must_contain[i]);
491 :
492 42 : bool replicated = (broken_attr->systemFlags &
493 : (DS_FLAG_ATTR_NOT_REPLICATED | DS_FLAG_ATTR_IS_CONSTRUCTED)) == 0;
494 :
495 42 : if (replicated) {
496 3 : ldb_asprintf_errstring(ldb, "objectclass_attrs: at least one mandatory "
497 : "attribute ('%s') on entry '%s' wasn't specified!",
498 3 : found_must_contain[i],
499 : ldb_dn_get_linearized(msg->dn));
500 3 : return LDB_ERR_OBJECT_CLASS_VIOLATION;
501 : }
502 : }
503 : }
504 :
505 623639 : if (isSchemaAttr) {
506 : /*
507 : * Before really adding an attribute in the database,
508 : * let's check that we can translate it into a dsdb_attribute and
509 : * that we can find a valid syntax object.
510 : * If not it's better to reject this attribute than not be able
511 : * to start samba next time due to schema being unloadable.
512 : */
513 140963 : struct dsdb_attribute *att = talloc(ac, struct dsdb_attribute);
514 : const struct dsdb_syntax *attrSyntax;
515 : WERROR status;
516 :
517 140963 : status = dsdb_attribute_from_ldb(NULL, msg, att);
518 140963 : if (!W_ERROR_IS_OK(status)) {
519 0 : ldb_set_errstring(ldb,
520 : "objectclass: failed to translate the schemaAttribute to a dsdb_attribute");
521 0 : return LDB_ERR_UNWILLING_TO_PERFORM;
522 : }
523 :
524 140963 : attrSyntax = dsdb_syntax_for_attribute(att);
525 140963 : if (!attrSyntax) {
526 0 : ldb_set_errstring(ldb,
527 : "objectclass: unknown attribute syntax");
528 0 : return LDB_ERR_UNWILLING_TO_PERFORM;
529 : }
530 : }
531 623639 : return ldb_module_done(ac->req, ac->mod_ares->controls,
532 623639 : ac->mod_ares->response, LDB_SUCCESS);
533 : }
534 :
535 1247478 : static int get_search_callback(struct ldb_request *req, struct ldb_reply *ares)
536 : {
537 : struct ldb_context *ldb;
538 : struct oc_context *ac;
539 : int ret;
540 :
541 1247478 : ac = talloc_get_type(req->context, struct oc_context);
542 1247478 : ldb = ldb_module_get_ctx(ac->module);
543 :
544 1247478 : if (!ares) {
545 0 : return ldb_module_done(ac->req, NULL, NULL,
546 : LDB_ERR_OPERATIONS_ERROR);
547 : }
548 1247478 : if (ares->error != LDB_SUCCESS) {
549 0 : return ldb_module_done(ac->req, ares->controls,
550 : ares->response, ares->error);
551 : }
552 :
553 1247478 : ldb_reset_err_string(ldb);
554 :
555 1247478 : switch (ares->type) {
556 623739 : case LDB_REPLY_ENTRY:
557 623739 : if (ac->search_res != NULL) {
558 0 : ldb_set_errstring(ldb, "Too many results");
559 0 : talloc_free(ares);
560 0 : return ldb_module_done(ac->req, NULL, NULL,
561 : LDB_ERR_OPERATIONS_ERROR);
562 : }
563 :
564 623739 : ac->search_res = talloc_steal(ac, ares);
565 623739 : break;
566 :
567 0 : case LDB_REPLY_REFERRAL:
568 : /* ignore */
569 0 : talloc_free(ares);
570 0 : break;
571 :
572 623739 : case LDB_REPLY_DONE:
573 623739 : talloc_free(ares);
574 623739 : ret = attr_handler2(ac);
575 623739 : if (ret != LDB_SUCCESS) {
576 100 : return ldb_module_done(ac->req, NULL, NULL, ret);
577 : }
578 623639 : break;
579 : }
580 :
581 1247378 : return LDB_SUCCESS;
582 : }
583 :
584 624192 : static int oc_op_callback(struct ldb_request *req, struct ldb_reply *ares)
585 : {
586 : struct oc_context *ac;
587 : struct ldb_context *ldb;
588 : struct ldb_request *search_req;
589 : struct ldb_dn *base_dn;
590 : int ret;
591 : static const char *attrs[] = {"nTSecurityDescriptor", "*", NULL};
592 :
593 624192 : ac = talloc_get_type(req->context, struct oc_context);
594 624192 : ldb = ldb_module_get_ctx(ac->module);
595 :
596 624192 : if (!ares) {
597 0 : return ldb_module_done(ac->req, NULL, NULL,
598 : LDB_ERR_OPERATIONS_ERROR);
599 : }
600 :
601 624192 : if (ares->type == LDB_REPLY_REFERRAL) {
602 10 : return ldb_module_send_referral(ac->req, ares->referral);
603 : }
604 :
605 624182 : if (ares->error != LDB_SUCCESS) {
606 443 : return ldb_module_done(ac->req, ares->controls, ares->response,
607 : ares->error);
608 : }
609 :
610 623739 : if (ares->type != LDB_REPLY_DONE) {
611 0 : talloc_free(ares);
612 0 : return ldb_module_done(ac->req, NULL, NULL,
613 : LDB_ERR_OPERATIONS_ERROR);
614 : }
615 :
616 623739 : ac->search_res = NULL;
617 623739 : ac->mod_ares = talloc_steal(ac, ares);
618 :
619 : /* This looks up all attributes of our just added/modified entry */
620 1569922 : base_dn = ac->req->operation == LDB_ADD ? ac->req->op.add.message->dn
621 623739 : : ac->req->op.mod.message->dn;
622 623739 : ret = ldb_build_search_req(&search_req, ldb, ac, base_dn,
623 : LDB_SCOPE_BASE, "(objectClass=*)",
624 : attrs, NULL, ac,
625 : get_search_callback, ac->req);
626 623739 : LDB_REQ_SET_LOCATION(search_req);
627 623739 : if (ret != LDB_SUCCESS) {
628 0 : return ldb_module_done(ac->req, NULL, NULL, ret);
629 : }
630 :
631 623739 : ret = ldb_request_add_control(search_req, LDB_CONTROL_SHOW_RECYCLED_OID,
632 : true, NULL);
633 623739 : if (ret != LDB_SUCCESS) {
634 0 : return ldb_module_done(ac->req, NULL, NULL, ret);
635 : }
636 :
637 : /*
638 : * This ensures we see if there was a DN, that pointed at an
639 : * object that is now deleted, that we still consider the
640 : * schema check to have passed
641 : */
642 623739 : ret = ldb_request_add_control(search_req, LDB_CONTROL_REVEAL_INTERNALS,
643 : false, NULL);
644 623739 : if (ret != LDB_SUCCESS) {
645 0 : return ldb_module_done(ac->req, NULL, NULL, ret);
646 : }
647 :
648 623739 : ret = ldb_next_request(ac->module, search_req);
649 623739 : if (ret != LDB_SUCCESS) {
650 0 : return ldb_module_done(ac->req, NULL, NULL, ret);
651 : }
652 :
653 : /* "ldb_module_done" isn't called here since we need to do additional
654 : * checks. It is called at the end of "attr_handler2". */
655 623739 : return LDB_SUCCESS;
656 : }
657 :
658 323020 : static int objectclass_attrs_add(struct ldb_module *module,
659 : struct ldb_request *req)
660 : {
661 : struct ldb_context *ldb;
662 : struct oc_context *ac;
663 :
664 323020 : ldb = ldb_module_get_ctx(module);
665 :
666 323020 : ldb_debug(ldb, LDB_DEBUG_TRACE, "objectclass_attrs_add\n");
667 :
668 : /* do not manipulate our control entries */
669 323020 : if (ldb_dn_is_special(req->op.add.message->dn)) {
670 375 : return ldb_next_request(module, req);
671 : }
672 :
673 322645 : ac = oc_init_context(module, req);
674 322645 : if (ac == NULL) {
675 0 : return ldb_operr(ldb);
676 : }
677 :
678 : /* without schema, there isn't much to do here */
679 322645 : if (ac->schema == NULL) {
680 0 : talloc_free(ac);
681 0 : return ldb_next_request(module, req);
682 : }
683 :
684 322645 : return attr_handler(ac);
685 : }
686 :
687 329084 : static int objectclass_attrs_modify(struct ldb_module *module,
688 : struct ldb_request *req)
689 : {
690 : struct ldb_context *ldb;
691 : struct ldb_control *sd_propagation_control;
692 : int ret;
693 :
694 : struct oc_context *ac;
695 :
696 329084 : ldb = ldb_module_get_ctx(module);
697 :
698 329084 : ldb_debug(ldb, LDB_DEBUG_TRACE, "objectclass_attrs_modify\n");
699 :
700 : /* do not manipulate our control entries */
701 329084 : if (ldb_dn_is_special(req->op.mod.message->dn)) {
702 560 : return ldb_next_request(module, req);
703 : }
704 :
705 328524 : sd_propagation_control = ldb_request_get_control(req,
706 : DSDB_CONTROL_SEC_DESC_PROPAGATION_OID);
707 328524 : if (sd_propagation_control != NULL) {
708 26892 : if (req->op.mod.message->num_elements != 1) {
709 0 : return ldb_module_operr(module);
710 : }
711 26892 : ret = strcmp(req->op.mod.message->elements[0].name,
712 : "nTSecurityDescriptor");
713 26892 : if (ret != 0) {
714 0 : return ldb_module_operr(module);
715 : }
716 :
717 26892 : return ldb_next_request(module, req);
718 : }
719 :
720 301632 : ac = oc_init_context(module, req);
721 301632 : if (ac == NULL) {
722 0 : return ldb_operr(ldb);
723 : }
724 :
725 : /* without schema, there isn't much to do here */
726 301632 : if (ac->schema == NULL) {
727 0 : talloc_free(ac);
728 0 : return ldb_next_request(module, req);
729 : }
730 :
731 301632 : return attr_handler(ac);
732 : }
733 :
734 : static const struct ldb_module_ops ldb_objectclass_attrs_module_ops = {
735 : .name = "objectclass_attrs",
736 : .add = objectclass_attrs_add,
737 : .modify = objectclass_attrs_modify
738 : };
739 :
740 4223 : int ldb_objectclass_attrs_module_init(const char *version)
741 : {
742 4223 : LDB_MODULE_CHECK_VERSION(version);
743 4223 : return ldb_register_module(&ldb_objectclass_attrs_module_ops);
744 : }
|