ruby_smb 1.0.4 → 2.0.2

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 (130) 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 +207 -18
  15. data/lib/ruby_smb/client/authentication.rb +27 -8
  16. data/lib/ruby_smb/client/encryption.rb +62 -0
  17. data/lib/ruby_smb/client/negotiation.rb +153 -12
  18. data/lib/ruby_smb/client/signing.rb +19 -0
  19. data/lib/ruby_smb/client/tree_connect.rb +4 -4
  20. data/lib/ruby_smb/client/utils.rb +8 -7
  21. data/lib/ruby_smb/client/winreg.rb +46 -0
  22. data/lib/ruby_smb/crypto.rb +30 -0
  23. data/lib/ruby_smb/dcerpc.rb +38 -0
  24. data/lib/ruby_smb/dcerpc/bind.rb +2 -2
  25. data/lib/ruby_smb/dcerpc/bind_ack.rb +2 -2
  26. data/lib/ruby_smb/dcerpc/error.rb +3 -0
  27. data/lib/ruby_smb/dcerpc/ndr.rb +95 -16
  28. data/lib/ruby_smb/dcerpc/pdu_header.rb +1 -1
  29. data/lib/ruby_smb/dcerpc/request.rb +28 -9
  30. data/lib/ruby_smb/dcerpc/rrp_unicode_string.rb +35 -0
  31. data/lib/ruby_smb/dcerpc/srvsvc.rb +10 -0
  32. data/lib/ruby_smb/dcerpc/srvsvc/net_share_enum_all.rb +9 -0
  33. data/lib/ruby_smb/dcerpc/winreg.rb +340 -0
  34. data/lib/ruby_smb/dcerpc/winreg/close_key_request.rb +24 -0
  35. data/lib/ruby_smb/dcerpc/winreg/close_key_response.rb +27 -0
  36. data/lib/ruby_smb/dcerpc/winreg/enum_key_request.rb +45 -0
  37. data/lib/ruby_smb/dcerpc/winreg/enum_key_response.rb +42 -0
  38. data/lib/ruby_smb/dcerpc/winreg/enum_value_request.rb +39 -0
  39. data/lib/ruby_smb/dcerpc/winreg/enum_value_response.rb +36 -0
  40. data/lib/ruby_smb/dcerpc/winreg/open_key_request.rb +34 -0
  41. data/lib/ruby_smb/dcerpc/winreg/open_key_response.rb +25 -0
  42. data/lib/ruby_smb/dcerpc/winreg/open_root_key_request.rb +43 -0
  43. data/lib/ruby_smb/dcerpc/winreg/open_root_key_response.rb +35 -0
  44. data/lib/ruby_smb/dcerpc/winreg/query_info_key_request.rb +27 -0
  45. data/lib/ruby_smb/dcerpc/winreg/query_info_key_response.rb +40 -0
  46. data/lib/ruby_smb/dcerpc/winreg/query_value_request.rb +39 -0
  47. data/lib/ruby_smb/dcerpc/winreg/query_value_response.rb +57 -0
  48. data/lib/ruby_smb/dcerpc/winreg/regsam.rb +40 -0
  49. data/lib/ruby_smb/dispatcher/socket.rb +4 -3
  50. data/lib/ruby_smb/error.rb +28 -1
  51. data/lib/ruby_smb/smb1/commands.rb +1 -1
  52. data/lib/ruby_smb/smb1/file.rb +6 -4
  53. data/lib/ruby_smb/smb1/packet/empty_packet.rb +4 -2
  54. data/lib/ruby_smb/smb1/packet/session_setup_legacy_request.rb +1 -1
  55. data/lib/ruby_smb/smb1/packet/session_setup_legacy_response.rb +2 -2
  56. data/lib/ruby_smb/smb1/packet/session_setup_request.rb +1 -1
  57. data/lib/ruby_smb/smb1/packet/session_setup_response.rb +2 -2
  58. data/lib/ruby_smb/smb1/packet/write_andx_request.rb +1 -1
  59. data/lib/ruby_smb/smb1/pipe.rb +79 -3
  60. data/lib/ruby_smb/smb1/tree.rb +12 -3
  61. data/lib/ruby_smb/smb2/bit_field/session_flags.rb +2 -1
  62. data/lib/ruby_smb/smb2/bit_field/share_flags.rb +6 -4
  63. data/lib/ruby_smb/smb2/file.rb +25 -43
  64. data/lib/ruby_smb/smb2/negotiate_context.rb +108 -0
  65. data/lib/ruby_smb/smb2/packet.rb +2 -0
  66. data/lib/ruby_smb/smb2/packet/compression_transform_header.rb +41 -0
  67. data/lib/ruby_smb/smb2/packet/error_packet.rb +9 -4
  68. data/lib/ruby_smb/smb2/packet/negotiate_request.rb +51 -14
  69. data/lib/ruby_smb/smb2/packet/negotiate_response.rb +50 -4
  70. data/lib/ruby_smb/smb2/packet/transform_header.rb +84 -0
  71. data/lib/ruby_smb/smb2/packet/tree_connect_request.rb +92 -6
  72. data/lib/ruby_smb/smb2/packet/tree_connect_response.rb +8 -26
  73. data/lib/ruby_smb/smb2/pipe.rb +77 -3
  74. data/lib/ruby_smb/smb2/smb2_header.rb +1 -1
  75. data/lib/ruby_smb/smb2/tree.rb +23 -17
  76. data/lib/ruby_smb/version.rb +1 -1
  77. data/ruby_smb.gemspec +5 -3
  78. data/spec/lib/ruby_smb/client_spec.rb +1441 -61
  79. data/spec/lib/ruby_smb/crypto_spec.rb +25 -0
  80. data/spec/lib/ruby_smb/dcerpc/bind_ack_spec.rb +2 -2
  81. data/spec/lib/ruby_smb/dcerpc/bind_spec.rb +2 -2
  82. data/spec/lib/ruby_smb/dcerpc/ndr_spec.rb +410 -0
  83. data/spec/lib/ruby_smb/dcerpc/request_spec.rb +50 -7
  84. data/spec/lib/ruby_smb/dcerpc/rrp_unicode_string_spec.rb +98 -0
  85. data/spec/lib/ruby_smb/dcerpc/srvsvc/net_share_enum_all_spec.rb +13 -0
  86. data/spec/lib/ruby_smb/dcerpc/srvsvc_spec.rb +60 -0
  87. data/spec/lib/ruby_smb/dcerpc/winreg/close_key_request_spec.rb +28 -0
  88. data/spec/lib/ruby_smb/dcerpc/winreg/close_key_response_spec.rb +36 -0
  89. data/spec/lib/ruby_smb/dcerpc/winreg/enum_key_request_spec.rb +108 -0
  90. data/spec/lib/ruby_smb/dcerpc/winreg/enum_key_response_spec.rb +97 -0
  91. data/spec/lib/ruby_smb/dcerpc/winreg/enum_value_request_spec.rb +94 -0
  92. data/spec/lib/ruby_smb/dcerpc/winreg/enum_value_response_spec.rb +82 -0
  93. data/spec/lib/ruby_smb/dcerpc/winreg/open_key_request_spec.rb +74 -0
  94. data/spec/lib/ruby_smb/dcerpc/winreg/open_key_response_spec.rb +35 -0
  95. data/spec/lib/ruby_smb/dcerpc/winreg/open_root_key_request_spec.rb +90 -0
  96. data/spec/lib/ruby_smb/dcerpc/winreg/open_root_key_response_spec.rb +38 -0
  97. data/spec/lib/ruby_smb/dcerpc/winreg/query_info_key_request_spec.rb +39 -0
  98. data/spec/lib/ruby_smb/dcerpc/winreg/query_info_key_response_spec.rb +113 -0
  99. data/spec/lib/ruby_smb/dcerpc/winreg/query_value_request_spec.rb +88 -0
  100. data/spec/lib/ruby_smb/dcerpc/winreg/query_value_response_spec.rb +150 -0
  101. data/spec/lib/ruby_smb/dcerpc/winreg/regsam_spec.rb +32 -0
  102. data/spec/lib/ruby_smb/dcerpc/winreg_spec.rb +710 -0
  103. data/spec/lib/ruby_smb/dcerpc_spec.rb +81 -0
  104. data/spec/lib/ruby_smb/dispatcher/socket_spec.rb +2 -2
  105. data/spec/lib/ruby_smb/error_spec.rb +59 -0
  106. data/spec/lib/ruby_smb/smb1/file_spec.rb +9 -1
  107. data/spec/lib/ruby_smb/smb1/packet/empty_packet_spec.rb +10 -0
  108. data/spec/lib/ruby_smb/smb1/packet/session_setup_legacy_request_spec.rb +2 -2
  109. data/spec/lib/ruby_smb/smb1/packet/session_setup_legacy_response_spec.rb +2 -2
  110. data/spec/lib/ruby_smb/smb1/packet/session_setup_request_spec.rb +2 -2
  111. data/spec/lib/ruby_smb/smb1/packet/session_setup_response_spec.rb +1 -1
  112. data/spec/lib/ruby_smb/smb1/pipe_spec.rb +210 -148
  113. data/spec/lib/ruby_smb/smb2/bit_field/session_flags_spec.rb +9 -0
  114. data/spec/lib/ruby_smb/smb2/bit_field/share_flags_spec.rb +27 -0
  115. data/spec/lib/ruby_smb/smb2/file_spec.rb +86 -62
  116. data/spec/lib/ruby_smb/smb2/negotiate_context_spec.rb +332 -0
  117. data/spec/lib/ruby_smb/smb2/packet/compression_transform_header_spec.rb +108 -0
  118. data/spec/lib/ruby_smb/smb2/packet/error_packet_spec.rb +29 -2
  119. data/spec/lib/ruby_smb/smb2/packet/negotiate_request_spec.rb +138 -3
  120. data/spec/lib/ruby_smb/smb2/packet/negotiate_response_spec.rb +120 -2
  121. data/spec/lib/ruby_smb/smb2/packet/transform_header_spec.rb +220 -0
  122. data/spec/lib/ruby_smb/smb2/packet/tree_connect_request_spec.rb +339 -9
  123. data/spec/lib/ruby_smb/smb2/packet/tree_connect_response_spec.rb +3 -30
  124. data/spec/lib/ruby_smb/smb2/pipe_spec.rb +220 -149
  125. data/spec/lib/ruby_smb/smb2/smb2_header_spec.rb +2 -2
  126. data/spec/lib/ruby_smb/smb2/tree_spec.rb +53 -8
  127. metadata +187 -81
  128. metadata.gz.sig +0 -0
  129. data/lib/ruby_smb/smb1/dcerpc.rb +0 -72
  130. data/lib/ruby_smb/smb2/dcerpc.rb +0 -75
