ruby_smb 2.0.0 → 2.0.5
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.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +3 -3
- data.tar.gz.sig +5 -1
- data/examples/anonymous_auth.rb +3 -3
- data/examples/append_file.rb +10 -8
- data/examples/authenticate.rb +9 -5
- data/examples/delete_file.rb +8 -6
- data/examples/enum_registry_key.rb +5 -4
- data/examples/enum_registry_values.rb +5 -4
- data/examples/list_directory.rb +8 -6
- data/examples/negotiate_with_netbios_service.rb +9 -5
- data/examples/net_share_enum_all.rb +6 -4
- data/examples/pipes.rb +11 -12
- data/examples/query_service_status.rb +64 -0
- data/examples/read_file.rb +8 -6
- data/examples/read_registry_key_value.rb +6 -5
- data/examples/rename_file.rb +9 -7
- data/examples/tree_connect.rb +7 -5
- data/examples/write_file.rb +9 -7
- data/lib/ruby_smb/client.rb +117 -53
- data/lib/ruby_smb/client/authentication.rb +7 -12
- data/lib/ruby_smb/client/echo.rb +2 -4
- data/lib/ruby_smb/client/negotiation.rb +31 -12
- data/lib/ruby_smb/client/tree_connect.rb +2 -4
- data/lib/ruby_smb/client/utils.rb +16 -10
- data/lib/ruby_smb/client/winreg.rb +1 -1
- data/lib/ruby_smb/dcerpc.rb +4 -0
- data/lib/ruby_smb/dcerpc/error.rb +3 -0
- data/lib/ruby_smb/dcerpc/ndr.rb +306 -44
- data/lib/ruby_smb/dcerpc/netlogon.rb +101 -0
- data/lib/ruby_smb/dcerpc/netlogon/netr_server_authenticate3_request.rb +28 -0
- data/lib/ruby_smb/dcerpc/netlogon/netr_server_authenticate3_response.rb +26 -0
- data/lib/ruby_smb/dcerpc/netlogon/netr_server_password_set2_request.rb +27 -0
- data/lib/ruby_smb/dcerpc/netlogon/netr_server_password_set2_response.rb +23 -0
- data/lib/ruby_smb/dcerpc/netlogon/netr_server_req_challenge_request.rb +25 -0
- data/lib/ruby_smb/dcerpc/netlogon/netr_server_req_challenge_response.rb +24 -0
- data/lib/ruby_smb/dcerpc/request.rb +19 -0
- data/lib/ruby_smb/dcerpc/rpc_security_attributes.rb +34 -0
- data/lib/ruby_smb/dcerpc/rrp_unicode_string.rb +9 -6
- data/lib/ruby_smb/dcerpc/svcctl.rb +479 -0
- data/lib/ruby_smb/dcerpc/svcctl/change_service_config_w_request.rb +48 -0
- data/lib/ruby_smb/dcerpc/svcctl/change_service_config_w_response.rb +26 -0
- data/lib/ruby_smb/dcerpc/svcctl/close_service_handle_request.rb +25 -0
- data/lib/ruby_smb/dcerpc/svcctl/close_service_handle_response.rb +26 -0
- data/lib/ruby_smb/dcerpc/svcctl/control_service_request.rb +26 -0
- data/lib/ruby_smb/dcerpc/svcctl/control_service_response.rb +26 -0
- data/lib/ruby_smb/dcerpc/svcctl/open_sc_manager_w_request.rb +35 -0
- data/lib/ruby_smb/dcerpc/svcctl/open_sc_manager_w_response.rb +23 -0
- data/lib/ruby_smb/dcerpc/svcctl/open_service_w_request.rb +31 -0
- data/lib/ruby_smb/dcerpc/svcctl/open_service_w_response.rb +23 -0
- data/lib/ruby_smb/dcerpc/svcctl/query_service_config_w_request.rb +25 -0
- data/lib/ruby_smb/dcerpc/svcctl/query_service_config_w_response.rb +44 -0
- data/lib/ruby_smb/dcerpc/svcctl/query_service_status_request.rb +23 -0
- data/lib/ruby_smb/dcerpc/svcctl/query_service_status_response.rb +27 -0
- data/lib/ruby_smb/dcerpc/svcctl/service_status.rb +25 -0
- data/lib/ruby_smb/dcerpc/svcctl/start_service_w_request.rb +27 -0
- data/lib/ruby_smb/dcerpc/svcctl/start_service_w_response.rb +25 -0
- data/lib/ruby_smb/dcerpc/winreg.rb +98 -17
- data/lib/ruby_smb/dcerpc/winreg/create_key_request.rb +73 -0
- data/lib/ruby_smb/dcerpc/winreg/create_key_response.rb +36 -0
- data/lib/ruby_smb/dcerpc/winreg/enum_key_request.rb +1 -1
- data/lib/ruby_smb/dcerpc/winreg/enum_value_request.rb +1 -1
- data/lib/ruby_smb/dcerpc/winreg/enum_value_response.rb +1 -1
- data/lib/ruby_smb/dcerpc/winreg/open_root_key_request.rb +4 -4
- data/lib/ruby_smb/dcerpc/winreg/query_info_key_request.rb +1 -1
- data/lib/ruby_smb/dcerpc/winreg/query_value_request.rb +7 -6
- data/lib/ruby_smb/dcerpc/winreg/query_value_response.rb +10 -10
- data/lib/ruby_smb/dcerpc/winreg/save_key_request.rb +37 -0
- data/lib/ruby_smb/dcerpc/winreg/save_key_response.rb +23 -0
- data/lib/ruby_smb/dispatcher/base.rb +1 -1
- data/lib/ruby_smb/dispatcher/socket.rb +3 -2
- data/lib/ruby_smb/error.rb +21 -5
- data/lib/ruby_smb/field/stringz16.rb +17 -1
- data/lib/ruby_smb/generic_packet.rb +11 -1
- data/lib/ruby_smb/nbss/session_header.rb +4 -4
- data/lib/ruby_smb/smb1/file.rb +9 -24
- data/lib/ruby_smb/smb1/pipe.rb +8 -6
- data/lib/ruby_smb/smb1/tree.rb +22 -9
- data/lib/ruby_smb/smb2/file.rb +46 -46
- data/lib/ruby_smb/smb2/packet/negotiate_response.rb +1 -1
- data/lib/ruby_smb/smb2/pipe.rb +9 -6
- data/lib/ruby_smb/smb2/tree.rb +30 -20
- data/lib/ruby_smb/version.rb +1 -1
- data/spec/lib/ruby_smb/client_spec.rb +248 -109
- data/spec/lib/ruby_smb/dcerpc/ndr_spec.rb +1396 -77
- data/spec/lib/ruby_smb/dcerpc/netlogon/netr_server_authenticate3_request_spec.rb +69 -0
- data/spec/lib/ruby_smb/dcerpc/netlogon/netr_server_authenticate3_response_spec.rb +53 -0
- data/spec/lib/ruby_smb/dcerpc/netlogon/netr_server_password_set2_request_spec.rb +69 -0
- data/spec/lib/ruby_smb/dcerpc/netlogon/netr_server_password_set2_response_spec.rb +37 -0
- data/spec/lib/ruby_smb/dcerpc/netlogon/netr_server_req_challenge_request_spec.rb +45 -0
- data/spec/lib/ruby_smb/dcerpc/netlogon/netr_server_req_challenge_response_spec.rb +37 -0
- data/spec/lib/ruby_smb/dcerpc/rpc_security_attributes_spec.rb +161 -0
- data/spec/lib/ruby_smb/dcerpc/rrp_unicode_string_spec.rb +49 -12
- data/spec/lib/ruby_smb/dcerpc/svcctl/change_service_config_w_request_spec.rb +191 -0
- data/spec/lib/ruby_smb/dcerpc/svcctl/change_service_config_w_response_spec.rb +38 -0
- data/spec/lib/ruby_smb/dcerpc/svcctl/close_service_handle_request_spec.rb +30 -0
- data/spec/lib/ruby_smb/dcerpc/svcctl/close_service_handle_response_spec.rb +38 -0
- data/spec/lib/ruby_smb/dcerpc/svcctl/control_service_request_spec.rb +39 -0
- data/spec/lib/ruby_smb/dcerpc/svcctl/control_service_response_spec.rb +38 -0
- data/spec/lib/ruby_smb/dcerpc/svcctl/open_sc_manager_w_request_spec.rb +78 -0
- data/spec/lib/ruby_smb/dcerpc/svcctl/open_sc_manager_w_response_spec.rb +38 -0
- data/spec/lib/ruby_smb/dcerpc/svcctl/open_service_w_request_spec.rb +59 -0
- data/spec/lib/ruby_smb/dcerpc/svcctl/open_service_w_response_spec.rb +38 -0
- data/spec/lib/ruby_smb/dcerpc/svcctl/query_service_config_w_request_spec.rb +38 -0
- data/spec/lib/ruby_smb/dcerpc/svcctl/query_service_config_w_response_spec.rb +152 -0
- data/spec/lib/ruby_smb/dcerpc/svcctl/query_service_status_request_spec.rb +30 -0
- data/spec/lib/ruby_smb/dcerpc/svcctl/query_service_status_response_spec.rb +38 -0
- data/spec/lib/ruby_smb/dcerpc/svcctl/service_status_spec.rb +72 -0
- data/spec/lib/ruby_smb/dcerpc/svcctl/start_service_w_request_spec.rb +46 -0
- data/spec/lib/ruby_smb/dcerpc/svcctl/start_service_w_response_spec.rb +30 -0
- data/spec/lib/ruby_smb/dcerpc/svcctl_spec.rb +512 -0
- data/spec/lib/ruby_smb/dcerpc/winreg/create_key_request_spec.rb +110 -0
- data/spec/lib/ruby_smb/dcerpc/winreg/create_key_response_spec.rb +44 -0
- data/spec/lib/ruby_smb/dcerpc/winreg/enum_key_request_spec.rb +0 -4
- data/spec/lib/ruby_smb/dcerpc/winreg/enum_value_request_spec.rb +2 -2
- data/spec/lib/ruby_smb/dcerpc/winreg/enum_value_response_spec.rb +2 -2
- data/spec/lib/ruby_smb/dcerpc/winreg/open_root_key_request_spec.rb +9 -4
- data/spec/lib/ruby_smb/dcerpc/winreg/query_info_key_request_spec.rb +0 -4
- data/spec/lib/ruby_smb/dcerpc/winreg/query_value_request_spec.rb +17 -17
- data/spec/lib/ruby_smb/dcerpc/winreg/query_value_response_spec.rb +11 -23
- data/spec/lib/ruby_smb/dcerpc/winreg/save_key_request_spec.rb +57 -0
- data/spec/lib/ruby_smb/dcerpc/winreg/save_key_response_spec.rb +22 -0
- data/spec/lib/ruby_smb/dcerpc/winreg_spec.rb +227 -41
- data/spec/lib/ruby_smb/dispatcher/socket_spec.rb +12 -12
- data/spec/lib/ruby_smb/error_spec.rb +34 -5
- data/spec/lib/ruby_smb/field/stringz16_spec.rb +12 -0
- data/spec/lib/ruby_smb/generic_packet_spec.rb +7 -0
- data/spec/lib/ruby_smb/nbss/session_header_spec.rb +4 -11
- data/spec/lib/ruby_smb/smb1/file_spec.rb +1 -3
- data/spec/lib/ruby_smb/smb1/pipe_spec.rb +30 -5
- data/spec/lib/ruby_smb/smb1/tree_spec.rb +22 -0
- data/spec/lib/ruby_smb/smb2/file_spec.rb +73 -21
- data/spec/lib/ruby_smb/smb2/pipe_spec.rb +9 -5
- data/spec/lib/ruby_smb/smb2/tree_spec.rb +64 -7
- metadata +91 -2
- metadata.gz.sig +0 -0
@@ -9,16 +9,45 @@ RSpec.describe RubySMB::Error::InvalidPacket do
|
|
9
9
|
end
|
10
10
|
|
11
11
|
context 'with a Hash' do
|
12
|
-
|
13
|
-
|
12
|
+
let(:ex) do
|
13
|
+
described_class.new(
|
14
14
|
expected_proto: RubySMB::SMB1::SMB_PROTOCOL_ID,
|
15
15
|
expected_cmd: RubySMB::SMB1::Packet::NegotiateResponseExtended::COMMAND,
|
16
16
|
expected_custom: "extended_security=1",
|
17
|
-
|
18
|
-
received_cmd: RubySMB::SMB2::Packet::NegotiateResponse::COMMAND,
|
17
|
+
packet: packet,
|
19
18
|
received_custom: "extended_security=0"
|
20
19
|
)
|
21
|
-
|
20
|
+
end
|
21
|
+
|
22
|
+
context 'with an SMB2 packet' do
|
23
|
+
let(:packet) { RubySMB::SMB2::Packet::NegotiateResponse.new }
|
24
|
+
|
25
|
+
it 'outputs the expected error message' do
|
26
|
+
expect(ex.to_s).to eq('Expecting SMB1 protocol with command=114 (extended_security=1), got SMB2 protocol with command=0 (extended_security=0), Status: (0x00000000) STATUS_SUCCESS: The operation completed successfully.')
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
context 'with an SMB1 packet' do
|
31
|
+
let(:packet) { RubySMB::SMB1::Packet::ReadAndxRequest.new }
|
32
|
+
|
33
|
+
it 'outputs the expected error message' do
|
34
|
+
expect(ex.to_s).to eq('Expecting SMB1 protocol with command=114 (extended_security=1), got SMB1 protocol with command=46 (extended_security=0), Status: (0x00000000) STATUS_SUCCESS: The operation completed successfully.')
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
context 'without packet' do
|
39
|
+
let(:ex) do
|
40
|
+
described_class.new(
|
41
|
+
expected_proto: RubySMB::SMB1::SMB_PROTOCOL_ID,
|
42
|
+
expected_cmd: RubySMB::SMB1::Packet::NegotiateResponseExtended::COMMAND,
|
43
|
+
expected_custom: "extended_security=1",
|
44
|
+
received_custom: "extended_security=0"
|
45
|
+
)
|
46
|
+
end
|
47
|
+
|
48
|
+
it 'outputs the expected error message' do
|
49
|
+
expect(ex.to_s).to eq('Expecting SMB1 protocol with command=114 (extended_security=1), got ??? protocol with command=??? (extended_security=0)')
|
50
|
+
end
|
22
51
|
end
|
23
52
|
end
|
24
53
|
|
@@ -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
|
@@ -88,6 +88,13 @@ RSpec.describe RubySMB::GenericPacket do
|
|
88
88
|
packet = RubySMB::SMB2::Packet::NegotiateResponse.read(smb2_error_packet.to_binary_s)
|
89
89
|
expect(packet.original_command).to eq RubySMB::SMB2::Packet::NegotiateResponse::COMMAND
|
90
90
|
end
|
91
|
+
|
92
|
+
context 'when the server returns an SMB1 error packet' do
|
93
|
+
let(:smb1_error_packet) { RubySMB::SMB1::Packet::EmptyPacket.new }
|
94
|
+
it 'returns the empty packet instead of the asked for class' do
|
95
|
+
expect(RubySMB::SMB2::Packet::NegotiateResponse.read(smb1_error_packet.to_binary_s)).to be_a RubySMB::SMB1::Packet::EmptyPacket
|
96
|
+
end
|
97
|
+
end
|
91
98
|
end
|
92
99
|
end
|
93
100
|
|
@@ -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 :
|
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 '#
|
19
|
-
it 'is a
|
20
|
-
expect(session_header.
|
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
|
@@ -521,9 +521,7 @@ RSpec.describe RubySMB::SMB1::File do
|
|
521
521
|
|
522
522
|
it 'raises an InvalidPacket exception if the response is not valid' do
|
523
523
|
allow(response).to receive(:valid?).and_return(false)
|
524
|
-
|
525
|
-
allow(response).to receive(:smb_header).and_return(smb_header)
|
526
|
-
allow(smb_header).to receive_messages(:protocol => nil, :command => nil)
|
524
|
+
allow(response).to receive(:packet_smb_version)
|
527
525
|
expect { file.close }.to raise_error(RubySMB::Error::InvalidPacket)
|
528
526
|
end
|
529
527
|
|
@@ -80,9 +80,7 @@ RSpec.describe RubySMB::SMB1::Pipe do
|
|
80
80
|
|
81
81
|
it 'raises an InvalidPacket exception if the response is not valid' do
|
82
82
|
allow(response).to receive(:valid?).and_return(false)
|
83
|
-
|
84
|
-
allow(response).to receive(:smb_header).and_return(smb_header)
|
85
|
-
allow(smb_header).to receive_messages(:protocol => nil, :command => nil)
|
83
|
+
allow(response).to receive(:packet_smb_version)
|
86
84
|
expect { pipe.peek }.to raise_error(RubySMB::Error::InvalidPacket)
|
87
85
|
end
|
88
86
|
|
@@ -149,12 +147,40 @@ RSpec.describe RubySMB::SMB1::Pipe do
|
|
149
147
|
end
|
150
148
|
end
|
151
149
|
|
150
|
+
context 'with \'\\srvsvc\' filename' do
|
151
|
+
it 'extends Srvsvc class' do
|
152
|
+
pipe = described_class.new(tree: tree, response: nt_create_andx_response, name: '\\srvsvc')
|
153
|
+
expect(pipe.respond_to?(:net_share_enum_all)).to be true
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
152
157
|
context 'with \'winreg\' filename' do
|
153
158
|
it 'extends Winreg class' do
|
154
159
|
pipe = described_class.new(tree: tree, response: nt_create_andx_response, name: 'winreg')
|
155
160
|
expect(pipe.respond_to?(:has_registry_key?)).to be true
|
156
161
|
end
|
157
162
|
end
|
163
|
+
|
164
|
+
context 'with \'\\winreg\' filename' do
|
165
|
+
it 'extends Winreg class' do
|
166
|
+
pipe = described_class.new(tree: tree, response: nt_create_andx_response, name: '\\winreg')
|
167
|
+
expect(pipe.respond_to?(:has_registry_key?)).to be true
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
context 'with \'svcctl\' filename' do
|
172
|
+
it 'extends svcctl class' do
|
173
|
+
pipe = described_class.new(tree: tree, response: nt_create_andx_response, name: 'svcctl')
|
174
|
+
expect(pipe.respond_to?(:query_service_config)).to be true
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
context 'with \'\\svcctl\' filename' do
|
179
|
+
it 'extends svcctl class' do
|
180
|
+
pipe = described_class.new(tree: tree, response: nt_create_andx_response, name: '\\svcctl')
|
181
|
+
expect(pipe.respond_to?(:query_service_config)).to be true
|
182
|
+
end
|
183
|
+
end
|
158
184
|
end
|
159
185
|
|
160
186
|
describe '#dcerpc_request' do
|
@@ -238,8 +264,7 @@ RSpec.describe RubySMB::SMB1::Pipe do
|
|
238
264
|
|
239
265
|
context 'when the response is not a Trans packet' do
|
240
266
|
it 'raises an InvalidPacket exception' do
|
241
|
-
allow(trans_nmpipe_response).to
|
242
|
-
allow(trans_nmpipe_response).to receive_message_chain(:smb_header, :command)
|
267
|
+
allow(trans_nmpipe_response).to receive(:packet_smb_version)
|
243
268
|
allow(trans_nmpipe_response).to receive(:valid?).and_return(false)
|
244
269
|
expect { pipe.dcerpc_request(stub_packet, options) }.to raise_error(RubySMB::Error::InvalidPacket)
|
245
270
|
end
|
@@ -492,4 +492,26 @@ RSpec.describe RubySMB::SMB1::Tree do
|
|
492
492
|
end
|
493
493
|
end
|
494
494
|
|
495
|
+
describe '#open_pipe' do
|
496
|
+
let(:opts) { { filename: 'test', write: true } }
|
497
|
+
before :example do
|
498
|
+
allow(tree).to receive(:open_file)
|
499
|
+
end
|
500
|
+
|
501
|
+
it 'calls #open_file with the provided options' do
|
502
|
+
opts[:filename] ='\\test'
|
503
|
+
expect(tree).to receive(:open_file).with(opts)
|
504
|
+
tree.open_pipe(opts)
|
505
|
+
end
|
506
|
+
|
507
|
+
it 'prepends the filename with \\ if needed' do
|
508
|
+
expect(tree).to receive(:open_file).with( { filename: '\\test', write: true } )
|
509
|
+
tree.open_pipe(opts)
|
510
|
+
end
|
511
|
+
|
512
|
+
it 'does not modify the original option hash' do
|
513
|
+
tree.open_pipe(opts)
|
514
|
+
expect(opts).to eq( { filename: 'test', write: true } )
|
515
|
+
end
|
516
|
+
end
|
495
517
|
end
|
@@ -43,7 +43,7 @@ RSpec.describe RubySMB::SMB2::File do
|
|
43
43
|
it { is_expected.to respond_to :size }
|
44
44
|
it { is_expected.to respond_to :size_on_disk }
|
45
45
|
it { is_expected.to respond_to :tree }
|
46
|
-
it { is_expected.to respond_to :
|
46
|
+
it { is_expected.to respond_to :tree_connect_encrypt_data }
|
47
47
|
|
48
48
|
it 'pulls the attributes from the response packet' do
|
49
49
|
expect(file.attributes).to eq create_response.file_attributes
|
@@ -73,8 +73,8 @@ RSpec.describe RubySMB::SMB2::File do
|
|
73
73
|
expect(file.size_on_disk).to eq create_response.allocation_size
|
74
74
|
end
|
75
75
|
|
76
|
-
it 'sets the
|
77
|
-
expect(file.
|
76
|
+
it 'sets the tree_connect_encrypt_data flag to false by default' do
|
77
|
+
expect(file.tree_connect_encrypt_data).to be false
|
78
78
|
end
|
79
79
|
|
80
80
|
describe '#set_header_fields' do
|
@@ -107,6 +107,10 @@ RSpec.describe RubySMB::SMB2::File do
|
|
107
107
|
it 'sets the offset of the packet' do
|
108
108
|
expect(file.read_packet(offset: 55).offset).to eq 55
|
109
109
|
end
|
110
|
+
|
111
|
+
it 'sets the credit_charge of the packet' do
|
112
|
+
expect(file.read_packet(credit_charge: 3).smb2_header.credit_charge).to eq 3
|
113
|
+
end
|
110
114
|
end
|
111
115
|
|
112
116
|
describe '#read' do
|
@@ -125,14 +129,14 @@ RSpec.describe RubySMB::SMB2::File do
|
|
125
129
|
end
|
126
130
|
|
127
131
|
it 'uses a single packet to read the entire file' do
|
128
|
-
expect(file).to receive(:read_packet).with(read_length: 108, offset: 0).and_return(small_read)
|
132
|
+
expect(file).to receive(:read_packet).with(read_length: 108, offset: 0, credit_charge: 0).and_return(small_read)
|
129
133
|
expect(client).to receive(:send_recv).with(small_read, encrypt: false).and_return 'fake data'
|
130
134
|
expect(RubySMB::SMB2::Packet::ReadResponse).to receive(:read).with('fake data').and_return(small_response)
|
131
135
|
expect(file.read).to eq 'fake data'
|
132
136
|
end
|
133
137
|
|
134
138
|
it 'calls Client #send_recv with encryption set if required' do
|
135
|
-
file.
|
139
|
+
file.tree_connect_encrypt_data = true
|
136
140
|
expect(client).to receive(:send_recv).with(small_read, encrypt: true)
|
137
141
|
file.read
|
138
142
|
end
|
@@ -161,14 +165,15 @@ RSpec.describe RubySMB::SMB2::File do
|
|
161
165
|
}
|
162
166
|
|
163
167
|
before :example do
|
168
|
+
client.server_supports_multi_credit = 1
|
164
169
|
allow(file).to receive(:read_packet)
|
165
170
|
allow(client).to receive(:send_recv)
|
166
171
|
allow(RubySMB::SMB2::Packet::ReadResponse).to receive(:read).and_return(big_response)
|
167
172
|
end
|
168
173
|
|
169
|
-
it 'uses
|
170
|
-
expect(file).to receive(:read_packet).once.with(read_length: described_class::MAX_PACKET_SIZE, offset: 0).and_return(big_read)
|
171
|
-
expect(file).to receive(:read_packet).once.with(read_length: described_class::MAX_PACKET_SIZE, offset: described_class::MAX_PACKET_SIZE).and_return(big_read)
|
174
|
+
it 'uses multiple packets to read the file in chunks' do
|
175
|
+
expect(file).to receive(:read_packet).once.with(read_length: described_class::MAX_PACKET_SIZE, offset: 0, credit_charge: 1).and_return(big_read)
|
176
|
+
expect(file).to receive(:read_packet).once.with(read_length: described_class::MAX_PACKET_SIZE, offset: described_class::MAX_PACKET_SIZE, credit_charge: 1).and_return(big_read)
|
172
177
|
expect(client).to receive(:send_recv).twice.and_return 'fake data'
|
173
178
|
expect(RubySMB::SMB2::Packet::ReadResponse).to receive(:read).twice.with('fake data').and_return(big_response)
|
174
179
|
file.read(bytes: (described_class::MAX_PACKET_SIZE * 2))
|
@@ -177,14 +182,14 @@ RSpec.describe RubySMB::SMB2::File do
|
|
177
182
|
it 'calls Client #send_recv with encryption set if required' do
|
178
183
|
read_request = double('Read Request')
|
179
184
|
allow(file).to receive(:read_packet).and_return(read_request)
|
180
|
-
file.
|
185
|
+
file.tree_connect_encrypt_data = true
|
181
186
|
expect(client).to receive(:send_recv).twice.with(read_request, encrypt: true)
|
182
187
|
file.read(bytes: (described_class::MAX_PACKET_SIZE * 2))
|
183
188
|
end
|
184
189
|
|
185
190
|
context 'when the second response is not valid' do
|
186
191
|
it 'raise an InvalidPacket exception' do
|
187
|
-
allow(file).to receive(:read_packet).with(read_length: described_class::MAX_PACKET_SIZE, offset: described_class::MAX_PACKET_SIZE) do
|
192
|
+
allow(file).to receive(:read_packet).with(read_length: described_class::MAX_PACKET_SIZE, offset: described_class::MAX_PACKET_SIZE, credit_charge: 1) do
|
188
193
|
big_response.smb2_header.command = RubySMB::SMB2::Commands::LOGOFF
|
189
194
|
end
|
190
195
|
expect { file.read(bytes: (described_class::MAX_PACKET_SIZE * 2)) }.to raise_error(RubySMB::Error::InvalidPacket)
|
@@ -193,12 +198,31 @@ RSpec.describe RubySMB::SMB2::File do
|
|
193
198
|
|
194
199
|
context 'when the second response status code is not STATUS_SUCCESS' do
|
195
200
|
it 'raise an UnexpectedStatusCode exception' do
|
196
|
-
allow(file).to receive(:read_packet).with(read_length: described_class::MAX_PACKET_SIZE, offset: described_class::MAX_PACKET_SIZE) do
|
201
|
+
allow(file).to receive(:read_packet).with(read_length: described_class::MAX_PACKET_SIZE, offset: described_class::MAX_PACKET_SIZE, credit_charge: 1) do
|
197
202
|
big_response.smb2_header.nt_status = WindowsError::NTStatus::STATUS_INVALID_HANDLE.value
|
198
203
|
end
|
199
204
|
expect { file.read(bytes: (described_class::MAX_PACKET_SIZE * 2)) }.to raise_error(RubySMB::Error::UnexpectedStatusCode)
|
200
205
|
end
|
201
206
|
end
|
207
|
+
|
208
|
+
context 'when the server does not support multi credits' do
|
209
|
+
it 'reads 65536 bytes at a time with no credit charge' do
|
210
|
+
client.server_supports_multi_credit = false
|
211
|
+
expect(file).to receive(:read_packet).once.with(read_length: 65536, offset: 0, credit_charge: 0).and_return(big_read)
|
212
|
+
expect(file).to receive(:read_packet).once.with(read_length: 65536, offset: 65536, credit_charge: 0).and_return(big_read)
|
213
|
+
file.read(bytes: (described_class::MAX_PACKET_SIZE * 4))
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
context 'when the server supports multi credits' do
|
218
|
+
it 'reads a number of bytes equal to #server_max_read_size, with the expected credit charge' do
|
219
|
+
credit_charge = (90000 - 1) / 65536 + 1
|
220
|
+
client.server_max_read_size = 90000
|
221
|
+
expect(file).to receive(:read_packet).once.with(read_length: 90000, offset: 0, credit_charge: credit_charge).and_return(big_read)
|
222
|
+
expect(file).to receive(:read_packet).once.with(read_length: (described_class::MAX_PACKET_SIZE * 4 - 90000), offset: 90000, credit_charge: credit_charge).and_return(big_read)
|
223
|
+
file.read(bytes: (described_class::MAX_PACKET_SIZE * 4))
|
224
|
+
end
|
225
|
+
end
|
202
226
|
end
|
203
227
|
end
|
204
228
|
|
@@ -222,6 +246,10 @@ RSpec.describe RubySMB::SMB2::File do
|
|
222
246
|
it 'sets the buffer on the packet' do
|
223
247
|
expect(file.write_packet(data:'hello').buffer).to eq 'hello'
|
224
248
|
end
|
249
|
+
|
250
|
+
it 'sets the credit_charge on the packet header' do
|
251
|
+
expect(file.write_packet(credit_charge: 5).smb2_header.credit_charge).to eq 5
|
252
|
+
end
|
225
253
|
end
|
226
254
|
|
227
255
|
describe '#write' do
|
@@ -235,13 +263,18 @@ RSpec.describe RubySMB::SMB2::File do
|
|
235
263
|
it 'calls Client #send_recv with encryption set if required' do
|
236
264
|
write_request = double('Write Request')
|
237
265
|
allow(file).to receive(:write_packet).and_return(write_request)
|
238
|
-
file.
|
266
|
+
file.tree_connect_encrypt_data = true
|
239
267
|
expect(client).to receive(:send_recv).once.with(write_request, encrypt: true).and_return(write_response.to_binary_s)
|
240
268
|
file.write(data: 'test')
|
241
269
|
end
|
242
270
|
end
|
243
271
|
|
244
272
|
context 'for a large write' do
|
273
|
+
before :example do
|
274
|
+
allow(client).to receive(:send_recv).and_return(write_response.to_binary_s)
|
275
|
+
client.server_supports_multi_credit = 1
|
276
|
+
end
|
277
|
+
|
245
278
|
it 'sends multiple packets' do
|
246
279
|
expect(client).to receive(:send_recv).twice.and_return(write_response.to_binary_s)
|
247
280
|
file.write(data: SecureRandom.random_bytes(described_class::MAX_PACKET_SIZE + 1))
|
@@ -250,10 +283,31 @@ RSpec.describe RubySMB::SMB2::File do
|
|
250
283
|
it 'calls Client #send_recv with encryption set if required' do
|
251
284
|
write_request = double('Write Request')
|
252
285
|
allow(file).to receive(:write_packet).and_return(write_request)
|
253
|
-
file.
|
286
|
+
file.tree_connect_encrypt_data = true
|
254
287
|
expect(client).to receive(:send_recv).twice.with(write_request, encrypt: true).and_return(write_response.to_binary_s)
|
255
288
|
file.write(data: SecureRandom.random_bytes(described_class::MAX_PACKET_SIZE + 1))
|
256
289
|
end
|
290
|
+
|
291
|
+
context 'when the server does not support multi credits' do
|
292
|
+
it 'writes 65536 bytes at a time with no credit charge' do
|
293
|
+
client.server_supports_multi_credit = false
|
294
|
+
data = SecureRandom.random_bytes(65536 * 2)
|
295
|
+
expect(file).to receive(:write_packet).once.with(data: data[0, 65536], offset: 0, credit_charge: 0)
|
296
|
+
expect(file).to receive(:write_packet).once.with(data: data[65536, (data.size - 65536)], offset: 65536, credit_charge: 0)
|
297
|
+
file.write(data: data)
|
298
|
+
end
|
299
|
+
end
|
300
|
+
|
301
|
+
context 'when the server supports multi credits' do
|
302
|
+
it 'reads a number of bytes equal to #server_max_write_size, with the expected credit charge' do
|
303
|
+
data = SecureRandom.random_bytes(90000 * 2)
|
304
|
+
credit_charge = (90000 - 1) / 65536 + 1
|
305
|
+
client.server_max_write_size = 90000
|
306
|
+
expect(file).to receive(:write_packet).once.with(data: data[0, 90000], offset: 0, credit_charge: credit_charge)
|
307
|
+
expect(file).to receive(:write_packet).once.with(data: data[90000, (data.size - 90000)], offset: 90000, credit_charge: credit_charge)
|
308
|
+
file.write(data: data)
|
309
|
+
end
|
310
|
+
end
|
257
311
|
end
|
258
312
|
|
259
313
|
it 'raises an InvalidPacket exception if the response is not valid' do
|
@@ -307,7 +361,7 @@ RSpec.describe RubySMB::SMB2::File do
|
|
307
361
|
it 'calls Client #send_recv with encryption set if required' do
|
308
362
|
allow(file).to receive(:delete_packet)
|
309
363
|
allow(RubySMB::SMB2::Packet::SetInfoResponse).to receive(:read).and_return(small_response)
|
310
|
-
file.
|
364
|
+
file.tree_connect_encrypt_data = true
|
311
365
|
expect(client).to receive(:send_recv).with(small_delete, encrypt: true)
|
312
366
|
file.delete
|
313
367
|
end
|
@@ -349,7 +403,7 @@ RSpec.describe RubySMB::SMB2::File do
|
|
349
403
|
|
350
404
|
it 'calls Client #send_recv with encryption set if required' do
|
351
405
|
allow(RubySMB::SMB2::Packet::SetInfoResponse).to receive(:read).and_return(small_response)
|
352
|
-
file.
|
406
|
+
file.tree_connect_encrypt_data = true
|
353
407
|
expect(client).to receive(:send_recv).with(small_rename, encrypt: true)
|
354
408
|
file.rename('new_file.txt')
|
355
409
|
end
|
@@ -393,7 +447,7 @@ RSpec.describe RubySMB::SMB2::File do
|
|
393
447
|
end
|
394
448
|
|
395
449
|
it 'calls Client #send_recv with encryption set if required' do
|
396
|
-
file.
|
450
|
+
file.tree_connect_encrypt_data = true
|
397
451
|
expect(client).to receive(:send_recv).with(request, encrypt: true)
|
398
452
|
file.close
|
399
453
|
end
|
@@ -405,9 +459,7 @@ RSpec.describe RubySMB::SMB2::File do
|
|
405
459
|
|
406
460
|
it 'raises an InvalidPacket exception if the response is not valid' do
|
407
461
|
allow(response).to receive(:valid?).and_return(false)
|
408
|
-
|
409
|
-
allow(response).to receive(:smb2_header).and_return(smb2_header)
|
410
|
-
allow(smb2_header).to receive_messages(:protocol => nil, :command => nil)
|
462
|
+
allow(response).to receive(:packet_smb_version)
|
411
463
|
expect { file.close }.to raise_error(RubySMB::Error::InvalidPacket)
|
412
464
|
end
|
413
465
|
|
@@ -465,7 +517,7 @@ RSpec.describe RubySMB::SMB2::File do
|
|
465
517
|
it 'calls Client #send_recv with encryption set if required' do
|
466
518
|
request = double('Request')
|
467
519
|
allow(file).to receive(:read_packet).and_return(request)
|
468
|
-
file.
|
520
|
+
file.tree_connect_encrypt_data = true
|
469
521
|
expect(client).to receive(:send_recv).with(request, encrypt: true)
|
470
522
|
file.send_recv_read
|
471
523
|
end
|
@@ -528,7 +580,7 @@ RSpec.describe RubySMB::SMB2::File do
|
|
528
580
|
end
|
529
581
|
|
530
582
|
it 'calls Client #send_recv with encryption set if required' do
|
531
|
-
file.
|
583
|
+
file.tree_connect_encrypt_data = true
|
532
584
|
expect(client).to receive(:send_recv).with(request, encrypt: true)
|
533
585
|
file.send_recv_write
|
534
586
|
end
|
@@ -86,9 +86,7 @@ RSpec.describe RubySMB::SMB2::Pipe do
|
|
86
86
|
|
87
87
|
it 'raises an InvalidPacket exception if the response is not valid' do
|
88
88
|
allow(response).to receive(:valid?).and_return(false)
|
89
|
-
|
90
|
-
allow(response).to receive(:smb2_header).and_return(smb2_header)
|
91
|
-
allow(smb2_header).to receive_messages(:protocol => nil, :command => nil)
|
89
|
+
allow(response).to receive(:packet_smb_version)
|
92
90
|
expect { pipe.peek }.to raise_error(RubySMB::Error::InvalidPacket)
|
93
91
|
end
|
94
92
|
|
@@ -161,6 +159,13 @@ RSpec.describe RubySMB::SMB2::Pipe do
|
|
161
159
|
expect(pipe.respond_to?(:has_registry_key?)).to be true
|
162
160
|
end
|
163
161
|
end
|
162
|
+
|
163
|
+
context 'with \'svcctl\' filename' do
|
164
|
+
it 'extends svcctl class' do
|
165
|
+
pipe = described_class.new(tree: tree, response: create_response, name: 'svcctl')
|
166
|
+
expect(pipe.respond_to?(:query_service_config)).to be true
|
167
|
+
end
|
168
|
+
end
|
164
169
|
end
|
165
170
|
|
166
171
|
describe '#dcerpc_request' do
|
@@ -254,8 +259,7 @@ RSpec.describe RubySMB::SMB2::Pipe do
|
|
254
259
|
|
255
260
|
context 'when the response is not an IoctlResponse packet' do
|
256
261
|
it 'raises an InvalidPacket exception' do
|
257
|
-
allow(ioctl_response).to
|
258
|
-
allow(ioctl_response).to receive_message_chain(:smb2_header, :command)
|
262
|
+
allow(ioctl_response).to receive(:packet_smb_version)
|
259
263
|
allow(ioctl_response).to receive(:valid?).and_return(false)
|
260
264
|
expect { pipe.ioctl_send_recv(dcerpc_request, options) }.to raise_error(RubySMB::Error::InvalidPacket)
|
261
265
|
end
|