/src/samba/libcli/security/sddl.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | Unix SMB/CIFS implementation. |
3 | | |
4 | | security descriptor description language functions |
5 | | |
6 | | Copyright (C) Andrew Tridgell 2005 |
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 "replace.h" |
23 | | #include "lib/util/debug.h" |
24 | | #include "libcli/security/security.h" |
25 | | #include "libcli/security/conditional_ace.h" |
26 | | #include "librpc/gen_ndr/ndr_misc.h" |
27 | | #include "lib/util/smb_strtox.h" |
28 | | #include "libcli/security/sddl.h" |
29 | | #include "system/locale.h" |
30 | | #include "lib/util/util_str_hex.h" |
31 | | |
32 | | |
33 | | struct sddl_transition_state { |
34 | | const struct dom_sid *machine_sid; |
35 | | const struct dom_sid *domain_sid; |
36 | | const struct dom_sid *forest_sid; |
37 | | }; |
38 | | |
39 | | struct flag_map { |
40 | | const char *name; |
41 | | uint32_t flag; |
42 | | }; |
43 | | |
44 | | static bool sddl_map_flag( |
45 | | const struct flag_map *map, |
46 | | const char *str, |
47 | | size_t *plen, |
48 | | uint32_t *pflag) |
49 | 185k | { |
50 | 1.48M | while (map->name != NULL) { |
51 | 1.46M | size_t len = strlen(map->name); |
52 | 1.46M | int cmp = strncmp(map->name, str, len); |
53 | | |
54 | 1.46M | if (cmp == 0) { |
55 | 170k | *plen = len; |
56 | 170k | *pflag = map->flag; |
57 | 170k | return true; |
58 | 170k | } |
59 | 1.29M | map += 1; |
60 | 1.29M | } |
61 | 15.4k | return false; |
62 | 185k | } |
63 | | |
64 | | /* |
65 | | map a series of letter codes into a uint32_t |
66 | | */ |
67 | | static bool sddl_map_flags(const struct flag_map *map, const char *str, |
68 | | uint32_t *pflags, size_t *plen, |
69 | | bool unknown_flag_is_part_of_next_thing) |
70 | 169k | { |
71 | 169k | const char *str0 = str; |
72 | 169k | if (plen != NULL) { |
73 | 20.2k | *plen = 0; |
74 | 20.2k | } |
75 | 169k | *pflags = 0; |
76 | 175k | while (str[0] != '\0' && isupper((unsigned char)str[0])) { |
77 | 6.36k | size_t len; |
78 | 6.36k | uint32_t flags; |
79 | 6.36k | bool found; |
80 | | |
81 | 6.36k | found = sddl_map_flag(map, str, &len, &flags); |
82 | 6.36k | if (!found) { |
83 | 151 | break; |
84 | 151 | } |
85 | | |
86 | 6.20k | *pflags |= flags; |
87 | 6.20k | if (plen != NULL) { |
88 | 3.40k | *plen += len; |
89 | 3.40k | } |
90 | 6.20k | str += len; |
91 | 6.20k | } |
92 | | /* |
93 | | * For ACL flags, unknown_flag_is_part_of_next_thing is set, |
94 | | * and we expect some more stuff that isn't flags. |
95 | | * |
96 | | * For ACE flags, unknown_flag_is_part_of_next_thing is unset, |
97 | | * and the flags have been tokenised into their own little |
98 | | * string. We don't expect anything here, even whitespace. |
99 | | */ |
100 | 169k | if (*str == '\0' || unknown_flag_is_part_of_next_thing) { |
101 | 169k | return true; |
102 | 169k | } |
103 | 65 | DBG_WARNING("Unknown flag - '%s' in '%s'\n", str, str0); |
104 | 65 | return false; |
105 | 169k | } |
106 | | |
107 | | |
108 | | /* |
109 | | a mapping between the 2 letter SID codes and sid strings |
110 | | */ |
111 | | static const struct { |
112 | | const char *code; |
113 | | const char *sid; |
114 | | uint32_t machine_rid; |
115 | | uint32_t domain_rid; |
116 | | uint32_t forest_rid; |
117 | | } sid_codes[] = { |
118 | | { .code = "WD", .sid = SID_WORLD }, |
119 | | |
120 | | { .code = "CO", .sid = SID_CREATOR_OWNER }, |
121 | | { .code = "CG", .sid = SID_CREATOR_GROUP }, |
122 | | { .code = "OW", .sid = SID_OWNER_RIGHTS }, |
123 | | |
124 | | { .code = "NU", .sid = SID_NT_NETWORK }, |
125 | | { .code = "IU", .sid = SID_NT_INTERACTIVE }, |
126 | | { .code = "SU", .sid = SID_NT_SERVICE }, |
127 | | { .code = "AN", .sid = SID_NT_ANONYMOUS }, |
128 | | { .code = "ED", .sid = SID_NT_ENTERPRISE_DCS }, |
129 | | { .code = "PS", .sid = SID_NT_SELF }, |
130 | | { .code = "AU", .sid = SID_NT_AUTHENTICATED_USERS }, |
131 | | { .code = "RC", .sid = SID_NT_RESTRICTED }, |
132 | | { .code = "SY", .sid = SID_NT_SYSTEM }, |
133 | | { .code = "LS", .sid = SID_NT_LOCAL_SERVICE }, |
134 | | { .code = "NS", .sid = SID_NT_NETWORK_SERVICE }, |
135 | | { .code = "WR", .sid = SID_SECURITY_RESTRICTED_CODE }, |
136 | | |
137 | | { .code = "BA", .sid = SID_BUILTIN_ADMINISTRATORS }, |
138 | | { .code = "BU", .sid = SID_BUILTIN_USERS }, |
139 | | { .code = "BG", .sid = SID_BUILTIN_GUESTS }, |
140 | | { .code = "PU", .sid = SID_BUILTIN_POWER_USERS }, |
141 | | { .code = "AO", .sid = SID_BUILTIN_ACCOUNT_OPERATORS }, |
142 | | { .code = "SO", .sid = SID_BUILTIN_SERVER_OPERATORS }, |
143 | | { .code = "PO", .sid = SID_BUILTIN_PRINT_OPERATORS }, |
144 | | { .code = "BO", .sid = SID_BUILTIN_BACKUP_OPERATORS }, |
145 | | { .code = "RE", .sid = SID_BUILTIN_REPLICATOR }, |
146 | | { .code = "RU", .sid = SID_BUILTIN_PREW2K }, |
147 | | { .code = "RD", .sid = SID_BUILTIN_REMOTE_DESKTOP_USERS }, |
148 | | { .code = "NO", .sid = SID_BUILTIN_NETWORK_CONF_OPERATORS }, |
149 | | |
150 | | { .code = "MU", .sid = SID_BUILTIN_PERFMON_USERS }, |
151 | | { .code = "LU", .sid = SID_BUILTIN_PERFLOG_USERS }, |
152 | | { .code = "IS", .sid = SID_BUILTIN_IUSERS }, |
153 | | { .code = "CY", .sid = SID_BUILTIN_CRYPTO_OPERATORS }, |
154 | | { .code = "ER", .sid = SID_BUILTIN_EVENT_LOG_READERS }, |
155 | | { .code = "CD", .sid = SID_BUILTIN_CERT_SERV_DCOM_ACCESS }, |
156 | | { .code = "RA", .sid = SID_BUILTIN_RDS_REMOTE_ACCESS_SERVERS }, |
157 | | { .code = "ES", .sid = SID_BUILTIN_RDS_ENDPOINT_SERVERS }, |
158 | | { .code = "MS", .sid = SID_BUILTIN_RDS_MANAGEMENT_SERVERS }, |
159 | | { .code = "HA", .sid = SID_BUILTIN_HYPER_V_ADMINS }, |
160 | | { .code = "AA", .sid = SID_BUILTIN_ACCESS_CONTROL_ASSISTANCE_OPS }, |
161 | | { .code = "RM", .sid = SID_BUILTIN_REMOTE_MANAGEMENT_USERS }, |
162 | | |
163 | | { .code = "UD", .sid = SID_USER_MODE_DRIVERS }, |
164 | | |
165 | | { .code = "AC", .sid = SID_SECURITY_BUILTIN_PACKAGE_ANY_PACKAGE }, |
166 | | |
167 | | { .code = "LW", .sid = SID_SECURITY_MANDATORY_LOW }, |
168 | | { .code = "ME", .sid = SID_SECURITY_MANDATORY_MEDIUM }, |
169 | | { .code = "MP", .sid = SID_SECURITY_MANDATORY_MEDIUM_PLUS }, |
170 | | { .code = "HI", .sid = SID_SECURITY_MANDATORY_HIGH }, |
171 | | { .code = "SI", .sid = SID_SECURITY_MANDATORY_SYSTEM }, |
172 | | |
173 | | { .code = "AS", .sid = SID_AUTHENTICATION_AUTHORITY_ASSERTED_IDENTITY }, |
174 | | { .code = "SS", .sid = SID_SERVICE_ASSERTED_IDENTITY }, |
175 | | |
176 | | { .code = "RO", .forest_rid = DOMAIN_RID_ENTERPRISE_READONLY_DCS }, |
177 | | |
178 | | { .code = "LA", .machine_rid = DOMAIN_RID_ADMINISTRATOR }, |
179 | | { .code = "LG", .machine_rid = DOMAIN_RID_GUEST }, |
180 | | |
181 | | { .code = "DA", .domain_rid = DOMAIN_RID_ADMINS }, |
182 | | { .code = "DU", .domain_rid = DOMAIN_RID_USERS }, |
183 | | { .code = "DG", .domain_rid = DOMAIN_RID_GUESTS }, |
184 | | { .code = "DC", .domain_rid = DOMAIN_RID_DOMAIN_MEMBERS }, |
185 | | { .code = "DD", .domain_rid = DOMAIN_RID_DCS }, |
186 | | { .code = "CA", .domain_rid = DOMAIN_RID_CERT_ADMINS }, |
187 | | { .code = "SA", .forest_rid = DOMAIN_RID_SCHEMA_ADMINS }, |
188 | | { .code = "EA", .forest_rid = DOMAIN_RID_ENTERPRISE_ADMINS }, |
189 | | { .code = "PA", .domain_rid = DOMAIN_RID_POLICY_ADMINS }, |
190 | | |
191 | | { .code = "CN", .domain_rid = DOMAIN_RID_CLONEABLE_CONTROLLERS }, |
192 | | |
193 | | { .code = "AP", .domain_rid = DOMAIN_RID_PROTECTED_USERS }, |
194 | | { .code = "KA", .domain_rid = DOMAIN_RID_KEY_ADMINS }, |
195 | | { .code = "EK", .forest_rid = DOMAIN_RID_ENTERPRISE_KEY_ADMINS }, |
196 | | |
197 | | { .code = "RS", .domain_rid = DOMAIN_RID_RAS_SERVERS } |
198 | | }; |
199 | | |
200 | | /* |
201 | | decode a SID |
202 | | It can either be a special 2 letter code, or in S-* format |
203 | | */ |
204 | | static bool sddl_transition_decode_sid(const char **sddlp, |
205 | | struct sddl_transition_state *state, |
206 | | struct dom_sid *sid) |
207 | 185k | { |
208 | 185k | const char *sddl = (*sddlp); |
209 | 185k | size_t i; |
210 | | |
211 | | /* see if its in the numeric format */ |
212 | 185k | if (strncasecmp(sddl, "S-", 2) == 0) { |
213 | 27.4k | size_t len = strspn(sddl + 2, "-0123456789ABCDEFabcdefxX") + 2; |
214 | 27.4k | if (len < 5) { /* S-1-x */ |
215 | 5 | return false; |
216 | 5 | } |
217 | 27.4k | if (len > DOM_SID_STR_BUFLEN) { /* Invalid SID */ |
218 | 33 | return false; |
219 | 33 | } |
220 | 27.4k | if (sddl[len - 1] == 'D' && sddl[len] == ':') { |
221 | | /* |
222 | | * we have run into the "D:" dacl marker, mistaking it |
223 | | * for a hex digit. There is no other way for this |
224 | | * pair to occur at the end of a SID in SDDL. |
225 | | */ |
226 | 111 | len--; |
227 | 111 | } |
228 | | |
229 | 27.4k | { |
230 | 27.4k | const char *end = NULL; |
231 | 27.4k | char sid_str[DOM_SID_STR_BUFLEN + 1]; |
232 | 27.4k | bool ok; |
233 | | |
234 | 27.4k | memcpy(sid_str, sddl, len); |
235 | 27.4k | sid_str[len] = '\0'; |
236 | | |
237 | 27.4k | ok = dom_sid_parse_endp(sid_str, sid, &end); |
238 | 27.4k | if (!ok) { |
239 | 720 | DBG_WARNING("could not parse SID '%s'\n", |
240 | 720 | sid_str); |
241 | 720 | return false; |
242 | 720 | } |
243 | 26.7k | if (sid_str + len != end) { |
244 | 17 | DBG_WARNING("trailing junk after SID '%s'\n", |
245 | 17 | sid_str); |
246 | 17 | return false; |
247 | 17 | } |
248 | 26.7k | } |
249 | 26.6k | (*sddlp) += len; |
250 | 26.6k | return true; |
251 | 26.7k | } |
252 | | |
253 | | /* now check for one of the special codes */ |
254 | 5.11M | for (i=0;i<ARRAY_SIZE(sid_codes);i++) { |
255 | 5.11M | if (strncmp(sid_codes[i].code, sddl, 2) == 0) break; |
256 | 5.11M | } |
257 | 157k | if (i == ARRAY_SIZE(sid_codes)) { |
258 | 433 | DEBUG(1,("Unknown sddl sid code '%2.2s'\n", sddl)); |
259 | 433 | return false; |
260 | 433 | } |
261 | | |
262 | 157k | (*sddlp) += 2; |
263 | | |
264 | | |
265 | 157k | if (sid_codes[i].machine_rid != 0) { |
266 | 20.6k | return sid_compose(sid, |
267 | 20.6k | state->machine_sid, |
268 | 20.6k | sid_codes[i].machine_rid); |
269 | 20.6k | } |
270 | | |
271 | 136k | if (sid_codes[i].domain_rid != 0) { |
272 | 17.1k | return sid_compose(sid, |
273 | 17.1k | state->domain_sid, |
274 | 17.1k | sid_codes[i].domain_rid); |
275 | 17.1k | } |
276 | | |
277 | 119k | if (sid_codes[i].forest_rid != 0) { |
278 | 6.43k | return sid_compose(sid, |
279 | 6.43k | state->forest_sid, |
280 | 6.43k | sid_codes[i].forest_rid); |
281 | 6.43k | } |
282 | | |
283 | 113k | return dom_sid_parse(sid_codes[i].sid, sid); |
284 | 119k | } |
285 | | |
286 | | static struct dom_sid *sddl_transition_decode_sid_talloc( |
287 | | TALLOC_CTX *mem_ctx, |
288 | | const char **sddlp, |
289 | | struct sddl_transition_state *state) |
290 | 37.0k | { |
291 | 37.0k | struct dom_sid sid; |
292 | 37.0k | bool ok; |
293 | | |
294 | 37.0k | ok = sddl_transition_decode_sid(sddlp, state, &sid); |
295 | 37.0k | if (!ok) { |
296 | 833 | return NULL; |
297 | 833 | } |
298 | 36.1k | return dom_sid_dup(mem_ctx, &sid); |
299 | 37.0k | } |
300 | | |
301 | | struct dom_sid *sddl_decode_sid(TALLOC_CTX *mem_ctx, const char **sddlp, |
302 | | const struct dom_sid *domain_sid) |
303 | 34.5k | { |
304 | 34.5k | struct sddl_transition_state state = { |
305 | | /* |
306 | | * TODO: verify .machine_rid values really belong |
307 | | * to the machine_sid on a member, once |
308 | | * we pass machine_sid from the caller... |
309 | | */ |
310 | 34.5k | .machine_sid = domain_sid, |
311 | 34.5k | .domain_sid = domain_sid, |
312 | 34.5k | .forest_sid = domain_sid, |
313 | 34.5k | }; |
314 | | |
315 | 34.5k | return sddl_transition_decode_sid_talloc(mem_ctx, sddlp, &state); |
316 | 34.5k | } |
317 | | |
318 | | |
319 | | static const struct flag_map ace_types[] = { |
320 | | { "AU", SEC_ACE_TYPE_SYSTEM_AUDIT }, |
321 | | { "AL", SEC_ACE_TYPE_SYSTEM_ALARM }, |
322 | | { "OA", SEC_ACE_TYPE_ACCESS_ALLOWED_OBJECT }, |
323 | | { "OD", SEC_ACE_TYPE_ACCESS_DENIED_OBJECT }, |
324 | | { "OU", SEC_ACE_TYPE_SYSTEM_AUDIT_OBJECT }, |
325 | | { "OL", SEC_ACE_TYPE_SYSTEM_ALARM_OBJECT }, |
326 | | { "A", SEC_ACE_TYPE_ACCESS_ALLOWED }, |
327 | | { "D", SEC_ACE_TYPE_ACCESS_DENIED }, |
328 | | |
329 | | { "XA", SEC_ACE_TYPE_ACCESS_ALLOWED_CALLBACK }, |
330 | | { "XD", SEC_ACE_TYPE_ACCESS_DENIED_CALLBACK }, |
331 | | { "ZA", SEC_ACE_TYPE_ACCESS_ALLOWED_CALLBACK_OBJECT }, |
332 | | /* |
333 | | * SEC_ACE_TYPE_ACCESS_DENIED_CALLBACK_OBJECT exists but has |
334 | | * no SDDL flag. |
335 | | * |
336 | | * ZA and XU are switched in [MS-DTYP] as of version 36.0, |
337 | | * but this should be corrected in later versions. |
338 | | */ |
339 | | { "XU", SEC_ACE_TYPE_SYSTEM_AUDIT_CALLBACK }, |
340 | | |
341 | | { "RA", SEC_ACE_TYPE_SYSTEM_RESOURCE_ATTRIBUTE }, |
342 | | { NULL, 0 } |
343 | | }; |
344 | | |
345 | | static const struct flag_map ace_flags[] = { |
346 | | { "OI", SEC_ACE_FLAG_OBJECT_INHERIT }, |
347 | | { "CI", SEC_ACE_FLAG_CONTAINER_INHERIT }, |
348 | | { "NP", SEC_ACE_FLAG_NO_PROPAGATE_INHERIT }, |
349 | | { "IO", SEC_ACE_FLAG_INHERIT_ONLY }, |
350 | | { "ID", SEC_ACE_FLAG_INHERITED_ACE }, |
351 | | { "SA", SEC_ACE_FLAG_SUCCESSFUL_ACCESS }, |
352 | | { "FA", SEC_ACE_FLAG_FAILED_ACCESS }, |
353 | | { NULL, 0 }, |
354 | | }; |
355 | | |
356 | | static const struct flag_map ace_access_mask[] = { |
357 | | { "CC", SEC_ADS_CREATE_CHILD }, |
358 | | { "DC", SEC_ADS_DELETE_CHILD }, |
359 | | { "LC", SEC_ADS_LIST }, |
360 | | { "SW", SEC_ADS_SELF_WRITE }, |
361 | | { "RP", SEC_ADS_READ_PROP }, |
362 | | { "WP", SEC_ADS_WRITE_PROP }, |
363 | | { "DT", SEC_ADS_DELETE_TREE }, |
364 | | { "LO", SEC_ADS_LIST_OBJECT }, |
365 | | { "CR", SEC_ADS_CONTROL_ACCESS }, |
366 | | { "SD", SEC_STD_DELETE }, |
367 | | { "RC", SEC_STD_READ_CONTROL }, |
368 | | { "WD", SEC_STD_WRITE_DAC }, |
369 | | { "WO", SEC_STD_WRITE_OWNER }, |
370 | | { "GA", SEC_GENERIC_ALL }, |
371 | | { "GX", SEC_GENERIC_EXECUTE }, |
372 | | { "GW", SEC_GENERIC_WRITE }, |
373 | | { "GR", SEC_GENERIC_READ }, |
374 | | { NULL, 0 } |
375 | | }; |
376 | | |
377 | | static const struct flag_map decode_ace_access_mask[] = { |
378 | | { "FA", FILE_GENERIC_ALL }, |
379 | | { "FR", FILE_GENERIC_READ }, |
380 | | { "FW", FILE_GENERIC_WRITE }, |
381 | | { "FX", FILE_GENERIC_EXECUTE }, |
382 | | { NULL, 0 }, |
383 | | }; |
384 | | |
385 | | |
386 | | static char *sddl_match_file_rights(TALLOC_CTX *mem_ctx, |
387 | | uint32_t flags) |
388 | 2.76k | { |
389 | 2.76k | int i; |
390 | | |
391 | | /* try to find an exact match */ |
392 | 11.4k | for (i=0;decode_ace_access_mask[i].name;i++) { |
393 | 9.61k | if (decode_ace_access_mask[i].flag == flags) { |
394 | 905 | return talloc_strdup(mem_ctx, |
395 | 905 | decode_ace_access_mask[i].name); |
396 | 905 | } |
397 | 9.61k | } |
398 | 1.85k | return NULL; |
399 | 2.76k | } |
400 | | |
401 | | static bool sddl_decode_access(const char *str, uint32_t *pmask) |
402 | 148k | { |
403 | 148k | const char *str0 = str; |
404 | 148k | char *end = NULL; |
405 | 148k | uint32_t mask = 0; |
406 | 148k | unsigned long long numeric_mask; |
407 | 148k | int err; |
408 | | /* |
409 | | * The access mask can be a number or a series of flags. |
410 | | * |
411 | | * Canonically the number is expressed in hexadecimal (with 0x), but |
412 | | * per MS-DTYP and Windows behaviour, octal and decimal numbers are |
413 | | * also accepted. |
414 | | * |
415 | | * Windows has two behaviours we choose not to replicate: |
416 | | * |
417 | | * 1. numbers exceeding 0xffffffff are truncated at that point, |
418 | | * turning on all access flags. |
419 | | * |
420 | | * 2. negative numbers are accepted, so e.g. -2 becomes 0xfffffffe. |
421 | | */ |
422 | 148k | numeric_mask = smb_strtoull(str, &end, 0, &err, SMB_STR_STANDARD); |
423 | 148k | if (err == 0) { |
424 | 8.10k | if (numeric_mask > UINT32_MAX) { |
425 | 176 | DBG_WARNING("Bad numeric flag value - %llu in %s\n", |
426 | 176 | numeric_mask, str0); |
427 | 176 | return false; |
428 | 176 | } |
429 | 7.92k | if (end - str > sizeof("037777777777")) { |
430 | | /* here's the tricky thing: if a number is big |
431 | | * enough to overflow the uint64, it might end |
432 | | * up small enough to fit in the uint32, and |
433 | | * we'd miss that it overflowed. So we count |
434 | | * the digits -- any more than 12 (for |
435 | | * "037777777777") is too long for 32 bits, |
436 | | * and the shortest 64-bit wrapping string is |
437 | | * 19 (for "0x1" + 16 zeros). |
438 | | */ |
439 | 30 | DBG_WARNING("Bad numeric flag value in '%s'\n", str0); |
440 | 30 | return false; |
441 | 30 | } |
442 | 7.89k | if (*end != '\0') { |
443 | 37 | DBG_WARNING("Bad characters in '%s'\n", str0); |
444 | 37 | return false; |
445 | 37 | } |
446 | 7.85k | *pmask = numeric_mask; |
447 | 7.85k | return true; |
448 | 7.89k | } |
449 | | /* It's not a positive number, so we'll look for flags */ |
450 | | |
451 | 155k | while ((str[0] != '\0') && |
452 | 155k | (isupper((unsigned char)str[0]) || str[0] == ' ')) { |
453 | 15.2k | uint32_t flags = 0; |
454 | 15.2k | size_t len = 0; |
455 | 15.2k | bool found; |
456 | 16.5k | while (str[0] == ' ') { |
457 | | /* |
458 | | * Following Windows we accept spaces between flags |
459 | | * but not after flags. Not tabs, though, never tabs. |
460 | | */ |
461 | 1.31k | str++; |
462 | 1.31k | if (str[0] == '\0') { |
463 | 19 | DBG_WARNING("trailing whitespace in flags " |
464 | 19 | "- '%s'\n", str0); |
465 | 19 | return false; |
466 | 19 | } |
467 | 1.31k | } |
468 | 15.1k | found = sddl_map_flag( |
469 | 15.1k | ace_access_mask, str, &len, &flags); |
470 | 15.1k | found |= sddl_map_flag( |
471 | 15.1k | decode_ace_access_mask, str, &len, &flags); |
472 | 15.1k | if (!found) { |
473 | 113 | DEBUG(1, ("Unknown flag - %s in %s\n", str, str0)); |
474 | 113 | return false; |
475 | 113 | } |
476 | 15.0k | mask |= flags; |
477 | 15.0k | str += len; |
478 | 15.0k | } |
479 | 140k | if (*str != '\0') { |
480 | 44 | DBG_WARNING("Bad characters in '%s'\n", str0); |
481 | 44 | return false; |
482 | 44 | } |
483 | 140k | *pmask = mask; |
484 | 140k | return true; |
485 | 140k | } |
486 | | |
487 | | |
488 | | static bool sddl_decode_guid(const char *str, struct GUID *guid) |
489 | 5.44k | { |
490 | 5.44k | if (strlen(str) != 36) { |
491 | 107 | return false; |
492 | 107 | } |
493 | 5.34k | return parse_guid_string(str, guid); |
494 | 5.44k | } |
495 | | |
496 | | |
497 | | |
498 | | static DATA_BLOB sddl_decode_conditions(TALLOC_CTX *mem_ctx, |
499 | | const enum ace_condition_flags ace_condition_flags, |
500 | | const char *conditions, |
501 | | size_t *length, |
502 | | const char **msg, |
503 | | size_t *msg_offset) |
504 | 42.2k | { |
505 | 42.2k | DATA_BLOB blob = {0}; |
506 | 42.2k | struct ace_condition_script *script = NULL; |
507 | 42.2k | script = ace_conditions_compile_sddl(mem_ctx, |
508 | 42.2k | ace_condition_flags, |
509 | 42.2k | conditions, |
510 | 42.2k | msg, |
511 | 42.2k | msg_offset, |
512 | 42.2k | length); |
513 | 42.2k | if (script != NULL) { |
514 | 38.2k | bool ok = conditional_ace_encode_binary(mem_ctx, |
515 | 38.2k | script, |
516 | 38.2k | &blob); |
517 | 38.2k | if (! ok) { |
518 | 781 | DBG_ERR("could not blobify '%s'\n", conditions); |
519 | 781 | } |
520 | 38.2k | } |
521 | 42.2k | return blob; |
522 | 42.2k | } |
523 | | |
524 | | |
525 | | /* |
526 | | decode an ACE |
527 | | return true on success, false on failure |
528 | | note that this routine modifies the string |
529 | | */ |
530 | | static bool sddl_decode_ace(TALLOC_CTX *mem_ctx, |
531 | | struct security_ace *ace, |
532 | | const enum ace_condition_flags ace_condition_flags, |
533 | | char **sddl_copy, |
534 | | struct sddl_transition_state *state, |
535 | | const char **msg, size_t *msg_offset) |
536 | 149k | { |
537 | 149k | const char *tok[7]; |
538 | 149k | const char *s; |
539 | 149k | uint32_t v; |
540 | 149k | bool ok; |
541 | 149k | size_t len; |
542 | 149k | size_t count = 0; |
543 | 149k | char *str = *sddl_copy; |
544 | 149k | bool has_extra_data = false; |
545 | 149k | ZERO_STRUCTP(ace); |
546 | | |
547 | 149k | *msg_offset = 1; |
548 | 149k | if (*str != '(') { |
549 | 0 | *msg = talloc_strdup(mem_ctx, "Not an ACE"); |
550 | 0 | return false; |
551 | 0 | } |
552 | 149k | str++; |
553 | | /* |
554 | | * First we split apart the 6 (or 7) tokens. |
555 | | * |
556 | | * 0. ace type |
557 | | * 1. ace flags |
558 | | * 2. access mask |
559 | | * 3. object guid |
560 | | * 4. inherit guid |
561 | | * 5. sid |
562 | | * |
563 | | * 6/extra_data rare optional extra data |
564 | | */ |
565 | 149k | tok[0] = str; |
566 | 9.37M | while (*str != '\0') { |
567 | 9.37M | if (*str == ';') { |
568 | 804k | *str = '\0'; |
569 | 804k | str++; |
570 | 804k | count++; |
571 | 804k | tok[count] = str; |
572 | 804k | if (count == 6) { |
573 | | /* |
574 | | * this looks like a conditional ACE |
575 | | * or resource ACE, but we can't say |
576 | | * for sure until we look at the ACE |
577 | | * type (tok[0]), after the loop. |
578 | | */ |
579 | 58.3k | has_extra_data = true; |
580 | 58.3k | break; |
581 | 58.3k | } |
582 | 746k | continue; |
583 | 804k | } |
584 | | /* |
585 | | * we are not expecting a ')' in the 6 sections of an |
586 | | * ordinary ACE, except ending the last one. |
587 | | */ |
588 | 8.56M | if (*str == ')') { |
589 | 90.9k | count++; |
590 | 90.9k | *str = '\0'; |
591 | 90.9k | str++; |
592 | 90.9k | break; |
593 | 90.9k | } |
594 | 8.47M | str++; |
595 | 8.47M | } |
596 | 149k | if (count != 6) { |
597 | | /* we hit the '\0' or ')' before all of ';;;;;)' */ |
598 | 221 | *msg = talloc_asprintf(mem_ctx, |
599 | 221 | "malformed ACE with only %zu ';'", |
600 | 221 | MIN(count - 1, count)); |
601 | 221 | return false; |
602 | 221 | } |
603 | | |
604 | | /* parse ace type */ |
605 | 149k | ok = sddl_map_flag(ace_types, tok[0], &len, &v); |
606 | 149k | if (!ok) { |
607 | 42 | *msg = talloc_asprintf(mem_ctx, |
608 | 42 | "Unknown ACE type - %s", tok[0]); |
609 | 42 | return false; |
610 | 42 | } |
611 | 149k | if (tok[0][len] != '\0') { |
612 | 70 | *msg = talloc_asprintf(mem_ctx, |
613 | 70 | "Garbage after ACE type - %s", tok[0]); |
614 | 70 | return false; |
615 | 70 | } |
616 | | |
617 | 149k | ace->type = v; |
618 | | |
619 | | /* |
620 | | * Only callback and resource aces should have trailing data. |
621 | | */ |
622 | 149k | if (sec_ace_callback(ace->type)) { |
623 | 42.3k | if (! has_extra_data) { |
624 | 13 | *msg = talloc_strdup( |
625 | 13 | mem_ctx, |
626 | 13 | "callback ACE has no trailing data"); |
627 | 13 | *msg_offset = str - *sddl_copy; |
628 | 13 | return false; |
629 | 13 | } |
630 | 106k | } else if (sec_ace_resource(ace->type)) { |
631 | 15.8k | if (! has_extra_data) { |
632 | 6 | *msg = talloc_strdup( |
633 | 6 | mem_ctx, |
634 | 6 | "resource attribute ACE has no trailing data"); |
635 | 6 | *msg_offset = str - *sddl_copy; |
636 | 6 | return false; |
637 | 6 | } |
638 | 90.8k | } else if (has_extra_data) { |
639 | 19 | *msg = talloc_strdup( |
640 | 19 | mem_ctx, |
641 | 19 | "ACE has trailing section but is not a " |
642 | 19 | "callback or resource ACE"); |
643 | 19 | *msg_offset = str - *sddl_copy; |
644 | 19 | return false; |
645 | 19 | } |
646 | | |
647 | | /* ace flags */ |
648 | 149k | if (!sddl_map_flags(ace_flags, tok[1], &v, NULL, false)) { |
649 | 65 | *msg = talloc_strdup(mem_ctx, |
650 | 65 | "could not parse flags"); |
651 | 65 | *msg_offset = tok[1] - *sddl_copy; |
652 | 65 | return false; |
653 | 65 | } |
654 | 148k | ace->flags = v; |
655 | | |
656 | | /* access mask */ |
657 | 148k | ok = sddl_decode_access(tok[2], &ace->access_mask); |
658 | 148k | if (!ok) { |
659 | 419 | *msg = talloc_strdup(mem_ctx, |
660 | 419 | "could not parse access string"); |
661 | 419 | *msg_offset = tok[2] - *sddl_copy; |
662 | 419 | return false; |
663 | 419 | } |
664 | | |
665 | | /* object */ |
666 | 148k | if (tok[3][0] != 0) { |
667 | 2.73k | ok = sddl_decode_guid(tok[3], &ace->object.object.type.type); |
668 | 2.73k | if (!ok) { |
669 | 200 | *msg = talloc_strdup(mem_ctx, |
670 | 200 | "could not parse object GUID"); |
671 | 200 | *msg_offset = tok[3] - *sddl_copy; |
672 | 200 | return false; |
673 | 200 | } |
674 | 2.53k | ace->object.object.flags |= SEC_ACE_OBJECT_TYPE_PRESENT; |
675 | 2.53k | } |
676 | | |
677 | | /* inherit object */ |
678 | 148k | if (tok[4][0] != 0) { |
679 | 2.71k | ok = sddl_decode_guid(tok[4], |
680 | 2.71k | &ace->object.object.inherited_type.inherited_type); |
681 | 2.71k | if (!ok) { |
682 | 270 | *msg = talloc_strdup( |
683 | 270 | mem_ctx, |
684 | 270 | "could not parse inherited object GUID"); |
685 | 270 | *msg_offset = tok[4] - *sddl_copy; |
686 | 270 | return false; |
687 | 270 | } |
688 | 2.44k | ace->object.object.flags |= SEC_ACE_INHERITED_OBJECT_TYPE_PRESENT; |
689 | 2.44k | } |
690 | | |
691 | | /* trustee */ |
692 | 148k | s = tok[5]; |
693 | 148k | ok = sddl_transition_decode_sid(&s, state, &ace->trustee); |
694 | 148k | if (!ok) { |
695 | 375 | *msg = talloc_strdup( |
696 | 375 | mem_ctx, |
697 | 375 | "could not parse trustee SID"); |
698 | 375 | *msg_offset = tok[5] - *sddl_copy; |
699 | 375 | return false; |
700 | 375 | } |
701 | 147k | if (*s != '\0') { |
702 | 22 | *msg = talloc_strdup( |
703 | 22 | mem_ctx, |
704 | 22 | "garbage after trustee SID"); |
705 | 22 | *msg_offset = s - *sddl_copy; |
706 | 22 | return false; |
707 | 22 | } |
708 | | |
709 | 147k | if (sec_ace_callback(ace->type)) { |
710 | | /* |
711 | | * This is either a conditional ACE or some unknown |
712 | | * type of callback ACE that will be rejected by the |
713 | | * conditional ACE compiler. |
714 | | */ |
715 | 42.2k | size_t length; |
716 | 42.2k | DATA_BLOB conditions = {0}; |
717 | 42.2k | s = tok[6]; |
718 | | |
719 | 42.2k | conditions = sddl_decode_conditions(mem_ctx, |
720 | 42.2k | ace_condition_flags, |
721 | 42.2k | s, |
722 | 42.2k | &length, |
723 | 42.2k | msg, |
724 | 42.2k | msg_offset); |
725 | 42.2k | if (conditions.data == NULL) { |
726 | 4.78k | DBG_NOTICE("Conditional ACE compilation failure at %zu: %s\n", |
727 | 4.78k | *msg_offset, *msg); |
728 | 4.78k | *msg_offset += s - *sddl_copy; |
729 | 4.78k | return false; |
730 | 4.78k | } |
731 | 37.4k | ace->coda.conditions = conditions; |
732 | | |
733 | | /* |
734 | | * We have found the end of the conditions, and the |
735 | | * next character should be the ')' to end the ACE. |
736 | | */ |
737 | 37.4k | if (s[length] != ')') { |
738 | 1.54k | *msg = talloc_strdup( |
739 | 1.54k | mem_ctx, |
740 | 1.54k | "Conditional ACE has trailing bytes" |
741 | 1.54k | " or lacks ')'"); |
742 | 1.54k | *msg_offset = s + length - *sddl_copy; |
743 | 1.54k | return false; |
744 | 1.54k | } |
745 | 35.9k | str = discard_const_p(char, s + length + 1); |
746 | 105k | } else if (sec_ace_resource(ace->type)) { |
747 | 15.8k | size_t length; |
748 | 15.8k | struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 *claim = NULL; |
749 | | |
750 | 15.8k | if (! dom_sid_equal(&ace->trustee, &global_sid_World)) { |
751 | | /* these are just the rules */ |
752 | 166 | *msg = talloc_strdup( |
753 | 166 | mem_ctx, |
754 | 166 | "Resource Attribute ACE trustee must be " |
755 | 166 | "'S-1-1-0' or 'WD'."); |
756 | 166 | *msg_offset = tok[5] - *sddl_copy; |
757 | 166 | return false; |
758 | 166 | } |
759 | | |
760 | 15.6k | s = tok[6]; |
761 | 15.6k | claim = sddl_decode_resource_attr(mem_ctx, s, &length); |
762 | 15.6k | if (claim == NULL) { |
763 | 3.31k | *msg = talloc_strdup( |
764 | 3.31k | mem_ctx, |
765 | 3.31k | "Resource Attribute ACE parse failure"); |
766 | 3.31k | *msg_offset = s - *sddl_copy; |
767 | 3.31k | return false; |
768 | 3.31k | } |
769 | 12.3k | ace->coda.claim = *claim; |
770 | | |
771 | | /* |
772 | | * We want a ')' to end the ACE. |
773 | | */ |
774 | 12.3k | if (s[length] != ')') { |
775 | 794 | *msg = talloc_strdup( |
776 | 794 | mem_ctx, |
777 | 794 | "Resource Attribute ACE has trailing bytes" |
778 | 794 | " or lacks ')'"); |
779 | 794 | *msg_offset = s + length - *sddl_copy; |
780 | 794 | return false; |
781 | 794 | } |
782 | 11.5k | str = discard_const_p(char, s + length + 1); |
783 | 11.5k | } |
784 | | |
785 | 137k | *sddl_copy = str; |
786 | 137k | return true; |
787 | 147k | } |
788 | | |
789 | | static const struct flag_map acl_flags[] = { |
790 | | { "P", SEC_DESC_DACL_PROTECTED }, |
791 | | { "AR", SEC_DESC_DACL_AUTO_INHERIT_REQ }, |
792 | | { "AI", SEC_DESC_DACL_AUTO_INHERITED }, |
793 | | { NULL, 0 } |
794 | | }; |
795 | | |
796 | | /* |
797 | | decode an ACL |
798 | | */ |
799 | | static struct security_acl *sddl_decode_acl(struct security_descriptor *sd, |
800 | | const enum ace_condition_flags ace_condition_flags, |
801 | | const char **sddlp, uint32_t *flags, |
802 | | struct sddl_transition_state *state, |
803 | | const char **msg, size_t *msg_offset) |
804 | 20.3k | { |
805 | 20.3k | const char *sddl = *sddlp; |
806 | 20.3k | char *sddl_copy = NULL; |
807 | 20.3k | char *aces_start = NULL; |
808 | 20.3k | struct security_acl *acl; |
809 | 20.3k | size_t len; |
810 | 20.3k | *flags = 0; |
811 | | |
812 | 20.3k | acl = talloc_zero(sd, struct security_acl); |
813 | 20.3k | if (acl == NULL) { |
814 | 0 | return NULL; |
815 | 0 | } |
816 | 20.3k | acl->revision = SECURITY_ACL_REVISION_ADS; |
817 | | |
818 | 20.3k | if (isupper((unsigned char)sddl[0]) && sddl[1] == ':') { |
819 | | /* its an empty ACL */ |
820 | 84 | return acl; |
821 | 84 | } |
822 | | |
823 | | /* Windows AD allows spaces here */ |
824 | 20.8k | while (*sddl == ' ') { |
825 | 597 | sddl++; |
826 | 597 | } |
827 | | |
828 | | /* work out the ACL flags */ |
829 | 20.2k | if (!sddl_map_flags(acl_flags, sddl, flags, &len, true)) { |
830 | 0 | *msg = talloc_strdup(sd, "bad ACL flags"); |
831 | 0 | *msg_offset = 0; |
832 | 0 | talloc_free(acl); |
833 | 0 | return NULL; |
834 | 0 | } |
835 | 20.2k | sddl += len; |
836 | | |
837 | 20.2k | if (sddl[0] != '(') { |
838 | | /* |
839 | | * it is empty apart from the flags |
840 | | * (or the flags are bad, and we will find out when |
841 | | * we try to parse the next bit as a top-level fragment) |
842 | | */ |
843 | 296 | *sddlp = sddl; |
844 | 296 | return acl; |
845 | 296 | } |
846 | | |
847 | | /* |
848 | | * now the ACEs |
849 | | * |
850 | | * For this we make a copy of the rest of the SDDL, which the ACE |
851 | | * tokeniser will mutilate by putting '\0' where it finds ';'. |
852 | | * |
853 | | * We need to copy the rest of the SDDL string because it is not |
854 | | * possible in general to find where an ACL ends if there are |
855 | | * conditional ACEs. |
856 | | */ |
857 | | |
858 | 19.9k | sddl_copy = talloc_strdup(acl, sddl); |
859 | 19.9k | if (sddl_copy == NULL) { |
860 | 0 | TALLOC_FREE(acl); |
861 | 0 | return NULL; |
862 | 0 | } |
863 | 19.9k | aces_start = sddl_copy; |
864 | | |
865 | 157k | while (*sddl_copy == '(') { |
866 | 149k | bool ok; |
867 | 149k | if (acl->num_aces > UINT16_MAX / 16) { |
868 | | /* |
869 | | * We can't fit this many ACEs in a wire ACL |
870 | | * which has a 16 bit size field (and 16 is |
871 | | * the minimal size of an ACE with no subauths). |
872 | | */ |
873 | 4 | talloc_free(acl); |
874 | 4 | return NULL; |
875 | 4 | } |
876 | | |
877 | 149k | acl->aces = talloc_realloc(acl, acl->aces, struct security_ace, |
878 | 149k | acl->num_aces+1); |
879 | 149k | if (acl->aces == NULL) { |
880 | 0 | talloc_free(acl); |
881 | 0 | return NULL; |
882 | 0 | } |
883 | 149k | ok = sddl_decode_ace(acl->aces, &acl->aces[acl->num_aces], |
884 | 149k | ace_condition_flags, |
885 | 149k | &sddl_copy, state, msg, msg_offset); |
886 | 149k | if (!ok) { |
887 | 12.3k | *msg_offset += sddl_copy - aces_start; |
888 | 12.3k | talloc_steal(sd, *msg); |
889 | 12.3k | talloc_free(acl); |
890 | 12.3k | return NULL; |
891 | 12.3k | } |
892 | 137k | acl->num_aces++; |
893 | 137k | } |
894 | 7.66k | sddl += sddl_copy - aces_start; |
895 | 7.66k | TALLOC_FREE(aces_start); |
896 | 7.66k | (*sddlp) = sddl; |
897 | 7.66k | return acl; |
898 | 19.9k | } |
899 | | |
900 | | /* |
901 | | * Decode a security descriptor in SDDL format, catching compilation |
902 | | * error messages, if any. |
903 | | * |
904 | | * The message will be a direct talloc child of mem_ctx or NULL. |
905 | | */ |
906 | | struct security_descriptor *sddl_decode_err_msg(TALLOC_CTX *mem_ctx, const char *sddl, |
907 | | const struct dom_sid *domain_sid, |
908 | | const enum ace_condition_flags ace_condition_flags, |
909 | | const char **msg, size_t *msg_offset) |
910 | 22.1k | { |
911 | 22.1k | struct sddl_transition_state state = { |
912 | | /* |
913 | | * TODO: verify .machine_rid values really belong |
914 | | * to the machine_sid on a member, once |
915 | | * we pass machine_sid from the caller... |
916 | | */ |
917 | 22.1k | .machine_sid = domain_sid, |
918 | 22.1k | .domain_sid = domain_sid, |
919 | 22.1k | .forest_sid = domain_sid, |
920 | 22.1k | }; |
921 | 22.1k | const char *start = sddl; |
922 | 22.1k | struct security_descriptor *sd = NULL; |
923 | | |
924 | 22.1k | if (msg == NULL || msg_offset == NULL) { |
925 | 0 | DBG_ERR("Programmer misbehaviour: use sddl_decode() " |
926 | 0 | "or provide msg pointers.\n"); |
927 | 0 | return NULL; |
928 | 0 | } |
929 | 22.1k | *msg = NULL; |
930 | 22.1k | *msg_offset = 0; |
931 | | |
932 | 22.1k | sd = security_descriptor_initialise(mem_ctx); |
933 | 22.1k | if (sd == NULL) { |
934 | 0 | return NULL; |
935 | 0 | } |
936 | | |
937 | 31.9k | while (*sddl) { |
938 | 23.1k | uint32_t flags; |
939 | 23.1k | char c = sddl[0]; |
940 | 23.1k | if (sddl[1] != ':') { |
941 | 225 | *msg = talloc_strdup(mem_ctx, |
942 | 225 | "expected '[OGDS]:' section start " |
943 | 225 | "(or the previous section ended prematurely)"); |
944 | 225 | goto failed; |
945 | 225 | } |
946 | 22.8k | sddl += 2; |
947 | 22.8k | switch (c) { |
948 | 10.4k | case 'D': |
949 | 10.4k | if (sd->dacl != NULL) goto failed; |
950 | 10.4k | sd->dacl = sddl_decode_acl(sd, ace_condition_flags, &sddl, &flags, &state, msg, msg_offset); |
951 | 10.4k | if (sd->dacl == NULL) goto failed; |
952 | 1.34k | sd->type |= flags | SEC_DESC_DACL_PRESENT; |
953 | 1.34k | break; |
954 | 9.91k | case 'S': |
955 | 9.91k | if (sd->sacl != NULL) goto failed; |
956 | 9.91k | sd->sacl = sddl_decode_acl(sd, ace_condition_flags, &sddl, &flags, &state, msg, msg_offset); |
957 | 9.91k | if (sd->sacl == NULL) goto failed; |
958 | | /* this relies on the SEC_DESC_SACL_* flags being |
959 | | 1 bit shifted from the SEC_DESC_DACL_* flags */ |
960 | 6.70k | sd->type |= (flags<<1) | SEC_DESC_SACL_PRESENT; |
961 | 6.70k | break; |
962 | 1.96k | case 'O': |
963 | 1.96k | if (sd->owner_sid != NULL) goto failed; |
964 | 1.96k | sd->owner_sid = sddl_transition_decode_sid_talloc( |
965 | 1.96k | sd, &sddl, &state); |
966 | 1.96k | if (sd->owner_sid == NULL) goto failed; |
967 | 1.34k | break; |
968 | 1.34k | case 'G': |
969 | 529 | if (sd->group_sid != NULL) goto failed; |
970 | 526 | sd->group_sid = sddl_transition_decode_sid_talloc( |
971 | 526 | sd, &sddl, &state); |
972 | 526 | if (sd->group_sid == NULL) goto failed; |
973 | 357 | break; |
974 | 357 | default: |
975 | 4 | *msg = talloc_strdup(mem_ctx, "unexpected character (expected [OGDS])"); |
976 | 4 | goto failed; |
977 | 22.8k | } |
978 | 22.8k | } |
979 | 8.81k | return sd; |
980 | 13.3k | failed: |
981 | 13.3k | if (*msg != NULL) { |
982 | 11.7k | *msg = talloc_steal(mem_ctx, *msg); |
983 | 11.7k | } |
984 | | /* |
985 | | * The actual message (*msg) might still be NULL, but the |
986 | | * offset at least provides a clue. |
987 | | */ |
988 | 13.3k | *msg_offset += sddl - start; |
989 | | |
990 | 13.3k | if (*msg_offset > strlen(sddl)) { |
991 | | /* |
992 | | * It's not that we *don't* trust our pointer difference |
993 | | * arithmetic, just that we *shouldn't*. Let's render it |
994 | | * harmless, before Python tries printing 18 quadrillion |
995 | | * spaces. |
996 | | */ |
997 | 5.33k | DBG_WARNING("sddl error message offset %zu is too big\n", |
998 | 5.33k | *msg_offset); |
999 | 5.33k | *msg_offset = 0; |
1000 | 5.33k | } |
1001 | 13.3k | DEBUG(2,("Badly formatted SDDL '%s'\n", sddl)); |
1002 | 13.3k | talloc_free(sd); |
1003 | 13.3k | return NULL; |
1004 | 22.1k | } |
1005 | | |
1006 | | |
1007 | | /* |
1008 | | decode a security descriptor in SDDL format |
1009 | | */ |
1010 | | struct security_descriptor *sddl_decode(TALLOC_CTX *mem_ctx, const char *sddl, |
1011 | | const struct dom_sid *domain_sid) |
1012 | 22.1k | { |
1013 | 22.1k | const char *msg = NULL; |
1014 | 22.1k | size_t msg_offset = 0; |
1015 | 22.1k | struct security_descriptor *sd = sddl_decode_err_msg(mem_ctx, |
1016 | 22.1k | sddl, |
1017 | 22.1k | domain_sid, |
1018 | 22.1k | ACE_CONDITION_FLAG_ALLOW_DEVICE, |
1019 | 22.1k | &msg, |
1020 | 22.1k | &msg_offset); |
1021 | 22.1k | if (sd == NULL) { |
1022 | 13.3k | DBG_NOTICE("could not decode '%s'\n", sddl); |
1023 | 13.3k | if (msg != NULL) { |
1024 | 11.7k | DBG_NOTICE(" %*c\n", |
1025 | 11.7k | (int)msg_offset, '^'); |
1026 | 11.7k | DBG_NOTICE("error '%s'\n", msg); |
1027 | 11.7k | talloc_free(discard_const(msg)); |
1028 | 11.7k | } |
1029 | 13.3k | } |
1030 | 22.1k | return sd; |
1031 | 22.1k | } |
1032 | | |
1033 | | /* |
1034 | | turn a set of flags into a string |
1035 | | */ |
1036 | | static char *sddl_flags_to_string(TALLOC_CTX *mem_ctx, const struct flag_map *map, |
1037 | | uint32_t flags, bool check_all) |
1038 | 82.3k | { |
1039 | 82.3k | int i; |
1040 | 82.3k | char *s; |
1041 | | |
1042 | | /* try to find an exact match */ |
1043 | 930k | for (i=0;map[i].name;i++) { |
1044 | 875k | if (map[i].flag == flags) { |
1045 | 27.9k | return talloc_strdup(mem_ctx, map[i].name); |
1046 | 27.9k | } |
1047 | 875k | } |
1048 | | |
1049 | 54.4k | s = talloc_strdup(mem_ctx, ""); |
1050 | | |
1051 | | /* now by bits */ |
1052 | 670k | for (i=0;map[i].name;i++) { |
1053 | 615k | if ((flags & map[i].flag) != 0) { |
1054 | 28.4k | s = talloc_asprintf_append_buffer(s, "%s", map[i].name); |
1055 | 28.4k | if (s == NULL) goto failed; |
1056 | 28.4k | flags &= ~map[i].flag; |
1057 | 28.4k | } |
1058 | 615k | } |
1059 | | |
1060 | 54.4k | if (check_all && flags != 0) { |
1061 | 2.76k | goto failed; |
1062 | 2.76k | } |
1063 | | |
1064 | 51.6k | return s; |
1065 | | |
1066 | 2.76k | failed: |
1067 | 2.76k | talloc_free(s); |
1068 | 2.76k | return NULL; |
1069 | 54.4k | } |
1070 | | |
1071 | | /* |
1072 | | encode a sid in SDDL format |
1073 | | */ |
1074 | | static char *sddl_transition_encode_sid(TALLOC_CTX *mem_ctx, const struct dom_sid *sid, |
1075 | | struct sddl_transition_state *state) |
1076 | 40.3k | { |
1077 | 40.3k | bool in_machine = dom_sid_in_domain(state->machine_sid, sid); |
1078 | 40.3k | bool in_domain = dom_sid_in_domain(state->domain_sid, sid); |
1079 | 40.3k | bool in_forest = dom_sid_in_domain(state->forest_sid, sid); |
1080 | 40.3k | struct dom_sid_buf buf; |
1081 | 40.3k | const char *sidstr = dom_sid_str_buf(sid, &buf); |
1082 | 40.3k | uint32_t rid = 0; |
1083 | 40.3k | size_t i; |
1084 | | |
1085 | 40.3k | if (sid->num_auths > 1) { |
1086 | 24.1k | rid = sid->sub_auths[sid->num_auths-1]; |
1087 | 24.1k | } |
1088 | | |
1089 | 2.03M | for (i=0;i<ARRAY_SIZE(sid_codes);i++) { |
1090 | | /* seen if its a well known sid */ |
1091 | 2.02M | if (sid_codes[i].sid != NULL) { |
1092 | 1.69M | int cmp; |
1093 | | |
1094 | 1.69M | cmp = strcmp(sidstr, sid_codes[i].sid); |
1095 | 1.69M | if (cmp != 0) { |
1096 | 1.68M | continue; |
1097 | 1.68M | } |
1098 | | |
1099 | 11.3k | return talloc_strdup(mem_ctx, sid_codes[i].code); |
1100 | 1.69M | } |
1101 | | |
1102 | 328k | if (rid == 0) { |
1103 | 174k | continue; |
1104 | 174k | } |
1105 | | |
1106 | 154k | if (in_machine && sid_codes[i].machine_rid == rid) { |
1107 | 9.39k | return talloc_strdup(mem_ctx, sid_codes[i].code); |
1108 | 9.39k | } |
1109 | 145k | if (in_domain && sid_codes[i].domain_rid == rid) { |
1110 | 2.48k | return talloc_strdup(mem_ctx, sid_codes[i].code); |
1111 | 2.48k | } |
1112 | 142k | if (in_forest && sid_codes[i].forest_rid == rid) { |
1113 | 2.34k | return talloc_strdup(mem_ctx, sid_codes[i].code); |
1114 | 2.34k | } |
1115 | 142k | } |
1116 | | |
1117 | 14.8k | return talloc_strdup(mem_ctx, sidstr); |
1118 | 40.3k | } |
1119 | | |
1120 | | char *sddl_encode_sid(TALLOC_CTX *mem_ctx, const struct dom_sid *sid, |
1121 | | const struct dom_sid *domain_sid) |
1122 | 13.7k | { |
1123 | 13.7k | struct sddl_transition_state state = { |
1124 | | /* |
1125 | | * TODO: verify .machine_rid values really belong |
1126 | | * to the machine_sid on a member, once |
1127 | | * we pass machine_sid from the caller... |
1128 | | */ |
1129 | 13.7k | .machine_sid = domain_sid, |
1130 | 13.7k | .domain_sid = domain_sid, |
1131 | 13.7k | .forest_sid = domain_sid, |
1132 | 13.7k | }; |
1133 | 13.7k | return sddl_transition_encode_sid(mem_ctx, sid, &state); |
1134 | 13.7k | } |
1135 | | |
1136 | | |
1137 | | |
1138 | | /* |
1139 | | encode an ACE in SDDL format |
1140 | | */ |
1141 | | static char *sddl_transition_encode_ace(TALLOC_CTX *mem_ctx, const struct security_ace *ace, |
1142 | | struct sddl_transition_state *state) |
1143 | 26.1k | { |
1144 | 26.1k | char *sddl = NULL; |
1145 | 26.1k | TALLOC_CTX *tmp_ctx; |
1146 | 26.1k | struct GUID_txt_buf object_buf, iobject_buf; |
1147 | 26.1k | const char *sddl_type="", *sddl_flags="", *sddl_mask="", |
1148 | 26.1k | *sddl_object="", *sddl_iobject="", *sddl_trustee=""; |
1149 | 26.1k | tmp_ctx = talloc_new(mem_ctx); |
1150 | 26.1k | if (tmp_ctx == NULL) { |
1151 | 0 | DEBUG(0, ("talloc_new failed\n")); |
1152 | 0 | return NULL; |
1153 | 0 | } |
1154 | | |
1155 | 26.1k | sddl_type = sddl_flags_to_string(tmp_ctx, ace_types, ace->type, true); |
1156 | 26.1k | if (sddl_type == NULL) { |
1157 | 0 | goto failed; |
1158 | 0 | } |
1159 | | |
1160 | 26.1k | sddl_flags = sddl_flags_to_string(tmp_ctx, ace_flags, ace->flags, |
1161 | 26.1k | true); |
1162 | 26.1k | if (sddl_flags == NULL) { |
1163 | 0 | goto failed; |
1164 | 0 | } |
1165 | | |
1166 | 26.1k | sddl_mask = sddl_flags_to_string(tmp_ctx, ace_access_mask, |
1167 | 26.1k | ace->access_mask, true); |
1168 | 26.1k | if (sddl_mask == NULL) { |
1169 | 2.76k | sddl_mask = sddl_match_file_rights(tmp_ctx, |
1170 | 2.76k | ace->access_mask); |
1171 | 2.76k | if (sddl_mask == NULL) { |
1172 | 1.85k | sddl_mask = talloc_asprintf(tmp_ctx, "0x%x", |
1173 | 1.85k | ace->access_mask); |
1174 | 1.85k | } |
1175 | 2.76k | if (sddl_mask == NULL) { |
1176 | 0 | goto failed; |
1177 | 0 | } |
1178 | 2.76k | } |
1179 | | |
1180 | 26.1k | if (sec_ace_object(ace->type)) { |
1181 | 14.9k | const struct security_ace_object *object = &ace->object.object; |
1182 | | |
1183 | 14.9k | if (ace->object.object.flags & SEC_ACE_OBJECT_TYPE_PRESENT) { |
1184 | 477 | sddl_object = GUID_buf_string( |
1185 | 477 | &object->type.type, &object_buf); |
1186 | 477 | } |
1187 | | |
1188 | 14.9k | if (ace->object.object.flags & |
1189 | 14.9k | SEC_ACE_INHERITED_OBJECT_TYPE_PRESENT) { |
1190 | 558 | sddl_iobject = GUID_buf_string( |
1191 | 558 | &object->inherited_type.inherited_type, |
1192 | 558 | &iobject_buf); |
1193 | 558 | } |
1194 | 14.9k | } |
1195 | 26.1k | sddl_trustee = sddl_transition_encode_sid(tmp_ctx, &ace->trustee, state); |
1196 | 26.1k | if (sddl_trustee == NULL) { |
1197 | 0 | goto failed; |
1198 | 0 | } |
1199 | | |
1200 | 26.1k | if (sec_ace_callback(ace->type)) { |
1201 | | /* encode the conditional part */ |
1202 | 13.7k | struct ace_condition_script *s = NULL; |
1203 | 13.7k | const char *sddl_conditions = NULL; |
1204 | | |
1205 | 13.7k | s = parse_conditional_ace(tmp_ctx, ace->coda.conditions); |
1206 | | |
1207 | 13.7k | if (s == NULL) { |
1208 | 0 | goto failed; |
1209 | 0 | } |
1210 | | |
1211 | 13.7k | sddl_conditions = sddl_from_conditional_ace(tmp_ctx, s); |
1212 | 13.7k | if (sddl_conditions == NULL) { |
1213 | 0 | goto failed; |
1214 | 0 | } |
1215 | | |
1216 | 13.7k | sddl = talloc_asprintf(mem_ctx, "%s;%s;%s;%s;%s;%s;%s", |
1217 | 13.7k | sddl_type, sddl_flags, sddl_mask, |
1218 | 13.7k | sddl_object, sddl_iobject, |
1219 | 13.7k | sddl_trustee, sddl_conditions); |
1220 | 13.7k | } else if (sec_ace_resource(ace->type)) { |
1221 | | /* encode the resource part */ |
1222 | 3.44k | const char *coda = NULL; |
1223 | 3.44k | coda = sddl_resource_attr_from_claim(tmp_ctx, |
1224 | 3.44k | &ace->coda.claim); |
1225 | | |
1226 | 3.44k | if (coda == NULL) { |
1227 | 0 | DBG_WARNING("resource ACE has invalid claim\n"); |
1228 | 0 | goto failed; |
1229 | 0 | } |
1230 | 3.44k | sddl = talloc_asprintf(mem_ctx, "%s;%s;%s;%s;%s;%s;%s", |
1231 | 3.44k | sddl_type, sddl_flags, sddl_mask, |
1232 | 3.44k | sddl_object, sddl_iobject, |
1233 | 3.44k | sddl_trustee, coda); |
1234 | 9.00k | } else { |
1235 | 9.00k | sddl = talloc_asprintf(mem_ctx, "%s;%s;%s;%s;%s;%s", |
1236 | 9.00k | sddl_type, sddl_flags, sddl_mask, |
1237 | 9.00k | sddl_object, sddl_iobject, sddl_trustee); |
1238 | 9.00k | } |
1239 | 26.1k | failed: |
1240 | 26.1k | talloc_free(tmp_ctx); |
1241 | 26.1k | return sddl; |
1242 | 26.1k | } |
1243 | | |
1244 | | char *sddl_encode_ace(TALLOC_CTX *mem_ctx, const struct security_ace *ace, |
1245 | | const struct dom_sid *domain_sid) |
1246 | 0 | { |
1247 | 0 | struct sddl_transition_state state = { |
1248 | | /* |
1249 | | * TODO: verify .machine_rid values really belong |
1250 | | * to the machine_sid on a member, once |
1251 | | * we pass machine_sid from the caller... |
1252 | | */ |
1253 | 0 | .machine_sid = domain_sid, |
1254 | 0 | .domain_sid = domain_sid, |
1255 | 0 | .forest_sid = domain_sid, |
1256 | 0 | }; |
1257 | 0 | return sddl_transition_encode_ace(mem_ctx, ace, &state); |
1258 | 0 | } |
1259 | | |
1260 | | /* |
1261 | | encode an ACL in SDDL format |
1262 | | */ |
1263 | | static char *sddl_encode_acl(TALLOC_CTX *mem_ctx, const struct security_acl *acl, |
1264 | | uint32_t flags, struct sddl_transition_state *state) |
1265 | 3.78k | { |
1266 | 3.78k | char *sddl; |
1267 | 3.78k | uint32_t i; |
1268 | | |
1269 | | /* add any ACL flags */ |
1270 | 3.78k | sddl = sddl_flags_to_string(mem_ctx, acl_flags, flags, false); |
1271 | 3.78k | if (sddl == NULL) goto failed; |
1272 | | |
1273 | | /* now the ACEs, encoded in braces */ |
1274 | 29.9k | for (i=0;i<acl->num_aces;i++) { |
1275 | 26.1k | char *ace = sddl_transition_encode_ace(sddl, &acl->aces[i], state); |
1276 | 26.1k | if (ace == NULL) goto failed; |
1277 | 26.1k | sddl = talloc_asprintf_append_buffer(sddl, "(%s)", ace); |
1278 | 26.1k | if (sddl == NULL) goto failed; |
1279 | 26.1k | talloc_free(ace); |
1280 | 26.1k | } |
1281 | | |
1282 | 3.78k | return sddl; |
1283 | | |
1284 | 0 | failed: |
1285 | 0 | talloc_free(sddl); |
1286 | 0 | return NULL; |
1287 | 3.78k | } |
1288 | | |
1289 | | |
1290 | | /* |
1291 | | encode a security descriptor to SDDL format |
1292 | | */ |
1293 | | char *sddl_encode(TALLOC_CTX *mem_ctx, const struct security_descriptor *sd, |
1294 | | const struct dom_sid *domain_sid) |
1295 | 4.09k | { |
1296 | 4.09k | struct sddl_transition_state state = { |
1297 | | /* |
1298 | | * TODO: verify .machine_rid values really belong |
1299 | | * to the machine_sid on a member, once |
1300 | | * we pass machine_sid from the caller... |
1301 | | */ |
1302 | 4.09k | .machine_sid = domain_sid, |
1303 | 4.09k | .domain_sid = domain_sid, |
1304 | 4.09k | .forest_sid = domain_sid, |
1305 | 4.09k | }; |
1306 | 4.09k | char *sddl; |
1307 | 4.09k | TALLOC_CTX *tmp_ctx; |
1308 | | |
1309 | | /* start with a blank string */ |
1310 | 4.09k | sddl = talloc_strdup(mem_ctx, ""); |
1311 | 4.09k | if (sddl == NULL) goto failed; |
1312 | | |
1313 | 4.09k | tmp_ctx = talloc_new(sddl); |
1314 | 4.09k | if (tmp_ctx == NULL) { |
1315 | 0 | goto failed; |
1316 | 0 | } |
1317 | | |
1318 | 4.09k | if (sd->owner_sid != NULL) { |
1319 | 377 | char *sid = sddl_transition_encode_sid(tmp_ctx, sd->owner_sid, &state); |
1320 | 377 | if (sid == NULL) goto failed; |
1321 | 377 | sddl = talloc_asprintf_append_buffer(sddl, "O:%s", sid); |
1322 | 377 | if (sddl == NULL) goto failed; |
1323 | 377 | } |
1324 | | |
1325 | 4.09k | if (sd->group_sid != NULL) { |
1326 | 119 | char *sid = sddl_transition_encode_sid(tmp_ctx, sd->group_sid, &state); |
1327 | 119 | if (sid == NULL) goto failed; |
1328 | 119 | sddl = talloc_asprintf_append_buffer(sddl, "G:%s", sid); |
1329 | 119 | if (sddl == NULL) goto failed; |
1330 | 119 | } |
1331 | | |
1332 | 4.09k | if ((sd->type & SEC_DESC_DACL_PRESENT) && sd->dacl != NULL) { |
1333 | 386 | char *acl = sddl_encode_acl(tmp_ctx, sd->dacl, sd->type, &state); |
1334 | 386 | if (acl == NULL) goto failed; |
1335 | 386 | sddl = talloc_asprintf_append_buffer(sddl, "D:%s", acl); |
1336 | 386 | if (sddl == NULL) goto failed; |
1337 | 386 | } |
1338 | | |
1339 | 4.09k | if ((sd->type & SEC_DESC_SACL_PRESENT) && sd->sacl != NULL) { |
1340 | 3.40k | char *acl = sddl_encode_acl(tmp_ctx, sd->sacl, sd->type>>1, &state); |
1341 | 3.40k | if (acl == NULL) goto failed; |
1342 | 3.40k | sddl = talloc_asprintf_append_buffer(sddl, "S:%s", acl); |
1343 | 3.40k | if (sddl == NULL) goto failed; |
1344 | 3.40k | } |
1345 | | |
1346 | 4.09k | talloc_free(tmp_ctx); |
1347 | 4.09k | return sddl; |
1348 | | |
1349 | 0 | failed: |
1350 | 0 | talloc_free(sddl); |
1351 | 0 | return NULL; |
1352 | 4.09k | } |