Coverage for /root/GitHubProjects/impacket/impacket/tds.py: 14%

# Coverage for /root/GitHubProjects/impacket/impacket/tds.py : 14%

## 978 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# [MS-TDS] & [MC-SQLR] implementation.

11#

12# Author:

13# Alberto Solino (@agsolino)

14#

15# ToDo:

16# [ ] Add all the tokens left

17# [ ] parseRow should be rewritten and add support for all the SQL types in a

18# good way. Right now it just supports a few types.

19# [ ] printRows is crappy, just an easy way to print the rows. It should be

20# rewritten to output like a normal SQL client

21#

22

23 from __future__ import division

24 from __future__ import print_function

25 import struct

26 import socket

27 import select

28 import random

29 import binascii

30 import math

31 import datetime

32 import string

33

34 from impacket import ntlm, uuid, LOG

35 from impacket.structure import Structure

36

37 try:

38 from OpenSSL import SSL

39 except:

40 LOG.critical("pyOpenSSL is not installed, can't continue")

41 raise

42

43# We need to have a fake Logger to be compatible with the way Impact

44# prints information. Outside Impact it's just a print. Inside

45# we will receive the Impact logger instance to print row information

46# The rest it processed through the standard impacket logging mech.

47 class DummyPrint:

48 def logMessage(self,message):

49 if message == '\n':

50 print(message)

51 else:

52 print(message, end=' ')

53

54# MC-SQLR Constants and Structures

55 SQLR_PORT = 1434

56 SQLR_CLNT_BCAST_EX = 0x02

57 SQLR_CLNT_UCAST_EX = 0x03

58 SQLR_CLNT_UCAST_INST= 0x04

59 SQLR_CLNT_UCAST_DAC = 0x0f

60

61

62 class SQLR(Structure):

63 commonHdr = (

64 ('OpCode','B'),

65 )

66

67 class SQLR_UCAST_INST(SQLR):

68 structure = (

69 ('Instance',':')

70 )

71 def __init__(self, data = None):

72 SQLR.__init__(self,data)

73 if data is not None:

74 self['OpCode'] = SQLR_CLNT_UCAST_INST

75

76 class SQLR_UCAST_DAC(SQLR):

77 structure = (

78 ('Protocol', 'B=1'),

79 ('Instance', ':'),

80 )

81 def __init__(self, data = None):

82 SQLR.__init__(self,data)

83 if data is not None:

84 self['OpCode'] = SQLR_CLNT_UCAST_DAC

85

86 class SQLR_Response(SQLR):

87 structure = (

88 ('Size','<H'),

89 ('_Data','_-Data','self["Size"]'),

90 ('Data',':'),

91 )

92

93 class SQLErrorException(Exception):

94 pass

95

96# TDS Constants and Structures

97

98# TYPE constants

99 TDS_SQL_BATCH = 1

100 TDS_PRE_TDS_LOGIN = 2

101 TDS_RPC = 3

102 TDS_TABULAR = 4

103 TDS_ATTENTION = 6

104 TDS_BULK_LOAD_DATA = 7

105 TDS_TRANSACTION = 14

106 TDS_LOGIN7 = 16

107 TDS_SSPI = 17

108 TDS_PRE_LOGIN = 18

109

110# Status constants

111 TDS_STATUS_NORMAL = 0

112 TDS_STATUS_EOM = 1

113 TDS_STATUS_RESET_CONNECTION = 8

114 TDS_STATUS_RESET_SKIPTRANS = 16

115

116# Encryption

117 TDS_ENCRYPT_OFF = 0

118 TDS_ENCRYPT_ON = 1

119 TDS_ENCRYPT_NOT_SUP = 2

120 TDS_ENCRYPT_REQ = 3

121

122# Option 2 Flags

123 TDS_INTEGRATED_SECURITY_ON = 0x80

124 TDS_INIT_LANG_FATAL = 0x01

125 TDS_ODBC_ON = 0x02

126

127# Token Types

128 TDS_ALTMETADATA_TOKEN = 0x88

129 TDS_ALTROW_TOKEN = 0xD3

130 TDS_COLMETADATA_TOKEN = 0x81

131 TDS_COLINFO_TOKEN = 0xA5

132 TDS_DONE_TOKEN = 0xFD

133 TDS_DONEPROC_TOKEN = 0xFE

134 TDS_DONEINPROC_TOKEN = 0xFF

135 TDS_ENVCHANGE_TOKEN = 0xE3

136 TDS_ERROR_TOKEN = 0xAA

137 TDS_INFO_TOKEN = 0xAB

138 TDS_LOGINACK_TOKEN = 0xAD

139 TDS_NBCROW_TOKEN = 0xD2

140 TDS_OFFSET_TOKEN = 0x78

141 TDS_ORDER_TOKEN = 0xA9

142 TDS_RETURNSTATUS_TOKEN = 0x79

143 TDS_RETURNVALUE_TOKEN = 0xAC

144 TDS_ROW_TOKEN = 0xD1

145 TDS_SSPI_TOKEN = 0xED

146 TDS_TABNAME_TOKEN = 0xA4

147

148# ENVCHANGE Types

149 TDS_ENVCHANGE_DATABASE = 1

150 TDS_ENVCHANGE_LANGUAGE = 2

151 TDS_ENVCHANGE_CHARSET = 3

152 TDS_ENVCHANGE_PACKETSIZE = 4

153 TDS_ENVCHANGE_UNICODE = 5

154 TDS_ENVCHANGE_UNICODE_DS = 6

155 TDS_ENVCHANGE_COLLATION = 7

156 TDS_ENVCHANGE_TRANS_START = 8

157 TDS_ENVCHANGE_TRANS_COMMIT = 9

158 TDS_ENVCHANGE_ROLLBACK = 10

159 TDS_ENVCHANGE_DTC = 11

160

161# Column types

162# FIXED-LEN Data Types

163 TDS_NULL_TYPE = 0x1F

164 TDS_INT1TYPE = 0x30

165 TDS_BITTYPE = 0x32

166 TDS_INT2TYPE = 0x34

167 TDS_INT4TYPE = 0x38

168 TDS_DATETIM4TYPE = 0x3A

169 TDS_FLT4TYPE = 0x3B

170 TDS_MONEYTYPE = 0x3C

171 TDS_DATETIMETYPE = 0x3D

172 TDS_FLT8TYPE = 0x3E

173 TDS_MONEY4TYPE = 0x7A

174 TDS_INT8TYPE = 0x7F

175

176# VARIABLE-Len Data Types

177 TDS_GUIDTYPE = 0x24

178 TDS_INTNTYPE = 0x26

179 TDS_DECIMALTYPE = 0x37

180 TDS_NUMERICTYPE = 0x3F

181 TDS_BITNTYPE = 0x68

182 TDS_DECIMALNTYPE = 0x6A

183 TDS_NUMERICNTYPE = 0x6C

184 TDS_FLTNTYPE = 0x6D

185 TDS_MONEYNTYPE = 0x6E

186 TDS_DATETIMNTYPE = 0x6F

187 TDS_DATENTYPE = 0x28

188 TDS_TIMENTYPE = 0x29

189 TDS_DATETIME2NTYPE = 0x2A

190 TDS_DATETIMEOFFSETNTYPE = 0x2B

191 TDS_CHARTYPE = 0x2F

192 TDS_VARCHARTYPE = 0x27

193 TDS_BINARYTYPE = 0x2D

194 TDS_VARBINARYTYPE = 0x25

195 TDS_BIGVARBINTYPE = 0xA5

196 TDS_BIGVARCHRTYPE = 0xA7

197 TDS_BIGBINARYTYPE = 0xAD

198 TDS_BIGCHARTYPE = 0xAF

199 TDS_NVARCHARTYPE = 0xE7

200 TDS_NCHARTYPE = 0xEF

201 TDS_XMLTYPE = 0xF1

202 TDS_UDTTYPE = 0xF0

203 TDS_TEXTTYPE = 0x23

204 TDS_IMAGETYPE = 0x22

205 TDS_NTEXTTYPE = 0x63

206 TDS_SSVARIANTTYPE = 0x62

207

208 class TDSPacket(Structure):

