Coverage Report

Created: 2026-04-01 06:26

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/samba/libcli/security/dom_sid.c
Line
Count
Source
1
/*
2
   Unix SMB/CIFS implementation.
3
   Samba utility functions
4
5
   Copyright (C) Stefan (metze) Metzmacher  2002-2004
6
   Copyright (C) Andrew Tridgell    1992-2004
7
   Copyright (C) Jeremy Allison     1999
8
9
   This program is free software; you can redistribute it and/or modify
10
   it under the terms of the GNU General Public License as published by
11
   the Free Software Foundation; either version 3 of the License, or
12
   (at your option) any later version.
13
14
   This program is distributed in the hope that it will be useful,
15
   but WITHOUT ANY WARRANTY; without even the implied warranty of
16
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17
   GNU General Public License for more details.
18
19
   You should have received a copy of the GNU General Public License
20
   along with this program.  If not, see <http://www.gnu.org/licenses/>.
21
*/
22
23
#include "replace.h"
24
#include "lib/util/data_blob.h"
25
#include "system/locale.h"
26
#include "lib/util/debug.h"
27
#include "lib/util/util.h"
28
#include "librpc/gen_ndr/security.h"
29
#include "dom_sid.h"
30
#include "lib/util/smb_strtox.h"
31
#include "lib/util/tsort.h"
32
33
/*****************************************************************
34
 Compare the auth portion of two sids.
35
*****************************************************************/
36
37
int dom_sid_compare_auth(const struct dom_sid *sid1,
38
       const struct dom_sid *sid2)
39
3.93M
{
40
3.93M
  int i;
41
42
3.93M
  if (sid1 == sid2) {
43
0
    return 0;
44
0
  }
45
46
3.93M
  if (sid1 == NULL) {
47
0
    return -1;
48
0
  }
49
50
3.93M
  if (sid2 == NULL) {
51
0
    return 1;
52
0
  }
53
54
3.93M
  if (sid1->sid_rev_num != sid2->sid_rev_num) {
55
2.46M
    return NUMERIC_CMP(sid1->sid_rev_num, sid2->sid_rev_num);
56
2.46M
  }
57
58
4.19M
  for (i = 0; i < 6; i++) {
59
3.94M
    if (sid1->id_auth[i] != sid2->id_auth[i]) {
60
1.22M
      return NUMERIC_CMP(sid1->id_auth[i], sid2->id_auth[i]);
61
1.22M
    }
62
3.94M
  }
63
64
249k
  return 0;
65
1.47M
}
66
67
/*****************************************************************
68
 Compare two sids.
69
*****************************************************************/
70
71
int dom_sid_compare(const struct dom_sid *sid1, const struct dom_sid *sid2)
72
4.91M
{
73
4.91M
  int i;
74
75
4.91M
  if (sid1 == sid2) {
76
6.78k
    return 0;
77
6.78k
  }
78
79
4.91M
  if (sid1 == NULL) {
80
0
    return -1;
81
0
  }
82
83
4.91M
  if (sid2 == NULL) {
84
53.0k
    return 1;
85
53.0k
  }
86
87
  /* Compare most likely different rids, first: i.e start at end */
88
4.85M
  if (sid1->num_auths != sid2->num_auths) {
89
888k
    return NUMERIC_CMP(sid1->num_auths, sid2->num_auths);
90
888k
  }
91
4.08M
  for (i = sid1->num_auths-1; i >= 0; --i) {
92
189k
    if (sid1->sub_auths[i] < sid2->sub_auths[i]) {
93
28.7k
      return -1;
94
28.7k
    }
95
161k
    if (sid1->sub_auths[i] > sid2->sub_auths[i]) {
96
46.7k
      return 1;
97
46.7k
    }
98
161k
  }
99
100
3.89M
  return dom_sid_compare_auth(sid1, sid2);
101
3.97M
}
102
103
/*****************************************************************
104
 Compare two sids.
105
*****************************************************************/
106
107
bool dom_sid_equal(const struct dom_sid *sid1, const struct dom_sid *sid2)
108
4.91M
{
109
4.91M
  return dom_sid_compare(sid1, sid2) == 0;
110
4.91M
}
111
112
/*****************************************************************
113
 Add a rid to the end of a sid
114
*****************************************************************/
115
116
bool sid_append_rid(struct dom_sid *sid, uint32_t rid)
117
314k
{
118
314k
  if (sid->num_auths < ARRAY_SIZE(sid->sub_auths)) {
119
314k
    sid->sub_auths[sid->num_auths++] = rid;
120
314k
    return true;
121
314k
  }
122
284
  return false;
123
314k
}
124
125
/*
126
  See if 2 SIDs are in the same domain
127
  this just compares the leading sub-auths
128
*/
129
int dom_sid_compare_domain(const struct dom_sid *sid1,
130
         const struct dom_sid *sid2)
