require 'rex/proto/smb'
require 'rex/proto/dcerpc'
require 'rex/encoder/ndr'

module Msf

###
#
# This mixin provides utility methods for interacting with a SMB/CIFS service on
# a remote machine.  These methods may generally be useful in the context of
# exploitation.  This mixin extends the Tcp exploit mixin. Only one SMB
# service can be accessed at a time using this class.
#
###

module Exploit::Remote::SMB

	include Exploit::Remote::Tcp
	SIMPLE = Rex::Proto::SMB::SimpleClient
	XCEPT  = Rex::Proto::SMB::Exceptions
	CONST  = Rex::Proto::SMB::Constants
	
	# Alias over the Rex DCERPC protocol modules
	DCERPCPacket   = Rex::Proto::DCERPC::Packet
	DCERPCClient   = Rex::Proto::DCERPC::Client
	DCERPCResponse = Rex::Proto::DCERPC::Response
	DCERPCUUID     = Rex::Proto::DCERPC::UUID
	NDR            = Rex::Encoder::NDR

	def initialize(info = {})
		super
	
		register_evasion_options(
		[
			OptBool.new('SMB::pipe_evasion',     [ true, 'Enable segmented read/writes for SMB Pipes', 'False']),
			OptInt.new('SMB::pipe_write_min_size', [ true, 'Minimum buffer size for pipe writes',  1]),
			OptInt.new('SMB::pipe_write_max_size', [ true, 'Maximum buffer size for pipe writes', 1024]),
			OptInt.new('SMB::pipe_read_min_size',  [ true, 'Minimum buffer size for pipe reads',  1]),
			OptInt.new('SMB::pipe_read_max_size',  [ true, 'Maximum buffer size for pipe reads', 1024]),
			OptInt.new('SMB::pad_data_level',  [ true, 'Place extra padding between headers and data (level 0-3)', 0]),
			OptInt.new('SMB::pad_file_level',  [ true, 'Obscure path names used in open/create (level 0-3)', 0]),
			OptInt.new('SMB::obscure_trans_pipe_level',  [ true, 'Obscure PIPE string in TransNamedPipe (level 0-3)', 0]),
			
		], Msf::Exploit::Remote::SMB)

		register_advanced_options(
		[
			OptBool.new('SMBDirect', [ true, 'The target port is a raw SMB service (not NetBIOS)', 'True' ]),
			OptString.new('SMBUser', [ false, 'The username to authenticate as', '']),
			OptString.new('SMBPass', [ false, 'The password for the specified username', '']),
			OptString.new('SMBDomain',  [ false, 'The Windows domain to use for authentication', 'WORKGROUP']),
			OptString.new('SMBName', [ true, 'The NetBIOS hostname (required for port 139 connections)', '*SMBSERVER'])
		], Msf::Exploit::Remote::SMB)

		register_options(
			[
				Opt::RHOST,
				OptInt.new('RPORT', [ true, 'Set the SMB service port', 445])	
			], Msf::Exploit::Remote::SMB)
	end

	def connect()
		
		disconnect()
		
		super
	
		self.simple = SIMPLE.new(self.sock, datastore['SMBDirect'])

		# setup pipe evasion foo
		if datastore['SMB::pipe_evasion']
			# XXX - insert code to change the instance of the read/write functions to do segmentation
		end
		
		if (datastore['SMB::pad_data_level'])
			self.simple.client.evasion_opts['pad_data'] = datastore['SMB::pad_data_level']
		end

		if (datastore['SMB::pad_file_level'])
			self.simple.client.evasion_opts['pad_file'] = datastore['SMB::pad_file_level']
		end

		if (datastore['SMB::obscure_trans_pipe_level'])
			self.simple.client.evasion_opts['obscure_trans_pipe'] = datastore['SMB::obscure_trans_pipe_level']
		end
	end

	# Convert a standard ASCII string to 16-bit Unicode
	def unicode(str)
		Rex::Text.to_unicode(str)
	end
	
	# This method establishes a SMB session over the default socket
	def smb_login
		simple.login(
			datastore['SMBName'], 
			datastore['SMBUser'],
			datastore['SMBPass'],
			datastore['SMBDomain']
		)
		
		simple.connect('IPC$')
	end

	# This method returns the native operating system of the peer	
	def smb_peer_os
		self.simple.client.peer_native_os
	end
	
	# This method returns the native lanman version of the peer
	def smb_peer_lm
		self.simple.client.peer_native_lm
	end
	
	# This method opens a handle to an IPC pipe
	def smb_create(pipe)
		self.simple.create_pipe(pipe)
	end
	
	def smb_hostname
		datastore['SMBName'] || '*SMBSERVER'
	end
	
	attr_accessor :simple

end
end
