ruby_smb 1.0.2 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (200) hide show
  1. checksums.yaml +5 -5
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/.travis.yml +3 -2
  5. data/Gemfile +6 -2
  6. data/README.md +35 -47
  7. data/examples/enum_registry_key.rb +28 -0
  8. data/examples/enum_registry_values.rb +30 -0
  9. data/examples/negotiate.rb +51 -8
  10. data/examples/pipes.rb +2 -1
  11. data/examples/read_file_encryption.rb +56 -0
  12. data/examples/read_registry_key_value.rb +32 -0
  13. data/lib/ruby_smb.rb +4 -1
  14. data/lib/ruby_smb/client.rb +200 -20
  15. data/lib/ruby_smb/client/authentication.rb +70 -33
  16. data/lib/ruby_smb/client/echo.rb +20 -2
  17. data/lib/ruby_smb/client/encryption.rb +62 -0
  18. data/lib/ruby_smb/client/negotiation.rb +160 -24
  19. data/lib/ruby_smb/client/signing.rb +19 -0
  20. data/lib/ruby_smb/client/tree_connect.rb +24 -18
  21. data/lib/ruby_smb/client/utils.rb +8 -7
  22. data/lib/ruby_smb/client/winreg.rb +46 -0
  23. data/lib/ruby_smb/crypto.rb +30 -0
  24. data/lib/ruby_smb/dcerpc.rb +38 -0
  25. data/lib/ruby_smb/dcerpc/bind.rb +2 -2
  26. data/lib/ruby_smb/dcerpc/bind_ack.rb +2 -2
  27. data/lib/ruby_smb/dcerpc/error.rb +3 -0
  28. data/lib/ruby_smb/dcerpc/ndr.rb +95 -16
  29. data/lib/ruby_smb/dcerpc/pdu_header.rb +1 -1
  30. data/lib/ruby_smb/dcerpc/request.rb +28 -9
  31. data/lib/ruby_smb/dcerpc/rrp_unicode_string.rb +35 -0
  32. data/lib/ruby_smb/dcerpc/srvsvc.rb +10 -0
  33. data/lib/ruby_smb/dcerpc/srvsvc/net_share_enum_all.rb +9 -0
  34. data/lib/ruby_smb/dcerpc/winreg.rb +340 -0
  35. data/lib/ruby_smb/dcerpc/winreg/close_key_request.rb +24 -0
  36. data/lib/ruby_smb/dcerpc/winreg/close_key_response.rb +27 -0
  37. data/lib/ruby_smb/dcerpc/winreg/enum_key_request.rb +45 -0
  38. data/lib/ruby_smb/dcerpc/winreg/enum_key_response.rb +42 -0
  39. data/lib/ruby_smb/dcerpc/winreg/enum_value_request.rb +39 -0
  40. data/lib/ruby_smb/dcerpc/winreg/enum_value_response.rb +36 -0
  41. data/lib/ruby_smb/dcerpc/winreg/open_key_request.rb +34 -0
  42. data/lib/ruby_smb/dcerpc/winreg/open_key_response.rb +25 -0
  43. data/lib/ruby_smb/dcerpc/winreg/open_root_key_request.rb +43 -0
  44. data/lib/ruby_smb/dcerpc/winreg/open_root_key_response.rb +35 -0
  45. data/lib/ruby_smb/dcerpc/winreg/query_info_key_request.rb +27 -0
  46. data/lib/ruby_smb/dcerpc/winreg/query_info_key_response.rb +40 -0
  47. data/lib/ruby_smb/dcerpc/winreg/query_value_request.rb +39 -0
  48. data/lib/ruby_smb/dcerpc/winreg/query_value_response.rb +57 -0
  49. data/lib/ruby_smb/dcerpc/winreg/regsam.rb +40 -0
  50. data/lib/ruby_smb/dispatcher/socket.rb +5 -3
  51. data/lib/ruby_smb/error.rb +68 -2
  52. data/lib/ruby_smb/generic_packet.rb +33 -4
  53. data/lib/ruby_smb/smb1/commands.rb +1 -1
  54. data/lib/ruby_smb/smb1/file.rb +66 -15
  55. data/lib/ruby_smb/smb1/packet/close_request.rb +2 -5
  56. data/lib/ruby_smb/smb1/packet/close_response.rb +2 -1
  57. data/lib/ruby_smb/smb1/packet/echo_request.rb +2 -4
  58. data/lib/ruby_smb/smb1/packet/echo_response.rb +2 -1
  59. data/lib/ruby_smb/smb1/packet/empty_packet.rb +10 -1
  60. data/lib/ruby_smb/smb1/packet/logoff_request.rb +2 -4
  61. data/lib/ruby_smb/smb1/packet/logoff_response.rb +2 -1
  62. data/lib/ruby_smb/smb1/packet/negotiate_request.rb +2 -5
  63. data/lib/ruby_smb/smb1/packet/negotiate_response.rb +3 -7
  64. data/lib/ruby_smb/smb1/packet/negotiate_response_extended.rb +4 -4
  65. data/lib/ruby_smb/smb1/packet/nt_create_andx_request.rb +2 -4
  66. data/lib/ruby_smb/smb1/packet/nt_create_andx_response.rb +2 -1
  67. data/lib/ruby_smb/smb1/packet/nt_trans/create_request.rb +2 -1
  68. data/lib/ruby_smb/smb1/packet/nt_trans/create_response.rb +2 -1
  69. data/lib/ruby_smb/smb1/packet/nt_trans/request.rb +2 -4
  70. data/lib/ruby_smb/smb1/packet/nt_trans/response.rb +2 -1
  71. data/lib/ruby_smb/smb1/packet/read_andx_request.rb +2 -5
  72. data/lib/ruby_smb/smb1/packet/read_andx_response.rb +2 -1
  73. data/lib/ruby_smb/smb1/packet/session_setup_legacy_request.rb +2 -1
  74. data/lib/ruby_smb/smb1/packet/session_setup_legacy_response.rb +3 -2
  75. data/lib/ruby_smb/smb1/packet/session_setup_request.rb +2 -5
  76. data/lib/ruby_smb/smb1/packet/session_setup_response.rb +3 -2
  77. data/lib/ruby_smb/smb1/packet/trans/peek_nmpipe_request.rb +0 -1
  78. data/lib/ruby_smb/smb1/packet/trans/peek_nmpipe_response.rb +3 -2
  79. data/lib/ruby_smb/smb1/packet/trans/request.rb +2 -5
  80. data/lib/ruby_smb/smb1/packet/trans/response.rb +2 -1
  81. data/lib/ruby_smb/smb1/packet/trans/transact_nmpipe_request.rb +1 -1
  82. data/lib/ruby_smb/smb1/packet/trans/transact_nmpipe_response.rb +1 -1
  83. data/lib/ruby_smb/smb1/packet/trans2/find_first2_request.rb +2 -1
  84. data/lib/ruby_smb/smb1/packet/trans2/find_first2_response.rb +8 -2
  85. data/lib/ruby_smb/smb1/packet/trans2/find_next2_request.rb +2 -1
  86. data/lib/ruby_smb/smb1/packet/trans2/find_next2_response.rb +8 -2
  87. data/lib/ruby_smb/smb1/packet/trans2/open2_request.rb +2 -1
  88. data/lib/ruby_smb/smb1/packet/trans2/open2_response.rb +2 -1
  89. data/lib/ruby_smb/smb1/packet/trans2/request.rb +2 -4
  90. data/lib/ruby_smb/smb1/packet/trans2/request_secondary.rb +2 -4
  91. data/lib/ruby_smb/smb1/packet/trans2/response.rb +2 -1
  92. data/lib/ruby_smb/smb1/packet/trans2/set_file_information_request.rb +2 -1
  93. data/lib/ruby_smb/smb1/packet/trans2/set_file_information_response.rb +2 -1
  94. data/lib/ruby_smb/smb1/packet/tree_connect_request.rb +2 -4
  95. data/lib/ruby_smb/smb1/packet/tree_connect_response.rb +13 -3
  96. data/lib/ruby_smb/smb1/packet/tree_disconnect_request.rb +2 -4
  97. data/lib/ruby_smb/smb1/packet/tree_disconnect_response.rb +2 -1
  98. data/lib/ruby_smb/smb1/packet/write_andx_request.rb +3 -6
  99. data/lib/ruby_smb/smb1/packet/write_andx_response.rb +2 -1
  100. data/lib/ruby_smb/smb1/pipe.rb +87 -6
  101. data/lib/ruby_smb/smb1/tree.rb +41 -3
  102. data/lib/ruby_smb/smb2/bit_field/session_flags.rb +2 -1
  103. data/lib/ruby_smb/smb2/bit_field/share_flags.rb +6 -4
  104. data/lib/ruby_smb/smb2/file.rb +103 -25
  105. data/lib/ruby_smb/smb2/negotiate_context.rb +108 -0
  106. data/lib/ruby_smb/smb2/packet.rb +2 -0
  107. data/lib/ruby_smb/smb2/packet/close_request.rb +2 -4
  108. data/lib/ruby_smb/smb2/packet/close_response.rb +2 -1
  109. data/lib/ruby_smb/smb2/packet/compression_transform_header.rb +41 -0
  110. data/lib/ruby_smb/smb2/packet/create_request.rb +2 -4
  111. data/lib/ruby_smb/smb2/packet/create_response.rb +2 -1
  112. data/lib/ruby_smb/smb2/packet/echo_request.rb +2 -4
  113. data/lib/ruby_smb/smb2/packet/echo_response.rb +2 -1
  114. data/lib/ruby_smb/smb2/packet/error_packet.rb +15 -3
  115. data/lib/ruby_smb/smb2/packet/ioctl_request.rb +2 -5
  116. data/lib/ruby_smb/smb2/packet/ioctl_response.rb +2 -1
  117. data/lib/ruby_smb/smb2/packet/logoff_request.rb +2 -4
  118. data/lib/ruby_smb/smb2/packet/logoff_response.rb +2 -1
  119. data/lib/ruby_smb/smb2/packet/negotiate_request.rb +51 -17
  120. data/lib/ruby_smb/smb2/packet/negotiate_response.rb +51 -4
  121. data/lib/ruby_smb/smb2/packet/query_directory_request.rb +2 -4
  122. data/lib/ruby_smb/smb2/packet/query_directory_response.rb +8 -2
  123. data/lib/ruby_smb/smb2/packet/read_request.rb +2 -4
  124. data/lib/ruby_smb/smb2/packet/read_response.rb +2 -1
  125. data/lib/ruby_smb/smb2/packet/session_setup_request.rb +2 -5
  126. data/lib/ruby_smb/smb2/packet/session_setup_response.rb +2 -1
  127. data/lib/ruby_smb/smb2/packet/set_info_request.rb +2 -4
  128. data/lib/ruby_smb/smb2/packet/set_info_response.rb +2 -1
  129. data/lib/ruby_smb/smb2/packet/transform_header.rb +84 -0
  130. data/lib/ruby_smb/smb2/packet/tree_connect_request.rb +93 -10
  131. data/lib/ruby_smb/smb2/packet/tree_connect_response.rb +10 -22
  132. data/lib/ruby_smb/smb2/packet/tree_disconnect_request.rb +2 -4
  133. data/lib/ruby_smb/smb2/packet/tree_disconnect_response.rb +2 -1
  134. data/lib/ruby_smb/smb2/packet/write_request.rb +2 -4
  135. data/lib/ruby_smb/smb2/packet/write_response.rb +2 -1
  136. data/lib/ruby_smb/smb2/pipe.rb +86 -12
  137. data/lib/ruby_smb/smb2/smb2_header.rb +1 -1
  138. data/lib/ruby_smb/smb2/tree.rb +65 -21
  139. data/lib/ruby_smb/version.rb +1 -1
  140. data/ruby_smb.gemspec +5 -3
  141. data/spec/lib/ruby_smb/client_spec.rb +1563 -104
  142. data/spec/lib/ruby_smb/crypto_spec.rb +25 -0
  143. data/spec/lib/ruby_smb/dcerpc/bind_ack_spec.rb +2 -2
  144. data/spec/lib/ruby_smb/dcerpc/bind_spec.rb +2 -2
  145. data/spec/lib/ruby_smb/dcerpc/ndr_spec.rb +410 -0
  146. data/spec/lib/ruby_smb/dcerpc/request_spec.rb +50 -7
  147. data/spec/lib/ruby_smb/dcerpc/rrp_unicode_string_spec.rb +98 -0
  148. data/spec/lib/ruby_smb/dcerpc/srvsvc/net_share_enum_all_spec.rb +13 -0
  149. data/spec/lib/ruby_smb/dcerpc/srvsvc_spec.rb +60 -0
  150. data/spec/lib/ruby_smb/dcerpc/winreg/close_key_request_spec.rb +28 -0
  151. data/spec/lib/ruby_smb/dcerpc/winreg/close_key_response_spec.rb +36 -0
  152. data/spec/lib/ruby_smb/dcerpc/winreg/enum_key_request_spec.rb +108 -0
  153. data/spec/lib/ruby_smb/dcerpc/winreg/enum_key_response_spec.rb +97 -0
  154. data/spec/lib/ruby_smb/dcerpc/winreg/enum_value_request_spec.rb +94 -0
  155. data/spec/lib/ruby_smb/dcerpc/winreg/enum_value_response_spec.rb +82 -0
  156. data/spec/lib/ruby_smb/dcerpc/winreg/open_key_request_spec.rb +74 -0
  157. data/spec/lib/ruby_smb/dcerpc/winreg/open_key_response_spec.rb +35 -0
  158. data/spec/lib/ruby_smb/dcerpc/winreg/open_root_key_request_spec.rb +90 -0
  159. data/spec/lib/ruby_smb/dcerpc/winreg/open_root_key_response_spec.rb +38 -0
  160. data/spec/lib/ruby_smb/dcerpc/winreg/query_info_key_request_spec.rb +39 -0
  161. data/spec/lib/ruby_smb/dcerpc/winreg/query_info_key_response_spec.rb +113 -0
  162. data/spec/lib/ruby_smb/dcerpc/winreg/query_value_request_spec.rb +88 -0
  163. data/spec/lib/ruby_smb/dcerpc/winreg/query_value_response_spec.rb +150 -0
  164. data/spec/lib/ruby_smb/dcerpc/winreg/regsam_spec.rb +32 -0
  165. data/spec/lib/ruby_smb/dcerpc/winreg_spec.rb +710 -0
  166. data/spec/lib/ruby_smb/dcerpc_spec.rb +81 -0
  167. data/spec/lib/ruby_smb/dispatcher/socket_spec.rb +3 -1
  168. data/spec/lib/ruby_smb/error_spec.rb +59 -0
  169. data/spec/lib/ruby_smb/generic_packet_spec.rb +52 -4
  170. data/spec/lib/ruby_smb/smb1/file_spec.rb +191 -2
  171. data/spec/lib/ruby_smb/smb1/packet/empty_packet_spec.rb +68 -0
  172. data/spec/lib/ruby_smb/smb1/packet/session_setup_legacy_request_spec.rb +2 -2
  173. data/spec/lib/ruby_smb/smb1/packet/session_setup_legacy_response_spec.rb +2 -2
  174. data/spec/lib/ruby_smb/smb1/packet/session_setup_request_spec.rb +2 -2
  175. data/spec/lib/ruby_smb/smb1/packet/session_setup_response_spec.rb +1 -1
  176. data/spec/lib/ruby_smb/smb1/packet/trans2/find_first2_response_spec.rb +11 -2
  177. data/spec/lib/ruby_smb/smb1/packet/trans2/find_next2_response_spec.rb +11 -2
  178. data/spec/lib/ruby_smb/smb1/packet/tree_connect_response_spec.rb +40 -0
  179. data/spec/lib/ruby_smb/smb1/pipe_spec.rb +272 -149
  180. data/spec/lib/ruby_smb/smb1/tree_spec.rb +44 -7
  181. data/spec/lib/ruby_smb/smb2/bit_field/session_flags_spec.rb +9 -0
  182. data/spec/lib/ruby_smb/smb2/bit_field/share_flags_spec.rb +27 -0
  183. data/spec/lib/ruby_smb/smb2/file_spec.rb +323 -6
  184. data/spec/lib/ruby_smb/smb2/negotiate_context_spec.rb +332 -0
  185. data/spec/lib/ruby_smb/smb2/packet/compression_transform_header_spec.rb +108 -0
  186. data/spec/lib/ruby_smb/smb2/packet/error_packet_spec.rb +78 -0
  187. data/spec/lib/ruby_smb/smb2/packet/negotiate_request_spec.rb +138 -3
  188. data/spec/lib/ruby_smb/smb2/packet/negotiate_response_spec.rb +120 -2
  189. data/spec/lib/ruby_smb/smb2/packet/query_directory_response_spec.rb +8 -0
  190. data/spec/lib/ruby_smb/smb2/packet/transform_header_spec.rb +220 -0
  191. data/spec/lib/ruby_smb/smb2/packet/tree_connect_request_spec.rb +339 -9
  192. data/spec/lib/ruby_smb/smb2/packet/tree_connect_response_spec.rb +3 -22
  193. data/spec/lib/ruby_smb/smb2/pipe_spec.rb +286 -149
  194. data/spec/lib/ruby_smb/smb2/smb2_header_spec.rb +2 -2
  195. data/spec/lib/ruby_smb/smb2/tree_spec.rb +261 -2
  196. metadata +191 -83
  197. metadata.gz.sig +0 -0
  198. data/lib/ruby_smb/smb1/dcerpc.rb +0 -67
  199. data/lib/ruby_smb/smb2/dcerpc.rb +0 -70
  200. data/spec/lib/ruby_smb/smb1/packet/error_packet_spec.rb +0 -37