209 structure = (

210 ('Type','<B'),

211 ('Status','<B=1'),

212 ('Length','>H=8+len(Data)'),

213 ('SPID','>H=0'),

214 ('PacketID','<B=0'),

215 ('Window','<B=0'),

216 ('Data',':'),

217 )

218

219 class TDS_PRELOGIN(Structure):

220 structure = (

221 ('VersionToken','>B=0'),

222 ('VersionOffset','>H'),

223 ('VersionLength','>H=len(self["Version"])'),

224 ('EncryptionToken','>B=0x1'),

225 ('EncryptionOffset','>H'),

226 ('EncryptionLength','>H=1'),

227 ('InstanceToken','>B=2'),

228 ('InstanceOffset','>H'),

229 ('InstanceLength','>H=len(self["Instance"])'),

230 ('ThreadIDToken','>B=3'),

231 ('ThreadIDOffset','>H'),

232 ('ThreadIDLength','>H=4'),

233 ('EndToken','>B=0xff'),

234 ('_Version','_-Version','self["VersionLength"]'),

235 ('Version',':'),

236 ('Encryption','B'),

237 ('_Instance','_-Instance','self["InstanceLength"]-1'),

238 ('Instance',':'),

239 ('ThreadID',':'),

240 )

241

242 def getData(self):

243 self['VersionOffset']=21

244 self['EncryptionOffset']=self['VersionOffset'] + len(self['Version'])

245 self['InstanceOffset']=self['EncryptionOffset'] + 1

246 self['ThreadIDOffset']=self['InstanceOffset'] + len(self['Instance'])

247 return Structure.getData(self)

248

249 class TDS_LOGIN(Structure):

250 structure = (

251 ('Length','<L=0'),

252 ('TDSVersion','>L=0x71'),

253 ('PacketSize','<L=32764'),

254 ('ClientProgVer','>L=7'),

255 ('ClientPID','<L=0'),

256 ('ConnectionID','<L=0'),

257 ('OptionFlags1','<B=0xe0'),

258 ('OptionFlags2','<B'),

259 ('TypeFlags','<B=0'),

260 ('OptionFlags3','<B=0'),

261 ('ClientTimeZone','<L=0'),

262 ('ClientLCID','<L=0'),

263 ('HostNameOffset','<H'),

264 ('HostNameLength','<H=len(self["HostName"])//2'),

265 ('UserNameOffset','<H=0'),

266 ('UserNameLength','<H=len(self["UserName"])//2'),

267 ('PasswordOffset','<H=0'),

268 ('PasswordLength','<H=len(self["Password"])//2'),

269 ('AppNameOffset','<H'),

270 ('AppNameLength','<H=len(self["AppName"])//2'),

271 ('ServerNameOffset','<H'),

272 ('ServerNameLength','<H=len(self["ServerName"])//2'),

273 ('UnusedOffset','<H=0'),

274 ('UnusedLength','<H=0'),

275 ('CltIntNameOffset','<H'),

276 ('CltIntNameLength','<H=len(self["CltIntName"])//2'),

277 ('LanguageOffset','<H=0'),

278 ('LanguageLength','<H=0'),

279 ('DatabaseOffset','<H=0'),

280 ('DatabaseLength','<H=len(self["Database"])//2'),

281 ('ClientID','6s=b"\x01\x02\x03\x04\x05\x06"'),

282 ('SSPIOffset','<H'),

283 ('SSPILength','<H=len(self["SSPI"])'),

284 ('AtchDBFileOffset','<H'),

285 ('AtchDBFileLength','<H=len(self["AtchDBFile"])//2'),

286 ('HostName',':'),

287 ('UserName',':'),

288 ('Password',':'),

289 ('AppName',':'),

290 ('ServerName',':'),

291 ('CltIntName',':'),

292 ('Database',':'),

293 ('SSPI',':'),

294 ('AtchDBFile',':'),

295 )

296 def __init__(self,data=None):

297 Structure.__init__(self,data)

298 if data is None:

299 self['UserName'] = ''

300 self['Password'] = ''

301 self['Database'] = ''

302 self['AtchDBFile'] = ''

303

304 def fromString(self, data):

305 Structure.fromString(self, data)

306 if self['HostNameLength'] > 0:

307 self['HostName'] = data[self['HostNameOffset']:][:self['HostNameLength']*2]

308

309 if self['UserNameLength'] > 0:

310 self['UserName'] = data[self['UserNameOffset']:][:self['UserNameLength']*2]

311

312 if self['PasswordLength'] > 0:

313 self['Password'] = data[self['PasswordOffset']:][:self['PasswordLength']*2]

314

315 if self['AppNameLength'] > 0:

316 self['AppName'] = data[self['AppNameOffset']:][:self['AppNameLength']*2]

317

318 if self['ServerNameLength'] > 0:

319 self['ServerName'] = data[self['ServerNameOffset']:][:self['ServerNameLength']*2]

320

321 if self['CltIntNameLength'] > 0:

322 self['CltIntName'] = data[self['CltIntNameOffset']:][:self['CltIntNameLength']*2]

323

324 if self['DatabaseLength'] > 0:

325 self['Database'] = data[self['DatabaseOffset']:][:self['DatabaseLength']*2]

326

327 if self['SSPILength'] > 0:

328 self['SSPI'] = data[self['SSPIOffset']:][:self['SSPILength']*2]

329

330 if self['AtchDBFileLength'] > 0:

331 self['AtchDBFile'] = data[self['AtchDBFileOffset']:][:self['AtchDBFileLength']*2]

332

333 def getData(self):

334 index = 36+50

335 self['HostNameOffset']= index

336

337 index += len(self['HostName'])

338

339 if self['UserName'] != '':

340 self['UserNameOffset'] = index

341 else:

342 self['UserNameOffset'] = 0

343

344 index += len(self['UserName'])

345

346 if self['Password'] != '':

347 self['PasswordOffset'] = index

348 else:

349 self['PasswordOffset'] = 0

350

351 index += len(self['Password'])

352

353 self['AppNameOffset']= index

354 self['ServerNameOffset']=self['AppNameOffset'] + len(self['AppName'])

355 self['CltIntNameOffset']=self['ServerNameOffset'] + len(self['ServerName'])

356 self['LanguageOffset']=self['CltIntNameOffset'] + len(self['CltIntName'])

357 self['DatabaseOffset']=self['LanguageOffset']

358 self['SSPIOffset']=self['DatabaseOffset'] + len(self['Database'])

359 self['AtchDBFileOffset']=self['SSPIOffset'] + len(self['SSPI'])

360 return Structure.getData(self)

361

362 class TDS_LOGIN_ACK(Structure):

363 structure = (

364 ('TokenType','<B'),

365 ('Length','<H'),

366 ('Interface','<B'),

367 ('TDSVersion','<L'),

368 ('ProgNameLen','<B'),

369 ('_ProgNameLen','_-ProgName','self["ProgNameLen"]*2'),

370 ('ProgName',':'),

371 ('MajorVer','<B'),

372 ('MinorVer','<B'),

373 ('BuildNumHi','<B'),

374 ('BuildNumLow','<B'),

375 )

376

377 class TDS_RETURNSTATUS(Structure):

378 structure = (

379 ('TokenType','<B'),

380 ('Value','<L'),

381 )

382

383 class TDS_INFO_ERROR(Structure):

384 structure = (

385 ('TokenType','<B'),

386 ('Length','<H'),

387 ('Number','<L'),

388 ('State','<B'),

389 ('Class','<B'),

390 ('MsgTextLen','<H'),

391 ('_MsgTextLen','_-MsgText','self["MsgTextLen"]*2'),

392 ('MsgText',':'),

393 ('ServerNameLen','<B'),

394 ('_ServerNameLen','_-ServerName','self["ServerNameLen"]*2'),

395 ('ServerName',':'),

396 ('ProcNameLen','<B'),

397 ('_ProcNameLen','_-ProcName','self["ProcNameLen"]*2'),

398 ('ProcName',':'),

399 ('LineNumber','<H'),

400 )

401

402 class TDS_ENVCHANGE(Structure):

