1 # Copyright (c) 2003-2016 CORE Security Technologies 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 # Author: Alberto Solino (@agsolino) 8 # 9 # TODO: 10 # [-] Functions should return NT error codes 11 # [-] Handling errors in all situations, right now it's just raising exceptions. 12 # [*] Standard authentication support 13 # [ ] Organize the connectionData stuff 14 # [*] Add capability to send a bad user ID if the user is not authenticated, 15 # right now you can ask for any command without actually being authenticated 16 # [ ] PATH TRAVERSALS EVERYWHERE.. BE WARNED! 17 # [ ] Check the credentials.. now we're just letting everybody to log in. 18 # [ ] Check error situation (now many places assume the right data is coming) 19 # [ ] Implement IPC to the main process so the connectionData is on a single place 20 # [ ] Hence.. implement locking 21 # estamos en la B 22 23 from __future__ import with_statement 24 import calendar 25 import socket 26 import time 27 import datetime 28 import struct 29 import ConfigParser 30 import SocketServer 31 import threading 32 import logging 33 import logging.config 34 import ntpath 35 import os 36 import fnmatch 37 import errno 38 import sys 39 import random 40 import shutil 41 from binascii import hexlify 42 43 # For signing 44 from impacket import smb, nmb, ntlm, uuid, LOG 45 from impacket import smb3structs as smb2 46 from impacket.spnego import SPNEGO_NegTokenInit, TypesMech, MechTypes, SPNEGO_NegTokenResp, ASN1_AID, ASN1_SUPPORTED_MECH 47 from impacket.nt_errors import STATUS_NO_MORE_FILES, STATUS_NETWORK_NAME_DELETED, STATUS_INVALID_PARAMETER, \ 48 STATUS_FILE_CLOSED, STATUS_MORE_PROCESSING_REQUIRED, STATUS_OBJECT_PATH_NOT_FOUND, STATUS_DIRECTORY_NOT_EMPTY, \ 49 STATUS_FILE_IS_A_DIRECTORY, STATUS_NOT_IMPLEMENTED, STATUS_INVALID_HANDLE, STATUS_OBJECT_NAME_COLLISION, \ 50 STATUS_NO_SUCH_FILE, STATUS_CANCELLED, STATUS_OBJECT_NAME_NOT_FOUND, STATUS_SUCCESS, STATUS_ACCESS_DENIED, \ 51 STATUS_NOT_SUPPORTED, STATUS_INVALID_DEVICE_REQUEST, STATUS_FS_DRIVER_REQUIRED, STATUS_INVALID_INFO_CLASS 52 53 # These ones not defined in nt_errors 54 STATUS_SMB_BAD_UID = 0x005B0002 55 STATUS_SMB_BAD_TID = 0x00050002 56 57 # Utility functions 58 # and general functions. 59 # There are some common functions that can be accessed from more than one SMB 60 # command (or either TRANSACTION). That's why I'm putting them here 61 # TODO: Return NT ERROR Codes 62 63 def outputToJohnFormat(challenge, username, domain, lmresponse, ntresponse): 64 # We don't want to add a possible failure here, since this is an 65 # extra bonus. We try, if it fails, returns nothing 66 ret_value = '' 67 try: 68 if len(ntresponse) > 24: 69 # Extended Security - NTLMv2 70 ret_value = {'hash_string':'%s::%s:%s:%s:%s' % (username.decode('utf-16le'), domain.decode('utf-16le'), hexlify(challenge), hexlify(ntresponse)[:32], hexlify(ntresponse)[32:]), 'hash_version':'ntlmv2'} 71 else: 72 # NTLMv1 73 ret_value = {'hash_string':'%s::%s:%s:%s:%s' % (username.decode('utf-16le'), domain.decode('utf-16le'), hexlify(lmresponse), hexlify(ntresponse), hexlify(challenge)), 'hash_version':'ntlm'} 74 except: 75 # Let's try w/o decoding Unicode 76 try: 77 if len(ntresponse) > 24: 78 # Extended Security - NTLMv2 79 ret_value = {'hash_string':'%s::%s:%s:%s:%s' % (username, domain, hexlify(challenge), hexlify(ntresponse)[:32], hexlify(ntresponse)[32:]), 'hash_version':'ntlmv2'} 80 else: 81 # NTLMv1 82 ret_value = {'hash_string':'%s::%s:%s:%s:%s' % (username, domain, hexlify(lmresponse), hexlify(ntresponse), hexlify(challenge)), 'hash_version':'ntlm'} 83 except Exception, e: 84 LOG.error("outputToJohnFormat: %s" % e) 85 pass 86 87 return ret_value 88 89 def writeJohnOutputToFile(hash_string, hash_version, file_name): 90 fn_data = os.path.splitext(file_name) 91 if hash_version == "ntlmv2": 92 output_filename = fn_data[0] + "_ntlmv2" + fn_data[1] 93 else: 94 output_filename = fn_data[0] + "_ntlm" + fn_data[1] 95 96 with open(output_filename,"a") as f: 97 f.write(hash_string) 98 f.write('\n') 99 100 101 def decodeSMBString( flags, text ): 102 if flags & smb.SMB.FLAGS2_UNICODE: 103 return text.decode('utf-16le') 104 else: 105 return text 106 107 def encodeSMBString( flags, text ): 108 if flags & smb.SMB.FLAGS2_UNICODE: 109 return (text).encode('utf-16le') 110 else: 111 return text 112 113 def getFileTime(t): 114 t *= 10000000 115 t += 116444736000000000 116 return t 117 118 def getUnixTime(t): 119 t -= 116444736000000000 120 t /= 10000000 121 return t 122 123 def getSMBDate(t): 124 # TODO: Fix this :P 125 d = datetime.date.fromtimestamp(t) 126 year = d.year - 1980 127 ret = (year << 8) + (d.month << 4) + d.day 128 return ret 129 130 def getSMBTime(t): 131 # TODO: Fix this :P 132 d = datetime.datetime.fromtimestamp(t) 133 return (d.hour << 8) + (d.minute << 4) + d.second 134 135 def getShares(connId, smbServer): 136 config = smbServer.getServerConfig() 137 sections = config.sections() 138 # Remove the global one 139 del(sections[sections.index('global')]) 140 shares = {} 141 for i in sections: 142 shares[i] = dict(config.items(i)) 143 return shares 144 145 def searchShare(connId, share, smbServer): 146 config = smbServer.getServerConfig() 147 if config.has_section(share): 148 return dict(config.items(share)) 149 else: 150 return None 151 152 def openFile(path,fileName, accessMode, fileAttributes, openMode): 153 fileName = os.path.normpath(fileName.replace('\\','/')) 154 errorCode = 0 155 if len(fileName) > 0 and (fileName[0] == '/' or fileName[0] == '\\'): 156 # strip leading '/' 157 fileName = fileName[1:] 158 pathName = os.path.join(path,fileName) 159 mode = 0 160 # Check the Open Mode 161 if openMode & 0x10: 162 # If the file does not exist, create it. 163 mode = os.O_CREAT 164 else: 165 # If file does not exist, return an error 166 if os.path.exists(pathName) is not True: 167 errorCode = STATUS_NO_SUCH_FILE 168 return 0,mode, pathName, errorCode 169 170 if os.path.isdir(pathName) and (fileAttributes & smb.ATTR_DIRECTORY) == 0: 171 # Request to open a normal file and this is actually a directory 172 errorCode = STATUS_FILE_IS_A_DIRECTORY 173 return 0, mode, pathName, errorCode 174 # Check the Access Mode 175 if accessMode & 0x7 == 1: 176 mode |= os.O_WRONLY 177 elif accessMode & 0x7 == 2: 178 mode |= os.O_RDWR 179 else: 180 mode = os.O_RDONLY 181 182 try: 183 if sys.platform == 'win32': 184 mode |= os.O_BINARY 185 fid = os.open(pathName, mode) 186 except Exception, e: 187 LOG.error("openFile: %s,%s" % (pathName, mode) ,e) 188 fid = 0 189 errorCode = STATUS_ACCESS_DENIED 190 191 return fid, mode, pathName, errorCode 192 193 def queryFsInformation(path, filename, level=0): 194 195 if isinstance(filename,unicode): 196 encoding = 'utf-16le' 197 flags = smb.SMB.FLAGS2_UNICODE 198 else: 199 encoding = 'ascii' 200 flags = 0 201 202 fileName = os.path.normpath(filename.replace('\\','/')) 203 if len(fileName) > 0 and (fileName[0] == '/' or fileName[0] == '\\'): 204 # strip leading '/' 205 fileName = fileName[1:] 206 pathName = os.path.join(path,fileName) 207 fileSize = os.path.getsize(pathName) 208 (mode, ino, dev, nlink, uid, gid, size, atime, mtime, ctime) = os.stat(pathName) 209 if level == smb.SMB_QUERY_FS_ATTRIBUTE_INFO or level == smb2.SMB2_FILESYSTEM_ATTRIBUTE_INFO: 210 data = smb.SMBQueryFsAttributeInfo() 211 data['FileSystemAttributes'] = smb.FILE_CASE_SENSITIVE_SEARCH | smb.FILE_CASE_PRESERVED_NAMES 212 data['MaxFilenNameLengthInBytes'] = 255 213 data['LengthOfFileSystemName'] = len('XTFS')*2 214 data['FileSystemName'] = 'XTFS'.encode('utf-16le') 215 return data.getData() 216 elif level == smb.SMB_INFO_VOLUME: 217 data = smb.SMBQueryFsInfoVolume( flags = flags ) 218 data['VolumeLabel'] = 'SHARE'.encode(encoding) 219 return data.getData() 220 elif level == smb.SMB_QUERY_FS_VOLUME_INFO or level == smb2.SMB2_FILESYSTEM_VOLUME_INFO: 221 data = smb.SMBQueryFsVolumeInfo() 222 data['VolumeLabel'] = '' 223 data['VolumeCreationTime'] = getFileTime(ctime) 224 return data.getData() 225 elif level == smb.SMB_QUERY_FS_SIZE_INFO: 226 data = smb.SMBQueryFsSizeInfo() 227 return data.getData() 228 elif level == smb.FILE_FS_FULL_SIZE_INFORMATION: 229 data = smb.SMBFileFsFullSizeInformation() 230 return data.getData() 231 elif level == smb.FILE_FS_SIZE_INFORMATION: 232 data = smb.FileFsSizeInformation() 233 return data.getData() 234 else: 235 lastWriteTime = mtime 236 attribs = 0 237 if os.path.isdir(pathName): 238 attribs |= smb.SMB_FILE_ATTRIBUTE_DIRECTORY 239 if os.path.isfile(pathName): 240 attribs |= smb.SMB_FILE_ATTRIBUTE_NORMAL 241 fileAttributes = attribs 242 return fileSize, lastWriteTime, fileAttributes 243 244 def findFirst2(path, fileName, level, searchAttributes, isSMB2 = False): 245 # TODO: Depending on the level, this could be done much simpler 246 247 #print "FindFirs2 path:%s, filename:%s" % (path, fileName) 248 fileName = os.path.normpath(fileName.replace('\\','/')) 249 # Let's choose the right encoding depending on the request 250 if isinstance(fileName,unicode): 251 encoding = 'utf-16le' 252 flags = smb.SMB.FLAGS2_UNICODE 253 else: 254 encoding = 'ascii' 255 flags = 0 256 257 if len(fileName) > 0 and (fileName[0] == '/' or fileName[0] == '\\'): 258 # strip leading '/' 259 fileName = fileName[1:] 260 261 pathName = os.path.join(path,fileName) 262 files = [] 263 264 if pathName.find('*') == -1 and pathName.find('?') == -1: 265 # No search patterns 266 pattern = '' 267 else: 268 pattern = os.path.basename(pathName) 269 dirName = os.path.dirname(pathName) 270 271 # Always add . and .. Not that important for Windows, but Samba whines if 272 # not present (for * search only) 273 if pattern == '*': 274 files.append(os.path.join(dirName,'.')) 275 files.append(os.path.join(dirName,'..')) 276 277 if pattern != '': 278 for file in os.listdir(dirName): 279 if fnmatch.fnmatch(file.lower(),pattern.lower()): 280 entry = os.path.join(dirName, file) 281 if os.path.isdir(entry): 282 if searchAttributes & smb.ATTR_DIRECTORY: 283 files.append(entry) 284 else: 285 files.append(entry) 286 else: 287 if os.path.exists(pathName): 288 files.append(pathName) 289 290 searchResult = [] 291 searchCount = len(files) 292 errorCode = STATUS_SUCCESS 293 294 for i in files: 295 if level == smb.SMB_FIND_FILE_BOTH_DIRECTORY_INFO or level == smb2.SMB2_FILE_BOTH_DIRECTORY_INFO: 296 item = smb.SMBFindFileBothDirectoryInfo( flags = flags ) 297 elif level == smb.SMB_FIND_FILE_DIRECTORY_INFO or level == smb2.SMB2_FILE_DIRECTORY_INFO: 298 item = smb.SMBFindFileDirectoryInfo( flags = flags ) 299 elif level == smb.SMB_FIND_FILE_FULL_DIRECTORY_INFO or level == smb2.SMB2_FULL_DIRECTORY_INFO: 300 item = smb.SMBFindFileFullDirectoryInfo( flags = flags ) 301 elif level == smb.SMB_FIND_INFO_STANDARD: 302 item = smb.SMBFindInfoStandard( flags = flags ) 303 elif level == smb.SMB_FIND_FILE_ID_FULL_DIRECTORY_INFO or level == smb2.SMB2_FILE_ID_FULL_DIRECTORY_INFO: 304 item = smb.SMBFindFileIdFullDirectoryInfo( flags = flags ) 305 elif level == smb.SMB_FIND_FILE_ID_BOTH_DIRECTORY_INFO or level == smb2.SMB2_FILE_ID_BOTH_DIRECTORY_INFO: 306 item = smb.SMBFindFileIdBothDirectoryInfo( flags = flags ) 307 elif level == smb.SMB_FIND_FILE_NAMES_INFO or level == smb2.SMB2_FILE_NAMES_INFO: 308 item = smb.SMBFindFileNamesInfo( flags = flags ) 309 else: 310 LOG.error("Wrong level %d!" % level) 311 return searchResult, searchCount, STATUS_NOT_SUPPORTED 312 313 (mode, ino, dev, nlink, uid, gid, size, atime, mtime, ctime) = os.stat(i) 314 if os.path.isdir(i): 315 item['ExtFileAttributes'] = smb.ATTR_DIRECTORY 316 else: 317 item['ExtFileAttributes'] = smb.ATTR_NORMAL | smb.ATTR_ARCHIVE 318 319 item['FileName'] = os.path.basename(i).encode(encoding) 320 321 if level == smb.SMB_FIND_FILE_BOTH_DIRECTORY_INFO or level == smb.SMB_FIND_FILE_ID_BOTH_DIRECTORY_INFO or level == smb2.SMB2_FILE_ID_BOTH_DIRECTORY_INFO or level == smb2.SMB2_FILE_BOTH_DIRECTORY_INFO: 322 item['EaSize'] = 0 323 item['EndOfFile'] = size 324 item['AllocationSize'] = size 325 item['CreationTime'] = getFileTime(ctime) 326 item['LastAccessTime'] = getFileTime(atime) 327 item['LastWriteTime'] = getFileTime(mtime) 328 item['LastChangeTime'] = getFileTime(mtime) 329 item['ShortName'] = '\x00'*24 330 item['FileName'] = os.path.basename(i).encode(encoding) 331 padLen = (8-(len(item) % 8)) % 8 332 item['NextEntryOffset'] = len(item) + padLen 333 elif level == smb.SMB_FIND_FILE_DIRECTORY_INFO: 334 item['EndOfFile'] = size 335 item['AllocationSize'] = size 336 item['CreationTime'] = getFileTime(ctime) 337 item['LastAccessTime'] = getFileTime(atime) 338 item['LastWriteTime'] = getFileTime(mtime) 339 item['LastChangeTime'] = getFileTime(mtime) 340 item['FileName'] = os.path.basename(i).encode(encoding) 341 padLen = (8-(len(item) % 8)) % 8 342 item['NextEntryOffset'] = len(item) + padLen 343 elif level == smb.SMB_FIND_FILE_FULL_DIRECTORY_INFO or level == smb.SMB_FIND_FILE_ID_FULL_DIRECTORY_INFO or level == smb2.SMB2_FULL_DIRECTORY_INFO: 344 item['EaSize'] = 0 345 item['EndOfFile'] = size 346 item['AllocationSize'] = size 347 item['CreationTime'] = getFileTime(ctime) 348 item['LastAccessTime'] = getFileTime(atime) 349 item['LastWriteTime'] = getFileTime(mtime) 350 item['LastChangeTime'] = getFileTime(mtime) 351 padLen = (8-(len(item) % 8)) % 8 352 item['NextEntryOffset'] = len(item) + padLen 353 elif level == smb.SMB_FIND_INFO_STANDARD: 354 item['EaSize'] = size 355 item['CreationDate'] = getSMBDate(ctime) 356 item['CreationTime'] = getSMBTime(ctime) 357 item['LastAccessDate'] = getSMBDate(atime) 358 item['LastAccessTime'] = getSMBTime(atime) 359 item['LastWriteDate'] = getSMBDate(mtime) 360 item['LastWriteTime'] = getSMBTime(mtime) 361 searchResult.append(item) 362 363 # No more files 364 if (level >= smb.SMB_FIND_FILE_DIRECTORY_INFO or isSMB2 == True) and searchCount > 0: 365 searchResult[-1]['NextEntryOffset'] = 0 366 367 return searchResult, searchCount, errorCode 368 369 def queryFileInformation(path, filename, level): 370 #print "queryFileInfo path: %s, filename: %s, level:0x%x" % (path,filename,level) 371 return queryPathInformation(path,filename, level) 372 373 def queryPathInformation(path, filename, level): 374 # TODO: Depending on the level, this could be done much simpler 375 #print "queryPathInfo path: %s, filename: %s, level:0x%x" % (path,filename,level) 376 try: 377 errorCode = 0 378 fileName = os.path.normpath(filename.replace('\\','/')) 379 if len(fileName) > 0 and (fileName[0] == '/' or fileName[0] == '\\') and path != '': 380 # strip leading '/' 381 fileName = fileName[1:] 382 pathName = os.path.join(path,fileName) 383 if os.path.exists(pathName): 384 (mode, ino, dev, nlink, uid, gid, size, atime, mtime, ctime) = os.stat(pathName) 385 if level == smb.SMB_QUERY_FILE_BASIC_INFO: 386 infoRecord = smb.SMBQueryFileBasicInfo() 387 infoRecord['CreationTime'] = getFileTime(ctime) 388 infoRecord['LastAccessTime'] = getFileTime(atime) 389 infoRecord['LastWriteTime'] = getFileTime(mtime) 390 infoRecord['LastChangeTime'] = getFileTime(mtime) 391 if os.path.isdir(pathName): 392 infoRecord['ExtFileAttributes'] = smb.ATTR_DIRECTORY 393 else: 394 infoRecord['ExtFileAttributes'] = smb.ATTR_NORMAL | smb.ATTR_ARCHIVE 395 elif level == smb.SMB_QUERY_FILE_STANDARD_INFO: 396 infoRecord = smb.SMBQueryFileStandardInfo() 397 infoRecord['AllocationSize'] = size 398 infoRecord['EndOfFile'] = size 399 if os.path.isdir(pathName): 400 infoRecord['Directory'] = 1 401 else: 402 infoRecord['Directory'] = 0 403 elif level == smb.SMB_QUERY_FILE_ALL_INFO or level == smb2.SMB2_FILE_ALL_INFO: 404 infoRecord = smb.SMBQueryFileAllInfo() 405 infoRecord['CreationTime'] = getFileTime(ctime) 406 infoRecord['LastAccessTime'] = getFileTime(atime) 407 infoRecord['LastWriteTime'] = getFileTime(mtime) 408 infoRecord['LastChangeTime'] = getFileTime(mtime) 409 if os.path.isdir(pathName): 410 infoRecord['ExtFileAttributes'] = smb.ATTR_DIRECTORY 411 else: 412 infoRecord['ExtFileAttributes'] = smb.ATTR_NORMAL | smb.ATTR_ARCHIVE 413 infoRecord['AllocationSize'] = size 414 infoRecord['EndOfFile'] = size 415 if os.path.isdir(pathName): 416 infoRecord['Directory'] = 1 417 else: 418 infoRecord['Directory'] = 0 419 infoRecord['FileName'] = filename.encode('utf-16le') 420 elif level == smb2.SMB2_FILE_NETWORK_OPEN_INFO: 421 infoRecord = smb.SMBFileNetworkOpenInfo() 422 infoRecord['CreationTime'] = getFileTime(ctime) 423 infoRecord['LastAccessTime'] = getFileTime(atime) 424 infoRecord['LastWriteTime'] = getFileTime(mtime) 425 infoRecord['ChangeTime'] = getFileTime(mtime) 426 infoRecord['AllocationSize'] = size 427 infoRecord['EndOfFile'] = size 428 if os.path.isdir(pathName): 429 infoRecord['FileAttributes'] = smb.ATTR_DIRECTORY 430 else: 431 infoRecord['FileAttributes'] = smb.ATTR_NORMAL | smb.ATTR_ARCHIVE 432 elif level == smb.SMB_QUERY_FILE_EA_INFO or level == smb2.SMB2_FILE_EA_INFO: 433 infoRecord = smb.SMBQueryFileEaInfo() 434 elif level == smb2.SMB2_FILE_STREAM_INFO: 435 infoRecord = smb.SMBFileStreamInformation() 436 else: 437 LOG.error('Unknown level for query path info! 0x%x' % level) 438 # UNSUPPORTED 439 return None, STATUS_NOT_SUPPORTED 440 441 return infoRecord, errorCode 442 else: 443 # NOT FOUND 444 return None, STATUS_OBJECT_NAME_NOT_FOUND 445 except Exception, e: 446 LOG.error('queryPathInfo: %s' % e) 447 raise 448 449 def queryDiskInformation(path): 450 # TODO: Do something useful here :) 451 # For now we just return fake values 452 totalUnits = 65535 453 freeUnits = 65535 454 return totalUnits, freeUnits 455 456 # Here we implement the NT transaction handlers 457 class NTTRANSCommands: 458 def default(self, connId, smbServer, recvPacket, parameters, data, maxDataCount = 0): 459 pass 460 461 # Here we implement the NT transaction handlers 462 class TRANSCommands: 463 @staticmethod 464 def lanMan(connId, smbServer, recvPacket, parameters, data, maxDataCount = 0): 465 # Minimal [MS-RAP] implementation, just to return the shares 466 connData = smbServer.getConnectionData(connId) 467 468 respSetup = '' 469 respParameters = '' 470 respData = '' 471 errorCode = STATUS_SUCCESS 472 if struct.unpack('<H',parameters[:2])[0] == 0: 473 # NetShareEnum Request 474 netShareEnum = smb.SMBNetShareEnum(parameters) 475 if netShareEnum['InfoLevel'] == 1: 476 shares = getShares(connId, smbServer) 477 respParameters = smb.SMBNetShareEnumResponse() 478 respParameters['EntriesReturned'] = len(shares) 479 respParameters['EntriesAvailable'] = len(shares) 480 tailData = '' 481 for i in shares: 482 # NetShareInfo1 len == 20 483 entry = smb.NetShareInfo1() 484 entry['NetworkName'] = i + '\x00'*(13-len(i)) 485 entry['Type'] = int(shares[i]['share type']) 486 # (beto) If offset == 0 it crashes explorer.exe on windows 7 487 entry['RemarkOffsetLow'] = 20 * len(shares) + len(tailData) 488 respData += entry.getData() 489 if shares[i].has_key('comment'): 490 tailData += shares[i]['comment'] + '\x00' 491 else: 492 tailData += '\x00' 493 respData += tailData 494 else: 495 # We don't support other info levels 496 errorCode = STATUS_NOT_SUPPORTED 497 elif struct.unpack('<H',parameters[:2])[0] == 13: 498 # NetrServerGetInfo Request 499 respParameters = smb.SMBNetServerGetInfoResponse() 500 netServerInfo = smb.SMBNetServerInfo1() 501 netServerInfo['ServerName'] = smbServer.getServerName() 502 respData = str(netServerInfo) 503 respParameters['TotalBytesAvailable'] = len(respData) 504 elif struct.unpack('<H',parameters[:2])[0] == 1: 505 # NetrShareGetInfo Request 506 request = smb.SMBNetShareGetInfo(parameters) 507 respParameters = smb.SMBNetShareGetInfoResponse() 508 shares = getShares(connId, smbServer) 509 share = shares[request['ShareName'].upper()] 510 shareInfo = smb.NetShareInfo1() 511 shareInfo['NetworkName'] = request['ShareName'].upper() + '\x00' 512 shareInfo['Type'] = int(share['share type']) 513 respData = shareInfo.getData() 514 if share.has_key('comment'): 515 shareInfo['RemarkOffsetLow'] = len(respData) 516 respData += share['comment'] + '\x00' 517 respParameters['TotalBytesAvailable'] = len(respData) 518 519 else: 520 # We don't know how to handle anything else 521 errorCode = STATUS_NOT_SUPPORTED 522 523 smbServer.setConnectionData(connId, connData) 524 525 return respSetup, respParameters, respData, errorCode 526 527 @staticmethod 528 def transactNamedPipe(connId, smbServer, recvPacket, parameters, data, maxDataCount = 0): 529 connData = smbServer.getConnectionData(connId) 530 531 respSetup = '' 532 respParameters = '' 533 respData = '' 534 errorCode = STATUS_SUCCESS 535 SMBCommand = smb.SMBCommand(recvPacket['Data'][0]) 536 transParameters= smb.SMBTransaction_Parameters(SMBCommand['Parameters']) 537 538 # Extract the FID 539 fid = struct.unpack('<H', transParameters['Setup'][2:])[0] 540 541 if connData['OpenedFiles'].has_key(fid): 542 fileHandle = connData['OpenedFiles'][fid]['FileHandle'] 543 if fileHandle != PIPE_FILE_DESCRIPTOR: 544 os.write(fileHandle,data) 545 respData = os.read(fileHandle,data) 546 else: 547 sock = connData['OpenedFiles'][fid]['Socket'] 548 sock.send(data) 549 respData = sock.recv(maxDataCount) 550 else: 551 errorCode = STATUS_INVALID_HANDLE 552 553 smbServer.setConnectionData(connId, connData) 554 555 return respSetup, respParameters, respData, errorCode 556 557 # Here we implement the transaction2 handlers 558 class TRANS2Commands: 559 # All these commands return setup, parameters, data, errorCode 560 @staticmethod 561 def setPathInformation(connId, smbServer, recvPacket, parameters, data, maxDataCount = 0): 562 connData = smbServer.getConnectionData(connId) 563 564 respSetup = '' 565 respParameters = '' 566 respData = '' 567 errorCode = STATUS_SUCCESS 568 setPathInfoParameters = smb.SMBSetPathInformation_Parameters(flags = recvPacket['Flags2'], data = parameters) 569 if connData['ConnectedShares'].has_key(recvPacket['Tid']): 570 path = connData['ConnectedShares'][recvPacket['Tid']]['path'] 571 fileName = decodeSMBString(recvPacket['Flags2'], setPathInfoParameters['FileName']) 572 fileName = os.path.normpath(fileName.replace('\\','/')) 573 if len(fileName) > 0 and (fileName[0] == '/' or fileName[0] == '\\') and path != '': 574 # strip leading '/' 575 fileName = fileName[1:] 576 pathName = os.path.join(path,fileName) 577 if os.path.exists(pathName): 578 informationLevel = setPathInfoParameters['InformationLevel'] 579 if informationLevel == smb.SMB_SET_FILE_BASIC_INFO: 580 infoRecord = smb.SMBSetFileBasicInfo(data) 581 # Creation time won't be set, the other ones we play with. 582 atime = infoRecord['LastAccessTime'] 583 if atime == 0: 584 atime = -1 585 else: 586 atime = getUnixTime(atime) 587 mtime = infoRecord['LastWriteTime'] 588 if mtime == 0: 589 mtime = -1 590 else: 591 mtime = getUnixTime(mtime) 592 if mtime != -1 or atime != -1: 593 os.utime(pathName,(atime,mtime)) 594 else: 595 smbServer.log('Unknown level for set path info! 0x%x' % setPathInfoParameters['InformationLevel'], logging.ERROR) 596 # UNSUPPORTED 597 errorCode = STATUS_NOT_SUPPORTED 598 else: 599 errorCode = STATUS_OBJECT_NAME_NOT_FOUND 600 601 if errorCode == STATUS_SUCCESS: 602 respParameters = smb.SMBSetPathInformationResponse_Parameters() 603 604 else: 605 errorCode = STATUS_SMB_BAD_TID 606 607 smbServer.setConnectionData(connId, connData) 608 609 return respSetup, respParameters, respData, errorCode 610 611 612 @staticmethod 613 def setFileInformation(connId, smbServer, recvPacket, parameters, data, maxDataCount = 0): 614 connData = smbServer.getConnectionData(connId) 615 616 respSetup = '' 617 respParameters = '' 618 respData = '' 619 errorCode = STATUS_SUCCESS 620 setFileInfoParameters = smb.SMBSetFileInformation_Parameters(parameters) 621 622 if connData['ConnectedShares'].has_key(recvPacket['Tid']): 623 if connData['OpenedFiles'].has_key(setFileInfoParameters['FID']): 624 fileName = connData['OpenedFiles'][setFileInfoParameters['FID']]['FileName'] 625 informationLevel = setFileInfoParameters['InformationLevel'] 626 if informationLevel == smb.SMB_SET_FILE_DISPOSITION_INFO: 627 infoRecord = smb.SMBSetFileDispositionInfo(parameters) 628 if infoRecord['DeletePending'] > 0: 629 # Mark this file for removal after closed 630 connData['OpenedFiles'][setFileInfoParameters['FID']]['DeleteOnClose'] = True 631 respParameters = smb.SMBSetFileInformationResponse_Parameters() 632 elif informationLevel == smb.SMB_SET_FILE_BASIC_INFO: 633 infoRecord = smb.SMBSetFileBasicInfo(data) 634 # Creation time won't be set, the other ones we play with. 635 atime = infoRecord['LastAccessTime'] 636 if atime == 0: 637 atime = -1 638 else: 639 atime = getUnixTime(atime) 640 mtime = infoRecord['LastWriteTime'] 641 if mtime == 0: 642 mtime = -1 643 else: 644 mtime = getUnixTime(mtime) 645 os.utime(fileName,(atime,mtime)) 646 elif informationLevel == smb.SMB_SET_FILE_END_OF_FILE_INFO: 647 fileHandle = connData['OpenedFiles'][setFileInfoParameters['FID']]['FileHandle'] 648 infoRecord = smb.SMBSetFileEndOfFileInfo(data) 649 if infoRecord['EndOfFile'] > 0: 650 os.lseek(fileHandle, infoRecord['EndOfFile']-1, 0) 651 os.write(fileHandle, '\x00') 652 else: 653 smbServer.log('Unknown level for set file info! 0x%x' % setFileInfoParameters['InformationLevel'], logging.ERROR) 654 # UNSUPPORTED 655 errorCode = STATUS_NOT_SUPPORTED 656 else: 657 errorCode = STATUS_NO_SUCH_FILE 658 659 if errorCode == STATUS_SUCCESS: 660 respParameters = smb.SMBSetFileInformationResponse_Parameters() 661 else: 662 errorCode = STATUS_SMB_BAD_TID 663 664 smbServer.setConnectionData(connId, connData) 665 666 return respSetup, respParameters, respData, errorCode 667 668 @staticmethod 669 def queryFileInformation(connId, smbServer, recvPacket, parameters, data, maxDataCount = 0): 670 connData = smbServer.getConnectionData(connId) 671 672 respSetup = '' 673 respParameters = '' 674 respData = '' 675 676 queryFileInfoParameters = smb.SMBQueryFileInformation_Parameters(parameters) 677 678 if connData['ConnectedShares'].has_key(recvPacket['Tid']): 679 if connData['OpenedFiles'].has_key(queryFileInfoParameters['FID']): 680 fileName = connData['OpenedFiles'][queryFileInfoParameters['FID']]['FileName'] 681 682 infoRecord, errorCode = queryFileInformation('', fileName, queryFileInfoParameters['InformationLevel']) 683 684 if infoRecord is not None: 685 respParameters = smb.SMBQueryFileInformationResponse_Parameters() 686 respData = infoRecord 687 else: 688 errorCode = STATUS_INVALID_HANDLE 689 else: 690 errorCode = STATUS_SMB_BAD_TID 691 692 smbServer.setConnectionData(connId, connData) 693 694 return respSetup, respParameters, respData, errorCode 695 696 @staticmethod 697 def queryPathInformation(connId, smbServer, recvPacket, parameters, data, maxDataCount = 0): 698 connData = smbServer.getConnectionData(connId) 699 700 respSetup = '' 701 respParameters = '' 702 respData = '' 703 errorCode = 0 704 705 queryPathInfoParameters = smb.SMBQueryPathInformation_Parameters(flags = recvPacket['Flags2'], data = parameters) 706 707 if connData['ConnectedShares'].has_key(recvPacket['Tid']): 708 path = connData['ConnectedShares'][recvPacket['Tid']]['path'] 709 try: 710 infoRecord, errorCode = queryPathInformation(path, decodeSMBString(recvPacket['Flags2'], queryPathInfoParameters['FileName']), queryPathInfoParameters['InformationLevel']) 711 except Exception, e: 712 smbServer.log("queryPathInformation: %s" % e,logging.ERROR) 713 714 if infoRecord is not None: 715 respParameters = smb.SMBQueryPathInformationResponse_Parameters() 716 respData = infoRecord 717 else: 718 errorCode = STATUS_SMB_BAD_TID 719 720 smbServer.setConnectionData(connId, connData) 721 722 return respSetup, respParameters, respData, errorCode 723 724 @staticmethod 725 def queryFsInformation(connId, smbServer, recvPacket, parameters, data, maxDataCount = 0): 726 connData = smbServer.getConnectionData(connId) 727 errorCode = 0 728 # Get the Tid associated 729 if connData['ConnectedShares'].has_key(recvPacket['Tid']): 730 data = queryFsInformation(connData['ConnectedShares'][recvPacket['Tid']]['path'], '', struct.unpack('<H',parameters)[0]) 731 732 smbServer.setConnectionData(connId, connData) 733 734 return '','', data, errorCode 735 736 @staticmethod 737 def findNext2(connId, smbServer, recvPacket, parameters, data, maxDataCount): 738 connData = smbServer.getConnectionData(connId) 739 740 respSetup = '' 741 respParameters = '' 742 respData = '' 743 errorCode = STATUS_SUCCESS 744 findNext2Parameters = smb.SMBFindNext2_Parameters(flags = recvPacket['Flags2'], data = parameters) 745 746 sid = findNext2Parameters['SID'] 747 if connData['ConnectedShares'].has_key(recvPacket['Tid']): 748 if connData['SIDs'].has_key(sid): 749 searchResult = connData['SIDs'][sid] 750 respParameters = smb.SMBFindNext2Response_Parameters() 751 endOfSearch = 1 752 searchCount = 1 753 totalData = 0 754 for i in enumerate(searchResult): 755 data = i[1].getData() 756 lenData = len(data) 757 if (totalData+lenData) >= maxDataCount or (i[0]+1) >= findNext2Parameters['SearchCount']: 758 # We gotta stop here and continue on a find_next2 759 endOfSearch = 0 760 connData['SIDs'][sid] = searchResult[i[0]:] 761 respParameters['LastNameOffset'] = totalData 762 break 763 else: 764 searchCount +=1 765 respData += data 766 totalData += lenData 767 768 # Have we reached the end of the search or still stuff to send? 769 if endOfSearch > 0: 770 # Let's remove the SID from our ConnData 771 del(connData['SIDs'][sid]) 772 773 respParameters['EndOfSearch'] = endOfSearch 774 respParameters['SearchCount'] = searchCount 775 else: 776 errorCode = STATUS_INVALID_HANDLE 777 else: 778 errorCode = STATUS_SMB_BAD_TID 779 780 smbServer.setConnectionData(connId, connData) 781 782 return respSetup, respParameters, respData, errorCode 783 784 @staticmethod 785 def findFirst2(connId, smbServer, recvPacket, parameters, data, maxDataCount): 786 connData = smbServer.getConnectionData(connId) 787 788 respSetup = '' 789 respParameters = '' 790 respData = '' 791 findFirst2Parameters = smb.SMBFindFirst2_Parameters( recvPacket['Flags2'], data = parameters) 792 793 if connData['ConnectedShares'].has_key(recvPacket['Tid']): 794 path = connData['ConnectedShares'][recvPacket['Tid']]['path'] 795 796 searchResult, searchCount, errorCode = findFirst2(path, 797 decodeSMBString( recvPacket['Flags2'], findFirst2Parameters['FileName'] ), 798 findFirst2Parameters['InformationLevel'], 799 findFirst2Parameters['SearchAttributes'] ) 800 801 respParameters = smb.SMBFindFirst2Response_Parameters() 802 endOfSearch = 1 803 sid = 0x80 # default SID 804 searchCount = 0 805 totalData = 0 806 for i in enumerate(searchResult): 807 #i[1].dump() 808 data = i[1].getData() 809 lenData = len(data) 810 if (totalData+lenData) >= maxDataCount or (i[0]+1) > findFirst2Parameters['SearchCount']: 811 # We gotta stop here and continue on a find_next2 812 endOfSearch = 0 813 # Simple way to generate a fid 814 if len(connData['SIDs']) == 0: 815 sid = 1 816 else: 817 sid = connData['SIDs'].keys()[-1] + 1 818 # Store the remaining search results in the ConnData SID 819 connData['SIDs'][sid] = searchResult[i[0]:] 820 respParameters['LastNameOffset'] = totalData 821 break 822 else: 823 searchCount +=1 824 respData += data 825 826 padLen = (8-(lenData % 8)) %8 827 respData += '\xaa'*padLen 828 totalData += lenData + padLen 829 830 respParameters['SID'] = sid 831 respParameters['EndOfSearch'] = endOfSearch 832 respParameters['SearchCount'] = searchCount 833 else: 834 errorCode = STATUS_SMB_BAD_TID 835 836 smbServer.setConnectionData(connId, connData) 837 838 return respSetup, respParameters, respData, errorCode 839 840 # Here we implement the commands handlers 841 class SMBCommands: 842 843 @staticmethod 844 def smbTransaction(connId, smbServer, SMBCommand, recvPacket, transCommands): 845 connData = smbServer.getConnectionData(connId) 846 847 respSMBCommand = smb.SMBCommand(recvPacket['Command']) 848 849 transParameters= smb.SMBTransaction_Parameters(SMBCommand['Parameters']) 850 851 # Do the stuff 852 if transParameters['ParameterCount'] != transParameters['TotalParameterCount']: 853 # TODO: Handle partial parameters 854 raise Exception("Unsupported partial parameters in TRANSACT2!") 855 else: 856 transData = smb.SMBTransaction_SData(flags = recvPacket['Flags2']) 857 # Standard says servers shouldn't trust Parameters and Data comes 858 # in order, so we have to parse the offsets, ugly 859 860 paramCount = transParameters['ParameterCount'] 861 transData['Trans_ParametersLength'] = paramCount 862 dataCount = transParameters['DataCount'] 863 transData['Trans_DataLength'] = dataCount 864 transData.fromString(SMBCommand['Data']) 865 if transParameters['ParameterOffset'] > 0: 866 paramOffset = transParameters['ParameterOffset'] - 63 - transParameters['SetupLength'] 867 transData['Trans_Parameters'] = SMBCommand['Data'][paramOffset:paramOffset+paramCount] 868 else: 869 transData['Trans_Parameters'] = '' 870 871 if transParameters['DataOffset'] > 0: 872 dataOffset = transParameters['DataOffset'] - 63 - transParameters['SetupLength'] 873 transData['Trans_Data'] = SMBCommand['Data'][dataOffset:dataOffset + dataCount] 874 else: 875 transData['Trans_Data'] = '' 876 877 # Call the handler for this TRANSACTION 878 if transParameters['SetupCount'] == 0: 879 # No subcommand, let's play with the Name 880 command = decodeSMBString(recvPacket['Flags2'],transData['Name']) 881 else: 882 command = struct.unpack('<H', transParameters['Setup'][:2])[0] 883 884 if transCommands.has_key(command): 885 # Call the TRANS subcommand 886 setup = '' 887 parameters = '' 888 data = '' 889 try: 890 setup, parameters, data, errorCode = transCommands[command](connId, 891 smbServer, 892 recvPacket, 893 transData['Trans_Parameters'], 894 transData['Trans_Data'], 895 transParameters['MaxDataCount']) 896 except Exception, e: 897 #print 'Transaction: %s' % e,e 898 smbServer.log('Transaction: (%r,%s)' % (command, e), logging.ERROR) 899 errorCode = STATUS_ACCESS_DENIED 900 #raise 901 902 if setup == '' and parameters == '' and data == '': 903 # Something wen't wrong 904 respParameters = '' 905 respData = '' 906 else: 907 # Build the answer 908 data = str(data) 909 remainingData = len(data) 910 parameters = str(parameters) 911 remainingParameters = len(parameters) 912 commands = [] 913 dataDisplacement = 0 914 while remainingData > 0 or remainingParameters > 0: 915 respSMBCommand = smb.SMBCommand(recvPacket['Command']) 916 respParameters = smb.SMBTransactionResponse_Parameters() 917 respData = smb.SMBTransaction2Response_Data() 918 919 respParameters['TotalParameterCount'] = len(parameters) 920 respParameters['ParameterCount'] = len(parameters) 921 respData['Trans_ParametersLength'] = len(parameters) 922 respParameters['TotalDataCount'] = len(data) 923 respParameters['DataDisplacement'] = dataDisplacement 924 925 # TODO: Do the same for parameters 926 if len(data) > transParameters['MaxDataCount']: 927 # Answer doesn't fit in this packet 928 LOG.debug("Lowering answer from %d to %d" % (len(data),transParameters['MaxDataCount']) ) 929 respParameters['DataCount'] = transParameters['MaxDataCount'] 930 else: 931 respParameters['DataCount'] = len(data) 932 933 respData['Trans_DataLength'] = respParameters['DataCount'] 934 respParameters['SetupCount'] = len(setup) 935 respParameters['Setup'] = setup 936 # TODO: Make sure we're calculating the pad right 937 if len(parameters) > 0: 938 #padLen = 4 - (55 + len(setup)) % 4 939 padLen = (4 - (55 + len(setup)) % 4 ) % 4 940 padBytes = '\xFF' * padLen 941 respData['Pad1'] = padBytes 942 respParameters['ParameterOffset'] = 55 + len(setup) + padLen 943 else: 944 padLen = 0 945 respParameters['ParameterOffset'] = 0 946 respData['Pad1'] = '' 947 948 if len(data) > 0: 949 #pad2Len = 4 - (55 + len(setup) + padLen + len(parameters)) % 4 950 pad2Len = (4 - (55 + len(setup) + padLen + len(parameters)) % 4) % 4 951 respData['Pad2'] = '\xFF' * pad2Len 952 respParameters['DataOffset'] = 55 + len(setup) + padLen + len(parameters) + pad2Len 953 else: 954 respParameters['DataOffset'] = 0 955 respData['Pad2'] = '' 956 957 respData['Trans_Parameters'] = parameters[:respParameters['ParameterCount']] 958 respData['Trans_Data'] = data[:respParameters['DataCount']] 959 respSMBCommand['Parameters'] = respParameters 960 respSMBCommand['Data'] = respData 961 962 data = data[respParameters['DataCount']:] 963 remainingData -= respParameters['DataCount'] 964 dataDisplacement += respParameters['DataCount'] + 1 965 966 parameters = parameters[respParameters['ParameterCount']:] 967 remainingParameters -= respParameters['ParameterCount'] 968 commands.append(respSMBCommand) 969 970 smbServer.setConnectionData(connId, connData) 971 return commands, None, errorCode 972 973 else: 974 smbServer.log("Unsupported Transact command %r" % command, logging.ERROR) 975 respParameters = '' 976 respData = '' 977 errorCode = STATUS_NOT_IMPLEMENTED 978 979 respSMBCommand['Parameters'] = respParameters 980 respSMBCommand['Data'] = respData 981 smbServer.setConnectionData(connId, connData) 982 983 return [respSMBCommand], None, errorCode 984 985 986 @staticmethod 987 def smbNTTransact(connId, smbServer, SMBCommand, recvPacket, transCommands): 988 connData = smbServer.getConnectionData(connId) 989 990 respSMBCommand = smb.SMBCommand(recvPacket['Command']) 991 992 NTTransParameters= smb.SMBNTTransaction_Parameters(SMBCommand['Parameters']) 993 # Do the stuff 994 if NTTransParameters['ParameterCount'] != NTTransParameters['TotalParameterCount']: 995 # TODO: Handle partial parameters 996 raise Exception("Unsupported partial parameters in NTTrans!") 997 else: 998 NTTransData = smb.SMBNTTransaction_Data() 999 # Standard says servers shouldn't trust Parameters and Data comes 1000 # in order, so we have to parse the offsets, ugly 1001 1002 paramCount = NTTransParameters['ParameterCount'] 1003 NTTransData['NT_Trans_ParametersLength'] = paramCount 1004 dataCount = NTTransParameters['DataCount'] 1005 NTTransData['NT_Trans_DataLength'] = dataCount 1006 1007 if NTTransParameters['ParameterOffset'] > 0: 1008 paramOffset = NTTransParameters['ParameterOffset'] - 73 - NTTransParameters['SetupLength'] 1009 NTTransData['NT_Trans_Parameters'] = SMBCommand['Data'][paramOffset:paramOffset+paramCount] 1010 else: 1011 NTTransData['NT_Trans_Parameters'] = '' 1012 1013 if NTTransParameters['DataOffset'] > 0: 1014 dataOffset = NTTransParameters['DataOffset'] - 73 - NTTransParameters['SetupLength'] 1015 NTTransData['NT_Trans_Data'] = SMBCommand['Data'][dataOffset:dataOffset + dataCount] 1016 else: 1017 NTTransData['NT_Trans_Data'] = '' 1018 1019 # Call the handler for this TRANSACTION 1020 command = NTTransParameters['Function'] 1021 if transCommands.has_key(command): 1022 # Call the NT TRANS subcommand 1023 setup = '' 1024 parameters = '' 1025 data = '' 1026 try: 1027 setup, parameters, data, errorCode = transCommands[command](connId, 1028 smbServer, 1029 recvPacket, 1030 NTTransData['NT_Trans_Parameters'], 1031 NTTransData['NT_Trans_Data'], 1032 NTTransParameters['MaxDataCount']) 1033 except Exception, e: 1034 smbServer.log('NTTransaction: (0x%x,%s)' % (command, e), logging.ERROR) 1035 errorCode = STATUS_ACCESS_DENIED 1036 #raise 1037 1038 if setup == '' and parameters == '' and data == '': 1039 # Something wen't wrong 1040 respParameters = '' 1041 respData = '' 1042 if errorCode == STATUS_SUCCESS: 1043 errorCode = STATUS_ACCESS_DENIED 1044 else: 1045 # Build the answer 1046 data = str(data) 1047 remainingData = len(data) 1048 parameters = str(parameters) 1049 remainingParameters = len(parameters) 1050 commands = [] 1051 dataDisplacement = 0 1052 while remainingData > 0 or remainingParameters > 0: 1053 respSMBCommand = smb.SMBCommand(recvPacket['Command']) 1054 respParameters = smb.SMBNTTransactionResponse_Parameters() 1055 respData = smb.SMBNTTransactionResponse_Data() 1056 1057 respParameters['TotalParameterCount'] = len(parameters) 1058 respParameters['ParameterCount'] = len(parameters) 1059 respData['Trans_ParametersLength'] = len(parameters) 1060 respParameters['TotalDataCount'] = len(data) 1061 respParameters['DataDisplacement'] = dataDisplacement 1062 # TODO: Do the same for parameters 1063 if len(data) > NTTransParameters['MaxDataCount']: 1064 # Answer doesn't fit in this packet 1065 LOG.debug("Lowering answer from %d to %d" % (len(data),NTTransParameters['MaxDataCount']) ) 1066 respParameters['DataCount'] = NTTransParameters['MaxDataCount'] 1067 else: 1068 respParameters['DataCount'] = len(data) 1069 1070 respData['NT_Trans_DataLength'] = respParameters['DataCount'] 1071 respParameters['SetupCount'] = len(setup) 1072 respParameters['Setup'] = setup 1073 # TODO: Make sure we're calculating the pad right 1074 if len(parameters) > 0: 1075 #padLen = 4 - (71 + len(setup)) % 4 1076 padLen = (4 - (73 + len(setup)) % 4 ) % 4 1077 padBytes = '\xFF' * padLen 1078 respData['Pad1'] = padBytes 1079 respParameters['ParameterOffset'] = 73 + len(setup) + padLen 1080 else: 1081 padLen = 0 1082 respParameters['ParameterOffset'] = 0 1083 respData['Pad1'] = '' 1084 1085 if len(data) > 0: 1086 #pad2Len = 4 - (71 + len(setup) + padLen + len(parameters)) % 4 1087 pad2Len = (4 - (73 + len(setup) + padLen + len(parameters)) % 4) % 4 1088 respData['Pad2'] = '\xFF' * pad2Len 1089 respParameters['DataOffset'] = 73 + len(setup) + padLen + len(parameters) + pad2Len 1090 else: 1091 respParameters['DataOffset'] = 0 1092 respData['Pad2'] = '' 1093 1094 respData['NT_Trans_Parameters'] = parameters[:respParameters['ParameterCount']] 1095 respData['NT_Trans_Data'] = data[:respParameters['DataCount']] 1096 respSMBCommand['Parameters'] = respParameters 1097 respSMBCommand['Data'] = respData 1098 1099 data = data[respParameters['DataCount']:] 1100 remainingData -= respParameters['DataCount'] 1101 dataDisplacement += respParameters['DataCount'] + 1 1102 1103 parameters = parameters[respParameters['ParameterCount']:] 1104 remainingParameters -= respParameters['ParameterCount'] 1105 commands.append(respSMBCommand) 1106 1107 smbServer.setConnectionData(connId, connData) 1108 return commands, None, errorCode 1109 1110 else: 1111 #smbServer.log("Unsupported NTTransact command 0x%x" % command, logging.ERROR) 1112 respParameters = '' 1113 respData = '' 1114 errorCode = STATUS_NOT_IMPLEMENTED 1115 1116 respSMBCommand['Parameters'] = respParameters 1117 respSMBCommand['Data'] = respData 1118 1119 smbServer.setConnectionData(connId, connData) 1120 return [respSMBCommand], None, errorCode 1121 1122 1123 @staticmethod 1124 def smbTransaction2(connId, smbServer, SMBCommand, recvPacket, transCommands): 1125 connData = smbServer.getConnectionData(connId) 1126 1127 respSMBCommand = smb.SMBCommand(recvPacket['Command']) 1128 1129 trans2Parameters= smb.SMBTransaction2_Parameters(SMBCommand['Parameters']) 1130 1131 # Do the stuff 1132 if trans2Parameters['ParameterCount'] != trans2Parameters['TotalParameterCount']: 1133 # TODO: Handle partial parameters 1134 #print "Unsupported partial parameters in TRANSACT2!" 1135 raise Exception("Unsupported partial parameters in TRANSACT2!") 1136 else: 1137 trans2Data = smb.SMBTransaction2_Data() 1138 # Standard says servers shouldn't trust Parameters and Data comes 1139 # in order, so we have to parse the offsets, ugly 1140 1141 paramCount = trans2Parameters['ParameterCount'] 1142 trans2Data['Trans_ParametersLength'] = paramCount 1143 dataCount = trans2Parameters['DataCount'] 1144 trans2Data['Trans_DataLength'] = dataCount 1145 1146 if trans2Parameters['ParameterOffset'] > 0: 1147 paramOffset = trans2Parameters['ParameterOffset'] - 63 - trans2Parameters['SetupLength'] 1148 trans2Data['Trans_Parameters'] = SMBCommand['Data'][paramOffset:paramOffset+paramCount] 1149 else: 1150 trans2Data['Trans_Parameters'] = '' 1151 1152 if trans2Parameters['DataOffset'] > 0: 1153 dataOffset = trans2Parameters['DataOffset'] - 63 - trans2Parameters['SetupLength'] 1154 trans2Data['Trans_Data'] = SMBCommand['Data'][dataOffset:dataOffset + dataCount] 1155 else: 1156 trans2Data['Trans_Data'] = '' 1157 1158 # Call the handler for this TRANSACTION 1159 command = struct.unpack('<H', trans2Parameters['Setup'])[0] 1160 if transCommands.has_key(command): 1161 # Call the TRANS2 subcommand 1162 try: 1163 setup, parameters, data, errorCode = transCommands[command](connId, 1164 smbServer, 1165 recvPacket, 1166 trans2Data['Trans_Parameters'], 1167 trans2Data['Trans_Data'], 1168 trans2Parameters['MaxDataCount']) 1169 except Exception, e: 1170 smbServer.log('Transaction2: (0x%x,%s)' % (command, e), logging.ERROR) 1171 #import traceback 1172 #traceback.print_exc() 1173 raise 1174 1175 if setup == '' and parameters == '' and data == '': 1176 # Something wen't wrong 1177 respParameters = '' 1178 respData = '' 1179 else: 1180 # Build the answer 1181 data = str(data) 1182 remainingData = len(data) 1183 parameters = str(parameters) 1184 remainingParameters = len(parameters) 1185 commands = [] 1186 dataDisplacement = 0 1187 while remainingData > 0 or remainingParameters > 0: 1188 respSMBCommand = smb.SMBCommand(recvPacket['Command']) 1189 respParameters = smb.SMBTransaction2Response_Parameters() 1190 respData = smb.SMBTransaction2Response_Data() 1191 1192 respParameters['TotalParameterCount'] = len(parameters) 1193 respParameters['ParameterCount'] = len(parameters) 1194 respData['Trans_ParametersLength'] = len(parameters) 1195 respParameters['TotalDataCount'] = len(data) 1196 respParameters['DataDisplacement'] = dataDisplacement 1197 # TODO: Do the same for parameters 1198 if len(data) > trans2Parameters['MaxDataCount']: 1199 # Answer doesn't fit in this packet 1200 LOG.debug("Lowering answer from %d to %d" % (len(data),trans2Parameters['MaxDataCount']) ) 1201 respParameters['DataCount'] = trans2Parameters['MaxDataCount'] 1202 else: 1203 respParameters['DataCount'] = len(data) 1204 1205 respData['Trans_DataLength'] = respParameters['DataCount'] 1206 respParameters['SetupCount'] = len(setup) 1207 respParameters['Setup'] = setup 1208 # TODO: Make sure we're calculating the pad right 1209 if len(parameters) > 0: 1210 #padLen = 4 - (55 + len(setup)) % 4 1211 padLen = (4 - (55 + len(setup)) % 4 ) % 4 1212 padBytes = '\xFF' * padLen 1213 respData['Pad1'] = padBytes 1214 respParameters['ParameterOffset'] = 55 + len(setup) + padLen 1215 else: 1216 padLen = 0 1217 respParameters['ParameterOffset'] = 0 1218 respData['Pad1'] = '' 1219 1220 if len(data) > 0: 1221 #pad2Len = 4 - (55 + len(setup) + padLen + len(parameters)) % 4 1222 pad2Len = (4 - (55 + len(setup) + padLen + len(parameters)) % 4) % 4 1223 respData['Pad2'] = '\xFF' * pad2Len 1224 respParameters['DataOffset'] = 55 + len(setup) + padLen + len(parameters) + pad2Len 1225 else: 1226 respParameters['DataOffset'] = 0 1227 respData['Pad2'] = '' 1228 1229 respData['Trans_Parameters'] = parameters[:respParameters['ParameterCount']] 1230 respData['Trans_Data'] = data[:respParameters['DataCount']] 1231 respSMBCommand['Parameters'] = respParameters 1232 respSMBCommand['Data'] = respData 1233 1234 data = data[respParameters['DataCount']:] 1235 remainingData -= respParameters['DataCount'] 1236 dataDisplacement += respParameters['DataCount'] + 1 1237 1238 parameters = parameters[respParameters['ParameterCount']:] 1239 remainingParameters -= respParameters['ParameterCount'] 1240 commands.append(respSMBCommand) 1241 1242 smbServer.setConnectionData(connId, connData) 1243 return commands, None, errorCode 1244 1245 else: 1246 smbServer.log("Unsupported Transact/2 command 0x%x" % command, logging.ERROR) 1247 respParameters = '' 1248 respData = '' 1249 errorCode = STATUS_NOT_IMPLEMENTED 1250 1251 respSMBCommand['Parameters'] = respParameters 1252 respSMBCommand['Data'] = respData 1253 1254 smbServer.setConnectionData(connId, connData) 1255 return [respSMBCommand], None, errorCode 1256 1257 @staticmethod 1258 def smbComLockingAndX(connId, smbServer, SMBCommand, recvPacket): 1259 connData = smbServer.getConnectionData(connId) 1260 1261 respSMBCommand = smb.SMBCommand(smb.SMB.SMB_COM_LOCKING_ANDX) 1262 respParameters = '' 1263 respData = '' 1264 1265 # I'm actually doing nothing.. just make MacOS happy ;) 1266 errorCode = STATUS_SUCCESS 1267 1268 respSMBCommand['Parameters'] = respParameters 1269 respSMBCommand['Data'] = respData 1270 smbServer.setConnectionData(connId, connData) 1271 1272 return [respSMBCommand], None, errorCode 1273 1274 1275 @staticmethod 1276 def smbComClose(connId, smbServer, SMBCommand, recvPacket): 1277 connData = smbServer.getConnectionData(connId) 1278 1279 respSMBCommand = smb.SMBCommand(smb.SMB.SMB_COM_CLOSE) 1280 respParameters = '' 1281 respData = '' 1282 1283 comClose = smb.SMBClose_Parameters(SMBCommand['Parameters']) 1284 1285 if connData['OpenedFiles'].has_key(comClose['FID']): 1286 errorCode = STATUS_SUCCESS 1287 fileHandle = connData['OpenedFiles'][comClose['FID']]['FileHandle'] 1288 try: 1289 if fileHandle == PIPE_FILE_DESCRIPTOR: 1290 connData['OpenedFiles'][comClose['FID']]['Socket'].close() 1291 elif fileHandle != VOID_FILE_DESCRIPTOR: 1292 os.close(fileHandle) 1293 except Exception, e: 1294 smbServer.log("comClose %s" % e, logging.ERROR) 1295 errorCode = STATUS_ACCESS_DENIED 1296 else: 1297 # Check if the file was marked for removal 1298 if connData['OpenedFiles'][comClose['FID']]['DeleteOnClose'] is True: 1299 try: 1300 os.remove(connData['OpenedFiles'][comClose['FID']]['FileName']) 1301 except Exception, e: 1302 smbServer.log("comClose %s" % e, logging.ERROR) 1303 errorCode = STATUS_ACCESS_DENIED 1304 del(connData['OpenedFiles'][comClose['FID']]) 1305 else: 1306 errorCode = STATUS_INVALID_HANDLE 1307 1308 if errorCode > 0: 1309 respParameters = '' 1310 respData = '' 1311 1312 respSMBCommand['Parameters'] = respParameters 1313 respSMBCommand['Data'] = respData 1314 smbServer.setConnectionData(connId, connData) 1315 1316 return [respSMBCommand], None, errorCode 1317 1318 @staticmethod 1319 def smbComWrite(connId, smbServer, SMBCommand, recvPacket): 1320 connData = smbServer.getConnectionData(connId) 1321 1322 respSMBCommand = smb.SMBCommand(smb.SMB.SMB_COM_WRITE) 1323 respParameters = smb.SMBWriteResponse_Parameters() 1324 respData = '' 1325 1326 comWriteParameters = smb.SMBWrite_Parameters(SMBCommand['Parameters']) 1327 comWriteData = smb.SMBWrite_Data(SMBCommand['Data']) 1328 1329 if connData['OpenedFiles'].has_key(comWriteParameters['Fid']): 1330 fileHandle = connData['OpenedFiles'][comWriteParameters['Fid']]['FileHandle'] 1331 errorCode = STATUS_SUCCESS 1332 try: 1333 if fileHandle != PIPE_FILE_DESCRIPTOR: 1334 # TODO: Handle big size files 1335 # If we're trying to write past the file end we just skip the write call (Vista does this) 1336 if os.lseek(fileHandle, 0, 2) >= comWriteParameters['Offset']: 1337 os.lseek(fileHandle,comWriteParameters['Offset'],0) 1338 os.write(fileHandle,comWriteData['Data']) 1339 else: 1340 sock = connData['OpenedFiles'][comWriteParameters['Fid']]['Socket'] 1341 sock.send(comWriteData['Data']) 1342 respParameters['Count'] = comWriteParameters['Count'] 1343 except Exception, e: 1344 smbServer.log('smbComWrite: %s' % e, logging.ERROR) 1345 errorCode = STATUS_ACCESS_DENIED 1346 else: 1347 errorCode = STATUS_INVALID_HANDLE 1348 1349 1350 if errorCode > 0: 1351 respParameters = '' 1352 respData = '' 1353 1354 respSMBCommand['Parameters'] = respParameters 1355 respSMBCommand['Data'] = respData 1356 smbServer.setConnectionData(connId, connData) 1357 1358 return [respSMBCommand], None, errorCode 1359 1360 @staticmethod 1361 def smbComFlush(connId, smbServer, SMBCommand,recvPacket ): 1362 connData = smbServer.getConnectionData(connId) 1363 1364 respSMBCommand = smb.SMBCommand(smb.SMB.SMB_COM_FLUSH) 1365 respParameters = '' 1366 respData = '' 1367 1368 comFlush = smb.SMBFlush_Parameters(SMBCommand['Parameters']) 1369 1370 if connData['OpenedFiles'].has_key(comFlush['FID']): 1371 errorCode = STATUS_SUCCESS 1372 fileHandle = connData['OpenedFiles'][comFlush['FID']]['FileHandle'] 1373 try: 1374 os.fsync(fileHandle) 1375 except Exception, e: 1376 smbServer.log("comFlush %s" % e, logging.ERROR) 1377 errorCode = STATUS_ACCESS_DENIED 1378 else: 1379 errorCode = STATUS_INVALID_HANDLE 1380 1381 if errorCode > 0: 1382 respParameters = '' 1383 respData = '' 1384 1385 respSMBCommand['Parameters'] = respParameters 1386 respSMBCommand['Data'] = respData 1387 smbServer.setConnectionData(connId, connData) 1388 1389 return [respSMBCommand], None, errorCode 1390 1391 1392 @staticmethod 1393 def smbComCreateDirectory(connId, smbServer, SMBCommand,recvPacket ): 1394 connData = smbServer.getConnectionData(connId) 1395 1396 respSMBCommand = smb.SMBCommand(smb.SMB.SMB_COM_CREATE_DIRECTORY) 1397 respParameters = '' 1398 respData = '' 1399 1400 comCreateDirectoryData= smb.SMBCreateDirectory_Data(flags = recvPacket['Flags2'], data = SMBCommand['Data']) 1401 1402 # Get the Tid associated 1403 if connData['ConnectedShares'].has_key(recvPacket['Tid']): 1404 errorCode = STATUS_SUCCESS 1405 path = connData['ConnectedShares'][recvPacket['Tid']]['path'] 1406 fileName = os.path.normpath(decodeSMBString(recvPacket['Flags2'],comCreateDirectoryData['DirectoryName']).replace('\\','/')) 1407 if len(fileName) > 0: 1408 if fileName[0] == '/' or fileName[0] == '\\': 1409 # strip leading '/' 1410 fileName = fileName[1:] 1411 pathName = os.path.join(path,fileName) 1412 if os.path.exists(pathName): 1413 errorCode = STATUS_OBJECT_NAME_COLLISION 1414 1415 # TODO: More checks here in the future.. Specially when we support 1416 # user access 1417 else: 1418 try: 1419 os.mkdir(pathName) 1420 except Exception, e: 1421 smbServer.log("smbComCreateDirectory: %s" % e, logging.ERROR) 1422 errorCode = STATUS_ACCESS_DENIED 1423 else: 1424 errorCode = STATUS_SMB_BAD_TID 1425 1426 1427 if errorCode > 0: 1428 respParameters = '' 1429 respData = '' 1430 1431 respSMBCommand['Parameters'] = respParameters 1432 respSMBCommand['Data'] = respData 1433 smbServer.setConnectionData(connId, connData) 1434 1435 return [respSMBCommand], None, errorCode 1436 1437 @staticmethod 1438 def smbComRename(connId, smbServer, SMBCommand, recvPacket ): 1439 connData = smbServer.getConnectionData(connId) 1440 1441 respSMBCommand = smb.SMBCommand(smb.SMB.SMB_COM_RENAME) 1442 respParameters = '' 1443 respData = '' 1444 1445 comRenameData = smb.SMBRename_Data(flags = recvPacket['Flags2'], data = SMBCommand['Data']) 1446 # Get the Tid associated 1447 if connData['ConnectedShares'].has_key(recvPacket['Tid']): 1448 errorCode = STATUS_SUCCESS 1449 path = connData['ConnectedShares'][recvPacket['Tid']]['path'] 1450 oldFileName = os.path.normpath(decodeSMBString(recvPacket['Flags2'],comRenameData['OldFileName']).replace('\\','/')) 1451 newFileName = os.path.normpath(decodeSMBString(recvPacket['Flags2'],comRenameData['NewFileName']).replace('\\','/')) 1452 if len(oldFileName) > 0 and (oldFileName[0] == '/' or oldFileName[0] == '\\'): 1453 # strip leading '/' 1454 oldFileName = oldFileName[1:] 1455 oldPathName = os.path.join(path,oldFileName) 1456 if len(newFileName) > 0 and (newFileName[0] == '/' or newFileName[0] == '\\'): 1457 # strip leading '/' 1458 newFileName = newFileName[1:] 1459 newPathName = os.path.join(path,newFileName) 1460 1461 if os.path.exists(oldPathName) is not True: 1462 errorCode = STATUS_NO_SUCH_FILE 1463 1464 # TODO: More checks here in the future.. Specially when we support 1465 # user access 1466 else: 1467 try: 1468 os.rename(oldPathName,newPathName) 1469 except OSError, e: 1470 smbServer.log("smbComRename: %s" % e, logging.ERROR) 1471 errorCode = STATUS_ACCESS_DENIED 1472 else: 1473 errorCode = STATUS_SMB_BAD_TID 1474 1475 1476 if errorCode > 0: 1477 respParameters = '' 1478 respData = '' 1479 1480 respSMBCommand['Parameters'] = respParameters 1481 respSMBCommand['Data'] = respData 1482 smbServer.setConnectionData(connId, connData) 1483 1484 return [respSMBCommand], None, errorCode 1485 1486 @staticmethod 1487 def smbComDelete(connId, smbServer, SMBCommand, recvPacket ): 1488 connData = smbServer.getConnectionData(connId) 1489 1490 respSMBCommand = smb.SMBCommand(smb.SMB.SMB_COM_DELETE) 1491 respParameters = '' 1492 respData = '' 1493 1494 comDeleteData = smb.SMBDelete_Data(flags = recvPacket['Flags2'], data = SMBCommand['Data']) 1495 1496 # Get the Tid associated 1497 if connData['ConnectedShares'].has_key(recvPacket['Tid']): 1498 errorCode = STATUS_SUCCESS 1499 path = connData['ConnectedShares'][recvPacket['Tid']]['path'] 1500 fileName = os.path.normpath(decodeSMBString(recvPacket['Flags2'],comDeleteData['FileName']).replace('\\','/')) 1501 if len(fileName) > 0 and (fileName[0] == '/' or fileName[0] == '\\'): 1502 # strip leading '/' 1503 fileName = fileName[1:] 1504 pathName = os.path.join(path,fileName) 1505 if os.path.exists(pathName) is not True: 1506 errorCode = STATUS_NO_SUCH_FILE 1507 1508 # TODO: More checks here in the future.. Specially when we support 1509 # user access 1510 else: 1511 try: 1512 os.remove(pathName) 1513 except OSError, e: 1514 smbServer.log("smbComDelete: %s" % e, logging.ERROR) 1515 errorCode = STATUS_ACCESS_DENIED 1516 else: 1517 errorCode = STATUS_SMB_BAD_TID 1518 1519 if errorCode > 0: 1520 respParameters = '' 1521 respData = '' 1522 1523 respSMBCommand['Parameters'] = respParameters 1524 respSMBCommand['Data'] = respData 1525 smbServer.setConnectionData(connId, connData) 1526 1527 return [respSMBCommand], None, errorCode 1528 1529 1530 @staticmethod 1531 def smbComDeleteDirectory(connId, smbServer, SMBCommand, recvPacket ): 1532 connData = smbServer.getConnectionData(connId) 1533 1534 respSMBCommand = smb.SMBCommand(smb.SMB.SMB_COM_DELETE_DIRECTORY) 1535 respParameters = '' 1536 respData = '' 1537 1538 comDeleteDirectoryData= smb.SMBDeleteDirectory_Data(flags = recvPacket['Flags2'], data = SMBCommand['Data']) 1539 1540 # Get the Tid associated 1541 if connData['ConnectedShares'].has_key(recvPacket['Tid']): 1542 errorCode = STATUS_SUCCESS 1543 path = connData['ConnectedShares'][recvPacket['Tid']]['path'] 1544 fileName = os.path.normpath(decodeSMBString(recvPacket['Flags2'],comDeleteDirectoryData['DirectoryName']).replace('\\','/')) 1545 if len(fileName) > 0 and (fileName[0] == '/' or fileName[0] == '\\'): 1546 # strip leading '/' 1547 fileName = fileName[1:] 1548 pathName = os.path.join(path,fileName) 1549 if os.path.exists(pathName) is not True: 1550 errorCode = STATUS_NO_SUCH_FILE 1551 1552 # TODO: More checks here in the future.. Specially when we support 1553 # user access 1554 else: 1555 try: 1556 os.rmdir(pathName) 1557 except OSError, e: 1558 smbServer.log("smbComDeleteDirectory: %s" % e,logging.ERROR) 1559 if e.errno == errno.ENOTEMPTY: 1560 errorCode = STATUS_DIRECTORY_NOT_EMPTY 1561 else: 1562 errorCode = STATUS_ACCESS_DENIED 1563 else: 1564 errorCode = STATUS_SMB_BAD_TID 1565 1566 if errorCode > 0: 1567 respParameters = '' 1568 respData = '' 1569 1570 respSMBCommand['Parameters'] = respParameters 1571 respSMBCommand['Data'] = respData 1572 smbServer.setConnectionData(connId, connData) 1573 1574 return [respSMBCommand], None, errorCode 1575 1576 1577 @staticmethod 1578 def smbComWriteAndX(connId, smbServer, SMBCommand, recvPacket): 1579 connData = smbServer.getConnectionData(connId) 1580 1581 respSMBCommand = smb.SMBCommand(smb.SMB.SMB_COM_WRITE_ANDX) 1582 respParameters = smb.SMBWriteAndXResponse_Parameters() 1583 respData = '' 1584 1585 if SMBCommand['WordCount'] == 0x0C: 1586 writeAndX = smb.SMBWriteAndX_Parameters_Short(SMBCommand['Parameters']) 1587 writeAndXData = smb.SMBWriteAndX_Data_Short() 1588 else: 1589 writeAndX = smb.SMBWriteAndX_Parameters(SMBCommand['Parameters']) 1590 writeAndXData = smb.SMBWriteAndX_Data() 1591 writeAndXData['DataLength'] = writeAndX['DataLength'] 1592 writeAndXData['DataOffset'] = writeAndX['DataOffset'] 1593 writeAndXData.fromString(SMBCommand['Data']) 1594 1595 1596 if connData['OpenedFiles'].has_key(writeAndX['Fid']): 1597 fileHandle = connData['OpenedFiles'][writeAndX['Fid']]['FileHandle'] 1598 errorCode = STATUS_SUCCESS 1599 try: 1600 if fileHandle != PIPE_FILE_DESCRIPTOR: 1601 offset = writeAndX['Offset'] 1602 if writeAndX.fields.has_key('HighOffset'): 1603 offset += (writeAndX['HighOffset'] << 32) 1604 # If we're trying to write past the file end we just skip the write call (Vista does this) 1605 if os.lseek(fileHandle, 0, 2) >= offset: 1606 os.lseek(fileHandle,offset,0) 1607 os.write(fileHandle,writeAndXData['Data']) 1608 else: 1609 sock = connData['OpenedFiles'][writeAndX['Fid']]['Socket'] 1610 sock.send(writeAndXData['Data']) 1611 1612 respParameters['Count'] = writeAndX['DataLength'] 1613 respParameters['Available']= 0xff 1614 except Exception, e: 1615 smbServer.log('smbComWriteAndx: %s' % e, logging.ERROR) 1616 errorCode = STATUS_ACCESS_DENIED 1617 else: 1618 errorCode = STATUS_INVALID_HANDLE 1619 1620 if errorCode > 0: 1621 respParameters = '' 1622 respData = '' 1623 1624 respSMBCommand['Parameters'] = respParameters 1625 respSMBCommand['Data'] = respData 1626 smbServer.setConnectionData(connId, connData) 1627 1628 return [respSMBCommand], None, errorCode 1629 1630 @staticmethod 1631 def smbComRead(connId, smbServer, SMBCommand, recvPacket): 1632 connData = smbServer.getConnectionData(connId) 1633 1634 respSMBCommand = smb.SMBCommand(smb.SMB.SMB_COM_READ) 1635 respParameters = smb.SMBReadResponse_Parameters() 1636 respData = smb.SMBReadResponse_Data() 1637 1638 comReadParameters = smb.SMBRead_Parameters(SMBCommand['Parameters']) 1639 1640 if connData['OpenedFiles'].has_key(comReadParameters['Fid']): 1641 fileHandle = connData['OpenedFiles'][comReadParameters['Fid']]['FileHandle'] 1642 errorCode = STATUS_SUCCESS 1643 try: 1644 if fileHandle != PIPE_FILE_DESCRIPTOR: 1645 # TODO: Handle big size files 1646 os.lseek(fileHandle,comReadParameters['Offset'],0) 1647 content = os.read(fileHandle,comReadParameters['Count']) 1648 else: 1649 sock = connData['OpenedFiles'][comReadParameters['Fid']]['Socket'] 1650 content = sock.recv(comReadParameters['Count']) 1651 respParameters['Count'] = len(content) 1652 respData['DataLength'] = len(content) 1653 respData['Data'] = content 1654 except Exception, e: 1655 smbServer.log('smbComRead: %s ' % e, logging.ERROR) 1656 errorCode = STATUS_ACCESS_DENIED 1657 else: 1658 errorCode = STATUS_INVALID_HANDLE 1659 1660 if errorCode > 0: 1661 respParameters = '' 1662 respData = '' 1663 1664 respSMBCommand['Parameters'] = respParameters 1665 respSMBCommand['Data'] = respData 1666 smbServer.setConnectionData(connId, connData) 1667 1668 return [respSMBCommand], None, errorCode 1669 1670 @staticmethod 1671 def smbComReadAndX(connId, smbServer, SMBCommand, recvPacket): 1672 connData = smbServer.getConnectionData(connId) 1673 1674 respSMBCommand = smb.SMBCommand(smb.SMB.SMB_COM_READ_ANDX) 1675 respParameters = smb.SMBReadAndXResponse_Parameters() 1676 respData = '' 1677 1678 if SMBCommand['WordCount'] == 0x0A: 1679 readAndX = smb.SMBReadAndX_Parameters2(SMBCommand['Parameters']) 1680 else: 1681 readAndX = smb.SMBReadAndX_Parameters(SMBCommand['Parameters']) 1682 1683 if connData['OpenedFiles'].has_key(readAndX['Fid']): 1684 fileHandle = connData['OpenedFiles'][readAndX['Fid']]['FileHandle'] 1685 errorCode = 0 1686 try: 1687 if fileHandle != PIPE_FILE_DESCRIPTOR: 1688 offset = readAndX['Offset'] 1689 if readAndX.fields.has_key('HighOffset'): 1690 offset += (readAndX['HighOffset'] << 32) 1691 os.lseek(fileHandle,offset,0) 1692 content = os.read(fileHandle,readAndX['MaxCount']) 1693 else: 1694 sock = connData['OpenedFiles'][readAndX['Fid']]['Socket'] 1695 content = sock.recv(readAndX['MaxCount']) 1696 respParameters['Remaining'] = 0xffff 1697 respParameters['DataCount'] = len(content) 1698 respParameters['DataOffset'] = 59 1699 respParameters['DataCount_Hi'] = 0 1700 respData = content 1701 except Exception, e: 1702 smbServer.log('smbComReadAndX: %s ' % e, logging.ERROR) 1703 errorCode = STATUS_ACCESS_DENIED 1704 else: 1705 errorCode = STATUS_INVALID_HANDLE 1706 1707 if errorCode > 0: 1708 respParameters = '' 1709 respData = '' 1710 1711 respSMBCommand['Parameters'] = respParameters 1712 respSMBCommand['Data'] = respData 1713 smbServer.setConnectionData(connId, connData) 1714 1715 return [respSMBCommand], None, errorCode 1716 1717 @staticmethod 1718 def smbQueryInformation(connId, smbServer, SMBCommand, recvPacket): 1719 connData = smbServer.getConnectionData(connId) 1720 1721 respSMBCommand = smb.SMBCommand(smb.SMB.SMB_COM_QUERY_INFORMATION) 1722 respParameters = smb.SMBQueryInformationResponse_Parameters() 1723 respData = '' 1724 1725 queryInformation= smb.SMBQueryInformation_Data(flags = recvPacket['Flags2'], data = SMBCommand['Data']) 1726 1727 # Get the Tid associated 1728 if connData['ConnectedShares'].has_key(recvPacket['Tid']): 1729 fileSize, lastWriteTime, fileAttributes = queryFsInformation( 1730 connData['ConnectedShares'][recvPacket['Tid']]['path'], 1731 decodeSMBString(recvPacket['Flags2'],queryInformation['FileName'])) 1732 1733 respParameters['FileSize'] = fileSize 1734 respParameters['LastWriteTime'] = lastWriteTime 1735 respParameters['FileAttributes'] = fileAttributes 1736 errorCode = STATUS_SUCCESS 1737 else: 1738 # STATUS_SMB_BAD_TID 1739 errorCode = STATUS_SMB_BAD_TID 1740 respParameters = '' 1741 respData = '' 1742 1743 respSMBCommand['Parameters'] = respParameters 1744 respSMBCommand['Data'] = respData 1745 1746 smbServer.setConnectionData(connId, connData) 1747 return [respSMBCommand], None, errorCode 1748 1749 @staticmethod 1750 def smbQueryInformationDisk(connId, smbServer, SMBCommand, recvPacket): 1751 connData = smbServer.getConnectionData(connId) 1752 1753 respSMBCommand = smb.SMBCommand(smb.SMB.SMB_COM_QUERY_INFORMATION_DISK) 1754 respParameters = smb.SMBQueryInformationDiskResponse_Parameters() 1755 respData = '' 1756 1757 # Get the Tid associated 1758 if connData['ConnectedShares'].has_key(recvPacket['Tid']): 1759 totalUnits, freeUnits = queryDiskInformation( 1760 connData['ConnectedShares'][recvPacket['Tid']]['path']) 1761 1762 respParameters['TotalUnits'] = totalUnits 1763 respParameters['BlocksPerUnit'] = 1 1764 respParameters['BlockSize'] = 1 1765 respParameters['FreeUnits'] = freeUnits 1766 errorCode = STATUS_SUCCESS 1767 else: 1768 # STATUS_SMB_BAD_TID 1769 respData = '' 1770 respParameters = '' 1771 errorCode = STATUS_SMB_BAD_TID 1772 1773 1774 respSMBCommand['Parameters'] = respParameters 1775 respSMBCommand['Data'] = respData 1776 1777 smbServer.setConnectionData(connId, connData) 1778 return [respSMBCommand], None, errorCode 1779 1780 @staticmethod 1781 def smbComEcho(connId, smbServer, SMBCommand, recvPacket): 1782 connData = smbServer.getConnectionData(connId) 1783 1784 respSMBCommand = smb.SMBCommand(smb.SMB.SMB_COM_ECHO) 1785 respParameters = smb.SMBEchoResponse_Parameters() 1786 respData = smb.SMBEchoResponse_Data() 1787 1788 echoData = smb.SMBEcho_Data(SMBCommand['Data']) 1789 1790 respParameters['SequenceNumber'] = 1 1791 respData['Data'] = echoData['Data'] 1792 1793 respSMBCommand['Parameters'] = respParameters 1794 respSMBCommand['Data'] = respData 1795 1796 errorCode = STATUS_SUCCESS 1797 smbServer.setConnectionData(connId, connData) 1798 return [respSMBCommand], None, errorCode 1799 1800 @staticmethod 1801 def smbComTreeDisconnect(connId, smbServer, SMBCommand, recvPacket): 1802 connData = smbServer.getConnectionData(connId) 1803 1804 respSMBCommand = smb.SMBCommand(smb.SMB.SMB_COM_TREE_DISCONNECT) 1805 1806 # Check if the Tid matches the Tid trying to disconnect 1807 respParameters = '' 1808 respData = '' 1809 1810 if connData['ConnectedShares'].has_key(recvPacket['Tid']): 1811 smbServer.log("Disconnecting Share(%d:%s)" % (recvPacket['Tid'],connData['ConnectedShares'][recvPacket['Tid']]['shareName'])) 1812 del(connData['ConnectedShares'][recvPacket['Tid']]) 1813 errorCode = STATUS_SUCCESS 1814 else: 1815 # STATUS_SMB_BAD_TID 1816 errorCode = STATUS_SMB_BAD_TID 1817 1818 respSMBCommand['Parameters'] = respParameters 1819 respSMBCommand['Data'] = respData 1820 1821 smbServer.setConnectionData(connId, connData) 1822 return [respSMBCommand], None, errorCode 1823 1824 @staticmethod 1825 def smbComLogOffAndX(connId, smbServer, SMBCommand, recvPacket): 1826 connData = smbServer.getConnectionData(connId) 1827 1828 respSMBCommand = smb.SMBCommand(smb.SMB.SMB_COM_LOGOFF_ANDX) 1829 1830 # Check if the Uid matches the user trying to logoff 1831 respParameters = '' 1832 respData = '' 1833 if recvPacket['Uid'] != connData['Uid']: 1834 # STATUS_SMB_BAD_UID 1835 errorCode = STATUS_SMB_BAD_UID 1836 else: 1837 errorCode = STATUS_SUCCESS 1838 1839 respSMBCommand['Parameters'] = respParameters 1840 respSMBCommand['Data'] = respData 1841 connData['Uid'] = 0 1842 1843 smbServer.setConnectionData(connId, connData) 1844 1845 return [respSMBCommand], None, errorCode 1846 1847 @staticmethod 1848 def smbComQueryInformation2(connId, smbServer, SMBCommand, recvPacket): 1849 connData = smbServer.getConnectionData(connId) 1850 1851 respSMBCommand = smb.SMBCommand(smb.SMB.SMB_COM_QUERY_INFORMATION2) 1852 respParameters = smb.SMBQueryInformation2Response_Parameters() 1853 respData = '' 1854 1855 queryInformation2 = smb.SMBQueryInformation2_Parameters(SMBCommand['Parameters']) 1856 errorCode = 0xFF 1857 if connData['OpenedFiles'].has_key(queryInformation2['Fid']): 1858 errorCode = STATUS_SUCCESS 1859 pathName = connData['OpenedFiles'][queryInformation2['Fid']]['FileName'] 1860 try: 1861 (mode, ino, dev, nlink, uid, gid, size, atime, mtime, ctime) = os.stat(pathName) 1862 respParameters['CreateDate'] = getSMBDate(ctime) 1863 respParameters['CreationTime'] = getSMBTime(ctime) 1864 respParameters['LastAccessDate'] = getSMBDate(atime) 1865 respParameters['LastAccessTime'] = getSMBTime(atime) 1866 respParameters['LastWriteDate'] = getSMBDate(mtime) 1867 respParameters['LastWriteTime'] = getSMBTime(mtime) 1868 respParameters['FileDataSize'] = size 1869 respParameters['FileAllocationSize'] = size 1870 attribs = 0 1871 if os.path.isdir(pathName): 1872 attribs = smb.SMB_FILE_ATTRIBUTE_DIRECTORY 1873 if os.path.isfile(pathName): 1874 attribs = smb.SMB_FILE_ATTRIBUTE_NORMAL 1875 respParameters['FileAttributes'] = attribs 1876 except Exception, e: 1877 smbServer.log('smbComQueryInformation2 %s' % e,logging.ERROR) 1878 errorCode = STATUS_ACCESS_DENIED 1879 1880 if errorCode > 0: 1881 respParameters = '' 1882 respData = '' 1883 1884 respSMBCommand['Parameters'] = respParameters 1885 respSMBCommand['Data'] = respData 1886 smbServer.setConnectionData(connId, connData) 1887 1888 return [respSMBCommand], None, errorCode 1889 1890 @staticmethod 1891 def smbComNtCreateAndX(connId, smbServer, SMBCommand, recvPacket): 1892 # TODO: Fully implement this 1893 connData = smbServer.getConnectionData(connId) 1894 1895 respSMBCommand = smb.SMBCommand(smb.SMB.SMB_COM_NT_CREATE_ANDX) 1896 respParameters = smb.SMBNtCreateAndXResponse_Parameters() 1897 respData = '' 1898 1899 ntCreateAndXParameters = smb.SMBNtCreateAndX_Parameters(SMBCommand['Parameters']) 1900 ntCreateAndXData = smb.SMBNtCreateAndX_Data( flags = recvPacket['Flags2'], data = SMBCommand['Data']) 1901 1902 #if ntCreateAndXParameters['CreateFlags'] & 0x10: # NT_CREATE_REQUEST_EXTENDED_RESPONSE 1903 # respParameters = smb.SMBNtCreateAndXExtendedResponse_Parameters() 1904 # respParameters['VolumeGUID'] = '\x00' 1905 1906 # Get the Tid associated 1907 if connData['ConnectedShares'].has_key(recvPacket['Tid']): 1908 # If we have a rootFid, the path is relative to that fid 1909 errorCode = STATUS_SUCCESS 1910 if ntCreateAndXParameters['RootFid'] > 0: 1911 path = connData['OpenedFiles'][ntCreateAndXParameters['RootFid']]['FileName'] 1912 LOG.debug("RootFid present %s!" % path) 1913 else: 1914 if connData['ConnectedShares'][recvPacket['Tid']].has_key('path'): 1915 path = connData['ConnectedShares'][recvPacket['Tid']]['path'] 1916 else: 1917 path = 'NONE' 1918 errorCode = STATUS_ACCESS_DENIED 1919 1920 deleteOnClose = False 1921 1922 fileName = os.path.normpath(decodeSMBString(recvPacket['Flags2'],ntCreateAndXData['FileName']).replace('\\','/')) 1923 if len(fileName) > 0 and (fileName[0] == '/' or fileName[0] == '\\'): 1924 # strip leading '/' 1925 fileName = fileName[1:] 1926 pathName = os.path.join(path,fileName) 1927 createDisposition = ntCreateAndXParameters['Disposition'] 1928 mode = 0 1929 1930 if createDisposition == smb.FILE_SUPERSEDE: 1931 mode |= os.O_TRUNC | os.O_CREAT 1932 elif createDisposition & smb.FILE_OVERWRITE_IF == smb.FILE_OVERWRITE_IF: 1933 mode |= os.O_TRUNC | os.O_CREAT 1934 elif createDisposition & smb.FILE_OVERWRITE == smb.FILE_OVERWRITE: 1935 if os.path.exists(pathName) is True: 1936 mode |= os.O_TRUNC 1937 else: 1938 errorCode = STATUS_NO_SUCH_FILE 1939 elif createDisposition & smb.FILE_OPEN_IF == smb.FILE_OPEN_IF: 1940 if os.path.exists(pathName) is True: 1941 mode |= os.O_TRUNC 1942 else: 1943 mode |= os.O_TRUNC | os.O_CREAT 1944 elif createDisposition & smb.FILE_CREATE == smb.FILE_CREATE: 1945 if os.path.exists(pathName) is True: 1946 errorCode = STATUS_OBJECT_NAME_COLLISION 1947 else: 1948 mode |= os.O_CREAT 1949 elif createDisposition & smb.FILE_OPEN == smb.FILE_OPEN: 1950 if os.path.exists(pathName) is not True and smbServer.getRegisteredNamedPipes().has_key(unicode(pathName)) is not True: 1951 errorCode = STATUS_NO_SUCH_FILE 1952 1953 if errorCode == STATUS_SUCCESS: 1954 desiredAccess = ntCreateAndXParameters['AccessMask'] 1955 if (desiredAccess & smb.FILE_READ_DATA) or (desiredAccess & smb.GENERIC_READ): 1956 mode |= os.O_RDONLY 1957 if (desiredAccess & smb.FILE_WRITE_DATA) or (desiredAccess & smb.GENERIC_WRITE): 1958 if (desiredAccess & smb.FILE_READ_DATA) or (desiredAccess & smb.GENERIC_READ): 1959 mode |= os.O_RDWR #| os.O_APPEND 1960 else: 1961 mode |= os.O_WRONLY #| os.O_APPEND 1962 if desiredAccess & smb.GENERIC_ALL: 1963 mode |= os.O_RDWR #| os.O_APPEND 1964 1965 createOptions = ntCreateAndXParameters['CreateOptions'] 1966 if mode & os.O_CREAT == os.O_CREAT: 1967 if createOptions & smb.FILE_DIRECTORY_FILE == smb.FILE_DIRECTORY_FILE: 1968 try: 1969 # Let's create the directory 1970 os.mkdir(pathName) 1971 mode = os.O_RDONLY 1972 except Exception, e: 1973 smbServer.log("NTCreateAndX: %s,%s,%s" % (pathName,mode,e),logging.ERROR) 1974 errorCode = STATUS_ACCESS_DENIED 1975 if createOptions & smb.FILE_NON_DIRECTORY_FILE == smb.FILE_NON_DIRECTORY_FILE: 1976 # If the file being opened is a directory, the server MUST fail the request with 1977 # STATUS_FILE_IS_A_DIRECTORY in the Status field of the SMB Header in the server 1978 # response. 1979 if os.path.isdir(pathName) is True: 1980 errorCode = STATUS_FILE_IS_A_DIRECTORY 1981 1982 if createOptions & smb.FILE_DELETE_ON_CLOSE == smb.FILE_DELETE_ON_CLOSE: 1983 deleteOnClose = True 1984 1985 if errorCode == STATUS_SUCCESS: 1986 try: 1987 if os.path.isdir(pathName) and sys.platform == 'win32': 1988 fid = VOID_FILE_DESCRIPTOR 1989 else: 1990 if sys.platform == 'win32': 1991 mode |= os.O_BINARY 1992 if smbServer.getRegisteredNamedPipes().has_key(unicode(pathName)): 1993 fid = PIPE_FILE_DESCRIPTOR 1994 sock = socket.socket() 1995 sock.connect(smbServer.getRegisteredNamedPipes()[unicode(pathName)]) 1996 else: 1997 fid = os.open(pathName, mode) 1998 except Exception, e: 1999 smbServer.log("NTCreateAndX: %s,%s,%s" % (pathName,mode,e),logging.ERROR) 2000 #print e 2001 fid = 0 2002 errorCode = STATUS_ACCESS_DENIED 2003 else: 2004 errorCode = STATUS_SMB_BAD_TID 2005 2006 if errorCode == STATUS_SUCCESS: 2007 # Simple way to generate a fid 2008 if len(connData['OpenedFiles']) == 0: 2009 fakefid = 1 2010 else: 2011 fakefid = connData['OpenedFiles'].keys()[-1] + 1 2012 respParameters['Fid'] = fakefid 2013 respParameters['CreateAction'] = createDisposition 2014 if fid == PIPE_FILE_DESCRIPTOR: 2015 respParameters['FileAttributes'] = 0x80 2016 respParameters['IsDirectory'] = 0 2017 respParameters['CreateTime'] = 0 2018 respParameters['LastAccessTime'] = 0 2019 respParameters['LastWriteTime'] = 0 2020 respParameters['LastChangeTime'] = 0 2021 respParameters['AllocationSize'] = 4096 2022 respParameters['EndOfFile'] = 0 2023 respParameters['FileType'] = 2 2024 respParameters['IPCState'] = 0x5ff 2025 else: 2026 if os.path.isdir(pathName): 2027 respParameters['FileAttributes'] = smb.SMB_FILE_ATTRIBUTE_DIRECTORY 2028 respParameters['IsDirectory'] = 1 2029 else: 2030 respParameters['IsDirectory'] = 0 2031 respParameters['FileAttributes'] = ntCreateAndXParameters['FileAttributes'] 2032 # Let's get this file's information 2033 respInfo, errorCode = queryPathInformation('',pathName,level= smb.SMB_QUERY_FILE_ALL_INFO) 2034 if errorCode == STATUS_SUCCESS: 2035 respParameters['CreateTime'] = respInfo['CreationTime'] 2036 respParameters['LastAccessTime'] = respInfo['LastAccessTime'] 2037 respParameters['LastWriteTime'] = respInfo['LastWriteTime'] 2038 respParameters['LastChangeTime'] = respInfo['LastChangeTime'] 2039 respParameters['FileAttributes'] = respInfo['ExtFileAttributes'] 2040 respParameters['AllocationSize'] = respInfo['AllocationSize'] 2041 respParameters['EndOfFile'] = respInfo['EndOfFile'] 2042 else: 2043 respParameters = '' 2044 respData = '' 2045 2046 if errorCode == STATUS_SUCCESS: 2047 # Let's store the fid for the connection 2048 # smbServer.log('Create file %s, mode:0x%x' % (pathName, mode)) 2049 connData['OpenedFiles'][fakefid] = {} 2050 connData['OpenedFiles'][fakefid]['FileHandle'] = fid 2051 connData['OpenedFiles'][fakefid]['FileName'] = pathName 2052 connData['OpenedFiles'][fakefid]['DeleteOnClose'] = deleteOnClose 2053 if fid == PIPE_FILE_DESCRIPTOR: 2054 connData['OpenedFiles'][fakefid]['Socket'] = sock 2055 else: 2056 respParameters = '' 2057 respData = '' 2058 2059 respSMBCommand['Parameters'] = respParameters 2060 respSMBCommand['Data'] = respData 2061 smbServer.setConnectionData(connId, connData) 2062 2063 return [respSMBCommand], None, errorCode 2064 2065 @staticmethod 2066 def smbComOpenAndX(connId, smbServer, SMBCommand, recvPacket): 2067 connData = smbServer.getConnectionData(connId) 2068 2069 respSMBCommand = smb.SMBCommand(smb.SMB.SMB_COM_OPEN_ANDX) 2070 respParameters = smb.SMBOpenAndXResponse_Parameters() 2071 respData = '' 2072 2073 openAndXParameters = smb.SMBOpenAndX_Parameters(SMBCommand['Parameters']) 2074 openAndXData = smb.SMBOpenAndX_Data( flags = recvPacket['Flags2'], data = SMBCommand['Data']) 2075 2076 # Get the Tid associated 2077 if connData['ConnectedShares'].has_key(recvPacket['Tid']): 2078 path = connData['ConnectedShares'][recvPacket['Tid']]['path'] 2079 openedFile, mode, pathName, errorCode = openFile(path, 2080 decodeSMBString(recvPacket['Flags2'],openAndXData['FileName']), 2081 openAndXParameters['DesiredAccess'], 2082 openAndXParameters['FileAttributes'], 2083 openAndXParameters['OpenMode']) 2084 else: 2085 errorCode = STATUS_SMB_BAD_TID 2086 2087 if errorCode == STATUS_SUCCESS: 2088 # Simple way to generate a fid 2089 fid = len(connData['OpenedFiles']) + 1 2090 if len(connData['OpenedFiles']) == 0: 2091 fid = 1 2092 else: 2093 fid = connData['OpenedFiles'].keys()[-1] + 1 2094 respParameters['Fid'] = fid 2095 if mode & os.O_CREAT: 2096 # File did not exist and was created 2097 respParameters['Action'] = 0x2 2098 elif mode & os.O_RDONLY: 2099 # File existed and was opened 2100 respParameters['Action'] = 0x1 2101 elif mode & os.O_APPEND: 2102 # File existed and was opened 2103 respParameters['Action'] = 0x1 2104 else: 2105 # File existed and was truncated 2106 respParameters['Action'] = 0x3 2107 2108 # Let's store the fid for the connection 2109 #smbServer.log('Opening file %s' % pathName) 2110 connData['OpenedFiles'][fid] = {} 2111 connData['OpenedFiles'][fid]['FileHandle'] = openedFile 2112 connData['OpenedFiles'][fid]['FileName'] = pathName 2113 connData['OpenedFiles'][fid]['DeleteOnClose'] = False 2114 else: 2115 respParameters = '' 2116 respData = '' 2117 2118 respSMBCommand['Parameters'] = respParameters 2119 respSMBCommand['Data'] = respData 2120 smbServer.setConnectionData(connId, connData) 2121 2122 return [respSMBCommand], None, errorCode 2123 2124 @staticmethod 2125 def smbComTreeConnectAndX(connId, smbServer, SMBCommand, recvPacket): 2126 connData = smbServer.getConnectionData(connId) 2127 2128 resp = smb.NewSMBPacket() 2129 resp['Flags1'] = smb.SMB.FLAGS1_REPLY 2130 resp['Flags2'] = smb.SMB.FLAGS2_EXTENDED_SECURITY | smb.SMB.FLAGS2_NT_STATUS | smb.SMB.FLAGS2_LONG_NAMES | recvPacket['Flags2'] & smb.SMB.FLAGS2_UNICODE 2131 2132 resp['Tid'] = recvPacket['Tid'] 2133 resp['Mid'] = recvPacket['Mid'] 2134 resp['Pid'] = connData['Pid'] 2135 2136 respSMBCommand = smb.SMBCommand(smb.SMB.SMB_COM_TREE_CONNECT_ANDX) 2137 respParameters = smb.SMBTreeConnectAndXResponse_Parameters() 2138 respData = smb.SMBTreeConnectAndXResponse_Data() 2139 2140 treeConnectAndXParameters = smb.SMBTreeConnectAndX_Parameters(SMBCommand['Parameters']) 2141 2142 if treeConnectAndXParameters['Flags'] & 0x8: 2143 respParameters = smb.SMBTreeConnectAndXExtendedResponse_Parameters() 2144 2145 treeConnectAndXData = smb.SMBTreeConnectAndX_Data( flags = recvPacket['Flags2'] ) 2146 treeConnectAndXData['_PasswordLength'] = treeConnectAndXParameters['PasswordLength'] 2147 treeConnectAndXData.fromString(SMBCommand['Data']) 2148 2149 errorCode = STATUS_SUCCESS 2150 2151 ## Process here the request, does the share exist? 2152 UNCOrShare = decodeSMBString(recvPacket['Flags2'], treeConnectAndXData['Path']) 2153 2154 # Is this a UNC? 2155 if ntpath.ismount(UNCOrShare): 2156 path = UNCOrShare.split('\\')[3] 2157 else: 2158 path = ntpath.basename(UNCOrShare) 2159 2160 share = searchShare(connId, path, smbServer) 2161 if share is not None: 2162 # Simple way to generate a Tid 2163 if len(connData['ConnectedShares']) == 0: 2164 tid = 1 2165 else: 2166 tid = connData['ConnectedShares'].keys()[-1] + 1 2167 connData['ConnectedShares'][tid] = share 2168 connData['ConnectedShares'][tid]['shareName'] = path 2169 resp['Tid'] = tid 2170 #smbServer.log("Connecting Share(%d:%s)" % (tid,path)) 2171 else: 2172 smbServer.log("TreeConnectAndX not found %s" % path, logging.ERROR) 2173 errorCode = STATUS_OBJECT_PATH_NOT_FOUND 2174 resp['ErrorCode'] = errorCode >> 16 2175 resp['ErrorClass'] = errorCode & 0xff 2176 ## 2177 respParameters['OptionalSupport'] = smb.SMB.SMB_SUPPORT_SEARCH_BITS 2178 2179 if path == 'IPC$': 2180 respData['Service'] = 'IPC' 2181 else: 2182 respData['Service'] = path 2183 respData['PadLen'] = 0 2184 respData['NativeFileSystem'] = encodeSMBString(recvPacket['Flags2'], 'NTFS' ) 2185 2186 respSMBCommand['Parameters'] = respParameters 2187 respSMBCommand['Data'] = respData 2188 2189 resp['Uid'] = connData['Uid'] 2190 resp.addCommand(respSMBCommand) 2191 smbServer.setConnectionData(connId, connData) 2192 2193 return None, [resp], errorCode 2194 2195 @staticmethod 2196 def smbComSessionSetupAndX(connId, smbServer, SMBCommand, recvPacket): 2197 connData = smbServer.getConnectionData(connId, checkStatus = False) 2198 2199 respSMBCommand = smb.SMBCommand(smb.SMB.SMB_COM_SESSION_SETUP_ANDX) 2200 2201 # From [MS-SMB] 2202 # When extended security is being used (see section 3.2.4.2.4), the 2203 # request MUST take the following form 2204 # [..] 2205 # WordCount (1 byte): The value of this field MUST be 0x0C. 2206 if SMBCommand['WordCount'] == 12: 2207 # Extended security. Here we deal with all SPNEGO stuff 2208 respParameters = smb.SMBSessionSetupAndX_Extended_Response_Parameters() 2209 respData = smb.SMBSessionSetupAndX_Extended_Response_Data(flags = recvPacket['Flags2']) 2210 sessionSetupParameters = smb.SMBSessionSetupAndX_Extended_Parameters(SMBCommand['Parameters']) 2211 sessionSetupData = smb.SMBSessionSetupAndX_Extended_Data() 2212 sessionSetupData['SecurityBlobLength'] = sessionSetupParameters['SecurityBlobLength'] 2213 sessionSetupData.fromString(SMBCommand['Data']) 2214 connData['Capabilities'] = sessionSetupParameters['Capabilities'] 2215 2216 rawNTLM = False 2217 if struct.unpack('B',sessionSetupData['SecurityBlob'][0])[0] == ASN1_AID: 2218 # NEGOTIATE packet 2219 blob = SPNEGO_NegTokenInit(sessionSetupData['SecurityBlob']) 2220 token = blob['MechToken'] 2221 if len(blob['MechTypes'][0]) > 0: 2222 # Is this GSSAPI NTLM or something else we don't support? 2223 mechType = blob['MechTypes'][0] 2224 if mechType != TypesMech['NTLMSSP - Microsoft NTLM Security Support Provider']: 2225 # Nope, do we know it? 2226 if MechTypes.has_key(mechType): 2227 mechStr = MechTypes[mechType] 2228 else: 2229 mechStr = hexlify(mechType) 2230 smbServer.log("Unsupported MechType '%s'" % mechStr, logging.CRITICAL) 2231 # We don't know the token, we answer back again saying 2232 # we just support NTLM. 2233 # ToDo: Build this into a SPNEGO_NegTokenResp() 2234 respToken = '\xa1\x15\x30\x13\xa0\x03\x0a\x01\x03\xa1\x0c\x06\x0a\x2b\x06\x01\x04\x01\x82\x37\x02\x02\x0a' 2235 respParameters['SecurityBlobLength'] = len(respToken) 2236 respData['SecurityBlobLength'] = respParameters['SecurityBlobLength'] 2237 respData['SecurityBlob'] = respToken 2238 respData['NativeOS'] = encodeSMBString(recvPacket['Flags2'], smbServer.getServerOS()) 2239 respData['NativeLanMan'] = encodeSMBString(recvPacket['Flags2'], smbServer.getServerOS()) 2240 respSMBCommand['Parameters'] = respParameters 2241 respSMBCommand['Data'] = respData 2242 return [respSMBCommand], None, STATUS_MORE_PROCESSING_REQUIRED 2243 2244 elif struct.unpack('B',sessionSetupData['SecurityBlob'][0])[0] == ASN1_SUPPORTED_MECH: 2245 # AUTH packet 2246 blob = SPNEGO_NegTokenResp(sessionSetupData['SecurityBlob']) 2247 token = blob['ResponseToken'] 2248 else: 2249 # No GSSAPI stuff, raw NTLMSSP 2250 rawNTLM = True 2251 token = sessionSetupData['SecurityBlob'] 2252 2253 # Here we only handle NTLMSSP, depending on what stage of the 2254 # authentication we are, we act on it 2255 messageType = struct.unpack('<L',token[len('NTLMSSP\x00'):len('NTLMSSP\x00')+4])[0] 2256 2257 if messageType == 0x01: 2258 # NEGOTIATE_MESSAGE 2259 negotiateMessage = ntlm.NTLMAuthNegotiate() 2260 negotiateMessage.fromString(token) 2261 # Let's store it in the connection data 2262 connData['NEGOTIATE_MESSAGE'] = negotiateMessage 2263 # Let's build the answer flags 2264 # TODO: Parse all the flags. With this we're leaving some clients out 2265 2266 ansFlags = 0 2267 2268 if negotiateMessage['flags'] & ntlm.NTLMSSP_NEGOTIATE_56: 2269 ansFlags |= ntlm.NTLMSSP_NEGOTIATE_56 2270 if negotiateMessage['flags'] & ntlm.NTLMSSP_NEGOTIATE_128: 2271 ansFlags |= ntlm.NTLMSSP_NEGOTIATE_128 2272 if negotiateMessage['flags'] & ntlm.NTLMSSP_NEGOTIATE_KEY_EXCH: 2273 ansFlags |= ntlm.NTLMSSP_NEGOTIATE_KEY_EXCH 2274 if negotiateMessage['flags'] & ntlm.NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY: 2275 ansFlags |= ntlm.NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY 2276 if negotiateMessage['flags'] & ntlm.NTLMSSP_NEGOTIATE_UNICODE: 2277 ansFlags |= ntlm.NTLMSSP_NEGOTIATE_UNICODE 2278 if negotiateMessage['flags'] & ntlm.NTLM_NEGOTIATE_OEM: 2279 ansFlags |= ntlm.NTLM_NEGOTIATE_OEM 2280 2281 ansFlags |= ntlm.NTLMSSP_NEGOTIATE_VERSION | ntlm.NTLMSSP_NEGOTIATE_TARGET_INFO | ntlm.NTLMSSP_TARGET_TYPE_SERVER | ntlm.NTLMSSP_NEGOTIATE_NTLM | ntlm.NTLMSSP_REQUEST_TARGET 2282 2283 # Generate the AV_PAIRS 2284 av_pairs = ntlm.AV_PAIRS() 2285 # TODO: Put the proper data from SMBSERVER config 2286 av_pairs[ntlm.NTLMSSP_AV_HOSTNAME] = av_pairs[ntlm.NTLMSSP_AV_DNS_HOSTNAME] = smbServer.getServerName().encode('utf-16le') 2287 av_pairs[ntlm.NTLMSSP_AV_DOMAINNAME] = av_pairs[ntlm.NTLMSSP_AV_DNS_DOMAINNAME] = smbServer.getServerDomain().encode('utf-16le') 2288 av_pairs[ntlm.NTLMSSP_AV_TIME] = struct.pack('<q', (116444736000000000 + calendar.timegm(time.gmtime()) * 10000000) ) 2289 2290 challengeMessage = ntlm.NTLMAuthChallenge() 2291 challengeMessage['flags'] = ansFlags 2292 challengeMessage['domain_len'] = len(smbServer.getServerDomain().encode('utf-16le')) 2293 challengeMessage['domain_max_len'] = challengeMessage['domain_len'] 2294 challengeMessage['domain_offset'] = 40 + 16 2295 challengeMessage['challenge'] = smbServer.getSMBChallenge() 2296 challengeMessage['domain_name'] = smbServer.getServerDomain().encode('utf-16le') 2297 challengeMessage['TargetInfoFields_len'] = len(av_pairs) 2298 challengeMessage['TargetInfoFields_max_len'] = len(av_pairs) 2299 challengeMessage['TargetInfoFields'] = av_pairs 2300 challengeMessage['TargetInfoFields_offset'] = 40 + 16 + len(challengeMessage['domain_name']) 2301 challengeMessage['Version'] = '\xff'*8 2302 challengeMessage['VersionLen'] = 8 2303 2304 if rawNTLM is False: 2305 respToken = SPNEGO_NegTokenResp() 2306 # accept-incomplete. We want more data 2307 respToken['NegResult'] = '\x01' 2308 respToken['SupportedMech'] = TypesMech['NTLMSSP - Microsoft NTLM Security Support Provider'] 2309 2310 respToken['ResponseToken'] = challengeMessage.getData() 2311 else: 2312 respToken = challengeMessage 2313 2314 # Setting the packet to STATUS_MORE_PROCESSING 2315 errorCode = STATUS_MORE_PROCESSING_REQUIRED 2316 # Let's set up an UID for this connection and store it 2317 # in the connection's data 2318 # Picking a fixed value 2319 # TODO: Manage more UIDs for the same session 2320 connData['Uid'] = 10 2321 # Let's store it in the connection data 2322 connData['CHALLENGE_MESSAGE'] = challengeMessage 2323 2324 elif messageType == 0x02: 2325 # CHALLENGE_MESSAGE 2326 raise Exception('Challenge Message raise, not implemented!') 2327 elif messageType == 0x03: 2328 # AUTHENTICATE_MESSAGE, here we deal with authentication 2329 authenticateMessage = ntlm.NTLMAuthChallengeResponse() 2330 authenticateMessage.fromString(token) 2331 smbServer.log("AUTHENTICATE_MESSAGE (%s\\%s,%s)" % (authenticateMessage['domain_name'], authenticateMessage['user_name'], authenticateMessage['host_name'])) 2332 # TODO: Check the credentials! Now granting permissions 2333 2334 respToken = SPNEGO_NegTokenResp() 2335 # accept-completed 2336 respToken['NegResult'] = '\x00' 2337 2338 # Status SUCCESS 2339 errorCode = STATUS_SUCCESS 2340 smbServer.log('User %s\\%s authenticated successfully' % (authenticateMessage['user_name'], authenticateMessage['host_name'])) 2341 # Let's store it in the connection data 2342 connData['AUTHENTICATE_MESSAGE'] = authenticateMessage 2343 try: 2344 jtr_dump_path = smbServer.getJTRdumpPath() 2345 ntlm_hash_data = outputToJohnFormat( connData['CHALLENGE_MESSAGE']['challenge'], authenticateMessage['user_name'], authenticateMessage['domain_name'], authenticateMessage['lanman'], authenticateMessage['ntlm'] ) 2346 smbServer.log(ntlm_hash_data['hash_string']) 2347 if jtr_dump_path is not '': 2348 writeJohnOutputToFile(ntlm_hash_data['hash_string'], ntlm_hash_data['hash_version'], jtr_dump_path) 2349 except: 2350 smbServer.log("Could not write NTLM Hashes to the specified JTR_Dump_Path %s" % jtr_dump_path) 2351 else: 2352 raise Exception("Unknown NTLMSSP MessageType %d" % messageType) 2353 2354 respParameters['SecurityBlobLength'] = len(respToken) 2355 respData['SecurityBlobLength'] = respParameters['SecurityBlobLength'] 2356 respData['SecurityBlob'] = respToken.getData() 2357 2358 else: 2359 # Process Standard Security 2360 respParameters = smb.SMBSessionSetupAndXResponse_Parameters() 2361 respData = smb.SMBSessionSetupAndXResponse_Data() 2362 sessionSetupParameters = smb.SMBSessionSetupAndX_Parameters(SMBCommand['Parameters']) 2363 sessionSetupData = smb.SMBSessionSetupAndX_Data() 2364 sessionSetupData['AnsiPwdLength'] = sessionSetupParameters['AnsiPwdLength'] 2365 sessionSetupData['UnicodePwdLength'] = sessionSetupParameters['UnicodePwdLength'] 2366 sessionSetupData.fromString(SMBCommand['Data']) 2367 connData['Capabilities'] = sessionSetupParameters['Capabilities'] 2368 # Do the verification here, for just now we grant access 2369 # TODO: Manage more UIDs for the same session 2370 errorCode = STATUS_SUCCESS 2371 connData['Uid'] = 10 2372 respParameters['Action'] = 0 2373 smbServer.log('User %s\\%s authenticated successfully (basic)' % (sessionSetupData['PrimaryDomain'], sessionSetupData['Account'])) 2374 try: 2375 jtr_dump_path = smbServer.getJTRdumpPath() 2376 ntlm_hash_data = outputToJohnFormat( '', sessionSetupData['Account'], sessionSetupData['PrimaryDomain'], sessionSetupData['AnsiPwd'], sessionSetupData['UnicodePwd'] ) 2377 smbServer.log(ntlm_hash_data['hash_string']) 2378 if jtr_dump_path is not '': 2379 writeJohnOutputToFile(ntlm_hash_data['hash_string'], ntlm_hash_data['hash_version'], jtr_dump_path) 2380 except: 2381 smbServer.log("Could not write NTLM Hashes to the specified JTR_Dump_Path %s" % jtr_dump_path) 2382 2383 respData['NativeOS'] = encodeSMBString(recvPacket['Flags2'], smbServer.getServerOS()) 2384 respData['NativeLanMan'] = encodeSMBString(recvPacket['Flags2'], smbServer.getServerOS()) 2385 respSMBCommand['Parameters'] = respParameters 2386 respSMBCommand['Data'] = respData 2387 2388 # From now on, the client can ask for other commands 2389 connData['Authenticated'] = True 2390 # For now, just switching to nobody 2391 #os.setregid(65534,65534) 2392 #os.setreuid(65534,65534) 2393 smbServer.setConnectionData(connId, connData) 2394 2395 return [respSMBCommand], None, errorCode 2396 2397 @staticmethod 2398 def smbComNegotiate(connId, smbServer, SMBCommand, recvPacket ): 2399 connData = smbServer.getConnectionData(connId, checkStatus = False) 2400 connData['Pid'] = recvPacket['Pid'] 2401 2402 SMBCommand = smb.SMBCommand(recvPacket['Data'][0]) 2403 respSMBCommand = smb.SMBCommand(smb.SMB.SMB_COM_NEGOTIATE) 2404 2405 resp = smb.NewSMBPacket() 2406 resp['Flags1'] = smb.SMB.FLAGS1_REPLY 2407 resp['Pid'] = connData['Pid'] 2408 resp['Tid'] = recvPacket['Tid'] 2409 resp['Mid'] = recvPacket['Mid'] 2410 2411 # TODO: We support more dialects, and parse them accordingly 2412 dialects = SMBCommand['Data'].split('\x02') 2413 try: 2414 index = dialects.index('NT LM 0.12\x00') - 1 2415 # Let's fill the data for NTLM 2416 if recvPacket['Flags2'] & smb.SMB.FLAGS2_EXTENDED_SECURITY: 2417 resp['Flags2'] = smb.SMB.FLAGS2_EXTENDED_SECURITY | smb.SMB.FLAGS2_NT_STATUS | smb.SMB.FLAGS2_UNICODE 2418 #resp['Flags2'] = smb.SMB.FLAGS2_EXTENDED_SECURITY | smb.SMB.FLAGS2_NT_STATUS 2419 _dialects_data = smb.SMBExtended_Security_Data() 2420 _dialects_data['ServerGUID'] = 'A'*16 2421 blob = SPNEGO_NegTokenInit() 2422 blob['MechTypes'] = [TypesMech['NTLMSSP - Microsoft NTLM Security Support Provider']] 2423 _dialects_data['SecurityBlob'] = blob.getData() 2424 2425 _dialects_parameters = smb.SMBExtended_Security_Parameters() 2426 _dialects_parameters['Capabilities'] = smb.SMB.CAP_EXTENDED_SECURITY | smb.SMB.CAP_USE_NT_ERRORS | smb.SMB.CAP_NT_SMBS | smb.SMB.CAP_UNICODE 2427 _dialects_parameters['ChallengeLength'] = 0 2428 2429 else: 2430 resp['Flags2'] = smb.SMB.FLAGS2_NT_STATUS | smb.SMB.FLAGS2_UNICODE 2431 _dialects_parameters = smb.SMBNTLMDialect_Parameters() 2432 _dialects_data= smb.SMBNTLMDialect_Data() 2433 _dialects_data['Payload'] = '' 2434 if connData.has_key('EncryptionKey'): 2435 _dialects_data['Challenge'] = connData['EncryptionKey'] 2436 _dialects_parameters['ChallengeLength'] = len(str(_dialects_data)) 2437 else: 2438 # TODO: Handle random challenges, now one that can be used with rainbow tables 2439 _dialects_data['Challenge'] = '\x11\x22\x33\x44\x55\x66\x77\x88' 2440 _dialects_parameters['ChallengeLength'] = 8 2441 _dialects_parameters['Capabilities'] = smb.SMB.CAP_USE_NT_ERRORS | smb.SMB.CAP_NT_SMBS 2442 2443 # Let's see if we need to support RPC_REMOTE_APIS 2444 config = smbServer.getServerConfig() 2445 if config.has_option('global','rpc_apis'): 2446 if config.getboolean('global', 'rpc_apis') is True: 2447 _dialects_parameters['Capabilities'] |= smb.SMB.CAP_RPC_REMOTE_APIS 2448 2449 _dialects_parameters['DialectIndex'] = index 2450 _dialects_parameters['SecurityMode'] = smb.SMB.SECURITY_AUTH_ENCRYPTED | smb.SMB.SECURITY_SHARE_USER 2451 _dialects_parameters['MaxMpxCount'] = 1 2452 _dialects_parameters['MaxNumberVcs'] = 1 2453 _dialects_parameters['MaxBufferSize'] = 64000 2454 _dialects_parameters['MaxRawSize'] = 65536 2455 _dialects_parameters['SessionKey'] = 0 2456 _dialects_parameters['LowDateTime'] = 0 2457 _dialects_parameters['HighDateTime'] = 0 2458 _dialects_parameters['ServerTimeZone'] = 0 2459 2460 2461 respSMBCommand['Data'] = _dialects_data 2462 respSMBCommand['Parameters'] = _dialects_parameters 2463 connData['_dialects_data'] = _dialects_data 2464 connData['_dialects_parameters'] = _dialects_parameters 2465 2466 except Exception, e: 2467 # No NTLM throw an error 2468 smbServer.log('smbComNegotiate: %s' % e, logging.ERROR) 2469 respSMBCommand['Data'] = struct.pack('<H',0xffff) 2470 2471 2472 smbServer.setConnectionData(connId, connData) 2473 2474 resp.addCommand(respSMBCommand) 2475 2476 return None, [resp], STATUS_SUCCESS 2477 2478 @staticmethod 2479 def default(connId, smbServer, SMBCommand, recvPacket): 2480 # By default we return an SMB Packet with error not implemented 2481 smbServer.log("Not implemented command: 0x%x" % recvPacket['Command'],logging.DEBUG) 2482 packet = smb.NewSMBPacket() 2483 packet['Flags1'] = smb.SMB.FLAGS1_REPLY 2484 packet['Flags2'] = smb.SMB.FLAGS2_NT_STATUS 2485 packet['Command'] = recvPacket['Command'] 2486 packet['Pid'] = recvPacket['Pid'] 2487 packet['Tid'] = recvPacket['Tid'] 2488 packet['Mid'] = recvPacket['Mid'] 2489 packet['Uid'] = recvPacket['Uid'] 2490 packet['Data'] = '\x00\x00\x00' 2491 errorCode = STATUS_NOT_IMPLEMENTED 2492 packet['ErrorCode'] = errorCode >> 16 2493 packet['ErrorClass'] = errorCode & 0xff 2494 2495 return None, [packet], errorCode 2496 2497 class SMB2Commands: 2498 @staticmethod 2499 def smb2Negotiate(connId, smbServer, recvPacket, isSMB1 = False): 2500 connData = smbServer.getConnectionData(connId, checkStatus = False) 2501 2502 respPacket = smb2.SMB2Packet() 2503 respPacket['Flags'] = smb2.SMB2_FLAGS_SERVER_TO_REDIR 2504 respPacket['Status'] = STATUS_SUCCESS 2505 respPacket['CreditRequestResponse'] = 1 2506 respPacket['Command'] = smb2.SMB2_NEGOTIATE 2507 respPacket['SessionID'] = 0 2508 if isSMB1 is False: 2509 respPacket['MessageID'] = recvPacket['MessageID'] 2510 else: 2511 respPacket['MessageID'] = 0 2512 respPacket['TreeID'] = 0 2513 2514 2515 respSMBCommand = smb2.SMB2Negotiate_Response() 2516 2517 respSMBCommand['SecurityMode'] = 1 2518 if isSMB1 is True: 2519 # Let's first parse the packet to see if the client supports SMB2 2520 SMBCommand = smb.SMBCommand(recvPacket['Data'][0]) 2521 2522 dialects = SMBCommand['Data'].split('\x02') 2523 if 'SMB 2.002\x00' in dialects or 'SMB 2.???\x00' in dialects: 2524 respSMBCommand['DialectRevision'] = smb2.SMB2_DIALECT_002 2525 else: 2526 # Client does not support SMB2 fallbacking 2527 raise Exception('SMB2 not supported, fallbacking') 2528 else: 2529 respSMBCommand['DialectRevision'] = smb2.SMB2_DIALECT_002 2530 respSMBCommand['ServerGuid'] = 'A'*16 2531 respSMBCommand['Capabilities'] = 0 2532 respSMBCommand['MaxTransactSize'] = 65536 2533 respSMBCommand['MaxReadSize'] = 65536 2534 respSMBCommand['MaxWriteSize'] = 65536 2535 respSMBCommand['SystemTime'] = getFileTime(calendar.timegm(time.gmtime())) 2536 respSMBCommand['ServerStartTime'] = getFileTime(calendar.timegm(time.gmtime())) 2537 respSMBCommand['SecurityBufferOffset'] = 0x80 2538 2539 blob = SPNEGO_NegTokenInit() 2540 blob['MechTypes'] = [TypesMech['NTLMSSP - Microsoft NTLM Security Support Provider']] 2541 2542 respSMBCommand['Buffer'] = blob.getData() 2543 respSMBCommand['SecurityBufferLength'] = len(respSMBCommand['Buffer']) 2544 2545 respPacket['Data'] = respSMBCommand 2546 2547 smbServer.setConnectionData(connId, connData) 2548 2549 return None, [respPacket], STATUS_SUCCESS 2550 2551 @staticmethod 2552 def smb2SessionSetup(connId, smbServer, recvPacket): 2553 connData = smbServer.getConnectionData(connId, checkStatus = False) 2554 2555 respSMBCommand = smb2.SMB2SessionSetup_Response() 2556 2557 sessionSetupData = smb2.SMB2SessionSetup(recvPacket['Data']) 2558 2559 connData['Capabilities'] = sessionSetupData['Capabilities'] 2560 2561 securityBlob = sessionSetupData['Buffer'] 2562 2563 rawNTLM = False 2564 if struct.unpack('B',securityBlob[0])[0] == ASN1_AID: 2565 # NEGOTIATE packet 2566 blob = SPNEGO_NegTokenInit(securityBlob) 2567 token = blob['MechToken'] 2568 if len(blob['MechTypes'][0]) > 0: 2569 # Is this GSSAPI NTLM or something else we don't support? 2570 mechType = blob['MechTypes'][0] 2571 if mechType != TypesMech['NTLMSSP - Microsoft NTLM Security Support Provider']: 2572 # Nope, do we know it? 2573 if MechTypes.has_key(mechType): 2574 mechStr = MechTypes[mechType] 2575 else: 2576 mechStr = hexlify(mechType) 2577 smbServer.log("Unsupported MechType '%s'" % mechStr, logging.CRITICAL) 2578 # We don't know the token, we answer back again saying 2579 # we just support NTLM. 2580 # ToDo: Build this into a SPNEGO_NegTokenResp() 2581 respToken = '\xa1\x15\x30\x13\xa0\x03\x0a\x01\x03\xa1\x0c\x06\x0a\x2b\x06\x01\x04\x01\x82\x37\x02\x02\x0a' 2582 respSMBCommand['SecurityBufferOffset'] = 0x48 2583 respSMBCommand['SecurityBufferLength'] = len(respToken) 2584 respSMBCommand['Buffer'] = respToken 2585 2586 return [respSMBCommand], None, STATUS_MORE_PROCESSING_REQUIRED 2587 elif struct.unpack('B',securityBlob[0])[0] == ASN1_SUPPORTED_MECH: 2588 # AUTH packet 2589 blob = SPNEGO_NegTokenResp(securityBlob) 2590 token = blob['ResponseToken'] 2591 else: 2592 # No GSSAPI stuff, raw NTLMSSP 2593 rawNTLM = True 2594 token = securityBlob 2595 2596 # Here we only handle NTLMSSP, depending on what stage of the 2597 # authentication we are, we act on it 2598 messageType = struct.unpack('<L',token[len('NTLMSSP\x00'):len('NTLMSSP\x00')+4])[0] 2599 2600 if messageType == 0x01: 2601 # NEGOTIATE_MESSAGE 2602 negotiateMessage = ntlm.NTLMAuthNegotiate() 2603 negotiateMessage.fromString(token) 2604 # Let's store it in the connection data 2605 connData['NEGOTIATE_MESSAGE'] = negotiateMessage 2606 # Let's build the answer flags 2607 # TODO: Parse all the flags. With this we're leaving some clients out 2608 2609 ansFlags = 0 2610 2611 if negotiateMessage['flags'] & ntlm.NTLMSSP_NEGOTIATE_56: 2612 ansFlags |= ntlm.NTLMSSP_NEGOTIATE_56 2613 if negotiateMessage['flags'] & ntlm.NTLMSSP_NEGOTIATE_128: 2614 ansFlags |= ntlm.NTLMSSP_NEGOTIATE_128 2615 if negotiateMessage['flags'] & ntlm.NTLMSSP_NEGOTIATE_KEY_EXCH: 2616 ansFlags |= ntlm.NTLMSSP_NEGOTIATE_KEY_EXCH 2617 if negotiateMessage['flags'] & ntlm.NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY: 2618 ansFlags |= ntlm.NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY 2619 if negotiateMessage['flags'] & ntlm.NTLMSSP_NEGOTIATE_UNICODE: 2620 ansFlags |= ntlm.NTLMSSP_NEGOTIATE_UNICODE 2621 if negotiateMessage['flags'] & ntlm.NTLM_NEGOTIATE_OEM: 2622 ansFlags |= ntlm.NTLM_NEGOTIATE_OEM 2623 2624 ansFlags |= ntlm.NTLMSSP_NEGOTIATE_VERSION | ntlm.NTLMSSP_NEGOTIATE_TARGET_INFO | ntlm.NTLMSSP_TARGET_TYPE_SERVER | ntlm.NTLMSSP_NEGOTIATE_NTLM | ntlm.NTLMSSP_REQUEST_TARGET 2625 2626 # Generate the AV_PAIRS 2627 av_pairs = ntlm.AV_PAIRS() 2628 # TODO: Put the proper data from SMBSERVER config 2629 av_pairs[ntlm.NTLMSSP_AV_HOSTNAME] = av_pairs[ntlm.NTLMSSP_AV_DNS_HOSTNAME] = smbServer.getServerName().encode('utf-16le') 2630 av_pairs[ntlm.NTLMSSP_AV_DOMAINNAME] = av_pairs[ntlm.NTLMSSP_AV_DNS_DOMAINNAME] = smbServer.getServerDomain().encode('utf-16le') 2631 av_pairs[ntlm.NTLMSSP_AV_TIME] = struct.pack('<q', (116444736000000000 + calendar.timegm(time.gmtime()) * 10000000) ) 2632 2633 challengeMessage = ntlm.NTLMAuthChallenge() 2634 challengeMessage['flags'] = ansFlags 2635 challengeMessage['domain_len'] = len(smbServer.getServerDomain().encode('utf-16le')) 2636 challengeMessage['domain_max_len'] = challengeMessage['domain_len'] 2637 challengeMessage['domain_offset'] = 40 + 16 2638 challengeMessage['challenge'] = smbServer.getSMBChallenge() 2639 challengeMessage['domain_name'] = smbServer.getServerDomain().encode('utf-16le') 2640 challengeMessage['TargetInfoFields_len'] = len(av_pairs) 2641 challengeMessage['TargetInfoFields_max_len'] = len(av_pairs) 2642 challengeMessage['TargetInfoFields'] = av_pairs 2643 challengeMessage['TargetInfoFields_offset'] = 40 + 16 + len(challengeMessage['domain_name']) 2644 challengeMessage['Version'] = '\xff'*8 2645 challengeMessage['VersionLen'] = 8 2646 2647 if rawNTLM is False: 2648 respToken = SPNEGO_NegTokenResp() 2649 # accept-incomplete. We want more data 2650 respToken['NegResult'] = '\x01' 2651 respToken['SupportedMech'] = TypesMech['NTLMSSP - Microsoft NTLM Security Support Provider'] 2652 2653 respToken['ResponseToken'] = challengeMessage.getData() 2654 else: 2655 respToken = challengeMessage 2656 2657 # Setting the packet to STATUS_MORE_PROCESSING 2658 errorCode = STATUS_MORE_PROCESSING_REQUIRED 2659 # Let's set up an UID for this connection and store it 2660 # in the connection's data 2661 # Picking a fixed value 2662 # TODO: Manage more UIDs for the same session 2663 connData['Uid'] = random.randint(1,0xffffffff) 2664 # Let's store it in the connection data 2665 connData['CHALLENGE_MESSAGE'] = challengeMessage 2666 2667 elif messageType == 0x02: 2668 # CHALLENGE_MESSAGE 2669 raise Exception('Challenge Message raise, not implemented!') 2670 elif messageType == 0x03: 2671 # AUTHENTICATE_MESSAGE, here we deal with authentication 2672 authenticateMessage = ntlm.NTLMAuthChallengeResponse() 2673 authenticateMessage.fromString(token) 2674 smbServer.log("AUTHENTICATE_MESSAGE (%s\\%s,%s)" % (authenticateMessage['domain_name'], authenticateMessage['user_name'], authenticateMessage['host_name'])) 2675 # TODO: Check the credentials! Now granting permissions 2676 2677 respToken = SPNEGO_NegTokenResp() 2678 # accept-completed 2679 respToken['NegResult'] = '\x00' 2680 2681 # Status SUCCESS 2682 errorCode = STATUS_SUCCESS 2683 smbServer.log('User %s\\%s authenticated successfully' % (authenticateMessage['user_name'], authenticateMessage['host_name'])) 2684 # Let's store it in the connection data 2685 connData['AUTHENTICATE_MESSAGE'] = authenticateMessage 2686 try: 2687 jtr_dump_path = smbServer.getJTRdumpPath() 2688 ntlm_hash_data = outputToJohnFormat( connData['CHALLENGE_MESSAGE']['challenge'], authenticateMessage['user_name'], authenticateMessage['domain_name'], authenticateMessage['lanman'], authenticateMessage['ntlm'] ) 2689 smbServer.log(ntlm_hash_data['hash_string']) 2690 if jtr_dump_path is not '': 2691 writeJohnOutputToFile(ntlm_hash_data['hash_string'], ntlm_hash_data['hash_version'], jtr_dump_path) 2692 except: 2693 smbServer.log("Could not write NTLM Hashes to the specified JTR_Dump_Path %s" % jtr_dump_path) 2694 respSMBCommand['SessionFlags'] = 1 2695 else: 2696 raise Exception("Unknown NTLMSSP MessageType %d" % messageType) 2697 2698 respSMBCommand['SecurityBufferOffset'] = 0x48 2699 respSMBCommand['SecurityBufferLength'] = len(respToken) 2700 respSMBCommand['Buffer'] = respToken.getData() 2701 2702 # From now on, the client can ask for other commands 2703 connData['Authenticated'] = True 2704 # For now, just switching to nobody 2705 #os.setregid(65534,65534) 2706 #os.setreuid(65534,65534) 2707 smbServer.setConnectionData(connId, connData) 2708 2709 return [respSMBCommand], None, errorCode 2710 2711 @staticmethod 2712 def smb2TreeConnect(connId, smbServer, recvPacket): 2713 connData = smbServer.getConnectionData(connId) 2714 2715 respPacket = smb2.SMB2Packet() 2716 respPacket['Flags'] = smb2.SMB2_FLAGS_SERVER_TO_REDIR 2717 respPacket['Status'] = STATUS_SUCCESS 2718 respPacket['CreditRequestResponse'] = 1 2719 respPacket['Command'] = recvPacket['Command'] 2720 respPacket['SessionID'] = connData['Uid'] 2721 respPacket['Reserved'] = recvPacket['Reserved'] 2722 respPacket['MessageID'] = recvPacket['MessageID'] 2723 respPacket['TreeID'] = recvPacket['TreeID'] 2724 2725 respSMBCommand = smb2.SMB2TreeConnect_Response() 2726 2727 treeConnectRequest = smb2.SMB2TreeConnect(recvPacket['Data']) 2728 2729 errorCode = STATUS_SUCCESS 2730 2731 ## Process here the request, does the share exist? 2732 path = str(recvPacket)[treeConnectRequest['PathOffset']:][:treeConnectRequest['PathLength']] 2733 UNCOrShare = path.decode('utf-16le') 2734 2735 # Is this a UNC? 2736 if ntpath.ismount(UNCOrShare): 2737 path = UNCOrShare.split('\\')[3] 2738 else: 2739 path = ntpath.basename(UNCOrShare) 2740 2741 share = searchShare(connId, path.upper(), smbServer) 2742 if share is not None: 2743 # Simple way to generate a Tid 2744 if len(connData['ConnectedShares']) == 0: 2745 tid = 1 2746 else: 2747 tid = connData['ConnectedShares'].keys()[-1] + 1 2748 connData['ConnectedShares'][tid] = share 2749 connData['ConnectedShares'][tid]['shareName'] = path 2750 respPacket['TreeID'] = tid 2751 smbServer.log("Connecting Share(%d:%s)" % (tid,path)) 2752 else: 2753 smbServer.log("SMB2_TREE_CONNECT not found %s" % path, logging.ERROR) 2754 errorCode = STATUS_OBJECT_PATH_NOT_FOUND 2755 respPacket['Status'] = errorCode 2756 ## 2757 2758 if path == 'IPC$': 2759 respSMBCommand['ShareType'] = smb2.SMB2_SHARE_TYPE_PIPE 2760 respSMBCommand['ShareFlags'] = 0x30 2761 else: 2762 respSMBCommand['ShareType'] = smb2.SMB2_SHARE_TYPE_DISK 2763 respSMBCommand['ShareFlags'] = 0x0 2764 2765 respSMBCommand['Capabilities'] = 0 2766 respSMBCommand['MaximalAccess'] = 0x000f01ff 2767 2768 respPacket['Data'] = respSMBCommand 2769 2770 smbServer.setConnectionData(connId, connData) 2771 2772 return None, [respPacket], errorCode 2773 2774 @staticmethod 2775 def smb2Create(connId, smbServer, recvPacket): 2776 connData = smbServer.getConnectionData(connId) 2777 2778 respSMBCommand = smb2.SMB2Create_Response() 2779 2780 ntCreateRequest = smb2.SMB2Create(recvPacket['Data']) 2781 2782 respSMBCommand['Buffer'] = '\x00' 2783 # Get the Tid associated 2784 if connData['ConnectedShares'].has_key(recvPacket['TreeID']): 2785 # If we have a rootFid, the path is relative to that fid 2786 errorCode = STATUS_SUCCESS 2787 if connData['ConnectedShares'][recvPacket['TreeID']].has_key('path'): 2788 path = connData['ConnectedShares'][recvPacket['TreeID']]['path'] 2789 else: 2790 path = 'NONE' 2791 errorCode = STATUS_ACCESS_DENIED 2792 2793 deleteOnClose = False 2794 2795 fileName = os.path.normpath(ntCreateRequest['Buffer'][:ntCreateRequest['NameLength']].decode('utf-16le').replace('\\','/')) 2796 if len(fileName) > 0 and (fileName[0] == '/' or fileName[0] == '\\'): 2797 # strip leading '/' 2798 fileName = fileName[1:] 2799 pathName = os.path.join(path,fileName) 2800 createDisposition = ntCreateRequest['CreateDisposition'] 2801 mode = 0 2802 2803 if createDisposition == smb2.FILE_SUPERSEDE: 2804 mode |= os.O_TRUNC | os.O_CREAT 2805 elif createDisposition & smb2.FILE_OVERWRITE_IF == smb2.FILE_OVERWRITE_IF: 2806 mode |= os.O_TRUNC | os.O_CREAT 2807 elif createDisposition & smb2.FILE_OVERWRITE == smb2.FILE_OVERWRITE: 2808 if os.path.exists(pathName) is True: 2809 mode |= os.O_TRUNC 2810 else: 2811 errorCode = STATUS_NO_SUCH_FILE 2812 elif createDisposition & smb2.FILE_OPEN_IF == smb2.FILE_OPEN_IF: 2813 if os.path.exists(pathName) is True: 2814 mode |= os.O_TRUNC 2815 else: 2816 mode |= os.O_TRUNC | os.O_CREAT 2817 elif createDisposition & smb2.FILE_CREATE == smb2.FILE_CREATE: 2818 if os.path.exists(pathName) is True: 2819 errorCode = STATUS_OBJECT_NAME_COLLISION 2820 else: 2821 mode |= os.O_CREAT 2822 elif createDisposition & smb2.FILE_OPEN == smb2.FILE_OPEN: 2823 if os.path.exists(pathName) is not True and smbServer.getRegisteredNamedPipes().has_key(unicode(pathName)) is not True: 2824 errorCode = STATUS_NO_SUCH_FILE 2825 2826 if errorCode == STATUS_SUCCESS: 2827 desiredAccess = ntCreateRequest['DesiredAccess'] 2828 if (desiredAccess & smb2.FILE_READ_DATA) or (desiredAccess & smb2.GENERIC_READ): 2829 mode |= os.O_RDONLY 2830 if (desiredAccess & smb2.FILE_WRITE_DATA) or (desiredAccess & smb2.GENERIC_WRITE): 2831 if (desiredAccess & smb2.FILE_READ_DATA) or (desiredAccess & smb2.GENERIC_READ): 2832 mode |= os.O_RDWR #| os.O_APPEND 2833 else: 2834 mode |= os.O_WRONLY #| os.O_APPEND 2835 if desiredAccess & smb2.GENERIC_ALL: 2836 mode |= os.O_RDWR #| os.O_APPEND 2837 2838 createOptions = ntCreateRequest['CreateOptions'] 2839 if mode & os.O_CREAT == os.O_CREAT: 2840 if createOptions & smb2.FILE_DIRECTORY_FILE == smb2.FILE_DIRECTORY_FILE: 2841 try: 2842 # Let's create the directory 2843 os.mkdir(pathName) 2844 mode = os.O_RDONLY 2845 except Exception, e: 2846 smbServer.log("SMB2_CREATE: %s,%s,%s" % (pathName,mode,e),logging.ERROR) 2847 errorCode = STATUS_ACCESS_DENIED 2848 if createOptions & smb2.FILE_NON_DIRECTORY_FILE == smb2.FILE_NON_DIRECTORY_FILE: 2849 # If the file being opened is a directory, the server MUST fail the request with 2850 # STATUS_FILE_IS_A_DIRECTORY in the Status field of the SMB Header in the server 2851 # response. 2852 if os.path.isdir(pathName) is True: 2853 errorCode = STATUS_FILE_IS_A_DIRECTORY 2854 2855 if createOptions & smb2.FILE_DELETE_ON_CLOSE == smb2.FILE_DELETE_ON_CLOSE: 2856 deleteOnClose = True 2857 2858 if errorCode == STATUS_SUCCESS: 2859 try: 2860 if os.path.isdir(pathName) and sys.platform == 'win32': 2861 fid = VOID_FILE_DESCRIPTOR 2862 else: 2863 if sys.platform == 'win32': 2864 mode |= os.O_BINARY 2865 if smbServer.getRegisteredNamedPipes().has_key(unicode(pathName)): 2866 fid = PIPE_FILE_DESCRIPTOR 2867 sock = socket.socket() 2868 sock.connect(smbServer.getRegisteredNamedPipes()[unicode(pathName)]) 2869 else: 2870 fid = os.open(pathName, mode) 2871 except Exception, e: 2872 smbServer.log("SMB2_CREATE: %s,%s,%s" % (pathName,mode,e),logging.ERROR) 2873 #print e 2874 fid = 0 2875 errorCode = STATUS_ACCESS_DENIED 2876 else: 2877 errorCode = STATUS_SMB_BAD_TID 2878 2879 if errorCode == STATUS_SUCCESS: 2880 # Simple way to generate a fid 2881 fakefid = uuid.generate() 2882 2883 respSMBCommand['FileID'] = fakefid 2884 respSMBCommand['CreateAction'] = createDisposition 2885 2886 if fid == PIPE_FILE_DESCRIPTOR: 2887 respSMBCommand['CreationTime'] = 0 2888 respSMBCommand['LastAccessTime'] = 0 2889 respSMBCommand['LastWriteTime'] = 0 2890 respSMBCommand['ChangeTime'] = 0 2891 respSMBCommand['AllocationSize'] = 4096 2892 respSMBCommand['EndOfFile'] = 0 2893 respSMBCommand['FileAttributes'] = 0x80 2894 2895 else: 2896 if os.path.isdir(pathName): 2897 respSMBCommand['FileAttributes'] = smb.SMB_FILE_ATTRIBUTE_DIRECTORY 2898 else: 2899 respSMBCommand['FileAttributes'] = ntCreateRequest['FileAttributes'] 2900 # Let's get this file's information 2901 respInfo, errorCode = queryPathInformation('',pathName,level= smb.SMB_QUERY_FILE_ALL_INFO) 2902 if errorCode == STATUS_SUCCESS: 2903 respSMBCommand['CreationTime'] = respInfo['CreationTime'] 2904 respSMBCommand['LastAccessTime'] = respInfo['LastAccessTime'] 2905 respSMBCommand['LastWriteTime'] = respInfo['LastWriteTime'] 2906 respSMBCommand['LastChangeTime'] = respInfo['LastChangeTime'] 2907 respSMBCommand['FileAttributes'] = respInfo['ExtFileAttributes'] 2908 respSMBCommand['AllocationSize'] = respInfo['AllocationSize'] 2909 respSMBCommand['EndOfFile'] = respInfo['EndOfFile'] 2910 2911 if errorCode == STATUS_SUCCESS: 2912 # Let's store the fid for the connection 2913 # smbServer.log('Create file %s, mode:0x%x' % (pathName, mode)) 2914 connData['OpenedFiles'][fakefid] = {} 2915 connData['OpenedFiles'][fakefid]['FileHandle'] = fid 2916 connData['OpenedFiles'][fakefid]['FileName'] = pathName 2917 connData['OpenedFiles'][fakefid]['DeleteOnClose'] = deleteOnClose 2918 connData['OpenedFiles'][fakefid]['Open'] = {} 2919 connData['OpenedFiles'][fakefid]['Open']['EnumerationLocation'] = 0 2920 connData['OpenedFiles'][fakefid]['Open']['EnumerationSearchPattern'] = '' 2921 if fid == PIPE_FILE_DESCRIPTOR: 2922 connData['OpenedFiles'][fakefid]['Socket'] = sock 2923 else: 2924 respSMBCommand = smb2.SMB2Error() 2925 2926 if errorCode == STATUS_SUCCESS: 2927 connData['LastRequest']['SMB2_CREATE'] = respSMBCommand 2928 smbServer.setConnectionData(connId, connData) 2929 2930 return [respSMBCommand], None, errorCode 2931 2932 @staticmethod 2933 def smb2Close(connId, smbServer, recvPacket): 2934 connData = smbServer.getConnectionData(connId) 2935 2936 respSMBCommand = smb2.SMB2Close_Response() 2937 2938 closeRequest = smb2.SMB2Close(recvPacket['Data']) 2939 2940 if str(closeRequest['FileID']) == '\xff'*16: 2941 # Let's take the data from the lastRequest 2942 if connData['LastRequest'].has_key('SMB2_CREATE'): 2943 fileID = connData['LastRequest']['SMB2_CREATE']['FileID'] 2944 else: 2945 fileID = str(closeRequest['FileID']) 2946 else: 2947 fileID = str(closeRequest['FileID']) 2948 2949 if connData['OpenedFiles'].has_key(fileID): 2950 errorCode = STATUS_SUCCESS 2951 fileHandle = connData['OpenedFiles'][fileID]['FileHandle'] 2952 pathName = connData['OpenedFiles'][fileID]['FileName'] 2953 infoRecord = None 2954 try: 2955 if fileHandle == PIPE_FILE_DESCRIPTOR: 2956 connData['OpenedFiles'][fileID]['Socket'].close() 2957 elif fileHandle != VOID_FILE_DESCRIPTOR: 2958 os.close(fileHandle) 2959 infoRecord, errorCode = queryFileInformation(os.path.dirname(pathName), os.path.basename(pathName), smb2.SMB2_FILE_NETWORK_OPEN_INFO) 2960 except Exception, e: 2961 smbServer.log("SMB2_CLOSE %s" % e, logging.ERROR) 2962 errorCode = STATUS_INVALID_HANDLE 2963 else: 2964 # Check if the file was marked for removal 2965 if connData['OpenedFiles'][fileID]['DeleteOnClose'] is True: 2966 try: 2967 if os.path.isdir(pathName): 2968 shutil.rmtree(connData['OpenedFiles'][fileID]['FileName']) 2969 else: 2970 os.remove(connData['OpenedFiles'][fileID]['FileName']) 2971 except Exception, e: 2972 smbServer.log("SMB2_CLOSE %s" % e, logging.ERROR) 2973 errorCode = STATUS_ACCESS_DENIED 2974 2975 # Now fill out the response 2976 if infoRecord is not None: 2977 respSMBCommand['CreationTime'] = infoRecord['CreationTime'] 2978 respSMBCommand['LastAccessTime'] = infoRecord['LastAccessTime'] 2979 respSMBCommand['LastWriteTime'] = infoRecord['LastWriteTime'] 2980 respSMBCommand['ChangeTime'] = infoRecord['ChangeTime'] 2981 respSMBCommand['AllocationSize'] = infoRecord['AllocationSize'] 2982 respSMBCommand['EndofFile'] = infoRecord['EndOfFile'] 2983 respSMBCommand['FileAttributes'] = infoRecord['FileAttributes'] 2984 if errorCode == STATUS_SUCCESS: 2985 del(connData['OpenedFiles'][fileID]) 2986 else: 2987 errorCode = STATUS_INVALID_HANDLE 2988 2989 smbServer.setConnectionData(connId, connData) 2990 return [respSMBCommand], None, errorCode 2991 2992 @staticmethod 2993 def smb2QueryInfo(connId, smbServer, recvPacket): 2994 connData = smbServer.getConnectionData(connId) 2995 2996 respSMBCommand = smb2.SMB2QueryInfo_Response() 2997 2998 queryInfo = smb2.SMB2QueryInfo(recvPacket['Data']) 2999 3000 errorCode = STATUS_SUCCESS 3001 3002 respSMBCommand['OutputBufferOffset'] = 0x48 3003 respSMBCommand['Buffer'] = '\x00' 3004 3005 if str(queryInfo['FileID']) == '\xff'*16: 3006 # Let's take the data from the lastRequest 3007 if connData['LastRequest'].has_key('SMB2_CREATE'): 3008 fileID = connData['LastRequest']['SMB2_CREATE']['FileID'] 3009 else: 3010 fileID = str(queryInfo['FileID']) 3011 else: 3012 fileID = str(queryInfo['FileID']) 3013 3014 if connData['ConnectedShares'].has_key(recvPacket['TreeID']): 3015 if connData['OpenedFiles'].has_key(fileID): 3016 fileName = connData['OpenedFiles'][fileID]['FileName'] 3017 3018 if queryInfo['InfoType'] == smb2.SMB2_0_INFO_FILE: 3019 if queryInfo['FileInfoClass'] == smb2.SMB2_FILE_INTERNAL_INFO: 3020 # No need to call queryFileInformation, we have the data here 3021 infoRecord = smb2.FileInternalInformation() 3022 infoRecord['IndexNumber'] = fileID 3023 else: 3024 infoRecord, errorCode = queryFileInformation(os.path.dirname(fileName), os.path.basename(fileName), queryInfo['FileInfoClass']) 3025 elif queryInfo['InfoType'] == smb2.SMB2_0_INFO_FILESYSTEM: 3026 infoRecord = queryFsInformation(os.path.dirname(fileName), os.path.basename(fileName), queryInfo['FileInfoClass']) 3027 elif queryInfo['InfoType'] == smb2.SMB2_0_INFO_SECURITY: 3028 # Failing for now, until we support it 3029 infoRecord = None 3030 errorCode = STATUS_ACCESS_DENIED 3031 else: 3032 smbServer.log("queryInfo not supported (%x)" % queryInfo['InfoType'], logging.ERROR) 3033 3034 if infoRecord is not None: 3035 respSMBCommand['OutputBufferLength'] = len(infoRecord) 3036 respSMBCommand['Buffer'] = infoRecord 3037 else: 3038 errorCode = STATUS_INVALID_HANDLE 3039 else: 3040 errorCode = STATUS_SMB_BAD_TID 3041 3042 3043 smbServer.setConnectionData(connId, connData) 3044 return [respSMBCommand], None, errorCode 3045 3046 @staticmethod 3047 def smb2SetInfo(connId, smbServer, recvPacket): 3048 connData = smbServer.getConnectionData(connId) 3049 3050 respSMBCommand = smb2.SMB2SetInfo_Response() 3051 3052 setInfo = smb2.SMB2SetInfo(recvPacket['Data']) 3053 3054 errorCode = STATUS_SUCCESS 3055 3056 if str(setInfo['FileID']) == '\xff'*16: 3057 # Let's take the data from the lastRequest 3058 if connData['LastRequest'].has_key('SMB2_CREATE'): 3059 fileID = connData['LastRequest']['SMB2_CREATE']['FileID'] 3060 else: 3061 fileID = str(setInfo['FileID']) 3062 else: 3063 fileID = str(setInfo['FileID']) 3064 3065 if connData['ConnectedShares'].has_key(recvPacket['TreeID']): 3066 path = connData['ConnectedShares'][recvPacket['TreeID']]['path'] 3067 if connData['OpenedFiles'].has_key(fileID): 3068 pathName = connData['OpenedFiles'][fileID]['FileName'] 3069 3070 if setInfo['InfoType'] == smb2.SMB2_0_INFO_FILE: 3071 # The file information is being set 3072 informationLevel = setInfo['FileInfoClass'] 3073 if informationLevel == smb2.SMB2_FILE_DISPOSITION_INFO: 3074 infoRecord = smb.SMBSetFileDispositionInfo(setInfo['Buffer']) 3075 if infoRecord['DeletePending'] > 0: 3076 # Mark this file for removal after closed 3077 connData['OpenedFiles'][fileID]['DeleteOnClose'] = True 3078 elif informationLevel == smb2.SMB2_FILE_BASIC_INFO: 3079 infoRecord = smb.SMBSetFileBasicInfo(setInfo['Buffer']) 3080 # Creation time won't be set, the other ones we play with. 3081 atime = infoRecord['LastWriteTime'] 3082 if atime == 0: 3083 atime = -1 3084 else: 3085 atime = getUnixTime(atime) 3086 mtime = infoRecord['ChangeTime'] 3087 if mtime == 0: 3088 mtime = -1 3089 else: 3090 mtime = getUnixTime(mtime) 3091 if atime > 0 and mtime > 0: 3092 os.utime(pathName,(atime,mtime)) 3093 elif informationLevel == smb2.SMB2_FILE_END_OF_FILE_INFO: 3094 fileHandle = connData['OpenedFiles'][fileID]['FileHandle'] 3095 infoRecord = smb.SMBSetFileEndOfFileInfo(setInfo['Buffer']) 3096 if infoRecord['EndOfFile'] > 0: 3097 os.lseek(fileHandle, infoRecord['EndOfFile']-1, 0) 3098 os.write(fileHandle, '\x00') 3099 elif informationLevel == smb2.SMB2_FILE_RENAME_INFO: 3100 renameInfo = smb2.FILE_RENAME_INFORMATION_TYPE_2(setInfo['Buffer']) 3101 newPathName = os.path.join(path,renameInfo['FileName'].decode('utf-16le').replace('\\', '/')) 3102 if renameInfo['ReplaceIfExists'] == 0 and os.path.exists(newPathName): 3103 return [smb2.SMB2Error()], None, STATUS_OBJECT_NAME_COLLISION 3104 try: 3105 os.rename(pathName,newPathName) 3106 connData['OpenedFiles'][fileID]['FileName'] = newPathName 3107 except Exception, e: 3108 smbServer.log("smb2SetInfo: %s" % e, logging.ERROR) 3109 errorCode = STATUS_ACCESS_DENIED 3110 else: 3111 smbServer.log('Unknown level for set file info! 0x%x' % informationLevel, logging.ERROR) 3112 # UNSUPPORTED 3113 errorCode = STATUS_NOT_SUPPORTED 3114 #elif setInfo['InfoType'] == smb2.SMB2_0_INFO_FILESYSTEM: 3115 # # The underlying object store information is being set. 3116 # setInfo = queryFsInformation('/', fileName, queryInfo['FileInfoClass']) 3117 #elif setInfo['InfoType'] == smb2.SMB2_0_INFO_SECURITY: 3118 # # The security information is being set. 3119 # # Failing for now, until we support it 3120 # infoRecord = None 3121 # errorCode = STATUS_ACCESS_DENIED 3122 #elif setInfo['InfoType'] == smb2.SMB2_0_INFO_QUOTA: 3123 # # The underlying object store quota information is being set. 3124 # setInfo = queryFsInformation('/', fileName, queryInfo['FileInfoClass']) 3125 else: 3126 smbServer.log("setInfo not supported (%x)" % setInfo['InfoType'], logging.ERROR) 3127 3128 else: 3129 errorCode = STATUS_INVALID_HANDLE 3130 else: 3131 errorCode = STATUS_SMB_BAD_TID 3132 3133 3134 smbServer.setConnectionData(connId, connData) 3135 return [respSMBCommand], None, errorCode 3136 3137 @staticmethod 3138 def smb2Write(connId, smbServer, recvPacket): 3139 connData = smbServer.getConnectionData(connId) 3140 3141 respSMBCommand = smb2.SMB2Write_Response() 3142 writeRequest = smb2.SMB2Write(recvPacket['Data']) 3143 3144 respSMBCommand['Buffer'] = '\x00' 3145 3146 if str(writeRequest['FileID']) == '\xff'*16: 3147 # Let's take the data from the lastRequest 3148 if connData['LastRequest'].has_key('SMB2_CREATE'): 3149 fileID = connData['LastRequest']['SMB2_CREATE']['FileID'] 3150 else: 3151 fileID = str(writeRequest['FileID']) 3152 else: 3153 fileID = str(writeRequest['FileID']) 3154 3155 if connData['OpenedFiles'].has_key(fileID): 3156 fileHandle = connData['OpenedFiles'][fileID]['FileHandle'] 3157 errorCode = STATUS_SUCCESS 3158 try: 3159 if fileHandle != PIPE_FILE_DESCRIPTOR: 3160 offset = writeRequest['Offset'] 3161 # If we're trying to write past the file end we just skip the write call (Vista does this) 3162 if os.lseek(fileHandle, 0, 2) >= offset: 3163 os.lseek(fileHandle,offset,0) 3164 os.write(fileHandle,writeRequest['Buffer']) 3165 else: 3166 sock = connData['OpenedFiles'][fileID]['Socket'] 3167 sock.send(writeRequest['Buffer']) 3168 3169 respSMBCommand['Count'] = writeRequest['Length'] 3170 respSMBCommand['Remaining']= 0xff 3171 except Exception, e: 3172 smbServer.log('SMB2_WRITE: %s' % e, logging.ERROR) 3173 errorCode = STATUS_ACCESS_DENIED 3174 else: 3175 errorCode = STATUS_INVALID_HANDLE 3176 3177 smbServer.setConnectionData(connId, connData) 3178 return [respSMBCommand], None, errorCode 3179 3180 @staticmethod 3181 def smb2Read(connId, smbServer, recvPacket): 3182 connData = smbServer.getConnectionData(connId) 3183 3184 respSMBCommand = smb2.SMB2Read_Response() 3185 readRequest = smb2.SMB2Read(recvPacket['Data']) 3186 3187 respSMBCommand['Buffer'] = '\x00' 3188 3189 if str(readRequest['FileID']) == '\xff'*16: 3190 # Let's take the data from the lastRequest 3191 if connData['LastRequest'].has_key('SMB2_CREATE'): 3192 fileID = connData['LastRequest']['SMB2_CREATE']['FileID'] 3193 else: 3194 fileID = str(readRequest['FileID']) 3195 else: 3196 fileID = str(readRequest['FileID']) 3197 3198 if connData['OpenedFiles'].has_key(fileID): 3199 fileHandle = connData['OpenedFiles'][fileID]['FileHandle'] 3200 errorCode = 0 3201 try: 3202 if fileHandle != PIPE_FILE_DESCRIPTOR: 3203 offset = readRequest['Offset'] 3204 os.lseek(fileHandle,offset,0) 3205 content = os.read(fileHandle,readRequest['Length']) 3206 else: 3207 sock = connData['OpenedFiles'][fileID]['Socket'] 3208 content = sock.recv(readRequest['Length']) 3209 3210 respSMBCommand['DataOffset'] = 0x50 3211 respSMBCommand['DataLength'] = len(content) 3212 respSMBCommand['DataRemaining']= 0 3213 respSMBCommand['Buffer'] = content 3214 except Exception, e: 3215 smbServer.log('SMB2_READ: %s ' % e, logging.ERROR) 3216 errorCode = STATUS_ACCESS_DENIED 3217 else: 3218 errorCode = STATUS_INVALID_HANDLE 3219 3220 smbServer.setConnectionData(connId, connData) 3221 return [respSMBCommand], None, errorCode 3222 3223 @staticmethod 3224 def smb2Flush(connId, smbServer, recvPacket): 3225 connData = smbServer.getConnectionData(connId) 3226 3227 respSMBCommand = smb2.SMB2Flush_Response() 3228 flushRequest = smb2.SMB2Flush(recvPacket['Data']) 3229 3230 if connData['OpenedFiles'].has_key(str(flushRequest['FileID'])): 3231 fileHandle = connData['OpenedFiles'][str(flushRequest['FileID'])]['FileHandle'] 3232 errorCode = STATUS_SUCCESS 3233 try: 3234 os.fsync(fileHandle) 3235 except Exception, e: 3236 smbServer.log("SMB2_FLUSH %s" % e, logging.ERROR) 3237 errorCode = STATUS_ACCESS_DENIED 3238 else: 3239 errorCode = STATUS_INVALID_HANDLE 3240 3241 smbServer.setConnectionData(connId, connData) 3242 return [respSMBCommand], None, errorCode 3243 3244 3245 @staticmethod 3246 def smb2QueryDirectory(connId, smbServer, recvPacket): 3247 connData = smbServer.getConnectionData(connId) 3248 respSMBCommand = smb2.SMB2QueryDirectory_Response() 3249 queryDirectoryRequest = smb2.SMB2QueryDirectory(recvPacket['Data']) 3250 3251 respSMBCommand['Buffer'] = '\x00' 3252 3253 # The server MUST locate the tree connection, as specified in section 3.3.5.2.11. 3254 if connData['ConnectedShares'].has_key(recvPacket['TreeID']) is False: 3255 return [smb2.SMB2Error()], None, STATUS_NETWORK_NAME_DELETED 3256 3257 # Next, the server MUST locate the open for the directory to be queried 3258 # If no open is found, the server MUST fail the request with STATUS_FILE_CLOSED 3259 if str(queryDirectoryRequest['FileID']) == '\xff'*16: 3260 # Let's take the data from the lastRequest 3261 if connData['LastRequest'].has_key('SMB2_CREATE'): 3262 fileID = connData['LastRequest']['SMB2_CREATE']['FileID'] 3263 else: 3264 fileID = str(queryDirectoryRequest['FileID']) 3265 else: 3266 fileID = str(queryDirectoryRequest['FileID']) 3267 3268 if connData['OpenedFiles'].has_key(fileID) is False: 3269 return [smb2.SMB2Error()], None, STATUS_FILE_CLOSED 3270 3271 # If the open is not an open to a directory, the request MUST be failed 3272 # with STATUS_INVALID_PARAMETER. 3273 if os.path.isdir(connData['OpenedFiles'][fileID]['FileName']) is False: 3274 return [smb2.SMB2Error()], None, STATUS_INVALID_PARAMETER 3275 3276 # If any other information class is specified in the FileInformationClass 3277 # field of the SMB2 QUERY_DIRECTORY Request, the server MUST fail the 3278 # operation with STATUS_INVALID_INFO_CLASS. 3279 if queryDirectoryRequest['FileInformationClass'] not in ( 3280 smb2.FILE_DIRECTORY_INFORMATION, smb2.FILE_FULL_DIRECTORY_INFORMATION, smb2.FILEID_FULL_DIRECTORY_INFORMATION, 3281 smb2.FILE_BOTH_DIRECTORY_INFORMATION, smb2.FILEID_BOTH_DIRECTORY_INFORMATION, smb2.FILENAMES_INFORMATION): 3282 return [smb2.SMB2Error()], None, STATUS_INVALID_INFO_CLASS 3283 3284 # If SMB2_REOPEN is set in the Flags field of the SMB2 QUERY_DIRECTORY 3285 # Request, the server SHOULD<326> set Open.EnumerationLocation to 0 3286 # and Open.EnumerationSearchPattern to an empty string. 3287 if queryDirectoryRequest['Flags'] & smb2.SMB2_REOPEN: 3288 connData['OpenedFiles'][fileID]['Open']['EnumerationLocation'] = 0 3289 connData['OpenedFiles'][fileID]['Open']['EnumerationSearchPattern'] = '' 3290 3291 # If SMB2_RESTART_SCANS is set in the Flags field of the SMB2 3292 # QUERY_DIRECTORY Request, the server MUST set 3293 # Open.EnumerationLocation to 0. 3294 if queryDirectoryRequest['Flags'] & smb2.SMB2_RESTART_SCANS: 3295 connData['OpenedFiles'][fileID]['Open']['EnumerationLocation'] = 0 3296 3297 # If Open.EnumerationLocation is 0 and Open.EnumerationSearchPattern 3298 # is an empty string, then Open.EnumerationSearchPattern MUST be set 3299 # to the search pattern specified in the SMB2 QUERY_DIRECTORY by 3300 # FileNameOffset and FileNameLength. If FileNameLength is 0, the server 3301 # SHOULD<327> set Open.EnumerationSearchPattern as "*" to search all entries. 3302 3303 pattern = queryDirectoryRequest['Buffer'].decode('utf-16le') 3304 if connData['OpenedFiles'][fileID]['Open']['EnumerationLocation'] == 0 and \ 3305 connData['OpenedFiles'][fileID]['Open']['EnumerationSearchPattern'] == '': 3306 if pattern == '': 3307 pattern = '*' 3308 connData['OpenedFiles'][fileID]['Open']['EnumerationSearchPattern'] = pattern 3309 3310 # If SMB2_INDEX_SPECIFIED is set and FileNameLength is not zero, 3311 # the server MUST set Open.EnumerationSearchPattern to the search pattern 3312 # specified in the request by FileNameOffset and FileNameLength. 3313 if queryDirectoryRequest['Flags'] & smb2.SMB2_INDEX_SPECIFIED and \ 3314 queryDirectoryRequest['FileNameLength'] > 0: 3315 connData['OpenedFiles'][fileID]['Open']['EnumerationSearchPattern'] = pattern 3316 3317 pathName = os.path.join(os.path.normpath(connData['OpenedFiles'][fileID]['FileName']),pattern) 3318 searchResult, searchCount, errorCode = findFirst2(os.path.dirname(pathName), 3319 os.path.basename(pathName), 3320 queryDirectoryRequest['FileInformationClass'], 3321 smb.ATTR_DIRECTORY, isSMB2 = True ) 3322 3323 if errorCode != STATUS_SUCCESS: 3324 return [smb2.SMB2Error()], None, errorCode 3325 3326 if searchCount > 2 and pattern == '*': 3327 # strip . and .. 3328 searchCount -= 2 3329 searchResult = searchResult[2:] 3330 3331 if searchCount == 0 and connData['OpenedFiles'][fileID]['Open']['EnumerationLocation'] == 0: 3332 return [smb2.SMB2Error()], None, STATUS_NO_SUCH_FILE 3333 3334 if connData['OpenedFiles'][fileID]['Open']['EnumerationLocation'] < 0: 3335 return [smb2.SMB2Error()], None, STATUS_NO_MORE_FILES 3336 3337 totalData = 0 3338 respData = '' 3339 for nItem in range(connData['OpenedFiles'][fileID]['Open']['EnumerationLocation'], searchCount): 3340 connData['OpenedFiles'][fileID]['Open']['EnumerationLocation'] += 1 3341 if queryDirectoryRequest['Flags'] & smb2.SL_RETURN_SINGLE_ENTRY: 3342 # If single entry is requested we must clear the NextEntryOffset 3343 searchResult[nItem]['NextEntryOffset'] = 0 3344 data = searchResult[nItem].getData() 3345 lenData = len(data) 3346 padLen = (8-(lenData % 8)) %8 3347 3348 if (totalData+lenData) >= queryDirectoryRequest['OutputBufferLength']: 3349 connData['OpenedFiles'][fileID]['Open']['EnumerationLocation'] -= 1 3350 break 3351 else: 3352 respData += data + '\x00'*padLen 3353 totalData += lenData + padLen 3354 3355 if queryDirectoryRequest['Flags'] & smb2.SL_RETURN_SINGLE_ENTRY: 3356 break 3357 3358 if connData['OpenedFiles'][fileID]['Open']['EnumerationLocation'] >= searchCount: 3359 connData['OpenedFiles'][fileID]['Open']['EnumerationLocation'] = -1 3360 3361 respSMBCommand['OutputBufferOffset'] = 0x48 3362 respSMBCommand['OutputBufferLength'] = totalData 3363 respSMBCommand['Buffer'] = respData 3364 3365 smbServer.setConnectionData(connId, connData) 3366 return [respSMBCommand], None, errorCode 3367 3368 @staticmethod 3369 def smb2ChangeNotify(connId, smbServer, recvPacket): 3370 3371 return [smb2.SMB2Error()], None, STATUS_NOT_SUPPORTED 3372 3373 @staticmethod 3374 def smb2Echo(connId, smbServer, recvPacket): 3375 3376 respSMBCommand = smb2.SMB2Echo_Response() 3377 3378 return [respSMBCommand], None, STATUS_SUCCESS 3379 3380 @staticmethod 3381 def smb2TreeDisconnect(connId, smbServer, recvPacket): 3382 connData = smbServer.getConnectionData(connId) 3383 3384 respSMBCommand = smb2.SMB2TreeDisconnect_Response() 3385 3386 if connData['ConnectedShares'].has_key(recvPacket['TreeID']): 3387 smbServer.log("Disconnecting Share(%d:%s)" % (recvPacket['TreeID'],connData['ConnectedShares'][recvPacket['TreeID']]['shareName'])) 3388 del(connData['ConnectedShares'][recvPacket['TreeID']]) 3389 errorCode = STATUS_SUCCESS 3390 else: 3391 # STATUS_SMB_BAD_TID 3392 errorCode = STATUS_SMB_BAD_TID 3393 3394 3395 smbServer.setConnectionData(connId, connData) 3396 return [respSMBCommand], None, errorCode 3397 3398 @staticmethod 3399 def smb2Logoff(connId, smbServer, recvPacket): 3400 connData = smbServer.getConnectionData(connId) 3401 3402 respSMBCommand = smb2.SMB2Logoff_Response() 3403 3404 if recvPacket['SessionID'] != connData['Uid']: 3405 # STATUS_SMB_BAD_UID 3406 errorCode = STATUS_SMB_BAD_UID 3407 else: 3408 errorCode = STATUS_SUCCESS 3409 3410 connData['Uid'] = 0 3411 3412 smbServer.setConnectionData(connId, connData) 3413 return [respSMBCommand], None, errorCode 3414 3415 @staticmethod 3416 def smb2Ioctl(connId, smbServer, recvPacket): 3417 connData = smbServer.getConnectionData(connId) 3418 3419 respSMBCommand = smb2.SMB2Ioctl_Response() 3420 ioctlRequest = smb2.SMB2Ioctl(recvPacket['Data']) 3421 3422 ioctls = smbServer.getIoctls() 3423 if ioctls.has_key(ioctlRequest['CtlCode']): 3424 outputData, errorCode = ioctls[ioctlRequest['CtlCode']](connId, smbServer, ioctlRequest) 3425 if errorCode == STATUS_SUCCESS: 3426 respSMBCommand['CtlCode'] = ioctlRequest['CtlCode'] 3427 respSMBCommand['FileID'] = ioctlRequest['FileID'] 3428 respSMBCommand['InputOffset'] = 0 3429 respSMBCommand['InputCount'] = 0 3430 respSMBCommand['OutputOffset'] = 0x70 3431 respSMBCommand['OutputCount'] = len(outputData) 3432 respSMBCommand['Flags'] = 0 3433 respSMBCommand['Buffer'] = outputData 3434 else: 3435 respSMBCommand = outputData 3436 else: 3437 smbServer.log("Ioctl not implemented command: 0x%x" % ioctlRequest['CtlCode'],logging.DEBUG) 3438 errorCode = STATUS_INVALID_DEVICE_REQUEST 3439 respSMBCommand = smb2.SMB2Error() 3440 3441 smbServer.setConnectionData(connId, connData) 3442 return [respSMBCommand], None, errorCode 3443 3444 @staticmethod 3445 def smb2Lock(connId, smbServer, recvPacket): 3446 connData = smbServer.getConnectionData(connId) 3447 3448 respSMBCommand = smb2.SMB2Lock_Response() 3449 3450 # I'm actually doing nothing.. just make MacOS happy ;) 3451 errorCode = STATUS_SUCCESS 3452 3453 smbServer.setConnectionData(connId, connData) 3454 return [respSMBCommand], None, errorCode 3455 3456 @staticmethod 3457 def smb2Cancel(connId, smbServer, recvPacket): 3458 # I'm actually doing nothing 3459 return [smb2.SMB2Error()], None, STATUS_CANCELLED 3460 3461 @staticmethod 3462 def default(connId, smbServer, recvPacket): 3463 # By default we return an SMB Packet with error not implemented 3464 smbServer.log("Not implemented command: 0x%x" % recvPacket['Command'],logging.DEBUG) 3465 return [smb2.SMB2Error()], None, STATUS_NOT_SUPPORTED 3466 3467 class Ioctls: 3468 @staticmethod 3469 def fsctlDfsGetReferrals(connId, smbServer, ioctlRequest): 3470 return smb2.SMB2Error(), STATUS_FS_DRIVER_REQUIRED 3471 3472 @staticmethod 3473 def fsctlPipeTransceive(connId, smbServer, ioctlRequest): 3474 connData = smbServer.getConnectionData(connId) 3475 3476 ioctlResponse = '' 3477 3478 if connData['OpenedFiles'].has_key(str(ioctlRequest['FileID'])): 3479 fileHandle = connData['OpenedFiles'][str(ioctlRequest['FileID'])]['FileHandle'] 3480 errorCode = STATUS_SUCCESS 3481 try: 3482 if fileHandle != PIPE_FILE_DESCRIPTOR: 3483 errorCode = STATUS_INVALID_DEVICE_REQUEST 3484 else: 3485 sock = connData['OpenedFiles'][str(ioctlRequest['FileID'])]['Socket'] 3486 sock.sendall(ioctlRequest['Buffer']) 3487 ioctlResponse = sock.recv(ioctlRequest['MaxOutputResponse']) 3488 except Exception, e: 3489 smbServer.log('fsctlPipeTransceive: %s ' % e, logging.ERROR) 3490 errorCode = STATUS_ACCESS_DENIED 3491 else: 3492 errorCode = STATUS_INVALID_DEVICE_REQUEST 3493 3494 smbServer.setConnectionData(connId, connData) 3495 return ioctlResponse, errorCode 3496 3497 @staticmethod 3498 def fsctlValidateNegotiateInfo(connId, smbServer, ioctlRequest): 3499 connData = smbServer.getConnectionData(connId) 3500 3501 errorCode = STATUS_SUCCESS 3502 3503 validateNegotiateInfo = smb2.VALIDATE_NEGOTIATE_INFO(ioctlRequest['Buffer']) 3504 validateNegotiateInfo['Capabilities'] = 0 3505 validateNegotiateInfo['Guid'] = 'A'*16 3506 validateNegotiateInfo['SecurityMode'] = 1 3507 validateNegotiateInfo['Dialects'] = (smb2.SMB2_DIALECT_002,) 3508 3509 smbServer.setConnectionData(connId, connData) 3510 return validateNegotiateInfo.getData(), errorCode 3511 3512 3513 class SMBSERVERHandler(SocketServer.BaseRequestHandler): 3514 def __init__(self, request, client_address, server, select_poll = False): 3515 self.__SMB = server 3516 self.__ip, self.__port = client_address 3517 self.__request = request 3518 self.__connId = threading.currentThread().getName() 3519 self.__timeOut = 60*5 3520 self.__select_poll = select_poll 3521 #self.__connId = os.getpid() 3522 SocketServer.BaseRequestHandler.__init__(self, request, client_address, server) 3523 3524 def handle(self): 3525 self.__SMB.log("Incoming connection (%s,%d)" % (self.__ip, self.__port)) 3526 self.__SMB.addConnection(self.__connId, self.__ip, self.__port) 3527 while True: 3528 try: 3529 # Firt of all let's get the NETBIOS packet 3530 session = nmb.NetBIOSTCPSession(self.__SMB.getServerName(),'HOST', self.__ip, sess_port = self.__port, sock = self.__request, select_poll = self.__select_poll) 3531 try: 3532 p = session.recv_packet(self.__timeOut) 3533 except nmb.NetBIOSTimeout: 3534 raise 3535 except nmb.NetBIOSError: 3536 break 3537 3538 if p.get_type() == nmb.NETBIOS_SESSION_REQUEST: 3539 # Someone is requesting a session, we're gonna accept them all :) 3540 _, rn, my = p.get_trailer().split(' ') 3541 remote_name = nmb.decode_name('\x20'+rn) 3542 myname = nmb.decode_name('\x20'+my) 3543 self.__SMB.log("NetBIOS Session request (%s,%s,%s)" % (self.__ip, remote_name[1].strip(), myname[1])) 3544 r = nmb.NetBIOSSessionPacket() 3545 r.set_type(nmb.NETBIOS_SESSION_POSITIVE_RESPONSE) 3546 r.set_trailer(p.get_trailer()) 3547 self.__request.send(r.rawData()) 3548 else: 3549 resp = self.__SMB.processRequest(self.__connId, p.get_trailer()) 3550 # Send all the packets recevied. Except for big transactions this should be 3551 # a single packet 3552 for i in resp: 3553 session.send_packet(str(i)) 3554 except Exception, e: 3555 self.__SMB.log("Handle: %s" % e) 3556 #import traceback 3557 #traceback.print_exc() 3558 break 3559 3560 def finish(self): 3561 # Thread/process is dying, we should tell the main SMB thread to remove all this thread data 3562 self.__SMB.log("Closing down connection (%s,%d)" % (self.__ip, self.__port)) 3563 self.__SMB.removeConnection(self.__connId) 3564 return SocketServer.BaseRequestHandler.finish(self) 3565 3566 class SMBSERVER(SocketServer.ThreadingMixIn, SocketServer.TCPServer): 3567 #class SMBSERVER(SocketServer.ForkingMixIn, SocketServer.TCPServer): 3568 def __init__(self, server_address, handler_class=SMBSERVERHandler, config_parser = None): 3569 SocketServer.TCPServer.allow_reuse_address = True 3570 SocketServer.TCPServer.__init__(self, server_address, handler_class) 3571 3572 # Server name and OS to be presented whenever is necessary 3573 self.__serverName = '' 3574 self.__serverOS = '' 3575 self.__serverDomain = '' 3576 self.__challenge = '' 3577 self.__log = None 3578 3579 # Our ConfigParser data 3580 self.__serverConfig = config_parser 3581 3582 # Our credentials to be used during the server's lifetime 3583 self.__credentials = {} 3584 3585 # Our log file 3586 self.__logFile = '' 3587 3588 # Registered Named Pipes, format is PipeName,Socket 3589 self.__registeredNamedPipes = {} 3590 3591 # JTR dump path 3592 self.__jtr_dump_path = '' 3593 3594 # SMB2 Support flag = default not active 3595 self.__SMB2Support = False 3596 3597 # Our list of commands we will answer, by default the NOT IMPLEMENTED one 3598 self.__smbCommandsHandler = SMBCommands() 3599 self.__smbTrans2Handler = TRANS2Commands() 3600 self.__smbTransHandler = TRANSCommands() 3601 self.__smbNTTransHandler = NTTRANSCommands() 3602 self.__smb2CommandsHandler = SMB2Commands() 3603 self.__IoctlHandler = Ioctls() 3604 3605 self.__smbNTTransCommands = { 3606 # NT IOCTL, can't find doc for this 3607 0xff :self.__smbNTTransHandler.default 3608 } 3609 3610 self.__smbTransCommands = { 3611 '\\PIPE\\LANMAN' :self.__smbTransHandler.lanMan, 3612 smb.SMB.TRANS_TRANSACT_NMPIPE :self.__smbTransHandler.transactNamedPipe, 3613 } 3614 self.__smbTrans2Commands = { 3615 smb.SMB.TRANS2_FIND_FIRST2 :self.__smbTrans2Handler.findFirst2, 3616 smb.SMB.TRANS2_FIND_NEXT2 :self.__smbTrans2Handler.findNext2, 3617 smb.SMB.TRANS2_QUERY_FS_INFORMATION :self.__smbTrans2Handler.queryFsInformation, 3618 smb.SMB.TRANS2_QUERY_PATH_INFORMATION :self.__smbTrans2Handler.queryPathInformation, 3619 smb.SMB.TRANS2_QUERY_FILE_INFORMATION :self.__smbTrans2Handler.queryFileInformation, 3620 smb.SMB.TRANS2_SET_FILE_INFORMATION :self.__smbTrans2Handler.setFileInformation, 3621 smb.SMB.TRANS2_SET_PATH_INFORMATION :self.__smbTrans2Handler.setPathInformation 3622 } 3623 3624 self.__smbCommands = { 3625 #smb.SMB.SMB_COM_FLUSH: self.__smbCommandsHandler.smbComFlush, 3626 smb.SMB.SMB_COM_CREATE_DIRECTORY: self.__smbCommandsHandler.smbComCreateDirectory, 3627 smb.SMB.SMB_COM_DELETE_DIRECTORY: self.__smbCommandsHandler.smbComDeleteDirectory, 3628 smb.SMB.SMB_COM_RENAME: self.__smbCommandsHandler.smbComRename, 3629 smb.SMB.SMB_COM_DELETE: self.__smbCommandsHandler.smbComDelete, 3630 smb.SMB.SMB_COM_NEGOTIATE: self.__smbCommandsHandler.smbComNegotiate, 3631 smb.SMB.SMB_COM_SESSION_SETUP_ANDX: self.__smbCommandsHandler.smbComSessionSetupAndX, 3632 smb.SMB.SMB_COM_LOGOFF_ANDX: self.__smbCommandsHandler.smbComLogOffAndX, 3633 smb.SMB.SMB_COM_TREE_CONNECT_ANDX: self.__smbCommandsHandler.smbComTreeConnectAndX, 3634 smb.SMB.SMB_COM_TREE_DISCONNECT: self.__smbCommandsHandler.smbComTreeDisconnect, 3635 smb.SMB.SMB_COM_ECHO: self.__smbCommandsHandler.smbComEcho, 3636 smb.SMB.SMB_COM_QUERY_INFORMATION: self.__smbCommandsHandler.smbQueryInformation, 3637 smb.SMB.SMB_COM_TRANSACTION2: self.__smbCommandsHandler.smbTransaction2, 3638 smb.SMB.SMB_COM_TRANSACTION: self.__smbCommandsHandler.smbTransaction, 3639 # Not needed for now 3640 smb.SMB.SMB_COM_NT_TRANSACT: self.__smbCommandsHandler.smbNTTransact, 3641 smb.SMB.SMB_COM_QUERY_INFORMATION_DISK: self.__smbCommandsHandler.smbQueryInformationDisk, 3642 smb.SMB.SMB_COM_OPEN_ANDX: self.__smbCommandsHandler.smbComOpenAndX, 3643 smb.SMB.SMB_COM_QUERY_INFORMATION2: self.__smbCommandsHandler.smbComQueryInformation2, 3644 smb.SMB.SMB_COM_READ_ANDX: self.__smbCommandsHandler.smbComReadAndX, 3645 smb.SMB.SMB_COM_READ: self.__smbCommandsHandler.smbComRead, 3646 smb.SMB.SMB_COM_WRITE_ANDX: self.__smbCommandsHandler.smbComWriteAndX, 3647 smb.SMB.SMB_COM_WRITE: self.__smbCommandsHandler.smbComWrite, 3648 smb.SMB.SMB_COM_CLOSE: self.__smbCommandsHandler.smbComClose, 3649 smb.SMB.SMB_COM_LOCKING_ANDX: self.__smbCommandsHandler.smbComLockingAndX, 3650 smb.SMB.SMB_COM_NT_CREATE_ANDX: self.__smbCommandsHandler.smbComNtCreateAndX, 3651 0xFF: self.__smbCommandsHandler.default 3652 } 3653 3654 self.__smb2Ioctls = { 3655 smb2.FSCTL_DFS_GET_REFERRALS: self.__IoctlHandler.fsctlDfsGetReferrals, 3656 # smb2.FSCTL_PIPE_PEEK: self.__IoctlHandler.fsctlPipePeek, 3657 # smb2.FSCTL_PIPE_WAIT: self.__IoctlHandler.fsctlPipeWait, 3658 smb2.FSCTL_PIPE_TRANSCEIVE: self.__IoctlHandler.fsctlPipeTransceive, 3659 # smb2.FSCTL_SRV_COPYCHUNK: self.__IoctlHandler.fsctlSrvCopyChunk, 3660 # smb2.FSCTL_SRV_ENUMERATE_SNAPSHOTS: self.__IoctlHandler.fsctlSrvEnumerateSnapshots, 3661 # smb2.FSCTL_SRV_REQUEST_RESUME_KEY: self.__IoctlHandler.fsctlSrvRequestResumeKey, 3662 # smb2.FSCTL_SRV_READ_HASH: self.__IoctlHandler.fsctlSrvReadHash, 3663 # smb2.FSCTL_SRV_COPYCHUNK_WRITE: self.__IoctlHandler.fsctlSrvCopyChunkWrite, 3664 # smb2.FSCTL_LMR_REQUEST_RESILIENCY: self.__IoctlHandler.fsctlLmrRequestResiliency, 3665 # smb2.FSCTL_QUERY_NETWORK_INTERFACE_INFO: self.__IoctlHandler.fsctlQueryNetworkInterfaceInfo, 3666 # smb2.FSCTL_SET_REPARSE_POINT: self.__IoctlHandler.fsctlSetReparsePoint, 3667 # smb2.FSCTL_DFS_GET_REFERRALS_EX: self.__IoctlHandler.fsctlDfsGetReferralsEx, 3668 # smb2.FSCTL_FILE_LEVEL_TRIM: self.__IoctlHandler.fsctlFileLevelTrim, 3669 smb2.FSCTL_VALIDATE_NEGOTIATE_INFO: self.__IoctlHandler.fsctlValidateNegotiateInfo, 3670 } 3671 3672 self.__smb2Commands = { 3673 smb2.SMB2_NEGOTIATE: self.__smb2CommandsHandler.smb2Negotiate, 3674 smb2.SMB2_SESSION_SETUP: self.__smb2CommandsHandler.smb2SessionSetup, 3675 smb2.SMB2_LOGOFF: self.__smb2CommandsHandler.smb2Logoff, 3676 smb2.SMB2_TREE_CONNECT: self.__smb2CommandsHandler.smb2TreeConnect, 3677 smb2.SMB2_TREE_DISCONNECT: self.__smb2CommandsHandler.smb2TreeDisconnect, 3678 smb2.SMB2_CREATE: self.__smb2CommandsHandler.smb2Create, 3679 smb2.SMB2_CLOSE: self.__smb2CommandsHandler.smb2Close, 3680 smb2.SMB2_FLUSH: self.__smb2CommandsHandler.smb2Flush, 3681 smb2.SMB2_READ: self.__smb2CommandsHandler.smb2Read, 3682 smb2.SMB2_WRITE: self.__smb2CommandsHandler.smb2Write, 3683 smb2.SMB2_LOCK: self.__smb2CommandsHandler.smb2Lock, 3684 smb2.SMB2_IOCTL: self.__smb2CommandsHandler.smb2Ioctl, 3685 smb2.SMB2_CANCEL: self.__smb2CommandsHandler.smb2Cancel, 3686 smb2.SMB2_ECHO: self.__smb2CommandsHandler.smb2Echo, 3687 smb2.SMB2_QUERY_DIRECTORY: self.__smb2CommandsHandler.smb2QueryDirectory, 3688 smb2.SMB2_CHANGE_NOTIFY: self.__smb2CommandsHandler.smb2ChangeNotify, 3689 smb2.SMB2_QUERY_INFO: self.__smb2CommandsHandler.smb2QueryInfo, 3690 smb2.SMB2_SET_INFO: self.__smb2CommandsHandler.smb2SetInfo, 3691 # smb2.SMB2_OPLOCK_BREAK: self.__smb2CommandsHandler.smb2SessionSetup, 3692 0xFF: self.__smb2CommandsHandler.default 3693 } 3694 3695 # List of active connections 3696 self.__activeConnections = {} 3697 3698 def getIoctls(self): 3699 return self.__smb2Ioctls 3700 3701 def getCredentials(self): 3702 return self.__credentials 3703 3704 def removeConnection(self, name): 3705 try: 3706 del(self.__activeConnections[name]) 3707 except: 3708 pass 3709 self.log("Remaining connections %s" % self.__activeConnections.keys()) 3710 3711 def addConnection(self, name, ip, port): 3712 self.__activeConnections[name] = {} 3713 # Let's init with some know stuff we will need to have 3714 # TODO: Document what's in there 3715 #print "Current Connections", self.__activeConnections.keys() 3716 self.__activeConnections[name]['PacketNum'] = 0 3717 self.__activeConnections[name]['ClientIP'] = ip 3718 self.__activeConnections[name]['ClientPort'] = port 3719 self.__activeConnections[name]['Uid'] = 0 3720 self.__activeConnections[name]['ConnectedShares'] = {} 3721 self.__activeConnections[name]['OpenedFiles'] = {} 3722 # SID results for findfirst2 3723 self.__activeConnections[name]['SIDs'] = {} 3724 self.__activeConnections[name]['LastRequest'] = {} 3725 3726 def getActiveConnections(self): 3727 return self.__activeConnections 3728 3729 def setConnectionData(self, connId, data): 3730 self.__activeConnections[connId] = data 3731 #print "setConnectionData" 3732 #print self.__activeConnections 3733 3734 def getConnectionData(self, connId, checkStatus = True): 3735 conn = self.__activeConnections[connId] 3736 if checkStatus is True: 3737 if conn.has_key('Authenticated') is not True: 3738 # Can't keep going further 3739 raise Exception("User not Authenticated!") 3740 return conn 3741 3742 def getRegisteredNamedPipes(self): 3743 return self.__registeredNamedPipes 3744 3745 def registerNamedPipe(self, pipeName, address): 3746 self.__registeredNamedPipes[unicode(pipeName)] = address 3747 return True 3748 3749 def unregisterNamedPipe(self, pipeName): 3750 if self.__registeredNamedPipes.has_key(pipeName): 3751 del(self.__registeredNamedPipes[unicode(pipeName)]) 3752 return True 3753 return False 3754 3755 def unregisterTransaction(self, transCommand): 3756 if self.__smbTransCommands.has_key(transCommand): 3757 del(self.__smbTransCommands[transCommand]) 3758 3759 def hookTransaction(self, transCommand, callback): 3760 # If you call this function, callback will replace 3761 # the current Transaction sub command. 3762 # (don't get confused with the Transaction smbCommand) 3763 # If the transaction sub command doesn't not exist, it is added 3764 # If the transaction sub command exists, it returns the original function # replaced 3765 # 3766 # callback MUST be declared as: 3767 # callback(connId, smbServer, recvPacket, parameters, data, maxDataCount=0) 3768 # 3769 # WHERE: 3770 # 3771 # connId : the connection Id, used to grab/update information about 3772 # the current connection 3773 # smbServer : the SMBServer instance available for you to ask 3774 # configuration data 3775 # recvPacket : the full SMBPacket that triggered this command 3776 # parameters : the transaction parameters 3777 # data : the transaction data 3778 # maxDataCount: the max amount of data that can be transfered agreed 3779 # with the client 3780 # 3781 # and MUST return: 3782 # respSetup, respParameters, respData, errorCode 3783 # 3784 # WHERE: 3785 # 3786 # respSetup: the setup response of the transaction 3787 # respParameters: the parameters response of the transaction 3788 # respData: the data reponse of the transaction 3789 # errorCode: the NT error code 3790 3791 if self.__smbTransCommands.has_key(transCommand): 3792 originalCommand = self.__smbTransCommands[transCommand] 3793 else: 3794 originalCommand = None 3795 3796 self.__smbTransCommands[transCommand] = callback 3797 return originalCommand 3798 3799 def unregisterTransaction2(self, transCommand): 3800 if self.__smbTrans2Commands.has_key(transCommand): 3801 del(self.__smbTrans2Commands[transCommand]) 3802 3803 def hookTransaction2(self, transCommand, callback): 3804 # Here we should add to __smbTrans2Commands 3805 # Same description as Transaction 3806 if self.__smbTrans2Commands.has_key(transCommand): 3807 originalCommand = self.__smbTrans2Commands[transCommand] 3808 else: 3809 originalCommand = None 3810 3811 self.__smbTrans2Commands[transCommand] = callback 3812 return originalCommand 3813 3814 def unregisterNTTransaction(self, transCommand): 3815 if self.__smbNTTransCommands.has_key(transCommand): 3816 del(self.__smbNTTransCommands[transCommand]) 3817 3818 def hookNTTransaction(self, transCommand, callback): 3819 # Here we should add to __smbNTTransCommands 3820 # Same description as Transaction 3821 if self.__smbNTTransCommands.has_key(transCommand): 3822 originalCommand = self.__smbNTTransCommands[transCommand] 3823 else: 3824 originalCommand = None 3825 3826 self.__smbNTTransCommands[transCommand] = callback 3827 return originalCommand 3828 3829 def unregisterSmbCommand(self, smbCommand): 3830 if self.__smbCommands.has_key(smbCommand): 3831 del(self.__smbCommands[smbCommand]) 3832 3833 def hookSmbCommand(self, smbCommand, callback): 3834 # Here we should add to self.__smbCommands 3835 # If you call this function, callback will replace 3836 # the current smbCommand. 3837 # If smbCommand doesn't not exist, it is added 3838 # If SMB command exists, it returns the original function replaced 3839 # 3840 # callback MUST be declared as: 3841 # callback(connId, smbServer, SMBCommand, recvPacket) 3842 # 3843 # WHERE: 3844 # 3845 # connId : the connection Id, used to grab/update information about 3846 # the current connection 3847 # smbServer : the SMBServer instance available for you to ask 3848 # configuration data 3849 # SMBCommand: the SMBCommand itself, with its data and parameters. 3850 # Check smb.py:SMBCommand() for a reference 3851 # recvPacket: the full SMBPacket that triggered this command 3852 # 3853 # and MUST return: 3854 # <list of respSMBCommands>, <list of packets>, errorCode 3855 # <list of packets> has higher preference over commands, in case you 3856 # want to change the whole packet 3857 # errorCode: the NT error code 3858 # 3859 # For SMB_COM_TRANSACTION2, SMB_COM_TRANSACTION and SMB_COM_NT_TRANSACT 3860 # the callback function is slightly different: 3861 # 3862 # callback(connId, smbServer, SMBCommand, recvPacket, transCommands) 3863 # 3864 # WHERE: 3865 # 3866 # transCommands: a list of transaction subcommands already registered 3867 # 3868 3869 if self.__smbCommands.has_key(smbCommand): 3870 originalCommand = self.__smbCommands[smbCommand] 3871 else: 3872 originalCommand = None 3873 3874 self.__smbCommands[smbCommand] = callback 3875 return originalCommand 3876 3877 def unregisterSmb2Command(self, smb2Command): 3878 if self.__smb2Commands.has_key(smb2Command): 3879 del(self.__smb2Commands[smb2Command]) 3880 3881 def hookSmb2Command(self, smb2Command, callback): 3882 if self.__smb2Commands.has_key(smb2Command): 3883 originalCommand = self.__smb2Commands[smb2Command] 3884 else: 3885 originalCommand = None 3886 3887 self.__smb2Commands[smb2Command] = callback 3888 return originalCommand 3889 3890 def log(self, msg, level=logging.INFO): 3891 self.__log.log(level,msg) 3892 3893 def getServerName(self): 3894 return self.__serverName 3895 3896 def getServerOS(self): 3897 return self.__serverOS 3898 3899 def getServerDomain(self): 3900 return self.__serverDomain 3901 3902 def getSMBChallenge(self): 3903 return self.__challenge 3904 3905 def getServerConfig(self): 3906 return self.__serverConfig 3907 3908 def setServerConfig(self, config): 3909 self.__serverConfig = config 3910 3911 def getJTRdumpPath(self): 3912 return self.__jtr_dump_path 3913 3914 def verify_request(self, request, client_address): 3915 # TODO: Control here the max amount of processes we want to launch 3916 # returning False, closes the connection 3917 return True 3918 3919 def processRequest(self, connId, data): 3920 3921 # TODO: Process batched commands. 3922 isSMB2 = False 3923 SMBCommand = None 3924 try: 3925 packet = smb.NewSMBPacket(data = data) 3926 SMBCommand = smb.SMBCommand(packet['Data'][0]) 3927 except: 3928 # Maybe a SMB2 packet? 3929 packet = smb2.SMB2Packet(data = data) 3930 isSMB2 = True 3931 3932 # We might have compound requests 3933 compoundedPacketsResponse = [] 3934 compoundedPackets = [] 3935 try: 3936 # Search out list of implemented commands 3937 # We provide them with: 3938 # connId : representing the data for this specific connection 3939 # self : the SMBSERVER if they want to ask data to it 3940 # SMBCommand : the SMBCommand they are expecting to process 3941 # packet : the received packet itself, in case they need more data than the actual command 3942 # Only for Transactions 3943 # transCommand: a list of transaction subcommands 3944 # We expect to get: 3945 # respCommands: a list of answers for the commands processed 3946 # respPacket : if the commands chose to directly craft packet/s, we use this and not the previous 3947 # this MUST be a list 3948 # errorCode : self explanatory 3949 if isSMB2 is False: 3950 if packet['Command'] == smb.SMB.SMB_COM_TRANSACTION2: 3951 respCommands, respPackets, errorCode = self.__smbCommands[packet['Command']]( 3952 connId, 3953 self, 3954 SMBCommand, 3955 packet, 3956 self.__smbTrans2Commands) 3957 elif packet['Command'] == smb.SMB.SMB_COM_NT_TRANSACT: 3958 respCommands, respPackets, errorCode = self.__smbCommands[packet['Command']]( 3959 connId, 3960 self, 3961 SMBCommand, 3962 packet, 3963 self.__smbNTTransCommands) 3964 elif packet['Command'] == smb.SMB.SMB_COM_TRANSACTION: 3965 respCommands, respPackets, errorCode = self.__smbCommands[packet['Command']]( 3966 connId, 3967 self, 3968 SMBCommand, 3969 packet, 3970 self.__smbTransCommands) 3971 else: 3972 if self.__smbCommands.has_key(packet['Command']): 3973 if self.__SMB2Support is True: 3974 if packet['Command'] == smb.SMB.SMB_COM_NEGOTIATE: 3975 try: 3976 respCommands, respPackets, errorCode = self.__smb2Commands[smb2.SMB2_NEGOTIATE](connId, self, packet, True) 3977 isSMB2 = True 3978 except Exception, e: 3979 self.log('SMB2_NEGOTIATE: %s' % e, logging.ERROR) 3980 # If something went wrong, let's fallback to SMB1 3981 respCommands, respPackets, errorCode = self.__smbCommands[packet['Command']]( 3982 connId, 3983 self, 3984 SMBCommand, 3985 packet) 3986 #self.__SMB2Support = False 3987 pass 3988 else: 3989 respCommands, respPackets, errorCode = self.__smbCommands[packet['Command']]( 3990 connId, 3991 self, 3992 SMBCommand, 3993 packet) 3994 else: 3995 respCommands, respPackets, errorCode = self.__smbCommands[packet['Command']]( 3996 connId, 3997 self, 3998 SMBCommand, 3999 packet) 4000 else: 4001 respCommands, respPackets, errorCode = self.__smbCommands[255](connId, self, SMBCommand, packet) 4002 4003 compoundedPacketsResponse.append((respCommands, respPackets, errorCode)) 4004 compoundedPackets.append(packet) 4005 4006 else: 4007 done = False 4008 while not done: 4009 if self.__smb2Commands.has_key(packet['Command']): 4010 if self.__SMB2Support is True: 4011 respCommands, respPackets, errorCode = self.__smb2Commands[packet['Command']]( 4012 connId, 4013 self, 4014 packet) 4015 else: 4016 respCommands, respPackets, errorCode = self.__smb2Commands[255](connId, self, packet) 4017 else: 4018 respCommands, respPackets, errorCode = self.__smb2Commands[255](connId, self, packet) 4019 # Let's store the result for this compounded packet 4020 compoundedPacketsResponse.append((respCommands, respPackets, errorCode)) 4021 compoundedPackets.append(packet) 4022 if packet['NextCommand'] != 0: 4023 data = data[packet['NextCommand']:] 4024 packet = smb2.SMB2Packet(data = data) 4025 else: 4026 done = True 4027 4028 except Exception, e: 4029 #import traceback 4030 #traceback.print_exc() 4031 # Something wen't wrong, defaulting to Bad user ID 4032 self.log('processRequest (0x%x,%s)' % (packet['Command'],e), logging.ERROR) 4033 raise 4034 4035 # We prepare the response packet to commands don't need to bother about that. 4036 connData = self.getConnectionData(connId, False) 4037 4038 # Force reconnection loop.. This is just a test.. client will send me back credentials :) 4039 #connData['PacketNum'] += 1 4040 #if connData['PacketNum'] == 15: 4041 # connData['PacketNum'] = 0 4042 # # Something wen't wrong, defaulting to Bad user ID 4043 # self.log('Sending BAD USER ID!', logging.ERROR) 4044 # #raise 4045 # packet['Flags1'] |= smb.SMB.FLAGS1_REPLY 4046 # packet['Flags2'] = 0 4047 # errorCode = STATUS_SMB_BAD_UID 4048 # packet['ErrorCode'] = errorCode >> 16 4049 # packet['ErrorClass'] = errorCode & 0xff 4050 # return [packet] 4051 4052 self.setConnectionData(connId, connData) 4053 4054 packetsToSend = [] 4055 for packetNum in range(len(compoundedPacketsResponse)): 4056 respCommands, respPackets, errorCode = compoundedPacketsResponse[packetNum] 4057 packet = compoundedPackets[packetNum] 4058 if respPackets is None: 4059 for respCommand in respCommands: 4060 if isSMB2 is False: 4061 respPacket = smb.NewSMBPacket() 4062 respPacket['Flags1'] = smb.SMB.FLAGS1_REPLY 4063 4064 # TODO this should come from a per session configuration 4065 respPacket['Flags2'] = smb.SMB.FLAGS2_EXTENDED_SECURITY | smb.SMB.FLAGS2_NT_STATUS | smb.SMB.FLAGS2_LONG_NAMES | packet['Flags2'] & smb.SMB.FLAGS2_UNICODE 4066 #respPacket['Flags2'] = smb.SMB.FLAGS2_EXTENDED_SECURITY | smb.SMB.FLAGS2_NT_STATUS | smb.SMB.FLAGS2_LONG_NAMES 4067 #respPacket['Flags1'] = 0x98 4068 #respPacket['Flags2'] = 0xc807 4069 4070 4071 respPacket['Tid'] = packet['Tid'] 4072 respPacket['Mid'] = packet['Mid'] 4073 respPacket['Pid'] = packet['Pid'] 4074 respPacket['Uid'] = connData['Uid'] 4075 4076 respPacket['ErrorCode'] = errorCode >> 16 4077 respPacket['_reserved'] = errorCode >> 8 & 0xff 4078 respPacket['ErrorClass'] = errorCode & 0xff 4079 respPacket.addCommand(respCommand) 4080 4081 packetsToSend.append(respPacket) 4082 else: 4083 respPacket = smb2.SMB2Packet() 4084 respPacket['Flags'] = smb2.SMB2_FLAGS_SERVER_TO_REDIR 4085 if packetNum > 0: 4086 respPacket['Flags'] |= smb2.SMB2_FLAGS_RELATED_OPERATIONS 4087 respPacket['Status'] = errorCode 4088 respPacket['CreditRequestResponse'] = packet['CreditRequestResponse'] 4089 respPacket['Command'] = packet['Command'] 4090 respPacket['CreditCharge'] = packet['CreditCharge'] 4091 #respPacket['CreditCharge'] = 0 4092 respPacket['Reserved'] = packet['Reserved'] 4093 respPacket['SessionID'] = connData['Uid'] 4094 respPacket['MessageID'] = packet['MessageID'] 4095 respPacket['TreeID'] = packet['TreeID'] 4096 respPacket['Data'] = str(respCommand) 4097 packetsToSend.append(respPacket) 4098 else: 4099 # The SMBCommand took care of building the packet 4100 packetsToSend = respPackets 4101 4102 if isSMB2 is True: 4103 # Let's build a compound answer 4104 finalData = '' 4105 i = 0 4106 for i in range(len(packetsToSend)-1): 4107 packet = packetsToSend[i] 4108 # Align to 8-bytes 4109 padLen = (8 - (len(packet) % 8) ) % 8 4110 packet['NextCommand'] = len(packet) + padLen 4111 finalData += str(packet) + padLen*'\x00' 4112 4113 # Last one 4114 finalData += str(packetsToSend[len(packetsToSend)-1]) 4115 packetsToSend = [finalData] 4116 4117 # We clear the compound requests 4118 connData['LastRequest'] = {} 4119 4120 return packetsToSend 4121 4122 def processConfigFile(self, configFile = None): 4123 # TODO: Do a real config parser 4124 if self.__serverConfig is None: 4125 if configFile is None: 4126 configFile = 'smb.conf' 4127 self.__serverConfig = ConfigParser.ConfigParser() 4128 self.__serverConfig.read(configFile) 4129 4130 self.__serverName = self.__serverConfig.get('global','server_name') 4131 self.__serverOS = self.__serverConfig.get('global','server_os') 4132 self.__serverDomain = self.__serverConfig.get('global','server_domain') 4133 self.__logFile = self.__serverConfig.get('global','log_file') 4134 if self.__serverConfig.has_option('global', 'challenge'): 4135 self.__challenge = self.__serverConfig.get('global', 'challenge') 4136 else: 4137 self.__challenge = 'A'*8 4138 4139 if self.__serverConfig.has_option("global", "jtr_dump_path"): 4140 self.__jtr_dump_path = self.__serverConfig.get("global", "jtr_dump_path") 4141 4142 if self.__serverConfig.has_option("global", "SMB2Support"): 4143 self.__SMB2Support = self.__serverConfig.getboolean("global","SMB2Support") 4144 else: 4145 self.__SMB2Support = False 4146 4147 if self.__logFile != 'None': 4148 logging.basicConfig(filename = self.__logFile, 4149 level = logging.DEBUG, 4150 format="%(asctime)s: %(levelname)s: %(message)s", 4151 datefmt = '%m/%d/%Y %I:%M:%S %p') 4152 self.__log = LOG 4153 4154 # Process the credentials 4155 credentials_fname = self.__serverConfig.get('global','credentials_file') 4156 if credentials_fname is not "": 4157 cred = open(credentials_fname) 4158 line = cred.readline() 4159 while line: 4160 name, domain, lmhash, nthash = line.split(':') 4161 self.__credentials[name] = (domain, lmhash, nthash.strip('\r\n')) 4162 line = cred.readline() 4163 cred.close() 4164 self.log('Config file parsed') 4165 4166 # For windows platforms, opening a directory is not an option, so we set a void FD 4167 VOID_FILE_DESCRIPTOR = -1 4168 PIPE_FILE_DESCRIPTOR = -2 4169