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 930133 : static struct oc_context *oc_init_context(struct ldb_module *module,
56 : struct ldb_request *req)
57 : {
58 106809 : struct ldb_context *ldb;
59 106809 : struct oc_context *ac;
60 :
61 930133 : ldb = ldb_module_get_ctx(module);
62 :
63 930133 : ac = talloc_zero(req, struct oc_context);
64 930133 : if (ac == NULL) {
65 0 : ldb_oom(ldb);
66 0 : return NULL;
67 : }
68 :
69 930133 : ac->module = module;
70 930133 : ac->req = req;
71 930133 : ac->schema = dsdb_get_schema(ldb, ac);
72 :
73 930133 : 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 2929721 : 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 407697 : int i;
133 2929721 : bool values_copied = false;
134 :
135 5858861 : for (i=0; i<el->num_values; i++) {
136 407697 : struct ldb_val v;
137 407697 : 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 3336846 : ret = attr->ldb_schema_attribute->syntax->canonicalise_fn(ldb,
145 2929149 : msg->elements,
146 2929149 : &el->values[i],
147 : &v);
148 2929149 : if (ret != LDB_SUCCESS) {
149 9 : return ret;
150 : }
151 2929140 : if (data_blob_cmp(&v, &el->values[i]) == 0) {
152 : /* no need to replace it */
153 2928956 : talloc_free(v.data);
154 2928956 : 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 2522015 : return LDB_SUCCESS;
178 : }
179 :
180 930133 : static int attr_handler(struct oc_context *ac)
181 : {
182 106809 : struct ldb_context *ldb;
183 106809 : struct ldb_message *msg;
184 106809 : struct ldb_request *child_req;
185 106809 : const struct dsdb_attribute *attr;
186 106809 : unsigned int i;
187 106809 : int ret;
188 106809 : WERROR werr;
189 106809 : struct dsdb_syntax_ctx syntax_ctx;
190 :
191 930133 : ldb = ldb_module_get_ctx(ac->module);
192 :
193 930133 : if (ac->req->operation == LDB_ADD) {
194 542508 : msg = ldb_msg_copy_shallow(ac, ac->req->op.add.message);
195 : } else {
196 387625 : msg = ldb_msg_copy_shallow(ac, ac->req->op.mod.message);
197 : }
198 930133 : if (msg == NULL) {
199 0 : return ldb_oom(ldb);
200 : }
201 930133 : ac->msg = msg;
202 :
203 : /* initialize syntax checking context */
204 930133 : 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 9135186 : for (i = 0; i < msg->num_elements; i++) {
210 9274594 : attr = dsdb_attribute_by_lDAPDisplayName(ac->schema,
211 8098348 : msg->elements[i].name);
212 8098348 : 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 8098365 : 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 8098342 : 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 273691 : if (!ldb_request_get_control(ac->req, LDB_CONTROL_RELAX_OID) &&
250 236719 : !ldb_request_get_control(ac->req, DSDB_CONTROL_DBCHECK) &&
251 101049 : !ldb_request_get_control(ac->req, DSDB_CONTROL_RESTORE_TOMBSTONE_OID) &&
252 100519 : ldb_attr_cmp(attr->lDAPDisplayName, "objectClass") != 0 &&
253 100442 : ldb_attr_cmp(attr->lDAPDisplayName, "name") != 0 &&
254 100441 : ldb_attr_cmp(attr->lDAPDisplayName, "distinguishedName") != 0 &&
255 100438 : ldb_attr_cmp(attr->lDAPDisplayName, "msDS-AdditionalDnsHostName") != 0 &&
256 100434 : 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 100434 : if (ldb_dn_compare_base(ldb_get_schema_basedn(ldb), msg->dn) != 0) {
262 96047 : if (!dsdb_have_system_access(
263 : ac->module,
264 : ac->req,
265 : SYSTEM_CONTROL_KEEP_CRITICAL))
266 : {
267 5 : ldb_asprintf_errstring(ldb,
268 : "objectclass_attrs: attribute '%s' on entry '%s' can only be modified as system",
269 5 : msg->elements[i].name,
270 : ldb_dn_get_linearized(msg->dn));
271 5 : return LDB_ERR_CONSTRAINT_VIOLATION;
272 : }
273 : }
274 : }
275 : }
276 :
277 8098337 : if (!(msg->elements[i].flags & LDB_FLAG_INTERNAL_DISABLE_VALIDATION)) {
278 8098245 : werr = attr->syntax->validate_ldb(&syntax_ctx, attr,
279 6921999 : &msg->elements[i]);
280 8098319 : if (!W_ERROR_IS_OK(werr) &&
281 74 : !ldb_request_get_control(ac->req, DSDB_CONTROL_DBCHECK)) {
282 72 : ldb_asprintf_errstring(ldb, "objectclass_attrs: attribute '%s' on entry '%s' contains at least one invalid value!",
283 72 : msg->elements[i].name,
284 : ldb_dn_get_linearized(msg->dn));
285 72 : return LDB_ERR_INVALID_ATTRIBUTE_SYNTAX;
286 : }
287 : }
288 :
289 8098265 : if ((attr->systemFlags & DS_FLAG_ATTR_IS_CONSTRUCTED) != 0) {
290 3 : ldb_asprintf_errstring(ldb, "objectclass_attrs: attribute '%s' on entry '%s' is constructed!",
291 3 : msg->elements[i].name,
292 : ldb_dn_get_linearized(msg->dn));
293 3 : if (ac->req->operation == LDB_ADD) {
294 3 : return LDB_ERR_UNDEFINED_ATTRIBUTE_TYPE;
295 : } else {
296 0 : return LDB_ERR_CONSTRAINT_VIOLATION;
297 : }
298 : }
299 :
300 : /* "dSHeuristics" syntax check */
301 8098262 : if (ldb_attr_cmp(attr->lDAPDisplayName, "dSHeuristics") == 0) {
302 13422 : ret = oc_validate_dsheuristics(&(msg->elements[i]));
303 13422 : if (ret != LDB_SUCCESS) {
304 9 : return ret;
305 : }
306 : }
307 :
308 : /* auto normalise some attribute values */
309 8098253 : if (attr->syntax->auto_normalise) {
310 2929721 : ret = oc_auto_normalise(ldb, attr, msg, &msg->elements[i]);
311 2929721 : if (ret != LDB_SUCCESS) {
312 9 : return ret;
313 : }
314 : }
315 :
316 : /* Substitute the attribute name to match in case */
317 8098244 : msg->elements[i].name = attr->lDAPDisplayName;
318 : }
319 :
320 930029 : no_attribute:
321 930029 : if (ac->req->operation == LDB_ADD) {
322 542460 : ret = ldb_build_add_req(&child_req, ldb, ac,
323 458849 : msg, ac->req->controls,
324 : ac, oc_op_callback, ac->req);
325 542460 : LDB_REQ_SET_LOCATION(child_req);
326 : } else {
327 387569 : ret = ldb_build_mod_req(&child_req, ldb, ac,
328 364371 : msg, ac->req->controls,
329 : ac, oc_op_callback, ac->req);
330 387569 : LDB_REQ_SET_LOCATION(child_req);
331 : }
332 930029 : if (ret != LDB_SUCCESS) {
333 0 : return ret;
334 : }
335 :
336 930029 : return ldb_next_request(ac->module, child_req);
337 : }
338 :
339 : /*
340 : these are attributes which are left over from old ways of doing
341 : things in ldb, and are harmless
342 : */
343 : static const char *harmless_attrs[] = { "parentGUID", NULL };
344 :
345 929211 : static int attr_handler2(struct oc_context *ac)
346 : {
347 106808 : struct ldb_context *ldb;
348 106808 : struct ldb_message_element *oc_element;
349 106808 : struct ldb_message *msg;
350 106808 : const char **must_contain, **may_contain, **found_must_contain;
351 : /* There exists a hardcoded delete-protected attributes list in AD */
352 929211 : const char *del_prot_attributes[] = { "nTSecurityDescriptor",
353 : "objectSid", "sAMAccountType", "sAMAccountName", "groupType",
354 : "primaryGroupID", "userAccountControl", "accountExpires",
355 : "badPasswordTime", "badPwdCount", "codePage", "countryCode",
356 : "lastLogoff", "lastLogon", "logonCount", "pwdLastSet", NULL },
357 : **l;
358 106808 : const struct dsdb_attribute *attr;
359 106808 : unsigned int i;
360 106808 : bool found;
361 929211 : bool isSchemaAttr = false;
362 :
363 929211 : ldb = ldb_module_get_ctx(ac->module);
364 :
365 929211 : if (ac->search_res == NULL) {
366 0 : return ldb_operr(ldb);
367 : }
368 :
369 : /* We rely here on the preceding "objectclass" LDB module which did
370 : * already fix up the objectclass list (inheritance, order...). */
371 929211 : oc_element = ldb_msg_find_element(ac->search_res->message,
372 : "objectClass");
373 929211 : if (oc_element == NULL) {
374 0 : return ldb_operr(ldb);
375 : }
376 :
377 : /* LSA-specific object classes are not allowed to be created over LDAP,
378 : * so we need to tell if this connection is internal (trusted) or not
379 : * (untrusted).
380 : *
381 : * Hongwei Sun from Microsoft explains:
382 : * The constraint in 3.1.1.5.2.2 MS-ADTS means that LSA objects cannot
383 : * be added or modified through the LDAP interface, instead they can
384 : * only be handled through LSA Policy API. This is also explained in
385 : * 7.1.6.9.7 MS-ADTS as follows:
386 : * "Despite being replicated normally between peer DCs in a domain,
387 : * the process of creating or manipulating TDOs is specifically
388 : * restricted to the LSA Policy APIs, as detailed in [MS-LSAD] section
389 : * 3.1.1.5. Unlike other objects in the DS, TDOs may not be created or
390 : * manipulated by client machines over the LDAPv3 transport."
391 : */
392 3082903 : for (i = 0; i < oc_element->num_values; i++) {
393 2153694 : char * attname = (char *)oc_element->values[i].data;
394 2153694 : if (ldb_req_is_untrusted(ac->req)) {
395 501587 : if (strcmp(attname, "secret") == 0 ||
396 501585 : strcmp(attname, "trustedDomain") == 0) {
397 2 : ldb_asprintf_errstring(ldb, "objectclass_attrs: LSA objectclasses (entry '%s') cannot be created or changed over LDAP!",
398 2 : ldb_dn_get_linearized(ac->search_res->message->dn));
399 2 : return LDB_ERR_UNWILLING_TO_PERFORM;
400 : }
401 : }
402 2153692 : if (strcmp(attname, "attributeSchema") == 0) {
403 221548 : isSchemaAttr = true;
404 : }
405 : }
406 :
407 929209 : must_contain = dsdb_full_attribute_list(ac, ac->schema, oc_element,
408 : DSDB_SCHEMA_ALL_MUST);
409 929209 : may_contain = dsdb_full_attribute_list(ac, ac->schema, oc_element,
410 : DSDB_SCHEMA_ALL_MAY);
411 929209 : found_must_contain = const_str_list(str_list_copy(ac, must_contain));
412 929209 : if ((must_contain == NULL) || (may_contain == NULL)
413 929209 : || (found_must_contain == NULL)) {
414 0 : return ldb_operr(ldb);
415 : }
416 :
417 : /* Check the delete-protected attributes list */
418 929209 : msg = ac->search_res->message;
419 15796075 : for (l = del_prot_attributes; *l != NULL; l++) {
420 1708928 : struct ldb_message_element *el;
421 :
422 14866930 : el = ldb_msg_find_element(ac->msg, *l);
423 14866930 : if (el == NULL) {
424 : /*
425 : * It was not specified in the add or modify,
426 : * so it doesn't need to be in the stored record
427 : */
428 13736928 : continue;
429 : }
430 :
431 1130002 : found = str_list_check_ci(must_contain, *l);
432 1130002 : if (!found) {
433 467855 : found = str_list_check_ci(may_contain, *l);
434 : }
435 1130002 : if (found && (ldb_msg_find_element(msg, *l) == NULL)) {
436 64 : ldb_asprintf_errstring(ldb, "objectclass_attrs: delete protected attribute '%s' on entry '%s' missing!",
437 : *l,
438 : ldb_dn_get_linearized(msg->dn));
439 64 : return LDB_ERR_UNWILLING_TO_PERFORM;
440 : }
441 : }
442 :
443 : /* Check if all specified attributes are valid in the given
444 : * objectclasses and if they meet additional schema restrictions. */
445 20656329 : for (i = 0; i < msg->num_elements; i++) {
446 21997761 : attr = dsdb_attribute_by_lDAPDisplayName(ac->schema,
447 19727215 : msg->elements[i].name);
448 19727215 : if (attr == NULL) {
449 0 : if (ldb_request_get_control(ac->req, DSDB_CONTROL_DBCHECK)) {
450 : /* allow this to make it possible for dbcheck
451 : to remove bad attributes */
452 0 : continue;
453 : }
454 0 : return ldb_operr(ldb);
455 : }
456 :
457 19727215 : if (attr->linkID & 1) {
458 : /*
459 : * We need to allow backlinks on all objects
460 : * even if the schema doesn't allow it.
461 : */
462 94779 : continue;
463 : }
464 :
465 : /* We can use "str_list_check" with "strcmp" here since the
466 : * attribute information from the schema are always equal
467 : * up-down-cased. */
468 19632436 : found = str_list_check(must_contain, attr->lDAPDisplayName);
469 19632436 : if (found) {
470 6479112 : str_list_remove(found_must_contain, attr->lDAPDisplayName);
471 : } else {
472 13153324 : found = str_list_check(may_contain, attr->lDAPDisplayName);
473 : }
474 19632436 : if (!found) {
475 31 : found = str_list_check(harmless_attrs, attr->lDAPDisplayName);
476 : }
477 19632436 : if (!found) {
478 : /* we allow this for dbcheck to fix the rest of this broken entry */
479 31 : if (!ldb_request_get_control(ac->req, DSDB_CONTROL_DBCHECK) ||
480 0 : ac->req->operation == LDB_ADD) {
481 31 : ldb_asprintf_errstring(ldb, "objectclass_attrs: attribute '%s' on entry '%s' does not exist in the specified objectclasses!",
482 31 : msg->elements[i].name,
483 : ldb_dn_get_linearized(msg->dn));
484 31 : return LDB_ERR_OBJECT_CLASS_VIOLATION;
485 : }
486 : }
487 : }
488 :
489 : /*
490 : * We skip this check under dbcheck to allow fixing of other
491 : * attributes even if an attribute is missing. This matters
492 : * for CN=RID Set as the required attribute rIDNextRid is not
493 : * replicated.
494 : */
495 964405 : if (found_must_contain[0] != NULL &&
496 35291 : ldb_msg_check_string_attribute(msg, "isDeleted", "TRUE") == 0) {
497 :
498 62 : for (i = 0; found_must_contain[i] != NULL; i++) {
499 42 : const struct dsdb_attribute *broken_attr = dsdb_attribute_by_lDAPDisplayName(ac->schema,
500 42 : found_must_contain[i]);
501 :
502 42 : bool replicated = (broken_attr->systemFlags &
503 : (DS_FLAG_ATTR_NOT_REPLICATED | DS_FLAG_ATTR_IS_CONSTRUCTED)) == 0;
504 :
505 42 : if (replicated) {
506 3 : ldb_asprintf_errstring(ldb, "objectclass_attrs: at least one mandatory "
507 : "attribute ('%s') on entry '%s' wasn't specified!",
508 3 : found_must_contain[i],
509 : ldb_dn_get_linearized(msg->dn));
510 3 : return LDB_ERR_OBJECT_CLASS_VIOLATION;
511 : }
512 : }
513 : }
514 :
515 929111 : if (isSchemaAttr) {
516 : /*
517 : * Before really adding an attribute in the database,
518 : * let's check that we can translate it into a dsdb_attribute and
519 : * that we can find a valid syntax object.
520 : * If not it's better to reject this attribute than not be able
521 : * to start samba next time due to schema being unloadable.
522 : */
523 221548 : struct dsdb_attribute *att = talloc(ac, struct dsdb_attribute);
524 36920 : const struct dsdb_syntax *attrSyntax;
525 36920 : WERROR status;
526 :
527 221548 : status = dsdb_attribute_from_ldb(NULL, msg, att);
528 221548 : if (!W_ERROR_IS_OK(status)) {
529 0 : ldb_set_errstring(ldb,
530 : "objectclass: failed to translate the schemaAttribute to a dsdb_attribute");
531 0 : return LDB_ERR_UNWILLING_TO_PERFORM;
532 : }
533 :
534 221548 : attrSyntax = dsdb_syntax_for_attribute(att);
535 221548 : if (!attrSyntax) {
536 0 : ldb_set_errstring(ldb,
537 : "objectclass: unknown attribute syntax");
538 0 : return LDB_ERR_UNWILLING_TO_PERFORM;
539 : }
540 : }
541 929111 : return ldb_module_done(ac->req, ac->mod_ares->controls,
542 929111 : ac->mod_ares->response, LDB_SUCCESS);
543 : }
544 :
545 1858422 : static int get_search_callback(struct ldb_request *req, struct ldb_reply *ares)
546 : {
547 213616 : struct ldb_context *ldb;
548 213616 : struct oc_context *ac;
549 213616 : int ret;
550 :
551 1858422 : ac = talloc_get_type(req->context, struct oc_context);
552 1858422 : ldb = ldb_module_get_ctx(ac->module);
553 :
554 1858422 : if (!ares) {
555 0 : return ldb_module_done(ac->req, NULL, NULL,
556 : LDB_ERR_OPERATIONS_ERROR);
557 : }
558 1858422 : if (ares->error != LDB_SUCCESS) {
559 0 : return ldb_module_done(ac->req, ares->controls,
560 : ares->response, ares->error);
561 : }
562 :
563 1858422 : ldb_reset_err_string(ldb);
564 :
565 1858422 : switch (ares->type) {
566 929211 : case LDB_REPLY_ENTRY:
567 929211 : if (ac->search_res != NULL) {
568 0 : ldb_set_errstring(ldb, "Too many results");
569 0 : talloc_free(ares);
570 0 : return ldb_module_done(ac->req, NULL, NULL,
571 : LDB_ERR_OPERATIONS_ERROR);
572 : }
573 :
574 929211 : ac->search_res = talloc_steal(ac, ares);
575 929211 : break;
576 :
577 0 : case LDB_REPLY_REFERRAL:
578 : /* ignore */
579 0 : talloc_free(ares);
580 0 : break;
581 :
582 929211 : case LDB_REPLY_DONE:
583 929211 : talloc_free(ares);
584 929211 : ret = attr_handler2(ac);
585 929211 : if (ret != LDB_SUCCESS) {
586 100 : return ldb_module_done(ac->req, NULL, NULL, ret);
587 : }
588 822303 : break;
589 : }
590 :
591 1644706 : return LDB_SUCCESS;
592 : }
593 :
594 930037 : static int oc_op_callback(struct ldb_request *req, struct ldb_reply *ares)
595 : {
596 106809 : struct oc_context *ac;
597 106809 : struct ldb_context *ldb;
598 106809 : struct ldb_request *search_req;
599 106809 : struct ldb_dn *base_dn;
600 106809 : int ret;
601 106809 : static const char *attrs[] = {"nTSecurityDescriptor", "*", NULL};
602 :
603 930037 : ac = talloc_get_type(req->context, struct oc_context);
604 930037 : ldb = ldb_module_get_ctx(ac->module);
605 :
606 930037 : if (!ares) {
607 0 : return ldb_module_done(ac->req, NULL, NULL,
608 : LDB_ERR_OPERATIONS_ERROR);
609 : }
610 :
611 930037 : if (ares->type == LDB_REPLY_REFERRAL) {
612 10 : return ldb_module_send_referral(ac->req, ares->referral);
613 : }
614 :
615 930027 : if (ares->error != LDB_SUCCESS) {
616 816 : return ldb_module_done(ac->req, ares->controls, ares->response,
617 : ares->error);
618 : }
619 :
620 929211 : if (ares->type != LDB_REPLY_DONE) {
621 0 : talloc_free(ares);
622 0 : return ldb_module_done(ac->req, NULL, NULL,
623 : LDB_ERR_OPERATIONS_ERROR);
624 : }
625 :
626 929211 : ac->search_res = NULL;
627 929211 : ac->mod_ares = talloc_steal(ac, ares);
628 :
629 : /* This looks up all attributes of our just added/modified entry */
630 2400609 : base_dn = ac->req->operation == LDB_ADD ? ac->req->op.add.message->dn
631 929211 : : ac->req->op.mod.message->dn;
632 929211 : ret = ldb_build_search_req(&search_req, ldb, ac, base_dn,
633 : LDB_SCOPE_BASE, "(objectClass=*)",
634 : attrs, NULL, ac,
635 : get_search_callback, ac->req);
636 929211 : LDB_REQ_SET_LOCATION(search_req);
637 929211 : if (ret != LDB_SUCCESS) {
638 0 : return ldb_module_done(ac->req, NULL, NULL, ret);
639 : }
640 :
641 929211 : ret = ldb_request_add_control(search_req, LDB_CONTROL_SHOW_RECYCLED_OID,
642 : true, NULL);
643 929211 : if (ret != LDB_SUCCESS) {
644 0 : return ldb_module_done(ac->req, NULL, NULL, ret);
645 : }
646 :
647 : /*
648 : * This ensures we see if there was a DN, that pointed at an
649 : * object that is now deleted, that we still consider the
650 : * schema check to have passed
651 : */
652 929211 : ret = ldb_request_add_control(search_req, LDB_CONTROL_REVEAL_INTERNALS,
653 : false, NULL);
654 929211 : if (ret != LDB_SUCCESS) {
655 0 : return ldb_module_done(ac->req, NULL, NULL, ret);
656 : }
657 :
658 929211 : ret = ldb_next_request(ac->module, search_req);
659 929211 : if (ret != LDB_SUCCESS) {
660 0 : return ldb_module_done(ac->req, NULL, NULL, ret);
661 : }
662 :
663 : /* "ldb_module_done" isn't called here since we need to do additional
664 : * checks. It is called at the end of "attr_handler2". */
665 822403 : return LDB_SUCCESS;
666 : }
667 :
668 543046 : static int objectclass_attrs_add(struct ldb_module *module,
669 : struct ldb_request *req)
670 : {
671 83679 : struct ldb_context *ldb;
672 83679 : struct oc_context *ac;
673 :
674 543046 : ldb = ldb_module_get_ctx(module);
675 :
676 543046 : ldb_debug(ldb, LDB_DEBUG_TRACE, "objectclass_attrs_add\n");
677 :
678 : /* do not manipulate our control entries */
679 543046 : if (ldb_dn_is_special(req->op.add.message->dn)) {
680 538 : return ldb_next_request(module, req);
681 : }
682 :
683 542508 : ac = oc_init_context(module, req);
684 542508 : if (ac == NULL) {
685 0 : return ldb_operr(ldb);
686 : }
687 :
688 : /* without schema, there isn't much to do here */
689 542508 : if (ac->schema == NULL) {
690 0 : talloc_free(ac);
691 0 : return ldb_next_request(module, req);
692 : }
693 :
694 542508 : return attr_handler(ac);
695 : }
696 :
697 642716 : static int objectclass_attrs_modify(struct ldb_module *module,
698 : struct ldb_request *req)
699 : {
700 27627 : struct ldb_context *ldb;
701 27627 : struct ldb_control *sd_propagation_control;
702 27627 : int ret;
703 :
704 27627 : struct oc_context *ac;
705 :
706 642716 : ldb = ldb_module_get_ctx(module);
707 :
708 642716 : ldb_debug(ldb, LDB_DEBUG_TRACE, "objectclass_attrs_modify\n");
709 :
710 : /* do not manipulate our control entries */
711 642716 : if (ldb_dn_is_special(req->op.mod.message->dn)) {
712 715 : return ldb_next_request(module, req);
713 : }
714 :
715 642001 : sd_propagation_control = ldb_request_get_control(req,
716 : DSDB_CONTROL_SEC_DESC_PROPAGATION_OID);
717 642001 : if (sd_propagation_control != NULL) {
718 254376 : if (req->op.mod.message->num_elements != 1) {
719 0 : return ldb_module_operr(module);
720 : }
721 254376 : ret = strcmp(req->op.mod.message->elements[0].name,
722 : "nTSecurityDescriptor");
723 254376 : if (ret != 0) {
724 0 : return ldb_module_operr(module);
725 : }
726 :
727 254376 : return ldb_next_request(module, req);
728 : }
729 :
730 387625 : ac = oc_init_context(module, req);
731 387625 : if (ac == NULL) {
732 0 : return ldb_operr(ldb);
733 : }
734 :
735 : /* without schema, there isn't much to do here */
736 387625 : if (ac->schema == NULL) {
737 0 : talloc_free(ac);
738 0 : return ldb_next_request(module, req);
739 : }
740 :
741 387625 : return attr_handler(ac);
742 : }
743 :
744 : static const struct ldb_module_ops ldb_objectclass_attrs_module_ops = {
745 : .name = "objectclass_attrs",
746 : .add = objectclass_attrs_add,
747 : .modify = objectclass_attrs_modify
748 : };
749 :
750 6040 : int ldb_objectclass_attrs_module_init(const char *version)
751 : {
752 6040 : LDB_MODULE_CHECK_VERSION(version);
753 6040 : return ldb_register_module(&ldb_objectclass_attrs_module_ops);
754 : }
|