ruby_smb 3.3.19 → 3.3.21

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 (39) hide show
  1. checksums.yaml +4 -4
  2. data/examples/anonymous_auth.rb +5 -0
  3. data/examples/append_file.rb +57 -14
  4. data/examples/authenticate.rb +64 -16
  5. data/examples/delete_file.rb +53 -11
  6. data/examples/dump_secrets_from_sid.rb +43 -8
  7. data/examples/enum_domain_users.rb +51 -8
  8. data/examples/enum_registry_key.rb +51 -7
  9. data/examples/enum_registry_values.rb +51 -9
  10. data/examples/get_computer_info.rb +48 -8
  11. data/examples/list_directory.rb +54 -12
  12. data/examples/negotiate.rb +54 -42
  13. data/examples/negotiate_with_netbios_service.rb +55 -16
  14. data/examples/net_share_enum_all.rb +47 -8
  15. data/examples/pipes.rb +51 -7
  16. data/examples/query_service_status.rb +51 -8
  17. data/examples/read_file_encryption.rb +71 -26
  18. data/examples/read_registry_key_value.rb +54 -9
  19. data/examples/rename_file.rb +58 -15
  20. data/examples/write_file.rb +58 -15
  21. data/lib/ruby_smb/smb1/packet/trans2/query_fs_information_level/query_fs_cifs_unix_info.rb +31 -0
  22. data/lib/ruby_smb/smb1/packet/trans2/query_fs_information_level.rb +4 -0
  23. data/lib/ruby_smb/smb1/packet/trans2/query_fs_information_request.rb +12 -4
  24. data/lib/ruby_smb/smb1/packet/trans2/set_fs_information_level.rb +28 -0
  25. data/lib/ruby_smb/smb1/packet/trans2/set_fs_information_request.rb +90 -0
  26. data/lib/ruby_smb/smb1/packet/trans2/set_fs_information_response.rb +56 -0
  27. data/lib/ruby_smb/smb1/packet/trans2/set_information_level.rb +35 -0
  28. data/lib/ruby_smb/smb1/packet/trans2/set_path_information_request.rb +75 -0
  29. data/lib/ruby_smb/smb1/packet/trans2/set_path_information_response.rb +51 -0
  30. data/lib/ruby_smb/smb1/packet/trans2.rb +8 -0
  31. data/lib/ruby_smb/smb1/tree.rb +124 -0
  32. data/lib/ruby_smb/version.rb +1 -1
  33. data/spec/lib/ruby_smb/smb1/packet/trans2/query_fs_information_request_spec.rb +6 -1
  34. data/spec/lib/ruby_smb/smb1/packet/trans2/set_fs_information_request_spec.rb +52 -0
  35. data/spec/lib/ruby_smb/smb1/packet/trans2/set_fs_information_response_spec.rb +29 -0
  36. data/spec/lib/ruby_smb/smb1/packet/trans2/set_path_information_request_spec.rb +114 -0
  37. data/spec/lib/ruby_smb/smb1/packet/trans2/set_path_information_response_spec.rb +54 -0
  38. data/spec/lib/ruby_smb/smb1/tree_spec.rb +124 -0
  39. metadata +17 -2
