ruby_smb 1.0.3 → 1.0.4

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 (108) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/lib/ruby_smb/client.rb +26 -4
  5. data/lib/ruby_smb/client/authentication.rb +43 -25
  6. data/lib/ruby_smb/client/echo.rb +20 -2
  7. data/lib/ruby_smb/client/negotiation.rb +27 -12
  8. data/lib/ruby_smb/client/tree_connect.rb +20 -14
  9. data/lib/ruby_smb/error.rb +40 -1
  10. data/lib/ruby_smb/generic_packet.rb +33 -4
  11. data/lib/ruby_smb/smb1/dcerpc.rb +7 -2
  12. data/lib/ruby_smb/smb1/file.rb +60 -11
  13. data/lib/ruby_smb/smb1/packet/close_request.rb +2 -5
  14. data/lib/ruby_smb/smb1/packet/close_response.rb +2 -1
  15. data/lib/ruby_smb/smb1/packet/echo_request.rb +2 -4
  16. data/lib/ruby_smb/smb1/packet/echo_response.rb +2 -1
  17. data/lib/ruby_smb/smb1/packet/empty_packet.rb +7 -0
  18. data/lib/ruby_smb/smb1/packet/logoff_request.rb +2 -4
  19. data/lib/ruby_smb/smb1/packet/logoff_response.rb +2 -1
  20. data/lib/ruby_smb/smb1/packet/negotiate_request.rb +2 -5
  21. data/lib/ruby_smb/smb1/packet/negotiate_response.rb +3 -7
  22. data/lib/ruby_smb/smb1/packet/negotiate_response_extended.rb +4 -4
  23. data/lib/ruby_smb/smb1/packet/nt_create_andx_request.rb +2 -4
  24. data/lib/ruby_smb/smb1/packet/nt_create_andx_response.rb +2 -1
  25. data/lib/ruby_smb/smb1/packet/nt_trans/create_request.rb +2 -1
  26. data/lib/ruby_smb/smb1/packet/nt_trans/create_response.rb +2 -1
  27. data/lib/ruby_smb/smb1/packet/nt_trans/request.rb +2 -4
  28. data/lib/ruby_smb/smb1/packet/nt_trans/response.rb +2 -1
  29. data/lib/ruby_smb/smb1/packet/read_andx_request.rb +2 -5
  30. data/lib/ruby_smb/smb1/packet/read_andx_response.rb +2 -1
  31. data/lib/ruby_smb/smb1/packet/session_setup_legacy_request.rb +2 -1
  32. data/lib/ruby_smb/smb1/packet/session_setup_legacy_response.rb +2 -1
  33. data/lib/ruby_smb/smb1/packet/session_setup_request.rb +2 -5
  34. data/lib/ruby_smb/smb1/packet/session_setup_response.rb +2 -1
  35. data/lib/ruby_smb/smb1/packet/trans/peek_nmpipe_request.rb +0 -1
  36. data/lib/ruby_smb/smb1/packet/trans/peek_nmpipe_response.rb +3 -2
  37. data/lib/ruby_smb/smb1/packet/trans/request.rb +2 -5
  38. data/lib/ruby_smb/smb1/packet/trans/response.rb +2 -1
  39. data/lib/ruby_smb/smb1/packet/trans/transact_nmpipe_request.rb +1 -1
  40. data/lib/ruby_smb/smb1/packet/trans/transact_nmpipe_response.rb +1 -1
  41. data/lib/ruby_smb/smb1/packet/trans2/find_first2_request.rb +2 -1
  42. data/lib/ruby_smb/smb1/packet/trans2/find_first2_response.rb +8 -2
  43. data/lib/ruby_smb/smb1/packet/trans2/find_next2_request.rb +2 -1
  44. data/lib/ruby_smb/smb1/packet/trans2/find_next2_response.rb +8 -2
  45. data/lib/ruby_smb/smb1/packet/trans2/open2_request.rb +2 -1
  46. data/lib/ruby_smb/smb1/packet/trans2/open2_response.rb +2 -1
  47. data/lib/ruby_smb/smb1/packet/trans2/request.rb +2 -4
  48. data/lib/ruby_smb/smb1/packet/trans2/request_secondary.rb +2 -4
  49. data/lib/ruby_smb/smb1/packet/trans2/response.rb +2 -1
  50. data/lib/ruby_smb/smb1/packet/trans2/set_file_information_request.rb +2 -1
  51. data/lib/ruby_smb/smb1/packet/trans2/set_file_information_response.rb +2 -1
  52. data/lib/ruby_smb/smb1/packet/tree_connect_request.rb +2 -4
  53. data/lib/ruby_smb/smb1/packet/tree_connect_response.rb +13 -3
  54. data/lib/ruby_smb/smb1/packet/tree_disconnect_request.rb +2 -4
  55. data/lib/ruby_smb/smb1/packet/tree_disconnect_response.rb +2 -1
  56. data/lib/ruby_smb/smb1/packet/write_andx_request.rb +2 -5
  57. data/lib/ruby_smb/smb1/packet/write_andx_response.rb +2 -1
  58. data/lib/ruby_smb/smb1/pipe.rb +8 -3
  59. data/lib/ruby_smb/smb1/tree.rb +40 -2
  60. data/lib/ruby_smb/smb2/dcerpc.rb +7 -2
  61. data/lib/ruby_smb/smb2/file.rb +97 -1
  62. data/lib/ruby_smb/smb2/packet/close_request.rb +2 -4
  63. data/lib/ruby_smb/smb2/packet/close_response.rb +2 -1
  64. data/lib/ruby_smb/smb2/packet/create_request.rb +2 -4
  65. data/lib/ruby_smb/smb2/packet/create_response.rb +2 -1
  66. data/lib/ruby_smb/smb2/packet/echo_request.rb +2 -4
  67. data/lib/ruby_smb/smb2/packet/echo_response.rb +2 -1
  68. data/lib/ruby_smb/smb2/packet/error_packet.rb +7 -0
  69. data/lib/ruby_smb/smb2/packet/ioctl_request.rb +2 -5
  70. data/lib/ruby_smb/smb2/packet/ioctl_response.rb +2 -1
  71. data/lib/ruby_smb/smb2/packet/logoff_request.rb +2 -4
  72. data/lib/ruby_smb/smb2/packet/logoff_response.rb +2 -1
  73. data/lib/ruby_smb/smb2/packet/negotiate_request.rb +2 -5
  74. data/lib/ruby_smb/smb2/packet/negotiate_response.rb +2 -1
  75. data/lib/ruby_smb/smb2/packet/query_directory_request.rb +2 -4
  76. data/lib/ruby_smb/smb2/packet/query_directory_response.rb +8 -2
  77. data/lib/ruby_smb/smb2/packet/read_request.rb +2 -4
  78. data/lib/ruby_smb/smb2/packet/read_response.rb +2 -1
  79. data/lib/ruby_smb/smb2/packet/session_setup_request.rb +2 -5
  80. data/lib/ruby_smb/smb2/packet/session_setup_response.rb +2 -1
  81. data/lib/ruby_smb/smb2/packet/set_info_request.rb +2 -4
  82. data/lib/ruby_smb/smb2/packet/set_info_response.rb +2 -1
  83. data/lib/ruby_smb/smb2/packet/tree_connect_request.rb +2 -5
  84. data/lib/ruby_smb/smb2/packet/tree_connect_response.rb +8 -2
  85. data/lib/ruby_smb/smb2/packet/tree_disconnect_request.rb +2 -4
  86. data/lib/ruby_smb/smb2/packet/tree_disconnect_response.rb +2 -1
  87. data/lib/ruby_smb/smb2/packet/write_request.rb +2 -4
  88. data/lib/ruby_smb/smb2/packet/write_response.rb +2 -1
  89. data/lib/ruby_smb/smb2/pipe.rb +9 -9
  90. data/lib/ruby_smb/smb2/tree.rb +44 -6
  91. data/lib/ruby_smb/version.rb +1 -1
  92. data/spec/lib/ruby_smb/client_spec.rb +123 -11
  93. data/spec/lib/ruby_smb/generic_packet_spec.rb +52 -4
  94. data/spec/lib/ruby_smb/smb1/file_spec.rb +182 -1
  95. data/spec/lib/ruby_smb/smb1/packet/{error_packet_spec.rb → empty_packet_spec.rb} +21 -0
  96. data/spec/lib/ruby_smb/smb1/packet/trans2/find_first2_response_spec.rb +11 -2
  97. data/spec/lib/ruby_smb/smb1/packet/trans2/find_next2_response_spec.rb +11 -2
  98. data/spec/lib/ruby_smb/smb1/packet/tree_connect_response_spec.rb +40 -0
  99. data/spec/lib/ruby_smb/smb1/pipe_spec.rb +63 -2
  100. data/spec/lib/ruby_smb/smb1/tree_spec.rb +44 -7
  101. data/spec/lib/ruby_smb/smb2/file_spec.rb +295 -2
  102. data/spec/lib/ruby_smb/smb2/packet/error_packet_spec.rb +51 -0
  103. data/spec/lib/ruby_smb/smb2/packet/query_directory_response_spec.rb +8 -0
  104. data/spec/lib/ruby_smb/smb2/packet/tree_connect_response_spec.rb +8 -0
  105. data/spec/lib/ruby_smb/smb2/pipe_spec.rb +69 -3
  106. data/spec/lib/ruby_smb/smb2/tree_spec.rb +214 -0
  107. metadata +6 -4
  108. metadata.gz.sig +0 -0
