ldb_map: Fix CID 1034791 Dereference null return value
[Samba.git] / lib / ldb / ldb_map / ldb_map.c
blobce2d660c87eeb5f2ef6243c2f5fcfa0dd4114add
1 /*
2 ldb database mapping module
4 Copyright (C) Jelmer Vernooij 2005
5 Copyright (C) Martin Kuehl <mkhl@samba.org> 2006
6 Copyright (C) Simo Sorce 2008
8 ** NOTE! The following LGPL license applies to the ldb
9 ** library. This does NOT imply that all of Samba is released
10 ** under the LGPL
12 This library is free software; you can redistribute it and/or
13 modify it under the terms of the GNU Lesser General Public
14 License as published by the Free Software Foundation; either
15 version 3 of the License, or (at your option) any later version.
17 This library is distributed in the hope that it will be useful,
18 but WITHOUT ANY WARRANTY; without even the implied warranty of
19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 Lesser General Public License for more details.
22 You should have received a copy of the GNU Lesser General Public
23 License along with this library; if not, see <http://www.gnu.org/licenses/>.
27 /*
28 * Name: ldb
30 * Component: ldb ldb_map module
32 * Description: Map portions of data into a different format on a
33 * remote partition.
35 * Author: Jelmer Vernooij, Martin Kuehl
38 #include "replace.h"
39 #include "system/filesys.h"
40 #include "system/time.h"
41 #include "ldb_map.h"
42 #include "ldb_map_private.h"
44 #ifndef _PUBLIC_
45 #define _PUBLIC_
46 #endif
48 /* Description of the provided ldb requests:
49 - special attribute 'isMapped'
51 - search:
52 - if parse tree can be split
53 - search remote records w/ remote attrs and parse tree
54 - otherwise
55 - enumerate all remote records
56 - for each remote result
57 - map remote result to local message
58 - search local result
59 - is present
60 - merge local into remote result
61 - run callback on merged result
62 - otherwise
63 - run callback on remote result
65 - add:
66 - split message into local and remote part
67 - if local message is not empty
68 - add isMapped to local message
69 - add local message
70 - add remote message
72 - modify:
73 - split message into local and remote part
74 - if local message is not empty
75 - add isMapped to local message
76 - search for local record
77 - if present
78 - modify local record
79 - otherwise
80 - add local message
81 - modify remote record
83 - delete:
84 - search for local record
85 - if present
86 - delete local record
87 - delete remote record
89 - rename:
90 - search for local record
91 - if present
92 - rename local record
93 - modify local isMapped
94 - rename remote record
99 /* Private data structures
100 * ======================= */
102 /* Global private data */
103 /* Extract mappings from private data. */
104 const struct ldb_map_context *map_get_context(struct ldb_module *module)
106 const struct map_private *data = talloc_get_type(ldb_module_get_private(module), struct map_private);
107 return data->context;
110 /* Create a generic request context. */
111 struct map_context *map_init_context(struct ldb_module *module,
112 struct ldb_request *req)
114 struct ldb_context *ldb;
115 struct map_context *ac;
117 ldb = ldb_module_get_ctx(module);
119 ac = talloc_zero(req, struct map_context);
120 if (ac == NULL) {
121 ldb_set_errstring(ldb, "Out of Memory");
122 return NULL;
125 ac->module = module;
126 ac->req = req;
128 return ac;
131 /* Dealing with DNs for different partitions
132 * ========================================= */
134 /* Check whether any data should be stored in the local partition. */
135 bool map_check_local_db(struct ldb_module *module)
137 const struct ldb_map_context *data = map_get_context(module);
139 if (!data->remote_base_dn || !data->local_base_dn) {
140 return false;
143 return true;
146 /* Copy a DN with the base DN of the local partition. */
147 static struct ldb_dn *ldb_dn_rebase_local(void *mem_ctx, const struct ldb_map_context *data, struct ldb_dn *dn)
149 struct ldb_dn *new_dn;
151 new_dn = ldb_dn_copy(mem_ctx, dn);
152 if ( ! ldb_dn_validate(new_dn)) {
153 talloc_free(new_dn);
154 return NULL;
157 /* may be we don't need to rebase at all */
158 if ( ! data->remote_base_dn || ! data->local_base_dn) {
159 return new_dn;
162 if ( ! ldb_dn_remove_base_components(new_dn, ldb_dn_get_comp_num(data->remote_base_dn))) {
163 talloc_free(new_dn);
164 return NULL;
167 if ( ! ldb_dn_add_base(new_dn, data->local_base_dn)) {
168 talloc_free(new_dn);
169 return NULL;
172 return new_dn;
175 /* Copy a DN with the base DN of the remote partition. */
176 static struct ldb_dn *ldb_dn_rebase_remote(void *mem_ctx, const struct ldb_map_context *data, struct ldb_dn *dn)
178 struct ldb_dn *new_dn;
180 new_dn = ldb_dn_copy(mem_ctx, dn);
181 if ( ! ldb_dn_validate(new_dn)) {
182 talloc_free(new_dn);
183 return NULL;
186 /* may be we don't need to rebase at all */
187 if ( ! data->remote_base_dn || ! data->local_base_dn) {
188 return new_dn;
191 if ( ! ldb_dn_remove_base_components(new_dn, ldb_dn_get_comp_num(data->local_base_dn))) {
192 talloc_free(new_dn);
193 return NULL;
196 if ( ! ldb_dn_add_base(new_dn, data->remote_base_dn)) {
197 talloc_free(new_dn);
198 return NULL;
201 return new_dn;
204 /* Run a request and make sure it targets the remote partition. */
205 /* TODO: free old DNs and messages? */
206 int ldb_next_remote_request(struct ldb_module *module, struct ldb_request *request)
208 const struct ldb_map_context *data = map_get_context(module);
209 struct ldb_context *ldb;
210 struct ldb_message *msg;
212 ldb = ldb_module_get_ctx(module);
214 switch (request->operation) {
215 case LDB_SEARCH:
216 if (request->op.search.base) {
217 request->op.search.base = ldb_dn_rebase_remote(request, data, request->op.search.base);
218 } else {
219 request->op.search.base = data->remote_base_dn;
220 /* TODO: adjust scope? */
222 break;
224 case LDB_ADD:
225 msg = ldb_msg_copy_shallow(request, request->op.add.message);
226 if (msg == NULL) {
227 return LDB_ERR_OPERATIONS_ERROR;
229 msg->dn = ldb_dn_rebase_remote(msg, data, msg->dn);
230 request->op.add.message = msg;
231 break;
233 case LDB_MODIFY:
234 msg = ldb_msg_copy_shallow(request, request->op.mod.message);
235 if (msg == NULL) {
236 return LDB_ERR_OPERATIONS_ERROR;
238 msg->dn = ldb_dn_rebase_remote(msg, data, msg->dn);
239 request->op.mod.message = msg;
240 break;
242 case LDB_DELETE:
243 request->op.del.dn = ldb_dn_rebase_remote(request, data, request->op.del.dn);
244 break;
246 case LDB_RENAME:
247 request->op.rename.olddn = ldb_dn_rebase_remote(request, data, request->op.rename.olddn);
248 request->op.rename.newdn = ldb_dn_rebase_remote(request, data, request->op.rename.newdn);
249 break;
251 default:
252 ldb_debug(ldb, LDB_DEBUG_ERROR, "ldb_map: "
253 "Invalid remote request!");
254 return LDB_ERR_OPERATIONS_ERROR;
257 return ldb_next_request(module, request);
261 /* Finding mappings for attributes and objectClasses
262 * ================================================= */
264 /* Find an objectClass mapping by the local name. */
265 static const struct ldb_map_objectclass *map_objectclass_find_local(const struct ldb_map_context *data, const char *name)
267 unsigned int i;
269 for (i = 0; data->objectclass_maps && data->objectclass_maps[i].local_name; i++) {
270 if (ldb_attr_cmp(data->objectclass_maps[i].local_name, name) == 0) {
271 return &data->objectclass_maps[i];
275 return NULL;
278 /* Find an objectClass mapping by the remote name. */
279 static const struct ldb_map_objectclass *map_objectclass_find_remote(const struct ldb_map_context *data, const char *name)
281 unsigned int i;
283 for (i = 0; data->objectclass_maps && data->objectclass_maps[i].remote_name; i++) {
284 if (ldb_attr_cmp(data->objectclass_maps[i].remote_name, name) == 0) {
285 return &data->objectclass_maps[i];
289 return NULL;
292 /* Find an attribute mapping by the local name. */
293 const struct ldb_map_attribute *map_attr_find_local(const struct ldb_map_context *data, const char *name)
295 unsigned int i;
297 for (i = 0; data->attribute_maps[i].local_name; i++) {
298 if (ldb_attr_cmp(data->attribute_maps[i].local_name, name) == 0) {
299 return &data->attribute_maps[i];
302 for (i = 0; data->attribute_maps[i].local_name; i++) {
303 if (ldb_attr_cmp(data->attribute_maps[i].local_name, "*") == 0) {
304 return &data->attribute_maps[i];
308 return NULL;
311 /* Find an attribute mapping by the remote name. */
312 const struct ldb_map_attribute *map_attr_find_remote(const struct ldb_map_context *data, const char *name)
314 const struct ldb_map_attribute *map;
315 const struct ldb_map_attribute *wildcard = NULL;
316 unsigned int i, j;
318 for (i = 0; data->attribute_maps[i].local_name; i++) {
319 map = &data->attribute_maps[i];
320 if (ldb_attr_cmp(map->local_name, "*") == 0) {
321 wildcard = &data->attribute_maps[i];
324 switch (map->type) {
325 case LDB_MAP_IGNORE:
326 break;
328 case LDB_MAP_KEEP:
329 if (ldb_attr_cmp(map->local_name, name) == 0) {
330 return map;
332 break;
334 case LDB_MAP_RENAME:
335 case LDB_MAP_CONVERT:
336 if (ldb_attr_cmp(map->u.rename.remote_name, name) == 0) {
337 return map;
339 break;
341 case LDB_MAP_GENERATE:
342 for (j = 0; map->u.generate.remote_names && map->u.generate.remote_names[j]; j++) {
343 if (ldb_attr_cmp(map->u.generate.remote_names[j], name) == 0) {
344 return map;
347 break;
351 /* We didn't find it, so return the wildcard record if one was configured */
352 return wildcard;
356 /* Mapping attributes
357 * ================== */
359 /* Check whether an attribute will be mapped into the remote partition. */
360 bool map_attr_check_remote(const struct ldb_map_context *data, const char *attr)
362 const struct ldb_map_attribute *map = map_attr_find_local(data, attr);
364 if (map == NULL) {
365 return false;
367 if (map->type == LDB_MAP_IGNORE) {
368 return false;
371 return true;
374 /* Map an attribute name into the remote partition. */
375 const char *map_attr_map_local(void *mem_ctx, const struct ldb_map_attribute *map, const char *attr)
377 if (map == NULL) {
378 return talloc_strdup(mem_ctx, attr);
381 switch (map->type) {
382 case LDB_MAP_KEEP:
383 return talloc_strdup(mem_ctx, attr);
385 case LDB_MAP_RENAME:
386 case LDB_MAP_CONVERT:
387 return talloc_strdup(mem_ctx, map->u.rename.remote_name);
389 default:
390 return NULL;
394 /* Map an attribute name back into the local partition. */
395 const char *map_attr_map_remote(void *mem_ctx, const struct ldb_map_attribute *map, const char *attr)
397 if (map == NULL) {
398 return talloc_strdup(mem_ctx, attr);
401 if (map->type == LDB_MAP_KEEP) {
402 return talloc_strdup(mem_ctx, attr);
405 return talloc_strdup(mem_ctx, map->local_name);
409 /* Merge two lists of attributes into a single one. */
410 int map_attrs_merge(struct ldb_module *module, void *mem_ctx,
411 const char ***attrs, const char * const *more_attrs)
413 unsigned int i, j, k;
415 for (i = 0; *attrs && (*attrs)[i]; i++) /* noop */ ;
416 for (j = 0; more_attrs && more_attrs[j]; j++) /* noop */ ;
418 *attrs = talloc_realloc(mem_ctx, *attrs, const char *, i+j+1);
419 if (*attrs == NULL) {
420 map_oom(module);
421 return -1;
424 for (k = 0; k < j; k++) {
425 (*attrs)[i + k] = more_attrs[k];
428 (*attrs)[i+k] = NULL;
430 return 0;
433 /* Mapping ldb values
434 * ================== */
436 /* Map an ldb value into the remote partition. */
437 struct ldb_val ldb_val_map_local(struct ldb_module *module, void *mem_ctx,
438 const struct ldb_map_attribute *map, const struct ldb_val *val)
440 if (map && (map->type == LDB_MAP_CONVERT) && (map->u.convert.convert_local)) {
441 return map->u.convert.convert_local(module, mem_ctx, val);
444 return ldb_val_dup(mem_ctx, val);
447 /* Map an ldb value back into the local partition. */
448 struct ldb_val ldb_val_map_remote(struct ldb_module *module, void *mem_ctx,
449 const struct ldb_map_attribute *map, const struct ldb_val *val)
451 if (map && (map->type == LDB_MAP_CONVERT) && (map->u.convert.convert_remote)) {
452 return map->u.convert.convert_remote(module, mem_ctx, val);
455 return ldb_val_dup(mem_ctx, val);
459 /* Mapping DNs
460 * =========== */
462 /* Check whether a DN is below the local baseDN. */
463 bool ldb_dn_check_local(struct ldb_module *module, struct ldb_dn *dn)
465 const struct ldb_map_context *data = map_get_context(module);
467 if (!data->local_base_dn) {
468 return true;
471 return ldb_dn_compare_base(data->local_base_dn, dn) == 0;
474 /* Map a DN into the remote partition. */
475 struct ldb_dn *ldb_dn_map_local(struct ldb_module *module, void *mem_ctx, struct ldb_dn *dn)
477 const struct ldb_map_context *data = map_get_context(module);
478 struct ldb_context *ldb;
479 struct ldb_dn *newdn;
480 const struct ldb_map_attribute *map;
481 enum ldb_map_attr_type map_type;
482 const char *name;
483 struct ldb_val value;
484 int i, ret;
486 if (dn == NULL) {
487 return NULL;
490 ldb = ldb_module_get_ctx(module);
492 newdn = ldb_dn_copy(mem_ctx, dn);
493 if (newdn == NULL) {
494 map_oom(module);
495 return NULL;
498 /* For each RDN, map the component name and possibly the value */
499 for (i = 0; i < ldb_dn_get_comp_num(newdn); i++) {
500 map = map_attr_find_local(data, ldb_dn_get_component_name(dn, i));
502 /* Unknown attribute - leave this RDN as is and hope the best... */
503 if (map == NULL) {
504 map_type = LDB_MAP_KEEP;
505 } else {
506 map_type = map->type;
509 switch (map_type) {
510 case LDB_MAP_IGNORE:
511 case LDB_MAP_GENERATE:
512 ldb_debug(ldb, LDB_DEBUG_ERROR, "ldb_map: "
513 "LDB_MAP_IGNORE/LDB_MAP_GENERATE attribute '%s' "
514 "used in DN!", ldb_dn_get_component_name(dn, i));
515 goto failed;
517 case LDB_MAP_CONVERT:
518 if (map->u.convert.convert_local == NULL) {
519 ldb_debug(ldb, LDB_DEBUG_ERROR, "ldb_map: "
520 "'convert_local' not set for attribute '%s' "
521 "used in DN!", ldb_dn_get_component_name(dn, i));
522 goto failed;
524 /* fall through */
525 case LDB_MAP_KEEP:
526 case LDB_MAP_RENAME:
527 name = map_attr_map_local(newdn, map, ldb_dn_get_component_name(dn, i));
528 if (name == NULL) goto failed;
530 value = ldb_val_map_local(module, newdn, map, ldb_dn_get_component_val(dn, i));
531 if (value.data == NULL) goto failed;
533 ret = ldb_dn_set_component(newdn, i, name, value);
534 if (ret != LDB_SUCCESS) {
535 goto failed;
538 break;
542 return newdn;
544 failed:
545 talloc_free(newdn);
546 return NULL;
549 /* Map a DN into the local partition. */
550 struct ldb_dn *ldb_dn_map_remote(struct ldb_module *module, void *mem_ctx, struct ldb_dn *dn)
552 const struct ldb_map_context *data = map_get_context(module);
553 struct ldb_context *ldb;
554 struct ldb_dn *newdn;
555 const struct ldb_map_attribute *map;
556 enum ldb_map_attr_type map_type;
557 const char *name;
558 struct ldb_val value;
559 int i, ret;
561 if (dn == NULL) {
562 return NULL;
565 ldb = ldb_module_get_ctx(module);
567 newdn = ldb_dn_copy(mem_ctx, dn);
568 if (newdn == NULL) {
569 map_oom(module);
570 return NULL;
573 /* For each RDN, map the component name and possibly the value */
574 for (i = 0; i < ldb_dn_get_comp_num(newdn); i++) {
575 map = map_attr_find_remote(data, ldb_dn_get_component_name(dn, i));
577 /* Unknown attribute - leave this RDN as is and hope the best... */
578 if (map == NULL) {
579 map_type = LDB_MAP_KEEP;
580 } else {
581 map_type = map->type;
584 switch (map_type) {
585 case LDB_MAP_IGNORE:
586 case LDB_MAP_GENERATE:
587 ldb_debug(ldb, LDB_DEBUG_ERROR, "ldb_map: "
588 "LDB_MAP_IGNORE/LDB_MAP_GENERATE attribute '%s' "
589 "used in DN!", ldb_dn_get_component_name(dn, i));
590 goto failed;
592 case LDB_MAP_CONVERT:
593 if (map->u.convert.convert_remote == NULL) {
594 ldb_debug(ldb, LDB_DEBUG_ERROR, "ldb_map: "
595 "'convert_remote' not set for attribute '%s' "
596 "used in DN!", ldb_dn_get_component_name(dn, i));
597 goto failed;
599 /* fall through */
600 case LDB_MAP_KEEP:
601 case LDB_MAP_RENAME:
602 name = map_attr_map_remote(newdn, map, ldb_dn_get_component_name(dn, i));
603 if (name == NULL) goto failed;
605 value = ldb_val_map_remote(module, newdn, map, ldb_dn_get_component_val(dn, i));
606 if (value.data == NULL) goto failed;
608 ret = ldb_dn_set_component(newdn, i, name, value);
609 if (ret != LDB_SUCCESS) {
610 goto failed;
613 break;
617 return newdn;
619 failed:
620 talloc_free(newdn);
621 return NULL;
624 /* Map a DN and its base into the local partition. */
625 /* TODO: This should not be required with GUIDs. */
626 struct ldb_dn *ldb_dn_map_rebase_remote(struct ldb_module *module, void *mem_ctx, struct ldb_dn *dn)
628 const struct ldb_map_context *data = map_get_context(module);
629 struct ldb_dn *dn1, *dn2;
631 dn1 = ldb_dn_rebase_local(mem_ctx, data, dn);
632 dn2 = ldb_dn_map_remote(module, mem_ctx, dn1);
634 talloc_free(dn1);
635 return dn2;
639 /* Converting DNs and objectClasses (as ldb values)
640 * ================================================ */
642 /* Map a DN contained in an ldb value into the remote partition. */
643 static struct ldb_val ldb_dn_convert_local(struct ldb_module *module, void *mem_ctx, const struct ldb_val *val)
645 struct ldb_context *ldb;
646 struct ldb_dn *dn, *newdn;
647 struct ldb_val newval;
649 ldb = ldb_module_get_ctx(module);
651 dn = ldb_dn_from_ldb_val(mem_ctx, ldb, val);
652 if (! ldb_dn_validate(dn)) {
653 newval.length = 0;
654 newval.data = NULL;
655 talloc_free(dn);
656 return newval;
658 newdn = ldb_dn_map_local(module, mem_ctx, dn);
659 talloc_free(dn);
661 newval.length = 0;
662 newval.data = (uint8_t *)ldb_dn_alloc_linearized(mem_ctx, newdn);
663 if (newval.data) {
664 newval.length = strlen((char *)newval.data);
666 talloc_free(newdn);
668 return newval;
671 /* Map a DN contained in an ldb value into the local partition. */
672 static struct ldb_val ldb_dn_convert_remote(struct ldb_module *module, void *mem_ctx, const struct ldb_val *val)
674 struct ldb_context *ldb;
675 struct ldb_dn *dn, *newdn;
676 struct ldb_val newval;
678 ldb = ldb_module_get_ctx(module);
680 dn = ldb_dn_from_ldb_val(mem_ctx, ldb, val);
681 if (! ldb_dn_validate(dn)) {
682 newval.length = 0;
683 newval.data = NULL;
684 talloc_free(dn);
685 return newval;
687 newdn = ldb_dn_map_remote(module, mem_ctx, dn);
688 talloc_free(dn);
690 newval.length = 0;
691 newval.data = (uint8_t *)ldb_dn_alloc_linearized(mem_ctx, newdn);
692 if (newval.data) {
693 newval.length = strlen((char *)newval.data);
695 talloc_free(newdn);
697 return newval;
700 /* Map an objectClass into the remote partition. */
701 static struct ldb_val map_objectclass_convert_local(struct ldb_module *module, void *mem_ctx, const struct ldb_val *val)
703 const struct ldb_map_context *data = map_get_context(module);
704 const char *name = (char *)val->data;
705 const struct ldb_map_objectclass *map = map_objectclass_find_local(data, name);
706 struct ldb_val newval;
708 if (map) {
709 newval.data = (uint8_t*)talloc_strdup(mem_ctx, map->remote_name);
710 newval.length = strlen((char *)newval.data);
711 return newval;
714 return ldb_val_dup(mem_ctx, val);
717 /* Generate a remote message with a mapped objectClass. */
718 static void map_objectclass_generate_remote(struct ldb_module *module, const char *local_attr, const struct ldb_message *old, struct ldb_message *remote, struct ldb_message *local)
720 const struct ldb_map_context *data = map_get_context(module);
721 struct ldb_context *ldb;
722 struct ldb_message_element *el, *oc;
723 struct ldb_val val;
724 bool found_extensibleObject = false;
725 unsigned int i;
727 ldb = ldb_module_get_ctx(module);
729 /* Find old local objectClass */
730 oc = ldb_msg_find_element(old, "objectClass");
731 if (oc == NULL) {
732 return;
735 /* Prepare new element */
736 el = talloc_zero(remote, struct ldb_message_element);
737 if (el == NULL) {
738 ldb_oom(ldb);
739 return; /* TODO: fail? */
742 /* Copy local objectClass element, reverse space for an extra value */
743 el->num_values = oc->num_values + 1;
744 el->values = talloc_array(el, struct ldb_val, el->num_values);
745 if (el->values == NULL) {
746 talloc_free(el);
747 ldb_oom(ldb);
748 return; /* TODO: fail? */
751 /* Copy local element name "objectClass" */
752 el->name = talloc_strdup(el, local_attr);
754 /* Convert all local objectClasses */
755 for (i = 0; i < el->num_values - 1; i++) {
756 el->values[i] = map_objectclass_convert_local(module, el->values, &oc->values[i]);
757 if (ldb_attr_cmp((char *)el->values[i].data, data->add_objectclass) == 0) {
758 found_extensibleObject = true;
762 if (!found_extensibleObject) {
763 val.data = (uint8_t *)talloc_strdup(el->values, data->add_objectclass);
764 val.length = strlen((char *)val.data);
766 /* Append additional objectClass data->add_objectclass */
767 el->values[i] = val;
768 } else {
769 el->num_values--;
772 /* Add new objectClass to remote message */
773 ldb_msg_add(remote, el, 0);
776 /* Map an objectClass into the local partition. */
777 static struct ldb_val map_objectclass_convert_remote(struct ldb_module *module, void *mem_ctx, const struct ldb_val *val)
779 const struct ldb_map_context *data = map_get_context(module);
780 const char *name = (char *)val->data;
781 const struct ldb_map_objectclass *map = map_objectclass_find_remote(data, name);
782 struct ldb_val newval;
784 if (map) {
785 newval.data = (uint8_t*)talloc_strdup(mem_ctx, map->local_name);
786 newval.length = strlen((char *)newval.data);
787 return newval;
790 return ldb_val_dup(mem_ctx, val);
793 /* Generate a local message with a mapped objectClass. */
794 static struct ldb_message_element *map_objectclass_generate_local(struct ldb_module *module, void *mem_ctx, const char *local_attr, const struct ldb_message *remote)
796 const struct ldb_map_context *data = map_get_context(module);
797 struct ldb_context *ldb;
798 struct ldb_message_element *el, *oc;
799 struct ldb_val val;
800 unsigned int i;
802 ldb = ldb_module_get_ctx(module);
804 /* Find old remote objectClass */
805 oc = ldb_msg_find_element(remote, "objectClass");
806 if (oc == NULL) {
807 return NULL;
810 /* Prepare new element */
811 el = talloc_zero(mem_ctx, struct ldb_message_element);
812 if (el == NULL) {
813 ldb_oom(ldb);
814 return NULL;
817 /* Copy remote objectClass element */
818 el->num_values = oc->num_values;
819 el->values = talloc_array(el, struct ldb_val, el->num_values);
820 if (el->values == NULL) {
821 talloc_free(el);
822 ldb_oom(ldb);
823 return NULL;
826 /* Copy remote element name "objectClass" */
827 el->name = talloc_strdup(el, local_attr);
829 /* Convert all remote objectClasses */
830 for (i = 0; i < el->num_values; i++) {
831 el->values[i] = map_objectclass_convert_remote(module, el->values, &oc->values[i]);
834 val.data = (uint8_t *)talloc_strdup(el->values, data->add_objectclass);
835 val.length = strlen((char *)val.data);
837 /* Remove last value if it was the string in data->add_objectclass (eg samba4top, extensibleObject) */
838 if (ldb_val_equal_exact(&val, &el->values[i-1])) {
839 el->num_values--;
840 el->values = talloc_realloc(el, el->values, struct ldb_val, el->num_values);
841 if (el->values == NULL) {
842 talloc_free(el);
843 ldb_oom(ldb);
844 return NULL;
848 return el;
851 static const struct ldb_map_attribute objectclass_convert_map = {
852 .local_name = "objectClass",
853 .type = LDB_MAP_CONVERT,
854 .u = {
855 .convert = {
856 .remote_name = "objectClass",
857 .convert_local = map_objectclass_convert_local,
858 .convert_remote = map_objectclass_convert_remote,
864 /* Mappings for searches on objectClass= assuming a one-to-one
865 * mapping. Needed because this is a generate operator for the
866 * add/modify code */
867 static int map_objectclass_convert_operator(struct ldb_module *module, void *mem_ctx,
868 struct ldb_parse_tree **new, const struct ldb_parse_tree *tree)
871 return map_subtree_collect_remote_simple(module, mem_ctx, new, tree, &objectclass_convert_map);
874 /* Auxiliary request construction
875 * ============================== */
877 /* Build a request to search a record by its DN. */
878 struct ldb_request *map_search_base_req(struct map_context *ac, struct ldb_dn *dn, const char * const *attrs, struct ldb_parse_tree *tree, void *context, ldb_map_callback_t callback)
880 struct ldb_parse_tree *search_tree;
881 struct ldb_context *ldb;
882 struct ldb_request *req;
883 int ret;
885 ldb = ldb_module_get_ctx(ac->module);
887 if (tree) {
888 search_tree = tree;
889 } else {
890 search_tree = ldb_parse_tree(ac, NULL);
891 if (search_tree == NULL) {
892 return NULL;
896 ret = ldb_build_search_req_ex(&req, ldb, ac,
897 dn, LDB_SCOPE_BASE,
898 search_tree, attrs,
899 NULL,
900 context, callback,
901 ac->req);
902 LDB_REQ_SET_LOCATION(req);
903 if (ret != LDB_SUCCESS) {
904 return NULL;
907 return req;
910 /* Build a request to update the 'IS_MAPPED' attribute */
911 struct ldb_request *map_build_fixup_req(struct map_context *ac,
912 struct ldb_dn *olddn,
913 struct ldb_dn *newdn,
914 void *context,
915 ldb_map_callback_t callback)
917 struct ldb_context *ldb;
918 struct ldb_request *req;
919 struct ldb_message *msg;
920 const char *dn;
921 int ret;
923 ldb = ldb_module_get_ctx(ac->module);
925 /* Prepare message */
926 msg = ldb_msg_new(ac);
927 if (msg == NULL) {
928 map_oom(ac->module);
929 return NULL;
932 /* Update local 'IS_MAPPED' to the new remote DN */
933 msg->dn = ldb_dn_copy(msg, olddn);
934 dn = ldb_dn_alloc_linearized(msg, newdn);
935 if ( ! dn || ! ldb_dn_validate(msg->dn)) {
936 goto failed;
938 if (ldb_msg_add_empty(msg, IS_MAPPED, LDB_FLAG_MOD_REPLACE, NULL) != 0) {
939 goto failed;
941 if (ldb_msg_add_string(msg, IS_MAPPED, dn) != 0) {
942 goto failed;
945 /* Prepare request */
946 ret = ldb_build_mod_req(&req, ldb,
947 ac, msg, NULL,
948 context, callback,
949 ac->req);
950 LDB_REQ_SET_LOCATION(req);
951 if (ret != LDB_SUCCESS) {
952 goto failed;
954 talloc_steal(req, msg);
956 return req;
957 failed:
958 talloc_free(msg);
959 return NULL;
962 /* Module initialization
963 * ===================== */
966 /* Builtin mappings for DNs and objectClasses */
967 static const struct ldb_map_attribute builtin_attribute_maps[] = {
969 .local_name = "dn",
970 .type = LDB_MAP_CONVERT,
971 .u = {
972 .convert = {
973 .remote_name = "dn",
974 .convert_local = ldb_dn_convert_local,
975 .convert_remote = ldb_dn_convert_remote,
980 .local_name = NULL,
984 static const struct ldb_map_attribute objectclass_attribute_map = {
985 .local_name = "objectClass",
986 .type = LDB_MAP_GENERATE,
987 .convert_operator = map_objectclass_convert_operator,
988 .u = {
989 .generate = {
990 .remote_names = { "objectClass", NULL },
991 .generate_local = map_objectclass_generate_local,
992 .generate_remote = map_objectclass_generate_remote,
998 /* Find the special 'MAP_DN_NAME' record and store local and remote
999 * base DNs in private data. */
1000 static int map_init_dns(struct ldb_module *module, struct ldb_map_context *data, const char *name)
1002 static const char * const attrs[] = { MAP_DN_FROM, MAP_DN_TO, NULL };
1003 struct ldb_context *ldb;
1004 struct ldb_dn *dn;
1005 struct ldb_message *msg;
1006 struct ldb_result *res;
1007 int ret;
1009 if (!name) {
1010 data->local_base_dn = NULL;
1011 data->remote_base_dn = NULL;
1012 return LDB_SUCCESS;
1015 ldb = ldb_module_get_ctx(module);
1017 dn = ldb_dn_new_fmt(data, ldb, "%s=%s", MAP_DN_NAME, name);
1018 if ( ! ldb_dn_validate(dn)) {
1019 ldb_debug(ldb, LDB_DEBUG_ERROR, "ldb_map: "
1020 "Failed to construct '%s' DN!", MAP_DN_NAME);
1021 return LDB_ERR_OPERATIONS_ERROR;
1024 ret = ldb_search(ldb, data, &res, dn, LDB_SCOPE_BASE, attrs, NULL);
1025 talloc_free(dn);
1026 if (ret != LDB_SUCCESS) {
1027 return ret;
1029 if (res->count == 0) {
1030 ldb_debug(ldb, LDB_DEBUG_ERROR, "ldb_map: "
1031 "No results for '%s=%s'!", MAP_DN_NAME, name);
1032 talloc_free(res);
1033 return LDB_ERR_CONSTRAINT_VIOLATION;
1035 if (res->count > 1) {
1036 ldb_debug(ldb, LDB_DEBUG_ERROR, "ldb_map: "
1037 "Too many results for '%s=%s'!", MAP_DN_NAME, name);
1038 talloc_free(res);
1039 return LDB_ERR_CONSTRAINT_VIOLATION;
1042 msg = res->msgs[0];
1043 data->local_base_dn = ldb_msg_find_attr_as_dn(ldb, data, msg, MAP_DN_FROM);
1044 data->remote_base_dn = ldb_msg_find_attr_as_dn(ldb, data, msg, MAP_DN_TO);
1045 talloc_free(res);
1047 return LDB_SUCCESS;
1050 /* Store attribute maps and objectClass maps in private data. */
1051 static int map_init_maps(struct ldb_module *module, struct ldb_map_context *data,
1052 const struct ldb_map_attribute *attrs,
1053 const struct ldb_map_objectclass *ocls,
1054 const char * const *wildcard_attributes)
1056 unsigned int i, j, last;
1057 last = 0;
1059 /* Count specified attribute maps */
1060 for (i = 0; attrs[i].local_name; i++) /* noop */ ;
1061 /* Count built-in attribute maps */
1062 for (j = 0; builtin_attribute_maps[j].local_name; j++) /* noop */ ;
1064 /* Store list of attribute maps */
1065 data->attribute_maps = talloc_array(data, struct ldb_map_attribute, i+j+2);
1066 if (data->attribute_maps == NULL) {
1067 map_oom(module);
1068 return LDB_ERR_OPERATIONS_ERROR;
1071 /* Specified ones go first */
1072 for (i = 0; attrs[i].local_name; i++) {
1073 data->attribute_maps[last] = attrs[i];
1074 last++;
1077 /* Built-in ones go last */
1078 for (i = 0; builtin_attribute_maps[i].local_name; i++) {
1079 data->attribute_maps[last] = builtin_attribute_maps[i];
1080 last++;
1083 if (data->add_objectclass) {
1084 /* ObjectClass one is very last, if required */
1085 data->attribute_maps[last] = objectclass_attribute_map;
1086 last++;
1087 } else if (ocls) {
1088 data->attribute_maps[last] = objectclass_convert_map;
1089 last++;
1092 /* Ensure 'local_name == NULL' for the last entry */
1093 memset(&data->attribute_maps[last], 0, sizeof(struct ldb_map_attribute));
1095 /* Store list of objectClass maps */
1096 data->objectclass_maps = ocls;
1098 data->wildcard_attributes = wildcard_attributes;
1100 return LDB_SUCCESS;
1103 /* Initialize global private data. */
1104 _PUBLIC_ int ldb_map_init(struct ldb_module *module, const struct ldb_map_attribute *attrs,
1105 const struct ldb_map_objectclass *ocls,
1106 const char * const *wildcard_attributes,
1107 const char *add_objectclass,
1108 const char *name)
1110 struct map_private *data;
1111 int ret;
1113 /* Prepare private data */
1114 data = talloc_zero(module, struct map_private);
1115 if (data == NULL) {
1116 map_oom(module);
1117 return LDB_ERR_OPERATIONS_ERROR;
1120 ldb_module_set_private(module, data);
1122 data->context = talloc_zero(data, struct ldb_map_context);
1123 if (!data->context) {
1124 map_oom(module);
1125 return LDB_ERR_OPERATIONS_ERROR;
1128 /* Store local and remote baseDNs */
1129 ret = map_init_dns(module, data->context, name);
1130 if (ret != LDB_SUCCESS) {
1131 talloc_free(data);
1132 return ret;
1135 data->context->add_objectclass = add_objectclass;
1137 /* Store list of attribute and objectClass maps */
1138 ret = map_init_maps(module, data->context, attrs, ocls, wildcard_attributes);
1139 if (ret != LDB_SUCCESS) {
1140 talloc_free(data);
1141 return ret;
1144 return LDB_SUCCESS;