Coverage Report

Created: 2025-07-23 07:04

/src/samba/source4/dsdb/common/dsdb_dn.c
Line
Count
Source (jump to first uncovered line)
1
/* 
2
   Unix SMB/CIFS implementation.
3
   Samba utility functions
4
5
   Copyright (C) Andrew Tridgell 2009
6
   Copyright (C) Andrew Bartlett <abartlet@samba.org> 2009
7
8
   This program is free software; you can redistribute it and/or modify
9
   it under the terms of the GNU General Public License as published by
10
   the Free Software Foundation; either version 3 of the License, or
11
   (at your option) any later version.
12
13
   This program is distributed in the hope that it will be useful,
14
   but WITHOUT ANY WARRANTY; without even the implied warranty of
15
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16
   GNU General Public License for more details.
17
18
   You should have received a copy of the GNU General Public License
19
   along with this program.  If not, see <http://www.gnu.org/licenses/>.
20
*/
21
22
#include "includes.h"
23
#include "dsdb/samdb/samdb.h"
24
#include <ldb_module.h>
25
#include "librpc/ndr/libndr.h"
26
#include "libcli/security/dom_sid.h"
27
#include "lib/util/smb_strtox.h"
28
29
enum dsdb_dn_format dsdb_dn_oid_to_format(const char *oid) 
30
0
{
31
0
  if (strcmp(oid, LDB_SYNTAX_DN) == 0) {
32
0
    return DSDB_NORMAL_DN;
33
0
  } else if (strcmp(oid, DSDB_SYNTAX_BINARY_DN) == 0) {
34
0
    return DSDB_BINARY_DN;
35
0
  } else if (strcmp(oid, DSDB_SYNTAX_STRING_DN) == 0) {
36
0
    return DSDB_STRING_DN;
37
0
  } else if (strcmp(oid, DSDB_SYNTAX_OR_NAME) == 0) {
38
0
    return DSDB_NORMAL_DN;
39
0
  } else {
40
0
    return DSDB_INVALID_DN;
41
0
  }
42
0
}
43
44
static struct dsdb_dn *dsdb_dn_construct_internal(TALLOC_CTX *mem_ctx, 
45
              struct ldb_dn *dn, 
46
              DATA_BLOB extra_part, 
47
              enum dsdb_dn_format dn_format, 
48
              const char *oid) 
49
0
{
50
0
  struct dsdb_dn *dsdb_dn = NULL;
51
52
0
  switch (dn_format) {
53
0
  case DSDB_BINARY_DN:
54
0
  case DSDB_STRING_DN:
55
0
    break;
56
0
  case DSDB_NORMAL_DN:
57
0
    if (extra_part.length != 0) {
58
0
      errno = EINVAL;
59
0
      return NULL;
60
0
    }
61
0
    break;
62
0
  case DSDB_INVALID_DN:
63
0
  default:
64
0
    errno = EINVAL;
65
0
    return NULL;
66
0
  }
67
68
0
  dsdb_dn = talloc(mem_ctx, struct dsdb_dn);
69
0
  if (!dsdb_dn) {
70
0
    errno = ENOMEM;
71
0
    return NULL;
72
0
  }
73
0
  dsdb_dn->dn = talloc_steal(dsdb_dn, dn);
74
0
  dsdb_dn->extra_part = extra_part;
75
0
  dsdb_dn->dn_format = dn_format;
76
77
0
  dsdb_dn->oid = oid;
78
0
  talloc_steal(dsdb_dn, extra_part.data);
79
0
  return dsdb_dn;
80
0
}
81
82
struct dsdb_dn *dsdb_dn_construct(TALLOC_CTX *mem_ctx, struct ldb_dn *dn, DATA_BLOB extra_part, 
83
          const char *oid) 
84
0
{
85
0
  enum dsdb_dn_format dn_format = dsdb_dn_oid_to_format(oid);
86
0
  return dsdb_dn_construct_internal(mem_ctx, dn, extra_part, dn_format, oid);
87
0
}
88
89
struct dsdb_dn *dsdb_dn_parse_trusted(TALLOC_CTX *mem_ctx, struct ldb_context *ldb, 
90
              const struct ldb_val *dn_blob, const char *dn_oid)