@@ -0,0 +1,220 @@
1
+ RSpec.describe RubySMB::SMB2::Packet::TransformHeader do
2
+ subject(:packet) { described_class.new }
3
+
4
+ it { is_expected.to respond_to :protocol }
5
+ it { is_expected.to respond_to :signature }
6
+ it { is_expected.to respond_to :nonce }
7
+ it { is_expected.to respond_to :original_message_size }
8
+ it { is_expected.to respond_to :flags }
9
+ it { is_expected.to respond_to :session_id }
10
+ it { is_expected.to respond_to :encrypted_data }
11
+
12
+ it 'is little endian' do
13
+ expect(described_class.fields.instance_variable_get(:@hints)[:endian]).to eq :little
14
+ end
15
+
16
+ describe '#protocol' do
17
+ it 'is a 32-bit field' do
18
+ expect(packet.protocol).to be_a BinData::Bit32
19
+ end
20
+
21
+ it 'has an initial value of 0xFD534D42' do
22
+ expect(packet.protocol).to eq(0xFD534D42)
23
+ end
24
+ end
25
+
26
+ describe '#signature' do
27
+ it 'is a String' do
28
+ expect(packet.signature).to be_a BinData::String
29
+ end
30
+ end
31
+
32
+ describe '#nonce' do
33
+ it 'is a String' do
34
+ expect(packet.nonce).to be_a BinData::String
35
+ end
36
+ end
37
+
38
+ describe '#original_message_size ' do
39
+ it 'is a 32-bit unsigned integer' do
40
+ expect(packet.original_message_size).to be_a BinData::Uint32le
41
+ end
42
+ end
43
+
44
+ describe '#flags' do
45
+ it 'is a 16-bit unsigned integer' do
46
+ expect(packet.flags).to be_a BinData::Uint16le
47
+ end
48
+ end
49
+
50
+ describe '#session_id' do
51
+ it 'is a 64-bit unsigned integer' do
52
+ expect(packet.session_id).to be_a BinData::Uint64le
53
+ end
54
+ end
55
+
56
+ describe '#encrypted_data' do
57
+ it 'is an Array' do
58
+ expect(packet.encrypted_data).to be_a BinData::Array
59
+ end
60
+ end
61
+
62
+ describe '#decrypt' do
63
+ let(:key) { "\x56\x89\xd1\xbb\xf7\x45\xc0\xb6\x68\x81\x07\xe4\x7d\x35\xaf\xd3".b }
64
+ let(:data) { 'data'.b }
65
+ before :example do
66
+ packet.original_message_size = data.length
67
+ end
68
+
69
+ it 'raises the expected exception if the given algorithm is invalid' do
70
+ expect { packet.decrypt(key, algorithm: 'RC4') }.to raise_error(
71
+ RubySMB::Error::EncryptionError,
72
+ 'Error while decrypting with \'RC4\' (ArgumentError: Invalid algorithm, must be either AES-128-CCM or AES-128-GCM)'
73
+ )
74
+ end
75
+
76
+ context 'with AES-128-GCM algorithm (default)' do
77
+ before :example do
78
+ begin
79
+ OpenSSL::Cipher.new('AES-128-GCM')
80
+ rescue
81
+ skip(
82
+ "This test cannot be run since the version of OpenSSL the ruby "\
83
+ "OpenSSL extension was built with (#{OpenSSL::OPENSSL_VERSION}) "\
84
+ "does not support AES-128-GCM cipher")
85
+ end
86
+ packet.encrypted_data = "\x06\x45\x16\x36".bytes
87
+ packet.signature = "\x63\xb2\xf9\xe0\xb7\x43\xdb\xaf\x26\x8e\xd7\x42\xd3\xb2\xde\x0d"
88
+ packet.nonce = "\xe1\xb0\xa7\x20\xd9\xd9\x69\x3c\x79\xd0\x9c\x53\x00\x00\x00\x00"
89
+ end
90
+
91
+ it 'generates a cipher using OpenSSL::Cipher' do
92
+ expect(OpenSSL::Cipher).to receive(:new).with('AES-128-GCM').and_call_original
93
+ packet.decrypt(key)
94
+ end
95
+
96
+ it 'returns the expected decrypted string' do
97
+ expect(packet.decrypt(key)).to eq(data)
98
+ end
99
+
100
+ it 'raises the expected exception if an error occurs' do
101
+ allow(OpenSSL::Cipher).to receive(:new).and_raise(
102
+ RuntimeError.new('unsupported cipher algorithm (AES-128-GCM)'))
103
+ expect { packet.decrypt(key) }.to raise_error(
104
+ RubySMB::Error::EncryptionError,
105
+ 'Error while decrypting with \'AES-128-GCM\' (RuntimeError: unsupported cipher algorithm (AES-128-GCM))'
106
+ )
107
+ end
108
+ end
109
+
110
+ context 'with AES-128-CCM algorithm' do
111
+ before :example do
112
+ packet.encrypted_data = "\xf0\x05\x61\x91".bytes
113
+ packet.signature = "\xdd\x51\x9a\xc5\x6d\x38\x68\xdc\x36\x89\xb8\x99\xd8\x4a\xb8\x4a".b
114
+ packet.nonce = "\x8a\x6e\x2a\x87\x11\x61\x85\xd2\x15\x69\xf7\x00\x00\x00\x00\x00".b
115
+ end
116
+
117
+ it 'generates a cipher using OpenSSL::CCM' do
118
+ expect(OpenSSL::CCM).to receive(:new).with('AES', key, 16).and_call_original
119
+ packet.decrypt(key, algorithm: 'AES-128-CCM')
120
+ end
121
+
122
+ it 'returns the expected decrypted string' do
123
+ expect(packet.decrypt(key, algorithm: 'AES-128-CCM')).to eq(data)
124
+ end
125
+
126
+ it 'raises the expected exception if an error occurs' do
127
+ allow(OpenSSL::CCM).to receive(:new).and_raise(
128
+ OpenSSL::CCMError.new('unsupported cipher algorithm (AES-128-CCM)'))
129
+ expect { packet.decrypt(key, algorithm: 'AES-128-CCM') }.to raise_error(
130
+ RubySMB::Error::EncryptionError,
131
+ 'Error while decrypting with \'AES-128-CCM\' (OpenSSL::CCMError: unsupported cipher algorithm (AES-128-CCM))'
132
+ )
133
+ end
134
+ end
135
+ end
136
+
137
+ describe '#encrypt' do
138
+ let(:key) { "\x56\x89\xd1\xbb\xf7\x45\xc0\xb6\x68\x81\x07\xe4\x7d\x35\xaf\xd3".b }
139
+ let(:struct) { RubySMB::SMB2::Packet::TreeConnectRequest.new }
140
+
141
+ it 'raises the expected exception if the given algorithm is invalid' do
142
+ expect { packet.encrypt(struct, key, algorithm: 'RC4') }.to raise_error(
143
+ RubySMB::Error::EncryptionError,
144
+ 'Error while encrypting with \'RC4\' (ArgumentError: Invalid algorithm, must be either AES-128-CCM or AES-128-GCM)'
145
+ )
146
+ end
147
+
148
+ context 'with AES-128-GCM algorithm (default)' do
149
+ before :example do
150
+ begin
151
+ OpenSSL::Cipher.new('AES-128-GCM')
152
+ rescue
153
+ skip(
154
+ "This test cannot be run since the version of OpenSSL the ruby "\
155
+ "OpenSSL extension was built with (#{OpenSSL::OPENSSL_VERSION}) "\
156
+ "does not support AES-128-GCM cipher")
157
+ end
158
+ end
159
+
160
+ it 'generates a cipher using OpenSSL::Cipher' do
161
+ expect(OpenSSL::Cipher).to receive(:new).with('AES-128-GCM').and_call_original
162
+ packet.encrypt(struct, key)
163
+ end
164
+
165
+ it 'encrypts a BinData structure' do
166
+ packet.encrypt(struct, key)
167
+ expect(packet.decrypt(key)).to eq(struct.to_binary_s)
168
+ end
169
+
170
+ it 'encrypts a string' do
171
+ packet.encrypt('data', key)
172
+ expect(packet.decrypt(key)).to eq('data')
173
+ end
174
+
175
+ it 'raises the expected exception if an error occurs' do
176
+ allow(OpenSSL::Cipher).to receive(:new).and_raise(
177
+ RuntimeError.new('unsupported cipher algorithm (AES-128-GCM)'))
178
+ expect { packet.encrypt('data', key) }.to raise_error(
179
+ RubySMB::Error::EncryptionError,
180
+ 'Error while encrypting with \'AES-128-GCM\' (RuntimeError: unsupported cipher algorithm (AES-128-GCM))'
181
+ )
182
+ end
183
+ end
184
+
185
+ context 'with AES-128-CCM algorithm' do
186
+ it 'generates a cipher using OpenSSL::CCM' do
187
+ expect(OpenSSL::CCM).to receive(:new).with('AES', key, 16).and_call_original
188
+ packet.encrypt(struct, key, algorithm: 'AES-128-CCM')
189
+ end
190
+
191
+ it 'encrypts a BinData structure' do
192
+ packet.encrypt(struct, key, algorithm: 'AES-128-CCM')
193
+ expect(packet.decrypt(key, algorithm: 'AES-128-CCM')).to eq(struct.to_binary_s)
194
+ end
195
+
196
+ it 'encrypts a string' do
197
+ packet.encrypt('data', key, algorithm: 'AES-128-CCM')
198
+ expect(packet.decrypt(key, algorithm: 'AES-128-CCM')).to eq('data')
199
+ end
200
+
201
+ it 'raises the expected exception if an error occurs' do
202
+ allow(OpenSSL::CCM).to receive(:new).and_raise(
203
+ OpenSSL::CCMError.new('unsupported cipher algorithm (AES-128-CCM)'))
204
+ expect { packet.encrypt('data', key, algorithm: 'AES-128-CCM') }.to raise_error(
205
+ RubySMB::Error::EncryptionError,
206
+ 'Error while encrypting with \'AES-128-CCM\' (OpenSSL::CCMError: unsupported cipher algorithm (AES-128-CCM))'
207
+ )
208
+ end
209
+ end
210
+ end
211
+
212
+ it 'reads binary data as expected' do
213
+ data = described_class.new
214
+ key = "\x56\x89\xd1\xbb\xf7\x45\xc0\xb6\x68\x81\x07\xe4\x7d\x35\xaf\xd3".b
215
+ struct = RubySMB::SMB2::Packet::TreeConnectRequest.new
216
+ data.encrypt(struct, key, algorithm: 'AES-128-CCM')
217
+ expect(described_class.read(data.to_binary_s)).to eq(data)
218
+ end
219
+ end
220
+
@@ -9,6 +9,7 @@ RSpec.describe RubySMB::SMB2::Packet::TreeConnectRequest do
9
9
  it { is_expected.to respond_to :path_offset }
