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
@@ -0,0 +1,81 @@
1
+ RSpec.describe RubySMB::Dcerpc do
2
+ let(:tree) { double('Tree') }
3
+ let(:pipe) do
4
+ RubySMB::SMB1::Pipe.new(
5
+ tree: tree,
6
+ response: RubySMB::SMB1::Packet::NtCreateAndxResponse.new,
7
+ name: 'winreg'
8
+ )
9
+ end
10
+
11
+ describe '#bind' do
12
+ let(:options) { { endpoint: RubySMB::Dcerpc::Winreg } }
13
+ let(:bind_packet) { RubySMB::Dcerpc::Bind.new(options) }
14
+ let(:bind_ack_packet) { RubySMB::Dcerpc::BindAck.new }
15
+ let(:client) { double('Client') }
16
+
17
+ before :example do
18
+ allow(RubySMB::Dcerpc::Bind).to receive(:new).and_return(bind_packet)
19
+ allow(pipe).to receive(:write)
20
+ allow(pipe).to receive(:read)
21
+ bind_ack_packet.p_result_list.n_results = 1
22
+ bind_ack_packet.p_result_list.p_results[0].result = RubySMB::Dcerpc::BindAck::ACCEPTANCE
23
+ allow(RubySMB::Dcerpc::BindAck).to receive(:read).and_return(bind_ack_packet)
24
+ allow(tree).to receive(:client).and_return(client)
25
+ allow(client).to receive(:max_buffer_size=)
26
+ end
27
+
28
+ it 'creates a Bind packet' do
29
+ pipe.bind(options)
30
+ expect(RubySMB::Dcerpc::Bind).to have_received(:new).with(options)
31
+ end
32
+
33
+ it 'writes to the named pipe' do
34
+ pipe.bind(options)
35
+ expect(pipe).to have_received(:write).with(data: bind_packet.to_binary_s)
36
+ end
37
+
38
+ it 'reads the socket' do
39
+ pipe.bind(options)
40
+ expect(pipe).to have_received(:read)
41
+ end
42
+
43
+ it 'creates a BindAck packet from the response' do
44
+ raw_response = RubySMB::Dcerpc::BindAck.new.to_binary_s
45
+ allow(pipe).to receive(:read).and_return(raw_response)
46
+ pipe.bind(options)
47
+ expect(RubySMB::Dcerpc::BindAck).to have_received(:read).with(raw_response)
48
+ end
49
+
50
+ it 'raises the expected exception when an invalid packet is received' do
51
+ allow(RubySMB::Dcerpc::BindAck).to receive(:read).and_raise(IOError)
52
+ expect { pipe.bind(options) }.to raise_error(RubySMB::Dcerpc::Error::InvalidPacket)
53
+ end
54
+
55
+ it 'raises the expected exception when it is not a BindAck packet' do
56
+ response = RubySMB::Dcerpc::Bind.new
57
+ allow(RubySMB::Dcerpc::BindAck).to receive(:read).and_return(response)
58
+ expect { pipe.bind(options) }.to raise_error(RubySMB::Dcerpc::Error::BindError)
59
+ end
60
+
61
+ it 'raises an exception when no result is returned' do
62
+ bind_ack_packet.p_result_list.n_results = 0
63
+ expect { pipe.bind(options) }.to raise_error(RubySMB::Dcerpc::Error::BindError)
64
+ end
65
+
66
+ it 'raises an exception when result is not ACCEPTANCE' do
67
+ bind_ack_packet.p_result_list.p_results[0].result = RubySMB::Dcerpc::BindAck::USER_REJECTION
68
+ expect { pipe.bind(options) }.to raise_error(RubySMB::Dcerpc::Error::BindError)
69
+ end
70
+
71
+ it 'sets the Tree #client.max_buffer_size property to the DCERPC response #max_xmit_frag property value' do
72
+ bind_ack_packet.max_xmit_frag = 64
73
+ pipe.bind(options)
74
+ expect(client).to have_received(:max_buffer_size=).with(64)
75
+ end
76
+
77
+ it 'returns the expected BindAck packet' do
78
+ expect(pipe.bind(options)).to eq(bind_ack_packet)
79
+ end
80
+ end
81
+ end
@@ -51,9 +51,9 @@ RSpec.describe RubySMB::Dispatcher::Socket do
51
51
  let(:session_header) { RubySMB::Nbss::SessionHeader.new }
52
52
 
53
53
  context 'when reading from the socket results in a nil value' do
54
- it 'should raise Error::NetBiosSessionService' do
54
+ it 'should raise Error::CommunicationError' do
55
55
  smb_socket.tcp_socket = blank_socket
