Line data Source code
1 : /*
2 : ldb database library
3 :
4 : Copyright (C) Simo Sorce 2005-2008
5 : Copyright (C) Andrew Bartlett <abartlet@samba.org> 2007-2009
6 :
7 : This program is free software; you can redistribute it and/or modify
8 : it under the terms of the GNU General Public License as published by
9 : the Free Software Foundation; either version 3 of the License, or
10 : (at your option) any later version.
11 :
12 : This program is distributed in the hope that it will be useful,
13 : but WITHOUT ANY WARRANTY; without even the implied warranty of
14 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 : GNU General Public License for more details.
16 :
17 : You should have received a copy of the GNU General Public License
18 : along with this program. If not, see <http://www.gnu.org/licenses/>.
19 : */
20 :
21 : /*
22 : * Name: ldb
23 : *
24 : * Component: ldb extended dn control module
25 : *
26 : * Description: this module builds a special dn for returned search
27 : * results nad creates the special DN in the backend store for new
28 : * values.
29 : *
30 : * This also has the curious result that we convert <SID=S-1-2-345>
31 : * in an attribute value into a normal DN for the rest of the stack
32 : * to process
33 : *
34 : * Authors: Simo Sorce
35 : * Andrew Bartlett
36 : */
37 :
38 : #include "includes.h"
39 : #include <ldb.h>
40 : #include <ldb_errors.h>
41 : #include <ldb_module.h>
42 : #include "librpc/gen_ndr/ndr_misc.h"
43 : #include "dsdb/samdb/samdb.h"
44 : #include "libcli/security/security.h"
45 : #include "dsdb/samdb/ldb_modules/util.h"
46 : #include <time.h>
47 :
48 : struct extended_dn_replace_list {
49 : struct extended_dn_replace_list *next;
50 : struct dsdb_dn *dsdb_dn;
51 : TALLOC_CTX *mem_ctx;
52 : struct ldb_val *replace_dn;
53 : struct extended_dn_context *ac;
54 : struct ldb_request *search_req;
55 : bool fpo_enabled;
56 : bool require_object;
57 : bool got_entry;
58 : };
59 :
60 :
61 : struct extended_dn_context {
62 : const struct dsdb_schema *schema;
63 : struct ldb_module *module;
64 : struct ldb_context *ldb;
65 : struct ldb_request *req;
66 : struct ldb_request *new_req;
67 :
68 : struct extended_dn_replace_list *ops;
69 : struct extended_dn_replace_list *cur;
70 :
71 : /*
72 : * Used by the FPO-enabled attribute validation.
73 : */
74 : struct dsdb_trust_routing_table *routing_table;
75 : };
76 :
77 :
78 863788 : static struct extended_dn_context *extended_dn_context_init(struct ldb_module *module,
79 : struct ldb_request *req)
80 : {
81 100754 : struct extended_dn_context *ac;
82 863788 : struct ldb_context *ldb = ldb_module_get_ctx(module);
83 863788 : ac = talloc_zero(req, struct extended_dn_context);
84 863788 : if (ac == NULL) {
85 0 : ldb_oom(ldb);
86 0 : return NULL;
87 : }
88 :
89 863788 : ac->schema = dsdb_get_schema(ldb, ac);
90 863788 : ac->module = module;
91 863788 : ac->ldb = ldb;
92 863788 : ac->req = req;
93 :
94 863788 : return ac;
95 : }
96 :
97 157987 : static int extended_replace_dn(struct extended_dn_replace_list *os,
98 : struct ldb_dn *dn)
99 : {
100 157987 : struct dsdb_dn *dsdb_dn = NULL;
101 157987 : const char *str = NULL;
102 :
103 : /*
104 : * Rebuild with the string or binary 'extra part' the
105 : * DN may have had as a prefix
106 : */
107 163842 : dsdb_dn = dsdb_dn_construct(os, dn,
108 152132 : os->dsdb_dn->extra_part,
109 157987 : os->dsdb_dn->oid);
110 157987 : if (dsdb_dn == NULL) {
111 0 : return ldb_module_operr(os->ac->module);
112 : }
113 :
114 157987 : str = dsdb_dn_get_extended_linearized(os->mem_ctx,
115 : dsdb_dn, 1);
116 157987 : if (str == NULL) {
117 0 : return ldb_module_operr(os->ac->module);
118 : }
119 :
120 : /*
121 : * Replace the DN with the extended version of the DN
122 : * (ie, add SID and GUID)
123 : */
124 157987 : *os->replace_dn = data_blob_string_const(str);
125 157987 : os->got_entry = true;
126 157987 : return LDB_SUCCESS;
127 : }
128 :
129 78 : static int extended_dn_handle_fpo_attr(struct extended_dn_replace_list *os)
130 : {
131 78 : struct dom_sid target_sid = { 0, };
132 78 : struct dom_sid target_domain = { 0, };
133 78 : struct ldb_message *fmsg = NULL;
134 78 : char *fsid = NULL;
135 78 : const struct dom_sid *domain_sid = NULL;
136 78 : struct ldb_dn *domain_dn = NULL;
137 78 : const struct lsa_TrustDomainInfoInfoEx *tdo = NULL;
138 78 : uint32_t trust_attributes = 0;
139 78 : const char *no_attrs[] = { NULL, };
140 78 : struct ldb_result *res = NULL;
141 0 : NTSTATUS status;
142 0 : bool match;
143 0 : bool ok;
144 0 : int ret;
145 :
146 : /*
147 : * DN doesn't exist yet
148 : *
149 : * Check if a foreign SID is specified,
150 : * which would trigger the creation
151 : * of a foreignSecurityPrincipal.
152 : */
153 78 : status = dsdb_get_extended_dn_sid(os->dsdb_dn->dn,
154 : &target_sid,
155 : "SID");
156 78 : if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
157 : /*
158 : * No SID specified
159 : */
160 27 : return dsdb_module_werror(os->ac->module,
161 : LDB_ERR_NO_SUCH_OBJECT,
162 : WERR_NO_SUCH_USER,
163 : "specified dn doesn't exist");
164 : }
165 51 : if (!NT_STATUS_IS_OK(status)) {
166 0 : return ldb_module_operr(os->ac->module);
167 : }
168 51 : if (ldb_dn_get_extended_comp_num(os->dsdb_dn->dn) != 1) {
169 0 : return dsdb_module_werror(os->ac->module,
170 : LDB_ERR_NO_SUCH_OBJECT,
171 : WERR_NO_SUCH_USER,
172 : "specified extended component other than SID");
173 : }
174 51 : if (ldb_dn_get_comp_num(os->dsdb_dn->dn) != 0) {
175 0 : return dsdb_module_werror(os->ac->module,
176 : LDB_ERR_NO_SUCH_OBJECT,
177 : WERR_NO_SUCH_USER,
178 : "specified more the SID");
179 : }
180 :
181 51 : target_domain = target_sid;
182 51 : sid_split_rid(&target_domain, NULL);
183 :
184 51 : match = dom_sid_equal(&global_sid_Builtin, &target_domain);
185 51 : if (match) {
186 : /*
187 : * Non existing BUILTIN sid
188 : */
189 4 : return dsdb_module_werror(os->ac->module,
190 : LDB_ERR_NO_SUCH_OBJECT,
191 : WERR_NO_SUCH_MEMBER,
192 : "specified sid doesn't exist in BUILTIN");
193 : }
194 :
195 47 : domain_sid = samdb_domain_sid(os->ac->ldb);
196 47 : if (domain_sid == NULL) {
197 0 : return ldb_module_operr(os->ac->module);
198 : }
199 47 : match = dom_sid_equal(domain_sid, &target_domain);
200 47 : if (match) {
201 : /*
202 : * Non existing SID in our domain.
203 : */
204 4 : return dsdb_module_werror(os->ac->module,
205 : LDB_ERR_UNWILLING_TO_PERFORM,
206 : WERR_DS_INVALID_GROUP_TYPE,
207 : "specified sid doesn't exist in domain");
208 : }
209 :
210 43 : if (os->ac->routing_table == NULL) {
211 43 : status = dsdb_trust_routing_table_load(os->ac->ldb, os->ac,
212 43 : &os->ac->routing_table);
213 43 : if (!NT_STATUS_IS_OK(status)) {
214 0 : return ldb_module_operr(os->ac->module);
215 : }
216 : }
217 :
218 43 : tdo = dsdb_trust_domain_by_sid(os->ac->routing_table,
219 : &target_domain, NULL);
220 43 : if (tdo != NULL) {
221 11 : trust_attributes = tdo->trust_attributes;
222 : }
223 :
224 43 : if (trust_attributes & LSA_TRUST_ATTRIBUTE_WITHIN_FOREST) {
225 0 : return dsdb_module_werror(os->ac->module,
226 : LDB_ERR_UNWILLING_TO_PERFORM,
227 : WERR_DS_INVALID_GROUP_TYPE,
228 : "specified sid doesn't exist in forest");
229 : }
230 :
231 43 : fmsg = ldb_msg_new(os);
232 43 : if (fmsg == NULL) {
233 0 : return ldb_module_oom(os->ac->module);
234 : }
235 :
236 43 : fsid = dom_sid_string(fmsg, &target_sid);
237 43 : if (fsid == NULL) {
238 0 : return ldb_module_oom(os->ac->module);
239 : }
240 :
241 43 : domain_dn = ldb_get_default_basedn(os->ac->ldb);
242 43 : if (domain_dn == NULL) {
243 0 : return ldb_module_operr(os->ac->module);
244 : }
245 :
246 43 : fmsg->dn = ldb_dn_copy(fmsg, domain_dn);
247 43 : if (fmsg->dn == NULL) {
248 0 : return ldb_module_oom(os->ac->module);
249 : }
250 :
251 43 : ok = ldb_dn_add_child_fmt(fmsg->dn,
252 : "CN=%s,CN=ForeignSecurityPrincipals",
253 : fsid);
254 43 : if (!ok) {
255 0 : return ldb_module_oom(os->ac->module);
256 : }
257 :
258 43 : ret = ldb_msg_add_string(fmsg, "objectClass", "foreignSecurityPrincipal");
259 43 : if (ret != LDB_SUCCESS) {
260 0 : return ret;
261 : }
262 :
263 43 : ret = dsdb_module_add(os->ac->module, fmsg,
264 : DSDB_FLAG_AS_SYSTEM |
265 : DSDB_FLAG_NEXT_MODULE,
266 43 : os->ac->req);
267 43 : if (ret != LDB_SUCCESS) {
268 0 : return ret;
269 : }
270 :
271 43 : ret = dsdb_module_search_dn(os->ac->module, fmsg, &res,
272 : fmsg->dn, no_attrs,
273 : DSDB_FLAG_AS_SYSTEM |
274 : DSDB_FLAG_NEXT_MODULE |
275 : DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT,
276 43 : os->ac->req);
277 43 : if (ret != LDB_SUCCESS) {
278 0 : return ret;
279 : }
280 :
281 : /*
282 : * dsdb_module_search_dn() guarantees exactly one result message
283 : * on success.
284 : */
285 43 : ret = extended_replace_dn(os, res->msgs[0]->dn);
286 43 : TALLOC_FREE(fmsg);
287 43 : if (ret != LDB_SUCCESS) {
288 0 : return ret;
289 : }
290 :
291 43 : return LDB_SUCCESS;
292 : }
293 :
294 : /* An extra layer of indirection because LDB does not allow the original request to be altered */
295 :
296 128943 : static int extended_final_callback(struct ldb_request *req, struct ldb_reply *ares)
297 : {
298 128943 : int ret = LDB_ERR_OPERATIONS_ERROR;
299 4429 : struct extended_dn_context *ac;
300 128943 : ac = talloc_get_type(req->context, struct extended_dn_context);
301 :
302 128943 : if (ares->error != LDB_SUCCESS) {
303 291 : ret = ldb_module_done(ac->req, ares->controls,
304 : ares->response, ares->error);
305 : } else {
306 128652 : switch (ares->type) {
307 0 : case LDB_REPLY_ENTRY:
308 :
309 0 : ret = ldb_module_send_entry(ac->req, ares->message, ares->controls);
310 0 : break;
311 1 : case LDB_REPLY_REFERRAL:
312 :
313 1 : ret = ldb_module_send_referral(ac->req, ares->referral);
314 1 : break;
315 128651 : case LDB_REPLY_DONE:
316 :
317 128651 : ret = ldb_module_done(ac->req, ares->controls,
318 : ares->response, ares->error);
319 128651 : break;
320 : }
321 : }
322 128943 : return ret;
323 : }
324 :
325 318925 : static int extended_replace_callback(struct ldb_request *req, struct ldb_reply *ares)
326 : {
327 318925 : struct extended_dn_replace_list *os = talloc_get_type(req->context,
328 : struct extended_dn_replace_list);
329 :
330 318925 : if (!ares) {
331 0 : return ldb_module_done(os->ac->req, NULL, NULL,
332 : LDB_ERR_OPERATIONS_ERROR);
333 : }
334 318925 : if (ares->error == LDB_ERR_NO_SUCH_OBJECT) {
335 3037 : if (os->got_entry) {
336 : /* This is in internal error... */
337 0 : int ret = ldb_module_operr(os->ac->module);
338 0 : return ldb_module_done(os->ac->req, NULL, NULL, ret);
339 : }
340 :
341 3037 : if (os->require_object && os->fpo_enabled) {
342 0 : int ret;
343 :
344 78 : ret = extended_dn_handle_fpo_attr(os);
345 78 : if (ret != LDB_SUCCESS) {
346 35 : return ldb_module_done(os->ac->req, NULL, NULL,
347 : ret);
348 : }
349 : /* os->got_entry is true at this point... */
350 : }
351 :
352 3002 : if (!os->got_entry && os->require_object) {
353 : /*
354 : * It's an error if the target doesn't exist,
355 : * unless it's a delete.
356 : */
357 21 : int ret = dsdb_module_werror(os->ac->module,
358 : LDB_ERR_CONSTRAINT_VIOLATION,
359 : WERR_DS_NAME_REFERENCE_INVALID,
360 : "Referenced object not found");
361 21 : return ldb_module_done(os->ac->req, NULL, NULL, ret);
362 : }
363 :
364 : /* Don't worry too much about dangling references */
365 :
366 2981 : ldb_reset_err_string(os->ac->ldb);
367 2981 : if (os->next) {
368 330 : struct extended_dn_replace_list *next;
369 :
370 1905 : next = os->next;
371 :
372 1905 : talloc_free(os);
373 :
374 1905 : os = next;
375 1905 : return ldb_next_request(os->ac->module, next->search_req);
376 : } else {
377 : /* Otherwise, we are done - let's run the
378 : * request now we have swapped the DNs for the
379 : * full versions */
380 1076 : return ldb_next_request(os->ac->module, os->ac->new_req);
381 : }
382 : }
383 315888 : if (ares->error != LDB_SUCCESS) {
384 0 : return ldb_module_done(os->ac->req, ares->controls,
385 : ares->response, ares->error);
386 : }
387 :
388 : /* Only entries are interesting, and we only want the olddn */
389 315888 : switch (ares->type) {
390 157944 : case LDB_REPLY_ENTRY:
391 : {
392 : /* This *must* be the right DN, as this is a base
393 : * search. We can't check, as it could be an extended
394 : * DN, so a module below will resolve it */
395 5855 : int ret;
396 :
397 157944 : ret = extended_replace_dn(os, ares->message->dn);
398 157944 : if (ret != LDB_SUCCESS) {
399 0 : return ldb_module_done(os->ac->req, NULL, NULL, ret);
400 : }
401 : /* os->got_entry is true at this point */
402 152089 : break;
403 : }
404 0 : case LDB_REPLY_REFERRAL:
405 : /* ignore */
406 0 : break;
407 :
408 157944 : case LDB_REPLY_DONE:
409 :
410 157944 : talloc_free(ares);
411 :
412 157944 : if (!os->got_entry && os->require_object && os->fpo_enabled) {
413 0 : int ret;
414 :
415 0 : ret = extended_dn_handle_fpo_attr(os);
416 0 : if (ret != LDB_SUCCESS) {
417 0 : return ldb_module_done(os->ac->req, NULL, NULL,
418 : ret);
419 : }
420 : /* os->got_entry is true at this point... */
421 : }
422 :
423 157944 : if (!os->got_entry && os->require_object) {
424 : /*
425 : * It's an error if the target doesn't exist,
426 : * unless it's a delete.
427 : */
428 0 : int ret = dsdb_module_werror(os->ac->module,
429 : LDB_ERR_CONSTRAINT_VIOLATION,
430 : WERR_DS_NAME_REFERENCE_INVALID,
431 : "Referenced object not found");
432 0 : return ldb_module_done(os->ac->req, NULL, NULL, ret);
433 : }
434 :
435 : /* Run the next search */
436 :
437 157944 : if (os->next) {
438 1604 : struct extended_dn_replace_list *next;
439 :
440 30181 : next = os->next;
441 :
442 30181 : talloc_free(os);
443 :
444 30181 : os = next;
445 30181 : return ldb_next_request(os->ac->module, next->search_req);
446 : } else {
447 : /* Otherwise, we are done - let's run the
448 : * request now we have swapped the DNs for the
449 : * full versions */
450 127763 : return ldb_next_request(os->ac->module, os->ac->new_req);
451 : }
452 : }
453 :
454 157944 : talloc_free(ares);
455 157944 : return LDB_SUCCESS;
456 : }
457 :
458 : /* We have a 'normal' DN in the inbound request. We need to find out
459 : * what the GUID and SID are on the DN it points to, so we can
460 : * construct an extended DN for storage.
461 : *
462 : * This creates a list of DNs to look up, and the plain DN to replace
463 : */
464 :
465 298189 : static int extended_store_replace(struct extended_dn_context *ac,
466 : TALLOC_CTX *callback_mem_ctx,
467 : struct ldb_dn *self_dn,
468 : struct ldb_val *plain_dn,
469 : bool is_delete,
470 : const struct dsdb_attribute *schema_attr)
471 : {
472 298189 : const char *oid = schema_attr->syntax->ldap_oid;
473 14758 : int ret;
474 14758 : struct extended_dn_replace_list *os;
475 14758 : static const char *attrs[] = {
476 : "objectSid",
477 : "objectGUID",
478 : NULL
479 : };
480 298189 : uint32_t ctrl_flags = 0;
481 298189 : bool is_untrusted = ldb_req_is_untrusted(ac->req);
482 :
483 298189 : os = talloc_zero(ac, struct extended_dn_replace_list);
484 298189 : if (!os) {
485 0 : return ldb_oom(ac->ldb);
486 : }
487 :
488 298189 : os->ac = ac;
489 :
490 298189 : os->mem_ctx = callback_mem_ctx;
491 :
492 298189 : os->dsdb_dn = dsdb_dn_parse(os, ac->ldb, plain_dn, oid);
493 298189 : if (!os->dsdb_dn || !ldb_dn_validate(os->dsdb_dn->dn)) {
494 12 : talloc_free(os);
495 12 : ldb_asprintf_errstring(ac->ldb,
496 12 : "could not parse %.*s as a %s DN", (int)plain_dn->length, plain_dn->data,
497 : oid);
498 12 : return LDB_ERR_INVALID_DN_SYNTAX;
499 : }
500 :
501 298177 : if (self_dn != NULL) {
502 54655 : ret = ldb_dn_compare(self_dn, os->dsdb_dn->dn);
503 54655 : if (ret == 0) {
504 : /*
505 : * If this is a reference to the object
506 : * itself during an 'add', we won't
507 : * be able to find the object.
508 : */
509 32485 : talloc_free(os);
510 32485 : return LDB_SUCCESS;
511 : }
512 : }
513 :
514 265692 : if (is_delete && !ldb_dn_has_extended(os->dsdb_dn->dn)) {
515 : /* NO need to figure this DN out, this element is
516 : * going to be deleted anyway, and because it's not
517 : * extended, we have enough information to do the
518 : * delete */
519 104707 : talloc_free(os);
520 104707 : return LDB_SUCCESS;
521 : }
522 :
523 :
524 160985 : os->replace_dn = plain_dn;
525 :
526 : /* The search request here might happen to be for an
527 : * 'extended' style DN, such as <GUID=abced...>. The next
528 : * module in the stack will convert this into a normal DN for
529 : * processing */
530 167348 : ret = ldb_build_search_req(&os->search_req,
531 160985 : ac->ldb, os, os->dsdb_dn->dn, LDB_SCOPE_BASE, NULL,
532 : attrs, NULL, os, extended_replace_callback,
533 : ac->req);
534 160985 : LDB_REQ_SET_LOCATION(os->search_req);
535 160985 : if (ret != LDB_SUCCESS) {
536 0 : talloc_free(os);
537 0 : return ret;
538 : }
539 :
540 : /*
541 : * By default we require the presence of the target.
542 : */
543 160985 : os->require_object = true;
544 :
545 : /*
546 : * Handle FPO-enabled attributes, see
547 : * [MS-ADTS] 3.1.1.5.2.3 Special Classes and Attributes:
548 : *
549 : * FPO-enabled attributes: member, msDS-MembersForAzRole,
550 : * msDS-NeverRevealGroup, msDS-NonMembers, msDS-RevealOnDemandGroup,
551 : * msDS-ServiceAccount.
552 : *
553 : * Note there's no msDS-ServiceAccount in any schema (only
554 : * msDS-HostServiceAccount and that's not an FPO-enabled attribute
555 : * at least not in W2008R2)
556 : *
557 : * msDS-NonMembers always generates NOT_SUPPORTED against W2008R2.
558 : *
559 : * See also [MS-SAMR] 3.1.1.8.9 member.
560 : */
561 160985 : switch (schema_attr->attributeID_id) {
562 14944 : case DRSUAPI_ATTID_member:
563 : case DRSUAPI_ATTID_msDS_MembersForAzRole:
564 : case DRSUAPI_ATTID_msDS_NeverRevealGroup:
565 : case DRSUAPI_ATTID_msDS_RevealOnDemandGroup:
566 14944 : os->fpo_enabled = true;
567 14948 : break;
568 :
569 4 : case DRSUAPI_ATTID_msDS_HostServiceAccount:
570 : /* This is NOT a FPO-enabled attribute */
571 4 : break;
572 :
573 4 : case DRSUAPI_ATTID_msDS_NonMembers:
574 4 : return dsdb_module_werror(os->ac->module,
575 : LDB_ERR_UNWILLING_TO_PERFORM,
576 : WERR_NOT_SUPPORTED,
577 : "msDS-NonMembers is not supported");
578 : }
579 :
580 160981 : if (schema_attr->linkID == 0) {
581 : /*
582 : * None linked attributes allow references
583 : * to deleted objects.
584 : */
585 116947 : ctrl_flags |= DSDB_SEARCH_SHOW_RECYCLED;
586 : }
587 :
588 160981 : if (is_delete) {
589 : /*
590 : * On delete want to be able to
591 : * find a deleted object, but
592 : * it's not a problem if they doesn't
593 : * exist.
594 : */
595 730 : ctrl_flags |= DSDB_SEARCH_SHOW_RECYCLED;
596 730 : os->require_object = false;
597 : }
598 :
599 160981 : if (!is_untrusted) {
600 143448 : struct ldb_control *ctrl = NULL;
601 :
602 : /*
603 : * During provision or dbcheck we may not find
604 : * an object.
605 : */
606 :
607 143448 : ctrl = ldb_request_get_control(ac->req, LDB_CONTROL_RELAX_OID);
608 143448 : if (ctrl != NULL) {
609 17796 : os->require_object = false;
610 : }
611 143448 : ctrl = ldb_request_get_control(ac->req, DSDB_CONTROL_DBCHECK);
612 143448 : if (ctrl != NULL) {
613 104432 : os->require_object = false;
614 : }
615 : }
616 :
617 160981 : ret = dsdb_request_add_controls(os->search_req,
618 : DSDB_FLAG_AS_SYSTEM |
619 : ctrl_flags |
620 : DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT);
621 160981 : if (ret != LDB_SUCCESS) {
622 0 : talloc_free(os);
623 0 : return ret;
624 : }
625 :
626 160981 : if (ac->ops) {
627 32086 : ac->cur->next = os;
628 : } else {
629 128895 : ac->ops = os;
630 : }
631 160981 : ac->cur = os;
632 :
633 160981 : return LDB_SUCCESS;
634 : }
635 :
636 :
637 : /* add */
638 543719 : static int extended_dn_add(struct ldb_module *module, struct ldb_request *req)
639 : {
640 83679 : struct extended_dn_context *ac;
641 83679 : int ret;
642 83679 : unsigned int i, j;
643 :
644 543719 : if (ldb_dn_is_special(req->op.add.message->dn)) {
645 : /* do not manipulate our control entries */
646 538 : return ldb_next_request(module, req);
647 : }
648 :
649 543181 : ac = extended_dn_context_init(module, req);
650 543181 : if (!ac) {
651 0 : return ldb_operr(ldb_module_get_ctx(module));
652 : }
653 :
654 543181 : if (!ac->schema) {
655 : /* without schema, this doesn't make any sense */
656 0 : talloc_free(ac);
657 0 : return ldb_next_request(module, req);
658 : }
659 :
660 5504072 : for (i=0; i < req->op.add.message->num_elements; i++) {
661 4960891 : const struct ldb_message_element *el = &req->op.add.message->elements[i];
662 850758 : const struct dsdb_attribute *schema_attr
663 4960891 : = dsdb_attribute_by_lDAPDisplayName(ac->schema, el->name);
664 4960891 : if (!schema_attr) {
665 483 : continue;
666 : }
667 :
668 : /* We only setup an extended DN GUID on DN elements */
669 4960408 : if (schema_attr->dn_format == DSDB_INVALID_DN) {
670 4913484 : continue;
671 : }
672 :
673 46924 : if (schema_attr->attributeID_id == DRSUAPI_ATTID_distinguishedName) {
674 : /* distinguishedName values are ignored */
675 10 : continue;
676 : }
677 :
678 : /* Before we setup a procedure to modify the incoming message, we must copy it */
679 46914 : if (!ac->new_req) {
680 42973 : struct ldb_message *msg = ldb_msg_copy(ac, req->op.add.message);
681 42973 : if (!msg) {
682 0 : return ldb_oom(ldb_module_get_ctx(module));
683 : }
684 :
685 42973 : ret = ldb_build_add_req(&ac->new_req, ac->ldb, ac, msg, req->controls, ac, extended_final_callback, req);
686 42973 : LDB_REQ_SET_LOCATION(ac->new_req);
687 42973 : if (ret != LDB_SUCCESS) {
688 0 : return ret;
689 : }
690 : }
691 : /* Re-calculate el */
692 46914 : el = &ac->new_req->op.add.message->elements[i];
693 101569 : for (j = 0; j < el->num_values; j++) {
694 63278 : ret = extended_store_replace(ac, ac->new_req,
695 54655 : req->op.add.message->dn,
696 54655 : &el->values[j],
697 : false, schema_attr);
698 54655 : if (ret != LDB_SUCCESS) {
699 0 : return ret;
700 : }
701 : }
702 : }
703 :
704 : /* if no DNs were set continue */
705 543181 : if (ac->ops == NULL) {
706 532552 : talloc_free(ac);
707 532552 : return ldb_next_request(module, req);
708 : }
709 :
710 : /* start with the searches */
711 10629 : return ldb_next_request(module, ac->ops->search_req);
712 : }
713 :
714 : /* modify */
715 321322 : static int extended_dn_modify(struct ldb_module *module, struct ldb_request *req)
716 : {
717 : /* Look over list of modifications */
718 : /* Find if any are for linked attributes */
719 : /* Determine the effect of the modification */
720 : /* Apply the modify to the linked entry */
721 :
722 17201 : unsigned int i, j;
723 17201 : struct extended_dn_context *ac;
724 321322 : struct ldb_control *fix_links_control = NULL;
725 321322 : struct ldb_control *fix_link_sid_ctrl = NULL;
726 17201 : int ret;
727 :
728 321322 : if (ldb_dn_is_special(req->op.mod.message->dn)) {
729 : /* do not manipulate our control entries */
730 715 : return ldb_next_request(module, req);
731 : }
732 :
733 320607 : ac = extended_dn_context_init(module, req);
734 320607 : if (!ac) {
735 0 : return ldb_operr(ldb_module_get_ctx(module));
736 : }
737 :
738 320607 : if (!ac->schema) {
739 0 : talloc_free(ac);
740 : /* without schema, this doesn't make any sense */
741 0 : return ldb_next_request(module, req);
742 : }
743 :
744 320607 : fix_links_control = ldb_request_get_control(req,
745 : DSDB_CONTROL_DBCHECK_FIX_DUPLICATE_LINKS);
746 320607 : if (fix_links_control != NULL) {
747 2 : return ldb_next_request(module, req);
748 : }
749 :
750 320605 : fix_link_sid_ctrl = ldb_request_get_control(ac->req,
751 : DSDB_CONTROL_DBCHECK_FIX_LINK_DN_SID);
752 320605 : if (fix_link_sid_ctrl != NULL) {
753 2 : return ldb_next_request(module, req);
754 : }
755 :
756 828891 : for (i=0; i < req->op.mod.message->num_elements; i++) {
757 508304 : const struct ldb_message_element *el = &req->op.mod.message->elements[i];
758 22332 : const struct dsdb_attribute *schema_attr
759 508304 : = dsdb_attribute_by_lDAPDisplayName(ac->schema, el->name);
760 508304 : if (!schema_attr) {
761 1926 : continue;
762 : }
763 :
764 : /* We only setup an extended DN GUID on these particular DN objects */
765 506378 : if (schema_attr->dn_format == DSDB_INVALID_DN) {
766 265377 : continue;
767 : }
768 :
769 241001 : if (schema_attr->attributeID_id == DRSUAPI_ATTID_distinguishedName) {
770 : /* distinguishedName values are ignored */
771 277 : continue;
772 : }
773 :
774 : /* Before we setup a procedure to modify the incoming message, we must copy it */
775 240724 : if (!ac->new_req) {
776 119258 : struct ldb_message *msg = ldb_msg_copy(ac, req->op.mod.message);
777 119258 : if (!msg) {
778 0 : talloc_free(ac);
779 0 : return ldb_oom(ac->ldb);
780 : }
781 :
782 119258 : ret = ldb_build_mod_req(&ac->new_req, ac->ldb, ac, msg, req->controls, ac, extended_final_callback, req);
783 119258 : LDB_REQ_SET_LOCATION(ac->new_req);
784 119258 : if (ret != LDB_SUCCESS) {
785 0 : talloc_free(ac);
786 0 : return ret;
787 : }
788 : }
789 : /* Re-calculate el */
790 240724 : el = &ac->new_req->op.mod.message->elements[i];
791 : /* For each value being added, we need to setup the lookups to fill in the extended DN */
792 484242 : for (j = 0; j < el->num_values; j++) {
793 : /* If we are just going to delete this
794 : * element, only do a lookup if
795 : * extended_store_replace determines it's an
796 : * input of an extended DN */
797 243534 : bool is_delete = (LDB_FLAG_MOD_TYPE(el->flags) == LDB_FLAG_MOD_DELETE);
798 :
799 249669 : ret = extended_store_replace(ac, ac->new_req,
800 : NULL, /* self_dn to be ignored */
801 243534 : &el->values[j],
802 : is_delete, schema_attr);
803 243534 : if (ret != LDB_SUCCESS) {
804 16 : talloc_free(ac);
805 16 : return ret;
806 : }
807 : }
808 : }
809 :
810 : /* if DNs were set continue */
811 320587 : if (ac->ops == NULL) {
812 202321 : talloc_free(ac);
813 202321 : return ldb_next_request(module, req);
814 : }
815 :
816 : /* start with the searches */
817 118266 : return ldb_next_request(module, ac->ops->search_req);
818 : }
819 :
820 : static const struct ldb_module_ops ldb_extended_dn_store_module_ops = {
821 : .name = "extended_dn_store",
822 : .add = extended_dn_add,
823 : .modify = extended_dn_modify,
824 : };
825 :
826 6040 : int ldb_extended_dn_store_module_init(const char *version)
827 : {
828 6040 : LDB_MODULE_CHECK_VERSION(version);
829 6040 : return ldb_register_module(&ldb_extended_dn_store_module_ops);
830 : }
|