Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/scapy/layers/ldap.py: 52%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

488 statements  

1# SPDX-License-Identifier: GPL-2.0-only 

2# This file is part of Scapy 

3# See https://scapy.net/ for more information 

4# Copyright (C) Gabriel Potter <gabriel[]potter[]fr> 

5 

6""" 

7LDAP 

8 

9RFC 1777 - LDAP v2 

10RFC 4511 - LDAP v3 

11 

12Note: to mimic Microsoft Windows LDAP packets, you must set:: 

13 

14 conf.ASN1_default_long_size = 4 

15""" 

16 

17import collections 

18import ssl 

19import socket 

20import struct 

21import uuid 

22 

23from enum import Enum 

24 

25from scapy.arch import get_if_addr 

26from scapy.ansmachine import AnsweringMachine 

27from scapy.asn1.asn1 import ( 

28 ASN1_STRING, 

29 ASN1_Class, 

30 ASN1_Codecs, 

31) 

32from scapy.asn1.ber import BERcodec_STRING 

33from scapy.asn1fields import ( 

34 ASN1F_BOOLEAN, 

35 ASN1F_CHOICE, 

36 ASN1F_ENUMERATED, 

37 ASN1F_INTEGER, 

38 ASN1F_NULL, 

39 ASN1F_PACKET, 

40 ASN1F_SEQUENCE, 

41 ASN1F_SEQUENCE_OF, 

42 ASN1F_SET_OF, 

43 ASN1F_STRING, 

44 ASN1F_optional, 

45) 

46from scapy.asn1packet import ASN1_Packet 

47from scapy.config import conf 

48from scapy.error import log_runtime 

49from scapy.fields import ( 

50 FlagsField, 

51 ThreeBytesField, 

52) 

53from scapy.packet import ( 

54 Packet, 

55 bind_bottom_up, 

56 bind_layers, 

57) 

58from scapy.sendrecv import send 

59from scapy.supersocket import ( 

60 SimpleSocket, 

61 StreamSocket, 

62) 

63 

64from scapy.layers.dns import dns_resolve 

65from scapy.layers.inet import IP, TCP, UDP 

66from scapy.layers.inet6 import IPv6 

67from scapy.layers.gssapi import ( 

68 GSS_C_FLAGS, 

69 GSS_S_COMPLETE, 

70 GSSAPI_BLOB, 

71 GSSAPI_BLOB_SIGNATURE, 

72 SSP, 

73) 

74from scapy.layers.kerberos import ( 

75 _ASN1FString_PacketField, 

76) 

77from scapy.layers.netbios import NBTDatagram 

78from scapy.layers.smb import ( 

79 NETLOGON, 

80 NETLOGON_SAM_LOGON_RESPONSE_EX, 

81) 

82 

83 

84# Elements of protocol 

85# https://datatracker.ietf.org/doc/html/rfc1777#section-4 

86 

87LDAPString = ASN1F_STRING 

88LDAPOID = ASN1F_STRING 

89LDAPDN = LDAPString 

90RelativeLDAPDN = LDAPString 

91AttributeType = LDAPString 

92AttributeValue = ASN1F_STRING 

93URI = LDAPString 

94 

95 

96class AttributeValueAssertion(ASN1_Packet): 

97 ASN1_codec = ASN1_Codecs.BER 

98 ASN1_root = ASN1F_SEQUENCE( 

99 AttributeType("attributeType", "organizationName"), 

100 AttributeValue("attributeValue", ""), 

101 ) 

102 

103 

104class LDAPReferral(ASN1_Packet): 

105 ASN1_codec = ASN1_Codecs.BER 

106 ASN1_root = LDAPString("uri", "") 

107 

108 

109LDAPResult = ( 

110 ASN1F_ENUMERATED( 

111 "resultCode", 

112 0, 

113 { 

114 0: "success", 

115 1: "operationsError", 

116 2: "protocolError", 

117 3: "timeLimitExceeded", 

118 4: "sizeLimitExceeded", 

119 5: "compareFalse", 

120 6: "compareTrue", 

121 7: "authMethodNotSupported", 

122 8: "strongAuthRequired", 

123 10: "referral", 

124 11: "adminLimitExceeded", 

125 14: "saslBindInProgress", 

126 16: "noSuchAttribute", 

127 17: "undefinedAttributeType", 

128 18: "inappropriateMatching", 

129 19: "constraintViolation", 

130 20: "attributeOrValueExists", 

131 21: "invalidAttributeSyntax", 

132 32: "noSuchObject", 

133 33: "aliasProblem", 

134 34: "invalidDNSyntax", 

135 35: "isLeaf", 

136 36: "aliasDereferencingProblem", 

137 48: "inappropriateAuthentication", 

138 49: "invalidCredentials", 

139 50: "insufficientAccessRights", 

140 51: "busy", 

141 52: "unavailable", 

142 53: "unwillingToPerform", 

143 54: "loopDetect", 

144 64: "namingViolation", 

145 65: "objectClassViolation", 

146 66: "notAllowedOnNonLeaf", 

147 67: "notAllowedOnRDN", 

148 68: "entryAlreadyExists", 

149 69: "objectClassModsProhibited", 

150 70: "resultsTooLarge", # CLDAP 

151 80: "other", 

152 }, 

153 ), 

154 LDAPDN("matchedDN", ""), 

155 LDAPString("diagnosticMessage", ""), 

156 # LDAP v3 only 

157 ASN1F_optional(ASN1F_SEQUENCE_OF("referral", [], LDAPReferral, implicit_tag=0xA3)), 

158) 

159 

160 

161# ldap APPLICATION 

162 

163 

164class ASN1_Class_LDAP(ASN1_Class): 

165 name = "LDAP" 

166 # APPLICATION + CONSTRUCTED = 0x40 | 0x20 

167 BindRequest = 0x60 

168 BindResponse = 0x61 

169 UnbindRequest = 0x42 # not constructed 

170 SearchRequest = 0x63 

171 SearchResultEntry = 0x64 

172 SearchResultDone = 0x65 

173 SearchResultReference = 0x66 

174 ModifyRequest = 0x67 

175 ModifyResponse = 0x68 

176 AddRequest = 0x69 

177 AddResponse = 0x6A 

178 DelRequest = 0x6B 

179 DelResponse = 0x6C 

180 ModifyDNRequest = 0x6D 

181 ModifyDNResponse = 0x6E 

182 CompareRequest = 0x6F 

183 CompareResponse = 0x70 

184 AbandonRequest = 0x71 

185 ExtendedRequest = 0x72 

186 ExtendedResponse = 0x73 

187 

188 

189# Bind operation 

190# https://datatracker.ietf.org/doc/html/rfc1777#section-4.1 

191 

192 

193class ASN1_Class_LDAP_Authentication(ASN1_Class): 

194 name = "LDAP Authentication" 

195 # CONTEXT-SPECIFIC = 0x80 

196 simple = 0x80 

197 krbv42LDAP = 0x81 

198 krbv42DSA = 0x82 

199 sasl = 0xA3 # CONTEXT-SPECIFIC | CONSTRUCTED 

