ruby_smb 2.0.7 → 2.0.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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