56
- expect { smb_socket.recv_packet }.to raise_error(::RubySMB::Error::NetBiosSessionService)
56
+ expect { smb_socket.recv_packet }.to raise_error(::RubySMB::Error::CommunicationError)
57
57
  end
58
58
  end
59
59
 
@@ -102,26 +102,26 @@ RSpec.describe RubySMB::Dispatcher::Socket do
102
102
  end
103
103
 
104
104
  it 'reads the number of bytes specified in the nbss header' do
105
- packet_length = 10
106
- session_header.packet_length = packet_length
105
+ stream_protocol_length = 10
106
+ session_header.stream_protocol_length = stream_protocol_length
107
107
  allow(RubySMB::Nbss::SessionHeader).to receive(:read).and_return(session_header)
108
108
  expect(response_socket).to receive(:read).with(4).and_return(session_header)
109
- expect(response_socket).to receive(:read).with(packet_length).and_return('A' * packet_length)
109
+ expect(response_socket).to receive(:read).with(stream_protocol_length).and_return('A' * stream_protocol_length)
110
110
  smb_socket.recv_packet
111
111
  end
112
112
 
113
113
  context 'when the socket does not return everything the first time' do
114
114
  it 'calls #read several times until everything is returned' do
115
- packet_length = 67
115
+ stream_protocol_length = 67
116
116
  returned_length = 10
117
- session_header.packet_length = packet_length
117
+ session_header.stream_protocol_length = stream_protocol_length
118
118
  allow(RubySMB::Nbss::SessionHeader).to receive(:read).and_return(session_header)
119
119
 
120
120
  expect(response_socket).to receive(:read).with(4).and_return(session_header)
121
121
  loop do
122
- expect(response_socket).to receive(:read).with(packet_length).and_return('A' * returned_length).once
123
- packet_length -= returned_length
124
- break if packet_length <= 0
122
+ expect(response_socket).to receive(:read).with(stream_protocol_length).and_return('A' * returned_length).once
123
+ stream_protocol_length -= returned_length
124
+ break if stream_protocol_length <= 0
125
125
  end
126
126
  smb_socket.recv_packet
127
127
  end
@@ -134,7 +134,7 @@ RSpec.describe RubySMB::Dispatcher::Socket do
134
134
 
135
135
  context 'when the SMB packet is empty' do
136
136
  it 'returns the nbss header only' do
137
- session_header.packet_length = 0
137
+ session_header.stream_protocol_length = 0
138
138
  allow(RubySMB::Nbss::SessionHeader).to receive(:read).and_return(session_header)
139
139
  expect(smb_socket.recv_packet(full_response: true)).to eq(session_header.to_binary_s)
140
140
  end
@@ -148,7 +148,7 @@ RSpec.describe RubySMB::Dispatcher::Socket do
148
148
 
149
149
  context 'when the SMB packet is empty' do
150
150
  it 'returns an empty string' do
151
- session_header.packet_length = 0
151
+ session_header.stream_protocol_length = 0
152
152
  allow(RubySMB::Nbss::SessionHeader).to receive(:read).and_return(session_header)
153
153
  expect(smb_socket.recv_packet(full_response: false)).to eq('')
154
154
  end
@@ -0,0 +1,59 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe RubySMB::Error::InvalidPacket do
4
+ context 'with a String' do
5
+ it 'outputs the expected error message' do
6
+ ex = described_class.new('My exception')
7
+ expect(ex.to_s).to eq('My exception')
8
+ end
9
+ end
10
+
11
+ context 'with a Hash' do
12
+ it 'outputs the expected error message' do
13
+ ex = described_class.new(
14
+ expected_proto: RubySMB::SMB1::SMB_PROTOCOL_ID,
15
+ expected_cmd: RubySMB::SMB1::Packet::NegotiateResponseExtended::COMMAND,
16
+ expected_custom: "extended_security=1",
17
+ received_proto: RubySMB::SMB2::SMB2_PROTOCOL_ID,
18
+ received_cmd: RubySMB::SMB2::Packet::NegotiateResponse::COMMAND,
19
+ received_custom: "extended_security=0"
20
+ )
21
+ expect(ex.to_s).to eq('Expecting SMB1 protocol with command=114 (extended_security=1), got SMB2 protocol with command=0 (extended_security=0)')
22
+ end
23
+ end
24
+
25
+ context 'with an unsupported type' do
26
+ it 'raises the expected exception' do
27
+ expect { described_class.new(['wrong']) }.to raise_error(
28
+ ArgumentError,
29
+ 'InvalidPacket expects a String or a Hash, got a Array'
30
+ )
31
+ end
32
+ end
33
+ end
34
+
35
+
36
+ RSpec.describe RubySMB::Error::UnexpectedStatusCode do
37
+ context 'with a WindowsError::ErrorCode' do
38
+ it 'outputs the expected error message' do
39
+ ex = described_class.new(WindowsError::NTStatus::STATUS_BUFFER_OVERFLOW)
40
+ expect(ex.to_s).to eq('The server responded with an unexpected status code: STATUS_BUFFER_OVERFLOW')
41
+ end
42
+ end
43
+
44
+ context 'with an Integer' do
45
+ it 'outputs the expected error message' do
46
+ ex = described_class.new(0x80000005)
47
+ expect(ex.to_s).to eq('The server responded with an unexpected status code: STATUS_BUFFER_OVERFLOW')
48
+ end
49
+ end
50
+
51
+ context 'with an unsupported type' do
52
+ it 'raises the expected exception' do
53
+ expect { described_class.new(['wrong']) }.to raise_error(
54
+ ArgumentError,
55
+ 'Status code must be a WindowsError::ErrorCode or an Integer, got Array'
56
+ )
57
+ end
58
+ end
59
+ end
@@ -44,5 +44,17 @@ RSpec.describe RubySMB::Field::Stringz16 do
44
44
  io = StringIO.new("A\x00B\x00C\x00D\x00")
