# coding=utf-8 # # Copyright (C) 2009-2021 Splunk Inc. All Rights Reserved. from __future__ import absolute_import, division, print_function, unicode_literals import itertools import struct from .six import text_type, PY3 from .six.moves import range, map # References: # 1. [Security Descriptor String Format](http://goo.gl/4IEHBc). # 2. [ACE header](http://goo.gl/v8O4aG). # 3. [ACE strings](http://goo.gl/Y10RdI). # 4. [Object-specific ACEs](http://goo.gl/gnrgnY). # 5. [SID String Format Syntax](http://goo.gl/itojP1) # 6. [Microsoft Windows SDK 8.1](http://goo.gl/FWc2Ay), especially sddl.h and winnt.h. # 7. [Operational attributes](http://goo.gl/mlX0GW) # 8. [RFC 3673: Lightweight Directory Access Protocol version 3 (LDAPv3): All Operational Attributes](http://goo.gl/aOawGy). _ace_type_strings = { # Numeric keys are defined in winnt.h and string values are defined in sddl.h # Documented at [ACE strings](http://goo.gl/Y10RdI) 0x00: 'A', # ACCESS_ALLOWED_ACE_TYPE => SDDL_ACCESS_ALLOWED 0x01: 'D', # ACCESS_DENIED_ACE_TYPE => SDDL_ACCESS_DENIED 0x05: 'OA', # ACCESS_ALLOWED_OBJECT_ACE_TYPE => SDDL_OBJECT_ACCESS_ALLOWED 0x06: 'OD', # ACCESS_DENIED_OBJECT_ACE_TYPE => SDDL_OBJECT_ACCESS_DENIED 0x02: 'AU', # SYSTEM_AUDIT_ACE_TYPE => SDDL_AUDIT 0x03: 'AL', # SYSTEM_ALARM_ACE_TYPE => SDDL_ALARM 0x07: 'OU', # SYSTEM_AUDIT_OBJECT_ACE_TYPE => SDDL_OBJECT_AUDIT 0x08: 'OL', # SYSTEM_ALARM_OBJECT_ACE_TYPE => SDDL_OBJECT_ALARM 0x11: 'ML', # SYSTEM_MANDATORY_LABEL_ACE_TYPE 0x09: 'XA', # ACCESS_ALLOWED_CALLBACK_ACE_TYPE => SDDL_CALLBACK_ACCESS_ALLOWED 0x0A: 'XD', # ACCESS_DENIED_CALLBACK_ACE_TYPE => ACCESS_DENIED_CALLBACK_ACE_TYPE 0x12: 'RA', # SYSTEM_RESOURCE_ATTRIBUTE_ACE_TYPE => SDDL_RESOURCE_ATTRIBUTE 0x13: 'SP', # SYSTEM_SCOPED_POLICY_ID_ACE_TYPE => SDDL_SCOPED_POLICY_ID 0x0D: 'XU', # SYSTEM_AUDIT_CALLBACK_ACE_TYPE => SDDL_CALLBACK_AUDIT 0x0B: 'ZA', # ACCESS_ALLOWED_CALLBACK_OBJECT_ACE_TYPE => SDDL_CALLBACK_OBJECT_ACCESS_ALLOWED 0x0C: 'ZD', # ACCESS_DENIED_CALLBACK_OBJECT_ACE_TYPE => SDDL_CALLBACK_OBJECT_ACCESS_DENIED # Documented at [ACE_HEADER](http://goo.gl/v8O4aG) 0x04: '', # Reserved for future use: ACCESS_ALLOWED_COMPOUND_ACE_TYPE 0x0E: '', # Reserved for future use: SYSTEM_ALARM_CALLBACK_ACE_TYPE 0x10: '', # Reserved for future use: SYSTEM_ALARM_CALLBACK_OBJECT_ACE_TYPE => 0x0F: '', # SYSTEM_AUDIT_CALLBACK_OBJECT_ACE_TYPE: # Object-specific system-audit callback ACE that uses the SYSTEM_AUDIT_CALLBACK_OBJECT_ACE: } _attribute_syntaxes = { # Attribute oMSyntax map # Syntax attributeSyntax ASN 1-Encoded OID Description '1': 'Boolean', # 2.5.5.8 \x550508 TRUE or FALSE values. '2': 'Integer', # 2.5.5.9 \x550509 A 32-bit number. '4': 'String(Octet)', # 2.5.5.10 \x55050A A string of bytes (Octet). '6': 'String(Object-Identifier)', # 2.5.5.2 \x550502 The object identifier. '10': 'Enumeration', # 2.5.5.9 \x550509 A 32-bit enumeration. '18': 'String(Numeric)', # 2.5.5.6 \x550506 A sequence of digits. '19': 'String(Printable)', # 2.5.5.5 \x550505 Printable case-sensitive string. '20': 'CaseIgnoreString(Teletex)', # 2.5.5.4 \x550504 Teletex. Does not differentiate upper case # and lowercase. '22': 'String(IA5)', # 2.5.5.5 \x550505 IA5-String. Character set is case- # sensitive. '23': 'String(UTC-Time)', # 2.5.5.11 \x55050B UTC Time. '24': 'String(Generalized-Time)', # 2.5.5.11 \x55050B Generalized-Time. '27': 'Case-Sensitive String', # 2.5.5.3 \x550503 General String. Differentiates uppercase # and lowercase. '64': 'String(Unicode)', # 2.5.5.12 \x55050C Unicode string. '65': 'LargeInteger', # 2.5.5.16 \x550510 A 64-bit number. '66': 'String(NT-Sec-Desc)', # 2.5.5.15 \x55050F A Microsoft® Windows NT® Security # descriptor. '127': 'Object' # 2.5.5.1 \x550501 The fully qualified name of an object in # the directory. # 2.5.5.7 \x550507 A distinguished name plus a binary large # object. # 2.5.5.13 \x55050D Presentation address. # 2.5.5.14 \x55050E A DN-String plus a Unicode string. } _object_specific_ace_types = { 0x05, # ACCESS_ALLOWED_OBJECT_ACE_TYPE # Used in a DACL to allow a trustee access to a property or property set on the object, or to limit ACE # inheritance to a specified type of child object. 0x06, # ACCESS_DENIED_OBJECT_ACE_TYPE # Used in a DACL to deny a trustee access to a property or property set on the object, or to limit ACE # inheritance to a specified type of child object. 0x07, # SYSTEM_AUDIT_OBJECT_ACE_TYPE # Used in a SACL to log a trustee's attempts to access a property or property set on the object, or to limit # ACE inheritance to a specified type of child object. } _well_known_sid_strings = { 0x00000007: 'AN', # SDDL_ANONYMOUS # Anonymous logon. The corresponding RID is SECURITY_ANONYMOUS_LOGON_RID. 0x00000224: 'AO', # SDDL_ACCOUNT_OPERATORS # Account operators. The corresponding RID is DOMAIN_ALIAS_RID_ACCOUNT_OPS. 0x0000000B: 'AU', # SDDL_AUTHENTICATED_USERS # Authenticated users. The corresponding RID is SECURITY_AUTHENTICATED_USER_RID. 0x00000220: 'BA', # SDDL_BUILTIN_ADMINISTRATORS # Built-in administrators. The corresponding RID is DOMAIN_ALIAS_RID_ADMINS. 0x00000222: 'BG', # SDDL_BUILTIN_GUESTS # Built-in guests. The corresponding RID is DOMAIN_ALIAS_RID_GUESTS. 0x00000227: 'BO', # SDDL_BACKUP_OPERATORS # Backup operators. The corresponding RID is DOMAIN_ALIAS_RID_BACKUP_OPS. 0x00000221: 'BU', # SDDL_BUILTIN_USERS # Built-in users. The corresponding RID is DOMAIN_ALIAS_RID_USERS. 0x00000205: 'CA', # SDDL_CERT_SERV_ADMINISTRATORS # Certificate publishers. The corresponding RID is DOMAIN_GROUP_RID_CERT_ADMINS. 0x0000023E: 'CD', # SDDL_CERTSVC_DCOM_ACCESS # Users who can connect to certification authorities using Distributed Component Object Model # (DCOM). The corresponding RID is DOMAIN_ALIAS_RID_CERTSVC_DCOM_ACCESS_GROUP. 0x00000001: 'CG', # SDDL_CREATOR_GROUP # Creator group. The corresponding RID is SECURITY_CREATOR_GROUP_RID. 0x00000200: 'DA', # SDDL_DOMAIN_ADMINISTRATORS # Domain administrators. The corresponding RID is DOMAIN_GROUP_RID_ADMINS. 0x00000203: 'DC', # SDDL_DOMAIN_COMPUTERS # Domain computers. The corresponding RID is DOMAIN_GROUP_RID_COMPUTERS. 0x00000204: 'DD', # SDDL_DOMAIN_DOMAIN_CONTROLLERS # Domain controllers. The corresponding RID is DOMAIN_GROUP_RID_CONTROLLERS. 0x00000202: 'DG', # SDDL_DOMAIN_GUESTS # Domain guests. The corresponding RID is DOMAIN_GROUP_RID_GUESTS. 0x00000201: 'DU', # SDDL_DOMAIN_USERS # Domain users. The corresponding RID is DOMAIN_GROUP_RID_USERS. 0x00000207: 'EA', # SDDL_ENTERPRISE_ADMINS # Enterprise administrators. The corresponding RID is DOMAIN_GROUP_RID_ENTERPRISE_ADMINS. 0x00000009: 'ED', # SDDL_ENTERPRISE_DOMAIN_CONTROLLERS # Enterprise domain controllers. The corresponding RID is SECURITY_SERVER_LOGON_RID. 0x00003000: 'HI', # SDDL_ML_HIGH # High integrity level. The corresponding RID is SECURITY_MANDATORY_HIGH_RID. 0x00000004: 'IU', # SDDL_INTERACTIVE # Interactively logged-on user. This is a group identifier added to the token of a process when # it was logged on interactively. The corresponding logon type is LOGON32_LOGON_INTERACTIVE. The # corresponding RID is SECURITY_INTERACTIVE_RID. 0x000001F4: 'LA', # SDDL_LOCAL_ADMIN # Local administrator. The corresponding RID is DOMAIN_USER_RID_ADMIN. 0x000001F5: 'LG', # SDDL_LOCAL_GUEST # Local guest. The corresponding RID is DOMAIN_USER_RID_GUEST. 0x00000013: 'LS', # SDDL_LOCAL_SERVICE # Local service account. The corresponding RID is SECURITY_LOCAL_SERVICE_RID. 0x00001000: 'LW', # SDDL_ML_LOW # Low integrity level. The corresponding RID is SECURITY_MANDATORY_LOW_RID. 0x00002000: 'ME', # SDDL_MLMEDIUM # Medium integrity level. The corresponding RID is SECURITY_MANDATORY_MEDIUM_RID. 0x0000022F: 'MU', # SDDL_PERFMON_USERS # Performance Monitor users. 0x0000022C: 'NO', # SDDL_NETWORK_CONFIGURATION_OPS # Network configuration operators. The corresponding RID is # DOMAIN_ALIAS_RID_NETWORK_CONFIGURATION_OPS. 0x00000014: 'NS', # SDDL_NETWORK_SERVICE # Network service account. The corresponding RID is SECURITY_NETWORK_SERVICE_RID. 0x00000002: 'NU', # SDDL_NETWORK # Network logon user. This is a group identifier added to the token of a process when it was # logged on across a network. The corresponding logon type is LOGON32_LOGON_NETWORK. The # corresponding RID is SECURITY_NETWORK_RID. 0x00000208: 'PA', # SDDL_GROUP_POLICY_ADMINS # Group Policy administrators. The corresponding RID is DOMAIN_GROUP_RID_POLICY_ADMINS. 0x00000226: 'PO', # SDDL_PRINTER_OPERATORS # Printer operators. The corresponding RID is DOMAIN_ALIAS_RID_PRINT_OPS. 0x0000000A: 'PS', # SDDL_PERSONAL_SELF # Principal self. The corresponding RID is SECURITY_PRINCIPAL_SELF_RID. 0x00000223: 'PU', # SDDL_POWER_USERS # Power users. The corresponding RID is DOMAIN_ALIAS_RID_POWER_USERS. 0x0000000C: 'RC', # SDDL_RESTRICTED_CODE # Restricted code. This is a restricted token created using the CreateRestrictedToken function. # The corresponding RID is SECURITY_RESTRICTED_CODE_RID. 0x0000022B: 'RD', # SDDL_REMOTE_DESKTOP # Terminal server users. The corresponding RID is DOMAIN_ALIAS_RID_REMOTE_DESKTOP_USERS. 0x00000228: 'RE', # SDDL_REPLICATOR # Replicator. The corresponding RID is DOMAIN_ALIAS_RID_REPLICATOR. 0x000001F2: 'RO', # SDDL_ENTERPRISE_RO_DCs # Enterprise Read-only domain controllers. The corresponding RID is # DOMAIN_GROUP_RID_ENTERPRISE_READONLY_DOMAIN_CONTROLLERS. 0x00000229: 'RS', # SDDL_RAS_SERVERS # RAS servers group. The corresponding RID is DOMAIN_ALIAS_RID_RAS_SERVERS. 0x0000022A: 'RU', # SDDL_ALIAS_PREW2KCOMPACC # Alias to grant permissions to accounts that use applications compatible with operating systems # previous to Windows 2000. The corresponding RID is DOMAIN_ALIAS_RID_PREW2KCOMPACCESS. 0x00000206: 'SA', # SDDL_SCHEMA_ADMINISTRATORS # Schema administrators. The corresponding RID is DOMAIN_GROUP_RID_SCHEMA_ADMINS. 0x00004000: 'SI', # SDDL_ML_SYSTEM # System integrity level. The corresponding RID is SECURITY_MANDATORY_SYSTEM_RID. 0x00000225: 'SO', # SDDL_SERVER_OPERATORS # Server operators. The corresponding RID is DOMAIN_ALIAS_RID_SYSTEM_OPS. 0x00000006: 'SU', # SDDL_SERVICE # Service logon user. This is a group identifier added to the token of a process when it was # logged as a service. The corresponding logon type is LOGON32_LOGON_SERVICE. The corresponding # RID is SECURITY_SERVICE_RID. 0x00000012: 'SY', # SDDL_LOCAL_SYSTEM # Local system. The corresponding RID is SECURITY_LOCAL_SYSTEM_RID. } def format_acl(value, offset): """ :param value: :param offset: :return: """ acl_start = struct.unpack_from(b'I', value, offset)[0] acl_size, ace_count = struct.unpack_from(b'HH', value, acl_start + 2) ace_start = acl_start + 8 text = '' for i in range(0, ace_count): ace_type, ace_flags, ace_size, rights = struct.unpack_from(b'BBHI', value, ace_start) ace_type_string = _ace_type_strings[ace_type] ace_flags_string = ''.join(( 'CI' if ace_flags & 0x02 else '', # CONTAINER_INHERIT_ACE => SDDL_CONTAINER_INHERIT 'OI' if ace_flags & 0x01 else '', # OBJECT_INHERIT_ACE => SDDL_OBJECT_INHERIT 'NP' if ace_flags & 0x04 else '', # NO_PROPAGATE_INHERIT_ACE => SDDL_NO_PROPAGATE 'IO' if ace_flags & 0x08 else '', # INHERIT_ONLY_ACE => SDDL_INHERIT_ONLY 'ID' if ace_flags & 0x10 else '', # INHERITED_ACE => SDDL_INHERITED 'SA' if ace_flags & 0x40 else '', # SUCCESSFUL_ACCESS_ACE_FLAG => SDDL_AUDIT_SUCCESS 'FA' if ace_flags & 0x80 else '', # FAILED_ACCESS_ACE_FLAG => SDDL_AUDIT_FAILURE )) offset = ace_start + 8 if ace_type in _object_specific_ace_types: object_guid = format_guid(value, offset) offset += 16 inherit_object_guid = format_guid(value, offset) offset += 16 else: object_guid = inherit_object_guid = '' account_sid = format_sid(value, offset, _well_known_sid_strings) if account_sid == 'S-1-0-0': account_sid = '' offset += 16 if ace_type == 0x12: # SYSTEM_RESOURCE_ATTRIBUTE_ACE_TYPE from base64 import b16encode def unpack_null_terminated_utf16_string(string, start): end = start while struct.unpack_from(b'H', string, end) != 0: pass return '"' + text_type(struct.unpack_from(str(end - start) + 's', string, start)[0], 'utf-16') + '"' def unpack_claim_security_attribute_type_int64(string, start): return text_type(struct.unpack_from(b'l', string, start)[0]) def unpack_claim_security_attribute_type_uint64(string, start): return text_type(struct.unpack_from(b'L', string, start)[0]) def unpack_claim_security_attribute_type_octet_string(string, start): # See http://msdn.microsoft.com/en-us/library/windows/desktop/hh448485(v=vs.85).aspx # TODO: Do not assume 64-bit octet offset as this code does octet_offset, octet_length = struct.unpack_from(b'LL', string, start) return b16encode((struct.unpack_from(str(octet_length) + 's', string, start)[0])).decode('utf-8') def unpack_claim_security_attribute_type_sid(string, start): # See http://msdn.microsoft.com/en-us/library/windows/desktop/hh448483(v=vs.85).aspx # TODO: Do not assume a 64-bit name offset as this code does octet_offset, name_length = struct.unpack_from(b'LL', string, start) return format_sid(string, octet_offset) claim_name_offset, claim_type, reserved, claim_flags, claim_count = struct.unpack_from(b'IHHII', value, offset) offset += 16 claim_value_offsets = struct.unpack_from(str(claim_count) + 'I', value, offset) claim_name = unpack_null_terminated_utf16_string(value, claim_name_offset) if claim_type == 0x0001: # CLAIM_SECURITY_ATTRIBUTE_TYPE_INT64 claim_values = [unpack_claim_security_attribute_type_int64(value, x) for x in claim_value_offsets] claim_type = 'TI' elif claim_type == 0x02: # CLAIM_SECURITY_ATTRIBUTE_TYPE_UINT64 claim_values = [unpack_claim_security_attribute_type_uint64(value, x) for x in claim_value_offsets] claim_type = 'TU' elif claim_type == 0x03: # CLAIM_SECURITY_ATTRIBUTE_TYPE_STRING claim_values = [unpack_null_terminated_utf16_string(value, x) for x in claim_value_offsets] claim_type = 'TS' elif claim_type == 0x05: # CLAIM_SECURITY_ATTRIBUTE_TYPE_SID claim_values = [unpack_claim_security_attribute_type_octet_string(value, x) for x in claim_value_offsets] claim_type = 'TD' elif claim_type == 0x06: # CLAIM_SECURITY_ATTRIBUTE_TYPE_BOOLEAN claim_values = [unpack_claim_security_attribute_type_uint64(value, x) for x in claim_value_offsets] claim_type = 'TB' elif claim_type == 0x10: # CLAIM_SECURITY_ATTRIBUTE_TYPE_OCTET_STRING claim_values = [unpack_claim_security_attribute_type_octet_string(value, x) for x in claim_value_offsets] claim_type = 'TX' else: # We should never reach this code # TODO: log (?) claim_values = () claim_type = '' text += '(' + ';'.join( ( ace_type_string, ace_flags_string, hex(rights), object_guid, inherit_object_guid, account_sid, '(' + ','.join( itertools.chain((claim_name, claim_type, text_type(claim_flags)), claim_values)) + ')' )) + ')' else: text += '(' + ';'.join( ( ace_type_string, ace_flags_string, hex(rights), object_guid, inherit_object_guid, account_sid )) + ')' ace_start += ace_size return text def format_attribute_syntax(value): """ Converts an oMSyntax attribute value to an LDAP syntax name. See :data:`attribute_syntaxes` and the MSDN article `LDAP Representations `_ for a description of the LDAP syntaxes supported by Microsoft Active Directory. :param value: Decimal string. :type value: ``str`` :return: LDAP syntax name. :rtype unicode: """ syntax_name = ' = '.join((value, _attribute_syntaxes.get(value, ''))) return syntax_name def format_filetime(value): """ Formats a time interval value as an ISO-8601 date/time string. **Example:** .. codeblock::PowerShell $epochTime = New-Object 'System.DateTime' (1970, 1, 1) "0x$($epochTime.ToFileTimeUtc().ToString('x8'))" # Displays: 0x19db1ded53e8000 .. codeblock::python app.format_time_interval(unicode(0x19db1ded53e8000), 0x)) # Displays: '1970-01-01T00:00:00Z' **Example:** .. codeblock::PowerShell $dateTime = New-Object 'System.DateTime' (2014, 9, 23, 0, 17, 46, 67) "0x$($dateTime.ToFileTimeUtc().ToString('x8'))" # Displays: 0x1cfd6c3ccd76a30 .. codeblock::python app.format_time_interval(unicode(0x1cfd6c3ccd76a30)) # Displays: '2014-09-23T00:17:46.067000Z' **References:** 1. `Microsoft Windows FILETIME structure `_ 2. `Microsoft Active Directory Interval syntax `_ """ file_time = int(value) if file_time in (0x0000000000000000, 0x7FFFFFFFFFFFFFFF): return '(never)' from datetime import datetime epoch_time = 0x019db1ded53e8000 seconds, hundreds_of_nanoseconds = divmod(file_time - epoch_time, 10000000) microseconds = hundreds_of_nanoseconds // 10 try: dt = datetime.utcfromtimestamp(seconds) except ValueError: text = '' else: dt = dt.replace(microsecond=microseconds) text = dt.isoformat() + 'Z' return text def format_group_type(value): """ Converts the base 10 string representation of a 32-bit integer to a sorted list of `Group-Type` names. :param unicode value: Base 10 string representation of a 32-bit integer. :return list: Sorted list of `Group-Type` names. **References:** 1. `Group-Type attribute `_ """ flags = int(value) names = [] # Confirmed: These names are the same as displayed by Active Directory # # 1. BUILTIN_LOCAL_GROUP CN=Hyper-V Administrators,CN=Builtin,DC=msapps,DC=local # 2. ACCOUNT_GROUP CN=DnsUpdateProxy,CN=Users,DC=msapps,DC=local # 3. RESOURCE_GROUP CN=DHCP Administrators,CN=Users,DC=msapps,DC=local # 4. UNIVERSAL_GROUP CN=Enterprise Admins,CN=Users,DC=msapps,DC=local # 5. SECURITY_ENABLED CN=DHCP Administrators,CN=Users,DC=msapps,DC=local # # Unconfirmed but on the web you will find people using these names: # # 1. APP_BASIC_GROUP # 2. APP_QUERY_GROUP # # See, for example, http://goo.gl/bReghZ. if flags & 0x00000001: # Specifies a group that is created by the system. names.append('BUILTIN_LOCAL_GROUP') if flags & 0x00000002: names.append('ACCOUNT_GROUP') if flags & 0x00000004: names.append('RESOURCE_GROUP') if flags & 0x00000008: names.append('UNIVERSAL_GROUP') if flags & 0x00000010: # Specifies an APP_BASIC group for Windows Server Authorization Manager. names.append('APP_BASIC_GROUP') if flags & 0x00000020: # Specifies an APP_QUERY group for Windows Server Authorization Manager. names.append('APP_QUERY_GROUP') if flags & 0x80000000: # Specifies a security group. If this flag is not set, the group is a distribution group. names.append('SECURITY_ENABLED') names.sort() return names def format_guid(value, offset=0): """ Converts the binary representation of a GUID structure to string form. :param bytes value: :return unicode: """ value = struct.unpack_from(b'I2H8B', value, offset) text = '{0:08x}-{1:04x}-{2:04x}-{3:02x}{4:02x}-{5:02x}{6:02x}{7:02x}{8:02x}{9:02x}{10:02x}'.format(*value) return text def format_instance_type(value): """ Converts the base 10 string representation of a 32-bit integer to a list of `Instance-Type` names. :param unicode value: Base 10 string representation of a 32-bit integer. :return list: Sorted list of `Instance-Type` names. **References:** 1. `Instance-Type attribute `_ """ flags = int(value) names = [] # Confirmed: These are the same values as displayed by Active Directory: # # 1. IS_NC_HEAD DC=msapps,DC=local # 2. WRITE CN=david-noble,OU=Staff,DC=msapps,DC=local # # Unconfirmed: # # 1. 0x02 # 2. NC_ABOVE Some evidence at http://goo.gl/rzBtFg # 3. 0x10 # 4. 0x20 if flags & 0x00000001: # The head of naming context. names.append('IS_NC_HEAD') if flags & 0x00000002: # This replica is not instantiated. names.append('0x02') if flags & 0x00000004: # The object is writable on this directory. names.append('WRITE') if flags & 0x00000008: # The naming context above this one on this directory is held. names.append('NC_ABOVE') if flags & 0x00000010: # The naming context is under construction for the first time by replication. names.append('0x10') if flags & 0x00000020: # The naming context is in the process of being removed from the local DSA. names.append('0x20') names.sort() return names def format_sam_account_type(value): """ Converts the base 10 string representation of a 32-bit integer to a `SAM-Account-Type` name sans `SAM_` prefix. :param unicode value: Base 10 string representation of a 32-bit integer. :return unicode: `SAM-Account-Type` name sans `SAM_` prefix. **References:** 1. `SAM-Account-Type attribute `_ 2. `ACCOUNT_TYPE Values `_ """ code = int(value) # Confirmed: These names are as displayed by Active Directory: # # 1. ALIAS_OBJECT DHCP Administrators,CN=Users,DC=msapps,DC=local # 2. GROUP_OBJECT CN=DnsUpdateProxy,CN=Users,DC=msapps,DC=local # 3. MACHINE_ACCOUNT CN=EXCHANGE,CN=Computers,DC=msapps,DC=local # 4. NORMAL_USER_ACCOUNT CN=Splunker,CN=Users,DC=msapps,DC=local # # Unconfirmed: # # 1. DOMAIN_OBJECT # 2. NON_SECURITY_GROUP_OBJECT # 3. NON_SECURITY_ALIAS_OBJECT # 4. TRUST_ACCOUNT # 5. APP_BASIC_GROUP # 6. APP_QUERY_GROUP if code == 0x0000000000: name = 'DOMAIN_OBJECT' elif code == 0x10000000: name = 'GROUP_OBJECT' elif code == 0x10000001: name = 'NON_SECURITY_GROUP_OBJECT' elif code == 0x20000000: name = 'ALIAS_OBJECT' elif code == 0x20000001: name = 'NON_SECURITY_ALIAS_OBJECT' elif code == 0x30000000: name = 'NORMAL_USER_ACCOUNT' elif code == 0x30000001: name = 'MACHINE_ACCOUNT' elif code == 0x30000002: name = 'TRUST_ACCOUNT' elif code == 0x40000000: name = 'APP_BASIC_GROUP' elif code == 0x40000001: # SAM_APP_QUERY_GROUP name = 'APP_QUERY_GROUP' else: name = value return name def format_sid(value, offset=0, rid_map=None): """ Converts a SID structure to a SID string. :param bytes value: A byte buffer. :param int offset: Offset of a SID structure within :paramref:`value`. .. codeblock:: c typedef struct _SID_IDENTIFIER_AUTHORITY { BYTE Value[6]; } SID_IDENTIFIER_AUTHORITY,*PSID_IDENTIFIER_AUTHORITY,*LPSID_IDENTIFIER_AUTHORITY; typedef struct _SID { BYTE Revision; BYTE SubAuthorityCount; SID_IDENTIFIER_AUTHORITY IdentifierAuthority; DWORD SubAuthority[ANYSIZE_ARRAY]; } SID, *PISID; :return: String representation of the SID starting at `offset` within the `value` buffer. The string format of the SID type is described in this MSDN article: `SID String Format Syntax `_. """ def unpack_identifier_authority(): triplet = struct.unpack_from(b'>3H', value, offset + 2) result = triplet[2] + (triplet[1] << 16) + (triplet[0] << 32) return result revision, sub_authority_count = struct.unpack_from(b'BB', value, offset) sub_authority_count &= 0xFF # upper nibble is reserved for future use if rid_map is not None: rid_offset = offset + 8 + 4 * (sub_authority_count - 1) rid = struct.unpack_from(b'I', value, rid_offset)[0] try: if rid == 0x00000000 and sub_authority_count == 1: identifier_authority = unpack_identifier_authority() if identifier_authority == 1: # SECURITY_WORLD_SID_AUTHORITY text = 'WD' elif identifier_authority == 3: # SECURITY_CREATOR_SID_AUTHORITY text = 'CO' else: text = '-'.join(('S', text_type(revision), text_type(identifier_authority), text_type(rid))) else: text = rid_map[rid] return text except KeyError: pass identifier_authority = unpack_identifier_authority() sub_authorities = struct.unpack_from(str(sub_authority_count) + 'I', value, offset + 8) text = '-'.join(itertools.chain( ( 'S', text_type(revision), text_type(identifier_authority) if identifier_authority < 0x100000000 else '{0:#014x}'.format( identifier_authority) ), map(lambda p: text_type(p), sub_authorities))) return text def format_security_descriptor(value): """ Converts the binary representation of a Windows security descriptor to its string form. This function uses the `Security Descriptor String Format `_ to represent security descriptors. :param value: The binary representation of a Windows security descriptor. :return: String form of :paramref:`value`. """ start_owner, start_group = struct.unpack_from(b'II', value, 4) text = '' if start_owner: text += 'O:' + format_sid(value, start_owner, _well_known_sid_strings) if start_group: text += 'G:' + format_sid(value, start_group, _well_known_sid_strings) control = struct.unpack_from(b'H', value, 2)[0] if control & 0x0004: # SE_DACL_PRESENT text += 'D:' if control & 0x1000: # SE_DACL_PROTECTED text += 'P' if control & 0x0100: # SE_DACL_AUTO_INHERIT_REQ text += 'AR' if control & 0x0400: # SE_DACL_AUTO_INHERITED: text += 'AI' text += format_acl(value, 16) if control & 0x0010: # SE_SACL_PRESENT text += 'S:' if control & 0x2000: # SE_SACL_PROTECTED text += 'P' if control & 0x0200: # SE_SACL_AUTO_INHERIT_REQ text += 'AR' if control & 0x0800: # SE_SACL_AUTO_INHERITED: text += 'AI' text += format_acl(value, 12) return text def format_user_flag_enum(value): """ Converts the base 10 string representation of a 32-bit integer to a list of ADS_USER_FLAG_ENUM values. :param value: String representation of a 32-bit integer in base 10. :return: Sorted list of ADS_USER_FLAG_ENUM values sans `ADS_UF_` prefix. **References:** 1. `ADS_USER_FLAG_ENUM enumeration `_ 2. `USER_INFO_1008 structure `_ 3. `User-Account-Control attribute `_ 4. `ms-DS-User-Account-Control-Computed attribute `_ """ flags = int(value) names = [] # Well documented so confidence is high that these names are correct # Zero or more these flags may be set if flags & 0x0000001: names.append('SCRIPT') if flags & 0x0000002: names.append('ACCOUNTDISABLE') if flags & 0x0000008: names.append('HOMEDIR_REQUIRED') if flags & 0x0000010: names.append('LOCKOUT') if flags & 0x0000020: names.append('PASSWD_NOTREQD') if flags & 0x0000040: names.append('PASSWD_CANT_CHANGE') if flags & 0x0000080: names.append('ENCRYPTED_TEXT_PASSWORD_ALLOWED') if flags & 0x0010000: names.append('DONT_EXPIRE_PASSWD') if flags & 0x0020000: names.append('MNS_LOGON_ACCOUNT') if flags & 0x0040000: names.append('SMARTCARD_REQUIRED') if flags & 0x0080000: names.append('TRUSTED_FOR_DELEGATION') if flags & 0x0100000: names.append('NOT_DELEGATED') if flags & 0x0200000: names.append('USE_DES_KEY_ONLY') if flags & 0x0400000: names.append('DONT_REQUIRE_PREAUTH') if flags & 0x0800000: names.append('PASSWORD_EXPIRED') if flags & 0x1000000: names.append('TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION') # Zero or one of these flags may be set if flags & 0x000000100: names.append('TEMP_DUPLICATE_ACCOUNT') elif flags & 0x0000200: names.append('NORMAL_ACCOUNT') elif flags & 0x0001000: names.append('WORKSTATION_TRUST_ACCOUNT') elif flags & 0x0002000: names.append('SERVER_TRUST_ACCOUNT') elif flags & 0x0000800: names.append('INTERDOMAIN_TRUST_ACCOUNT') names.sort() return names def format_unicode(raw_value): try: if str is not bytes: # Python 3 return str(raw_value, 'utf-8', errors='strict') else: # Python 2 return unicode(raw_value, 'utf-8', errors='strict') except (TypeError, UnicodeDecodeError): pass return raw_value formatting_extensions = { # attributeId values '1.2.840.113556.1.2.231': format_attribute_syntax, # oMSyntax '1.2.840.113556.1.4.159': format_filetime, # Account-Expires '1.2.840.113556.1.4.49': format_filetime, # Bad-Password-Time '1.2.840.113556.1.4.13': format_filetime, # Builtin-Creation-Time '1.2.840.113556.1.4.26': format_filetime, # Creation-Time '1.2.840.113556.1.4.720': format_filetime, # dhcp-Update-Time '1.2.840.113556.1.4.519': format_filetime, # Last-Backup-Restoration-Time '1.2.840.113556.1.4.50': format_filetime, # Last-Content-Indexed '1.2.840.113556.1.4.51': format_filetime, # Last-Logoff '1.2.840.113556.1.4.52': format_filetime, # Last-Logon '1.2.840.113556.1.4.1696': format_filetime, # Last-Logon-Timestamp '1.2.840.113556.1.4.53': format_filetime, # Last-Set-Time '1.2.840.113556.1.4.662': format_filetime, # Lockout-Time '1.2.840.113556.1.4.66': format_filetime, # LSA-Creation-Time '1.2.840.113556.1.4.2262': format_filetime, # ms-DS-Approximate-Last-Logon-Time-Stamp '1.2.840.113556.1.4.1442': format_filetime, # ms-DS-Cached-Membership-Time-Stamp '1.2.840.113556.1.4.1971': format_filetime, # ms-DS-Last-Failed-Interactive-Logon-Time '1.2.840.113556.1.4.1970': format_filetime, # ms-DS-Last-Successful-Interactive-Logon-Time '1.2.840.113556.1.4.1996': format_filetime, # ms-DS-User-Password-Expiry-Time-Computed '1.2.840.113556.1.4.96': format_filetime, # Pwd-Last-Set '1.2.840.113556.1.4.502': format_filetime, # Time-Vol-Change '1.2.840.113556.1.4.750': format_group_type, # Group-Type '1.2.840.113556.1.4.149': format_guid, # Attribute-Security-GUID '1.2.840.113556.1.4.533': format_guid, # FRS-Replica-Set-GUID '1.2.840.113556.1.4.43': format_guid, # FRS-Version-GUID '1.2.840.113556.1.4.1428': format_guid, # ms-COM-ObjectId '1.2.840.113556.1.4.2032': format_guid, # ms-DFS-Generation-GUID-v2 '1.2.840.113556.1.4.2041': format_guid, # ms-DFS-Link-Identity-GUID-v2 '1.2.840.113556.1.4.2033': format_guid, # ms-DFS-Namespace-Identity-GUID-v2 '1.2.840.113556.1.6.13.3.18': format_guid, # ms-DFSR-ContentSetGuid '1.2.840.113556.1.6.13.3.23': format_guid, # ms-DFSR-ReplicationGroupGuid '1.2.840.113556.1.4.1949': format_guid, # ms-DS-Az-Object-Guid '1.2.840.113556.1.4.1360': format_guid, # MS-DS-Consistency-Guid '1.2.840.113556.1.4.2062': format_guid, # ms-DS-Optional-Feature-GUID '1.2.840.113556.1.4.1965': format_guid, # ms-FVE-RecoveryGuid '1.2.840.113556.1.4.1998': format_guid, # ms-FVE-VolumeGuid '1.2.840.113556.1.4.359': format_guid, # Netboot-GUID '1.2.840.113556.1.4.2': format_guid, # Object-Guid '1.2.840.113556.1.4.505': format_guid, # OMT-Guid '1.2.840.113556.1.4.333': format_guid, # OMT-Indx-Guid '1.2.840.113556.1.4.1224': format_guid, # Parent-GUID '1.2.840.113556.1.4.205': format_guid, # PKT-Guid '1.2.840.113556.1.4.148': format_guid, # Schema-ID-GUID '1.2.840.113556.1.4.122': format_guid, # Service-Class-ID '1.2.840.113556.1.4.362': format_guid, # Site-GUID '1.2.840.113556.1.4.336': format_guid, # Vol-Table-GUID '1.2.840.113556.1.4.334': format_guid, # Vol-Table-Idx-GUID '1.2.840.113556.1.2.1': format_instance_type, # Instance-Type '1.2.840.113556.1.4.302': format_sam_account_type, # SAM-Account-Type '1.2.840.113556.1.4.2154': format_sid, # ms-Authz-Central-Access-Policy-ID '1.2.840.113556.1.4.1410': format_sid, # MS-DS-Creator-SID '1.2.840.113556.1.4.1844': format_sid, # ms-DS-Quota-Trustee '1.2.840.113556.1.4.146': format_sid, # Object-Sid '1.2.840.113556.1.4.121': format_sid, # Security-Identifier '1.2.840.113556.1.4.609': format_sid, # SID-History '1.2.840.113556.1.4.667': format_sid, # Sync-With-SID '1.2.840.113556.1.4.1301': format_sid, # Token-Groups '1.2.840.113556.1.4.1418': format_sid, # Token-Groups-Global-And-Universal '1.2.840.113556.1.4.1303': format_sid, # Token-Groups-No-GC-Acceptable '1.2.840.113556.1.4.8': format_user_flag_enum, # User-Account-Control # formatter specially for msExchMailboxSecurityDescriptor '1.2.840.113556.1.4.7000.102.80' : format_security_descriptor, # msExchMailboxSecurityDescriptor # below formatters are present in standard formatter of ldap3 but for keeping the # consistency in formatting with the previous versions, this formatters are added here. '1.2.840.113556.1.4.903': format_unicode, # Object (DN-binary) - Microsoft '1.2.840.113556.1.4.1221': format_unicode # Object (OR-name) - Microsoft }