@@ -4,15 +4,13 @@ module RubySMB
4
4
  # An SMB2 TreeDisconnectRequest Packet as defined in
5
5
  # [2.2.11 SMB2 TREE_DISCONNECT Request](https://msdn.microsoft.com/en-us/library/cc246500.aspx)
6
6
  class TreeDisconnectRequest < RubySMB::GenericPacket
7
+ COMMAND = RubySMB::SMB2::Commands::TREE_DISCONNECT
8
+
7
9
  endian :little
8
10
  smb2_header :smb2_header
9
11
  uint16 :structure_size, label: 'Structure Size', initial_value: 4
10
12
  uint16 :reserved, label: 'Reserved', initial_value: 0
11
13
 
12
- def initialize_instance
13
- super
14
- smb2_header.command = RubySMB::SMB2::Commands::TREE_DISCONNECT
15
- end
16
14
  end
17
15
  end
18
16
  end
@@ -4,13 +4,14 @@ module RubySMB
4
4
  # An SMB2 TreeDisconnectResponse Packet as defined in
5
5
  # [2.2.12 SMB2 TREE_DISCONNECT Response](https://msdn.microsoft.com/en-us/library/cc246501.aspx)
6
6
  class TreeDisconnectResponse < RubySMB::GenericPacket
7
+ COMMAND = RubySMB::SMB2::Commands::TREE_DISCONNECT
8
+
7
9
  endian :little
