Coverage Report

Created: 2025-12-31 06:20

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/samba/lib/ldb/ldb_map/ldb_map.c
Line
Count
Source
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
const struct ldb_map_context *map_get_context(struct ldb_module *module)
105
0
{
106
0
  const struct map_private *data = talloc_get_type(ldb_module_get_private(module), struct map_private);
107
0
  return data->context;
108
0
}
109
110
/* Create a generic request context. */
111
struct map_context *map_init_context(struct ldb_module *module,
112
          struct ldb_request *req)
113
0
{
114
0
  struct ldb_context *ldb;
115
0
  struct map_context *ac;
116
117
0
  ldb = ldb_module_get_ctx(module);
118
119
0
  ac = talloc_zero(req, struct map_context);
120
0
  if (ac == NULL) {
121
0
    ldb_set_errstring(ldb, "Out of Memory");
122
0
    return NULL;
123
0
  }
124
125
0
  ac->module = module;
126
0
  ac->req = req;
127
128
0
  return ac;
129
0
}
130
131
/* Dealing with DNs for different partitions
132
 * ========================================= */
133
134
/* Check whether any data should be stored in the local partition. */
135
bool map_check_local_db(struct ldb_module *module)
136
0
{
137
0
  const struct ldb_map_context *data = map_get_context(module);
138
139
0
  if (!data->remote_base_dn || !data->local_base_dn) {
140
0
    return false;
141
0
  }
142
143
0
  return true;
144
0
}
145
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)
148
0
{
149
0
  struct ldb_dn *new_dn;
150
151
0
  new_dn = ldb_dn_copy(mem_ctx, dn);
152
0
  if ( ! ldb_dn_validate(new_dn)) {
153
0
    talloc_free(new_dn);
154
0
    return NULL;
155
0
  }
156
157
  /* may be we don't need to rebase at all */
158
0
  if ( ! data->remote_base_dn || ! data->local_base_dn) {
159
0
    return new_dn;
160
0
  }
161
162
0
  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
0
  }
166
167
0
  if ( ! ldb_dn_add_base(new_dn, data->local_base_dn)) {
168
0
    talloc_free(new_dn);
169
0
    return NULL;
170
0
  }
171
172
0
  return new_dn;
173
0
}
174
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)
177
0
{
178
0
  struct ldb_dn *new_dn;
179
180
0
  new_dn = ldb_dn_copy(mem_ctx, dn);
181
0
  if ( ! ldb_dn_validate(new_dn)) {
182
0
    talloc_free(new_dn);
183
0
    return NULL;
184
0
  }
185
186
  /* may be we don't need to rebase at all */
187
0
  if ( ! data->remote_base_dn || ! data->local_base_dn) {
188
0
    return new_dn;
189
0
  }
190
191
0
  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
0
  }
195
196
0
  if ( ! ldb_dn_add_base(new_dn, data->remote_base_dn)) {
197
0
    talloc_free(new_dn);
198
0
    return NULL;
199
0
  }
200
201
0
  return new_dn;
202
0
}
203
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)
207
0
{
208
0
  const struct ldb_map_context *data = map_get_context(module);
209
0
  struct ldb_context *ldb;
210
0
  struct ldb_message *msg;
211
212
0
  ldb = ldb_module_get_ctx(module);
213
214
0
  switch (request->operation) {
215
0
  case LDB_SEARCH:
216
0
    if (request->op.search.base) {
217
0
      request->op.search.base = ldb_dn_rebase_remote(request, data, request->op.search.base);
218
0
    } else {
219
0
      request->op.search.base = data->remote_base_dn;
220
      /* TODO: adjust scope? */
221
0
    }
222
0
    break;
223
224
0
  case LDB_ADD:
225
0
    msg = ldb_msg_copy_shallow(request, request->op.add.message);
226
0
    if (msg == NULL) {
227
0
      return LDB_ERR_OPERATIONS_ERROR;
228
0
    }
229
0
    msg->dn = ldb_dn_rebase_remote(msg, data, msg->dn);
230
0
    request->op.add.message = msg;
231
0
    break;
232
233
0
  case LDB_MODIFY:
234
0
    msg = ldb_msg_copy_shallow(request, request->op.mod.message);
235
0
    if (msg == NULL) {
236
0
      return LDB_ERR_OPERATIONS_ERROR;
237
0
    }
238
0
    msg->dn = ldb_dn_rebase_remote(msg, data, msg->dn);
239
0
    request->op.mod.message = msg;
240
0
    break;
241
242
0
  case LDB_DELETE:
243
0
    request->op.del.dn = ldb_dn_rebase_remote(request, data, request->op.del.dn);
244
0
    break;
245
246
0
  case LDB_RENAME:
247
0
    request->op.rename.olddn = ldb_dn_rebase_remote(request, data, request->op.rename.olddn);
248
0
    request->op.rename.newdn = ldb_dn_rebase_remote(request, data, request->op.rename.newdn);
249
0
    break;
250
251
0
  default:
252
0
    ldb_debug(ldb, LDB_DEBUG_ERROR, "ldb_map: "
253
0
        "Invalid remote request!");
254
0
    return LDB_ERR_OPERATIONS_ERROR;
255
0
  }
256
257
0
  return ldb_next_request(module, request);
258
0
}
259
260
261
/* Finding mappings for attributes and objectClasses
262
 * ================================================= */
