Module: RubySMB::Client::Negotiation

Included in:
RubySMB::Client
Defined in:
lib/ruby_smb/client/negotiation.rb

Overview

This module holds all of the methods backing the #negotiate method

Instance Method Summary collapse

Instance Method Details

#negotiatevoid

This method returns an undefined value.

Handles the entire SMB Multi-Protocol Negotiation from the Client to the Server. It sets state on the client appropriate to the protocol and capabilites negotiated during the exchange.



12
13
14
15
16
17
18
19
20
21
22
23
24
# File 'lib/ruby_smb/client/negotiation.rb', line 12

def negotiate
  begin
    raw_response    = negotiate_request
    response_packet = negotiate_response(raw_response)
    parse_negotiate_response(response_packet)
  rescue RubySMB::Error::InvalidPacket, Errno::ECONNRESET
    error = "Unable to Negotiate with remote host"
    if smb1 && !smb2
      error << ', SMB1 may be disabled'
    end
    raise RubySMB::Error::NegotiationFailure, error
  end
end

#negotiate_requestString

Creates and dispatches the first Negotiate Request Packet and returns the raw response data.

Returns:

  • the raw binary string containing the response from the server



30
31
32
33
34
35
36
37
# File 'lib/ruby_smb/client/negotiation.rb', line 30

def negotiate_request
  if smb1
    request = smb1_negotiate_request
  elsif smb2
    request = smb2_negotiate_request
  end
  send_recv(request)
end

#negotiate_response(raw_data) ⇒ RubySMB::SMB1::Packet::NegotiateResponseExtended, RubySMB::SMB2::Packet::NegotiateResponse

Takes the raw response data from the server and tries parse it into a valid Response packet object. This method currently assumes that all SMB1 will use Extended Security.

Parameters:

  • the raw binary response from the server

Returns:

  • when the response is an SMB1 Extended Security Negotiate Response Packet

  • when the response is an SMB2 Negotiate Response Packet



46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
# File 'lib/ruby_smb/client/negotiation.rb', line 46

def negotiate_response(raw_data)
  response = nil
  if smb1
    begin
      packet = RubySMB::SMB1::Packet::NegotiateResponseExtended.read raw_data
    rescue Exception => e
      raise RubySMB::Error::InvalidPacket, "Not a Valid SMB1 Negoitate Response #{e.message}"
    end
    if packet.valid?
      response = packet
    end
  end
  if smb2 && response.nil?
    begin
      packet = RubySMB::SMB2::Packet::NegotiateResponse.read raw_data
    rescue Exception => e
      raise RubySMB::Error::InvalidPacket, "Not a Valid SMB2 Negoitate Response #{e.message}"
    end
    response = packet
  end
  if response.nil?
    raise RubySMB::Error::InvalidPacket, "No Valid Negotiate Response found"
  end
  response
end

#parse_negotiate_response(packet) ⇒ void

This method returns an undefined value.

Sets the supported SMB Protocol and whether or not Signing is enabled based on the Negotiate Response Packet.

Parameters:

  • if SMB1 was negotiated

  • if SMB2 was negotiated



78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
# File 'lib/ruby_smb/client/negotiation.rb', line 78

def parse_negotiate_response(packet)
  case packet
    when RubySMB::SMB1::Packet::NegotiateResponseExtended
      self.smb1 = true
      self.smb2 = false
      if packet.parameter_block.security_mode.security_signatures_required == 1
        self.signing_required = true
      else
        self.signing_required = false
      end
      'SMB1'
    when RubySMB::SMB2::Packet::NegotiateResponse
      self.smb1 = false
      self.smb2 = true
      if packet.security_mode.signing_required == 1
        self.signing_required = true
      else
        self.signing_required = false
      end
      'SMB2'
  end
end

#smb1_negotiate_requestRubySMB::SMB1::Packet::NegotiateRequest

Create a SMB1::Packet::NegotiateRequest packet with the dialects filled in based on the protocol options set on the Client.

Returns:

  • a completed SMB1 Negotiate Request packet



106
107
108
109
110
111
112
113
114
115
116
117
# File 'lib/ruby_smb/client/negotiation.rb', line 106

def smb1_negotiate_request
  packet = RubySMB::SMB1::Packet::NegotiateRequest.new
  # Default to always enabling Extended Security. It simplifies the Negotiation process
  # while being gauranteed to work with any modern Windows system. We can get more sophisticated
  # with switching this on and off at a later date if the need arises.
  packet.smb_header.flags2.extended_security = 1
  # There is no real good reason to ever send an SMB1 Negotiate packet
  # to Negotiate strictly SMB2, but the protocol WILL support it
  packet.add_dialect(SMB1_DIALECT_SMB1_DEFAULT) if smb1
  packet.add_dialect(SMB1_DIALECT_SMB2_DEFAULT) if smb2
  packet
end

#smb2_negotiate_requestObject

Create a SMB2::Packet::NegotiateRequest packet with the default dialect added. This will never be used when we may want to communicate over SMB1

@ return [RubySMB::SMB2::Packet::NegotiateRequest] a completed SMB2 Negotiate Request packet



124
125
126
127
128
129
130
# File 'lib/ruby_smb/client/negotiation.rb', line 124

def smb2_negotiate_request
  packet = RubySMB::SMB2::Packet::NegotiateRequest.new
  packet.security_mode.signing_enabled = 1
  packet.add_dialect(SMB2_DIALECT_DEFAULT)
  packet.client_guid = SecureRandom.random_bytes(16)
  packet
end