ruby_smb 1.0.3 → 2.0.1

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 (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 +233 -22
  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 +172 -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 +4 -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 +50 -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 +52 -5
  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 +1612 -108
  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 +2 -2
  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,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
 
@@ -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
@@ -63,8 +63,13 @@ RSpec.describe RubySMB::GenericPacket do
63
63
  expect(RubySMB::SMB1::Packet::NegotiateResponse.read(smb1_error_packet.to_binary_s)).to be_a RubySMB::SMB1::Packet::EmptyPacket
64
64
  end
65
65
 
66
- it 'reraises the exception if it is not a valid error packet either' do
67
- expect{RubySMB::SMB1::Packet::NegotiateResponse.read('a')}.to raise_error(EOFError)
66
+ it 'raises an InvaliPacket exception if it is not a valid error packet either' do
67
+ expect{RubySMB::SMB1::Packet::NegotiateResponse.read('a')}.to raise_error(RubySMB::Error::InvalidPacket)
68
+ end
69
+
70
+ it 'sets the EmptyPacket#original_command attribute to the original COMMAND' do
71
+ packet = RubySMB::SMB1::Packet::NegotiateResponse.read(smb1_error_packet.to_binary_s)
72
+ expect(packet.original_command).to eq RubySMB::SMB1::Packet::NegotiateResponse::COMMAND
68
73
  end
69
74
  end
70
75
 
@@ -75,8 +80,51 @@ RSpec.describe RubySMB::GenericPacket do
75
80
  expect(RubySMB::SMB2::Packet::NegotiateResponse.read(smb2_error_packet.to_binary_s)).to be_a RubySMB::SMB2::Packet::ErrorPacket
76
81
  end
77
82
 
78
- it 'reraises the exception if it is not a valid error packet either' do
79
- expect{RubySMB::SMB2::Packet::NegotiateResponse.read('a')}.to raise_error(EOFError)
83
+ it 'raises an InvaliPacket exception if it is not a valid error packet either' do
84
+ expect{RubySMB::SMB2::Packet::NegotiateResponse.read('a')}.to raise_error(RubySMB::Error::InvalidPacket)
85
+ end
86
+
87
+ it 'sets the ErrorPacket#original_command attribute to the original COMMAND' do
88
+ packet = RubySMB::SMB2::Packet::NegotiateResponse.read(smb2_error_packet.to_binary_s)
89
+ expect(packet.original_command).to eq RubySMB::SMB2::Packet::NegotiateResponse::COMMAND
90
+ end
91
+ end
92
+ end
93
+
94
+ describe '#valid?' do
95
+ context 'when reading an SMB1 packet' do
96
+ let(:packet) { RubySMB::SMB1::Packet::NegotiateResponse.new }
97
+
98
+ it 'returns true if the packet protocol ID and header command are valid' do
99
+ expect(packet).to be_valid
100
+ end
101
+
102
+ it 'returns false if the packet protocol ID is wrong' do
103
+ packet.smb_header.protocol = RubySMB::SMB2::SMB2_PROTOCOL_ID
104
+ expect(packet).to_not be_valid
105
+ end
106
+
107
+ it 'returns false if the packet header command is wrong' do
108
+ packet.smb_header.command = RubySMB::SMB1::Commands::SMB_COM_TREE_CONNECT
109
+ expect(packet).to_not be_valid
110
+ end
111
+ end
112
+
113
+ context 'when reading an SMB2 packet' do
114
+ let(:packet) { RubySMB::SMB2::Packet::NegotiateResponse.new }
115
+
116
+ it 'returns true if the packet protocol ID and header command are valid' do
117
+ expect(packet).to be_valid
118
+ end
119
+
120
+ it 'returns false if the packet protocol ID is wrong' do
121
+ packet.smb2_header.protocol = RubySMB::SMB1::SMB_PROTOCOL_ID
122
+ expect(packet).to_not be_valid
123
+ end
124
+
125
+ it 'returns false if the packet header command is wrong' do
126
+ packet.smb2_header.command = RubySMB::SMB2::Commands::TREE_CONNECT
127
+ expect(packet).to_not be_valid
80
128
  end
81
129
  end
82
130
  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
@@ -163,7 +171,7 @@ RSpec.describe RubySMB::SMB1::File do
163
171
  end
164
172
 
165
173
  context 'when sending the request packet and gets a response back' do
166
- context 'when the response is not a ReadAndxResponse packet' do
174
+ context 'when the response is not valid' do
167
175
  it 'raise an InvalidPacket exception' do
168
176
  read_andx_response.smb_header.command = RubySMB::SMB1::Commands::SMB_COM_ECHO
169
177
  expect { file.read }.to raise_error(RubySMB::Error::InvalidPacket)
@@ -181,6 +189,7 @@ RSpec.describe RubySMB::SMB1::File do
181
189
  let(:empty_packet) do
182
190
  empty_packet = RubySMB::SMB1::Packet::EmptyPacket.new
183
191
  empty_packet.smb_header.command = RubySMB::SMB1::Commands::SMB_COM_READ_ANDX
192
+ empty_packet.original_command = RubySMB::SMB1::Commands::SMB_COM_READ_ANDX
184
193
  empty_packet
185
194
  end
186
195
 
@@ -345,6 +354,11 @@ RSpec.describe RubySMB::SMB1::File do
345
354
  set_info_res.smb_header.nt_status = status.value
346
355
  expect(file.delete).to eq status
347
356
  end
357
+
358
+ it 'raises an InvalidPacket exception if the response is not valid' do
359
+ allow(set_info_res).to receive(:valid?).and_return(false)
360
+ expect { file.delete }.to raise_error(RubySMB::Error::InvalidPacket)
361
+ end
348
362
  end
349
363
 
350
364
  describe '#delete_packet' do
@@ -415,6 +429,11 @@ RSpec.describe RubySMB::SMB1::File do
415
429
  set_info_res.smb_header.nt_status = status.value
416
430
  expect(file.rename(filename)).to eq status
417
431
  end
432
+
433
+ it 'raises an InvalidPacket exception if the response is not valid' do
434
+ allow(set_info_res).to receive(:valid?).and_return(false)
435
+ expect { file.rename(filename) }.to raise_error(RubySMB::Error::InvalidPacket)
436
+ end
418
437
  end
419
438
 
420
439
  describe '#rename_packet' do
@@ -465,5 +484,175 @@ RSpec.describe RubySMB::SMB1::File do
465
484
  expect(parameter_block.max_data_count).to eq 16_384
466
485
  end
467
486
  end
487
+
488
+ describe '#close' do
489
+ let(:request) { double('CloseRequest') }
490
+ let(:response) { double('CloseResponse') }
491
+ let(:raw_response) { double('Raw response') }
492
+
493
+ before :example do
494
+ allow(RubySMB::SMB1::Packet::CloseRequest).to receive(:new).and_return(request)
495
+ allow(file).to receive(:set_header_fields).and_return(request)
496
+ allow(client).to receive(:send_recv).and_return(raw_response)
497
+ allow(RubySMB::SMB1::Packet::CloseResponse).to receive(:read).and_return(response)
498
+ allow(response).to receive(:valid?).and_return(true)
499
+ allow(response).to receive(:status_code).and_return(WindowsError::NTStatus::STATUS_SUCCESS)
500
+ end
501
+
502
+ it 'creates a new SMB1 CloseRequest packet' do
503
+ expect(RubySMB::SMB1::Packet::CloseRequest).to receive(:new)
504
+ file.close
505
+ end
506
+
507
+ it 'calls Tree #set_header_fields to set SetFileInformationRequest headers' do
508
+ expect(file).to receive(:set_header_fields).with(request)
509
+ file.close
510
+ end
511
+
512
+ it 'calls Client #send_recv with the expected request' do
513
+ expect(client).to receive(:send_recv).with(request)
514
+ file.close
515
+ end
516
+
517
+ it 'parses the response as a SMB1 CloseResponse packet' do
518
+ expect(RubySMB::SMB1::Packet::CloseResponse).to receive(:read).with(raw_response)
519
+ file.close
520
+ end
521
+
522
+ it 'raises an InvalidPacket exception if the response is not valid' do
523
+ allow(response).to receive(:valid?).and_return(false)
524
+ smb_header = double('SMB Header')
525
+ allow(response).to receive(:smb_header).and_return(smb_header)
526
+ allow(smb_header).to receive_messages(:protocol => nil, :command => nil)
527
+ expect { file.close }.to raise_error(RubySMB::Error::InvalidPacket)
528
+ end
529
+
530
+ it 'raises an UnexpectedStatusCode exception if the response status code is not STATUS_SUCCESS' do
531
+ allow(response).to receive(:status_code).and_return(WindowsError::NTStatus::STATUS_OBJECT_NAME_NOT_FOUND)
532
+ expect { file.close }.to raise_error(RubySMB::Error::UnexpectedStatusCode)
533
+ end
534
+
535
+ it 'returns the response status code' do
536
+ expect(file.close).to eq WindowsError::NTStatus::STATUS_SUCCESS
537
+ end
538
+ end
539
+
540
+ describe '#send_recv_read' do
541
+ let(:read_data) { 'read data' }
542
+ let(:raw_response) { double('fake raw response data') }
543
+ let(:read_andx_response) {
544
+ res = RubySMB::SMB1::Packet::ReadAndxResponse.new
545
+ res.data_block.data = read_data
546
+ res
547
+ }
548
+
549
+ before :example do
550
+ allow(client).to receive(:send_recv).and_return(raw_response)
551
+ allow(RubySMB::SMB1::Packet::ReadAndxResponse).to receive(:read).with(raw_response).and_return(read_andx_response)
552
+ end
553
+
554
+ context 'when the number of bytes to read is not provided' do
555
+ it 'reads 0 bytes by default' do
556
+ expect(file).to receive(:read_packet).with(read_length: 0, offset: 0).once.and_call_original
557
+ file.send_recv_read
558
+ end
559
+ end
560
+
561
+ it 'only reads the number of bytes provided as argument' do
562
+ bytes = 5
563
+ expect(file).to receive(:read_packet).with(read_length: bytes, offset: 0).once.and_call_original
564
+ file.send_recv_read(read_length: bytes)
565
+ end
566
+
567
+ it 'reads from the offset provided as argument' do
568
+ offset = 3
569
+ expect(file).to receive(:read_packet).with(read_length: 0, offset: offset).once.and_call_original
570
+ file.send_recv_read(offset: offset)
571
+ end
572
+
573
+ it 'calls Client #send_recv with the expected request' do
574
+ request = double('Request')
575
+ allow(file).to receive(:read_packet).and_return(request)
576
+ expect(client).to receive(:send_recv).with(request)
577
+ file.send_recv_read
578
+ end
579
+
580
+ it 'parses the response as a SMB1 ReadAndxResponse packet' do
581
+ expect(RubySMB::SMB1::Packet::ReadAndxResponse).to receive(:read).with(raw_response)
582
+ file.send_recv_read
583
+ end
584
+
585
+ it 'raises an InvalidPacket exception if the response is not valid' do
586
+ allow(read_andx_response).to receive(:valid?).and_return(false)
587
+ expect { file.send_recv_read }.to raise_error(RubySMB::Error::InvalidPacket)
588
+ end
589
+
590
+ it 'raises an UnexpectedStatusCode exception if the response status code is not STATUS_SUCCESS' do
591
+ allow(read_andx_response).to receive(:status_code).and_return(WindowsError::NTStatus::STATUS_OBJECT_NAME_NOT_FOUND)
592
+ expect { file.send_recv_read }.to raise_error(RubySMB::Error::UnexpectedStatusCode)
593
+ end
594
+
595
+ it 'returns the expected string' do
596
+ expect(file.send_recv_read).to eq(read_data)
597
+ end
598
+ end
599
+
600
+ describe '#send_recv_write' do
601
+ let(:write_data) { 'write data' }
602
+ let(:request) { double('Request') }
603
+ let(:raw_response) { double('fake raw response data') }
604
+ let(:write_andx_response) {
605
+ res = RubySMB::SMB1::Packet::WriteAndxResponse.new
606
+ res.parameter_block.count_low = write_data.size
607
+ res
608
+ }
609
+
610
+ before :example do
611
+ allow(file).to receive(:write_packet).and_return(request)
612
+ allow(request).to receive(:set_64_bit_offset)
613
+ allow(client).to receive(:send_recv).and_return(raw_response)
614
+ allow(RubySMB::SMB1::Packet::WriteAndxResponse).to receive(:read).with(raw_response).and_return(write_andx_response)
615
+ end
616
+
617
+ it 'reads 0 bytes from offset 0 by default' do
618
+ expect(file).to receive(:write_packet).with(data: '', offset: 0).once.and_call_original
619
+ file.send_recv_write
620
+ end
621
+
622
+ it 'writes the data provided as argument' do
623
+ expect(file).to receive(:write_packet).with(data: write_data, offset: 0).once.and_call_original
624
+ file.send_recv_write(data: write_data)
625
+ end
626
+
627
+ it 'reads from the offset provided as argument' do
628
+ offset = 3
629
+ expect(file).to receive(:write_packet).with(data: '', offset: offset).once.and_call_original
630
+ file.send_recv_write(offset: offset)
631
+ end
632
+
633
+ it 'sets the 64 bit offset to true' do
634
+ expect(request).to receive(:set_64_bit_offset).with(true)
635
+ file.send_recv_write
636
+ end
637
+
638
+ it 'calls Client #send_recv with the expected request' do
639
+ expect(client).to receive(:send_recv).with(request)
640
+ file.send_recv_write
641
+ end
642
+
643
+ it 'parses the response as a SMB1 WriteAndxResponse packet' do
644
+ expect(RubySMB::SMB1::Packet::WriteAndxResponse).to receive(:read).with(raw_response)
645
+ file.send_recv_write
646
+ end
647
+
648
+ it 'raises an InvalidPacket exception if the response is not valid' do
649
+ allow(write_andx_response).to receive(:valid?).and_return(false)
650
+ expect { file.send_recv_write }.to raise_error(RubySMB::Error::InvalidPacket)
651
+ end
652
+
653
+ it 'returns the expected response #count_low value' do
654
+ expect(file.send_recv_write).to eq(write_data.size)
655
+ end
656
+ end
468
657
  end
469
658