Line data Source code
1 : /*
2 : ldb database mapping module
3 :
4 : Copyright (C) Jelmer Vernooij 2005
5 : Copyright (C) Martin Kuehl <mkhl@samba.org> 2006
6 : Copyright (C) Simo Sorce 2008
7 :
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
11 :
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.
16 :
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.
21 :
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/>.
24 :
25 : */
26 :
27 : /*
28 : * Name: ldb
29 : *
30 : * Component: ldb ldb_map module
31 : *
32 : * Description: Map portions of data into a different format on a
33 : * remote partition.
34 : *
35 : * Author: Jelmer Vernooij, Martin Kuehl
36 : */
37 :
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"
43 :
44 : #ifndef _PUBLIC_
45 : #define _PUBLIC_
46 : #endif
47 :
48 : /* Description of the provided ldb requests:
49 : - special attribute 'isMapped'
50 :
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
64 :
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
71 :
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
82 :
83 : - delete:
84 : - search for local record
85 : - if present
86 : - delete local record
87 : - delete remote record
88 :
89 : - rename:
90 : - search for local record
91 : - if present
92 : - rename local record
93 : - modify local isMapped
94 : - rename remote record
95 : */
96 :
97 :
98 :
99 : /* Private data structures
100 : * ======================= */
101 :
102 : /* Global private data */
103 : /* Extract mappings from private data. */
104 6790 : const struct ldb_map_context *map_get_context(struct ldb_module *module)
105 : {
106 6790 : const struct map_private *data = talloc_get_type(ldb_module_get_private(module), struct map_private);
107 6790 : return data->context;
108 : }
109 :
110 : /* Create a generic request context. */
111 190 : struct map_context *map_init_context(struct ldb_module *module,
112 : struct ldb_request *req)
113 : {
114 190 : struct ldb_context *ldb;
115 190 : struct map_context *ac;
116 :
117 190 : ldb = ldb_module_get_ctx(module);
118 :
119 190 : ac = talloc_zero(req, struct map_context);
120 190 : if (ac == NULL) {
121 0 : ldb_set_errstring(ldb, "Out of Memory");
122 0 : return NULL;
123 : }
124 :
125 190 : ac->module = module;
126 190 : ac->req = req;
127 :
128 190 : return ac;
129 : }
130 :
131 : /* Dealing with DNs for different partitions
132 : * ========================================= */
133 :
134 : /* Check whether any data should be stored in the local partition. */
135 422 : bool map_check_local_db(struct ldb_module *module)
136 : {
137 422 : const struct ldb_map_context *data = map_get_context(module);
138 :
139 422 : if (!data->remote_base_dn || !data->local_base_dn) {
140 0 : return false;
141 : }
142 :
143 0 : return true;
144 : }
145 :
146 : /* Copy a DN with the base DN of the local partition. */
147 254 : static struct ldb_dn *ldb_dn_rebase_local(void *mem_ctx, const struct ldb_map_context *data, struct ldb_dn *dn)
148 : {
149 254 : struct ldb_dn *new_dn;
150 :
151 254 : new_dn = ldb_dn_copy(mem_ctx, dn);
152 254 : if ( ! ldb_dn_validate(new_dn)) {
153 0 : talloc_free(new_dn);
154 0 : return NULL;
155 : }
156 :
157 : /* may be we don't need to rebase at all */
158 254 : if ( ! data->remote_base_dn || ! data->local_base_dn) {
159 0 : return new_dn;
160 : }
161 :
162 254 : if ( ! ldb_dn_remove_base_components(new_dn, ldb_dn_get_comp_num(data->remote_base_dn))) {
163 0 : talloc_free(new_dn);
164 0 : return NULL;
165 : }
166 :
167 254 : if ( ! ldb_dn_add_base(new_dn, data->local_base_dn)) {
168 0 : talloc_free(new_dn);
169 0 : return NULL;
170 : }
171 :
172 0 : return new_dn;
173 : }
174 :
175 : /* Copy a DN with the base DN of the remote partition. */
176 193 : static struct ldb_dn *ldb_dn_rebase_remote(void *mem_ctx, const struct ldb_map_context *data, struct ldb_dn *dn)
177 : {
178 193 : struct ldb_dn *new_dn;
179 :
180 193 : new_dn = ldb_dn_copy(mem_ctx, dn);
181 193 : if ( ! ldb_dn_validate(new_dn)) {
182 0 : talloc_free(new_dn);
183 0 : return NULL;
184 : }
185 :
186 : /* may be we don't need to rebase at all */
187 193 : if ( ! data->remote_base_dn || ! data->local_base_dn) {
188 0 : return new_dn;
189 : }
190 :
191 193 : if ( ! ldb_dn_remove_base_components(new_dn, ldb_dn_get_comp_num(data->local_base_dn))) {
192 0 : talloc_free(new_dn);
193 0 : return NULL;
194 : }
195 :
196 193 : if ( ! ldb_dn_add_base(new_dn, data->remote_base_dn)) {
197 0 : talloc_free(new_dn);
198 0 : return NULL;
199 : }
200 :
201 0 : return new_dn;
202 : }
203 :
204 : /* Run a request and make sure it targets the remote partition. */
205 : /* TODO: free old DNs and messages? */
206 190 : int ldb_next_remote_request(struct ldb_module *module, struct ldb_request *request)
207 : {
208 190 : const struct ldb_map_context *data = map_get_context(module);
209 190 : struct ldb_context *ldb;
210 190 : struct ldb_message *msg;
211 :
212 190 : ldb = ldb_module_get_ctx(module);
213 :
214 190 : switch (request->operation) {
215 162 : case LDB_SEARCH:
216 162 : if (request->op.search.base) {
217 162 : request->op.search.base = ldb_dn_rebase_remote(request, data, request->op.search.base);
218 : } else {
219 0 : request->op.search.base = data->remote_base_dn;
220 : /* TODO: adjust scope? */
221 : }
222 0 : break;
223 :
224 6 : case LDB_ADD:
225 6 : msg = ldb_msg_copy_shallow(request, request->op.add.message);
226 6 : if (msg == NULL) {
227 0 : return LDB_ERR_OPERATIONS_ERROR;
228 : }
229 6 : msg->dn = ldb_dn_rebase_remote(msg, data, msg->dn);
230 6 : request->op.add.message = msg;
231 6 : break;
232 :
233 9 : case LDB_MODIFY:
234 9 : msg = ldb_msg_copy_shallow(request, request->op.mod.message);
235 9 : if (msg == NULL) {
236 0 : return LDB_ERR_OPERATIONS_ERROR;
237 : }
238 9 : msg->dn = ldb_dn_rebase_remote(msg, data, msg->dn);
239 9 : request->op.mod.message = msg;
240 9 : break;
241 :
242 10 : case LDB_DELETE:
243 10 : request->op.del.dn = ldb_dn_rebase_remote(request, data, request->op.del.dn);
244 10 : break;
245 :
246 3 : case LDB_RENAME:
247 3 : request->op.rename.olddn = ldb_dn_rebase_remote(request, data, request->op.rename.olddn);
248 3 : request->op.rename.newdn = ldb_dn_rebase_remote(request, data, request->op.rename.newdn);
249 3 : break;
250 :
251 0 : default:
252 0 : ldb_debug(ldb, LDB_DEBUG_ERROR, "ldb_map: "
253 : "Invalid remote request!");
254 0 : return LDB_ERR_OPERATIONS_ERROR;
255 : }
256 :
257 190 : return ldb_next_request(module, request);
258 : }
259 :
260 :
261 : /* Finding mappings for attributes and objectClasses
262 : * ================================================= */
263 :
264 : /* Find an objectClass mapping by the local name. */
265 35 : static const struct ldb_map_objectclass *map_objectclass_find_local(const struct ldb_map_context *data, const char *name)
266 : {
267 35 : unsigned int i;
268 :
269 112 : for (i = 0; data->objectclass_maps && data->objectclass_maps[i].local_name; i++) {
270 98 : if (ldb_attr_cmp(data->objectclass_maps[i].local_name, name) == 0) {
271 21 : return &data->objectclass_maps[i];
272 : }
273 : }
274 :
275 0 : return NULL;
276 : }
277 :
278 : /* Find an objectClass mapping by the remote name. */
279 123 : static const struct ldb_map_objectclass *map_objectclass_find_remote(const struct ldb_map_context *data, const char *name)
280 : {
281 123 : unsigned int i;
282 :
283 411 : for (i = 0; data->objectclass_maps && data->objectclass_maps[i].remote_name; i++) {
284 375 : if (ldb_attr_cmp(data->objectclass_maps[i].remote_name, name) == 0) {
285 87 : return &data->objectclass_maps[i];
286 : }
287 : }
288 :
289 0 : return NULL;
290 : }
291 :
292 : /* Find an attribute mapping by the local name. */
293 5387 : const struct ldb_map_attribute *map_attr_find_local(const struct ldb_map_context *data, const char *name)
294 : {
295 5387 : unsigned int i;
296 :
297 258108 : for (i = 0; data->attribute_maps[i].local_name; i++) {
298 257420 : if (ldb_attr_cmp(data->attribute_maps[i].local_name, name) == 0) {
299 4699 : return &data->attribute_maps[i];
300 : }
301 : }
302 59856 : for (i = 0; data->attribute_maps[i].local_name; i++) {
303 59168 : if (ldb_attr_cmp(data->attribute_maps[i].local_name, "*") == 0) {
304 0 : return &data->attribute_maps[i];
305 : }
306 : }
307 :
308 0 : return NULL;
309 : }
310 :
311 : /* Find an attribute mapping by the remote name. */
312 798 : const struct ldb_map_attribute *map_attr_find_remote(const struct ldb_map_context *data, const char *name)
313 : {
314 798 : const struct ldb_map_attribute *map;
315 798 : const struct ldb_map_attribute *wildcard = NULL;
316 798 : unsigned int i, j;
317 :
318 19735 : for (i = 0; data->attribute_maps[i].local_name; i++) {
319 19735 : map = &data->attribute_maps[i];
320 19735 : if (ldb_attr_cmp(map->local_name, "*") == 0) {
321 0 : wildcard = &data->attribute_maps[i];
322 : }
323 :
324 19735 : switch (map->type) {
325 0 : case LDB_MAP_IGNORE:
326 0 : break;
327 :
328 1864 : case LDB_MAP_KEEP:
329 1864 : if (ldb_attr_cmp(map->local_name, name) == 0) {
330 787 : return map;
331 : }
332 0 : break;
333 :
334 8538 : case LDB_MAP_RENAME:
335 : case LDB_MAP_RENDROP:
336 : case LDB_MAP_CONVERT:
337 8538 : if (ldb_attr_cmp(map->u.rename.remote_name, name) == 0) {
338 11 : return map;
339 : }
340 0 : break;
341 :
342 0 : case LDB_MAP_GENERATE:
343 1596 : for (j = 0; map->u.generate.remote_names[j]; j++) {
344 798 : if (ldb_attr_cmp(map->u.generate.remote_names[j], name) == 0) {
345 0 : return map;
346 : }
347 : }
348 0 : break;
349 : }
350 : }
351 :
352 : /* We didn't find it, so return the wildcard record if one was configured */
353 0 : return wildcard;
354 : }
355 :
356 :
357 : /* Mapping attributes
358 : * ================== */
359 :
360 : /* Check whether an attribute will be mapped into the remote partition. */
361 1131 : bool map_attr_check_remote(const struct ldb_map_context *data, const char *attr)
362 : {
363 1131 : const struct ldb_map_attribute *map = map_attr_find_local(data, attr);
364 :
365 1131 : if (map == NULL) {
366 0 : return false;
367 : }
368 762 : if (map->type == LDB_MAP_IGNORE) {
369 140 : return false;
370 : }
371 :
372 0 : return true;
373 : }
374 :
375 : /* Map an attribute name into the remote partition. */
376 320 : const char *map_attr_map_local(void *mem_ctx, const struct ldb_map_attribute *map, const char *attr)
377 : {
378 320 : if (map == NULL) {
379 0 : return talloc_strdup(mem_ctx, attr);
380 : }
381 :
382 320 : switch (map->type) {
383 120 : case LDB_MAP_KEEP:
384 120 : return talloc_strdup(mem_ctx, attr);
385 :
386 200 : case LDB_MAP_RENAME:
387 : case LDB_MAP_RENDROP:
388 : case LDB_MAP_CONVERT:
389 200 : return talloc_strdup(mem_ctx, map->u.rename.remote_name);
390 :
391 0 : default:
392 0 : return NULL;
393 : }
394 : }
395 :
396 : /* Map an attribute name back into the local partition. */
397 798 : const char *map_attr_map_remote(void *mem_ctx, const struct ldb_map_attribute *map, const char *attr)
398 : {
399 798 : if (map == NULL) {
400 0 : return talloc_strdup(mem_ctx, attr);
401 : }
402 :
403 798 : if (map->type == LDB_MAP_KEEP) {
404 787 : return talloc_strdup(mem_ctx, attr);
405 : }
406 :
407 11 : return talloc_strdup(mem_ctx, map->local_name);
408 : }
409 :
410 :
411 : /* Merge two lists of attributes into a single one. */
412 186 : int map_attrs_merge(struct ldb_module *module, void *mem_ctx,
413 : const char ***attrs, const char * const *more_attrs)
414 : {
415 186 : unsigned int i, j, k;
416 :
417 503 : for (i = 0; *attrs && (*attrs)[i]; i++) /* noop */ ;
418 434 : for (j = 0; more_attrs && more_attrs[j]; j++) /* noop */ ;
419 :
420 186 : *attrs = talloc_realloc(mem_ctx, *attrs, const char *, i+j+1);
421 186 : if (*attrs == NULL) {
422 0 : map_oom(module);
423 0 : return -1;
424 : }
425 :
426 434 : for (k = 0; k < j; k++) {
427 248 : (*attrs)[i + k] = more_attrs[k];
428 : }
429 :
430 186 : (*attrs)[i+k] = NULL;
431 :
432 186 : return 0;
433 : }
434 :
435 : /* Mapping ldb values
436 : * ================== */
437 :
438 : /* Map an ldb value into the remote partition. */
439 192 : struct ldb_val ldb_val_map_local(struct ldb_module *module, void *mem_ctx,
440 : const struct ldb_map_attribute *map, const struct ldb_val *val)
441 : {
442 192 : if (map && (map->type == LDB_MAP_CONVERT) && (map->u.convert.convert_local)) {
443 37 : return map->u.convert.convert_local(module, mem_ctx, val);
444 : }
445 :
446 155 : return ldb_val_dup(mem_ctx, val);
447 : }
448 :
449 : /* Map an ldb value back into the local partition. */
450 1376 : struct ldb_val ldb_val_map_remote(struct ldb_module *module, void *mem_ctx,
451 : const struct ldb_map_attribute *map, const struct ldb_val *val)
452 : {
453 1376 : if (map && (map->type == LDB_MAP_CONVERT) && (map->u.convert.convert_remote)) {
454 178 : return map->u.convert.convert_remote(module, mem_ctx, val);
455 : }
456 :
457 1198 : return ldb_val_dup(mem_ctx, val);
458 : }
459 :
460 :
461 : /* Mapping DNs
462 : * =========== */
463 :
464 : /* Check whether a DN is below the local baseDN. */
465 211 : bool ldb_dn_check_local(struct ldb_module *module, struct ldb_dn *dn)
466 : {
467 211 : const struct ldb_map_context *data = map_get_context(module);
468 :
469 211 : if (!data->local_base_dn) {
470 0 : return true;
471 : }
472 :
473 211 : return ldb_dn_compare_base(data->local_base_dn, dn) == 0;
474 : }
475 :
476 : /* Map a DN into the remote partition. */
477 31 : struct ldb_dn *ldb_dn_map_local(struct ldb_module *module, void *mem_ctx, struct ldb_dn *dn)
478 : {
479 31 : const struct ldb_map_context *data = map_get_context(module);
480 31 : struct ldb_context *ldb;
481 31 : struct ldb_dn *newdn;
482 31 : const struct ldb_map_attribute *map;
483 31 : enum ldb_map_attr_type map_type;
484 31 : const char *name;
485 31 : struct ldb_val value;
486 31 : int i, ret;
487 :
488 31 : if (dn == NULL) {
489 0 : return NULL;
490 : }
491 :
492 31 : ldb = ldb_module_get_ctx(module);
493 :
494 31 : newdn = ldb_dn_copy(mem_ctx, dn);
495 31 : if (newdn == NULL) {
496 0 : map_oom(module);
497 0 : return NULL;
498 : }
499 :
500 : /* For each RDN, map the component name and possibly the value */
501 132 : for (i = 0; i < ldb_dn_get_comp_num(newdn); i++) {
502 101 : map = map_attr_find_local(data, ldb_dn_get_component_name(dn, i));
503 :
504 : /* Unknown attribute - leave this RDN as is and hope the best... */
505 101 : if (map == NULL) {
506 0 : map_type = LDB_MAP_KEEP;
507 : } else {
508 101 : map_type = map->type;
509 : }
510 :
511 101 : switch (map_type) {
512 0 : case LDB_MAP_IGNORE:
513 : case LDB_MAP_GENERATE:
514 0 : ldb_debug(ldb, LDB_DEBUG_ERROR, "ldb_map: "
515 : "LDB_MAP_IGNORE/LDB_MAP_GENERATE attribute '%s' "
516 : "used in DN!", ldb_dn_get_component_name(dn, i));
517 0 : goto failed;
518 :
519 0 : case LDB_MAP_CONVERT:
520 0 : if (map->u.convert.convert_local == NULL) {
521 0 : ldb_debug(ldb, LDB_DEBUG_ERROR, "ldb_map: "
522 : "'convert_local' not set for attribute '%s' "
523 : "used in DN!", ldb_dn_get_component_name(dn, i));
524 0 : goto failed;
525 : }
526 :
527 101 : FALL_THROUGH;
528 : case LDB_MAP_KEEP:
529 : case LDB_MAP_RENAME:
530 : case LDB_MAP_RENDROP:
531 101 : name = map_attr_map_local(newdn, map, ldb_dn_get_component_name(dn, i));
532 101 : if (name == NULL) goto failed;
533 :
534 101 : value = ldb_val_map_local(module, newdn, map, ldb_dn_get_component_val(dn, i));
535 101 : if (value.data == NULL) goto failed;
536 :
537 101 : ret = ldb_dn_set_component(newdn, i, name, value);
538 101 : if (ret != LDB_SUCCESS) {
539 0 : goto failed;
540 : }
541 :
542 0 : break;
543 : }
544 : }
545 :
546 0 : return newdn;
547 :
548 0 : failed:
549 0 : talloc_free(newdn);
550 0 : return NULL;
551 : }
552 :
553 : /* Map a DN into the local partition. */
554 254 : struct ldb_dn *ldb_dn_map_remote(struct ldb_module *module, void *mem_ctx, struct ldb_dn *dn)
555 : {
556 254 : const struct ldb_map_context *data = map_get_context(module);
557 254 : struct ldb_context *ldb;
558 254 : struct ldb_dn *newdn;
559 254 : const struct ldb_map_attribute *map;
560 254 : enum ldb_map_attr_type map_type;
561 254 : const char *name;
562 254 : struct ldb_val value;
563 254 : int i, ret;
564 :
565 254 : if (dn == NULL) {
566 0 : return NULL;
567 : }
568 :
569 254 : ldb = ldb_module_get_ctx(module);
570 :
571 254 : newdn = ldb_dn_copy(mem_ctx, dn);
572 254 : if (newdn == NULL) {
573 0 : map_oom(module);
574 0 : return NULL;
575 : }
576 :
577 : /* For each RDN, map the component name and possibly the value */
578 1052 : for (i = 0; i < ldb_dn_get_comp_num(newdn); i++) {
579 798 : map = map_attr_find_remote(data, ldb_dn_get_component_name(dn, i));
580 :
581 : /* Unknown attribute - leave this RDN as is and hope the best... */
582 798 : if (map == NULL) {
583 0 : map_type = LDB_MAP_KEEP;
584 : } else {
585 798 : map_type = map->type;
586 : }
587 :
588 798 : switch (map_type) {
589 0 : case LDB_MAP_IGNORE:
590 : case LDB_MAP_GENERATE:
591 0 : ldb_debug(ldb, LDB_DEBUG_ERROR, "ldb_map: "
592 : "LDB_MAP_IGNORE/LDB_MAP_GENERATE attribute '%s' "
593 : "used in DN!", ldb_dn_get_component_name(dn, i));
594 0 : goto failed;
595 :
596 0 : case LDB_MAP_CONVERT:
597 0 : if (map->u.convert.convert_remote == NULL) {
598 0 : ldb_debug(ldb, LDB_DEBUG_ERROR, "ldb_map: "
599 : "'convert_remote' not set for attribute '%s' "
600 : "used in DN!", ldb_dn_get_component_name(dn, i));
601 0 : goto failed;
602 : }
603 :
604 798 : FALL_THROUGH;
605 : case LDB_MAP_KEEP:
606 : case LDB_MAP_RENAME:
607 : case LDB_MAP_RENDROP:
608 798 : name = map_attr_map_remote(newdn, map, ldb_dn_get_component_name(dn, i));
609 798 : if (name == NULL) goto failed;
610 :
611 798 : value = ldb_val_map_remote(module, newdn, map, ldb_dn_get_component_val(dn, i));
612 798 : if (value.data == NULL) goto failed;
613 :
614 798 : ret = ldb_dn_set_component(newdn, i, name, value);
615 798 : if (ret != LDB_SUCCESS) {
616 0 : goto failed;
617 : }
618 :
619 0 : break;
620 : }
621 : }
622 :
623 0 : return newdn;
624 :
625 0 : failed:
626 0 : talloc_free(newdn);
627 0 : return NULL;
628 : }
629 :
630 : /* Map a DN and its base into the local partition. */
631 : /* TODO: This should not be required with GUIDs. */
632 254 : struct ldb_dn *ldb_dn_map_rebase_remote(struct ldb_module *module, void *mem_ctx, struct ldb_dn *dn)
633 : {
634 254 : const struct ldb_map_context *data = map_get_context(module);
635 254 : struct ldb_dn *dn1, *dn2;
636 :
637 254 : dn1 = ldb_dn_rebase_local(mem_ctx, data, dn);
638 254 : dn2 = ldb_dn_map_remote(module, mem_ctx, dn1);
639 :
640 254 : talloc_free(dn1);
641 254 : return dn2;
642 : }
643 :
644 :
645 : /* Converting DNs and objectClasses (as ldb values)
646 : * ================================================ */
647 :
648 : /* Map a DN contained in an ldb value into the remote partition. */
649 0 : static struct ldb_val ldb_dn_convert_local(struct ldb_module *module, void *mem_ctx, const struct ldb_val *val)
650 : {
651 0 : struct ldb_context *ldb;
652 0 : struct ldb_dn *dn, *newdn;
653 0 : struct ldb_val newval;
654 :
655 0 : ldb = ldb_module_get_ctx(module);
656 :
657 0 : dn = ldb_dn_from_ldb_val(mem_ctx, ldb, val);
658 0 : if (! ldb_dn_validate(dn)) {
659 0 : newval.length = 0;
660 0 : newval.data = NULL;
661 0 : talloc_free(dn);
662 0 : return newval;
663 : }
664 0 : newdn = ldb_dn_map_local(module, mem_ctx, dn);
665 0 : talloc_free(dn);
666 :
667 0 : newval.length = 0;
668 0 : newval.data = (uint8_t *)ldb_dn_alloc_linearized(mem_ctx, newdn);
669 0 : if (newval.data) {
670 0 : newval.length = strlen((char *)newval.data);
671 : }
672 0 : talloc_free(newdn);
673 :
674 0 : return newval;
675 : }
676 :
677 : /* Map a DN contained in an ldb value into the local partition. */
678 0 : static struct ldb_val ldb_dn_convert_remote(struct ldb_module *module, void *mem_ctx, const struct ldb_val *val)
679 : {
680 0 : struct ldb_context *ldb;
681 0 : struct ldb_dn *dn, *newdn;
682 0 : struct ldb_val newval;
683 :
684 0 : ldb = ldb_module_get_ctx(module);
685 :
686 0 : dn = ldb_dn_from_ldb_val(mem_ctx, ldb, val);
687 0 : if (! ldb_dn_validate(dn)) {
688 0 : newval.length = 0;
689 0 : newval.data = NULL;
690 0 : talloc_free(dn);
691 0 : return newval;
692 : }
693 0 : newdn = ldb_dn_map_remote(module, mem_ctx, dn);
694 0 : talloc_free(dn);
695 :
696 0 : newval.length = 0;
697 0 : newval.data = (uint8_t *)ldb_dn_alloc_linearized(mem_ctx, newdn);
698 0 : if (newval.data) {
699 0 : newval.length = strlen((char *)newval.data);
700 : }
701 0 : talloc_free(newdn);
702 :
703 0 : return newval;
704 : }
705 :
706 : /* Map an objectClass into the remote partition. */
707 35 : static struct ldb_val map_objectclass_convert_local(struct ldb_module *module, void *mem_ctx, const struct ldb_val *val)
708 : {
709 35 : const struct ldb_map_context *data = map_get_context(module);
710 35 : const char *name = (char *)val->data;
711 35 : const struct ldb_map_objectclass *map = map_objectclass_find_local(data, name);
712 35 : struct ldb_val newval;
713 :
714 35 : if (map) {
715 21 : newval.data = (uint8_t*)talloc_strdup(mem_ctx, map->remote_name);
716 21 : newval.length = strlen((char *)newval.data);
717 21 : return newval;
718 : }
719 :
720 14 : return ldb_val_dup(mem_ctx, val);
721 : }
722 :
723 : /* Generate a remote message with a mapped objectClass. */
724 0 : 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)
725 : {
726 0 : const struct ldb_map_context *data = map_get_context(module);
727 0 : struct ldb_context *ldb;
728 0 : struct ldb_message_element *el, *oc;
729 0 : struct ldb_val val;
730 0 : bool found_extensibleObject = false;
731 0 : unsigned int i;
732 0 : int ret;
733 :
734 0 : ldb = ldb_module_get_ctx(module);
735 :
736 : /* Find old local objectClass */
737 0 : oc = ldb_msg_find_element(old, "objectClass");
738 0 : if (oc == NULL) {
739 0 : return;
740 : }
741 :
742 : /* Prepare new element */
743 0 : el = talloc_zero(remote, struct ldb_message_element);
744 0 : if (el == NULL) {
745 0 : ldb_oom(ldb);
746 0 : return; /* TODO: fail? */
747 : }
748 :
749 : /* Copy local objectClass element, reverse space for an extra value */
750 0 : el->num_values = oc->num_values + 1;
751 0 : el->values = talloc_array(el, struct ldb_val, el->num_values);
752 0 : if (el->values == NULL) {
753 0 : talloc_free(el);
754 0 : ldb_oom(ldb);
755 0 : return; /* TODO: fail? */
756 : }
757 :
758 : /* Copy local element name "objectClass" */
759 0 : el->name = talloc_strdup(el, local_attr);
760 :
761 : /* Convert all local objectClasses */
762 0 : for (i = 0; i < el->num_values - 1; i++) {
763 0 : el->values[i] = map_objectclass_convert_local(module, el->values, &oc->values[i]);
764 0 : if (ldb_attr_cmp((char *)el->values[i].data, data->add_objectclass) == 0) {
765 0 : found_extensibleObject = true;
766 : }
767 : }
768 :
769 0 : if (!found_extensibleObject) {
770 0 : val.data = (uint8_t *)talloc_strdup(el->values, data->add_objectclass);
771 0 : val.length = strlen((char *)val.data);
772 :
773 : /* Append additional objectClass data->add_objectclass */
774 0 : el->values[i] = val;
775 : } else {
776 0 : el->num_values--;
777 : }
778 :
779 : /* Add new objectClass to remote message */
780 0 : ret = ldb_msg_add(remote, el, 0);
781 0 : if (ret != LDB_SUCCESS) {
782 0 : ldb_oom(ldb);
783 0 : return;
784 : }
785 : }
786 :
787 : /* Map an objectClass into the local partition. */
788 123 : static struct ldb_val map_objectclass_convert_remote(struct ldb_module *module, void *mem_ctx, const struct ldb_val *val)
789 : {
790 123 : const struct ldb_map_context *data = map_get_context(module);
791 123 : const char *name = (char *)val->data;
792 123 : const struct ldb_map_objectclass *map = map_objectclass_find_remote(data, name);
793 123 : struct ldb_val newval;
794 :
795 123 : if (map) {
796 87 : newval.data = (uint8_t*)talloc_strdup(mem_ctx, map->local_name);
797 87 : newval.length = strlen((char *)newval.data);
798 87 : return newval;
799 : }
800 :
801 36 : return ldb_val_dup(mem_ctx, val);
802 : }
803 :
804 : /* Generate a local message with a mapped objectClass. */
805 0 : 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)
806 : {
807 0 : const struct ldb_map_context *data = map_get_context(module);
808 0 : struct ldb_context *ldb;
809 0 : struct ldb_message_element *el, *oc;
810 0 : struct ldb_val val;
811 0 : unsigned int i;
812 :
813 0 : ldb = ldb_module_get_ctx(module);
814 :
815 : /* Find old remote objectClass */
816 0 : oc = ldb_msg_find_element(remote, "objectClass");
817 0 : if (oc == NULL) {
818 0 : return NULL;
819 : }
820 :
821 : /* Prepare new element */
822 0 : el = talloc_zero(mem_ctx, struct ldb_message_element);
823 0 : if (el == NULL) {
824 0 : ldb_oom(ldb);
825 0 : return NULL;
826 : }
827 :
828 : /* Copy remote objectClass element */
829 0 : el->num_values = oc->num_values;
830 0 : el->values = talloc_array(el, struct ldb_val, el->num_values);
831 0 : if (el->values == NULL) {
832 0 : talloc_free(el);
833 0 : ldb_oom(ldb);
834 0 : return NULL;
835 : }
836 :
837 : /* Copy remote element name "objectClass" */
838 0 : el->name = talloc_strdup(el, local_attr);
839 :
840 : /* Convert all remote objectClasses */
841 0 : for (i = 0; i < el->num_values; i++) {
842 0 : el->values[i] = map_objectclass_convert_remote(module, el->values, &oc->values[i]);
843 : }
844 :
845 0 : val.data = (uint8_t *)talloc_strdup(el->values, data->add_objectclass);
846 0 : val.length = strlen((char *)val.data);
847 :
848 : /* Remove last value if it was the string in data->add_objectclass (eg samba4top, extensibleObject) */
849 0 : if (ldb_val_equal_exact(&val, &el->values[i-1])) {
850 0 : el->num_values--;
851 0 : el->values = talloc_realloc(el, el->values, struct ldb_val, el->num_values);
852 0 : if (el->values == NULL) {
853 0 : talloc_free(el);
854 0 : ldb_oom(ldb);
855 0 : return NULL;
856 : }
857 : }
858 :
859 0 : return el;
860 : }
861 :
862 : static const struct ldb_map_attribute objectclass_convert_map = {
863 : .local_name = "objectClass",
864 : .type = LDB_MAP_CONVERT,
865 : .u = {
866 : .convert = {
867 : .remote_name = "objectClass",
868 : .convert_local = map_objectclass_convert_local,
869 : .convert_remote = map_objectclass_convert_remote,
870 : },
871 : },
872 : };
873 :
874 :
875 : /* Mappings for searches on objectClass= assuming a one-to-one
876 : * mapping. Needed because this is a generate operator for the
877 : * add/modify code */
878 0 : static int map_objectclass_convert_operator(struct ldb_module *module, void *mem_ctx,
879 : struct ldb_parse_tree **new, const struct ldb_parse_tree *tree)
880 : {
881 :
882 0 : return map_subtree_collect_remote_simple(module, mem_ctx, new, tree, &objectclass_convert_map);
883 : }
884 :
885 : /* Auxiliary request construction
886 : * ============================== */
887 :
888 : /* Build a request to search a record by its DN. */
889 273 : 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)
890 : {
891 273 : struct ldb_parse_tree *search_tree;
892 273 : struct ldb_context *ldb;
893 273 : struct ldb_request *req;
894 273 : int ret;
895 :
896 273 : ldb = ldb_module_get_ctx(ac->module);
897 :
898 273 : if (tree) {
899 0 : search_tree = tree;
900 : } else {
901 254 : search_tree = ldb_parse_tree(ac, NULL);
902 254 : if (search_tree == NULL) {
903 0 : return NULL;
904 : }
905 : }
906 :
907 273 : ret = ldb_build_search_req_ex(&req, ldb, ac,
908 : dn, LDB_SCOPE_BASE,
909 : search_tree, attrs,
910 : NULL,
911 : context, callback,
912 : ac->req);
913 273 : LDB_REQ_SET_LOCATION(req);
914 273 : if (ret != LDB_SUCCESS) {
915 0 : return NULL;
916 : }
917 :
918 273 : return req;
919 : }
920 :
921 : /* Build a request to update the 'IS_MAPPED' attribute */
922 3 : struct ldb_request *map_build_fixup_req(struct map_context *ac,
923 : struct ldb_dn *olddn,
924 : struct ldb_dn *newdn,
925 : void *context,
926 : ldb_map_callback_t callback)
927 : {
928 3 : struct ldb_context *ldb;
929 3 : struct ldb_request *req;
930 3 : struct ldb_message *msg;
931 3 : const char *dn;
932 3 : int ret;
933 :
934 3 : ldb = ldb_module_get_ctx(ac->module);
935 :
936 : /* Prepare message */
937 3 : msg = ldb_msg_new(ac);
938 3 : if (msg == NULL) {
939 0 : map_oom(ac->module);
940 0 : return NULL;
941 : }
942 :
943 : /* Update local 'IS_MAPPED' to the new remote DN */
944 3 : msg->dn = ldb_dn_copy(msg, olddn);
945 3 : dn = ldb_dn_alloc_linearized(msg, newdn);
946 3 : if ( ! dn || ! ldb_dn_validate(msg->dn)) {
947 0 : goto failed;
948 : }
949 3 : if (ldb_msg_append_string(msg, IS_MAPPED, dn, LDB_FLAG_MOD_REPLACE) != 0) {
950 0 : goto failed;
951 : }
952 :
953 : /* Prepare request */
954 3 : ret = ldb_build_mod_req(&req, ldb,
955 : ac, msg, NULL,
956 : context, callback,
957 : ac->req);
958 3 : LDB_REQ_SET_LOCATION(req);
959 3 : if (ret != LDB_SUCCESS) {
960 0 : goto failed;
961 : }
962 3 : talloc_steal(req, msg);
963 :
964 3 : return req;
965 0 : failed:
966 0 : talloc_free(msg);
967 0 : return NULL;
968 : }
969 :
970 : /* Module initialization
971 : * ===================== */
972 :
973 :
974 : /* Builtin mappings for DNs and objectClasses */
975 : static const struct ldb_map_attribute builtin_attribute_maps[] = {
976 : {
977 : .local_name = "dn",
978 : .type = LDB_MAP_CONVERT,
979 : .u = {
980 : .convert = {
981 : .remote_name = "dn",
982 : .convert_local = ldb_dn_convert_local,
983 : .convert_remote = ldb_dn_convert_remote,
984 : },
985 : },
986 : },
987 : {
988 : .local_name = NULL,
989 : }
990 : };
991 :
992 : static const struct ldb_map_attribute objectclass_attribute_map = {
993 : .local_name = "objectClass",
994 : .type = LDB_MAP_GENERATE,
995 : .convert_operator = map_objectclass_convert_operator,
996 : .u = {
997 : .generate = {
998 : .remote_names = { "objectClass", NULL },
999 : .generate_local = map_objectclass_generate_local,
1000 : .generate_remote = map_objectclass_generate_remote,
1001 : },
1002 : },
1003 : };
1004 :
1005 :
1006 : /* Find the special 'MAP_DN_NAME' record and store local and remote
1007 : * base DNs in private data. */
1008 11 : static int map_init_dns(struct ldb_module *module, struct ldb_map_context *data, const char *name)
1009 : {
1010 11 : static const char * const attrs[] = { MAP_DN_FROM, MAP_DN_TO, NULL };
1011 11 : struct ldb_context *ldb;
1012 11 : struct ldb_dn *dn;
1013 11 : struct ldb_message *msg;
1014 11 : struct ldb_result *res;
1015 11 : int ret;
1016 :
1017 11 : if (!name) {
1018 0 : data->local_base_dn = NULL;
1019 0 : data->remote_base_dn = NULL;
1020 0 : return LDB_SUCCESS;
1021 : }
1022 :
1023 11 : ldb = ldb_module_get_ctx(module);
1024 :
1025 11 : dn = ldb_dn_new_fmt(data, ldb, "%s=%s", MAP_DN_NAME, name);
1026 11 : if ( ! ldb_dn_validate(dn)) {
1027 0 : ldb_debug(ldb, LDB_DEBUG_ERROR, "ldb_map: "
1028 : "Failed to construct '%s' DN!", MAP_DN_NAME);
1029 0 : return LDB_ERR_OPERATIONS_ERROR;
1030 : }
1031 :
1032 11 : ret = ldb_search(ldb, data, &res, dn, LDB_SCOPE_BASE, attrs, NULL);
1033 11 : talloc_free(dn);
1034 11 : if (ret != LDB_SUCCESS) {
1035 0 : return ret;
1036 : }
1037 11 : if (res->count == 0) {
1038 0 : ldb_debug(ldb, LDB_DEBUG_ERROR, "ldb_map: "
1039 : "No results for '%s=%s'!", MAP_DN_NAME, name);
1040 0 : talloc_free(res);
1041 0 : return LDB_ERR_CONSTRAINT_VIOLATION;
1042 : }
1043 11 : if (res->count > 1) {
1044 0 : ldb_debug(ldb, LDB_DEBUG_ERROR, "ldb_map: "
1045 : "Too many results for '%s=%s'!", MAP_DN_NAME, name);
1046 0 : talloc_free(res);
1047 0 : return LDB_ERR_CONSTRAINT_VIOLATION;
1048 : }
1049 :
1050 11 : msg = res->msgs[0];
1051 11 : data->local_base_dn = ldb_msg_find_attr_as_dn(ldb, data, msg, MAP_DN_FROM);
1052 11 : data->remote_base_dn = ldb_msg_find_attr_as_dn(ldb, data, msg, MAP_DN_TO);
1053 11 : talloc_free(res);
1054 :
1055 11 : return LDB_SUCCESS;
1056 : }
1057 :
1058 : /* Store attribute maps and objectClass maps in private data. */
1059 11 : static int map_init_maps(struct ldb_module *module, struct ldb_map_context *data,
1060 : const struct ldb_map_attribute *attrs,
1061 : const struct ldb_map_objectclass *ocls,
1062 : const char * const *wildcard_attributes)
1063 : {
1064 11 : unsigned int i, j, last;
1065 11 : last = 0;
1066 :
1067 : /* Count specified attribute maps */
1068 935 : for (i = 0; attrs[i].local_name; i++) /* noop */ ;
1069 : /* Count built-in attribute maps */
1070 22 : for (j = 0; builtin_attribute_maps[j].local_name; j++) /* noop */ ;
1071 :
1072 : /* Store list of attribute maps */
1073 11 : data->attribute_maps = talloc_array(data, struct ldb_map_attribute, i+j+2);
1074 11 : if (data->attribute_maps == NULL) {
1075 0 : map_oom(module);
1076 0 : return LDB_ERR_OPERATIONS_ERROR;
1077 : }
1078 :
1079 : /* Specified ones go first */
1080 935 : for (i = 0; attrs[i].local_name; i++) {
1081 924 : data->attribute_maps[last] = attrs[i];
1082 924 : last++;
1083 : }
1084 :
1085 : /* Built-in ones go last */
1086 22 : for (i = 0; builtin_attribute_maps[i].local_name; i++) {
1087 11 : data->attribute_maps[last] = builtin_attribute_maps[i];
1088 11 : last++;
1089 : }
1090 :
1091 11 : if (data->add_objectclass) {
1092 : /* ObjectClass one is very last, if required */
1093 0 : data->attribute_maps[last] = objectclass_attribute_map;
1094 0 : last++;
1095 11 : } else if (ocls) {
1096 11 : data->attribute_maps[last] = objectclass_convert_map;
1097 11 : last++;
1098 : }
1099 :
1100 : /* Ensure 'local_name == NULL' for the last entry */
1101 11 : memset(&data->attribute_maps[last], 0, sizeof(struct ldb_map_attribute));
1102 :
1103 : /* Store list of objectClass maps */
1104 11 : data->objectclass_maps = ocls;
1105 :
1106 11 : data->wildcard_attributes = wildcard_attributes;
1107 :
1108 11 : return LDB_SUCCESS;
1109 : }
1110 :
1111 : /* Initialize global private data. */
1112 11 : _PUBLIC_ int ldb_map_init(struct ldb_module *module, const struct ldb_map_attribute *attrs,
1113 : const struct ldb_map_objectclass *ocls,
1114 : const char * const *wildcard_attributes,
1115 : const char *add_objectclass,
1116 : const char *name)
1117 : {
1118 11 : struct map_private *data;
1119 11 : int ret;
1120 :
1121 : /* Prepare private data */
1122 11 : data = talloc_zero(module, struct map_private);
1123 11 : if (data == NULL) {
1124 0 : map_oom(module);
1125 0 : return LDB_ERR_OPERATIONS_ERROR;
1126 : }
1127 :
1128 11 : ldb_module_set_private(module, data);
1129 :
1130 11 : data->context = talloc_zero(data, struct ldb_map_context);
1131 11 : if (!data->context) {
1132 0 : map_oom(module);
1133 0 : return LDB_ERR_OPERATIONS_ERROR;
1134 : }
1135 :
1136 : /* Store local and remote baseDNs */
1137 11 : ret = map_init_dns(module, data->context, name);
1138 11 : if (ret != LDB_SUCCESS) {
1139 0 : talloc_free(data);
1140 0 : return ret;
1141 : }
1142 :
1143 11 : data->context->add_objectclass = add_objectclass;
1144 :
1145 : /* Store list of attribute and objectClass maps */
1146 11 : ret = map_init_maps(module, data->context, attrs, ocls, wildcard_attributes);
1147 11 : if (ret != LDB_SUCCESS) {
1148 0 : talloc_free(data);
1149 0 : return ret;
1150 : }
1151 :
1152 0 : return LDB_SUCCESS;
1153 : }
|