131
0
{
132
0
  int n, i;
133
134
0
  n = MIN(sid1->num_auths, sid2->num_auths);
135
136
0
  for (i = n-1; i >= 0; --i) {
137
0
    if (sid1->sub_auths[i] < sid2->sub_auths[i]) {
138
0
      return -1;
139
0
    }
140
0
    if (sid1->sub_auths[i] > sid2->sub_auths[i]) {
141
0
      return 1;
142
0
    }
143
0
  }
144
145
0
  return dom_sid_compare_auth(sid1, sid2);
146
0
}
147
148
/*****************************************************************
149
 Convert a string to a SID. Returns True on success, False on fail.
150
 Return the first character not parsed in endp.
151
*****************************************************************/
152
348k
#define AUTHORITY_MASK (~(0xffffffffffffULL))
153
154
bool dom_sid_parse_endp(const char *sidstr,struct dom_sid *sidout,
155
      const char **endp)
156
203k
{
157
203k
  const char *p;
158
203k
  char *q = NULL;
159
203k
  char *end = NULL;
160
203k
  uint64_t conv;
161
203k
  int error = 0;
162
163
203k
  *sidout = (struct dom_sid) {};
164
165
203k
  if ((sidstr[0] != 'S' && sidstr[0] != 's') || sidstr[1] != '-') {
166
24.7k
    DBG_INFO("'%s' is not a SID\n", sidstr);
167
24.7k
    return false;
168
24.7k
  }
169
170
  /* Get the revision number. */
171
179k
  p = sidstr + 2;
172
173
179k
  if (!isdigit((unsigned char)*p)) {
174
689
    goto format_error;
175
689
  }
176
177
178k
  conv = smb_strtoul(p, &q, 10, &error, SMB_STR_STANDARD);
178
178k
  if (error != 0 || (*q != '-') || conv > UINT8_MAX || q - p > 4) {
179
3.46k
    goto format_error;
180
3.46k
  }
181
175k
  sidout->sid_rev_num = (uint8_t) conv;
182
175k
  q++;
183
184
175k
  if (!isdigit((unsigned char)*q)) {
185
718
    goto format_error;
186
718
  }
187
203k
  while (q[0] == '0' && isdigit((unsigned char)q[1])) {
188
    /*
189
     * strtoull will think this is octal, which is not how SIDs
190
     * work! So let's walk along until there are no leading zeros
191
     * (or a single zero).
192
     */
193
29.2k
    q++;
194
29.2k
  }
195
196
  /* get identauth */
197
174k
  conv = smb_strtoull(q, &end, 0, &error, SMB_STR_STANDARD);
198
174k
  if (conv & AUTHORITY_MASK || error != 0) {
199
700
    goto format_error;
200
700
  }
201
173k
  if (conv >= (1ULL << 48) || end - q > 15) {
202
    /*
203
     * This identauth looks like a big number, but resolves to a
204
     * small number after rounding.
205
     */
206
418
    goto format_error;
207
418
  }
208
209
  /* NOTE - the conv value is in big-endian format. */
210
173k
  sidout->id_auth[0] = (conv & 0xff0000000000ULL) >> 40;
211
173k
  sidout->id_auth[1] = (conv & 0x00ff00000000ULL) >> 32;
212
173k
  sidout->id_auth[2] = (conv & 0x0000ff000000ULL) >> 24;
213
173k
  sidout->id_auth[3] = (conv & 0x000000ff0000ULL) >> 16;
214
173k
  sidout->id_auth[4] = (conv & 0x00000000ff00ULL) >> 8;
215
173k
  sidout->id_auth[5] = (conv & 0x0000000000ffULL);
216
217
173k
  sidout->num_auths = 0;
218
173k
  q = end;
219
173k
  if (*q != '-') {
220
    /* Just id_auth, no subauths */
221
24.5k
    goto done;
222
24.5k
  }
223
224
148k
  q++;
225
226
277k
  while (true) {
227
277k
    if (!isdigit((unsigned char)*q)) {
228
878
      goto format_error;
229
878
    }
230
287k
    while (q[0] == '0' && isdigit((unsigned char)q[1])) {
231
      /*
232
       * strtoull will think this is octal, which is not how
233
       * SIDs work! So let's walk along until there are no
234
       * leading zeros (or a single zero).
235
       */
236
10.1k
      q++;
237
10.1k
    }
238
276k
    conv = smb_strtoull(q, &end, 0, &error, SMB_STR_STANDARD);
239
276k
    if (conv > UINT32_MAX || error != 0 || end - q > 12) {
240
      /*
241
       * This sub-auth is greater than 4294967295,
242
       * and hence invalid. Windows will treat it as
243
       * 4294967295, while we prefer to refuse (old
244
       * versions of Samba will wrap, arriving at
245
       * another number altogether).
246
                         */
247
1.21k
      DBG_NOTICE("bad sub-auth in %s\n", sidstr);
248
1.21k
      goto format_error;
249
1.21k
    }
250
251
275k
    if (!sid_append_rid(sidout, conv)) {
252
284
      DEBUG(3, ("Too many sid auths in %s\n", sidstr));
253
284
      return false;
254
284
    }
255
256
275k
    q = end;
257
275k
    if (*q != '-') {
258
146k
      break;
259
146k
    }
260
129k
    q += 1;
261
129k
  }
262
170k
done:
263
170k
  if (endp != NULL) {
264
47.7k
    *endp = q;
265
47.7k
  }
266
170k
  return true;
267
268
8.07k
format_error:
269
8.07k
  DEBUG(3, ("string_to_sid: SID %s is not in a valid format\n", sidstr));
270
8.07k
  return false;
271
148k
}
272
273
bool string_to_sid(struct dom_sid *sidout, const char *sidstr)
274
6
{
275
6
  return dom_sid_parse(sidstr, sidout);
276
6
}
277
278
bool dom_sid_parse(const char *sidstr, struct dom_sid *ret)
279
123k
{
280
123k
  return dom_sid_parse_endp(sidstr, ret, NULL);
281
123k
}
282
283
/*
284
  convert a string to a dom_sid, returning a talloc'd dom_sid
285
*/
286
struct dom_sid *dom_sid_parse_talloc(TALLOC_CTX *mem_ctx, const char *sidstr)
287
0
{
288
0
  struct dom_sid *ret;
289
0
  ret = talloc(mem_ctx, struct dom_sid);
290
0
  if (!ret) {
291
0
    return NULL;
292
0
  }
293
0
  if (!dom_sid_parse(sidstr, ret)) {
294
0
    talloc_free(ret);
295
0
    return NULL;
296
0
  }
297
298
0
  return ret;
299
0
}
300
301
/*
302
  convert a string to a dom_sid, returning a talloc'd dom_sid
303
*/
304
struct dom_sid *dom_sid_parse_length(TALLOC_CTX *mem_ctx, const DATA_BLOB *sid)
305
0
{
306
0
  char p[sid->length+1];
307
0
  memcpy(p, sid->data, sid->length);
308
0
  p[sid->length] = '\0';
309
0
  return dom_sid_parse_talloc(mem_ctx, p);
310
0
}
311
312
/*
313
  copy a dom_sid structure
314
*/
315
struct dom_sid *dom_sid_dup(TALLOC_CTX *mem_ctx, const struct dom_sid *dom_sid)
316
37.6k
{
317
37.6k
  struct dom_sid *ret;
318
319
37.6k
  if (!dom_sid) {
320
0
    return NULL;
321
0
  }
322
323
37.6k
  ret = talloc(mem_ctx, struct dom_sid);
324
37.6k
  if (!ret) {
325
0
    return NULL;
326
0
  }
327
37.6k
  sid_copy(ret, dom_sid);
328
329
37.6k
  return ret;
330
37.6k
}
331
332
/*
333
  add a rid to a domain dom_sid to make a full dom_sid. This function
334
  returns a new sid in the supplied memory context
335
*/
336
struct dom_sid *dom_sid_add_rid(TALLOC_CTX *mem_ctx,
337
        const struct dom_sid *domain_sid,
338
        uint32_t rid)