@@ -0,0 +1,68 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe RubySMB::SMB1::Packet::EmptyPacket do
4
+ subject(:packet) { described_class.new }
5
+
6
+ describe '#smb_header' do
7
+ subject(:header) { packet.smb_header }
8
+
9
+ it 'is a standard SMB Header' do
10
+ expect(header).to be_a RubySMB::SMB1::SMBHeader
11
+ end
12
+ end
13
+
14
+ describe '#parameter_block' do
15
+ subject(:parameter_block) { packet.parameter_block }
16
+
17
+ it 'is a standard ParameterBlock' do
18
+ expect(parameter_block).to be_a RubySMB::SMB1::ParameterBlock
19
+ end
20
+
21
+ it 'should be empty' do
22
+ expect(parameter_block.to_binary_s).to eq "\x00"
23
+ end
24
+ end
25
+
26
+ describe '#data_block' do
27
+ subject(:data_block) { packet.data_block }
28
+
29
+ it 'is a standard DataBlock' do
30
+ expect(data_block).to be_a RubySMB::SMB1::DataBlock
31
+ end
32
+
33
+ it 'should be empty' do
34
+ expect(data_block.to_binary_s).to eq "\x00\x00"
35
+ end
36
+ end
37
+
38
+ describe '#valid?' do
39
+ before :example do
40
+ packet.original_command = RubySMB::SMB1::Commands::SMB_COM_TREE_CONNECT
41
+ packet.smb_header.command = RubySMB::SMB1::Commands::SMB_COM_TREE_CONNECT
42
+ end
43
+
44
+ it 'returns true if the packet protocol ID and header command are valid' do
45
+ expect(packet).to be_valid
46
+ end
47
+
48
+ it 'returns false if the packet protocol ID is wrong' do
49
+ packet.smb_header.protocol = RubySMB::SMB2::SMB2_PROTOCOL_ID
50
+ expect(packet).to_not be_valid
51
+ end
52
+
53
+ it 'returns false if the packet header command is wrong' do
54
+ packet.smb_header.command = RubySMB::SMB1::Commands::SMB_COM_NEGOTIATE
55
+ expect(packet).to_not be_valid
56
+ end
57
+
58
+ it 'returns false if the packet parameter block size is not 0' do
59
+ packet.parameter_block.word_count = 10
60
+ expect(packet).to_not be_valid
61
+ end
62
+
63
+ it 'returns false if the packet data block size is not 0' do
64
+ packet.data_block.byte_count = 10
65
+ expect(packet).to_not be_valid
66
+ end
67
+ end
68
+ end
@@ -10,8 +10,8 @@ RSpec.describe RubySMB::SMB1::Packet::SessionSetupLegacyRequest do
10
10
  expect(header).to be_a RubySMB::SMB1::SMBHeader