200 # [MS-ADTS] sect 5.1.1.1 

201 sicilyPackageDiscovery = 0x89 

202 sicilyNegotiate = 0x8A 

203 sicilyResponse = 0x8B 

204 

205 

206# simple 

207class LDAP_Authentication_simple(ASN1_STRING): 

208 tag = ASN1_Class_LDAP_Authentication.simple 

209 

210 

211class BERcodec_LDAP_Authentication_simple(BERcodec_STRING): 

212 tag = ASN1_Class_LDAP_Authentication.simple 

213 

214 

215class ASN1F_LDAP_Authentication_simple(ASN1F_STRING): 

216 ASN1_tag = ASN1_Class_LDAP_Authentication.simple 

217 

218 

219# krbv42LDAP 

220class LDAP_Authentication_krbv42LDAP(ASN1_STRING): 

221 tag = ASN1_Class_LDAP_Authentication.krbv42LDAP 

222 

223 

224class BERcodec_LDAP_Authentication_krbv42LDAP(BERcodec_STRING): 

225 tag = ASN1_Class_LDAP_Authentication.krbv42LDAP 

226 

227 

228class ASN1F_LDAP_Authentication_krbv42LDAP(ASN1F_STRING): 

229 ASN1_tag = ASN1_Class_LDAP_Authentication.krbv42LDAP 

230 

231 

232# krbv42DSA 

233class LDAP_Authentication_krbv42DSA(ASN1_STRING): 

234 tag = ASN1_Class_LDAP_Authentication.krbv42DSA 

235 

236 

237class BERcodec_LDAP_Authentication_krbv42DSA(BERcodec_STRING): 

238 tag = ASN1_Class_LDAP_Authentication.krbv42DSA 

239 

240 

241class ASN1F_LDAP_Authentication_krbv42DSA(ASN1F_STRING): 

242 ASN1_tag = ASN1_Class_LDAP_Authentication.krbv42DSA 

243 

244 

245# sicilyPackageDiscovery 

246class LDAP_Authentication_sicilyPackageDiscovery(ASN1_STRING): 

247 tag = ASN1_Class_LDAP_Authentication.sicilyPackageDiscovery 

248 

249 

250class BERcodec_LDAP_Authentication_sicilyPackageDiscovery(BERcodec_STRING): 

251 tag = ASN1_Class_LDAP_Authentication.sicilyPackageDiscovery 

252 

253 

254class ASN1F_LDAP_Authentication_sicilyPackageDiscovery(ASN1F_STRING): 

255 ASN1_tag = ASN1_Class_LDAP_Authentication.sicilyPackageDiscovery 

256 

257 

258# sicilyNegotiate 

259class LDAP_Authentication_sicilyNegotiate(ASN1_STRING): 

260 tag = ASN1_Class_LDAP_Authentication.sicilyNegotiate 

261 

262 

263class BERcodec_LDAP_Authentication_sicilyNegotiate(BERcodec_STRING): 

264 tag = ASN1_Class_LDAP_Authentication.sicilyNegotiate 

265 

266 

267class ASN1F_LDAP_Authentication_sicilyNegotiate(ASN1F_STRING): 

268 ASN1_tag = ASN1_Class_LDAP_Authentication.sicilyNegotiate 

269 

270 

271# sicilyResponse 

272class LDAP_Authentication_sicilyResponse(ASN1_STRING): 

273 tag = ASN1_Class_LDAP_Authentication.sicilyResponse 

274 

275 

276class BERcodec_LDAP_Authentication_sicilyResponse(BERcodec_STRING): 

277 tag = ASN1_Class_LDAP_Authentication.sicilyResponse 

278 

279 

280class ASN1F_LDAP_Authentication_sicilyResponse(ASN1F_STRING): 

281 ASN1_tag = ASN1_Class_LDAP_Authentication.sicilyResponse 

282 

283 

284_SASL_MECHANISMS = {b"GSS-SPNEGO": GSSAPI_BLOB, b"GSSAPI": GSSAPI_BLOB} 

285 

286 

287class _SaslCredentialsField(_ASN1FString_PacketField): 

288 def m2i(self, pkt, s): 

289 val = super(_SaslCredentialsField, self).m2i(pkt, s) 

290 if not val[0].val: 

291 return val 

292 if pkt.mechanism.val in _SASL_MECHANISMS: 

293 return ( 

294 _SASL_MECHANISMS[pkt.mechanism.val](val[0].val, _underlayer=pkt), 

295 val[1], 

296 ) 

297 return val 

298 

299 

300class LDAP_Authentication_SaslCredentials(ASN1_Packet): 

301 ASN1_codec = ASN1_Codecs.BER 

302 ASN1_root = ASN1F_SEQUENCE( 

303 LDAPString("mechanism", ""), 

304 ASN1F_optional( 

305 _SaslCredentialsField("credentials", ""), 

306 ), 

307 implicit_tag=ASN1_Class_LDAP_Authentication.sasl, 

308 ) 

309 

310 

311class LDAP_BindRequest(ASN1_Packet): 

312 ASN1_codec = ASN1_Codecs.BER 

313 ASN1_root = ASN1F_SEQUENCE( 

314 ASN1F_INTEGER("version", 3), 

315 LDAPDN("bind_name", ""), 

316 ASN1F_CHOICE( 

317 "authentication", 

318 None, 

319 ASN1F_LDAP_Authentication_simple, 

320 ASN1F_LDAP_Authentication_krbv42LDAP, 

321 ASN1F_LDAP_Authentication_krbv42DSA, 

322 LDAP_Authentication_SaslCredentials, 

323 ), 

324 implicit_tag=ASN1_Class_LDAP.BindRequest, 

325 ) 

326 

327 

328class LDAP_BindResponse(ASN1_Packet): 

329 ASN1_codec = ASN1_Codecs.BER 

330 ASN1_root = ASN1F_SEQUENCE( 

331 *( 

332 LDAPResult 

333 + ( 

334 ASN1F_optional( 

335 # For GSSAPI, the response is wrapped in 

336 # LDAP_Authentication_SaslCredentials 

337 ASN1F_STRING("serverSaslCredsWrap", "", implicit_tag=0xA7), 

338 ), 

339 ) 

340 + ( 

341 ASN1F_optional( 

342 ASN1F_STRING("serverSaslCreds", "", implicit_tag=0x87), 

343 ), 

344 ) 

345 ), 

346 implicit_tag=ASN1_Class_LDAP.BindResponse, 

347 ) 

348 

349 @property 

350 def serverCreds(self): 

351 """ 

352 serverCreds field in SicilyBindResponse 

353 """ 

354 return self.matchedDN.val 

355 

356 @serverCreds.setter 

357 def serverCreds(self, val): 

358 """ 

359 serverCreds field in SicilyBindResponse 

360 """ 

361 self.matchedDN = ASN1_STRING(val) 

362 

363 @property 

364 def serverSaslCredsData(self): 

365 """ 

366 Get serverSaslCreds or serverSaslCredsWrap depending on what's available 

367 """ 

368 if self.serverSaslCredsWrap and self.serverSaslCredsWrap.val: 

