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.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +0 -0
- data/lib/ruby_smb/client.rb +26 -4
- data/lib/ruby_smb/client/authentication.rb +43 -25
- data/lib/ruby_smb/client/echo.rb +20 -2
- data/lib/ruby_smb/client/negotiation.rb +27 -12
- data/lib/ruby_smb/client/tree_connect.rb +20 -14
- data/lib/ruby_smb/error.rb +40 -1
- data/lib/ruby_smb/generic_packet.rb +33 -4
- data/lib/ruby_smb/smb1/dcerpc.rb +7 -2
- data/lib/ruby_smb/smb1/file.rb +60 -11
- data/lib/ruby_smb/smb1/packet/close_request.rb +2 -5
- data/lib/ruby_smb/smb1/packet/close_response.rb +2 -1
- data/lib/ruby_smb/smb1/packet/echo_request.rb +2 -4
- data/lib/ruby_smb/smb1/packet/echo_response.rb +2 -1
- data/lib/ruby_smb/smb1/packet/empty_packet.rb +7 -0
- data/lib/ruby_smb/smb1/packet/logoff_request.rb +2 -4
- data/lib/ruby_smb/smb1/packet/logoff_response.rb +2 -1
- data/lib/ruby_smb/smb1/packet/negotiate_request.rb +2 -5
- data/lib/ruby_smb/smb1/packet/negotiate_response.rb +3 -7
- data/lib/ruby_smb/smb1/packet/negotiate_response_extended.rb +4 -4
- data/lib/ruby_smb/smb1/packet/nt_create_andx_request.rb +2 -4
- data/lib/ruby_smb/smb1/packet/nt_create_andx_response.rb +2 -1
- data/lib/ruby_smb/smb1/packet/nt_trans/create_request.rb +2 -1
- data/lib/ruby_smb/smb1/packet/nt_trans/create_response.rb +2 -1
- data/lib/ruby_smb/smb1/packet/nt_trans/request.rb +2 -4
- data/lib/ruby_smb/smb1/packet/nt_trans/response.rb +2 -1
- data/lib/ruby_smb/smb1/packet/read_andx_request.rb +2 -5
- data/lib/ruby_smb/smb1/packet/read_andx_response.rb +2 -1
- data/lib/ruby_smb/smb1/packet/session_setup_legacy_request.rb +2 -1
- data/lib/ruby_smb/smb1/packet/session_setup_legacy_response.rb +2 -1
- data/lib/ruby_smb/smb1/packet/session_setup_request.rb +2 -5
- data/lib/ruby_smb/smb1/packet/session_setup_response.rb +2 -1
- data/lib/ruby_smb/smb1/packet/trans/peek_nmpipe_request.rb +0 -1
- data/lib/ruby_smb/smb1/packet/trans/peek_nmpipe_response.rb +3 -2
- data/lib/ruby_smb/smb1/packet/trans/request.rb +2 -5
- data/lib/ruby_smb/smb1/packet/trans/response.rb +2 -1
- data/lib/ruby_smb/smb1/packet/trans/transact_nmpipe_request.rb +1 -1
- data/lib/ruby_smb/smb1/packet/trans/transact_nmpipe_response.rb +1 -1
- data/lib/ruby_smb/smb1/packet/trans2/find_first2_request.rb +2 -1
- data/lib/ruby_smb/smb1/packet/trans2/find_first2_response.rb +8 -2
- data/lib/ruby_smb/smb1/packet/trans2/find_next2_request.rb +2 -1
- data/lib/ruby_smb/smb1/packet/trans2/find_next2_response.rb +8 -2
- data/lib/ruby_smb/smb1/packet/trans2/open2_request.rb +2 -1
- data/lib/ruby_smb/smb1/packet/trans2/open2_response.rb +2 -1
- data/lib/ruby_smb/smb1/packet/trans2/request.rb +2 -4
- data/lib/ruby_smb/smb1/packet/trans2/request_secondary.rb +2 -4
- data/lib/ruby_smb/smb1/packet/trans2/response.rb +2 -1
- data/lib/ruby_smb/smb1/packet/trans2/set_file_information_request.rb +2 -1
- data/lib/ruby_smb/smb1/packet/trans2/set_file_information_response.rb +2 -1
- data/lib/ruby_smb/smb1/packet/tree_connect_request.rb +2 -4
- data/lib/ruby_smb/smb1/packet/tree_connect_response.rb +13 -3
- data/lib/ruby_smb/smb1/packet/tree_disconnect_request.rb +2 -4
- data/lib/ruby_smb/smb1/packet/tree_disconnect_response.rb +2 -1
- data/lib/ruby_smb/smb1/packet/write_andx_request.rb +2 -5
- data/lib/ruby_smb/smb1/packet/write_andx_response.rb +2 -1
- data/lib/ruby_smb/smb1/pipe.rb +8 -3
- data/lib/ruby_smb/smb1/tree.rb +40 -2
- data/lib/ruby_smb/smb2/dcerpc.rb +7 -2
- data/lib/ruby_smb/smb2/file.rb +97 -1
- data/lib/ruby_smb/smb2/packet/close_request.rb +2 -4
- data/lib/ruby_smb/smb2/packet/close_response.rb +2 -1
- data/lib/ruby_smb/smb2/packet/create_request.rb +2 -4
- data/lib/ruby_smb/smb2/packet/create_response.rb +2 -1
- data/lib/ruby_smb/smb2/packet/echo_request.rb +2 -4
- data/lib/ruby_smb/smb2/packet/echo_response.rb +2 -1
- data/lib/ruby_smb/smb2/packet/error_packet.rb +7 -0
- data/lib/ruby_smb/smb2/packet/ioctl_request.rb +2 -5
- data/lib/ruby_smb/smb2/packet/ioctl_response.rb +2 -1
- data/lib/ruby_smb/smb2/packet/logoff_request.rb +2 -4
- data/lib/ruby_smb/smb2/packet/logoff_response.rb +2 -1
- data/lib/ruby_smb/smb2/packet/negotiate_request.rb +2 -5
- data/lib/ruby_smb/smb2/packet/negotiate_response.rb +2 -1
- data/lib/ruby_smb/smb2/packet/query_directory_request.rb +2 -4
- data/lib/ruby_smb/smb2/packet/query_directory_response.rb +8 -2
- data/lib/ruby_smb/smb2/packet/read_request.rb +2 -4
- data/lib/ruby_smb/smb2/packet/read_response.rb +2 -1
- data/lib/ruby_smb/smb2/packet/session_setup_request.rb +2 -5
- data/lib/ruby_smb/smb2/packet/session_setup_response.rb +2 -1
- data/lib/ruby_smb/smb2/packet/set_info_request.rb +2 -4
- data/lib/ruby_smb/smb2/packet/set_info_response.rb +2 -1
- data/lib/ruby_smb/smb2/packet/tree_connect_request.rb +2 -5
- data/lib/ruby_smb/smb2/packet/tree_connect_response.rb +8 -2
- data/lib/ruby_smb/smb2/packet/tree_disconnect_request.rb +2 -4
- data/lib/ruby_smb/smb2/packet/tree_disconnect_response.rb +2 -1
- data/lib/ruby_smb/smb2/packet/write_request.rb +2 -4
- data/lib/ruby_smb/smb2/packet/write_response.rb +2 -1
- data/lib/ruby_smb/smb2/pipe.rb +9 -9
- data/lib/ruby_smb/smb2/tree.rb +44 -6
- data/lib/ruby_smb/version.rb +1 -1
- data/spec/lib/ruby_smb/client_spec.rb +123 -11
- data/spec/lib/ruby_smb/generic_packet_spec.rb +52 -4
- data/spec/lib/ruby_smb/smb1/file_spec.rb +182 -1
- data/spec/lib/ruby_smb/smb1/packet/{error_packet_spec.rb → empty_packet_spec.rb} +21 -0
- data/spec/lib/ruby_smb/smb1/packet/trans2/find_first2_response_spec.rb +11 -2
- data/spec/lib/ruby_smb/smb1/packet/trans2/find_next2_response_spec.rb +11 -2
- data/spec/lib/ruby_smb/smb1/packet/tree_connect_response_spec.rb +40 -0
- data/spec/lib/ruby_smb/smb1/pipe_spec.rb +63 -2
- data/spec/lib/ruby_smb/smb1/tree_spec.rb +44 -7
- data/spec/lib/ruby_smb/smb2/file_spec.rb +295 -2
- data/spec/lib/ruby_smb/smb2/packet/error_packet_spec.rb +51 -0
- data/spec/lib/ruby_smb/smb2/packet/query_directory_response_spec.rb +8 -0
- data/spec/lib/ruby_smb/smb2/packet/tree_connect_response_spec.rb +8 -0
- data/spec/lib/ruby_smb/smb2/pipe_spec.rb +69 -3
- data/spec/lib/ruby_smb/smb2/tree_spec.rb +214 -0
- metadata +6 -4
- metadata.gz.sig +0 -0
@@ -99,7 +99,17 @@ RSpec.describe RubySMB::SMB2::File do
|
|
99
99
|
describe '#read' do
|
100
100
|
context 'for a small file' do
|
101
101
|
let(:small_read) { file.read_packet(read_length: 108) }
|
102
|
-
let(:small_response) {
|
102
|
+
let(:small_response) {
|
103
|
+
response = RubySMB::SMB2::Packet::ReadResponse.new(data_length: 9, buffer: 'fake data')
|
104
|
+
response.smb2_header.command = RubySMB::SMB2::Commands::READ
|
105
|
+
response
|
106
|
+
}
|
107
|
+
|
108
|
+
before :example do
|
109
|
+
allow(file).to receive(:read_packet)
|
110
|
+
allow(client).to receive(:send_recv)
|
111
|
+
allow(RubySMB::SMB2::Packet::ReadResponse).to receive(:read).and_return(small_response)
|
112
|
+
end
|
103
113
|
|
104
114
|
it 'uses a single packet to read the entire file' do
|
105
115
|
expect(file).to receive(:read_packet).with(read_length: 108, offset: 0).and_return(small_read)
|
@@ -107,11 +117,35 @@ RSpec.describe RubySMB::SMB2::File do
|
|
107
117
|
expect(RubySMB::SMB2::Packet::ReadResponse).to receive(:read).with('fake data').and_return(small_response)
|
108
118
|
expect(file.read).to eq 'fake data'
|
109
119
|
end
|
120
|
+
|
121
|
+
context 'when the response is not valid' do
|
122
|
+
it 'raise an InvalidPacket exception' do
|
123
|
+
small_response.smb2_header.command = RubySMB::SMB2::Commands::LOGOFF
|
124
|
+
expect { file.read }.to raise_error(RubySMB::Error::InvalidPacket)
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
context 'when the response status code is not STATUS_SUCCESS' do
|
129
|
+
it 'raise an UnexpectedStatusCode exception' do
|
130
|
+
small_response.smb2_header.nt_status = WindowsError::NTStatus::STATUS_INVALID_HANDLE.value
|
131
|
+
expect { file.read }.to raise_error(RubySMB::Error::UnexpectedStatusCode)
|
132
|
+
end
|
133
|
+
end
|
110
134
|
end
|
111
135
|
|
112
136
|
context 'for a larger file' do
|
113
137
|
let(:big_read) { file.read_packet(read_length: 108) }
|
114
|
-
let(:big_response) {
|
138
|
+
let(:big_response) {
|
139
|
+
response = RubySMB::SMB2::Packet::ReadResponse.new(data_length: 9, buffer: 'fake data')
|
140
|
+
response.smb2_header.command = RubySMB::SMB2::Commands::READ
|
141
|
+
response
|
142
|
+
}
|
143
|
+
|
144
|
+
before :example do
|
145
|
+
allow(file).to receive(:read_packet)
|
146
|
+
allow(client).to receive(:send_recv)
|
147
|
+
allow(RubySMB::SMB2::Packet::ReadResponse).to receive(:read).and_return(big_response)
|
148
|
+
end
|
115
149
|
|
116
150
|
it 'uses a multiple packet to read the file in chunks' do
|
117
151
|
expect(file).to receive(:read_packet).once.with(read_length: described_class::MAX_PACKET_SIZE, offset: 0).and_return(big_read)
|
@@ -120,6 +154,24 @@ RSpec.describe RubySMB::SMB2::File do
|
|
120
154
|
expect(RubySMB::SMB2::Packet::ReadResponse).to receive(:read).twice.with('fake data').and_return(big_response)
|
121
155
|
file.read(bytes: (described_class::MAX_PACKET_SIZE * 2))
|
122
156
|
end
|
157
|
+
|
158
|
+
context 'when the second response is not valid' do
|
159
|
+
it 'raise an InvalidPacket exception' do
|
160
|
+
allow(file).to receive(:read_packet).with(read_length: described_class::MAX_PACKET_SIZE, offset: described_class::MAX_PACKET_SIZE) do
|
161
|
+
big_response.smb2_header.command = RubySMB::SMB2::Commands::LOGOFF
|
162
|
+
end
|
163
|
+
expect { file.read(bytes: (described_class::MAX_PACKET_SIZE * 2)) }.to raise_error(RubySMB::Error::InvalidPacket)
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
context 'when the second response status code is not STATUS_SUCCESS' do
|
168
|
+
it 'raise an UnexpectedStatusCode exception' do
|
169
|
+
allow(file).to receive(:read_packet).with(read_length: described_class::MAX_PACKET_SIZE, offset: described_class::MAX_PACKET_SIZE) do
|
170
|
+
big_response.smb2_header.nt_status = WindowsError::NTStatus::STATUS_INVALID_HANDLE.value
|
171
|
+
end
|
172
|
+
expect { file.read(bytes: (described_class::MAX_PACKET_SIZE * 2)) }.to raise_error(RubySMB::Error::UnexpectedStatusCode)
|
173
|
+
end
|
174
|
+
end
|
123
175
|
end
|
124
176
|
end
|
125
177
|
|
@@ -160,6 +212,13 @@ RSpec.describe RubySMB::SMB2::File do
|
|
160
212
|
file.write(data: SecureRandom.random_bytes(described_class::MAX_PACKET_SIZE + 1))
|
161
213
|
end
|
162
214
|
end
|
215
|
+
|
216
|
+
it 'raises an InvalidPacket exception if the response is not valid' do
|
217
|
+
allow(client).to receive(:send_recv)
|
218
|
+
allow(RubySMB::SMB2::Packet::WriteResponse).to receive(:read).and_return(write_response)
|
219
|
+
allow(write_response).to receive(:valid?).and_return(false)
|
220
|
+
expect { file.write(data: 'test') }.to raise_error(RubySMB::Error::InvalidPacket)
|
221
|
+
end
|
163
222
|
end
|
164
223
|
|
165
224
|
describe '#delete_packet' do
|
@@ -193,6 +252,14 @@ RSpec.describe RubySMB::SMB2::File do
|
|
193
252
|
expect(RubySMB::SMB2::Packet::SetInfoResponse).to receive(:read).with('raw_response').and_return(small_response)
|
194
253
|
expect(file.delete).to eq WindowsError::NTStatus::STATUS_SUCCESS
|
195
254
|
end
|
255
|
+
|
256
|
+
it 'raises an InvalidPacket exception if the response is not valid' do
|
257
|
+
allow(file).to receive(:delete_packet)
|
258
|
+
allow(client).to receive(:send_recv)
|
259
|
+
allow(RubySMB::SMB2::Packet::SetInfoResponse).to receive(:read).and_return(small_response)
|
260
|
+
allow(small_response).to receive(:valid?).and_return(false)
|
261
|
+
expect { file.delete }.to raise_error(RubySMB::Error::InvalidPacket)
|
262
|
+
end
|
196
263
|
end
|
197
264
|
end
|
198
265
|
|
@@ -228,6 +295,232 @@ RSpec.describe RubySMB::SMB2::File do
|
|
228
295
|
expect(RubySMB::SMB2::Packet::SetInfoResponse).to receive(:read).with('raw_response').and_return(small_response)
|
229
296
|
expect(file.rename('new_file.txt')).to eq WindowsError::NTStatus::STATUS_SUCCESS
|
230
297
|
end
|
298
|
+
|
299
|
+
it 'raises an InvalidPacket exception if the response is not valid' do
|
300
|
+
allow(client).to receive(:send_recv)
|
301
|
+
allow(RubySMB::SMB2::Packet::SetInfoResponse).to receive(:read).and_return(small_response)
|
302
|
+
allow(small_response).to receive(:valid?).and_return(false)
|
303
|
+
expect { file.rename('new_file.txt') }.to raise_error(RubySMB::Error::InvalidPacket)
|
304
|
+
end
|
305
|
+
end
|
306
|
+
end
|
307
|
+
|
308
|
+
describe '#close' do
|
309
|
+
let(:request) { double('CloseRequest') }
|
310
|
+
let(:response) { double('CloseResponse') }
|
311
|
+
let(:raw_response) { double('Raw response') }
|
312
|
+
|
313
|
+
before :example do
|
314
|
+
allow(RubySMB::SMB2::Packet::CloseRequest).to receive(:new).and_return(request)
|
315
|
+
allow(file).to receive(:set_header_fields).and_return(request)
|
316
|
+
allow(client).to receive(:send_recv).and_return(raw_response)
|
317
|
+
allow(RubySMB::SMB2::Packet::CloseResponse).to receive(:read).and_return(response)
|
318
|
+
allow(response).to receive(:valid?).and_return(true)
|
319
|
+
allow(response).to receive(:status_code).and_return(WindowsError::NTStatus::STATUS_SUCCESS)
|
320
|
+
end
|
321
|
+
|
322
|
+
it 'creates a new SMB2 CloseRequest packet' do
|
323
|
+
expect(RubySMB::SMB2::Packet::CloseRequest).to receive(:new)
|
324
|
+
file.close
|
325
|
+
end
|
326
|
+
|
327
|
+
it 'calls Tree #set_header_fields to set SetFileInformationRequest headers' do
|
328
|
+
expect(file).to receive(:set_header_fields).with(request)
|
329
|
+
file.close
|
330
|
+
end
|
331
|
+
|
332
|
+
it 'calls Client #send_recv with the expected request' do
|
333
|
+
expect(client).to receive(:send_recv).with(request)
|
334
|
+
file.close
|
335
|
+
end
|
336
|
+
|
337
|
+
it 'parses the response as a SMB2 CloseResponse packet' do
|
338
|
+
expect(RubySMB::SMB2::Packet::CloseResponse).to receive(:read).with(raw_response)
|
339
|
+
file.close
|
340
|
+
end
|
341
|
+
|
342
|
+
it 'raises an InvalidPacket exception if the response is not valid' do
|
343
|
+
allow(response).to receive(:valid?).and_return(false)
|
344
|
+
smb2_header = double('SMB2 Header')
|
345
|
+
allow(response).to receive(:smb2_header).and_return(smb2_header)
|
346
|
+
allow(smb2_header).to receive_messages(:protocol => nil, :command => nil)
|
347
|
+
expect { file.close }.to raise_error(RubySMB::Error::InvalidPacket)
|
348
|
+
end
|
349
|
+
|
350
|
+
it 'raises an UnexpectedStatusCode exception if the response status code is not STATUS_SUCCESS' do
|
351
|
+
allow(response).to receive(:status_code).and_return(WindowsError::NTStatus::STATUS_OBJECT_NAME_NOT_FOUND)
|
352
|
+
expect { file.close }.to raise_error(RubySMB::Error::UnexpectedStatusCode)
|
353
|
+
end
|
354
|
+
|
355
|
+
it 'returns the response status code' do
|
356
|
+
expect(file.close).to eq WindowsError::NTStatus::STATUS_SUCCESS
|
357
|
+
end
|
358
|
+
end
|
359
|
+
|
360
|
+
describe '#send_recv_read' do
|
361
|
+
let(:read_data) { 'read data' }
|
362
|
+
let(:raw_response) { double('fake raw response data') }
|
363
|
+
let(:read_response) {
|
364
|
+
res = RubySMB::SMB2::Packet::ReadResponse.new
|
365
|
+
res.data_length = read_data.size
|
366
|
+
res.buffer = read_data
|
367
|
+
res
|
368
|
+
}
|
369
|
+
|
370
|
+
before :example do
|
371
|
+
allow(client).to receive(:send_recv).and_return(raw_response)
|
372
|
+
allow(RubySMB::SMB2::Packet::ReadResponse).to receive(:read).with(raw_response).and_return(read_response)
|
373
|
+
end
|
374
|
+
|
375
|
+
context 'when the number of bytes to read is not provided' do
|
376
|
+
it 'reads 0 bytes by default' do
|
377
|
+
expect(file).to receive(:read_packet).with(read_length: 0, offset: 0).once.and_call_original
|
378
|
+
file.send_recv_read
|
379
|
+
end
|
380
|
+
end
|
381
|
+
|
382
|
+
it 'only reads the number of bytes provided as argument' do
|
383
|
+
bytes = 5
|
384
|
+
expect(file).to receive(:read_packet).with(read_length: bytes, offset: 0).once.and_call_original
|
385
|
+
file.send_recv_read(read_length: bytes)
|
386
|
+
end
|
387
|
+
|
388
|
+
it 'reads from the offset provided as argument' do
|
389
|
+
offset = 3
|
390
|
+
expect(file).to receive(:read_packet).with(read_length: 0, offset: offset).once.and_call_original
|
391
|
+
file.send_recv_read(offset: offset)
|
392
|
+
end
|
393
|
+
|
394
|
+
it 'calls Client #send_recv with the expected request' do
|
395
|
+
request = double('Request')
|
396
|
+
allow(file).to receive(:read_packet).and_return(request)
|
397
|
+
expect(client).to receive(:send_recv).with(request)
|
398
|
+
file.send_recv_read
|
399
|
+
end
|
400
|
+
|
401
|
+
it 'parses the response as a SMB2 ReadResponse packet' do
|
402
|
+
expect(RubySMB::SMB2::Packet::ReadResponse).to receive(:read).with(raw_response)
|
403
|
+
file.send_recv_read
|
404
|
+
end
|
405
|
+
|
406
|
+
it 'raises an InvalidPacket exception if the response is not valid' do
|
407
|
+
allow(read_response).to receive(:valid?).and_return(false)
|
408
|
+
expect { file.send_recv_read }.to raise_error(RubySMB::Error::InvalidPacket)
|
409
|
+
end
|
410
|
+
|
411
|
+
context 'when the response status code is STATUS_PENDING' do
|
412
|
+
before :example do
|
413
|
+
allow(file).to receive(:sleep)
|
414
|
+
allow(read_response).to receive(:status_code).and_return(WindowsError::NTStatus::STATUS_PENDING)
|
415
|
+
allow(dispatcher).to receive(:recv_packet).and_return(raw_response)
|
416
|
+
end
|
417
|
+
|
418
|
+
it 'wait 1 second and calls Client dispatcher #recv_packet method one more time' do
|
419
|
+
expect(file).to receive(:sleep).with(1)
|
420
|
+
expect(dispatcher).to receive(:recv_packet)
|
421
|
+
file.send_recv_read
|
422
|
+
end
|
423
|
+
|
424
|
+
it 'parses the response as a SMB2 ReadResponse packet' do
|
425
|
+
expect(RubySMB::SMB2::Packet::ReadResponse).to receive(:read).twice.with(raw_response)
|
426
|
+
file.send_recv_read
|
427
|
+
end
|
428
|
+
|
429
|
+
it 'raises an InvalidPacket exception if the response is not valid' do
|
430
|
+
allow(dispatcher).to receive(:recv_packet) do
|
431
|
+
allow(read_response).to receive(:valid?).and_return(false)
|
432
|
+
raw_response
|
433
|
+
end
|
434
|
+
expect { file.send_recv_read }.to raise_error(RubySMB::Error::InvalidPacket)
|
435
|
+
end
|
436
|
+
end
|
437
|
+
|
438
|
+
it 'raises an UnexpectedStatusCode exception if the response status code is not STATUS_SUCCESS' do
|
439
|
+
allow(read_response).to receive(:status_code).and_return(WindowsError::NTStatus::STATUS_OBJECT_NAME_NOT_FOUND)
|
440
|
+
expect { file.send_recv_read }.to raise_error(RubySMB::Error::UnexpectedStatusCode)
|
441
|
+
end
|
442
|
+
|
443
|
+
it 'returns the expected string' do
|
444
|
+
expect(file.send_recv_read).to eq(read_data)
|
445
|
+
end
|
446
|
+
end
|
447
|
+
|
448
|
+
describe '#send_recv_write' do
|
449
|
+
let(:write_data) { 'write data' }
|
450
|
+
let(:request) { double('Request') }
|
451
|
+
let(:raw_response) { double('fake raw response data') }
|
452
|
+
let(:write_response) {
|
453
|
+
res = RubySMB::SMB2::Packet::WriteResponse.new
|
454
|
+
res.write_count = write_data.size
|
455
|
+
res
|
456
|
+
}
|
457
|
+
|
458
|
+
before :example do
|
459
|
+
allow(file).to receive(:write_packet).and_return(request)
|
460
|
+
allow(client).to receive(:send_recv).and_return(raw_response)
|
461
|
+
allow(RubySMB::SMB2::Packet::WriteResponse).to receive(:read).with(raw_response).and_return(write_response)
|
462
|
+
end
|
463
|
+
|
464
|
+
it 'reads 0 bytes from offset 0 by default' do
|
465
|
+
expect(file).to receive(:write_packet).with(data: '', offset: 0).once.and_call_original
|
466
|
+
file.send_recv_write
|
467
|
+
end
|
468
|
+
|
469
|
+
it 'writes the data provided as argument' do
|
470
|
+
expect(file).to receive(:write_packet).with(data: write_data, offset: 0).once.and_call_original
|
471
|
+
file.send_recv_write(data: write_data)
|
472
|
+
end
|
473
|
+
|
474
|
+
it 'reads from the offset provided as argument' do
|
475
|
+
offset = 3
|
476
|
+
expect(file).to receive(:write_packet).with(data: '', offset: offset).once.and_call_original
|
477
|
+
file.send_recv_write(offset: offset)
|
478
|
+
end
|
479
|
+
|
480
|
+
it 'calls Client #send_recv with the expected request' do
|
481
|
+
expect(client).to receive(:send_recv).with(request)
|
482
|
+
file.send_recv_write
|
483
|
+
end
|
484
|
+
|
485
|
+
it 'parses the response as a SMB1 WriteResponse packet' do
|
486
|
+
expect(RubySMB::SMB2::Packet::WriteResponse).to receive(:read).with(raw_response)
|
487
|
+
file.send_recv_write
|
488
|
+
end
|
489
|
+
|
490
|
+
context 'when the response status code is STATUS_PENDING' do
|
491
|
+
before :example do
|
492
|
+
allow(file).to receive(:sleep)
|
493
|
+
allow(write_response).to receive(:status_code).and_return(WindowsError::NTStatus::STATUS_PENDING)
|
494
|
+
allow(dispatcher).to receive(:recv_packet).and_return(raw_response)
|
495
|
+
end
|
496
|
+
|
497
|
+
it 'wait 1 second and calls Client dispatcher #recv_packet method one more time' do
|
498
|
+
expect(file).to receive(:sleep).with(1)
|
499
|
+
expect(dispatcher).to receive(:recv_packet)
|
500
|
+
file.send_recv_write
|
501
|
+
end
|
502
|
+
|
503
|
+
it 'parses the response as a SMB2 WriteResponse packet' do
|
504
|
+
expect(RubySMB::SMB2::Packet::WriteResponse).to receive(:read).twice.with(raw_response)
|
505
|
+
file.send_recv_write
|
506
|
+
end
|
507
|
+
|
508
|
+
it 'raises an InvalidPacket exception if the response is not valid' do
|
509
|
+
allow(dispatcher).to receive(:recv_packet) do
|
510
|
+
allow(write_response).to receive(:valid?).and_return(false)
|
511
|
+
raw_response
|
512
|
+
end
|
513
|
+
expect { file.send_recv_write }.to raise_error(RubySMB::Error::InvalidPacket)
|
514
|
+
end
|
515
|
+
end
|
516
|
+
|
517
|
+
it 'raises an InvalidPacket exception if the response is not valid' do
|
518
|
+
allow(write_response).to receive(:valid?).and_return(false)
|
519
|
+
expect { file.send_recv_write }.to raise_error(RubySMB::Error::InvalidPacket)
|
520
|
+
end
|
521
|
+
|
522
|
+
it 'returns the expected response #write_count value' do
|
523
|
+
expect(file.send_recv_write).to eq(write_data.size)
|
231
524
|
end
|
232
525
|
end
|
233
526
|
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe RubySMB::SMB2::Packet::ErrorPacket do
|
4
|
+
subject(:packet) { described_class.new }
|
5
|
+
|
6
|
+
it { is_expected.to respond_to :smb2_header }
|
7
|
+
it { is_expected.to respond_to :structure_size }
|
8
|
+
it { is_expected.to respond_to :error_data }
|
9
|
+
|
10
|
+
describe '#smb2_header' do
|
11
|
+
subject(:header) { packet.smb2_header }
|
12
|
+
|
13
|
+
it 'is a standard SMB2 Header' do
|
14
|
+
expect(header).to be_a RubySMB::SMB2::SMB2Header
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
describe '#structure_size' do
|
19
|
+
it 'should be a 16-bit unsigned integer' do
|
20
|
+
expect(packet.structure_size).to be_a BinData::Uint16le
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
describe '#error_data' do
|
25
|
+
it 'should be a 8-bit unsigned integer' do
|
26
|
+
expect(packet.error_data).to be_a BinData::Uint8
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
describe '#valid?' do
|
31
|
+
before :example do
|
32
|
+
packet.original_command = RubySMB::SMB2::Commands::LOGOFF
|
33
|
+
packet.smb2_header.command = RubySMB::SMB2::Commands::LOGOFF
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'returns true if the packet protocol ID and header command are valid' do
|
37
|
+
expect(packet).to be_valid
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'returns false if the packet protocol ID is wrong' do
|
41
|
+
packet.smb2_header.protocol = RubySMB::SMB1::SMB_PROTOCOL_ID
|
42
|
+
expect(packet).to_not be_valid
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'returns false if the packet header command is wrong' do
|
46
|
+
packet.smb2_header.command = RubySMB::SMB2::Commands::NEGOTIATE
|
47
|
+
expect(packet).to_not be_valid
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
@@ -60,5 +60,13 @@ RSpec.describe RubySMB::SMB2::Packet::QueryDirectoryResponse do
|
|
60
60
|
packet.buffer = names_blob
|
61
61
|
expect(packet.results(RubySMB::Fscc::FileInformation::FileNamesInformation)).to eq names_array
|
62
62
|
end
|
63
|
+
|
64
|
+
context 'when the File Information is not a valid' do
|
65
|
+
it 'raises an InvalidPacket exception' do
|
66
|
+
packet.buffer = names_blob
|
67
|
+
allow(RubySMB::Fscc::FileInformation::FileNamesInformation).to receive(:read).and_raise(IOError)
|
68
|
+
expect { packet.results(RubySMB::Fscc::FileInformation::FileNamesInformation) }.to raise_error(RubySMB::Error::InvalidPacket)
|
69
|
+
end
|
70
|
+
end
|
63
71
|
end
|
64
72
|
end
|
@@ -52,5 +52,13 @@ RSpec.describe RubySMB::SMB2::Packet::TreeConnectResponse do
|
|
52
52
|
allow(packet).to receive(:is_directory?).and_return(false)
|
53
53
|
expect(packet.access_rights).to be_a RubySMB::SMB2::BitField::FileAccessMask
|
54
54
|
end
|
55
|
+
|
56
|
+
context 'when it is not a valid FileAccessMask' do
|
57
|
+
it 'raises an InvalidBitField exception' do
|
58
|
+
allow(packet).to receive(:is_directory?).and_return(false)
|
59
|
+
allow(RubySMB::SMB2::BitField::FileAccessMask).to receive(:read).and_raise(IOError)
|
60
|
+
expect { packet.access_rights }.to raise_error(RubySMB::Error::InvalidBitField)
|
61
|
+
end
|
62
|
+
end
|
55
63
|
end
|
56
64
|
end
|
@@ -36,6 +36,72 @@ RSpec.describe RubySMB::SMB2::Pipe do
|
|
36
36
|
|
37
37
|
subject(:pipe) { described_class.new(name: 'msf-pipe', response: create_response, tree: tree) }
|
38
38
|
|
39
|
+
describe '#peek' do
|
40
|
+
let(:request) { RubySMB::SMB2::Packet::IoctlRequest.new }
|
41
|
+
let(:raw_response) { double('Raw response') }
|
42
|
+
let(:response) { double('Response') }
|
43
|
+
|
44
|
+
before :example do
|
45
|
+
allow(RubySMB::SMB2::Packet::IoctlRequest).to receive(:new).and_return(request)
|
46
|
+
allow(client).to receive(:send_recv).and_return(raw_response)
|
47
|
+
allow(RubySMB::SMB2::Packet::IoctlResponse).to receive(:read).and_return(response)
|
48
|
+
allow(response).to receive(:valid?).and_return(true)
|
49
|
+
allow(response).to receive(:status_code).and_return(WindowsError::NTStatus::STATUS_SUCCESS)
|
50
|
+
end
|
51
|
+
|
52
|
+
it 'creates a IoctlRequest'do
|
53
|
+
expect(RubySMB::SMB2::Packet::IoctlRequest).to receive(:new)
|
54
|
+
pipe.peek
|
55
|
+
end
|
56
|
+
|
57
|
+
it 'sets the request #ctl_code field' do
|
58
|
+
expect(request).to receive(:ctl_code=).with(RubySMB::Fscc::ControlCodes::FSCTL_PIPE_PEEK)
|
59
|
+
pipe.peek
|
60
|
+
end
|
61
|
+
|
62
|
+
it 'sets the request #is_fsctl flag to true' do
|
63
|
+
pipe.peek
|
64
|
+
expect(request.flags.is_fsctl).to eq 1
|
65
|
+
end
|
66
|
+
|
67
|
+
it 'sets the request #max_output_response field to the expected value' do
|
68
|
+
pipe.peek(peek_size: 10)
|
69
|
+
expect(request.max_output_response).to eq(16 + 10)
|
70
|
+
end
|
71
|
+
|
72
|
+
it 'calls #set_header_fields' do
|
73
|
+
expect(pipe).to receive(:set_header_fields).with(request)
|
74
|
+
pipe.peek
|
75
|
+
end
|
76
|
+
|
77
|
+
it 'calls Client #send_recv' do
|
78
|
+
expect(client).to receive(:send_recv).with(request)
|
79
|
+
pipe.peek
|
80
|
+
end
|
81
|
+
|
82
|
+
it 'parses the response as a SMB2 IoctlResponse packet' do
|
83
|
+
expect(RubySMB::SMB2::Packet::IoctlResponse).to receive(:read).with(raw_response)
|
84
|
+
pipe.peek
|
85
|
+
end
|
86
|
+
|
87
|
+
it 'raises an InvalidPacket exception if the response is not valid' do
|
88
|
+
allow(response).to receive(:valid?).and_return(false)
|
89
|
+
smb2_header = double('SMB2 Header')
|
90
|
+
allow(response).to receive(:smb2_header).and_return(smb2_header)
|
91
|
+
allow(smb2_header).to receive_messages(:protocol => nil, :command => nil)
|
92
|
+
expect { pipe.peek }.to raise_error(RubySMB::Error::InvalidPacket)
|
93
|
+
end
|
94
|
+
|
95
|
+
it 'raises an UnexpectedStatusCode exception if the response status code is not STATUS_SUCCESS or STATUS_BUFFER_OVERFLOW' do
|
96
|
+
allow(response).to receive(:status_code).and_return(WindowsError::NTStatus::STATUS_OBJECT_NAME_NOT_FOUND)
|
97
|
+
expect { pipe.peek }.to raise_error(RubySMB::Error::UnexpectedStatusCode)
|
98
|
+
end
|
99
|
+
|
100
|
+
it 'returns the expected response' do
|
101
|
+
expect(pipe.peek).to eq(response)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
39
105
|
describe '#peek_available' do
|
40
106
|
it 'reads the correct number of bytes available' do
|
41
107
|
allow(pipe).to receive(:peek) { ioctl_response }
|
@@ -244,9 +310,9 @@ RSpec.describe RubySMB::SMB2::Pipe do
|
|
244
310
|
pipe.ioctl_send_recv(action, options)
|
245
311
|
end
|
246
312
|
|
247
|
-
it 'raises the expected exception when it is not a
|
248
|
-
|
249
|
-
allow(RubySMB::SMB2::Packet::IoctlResponse).to receive(:read).and_return(
|
313
|
+
it 'raises the expected exception when it is not a valid packet' do
|
314
|
+
ioctl_response.smb2_header.command = RubySMB::SMB2::Commands::LOGOFF
|
315
|
+
allow(RubySMB::SMB2::Packet::IoctlResponse).to receive(:read).and_return(ioctl_response)
|
250
316
|
expect { pipe.ioctl_send_recv(action, options) }.to raise_error(RubySMB::Error::InvalidPacket)
|
251
317
|
end
|
252
318
|
|