263
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)
266
0
{
267
0
  unsigned int i;
268
269
0
  for (i = 0; data->objectclass_maps && data->objectclass_maps[i].local_name; i++) {
270
0
    if (ldb_attr_cmp(data->objectclass_maps[i].local_name, name) == 0) {
271
0
      return &data->objectclass_maps[i];
272
0
    }
273
0
  }
274
275
0
  return NULL;
276
0
}
277
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)
280
0
{
281
0
  unsigned int i;
282
283
0
  for (i = 0; data->objectclass_maps && data->objectclass_maps[i].remote_name; i++) {
284
0
    if (ldb_attr_cmp(data->objectclass_maps[i].remote_name, name) == 0) {
285
0
      return &data->objectclass_maps[i];
286
0
    }
287
0
  }
288
289
0
  return NULL;
290
0
}
291
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)
294
0
{
295
0
  unsigned int i;
296
297
0
  for (i = 0; data->attribute_maps[i].local_name; i++) {
298
0
    if (ldb_attr_cmp(data->attribute_maps[i].local_name, name) == 0) {
299
0
      return &data->attribute_maps[i];
300
0
    }
301
0
  }
302
0
  for (i = 0; data->attribute_maps[i].local_name; i++) {
303
0
    if (ldb_attr_cmp(data->attribute_maps[i].local_name, "*") == 0) {
304
0
      return &data->attribute_maps[i];
305
0
    }
306
0
  }
307
308
0
  return NULL;
309
0
}
310
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)
313
0
{
314
0
  const struct ldb_map_attribute *map;
315
0
  const struct ldb_map_attribute *wildcard = NULL;
316
0
  unsigned int i, j;
317
318
0
  for (i = 0; data->attribute_maps[i].local_name; i++) {
319
0
    map = &data->attribute_maps[i];
320
0
    if (ldb_attr_cmp(map->local_name, "*") == 0) {
321
0
      wildcard = &data->attribute_maps[i];
322
0
    }
323
324
0
    switch (map->type) {
325
0
    case LDB_MAP_IGNORE:
326
0
      break;
327
328
0
    case LDB_MAP_KEEP:
329
0
      if (ldb_attr_cmp(map->local_name, name) == 0) {
330
0
        return map;
331
0
      }
332
0
      break;
333
334
0
    case LDB_MAP_RENAME:
335
0
    case LDB_MAP_RENDROP:
336
0
    case LDB_MAP_CONVERT:
337
0
      if (ldb_attr_cmp(map->u.rename.remote_name, name) == 0) {
338
0
        return map;
339
0
      }
340
0
      break;
341
342
0
    case LDB_MAP_GENERATE:
343
0
      for (j = 0; map->u.generate.remote_names[j]; j++) {
344
0
        if (ldb_attr_cmp(map->u.generate.remote_names[j], name) == 0) {
345
0
          return map;
346
0
        }
347
0
      }
348
0
      break;
349
0
    }
350
0
  }
351
352
  /* We didn't find it, so return the wildcard record if one was configured */
353
0
  return wildcard;
354
0
}
355
356
357
/* Mapping attributes
358
 * ================== */
