# Copyright (c) 2003-2006 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.
#
# $Id: smb.py,v 1.7 2006/05/23 21:19:25 gera Exp $
#
# -*- mode: python; tab-width: 4 -*-
# $Id: smb.py,v 1.7 2006/05/23 21:19:25 gera Exp $
#
# Copyright (C) 2001 Michael Teo <michaelteo@bigfoot.com>
# smb.py - SMB/CIFS library
#
# This software is provided 'as-is', without any express or implied warranty.
# In no event will the author be held liable for any damages arising from the
# use of this software.
#
# Permission is granted to anyone to use this software for any purpose,
# including commercial applications, and to alter it and redistribute it
# freely, subject to the following restrictions:
#
# 1. The origin of this software must not be misrepresented; you must not
# claim that you wrote the original software. If you use this software
# in a product, an acknowledgment in the product documentation would be
# appreciated but is not required.
#
# 2. Altered source versions must be plainly marked as such, and must not be
# misrepresented as being the original software.
#
# 3. This notice cannot be removed or altered from any source distribution.
#
# Altered source done by Alberto Solino
# Todo:
# [ ] Try [SMB]transport fragmentation using Transact requests
# [ ] Try other methods of doing write (write_raw, transact2, write, write_and_unlock, write_and_close, write_mpx)
# [-] Try replacements for SMB_COM_NT_CREATE_ANDX (CREATE, T_TRANSACT_CREATE, OPEN_ANDX works
# [x] Fix forceWriteAndx, which needs to send a RecvRequest, because recv() will not send it
# [x] Fix Recv() when using RecvAndx and the answer comes splet in several packets
# [ ] Try [SMB]transport fragmentation with overlaping segments
# [ ] Try [SMB]transport fragmentation with out of order segments
# [x] Do chained AndX requests
import os, sys, socket, string, re, select, errno
import nmb
import types
from random import randint
from struct import *
import ntlm
from dcerpc import samr
from structure import Structure
unicode_support = 0
unicode_convert = 1
try:
from cStringIO import StringIO
except ImportError:
from StringIO import StringIO
from binascii import a2b_hex
CVS_REVISION = '$Revision: 1.7 $'
# Shared Device Type
SHARED_DISK = 0x00
SHARED_PRINT_QUEUE = 0x01
SHARED_DEVICE = 0x02
SHARED_IPC = 0x03
# Extended attributes mask
ATTR_ARCHIVE = 0x020
ATTR_COMPRESSED = 0x800
ATTR_NORMAL = 0x080
ATTR_HIDDEN = 0x002
ATTR_READONLY = 0x001
ATTR_TEMPORARY = 0x100
ATTR_DIRECTORY = 0x010
ATTR_SYSTEM = 0x004
# Service Type
SERVICE_DISK = 'A:'
SERVICE_PRINTER = 'LPT1:'
SERVICE_IPC = 'IPC'
SERVICE_COMM = 'COMM'
SERVICE_ANY = '?????'
# Server Type (Can be used to mask with SMBMachine.get_type() or SMBDomain.get_type())
SV_TYPE_WORKSTATION = 0x00000001
SV_TYPE_SERVER = 0x00000002
SV_TYPE_SQLSERVER = 0x00000004
SV_TYPE_DOMAIN_CTRL = 0x00000008
SV_TYPE_DOMAIN_BAKCTRL = 0x00000010
SV_TYPE_TIME_SOURCE = 0x00000020
SV_TYPE_AFP = 0x00000040
SV_TYPE_NOVELL = 0x00000080
SV_TYPE_DOMAIN_MEMBER = 0x00000100
SV_TYPE_PRINTQ_SERVER = 0x00000200
SV_TYPE_DIALIN_SERVER = 0x00000400
SV_TYPE_XENIX_SERVER = 0x00000800
SV_TYPE_NT = 0x00001000
SV_TYPE_WFW = 0x00002000
SV_TYPE_SERVER_NT = 0x00004000
SV_TYPE_POTENTIAL_BROWSER = 0x00010000
SV_TYPE_BACKUP_BROWSER = 0x00020000
SV_TYPE_MASTER_BROWSER = 0x00040000
SV_TYPE_DOMAIN_MASTER = 0x00080000
SV_TYPE_LOCAL_LIST_ONLY = 0x40000000
SV_TYPE_DOMAIN_ENUM = 0x80000000
# Options values for SMB.stor_file and SMB.retr_file
SMB_O_CREAT = 0x10 # Create the file if file does not exists. Otherwise, operation fails.
SMB_O_EXCL = 0x00 # When used with SMB_O_CREAT, operation fails if file exists. Cannot be used with SMB_O_OPEN.
SMB_O_OPEN = 0x01 # Open the file if the file exists
SMB_O_TRUNC = 0x02 # Truncate the file if the file exists
# Share Access Mode
SMB_SHARE_COMPAT = 0x00
SMB_SHARE_DENY_EXCL = 0x10
SMB_SHARE_DENY_WRITE = 0x20
SMB_SHARE_DENY_READEXEC = 0x30
SMB_SHARE_DENY_NONE = 0x40
SMB_ACCESS_READ = 0x00
SMB_ACCESS_WRITE = 0x01
SMB_ACCESS_READWRITE = 0x02
SMB_ACCESS_EXEC = 0x03
TRANS_DISCONNECT_TID = 1
TRANS_NO_RESPONSE = 2
#*********************************** TEMP BEGIN ********************************
def set_key_odd_parity(key):
""
for i in range(len(key)):
for k in range(7):
bit = 0
t = key[i] >> k
bit = (t ^ bit) & 0x1
key[i] = (key[i] & 0xFE) | bit
return key
#*********************************** TEMP END ********************************
def strerror(errclass, errcode):
if errclass == 0x01:
return 'OS error', ERRDOS.get(errcode, 'Unknown error')
elif errclass == 0x02:
return 'Server error', ERRSRV.get(errcode, 'Unknown error')
elif errclass == 0x03:
return 'Hardware error', ERRHRD.get(errcode, 'Unknown error')
# This is not a standard error class for SMB
elif errclass == 0x80:
return 'Browse error', ERRBROWSE.get(errcode, 'Unknown error')
elif errclass == 0xff:
return 'Bad command', 'Bad command. Please file bug report'
else:
return 'Unknown error', 'Unknown error'
# Raised when an error has occured during a session
class SessionError(Exception):
# Error codes
# SMB X/Open error codes for the ERRDOS error class
ERRsuccess = 0
ERRbadfunc = 1
ERRbadfile = 2
ERRbadpath = 3
ERRnofids = 4
ERRnoaccess = 5
ERRbadfid = 6
ERRbadmcb = 7
ERRnomem = 8
ERRbadmem = 9
ERRbadenv = 10
ERRbadaccess = 12
ERRbaddata = 13
ERRres = 14
ERRbaddrive = 15
ERRremcd = 16
ERRdiffdevice = 17
ERRnofiles = 18
ERRgeneral = 31
ERRbadshare = 32
ERRlock = 33
ERRunsup = 50
ERRnetnamedel = 64
ERRnosuchshare = 67
ERRfilexists = 80
ERRinvalidparam = 87
ERRcannotopen = 110
ERRinsufficientbuffer = 122
ERRinvalidname = 123
ERRunknownlevel = 124
ERRnotlocked = 158
ERRrename = 183
ERRbadpipe = 230
ERRpipebusy = 231
ERRpipeclosing = 232
ERRnotconnected = 233
ERRmoredata = 234
ERRnomoreitems = 259
ERRbaddirectory = 267
ERReasnotsupported = 282
ERRlogonfailure = 1326
ERRbuftoosmall = 2123
ERRunknownipc = 2142
ERRnosuchprintjob = 2151
ERRinvgroup = 2455
# here's a special one from observing NT
ERRnoipc = 66
# These errors seem to be only returned by the NT printer driver system
ERRdriveralreadyinstalled = 1795
ERRunknownprinterport = 1796
ERRunknownprinterdriver = 1797
ERRunknownprintprocessor = 1798
ERRinvalidseparatorfile = 1799
ERRinvalidjobpriority = 1800
ERRinvalidprintername = 1801
ERRprinteralreadyexists = 1802
ERRinvalidprintercommand = 1803
ERRinvaliddatatype = 1804
ERRinvalidenvironment = 1805
ERRunknownprintmonitor = 3000
ERRprinterdriverinuse = 3001
ERRspoolfilenotfound = 3002
ERRnostartdoc = 3003
ERRnoaddjob = 3004
ERRprintprocessoralreadyinstalled = 3005
ERRprintmonitoralreadyinstalled = 3006
ERRinvalidprintmonitor = 3007
ERRprintmonitorinuse = 3008
ERRprinterhasjobsqueued = 3009
# Error codes for the ERRSRV class
ERRerror = 1
ERRbadpw = 2
ERRbadtype = 3
ERRaccess = 4
ERRinvnid = 5
ERRinvnetname = 6
ERRinvdevice = 7
ERRqfull = 49
ERRqtoobig = 50
ERRinvpfid = 52
ERRsmbcmd = 64
ERRsrverror = 65
ERRfilespecs = 67
ERRbadlink = 68
ERRbadpermits = 69
ERRbadpid = 70
ERRsetattrmode = 71
ERRpaused = 81
ERRmsgoff = 82
ERRnoroom = 83
ERRrmuns = 87
ERRtimeout = 88
ERRnoresource = 89
ERRtoomanyuids = 90
ERRbaduid = 91
ERRuseMPX = 250
ERRuseSTD = 251
ERRcontMPX = 252
ERRbadPW = None
ERRnosupport = 0
ERRunknownsmb = 22
# Error codes for the ERRHRD class
ERRnowrite = 19
ERRbadunit = 20
ERRnotready = 21
ERRbadcmd = 22
ERRdata = 23
ERRbadreq = 24
ERRseek = 25
ERRbadmedia = 26
ERRbadsector = 27
ERRnopaper = 28
ERRwrite = 29
ERRread = 30
ERRgeneral = 31
ERRwrongdisk = 34
ERRFCBunavail = 35
ERRsharebufexc = 36
ERRdiskfull = 39
hard_msgs = {
19: ("ERRnowrite", "Attempt to write on write-protected diskette."),
20: ("ERRbadunit", "Unknown unit."),
21: ("ERRnotready", "Drive not ready."),
22: ("ERRbadcmd", "Unknown command."),
23: ("ERRdata", "Data error (CRC)."),
24: ("ERRbadreq", "Bad request structure length."),
25 : ("ERRseek", "Seek error."),
26: ("ERRbadmedia", "Unknown media type."),
27: ("ERRbadsector", "Sector not found."),
28: ("ERRnopaper", "Printer out of paper."),
29: ("ERRwrite", "Write fault."),
30: ("ERRread", "Read fault."),
31: ("ERRgeneral", "General failure."),
32: ("ERRbadshare", "An open conflicts with an existing open."),
33: ("ERRlock", "A Lock request conflicted with an existing lock or specified an invalid mode, or an Unlock requested attempted to remove a lock held by another process."),
34: ("ERRwrongdisk", "The wrong disk was found in a drive."),
35: ("ERRFCBUnavail", "No FCBs are available to process request."),
36: ("ERRsharebufexc", "A sharing buffer has been exceeded.")
}
dos_msgs = {
ERRbadfunc: ("ERRbadfunc", "Invalid function."),
ERRbadfile: ("ERRbadfile", "File not found."),
ERRbadpath: ("ERRbadpath", "Directory invalid."),
ERRnofids: ("ERRnofids", "No file descriptors available"),
ERRnoaccess: ("ERRnoaccess", "Access denied."),
ERRbadfid: ("ERRbadfid", "Invalid file handle."),
ERRbadmcb: ("ERRbadmcb", "Memory control blocks destroyed."),
ERRnomem: ("ERRnomem", "Insufficient server memory to perform the requested function."),
ERRbadmem: ("ERRbadmem", "Invalid memory block address."),
ERRbadenv: ("ERRbadenv", "Invalid environment."),
11: ("ERRbadformat", "Invalid format."),
ERRbadaccess: ("ERRbadaccess", "Invalid open mode."),
ERRbaddata: ("ERRbaddata", "Invalid data."),
ERRres: ("ERRres", "reserved."),
ERRbaddrive: ("ERRbaddrive", "Invalid drive specified."),
ERRremcd: ("ERRremcd", "A Delete Directory request attempted to remove the server's current directory."),
ERRdiffdevice: ("ERRdiffdevice", "Not same device."),
ERRnofiles: ("ERRnofiles", "A File Search command can find no more files matching the specified criteria."),
ERRbadshare: ("ERRbadshare", "The sharing mode specified for an Open conflicts with existing FIDs on the file."),
ERRlock: ("ERRlock", "A Lock request conflicted with an existing lock or specified an invalid mode, or an Unlock requested attempted to remove a lock held by another process."),
ERRunsup: ("ERRunsup", "The operation is unsupported"),
ERRnosuchshare: ("ERRnosuchshare", "You specified an invalid share name"),
ERRfilexists: ("ERRfilexists", "The file named in a Create Directory, Make New File or Link request already exists."),
ERRinvalidname: ("ERRinvalidname", "Invalid name"),
ERRbadpipe: ("ERRbadpipe", "Pipe invalid."),
ERRpipebusy: ("ERRpipebusy", "All instances of the requested pipe are busy."),
ERRpipeclosing: ("ERRpipeclosing", "Pipe close in progress."),
ERRnotconnected: ("ERRnotconnected", "No process on other end of pipe."),
ERRmoredata: ("ERRmoredata", "There is more data to be returned."),
ERRinvgroup: ("ERRinvgroup", "Invalid workgroup (try the -W option)"),
ERRlogonfailure: ("ERRlogonfailure", "Logon failure"),
ERRdiskfull: ("ERRdiskfull", "Disk full"),
ERRgeneral: ("ERRgeneral", "General failure"),
ERRunknownlevel: ("ERRunknownlevel", "Unknown info level")
}
server_msgs = {
1: ("ERRerror", "Non-specific error code."),
2: ("ERRbadpw", "Bad password - name/password pair in a Tree Connect or Session Setup are invalid."),
3: ("ERRbadtype", "reserved."),
4: ("ERRaccess", "The requester does not have the necessary access rights within the specified context for the requested function. The context is defined by the TID or the UID."),
5: ("ERRinvnid", "The tree ID (TID) specified in a command was invalid."),
6: ("ERRinvnetname", "Invalid network name in tree connect."),
7: ("ERRinvdevice", "Invalid device - printer request made to non-printer connection or non-printer request made to printer connection."),
49: ("ERRqfull", "Print queue full (files) -- returned by open print file."),
50: ("ERRqtoobig", "Print queue full -- no space."),
51: ("ERRqeof", "EOF on print queue dump."),
52: ("ERRinvpfid", "Invalid print file FID."),
64: ("ERRsmbcmd", "The server did not recognize the command received."),
65: ("ERRsrverror","The server encountered an internal error, e.g., system file unavailable."),
67: ("ERRfilespecs", "The file handle (FID) and pathname parameters contained an invalid combination of values."),
68: ("ERRreserved", "reserved."),
69: ("ERRbadpermits", "The access permissions specified for a file or directory are not a valid combination. The server cannot set the requested attribute."),
70: ("ERRreserved", "reserved."),
71: ("ERRsetattrmode", "The attribute mode in the Set File Attribute request is invalid."),
81: ("ERRpaused", "Server is paused."),
82: ("ERRmsgoff", "Not receiving messages."),
83: ("ERRnoroom", "No room to buffer message."),
87: ("ERRrmuns", "Too many remote user names."),
88: ("ERRtimeout", "Operation timed out."),
89: ("ERRnoresource", "No resources currently available for request."),
90: ("ERRtoomanyuids", "Too many UIDs active on this session."),
91: ("ERRbaduid", "The UID is not known as a valid ID on this session."),
250: ("ERRusempx","Temp unable to support Raw, use MPX mode."),
251: ("ERRusestd","Temp unable to support Raw, use standard read/write."),
252: ("ERRcontmpx", "Continue in MPX mode."),
253: ("ERRreserved", "reserved."),
254: ("ERRreserved", "reserved."),
0xFFFF: ("ERRnosupport", "Function not supported.")
}
# Error clases
ERRDOS = 0x1
error_classes = { 0: ("SUCCESS", {}),
ERRDOS: ("ERRDOS", dos_msgs),
0x02: ("ERRSRV",server_msgs),
0x03: ("ERRHRD",hard_msgs),
0x04: ("ERRXOS", {} ),
0xE1: ("ERRRMX1", {} ),
0xE2: ("ERRRMX2", {} ),
0xE3: ("ERRRMX3", {} ),
0xFF: ("ERRCMD", {} ) }
def __init__( self, str, error_class, error_code):
self.args = str
self.error_class = error_class
self.error_code = error_code
def get_error_class( self ):
return self.error_class
def get_error_code( self ):
return self.error_code
def __str__( self ):
error_class = SessionError.error_classes.get( self.error_class, None )
if not error_class:
error_code_str = self.error_code
error_class_str = self.error_class
else:
error_class_str = error_class[0]
error_code = error_class[1].get( self.error_code, None )
if not error_code:
error_code_str = self.error_code
else:
error_code_str = '%s(%s)' % (error_code)
return 'SessionError: %s, class: %s, code: %s' % (self.args, error_class_str, error_code_str)
# Raised when an supported feature is present/required in the protocol but is not
# currently supported by pysmb
class UnsupportedFeature(Exception): pass
# Contains information about a SMB shared device/service
class SharedDevice:
def __init__(self, name, type, comment):
self.__name = name
self.__type = type
self.__comment = comment
def get_name(self):
return self.__name
def get_type(self):
return self.__type
def get_comment(self):
return self.__comment
def __repr__(self):
return '<SharedDevice instance: name=' + self.__name + ', type=' + str(self.__type) + ', comment="' + self.__comment + '">'
# Contains information about the shared file/directory
class SharedFile:
def __init__(self, ctime, atime, mtime, filesize, allocsize, attribs, shortname, longname):
self.__ctime = ctime
self.__atime = atime
self.__mtime = mtime
self.__filesize = filesize
self.__allocsize = allocsize
self.__attribs = attribs
try:
self.__shortname = shortname[:string.index(shortname, '\0')]
except ValueError:
self.__shortname = shortname
try:
self.__longname = longname[:string.index(longname, '\0')]
except ValueError:
self.__longname = longname
def get_ctime(self):
return self.__ctime
def get_ctime_epoch(self):
return self.__convert_smbtime(self.__ctime)
def get_mtime(self):
return self.__mtime
def get_mtime_epoch(self):
return self.__convert_smbtime(self.__mtime)
def get_atime(self):
return self.__atime
def get_atime_epoch(self):
return self.__convert_smbtime(self.__atime)
def get_filesize(self):
return self.__filesize
def get_allocsize(self):
return self.__allocsize
def get_attributes(self):
return self.__attribs
def is_archive(self):
return self.__attribs & ATTR_ARCHIVE
def is_compressed(self):
return self.__attribs & ATTR_COMPRESSED
def is_normal(self):
return self.__attribs & ATTR_NORMAL
def is_hidden(self):
return self.__attribs & ATTR_HIDDEN
def is_readonly(self):
return self.__attribs & ATTR_READONLY
def is_temporary(self):
return self.__attribs & ATTR_TEMPORARY
def is_directory(self):
return self.__attribs & ATTR_DIRECTORY
def is_system(self):
return self.__attribs & ATTR_SYSTEM
def get_shortname(self):
return self.__shortname
def get_longname(self):
return self.__longname
def __repr__(self):
return '<SharedFile instance: shortname="' + self.__shortname + '", longname="' + self.__longname + '", filesize=' + str(self.__filesize) + '>'
def __convert_smbtime(self, t):
x = t >> 32
y = t & 0xffffffffL
geo_cal_offset = 11644473600.0 # = 369.0 * 365.25 * 24 * 60 * 60 - (3.0 * 24 * 60 * 60 + 6.0 * 60 * 60)
return ((x * 4.0 * (1 << 30) + (y & 0xfff00000L)) * 1.0e-7 - geo_cal_offset)
# Contain information about a SMB machine
class SMBMachine:
def __init__(self, nbname, type, comment):
self.__nbname = nbname
self.__type = type
self.__comment = comment
def __repr__(self):
return '<SMBMachine instance: nbname="' + self.__nbname + '", type=' + hex(self.__type) + ', comment="' + self.__comment + '">'
class SMBDomain:
def __init__(self, nbgroup, type, master_browser):
self.__nbgroup = nbgroup
self.__type = type
self.__master_browser = master_browser
def __repr__(self):
return '<SMBDomain instance: nbgroup="' + self.__nbgroup + '", type=' + hex(self.__type) + ', master browser="' + self.__master_browser + '">'
# Represents a SMB Packet
class NewSMBPacket(Structure):
structure = (
('Signature', '"\xffSMB'),
('Command','B=0'),
('ErrorClass','B=0'),
('_reserved','B=0'),
('ErrorCode','<H=0'),
('Flags1','B=0'),
('Flags2','<H=0'),
('Padding','12s=""'),
('Tid','<H=0xffff'),
('Pid','<H=0'),
('Uid','<H=0'),
('Mid','<H=0'),
('Data','*:'),
)
def __init__(self, **kargs):
Structure.__init__(self, **kargs)
if not kargs.has_key('data'):
self['Data'] = []
def addCommand(self, command):
if len(self['Data']) == 0:
self['Command'] = command.command
else:
self['Data'][-1]['Parameters']['AndXCommand'] = command.command
self['Data'][-1]['Parameters']['AndXOffset'] = len(self)
self['Data'].append(command)
def isMoreData(self):
return (self['Command'] in [SMB.SMB_COM_TRANSACTION, SMB.SMB_COM_READ_ANDX, SMB.SMB_COM_READ_RAW] and
self['ErrorClass'] == 1 and self['ErrorCode'] == SessionError.ERRmoredata)
def isValidAnswer(self, cmd):
# this was inside a loop reading more from the net (with recv_packet(None))
if self['Command'] == cmd:
if (self['ErrorClass'] == 0x00 and
self['ErrorCode'] == 0x00):
return 1
elif self.isMoreData():
return 1
raise SessionError, ("SMB Library Error", self['ErrorClass'], self['ErrorCode'])
else:
raise UnsupportedFeature, ("Unexpected answer from server: Got %d, Expected %d" % (self['Command'], cmd))
class SMBPacket:
def __init__(self,data = ''):
# The uid attribute will be set when the client calls the login() method
self._command = 0x0
self._error_class = 0x0
self._error_code = 0x0
self._flags = 0x0
self._flags2 = 0x0
self._pad = '\0' * 12
self._tid = 0x0
self._pid = 0x0
self._uid = 0x0
self._mid = 0x0
self._wordcount = 0x0
self._parameter_words = ''
self._bytecount = 0x0
self._buffer = ''
if data != '':
self._command = ord(data[4])
self._error_class = ord(data[5])
self._error_code = unpack('<H',data[7:9])[0]
self._flags = ord(data[9])
self._flags2 = unpack('<H',data[10:12])[0]
self._tid = unpack('<H',data[24:26])[0]
self._pid = unpack('<H',data[26:28])[0]
self._uid = unpack('<H',data[28:30])[0]
self._mid = unpack('<H',data[30:32])[0]
self._wordcount = ord(data[32])
self._parameter_words = data[33:33+self._wordcount*2]
self._bytecount = ord(data[33+self._wordcount*2])
self._buffer = data[35+self._wordcount*2:]
def set_command(self,command):
self._command = command
def set_error_class(self, error_class):
self._error_class = error_class
def set_error_code(self,error_code):
self._error_code = error_code
def set_flags(self,flags):
self._flags = flags
def set_flags2(self, flags2):
self._flags2 = flags2
def set_pad(self, pad):
self._pad = pad
def set_tid(self,tid):
self._tid = tid
def set_pid(self,pid):
self._pid = pid
def set_uid(self,uid):
self._uid = uid
def set_mid(self,mid):
self._mid = mid
def set_parameter_words(self,param):
self._parameter_words = param
self._wordcount = len(param)/2
def set_buffer(self,buffer):
if type(buffer) is types.UnicodeType:
raise Exception('SMBPacket: Invalid buffer. Received unicode')
self._buffer = buffer
self._bytecount = len(buffer)
def get_command(self):
return self._command
def get_error_class(self):
return self._error_class
def get_error_code(self):
return self._error_code
def get_flags(self):
return self._flags
def get_flags2(self):
return self._flags2
def get_pad(self):
return self._pad
def get_tid(self):
return self._tid
def get_pid(self):
return self._pid
def get_uid(self):
return self._uid
def get_mid(self):
return self._mid
def get_parameter_words(self):
return self._parameter_words
def get_wordcount(self):
return self._wordcount
def get_bytecount(self):
return self._bytecount
def get_buffer(self):
return self._buffer
def rawData(self):
data = pack('<4sBBBHBH12sHHHHB','\xffSMB',self._command,self._error_class,0,self._error_code,self._flags,
self._flags2,self._pad,self._tid, self._pid, self._uid, self._mid, self._wordcount) + self._parameter_words + pack('<H',self._bytecount) + self._buffer
return data
class TRANSHeader:
def __init__(self,params = '', data = ''):
self._total_param_count = 0
self._total_data_count = 0
self._max_param_count = 0
self._max_data_count = 0
self._max_setup_count = 0
self._flags = 0
self._timeout = 0
self._param_count = 0
self._param_offset = 0
self._data_count = 0
self._data_offset = 0
self._setup_count = 0
self._setup = 0
self._name = ''
self._pad = ''
self._parameters = 0
self._data = 0
if data != '' and params != '':
self._total_param_count, self._total_data_count, _, self._param_count, self._param_offset, self._param_displacement, self._data_count, self._data_offset, self._data_displacement, self._setup_count, _ = unpack ('<HHHHHHHHHBB', params)
self._data = data[-self._data_count:]; # Remove -potential- prefix padding.
def set_flags(self, flags):
self._flags = flags
def set_name(self,name):
self._name = name
def set_setup(self,setup):
self._setup = setup
def set_parameters(self,parameters):
self._parameters = parameters
self._total_param_count = len(parameters)
def set_data(self, data):
self._data = data
self._total_data_count = len(data)
def set_max_data_count(self, max):
self._max_data_count = max
def set_max_param_count(self, max):
self._max_param_count = max
def get_rawParameters(self):
self._param_offset = 32+3+28+len(self._setup)+len(self._name)
self._data_offset = self._param_offset + len(self._parameters)
return pack('<HHHHBBHLHHHHHBB', self._total_param_count, self._total_data_count, self._max_param_count, self._max_data_count, self._max_setup_count,
0,self._flags, self._timeout, 0, self._total_param_count, self._param_offset , self._total_data_count, self._data_offset, len(self._setup) / 2,0 ) + self._setup
def get_data(self):
return self._data
def rawData(self):
return self._name + self._parameters + self._data
class SMBCommand(Structure):
structure = (
('WordCount', 'B=len(Parameters)/2'),
('_ParametersLength','_-Parameters','WordCount*2'),
('Parameters',':'), # default set by constructor
('ByteCount','<H-Data'),
('Data',':'), # default set by constructor
)
def __init__(self, commandOrData = None, data = None, **kargs):
if type(commandOrData) == type(0):
self.command = commandOrData
else:
data = data or commandOrData
Structure.__init__(self, data = data, **kargs)
if data is None:
self['Parameters'] = ''
self['Data'] = ''
class AsciiOrUnicodeStructure(Structure):
def __init__(self, flags = 0, **kargs):
if flags & SMB.FLAGS2_UNICODE:
self.structure = self.UnicodeStructure
else:
self.structure = self.AsciiStructure
return Structure.__init__(self, **kargs)
class SMBCommand_Parameters(Structure):
pass
class SMBAndXCommand_Parameters(Structure):
commonHdr = (
('AndXCommand','B=0xff'),
('_reserved','B=0'),
('AndXOffset','<H=0'),
)
structure = ( # default structure, overriden by subclasses
('Data',':=""'),
)
class SMBSessionSetupAndX_Parameters(SMBAndXCommand_Parameters):
structure = (
('MaxBuffer','<H'),
('MaxMpxCount','<H'),
('VCNumber','<H'),
('SessionKey','<L'),
('AnsiPwdLength','<H'),
('UnicodePwdLength','<H'),
('_reserved','<L=0'),
('Capabilities','<L'),
)
class SMBSessionSetupAndX_Data(AsciiOrUnicodeStructure):
AsciiStructure = (
('AnsiPwdLength','_-AnsiPwd'),
('UnicodePwdLength','_-UnicodePwd'),
('AnsiPwd',':=""'),
('UnicodePwd',':=""'),
('Account','z=""'),
('PrimaryDomain','z=""'),
('NativeOS','z=""'),
('NativeLanMan','z=""'),
)
UnicodeStructure = (
('AnsiPwdLength','_-AnsiPwd'),
('UnicodePwdLength','_-UnicodePwd'),
('AnsiPwd',':=""'),
('UnicodePwd',':=""'),
('Account','w=""'),
('PrimaryDomain','w=""'),
('NativeOS','w=""'),
('NativeLanMan','w=""'),
)
class SMBSessionSetupAndXResponse_Parameters(SMBAndXCommand_Parameters):
structure = (
('Action','<H'),
)
class SMBSessionSetupAndXResponse_Data(AsciiOrUnicodeStructure):
AsciiStructure = (
('NativeOS','z=""'),
('NativeLanMan','z=""'),
('PrimaryDomain','z=""'),
)
UnicodeStructure = (
('NativeOS','w=""'),
('NativeLanMan','w=""'),
('PrimaryDomain','w=""'),
)
class SMBTreeConnect_Parameters(SMBCommand_Parameters):
structure = (
)
class SMBTreeConnect_Data(SMBCommand_Parameters):
structure = (
('PathFormat','"\x04'),
('Path','z'),
('PasswordFormat','"\x04'),
('Password','z'),
('ServiceFormat','"\x04'),
('Service','z'),
)
class SMBTreeConnectAndX_Parameters(SMBAndXCommand_Parameters):
structure = (
('Flags','<H=0'),
('PasswordLength','<H'),
)
class SMBTreeConnectAndX_Data(SMBCommand_Parameters):
structure = (
('_PasswordLength','_-Password'),
('Password',':'),
('Path','z'),
('Service','z'),
)
class SMBNtCreateAndX_Parameters(SMBAndXCommand_Parameters):
structure = (
('_reserved', 'B=0'),
('FileNameLength','<H'),
('CreateFlags','<L'),
('RootFid','<L=0'),
('AccessMask','<L'),
('AllocationSizeLo','<L=0'),
('AllocationSizeHi','<L=0'),
('FileAttributes','<L=0'),
('ShareAccess','<L=3'),
('Disposition','<L=1'),
('CreateOptions','<L'),
('Impersonation','<L=2'),
('SecurityFlags','B=3'),
)
class SMBNtCreateAndXResponse_Parameters(SMBAndXCommand_Parameters):
# XXX Is there a memory leak in the response for NTCreate (where the Data section would be) in Win 2000, Win XP, and Win 2003?
structure = (
('OplockLevel', 'B=0'),
('Fid','<H'),
('CreateAction','<L'),
('CraetionTimeLo','<L=0'),
('CraetionTimeHi','<L=0'),
('AccessTimeLo','<L=0'),
('AccessTimeHi','<L=0'),
('LastWriteTimeLo','<L=0'),
('LastWriteTimeHi','<L=0'),
('ChangeTimeLo','<L=0'),
('ChangeTimeHi','<L=0'),
('FileAttributes','<L=0x80'),
('AllocationSizeLo','<L=0'),
('AllocationSizeHi','<L=0'),
('EndOfFileLo','<L=0'),
('EndOfFileHi','<L=0'),
('FileType','<H=0'),
('IPCState','<H=0'),
('IsDirectory','B'),
)
class SMBNtCreateAndX_Data(Structure):
structure = (
('FileName','z'),
)
class SMBOpenAndX_Parameters(SMBAndXCommand_Parameters):
structure = (
('Flags','<H=0'),
('DesiredAccess','<H=0'),
('SearchAttributes','<H=0'),
('FileAttributes','<H=0'),
('CreationTime','<L=0'),
('OpenMode','<H=1'), # SMB_O_OPEN = 1
('AllocationSize','<L=0'),
('Reserved','8s=""'),
)
class SMBOpenAndX_Data(SMBNtCreateAndX_Data):
pass
class SMBOpenAndXResponse_Parameters(SMBAndXCommand_Parameters):
structure = (
('Fid','<H=0'),
('FileAttributes','<H=0'),
('LastWriten','<L=0'),
('FileSize','<L=0'),
('GrantedAccess','<H=0'),
('FileType','<H=0'),
('IPCState','<H=0'),
('Action','<H=0'),
('ServerFid','<L=0'),
('_reserved','<H=0'),
)
class SMBWrite_Parameters(SMBCommand_Parameters):
structure = (
('Fid','<H'),
('Count','<H'),
('Offset','<L'),
('Remaining','<H'),
)
class SMBWrite_Data(Structure):
structure = (
('BufferFormat','<B=1'),
('DataLength','<H-Data'),
('Data',':'),
)
class SMBWriteAndX_Parameters(SMBAndXCommand_Parameters):
structure = (
('Fid','<H'),
('Offset','<L'),
('_reserved','<L=0xff'),
('WriteMode','<H=8'),
('Remaining','<H'),
('DataLength_Hi','<H=0'),
('DataLength','<H'),
('DataOffset','<H=0'),
('HighOffset','<L=0'),
)
class SMBWriteRaw_Parameters(SMBCommand_Parameters):
structure = (
('Fid','<H'),
('Count','<H'),
('_reserved','<H=0'),
('Offset','<L'),
('Timeout','<L=0'),
('WriteMode','<H=0'),
('_reserved2','<L=0'),
('DataLength','<H'),
('DataOffset','<H=0'),
)
class SMBRead_Parameters(SMBCommand_Parameters):
structure = (
('Fid','<H'),
('Count','<H'),
('Offset','<L'),
('Remaining','<H=Count'),
)
class SMBReadResponse_Parameters(Structure):
structure = (
('Count','<H=0'),
('_reserved','"\0\0\0\0\0\0\0\0'),
)
class SMBReadResponse_Data(Structure):
structure = (
('BufferFormat','<B'),
('DataLength','<H-Data'),
('Data',':'),
)
class SMBReadRaw_Parameters(SMBCommand_Parameters):
structure = (
('Fid','<H'),
('Offset','<L'),
('MaxCount','<H'),
('MinCount','<H=MaxCount'),
('Timeout','<L=0'),
('_reserved','<H=0'),
)
class SMBReadAndX_Parameters(SMBAndXCommand_Parameters):
structure = (
('Fid','<H'),
('Offset','<L'),
('MaxCount','<H'),
('MinCount','<H=MaxCount'),
('_reserved','<L=0xffffffff'),
('Remaining','<H=MaxCount'),
('HighOffset','<L=0'),
)
class SMBReadAndXResponse_Parameters(SMBAndXCommand_Parameters):
structure = (
('Remaining','<H=0'),
('DataMode','<H=0'),
('_reserved','<H=0'),
('DataCount','<H'),
('DataOffset','<H'),
('DataCount_Hi','<L'),
('_reserved2','"\0\0\0\0\0\0'),
)
class SMBOpen_Parameters(SMBCommand_Parameters):
structure = (
('DesiredAccess','<H=0'),
('SearchAttributes','<H=0'),
)
class SMBOpen_Data(Structure):
structure = (
('FileNameFormat','"\x04'),
('FileName','z'),
)
class SMBOpenResponse_Parameters(SMBCommand_Parameters):
structure = (
('Fid','<H=0'),
('FileAttributes','<H=0'),
('LastWriten','<L=0'),
('FileSize','<L=0'),
('GrantedAccess','<H=0'),
)
class NTLMDialect(SMBPacket):
def __init__(self,data=''):
SMBPacket.__init__(self,data)
self._selected_dialect = 0
self._security_mode = 0
self._max_mpx = 0
self._max_vc = 0
self._max_buffer = 0
self._max_raw = 0
self._session_key = 0
self._lsw_capabilities = 0
self._msw_capabilities = 0
self._utc_high = 0
self._utc_low = 0
self._minutes_utc = 0
self._encryption_key_len = 0
self._encryption_key = ''
self._server_domain = ''
self._server_name = ''
if data:
self._selected_dialect, self._security_mode, self._max_mpx, self._max_vc = unpack('<HBHH',self.get_parameter_words()[:7])
self._max_buffer,self._max_raw, self._session_key, self._lsw_capabilities, self._msw_capabilities = unpack('<lllHH', self.get_parameter_words()[7:16+7])
self._utc_low, self._utc_high,self._minutes_utc, self._encryption_key_len = unpack('<LLhB',self.get_parameter_words()[23:34])
if self._encryption_key_len > 0 and len(self.get_buffer()) >= self._encryption_key_len:
self._encryption_key = self.get_buffer()[:self._encryption_key_len]
buf = self.get_buffer()
# Look for the server domain offset
self._server_name = '<Unknown>'
self._server_domain = '<Unknown>'
try:
if self._lsw_capabilities & 0x3: # is this unicode?
offset = self._encryption_key_len
if offset & 0x01:
offset += 1
end = offset
while ord(buf[end]) or ord(buf[end+1]):
end += 2
self._server_domain = unicode(buf[offset:end],'utf_16_le')
end += 2
offset = end
while ord(buf[end]) or ord(buf[end+1]):
end += 2
self._server_name = unicode(buf[offset:end],'utf_16_le')
else:
offset = self._encryption_key_len
idx1 = string.find(buf,'\0',offset)
if idx1 != -1:
self._server_domain = buf[offset:idx1]
idx2 = string.find(buf, '\0', idx1 + 1)
if idx2 != -1:
self._server_name = buf[idx1+1:idx2]
except:
pass
else:
self._encryption_key = ''
def get_selected_dialect(self):
return self._selected_dialect
def get_security_mode(self):
return self._security_mode
def get_max_mpx(self):
return self._max_mpx
def get_max_vc(self):
return self._max_vc
def get_max_buffer(self):
return self._max_buffer
def get_max_raw(self):
return self._max_raw
def get_session_key(self):
return self._session_key
def get_lsw_capabilities(self):
return self._lsw_capabilities
def get_msw_capabilities(self):
return self._msw_capabilities
def get_utc(self):
return self._utc_high, self._utc_low
def get_minutes_utc(self):
return self._minutes_utc
def get_encryption_key_len(self):
return self._encryption_key_len
def get_encryption_key(self):
return self._encryption_key
def get_server_domain(self):
return self._server_domain
def get_server_name(self):
return self._server_name
def is_auth_mode(self):
return self._security_mode & SMB.SECURITY_AUTH_MASK
def is_share_mode(self):
return self._security_mode & SMB.SECURITY_SHARE_MASK
def is_rawmode(self):
return self._lsw_capabilities & SMB.CAP_RAW_MODE
class SMB:
# SMB Command Codes
SMB_COM_CREATE_DIRECTORY = 0x00
SMB_COM_DELETE_DIRECTORY = 0x01
SMB_COM_OPEN = 0x02
SMB_COM_CREATE = 0x03
SMB_COM_CLOSE = 0x04
SMB_COM_FLUSH = 0x05
SMB_COM_DELETE = 0x06
SMB_COM_RENAME = 0x07
SMB_COM_QUERY_INFORMATION = 0x08
SMB_COM_SET_INFORMATION = 0x09
SMB_COM_READ = 0x0A
SMB_COM_WRITE = 0x0B
SMB_COM_LOCK_BYTE_RANGE = 0x0C
SMB_COM_UNLOCK_BYTE_RANGE = 0x0D
SMB_COM_CREATE_TEMPORARY = 0x0E
SMB_COM_CREATE_NEW = 0x0F
SMB_COM_CHECK_DIRECTORY = 0x10
SMB_COM_PROCESS_EXIT = 0x11
SMB_COM_SEEK = 0x12
SMB_COM_LOCK_AND_READ = 0x13
SMB_COM_WRITE_AND_UNLOCK = 0x14
SMB_COM_READ_RAW = 0x1A
SMB_COM_READ_MPX = 0x1B
SMB_COM_READ_MPX_SECONDARY = 0x1C
SMB_COM_WRITE_RAW = 0x1D
SMB_COM_WRITE_MPX = 0x1E
SMB_COM_WRITE_MPX_SECONDARY = 0x1F
SMB_COM_WRITE_COMPLETE = 0x20
SMB_COM_QUERY_SERVER = 0x21
SMB_COM_SET_INFORMATION2 = 0x22
SMB_COM_QUERY_INFORMATION2 = 0x23
SMB_COM_LOCKING_ANDX = 0x24
SMB_COM_TRANSACTION = 0x25
SMB_COM_TRANSACTION_SECONDARY = 0x26
SMB_COM_IOCTL = 0x27
SMB_COM_IOCTL_SECONDARY = 0x28
SMB_COM_COPY = 0x29
SMB_COM_MOVE = 0x2A
SMB_COM_ECHO = 0x2B
SMB_COM_WRITE_AND_CLOSE = 0x2C
SMB_COM_OPEN_ANDX = 0x2D
SMB_COM_READ_ANDX = 0x2E
SMB_COM_WRITE_ANDX = 0x2F
SMB_COM_NEW_FILE_SIZE = 0x30
SMB_COM_CLOSE_AND_TREE_DISC = 0x31
SMB_COM_TRANSACTION2 = 0x32
SMB_COM_TRANSACTION2_SECONDARY = 0x33
SMB_COM_FIND_CLOSE2 = 0x34
SMB_COM_FIND_NOTIFY_CLOSE = 0x35
# Used by Xenix/Unix 0x60 - 0x6E
SMB_COM_TREE_CONNECT = 0x70
SMB_COM_TREE_DISCONNECT = 0x71
SMB_COM_NEGOTIATE = 0x72
SMB_COM_SESSION_SETUP_ANDX = 0x73
SMB_COM_LOGOFF_ANDX = 0x74
SMB_COM_TREE_CONNECT_ANDX = 0x75
SMB_COM_QUERY_INFORMATION_DISK = 0x80
SMB_COM_SEARCH = 0x81
SMB_COM_FIND = 0x82
SMB_COM_FIND_UNIQUE = 0x83
SMB_COM_FIND_CLOSE = 0x84
SMB_COM_NT_TRANSACT = 0xA0
SMB_COM_NT_TRANSACT_SECONDARY = 0xA1
SMB_COM_NT_CREATE_ANDX = 0xA2
SMB_COM_NT_CANCEL = 0xA4
SMB_COM_NT_RENAME = 0xA5
SMB_COM_OPEN_PRINT_FILE = 0xC0
SMB_COM_WRITE_PRINT_FILE = 0xC1
SMB_COM_CLOSE_PRINT_FILE = 0xC2
SMB_COM_GET_PRINT_QUEUE = 0xC3
SMB_COM_READ_BULK = 0xD8
SMB_COM_WRITE_BULK = 0xD9
SMB_COM_WRITE_BULK_DATA = 0xDA
# Security Share Mode (Used internally by SMB class)
SECURITY_SHARE_MASK = 0x01
SECURITY_SHARE_SHARE = 0x00
SECURITY_SHARE_USER = 0x01
# Security Auth Mode (Used internally by SMB class)
SECURITY_AUTH_MASK = 0x02
SECURITY_AUTH_ENCRYPTED = 0x02
SECURITY_AUTH_PLAINTEXT = 0x00
# Raw Mode Mask (Used internally by SMB class. Good for dialect up to and including LANMAN2.1)
RAW_READ_MASK = 0x01
RAW_WRITE_MASK = 0x02
# Capabilities Mask (Used internally by SMB class. Good for dialect NT LM 0.12)
CAP_RAW_MODE = 0x0001
CAP_MPX_MODE = 0x0002
CAP_UNICODE = 0x0004
CAP_LARGE_FILES = 0x0008
CAP_EXTENDED_SECURITY = 0x80000000
# Flags1 Mask
FLAGS1_PATHCASELESS = 0x08
# Flags2 Mask
FLAGS2_LONG_FILENAME = 0x0001
FLAGS2_USE_NT_ERRORS = 0x4000
FLAGS2_UNICODE = 0x8000
def __init__(self, remote_name, remote_host, my_name = None, host_type = nmb.TYPE_SERVER, sess_port = nmb.NETBIOS_SESSION_PORT, timeout=None):
# The uid attribute will be set when the client calls the login() method
self.__uid = 0
self.__server_os = ''
self.__server_lanman = ''
self.__server_domain = ''
self.__remote_name = string.upper(remote_name)
self.__is_pathcaseless = 0
self.__ntlm_dialect = 0
self.__sess = None
if timeout==None:
self.__timeout = 30
else:
self.__timeout = timeout
if not my_name:
my_name = socket.gethostname()
i = string.find(my_name, '.')
if i > -1:
my_name = my_name[:i]
try:
self.__sess = nmb.NetBIOSSession(my_name, remote_name, remote_host, host_type, sess_port, timeout)
except socket.error, ex:
raise ex
# Initialize values __ntlm_dialect, __is_pathcaseless
self.__neg_session()
# If the following assertion fails, then mean that the encryption key is not sent when
# encrypted authentication is required by the server.
assert (self.__ntlm_dialect.is_auth_mode() == SMB.SECURITY_AUTH_PLAINTEXT) or (self.__ntlm_dialect.is_auth_mode() == SMB.SECURITY_AUTH_ENCRYPTED and self.__ntlm_dialect.get_encryption_key() and self.__ntlm_dialect.get_encryption_key_len() >= 8)
# Call login() without any authentication information to setup a session if the remote server
# is in share mode.
if self.__ntlm_dialect.is_share_mode() == SMB.SECURITY_SHARE_SHARE:
self.login('', '')
def set_timeout(self, timeout):
self.__timeout = timeout
def __del__(self):
if self.__sess:
self.__sess.close()
def __decode_smb(self, data):
_, cmd, err_class, _, err_code, flags1, flags2, _, tid, pid, uid, mid, wcount = unpack('<4sBBBHBH12sHHHHB', data[:33])
param_end = 33 + wcount * 2
return cmd, err_class, err_code, flags1, flags2, tid, uid, mid, data[33:param_end], data[param_end + 2:]
def recvSMB(self):
r = self.__sess.recv_packet(self.__timeout)
return NewSMBPacket(data = r.get_trailer())
def recv_packet(self):
r = self.__sess.recv_packet(self.__timeout)
return SMBPacket(r.get_trailer())
def __decode_trans(self, params, data):
totparamcnt, totdatacnt, _, paramcnt, paramoffset, paramds, datacnt, dataoffset, datads, setupcnt = unpack('<HHHHHHHHHB', params[:19])
if paramcnt + paramds < totparamcnt or datacnt + datads < totdatacnt:
has_more = 1
else:
has_more = 0
paramoffset = paramoffset - 55 - setupcnt * 2
dataoffset = dataoffset - 55 - setupcnt * 2
return has_more, params[20:20 + setupcnt * 2], data[paramoffset:paramoffset + paramcnt], data[dataoffset:dataoffset + datacnt]
def sendSMB(self,smb):
smb['Uid'] = self.__uid
smb['Pid'] = os.getpid()
self.__sess.send_packet(str(smb))
def send_smb(self,s):
s.set_uid(self.__uid)
s.set_pid(os.getpid())
self.__sess.send_packet(s.rawData())
def __send_smb_packet(self, cmd, flags, flags2, tid, mid, params = '', data = ''):
smb = NewSMBPacket()
smb['Flags'] = flags
smb['Flags2'] = flags2
smb['Tid'] = tid
smb['Mid'] = mid
cmd = SMBCommand(cmd)
smb.addCommand(cmd)
cmd['Parameters'] = params
cmd['Data'] = data
self.sendSMB(smb)
def isValidAnswer(self, s, cmd):
while 1:
if s.rawData():
if s.get_command() == cmd:
if s.get_error_class() == 0x00 and s.get_error_code() == 0x00:
return 1
else:
raise SessionError, ( "SMB Library Error", s.get_error_class(), s.get_error_code())
else:
break
# raise SessionError("Invalid command received. %x" % cmd)
# s=self.recv_packet(None)
return 0
def __neg_session(self):
s = SMBPacket()
s.set_command(SMB.SMB_COM_NEGOTIATE)
s.set_buffer('\x02NT LM 0.12\x00')
self.send_smb(s)
while 1:
s = self.recv_packet()
if self.isValidAnswer(s,SMB.SMB_COM_NEGOTIATE):
self.__ntlm_dialect = NTLMDialect(s.rawData())
if self.__ntlm_dialect.get_selected_dialect() == 0xffff:
raise UnsupportedFeature,"Remote server does not know NT LM 0.12"
#NL LM 0.12 dialect selected
if self.__ntlm_dialect.get_lsw_capabilities() & SMB.CAP_EXTENDED_SECURITY:
raise UnsupportedFeature, "This version of pysmb does not support extended security validation. Please file a request for it."
self.__is_pathcaseless = s.get_flags() & SMB.FLAGS1_PATHCASELESS
return 1
else:
return 0
def tree_connect(self, path, password = '', service = SERVICE_ANY):
# return 0x800
if password:
# Password is only encrypted if the server passed us an "encryption" during protocol dialect
if self.__ntlm_dialect.get_encryption_key():
# this code is untested
password = self.get_ntlmv1_response(ntlm.compute_lmhash(password))
if not unicode_support:
if unicode_convert:
path = str(path)
else:
raise Except('SMB: Can\t conver path from unicode!')
smb = NewSMBPacket()
smb['Flags1'] = 8
treeConnect = SMBCommand(SMB.SMB_COM_TREE_CONNECT)
treeConnect['Parameters'] = SMBTreeConnect_Parameters()
treeConnect['Data'] = SMBTreeConnect_Data()
treeConnect['Data']['Path'] = path.upper()
treeConnect['Data']['Password'] = password
treeConnect['Data']['Service'] = service
smb.addCommand(treeConnect)
self.sendSMB(smb)
while 1:
smb = self.recvSMB()
if smb.isValidAnswer(SMB.SMB_COM_TREE_CONNECT):
# XXX Here we are ignoring the rest of the response
return smb['Tid']
return smb['Tid']
def tree_connect_andx(self, path, password = None, service = SERVICE_ANY):
if password:
# Password is only encrypted if the server passed us an "encryption" during protocol dialect
if self.__ntlm_dialect.get_encryption_key():
# this code is untested
password = self.get_ntlmv1_response(ntlm.compute_lmhash(password))
else:
password = '\x00'
if not unicode_support:
if unicode_convert:
path = str(path)
else:
raise Except('SMB: Can\t convert path from unicode!')
smb = NewSMBPacket()
smb['Flags1'] = 8
treeConnect = SMBCommand(SMB.SMB_COM_TREE_CONNECT_ANDX)
treeConnect['Parameters'] = SMBTreeConnectAndX_Parameters()
treeConnect['Data'] = SMBTreeConnectAndX_Data()
treeConnect['Parameters']['PasswordLength'] = len(password)
treeConnect['Data']['Password'] = password
treeConnect['Data']['Path'] = path.upper()
treeConnect['Data']['Service'] = service
smb.addCommand(treeConnect)
# filename = "\PIPE\epmapper"
# ntCreate = SMBCommand(SMB.SMB_COM_NT_CREATE_ANDX)
# ntCreate['Parameters'] = SMBNtCreateAndX_Parameters()
# ntCreate['Data'] = SMBNtCreateAndX_Data()
# ntCreate['Parameters']['FileNameLength'] = len(filename)
# ntCreate['Parameters']['CreateFlags'] = 0
# ntCreate['Parameters']['AccessMask'] = 0x3
# ntCreate['Parameters']['CreateOptions'] = 0x0
# ntCreate['Data']['FileName'] = filename
# smb.addCommand(ntCreate)
self.sendSMB(smb)
while 1:
smb = self.recvSMB()
if smb.isValidAnswer(SMB.SMB_COM_TREE_CONNECT_ANDX):
# XXX Here we are ignoring the rest of the response
return smb['Tid']
return smb['Tid']
# backwars compatibility
connect_tree = tree_connect_andx
def get_server_name(self):
return self.__ntlm_dialect.get_server_name()
def get_session_key(self):
return self.__ntlm_dialect.get_session_key()
def get_server_time(self):
high, low = self.__ntlm_dialect.get_utc()
min = self.__ntlm_dialect.get_minutes_utc()
return samr.display_time(high, low, min)
def disconnect_tree(self, tid):
smb = NewSMBPacket()
smb['Tid'] = tid
smb.addCommand(SMBCommand(SMB.SMB_COM_TREE_DISCONNECT))
self.sendSMB(smb)
smb = self.recvSMB()
def open(self, tid, filename, open_mode, desired_access):
smb = NewSMBPacket()
smb['Flags'] = 8
smb['Flags2'] = SMB.FLAGS2_LONG_FILENAME
smb['Tid'] = tid
openFile = SMBCommand(SMB.SMB_COM_OPEN)
openFile['Parameters'] = SMBOpen_Parameters()
openFile['Parameters']['DesiredAccess'] = desired_access
openFile['Parameters']['OpenMode'] = open_mode
openFile['Parameters']['SearchAttributes'] = ATTR_READONLY | ATTR_HIDDEN | ATTR_ARCHIVE
openFile['Data'] = SMBOpen_Data()
openFile['Data']['FileName'] = filename
smb.addCommand(openFile)
self.sendSMB(smb)
smb = self.recvSMB()
if smb.isValidAnswer(SMB.SMB_COM_OPEN):
# XXX Here we are ignoring the rest of the response
openFileResponse = SMBCommand(smb['Data'][0])
openFileParameters = SMBOpenResponse_Parameters(openFileResponse['Parameters'])
return (
openFileParameters['Fid'],
openFileParameters['FileAttributes'],
openFileParameters['LastWriten'],
openFileParameters['FileSize'],
openFileParameters['GrantedAccess'],
)
def open_andx(self, tid, filename, open_mode, desired_access):
smb = NewSMBPacket()
smb['Flags'] = 8
smb['Flags2'] = SMB.FLAGS2_LONG_FILENAME
smb['Tid'] = tid
openFile = SMBCommand(SMB.SMB_COM_OPEN_ANDX)
openFile['Parameters'] = SMBOpenAndX_Parameters()
openFile['Parameters']['DesiredAccess'] = desired_access
openFile['Parameters']['OpenMode'] = open_mode
openFile['Parameters']['SearchAttributes'] = ATTR_READONLY | ATTR_HIDDEN | ATTR_ARCHIVE
openFile['Data'] = SMBOpenAndX_Data()
openFile['Data']['FileName'] = filename
smb.addCommand(openFile)
self.sendSMB(smb)
smb = self.recvSMB()
if smb.isValidAnswer(SMB.SMB_COM_OPEN_ANDX):
# XXX Here we are ignoring the rest of the response
openFileResponse = SMBCommand(smb['Data'][0])
openFileParameters = SMBOpenAndXResponse_Parameters(openFileResponse['Parameters'])
return (
openFileParameters['Fid'],
openFileParameters['FileAttributes'],
openFileParameters['LastWriten'],
openFileParameters['FileSize'],
openFileParameters['GrantedAccess'],
openFileParameters['FileType'],
openFileParameters['IPCState'],
openFileParameters['Action'],
openFileParameters['ServerFid'],
)
def close(self, tid, fid):
s = SMBPacket()
s.set_command(SMB.SMB_COM_CLOSE)
s.set_tid(tid)
s.set_parameter_words(pack('<HL', fid, 0))
self.send_smb(s)
s = self.recv_packet()
def send_trans(self, tid, setup, name, param, data, noAnswer = 0):
t = TRANSHeader()
s = SMBPacket()
s.set_tid(tid)
s.set_command(SMB.SMB_COM_TRANSACTION)
s.set_flags(self.__is_pathcaseless)
s.set_flags2(SMB.FLAGS2_LONG_FILENAME)
t.set_setup(setup)
t.set_name(name)
t.set_parameters(param)
t.set_data(data)
t.set_max_param_count(1024) # Saca esto y se muere remotamente
t.set_max_data_count(65504) # Saca esto y se muere remotamente
if noAnswer:
t.set_flags(TRANS_NO_RESPONSE)
s.set_parameter_words(t.get_rawParameters())
s.set_buffer(t.rawData())
self.send_smb(s)
def __trans(self, tid, setup, name, param, data):
data_len = len(data)
name_len = len(name)
param_len = len(param)
setup_len = len(setup)
assert setup_len & 0x01 == 0
param_offset = name_len + setup_len + 63
data_offset = param_offset + param_len
self.__send_smb_packet(SMB.SMB_COM_TRANSACTION, self.__is_pathcaseless, SMB.FLAGS2_LONG_FILENAME, tid, 0, pack('<HHHHBBHLHHHHHBB', param_len, data_len, 1024, 65504, 0, 0, 0, 0, 0, param_len, param_offset, data_len, data_offset, setup_len / 2, 0) + setup, name + param + data)
def trans2(self, tid, setup, name, param, data):
data_len = len(data)
name_len = len(name)
param_len = len(param)
setup_len = len(setup)
assert setup_len & 0x01 == 0
param_offset = name_len + setup_len + 63
data_offset = param_offset + param_len
self.__send_smb_packet(SMB.SMB_COM_TRANSACTION2, self.__is_pathcaseless, SMB.FLAGS2_LONG_FILENAME, tid, 0, pack('<HHHHBBHLHHHHHBB', param_len, data_len, 1024, self.__ntlm_dialect.get_max_buffer(), 0, 0, 0, 0, 0, param_len, param_offset, data_len, data_offset, setup_len / 2, 0) + setup, name + param + data)
def query_file_info(self, tid, fid):
self.trans2(tid, '\x07\x00', '\x00', pack('<HH', fid, 0x107), '')
while 1:
s = self.recv_packet()
if self.isValidAnswer(s,SMB.SMB_COM_TRANSACTION2):
f1, f2 = unpack('<LL', s.get_buffer()[53:53+8])
return (f2 & 0xffffffffL) << 32 | f1
def __nonraw_retr_file(self, tid, fid, offset, datasize, callback):
max_buf_size = self.__ntlm_dialect.get_max_buffer() & ~0x3ff # Read in multiple KB blocks
read_offset = offset
while read_offset < datasize:
data = self.read_andx(tid, fid, read_offset, max_buf_size)
callback(data)
read_offset += len(data)
def __raw_retr_file(self, tid, fid, offset, datasize, callback):
max_buf_size = self.__ntlm_dialect.get_max_buffer() & ~0x3ff # Write in multiple KB blocks
read_offset = offset
while read_offset < datasize:
data = self.read_raw(tid, fid, read_offset, 0xffff)
if not data:
# No data returned. Need to send SMB_COM_READ_ANDX to find out what is the error.
data = self.read_andx(tid, fid, read_offset, max_buf_size)
callback(data)
read_offset += len(data)
def __nonraw_stor_file(self, tid, fid, offset, datasize, callback):
max_buf_size = self.__ntlm_dialect.get_max_buffer() & ~0x3ff # Write in multiple KB blocks
write_offset = offset
while 1:
data = callback(max_buf_size)
if not data:
break
self.__send_smb_packet(SMB.SMB_COM_WRITE_ANDX, 0, 0, tid, 0, pack('<BBHHLLHHHHH', 0xff, 0, 0, fid, write_offset, 0, 0, 0, 0, len(data), 59), data)
while 1:
s = self.recv_packet()
if self.isValidAnswer(s,SMB.SMB_COM_WRITE_ANDX):
offset = unpack('<H', s.get_parameter_words()[2:4])[0]
write_offset = write_offset + unpack('<H', s.get_parameter_words()[4:6])[0]
break
def __raw_stor_file(self, tid, fid, offset, datasize, callback):
write_offset = offset
while 1:
read_data = callback(65535)
if not read_data:
break
read_len = len(read_data)
self.__send_smb_packet(SMB.SMB_COM_WRITE_RAW, 0, 0, tid, 0, pack('<HHHLLHLHH', fid, read_len, 0, write_offset, 0, 0, 0, 0, 59), '')
while 1:
s = self.recv_packet()
if self.isValidAnswer(s,SMB.SMB_COM_WRITE_RAW):
self.__sess.send_packet(read_data)
write_offset = write_offset + read_len
break
# We need to close fid to check whether the last raw packet is written successfully
self.__send_smb_packet(SMB.SMB_COM_CLOSE, 0, 0, tid, 0, pack('<HL', fid, 0), '')
while 1:
s = self.recv_packet()
if self.isValidAnswer(s,SMB.SMB_COM_CLOSE):
if s.get_error_class() == 0x00 and s.get_error_code() == 0x00:
return
def __browse_servers(self, server_flags, container_type, domain):
tid = self.tree_connect_andx('\\\\' + self.__remote_name + '\\IPC$')
buf = StringIO()
try:
if server_flags & 0x80000000:
self.__trans(tid, '', '\\PIPE\\LANMAN\x00', '\x68\x00WrLehDz\x00' + 'B16BBDz\x00\x01\x00\xff\xff\x00\x00\x00\x80', '')
else:
self.__trans(tid, '', '\\PIPE\\LANMAN\x00', '\x68\x00WrLehDz\x00' + 'B16BBDz\x00\x01\x00\xff\xff' + pack('<l', server_flags) + domain + '\x00', '')
servers = [ ]
entry_count = 0
while 1:
s = self.recv_packet()
if self.isValidAnswer(s,SMB.SMB_COM_TRANSACTION):
has_more, _, transparam, transdata = self.__decode_trans(s.get_parameter_words(), s.get_buffer())
if not entry_count:
status, convert, entry_count, avail_entry = unpack('<HHHH', transparam[:8])
if status and status != 234: # status 234 means have more data
raise SessionError, ( 'Browse domains failed. (ErrClass: %d and ErrCode: %d)' % ( 0x80, status ), 0x80, status )
buf.write(transdata)
if not has_more:
server_data = buf.getvalue()
for i in range(0, entry_count):
server, _, server_type, comment_offset = unpack('<16s2sll', server_data[i * 26:i * 26 + 26])
idx = string.find(server, '\0')
idx2 = string.find(server_data, '\0', comment_offset)
if idx < 0:
server = server[:idx]
servers.append(container_type(server, server_type, server_data[comment_offset:idx2]))
return servers
finally:
buf.close()
self.disconnect_tree(tid)
def get_server_domain(self):
return self.__server_domain
def get_server_os(self):
return self.__server_os
def get_server_lanman(self):
return self.__server_lanman
def is_login_required(self):
# Login is required if share mode is user. Otherwise only public services or services in share mode
# are allowed.
return self.__ntlm_dialect.is_share_mode() == SMB.SECURITY_SHARE_USER
def get_ntlmv1_response(self, key):
challenge = self.__ntlm_dialect.get_encryption_key()
return ntlm.get_ntlmv1_response(key, challenge)
def hmac_md5(self, key, data):
import POW
h = POW.Hmac(POW.MD5_DIGEST, key)
h.update(data)
result = h.mac()
return result
def get_ntlmv2_response(self, hash):
"""
blob = RandomBytes( blobsize );
data = concat( ServerChallenge, 8, blob, blobsize );
hmac = hmac_md5( v2hash, 16, data, (8 + blobsize) );
v2resp = concat( hmac, 16, blob, blobsize );
"""
return ''
def login(self, user, password, domain = '', lmhash = '', nthash = ''):
if password != '' or (password == '' and lmhash == '' and nthash == ''):
self.login_plaintext_password(user, password)
elif lmhash != '' or nthash != '':
self.login_pass_the_hash(user, lmhash, nthash, domain)
def _login(self, user, pwd_ansi, pwd_unicode, domain = ''):
smb = NewSMBPacket()
smb['Flags1'] = 8
sessionSetup = SMBCommand(SMB.SMB_COM_SESSION_SETUP_ANDX)
sessionSetup['Parameters'] = SMBSessionSetupAndX_Parameters()
sessionSetup['Data'] = SMBSessionSetupAndX_Data()
sessionSetup['Parameters']['MaxBuffer'] = 65535
sessionSetup['Parameters']['MaxMpxCount'] = 2
sessionSetup['Parameters']['VCNumber'] = os.getpid()
sessionSetup['Parameters']['SessionKey'] = self.__ntlm_dialect.get_session_key()
sessionSetup['Parameters']['AnsiPwdLength'] = len(pwd_ansi)
sessionSetup['Parameters']['UnicodePwdLength'] = len(pwd_unicode)
sessionSetup['Parameters']['Capabilities'] = SMB.CAP_RAW_MODE
sessionSetup['Data']['AnsiPwd'] = pwd_ansi
sessionSetup['Data']['UnicodePwd'] = pwd_unicode
sessionSetup['Data']['Account'] = str(user)
sessionSetup['Data']['PrimaryDomain'] = str(domain)
sessionSetup['Data']['NativeOS'] = str(os.name)
sessionSetup['Data']['NativeLanMan'] = 'pysmb'
smb.addCommand(sessionSetup)
self.sendSMB(smb)
smb = self.recvSMB()
if smb.isValidAnswer(SMB.SMB_COM_SESSION_SETUP_ANDX):
# We will need to use this uid field for all future requests/responses
self.__uid = smb['Uid']
sessionResponse = SMBCommand(smb['Data'][0])
sessionParameters = SMBSessionSetupAndXResponse_Parameters(sessionResponse['Parameters'])
sessionData = SMBSessionSetupAndXResponse_Data(flags = smb['Flags2'], data = sessionResponse['Data'])
self.__server_os = sessionData['NativeOS']
self.__server_lanman = sessionData['NativeLanMan']
self.__server_domain = sessionData['PrimaryDomain']
return 1
else: raise Exception('Error: Could not login successfully')
def read(self, tid, fid, offset=0, max_size = None, wait_answer=1):
if not max_size:
max_size = self.__ntlm_dialect.get_max_buffer() # Read in multiple KB blocks
# max_size is not working, because although it would, the server returns an error (More data avail)
smb = NewSMBPacket()
smb['Flags1'] = 0x18
smb['Flags2'] = 0
smb['Tid'] = tid
read = SMBCommand(SMB.SMB_COM_READ)
read['Parameters'] = SMBRead_Parameters()
read['Parameters']['Fid'] = fid
read['Parameters']['Offset'] = offset
read['Parameters']['Count'] = max_size
smb.addCommand(read)
if wait_answer:
answer = ''
while 1:
self.sendSMB(smb)
ans = self.recvSMB()
if ans.isValidAnswer(SMB.SMB_COM_READ):
readResponse = SMBCommand(ans['Data'][0])
readParameters = SMBReadResponse_Parameters(readResponse['Parameters'])
readData = SMBReadResponse_Data(readResponse['Data'])
return readData['Data']
return None
def read_andx(self, tid, fid, offset=0, max_size = None, wait_answer=1):
if not max_size:
max_size = self.__ntlm_dialect.get_max_buffer() # Read in multiple KB blocks
# max_size is not working, because although it would, the server returns an error (More data avail)
smb = NewSMBPacket()
smb['Flags1'] = 0x18
smb['Flags2'] = 0
smb['Tid'] = tid
readAndX = SMBCommand(SMB.SMB_COM_READ_ANDX)
readAndX['Parameters'] = SMBReadAndX_Parameters()
readAndX['Parameters']['Fid'] = fid
readAndX['Parameters']['Offset'] = offset
readAndX['Parameters']['MaxCount'] = max_size
smb.addCommand(readAndX)
if wait_answer:
answer = ''
while 1:
self.sendSMB(smb)
ans = self.recvSMB()
if ans.isValidAnswer(SMB.SMB_COM_READ_ANDX):
# XXX Here we are only using a few fields from the response
readAndXResponse = SMBCommand(ans['Data'][0])
readAndXParameters = SMBReadAndXResponse_Parameters(readAndXResponse['Parameters'])
offset = readAndXParameters['DataOffset']
count = readAndXParameters['DataCount']+0x10000*readAndXParameters['DataCount_Hi']
answer += str(ans)[offset:offset+count]
if not ans.isMoreData():
return answer
max_size = min(max_size, readAndXParameters['Remaining'])
readAndX['Parameters']['Offset'] += count # XXX Offset is not important (apparently)
return None
def read_raw(self, tid, fid, offset=0, max_size = None, wait_answer=1):
if not max_size:
max_size = self.__ntlm_dialect.get_max_buffer() # Read in multiple KB blocks
# max_size is not working, because although it would, the server returns an error (More data avail)
smb = NewSMBPacket()
smb['Flags1'] = 0x18
smb['Flags2'] = 0
smb['Tid'] = tid
readRaw = SMBCommand(SMB.SMB_COM_READ_RAW)
readRaw['Parameters'] = SMBReadRaw_Parameters()
readRaw['Parameters']['Fid'] = fid
readRaw['Parameters']['Offset'] = offset
readRaw['Parameters']['MaxCount'] = max_size
smb.addCommand(readRaw)
self.sendSMB(smb)
if wait_answer:
data = self.__sess.recv_packet(self.__timeout).get_trailer()
if not data:
# If there is no data it means there was an error
data = self.read_andx(tid, fid, offset, max_size)
return data
return None
def write(self,tid,fid,data, offset = 0, wait_answer=1):
smb = NewSMBPacket()
smb['Flags1'] = 0x18
smb['Flags2'] = 0
smb['Tid'] = tid
write = SMBCommand(SMB.SMB_COM_WRITE)
smb.addCommand(write)
write['Parameters'] = SMBWrite_Parameters()
write['Data'] = SMBWrite_Data()
write['Parameters']['Fid'] = fid
write['Parameters']['Count'] = len(data)
write['Parameters']['Offset'] = offset
write['Parameters']['Remaining'] = len(data)
write['Data']['Data'] = data
self.sendSMB(smb)
if wait_answer:
smb = self.recvSMB()
if smb.isValidAnswer(SMB.SMB_COM_WRITE):
return smb
return None
def write_andx(self,tid,fid,data, offset = 0, wait_answer=1):
smb = NewSMBPacket()
smb['Flags1'] = 0x18
smb['Flags2'] = 0
smb['Tid'] = tid
writeAndX = SMBCommand(SMB.SMB_COM_WRITE_ANDX)
smb.addCommand(writeAndX)
writeAndX['Parameters'] = SMBWriteAndX_Parameters()
writeAndX['Parameters']['Fid'] = fid
writeAndX['Parameters']['Offset'] = offset
writeAndX['Parameters']['WriteMode'] = 8
writeAndX['Parameters']['Remaining'] = len(data)
writeAndX['Parameters']['DataLength'] = len(data)
writeAndX['Parameters']['DataOffset'] = len(smb) # this length already includes the parameter
writeAndX['Data'] = data
self.sendSMB(smb)
if wait_answer:
smb = self.recvSMB()
if smb.isValidAnswer(SMB.SMB_COM_WRITE_ANDX):
return smb
return None
def write_raw(self,tid,fid,data, offset = 0, wait_answer=1):
smb = NewSMBPacket()
smb['Flags1'] = 0x18
smb['Flags2'] = 0
smb['Tid'] = tid
writeRaw = SMBCommand(SMB.SMB_COM_WRITE_RAW)
smb.addCommand(writeRaw)
writeRaw['Parameters'] = SMBWriteRaw_Parameters()
writeRaw['Parameters']['Fid'] = fid
writeRaw['Parameters']['Offset'] = offset
writeRaw['Parameters']['Count'] = len(data)
writeRaw['Parameters']['DataLength'] = 0
writeRaw['Parameters']['DataOffset'] = 0
self.sendSMB(smb)
self.__sess.send_packet(data)
if wait_answer:
smb = self.recvSMB()
if smb.isValidAnswer(SMB.SMB_COM_WRITE_RAW):
return smb
return None
def TransactNamedPipe(self, tid, fid, data = '', noAnswer = 0, waitAnswer = 1, offset = 0):
self.send_trans(tid,pack('<HH', 0x26, fid),'\\PIPE\\\x00','',data, noAnswer = noAnswer)
if noAnswer or not waitAnswer:
return
s = self.recv_packet()
if self.isValidAnswer(s,SMB.SMB_COM_TRANSACTION):
trans = TRANSHeader(s.get_parameter_words(), s.get_buffer())
return trans.get_data()
return None
def nt_create_andx(self,tid,filename):
smb = NewSMBPacket()
smb['Flags1'] = 0x18
smb['Flags2'] = SMB.FLAGS2_LONG_FILENAME
smb['Tid'] = tid
ntCreate = SMBCommand(SMB.SMB_COM_NT_CREATE_ANDX)
ntCreate['Parameters'] = SMBNtCreateAndX_Parameters()
ntCreate['Data'] = SMBNtCreateAndX_Data()
ntCreate['Parameters']['FileNameLength'] = len(filename)
ntCreate['Parameters']['CreateFlags'] = 0x16
ntCreate['Parameters']['AccessMask'] = 0x2019f
ntCreate['Parameters']['CreateOptions'] = 0x40
ntCreate['Data']['FileName'] = filename
smb.addCommand(ntCreate)
self.sendSMB(smb)
while 1:
smb = self.recvSMB()
if smb.isValidAnswer(SMB.SMB_COM_NT_CREATE_ANDX):
# XXX Here we are ignoring the rest of the response
ntCreateResponse = SMBCommand(smb['Data'][0])
ntCreateParameters = SMBNtCreateAndXResponse_Parameters(ntCreateResponse['Parameters'])
return ntCreateParameters['Fid']
def login_pass_the_hash(self, user, lmhash, nthash, domain = ''):
if len(lmhash) % 2: lmhash = '0%s' % lmhash
if len(nthash) % 2: nthash = '0%s' % nthash
if lmhash: lmhash = self.get_ntlmv1_response(a2b_hex(lmhash))
if nthash: nthash = self.get_ntlmv1_response(a2b_hex(nthash))
self._login(user, lmhash, nthash, domain)
def login_plaintext_password(self, name, password, domain = ''):
# Password is only encrypted if the server passed us an "encryption key" during protocol dialect negotiation
if password and self.__ntlm_dialect.get_encryption_key():
lmhash = ntlm.compute_lmhash(password)
nthash = ntlm.compute_nthash(password)
lmhash = self.get_ntlmv1_response(lmhash)
nthash = self.get_ntlmv1_response(nthash)
else:
lmhash = password
nthash = ''
self._login(name, lmhash, nthash, domain)
def logoff(self):
s = SMBPacket()
s.set_command(SMB.SMB_COM_LOGOFF_ANDX)
s.set_parameter_words('\xff\x00\x00\x00')
self.send_smb(s)
s = self.recv_packet()
def list_shared(self):
tid = self.tree_connect_andx('\\\\' + self.__remote_name + '\\IPC$')
buf = StringIO()
try:
self.send_trans(tid, '', '\\PIPE\\LANMAN\0', '\x00\x00WrLeh\0B13BWz\0\x01\x00\xe0\xff', '')
numentries = 0
share_list = [ ]
while 1:
s = self.recv_packet()
if self.isValidAnswer(s,SMB.SMB_COM_TRANSACTION):
has_more, _, transparam, transdata = self.__decode_trans(s.get_parameter_words(), s.get_buffer())
if not numentries:
status, data_offset, numentries = unpack('<HHH', transparam[:6])
buf.write(transdata)
if not has_more:
share_data = buf.getvalue()
offset = 0
for i in range(0, numentries):
name = share_data[offset:string.find(share_data, '\0', offset)]
type, commentoffset = unpack('<HH', share_data[offset + 14:offset + 18])
comment = share_data[commentoffset-data_offset:share_data.find('\0', commentoffset-data_offset)]
offset = offset + 20
share_list.append(SharedDevice(name, type, comment))
return share_list
finally:
buf.close()
self.disconnect_tree(tid)
def list_path(self, service, path = '*', password = None):
path = string.replace(path, '/', '\\')
tid = self.tree_connect_andx('\\\\' + self.__remote_name + '\\' + service, password)
try:
self.trans2(tid, '\x01\x00', '\x00', '\x16\x00\x00\x02\x06\x00\x04\x01\x00\x00\x00\x00' + path + '\x00', '')
while 1:
s = self.recv_packet()
if self.isValidAnswer(s,SMB.SMB_COM_TRANSACTION2):
has_more, _, transparam, transdata = self.__decode_trans(s.get_parameter_words(), s.get_buffer())
sid, searchcnt, eos, erroffset, lastnameoffset = unpack('<HHHHH', transparam)
files = [ ]
offset = 0
data_len = len(transdata)
while offset < data_len:
nextentry, fileindex, lowct, highct, lowat, highat, lowmt, highmt, lowcht, hightcht, loweof, higheof, lowsz, highsz, attrib, longnamelen, easz, shortnamelen = unpack('<lL12LLlLB', transdata[offset:offset + 69])
files.append(SharedFile(highct << 32 | lowct, highat << 32 | lowat, highmt << 32 | lowmt, higheof << 32 | loweof, highsz << 32 | lowsz, attrib, transdata[offset + 70:offset + 70 + shortnamelen], transdata[offset + 94:offset + 94 + longnamelen]))
offset = offset + nextentry
if not nextentry:
break
return files
finally:
self.disconnect_tree(tid)
def retr_file(self, service, filename, callback, mode = SMB_O_OPEN, offset = 0, password = None):
filename = string.replace(filename, '/', '\\')
fid = -1
tid = self.tree_connect_andx('\\\\' + self.__remote_name + '\\' + service, password)
try:
fid, attrib, lastwritetime, datasize, grantedaccess, filetype, devicestate, action, serverfid = self.open_andx(tid, filename, mode, SMB_ACCESS_READ | SMB_SHARE_DENY_WRITE)
if not datasize:
datasize = self.query_file_info(tid, fid)
if self.__ntlm_dialect.is_rawmode():
self.__raw_retr_file(tid, fid, offset, datasize, callback)
else:
self.__nonraw_retr_file(tid, fid, offset, datasize, callback)
finally:
if fid >= 0:
self.close(tid, fid)
self.disconnect_tree(tid)
def stor_file(self, service, filename, callback, mode = SMB_O_CREAT | SMB_O_TRUNC, offset = 0, password = None):
filename = string.replace(filename, '/', '\\')
fid = -1
tid = self.tree_connect_andx('\\\\' + self.__remote_name + '\\' + service, password)
try:
fid, attrib, lastwritetime, datasize, grantedaccess, filetype, devicestate, action, serverfid = self.open_andx(tid, filename, mode, SMB_ACCESS_WRITE | SMB_SHARE_DENY_WRITE)
# If the max_transmit buffer size is more than 16KB, upload process using non-raw mode is actually
# faster than using raw-mode.
if self.__ntlm_dialect.get_max_buffer() < 16384 and self.__ntlm_dialect.is_rawmode():
# Once the __raw_stor_file returns, fid is already closed
self.__raw_stor_file(tid, fid, offset, datasize, callback)
fid = -1
else:
self.__nonraw_stor_file(tid, fid, offset, datasize, callback)
finally:
if fid >= 0:
self.close(tid, fid)
self.disconnect_tree(tid)
def copy(self, src_service, src_path, dest_service, dest_path, callback = None, write_mode = SMB_O_CREAT | SMB_O_TRUNC, src_password = None, dest_password = None):
dest_path = string.replace(dest_path, '/', '\\')
src_path = string.replace(src_path, '/', '\\')
src_tid = self.tree_connect_andx('\\\\' + self.__remote_name + '\\' + src_service, src_password)
dest_tid = -1
try:
if src_service == dest_service:
dest_tid = src_tid
else:
dest_tid = self.tree_connect_andx('\\\\' + self.__remote_name + '\\' + dest_service, dest_password)
dest_fid = self.open_andx(dest_tid, dest_path, write_mode, SMB_ACCESS_WRITE | SMB_SHARE_DENY_WRITE)[0]
src_fid, _, _, src_datasize, _, _, _, _, _ = self.open_andx(src_tid, src_path, SMB_O_OPEN, SMB_ACCESS_READ | SMB_SHARE_DENY_WRITE)
if callback:
callback(0, src_datasize)
max_buf_size = (self.__ntlm_dialect.get_max_buffer() >> 10) << 10
read_offset = 0
write_offset = 0
while read_offset < src_datasize:
self.__send_smb_packet(SMB.SMB_COM_READ_ANDX, 0, 0, src_tid, 0, pack('<BBHHLHHLH', 0xff, 0, 0, src_fid, read_offset, max_buf_size, max_buf_size, 0, 0), '')
while 1:
s = self.recv_packet()
if self.isValidAnswer(s,SMB.SMB_COM_READ_ANDX):
offset = unpack('<H', s.get_parameter_words()[2:4])[0]
data_len, dataoffset = unpack('<HH', s.get_parameter_words()[10+offset:14+offset])
if data_len == len(d):
self.__send_smb_packet(SMB.SMB_COM_WRITE_ANDX, 0, 0, dest_tid, 0, pack('<BBHHLLHHHHH', 0xff, 0, 0, dest_fid, write_offset, 0, 0, 0, 0, data_len, 59), d)
else:
self.__send_smb_packet(SMB.SMB_COM_WRITE_ANDX, 0, 0, dest_tid, 0, pack('<BBHHLLHHHHH', 0xff, 0, 0, dest_fid, write_offset, 0, 0, 0, 0, data_len, 59), d[dataoffset - 59:dataoffset - 59 + data_len])
while 1:
s = self.recv_packet()
if self.isValidAnswer(s,SMB.SMB_COM_WRITE_ANDX):
offset = unpack('<H', s.get_parameter_words()[2:4])[0]
write_offset = write_offset + unpack('<H', s.get_parameter_words()[4+offset:6+offset])[0]
break
read_offset = read_offset + data_len
if callback:
callback(read_offset, src_datasize)
break
finally:
self.disconnect_tree(src_tid)
if dest_tid > -1 and src_service != dest_service:
self.disconnect_tree(dest_tid)
def check_dir(self, service, path, password = None):
tid = self.tree_connect_andx('\\\\' + self.__remote_name + '\\' + service, password)
try:
self.__send_smb_packet(SMB.SMB_COM_CHECK_DIRECTORY, 0x08, 0, tid, 0, '', '\x04' + path + '\x00')
while 1:
s = self.recv_packet()
if self.isValidAnswer(s,SMB.SMB_COM_CHECK_DIRECTORY):
return
finally:
self.disconnect_tree(s.get_tid())
def remove(self, service, path, password = None):
# Perform a list to ensure the path exists
self.list_path(service, path, password)
tid = self.tree_connect_andx('\\\\' + self.__remote_name + '\\' + service, password)
try:
self.__send_smb_packet(SMB.SMB_COM_DELETE, 0x08, 0, tid, 0, pack('<H', ATTR_HIDDEN | ATTR_SYSTEM | ATTR_ARCHIVE), '\x04' + path + '\x00')
while 1:
s = self.recv_packet()
if self.isValidAnswer(s,SMB.SMB_COM_DELETE):
return
finally:
self.disconnect_tree(s.get_tid())
def rmdir(self, service, path, password = None):
# Check that the directory exists
self.check_dir(service, path, password)
tid = self.tree_connect_andx('\\\\' + self.__remote_name + '\\' + service, password)
try:
self.__send_smb_packet(SMB.SMB_COM_DELETE_DIRECTORY, 0x08, 0, tid, 0, '', '\x04' + path + '\x00')
while 1:
s = self.recv_packet()
if self.isValidAnswer(s,SMB.SMB_COM_DELETE_DIRECTORY):
return
finally:
self.disconnect_tree(s.get_tid())
def mkdir(self, service, path, password = None):
tid = self.tree_connect_andx('\\\\' + self.__remote_name + '\\' + service, password)
try:
s = SMBPacket()
s.set_command(SMB.SMB_COM_CREATE_DIRECTORY)
s.set_flags(0x08)
s.set_flags2(0)
s.set_tid(tid)
s.set_parameter_words('') # check this! don't know if i don'thave to put this
s.set_buffer('\x04' + path + '\x00')
self.send_smb(s)
s = self.recv_packet()
if self.isValidAnswer(s,SMB.SMB_COM_CREATE_DIRECTORY):
return 1
return 0
finally:
self.disconnect_tree(s.get_tid())
def rename(self, service, old_path, new_path, password = None):
tid = self.tree_connect_andx('\\\\' + self.__remote_name + '\\' + service, password)
try:
s = SMBPacket()
s.set_command(SMB.SMB_COM_RENAME)
s.set_flags(0x08)
s.set_flags2(0)
s.set_tid(tid)
s.set_parameter_words(pack('<H', ATTR_SYSTEM | ATTR_HIDDEN | ATTR_DIRECTORY))
s.set_buffer('\x04' + old_path + '\x00\x04' + new_path + '\x00')
self.send_smb(s)
s = self.recv_packet()
if self.isValidAnswer(s,SMB.SMB_COM_RENAME):
return 1
return 0
finally:
self.disconnect_tree(s.get_tid())
def browse_domains(self):
return self.__browse_servers(SV_TYPE_DOMAIN_ENUM, SMBDomain, '')
def browse_servers_for_domain(self, domain = None):
if not domain:
domain = self.__server_domain
return self.__browse_servers(SV_TYPE_SERVER | SV_TYPE_PRINTQ_SERVER | SV_TYPE_WFW | SV_TYPE_NT, SMBMachine, domain)
def get_socket(self):
return self.__sess.get_socket()
ERRDOS = { 1: 'Invalid function',
2: 'File not found',
3: 'Invalid directory',
4: 'Too many open files',
5: 'Access denied',
6: 'Invalid file handle. Please file a bug report.',
7: 'Memory control blocks destroyed',
8: 'Out of memory',
9: 'Invalid memory block address',
10: 'Invalid environment',
11: 'Invalid format',
12: 'Invalid open mode',
13: 'Invalid data',
15: 'Invalid drive',
16: 'Attempt to remove server\'s current directory',
17: 'Not the same device',
18: 'No files found',
32: 'Sharing mode conflicts detected',
33: 'Lock request conflicts detected',
80: 'File already exists'
}
ERRSRV = { 1: 'Non-specific error',
2: 'Bad password',
4: 'Access denied',
5: 'Invalid tid. Please file a bug report.',
6: 'Invalid network name',
7: 'Invalid device',
49: 'Print queue full',
50: 'Print queue full',
51: 'EOF on print queue dump',
52: 'Invalid print file handle',
64: 'Command not recognized. Please file a bug report.',
65: 'Internal server error',
67: 'Invalid path',
69: 'Invalid access permissions',
71: 'Invalid attribute mode',
81: 'Server is paused',
82: 'Not receiving messages',
83: 'No room to buffer messages',
87: 'Too many remote user names',
88: 'Operation timeout',
89: 'Out of resources',
91: 'Invalid user handle. Please file a bug report.',
250: 'Temporarily unable to support raw mode for transfer',
251: 'Temporarily unable to support raw mode for transfer',
252: 'Continue in MPX mode',
65535: 'Unsupported function'
}
ERRHRD = { 19: 'Media is write-protected',
20: 'Unknown unit',
21: 'Drive not ready',
22: 'Unknown command',
23: 'CRC error',
24: 'Bad request',
25: 'Seek error',
26: 'Unknown media type',
27: 'Sector not found',
28: 'Printer out of paper',
29: 'Write fault',
30: 'Read fault',
31: 'General failure',
32: 'Open conflicts with an existing open',
33: 'Invalid lock request',
34: 'Wrong disk in drive',
35: 'FCBs not available',
36: 'Sharing buffer exceeded'
}
syntax highlighted by Code2HTML, v. 0.9.1