ruby_smb 2.0.7 → 2.0.11

Sign up to get free protection for your applications and to get access to all the features.
Files changed (53) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/.github/workflows/verify.yml +57 -0
  4. data/README.md +0 -1
  5. data/examples/auth_capture.rb +71 -0
  6. data/lib/ruby_smb/client/negotiation.rb +11 -13
  7. data/lib/ruby_smb/client.rb +32 -27
  8. data/lib/ruby_smb/compression/lznt1.rb +164 -0
  9. data/lib/ruby_smb/compression.rb +7 -0
  10. data/lib/ruby_smb/dialect.rb +45 -0
  11. data/lib/ruby_smb/dispatcher/base.rb +1 -1
  12. data/lib/ruby_smb/dispatcher/socket.rb +1 -1
  13. data/lib/ruby_smb/gss/provider/authenticator.rb +42 -0
  14. data/lib/ruby_smb/gss/provider/ntlm.rb +303 -0
  15. data/lib/ruby_smb/gss/provider.rb +35 -0
  16. data/lib/ruby_smb/gss.rb +56 -63
  17. data/lib/ruby_smb/ntlm.rb +45 -0
  18. data/lib/ruby_smb/server/server_client/negotiation.rb +155 -0
  19. data/lib/ruby_smb/server/server_client/session_setup.rb +82 -0
  20. data/lib/ruby_smb/server/server_client.rb +163 -0
  21. data/lib/ruby_smb/server.rb +54 -0
  22. data/lib/ruby_smb/signing.rb +59 -0
  23. data/lib/ruby_smb/smb1/packet/negotiate_response.rb +11 -11
  24. data/lib/ruby_smb/smb1/packet/negotiate_response_extended.rb +1 -1
  25. data/lib/ruby_smb/smb1/packet/session_setup_request.rb +1 -1
  26. data/lib/ruby_smb/smb1/tree.rb +1 -1
  27. data/lib/ruby_smb/smb2/negotiate_context.rb +18 -2
  28. data/lib/ruby_smb/smb2/packet/compression_transform_header.rb +4 -0
  29. data/lib/ruby_smb/smb2/packet/negotiate_request.rb +9 -0
  30. data/lib/ruby_smb/smb2/packet/negotiate_response.rb +0 -1
  31. data/lib/ruby_smb/smb2/packet/session_setup_response.rb +2 -2
  32. data/lib/ruby_smb/smb2/packet/tree_connect_request.rb +1 -1
  33. data/lib/ruby_smb/smb2/tree.rb +1 -1
  34. data/lib/ruby_smb/smb2.rb +3 -1
  35. data/lib/ruby_smb/version.rb +1 -1
  36. data/lib/ruby_smb.rb +5 -3
  37. data/spec/lib/ruby_smb/client_spec.rb +24 -16
  38. data/spec/lib/ruby_smb/compression/lznt1_spec.rb +32 -0
  39. data/spec/lib/ruby_smb/gss/provider/ntlm/account_spec.rb +32 -0
  40. data/spec/lib/ruby_smb/gss/provider/ntlm/authenticator_spec.rb +101 -0
  41. data/spec/lib/ruby_smb/gss/provider/ntlm/os_version_spec.rb +32 -0
  42. data/spec/lib/ruby_smb/gss/provider/ntlm_spec.rb +113 -0
  43. data/spec/lib/ruby_smb/server/server_client_spec.rb +156 -0
  44. data/spec/lib/ruby_smb/server_spec.rb +32 -0
  45. data/spec/lib/ruby_smb/smb1/tree_spec.rb +4 -4
  46. data/spec/lib/ruby_smb/smb2/negotiate_context_spec.rb +2 -2
  47. data/spec/lib/ruby_smb/smb2/tree_spec.rb +5 -5
  48. data/spec/spec_helper.rb +1 -1
  49. data.tar.gz.sig +3 -1
  50. metadata +30 -4
  51. metadata.gz.sig +0 -0
  52. data/.travis.yml +0 -6
  53. data/lib/ruby_smb/client/signing.rb +0 -64