11
11
  end
12
12
 
13
- it 'should have the command set to SMB_COM_NEGOTIATE' do
14
- expect(header.command).to eq RubySMB::SMB1::Commands::SMB_COM_SESSION_SETUP
13
+ it 'should have the command set to SMB_COM_SESSION_SETUP_ANDX' do
14
+ expect(header.command).to eq RubySMB::SMB1::Commands::SMB_COM_SESSION_SETUP_ANDX
15
15
  end
16
16
 
17
17
  it 'should not have the response flag set' do
@@ -10,8 +10,8 @@ RSpec.describe RubySMB::SMB1::Packet::SessionSetupLegacyResponse do
10
10
  expect(header).to be_a RubySMB::SMB1::SMBHeader
11
11
  end
12
12
 
13
- it 'should have the command set to SMB_COM_NEGOTIATE' do
14
- expect(header.command).to eq RubySMB::SMB1::Commands::SMB_COM_SESSION_SETUP
13
+ it 'should have the command set to SMB_COM_SESSION_SETUP_ANDX' do
14
+ expect(header.command).to eq RubySMB::SMB1::Commands::SMB_COM_SESSION_SETUP_ANDX
15
15
  end
16
16
 
17
17
  it 'should have the response flag set' do
@@ -10,8 +10,8 @@ RSpec.describe RubySMB::SMB1::Packet::SessionSetupRequest do
10
10
  expect(header).to be_a RubySMB::SMB1::SMBHeader
11
11
  end
12
12
 
13
- it 'should have the command set to SMB_COM_NEGOTIATE' do
14
- expect(header.command).to eq RubySMB::SMB1::Commands::SMB_COM_SESSION_SETUP
13
+ it 'should have the command set to SMB_COM_SESSION_SETUP_ANDX' do
14
+ expect(header.command).to eq RubySMB::SMB1::Commands::SMB_COM_SESSION_SETUP_ANDX
15
15
  end
16
16
 
