4 Copyright (C) Simo Sorce 2005-2008
5 Copyright (C) Andrew Bartlett <abartlet@samba.org> 2007-2008
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.
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.
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/>.
24 * Component: ldb extended dn control module
26 * Description: this module builds a special dn for returned search
27 * results nad creates the special DN in the backend store for new
35 #include "ldb/include/ldb.h"
36 #include "ldb/include/ldb_errors.h"
37 #include "ldb/include/ldb_private.h"
38 #include "librpc/gen_ndr/ndr_misc.h"
39 #include "dsdb/samdb/samdb.h"
40 #include "libcli/security/security.h"
44 struct extended_dn_replace_list {
45 struct extended_dn_replace_list *next;
48 struct ldb_val *replace_dn;
49 struct extended_dn_context *ac;
50 struct ldb_request *search_req;
54 struct extended_dn_context {
55 const struct dsdb_schema *schema;
56 struct ldb_module *module;
57 struct ldb_request *req;
59 struct extended_dn_replace_list *ops;
60 struct extended_dn_replace_list *cur;
64 static struct extended_dn_context *extended_dn_context_init(struct ldb_module *module,
65 struct ldb_request *req)
67 struct extended_dn_context *ac;
69 ac = talloc_zero(req, struct extended_dn_context);
71 ldb_set_errstring(module->ldb, "Out of Memory");
75 ac->schema = dsdb_get_schema(module->ldb);
82 static bool is_attr_in_list(const char * const * attrs, const char *attr)
86 for (i = 0; attrs[i]; i++) {
87 if (strcasecmp(attrs[i], attr) == 0)
94 static char **copy_attrs(void *mem_ctx, const char * const * attrs)
99 for (num = 0; attrs[num]; num++);
101 new = talloc_array(mem_ctx, char *, num + 1);
102 if (!new) return NULL;
104 for(i = 0; i < num; i++) {
105 new[i] = talloc_strdup(new, attrs[i]);
116 static bool add_attrs(void *mem_ctx, char ***attrs, const char *attr)
121 for (num = 0; (*attrs)[num]; num++);
123 new = talloc_realloc(mem_ctx, *attrs, char *, num + 2);
124 if (!new) return false;
128 new[num] = talloc_strdup(new, attr);
129 if (!new[num]) return false;
136 static int inject_extended_dn(struct ldb_reply *ares,
137 struct ldb_context *ldb,
143 const struct ldb_val *val;
144 const DATA_BLOB *guid_blob;
145 const DATA_BLOB *sid_blob;
147 guid_blob = ldb_msg_find_ldb_val(ares->message, "objectGUID");
148 sid_blob = ldb_msg_find_ldb_val(ares->message, "objectSID");
151 return LDB_ERR_OPERATIONS_ERROR;
154 ret = ldb_dn_set_extended_component(ares->message->dn, "GUID", guid_blob);
155 if (ret != LDB_SUCCESS) {
159 ret = ldb_dn_set_extended_component(ares->message->dn, "SID", sid_blob);
160 if (ret != LDB_SUCCESS) {
166 ldb_msg_remove_attr(ares->message, "objectGUID");
169 if (sid_blob && remove_sid) {
170 ldb_msg_remove_attr(ares->message, "objectSID");
173 val = ldb_msg_find_ldb_val(ares->message, "distinguishedName");
175 ldb_msg_remove_attr(ares->message, "distinguishedName");
176 ret = ldb_msg_add_steal_string(ares->message, "distinguishedName",
177 ldb_dn_extended_linearized(ares->message, ares->message->dn, type));
178 if (ret != LDB_SUCCESS) {
179 return LDB_ERR_OPERATIONS_ERROR;
186 struct extended_search_context {
187 struct ldb_module *module;
188 const struct dsdb_schema *schema;
189 struct ldb_request *req;
190 struct ldb_control *control;
191 struct ldb_dn *basedn;
192 char *wellknown_object;
197 const char * const *cast_attrs;
200 static int extended_callback(struct ldb_request *req, struct ldb_reply *ares)
202 struct extended_search_context *ac;
204 struct ldb_message *msg = ares->message;
206 ac = talloc_get_type(req->context, struct extended_search_context);
209 return ldb_module_done(ac->req, NULL, NULL,
210 LDB_ERR_OPERATIONS_ERROR);
212 if (ares->error != LDB_SUCCESS) {
213 return ldb_module_done(ac->req, ares->controls,
214 ares->response, ares->error);
217 switch (ares->type) {
218 case LDB_REPLY_REFERRAL:
219 return ldb_module_send_referral(ac->req, ares->referral);
222 return ldb_module_done(ac->req, ares->controls,
223 ares->response, LDB_SUCCESS);
224 case LDB_REPLY_ENTRY:
229 /* for each record returned post-process to add any derived
230 attributes that have been asked for */
231 ret = inject_extended_dn(ares, ac->module->ldb,
232 ac->extended_type, ac->remove_guid,
234 if (ret != LDB_SUCCESS) {
235 return ldb_module_done(ac->req, NULL, NULL, ret);
239 for (i = 0; i < msg->num_elements; i++) {
240 const struct dsdb_attribute *attribute = dsdb_attribute_by_lDAPDisplayName(ac->schema, msg->elements[i].name);
244 /* Look to see if this attributeSyntax is a DN */
245 if (!((strcmp(attribute->attributeSyntax_oid, "2.5.5.1") == 0) ||
246 (strcmp(attribute->attributeSyntax_oid, "2.5.5.7") == 0))) {
249 for (j = 0; j < msg->elements[i].num_values; j++) {
251 struct ldb_dn *dn = ldb_dn_from_ldb_val(ac, ac->module->ldb, &msg->elements[i].values[j]);
253 return ldb_module_done(ac->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
257 dn_str = talloc_steal(msg->elements[i].values,
258 ldb_dn_get_linearized(dn));
260 dn_str = talloc_steal(msg->elements[i].values,
261 ldb_dn_extended_linearized(msg->elements[i].values,
262 dn, ac->extended_type));
264 msg->elements[i].values[j] = data_blob_string_const(dn_str);
268 return ldb_module_send_entry(ac->req, msg);
271 static int extended_base_callback(struct ldb_request *req, struct ldb_reply *ares)
273 struct extended_search_context *ac;
274 struct ldb_request *down_req;
275 struct ldb_control **saved_controls;
276 struct ldb_message_element *el;
281 const char *found = NULL;
283 ac = talloc_get_type(req->context, struct extended_search_context);
286 return ldb_module_done(ac->req, NULL, NULL,
287 LDB_ERR_OPERATIONS_ERROR);
289 if (ares->error != LDB_SUCCESS) {
290 return ldb_module_done(ac->req, ares->controls,
291 ares->response, ares->error);
294 switch (ares->type) {
295 case LDB_REPLY_ENTRY:
296 if (!ac->wellknown_object) {
297 ac->basedn = ares->message->dn;
301 wkn_len = strlen(ac->wellknown_object);
303 el = ldb_msg_find_element(ares->message, "wellKnownObjects");
309 for (i=0; i < el->num_values; i++) {
310 valstr = talloc_strndup(ac,
311 (const char *)el->values[i].data,
312 el->values[i].length);
314 ldb_oom(ac->module->ldb);
315 return ldb_module_done(ac->req, NULL, NULL,
316 LDB_ERR_OPERATIONS_ERROR);
319 if (strncasecmp(valstr, ac->wellknown_object, wkn_len) != 0) {
324 found = &valstr[wkn_len];
332 ac->basedn = ldb_dn_new(ac, ac->module->ldb, found);
335 ldb_oom(ac->module->ldb);
336 return ldb_module_done(ac->req, NULL, NULL,
337 LDB_ERR_OPERATIONS_ERROR);
342 case LDB_REPLY_REFERRAL:
348 const char *str = talloc_asprintf(req, "Base-DN '%s' not found",
349 ldb_dn_get_linearized(ac->req->op.search.base));
350 ldb_set_errstring(ac->module->ldb, str);
351 return ldb_module_done(ac->req, NULL, NULL,
352 LDB_ERR_NO_SUCH_OBJECT);
355 ret = ldb_build_search_req_ex(&down_req,
358 ac->req->op.search.scope,
359 ac->req->op.search.tree,
362 ac, extended_callback,
364 if (ret != LDB_SUCCESS) {
365 return ldb_module_done(ac->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
369 /* save it locally and remove it from the list */
370 /* we do not need to replace them later as we
371 * are keeping the original req intact */
372 if (!save_controls(ac->control, down_req, &saved_controls)) {
373 return ldb_module_done(ac->req, NULL, NULL,
374 LDB_ERR_OPERATIONS_ERROR);
378 /* perform the search */
379 return ldb_next_request(ac->module, down_req);
384 static int extended_dn_search(struct ldb_module *module, struct ldb_request *req)
386 struct ldb_control *control;
387 struct ldb_extended_dn_control *extended_ctrl = NULL;
388 struct ldb_control **saved_controls;
389 struct extended_search_context *ac;
390 struct ldb_request *down_req;
393 struct ldb_dn *base_dn = NULL;
394 enum ldb_scope base_dn_scope = LDB_SCOPE_BASE;
395 const char *base_dn_filter = NULL;
396 const char * const *base_dn_attrs = NULL;
397 char *wellknown_object = NULL;
398 static const char *dnattr[] = {
402 static const char *wkattr[] = {
407 if (ldb_dn_has_extended(req->op.search.base)) {
408 const struct ldb_val *sid_val, *guid_val, *wkguid_val;
409 struct ldb_dn *dn = req->op.search.base;
411 sid_val = ldb_dn_get_extended_component(dn, "SID");
412 guid_val = ldb_dn_get_extended_component(dn, "GUID");
413 wkguid_val = ldb_dn_get_extended_component(dn, "WKGUID");
416 /* TODO: do a search over all partitions */
417 base_dn = ldb_get_default_basedn(module->ldb);
418 base_dn_filter = talloc_asprintf(req, "(objectSid=%s)",
419 ldb_binary_encode(req, *sid_val));
420 if (!base_dn_filter) {
421 ldb_oom(module->ldb);
422 return LDB_ERR_OPERATIONS_ERROR;
424 base_dn_scope = LDB_SCOPE_SUBTREE;
425 base_dn_attrs = dnattr;
427 } else if (guid_val) {
429 /* TODO: do a search over all partitions */
430 base_dn = ldb_get_default_basedn(module->ldb);
431 base_dn_filter = talloc_asprintf(req, "(objectGUID=%s)",
432 ldb_binary_encode(req, *guid_val));
433 if (!base_dn_filter) {
434 ldb_oom(module->ldb);
435 return LDB_ERR_OPERATIONS_ERROR;
437 base_dn_scope = LDB_SCOPE_SUBTREE;
438 base_dn_attrs = dnattr;
441 } else if (wkguid_val) {
446 wkguid_dup = talloc_strndup(req, (char *)wkguid_val->data, wkguid_val->length);
448 p = strchr(wkguid_dup, ',');
450 return LDB_ERR_INVALID_DN_SYNTAX;
456 wellknown_object = talloc_asprintf(req, "B:32:%s:", wkguid_dup);
457 if (!wellknown_object) {
458 ldb_oom(module->ldb);
459 return LDB_ERR_OPERATIONS_ERROR;
463 p = strchr(tail_str, '>');
465 return LDB_ERR_INVALID_DN_SYNTAX;
469 base_dn = ldb_dn_new(req, module->ldb, tail_str);
470 talloc_free(wkguid_dup);
472 ldb_oom(module->ldb);
473 return LDB_ERR_OPERATIONS_ERROR;
475 base_dn_filter = talloc_strdup(req, "(objectClass=*)");
476 if (!base_dn_filter) {
477 ldb_oom(module->ldb);
478 return LDB_ERR_OPERATIONS_ERROR;
480 base_dn_scope = LDB_SCOPE_BASE;
481 base_dn_attrs = wkattr;
485 /* check if there's an extended dn control */
486 control = ldb_request_get_control(req, LDB_CONTROL_EXTENDED_DN_OID);
487 if (control && control->data) {
488 extended_ctrl = talloc_get_type(control->data, struct ldb_extended_dn_control);
489 if (!extended_ctrl) {
490 return LDB_ERR_PROTOCOL_ERROR;
494 ac = talloc_zero(req, struct extended_search_context);
496 ldb_oom(module->ldb);
497 return LDB_ERR_OPERATIONS_ERROR;
501 ac->schema = dsdb_get_schema(module->ldb);
503 ac->control = control;
505 ac->wellknown_object = wellknown_object;
507 ac->remove_guid = false;
508 ac->remove_sid = false;
511 /* no schema yet? go on */
513 return ldb_next_request(module, req);
519 ac->extended_type = extended_ctrl->type;
521 ac->extended_type = 0;
524 /* check if attrs only is specified, in that case check wether we need to modify them */
525 if (req->op.search.attrs) {
526 if (! is_attr_in_list(req->op.search.attrs, "objectGUID")) {
527 ac->remove_guid = true;
529 if (! is_attr_in_list(req->op.search.attrs, "objectSID")) {
530 ac->remove_sid = true;
532 if (ac->remove_guid || ac->remove_sid) {
533 new_attrs = copy_attrs(ac, req->op.search.attrs);
534 if (new_attrs == NULL) {
535 ldb_oom(module->ldb);
536 return LDB_ERR_OPERATIONS_ERROR;
539 if (ac->remove_guid) {
540 if (!add_attrs(ac, &new_attrs, "objectGUID"))
541 return LDB_ERR_OPERATIONS_ERROR;
543 if (ac->remove_sid) {
544 if (!add_attrs(ac, &new_attrs, "objectSID"))
545 return LDB_ERR_OPERATIONS_ERROR;
547 ac->cast_attrs = (const char * const *)new_attrs;
549 ac->cast_attrs = req->op.search.attrs;
554 /* If the base DN was an extended DN (perhaps a well known
555 * GUID) then re-create the search */
557 ret = ldb_build_search_req(&down_req,
564 ac, extended_base_callback,
566 if (ret != LDB_SUCCESS) {
567 return LDB_ERR_OPERATIONS_ERROR;
570 /* perform the search */
571 return ldb_next_request(module, down_req);
574 ret = ldb_build_search_req_ex(&down_req,
577 req->op.search.scope,
581 ac, extended_callback,
583 if (ret != LDB_SUCCESS) {
584 return LDB_ERR_OPERATIONS_ERROR;
588 /* save it locally and remove it from the list */
589 /* we do not need to replace them later as we
590 * are keeping the original req intact */
591 if (!save_controls(control, down_req, &saved_controls)) {
592 return LDB_ERR_OPERATIONS_ERROR;
596 /* perform the search */
597 return ldb_next_request(module, down_req);
600 static int extended_replace_dn(struct ldb_request *req, struct ldb_reply *ares)
602 struct extended_dn_replace_list *os = talloc_get_type(req->context,
603 struct extended_dn_replace_list);
606 return ldb_module_done(os->ac->req, NULL, NULL,
607 LDB_ERR_OPERATIONS_ERROR);
609 if (ares->error != LDB_SUCCESS) {
610 return ldb_module_done(os->ac->req, ares->controls,
611 ares->response, ares->error);
614 /* Only entries are interesting, and we only want the olddn */
615 switch (ares->type) {
616 case LDB_REPLY_ENTRY:
618 const struct ldb_val *sid = ldb_msg_find_ldb_val(ares->message, "objectSid");
619 const struct ldb_val *guid = ldb_msg_find_ldb_val(ares->message, "objectGUID");
620 struct ldb_dn *dn = ares->message->dn;
621 int ret = ldb_dn_compare(dn, req->op.search.base);
623 /* Guh? We only asked for this DN */
625 return ldb_module_done(os->ac->req, NULL, NULL,
626 LDB_ERR_OPERATIONS_ERROR);
629 ret = ldb_dn_set_extended_component(dn, "GUID", guid);
630 if (ret != LDB_SUCCESS) {
634 ret = ldb_dn_set_extended_component(dn, "SID", sid);
635 if (ret != LDB_SUCCESS) {
640 *os->replace_dn = data_blob_string_const(
641 ldb_dn_extended_linearized(os->mem_ctx,
643 if (os->replace_dn->data != NULL) {
644 return LDB_ERR_OPERATIONS_ERROR;
647 case LDB_REPLY_REFERRAL:
655 /* Run the next search */
658 struct extended_dn_replace_list *next;
665 return ldb_next_request(os->ac->module, next->search_req);
667 /* Otherwise, we are done - let's run the
668 * request now we have swapped the DNs for the
670 return ldb_next_request(os->ac->module, os->ac->req);
678 /* We have a 'normal' DN in the inbound request. We need to find out
679 * what the GUID and SID are on the DN it points to, so we can
680 * construct an extended DN for storage.
682 * This creates a list of DNs to look up, and the plain DN to replace
685 static int extended_store_replace(struct extended_dn_context *ac,
686 TALLOC_CTX *callback_mem_ctx,
687 struct ldb_val *plain_dn)
690 struct extended_dn_replace_list *os;
691 static const char *attrs[] = {
697 struct ldb_dn *dn = ldb_dn_from_ldb_val(ac, ac->module->ldb, plain_dn);
698 if (!dn || !ldb_dn_validate(dn)) {
699 ldb_asprintf_errstring(ac->module->ldb,
700 "could not parse %.*s as a DN", (int)plain_dn->length, plain_dn->data);
701 return LDB_ERR_INVALID_DN_SYNTAX;
704 os = talloc_zero(ac, struct extended_dn_replace_list);
706 return LDB_ERR_OPERATIONS_ERROR;
711 os->mem_ctx = callback_mem_ctx;
713 os->dn = talloc_steal(os, dn);
715 os->replace_dn = plain_dn;
718 return LDB_ERR_OPERATIONS_ERROR;
721 ret = ldb_build_search_req(&os->search_req,
722 ac->module->ldb, ac->ops, dn, LDB_SCOPE_BASE, NULL,
723 attrs, NULL, os, extended_replace_dn,
738 static int extended_dn_add(struct ldb_module *module, struct ldb_request *req)
740 struct extended_dn_context *ac;
744 if (ldb_dn_is_special(req->op.add.message->dn)) {
745 /* do not manipulate our control entries */
746 return ldb_next_request(module, req);
749 ac = extended_dn_context_init(module, req);
751 return LDB_ERR_OPERATIONS_ERROR;
755 /* without schema, this doesn't make any sense */
757 return ldb_next_request(module, req);
760 for (i=0; i < req->op.add.message->num_elements; i++) {
761 const struct ldb_message_element *el = &req->op.add.message->elements[i];
762 const struct dsdb_attribute *schema_attr
763 = dsdb_attribute_by_lDAPDisplayName(ac->schema, el->name);
765 ldb_asprintf_errstring(module->ldb,
766 "attribute %s is not a valid attribute in schema", el->name);
767 return LDB_ERR_OBJECT_CLASS_VIOLATION;
770 /* We only setup an extended DN GUID on these particular DN objects */
771 if (!((strcmp(schema_attr->attributeSyntax_oid, "2.5.5.1") == 0) ||
772 (strcmp(schema_attr->attributeSyntax_oid, "2.5.5.7") == 0))) {
776 for (j = 0; j < el->num_values; j++) {
777 ret = extended_store_replace(ac, req->op.add.message->elements, &el->values[j]);
778 if (ret != LDB_SUCCESS) {
784 /* if DNs were set continue */
785 if (ac->ops == NULL) {
787 return ldb_next_request(module, req);
790 /* start with the searches */
791 return ldb_next_request(module, ac->ops->search_req);
795 static int extended_dn_modify(struct ldb_module *module, struct ldb_request *req)
797 /* Look over list of modifications */
798 /* Find if any are for linked attributes */
799 /* Determine the effect of the modification */
800 /* Apply the modify to the linked entry */
803 struct extended_dn_context *ac;
806 if (ldb_dn_is_special(req->op.mod.message->dn)) {
807 /* do not manipulate our control entries */
808 return ldb_next_request(module, req);
811 ac = extended_dn_context_init(module, req);
813 return LDB_ERR_OPERATIONS_ERROR;
817 /* without schema, this doesn't make any sense */
818 return ldb_next_request(module, req);
821 for (i=0; i < req->op.mod.message->num_elements; i++) {
822 const struct ldb_message_element *el = &req->op.mod.message->elements[i];
823 const struct dsdb_attribute *schema_attr
824 = dsdb_attribute_by_lDAPDisplayName(ac->schema, el->name);
826 ldb_asprintf_errstring(module->ldb,
827 "attribute %s is not a valid attribute in schema", el->name);
828 return LDB_ERR_OBJECT_CLASS_VIOLATION;
831 /* We only setup an extended DN GUID on these particular DN objects */
832 if (strcmp(schema_attr->attributeSyntax_oid, LDB_SYNTAX_DN) != 0 &&
833 strcmp(schema_attr->attributeSyntax_oid, "1.2.840.113556.1.4.903") != 0) {
837 switch (el->flags & LDB_FLAG_MOD_MASK) {
838 case LDB_FLAG_MOD_REPLACE:
839 case LDB_FLAG_MOD_ADD:
841 /* For each value being added, we need to setup the lookups to fill in the extended DN */
842 for (j = 0; j < el->num_values; j++) {
843 struct ldb_dn *dn = ldb_dn_from_ldb_val(ac, module->ldb, &el->values[j]);
844 if (!dn || !ldb_dn_validate(dn)) {
845 ldb_asprintf_errstring(module->ldb,
846 "could not parse attribute %s as a DN", el->name);
847 return LDB_ERR_INVALID_DN_SYNTAX;
850 ret = extended_store_replace(ac, req->op.mod.message->elements, &el->values[j]);
851 if (ret != LDB_SUCCESS) {
863 static int extended_dn_init(struct ldb_module *module)
867 ret = ldb_mod_register_control(module, LDB_CONTROL_EXTENDED_DN_OID);
868 if (ret != LDB_SUCCESS) {
869 ldb_debug(module->ldb, LDB_DEBUG_ERROR,
870 "extended_dn: Unable to register control with rootdse!\n");
871 return LDB_ERR_OPERATIONS_ERROR;
874 return ldb_next_init(module);
877 _PUBLIC_ const struct ldb_module_ops ldb_extended_dn_module_ops = {
878 .name = "extended_dn",
879 .search = extended_dn_search,
880 .init_context = extended_dn_init,
881 .add = extended_dn_add,
882 .modify = extended_dn_modify,