@@ -0,0 +1,54 @@
1
+ require 'socket'
2
+
3
+ module RubySMB
4
+ # This class provides the SMB server core. Settings that are relevant server wide are managed by this object.
5
+ # Currently, the server only supports negotiating and authenticating requests. No other server functionality is
6
+ # available at this time. The negotiating and authentication is supported for SMB versions 1 through 3.1.1.
7
+ class Server
8
+ require 'ruby_smb/server/server_client'
9
+ require 'ruby_smb/gss/provider/ntlm'
10
+
11
+ Connection = Struct.new(:client, :thread)
12
+
13
+ # @param server_sock the socket on which the server should listen
14
+ # @param [Gss::Provider] the authentication provider
15
+ def initialize(server_sock: nil, gss_provider: nil)
16
+ server_sock = ::TCPServer.new(445) if server_sock.nil?
17
+
18
+ @guid = Random.new.bytes(16)
19
+ @socket = server_sock
20
+ @connections = []
21
+ @gss_provider = gss_provider || Gss::Provider::NTLM.new
22
+ # reject the wildcard dialect because it's not a real dialect we can use for this purpose
23
+ @dialects = RubySMB::Dialect::ALL.keys.reject { |dialect| dialect == "0x%04x" % RubySMB::SMB2::SMB2_WILDCARD_REVISION }.reverse
24
+ end
25
+
26
+ # Run the server and accept any connections. For each connection, the block will be executed if specified. When the
27
+ # block returns false, the loop will exit and the server will no long accept new connections.
28
+ def run(&block)
29
+ loop do
30
+ sock = @socket.accept
31
+ server_client = ServerClient.new(self, RubySMB::Dispatcher::Socket.new(sock))
32
+ @connections << Connection.new(server_client, Thread.new { server_client.run })
33
+
34
+ break unless block.nil? || block.call(server_client)
35
+ end
36
+ end
37
+
38
+ # The dialects that this server will negotiate with clients, in ascending order of preference.
39
+ # @!attribute [r] dialects
40
+ # @return [Array<String>]
41
+ attr_accessor :dialects
42
+
43
+ # The GSS Provider instance that this server will use to authenticate
44
+ # incoming client connections.
45
+ # @!attribute [r] gss_provider
46
+ # @return [RubySMB::Gss::Provider::Base]
47
+ attr_reader :gss_provider
48
+
49
+ # The 16 byte GUID that uniquely identifies this server instance.
50
+ # @!attribute [r] guid
51
+ attr_reader :guid
52
+ end
53
+ end
54
+
@@ -0,0 +1,59 @@
1
+ module RubySMB
2
+ # Contains the methods for handling packet signing
3
+ module Signing
4
+ # The NTLM Session Key used for signing
5
+ # @!attribute [rw] session_key
6
+ # @return [String]
7
+ attr_accessor :session_key
8
+
9
+ # Take an SMB1 packet and sign it.
10
+ #
11
+ # @param packet [RubySMB::GenericPacket] the packet to sign
12
+ # @return [RubySMB::GenericPacket] the signed packet
13
+ def smb1_sign(packet)
14
+ # Pack the Sequence counter into a int64le
15
+ packed_sequence_counter = [sequence_counter].pack('Q<')
16
+ packet.smb_header.security_features = packed_sequence_counter
17
+ signature = OpenSSL::Digest::MD5.digest(session_key + packet.to_binary_s)[0, 8]
18
+ packet.smb_header.security_features = signature
19
+ @sequence_counter += 1
20
+
21
+ packet
22
+ end
23
+
24
+ # Take an SMB2 packet and sign it.
25
+ #
26
+ # @param packet [RubySMB::GenericPacket] the packet to sign
27
+ # @return [RubySMB::GenericPacket] the signed packet
28
+ def smb2_sign(packet)
29
+ packet.smb2_header.flags.signed = 1
30
+ packet.smb2_header.signature = "\x00" * 16
31
+ hmac = OpenSSL::HMAC.digest(OpenSSL::Digest.new('SHA256'), session_key, packet.to_binary_s)
32
+ packet.smb2_header.signature = hmac[0, 16]
33
+
34
+ packet
35
+ end
36
+
37
+ # Take an SMB3 packet and sign it.
38
+ #
39
+ # @param packet [RubySMB::GenericPacket] the packet to sign
40
+ # @return [RubySMB::GenericPacket] the signed packet
41
+ def smb3_sign(packet)
42
+ case @dialect
43
+ when '0x0300', '0x0302'
44
+ signing_key = Crypto::KDF.counter_mode(@session_key, "SMB2AESCMAC\x00", "SmbSign\x00")
45
+ when '0x0311'
46
+ signing_key = Crypto::KDF.counter_mode(@session_key, "SMBSigningKey\x00", @preauth_integrity_hash_value)
47
+ else
48
+ raise Error::SigningError.new("Dialect #{@dialect.inspect} is incompatible with SMBv3 signing")
49
+ end
50
+
51
+ packet.smb2_header.flags.signed = 1
52
+ packet.smb2_header.signature = "\x00" * 16
53
+ hmac = OpenSSL::CMAC.digest('AES', signing_key, packet.to_binary_s)
54
+ packet.smb2_header.signature = hmac[0, 16]
55
+
56
+ packet
57
+ end
58
+ end
59
+ end
@@ -8,17 +8,17 @@ module RubySMB
8
8
 
