Coverage for /root/GitHubProjects/impacket/impacket/ldap/ldaptypes.py : 44%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1# Impacket - Collection of Python classes for working with network protocols.
2#
3# SECUREAUTH LABS. Copyright (C) 2020 SecureAuth Corporation. All rights reserved.
4#
5# This software is provided under a slightly modified version
6# of the Apache Software License. See the accompanying LICENSE file
7# for more information.
8#
9# Description:
10# Structures and types used in LDAP
11# Contains the Structures for the NT Security Descriptor (non-RPC format) and
12# all ACL related structures
13#
14# Author:
15# Dirk-jan Mollema (@_dirkjan) / Fox-IT (https://www.fox-it.com)
16#
17from struct import unpack, pack
18from impacket.structure import Structure
20# Global constant if the library should recalculate ACE sizes in objects that are decoded/re-encoded.
21# This defaults to True, but this causes the ACLs to not match on a binary level
22# since Active Directory for some reason sometimes adds null bytes to the end of ACEs.
23# This is valid according to the spec (see 2.4.4), but since impacket encodes them more efficiently
24# this should be turned off if running unit tests.
25RECALC_ACE_SIZE = True
27# LDAP SID structure - based on SAMR_RPC_SID, except the SubAuthority is LE here
28class LDAP_SID_IDENTIFIER_AUTHORITY(Structure):
29 structure = (
30 ('Value','6s'),
31 )
33class LDAP_SID(Structure):
34 structure = (
35 ('Revision','<B'),
36 ('SubAuthorityCount','<B'),
37 ('IdentifierAuthority',':',LDAP_SID_IDENTIFIER_AUTHORITY),
38 ('SubLen','_-SubAuthority','self["SubAuthorityCount"]*4'),
39 ('SubAuthority',':'),
40 )
42 def formatCanonical(self):
43 ans = 'S-%d-%d' % (self['Revision'], ord(self['IdentifierAuthority']['Value'][5:6]))
44 for i in range(self['SubAuthorityCount']):
45 ans += '-%d' % ( unpack('<L',self['SubAuthority'][i*4:i*4+4])[0])
46 return ans
48 def fromCanonical(self, canonical):
49 items = canonical.split('-')
50 self['Revision'] = int(items[1])
51 self['IdentifierAuthority'] = LDAP_SID_IDENTIFIER_AUTHORITY()
52 self['IdentifierAuthority']['Value'] = b'\x00\x00\x00\x00\x00' + pack('B',int(items[2]))
53 self['SubAuthorityCount'] = len(items) - 3
54 self['SubAuthority'] = b''
55 for i in range(self['SubAuthorityCount']):
56 self['SubAuthority'] += pack('<L', int(items[i+3]))
58"""
59Self-relative security descriptor as described in 2.4.6
60https://msdn.microsoft.com/en-us/library/cc230366.aspx
61"""
62class SR_SECURITY_DESCRIPTOR(Structure):
63 structure = (
64 ('Revision','c'),
65 ('Sbz1','c'),
66 ('Control','<H'),
67 ('OffsetOwner','<L'),
68 ('OffsetGroup','<L'),
69 ('OffsetSacl','<L'),
70 ('OffsetDacl','<L'),
71 ('Sacl',':'),
72 ('Dacl',':'),
73 ('OwnerSid',':'),
74 ('GroupSid',':'),
75 )
77 def fromString(self, data):
78 Structure.fromString(self, data)
79 # All these fields are optional, if the offset is 0 they are empty
80 # there are also flags indicating if they are present
81 # TODO: parse those if it adds value
82 if self['OffsetOwner'] != 0:
83 self['OwnerSid'] = LDAP_SID(data=data[self['OffsetOwner']:])
84 else:
85 self['OwnerSid'] = b''
87 if self['OffsetGroup'] != 0:
88 self['GroupSid'] = LDAP_SID(data=data[self['OffsetGroup']:])
89 else:
90 self['GroupSid'] = b''
92 if self['OffsetSacl'] != 0:
93 self['Sacl'] = ACL(data=data[self['OffsetSacl']:])
94 else:
95 self['Sacl'] = b''
97 if self['OffsetDacl'] != 0:
98 self['Dacl'] = ACL(data=data[self['OffsetDacl']:])
99 else:
100 self['Sacl'] = b''
102 def getData(self):
103 headerlen = 20
104 # Reconstruct the security descriptor
105 # flags are currently not set automatically
106 # TODO: do this?
107 datalen = 0
108 if self['Sacl'] != b'':
109 self['OffsetSacl'] = headerlen + datalen
110 datalen += len(self['Sacl'].getData())
111 else:
112 self['OffsetSacl'] = 0
114 if self['Dacl'] != b'':
115 self['OffsetDacl'] = headerlen + datalen
116 datalen += len(self['Dacl'].getData())
117 else:
118 self['OffsetDacl'] = 0
120 if self['OwnerSid'] != b'':
121 self['OffsetOwner'] = headerlen + datalen
122 datalen += len(self['OwnerSid'].getData())
123 else:
124 self['OffsetOwner'] = 0
126 if self['GroupSid'] != b'':
127 self['OffsetGroup'] = headerlen + datalen
128 datalen += len(self['GroupSid'].getData())
129 else:
130 self['OffsetGroup'] = 0
131 return Structure.getData(self)
133"""
134ACE as described in 2.4.4
135https://msdn.microsoft.com/en-us/library/cc230295.aspx
136"""
137class ACE(Structure):
138 # Flag constants
139 CONTAINER_INHERIT_ACE = 0x02
140 FAILED_ACCESS_ACE_FLAG = 0x80
141 INHERIT_ONLY_ACE = 0x08
142 INHERITED_ACE = 0x10
143 NO_PROPAGATE_INHERIT_ACE = 0x04
144 OBJECT_INHERIT_ACE = 0x01
145 SUCCESSFUL_ACCESS_ACE_FLAG = 0x40
147 structure = (
148 #
149 # ACE_HEADER as described in 2.4.4.1
150 # https://msdn.microsoft.com/en-us/library/cc230296.aspx
151 #
152 ('AceType','B'),
153 ('AceFlags','B'),
154 ('AceSize','<H'),
155 # Virtual field to calculate data length from AceSize
156 ('AceLen', '_-Ace', 'self["AceSize"]-4'),
157 #
158 # ACE body, is parsed depending on the type
159 #
160 ('Ace',':')
161 )
163 def fromString(self, data):
164 # This will parse the header
165 Structure.fromString(self, data)
166 # Now we parse the ACE body according to its type
167 self['TypeName'] = ACE_TYPE_MAP[self['AceType']].__name__
168 self['Ace'] = ACE_TYPE_MAP[self['AceType']](data=self['Ace'])
170 def getData(self):
171 if RECALC_ACE_SIZE or 'AceSize' not in self.fields:
172 self['AceSize'] = len(self['Ace'].getData())+4 # Header size (4 bytes) is included
173 if self['AceSize'] % 4 != 0:
174 # Make sure the alignment is correct
175 self['AceSize'] += self['AceSize'] % 4
176 data = Structure.getData(self)
177 # For some reason ACEs are sometimes longer than they need to be
178 # we fill this space up with null bytes to make sure the object
179 # we create is identical to the original object
180 if len(data) < self['AceSize']:
181 data += '\x00' * (self['AceSize'] - len(data))
182 return data
184 def hasFlag(self, flag):
185 return self['AceFlags'] & flag == flag
187"""
188ACCESS_MASK as described in 2.4.3
189https://msdn.microsoft.com/en-us/library/cc230294.aspx
190"""
191class ACCESS_MASK(Structure):
192 # Flag constants
193 GENERIC_READ = 0x80000000
194 GENERIC_WRITE = 0x40000000
195 GENERIC_EXECUTE = 0x20000000
196 GENERIC_ALL = 0x10000000
197 MAXIMUM_ALLOWED = 0x02000000
198 ACCESS_SYSTEM_SECURITY = 0x01000000
199 SYNCHRONIZE = 0x00100000
200 WRITE_OWNER = 0x00080000
201 WRITE_DACL = 0x00040000
202 READ_CONTROL = 0x00020000
203 DELETE = 0x00010000
205 structure = (
206 ('Mask', '<L'),
207 )
209 def hasPriv(self, priv):
210 return self['Mask'] & priv == priv
212 def setPriv(self, priv):
213 self['Mask'] |= priv
215 def removePriv(self, priv):
216 self['Mask'] ^= priv
218"""
219ACCESS_ALLOWED_ACE as described in 2.4.4.2
220https://msdn.microsoft.com/en-us/library/cc230286.aspx
221"""
222class ACCESS_ALLOWED_ACE(Structure):
223 ACE_TYPE = 0x00
224 structure = (
225 ('Mask', ':', ACCESS_MASK),
226 ('Sid', ':', LDAP_SID)
227 )
229"""
230ACCESS_ALLOWED_OBJECT_ACE as described in 2.4.4.3
231https://msdn.microsoft.com/en-us/library/cc230289.aspx
232"""
233class ACCESS_ALLOWED_OBJECT_ACE(Structure):
234 ACE_TYPE = 0x05
236 # Flag contstants
237 ACE_OBJECT_TYPE_PRESENT = 0x01
238 ACE_INHERITED_OBJECT_TYPE_PRESENT = 0x02
240 # ACE type specific mask constants
241 # Note that while not documented, these also seem valid
242 # for ACCESS_ALLOWED_ACE types
243 ADS_RIGHT_DS_CONTROL_ACCESS = 0x00000100
244 ADS_RIGHT_DS_CREATE_CHILD = 0x00000001
245 ADS_RIGHT_DS_DELETE_CHILD = 0x00000002
246 ADS_RIGHT_DS_READ_PROP = 0x00000010
247 ADS_RIGHT_DS_WRITE_PROP = 0x00000020
248 ADS_RIGHT_DS_SELF = 0x00000008
251 structure = (
252 ('Mask', ':', ACCESS_MASK),
253 ('Flags', '<L'),
254 # Optional field
255 ('ObjectTypeLen','_-ObjectType','self.checkObjectType(self["Flags"])'),
256 ('ObjectType', ':=""'),
257 # Optional field
258 ('InheritedObjectTypeLen','_-InheritedObjectType','self.checkInheritedObjectType(self["Flags"])'),
259 ('InheritedObjectType', ':=""'),
260 ('Sid', ':', LDAP_SID)
261 )
263 @staticmethod
264 def checkInheritedObjectType(flags):
265 if flags & ACCESS_ALLOWED_OBJECT_ACE.ACE_INHERITED_OBJECT_TYPE_PRESENT:
266 return 16
267 return 0
269 @staticmethod
270 def checkObjectType(flags):
271 if flags & ACCESS_ALLOWED_OBJECT_ACE.ACE_OBJECT_TYPE_PRESENT:
272 return 16
273 return 0
275 def getData(self):
276 # Set the correct flags
277 if self['ObjectType'] != b'':
278 self['Flags'] |= self.ACE_OBJECT_TYPE_PRESENT
279 if self['InheritedObjectType'] != b'':
280 self['Flags'] |= self.ACE_INHERITED_OBJECT_TYPE_PRESENT
281 return Structure.getData(self)
283 def hasFlag(self, flag):
284 return self['Flags'] & flag == flag
286"""
287ACCESS_DENIED_ACE as described in 2.4.4.4
288https://msdn.microsoft.com/en-us/library/cc230291.aspx
289Structure is identical to ACCESS_ALLOWED_ACE
290"""
291class ACCESS_DENIED_ACE(ACCESS_ALLOWED_ACE):
292 ACE_TYPE = 0x01
294"""
295ACCESS_DENIED_OBJECT_ACE as described in 2.4.4.5
296https://msdn.microsoft.com/en-us/library/gg750297.aspx
297Structure is identical to ACCESS_ALLOWED_OBJECT_ACE
298"""
299class ACCESS_DENIED_OBJECT_ACE(ACCESS_ALLOWED_OBJECT_ACE):
300 ACE_TYPE = 0x06
302"""
303ACCESS_ALLOWED_CALLBACK_ACE as described in 2.4.4.6
304https://msdn.microsoft.com/en-us/library/cc230287.aspx
305"""
306class ACCESS_ALLOWED_CALLBACK_ACE(Structure):
307 ACE_TYPE = 0x09
308 structure = (
309 ('Mask', ':', ACCESS_MASK),
310 ('Sid', ':', LDAP_SID),
311 ('ApplicationData', ':')
312 )
314"""
315ACCESS_DENIED_OBJECT_ACE as described in 2.4.4.7
316https://msdn.microsoft.com/en-us/library/cc230292.aspx
317Structure is identical to ACCESS_ALLOWED_CALLBACK_ACE
318"""
319class ACCESS_DENIED_CALLBACK_ACE(ACCESS_ALLOWED_CALLBACK_ACE):
320 ACE_TYPE = 0x0A
322"""
323ACCESS_ALLOWED_CALLBACK_OBJECT_ACE as described in 2.4.4.8
324https://msdn.microsoft.com/en-us/library/cc230288.aspx
325"""
326class ACCESS_ALLOWED_CALLBACK_OBJECT_ACE(ACCESS_ALLOWED_OBJECT_ACE):
327 ACE_TYPE = 0x0B
328 structure = (
329 ('Mask', ':', ACCESS_MASK),
330 ('Flags', '<L'),
331 # Optional field
332 ('ObjectTypeLen','_-ObjectType','self.checkObjectType(self["Flags"])'),
333 ('ObjectType', ':=""'),
334 # Optional field
335 ('InheritedObjectTypeLen','_-InheritedObjectType','self.checkInheritedObjectType(self["Flags"])'),
336 ('InheritedObjectType', ':=""'),
337 ('Sid', ':', LDAP_SID),
338 ('ApplicationData', ':')
339 )
341"""
342ACCESS_DENIED_CALLBACK_OBJECT_ACE as described in 2.4.4.7
343https://msdn.microsoft.com/en-us/library/cc230292.aspx
344Structure is identical to ACCESS_ALLOWED_OBJECT_OBJECT_ACE
345"""
346class ACCESS_DENIED_CALLBACK_OBJECT_ACE(ACCESS_ALLOWED_CALLBACK_OBJECT_ACE):
347 ACE_TYPE = 0x0C
349"""
350SYSTEM_AUDIT_ACE as described in 2.4.4.10
351https://msdn.microsoft.com/en-us/library/cc230376.aspx
352Structure is identical to ACCESS_ALLOWED_ACE
353"""
354class SYSTEM_AUDIT_ACE(ACCESS_ALLOWED_ACE):
355 ACE_TYPE = 0x02
358"""
359SYSTEM_AUDIT_OBJECT_ACE as described in 2.4.4.11
360https://msdn.microsoft.com/en-us/library/gg750298.aspx
361Structure is identical to ACCESS_ALLOWED_CALLBACK_OBJECT_ACE
362"""
363class SYSTEM_AUDIT_OBJECT_ACE(ACCESS_ALLOWED_CALLBACK_OBJECT_ACE):
364 ACE_TYPE = 0x07
367"""
368SYSTEM_AUDIT_CALLBACK_ACE as described in 2.4.4.12
369https://msdn.microsoft.com/en-us/library/cc230377.aspx
370Structure is identical to ACCESS_ALLOWED_CALLBACK_ACE
371"""
372class SYSTEM_AUDIT_CALLBACK_ACE(ACCESS_ALLOWED_CALLBACK_ACE):
373 ACE_TYPE = 0x0D
375"""
376SYSTEM_AUDIT_CALLBACK_ACE as described in 2.4.4.13
377https://msdn.microsoft.com/en-us/library/cc230379.aspx
378Structure is identical to ACCESS_ALLOWED_ACE, but with custom masks and meanings.
379Lets keep it separate for now
380"""
381class SYSTEM_MANDATORY_LABEL_ACE(Structure):
382 ACE_TYPE = 0x11
383 structure = (
384 ('Mask', ':', ACCESS_MASK),
385 ('Sid', ':', LDAP_SID)
386 )
388"""
389SYSTEM_AUDIT_CALLBACK_ACE as described in 2.4.4.14
390https://msdn.microsoft.com/en-us/library/cc230378.aspx
391Structure is identical to ACCESS_ALLOWED_CALLBACK_OBJECT_ACE
392"""
393class SYSTEM_AUDIT_CALLBACK_OBJECT_ACE(ACCESS_ALLOWED_CALLBACK_OBJECT_ACE):
394 ACE_TYPE = 0x0F
396"""
397SYSTEM_RESOURCE_ATTRIBUTE_ACE as described in 2.4.4.15
398https://msdn.microsoft.com/en-us/library/hh877837.aspx
399Structure is identical to ACCESS_ALLOWED_CALLBACK_ACE
400The application data however is encoded in CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1
401format as described in section 2.4.10.1
402Todo: implement this substructure if needed
403"""
404class SYSTEM_RESOURCE_ATTRIBUTE_ACE(ACCESS_ALLOWED_CALLBACK_ACE):
405 ACE_TYPE = 0x12
408"""
409SYSTEM_SCOPED_POLICY_ID_ACE as described in 2.4.4.16
410https://msdn.microsoft.com/en-us/library/hh877846.aspx
411Structure is identical to ACCESS_ALLOWED_ACE
412The Sid data MUST match a CAPID of a CentralAccessPolicy
413contained in the CentralAccessPoliciesList
414Todo: implement this substructure if needed
415Also the ACCESS_MASK must always be 0
416"""
417class SYSTEM_SCOPED_POLICY_ID_ACE(ACCESS_ALLOWED_ACE):
418 ACE_TYPE = 0x13
420# All the ACE types in a list
421ACE_TYPES = [
422 ACCESS_ALLOWED_ACE,
423 ACCESS_ALLOWED_OBJECT_ACE,
424 ACCESS_DENIED_ACE,
425 ACCESS_DENIED_OBJECT_ACE,
426 ACCESS_ALLOWED_CALLBACK_ACE,
427 ACCESS_DENIED_CALLBACK_ACE,
428 ACCESS_ALLOWED_CALLBACK_OBJECT_ACE,
429 ACCESS_DENIED_CALLBACK_OBJECT_ACE,
430 SYSTEM_AUDIT_ACE,
431 SYSTEM_AUDIT_OBJECT_ACE,
432 SYSTEM_AUDIT_CALLBACK_ACE,
433 SYSTEM_MANDATORY_LABEL_ACE,
434 SYSTEM_AUDIT_CALLBACK_OBJECT_ACE,
435 SYSTEM_RESOURCE_ATTRIBUTE_ACE,
436 SYSTEM_SCOPED_POLICY_ID_ACE
437]
439# A dict of all the ACE types indexed by their type number
440ACE_TYPE_MAP = {ace.ACE_TYPE: ace for ace in ACE_TYPES}
442"""
443ACL as described in 2.4.5
444https://msdn.microsoft.com/en-us/library/cc230297.aspx
445"""
446class ACL(Structure):
447 structure = (
448 ('AclRevision', 'B'),
449 ('Sbz1', 'B'),
450 ('AclSize', '<H'),
451 ('AceCount', '<H'),
452 ('Sbz2', '<H'),
453 # Virtual field to calculate data length from AclSize
454 ('DataLen', '_-Data', 'self["AclSize"]-8'),
455 ('Data', ':'),
456 )
458 def fromString(self, data):
459 self.aces = []
460 Structure.fromString(self, data)
461 for i in range(self['AceCount']):
462 # If we don't have any data left, return
463 if len(self['Data']) == 0:
464 raise Exception("ACL header indicated there are more ACLs to unpack, but there is no more data")
465 ace = ACE(data=self['Data'])
466 self.aces.append(ace)
467 self['Data'] = self['Data'][ace['AceSize']:]
468 self['Data'] = self.aces
470 def getData(self):
471 self['AceCount'] = len(self.aces)
472 # We modify the data field to be able to use the
473 # parent class parsing
474 self['Data'] = b''.join([ace.getData() for ace in self.aces])
475 self['AclSize'] = len(self['Data'])+8 # Header size (8 bytes) is included
476 data = Structure.getData(self)
477 # Put the ACEs back in data
478 self['Data'] = self.aces
479 return data
481"""
482objectClass mapping to GUID for some common classes (index is the ldapDisplayName).
483Reference:
484 https://msdn.microsoft.com/en-us/library/ms680938(v=vs.85).aspx
485Can also be queried from the Schema
486"""
487OBJECTTYPE_GUID_MAP = {
488 b'group': 'bf967a9c-0de6-11d0-a285-00aa003049e2',
489 b'domain': '19195a5a-6da0-11d0-afd3-00c04fd930c9',
490 b'organizationalUnit': 'bf967aa5-0de6-11d0-a285-00aa003049e2',
491 b'user': 'bf967aba-0de6-11d0-a285-00aa003049e2',
492 b'groupPolicyContainer': 'f30e3bc2-9ff0-11d1-b603-0000f80367c1'
493}