339
0
{
340
0
  struct dom_sid *sid;
341
342
0
  sid = dom_sid_dup(mem_ctx, domain_sid);
343
0
  if (!sid) return NULL;
344
345
0
  if (!sid_append_rid(sid, rid)) {
346
0
    talloc_free(sid);
347
0
    return NULL;
348
0
  }
349
350
0
  return sid;
351
0
}
352
353
/*
354
  Split up a SID into its domain and RID part
355
*/
356
NTSTATUS dom_sid_split_rid(TALLOC_CTX *mem_ctx, const struct dom_sid *sid,
357
         struct dom_sid **domain, uint32_t *rid)
358
0
{
359
0
  if (sid->num_auths == 0) {
360
0
    return NT_STATUS_INVALID_PARAMETER;
361
0
  }
362
363
0
  if (domain) {
364
0
    if (!(*domain = dom_sid_dup(mem_ctx, sid))) {
365
0
      return NT_STATUS_NO_MEMORY;
366
0
    }
367
368
0
    (*domain)->num_auths -= 1;
369
0
  }
370
371
0
  if (rid) {
372
0
    *rid = sid->sub_auths[sid->num_auths - 1];
373
0
  }
374
375
0
  return NT_STATUS_OK;
376
0
}
377
378
/*
379
  return true if the 2nd sid contains or matches the prefix_sid
380
*/
381
bool dom_sid_match_prefix(const struct dom_sid *prefix_sid,
382
        const struct dom_sid *sid)
