ruby_smb 1.0.2 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (200) hide show
  1. checksums.yaml +5 -5
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/.travis.yml +3 -2
  5. data/Gemfile +6 -2
  6. data/README.md +35 -47
  7. data/examples/enum_registry_key.rb +28 -0
  8. data/examples/enum_registry_values.rb +30 -0
  9. data/examples/negotiate.rb +51 -8
  10. data/examples/pipes.rb +2 -1
  11. data/examples/read_file_encryption.rb +56 -0
  12. data/examples/read_registry_key_value.rb +32 -0
  13. data/lib/ruby_smb.rb +4 -1
  14. data/lib/ruby_smb/client.rb +200 -20
  15. data/lib/ruby_smb/client/authentication.rb +70 -33
  16. data/lib/ruby_smb/client/echo.rb +20 -2
  17. data/lib/ruby_smb/client/encryption.rb +62 -0
  18. data/lib/ruby_smb/client/negotiation.rb +160 -24
  19. data/lib/ruby_smb/client/signing.rb +19 -0
  20. data/lib/ruby_smb/client/tree_connect.rb +24 -18
  21. data/lib/ruby_smb/client/utils.rb +8 -7
  22. data/lib/ruby_smb/client/winreg.rb +46 -0
  23. data/lib/ruby_smb/crypto.rb +30 -0
  24. data/lib/ruby_smb/dcerpc.rb +38 -0
  25. data/lib/ruby_smb/dcerpc/bind.rb +2 -2
  26. data/lib/ruby_smb/dcerpc/bind_ack.rb +2 -2
  27. data/lib/ruby_smb/dcerpc/error.rb +3 -0
  28. data/lib/ruby_smb/dcerpc/ndr.rb +95 -16
  29. data/lib/ruby_smb/dcerpc/pdu_header.rb +1 -1
  30. data/lib/ruby_smb/dcerpc/request.rb +28 -9
  31. data/lib/ruby_smb/dcerpc/rrp_unicode_string.rb +35 -0
  32. data/lib/ruby_smb/dcerpc/srvsvc.rb +10 -0
  33. data/lib/ruby_smb/dcerpc/srvsvc/net_share_enum_all.rb +9 -0
  34. data/lib/ruby_smb/dcerpc/winreg.rb +340 -0
  35. data/lib/ruby_smb/dcerpc/winreg/close_key_request.rb +24 -0
  36. data/lib/ruby_smb/dcerpc/winreg/close_key_response.rb +27 -0
  37. data/lib/ruby_smb/dcerpc/winreg/enum_key_request.rb +45 -0
  38. data/lib/ruby_smb/dcerpc/winreg/enum_key_response.rb +42 -0
  39. data/lib/ruby_smb/dcerpc/winreg/enum_value_request.rb +39 -0
  40. data/lib/ruby_smb/dcerpc/winreg/enum_value_response.rb +36 -0
  41. data/lib/ruby_smb/dcerpc/winreg/open_key_request.rb +34 -0
  42. data/lib/ruby_smb/dcerpc/winreg/open_key_response.rb +25 -0
  43. data/lib/ruby_smb/dcerpc/winreg/open_root_key_request.rb +43 -0
  44. data/lib/ruby_smb/dcerpc/winreg/open_root_key_response.rb +35 -0
  45. data/lib/ruby_smb/dcerpc/winreg/query_info_key_request.rb +27 -0
  46. data/lib/ruby_smb/dcerpc/winreg/query_info_key_response.rb +40 -0
  47. data/lib/ruby_smb/dcerpc/winreg/query_value_request.rb +39 -0
  48. data/lib/ruby_smb/dcerpc/winreg/query_value_response.rb +57 -0
  49. data/lib/ruby_smb/dcerpc/winreg/regsam.rb +40 -0
  50. data/lib/ruby_smb/dispatcher/socket.rb +5 -3
  51. data/lib/ruby_smb/error.rb +68 -2
  52. data/lib/ruby_smb/generic_packet.rb +33 -4
  53. data/lib/ruby_smb/smb1/commands.rb +1 -1
  54. data/lib/ruby_smb/smb1/file.rb +66 -15
  55. data/lib/ruby_smb/smb1/packet/close_request.rb +2 -5
  56. data/lib/ruby_smb/smb1/packet/close_response.rb +2 -1
  57. data/lib/ruby_smb/smb1/packet/echo_request.rb +2 -4
  58. data/lib/ruby_smb/smb1/packet/echo_response.rb +2 -1
  59. data/lib/ruby_smb/smb1/packet/empty_packet.rb +10 -1
  60. data/lib/ruby_smb/smb1/packet/logoff_request.rb +2 -4
  61. data/lib/ruby_smb/smb1/packet/logoff_response.rb +2 -1
  62. data/lib/ruby_smb/smb1/packet/negotiate_request.rb +2 -5
  63. data/lib/ruby_smb/smb1/packet/negotiate_response.rb +3 -7
  64. data/lib/ruby_smb/smb1/packet/negotiate_response_extended.rb +4 -4
  65. data/lib/ruby_smb/smb1/packet/nt_create_andx_request.rb +2 -4
  66. data/lib/ruby_smb/smb1/packet/nt_create_andx_response.rb +2 -1
  67. data/lib/ruby_smb/smb1/packet/nt_trans/create_request.rb +2 -1
  68. data/lib/ruby_smb/smb1/packet/nt_trans/create_response.rb +2 -1
  69. data/lib/ruby_smb/smb1/packet/nt_trans/request.rb +2 -4
  70. data/lib/ruby_smb/smb1/packet/nt_trans/response.rb +2 -1
  71. data/lib/ruby_smb/smb1/packet/read_andx_request.rb +2 -5
  72. data/lib/ruby_smb/smb1/packet/read_andx_response.rb +2 -1
  73. data/lib/ruby_smb/smb1/packet/session_setup_legacy_request.rb +2 -1
  74. data/lib/ruby_smb/smb1/packet/session_setup_legacy_response.rb +3 -2
  75. data/lib/ruby_smb/smb1/packet/session_setup_request.rb +2 -5
  76. data/lib/ruby_smb/smb1/packet/session_setup_response.rb +3 -2
  77. data/lib/ruby_smb/smb1/packet/trans/peek_nmpipe_request.rb +0 -1
  78. data/lib/ruby_smb/smb1/packet/trans/peek_nmpipe_response.rb +3 -2
  79. data/lib/ruby_smb/smb1/packet/trans/request.rb +2 -5
  80. data/lib/ruby_smb/smb1/packet/trans/response.rb +2 -1
  81. data/lib/ruby_smb/smb1/packet/trans/transact_nmpipe_request.rb +1 -1
  82. data/lib/ruby_smb/smb1/packet/trans/transact_nmpipe_response.rb +1 -1
  83. data/lib/ruby_smb/smb1/packet/trans2/find_first2_request.rb +2 -1
  84. data/lib/ruby_smb/smb1/packet/trans2/find_first2_response.rb +8 -2
  85. data/lib/ruby_smb/smb1/packet/trans2/find_next2_request.rb +2 -1
  86. data/lib/ruby_smb/smb1/packet/trans2/find_next2_response.rb +8 -2
  87. data/lib/ruby_smb/smb1/packet/trans2/open2_request.rb +2 -1
  88. data/lib/ruby_smb/smb1/packet/trans2/open2_response.rb +2 -1
  89. data/lib/ruby_smb/smb1/packet/trans2/request.rb +2 -4
  90. data/lib/ruby_smb/smb1/packet/trans2/request_secondary.rb +2 -4
  91. data/lib/ruby_smb/smb1/packet/trans2/response.rb +2 -1
  92. data/lib/ruby_smb/smb1/packet/trans2/set_file_information_request.rb +2 -1
  93. data/lib/ruby_smb/smb1/packet/trans2/set_file_information_response.rb +2 -1
  94. data/lib/ruby_smb/smb1/packet/tree_connect_request.rb +2 -4
  95. data/lib/ruby_smb/smb1/packet/tree_connect_response.rb +13 -3
  96. data/lib/ruby_smb/smb1/packet/tree_disconnect_request.rb +2 -4
  97. data/lib/ruby_smb/smb1/packet/tree_disconnect_response.rb +2 -1
  98. data/lib/ruby_smb/smb1/packet/write_andx_request.rb +3 -6
  99. data/lib/ruby_smb/smb1/packet/write_andx_response.rb +2 -1
  100. data/lib/ruby_smb/smb1/pipe.rb +87 -6
  101. data/lib/ruby_smb/smb1/tree.rb +41 -3
  102. data/lib/ruby_smb/smb2/bit_field/session_flags.rb +2 -1
  103. data/lib/ruby_smb/smb2/bit_field/share_flags.rb +6 -4
  104. data/lib/ruby_smb/smb2/file.rb +103 -25
  105. data/lib/ruby_smb/smb2/negotiate_context.rb +108 -0
  106. data/lib/ruby_smb/smb2/packet.rb +2 -0
  107. data/lib/ruby_smb/smb2/packet/close_request.rb +2 -4
  108. data/lib/ruby_smb/smb2/packet/close_response.rb +2 -1
  109. data/lib/ruby_smb/smb2/packet/compression_transform_header.rb +41 -0
  110. data/lib/ruby_smb/smb2/packet/create_request.rb +2 -4
  111. data/lib/ruby_smb/smb2/packet/create_response.rb +2 -1
  112. data/lib/ruby_smb/smb2/packet/echo_request.rb +2 -4
  113. data/lib/ruby_smb/smb2/packet/echo_response.rb +2 -1
  114. data/lib/ruby_smb/smb2/packet/error_packet.rb +15 -3
  115. data/lib/ruby_smb/smb2/packet/ioctl_request.rb +2 -5
  116. data/lib/ruby_smb/smb2/packet/ioctl_response.rb +2 -1
  117. data/lib/ruby_smb/smb2/packet/logoff_request.rb +2 -4
  118. data/lib/ruby_smb/smb2/packet/logoff_response.rb +2 -1
  119. data/lib/ruby_smb/smb2/packet/negotiate_request.rb +51 -17
  120. data/lib/ruby_smb/smb2/packet/negotiate_response.rb +51 -4
  121. data/lib/ruby_smb/smb2/packet/query_directory_request.rb +2 -4
  122. data/lib/ruby_smb/smb2/packet/query_directory_response.rb +8 -2
  123. data/lib/ruby_smb/smb2/packet/read_request.rb +2 -4
  124. data/lib/ruby_smb/smb2/packet/read_response.rb +2 -1
  125. data/lib/ruby_smb/smb2/packet/session_setup_request.rb +2 -5
  126. data/lib/ruby_smb/smb2/packet/session_setup_response.rb +2 -1
  127. data/lib/ruby_smb/smb2/packet/set_info_request.rb +2 -4
  128. data/lib/ruby_smb/smb2/packet/set_info_response.rb +2 -1
  129. data/lib/ruby_smb/smb2/packet/transform_header.rb +84 -0
  130. data/lib/ruby_smb/smb2/packet/tree_connect_request.rb +93 -10
  131. data/lib/ruby_smb/smb2/packet/tree_connect_response.rb +10 -22
  132. data/lib/ruby_smb/smb2/packet/tree_disconnect_request.rb +2 -4
  133. data/lib/ruby_smb/smb2/packet/tree_disconnect_response.rb +2 -1
  134. data/lib/ruby_smb/smb2/packet/write_request.rb +2 -4
  135. data/lib/ruby_smb/smb2/packet/write_response.rb +2 -1
  136. data/lib/ruby_smb/smb2/pipe.rb +86 -12
  137. data/lib/ruby_smb/smb2/smb2_header.rb +1 -1
  138. data/lib/ruby_smb/smb2/tree.rb +65 -21
  139. data/lib/ruby_smb/version.rb +1 -1
  140. data/ruby_smb.gemspec +5 -3
  141. data/spec/lib/ruby_smb/client_spec.rb +1563 -104
  142. data/spec/lib/ruby_smb/crypto_spec.rb +25 -0
  143. data/spec/lib/ruby_smb/dcerpc/bind_ack_spec.rb +2 -2
  144. data/spec/lib/ruby_smb/dcerpc/bind_spec.rb +2 -2
  145. data/spec/lib/ruby_smb/dcerpc/ndr_spec.rb +410 -0
  146. data/spec/lib/ruby_smb/dcerpc/request_spec.rb +50 -7
  147. data/spec/lib/ruby_smb/dcerpc/rrp_unicode_string_spec.rb +98 -0
  148. data/spec/lib/ruby_smb/dcerpc/srvsvc/net_share_enum_all_spec.rb +13 -0
  149. data/spec/lib/ruby_smb/dcerpc/srvsvc_spec.rb +60 -0
  150. data/spec/lib/ruby_smb/dcerpc/winreg/close_key_request_spec.rb +28 -0
  151. data/spec/lib/ruby_smb/dcerpc/winreg/close_key_response_spec.rb +36 -0
  152. data/spec/lib/ruby_smb/dcerpc/winreg/enum_key_request_spec.rb +108 -0
  153. data/spec/lib/ruby_smb/dcerpc/winreg/enum_key_response_spec.rb +97 -0
  154. data/spec/lib/ruby_smb/dcerpc/winreg/enum_value_request_spec.rb +94 -0
  155. data/spec/lib/ruby_smb/dcerpc/winreg/enum_value_response_spec.rb +82 -0
  156. data/spec/lib/ruby_smb/dcerpc/winreg/open_key_request_spec.rb +74 -0
  157. data/spec/lib/ruby_smb/dcerpc/winreg/open_key_response_spec.rb +35 -0
  158. data/spec/lib/ruby_smb/dcerpc/winreg/open_root_key_request_spec.rb +90 -0
  159. data/spec/lib/ruby_smb/dcerpc/winreg/open_root_key_response_spec.rb +38 -0
  160. data/spec/lib/ruby_smb/dcerpc/winreg/query_info_key_request_spec.rb +39 -0
  161. data/spec/lib/ruby_smb/dcerpc/winreg/query_info_key_response_spec.rb +113 -0
  162. data/spec/lib/ruby_smb/dcerpc/winreg/query_value_request_spec.rb +88 -0
  163. data/spec/lib/ruby_smb/dcerpc/winreg/query_value_response_spec.rb +150 -0
  164. data/spec/lib/ruby_smb/dcerpc/winreg/regsam_spec.rb +32 -0
  165. data/spec/lib/ruby_smb/dcerpc/winreg_spec.rb +710 -0
  166. data/spec/lib/ruby_smb/dcerpc_spec.rb +81 -0
  167. data/spec/lib/ruby_smb/dispatcher/socket_spec.rb +3 -1
  168. data/spec/lib/ruby_smb/error_spec.rb +59 -0
  169. data/spec/lib/ruby_smb/generic_packet_spec.rb +52 -4
  170. data/spec/lib/ruby_smb/smb1/file_spec.rb +191 -2
  171. data/spec/lib/ruby_smb/smb1/packet/empty_packet_spec.rb +68 -0
  172. data/spec/lib/ruby_smb/smb1/packet/session_setup_legacy_request_spec.rb +2 -2
  173. data/spec/lib/ruby_smb/smb1/packet/session_setup_legacy_response_spec.rb +2 -2
  174. data/spec/lib/ruby_smb/smb1/packet/session_setup_request_spec.rb +2 -2
  175. data/spec/lib/ruby_smb/smb1/packet/session_setup_response_spec.rb +1 -1
  176. data/spec/lib/ruby_smb/smb1/packet/trans2/find_first2_response_spec.rb +11 -2
  177. data/spec/lib/ruby_smb/smb1/packet/trans2/find_next2_response_spec.rb +11 -2
  178. data/spec/lib/ruby_smb/smb1/packet/tree_connect_response_spec.rb +40 -0
  179. data/spec/lib/ruby_smb/smb1/pipe_spec.rb +272 -149
  180. data/spec/lib/ruby_smb/smb1/tree_spec.rb +44 -7
  181. data/spec/lib/ruby_smb/smb2/bit_field/session_flags_spec.rb +9 -0
  182. data/spec/lib/ruby_smb/smb2/bit_field/share_flags_spec.rb +27 -0
  183. data/spec/lib/ruby_smb/smb2/file_spec.rb +323 -6
  184. data/spec/lib/ruby_smb/smb2/negotiate_context_spec.rb +332 -0
  185. data/spec/lib/ruby_smb/smb2/packet/compression_transform_header_spec.rb +108 -0
  186. data/spec/lib/ruby_smb/smb2/packet/error_packet_spec.rb +78 -0
  187. data/spec/lib/ruby_smb/smb2/packet/negotiate_request_spec.rb +138 -3
  188. data/spec/lib/ruby_smb/smb2/packet/negotiate_response_spec.rb +120 -2
  189. data/spec/lib/ruby_smb/smb2/packet/query_directory_response_spec.rb +8 -0
  190. data/spec/lib/ruby_smb/smb2/packet/transform_header_spec.rb +220 -0
  191. data/spec/lib/ruby_smb/smb2/packet/tree_connect_request_spec.rb +339 -9
  192. data/spec/lib/ruby_smb/smb2/packet/tree_connect_response_spec.rb +3 -22
  193. data/spec/lib/ruby_smb/smb2/pipe_spec.rb +286 -149
  194. data/spec/lib/ruby_smb/smb2/smb2_header_spec.rb +2 -2
  195. data/spec/lib/ruby_smb/smb2/tree_spec.rb +261 -2
  196. metadata +191 -83
  197. metadata.gz.sig +0 -0
  198. data/lib/ruby_smb/smb1/dcerpc.rb +0 -67
  199. data/lib/ruby_smb/smb2/dcerpc.rb +0 -70
  200. data/spec/lib/ruby_smb/smb1/packet/error_packet_spec.rb +0 -37