45
45
  expect { stringz16.read(io) }.to raise_error(EOFError)
46
46
  end
47
+
48
+ it 'trims the string to #max_length and makes sure it ends with a null terminator' do
49
+ io = StringIO.new("A\x00B\x00C\x00D\x00")
50
+ str = described_class.new(max_length: 6)
51
+ expect(str.read(io).to_binary_s).to eq("A\x00B\x00\x00\x00".b)
52
+ end
53
+
54
+ it 'raises an exception when #max_length is not a multiple of two' do
55
+ io = StringIO.new("A\x00B\x00C\x00D\x00")
56
+ str = described_class.new(max_length: 5)
57
+ expect { str.read(io) }.to raise_error(ArgumentError)
58
+ end
47
59
  end
48
60
  end
@@ -2,8 +2,7 @@ RSpec.describe RubySMB::Nbss::SessionHeader do
2
2
  subject(:session_header) { described_class.new }
3
3
 
4
4
  it { is_expected.to respond_to :session_packet_type }
5
- it { is_expected.to respond_to :flags }
6
- it { is_expected.to respond_to :packet_length }
5
+ it { is_expected.to respond_to :stream_protocol_length }
7
6
 
8
7
  it 'is big endian' do
9
8
  expect(described_class.fields.instance_variable_get(:@hints)[:endian]).to eq :big
@@ -15,15 +14,9 @@ RSpec.describe RubySMB::Nbss::SessionHeader do
15
14
  end
16
15
  end
17
16
 
18
- describe '#flags' do
19
- it 'is a 7-bit Unsigned Integer' do
20
- expect(session_header.flags).to be_a BinData::Bit7
21
- end
22
- end
23
-
24
- describe '#packet_length' do
25
- it 'is a 17-bit Unsigned Integer' do
26
- expect(session_header.packet_length).to be_a BinData::Bit17
17
+ describe '#stream_protocol_length' do
18
+ it 'is a 24-bit Unsigned Integer' do
19
+ expect(session_header.stream_protocol_length).to be_a BinData::Uint24be
27
20
  end
28
21
  end
29
22
  end
@@ -110,10 +110,18 @@ RSpec.describe RubySMB::SMB1::File do
110
110
  file.read_packet
111
111
  end
112
112
 
113
- it 'sets the read_length of the packet' do
113
+ it 'sets the #max_count_of_bytes_to_return of the packet' do
114
114
  expect(file.read_packet(read_length: 55).parameter_block.max_count_of_bytes_to_return).to eq 55
115
115
  end
116
116
 
117
+ it 'sets the #min_count_of_bytes_to_return of the packet' do
118
+ expect(file.read_packet(read_length: 55).parameter_block.min_count_of_bytes_to_return).to eq 55
119
+ end
120
+
121
+ it 'sets the #remaining of the packet' do
122
+ expect(file.read_packet(read_length: 55).parameter_block.remaining).to eq 55
123
+ end
124
+
117
125
  it 'sets the offset of the packet' do
118
126
  expect(file.read_packet(offset: 55).parameter_block.offset).to eq 55
119
127
  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
@@ -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) {
@@ -124,201 +121,273 @@ RSpec.describe RubySMB::SMB1::Pipe do
124
121
  end
125
122
  end
126
123
 
127
- context 'with DCERPC' do
128
- describe '#net_share_enum_all' do
129
- let(:host) { '1.2.3.4' }
130
- let(:dcerpc_response) { RubySMB::Dcerpc::Response.new }
131
-
132
- before :example do
133
- allow(pipe).to receive(:bind)
134
- allow(pipe).to receive(:request).and_return(dcerpc_response)
135
- 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)
136
130
  end