369 wrap = LDAP_Authentication_SaslCredentials(self.serverSaslCredsWrap.val) 

370 val = wrap.credentials 

371 if isinstance(val, ASN1_STRING): 

372 return val.val 

373 return bytes(val) 

374 elif self.serverSaslCreds and self.serverSaslCreds.val: 

375 return self.serverSaslCreds.val 

376 else: 

377 return None 

378 

379 

380# Unbind operation 

381# https://datatracker.ietf.org/doc/html/rfc1777#section-4.2 

382 

383 

384class LDAP_UnbindRequest(ASN1_Packet): 

385 ASN1_codec = ASN1_Codecs.BER 

386 ASN1_root = ASN1F_SEQUENCE( 

387 ASN1F_NULL("info", 0), 

388 implicit_tag=ASN1_Class_LDAP.UnbindRequest, 

389 ) 

390 

391 

392# Search operation 

393# https://datatracker.ietf.org/doc/html/rfc1777#section-4.3 

394 

395 

396class LDAP_SubstringFilterInitial(ASN1_Packet): 

397 ASN1_codec = ASN1_Codecs.BER 

398 ASN1_root = LDAPString("initial", "") 

399 

400 

401class LDAP_SubstringFilterAny(ASN1_Packet): 

402 ASN1_codec = ASN1_Codecs.BER 

403 ASN1_root = LDAPString("any", "") 

404 

405 

406class LDAP_SubstringFilterFinal(ASN1_Packet): 

407 ASN1_codec = ASN1_Codecs.BER 

408 ASN1_root = LDAPString("final", "") 

409 

410 

411class LDAP_SubstringFilterStr(ASN1_Packet): 

412 ASN1_codec = ASN1_Codecs.BER 

413 ASN1_root = ASN1F_CHOICE( 

414 "str", 

415 ASN1_STRING(""), 

416 ASN1F_PACKET( 

417 "initial", 

418 LDAP_SubstringFilterInitial(), 

419 LDAP_SubstringFilterInitial, 

420 implicit_tag=0x80, 

421 ), 

422 ASN1F_PACKET( 

423 "any", LDAP_SubstringFilterAny(), LDAP_SubstringFilterAny, implicit_tag=0x81 

424 ), 

425 ASN1F_PACKET( 

426 "final", 

427 LDAP_SubstringFilterFinal(), 

428 LDAP_SubstringFilterFinal, 

429 implicit_tag=0x82, 

430 ), 

431 ) 

432 

433 

434class LDAP_SubstringFilter(ASN1_Packet): 

435 ASN1_codec = ASN1_Codecs.BER 

436 ASN1_root = ASN1F_SEQUENCE( 

437 AttributeType("type", ""), 

438 ASN1F_SEQUENCE_OF("filters", [], LDAP_SubstringFilterStr), 

439 ) 

440 

441 

442_LDAP_Filter = lambda *args, **kwargs: LDAP_Filter(*args, **kwargs) 

443 

444 

445class LDAP_FilterAnd(ASN1_Packet): 

446 ASN1_codec = ASN1_Codecs.BER 

447 ASN1_root = ASN1F_SET_OF("and_", [], _LDAP_Filter) 

448 

449 

450class LDAP_FilterOr(ASN1_Packet): 

451 ASN1_codec = ASN1_Codecs.BER 

452 ASN1_root = ASN1F_SET_OF("or_", [], _LDAP_Filter) 

453 

454 

455class LDAP_FilterPresent(ASN1_Packet): 

456 ASN1_codec = ASN1_Codecs.BER 

457 ASN1_root = AttributeType("present", "") 

458 

459 

460class LDAP_FilterEqual(ASN1_Packet): 

461 ASN1_codec = ASN1_Codecs.BER 

462 ASN1_root = AttributeValueAssertion.ASN1_root 

463 

464 

465class LDAP_FilterGreaterOrEqual(ASN1_Packet): 

466 ASN1_codec = ASN1_Codecs.BER 

467 ASN1_root = AttributeValueAssertion.ASN1_root 

468 

469 

470class LDAP_FilterLesserOrEqual(ASN1_Packet): 

471 ASN1_codec = ASN1_Codecs.BER 

472 ASN1_root = AttributeValueAssertion.ASN1_root 

473 

474 

475class LDAP_FilterLessOrEqual(ASN1_Packet): 

476 ASN1_codec = ASN1_Codecs.BER 

477 ASN1_root = AttributeValueAssertion.ASN1_root 

478 

479 

480class LDAP_FilterApproxMatch(ASN1_Packet): 

481 ASN1_codec = ASN1_Codecs.BER 

482 ASN1_root = AttributeValueAssertion.ASN1_root 

483 

484 

485class ASN1_Class_LDAP_Filter(ASN1_Class): 

486 name = "LDAP Filter" 

487 # CONTEXT-SPECIFIC + CONSTRUCTED = 0x80 | 0x20 

488 And = 0xA0 

489 Or = 0xA1 

490 Not = 0xA2 

491 EqualityMatch = 0xA3 

492 Substrings = 0xA4 

493 GreaterOrEqual = 0xA5 

494 LessOrEqual = 0xA6 

495 Present = 0x87 # not constructed 

496 ApproxMatch = 0xA8 

497 

498 

499class LDAP_Filter(ASN1_Packet): 

500 ASN1_codec = ASN1_Codecs.BER 

501 ASN1_root = ASN1F_CHOICE( 

502 "filter", 

503 LDAP_FilterPresent(), 

504 ASN1F_PACKET( 

505 "and_", None, LDAP_FilterAnd, implicit_tag=ASN1_Class_LDAP_Filter.And 

506 ), 

507 ASN1F_PACKET( 

508 "or_", None, LDAP_FilterOr, implicit_tag=ASN1_Class_LDAP_Filter.Or 

509 ), 

510 ASN1F_PACKET( 

511 "not_", None, _LDAP_Filter, implicit_tag=ASN1_Class_LDAP_Filter.Not 

512 ), 

513 ASN1F_PACKET( 

514 "equalityMatch", 

515 None, 

516 LDAP_FilterEqual, 

517 implicit_tag=ASN1_Class_LDAP_Filter.EqualityMatch, 

518 ), 

519 ASN1F_PACKET( 

520 "substrings", 

521 None, 

522 LDAP_SubstringFilter, 

523 implicit_tag=ASN1_Class_LDAP_Filter.Substrings, 

524 ), 

525 ASN1F_PACKET( 

526 "greaterOrEqual", 

527 None, 

528 LDAP_FilterGreaterOrEqual, 

529 implicit_tag=ASN1_Class_LDAP_Filter.GreaterOrEqual, 

530 ), 

531 ASN1F_PACKET( 

532 "lessOrEqual", 

533 None, 

534 LDAP_FilterLessOrEqual, 

535 implicit_tag=ASN1_Class_LDAP_Filter.LessOrEqual, 

536 ), 

537 ASN1F_PACKET( 

538 "present", 

539 None, 

540 LDAP_FilterPresent, 

541 implicit_tag=ASN1_Class_LDAP_Filter.Present, 

542 ), 

543 ASN1F_PACKET( 

544 "approxMatch", 

545 None, 

546 LDAP_FilterApproxMatch, 

547 implicit_tag=ASN1_Class_LDAP_Filter.ApproxMatch, 

548 ), 

549 ) 

