Skip to content

Commit

Permalink
Handle SMB2 compound related requests
Browse files Browse the repository at this point in the history
  • Loading branch information
zeroSteiner committed Mar 28, 2024
1 parent 3e954a2 commit 3dceda0
Show file tree
Hide file tree
Showing 4 changed files with 61 additions and 6 deletions.
27 changes: 24 additions & 3 deletions lib/ruby_smb/server/server_client.rb
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ def initialize(server, dispatcher)

# session id => session instance
@session_table = {}
@smb2_related_operations_state = {}
end

#
Expand Down Expand Up @@ -334,9 +335,23 @@ def handle_smb1(raw_request, header)
# @raise [NotImplementedError] Raised when the requested operation is not
# supported.
def handle_smb2(raw_request, header)
session = @session_table[header.session_id]
session_required = !(header.command == SMB2::Commands::SESSION_SETUP && header.session_id == 0)

if header.flags.related_operations == 0
@smb2_related_operations_state.clear
session = @session_table[header.session_id]
@smb2_related_operations_state[:session_id] = header.session_id
else
# see: https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-smb2/46dd4182-62d3-4e30-9fe5-e2ec124edca1
if @smb2_related_operations_state.fetch(:session_id) == 0 && session_required
response = SMB2::Packet::ErrorPacket.new
response.smb2_header.nt_status = WindowsError::NTStatus::STATUS_INVALID_PARAMETER
return response
end
session = @session_table[@smb2_related_operations_state[:session_id]]
end

if session.nil? && !(header.command == SMB2::Commands::SESSION_SETUP && header.session_id == 0)
if session.nil? && session_required
response = SMB2::Packet::ErrorPacket.new
response.smb2_header.nt_status = WindowsError::NTStatus::STATUS_USER_SESSION_DELETED
return response
Expand Down Expand Up @@ -387,7 +402,13 @@ def handle_smb2(raw_request, header)
end

logger.debug("Dispatching request to #{dispatcher} (session: #{session.inspect})")
send(dispatcher, request, session)
response = send(dispatcher, request, session)

if response.is_a?(SMB2::Packet::ErrorPacket)
@smb2_related_operations_state.clear
end

response
end

def _handle_smb2(raw_request)
Expand Down
2 changes: 2 additions & 0 deletions lib/ruby_smb/server/server_client/session_setup.rb
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,8 @@ def do_session_setup_smb2(request, session)
update_preauth_hash(response)
end

@smb2_related_operations_state[:session_id] = session_id

response
end

Expand Down
36 changes: 33 additions & 3 deletions lib/ruby_smb/server/server_client/share_io.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,46 @@ def proxy_share_io_smb1(request, session)
alias :do_transactions2_smb1 :proxy_share_io_smb1

def proxy_share_io_smb2(request, session)
# see: https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-smb2/9a639360-87be-4d49-a1dd-4c6be0c020bd
share_processor = session.tree_connect_table[request.smb2_header.tree_id]
if request.smb2_header.flags.related_operations == 0
# see: https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-smb2/9a639360-87be-4d49-a1dd-4c6be0c020bd
share_processor = session.tree_connect_table[request.smb2_header.tree_id]
@smb2_related_operations_state[:tree_id] = request.smb2_header.tree_id
else
# see: https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-smb2/46dd4182-62d3-4e30-9fe5-e2ec124edca1
if @smb2_related_operations_state.fetch(:tree_id) == 0
response = SMB2::Packet::ErrorPacket.new
response.smb2_header.nt_status = WindowsError::NTStatus::STATUS_INVALID_PARAMETER
return response
end
share_processor = session.tree_connect_table[@smb2_related_operations_state[:tree_id]]
end

if share_processor.nil?
response = SMB2::Packet::ErrorPacket.new
response.smb2_header.nt_status = WindowsError::NTStatus::STATUS_NETWORK_NAME_DELETED
return response
end

if request.field_names.include?(:file_id)
if request.smb2_header.flags.related_operations == 0
@smb2_related_operations_state[:file_id] = request.file_id
elsif @smb2_related_operations_state[:file_id].nil?
response = SMB2::Packet::ErrorPacket.new
response.smb2_header.nt_status = WindowsError::NTStatus::STATUS_INVALID_HANDLE
return response
else
request.file_id = @smb2_related_operations_state[:file_id]
end
end

logger.debug("Received #{SMB2::Commands.name(request.smb2_header.command)} request for share: #{share_processor.provider.name}")
share_processor.share_io(__callee__, request)
response = share_processor.share_io(__callee__, request)

if response.field_names.include?(:file_id)
@smb2_related_operations_state[:file_id] = response.file_id
end

response
end

alias :do_close_smb2 :proxy_share_io_smb2
Expand Down
2 changes: 2 additions & 0 deletions lib/ruby_smb/server/server_client/tree_connect.rb
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,8 @@ def do_tree_connect_smb2(request, session)
session.tree_connect_table[tree_id] = share_processor = share_provider.new_processor(self, session)
response.maximal_access = share_processor.maximal_access

@smb2_related_operations_state[:tree_id] = tree_id

response
end

Expand Down

0 comments on commit 3dceda0

Please sign in to comment.