91
0
{
92
0
  struct dsdb_dn *dsdb_dn;
93
0
  struct ldb_dn *dn;
94
0
  size_t len;
95
0
  TALLOC_CTX *tmp_ctx;
96
0
  char *p1;
97
0
  char *p2;
98
0
  uint32_t blen;
99
0
  struct ldb_val bval;
100
0
  struct ldb_val dval;
101
0
  char *dn_str;
102
0
  int error = 0;
103
104
0
  enum dsdb_dn_format dn_format = dsdb_dn_oid_to_format(dn_oid);
105
106
0
  if (dn_blob == NULL || dn_blob->data == NULL || dn_blob->length == 0) {
107
0
    return NULL;
108
0
  }
109
110
0
  switch (dn_format) {
111
0
  case DSDB_INVALID_DN:
112
0
    return NULL;
113
0
  case DSDB_NORMAL_DN:
114
0
  {
115
0
    dn = ldb_dn_from_ldb_val(mem_ctx, ldb, dn_blob);
116
0
    if (!dn) {
117
0
      talloc_free(dn);
118
0
      return NULL;
119
0
    }
120
0
    return dsdb_dn_construct_internal(mem_ctx, dn, data_blob_null, dn_format, dn_oid);
121
0
  }
122
0
  case DSDB_BINARY_DN:
123
0
    if (dn_blob->length < 2 || dn_blob->data[0] != 'B' || dn_blob->data[1] != ':') {
124
0
      return NULL;
125
0
    }
126
0
    break;
127
0
  case DSDB_STRING_DN:
128
0
    if (dn_blob->length < 2 || dn_blob->data[0] != 'S' || dn_blob->data[1] != ':') {
129
0
      return NULL;
130
0
    }
131
0
    break;
132
0
  default:
133
0
    return NULL;
134
0
  }
135
136
0
  if (strlen((const char*)dn_blob->data) != dn_blob->length) {
137
    /* The RDN must not contain a character with value 0x0 */
138
0
    return NULL;
139
0
  }
140
141
0
  tmp_ctx = talloc_new(mem_ctx);
142
0
  if (tmp_ctx == NULL) {
143
0
    return NULL;
144
0
  }
145
146
0
  len = dn_blob->length - 2;
147
0
  p1 = talloc_strndup(tmp_ctx, (const char *)dn_blob->data + 2, len);
148
0
  if (!p1) {
149
0
    goto failed;
150
0
  }
151
152
0
  errno = 0;
153
0
  blen = smb_strtoul(p1, &p2, 10, &error, SMB_STR_STANDARD);
154
0
  if (error != 0) {
155
0
    DEBUG(10, (__location__ ": failed\n"));
156
0
    goto failed;
157
0
  }
158
0
  if (p2 == NULL) {
159
0
    DEBUG(10, (__location__ ": failed\n"));
160
0
    goto failed;
161
0
  }
162
0
  if (p2[0] != ':') {
163
0
    DEBUG(10, (__location__ ": failed\n"));
164
0
    goto failed;
165
0
  }
166
0
  len -= PTR_DIFF(p2,p1);//???
167
0
  p1 = p2+1;
168
0
  len--;
169
    
170
0
  if (blen >= len) {
171
0
    DEBUG(10, (__location__ ": blen=%u len=%u\n", (unsigned)blen, (unsigned)len));
172
0
    goto failed;
173
0
  }
174
    
175
0
  p2 = p1 + blen;
176
0
  if (p2[0] != ':') {
177
0
    DEBUG(10, (__location__ ": %s", p2));
178
0
    goto failed;
179
0
  }
180
0
  dn_str = p2+1;
181
    
182
    
183
0
  switch (dn_format) {
184
0
  case DSDB_BINARY_DN:
185
0
    if ((blen % 2 != 0)) {
186
0
      DEBUG(10, (__location__ ": blen=%u - not an even number\n", (unsigned)blen));
187
0
      goto failed;
188
0
    }
189
    
190
0
    if (blen >= 2) {
191
0
      bval.length = (blen/2)+1;
192
0
      bval.data = talloc_size(tmp_ctx, bval.length);
193
0
      if (bval.data == NULL) {
194
0
        DEBUG(10, (__location__ ": err\n"));
195
0
        goto failed;
196
0
      }
197
0
      bval.data[bval.length-1] = 0;
198
    
199
0
      bval.length = strhex_to_str((char *)bval.data, bval.length,
200
0
                p1, blen);
201
0
      if (bval.length != (blen / 2)) {
202
0
        DEBUG(10, (__location__ ": non hexadecimal characters found in binary prefix\n"));
203
0
        goto failed;
204
0
      }
205
0
    } else {
206
0
      bval = data_blob_null;
207
0
    }
208
209
0
    break;
210
0
  case DSDB_STRING_DN:
211
0
    bval = data_blob(p1, blen);
212
0
    break;
213
0
  default:
214
    /* never reached */
215
0
    return NULL;
216
0
  }
217
  
218
219
0
  dval.data = (uint8_t *)dn_str;
220
0
  dval.length = strlen(dn_str);
221
    
222
0
  dn = ldb_dn_from_ldb_val(tmp_ctx, ldb, &dval);
223
0
  if (!dn) {
224
0
    DEBUG(10, (__location__ ": err\n"));
225
0
    goto failed;
226
0
  }
227
    
228
0
  dsdb_dn = dsdb_dn_construct(mem_ctx, dn, bval, dn_oid);
229
    
230
0
  talloc_free(tmp_ctx);
231
0
  return dsdb_dn;
232
233
0
failed:
234
0
  talloc_free(tmp_ctx);
235
0
  return NULL;
236
0
}
237
238
struct dsdb_dn *dsdb_dn_parse(TALLOC_CTX *mem_ctx, struct ldb_context *ldb, 
239
            const struct ldb_val *dn_blob, const char *dn_oid)