550 

551 

552class LDAP_SearchRequestAttribute(ASN1_Packet): 

553 ASN1_codec = ASN1_Codecs.BER 

554 ASN1_root = AttributeType("type", "") 

555 

556 

557class LDAP_SearchRequest(ASN1_Packet): 

558 ASN1_codec = ASN1_Codecs.BER 

559 ASN1_root = ASN1F_SEQUENCE( 

560 LDAPDN("baseObject", ""), 

561 ASN1F_ENUMERATED( 

562 "scope", 0, {0: "baseObject", 1: "singleLevel", 2: "wholeSubtree"} 

563 ), 

564 ASN1F_ENUMERATED( 

565 "derefAliases", 

566 0, 

567 { 

568 0: "neverDerefAliases", 

569 1: "derefInSearching", 

570 2: "derefFindingBaseObj", 

571 3: "derefAlways", 

572 }, 

573 ), 

574 ASN1F_INTEGER("sizeLimit", 0), 

575 ASN1F_INTEGER("timeLimit", 0), 

576 ASN1F_BOOLEAN("attrsOnly", False), 

577 ASN1F_PACKET("filter", LDAP_Filter(), LDAP_Filter), 

578 ASN1F_SEQUENCE_OF("attributes", [], LDAP_SearchRequestAttribute), 

579 implicit_tag=ASN1_Class_LDAP.SearchRequest, 

580 ) 

581 

582 

583class LDAP_SearchResponseEntryAttributeValue(ASN1_Packet): 

584 ASN1_codec = ASN1_Codecs.BER 

585 ASN1_root = AttributeValue("value", "") 

586 

587 

588class LDAP_SearchResponseEntryAttribute(ASN1_Packet): 

589 ASN1_codec = ASN1_Codecs.BER 

590 ASN1_root = ASN1F_SEQUENCE( 

591 AttributeType("type", ""), 

592 ASN1F_SET_OF("values", [], LDAP_SearchResponseEntryAttributeValue), 

593 ) 

594 

595 

596class LDAP_SearchResponseEntry(ASN1_Packet): 

597 ASN1_codec = ASN1_Codecs.BER 

598 ASN1_root = ASN1F_SEQUENCE( 

599 LDAPDN("objectName", ""), 

600 ASN1F_SEQUENCE_OF( 

601 "attributes", 

602 LDAP_SearchResponseEntryAttribute(), 

603 LDAP_SearchResponseEntryAttribute, 

604 ), 

605 implicit_tag=ASN1_Class_LDAP.SearchResultEntry, 

606 ) 

607 

608 

609class LDAP_SearchResponseResultDone(ASN1_Packet): 

610 ASN1_codec = ASN1_Codecs.BER 

611 ASN1_root = ASN1F_SEQUENCE( 

612 *LDAPResult, 

613 implicit_tag=ASN1_Class_LDAP.SearchResultDone, 

614 ) 

615 

616 

617class LDAP_AbandonRequest(ASN1_Packet): 

618 ASN1_codec = ASN1_Codecs.BER 

619 ASN1_root = ASN1F_SEQUENCE( 

620 ASN1F_INTEGER("messageID", 0), 

621 implicit_tag=ASN1_Class_LDAP.AbandonRequest, 

622 ) 

623 

624 

625# LDAP v3 

626 

627 

628class LDAP_Control(ASN1_Packet): 

629 ASN1_codec = ASN1_Codecs.BER 

630 ASN1_root = ASN1F_SEQUENCE( 

631 LDAPOID("controlType", ""), 

632 ASN1F_optional( 

633 ASN1F_BOOLEAN("criticality", False), 

634 ), 

635 ASN1F_optional(ASN1F_STRING("controlValue", "")), 

636 ) 

637 

638 

639# LDAP 

640 

641 

642class LDAP(ASN1_Packet): 

643 ASN1_codec = ASN1_Codecs.BER 

644 ASN1_root = ASN1F_SEQUENCE( 

645 ASN1F_INTEGER("messageID", 0), 

646 ASN1F_CHOICE( 

647 "protocolOp", 

648 LDAP_SearchRequest(), 

649 LDAP_BindRequest, 

650 LDAP_BindResponse, 

651 LDAP_SearchRequest, 

652 LDAP_SearchResponseEntry, 

653 LDAP_SearchResponseResultDone, 

654 LDAP_AbandonRequest, 

655 LDAP_UnbindRequest, 

656 ), 

657 # LDAP v3 only 

658 ASN1F_optional( 

659 ASN1F_SEQUENCE_OF("Controls", None, LDAP_Control, implicit_tag=0xA0) 

660 ), 

661 ) 

662 

663 def answers(self, other): 

664 return isinstance(other, LDAP) and other.messageID == self.messageID 

665 

666 def mysummary(self): 

667 return ( 

668 "%s(%s)" 

669 % ( 

670 self.protocolOp.__class__.__name__.replace("_", " "), 

671 self.messageID.val, 

672 ), 

673 [LDAP], 

674 ) 

675 

676 

677bind_layers(LDAP, LDAP) 

678 

679bind_bottom_up(TCP, LDAP, dport=389) 

680bind_bottom_up(TCP, LDAP, sport=389) 

681bind_bottom_up(TCP, LDAP, dport=3268) 

682bind_bottom_up(TCP, LDAP, sport=3268) 

683bind_layers(TCP, LDAP, sport=389, dport=389) 

684 

685# CLDAP - rfc1798 

686 

687 

688class CLDAP(ASN1_Packet): 

689 ASN1_codec = ASN1_Codecs.BER 

690 ASN1_root = ASN1F_SEQUENCE( 

691 LDAP.ASN1_root.seq[0], # messageID 

692 ASN1F_optional( 

693 LDAPDN("user", ""), 

694 ), 

695 LDAP.ASN1_root.seq[1], # protocolOp 

696 ) 

697 

698 def answers(self, other): 

699 return isinstance(other, CLDAP) and other.messageID == self.messageID 

700 

701 

702bind_layers(CLDAP, CLDAP) 

703 

704bind_bottom_up(UDP, CLDAP, dport=389) 

705bind_bottom_up(UDP, CLDAP, sport=389) 

706bind_layers(UDP, CLDAP, sport=389, dport=389) 

707 

708 

709# Small CLDAP Answering machine: [MS-ADTS] 6.3.3 - Ldap ping 

710 

711 

712class LdapPing_am(AnsweringMachine): 

713 function_name = "ldappingd" 

714 filter = "udp port 389 or 138" 

715 send_function = staticmethod(send) 

716 

717 def parse_options( 

718 self, 

719 NetbiosDomainName="DOMAIN", 

720 DomainGuid=uuid.UUID("192bc4b3-0085-4521-83fe-062913ef59f2"), 

721 DcSiteName="Default-First-Site-Name", 

722 NetbiosComputerName="SRV1", 

723 DnsForestName=None, 

724 DnsHostName=None, 

725 src_ip=None, 

726 src_ip6=None, 

727 ): 

