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

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

## 183 statements

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#

17 from struct import unpack, pack

18 from impacket.structure import Structure

19

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.

25 RECALC_ACE_SIZE = True

26

27# LDAP SID structure - based on SAMR_RPC_SID, except the SubAuthority is LE here

28 class LDAP_SID_IDENTIFIER_AUTHORITY(Structure):

29 structure = (

30 ('Value','6s'),

31 )

32

33 class 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 )

41

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

47

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]))

57

58"""

59 Self-relative security descriptor as described in 2.4.6

60 https://msdn.microsoft.com/en-us/library/cc230366.aspx

61"""

62 class 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 )

76

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''

86

87 if self['OffsetGroup'] != 0:

88 self['GroupSid'] = LDAP_SID(data=data[self['OffsetGroup']:])

89 else:

90 self['GroupSid'] = b''

91

92 if self['OffsetSacl'] != 0:

93 self['Sacl'] = ACL(data=data[self['OffsetSacl']:])

94 else:

95 self['Sacl'] = b''

96

97 if self['OffsetDacl'] != 0:

98 self['Dacl'] = ACL(data=data[self['OffsetDacl']:])

99 else:

100 self['Sacl'] = b''

101

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

113

114 if self['Dacl'] != b'':

115 self['OffsetDacl'] = headerlen + datalen

116 datalen += len(self['Dacl'].getData())

117 else:

118 self['OffsetDacl'] = 0

119

120 if self['OwnerSid'] != b'':

121 self['OffsetOwner'] = headerlen + datalen

122 datalen += len(self['OwnerSid'].getData())

123 else:

124 self['OffsetOwner'] = 0

125

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)

132

133"""

134 ACE as described in 2.4.4

135 https://msdn.microsoft.com/en-us/library/cc230295.aspx

136"""

137 class 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

146

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 )

162

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'])

169

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

183

184 def hasFlag(self, flag):

185 return self['AceFlags'] & flag == flag

186

187"""

188 ACCESS_MASK as described in 2.4.3

189 https://msdn.microsoft.com/en-us/library/cc230294.aspx

190"""

191 class 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

204

205 structure = (

206 ('Mask', '<L'),

207 )

208

209 def hasPriv(self, priv):

210 return self['Mask'] & priv == priv

211

212 def setPriv(self, priv):

213 self['Mask'] |= priv

214

215 def removePriv(self, priv):

216 self['Mask'] ^= priv

217

218"""

219 ACCESS_ALLOWED_ACE as described in 2.4.4.2

220 https://msdn.microsoft.com/en-us/library/cc230286.aspx

221"""

222 class ACCESS_ALLOWED_ACE(Structure):

223 ACE_TYPE = 0x00

224 structure = (

225 ('Mask', ':', ACCESS_MASK),

226 ('Sid', ':', LDAP_SID)

227 )

228

229"""

230 ACCESS_ALLOWED_OBJECT_ACE as described in 2.4.4.3

231 https://msdn.microsoft.com/en-us/library/cc230289.aspx

232"""

233 class ACCESS_ALLOWED_OBJECT_ACE(Structure):

234 ACE_TYPE = 0x05

235

236 # Flag contstants

237 ACE_OBJECT_TYPE_PRESENT = 0x01

238 ACE_INHERITED_OBJECT_TYPE_PRESENT = 0x02

239

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

249

250

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 )

262

263 @staticmethod

264 def checkInheritedObjectType(flags):

265 if flags & ACCESS_ALLOWED_OBJECT_ACE.ACE_INHERITED_OBJECT_TYPE_PRESENT:

266 return 16

267 return 0

268

269 @staticmethod

270 def checkObjectType(flags):

271 if flags & ACCESS_ALLOWED_OBJECT_ACE.ACE_OBJECT_TYPE_PRESENT:

272 return 16

273 return 0

274

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)

282

283 def hasFlag(self, flag):

284 return self['Flags'] & flag == flag

285

286"""

287 ACCESS_DENIED_ACE as described in 2.4.4.4

288 https://msdn.microsoft.com/en-us/library/cc230291.aspx

289 Structure is identical to ACCESS_ALLOWED_ACE

290"""

291 class ACCESS_DENIED_ACE(ACCESS_ALLOWED_ACE):

292 ACE_TYPE = 0x01

293

294"""

295 ACCESS_DENIED_OBJECT_ACE as described in 2.4.4.5

296 https://msdn.microsoft.com/en-us/library/gg750297.aspx

297 Structure is identical to ACCESS_ALLOWED_OBJECT_ACE

298"""

299 class ACCESS_DENIED_OBJECT_ACE(ACCESS_ALLOWED_OBJECT_ACE):

300 ACE_TYPE = 0x06

301

302"""

303 ACCESS_ALLOWED_CALLBACK_ACE as described in 2.4.4.6

304 https://msdn.microsoft.com/en-us/library/cc230287.aspx

305"""

306 class ACCESS_ALLOWED_CALLBACK_ACE(Structure):

307 ACE_TYPE = 0x09

308 structure = (

309 ('Mask', ':', ACCESS_MASK),

310 ('Sid', ':', LDAP_SID),

311 ('ApplicationData', ':')

312 )

313