8
10
  smb2_header :smb2_header
9
11
  uint16 :structure_size, label: 'Structure Size', initial_value: 4
10
12
 
11
13
  def initialize_instance
12
14
  super
13
- smb2_header.command = RubySMB::SMB2::Commands::TREE_DISCONNECT
14
15
  smb2_header.flags.reply = 1
15
16
  end
16
17
  end
@@ -4,6 +4,8 @@ module RubySMB
4
4
  # An SMB2 Write Request Packet as defined in
5
5
  # [2.2.21 SMB2 WRITE Request](https://msdn.microsoft.com/en-us/library/cc246532.aspx)
6
6
  class WriteRequest < RubySMB::GenericPacket
7
+ COMMAND = RubySMB::SMB2::Commands::WRITE
8
+
7
9
  endian :little
8
10
 
9
11
  smb2_header :smb2_header
@@ -19,10 +21,6 @@ module RubySMB
19
21
  uint32 :flags, label: 'Flags'
20
22
  string :buffer, label: 'Write Data Buffer'
21
23
 
22
- def initialize_instance
23
- super
24
- smb2_header.command = RubySMB::SMB2::Commands::WRITE
25
- end
26
24
  end
27
25
  end
28
26
  end
@@ -4,6 +4,8 @@ module RubySMB
4
4
  # An SMB2 Write Response Packet as defined in
5
5
  # [2.2.22 SMB2 WRITE Response](https://msdn.microsoft.com/en-us/library/cc246533.aspx)
6
6
  class WriteResponse < RubySMB::GenericPacket
7
+ COMMAND = RubySMB::SMB2::Commands::WRITE
8
+
7
9
  endian :little
8
10
 
9
11
  smb2_header :smb2_header
@@ -17,7 +19,6 @@ module RubySMB
17
19
 
18
20
  def initialize_instance
19
21
  super
20
- smb2_header.command = RubySMB::SMB2::Commands::WRITE
21
22
  smb2_header.flags.reply = 1
22
23
  end
23
24
  end
@@ -14,7 +14,7 @@ module RubySMB
14
14
  #
15
15
  # @param peek_size [Integer] Amount of data to peek
16
16
  # @return [RubySMB::SMB2::Packet::IoctlResponse]
17
- # @raise [RubySMB::Error::InvalidPacket] if not a valid FSCTL_PIPE_PEEK response
17
+ # @raise [RubySMB::Error::InvalidPacket] if not a valid FIoctlResponse response
18
18
  # @raise [RubySMB::Error::UnexpectedStatusCode] If status is not STATUS_BUFFER_OVERFLOW or STATUS_SUCCESS
19
19
  def peek(peek_size: 0)
20
20
  packet = RubySMB::SMB2::Packet::IoctlRequest.new
@@ -24,19 +24,19 @@ module RubySMB
24
24
  packet.max_output_response = 16 + peek_size
25
25
  packet = set_header_fields(packet)
26
26
  raw_response = @tree.client.send_recv(packet)
27
- begin
28
- response = RubySMB::SMB2::Packet::IoctlResponse.read(raw_response)
29
- rescue IOError
30
- response = RubySMB::SMB2::Packet::ErrorPacket.read(raw_response)
27
+ response = RubySMB::SMB2::Packet::IoctlResponse.read(raw_response)
28
+ unless response.valid?
29
+ raise RubySMB::Error::InvalidPacket.new(
30
+ expected_proto: RubySMB::SMB2::SMB2_PROTOCOL_ID,
31
+ expected_cmd: RubySMB::SMB2::Packet::IoctlResponse::COMMAND,
32
+ received_proto: response.smb2_header.protocol,
33
+ received_cmd: response.smb2_header.command
34
+ )
31
35
  end