383
41.1k
{
384
41.1k
  int i;
385
386
41.1k
  if (!prefix_sid || !sid) {
387
0
    return false;
388
0
  }
389
390
41.1k
  if (prefix_sid->num_auths > sid->num_auths) {
391
0
    return false;
392
0
  }
393
394
201k
  for (i = prefix_sid->num_auths-1; i >= 0; --i) {
395
161k
    if (prefix_sid->sub_auths[i] != sid->sub_auths[i]) {
396
993
      return false;
397
993
    }
398
161k
  }
399
400
40.1k
  return dom_sid_compare_auth(prefix_sid, sid) == 0;
401
41.1k
}
402
403
/*
404
  return true if the 2nd sid is in the domain given by the first sid
405
*/
406
bool dom_sid_in_domain(const struct dom_sid *domain_sid,
407
           const struct dom_sid *sid)
408
111k
{
409
111k
  if (!domain_sid || !sid) {
410
37.7k
    return false;
411
37.7k
  }
412
413
73.5k
  if (sid->num_auths < 2) {
414
20.5k
    return false;
415
20.5k
  }
416
417
52.9k
  if (domain_sid->num_auths != (sid->num_auths - 1)) {
418
11.8k
    return false;
419
11.8k
  }
420
421
41.1k
  return dom_sid_match_prefix(domain_sid, sid);
422
52.9k
}
423
424
bool dom_sid_has_account_domain(const struct dom_sid *sid)
425
0
{
426
0
  if (sid == NULL) {
427
0
    return false;
428
0
  }
429
430
0
  if (sid->sid_rev_num != 1) {
431
0
    return false;
432
0
  }
433
0
  if (sid->num_auths != 5) {
434
0
    return false;
435
0
  }
436
0
  if (sid->id_auth[5] != 5) {
437
0
    return false;
438
0
  }
439
0
  if (sid->id_auth[4] != 0) {
440
0
    return false;
441
0
  }
442
0
  if (sid->id_auth[3] != 0) {
443
0
    return false;
444
0
  }
445
0
  if (sid->id_auth[2] != 0) {
446
0
    return false;
447
0
  }
448
0
  if (sid->id_auth[1] != 0) {
449
0
    return false;
450
0
  }
451
0
  if (sid->id_auth[0] != 0) {
452
0
    return false;
453
0
  }
454
0
  if (sid->sub_auths[0] != 21) {
455
0
    return false;
456
0
  }
457
458
0
  return true;
459
0
}
460
461
bool dom_sid_is_valid_account_domain(const struct dom_sid *sid)
462
0
{
463
  /*
464
   * We expect S-1-5-21-9-8-7, but we don't
465
   * allow S-1-5-21-0-0-0 as this is used
466
   * for claims and compound identities.
467
   *
468
   * With this structure:
469
   *
470
   * struct dom_sid {
471
   *     uint8_t sid_rev_num;
472
   *     int8_t num_auths; [range(0,15)]
473
   *     uint8_t id_auth[6];
474
   *     uint32_t sub_auths[15];
475
   * }
476
   *
477
   * S-1-5-21-9-8-7 looks like this:
478
   * {1, 4, {0,0,0,0,0,5}, {21,9,8,7,0,0,0,0,0,0,0,0,0,0,0}};
479
   */
480
0
  if (sid == NULL) {
481
0
    return false;
482
0
  }
483
484
0
  if (sid->sid_rev_num != 1) {
485
0
    return false;
486
0
  }
487
0
  if (sid->num_auths != 4) {
488
0
    return false;
489
0
  }
490
0
  if (sid->id_auth[5] != 5) {
491
0
    return false;
492
0
  }
493
0
  if (sid->id_auth[4] != 0) {
494
0
    return false;
495
0
  }
496
0
  if (sid->id_auth[3] != 0) {
497
0
    return false;
498
0
  }
499
0
  if (sid->id_auth[2] != 0) {
500
0
    return false;
501
0
  }
502
0
  if (sid->id_auth[1] != 0) {
503
0
    return false;
504
0
  }
505
0
  if (sid->id_auth[0] != 0) {
506
0
    return false;
507
0
  }
508
0
  if (sid->sub_auths[0] != 21) {
509
0
    return false;
510
0
  }
511
0
  if (sid->sub_auths[1] == 0) {
512
0
    return false;
513
0
  }
514
0
  if (sid->sub_auths[2] == 0) {
515
0
    return false;
516
0
  }
517
0
  if (sid->sub_auths[3] == 0) {
518
0
    return false;
519
0
  }
520
521
0
  return true;
522
0
}
523
524
/*
525
  Convert a dom_sid to a string, printing into a buffer. Return the
526
  string length. If it overflows, return the string length that would
527
  result (buflen needs to be +1 for the terminating 0).
528
*/
529
static int dom_sid_string_buf(const struct dom_sid *sid, char *buf, int buflen)
530
580k
{
531
580k
  int i, ofs, ret;
532
580k
  uint64_t ia;
533
534
580k
  if (!sid) {
535
0
    return strlcpy(buf, "(NULL SID)", buflen);
536
0
  }
537
538
580k
  ia = ((uint64_t)sid->id_auth[5]) +
539
580k
    ((uint64_t)sid->id_auth[4] << 8 ) +
540
580k
    ((uint64_t)sid->id_auth[3] << 16) +
541
580k
    ((uint64_t)sid->id_auth[2] << 24) +
542
580k
    ((uint64_t)sid->id_auth[1] << 32) +
543
580k
    ((uint64_t)sid->id_auth[0] << 40);
544
545
580k
  ret = snprintf(buf, buflen, "S-%"PRIu8"-", sid->sid_rev_num);
546
580k
  if (ret < 0) {
547
0
    return ret;
548
0
  }
549
580k
  ofs = ret;
550
551
580k
  if (ia >= UINT32_MAX) {
552
134k
    ret = snprintf(buf+ofs, MAX(buflen-ofs, 0), "0x%"PRIx64, ia);
553
445k
  } else {
554
445k
    ret = snprintf(buf+ofs, MAX(buflen-ofs, 0), "%"PRIu64, ia);
555
445k
  }
556
580k
  if (ret < 0) {
557
0
    return ret;
558
0
  }
559
580k
  ofs += ret;
560
561
1.34M
  for (i = 0; i < sid->num_auths; i++) {
562
762k
    ret = snprintf(
563
762k
      buf+ofs,
564
762k
      MAX(buflen-ofs, 0),
565
762k
      "-%"PRIu32,
566
762k
      sid->sub_auths[i]);
567
762k
    if (ret < 0) {
568
0
      return ret;
569
0
    }
570
762k
    ofs += ret;
571
762k
  }
572
580k
  return ofs;
573
580k
}
574
575
/*
576
  convert a dom_sid to a string
577
*/
578
char *dom_sid_string(TALLOC_CTX *mem_ctx, const struct dom_sid *sid)
579
15.4k
{
580
15.4k
  char buf[DOM_SID_STR_BUFLEN];
581
15.4k
  char *result;
582
15.4k
  int len;
583
584
15.4k
  len = dom_sid_string_buf(sid, buf, sizeof(buf));
585
586
15.4k
  if ((len < 0) || (len+1 > sizeof(buf))) {
587
0
    return talloc_strdup(mem_ctx, "(SID ERR)");
588
0
  }
589
590
  /*
591
   * Avoid calling strlen (via talloc_strdup), we already have
592
   * the length
593
   */
594
15.4k
  result = (char *)talloc_memdup(mem_ctx, buf, len+1);
595
15.4k
  if (result == NULL) {
596
0
    return NULL;
597
0
  }
598
599
  /*
600
   * beautify the talloc_report output
601
   */
602
15.4k
  talloc_set_name_const(result, result);
603
15.4k
  return result;
604
15.4k
}
605
606
char *dom_sid_str_buf(const struct dom_sid *sid, struct dom_sid_buf *dst)
607
565k
{
608
565k
  int ret;
609
565k
  ret = dom_sid_string_buf(sid, dst->buf, sizeof(dst->buf));
610
565k
  if ((ret < 0) || (ret >= sizeof(dst->buf))) {
611
0
    strlcpy(dst->buf, "(INVALID SID)", sizeof(dst->buf));
612
0
  }
613
565k
  return dst->buf;
614
565k
}