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

15 from struct import unpack, pack

16 from impacket.structure import Structure

17

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.

23 RECALC_ACE_SIZE = True

24

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

26 class LDAP_SID_IDENTIFIER_AUTHORITY(Structure):

27 structure = (

28 ('Value','6s'),

29 )

30

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

39

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

45

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

55

56"""

57 Self-relative security descriptor as described in 2.4.6

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

59"""

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

74

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

84

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

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

87 else:

88 self['GroupSid'] = b''

89

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

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

92 else:

93 self['Sacl'] = b''

94

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

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

97 else:

98 self['Sacl'] = b''

99

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

111

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

113 self['OffsetDacl'] = headerlen + datalen

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

115 else:

116 self['OffsetDacl'] = 0

117

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

119 self['OffsetOwner'] = headerlen + datalen

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

121 else:

122 self['OffsetOwner'] = 0

123

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)

130

131"""

132 ACE as described in 2.4.4

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

134"""

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

144

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 )

160

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

167

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

181

182 def hasFlag(self, flag):

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

184

185"""

186 ACCESS_MASK as described in 2.4.3

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

188"""

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

202

203 structure = (

204 ('Mask', '<L'),

205 )

206

207 def hasPriv(self, priv):

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

209

210 def setPriv(self, priv):

211 self['Mask'] |= priv

212

213 def removePriv(self, priv):

214 self['Mask'] ^= priv

215

216"""

217 ACCESS_ALLOWED_ACE as described in 2.4.4.2

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

219"""

220 class ACCESS_ALLOWED_ACE(Structure):

221 ACE_TYPE = 0x00

222 structure = (

223 ('Mask', ':', ACCESS_MASK),

224 ('Sid', ':', LDAP_SID)

225 )

226

227"""

228 ACCESS_ALLOWED_OBJECT_ACE as described in 2.4.4.3

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

230"""

231 class ACCESS_ALLOWED_OBJECT_ACE(Structure):

232 ACE_TYPE = 0x05

233

234 # Flag contstants

235 ACE_OBJECT_TYPE_PRESENT = 0x01

236 ACE_INHERITED_OBJECT_TYPE_PRESENT = 0x02

237

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

247

248

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 )

260

261 @staticmethod

262 def checkInheritedObjectType(flags):

263 if flags & ACCESS_ALLOWED_OBJECT_ACE.ACE_INHERITED_OBJECT_TYPE_PRESENT:

264 return 16

265 return 0

266

267 @staticmethod

268 def checkObjectType(flags):

269 if flags & ACCESS_ALLOWED_OBJECT_ACE.ACE_OBJECT_TYPE_PRESENT:

270 return 16

271 return 0

272

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)

280

281 def hasFlag(self, flag):

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

283

284"""

285 ACCESS_DENIED_ACE as described in 2.4.4.4

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

287 Structure is identical to ACCESS_ALLOWED_ACE

288"""

289 class ACCESS_DENIED_ACE(ACCESS_ALLOWED_ACE):

290 ACE_TYPE = 0x01

291

292"""

293 ACCESS_DENIED_OBJECT_ACE as described in 2.4.4.5

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

295 Structure is identical to ACCESS_ALLOWED_OBJECT_ACE

296"""

297 class ACCESS_DENIED_OBJECT_ACE(ACCESS_ALLOWED_OBJECT_ACE):

298 ACE_TYPE = 0x06

299

300"""

301 ACCESS_ALLOWED_CALLBACK_ACE as described in 2.4.4.6

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

303"""

304 class ACCESS_ALLOWED_CALLBACK_ACE(Structure):

305 ACE_TYPE = 0x09

306 structure = (

307 ('Mask', ':', ACCESS_MASK),

308 ('Sid', ':', LDAP_SID),

309 ('ApplicationData', ':')

310 )

311

312"""

313 ACCESS_DENIED_OBJECT_ACE as described in 2.4.4.7

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

315 Structure is identical to ACCESS_ALLOWED_CALLBACK_ACE

316"""