32
36
 
33
37
  unless response.status_code == WindowsError::NTStatus::STATUS_BUFFER_OVERFLOW or response.status_code == WindowsError::NTStatus::STATUS_SUCCESS
34
38
  raise RubySMB::Error::UnexpectedStatusCode, response.status_code.name
35
39
  end
36
-
37
- unless response.smb2_header.command == RubySMB::SMB2::Commands::IOCTL
38
- raise RubySMB::Error::InvalidPacket, 'Not an IoctlResponse packet'
39
- end
40
40
  response
41
41
  end
42
42
 
@@ -34,11 +34,20 @@ module RubySMB
34
34
  # Disconnects this Tree from the current session
35
35
  #
36
36
  # @return [WindowsError::ErrorCode] the NTStatus sent back by the server.
37
+ # @raise [RubySMB::Error::InvalidPacket] if the response is not a TreeDisconnectResponse packet
37
38
  def disconnect!
38
39
  request = RubySMB::SMB2::Packet::TreeDisconnectRequest.new
39
40
  request = set_header_fields(request)
40
41
  raw_response = client.send_recv(request)
41
42
  response = RubySMB::SMB2::Packet::TreeDisconnectResponse.read(raw_response)
43
+ unless response.valid?
44
+ raise RubySMB::Error::InvalidPacket.new(
45
+ expected_proto: RubySMB::SMB2::SMB2_PROTOCOL_ID,
46
+ expected_cmd: RubySMB::SMB2::Packet::TreeDisconnectResponse::COMMAND,
47
+ received_proto: response.smb2_header.protocol,
48
+ received_cmd: response.smb2_header.command
49
+ )
50
+ end
42
51
  response.status_code
43
52
  end
44
53
 
@@ -89,9 +98,16 @@ module RubySMB
89
98
 
90
99
  raw_response = client.send_recv(create_request)
91
100
  response = RubySMB::SMB2::Packet::CreateResponse.read(raw_response)
92
-
93
- if response.is_a?(RubySMB::SMB2::Packet::ErrorPacket)
94
- raise RubySMB::Error::RubySMBError
101
+ unless response.valid?
102
+ raise RubySMB::Error::InvalidPacket.new(
103
+ expected_proto: RubySMB::SMB2::SMB2_PROTOCOL_ID,
104
+ expected_cmd: RubySMB::SMB2::Packet::CreateResponse::COMMAND,
105
+ received_proto: response.smb2_header.protocol,
106
+ received_cmd: response.smb2_header.command
107
+ )
108
+ end
109
+ unless response.status_code == WindowsError::NTStatus::STATUS_SUCCESS
110
+ raise RubySMB::Error::UnexpectedStatusCode, response.status_code.name
95
111
  end
96
112
 
97
113
  case @share_type
@@ -102,7 +118,7 @@ module RubySMB
102
118
  # when 0x03
103
119
  # it's a printer!
104
120
  else
105
- raise RubySMB::Error::RubySMBError
121
+ raise RubySMB::Error::RubySMBError, 'Unsupported share type'
106
122
  end
107
123
  end
108
124
 
@@ -116,6 +132,7 @@ module RubySMB
116
132
  # @param pattern [String] search pattern
117
133
  # @param type [Class] file information class
118
134
  # @return [Array] array of directory structures
135
+ # @raise [RubySMB::Error::InvalidPacket] if the response is not a QueryDirectoryResponse packet
119
136
  def list(directory: nil, pattern: '*', type: RubySMB::Fscc::FileInformation::FileIdFullDirectoryInformation)
120
137
  create_response = open_directory(directory: directory)
121
138
  file_id = create_response.file_id
@@ -133,13 +150,20 @@ module RubySMB
133
150
  loop do
134
151
  response = client.send_recv(directory_request)
135
152
  directory_response = RubySMB::SMB2::Packet::QueryDirectoryResponse.read(response)
153
+ unless directory_response.valid?
154
+ raise RubySMB::Error::InvalidPacket.new(
155
+ expected_proto: RubySMB::SMB2::SMB2_PROTOCOL_ID,
156
+ expected_cmd: RubySMB::SMB2::Packet::QueryDirectoryResponse::COMMAND,
157
+ received_proto: directory_response.smb2_header.protocol,
158
+ received_cmd: directory_response.smb2_header.command
159
+ )
160
+ end
136
161
 
137
162
  status_code = directory_response.smb2_header.nt_status.to_nt_status
138
163
 
139
164
  break if status_code == WindowsError::NTStatus::STATUS_NO_MORE_FILES
140
165
 
141
166
  unless status_code == WindowsError::NTStatus::STATUS_SUCCESS