240
0
{
241
0
  struct dsdb_dn *dsdb_dn = dsdb_dn_parse_trusted(mem_ctx, ldb,
242
0
              dn_blob, dn_oid);
243
0
  if (dsdb_dn == NULL) {
244
0
    return NULL;
245
0
  }
246
0
  if (ldb_dn_validate(dsdb_dn->dn) == false) {
247
0
    DEBUG(10, ("could not parse %.*s as a %s DN\n",
248
0
         (int)dn_blob->length, dn_blob->data,
249
0
         dn_oid));
250
0
    return NULL;
251
0
  }
252
0
  return dsdb_dn;
253
0
}
254
255
static char *dsdb_dn_get_with_postfix(TALLOC_CTX *mem_ctx, 
256
             struct dsdb_dn *dsdb_dn,
257
             const char *postfix)
258
0
{
259
0
  if (!postfix) {
260
0
    return NULL;
261
0
  }
262
263
0
  switch (dsdb_dn->dn_format) {
264
0
  case DSDB_NORMAL_DN:
265
0
  {
266
0
    return talloc_strdup(mem_ctx, postfix);
267
0
  }
268
0
  case DSDB_BINARY_DN:
269
0
  {
270
0
    char *hexstr = data_blob_hex_string_upper(mem_ctx, &dsdb_dn->extra_part);
271
  
272
0
    char *p = talloc_asprintf(mem_ctx, "B:%u:%s:%s", (unsigned)(dsdb_dn->extra_part.length*2), hexstr, 
273
0
            postfix);
274
0
    talloc_free(hexstr);
275
0
    return p;
276
0
  }
277
0
  case DSDB_STRING_DN:
278
0
  {
279
0
    return talloc_asprintf(mem_ctx, "S:%u:%*.*s:%s", 
280
0
            (unsigned)(dsdb_dn->extra_part.length), 
281
0
            (int)(dsdb_dn->extra_part.length), 
282
0
            (int)(dsdb_dn->extra_part.length), 
283
0
            (const char *)dsdb_dn->extra_part.data, 
284
0
            postfix);
285
0
  }
286
0
  default:
287
0
    return NULL;
288
0
  }
289
0
}
290
291
char *dsdb_dn_get_linearized(TALLOC_CTX *mem_ctx, 
292
            struct dsdb_dn *dsdb_dn)