359
360
/* Check whether an attribute will be mapped into the remote partition. */
361
bool map_attr_check_remote(const struct ldb_map_context *data, const char *attr)
362
0
{
363
0
  const struct ldb_map_attribute *map = map_attr_find_local(data, attr);
364
365
0
  if (map == NULL) {
366
0
    return false;
367
0
  }
368
0
  if (map->type == LDB_MAP_IGNORE) {
369
0
    return false;
370
0
  }
371
372
0
  return true;
373
0
}
374
375
/* Map an attribute name into the remote partition. */
376
const char *map_attr_map_local(void *mem_ctx, const struct ldb_map_attribute *map, const char *attr)
377
0
{
378
0
  if (map == NULL) {
379
0
    return talloc_strdup(mem_ctx, attr);
380
0
  }
381
382
0
  switch (map->type) {
383
0
  case LDB_MAP_KEEP:
384
0
    return talloc_strdup(mem_ctx, attr);
385
386
0
  case LDB_MAP_RENAME:
387
0
  case LDB_MAP_RENDROP:
388
0
  case LDB_MAP_CONVERT:
389
0
    return talloc_strdup(mem_ctx, map->u.rename.remote_name);
390
391
0
  default:
392
0
    return NULL;
393
0
  }
394
0
}
395
396
/* Map an attribute name back into the local partition. */
397
const char *map_attr_map_remote(void *mem_ctx, const struct ldb_map_attribute *map, const char *attr)
398
0
{
399
0
  if (map == NULL) {
400
0
    return talloc_strdup(mem_ctx, attr);
401
0
  }
402
403
0
  if (map->type == LDB_MAP_KEEP) {
404
0
    return talloc_strdup(mem_ctx, attr);
405
0
  }
406
407
0
  return talloc_strdup(mem_ctx, map->local_name);
408
0
}
409
410
411
/* Merge two lists of attributes into a single one. */
412
int map_attrs_merge(struct ldb_module *module, void *mem_ctx, 
413
        const char ***attrs, const char * const *more_attrs)
414
0
{
415
0
  unsigned int i, j, k;
416
417
0
  for (i = 0; *attrs && (*attrs)[i]; i++) /* noop */ ;
418
0
  for (j = 0; more_attrs && more_attrs[j]; j++) /* noop */ ;
419
  
420
0
  *attrs = talloc_realloc(mem_ctx, *attrs, const char *, i+j+1);
421
0
  if (*attrs == NULL) {
422
0
    map_oom(module);
423
0
    return -1;
424
0
  }
425
426
0
  for (k = 0; k < j; k++) {
427
0
    (*attrs)[i + k] = more_attrs[k];
428
0
  }
429
430
0
  (*attrs)[i+k] = NULL;
431
432
0
  return 0;
433
0
}
434
435
/* Mapping ldb values
436
 * ================== */
437
438
/* Map an ldb value into the remote partition. */
439
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
0
{
442
0
  if (map && (map->type == LDB_MAP_CONVERT) && (map->u.convert.convert_local)) {
443
0
    return map->u.convert.convert_local(module, mem_ctx, val);
444
0
  }
445
446
0
  return ldb_val_dup(mem_ctx, val);
447
0
}
448
449
/* Map an ldb value back into the local partition. */
450
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
0
{
453
0
  if (map && (map->type == LDB_MAP_CONVERT) && (map->u.convert.convert_remote)) {
454
0
    return map->u.convert.convert_remote(module, mem_ctx, val);
455
0
  }
456
457
0
  return ldb_val_dup(mem_ctx, val);
458
0
}
459
460
461
/* Mapping DNs
462
 * =========== */