142
-
143
167
  raise RubySMB::Error::UnexpectedStatusCode, status_code.to_s
144
168
  end
145
169
 
@@ -162,6 +186,7 @@ module RubySMB
162
186
  # @param write [Boolean] whether to request write access
163
187
  # @param delete [Boolean] whether to request delete access
164
188
  # @return [RubySMB::SMB2::Packet::CreateResponse] the response packet returned from the server
189
+ # @raise [RubySMB::Error::InvalidPacket] if the response is not a CreateResponse packet
165
190
  def open_directory(directory: nil, disposition: RubySMB::Dispositions::FILE_OPEN,
166
191
  impersonation: RubySMB::ImpersonationLevels::SEC_IMPERSONATE,
167
192
  read: true, write: false, delete: false)
@@ -169,7 +194,20 @@ module RubySMB
169
194
  create_request = open_directory_packet(directory: directory, disposition: disposition,
170
195
  impersonation: impersonation, read: read, write: write, delete: delete)
171
196
  raw_response = client.send_recv(create_request)
172
- RubySMB::SMB2::Packet::CreateResponse.read(raw_response)
197
+ response = RubySMB::SMB2::Packet::CreateResponse.read(raw_response)
198
+ unless response.valid?
199
+ raise RubySMB::Error::InvalidPacket.new(
200
+ expected_proto: RubySMB::SMB2::SMB2_PROTOCOL_ID,
201
+ expected_cmd: RubySMB::SMB2::Packet::CreateResponse::COMMAND,
202
+ received_proto: response.smb2_header.protocol,
203
+ received_cmd: response.smb2_header.command
204
+ )
205
+ end
206
+ unless response.status_code == WindowsError::NTStatus::STATUS_SUCCESS
207
+ raise RubySMB::Error::UnexpectedStatusCode, response.status_code.name
208
+ end
209
+
210
+ response
173
211
  end
174
212
 
175
213
  # Creates the Packet for the #open_directory method.
@@ -1,3 +1,3 @@
1
1
  module RubySMB
2
- VERSION = '1.0.3'.freeze
2
+ VERSION = '1.0.4'.freeze
3
3
  end
@@ -9,6 +9,7 @@ RSpec.describe RubySMB::Client do
9
9
  let(:smb1_client) { described_class.new(dispatcher, smb2: false, username: username, password: password) }
10
10
  let(:smb2_client) { described_class.new(dispatcher, smb1: false, username: username, password: password) }
11
11
  let(:empty_packet) { RubySMB::SMB1::Packet::EmptyPacket.new }
12
+ let(:error_packet) { RubySMB::SMB2::Packet::ErrorPacket.new }
12
13
 
13
14
  describe '#initialize' do
14
15
  it 'should raise an ArgumentError without a valid dispatcher' do
@@ -153,6 +154,94 @@ RSpec.describe RubySMB::Client do
153
154
  end
154
155
  end
155
156
 
