ruby_smb 1.0.5 → 2.0.3

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 (191) 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/anonymous_auth.rb +3 -3
  8. data/examples/append_file.rb +10 -8
  9. data/examples/authenticate.rb +9 -5
  10. data/examples/delete_file.rb +8 -6
  11. data/examples/enum_registry_key.rb +29 -0
  12. data/examples/enum_registry_values.rb +31 -0
  13. data/examples/list_directory.rb +8 -6
  14. data/examples/negotiate.rb +51 -8
  15. data/examples/negotiate_with_netbios_service.rb +9 -5
  16. data/examples/net_share_enum_all.rb +6 -4
  17. data/examples/pipes.rb +13 -13
  18. data/examples/query_service_status.rb +64 -0
  19. data/examples/read_file.rb +8 -6
  20. data/examples/read_file_encryption.rb +56 -0
  21. data/examples/read_registry_key_value.rb +33 -0
  22. data/examples/rename_file.rb +9 -7
  23. data/examples/tree_connect.rb +7 -5
  24. data/examples/write_file.rb +9 -7
  25. data/lib/ruby_smb.rb +4 -1
  26. data/lib/ruby_smb/client.rb +239 -21
  27. data/lib/ruby_smb/client/authentication.rb +27 -8
  28. data/lib/ruby_smb/client/encryption.rb +62 -0
  29. data/lib/ruby_smb/client/negotiation.rb +154 -12
  30. data/lib/ruby_smb/client/signing.rb +19 -0
  31. data/lib/ruby_smb/client/tree_connect.rb +4 -4
  32. data/lib/ruby_smb/client/utils.rb +8 -7
  33. data/lib/ruby_smb/client/winreg.rb +46 -0
  34. data/lib/ruby_smb/crypto.rb +30 -0
  35. data/lib/ruby_smb/dcerpc.rb +40 -0
  36. data/lib/ruby_smb/dcerpc/bind.rb +2 -2
  37. data/lib/ruby_smb/dcerpc/bind_ack.rb +2 -2
  38. data/lib/ruby_smb/dcerpc/error.rb +6 -0
  39. data/lib/ruby_smb/dcerpc/ndr.rb +260 -16
  40. data/lib/ruby_smb/dcerpc/pdu_header.rb +1 -1
  41. data/lib/ruby_smb/dcerpc/request.rb +41 -9
  42. data/lib/ruby_smb/dcerpc/rpc_security_attributes.rb +34 -0
  43. data/lib/ruby_smb/dcerpc/rrp_unicode_string.rb +38 -0
  44. data/lib/ruby_smb/dcerpc/srvsvc.rb +10 -0
  45. data/lib/ruby_smb/dcerpc/srvsvc/net_share_enum_all.rb +9 -0
  46. data/lib/ruby_smb/dcerpc/svcctl.rb +479 -0
  47. data/lib/ruby_smb/dcerpc/svcctl/change_service_config_w_request.rb +48 -0
  48. data/lib/ruby_smb/dcerpc/svcctl/change_service_config_w_response.rb +26 -0
  49. data/lib/ruby_smb/dcerpc/svcctl/close_service_handle_request.rb +25 -0
  50. data/lib/ruby_smb/dcerpc/svcctl/close_service_handle_response.rb +26 -0
  51. data/lib/ruby_smb/dcerpc/svcctl/control_service_request.rb +26 -0
  52. data/lib/ruby_smb/dcerpc/svcctl/control_service_response.rb +26 -0
  53. data/lib/ruby_smb/dcerpc/svcctl/open_sc_manager_w_request.rb +35 -0
  54. data/lib/ruby_smb/dcerpc/svcctl/open_sc_manager_w_response.rb +23 -0
  55. data/lib/ruby_smb/dcerpc/svcctl/open_service_w_request.rb +31 -0
  56. data/lib/ruby_smb/dcerpc/svcctl/open_service_w_response.rb +23 -0
  57. data/lib/ruby_smb/dcerpc/svcctl/query_service_config_w_request.rb +25 -0
  58. data/lib/ruby_smb/dcerpc/svcctl/query_service_config_w_response.rb +44 -0
  59. data/lib/ruby_smb/dcerpc/svcctl/query_service_status_request.rb +23 -0
  60. data/lib/ruby_smb/dcerpc/svcctl/query_service_status_response.rb +27 -0
  61. data/lib/ruby_smb/dcerpc/svcctl/service_status.rb +25 -0
  62. data/lib/ruby_smb/dcerpc/svcctl/start_service_w_request.rb +27 -0
  63. data/lib/ruby_smb/dcerpc/svcctl/start_service_w_response.rb +25 -0
  64. data/lib/ruby_smb/dcerpc/winreg.rb +421 -0
  65. data/lib/ruby_smb/dcerpc/winreg/close_key_request.rb +24 -0
  66. data/lib/ruby_smb/dcerpc/winreg/close_key_response.rb +27 -0
  67. data/lib/ruby_smb/dcerpc/winreg/create_key_request.rb +73 -0
  68. data/lib/ruby_smb/dcerpc/winreg/create_key_response.rb +36 -0
  69. data/lib/ruby_smb/dcerpc/winreg/enum_key_request.rb +45 -0
  70. data/lib/ruby_smb/dcerpc/winreg/enum_key_response.rb +42 -0
  71. data/lib/ruby_smb/dcerpc/winreg/enum_value_request.rb +39 -0
  72. data/lib/ruby_smb/dcerpc/winreg/enum_value_response.rb +36 -0
  73. data/lib/ruby_smb/dcerpc/winreg/open_key_request.rb +34 -0
  74. data/lib/ruby_smb/dcerpc/winreg/open_key_response.rb +25 -0
  75. data/lib/ruby_smb/dcerpc/winreg/open_root_key_request.rb +43 -0
  76. data/lib/ruby_smb/dcerpc/winreg/open_root_key_response.rb +35 -0
  77. data/lib/ruby_smb/dcerpc/winreg/query_info_key_request.rb +27 -0
  78. data/lib/ruby_smb/dcerpc/winreg/query_info_key_response.rb +40 -0
  79. data/lib/ruby_smb/dcerpc/winreg/query_value_request.rb +40 -0
  80. data/lib/ruby_smb/dcerpc/winreg/query_value_response.rb +57 -0
  81. data/lib/ruby_smb/dcerpc/winreg/regsam.rb +40 -0
  82. data/lib/ruby_smb/dcerpc/winreg/save_key_request.rb +37 -0
  83. data/lib/ruby_smb/dcerpc/winreg/save_key_response.rb +23 -0
  84. data/lib/ruby_smb/dispatcher/base.rb +1 -1
  85. data/lib/ruby_smb/dispatcher/socket.rb +5 -4
  86. data/lib/ruby_smb/error.rb +28 -1
  87. data/lib/ruby_smb/field/stringz16.rb +17 -1
  88. data/lib/ruby_smb/nbss/session_header.rb +4 -4
  89. data/lib/ruby_smb/smb1/commands.rb +1 -1
  90. data/lib/ruby_smb/smb1/file.rb +8 -14
  91. data/lib/ruby_smb/smb1/packet/session_setup_legacy_request.rb +1 -1
  92. data/lib/ruby_smb/smb1/packet/session_setup_legacy_response.rb +2 -2
  93. data/lib/ruby_smb/smb1/packet/session_setup_request.rb +1 -1
  94. data/lib/ruby_smb/smb1/packet/session_setup_response.rb +2 -2
  95. data/lib/ruby_smb/smb1/packet/write_andx_request.rb +1 -1
  96. data/lib/ruby_smb/smb1/pipe.rb +81 -3
  97. data/lib/ruby_smb/smb1/tree.rb +12 -3
  98. data/lib/ruby_smb/smb2/bit_field/session_flags.rb +2 -1
  99. data/lib/ruby_smb/smb2/bit_field/share_flags.rb +6 -4
  100. data/lib/ruby_smb/smb2/file.rb +51 -61
  101. data/lib/ruby_smb/smb2/negotiate_context.rb +108 -0
  102. data/lib/ruby_smb/smb2/packet.rb +2 -0
  103. data/lib/ruby_smb/smb2/packet/compression_transform_header.rb +41 -0
  104. data/lib/ruby_smb/smb2/packet/error_packet.rb +2 -4
  105. data/lib/ruby_smb/smb2/packet/negotiate_request.rb +51 -14
  106. data/lib/ruby_smb/smb2/packet/negotiate_response.rb +50 -4
  107. data/lib/ruby_smb/smb2/packet/transform_header.rb +84 -0
  108. data/lib/ruby_smb/smb2/packet/tree_connect_request.rb +92 -6
  109. data/lib/ruby_smb/smb2/packet/tree_connect_response.rb +8 -26
  110. data/lib/ruby_smb/smb2/pipe.rb +80 -3
  111. data/lib/ruby_smb/smb2/smb2_header.rb +1 -1
  112. data/lib/ruby_smb/smb2/tree.rb +32 -20
  113. data/lib/ruby_smb/version.rb +1 -1
  114. data/ruby_smb.gemspec +5 -3
  115. data/spec/lib/ruby_smb/client_spec.rb +1583 -102
  116. data/spec/lib/ruby_smb/crypto_spec.rb +25 -0
  117. data/spec/lib/ruby_smb/dcerpc/bind_ack_spec.rb +2 -2
  118. data/spec/lib/ruby_smb/dcerpc/bind_spec.rb +2 -2
  119. data/spec/lib/ruby_smb/dcerpc/ndr_spec.rb +1729 -0
  120. data/spec/lib/ruby_smb/dcerpc/request_spec.rb +50 -7
  121. data/spec/lib/ruby_smb/dcerpc/rpc_security_attributes_spec.rb +161 -0
  122. data/spec/lib/ruby_smb/dcerpc/rrp_unicode_string_spec.rb +135 -0
  123. data/spec/lib/ruby_smb/dcerpc/srvsvc/net_share_enum_all_spec.rb +13 -0
  124. data/spec/lib/ruby_smb/dcerpc/srvsvc_spec.rb +60 -0
  125. data/spec/lib/ruby_smb/dcerpc/svcctl/change_service_config_w_request_spec.rb +191 -0
  126. data/spec/lib/ruby_smb/dcerpc/svcctl/change_service_config_w_response_spec.rb +38 -0
  127. data/spec/lib/ruby_smb/dcerpc/svcctl/close_service_handle_request_spec.rb +30 -0
  128. data/spec/lib/ruby_smb/dcerpc/svcctl/close_service_handle_response_spec.rb +38 -0
  129. data/spec/lib/ruby_smb/dcerpc/svcctl/control_service_request_spec.rb +39 -0
  130. data/spec/lib/ruby_smb/dcerpc/svcctl/control_service_response_spec.rb +38 -0
  131. data/spec/lib/ruby_smb/dcerpc/svcctl/open_sc_manager_w_request_spec.rb +78 -0
  132. data/spec/lib/ruby_smb/dcerpc/svcctl/open_sc_manager_w_response_spec.rb +38 -0
  133. data/spec/lib/ruby_smb/dcerpc/svcctl/open_service_w_request_spec.rb +59 -0
  134. data/spec/lib/ruby_smb/dcerpc/svcctl/open_service_w_response_spec.rb +38 -0
  135. data/spec/lib/ruby_smb/dcerpc/svcctl/query_service_config_w_request_spec.rb +38 -0
  136. data/spec/lib/ruby_smb/dcerpc/svcctl/query_service_config_w_response_spec.rb +152 -0
  137. data/spec/lib/ruby_smb/dcerpc/svcctl/query_service_status_request_spec.rb +30 -0
  138. data/spec/lib/ruby_smb/dcerpc/svcctl/query_service_status_response_spec.rb +38 -0
  139. data/spec/lib/ruby_smb/dcerpc/svcctl/service_status_spec.rb +72 -0
  140. data/spec/lib/ruby_smb/dcerpc/svcctl/start_service_w_request_spec.rb +46 -0
  141. data/spec/lib/ruby_smb/dcerpc/svcctl/start_service_w_response_spec.rb +30 -0
  142. data/spec/lib/ruby_smb/dcerpc/svcctl_spec.rb +512 -0
  143. data/spec/lib/ruby_smb/dcerpc/winreg/close_key_request_spec.rb +28 -0
  144. data/spec/lib/ruby_smb/dcerpc/winreg/close_key_response_spec.rb +36 -0
  145. data/spec/lib/ruby_smb/dcerpc/winreg/create_key_request_spec.rb +110 -0
  146. data/spec/lib/ruby_smb/dcerpc/winreg/create_key_response_spec.rb +44 -0
  147. data/spec/lib/ruby_smb/dcerpc/winreg/enum_key_request_spec.rb +104 -0
  148. data/spec/lib/ruby_smb/dcerpc/winreg/enum_key_response_spec.rb +97 -0
  149. data/spec/lib/ruby_smb/dcerpc/winreg/enum_value_request_spec.rb +94 -0
  150. data/spec/lib/ruby_smb/dcerpc/winreg/enum_value_response_spec.rb +82 -0
  151. data/spec/lib/ruby_smb/dcerpc/winreg/open_key_request_spec.rb +74 -0
  152. data/spec/lib/ruby_smb/dcerpc/winreg/open_key_response_spec.rb +35 -0
  153. data/spec/lib/ruby_smb/dcerpc/winreg/open_root_key_request_spec.rb +95 -0
  154. data/spec/lib/ruby_smb/dcerpc/winreg/open_root_key_response_spec.rb +38 -0
  155. data/spec/lib/ruby_smb/dcerpc/winreg/query_info_key_request_spec.rb +35 -0
  156. data/spec/lib/ruby_smb/dcerpc/winreg/query_info_key_response_spec.rb +113 -0
  157. data/spec/lib/ruby_smb/dcerpc/winreg/query_value_request_spec.rb +88 -0
  158. data/spec/lib/ruby_smb/dcerpc/winreg/query_value_response_spec.rb +138 -0
  159. data/spec/lib/ruby_smb/dcerpc/winreg/regsam_spec.rb +32 -0
  160. data/spec/lib/ruby_smb/dcerpc/winreg/save_key_request_spec.rb +57 -0
  161. data/spec/lib/ruby_smb/dcerpc/winreg/save_key_response_spec.rb +22 -0
  162. data/spec/lib/ruby_smb/dcerpc/winreg_spec.rb +884 -0
  163. data/spec/lib/ruby_smb/dcerpc_spec.rb +81 -0
  164. data/spec/lib/ruby_smb/dispatcher/socket_spec.rb +12 -12
  165. data/spec/lib/ruby_smb/error_spec.rb +59 -0
  166. data/spec/lib/ruby_smb/field/stringz16_spec.rb +12 -0
  167. data/spec/lib/ruby_smb/nbss/session_header_spec.rb +4 -11
  168. data/spec/lib/ruby_smb/smb1/file_spec.rb +9 -1
  169. data/spec/lib/ruby_smb/smb1/packet/session_setup_legacy_request_spec.rb +2 -2
  170. data/spec/lib/ruby_smb/smb1/packet/session_setup_legacy_response_spec.rb +2 -2
  171. data/spec/lib/ruby_smb/smb1/packet/session_setup_request_spec.rb +2 -2
  172. data/spec/lib/ruby_smb/smb1/packet/session_setup_response_spec.rb +1 -1
  173. data/spec/lib/ruby_smb/smb1/pipe_spec.rb +216 -147
  174. data/spec/lib/ruby_smb/smb2/bit_field/session_flags_spec.rb +9 -0
  175. data/spec/lib/ruby_smb/smb2/bit_field/share_flags_spec.rb +27 -0
  176. data/spec/lib/ruby_smb/smb2/file_spec.rb +146 -68
  177. data/spec/lib/ruby_smb/smb2/negotiate_context_spec.rb +332 -0
  178. data/spec/lib/ruby_smb/smb2/packet/compression_transform_header_spec.rb +108 -0
  179. data/spec/lib/ruby_smb/smb2/packet/error_packet_spec.rb +3 -24
  180. data/spec/lib/ruby_smb/smb2/packet/negotiate_request_spec.rb +138 -3
  181. data/spec/lib/ruby_smb/smb2/packet/negotiate_response_spec.rb +120 -2
  182. data/spec/lib/ruby_smb/smb2/packet/transform_header_spec.rb +220 -0
  183. data/spec/lib/ruby_smb/smb2/packet/tree_connect_request_spec.rb +339 -9
  184. data/spec/lib/ruby_smb/smb2/packet/tree_connect_response_spec.rb +3 -30
  185. data/spec/lib/ruby_smb/smb2/pipe_spec.rb +226 -148
  186. data/spec/lib/ruby_smb/smb2/smb2_header_spec.rb +2 -2
  187. data/spec/lib/ruby_smb/smb2/tree_spec.rb +88 -9
  188. metadata +257 -81
  189. metadata.gz.sig +0 -0
  190. data/lib/ruby_smb/smb1/dcerpc.rb +0 -72
  191. data/lib/ruby_smb/smb2/dcerpc.rb +0 -75