131
+ end
137
132
 
138
- it 'calls #bind with the expected arguments' do
139
- expect(pipe).to receive(:bind).with(endpoint: RubySMB::Dcerpc::Srvsvc)
140
- pipe.net_share_enum_all(host)
141
- 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
142
144
 
143
- it 'calls #request with the expected arguments' do
144
- expect(pipe).to receive(:request).with(RubySMB::Dcerpc::Srvsvc::NET_SHARE_ENUM_ALL, host: host)
145
- 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
146
149
  end
150
+ end
147
151
 
148
- it 'parse the response with NetShareEnumAll #parse_response method' do
149
- stub = 'ABCD'
150
- dcerpc_response.alloc_hint = stub.size
151
- dcerpc_response.stub = stub
152
- expect(RubySMB::Dcerpc::Srvsvc::NetShareEnumAll).to receive(:parse_response).with(stub)
153
- 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
154
156
  end
157
+ end
155
158
 
156
- it 'returns the remote shares' do
157
- shares = [
158
- ["C$", "DISK", "Default share"],
159
- ["Shared", "DISK", ""],
160
- ["IPC$", "IPC", "Remote IPC"],
161
- ["ADMIN$", "DISK", "Remote Admin"]
162
- ]
163
- output = [
164
- {:name=>"C$", :type=>"DISK", :comment=>"Default share"},
165
- {:name=>"Shared", :type=>"DISK", :comment=>""},
166
- {:name=>"IPC$", :type=>"IPC", :comment=>"Remote IPC"},
167
- {:name=>"ADMIN$", :type=>"DISK", :comment=>"Remote Admin"},
168
- ]
169
- allow(RubySMB::Dcerpc::Srvsvc::NetShareEnumAll).to receive(:parse_response).and_return(shares)
170
- expect(pipe.net_share_enum_all(host)).to eq(output)
159
+ context 'with \'svcctl\' filename' do
160
+ it 'extends svcctl class' do
161
+ pipe = described_class.new(tree: tree, response: nt_create_andx_response, name: 'svcctl')
162
+ expect(pipe.respond_to?(:query_service_config)).to be true
171
163
  end
172
164
  end
165
+ end
173
166
 
174
- describe '#bind' do
175
- let(:options) { { endpoint: RubySMB::Dcerpc::Srvsvc } }
176
- let(:bind_packet) { RubySMB::Dcerpc::Bind.new(options) }
177
- let(:bind_ack_packet) { RubySMB::Dcerpc::BindAck.new }
167
+ describe '#dcerpc_request' do
168
+ let(:options) { { host: '1.2.3.4' } }
169
+ let(:stub_packet ) { RubySMB::Dcerpc::Winreg::OpenKeyRequest.new }
170
+ let(:dcerpc_request) { double('DCERPC Request') }
171
+ let(:request_stub) { double('Request stub') }
172
+ let(:binary_dcerpc_request) { double('Binary DCERPC Request') }
173
+ let(:trans_nmpipe_request) { double('TransactNmpipeRequest') }
174
+ let(:trans_data) { double('Trans data') }
175
+ let(:trans_nmpipe_raw_response) { double('Trans nmpipe raw response') }
176
+ let(:trans_nmpipe_response) { double('TransactNmpipeResponse') }
177
+ let(:raw_data) { double('Raw data') }
178
+ let(:dcerpc_response) { double('DCERPC Response') }
179
+ let(:result) { 'Result' }
178
180
 
179
- before :example do
180
- allow(RubySMB::Dcerpc::Bind).to receive(:new).and_return(bind_packet)
181
- allow(pipe).to receive(:write)
182
- allow(pipe).to receive(:read)
183
- bind_ack_packet.p_result_list.n_results = 1
184
- bind_ack_packet.p_result_list.p_results[0].result = RubySMB::Dcerpc::BindAck::ACCEPTANCE
185
- allow(RubySMB::Dcerpc::BindAck).to receive(:read).and_return(bind_ack_packet)
186
- end
181
+ before :example do
182
+ allow(RubySMB::Dcerpc::Request).to receive(:new).and_return(dcerpc_request)
183
+ allow(dcerpc_request).to receive_messages(
184
+ :stub => request_stub,
185
+ :to_binary_s => binary_dcerpc_request
186
+ )
187
+ allow(request_stub).to receive(:read)
188
+ allow(RubySMB::SMB1::Packet::Trans::TransactNmpipeRequest).to receive(:new).and_return(trans_nmpipe_request)
189
+ allow(tree).to receive(:set_header_fields)
190
+ allow(trans_nmpipe_request).to receive_message_chain(:data_block, :trans_data => trans_data)
191
+ allow(trans_nmpipe_request).to receive(:set_fid)
192
+ allow(trans_data).to receive(:write_data=)
193
+ allow(client).to receive(:send_recv).and_return(trans_nmpipe_raw_response)
194
+ allow(RubySMB::SMB1::Packet::Trans::TransactNmpipeResponse).to receive(:read).and_return(trans_nmpipe_response)
195
+ allow(trans_nmpipe_response).to receive_messages(
196
+ :valid? => true,
197
+ :status_code => WindowsError::NTStatus::STATUS_SUCCESS
198
+ )
199
+ allow(trans_nmpipe_response).to receive_message_chain(:data_block, :trans_data, :read_data, :to_binary_s => raw_data)
200
+ allow(RubySMB::Dcerpc::Response).to receive(:read).and_return(dcerpc_response)
201
+ allow(dcerpc_response).to receive_message_chain(:pdu_header, :ptype => RubySMB::Dcerpc::PTypes::RESPONSE)
202
+ allow(dcerpc_response).to receive(:stub).and_return(result)
203
+ end
187
204
 
