Coverage Report

Created: 2025-11-16 06:57

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/samba/lib/fuzzing/fuzz_sddl_access_check.c
Line
Count
Source
1
/*
2
  Fuzz access check using SDDL strings and a known token
3
  Copyright (C) Catalyst IT 2023
4
5
  This program is free software; you can redistribute it and/or modify
6
  it under the terms of the GNU General Public License as published by
7
  the Free Software Foundation; either version 3 of the License, or
8
  (at your option) any later version.
9
10
  This program is distributed in the hope that it will be useful,
11
  but WITHOUT ANY WARRANTY; without even the implied warranty of
12
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
  GNU General Public License for more details.
14
15
  You should have received a copy of the GNU General Public License
16
  along with this program.  If not, see <http://www.gnu.org/licenses/>.
17
*/
18
19
#include "replace.h"
20
#include "libcli/security/security.h"
21
#include "libcli/security/conditional_ace.h"
22
#include "libcli/security/claims-conversions.h"
23
#include "lib/util/attr.h"
24
#include "librpc/gen_ndr/ndr_security.h"
25
#include "librpc/gen_ndr/ndr_conditional_ace.h"
26
#include "lib/util/bytearray.h"
27
#include "fuzzing/fuzzing.h"
28
29
30
static struct security_token token = {0};
31
32
static struct dom_sid dom_sid = {0};
33
34
/*
35
 * For this one we initialise a security token to have a few claims
36
 * and SIDs. The fuzz strings contain SDDL that will be tested against
37
 * this token in se_access_check() or sec_access_check_ds() --
38
 * supposing they compile.
39
 */