@@ -30,35 +30,8 @@ RSpec.describe RubySMB::SMB2::Packet::TreeConnectResponse do
30
30
  end
31
31
  end
32
32
 
33
- describe '#is_directory?' do
34
- it 'returns true if #share_type is 0x01' do
35
- packet.share_type = 0x01
36
- expect(packet.is_directory?).to be true
37
- end
38
-
39
- it 'returns false if #share_type is not 0x01' do
40
- packet.share_type = 0x02
41
- expect(packet.is_directory?).to be false
42
- end
43
- end
44
-
45
- describe '#access_rights' do
46
- it 'is a DirectoryAccessMask if the Tree is a directory' do
47
- allow(packet).to receive(:is_directory?).and_return(true)
48
- expect(packet.access_rights).to be_a RubySMB::SMB2::BitField::DirectoryAccessMask
49
- end
50
-
51
- it 'is a FileAccessMask if the Tree is not a directory' do
52
- allow(packet).to receive(:is_directory?).and_return(false)
53
- expect(packet.access_rights).to be_a RubySMB::SMB2::BitField::FileAccessMask
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
33
+ it 'reads binary data as expected' do
34
+ data = described_class.new
35
+ expect(described_class.read(data.to_binary_s)).to eq(data)
63
36
  end
64
37
  end