293
0
{
294
0
  const char *postfix = ldb_dn_get_linearized(dsdb_dn->dn);
295
0
  return dsdb_dn_get_with_postfix(mem_ctx, dsdb_dn, postfix);
296
0
}
297
298
char *dsdb_dn_get_casefold(TALLOC_CTX *mem_ctx, 
299
         struct dsdb_dn *dsdb_dn) 
300
0
{
301
0
  const char *postfix = ldb_dn_get_casefold(dsdb_dn->dn);
302
0
  return dsdb_dn_get_with_postfix(mem_ctx, dsdb_dn, postfix);
303
0
}
304
305
char *dsdb_dn_get_extended_linearized(TALLOC_CTX *mem_ctx, 
306
              struct dsdb_dn *dsdb_dn,
307
              int mode)
308
0
{
309
0
  char *postfix = ldb_dn_get_extended_linearized(mem_ctx, dsdb_dn->dn, mode);
310
0
  char *ret = dsdb_dn_get_with_postfix(mem_ctx, dsdb_dn, postfix);
311
0
  talloc_free(postfix);
312
0
  return ret;
313
0
}
314
315
int dsdb_dn_binary_canonicalise(struct ldb_context *ldb, void *mem_ctx,
316
        const struct ldb_val *in, struct ldb_val *out)
317
0
{
318
0
  struct dsdb_dn *dsdb_dn = dsdb_dn_parse(mem_ctx, ldb, in, DSDB_SYNTAX_BINARY_DN);
319
  
320
0
  if (!dsdb_dn) {
321
0
    return -1;
322
0
  }
323
0
  *out = data_blob_string_const(dsdb_dn_get_casefold(mem_ctx, dsdb_dn));
324
0
  talloc_free(dsdb_dn);
325
0
  if (!out->data) {
326
0
    return -1;
327
0
  }
328
0
  return 0;
329
0
}
330
331
int dsdb_dn_binary_comparison(struct ldb_context *ldb, void *mem_ctx,
332
             const struct ldb_val *v1,
333
             const struct ldb_val *v2)
334
0
{
335
0
  return ldb_any_comparison(ldb, mem_ctx, dsdb_dn_binary_canonicalise, v1, v2);
336
0
}
337
338
int dsdb_dn_string_canonicalise(struct ldb_context *ldb, void *mem_ctx,
339
        const struct ldb_val *in, struct ldb_val *out)
340
0
{
341
0
  struct dsdb_dn *dsdb_dn = dsdb_dn_parse(mem_ctx, ldb, in, DSDB_SYNTAX_STRING_DN);
342
  
343
0
  if (!dsdb_dn) {
344
0
    return -1;
345
0
  }
346
0
  *out = data_blob_string_const(dsdb_dn_get_casefold(mem_ctx, dsdb_dn));
347
0
  talloc_free(dsdb_dn);
348
0
  if (!out->data) {
349
0
    return -1;
350
0
  }
351
0
  return 0;
352
0
}
353
354
int dsdb_dn_string_comparison(struct ldb_context *ldb, void *mem_ctx,
355
             const struct ldb_val *v1,
356
             const struct ldb_val *v2)
357
0
{
358
0
  return ldb_any_comparison(ldb, mem_ctx, dsdb_dn_string_canonicalise, v1, v2);
359
0
}
360
361
/*
362
 * format a drsuapi_DsReplicaObjectIdentifier naming context as a string for debugging
363
 *
364
 * When forming a DN for DB access you must use drs_ObjectIdentifier_to_dn()
365
 */
366
char *drs_ObjectIdentifier_to_debug_string(TALLOC_CTX *mem_ctx,
367
             struct drsuapi_DsReplicaObjectIdentifier *nc)