314"""

315 ACCESS_DENIED_OBJECT_ACE as described in 2.4.4.7

316 https://msdn.microsoft.com/en-us/library/cc230292.aspx

317 Structure is identical to ACCESS_ALLOWED_CALLBACK_ACE

318"""

319 class ACCESS_DENIED_CALLBACK_ACE(ACCESS_ALLOWED_CALLBACK_ACE):

320 ACE_TYPE = 0x0A

321

322"""

323 ACCESS_ALLOWED_CALLBACK_OBJECT_ACE as described in 2.4.4.8

324 https://msdn.microsoft.com/en-us/library/cc230288.aspx

325"""

326 class 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 )

340

341"""

342 ACCESS_DENIED_CALLBACK_OBJECT_ACE as described in 2.4.4.7

343 https://msdn.microsoft.com/en-us/library/cc230292.aspx

344 Structure is identical to ACCESS_ALLOWED_OBJECT_OBJECT_ACE

345"""

346 class ACCESS_DENIED_CALLBACK_OBJECT_ACE(ACCESS_ALLOWED_CALLBACK_OBJECT_ACE):

347 ACE_TYPE = 0x0C

348

349"""

350 SYSTEM_AUDIT_ACE as described in 2.4.4.10

351 https://msdn.microsoft.com/en-us/library/cc230376.aspx

352 Structure is identical to ACCESS_ALLOWED_ACE

353"""

354 class SYSTEM_AUDIT_ACE(ACCESS_ALLOWED_ACE):

355 ACE_TYPE = 0x02

356

357

358"""

359 SYSTEM_AUDIT_OBJECT_ACE as described in 2.4.4.11

360 https://msdn.microsoft.com/en-us/library/gg750298.aspx

361 Structure is identical to ACCESS_ALLOWED_CALLBACK_OBJECT_ACE

362"""

363 class SYSTEM_AUDIT_OBJECT_ACE(ACCESS_ALLOWED_CALLBACK_OBJECT_ACE):

364 ACE_TYPE = 0x07

365

366

367"""

368 SYSTEM_AUDIT_CALLBACK_ACE as described in 2.4.4.12

369 https://msdn.microsoft.com/en-us/library/cc230377.aspx

370 Structure is identical to ACCESS_ALLOWED_CALLBACK_ACE

371"""

372 class SYSTEM_AUDIT_CALLBACK_ACE(ACCESS_ALLOWED_CALLBACK_ACE):

373 ACE_TYPE = 0x0D

374

375"""

376 SYSTEM_AUDIT_CALLBACK_ACE as described in 2.4.4.13

377 https://msdn.microsoft.com/en-us/library/cc230379.aspx

378 Structure is identical to ACCESS_ALLOWED_ACE, but with custom masks and meanings.

379 Lets keep it separate for now

380"""

381 class SYSTEM_MANDATORY_LABEL_ACE(Structure):

382 ACE_TYPE = 0x11

383 structure = (

384 ('Mask', ':', ACCESS_MASK),

385 ('Sid', ':', LDAP_SID)

386 )

387

388"""

389 SYSTEM_AUDIT_CALLBACK_ACE as described in 2.4.4.14

390 https://msdn.microsoft.com/en-us/library/cc230378.aspx

391 Structure is identical to ACCESS_ALLOWED_CALLBACK_OBJECT_ACE

392"""

393 class SYSTEM_AUDIT_CALLBACK_OBJECT_ACE(ACCESS_ALLOWED_CALLBACK_OBJECT_ACE):

394 ACE_TYPE = 0x0F

395

396"""

397 SYSTEM_RESOURCE_ATTRIBUTE_ACE as described in 2.4.4.15

398 https://msdn.microsoft.com/en-us/library/hh877837.aspx

399 Structure is identical to ACCESS_ALLOWED_CALLBACK_ACE

400 The application data however is encoded in CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1

401 format as described in section 2.4.10.1

402 Todo: implement this substructure if needed

403"""

404 class SYSTEM_RESOURCE_ATTRIBUTE_ACE(ACCESS_ALLOWED_CALLBACK_ACE):

405 ACE_TYPE = 0x12

406

407

408"""

409 SYSTEM_SCOPED_POLICY_ID_ACE as described in 2.4.4.16

410 https://msdn.microsoft.com/en-us/library/hh877846.aspx

411 Structure is identical to ACCESS_ALLOWED_ACE

412 The Sid data MUST match a CAPID of a CentralAccessPolicy

413 contained in the CentralAccessPoliciesList

414 Todo: implement this substructure if needed

415 Also the ACCESS_MASK must always be 0

416"""

417 class SYSTEM_SCOPED_POLICY_ID_ACE(ACCESS_ALLOWED_ACE):

418 ACE_TYPE = 0x13

419

420# All the ACE types in a list

421 ACE_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]

438

439# A dict of all the ACE types indexed by their type number

440 ACE_TYPE_MAP = {ace.ACE_TYPE: ace for ace in ACE_TYPES}

441

442"""

443 ACL as described in 2.4.5

444 https://msdn.microsoft.com/en-us/library/cc230297.aspx

445"""

446 class 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 )

457

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

469

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

480

481"""

482 objectClass mapping to GUID for some common classes (index is the ldapDisplayName).

483 Reference:

484 https://msdn.microsoft.com/en-us/library/ms680938(v=vs.85).aspx

485 Can also be queried from the Schema

486"""

487 OBJECTTYPE_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}