17
17
  it 'should not have the response flag set' do
@@ -11,7 +11,7 @@ RSpec.describe RubySMB::SMB1::Packet::SessionSetupResponse do
11
11
  end
12
12
 
13
13
  it 'should have the command set to SMB_COM_NEGOTIATE' do
14
- expect(header.command).to eq RubySMB::SMB1::Commands::SMB_COM_SESSION_SETUP
14
+ expect(header.command).to eq RubySMB::SMB1::Commands::SMB_COM_SESSION_SETUP_ANDX
15
15
  end
16
16
 
17
17
  it 'should have the response flag set' do
@@ -80,6 +80,8 @@ RSpec.describe RubySMB::SMB1::Packet::Trans2::FindFirst2Response do
80
80
 
81
81
  let(:names_blob) { names_array.collect(&:to_binary_s).join('') }
82
82
 
83
+ let(:find_info) { FindFileFullDirectoryInfo.new }
84
+
83
85
  it 'returns an array of parsed FindFileFullDirectoryInfo structs' do
84
86
  packet.data_block.trans2_data.buffer = names_blob
85
87
  expect(packet.results(FindFileFullDirectoryInfo, unicode: false)).to eq names_array
@@ -87,7 +89,6 @@ RSpec.describe RubySMB::SMB1::Packet::Trans2::FindFirst2Response do
87
89
 
88
90
  it 'sets the FindFileFullDirectoryInfo unicode attribute when unicode argument is true' do
89
91
  packet.data_block.trans2_data.buffer = names1.to_binary_s
90
- find_info = FindFileFullDirectoryInfo.new
91
92
  allow(FindFileFullDirectoryInfo).to receive(:new).and_return find_info
92
93
  expect(find_info).to receive(:unicode=).with(true).once
93
94
  packet.results(FindFileFullDirectoryInfo, unicode: true)
@@ -95,10 +96,18 @@ RSpec.describe RubySMB::SMB1::Packet::Trans2::FindFirst2Response do
95
96
 
96
97
  it 'does not set the FindFileFullDirectoryInfo unicode attribute when unicode argument is false' do
97
98
  packet.data_block.trans2_data.buffer = names1.to_binary_s
98
- find_info = FindFileFullDirectoryInfo.new
99
99
  allow(FindFileFullDirectoryInfo).to receive(:new).and_return find_info
100
100
  expect(find_info).to receive(:unicode=).with(false).once
101
101
  packet.results(FindFileFullDirectoryInfo, unicode: false)
102
102
  end
103
+
104
+ context 'when the File Information is not a valid' do
105
+ it 'raises an InvalidPacket exception' do
106
+ packet.data_block.trans2_data.buffer = names1.to_binary_s
107
+ allow(FindFileFullDirectoryInfo).to receive(:new).and_return(find_info)
108
+ allow(find_info).to receive(:read).and_raise(IOError)
109
+ expect { packet.results(FindFileFullDirectoryInfo, unicode: false) }.to raise_error(RubySMB::Error::InvalidPacket)
110
+ end
111
+ end
103
112
  end
104
113
  end
@@ -78,6 +78,8 @@ RSpec.describe RubySMB::SMB1::Packet::Trans2::FindNext2Response do
78
78
 
79
79
  let(:names_blob) { names_array.collect(&:to_binary_s).join('') }
80
80
 
81
+ let(:find_info) { FindFileFullDirectoryInfo.new }
82
+
81
83
  it 'returns an array of parsed FindFileFullDirectoryInfo structs' do
82
84
  packet.data_block.trans2_data.buffer = names_blob
83
85
  expect(packet.results(FindFileFullDirectoryInfo, unicode: false)).to eq names_array
@@ -85,7 +87,6 @@ RSpec.describe RubySMB::SMB1::Packet::Trans2::FindNext2Response do
85
87
 
86
88
  it 'sets the FindFileFullDirectoryInfo unicode attribute when unicode argument is true' do
87
89
  packet.data_block.trans2_data.buffer = names1.to_binary_s
88
- find_info = FindFileFullDirectoryInfo.new
89
90
  allow(FindFileFullDirectoryInfo).to receive(:new).and_return find_info
90
91
  expect(find_info).to receive(:unicode=).with(true).once
91
92
  packet.results(FindFileFullDirectoryInfo, unicode: true)
@@ -93,10 +94,18 @@ RSpec.describe RubySMB::SMB1::Packet::Trans2::FindNext2Response do
93
94
 
94
95
  it 'does not set the FindFileFullDirectoryInfo unicode attribute when unicode argument is false' do
95
96
  packet.data_block.trans2_data.buffer = names1.to_binary_s
96
- find_info = FindFileFullDirectoryInfo.new
97
97
  allow(FindFileFullDirectoryInfo).to receive(:new).and_return find_info
98
98
  expect(find_info).to receive(:unicode=).with(false).once
99
99
  packet.results(FindFileFullDirectoryInfo, unicode: false)
100
100
  end
101
+
102
+ context 'when the File Information is not a valid' do
103
+ it 'raises an InvalidPacket exception' do
104
+ packet.data_block.trans2_data.buffer = names1.to_binary_s
105
+ allow(FindFileFullDirectoryInfo).to receive(:new).and_return(find_info)
106
+ allow(find_info).to receive(:read).and_raise(IOError)
107
+ expect { packet.results(FindFileFullDirectoryInfo, unicode: false) }.to raise_error(RubySMB::Error::InvalidPacket)
108
+ end
109
+ end
101
110
  end
102
111
  end
@@ -104,4 +104,44 @@ RSpec.describe RubySMB::SMB1::Packet::TreeConnectResponse do
104
104
  expect(file_response.guest_access_rights).to be_a RubySMB::SMB1::BitField::FileAccessMask
105
105
  end
106
106
  end
107
+
108
+ describe '#access_rights' do
109
+ it 'is a DirectoryAccessMask if the Tree is a directory' do
110
+ allow(packet).to receive(:is_directory?).and_return(true)
111
+ expect(packet.access_rights).to be_a RubySMB::SMB1::BitField::DirectoryAccessMask
112
+ end
113
+
114
+ it 'is a FileAccessMask if the Tree is not a directory' do
115
+ allow(packet).to receive(:is_directory?).and_return(false)
116
+ expect(packet.access_rights).to be_a RubySMB::SMB1::BitField::FileAccessMask
117
+ end
118
+
119
+ context 'when it is not a valid FileAccessMask' do
120
+ it 'raises an InvalidBitField exception' do
121
+ allow(packet).to receive(:is_directory?).and_return(false)
122
+ allow(RubySMB::SMB1::BitField::FileAccessMask).to receive(:read).and_raise(IOError)
123
+ expect { packet.access_rights }.to raise_error(RubySMB::Error::InvalidBitField)
124
+ end
125
+ end
126
+ end
127
+
128
+ describe '#guest_access_rights' do
129
+ it 'is a DirectoryAccessMask if the Tree is a directory' do
130
+ allow(packet).to receive(:is_directory?).and_return(true)
131
+ expect(packet.guest_access_rights).to be_a RubySMB::SMB1::BitField::DirectoryAccessMask
132
+ end
133
+
134
+ it 'is a FileAccessMask if the Tree is not a directory' do
135
+ allow(packet).to receive(:is_directory?).and_return(false)
136
+ expect(packet.guest_access_rights).to be_a RubySMB::SMB1::BitField::FileAccessMask
137
+ end
138
+
139
+ context 'when it is not a valid FileAccessMask' do
140
+ it 'raises an InvalidBitField exception' do
141
+ allow(packet).to receive(:is_directory?).and_return(false)
142
+ allow(RubySMB::SMB1::BitField::FileAccessMask).to receive(:read).and_raise(IOError)
143
+ expect { packet.guest_access_rights }.to raise_error(RubySMB::Error::InvalidBitField)
144
+ end
145
+ end
146
+ end
107
147
  end