403 structure = (

404 ('TokenType','<B'),

405 ('Length','<H=4+len(Data)'),

406 ('Type','<B'),

407 ('_Data','_-Data','self["Length"]-1'),

408 ('Data',':'),

409 )

410

411 class TDS_DONEINPROC(Structure):

412 structure = (

413 ('TokenType','<B'),

414 ('Status','<H'),

415 ('CurCmd','<H'),

416 ('DoneRowCount','<L'),

417 )

418

419 class TDS_ORDER(Structure):

420 structure = (

421 ('TokenType','<B'),

422 ('Length','<H'),

423 ('_Data','_-Data','self["Length"]'),

424 ('Data',':'),

425 )

426

427

428 class TDS_ENVCHANGE_VARCHAR(Structure):

429 structure = (

430 ('NewValueLen','<B=len(NewValue)'),

431 ('_NewValue','_-NewValue','self["NewValueLen"]*2'),

432 ('NewValue',':'),

433 ('OldValueLen','<B=len(OldValue)'),

434 ('_OldValue','_-OldValue','self["OldValueLen"]*2'),

435 ('OldValue',':'),

436 )

437

438 class TDS_ROW(Structure):

439 structure = (

440 ('TokenType','<B'),

441 ('Data',':'),

442 )

443

444 class TDS_DONE(Structure):

445 structure = (

446 ('TokenType','<B'),

447 ('Status','<H'),

448 ('CurCmd','<H'),

449 ('DoneRowCount','<L'),

450 )

451

452 class TDS_COLMETADATA(Structure):

453 structure = (

454 ('TokenType','<B'),

455 ('Count','<H'),

456 ('Data',':'),

457 )

458

459 class MSSQL:

460 def __init__(self, address, port=1433, rowsPrinter=DummyPrint()):

461 #self.packetSize = 32764

462 self.packetSize = 32763

463 self.server = address

464 self.port = port

465 self.socket = 0

466 self.replies = {}

467 self.colMeta = []

468 self.rows = []

469 self.currentDB = ''

470 self.COL_SEPARATOR = ' '

471 self.MAX_COL_LEN = 255

472 self.lastError = False

473 self.tlsSocket = None

474 self.__rowsPrinter = rowsPrinter

475

476 def getInstances(self, timeout = 5):

477 packet = SQLR()

478 packet['OpCode'] = SQLR_CLNT_UCAST_EX

479

480 # Open the connection

481 af, socktype, proto, canonname, sa = socket.getaddrinfo(self.server, SQLR_PORT, 0, socket.SOCK_DGRAM)[0]

482 s = socket.socket(af, socktype, proto)

483

484 s.sendto(packet.getData(), 0, ( self.server, SQLR_PORT ))

485 ready, _, _ = select.select([ s.fileno() ], [ ] , [ ], timeout)

486 if not ready:

487 return []

488 else:

489 data, _ = s.recvfrom(65536, 0)

490

491 s.close()

492 resp = SQLR_Response(data)

493

494 # Now parse the results

495 entries = resp['Data'].split(b';;')

496

497 # We don't want the last one, it's empty

498 entries.pop()

499

500 # the answer to send back

501 resp = []

502

503 for i, entry in enumerate(entries):

504 fields = entry.split(b';')

505 ret = {}

506 for j, field in enumerate(fields):

507 if (j & 0x1) == 0:

508 ret[field.decode('utf-8')] = fields[j+1].decode('utf-8')

509 resp.append(ret)

510

511 return resp

512

513

514 def preLogin(self):

515 prelogin = TDS_PRELOGIN()

516 prelogin['Version'] = b"\x08\x00\x01\x55\x00\x00"

517 #prelogin['Encryption'] = TDS_ENCRYPT_NOT_SUP

518 prelogin['Encryption'] = TDS_ENCRYPT_OFF

519 prelogin['ThreadID'] = struct.pack('<L',random.randint(0,65535))

520 prelogin['Instance'] = b'MSSQLServer\x00'

521

522 self.sendTDS(TDS_PRE_LOGIN, prelogin.getData(), 0)

523 tds = self.recvTDS()

524

525 return TDS_PRELOGIN(tds['Data'])

526

527 def encryptPassword(self, password ):

528 return bytes(bytearray([((x & 0x0f) << 4) + ((x & 0xf0) >> 4) ^ 0xa5 for x in bytearray(password)]))

529

530 def connect(self):

531 af, socktype, proto, canonname, sa = socket.getaddrinfo(self.server, self.port, 0, socket.SOCK_STREAM)[0]

532 sock = socket.socket(af, socktype, proto)

533

534 try:

535 sock.connect(sa)

536 except Exception:

537 #import traceback

538 #traceback.print_exc()

539 raise

540

541 self.socket = sock

542 return sock

543

544 def disconnect(self):

545 if self.socket:

546 return self.socket.close()

547

548 def setPacketSize(self, packetSize):

549 self.packetSize = packetSize

550

551 def getPacketSize(self):

552 return self.packetSize

553

554 def socketSendall(self,data):

555 if self.tlsSocket is None:

556 return self.socket.sendall(data)

557 else:

558 self.tlsSocket.sendall(data)

559 dd = self.tlsSocket.bio_read(self.packetSize)

560 return self.socket.sendall(dd)

561

562 def sendTDS(self, packetType, data, packetID = 1):

563 if (len(data)-8) > self.packetSize:

564 remaining = data[self.packetSize-8:]

565 tds = TDSPacket()

566 tds['Type'] = packetType

567 tds['Status'] = TDS_STATUS_NORMAL

568 tds['PacketID'] = packetID

569 tds['Data'] = data[:self.packetSize-8]

570 self.socketSendall(tds.getData())

571

572 while len(remaining) > (self.packetSize-8):

573 packetID += 1

574 tds['PacketID'] = packetID

575 tds['Data'] = remaining[:self.packetSize-8]

576 self.socketSendall(tds.getData())

577 remaining = remaining[self.packetSize-8:]

578 data = remaining

579 packetID+=1

580

581 tds = TDSPacket()

582 tds['Type'] = packetType

583 tds['Status'] = TDS_STATUS_EOM

584 tds['PacketID'] = packetID

585 tds['Data'] = data

586 self.socketSendall(tds.getData())

587

588 def socketRecv(self, packetSize):

589 data = self.socket.recv(packetSize)

590 if self.tlsSocket is not None:

591 dd = ''

592 self.tlsSocket.bio_write(data)

593 while True:

594 try:

595 dd += self.tlsSocket.read(packetSize)

596 except SSL.WantReadError:

597 data2 = self.socket.recv(packetSize - len(data) )

598 self.tlsSocket.bio_write(data2)

599 pass

600 else:

601 data = dd

602 break

603 return data

604

605 def recvTDS(self, packetSize = None):

606 # Do reassembly here

607 if packetSize is None:

608 packetSize = self.packetSize

609 packet = TDSPacket(self.socketRecv(packetSize))

610 status = packet['Status']

611 packetLen = packet['Length']-8

612 while packetLen > len(packet['Data']):

613 data = self.socketRecv(packetSize)

614 packet['Data'] += data

615

616 remaining = None

617 if packetLen < len(packet['Data']):

618 remaining = packet['Data'][packetLen:]

619 packet['Data'] = packet['Data'][:packetLen]

620

621 #print "REMAINING ",

622 #if remaining is None:

623 # print None

624 #else:

625 # print len(remaining)

626

627 while status != TDS_STATUS_EOM:

628 if remaining is not None:

629 tmpPacket = TDSPacket(remaining)

630 else:

631 tmpPacket = TDSPacket(self.socketRecv(packetSize))

632

633 packetLen = tmpPacket['Length'] - 8

634 while packetLen > len(tmpPacket['Data']):

635 data = self.socketRecv(packetSize)

636 tmpPacket['Data'] += data

637

638 remaining = None

639 if packetLen < len(tmpPacket['Data']):

640 remaining = tmpPacket['Data'][packetLen:]

641 tmpPacket['Data'] = tmpPacket['Data'][:packetLen]

642

643 status = tmpPacket['Status']

644 packet['Data'] += tmpPacket['Data']

645 packet['Length'] += tmpPacket['Length'] - 8

646

647 #print packet['Length']

648 return packet

649