9
9
  # An SMB_Parameters Block as defined by the {NegotiateResponse}.
10
10
  class ParameterBlock < RubySMB::SMB1::ParameterBlock
11
- uint16 :dialect_index, label: 'Dialect Index'
12
- security_mode :security_mode
13
- uint16 :max_mpx_count, label: 'Max Multiplex Count'
14
- uint16 :max_number_vcs, label: 'Max Virtual Circuits'
15
- uint32 :max_buffer_size, label: 'Max Buffer Size'
16
- uint32 :max_raw_size, label: 'Max Raw Size'
17
- uint32 :session_key, label: 'Session Key'
18
- capabilities :capabilities
19
- file_time :system_time, label: 'Server System Time'
20
- int16 :server_time_zone, label: 'Server TimeZone'
21
- uint8 :challenge_length, label: 'Challenge Length', initial_value: 0x08
11
+ uint16 :dialect_index, label: 'Dialect Index'
12
+ security_mode :security_mode, onlyif: -> { dialect_index != 0xffff }
13
+ uint16 :max_mpx_count, label: 'Max Multiplex Count', onlyif: -> { dialect_index != 0xffff }
14
+ uint16 :max_number_vcs, label: 'Max Virtual Circuits', onlyif: -> { dialect_index != 0xffff }
15
+ uint32 :max_buffer_size, label: 'Max Buffer Size', onlyif: -> { dialect_index != 0xffff }
16
+ uint32 :max_raw_size, label: 'Max Raw Size', onlyif: -> { dialect_index != 0xffff }
17
+ uint32 :session_key, label: 'Session Key', onlyif: -> { dialect_index != 0xffff }
18
+ capabilities :capabilities, onlyif: -> { dialect_index != 0xffff }
19
+ file_time :system_time, label: 'Server System Time', onlyif: -> { dialect_index != 0xffff }
20
+ int16 :server_time_zone, label: 'Server TimeZone', onlyif: -> { dialect_index != 0xffff }
21
+ uint8 :challenge_length, label: 'Challenge Length', initial_value: 0x08, onlyif: -> { dialect_index != 0xffff }
22
22
  end
23
23
 
24
24
  # An SMB_Data Block as defined by the {NegotiateResponse}
@@ -8,7 +8,7 @@ module RubySMB
8
8
 
9
9
  # An SMB_Parameters Block as defined by the {NegotiateResponseExtended}.
10
10
  class ParameterBlock < RubySMB::SMB1::ParameterBlock
11
- uint16 :dialect_index, label: 'Dialect Index'
11
+ uint16 :dialect_index, label: 'Dialect Index'
12
12
  security_mode :security_mode
13
13
  uint16 :max_mpx_count, label: 'Max Multiplex Count'
14
14
  uint16 :max_number_vcs, label: 'Max Virtual Circuits'
@@ -47,7 +47,7 @@ module RubySMB
47
47
 
48
48
  # Takes an NTLM Type 3 Message and creates the GSS Security Blob
49
49
  # for it and sets it in the {RubySMB::SMB1::Packet::SessionSetupRequest::DataBlock#security_blob}
50
- # field. It also automaticaly sets the length in
50
+ # field. It also automatically sets the length in
51
51
  # {RubySMB::SMB1::Packet::SessionSetupRequest::ParameterBlock#security_blob_length}
52
52
  #
53
53
  # @param type3_message [String] the serialized Type 3 NTLM message
@@ -60,7 +60,7 @@ module RubySMB
60
60
  opts = opts.dup
61
61
  opts[:filename] = opts[:filename].dup
62
62
  opts[:filename].prepend('\\') unless opts[:filename].start_with?('\\')
63
- open_file(opts)
63
+ open_file(**opts)
64
64
  end
