上一篇我们提到,安全描述符可通过ConvertSecurityDescriptorToStringSecurityDescriptor()和ConvertStringSecurityDescriptorToSecurityDescriptor()两个函数在二进制和字符串两种形式间转换,我们很自然地会想到,要实现这种转换,需要为字符串形式的安全描述符定义严格的语法规则,用来精确无误地描述安全描述符各个部分的格式,这就是安全描述符定义语言SDDL(Security Descriptor Definition Language)所扮演的角色。
用SDDL定义的安全描述符是一个以空字符(null)结尾的字符串,具有复杂的语法规则,细节附在文后,现重点介绍其主要框架。
回忆前面文章中描述的安全描述符结构,其中有四个主要部分,在SDDL中分别用四个字符标记来表示如下:
- O:owner_sid
- G:group_sid
- D:dacl_flags(string_ace1)(string_ace2)...(string_acen)
- S:sacl_flags(string_ace1)(string_ace2)...(string_acen)
"O:"表示对象的所有者,"G:"表示对象所有者所属的主组,“D:"表示DACL,"S:"表示SACL。
owner_sid是一个标识对象所有者的SID字符串,group_sid是标识对象所有者所属主组的SID字符串,代表各类SID的具体字符串清单附在文后。dacl_flags是应用于DACL的安全描述符控制标志,可以是以下字符串标记的组合:
控制标志的字符形式 | 在sddl.h中的定义 | 说明 |
"P" | SDDL_PROTECTED | 设置了SE_DACL_PROTECTED标志 |
"AR" | SDDL_AUTO_INHERIT_REQ | 设置了SE_DACL_AUTO_INHERIT_REQ标志 |
"AI" | SDDL_AUTO_INHERITED | 设置了SE_DACL_AUTO_INHERIT标志 |
"NO_ACCESS_CONTROL" | SDDL_NULL_ACL | ACL为空。(Windows Server 2008,Windows Vista,Windows Server 2003本标志无效) |
sacl_flags是应用于SACL的安全描述符控制标志,使用与DACL相同的控制字符串。string_ace是描述ACE的字符串,ACE字符串必须放在圆括号中。
对于安全描述符中的各种控制位,SDDL采用不同的方式处理。SE_DACL_PRESENT和SE_SACL_PRESENT分别标识安全描述符中存在DACL和SACL,在字符串形式中体现为存在"D:"和"S:"标记,即只要这两个标记存在,就表示SE_DACL_PRESENT和SE_SACL_PRESENT被设置了。
SE_OWNER_DEFAULTED、SE_GROUP_DEFAULTED、SE_DACL_DEFAULTED和SE_SACL_DEFAULTED不在字符串形式中出现。
SE_SELF_RELATIVE位也不在字符串形式中出现,但调用ConvertStringSecurityDescriptorToSecurityDescriptor()从字符串形式转换到二进制形式时,该标志是被设置的。
其他的控制标志在dacl_flags或sacl_flags字段中体现。
在调用ConvertSecurityDescriptorToStringSecurityDescriptor()将安全描述符由二进制形式转换成字符串形式时,不存在的组件不会在字符串形式中出现,比如如果没有设置SE_DACL_PRESENT标志,字符串中不会有"D:"部分。也可以通过入口参数SECURITY_INFORMATION设置相应的标志值来指定要包含在输出字符串中的成分。
如果字符串中包含"D:"或"S:",但后面不跟任何信息,就表示这是一个空的ACL。
- ACE字符串
在字符串形式的安全描述符中,每个代表ACE的串都要放在圆括号中。每个ACE字符串都遵循以下格式,每字段间用分号(;)隔离:
ace_type;ace_flags;rights;object_guid;inherit_object_guid;account_sid;(resource_attribute)
act_type为ACE_HEADER中AceType成员的每个取值都赋予了一个简短的字符串标识,用来说明该ACE的类型,这些定义都在sddl.h中。
ACE 类型串 | sddl.h中对应常量 | AceType 值(见ACE_HEADER文档) |
”A“ | SDDL_ACCESS_ALLOWED | ACCESS_ALLOWED_ACE_TYPE |
"D" | SDDL_ACCESS_DENIED | ACCESS_DENIED_ACE_TYPE |
"OA" | SDDL_OBJECT_ACCESS_ALLOWED | ACCESS_ALLOWED_OBJECT_ACE_TYPE |
"OD" | SDDL_OBJECT_ACCESS_DENIED | ACCESS_DENIED_OBJECT_ACE_TYPE |
"AU" | SDDL_AUDIT | SYSTEM_AUDIT_ACE_TYPE |
"AL" | SDDL_ALARM | SYSTEM_ALARM_ACE_TYPE |
"OU" | SDDL_OBJECT_AUDIT | SYSTEM_AUDIT_OBJECT_ACE_TYPE |
"OL" | SDDL_OBJECT_ALARM | SYSTEM_ALARM_OBJECT_ACE_TYPE |
"ML" | SDDL_MANDATORY_LABEL | SYSTEM_MANDATORY_LABEL_ACE_TYPE Windows Server 2003中无效 |
"XA" | SDDL_CALLBACK_ACCESS_ALLOWED | ACCESS_ALLOWED_CALLBACK_ACE_TYPE Windows Server 2008, Windows Vista and Windows Server 2003中无效 |
"XD" | SDDL_CALLBACK_ACCESS_DENIED | ACCESS_DENIED_CALLBACK_ACE_TYPE Windows Server 2008, Windows Vista and Windows Server 2003中无效 |
"RA" | SDDL_RESOURCE_ATTRIBUTE | SYSTEM_RESOURCE_ATTRIBUTE_ACE_TYPE Windows Server 2008 R2, Windows 7, Windows Server 2008, Windows Vista and Windows Server 2003中无效 |
"SP" | SDDL_SCOPED_POLICY_ID | SYSTEM_SCOPED_POLICY_ID_ACE_TYPE Windows Server 2008 R2, Windows 7, Windows Server 2008, Windows Vista and Windows Server 2003中无效 |
"XU" | SDDL_CALLBACK_AUDIT | SYSTEM_AUDIT_CALLBACK_ACE_TYPE Windows Server 2008 R2, Windows 7, Windows Server 2008, Windows Vista and Windows Server 2003中无效 |
"ZA" | SDDL_CALLBACK_OBJECT_ACCESS_ALLOWED | ACCESS_ALLOWED_CALLBACK_ACE_TYPE Windows Server 2008 R2, Windows 7, Windows Server 2008, Windows Vista and Windows Server 2003中无效 |
"TL" | SDDL_PROCESS_TRUST_LABEL | SYSTEM_PROCESS_TRUST_LABEL_ACE_TYPE Windows Server 2012, Windows 8, Windows Server 2008 R2, Windows 7, Windows Server 2008, Windows Vista and Windows Server 2003中无效 |
"FL" | SDDL_ACCESS_FILTER | SYSTEM_ACCESS_FILTER_ACE_TYPE |
ace_flags字段为ACE_HEADER中AceFlags成员的每个取值都赋予了一个简短的字符串标识,用来在ACE字符串中说明该ACE的工作机制,这些定义也都在sddl.h中。
ACE标志串 | sddl.h中对应常量 | ACE标志值 |
"CI" | SDDL_CONTAINER_INHERIT | CONTAINER_INHERIT_ACE |
"OI" | SDDL_OBJECT_INHERIT | OBJECT_INHERIT_ACE |
"NP" | SDDL_NO_PROPAGATE | NO_PROPAGATE_INHERIT_ACE |
"IO" | SDDL_INHERIT_ONLY | INHERIT_ONLY_ACE |
"ID" | SDDL_INHERITED INHERITED_ACE | INHERITED_ACE |
"SA" | SDDL_AUDIT_SUCCESS | SUCCESSFUL_ACCESS_ACE_FLAG |
"FA" | SDDL_AUDIT_FAILURE | FAILED_ACCESS_ACE_FLAG |
"TP" | SDDL_TRUST_PROTECTED_FILTER | TRUST_PROTECTED_FILTER_ACE_FLAG Windows Server 2016, Windows 10 Version 1607, Windows 10 Version 1511, Windows 10 Version 1507, Windows Server 2012 R2, Windows 8.1, Windows Server 2012, Windows 8, Windows Server 2008 R2, Windows 7, Windows Server 2008, Windows Vista and Windows Server 2003中无效。 |
"CR" | SDDL_CRITICAL | CRITICAL_ACE_FLAG Windows Server Version 1803, Windows 10 Version 1803, Windows Server Version 1709, Windows 10 Version 1709, Windows 10 Version 1703, Windows Server 2016, Windows 10 Version 1607, Windows 10 Version 1511, Windows 10 Version 1507, Windows Server 2012 R2, Windows 8.1, Windows Server 2012, Windows 8, Windows Server 2008 R2, Windows 7, Windows Server 2008, Windows Vista and Windows Server 2003中无效 |
ACE_HEADER中AceType和AceFlags的取值都可以在ACE_HEADDER文档(ACE_HEADER (winnt.h) - Win32 apps | Microsoft Learn)中查询到。
rights字段是ACE控制的权限的字符串表示,它既可以用权限位的十六进制字符串表示,如"0x7800003F",也可以用下面各表中的字符串。
通用访问权限
权限字符串 | sddl.h中对应常量 | 权限值 |
"GA" | SDDL_GENERIC_ALL GENERIC | ALL |
"GR" | SDDL_GENERIC_READ | GENERIC_READ |
"GW" | SDDL_GENERIC_WRITE | GENERIC_WRITE |
"GX" | SDDL_GENERIC_EXECUTE | GENERIC_EXECUTE |
标准访问权限
权限字符串 | sddl.h中对应常量 | 权限值 |
"RC" | SDDL_READ_CONTROL | READ_CONTROL |
"SD" | SDDL_STANDARD_DELETE | DELETE |
"WD" | SDDL_WRITE_DAC | WRITE_DAC |
"WO" | SDDL_WRITE_OWNER | WRITE_OWNER |
目录服务对象访问权限
权限字符串 | sddl.h中对应常量 | 权限值 |
"RP" | SDDL_READ_PROPERTY | ADS_RIGHT_DS_READ_PROP |
"WP" | SDDL_WRITE_PROPERTY | ADS_RIGHT_DS_WRITE_PROP |
"CC" | SDDL_CREATE_CHILD | ADS_RIGHT_DS_CREATE_CHILD |
"DC" | SDDL_DELETE_CHILD | ADS_RIGHT_DS_DELETE_CHILD |
"LC" | SDDL_LIST_CHILDREN | ADS_RIGHT_ACTRL_DS_LIST |
"SW" | SDDL_SELF_WRITE | DS_SELF |
"LO" | SDDL_LIST_OBJECT | DS_LIST_OBJECT |
"DT" | SDDL_DELETE_TREE | DS_DELETE_TREE |
"CR" | SDDL_CONTROL_ACCESS | ADS_RIGHT_DS_CONTROL_ACCESS |
文件访问权限
权限字符串 | Sddl.h中对应常量 | 权限值 |
“FA” | SDDL_FILE_ALL | FILE_GENERIC_ALL |
“FR” | SDDL_FILE_READ | FILE_GENERIC_READ |
“FW” | SDDL_FILE_WRITE | FILE_GENERIC_WRITE |
“FX” | SDDL_FILE_EXECUTE | FILE_GENERIC_EXECUTE |
注册表访问权限
权限字符串 | sddl.h中对应常量 | 权限值 |
"KA" | SDDL_KEY_ALL | KEY_ALL_ACCESS |
"KR" | SDDL_KEY_READ | KEY_READ |
"KW" | SDDL_KEY_WRITE | KEY_WRITE |
"KX" | SDDL_KEY_EXECUTE | KEY_EXECUTE |
MIC(Mandatory Integrity Control)访问权限
权限字符串 | sddl.h中对应常量 | 权限值 |
"NR" | SDDL_NO_READ | SYSTEM_MANDATORY_LABEL_NO_READ_UP Windows Server 2008, Windows Vista and Windows Server 2003:中无效 |
"NW" | SDDL_NO_WRITE_UP | SYSTEM_MANDATORY_LABEL_NO_WRITE_UP Windows Server 2008, Windows Vista and Windows Server 2003:中无效 |
"NX" | SDDL_NO_EXECUTE_UP | SYSTEM_MANDATORY_LABEL_NO_EXECUTE_UP Windows Server 2008, Windows Vista and Windows Server 2003:中无效 |
object_guid字段是对象特定ACE中的ObjectType成员指定的GUID的字符串表示,可用UuidtoString()函数生成该字符串。下面是两个常用的GUID:
权限和GUID | 权限 |
CR;ab721a53-1e2f-11d0-9819-00aa0040529b | 更改密码 |
CR;00299570-246d-11d0-a768-00aa006e0529 | 重设密码 |
inherit_object_guid字段是对象特定ACE中的InheritObjectType成员的GUID的字符串表示,该字符串可由UuidToString()生成。
account_sid字段是ACE针对的受信任实体的SID字符串。
最后一个字段是resource_attribute,专用于资源ACE,是可选的。资源ACE是一种新型的ACE,在Windows 7之前的Windows客户端版本和Windows Server 2008 R2之前的Windows服务器版本之前是没有资源ACE的。资源ACE可为对象指定附加的属性,以条件ACE的形式指定访问或审核策略。在下面的条件ACE部分再做一下简单介绍。
现在我们看一下ACE的实例:
(A;;RPWPCCDCLCSWRCWDWOGA;;;S-1-1-0)
该ACE可分解成下面的ACE信息:
AceType : 0x00(ACCESS_ALLOWED_ACE_TYPE)
AceFlags: 0x00
Access Mask:0x100e003f
READ_CONTROL
WRITE_DAC
WRITE_OWNER
GENERIC_ALL
Other access rights(0x0000003f)
Ace Sid : (S-1-1-0)
再看一个安全描述符的实例:
“O:AOG:DAD(A;;RPWPCCDCLCSWRCWDWOGA;;;S-1-0-0)”
该字符串描述了一下安全描述符:
Revision: 0x00000001
Control : 0x0004
SE_DACL_PRESENT
Owner: (S-1-5-32-548)
PrimaryGroup: (S-1-5-21-397955417-626881126-188441444-512)
DACL
Revision: 0x02
Size: 0x001c
AceCount:0x0001
Ace[00]
AceType: 0x00(ACCESS_ALLOWED_ACE_TYPE)
AceSize: 0x0014
InheritFlags: 0x00
Access Mask: 0x100e003f
READ_CONTROL
WRITE_DAC
WRITE_OWNER
GENERIC_ALL
Others(0x0000003f)
Ace Sid: (S-1-0-0)
SACL
Not present
这两个例子都涉及到了普通ACE的SDDL表示,还有一种新型的功能更强大、结构更复杂的ACE叫条件ACE,SDDL也为其定义了语法规则,值得用比较大的篇幅去介绍,下一篇我们介绍一下条件ACE。
附:SID字符串
SDDL alias | Well-Known SID name |
"DA" | DOMAIN_ADMINS |
"DG" | DOMAIN_GUESTS |
"DU" | DOMAIN_USERS |
"ED" | ENTERPRISE_DOMAIN_CONTROLLERS |
"DD" | DOMAIN_DOMAIN_CONTROLLERS |
"DC" | DOMAIN_COMPUTERS |
"BA" | BUILTIN_ADMINISTRATORS |
"BG" | BUILTIN_GUESTS |
"BU" | BUILTIN_USERS |
"LA" | ADMINISTRATOR<81> |
"LG" | GUEST |
"AO" | ACCOUNT_OPERATORS |
"BO" | BACKUP_OPERATORS |
"PO" | PRINTER_OPERATORS |
"SO" | SERVER_OPERATORS |
"AU" | AUTHENTICATED_USERS |
"PS" | PRINCIPAL_SELF |
"CO" | CREATOR_OWNER |
"CG" | CREATOR_GROUP |
"SY" | LOCAL_SYSTEM |
"PU" | POWER_USERS |
"WD" | EVERYONE |
"RE" | REPLICATOR |
"IU" | INTERACTIVE |
"NU" | NETWORK |
"SU" | SERVICE |
"RC" | RESTRICTED_CODE |
"WR" | WRITE_RESTRICTED_CODE |
"AN" | ANONYMOUS |
"SA" | SCHEMA_ADMINISTRATORS |
"CA" | CERT_PUBLISHERS |
"RS" | RAS_SERVERS |
"EA" | ENTERPRISE_ADMINS |
"PA" | GROUP_POLICY_CREATOR_OWNER |
"RU" | ALIAS_PREW2KCOMPACC |
"LS" | LOCAL_SERVICE |
"NS" | NETWORK_SERVICE |
"RD" | REMOTE_DESKTOP |
"NO" | NETWORK_CONFIGURATION_OPS |
"MU" | PERFMON_USERS |
"LU" | PERFLOG_USERS |
"IS" | IIS_USERS |
"CY" | CRYPTO_OPERATORS |
"OW" | OWNER_RIGHTS |
"ER" | EVENT_LOG_READERS |
"RO" | ENTERPRISE_RO_DCS |
"CD" | CERTSVC_DCOM_ACCESS |
"AC" | ALL_APP_PACKAGES |
"RA" | RDS_REMOTE_ACCESS_SERVERS |
"ES" | RDS_ENDPOINT_SERVERS |
"MS" | RDS_MANAGEMENT_SERVERS |
"UD" | USER_MODE_DRIVERS |
"HA" | HYPER_V_ADMINS |
"CN" | CLONEABLE_CONTROLLERS |
"AA" | ACCESS_CONTROL_ASSISTANCE_OPS |
"RM" | REMOTE_MANAGEMENT_USERS |
"LW" | ML_LOW |
"ME" | ML_MEDIUM |
"MP" | ML MEDIUM PLUS |
"HI" | ML_HIGH |
"SI" | ML_SYSTEM |
附:SDDL语法结构(RFC5234 ABNF语法)
sddl = [owner-string] [group-string] [dacl-string] [sacl-string]
owner-string = "O:" sid-string
group-string = "G:" sid-string
dacl-string = "D:" [acl-flag-string] [aces]
sacl-string = "S:" [acl-flag-string] [aces]
sid-string = sid-token / sid-value
sid-value = SID
;defined in section 2.4.2.1
sid-token = "DA"/ "DG" / "DU" / "ED" / "DD" / "DC" / "BA" / "BG" / "BU" / "LA" / "LG" / "AO" / "BO" / "PO" / "SO" / "AU" / "PS" / "CO" / "CG" / "SY" / "PU" / "WD" / "RE" / "IU" / "NU" / "SU" / "RC" / "WR" / "AN" / "SA" / "CA" / "RS" / "EA" / "PA" / "RU" / "LS" / "NS" / "RD" / "NO" / "MU" / "LU" / "IS" / "CY" / "OW" / "ER" / "RO" / "CD" / "AC" / "RA" / "ES" / "MS" / "UD" / "HA" / "CN" / "AA" / "RM" / "LW" / "ME" /"MP" / "HI" / "SI"
acl-flag-string = *acl-flag
acl-flag = "P" / "AR" / "AI"
aces = *(ace / conditional-ace / resource-attribute-ace)
ace = "(" ace-type ";" [ace-flag-string] ";" ace-rights ";" [object-guid] ";" [inherit-object-guid] ";" sid-string ")"
ace-type = "A" / "D" / "OA" / "OD" / "AU" / "OU" / "ML" / "SP"
conditional-ace = "(" conditional-ace-type ";" [ace-flag-string] ";" ace-rights ";" [object-guid] ";" [inherit-object-guid] ";" sid-string ";" "(" cond-expr ")" ")"
conditional-ace-type = "XA" / "XD" / "ZA" / "XU"
central-policy-ace = "(" "SP" ";" [ace-flag-string] ";;;;" capid-value-sid ")"
capid-value-sid = "S-1-17-" 1*SubAuthority
; SubAuthority defined in section 2.4.2.1
resource-attribute-ace = "(" "RA" ";" [ace-flag-string] ";;;;" ( "WD" / "S-1-1-0" ) ";(" attribute-data "))"
attribute-data = DQUOTE 1*attr-char2 DQUOTE "," ( TI-attr / TU-attr / TS-attr / TD-attr / TX-attr / TB-attr )
TI-attr = "TI" "," attr-flags *("," int-64)
TU-attr = "TU" "," attr-flags *("," uint-64)
TS-attr = "TS" "," attr-flags *("," char-string)
TD-attr = "TD" "," attr-flags *("," sid-string)
TX-attr = "TX" "," attr-flags *("," octet-string)
TB-attr = "TB" "," attr-flags *("," ( "0" / "1" ) )
attr-flags = "0x" ([*4HEXDIG "00"] sys-attr-flags / *"0" sys-attr-flags / *"0" HEXDIG)
sys-attr-flags = ( "0"/ "1" / "2" / "3" ) HEXDIG
ace-flag-string = ace-flag ace-flag-string / ""
ace-flag = "CI" / "OI" / "NP" / "IO" / "ID" / "SA" / "FA"
ace-rights = (*text-rights-string) / ("0x" 1*8HEXDIG) / ("0" 1*%x30-37) / (1*DIGIT )
; numeric values must fit within 64 bits
text-rights-string = generic-rights-string / standard-rights-string / object-specific-rights-string
generic-rights-string = generic-right / generic-rights-string / ""
generic-right = "GA" / "GW" / "GR" / "GX"
standard-rights-string = standard-right / standard-rights-string / ""
standard-right = "WO" / "WD" / "RC" / "SD"
object-specific-rights-string = object-specific-right / object-specific-rights-string / ""
object-specific-right = <any object-specific right, for objects like files, registry keys, directory objects, and others>
guid = "" / 8HEXDIG "-" 4HEXDIG "-" 4HEXDIG "-" 4HEXDIG "-" 12HEXDIG
; The second option is the GUID of the object in the form
; "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX" Where each "X" is a Hex digit
object-guid = guid
inherit-object-guid = guid
wspace = 1*(%x09-0D / %x20)
term = [wspace] (memberof-op / exists-op / rel-op / contains-op / anyof-op / attr-name / rel-op2) [wspace]
; multiple rules for cond-expr to represent different precedence of || and &&
; super-term and factor are intermediate rules and used only in this part of the grammar
cond-expr = expr
expr = super-term [wspace] *( "||" [wspace] super-term )
super-term = factor [wspace] *( "&&" [wspace] factor )
factor = term
factor /= "(" [wspace] expr [wspace] ")"
factor /= "!" [wspace] factor
;
memberof-op = ( "Member_of" / "Not_Member_of" / "Member_of_Any" / "Not_Member_of_Any" / "Device_Member_of" / "Device_Member_of_Any" / "Not_Device_Member_of" / "Not_Device_Member_of_Any" ) wspace sid-array
exists-op = ( "Exists" / "Not_exists") wspace attr-name
rel-op = attr-name [wspace] ("<" / "<=" / ">" / ">=") [wspace] (attr-name2 / value)
; only scalars
rel-op2 = attr-name [wspace] ("==" / "!=") [wspace] ( attr-name2 / value-array )
; scalar or list
contains-op = attr-name wspace ("Contains" / "Not_Contains") wspace (attr-name2 / value-array)
anyof-op = attr-name wspace ("Any_of" / "Not_Any_of") wspace (attr-name2 / value-array)
attr-name1 = attr-char1 *(attr-char1 / "@")
; old simple name
attr-char1 = 1*(ALPHA / DIGIT / ":" / "." / "/" / "_")
attr-name2 = ("@user." / "@device." / "@resource.") 1*attr-char2
; new prefixed name form
attr-char2 = attr-char1 / lit-char
attr-name = attr-name1 / attr-name2
; either name form
sid-array = "{" [wspace] literal-SID [wspace] *( "," [wspace] literal-SID [wspace]) "}"
literal-SID = "SID(" sid-string ")"
value-array = value [wspace] / "{" [wspace] value [wspace] *("," [wspace] value [wspace]) "}"
value = int-64 / char-string / octet-string
int-64 = ["+" / "-"] ("0x" 1*HEXDIG) / ("0" 1*%x30-37) / 1*DIGIT
; values must fit within 64 bits in two's complement form
uint-64 = ("0x" 1*HEXDIG) / ("0" 1*%x30-37) / 1*DIGIT
; values must fit within 64 bits
char-string = DQUOTE *(CHAR) DQUOTE
octet-string = "#" *(2HEXDIG)
lit-char = "#" / "$" / "'" / "*" / "+" / "-" / "." / "/" / ":" / ";" / "?" / "@" / "[" / "\" / "]" / "^" / "_" / "`" / "{" / "}" / "~" / %x0080-FFFF / ( "%" 4HEXDIG)
; 4HEXDIG can have any value except 0000 (NULL)