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