@@ -27,14 +27,14 @@ RSpec.describe RubySMB::SMB2::Pipe do
27
27
  last_write: time
28
28
  )
29
29
  }
30
-
31
30
  let(:ioctl_response) {
32
31
  packet = RubySMB::SMB2::Packet::IoctlResponse.new
33
32
  packet.buffer = "\x03\x00\x00\x00" + "\x10\x20\x30\x40" + "\x00\x00\x00\x00" + "\x00\x00\x00\x00"
34
33
  packet
35
34
  }
35
+ let(:filename) { 'msf-pipe' }
36
36
 
37
- subject(:pipe) { described_class.new(name: 'msf-pipe', response: create_response, tree: tree) }
37
+ subject(:pipe) { described_class.new(name: filename, response: create_response, tree: tree) }
38
38
 
39
39
  describe '#peek' do
40
40
  let(:request) { RubySMB::SMB2::Packet::IoctlRequest.new }
@@ -127,206 +127,284 @@ RSpec.describe RubySMB::SMB2::Pipe do
127
127
  end
128
128
  end
129
129
 
130
- context 'with DCERPC' do
131
- describe '#net_share_enum_all' do
132
- let(:host) { '1.2.3.4' }
133
- let(:dcerpc_response) { RubySMB::Dcerpc::Response.new }
134
-
135
- before :example do
136
- allow(pipe).to receive(:bind)
137
- allow(pipe).to receive(:request).and_return(dcerpc_response)
138
- allow(RubySMB::Dcerpc::Srvsvc::NetShareEnumAll).to receive(:parse_response).and_return([])
130
+ describe '#initialize' do
131
+ context 'when name is not provided' do
132
+ it 'raises an ArgumentError' do
133
+ expect {
134
+ described_class.new(tree: tree, response: create_response, name: nil)
135
+ }.to raise_error(ArgumentError)
139
136
  end