65
65
 
66
66
  # Open a file on the remote share.
@@ -69,9 +69,22 @@ module RubySMB
69
69
  class NetnameNegotiateContextId < BinData::Record
70
70
  endian :little
71
71
 
72
- stringz16 :net_name, label: 'Net Name'
72
+ count_bytes_remaining :bytes_remaining
73
+ default_parameter data_length: nil
74
+ hide :bytes_remaining
75
+
76
+ string16 :net_name, label: 'Net Name', read_length: -> { data_length.nil? ? bytes_remaining : data_length }
73
77
  end
74
78
 
79
+ # An SMB2 TRANSPORT_CAPABILITIES context struct as defined in
80
+ # [2.2.3.1.5 SMB2_TRANSPORT_CAPABILITIES](https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-smb2/450a1888-a645-4988-8638-5a11f4617545)
81
+ class TransportCapabilities < BinData::Record
82
+ SMB2_ACCEPT_TRANSPORT_LEVEL_SECURITY = 1 # Transport security is offered to skip SMB2 encryption on this connection.
83
+
84
+ endian :little
85
+
86
+ uint32 :flags, label: 'Flags'
87
+ end
75
88
 
76
89
  # An SMB2 NEGOTIATE_CONTEXT struct as defined in
77
90
  # [2.2.3.1 SMB2 NEGOTIATE_CONTEXT Request Values](https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-smb2/15332256-522e-4a53-8cd7-0bd17678a2f7)
@@ -84,6 +97,8 @@ module RubySMB
84
97
  SMB2_COMPRESSION_CAPABILITIES = 0x0003
85
98
  # The NegotiateContext Data field contains the server name to which the client connects.
86
99
  SMB2_NETNAME_NEGOTIATE_CONTEXT_ID = 0x0005
100
+ # The NegotiateContext Data field contains the transport capabilities, as specified in section 2.2.3.1.5.
101
+ SMB2_TRANSPORT_CAPABILITIES = 0x0006
87
102
 
88
103
  endian :little
89
104
 
@@ -95,7 +110,8 @@ module RubySMB
95
110
  preauth_integrity_capabilities SMB2_PREAUTH_INTEGRITY_CAPABILITIES, label: 'Preauthentication Integrity Capabilities'
96
111
  encryption_capabilities SMB2_ENCRYPTION_CAPABILITIES, label: 'Encryption Capabilities'
97
112
  compression_capabilities SMB2_COMPRESSION_CAPABILITIES, label: 'Compression Capabilities'
98
- netname_negotiate_context_id SMB2_NETNAME_NEGOTIATE_CONTEXT_ID, label: 'Netname Negotiate Context ID'
113
+ netname_negotiate_context_id SMB2_NETNAME_NEGOTIATE_CONTEXT_ID, label: 'Netname Negotiate Context ID', data_length: :data_length
114
+ transport_capabilities SMB2_TRANSPORT_CAPABILITIES, label: 'Transport Capabilities'
99
115
  end
100
116
 
101
117
  def pad_length
@@ -3,6 +3,9 @@ module RubySMB
3
3
  module Packet
4
4
  # An SMB2 COMPRESSION_TRANSFORM_HEADER Packet as defined in
5
5
  # [2.2.42 SMB2 COMPRESSION_TRANSFORM_HEADER](https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-smb2/1d435f21-9a21-4f4c-828e-624a176cf2a0)
6
+ # NOTE: On 2021-04-06 the official documentation split the definition of COMPRESSION_TRANSFORM_HEADER into the following two variants:
7
+ # * SMB2_COMPRESSION_TRANSFORM_HEADER_CHAINED
8
+ # * SMB2_COMPRESSION_TRANSFORM_HEADER_UNCHAINED
6
9
  class CompressionTransformHeader < BinData::Record
7
10
  endian :little
8
11
 
@@ -11,6 +14,7 @@ module RubySMB
11
14
  uint16 :compression_algorithm, label: 'Compression Algorithm'
12
15
  uint16 :flags, label: 'Flags'
13
16
  uint32 :offset, label: 'Offset / Length'
17
+ array :compressed_data, label: 'Compressed Data', type: :uint8, read_until: :eof
14
18
  end
15
19
 
16
20
  # An SMB2 SMB2_COMPRESSION_TRANSFORM_HEADER_PAYLOAD Packet as defined in