463
464
/* Check whether a DN is below the local baseDN. */
465
bool ldb_dn_check_local(struct ldb_module *module, struct ldb_dn *dn)
466
0
{
467
0
  const struct ldb_map_context *data = map_get_context(module);
468
469
0
  if (!data->local_base_dn) {
470
0
    return true;
471
0
  }
472
473
0
  return ldb_dn_compare_base(data->local_base_dn, dn) == 0;
474
0
}
475
476
/* Map a DN into the remote partition. */
477
struct ldb_dn *ldb_dn_map_local(struct ldb_module *module, void *mem_ctx, struct ldb_dn *dn)
478
0
{
479
0
  const struct ldb_map_context *data = map_get_context(module);
480
0
  struct ldb_context *ldb;
481
0
  struct ldb_dn *newdn;
482
0
  const struct ldb_map_attribute *map;
483
0
  enum ldb_map_attr_type map_type;
484
0
  const char *name;
485
0
  struct ldb_val value;
486
0
  int i, ret;
487
488
0
  if (dn == NULL) {
489
0
    return NULL;
490
0
  }
491
492
0
  ldb = ldb_module_get_ctx(module);
493
494
0
  newdn = ldb_dn_copy(mem_ctx, dn);
495
0
  if (newdn == NULL) {
496
0
    map_oom(module);
497
0
    return NULL;
498
0
  }
499
500
  /* For each RDN, map the component name and possibly the value */
501
0
  for (i = 0; i < ldb_dn_get_comp_num(newdn); i++) {
502
0
    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
0
    if (map == NULL) {
506
0
      map_type = LDB_MAP_KEEP;
507
0
    } else {
508
0
      map_type = map->type;
509
0
    }
510
511
0
    switch (map_type) {
512
0
    case LDB_MAP_IGNORE:
513
0
    case LDB_MAP_GENERATE:
514
0
      ldb_debug(ldb, LDB_DEBUG_ERROR, "ldb_map: "
515
0
          "LDB_MAP_IGNORE/LDB_MAP_GENERATE attribute '%s' "
516
0
          "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
0
            "'convert_local' not set for attribute '%s' "
523
0
            "used in DN!", ldb_dn_get_component_name(dn, i));
524
0
        goto failed;
525
0
      }
526
527
0
      FALL_THROUGH;
528
0
    case LDB_MAP_KEEP:
529
0
    case LDB_MAP_RENAME:
530
0
    case LDB_MAP_RENDROP:
531
0
      name = map_attr_map_local(newdn, map, ldb_dn_get_component_name(dn, i));
532
0
      if (name == NULL) goto failed;
533
534
0
      value = ldb_val_map_local(module, newdn, map, ldb_dn_get_component_val(dn, i));
535
0
      if (value.data == NULL) goto failed;
536
537
0
      ret = ldb_dn_set_component(newdn, i, name, value);
538
0
      if (ret != LDB_SUCCESS) {
539
0
        goto failed;
540
0
      }
541
542
0
      break;
543
0
    }
544
0
  }
545
546
0
  return newdn;
547
548
0
failed:
549
0
  talloc_free(newdn);
550
0
  return NULL;
551
0
}
552
553
/* Map a DN into the local partition. */
554
struct ldb_dn *ldb_dn_map_remote(struct ldb_module *module, void *mem_ctx, struct ldb_dn *dn)
555
0
{
556
0
  const struct ldb_map_context *data = map_get_context(module);
557
0
  struct ldb_context *ldb;
558
0
  struct ldb_dn *newdn;
559
0
  const struct ldb_map_attribute *map;
560
0
  enum ldb_map_attr_type map_type;
561
0
  const char *name;
562
0
  struct ldb_val value;
563
0
  int i, ret;
564
565
0
  if (dn == NULL) {
566
0
    return NULL;
567
0
  }
568
569
0
  ldb = ldb_module_get_ctx(module);
570
571
0
  newdn = ldb_dn_copy(mem_ctx, dn);
572
0
  if (newdn == NULL) {
573
0
    map_oom(module);
574
0
    return NULL;
575
0
  }
576
577
  /* For each RDN, map the component name and possibly the value */
578
0
  for (i = 0; i < ldb_dn_get_comp_num(newdn); i++) {
579
0
    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
0
    if (map == NULL) {
583
0
      map_type = LDB_MAP_KEEP;
584
0
    } else {
585
0
      map_type = map->type;
586
0
    }
587
588
0
    switch (map_type) {
589
0
    case LDB_MAP_IGNORE:
590
0
    case LDB_MAP_GENERATE:
591
0
      ldb_debug(ldb, LDB_DEBUG_ERROR, "ldb_map: "
592
0
          "LDB_MAP_IGNORE/LDB_MAP_GENERATE attribute '%s' "
593
0
          "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
0
            "'convert_remote' not set for attribute '%s' "
600
0
            "used in DN!", ldb_dn_get_component_name(dn, i));
601
0
        goto failed;
602
0
      }
603
604
0
      FALL_THROUGH;
605
0
    case LDB_MAP_KEEP:
606
0
    case LDB_MAP_RENAME:
607
0
    case LDB_MAP_RENDROP:
608
0
      name = map_attr_map_remote(newdn, map, ldb_dn_get_component_name(dn, i));
609
0
      if (name == NULL) goto failed;
610
611
0
      value = ldb_val_map_remote(module, newdn, map, ldb_dn_get_component_val(dn, i));
612
0
      if (value.data == NULL) goto failed;
613
614
0
      ret = ldb_dn_set_component(newdn, i, name, value);
615
0
      if (ret != LDB_SUCCESS) {
616
0
        goto failed;
617
0
      }
618
619
0
      break;
620
0
    }
621
0
  }