157
+ describe '#logoff!' do
158
+ context 'with SMB1' do
159
+ let(:raw_response) { double('Raw response') }
160
+ let(:logoff_response) {
161
+ RubySMB::SMB1::Packet::LogoffResponse.new(smb_header: {:command => RubySMB::SMB1::Commands::SMB_COM_LOGOFF} )
162
+ }
163
+ before :example do
164
+ allow(smb1_client).to receive(:send_recv).and_return(raw_response)
165
+ allow(RubySMB::SMB1::Packet::LogoffResponse).to receive(:read).and_return(logoff_response)
166
+ allow(smb1_client).to receive(:wipe_state!)
167
+ end
168
+
169
+ it 'creates a LogoffRequest packet' do
170
+ expect(RubySMB::SMB1::Packet::LogoffRequest).to receive(:new).and_call_original
171
+ smb1_client.logoff!
172
+ end
173
+
174
+ it 'calls #send_recv' do
175
+ expect(smb1_client).to receive(:send_recv)
176
+ smb1_client.logoff!
177
+ end
178
+
179
+ it 'reads the raw response as a LogoffResponse packet' do
180
+ expect(RubySMB::SMB1::Packet::LogoffResponse).to receive(:read).with(raw_response)
181
+ smb1_client.logoff!
182
+ end
183
+
184
+ it 'raise an InvalidPacket exception when the response is an empty packet' do
185
+ allow(RubySMB::SMB1::Packet::LogoffResponse).to receive(:read).and_return(RubySMB::SMB1::Packet::EmptyPacket.new)
186
+ expect {smb1_client.logoff!}.to raise_error(RubySMB::Error::InvalidPacket)
187
+ end
188
+
189
+ it 'raise an InvalidPacket exception when the response is not valid' do
190
+ allow(logoff_response).to receive(:valid?).and_return(false)
191
+ expect {smb1_client.logoff!}.to raise_error(RubySMB::Error::InvalidPacket)
192
+ end
193
+
194
+ it 'calls #wipe_state!' do
195
+ expect(smb1_client).to receive(:wipe_state!)
196
+ smb1_client.logoff!
197
+ end
198
+
199
+ it 'returns the expected status code' do
200
+ logoff_response.smb_header.nt_status = WindowsError::NTStatus::STATUS_PENDING.value
201
+ allow(RubySMB::SMB1::Packet::LogoffResponse).to receive(:read).and_return(logoff_response)
202
+ expect(smb1_client.logoff!).to eq(WindowsError::NTStatus::STATUS_PENDING)
203
+ end
204
+ end
205
+
206
+ context 'with SMB2' do
207
+ let(:raw_response) { double('Raw response') }
208
+ let(:logoff_response) {
209
+ RubySMB::SMB2::Packet::LogoffResponse.new(smb_header: {:command => RubySMB::SMB2::Commands::LOGOFF} )
210
+ }
211
+ before :example do
212
+ allow(smb2_client).to receive(:send_recv).and_return(raw_response)
213
+ allow(RubySMB::SMB2::Packet::LogoffResponse).to receive(:read).and_return(logoff_response)
214
+ allow(smb2_client).to receive(:wipe_state!)
215
+ end
216
+
217
+ it 'creates a LogoffRequest packet' do
218
+ expect(RubySMB::SMB2::Packet::LogoffRequest).to receive(:new).and_call_original
219
+ smb2_client.logoff!
220
+ end
221
+
222
+ it 'calls #send_recv' do
223
+ expect(smb2_client).to receive(:send_recv)
224
+ smb2_client.logoff!
225
+ end
226
+
227
+ it 'reads the raw response as a LogoffResponse packet' do
228
+ expect(RubySMB::SMB2::Packet::LogoffResponse).to receive(:read).with(raw_response)
229
+ smb2_client.logoff!
230
+ end
231
+
232
+ it 'raise an InvalidPacket exception when the response is an error packet' do
233
+ allow(RubySMB::SMB2::Packet::LogoffResponse).to receive(:read).and_return(RubySMB::SMB2::Packet::ErrorPacket.new)
234
+ expect {smb2_client.logoff!}.to raise_error(RubySMB::Error::InvalidPacket)
235
+ end
236
+
237
+ it 'raise an InvalidPacket exception when the response is not a LOGOFF command' do
238
+ logoff_response.smb2_header.command = RubySMB::SMB2::Commands::ECHO
239
+ allow(RubySMB::SMB2::Packet::LogoffResponse).to receive(:read).and_return(logoff_response)
240
+ expect {smb2_client.logoff!}.to raise_error(RubySMB::Error::InvalidPacket)
241
+ end
242
+ end
243
+ end
244
+
156
245
  context 'NetBIOS Session Service' do
157
246
  describe '#session_request' do
158
247
  let(:session_header) { RubySMB::Nbss::SessionHeader.new }
@@ -197,6 +286,11 @@ RSpec.describe RubySMB::Client do
197
286
  allow(dispatcher).to receive(:recv_packet).and_return(negative_session_response.to_binary_s)
198
287
  expect { client.session_request }.to raise_error(RubySMB::Error::NetBiosSessionService)
199
288
  end
289
+
290
+ it 'raises an InvalidPacket exception when an error occurs while reading' do
291
+ allow(RubySMB::Nbss::SessionHeader).to receive(:read).and_raise(IOError)
292
+ expect { client.session_request }.to raise_error(RubySMB::Error::InvalidPacket)
293
+ end
200
294
  end
201
295
 
202
296
  describe '#session_request_packet' do
@@ -332,10 +426,15 @@ RSpec.describe RubySMB::Client do
332
426
  expect(smb1_client.negotiate_response(smb1_extended_response_raw)).to eq smb1_extended_response
333
427
  end
334
428
 
335
- it 'raises an exception if the Response is invalid' do
429
+ it 'raises an exception if the response is not a SMB packet' do
336
430
  expect { smb1_client.negotiate_response(random_junk) }.to raise_error(RubySMB::Error::InvalidPacket)
337
431
  end
338
432
 
433
+ it 'raises an InvalidPacket error if the response is not a valid response' do
434
+ empty_packet.smb_header.command = RubySMB::SMB2::Commands::NEGOTIATE
435
+ expect { smb1_client.negotiate_response(empty_packet.to_binary_s) }.to raise_error(RubySMB::Error::InvalidPacket)
436
+ end
437
+
339
438
  it 'considers the response invalid if it is not an actual Negotiate Response' do
340
439
  bogus_response = smb1_extended_response
341
440
  bogus_response.smb_header.command = 0xff
@@ -357,6 +456,12 @@ RSpec.describe RubySMB::Client do
357
456
  it 'raises an exception if the Response is invalid' do