@@ -64,6 +64,15 @@ module RubySMB
64
64
  self.negotiate_context_list
65
65
  end
66
66
 
67
+ # Find the first Negotiate Context structure that matches the given
68
+ # context type
69
+ #
70
+ # @param [Integer] the Negotiate Context structure you wish to add
71
+ # @return [NegotiateContext] the Negotiate Context structure or nil if
72
+ # not found
73
+ def find_negotiate_context(type)
74
+ negotiate_context_list.find { |nc| nc.context_type == type }
75
+ end
67
76
 
68
77
  private
69
78
 
@@ -59,7 +59,6 @@ module RubySMB
59
59
  self.negotiate_context_list
60
60
  end
61
61
 
62
-
63
62
  private
64
63
 
65
64
  # Determines the correct length for the padding, so that the next
@@ -11,8 +11,8 @@ module RubySMB
11
11
  uint16 :structure_size, label: 'Structure Size', initial_value: 9
12
12
  session_flags :session_flags
13
13
  uint16 :security_buffer_offset, label: 'Security Buffer Offset', initial_value: 0x48
14
- uint16 :security_buffer_length, label: 'Security Buffer Length'
15
- string :buffer, label: 'Security Buffer', length: -> { security_buffer_length }
14
+ uint16 :security_buffer_length, label: 'Security Buffer Length', initial_value: -> { buffer.length }
15
+ string :buffer, label: 'Security Buffer', read_length: -> { security_buffer_length }
16
16
 
17
17
  def initialize_instance
18
18
  super
@@ -101,7 +101,7 @@ module RubySMB
101
101
  path.to_binary_s.length
102
102
  end
103
103
  end
104
- string16 :path, label: 'Path Buffer', onlyif: -> { flags != SMB2_TREE_CONNECT_FLAG_EXTENSION_PRESENT }
104
+ string16 :path, label: 'Path Buffer', onlyif: -> { flags != SMB2_TREE_CONNECT_FLAG_EXTENSION_PRESENT }, read_length: -> { path_length }
105
105
  tree_connect_request_extension :tree_connect_request_extension, label: 'Tree Connect Request Extension', onlyif: -> { flags == SMB2_TREE_CONNECT_FLAG_EXTENSION_PRESENT }
106
106
  end
107
107
  end
@@ -61,7 +61,7 @@ module RubySMB
61
61
  opts = opts.dup
62
62
  opts[:filename] = opts[:filename].dup
63
63
  opts[:filename] = opts[:filename][1..-1] if opts[:filename].start_with? '\\'
64
- open_file(opts)
64
+ open_file(**opts)
65
65
  end
66
66
 