137
+ end
140
138
 
141
- it 'calls #bind with the expected arguments' do
142
- expect(pipe).to receive(:bind).with(endpoint: RubySMB::Dcerpc::Srvsvc)
143
- pipe.net_share_enum_all(host)
144
- end
139
+ it 'calls the superclass with the expected arguments' do
140
+ expect(pipe.tree).to eq(tree)
141
+ expect(pipe.name).to eq(filename)
142
+ expect(pipe.attributes).to eq(create_response.file_attributes)
143
+ expect(pipe.guid).to eq(create_response.file_id)
144
+ expect(pipe.last_access).to eq(create_response.last_access.to_datetime)
145
+ expect(pipe.last_change).to eq(create_response.last_change.to_datetime)
146
+ expect(pipe.last_write).to eq(create_response.last_write.to_datetime)
147
+ expect(pipe.size).to eq(create_response.end_of_file)
148
+ expect(pipe.size_on_disk).to eq(create_response.allocation_size)
149
+ end
145
150
 
146
- it 'calls #request with the expected arguments' do
147
- expect(pipe).to receive(:request).with(RubySMB::Dcerpc::Srvsvc::NET_SHARE_ENUM_ALL, host: host)
148
- pipe.net_share_enum_all(host)
151
+ context 'with \'srvsvc\' filename' do
152
+ it 'extends Srvsvc class' do
153
+ pipe = described_class.new(tree: tree, response: create_response, name: 'srvsvc')
154
+ expect(pipe.respond_to?(:net_share_enum_all)).to be true
149
155
  end
156
+ end
150
157
 
151
- it 'parse the response with NetShareEnumAll #parse_response method' do
152
- stub = 'ABCD'
153
- dcerpc_response.alloc_hint = stub.size
154
- dcerpc_response.stub = stub
155
- expect(RubySMB::Dcerpc::Srvsvc::NetShareEnumAll).to receive(:parse_response).with(stub)
156
- pipe.net_share_enum_all(host)
158
+ context 'with \'winreg\' filename' do
159
+ it 'extends Winreg class' do
160
+ pipe = described_class.new(tree: tree, response: create_response, name: 'winreg')
161
+ expect(pipe.respond_to?(:has_registry_key?)).to be true
157
162
  end
163
+ end
158
164
 