728 self.NetbiosDomainName = NetbiosDomainName 

729 self.DnsForestName = DnsForestName or (NetbiosDomainName + ".LOCAL") 

730 self.DomainGuid = DomainGuid 

731 self.DcSiteName = DcSiteName 

732 self.NetbiosComputerName = NetbiosComputerName 

733 self.DnsHostName = DnsHostName or ( 

734 NetbiosComputerName + "." + self.DnsForestName 

735 ) 

736 self.src_ip = src_ip 

737 self.src_ip6 = src_ip6 

738 

739 def is_request(self, req): 

740 # [MS-ADTS] 6.3.3 - Example: 

741 # (&(DnsDomain=abcde.corp.microsoft.com)(Host=abcdefgh-dev)(User=abcdefgh- 

742 # dev$)(AAC=\80\00\00\00)(DomainGuid=\3b\b0\21\ca\d3\6d\d1\11\8a\7d\b8\df\b1\56\87\1f)(NtVer 

743 # =\06\00\00\00)) 

744 if NBTDatagram in req: 

745 # special case: mailslot ping 

746 from scapy.layers.smb import SMBMailslot_Write, NETLOGON_SAM_LOGON_REQUEST 

747 try: 

748 return ( 

749 SMBMailslot_Write in req and 

750 NETLOGON_SAM_LOGON_REQUEST in req.Data 

751 ) 

752 except AttributeError: 

753 return False 

754 if CLDAP not in req or not isinstance(req.protocolOp, LDAP_SearchRequest): 

755 return False 

756 req = req.protocolOp 

757 return ( 

758 req.attributes 

759 and req.attributes[0].type.val.lower() == b"netlogon" 

760 and req.filter 

761 and isinstance(req.filter.filter, LDAP_FilterAnd) 

762 and any( 

763 x.filter.attributeType.val == b"NtVer" for x in req.filter.filter.and_ 

764 ) 

765 ) 

766 

767 def make_reply(self, req): 

768 if NBTDatagram in req: 

769 # Special case 

770 return self.make_mailslot_ping_reply(req) 

771 if IPv6 in req: 

772 resp = IPv6(dst=req[IPv6].src, src=self.src_ip6 or req[IPv6].dst) 

773 else: 

774 resp = IP(dst=req[IP].src, src=self.src_ip or req[IP].dst) 

775 resp /= UDP(sport=req.dport, dport=req.sport) 

776 # get the DnsDomainName from the request 

777 try: 

778 DnsDomainName = next( 

779 x.filter.attributeValue.val 

780 for x in req.protocolOp.filter.filter.and_ 

781 if x.filter.attributeType.val == b"DnsDomain" 

782 ) 

783 except StopIteration: 

784 return 

785 return ( 

786 resp 

787 / CLDAP( 

788 protocolOp=LDAP_SearchResponseEntry( 

789 attributes=[ 

790 LDAP_SearchResponseEntryAttribute( 

791 values=[ 

792 LDAP_SearchResponseEntryAttributeValue( 

793 value=ASN1_STRING( 

794 val=bytes( 

795 NETLOGON_SAM_LOGON_RESPONSE_EX( 

796 # Mandatory fields 

797 DnsDomainName=DnsDomainName, 

798 NtVersion="V1+V5", 

799 LmNtToken=65535, 

800 Lm20Token=65535, 

801 # Below can be customized 

802 Flags=0x3F3FD, 

803 DomainGuid=self.DomainGuid, 

804 DnsForestName=self.DnsForestName, 

805 DnsHostName=self.DnsHostName, 

806 NetbiosDomainName=self.NetbiosDomainName, # noqa: E501 

807 NetbiosComputerName=self.NetbiosComputerName, # noqa: E501 

808 UserName=b".", 

809 DcSiteName=self.DcSiteName, 

810 ClientSiteName=self.DcSiteName, 

811 ) 

812 ) 

813 ) 

814 ) 

815 ], 

816 type=ASN1_STRING(b"Netlogon"), 

817 ) 

818 ], 

819 ), 

820 messageID=req.messageID, 

821 user=None, 

822 ) 

823 / CLDAP( 

824 protocolOp=LDAP_SearchResponseResultDone( 

825 referral=None, 

826 resultCode=0, 

827 ), 

828 messageID=req.messageID, 

829 user=None, 

830 ) 

831 ) 

832 

833 def make_mailslot_ping_reply(self, req): 

834 # type: (Packet) -> Packet 

835 from scapy.layers.smb import ( 

836 SMBMailslot_Write, 

837 SMB_Header, 

838 DcSockAddr, 

839 NETLOGON_SAM_LOGON_RESPONSE_EX, 

840 ) 

841 resp = IP(dst=req[IP].src) / UDP( 

842 sport=req.dport, 

843 dport=req.sport, 

844 ) 

845 address = self.src_ip or get_if_addr(self.optsniff.get("iface", conf.iface)) 

846 resp /= NBTDatagram( 

847 SourceName=req.DestinationName, 

848 SUFFIX1=req.SUFFIX2, 

849 DestinationName=req.SourceName, 

850 SUFFIX2=req.SUFFIX1, 

851 SourceIP=address, 

852 ) / SMB_Header() / SMBMailslot_Write( 

853 Name=req.Data.MailslotName, 

854 ) 

855 NetbiosDomainName = req.DestinationName.strip() 

856 resp.Data = NETLOGON_SAM_LOGON_RESPONSE_EX( 

857 # Mandatory fields 

858 NetbiosDomainName=NetbiosDomainName, 

859 DcSockAddr=DcSockAddr( 

860 sin_addr=address, 

861 ), 

862 NtVersion="V1+V5EX+V5EX_WITH_IP", 

863 LmNtToken=65535, 

864 Lm20Token=65535, 

865 # Below can be customized 

866 Flags=0x3F3FD, 

867 DomainGuid=self.DomainGuid, 

868 DnsForestName=self.DnsForestName, 

869 DnsDomainName=self.DnsForestName, 

870 DnsHostName=self.DnsHostName, 

871 NetbiosComputerName=self.NetbiosComputerName, 

872 DcSiteName=self.DcSiteName, 

873 ClientSiteName=self.DcSiteName, 

874 ) 

875 return resp 

876 

877 

878_located_dc = collections.namedtuple("LocatedDC", ["ip", "samlogon"]) 

879_dclocatorcache = conf.netcache.new_cache("dclocator", 600) 

880 

881 

882@conf.commands.register 

883def dclocator( 

884 realm, qtype="A", mode="ldap", port=None, timeout=1, NtVersion=None, debug=0 

885): 

886 """ 

887 Perform a DC Locator as per [MS-ADTS] sect 6.3.6 or RFC4120. 

888 

889 :param realm: the kerberos realm to locate 

890 :param mode: Detect if a server is up and joinable thanks to one of: 

891 

892 - 'nocheck': Do not check that servers are online. 

893 - 'ldap': Use the LDAP ping (CLDAP) per [MS-ADTS]. Default. 

894 This will however not work with MIT Kerberos servers. 

895 - 'connect': connect to specified port to test the connection. 

896 

897 :param mode: in connect mode, the port to connect to. (e.g. 88) 

898 :param debug: print debug logs 

899 

900 This is cached in conf.netcache.dclocator. 

901 """ 