188
- it 'creates a Bind packet' do
189
- expect(RubySMB::Dcerpc::Bind).to receive(:new).with(options).and_return(bind_packet)
190
- pipe.bind(options)
191
- end
205
+ it 'creates a Request packet with the expected arguments' do
206
+ pipe.dcerpc_request(stub_packet, options)
207
+ expect(options).to eq( { host: '1.2.3.4', endpoint: 'Winreg' })
208
+ expect(RubySMB::Dcerpc::Request).to have_received(:new).with({ opnum: stub_packet.opnum }, options)
209
+ end
192
210
 
193
- it 'writes to the named pipe' do
194
- expect(pipe).to receive(:write).with(data: bind_packet.to_binary_s)
195
- pipe.bind(options)
196
- end
211
+ it 'sets DCERPC request stub to the stub packet passed as argument' do
212
+ pipe.dcerpc_request(stub_packet, options)
213
+ expect(request_stub).to have_received(:read).with(stub_packet.to_binary_s)
214
+ end
215
+
216
+ it 'creates a Trans TransactNmpipeRequest packet' do
217
+ pipe.dcerpc_request(stub_packet, options)
218
+ expect(RubySMB::SMB1::Packet::Trans::TransactNmpipeRequest).to have_received(:new).with(options)
219
+ end
220
+
221
+ it 'calls Tree #set_header_fields' do
222
+ pipe.dcerpc_request(stub_packet, options)
223
+ expect(tree).to have_received(:set_header_fields).with(trans_nmpipe_request)
224
+ end
225
+
226
+ it 'calls TransactNmpipeRequest #set_fid' do
227
+ pipe.dcerpc_request(stub_packet, options)
228
+ expect(trans_nmpipe_request).to have_received(:set_fid).with(pipe.fid)
229
+ end
230
+
231
+ it 'sets the expected #write_data request property' do
232
+ pipe.dcerpc_request(stub_packet, options)
233
+ expect(trans_data).to have_received(:write_data=).with(binary_dcerpc_request)
234
+ end
197
235
 
198
- it 'reads the socket' do
199
- expect(pipe).to receive(:read)
200
- pipe.bind(options)
236
+ it 'sends the expected request' do
237
+ pipe.dcerpc_request(stub_packet, options)
238
+ expect(client).to have_received(:send_recv).with(trans_nmpipe_request)
239
+ end
240
+
241
+ it 'creates a Trans TransactNmpipeResponse packet from the response' do
242
+ pipe.dcerpc_request(stub_packet, options)
243
+ expect(RubySMB::SMB1::Packet::Trans::TransactNmpipeResponse).to have_received(:read).with(trans_nmpipe_raw_response)
244
+ end
245
+
246
+ context 'when the response is not a Trans packet' do
247
+ it 'raises an InvalidPacket exception' do
248
+ allow(trans_nmpipe_response).to receive_message_chain(:smb_header, :protocol)
249
+ allow(trans_nmpipe_response).to receive_message_chain(:smb_header, :command)
250
+ allow(trans_nmpipe_response).to receive(:valid?).and_return(false)
251
+ expect { pipe.dcerpc_request(stub_packet, options) }.to raise_error(RubySMB::Error::InvalidPacket)
201
252
  end
253
+ end
202
254
 