10
10
  it { is_expected.to respond_to :path_length }
11
11
  it { is_expected.to respond_to :path }
12
+ it { is_expected.to respond_to :tree_connect_request_extension }
12
13
 
13
14
  it 'is little endian' do
14
15
  expect(described_class.fields.instance_variable_get(:@hints)[:endian]).to eq :little
@@ -30,18 +31,347 @@ RSpec.describe RubySMB::SMB2::Packet::TreeConnectRequest do
30
31
  end
31
32
  end
32
33
 
33
- describe '#encode_path' do
34
- let(:path) { '\\192.168.1.1\\example' }
35
- let(:encoded_path) { path.encode('utf-16le').force_encoding('binary') }
34
+ describe '#structure_size' do
35
+ it 'should be a 16-bit unsigned integer' do
36
+ expect(packet.structure_size).to be_a BinData::Uint16le
37
+ end
38
+
39
+ it 'should have a default value of 9 as per the SMB2 spec' do
40
+ expect(packet.structure_size).to eq 9
41
+ end
42
+ end
43
+
44
+ describe '#flags' do
45
+ it 'should be a 16-bit unsigned integer' do
46
+ expect(packet.flags).to be_a BinData::Uint16le
47
+ end
48
+ end
49
+
50
+ describe '#path_offset' do
51
+ it 'should be a 16-bit unsigned integer' do
52
+ expect(packet.path_offset).to be_a BinData::Uint16le
53
+ end
54
+
55
+ context 'when flags is set to SMB2_TREE_CONNECT_FLAG_EXTENSION_PRESENT' do
56
+ it 'should be set to the offset, in bytes, of the full share path name from the beginning of the packet header' do
57
+ packet.flags = described_class::SMB2_TREE_CONNECT_FLAG_EXTENSION_PRESENT
58
+ expect(packet.path_offset).to eq(88)
59
+ end
60
+ end
61
+
62
+ context 'when flags is not set to SMB2_TREE_CONNECT_FLAG_EXTENSION_PRESENT' do
63
+ it 'should be set to the offset, in bytes, of the full share path name from the beginning of the packet header' do
64
+ expect(packet.path_offset).to eq(72)
65
+ end
66
+ end
67
+ end
68
+
69
+ describe '#path_length' do
70
+ let(:path) { '\\\\server\\path' }
71
+
72
+ it 'should be a 16-bit unsigned integer' do
73
+ expect(packet.path_length).to be_a BinData::Uint16le
74
+ end
75
+
76
+ context 'when flags is set to SMB2_TREE_CONNECT_FLAG_EXTENSION_PRESENT' do
77
+ it 'should be the length of the full share path name (unicode) in bytes' do
78
+ packet.flags = described_class::SMB2_TREE_CONNECT_FLAG_EXTENSION_PRESENT
79
+ packet.tree_connect_request_extension.path = path
80
+ expect(packet.path_length).to eq(path.length * 2)
81
+ end
82
+ end
83
+
84
+ context 'when flags is not set to SMB2_TREE_CONNECT_FLAG_EXTENSION_PRESENT' do
85
+ it 'should be the length of the full share path name (unicode) in bytes' do
86
+ packet.path = path
87
+ expect(packet.path_length).to eq(path.length * 2)
88
+ end
89
+ end
90
+ end
91
+
92
+ describe '#path' do
93
+ it 'should be a unicode string' do
94
+ expect(packet.path).to be_a RubySMB::Field::String16
95
+ end
96
+
97
+ it 'exists if #flags is not set to SMB2_TREE_CONNECT_FLAG_EXTENSION_PRESENT' do
98
+ packet.flags = described_class::SMB2_TREE_CONNECT_FLAG_REDIRECT_TO_OWNER
99
+ expect(packet.path?).to be true
100
+ end
101
+ end
102
+
103
+ describe '#tree_connect_request_extension' do
104
+ it 'is a TreeConnectRequestExtension structure' do
105
+ expect(packet.tree_connect_request_extension).to be_a RubySMB::SMB2::Packet::TreeConnectRequestExtension
106
+ end
107
+
108
+ it 'exists if #flags is set to SMB2_TREE_CONNECT_FLAG_EXTENSION_PRESENT' do
109
+ packet.flags = described_class::SMB2_TREE_CONNECT_FLAG_EXTENSION_PRESENT
110
+ expect(packet.tree_connect_request_extension?).to be true
111
+ end
112
+ end
113
+
114
+ it 'reads binary data as expected' do
115
+ data = described_class.new
116
+ expect(described_class.read(data.to_binary_s)).to eq(data)
117
+ data = described_class.new(flags: described_class::SMB2_TREE_CONNECT_FLAG_EXTENSION_PRESENT)
118
+ data.tree_connect_request_extension.tree_connect_contexts << RubySMB::SMB2::Packet::TreeConnectContext.new(context_type: 1)
119
+ expect(described_class.read(data.to_binary_s)).to eq(data)
120
+ end
121
+ end
122
+
123
+ RSpec.describe RubySMB::SMB2::Packet::TreeConnectRequestExtension do
124
+ subject(:packet) { described_class.new }
125
+
126
+ it { is_expected.to respond_to :tree_connect_context_offset }
127
+ it { is_expected.to respond_to :tree_connect_context_count }
128
+ it { is_expected.to respond_to :reserved }
129
+ it { is_expected.to respond_to :path }
130
+ it { is_expected.to respond_to :tree_connect_contexts }
131
+
132
+ it 'is little endian' do
133
+ expect(described_class.fields.instance_variable_get(:@hints)[:endian]).to eq :little
134
+ end
135
+
136
+ describe '#tree_connect_context_offset' do
137
+ it 'is a 32-bit unsigned integer' do
138
+ expect(packet.tree_connect_context_offset).to be_a BinData::Uint32le
139
+ end
140
+
141
+ it 'is the offset from the start of the SMB2 TREE_CONNECT request of an array of tree connect contexts' do
142
+ tc = RubySMB::SMB2::Packet::TreeConnectRequest.new(
143
+ flags: RubySMB::SMB2::Packet::TreeConnectRequest::SMB2_TREE_CONNECT_FLAG_EXTENSION_PRESENT
144
+ )
145
+ expect(tc.tree_connect_request_extension.tree_connect_context_offset).to eq(16)
146
+ end
147
+ end
148
+
149
+ describe '#tree_connect_context_count' do
150
+ it 'should be a 16-bit unsigned integer' do
151
+ expect(packet.tree_connect_context_count).to be_a BinData::Uint16le
152
+ end
153
+
154
+ it 'should be the #tree_connect_contexts size' do
155
+ packet.tree_connect_contexts << RubySMB::SMB2::Packet::TreeConnectContext.new(context_type: 1)
156
+ packet.tree_connect_contexts << RubySMB::SMB2::Packet::TreeConnectContext.new(context_type: 1)
157
+ expect(packet.tree_connect_context_count).to eq(2)
158
+ end
159
+ end
160
+
161
+ describe '#reserved' do
162
+ it 'should be a binary string' do
163
+ expect(packet.reserved).to be_a BinData::String
164
+ end
165
+
166
+ it 'is 10-bytes long' do
167
+ expect(packet.reserved.length).to eq(10)
168
+ end
169
+ end
170
+
171
+ describe '#path' do
172
+ it 'should be a unicode string' do
173
+ expect(packet.path).to be_a RubySMB::Field::String16
174
+ end
175
+ end
176
+
177
+ describe '#tree_connect_contexts' do
178
+ it 'is an Array field' do
179
+ expect(packet.tree_connect_contexts).to be_a BinData::Array
180
+ end
181
+
182
+ it 'has #tree_connect_context_count elements' do
183
+ packet.tree_connect_context_count = 3
184
+ expect(packet.tree_connect_contexts.size).to eq(3)
185
+ end
186
+ end
187
+
188
+ it 'reads binary data as expected' do
189
+ data = described_class.new
190
+ expect(described_class.read(data.to_binary_s)).to eq(data)
191
+ data.tree_connect_contexts << RubySMB::SMB2::Packet::TreeConnectContext.new(context_type: 1)
192
+ data.tree_connect_contexts << RubySMB::SMB2::Packet::TreeConnectContext.new(context_type: 1)
193
+ expect(described_class.read(data.to_binary_s)).to eq(data)
194
+ end
195
+ end
196
+
197
+ RSpec.describe RubySMB::SMB2::Packet::TreeConnectContext do
198
+ subject(:packet) do
199
+ described_class.new(
200
+ context_type: described_class::SMB2_REMOTED_IDENTITY_TREE_CONNECT_CONTEXT_ID
201
+ )
202
+ end
203
+
204
+ it { is_expected.to respond_to :context_type }
205
+ it { is_expected.to respond_to :data_length }
206
+ it { is_expected.to respond_to :reserved }
207
+ it { is_expected.to respond_to :data }
208
+
209
+ it 'is little endian' do
210
+ expect(described_class.fields.instance_variable_get(:@hints)[:endian]).to eq :little
211
+ end
212
+
213
+ describe '#context_type' do
214
+ it 'should be a 16-bit unsigned integer' do
215
+ expect(packet.context_type).to be_a BinData::Uint16le
216
+ end
217
+ end
218
+
219
+ describe '#data_length' do
220
+ it 'should be a 16-bit unsigned integer' do
221
+ expect(packet.data_length).to be_a BinData::Uint16le
222
+ end
223
+
224
+ it 'is the length, in bytes, of the Data field' do
225
+ expect(packet.data_length).to eq(packet.data.to_binary_s.size)
226
+ end
227
+ end
228
+
229
+ describe '#reserved' do
230
+ it 'is a 32-bit unsigned integer' do
231
+ expect(packet.reserved).to be_a BinData::Uint32le
232
+ end
233
+ end
234
+
235
+ describe '#data' do
236
+ it 'is a BinData Choice' do
237
+ expect(packet.data).to be_a BinData::Choice
238
+ end
239
+
240
+ it 'contains the structure defined by #context_type' do
241
+ expect(packet.data).to eq(RubySMB::SMB2::Packet::RemotedIdentityTreeConnectContext.new)
242
+ end
243
+ end
244
+
245
+ it 'reads binary data as expected' do
246
+ data = described_class.new(context_type: 1)
247
+ expect(described_class.read(data.to_binary_s)).to eq(data)
248
+ end
249
+ end
250
+
251
+ RSpec.describe RubySMB::SMB2::Packet::RemotedIdentityTreeConnectContext do
252
+ subject(:packet) { described_class.new }
253
+
254
+ it { is_expected.to respond_to :ticket_type }
255
+ it { is_expected.to respond_to :ticket_size }
256
+ it { is_expected.to respond_to :user }
257
+ it { is_expected.to respond_to :user_name }
258
+ it { is_expected.to respond_to :domain }
259
+ it { is_expected.to respond_to :groups }
260
+ it { is_expected.to respond_to :restricted_groups }
261
+ it { is_expected.to respond_to :privileges }
262
+ it { is_expected.to respond_to :primary_group }
263
+ it { is_expected.to respond_to :owner }
264
+ it { is_expected.to respond_to :default_dacl }
265
+ it { is_expected.to respond_to :device_groups }
266
+ it { is_expected.to respond_to :user_claims }
267
+ it { is_expected.to respond_to :device_claims }
268
+ it { is_expected.to respond_to :ticket_info }
269
+
270
+ it 'is little endian' do
271
+ expect(described_class.fields.instance_variable_get(:@hints)[:endian]).to eq :little
272
+ end
273
+
274
+ describe '#ticket_type' do
275
+ it 'should be a 16-bit unsigned integer' do
276
+ expect(packet.ticket_type).to be_a BinData::Uint16le
277
+ end
278
+
279
+ it 'should be 1' do
280
+ expect(packet.ticket_type).to eq(1)
281
+ end
282
+ end
283
+
284
+ describe '#ticket_size' do
285
+ it 'should be a 16-bit unsigned integer' do
286
+ expect(packet.ticket_size).to be_a BinData::Uint16le
287
+ end
288
+
289
+ it 'is the total size of this structure' do
290
+ packet.ticket_info = 'Ticket Info'
291
+ expect(packet.ticket_size).to eq(packet.num_bytes)
292
+ end
293
+ end
294
+
295
+ describe '#user' do
296
+ it 'should be a 16-bit unsigned integer' do
297
+ expect(packet.user).to be_a BinData::Uint16le
298
+ end
299
+ end
300
+
301
+ describe '#user_name' do
302
+ it 'should be a 16-bit unsigned integer' do
303
+ expect(packet.user_name).to be_a BinData::Uint16le
304
+ end
305
+ end
36
306
 