650 def kerberosLogin(self, database, username, password='', domain='', hashes=None, aesKey='', kdcHost=None, TGT=None, TGS=None, useCache=True):

651

652 if hashes is not None:

653 lmhash, nthash = hashes.split(':')

654 lmhash = binascii.a2b_hex(lmhash)

655 nthash = binascii.a2b_hex(nthash)

656 else:

657 lmhash = ''

658 nthash = ''

659

660 resp = self.preLogin()

661 # Test this!

662 if resp['Encryption'] == TDS_ENCRYPT_REQ or resp['Encryption'] == TDS_ENCRYPT_OFF:

663 LOG.info("Encryption required, switching to TLS")

664

665 # Switching to TLS now

666 ctx = SSL.Context(SSL.TLSv1_METHOD)

667 ctx.set_cipher_list('RC4, AES256')

668 tls = SSL.Connection(ctx,None)

669 tls.set_connect_state()

670 while True:

671 try:

672 tls.do_handshake()

673 except SSL.WantReadError:

674 data = tls.bio_read(4096)

675 self.sendTDS(TDS_PRE_LOGIN, data,0)

676 tds = self.recvTDS()

677 tls.bio_write(tds['Data'])

678 else:

679 break

680

681 # SSL and TLS limitation: Secure Socket Layer (SSL) and its replacement,

682 # Transport Layer Security(TLS), limit data fragments to 16k in size.

683 self.packetSize = 16*1024-1

684 self.tlsSocket = tls

685

686

687 login = TDS_LOGIN()

688

689 login['HostName'] = (''.join([random.choice(string.ascii_letters) for _ in range(8)])).encode('utf-16le')

690 login['AppName'] = (''.join([random.choice(string.ascii_letters) for _ in range(8)])).encode('utf-16le')

691 login['ServerName'] = self.server.encode('utf-16le')

692 login['CltIntName'] = login['AppName']

693 login['ClientPID'] = random.randint(0,1024)

694 login['PacketSize'] = self.packetSize

695 if database is not None:

696 login['Database'] = database.encode('utf-16le')

697 login['OptionFlags2'] = TDS_INIT_LANG_FATAL | TDS_ODBC_ON

698

699 from impacket.spnego import SPNEGO_NegTokenInit, TypesMech

700 # Importing down here so pyasn1 is not required if kerberos is not used.

701 from impacket.krb5.asn1 import AP_REQ, Authenticator, TGS_REP, seq_set

702 from impacket.krb5.kerberosv5 import getKerberosTGT, getKerberosTGS, KerberosError

703 from impacket.krb5 import constants

704 from impacket.krb5.types import Principal, KerberosTime, Ticket

705 from pyasn1.codec.der import decoder, encoder

706 from pyasn1.type.univ import noValue

707 from impacket.krb5.ccache import CCache

708 import os

709 import datetime

710

711 if useCache is True:

712 try:

713 ccache = CCache.loadFile(os.getenv('KRB5CCNAME'))

714 except:

715 # No cache present

716 pass

717 else:

718 # retrieve domain information from CCache file if needed

719 if domain == '':

720 domain = ccache.principal.realm['data'].decode('utf-8')

721 LOG.debug('Domain retrieved from CCache: %s' % domain)

722

723 LOG.debug("Using Kerberos Cache: %s" % os.getenv('KRB5CCNAME'))

724 principal = 'MSSQLSvc/%s.%s:%d@%s' % (self.server.split('.')[0], domain, self.port, domain.upper())

725 creds = ccache.getCredential(principal)

726

727 if creds is not None:

728 TGS = creds.toTGS(principal)

729 LOG.debug('Using TGS from cache')

730 else:

731 # search for the port's instance name instead (instance name based SPN)

732 LOG.debug('Searching target\'s instances to look for port number %s' % self.port)

733 instances = self.getInstances()

734 instanceName = None

735 for i in instances:

736 try:

737 if int(i['tcp']) == self.port:

738 instanceName = i['InstanceName']

739 except:

740 pass

741

742 if instanceName:

743 principal = 'MSSQLSvc/%s.%s:%s@%s' % (self.server, domain, instanceName, domain.upper())

744 creds = ccache.getCredential(principal)

745

746 if creds is not None:

747 TGS = creds.toTGS(principal)

748 LOG.debug('Using TGS from cache')

749 else:

750 # Let's try for the TGT and go from there

751 principal = 'krbtgt/%s@%s' % (domain.upper(),domain.upper())

752 creds = ccache.getCredential(principal)

753 if creds is not None:

754 TGT = creds.toTGT()

755 LOG.debug('Using TGT from cache')

756 else:

757 LOG.debug("No valid credentials found in cache. ")

758

759 # retrieve user information from CCache file if needed

760 if username == '' and creds is not None:

761 username = creds['client'].prettyPrint().split(b'@')[0].decode('utf-8')

762 LOG.debug('Username retrieved from CCache: %s' % username)

763 elif username == '' and len(ccache.principal.components) > 0:

764 username = ccache.principal.components[0]['data'].decode('utf-8')

765 LOG.debug('Username retrieved from CCache: %s' % username)

766

767 # First of all, we need to get a TGT for the user

768 userName = Principal(username, type=constants.PrincipalNameType.NT_PRINCIPAL.value)

769 while True:

770 if TGT is None:

771 if TGS is None:

772 try:

773 tgt, cipher, oldSessionKey, sessionKey = getKerberosTGT(userName, password, domain, lmhash, nthash, aesKey, kdcHost)

774 except KerberosError as e:

775 if e.getErrorCode() == constants.ErrorCodes.KDC_ERR_ETYPE_NOSUPP.value:

776 # We might face this if the target does not support AES

777 # So, if that's the case we'll force using RC4 by converting

778 # the password to lm/nt hashes and hope for the best. If that's already

779 # done, byebye.

780 if lmhash == '' and nthash == '' and (aesKey == '' or aesKey is None) and TGT is None and TGS is None:

781 from impacket.ntlm import compute_lmhash, compute_nthash

782 LOG.debug('Got KDC_ERR_ETYPE_NOSUPP, fallback to RC4')

783 lmhash = compute_lmhash(password)

784 nthash = compute_nthash(password)

785 continue

786 else:

787 raise

788 else:

789 raise

790 else:

791 tgt = TGT['KDC_REP']

792 cipher = TGT['cipher']

793 sessionKey = TGT['sessionKey']

794

795 if TGS is None:

796 # From https://msdn.microsoft.com/en-us/library/ms191153.aspx?f=255&MSPPError=-2147217396

797 # Beginning with SQL Server 2008, the SPN format is changed in order to support Kerberos authentication

798 # on TCP/IP, named pipes, and shared memory. The supported SPN formats for named and default instances

799 # are as follows.

800 # Named instance

801 # MSSQLSvc/FQDN:[port | instancename], where:

802 # MSSQLSvc is the service that is being registered.

803 # FQDN is the fully qualified domain name of the server.

804 # port is the TCP port number.

805 # instancename is the name of the SQL Server instance.

806 serverName = Principal('MSSQLSvc/%s.%s:%d' % (self.server.split('.')[0], domain, self.port), type=constants.PrincipalNameType.NT_SRV_INST.value)

807 try:

808 tgs, cipher, oldSessionKey, sessionKey = getKerberosTGS(serverName, domain, kdcHost, tgt, cipher, sessionKey)

809 except KerberosError as e:

810 if e.getErrorCode() == constants.ErrorCodes.KDC_ERR_ETYPE_NOSUPP.value:

811 # We might face this if the target does not support AES

812 # So, if that's the case we'll force using RC4 by converting

813 # the password to lm/nt hashes and hope for the best. If that's already

814 # done, byebye.

815 if lmhash == '' and nthash == '' and (aesKey == '' or aesKey is None) and TGT is None and TGS is None:

816 from impacket.ntlm import compute_lmhash, compute_nthash

817 LOG.debug('Got KDC_ERR_ETYPE_NOSUPP, fallback to RC4')

818 lmhash = compute_lmhash(password)

819 nthash = compute_nthash(password)

820 else:

821 raise

822 else:

823 raise

824 else:

825 break

826 else:

827 tgs = TGS['KDC_REP']

828 cipher = TGS['cipher']