159
- it 'returns the remote shares' do
160
- shares = [
161
- ["C$", "DISK", "Default share"],
162
- ["Shared", "DISK", ""],
163
- ["IPC$", "IPC", "Remote IPC"],
164
- ["ADMIN$", "DISK", "Remote Admin"]
165
- ]
166
- output = [
167
- {:name=>"C$", :type=>"DISK", :comment=>"Default share"},
168
- {:name=>"Shared", :type=>"DISK", :comment=>""},
169
- {:name=>"IPC$", :type=>"IPC", :comment=>"Remote IPC"},
170
- {:name=>"ADMIN$", :type=>"DISK", :comment=>"Remote Admin"},
171
- ]
172
- allow(RubySMB::Dcerpc::Srvsvc::NetShareEnumAll).to receive(:parse_response).and_return(shares)
173
- expect(pipe.net_share_enum_all(host)).to eq(output)
165
+ context 'with \'svcctl\' filename' do
166
+ it 'extends svcctl class' do
167
+ pipe = described_class.new(tree: tree, response: create_response, name: 'svcctl')
168
+ expect(pipe.respond_to?(:query_service_config)).to be true
174
169
  end
175
170
  end
171
+ end
176
172
 
177
- describe '#bind' do
178
- let(:options) { { endpoint: RubySMB::Dcerpc::Srvsvc } }
179
- let(:bind_packet) { RubySMB::Dcerpc::Bind.new(options) }
180
- let(:bind_ack_packet) { RubySMB::Dcerpc::BindAck.new }
173
+ describe '#dcerpc_request' do
174
+ let(:options) { { host: '1.2.3.4' } }
175
+ let(:stub_packet ) { RubySMB::Dcerpc::Winreg::OpenKeyRequest.new }
176
+ let(:dcerpc_request) { double('DCERPC Request') }
177
+ let(:request_stub) { double('Request stub') }
178
+ before :example do
179
+ allow(RubySMB::Dcerpc::Request).to receive(:new).and_return(dcerpc_request)
180
+ allow(dcerpc_request).to receive(:stub).and_return(request_stub)
181
+ allow(request_stub).to receive(:read)
182
+ allow(pipe).to receive(:ioctl_send_recv)
183
+ end
181
184
 
182
- before :example do
183
- allow(RubySMB::Dcerpc::Bind).to receive(:new).and_return(bind_packet)
184
- allow(pipe).to receive(:write)
185
- allow(pipe).to receive(:read)
186
- bind_ack_packet.p_result_list.n_results = 1
187
- bind_ack_packet.p_result_list.p_results[0].result = RubySMB::Dcerpc::BindAck::ACCEPTANCE
188
- allow(RubySMB::Dcerpc::BindAck).to receive(:read).and_return(bind_ack_packet)
189
- end
185
+ it 'creates a Request packet with the expected arguments' do
186
+ pipe.dcerpc_request(stub_packet, options)
187
+ expect(options).to eq( { host: '1.2.3.4', endpoint: 'Winreg' })
188
+ expect(RubySMB::Dcerpc::Request).to have_received(:new).with({ opnum: stub_packet.opnum }, options)
189
+ end
190
190
 
191
- it 'creates a Bind packet' do
192
- expect(RubySMB::Dcerpc::Bind).to receive(:new).with(options).and_return(bind_packet)
193
- pipe.bind(options)
194
- end
191
+ it 'sets DCERPC request stub to the stub packet passed as argument' do
192
+ pipe.dcerpc_request(stub_packet, options)
193
+ expect(request_stub).to have_received(:read).with(stub_packet.to_binary_s)
194
+ end
195
195
 
196
- it 'writes to the named pipe' do
197
- expect(pipe).to receive(:write).with(data: bind_packet.to_binary_s)
198
- pipe.bind(options)
199
- end
196
+ it 'calls #ioctl_send_recv with the expected arguments' do
197
+ pipe.dcerpc_request(stub_packet, options)
198
+ expect(pipe).to have_received(:ioctl_send_recv).with(dcerpc_request, options)
199
+ end
200
+ end
200
201
 