@@ -0,0 +1,35 @@
1
+ module RubySMB
2
+ module SMB1
3
+ module Packet
4
+ module Trans2
5
+ # Information Level codes valid for Trans2 SET_PATH_INFORMATION and
6
+ # SET_FILE_INFORMATION requests. See
7
+ # [MS-CIFS 2.2.2.3.4 SET Information Level Codes](https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-cifs/0321265e-312a-4721-90fa-cd40a443ed86).
8
+ #
9
+ # FSCC pass-through levels are defined in
10
+ # {RubySMB::Fscc::FileInformation} and require
11
+ # `SMB_INFO_PASSTHROUGH` to be added. The constants defined here are
12
+ # the CIFS UNIX Extensions info levels used by Samba servers that
13
+ # advertise UNIX extensions support in their negotiate response. These
14
+ # levels fall outside MS-CIFS; their wire format is defined by the
15
+ # CIFS UNIX Extensions draft and implemented by Samba's
16
+ # `smb_set_file_unix_link` handler at
17
+ # [source3/smbd/smb1_trans2.c:3643-3712](https://github.com/samba-team/samba/blob/33f516c06756e12a9d11f50e2bf309171cdf5c88/source3/smbd/smb1_trans2.c#L3643-L3712).
18
+ module SetInformationLevel
19
+ # Set the symbolic link target for a file. The Trans2 parameters
20
+ # block carries the path being created (the symlink itself); the
21
+ # Trans2 data block carries the target path as a null-terminated
22
+ # string.
23
+ SMB_SET_FILE_UNIX_LINK = 0x0201
24
+
25
+ # Create a hard link. Data block carries the existing file path.
26
+ SMB_SET_FILE_UNIX_HLINK = 0x0203
27
+
28
+ def self.name(value)
29
+ constants.select { |c| c.upcase == c }.find { |c| const_get(c) == value }
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,75 @@
1
+ module RubySMB
2
+ module SMB1
3
+ module Packet
4
+ module Trans2
5
+ # The Trans2 Parameter Block for a SET_PATH_INFORMATION request as
6
+ # defined in
7
+ # [MS-CIFS 2.2.6.7.1 Request](https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-cifs/2ca0642a-1cd4-4e72-a16f-9e804a218262).
8
+ class SetPathInformationRequestTrans2Parameters < BinData::Record
9
+ endian :little
10
+
11
+ uint16 :information_level, label: 'Information Level'
12
+ uint32 :reserved, label: 'Reserved'
13
+ choice :filename, copy_on_change: true, selection: -> { parent.parent.smb_header.flags2.unicode } do
14
+ stringz16 1, label: 'FileName'
15
+ stringz 0, label: 'FileName'
16
+ end
17
+
18
+ # Returns the length of the Trans2Parameters struct
19
+ # in number of bytes
20
+ def length
21
+ do_num_bytes
22
+ end
23
+ end
24
+
25
+ # The Trans2 Data Block for a SET_PATH_INFORMATION request as defined
26
+ # in
27
+ # [MS-CIFS 2.2.6.7.1 Request](https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-cifs/2ca0642a-1cd4-4e72-a16f-9e804a218262).
28
+ #
29
+ # The data layout depends on the Information Level being set, so the
30
+ # block carries an opaque byte buffer that the caller fills in for the
31
+ # target info level. SMB_SET_FILE_UNIX_LINK (0x0201) for example
32
+ # carries the symlink target as a null-terminated string.
33
+ class SetPathInformationRequestTrans2Data < BinData::Record
34
+ string :buffer, read_length: -> { parent.buffer_read_length }
35
+
36
+ # Returns the length of the Trans2Data struct
37
+ # in number of bytes
38
+ def length
39
+ do_num_bytes
40
+ end
41
+ end
42
+
43
+ # The {RubySMB::SMB1::DataBlock} specific to this packet type. See
44
+ # [MS-CIFS 2.2.6.7.1 Request](https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-cifs/2ca0642a-1cd4-4e72-a16f-9e804a218262).
45
+ class SetPathInformationRequestDataBlock < RubySMB::SMB1::Packet::Trans2::DataBlock
46
+ uint8 :name, label: 'Name', initial_value: 0x00
47
+ string :pad1, length: -> { pad1_length }
48
+ set_path_information_request_trans2_parameters :trans2_parameters, label: 'Trans2 Parameters'
49
+ string :pad2, length: -> { pad2_length }
50
+ set_path_information_request_trans2_data :trans2_data, label: 'Trans2 Data'
51
+ end
52
+
53
+ # A Trans2 SET_PATH_INFORMATION Request Packet as defined in
54
+ # [MS-CIFS 2.2.6.7.1 Request](https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-cifs/2ca0642a-1cd4-4e72-a16f-9e804a218262).
55
+ # See also the subcommand overview at
56
+ # [MS-CIFS 2.2.6.7 TRANS2_SET_PATH_INFORMATION (0x0006)](https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-cifs/a23483d9-6543-4aaa-a996-e7c9506f8b94).
57
+ class SetPathInformationRequest < RubySMB::GenericPacket
58
+ COMMAND = RubySMB::SMB1::Commands::SMB_COM_TRANSACTION2
59
+
60
+ class ParameterBlock < RubySMB::SMB1::Packet::Trans2::Request::ParameterBlock
61
+ end
62
+
63
+ smb_header :smb_header
64
+ parameter_block :parameter_block
65
+ set_path_information_request_data_block :data_block
66
+
67
+ def initialize_instance
68
+ super
69
+ parameter_block.setup << RubySMB::SMB1::Packet::Trans2::Subcommands::SET_PATH_INFORMATION
70
+ end
71
+ end
72
+ end
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,51 @@
1
+ module RubySMB
2
+ module SMB1
3
+ module Packet
4
+ module Trans2
5
+ # The Trans2 Parameter Block for a SET_PATH_INFORMATION response as
6
+ # defined in
7
+ # [MS-CIFS 2.2.6.7.2 Response](https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-cifs/cf1cd579-9687-465d-9274-08ebb5944cd3).
8
+ class SetPathInformationResponseTrans2Parameters < BinData::Record
9
+ endian :little
10
+
11
+ uint16 :ea_error_offset, label: 'Extended Attribute Error Offset'
12
+
13
+ # Returns the length of the Trans2Parameters struct
14
+ # in number of bytes
15
+ def length
16
+ do_num_bytes
17
+ end
18
+ end
19
+
20
+ # The {RubySMB::SMB1::DataBlock} specific to this packet type. See
21
+ # [MS-CIFS 2.2.6.7.2 Response](https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-cifs/cf1cd579-9687-465d-9274-08ebb5944cd3).
22
+ class SetPathInformationResponseDataBlock < RubySMB::SMB1::Packet::Trans2::DataBlock
23
+ string :pad1, length: -> { pad1_length }
24
+ set_path_information_response_trans2_parameters :trans2_parameters, label: 'Trans2 Parameters'
25
+ # trans2_data: No data is sent by this message.
26
+ end
27
+
28
+ # A Trans2 SET_PATH_INFORMATION Response Packet as defined in
29
+ # [MS-CIFS 2.2.6.7.2 Response](https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-cifs/cf1cd579-9687-465d-9274-08ebb5944cd3).
30
+ # See also the subcommand overview at
31
+ # [MS-CIFS 2.2.6.7 TRANS2_SET_PATH_INFORMATION (0x0006)](https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-cifs/a23483d9-6543-4aaa-a996-e7c9506f8b94).
32
+ class SetPathInformationResponse < RubySMB::GenericPacket
33
+ COMMAND = RubySMB::SMB1::Commands::SMB_COM_TRANSACTION2
34
+
35
+ class ParameterBlock < RubySMB::SMB1::Packet::Trans2::Response::ParameterBlock
36
+ end
37
+
38
+ smb_header :smb_header
39
+ parameter_block :parameter_block
40
+ set_path_information_response_data_block :data_block
41
+
42
+ def initialize_instance
43
+ super
44
+ parameter_block.setup << RubySMB::SMB1::Packet::Trans2::Subcommands::SET_PATH_INFORMATION
45
+ smb_header.flags.reply = 1
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
@@ -7,6 +7,8 @@ module RubySMB
7
7
  require 'ruby_smb/smb1/packet/trans2/find_information_level'