@@ -1,15 +1,12 @@
1
1
  RSpec.describe RubySMB::SMB1::Pipe do
2
2
 
3
+ it { expect(described_class).to be < RubySMB::SMB1::File }
4
+
3
5
  let(:peek_nmpipe_response) {
4
6
  packet = RubySMB::SMB1::Packet::Trans::PeekNmpipeResponse.new
5
7
  packet.data_block.trans_parameters.read("\x10\x20\x00\x00\x03\x00")
6
8
  packet
7
9
  }
8
-
9
- describe RubySMB::SMB1::Pipe do
10
- it { expect(described_class).to be < RubySMB::SMB1::File }
11
- end
12
-
13
10
  let(:dispatcher) { RubySMB::Dispatcher::Socket.new(double('socket')) }
14
11
  let(:client) { RubySMB::Client.new(dispatcher, username: 'msfadmin', password: 'msfadmin') }
15
12
  let(:connect_response) {
@@ -37,6 +34,68 @@ RSpec.describe RubySMB::SMB1::Pipe do
37
34
  described_class.new(tree: tree, response: nt_create_andx_response, name: filename)
38
35
  }
39
36
 
37
+ describe '#peek' do
38
+ let(:request) { RubySMB::SMB1::Packet::Trans::PeekNmpipeRequest.new }
39
+ let(:raw_response) { double('Raw response') }
40
+ let(:response) { double('Response') }
41
+
42
+ before :example do
43
+ allow(RubySMB::SMB1::Packet::Trans::PeekNmpipeRequest).to receive(:new).and_return(request)
44
+ allow(client).to receive(:send_recv).and_return(raw_response)
45
+ allow(RubySMB::SMB1::Packet::Trans::PeekNmpipeResponse).to receive(:read).and_return(response)
46
+ allow(response).to receive(:valid?).and_return(true)
47
+ allow(response).to receive(:status_code).and_return(WindowsError::NTStatus::STATUS_SUCCESS)
48
+ end
49
+
50
+ it 'creates a PeekNmpipeRequest'do
51
+ expect(RubySMB::SMB1::Packet::Trans::PeekNmpipeRequest).to receive(:new)
52
+ pipe.peek
53
+ end
54
+
55
+ it 'sets the request #fid field' do
56
+ expect(request).to receive(:fid=).with(pipe.fid)
57
+ pipe.peek
58
+ end
59
+
60
+ it 'sets the request #max_data_count fieldto the peek_size argument' do
61
+ peek_size = 5
62
+ pipe.peek(peek_size: peek_size)
63
+ expect(request.parameter_block.max_data_count).to eq(peek_size)
64
+ end
65
+
66
+ it 'calls Tree #set_header_fields' do
67
+ expect(tree).to receive(:set_header_fields).with(request)
68
+ pipe.peek
69
+ end
70
+
71
+ it 'calls Client #send_recv' do
72
+ expect(client).to receive(:send_recv).with(request)
73
+ pipe.peek
74
+ end
75
+
76
+ it 'parses the response as a SMB1 PeekNmpipeResponse packet' do
77
+ expect(RubySMB::SMB1::Packet::Trans::PeekNmpipeResponse).to receive(:read).with(raw_response)
78
+ pipe.peek
79
+ end
80
+
81
+ it 'raises an InvalidPacket exception if the response is not valid' do
82
+ allow(response).to receive(:valid?).and_return(false)
83
+ smb_header = double('SMB Header')
84
+ allow(response).to receive(:smb_header).and_return(smb_header)
85
+ allow(smb_header).to receive_messages(:protocol => nil, :command => nil)
86
+ expect { pipe.peek }.to raise_error(RubySMB::Error::InvalidPacket)
87
+ end
88
+
89
+ it 'raises an UnexpectedStatusCode exception if the response status code is not STATUS_SUCCESS or STATUS_BUFFER_OVERFLOW' do
90
+ allow(response).to receive(:status_code).and_return(WindowsError::NTStatus::STATUS_OBJECT_NAME_NOT_FOUND)
91
+ expect { pipe.peek }.to raise_error(RubySMB::Error::UnexpectedStatusCode)
92
+ end
93
+
94
+ it 'returns the expected response' do
95
+ expect(pipe.peek).to eq(response)
96
+ end
97
+ end
98
+
40
99
  describe '#peek_available' do
41
100
  it 'reads the correct number of bytes available' do
42
101
  allow(pipe).to receive(:peek) { peek_nmpipe_response }
@@ -62,202 +121,266 @@ RSpec.describe RubySMB::SMB1::Pipe do
62
121
  end
63
122
  end
64
123
 
65
- context 'with DCERPC' do
66
- describe '#net_share_enum_all' do
67
- let(:host) { '1.2.3.4' }
68
- let(:dcerpc_response) { RubySMB::Dcerpc::Response.new }
69
-
70
- before :example do
71
- allow(pipe).to receive(:bind)
72
- allow(pipe).to receive(:request).and_return(dcerpc_response)
73
- allow(RubySMB::Dcerpc::Srvsvc::NetShareEnumAll).to receive(:parse_response).and_return([])
124
+ describe '#initialize' do
125
+ context 'when name is not provided' do
126
+ it 'raises an ArgumentError' do
127
+ expect {
128
+ described_class.new(tree: tree, response: nt_create_andx_response, name: nil)
129
+ }.to raise_error(ArgumentError)
74
130
  end
131
+ end
75
132
 
76
- it 'calls #bind with the expected arguments' do
77
- expect(pipe).to receive(:bind).with(endpoint: RubySMB::Dcerpc::Srvsvc)
78
- pipe.net_share_enum_all(host)
79
- end
133
+ it 'calls the superclass with the expected arguments' do
134
+ expect(pipe.tree).to eq(tree)
135
+ expect(pipe.name).to eq(filename)
136
+ expect(pipe.attributes).to eq(nt_create_andx_response.parameter_block.ext_file_attributes)
137
+ expect(pipe.fid).to eq(nt_create_andx_response.parameter_block.fid)
138
+ expect(pipe.last_access).to eq(nt_create_andx_response.parameter_block.last_access_time.to_datetime)
139
+ expect(pipe.last_change).to eq(nt_create_andx_response.parameter_block.last_change_time.to_datetime)
140
+ expect(pipe.last_write).to eq(nt_create_andx_response.parameter_block.last_write_time.to_datetime)
141
+ expect(pipe.size).to eq(nt_create_andx_response.parameter_block.end_of_file)
142
+ expect(pipe.size_on_disk).to eq(nt_create_andx_response.parameter_block.allocation_size)
143
+ end
80
144
 
81
- it 'calls #request with the expected arguments' do
82
- expect(pipe).to receive(:request).with(RubySMB::Dcerpc::Srvsvc::NET_SHARE_ENUM_ALL, host: host)
83
- pipe.net_share_enum_all(host)
145
+ context 'with \'srvsvc\' filename' do
146
+ it 'extends Srvsvc class' do
147
+ pipe = described_class.new(tree: tree, response: nt_create_andx_response, name: 'srvsvc')
148
+ expect(pipe.respond_to?(:net_share_enum_all)).to be true
84
149
  end
150
+ end
85
151
 