203
- it 'creates a BindAck packet from the response' do
204
- raw_response = RubySMB::Dcerpc::BindAck.new.to_binary_s
205
- allow(pipe).to receive(:read).and_return(raw_response)
206
- expect(RubySMB::Dcerpc::BindAck).to receive(:read).with(raw_response).and_return(bind_ack_packet)
207
- pipe.bind(options)
255
+ context 'when the response status code is not STATUS_SUCCESS or STATUS_BUFFER_OVERFLOW' do
256
+ it 'raises an UnexpectedStatusCode exception' do
257
+ allow(trans_nmpipe_response).to receive(:status_code).and_return(WindowsError::NTStatus::STATUS_INVALID_HANDLE)
258
+ expect { pipe.dcerpc_request(stub_packet, options) }.to raise_error(RubySMB::Error::UnexpectedStatusCode)
208
259
  end
260
+ end
209
261
 
210
- it 'raises the expected exception when an invalid packet is received' do
211
- allow(RubySMB::Dcerpc::BindAck).to receive(:read).and_raise(IOError)
212
- expect { pipe.bind(options) }.to raise_error(RubySMB::Dcerpc::Error::InvalidPacket)
262
+ context 'when the response status code is STATUS_SUCCESS' do
263
+ it 'does not raise any exception' do
264
+ expect { pipe.dcerpc_request(stub_packet, options) }.not_to raise_error
213
265
  end
214
266
 
215
- it 'raises the expected exception when it is not a BindAck packet' do
216
- response = RubySMB::Dcerpc::Bind.new
217
- allow(RubySMB::Dcerpc::BindAck).to receive(:read).and_return(response)
218
- expect { pipe.bind(options) }.to raise_error(RubySMB::Dcerpc::Error::BindError)
267
+ it 'creates a DCERPC Response packet from the response' do
268
+ pipe.dcerpc_request(stub_packet, options)
269
+ expect(RubySMB::Dcerpc::Response).to have_received(:read).with(raw_data)
219
270
  end
220
271
 
221
- it 'raises an exception when no result is returned' do
222
- bind_ack_packet.p_result_list.n_results = 0
223
- expect { pipe.bind(options) }.to raise_error(RubySMB::Dcerpc::Error::BindError)
272
+ context 'when an IOError occurs while parsing the DCERPC response' do
273
+ it 'raises an InvalidPacket exception' do
274
+ allow(RubySMB::Dcerpc::Response).to receive(:read).and_raise(IOError)
275
+ expect { pipe.dcerpc_request(stub_packet, options) }.to raise_error(RubySMB::Dcerpc::Error::InvalidPacket)
276
+ end
224
277
  end
225
278
 
226
- it 'raises an exception when result is not ACCEPTANCE' do
227
- bind_ack_packet.p_result_list.p_results[0].result = RubySMB::Dcerpc::BindAck::USER_REJECTION
228
- expect { pipe.bind(options) }.to raise_error(RubySMB::Dcerpc::Error::BindError)
279
+ context 'when the response is not a DCERPC Response packet' do
280
+ it 'raises an InvalidPacket exception' do
281
+ allow(dcerpc_response).to receive_message_chain(:pdu_header, :ptype => RubySMB::Dcerpc::PTypes::FAULT)
282
+ expect { pipe.dcerpc_request(stub_packet, options) }.to raise_error(RubySMB::Dcerpc::Error::InvalidPacket)
283
+ end
229
284
  end
230
285
 
231
- it 'returns the expected BindAck packet' do
232
- expect(pipe.bind(options)).to eq(bind_ack_packet)
286
+ it 'returns the expected stub data' do
287
+ expect(pipe.dcerpc_request(stub_packet, options)).to eq(result)
233
288
  end
234
289
  end
235
290
 
236
- describe '#request' do
237
- let(:options) { { host: '1.2.3.4' } }
238
- let(:opnum) { RubySMB::Dcerpc::Srvsvc::NET_SHARE_ENUM_ALL }
239
- let(:req_packet) { RubySMB::Dcerpc::Request.new({ :opnum => opnum }, options) }
240
- let(:nmpipe_packet) { RubySMB::SMB1::Packet::Trans::TransactNmpipeRequest.new(options) }
241
- let(:nmpipe_response) { RubySMB::SMB1::Packet::Trans::TransactNmpipeResponse.new }
242
- let(:res_packet) { RubySMB::Dcerpc::Response.new }
243
-
291
+ context 'when the response status code is STATUS_BUFFER_OVERFLOW' do
292
+ let(:data_count) { 100 }
293
+ let(:added_raw_data) { double('Added raw data') }
244
294
  before :example do
