# Copyright (c) 2003-2016 CORE Security Technologies # # This software is provided under under a slightly modified version # of the Apache Software License. See the accompanying LICENSE file # for more information. # # Author: Alberto Solino (@agsolino) # # TODO: # [-] Functions should return NT error codes # [-] Handling errors in all situations, right now it's just raising exceptions. # [*] Standard authentication support # [ ] Organize the connectionData stuff # [*] Add capability to send a bad user ID if the user is not authenticated, # right now you can ask for any command without actually being authenticated # [ ] PATH TRAVERSALS EVERYWHERE.. BE WARNED! # [ ] Check the credentials.. now we're just letting everybody to log in. # [ ] Check error situation (now many places assume the right data is coming) # [ ] Implement IPC to the main process so the connectionData is on a single place # [ ] Hence.. implement locking # estamos en la B from __future__ import with_statement import calendar import socket import time import datetime import struct import ConfigParser import SocketServer import threading import logging import logging.config import ntpath import os import fnmatch import errno import sys import random import shutil from binascii import hexlify # For signing from impacket import smb, nmb, ntlm, uuid, LOG from impacket import smb3structs as smb2 from impacket.spnego import SPNEGO_NegTokenInit, TypesMech, MechTypes, SPNEGO_NegTokenResp, ASN1_AID, ASN1_SUPPORTED_MECH from impacket.nt_errors import STATUS_NO_MORE_FILES, STATUS_NETWORK_NAME_DELETED, STATUS_INVALID_PARAMETER, \ STATUS_FILE_CLOSED, STATUS_MORE_PROCESSING_REQUIRED, STATUS_OBJECT_PATH_NOT_FOUND, STATUS_DIRECTORY_NOT_EMPTY, \ STATUS_FILE_IS_A_DIRECTORY, STATUS_NOT_IMPLEMENTED, STATUS_INVALID_HANDLE, STATUS_OBJECT_NAME_COLLISION, \ STATUS_NO_SUCH_FILE, STATUS_CANCELLED, STATUS_OBJECT_NAME_NOT_FOUND, STATUS_SUCCESS, STATUS_ACCESS_DENIED, \ STATUS_NOT_SUPPORTED, STATUS_INVALID_DEVICE_REQUEST, STATUS_FS_DRIVER_REQUIRED, STATUS_INVALID_INFO_CLASS # These ones not defined in nt_errors STATUS_SMB_BAD_UID = 0x005B0002 STATUS_SMB_BAD_TID = 0x00050002 # Utility functions # and general functions. # There are some common functions that can be accessed from more than one SMB # command (or either TRANSACTION). That's why I'm putting them here # TODO: Return NT ERROR Codes def outputToJohnFormat(challenge, username, domain, lmresponse, ntresponse): # We don't want to add a possible failure here, since this is an # extra bonus. We try, if it fails, returns nothing ret_value = '' try: if len(ntresponse) > 24: # Extended Security - NTLMv2 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'} else: # NTLMv1 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'} except: # Let's try w/o decoding Unicode try: if len(ntresponse) > 24: # Extended Security - NTLMv2 ret_value = {'hash_string':'%s::%s:%s:%s:%s' % (username, domain, hexlify(challenge), hexlify(ntresponse)[:32], hexlify(ntresponse)[32:]), 'hash_version':'ntlmv2'} else: # NTLMv1 ret_value = {'hash_string':'%s::%s:%s:%s:%s' % (username, domain, hexlify(lmresponse), hexlify(ntresponse), hexlify(challenge)), 'hash_version':'ntlm'} except Exception, e: LOG.error("outputToJohnFormat: %s" % e) pass return ret_value def writeJohnOutputToFile(hash_string, hash_version, file_name): fn_data = os.path.splitext(file_name) if hash_version == "ntlmv2": output_filename = fn_data[0] + "_ntlmv2" + fn_data[1] else: output_filename = fn_data[0] + "_ntlm" + fn_data[1] with open(output_filename,"a") as f: f.write(hash_string) f.write('\n') def decodeSMBString( flags, text ): if flags & smb.SMB.FLAGS2_UNICODE: return text.decode('utf-16le') else: return text def encodeSMBString( flags, text ): if flags & smb.SMB.FLAGS2_UNICODE: return (text).encode('utf-16le') else: return text def getFileTime(t): t *= 10000000 t += 116444736000000000 return t def getUnixTime(t): t -= 116444736000000000 t /= 10000000 return t def getSMBDate(t): # TODO: Fix this :P d = datetime.date.fromtimestamp(t) year = d.year - 1980 ret = (year << 8) + (d.month << 4) + d.day return ret def getSMBTime(t): # TODO: Fix this :P d = datetime.datetime.fromtimestamp(t) return (d.hour << 8) + (d.minute << 4) + d.second def getShares(connId, smbServer): config = smbServer.getServerConfig() sections = config.sections() # Remove the global one del(sections[sections.index('global')]) shares = {} for i in sections: shares[i] = dict(config.items(i)) return shares def searchShare(connId, share, smbServer): config = smbServer.getServerConfig() if config.has_section(share): return dict(config.items(share)) else: return None def openFile(path,fileName, accessMode, fileAttributes, openMode): fileName = os.path.normpath(fileName.replace('\\','/')) errorCode = 0 if len(fileName) > 0 and (fileName[0] == '/' or fileName[0] == '\\'): # strip leading '/' fileName = fileName[1:] pathName = os.path.join(path,fileName) mode = 0 # Check the Open Mode if openMode & 0x10: # If the file does not exist, create it. mode = os.O_CREAT else: # If file does not exist, return an error if os.path.exists(pathName) is not True: errorCode = STATUS_NO_SUCH_FILE return 0,mode, pathName, errorCode if os.path.isdir(pathName) and (fileAttributes & smb.ATTR_DIRECTORY) == 0: # Request to open a normal file and this is actually a directory errorCode = STATUS_FILE_IS_A_DIRECTORY return 0, mode, pathName, errorCode # Check the Access Mode if accessMode & 0x7 == 1: mode |= os.O_WRONLY elif accessMode & 0x7 == 2: mode |= os.O_RDWR else: mode = os.O_RDONLY try: if sys.platform == 'win32': mode |= os.O_BINARY fid = os.open(pathName, mode) except Exception, e: LOG.error("openFile: %s,%s" % (pathName, mode) ,e) fid = 0 errorCode = STATUS_ACCESS_DENIED return fid, mode, pathName, errorCode def queryFsInformation(path, filename, level=0): if isinstance(filename,unicode): encoding = 'utf-16le' flags = smb.SMB.FLAGS2_UNICODE else: encoding = 'ascii' flags = 0 fileName = os.path.normpath(filename.replace('\\','/')) if len(fileName) > 0 and (fileName[0] == '/' or fileName[0] == '\\'): # strip leading '/' fileName = fileName[1:] pathName = os.path.join(path,fileName) fileSize = os.path.getsize(pathName) (mode, ino, dev, nlink, uid, gid, size, atime, mtime, ctime) = os.stat(pathName) if level == smb.SMB_QUERY_FS_ATTRIBUTE_INFO or level == smb2.SMB2_FILESYSTEM_ATTRIBUTE_INFO: data = smb.SMBQueryFsAttributeInfo() data['FileSystemAttributes'] = smb.FILE_CASE_SENSITIVE_SEARCH | smb.FILE_CASE_PRESERVED_NAMES data['MaxFilenNameLengthInBytes'] = 255 data['LengthOfFileSystemName'] = len('XTFS')*2 data['FileSystemName'] = 'XTFS'.encode('utf-16le') return data.getData() elif level == smb.SMB_INFO_VOLUME: data = smb.SMBQueryFsInfoVolume( flags = flags ) data['VolumeLabel'] = 'SHARE'.encode(encoding) return data.getData() elif level == smb.SMB_QUERY_FS_VOLUME_INFO or level == smb2.SMB2_FILESYSTEM_VOLUME_INFO: data = smb.SMBQueryFsVolumeInfo() data['VolumeLabel'] = '' data['VolumeCreationTime'] = getFileTime(ctime) return data.getData() elif level == smb.SMB_QUERY_FS_SIZE_INFO: data = smb.SMBQueryFsSizeInfo() return data.getData() elif level == smb.FILE_FS_FULL_SIZE_INFORMATION: data = smb.SMBFileFsFullSizeInformation() return data.getData() elif level == smb.FILE_FS_SIZE_INFORMATION: data = smb.FileFsSizeInformation() return data.getData() else: lastWriteTime = mtime attribs = 0 if os.path.isdir(pathName): attribs |= smb.SMB_FILE_ATTRIBUTE_DIRECTORY if os.path.isfile(pathName): attribs |= smb.SMB_FILE_ATTRIBUTE_NORMAL fileAttributes = attribs return fileSize, lastWriteTime, fileAttributes def findFirst2(path, fileName, level, searchAttributes, isSMB2 = False): # TODO: Depending on the level, this could be done much simpler #print "FindFirs2 path:%s, filename:%s" % (path, fileName) fileName = os.path.normpath(fileName.replace('\\','/')) # Let's choose the right encoding depending on the request if isinstance(fileName,unicode): encoding = 'utf-16le' flags = smb.SMB.FLAGS2_UNICODE else: encoding = 'ascii' flags = 0 if len(fileName) > 0 and (fileName[0] == '/' or fileName[0] == '\\'): # strip leading '/' fileName = fileName[1:] pathName = os.path.join(path,fileName) files = [] if pathName.find('*') == -1 and pathName.find('?') == -1: # No search patterns pattern = '' else: pattern = os.path.basename(pathName) dirName = os.path.dirname(pathName) # Always add . and .. Not that important for Windows, but Samba whines if # not present (for * search only) if pattern == '*': files.append(os.path.join(dirName,'.')) files.append(os.path.join(dirName,'..')) if pattern != '': for file in os.listdir(dirName): if fnmatch.fnmatch(file.lower(),pattern.lower()): entry = os.path.join(dirName, file) if os.path.isdir(entry): if searchAttributes & smb.ATTR_DIRECTORY: files.append(entry) else: files.append(entry) else: if os.path.exists(pathName): files.append(pathName) searchResult = [] searchCount = len(files) errorCode = STATUS_SUCCESS for i in files: if level == smb.SMB_FIND_FILE_BOTH_DIRECTORY_INFO or level == smb2.SMB2_FILE_BOTH_DIRECTORY_INFO: item = smb.SMBFindFileBothDirectoryInfo( flags = flags ) elif level == smb.SMB_FIND_FILE_DIRECTORY_INFO or level == smb2.SMB2_FILE_DIRECTORY_INFO: item = smb.SMBFindFileDirectoryInfo( flags = flags ) elif level == smb.SMB_FIND_FILE_FULL_DIRECTORY_INFO or level == smb2.SMB2_FULL_DIRECTORY_INFO: item = smb.SMBFindFileFullDirectoryInfo( flags = flags ) elif level == smb.SMB_FIND_INFO_STANDARD: item = smb.SMBFindInfoStandard( flags = flags ) elif level == smb.SMB_FIND_FILE_ID_FULL_DIRECTORY_INFO or level == smb2.SMB2_FILE_ID_FULL_DIRECTORY_INFO: item = smb.SMBFindFileIdFullDirectoryInfo( flags = flags ) elif level == smb.SMB_FIND_FILE_ID_BOTH_DIRECTORY_INFO or level == smb2.SMB2_FILE_ID_BOTH_DIRECTORY_INFO: item = smb.SMBFindFileIdBothDirectoryInfo( flags = flags ) elif level == smb.SMB_FIND_FILE_NAMES_INFO or level == smb2.SMB2_FILE_NAMES_INFO: item = smb.SMBFindFileNamesInfo( flags = flags ) else: LOG.error("Wrong level %d!" % level) return searchResult, searchCount, STATUS_NOT_SUPPORTED (mode, ino, dev, nlink, uid, gid, size, atime, mtime, ctime) = os.stat(i) if os.path.isdir(i): item['ExtFileAttributes'] = smb.ATTR_DIRECTORY else: item['ExtFileAttributes'] = smb.ATTR_NORMAL | smb.ATTR_ARCHIVE item['FileName'] = os.path.basename(i).encode(encoding) 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: item['EaSize'] = 0 item['EndOfFile'] = size item['AllocationSize'] = size item['CreationTime'] = getFileTime(ctime) item['LastAccessTime'] = getFileTime(atime) item['LastWriteTime'] = getFileTime(mtime) item['LastChangeTime'] = getFileTime(mtime) item['ShortName'] = '\x00'*24 item['FileName'] = os.path.basename(i).encode(encoding) padLen = (8-(len(item) % 8)) % 8 item['NextEntryOffset'] = len(item) + padLen elif level == smb.SMB_FIND_FILE_DIRECTORY_INFO: item['EndOfFile'] = size item['AllocationSize'] = size item['CreationTime'] = getFileTime(ctime) item['LastAccessTime'] = getFileTime(atime) item['LastWriteTime'] = getFileTime(mtime) item['LastChangeTime'] = getFileTime(mtime) item['FileName'] = os.path.basename(i).encode(encoding) padLen = (8-(len(item) % 8)) % 8 item['NextEntryOffset'] = len(item) + padLen 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: item['EaSize'] = 0 item['EndOfFile'] = size item['AllocationSize'] = size item['CreationTime'] = getFileTime(ctime) item['LastAccessTime'] = getFileTime(atime) item['LastWriteTime'] = getFileTime(mtime) item['LastChangeTime'] = getFileTime(mtime) padLen = (8-(len(item) % 8)) % 8 item['NextEntryOffset'] = len(item) + padLen elif level == smb.SMB_FIND_INFO_STANDARD: item['EaSize'] = size item['CreationDate'] = getSMBDate(ctime) item['CreationTime'] = getSMBTime(ctime) item['LastAccessDate'] = getSMBDate(atime) item['LastAccessTime'] = getSMBTime(atime) item['LastWriteDate'] = getSMBDate(mtime) item['LastWriteTime'] = getSMBTime(mtime) searchResult.append(item) # No more files if (level >= smb.SMB_FIND_FILE_DIRECTORY_INFO or isSMB2 == True) and searchCount > 0: searchResult[-1]['NextEntryOffset'] = 0 return searchResult, searchCount, errorCode def queryFileInformation(path, filename, level): #print "queryFileInfo path: %s, filename: %s, level:0x%x" % (path,filename,level) return queryPathInformation(path,filename, level) def queryPathInformation(path, filename, level): # TODO: Depending on the level, this could be done much simpler #print "queryPathInfo path: %s, filename: %s, level:0x%x" % (path,filename,level) try: errorCode = 0 fileName = os.path.normpath(filename.replace('\\','/')) if len(fileName) > 0 and (fileName[0] == '/' or fileName[0] == '\\') and path != '': # strip leading '/' fileName = fileName[1:] pathName = os.path.join(path,fileName) if os.path.exists(pathName): (mode, ino, dev, nlink, uid, gid, size, atime, mtime, ctime) = os.stat(pathName) if level == smb.SMB_QUERY_FILE_BASIC_INFO: infoRecord = smb.SMBQueryFileBasicInfo() infoRecord['CreationTime'] = getFileTime(ctime) infoRecord['LastAccessTime'] = getFileTime(atime) infoRecord['LastWriteTime'] = getFileTime(mtime) infoRecord['LastChangeTime'] = getFileTime(mtime) if os.path.isdir(pathName): infoRecord['ExtFileAttributes'] = smb.ATTR_DIRECTORY else: infoRecord['ExtFileAttributes'] = smb.ATTR_NORMAL | smb.ATTR_ARCHIVE elif level == smb.SMB_QUERY_FILE_STANDARD_INFO: infoRecord = smb.SMBQueryFileStandardInfo() infoRecord['AllocationSize'] = size infoRecord['EndOfFile'] = size if os.path.isdir(pathName): infoRecord['Directory'] = 1 else: infoRecord['Directory'] = 0 elif level == smb.SMB_QUERY_FILE_ALL_INFO or level == smb2.SMB2_FILE_ALL_INFO: infoRecord = smb.SMBQueryFileAllInfo() infoRecord['CreationTime'] = getFileTime(ctime) infoRecord['LastAccessTime'] = getFileTime(atime) infoRecord['LastWriteTime'] = getFileTime(mtime) infoRecord['LastChangeTime'] = getFileTime(mtime) if os.path.isdir(pathName): infoRecord['ExtFileAttributes'] = smb.ATTR_DIRECTORY else: infoRecord['ExtFileAttributes'] = smb.ATTR_NORMAL | smb.ATTR_ARCHIVE infoRecord['AllocationSize'] = size infoRecord['EndOfFile'] = size if os.path.isdir(pathName): infoRecord['Directory'] = 1 else: infoRecord['Directory'] = 0 infoRecord['FileName'] = filename.encode('utf-16le') elif level == smb2.SMB2_FILE_NETWORK_OPEN_INFO: infoRecord = smb.SMBFileNetworkOpenInfo() infoRecord['CreationTime'] = getFileTime(ctime) infoRecord['LastAccessTime'] = getFileTime(atime) infoRecord['LastWriteTime'] = getFileTime(mtime) infoRecord['ChangeTime'] = getFileTime(mtime) infoRecord['AllocationSize'] = size infoRecord['EndOfFile'] = size if os.path.isdir(pathName): infoRecord['FileAttributes'] = smb.ATTR_DIRECTORY else: infoRecord['FileAttributes'] = smb.ATTR_NORMAL | smb.ATTR_ARCHIVE elif level == smb.SMB_QUERY_FILE_EA_INFO or level == smb2.SMB2_FILE_EA_INFO: infoRecord = smb.SMBQueryFileEaInfo() elif level == smb2.SMB2_FILE_STREAM_INFO: infoRecord = smb.SMBFileStreamInformation() else: LOG.error('Unknown level for query path info! 0x%x' % level) # UNSUPPORTED return None, STATUS_NOT_SUPPORTED return infoRecord, errorCode else: # NOT FOUND return None, STATUS_OBJECT_NAME_NOT_FOUND except Exception, e: LOG.error('queryPathInfo: %s' % e) raise def queryDiskInformation(path): # TODO: Do something useful here :) # For now we just return fake values totalUnits = 65535 freeUnits = 65535 return totalUnits, freeUnits # Here we implement the NT transaction handlers class NTTRANSCommands: def default(self, connId, smbServer, recvPacket, parameters, data, maxDataCount = 0): pass # Here we implement the NT transaction handlers class TRANSCommands: @staticmethod def lanMan(connId, smbServer, recvPacket, parameters, data, maxDataCount = 0): # Minimal [MS-RAP] implementation, just to return the shares connData = smbServer.getConnectionData(connId) respSetup = '' respParameters = '' respData = '' errorCode = STATUS_SUCCESS if struct.unpack(' 0 and (fileName[0] == '/' or fileName[0] == '\\') and path != '': # strip leading '/' fileName = fileName[1:] pathName = os.path.join(path,fileName) if os.path.exists(pathName): informationLevel = setPathInfoParameters['InformationLevel'] if informationLevel == smb.SMB_SET_FILE_BASIC_INFO: infoRecord = smb.SMBSetFileBasicInfo(data) # Creation time won't be set, the other ones we play with. atime = infoRecord['LastAccessTime'] if atime == 0: atime = -1 else: atime = getUnixTime(atime) mtime = infoRecord['LastWriteTime'] if mtime == 0: mtime = -1 else: mtime = getUnixTime(mtime) if mtime != -1 or atime != -1: os.utime(pathName,(atime,mtime)) else: smbServer.log('Unknown level for set path info! 0x%x' % setPathInfoParameters['InformationLevel'], logging.ERROR) # UNSUPPORTED errorCode = STATUS_NOT_SUPPORTED else: errorCode = STATUS_OBJECT_NAME_NOT_FOUND if errorCode == STATUS_SUCCESS: respParameters = smb.SMBSetPathInformationResponse_Parameters() else: errorCode = STATUS_SMB_BAD_TID smbServer.setConnectionData(connId, connData) return respSetup, respParameters, respData, errorCode @staticmethod def setFileInformation(connId, smbServer, recvPacket, parameters, data, maxDataCount = 0): connData = smbServer.getConnectionData(connId) respSetup = '' respParameters = '' respData = '' errorCode = STATUS_SUCCESS setFileInfoParameters = smb.SMBSetFileInformation_Parameters(parameters) if connData['ConnectedShares'].has_key(recvPacket['Tid']): if connData['OpenedFiles'].has_key(setFileInfoParameters['FID']): fileName = connData['OpenedFiles'][setFileInfoParameters['FID']]['FileName'] informationLevel = setFileInfoParameters['InformationLevel'] if informationLevel == smb.SMB_SET_FILE_DISPOSITION_INFO: infoRecord = smb.SMBSetFileDispositionInfo(parameters) if infoRecord['DeletePending'] > 0: # Mark this file for removal after closed connData['OpenedFiles'][setFileInfoParameters['FID']]['DeleteOnClose'] = True respParameters = smb.SMBSetFileInformationResponse_Parameters() elif informationLevel == smb.SMB_SET_FILE_BASIC_INFO: infoRecord = smb.SMBSetFileBasicInfo(data) # Creation time won't be set, the other ones we play with. atime = infoRecord['LastAccessTime'] if atime == 0: atime = -1 else: atime = getUnixTime(atime) mtime = infoRecord['LastWriteTime'] if mtime == 0: mtime = -1 else: mtime = getUnixTime(mtime) os.utime(fileName,(atime,mtime)) elif informationLevel == smb.SMB_SET_FILE_END_OF_FILE_INFO: fileHandle = connData['OpenedFiles'][setFileInfoParameters['FID']]['FileHandle'] infoRecord = smb.SMBSetFileEndOfFileInfo(data) if infoRecord['EndOfFile'] > 0: os.lseek(fileHandle, infoRecord['EndOfFile']-1, 0) os.write(fileHandle, '\x00') else: smbServer.log('Unknown level for set file info! 0x%x' % setFileInfoParameters['InformationLevel'], logging.ERROR) # UNSUPPORTED errorCode = STATUS_NOT_SUPPORTED else: errorCode = STATUS_NO_SUCH_FILE if errorCode == STATUS_SUCCESS: respParameters = smb.SMBSetFileInformationResponse_Parameters() else: errorCode = STATUS_SMB_BAD_TID smbServer.setConnectionData(connId, connData) return respSetup, respParameters, respData, errorCode @staticmethod def queryFileInformation(connId, smbServer, recvPacket, parameters, data, maxDataCount = 0): connData = smbServer.getConnectionData(connId) respSetup = '' respParameters = '' respData = '' queryFileInfoParameters = smb.SMBQueryFileInformation_Parameters(parameters) if connData['ConnectedShares'].has_key(recvPacket['Tid']): if connData['OpenedFiles'].has_key(queryFileInfoParameters['FID']): fileName = connData['OpenedFiles'][queryFileInfoParameters['FID']]['FileName'] infoRecord, errorCode = queryFileInformation('', fileName, queryFileInfoParameters['InformationLevel']) if infoRecord is not None: respParameters = smb.SMBQueryFileInformationResponse_Parameters() respData = infoRecord else: errorCode = STATUS_INVALID_HANDLE else: errorCode = STATUS_SMB_BAD_TID smbServer.setConnectionData(connId, connData) return respSetup, respParameters, respData, errorCode @staticmethod def queryPathInformation(connId, smbServer, recvPacket, parameters, data, maxDataCount = 0): connData = smbServer.getConnectionData(connId) respSetup = '' respParameters = '' respData = '' errorCode = 0 queryPathInfoParameters = smb.SMBQueryPathInformation_Parameters(flags = recvPacket['Flags2'], data = parameters) if connData['ConnectedShares'].has_key(recvPacket['Tid']): path = connData['ConnectedShares'][recvPacket['Tid']]['path'] try: infoRecord, errorCode = queryPathInformation(path, decodeSMBString(recvPacket['Flags2'], queryPathInfoParameters['FileName']), queryPathInfoParameters['InformationLevel']) except Exception, e: smbServer.log("queryPathInformation: %s" % e,logging.ERROR) if infoRecord is not None: respParameters = smb.SMBQueryPathInformationResponse_Parameters() respData = infoRecord else: errorCode = STATUS_SMB_BAD_TID smbServer.setConnectionData(connId, connData) return respSetup, respParameters, respData, errorCode @staticmethod def queryFsInformation(connId, smbServer, recvPacket, parameters, data, maxDataCount = 0): connData = smbServer.getConnectionData(connId) errorCode = 0 # Get the Tid associated if connData['ConnectedShares'].has_key(recvPacket['Tid']): data = queryFsInformation(connData['ConnectedShares'][recvPacket['Tid']]['path'], '', struct.unpack('= maxDataCount or (i[0]+1) >= findNext2Parameters['SearchCount']: # We gotta stop here and continue on a find_next2 endOfSearch = 0 connData['SIDs'][sid] = searchResult[i[0]:] respParameters['LastNameOffset'] = totalData break else: searchCount +=1 respData += data totalData += lenData # Have we reached the end of the search or still stuff to send? if endOfSearch > 0: # Let's remove the SID from our ConnData del(connData['SIDs'][sid]) respParameters['EndOfSearch'] = endOfSearch respParameters['SearchCount'] = searchCount else: errorCode = STATUS_INVALID_HANDLE else: errorCode = STATUS_SMB_BAD_TID smbServer.setConnectionData(connId, connData) return respSetup, respParameters, respData, errorCode @staticmethod def findFirst2(connId, smbServer, recvPacket, parameters, data, maxDataCount): connData = smbServer.getConnectionData(connId) respSetup = '' respParameters = '' respData = '' findFirst2Parameters = smb.SMBFindFirst2_Parameters( recvPacket['Flags2'], data = parameters) if connData['ConnectedShares'].has_key(recvPacket['Tid']): path = connData['ConnectedShares'][recvPacket['Tid']]['path'] searchResult, searchCount, errorCode = findFirst2(path, decodeSMBString( recvPacket['Flags2'], findFirst2Parameters['FileName'] ), findFirst2Parameters['InformationLevel'], findFirst2Parameters['SearchAttributes'] ) respParameters = smb.SMBFindFirst2Response_Parameters() endOfSearch = 1 sid = 0x80 # default SID searchCount = 0 totalData = 0 for i in enumerate(searchResult): #i[1].dump() data = i[1].getData() lenData = len(data) if (totalData+lenData) >= maxDataCount or (i[0]+1) > findFirst2Parameters['SearchCount']: # We gotta stop here and continue on a find_next2 endOfSearch = 0 # Simple way to generate a fid if len(connData['SIDs']) == 0: sid = 1 else: sid = connData['SIDs'].keys()[-1] + 1 # Store the remaining search results in the ConnData SID connData['SIDs'][sid] = searchResult[i[0]:] respParameters['LastNameOffset'] = totalData break else: searchCount +=1 respData += data padLen = (8-(lenData % 8)) %8 respData += '\xaa'*padLen totalData += lenData + padLen respParameters['SID'] = sid respParameters['EndOfSearch'] = endOfSearch respParameters['SearchCount'] = searchCount else: errorCode = STATUS_SMB_BAD_TID smbServer.setConnectionData(connId, connData) return respSetup, respParameters, respData, errorCode # Here we implement the commands handlers class SMBCommands: @staticmethod def smbTransaction(connId, smbServer, SMBCommand, recvPacket, transCommands): connData = smbServer.getConnectionData(connId) respSMBCommand = smb.SMBCommand(recvPacket['Command']) transParameters= smb.SMBTransaction_Parameters(SMBCommand['Parameters']) # Do the stuff if transParameters['ParameterCount'] != transParameters['TotalParameterCount']: # TODO: Handle partial parameters raise Exception("Unsupported partial parameters in TRANSACT2!") else: transData = smb.SMBTransaction_SData(flags = recvPacket['Flags2']) # Standard says servers shouldn't trust Parameters and Data comes # in order, so we have to parse the offsets, ugly paramCount = transParameters['ParameterCount'] transData['Trans_ParametersLength'] = paramCount dataCount = transParameters['DataCount'] transData['Trans_DataLength'] = dataCount transData.fromString(SMBCommand['Data']) if transParameters['ParameterOffset'] > 0: paramOffset = transParameters['ParameterOffset'] - 63 - transParameters['SetupLength'] transData['Trans_Parameters'] = SMBCommand['Data'][paramOffset:paramOffset+paramCount] else: transData['Trans_Parameters'] = '' if transParameters['DataOffset'] > 0: dataOffset = transParameters['DataOffset'] - 63 - transParameters['SetupLength'] transData['Trans_Data'] = SMBCommand['Data'][dataOffset:dataOffset + dataCount] else: transData['Trans_Data'] = '' # Call the handler for this TRANSACTION if transParameters['SetupCount'] == 0: # No subcommand, let's play with the Name command = decodeSMBString(recvPacket['Flags2'],transData['Name']) else: command = struct.unpack(' 0 or remainingParameters > 0: respSMBCommand = smb.SMBCommand(recvPacket['Command']) respParameters = smb.SMBTransactionResponse_Parameters() respData = smb.SMBTransaction2Response_Data() respParameters['TotalParameterCount'] = len(parameters) respParameters['ParameterCount'] = len(parameters) respData['Trans_ParametersLength'] = len(parameters) respParameters['TotalDataCount'] = len(data) respParameters['DataDisplacement'] = dataDisplacement # TODO: Do the same for parameters if len(data) > transParameters['MaxDataCount']: # Answer doesn't fit in this packet LOG.debug("Lowering answer from %d to %d" % (len(data),transParameters['MaxDataCount']) ) respParameters['DataCount'] = transParameters['MaxDataCount'] else: respParameters['DataCount'] = len(data) respData['Trans_DataLength'] = respParameters['DataCount'] respParameters['SetupCount'] = len(setup) respParameters['Setup'] = setup # TODO: Make sure we're calculating the pad right if len(parameters) > 0: #padLen = 4 - (55 + len(setup)) % 4 padLen = (4 - (55 + len(setup)) % 4 ) % 4 padBytes = '\xFF' * padLen respData['Pad1'] = padBytes respParameters['ParameterOffset'] = 55 + len(setup) + padLen else: padLen = 0 respParameters['ParameterOffset'] = 0 respData['Pad1'] = '' if len(data) > 0: #pad2Len = 4 - (55 + len(setup) + padLen + len(parameters)) % 4 pad2Len = (4 - (55 + len(setup) + padLen + len(parameters)) % 4) % 4 respData['Pad2'] = '\xFF' * pad2Len respParameters['DataOffset'] = 55 + len(setup) + padLen + len(parameters) + pad2Len else: respParameters['DataOffset'] = 0 respData['Pad2'] = '' respData['Trans_Parameters'] = parameters[:respParameters['ParameterCount']] respData['Trans_Data'] = data[:respParameters['DataCount']] respSMBCommand['Parameters'] = respParameters respSMBCommand['Data'] = respData data = data[respParameters['DataCount']:] remainingData -= respParameters['DataCount'] dataDisplacement += respParameters['DataCount'] + 1 parameters = parameters[respParameters['ParameterCount']:] remainingParameters -= respParameters['ParameterCount'] commands.append(respSMBCommand) smbServer.setConnectionData(connId, connData) return commands, None, errorCode else: smbServer.log("Unsupported Transact command %r" % command, logging.ERROR) respParameters = '' respData = '' errorCode = STATUS_NOT_IMPLEMENTED respSMBCommand['Parameters'] = respParameters respSMBCommand['Data'] = respData smbServer.setConnectionData(connId, connData) return [respSMBCommand], None, errorCode @staticmethod def smbNTTransact(connId, smbServer, SMBCommand, recvPacket, transCommands): connData = smbServer.getConnectionData(connId) respSMBCommand = smb.SMBCommand(recvPacket['Command']) NTTransParameters= smb.SMBNTTransaction_Parameters(SMBCommand['Parameters']) # Do the stuff if NTTransParameters['ParameterCount'] != NTTransParameters['TotalParameterCount']: # TODO: Handle partial parameters raise Exception("Unsupported partial parameters in NTTrans!") else: NTTransData = smb.SMBNTTransaction_Data() # Standard says servers shouldn't trust Parameters and Data comes # in order, so we have to parse the offsets, ugly paramCount = NTTransParameters['ParameterCount'] NTTransData['NT_Trans_ParametersLength'] = paramCount dataCount = NTTransParameters['DataCount'] NTTransData['NT_Trans_DataLength'] = dataCount if NTTransParameters['ParameterOffset'] > 0: paramOffset = NTTransParameters['ParameterOffset'] - 73 - NTTransParameters['SetupLength'] NTTransData['NT_Trans_Parameters'] = SMBCommand['Data'][paramOffset:paramOffset+paramCount] else: NTTransData['NT_Trans_Parameters'] = '' if NTTransParameters['DataOffset'] > 0: dataOffset = NTTransParameters['DataOffset'] - 73 - NTTransParameters['SetupLength'] NTTransData['NT_Trans_Data'] = SMBCommand['Data'][dataOffset:dataOffset + dataCount] else: NTTransData['NT_Trans_Data'] = '' # Call the handler for this TRANSACTION command = NTTransParameters['Function'] if transCommands.has_key(command): # Call the NT TRANS subcommand setup = '' parameters = '' data = '' try: setup, parameters, data, errorCode = transCommands[command](connId, smbServer, recvPacket, NTTransData['NT_Trans_Parameters'], NTTransData['NT_Trans_Data'], NTTransParameters['MaxDataCount']) except Exception, e: smbServer.log('NTTransaction: (0x%x,%s)' % (command, e), logging.ERROR) errorCode = STATUS_ACCESS_DENIED #raise if setup == '' and parameters == '' and data == '': # Something wen't wrong respParameters = '' respData = '' if errorCode == STATUS_SUCCESS: errorCode = STATUS_ACCESS_DENIED else: # Build the answer data = str(data) remainingData = len(data) parameters = str(parameters) remainingParameters = len(parameters) commands = [] dataDisplacement = 0 while remainingData > 0 or remainingParameters > 0: respSMBCommand = smb.SMBCommand(recvPacket['Command']) respParameters = smb.SMBNTTransactionResponse_Parameters() respData = smb.SMBNTTransactionResponse_Data() respParameters['TotalParameterCount'] = len(parameters) respParameters['ParameterCount'] = len(parameters) respData['Trans_ParametersLength'] = len(parameters) respParameters['TotalDataCount'] = len(data) respParameters['DataDisplacement'] = dataDisplacement # TODO: Do the same for parameters if len(data) > NTTransParameters['MaxDataCount']: # Answer doesn't fit in this packet LOG.debug("Lowering answer from %d to %d" % (len(data),NTTransParameters['MaxDataCount']) ) respParameters['DataCount'] = NTTransParameters['MaxDataCount'] else: respParameters['DataCount'] = len(data) respData['NT_Trans_DataLength'] = respParameters['DataCount'] respParameters['SetupCount'] = len(setup) respParameters['Setup'] = setup # TODO: Make sure we're calculating the pad right if len(parameters) > 0: #padLen = 4 - (71 + len(setup)) % 4 padLen = (4 - (73 + len(setup)) % 4 ) % 4 padBytes = '\xFF' * padLen respData['Pad1'] = padBytes respParameters['ParameterOffset'] = 73 + len(setup) + padLen else: padLen = 0 respParameters['ParameterOffset'] = 0 respData['Pad1'] = '' if len(data) > 0: #pad2Len = 4 - (71 + len(setup) + padLen + len(parameters)) % 4 pad2Len = (4 - (73 + len(setup) + padLen + len(parameters)) % 4) % 4 respData['Pad2'] = '\xFF' * pad2Len respParameters['DataOffset'] = 73 + len(setup) + padLen + len(parameters) + pad2Len else: respParameters['DataOffset'] = 0 respData['Pad2'] = '' respData['NT_Trans_Parameters'] = parameters[:respParameters['ParameterCount']] respData['NT_Trans_Data'] = data[:respParameters['DataCount']] respSMBCommand['Parameters'] = respParameters respSMBCommand['Data'] = respData data = data[respParameters['DataCount']:] remainingData -= respParameters['DataCount'] dataDisplacement += respParameters['DataCount'] + 1 parameters = parameters[respParameters['ParameterCount']:] remainingParameters -= respParameters['ParameterCount'] commands.append(respSMBCommand) smbServer.setConnectionData(connId, connData) return commands, None, errorCode else: #smbServer.log("Unsupported NTTransact command 0x%x" % command, logging.ERROR) respParameters = '' respData = '' errorCode = STATUS_NOT_IMPLEMENTED respSMBCommand['Parameters'] = respParameters respSMBCommand['Data'] = respData smbServer.setConnectionData(connId, connData) return [respSMBCommand], None, errorCode @staticmethod def smbTransaction2(connId, smbServer, SMBCommand, recvPacket, transCommands): connData = smbServer.getConnectionData(connId) respSMBCommand = smb.SMBCommand(recvPacket['Command']) trans2Parameters= smb.SMBTransaction2_Parameters(SMBCommand['Parameters']) # Do the stuff if trans2Parameters['ParameterCount'] != trans2Parameters['TotalParameterCount']: # TODO: Handle partial parameters #print "Unsupported partial parameters in TRANSACT2!" raise Exception("Unsupported partial parameters in TRANSACT2!") else: trans2Data = smb.SMBTransaction2_Data() # Standard says servers shouldn't trust Parameters and Data comes # in order, so we have to parse the offsets, ugly paramCount = trans2Parameters['ParameterCount'] trans2Data['Trans_ParametersLength'] = paramCount dataCount = trans2Parameters['DataCount'] trans2Data['Trans_DataLength'] = dataCount if trans2Parameters['ParameterOffset'] > 0: paramOffset = trans2Parameters['ParameterOffset'] - 63 - trans2Parameters['SetupLength'] trans2Data['Trans_Parameters'] = SMBCommand['Data'][paramOffset:paramOffset+paramCount] else: trans2Data['Trans_Parameters'] = '' if trans2Parameters['DataOffset'] > 0: dataOffset = trans2Parameters['DataOffset'] - 63 - trans2Parameters['SetupLength'] trans2Data['Trans_Data'] = SMBCommand['Data'][dataOffset:dataOffset + dataCount] else: trans2Data['Trans_Data'] = '' # Call the handler for this TRANSACTION command = struct.unpack(' 0 or remainingParameters > 0: respSMBCommand = smb.SMBCommand(recvPacket['Command']) respParameters = smb.SMBTransaction2Response_Parameters() respData = smb.SMBTransaction2Response_Data() respParameters['TotalParameterCount'] = len(parameters) respParameters['ParameterCount'] = len(parameters) respData['Trans_ParametersLength'] = len(parameters) respParameters['TotalDataCount'] = len(data) respParameters['DataDisplacement'] = dataDisplacement # TODO: Do the same for parameters if len(data) > trans2Parameters['MaxDataCount']: # Answer doesn't fit in this packet LOG.debug("Lowering answer from %d to %d" % (len(data),trans2Parameters['MaxDataCount']) ) respParameters['DataCount'] = trans2Parameters['MaxDataCount'] else: respParameters['DataCount'] = len(data) respData['Trans_DataLength'] = respParameters['DataCount'] respParameters['SetupCount'] = len(setup) respParameters['Setup'] = setup # TODO: Make sure we're calculating the pad right if len(parameters) > 0: #padLen = 4 - (55 + len(setup)) % 4 padLen = (4 - (55 + len(setup)) % 4 ) % 4 padBytes = '\xFF' * padLen respData['Pad1'] = padBytes respParameters['ParameterOffset'] = 55 + len(setup) + padLen else: padLen = 0 respParameters['ParameterOffset'] = 0 respData['Pad1'] = '' if len(data) > 0: #pad2Len = 4 - (55 + len(setup) + padLen + len(parameters)) % 4 pad2Len = (4 - (55 + len(setup) + padLen + len(parameters)) % 4) % 4 respData['Pad2'] = '\xFF' * pad2Len respParameters['DataOffset'] = 55 + len(setup) + padLen + len(parameters) + pad2Len else: respParameters['DataOffset'] = 0 respData['Pad2'] = '' respData['Trans_Parameters'] = parameters[:respParameters['ParameterCount']] respData['Trans_Data'] = data[:respParameters['DataCount']] respSMBCommand['Parameters'] = respParameters respSMBCommand['Data'] = respData data = data[respParameters['DataCount']:] remainingData -= respParameters['DataCount'] dataDisplacement += respParameters['DataCount'] + 1 parameters = parameters[respParameters['ParameterCount']:] remainingParameters -= respParameters['ParameterCount'] commands.append(respSMBCommand) smbServer.setConnectionData(connId, connData) return commands, None, errorCode else: smbServer.log("Unsupported Transact/2 command 0x%x" % command, logging.ERROR) respParameters = '' respData = '' errorCode = STATUS_NOT_IMPLEMENTED respSMBCommand['Parameters'] = respParameters respSMBCommand['Data'] = respData smbServer.setConnectionData(connId, connData) return [respSMBCommand], None, errorCode @staticmethod def smbComLockingAndX(connId, smbServer, SMBCommand, recvPacket): connData = smbServer.getConnectionData(connId) respSMBCommand = smb.SMBCommand(smb.SMB.SMB_COM_LOCKING_ANDX) respParameters = '' respData = '' # I'm actually doing nothing.. just make MacOS happy ;) errorCode = STATUS_SUCCESS respSMBCommand['Parameters'] = respParameters respSMBCommand['Data'] = respData smbServer.setConnectionData(connId, connData) return [respSMBCommand], None, errorCode @staticmethod def smbComClose(connId, smbServer, SMBCommand, recvPacket): connData = smbServer.getConnectionData(connId) respSMBCommand = smb.SMBCommand(smb.SMB.SMB_COM_CLOSE) respParameters = '' respData = '' comClose = smb.SMBClose_Parameters(SMBCommand['Parameters']) if connData['OpenedFiles'].has_key(comClose['FID']): errorCode = STATUS_SUCCESS fileHandle = connData['OpenedFiles'][comClose['FID']]['FileHandle'] try: if fileHandle == PIPE_FILE_DESCRIPTOR: connData['OpenedFiles'][comClose['FID']]['Socket'].close() elif fileHandle != VOID_FILE_DESCRIPTOR: os.close(fileHandle) except Exception, e: smbServer.log("comClose %s" % e, logging.ERROR) errorCode = STATUS_ACCESS_DENIED else: # Check if the file was marked for removal if connData['OpenedFiles'][comClose['FID']]['DeleteOnClose'] is True: try: os.remove(connData['OpenedFiles'][comClose['FID']]['FileName']) except Exception, e: smbServer.log("comClose %s" % e, logging.ERROR) errorCode = STATUS_ACCESS_DENIED del(connData['OpenedFiles'][comClose['FID']]) else: errorCode = STATUS_INVALID_HANDLE if errorCode > 0: respParameters = '' respData = '' respSMBCommand['Parameters'] = respParameters respSMBCommand['Data'] = respData smbServer.setConnectionData(connId, connData) return [respSMBCommand], None, errorCode @staticmethod def smbComWrite(connId, smbServer, SMBCommand, recvPacket): connData = smbServer.getConnectionData(connId) respSMBCommand = smb.SMBCommand(smb.SMB.SMB_COM_WRITE) respParameters = smb.SMBWriteResponse_Parameters() respData = '' comWriteParameters = smb.SMBWrite_Parameters(SMBCommand['Parameters']) comWriteData = smb.SMBWrite_Data(SMBCommand['Data']) if connData['OpenedFiles'].has_key(comWriteParameters['Fid']): fileHandle = connData['OpenedFiles'][comWriteParameters['Fid']]['FileHandle'] errorCode = STATUS_SUCCESS try: if fileHandle != PIPE_FILE_DESCRIPTOR: # TODO: Handle big size files # If we're trying to write past the file end we just skip the write call (Vista does this) if os.lseek(fileHandle, 0, 2) >= comWriteParameters['Offset']: os.lseek(fileHandle,comWriteParameters['Offset'],0) os.write(fileHandle,comWriteData['Data']) else: sock = connData['OpenedFiles'][comWriteParameters['Fid']]['Socket'] sock.send(comWriteData['Data']) respParameters['Count'] = comWriteParameters['Count'] except Exception, e: smbServer.log('smbComWrite: %s' % e, logging.ERROR) errorCode = STATUS_ACCESS_DENIED else: errorCode = STATUS_INVALID_HANDLE if errorCode > 0: respParameters = '' respData = '' respSMBCommand['Parameters'] = respParameters respSMBCommand['Data'] = respData smbServer.setConnectionData(connId, connData) return [respSMBCommand], None, errorCode @staticmethod def smbComFlush(connId, smbServer, SMBCommand,recvPacket ): connData = smbServer.getConnectionData(connId) respSMBCommand = smb.SMBCommand(smb.SMB.SMB_COM_FLUSH) respParameters = '' respData = '' comFlush = smb.SMBFlush_Parameters(SMBCommand['Parameters']) if connData['OpenedFiles'].has_key(comFlush['FID']): errorCode = STATUS_SUCCESS fileHandle = connData['OpenedFiles'][comFlush['FID']]['FileHandle'] try: os.fsync(fileHandle) except Exception, e: smbServer.log("comFlush %s" % e, logging.ERROR) errorCode = STATUS_ACCESS_DENIED else: errorCode = STATUS_INVALID_HANDLE if errorCode > 0: respParameters = '' respData = '' respSMBCommand['Parameters'] = respParameters respSMBCommand['Data'] = respData smbServer.setConnectionData(connId, connData) return [respSMBCommand], None, errorCode @staticmethod def smbComCreateDirectory(connId, smbServer, SMBCommand,recvPacket ): connData = smbServer.getConnectionData(connId) respSMBCommand = smb.SMBCommand(smb.SMB.SMB_COM_CREATE_DIRECTORY) respParameters = '' respData = '' comCreateDirectoryData= smb.SMBCreateDirectory_Data(flags = recvPacket['Flags2'], data = SMBCommand['Data']) # Get the Tid associated if connData['ConnectedShares'].has_key(recvPacket['Tid']): errorCode = STATUS_SUCCESS path = connData['ConnectedShares'][recvPacket['Tid']]['path'] fileName = os.path.normpath(decodeSMBString(recvPacket['Flags2'],comCreateDirectoryData['DirectoryName']).replace('\\','/')) if len(fileName) > 0: if fileName[0] == '/' or fileName[0] == '\\': # strip leading '/' fileName = fileName[1:] pathName = os.path.join(path,fileName) if os.path.exists(pathName): errorCode = STATUS_OBJECT_NAME_COLLISION # TODO: More checks here in the future.. Specially when we support # user access else: try: os.mkdir(pathName) except Exception, e: smbServer.log("smbComCreateDirectory: %s" % e, logging.ERROR) errorCode = STATUS_ACCESS_DENIED else: errorCode = STATUS_SMB_BAD_TID if errorCode > 0: respParameters = '' respData = '' respSMBCommand['Parameters'] = respParameters respSMBCommand['Data'] = respData smbServer.setConnectionData(connId, connData) return [respSMBCommand], None, errorCode @staticmethod def smbComRename(connId, smbServer, SMBCommand, recvPacket ): connData = smbServer.getConnectionData(connId) respSMBCommand = smb.SMBCommand(smb.SMB.SMB_COM_RENAME) respParameters = '' respData = '' comRenameData = smb.SMBRename_Data(flags = recvPacket['Flags2'], data = SMBCommand['Data']) # Get the Tid associated if connData['ConnectedShares'].has_key(recvPacket['Tid']): errorCode = STATUS_SUCCESS path = connData['ConnectedShares'][recvPacket['Tid']]['path'] oldFileName = os.path.normpath(decodeSMBString(recvPacket['Flags2'],comRenameData['OldFileName']).replace('\\','/')) newFileName = os.path.normpath(decodeSMBString(recvPacket['Flags2'],comRenameData['NewFileName']).replace('\\','/')) if len(oldFileName) > 0 and (oldFileName[0] == '/' or oldFileName[0] == '\\'): # strip leading '/' oldFileName = oldFileName[1:] oldPathName = os.path.join(path,oldFileName) if len(newFileName) > 0 and (newFileName[0] == '/' or newFileName[0] == '\\'): # strip leading '/' newFileName = newFileName[1:] newPathName = os.path.join(path,newFileName) if os.path.exists(oldPathName) is not True: errorCode = STATUS_NO_SUCH_FILE # TODO: More checks here in the future.. Specially when we support # user access else: try: os.rename(oldPathName,newPathName) except OSError, e: smbServer.log("smbComRename: %s" % e, logging.ERROR) errorCode = STATUS_ACCESS_DENIED else: errorCode = STATUS_SMB_BAD_TID if errorCode > 0: respParameters = '' respData = '' respSMBCommand['Parameters'] = respParameters respSMBCommand['Data'] = respData smbServer.setConnectionData(connId, connData) return [respSMBCommand], None, errorCode @staticmethod def smbComDelete(connId, smbServer, SMBCommand, recvPacket ): connData = smbServer.getConnectionData(connId) respSMBCommand = smb.SMBCommand(smb.SMB.SMB_COM_DELETE) respParameters = '' respData = '' comDeleteData = smb.SMBDelete_Data(flags = recvPacket['Flags2'], data = SMBCommand['Data']) # Get the Tid associated if connData['ConnectedShares'].has_key(recvPacket['Tid']): errorCode = STATUS_SUCCESS path = connData['ConnectedShares'][recvPacket['Tid']]['path'] fileName = os.path.normpath(decodeSMBString(recvPacket['Flags2'],comDeleteData['FileName']).replace('\\','/')) if len(fileName) > 0 and (fileName[0] == '/' or fileName[0] == '\\'): # strip leading '/' fileName = fileName[1:] pathName = os.path.join(path,fileName) if os.path.exists(pathName) is not True: errorCode = STATUS_NO_SUCH_FILE # TODO: More checks here in the future.. Specially when we support # user access else: try: os.remove(pathName) except OSError, e: smbServer.log("smbComDelete: %s" % e, logging.ERROR) errorCode = STATUS_ACCESS_DENIED else: errorCode = STATUS_SMB_BAD_TID if errorCode > 0: respParameters = '' respData = '' respSMBCommand['Parameters'] = respParameters respSMBCommand['Data'] = respData smbServer.setConnectionData(connId, connData) return [respSMBCommand], None, errorCode @staticmethod def smbComDeleteDirectory(connId, smbServer, SMBCommand, recvPacket ): connData = smbServer.getConnectionData(connId) respSMBCommand = smb.SMBCommand(smb.SMB.SMB_COM_DELETE_DIRECTORY) respParameters = '' respData = '' comDeleteDirectoryData= smb.SMBDeleteDirectory_Data(flags = recvPacket['Flags2'], data = SMBCommand['Data']) # Get the Tid associated if connData['ConnectedShares'].has_key(recvPacket['Tid']): errorCode = STATUS_SUCCESS path = connData['ConnectedShares'][recvPacket['Tid']]['path'] fileName = os.path.normpath(decodeSMBString(recvPacket['Flags2'],comDeleteDirectoryData['DirectoryName']).replace('\\','/')) if len(fileName) > 0 and (fileName[0] == '/' or fileName[0] == '\\'): # strip leading '/' fileName = fileName[1:] pathName = os.path.join(path,fileName) if os.path.exists(pathName) is not True: errorCode = STATUS_NO_SUCH_FILE # TODO: More checks here in the future.. Specially when we support # user access else: try: os.rmdir(pathName) except OSError, e: smbServer.log("smbComDeleteDirectory: %s" % e,logging.ERROR) if e.errno == errno.ENOTEMPTY: errorCode = STATUS_DIRECTORY_NOT_EMPTY else: errorCode = STATUS_ACCESS_DENIED else: errorCode = STATUS_SMB_BAD_TID if errorCode > 0: respParameters = '' respData = '' respSMBCommand['Parameters'] = respParameters respSMBCommand['Data'] = respData smbServer.setConnectionData(connId, connData) return [respSMBCommand], None, errorCode @staticmethod def smbComWriteAndX(connId, smbServer, SMBCommand, recvPacket): connData = smbServer.getConnectionData(connId) respSMBCommand = smb.SMBCommand(smb.SMB.SMB_COM_WRITE_ANDX) respParameters = smb.SMBWriteAndXResponse_Parameters() respData = '' if SMBCommand['WordCount'] == 0x0C: writeAndX = smb.SMBWriteAndX_Parameters_Short(SMBCommand['Parameters']) writeAndXData = smb.SMBWriteAndX_Data_Short() else: writeAndX = smb.SMBWriteAndX_Parameters(SMBCommand['Parameters']) writeAndXData = smb.SMBWriteAndX_Data() writeAndXData['DataLength'] = writeAndX['DataLength'] writeAndXData['DataOffset'] = writeAndX['DataOffset'] writeAndXData.fromString(SMBCommand['Data']) if connData['OpenedFiles'].has_key(writeAndX['Fid']): fileHandle = connData['OpenedFiles'][writeAndX['Fid']]['FileHandle'] errorCode = STATUS_SUCCESS try: if fileHandle != PIPE_FILE_DESCRIPTOR: offset = writeAndX['Offset'] if writeAndX.fields.has_key('HighOffset'): offset += (writeAndX['HighOffset'] << 32) # If we're trying to write past the file end we just skip the write call (Vista does this) if os.lseek(fileHandle, 0, 2) >= offset: os.lseek(fileHandle,offset,0) os.write(fileHandle,writeAndXData['Data']) else: sock = connData['OpenedFiles'][writeAndX['Fid']]['Socket'] sock.send(writeAndXData['Data']) respParameters['Count'] = writeAndX['DataLength'] respParameters['Available']= 0xff except Exception, e: smbServer.log('smbComWriteAndx: %s' % e, logging.ERROR) errorCode = STATUS_ACCESS_DENIED else: errorCode = STATUS_INVALID_HANDLE if errorCode > 0: respParameters = '' respData = '' respSMBCommand['Parameters'] = respParameters respSMBCommand['Data'] = respData smbServer.setConnectionData(connId, connData) return [respSMBCommand], None, errorCode @staticmethod def smbComRead(connId, smbServer, SMBCommand, recvPacket): connData = smbServer.getConnectionData(connId) respSMBCommand = smb.SMBCommand(smb.SMB.SMB_COM_READ) respParameters = smb.SMBReadResponse_Parameters() respData = smb.SMBReadResponse_Data() comReadParameters = smb.SMBRead_Parameters(SMBCommand['Parameters']) if connData['OpenedFiles'].has_key(comReadParameters['Fid']): fileHandle = connData['OpenedFiles'][comReadParameters['Fid']]['FileHandle'] errorCode = STATUS_SUCCESS try: if fileHandle != PIPE_FILE_DESCRIPTOR: # TODO: Handle big size files os.lseek(fileHandle,comReadParameters['Offset'],0) content = os.read(fileHandle,comReadParameters['Count']) else: sock = connData['OpenedFiles'][comReadParameters['Fid']]['Socket'] content = sock.recv(comReadParameters['Count']) respParameters['Count'] = len(content) respData['DataLength'] = len(content) respData['Data'] = content except Exception, e: smbServer.log('smbComRead: %s ' % e, logging.ERROR) errorCode = STATUS_ACCESS_DENIED else: errorCode = STATUS_INVALID_HANDLE if errorCode > 0: respParameters = '' respData = '' respSMBCommand['Parameters'] = respParameters respSMBCommand['Data'] = respData smbServer.setConnectionData(connId, connData) return [respSMBCommand], None, errorCode @staticmethod def smbComReadAndX(connId, smbServer, SMBCommand, recvPacket): connData = smbServer.getConnectionData(connId) respSMBCommand = smb.SMBCommand(smb.SMB.SMB_COM_READ_ANDX) respParameters = smb.SMBReadAndXResponse_Parameters() respData = '' if SMBCommand['WordCount'] == 0x0A: readAndX = smb.SMBReadAndX_Parameters2(SMBCommand['Parameters']) else: readAndX = smb.SMBReadAndX_Parameters(SMBCommand['Parameters']) if connData['OpenedFiles'].has_key(readAndX['Fid']): fileHandle = connData['OpenedFiles'][readAndX['Fid']]['FileHandle'] errorCode = 0 try: if fileHandle != PIPE_FILE_DESCRIPTOR: offset = readAndX['Offset'] if readAndX.fields.has_key('HighOffset'): offset += (readAndX['HighOffset'] << 32) os.lseek(fileHandle,offset,0) content = os.read(fileHandle,readAndX['MaxCount']) else: sock = connData['OpenedFiles'][readAndX['Fid']]['Socket'] content = sock.recv(readAndX['MaxCount']) respParameters['Remaining'] = 0xffff respParameters['DataCount'] = len(content) respParameters['DataOffset'] = 59 respParameters['DataCount_Hi'] = 0 respData = content except Exception, e: smbServer.log('smbComReadAndX: %s ' % e, logging.ERROR) errorCode = STATUS_ACCESS_DENIED else: errorCode = STATUS_INVALID_HANDLE if errorCode > 0: respParameters = '' respData = '' respSMBCommand['Parameters'] = respParameters respSMBCommand['Data'] = respData smbServer.setConnectionData(connId, connData) return [respSMBCommand], None, errorCode @staticmethod def smbQueryInformation(connId, smbServer, SMBCommand, recvPacket): connData = smbServer.getConnectionData(connId) respSMBCommand = smb.SMBCommand(smb.SMB.SMB_COM_QUERY_INFORMATION) respParameters = smb.SMBQueryInformationResponse_Parameters() respData = '' queryInformation= smb.SMBQueryInformation_Data(flags = recvPacket['Flags2'], data = SMBCommand['Data']) # Get the Tid associated if connData['ConnectedShares'].has_key(recvPacket['Tid']): fileSize, lastWriteTime, fileAttributes = queryFsInformation( connData['ConnectedShares'][recvPacket['Tid']]['path'], decodeSMBString(recvPacket['Flags2'],queryInformation['FileName'])) respParameters['FileSize'] = fileSize respParameters['LastWriteTime'] = lastWriteTime respParameters['FileAttributes'] = fileAttributes errorCode = STATUS_SUCCESS else: # STATUS_SMB_BAD_TID errorCode = STATUS_SMB_BAD_TID respParameters = '' respData = '' respSMBCommand['Parameters'] = respParameters respSMBCommand['Data'] = respData smbServer.setConnectionData(connId, connData) return [respSMBCommand], None, errorCode @staticmethod def smbQueryInformationDisk(connId, smbServer, SMBCommand, recvPacket): connData = smbServer.getConnectionData(connId) respSMBCommand = smb.SMBCommand(smb.SMB.SMB_COM_QUERY_INFORMATION_DISK) respParameters = smb.SMBQueryInformationDiskResponse_Parameters() respData = '' # Get the Tid associated if connData['ConnectedShares'].has_key(recvPacket['Tid']): totalUnits, freeUnits = queryDiskInformation( connData['ConnectedShares'][recvPacket['Tid']]['path']) respParameters['TotalUnits'] = totalUnits respParameters['BlocksPerUnit'] = 1 respParameters['BlockSize'] = 1 respParameters['FreeUnits'] = freeUnits errorCode = STATUS_SUCCESS else: # STATUS_SMB_BAD_TID respData = '' respParameters = '' errorCode = STATUS_SMB_BAD_TID respSMBCommand['Parameters'] = respParameters respSMBCommand['Data'] = respData smbServer.setConnectionData(connId, connData) return [respSMBCommand], None, errorCode @staticmethod def smbComEcho(connId, smbServer, SMBCommand, recvPacket): connData = smbServer.getConnectionData(connId) respSMBCommand = smb.SMBCommand(smb.SMB.SMB_COM_ECHO) respParameters = smb.SMBEchoResponse_Parameters() respData = smb.SMBEchoResponse_Data() echoData = smb.SMBEcho_Data(SMBCommand['Data']) respParameters['SequenceNumber'] = 1 respData['Data'] = echoData['Data'] respSMBCommand['Parameters'] = respParameters respSMBCommand['Data'] = respData errorCode = STATUS_SUCCESS smbServer.setConnectionData(connId, connData) return [respSMBCommand], None, errorCode @staticmethod def smbComTreeDisconnect(connId, smbServer, SMBCommand, recvPacket): connData = smbServer.getConnectionData(connId) respSMBCommand = smb.SMBCommand(smb.SMB.SMB_COM_TREE_DISCONNECT) # Check if the Tid matches the Tid trying to disconnect respParameters = '' respData = '' if connData['ConnectedShares'].has_key(recvPacket['Tid']): smbServer.log("Disconnecting Share(%d:%s)" % (recvPacket['Tid'],connData['ConnectedShares'][recvPacket['Tid']]['shareName'])) del(connData['ConnectedShares'][recvPacket['Tid']]) errorCode = STATUS_SUCCESS else: # STATUS_SMB_BAD_TID errorCode = STATUS_SMB_BAD_TID respSMBCommand['Parameters'] = respParameters respSMBCommand['Data'] = respData smbServer.setConnectionData(connId, connData) return [respSMBCommand], None, errorCode @staticmethod def smbComLogOffAndX(connId, smbServer, SMBCommand, recvPacket): connData = smbServer.getConnectionData(connId) respSMBCommand = smb.SMBCommand(smb.SMB.SMB_COM_LOGOFF_ANDX) # Check if the Uid matches the user trying to logoff respParameters = '' respData = '' if recvPacket['Uid'] != connData['Uid']: # STATUS_SMB_BAD_UID errorCode = STATUS_SMB_BAD_UID else: errorCode = STATUS_SUCCESS respSMBCommand['Parameters'] = respParameters respSMBCommand['Data'] = respData connData['Uid'] = 0 smbServer.setConnectionData(connId, connData) return [respSMBCommand], None, errorCode @staticmethod def smbComQueryInformation2(connId, smbServer, SMBCommand, recvPacket): connData = smbServer.getConnectionData(connId) respSMBCommand = smb.SMBCommand(smb.SMB.SMB_COM_QUERY_INFORMATION2) respParameters = smb.SMBQueryInformation2Response_Parameters() respData = '' queryInformation2 = smb.SMBQueryInformation2_Parameters(SMBCommand['Parameters']) errorCode = 0xFF if connData['OpenedFiles'].has_key(queryInformation2['Fid']): errorCode = STATUS_SUCCESS pathName = connData['OpenedFiles'][queryInformation2['Fid']]['FileName'] try: (mode, ino, dev, nlink, uid, gid, size, atime, mtime, ctime) = os.stat(pathName) respParameters['CreateDate'] = getSMBDate(ctime) respParameters['CreationTime'] = getSMBTime(ctime) respParameters['LastAccessDate'] = getSMBDate(atime) respParameters['LastAccessTime'] = getSMBTime(atime) respParameters['LastWriteDate'] = getSMBDate(mtime) respParameters['LastWriteTime'] = getSMBTime(mtime) respParameters['FileDataSize'] = size respParameters['FileAllocationSize'] = size attribs = 0 if os.path.isdir(pathName): attribs = smb.SMB_FILE_ATTRIBUTE_DIRECTORY if os.path.isfile(pathName): attribs = smb.SMB_FILE_ATTRIBUTE_NORMAL respParameters['FileAttributes'] = attribs except Exception, e: smbServer.log('smbComQueryInformation2 %s' % e,logging.ERROR) errorCode = STATUS_ACCESS_DENIED if errorCode > 0: respParameters = '' respData = '' respSMBCommand['Parameters'] = respParameters respSMBCommand['Data'] = respData smbServer.setConnectionData(connId, connData) return [respSMBCommand], None, errorCode @staticmethod def smbComNtCreateAndX(connId, smbServer, SMBCommand, recvPacket): # TODO: Fully implement this connData = smbServer.getConnectionData(connId) respSMBCommand = smb.SMBCommand(smb.SMB.SMB_COM_NT_CREATE_ANDX) respParameters = smb.SMBNtCreateAndXResponse_Parameters() respData = '' ntCreateAndXParameters = smb.SMBNtCreateAndX_Parameters(SMBCommand['Parameters']) ntCreateAndXData = smb.SMBNtCreateAndX_Data( flags = recvPacket['Flags2'], data = SMBCommand['Data']) #if ntCreateAndXParameters['CreateFlags'] & 0x10: # NT_CREATE_REQUEST_EXTENDED_RESPONSE # respParameters = smb.SMBNtCreateAndXExtendedResponse_Parameters() # respParameters['VolumeGUID'] = '\x00' # Get the Tid associated if connData['ConnectedShares'].has_key(recvPacket['Tid']): # If we have a rootFid, the path is relative to that fid errorCode = STATUS_SUCCESS if ntCreateAndXParameters['RootFid'] > 0: path = connData['OpenedFiles'][ntCreateAndXParameters['RootFid']]['FileName'] LOG.debug("RootFid present %s!" % path) else: if connData['ConnectedShares'][recvPacket['Tid']].has_key('path'): path = connData['ConnectedShares'][recvPacket['Tid']]['path'] else: path = 'NONE' errorCode = STATUS_ACCESS_DENIED deleteOnClose = False fileName = os.path.normpath(decodeSMBString(recvPacket['Flags2'],ntCreateAndXData['FileName']).replace('\\','/')) if len(fileName) > 0 and (fileName[0] == '/' or fileName[0] == '\\'): # strip leading '/' fileName = fileName[1:] pathName = os.path.join(path,fileName) createDisposition = ntCreateAndXParameters['Disposition'] mode = 0 if createDisposition == smb.FILE_SUPERSEDE: mode |= os.O_TRUNC | os.O_CREAT elif createDisposition & smb.FILE_OVERWRITE_IF == smb.FILE_OVERWRITE_IF: mode |= os.O_TRUNC | os.O_CREAT elif createDisposition & smb.FILE_OVERWRITE == smb.FILE_OVERWRITE: if os.path.exists(pathName) is True: mode |= os.O_TRUNC else: errorCode = STATUS_NO_SUCH_FILE elif createDisposition & smb.FILE_OPEN_IF == smb.FILE_OPEN_IF: if os.path.exists(pathName) is True: mode |= os.O_TRUNC else: mode |= os.O_TRUNC | os.O_CREAT elif createDisposition & smb.FILE_CREATE == smb.FILE_CREATE: if os.path.exists(pathName) is True: errorCode = STATUS_OBJECT_NAME_COLLISION else: mode |= os.O_CREAT elif createDisposition & smb.FILE_OPEN == smb.FILE_OPEN: if os.path.exists(pathName) is not True and smbServer.getRegisteredNamedPipes().has_key(unicode(pathName)) is not True: errorCode = STATUS_NO_SUCH_FILE if errorCode == STATUS_SUCCESS: desiredAccess = ntCreateAndXParameters['AccessMask'] if (desiredAccess & smb.FILE_READ_DATA) or (desiredAccess & smb.GENERIC_READ): mode |= os.O_RDONLY if (desiredAccess & smb.FILE_WRITE_DATA) or (desiredAccess & smb.GENERIC_WRITE): if (desiredAccess & smb.FILE_READ_DATA) or (desiredAccess & smb.GENERIC_READ): mode |= os.O_RDWR #| os.O_APPEND else: mode |= os.O_WRONLY #| os.O_APPEND if desiredAccess & smb.GENERIC_ALL: mode |= os.O_RDWR #| os.O_APPEND createOptions = ntCreateAndXParameters['CreateOptions'] if mode & os.O_CREAT == os.O_CREAT: if createOptions & smb.FILE_DIRECTORY_FILE == smb.FILE_DIRECTORY_FILE: try: # Let's create the directory os.mkdir(pathName) mode = os.O_RDONLY except Exception, e: smbServer.log("NTCreateAndX: %s,%s,%s" % (pathName,mode,e),logging.ERROR) errorCode = STATUS_ACCESS_DENIED if createOptions & smb.FILE_NON_DIRECTORY_FILE == smb.FILE_NON_DIRECTORY_FILE: # If the file being opened is a directory, the server MUST fail the request with # STATUS_FILE_IS_A_DIRECTORY in the Status field of the SMB Header in the server # response. if os.path.isdir(pathName) is True: errorCode = STATUS_FILE_IS_A_DIRECTORY if createOptions & smb.FILE_DELETE_ON_CLOSE == smb.FILE_DELETE_ON_CLOSE: deleteOnClose = True if errorCode == STATUS_SUCCESS: try: if os.path.isdir(pathName) and sys.platform == 'win32': fid = VOID_FILE_DESCRIPTOR else: if sys.platform == 'win32': mode |= os.O_BINARY if smbServer.getRegisteredNamedPipes().has_key(unicode(pathName)): fid = PIPE_FILE_DESCRIPTOR sock = socket.socket() sock.connect(smbServer.getRegisteredNamedPipes()[unicode(pathName)]) else: fid = os.open(pathName, mode) except Exception, e: smbServer.log("NTCreateAndX: %s,%s,%s" % (pathName,mode,e),logging.ERROR) #print e fid = 0 errorCode = STATUS_ACCESS_DENIED else: errorCode = STATUS_SMB_BAD_TID if errorCode == STATUS_SUCCESS: # Simple way to generate a fid if len(connData['OpenedFiles']) == 0: fakefid = 1 else: fakefid = connData['OpenedFiles'].keys()[-1] + 1 respParameters['Fid'] = fakefid respParameters['CreateAction'] = createDisposition if fid == PIPE_FILE_DESCRIPTOR: respParameters['FileAttributes'] = 0x80 respParameters['IsDirectory'] = 0 respParameters['CreateTime'] = 0 respParameters['LastAccessTime'] = 0 respParameters['LastWriteTime'] = 0 respParameters['LastChangeTime'] = 0 respParameters['AllocationSize'] = 4096 respParameters['EndOfFile'] = 0 respParameters['FileType'] = 2 respParameters['IPCState'] = 0x5ff else: if os.path.isdir(pathName): respParameters['FileAttributes'] = smb.SMB_FILE_ATTRIBUTE_DIRECTORY respParameters['IsDirectory'] = 1 else: respParameters['IsDirectory'] = 0 respParameters['FileAttributes'] = ntCreateAndXParameters['FileAttributes'] # Let's get this file's information respInfo, errorCode = queryPathInformation('',pathName,level= smb.SMB_QUERY_FILE_ALL_INFO) if errorCode == STATUS_SUCCESS: respParameters['CreateTime'] = respInfo['CreationTime'] respParameters['LastAccessTime'] = respInfo['LastAccessTime'] respParameters['LastWriteTime'] = respInfo['LastWriteTime'] respParameters['LastChangeTime'] = respInfo['LastChangeTime'] respParameters['FileAttributes'] = respInfo['ExtFileAttributes'] respParameters['AllocationSize'] = respInfo['AllocationSize'] respParameters['EndOfFile'] = respInfo['EndOfFile'] else: respParameters = '' respData = '' if errorCode == STATUS_SUCCESS: # Let's store the fid for the connection # smbServer.log('Create file %s, mode:0x%x' % (pathName, mode)) connData['OpenedFiles'][fakefid] = {} connData['OpenedFiles'][fakefid]['FileHandle'] = fid connData['OpenedFiles'][fakefid]['FileName'] = pathName connData['OpenedFiles'][fakefid]['DeleteOnClose'] = deleteOnClose if fid == PIPE_FILE_DESCRIPTOR: connData['OpenedFiles'][fakefid]['Socket'] = sock else: respParameters = '' respData = '' respSMBCommand['Parameters'] = respParameters respSMBCommand['Data'] = respData smbServer.setConnectionData(connId, connData) return [respSMBCommand], None, errorCode @staticmethod def smbComOpenAndX(connId, smbServer, SMBCommand, recvPacket): connData = smbServer.getConnectionData(connId) respSMBCommand = smb.SMBCommand(smb.SMB.SMB_COM_OPEN_ANDX) respParameters = smb.SMBOpenAndXResponse_Parameters() respData = '' openAndXParameters = smb.SMBOpenAndX_Parameters(SMBCommand['Parameters']) openAndXData = smb.SMBOpenAndX_Data( flags = recvPacket['Flags2'], data = SMBCommand['Data']) # Get the Tid associated if connData['ConnectedShares'].has_key(recvPacket['Tid']): path = connData['ConnectedShares'][recvPacket['Tid']]['path'] openedFile, mode, pathName, errorCode = openFile(path, decodeSMBString(recvPacket['Flags2'],openAndXData['FileName']), openAndXParameters['DesiredAccess'], openAndXParameters['FileAttributes'], openAndXParameters['OpenMode']) else: errorCode = STATUS_SMB_BAD_TID if errorCode == STATUS_SUCCESS: # Simple way to generate a fid fid = len(connData['OpenedFiles']) + 1 if len(connData['OpenedFiles']) == 0: fid = 1 else: fid = connData['OpenedFiles'].keys()[-1] + 1 respParameters['Fid'] = fid if mode & os.O_CREAT: # File did not exist and was created respParameters['Action'] = 0x2 elif mode & os.O_RDONLY: # File existed and was opened respParameters['Action'] = 0x1 elif mode & os.O_APPEND: # File existed and was opened respParameters['Action'] = 0x1 else: # File existed and was truncated respParameters['Action'] = 0x3 # Let's store the fid for the connection #smbServer.log('Opening file %s' % pathName) connData['OpenedFiles'][fid] = {} connData['OpenedFiles'][fid]['FileHandle'] = openedFile connData['OpenedFiles'][fid]['FileName'] = pathName connData['OpenedFiles'][fid]['DeleteOnClose'] = False else: respParameters = '' respData = '' respSMBCommand['Parameters'] = respParameters respSMBCommand['Data'] = respData smbServer.setConnectionData(connId, connData) return [respSMBCommand], None, errorCode @staticmethod def smbComTreeConnectAndX(connId, smbServer, SMBCommand, recvPacket): connData = smbServer.getConnectionData(connId) resp = smb.NewSMBPacket() resp['Flags1'] = smb.SMB.FLAGS1_REPLY resp['Flags2'] = smb.SMB.FLAGS2_EXTENDED_SECURITY | smb.SMB.FLAGS2_NT_STATUS | smb.SMB.FLAGS2_LONG_NAMES | recvPacket['Flags2'] & smb.SMB.FLAGS2_UNICODE resp['Tid'] = recvPacket['Tid'] resp['Mid'] = recvPacket['Mid'] resp['Pid'] = connData['Pid'] respSMBCommand = smb.SMBCommand(smb.SMB.SMB_COM_TREE_CONNECT_ANDX) respParameters = smb.SMBTreeConnectAndXResponse_Parameters() respData = smb.SMBTreeConnectAndXResponse_Data() treeConnectAndXParameters = smb.SMBTreeConnectAndX_Parameters(SMBCommand['Parameters']) if treeConnectAndXParameters['Flags'] & 0x8: respParameters = smb.SMBTreeConnectAndXExtendedResponse_Parameters() treeConnectAndXData = smb.SMBTreeConnectAndX_Data( flags = recvPacket['Flags2'] ) treeConnectAndXData['_PasswordLength'] = treeConnectAndXParameters['PasswordLength'] treeConnectAndXData.fromString(SMBCommand['Data']) errorCode = STATUS_SUCCESS ## Process here the request, does the share exist? UNCOrShare = decodeSMBString(recvPacket['Flags2'], treeConnectAndXData['Path']) # Is this a UNC? if ntpath.ismount(UNCOrShare): path = UNCOrShare.split('\\')[3] else: path = ntpath.basename(UNCOrShare) share = searchShare(connId, path, smbServer) if share is not None: # Simple way to generate a Tid if len(connData['ConnectedShares']) == 0: tid = 1 else: tid = connData['ConnectedShares'].keys()[-1] + 1 connData['ConnectedShares'][tid] = share connData['ConnectedShares'][tid]['shareName'] = path resp['Tid'] = tid #smbServer.log("Connecting Share(%d:%s)" % (tid,path)) else: smbServer.log("TreeConnectAndX not found %s" % path, logging.ERROR) errorCode = STATUS_OBJECT_PATH_NOT_FOUND resp['ErrorCode'] = errorCode >> 16 resp['ErrorClass'] = errorCode & 0xff ## respParameters['OptionalSupport'] = smb.SMB.SMB_SUPPORT_SEARCH_BITS if path == 'IPC$': respData['Service'] = 'IPC' else: respData['Service'] = path respData['PadLen'] = 0 respData['NativeFileSystem'] = encodeSMBString(recvPacket['Flags2'], 'NTFS' ) respSMBCommand['Parameters'] = respParameters respSMBCommand['Data'] = respData resp['Uid'] = connData['Uid'] resp.addCommand(respSMBCommand) smbServer.setConnectionData(connId, connData) return None, [resp], errorCode @staticmethod def smbComSessionSetupAndX(connId, smbServer, SMBCommand, recvPacket): connData = smbServer.getConnectionData(connId, checkStatus = False) respSMBCommand = smb.SMBCommand(smb.SMB.SMB_COM_SESSION_SETUP_ANDX) # From [MS-SMB] # When extended security is being used (see section 3.2.4.2.4), the # request MUST take the following form # [..] # WordCount (1 byte): The value of this field MUST be 0x0C. if SMBCommand['WordCount'] == 12: # Extended security. Here we deal with all SPNEGO stuff respParameters = smb.SMBSessionSetupAndX_Extended_Response_Parameters() respData = smb.SMBSessionSetupAndX_Extended_Response_Data(flags = recvPacket['Flags2']) sessionSetupParameters = smb.SMBSessionSetupAndX_Extended_Parameters(SMBCommand['Parameters']) sessionSetupData = smb.SMBSessionSetupAndX_Extended_Data() sessionSetupData['SecurityBlobLength'] = sessionSetupParameters['SecurityBlobLength'] sessionSetupData.fromString(SMBCommand['Data']) connData['Capabilities'] = sessionSetupParameters['Capabilities'] rawNTLM = False if struct.unpack('B',sessionSetupData['SecurityBlob'][0])[0] == ASN1_AID: # NEGOTIATE packet blob = SPNEGO_NegTokenInit(sessionSetupData['SecurityBlob']) token = blob['MechToken'] if len(blob['MechTypes'][0]) > 0: # Is this GSSAPI NTLM or something else we don't support? mechType = blob['MechTypes'][0] if mechType != TypesMech['NTLMSSP - Microsoft NTLM Security Support Provider']: # Nope, do we know it? if MechTypes.has_key(mechType): mechStr = MechTypes[mechType] else: mechStr = hexlify(mechType) smbServer.log("Unsupported MechType '%s'" % mechStr, logging.CRITICAL) # We don't know the token, we answer back again saying # we just support NTLM. # ToDo: Build this into a SPNEGO_NegTokenResp() respToken = '\xa1\x15\x30\x13\xa0\x03\x0a\x01\x03\xa1\x0c\x06\x0a\x2b\x06\x01\x04\x01\x82\x37\x02\x02\x0a' respParameters['SecurityBlobLength'] = len(respToken) respData['SecurityBlobLength'] = respParameters['SecurityBlobLength'] respData['SecurityBlob'] = respToken respData['NativeOS'] = encodeSMBString(recvPacket['Flags2'], smbServer.getServerOS()) respData['NativeLanMan'] = encodeSMBString(recvPacket['Flags2'], smbServer.getServerOS()) respSMBCommand['Parameters'] = respParameters respSMBCommand['Data'] = respData return [respSMBCommand], None, STATUS_MORE_PROCESSING_REQUIRED elif struct.unpack('B',sessionSetupData['SecurityBlob'][0])[0] == ASN1_SUPPORTED_MECH: # AUTH packet blob = SPNEGO_NegTokenResp(sessionSetupData['SecurityBlob']) token = blob['ResponseToken'] else: # No GSSAPI stuff, raw NTLMSSP rawNTLM = True token = sessionSetupData['SecurityBlob'] # Here we only handle NTLMSSP, depending on what stage of the # authentication we are, we act on it messageType = struct.unpack('> 16 packet['ErrorClass'] = errorCode & 0xff return None, [packet], errorCode class SMB2Commands: @staticmethod def smb2Negotiate(connId, smbServer, recvPacket, isSMB1 = False): connData = smbServer.getConnectionData(connId, checkStatus = False) respPacket = smb2.SMB2Packet() respPacket['Flags'] = smb2.SMB2_FLAGS_SERVER_TO_REDIR respPacket['Status'] = STATUS_SUCCESS respPacket['CreditRequestResponse'] = 1 respPacket['Command'] = smb2.SMB2_NEGOTIATE respPacket['SessionID'] = 0 if isSMB1 is False: respPacket['MessageID'] = recvPacket['MessageID'] else: respPacket['MessageID'] = 0 respPacket['TreeID'] = 0 respSMBCommand = smb2.SMB2Negotiate_Response() respSMBCommand['SecurityMode'] = 1 if isSMB1 is True: # Let's first parse the packet to see if the client supports SMB2 SMBCommand = smb.SMBCommand(recvPacket['Data'][0]) dialects = SMBCommand['Data'].split('\x02') if 'SMB 2.002\x00' in dialects or 'SMB 2.???\x00' in dialects: respSMBCommand['DialectRevision'] = smb2.SMB2_DIALECT_002 else: # Client does not support SMB2 fallbacking raise Exception('SMB2 not supported, fallbacking') else: respSMBCommand['DialectRevision'] = smb2.SMB2_DIALECT_002 respSMBCommand['ServerGuid'] = 'A'*16 respSMBCommand['Capabilities'] = 0 respSMBCommand['MaxTransactSize'] = 65536 respSMBCommand['MaxReadSize'] = 65536 respSMBCommand['MaxWriteSize'] = 65536 respSMBCommand['SystemTime'] = getFileTime(calendar.timegm(time.gmtime())) respSMBCommand['ServerStartTime'] = getFileTime(calendar.timegm(time.gmtime())) respSMBCommand['SecurityBufferOffset'] = 0x80 blob = SPNEGO_NegTokenInit() blob['MechTypes'] = [TypesMech['NTLMSSP - Microsoft NTLM Security Support Provider']] respSMBCommand['Buffer'] = blob.getData() respSMBCommand['SecurityBufferLength'] = len(respSMBCommand['Buffer']) respPacket['Data'] = respSMBCommand smbServer.setConnectionData(connId, connData) return None, [respPacket], STATUS_SUCCESS @staticmethod def smb2SessionSetup(connId, smbServer, recvPacket): connData = smbServer.getConnectionData(connId, checkStatus = False) respSMBCommand = smb2.SMB2SessionSetup_Response() sessionSetupData = smb2.SMB2SessionSetup(recvPacket['Data']) connData['Capabilities'] = sessionSetupData['Capabilities'] securityBlob = sessionSetupData['Buffer'] rawNTLM = False if struct.unpack('B',securityBlob[0])[0] == ASN1_AID: # NEGOTIATE packet blob = SPNEGO_NegTokenInit(securityBlob) token = blob['MechToken'] if len(blob['MechTypes'][0]) > 0: # Is this GSSAPI NTLM or something else we don't support? mechType = blob['MechTypes'][0] if mechType != TypesMech['NTLMSSP - Microsoft NTLM Security Support Provider']: # Nope, do we know it? if MechTypes.has_key(mechType): mechStr = MechTypes[mechType] else: mechStr = hexlify(mechType) smbServer.log("Unsupported MechType '%s'" % mechStr, logging.CRITICAL) # We don't know the token, we answer back again saying # we just support NTLM. # ToDo: Build this into a SPNEGO_NegTokenResp() respToken = '\xa1\x15\x30\x13\xa0\x03\x0a\x01\x03\xa1\x0c\x06\x0a\x2b\x06\x01\x04\x01\x82\x37\x02\x02\x0a' respSMBCommand['SecurityBufferOffset'] = 0x48 respSMBCommand['SecurityBufferLength'] = len(respToken) respSMBCommand['Buffer'] = respToken return [respSMBCommand], None, STATUS_MORE_PROCESSING_REQUIRED elif struct.unpack('B',securityBlob[0])[0] == ASN1_SUPPORTED_MECH: # AUTH packet blob = SPNEGO_NegTokenResp(securityBlob) token = blob['ResponseToken'] else: # No GSSAPI stuff, raw NTLMSSP rawNTLM = True token = securityBlob # Here we only handle NTLMSSP, depending on what stage of the # authentication we are, we act on it messageType = struct.unpack(' 0 and (fileName[0] == '/' or fileName[0] == '\\'): # strip leading '/' fileName = fileName[1:] pathName = os.path.join(path,fileName) createDisposition = ntCreateRequest['CreateDisposition'] mode = 0 if createDisposition == smb2.FILE_SUPERSEDE: mode |= os.O_TRUNC | os.O_CREAT elif createDisposition & smb2.FILE_OVERWRITE_IF == smb2.FILE_OVERWRITE_IF: mode |= os.O_TRUNC | os.O_CREAT elif createDisposition & smb2.FILE_OVERWRITE == smb2.FILE_OVERWRITE: if os.path.exists(pathName) is True: mode |= os.O_TRUNC else: errorCode = STATUS_NO_SUCH_FILE elif createDisposition & smb2.FILE_OPEN_IF == smb2.FILE_OPEN_IF: if os.path.exists(pathName) is True: mode |= os.O_TRUNC else: mode |= os.O_TRUNC | os.O_CREAT elif createDisposition & smb2.FILE_CREATE == smb2.FILE_CREATE: if os.path.exists(pathName) is True: errorCode = STATUS_OBJECT_NAME_COLLISION else: mode |= os.O_CREAT elif createDisposition & smb2.FILE_OPEN == smb2.FILE_OPEN: if os.path.exists(pathName) is not True and smbServer.getRegisteredNamedPipes().has_key(unicode(pathName)) is not True: errorCode = STATUS_NO_SUCH_FILE if errorCode == STATUS_SUCCESS: desiredAccess = ntCreateRequest['DesiredAccess'] if (desiredAccess & smb2.FILE_READ_DATA) or (desiredAccess & smb2.GENERIC_READ): mode |= os.O_RDONLY if (desiredAccess & smb2.FILE_WRITE_DATA) or (desiredAccess & smb2.GENERIC_WRITE): if (desiredAccess & smb2.FILE_READ_DATA) or (desiredAccess & smb2.GENERIC_READ): mode |= os.O_RDWR #| os.O_APPEND else: mode |= os.O_WRONLY #| os.O_APPEND if desiredAccess & smb2.GENERIC_ALL: mode |= os.O_RDWR #| os.O_APPEND createOptions = ntCreateRequest['CreateOptions'] if mode & os.O_CREAT == os.O_CREAT: if createOptions & smb2.FILE_DIRECTORY_FILE == smb2.FILE_DIRECTORY_FILE: try: # Let's create the directory os.mkdir(pathName) mode = os.O_RDONLY except Exception, e: smbServer.log("SMB2_CREATE: %s,%s,%s" % (pathName,mode,e),logging.ERROR) errorCode = STATUS_ACCESS_DENIED if createOptions & smb2.FILE_NON_DIRECTORY_FILE == smb2.FILE_NON_DIRECTORY_FILE: # If the file being opened is a directory, the server MUST fail the request with # STATUS_FILE_IS_A_DIRECTORY in the Status field of the SMB Header in the server # response. if os.path.isdir(pathName) is True: errorCode = STATUS_FILE_IS_A_DIRECTORY if createOptions & smb2.FILE_DELETE_ON_CLOSE == smb2.FILE_DELETE_ON_CLOSE: deleteOnClose = True if errorCode == STATUS_SUCCESS: try: if os.path.isdir(pathName) and sys.platform == 'win32': fid = VOID_FILE_DESCRIPTOR else: if sys.platform == 'win32': mode |= os.O_BINARY if smbServer.getRegisteredNamedPipes().has_key(unicode(pathName)): fid = PIPE_FILE_DESCRIPTOR sock = socket.socket() sock.connect(smbServer.getRegisteredNamedPipes()[unicode(pathName)]) else: fid = os.open(pathName, mode) except Exception, e: smbServer.log("SMB2_CREATE: %s,%s,%s" % (pathName,mode,e),logging.ERROR) #print e fid = 0 errorCode = STATUS_ACCESS_DENIED else: errorCode = STATUS_SMB_BAD_TID if errorCode == STATUS_SUCCESS: # Simple way to generate a fid fakefid = uuid.generate() respSMBCommand['FileID'] = fakefid respSMBCommand['CreateAction'] = createDisposition if fid == PIPE_FILE_DESCRIPTOR: respSMBCommand['CreationTime'] = 0 respSMBCommand['LastAccessTime'] = 0 respSMBCommand['LastWriteTime'] = 0 respSMBCommand['ChangeTime'] = 0 respSMBCommand['AllocationSize'] = 4096 respSMBCommand['EndOfFile'] = 0 respSMBCommand['FileAttributes'] = 0x80 else: if os.path.isdir(pathName): respSMBCommand['FileAttributes'] = smb.SMB_FILE_ATTRIBUTE_DIRECTORY else: respSMBCommand['FileAttributes'] = ntCreateRequest['FileAttributes'] # Let's get this file's information respInfo, errorCode = queryPathInformation('',pathName,level= smb.SMB_QUERY_FILE_ALL_INFO) if errorCode == STATUS_SUCCESS: respSMBCommand['CreationTime'] = respInfo['CreationTime'] respSMBCommand['LastAccessTime'] = respInfo['LastAccessTime'] respSMBCommand['LastWriteTime'] = respInfo['LastWriteTime'] respSMBCommand['LastChangeTime'] = respInfo['LastChangeTime'] respSMBCommand['FileAttributes'] = respInfo['ExtFileAttributes'] respSMBCommand['AllocationSize'] = respInfo['AllocationSize'] respSMBCommand['EndOfFile'] = respInfo['EndOfFile'] if errorCode == STATUS_SUCCESS: # Let's store the fid for the connection # smbServer.log('Create file %s, mode:0x%x' % (pathName, mode)) connData['OpenedFiles'][fakefid] = {} connData['OpenedFiles'][fakefid]['FileHandle'] = fid connData['OpenedFiles'][fakefid]['FileName'] = pathName connData['OpenedFiles'][fakefid]['DeleteOnClose'] = deleteOnClose connData['OpenedFiles'][fakefid]['Open'] = {} connData['OpenedFiles'][fakefid]['Open']['EnumerationLocation'] = 0 connData['OpenedFiles'][fakefid]['Open']['EnumerationSearchPattern'] = '' if fid == PIPE_FILE_DESCRIPTOR: connData['OpenedFiles'][fakefid]['Socket'] = sock else: respSMBCommand = smb2.SMB2Error() if errorCode == STATUS_SUCCESS: connData['LastRequest']['SMB2_CREATE'] = respSMBCommand smbServer.setConnectionData(connId, connData) return [respSMBCommand], None, errorCode @staticmethod def smb2Close(connId, smbServer, recvPacket): connData = smbServer.getConnectionData(connId) respSMBCommand = smb2.SMB2Close_Response() closeRequest = smb2.SMB2Close(recvPacket['Data']) if str(closeRequest['FileID']) == '\xff'*16: # Let's take the data from the lastRequest if connData['LastRequest'].has_key('SMB2_CREATE'): fileID = connData['LastRequest']['SMB2_CREATE']['FileID'] else: fileID = str(closeRequest['FileID']) else: fileID = str(closeRequest['FileID']) if connData['OpenedFiles'].has_key(fileID): errorCode = STATUS_SUCCESS fileHandle = connData['OpenedFiles'][fileID]['FileHandle'] pathName = connData['OpenedFiles'][fileID]['FileName'] infoRecord = None try: if fileHandle == PIPE_FILE_DESCRIPTOR: connData['OpenedFiles'][fileID]['Socket'].close() elif fileHandle != VOID_FILE_DESCRIPTOR: os.close(fileHandle) infoRecord, errorCode = queryFileInformation(os.path.dirname(pathName), os.path.basename(pathName), smb2.SMB2_FILE_NETWORK_OPEN_INFO) except Exception, e: smbServer.log("SMB2_CLOSE %s" % e, logging.ERROR) errorCode = STATUS_INVALID_HANDLE else: # Check if the file was marked for removal if connData['OpenedFiles'][fileID]['DeleteOnClose'] is True: try: if os.path.isdir(pathName): shutil.rmtree(connData['OpenedFiles'][fileID]['FileName']) else: os.remove(connData['OpenedFiles'][fileID]['FileName']) except Exception, e: smbServer.log("SMB2_CLOSE %s" % e, logging.ERROR) errorCode = STATUS_ACCESS_DENIED # Now fill out the response if infoRecord is not None: respSMBCommand['CreationTime'] = infoRecord['CreationTime'] respSMBCommand['LastAccessTime'] = infoRecord['LastAccessTime'] respSMBCommand['LastWriteTime'] = infoRecord['LastWriteTime'] respSMBCommand['ChangeTime'] = infoRecord['ChangeTime'] respSMBCommand['AllocationSize'] = infoRecord['AllocationSize'] respSMBCommand['EndofFile'] = infoRecord['EndOfFile'] respSMBCommand['FileAttributes'] = infoRecord['FileAttributes'] if errorCode == STATUS_SUCCESS: del(connData['OpenedFiles'][fileID]) else: errorCode = STATUS_INVALID_HANDLE smbServer.setConnectionData(connId, connData) return [respSMBCommand], None, errorCode @staticmethod def smb2QueryInfo(connId, smbServer, recvPacket): connData = smbServer.getConnectionData(connId) respSMBCommand = smb2.SMB2QueryInfo_Response() queryInfo = smb2.SMB2QueryInfo(recvPacket['Data']) errorCode = STATUS_SUCCESS respSMBCommand['OutputBufferOffset'] = 0x48 respSMBCommand['Buffer'] = '\x00' if str(queryInfo['FileID']) == '\xff'*16: # Let's take the data from the lastRequest if connData['LastRequest'].has_key('SMB2_CREATE'): fileID = connData['LastRequest']['SMB2_CREATE']['FileID'] else: fileID = str(queryInfo['FileID']) else: fileID = str(queryInfo['FileID']) if connData['ConnectedShares'].has_key(recvPacket['TreeID']): if connData['OpenedFiles'].has_key(fileID): fileName = connData['OpenedFiles'][fileID]['FileName'] if queryInfo['InfoType'] == smb2.SMB2_0_INFO_FILE: if queryInfo['FileInfoClass'] == smb2.SMB2_FILE_INTERNAL_INFO: # No need to call queryFileInformation, we have the data here infoRecord = smb2.FileInternalInformation() infoRecord['IndexNumber'] = fileID else: infoRecord, errorCode = queryFileInformation(os.path.dirname(fileName), os.path.basename(fileName), queryInfo['FileInfoClass']) elif queryInfo['InfoType'] == smb2.SMB2_0_INFO_FILESYSTEM: infoRecord = queryFsInformation(os.path.dirname(fileName), os.path.basename(fileName), queryInfo['FileInfoClass']) elif queryInfo['InfoType'] == smb2.SMB2_0_INFO_SECURITY: # Failing for now, until we support it infoRecord = None errorCode = STATUS_ACCESS_DENIED else: smbServer.log("queryInfo not supported (%x)" % queryInfo['InfoType'], logging.ERROR) if infoRecord is not None: respSMBCommand['OutputBufferLength'] = len(infoRecord) respSMBCommand['Buffer'] = infoRecord else: errorCode = STATUS_INVALID_HANDLE else: errorCode = STATUS_SMB_BAD_TID smbServer.setConnectionData(connId, connData) return [respSMBCommand], None, errorCode @staticmethod def smb2SetInfo(connId, smbServer, recvPacket): connData = smbServer.getConnectionData(connId) respSMBCommand = smb2.SMB2SetInfo_Response() setInfo = smb2.SMB2SetInfo(recvPacket['Data']) errorCode = STATUS_SUCCESS if str(setInfo['FileID']) == '\xff'*16: # Let's take the data from the lastRequest if connData['LastRequest'].has_key('SMB2_CREATE'): fileID = connData['LastRequest']['SMB2_CREATE']['FileID'] else: fileID = str(setInfo['FileID']) else: fileID = str(setInfo['FileID']) if connData['ConnectedShares'].has_key(recvPacket['TreeID']): path = connData['ConnectedShares'][recvPacket['TreeID']]['path'] if connData['OpenedFiles'].has_key(fileID): pathName = connData['OpenedFiles'][fileID]['FileName'] if setInfo['InfoType'] == smb2.SMB2_0_INFO_FILE: # The file information is being set informationLevel = setInfo['FileInfoClass'] if informationLevel == smb2.SMB2_FILE_DISPOSITION_INFO: infoRecord = smb.SMBSetFileDispositionInfo(setInfo['Buffer']) if infoRecord['DeletePending'] > 0: # Mark this file for removal after closed connData['OpenedFiles'][fileID]['DeleteOnClose'] = True elif informationLevel == smb2.SMB2_FILE_BASIC_INFO: infoRecord = smb.SMBSetFileBasicInfo(setInfo['Buffer']) # Creation time won't be set, the other ones we play with. atime = infoRecord['LastWriteTime'] if atime == 0: atime = -1 else: atime = getUnixTime(atime) mtime = infoRecord['ChangeTime'] if mtime == 0: mtime = -1 else: mtime = getUnixTime(mtime) if atime > 0 and mtime > 0: os.utime(pathName,(atime,mtime)) elif informationLevel == smb2.SMB2_FILE_END_OF_FILE_INFO: fileHandle = connData['OpenedFiles'][fileID]['FileHandle'] infoRecord = smb.SMBSetFileEndOfFileInfo(setInfo['Buffer']) if infoRecord['EndOfFile'] > 0: os.lseek(fileHandle, infoRecord['EndOfFile']-1, 0) os.write(fileHandle, '\x00') elif informationLevel == smb2.SMB2_FILE_RENAME_INFO: renameInfo = smb2.FILE_RENAME_INFORMATION_TYPE_2(setInfo['Buffer']) newPathName = os.path.join(path,renameInfo['FileName'].decode('utf-16le').replace('\\', '/')) if renameInfo['ReplaceIfExists'] == 0 and os.path.exists(newPathName): return [smb2.SMB2Error()], None, STATUS_OBJECT_NAME_COLLISION try: os.rename(pathName,newPathName) connData['OpenedFiles'][fileID]['FileName'] = newPathName except Exception, e: smbServer.log("smb2SetInfo: %s" % e, logging.ERROR) errorCode = STATUS_ACCESS_DENIED else: smbServer.log('Unknown level for set file info! 0x%x' % informationLevel, logging.ERROR) # UNSUPPORTED errorCode = STATUS_NOT_SUPPORTED #elif setInfo['InfoType'] == smb2.SMB2_0_INFO_FILESYSTEM: # # The underlying object store information is being set. # setInfo = queryFsInformation('/', fileName, queryInfo['FileInfoClass']) #elif setInfo['InfoType'] == smb2.SMB2_0_INFO_SECURITY: # # The security information is being set. # # Failing for now, until we support it # infoRecord = None # errorCode = STATUS_ACCESS_DENIED #elif setInfo['InfoType'] == smb2.SMB2_0_INFO_QUOTA: # # The underlying object store quota information is being set. # setInfo = queryFsInformation('/', fileName, queryInfo['FileInfoClass']) else: smbServer.log("setInfo not supported (%x)" % setInfo['InfoType'], logging.ERROR) else: errorCode = STATUS_INVALID_HANDLE else: errorCode = STATUS_SMB_BAD_TID smbServer.setConnectionData(connId, connData) return [respSMBCommand], None, errorCode @staticmethod def smb2Write(connId, smbServer, recvPacket): connData = smbServer.getConnectionData(connId) respSMBCommand = smb2.SMB2Write_Response() writeRequest = smb2.SMB2Write(recvPacket['Data']) respSMBCommand['Buffer'] = '\x00' if str(writeRequest['FileID']) == '\xff'*16: # Let's take the data from the lastRequest if connData['LastRequest'].has_key('SMB2_CREATE'): fileID = connData['LastRequest']['SMB2_CREATE']['FileID'] else: fileID = str(writeRequest['FileID']) else: fileID = str(writeRequest['FileID']) if connData['OpenedFiles'].has_key(fileID): fileHandle = connData['OpenedFiles'][fileID]['FileHandle'] errorCode = STATUS_SUCCESS try: if fileHandle != PIPE_FILE_DESCRIPTOR: offset = writeRequest['Offset'] # If we're trying to write past the file end we just skip the write call (Vista does this) if os.lseek(fileHandle, 0, 2) >= offset: os.lseek(fileHandle,offset,0) os.write(fileHandle,writeRequest['Buffer']) else: sock = connData['OpenedFiles'][fileID]['Socket'] sock.send(writeRequest['Buffer']) respSMBCommand['Count'] = writeRequest['Length'] respSMBCommand['Remaining']= 0xff except Exception, e: smbServer.log('SMB2_WRITE: %s' % e, logging.ERROR) errorCode = STATUS_ACCESS_DENIED else: errorCode = STATUS_INVALID_HANDLE smbServer.setConnectionData(connId, connData) return [respSMBCommand], None, errorCode @staticmethod def smb2Read(connId, smbServer, recvPacket): connData = smbServer.getConnectionData(connId) respSMBCommand = smb2.SMB2Read_Response() readRequest = smb2.SMB2Read(recvPacket['Data']) respSMBCommand['Buffer'] = '\x00' if str(readRequest['FileID']) == '\xff'*16: # Let's take the data from the lastRequest if connData['LastRequest'].has_key('SMB2_CREATE'): fileID = connData['LastRequest']['SMB2_CREATE']['FileID'] else: fileID = str(readRequest['FileID']) else: fileID = str(readRequest['FileID']) if connData['OpenedFiles'].has_key(fileID): fileHandle = connData['OpenedFiles'][fileID]['FileHandle'] errorCode = 0 try: if fileHandle != PIPE_FILE_DESCRIPTOR: offset = readRequest['Offset'] os.lseek(fileHandle,offset,0) content = os.read(fileHandle,readRequest['Length']) else: sock = connData['OpenedFiles'][fileID]['Socket'] content = sock.recv(readRequest['Length']) respSMBCommand['DataOffset'] = 0x50 respSMBCommand['DataLength'] = len(content) respSMBCommand['DataRemaining']= 0 respSMBCommand['Buffer'] = content except Exception, e: smbServer.log('SMB2_READ: %s ' % e, logging.ERROR) errorCode = STATUS_ACCESS_DENIED else: errorCode = STATUS_INVALID_HANDLE smbServer.setConnectionData(connId, connData) return [respSMBCommand], None, errorCode @staticmethod def smb2Flush(connId, smbServer, recvPacket): connData = smbServer.getConnectionData(connId) respSMBCommand = smb2.SMB2Flush_Response() flushRequest = smb2.SMB2Flush(recvPacket['Data']) if connData['OpenedFiles'].has_key(str(flushRequest['FileID'])): fileHandle = connData['OpenedFiles'][str(flushRequest['FileID'])]['FileHandle'] errorCode = STATUS_SUCCESS try: os.fsync(fileHandle) except Exception, e: smbServer.log("SMB2_FLUSH %s" % e, logging.ERROR) errorCode = STATUS_ACCESS_DENIED else: errorCode = STATUS_INVALID_HANDLE smbServer.setConnectionData(connId, connData) return [respSMBCommand], None, errorCode @staticmethod def smb2QueryDirectory(connId, smbServer, recvPacket): connData = smbServer.getConnectionData(connId) respSMBCommand = smb2.SMB2QueryDirectory_Response() queryDirectoryRequest = smb2.SMB2QueryDirectory(recvPacket['Data']) respSMBCommand['Buffer'] = '\x00' # The server MUST locate the tree connection, as specified in section 3.3.5.2.11. if connData['ConnectedShares'].has_key(recvPacket['TreeID']) is False: return [smb2.SMB2Error()], None, STATUS_NETWORK_NAME_DELETED # Next, the server MUST locate the open for the directory to be queried # If no open is found, the server MUST fail the request with STATUS_FILE_CLOSED if str(queryDirectoryRequest['FileID']) == '\xff'*16: # Let's take the data from the lastRequest if connData['LastRequest'].has_key('SMB2_CREATE'): fileID = connData['LastRequest']['SMB2_CREATE']['FileID'] else: fileID = str(queryDirectoryRequest['FileID']) else: fileID = str(queryDirectoryRequest['FileID']) if connData['OpenedFiles'].has_key(fileID) is False: return [smb2.SMB2Error()], None, STATUS_FILE_CLOSED # If the open is not an open to a directory, the request MUST be failed # with STATUS_INVALID_PARAMETER. if os.path.isdir(connData['OpenedFiles'][fileID]['FileName']) is False: return [smb2.SMB2Error()], None, STATUS_INVALID_PARAMETER # If any other information class is specified in the FileInformationClass # field of the SMB2 QUERY_DIRECTORY Request, the server MUST fail the # operation with STATUS_INVALID_INFO_CLASS. if queryDirectoryRequest['FileInformationClass'] not in ( smb2.FILE_DIRECTORY_INFORMATION, smb2.FILE_FULL_DIRECTORY_INFORMATION, smb2.FILEID_FULL_DIRECTORY_INFORMATION, smb2.FILE_BOTH_DIRECTORY_INFORMATION, smb2.FILEID_BOTH_DIRECTORY_INFORMATION, smb2.FILENAMES_INFORMATION): return [smb2.SMB2Error()], None, STATUS_INVALID_INFO_CLASS # If SMB2_REOPEN is set in the Flags field of the SMB2 QUERY_DIRECTORY # Request, the server SHOULD<326> set Open.EnumerationLocation to 0 # and Open.EnumerationSearchPattern to an empty string. if queryDirectoryRequest['Flags'] & smb2.SMB2_REOPEN: connData['OpenedFiles'][fileID]['Open']['EnumerationLocation'] = 0 connData['OpenedFiles'][fileID]['Open']['EnumerationSearchPattern'] = '' # If SMB2_RESTART_SCANS is set in the Flags field of the SMB2 # QUERY_DIRECTORY Request, the server MUST set # Open.EnumerationLocation to 0. if queryDirectoryRequest['Flags'] & smb2.SMB2_RESTART_SCANS: connData['OpenedFiles'][fileID]['Open']['EnumerationLocation'] = 0 # If Open.EnumerationLocation is 0 and Open.EnumerationSearchPattern # is an empty string, then Open.EnumerationSearchPattern MUST be set # to the search pattern specified in the SMB2 QUERY_DIRECTORY by # FileNameOffset and FileNameLength. If FileNameLength is 0, the server # SHOULD<327> set Open.EnumerationSearchPattern as "*" to search all entries. pattern = queryDirectoryRequest['Buffer'].decode('utf-16le') if connData['OpenedFiles'][fileID]['Open']['EnumerationLocation'] == 0 and \ connData['OpenedFiles'][fileID]['Open']['EnumerationSearchPattern'] == '': if pattern == '': pattern = '*' connData['OpenedFiles'][fileID]['Open']['EnumerationSearchPattern'] = pattern # If SMB2_INDEX_SPECIFIED is set and FileNameLength is not zero, # the server MUST set Open.EnumerationSearchPattern to the search pattern # specified in the request by FileNameOffset and FileNameLength. if queryDirectoryRequest['Flags'] & smb2.SMB2_INDEX_SPECIFIED and \ queryDirectoryRequest['FileNameLength'] > 0: connData['OpenedFiles'][fileID]['Open']['EnumerationSearchPattern'] = pattern pathName = os.path.join(os.path.normpath(connData['OpenedFiles'][fileID]['FileName']),pattern) searchResult, searchCount, errorCode = findFirst2(os.path.dirname(pathName), os.path.basename(pathName), queryDirectoryRequest['FileInformationClass'], smb.ATTR_DIRECTORY, isSMB2 = True ) if errorCode != STATUS_SUCCESS: return [smb2.SMB2Error()], None, errorCode if searchCount > 2 and pattern == '*': # strip . and .. searchCount -= 2 searchResult = searchResult[2:] if searchCount == 0 and connData['OpenedFiles'][fileID]['Open']['EnumerationLocation'] == 0: return [smb2.SMB2Error()], None, STATUS_NO_SUCH_FILE if connData['OpenedFiles'][fileID]['Open']['EnumerationLocation'] < 0: return [smb2.SMB2Error()], None, STATUS_NO_MORE_FILES totalData = 0 respData = '' for nItem in range(connData['OpenedFiles'][fileID]['Open']['EnumerationLocation'], searchCount): connData['OpenedFiles'][fileID]['Open']['EnumerationLocation'] += 1 if queryDirectoryRequest['Flags'] & smb2.SL_RETURN_SINGLE_ENTRY: # If single entry is requested we must clear the NextEntryOffset searchResult[nItem]['NextEntryOffset'] = 0 data = searchResult[nItem].getData() lenData = len(data) padLen = (8-(lenData % 8)) %8 if (totalData+lenData) >= queryDirectoryRequest['OutputBufferLength']: connData['OpenedFiles'][fileID]['Open']['EnumerationLocation'] -= 1 break else: respData += data + '\x00'*padLen totalData += lenData + padLen if queryDirectoryRequest['Flags'] & smb2.SL_RETURN_SINGLE_ENTRY: break if connData['OpenedFiles'][fileID]['Open']['EnumerationLocation'] >= searchCount: connData['OpenedFiles'][fileID]['Open']['EnumerationLocation'] = -1 respSMBCommand['OutputBufferOffset'] = 0x48 respSMBCommand['OutputBufferLength'] = totalData respSMBCommand['Buffer'] = respData smbServer.setConnectionData(connId, connData) return [respSMBCommand], None, errorCode @staticmethod def smb2ChangeNotify(connId, smbServer, recvPacket): return [smb2.SMB2Error()], None, STATUS_NOT_SUPPORTED @staticmethod def smb2Echo(connId, smbServer, recvPacket): respSMBCommand = smb2.SMB2Echo_Response() return [respSMBCommand], None, STATUS_SUCCESS @staticmethod def smb2TreeDisconnect(connId, smbServer, recvPacket): connData = smbServer.getConnectionData(connId) respSMBCommand = smb2.SMB2TreeDisconnect_Response() if connData['ConnectedShares'].has_key(recvPacket['TreeID']): smbServer.log("Disconnecting Share(%d:%s)" % (recvPacket['TreeID'],connData['ConnectedShares'][recvPacket['TreeID']]['shareName'])) del(connData['ConnectedShares'][recvPacket['TreeID']]) errorCode = STATUS_SUCCESS else: # STATUS_SMB_BAD_TID errorCode = STATUS_SMB_BAD_TID smbServer.setConnectionData(connId, connData) return [respSMBCommand], None, errorCode @staticmethod def smb2Logoff(connId, smbServer, recvPacket): connData = smbServer.getConnectionData(connId) respSMBCommand = smb2.SMB2Logoff_Response() if recvPacket['SessionID'] != connData['Uid']: # STATUS_SMB_BAD_UID errorCode = STATUS_SMB_BAD_UID else: errorCode = STATUS_SUCCESS connData['Uid'] = 0 smbServer.setConnectionData(connId, connData) return [respSMBCommand], None, errorCode @staticmethod def smb2Ioctl(connId, smbServer, recvPacket): connData = smbServer.getConnectionData(connId) respSMBCommand = smb2.SMB2Ioctl_Response() ioctlRequest = smb2.SMB2Ioctl(recvPacket['Data']) ioctls = smbServer.getIoctls() if ioctls.has_key(ioctlRequest['CtlCode']): outputData, errorCode = ioctls[ioctlRequest['CtlCode']](connId, smbServer, ioctlRequest) if errorCode == STATUS_SUCCESS: respSMBCommand['CtlCode'] = ioctlRequest['CtlCode'] respSMBCommand['FileID'] = ioctlRequest['FileID'] respSMBCommand['InputOffset'] = 0 respSMBCommand['InputCount'] = 0 respSMBCommand['OutputOffset'] = 0x70 respSMBCommand['OutputCount'] = len(outputData) respSMBCommand['Flags'] = 0 respSMBCommand['Buffer'] = outputData else: respSMBCommand = outputData else: smbServer.log("Ioctl not implemented command: 0x%x" % ioctlRequest['CtlCode'],logging.DEBUG) errorCode = STATUS_INVALID_DEVICE_REQUEST respSMBCommand = smb2.SMB2Error() smbServer.setConnectionData(connId, connData) return [respSMBCommand], None, errorCode @staticmethod def smb2Lock(connId, smbServer, recvPacket): connData = smbServer.getConnectionData(connId) respSMBCommand = smb2.SMB2Lock_Response() # I'm actually doing nothing.. just make MacOS happy ;) errorCode = STATUS_SUCCESS smbServer.setConnectionData(connId, connData) return [respSMBCommand], None, errorCode @staticmethod def smb2Cancel(connId, smbServer, recvPacket): # I'm actually doing nothing return [smb2.SMB2Error()], None, STATUS_CANCELLED @staticmethod def default(connId, smbServer, recvPacket): # By default we return an SMB Packet with error not implemented smbServer.log("Not implemented command: 0x%x" % recvPacket['Command'],logging.DEBUG) return [smb2.SMB2Error()], None, STATUS_NOT_SUPPORTED class Ioctls: @staticmethod def fsctlDfsGetReferrals(connId, smbServer, ioctlRequest): return smb2.SMB2Error(), STATUS_FS_DRIVER_REQUIRED @staticmethod def fsctlPipeTransceive(connId, smbServer, ioctlRequest): connData = smbServer.getConnectionData(connId) ioctlResponse = '' if connData['OpenedFiles'].has_key(str(ioctlRequest['FileID'])): fileHandle = connData['OpenedFiles'][str(ioctlRequest['FileID'])]['FileHandle'] errorCode = STATUS_SUCCESS try: if fileHandle != PIPE_FILE_DESCRIPTOR: errorCode = STATUS_INVALID_DEVICE_REQUEST else: sock = connData['OpenedFiles'][str(ioctlRequest['FileID'])]['Socket'] sock.sendall(ioctlRequest['Buffer']) ioctlResponse = sock.recv(ioctlRequest['MaxOutputResponse']) except Exception, e: smbServer.log('fsctlPipeTransceive: %s ' % e, logging.ERROR) errorCode = STATUS_ACCESS_DENIED else: errorCode = STATUS_INVALID_DEVICE_REQUEST smbServer.setConnectionData(connId, connData) return ioctlResponse, errorCode @staticmethod def fsctlValidateNegotiateInfo(connId, smbServer, ioctlRequest): connData = smbServer.getConnectionData(connId) errorCode = STATUS_SUCCESS validateNegotiateInfo = smb2.VALIDATE_NEGOTIATE_INFO(ioctlRequest['Buffer']) validateNegotiateInfo['Capabilities'] = 0 validateNegotiateInfo['Guid'] = 'A'*16 validateNegotiateInfo['SecurityMode'] = 1 validateNegotiateInfo['Dialects'] = (smb2.SMB2_DIALECT_002,) smbServer.setConnectionData(connId, connData) return validateNegotiateInfo.getData(), errorCode class SMBSERVERHandler(SocketServer.BaseRequestHandler): def __init__(self, request, client_address, server, select_poll = False): self.__SMB = server self.__ip, self.__port = client_address self.__request = request self.__connId = threading.currentThread().getName() self.__timeOut = 60*5 self.__select_poll = select_poll #self.__connId = os.getpid() SocketServer.BaseRequestHandler.__init__(self, request, client_address, server) def handle(self): self.__SMB.log("Incoming connection (%s,%d)" % (self.__ip, self.__port)) self.__SMB.addConnection(self.__connId, self.__ip, self.__port) while True: try: # Firt of all let's get the NETBIOS packet session = nmb.NetBIOSTCPSession(self.__SMB.getServerName(),'HOST', self.__ip, sess_port = self.__port, sock = self.__request, select_poll = self.__select_poll) try: p = session.recv_packet(self.__timeOut) except nmb.NetBIOSTimeout: raise except nmb.NetBIOSError: break if p.get_type() == nmb.NETBIOS_SESSION_REQUEST: # Someone is requesting a session, we're gonna accept them all :) _, rn, my = p.get_trailer().split(' ') remote_name = nmb.decode_name('\x20'+rn) myname = nmb.decode_name('\x20'+my) self.__SMB.log("NetBIOS Session request (%s,%s,%s)" % (self.__ip, remote_name[1].strip(), myname[1])) r = nmb.NetBIOSSessionPacket() r.set_type(nmb.NETBIOS_SESSION_POSITIVE_RESPONSE) r.set_trailer(p.get_trailer()) self.__request.send(r.rawData()) else: resp = self.__SMB.processRequest(self.__connId, p.get_trailer()) # Send all the packets recevied. Except for big transactions this should be # a single packet for i in resp: session.send_packet(str(i)) except Exception, e: self.__SMB.log("Handle: %s" % e) #import traceback #traceback.print_exc() break def finish(self): # Thread/process is dying, we should tell the main SMB thread to remove all this thread data self.__SMB.log("Closing down connection (%s,%d)" % (self.__ip, self.__port)) self.__SMB.removeConnection(self.__connId) return SocketServer.BaseRequestHandler.finish(self) class SMBSERVER(SocketServer.ThreadingMixIn, SocketServer.TCPServer): #class SMBSERVER(SocketServer.ForkingMixIn, SocketServer.TCPServer): def __init__(self, server_address, handler_class=SMBSERVERHandler, config_parser = None): SocketServer.TCPServer.allow_reuse_address = True SocketServer.TCPServer.__init__(self, server_address, handler_class) # Server name and OS to be presented whenever is necessary self.__serverName = '' self.__serverOS = '' self.__serverDomain = '' self.__challenge = '' self.__log = None # Our ConfigParser data self.__serverConfig = config_parser # Our credentials to be used during the server's lifetime self.__credentials = {} # Our log file self.__logFile = '' # Registered Named Pipes, format is PipeName,Socket self.__registeredNamedPipes = {} # JTR dump path self.__jtr_dump_path = '' # SMB2 Support flag = default not active self.__SMB2Support = False # Our list of commands we will answer, by default the NOT IMPLEMENTED one self.__smbCommandsHandler = SMBCommands() self.__smbTrans2Handler = TRANS2Commands() self.__smbTransHandler = TRANSCommands() self.__smbNTTransHandler = NTTRANSCommands() self.__smb2CommandsHandler = SMB2Commands() self.__IoctlHandler = Ioctls() self.__smbNTTransCommands = { # NT IOCTL, can't find doc for this 0xff :self.__smbNTTransHandler.default } self.__smbTransCommands = { '\\PIPE\\LANMAN' :self.__smbTransHandler.lanMan, smb.SMB.TRANS_TRANSACT_NMPIPE :self.__smbTransHandler.transactNamedPipe, } self.__smbTrans2Commands = { smb.SMB.TRANS2_FIND_FIRST2 :self.__smbTrans2Handler.findFirst2, smb.SMB.TRANS2_FIND_NEXT2 :self.__smbTrans2Handler.findNext2, smb.SMB.TRANS2_QUERY_FS_INFORMATION :self.__smbTrans2Handler.queryFsInformation, smb.SMB.TRANS2_QUERY_PATH_INFORMATION :self.__smbTrans2Handler.queryPathInformation, smb.SMB.TRANS2_QUERY_FILE_INFORMATION :self.__smbTrans2Handler.queryFileInformation, smb.SMB.TRANS2_SET_FILE_INFORMATION :self.__smbTrans2Handler.setFileInformation, smb.SMB.TRANS2_SET_PATH_INFORMATION :self.__smbTrans2Handler.setPathInformation } self.__smbCommands = { #smb.SMB.SMB_COM_FLUSH: self.__smbCommandsHandler.smbComFlush, smb.SMB.SMB_COM_CREATE_DIRECTORY: self.__smbCommandsHandler.smbComCreateDirectory, smb.SMB.SMB_COM_DELETE_DIRECTORY: self.__smbCommandsHandler.smbComDeleteDirectory, smb.SMB.SMB_COM_RENAME: self.__smbCommandsHandler.smbComRename, smb.SMB.SMB_COM_DELETE: self.__smbCommandsHandler.smbComDelete, smb.SMB.SMB_COM_NEGOTIATE: self.__smbCommandsHandler.smbComNegotiate, smb.SMB.SMB_COM_SESSION_SETUP_ANDX: self.__smbCommandsHandler.smbComSessionSetupAndX, smb.SMB.SMB_COM_LOGOFF_ANDX: self.__smbCommandsHandler.smbComLogOffAndX, smb.SMB.SMB_COM_TREE_CONNECT_ANDX: self.__smbCommandsHandler.smbComTreeConnectAndX, smb.SMB.SMB_COM_TREE_DISCONNECT: self.__smbCommandsHandler.smbComTreeDisconnect, smb.SMB.SMB_COM_ECHO: self.__smbCommandsHandler.smbComEcho, smb.SMB.SMB_COM_QUERY_INFORMATION: self.__smbCommandsHandler.smbQueryInformation, smb.SMB.SMB_COM_TRANSACTION2: self.__smbCommandsHandler.smbTransaction2, smb.SMB.SMB_COM_TRANSACTION: self.__smbCommandsHandler.smbTransaction, # Not needed for now smb.SMB.SMB_COM_NT_TRANSACT: self.__smbCommandsHandler.smbNTTransact, smb.SMB.SMB_COM_QUERY_INFORMATION_DISK: self.__smbCommandsHandler.smbQueryInformationDisk, smb.SMB.SMB_COM_OPEN_ANDX: self.__smbCommandsHandler.smbComOpenAndX, smb.SMB.SMB_COM_QUERY_INFORMATION2: self.__smbCommandsHandler.smbComQueryInformation2, smb.SMB.SMB_COM_READ_ANDX: self.__smbCommandsHandler.smbComReadAndX, smb.SMB.SMB_COM_READ: self.__smbCommandsHandler.smbComRead, smb.SMB.SMB_COM_WRITE_ANDX: self.__smbCommandsHandler.smbComWriteAndX, smb.SMB.SMB_COM_WRITE: self.__smbCommandsHandler.smbComWrite, smb.SMB.SMB_COM_CLOSE: self.__smbCommandsHandler.smbComClose, smb.SMB.SMB_COM_LOCKING_ANDX: self.__smbCommandsHandler.smbComLockingAndX, smb.SMB.SMB_COM_NT_CREATE_ANDX: self.__smbCommandsHandler.smbComNtCreateAndX, 0xFF: self.__smbCommandsHandler.default } self.__smb2Ioctls = { smb2.FSCTL_DFS_GET_REFERRALS: self.__IoctlHandler.fsctlDfsGetReferrals, # smb2.FSCTL_PIPE_PEEK: self.__IoctlHandler.fsctlPipePeek, # smb2.FSCTL_PIPE_WAIT: self.__IoctlHandler.fsctlPipeWait, smb2.FSCTL_PIPE_TRANSCEIVE: self.__IoctlHandler.fsctlPipeTransceive, # smb2.FSCTL_SRV_COPYCHUNK: self.__IoctlHandler.fsctlSrvCopyChunk, # smb2.FSCTL_SRV_ENUMERATE_SNAPSHOTS: self.__IoctlHandler.fsctlSrvEnumerateSnapshots, # smb2.FSCTL_SRV_REQUEST_RESUME_KEY: self.__IoctlHandler.fsctlSrvRequestResumeKey, # smb2.FSCTL_SRV_READ_HASH: self.__IoctlHandler.fsctlSrvReadHash, # smb2.FSCTL_SRV_COPYCHUNK_WRITE: self.__IoctlHandler.fsctlSrvCopyChunkWrite, # smb2.FSCTL_LMR_REQUEST_RESILIENCY: self.__IoctlHandler.fsctlLmrRequestResiliency, # smb2.FSCTL_QUERY_NETWORK_INTERFACE_INFO: self.__IoctlHandler.fsctlQueryNetworkInterfaceInfo, # smb2.FSCTL_SET_REPARSE_POINT: self.__IoctlHandler.fsctlSetReparsePoint, # smb2.FSCTL_DFS_GET_REFERRALS_EX: self.__IoctlHandler.fsctlDfsGetReferralsEx, # smb2.FSCTL_FILE_LEVEL_TRIM: self.__IoctlHandler.fsctlFileLevelTrim, smb2.FSCTL_VALIDATE_NEGOTIATE_INFO: self.__IoctlHandler.fsctlValidateNegotiateInfo, } self.__smb2Commands = { smb2.SMB2_NEGOTIATE: self.__smb2CommandsHandler.smb2Negotiate, smb2.SMB2_SESSION_SETUP: self.__smb2CommandsHandler.smb2SessionSetup, smb2.SMB2_LOGOFF: self.__smb2CommandsHandler.smb2Logoff, smb2.SMB2_TREE_CONNECT: self.__smb2CommandsHandler.smb2TreeConnect, smb2.SMB2_TREE_DISCONNECT: self.__smb2CommandsHandler.smb2TreeDisconnect, smb2.SMB2_CREATE: self.__smb2CommandsHandler.smb2Create, smb2.SMB2_CLOSE: self.__smb2CommandsHandler.smb2Close, smb2.SMB2_FLUSH: self.__smb2CommandsHandler.smb2Flush, smb2.SMB2_READ: self.__smb2CommandsHandler.smb2Read, smb2.SMB2_WRITE: self.__smb2CommandsHandler.smb2Write, smb2.SMB2_LOCK: self.__smb2CommandsHandler.smb2Lock, smb2.SMB2_IOCTL: self.__smb2CommandsHandler.smb2Ioctl, smb2.SMB2_CANCEL: self.__smb2CommandsHandler.smb2Cancel, smb2.SMB2_ECHO: self.__smb2CommandsHandler.smb2Echo, smb2.SMB2_QUERY_DIRECTORY: self.__smb2CommandsHandler.smb2QueryDirectory, smb2.SMB2_CHANGE_NOTIFY: self.__smb2CommandsHandler.smb2ChangeNotify, smb2.SMB2_QUERY_INFO: self.__smb2CommandsHandler.smb2QueryInfo, smb2.SMB2_SET_INFO: self.__smb2CommandsHandler.smb2SetInfo, # smb2.SMB2_OPLOCK_BREAK: self.__smb2CommandsHandler.smb2SessionSetup, 0xFF: self.__smb2CommandsHandler.default } # List of active connections self.__activeConnections = {} def getIoctls(self): return self.__smb2Ioctls def getCredentials(self): return self.__credentials def removeConnection(self, name): try: del(self.__activeConnections[name]) except: pass self.log("Remaining connections %s" % self.__activeConnections.keys()) def addConnection(self, name, ip, port): self.__activeConnections[name] = {} # Let's init with some know stuff we will need to have # TODO: Document what's in there #print "Current Connections", self.__activeConnections.keys() self.__activeConnections[name]['PacketNum'] = 0 self.__activeConnections[name]['ClientIP'] = ip self.__activeConnections[name]['ClientPort'] = port self.__activeConnections[name]['Uid'] = 0 self.__activeConnections[name]['ConnectedShares'] = {} self.__activeConnections[name]['OpenedFiles'] = {} # SID results for findfirst2 self.__activeConnections[name]['SIDs'] = {} self.__activeConnections[name]['LastRequest'] = {} def getActiveConnections(self): return self.__activeConnections def setConnectionData(self, connId, data): self.__activeConnections[connId] = data #print "setConnectionData" #print self.__activeConnections def getConnectionData(self, connId, checkStatus = True): conn = self.__activeConnections[connId] if checkStatus is True: if conn.has_key('Authenticated') is not True: # Can't keep going further raise Exception("User not Authenticated!") return conn def getRegisteredNamedPipes(self): return self.__registeredNamedPipes def registerNamedPipe(self, pipeName, address): self.__registeredNamedPipes[unicode(pipeName)] = address return True def unregisterNamedPipe(self, pipeName): if self.__registeredNamedPipes.has_key(pipeName): del(self.__registeredNamedPipes[unicode(pipeName)]) return True return False def unregisterTransaction(self, transCommand): if self.__smbTransCommands.has_key(transCommand): del(self.__smbTransCommands[transCommand]) def hookTransaction(self, transCommand, callback): # If you call this function, callback will replace # the current Transaction sub command. # (don't get confused with the Transaction smbCommand) # If the transaction sub command doesn't not exist, it is added # If the transaction sub command exists, it returns the original function # replaced # # callback MUST be declared as: # callback(connId, smbServer, recvPacket, parameters, data, maxDataCount=0) # # WHERE: # # connId : the connection Id, used to grab/update information about # the current connection # smbServer : the SMBServer instance available for you to ask # configuration data # recvPacket : the full SMBPacket that triggered this command # parameters : the transaction parameters # data : the transaction data # maxDataCount: the max amount of data that can be transfered agreed # with the client # # and MUST return: # respSetup, respParameters, respData, errorCode # # WHERE: # # respSetup: the setup response of the transaction # respParameters: the parameters response of the transaction # respData: the data reponse of the transaction # errorCode: the NT error code if self.__smbTransCommands.has_key(transCommand): originalCommand = self.__smbTransCommands[transCommand] else: originalCommand = None self.__smbTransCommands[transCommand] = callback return originalCommand def unregisterTransaction2(self, transCommand): if self.__smbTrans2Commands.has_key(transCommand): del(self.__smbTrans2Commands[transCommand]) def hookTransaction2(self, transCommand, callback): # Here we should add to __smbTrans2Commands # Same description as Transaction if self.__smbTrans2Commands.has_key(transCommand): originalCommand = self.__smbTrans2Commands[transCommand] else: originalCommand = None self.__smbTrans2Commands[transCommand] = callback return originalCommand def unregisterNTTransaction(self, transCommand): if self.__smbNTTransCommands.has_key(transCommand): del(self.__smbNTTransCommands[transCommand]) def hookNTTransaction(self, transCommand, callback): # Here we should add to __smbNTTransCommands # Same description as Transaction if self.__smbNTTransCommands.has_key(transCommand): originalCommand = self.__smbNTTransCommands[transCommand] else: originalCommand = None self.__smbNTTransCommands[transCommand] = callback return originalCommand def unregisterSmbCommand(self, smbCommand): if self.__smbCommands.has_key(smbCommand): del(self.__smbCommands[smbCommand]) def hookSmbCommand(self, smbCommand, callback): # Here we should add to self.__smbCommands # If you call this function, callback will replace # the current smbCommand. # If smbCommand doesn't not exist, it is added # If SMB command exists, it returns the original function replaced # # callback MUST be declared as: # callback(connId, smbServer, SMBCommand, recvPacket) # # WHERE: # # connId : the connection Id, used to grab/update information about # the current connection # smbServer : the SMBServer instance available for you to ask # configuration data # SMBCommand: the SMBCommand itself, with its data and parameters. # Check smb.py:SMBCommand() for a reference # recvPacket: the full SMBPacket that triggered this command # # and MUST return: # , , errorCode # has higher preference over commands, in case you # want to change the whole packet # errorCode: the NT error code # # For SMB_COM_TRANSACTION2, SMB_COM_TRANSACTION and SMB_COM_NT_TRANSACT # the callback function is slightly different: # # callback(connId, smbServer, SMBCommand, recvPacket, transCommands) # # WHERE: # # transCommands: a list of transaction subcommands already registered # if self.__smbCommands.has_key(smbCommand): originalCommand = self.__smbCommands[smbCommand] else: originalCommand = None self.__smbCommands[smbCommand] = callback return originalCommand def unregisterSmb2Command(self, smb2Command): if self.__smb2Commands.has_key(smb2Command): del(self.__smb2Commands[smb2Command]) def hookSmb2Command(self, smb2Command, callback): if self.__smb2Commands.has_key(smb2Command): originalCommand = self.__smb2Commands[smb2Command] else: originalCommand = None self.__smb2Commands[smb2Command] = callback return originalCommand def log(self, msg, level=logging.INFO): self.__log.log(level,msg) def getServerName(self): return self.__serverName def getServerOS(self): return self.__serverOS def getServerDomain(self): return self.__serverDomain def getSMBChallenge(self): return self.__challenge def getServerConfig(self): return self.__serverConfig def setServerConfig(self, config): self.__serverConfig = config def getJTRdumpPath(self): return self.__jtr_dump_path def verify_request(self, request, client_address): # TODO: Control here the max amount of processes we want to launch # returning False, closes the connection return True def processRequest(self, connId, data): # TODO: Process batched commands. isSMB2 = False SMBCommand = None try: packet = smb.NewSMBPacket(data = data) SMBCommand = smb.SMBCommand(packet['Data'][0]) except: # Maybe a SMB2 packet? packet = smb2.SMB2Packet(data = data) isSMB2 = True # We might have compound requests compoundedPacketsResponse = [] compoundedPackets = [] try: # Search out list of implemented commands # We provide them with: # connId : representing the data for this specific connection # self : the SMBSERVER if they want to ask data to it # SMBCommand : the SMBCommand they are expecting to process # packet : the received packet itself, in case they need more data than the actual command # Only for Transactions # transCommand: a list of transaction subcommands # We expect to get: # respCommands: a list of answers for the commands processed # respPacket : if the commands chose to directly craft packet/s, we use this and not the previous # this MUST be a list # errorCode : self explanatory if isSMB2 is False: if packet['Command'] == smb.SMB.SMB_COM_TRANSACTION2: respCommands, respPackets, errorCode = self.__smbCommands[packet['Command']]( connId, self, SMBCommand, packet, self.__smbTrans2Commands) elif packet['Command'] == smb.SMB.SMB_COM_NT_TRANSACT: respCommands, respPackets, errorCode = self.__smbCommands[packet['Command']]( connId, self, SMBCommand, packet, self.__smbNTTransCommands) elif packet['Command'] == smb.SMB.SMB_COM_TRANSACTION: respCommands, respPackets, errorCode = self.__smbCommands[packet['Command']]( connId, self, SMBCommand, packet, self.__smbTransCommands) else: if self.__smbCommands.has_key(packet['Command']): if self.__SMB2Support is True: if packet['Command'] == smb.SMB.SMB_COM_NEGOTIATE: try: respCommands, respPackets, errorCode = self.__smb2Commands[smb2.SMB2_NEGOTIATE](connId, self, packet, True) isSMB2 = True except Exception, e: self.log('SMB2_NEGOTIATE: %s' % e, logging.ERROR) # If something went wrong, let's fallback to SMB1 respCommands, respPackets, errorCode = self.__smbCommands[packet['Command']]( connId, self, SMBCommand, packet) #self.__SMB2Support = False pass else: respCommands, respPackets, errorCode = self.__smbCommands[packet['Command']]( connId, self, SMBCommand, packet) else: respCommands, respPackets, errorCode = self.__smbCommands[packet['Command']]( connId, self, SMBCommand, packet) else: respCommands, respPackets, errorCode = self.__smbCommands[255](connId, self, SMBCommand, packet) compoundedPacketsResponse.append((respCommands, respPackets, errorCode)) compoundedPackets.append(packet) else: done = False while not done: if self.__smb2Commands.has_key(packet['Command']): if self.__SMB2Support is True: respCommands, respPackets, errorCode = self.__smb2Commands[packet['Command']]( connId, self, packet) else: respCommands, respPackets, errorCode = self.__smb2Commands[255](connId, self, packet) else: respCommands, respPackets, errorCode = self.__smb2Commands[255](connId, self, packet) # Let's store the result for this compounded packet compoundedPacketsResponse.append((respCommands, respPackets, errorCode)) compoundedPackets.append(packet) if packet['NextCommand'] != 0: data = data[packet['NextCommand']:] packet = smb2.SMB2Packet(data = data) else: done = True except Exception, e: #import traceback #traceback.print_exc() # Something wen't wrong, defaulting to Bad user ID self.log('processRequest (0x%x,%s)' % (packet['Command'],e), logging.ERROR) raise # We prepare the response packet to commands don't need to bother about that. connData = self.getConnectionData(connId, False) # Force reconnection loop.. This is just a test.. client will send me back credentials :) #connData['PacketNum'] += 1 #if connData['PacketNum'] == 15: # connData['PacketNum'] = 0 # # Something wen't wrong, defaulting to Bad user ID # self.log('Sending BAD USER ID!', logging.ERROR) # #raise # packet['Flags1'] |= smb.SMB.FLAGS1_REPLY # packet['Flags2'] = 0 # errorCode = STATUS_SMB_BAD_UID # packet['ErrorCode'] = errorCode >> 16 # packet['ErrorClass'] = errorCode & 0xff # return [packet] self.setConnectionData(connId, connData) packetsToSend = [] for packetNum in range(len(compoundedPacketsResponse)): respCommands, respPackets, errorCode = compoundedPacketsResponse[packetNum] packet = compoundedPackets[packetNum] if respPackets is None: for respCommand in respCommands: if isSMB2 is False: respPacket = smb.NewSMBPacket() respPacket['Flags1'] = smb.SMB.FLAGS1_REPLY # TODO this should come from a per session configuration respPacket['Flags2'] = smb.SMB.FLAGS2_EXTENDED_SECURITY | smb.SMB.FLAGS2_NT_STATUS | smb.SMB.FLAGS2_LONG_NAMES | packet['Flags2'] & smb.SMB.FLAGS2_UNICODE #respPacket['Flags2'] = smb.SMB.FLAGS2_EXTENDED_SECURITY | smb.SMB.FLAGS2_NT_STATUS | smb.SMB.FLAGS2_LONG_NAMES #respPacket['Flags1'] = 0x98 #respPacket['Flags2'] = 0xc807 respPacket['Tid'] = packet['Tid'] respPacket['Mid'] = packet['Mid'] respPacket['Pid'] = packet['Pid'] respPacket['Uid'] = connData['Uid'] respPacket['ErrorCode'] = errorCode >> 16 respPacket['_reserved'] = errorCode >> 8 & 0xff respPacket['ErrorClass'] = errorCode & 0xff respPacket.addCommand(respCommand) packetsToSend.append(respPacket) else: respPacket = smb2.SMB2Packet() respPacket['Flags'] = smb2.SMB2_FLAGS_SERVER_TO_REDIR if packetNum > 0: respPacket['Flags'] |= smb2.SMB2_FLAGS_RELATED_OPERATIONS respPacket['Status'] = errorCode respPacket['CreditRequestResponse'] = packet['CreditRequestResponse'] respPacket['Command'] = packet['Command'] respPacket['CreditCharge'] = packet['CreditCharge'] #respPacket['CreditCharge'] = 0 respPacket['Reserved'] = packet['Reserved'] respPacket['SessionID'] = connData['Uid'] respPacket['MessageID'] = packet['MessageID'] respPacket['TreeID'] = packet['TreeID'] respPacket['Data'] = str(respCommand) packetsToSend.append(respPacket) else: # The SMBCommand took care of building the packet packetsToSend = respPackets if isSMB2 is True: # Let's build a compound answer finalData = '' i = 0 for i in range(len(packetsToSend)-1): packet = packetsToSend[i] # Align to 8-bytes padLen = (8 - (len(packet) % 8) ) % 8 packet['NextCommand'] = len(packet) + padLen finalData += str(packet) + padLen*'\x00' # Last one finalData += str(packetsToSend[len(packetsToSend)-1]) packetsToSend = [finalData] # We clear the compound requests connData['LastRequest'] = {} return packetsToSend def processConfigFile(self, configFile = None): # TODO: Do a real config parser if self.__serverConfig is None: if configFile is None: configFile = 'smb.conf' self.__serverConfig = ConfigParser.ConfigParser() self.__serverConfig.read(configFile) self.__serverName = self.__serverConfig.get('global','server_name') self.__serverOS = self.__serverConfig.get('global','server_os') self.__serverDomain = self.__serverConfig.get('global','server_domain') self.__logFile = self.__serverConfig.get('global','log_file') if self.__serverConfig.has_option('global', 'challenge'): self.__challenge = self.__serverConfig.get('global', 'challenge') else: self.__challenge = 'A'*8 if self.__serverConfig.has_option("global", "jtr_dump_path"): self.__jtr_dump_path = self.__serverConfig.get("global", "jtr_dump_path") if self.__serverConfig.has_option("global", "SMB2Support"): self.__SMB2Support = self.__serverConfig.getboolean("global","SMB2Support") else: self.__SMB2Support = False if self.__logFile != 'None': logging.basicConfig(filename = self.__logFile, level = logging.DEBUG, format="%(asctime)s: %(levelname)s: %(message)s", datefmt = '%m/%d/%Y %I:%M:%S %p') self.__log = LOG # Process the credentials credentials_fname = self.__serverConfig.get('global','credentials_file') if credentials_fname is not "": cred = open(credentials_fname) line = cred.readline() while line: name, domain, lmhash, nthash = line.split(':') self.__credentials[name] = (domain, lmhash, nthash.strip('\r\n')) line = cred.readline() cred.close() self.log('Config file parsed') # For windows platforms, opening a directory is not an option, so we set a void FD VOID_FILE_DESCRIPTOR = -1 PIPE_FILE_DESCRIPTOR = -2