86
- it 'parse the response with NetShareEnumAll #parse_response method' do
87
- stub = 'ABCD'
88
- dcerpc_response.alloc_hint = stub.size
89
- dcerpc_response.stub = stub
90
- expect(RubySMB::Dcerpc::Srvsvc::NetShareEnumAll).to receive(:parse_response).with(stub)
91
- pipe.net_share_enum_all(host)
152
+ context 'with \'winreg\' filename' do
153
+ it 'extends Winreg class' do
154
+ pipe = described_class.new(tree: tree, response: nt_create_andx_response, name: 'winreg')
155
+ expect(pipe.respond_to?(:has_registry_key?)).to be true
92
156
  end
157
+ end
158
+ end
93
159
 
94
- it 'returns the remote shares' do
95
- shares = [
96
- ["C$", "DISK", "Default share"],
97
- ["Shared", "DISK", ""],
98
- ["IPC$", "IPC", "Remote IPC"],
99
- ["ADMIN$", "DISK", "Remote Admin"]
100
- ]
101
- output = [
102
- {:name=>"C$", :type=>"DISK", :comment=>"Default share"},
103
- {:name=>"Shared", :type=>"DISK", :comment=>""},
104
- {:name=>"IPC$", :type=>"IPC", :comment=>"Remote IPC"},
105
- {:name=>"ADMIN$", :type=>"DISK", :comment=>"Remote Admin"},
106
- ]
107
- allow(RubySMB::Dcerpc::Srvsvc::NetShareEnumAll).to receive(:parse_response).and_return(shares)
108
- expect(pipe.net_share_enum_all(host)).to eq(output)
109
- end
160
+ describe '#dcerpc_request' do
161
+ let(:options) { { host: '1.2.3.4' } }
162
+ let(:stub_packet ) { RubySMB::Dcerpc::Winreg::OpenKeyRequest.new }
163
+ let(:dcerpc_request) { double('DCERPC Request') }
164
+ let(:request_stub) { double('Request stub') }
165
+ let(:binary_dcerpc_request) { double('Binary DCERPC Request') }
166
+ let(:trans_nmpipe_request) { double('TransactNmpipeRequest') }
167
+ let(:trans_data) { double('Trans data') }
168
+ let(:trans_nmpipe_raw_response) { double('Trans nmpipe raw response') }
169
+ let(:trans_nmpipe_response) { double('TransactNmpipeResponse') }
170
+ let(:raw_data) { double('Raw data') }
171
+ let(:dcerpc_response) { double('DCERPC Response') }
172
+ let(:result) { 'Result' }
173
+
174
+ before :example do
175
+ allow(RubySMB::Dcerpc::Request).to receive(:new).and_return(dcerpc_request)
176
+ allow(dcerpc_request).to receive_messages(
177
+ :stub => request_stub,
178
+ :to_binary_s => binary_dcerpc_request
179
+ )
180
+ allow(request_stub).to receive(:read)
181
+ allow(RubySMB::SMB1::Packet::Trans::TransactNmpipeRequest).to receive(:new).and_return(trans_nmpipe_request)
182
+ allow(tree).to receive(:set_header_fields)
183
+ allow(trans_nmpipe_request).to receive_message_chain(:data_block, :trans_data => trans_data)
184
+ allow(trans_nmpipe_request).to receive(:set_fid)
185
+ allow(trans_data).to receive(:write_data=)
186
+ allow(client).to receive(:send_recv).and_return(trans_nmpipe_raw_response)
187
+ allow(RubySMB::SMB1::Packet::Trans::TransactNmpipeResponse).to receive(:read).and_return(trans_nmpipe_response)
188
+ allow(trans_nmpipe_response).to receive_messages(
189
+ :valid? => true,
190
+ :status_code => WindowsError::NTStatus::STATUS_SUCCESS
191
+ )
192
+ allow(trans_nmpipe_response).to receive_message_chain(:data_block, :trans_data, :read_data, :to_binary_s => raw_data)
193
+ allow(RubySMB::Dcerpc::Response).to receive(:read).and_return(dcerpc_response)
194
+ allow(dcerpc_response).to receive_message_chain(:pdu_header, :ptype => RubySMB::Dcerpc::PTypes::RESPONSE)
195
+ allow(dcerpc_response).to receive(:stub).and_return(result)
110
196
  end
111
197
 
112
- describe '#bind' do
113
- let(:options) { { endpoint: RubySMB::Dcerpc::Srvsvc } }
114
- let(:bind_packet) { RubySMB::Dcerpc::Bind.new(options) }
115
- let(:bind_ack_packet) { RubySMB::Dcerpc::BindAck.new }
198
+ it 'creates a Request packet with the expected arguments' do
199
+ pipe.dcerpc_request(stub_packet, options)
200
+ expect(options).to eq( { host: '1.2.3.4', endpoint: 'Winreg' })
201
+ expect(RubySMB::Dcerpc::Request).to have_received(:new).with({ opnum: stub_packet.opnum }, options)
202
+ end
116
203
 
117
- before :example do
118
- allow(RubySMB::Dcerpc::Bind).to receive(:new).and_return(bind_packet)
119
- allow(pipe).to receive(:write)
120
- allow(pipe).to receive(:read)
121
- bind_ack_packet.p_result_list.n_results = 1
122
- bind_ack_packet.p_result_list.p_results[0].result = RubySMB::Dcerpc::BindAck::ACCEPTANCE
123
- allow(RubySMB::Dcerpc::BindAck).to receive(:read).and_return(bind_ack_packet)
124
- end
204
+ it 'sets DCERPC request stub to the stub packet passed as argument' do
205
+ pipe.dcerpc_request(stub_packet, options)
206
+ expect(request_stub).to have_received(:read).with(stub_packet.to_binary_s)
207
+ end
125
208
 
126
- it 'creates a Bind packet' do
127
- expect(RubySMB::Dcerpc::Bind).to receive(:new).with(options).and_return(bind_packet)
128
- pipe.bind(options)
129
- end
209
+ it 'creates a Trans TransactNmpipeRequest packet' do
210
+ pipe.dcerpc_request(stub_packet, options)
211
+ expect(RubySMB::SMB1::Packet::Trans::TransactNmpipeRequest).to have_received(:new).with(options)
212
+ end
130
213
 
131
- it 'writes to the named pipe' do
132
- expect(pipe).to receive(:write).with(data: bind_packet.to_binary_s)
133
- pipe.bind(options)
134
- end
214
+ it 'calls Tree #set_header_fields' do
215
+ pipe.dcerpc_request(stub_packet, options)
216
+ expect(tree).to have_received(:set_header_fields).with(trans_nmpipe_request)
217
+ end
135
218
 
136
- it 'reads the socket' do
137
- expect(pipe).to receive(:read)
138
- pipe.bind(options)
219
+ it 'calls TransactNmpipeRequest #set_fid' do
220
+ pipe.dcerpc_request(stub_packet, options)
221
+ expect(trans_nmpipe_request).to have_received(:set_fid).with(pipe.fid)
222
+ end
223
+
224
+ it 'sets the expected #write_data request property' do
225
+ pipe.dcerpc_request(stub_packet, options)
226
+ expect(trans_data).to have_received(:write_data=).with(binary_dcerpc_request)
227
+ end
228
+
229
+ it 'sends the expected request' do
230
+ pipe.dcerpc_request(stub_packet, options)
231
+ expect(client).to have_received(:send_recv).with(trans_nmpipe_request)
232
+ end
233
+
234
+ it 'creates a Trans TransactNmpipeResponse packet from the response' do
235
+ pipe.dcerpc_request(stub_packet, options)
236
+ expect(RubySMB::SMB1::Packet::Trans::TransactNmpipeResponse).to have_received(:read).with(trans_nmpipe_raw_response)
237
+ end
238
+
239
+ context 'when the response is not a Trans packet' do
240
+ it 'raises an InvalidPacket exception' do
241
+ allow(trans_nmpipe_response).to receive_message_chain(:smb_header, :protocol)
242
+ allow(trans_nmpipe_response).to receive_message_chain(:smb_header, :command)
243
+ allow(trans_nmpipe_response).to receive(:valid?).and_return(false)
244
+ expect { pipe.dcerpc_request(stub_packet, options) }.to raise_error(RubySMB::Error::InvalidPacket)
139
245
  end