368
0
{
369
0
  char *ret = NULL;
370
0
  TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
371
0
  if (!GUID_all_zero(&nc->guid)) {
372
0
    char *guid = GUID_string(tmp_ctx, &nc->guid);
373
0
    if (guid) {
374
0
      ret = talloc_asprintf_append(ret, "<GUID=%s>;", guid);
375
0
    }
376
0
  }
377
0
  if (nc->__ndr_size_sid != 0 && nc->sid.sid_rev_num != 0) {
378
0
    const char *sid = dom_sid_string(tmp_ctx, &nc->sid);
379
0
    if (sid) {
380
0
      ret = talloc_asprintf_append(ret, "<SID=%s>;", sid);
381
0
    }
382
0
  }
383
0
  if (nc->__ndr_size_dn != 0 && nc->dn) {
384
0
    ret = talloc_asprintf_append(ret, "%s", nc->dn);
385
0
  }
386
0
  talloc_free(tmp_ctx);
387
0
  talloc_steal(mem_ctx, ret);
388
0
  return ret;
389
0
}
390
391
/*
392
 * Safely convert a drsuapi_DsReplicaObjectIdentifier into an LDB DN
393
 *
394
 * We need to have GUID and SID priority and not allow extended
395
 * components in the DN.
396
 *
397
 * We must also totally honour the priority even if the string DN is not valid or able to parse as a DN.
398
 */
399
static struct ldb_dn *drs_ObjectIdentifier_to_dn(TALLOC_CTX *mem_ctx,
400
             struct ldb_context *ldb,
401
             struct drsuapi_DsReplicaObjectIdentifier *nc)
402
0
{
403
0
  struct ldb_dn *new_dn = NULL;
404
405
0
  if (!GUID_all_zero(&nc->guid)) {
406
0
    struct GUID_txt_buf buf;
407
0
    char *guid = GUID_buf_string(&nc->guid, &buf);
408
409
0
    new_dn = ldb_dn_new_fmt(mem_ctx,
410
0
          ldb,
411
0
          "<GUID=%s>",
412
0
          guid);
413
0
    if (new_dn == NULL) {
414
0
      DBG_ERR("Failed to prepare drs_ObjectIdentifier "
415
0
        "GUID %s into a DN\n",
416
0
        guid);
417
0
      return NULL;
418
0
    }
419
420
0
    return new_dn;
421
0
  }
422
423
0
  if (nc->__ndr_size_sid != 0 && nc->sid.sid_rev_num != 0) {
424
0
    struct dom_sid_buf buf;
425
0
    char *sid = dom_sid_str_buf(&nc->sid, &buf);
426
427
0
    new_dn = ldb_dn_new_fmt(mem_ctx,
428
0
          ldb,
429
0
          "<SID=%s>",
430
0
          sid);
431
0
    if (new_dn == NULL) {
432
0
      DBG_ERR("Failed to prepare drs_ObjectIdentifier "
433
0
        "SID %s into a DN\n",
434
0
        sid);
435
0
      return NULL;
436
0
    }
437
0
    return new_dn;
438
0
  }
439
440
0
  if (nc->__ndr_size_dn != 0 && nc->dn) {
441
0
    int dn_comp_num = 0;
442
0
    bool new_dn_valid = false;
443
444
0
    new_dn = ldb_dn_new(mem_ctx, ldb, nc->dn);
445
0
    if (new_dn == NULL) {
446
      /* Set to WARNING as this is user-controlled, don't print the value into the logs */
447
0
      DBG_WARNING("Failed to parse string DN in "
448
0
            "drs_ObjectIdentifier into an LDB DN\n");
449
0
      return NULL;
450
0
    }
451
452
0
    new_dn_valid = ldb_dn_validate(new_dn);
453
0
    if (!new_dn_valid) {
454
      /*
455
       * Set to WARNING as this is user-controlled,
456
       * but can print the value into the logs as it
457
       * parsed a bit
458
       */
459
0
      DBG_WARNING("Failed to validate string DN [%s] in "
460
0
            "drs_ObjectIdentifier as an LDB DN\n",
461
0
            ldb_dn_get_linearized(new_dn));
462
0
      return NULL;
463
0
    }
464
465
0
    dn_comp_num = ldb_dn_get_comp_num(new_dn);
466
0
    if (dn_comp_num <= 0) {
467
      /*
468
       * Set to WARNING as this is user-controlled,
469
       * but can print the value into the logs as it
470
       * parsed a bit
471
       */
472
0
      DBG_WARNING("DN [%s] in drs_ObjectIdentifier "
473
0
            "must have 1 or more components\n",
474
0
            ldb_dn_get_linearized(new_dn));
475
0
      return NULL;
476
0
    }
477
478
0
    if (ldb_dn_is_special(new_dn)) {
479
      /*
480
       * Set to WARNING as this is user-controlled,
481
       * but can print the value into the logs as it
482
       * parsed a bit
483
       */
484
0
      DBG_WARNING("New string DN [%s] in "
485
0
            "drs_ObjectIdentifier is a "
486
0
            "special LDB DN\n",
487
0
            ldb_dn_get_linearized(new_dn));
488
0
      return NULL;
489
0
    }
490
491
    /*
492
     * We want this just to be a string DN, extended
493
     * components are manually handled above
494
     */
495
0
    if (ldb_dn_has_extended(new_dn)) {
496
      /*
497
       * Set to WARNING as this is user-controlled,
498
       * but can print the value into the logs as it
499
       * parsed a bit
500
       */
501
0
      DBG_WARNING("Refusing to parse New string DN [%s] in "
502
0
            "drs_ObjectIdentifier as an "
503
0
            "extended LDB DN "
504
0
            "(GUIDs and SIDs should be in the "
505
0
            ".guid and .sid IDL elements, "
506
0
            "not in the string\n",
507
0
            ldb_dn_get_extended_linearized(mem_ctx,
508
0
                   new_dn,
509
0
                   1));
510
0
      return NULL;
511
0
    }
512
0
    return new_dn;
513
0
  }
514
515
0
  DBG_WARNING("Refusing to parse empty string DN "
516
0
        "(and no GUID or SID) "
517
0
        "drs_ObjectIdentifier into a empty "
518
0
        "(eg RootDSE) LDB DN\n");
519
0
  return NULL;
520
0
}
521
522
/*
523
 * Safely convert a drsuapi_DsReplicaObjectIdentifier into a validated
524
 * LDB DN of an existing DB entry, and/or find the NC root
525
 *
526
 * We need to have GUID and SID priority and not allow extended
527
 * components in the DN.
528
 *
529
 * We must also totally honour the priority even if the string DN is
530
 * not valid or able to parse as a DN.
531
 *
532
 * Finally, we must return the DN as found in the DB, as otherwise a
533
 * subsequent ldb_dn_compare(dn, nc_root) will fail (as this is based
534
 * on the string components).
535
 */