201
- it 'reads the socket' do
202
- expect(pipe).to receive(:read)
203
- pipe.bind(options)
202
+ describe '#ioctl_send_recv' do
203
+ let(:ioctl_request_packet) { double('IoctlRequest') }
204
+ let(:flags) { double('Flags') }
205
+ let(:dcerpc_request) { double('DCERPC Request') }
206
+ let(:binary_dcerpc_request) { double('Binary DCERPC Request') }
207
+ let(:options) { { host: '1.2.3.4' } }
208
+ let(:ioctl_raw_response) { double('IOCTL raw response') }
209
+ let(:ioctl_response) { double('IOCTL response') }
210
+ let(:raw_data) { double('Raw data') }
211
+ let(:dcerpc_response) { double('DCERPC Response') }
212
+ let(:result) { 'Result' }
213
+ before :example do
214
+ allow(RubySMB::SMB2::Packet::IoctlRequest).to receive(:new).and_return(ioctl_request_packet)
215
+ allow(pipe).to receive(:set_header_fields).and_return(ioctl_request_packet)
216
+ allow(ioctl_request_packet).to receive_messages(
217
+ :ctl_code= => nil,
218
+ :flags => flags,
219
+ :buffer= => nil
220
+ )
221
+ allow(flags).to receive(:is_fsctl=)
222
+ allow(dcerpc_request).to receive(:to_binary_s).and_return(binary_dcerpc_request)
223
+ allow(client).to receive(:send_recv).and_return(ioctl_raw_response)
224
+ allow(RubySMB::SMB2::Packet::IoctlResponse).to receive(:read).and_return(ioctl_response)
225
+ allow(ioctl_response).to receive_messages(
226
+ :valid? => true,
227
+ :status_code => WindowsError::NTStatus::STATUS_SUCCESS,
228
+ :output_data => raw_data
229
+ )
230
+ allow(RubySMB::Dcerpc::Response).to receive(:read).and_return(dcerpc_response)
231
+ allow(dcerpc_response).to receive_message_chain(:pdu_header, :ptype => RubySMB::Dcerpc::PTypes::RESPONSE)
232
+ allow(dcerpc_response).to receive(:stub).and_return(result)
233
+ end
234
+
235
+ it 'creates an IoctlRequest packet' do
236
+ pipe.ioctl_send_recv(dcerpc_request, options)
237
+ expect(RubySMB::SMB2::Packet::IoctlRequest).to have_received(:new).with(options)
238
+ end
239
+
240
+ it 'calls #set_header_fields' do
241
+ pipe.ioctl_send_recv(dcerpc_request, options)
242
+ expect(pipe).to have_received(:set_header_fields).with(ioctl_request_packet)
243
+ end
244
+
245
+ it 'sets the expected properties on the request packet' do
246
+ pipe.ioctl_send_recv(dcerpc_request, options)
247
+ expect(ioctl_request_packet).to have_received(:ctl_code=).with(0x11C017)
248
+ expect(flags).to have_received(:is_fsctl=).with(0x1)
249
+ expect(ioctl_request_packet).to have_received(:buffer=).with(binary_dcerpc_request)
250
+ end
251
+
252
+ it 'sends the expected request' do
253
+ pipe.ioctl_send_recv(dcerpc_request, options)
254
+ expect(client).to have_received(:send_recv).with(ioctl_request_packet)
255
+ end
256
+
257
+ it 'creates an IoctlResponse packet from the response' do
258
+ pipe.ioctl_send_recv(dcerpc_request, options)
259
+ expect(RubySMB::SMB2::Packet::IoctlResponse).to have_received(:read).with(ioctl_raw_response)
260
+ end
261
+
262
+ context 'when the response is not an IoctlResponse packet' do
263
+ it 'raises an InvalidPacket exception' do
264
+ allow(ioctl_response).to receive_message_chain(:smb2_header, :protocol)
265
+ allow(ioctl_response).to receive_message_chain(:smb2_header, :command)
266
+ allow(ioctl_response).to receive(:valid?).and_return(false)
267
+ expect { pipe.ioctl_send_recv(dcerpc_request, options) }.to raise_error(RubySMB::Error::InvalidPacket)
204
268
  end
269
+ end
205
270
 
206
- it 'creates a BindAck packet from the response' do
207
- raw_response = RubySMB::Dcerpc::BindAck.new.to_binary_s
208
- allow(pipe).to receive(:read).and_return(raw_response)
209
- expect(RubySMB::Dcerpc::BindAck).to receive(:read).with(raw_response).and_return(bind_ack_packet)
210
- pipe.bind(options)
271
+ context 'when the response status code is not STATUS_SUCCESS or STATUS_BUFFER_OVERFLOW' do
272
+ it 'raises an UnexpectedStatusCode exception' do
273
+ allow(ioctl_response).to receive(:status_code).and_return(WindowsError::NTStatus::STATUS_INVALID_HANDLE)
274
+ expect { pipe.ioctl_send_recv(dcerpc_request, options) }.to raise_error(RubySMB::Error::UnexpectedStatusCode)
211
275
  end
276
+ end
212
277
 
213
- it 'raises the expected exception when an invalid packet is received' do
214
- allow(RubySMB::Dcerpc::BindAck).to receive(:read).and_raise(IOError)
215
- expect { pipe.bind(options) }.to raise_error(RubySMB::Dcerpc::Error::InvalidPacket)
278
+ context 'when the response status code is STATUS_SUCCESS' do
279
+ it 'does not raise any exception' do
280
+ expect { pipe.ioctl_send_recv(dcerpc_request, options)}.not_to raise_error
216
281
  end
217
282
 
218
- it 'raises the expected exception when it is not a BindAck packet' do
219
- response = RubySMB::Dcerpc::Bind.new
220
- allow(RubySMB::Dcerpc::BindAck).to receive(:read).and_return(response)
221
- expect { pipe.bind(options) }.to raise_error(RubySMB::Dcerpc::Error::BindError)
283
+ it 'creates a DCERPC Response packet from the response' do
284
+ pipe.ioctl_send_recv(dcerpc_request, options)
285
+ expect(RubySMB::Dcerpc::Response).to have_received(:read).with(raw_data)
222
286
  end
223
287
 
224
- it 'raises an exception when no result is returned' do
225
- bind_ack_packet.p_result_list.n_results = 0
226
- expect { pipe.bind(options) }.to raise_error(RubySMB::Dcerpc::Error::BindError)
288
+ context 'when an IOError occurs while parsing the DCERPC response' do
289
+ it 'raises an InvalidPacket exception' do
290
+ allow(RubySMB::Dcerpc::Response).to receive(:read).and_raise(IOError)
291
+ expect { pipe.ioctl_send_recv(dcerpc_request, options) }.to raise_error(RubySMB::Dcerpc::Error::InvalidPacket)
292
+ end
227
293
  end
228
294
 
229
- it 'raises an exception when result is not ACCEPTANCE' do
230
- bind_ack_packet.p_result_list.p_results[0].result = RubySMB::Dcerpc::BindAck::USER_REJECTION
231
- expect { pipe.bind(options) }.to raise_error(RubySMB::Dcerpc::Error::BindError)
295
+ context 'when the response is not a DCERPC Response packet' do
296
+ it 'raises an InvalidPacket exception' do
297
+ allow(dcerpc_response).to receive_message_chain(:pdu_header, :ptype => RubySMB::Dcerpc::PTypes::FAULT)
298
+ expect { pipe.ioctl_send_recv(dcerpc_request, options) }.to raise_error(RubySMB::Dcerpc::Error::InvalidPacket)
299
+ end
232
300
  end
