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

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: 82 ↛ 85line 82 didn't jump to line 85, because the condition on line 82 was never false
83 self['OwnerSid'] = LDAP_SID(data=data[self['OffsetOwner']:])
84 else:
85 self['OwnerSid'] = b''
87 if self['OffsetGroup'] != 0: 87 ↛ 90line 87 didn't jump to line 90, because the condition on line 87 was never false
88 self['GroupSid'] = LDAP_SID(data=data[self['OffsetGroup']:])
89 else:
90 self['GroupSid'] = b''
92 if self['OffsetSacl'] != 0: 92 ↛ 95line 92 didn't jump to line 95, because the condition on line 92 was never false
93 self['Sacl'] = ACL(data=data[self['OffsetSacl']:])
94 else:
95 self['Sacl'] = b''
97 if self['OffsetDacl'] != 0: 97 ↛ 100line 97 didn't jump to line 100, because the condition on line 97 was never false
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'': 108 ↛ 112line 108 didn't jump to line 112, because the condition on line 108 was never false
109 self['OffsetSacl'] = headerlen + datalen
110 datalen += len(self['Sacl'].getData())
111 else:
112 self['OffsetSacl'] = 0
114 if self['Dacl'] != b'': 114 ↛ 118line 114 didn't jump to line 118, because the condition on line 114 was never false
115 self['OffsetDacl'] = headerlen + datalen
116 datalen += len(self['Dacl'].getData())
117 else:
118 self['OffsetDacl'] = 0
120 if self['OwnerSid'] != b'': 120 ↛ 124line 120 didn't jump to line 124, because the condition on line 120 was never false
121 self['OffsetOwner'] = headerlen + datalen
122 datalen += len(self['OwnerSid'].getData())
123 else:
124 self['OffsetOwner'] = 0
126 if self['GroupSid'] != b'': 126 ↛ 130line 126 didn't jump to line 130, because the condition on line 126 was never false
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: 171 ↛ 173line 171 didn't jump to line 173, because the condition on line 171 was never false
172 self['AceSize'] = len(self['Ace'].getData())+4 # Header size (4 bytes) is included
173 if self['AceSize'] % 4 != 0: 173 ↛ 175line 173 didn't jump to line 175, because the condition on line 173 was never true
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']: 180 ↛ 181line 180 didn't jump to line 181, because the condition on line 180 was never true
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: 463 ↛ 464line 463 didn't jump to line 464, because the condition on line 463 was never true
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}