358
457
  expect { smb2_client.negotiate_response(random_junk) }.to raise_error(RubySMB::Error::InvalidPacket)
359
458
  end
459
+
460
+ it 'considers the response invalid if it is not an actual Negotiate Response' do
461
+ bogus_response = smb2_response
462
+ bogus_response.smb2_header.command = RubySMB::SMB2::Commands::ECHO
463
+ expect { smb2_client.negotiate_response(bogus_response.to_binary_s) }.to raise_error(RubySMB::Error::InvalidPacket)
464
+ end
360
465
  end
361
466
 
362
467
  context 'with SMB1 and SMB2 enabled' do
@@ -624,7 +729,7 @@ RSpec.describe RubySMB::Client do
624
729
  expect { smb1_client.smb1_ntlmssp_challenge_packet(response.to_binary_s) }.to raise_error(RubySMB::Error::UnexpectedStatusCode)
625
730
  end
626
731
 
627
- it 'raises an InvalidPacket if the Command field is wrong' do
732
+ it 'raise an InvalidPacket exception when the response is not valid' do
628
733
  expect { smb1_client.smb1_ntlmssp_challenge_packet(wrong_command.to_binary_s) }.to raise_error(RubySMB::Error::InvalidPacket)
629
734
  end
630
735
  end
@@ -645,7 +750,7 @@ RSpec.describe RubySMB::Client do
645
750
  expect(smb1_client.smb1_ntlmssp_final_packet(response.to_binary_s)).to eq response
646
751
  end
647
752
 
648
- it 'raises an InvalidPacket if the Command field is wrong' do
753
+ it 'raise an InvalidPacket exception when the response is not valid' do
649
754
  expect { smb1_client.smb1_ntlmssp_final_packet(wrong_command.to_binary_s) }.to raise_error(RubySMB::Error::InvalidPacket)
650
755
  end
651
756
  end
@@ -717,12 +822,7 @@ RSpec.describe RubySMB::Client do
717
822
  expect(smb1_client.smb1_anonymous_auth_response(anonymous_response.to_binary_s)).to eq anonymous_response
718
823
  end
719
824
 
720
- it 'returns an empty packet if the raw data is not a valid response' do
721
- empty_packet.smb_header.command = RubySMB::SMB1::Commands::SMB_COM_SESSION_SETUP
722
- expect(smb1_client.smb1_anonymous_auth_response(empty_packet.to_binary_s)).to eq empty_packet
723
- end
724
-
725
- it 'raises an InvalidPacket error if the command is wrong' do
825
+ it 'raise an InvalidPacket exception when the response is not valid' do
726
826
  anonymous_response.smb_header.command = RubySMB::SMB1::Commands::SMB_COM_NEGOTIATE
727
827
  expect { smb1_client.smb1_anonymous_auth_response(anonymous_response.to_binary_s) }.to raise_error(RubySMB::Error::InvalidPacket)
728
828
  end
@@ -829,7 +929,7 @@ RSpec.describe RubySMB::Client do
829
929
  expect { smb2_client.smb2_ntlmssp_challenge_packet(response.to_binary_s) }.to raise_error(RubySMB::Error::UnexpectedStatusCode)
830
930
  end
831
931
 
832
- it 'raises an InvalidPacket if the Command field is wrong' do
932
+ it 'raise an InvalidPacket exception when the response is not valid' do
833
933
  expect { smb2_client.smb2_ntlmssp_challenge_packet(wrong_command.to_binary_s) }.to raise_error(RubySMB::Error::InvalidPacket)
834
934
  end
835
935
  end
@@ -888,7 +988,7 @@ RSpec.describe RubySMB::Client do
888
988
  expect(smb2_client.smb2_ntlmssp_final_packet(response.to_binary_s)).to eq response
889
989
  end
890
990
 
891
- it 'raises an InvalidPacket if the Command field is wrong' do
991
+ it 'raise an InvalidPacket exception when the response is not valid' do
892
992
  expect { smb2_client.smb2_ntlmssp_final_packet(wrong_command.to_binary_s) }.to raise_error(RubySMB::Error::InvalidPacket)
893
993
  end
894
994
  end
@@ -1199,6 +1299,12 @@ RSpec.describe RubySMB::Client do
1199
1299
  expect(smb1_client).to receive(:send_recv).and_return(echo_response.to_binary_s)
1200
1300
  expect(smb1_client.echo).to eq WindowsError::NTStatus::STATUS_ABANDONED
1201
1301
  end
1302
+
1303
+ it 'raise an InvalidPacket exception when the response is not valid' do
1304
+ echo_response.smb_header.command = RubySMB::SMB1::Commands::SMB_COM_SESSION_SETUP
1305
+ allow(smb1_client).to receive(:send_recv).and_return(echo_response.to_binary_s)
1306
+ expect { smb1_client.echo }.to raise_error(RubySMB::Error::InvalidPacket)
1307
+ end
1202
1308
  end