246
+ end
140
247
 
141
- it 'creates a BindAck packet from the response' do
142
- raw_response = RubySMB::Dcerpc::BindAck.new.to_binary_s
143
- allow(pipe).to receive(:read).and_return(raw_response)
144
- expect(RubySMB::Dcerpc::BindAck).to receive(:read).with(raw_response).and_return(bind_ack_packet)
145
- pipe.bind(options)
248
+ context 'when the response status code is not STATUS_SUCCESS or STATUS_BUFFER_OVERFLOW' do
249
+ it 'raises an UnexpectedStatusCode exception' do
250
+ allow(trans_nmpipe_response).to receive(:status_code).and_return(WindowsError::NTStatus::STATUS_INVALID_HANDLE)
251
+ expect { pipe.dcerpc_request(stub_packet, options) }.to raise_error(RubySMB::Error::UnexpectedStatusCode)
146
252
  end
253
+ end
147
254
 
148
- it 'raises the expected exception when an invalid packet is received' do
149
- allow(RubySMB::Dcerpc::BindAck).to receive(:read).and_raise(IOError)
150
- expect { pipe.bind(options) }.to raise_error(RubySMB::Dcerpc::Error::InvalidPacket)
255
+ context 'when the response status code is STATUS_SUCCESS' do
256
+ it 'does not raise any exception' do
257
+ expect { pipe.dcerpc_request(stub_packet, options) }.not_to raise_error
151
258
  end
152
259
 
153
- it 'raises the expected exception when it is not a BindAck packet' do
154
- response = RubySMB::Dcerpc::Bind.new
155
- allow(RubySMB::Dcerpc::BindAck).to receive(:read).and_return(response)
156
- expect { pipe.bind(options) }.to raise_error(RubySMB::Dcerpc::Error::BindError)
260
+ it 'creates a DCERPC Response packet from the response' do
261
+ pipe.dcerpc_request(stub_packet, options)
262
+ expect(RubySMB::Dcerpc::Response).to have_received(:read).with(raw_data)
157
263
  end
158
264
 
159
- it 'raises an exception when no result is returned' do
160
- bind_ack_packet.p_result_list.n_results = 0
161
- expect { pipe.bind(options) }.to raise_error(RubySMB::Dcerpc::Error::BindError)
265
+ context 'when an IOError occurs while parsing the DCERPC response' do
266
+ it 'raises an InvalidPacket exception' do
267
+ allow(RubySMB::Dcerpc::Response).to receive(:read).and_raise(IOError)
268
+ expect { pipe.dcerpc_request(stub_packet, options) }.to raise_error(RubySMB::Dcerpc::Error::InvalidPacket)
269
+ end
162
270
  end
163
271
 
164
- it 'raises an exception when result is not ACCEPTANCE' do
165
- bind_ack_packet.p_result_list.p_results[0].result = RubySMB::Dcerpc::BindAck::USER_REJECTION
166
- expect { pipe.bind(options) }.to raise_error(RubySMB::Dcerpc::Error::BindError)
272
+ context 'when the response is not a DCERPC Response packet' do
273
+ it 'raises an InvalidPacket exception' do
274
+ allow(dcerpc_response).to receive_message_chain(:pdu_header, :ptype => RubySMB::Dcerpc::PTypes::FAULT)
275
+ expect { pipe.dcerpc_request(stub_packet, options) }.to raise_error(RubySMB::Dcerpc::Error::InvalidPacket)
276
+ end
167
277
  end
168
278
 
169
- it 'returns the expected BindAck packet' do
170
- expect(pipe.bind(options)).to eq(bind_ack_packet)
279
+ it 'returns the expected stub data' do
280
+ expect(pipe.dcerpc_request(stub_packet, options)).to eq(result)
171
281
  end
172
282
  end
173
283
 
174
- describe '#request' do
175
- let(:options) { { host: '1.2.3.4' } }
176
- let(:opnum) { RubySMB::Dcerpc::Srvsvc::NET_SHARE_ENUM_ALL }
177
- let(:req_packet) { RubySMB::Dcerpc::Request.new({ :opnum => opnum }, options) }
178
- let(:nmpipe_packet) { RubySMB::SMB1::Packet::Trans::TransactNmpipeRequest.new(options) }
179
- let(:nmpipe_response) { RubySMB::SMB1::Packet::Trans::TransactNmpipeResponse.new }
180
- let(:res_packet) { RubySMB::Dcerpc::Response.new }
181
-
284
+ context 'when the response status code is STATUS_BUFFER_OVERFLOW' do
285
+ let(:data_count) { 100 }
286
+ let(:added_raw_data) { double('Added raw data') }
182
287
  before :example do
183
- allow(RubySMB::Dcerpc::Request).to receive(:new).and_return(req_packet)
184
- allow(RubySMB::SMB1::Packet::Trans::TransactNmpipeRequest).to receive(:new).and_return(nmpipe_packet)
185
- allow(client).to receive(:send_recv)
186
- allow(RubySMB::SMB1::Packet::Trans::TransactNmpipeResponse).to receive(:read).and_return(nmpipe_response)
187
- allow(RubySMB::Dcerpc::Response).to receive(:read).and_return(res_packet)
288
+ allow(trans_nmpipe_response).to receive(:status_code).and_return(WindowsError::NTStatus::STATUS_BUFFER_OVERFLOW)
289
+ allow(trans_nmpipe_response).to receive_message_chain(:parameter_block, :data_count => data_count)
290
+ allow(pipe).to receive(:read).and_return(added_raw_data)
291
+ allow(raw_data).to receive(:<<)
292
+ allow(dcerpc_response).to receive_message_chain(:pdu_header, :pfc_flags, :first_frag => 1)
293
+ allow(dcerpc_response).to receive_message_chain(:pdu_header, :pfc_flags, :last_frag => 1)
188
294
  end
189
295
 
190
- it 'creates a Request packet' do
191
- expect(RubySMB::Dcerpc::Request).to receive(:new).and_return(req_packet)
192
- pipe.request(opnum, options)
296
+ it 'does not raise any exception' do
297
+ expect { pipe.dcerpc_request(stub_packet, options) }.not_to raise_error
193
298
  end
194
299
 
195
- it 'creates a Trans TransactNmpipeRequest packet' do
196
- expect(RubySMB::SMB1::Packet::Trans::TransactNmpipeRequest).to receive(:new).and_return(nmpipe_packet)
197
- pipe.request(opnum, options)
300
+ it 'reads the expected number of bytes and concatenate it the first response raw data' do
301
+ pipe.dcerpc_request(stub_packet, options)
302
+ expect(pipe).to have_received(:read).with(bytes: tree.client.max_buffer_size - data_count)
303
+ expect(raw_data).to have_received(:<<).with(added_raw_data)
198
304
  end
199
305
 