37
- it 'sets the path string to a UTF-16LE version of the supplied string' do
38
- packet.encode_path(path)
39
- expect(packet.path).to eq encoded_path
307
+ describe '#domain' do
308
+ it 'should be a 16-bit unsigned integer' do
309
+ expect(packet.domain).to be_a BinData::Uint16le
40
310
  end
311
+ end
41
312
 
42
- it 'updates the #path_length' do
43
- packet.encode_path(path)
44
- expect(packet.path_length).to eq encoded_path.length
313
+ describe '#groups' do
314
+ it 'should be a 16-bit unsigned integer' do
315
+ expect(packet.groups).to be_a BinData::Uint16le
45
316
  end
46
317
  end
318
+
319
+ describe '#restricted_groups' do
320
+ it 'should be a 16-bit unsigned integer' do
321
+ expect(packet.restricted_groups).to be_a BinData::Uint16le
322
+ end
323
+ end
324
+
325
+ describe '#privileges' do
326
+ it 'should be a 16-bit unsigned integer' do
327
+ expect(packet.privileges).to be_a BinData::Uint16le
328
+ end
329
+ end
330
+
331
+ describe '#primary_group' do
332
+ it 'should be a 16-bit unsigned integer' do
333
+ expect(packet.primary_group).to be_a BinData::Uint16le
334
+ end
335
+ end
336
+
337
+ describe '#owner' do
338
+ it 'should be a 16-bit unsigned integer' do
339
+ expect(packet.owner).to be_a BinData::Uint16le
340
+ end
341
+ end
342
+
343
+ describe '#default_dacl' do
344
+ it 'should be a 16-bit unsigned integer' do
345
+ expect(packet.default_dacl).to be_a BinData::Uint16le
346
+ end
347
+ end
348
+
349
+ describe '#device_groups' do
350
+ it 'should be a 16-bit unsigned integer' do
351
+ expect(packet.device_groups).to be_a BinData::Uint16le
352
+ end
353
+ end
354
+
355
+ describe '#user_claims' do
356
+ it 'should be a 16-bit unsigned integer' do
357
+ expect(packet.user_claims).to be_a BinData::Uint16le
358
+ end
359
+ end
360
+
361
+ describe '#device_claims' do
362
+ it 'should be a 16-bit unsigned integer' do
363
+ expect(packet.device_claims).to be_a BinData::Uint16le
364
+ end
365
+ end
366
+
367
+ describe '#ticket_info' do
368
+ it 'should be string' do
369
+ expect(packet.ticket_info).to be_a BinData::String
370
+ end
371
+ end
372
+
373
+ it 'reads binary data as expected' do
374
+ data = described_class.new(ticket_info: 'ticket info')
375
+ expect(described_class.read(data.to_binary_s)).to eq(data)
376
+ end
47
377
  end