829 sessionKey = TGS['sessionKey']

830 break

831

832 # Let's build a NegTokenInit with a Kerberos REQ_AP

833

834 blob = SPNEGO_NegTokenInit()

835

836 # Kerberos

837 blob['MechTypes'] = [TypesMech['MS KRB5 - Microsoft Kerberos 5']]

838

839 # Let's extract the ticket from the TGS

840 tgs = decoder.decode(tgs, asn1Spec = TGS_REP())[0]

841 ticket = Ticket()

842 ticket.from_asn1(tgs['ticket'])

843

844 # Now let's build the AP_REQ

845 apReq = AP_REQ()

846 apReq['pvno'] = 5

847 apReq['msg-type'] = int(constants.ApplicationTagNumbers.AP_REQ.value)

848

849 opts = list()

850 apReq['ap-options'] = constants.encodeFlags(opts)

851 seq_set(apReq,'ticket', ticket.to_asn1)

852

853 authenticator = Authenticator()

854 authenticator['authenticator-vno'] = 5

855 authenticator['crealm'] = domain

856 seq_set(authenticator, 'cname', userName.components_to_asn1)

857 now = datetime.datetime.utcnow()

858

859 authenticator['cusec'] = now.microsecond

860 authenticator['ctime'] = KerberosTime.to_asn1(now)

861

862 encodedAuthenticator = encoder.encode(authenticator)

863

864 # Key Usage 11

865 # AP-REQ Authenticator (includes application authenticator

866 # subkey), encrypted with the application session key

867 # (Section 5.5.1)

868 encryptedEncodedAuthenticator = cipher.encrypt(sessionKey, 11, encodedAuthenticator, None)

869

870 apReq['authenticator'] = noValue

871 apReq['authenticator']['etype'] = cipher.enctype

872 apReq['authenticator']['cipher'] = encryptedEncodedAuthenticator

873

874 blob['MechToken'] = encoder.encode(apReq)

875

876 login['OptionFlags2'] |= TDS_INTEGRATED_SECURITY_ON

877

878 login['SSPI'] = blob.getData()

879 login['Length'] = len(login.getData())

880

881 # Send the NTLMSSP Negotiate or SQL Auth Packet

882 self.sendTDS(TDS_LOGIN7, login.getData())

883

884 # According to the specs, if encryption is not required, we must encrypt just

885 # the first Login packet :-o

886 if resp['Encryption'] == TDS_ENCRYPT_OFF:

887 self.tlsSocket = None

888

889 tds = self.recvTDS()

890

891 self.replies = self.parseReply(tds['Data'])

892

893 if TDS_LOGINACK_TOKEN in self.replies:

894 return True

895 else:

896 return False

897

898 def login(self, database, username, password='', domain='', hashes = None, useWindowsAuth = False):

899

900 if hashes is not None:

901 lmhash, nthash = hashes.split(':')

902 lmhash = binascii.a2b_hex(lmhash)

903 nthash = binascii.a2b_hex(nthash)

904 else:

905 lmhash = ''

906 nthash = ''

907

908 resp = self.preLogin()

909 # Test this!

910 if resp['Encryption'] == TDS_ENCRYPT_REQ or resp['Encryption'] == TDS_ENCRYPT_OFF:

911 LOG.info("Encryption required, switching to TLS")

912

913 # Switching to TLS now

914 ctx = SSL.Context(SSL.TLSv1_METHOD)

915 ctx.set_cipher_list('RC4, AES256')

916 tls = SSL.Connection(ctx,None)

917 tls.set_connect_state()

918 while True:

919 try:

920 tls.do_handshake()

921 except SSL.WantReadError:

922 data = tls.bio_read(4096)

923 self.sendTDS(TDS_PRE_LOGIN, data,0)

924 tds = self.recvTDS()

925 tls.bio_write(tds['Data'])

926 else:

927 break

928

929 # SSL and TLS limitation: Secure Socket Layer (SSL) and its replacement,

930 # Transport Layer Security(TLS), limit data fragments to 16k in size.

931 self.packetSize = 16*1024-1

932 self.tlsSocket = tls

933

934

935 login = TDS_LOGIN()

936

937 login['HostName'] = (''.join([random.choice(string.ascii_letters) for i in range(8)])).encode('utf-16le')

938 login['AppName'] = (''.join([random.choice(string.ascii_letters) for i in range(8)])).encode('utf-16le')

939 login['ServerName'] = self.server.encode('utf-16le')

940 login['CltIntName'] = login['AppName']

941 login['ClientPID'] = random.randint(0,1024)

942 login['PacketSize'] = self.packetSize

943 if database is not None:

944 login['Database'] = database.encode('utf-16le')

945 login['OptionFlags2'] = TDS_INIT_LANG_FATAL | TDS_ODBC_ON

946

947 if useWindowsAuth is True:

948 login['OptionFlags2'] |= TDS_INTEGRATED_SECURITY_ON

949 # NTLMSSP Negotiate

950 auth = ntlm.getNTLMSSPType1('','')

951 login['SSPI'] = auth.getData()

952 else:

953 login['UserName'] = username.encode('utf-16le')

954 login['Password'] = self.encryptPassword(password.encode('utf-16le'))

955 login['SSPI'] = ''

956

957

958 login['Length'] = len(login.getData())

959

960 # Send the NTLMSSP Negotiate or SQL Auth Packet

961 self.sendTDS(TDS_LOGIN7, login.getData())

962

963 # According to the specs, if encryption is not required, we must encrypt just

964 # the first Login packet :-o

965 if resp['Encryption'] == TDS_ENCRYPT_OFF:

966 self.tlsSocket = None

967

968 tds = self.recvTDS()

969

970

971 if useWindowsAuth is True:

972 serverChallenge = tds['Data'][3:]

973

974 # Generate the NTLM ChallengeResponse AUTH

975 type3, exportedSessionKey = ntlm.getNTLMSSPType3(auth, serverChallenge, username, password, domain, lmhash, nthash)

976

977 self.sendTDS(TDS_SSPI, type3.getData())

978 tds = self.recvTDS()

979

980 self.replies = self.parseReply(tds['Data'])

981

982 if TDS_LOGINACK_TOKEN in self.replies:

983 return True

984 else:

985 return False

986

987

988 def processColMeta(self):

989 for col in self.colMeta:

990 if col['Type'] in [TDS_NVARCHARTYPE, TDS_NCHARTYPE, TDS_NTEXTTYPE]:

991 col['Length'] = col['TypeData']//2

992 fmt = '%%-%ds'

993 elif col['Type'] in [TDS_GUIDTYPE]:

994 col['Length'] = 36

995 fmt = '%%%ds'

996 elif col['Type'] in [TDS_DECIMALNTYPE,TDS_NUMERICNTYPE]:

997 col['Length'] = ord(col['TypeData'][0:1])

998 fmt = '%%%ds'

999 elif col['Type'] in [TDS_DATETIMNTYPE]:

1000 col['Length'] = 19

1001 fmt = '%%-%ds'

1002 elif col['Type'] in [TDS_INT4TYPE, TDS_INTNTYPE]:

1003 col['Length'] = 11

1004 fmt = '%%%ds'

1005 elif col['Type'] in [TDS_FLTNTYPE, TDS_MONEYNTYPE]:

1006 col['Length'] = 25

1007 fmt = '%%%ds'

1008 elif col['Type'] in [TDS_BITNTYPE, TDS_BIGCHARTYPE]:

1009 col['Length'] = col['TypeData']

1010 fmt = '%%%ds'

1011 elif col['Type'] in [TDS_BIGBINARYTYPE, TDS_BIGVARBINTYPE]:

1012 col['Length'] = col['TypeData'] * 2

1013 fmt = '%%%ds'

1014 elif col['Type'] in [TDS_TEXTTYPE, TDS_BIGVARCHRTYPE]:

1015 col['Length'] = col['TypeData']

1016 fmt = '%%-%ds'

1017 else:

1018 col['Length'] = 10

1019 fmt = '%%%ds'

1020

1021 if len(col['Name']) > col['Length']:

1022 col['Length'] = len(col['Name'])

1023 elif col['Length'] > self.MAX_COL_LEN:

1024 col['Length'] = self.MAX_COL_LEN

1025

1026 col['Format'] = fmt % col['Length']

1027

1028

1029 def printColumnsHeader(self):

1030 if len(self.colMeta) == 0:

1031 return

1032 for col in self.colMeta:

1033 self.__rowsPrinter.logMessage(col['Format'] % col['Name'] + self.COL_SEPARATOR)

1034 self.__rowsPrinter.logMessage('\n')

1035 for col in self.colMeta:

1036 self.__rowsPrinter.logMessage('-'*col['Length'] + self.COL_SEPARATOR)

1037 self.__rowsPrinter.logMessage('\n')

1038

1039

1040 def printRows(self):

1041 if self.lastError is True:

1042 return

1043 self.processColMeta()

1044 self.printColumnsHeader()

1045 for row in self.rows:

1046 for col in self.colMeta:

1047 self.__rowsPrinter.logMessage(col['Format'] % row[col['Name']] + self.COL_SEPARATOR)

1048 self.__rowsPrinter.logMessage('\n')

1049

1050 def printReplies(self):

1051 for keys in list(self.replies.keys()):

1052 for i, key in enumerate(self.replies[keys]):

1053 if key['TokenType'] == TDS_ERROR_TOKEN:

1054 error = "ERROR(%s): Line %d: %s" % (key['ServerName'].decode('utf-16le'), key['LineNumber'], key['MsgText'].decode('utf-16le'))

1055 self.lastError = SQLErrorException("ERROR: Line %d: %s" % (key['LineNumber'], key['MsgText'].decode('utf-16le')))

1056 LOG.error(error)

1057

1058 elif key['TokenType'] == TDS_INFO_TOKEN:

1059 LOG.info("INFO(%s): Line %d: %s" % (key['ServerName'].decode('utf-16le'), key['LineNumber'], key['MsgText'].decode('utf-16le')))

1060

1061 elif key['TokenType'] == TDS_LOGINACK_TOKEN:

1062 LOG.info("ACK: Result: %s - %s (%d%d %d%d) " % (key['Interface'], key['ProgName'].decode('utf-16le'), key['MajorVer'], key['MinorVer'], key['BuildNumHi'], key['BuildNumLow']))

1063

1064 elif key['TokenType'] == TDS_ENVCHANGE_TOKEN:

1065 if key['Type'] in (TDS_ENVCHANGE_DATABASE, TDS_ENVCHANGE_LANGUAGE, TDS_ENVCHANGE_CHARSET, TDS_ENVCHANGE_PACKETSIZE):

1066 record = TDS_ENVCHANGE_VARCHAR(key['Data'])

1067 if record['OldValue'] == '':

1068 record['OldValue'] = 'None'.encode('utf-16le')

1069 elif record['NewValue'] == '':

1070 record['NewValue'] = 'None'.encode('utf-16le')

1071 if key['Type'] == TDS_ENVCHANGE_DATABASE:

1072 _type = 'DATABASE'

1073 elif key['Type'] == TDS_ENVCHANGE_LANGUAGE:

1074 _type = 'LANGUAGE'

1075 elif key['Type'] == TDS_ENVCHANGE_CHARSET:

1076 _type = 'CHARSET'

1077 elif key['Type'] == TDS_ENVCHANGE_PACKETSIZE:

1078 _type = 'PACKETSIZE'

1079 else:

1080 _type = "%d" % key['Type']

1081 LOG.info("ENVCHANGE(%s): Old Value: %s, New Value: %s" % (_type,record['OldValue'].decode('utf-16le'), record['NewValue'].decode('utf-16le')))

1082

1083 def parseRow(self,token,tuplemode=False):

1084 # TODO: This REALLY needs to be improved. Right now we don't support correctly all the data types

1085 # help would be appreciated ;)

1086 if len(token) == 1:

1087 return 0

1088

1089 row = [] if tuplemode else {}

1090

1091 origDataLen = len(token['Data'])

1092 data = token['Data']

1093 for col in self.colMeta:

1094 _type = col['Type']

1095 if (_type == TDS_NVARCHARTYPE) |\

1096 (_type == TDS_NCHARTYPE):

1097 #print "NVAR 0x%x" % _type

1098 charLen = struct.unpack('<H',data[:struct.calcsize('<H')])[0]

1099 data = data[struct.calcsize('<H'):]

1100 if charLen != 0xFFFF:

1101 value = data[:charLen].decode('utf-16le')

1102 data = data[charLen:]

1103 else:

1104 value = 'NULL'

1105

1106 elif _type == TDS_BIGVARCHRTYPE:

1107 charLen = struct.unpack('<H',data[:struct.calcsize('<H')])[0]

1108 data = data[struct.calcsize('<H'):]

1109 if charLen != 0xFFFF:

1110 value = data[:charLen]

1111 data = data[charLen:]

1112 else:

1113 value = 'NULL'

1114

1115 elif _type == TDS_GUIDTYPE:

1116 uuidLen = ord(data[0:1])

1117 data = data[1:]

1118 if uuidLen > 0:

1119 uu = data[:uuidLen]

1120 value = uuid.bin_to_string(uu)

1121 data = data[uuidLen:]

1122 else:

1123 value = 'NULL'

1124

1125 elif (_type == TDS_NTEXTTYPE) |\

1126 (_type == TDS_IMAGETYPE) :

1127 # Skip the pointer data

1128 charLen = ord(data[0:1])

1129 if charLen == 0:

1130 value = 'NULL'

1131 data = data[1:]

1132 else:

1133 data = data[1+charLen+8:]

1134 charLen = struct.unpack('<L',data[:struct.calcsize('<L')])[0]

1135 data = data[struct.calcsize('<L'):]

1136 if charLen != 0xFFFF:

1137 if _type == TDS_NTEXTTYPE:

1138 value = data[:charLen].decode('utf-16le')

1139 else:

1140 value = binascii.b2a_hex(data[:charLen])

1141 data = data[charLen:]

1142 else:

1143 value = 'NULL'

1144

1145 elif _type == TDS_TEXTTYPE:

1146 # Skip the pointer data

1147 charLen = ord(data[0:1])

1148 if charLen == 0:

1149 value = 'NULL'

1150 data = data[1:]

1151 else:

1152 data = data[1+charLen+8:]

1153 charLen = struct.unpack('<L',data[:struct.calcsize('<L')])[0]

1154 data = data[struct.calcsize('<L'):]

1155 if charLen != 0xFFFF:

1156 value = data[:charLen]

1157 data = data[charLen:]

1158 else:

1159 value = 'NULL'

1160

1161 elif (_type == TDS_BIGVARBINTYPE) |\

1162 (_type == TDS_BIGBINARYTYPE):

1163 charLen = struct.unpack('<H',data[:struct.calcsize('<H')])[0]

1164 data = data[struct.calcsize('<H'):]

1165 if charLen != 0xFFFF:

1166 value = binascii.b2a_hex(data[:charLen])

1167 data = data[charLen:]

1168 else:

1169 value = 'NULL'

1170

1171 elif (_type == TDS_DATETIM4TYPE) |\

1172 (_type == TDS_DATETIMNTYPE) |\

1173 (_type == TDS_DATETIMETYPE):

1174 value = ''

1175 if _type == TDS_DATETIMNTYPE:

1176 # For DATETIMNTYPE, the only valid lengths are 0x04 and 0x08, which map to smalldatetime and

1177 # datetime SQL data _types respectively.

1178 if ord(data[0:1]) == 4:

1179 _type = TDS_DATETIM4TYPE

1180 elif ord(data[0:1]) == 8:

1181 _type = TDS_DATETIMETYPE

1182 else:

1183 value = 'NULL'

1184 data = data[1:]

1185 if _type == TDS_DATETIMETYPE:

1186 # datetime is represented in the following sequence:

1187 # * One 4-byte signed integer that represents the number of days since January 1, 1900. Negative

1188 # numbers are allowed to represents dates since January 1, 1753.

1189 # * One 4-byte unsigned integer that represents the number of one three-hundredths of a second

1190 # (300 counts per second) elapsed since 12 AM that day.

1191 dateValue = struct.unpack('<l',data[:4])[0]