245
- allow(RubySMB::Dcerpc::Request).to receive(:new).and_return(req_packet)
246
- allow(RubySMB::SMB1::Packet::Trans::TransactNmpipeRequest).to receive(:new).and_return(nmpipe_packet)
247
- allow(client).to receive(:send_recv)
248
- allow(RubySMB::SMB1::Packet::Trans::TransactNmpipeResponse).to receive(:read).and_return(nmpipe_response)
249
- allow(RubySMB::Dcerpc::Response).to receive(:read).and_return(res_packet)
295
+ allow(trans_nmpipe_response).to receive(:status_code).and_return(WindowsError::NTStatus::STATUS_BUFFER_OVERFLOW)
296
+ allow(trans_nmpipe_response).to receive_message_chain(:parameter_block, :data_count => data_count)
297
+ allow(pipe).to receive(:read).and_return(added_raw_data)
298
+ allow(raw_data).to receive(:<<)
299
+ allow(dcerpc_response).to receive_message_chain(:pdu_header, :pfc_flags, :first_frag => 1)
300
+ allow(dcerpc_response).to receive_message_chain(:pdu_header, :pfc_flags, :last_frag => 1)
250
301
  end
251
302
 
252
- it 'creates a Request packet' do
253
- expect(RubySMB::Dcerpc::Request).to receive(:new).and_return(req_packet)
254
- pipe.request(opnum, options)
303
+ it 'does not raise any exception' do
304
+ expect { pipe.dcerpc_request(stub_packet, options) }.not_to raise_error
255
305
  end
256
306
 
257
- it 'creates a Trans TransactNmpipeRequest packet' do
258
- expect(RubySMB::SMB1::Packet::Trans::TransactNmpipeRequest).to receive(:new).and_return(nmpipe_packet)
259
- pipe.request(opnum, options)
307
+ it 'reads the expected number of bytes and concatenate it the first response raw data' do
308
+ pipe.dcerpc_request(stub_packet, options)
309
+ expect(pipe).to have_received(:read).with(bytes: tree.client.max_buffer_size - data_count)
310
+ expect(raw_data).to have_received(:<<).with(added_raw_data)
260
311
  end
261
312
 
262
- it 'calls Tree #set_header_fields' do
263
- expect(tree).to receive(:set_header_fields).with(nmpipe_packet)
264
- pipe.request(opnum, options)
313
+ it 'creates a DCERPC Response packet from the updated raw data' do
314
+ pipe.dcerpc_request(stub_packet, options)
315
+ expect(RubySMB::Dcerpc::Response).to have_received(:read).with(raw_data)
265
316
  end
266
317
 
267
- it 'calls TransactNmpipeRequest #set_fid' do
268
- expect(nmpipe_packet).to receive(:set_fid).with(pipe.fid)
269
- pipe.request(opnum, options)
318
+ context 'when an IOError occurs while parsing the DCERPC response' do
319
+ it 'raises an InvalidPacket exception' do
320
+ allow(RubySMB::Dcerpc::Response).to receive(:read).and_raise(IOError)
321
+ expect { pipe.dcerpc_request(stub_packet, options) }.to raise_error(RubySMB::Dcerpc::Error::InvalidPacket)
322
+ end
270
323
  end
271
324
 
272
- it 'sets the expected data on the request' do
273
- expect(client).to receive(:send_recv) do
274
- expect(nmpipe_packet.data_block.trans_data.write_data).to eq(req_packet.to_binary_s)
325
+ context 'when the response is not a DCERPC Response packet' do
326
+ it 'raises an InvalidPacket exception' do
327
+ allow(dcerpc_response).to receive_message_chain(:pdu_header, :ptype => RubySMB::Dcerpc::PTypes::FAULT)
328
+ expect { pipe.dcerpc_request(stub_packet, options) }.to raise_error(RubySMB::Dcerpc::Error::InvalidPacket)
275
329
  end
276
- pipe.request(opnum, options)
277
330
  end
278
331
 
279
- it 'sends the expected request' do
280
- expect(client).to receive(:send_recv).with(nmpipe_packet)
281
- pipe.request(opnum, options)
332
+ context 'when the response is not the first fragment' do
333
+ it 'raises an InvalidPacket exception' do
334
+ allow(dcerpc_response).to receive_message_chain(:pdu_header, :pfc_flags, :first_frag => 0)
335
+ expect { pipe.dcerpc_request(stub_packet, options) }.to raise_error(RubySMB::Dcerpc::Error::InvalidPacket)
336
+ end
282
337
  end
283
338
 
284
- it 'creates a Trans TransactNmpipeResponse packet from the response' do
285
- raw_response = double('Raw response')
286
- allow(client).to receive(:send_recv).and_return(raw_response)
287
- expect(RubySMB::SMB1::Packet::Trans::TransactNmpipeResponse).to receive(:read).with(raw_response).and_return(nmpipe_response)
288
- pipe.request(opnum, options)
289
- end
339
+ context 'when the response is the last fragment' do
340
+ it 'only reads the pipe once' do
341
+ pipe.dcerpc_request(stub_packet, options)
342
+ expect(RubySMB::Dcerpc::Response).to have_received(:read).once
343
+ end
290
344
 