8
8
  require 'ruby_smb/smb1/packet/trans2/query_information_level'
9
9
  require 'ruby_smb/smb1/packet/trans2/query_fs_information_level'
10
+ require 'ruby_smb/smb1/packet/trans2/set_information_level'
11
+ require 'ruby_smb/smb1/packet/trans2/set_fs_information_level'
10
12
  require 'ruby_smb/smb1/packet/trans2/data_block'
11
13
  require 'ruby_smb/smb1/packet/trans2/win9x_framing'
12
14
  require 'ruby_smb/smb1/packet/trans2/subcommands'
@@ -23,6 +25,12 @@ module RubySMB
23
25
  require 'ruby_smb/smb1/packet/trans2/set_file_information_response'
24
26
  require 'ruby_smb/smb1/packet/trans2/query_path_information_request'
25
27
  require 'ruby_smb/smb1/packet/trans2/query_path_information_response'
28
+ require 'ruby_smb/smb1/packet/trans2/set_path_information_request'
29
+ require 'ruby_smb/smb1/packet/trans2/set_path_information_response'
30
+ require 'ruby_smb/smb1/packet/trans2/query_fs_information_request'
31
+ require 'ruby_smb/smb1/packet/trans2/query_fs_information_response'
32
+ require 'ruby_smb/smb1/packet/trans2/set_fs_information_request'
33
+ require 'ruby_smb/smb1/packet/trans2/set_fs_information_response'
26
34
  end