317 class ACCESS_DENIED_CALLBACK_ACE(ACCESS_ALLOWED_CALLBACK_ACE):

318 ACE_TYPE = 0x0A

319

320"""

321 ACCESS_ALLOWED_CALLBACK_OBJECT_ACE as described in 2.4.4.8

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

323"""

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

338

339"""

340 ACCESS_DENIED_CALLBACK_OBJECT_ACE as described in 2.4.4.7

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

342 Structure is identical to ACCESS_ALLOWED_OBJECT_OBJECT_ACE

343"""

344 class ACCESS_DENIED_CALLBACK_OBJECT_ACE(ACCESS_ALLOWED_CALLBACK_OBJECT_ACE):

345 ACE_TYPE = 0x0C

346

347"""

348 SYSTEM_AUDIT_ACE as described in 2.4.4.10

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

350 Structure is identical to ACCESS_ALLOWED_ACE

351"""

352 class SYSTEM_AUDIT_ACE(ACCESS_ALLOWED_ACE):

353 ACE_TYPE = 0x02

354

355

356"""

357 SYSTEM_AUDIT_OBJECT_ACE as described in 2.4.4.11

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

359 Structure is identical to ACCESS_ALLOWED_CALLBACK_OBJECT_ACE

360"""

361 class SYSTEM_AUDIT_OBJECT_ACE(ACCESS_ALLOWED_CALLBACK_OBJECT_ACE):

362 ACE_TYPE = 0x07

363

364

365"""

366 SYSTEM_AUDIT_CALLBACK_ACE as described in 2.4.4.12

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

368 Structure is identical to ACCESS_ALLOWED_CALLBACK_ACE

369"""

370 class SYSTEM_AUDIT_CALLBACK_ACE(ACCESS_ALLOWED_CALLBACK_ACE):

371 ACE_TYPE = 0x0D

372

373"""

374 SYSTEM_AUDIT_CALLBACK_ACE as described in 2.4.4.13

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

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

377 Lets keep it separate for now

378"""

379 class SYSTEM_MANDATORY_LABEL_ACE(Structure):

380 ACE_TYPE = 0x11

381 structure = (

382 ('Mask', ':', ACCESS_MASK),

383 ('Sid', ':', LDAP_SID)

384 )

385

386"""

387 SYSTEM_AUDIT_CALLBACK_ACE as described in 2.4.4.14

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

389 Structure is identical to ACCESS_ALLOWED_CALLBACK_OBJECT_ACE

390"""

391 class SYSTEM_AUDIT_CALLBACK_OBJECT_ACE(ACCESS_ALLOWED_CALLBACK_OBJECT_ACE):

392 ACE_TYPE = 0x0F

393

394"""

395 SYSTEM_RESOURCE_ATTRIBUTE_ACE as described in 2.4.4.15

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

397 Structure is identical to ACCESS_ALLOWED_CALLBACK_ACE

398 The application data however is encoded in CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1

399 format as described in section 2.4.10.1

400 Todo: implement this substructure if needed

401"""

402 class SYSTEM_RESOURCE_ATTRIBUTE_ACE(ACCESS_ALLOWED_CALLBACK_ACE):

403 ACE_TYPE = 0x12

404

405

406"""

407 SYSTEM_SCOPED_POLICY_ID_ACE as described in 2.4.4.16

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

409 Structure is identical to ACCESS_ALLOWED_ACE

410 The Sid data MUST match a CAPID of a CentralAccessPolicy

411 contained in the CentralAccessPoliciesList

412 Todo: implement this substructure if needed

413 Also the ACCESS_MASK must always be 0

414"""

415 class SYSTEM_SCOPED_POLICY_ID_ACE(ACCESS_ALLOWED_ACE):

416 ACE_TYPE = 0x13

417

418# All the ACE types in a list

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

436

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

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

439

440"""

441 ACL as described in 2.4.5

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

443"""

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

455

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

467

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

478

479"""

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

481 Reference:

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

483 Can also be queried from the Schema

484"""

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