233
301
 
234
- it 'returns the expected BindAck packet' do
235
- expect(pipe.bind(options)).to eq(bind_ack_packet)
302
+ it 'returns the expected stub data' do
303
+ expect(pipe.ioctl_send_recv(dcerpc_request, options)).to eq(result)
236
304
  end
237
305
  end
238
306
 
239
- describe '#request' do
240
- let(:options) { { host: '1.2.3.4' } }
241
- let(:opnum) { RubySMB::Dcerpc::Srvsvc::NET_SHARE_ENUM_ALL }
242
- let(:req_packet) { RubySMB::Dcerpc::Request.new({ :opnum => opnum }, options) }
243
- let(:ioctl_response) { RubySMB::SMB2::Packet::IoctlResponse.new }
244
- let(:res_packet) { RubySMB::Dcerpc::Response.new }
245
-
307
+ context 'when the response status code is STATUS_BUFFER_OVERFLOW' do
308
+ let(:data_count) { 100 }
309
+ let(:added_raw_data) { double('Added raw data') }
246
310
  before :example do
247
- allow(RubySMB::Dcerpc::Request).to receive(:new).and_return(req_packet)
248
- allow(pipe).to receive(:ioctl_send_recv).and_return(ioctl_response)
249
- allow(RubySMB::Dcerpc::Response).to receive(:read).and_return(res_packet)
311
+ allow(ioctl_response).to receive(:status_code).and_return(WindowsError::NTStatus::STATUS_BUFFER_OVERFLOW)
312
+ allow(ioctl_response).to receive(:output_count).and_return(data_count)
313
+ allow(pipe).to receive(:read).and_return(added_raw_data)
314
+ allow(raw_data).to receive(:<<)
315
+ allow(dcerpc_response).to receive_message_chain(:pdu_header, :pfc_flags, :first_frag => 1)
316
+ allow(dcerpc_response).to receive_message_chain(:pdu_header, :pfc_flags, :last_frag => 1)
250
317
  end
251
318
 
252
- it 'creates a Request packet' do
253
- expect(RubySMB::Dcerpc::Request).to receive(:new).and_return(req_packet)
254
- pipe.request(opnum, options)
319
+ it 'does not raise any exception' do
320
+ expect { pipe.ioctl_send_recv(dcerpc_request, options) }.not_to raise_error
255
321
  end
256
322
 
257
- it 'calls #ioctl_send_recv' do
258
- expect(pipe).to receive(:ioctl_send_recv).with(req_packet, options)
259
- pipe.request(opnum, options)
323
+ it 'reads the expected number of bytes and concatenate it the first response raw data' do
324
+ pipe.ioctl_send_recv(dcerpc_request, options)
325
+ expect(pipe).to have_received(:read).with(bytes: tree.client.max_buffer_size - data_count)
326
+ expect(raw_data).to have_received(:<<).with(added_raw_data)
260
327
  end
261
328
 
262
- it 'creates a DCERPC Response packet from the response' do
263
- expect(RubySMB::Dcerpc::Response).to receive(:read).with(ioctl_response.output_data)
264
- pipe.request(opnum, options)
329
+ it 'creates a DCERPC Response packet from the updated raw data' do
330
+ pipe.ioctl_send_recv(dcerpc_request, options)
331
+ expect(RubySMB::Dcerpc::Response).to have_received(:read).with(raw_data)
265
332
  end
266
333
 
267
- it 'raises the expected exception when an invalid packet is received' do
268
- allow(RubySMB::Dcerpc::Response).to receive(:read).and_raise(IOError)
269
- expect { pipe.request(opnum, options) }.to raise_error(RubySMB::Dcerpc::Error::InvalidPacket)
334
+ context 'when an IOError occurs while parsing the DCERPC response' do
335
+ it 'raises an InvalidPacket exception' do
336
+ allow(RubySMB::Dcerpc::Response).to receive(:read).and_raise(IOError)
337
+ expect { pipe.ioctl_send_recv(dcerpc_request, options) }.to raise_error(RubySMB::Dcerpc::Error::InvalidPacket)
338
+ end
270
339
  end
271
340
 
272
- it 'raises the expected exception when it is not a BindAck packet' do
273
- response = RubySMB::Dcerpc::Request.new
274
- allow(RubySMB::Dcerpc::Response).to receive(:read).and_return(response)
275
- expect { pipe.request(opnum, options) }.to raise_error(RubySMB::Dcerpc::Error::InvalidPacket)
341
+ context 'when the response is not a DCERPC Response packet' do
342
+ it 'raises an InvalidPacket exception' do
343
+ allow(dcerpc_response).to receive_message_chain(:pdu_header, :ptype => RubySMB::Dcerpc::PTypes::FAULT)
344
+ expect { pipe.ioctl_send_recv(dcerpc_request, options) }.to raise_error(RubySMB::Dcerpc::Error::InvalidPacket)
345
+ end
276
346
  end
277
347
 
278
- it 'returns the expected DCERPC Response' do
279
- expect(pipe.request(opnum, options)).to eq(res_packet)
348
+ context 'when the response is not the first fragment' do
349
+ it 'raises an InvalidPacket exception' do
350
+ allow(dcerpc_response).to receive_message_chain(:pdu_header, :pfc_flags, :first_frag => 0)
351
+ expect { pipe.ioctl_send_recv(dcerpc_request, options) }.to raise_error(RubySMB::Dcerpc::Error::InvalidPacket)
352
+ end
280
353
  end