67
67
  def open_file(filename:, attributes: nil, options: nil, disposition: RubySMB::Dispositions::FILE_OPEN,
data/lib/ruby_smb/smb2.rb CHANGED
@@ -1,10 +1,12 @@
1
1
  module RubySMB
2
2
  # A packet parsing and manipulation library for the SMB2 protocol
3
3
  #
4
- # [[MS-SMB2] Server Mesage Block (SMB) Protocol Versions 2 and 3](https://msdn.microsoft.com/en-us/library/cc246482.aspx)
4
+ # [[MS-SMB2] Server Message Block (SMB) Protocol Versions 2 and 3](https://msdn.microsoft.com/en-us/library/cc246482.aspx)
5
5
  module SMB2
6
6
  # Protocol ID value. Translates to \xFESMB
7
7
  SMB2_PROTOCOL_ID = 0xFE534D42
8
+ # Wildcard revision, see: https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-smb2/63abf97c-0d09-47e2-88d6-6bfa552949a5
9
+ SMB2_WILDCARD_REVISION = 0x02ff
8
10
 
9
11
  require 'ruby_smb/smb2/info_type'
10
12
  require 'ruby_smb/smb2/commands'
@@ -1,3 +1,3 @@
1
1
  module RubySMB
2
- VERSION = '2.0.7'.freeze
2
+ VERSION = '2.0.11'.freeze
3
3
  end
data/lib/ruby_smb.rb CHANGED
@@ -8,8 +8,8 @@ require 'windows_error'
8
8
  require 'windows_error/nt_status'
9
9
  # A packet parsing and manipulation library for the SMB1 and SMB2 protocols
10
10
  #
11
- # [[MS-SMB] Server Mesage Block (SMB) Protocol Version 1](https://msdn.microsoft.com/en-us/library/cc246482.aspx)
12
- # [[MS-SMB2] Server Mesage Block (SMB) Protocol Versions 2 and 3](https://msdn.microsoft.com/en-us/library/cc246482.aspx)
11
+ # [[MS-SMB] Server Message Block (SMB) Protocol Version 1](https://msdn.microsoft.com/en-us/library/cc246482.aspx)
12
+ # [[MS-SMB2] Server Message Block (SMB) Protocol Versions 2 and 3](https://msdn.microsoft.com/en-us/library/cc246482.aspx)
13
13
  module RubySMB
14
14
  require 'ruby_smb/error'
15
15
  require 'ruby_smb/dispositions'
@@ -21,9 +21,11 @@ module RubySMB
21
21
  require 'ruby_smb/generic_packet'
22
22
  require 'ruby_smb/dispatcher'
23
23
  require 'ruby_smb/version'
24
- require 'ruby_smb/version'
25
24
  require 'ruby_smb/smb2'
26
25
  require 'ruby_smb/smb1'
27
26
  require 'ruby_smb/client'
28
27
  require 'ruby_smb/crypto'
28
+ require 'ruby_smb/compression'
29
+ require 'ruby_smb/server'
30
+ require 'ruby_smb/dialect'
29
31
  end
@@ -209,6 +209,8 @@ RSpec.describe RubySMB::Client do
209
209
 
210
210
  context 'when signing' do
211
211
  it 'calls #smb1_sign if it is an SMB1 packet' do
212
+ allow(client).to receive(:signing_required).and_return(true)
213
+ allow(client).to receive(:session_key).and_return(Random.new.bytes(16))
212
214
  expect(client).to receive(:smb1_sign).with(smb1_request).and_call_original
213
215
  client.send_recv(smb1_request)
214
216
  end
@@ -223,15 +225,11 @@ RSpec.describe RubySMB::Client do
223
225
 
224
226
  it 'calls #smb2_sign if it is an SMB2 client' do
225
227
  allow(smb2_client).to receive(:is_status_pending?).and_return(false)
228
+ allow(smb2_client).to receive(:signing_required).and_return(true)
229
+ allow(smb2_client).to receive(:session_key).and_return(Random.new.bytes(16))
226
230
  expect(smb2_client).to receive(:smb2_sign).with(smb2_request).and_call_original
227
231
  smb2_client.send_recv(smb2_request)
228
232
  end
229
-
230
- it 'calls #smb3_sign if it is an SMB3 client' do
231
- allow(smb3_client).to receive(:is_status_pending?).and_return(false)
232
- expect(smb3_client).to receive(:smb3_sign).with(smb2_request).and_call_original
233
- smb3_client.send_recv(smb2_request)
234
- end
235
233
  end
236
234
  end
237
235
 
@@ -809,6 +807,18 @@ RSpec.describe RubySMB::Client do
809
807
  smb1_extended_response.to_binary_s
810
808
  }
811
809
 
810
+ let(:smb1_response) {
811
+ packet = RubySMB::SMB1::Packet::NegotiateResponse.new
812
+ smb1_capabilities_dup = smb1_capabilities.dup
813
+ smb1_capabilities_dup[:extended_security] = 0
814
+
815
+ packet.parameter_block.capabilities = smb1_capabilities_dup
816
+ packet
817
+ }
818
+ let(:smb1_response_raw) {
819
+ smb1_response.to_binary_s
820
+ }
821
+
812
822
  let(:smb2_response) { RubySMB::SMB2::Packet::NegotiateResponse.new(dialect_revision: 0x200) }
813
823
  let(:smb3_response) { RubySMB::SMB2::Packet::NegotiateResponse.new(dialect_revision: 0x300) }
814
824
 
@@ -997,10 +1007,14 @@ RSpec.describe RubySMB::Client do
997
1007
 
998
1008
  describe '#negotiate_response' do
999
1009
  context 'with only SMB1' do
1000
- it 'returns a properly formed packet' do
1010
+ it 'returns a properly formed NegotiateResponseExtended packet if extended_security is set as 1' do
1001
1011
  expect(smb1_client.negotiate_response(smb1_extended_response_raw)).to eq smb1_extended_response
1002
1012
  end
1003
1013
 