1203
1309
 
1204
1310
  context 'with SMB2' do
@@ -1210,6 +1316,12 @@ RSpec.describe RubySMB::Client do
1210
1316
  expect(smb2_client).to receive(:send_recv).with(echo_request).and_return(echo_response.to_binary_s)
1211
1317
  expect(smb2_client.smb2_echo).to eq echo_response
1212
1318
  end
1319
+
1320
+ it 'raise an InvalidPacket exception when the response is not valid' do
1321
+ echo_response.smb2_header.command = RubySMB::SMB2::Commands::SESSION_SETUP
1322
+ allow(smb2_client).to receive(:send_recv).and_return(echo_response.to_binary_s)
1323
+ expect { smb2_client.smb2_echo }.to raise_error(RubySMB::Error::InvalidPacket)
1324
+ end
1213
1325
  end
1214
1326
  end
1215
1327
 
@@ -63,8 +63,13 @@ RSpec.describe RubySMB::GenericPacket do
63
63
  expect(RubySMB::SMB1::Packet::NegotiateResponse.read(smb1_error_packet.to_binary_s)).to be_a RubySMB::SMB1::Packet::EmptyPacket
64
64
  end
65
65
 
66
- it 'reraises the exception if it is not a valid error packet either' do
67
- expect{RubySMB::SMB1::Packet::NegotiateResponse.read('a')}.to raise_error(EOFError)
66
+ it 'raises an InvaliPacket exception if it is not a valid error packet either' do
67
+ expect{RubySMB::SMB1::Packet::NegotiateResponse.read('a')}.to raise_error(RubySMB::Error::InvalidPacket)
68
+ end
69
+
70
+ it 'sets the EmptyPacket#original_command attribute to the original COMMAND' do
71
+ packet = RubySMB::SMB1::Packet::NegotiateResponse.read(smb1_error_packet.to_binary_s)
72
+ expect(packet.original_command).to eq RubySMB::SMB1::Packet::NegotiateResponse::COMMAND
68
73
  end
69
74
  end
70
75
 
@@ -75,8 +80,51 @@ RSpec.describe RubySMB::GenericPacket do
75
80
  expect(RubySMB::SMB2::Packet::NegotiateResponse.read(smb2_error_packet.to_binary_s)).to be_a RubySMB::SMB2::Packet::ErrorPacket
76
81
  end
77
82
 
78
- it 'reraises the exception if it is not a valid error packet either' do
79
- expect{RubySMB::SMB2::Packet::NegotiateResponse.read('a')}.to raise_error(EOFError)
83
+ it 'raises an InvaliPacket exception if it is not a valid error packet either' do
84
+ expect{RubySMB::SMB2::Packet::NegotiateResponse.read('a')}.to raise_error(RubySMB::Error::InvalidPacket)
85
+ end
86
+
87
+ it 'sets the ErrorPacket#original_command attribute to the original COMMAND' do
88
+ packet = RubySMB::SMB2::Packet::NegotiateResponse.read(smb2_error_packet.to_binary_s)
89
+ expect(packet.original_command).to eq RubySMB::SMB2::Packet::NegotiateResponse::COMMAND
90
+ end
91
+ end
92
+ end
93
+
94
+ describe '#valid?' do
95
+ context 'when reading an SMB1 packet' do
96
+ let(:packet) { RubySMB::SMB1::Packet::NegotiateResponse.new }
97
+
98
+ it 'returns true if the packet protocol ID and header command are valid' do
99
+ expect(packet).to be_valid
100
+ end
101
+
102
+ it 'returns false if the packet protocol ID is wrong' do
103
+ packet.smb_header.protocol = RubySMB::SMB2::SMB2_PROTOCOL_ID
104
+ expect(packet).to_not be_valid
105
+ end
106
+
107
+ it 'returns false if the packet header command is wrong' do
108
+ packet.smb_header.command = RubySMB::SMB1::Commands::SMB_COM_TREE_CONNECT
109
+ expect(packet).to_not be_valid
110
+ end
111
+ end
112
+
113
+ context 'when reading an SMB2 packet' do
114
+ let(:packet) { RubySMB::SMB2::Packet::NegotiateResponse.new }
115
+
116
+ it 'returns true if the packet protocol ID and header command are valid' do
117
+ expect(packet).to be_valid
118
+ end
119
+
120
+ it 'returns false if the packet protocol ID is wrong' do
121
+ packet.smb2_header.protocol = RubySMB::SMB1::SMB_PROTOCOL_ID
122
+ expect(packet).to_not be_valid
123
+ end
124
+
125
+ it 'returns false if the packet header command is wrong' do
126
+ packet.smb2_header.command = RubySMB::SMB2::Commands::TREE_CONNECT
127
+ expect(packet).to_not be_valid
80
128
  end
81
129
  end
82
130
  end