281
- end
282
354
 
283
- describe '#ioctl_send_recv' do
284
- let(:action) { RubySMB::Dcerpc::Request.new({ :opnum => RubySMB::Dcerpc::Srvsvc::NET_SHARE_ENUM_ALL }, host: '1.2.3.4') }
285
- let(:options) { {} }
286
- let(:ioctl_request) { RubySMB::SMB2::Packet::IoctlRequest.new(options) }
287
- let(:ioctl_response) { RubySMB::SMB2::Packet::IoctlResponse.new }
355
+ context 'when the response is the last fragment' do
356
+ it 'only reads the pipe once' do
357
+ pipe.ioctl_send_recv(dcerpc_request, options)
358
+ expect(RubySMB::Dcerpc::Response).to have_received(:read).once
359
+ end
288
360
 
289
- before :example do
290
- allow(client).to receive(:send_recv).and_return(ioctl_response.to_binary_s)
361
+ it 'returns the expected stub data' do
362
+ expect(pipe.ioctl_send_recv(dcerpc_request, options)).to eq(result)
363
+ end
291
364
  end
292
365
 
293
- it 'calls #set_header_fields' do
294
- expect(pipe).to receive(:set_header_fields).with(ioctl_request).and_call_original
295
- pipe.ioctl_send_recv(action, options)
296
- end
366
+ context 'when the response is not the last fragment' do
367
+ let(:raw_data2) { double('Raw data #2') }
368
+ let(:dcerpc_response2) { double('DCERPC Response #2') }
369
+ let(:result2) { 'Result #2' }
370
+ before :example do
371
+ allow(dcerpc_response).to receive_message_chain(:pdu_header, :pfc_flags, :last_frag => 0)
372
+ allow(pipe).to receive(:read).with(bytes: tree.client.max_buffer_size).and_return(raw_data2)
373
+ allow(RubySMB::Dcerpc::Response).to receive(:read).with(raw_data2).and_return(dcerpc_response2)
374
+ allow(dcerpc_response2).to receive_message_chain(:pdu_header, :ptype => RubySMB::Dcerpc::PTypes::RESPONSE)
375
+ allow(dcerpc_response2).to receive_message_chain(:pdu_header, :pfc_flags, :last_frag => 1)
376
+ allow(dcerpc_response2).to receive(:stub).and_return(result2)
377
+ end
297
378
 
298
- it 'calls Client #send_recv with the expected request' do
299
- expect(client).to receive(:send_recv) do |req|
300
- expect(req.ctl_code).to eq(0x0011C017)
301
- expect(req.flags.is_fsctl).to eq(0x00000001)
302
- expect(req.buffer).to eq(action.to_binary_s)
303
- ioctl_response.to_binary_s
379
+ it 'reads the expected number of bytes' do
380
+ pipe.ioctl_send_recv(dcerpc_request, options)
381
+ expect(pipe).to have_received(:read).with(bytes: tree.client.max_buffer_size)
304
382
  end
305
- pipe.ioctl_send_recv(action, options)
306
- end
307
383
 
308
- it 'creates a IoctlResponse packet from the response' do
309
- expect(RubySMB::SMB2::Packet::IoctlResponse).to receive(:read).with(ioctl_response.to_binary_s).and_call_original
310
- pipe.ioctl_send_recv(action, options)
311
- end
384
+ it 'creates a DCERPC Response packet from the new raw data' do
385
+ pipe.ioctl_send_recv(dcerpc_request, options)
386
+ expect(RubySMB::Dcerpc::Response).to have_received(:read).with(raw_data2)
387
+ end
312
388
 
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)
316
- expect { pipe.ioctl_send_recv(action, options) }.to raise_error(RubySMB::Error::InvalidPacket)
317
- end
389
+ context 'when an IOError occurs while parsing the new DCERPC response' do
390
+ it 'raises an InvalidPacket exception' do
391
+ allow(RubySMB::Dcerpc::Response).to receive(:read).with(raw_data2).and_raise(IOError)
392
+ expect { pipe.ioctl_send_recv(dcerpc_request, options) }.to raise_error(RubySMB::Dcerpc::Error::InvalidPacket)
393
+ end
394
+ end
318
395
 
319
- it 'raises the expected exception when the status code is not STATUS_SUCCESS' do
320
- ioctl_response_packet = RubySMB::SMB2::Packet::IoctlResponse.new
321
- ioctl_response_packet.smb2_header.nt_status = WindowsError::NTStatus::STATUS_INVALID_HANDLE.value
322
- allow(RubySMB::SMB2::Packet::IoctlResponse).to receive(:read).with(ioctl_response.to_binary_s).and_return(ioctl_response_packet)
323
- expect { pipe.ioctl_send_recv(action, options) }.to raise_error(RubySMB::Error::UnexpectedStatusCode)
324
- end
396
+ context 'when the new response is not a DCERPC Response packet' do
397
+ it 'raises an InvalidPacket exception' do
398
+ allow(dcerpc_response2).to receive_message_chain(:pdu_header, :ptype => RubySMB::Dcerpc::PTypes::FAULT)
399
+ expect { pipe.ioctl_send_recv(dcerpc_request, options) }.to raise_error(RubySMB::Dcerpc::Error::InvalidPacket)
400
+ end
401
+ end
325
402
 
326
- it 'returns the expected DCERPC Response' do
327
- expect(pipe.ioctl_send_recv(action, options)).to eq(ioctl_response)
403
+ it 'returns the expected stub data' do
404
+ expect(pipe.ioctl_send_recv(dcerpc_request, options)).to eq(result)
405
+ end
328
406
  end
329
407
  end
330
408
  end
331
- end
332
409
 
410
+ end