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
@@ -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) { RubySMB::SMB2::Packet::ReadResponse.new(data_length: 9, buffer: 'fake data') }
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) { RubySMB::SMB2::Packet::ReadResponse.new(data_length: 9, buffer: 'fake data') }
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 IoctlResponse packet' do
248
- response = RubySMB::SMB2::Packet::LogoffResponse.new
249
- allow(RubySMB::SMB2::Packet::IoctlResponse).to receive(:read).and_return(response)
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