40
41
int LLVMFuzzerInitialize(int *argc, char ***argv)
42
4
{
43
4
  size_t i;
44
4
  TALLOC_CTX *mem_ctx = talloc_new(NULL);
45
4
  struct dom_sid *sid = NULL;
46
47
4
  struct claim_def {
48
4
    const char *type;
49
4
    const char *name;
50
4
    const char *claim_sddl;
51
4
  } claims[] = {
52
4
    {
53
4
      "user",
54
4
      "shoe size",
55
4
      "44"
56
4
    },
57
4
    {
58
4
      "user",
59
4
      "©",
60
4
      "{\"unknown\", \"\", \" ←ā\"}"
61
4
    },
62
4
    {
63
4
      "device",
64
4
      "©",
65
4
      "{\"unknown\", \" \", \" ←ā\"}"
66
4
    },
67
4
    {
68
4
      "device",
69
4
      "least favourite groups",
70
4
      "{SID(S-1-1-0),SID(S-1-5-3),SID(S-1-57777-333-33-33-2)}"
71
4
    },
72
4
    {
73
4
      "local",
74
4
      "birds",
75
4
      "{\"tern\"}"
76
4
    },
77
4
  };
78
79
4
  const char * device_sids[] = {
80
4
    "S-1-1-0",
81
4
    "S-1-333-66",
82
4
    "S-1-2-3-4-5-6-7-8-9",
83
4
  };
84
4
  const char * user_sids[] = {
85
4
    "S-1-333-66",
86
4
    "S-1-16-8448",
87
4
    "S-1-9-8-7",
88
4
  };
89
90
16
  for (i = 0; i < ARRAY_SIZE(user_sids); i++) {
91
12
    sid = sddl_decode_sid(mem_ctx, &user_sids[i], NULL);
92
12
    if (sid == NULL) {
93
0
      abort();
94
0
    }
95
12
    add_sid_to_array(mem_ctx, sid,
96
12
         &token.sids,
97
12
         &token.num_sids);
98
12
  }
99
100
16
  for (i = 0; i < ARRAY_SIZE(device_sids); i++) {
101
12
    sid = sddl_decode_sid(mem_ctx, &device_sids[i], NULL);
102
12
    if (sid == NULL) {
103
0
      abort();
104
0
    }
105
12
    add_sid_to_array(mem_ctx, sid,
106
12
         &token.device_sids,
107
12
         &token.num_device_sids);
108
12
  }
109
110
24
  for (i = 0; i < ARRAY_SIZE(claims); i++) {
111
20
    struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 *claim = NULL;
112
20
    struct claim_def c = claims[i];
113
114
20
    claim = parse_sddl_literal_as_claim(mem_ctx,
115
20
                c.name,
116
20
                c.claim_sddl);
117
20
    if (claim == NULL) {
118
0
      abort();
119
0
    }
120
20
    add_claim_to_token(mem_ctx, &token, claim, c.type);
121
20
  }
122
123
  /* we also need a global domain SID */
124
4
  string_to_sid(&dom_sid, device_sids[2]);
125
4
  return 0;
126
4
}
127
128
129
int LLVMFuzzerTestOneInput(const uint8_t *input, size_t len)
130
9.88k
{
131
9.88k
  TALLOC_CTX *mem_ctx = NULL;
132
9.88k
  struct security_descriptor *sd = NULL;
133
9.88k
  uint32_t access_desired;
134
9.88k
  uint32_t access_granted;
135
9.88k
  const char *sddl;
136
9.88k
  ssize_t i;
137
9.88k
  if (len < 5) {
138
7
    return 0;
139
7
  }
140
9.87k
  access_desired = PULL_LE_U32(input + len - 4, 0);
141
142
  /*
143
   * check there is a '\0'.
144
   *
145
   * Note this allows double-dealing for the last 4 bytes: they are used
146
   * as the access_desired mask (see just above) but also *could* be
147
   * part of the sddl string. But this doesn't matter, for three
148
   * reasons:
149
   *
150
   * 1. the desired access mask doesn't usually matter much.
151
   *
152
   * 2. the final '\0' is rarely the operative one. Usually the
153
   * effective string ends a long time before the end of the input, and
154
   * the tail is just junk that comes along for the ride.
155
   *
156
   * 3. Even if there is a case where the end of the SDDL is part of the
157
   * mask, the evolution strategy is very likely to try a different mask,
158
   * because it likes to add junk on the end.
159
   *
160
   * But still, you ask, WHY? So that the seeds from here can be shared
161
   * back and forth with the fuzz_sddl_parse seeds, which have the same
162
   * form of a null-terminated-string-with-trailing-junk. If we started
163
   * the loop at `len - 5` instead of `len - 1`, there might be
164
   * interesting seeds that are valid there that would fail here. That's
165
   * all.
166
   */
167
5.60M
  for (i = len - 1; i >= 0; i--) {
168
5.60M
    if (input[i] == 0) {
169
9.85k
      break;
170
9.85k
    }
171
5.60M
  }
172
9.87k
  if (i < 0) {
173
17
    return 0;
174
17
  }
175
176
9.85k
  sddl = (const char *)input;
177
9.85k
  mem_ctx = talloc_new(NULL);
178
179
9.85k
  sd = sddl_decode(mem_ctx, sddl, &dom_sid);
180
9.85k
  if (sd == NULL) {
181
9.07k
    goto end;
182
9.07k
  }
183
184
#ifdef FUZZ_SEC_ACCESS_CHECK_DS
185
  /*
186
   * The sec_access_check_ds() function has two arguments not found in
187
   * se_access_check, and also not found in our fuzzing examples.
188
   *
189
   * One is a struct object_tree, which is used for object ACE types.
190
   * The other is a SID, which is used as a default if an ACE lacks a
191
   * SID.
192
   */
193
  sec_access_check_ds(sd,
194
          &token,
195
          access_desired,
196
          &access_granted,
197
          NULL,
198
          NULL);
199
#else
200
787
  se_access_check(sd, &token, access_desired, &access_granted);
201
787
#endif
202
203
9.85k
end:
204
  talloc_free(mem_ctx);
205
9.85k
  return 0;
206
787
}