curl/tests/python_dependencies/impacket/smbserver.py

4169 lines
190 KiB
Python

# 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('<H',parameters[:2])[0] == 0:
# NetShareEnum Request
netShareEnum = smb.SMBNetShareEnum(parameters)
if netShareEnum['InfoLevel'] == 1:
shares = getShares(connId, smbServer)
respParameters = smb.SMBNetShareEnumResponse()
respParameters['EntriesReturned'] = len(shares)
respParameters['EntriesAvailable'] = len(shares)
tailData = ''
for i in shares:
# NetShareInfo1 len == 20
entry = smb.NetShareInfo1()
entry['NetworkName'] = i + '\x00'*(13-len(i))
entry['Type'] = int(shares[i]['share type'])
# (beto) If offset == 0 it crashes explorer.exe on windows 7
entry['RemarkOffsetLow'] = 20 * len(shares) + len(tailData)
respData += entry.getData()
if shares[i].has_key('comment'):
tailData += shares[i]['comment'] + '\x00'
else:
tailData += '\x00'
respData += tailData
else:
# We don't support other info levels
errorCode = STATUS_NOT_SUPPORTED
elif struct.unpack('<H',parameters[:2])[0] == 13:
# NetrServerGetInfo Request
respParameters = smb.SMBNetServerGetInfoResponse()
netServerInfo = smb.SMBNetServerInfo1()
netServerInfo['ServerName'] = smbServer.getServerName()
respData = str(netServerInfo)
respParameters['TotalBytesAvailable'] = len(respData)
elif struct.unpack('<H',parameters[:2])[0] == 1:
# NetrShareGetInfo Request
request = smb.SMBNetShareGetInfo(parameters)
respParameters = smb.SMBNetShareGetInfoResponse()
shares = getShares(connId, smbServer)
share = shares[request['ShareName'].upper()]
shareInfo = smb.NetShareInfo1()
shareInfo['NetworkName'] = request['ShareName'].upper() + '\x00'
shareInfo['Type'] = int(share['share type'])
respData = shareInfo.getData()
if share.has_key('comment'):
shareInfo['RemarkOffsetLow'] = len(respData)
respData += share['comment'] + '\x00'
respParameters['TotalBytesAvailable'] = len(respData)
else:
# We don't know how to handle anything else
errorCode = STATUS_NOT_SUPPORTED
smbServer.setConnectionData(connId, connData)
return respSetup, respParameters, respData, errorCode
@staticmethod
def transactNamedPipe(connId, smbServer, recvPacket, parameters, data, maxDataCount = 0):
connData = smbServer.getConnectionData(connId)
respSetup = ''
respParameters = ''
respData = ''
errorCode = STATUS_SUCCESS
SMBCommand = smb.SMBCommand(recvPacket['Data'][0])
transParameters= smb.SMBTransaction_Parameters(SMBCommand['Parameters'])
# Extract the FID
fid = struct.unpack('<H', transParameters['Setup'][2:])[0]
if connData['OpenedFiles'].has_key(fid):
fileHandle = connData['OpenedFiles'][fid]['FileHandle']
if fileHandle != PIPE_FILE_DESCRIPTOR:
os.write(fileHandle,data)
respData = os.read(fileHandle,data)
else:
sock = connData['OpenedFiles'][fid]['Socket']
sock.send(data)
respData = sock.recv(maxDataCount)
else:
errorCode = STATUS_INVALID_HANDLE
smbServer.setConnectionData(connId, connData)
return respSetup, respParameters, respData, errorCode
# Here we implement the transaction2 handlers
class TRANS2Commands:
# All these commands return setup, parameters, data, errorCode
@staticmethod
def setPathInformation(connId, smbServer, recvPacket, parameters, data, maxDataCount = 0):
connData = smbServer.getConnectionData(connId)
respSetup = ''
respParameters = ''
respData = ''
errorCode = STATUS_SUCCESS
setPathInfoParameters = smb.SMBSetPathInformation_Parameters(flags = recvPacket['Flags2'], data = parameters)
if connData['ConnectedShares'].has_key(recvPacket['Tid']):
path = connData['ConnectedShares'][recvPacket['Tid']]['path']
fileName = decodeSMBString(recvPacket['Flags2'], setPathInfoParameters['FileName'])
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):
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('<H',parameters)[0])
smbServer.setConnectionData(connId, connData)
return '','', data, errorCode
@staticmethod
def findNext2(connId, smbServer, recvPacket, parameters, data, maxDataCount):
connData = smbServer.getConnectionData(connId)
respSetup = ''
respParameters = ''
respData = ''
errorCode = STATUS_SUCCESS
findNext2Parameters = smb.SMBFindNext2_Parameters(flags = recvPacket['Flags2'], data = parameters)
sid = findNext2Parameters['SID']
if connData['ConnectedShares'].has_key(recvPacket['Tid']):
if connData['SIDs'].has_key(sid):
searchResult = connData['SIDs'][sid]
respParameters = smb.SMBFindNext2Response_Parameters()
endOfSearch = 1
searchCount = 1
totalData = 0
for i in enumerate(searchResult):
data = i[1].getData()
lenData = len(data)
if (totalData+lenData) >= 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('<H', transParameters['Setup'][:2])[0]
if transCommands.has_key(command):
# Call the TRANS subcommand
setup = ''
parameters = ''
data = ''
try:
setup, parameters, data, errorCode = transCommands[command](connId,
smbServer,
recvPacket,
transData['Trans_Parameters'],
transData['Trans_Data'],
transParameters['MaxDataCount'])
except Exception, e:
#print 'Transaction: %s' % e,e
smbServer.log('Transaction: (%r,%s)' % (command, e), logging.ERROR)
errorCode = STATUS_ACCESS_DENIED
#raise
if setup == '' and parameters == '' and data == '':
# Something wen't wrong
respParameters = ''
respData = ''
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.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('<H', trans2Parameters['Setup'])[0]
if transCommands.has_key(command):
# Call the TRANS2 subcommand
try:
setup, parameters, data, errorCode = transCommands[command](connId,
smbServer,
recvPacket,
trans2Data['Trans_Parameters'],
trans2Data['Trans_Data'],
trans2Parameters['MaxDataCount'])
except Exception, e:
smbServer.log('Transaction2: (0x%x,%s)' % (command, e), logging.ERROR)
#import traceback
#traceback.print_exc()
raise
if setup == '' and parameters == '' and data == '':
# Something wen't wrong
respParameters = ''
respData = ''
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.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('<L',token[len('NTLMSSP\x00'):len('NTLMSSP\x00')+4])[0]
if messageType == 0x01:
# NEGOTIATE_MESSAGE
negotiateMessage = ntlm.NTLMAuthNegotiate()
negotiateMessage.fromString(token)
# Let's store it in the connection data
connData['NEGOTIATE_MESSAGE'] = negotiateMessage
# Let's build the answer flags
# TODO: Parse all the flags. With this we're leaving some clients out
ansFlags = 0
if negotiateMessage['flags'] & ntlm.NTLMSSP_NEGOTIATE_56:
ansFlags |= ntlm.NTLMSSP_NEGOTIATE_56
if negotiateMessage['flags'] & ntlm.NTLMSSP_NEGOTIATE_128:
ansFlags |= ntlm.NTLMSSP_NEGOTIATE_128
if negotiateMessage['flags'] & ntlm.NTLMSSP_NEGOTIATE_KEY_EXCH:
ansFlags |= ntlm.NTLMSSP_NEGOTIATE_KEY_EXCH
if negotiateMessage['flags'] & ntlm.NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY:
ansFlags |= ntlm.NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY
if negotiateMessage['flags'] & ntlm.NTLMSSP_NEGOTIATE_UNICODE:
ansFlags |= ntlm.NTLMSSP_NEGOTIATE_UNICODE
if negotiateMessage['flags'] & ntlm.NTLM_NEGOTIATE_OEM:
ansFlags |= ntlm.NTLM_NEGOTIATE_OEM
ansFlags |= ntlm.NTLMSSP_NEGOTIATE_VERSION | ntlm.NTLMSSP_NEGOTIATE_TARGET_INFO | ntlm.NTLMSSP_TARGET_TYPE_SERVER | ntlm.NTLMSSP_NEGOTIATE_NTLM | ntlm.NTLMSSP_REQUEST_TARGET
# Generate the AV_PAIRS
av_pairs = ntlm.AV_PAIRS()
# TODO: Put the proper data from SMBSERVER config
av_pairs[ntlm.NTLMSSP_AV_HOSTNAME] = av_pairs[ntlm.NTLMSSP_AV_DNS_HOSTNAME] = smbServer.getServerName().encode('utf-16le')
av_pairs[ntlm.NTLMSSP_AV_DOMAINNAME] = av_pairs[ntlm.NTLMSSP_AV_DNS_DOMAINNAME] = smbServer.getServerDomain().encode('utf-16le')
av_pairs[ntlm.NTLMSSP_AV_TIME] = struct.pack('<q', (116444736000000000 + calendar.timegm(time.gmtime()) * 10000000) )
challengeMessage = ntlm.NTLMAuthChallenge()
challengeMessage['flags'] = ansFlags
challengeMessage['domain_len'] = len(smbServer.getServerDomain().encode('utf-16le'))
challengeMessage['domain_max_len'] = challengeMessage['domain_len']
challengeMessage['domain_offset'] = 40 + 16
challengeMessage['challenge'] = smbServer.getSMBChallenge()
challengeMessage['domain_name'] = smbServer.getServerDomain().encode('utf-16le')
challengeMessage['TargetInfoFields_len'] = len(av_pairs)
challengeMessage['TargetInfoFields_max_len'] = len(av_pairs)
challengeMessage['TargetInfoFields'] = av_pairs
challengeMessage['TargetInfoFields_offset'] = 40 + 16 + len(challengeMessage['domain_name'])
challengeMessage['Version'] = '\xff'*8
challengeMessage['VersionLen'] = 8
if rawNTLM is False:
respToken = SPNEGO_NegTokenResp()
# accept-incomplete. We want more data
respToken['NegResult'] = '\x01'
respToken['SupportedMech'] = TypesMech['NTLMSSP - Microsoft NTLM Security Support Provider']
respToken['ResponseToken'] = challengeMessage.getData()
else:
respToken = challengeMessage
# Setting the packet to STATUS_MORE_PROCESSING
errorCode = STATUS_MORE_PROCESSING_REQUIRED
# Let's set up an UID for this connection and store it
# in the connection's data
# Picking a fixed value
# TODO: Manage more UIDs for the same session
connData['Uid'] = 10
# Let's store it in the connection data
connData['CHALLENGE_MESSAGE'] = challengeMessage
elif messageType == 0x02:
# CHALLENGE_MESSAGE
raise Exception('Challenge Message raise, not implemented!')
elif messageType == 0x03:
# AUTHENTICATE_MESSAGE, here we deal with authentication
authenticateMessage = ntlm.NTLMAuthChallengeResponse()
authenticateMessage.fromString(token)
smbServer.log("AUTHENTICATE_MESSAGE (%s\\%s,%s)" % (authenticateMessage['domain_name'], authenticateMessage['user_name'], authenticateMessage['host_name']))
# TODO: Check the credentials! Now granting permissions
respToken = SPNEGO_NegTokenResp()
# accept-completed
respToken['NegResult'] = '\x00'
# Status SUCCESS
errorCode = STATUS_SUCCESS
smbServer.log('User %s\\%s authenticated successfully' % (authenticateMessage['user_name'], authenticateMessage['host_name']))
# Let's store it in the connection data
connData['AUTHENTICATE_MESSAGE'] = authenticateMessage
try:
jtr_dump_path = smbServer.getJTRdumpPath()
ntlm_hash_data = outputToJohnFormat( connData['CHALLENGE_MESSAGE']['challenge'], authenticateMessage['user_name'], authenticateMessage['domain_name'], authenticateMessage['lanman'], authenticateMessage['ntlm'] )
smbServer.log(ntlm_hash_data['hash_string'])
if jtr_dump_path is not '':
writeJohnOutputToFile(ntlm_hash_data['hash_string'], ntlm_hash_data['hash_version'], jtr_dump_path)
except:
smbServer.log("Could not write NTLM Hashes to the specified JTR_Dump_Path %s" % jtr_dump_path)
else:
raise Exception("Unknown NTLMSSP MessageType %d" % messageType)
respParameters['SecurityBlobLength'] = len(respToken)
respData['SecurityBlobLength'] = respParameters['SecurityBlobLength']
respData['SecurityBlob'] = respToken.getData()
else:
# Process Standard Security
respParameters = smb.SMBSessionSetupAndXResponse_Parameters()
respData = smb.SMBSessionSetupAndXResponse_Data()
sessionSetupParameters = smb.SMBSessionSetupAndX_Parameters(SMBCommand['Parameters'])
sessionSetupData = smb.SMBSessionSetupAndX_Data()
sessionSetupData['AnsiPwdLength'] = sessionSetupParameters['AnsiPwdLength']
sessionSetupData['UnicodePwdLength'] = sessionSetupParameters['UnicodePwdLength']
sessionSetupData.fromString(SMBCommand['Data'])
connData['Capabilities'] = sessionSetupParameters['Capabilities']
# Do the verification here, for just now we grant access
# TODO: Manage more UIDs for the same session
errorCode = STATUS_SUCCESS
connData['Uid'] = 10
respParameters['Action'] = 0
smbServer.log('User %s\\%s authenticated successfully (basic)' % (sessionSetupData['PrimaryDomain'], sessionSetupData['Account']))
try:
jtr_dump_path = smbServer.getJTRdumpPath()
ntlm_hash_data = outputToJohnFormat( '', sessionSetupData['Account'], sessionSetupData['PrimaryDomain'], sessionSetupData['AnsiPwd'], sessionSetupData['UnicodePwd'] )
smbServer.log(ntlm_hash_data['hash_string'])
if jtr_dump_path is not '':
writeJohnOutputToFile(ntlm_hash_data['hash_string'], ntlm_hash_data['hash_version'], jtr_dump_path)
except:
smbServer.log("Could not write NTLM Hashes to the specified JTR_Dump_Path %s" % jtr_dump_path)
respData['NativeOS'] = encodeSMBString(recvPacket['Flags2'], smbServer.getServerOS())
respData['NativeLanMan'] = encodeSMBString(recvPacket['Flags2'], smbServer.getServerOS())
respSMBCommand['Parameters'] = respParameters
respSMBCommand['Data'] = respData
# From now on, the client can ask for other commands
connData['Authenticated'] = True
# For now, just switching to nobody
#os.setregid(65534,65534)
#os.setreuid(65534,65534)
smbServer.setConnectionData(connId, connData)
return [respSMBCommand], None, errorCode
@staticmethod
def smbComNegotiate(connId, smbServer, SMBCommand, recvPacket ):
connData = smbServer.getConnectionData(connId, checkStatus = False)
connData['Pid'] = recvPacket['Pid']
SMBCommand = smb.SMBCommand(recvPacket['Data'][0])
respSMBCommand = smb.SMBCommand(smb.SMB.SMB_COM_NEGOTIATE)
resp = smb.NewSMBPacket()
resp['Flags1'] = smb.SMB.FLAGS1_REPLY
resp['Pid'] = connData['Pid']
resp['Tid'] = recvPacket['Tid']
resp['Mid'] = recvPacket['Mid']
# TODO: We support more dialects, and parse them accordingly
dialects = SMBCommand['Data'].split('\x02')
try:
index = dialects.index('NT LM 0.12\x00') - 1
# Let's fill the data for NTLM
if recvPacket['Flags2'] & smb.SMB.FLAGS2_EXTENDED_SECURITY:
resp['Flags2'] = smb.SMB.FLAGS2_EXTENDED_SECURITY | smb.SMB.FLAGS2_NT_STATUS | smb.SMB.FLAGS2_UNICODE
#resp['Flags2'] = smb.SMB.FLAGS2_EXTENDED_SECURITY | smb.SMB.FLAGS2_NT_STATUS
_dialects_data = smb.SMBExtended_Security_Data()
_dialects_data['ServerGUID'] = 'A'*16
blob = SPNEGO_NegTokenInit()
blob['MechTypes'] = [TypesMech['NTLMSSP - Microsoft NTLM Security Support Provider']]
_dialects_data['SecurityBlob'] = blob.getData()
_dialects_parameters = smb.SMBExtended_Security_Parameters()
_dialects_parameters['Capabilities'] = smb.SMB.CAP_EXTENDED_SECURITY | smb.SMB.CAP_USE_NT_ERRORS | smb.SMB.CAP_NT_SMBS | smb.SMB.CAP_UNICODE
_dialects_parameters['ChallengeLength'] = 0
else:
resp['Flags2'] = smb.SMB.FLAGS2_NT_STATUS | smb.SMB.FLAGS2_UNICODE
_dialects_parameters = smb.SMBNTLMDialect_Parameters()
_dialects_data= smb.SMBNTLMDialect_Data()
_dialects_data['Payload'] = ''
if connData.has_key('EncryptionKey'):
_dialects_data['Challenge'] = connData['EncryptionKey']
_dialects_parameters['ChallengeLength'] = len(str(_dialects_data))
else:
# TODO: Handle random challenges, now one that can be used with rainbow tables
_dialects_data['Challenge'] = '\x11\x22\x33\x44\x55\x66\x77\x88'
_dialects_parameters['ChallengeLength'] = 8
_dialects_parameters['Capabilities'] = smb.SMB.CAP_USE_NT_ERRORS | smb.SMB.CAP_NT_SMBS
# Let's see if we need to support RPC_REMOTE_APIS
config = smbServer.getServerConfig()
if config.has_option('global','rpc_apis'):
if config.getboolean('global', 'rpc_apis') is True:
_dialects_parameters['Capabilities'] |= smb.SMB.CAP_RPC_REMOTE_APIS
_dialects_parameters['DialectIndex'] = index
_dialects_parameters['SecurityMode'] = smb.SMB.SECURITY_AUTH_ENCRYPTED | smb.SMB.SECURITY_SHARE_USER
_dialects_parameters['MaxMpxCount'] = 1
_dialects_parameters['MaxNumberVcs'] = 1
_dialects_parameters['MaxBufferSize'] = 64000
_dialects_parameters['MaxRawSize'] = 65536
_dialects_parameters['SessionKey'] = 0
_dialects_parameters['LowDateTime'] = 0
_dialects_parameters['HighDateTime'] = 0
_dialects_parameters['ServerTimeZone'] = 0
respSMBCommand['Data'] = _dialects_data
respSMBCommand['Parameters'] = _dialects_parameters
connData['_dialects_data'] = _dialects_data
connData['_dialects_parameters'] = _dialects_parameters
except Exception, e:
# No NTLM throw an error
smbServer.log('smbComNegotiate: %s' % e, logging.ERROR)
respSMBCommand['Data'] = struct.pack('<H',0xffff)
smbServer.setConnectionData(connId, connData)
resp.addCommand(respSMBCommand)
return None, [resp], STATUS_SUCCESS
@staticmethod
def default(connId, smbServer, SMBCommand, recvPacket):
# By default we return an SMB Packet with error not implemented
smbServer.log("Not implemented command: 0x%x" % recvPacket['Command'],logging.DEBUG)
packet = smb.NewSMBPacket()
packet['Flags1'] = smb.SMB.FLAGS1_REPLY
packet['Flags2'] = smb.SMB.FLAGS2_NT_STATUS
packet['Command'] = recvPacket['Command']
packet['Pid'] = recvPacket['Pid']
packet['Tid'] = recvPacket['Tid']
packet['Mid'] = recvPacket['Mid']
packet['Uid'] = recvPacket['Uid']
packet['Data'] = '\x00\x00\x00'
errorCode = STATUS_NOT_IMPLEMENTED
packet['ErrorCode'] = errorCode >> 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('<L',token[len('NTLMSSP\x00'):len('NTLMSSP\x00')+4])[0]
if messageType == 0x01:
# NEGOTIATE_MESSAGE
negotiateMessage = ntlm.NTLMAuthNegotiate()
negotiateMessage.fromString(token)
# Let's store it in the connection data
connData['NEGOTIATE_MESSAGE'] = negotiateMessage
# Let's build the answer flags
# TODO: Parse all the flags. With this we're leaving some clients out
ansFlags = 0
if negotiateMessage['flags'] & ntlm.NTLMSSP_NEGOTIATE_56:
ansFlags |= ntlm.NTLMSSP_NEGOTIATE_56
if negotiateMessage['flags'] & ntlm.NTLMSSP_NEGOTIATE_128:
ansFlags |= ntlm.NTLMSSP_NEGOTIATE_128
if negotiateMessage['flags'] & ntlm.NTLMSSP_NEGOTIATE_KEY_EXCH:
ansFlags |= ntlm.NTLMSSP_NEGOTIATE_KEY_EXCH
if negotiateMessage['flags'] & ntlm.NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY:
ansFlags |= ntlm.NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY
if negotiateMessage['flags'] & ntlm.NTLMSSP_NEGOTIATE_UNICODE:
ansFlags |= ntlm.NTLMSSP_NEGOTIATE_UNICODE
if negotiateMessage['flags'] & ntlm.NTLM_NEGOTIATE_OEM:
ansFlags |= ntlm.NTLM_NEGOTIATE_OEM
ansFlags |= ntlm.NTLMSSP_NEGOTIATE_VERSION | ntlm.NTLMSSP_NEGOTIATE_TARGET_INFO | ntlm.NTLMSSP_TARGET_TYPE_SERVER | ntlm.NTLMSSP_NEGOTIATE_NTLM | ntlm.NTLMSSP_REQUEST_TARGET
# Generate the AV_PAIRS
av_pairs = ntlm.AV_PAIRS()
# TODO: Put the proper data from SMBSERVER config
av_pairs[ntlm.NTLMSSP_AV_HOSTNAME] = av_pairs[ntlm.NTLMSSP_AV_DNS_HOSTNAME] = smbServer.getServerName().encode('utf-16le')
av_pairs[ntlm.NTLMSSP_AV_DOMAINNAME] = av_pairs[ntlm.NTLMSSP_AV_DNS_DOMAINNAME] = smbServer.getServerDomain().encode('utf-16le')
av_pairs[ntlm.NTLMSSP_AV_TIME] = struct.pack('<q', (116444736000000000 + calendar.timegm(time.gmtime()) * 10000000) )
challengeMessage = ntlm.NTLMAuthChallenge()
challengeMessage['flags'] = ansFlags
challengeMessage['domain_len'] = len(smbServer.getServerDomain().encode('utf-16le'))
challengeMessage['domain_max_len'] = challengeMessage['domain_len']
challengeMessage['domain_offset'] = 40 + 16
challengeMessage['challenge'] = smbServer.getSMBChallenge()
challengeMessage['domain_name'] = smbServer.getServerDomain().encode('utf-16le')
challengeMessage['TargetInfoFields_len'] = len(av_pairs)
challengeMessage['TargetInfoFields_max_len'] = len(av_pairs)
challengeMessage['TargetInfoFields'] = av_pairs
challengeMessage['TargetInfoFields_offset'] = 40 + 16 + len(challengeMessage['domain_name'])
challengeMessage['Version'] = '\xff'*8
challengeMessage['VersionLen'] = 8
if rawNTLM is False:
respToken = SPNEGO_NegTokenResp()
# accept-incomplete. We want more data
respToken['NegResult'] = '\x01'
respToken['SupportedMech'] = TypesMech['NTLMSSP - Microsoft NTLM Security Support Provider']
respToken['ResponseToken'] = challengeMessage.getData()
else:
respToken = challengeMessage
# Setting the packet to STATUS_MORE_PROCESSING
errorCode = STATUS_MORE_PROCESSING_REQUIRED
# Let's set up an UID for this connection and store it
# in the connection's data
# Picking a fixed value
# TODO: Manage more UIDs for the same session
connData['Uid'] = random.randint(1,0xffffffff)
# Let's store it in the connection data
connData['CHALLENGE_MESSAGE'] = challengeMessage
elif messageType == 0x02:
# CHALLENGE_MESSAGE
raise Exception('Challenge Message raise, not implemented!')
elif messageType == 0x03:
# AUTHENTICATE_MESSAGE, here we deal with authentication
authenticateMessage = ntlm.NTLMAuthChallengeResponse()
authenticateMessage.fromString(token)
smbServer.log("AUTHENTICATE_MESSAGE (%s\\%s,%s)" % (authenticateMessage['domain_name'], authenticateMessage['user_name'], authenticateMessage['host_name']))
# TODO: Check the credentials! Now granting permissions
respToken = SPNEGO_NegTokenResp()
# accept-completed
respToken['NegResult'] = '\x00'
# Status SUCCESS
errorCode = STATUS_SUCCESS
smbServer.log('User %s\\%s authenticated successfully' % (authenticateMessage['user_name'], authenticateMessage['host_name']))
# Let's store it in the connection data
connData['AUTHENTICATE_MESSAGE'] = authenticateMessage
try:
jtr_dump_path = smbServer.getJTRdumpPath()
ntlm_hash_data = outputToJohnFormat( connData['CHALLENGE_MESSAGE']['challenge'], authenticateMessage['user_name'], authenticateMessage['domain_name'], authenticateMessage['lanman'], authenticateMessage['ntlm'] )
smbServer.log(ntlm_hash_data['hash_string'])
if jtr_dump_path is not '':
writeJohnOutputToFile(ntlm_hash_data['hash_string'], ntlm_hash_data['hash_version'], jtr_dump_path)
except:
smbServer.log("Could not write NTLM Hashes to the specified JTR_Dump_Path %s" % jtr_dump_path)
respSMBCommand['SessionFlags'] = 1
else:
raise Exception("Unknown NTLMSSP MessageType %d" % messageType)
respSMBCommand['SecurityBufferOffset'] = 0x48
respSMBCommand['SecurityBufferLength'] = len(respToken)
respSMBCommand['Buffer'] = respToken.getData()
# From now on, the client can ask for other commands
connData['Authenticated'] = True
# For now, just switching to nobody
#os.setregid(65534,65534)
#os.setreuid(65534,65534)
smbServer.setConnectionData(connId, connData)
return [respSMBCommand], None, errorCode
@staticmethod
def smb2TreeConnect(connId, smbServer, recvPacket):
connData = smbServer.getConnectionData(connId)
respPacket = smb2.SMB2Packet()
respPacket['Flags'] = smb2.SMB2_FLAGS_SERVER_TO_REDIR
respPacket['Status'] = STATUS_SUCCESS
respPacket['CreditRequestResponse'] = 1
respPacket['Command'] = recvPacket['Command']
respPacket['SessionID'] = connData['Uid']
respPacket['Reserved'] = recvPacket['Reserved']
respPacket['MessageID'] = recvPacket['MessageID']
respPacket['TreeID'] = recvPacket['TreeID']
respSMBCommand = smb2.SMB2TreeConnect_Response()
treeConnectRequest = smb2.SMB2TreeConnect(recvPacket['Data'])
errorCode = STATUS_SUCCESS
## Process here the request, does the share exist?
path = str(recvPacket)[treeConnectRequest['PathOffset']:][:treeConnectRequest['PathLength']]
UNCOrShare = path.decode('utf-16le')
# Is this a UNC?
if ntpath.ismount(UNCOrShare):
path = UNCOrShare.split('\\')[3]
else:
path = ntpath.basename(UNCOrShare)
share = searchShare(connId, path.upper(), 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
respPacket['TreeID'] = tid
smbServer.log("Connecting Share(%d:%s)" % (tid,path))
else:
smbServer.log("SMB2_TREE_CONNECT not found %s" % path, logging.ERROR)
errorCode = STATUS_OBJECT_PATH_NOT_FOUND
respPacket['Status'] = errorCode
##
if path == 'IPC$':
respSMBCommand['ShareType'] = smb2.SMB2_SHARE_TYPE_PIPE
respSMBCommand['ShareFlags'] = 0x30
else:
respSMBCommand['ShareType'] = smb2.SMB2_SHARE_TYPE_DISK
respSMBCommand['ShareFlags'] = 0x0
respSMBCommand['Capabilities'] = 0
respSMBCommand['MaximalAccess'] = 0x000f01ff
respPacket['Data'] = respSMBCommand
smbServer.setConnectionData(connId, connData)
return None, [respPacket], errorCode
@staticmethod
def smb2Create(connId, smbServer, recvPacket):
connData = smbServer.getConnectionData(connId)
respSMBCommand = smb2.SMB2Create_Response()
ntCreateRequest = smb2.SMB2Create(recvPacket['Data'])
respSMBCommand['Buffer'] = '\x00'
# Get the Tid associated
if connData['ConnectedShares'].has_key(recvPacket['TreeID']):
# If we have a rootFid, the path is relative to that fid
errorCode = STATUS_SUCCESS
if connData['ConnectedShares'][recvPacket['TreeID']].has_key('path'):
path = connData['ConnectedShares'][recvPacket['TreeID']]['path']
else:
path = 'NONE'
errorCode = STATUS_ACCESS_DENIED
deleteOnClose = False
fileName = os.path.normpath(ntCreateRequest['Buffer'][:ntCreateRequest['NameLength']].decode('utf-16le').replace('\\','/'))
if len(fileName) > 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:
# <list of respSMBCommands>, <list of packets>, errorCode
# <list of packets> 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