200
- it 'calls Tree #set_header_fields' do
201
- expect(tree).to receive(:set_header_fields).with(nmpipe_packet)
202
- pipe.request(opnum, options)
306
+ it 'creates a DCERPC Response packet from the updated raw data' do
307
+ pipe.dcerpc_request(stub_packet, options)
308
+ expect(RubySMB::Dcerpc::Response).to have_received(:read).with(raw_data)
203
309
  end
204
310
 
205
- it 'calls TransactNmpipeRequest #set_fid' do
206
- expect(nmpipe_packet).to receive(:set_fid).with(pipe.fid)
207
- pipe.request(opnum, options)
311
+ context 'when an IOError occurs while parsing the DCERPC response' do
312
+ it 'raises an InvalidPacket exception' do
313
+ allow(RubySMB::Dcerpc::Response).to receive(:read).and_raise(IOError)
314
+ expect { pipe.dcerpc_request(stub_packet, options) }.to raise_error(RubySMB::Dcerpc::Error::InvalidPacket)
315
+ end
208
316
  end
209
317
 
210
- it 'sets the expected data on the request' do
211
- expect(client).to receive(:send_recv) do
212
- expect(nmpipe_packet.data_block.trans_data.write_data).to eq(req_packet.to_binary_s)
318
+ context 'when the response is not a DCERPC Response packet' do
319
+ it 'raises an InvalidPacket exception' do
320
+ allow(dcerpc_response).to receive_message_chain(:pdu_header, :ptype => RubySMB::Dcerpc::PTypes::FAULT)
321
+ expect { pipe.dcerpc_request(stub_packet, options) }.to raise_error(RubySMB::Dcerpc::Error::InvalidPacket)
213
322
  end
214
- pipe.request(opnum, options)
215
323
  end
216
324
 
217
- it 'sends the expected request' do
218
- expect(client).to receive(:send_recv).with(nmpipe_packet)
219
- pipe.request(opnum, options)
325
+ context 'when the response is not the first fragment' do
326
+ it 'raises an InvalidPacket exception' do
327
+ allow(dcerpc_response).to receive_message_chain(:pdu_header, :pfc_flags, :first_frag => 0)
328
+ expect { pipe.dcerpc_request(stub_packet, options) }.to raise_error(RubySMB::Dcerpc::Error::InvalidPacket)
329
+ end
220
330
  end
221
331
 
222
- it 'creates a Trans TransactNmpipeResponse packet from the response' do
223
- raw_response = double('Raw response')
224
- allow(client).to receive(:send_recv).and_return(raw_response)
225
- expect(RubySMB::SMB1::Packet::Trans::TransactNmpipeResponse).to receive(:read).with(raw_response).and_return(nmpipe_response)
226
- pipe.request(opnum, options)
227
- end
332
+ context 'when the response is the last fragment' do
333
+ it 'only reads the pipe once' do
334
+ pipe.dcerpc_request(stub_packet, options)
335
+ expect(RubySMB::Dcerpc::Response).to have_received(:read).once
336
+ end
228
337
 
229
- it 'raises the expected exception when it is not a Trans packet' do
230
- response = RubySMB::SMB1::Packet::Trans2::Response.new
231
- allow(RubySMB::SMB1::Packet::Trans::TransactNmpipeResponse).to receive(:read).and_return(response)
232
- expect { pipe.request(opnum, options) }.to raise_error(RubySMB::Error::InvalidPacket)
338
+ it 'returns the expected stub data' do
339
+ expect(pipe.dcerpc_request(stub_packet, options)).to eq(result)
340
+ end
233
341
  end
234
342
 
235
- it 'raises the expected exception when the status code is not STATUS_SUCCESS' do
236
- nmpipe_response.smb_header.nt_status = WindowsError::NTStatus::STATUS_INVALID_HANDLE.value
237
- expect { pipe.request(opnum, options) }.to raise_error(RubySMB::Error::UnexpectedStatusCode)
238
- end
343
+ context 'when the response is not the last fragment' do
344
+ let(:raw_data2) { double('Raw data #2') }
345
+ let(:dcerpc_response2) { double('DCERPC Response #2') }
346
+ let(:result2) { 'Result #2' }
347
+ before :example do
348
+ allow(dcerpc_response).to receive_message_chain(:pdu_header, :pfc_flags, :last_frag => 0)
349
+ allow(pipe).to receive(:read).with(bytes: tree.client.max_buffer_size).and_return(raw_data2)
350
+ allow(RubySMB::Dcerpc::Response).to receive(:read).with(raw_data2).and_return(dcerpc_response2)
351
+ allow(dcerpc_response2).to receive_message_chain(:pdu_header, :ptype => RubySMB::Dcerpc::PTypes::RESPONSE)
352
+ allow(dcerpc_response2).to receive_message_chain(:pdu_header, :pfc_flags, :last_frag => 1)
353
+ allow(dcerpc_response2).to receive(:stub).and_return(result2)
354
+ end
239
355
 
240
- it 'creates a DCERPC Response packet from the response' do
241
- nmpipe_response.data_block.trans_data.read_data = "test"
242
- expect(RubySMB::Dcerpc::Response).to receive(:read).with(nmpipe_response.data_block.trans_data.read_data)
243
- pipe.request(opnum, options)
244
- end
356
+ it 'reads the expected number of bytes' do
357
+ pipe.dcerpc_request(stub_packet, options)
358
+ expect(pipe).to have_received(:read).with(bytes: tree.client.max_buffer_size)
359
+ end
245
360
 
246
- it 'raises the expected exception when an invalid packet is received' do
247
- allow(RubySMB::Dcerpc::Response).to receive(:read).and_raise(IOError)
248
- expect { pipe.request(opnum, options) }.to raise_error(RubySMB::Dcerpc::Error::InvalidPacket)
249
- end
361
+ it 'creates a DCERPC Response packet from the new raw data' do
362
+ pipe.dcerpc_request(stub_packet, options)
363
+ expect(RubySMB::Dcerpc::Response).to have_received(:read).with(raw_data2)
364
+ end
250
365
 
251
- it 'raises the expected exception when it is not a Response packet' do
252
- response = RubySMB::Dcerpc::Request.new
253
- allow(RubySMB::Dcerpc::Response).to receive(:read).and_return(response)
254
- expect { pipe.request(opnum, options) }.to raise_error(RubySMB::Dcerpc::Error::InvalidPacket)
255
- end
366
+ context 'when an IOError occurs while parsing the new DCERPC response' do
367
+ it 'raises an InvalidPacket exception' do
368
+ allow(RubySMB::Dcerpc::Response).to receive(:read).with(raw_data2).and_raise(IOError)
369
+ expect { pipe.dcerpc_request(stub_packet, options) }.to raise_error(RubySMB::Dcerpc::Error::InvalidPacket)
370
+ end
371
+ end
256
372
 
257
- it 'returns the expected DCERPC Response' do
258
- expect(pipe.request(opnum, options)).to eq(res_packet)
373
+ context 'when the new response is not a DCERPC Response packet' do
374
+ it 'raises an InvalidPacket exception' do
375
+ allow(dcerpc_response2).to receive_message_chain(:pdu_header, :ptype => RubySMB::Dcerpc::PTypes::FAULT)
376
+ expect { pipe.dcerpc_request(stub_packet, options) }.to raise_error(RubySMB::Dcerpc::Error::InvalidPacket)
377
+ end
378
+ end
379
+
380
+ it 'returns the expected stub data' do
381
+ expect(pipe.dcerpc_request(stub_packet, options)).to eq(result)
382
+ end
259
383
  end
260
384
  end
261
385
  end
262
-
263
386
  end