Files correlati : utilma verione di curl git-svn-id: svn://10.65.10.50/branches/R_10_00@24159 c028cbd2-c16b-5b4b-a496-9718f37d4682
		
			
				
	
	
		
			4169 lines
		
	
	
		
			190 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			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
 |