902 if NtVersion is None: 

903 # Windows' default 

904 NtVersion = ( 

905 0x00000002 # V5 

906 | 0x00000004 # V5EX 

907 | 0x00000010 # V5EX_WITH_CLOSEST_SITE 

908 | 0x01000000 # AVOID_NT4EMUL 

909 | 0x20000000 # IP 

910 ) 

911 # Check cache 

912 cache_ident = ";".join([realm, qtype, mode, str(NtVersion)]).lower() 

913 if cache_ident in _dclocatorcache: 

914 return _dclocatorcache[cache_ident] 

915 # Perform DNS-Based discovery (6.3.6.1) 

916 # 1. SRV records 

917 qname = "_kerberos._tcp.dc._msdcs.%s" % realm.lower() 

918 if debug: 

919 log_runtime.info("DC Locator: requesting SRV for '%s' ..." % qname) 

920 try: 

921 hosts = [ 

922 x.target 

923 for x in dns_resolve( 

924 qname=qname, 

925 qtype="SRV", 

926 timeout=timeout, 

927 ) 

928 ] 

929 except TimeoutError: 

930 raise TimeoutError("Resolution of %s timed out" % qname) 

931 if not hosts: 

932 raise ValueError("No DNS record found for %s" % qname) 

933 elif debug: 

934 log_runtime.info( 

935 "DC Locator: got %s. Resolving %s records ..." % (hosts, qtype) 

936 ) 

937 # 2. A records 

938 ips = [] 

939 for host in hosts: 

940 arec = dns_resolve( 

941 qname=host, 

942 qtype=qtype, 

943 timeout=timeout, 

944 ) 

945 if arec: 

946 ips.extend(x.rdata for x in arec) 

947 if not ips: 

948 raise ValueError("Could not get any %s records for %s" % (qtype, hosts)) 

949 elif debug: 

950 log_runtime.info("DC Locator: got %s . Mode: %s" % (ips, mode)) 

951 # Pick first online host. We have three options 

952 if mode == "nocheck": 

953 # Don't check anything. Not recommended 

954 return _located_dc(ips[0], None) 

955 elif mode == "connect": 

956 assert port is not None, "Must provide a port in connect mode !" 

957 # Compatibility with MIT Kerberos servers 

958 for ip in ips: # TODO: "addresses in weighted random order [RFC2782]" 

959 if debug: 

960 log_runtime.info("DC Locator: connecting to %s on %s ..." % (ip, port)) 

961 try: 

962 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 

963 sock.settimeout(timeout) 

964 sock.connect((ip, port)) 

965 # Success 

966 result = _located_dc(ip, None) 

967 # Cache 

968 _dclocatorcache[cache_ident] = result 

969 return result 

970 except OSError: 

971 # Host timed out, No route to host, etc. 

972 if debug: 

973 log_runtime.info("DC Locator: %s timed out." % ip) 

974 continue 

975 finally: 

976 sock.close() 

977 raise ValueError("No host was reachable on port %s among %s" % (port, ips)) 

978 elif mode == "ldap": 

979 # Real 'LDAP Ping' per [MS-ADTS] 

980 for ip in ips: # TODO: "addresses in weighted random order [RFC2782]" 

981 if debug: 

982 log_runtime.info("DC Locator: LDAP Ping %s on ..." % ip) 

983 try: 

984 sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) 

985 sock.settimeout(timeout) 

986 sock.connect((ip, 389)) 

987 sock = SimpleSocket(sock, CLDAP) 

988 pkt = sock.sr1( 

989 CLDAP( 

990 protocolOp=LDAP_SearchRequest( 

991 filter=LDAP_Filter( 

992 filter=LDAP_FilterAnd( 

993 and_=[ 

994 LDAP_Filter( 

995 filter=LDAP_FilterEqual( 

996 attributeType=ASN1_STRING(b"DnsDomain"), 

997 attributeValue=ASN1_STRING(realm), 

998 ) 

999 ), 

1000 LDAP_Filter( 

1001 filter=LDAP_FilterEqual( 

1002 attributeType=ASN1_STRING(b"NtVer"), 

1003 attributeValue=ASN1_STRING( 

1004 struct.pack("<I", NtVersion) 

1005 ), 

1006 ) 

1007 ), 

1008 ] 

1009 ) 

1010 ), 

1011 attributes=[ 

1012 LDAP_SearchRequestAttribute( 

1013 type=ASN1_STRING(b"Netlogon") 

1014 ) 

1015 ], 

1016 ), 

1017 user=None, 

1018 ), 

1019 timeout=timeout, 

1020 verbose=0, 

1021 ) 

1022 if pkt: 

1023 # Check if we have a search response 

1024 response = None 

1025 if isinstance(pkt.protocolOp, LDAP_SearchResponseEntry): 

1026 try: 

1027 response = next( 

1028 NETLOGON(x.values[0].value.val) 

1029 for x in pkt.protocolOp.attributes 

1030 if x.type.val == b"Netlogon" 

1031 ) 

1032 except StopIteration: 

1033 pass 

1034 result = _located_dc(ip, response) 

1035 # Cache 

1036 _dclocatorcache[cache_ident] = result 

1037 return result 

1038 except OSError: 

1039 # Host timed out, No route to host, etc. 

1040 if debug: 

1041 log_runtime.info("DC Locator: %s timed out." % ip) 

1042 continue 

1043 finally: 

1044 sock.close() 

1045 raise ValueError("No LDAP ping succeeded on any of %s. Try another mode?" % ips) 

1046 

1047 

1048##################### 

1049# Basic LDAP client # 

1050##################### 

1051 

1052 

1053class LDAP_BIND_MECHS(Enum): 

1054 NONE = "NONE" 

1055 SIMPLE = "SIMPLE" 

1056 SASL_GSSAPI = "GSSAPI" 

1057 SASL_GSS_SPNEGO = "GSS-SPNEGO" 

1058 SASL_EXTERNAL = "EXTERNAL" 

1059 SASL_DIGEST_MD5 = "DIGEST-MD5" 

1060 # [MS-ADTS] extension 

1061 SICILY = "SICILY" 

1062 

1063 

1064class LDAP_SASL_GSSAPI_SsfCap(Packet): 

1065 """ 

1066 RFC2222 sect 7.2.1 and 7.2.2 negotiate token 

1067 """ 

1068 

1069 fields_desc = [ 

1070 FlagsField( 

1071 "supported_security_layers", 

1072 0, 

1073 -8, 

1074 { 

1075 # https://github.com/cyrusimap/cyrus-sasl/blob/7e2feaeeb2e37d38cb5fa957d0e8a599ced22612/plugins/gssapi.c#L221 

1076 0x01: "NONE", 

1077 0x02: "INTEGRITY", 

1078 0x04: "CONFIDENTIALITY", 

1079 }, 

1080 ), 

1081 ThreeBytesField("max_output_token_size", 0), 

1082 ] 