27
35
  end
28
36
  end
@@ -195,6 +195,66 @@ module RubySMB
195
195
  results
196
196
  end
197
197
 
198
+ # Create a UNIX symbolic link on the remote share using the CIFS UNIX
199
+ # Extensions SMB_SET_FILE_UNIX_LINK information level (0x0201) via a
200
+ # Trans2 SET_PATH_INFORMATION request.
201
+ #
202
+ # @example
203
+ # tree = client.tree_connect("\\\\samba\\writable")
204
+ # tree.set_unix_link(symlink: 'escape', target: '../../../../etc')
205
+ #
206
+ # @param symlink [String] the path, relative to the share, where the
207
+ # symbolic link file will be created
208
+ # @param target [String] the destination the symbolic link points to
209
+ # @return [WindowsError::ErrorCode] the NTStatus returned by the server
210
+ # @raise [RubySMB::Error::InvalidPacket] if the response is not a Trans2
211
+ # SET_PATH_INFORMATION response
212
+ # @raise [RubySMB::Error::UnexpectedStatusCode] if the response NTStatus
213
+ # is not STATUS_SUCCESS
214
+ def set_unix_link(symlink:, target:)
215
+ # Samba gates SMB_SET_FILE_UNIX_LINK behind a per-session CIFS UNIX
216
+ # Extensions handshake: query server capabilities, then echo them
217
+ # back via SMB_SET_CIFS_UNIX_INFO. Without this, Samba responds to
218
+ # the symlink request with STATUS_INVALID_LEVEL even when
219
+ # `unix extensions = yes` is set server-side.
220
+ enable_cifs_unix_extensions
221
+
222
+ request = RubySMB::SMB1::Packet::Trans2::SetPathInformationRequest.new
223
+ request = set_header_fields(request)
224
+ # CIFS UNIX Extensions paths are raw byte strings, not UTF-16. Clear
225
+ # flags2.unicode so the filename parameter and target data block are
226
+ # emitted as bytes — Samba rejects the UTF-16 variant with
227
+ # STATUS_INVALID_LEVEL.
228
+ request.smb_header.flags2.unicode = 0
229
+
230
+ t2_params = request.data_block.trans2_parameters
231
+ t2_params.information_level = RubySMB::SMB1::Packet::Trans2::SetInformationLevel::SMB_SET_FILE_UNIX_LINK
232
+ t2_params.filename = symlink
233
+
234
+ encoded_target = target.dup.force_encoding(Encoding::ASCII_8BIT)
235
+ encoded_target << "\x00".b unless encoded_target.end_with?("\x00".b)
236
+ request.data_block.trans2_data.buffer = encoded_target
237
+
238
+ request.parameter_block.total_parameter_count = request.parameter_block.parameter_count
239
+ request.parameter_block.total_data_count = request.parameter_block.data_count
240
+ request.parameter_block.max_parameter_count = request.parameter_block.parameter_count
241
+ request.parameter_block.max_data_count = 0
242
+
243
+ raw_response = client.send_recv(request)
244
+ response = RubySMB::SMB1::Packet::Trans2::SetPathInformationResponse.read(raw_response)
245
+ unless response.valid?
246
+ raise RubySMB::Error::InvalidPacket.new(
247
+ expected_proto: RubySMB::SMB1::SMB_PROTOCOL_ID,
248
+ expected_cmd: RubySMB::SMB1::Packet::Trans2::SetPathInformationResponse::COMMAND,
249
+ packet: response
250
+ )
251
+ end
252
+ unless response.status_code == WindowsError::NTStatus::STATUS_SUCCESS
253
+ raise RubySMB::Error::UnexpectedStatusCode, response.status_code
254
+ end
255
+ response.status_code
256
+ end
257
+
198
258
  # Sets a few preset header fields that will always be set the same