291
- it 'raises the expected exception when it is not a Trans packet' do
292
- nmpipe_response.smb_header.command = RubySMB::SMB1::Commands::SMB_COM_TRANSACTION2
293
- expect { pipe.request(opnum, options) }.to raise_error(RubySMB::Error::InvalidPacket)
345
+ it 'returns the expected stub data' do
346
+ expect(pipe.dcerpc_request(stub_packet, options)).to eq(result)
347
+ end
294
348
  end
295
349
 
296
- it 'raises the expected exception when the status code is not STATUS_SUCCESS' do
297
- nmpipe_response.smb_header.nt_status = WindowsError::NTStatus::STATUS_INVALID_HANDLE.value
298
- expect { pipe.request(opnum, options) }.to raise_error(RubySMB::Error::UnexpectedStatusCode)
299
- end
350
+ context 'when the response is not the last fragment' do
351
+ let(:raw_data2) { double('Raw data #2') }
352
+ let(:dcerpc_response2) { double('DCERPC Response #2') }
353
+ let(:result2) { 'Result #2' }
354
+ before :example do
355
+ allow(dcerpc_response).to receive_message_chain(:pdu_header, :pfc_flags, :last_frag => 0)
356
+ allow(pipe).to receive(:read).with(bytes: tree.client.max_buffer_size).and_return(raw_data2)
357
+ allow(RubySMB::Dcerpc::Response).to receive(:read).with(raw_data2).and_return(dcerpc_response2)
358
+ allow(dcerpc_response2).to receive_message_chain(:pdu_header, :ptype => RubySMB::Dcerpc::PTypes::RESPONSE)
359
+ allow(dcerpc_response2).to receive_message_chain(:pdu_header, :pfc_flags, :last_frag => 1)
360
+ allow(dcerpc_response2).to receive(:stub).and_return(result2)
361
+ end
300
362
 
301
- it 'creates a DCERPC Response packet from the response' do
302
- nmpipe_response.data_block.trans_data.read_data = "test"
303
- expect(RubySMB::Dcerpc::Response).to receive(:read).with(nmpipe_response.data_block.trans_data.read_data)
304
- pipe.request(opnum, options)
305
- end
363
+ it 'reads the expected number of bytes' do
364
+ pipe.dcerpc_request(stub_packet, options)
365
+ expect(pipe).to have_received(:read).with(bytes: tree.client.max_buffer_size)
366
+ end
306
367
 
307
- it 'raises the expected exception when an invalid packet is received' do
308
- allow(RubySMB::Dcerpc::Response).to receive(:read).and_raise(IOError)
309
- expect { pipe.request(opnum, options) }.to raise_error(RubySMB::Dcerpc::Error::InvalidPacket)
310
- end
368
+ it 'creates a DCERPC Response packet from the new raw data' do
369
+ pipe.dcerpc_request(stub_packet, options)
370
+ expect(RubySMB::Dcerpc::Response).to have_received(:read).with(raw_data2)
371
+ end
311
372
 
312
- it 'raises the expected exception when it is not a Response packet' do
313
- response = RubySMB::Dcerpc::Request.new
314
- allow(RubySMB::Dcerpc::Response).to receive(:read).and_return(response)
315
- expect { pipe.request(opnum, options) }.to raise_error(RubySMB::Dcerpc::Error::InvalidPacket)
316
- end
373
+ context 'when an IOError occurs while parsing the new DCERPC response' do
374
+ it 'raises an InvalidPacket exception' do
375
+ allow(RubySMB::Dcerpc::Response).to receive(:read).with(raw_data2).and_raise(IOError)
376
+ expect { pipe.dcerpc_request(stub_packet, options) }.to raise_error(RubySMB::Dcerpc::Error::InvalidPacket)
377
+ end
378
+ end
317
379
 
318
- it 'returns the expected DCERPC Response' do
319
- expect(pipe.request(opnum, options)).to eq(res_packet)
380
+ context 'when the new response is not a DCERPC Response packet' do
381
+ it 'raises an InvalidPacket exception' do
382
+ allow(dcerpc_response2).to receive_message_chain(:pdu_header, :ptype => RubySMB::Dcerpc::PTypes::FAULT)
383
+ expect { pipe.dcerpc_request(stub_packet, options) }.to raise_error(RubySMB::Dcerpc::Error::InvalidPacket)
384
+ end
385
+ end
386
+
387
+ it 'returns the expected stub data' do
388
+ expect(pipe.dcerpc_request(stub_packet, options)).to eq(result)
389
+ end
320
390
  end
321
391
  end
322
392
  end
323
-
324
393
  end