622
623
0
  return newdn;
624
625
0
failed:
626
0
  talloc_free(newdn);
627
0
  return NULL;
628
0
}
629
630
/* Map a DN and its base into the local partition. */
631
/* TODO: This should not be required with GUIDs. */
632
struct ldb_dn *ldb_dn_map_rebase_remote(struct ldb_module *module, void *mem_ctx, struct ldb_dn *dn)
633
0
{
634
0
  const struct ldb_map_context *data = map_get_context(module);
635
0
  struct ldb_dn *dn1, *dn2;
636
637
0
  dn1 = ldb_dn_rebase_local(mem_ctx, data, dn);
638
0
  dn2 = ldb_dn_map_remote(module, mem_ctx, dn1);
639
640
0
  talloc_free(dn1);
641
0
  return dn2;
642
0
}
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
static struct ldb_val ldb_dn_convert_local(struct ldb_module *module, void *mem_ctx, const struct ldb_val *val)
650
0
{
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
0
  }
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
0
  }
672
0
  talloc_free(newdn);
673
674
0
  return newval;
675
0
}
676
677
/* Map a DN contained in an ldb value into the local partition. */
678
static struct ldb_val ldb_dn_convert_remote(struct ldb_module *module, void *mem_ctx, const struct ldb_val *val)
679
0
{
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
0
  }
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
0
  }
701
0
  talloc_free(newdn);
702
703
0
  return newval;
704
0
}
705
706
/* Map an objectClass into the remote partition. */
707
static struct ldb_val map_objectclass_convert_local(struct ldb_module *module, void *mem_ctx, const struct ldb_val *val)
708
0
{
709
0
  const struct ldb_map_context *data = map_get_context(module);
710
0
  const char *name = (char *)val->data;
711
0
  const struct ldb_map_objectclass *map = map_objectclass_find_local(data, name);
712
0
  struct ldb_val newval;
713
714
0
  if (map) {
715
0
    newval.data = (uint8_t*)talloc_strdup(mem_ctx, map->remote_name);
716
0
    newval.length = strlen((char *)newval.data);
717
0
    return newval;
718
0
  }
719
720
0
  return ldb_val_dup(mem_ctx, val);
721
0
}
722
723
/* Generate a remote message with a mapped objectClass. */
724
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
0
{
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
0
  }
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
0
  }
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
0
  }
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
0
    }
767
0
  }
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
0
  } else {
776
0
    el->num_values--;
777
0
  }
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
0
  }
785
0
}
786
787
/* Map an objectClass into the local partition. */
788
static struct ldb_val map_objectclass_convert_remote(struct ldb_module *module, void *mem_ctx, const struct ldb_val *val)
789
0
{
790
0
  const struct ldb_map_context *data = map_get_context(module);
791
0
  const char *name = (char *)val->data;
792
0
  const struct ldb_map_objectclass *map = map_objectclass_find_remote(data, name);
793
0
  struct ldb_val newval;
794
795
0
  if (map) {
796
0
    newval.data = (uint8_t*)talloc_strdup(mem_ctx, map->local_name);
797
0
    newval.length = strlen((char *)newval.data);
798
0
    return newval;
799
0
  }
800
801
0
  return ldb_val_dup(mem_ctx, val);
802
0
}
803
804
/* Generate a local message with a mapped objectClass. */
805
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
0
{
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
0
  }
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
0
  }
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
0
  }
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
0
  }
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
0
    }
857
0
  }
858
859
0
  return el;
860
0
}
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
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
0
{
881
  
882
0
  return map_subtree_collect_remote_simple(module, mem_ctx, new, tree, &objectclass_convert_map);
883
0
}
884
885
/* Auxiliary request construction
886
 * ============================== */
