2 Unix SMB/CIFS implementation.
4 security descriptor description language functions
6 Copyright (C) Andrew Tridgell 2005
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.
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.
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/>.
23 #include "libcli/security/dom_sid.h"
24 #include "librpc/gen_ndr/ndr_misc.h"
25 #include "system/locale.h"
33 map a series of letter codes into a uint32_t
35 static bool sddl_map_flags(const struct flag_map
*map
, const char *str
,
36 uint32_t *flags
, size_t *len
)
38 const char *str0
= str
;
41 while (str
[0] && isupper(str
[0])) {
43 for (i
=0;map
[i
].name
;i
++) {
44 size_t l
= strlen(map
[i
].name
);
45 if (strncmp(map
[i
].name
, str
, l
) == 0) {
46 *flags
|= map
[i
].flag
;
52 if (map
[i
].name
== NULL
) {
53 DEBUG(1, ("Unknown flag - %s in %s\n", str
, str0
));
61 a mapping between the 2 letter SID codes and sid strings
70 { "CO", SID_CREATOR_OWNER
},
71 { "CG", SID_CREATOR_GROUP
},
73 { "NU", SID_NT_NETWORK
},
74 { "IU", SID_NT_INTERACTIVE
},
75 { "SU", SID_NT_SERVICE
},
76 { "AN", SID_NT_ANONYMOUS
},
77 { "ED", SID_NT_ENTERPRISE_DCS
},
78 { "PS", SID_NT_SELF
},
79 { "AU", SID_NT_AUTHENTICATED_USERS
},
80 { "RC", SID_NT_RESTRICTED
},
81 { "SY", SID_NT_SYSTEM
},
82 { "LS", SID_NT_LOCAL_SERVICE
},
83 { "NS", SID_NT_NETWORK_SERVICE
},
85 { "BA", SID_BUILTIN_ADMINISTRATORS
},
86 { "BU", SID_BUILTIN_USERS
},
87 { "BG", SID_BUILTIN_GUESTS
},
88 { "PU", SID_BUILTIN_POWER_USERS
},
89 { "AO", SID_BUILTIN_ACCOUNT_OPERATORS
},
90 { "SO", SID_BUILTIN_SERVER_OPERATORS
},
91 { "PO", SID_BUILTIN_PRINT_OPERATORS
},
92 { "BO", SID_BUILTIN_BACKUP_OPERATORS
},
93 { "RE", SID_BUILTIN_REPLICATOR
},
94 { "BR", SID_BUILTIN_RAS_SERVERS
},
95 { "RU", SID_BUILTIN_PREW2K
},
96 { "RD", SID_BUILTIN_REMOTE_DESKTOP_USERS
},
97 { "NO", SID_BUILTIN_NETWORK_CONF_OPERATORS
},
98 { "IF", SID_BUILTIN_INCOMING_FOREST_TRUST
},
100 { "LA", NULL
, DOMAIN_RID_ADMINISTRATOR
},
101 { "LG", NULL
, DOMAIN_RID_GUEST
},
102 { "LK", NULL
, DOMAIN_RID_KRBTGT
},
104 { "ER", NULL
, DOMAIN_RID_ENTERPRISE_READONLY_DCS
},
105 { "DA", NULL
, DOMAIN_RID_ADMINS
},
106 { "DU", NULL
, DOMAIN_RID_USERS
},
107 { "DG", NULL
, DOMAIN_RID_GUESTS
},
108 { "DC", NULL
, DOMAIN_RID_DOMAIN_MEMBERS
},
109 { "DD", NULL
, DOMAIN_RID_DCS
},
110 { "CA", NULL
, DOMAIN_RID_CERT_ADMINS
},
111 { "SA", NULL
, DOMAIN_RID_SCHEMA_ADMINS
},
112 { "EA", NULL
, DOMAIN_RID_ENTERPRISE_ADMINS
},
113 { "PA", NULL
, DOMAIN_RID_POLICY_ADMINS
},
114 { "RO", NULL
, DOMAIN_RID_READONLY_DCS
},
115 { "RS", NULL
, DOMAIN_RID_RAS_SERVERS
}
120 It can either be a special 2 letter code, or in S-* format
122 static struct dom_sid
*sddl_decode_sid(TALLOC_CTX
*mem_ctx
, const char **sddlp
,
123 const struct dom_sid
*domain_sid
)
125 const char *sddl
= (*sddlp
);
128 /* see if its in the numeric format */
129 if (strncmp(sddl
, "S-", 2) == 0) {
132 size_t len
= strspn(sddl
+2, "-0123456789");
133 sid_str
= talloc_strndup(mem_ctx
, sddl
, len
+2);
138 sid
= dom_sid_parse_talloc(mem_ctx
, sid_str
);
139 talloc_free(sid_str
);
143 /* now check for one of the special codes */
144 for (i
=0;i
<ARRAY_SIZE(sid_codes
);i
++) {
145 if (strncmp(sid_codes
[i
].code
, sddl
, 2) == 0) break;
147 if (i
== ARRAY_SIZE(sid_codes
)) {
148 DEBUG(1,("Unknown sddl sid code '%2.2s'\n", sddl
));
154 if (sid_codes
[i
].sid
== NULL
) {
155 return dom_sid_add_rid(mem_ctx
, domain_sid
, sid_codes
[i
].rid
);
158 return dom_sid_parse_talloc(mem_ctx
, sid_codes
[i
].sid
);
161 static const struct flag_map ace_types
[] = {
162 { "AU", SEC_ACE_TYPE_SYSTEM_AUDIT
},
163 { "AL", SEC_ACE_TYPE_SYSTEM_ALARM
},
164 { "OA", SEC_ACE_TYPE_ACCESS_ALLOWED_OBJECT
},
165 { "OD", SEC_ACE_TYPE_ACCESS_DENIED_OBJECT
},
166 { "OU", SEC_ACE_TYPE_SYSTEM_AUDIT_OBJECT
},
167 { "OL", SEC_ACE_TYPE_SYSTEM_ALARM_OBJECT
},
168 { "A", SEC_ACE_TYPE_ACCESS_ALLOWED
},
169 { "D", SEC_ACE_TYPE_ACCESS_DENIED
},
173 static const struct flag_map ace_flags
[] = {
174 { "OI", SEC_ACE_FLAG_OBJECT_INHERIT
},
175 { "CI", SEC_ACE_FLAG_CONTAINER_INHERIT
},
176 { "NP", SEC_ACE_FLAG_NO_PROPAGATE_INHERIT
},
177 { "IO", SEC_ACE_FLAG_INHERIT_ONLY
},
178 { "ID", SEC_ACE_FLAG_INHERITED_ACE
},
179 { "SA", SEC_ACE_FLAG_SUCCESSFUL_ACCESS
},
180 { "FA", SEC_ACE_FLAG_FAILED_ACCESS
},
184 static const struct flag_map ace_access_mask
[] = {
185 { "RP", SEC_ADS_READ_PROP
},
186 { "WP", SEC_ADS_WRITE_PROP
},
187 { "CR", SEC_ADS_CONTROL_ACCESS
},
188 { "CC", SEC_ADS_CREATE_CHILD
},
189 { "DC", SEC_ADS_DELETE_CHILD
},
190 { "LC", SEC_ADS_LIST
},
191 { "LO", SEC_ADS_LIST_OBJECT
},
192 { "RC", SEC_STD_READ_CONTROL
},
193 { "WO", SEC_STD_WRITE_OWNER
},
194 { "WD", SEC_STD_WRITE_DAC
},
195 { "SD", SEC_STD_DELETE
},
196 { "DT", SEC_ADS_DELETE_TREE
},
197 { "SW", SEC_ADS_SELF_WRITE
},
198 { "GA", SEC_GENERIC_ALL
},
199 { "GR", SEC_GENERIC_READ
},
200 { "GW", SEC_GENERIC_WRITE
},
201 { "GX", SEC_GENERIC_EXECUTE
},
207 return true on success, false on failure
208 note that this routine modifies the string
210 static bool sddl_decode_ace(TALLOC_CTX
*mem_ctx
, struct security_ace
*ace
, char *str
,
211 const struct dom_sid
*domain_sid
)
221 /* parse out the 6 tokens */
224 char *ptr
= strchr(str
, ';');
225 if (ptr
== NULL
) return false;
232 if (!sddl_map_flags(ace_types
, tok
[0], &v
, NULL
)) {
238 if (!sddl_map_flags(ace_flags
, tok
[1], &v
, NULL
)) {
244 if (strncmp(tok
[2], "0x", 2) == 0) {
245 ace
->access_mask
= strtol(tok
[2], NULL
, 16);
247 if (!sddl_map_flags(ace_access_mask
, tok
[2], &v
, NULL
)) {
250 ace
->access_mask
= v
;
254 if (tok
[3][0] != 0) {
255 NTSTATUS status
= GUID_from_string(tok
[3],
256 &ace
->object
.object
.type
.type
);
257 if (!NT_STATUS_IS_OK(status
)) {
260 ace
->object
.object
.flags
|= SEC_ACE_OBJECT_TYPE_PRESENT
;
264 if (tok
[4][0] != 0) {
265 NTSTATUS status
= GUID_from_string(tok
[4],
266 &ace
->object
.object
.inherited_type
.inherited_type
);
267 if (!NT_STATUS_IS_OK(status
)) {
270 ace
->object
.object
.flags
|= SEC_ACE_INHERITED_OBJECT_TYPE_PRESENT
;
275 sid
= sddl_decode_sid(mem_ctx
, &s
, domain_sid
);
285 static const struct flag_map acl_flags
[] = {
286 { "P", SEC_DESC_DACL_PROTECTED
},
287 { "AR", SEC_DESC_DACL_AUTO_INHERIT_REQ
},
288 { "AI", SEC_DESC_DACL_AUTO_INHERITED
},
295 static struct security_acl
*sddl_decode_acl(struct security_descriptor
*sd
,
296 const char **sddlp
, uint32_t *flags
,
297 const struct dom_sid
*domain_sid
)
299 const char *sddl
= *sddlp
;
300 struct security_acl
*acl
;
305 acl
= talloc_zero(sd
, struct security_acl
);
306 if (acl
== NULL
) return NULL
;
307 acl
->revision
= SECURITY_ACL_REVISION_ADS
;
309 if (isupper(sddl
[0]) && sddl
[1] == ':') {
310 /* its an empty ACL */
314 /* work out the ACL flags */
315 if (!sddl_map_flags(acl_flags
, sddl
, flags
, &len
)) {
322 while (*sddl
== '(') {
324 len
= strcspn(sddl
+1, ")");
325 astr
= talloc_strndup(acl
, sddl
+1, len
);
326 if (astr
== NULL
|| sddl
[len
+1] != ')') {
330 acl
->aces
= talloc_realloc(acl
, acl
->aces
, struct security_ace
,
332 if (acl
->aces
== NULL
) {
336 if (!sddl_decode_ace(acl
->aces
, &acl
->aces
[acl
->num_aces
],
341 switch (acl
->aces
[acl
->num_aces
].type
) {
342 case SEC_ACE_TYPE_ACCESS_ALLOWED_OBJECT
:
343 case SEC_ACE_TYPE_ACCESS_DENIED_OBJECT
:
344 case SEC_ACE_TYPE_SYSTEM_AUDIT_OBJECT
:
345 case SEC_ACE_TYPE_SYSTEM_ALARM_OBJECT
:
346 acl
->revision
= SECURITY_ACL_REVISION_ADS
;
361 decode a security descriptor in SDDL format
363 struct security_descriptor
*sddl_decode(TALLOC_CTX
*mem_ctx
, const char *sddl
,
364 const struct dom_sid
*domain_sid
)
366 struct security_descriptor
*sd
;
367 sd
= talloc_zero(mem_ctx
, struct security_descriptor
);
369 sd
->revision
= SECURITY_DESCRIPTOR_REVISION_1
;
370 sd
->type
= SEC_DESC_SELF_RELATIVE
;
375 if (sddl
[1] != ':') goto failed
;
380 if (sd
->dacl
!= NULL
) goto failed
;
381 sd
->dacl
= sddl_decode_acl(sd
, &sddl
, &flags
, domain_sid
);
382 if (sd
->dacl
== NULL
) goto failed
;
383 sd
->type
|= flags
| SEC_DESC_DACL_PRESENT
;
386 if (sd
->sacl
!= NULL
) goto failed
;
387 sd
->sacl
= sddl_decode_acl(sd
, &sddl
, &flags
, domain_sid
);
388 if (sd
->sacl
== NULL
) goto failed
;
389 /* this relies on the SEC_DESC_SACL_* flags being
390 1 bit shifted from the SEC_DESC_DACL_* flags */
391 sd
->type
|= (flags
<<1) | SEC_DESC_SACL_PRESENT
;
394 if (sd
->owner_sid
!= NULL
) goto failed
;
395 sd
->owner_sid
= sddl_decode_sid(sd
, &sddl
, domain_sid
);
396 if (sd
->owner_sid
== NULL
) goto failed
;
399 if (sd
->group_sid
!= NULL
) goto failed
;
400 sd
->group_sid
= sddl_decode_sid(sd
, &sddl
, domain_sid
);
401 if (sd
->group_sid
== NULL
) goto failed
;
409 DEBUG(2,("Badly formatted SDDL '%s'\n", sddl
));
415 turn a set of flags into a string
417 static char *sddl_flags_to_string(TALLOC_CTX
*mem_ctx
, const struct flag_map
*map
,
418 uint32_t flags
, bool check_all
)
423 /* try to find an exact match */
424 for (i
=0;map
[i
].name
;i
++) {
425 if (map
[i
].flag
== flags
) {
426 return talloc_strdup(mem_ctx
, map
[i
].name
);
430 s
= talloc_strdup(mem_ctx
, "");
433 for (i
=0;map
[i
].name
;i
++) {
434 if ((flags
& map
[i
].flag
) != 0) {
435 s
= talloc_asprintf_append_buffer(s
, "%s", map
[i
].name
);
436 if (s
== NULL
) goto failed
;
437 flags
&= ~map
[i
].flag
;
441 if (check_all
&& flags
!= 0) {
453 encode a sid in SDDL format
455 static char *sddl_encode_sid(TALLOC_CTX
*mem_ctx
, const struct dom_sid
*sid
,
456 const struct dom_sid
*domain_sid
)
461 sidstr
= dom_sid_string(mem_ctx
, sid
);
462 if (sidstr
== NULL
) return NULL
;
464 /* seen if its a well known sid */
465 for (i
=0;sid_codes
[i
].sid
;i
++) {
466 if (strcmp(sidstr
, sid_codes
[i
].sid
) == 0) {
468 return talloc_strdup(mem_ctx
, sid_codes
[i
].code
);
472 /* or a well known rid in our domain */
473 if (dom_sid_in_domain(domain_sid
, sid
)) {
474 uint32_t rid
= sid
->sub_auths
[sid
->num_auths
-1];
475 for (;i
<ARRAY_SIZE(sid_codes
);i
++) {
476 if (rid
== sid_codes
[i
].rid
) {
478 return talloc_strdup(mem_ctx
, sid_codes
[i
].code
);
485 /* TODO: encode well known sids as two letter codes */
486 return dom_sid_string(mem_ctx
, sid
);
491 encode an ACE in SDDL format
493 static char *sddl_encode_ace(TALLOC_CTX
*mem_ctx
, const struct security_ace
*ace
,
494 const struct dom_sid
*domain_sid
)
498 const char *s_type
="", *s_flags
="", *s_mask
="",
499 *s_object
="", *s_iobject
="", *s_trustee
="";
501 tmp_ctx
= talloc_new(mem_ctx
);
502 if (tmp_ctx
== NULL
) {
503 DEBUG(0, ("talloc_new failed\n"));
507 s_type
= sddl_flags_to_string(tmp_ctx
, ace_types
, ace
->type
, true);
508 if (s_type
== NULL
) goto failed
;
510 s_flags
= sddl_flags_to_string(tmp_ctx
, ace_flags
, ace
->flags
, true);
511 if (s_flags
== NULL
) goto failed
;
513 s_mask
= sddl_flags_to_string(tmp_ctx
, ace_access_mask
, ace
->access_mask
, true);
514 if (s_mask
== NULL
) {
515 s_mask
= talloc_asprintf(tmp_ctx
, "0x%08x", ace
->access_mask
);
516 if (s_mask
== NULL
) goto failed
;
519 if (ace
->type
== SEC_ACE_TYPE_ACCESS_ALLOWED_OBJECT
||
520 ace
->type
== SEC_ACE_TYPE_ACCESS_DENIED_OBJECT
||
521 ace
->type
== SEC_ACE_TYPE_SYSTEM_AUDIT_OBJECT
||
522 ace
->type
== SEC_ACE_TYPE_SYSTEM_AUDIT_OBJECT
) {
523 if (ace
->object
.object
.flags
& SEC_ACE_OBJECT_TYPE_PRESENT
) {
524 s_object
= GUID_string(tmp_ctx
, &ace
->object
.object
.type
.type
);
525 if (s_object
== NULL
) goto failed
;
528 if (ace
->object
.object
.flags
& SEC_ACE_INHERITED_OBJECT_TYPE_PRESENT
) {
529 s_iobject
= GUID_string(tmp_ctx
, &ace
->object
.object
.inherited_type
.inherited_type
);
530 if (s_iobject
== NULL
) goto failed
;
534 s_trustee
= sddl_encode_sid(tmp_ctx
, &ace
->trustee
, domain_sid
);
535 if (s_trustee
== NULL
) goto failed
;
537 sddl
= talloc_asprintf(mem_ctx
, "%s;%s;%s;%s;%s;%s",
538 s_type
, s_flags
, s_mask
, s_object
, s_iobject
, s_trustee
);
541 talloc_free(tmp_ctx
);
546 encode an ACL in SDDL format
548 static char *sddl_encode_acl(TALLOC_CTX
*mem_ctx
, const struct security_acl
*acl
,
549 uint32_t flags
, const struct dom_sid
*domain_sid
)
554 /* add any ACL flags */
555 sddl
= sddl_flags_to_string(mem_ctx
, acl_flags
, flags
, false);
556 if (sddl
== NULL
) goto failed
;
558 /* now the ACEs, encoded in braces */
559 for (i
=0;i
<acl
->num_aces
;i
++) {
560 char *ace
= sddl_encode_ace(sddl
, &acl
->aces
[i
], domain_sid
);
561 if (ace
== NULL
) goto failed
;
562 sddl
= talloc_asprintf_append_buffer(sddl
, "(%s)", ace
);
563 if (sddl
== NULL
) goto failed
;
576 encode a security descriptor to SDDL format
578 char *sddl_encode(TALLOC_CTX
*mem_ctx
, const struct security_descriptor
*sd
,
579 const struct dom_sid
*domain_sid
)
584 /* start with a blank string */
585 sddl
= talloc_strdup(mem_ctx
, "");
586 if (sddl
== NULL
) goto failed
;
588 tmp_ctx
= talloc_new(mem_ctx
);
590 if (sd
->owner_sid
!= NULL
) {
591 char *sid
= sddl_encode_sid(tmp_ctx
, sd
->owner_sid
, domain_sid
);
592 if (sid
== NULL
) goto failed
;
593 sddl
= talloc_asprintf_append_buffer(sddl
, "O:%s", sid
);
594 if (sddl
== NULL
) goto failed
;
597 if (sd
->group_sid
!= NULL
) {
598 char *sid
= sddl_encode_sid(tmp_ctx
, sd
->group_sid
, domain_sid
);
599 if (sid
== NULL
) goto failed
;
600 sddl
= talloc_asprintf_append_buffer(sddl
, "G:%s", sid
);
601 if (sddl
== NULL
) goto failed
;
604 if ((sd
->type
& SEC_DESC_DACL_PRESENT
) && sd
->dacl
!= NULL
) {
605 char *acl
= sddl_encode_acl(tmp_ctx
, sd
->dacl
, sd
->type
, domain_sid
);
606 if (acl
== NULL
) goto failed
;
607 sddl
= talloc_asprintf_append_buffer(sddl
, "D:%s", acl
);
608 if (sddl
== NULL
) goto failed
;
611 if ((sd
->type
& SEC_DESC_SACL_PRESENT
) && sd
->sacl
!= NULL
) {
612 char *acl
= sddl_encode_acl(tmp_ctx
, sd
->sacl
, sd
->type
>>1, domain_sid
);
613 if (acl
== NULL
) goto failed
;
614 sddl
= talloc_asprintf_append_buffer(sddl
, "S:%s", acl
);
615 if (sddl
== NULL
) goto failed
;
618 talloc_free(tmp_ctx
);