536
int drs_ObjectIdentifier_to_dn_and_nc_root(TALLOC_CTX *mem_ctx,
537
             struct ldb_context *ldb,
538
             struct drsuapi_DsReplicaObjectIdentifier *nc,
539
             struct ldb_dn **normalised_dn,
540
             struct ldb_dn **nc_root)
541
0
{
542
0
  int ret;
543
0
  struct ldb_dn *new_dn = NULL;
544
545
0
  new_dn = drs_ObjectIdentifier_to_dn(mem_ctx,
546
0
              ldb,
547
0
              nc);
548
0
  if (new_dn == NULL) {
549
0
    return LDB_ERR_INVALID_DN_SYNTAX;
550
0
  }
551
552
0
  ret = dsdb_normalise_dn_and_find_nc_root(ldb,
553
0
             mem_ctx,
554
0
             new_dn,
555
0
             normalised_dn,
556
0
             nc_root);
557
0
  if (ret != LDB_SUCCESS) {
558
    /*
559
     * dsdb_normalise_dn_and_find_nc_root() sets LDB error
560
     * strings, and the functions it calls do also
561
     */
562
0
    DBG_NOTICE("Failed to find DN \"%s\" -> \"%s\" for normalisation: %s (%s)\n",
563
0
         drs_ObjectIdentifier_to_debug_string(mem_ctx, nc),
564
0
         ldb_dn_get_extended_linearized(mem_ctx, new_dn, 1),
565
0
         ldb_errstring(ldb),
566
0
         ldb_strerror(ret));
567
0
  }
568
569
0
  TALLOC_FREE(new_dn);
570
0
  return ret;
571
0
}