@@ -0,0 +1,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
@@ -85,7 +85,7 @@ RSpec.describe RubySMB::Dispatcher::Socket do
85
85
  end
86
86
 
87
87
  it 'reads the nbss header using Nbss::SessionHeader structure' do
88
- expect(RubySMB::Nbss::SessionHeader).to receive(:read).with(response_socket).and_return(session_header)
88
+ expect(RubySMB::Nbss::SessionHeader).to receive(:read).with(nbss).and_return(session_header)
89
89
  smb_socket.recv_packet
90
90
  end
91
91
 
@@ -105,6 +105,7 @@ RSpec.describe RubySMB::Dispatcher::Socket do
105
105
  packet_length = 10
106
106
  session_header.packet_length = packet_length
107
107
  allow(RubySMB::Nbss::SessionHeader).to receive(:read).and_return(session_header)
108
+ expect(response_socket).to receive(:read).with(4).and_return(session_header)
108
109
  expect(response_socket).to receive(:read).with(packet_length).and_return('A' * packet_length)
109
110
  smb_socket.recv_packet
110
111
  end
@@ -116,6 +117,7 @@ RSpec.describe RubySMB::Dispatcher::Socket do
116
117
  session_header.packet_length = packet_length
117
118
  allow(RubySMB::Nbss::SessionHeader).to receive(:read).and_return(session_header)
118
119
 
120
+ expect(response_socket).to receive(:read).with(4).and_return(session_header)
119
121
  loop do
120
122
  expect(response_socket).to receive(:read).with(packet_length).and_return('A' * returned_length).once
121
123
  packet_length -= returned_length
@@ -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