887
888
/* Build a request to search a record by its DN. */
889
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
0
{
891
0
  struct ldb_parse_tree *search_tree;
892
0
  struct ldb_context *ldb;
893
0
  struct ldb_request *req;
894
0
  int ret;
895
896
0
  ldb = ldb_module_get_ctx(ac->module);
897
898
0
  if (tree) {
899
0
    search_tree = tree;
900
0
  } else {
901
0
    search_tree = ldb_parse_tree(ac, NULL);
902
0
    if (search_tree == NULL) {
903
0
      return NULL;
904
0
    }
905
0
  }
906
907
0
  ret = ldb_build_search_req_ex(&req, ldb, ac,
908
0
          dn, LDB_SCOPE_BASE,
909
0
          search_tree, attrs,
910
0
          NULL,
911
0
          context, callback,
912
0
          ac->req);
913
0
  LDB_REQ_SET_LOCATION(req);
914
0
  if (ret != LDB_SUCCESS) {
915
0
    return NULL;
916
0
  }
917
918
0
  return req;
919
0
}
920
921
/* Build a request to update the 'IS_MAPPED' attribute */
922
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
0
{
928
0
  struct ldb_context *ldb;
929
0
  struct ldb_request *req;
930
0
  struct ldb_message *msg;
931
0
  const char *dn;
932
0
  int ret;
933
934
0
  ldb = ldb_module_get_ctx(ac->module);
935
936
  /* Prepare message */
937
0
  msg = ldb_msg_new(ac);
938
0
  if (msg == NULL) {
939
0
    map_oom(ac->module);
940
0
    return NULL;
941
0
  }
942
943
  /* Update local 'IS_MAPPED' to the new remote DN */
944
0
  msg->dn = ldb_dn_copy(msg, olddn);
945
0
  dn = ldb_dn_alloc_linearized(msg, newdn);
946
0
  if ( ! dn || ! ldb_dn_validate(msg->dn)) {
947
0
    goto failed;
948
0
  }
949
0
  if (ldb_msg_append_string(msg, IS_MAPPED, dn, LDB_FLAG_MOD_REPLACE) != 0) {
950
0
    goto failed;
951
0
  }
952
953
  /* Prepare request */
954
0
  ret = ldb_build_mod_req(&req, ldb,
955
0
        ac, msg, NULL,
956
0
        context, callback,
957
0
        ac->req);
958
0
  LDB_REQ_SET_LOCATION(req);
959
0
  if (ret != LDB_SUCCESS) {
960
0
    goto failed;
961
0
  }
962
0
  talloc_steal(req, msg);
963
964
0
  return req;
965
0
failed:
966
0
  talloc_free(msg);
967
0
  return NULL;
968
0
}
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
static int map_init_dns(struct ldb_module *module, struct ldb_map_context *data, const char *name)
1009
0
{
1010
0
  static const char * const attrs[] = { MAP_DN_FROM, MAP_DN_TO, NULL };
1011
0
  struct ldb_context *ldb;
1012
0
  struct ldb_dn *dn;
1013
0
  struct ldb_message *msg;
1014
0
  struct ldb_result *res;
1015
0
  int ret;
1016
1017
0
  if (!name) {
1018
0
    data->local_base_dn = NULL;
1019
0
    data->remote_base_dn = NULL;
1020
0
    return LDB_SUCCESS;
1021
0
  }
1022
1023
0
  ldb = ldb_module_get_ctx(module);
1024
1025
0
  dn = ldb_dn_new_fmt(data, ldb, "%s=%s", MAP_DN_NAME, name);
1026
0
  if ( ! ldb_dn_validate(dn)) {
1027
0
    ldb_debug(ldb, LDB_DEBUG_ERROR, "ldb_map: "
1028
0
        "Failed to construct '%s' DN!", MAP_DN_NAME);
1029
0
    return LDB_ERR_OPERATIONS_ERROR;
1030
0
  }
1031
1032
0
  ret = ldb_search(ldb, data, &res, dn, LDB_SCOPE_BASE, attrs, NULL);
1033
0
  talloc_free(dn);
1034
0
  if (ret != LDB_SUCCESS) {
1035
0
    return ret;
1036
0
  }
1037
0
  if (res->count == 0) {
1038
0
    ldb_debug(ldb, LDB_DEBUG_ERROR, "ldb_map: "
1039
0
        "No results for '%s=%s'!", MAP_DN_NAME, name);
1040
0
    talloc_free(res);
1041
0
    return LDB_ERR_CONSTRAINT_VIOLATION;
1042
0
  }
1043
0
  if (res->count > 1) {
1044
0
    ldb_debug(ldb, LDB_DEBUG_ERROR, "ldb_map: "
1045
0
        "Too many results for '%s=%s'!", MAP_DN_NAME, name);
1046
0
    talloc_free(res);
1047
0
    return LDB_ERR_CONSTRAINT_VIOLATION;
1048
0
  }
1049
1050
0
  msg = res->msgs[0];
1051
0
  data->local_base_dn = ldb_msg_find_attr_as_dn(ldb, data, msg, MAP_DN_FROM);
1052
0
  data->remote_base_dn = ldb_msg_find_attr_as_dn(ldb, data, msg, MAP_DN_TO);
1053
0
  talloc_free(res);
1054
1055
0
  return LDB_SUCCESS;
1056
0
}
1057
1058
/* Store attribute maps and objectClass maps in private data. */
1059
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
0
{
1064
0
  unsigned int i, j, last;
1065
0
  last = 0;
1066
1067
  /* Count specified attribute maps */
1068
0
  for (i = 0; attrs[i].local_name; i++) /* noop */ ;
1069
  /* Count built-in attribute maps */
1070
0
  for (j = 0; builtin_attribute_maps[j].local_name; j++) /* noop */ ;
1071
1072
  /* Store list of attribute maps */
1073
0
  data->attribute_maps = talloc_array(data, struct ldb_map_attribute, i+j+2);
1074
0
  if (data->attribute_maps == NULL) {
1075
0
    map_oom(module);
1076
0
    return LDB_ERR_OPERATIONS_ERROR;
1077
0
  }
1078
1079
  /* Specified ones go first */
1080
0
  for (i = 0; attrs[i].local_name; i++) {
1081
0
    data->attribute_maps[last] = attrs[i];
1082
0
    last++;
1083
0
  }
1084
1085
  /* Built-in ones go last */
1086
0
  for (i = 0; builtin_attribute_maps[i].local_name; i++) {
1087
0
    data->attribute_maps[last] = builtin_attribute_maps[i];
1088
0
    last++;
1089
0
  }
1090
1091
0
  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
0
  } else if (ocls) {
1096
0
    data->attribute_maps[last] = objectclass_convert_map;
1097
0
    last++;
1098
0
  }