1083 

1084 

1085class LDAP_Client(object): 

1086 """ 

1087 A basic LDAP client 

1088 

1089 :param mech: one of LDAP_BIND_MECHS 

1090 :param ssl: whether to use LDAPS or not 

1091 :param ssp: the SSP object to use for binding 

1092 

1093 :param sign: request signing when binding 

1094 :param encrypt: request encryption when binding 

1095 

1096 Example 1 - SICILY - NTLM:: 

1097 

1098 ssp = NTLMSSP(UPN="Administrator", PASSWORD="Password1!") 

1099 client = LDAP_Client( 

1100 LDAP_BIND_MECHS.SICILY, 

1101 ssp=ssp, 

1102 ) 

1103 client.connect("192.168.0.100") 

1104 client.bind() 

1105 

1106 Example 2 - SASL_GSSAPI - Kerberos:: 

1107 

1108 ssp = KerberosSSP(UPN="Administrator@domain.local", PASSWORD="Password1!", 

1109 SPN="ldap/dc1.domain.local") 

1110 client = LDAP_Client( 

1111 LDAP_BIND_MECHS.SASL_GSSAPI, 

1112 ssp=ssp, 

1113 ) 

1114 client.connect("192.168.0.100") 

1115 client.bind() 

1116 

1117 Example 3 - SASL_GSS_SPNEGO - NTLM / Kerberos:: 

1118 

1119 ssp = SPNEGOSSP([ 

1120 NTLMSSP(UPN="Administrator", PASSWORD="Password1!"), 

1121 KerberosSSP(UPN="Administrator@domain.local", PASSWORD="Password1!", 

1122 SPN="ldap/dc1.domain.local"), 

1123 ]) 

1124 client = LDAP_Client( 

1125 LDAP_BIND_MECHS.SASL_GSS_SPNEGO, 

1126 ssp=ssp, 

1127 ) 

1128 client.connect("192.168.0.100") 

1129 client.bind() 

1130 

1131 Example 4 - Simple bind:: 

1132 

1133 client = LDAP_Client(LDAP_BIND_MECHS.SIMPLE) 

1134 client.connect("192.168.0.100") 

1135 client.bind(simple_username="Administrator", 

1136 simple_password="Password1!") 

1137 """ 

1138 

1139 def __init__( 

1140 self, 

1141 mech, 

1142 verb=True, 

1143 ssl=False, 

1144 sslcontext=None, 

1145 ssp=None, 

1146 sign=False, 

1147 encrypt=False, 

1148 ): 

1149 self.sock = None 

1150 self.mech = mech 

1151 self.verb = verb 

1152 self.ssl = ssl 

1153 self.sslcontext = sslcontext 

1154 self.ssp = ssp # type: SSP 

1155 assert isinstance(mech, LDAP_BIND_MECHS) 

1156 if mech == LDAP_BIND_MECHS.SASL_GSSAPI: 

1157 from scapy.layers.kerberos import KerberosSSP 

1158 

1159 if not isinstance(self.ssp, KerberosSSP): 

1160 raise ValueError("Only raw KerberosSSP is supported with SASL_GSSAPI !") 

1161 elif mech == LDAP_BIND_MECHS.SASL_GSS_SPNEGO: 

1162 from scapy.layers.spnego import SPNEGOSSP 

1163 

1164 if not isinstance(self.ssp, SPNEGOSSP): 

1165 raise ValueError("Only SPNEGOSSP is supported with SASL_GSS_SPNEGO !") 

1166 elif mech == LDAP_BIND_MECHS.SICILY: 

1167 from scapy.layers.ntlm import NTLMSSP 

1168 

1169 if not isinstance(self.ssp, NTLMSSP): 

1170 raise ValueError("Only raw NTLMSSP is supported with SICILY !") 

1171 if self.ssp is not None and mech in [ 

1172 LDAP_BIND_MECHS.NONE, 

1173 LDAP_BIND_MECHS.SIMPLE, 

1174 ]: 

1175 raise ValueError("%s cannot be used with a ssp !" % mech.value) 

1176 self.sspcontext = None 

1177 self.sign = sign 

1178 self.encrypt = encrypt 

1179 self.messageID = 0 

1180 

1181 def connect(self, ip, port=None, timeout=5): 

1182 """ 

1183 Initiate a connection 

1184 """ 

1185 if port is None: 

1186 if self.ssl: 

1187 port = 636 

1188 else: 

1189 port = 389 

1190 sock = socket.socket() 

1191 sock.settimeout(timeout) 

1192 if self.verb: 

1193 print( 

1194 "\u2503 Connecting to %s on port %s%s..." 

1195 % ( 

1196 ip, 

1197 port, 

1198 " with SSL" if self.ssl else "", 

1199 ) 

1200 ) 

1201 sock.connect((ip, port)) 

1202 if self.verb: 

1203 print( 

1204 conf.color_theme.green( 

1205 "\u2514 Connected from %s" % repr(sock.getsockname()) 

1206 ) 

1207 ) 

1208 if self.ssl: 

1209 if self.sslcontext is None: 

1210 context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT) 

1211 context.check_hostname = False 

1212 context.verify_mode = ssl.CERT_NONE 

1213 else: 

1214 context = self.sslcontext 

1215 sock = context.wrap_socket(sock) 

1216 self.sock = StreamSocket(sock, LDAP) 

1217 

1218 def sr1(self, protocolOp, controls=None, **kwargs): 

1219 self.messageID += 1 

1220 if self.verb: 

1221 print(conf.color_theme.opening(">> %s" % protocolOp.__class__.__name__)) 

1222 resp = self.sock.sr1( 

1223 LDAP( 

1224 messageID=self.messageID, 

1225 protocolOp=protocolOp, 

1226 Controls=controls, 

1227 ), 

1228 verbose=0, 

1229 **kwargs, 

1230 ) 

1231 if self.verb: 

1232 print( 

1233 conf.color_theme.success( 

1234 "<< %s" 

1235 % ( 

1236 resp.protocolOp.__class__.__name__ 

1237 if LDAP in resp 

1238 else resp.__class__.__name__ 

1239 ) 

1240 ) 

1241 ) 

1242 return resp 

1243 

1244 def bind(self, simple_username=None, simple_password=None): 

1245 """ 

1246 Send Bind request. 

1247 This acts differently based on the :mech: provided during initialization. 

1248 """ 

1249 if self.mech == LDAP_BIND_MECHS.SIMPLE: 

1250 # Simple binding 

1251 resp = self.sr1( 

1252 LDAP_BindRequest( 

1253 bind_name=ASN1_STRING(simple_username or ""), 

1254 authentication=LDAP_Authentication_simple( 

1255 simple_password or "", 

1256 ), 

1257 ) 

1258 ) 

1259 if ( 

1260 LDAP not in resp 

1261 or not isinstance(resp.protocolOp, LDAP_BindResponse) 

1262 or resp.protocolOp.resultCode != 0 

1263 ): 

1264 if self.verb: 

1265 resp.show() 