1014
+ it 'returns a properly formed NegotiateResponse packet if extended_security is set as 0' do
1015
+ expect(smb1_client.negotiate_response(smb1_response_raw)).to eq smb1_response
1016
+ end
1017
+
1004
1018
  it 'raises an exception if the response is not a SMB packet' do
1005
1019
  expect { smb1_client.negotiate_response(random_junk) }.to raise_error(RubySMB::Error::InvalidPacket)
1006
1020
  end
@@ -1015,12 +1029,6 @@ RSpec.describe RubySMB::Client do
1015
1029
  bogus_response.smb_header.command = 0xff
1016
1030
  expect { smb1_client.negotiate_response(bogus_response.to_binary_s) }.to raise_error(RubySMB::Error::InvalidPacket)
1017
1031
  end
1018
-
1019
- it 'considers the response invalid if Extended Security is not enabled' do
1020
- bogus_response = smb1_extended_response
1021
- bogus_response.parameter_block.capabilities.extended_security = 0
1022
- expect { smb1_client.negotiate_response(bogus_response.to_binary_s) }.to raise_error(RubySMB::Error::InvalidPacket)
1023
- end
1024
1032
  end
1025
1033
 
1026
1034
  context 'with only SMB2' do
@@ -2077,7 +2085,7 @@ RSpec.describe RubySMB::Client do
2077
2085
  it 'generates the HMAC based on the packet and the NTLM session key and signs the packet with it' do
2078
2086
  smb2_client.session_key = 'foo'
2079
2087
  smb2_client.signing_required = true
2080
- expect(OpenSSL::HMAC).to receive(:digest).with(instance_of(OpenSSL::Digest::SHA256), smb2_client.session_key, request1.to_binary_s).and_return(fake_hmac)
2088
+ expect(OpenSSL::HMAC).to receive(:digest).with(instance_of(OpenSSL::Digest), smb2_client.session_key, request1.to_binary_s).and_return(fake_hmac)
2081
2089
  expect(smb2_client.smb2_sign(request1).smb2_header.signature).to eq fake_hmac
2082
2090
  end
2083
2091
  end
@@ -2177,7 +2185,7 @@ RSpec.describe RubySMB::Client do
2177
2185
  smb3_client.dialect = '0x0202'
2178
2186
  expect { smb3_client.smb3_sign(request) }.to raise_error(
2179
2187
  RubySMB::Error::SigningError,
2180
- 'Dialect is incompatible with SMBv3 signing'
2188
+ 'Dialect "0x0202" is incompatible with SMBv3 signing'
2181
2189
  )
2182
2190
  end
2183
2191
  end
@@ -2227,7 +2235,7 @@ RSpec.describe RubySMB::Client do
2227
2235
  smb3_client.dialect = '0x0202'
2228
2236
  expect { smb3_client.smb3_sign(request) }.to raise_error(
2229
2237
  RubySMB::Error::SigningError,
2230
- 'Dialect is incompatible with SMBv3 signing'
2238
+ 'Dialect "0x0202" is incompatible with SMBv3 signing'
2231
2239
  )
2232
2240
  end
2233
2241
  end
@@ -0,0 +1,32 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe RubySMB::Compression::LZNT1 do
4
+ describe '.compress' do
5
+ it 'generates an empty blob when provided an empty blob' do
6
+ expected = "".b
7
+ expect(described_class.compress('')).to eq(expected)
8
+ end
9
+
10
+ it 'generates a compressed blob when provided a string with non-reoccurring characters' do
11
+ expect(described_class.compress('RubySMB')).to eq("\x060RubySMB".b)
12
+ end
13
+
14
+ it 'generates a compressed blob when provided a string of reoccurring characters' do
15
+ expect(described_class.compress("\x01" * 0x200)).to eq("\x03\xB0\x02\x01\xFC\x01".b)
16
+ end
17
+ end
18
+
19
+ describe '.decompress' do
20
+ it 'generates a decompressed blob for a string with non-reoccurring characters' do
21
+ expect(described_class.decompress("\x060RubySMB".b)).to eq('RubySMB')
22
+ end
23
+
24
+ it 'generates a decompressed blob for a string of reoccurring characters' do
25
+ expect(described_class.decompress("\x03\xB0\x02\x01\xFC\x01".b)).to eq("\x01" * 0x200)
26
+ end
27
+
28
+ it 'raises an EncodingError when the length is invalid' do
29
+ expect { described_class.decompress("\x010".b) }.to raise_error(EncodingError)
30
+ end
31
+ end
32
+ end