199
259
  # way for Tree operations. This is, the TreeID and Extended Attributes.
200
260
  #
@@ -206,8 +266,72 @@ module RubySMB
206
266
  request
207
267
  end
208
268
 
269
+ # Perform the CIFS UNIX Extensions handshake on this tree: query the
270
+ # server's supported extensions (SMB_QUERY_CIFS_UNIX_INFO) and then
271
+ # echo the version and capability bits back via SMB_SET_CIFS_UNIX_INFO.
272
+ # Samba requires this round-trip to have completed on the current
273
+ # session before it will honor UNIX-extension info levels such as
274
+ # SMB_SET_FILE_UNIX_LINK.
275
+ #
276
+ # @return [WindowsError::ErrorCode] NTStatus of the SET reply
277
+ # @raise [RubySMB::Error::UnexpectedStatusCode] if either leg fails
278
+ def enable_cifs_unix_extensions
279
+ major, minor, capabilities = query_cifs_unix_info
280
+ set_cifs_unix_info(major: major, minor: minor, capabilities: capabilities)
281
+ end
282
+
209
283
  private
210
284
 
285
+ def query_cifs_unix_info
286
+ request = RubySMB::SMB1::Packet::Trans2::QueryFsInformationRequest.new
287
+ request = set_header_fields(request)
288
+ request.smb_header.flags2.unicode = 0
289
+ request.data_block.trans2_parameters.information_level =
290
+ RubySMB::SMB1::Packet::Trans2::QueryFsInformationLevel::SMB_QUERY_CIFS_UNIX_INFO
291
+ request.parameter_block.total_parameter_count = request.parameter_block.parameter_count
292
+ request.parameter_block.total_data_count = 0
293
+ request.parameter_block.max_parameter_count = 0
294
+ request.parameter_block.max_data_count = 560
295
+
296
+ raw_response = client.send_recv(request)
297
+ response = RubySMB::SMB1::Packet::Trans2::QueryFsInformationResponse.read(raw_response)
298
+ unless response.status_code == WindowsError::NTStatus::STATUS_SUCCESS
299
+ raise RubySMB::Error::UnexpectedStatusCode, response.status_code
300
+ end
301
+ info = RubySMB::SMB1::Packet::Trans2::QueryFsInformationLevel::QueryFsCifsUnixInfo.read(
302
+ response.data_block.trans2_data.buffer
303
+ )
304
+ [info.major_version.to_i, info.minor_version.to_i, info.capabilities.to_i]
305
+ end
306
+
307
+ def set_cifs_unix_info(major:, minor:, capabilities:)
308
+ request = RubySMB::SMB1::Packet::Trans2::SetFsInformationRequest.new
309
+ request = set_header_fields(request)
310
+ request.smb_header.flags2.unicode = 0
311
+ request.data_block.trans2_parameters.fid = 0
312
+ request.data_block.trans2_parameters.information_level =
313
+ RubySMB::SMB1::Packet::Trans2::SetFsInformationLevel::SMB_SET_CIFS_UNIX_INFO
314
+
315
+ info = RubySMB::SMB1::Packet::Trans2::QueryFsInformationLevel::QueryFsCifsUnixInfo.new
316
+ info.major_version = major
317
+ info.minor_version = minor
318
+ info.capabilities = capabilities
319
+ request.data_block.trans2_data.buffer = info.to_binary_s
320
+
321
+ request.parameter_block.total_parameter_count = request.parameter_block.parameter_count
322
+ request.parameter_block.total_data_count = request.parameter_block.data_count
323
+ request.parameter_block.max_parameter_count = request.parameter_block.parameter_count
324
+ request.parameter_block.max_data_count = 0
325
+
326
+ raw_response = client.send_recv(request)
327
+ response = RubySMB::SMB1::Packet::Trans2::SetFsInformationResponse.read(raw_response)
328
+ unless response.status_code == WindowsError::NTStatus::STATUS_SUCCESS
329
+ raise RubySMB::Error::UnexpectedStatusCode, response.status_code
330
+ end
331
+ response.status_code
332
+ end
333
+
334
+
211
335
  def _open(filename:, flags: nil, options: nil, disposition: RubySMB::Dispositions::FILE_OPEN,
212
336
  impersonation: RubySMB::ImpersonationLevels::SEC_IMPERSONATE, read: true, write: false, delete: false)
