1""" 2This module implements security descriptors, and the partial structures 3used in them, as specified in [MS-DTYP]. 4""" 5 6import struct 7 8 9# Security descriptor control flags 10# [MS-DTYP]: 2.4.6 11SECURITY_DESCRIPTOR_OWNER_DEFAULTED = 0x0001 12SECURITY_DESCRIPTOR_GROUP_DEFAULTED = 0x0002 13SECURITY_DESCRIPTOR_DACL_PRESENT = 0x0004 14SECURITY_DESCRIPTOR_DACL_DEFAULTED = 0x0008 15SECURITY_DESCRIPTOR_SACL_PRESENT = 0x0010 16SECURITY_DESCRIPTOR_SACL_DEFAULTED = 0x0020 17SECURITY_DESCRIPTOR_SERVER_SECURITY = 0x0040 18SECURITY_DESCRIPTOR_DACL_TRUSTED = 0x0080 19SECURITY_DESCRIPTOR_DACL_COMPUTED_INHERITANCE_REQUIRED = 0x0100 20SECURITY_DESCRIPTOR_SACL_COMPUTED_INHERITANCE_REQUIRED = 0x0200 21SECURITY_DESCRIPTOR_DACL_AUTO_INHERITED = 0x0400 22SECURITY_DESCRIPTOR_SACL_AUTO_INHERITED = 0x0800 23SECURITY_DESCRIPTOR_DACL_PROTECTED = 0x1000 24SECURITY_DESCRIPTOR_SACL_PROTECTED = 0x2000 25SECURITY_DESCRIPTOR_RM_CONTROL_VALID = 0x4000 26SECURITY_DESCRIPTOR_SELF_RELATIVE = 0x8000 27 28# ACE types 29# [MS-DTYP]: 2.4.4.1 30ACE_TYPE_ACCESS_ALLOWED = 0x00 31ACE_TYPE_ACCESS_DENIED = 0x01 32ACE_TYPE_SYSTEM_AUDIT = 0x02 33ACE_TYPE_SYSTEM_ALARM = 0x03 34ACE_TYPE_ACCESS_ALLOWED_COMPOUND = 0x04 35ACE_TYPE_ACCESS_ALLOWED_OBJECT = 0x05 36ACE_TYPE_ACCESS_DENIED_OBJECT = 0x06 37ACE_TYPE_SYSTEM_AUDIT_OBJECT = 0x07 38ACE_TYPE_SYSTEM_ALARM_OBJECT = 0x08 39ACE_TYPE_ACCESS_ALLOWED_CALLBACK = 0x09 40ACE_TYPE_ACCESS_DENIED_CALLBACK = 0x0A 41ACE_TYPE_ACCESS_ALLOWED_CALLBACK_OBJECT = 0x0B 42ACE_TYPE_ACCESS_DENIED_CALLBACK_OBJECT = 0x0C 43ACE_TYPE_SYSTEM_AUDIT_CALLBACK = 0x0D 44ACE_TYPE_SYSTEM_ALARM_CALLBACK = 0x0E 45ACE_TYPE_SYSTEM_AUDIT_CALLBACK_OBJECT = 0x0F 46ACE_TYPE_SYSTEM_ALARM_CALLBACK_OBJECT = 0x10 47ACE_TYPE_SYSTEM_MANDATORY_LABEL = 0x11 48ACE_TYPE_SYSTEM_RESOURCE_ATTRIBUTE = 0x12 49ACE_TYPE_SYSTEM_SCOPED_POLICY_ID = 0x13 50 51# ACE flags 52# [MS-DTYP]: 2.4.4.1 53ACE_FLAG_OBJECT_INHERIT = 0x01 54ACE_FLAG_CONTAINER_INHERIT = 0x02 55ACE_FLAG_NO_PROPAGATE_INHERIT = 0x04 56ACE_FLAG_INHERIT_ONLY = 0x08 57ACE_FLAG_INHERITED = 0x10 58ACE_FLAG_SUCCESSFUL_ACCESS = 0x40 59ACE_FLAG_FAILED_ACCESS = 0x80 60 61# Pre-defined well-known SIDs 62# [MS-DTYP]: 2.4.2.4 63SID_NULL = "S-1-0-0" 64SID_EVERYONE = "S-1-1-0" 65SID_LOCAL = "S-1-2-0" 66SID_CONSOLE_LOGON = "S-1-2-1" 67SID_CREATOR_OWNER = "S-1-3-0" 68SID_CREATOR_GROUP = "S-1-3-1" 69SID_OWNER_SERVER = "S-1-3-2" 70SID_GROUP_SERVER = "S-1-3-3" 71SID_OWNER_RIGHTS = "S-1-3-4" 72SID_NT_AUTHORITY = "S-1-5" 73SID_DIALUP = "S-1-5-1" 74SID_NETWORK = "S-1-5-2" 75SID_BATCH = "S-1-5-3" 76SID_INTERACTIVE = "S-1-5-4" 77SID_SERVICE = "S-1-5-6" 78SID_ANONYMOUS = "S-1-5-7" 79SID_PROXY = "S-1-5-8" 80SID_ENTERPRISE_DOMAIN_CONTROLLERS = "S-1-5-9" 81SID_PRINCIPAL_SELF = "S-1-5-10" 82SID_AUTHENTICATED_USERS = "S-1-5-11" 83SID_RESTRICTED_CODE = "S-1-5-12" 84SID_TERMINAL_SERVER_USER = "S-1-5-13" 85SID_REMOTE_INTERACTIVE_LOGON = "S-1-5-14" 86SID_THIS_ORGANIZATION = "S-1-5-15" 87SID_IUSR = "S-1-5-17" 88SID_LOCAL_SYSTEM = "S-1-5-18" 89SID_LOCAL_SERVICE = "S-1-5-19" 90SID_NETWORK_SERVICE = "S-1-5-20" 91SID_COMPOUNDED_AUTHENTICATION = "S-1-5-21-0-0-0-496" 92SID_CLAIMS_VALID = "S-1-5-21-0-0-0-497" 93SID_BUILTIN_ADMINISTRATORS = "S-1-5-32-544" 94SID_BUILTIN_USERS = "S-1-5-32-545" 95SID_BUILTIN_GUESTS = "S-1-5-32-546" 96SID_POWER_USERS = "S-1-5-32-547" 97SID_ACCOUNT_OPERATORS = "S-1-5-32-548" 98SID_SERVER_OPERATORS = "S-1-5-32-549" 99SID_PRINTER_OPERATORS = "S-1-5-32-550" 100SID_BACKUP_OPERATORS = "S-1-5-32-551" 101SID_REPLICATOR = "S-1-5-32-552" 102SID_ALIAS_PREW2KCOMPACC = "S-1-5-32-554" 103SID_REMOTE_DESKTOP = "S-1-5-32-555" 104SID_NETWORK_CONFIGURATION_OPS = "S-1-5-32-556" 105SID_INCOMING_FOREST_TRUST_BUILDERS = "S-1-5-32-557" 106SID_PERFMON_USERS = "S-1-5-32-558" 107SID_PERFLOG_USERS = "S-1-5-32-559" 108SID_WINDOWS_AUTHORIZATION_ACCESS_GROUP = "S-1-5-32-560" 109SID_TERMINAL_SERVER_LICENSE_SERVERS = "S-1-5-32-561" 110SID_DISTRIBUTED_COM_USERS = "S-1-5-32-562" 111SID_IIS_IUSRS = "S-1-5-32-568" 112SID_CRYPTOGRAPHIC_OPERATORS = "S-1-5-32-569" 113SID_EVENT_LOG_READERS = "S-1-5-32-573" 114SID_CERTIFICATE_SERVICE_DCOM_ACCESS = "S-1-5-32-574" 115SID_RDS_REMOTE_ACCESS_SERVERS = "S-1-5-32-575" 116SID_RDS_ENDPOINT_SERVERS = "S-1-5-32-576" 117SID_RDS_MANAGEMENT_SERVERS = "S-1-5-32-577" 118SID_HYPER_V_ADMINS = "S-1-5-32-578" 119SID_ACCESS_CONTROL_ASSISTANCE_OPS = "S-1-5-32-579" 120SID_REMOTE_MANAGEMENT_USERS = "S-1-5-32-580" 121SID_WRITE_RESTRICTED_CODE = "S-1-5-33" 122SID_NTLM_AUTHENTICATION = "S-1-5-64-10" 123SID_SCHANNEL_AUTHENTICATION = "S-1-5-64-14" 124SID_DIGEST_AUTHENTICATION = "S-1-5-64-21" 125SID_THIS_ORGANIZATION_CERTIFICATE = "S-1-5-65-1" 126SID_NT_SERVICE = "S-1-5-80" 127SID_USER_MODE_DRIVERS = "S-1-5-84-0-0-0-0-0" 128SID_LOCAL_ACCOUNT = "S-1-5-113" 129SID_LOCAL_ACCOUNT_AND_MEMBER_OF_ADMINISTRATORS_GROUP = "S-1-5-114" 130SID_OTHER_ORGANIZATION = "S-1-5-1000" 131SID_ALL_APP_PACKAGES = "S-1-15-2-1" 132SID_ML_UNTRUSTED = "S-1-16-0" 133SID_ML_LOW = "S-1-16-4096" 134SID_ML_MEDIUM = "S-1-16-8192" 135SID_ML_MEDIUM_PLUS = "S-1-16-8448" 136SID_ML_HIGH = "S-1-16-12288" 137SID_ML_SYSTEM = "S-1-16-16384" 138SID_ML_PROTECTED_PROCESS = "S-1-16-20480" 139SID_AUTHENTICATION_AUTHORITY_ASSERTED_IDENTITY = "S-1-18-1" 140SID_SERVICE_ASSERTED_IDENTITY = "S-1-18-2" 141SID_FRESH_PUBLIC_KEY_IDENTITY = "S-1-18-3" 142SID_KEY_TRUST_IDENTITY = "S-1-18-4" 143SID_KEY_PROPERTY_MFA = "S-1-18-5" 144SID_KEY_PROPERTY_ATTESTATION = "S-1-18-6" 145 146 147class SID(object): 148 """ 149 A Windows security identifier. Represents a single principal, such a 150 user or a group, as a sequence of numbers consisting of the revision, 151 identifier authority, and a variable-length list of subauthorities. 152 153 See [MS-DTYP]: 2.4.2 154 """ 155 def __init__(self, revision, identifier_authority, subauthorities): 156 #: Revision, should always be 1. 157 self.revision = revision 158 #: An integer representing the identifier authority. 159 self.identifier_authority = identifier_authority 160 #: A list of integers representing all subauthorities. 161 self.subauthorities = subauthorities 162 163 def __str__(self): 164 """ 165 String representation, as specified in [MS-DTYP]: 2.4.2.1 166 """ 167 if self.identifier_authority >= 2**32: 168 id_auth = '%#x' % (self.identifier_authority,) 169 else: 170 id_auth = self.identifier_authority 171 auths = [self.revision, id_auth] + self.subauthorities 172 return 'S-' + '-'.join(str(subauth) for subauth in auths) 173 174 def __repr__(self): 175 return 'SID(%r)' % (str(self),) 176 177 @classmethod 178 def from_bytes(cls, data, return_tail=False): 179 revision, subauth_count = struct.unpack('<BB', data[:2]) 180 identifier_authority = struct.unpack('>Q', '\x00\x00' + data[2:8])[0] 181 subauth_data = data[8:] 182 subauthorities = [struct.unpack('<L', subauth_data[4 * i : 4 * (i+1)])[0] 183 for i in range(subauth_count)] 184 sid = cls(revision, identifier_authority, subauthorities) 185 if return_tail: 186 return sid, subauth_data[4 * subauth_count :] 187 return sid 188 189 190class ACE(object): 191 """ 192 Represents a single access control entry. 193 194 See [MS-DTYP]: 2.4.4 195 """ 196 HEADER_FORMAT = '<BBH' 197 198 def __init__(self, type_, flags, mask, sid, additional_data): 199 #: An integer representing the type of the ACE. One of the 200 #: ``ACE_TYPE_*`` constants. Corresponds to the ``AceType`` field 201 #: from [MS-DTYP] 2.4.4.1. 202 self.type = type_ 203 #: An integer bitmask with ACE flags, corresponds to the 204 #: ``AceFlags`` field. 205 self.flags = flags 206 #: An integer representing the ``ACCESS_MASK`` as specified in 207 #: [MS-DTYP] 2.4.3. 208 self.mask = mask 209 #: The :class:`SID` of a trustee. 210 self.sid = sid 211 #: A dictionary of additional fields present in the ACE, depending 212 #: on the type. The following fields can be present: 213 #: 214 #: * ``flags`` 215 #: * ``object_type`` 216 #: * ``inherited_object_type`` 217 #: * ``application_data`` 218 #: * ``attribute_data`` 219 self.additional_data = additional_data 220 221 def __repr__(self): 222 return "ACE(type=%#04x, flags=%#04x, mask=%#010x, sid=%s)" % ( 223 self.type, self.flags, self.mask, self.sid, 224 ) 225 226 @property 227 def isInheritOnly(self): 228 """Convenience property which indicates if this ACE is inherit 229 only, meaning that it doesn't apply to the object itself.""" 230 return bool(self.flags & ACE_FLAG_INHERIT_ONLY) 231 232 @classmethod 233 def from_bytes(cls, data): 234 header_size = struct.calcsize(cls.HEADER_FORMAT) 235 header = data[:header_size] 236 type_, flags, size = struct.unpack(cls.HEADER_FORMAT, header) 237 238 assert len(data) >= size 239 240 body = data[header_size:size] 241 additional_data = {} 242 243 # In all ACE types, the mask immediately follows the header. 244 mask = struct.unpack('<I', body[:4])[0] 245 body = body[4:] 246 247 # All OBJECT-type ACEs contain additional flags, and two GUIDs as 248 # the following fields. 249 if type_ in (ACE_TYPE_ACCESS_ALLOWED_OBJECT, 250 ACE_TYPE_ACCESS_DENIED_OBJECT, 251 ACE_TYPE_ACCESS_ALLOWED_CALLBACK_OBJECT, 252 ACE_TYPE_ACCESS_DENIED_CALLBACK_OBJECT, 253 ACE_TYPE_SYSTEM_AUDIT_OBJECT, 254 ACE_TYPE_SYSTEM_AUDIT_CALLBACK_OBJECT): 255 additional_data['flags'] = struct.unpack('<I', body[:4])[0] 256 additional_data['object_type'] = body[4:20] 257 additional_data['inherited_object_type'] = body[20:36] 258 body = body[36:] 259 260 # Then the SID in all types. 261 sid, body = SID.from_bytes(body, return_tail=True) 262 263 # CALLBACK-type ACEs (and for some obscure reason, 264 # SYSTEM_AUDIT_OBJECT) have a final tail of application data. 265 if type_ in (ACE_TYPE_ACCESS_ALLOWED_CALLBACK, 266 ACE_TYPE_ACCESS_DENIED_CALLBACK, 267 ACE_TYPE_ACCESS_ALLOWED_CALLBACK_OBJECT, 268 ACE_TYPE_ACCESS_DENIED_CALLBACK_OBJECT, 269 ACE_TYPE_SYSTEM_AUDIT_OBJECT, 270 ACE_TYPE_SYSTEM_AUDIT_CALLBACK, 271 ACE_TYPE_SYSTEM_AUDIT_CALLBACK_OBJECT): 272 additional_data['application_data'] = body 273 274 # SYSTEM_RESOURCE_ATTRIBUTE ACEs have a tail of attribute data. 275 if type_ == ACE_TYPE_SYSTEM_RESOURCE_ATTRIBUTE: 276 additional_data['attribute_data'] = body 277 278 return cls(type_, flags, mask, sid, additional_data) 279 280 281class ACL(object): 282 """ 283 Access control list, encapsulating a sequence of access control 284 entries. 285 286 See [MS-DTYP]: 2.4.5 287 """ 288 HEADER_FORMAT = '<BBHHH' 289 290 def __init__(self, revision, aces): 291 #: Integer value of the revision. 292 self.revision = revision 293 #: List of :class:`ACE` instances. 294 self.aces = aces 295 296 def __repr__(self): 297 return "ACL(%r)" % (self.aces,) 298 299 @classmethod 300 def from_bytes(cls, data): 301 revision = None 302 aces = [] 303 304 header_size = struct.calcsize(cls.HEADER_FORMAT) 305 header, remaining = data[:header_size], data[header_size:] 306 revision, sbz1, size, count, sbz2 = struct.unpack(cls.HEADER_FORMAT, header) 307 308 assert len(data) >= size 309 310 for i in range(count): 311 ace_size = struct.unpack('<H', remaining[2:4])[0] 312 ace_data, remaining = remaining[:ace_size], remaining[ace_size:] 313 aces.append(ACE.from_bytes(ace_data)) 314 315 return cls(revision, aces) 316 317 318class SecurityDescriptor(object): 319 """ 320 Represents a security descriptor. 321 322 See [MS-DTYP]: 2.4.6 323 """ 324 325 HEADER_FORMAT = '<BBHIIII' 326 327 def __init__(self, flags, owner, group, dacl, sacl): 328 #: Integer bitmask of control flags. Corresponds to the 329 #: ``Control`` field in [MS-DTYP] 2.4.6. 330 self.flags = flags 331 #: Instance of :class:`SID` representing the owner user. 332 self.owner = owner 333 #: Instance of :class:`SID` representing the owner group. 334 self.group = group 335 #: Instance of :class:`ACL` representing the discretionary access 336 #: control list, which specifies access restrictions of an object. 337 self.dacl = dacl 338 #: Instance of :class:`ACL` representing the system access control 339 #: list, which specifies audit logging of an object. 340 self.sacl = sacl 341 342 @classmethod 343 def from_bytes(cls, data): 344 owner = None 345 group = None 346 dacl = None 347 sacl = None 348 349 header = data[:struct.calcsize(cls.HEADER_FORMAT)] 350 (revision, sbz1, flags, owner_offset, group_offset, sacl_offset, 351 dacl_offset) = struct.unpack(cls.HEADER_FORMAT, header) 352 353 assert revision == 1 354 assert flags & SECURITY_DESCRIPTOR_SELF_RELATIVE 355 for offset in (owner_offset, group_offset, sacl_offset, dacl_offset): 356 assert 0 <= offset < len(data) 357 358 if owner_offset: 359 owner = SID.from_bytes(data[owner_offset:]) 360 if group_offset: 361 group = SID.from_bytes(data[group_offset:]) 362 if dacl_offset: 363 dacl = ACL.from_bytes(data[dacl_offset:]) 364 if sacl_offset: 365 sacl = ACL.from_bytes(data[sacl_offset:]) 366 367 return cls(flags, owner, group, dacl, sacl) 368