1266 raise RuntimeError("LDAP simple bind failed !") 

1267 elif self.mech == LDAP_BIND_MECHS.SICILY: 

1268 # [MS-ADTS] sect 5.1.1.1.3 

1269 # 1. Package Discovery 

1270 resp = self.sr1( 

1271 LDAP_BindRequest( 

1272 bind_name=ASN1_STRING(b""), 

1273 authentication=LDAP_Authentication_sicilyPackageDiscovery(b""), 

1274 ) 

1275 ) 

1276 if resp.protocolOp.resultCode != 0: 

1277 resp.show() 

1278 raise RuntimeError("Sicily package discovery failed !") 

1279 # 2. First exchange: Negotiate 

1280 self.sspcontext, token, status = self.ssp.GSS_Init_sec_context( 

1281 self.sspcontext, 

1282 req_flags=( 

1283 GSS_C_FLAGS.GSS_C_REPLAY_FLAG 

1284 | GSS_C_FLAGS.GSS_C_SEQUENCE_FLAG 

1285 | GSS_C_FLAGS.GSS_C_MUTUAL_FLAG 

1286 | (GSS_C_FLAGS.GSS_C_INTEG_FLAG if self.sign else 0) 

1287 | (GSS_C_FLAGS.GSS_C_CONF_FLAG if self.encrypt else 0) 

1288 ), 

1289 ) 

1290 resp = self.sr1( 

1291 LDAP_BindRequest( 

1292 bind_name=ASN1_STRING(b"NTLM"), 

1293 authentication=LDAP_Authentication_sicilyNegotiate( 

1294 bytes(token), 

1295 ), 

1296 ) 

1297 ) 

1298 val = resp.protocolOp.serverCreds 

1299 if not val: 

1300 resp.show() 

1301 raise RuntimeError("Sicily negotiate failed !") 

1302 # 3. Second exchange: Response 

1303 self.sspcontext, token, status = self.ssp.GSS_Init_sec_context( 

1304 self.sspcontext, GSSAPI_BLOB(val) 

1305 ) 

1306 resp = self.sr1( 

1307 LDAP_BindRequest( 

1308 bind_name=ASN1_STRING(b"NTLM"), 

1309 authentication=LDAP_Authentication_sicilyResponse( 

1310 bytes(token), 

1311 ), 

1312 ) 

1313 ) 

1314 if resp.protocolOp.resultCode != 0: 

1315 resp.show() 

1316 raise RuntimeError("Sicily response failed !") 

1317 elif self.mech in [ 

1318 LDAP_BIND_MECHS.SASL_GSS_SPNEGO, 

1319 LDAP_BIND_MECHS.SASL_GSSAPI, 

1320 ]: 

1321 # GSSAPI or SPNEGO 

1322 self.sspcontext, token, status = self.ssp.GSS_Init_sec_context( 

1323 self.sspcontext, 

1324 req_flags=( 

1325 GSS_C_FLAGS.GSS_C_REPLAY_FLAG 

1326 | GSS_C_FLAGS.GSS_C_SEQUENCE_FLAG 

1327 | GSS_C_FLAGS.GSS_C_MUTUAL_FLAG 

1328 | (GSS_C_FLAGS.GSS_C_INTEG_FLAG if self.sign else 0) 

1329 | (GSS_C_FLAGS.GSS_C_CONF_FLAG if self.encrypt else 0) 

1330 ), 

1331 ) 

1332 while token: 

1333 resp = self.sr1( 

1334 LDAP_BindRequest( 

1335 bind_name=ASN1_STRING(b""), 

1336 authentication=LDAP_Authentication_SaslCredentials( 

1337 mechanism=ASN1_STRING(self.mech.value), 

1338 credentials=ASN1_STRING(bytes(token)), 

1339 ), 

1340 ) 

1341 ) 

1342 if not isinstance(resp.protocolOp, LDAP_BindResponse): 

1343 if self.verb: 

1344 print("%s bind failed !" % self.mech.name) 

1345 resp.show() 

1346 return 

1347 val = resp.protocolOp.serverSaslCredsData 

1348 if not val: 

1349 status = resp.protocolOp.resultCode 

1350 break 

1351 self.sspcontext, token, status = self.ssp.GSS_Init_sec_context( 

1352 self.sspcontext, GSSAPI_BLOB(val) 

1353 ) 

1354 if status != GSS_S_COMPLETE: 

1355 raise RuntimeError("%s bind returned %s !" % (self.mech.name, status)) 

1356 elif self.mech == LDAP_BIND_MECHS.SASL_GSSAPI: 

1357 # GSSAPI has 2 extra exchanges 

1358 # https://datatracker.ietf.org/doc/html/rfc2222#section-7.2.1 

1359 resp = self.sr1( 

1360 LDAP_BindRequest( 

1361 bind_name=ASN1_STRING(b""), 

1362 authentication=LDAP_Authentication_SaslCredentials( 

1363 mechanism=ASN1_STRING(self.mech.value), 

1364 credentials=None, 

1365 ), 

1366 ) 

1367 ) 

1368 # Parse server-supported layers 

1369 saslOptions = GSSAPI_BLOB_SIGNATURE(resp.protocolOp.serverSaslCredsData) 

1370 saslOptions.show() 

1371 saslOptions = LDAP_SASL_GSSAPI_SsfCap( 

1372 self.ssp.GSS_Unwrap(self.sspcontext, b"", saslOptions) 

1373 ) 

1374 if self.sign and not saslOptions.supported_security_layers.INTEGRITY: 

1375 raise RuntimeError("GSSAPI SASL failed to negotiate INTEGRITY !") 

1376 if ( 

1377 self.encrypt 

1378 and not saslOptions.supported_security_layers.CONFIDENTIALITY 

1379 ): 

1380 raise RuntimeError("GSSAPI SASL failed to negotiate CONFIDENTIALITY !") 

1381 # Announce client-supported layers 

1382 saslOptions = LDAP_SASL_GSSAPI_SsfCap( 

1383 supported_security_layers=( 

1384 "NONE" 

1385 + ("+INTEGRITY" if self.sign else "") 

1386 + ("+CONFIDENTIALITY" if self.encrypt else "") 

1387 ), 

1388 max_output_token_size=0xA00000, 

1389 ) 

1390 resp = self.sr1( 

1391 LDAP_BindRequest( 

1392 bind_name=ASN1_STRING(b""), 

1393 authentication=LDAP_Authentication_SaslCredentials( 

1394 mechanism=ASN1_STRING(self.mech.value), 

1395 credentials=self.ssp.GSS_Wrap( 

1396 self.sspcontext, bytes(saslOptions), False 

1397 )[1], 

1398 ), 

1399 ) 

1400 ) 

1401 if resp.protocolOp.resultCode != 0: 

1402 resp.show() 

1403 raise RuntimeError( 

1404 "GSSAPI SASL failed to negotiate client security flags !" 

1405 ) 

1406 if self.verb: 

1407 print("%s bind succeeded !" % self.mech.name) 

1408 

1409 def close(self): 

1410 if self.verb: 

1411 print("X Connection closed\n") 

1412 self.sock.close()