213
337
  unless client.server_supports_nt_smbs
@@ -1,3 +1,3 @@
1
1
  module RubySMB
2
- VERSION = '3.3.19'.freeze
2
+ VERSION = '3.3.21'.freeze
3
3
  end
@@ -40,7 +40,12 @@ RSpec.describe RubySMB::SMB1::Packet::Trans2::QueryFsInformationRequest do
40
40
 
41
41
  it { is_expected.to respond_to :name }
42
42
  it { is_expected.to respond_to :trans2_parameters }
43
- it { is_expected.to_not respond_to :trans2_data }
43
+ it { is_expected.to respond_to :trans2_data }
44
+
45
+ it '#trans2_data is a zero-length placeholder (this request carries no data payload)' do
46
+ expect(data_block.trans2_data).to be_a BinData::String
47
+ expect(data_block.trans2_data.num_bytes).to eq 0
48
+ end
44
49
 
45
50
  it 'should keep #trans2_parameters 4-byte aligned' do
46
51
  expect(data_block.trans2_parameters.abs_offset % 4).to eq 0
@@ -0,0 +1,52 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe RubySMB::SMB1::Packet::Trans2::SetFsInformationRequest do
4
+ subject(:packet) { described_class.new }
5
+
6
+ describe '#smb_header' do
7
+ subject(:header) { packet.smb_header }
8
+
9
+ it { is_expected.to be_a RubySMB::SMB1::SMBHeader }
10
+
11
+ it 'has the command set to SMB_COM_TRANSACTION2' do
12
+ expect(header.command).to eq RubySMB::SMB1::Commands::SMB_COM_TRANSACTION2
13
+ end
14
+ end
15
+
16
+ describe '#parameter_block' do
17
+ subject(:parameter_block) { packet.parameter_block }
18
+
19
+ it { is_expected.to be_a RubySMB::SMB1::Packet::Trans2::Request::ParameterBlock }
20
+
21
+ it 'uses the SET_FS_INFORMATION subcommand' do
22
+ expect(parameter_block.setup[0]).to eq RubySMB::SMB1::Packet::Trans2::Subcommands::SET_FS_INFORMATION
23
+ end
24
+ end
25
+
26
+ describe '#data_block' do
27
+ subject(:data_block) { packet.data_block }
28
+
29
+ it { is_expected.to respond_to :trans2_parameters }
30
+ it { is_expected.to respond_to :trans2_data }
31
+
32
+ describe '#trans2_parameters' do
33
+ subject(:parameters) { data_block.trans2_parameters }
34
+
35
+ it { is_expected.to respond_to :fid }
36
+ it { is_expected.to respond_to :information_level }
37
+
38
+ it 'is 4 bytes on the wire (fid + information_level)' do
39
+ parameters.fid = 0
40
+ parameters.information_level = RubySMB::SMB1::Packet::Trans2::SetFsInformationLevel::SMB_SET_CIFS_UNIX_INFO
41
+ expect(parameters.to_binary_s.bytesize).to eq 4
42
+ end
43
+ end
44
+
45
+ describe '#trans2_data' do
46
+ it 'carries an opaque byte buffer whose contents depend on the information level' do
47
+ data_block.trans2_data.buffer = "\x01\x00\x00\x00".b + "\x00".b * 8
48
+ expect(data_block.trans2_data.buffer.bytesize).to eq 12
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,29 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe RubySMB::SMB1::Packet::Trans2::SetFsInformationResponse do
4
+ subject(:packet) { described_class.new }
5
+
6
+ describe '#smb_header' do
7
+ subject(:header) { packet.smb_header }
8
+
9
+ it { is_expected.to be_a RubySMB::SMB1::SMBHeader }
10
+
11
+ it 'has the command set to SMB_COM_TRANSACTION2' do
12
+ expect(header.command).to eq RubySMB::SMB1::Commands::SMB_COM_TRANSACTION2
13
+ end
14
+
15
+ it 'sets the reply flag' do
16
+ expect(header.flags.reply).to eq 1
17
+ end
18
+ end
19
+
20
+ describe '#parameter_block' do
21
+ subject(:parameter_block) { packet.parameter_block }
22
+
23
+ it { is_expected.to be_a RubySMB::SMB1::Packet::Trans2::Response::ParameterBlock }
24
+
25
+ it 'uses the SET_FS_INFORMATION subcommand' do
26
+ expect(parameter_block.setup[0]).to eq RubySMB::SMB1::Packet::Trans2::Subcommands::SET_FS_INFORMATION
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,114 @@
1
+ RSpec.describe RubySMB::SMB1::Packet::Trans2::SetPathInformationRequest do
2
+ subject(:packet) { described_class.new }
3
+
4
+ describe '#smb_header' do
5
+ subject(:header) { packet.smb_header }
6
+
7
+ it 'is a standard SMB Header' do
8
+ expect(header).to be_a RubySMB::SMB1::SMBHeader
9
+ end
10
+
11
+ it 'should have the command set to SMB_COM_TRANSACTION2' do
12
+ expect(header.command).to eq RubySMB::SMB1::Commands::SMB_COM_TRANSACTION2
13
+ end
14
+
15
+ it 'should not have the response flag set' do
16
+ expect(header.flags.reply).to eq 0
17
+ end
18
+ end
19
+
20
+ describe '#parameter_block' do
21
+ subject(:parameter_block) { packet.parameter_block }
22
+
23
+ it 'is a standard ParameterBlock' do
24
+ expect(parameter_block).to be_a RubySMB::SMB1::Packet::Trans2::Request::ParameterBlock
25
+ end
26
+
27
+ it 'should have the setup set to the SET_PATH_INFORMATION subcommand' do
28
+ expect(parameter_block.setup).to include RubySMB::SMB1::Packet::Trans2::Subcommands::SET_PATH_INFORMATION
29
+ end
30
+ end
31
+
32
+ describe '#data_block' do
33
+ subject(:data_block) { packet.data_block }
34
+
35
+ it 'is a standard DataBlock' do
36
+ expect(data_block).to be_a RubySMB::SMB1::DataBlock
37
+ end
38
+
39
+ it { is_expected.to respond_to :name }
40
+ it { is_expected.to respond_to :trans2_parameters }
41
+ it { is_expected.to respond_to :trans2_data }
42
+
43
+ it 'should keep #trans2_parameters 4-byte aligned' do
44
+ expect(data_block.trans2_parameters.abs_offset % 4).to eq 0
45
+ end
46
+
47
+ describe '#trans2_parameters' do
48
+ subject(:parameters) { data_block.trans2_parameters }
49
+
50
+ it { is_expected.to respond_to :information_level }
51
+ it { is_expected.to respond_to :reserved }
52
+ it { is_expected.to respond_to :filename }
53
+
54
+ describe '#information_level' do
55
+ it 'is a 16-bit field' do
56
+ expect(parameters.information_level).to be_a BinData::Uint16le
57
+ end
58
+ end
59
+
60
+ describe '#reserved' do
61
+ it 'is a 32-bit field' do
62
+ expect(parameters.reserved).to be_a BinData::Uint32le
63
+ end
64
+ end
65
+
66
+ describe '#filename' do
67
+ it 'is a BinData::Choice' do
68
+ expect(parameters.filename).to be_a BinData::Choice
69
+ end
70
+
71
+ it 'encodes the filename as OEM when the unicode flag is cleared' do
72
+ packet.smb_header.flags2.unicode = 0
73
+ parameters.filename = 'link'
74
+ expect(parameters.filename.to_binary_s).to eq "link\x00".b
75
+ end
76
+
77
+ it 'encodes the filename as UTF-16LE when the unicode flag is set' do
78
+ packet.smb_header.flags2.unicode = 1
79
+ parameters.filename = 'link'
80
+ expect(parameters.filename.to_binary_s).to eq "link\x00".encode('UTF-16LE').b
81
+ end
82
+ end
83
+ end
84
+
85
+ describe '#trans2_data' do
86
+ subject(:data) { data_block.trans2_data }
87
+
88
+ it { is_expected.to respond_to :buffer }
89
+
90
+ it 'carries an opaque byte buffer for the info-level-specific payload' do
91
+ data.buffer = "target\x00"
92
+ expect(data.buffer.to_binary_s).to eq "target\x00".b
93
+ end
94
+ end
95
+ end
96
+
97
+ describe 'encoded bytes for an SMB_SET_FILE_UNIX_LINK request' do
98
+ it 'encodes the information level, filename and target in the expected positions' do
99
+ packet.smb_header.flags2.unicode = 1
100
+ packet.data_block.trans2_parameters.information_level =
101
+ RubySMB::SMB1::Packet::Trans2::SetInformationLevel::SMB_SET_FILE_UNIX_LINK
102
+ packet.data_block.trans2_parameters.filename = 'foo'
103
+ packet.data_block.trans2_data.buffer = "bar\x00".encode('UTF-16LE')
104
+
105
+ raw = packet.to_binary_s
106
+ # Information level (little-endian 0x0201)
107
+ expect(raw).to include("\x01\x02".b)
108
+ # Filename in UTF-16LE with null terminator
109
+ expect(raw).to include("foo\x00".encode('UTF-16LE').b)
110
+ # Target in UTF-16LE with null terminator
111
+ expect(raw).to include("bar\x00".encode('UTF-16LE').b)
112
+ end
113
+ end
114
+ end
@@ -0,0 +1,54 @@
1
+ RSpec.describe RubySMB::SMB1::Packet::Trans2::SetPathInformationResponse do
2
+ subject(:packet) { described_class.new }
3
+
4
+ describe '#smb_header' do
5
+ subject(:header) { packet.smb_header }
6
+
7
+ it 'is a standard SMB Header' do
8
+ expect(header).to be_a RubySMB::SMB1::SMBHeader
9
+ end
10
+
11
+ it 'should have the command set to SMB_COM_TRANSACTION2' do
12
+ expect(header.command).to eq RubySMB::SMB1::Commands::SMB_COM_TRANSACTION2
13
+ end
14
+
15
+ it 'should have the response flag set' do
16
+ expect(header.flags.reply).to eq 1
17
+ end
18
+ end
19
+
20
+ describe '#parameter_block' do
21
+ subject(:parameter_block) { packet.parameter_block }
22
+
23
+ it 'should have the setup set to the SET_PATH_INFORMATION subcommand' do
24
+ expect(parameter_block.setup).to include RubySMB::SMB1::Packet::Trans2::Subcommands::SET_PATH_INFORMATION
25
+ end
26
+ end
27
+
28
+ describe '#data_block' do
29
+ subject(:data_block) { packet.data_block }
30
+
31
+ it 'is a standard DataBlock' do
32
+ expect(data_block).to be_a RubySMB::SMB1::DataBlock
33
+ end
34
+
35
+ it { is_expected.to respond_to :trans2_parameters }
36
+ it { is_expected.to_not respond_to :trans2_data }
37
+
38
+ it 'should keep #trans2_parameters 4-byte aligned' do
39
+ expect(data_block.trans2_parameters.abs_offset % 4).to eq 0
40
+ end
41
+
42
+ describe '#trans2_parameters' do
43
+ subject(:parameters) { data_block.trans2_parameters }
44
+
45
+ it { is_expected.to respond_to :ea_error_offset }
46
+
47
+ describe '#ea_error_offset' do
48
+ it 'is a 16-bit field' do
49
+ expect(parameters.ea_error_offset).to be_a BinData::Uint16le
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end