1192 data = data[4:]

1193 if dateValue < 0:

1194 baseDate = datetime.date(1753,1,1)

1195 else:

1196 baseDate = datetime.date(1900,1,1)

1197 timeValue = struct.unpack('<L',data[:4])[0]

1198 data = data[4:]

1199 elif _type == TDS_DATETIM4TYPE:

1200 # Small datetime

1201 # 2.2.5.5.1.8

1202 # Date/Times

1203 # smalldatetime is represented in the following sequence:

1204 # * One 2-byte unsigned integer that represents the number of days since January 1, 1900.

1205 # * One 2-byte unsigned integer that represents the number of minutes elapsed since 12 AM that

1206 # day.

1207 dateValue = struct.unpack('<H',data[:struct.calcsize('<H')])[0]

1208 data = data[struct.calcsize('<H'):]

1209 timeValue = struct.unpack('<H',data[:struct.calcsize('<H')])[0]

1210 data = data[struct.calcsize('<H'):]

1211 baseDate = datetime.date(1900,1,1)

1212 if value != 'NULL':

1213 dateValue = datetime.date.fromordinal(baseDate.toordinal() + dateValue)

1214 hours, mod = divmod(timeValue//300, 60*60)

1215 minutes, second = divmod(mod, 60)

1216 value = datetime.datetime(dateValue.year, dateValue.month, dateValue.day, hours, minutes, second)

1217

1218 elif (_type == TDS_INT4TYPE) |\

1219 (_type == TDS_MONEY4TYPE) |\

1220 (_type == TDS_FLT4TYPE):

1221 #print "INT4"

1222 value = struct.unpack('<l',data[:struct.calcsize('<l')])[0]

1223 data = data[struct.calcsize('<l'):]

1224

1225 elif _type == TDS_FLTNTYPE:

1226 valueSize = ord(data[:1])

1227 if valueSize == 4:

1228 fmt = '<f'

1229 elif valueSize == 8:

1230 fmt = '<d'

1231

1232 data = data[1:]

1233

1234 if valueSize > 0:

1235 value = struct.unpack(fmt,data[:valueSize])[0]

1236 data = data[valueSize:]

1237 else:

1238 value = 'NULL'

1239

1240 elif _type == TDS_MONEYNTYPE:

1241 valueSize = ord(data[:1])

1242 if valueSize == 4:

1243 fmt = '<l'

1244 elif valueSize == 8:

1245 fmt = '<q'

1246

1247 data = data[1:]

1248

1249 if valueSize > 0:

1250 value = struct.unpack(fmt,data[:valueSize])[0]

1251 if valueSize == 4:

1252 value = float(value) // math.pow(10,4)

1253 else:

1254 value = float(value >> 32) // math.pow(10,4)

1255 data = data[valueSize:]

1256 else:

1257 value = 'NULL'

1258

1259

1260 elif _type == TDS_BIGCHARTYPE:

1261 #print "BIGC"

1262 charLen = struct.unpack('<H',data[:struct.calcsize('<H')])[0]

1263 data = data[struct.calcsize('<H'):]

1264 value = data[:charLen]

1265 data = data[charLen:]

1266

1267 elif (_type == TDS_INT8TYPE) |\

1268 (_type == TDS_FLT8TYPE) |\

1269 (_type == TDS_MONEYTYPE):

1270 #print "DATETIME"

1271 value = struct.unpack('<q',data[:struct.calcsize('<q')])[0]

1272 data = data[struct.calcsize('<q'):]

1273

1274

1275 elif _type == TDS_INT2TYPE:

1276 #print "INT2TYPE"

1277 value = struct.unpack('<H',(data[:2]))[0]

1278 data = data[2:]

1279

1280 elif _type == TDS_DATENTYPE:

1281 # date is represented as one 3-byte unsigned integer that represents the number of days since

1282 # January 1, year 1.

1283 valueSize = ord(data[:1])

1284 data = data[1:]

1285 if valueSize > 0:

1286 dateBytes = data[:valueSize]

1287 dateValue = struct.unpack('<L','\x00'+dateBytes)[0]

1288 value = datetime.date.fromtimestamp(dateValue)

1289 data = data[valueSize:]

1290 else:

1291 value = 'NULL'

1292

1293 elif (_type == TDS_BITTYPE) |\

1294 (_type == TDS_INT1TYPE):

1295 #print "BITTYPE"

1296 value = ord(data[:1])

1297 data = data[1:]

1298

1299 elif (_type == TDS_NUMERICNTYPE) |\

1300 (_type == TDS_DECIMALNTYPE):

1301 valueLen = ord(data[:1])

1302 data = data[1:]

1303 value = data[:valueLen]

1304 data = data[valueLen:]

1305 precision = ord(col['TypeData'][1:2])

1306 scale = ord(col['TypeData'][2:3])

1307 if valueLen > 0:

1308 isPositiveSign = ord(value[0:1])

1309 if (valueLen-1) == 2:

1310 fmt = '<H'

1311 elif (valueLen-1) == 4:

1312 fmt = '<L'

1313 elif (valueLen-1) == 8:

1314 fmt = '<Q'

1315 else:

1316 # Still don't know how to handle higher values

1317 value = "TODO: Interpret TDS_NUMERICNTYPE correctly"

1318 number = struct.unpack(fmt, value[1:])[0]

1319 number //= math.pow(precision, scale)

1320 if isPositiveSign == 0:

1321 number *= -1

1322 value = number

1323 else:

1324 value = 'NULL'

1325

1326 elif _type == TDS_BITNTYPE:

1327 #print "BITNTYPE"

1328 valueSize = ord(data[:1])

1329 data = data[1:]

1330 if valueSize > 0:

1331 if valueSize == 1:

1332 value = ord(data[:valueSize])

1333 else:

1334 value = data[:valueSize]

1335 else:

1336 value = 'NULL'

1337 data = data[valueSize:]

1338

1339 elif _type == TDS_INTNTYPE:

1340 valueSize = ord(data[:1])

1341 if valueSize == 1:

1342 fmt = '<B'

1343 elif valueSize == 2:

1344 fmt = '<h'

1345 elif valueSize == 4:

1346 fmt = '<l'

1347 elif valueSize == 8:

1348 fmt = '<q'

1349 else:

1350 fmt = ''

1351

1352 data = data[1:]

1353

1354 if valueSize > 0:

1355 value = struct.unpack(fmt,data[:valueSize])[0]

1356 data = data[valueSize:]

1357 else:

1358 value = 'NULL'

1359 elif _type == TDS_SSVARIANTTYPE:

1360 raise Exception("ParseRow: SQL Variant type not yet supported :(")

1361 else:

1362 raise Exception("ParseROW: Unsupported data type: 0%x" % _type)

1363

1364 if tuplemode:

1365 row.append(value)

1366 else:

1367 row[col['Name']] = value

1368

1369

1370 self.rows.append(row)

1371

1372 return origDataLen - len(data)

1373

1374 def parseColMetaData(self, token):

1375 # TODO Add support for more data types!

1376 count = token['Count']

1377 if count == 0xFFFF:

1378 return 0

1379

1380 self.colMeta = []

1381 origDataLen = len(token['Data'])

1382 data = token['Data']

1383 for i in range(count):

1384 column = {}

1385 userType = struct.unpack('<H',data[:struct.calcsize('<H')])[0]

1386 data = data[struct.calcsize('<H'):]

1387 flags = struct.unpack('<H',data[:struct.calcsize('<H')])[0]

1388 data = data[struct.calcsize('<H'):]

1389 colType = struct.unpack('<B',data[:struct.calcsize('<B')])[0]

1390 data = data[struct.calcsize('<B'):]

1391 if (colType == TDS_BITTYPE) |\

1392 (colType == TDS_INT1TYPE) |\

1393 (colType == TDS_INT2TYPE) |\

1394 (colType == TDS_INT8TYPE) |\

1395 (colType == TDS_DATETIMETYPE) |\

1396 (colType == TDS_DATETIM4TYPE) |\

1397 (colType == TDS_FLT4TYPE) |\

1398 (colType == TDS_FLT8TYPE) |\

1399 (colType == TDS_MONEYTYPE) |\

1400 (colType == TDS_MONEY4TYPE) |\

1401 (colType == TDS_DATENTYPE) |\

1402 (colType == TDS_INT4TYPE):

1403 typeData = ''

1404 elif (colType == TDS_INTNTYPE) |\

1405 (colType == TDS_TIMENTYPE) |\

1406 (colType == TDS_DATETIME2NTYPE) |\

1407 (colType == TDS_DATETIMEOFFSETNTYPE) |\

1408 (colType == TDS_FLTNTYPE) |\

1409 (colType == TDS_MONEYNTYPE) |\

1410 (colType == TDS_GUIDTYPE) |\

1411 (colType == TDS_BITNTYPE):

1412 typeData = ord(data[0:1])

1413 data = data[1:]

1414

1415 elif colType == TDS_DATETIMNTYPE:

1416 # For DATETIMNTYPE, the only valid lengths are 0x04 and 0x08, which map to smalldatetime and

1417 # datetime SQL data types respectively.

1418 typeData = ord(data[0:1])

1419 data = data[1:]

1420

1421 elif (colType == TDS_BIGVARBINTYPE) |\

1422 (colType == TDS_BIGBINARYTYPE) |\

1423 (colType == TDS_NCHARTYPE) |\

1424 (colType == TDS_NVARCHARTYPE) |\

1425 (colType == TDS_BIGVARCHRTYPE) |\

1426 (colType == TDS_BIGCHARTYPE):

1427 typeData = struct.unpack('<H',data[:2])[0]

1428 data = data[2:]

1429 elif (colType == TDS_DECIMALNTYPE) |\

1430 (colType == TDS_NUMERICNTYPE) |\

1431 (colType == TDS_DECIMALTYPE):

1432 typeData = data[:3]

1433 data = data[3:]

1434 elif (colType == TDS_IMAGETYPE) |\

1435 (colType == TDS_TEXTTYPE) |\

1436 (colType == TDS_XMLTYPE) |\

1437 (colType == TDS_SSVARIANTTYPE) |\

1438 (colType == TDS_NTEXTTYPE):

1439 typeData = struct.unpack('<L',data[:4])[0]

1440 data = data[4:]

1441 else:

1442 raise Exception("Unsupported data type: 0x%x" % colType)

1443

1444 # Collation exceptions:

1445 if (colType == TDS_NTEXTTYPE) |\

1446 (colType == TDS_BIGCHARTYPE) |\

1447 (colType == TDS_BIGVARCHRTYPE) |\

1448 (colType == TDS_NCHARTYPE) |\

1449 (colType == TDS_NVARCHARTYPE) |\

1450 (colType == TDS_TEXTTYPE):

1451 # Skip collation

1452 data = data[5:]

1453

1454 # PartTableName exceptions:

1455 if (colType == TDS_IMAGETYPE) |\

1456 (colType == TDS_TEXTTYPE) |\

1457 (colType == TDS_NTEXTTYPE):

1458 # This types have Table Elements, we just discard them for now.

1459 # ToDo parse this correctly!

1460 # Get the Length

1461 dataLen = struct.unpack('<H',data[:2])[0]

1462 data = data[2:]

1463 # skip the text

1464 data = data[dataLen*2:]

1465

1466 colNameLength = struct.unpack('<B',data[:struct.calcsize('<B')])[0]

1467 data = data[struct.calcsize('<B'):]

1468 colName = data[:colNameLength*2].decode('utf-16le')

1469 data = data[colNameLength*2:]

1470 column['Name'] = colName

1471 column['Type'] = colType

1472 column['TypeData'] = typeData

1473 column['Flags'] = flags

1474 self.colMeta.append(column)

1475

1476 return origDataLen - len(data)

1477

1478 def parseReply(self, tokens,tuplemode=False):

1479 if len(tokens) == 0:

1480 return False

1481

1482 replies = {}

1483 while len(tokens) > 0:

1484 tokenID = struct.unpack('B',tokens[0:1])[0]

1485 if tokenID == TDS_ERROR_TOKEN:

1486 token = TDS_INFO_ERROR(tokens)

1487 elif tokenID == TDS_RETURNSTATUS_TOKEN:

1488 token = TDS_RETURNSTATUS(tokens)

1489 elif tokenID == TDS_INFO_TOKEN:

1490 token = TDS_INFO_ERROR(tokens)

1491 elif tokenID == TDS_LOGINACK_TOKEN:

1492 token = TDS_LOGIN_ACK(tokens)

1493 elif tokenID == TDS_ENVCHANGE_TOKEN:

1494 token = TDS_ENVCHANGE(tokens)

1495 if token['Type'] is TDS_ENVCHANGE_PACKETSIZE:

1496 record = TDS_ENVCHANGE_VARCHAR(token['Data'])

1497 self.packetSize = int( record['NewValue'].decode('utf-16le') )

1498 elif token['Type'] is TDS_ENVCHANGE_DATABASE:

1499 record = TDS_ENVCHANGE_VARCHAR(token['Data'])

1500 self.currentDB = record['NewValue'].decode('utf-16le')

1501

1502 elif (tokenID == TDS_DONEINPROC_TOKEN) |\

1503 (tokenID == TDS_DONEPROC_TOKEN):

1504 token = TDS_DONEINPROC(tokens)

1505 elif tokenID == TDS_ORDER_TOKEN:

1506 token = TDS_ORDER(tokens)

1507 elif tokenID == TDS_ROW_TOKEN:

1508 #print "ROW"

1509 token = TDS_ROW(tokens)

1510 tokenLen = self.parseRow(token,tuplemode)

1511 token['Data'] = token['Data'][:tokenLen]

1512 elif tokenID == TDS_COLMETADATA_TOKEN:

1513 #print "COLMETA"

1514 token = TDS_COLMETADATA(tokens)

1515 tokenLen = self.parseColMetaData(token)

1516 token['Data'] = token['Data'][:tokenLen]

1517 elif tokenID == TDS_DONE_TOKEN:

1518 token = TDS_DONE(tokens)

1519 else:

1520 LOG.error("Unknown Token %x" % tokenID)

1521 return replies

1522

1523 if (tokenID in replies) is not True:

1524 replies[tokenID] = list()

1525

1526 replies[tokenID].append(token)

1527 tokens = tokens[len(token):]

1528 #print "TYPE 0x%x, LEN: %d" %(tokenID, len(token))

1529 #print repr(tokens[:10])

1530

1531 return replies

1532

1533 def batch(self, cmd,tuplemode=False,wait=True):

1534 # First of all we clear the rows, colMeta and lastError

1535 self.rows = []

1536 self.colMeta = []

1537 self.lastError = False

1538 self.sendTDS(TDS_SQL_BATCH, (cmd+'\r\n').encode('utf-16le'))

1539 if wait:

1540 tds = self.recvTDS()

1541 self.replies = self.parseReply(tds['Data'],tuplemode)

1542 return self.rows

1543 else:

1544 return True

1545

1546

1547 def batchStatement(self, cmd,tuplemode=False):

1548 # First of all we clear the rows, colMeta and lastError

1549 self.rows = []

1550 self.colMeta = []

1551 self.lastError = False

1552 self.sendTDS(TDS_SQL_BATCH, (cmd+'\r\n').encode('utf-16le'))

1553 #self.recvTDS()

1554

1555

1556 # Handy alias

1557 sql_query = batch

1558

1559 def changeDB(self, db):

1560 if db != self.currentDB:

1561 chdb = 'use %s' % db

1562 self.batch(chdb)

1563 self.printReplies()

1564

1565 def RunSQLQuery(self,db,sql_query,tuplemode=False,wait=True,**kwArgs):

1566 db = db or 'master'

1567 self.changeDB(db)

1568 self.printReplies()

1569 ret = self.batch(sql_query,tuplemode,wait)

1570 if wait:

1571 self.printReplies()

1572 if self.lastError:

1573 raise self.lastError

1574 if self.lastError:

1575 raise self.lastError

1576 return ret

1577

1578 def RunSQLStatement(self,db,sql_query,wait=True,**kwArgs):

1579 self.RunSQLQuery(db,sql_query,wait=wait)

1580 if self.lastError:

1581 raise self.lastError

1582 return True