1099
1100
  /* Ensure 'local_name == NULL' for the last entry */
1101
0
  memset(&data->attribute_maps[last], 0, sizeof(struct ldb_map_attribute));
1102
1103
  /* Store list of objectClass maps */
1104
0
  data->objectclass_maps = ocls;
1105
1106
0
  data->wildcard_attributes = wildcard_attributes;
1107
1108
0
  return LDB_SUCCESS;
1109
0
}
1110
1111
/* Initialize global private data. */
1112
_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
0
{
1118
0
  struct map_private *data;
1119
0
  int ret;
1120
1121
  /* Prepare private data */
1122
0
  data = talloc_zero(module, struct map_private);
1123
0
  if (data == NULL) {
1124
0
    map_oom(module);
1125
0
    return LDB_ERR_OPERATIONS_ERROR;
1126
0
  }
1127
1128
0
  ldb_module_set_private(module, data);
1129
1130
0
  data->context = talloc_zero(data, struct ldb_map_context);
1131
0
  if (!data->context) {
1132
0
    map_oom(module);
1133
0
    return LDB_ERR_OPERATIONS_ERROR;   
1134
0
  }
1135
1136
  /* Store local and remote baseDNs */
1137
0
  ret = map_init_dns(module, data->context, name);
1138
0
  if (ret != LDB_SUCCESS) {
1139
0
    talloc_free(data);
1140
0
    return ret;
1141
0
  }
1142
1143
0
  data->context->add_objectclass = add_objectclass;
1144
1145
  /* Store list of attribute and objectClass maps */
1146
0
  ret = map_init_maps(module, data->context, attrs, ocls, wildcard_attributes);
1147
0
  if (ret != LDB_SUCCESS) {
1148
0
    talloc_free(data);
1149
0
    return ret;
1150
0
  }
1151
1152
0
  return LDB_SUCCESS;
1153
0
}